diff --git a/src/constants/labels.ts b/src/constants/labels.ts new file mode 100644 index 0000000..9a53a08 --- /dev/null +++ b/src/constants/labels.ts @@ -0,0 +1,46 @@ +// ── Admin learning data display labels ── + +export const eventTypeLabels: Record = { + material_opened: '打开资料', + material_closed: '关闭资料', + position_changed: '位置变化', + heartbeat: '心跳', + marked_as_read: '标记已读', +}; + +export const eventStatusLabels: Record = { + processed: '已处理', + failed: '失败', + duplicate: '重复', + pending: '待处理', + warning: '警告', +}; + +export const sessionStatusLabels: Record = { + active: '活跃', + interrupted: '中断', + completed: '已完成', +}; + +export const progressStatusLabels: Record = { + reading: '阅读中', + completed: '已完成', + not_started: '未开始', +}; + +export const targetTypeLabels: Record = { + knowledge_source: '知识资料', + temporary_file: '临时文件', +}; + +export const recordTypeLabels: Record = { + reading: '阅读', + session: '会话', +}; + +export const parseStatusLabels: Record = { + pending: '待解析', + processing: '解析中', + completed: '已解析', + failed: '解析失败', +}; diff --git a/src/pages/LearningData.tsx b/src/pages/LearningData.tsx index a523440..634282c 100644 --- a/src/pages/LearningData.tsx +++ b/src/pages/LearningData.tsx @@ -3,6 +3,7 @@ import { Table, Tabs, Tag, Space, Input, Typography } from 'antd' import { SearchOutlined } from '@ant-design/icons' import { useQuery } from '@tanstack/react-query' import { api } from '@/services/http-client' +import { sessionStatusLabels } from '@/constants/labels' const { Title } = Typography @@ -47,7 +48,7 @@ export default function LearningData() { { title: '用户', dataIndex: 'userId', width: 100, ellipsis: true }, { title: '知识库', dataIndex: 'knowledgeBaseId', width: 100, ellipsis: true }, { title: '模式', dataIndex: 'mode', width: 80 }, - { title: '状态', dataIndex: 'status', width: 80, render: (s: string) => {s} }, + { title: '状态', dataIndex: 'status', width: 80, render: (s: string) => {sessionStatusLabels[s] || s} }, { title: '时长(分)', dataIndex: 'durationSeconds', width: 80, render: (s: number) => s ? Math.round(s / 60) : '-' }, { title: '开始时间', dataIndex: 'startedAt', width: 120, render: (d: string) => d ? new Date(d).toLocaleString() : '-' }, { title: '结束时间', dataIndex: 'endedAt', width: 120, render: (d: string) => d ? new Date(d).toLocaleString() : '-' }, diff --git a/src/pages/learning/Dashboard.tsx b/src/pages/learning/Dashboard.tsx index 9ee5de8..228407b 100644 --- a/src/pages/learning/Dashboard.tsx +++ b/src/pages/learning/Dashboard.tsx @@ -4,6 +4,7 @@ import { BookOutlined, ThunderboltOutlined, UserOutlined, WarningOutlined, Check import { useQuery } from '@tanstack/react-query'; import dayjs from 'dayjs'; import { learningAdminAPI } from '../../services/learningAdmin'; +import { eventTypeLabels } from '../../constants/labels'; const { RangePicker } = DatePicker; @@ -36,7 +37,7 @@ export default function DashboardPage() { { title: '用户', dataIndex: 'userId', key: 'userId', ellipsis: true, width: 120 }, { title: '资料', dataIndex: 'materialId', key: 'materialId', ellipsis: true, width: 120 }, { title: '类型', dataIndex: 'eventType', key: 'eventType', width: 110, - render: (t: string) => {t} }, + render: (t: string) => {eventTypeLabels[t] || t} }, { title: '错误码', dataIndex: 'errorCode', key: 'errorCode', width: 140, render: (c?: string) => c ? {c} : '-' }, { title: '时间', dataIndex: 'occurredAt', key: 'occurredAt', width: 160, diff --git a/src/pages/learning/DataPages.tsx b/src/pages/learning/DataPages.tsx index f41310e..ef99420 100644 --- a/src/pages/learning/DataPages.tsx +++ b/src/pages/learning/DataPages.tsx @@ -4,6 +4,7 @@ import { Table, Input, Select, Space, Tag, Button, Tabs, Drawer, Descriptions, D const { Text } = Typography; import { useQuery } from '@tanstack/react-query'; import { learningAdminAPI } from '../../services/learningAdmin'; +import { eventTypeLabels, eventStatusLabels, sessionStatusLabels, progressStatusLabels, targetTypeLabels, recordTypeLabels } from '../../constants/labels'; import dayjs from 'dayjs'; const { RangePicker } = DatePicker; @@ -39,10 +40,10 @@ export function ReadingEventPage() { { title: '事件ID', dataIndex: 'eventId', key: 'eventId', width: 120, ellipsis: true }, { title: '用户', dataIndex: 'userId', key: 'userId', width: 100, ellipsis: true }, { title: '资料', dataIndex: 'materialId', key: 'materialId', width: 100, ellipsis: true }, - { title: '类型', dataIndex: 'eventType', key: 'eventType', width: 120, render: (t: string) => {t} }, + { title: '类型', dataIndex: 'eventType', key: 'eventType', width: 120, render: (t: string) => {eventTypeLabels[t] || t} }, { title: 'Delta(s)', dataIndex: 'activeSecondsDelta', key: 'delta', width: 70 }, { title: '状态', dataIndex: 'status', key: 'status', width: 90, - render: (s: string) => {s} }, + render: (s: string) => {eventStatusLabels[s] || s} }, { title: '时间', dataIndex: 'createdAt', key: 'createdAt', width: 160, render: (t: string) => t ? dayjs(t).format('MM-DD HH:mm:ss') : '-' }, ]; @@ -54,7 +55,7 @@ export function ReadingEventPage() { setFilters(f => ({ ...f, materialId: e.target.value }))} style={{ width: 140 }} /> setFilters(f => ({ ...f, clientSessionId: e.target.value }))} style={{ width: 170 }} /> setFilters(f => ({ ...f, status: v || '' }))} options={[{ value: 'processed', label: '已处理' }, { value: 'failed', label: '失败' }, { value: 'duplicate', label: '重复' }, { value: 'pending', label: '待处理' }]} /> v?.slice(0, 12) || '-' }, { title: '用户', dataIndex: 'userId', key: 'userId', width: 90, ellipsis: true }, { title: '资料', dataIndex: 'materialId', key: 'materialId', width: 90, ellipsis: true }, - { title: '类型', dataIndex: 'targetType', key: 'targetType', width: 80, render: (v?: string) => v ? {v} : '-' }, + { title: '类型', dataIndex: 'targetType', key: 'targetType', width: 80, render: (v?: string) => v ? {targetTypeLabels[v] || v} : '-' }, { title: '模式', dataIndex: 'mode', key: 'mode', width: 70, render: (v?: string) => v || '-' }, { title: 'KB', dataIndex: 'knowledgeBaseId', key: 'kbId', width: 80, ellipsis: true, render: (v?: string) => v?.slice(0, 8) || '-' }, { title: '状态', dataIndex: 'status', key: 'status', width: 90, - render: (s: string) => {s} }, + render: (s: string) => {sessionStatusLabels[s] || s} }, { title: '时长(s)', dataIndex: 'totalActiveSeconds', key: 'seconds', width: 70 }, { title: '专注(分)', dataIndex: 'focusMinutes', key: 'focus', width: 70, render: (v?: number) => v ?? '-' }, { title: '开始', dataIndex: 'startedAt', key: 'startedAt', width: 150, render: (t: string) => t ? dayjs(t).format('MM-DD HH:mm') : '-' }, @@ -193,10 +194,10 @@ export function ProgressPage() { const columns = [ { title: '用户', dataIndex: 'userId', key: 'userId', width: 90, ellipsis: true }, { title: '资料', dataIndex: 'materialId', key: 'materialId', width: 110, ellipsis: true }, - { title: '目标类型', dataIndex: 'readingTargetType', key: 'targetType', width: 85, render: (v?: string) => v ? {v} : '-' }, + { title: '目标类型', dataIndex: 'readingTargetType', key: 'targetType', width: 85, render: (v?: string) => v ? {targetTypeLabels[v] || v} : '-' }, { title: '知识库', dataIndex: 'knowledgeBaseId', key: 'kbId', width: 75, ellipsis: true, render: (v?: string) => v?.slice(0,8) || '-' }, { title: 'Session', dataIndex: 'lastClientSessionId', key: 'sessionId', width: 120, ellipsis: true, render: (v?: string) => v?.slice(0,16) || '-' }, - { title: '状态', dataIndex: 'status', key: 'status', width: 85, render: (s: string) => {s} }, + { title: '状态', dataIndex: 'status', key: 'status', width: 85, render: (s: string) => {progressStatusLabels[s] || s} }, { title: '进度', dataIndex: 'lastProgress', key: 'progress', width: 65, render: (v: number) => v != null ? `${Math.round(v * 100)}%` : '-' }, { title: '位置', dataIndex: 'lastPosition', key: 'position', width: 100, ellipsis: true, render: (v?: object) => v ? JSON.stringify(v).slice(0,20) : '-' }, { title: '活跃秒', dataIndex: 'totalActiveSeconds', key: 'seconds', width: 70 }, @@ -320,7 +321,7 @@ export function RecordPage() { { title: '用户', dataIndex: 'userId', key: 'userId', width: 90, ellipsis: true }, { title: '资料', dataIndex: 'materialId', key: 'materialId', width: 100, ellipsis: true, render: (v?: string) => v?.slice(0,12) || '-' }, { title: '标题', dataIndex: 'title', key: 'title', width: 180 }, - { title: '类型', dataIndex: 'recordType', key: 'recordType', width: 100, render: (t: string) => {t} }, + { title: '类型', dataIndex: 'recordType', key: 'recordType', width: 100, render: (t: string) => {recordTypeLabels[t] || t} }, { title: '时长', dataIndex: 'durationSeconds', key: 'duration', width: 80 }, { title: '时间', dataIndex: 'occurredAt', key: 'occurredAt', width: 160, render: (t: string) => t ? dayjs(t).format('MM-DD HH:mm') : '-' }, ]; @@ -377,7 +378,7 @@ export function AnomalyPage() { { title: '事件ID', dataIndex: 'eventId', key: 'eventId', width: 120, ellipsis: true }, { title: '用户', dataIndex: 'userId', key: 'userId', width: 100, ellipsis: true }, { title: '资料', dataIndex: 'materialId', key: 'materialId', width: 100, ellipsis: true }, - { title: '事件类型', dataIndex: 'eventType', key: 'eventType', width: 120, render: (t: string) => {t} }, + { title: '事件类型', dataIndex: 'eventType', key: 'eventType', width: 120, render: (t: string) => {eventTypeLabels[t] || t} }, { title: '错误码', dataIndex: 'errorCode', key: 'errorCode', width: 140, render: (c?: string) => c ? {c} : '-' }, { title: '时间', dataIndex: 'createdAt', key: 'createdAt', width: 160, @@ -444,7 +445,7 @@ export function UserTimelinePage() { children: (
{entry.occurredAt ? dayjs(entry.occurredAt).format('YYYY-MM-DD HH:mm:ss') : '-'} -
{entry.recordType}
+
{recordTypeLabels[entry.recordType] || entry.recordType}
{entry.title || '-'} {entry.durationSeconds != null &&
时长 {entry.durationSeconds}s
} {entry.materialId &&
资料 {entry.materialId}
}