Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
A
audit_project_manager
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
audit_project_manager
Commits
dfebeb43
Commit
dfebeb43
authored
Aug 31, 2026
by
Andy-bubu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: refine shared empty states and selector options
parent
60d7b42c
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
86 additions
and
68 deletions
+86
-68
ui-architecture.md
docs/ui-architecture.md
+11
-4
xinyuan-design-system.md
docs/xinyuan-design-system.md
+15
-2
xinyuan-ui-audit.md
docs/xinyuan-ui-audit.md
+1
-0
DynamicFormValueEditor.jsx
src/components/DynamicFormValueEditor.jsx
+4
-0
ReferenceSearchSelect.jsx
src/components/ReferenceSearchSelect.jsx
+1
-11
UserSearchSelect.jsx
src/components/UserSearchSelect.jsx
+1
-4
shell.css
src/design-system/shell.css
+22
-4
ReferenceSearchSelect.jsx
...s/RecordDetailDrawer/components/ReferenceSearchSelect.jsx
+1
-6
TailwindEditableField.jsx
...s/RecordDetailDrawer/components/TailwindEditableField.jsx
+1
-6
index.jsx
src/pages/RecordDetailDrawer/index.jsx
+26
-22
AuditProjectMaterialField.jsx
...oject_manager_xy/components/AuditProjectMaterialField.jsx
+3
-9
No files found.
docs/ui-architecture.md
View file @
dfebeb43
...
...
@@ -15,6 +15,8 @@ Hover/Focus/Selected/Disabled。`src/pages` 下没有页面私有 CSS/Less/SCSS
-
`AppShell`
:固定浅色根主题、Pro Sidebar Provider、Navbar 和 base-aware 页面滚动容器。
-
`DashboardSidebar`
/
`DashboardNavbar`
:固定深色导航、offcanvas、应用切换、用户菜单与通知动作;
Navbar 不再提供主题切换。
标题层级固定为 Navbar 20px/600、页面分区 18px/700、卡片/列表模块 16px/600,模块标题
不得复用页面级标题字号或字重。
移动 Sidebar 直接组合 Pro Sheet,并在真实
`Sheet.Dialog`
上提供可访问名称,继续复用
`useSidebar`
的开关状态和同一份 Sidebar 内容。
-
`ApplicationSwitcherDialog`
:直接组合 HeroUI Modal/ScrollShadow/Surface/Avatar 与 Pro
...
...
@@ -31,7 +33,8 @@ Hover/Focus/Selected/Disabled。`src/pages` 下没有页面私有 CSS/Less/SCSS
右侧;Pagination 页码控制靠右。项目业务列表使用 20/50/100 条,选择器不显示搜索框,变更时重置到第一页。
该结构是跨应用固定基线,业务页面不得改变其层级、顺序、对齐和尺寸。
-
`XinyuanAutocomplete`
/
`FilterSelect`
/
`ReferenceSearchSelect`
:当前 HeroUI
`selectedKey(s)`
、
`onSelectionChange`
、
`inputValue`
合同。
`selectedKey(s)`
、
`onSelectionChange`
、
`inputValue`
合同;下拉选项统一单行展示业务标题,
内部 ID、账号和文件辅助描述只用于搜索/匹配,不渲染为第二行。
-
`FormDatePicker`
:HeroUI DatePicker/DateField/Calendar 与既有字符串协议的页面边界映射。
-
`FileUploadField`
:Pro DropZone files 到既有业务回调的页面边界映射。
-
`XinyuanModal`
:HeroUI Modal compound 结构、批准宽度和 Surface 层级。
...
...
@@ -52,7 +55,8 @@ Hover/Focus/Selected/Disabled。`src/pages` 下没有页面私有 CSS/Less/SCSS
交互样式。
详情弹窗的功能标题、阶段 Tabs 和顶部业务操作共同属于 HeroUI
`Modal.Header`
;Header 不参与
正文滚动,
`Modal.Body`
是唯一纵向滚动容器。Header 不展示记录 ID、内部名称或冗余副标题,
正文滚动,
`Modal.Body`
是唯一纵向滚动容器。Header 右侧为全屏/关闭控制预留 64px 安全间距,
操作组在安全边界内整体右对齐,不得与窗口控制 icon 重叠。Header 不展示记录 ID、内部名称或冗余副标题,
业务标题仍按视图字段在正文中呈现。正文不得从 Tabs 下方穿过,也不得通过给 Tabs 增加私有
背景、描边或阴影来遮挡滚动内容。
...
...
@@ -66,8 +70,11 @@ Hover/Focus/Selected/Disabled。`src/pages` 下没有页面私有 CSS/Less/SCSS
`EmptyStateSurface`
(HeroUI
`Surface secondary`
)承载,统一使用
`rounded-2xl`
(16px)圆角;
有真实数据时不挂载该层。纯空状态文案使用 HeroUI Pro
`EmptyState.Description`
的正常字重和弱化
颜色,不使用默认半粗
`EmptyState.Title`
。详情字段空值使用
`.rc-empty-field-text`
,比字段标题的
`text-muted`
再降低一层,避免“暂无内容”与标题混成同一视觉层级。加载、错误、权限和模板不可用
状态保持各自反馈组件,不复用空数据背景。
`text-muted`
再降低一层,避免“暂无内容”与标题混成同一视觉层级。加载、接口错误和权限拒绝
保持各自反馈组件,不复用空数据背景;模板不可用按专门规则使用灰色状态层。
表单模板不可用时,当前实例内容区同样使用
`EmptyStateSurface`
包裹 HeroUI Pro
`EmptyState`
,
以紧凑灰色背景明确区分不可用状态;模板加载成功后只渲染真实表单内容,不保留该灰色层。
通用新增/编辑表单和动态表单行弹窗在桌面使用等宽双列;其中所有选择字段(枚举、用户、引用)
必须跨满弹窗内容宽度,不得出现半列短选择框。详情弹窗(
`SubjectViewContent`
)始终
...
...
docs/xinyuan-design-system.md
View file @
dfebeb43
...
...
@@ -25,6 +25,11 @@ Inter 与系统回退字体之间切换。
-
HeroUI Pro Sidebar 为桌面 240px,
`collapsible="offcanvas"`
;移动端使用官方 80vw。
-
HeroUI Pro Navbar 高 64px,直接使用
`Sidebar.Trigger`
;Navbar 与页面共享桌面/平板/
手机 48/32/16px 对称 gutter。
-
标题层级固定区分:Navbar 页面标题为 20px/600;页面分区标题使用 18px/700;卡片或列表
模块标题使用 16px/600。模块标题不得与 Navbar 或页面分区标题使用同一字号和字重,避免
“驾驶舱”“项目进度分析”“项目状态分布”等不同层级看起来相同。
-
详情 Modal Header 的标题、Tabs 和操作组必须避开右上角全屏/关闭控制区;Header 行预留
64px 右侧安全间距,导出等操作组在安全边界内整体右对齐,禁止与窗口控制 icon 重叠。
-
Sidebar 用户入口必须使用
`Dropdown.Trigger`
,退出调用既有 auth service 和 base-aware 路由。
-
应用切换使用 HeroUI Modal 与 HeroUI Pro
`ItemCardGroup + ItemCard`
:46rem/42rem 几何、
28px inset、24px 官方关闭控件,移动一列、
`sm`
起始终两列;每个 Logo 使用 36px
...
...
@@ -90,8 +95,11 @@ Inter 与系统回退字体之间切换。
-
全应用空数据状态(暂无数据、暂无关联记录、暂无通知、暂无内容等)统一在空数据区域增加一层
HeroUI
`Surface secondary`
灰色背景,容器使用统一
`rounded-2xl`
圆角(16px);真实数据出现时
不渲染该层。纯空状态文案使用官方
`EmptyState.Description`
的正常字重弱化层级,不使用默认半粗
`EmptyState.Title`
冒充业务数据。加载中、接口错误、权限拒绝和模板不可用属于独立状态,不得误套
空数据背景。
`EmptyState.Title`
冒充业务数据。加载中、接口错误和权限拒绝属于独立状态,不得误套空数据背景;
模板不可用按下方专门规则使用灰色状态层。
-
表单模板不可用属于需要明确识别的内容状态:在当前表单实例内容区域使用同一层紧凑灰色
`Surface secondary`
和统一圆角承载
`EmptyState`
;模板恢复可用并渲染真实表单数据后立即移除
该灰色层,不在正常表单内容后保留空状态背景。
-
详情字段的空标量、空数组、空对象或格式化后无内容统一显示共享
`.rc-empty-field-text`
(14px、
正常字重、
`--rc-text-empty`
)的“暂无内容”。该颜色比字段标题的
`--muted`
更弱,且不使用输入框、
边框或额外灰色块制造空值层级;有值时恢复正文 Token。
...
...
@@ -172,6 +180,9 @@ Inter 与系统回退字体之间切换。
`#7e22ce`
、
`#0891b2`
、
`#475569`
、
`#dc2626`
、
`#ec4899`
、
`#f97316`
、
`#facc15`
、
`#60a5fa`
、
`#a855f7`
、
`#06b6d4`
、
`#1e293b`
。图标始终使用
`AppIcons`
汇出的 Ant Design Icons,ECharts 不作为图标来源。
-
所有 Autocomplete、引用、用户和材料选择器的下拉选项只展示一行业务标题;不得在第二行
展示内部 ID、用户名、文件名、年份或其它技术描述。描述字段可以继续保留用于搜索、匹配和
数据处理,但不能渲染为选项副标题。应用切换卡片的业务描述不属于选择器选项,可按应用切换规范保留。
-
同一图表的分类色按业务展示顺序连续取用相邻色相;有显式业务顺序时必须传入稳定顺序,
无显式顺序时按稳定 key 确定性分配。禁止在有业务顺序时使用名称哈希打散首轮色板,避免相邻
扇区或相邻图例出现无序跳色色系;筛选、排序和 Tab 切换后已有业务 key 保持原色。
...
...
@@ -186,6 +197,8 @@ Inter 与系统回退字体之间切换。
随主题 Surface 自动适配。图表外层模块继续使用 24px 共享圆角。
-
严谨业务界面不使用粉色、玫红或洋红危险色;HeroUI
`danger`
/
`danger-soft`
必须继承鑫元红系
`--danger`
Token。图表中的红色序列同样使用红系,不使用粉色替代危险语义。
-
详情请求失败、详情动作失败和保存失败使用 HeroUI
`toast.danger`
短提醒(约 4 秒自动消失),
不在弹窗内容流中渲染持续占位的错误 Alert;需要用户持续处理的锁定、字段校验等业务提示仍保留在上下文中。
## 禁止事项
...
...
docs/xinyuan-ui-audit.md
View file @
dfebeb43
...
...
@@ -291,6 +291,7 @@ HeroUI Context。`@heroui/styles@3.2.4` 的两个 pnpm peer 虚拟目录仅分
-
状态环图已启用 ECharts 外部分类标签和
`labelLine`
引导线,标签显示真实状态与数量,
使用
`labelLayout`
根据
`labelLinePoints`
将标签锚定到各自折线终点,避免文案与折线错位。
-
状态环图扇区统一使用
`padAngle: 1`
、2px Surface 色间隙和
`borderRadius: 10`
,扇区边界紧凑清晰且不改变真实数量比例;图表模块外层使用 24px 圆角。
-
详情弹窗的请求、动作和保存失败改为 HeroUI Toast 短提醒,不再在弹窗正文中占用大块 Alert 高度;操作按钮组整体右对齐,主操作保持最右。
-
删除、拒绝和危险操作的
`danger-soft`
背景已从默认粉色修正为鑫元红系,活动页面不再出现
粉色危险操作视觉。
-
列表普通业务单元格已关闭 Hover 文案 Tooltip;操作列仍保留查看、编辑、删除等真实操作按钮
...
...
src/components/DynamicFormValueEditor.jsx
View file @
dfebeb43
...
...
@@ -415,6 +415,7 @@ function FormInstance({
)
:
null
}
{
error
?
(
<
EmptyStateSurface
>
<
EmptyState
role=
"alert"
size=
"sm"
>
<
EmptyState
.
Header
>
<
EmptyState
.
Media
variant=
"icon"
>
...
...
@@ -426,6 +427,7 @@ function FormInstance({
</
EmptyState
.
Description
>
</
EmptyState
.
Header
>
</
EmptyState
>
</
EmptyStateSurface
>
)
:
formIdentifier
?
(
<
div
className=
"space-y-3"
>
{
canEdit
?
(
...
...
@@ -507,6 +509,7 @@ function ReadonlyFormInstance({ instance }) {
<
Disclosure
.
Body
>
<
div
className=
"py-2"
>
{
error
?
(
<
EmptyStateSurface
>
<
EmptyState
role=
"alert"
size=
"sm"
>
<
EmptyState
.
Header
>
<
EmptyState
.
Media
variant=
"icon"
>
...
...
@@ -516,6 +519,7 @@ function ReadonlyFormInstance({ instance }) {
<
EmptyState
.
Description
>
{
error
}
</
EmptyState
.
Description
>
</
EmptyState
.
Header
>
</
EmptyState
>
</
EmptyStateSurface
>
)
:
(
<
FormDataTable
definition=
{
definition
}
...
...
src/components/ReferenceSearchSelect.jsx
View file @
dfebeb43
...
...
@@ -593,19 +593,9 @@ export default function ReferenceSearchSelect({
onInputChange=
{
setQuery
}
onOpenChange=
{
handleOpenChange
}
renderOption=
{
(
option
)
=>
(
<
div
className=
"min-w-0 flex-1"
>
<
Label
className=
"block truncate"
title=
{
option
.
label
}
>
<
Label
className=
"block min-w-0 flex-1 truncate"
title=
{
option
.
label
}
>
{
option
.
label
}
</
Label
>
{
option
.
description
?
(
<
div
className=
"truncate text-xs text-muted"
title=
{
option
.
description
}
>
{
option
.
description
}
</
div
>
)
:
null
}
</
div
>
)
}
/>
);
...
...
src/components/UserSearchSelect.jsx
View file @
dfebeb43
...
...
@@ -101,10 +101,7 @@ export default function UserSearchSelect({
}
}
}
renderOption=
{
(
option
)
=>
(
<
span
className=
"min-w-0"
>
<
Label
className=
"block truncate"
>
{
option
.
label
}
</
Label
>
<
span
className=
"block truncate text-xs text-muted"
>
{
option
.
description
}
</
span
>
</
span
>
<
Label
className=
"block min-w-0 truncate"
>
{
option
.
label
}
</
Label
>
)
}
/>
);
...
...
src/design-system/shell.css
View file @
dfebeb43
...
...
@@ -438,20 +438,38 @@
.rc-section-title
{
margin
:
0
;
color
:
var
(
--rc-text-primary
);
font-size
:
1.25rem
;
font-weight
:
var
(
--rc-font-
semi
bold
);
line-height
:
1.
7
5rem
;
font-size
:
1.
1
25rem
;
font-weight
:
var
(
--rc-font-bold
);
line-height
:
1.5rem
;
letter-spacing
:
0
;
}
.rc-module-title
{
margin
:
0
;
color
:
var
(
--rc-text-primary
);
font-size
:
1
.125
rem
;
font-size
:
1rem
;
font-weight
:
var
(
--rc-font-semibold
);
line-height
:
1.5rem
;
letter-spacing
:
0
;
}
.rc-modal-header-actions
{
align-self
:
flex-start
;
display
:
flex
;
flex
:
0
0
auto
;
flex-wrap
:
wrap
;
justify-content
:
flex-end
;
gap
:
0.5rem
;
margin-inline-start
:
auto
;
max-width
:
100%
;
}
@media
(
max-width
:
40rem
)
{
.rc-modal-header-actions
{
flex-basis
:
100%
;
}
}
.rc-page-grid
{
min-width
:
0
;
gap
:
var
(
--rc-page-module-gap
);
...
...
src/pages/RecordDetailDrawer/components/ReferenceSearchSelect.jsx
View file @
dfebeb43
...
...
@@ -114,12 +114,7 @@ export default function ReferenceSearchSelect({
onInputChange=
{
setKeyword
}
onSelectionChange=
{
handleSelectionChange
}
renderOption=
{
(
option
)
=>
(
<
span
className=
"min-w-0"
>
<
Label
className=
"block truncate"
>
{
option
.
label
}
</
Label
>
<
span
className=
"block truncate text-xs text-muted"
>
{
option
.
description
}
</
span
>
</
span
>
<
Label
className=
"block min-w-0 truncate"
>
{
option
.
label
}
</
Label
>
)
}
/>
);
...
...
src/pages/RecordDetailDrawer/components/TailwindEditableField.jsx
View file @
dfebeb43
...
...
@@ -255,12 +255,7 @@ function UserSelector({ value, onChange, multiple, placeholder = '请选择用
onInputChange=
{
setKeyword
}
onSelectionChange=
{
handleSelectionChange
}
renderOption=
{
(
option
)
=>
(
<
span
className=
"min-w-0"
>
<
Label
className=
"block truncate"
>
{
option
.
label
}
</
Label
>
<
span
className=
"block truncate text-xs text-muted"
>
{
option
.
description
}
</
span
>
</
span
>
<
Label
className=
"block min-w-0 truncate"
>
{
option
.
label
}
</
Label
>
)
}
/>
);
...
...
src/pages/RecordDetailDrawer/index.jsx
View file @
dfebeb43
...
...
@@ -147,6 +147,24 @@ const RecordDetailDrawer = (props, ref) => {
editSessionRef
.
current
+=
1
;
},
[
open
,
recordName
]);
useEffect
(()
=>
{
if
(
error
)
{
toast
.
danger
(
error
,
{
timeout
:
4000
});
}
},
[
error
]);
useEffect
(()
=>
{
if
(
actionError
)
{
toast
.
danger
(
actionError
,
{
timeout
:
4000
});
}
},
[
actionError
]);
useEffect
(()
=>
{
if
(
saveError
)
{
toast
.
danger
(
saveError
,
{
timeout
:
4000
});
}
},
[
saveError
]);
useEffect
(()
=>
{
if
(
!
isEditing
)
{
setDraftRecord
(
cloneRecord
(
editableRecord
));
...
...
@@ -504,13 +522,15 @@ const RecordDetailDrawer = (props, ref) => {
)
:
null
}
</
div
>
<
Modal
.
Header
className=
"shrink-0 pr-16"
>
<
Modal
.
Header
className=
"shrink-0"
>
<
div
className=
"flex w-full min-w-0 flex-wrap items-start gap-3 pr-16"
>
<
div
className=
"min-w-0 flex-1"
>
<
Modal
.
Heading
>
{
title
||
'记录详情'
}
</
Modal
.
Heading
>
{
detailViews
.
length
>
1
?
(
<
Tabs
aria
-
label=
"详情视图"
className=
"
w-fit max-w-full min-w-0"
className=
"mt-3
w-fit max-w-full min-w-0"
selectedKey=
{
activeView
?.
name
}
variant=
"primary"
onSelectionChange=
{
setActiveTabKey
}
...
...
@@ -532,9 +552,10 @@ const RecordDetailDrawer = (props, ref) => {
</
Tabs
.
ListContainer
>
</
Tabs
>
)
:
null
}
</
div
>
{
visibleHeaderActions
.
length
>
0
?
(
<
div
className=
"flex min-w-0 flex-wrap justify-end gap-2
"
>
<
div
className=
"rc-modal-header-actions
"
>
{
visibleHeaderActions
.
map
((
action
)
=>
(
<
Button
isDisabled=
{
Boolean
(
actionLoadingKey
)
||
isActionDisabled
(
action
)
}
...
...
@@ -549,6 +570,7 @@ const RecordDetailDrawer = (props, ref) => {
))
}
</
div
>
)
:
null
}
</
div
>
</
Modal
.
Header
>
<
Modal
.
Body
>
...
...
@@ -556,26 +578,8 @@ const RecordDetailDrawer = (props, ref) => {
<
div
className=
"flex min-h-48 items-center justify-center"
role=
"status"
>
<
Spinner
size=
"sm"
/>
</
div
>
)
:
error
?
(
<
Alert
status=
"danger"
>
<
Alert
.
Content
><
Alert
.
Description
>
{
error
}
</
Alert
.
Description
></
Alert
.
Content
>
</
Alert
>
)
:
(
)
:
error
?
null
:
(
<
div
>
{
actionError
?
(
<
Alert
className=
"mb-4"
status=
"danger"
>
<
Alert
.
Content
>
<
Alert
.
Description
>
{
actionError
}
</
Alert
.
Description
>
</
Alert
.
Content
>
</
Alert
>
)
:
null
}
{
saveError
?
(
<
Alert
className=
"mb-4"
status=
"danger"
>
<
Alert
.
Content
>
<
Alert
.
Description
>
{
saveError
}
</
Alert
.
Description
>
</
Alert
.
Content
>
</
Alert
>
)
:
null
}
{
isRecordLocked
?
(
<
Alert
status=
"warning"
>
<
Alert
.
Content
>
...
...
src/pages/project_manager_xy/components/AuditProjectMaterialField.jsx
View file @
dfebeb43
...
...
@@ -29,11 +29,6 @@ function appendUniqueFiles(currentFiles, nextFiles) {
return
[...
filesByKey
.
values
()];
}
function
formatMaterialDescription
(
material
)
{
return
[
material
.
fileAsset
?.
name
,
material
.
archiveCategory
,
material
.
archiveYear
]
.
filter
(
Boolean
).
join
(
' · '
);
}
export
default
function
AuditProjectMaterialField
({
editSessionId
,
fieldPath
,
...
...
@@ -242,10 +237,9 @@ export default function AuditProjectMaterialField({
onInputChange={setKeyword}
onSelectionChange={handleMaterialSelection}
renderOption={(material) => (
<span className="min-w-0">
<Label className="block truncate" title={material.materialTitle}>{material.materialTitle}</Label>
<span className="mt-0.5 block truncate text-xs text-muted" title={formatMaterialDescription(material)}>{formatMaterialDescription(material)}</span>
</span>
<Label className="block min-w-0 truncate" title={material.materialTitle}>
{material.materialTitle}
</Label>
)}
/>
{materialsError ? (
...
...
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