diff --git a/db/zentao.sql b/db/zentao.sql index 4a57c7993f..784bb6ba55 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -809,7 +809,10 @@ CREATE TABLE IF NOT EXISTS `zt_doc` ( `template` varchar(30) NOT NULL DEFAULT '', `templateType` varchar(30) NOT NULL DEFAULT '', `templateDesc` text NULL, + `objects` text NULL, `chapterType` varchar(30) NOT NULL DEFAULT '', + `cycle` char(10) NOT NULL DEFAULT '', + `cycleConfig` text NULL, `module` varchar(30) NOT NULL DEFAULT '', `title` varchar(255) NOT NULL DEFAULT '', `keywords` varchar(255) NOT NULL DEFAULT '', diff --git a/module/action/config.php b/module/action/config.php index 60ad91f7aa..6a87cfd412 100755 --- a/module/action/config.php +++ b/module/action/config.php @@ -92,6 +92,7 @@ $config->action->needGetRelateField = ',branch,story,epic,requirement,prod $config->action->noLinkModules = ',doclib,module,webhook,gitlab,instance,gitea,gogs,sonarqube,pipeline,jenkins,kanban,kanbanspace,kanbancolumn,kanbanlane,kanbanregion,kanbancard,execution,project,traincategory,apistruct,program,product,user,entry,repo,pivot,scene,boardspace,auditplan,auditresult,productline,chapter,doc,'; $config->action->ignoreObjectType4Dynamic = 'kanbanregion,kanbanlane,kanbancolumn'; $config->action->ignoreActions4Dynamic = 'disconnectxuanxuan,reconnectxuanxuan,loginxuanxuan,logoutxuanxuan,editmr,removemr,syncdoingbyticket,syncdoingbystory,syncdoingbyuserstory,syncdoingbyepic,syncdoingbytask,syncdoingbybug,syncdoingbytodo,syncdoingbydemand'; +if(in_array($config->edition, array('open', 'biz'))) $config->action->ignoreObjectType4Dynamic .= ',reporttemplate'; $config->action->latestDateList = array('today', 'yesterday', 'thisWeek', 'lastWeek', 'thisMonth'); diff --git a/module/action/model.php b/module/action/model.php index 455f0b9db5..b2598e85e5 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -127,7 +127,7 @@ class actionModel extends model $this->file->updateObjectID($uid, $objectID, $objectType); - $this->loadModel('message')->send(strtolower($objectType), $objectID, $actionType, $actionID, $actor, $extra); + if(empty($this->app->installing) && empty($this->app->upgrading)) $this->loadModel('message')->send(strtolower($objectType), $objectID, $actionType, $actionID, $actor, $extra); $this->saveIndex($objectType, $objectID, $actionType); diff --git a/module/install/control.php b/module/install/control.php index 4419a8fb5c..1703bcf66c 100644 --- a/module/install/control.php +++ b/module/install/control.php @@ -309,6 +309,7 @@ class install extends control $mode = form::data()->get()->mode; $this->loadModel('setting')->setItem('system.common.global.mode', $mode); $this->loadModel('custom')->disableFeaturesByMode($mode); + $this->loadModel('weekly')->addBuiltinWeeklyTemplate(); return $this->send(array('result' => 'success', 'load' => inlink('step5'))); } diff --git a/module/weekly/config.php b/module/weekly/config.php new file mode 100644 index 0000000000..d503e95475 --- /dev/null +++ b/module/weekly/config.php @@ -0,0 +1,8 @@ +weekly->charts['weekly_term'] = array('date' => '$thisWeek'); +$config->weekly->charts['weekly_staff'] = array('date' => '$thisWeek'); +$config->weekly->charts['project_progress_summary'] = array(); +$config->weekly->charts['task_basicStatistic_finished'] = array('date' => '$thisWeek'); +$config->weekly->charts['task_basicStatistic_unfinished'] = array('date' => '$thisWeek'); +$config->weekly->charts['task_basicStatistic_workplan'] = array('date' => '$nextWeek'); +$config->weekly->charts['project_basicStatistic_workload'] = array(); diff --git a/module/weekly/lang/de.php b/module/weekly/lang/de.php index a93317c186..f13c5f21ce 100644 --- a/module/weekly/lang/de.php +++ b/module/weekly/lang/de.php @@ -22,10 +22,11 @@ $lang->weekly->postponed = 'Work unfinished this week'; $lang->weekly->nextWeek = 'Work planned for next week'; $lang->weekly->workloadByType = 'Workload Summary'; -$lang->weekly->term = 'Reporting Cycle'; -$lang->weekly->project = $lang->projectCommon . ' Name'; -$lang->weekly->master = 'Project Manager '; -$lang->weekly->staff = 'Weekly Effort'; +$lang->weekly->term = 'Reporting Cycle'; +$lang->weekly->project = $lang->projectCommon . ' Name'; +$lang->weekly->master = 'Project Manager '; +$lang->weekly->staff = 'Weekly Effort'; +$lang->weekly->projectTemplate = "{$lang->projectCommon} Weekly Report Template"; $lang->weekly->weekDesc = 'Week % s (% s ~% s)'; $lang->weekly->progress = 'Progress of the ' . $lang->projectCommon; @@ -38,10 +39,15 @@ $lang->weekly->ac = 'Actual Cost(AC)'; $lang->weekly->sv = 'Schedule Variance(SV%)'; $lang->weekly->cv = 'Cost Variance(CV%)'; -$lang->weekly->totalCount = 'Total : %u tasks'; +$lang->weekly->totalCount = 'Total : %u tasks'; +$lang->weekly->builtinDesc = "The system's built-in {$lang->projectCommon} weekly report template automatically generates this week's report under the {$lang->projectCommon} every Monday."; $lang->weekly->exportWeeklyReport = 'Export Weekly Report'; +$lang->weekly->builtInScopes = array(); +$lang->weekly->builtInScopes['rnd'] = array(); +$lang->weekly->builtInScopes['rnd']['project'] = 'Project'; + $lang->weekly->reportHelpNotice = <<PV Planned Value Calculation method: @@ -143,3 +149,5 @@ Calculation method: SV(%) = -1 * (1 - (EV / PV))%

CV(%) Schedule Variance

Calculation method: CV(%) = -1 * (1 - (EV / AC))% EOD; + +$lang->weekly->builtinRawContent = '{"type":"page","meta":{"id":"DzzHUxGuzi","title":"Project Weekly Report Template","createDate":1757395000965,"tags":[]},"blocks":{"type":"block","id":"cJZg7QCPpE","flavour":"affine:page","version":2,"props":{"title":{"$blocksuite:internal:text$":true,"delta":[{"insert":"Project Weekly Report Template"}]}},"children":[{"type":"block","id":"aS-yFwCosD","flavour":"affine:note","version":1,"props":{"xywh":"[0,0,498,92]","background":"--affine-note-background-white","index":"a0","lockedBySelf":false,"hidden":false,"displayMode":"both","edgeless":{"style":{"borderRadius":8,"borderSize":4,"borderStyle":"none","shadowType":"--affine-note-shadow-box"}}},"children":[{"type":"block","id":"YCy8XkfbbP","flavour":"affine:paragraph","version":1,"props":{"align":"left","type":"text","text":{"$blocksuite:internal:text$":true,"delta":[{"insert":" ","attributes":{"holder":{"id":"tsoxyETETF","name":"weekly_term","text":"Report Term","hint":"Filter: “Date Range”BetweenThis Week","data":{"type":"weekly_term","blockID":2746,"hint":"Filter: “Date Range”BetweenThis Week","text":"Report Term"}}}},{"insert":" weekly report: "},{"insert":" ","attributes":{"holder":{"id":"q1YcO2kmC7","name":"property_name","text":"Name","hint":"Name"}}},{"insert":" is managed by the project manager "},{"insert":" ","attributes":{"holder":{"id":"eefTgQguvL","name":"property_PM","text":"Manager","hint":"Manager"}}},{"insert":", the number of people involved is "},{"insert":" ","attributes":{"holder":{"id":"jwupyH0HTa","name":"weekly_staff","text":"Staff Number","hint":"Filter: “Date Range”BetweenThis Week","data":{"type":"weekly_staff","blockID":2747,"hint":"Filter: “Date Range”BetweenThis Week","text":"Staff Number"}}}},{"insert":"."}]},"collapsed":false},"children":[]},{"type":"block","id":"cL0cyfMcnI","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=project_progress_summary&blockID=__TML_ZENTAOCHART__{project_progress_summary}","clearBeforeLoad":false,"isTemplate":true,"title":"Project Progress Summary"}},"children":[]},{"type":"block","id":"FqBsfENZPf","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=task_basicStatistic_finished&blockID=__TML_ZENTAOCHART__{task_basicStatistic_finished}","clearBeforeLoad":false,"isTemplate":true,"title":"Finished Tasks"}},"children":[]},{"type":"block","id":"egherwGMIV","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=task_basicStatistic_unfinished&blockID=__TML_ZENTAOCHART__{task_basicStatistic_unfinished}","clearBeforeLoad":false,"isTemplate":true,"title":"Unfinished Tasks"}},"children":[]},{"type":"block","id":"xfmvHk6o3o","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=task_basicStatistic_workplan&blockID=__TML_ZENTAOCHART__{task_basicStatistic_workplan}","clearBeforeLoad":false,"isTemplate":true,"title":"Work Plan"}},"children":[]},{"type":"block","id":"ZZ4Jsuq4Bq","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=project_basicStatistic_workload&blockID=__TML_ZENTAOCHART__{project_basicStatistic_workload}","clearBeforeLoad":false,"isTemplate":true,"title":"Project Plan Workload Statistic"}},"children":[]},{"type":"block","id":"BPy4La92az","flavour":"affine:paragraph","version":1,"props":{"align":"left","type":"text","text":{"$blocksuite:internal:text$":true,"delta":[]},"collapsed":false},"children":[]}]}]}}'; diff --git a/module/weekly/lang/en.php b/module/weekly/lang/en.php index a93317c186..f13c5f21ce 100644 --- a/module/weekly/lang/en.php +++ b/module/weekly/lang/en.php @@ -22,10 +22,11 @@ $lang->weekly->postponed = 'Work unfinished this week'; $lang->weekly->nextWeek = 'Work planned for next week'; $lang->weekly->workloadByType = 'Workload Summary'; -$lang->weekly->term = 'Reporting Cycle'; -$lang->weekly->project = $lang->projectCommon . ' Name'; -$lang->weekly->master = 'Project Manager '; -$lang->weekly->staff = 'Weekly Effort'; +$lang->weekly->term = 'Reporting Cycle'; +$lang->weekly->project = $lang->projectCommon . ' Name'; +$lang->weekly->master = 'Project Manager '; +$lang->weekly->staff = 'Weekly Effort'; +$lang->weekly->projectTemplate = "{$lang->projectCommon} Weekly Report Template"; $lang->weekly->weekDesc = 'Week % s (% s ~% s)'; $lang->weekly->progress = 'Progress of the ' . $lang->projectCommon; @@ -38,10 +39,15 @@ $lang->weekly->ac = 'Actual Cost(AC)'; $lang->weekly->sv = 'Schedule Variance(SV%)'; $lang->weekly->cv = 'Cost Variance(CV%)'; -$lang->weekly->totalCount = 'Total : %u tasks'; +$lang->weekly->totalCount = 'Total : %u tasks'; +$lang->weekly->builtinDesc = "The system's built-in {$lang->projectCommon} weekly report template automatically generates this week's report under the {$lang->projectCommon} every Monday."; $lang->weekly->exportWeeklyReport = 'Export Weekly Report'; +$lang->weekly->builtInScopes = array(); +$lang->weekly->builtInScopes['rnd'] = array(); +$lang->weekly->builtInScopes['rnd']['project'] = 'Project'; + $lang->weekly->reportHelpNotice = <<PV Planned Value Calculation method: @@ -143,3 +149,5 @@ Calculation method: SV(%) = -1 * (1 - (EV / PV))%

CV(%) Schedule Variance

Calculation method: CV(%) = -1 * (1 - (EV / AC))% EOD; + +$lang->weekly->builtinRawContent = '{"type":"page","meta":{"id":"DzzHUxGuzi","title":"Project Weekly Report Template","createDate":1757395000965,"tags":[]},"blocks":{"type":"block","id":"cJZg7QCPpE","flavour":"affine:page","version":2,"props":{"title":{"$blocksuite:internal:text$":true,"delta":[{"insert":"Project Weekly Report Template"}]}},"children":[{"type":"block","id":"aS-yFwCosD","flavour":"affine:note","version":1,"props":{"xywh":"[0,0,498,92]","background":"--affine-note-background-white","index":"a0","lockedBySelf":false,"hidden":false,"displayMode":"both","edgeless":{"style":{"borderRadius":8,"borderSize":4,"borderStyle":"none","shadowType":"--affine-note-shadow-box"}}},"children":[{"type":"block","id":"YCy8XkfbbP","flavour":"affine:paragraph","version":1,"props":{"align":"left","type":"text","text":{"$blocksuite:internal:text$":true,"delta":[{"insert":" ","attributes":{"holder":{"id":"tsoxyETETF","name":"weekly_term","text":"Report Term","hint":"Filter: “Date Range”BetweenThis Week","data":{"type":"weekly_term","blockID":2746,"hint":"Filter: “Date Range”BetweenThis Week","text":"Report Term"}}}},{"insert":" weekly report: "},{"insert":" ","attributes":{"holder":{"id":"q1YcO2kmC7","name":"property_name","text":"Name","hint":"Name"}}},{"insert":" is managed by the project manager "},{"insert":" ","attributes":{"holder":{"id":"eefTgQguvL","name":"property_PM","text":"Manager","hint":"Manager"}}},{"insert":", the number of people involved is "},{"insert":" ","attributes":{"holder":{"id":"jwupyH0HTa","name":"weekly_staff","text":"Staff Number","hint":"Filter: “Date Range”BetweenThis Week","data":{"type":"weekly_staff","blockID":2747,"hint":"Filter: “Date Range”BetweenThis Week","text":"Staff Number"}}}},{"insert":"."}]},"collapsed":false},"children":[]},{"type":"block","id":"cL0cyfMcnI","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=project_progress_summary&blockID=__TML_ZENTAOCHART__{project_progress_summary}","clearBeforeLoad":false,"isTemplate":true,"title":"Project Progress Summary"}},"children":[]},{"type":"block","id":"FqBsfENZPf","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=task_basicStatistic_finished&blockID=__TML_ZENTAOCHART__{task_basicStatistic_finished}","clearBeforeLoad":false,"isTemplate":true,"title":"Finished Tasks"}},"children":[]},{"type":"block","id":"egherwGMIV","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=task_basicStatistic_unfinished&blockID=__TML_ZENTAOCHART__{task_basicStatistic_unfinished}","clearBeforeLoad":false,"isTemplate":true,"title":"Unfinished Tasks"}},"children":[]},{"type":"block","id":"xfmvHk6o3o","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=task_basicStatistic_workplan&blockID=__TML_ZENTAOCHART__{task_basicStatistic_workplan}","clearBeforeLoad":false,"isTemplate":true,"title":"Work Plan"}},"children":[]},{"type":"block","id":"ZZ4Jsuq4Bq","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=project_basicStatistic_workload&blockID=__TML_ZENTAOCHART__{project_basicStatistic_workload}","clearBeforeLoad":false,"isTemplate":true,"title":"Project Plan Workload Statistic"}},"children":[]},{"type":"block","id":"BPy4La92az","flavour":"affine:paragraph","version":1,"props":{"align":"left","type":"text","text":{"$blocksuite:internal:text$":true,"delta":[]},"collapsed":false},"children":[]}]}]}}'; diff --git a/module/weekly/lang/fr.php b/module/weekly/lang/fr.php index a93317c186..f13c5f21ce 100644 --- a/module/weekly/lang/fr.php +++ b/module/weekly/lang/fr.php @@ -22,10 +22,11 @@ $lang->weekly->postponed = 'Work unfinished this week'; $lang->weekly->nextWeek = 'Work planned for next week'; $lang->weekly->workloadByType = 'Workload Summary'; -$lang->weekly->term = 'Reporting Cycle'; -$lang->weekly->project = $lang->projectCommon . ' Name'; -$lang->weekly->master = 'Project Manager '; -$lang->weekly->staff = 'Weekly Effort'; +$lang->weekly->term = 'Reporting Cycle'; +$lang->weekly->project = $lang->projectCommon . ' Name'; +$lang->weekly->master = 'Project Manager '; +$lang->weekly->staff = 'Weekly Effort'; +$lang->weekly->projectTemplate = "{$lang->projectCommon} Weekly Report Template"; $lang->weekly->weekDesc = 'Week % s (% s ~% s)'; $lang->weekly->progress = 'Progress of the ' . $lang->projectCommon; @@ -38,10 +39,15 @@ $lang->weekly->ac = 'Actual Cost(AC)'; $lang->weekly->sv = 'Schedule Variance(SV%)'; $lang->weekly->cv = 'Cost Variance(CV%)'; -$lang->weekly->totalCount = 'Total : %u tasks'; +$lang->weekly->totalCount = 'Total : %u tasks'; +$lang->weekly->builtinDesc = "The system's built-in {$lang->projectCommon} weekly report template automatically generates this week's report under the {$lang->projectCommon} every Monday."; $lang->weekly->exportWeeklyReport = 'Export Weekly Report'; +$lang->weekly->builtInScopes = array(); +$lang->weekly->builtInScopes['rnd'] = array(); +$lang->weekly->builtInScopes['rnd']['project'] = 'Project'; + $lang->weekly->reportHelpNotice = <<PV Planned Value Calculation method: @@ -143,3 +149,5 @@ Calculation method: SV(%) = -1 * (1 - (EV / PV))%

CV(%) Schedule Variance

Calculation method: CV(%) = -1 * (1 - (EV / AC))% EOD; + +$lang->weekly->builtinRawContent = '{"type":"page","meta":{"id":"DzzHUxGuzi","title":"Project Weekly Report Template","createDate":1757395000965,"tags":[]},"blocks":{"type":"block","id":"cJZg7QCPpE","flavour":"affine:page","version":2,"props":{"title":{"$blocksuite:internal:text$":true,"delta":[{"insert":"Project Weekly Report Template"}]}},"children":[{"type":"block","id":"aS-yFwCosD","flavour":"affine:note","version":1,"props":{"xywh":"[0,0,498,92]","background":"--affine-note-background-white","index":"a0","lockedBySelf":false,"hidden":false,"displayMode":"both","edgeless":{"style":{"borderRadius":8,"borderSize":4,"borderStyle":"none","shadowType":"--affine-note-shadow-box"}}},"children":[{"type":"block","id":"YCy8XkfbbP","flavour":"affine:paragraph","version":1,"props":{"align":"left","type":"text","text":{"$blocksuite:internal:text$":true,"delta":[{"insert":" ","attributes":{"holder":{"id":"tsoxyETETF","name":"weekly_term","text":"Report Term","hint":"Filter: “Date Range”BetweenThis Week","data":{"type":"weekly_term","blockID":2746,"hint":"Filter: “Date Range”BetweenThis Week","text":"Report Term"}}}},{"insert":" weekly report: "},{"insert":" ","attributes":{"holder":{"id":"q1YcO2kmC7","name":"property_name","text":"Name","hint":"Name"}}},{"insert":" is managed by the project manager "},{"insert":" ","attributes":{"holder":{"id":"eefTgQguvL","name":"property_PM","text":"Manager","hint":"Manager"}}},{"insert":", the number of people involved is "},{"insert":" ","attributes":{"holder":{"id":"jwupyH0HTa","name":"weekly_staff","text":"Staff Number","hint":"Filter: “Date Range”BetweenThis Week","data":{"type":"weekly_staff","blockID":2747,"hint":"Filter: “Date Range”BetweenThis Week","text":"Staff Number"}}}},{"insert":"."}]},"collapsed":false},"children":[]},{"type":"block","id":"cL0cyfMcnI","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=project_progress_summary&blockID=__TML_ZENTAOCHART__{project_progress_summary}","clearBeforeLoad":false,"isTemplate":true,"title":"Project Progress Summary"}},"children":[]},{"type":"block","id":"FqBsfENZPf","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=task_basicStatistic_finished&blockID=__TML_ZENTAOCHART__{task_basicStatistic_finished}","clearBeforeLoad":false,"isTemplate":true,"title":"Finished Tasks"}},"children":[]},{"type":"block","id":"egherwGMIV","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=task_basicStatistic_unfinished&blockID=__TML_ZENTAOCHART__{task_basicStatistic_unfinished}","clearBeforeLoad":false,"isTemplate":true,"title":"Unfinished Tasks"}},"children":[]},{"type":"block","id":"xfmvHk6o3o","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=task_basicStatistic_workplan&blockID=__TML_ZENTAOCHART__{task_basicStatistic_workplan}","clearBeforeLoad":false,"isTemplate":true,"title":"Work Plan"}},"children":[]},{"type":"block","id":"ZZ4Jsuq4Bq","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=project_basicStatistic_workload&blockID=__TML_ZENTAOCHART__{project_basicStatistic_workload}","clearBeforeLoad":false,"isTemplate":true,"title":"Project Plan Workload Statistic"}},"children":[]},{"type":"block","id":"BPy4La92az","flavour":"affine:paragraph","version":1,"props":{"align":"left","type":"text","text":{"$blocksuite:internal:text$":true,"delta":[]},"collapsed":false},"children":[]}]}]}}'; diff --git a/module/weekly/lang/zh-cn.php b/module/weekly/lang/zh-cn.php index 70b4e58ae2..e0cc96061a 100644 --- a/module/weekly/lang/zh-cn.php +++ b/module/weekly/lang/zh-cn.php @@ -22,10 +22,11 @@ $lang->weekly->postponed = '本周未完成工作'; $lang->weekly->nextWeek = '下周工作计划'; $lang->weekly->workloadByType = '工作量统计'; -$lang->weekly->term = '报告周期'; -$lang->weekly->project = $lang->projectCommon . '名称'; -$lang->weekly->master = '项目经理 '; -$lang->weekly->staff = '本周投入人数'; +$lang->weekly->term = '报告周期'; +$lang->weekly->project = $lang->projectCommon . '名称'; +$lang->weekly->master = '项目经理 '; +$lang->weekly->staff = '本周投入人数'; +$lang->weekly->projectTemplate = "{$lang->projectCommon}周报模板"; $lang->weekly->weekDesc = '第 %s 周( %s ~ %s)'; $lang->weekly->progress = $lang->projectCommon . '当前进展状况'; @@ -38,10 +39,15 @@ $lang->weekly->ac = '实际花费的成本(AC)'; $lang->weekly->sv = '进度偏差率(SV%)'; $lang->weekly->cv = '成本偏差率(CV%)'; -$lang->weekly->totalCount = '总计 : %u 个任务'; +$lang->weekly->totalCount = '总计 : %u 个任务'; +$lang->weekly->builtinDesc = "系统内置的{$lang->projectCommon}周报模板,每周一在{$lang->projectCommon}下自动生成本周周报。"; $lang->weekly->exportWeeklyReport = '导出周报'; +$lang->weekly->builtInScopes = array(); +$lang->weekly->builtInScopes['rnd'] = array(); +$lang->weekly->builtInScopes['rnd']['project'] = '项目'; + $lang->weekly->reportHelpNotice = <<PV 计划完成的工作 计算方式: @@ -144,3 +150,5 @@ $lang->weekly->blockHelpNotice = <<CV(%) 成本偏差率 计算方式:CV(%) = -1 * (1 - (EV / AC))% EOD; + +$lang->weekly->builtinRawContent = '{"type":"page","meta":{"id":"pGFGrn8mbr","title":"项目周报模板","createDate":1757379096204,"tags":[]},"blocks":{"type":"block","id":"Z3C1batqB7","flavour":"affine:page","version":2,"props":{"title":{"$blocksuite:internal:text$":true,"delta":[{"insert":"项目周报模板"}]}},"children":[{"type":"block","id":"kDFfXsGBPr","flavour":"affine:note","version":1,"props":{"xywh":"[0,0,498,92]","background":"--affine-note-background-white","index":"a0","lockedBySelf":false,"hidden":false,"displayMode":"both","edgeless":{"style":{"borderRadius":8,"borderSize":4,"borderStyle":"none","shadowType":"--affine-note-shadow-box"}}},"children":[{"type":"block","id":"A8QUW_uBSE","flavour":"affine:paragraph","version":1,"props":{"align":"left","type":"text","text":{"$blocksuite:internal:text$":true,"delta":[{"insert":" ","attributes":{"holder":{"id":"yqEBqqGHil","name":"weekly_term","text":"报告周期","hint":"筛选条件:“date”介于本周","data":{"type":"weekly_term","blockID":2737,"hint":"筛选条件:“date”介于本周","text":"报告周期"}}}},{"insert":"周报:"},{"insert":" ","attributes":{"holder":{"id":"PcqpwqBES3","name":"property_name","text":"项目名称","hint":"项目名称"}}},{"insert":"项目由项目经理"},{"insert":" ","attributes":{"holder":{"id":"p7Gw9A-ktu","name":"property_PM","text":"负责人","hint":"负责人"}}},{"insert":"负责,投入人数为"},{"insert":" ","attributes":{"holder":{"id":"uZ3j7QvSxb","name":"weekly_staff","text":"投入人数","hint":"筛选条件:“date”介于本周","data":{"type":"weekly_staff","blockID":2738,"hint":"筛选条件:“date”介于本周","text":"投入人数"}}}},{"insert":"。"}]},"collapsed":false},"children":[]},{"type":"block","id":"V5pHY6-x0A","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=project_progress_summary&blockID=__TML_ZENTAOCHART__{project_progress_summary}","clearBeforeLoad":false,"isTemplate":true,"title":"项目进展状况"}},"children":[]},{"type":"block","id":"cbuO57uG59","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=task_basicStatistic_finished&blockID=__TML_ZENTAOCHART__{task_basicStatistic_finished}","clearBeforeLoad":false,"isTemplate":true,"title":"已完成任务情况"}},"children":[]},{"type":"block","id":"r3Mi7MQ4Jq","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=task_basicStatistic_unfinished&blockID=__TML_ZENTAOCHART__{task_basicStatistic_unfinished}","clearBeforeLoad":false,"isTemplate":true,"title":"未完成任务情况"}},"children":[]},{"type":"block","id":"jrqVmVb0Ze","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=task_basicStatistic_workplan&blockID=__TML_ZENTAOCHART__{task_basicStatistic_workplan}","clearBeforeLoad":false,"isTemplate":true,"title":"工作计划"}},"children":[]},{"type":"block","id":"RhBxuJ6tj-","flavour":"affine:embed-zui-custom","version":1,"props":{"index":"a0","xywh":"[0,0,0,0]","lockedBySelf":false,"rotate":0,"content":{"fetcher":"/index.php?m=reporttemplate&f=ajaxZentaoChart&type=project_basicStatistic_workload&blockID=__TML_ZENTAOCHART__{project_basicStatistic_workload}","clearBeforeLoad":false,"isTemplate":true,"title":"项目计划工作量统计"}},"children":[]},{"type":"block","id":"mpqkXURxFF","flavour":"affine:paragraph","version":1,"props":{"align":"left","type":"text","text":{"$blocksuite:internal:text$":true,"delta":[]},"collapsed":false},"children":[]}]}]}}'; diff --git a/module/weekly/model.php b/module/weekly/model.php index 3bfb860783..354e456d85 100755 --- a/module/weekly/model.php +++ b/module/weekly/model.php @@ -591,4 +591,197 @@ class weeklyModel extends model return $data; } + + /** + * 添加内置项目周报模板。 + * Add builtin project weekly report template. + * + * @access public + * @return bool + */ + public function addBuiltinWeeklyTemplate() + { + /* Set scope data. */ + $scopeID = $this->addBuiltinScope(); + if(!$scopeID) return false; + + /* Set category data. */ + $categoryID = $this->addBuiltinCategory($scopeID); + + /* Set docblock data. */ + $blockIdList = array(); + $docBlock = new stdclass(); + foreach($this->config->weekly->charts as $chartKey => $chartContent) + { + $docBlock->type = $chartKey; + $docBlock->content = json_encode($chartContent); + $this->dao->insert(TABLE_DOCBLOCK)->data($docBlock)->exec(); + $blockIdList[$chartKey] = $this->dao->lastInsertId(); + } + + /* Set template data. */ + $this->addBuiltinTemplate($scopeID, $categoryID, $blockIdList); + + return !dao::isError(); + } + + /** + * 添加内置报告模板范围。 + * Add builtin report template scope. + * + * @access public + * @return int|bool + */ + public function addBuiltinScope(): int|bool + { + $scope = $this->dao->select('id')->from(TABLE_DOCLIB)->where('type')->eq('reportTemplate')->andWhere('main')->eq(1)->andWhere('vision')->eq($this->config->vision)->fetch(); + if($scope) return false; + + $this->loadModel('setting'); + + /* Set scope data. */ + $scope = new stdClass(); + $scope->type = 'reportTemplate'; + $scope->main = '1'; + $scope->vision = $this->config->vision; + $scope->addedBy = 'system'; + $scope->addedDate = helper::now(); + foreach($this->lang->weekly->builtInScopes as $vision => $scopeList) + { + $scopeMaps = array(); + $scope->vision = $vision; + foreach($scopeList as $scopeKey => $scopeName) + { + if(empty($scopeName)) continue; + + $scope->name = $scopeName; + $this->dao->insert(TABLE_DOCLIB)->data($scope)->exec(); + $scopeMaps[$scopeKey] = $this->dao->lastInsertID(); + } + if(!empty($scopeMaps)) $this->setting->setItem("system.reporttemplate.builtInScopeMaps@{$vision}", json_encode($scopeMaps)); + } + + return array_push($scopeMaps); + } + + /** + * 添加内置报告模板分类。 + * Add builtin report template category. + * + * @param int $scopeID + * @access public + * @return int + */ + public function addBuiltinCategory(int $scopeID): int + { + /* Set category data. */ + $category = new stdClass(); + $category->root = $scopeID; + $category->name = $this->lang->projectCommon; + $category->grade = 1; + $category->type = 'reportTemplate'; + $this->dao->insert(TABLE_MODULE)->data($category)->exec(); + + $categoryID = $this->dao->lastInsertID(); + $this->dao->update(TABLE_MODULE)->set('`path`')->eq(",{$categoryID},")->where('id')->eq($categoryID)->exec(); + + return $categoryID; + } + + /** + * 添加内置报告模板。 + * Add builtin report template. + * + * @param int $libID + * @param int $moduleID + * @param array $blockIdList + * @access public + * @return bool + */ + public function addBuiltinTemplate(int $libID, int $moduleID, array $blockIdList): bool + { + $now = helper::now(); + $cycleConfig = array('turnon' => 'on', 'frequency' => 'week', 'acl' => 'open', 'readGroups' => array(), 'readUsers' => array(), 'groups' => array(), 'users' => array()); + + $objects = $this->dao->select('id')->from(TABLE_WORKFLOWGROUP)->where('type')->eq('project')->andWhere('projectModel')->eq('waterfall')->andWhere('status')->eq('normal')->andWhere('vision')->eq($this->config->vision)->andWhere('objectID')->eq(0)->andWhere('deleted')->eq(0)->fetchPairs('id'); + + $template = new stdclass(); + $template->lib = $libID; + $template->module = $moduleID; + $template->title = $this->lang->weekly->projectTemplate; + $template->type = 'text'; + $template->status = 'normal'; + $template->acl = 'open'; + $template->builtIn = 1; + $template->templateType = 'reportTemplate'; + $template->templateDesc = $this->lang->weekly->builtinDesc; + $template->cycle = 'week'; + $template->cycleConfig = json_encode($cycleConfig); + $template->objects = ',' . implode(',', $objects) . ','; + $template->addedBy = 'system'; + $template->addedDate = $now; + $this->dao->insert(TABLE_DOC)->data($template)->exec(); + + $templateID = $this->dao->lastInsertID(); + $this->dao->update(TABLE_DOC)->set('`path`')->eq(",{$templateID},")->set('`order`')->eq($templateID)->where('id')->eq($templateID)->exec(); + $this->dao->update(TABLE_DOCBLOCK)->set('doc')->eq($templateID)->where('id')->in(array_values($blockIdList))->exec(); + + $templateContent = new stdclass(); + $templateContent->doc = $templateID; + $templateContent->title = $template->title; + $templateContent->type = 'doc'; + $templateContent->version = 1; + $templateContent->rawContent = $this->getBuildinRawContent($blockIdList); + $templateContent->addedBy = 'system'; + $templateContent->addedDate = $now; + $this->dao->insert(TABLE_DOCCONTENT)->data($templateContent)->exec(); + + $this->loadModel('action')->create('reportTemplate', $templateID, 'Created', '', '', 'system'); + + return !dao::isError(); + } + + /** + * 获取内置项目周报模板内容。 + * Get builtin project weekly report template content. + * + * @param array $blockIdList + * @access public + * @return string + */ + public function getBuildinRawContent(array $blockIdList): string + { + $blockCodes = array(); + foreach($blockIdList as $blockKey => $blockID) $blockCodes[] = '{' . $blockKey . '}'; + + $rawContent = json_decode($this->lang->weekly->builtinRawContent); + foreach($rawContent as $content) + { + if(isset($content->id)) $content->id = uniqid(); + if(isset($content->createDate)) $content->createDate = time(); + if(empty($content->children)) continue; + + foreach($content->children as $childList) + { + if(isset($childList->id)) $childList->id = uniqid(); + if(empty($childList->children)) continue; + foreach($childList->children as $child) + { + if(isset($child->id)) $child->id = uniqid(); + if(!empty($child->props->text->delta)) + { + foreach($child->props->text->delta as $delta) + { + if(isset($delta->attributes->holder->id)) $delta->attributes->holder->id = uniqid(); + if(isset($delta->attributes->holder->name) && isset($blockIdList[$delta->attributes->holder->name])) $delta->attributes->holder->name = "{$delta->attributes->holder->name}_{$blockIdList[$delta->attributes->holder->name]}"; + if(isset($delta->attributes->holder->data->blockID) && isset($blockIdList[$delta->attributes->holder->data->type])) $delta->attributes->holder->data->blockID = $blockIdList[$delta->attributes->holder->data->type]; + } + } + if(!empty($child->props->content)) $child->props->content->fetcher = str_replace($blockCodes, array_values($blockIdList), $child->props->content->fetcher); + } + } + } + + return json_encode($rawContent); + } } diff --git a/module/weekly/test/lib/weekly.unittest.class.php b/module/weekly/test/lib/weekly.unittest.class.php index a4e4e11e64..3942ee0f58 100644 --- a/module/weekly/test/lib/weekly.unittest.class.php +++ b/module/weekly/test/lib/weekly.unittest.class.php @@ -373,4 +373,78 @@ class weeklyTest return $objects; } + + /** + * 添加内置报告模板范围。 + * Add built-in report template scope. + * + * @access public + * @return bool|object + */ + public function addBuiltinScopeTest(): bool|object + { + $scopeID = $this->objectModel->addBuiltinScope(); + if(!$scopeID) return false; + + return $this->objectModel->dao->select('*')->from(TABLE_DOCLIB)->where('id')->eq($scopeID)->fetch(); + } + + /** + * 添加内置分类。 + * Add built-in category. + * + * @access public + * @return object + */ + public function addBuiltinCategoryTest(): object + { + $scopeID = $this->objectModel->addBuiltinScope(); + $categroyID = $this->objectModel->addBuiltinCategory($scopeID); + return $this->objectModel->dao->select('*')->from(TABLE_MODULE)->where('id')->eq($categroyID)->fetch(); + } + + /** + * 添加内置报告模板。 + * Add builtin report template. + * + * @access public + * @return array|object + */ + public function addBuiltinTemplateTest(): array|object + { + $scopeID = $this->objectModel->addBuiltinScope(); + $categroyID = $this->objectModel->addBuiltinCategory($scopeID); + $this->objectModel->addBuiltinTemplate($scopeID, $categroyID, array()); + + if(dao::isError()) return dao::getError(); + return $this->objectModel->dao->select('*')->from(TABLE_DOC)->fetch(); + } + + /** + * 获取内置项目周报模板内容。 + * Get builtin project weekly report template content. + * + * @access public + * @return array + */ + public function getBuildinRawContentTest(): array + { + $content = $this->objectModel->getBuildinRawContent(array()); + return json_decode($content, true); + } + + /** + * 添加内置项目周报模板。 + * Add builtin project weekly report template. + * + * @access public + * @return bool|object + */ + public function addBuiltinWeeklyTemplateTest():bool|object + { + $result = $this->objectModel->addBuiltinWeeklyTemplate(); + if(!$result) return false; + + return $this->objectModel->dao->select('*')->from(TABLE_DOC)->fetch(); + } } diff --git a/module/weekly/test/model/addbuiltincategory.php b/module/weekly/test/model/addbuiltincategory.php new file mode 100644 index 0000000000..46522a51fe --- /dev/null +++ b/module/weekly/test/model/addbuiltincategory.php @@ -0,0 +1,26 @@ +#!/usr/bin/env php +addBuiltinCategory(); +timeout=0 +cid=1 + +- 测试添加内置分类 + - 属性id @1 + - 属性root @1 + - 属性name @项目 + - 属性grade @1 + - 属性type @reportTemplate + +*/ +include dirname(__FILE__, 5) . '/test/lib/init.php'; +include dirname(__FILE__, 2) . '/lib/weekly.unittest.class.php'; + +zenData('doclib')->gen(0); +zenData('module')->gen(0); +zenData('user')->gen(5); +su('admin'); + +$weeklyTester = new weeklyTest(); +r($weeklyTester->addBuiltinCategoryTest()) && p('id,root,name,grade,type') && e('1,1,项目,1,reportTemplate'); // 测试添加内置分类 diff --git a/module/weekly/test/model/addbuiltinscope.php b/module/weekly/test/model/addbuiltinscope.php new file mode 100644 index 0000000000..a04b890cdc --- /dev/null +++ b/module/weekly/test/model/addbuiltinscope.php @@ -0,0 +1,26 @@ +#!/usr/bin/env php +addBuiltinScope(); +timeout=0 +cid=1 + +- 测试添加内置范围 + - 属性name @项目 + - 属性type @reportTemplate + - 属性main @1 + - 属性addedBy @system +- 测试重复添加内置范围 @0 + +*/ +include dirname(__FILE__, 5) . '/test/lib/init.php'; +include dirname(__FILE__, 2) . '/lib/weekly.unittest.class.php'; + +zenData('doclib')->gen(0); +zenData('user')->gen(5); +su('admin'); + +$weeklyTester = new weeklyTest(); +r($weeklyTester->addBuiltinScopeTest()) && p('name,type,main,addedBy') && e('项目,reportTemplate,1,system'); // 测试添加内置范围 +r($weeklyTester->addBuiltinScopeTest()) && p() && e('0'); // 测试重复添加内置范围 diff --git a/module/weekly/test/model/addbuiltintemplate.php b/module/weekly/test/model/addbuiltintemplate.php new file mode 100644 index 0000000000..884c251467 --- /dev/null +++ b/module/weekly/test/model/addbuiltintemplate.php @@ -0,0 +1,33 @@ +#!/usr/bin/env php +addBuiltinTemplate(); +timeout=0 +cid=1 + +- 测试添加内置模板 + - 属性title @项目周报模板 + - 属性type @text + - 属性status @normal + - 属性acl @open + - 属性builtIn @1 + - 属性templateType @reportTemplate + - 属性cycle @week + - 属性addedBy @system + +*/ +include dirname(__FILE__, 5) . '/test/lib/init.php'; +include dirname(__FILE__, 2) . '/lib/weekly.unittest.class.php'; + +zenData('action')->gen(0); +zenData('doclib')->gen(0); +zenData('module')->gen(0); +zenData('workflowgroup')->gen(0); +zenData('doc')->gen(0); +zenData('doccontent')->gen(0); +zenData('user')->gen(5); +su('admin'); + +$weeklyTester = new weeklyTest(); +r($weeklyTester->addBuiltinTemplateTest()) && p('title,type,status,acl,builtIn,templateType,cycle,addedBy') && e('项目周报模板,text,normal,open,1,reportTemplate,week,system'); // 测试添加内置模板 diff --git a/module/weekly/test/model/addbuiltinweeklytemplate.php b/module/weekly/test/model/addbuiltinweeklytemplate.php new file mode 100644 index 0000000000..e955303a7a --- /dev/null +++ b/module/weekly/test/model/addbuiltinweeklytemplate.php @@ -0,0 +1,35 @@ +#!/usr/bin/env php +addBuiltinWeeklyTemplate(); +timeout=0 +cid=1 + +- 测试添加内置周报模板 + - 属性title @项目周报模板 + - 属性type @text + - 属性status @normal + - 属性acl @open + - 属性builtIn @1 + - 属性templateType @reportTemplate + - 属性cycle @week + - 属性addedBy @system +- 测试重复添加内置周报模板 @0 + +*/ +include dirname(__FILE__, 5) . '/test/lib/init.php'; +include dirname(__FILE__, 2) . '/lib/weekly.unittest.class.php'; + +zenData('docblock')->gen(0); +zenData('doclib')->gen(0); +zenData('module')->gen(0); +zenData('workflowgroup')->gen(0); +zenData('doc')->gen(0); +zenData('doccontent')->gen(0); +zenData('user')->gen(5); +su('admin'); + +$weeklyTester = new weeklyTest(); +r($weeklyTester->addBuiltinWeeklyTemplateTest()) && p('title,type,status,acl,builtIn,templateType,cycle,addedBy') && e('项目周报模板,text,normal,open,1,reportTemplate,week,system'); // 测试添加内置周报模板 +r($weeklyTester->addBuiltinWeeklyTemplateTest()) && p() && e('0'); // 测试重复添加内置周报模板 diff --git a/module/weekly/test/model/getbuildinrawcontent.php b/module/weekly/test/model/getbuildinrawcontent.php new file mode 100644 index 0000000000..5269f480f1 --- /dev/null +++ b/module/weekly/test/model/getbuildinrawcontent.php @@ -0,0 +1,29 @@ +#!/usr/bin/env php +getBuildinRawContent(); +timeout=0 +cid=1 + +- 检查模板标题属性title @项目周报模板 +- 获取holder数据 + - 属性name @weekly_term + - 属性text @报告周期 + - 属性hint @筛选条件:“date”介于本周 +- 检查区块数量 @6 + +*/ + +include dirname(__FILE__, 5) . '/test/lib/init.php'; +include dirname(__FILE__, 2) . '/lib/weekly.unittest.class.php'; + +zenData('user')->gen(5); +su('admin'); + +$weeklyTester = new weeklyTest(); +$content = $weeklyTester->getBuildinRawContentTest(); + +r($content['meta']) && p('title') && e('项目周报模板'); // 检查模板标题 +r($content['blocks']['children'][0]['children'][0]['props']['text']['delta'][0]['attributes']['holder']) && p('name,text,hint') && e('weekly_term,报告周期,筛选条件:“date”介于本周'); // 获取holder数据 +r(count($content['blocks'])) && p() && e('6'); // 检查区块数量 diff --git a/module/weekly/test/model/getreportdata.php b/module/weekly/test/model/getreportdata.php new file mode 100644 index 0000000000..c639016231 --- /dev/null +++ b/module/weekly/test/model/getreportdata.php @@ -0,0 +1,30 @@ +#!/usr/bin/env php +getReportData(); +timeout=0 +cid=1 + +- 检查是否存在pv属性 @1 +- 检查是否存在ev属性 @1 +- 检查是否存在project属性 @1 +- 检查是否存在staff属性 @1 +- 检查是否存在progress属性 @1 + +*/ +include dirname(__FILE__, 5) . '/test/lib/init.php'; +zenData('project')->loadYaml('project')->gen(5); +zenData('task')->loadYaml('task')->gen(30); +zenData('user')->gen(5); +su('admin'); + +global $tester; +$weeklyModel = $tester->loadModel('weekly'); +$result = $weeklyModel->getReportData(1); + +r(isset($result->pv)) && p() && e('1'); // 检查是否存在pv属性 +r(isset($result->ev)) && p() && e('1'); // 检查是否存在ev属性 +r(isset($result->project)) && p() && e('1'); // 检查是否存在project属性 +r(isset($result->staff)) && p() && e('1'); // 检查是否存在staff属性 +r(isset($result->progress)) && p() && e('1'); // 检查是否存在progress属性 diff --git a/module/weekly/test/yaml/project.yaml b/module/weekly/test/yaml/project.yaml new file mode 100644 index 0000000000..a1d346a4aa --- /dev/null +++ b/module/weekly/test/yaml/project.yaml @@ -0,0 +1,18 @@ +title: project. +desc: project data. +author: Sun Guangming +version: "1.0" +fields: + - field: id + range: 1-100 + - field: name + note: "名称" + fields: + - field: name1 + range: 敏捷项目,瀑布项目,看板项目 + - field: name2 + range: 1-100 + - field: model + range: scrum,waterfall,kanban + - field: type + range: project