From 80ff796981318c7ffe871ddb73bc205fd554a1da Mon Sep 17 00:00:00 2001 From: wangxiaojian <17816869670@163.com> Date: Thu, 9 Dec 2021 09:33:45 +0800 Subject: [PATCH] add requirement-task-overview scenario --- conf/dop/dop.yaml | 15 +- .../dop/component-protocol/components/all.go | 1 + .../chartBlock/burnoutChart/model.go | 102 +++++++ .../chartBlock/burnoutChart/render.go | 207 +++++++++++++ .../chartBlock/burnoutChartFilter/model.go | 60 ++++ .../chartBlock/burnoutChartFilter/render.go | 126 ++++++++ .../chartBlock/render.go | 24 ++ .../common/condition.go | 26 ++ .../common/gshelper/helper.go | 166 ++++++++++ .../requirement-task-overview/common/util.go | 52 ++++ .../requirement-task-overview/common/vars.go | 19 ++ .../container/info/model.go | 57 ++++ .../container/info/render.go | 106 +++++++ .../container/render.go | 24 ++ .../container/simpleChart/model.go | 65 ++++ .../container/simpleChart/render.go | 110 +++++++ .../distributePage/render.go | 24 ++ .../distributePage/topFilter/model.go | 59 ++++ .../distributePage/topFilter/render.go | 284 ++++++++++++++++++ .../requirement-task-overview/page/render.go | 24 ++ .../pageContent/render.go | 24 ++ .../requirement-task-overview/scenario.go | 29 ++ .../requirement-task-overview/tabs/render.go | 24 ++ .../scenarios/requirement-task-overview.yml | 265 ++++++++++++++++ modules/dop/services/issue/issue.go | 4 - 25 files changed, 1892 insertions(+), 5 deletions(-) create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChart/model.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChart/render.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChartFilter/model.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChartFilter/render.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/chartBlock/render.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/common/condition.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/common/gshelper/helper.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/common/util.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/common/vars.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/container/info/model.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/container/info/render.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/container/render.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/container/simpleChart/model.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/container/simpleChart/render.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/distributePage/render.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/distributePage/topFilter/model.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/distributePage/topFilter/render.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/page/render.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/pageContent/render.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/scenario.go create mode 100644 modules/dop/component-protocol/components/requirement-task-overview/tabs/render.go create mode 100644 modules/dop/component-protocol/scenarios/requirement-task-overview.yml diff --git a/conf/dop/dop.yaml b/conf/dop/dop.yaml index 933e30836db..c383e4ad141 100644 --- a/conf/dop/dop.yaml +++ b/conf/dop/dop.yaml @@ -286,4 +286,17 @@ component-protocol.components.issue-gantt.filter: component-protocol.components.issue-gantt.gantt: component-protocol.components.issue-gantt.ganttContainer: component-protocol.components.issue-gantt.topHead: -component-protocol.components.issue-gantt.issueAddButton: \ No newline at end of file +component-protocol.components.issue-gantt.issueAddButton: + +# requirement-task-overview +component-protocol.components.requirement-task-overview.page: +component-protocol.components.requirement-task-overview.distributePage: +component-protocol.components.requirement-task-overview.topFilter: +component-protocol.components.requirement-task-overview.container: +component-protocol.components.requirement-task-overview.chartBlock: +component-protocol.components.requirement-task-overview.simpleChart: +component-protocol.components.requirement-task-overview.info: +component-protocol.components.requirement-task-overview.burnoutChartFilter: +component-protocol.components.requirement-task-overview.burnoutChart: +component-protocol.components.requirement-task-overview.pageContent: +component-protocol.components.requirement-task-overview.tabs: diff --git a/modules/dop/component-protocol/components/all.go b/modules/dop/component-protocol/components/all.go index 58967c512b9..f8018d92387 100644 --- a/modules/dop/component-protocol/components/all.go +++ b/modules/dop/component-protocol/components/all.go @@ -22,6 +22,7 @@ import ( _ "github.com/erda-project/erda/modules/dop/component-protocol/components/issue-dashboard" _ "github.com/erda-project/erda/modules/dop/component-protocol/components/issue-gantt" _ "github.com/erda-project/erda/modules/dop/component-protocol/components/issue-manage" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview" _ "github.com/erda-project/erda/modules/dop/component-protocol/components/scenes-import-record" _ "github.com/erda-project/erda/modules/dop/component-protocol/components/test-dashboard" _ "github.com/erda-project/erda/modules/dop/component-protocol/components/test-report" diff --git a/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChart/model.go b/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChart/model.go new file mode 100644 index 00000000000..669f9002d2b --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChart/model.go @@ -0,0 +1,102 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package burnoutChart + +import ( + "context" + "encoding/json" + + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda/modules/dop/dao" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +type BurnoutChart struct { + base.DefaultProvider + + Type string `json:"type"` + Props Props `json:"props"` + Issues []dao.IssueItem `json:"-"` +} + +type ( + Props struct { + ChartType string `json:"chartType"` + Title string `json:"title"` + PureChart bool `json:"pureChart"` + Option Option `json:"option"` + } + + Option struct { + XAxis XAxis `json:"xAxis"` + YAxis YAxis `json:"yAxis"` + Legend Legend `json:"legend"` + Tooltip map[string]interface{} `json:"tooltip"` + Series []Series `json:"series"` + } + + XAxis struct { + Type string `json:"type"` + Data []string `json:"data"` + } + + YAxis struct { + Type string `json:"type"` + AxisLine map[string]interface{} `json:"axisLine"` + AxisLabel map[string]interface{} `json:"axisLabel"` + } + + Legend struct { + Show bool `json:"show"` + Bottom bool `json:"bottom"` + Data []string `json:"data"` + } + + Series struct { + Data []int `json:"data"` + Name string `json:"name"` + Type string `json:"type"` + Smooth bool `json:"smooth"` + ItemStyle map[string]interface{} `json:"itemStyle"` + MarkLine MarkLine `json:"markLine"` + } + + MarkLine struct { + Label map[string]interface{} `json:"label"` + LineStyle map[string]interface{} `json:"lineStyle"` + Data [][]Data `json:"data"` + } + + Data struct { + Name string `json:"name"` + Coord []string `json:"coord"` + } +) + +func (f *BurnoutChart) SetToProtocolComponent(c *cptype.Component) error { + b, err := json.Marshal(f) + if err != nil { + return err + } + return json.Unmarshal(b, &c) +} + +func (f *BurnoutChart) InitFromProtocol(ctx context.Context, c *cptype.Component) error { + b, err := json.Marshal(c) + if err != nil { + return err + } + return json.Unmarshal(b, f) +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChart/render.go b/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChart/render.go new file mode 100644 index 00000000000..cdf2cecbedd --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChart/render.go @@ -0,0 +1,207 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package burnoutChart + +import ( + "context" + "encoding/json" + "strconv" + "time" + + "github.com/erda-project/erda-infra/base/servicehub" + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda/apistructs" + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common" + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common/gshelper" + "github.com/erda-project/erda/modules/dop/dao" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +func init() { + base.InitProviderWithCreator(common.ScenarioKeyTestDashboard, "burnoutChart", func() servicehub.Provider { + return &BurnoutChart{} + }) +} + +func (f *BurnoutChart) Render(ctx context.Context, c *cptype.Component, scenario cptype.Scenario, event cptype.ComponentEvent, gs *cptype.GlobalStateData) error { + if err := f.InitFromProtocol(ctx, c); err != nil { + return err + } + + h := gshelper.NewGSHelper(gs) + f.Issues = h.GetIssueList() + + dates := make([]time.Time, 0) + dateMap := make(map[time.Time]int) + itr := h.GetIteration() + if itr.StartedAt == nil || itr.FinishedAt == nil { + return nil + } + for rd := common.RangeDate(*itr.StartedAt, *itr.FinishedAt); ; { + date := rd() + if date.IsZero() { + break + } + dates = append(dates, date) + dateMap[date] = 0 + } + + issueCreateMap := make(map[time.Time][]dao.IssueItem, 0) + issueFinishMap := make(map[time.Time][]dao.IssueItem, 0) + for _, issue := range f.Issues { + if h.GetBurnoutChartType() == "requirement" && + issue.Type != apistructs.IssueTypeRequirement { + continue + } + if h.GetBurnoutChartType() == "task" && + issue.Type != apistructs.IssueTypeTask { + continue + } + issueCreateMap[common.DateTime(issue.CreatedAt)] = append(issueCreateMap[common.DateTime(issue.CreatedAt)], issue) + if issue.FinishTime != nil { + issueFinishMap[common.DateTime(*issue.FinishTime)] = append(issueFinishMap[common.DateTime(*issue.FinishTime)], issue) + } + } + + cur := 0 + sum := 0 + for i, date := range dates { + if date.After(time.Now()) { + cur = i + break + } + if _, ok := issueCreateMap[date]; ok { + if h.GetBurnoutChartDimension() == "total" { + sum += len(issueCreateMap[date]) + } + if h.GetBurnoutChartDimension() == "workTime" { + for _, issue := range issueCreateMap[date] { + if issue.ManHour != "" { + var manHour apistructs.IssueManHour + if err := json.Unmarshal([]byte(issue.ManHour), &manHour); err != nil { + return err + } + sum += int(manHour.EstimateTime) / 60 + } + } + } + } + + if _, ok := issueFinishMap[date]; ok { + if h.GetBurnoutChartDimension() == "total" { + sum -= len(issueFinishMap[date]) + } + if h.GetBurnoutChartDimension() == "workTime" { + for _, issue := range issueFinishMap[date] { + if issue.ManHour != "" { + var manHour apistructs.IssueManHour + if err := json.Unmarshal([]byte(issue.ManHour), &manHour); err != nil { + return err + } + sum -= int(manHour.ThisElapsedTime) / 60 + } + } + } + } + dateMap[date] = sum + } + + f.Type = "Chart" + f.Props = Props{ + ChartType: "line", + Title: "燃尽图", + PureChart: true, + Option: Option{ + XAxis: XAxis{ + Type: "category", + Data: func() []string { + ss := make([]string, 0, len(dates)) + for _, v := range dates { + ss = append(ss, v.Format("01-02")) + } + return ss + }(), + }, + YAxis: YAxis{ + Type: "value", + AxisLine: map[string]interface{}{ + "lineStyle": map[string]interface{}{ + "color": "rgba(48,38,71,0.30)", + }, + }, + AxisLabel: map[string]interface{}{ + "formatter": func() string { + if h.GetBurnoutChartDimension() == "total" { + return "{value} 个" + } + return "{value} h" + }(), + }, + }, + Legend: Legend{ + Show: true, + Bottom: true, + Data: []string{"实际燃尽"}, + }, + Tooltip: map[string]interface{}{ + "trigger": "axis", + }, + Series: []Series{ + { + Data: func() []int { + counts := make([]int, 0) + for _, v := range dates[:cur] { + counts = append(counts, dateMap[v]) + } + return counts + }(), + Name: "实际燃尽", + Type: "line", + Smooth: false, + ItemStyle: map[string]interface{}{ + "color": "#D84B65", + }, + MarkLine: MarkLine{ + Label: map[string]interface{}{"position": "middle"}, + LineStyle: map[string]interface{}{ + "color": "rgba(48,38,71,0.20)", + }, + Data: [][]Data{ + { + { + Name: "预设燃尽", + Coord: func() []string { + ss := make([]string, 0, 2) + ss = append(ss, dates[0].Format("01-02"), strconv.Itoa(dateMap[dates[0]])) + return ss + }(), + }, + { + Coord: func() []string { + ss := make([]string, 0, 2) + ss = append(ss, dates[len(dates)-1].Format("01-02"), "0") + return ss + }(), + }, + }, + }, + }, + }, + }, + }, + } + + return f.SetToProtocolComponent(c) +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChartFilter/model.go b/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChartFilter/model.go new file mode 100644 index 00000000000..d318bcb0278 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChartFilter/model.go @@ -0,0 +1,60 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package burnoutChartFilter + +import ( + "context" + "encoding/json" + + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/filter" +) + +type Filter struct { + filter.CommonFilter + base.DefaultProvider + + State State `json:"state,omitempty"` +} + +type State struct { + Conditions []filter.PropCondition `json:"conditions,omitempty"` + Values Values `json:"values,omitempty"` +} + +type Values struct { + Type string `json:"type"` + Dimension string `json:"dimension"` +} + +const OperationKeyFilter filter.OperationKey = "filter" +const OperationOwnerSelectMe filter.OperationKey = "ownerSelectMe" + +func (f *Filter) SetToProtocolComponent(c *cptype.Component) error { + b, err := json.Marshal(f) + if err != nil { + return err + } + return json.Unmarshal(b, &c) +} + +func (f *Filter) InitFromProtocol(ctx context.Context, c *cptype.Component) error { + b, err := json.Marshal(c) + if err != nil { + return err + } + return json.Unmarshal(b, f) +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChartFilter/render.go b/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChartFilter/render.go new file mode 100644 index 00000000000..85aa98e6ea0 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChartFilter/render.go @@ -0,0 +1,126 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package burnoutChartFilter + +import ( + "context" + + "github.com/erda-project/erda-infra/base/servicehub" + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda/apistructs" + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common" + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common/gshelper" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/filter" +) + +func init() { + base.InitProviderWithCreator(common.ScenarioKeyTestDashboard, "burnoutChartFilter", func() servicehub.Provider { + return &Filter{} + }) +} + +func (f *Filter) Render(ctx context.Context, c *cptype.Component, scenario cptype.Scenario, event cptype.ComponentEvent, gs *cptype.GlobalStateData) error { + if err := f.InitFromProtocol(ctx, c); err != nil { + return err + } + + f.Props = filter.Props{ + Delay: 500, + } + + f.Operations = map[filter.OperationKey]filter.Operation{ + OperationKeyFilter: { + Key: OperationKeyFilter, + Reload: true, + }, + OperationOwnerSelectMe: { + Key: OperationOwnerSelectMe, + Reload: true, + }, + } + + f.State = State{ + Conditions: []filter.PropCondition{ + { + CustomProps: map[string]interface{}{ + "mode": "single", + }, + Key: "type", + Label: "类型", + EmptyText: "全部", + Fixed: true, + Type: filter.PropConditionTypeSelect, + Options: []filter.PropConditionOption{ + { + Label: "需求", + Value: "requirement", + }, + { + Label: "任务", + Value: "task", + }, + }, + }, + { + CustomProps: map[string]interface{}{ + "mode": "single", + }, + Key: "dimension", + Label: "维度", + EmptyText: "全部", + Fixed: true, + Type: filter.PropConditionTypeSelect, + Options: []filter.PropConditionOption{ + { + Label: "按事项个数", + Value: "total", + }, + { + Label: "按事项工时", + Value: "workTime", + }, + }, + }, + }, + Values: Values{ + Type: func() string { + if event.Operation != cptype.OperationKey(f.Operations[OperationKeyFilter].Key) || + f.State.Values.Type == "" { + return "requirement" + } + return f.State.Values.Type + }(), + Dimension: func() string { + if event.Operation != cptype.OperationKey(f.Operations[OperationKeyFilter].Key) || + f.State.Values.Dimension == "" { + return "total" + } + return f.State.Values.Dimension + }(), + }, + } + + h := gshelper.NewGSHelper(gs) + h.SetBurnoutChartType(f.State.Values.Type) + h.SetBurnoutChartDimension(f.State.Values.Dimension) + + return f.SetToProtocolComponent(c) +} + +func (f *Filter) InitDefaultOperation(ctx context.Context, iterations []apistructs.Iteration) error { + + return nil +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/render.go b/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/render.go new file mode 100644 index 00000000000..2f7faf3e14c --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/render.go @@ -0,0 +1,24 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package chartBlock + +import ( + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +func init() { + base.InitProvider(common.ScenarioKeyTestDashboard, "chartBlock") +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/common/condition.go b/modules/dop/component-protocol/components/requirement-task-overview/common/condition.go new file mode 100644 index 00000000000..f945ab13552 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/common/condition.go @@ -0,0 +1,26 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +type FrontendConditions struct { + IterationID int64 `json:"iteration,omitempty"` + AssigneeIDs []string `json:"member,omitempty"` +} + +type FilterConditions struct { + Type string `json:"type,omitempty"` + Value []string `json:"value,omitempty"` + Time []int64 `json:"time,omitempty"` +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/common/gshelper/helper.go b/modules/dop/component-protocol/components/requirement-task-overview/common/gshelper/helper.go new file mode 100644 index 00000000000..2889dfe7f05 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/common/gshelper/helper.go @@ -0,0 +1,166 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package gshelper + +import ( + "encoding/json" + + "github.com/mitchellh/mapstructure" + + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda/apistructs" + "github.com/erda-project/erda/modules/dop/dao" +) + +type GSHelper struct { + gs *cptype.GlobalStateData +} + +func NewGSHelper(gs *cptype.GlobalStateData) *GSHelper { + return &GSHelper{gs: gs} +} + +func (h *GSHelper) SetIteration(l apistructs.Iteration) { + if h.gs == nil { + return + } + (*h.gs)["Iteration"] = l +} + +func (h *GSHelper) GetIteration() apistructs.Iteration { + if h.gs == nil { + return apistructs.Iteration{} + } + var res apistructs.Iteration + b, err := json.Marshal((*h.gs)["Iteration"]) + if err != nil { + return apistructs.Iteration{} + } + err = json.Unmarshal(b, &res) + if err != nil { + return apistructs.Iteration{} + } + return res +} + +func (h *GSHelper) SetMembers(l []apistructs.Member) { + if h.gs == nil { + return + } + (*h.gs)["Members"] = l +} + +func (h *GSHelper) GetMembers() []apistructs.Member { + if h.gs == nil { + return nil + } + res := make([]apistructs.Member, 0) + _ = assign((*h.gs)["Members"], &res) + return res +} + +func (h *GSHelper) SetIssueList(l []dao.IssueItem) { + if h.gs == nil { + return + } + (*h.gs)["IssueList"] = l +} + +func (h *GSHelper) GetIssueList() []dao.IssueItem { + if h.gs == nil { + return nil + } + res := make([]dao.IssueItem, 0) + b, err := json.Marshal((*h.gs)["IssueList"]) + if err != nil { + return nil + } + err = json.Unmarshal(b, &res) + if err != nil { + return nil + } + return res +} + +func (h *GSHelper) SetIssueStateList(l []dao.IssueState) { + if h.gs == nil { + return + } + (*h.gs)["IssueStateList"] = l +} + +func (h *GSHelper) GetIssueStateList() []dao.IssueState { + if h.gs == nil { + return nil + } + res := make([]dao.IssueState, 0) + _ = assign((*h.gs)["IssueStateList"], &res) + return res +} + +func (h *GSHelper) SetIssueStageList(l []apistructs.IssueStage) { + if h.gs == nil { + return + } + (*h.gs)["IssueStageList"] = l +} + +func (h *GSHelper) GetIssueStageList() []apistructs.IssueStage { + if h.gs == nil { + return nil + } + res := make([]apistructs.IssueStage, 0) + _ = assign((*h.gs)["IssueStageList"], &res) + return res +} + +func (h *GSHelper) SetBurnoutChartType(t string) { + if h.gs == nil { + return + } + (*h.gs)["BurnoutChartType"] = t +} + +func (h *GSHelper) GetBurnoutChartType() string { + if h.gs == nil { + return "" + } + var t string + _ = assign((*h.gs)["BurnoutChartType"], &t) + return t +} + +func (h *GSHelper) SetBurnoutChartDimension(t string) { + if h.gs == nil { + return + } + (*h.gs)["BurnoutChartDimension"] = t +} + +func (h *GSHelper) GetBurnoutChartDimension() string { + if h.gs == nil { + return "" + } + var t string + _ = assign((*h.gs)["BurnoutChartDimension"], &t) + return t +} + +func assign(src, dst interface{}) error { + if src == nil || dst == nil { + return nil + } + return mapstructure.Decode(src, dst) +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/common/util.go b/modules/dop/component-protocol/components/requirement-task-overview/common/util.go new file mode 100644 index 00000000000..9d851e8df28 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/common/util.go @@ -0,0 +1,52 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +import ( + "time" + + "github.com/erda-project/erda/modules/dop/dao" +) + +func DateTime(t time.Time) time.Time { + y, m, d := t.Date() + return time.Date(y, m, d, 0, 0, 0, 0, t.Location()) +} + +func IssueCountIf(issues []dao.IssueItem, fn func(issue *dao.IssueItem) bool) int { + sum := 0 + for _, v := range issues { + if fn(&v) { + sum++ + } + } + return sum +} + +func RangeDate(start, end time.Time) func() time.Time { + y, m, d := start.Date() + start = time.Date(y, m, d, 0, 0, 0, 0, start.Location()) + y, m, d = end.Date() + end = time.Date(y, m, d, 0, 0, 0, 0, end.Location()) + + return func() time.Time { + if start.After(end) { + return time.Time{} + } + date := start + start = start.AddDate(0, 0, 1) + return date + } +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/common/vars.go b/modules/dop/component-protocol/components/requirement-task-overview/common/vars.go new file mode 100644 index 00000000000..af2f3fe9935 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/common/vars.go @@ -0,0 +1,19 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package common + +const ( + ScenarioKeyTestDashboard = "requirement-task-overview" +) diff --git a/modules/dop/component-protocol/components/requirement-task-overview/container/info/model.go b/modules/dop/component-protocol/components/requirement-task-overview/container/info/model.go new file mode 100644 index 00000000000..00a5b8bbed2 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/container/info/model.go @@ -0,0 +1,57 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package info + +import ( + "context" + "encoding/json" + + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda/modules/dop/dao" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +type Info struct { + base.DefaultProvider + + Type string `json:"type"` + Data DataDetail `json:"data"` + Issues []dao.IssueItem `json:"-"` +} + +type DataDetail struct { + Data [][]Data `json:"data"` +} + +type Data struct { + Main string `json:"main"` + Sub string `json:"sub"` +} + +func (i *Info) SetToProtocolComponent(c *cptype.Component) error { + b, err := json.Marshal(i) + if err != nil { + return err + } + return json.Unmarshal(b, &c) +} + +func (i *Info) InitFromProtocol(ctx context.Context, c *cptype.Component) error { + b, err := json.Marshal(c) + if err != nil { + return err + } + return json.Unmarshal(b, i) +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/container/info/render.go b/modules/dop/component-protocol/components/requirement-task-overview/container/info/render.go new file mode 100644 index 00000000000..e9a22952315 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/container/info/render.go @@ -0,0 +1,106 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package info + +import ( + "context" + "strconv" + + "github.com/erda-project/erda-infra/base/servicehub" + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common" + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common/gshelper" + "github.com/erda-project/erda/modules/dop/dao" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +func init() { + base.InitProviderWithCreator(common.ScenarioKeyTestDashboard, "info", func() servicehub.Provider { + return &Info{} + }) +} + +func (i *Info) Render(ctx context.Context, c *cptype.Component, scenario cptype.Scenario, event cptype.ComponentEvent, gs *cptype.GlobalStateData) error { + if err := i.InitFromProtocol(ctx, c); err != nil { + return err + } + + h := gshelper.NewGSHelper(gs) + i.Issues = h.GetIssueList() + + stats := getStats(i.Issues) + i.Data.Data = [][]Data{ + { + { + Main: strconv.Itoa(stats.Unclose), + Sub: "未完成", + }, + { + Main: strconv.Itoa(stats.Expire), + Sub: "已到期", + }, + { + Main: strconv.Itoa(stats.Today), + Sub: "本日截止", + }, + { + Main: strconv.Itoa(stats.Week), + Sub: "本周截止", + }, + }, + { + { + Main: strconv.Itoa(stats.Month), + Sub: "本月截止", + }, + { + Main: strconv.Itoa(stats.Undefined), + Sub: "未指定截止日期", + }, + }, + } + + return i.SetToProtocolComponent(c) +} + +type Stats struct { + Unclose int `json:"unclose,omitempty"` + Expire int `json:"expire,omitempty"` + Today int `json:"today,omitempty"` + Week int `json:"week,omitempty"` + Month int `json:"month,omitempty"` + Undefined int `json:"undefined,omitempty"` +} + +func getStats(issues []dao.IssueItem) (s Stats) { + for _, v := range issues { + if v.FinishTime == nil { + s.Unclose++ + } + switch v.ExpiryStatus { + case dao.ExpireTypeExpired: + s.Expire++ + case dao.ExpireTypeExpireIn1Day: + s.Today++ + case dao.ExpireTypeExpireIn7Days: + s.Week++ + case dao.ExpireTypeExpireIn30Days: + s.Month++ + case dao.ExpireTypeUndefined: + s.Undefined++ + } + } + return +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/container/render.go b/modules/dop/component-protocol/components/requirement-task-overview/container/render.go new file mode 100644 index 00000000000..0efb6a49ee6 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/container/render.go @@ -0,0 +1,24 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package container + +import ( + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +func init() { + base.InitProvider(common.ScenarioKeyTestDashboard, "container") +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/container/simpleChart/model.go b/modules/dop/component-protocol/components/requirement-task-overview/container/simpleChart/model.go new file mode 100644 index 00000000000..430e13b55ee --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/container/simpleChart/model.go @@ -0,0 +1,65 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package simpleChart + +import ( + "context" + "encoding/json" + + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda/modules/dop/dao" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +type SimpleChart struct { + base.DefaultProvider + Issues []dao.IssueItem `json:"-"` + Type string `json:"type"` + Data Data `json:"data"` +} + +type Data struct { + Main string `json:"main"` + Sub string `json:"sub"` + CompareText string `json:"compareText"` + CompareValue string `json:"compareValue"` + Chart Chart `json:"chart"` +} + +type Chart struct { + XAxis []string `json:"xAxis"` + Series []SeriesData `json:"series"` +} + +type SeriesData struct { + Name string `json:"name"` + Data []int `json:"data"` +} + +func (s *SimpleChart) SetToProtocolComponent(c *cptype.Component) error { + b, err := json.Marshal(s) + if err != nil { + return err + } + return json.Unmarshal(b, &c) +} + +func (s *SimpleChart) InitFromProtocol(ctx context.Context, c *cptype.Component) error { + b, err := json.Marshal(c) + if err != nil { + return err + } + return json.Unmarshal(b, s) +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/container/simpleChart/render.go b/modules/dop/component-protocol/components/requirement-task-overview/container/simpleChart/render.go new file mode 100644 index 00000000000..c8c33ddffa8 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/container/simpleChart/render.go @@ -0,0 +1,110 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package simpleChart + +import ( + "context" + "strconv" + "time" + + "github.com/erda-project/erda-infra/base/servicehub" + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common" + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common/gshelper" + "github.com/erda-project/erda/modules/dop/dao" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +func init() { + base.InitProviderWithCreator(common.ScenarioKeyTestDashboard, "simpleChart", func() servicehub.Provider { + return &SimpleChart{} + }) +} + +func (s *SimpleChart) Render(ctx context.Context, c *cptype.Component, scenario cptype.Scenario, event cptype.ComponentEvent, gs *cptype.GlobalStateData) error { + h := gshelper.NewGSHelper(gs) + + if err := s.InitFromProtocol(ctx, c); err != nil { + return err + } + + s.Issues = h.GetIssueList() + s.Type = "SimpleChart" + s.Data = Data{ + Main: strconv.Itoa(len(s.Issues)), + Sub: "总数", + CompareText: "较昨日", + CompareValue: func() string { + count := common.IssueCountIf(s.Issues, func(issue *dao.IssueItem) bool { + return common.DateTime(issue.CreatedAt).Equal(common.DateTime(time.Now())) + }) + if count > 0 { + return "+" + strconv.Itoa(count) + } + return strconv.Itoa(count) + }(), + } + + dates := make([]time.Time, 0) + dateMap := make(map[time.Time]int) + itr := h.GetIteration() + if itr.StartedAt == nil || itr.FinishedAt == nil { + return nil + } + for rd := common.RangeDate(*itr.StartedAt, *itr.FinishedAt); ; { + date := rd() + if date.IsZero() { + break + } + if date.After(common.DateTime(time.Now())) { + break + } + dates = append(dates, date) + dateMap[date] = 0 + } + for k := range dateMap { + for _, issue := range s.Issues { + created := common.DateTime(issue.CreatedAt) + if created.Equal(common.DateTime(k)) { + dateMap[created]++ + } + } + } + s.Data.Chart = Chart{ + XAxis: func() []string { + ss := make([]string, 0, len(dates)) + for _, v := range dates { + ss = append(ss, v.Format("2006-01-02")) + } + return ss + }(), + Series: []SeriesData{ + { + Name: "需求&任务总数", + Data: func() []int { + counts := make([]int, 0, len(dates)) + sum := 0 + for _, v := range dates { + sum += dateMap[v] + counts = append(counts, sum) + } + return counts + }(), + }, + }, + } + + return s.SetToProtocolComponent(c) +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/distributePage/render.go b/modules/dop/component-protocol/components/requirement-task-overview/distributePage/render.go new file mode 100644 index 00000000000..b8aa3fc0823 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/distributePage/render.go @@ -0,0 +1,24 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package summaryPage + +import ( + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +func init() { + base.InitProvider(common.ScenarioKeyTestDashboard, "distributePage") +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/distributePage/topFilter/model.go b/modules/dop/component-protocol/components/requirement-task-overview/distributePage/topFilter/model.go new file mode 100644 index 00000000000..dfc4941101d --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/distributePage/topFilter/model.go @@ -0,0 +1,59 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package topFilter + +import ( + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda/apistructs" + "github.com/erda-project/erda/bundle" + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common" + "github.com/erda-project/erda/modules/dop/dao" + "github.com/erda-project/erda/modules/dop/services/issue" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/filter" +) + +type ComponentFilter struct { + filter.CommonFilter + base.DefaultProvider + + sdk *cptype.SDK + bdl *bundle.Bundle + issueSvc *issue.Issue + State State `json:"state,omitempty"` + InParams InParams `json:"-"` + + // local vars + Iterations []apistructs.Iteration `json:"-"` + Members []apistructs.Member `json:"-"` + IssueList []dao.IssueItem `json:"-"` + IssueStateList []dao.IssueState `json:"-"` + Stages []apistructs.IssueStage `json:"-"` +} + +type State struct { + Conditions []filter.PropCondition `json:"conditions,omitempty"` + Values common.FrontendConditions `json:"values,omitempty"` + Base64UrlQueryParams string `json:"filter__urlQuery,omitempty"` +} + +type InParams struct { + FrontEndProjectID string `json:"projectId,omitempty"` + FrontendUrlQuery string `json:"filter__urlQuery,omitempty"` + ProjectID uint64 +} + +const OperationKeyFilter filter.OperationKey = "filter" +const OperationOwnerSelectMe filter.OperationKey = "ownerSelectMe" diff --git a/modules/dop/component-protocol/components/requirement-task-overview/distributePage/topFilter/render.go b/modules/dop/component-protocol/components/requirement-task-overview/distributePage/topFilter/render.go new file mode 100644 index 00000000000..d1668a85c2e --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/distributePage/topFilter/render.go @@ -0,0 +1,284 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package topFilter + +import ( + "context" + "encoding/base64" + "encoding/json" + "sort" + "strconv" + "time" + + "github.com/erda-project/erda-infra/base/servicehub" + "github.com/erda-project/erda-infra/providers/component-protocol/cptype" + "github.com/erda-project/erda-infra/providers/component-protocol/utils/cputil" + "github.com/erda-project/erda/apistructs" + "github.com/erda-project/erda/bundle" + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common" + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common/gshelper" + "github.com/erda-project/erda/modules/dop/component-protocol/types" + "github.com/erda-project/erda/modules/dop/services/issue" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/filter" +) + +func init() { + base.InitProviderWithCreator(common.ScenarioKeyTestDashboard, "topFilter", + func() servicehub.Provider { return &ComponentFilter{} }) +} + +func (f *ComponentFilter) InitFromProtocol(ctx context.Context, c *cptype.Component) error { + // component 序列化 + b, err := json.Marshal(c) + if err != nil { + return err + } + if err := json.Unmarshal(b, f); err != nil { + return err + } + + // sdk + f.sdk = cputil.SDK(ctx) + f.bdl = ctx.Value(types.GlobalCtxKeyBundle).(*bundle.Bundle) + f.issueSvc = ctx.Value(types.IssueService).(*issue.Issue) + return f.setInParams(ctx) +} + +func (f *ComponentFilter) setInParams(ctx context.Context) error { + b, err := json.Marshal(cputil.SDK(ctx).InParams) + if err != nil { + return err + } + if err := json.Unmarshal(b, &f.InParams); err != nil { + return err + } + + f.InParams.ProjectID, err = strconv.ParseUint(f.InParams.FrontEndProjectID, 10, 64) + return err +} + +func (f *ComponentFilter) SetToProtocolComponent(c *cptype.Component) error { + b, err := json.Marshal(f) + if err != nil { + return err + } + return json.Unmarshal(b, &c) +} + +func (f *ComponentFilter) Render(ctx context.Context, c *cptype.Component, scenario cptype.Scenario, event cptype.ComponentEvent, gs *cptype.GlobalStateData) error { + if err := f.InitFromProtocol(ctx, c); err != nil { + return err + } + + iterations, iterationOptions, err := f.getPropIterationsOptions() + if err != nil { + return err + } + + switch event.Operation { + case cptype.InitializeOperation, cptype.RenderingOperation: + if err = f.InitDefaultOperation(ctx, f.Iterations); err != nil { + return err + } + case cptype.OperationKey(f.Operations[OperationKeyFilter].Key): + } + + data, err := f.issueSvc.GetAllIssuesByProject(apistructs.IssueListRequest{ + Type: []apistructs.IssueType{ + apistructs.IssueTypeRequirement, + apistructs.IssueTypeTask, + }, + ProjectID: f.InParams.ProjectID, + IterationIDs: []int64{f.State.Values.IterationID}, + Assignees: f.State.Values.AssigneeIDs, + }) + if err != nil { + return err + } + + projectMemberOptions, err := f.getProjectMemberOptions() + if err != nil { + return err + } + + f.State.Conditions = []filter.PropCondition{ + { + EmptyText: "全部", + Fixed: true, + Key: "iteration", + Label: "迭代", + Options: iterationOptions, + Type: filter.PropConditionTypeSelect, + Required: true, + CustomProps: map[string]interface{}{ + "mode": "single", + }, + HaveFilter: true, + }, + { + EmptyText: "全部", + Fixed: true, + Key: "member", + Label: "成员", + Options: projectMemberOptions, + Type: filter.PropConditionTypeSelect, + HaveFilter: true, + }, + } + + f.IssueList = data + states, err := f.issueSvc.GetIssuesStatesByProjectID(f.InParams.ProjectID, apistructs.IssueTypeBug) + if err != nil { + return err + } + f.IssueStateList = states + + orgID, err := strconv.Atoi(f.sdk.Identity.OrgID) + if err != nil { + return err + } + + stages, err := f.issueSvc.GetIssueStage(&apistructs.IssueStageRequest{ + OrgID: int64(orgID), + IssueType: apistructs.IssueTypeBug, + }) + if err != nil { + return err + } + f.Stages = stages + + helper := gshelper.NewGSHelper(gs) + helper.SetIteration(iterations[f.State.Values.IterationID]) + helper.SetMembers(f.Members) + helper.SetIssueList(f.IssueList) + helper.SetIssueStateList(f.IssueStateList) + helper.SetIssueStageList(f.Stages) + + urlParam, err := f.generateUrlQueryParams() + if err != nil { + return err + } + f.State.Base64UrlQueryParams = urlParam + return f.SetToProtocolComponent(c) +} + +func (f *ComponentFilter) InitDefaultOperation(ctx context.Context, iterations []apistructs.Iteration) error { + f.Props = filter.Props{ + Delay: 300, + } + f.Operations = map[filter.OperationKey]filter.Operation{ + OperationKeyFilter: { + Key: OperationKeyFilter, + Reload: true, + }, + OperationOwnerSelectMe: { + Key: OperationOwnerSelectMe, + Reload: true, + }, + } + if f.InParams.FrontendUrlQuery != "" { + b, err := base64.StdEncoding.DecodeString(f.InParams.FrontendUrlQuery) + if err != nil { + return err + } + f.State.Values = common.FrontendConditions{} + if err = json.Unmarshal(b, &f.State.Values); err != nil { + return err + } + } else { + f.State.Values.IterationID = defaultIterationRetriever(iterations) + } + return nil +} + +func defaultIterationRetriever(iterations []apistructs.Iteration) int64 { + sort.Slice(iterations, func(i, j int) bool { + return iterations[i].ID > iterations[j].ID + }) + for _, v := range iterations { + if !time.Now().Before(*v.StartedAt) && + !time.Now().After(*v.FinishedAt) { + return v.ID + } + } + return -1 +} + +func (f *ComponentFilter) generateUrlQueryParams() (string, error) { + fb, err := json.Marshal(f.State.Values) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(fb), nil +} + +func (f *ComponentFilter) getPropIterationsOptions() (map[int64]apistructs.Iteration, []filter.PropConditionOption, error) { + iterations, err := f.bdl.ListProjectIterations(apistructs.IterationPagingRequest{PageNo: 1, PageSize: 1000, ProjectID: f.InParams.ProjectID, WithoutIssueSummary: true}, f.sdk.Identity.OrgID) + if err != nil { + return nil, nil, err + } + f.Iterations = append(iterations, apistructs.Iteration{ + ID: -1, + Title: "待处理", + StartedAt: func() *time.Time { + t := time.Now().AddDate(0, -1, 0) + return &t + }(), + FinishedAt: func() *time.Time { + t := time.Now().AddDate(0, -1, 0) + return &t + }(), + }) + var options []filter.PropConditionOption + itrMap := make(map[int64]apistructs.Iteration) + for _, iteration := range f.Iterations { + options = append(options, filter.PropConditionOption{ + Label: iteration.Title, + Value: iteration.ID, + }) + itrMap[iteration.ID] = iteration + } + return itrMap, options, nil +} + +func (f *ComponentFilter) getProjectMemberOptions() ([]filter.PropConditionOption, error) { + members, err := f.bdl.ListMembers(apistructs.MemberListRequest{ + ScopeType: apistructs.ProjectScope, + ScopeID: int64(f.InParams.ProjectID), + PageNo: 1, + PageSize: 500, + }) + if err != nil { + return nil, err + } + f.Members = members + var results []filter.PropConditionOption + for _, member := range members { + results = append(results, filter.PropConditionOption{ + Label: func() string { + if member.Nick != "" { + return member.Nick + } + if member.Name != "" { + return member.Name + } + return member.Mobile + }(), + Value: member.UserID, + }) + } + return results, nil +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/page/render.go b/modules/dop/component-protocol/components/requirement-task-overview/page/render.go new file mode 100644 index 00000000000..ea4ea3b36c0 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/page/render.go @@ -0,0 +1,24 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package page + +import ( + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +func init() { + base.InitProvider(common.ScenarioKeyTestDashboard, "page") +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/pageContent/render.go b/modules/dop/component-protocol/components/requirement-task-overview/pageContent/render.go new file mode 100644 index 00000000000..986d2f6ab71 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/pageContent/render.go @@ -0,0 +1,24 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pageContent + +import ( + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +func init() { + base.InitProvider(common.ScenarioKeyTestDashboard, "pageContent") +} diff --git a/modules/dop/component-protocol/components/requirement-task-overview/scenario.go b/modules/dop/component-protocol/components/requirement-task-overview/scenario.go new file mode 100644 index 00000000000..acd583a4917 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/scenario.go @@ -0,0 +1,29 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package requirement_task_overview + +import ( + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/chartBlock" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChart" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/chartBlock/burnoutChartFilter" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/container" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/container/info" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/container/simpleChart" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/distributePage" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/distributePage/topFilter" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/page" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/pageContent" + _ "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/tabs" +) diff --git a/modules/dop/component-protocol/components/requirement-task-overview/tabs/render.go b/modules/dop/component-protocol/components/requirement-task-overview/tabs/render.go new file mode 100644 index 00000000000..92bb3fabe76 --- /dev/null +++ b/modules/dop/component-protocol/components/requirement-task-overview/tabs/render.go @@ -0,0 +1,24 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tabs + +import ( + "github.com/erda-project/erda/modules/dop/component-protocol/components/requirement-task-overview/common" + "github.com/erda-project/erda/modules/openapi/component-protocol/components/base" +) + +func init() { + base.InitProvider(common.ScenarioKeyTestDashboard, "tabs") +} diff --git a/modules/dop/component-protocol/scenarios/requirement-task-overview.yml b/modules/dop/component-protocol/scenarios/requirement-task-overview.yml new file mode 100644 index 00000000000..a15c0de37a6 --- /dev/null +++ b/modules/dop/component-protocol/scenarios/requirement-task-overview.yml @@ -0,0 +1,265 @@ +scenario: requirement-task-overview + +hierarchy: + root: page + structure: + page: + - tabs + - pageContent + pageContent: + - topFilter + - container + - chartBlock + distributePage: + - topFilter + container: + left: simpleChart + right: info + chartBlock: + filter: burnoutChartFilter + children: + - burnoutChart + +components: + container: + type: LRContainer + topFilter: + type: ContractiveFilter + props: + delay: 300 + state: + conditions: + - emptyText: 全部 + fixed: true + key: iteration + label: 迭代 + options: + - label: '1.6' + value: '1.6' + - label: '1.5' + value: '1.5' + placeholder: 选择优先级 + type: select + - emptyText: 全部 + haveFilter: true + fixed: true + key: members + label: 成员 + options: + - label: 陈泳伸 + value: '1000014' + - label: 徐伟 + value: '1000045' + quickSelect: + label: 选择自己 + operationKey: ownerSelectMe + type: select + values: + priorities: + - HIGH + operations: + filter: + key: filter + reload: true + ownerSelectMe: + key: ownerSelectMe + reload: true + chartBlock: + type: ChartBlock + data: + title: 燃尽图 + burnoutChart: + type: Chart + props: + chartType: line + title: 燃尽图 + option: + xAxis: + type: category + data: + - 09-01 + - 09-02 + - 09-03 + - 09-04 + - 09-05 + - 09-06 + - 09-07 + - 09-08 + - 09-09 + - 09-10 + - 09-11 + - 09-12 + - 09-13 + - 09-14 + - 09-15 + - 09-16 + - 09-17 + - 09-18 + - 09-19 + - 09-20 + - 09-21 + - 09-22 + - 09-23 + - 09-24 + - 09-25 + - 09-26 + - 09-27 + - 09-28 + yAxis: + type: value + axisLine: + lineStyle: + color: 'rgba(48,38,71,0.20)' + axisLabel: + formatter: '{value} 个' + legend: + show: true + bottom: true + data: + - 实际燃尽 + tooltip: + trigger: axis + series: + - data: + - 150 + - 160 + - 138 + - 96 + name: 实际燃尽 + type: line + smooth: false + itemStyle: + color: '#D84B65' + markLine: + label: + position: middle + lineStyle: + type: solid + width: 2 + color: 'rgba(48,38,71,0.20)' + data: + - - name: 预设燃尽 + coord: + - 09-01 + - '150' + - coord: + - 09-07 + - '0' + symbol: none + pureChart: true + burnoutChartFilter: + type: ContractiveFilter + props: + delay: 500 + state: + conditions: + - emptyText: 全部 + key: type + fixed: true + label: 类型 + options: + - label: 需求 + value: requirement + - label: 任务 + value: task + placeholder: 选择优先级 + type: select + - emptyText: 全部 + key: dimension + fixed: true + label: 维度 + options: + - label: 按事项个数 + value: total + - label: 按事项状态 + value: state + placeholder: 选择优先级 + type: select + values: + type: requirement + dimension: total + operations: + filter: + key: filter + reload: true + ownerSelectMe: + key: ownerSelectMe + reload: true + simpleChart: + type: SimpleChart + data: + main: '2321' + sub: 总数 + compareText: 较昨日 + compareValue: '+4' + chart: + xAxis: + - '2021-01-20' + - '2021-01-21' + - '2021-01-22' + - '2021-01-23' + - '2021-01-24' + - '2021-01-25' + - '2021-01-26' + series: + - name: 需求&任务总数 + data: + - 820 + - 932 + - 901 + - 934 + - 10 + - 0 + - 0 + info: + type: TextBlockGroup + data: + data: + - - main: '233' + sub: 未完成 + - main: '233' + sub: 已到期 + - main: '233' + sub: 本日截止 + - main: '233' + sub: 本周截止 + - - main: '23' + sub: 本月截止 + - main: '5' + sub: 未指定截止日期 + tabs: + type: Radio + name: tabs + props: + visible: false + buttonStyle: solid + options: + - key: summary + text: 概览 + operations: + click: + reload: false + key: summary + command: + key: changeScenario + scenarioType: issue-summary-dashboard + scenarioKey: issue-summary-dashboard + - key: distribute + text: 分布 + operations: + click: + reload: false + key: distribute + command: + key: changeScenario + scenarioType: issue-summary-distribute + scenarioKey: issue-summary-distribute + radioType: button + state: + value: summary + data: { } + page: + type: Container + pageContent: + type: Container + diff --git a/modules/dop/services/issue/issue.go b/modules/dop/services/issue/issue.go index 56f706e4f3d..2123e174209 100644 --- a/modules/dop/services/issue/issue.go +++ b/modules/dop/services/issue/issue.go @@ -1580,10 +1580,6 @@ func (svc *Issue) GetIssuesByStates(req apistructs.WorkbenchRequest) (map[uint64 func (svc *Issue) GetAllIssuesByProject(req apistructs.IssueListRequest) ([]dao.IssueItem, error) { return svc.db.GetAllIssuesByProject(req) - // if err != nil { - // return data, nil - // } - // return data } func (svc *Issue) GetIssuesStatesByProjectID(projectID uint64, issueType apistructs.IssueType) ([]dao.IssueState, error) {