Commit 0f9e79a0 authored by Andy-bubu's avatar Andy-bubu

fix: align application switcher with Xinyuan baseline

parent 14cd2363
...@@ -20,6 +20,7 @@ app.js (主题初始化) ...@@ -20,6 +20,7 @@ app.js (主题初始化)
- `src/config/application.js`:应用名、base、主题存储键。 - `src/config/application.js`:应用名、base、主题存储键。
- `src/design-system/`:权威主题、Tokens 和跨应用 Shell 样式;页面不得复制这些规则。 - `src/design-system/`:权威主题、Tokens 和跨应用 Shell 样式;页面不得复制这些规则。
- `AppShell/DashboardSidebar/DashboardNavbar/ApplicationSwitcherDialog`:业务无关的共享壳层。 - `AppShell/DashboardSidebar/DashboardNavbar/ApplicationSwitcherDialog`:业务无关的共享壳层。
- `ApplicationSwitcherDialog` 从既有 `SiteContext.site.group` 读取动态分组,从 `GET /application/info` 与 `UserContext.applications` 合并真实可访问应用;分组仅影响呈现,不改变路由、会话或跳转。
- `SubjectWorkbench`:把现有 Subject/View/动作配置适配为工作台 props,不定义组件外观。 - `SubjectWorkbench`:把现有 Subject/View/动作配置适配为工作台 props,不定义组件外观。
- `record-workbench`:DataGrid/Pagination/ActionBar 组合;详情能力统一映射为操作列首个预览图标,首个业务列仅展示文本;不发请求、不解释业务动作。 - `record-workbench`:DataGrid/Pagination/ActionBar 组合;详情能力统一映射为操作列首个预览图标,首个业务列仅展示文本;不发请求、不解释业务动作。
- `RecordDetailDrawer`:详情草稿、Modal 状态和字段编排;保存仍由 `useSubjectRecord` 处理。 - `RecordDetailDrawer`:详情草稿、Modal 状态和字段编排;保存仍由 `useSubjectRecord` 处理。
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
- [x] 登录、鉴权、真实退出 - [x] 登录、鉴权、真实退出
- [x] Pro Sidebar/Navbar/offcanvas/mobile - [x] Pro Sidebar/Navbar/offcanvas/mobile
- [x] HeroUI 应用切换 Modal:46rem/42rem、桌面两列/移动单列、12px 列间距、API Logo/首字 identity、唯一当前态;用户 Dropdown - [x] HeroUI 应用切换 Modal:model_analysis 基准、46rem/42rem、28px inset、动态分组与真实计数、桌面两列/移动单列、API Logo/首字 identity、唯一当前态;用户 Dropdown
- [x] 七条 SubjectWorkbench 路由 - [x] 七条 SubjectWorkbench 路由
- [x] Tabs、SearchField/Surface 筛选 - [x] Tabs、SearchField/Surface 筛选
- [x] Pro DataGrid 选择、排序、固定列、横向滚动 - [x] Pro DataGrid 选择、排序、固定列、横向滚动
......
...@@ -17,7 +17,7 @@ Inter 禁止通过 `src/tailwind.css` 导入。Tailwind v4 内容扫描必须排 ...@@ -17,7 +17,7 @@ Inter 禁止通过 `src/tailwind.css` 导入。Tailwind v4 内容扫描必须排
- Shell:`@heroui-pro/react/sidebar`、`navbar`;桌面 offcanvas 真折叠,移动端 `Sidebar.Mobile`。 - Shell:`@heroui-pro/react/sidebar`、`navbar`;桌面 offcanvas 真折叠,移动端 `Sidebar.Mobile`。
- 全局操作:HeroUI Button/Tooltip/Dropdown;用户菜单的退出项调用真实登出回调。 - 全局操作:HeroUI Button/Tooltip/Dropdown;用户菜单的退出项调用真实登出回调。
- 应用切换:HeroUI Modal + Card/Surface/Avatar/Chip;最大 46rem x 42rem,桌面两列、移动端单列,列间距 12px。应用项使用 HeroUI Card 默认 24px 圆角与 16px 内边距;API 有 Logo 时显示真实 Logo,无 Logo 时显示应用首字,不得给所有应用重复套用鑫元 Logo。 - 应用切换:严格使用 model_analysis 当前 HeroUI Modal + Card/Surface/Avatar/Chip 基准;最大 46rem x 42rem、28px inset、24px 圆角,桌面两列、移动端单列,间距 12px。分组来自 `site.group`,保持后端分组与组内顺序并显示真实数量;未分组应用进入“其他应用”,无配置时回退“全部应用”。API 有 Logo 时显示真实 Logo,无 Logo 时显示应用首字,当前应用只显示一个 accent Chip。
- 数据视图:HeroUI Tabs;不手写 segmented/tab 外观。 - 数据视图:HeroUI Tabs;不手写 segmented/tab 外观。
- 筛选:HeroUI Surface + SearchField/Autocomplete/DatePicker/Disclosure。没有业务配置的筛选不得添加。 - 筛选:HeroUI Surface + SearchField/Autocomplete/DatePicker/Disclosure。没有业务配置的筛选不得添加。
- 列表:HeroUI Pro DataGrid、EmptyState、ActionBar + HeroUI Pagination;分页固定沿用业务默认每页 10 条,不显示额外页容量选择器。首列只在真实横向溢出时固定且仅展示文本,详情入口统一为操作列首个预览图标。操作列宽度由 32px 动作按钮、8px 间距和标准单元格内边距计算,`minWidth/width/maxWidth` 必须一致;表格未横向溢出时首个业务列使用 `1fr` 吸收剩余宽度,操作列不得参与余量分配;仅在横向溢出时固定在右端。 - 列表:HeroUI Pro DataGrid、EmptyState、ActionBar + HeroUI Pagination;分页固定沿用业务默认每页 10 条,不显示额外页容量选择器。首列只在真实横向溢出时固定且仅展示文本,详情入口统一为操作列首个预览图标。操作列宽度由 32px 动作按钮、8px 间距和标准单元格内边距计算,`minWidth/width/maxWidth` 必须一致;表格未横向溢出时首个业务列使用 `1fr` 吸收剩余宽度,操作列不得参与余量分配;仅在横向溢出时固定在右端。
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
- 登录:已使用基准居中 Card、Logo、字段高度、24px 节奏、验证码、filled/focus/autofill 状态;保留原登录协议。 - 登录:已使用基准居中 Card、Logo、字段高度、24px 节奏、验证码、filled/focus/autofill 状态;保留原登录协议。
- Shell:HeroUI Pro `Sidebar.Provider collapsible="offcanvas"`、Sidebar、Mobile、Trigger、Navbar 与真实用户 Dropdown/登出。 - Shell:HeroUI Pro `Sidebar.Provider collapsible="offcanvas"`、Sidebar、Mobile、Trigger、Navbar 与真实用户 Dropdown/登出。
- 应用切换:HeroUI Modal,46rem/42rem 合同,桌面两列、移动端单列,当前应用状态与 identity well。 - 应用切换:以 model_analysis 当前实现为唯一基准,HeroUI Modal 46rem/42rem、28px inset、桌面两列/移动端单列;复用 `SiteContext.site.group` 动态分组,显示真实计数,并保留唯一当前态与 identity well。
- Tabs/筛选:HeroUI Tabs、SearchField、Surface;当前业务仅配置标题关键词,不添加虚构筛选字段。 - Tabs/筛选:HeroUI Tabs、SearchField、Surface;当前业务仅配置标题关键词,不添加虚构筛选字段。
- 列表:Pro DataGrid、原生多选/排序/列宽/固定列/横向滚动、HeroUI Pagination、Pro ActionBar/EmptyState;分页固定每页 10 条并按问题整改基准移除页容量选择器;4 个以上动作只直出前两个,其余进入 Dropdown,危险动作最后。 - 列表:Pro DataGrid、原生多选/排序/列宽/固定列/横向滚动、HeroUI Pagination、Pro ActionBar/EmptyState;分页固定每页 10 条并按问题整改基准移除页容量选择器;4 个以上动作只直出前两个,其余进入 Dropdown,危险动作最后。
- 弹窗:HeroUI Modal/AlertDialog;详情查看态单一 secondary Surface;指派保留远程用户查询和 ID 提交。 - 弹窗:HeroUI Modal/AlertDialog;详情查看态单一 secondary Surface;指派保留远程用户查询和 ID 提交。
...@@ -69,7 +69,9 @@ ...@@ -69,7 +69,9 @@
操作列宽度回归复测:与运行中的“问题整改”在 1440x900 同视口比较,基准操作列为 144px。当前主表恢复同一列宽分配规则:未横向溢出时由首个业务列 `1fr` 吸收剩余宽度,操作列保持 144px;实测操作列 `x=1244..1388`,内容区 `x=1260..1372`,左右内边距均为 16px,三个 32px 按钮间距均为 8px。1440x900、1280x720、768x1024、390x844 浅/深色均复测,操作列在每个视口都为 144px;窄视口横向溢出态固定在 DataGrid 右端,四个视口 `document.body.scrollWidth === innerWidth`。 操作列宽度回归复测:与运行中的“问题整改”在 1440x900 同视口比较,基准操作列为 144px。当前主表恢复同一列宽分配规则:未横向溢出时由首个业务列 `1fr` 吸收剩余宽度,操作列保持 144px;实测操作列 `x=1244..1388`,内容区 `x=1260..1372`,左右内边距均为 16px,三个 32px 按钮间距均为 8px。1440x900、1280x720、768x1024、390x844 浅/深色均复测,操作列在每个视口都为 144px;窄视口横向溢出态固定在 DataGrid 右端,四个视口 `document.body.scrollWidth === innerWidth`。
应用切换专项复测:对照“问题整改”的 46rem/42rem Modal、Header、24px 关闭按钮和 36px identity 几何,使用桌面两列、移动端单列。1440x900 下两列应用项实测为 338x72px,HeroUI Card 使用默认 24px 圆角和 16px 内边距,网格间距 12px;移除无业务 Logo 时统一显示鑫元 Logo 的错误兜底,恢复 API Logo / 应用首字规则;只显示一个“当前”。390x844 下应用项为 310x72px,正文独立滚动,`document.body.scrollWidth === innerWidth === 390`;浅深色均已验证。 应用切换专项基准更新:应用切换改以 model_analysis 当前实现为唯一基准。结构使用 HeroUI Modal compound API,分组直接读取既有 `GET /info -> site.group`,应用仍由 `GET /application/info` 与登录用户可访问应用合并;保持后端分组及组内顺序、过滤空组和无权限项、显示真实数量,未引用项进入“其他应用”,无分组配置时回退“全部应用”。固定 46rem/42rem、28px inset、24px Modal 圆角、24px/12px 分组节奏、sm 起两列、移动一列、36px identity well,并确保只有一个“当前” Chip。
应用切换浏览器验收:同时打开目标 `http://localhost:8002/integrity_practice_management/` 与基准 `http://localhost:8003/model_analysis/`,在 1440x900、1280x720、768x1024、390x844 的浅色和深色模式逐项比较。1440x900 实测两侧均为 736x672px、24px 圆角、28px inset、Header 56px、Header/Body 间距 12px、关闭按钮 24x24px 且距右上各 28px、两列各 334px且间距 12px;1280x720 为 736x640px,768x1024 仍为两列,390x844 为 358x672px、左右各 16px 且单列 302px。各视口 Body 均独立滚动,页面横向滚动宽度等于视口宽度。Esc、关闭按钮和遮罩均可关闭,焦点回到“切换应用”入口;打开后 Tab 顺序从 Dialog 到关闭按钮。无 `site.group` 时验证只回退一个“全部应用”分组;`GET /application/info` 失败时验证 warning 与登录用户应用回退;透明 Logo 保持原图,损坏或缺失 Logo 回退首字,当前应用使用鑫元 Logo,四类 identity well 均为 36x36px;全部路径都保持唯一当前态。全部稳定帧截图存放在 `/tmp/integrity-switcher-qa.*`,未写入源码或 Git。
操作图标三层几何专项复测:清除 HeroUI `.button svg` 默认的 `4px -2px` margin,并把表格操作按钮在桌面和移动端都锁定为 32x32 正圆。`button`、Ant `.anticon` 和内部 `svg` 逐层实测为 32x32 / 16x16 / 16x16,内层 SVG margin 为 0,水平及垂直圆心偏差均为 0px。 操作图标三层几何专项复测:清除 HeroUI `.button svg` 默认的 `4px -2px` margin,并把表格操作按钮在桌面和移动端都锁定为 32x32 正圆。`button`、Ant `.anticon` 和内部 `svg` 逐层实测为 32x32 / 16x16 / 16x16,内层 SVG margin 为 0,水平及垂直圆心偏差均为 0px。
...@@ -81,7 +83,7 @@ ...@@ -81,7 +83,7 @@
退出登录使用独立 `logout-proof` 会话复测:点击 Sidebar 用户菜单的“退出登录”后,`POST /api/auth/logout` 返回 200,页面跳转到 `/integrity_practice_management/login`,`current_user` 与 `auth_token` 同时从 localStorage 删除,仅保留非鉴权主题键;控制台 0 error。此前旧验收会话中登录页仍带 `current_user` 是会话夹具残留,不是产品退出流程的持久化行为。 退出登录使用独立 `logout-proof` 会话复测:点击 Sidebar 用户菜单的“退出登录”后,`POST /api/auth/logout` 返回 200,页面跳转到 `/integrity_practice_management/login`,`current_user` 与 `auth_token` 同时从 localStorage 删除,仅保留非鉴权主题键;控制台 0 error。此前旧验收会话中登录页仍带 `current_user` 是会话夹具残留,不是产品退出流程的持久化行为。
新页面重载后控制台:0 error;保留 1 条开发环境 warning。曾发现并修复 `ComboBox.InputGroup fullWidth` DOM 透传错误,以及 checkbox 行点击误开详情的交互冲突。 新页面重载后控制台:0 error;保留 1 种既有 `PressResponder` 开发环境 warning,在 React StrictMode 下打印两次,应用切换打开/关闭没有新增日志。曾发现并修复 `ComboBox.InputGroup fullWidth` DOM 透传错误,以及 checkbox 行点击误开详情的交互冲突。
## 未挂载历史边界 ## 未挂载历史边界
......
export const APPLICATION_DISPLAY_NAME = '廉洁从业管理'; export const APPLICATION_DISPLAY_NAME = '廉洁从业管理';
export const APPLICATION_ROUTE_NAME = 'integrity_practice_management';
export const APPLICATION_BASE_PATH = '/integrity_practice_management/'; export const APPLICATION_BASE_PATH = '/integrity_practice_management/';
export const THEME_STORAGE_KEY = 'xinyuan_color_theme'; export const THEME_STORAGE_KEY = 'xinyuan_color_theme';
......
...@@ -539,6 +539,21 @@ ...@@ -539,6 +539,21 @@
margin-top: 1.5rem; margin-top: 1.5rem;
} }
.rc-app-switcher-dialog {
width: min(46rem, calc(100vw - 2rem));
max-width: min(46rem, calc(100vw - 2rem));
max-height: min(42rem, calc(100dvh - 2rem));
}
.rc-app-identity-well,
.rc-app-identity-avatar {
border-radius: var(--rc-radius-control);
}
.rc-app-identity-avatar {
background: transparent;
}
.rc-detail-surface.surface--secondary { .rc-detail-surface.surface--secondary {
border-radius: var(--rc-radius-detail-surface); border-radius: var(--rc-radius-detail-surface);
} }
......
...@@ -3,6 +3,7 @@ import { Archive, CheckSquare, FileDone, FileText, Settings } from '@/components ...@@ -3,6 +3,7 @@ import { Archive, CheckSquare, FileDone, FileText, Settings } from '@/components
import ApplicationSwitcherDialog from '@/components/ApplicationSwitcherDialog'; import ApplicationSwitcherDialog from '@/components/ApplicationSwitcherDialog';
import { APPLICATION_DISPLAY_NAME, ROUTE_TITLES } from '@/config/application'; import { APPLICATION_DISPLAY_NAME, ROUTE_TITLES } from '@/config/application';
import { UserContext } from '@/wrapper/Auth'; import { UserContext } from '@/wrapper/Auth';
import { SiteContext } from '@/wrapper/Site';
import { useContext, useEffect, useMemo, useState } from 'react'; import { useContext, useEffect, useMemo, useState } from 'react';
import { Outlet, useLocation } from 'umi'; import { Outlet, useLocation } from 'umi';
import { INTEGRITY_PRACTICE_MENU } from './integrityPracticeConfig'; import { INTEGRITY_PRACTICE_MENU } from './integrityPracticeConfig';
...@@ -21,6 +22,7 @@ const MENU_GROUPS = INTEGRITY_PRACTICE_MENU.map((group, index) => ({ ...@@ -21,6 +22,7 @@ const MENU_GROUPS = INTEGRITY_PRACTICE_MENU.map((group, index) => ({
export default function IntegrityPracticeLayout() { export default function IntegrityPracticeLayout() {
const location = useLocation(); const location = useLocation();
const userInfo = useContext(UserContext); const userInfo = useContext(UserContext);
const siteInfo = useContext(SiteContext);
const [sidebarOpen, setSidebarOpen] = useState(true); const [sidebarOpen, setSidebarOpen] = useState(true);
const [switcherOpen, setSwitcherOpen] = useState(false); const [switcherOpen, setSwitcherOpen] = useState(false);
const pageTitle = useMemo( const pageTitle = useMemo(
...@@ -46,7 +48,12 @@ export default function IntegrityPracticeLayout() { ...@@ -46,7 +48,12 @@ export default function IntegrityPracticeLayout() {
> >
<Outlet /> <Outlet />
</AppShell> </AppShell>
<ApplicationSwitcherDialog open={switcherOpen} onClose={() => setSwitcherOpen(false)} userInfo={userInfo} /> <ApplicationSwitcherDialog
open={switcherOpen}
siteInfo={siteInfo}
userInfo={userInfo}
onClose={() => setSwitcherOpen(false)}
/>
</> </>
); );
} }
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
- 默认入口:`/self_check_the_results_of_the_working_draft` - 默认入口:`/self_check_the_results_of_the_working_draft`
- 页面菜单与 Subject 动作配置:`integrityPracticeConfig.js` - 页面菜单与 Subject 动作配置:`integrityPracticeConfig.js`
- 页面统一使用鑫元 HeroUI Pro Shell、`RecordWorkbench` 和 `RecordDetailDrawer`;Sidebar 为 offcanvas,移动端通过可访问性 Dialog 打开 - 页面统一使用鑫元 HeroUI Pro Shell、`RecordWorkbench` 和 `RecordDetailDrawer`;Sidebar 为 offcanvas,移动端通过可访问性 Dialog 打开
- 应用切换继续读取现有 `GET /application/info` 和登录用户可访问应用,并按 `GET /info` 返回的 `site.group` 动态分组;未分组的可访问应用进入“其他应用”,无分组配置时回退“全部应用”,不改变鉴权、会话或跳转协议
- Subject 字段、Select 选项、列表视图和详情视图均从后端动态读取,页面不维护字段副本或硬编码选项 - Subject 字段、Select 选项、列表视图和详情视图均从后端动态读取,页面不维护字段副本或硬编码选项
| 菜单分组 | 页面 | Subject | | 菜单分组 | 页面 | Subject |
......
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