Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
M
model_analysis
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
model_analysis
Commits
00b6e86c
Commit
00b6e86c
authored
Sep 03, 2026
by
drigle
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: group model batches by model
parent
d99022c1
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
63 additions
and
28 deletions
+63
-28
manifest.json
public/manifest.json
+3
-2
model-monitor-source-fixtures.sql
scripts/mysql/model-monitor-source-fixtures.sql
+0
-0
Models.jsx
src/pages/project_manager_xy/Models.jsx
+58
-24
model-monitor.business-flow.md
src/pages/project_manager_xy/model-monitor.business-flow.md
+2
-2
No files found.
public/manifest.json
View file @
00b6e86c
{
{
"name"
:
"model_analysis"
,
"name"
:
"model_analysis"
,
"version"
:
"1.0.
2
"
,
"version"
:
"1.0.
3
"
,
"build"
:
"abc"
,
"build"
:
"abc"
,
"description"
:
"merge master"
,
"description"
:
"merge master"
,
"date"
:
"2025-07-14 19:27:20"
,
"date"
:
"2025-07-14 19:27:20"
,
"author"
:
"yang"
"author"
:
"yang"
,
"spa"
:
true
}
}
scripts/mysql/model-monitor-source-fixtures.sql
View file @
00b6e86c
This diff is collapsed.
Click to expand it.
src/pages/project_manager_xy/Models.jsx
View file @
00b6e86c
...
@@ -326,6 +326,21 @@ export default function Models() {
...
@@ -326,6 +326,21 @@ export default function Models() {
return
Number
(
right
)
-
Number
(
left
);
return
Number
(
right
)
-
Number
(
left
);
}),
[
batchesByYear
]);
}),
[
batchesByYear
]);
const
activeYearEntries
=
batchesByYear
[
activeBatchYear
]
||
[];
const
activeYearEntries
=
batchesByYear
[
activeBatchYear
]
||
[];
const
activeYearModelGroups
=
useMemo
(()
=>
{
const
grouped
=
new
Map
();
activeYearEntries
.
forEach
((
entry
)
=>
{
const
modelKey
=
`
${
entry
.
sourceModel
.
source_model_id
||
entry
.
sourceModel
.
model_code
||
entry
.
sourceModel
.
model_name
}
`
;
if
(
!
grouped
.
has
(
modelKey
))
{
grouped
.
set
(
modelKey
,
{
key
:
modelKey
,
model
:
entry
.
sourceModel
,
entries
:
[],
});
}
grouped
.
get
(
modelKey
).
entries
.
push
(
entry
);
});
return
[...
grouped
.
values
()];
},
[
activeYearEntries
]);
const
activeYearSelectableEntries
=
activeYearEntries
.
filter
((
entry
)
=>
!
syncedBatchCodes
.
includes
(
entry
.
key
));
const
activeYearSelectableEntries
=
activeYearEntries
.
filter
((
entry
)
=>
!
syncedBatchCodes
.
includes
(
entry
.
key
));
const
allActiveYearSelected
=
activeYearSelectableEntries
.
length
>
0
const
allActiveYearSelected
=
activeYearSelectableEntries
.
length
>
0
&&
activeYearSelectableEntries
.
every
((
entry
)
=>
selectedBatchCodes
.
includes
(
entry
.
key
));
&&
activeYearSelectableEntries
.
every
((
entry
)
=>
selectedBatchCodes
.
includes
(
entry
.
key
));
...
@@ -337,8 +352,8 @@ export default function Models() {
...
@@ -337,8 +352,8 @@ export default function Models() {
:
current
.
filter
((
code
)
=>
!
activeCodes
.
includes
(
code
)));
:
current
.
filter
((
code
)
=>
!
activeCodes
.
includes
(
code
)));
}
}
function
handle
ActiveYearBatchSelection
(
key
s
)
{
function
handle
BatchSelection
(
keys
,
entrie
s
)
{
const
selectableCodes
=
new
Set
(
activeYearSelectableEntries
.
map
((
entry
)
=>
entry
.
key
));
const
selectableCodes
=
new
Set
(
entries
.
filter
((
entry
)
=>
!
syncedBatchCodes
.
includes
(
entry
.
key
))
.
map
((
entry
)
=>
entry
.
key
));
const
nextCodes
=
keys
===
'all'
?
selectableCodes
:
new
Set
(
keys
);
const
nextCodes
=
keys
===
'all'
?
selectableCodes
:
new
Set
(
keys
);
setSelectedBatchCodes
((
current
)
=>
[
setSelectedBatchCodes
((
current
)
=>
[
...
current
.
filter
((
code
)
=>
!
selectableCodes
.
has
(
code
)),
...
current
.
filter
((
code
)
=>
!
selectableCodes
.
has
(
code
)),
...
@@ -521,32 +536,51 @@ export default function Models() {
...
@@ -521,32 +536,51 @@ export default function Models() {
{
activeYearEntries
.
length
?
(
{
activeYearEntries
.
length
?
(
<
ScrollShadow
className=
"max-h-[48vh] overflow-y-auto"
orientation=
"vertical"
size=
{
24
}
>
<
ScrollShadow
className=
"max-h-[48vh] overflow-y-auto"
orientation=
"vertical"
size=
{
24
}
>
<
ListView
<
div
className=
"space-y-3 pr-1"
>
aria
-
label=
"当前年份可同步批次"
{
activeYearModelGroups
.
map
((
modelGroup
)
=>
{
disabledKeys=
{
new
Set
(
activeYearEntries
.
filter
((
entry
)
=>
syncedBatchCodes
.
includes
(
entry
.
key
)).
map
((
entry
)
=>
entry
.
key
))
}
const
groupSelectableEntries
=
modelGroup
.
entries
.
filter
((
entry
)
=>
!
syncedBatchCodes
.
includes
(
entry
.
key
));
selectedKeys=
{
new
Set
(
activeYearEntries
.
filter
((
entry
)
=>
selectedBatchCodes
.
includes
(
entry
.
key
)).
map
((
entry
)
=>
entry
.
key
))
}
const
groupSelectedCount
=
groupSelectableEntries
.
filter
((
entry
)
=>
selectedBatchCodes
.
includes
(
entry
.
key
)).
length
;
selectionBehavior=
"toggle"
selectionMode=
"multiple"
variant=
"primary"
onSelectionChange=
{
handleActiveYearBatchSelection
}
>
{
activeYearEntries
.
map
((
entry
)
=>
{
const
synced
=
syncedBatchCodes
.
includes
(
entry
.
key
);
return
(
return
(
<
ListView
.
Item
id=
{
entry
.
key
}
key=
{
entry
.
key
}
textValue=
{
entry
.
sourceBatch
.
batch_name
}
>
<
section
className=
"overflow-hidden rounded-xl border border-default bg-surface"
key=
{
modelGroup
.
key
}
>
<
ListView
.
ItemContent
>
<
div
className=
"flex flex-wrap items-center justify-between gap-2 border-b border-default bg-surface-secondary px-4 py-3"
>
<
span
className=
"min-w-0 flex-1"
>
<
div
className=
"min-w-0"
>
<
ListView
.
Title
className=
"block"
>
{
entry
.
sourceBatch
.
batch_name
}
</
ListView
.
Title
>
<
h3
className=
"truncate text-sm font-semibold text-foreground"
>
{
modelGroup
.
model
.
model_name
}
</
h3
>
<
ListView
.
Description
className=
"block tabular-nums"
>
{
entry
.
sourceModel
.
model_name
}
·
{
entry
.
sourceModel
.
model_code
}
·
{
formatDateTime
(
entry
.
sourceBatch
.
source_batch_date
)
}
·
{
entry
.
sourceBatch
.
batch_code
}
</
ListView
.
Description
>
<
p
className=
"mt-0.5 truncate text-xs text-muted"
>
{
modelGroup
.
model
.
model_code
}
</
p
>
</
div
>
<
span
className=
"shrink-0 text-xs tabular-nums text-muted"
>
{
modelGroup
.
entries
.
length
}
个批次
{
groupSelectedCount
?
` · 已选 ${groupSelectedCount}`
:
''
}
</
span
>
</
span
>
</
ListView
.
ItemContent
>
</
div
>
<
ListView
.
ItemAction
>
<
ListView
{
synced
?
<
Tag
tone=
"emerald"
>
已同步
</
Tag
>
:
<
Description
className=
"tabular-nums"
>
{
Number
(
entry
.
sourceBatch
.
source_model_data_count
||
0
).
toLocaleString
(
'zh-CN'
)
}
条
</
Description
>
}
aria
-
label=
{
`${modelGroup.model.model_name} 批次`
}
</
ListView
.
ItemAction
>
disabledKeys=
{
new
Set
(
modelGroup
.
entries
.
filter
((
entry
)
=>
syncedBatchCodes
.
includes
(
entry
.
key
)).
map
((
entry
)
=>
entry
.
key
))
}
</
ListView
.
Item
>
selectedKeys=
{
new
Set
(
modelGroup
.
entries
.
filter
((
entry
)
=>
selectedBatchCodes
.
includes
(
entry
.
key
)).
map
((
entry
)
=>
entry
.
key
))
}
selectionBehavior=
"toggle"
selectionMode=
"multiple"
variant=
"primary"
onSelectionChange=
{
(
keys
)
=>
handleBatchSelection
(
keys
,
modelGroup
.
entries
)
}
>
{
modelGroup
.
entries
.
map
((
entry
)
=>
{
const
synced
=
syncedBatchCodes
.
includes
(
entry
.
key
);
return
(
<
ListView
.
Item
id=
{
entry
.
key
}
key=
{
entry
.
key
}
textValue=
{
entry
.
sourceBatch
.
batch_name
}
>
<
ListView
.
ItemContent
>
<
span
className=
"min-w-0 flex-1"
>
<
ListView
.
Title
className=
"block"
>
{
entry
.
sourceBatch
.
batch_name
}
</
ListView
.
Title
>
<
ListView
.
Description
className=
"block tabular-nums"
>
{
formatDateTime
(
entry
.
sourceBatch
.
source_batch_date
)
}
·
{
entry
.
sourceBatch
.
batch_code
}
</
ListView
.
Description
>
</
span
>
</
ListView
.
ItemContent
>
<
ListView
.
ItemAction
>
{
synced
?
<
Tag
tone=
"emerald"
>
已同步
</
Tag
>
:
<
Description
className=
"tabular-nums"
>
{
Number
(
entry
.
sourceBatch
.
source_model_data_count
||
0
).
toLocaleString
(
'zh-CN'
)
}
条
</
Description
>
}
</
ListView
.
ItemAction
>
</
ListView
.
Item
>
);
})
}
</
ListView
>
</
section
>
);
);
})
}
})
}
</
ListView
>
</
div
>
</
ScrollShadow
>
</
ScrollShadow
>
)
:
<
EmptyCollection
title=
"该年份没有来源批次"
description=
"请选择其他年份查看可同步批次。"
/>
}
)
:
<
EmptyCollection
title=
"该年份没有来源批次"
description=
"请选择其他年份查看可同步批次。"
/>
}
</
div
>
</
div
>
...
...
src/pages/project_manager_xy/model-monitor.business-flow.md
View file @
00b6e86c
...
@@ -60,7 +60,7 @@ loadSourceBatchData(sourceModel: SourceModel, sourceBatch: SourceBatch): Promise
...
@@ -60,7 +60,7 @@ loadSourceBatchData(sourceModel: SourceModel, sourceBatch: SourceBatch): Promise
`ANALYZE_MODEL_DATA_MX_*`
结果行不要求业务主键。每个批次只允许同步一次,结果行按来源读取顺序记录
`source_row_number`
并纯新增,不进行去重或更新。
`ANALYZE_MODEL_DATA_MX_*`
结果行不要求业务主键。每个批次只允许同步一次,结果行按来源读取顺序记录
`source_row_number`
并纯新增,不进行去重或更新。
MySQL 联调数据可通过
`scripts/mysql/model-monitor-source-fixtures.sql`
初始化。脚本只创建不存在的来源表,并按稳定键更新
3 个模型、9 个批次和 48 条结果样例;使用测试源启动前端时,必须把
`MODEL_MONITOR_CONNECTOR_NAME`
配置为对应的 MySQL Connector
。该环境只验证通用同步流程,不替代 Oracle 驱动和方言的集成验收。
MySQL 联调数据可通过
`scripts/mysql/model-monitor-source-fixtures.sql`
初始化。脚本只创建不存在的来源表,并按稳定键更新
4 个模型、16 个批次和 80 条结果样例;每个模型包含 2025 年历史批次和 2026 年当前批次,便于验证按年份分组、年份切换、跨年份勾选和全选口径。使用测试源启动前端时,必须把
`MODEL_MONITOR_CONNECTOR_NAME`
配置为对应的 MySQL Connector;当前测试环境的 Connector 名称为
`oqJDeVZszjRM`
,可用
`MODEL_MONITOR_CONNECTOR_NAME=oqJDeVZszjRM npm run dev`
启动
。该环境只验证通用同步流程,不替代 Oracle 驱动和方言的集成验收。
未来若固定数据源配置变化,只替换构建常量和适配器实现,不修改页面和本地持久化服务。
未来若固定数据源配置变化,只替换构建常量和适配器实现,不修改页面和本地持久化服务。
...
@@ -179,6 +179,6 @@ MySQL 联调数据可通过 `scripts/mysql/model-monitor-source-fixtures.sql`
...
@@ -179,6 +179,6 @@ MySQL 联调数据可通过 `scripts/mysql/model-monitor-source-fixtures.sql`
-
勾选批次记录后在视口固定 ActionBar 中执行转发、隐藏或清空选择;所有按钮继续调用原处理函数。
-
勾选批次记录后在视口固定 ActionBar 中执行转发、隐藏或清空选择;所有按钮继续调用原处理函数。
-
模型同步、批次同步、隐藏管理、转发提交、审批详情和填写使用 HeroUI Modal/AlertDialog;关闭或取消不触发保存请求。
-
模型同步、批次同步、隐藏管理、转发提交、审批详情和填写使用 HeroUI Modal/AlertDialog;关闭或取消不触发保存请求。
-
弹窗宽度按内容密度使用统一梯度:删除确认使用
`md`
,短单列表单使用
`lg`
,普通双列表单使用
`2xl`
,内容型详情、转发、批量同步和填写流程使用
`4xl`
(1088px);移动端保留 16px 安全边距,不改变关闭、校验、保存和审批回调。
-
弹窗宽度按内容密度使用统一梯度:删除确认使用
`md`
,短单列表单使用
`lg`
,普通双列表单使用
`2xl`
,内容型详情、转发、批量同步和填写流程使用
`4xl`
(1088px);移动端保留 16px 安全边距,不改变关闭、校验、保存和审批回调。
-
批量同步模型批次弹窗使用紧凑步骤条、年份筛选与全选工具行
,以及单一 HeroUI Pro ListView 原生多选;每个批次行展示来源模型信息
,切换年份继续保留其它年份已选项,已同步批次继续禁用,选择和同步口径不变。
-
批量同步模型批次弹窗使用紧凑步骤条、年份筛选与全选工具行
;年份下按来源模型分段展示批次,同一模型的批次集中在同一列表区,并在分段标题展示模型名称、编码和批次数量。各分段使用 HeroUI Pro ListView 原生多选
,切换年份继续保留其它年份已选项,已同步批次继续禁用,选择和同步口径不变。
-
查看态详情统一放在单一 secondary Surface;编辑态字段直接位于 Modal Overlay,字段必填、枚举来源、主键禁用和完成填写校验保持原规则。
-
查看态详情统一放在单一 secondary Surface;编辑态字段直接位于 Modal Overlay,字段必填、枚举来源、主键禁用和完成填写校验保持原规则。
-
Loading、错误和空状态由 HeroUI/HeroUI Pro 语义组件展示;不得生成测试记录或用伪造数值填充空页面。
-
Loading、错误和空状态由 HeroUI/HeroUI Pro 语义组件展示;不得生成测试记录或用伪造数值填充空页面。
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