Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
P
problem_rectification
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
David Yang
problem_rectification
Commits
2d3757b2
Commit
2d3757b2
authored
Jul 16, 2026
by
David Yang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 驾驶舱拆分
parent
c536e180
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
132 additions
and
68 deletions
+132
-68
.umirc.ts
.umirc.ts
+5
-0
Layout.jsx
src/pages/problem_rectification/Layout.jsx
+6
-3
BusinessDashboard.jsx
...lem_rectification/businessDashboard/BusinessDashboard.jsx
+5
-0
index.jsx
src/pages/problem_rectification/dashboard/index.jsx
+0
-0
problem-rectification.business-flow.md
...blem_rectification/problem-rectification.business-flow.md
+39
-16
problemRectificationDashboardService.js
...lem_rectification/problemRectificationDashboardService.js
+77
-49
No files found.
.umirc.ts
View file @
2d3757b2
...
...
@@ -8,6 +8,7 @@ function resolvePackageRoot(packageName: string) {
const
ROUTE_PATHS
=
{
login
:
'/login'
,
dashboard
:
'/integrity_risk_table/dashboard'
,
businessDashboard
:
'/integrity_risk_table/business_dashboard'
,
problemRectification
:
'/problem_rectification'
,
issueRectificationArchive
:
'/issue_rectification_archive'
,
problemRectificationForm
:
'/problem_rectification_form'
,
...
...
@@ -41,6 +42,10 @@ export default defineConfig({
path
:
ROUTE_PATHS
.
dashboard
,
component
:
'./problem_rectification/dashboard/index'
,
},
{
path
:
ROUTE_PATHS
.
businessDashboard
,
component
:
'./problem_rectification/businessDashboard/BusinessDashboard'
,
},
{
path
:
ROUTE_PATHS
.
problemRectification
,
component
:
'./problem_rectification/list/index'
,
...
...
src/pages/problem_rectification/Layout.jsx
View file @
2d3757b2
...
...
@@ -5,6 +5,7 @@ import { SiteContext } from '@/wrapper/Site';
import
{
ArchiveBoxIcon
,
Bars3Icon
,
ChartPieIcon
,
ClipboardDocumentCheckIcon
,
HomeModernIcon
,
Squares2X2Icon
,
...
...
@@ -25,6 +26,11 @@ const menuGroups = [
path
:
'/integrity_risk_table/dashboard'
,
icon
:
HomeModernIcon
,
},
{
name
:
'业务驾驶舱'
,
path
:
'/integrity_risk_table/business_dashboard'
,
icon
:
ChartPieIcon
,
},
{
name
:
'问题整改'
,
path
:
'/problem_rectification'
,
...
...
@@ -280,9 +286,6 @@ export default function ProblemRectificationLayout() {
<
h1
className=
"truncate text-sm font-semibold tracking-[0.02em] text-white/95"
>
{
APP_NAME
}
</
h1
>
<
div
className=
"mt-1 text-xs text-zinc-400"
>
替代旧多维表格版问题整改应用
</
div
>
</
div
>
{
applicationSwitcherButton
}
</>
...
...
src/pages/problem_rectification/businessDashboard/BusinessDashboard.jsx
0 → 100644
View file @
2d3757b2
import
{
ProblemRectificationDashboardPage
}
from
'../dashboard'
;
export
default
function
BusinessDashboard
()
{
return
<
ProblemRectificationDashboardPage
dashboardMode=
"business"
/>;
}
src/pages/problem_rectification/dashboard/index.jsx
View file @
2d3757b2
This diff is collapsed.
Click to expand it.
src/pages/problem_rectification/problem-rectification.business-flow.md
View file @
2d3757b2
...
...
@@ -38,15 +38,17 @@
## 3. 功能入口
建议在当前项目中保留以下
三
个一级入口:
建议在当前项目中保留以下
四
个一级入口:
-
驾驶舱:
`/integrity_risk_table/dashboard`
-
业务驾驶舱:
`/integrity_risk_table/business_dashboard`
-
问题整改:
`/problem_rectification`
-
往期材料归档:
`/issue_rectification_archive`
建议交互形态:
-
驾驶舱使用轻量卡片、统计区块、分组标签页和列表卡片
-
驾驶舱仅展示顶部横幅、部门分组切换与宏观总览 KPI 卡片
-
业务驾驶舱承接整改进度分布、问题风险定位、责任追踪与整改质效图表
-
问题整改主列表使用普通数据表格,不再依赖多维表格编辑器
-
新增、编辑、查看详情优先采用抽屉或弹窗,不走重型嵌套表格交互
-
批量操作放在顶部工具栏,不再依赖右键菜单作为唯一入口
...
...
@@ -79,20 +81,23 @@
### 3.1 目标页面拆分
为便于当前项目直接重构,建议把模块拆成以下
4
个页面入口:
为便于当前项目直接重构,建议把模块拆成以下
5
个页面入口:
1.
`integrity_risk_table/dashboard`
问题整改驾驶舱
2.
`problem_rectification`
2.
`integrity_risk_table/business_dashboard`
业务驾驶舱
3.
`problem_rectification`
问题整改主列表
3
.
`issue_rectification_archive`
4
.
`issue_rectification_archive`
归档批次列表
4
.
`problem_rectification_form`
5
.
`problem_rectification_form`
外链填写 / 复核 / 最终审核页
对应推荐页面职责:
-
驾驶舱:只做统计、提醒、分组切换,不承接编辑
-
驾驶舱:展示管理层首屏关注的宏观总览,保留部门分组切换,不承接编辑
-
业务驾驶舱:展示后续业务分析图表、责任追踪、重点问题清单与质效分布,保留部门分组切换,不承接编辑
-
主列表:承接查询、筛选、批量操作、详情入口
-
归档页:承接归档批次查看与明细追溯
-
外链流程页:承接填写、复核、最终审核,不与主列表混杂
...
...
@@ -403,7 +408,12 @@
## 9. 驾驶舱展示规则
当前驾驶舱页面以
`problem_rectification`
为唯一数据源,围绕 2026 年整改驾驶舱需求分为四层:
当前驾驶舱能力以
`problem_rectification`
为唯一数据源,并拆分为两个菜单入口:
-
`integrity_risk_table/dashboard`
:问题整改驾驶舱,仅展示顶部横幅、部门分组切换与宏观总览 KPI
-
`integrity_risk_table/business_dashboard`
:业务驾驶舱,展示原宏观总览之后的业务分析内容,并继续支持部门分组切换
围绕 2026 年整改驾驶舱需求,整体统计能力分为四层:
1.
宏观总览层
2.
问题分析与风险定位层
...
...
@@ -435,11 +445,24 @@
当前实现约定:
-
顶部继续保留按
`feedback_unit`
分组的轻量 tabs
-
tabs 下方分为 KPI 卡片、ECharts 图表区和重点问题清单区
-
驾驶舱仅负责统计展示,不承接编辑动作
-
两个驾驶舱入口顶部均保留按
`feedback_unit`
分组的轻量 tabs
-
两个驾驶舱入口顶部均提供“统计年份”选择器,默认“全部年份”,选择具体年份后仅统计该年份内的问题记录
-
问题整改驾驶舱的 tabs 下方只展示宏观总览 KPI 卡片
-
业务驾驶舱的 tabs 下方展示整改进度分布、业务风险分析、责任追踪和整改质效图表
-
两个驾驶舱均仅负责统计展示,不承接编辑动作
-
如果线上 Subject 后续把新增字段从
`text`
升级为
`select`
/
`date`
/
`boolean`
,驾驶舱统计口径无需调整,只需复用标准字段值
### 9.1.1 统计年份筛选
年份筛选用于支持管理层按年度查看整改概览和业务分析:
-
默认选项为“全部年份”,不做年度过滤,按当前数据全量统计
-
年份选项从当前已加载的
`problem_rectification`
记录自动提取,按年份倒序展示
-
记录年份优先从检查类日期和业务发生时间推导,依次读取:
`inspection_report_date`
、
`check_the_implementation_time`
、
`inspection_completed_at`
、
`the_time_of_rectification`
、
`actual_rectification_completed_at`
-
如果上述字段都无法识别年份,则回退到记录的
`created_at`
/
`updated_at`
-
选择年份后,部门分组、宏观 KPI、整改进度、问题风险定位、责任追踪和整改质效统计均基于该年份内记录重新计算
-
如果某年份下没有可展示记录,页面展示空状态,并提示切换其它年份或全部年份
### 9.2 宏观总览层
当前 KPI 统计口径:
...
...
@@ -454,6 +477,7 @@
-
以
`rectification_progress`
为维度统计数量和占比
-
当前页面使用环形图表达,不再沿用旧版折线趋势表达
-
业务驾驶舱仅保留整改进度分布图,不再展示单独的“进度口径说明”卡片
### 9.3 问题分析与风险定位层
...
...
@@ -466,7 +490,8 @@
-
经济损失问题:
`has_economic_loss`
+
`economic_loss_amount`
-
外部影响问题:
`has_external_impact`
+
`penalty_organization`
-
员工问责问题:
`has_employee_accountability`
+
`accountability_person_count`
-
重点问题清单中同时展示
`problem_severity_level`
-
页面以柱状图展示三类重点问题数量;点击柱状图或下方按钮后,通过弹框展示对应详情列表
-
重点问题详情列表中同时展示
`problem_severity_level`
、截止日期、反馈单位、标记信息和问题简述
### 9.4 问题责任追踪与执行层
...
...
@@ -477,10 +502,7 @@
-
X 轴:责任部门问题总数
-
Y 轴:整改完成率
-
气泡大小:逾期问题数
-
整改时效分析散点图:
-
X 轴:
`the_time_of_rectification - inspection_completed_at`
-
Y 轴:
`actual_rectification_completed_at - inspection_completed_at`
-
仅统计同时填写了预计整改时间、实际整改完成时间、实际检查完成日期且天数非负的问题
-
业务驾驶舱不再展示单独的“整改时效分析”散点图
当前逾期 / 延期判定说明:
...
...
@@ -701,6 +723,7 @@ const result = await queryUsers(0, 20, {
-
`src/pages/problem_rectification/Layout.jsx`
-
`src/pages/problem_rectification/dashboard/index.jsx`
-
`src/pages/problem_rectification/businessDashboard/BusinessDashboard.jsx`
-
`src/pages/problem_rectification/list/index.jsx`
-
`src/pages/problem_rectification/archive/index.jsx`
-
`src/pages/problem_rectification/form/index.jsx`
...
...
src/pages/problem_rectification/problemRectificationDashboardService.js
View file @
2d3757b2
...
...
@@ -35,6 +35,13 @@ const FIXED_PROGRESS_VALUES = new Set(['已整改', '已优化']);
const
POSITIVE_FLAGS
=
new
Set
([
'是'
,
'有'
,
'涉及'
,
'TRUE'
,
'1'
,
'Y'
,
'YES'
]);
const
SEVERITY_LEVELS
=
[
'低'
,
'中'
,
'高'
];
const
RATING_LEVELS
=
[
'低'
,
'中'
,
'高'
];
const
YEAR_SOURCE_FIELDS
=
[
'inspection_report_date'
,
'check_the_implementation_time'
,
'inspection_completed_at'
,
'the_time_of_rectification'
,
'actual_rectification_completed_at'
,
];
function
normalizeText
(
value
)
{
return
String
(
value
??
''
).
trim
();
...
...
@@ -81,6 +88,36 @@ function formatDateText(value) {
return
/^
\d{4}
-
\d{2}
-
\d{2}
/
.
test
(
text
)
?
text
.
slice
(
0
,
10
)
:
text
;
}
function
extractYear
(
value
)
{
const
text
=
normalizeText
(
value
);
if
(
!
text
)
{
return
null
;
}
const
parsed
=
parseDate
(
text
);
if
(
parsed
)
{
return
parsed
.
year
();
}
const
yearMatch
=
text
.
match
(
/
(?:
19|20
)\d{2}
/
);
if
(
!
yearMatch
)
{
return
null
;
}
return
Number
(
yearMatch
[
0
]);
}
function
extractRecordYear
(
record
,
metadata
)
{
for
(
const
field
of
YEAR_SOURCE_FIELDS
)
{
const
year
=
extractYear
(
metadata
[
field
]);
if
(
year
)
{
return
year
;
}
}
return
extractYear
(
record
?.
created_at
)
||
extractYear
(
record
?.
updated_at
);
}
function
isAffirmative
(
value
)
{
return
POSITIVE_FLAGS
.
has
(
normalizeText
(
value
).
toUpperCase
());
}
...
...
@@ -161,14 +198,6 @@ function sortByCountDesc(items = []) {
return
[...
items
].
sort
((
left
,
right
)
=>
right
.
value
-
left
.
value
);
}
function
diffDays
(
later
,
earlier
)
{
if
(
!
later
||
!
earlier
)
{
return
null
;
}
return
later
.
startOf
(
'day'
).
diff
(
earlier
.
startOf
(
'day'
),
'day'
);
}
function
buildDistributionMap
(
records
,
getKey
)
{
const
map
=
new
Map
();
...
...
@@ -274,19 +303,16 @@ function buildFocusIssueGroups(records) {
economicLoss
:
records
.
filter
((
record
)
=>
record
.
hasEconomicLoss
)
.
sort
(
sortBySeverityAndDueDate
)
.
slice
(
0
,
6
)
.
map
((
record
)
=>
formatItem
(
record
,
record
.
economicLossAmount
?
`损失
${
record
.
economicLossAmount
}
`
:
'已标记经济损失'
),
),
externalImpact
:
records
.
filter
((
record
)
=>
record
.
hasExternalImpact
)
.
sort
(
sortBySeverityAndDueDate
)
.
slice
(
0
,
6
)
.
map
((
record
)
=>
formatItem
(
record
,
record
.
penaltyOrganization
||
'已标记外部影响'
)),
accountability
:
records
.
filter
((
record
)
=>
record
.
hasEmployeeAccountability
)
.
sort
(
sortBySeverityAndDueDate
)
.
slice
(
0
,
6
)
.
map
((
record
)
=>
formatItem
(
record
,
...
...
@@ -359,34 +385,6 @@ function buildOverdueRanking(departmentStats) {
).
slice
(
0
,
10
);
}
function
buildTimelinessScatter
(
records
)
{
return
records
.
map
((
record
)
=>
{
const
plannedDays
=
diffDays
(
record
.
dueDate
,
record
.
inspectionCompletedAt
);
const
actualDays
=
diffDays
(
record
.
actualRectificationCompletedAt
,
record
.
inspectionCompletedAt
,
);
if
(
plannedDays
===
null
||
actualDays
===
null
||
plannedDays
<
0
||
actualDays
<
0
)
{
return
null
;
}
return
{
name
:
record
.
title
,
value
:
[
plannedDays
,
actualDays
,
record
.
severityLevel
||
'未评定'
],
isDelayed
:
actualDays
>
plannedDays
,
};
})
.
filter
(
Boolean
)
.
slice
(
0
,
120
);
}
function
buildQualityRatingDistribution
(
records
)
{
const
counts
=
{
高
:
0
,
...
...
@@ -429,7 +427,6 @@ function summarizeRecords(records) {
focusIssueGroups
:
buildFocusIssueGroups
(
records
),
overdueRanking
:
buildOverdueRanking
(
departmentStats
),
departmentMatrix
:
buildDepartmentMatrix
(
departmentStats
),
timelinessScatter
:
buildTimelinessScatter
(
records
),
qualityRatingDistribution
:
buildQualityRatingDistribution
(
records
),
};
}
...
...
@@ -454,6 +451,7 @@ function normalizeProblemRecord(record) {
const
normalizedRecord
=
{
id
:
record
?.
_id
||
record
?.
name
||
''
,
title
:
normalizeText
(
record
?.
title
)
||
normalizeText
(
metadata
.
project_name
)
||
'-'
,
recordYear
:
extractRecordYear
(
record
,
metadata
),
projectName
:
normalizeText
(
metadata
.
project_name
),
inspectUnit
:
normalizeText
(
metadata
.
inspect_the_implementing_unit
),
feedbackUnit
:
normalizeText
(
metadata
.
feedback_unit
),
...
...
@@ -527,6 +525,32 @@ function buildDepartmentBuckets(records) {
return
buckets
;
}
function
buildDashboardScope
(
records
)
{
const
departmentBuckets
=
buildDepartmentBuckets
(
records
);
const
summaries
=
Object
.
fromEntries
(
DEPARTMENT_GROUPS
.
map
((
group
)
=>
[
group
.
key
,
summarizeRecords
(
departmentBuckets
[
group
.
key
]
||
[]),
]),
);
return
{
departmentBuckets
,
summaries
,
isEmpty
:
records
.
length
===
0
,
};
}
function
buildYearOptions
(
records
)
{
return
Array
.
from
(
new
Set
(
records
.
map
((
record
)
=>
record
.
recordYear
)
.
filter
((
year
)
=>
Number
.
isInteger
(
year
)),
),
).
sort
((
left
,
right
)
=>
right
-
left
);
}
async
function
fetchAllProblemRectificationRecords
()
{
const
firstPage
=
await
queryRecords
({
subject
:
PROBLEM_RECTIFICATION_SUBJECT
,
...
...
@@ -558,27 +582,31 @@ export async function loadProblemRectificationDashboardData() {
try
{
const
rawRecords
=
await
fetchAllProblemRectificationRecords
();
const
records
=
rawRecords
.
map
(
normalizeProblemRecord
);
const
departmentBuckets
=
buildDepartmentBucket
s
(
records
);
const
summari
es
=
Object
.
fromEntries
(
DEPARTMENT_GROUPS
.
map
((
group
)
=>
[
group
.
key
,
summarizeRecords
(
departmentBuckets
[
group
.
key
]
||
[]
),
const
yearOptions
=
buildYearOption
s
(
records
);
const
allScope
=
buildDashboardScope
(
records
);
const
yearScop
es
=
Object
.
fromEntries
(
yearOptions
.
map
((
year
)
=>
[
String
(
year
)
,
buildDashboardScope
(
records
.
filter
((
record
)
=>
record
.
recordYear
===
year
)
),
]),
);
return
{
groups
:
DEPARTMENT_GROUPS
,
records
,
departmentBuckets
,
summaries
,
yearOptions
,
yearScopes
,
departmentBuckets
:
allScope
.
departmentBuckets
,
summaries
:
allScope
.
summaries
,
error
:
''
,
isEmpty
:
records
.
length
===
0
,
isEmpty
:
allScope
.
isEmpty
,
};
}
catch
(
error
)
{
return
{
groups
:
DEPARTMENT_GROUPS
,
records
:
[],
yearOptions
:
[],
yearScopes
:
{},
departmentBuckets
:
{},
summaries
:
{},
error
:
error
?.
message
||
'驾驶舱数据加载失败,请稍后重试。'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment