Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
O
oa_process_monitor
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
oa_process_monitor
Commits
2abf70b5
Commit
2abf70b5
authored
Aug 26, 2026
by
Andy-bubu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: align application switcher with xinyuan baseline
parent
627f09e4
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
96 additions
and
17 deletions
+96
-17
ui-architecture.md
docs/ui-architecture.md
+2
-0
ui-migration-checklist.md
docs/ui-migration-checklist.md
+1
-1
xinyuan-design-system.md
docs/xinyuan-design-system.md
+1
-1
xinyuan-ui-audit.md
docs/xinyuan-ui-audit.md
+5
-2
ApplicationSwitcherDialog.jsx
src/components/ApplicationSwitcherDialog.jsx
+82
-13
process-monitor.business-flow.md
src/pages/process_monitor/process-monitor.business-flow.md
+5
-0
No files found.
docs/ui-architecture.md
View file @
2abf70b5
...
@@ -4,6 +4,7 @@
...
@@ -4,6 +4,7 @@
-
Umi Max React 应用,base/publicPath 为
`/oa_process_monitor_new/`
。
-
Umi Max React 应用,base/publicPath 为
`/oa_process_monitor_new/`
。
-
`Site`
加载站点配置,
`Auth`
维护用户和真实退出,再进入
`AppShell`
。
-
`Site`
加载站点配置,
`Auth`
维护用户和真实退出,再进入
`AppShell`
。
-
应用切换由
`SiteContext`
提供
`/info -> site.group`
,按应用
`name`
与当前用户真实可访问应用匹配;分组仅负责展示顺序,不参与权限或跳转决策。
-
服务层继续拥有 API、Subject、payload、动态字段和业务转换;页面持有筛选、页码、选择、权限及回调。
-
服务层继续拥有 API、Subject、payload、动态字段和业务转换;页面持有筛选、页码、选择、权限及回调。
-
单个低频结构化筛选由列表工具栏的 HeroUI Popover 承载,Popover 内继续使用 HeroUI Autocomplete;父浮层保留子 Autocomplete Portal 的交互边界。
-
单个低频结构化筛选由列表工具栏的 HeroUI Popover 承载,Popover 内继续使用 HeroUI Autocomplete;父浮层保留子 Autocomplete Portal 的交互边界。
...
@@ -11,6 +12,7 @@
...
@@ -11,6 +12,7 @@
-
基础组件直接来自
`@heroui/react`
,Sidebar/Navbar/DataGrid/KPI/EmptyState 等直接来自
`@heroui-pro/react`
官方子路径。
-
基础组件直接来自
`@heroui/react`
,Sidebar/Navbar/DataGrid/KPI/EmptyState 等直接来自
`@heroui-pro/react`
官方子路径。
-
共享组件只做业务无关组合:Shell、应用切换、Autocomplete、详情导航、标题说明和行操作。
-
共享组件只做业务无关组合:Shell、应用切换、Autocomplete、详情导航、标题说明和行操作。
-
应用切换直接使用 HeroUI Modal/Card/Surface/Avatar 与 HeroUI Pro EmptyState;Modal Body 是唯一滚动容器,桌面及平板固定两列、移动端一列。
-
compound API 使用点语法,Button 使用
`onPress`
;不提供 Catalyst/Headless 旧 props 兼容层。
-
compound API 使用点语法,Button 使用
`onPress`
;不提供 Catalyst/Headless 旧 props 兼容层。
-
`XinyuanAutocomplete`
组合官方 Autocomplete、Popover Heading、SearchField 和 ListBox,不定义组件外观。
-
`XinyuanAutocomplete`
组合官方 Autocomplete、Popover Heading、SearchField 和 ListBox,不定义组件外观。
...
...
docs/ui-migration-checklist.md
View file @
2abf70b5
...
@@ -18,7 +18,7 @@
...
@@ -18,7 +18,7 @@
-
[
x
]
页面私有视觉 CSS 为零
-
[
x
]
页面私有视觉 CSS 为零
-
[
x
]
登录、Sidebar 真 offcanvas、Navbar 与 48/32/16 gutter
-
[
x
]
登录、Sidebar 真 offcanvas、Navbar 与 48/32/16 gutter
-
[
x
]
用户 Dropdown 与原有真实 logout 回调
-
[
x
]
用户 Dropdown 与原有真实 logout 回调
-
[
x
]
HeroUI Modal 应用切换、
两列卡片、identity well 和
当前状态
-
[
x
]
HeroUI Modal 应用切换、
`site.group`
动态分组、真实数量、两列卡片、identity well 和唯一
当前状态
## 页面
## 页面
...
...
docs/xinyuan-design-system.md
View file @
2abf70b5
...
@@ -27,7 +27,7 @@
...
@@ -27,7 +27,7 @@
-
Navbar 高 64px,直接使用 HeroUI Pro
`Sidebar.Trigger`
默认图形。
-
Navbar 高 64px,直接使用 HeroUI Pro
`Sidebar.Trigger`
默认图形。
-
Navbar 和页面共用左右 gutter:桌面 48px、平板 32px、手机 16px。
-
Navbar 和页面共用左右 gutter:桌面 48px、平板 32px、手机 16px。
-
Sidebar 用户入口通过
`Dropdown.Trigger`
打开菜单,退出必须调用现有真实 logout。
-
Sidebar 用户入口通过
`Dropdown.Trigger`
打开菜单,退出必须调用现有真实 logout。
-
应用切换使用 HeroUI Modal,桌面宽 46rem/最大高 42rem,手机一列、
`sm`
起固定两列。
-
应用切换使用 HeroUI Modal,桌面宽 46rem/最大高 42rem,手机一列、
`sm`
起固定两列
;分组来自
`/info -> site.group`
,保留后端顺序并提供“其他应用/全部应用”回退
。
## Surface 与布局
## Surface 与布局
...
...
docs/xinyuan-ui-audit.md
View file @
2abf70b5
...
@@ -5,7 +5,7 @@
...
@@ -5,7 +5,7 @@
## 范围与基准
## 范围与基准
-
目标仓库:
`oa_process_monitor`
,分支
`codex/xinyuan-ui-ux`
。
-
目标仓库:
`oa_process_monitor`
,分支
`codex/xinyuan-ui-ux`
。
-
唯一实现基准:本机
`problem_rectification`
的实际代码与运行页面
。
-
整体实现基准:本机
`problem_rectification`
的实际代码与运行页面;应用切换专项基准为本机
`model_analysis`
当前实现
。
-
路由基址:
`/oa_process_monitor_new/`
。
-
路由基址:
`/oa_process_monitor_new/`
。
-
活动路由:
`/login`
、
`/process_monitor`
、
`/process_monitor/:processId`
、
`/process_monitor/:processId/:application`
。
-
活动路由:
`/login`
、
`/process_monitor`
、
`/process_monitor/:processId`
、
`/process_monitor/:processId/:application`
。
-
业务依据:
`src/services/record-api.md`
、
`src/services/subjectsDefinition.md`
、
`src/pages/process_monitor/process-monitor.business-flow.md`
。
-
业务依据:
`src/services/record-api.md`
、
`src/services/subjectsDefinition.md`
、
`src/pages/process_monitor/process-monitor.business-flow.md`
。
...
@@ -30,7 +30,8 @@
...
@@ -30,7 +30,8 @@
## 迁移后审计结论
## 迁移后审计结论
-
登录、Shell、Sidebar、Navbar、用户退出菜单和应用切换均改为问题整改共享结构。
-
登录、Shell、Sidebar、Navbar 和用户退出菜单使用问题整改共享结构;应用切换同步 model_analysis 的 HeroUI Modal 几何、动态分组、真实数量和两列卡片结构。
-
应用切换消费现有
`SiteContext`
的
`/info -> site.group`
,按应用名称匹配真实可访问应用,保持分组与组内顺序,并保留原有应用获取、合并、当前应用判断及跳转逻辑。
-
三个流程监控页面直接使用 HeroUI/HeroUI Pro;KPI 使用 Pro KPI,列表使用 Pro DataGrid,空状态使用 Pro EmptyState。
-
三个流程监控页面直接使用 HeroUI/HeroUI Pro;KPI 使用 Pro KPI,列表使用 Pro DataGrid,空状态使用 Pro EmptyState。
-
筛选使用 HeroUI SearchField/Autocomplete,弹窗使用 Modal/AlertDialog,分页固定 10 条并使用 compound Pagination。
-
筛选使用 HeroUI SearchField/Autocomplete,弹窗使用 Modal/AlertDialog,分页固定 10 条并使用 compound Pagination。
-
详情查看态只保留一个
`Surface secondary`
;页面不再定义组件背景、颜色、描边、圆角、阴影或交互状态。
-
详情查看态只保留一个
`Surface secondary`
;页面不再定义组件背景、颜色、描边、圆角、阴影或交互状态。
...
@@ -48,6 +49,8 @@
...
@@ -48,6 +49,8 @@
## 浏览器证据与限制
## 浏览器证据与限制
-
已覆盖 1440x900、1280x720、768x1024、390x844;包含浅色、深色、Sidebar offcanvas、移动 Sidebar、应用切换和创建分组 Modal。
-
已覆盖 1440x900、1280x720、768x1024、390x844;包含浅色、深色、Sidebar offcanvas、移动 Sidebar、应用切换和创建分组 Modal。
-
2026-08-26 应用切换专项复验同时打开目标应用与
`model_analysis`
:四个视口的弹窗位置、宽高、24px 圆角、28px 内边距、Header/Body 起点、分组顶点、两列/单列断点及卡片尺寸逐项一致;深色 Portal 无浅色孤岛。
-
浏览器层覆盖动态分组顺序、组内顺序、空分组隐藏、未归组应用进入“其他应用”、无分组回退“全部应用”、真实数量、唯一“当前”、长列表 Body-only 滚动、失败/透明/缺失 Logo 稳定 identity well,以及 Escape、遮罩、关闭按钮和焦点顺序。
-
最终目标与基准应用均已在 1440x900、1280x720、768x1024、390x844 的浅色和深色模式完成本地截图比对;验收截图仅作本地临时证据,不纳入版本控制。
-
最终目标与基准应用均已在 1440x900、1280x720、768x1024、390x844 的浅色和深色模式完成本地截图比对;验收截图仅作本地临时证据,不纳入版本控制。
-
另记录
`final-production-app-switcher-1440x900-light.png`
、
`final-production-app-switcher-1440x900-three-apps.png`
和移动 Sidebar 截图;在相同视口逐项比较登录、Shell、页面 gutter、Surface、KPI、筛选、空 DataGrid、Pagination 和 overlay 几何。
-
另记录
`final-production-app-switcher-1440x900-light.png`
、
`final-production-app-switcher-1440x900-three-apps.png`
和移动 Sidebar 截图;在相同视口逐项比较登录、Shell、页面 gutter、Surface、KPI、筛选、空 DataGrid、Pagination 和 overlay 几何。
-
干净退出会话与应用切换会话的浏览器控制台均为 0 error / 0 warning;Autocomplete、Modal、应用切换和移动 Sidebar 的可访问名称已通过生产预览快照确认。
-
干净退出会话与应用切换会话的浏览器控制台均为 0 error / 0 warning;Autocomplete、Modal、应用切换和移动 Sidebar 的可访问名称已通过生产预览快照确认。
...
...
src/components/ApplicationSwitcherDialog.jsx
View file @
2abf70b5
...
@@ -96,6 +96,57 @@ function mergeApplications(fetchedApplications, fallbackApplications) {
...
@@ -96,6 +96,57 @@ function mergeApplications(fetchedApplications, fallbackApplications) {
});
});
}
}
function
buildApplicationSections
(
applications
,
applicationGroups
)
{
if
(
!
Array
.
isArray
(
applicationGroups
)
||
applicationGroups
.
length
===
0
)
{
return
[{
key
:
'all'
,
label
:
'全部应用'
,
applications
}];
}
const
applicationMap
=
new
Map
(
applications
.
map
((
application
)
=>
[
application
.
name
,
application
]),
);
const
groupedApplicationNames
=
new
Set
();
const
sections
=
applicationGroups
.
map
((
group
,
index
)
=>
{
const
groupApplications
=
Array
.
isArray
(
group
?.
applications
)
?
group
.
applications
.
map
((
name
)
=>
{
if
(
groupedApplicationNames
.
has
(
name
))
{
return
null
;
}
const
application
=
applicationMap
.
get
(
name
);
if
(
application
)
{
groupedApplicationNames
.
add
(
name
);
}
return
application
;
})
.
filter
(
Boolean
)
:
[];
return
{
key
:
`group-
${
index
}
-
${
group
?.
label
||
'application-group'
}
`,
label: group?.label || '应用分组',
applications: groupApplications,
};
})
.filter((section) => section.applications.length > 0);
const ungroupedApplications = applications.filter(
(application) => !groupedApplicationNames.has(application.name),
);
if (ungroupedApplications.length > 0) {
sections.push({
key: 'ungrouped',
label: '其他应用',
applications: ungroupedApplications,
});
}
return sections;
}
function isCurrentApplication(application) {
function isCurrentApplication(application) {
if (typeof window === 'undefined') {
if (typeof window === 'undefined') {
return false;
return false;
...
@@ -153,12 +204,12 @@ function ApplicationCard({ application, current }) {
...
@@ -153,12 +204,12 @@ function ApplicationCard({ application, current }) {
</
Avatar
>
</
Avatar
>
</
Surface
>
</
Surface
>
<
span
className=
"min-w-0 flex-1"
>
<
span
className=
"min-w-0 flex-1"
>
<
span
className=
"block
break-words font-medium leading-5 text-foreground
"
title=
{
application
.
title
}
>
<
span
className=
"block
truncate text-sm font-medium leading-5
"
title=
{
application
.
title
}
>
{
application
.
title
}
{
application
.
title
}
</
span
>
</
span
>
<
span
className=
"block truncate text-sm text-muted
"
title=
{
application
.
description
}
>
<
Description
className=
"mt-0.5 block truncate leading-4
"
title=
{
application
.
description
}
>
{
application
.
description
||
application
.
name
||
'应用'
}
{
application
.
description
||
application
.
name
||
'应用'
}
</
spa
n
>
</
Descriptio
n
>
</
span
>
</
span
>
{
current
?
<
Chip
color=
"accent"
size=
"sm"
>
当前
</
Chip
>
:
null
}
{
current
?
<
Chip
color=
"accent"
size=
"sm"
>
当前
</
Chip
>
:
null
}
</
a
>
</
a
>
...
@@ -188,6 +239,7 @@ function LoadingSkeleton() {
...
@@ -188,6 +239,7 @@ function LoadingSkeleton() {
export
default
function
ApplicationSwitcherDialog
({
export
default
function
ApplicationSwitcherDialog
({
open
,
open
,
onClose
,
onClose
,
siteInfo
,
userInfo
,
userInfo
,
})
{
})
{
const
fallbackApplications
=
useMemo
(()
=>
{
const
fallbackApplications
=
useMemo
(()
=>
{
...
@@ -215,6 +267,10 @@ export default function ApplicationSwitcherDialog({
...
@@ -215,6 +267,10 @@ export default function ApplicationSwitcherDialog({
const
acceptedNames
=
new
Set
([
baseName
,
baseName
.
replace
(
/_new$/
,
''
)]);
const
acceptedNames
=
new
Set
([
baseName
,
baseName
.
replace
(
/_new$/
,
''
)]);
return
applications
.
find
((
application
)
=>
acceptedNames
.
has
(
application
.
name
)
||
isCurrentApplication
(
application
))?.
name
||
''
;
return
applications
.
find
((
application
)
=>
acceptedNames
.
has
(
application
.
name
)
||
isCurrentApplication
(
application
))?.
name
||
''
;
},
[
applications
]);
},
[
applications
]);
const
applicationSections
=
useMemo
(
()
=>
buildApplicationSections
(
applications
,
siteInfo
?.
group
),
[
applications
,
siteInfo
?.
group
],
);
useEffect
(()
=>
{
useEffect
(()
=>
{
if
(
fallbackApplications
.
length
===
0
)
{
if
(
fallbackApplications
.
length
===
0
)
{
...
@@ -287,21 +343,24 @@ export default function ApplicationSwitcherDialog({
...
@@ -287,21 +343,24 @@ export default function ApplicationSwitcherDialog({
<
Modal
.
Container
className=
"w-full sm:w-full"
placement=
"center"
scroll=
"inside"
size=
"lg"
>
<
Modal
.
Container
className=
"w-full sm:w-full"
placement=
"center"
scroll=
"inside"
size=
"lg"
>
<
Modal
.
Dialog
<
Modal
.
Dialog
aria
-
label=
"应用切换"
aria
-
label=
"应用切换"
className=
"max-h-[min(42rem,calc(100dvh-2rem))]"
className=
"p-7"
style=
{
{
maxWidth
:
'min(46rem, calc(100vw - 2rem))'
}
}
style=
{
{
width
:
'min(46rem, calc(100vw - 2rem))'
,
maxWidth
:
'min(46rem, calc(100vw - 2rem))'
,
maxHeight
:
'min(42rem, calc(100dvh - 2rem))'
,
}
}
>
<
Modal
.
CloseTrigger
aria
-
label=
"关闭"
style=
{
{
insetInlineEnd
:
'1.75rem'
,
top
:
'1.75rem'
}
}
>
>
<
Modal
.
CloseTrigger
aria
-
label=
"关闭"
>
<
Close
size=
{
16
}
/>
<
Close
size=
{
16
}
/>
</
Modal
.
CloseTrigger
>
</
Modal
.
CloseTrigger
>
<
Modal
.
Header
>
<
Modal
.
Header
>
<
div
className=
"min-w-0"
>
<
Modal
.
Heading
>
应用切换
</
Modal
.
Heading
>
<
Modal
.
Heading
>
应用切换
</
Modal
.
Heading
>
<
Description
>
<
div
className=
"text-sm text-muted"
>
选择你可访问的应用
</
div
>
选择你可访问的应用
</
Description
>
</
div
>
</
Modal
.
Header
>
</
Modal
.
Header
>
<
Modal
.
Body
>
<
Modal
.
Body
style=
{
{
marginTop
:
'0.75rem'
}
}
>
{
status
===
'error'
&&
errorMessage
?
(
{
status
===
'error'
&&
errorMessage
?
(
<
Alert
status=
"warning"
>
<
Alert
status=
"warning"
>
<
Alert
.
Indicator
><
ExclamationTriangleIcon
size=
{
20
}
/></
Alert
.
Indicator
>
<
Alert
.
Indicator
><
ExclamationTriangleIcon
size=
{
20
}
/></
Alert
.
Indicator
>
...
@@ -312,8 +371,15 @@ export default function ApplicationSwitcherDialog({
...
@@ -312,8 +371,15 @@ export default function ApplicationSwitcherDialog({
{
status
===
'loading'
&&
applications
.
length
===
0
?
(
{
status
===
'loading'
&&
applications
.
length
===
0
?
(
<
LoadingSkeleton
/>
<
LoadingSkeleton
/>
)
:
applications
.
length
>
0
?
(
)
:
applications
.
length
>
0
?
(
<
div
className=
"space-y-6"
>
{
applicationSections
.
map
((
section
)
=>
(
<
section
key=
{
section
.
key
}
>
<
div
className=
"mb-3 flex items-center justify-between gap-3"
>
<
h3
className=
"text-sm font-medium leading-5"
>
{
section
.
label
}
</
h3
>
<
Description
>
{
section
.
applications
.
length
}
个应用
</
Description
>
</
div
>
<
div
className=
"grid gap-3 sm:grid-cols-2"
>
<
div
className=
"grid gap-3 sm:grid-cols-2"
>
{
applications
.
map
((
application
)
=>
(
{
section
.
applications
.
map
((
application
)
=>
(
<
ApplicationCard
<
ApplicationCard
key=
{
application
.
name
}
key=
{
application
.
name
}
application=
{
application
}
application=
{
application
}
...
@@ -321,6 +387,9 @@ export default function ApplicationSwitcherDialog({
...
@@ -321,6 +387,9 @@ export default function ApplicationSwitcherDialog({
/>
/>
))
}
))
}
</
div
>
</
div
>
</
section
>
))
}
</
div
>
)
:
(
)
:
(
<
EmptyState
size=
"md"
>
<
EmptyState
size=
"md"
>
<
EmptyState
.
Header
>
<
EmptyState
.
Header
>
...
...
src/pages/process_monitor/process-monitor.business-flow.md
View file @
2abf70b5
...
@@ -36,6 +36,11 @@
...
@@ -36,6 +36,11 @@
3.
`/process_monitor/:processId/:application`
3.
`/process_monitor/:processId/:application`
单条流程实例详情
单条流程实例详情
Shell 中的应用切换入口继续读取当前登录用户可访问应用,并结合
`GET /info -> site.group`
动态分组:按后端分组及组内顺序展示非空分组,
未被引用的可访问应用归入“其他应用”,没有分组配置时回退为“全部应用”。
弹窗只改变展示结构,不改变应用权限、当前应用判断和跳转逻辑。
---
---
## 4. 核心对象与动作
## 4. 核心对象与动作
...
...
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