diff --git a/src/production/care.go b/src/production/care.go new file mode 100644 index 0000000..d7f55c8 --- /dev/null +++ b/src/production/care.go @@ -0,0 +1,14 @@ +package production + +import "shylinux.com/x/ice" + +type care struct { + Tables + order string `data:"7"` +} + +func (s care) List(m *ice.Message, arg ...string) { + // s.ValueList(m, arg).Display("") +} + +func init() { ice.TeamCtxCmd(care{}) } diff --git a/src/production/common.go b/src/production/common.go index 3ca4b25..48fc8d9 100644 --- a/src/production/common.go +++ b/src/production/common.go @@ -111,6 +111,7 @@ func (s Table) RewriteAppend(m *ice.Message, arg ...string) *ice.Message { } func (s Table) PushIssueButton(m *ice.Message, value ice.Maps, user_uid string, arg ...ice.Any) { button := []ice.Any{} + // button := []ice.Any{s.Remove} defer func() { m.PushButton(button...) }() isCreator, isLeader, isWorker := s.IsCreator(m), s.IsLeader(m), s.IsWorker(m) switch IssueStatus(kit.Int(value[model.STATUS])) { @@ -255,16 +256,16 @@ func (s Table) Preview(m *ice.Message, arg ...string) { m.ProcessOpen(m.OptionDefault(model.LINK, web.S(m.Option(web.SPACE))+web.C(m.Option(ctx.INDEX)))) } func (s Table) Payfor(m *ice.Message, arg ...string) { - s.issueOtherCreate(m, deal{}, arg...) + s.issueOtherCreate(m, Deal{}, arg...) } func (s Issue) Payfor(m *ice.Message, arg ...string) { - s.commonOtherCreate(m, deal{}, arg...) + s.commonOtherCreate(m, Deal{}, arg...) } func (s Table) Discuss(m *ice.Message, arg ...string) { - s.issueOtherCreate(m, meet{}, arg...) + s.issueOtherCreate(m, Meet{}, arg...) } func (s Issue) Discuss(m *ice.Message, arg ...string) { - s.commonOtherCreate(m, meet{}, arg...) + s.commonOtherCreate(m, Meet{}, arg...) } func (s Table) issueOtherCreate(m *ice.Message, target ice.Any, arg ...string) { s.OtherCreate(m, target, kit.Simple(m.OptionSimple(model.ISSUE_UID), arg)...) @@ -326,11 +327,11 @@ func (s Table) CaseList(m *ice.Message, arg ...string) { m.PushAction(s.Preview) } func (s Table) MeetList(m *ice.Message, arg ...string) { - s.OtherList(m, meet{}).Display("meet.js") + s.OtherList(m, Meet{}).Display("meet.js") m.PushAction(s.Preview) } func (s Table) DealList(m *ice.Message, arg ...string) { - s.OtherList(m, deal{}).Display("deal.js") + s.OtherList(m, Deal{}).Display("deal.js") } func (s Table) finishCheck(m *ice.Message, target ice.Any, name string, arg ...string) bool { diff --git a/src/production/deal.go b/src/production/deal.go index 25aa958..eaa33c5 100644 --- a/src/production/deal.go +++ b/src/production/deal.go @@ -6,19 +6,19 @@ import ( kit "shylinux.com/x/toolkits" ) -type deal struct { +type Deal struct { Table - order string `data:"7"` + order string `data:"9"` fields string `data:"from_user_uid,to_user_uid,price,title,content"` create string `name:"create issue_uid* from_user_uid* to_user_uid* price* title* content" role:"leader"` modify string `name:"modify title* content" role:"leader"` } -func (s deal) Create(m *ice.Message, arg ...string) { +func (s Deal) Create(m *ice.Message, arg ...string) { s.ValueCreate(m, arg...) s.SendMessage(s.GetCommandUID(m), "", m.Option(model.TO_USER_UID)) } -func (s deal) List(m *ice.Message, arg ...string) { +func (s Deal) List(m *ice.Message, arg ...string) { if s.IsLeader(m) { s.ValueList(m, arg).PushAction() s.StatusPrice(m, arg...) @@ -39,4 +39,4 @@ func (s deal) List(m *ice.Message, arg ...string) { } } -func init() { ice.TeamCtxCmd(deal{}) } +func init() { ice.TeamCtxCmd(Deal{}) } diff --git a/src/production/design.go b/src/production/design.go index b6d3b63..bde22e7 100644 --- a/src/production/design.go +++ b/src/production/design.go @@ -30,10 +30,11 @@ func (s Design) Cancel(m *ice.Message, arg ...string) { } func (s Design) List(m *ice.Message, arg ...string) { user_uid := m.Option(model.USER_UID) + s.Limit(m, 300) s.Orders(m, model.STATUS, s.Desc(model.CREATED_AT)) s.ValueList(m, arg) s.SelectJoinPlan(m) - s.StatusCount(m, arg...) + // s.StatusCount(m, arg...) m.Table(func(value ice.Maps) { s.PushIssueButton(m, value, user_uid) }).Display("") } func init() { ice.TeamCtxCmd(Design{}) } diff --git a/src/production/design.js b/src/production/design.js index 5d14e3c..bcfd6cf 100644 --- a/src/production/design.js +++ b/src/production/design.js @@ -1,6 +1,6 @@ Volcanos(chat.ONIMPORT, { _init: function(can, msg) { - can.onimport.myView(can, msg, function(value) { return [ + can.onimport.myViewTabs(can, "status", msg, function(value) { return [ {view: html.TITLE, list: [value.title, can.onimport.textView(can, value, "status")]}, {view: html.STATUS, list: [ // can.onimport.beginTime(can, value), diff --git a/src/production/issue.go b/src/production/issue.go index c3ea823..5f0b922 100644 --- a/src/production/issue.go +++ b/src/production/issue.go @@ -41,10 +41,11 @@ func (s Issue) List(m *ice.Message, arg ...string) { return } user_uid, isCreator, isWorker := m.Option(model.USER_UID), s.IsCreator(m), s.IsWorker(m) + s.Limit(m, 300) s.Orders(m, model.STATUS, model.DESIGN_COUNT, model.TASK_COUNT, s.Desc(model.CREATED_AT)) s.ValueList(m, arg) s.SelectJoinPlan(m) - s.StatusCount(m, arg...) + // s.StatusCount(m, arg...) m.Table(func(value ice.Maps) { button := []ice.Any{} if PlanStatus(kit.Int(value[model.PLAN_STATUS])) != PlanFinish { diff --git a/src/production/issue.js b/src/production/issue.js index 8a1977e..6e8381b 100644 --- a/src/production/issue.js +++ b/src/production/issue.js @@ -1,6 +1,6 @@ Volcanos(chat.ONIMPORT, { _init: function(can, msg) { - can.onimport.myView(can, msg, function(value) { return [ + can.onimport.myViewTabs(can, "status", msg, function(value) { return [ {view: html.TITLE, list: [value.title, // value.issue_type != "feature" && can.onimport.textView(can, value, "issue_type"), // value.level != "level-3" && can.onimport.textView(can, value, "level"), diff --git a/src/production/meet.go b/src/production/meet.go index 6b79120..8868424 100644 --- a/src/production/meet.go +++ b/src/production/meet.go @@ -9,22 +9,22 @@ import ( "shylinux.com/x/operation/src/production/model" ) -type meet struct { +type Meet struct { Table - order string `data:"6"` + order string `data:"8"` fields string `data:"from_user_uid,to_user_uid,meet_type,title,content,link,begin_time,end_time,issue_uid,plan_uid,company_uid"` create string `name:"create issue_uid* from_user_uid* to_user_uid* meet_type* title* content* link* date time" role:"leader"` remove string `name:"remove" role:"leader"` } -func (s meet) Create(m *ice.Message, arg ...string) { +func (s Meet) Create(m *ice.Message, arg ...string) { t := time.Unix(kit.Time(m.Option("date")+" "+m.Option("time")+":00")/int64(time.Second), 0) m.Options(model.BEGIN_TIME, t.Format(ice.MOD_TIME), model.END_TIME, t.Add(30*time.Minute).Format(ice.MOD_TIME)) s.ValueCreate(m, m.OptionSimple("issue_uid,from_user_uid,to_user_uid,meet_type,title,content,link,begin_time,end_time,plan_uid")...) s.SendMessage(s.GetCommandUID(m), m.Option(model.FROM_USER_UID), m.Option(model.TO_USER_UID)) s.SendMessage(s.GetCommandUID(m), m.Option(model.TO_USER_UID), m.Option(model.FROM_USER_UID)) } -func (s meet) List(m *ice.Message, arg ...string) { +func (s Meet) List(m *ice.Message, arg ...string) { s.ValueList(m, arg).PushAction(s.Preview).Display("") if s.IsLeader(m) { m.RenameAppend(model.TO_USER_UID, model.USER_UID) @@ -34,7 +34,7 @@ func (s meet) List(m *ice.Message, arg ...string) { kit.If(!s.IsLeader(m) && m.Length() == 0, func() { m.SetResult().Echo("请等待管理员创建会议") }) } -func init() { ice.TeamCtxCmd(meet{}) } +func init() { ice.TeamCtxCmd(Meet{}) } type MeetType int diff --git a/src/production/model/model.go b/src/production/model/model.go index 24757b2..759beaa 100644 --- a/src/production/model/model.go +++ b/src/production/model/model.go @@ -8,6 +8,7 @@ const ( INFO = "info" LINK = "link" TYPE = "type" + ROLE = "role" LEVEL = "level" STATUS = "status" COUNT = "count" @@ -15,6 +16,8 @@ const ( TITLE = "title" CONTENT = "content" VERSION = "version" + COMPANY_UID = "company_uid" + PLACE_UID = "place_uid" USER_UID = "user_uid" USER_ROLE = "user_role" USER_NAME = "user_name" diff --git a/src/production/plan.go b/src/production/plan.go index fa00fbd..c4901d3 100644 --- a/src/production/plan.go +++ b/src/production/plan.go @@ -29,6 +29,7 @@ func (s Plan) List(m *ice.Message, arg ...string) { m.SetResult("请等待「管理员」创建迭代计划") } }) + s.Limit(m, 300) s.Orders(m, model.STATUS, model.ISSUE_COUNT, s.Desc(model.CREATED_AT)) s.ValueList(m, arg).Table(func(value ice.Maps) { button := []ice.Any{} @@ -56,7 +57,7 @@ func (s Plan) List(m *ice.Message, arg ...string) { } }).Display("").DisplayCSS("") m.Option("otherList", "issueList,designList,taskList,caseList,meetList,dealList") - s.StatusCount(m, arg...) + // s.StatusCount(m, arg...) } func (s Plan) Process(m *ice.Message, arg ...string) { s.changeStatus(m, PlanCreate, PlanProcess) diff --git a/src/production/plan.js b/src/production/plan.js index 71f11b6..20fda07 100644 --- a/src/production/plan.js +++ b/src/production/plan.js @@ -1,6 +1,6 @@ Volcanos(chat.ONIMPORT, { _init: function(can, msg) { - can.onimport.myView(can, msg, function(value) { return [ + can.onimport.myViewTabs(can, "plan_status", msg, function(value) { return [ {view: html.TITLE, list: [value.title, value.version, can.onimport.textView(can, value)]}, {view: html.STATUS, list: [can.onimport.beginTime(can, value), can.onimport.unitView(can, value, "issue_count", "个")]}, {view: html.OUTPUT, list: [value.content]}, can.onimport.titleAction(can, value), diff --git a/src/production/portal.json b/src/production/portal.json index 05e3e74..21ba2f6 100644 --- a/src/production/portal.json +++ b/src/production/portal.json @@ -2,6 +2,7 @@ "portal": "产品迭代", "member": "项目成员", "plan": "迭代计划", "issue": "产品需求", "design": "界面设计", "task": "开发任务", "case": "测试用例", "meet": "会议记录", "deal": "支付记录", + "take": "项目接单", "stat": "项目进度", "care": "新人帮助", "discuss": "约会", "payfor": "支付", "program": "编程", "process": "开始", "submit": "提交", "reback": "返工", "finish": "完成", "issueCreate": "原型设计", "designCreate": "界面设计", "taskCreate": "开发任务", "caseCreate": "测试用例", @@ -22,6 +23,9 @@ "caseCreate": "notice" }, "icons": { + "take": "meet.png", + "care": "meet.png", + "stat": "meet.png", "meet": "meet.png", "deal": "deal.png", "plan": "plan.png", @@ -88,7 +92,10 @@ "plan_status": { "create": "待开始", "process": "进行中", - "finish": "已完成" + "finish": "已完成", + "style": { + "create": "danger" + } }, "status": { "create": "待评审", diff --git a/src/production/stat.go b/src/production/stat.go new file mode 100644 index 0000000..ad7fba8 --- /dev/null +++ b/src/production/stat.go @@ -0,0 +1,93 @@ +package production + +import ( + "shylinux.com/x/ice" + "shylinux.com/x/operation/src/production/model" + kit "shylinux.com/x/toolkits" +) + +type Stat struct { + Tables + order string `data:"10"` +} + +func (s Stat) List(m *ice.Message, arg ...string) { + if !s.IsLeader(m) { + m.Echo("请申请成为「管理人员」") + return + } + deal, will := 0, 0 + s.SelectAuthPlace(m, story{}).Table(func(value ice.Maps) { + if value["auth_status"] != "issued" { + return + } + stats := s.Count(m, userStory{}, model.ROLE, model.STORY_UID, value[model.UID]) + sum := func(arg ...ice.Any) (res int) { + kit.For(arg, func(value ice.Any) { res += kit.Int(stats[kit.Format(value)]) }) + return + } + defer m.Push(model.STORY_UID, value[model.UID]) + m.Push(model.NAME, value[model.NAME]) + m.Push("成员人数", sum("1", "2", "3")) + stats = s.Count(m, Plan{}, model.STATUS, model.STORY_UID, value[model.UID], model.STATUS, kit.Format(PlanFinish)) + m.Push("已完成", sum(PlanFinish)) + stats = s.Count(m, Plan{}, model.STATUS, model.STORY_UID, value[model.UID], model.STATUS, kit.Format(PlanProcess), model.ISSUE_COUNT, "0") + m.Push("待接单", sum(PlanProcess)) + stats = s.Count(m, Issue{}, model.STATUS, model.STORY_UID, value[model.UID]) + m.Push("原型设计中", sum(IssueCreate, IssueApproved, IssueProcess, IssueSubmit)) + stats = s.Count(m, Design{}, model.STATUS, model.STORY_UID, value[model.UID]) + m.Push("界面设计中", sum(IssueCreate, IssueApproved, IssueProcess, IssueSubmit)) + price, msg := 0, m.Spawn() + s.Tables.Tables(msg, "left join deals on issues.user_uid = deals.user_uid AND issues.uid = deals.issue_uid") + s.Fields(msg, "issues.title, issues.price AS issue_price, SUM(deals.price) AS deal_price").Groups(msg, "issues.uid") + msg.Cmd(Issue{}, s.Select, "issues.story_uid = ? AND (status != ? AND status != ? AND status != ?) AND deals.deleted_at IS NULL", + value[model.UID], kit.Format(IssueCreate), kit.Format(IssueRejected), kit.Format(IssueCancel), + ).Table(func(value ice.Maps) { + p := kit.Int(value["issue_price"]) - kit.Int(value["deal_price"]) + kit.If(p > 0, func() { price += p }) + }) + s.Tables.Tables(msg, "left join deals on designs.user_uid = deals.user_uid AND designs.issue_uid = deals.issue_uid") + s.Fields(msg, "designs.title, designs.price AS design_price, SUM(deals.price) AS deal_price").Groups(msg, "designs.uid") + msg.Cmd(Design{}, s.Select, "designs.story_uid = ? AND (status != ? AND status != ? AND status != ?) AND deals.deleted_at IS NULL", + value[model.UID], kit.Format(IssueCreate), kit.Format(IssueRejected), kit.Format(IssueCancel), + ).Table(func(value ice.Maps) { + p := kit.Int(value["design_price"]) - kit.Int(value["deal_price"]) + kit.If(p > 0, func() { price += p }) + }) + m.Push("待支付", kit.Format(price/100)) + will += price + s.Fields(m, "SUM(price) AS price").Groups(m, model.STORY_UID) + m.Cmd(Deal{}, s.Select, model.STORY_UID, value[model.UID]).Table(func(value ice.Maps) { + m.Push("已支付", kit.Format(kit.Int(value[model.PRICE])/100)) + deal += kit.Int(value[model.PRICE]) + }) + good := 0 + s.Tables.Tables(msg, "left join deals on user_stories.user_uid = deals.to_user_uid") + s.Fields(msg, "SUM(price) AS price").Groups(msg, "user_stories.user_uid") + msg.Cmd(userStory{}, s.Select, "user_stories.story_uid", value[model.UID]).Table(func(value ice.Maps) { + kit.If(kit.Int(value["price"]) > 0, func() { good++ }) + }) + m.Push("已入门", good) + }) + stat := map[string]int{} + m.Table(func(value ice.Maps) { kit.For(value, func(key, value string) { stat[key] += kit.Int(value) }) }) + kit.For(stat, func(key string, value int) { + m.Push(key, kit.Select(kit.Format(value), "ContextOS 汇总数据", key == model.NAME)) + }) + m.Display("").SortIntR("成员人数") +} +func (s Table) SelectAuthPlace(m *ice.Message, target ice.Any, arg ...string) *ice.Message { + msg := s.SelectJoinAuth(m.Cmd(target, s.Select, model.UID, m.Option(model.STORY_UID))) + if msg.Append("auth_status") != "issued" { + return m.Echo("请申请服务认证") + } + return s.SelectJoinAuth(m.Cmd(target, s.Select, "company_uid = ? AND auth_uid IS NOT NULL AND auth_uid != ''", msg.Append(model.COMPANY_UID))) +} +func (s Stat) Count(m *ice.Message, target ice.Any, field string, arg ...string) map[string]string { + stats := map[string]string{} + s.Fields(m, field, "COUNT(*) AS count").Groups(m, field) + m.Cmd(target, s.Select, arg).Table(func(value ice.Maps) { stats[value[field]] = value[model.COUNT] }) + return stats +} + +func init() { ice.TeamCtxCmd(Stat{}) } diff --git a/src/production/stat.js b/src/production/stat.js new file mode 100644 index 0000000..968e5e8 --- /dev/null +++ b/src/production/stat.js @@ -0,0 +1,23 @@ +Volcanos(chat.ONIMPORT, { + _init: function(can, msg) { + // msg.Dump(can) + // return + can.onimport.myView(can, msg, function(value) { return [ + {view: html.TITLE, list: [ + value.name, value["已入门"]+"人"+" / "+value["成员人数"]+"人", + ]}, + {view: html.STATUS, list: [ + can.onimport.unitView(can, value, "已支付", "元"), + can.onimport.unitView(can, value, "已完成", "个"), + can.onimport.unitView(can, value, "待接单", "个"), + ]}, + {view: html.STATUS, list: [ + can.onimport.unitView(can, value, "待支付", "元"), + can.onimport.unitView(can, value, "原型设计中", "个"), + can.onimport.unitView(can, value, "界面设计中", "个"), + ]}, + ] }, function(event, value) { if (value.story_uid == "0") { return } + can.onimport.myStory(can, {index: "web.team.production.portal", args: [value.story_uid]}) + }) + }, +}) \ No newline at end of file diff --git a/src/production/take.go b/src/production/take.go new file mode 100644 index 0000000..7682317 --- /dev/null +++ b/src/production/take.go @@ -0,0 +1,51 @@ +package production + +import ( + "shylinux.com/x/ice" + "shylinux.com/x/operation/src/production/model" + kit "shylinux.com/x/toolkits" +) + +type take struct { + Tables + order string `data:"6"` +} + +func (s take) List(m *ice.Message, arg ...string) { + s.Limit(m, 300) + s.SelectAuthPlace(m, story{}).Table(func(value ice.Maps) { + if value["auth_status"] != "issued" { + return + } + m.Cmd(Plan{}, s.Select, model.STORY_UID, value[model.UID], model.STATUS, kit.Format(PlanProcess)).Table(func(val ice.Maps) { + if kit.Int(val[model.ISSUE_COUNT]) == 0 { + m.Push("type", "PM") + m.Push("uid", val["uid"]) + m.Push("title", val["title"]) + m.Push("content", val["content"]) + m.Push("user_uid", val["user_uid"]) + m.Push("story_uid", value[model.UID]) + m.Push("story_name", value[model.NAME]) + m.Push("created_at", value["created_at"]) + } else { + m.Cmd(Issue{}, s.Select, model.STORY_UID, value[model.UID], model.PLAN_UID, val[model.UID], + model.STATUS, kit.Format(IssueFinish), + model.DESIGN_COUNT, "0", + ).Table(func(val ice.Maps) { + m.Push("type", "UI") + m.Push("uid", val["uid"]) + m.Push("title", val["title"]) + m.Push("content", val["content"]) + m.Push("user_uid", val["user_uid"]) + m.Push("story_uid", value[model.UID]) + m.Push("story_name", value[model.NAME]) + m.Push("created_at", value["created_at"]) + }) + } + }) + }) + m.SortStrR("created_at") + m.Display("") +} + +func init() { ice.TeamCtxCmd(take{}) } diff --git a/src/production/take.js b/src/production/take.js new file mode 100644 index 0000000..3c9946a --- /dev/null +++ b/src/production/take.js @@ -0,0 +1,20 @@ +Volcanos(chat.ONIMPORT, { + _init: function(can, msg) { + can.onimport.myViewTabs(can, "type", msg, function(value) { return [ + {view: html.TITLE, list: [ + value.story_name, + value.title, can.onimport.textView(can, value, "type", "status"), can.onimport.titleAction(can, value), + ]}, + {view: html.STATUS, list: [can.onimport.timeView(can, value), value.user_name]}, + {view: html.OUTPUT, list: [value.content]}, + ] }, function(event, value) { + can.onimport.myStory(can, {index: "web.team.production.portal", args: [value.story_uid]}) + return + if (value.type == "PM") { + can.onimport.myStory(can, {index: "web.team.production.plan", args: [value.story_uid, value.uid]}) + } else { + can.onimport.myStory(can, {index: "web.team.production.issue", args: [value.story_uid, value.uid]}) + } + }) + }, +}) \ No newline at end of file