feat(admin): 用户体验体系化提升与高危缺陷修复
UX 缺陷修复: - 校验失败不再卡死弹窗按钮(Users/Roles/Bills) - 押金收取/批量收取/添加分期防重复提交;切换房型重置勾选 - AI 表单/批量确认不再出现"假成功" - Dashboard 各数据模块独立加载,单接口失败不再整页清零 - 房间可视化加载失败显示错误态而非永久转圈 - 学生编辑表单回填前重置,避免字段残留污染 - 覆盖式导入增加二次确认;恢复默认考勤时段确认并同步表单 - 金数据匹配关闭前确认,同步中禁止误关 体验提升: - 新增统一 QueryErrorState/QueryEmpty,20+ 页面加载失败显示错误态与重试 - 全局 ErrorBoundary + RouteKeeper 逐页兜底 - 新增 usePageVisible/useVisibleRefetch,保活页面切回自动刷新数据 - 新增首次登录角色引导 RoleTour 与业务闭环 NextStepHint 引导卡 - 重构 A2UI:useSubmissionState/useXCardSurface 收敛状态与命令生命周期, ArtifactErrorBoundary 渲染降级,图表空数据占位 - AI 助手欢迎语与建议话术按角色定制,会话列表空态引导 - 更新 a2ui-contract.md 契约文档说明实现现状
This commit is contained in:
@@ -9,6 +9,7 @@ import { PlusOutlined } from '@ant-design/icons';
|
||||
import api from '../api';
|
||||
import PermissionButton from '../components/PermissionButton';
|
||||
import EditableCell from '../components/EditableCell';
|
||||
import { QueryErrorState } from '../components/QueryState';
|
||||
import { message } from '../ui/app-message';
|
||||
|
||||
interface ClassroomOption {
|
||||
@@ -44,25 +45,22 @@ const AttendanceDevicesPage: React.FC = () => {
|
||||
data: fetchResult = { devices: [], classrooms: [] },
|
||||
isLoading,
|
||||
isFetching,
|
||||
isError,
|
||||
refetch,
|
||||
} = useQuery<{ devices: AttendanceDeviceRow[]; classrooms: ClassroomOption[] }>({
|
||||
queryKey: ['attendance-devices'],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const [devices, classroomList] = await Promise.all([
|
||||
api.get<AttendanceDeviceRow[]>('/attendance-devices'),
|
||||
api.get<ClassroomOption[]>('/classrooms'),
|
||||
]);
|
||||
return {
|
||||
devices: validateResponse<AttendanceDeviceRow[]>(attendanceDevicesSchema, devices),
|
||||
classrooms: validateResponse<ClassroomOption[]>(
|
||||
classroomOptionsSchema,
|
||||
classroomList,
|
||||
).filter((item: any) => item.status !== 'archived'),
|
||||
};
|
||||
} catch (error: any) {
|
||||
message.error(error?.message || '加载考勤机绑定失败');
|
||||
return { devices: [], classrooms: [] };
|
||||
}
|
||||
const [devices, classroomList] = await Promise.all([
|
||||
api.get<AttendanceDeviceRow[]>('/attendance-devices'),
|
||||
api.get<ClassroomOption[]>('/classrooms'),
|
||||
]);
|
||||
return {
|
||||
devices: validateResponse<AttendanceDeviceRow[]>(attendanceDevicesSchema, devices),
|
||||
classrooms: validateResponse<ClassroomOption[]>(
|
||||
classroomOptionsSchema,
|
||||
classroomList,
|
||||
).filter((item: any) => item.status !== 'archived'),
|
||||
};
|
||||
},
|
||||
});
|
||||
const data = fetchResult.devices;
|
||||
@@ -307,14 +305,22 @@ const AttendanceDevicesPage: React.FC = () => {
|
||||
添加考勤机
|
||||
</PermissionButton>
|
||||
</div>
|
||||
<Table<AttendanceDeviceRow>
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={filteredData}
|
||||
loading={loading}
|
||||
locale={{ emptyText: <Empty description="暂无考勤机绑定" /> }}
|
||||
pagination={{ defaultPageSize: 20, showSizeChanger: true }}
|
||||
/>
|
||||
{isError ? (
|
||||
<QueryErrorState
|
||||
title="考勤机数据加载失败"
|
||||
description="请检查网络后重试。"
|
||||
onRetry={() => void refetch()}
|
||||
/>
|
||||
) : (
|
||||
<Table<AttendanceDeviceRow>
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={filteredData}
|
||||
loading={loading}
|
||||
locale={{ emptyText: <Empty description="暂无考勤机绑定" /> }}
|
||||
pagination={{ defaultPageSize: 20, showSizeChanger: true }}
|
||||
/>
|
||||
)}
|
||||
<Modal
|
||||
title={editing ? '编辑考勤机绑定' : '添加考勤机绑定'}
|
||||
open={modalOpen}
|
||||
|
||||
Reference in New Issue
Block a user