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:
@@ -1,10 +1,11 @@
|
||||
import React, { lazy, Suspense, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { lazy, Suspense, useEffect, useMemo, useState } from 'react';
|
||||
import { XCard, registerCatalog, type XAgentCommand_v0_9 } from '@ant-design/x-card';
|
||||
import { Button, Spin, Tag, Tooltip, Typography } from 'antd';
|
||||
import { DownloadOutlined } from '@ant-design/icons';
|
||||
import type { EChartsType } from 'echarts/core';
|
||||
import type { EChartsOption } from '../../components/ECharts';
|
||||
import type { AiChartSchema } from './types';
|
||||
import { useXCardSurface } from './useSubmissionState';
|
||||
|
||||
// echarts 体积较大,仅在真正渲染图表时加载,避免打开 AI 抽屉就拉取
|
||||
const ReactECharts = lazy(() => import('../../components/ECharts'));
|
||||
@@ -202,6 +203,27 @@ const ChartPreview: React.FC<ChartPreviewProps> = ({ chart }) => {
|
||||
const option = useMemo<EChartsOption>(() => (chart ? buildOption(chart) : {}), [chart]);
|
||||
const [instance, setInstance] = useState<EChartsType | null>(null);
|
||||
if (!chart) return null;
|
||||
// 空数据集:渲染明确占位,而不是一张空白图
|
||||
if (!chart.rows || chart.rows.length === 0) {
|
||||
return (
|
||||
<div className="ai-chat-chart-card">
|
||||
<div className="ai-chat-chart-card__header">
|
||||
<Typography.Text strong>{chart.title}</Typography.Text>
|
||||
<Tag color="blue">{CHART_TYPE_LABELS[chart.chartType] ?? chart.chartType}</Tag>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
height: 120,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography.Text type="secondary">暂无数据</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const downloadImage = () => {
|
||||
if (!instance) return;
|
||||
@@ -254,46 +276,39 @@ export interface DynamicChartProps {
|
||||
* so history replays identically.
|
||||
*/
|
||||
export const DynamicChart: React.FC<DynamicChartProps> = ({ chart }) => {
|
||||
const commandsRef = useRef<XAgentCommand_v0_9[]>([]);
|
||||
const [commands, setCommands] = useState<XAgentCommand_v0_9[]>([]);
|
||||
const idRef = useRef<string>('');
|
||||
const sid = surfaceId(chart.id);
|
||||
const { commands, pushCommands } = useXCardSurface(sid);
|
||||
|
||||
useEffect(() => {
|
||||
const sid = surfaceId(chart.id);
|
||||
if (idRef.current !== sid) {
|
||||
commandsRef.current = [];
|
||||
idRef.current = sid;
|
||||
}
|
||||
const cmds = commandsRef.current;
|
||||
if (cmds.length === 0) {
|
||||
cmds.push({
|
||||
const cmds: XAgentCommand_v0_9[] = [
|
||||
{
|
||||
version: 'v0.9',
|
||||
createSurface: { surfaceId: sid, catalogId: CHART_CATALOG_ID },
|
||||
});
|
||||
}
|
||||
cmds.push({
|
||||
version: 'v0.9',
|
||||
updateDataModel: {
|
||||
surfaceId: sid,
|
||||
path: '/chart',
|
||||
value: chart,
|
||||
},
|
||||
});
|
||||
cmds.push({
|
||||
version: 'v0.9',
|
||||
updateComponents: {
|
||||
surfaceId: sid,
|
||||
components: [
|
||||
{
|
||||
id: 'root',
|
||||
component: 'ChartPreview',
|
||||
chart: { path: '/chart' },
|
||||
},
|
||||
],
|
||||
{
|
||||
version: 'v0.9',
|
||||
updateDataModel: {
|
||||
surfaceId: sid,
|
||||
path: '/chart',
|
||||
value: chart,
|
||||
},
|
||||
},
|
||||
});
|
||||
setCommands([...cmds]);
|
||||
}, [chart]);
|
||||
{
|
||||
version: 'v0.9',
|
||||
updateComponents: {
|
||||
surfaceId: sid,
|
||||
components: [
|
||||
{
|
||||
id: 'root',
|
||||
component: 'ChartPreview',
|
||||
chart: { path: '/chart' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
pushCommands(cmds);
|
||||
}, [chart, pushCommands, sid]);
|
||||
|
||||
return (
|
||||
<div className="ai-chat-chart">
|
||||
|
||||
Reference in New Issue
Block a user