Commit dfebeb43 authored by Andy-bubu's avatar Andy-bubu

fix: refine shared empty states and selector options

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