fix(web): Phase 3 前端 UX/i18n 修复 — 名称解析/确认对话框/日历切换/删除替换
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

- ConsultationList: 批量解析患者/医生名称替代截断 UUID
- PointsOrderList: 使用 product_name + 批量解析患者/核销人名称
- AppointmentList: 破坏性状态变更添加 Modal.confirm + 取消原因收集
- CalendarView: 添加 onPanelChange 回调支持月份切换
- DoctorSchedule: 日历视图切换月份自动刷新数据
- PointsRuleList: 移除无效删除按钮,Switch 添加启用/停用文字
- PointsProductList: 删除按钮替换为上架/下架 Switch
- PatientSelect: 性别显示中文化 (male→男, female→女)
- VitalSignsChart: API 失败时显示 Alert 错误提示
- PointsOrder 类型: 添加 product_name 字段
This commit is contained in:
iven
2026-04-25 19:49:25 +08:00
parent e8a794ff69
commit 5b520a168c
10 changed files with 184 additions and 66 deletions

View File

@@ -22,6 +22,7 @@ import {
pointsApi,
type PointsOrder,
} from '../../api/health/points';
import { patientApi } from '../../api/health/patients';
/** 订单状态映射 */
const STATUS_MAP: Record<string, { text: string; color: string }> = {
@@ -54,6 +55,9 @@ export default function PointsOrderList() {
const [verifyForm] = Form.useForm();
const [verifying, setVerifying] = useState(false);
// 名称缓存
const [nameCache, setNameCache] = useState<Record<string, string>>({});
// ---- 数据获取 ----
const fetchData = useCallback(async (p = page, ps = pageSize) => {
setLoading(true);
@@ -65,12 +69,30 @@ export default function PointsOrderList() {
});
setData(result.data);
setTotal(result.total);
// 批量解析患者名称
const patientIds = [...new Set(result.data.map((o) => o.patient_id))];
const missingIds = patientIds.filter((id) => !nameCache[id]);
if (missingIds.length > 0) {
const newNames: Record<string, string> = {};
await Promise.all(
missingIds.map(async (id) => {
try {
const detail = await patientApi.get(id);
newNames[id] = detail.name;
} catch {
newNames[id] = id.slice(0, 8);
}
}),
);
setNameCache((prev) => ({ ...prev, ...newNames }));
}
} catch {
message.error('加载订单列表失败');
} finally {
setLoading(false);
}
}, [page, pageSize, statusFilter]);
}, [page, pageSize, statusFilter, nameCache]);
useEffect(() => {
fetchData();
@@ -109,22 +131,19 @@ export default function PointsOrderList() {
),
},
{
title: '患者ID',
title: '患者',
dataIndex: 'patient_id',
key: 'patient_id',
width: 140,
render: (val: string) => (
<span style={{ fontFamily: 'monospace', fontSize: 12 }}>{truncateId(val)}</span>
),
width: 100,
render: (id: string) => nameCache[id] || id.slice(0, 8),
},
{
title: '商品ID',
dataIndex: 'product_id',
key: 'product_id',
title: '商品',
dataIndex: 'product_name',
key: 'product_name',
width: 140,
render: (val: string) => (
<span style={{ fontFamily: 'monospace', fontSize: 12 }}>{truncateId(val)}</span>
),
render: (name: string | null, record: PointsOrder) =>
name || truncateId(record.product_id),
},
{
title: '积分',
@@ -161,8 +180,8 @@ export default function PointsOrderList() {
title: '核销人',
dataIndex: 'verified_by',
key: 'verified_by',
width: 140,
render: (val: string | null) => val ? <Tag color="blue">{truncateId(val)}</Tag> : '-',
width: 100,
render: (val: string | null) => val ? <Tag color="blue">{nameCache[val] || val.slice(0, 8)}</Tag> : '-',
},
{
title: '过期时间',