吸收数字员工管理端运营台前端
This commit is contained in:
@@ -0,0 +1,386 @@
|
||||
import { XProTable } from '@/components/ProComponents';
|
||||
import WorkspaceLayout from '@/components/WorkspaceLayout';
|
||||
import { SUCCESS_CODE } from '@/constants/codes.constants';
|
||||
import { apiDigitalEmployeeAdminWorkbenchList } from '@/services/systemManage';
|
||||
import type {
|
||||
DigitalEmployeeAdminWorkbenchRow,
|
||||
DigitalEmployeeAdminWorkbenchView,
|
||||
} from '@/types/interfaces/systemManage';
|
||||
import type {
|
||||
ActionType,
|
||||
FormInstance,
|
||||
ProColumns,
|
||||
} from '@ant-design/pro-components';
|
||||
import { Button, Drawer, message, Space, Tabs, Tag, Typography } from 'antd';
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import { history, useModel } from 'umi';
|
||||
|
||||
const WORKBENCH_TABS: Array<{
|
||||
key: DigitalEmployeeAdminWorkbenchView;
|
||||
label: string;
|
||||
}> = [
|
||||
{ key: 'business', label: '业务视图' },
|
||||
{ key: 'interventions', label: '介入台' },
|
||||
{ key: 'artifact_lifecycle', label: '产物治理' },
|
||||
{ key: 'notifications', label: '通知运营' },
|
||||
{ key: 'audits', label: '审计诊断' },
|
||||
{ key: 'daily_reports', label: '日报运营' },
|
||||
{ key: 'route_governance', label: '路由治理' },
|
||||
];
|
||||
|
||||
const ENTITY_TYPE_OPTIONS = {
|
||||
plan: { text: 'Plan' },
|
||||
task: { text: 'Task' },
|
||||
run: { text: 'Run' },
|
||||
event: { text: 'Event' },
|
||||
artifact: { text: 'Artifact' },
|
||||
approval: { text: 'Approval' },
|
||||
};
|
||||
|
||||
const STATUS_TONE: Record<string, string> = {
|
||||
active: 'processing',
|
||||
allowed: 'success',
|
||||
approved: 'success',
|
||||
blocked: 'error',
|
||||
completed: 'success',
|
||||
deleted: 'default',
|
||||
failed: 'error',
|
||||
pending: 'warning',
|
||||
rejected: 'error',
|
||||
running: 'processing',
|
||||
synced: 'success',
|
||||
warn: 'warning',
|
||||
};
|
||||
|
||||
function compactText(value?: string, maxLength = 96): string {
|
||||
const text = (value || '').replace(/\s+/g, ' ').trim();
|
||||
if (!text) return '--';
|
||||
return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text;
|
||||
}
|
||||
|
||||
function jsonText(value?: Record<string, unknown>): string {
|
||||
if (!value) return '{}';
|
||||
return JSON.stringify(value, null, 2);
|
||||
}
|
||||
|
||||
function tone(value?: string): string {
|
||||
return value ? STATUS_TONE[value.toLowerCase()] || 'default' : 'default';
|
||||
}
|
||||
|
||||
function renderSummary(record: DigitalEmployeeAdminWorkbenchRow) {
|
||||
const title = compactText(record.title || record.entity_id, 72);
|
||||
const summary = compactText(record.summary || record.kind || record.category);
|
||||
const meta = [
|
||||
record.plan_id && `Plan ${record.plan_id}`,
|
||||
record.task_id && `Task ${record.task_id}`,
|
||||
record.run_id && `Run ${record.run_id}`,
|
||||
record.approval_id && `Approval ${record.approval_id}`,
|
||||
record.artifact_id && `Artifact ${record.artifact_id}`,
|
||||
record.route_decision_id && `Route ${record.route_decision_id}`,
|
||||
].filter((item): item is string => Boolean(item));
|
||||
|
||||
return (
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<Space size={6} wrap>
|
||||
<Typography.Text strong ellipsis style={{ maxWidth: 260 }}>
|
||||
{title}
|
||||
</Typography.Text>
|
||||
{record.status && <Tag color={tone(record.status)}>{record.status}</Tag>}
|
||||
</Space>
|
||||
<Typography.Text
|
||||
type="secondary"
|
||||
ellipsis
|
||||
style={{ display: 'block', maxWidth: 360, fontSize: 12 }}
|
||||
>
|
||||
{summary}
|
||||
</Typography.Text>
|
||||
{meta.length > 0 && (
|
||||
<Space size={[4, 4]} wrap style={{ marginTop: 4 }}>
|
||||
{meta.slice(0, 3).map((item) => (
|
||||
<Tag key={item} color="default" style={{ marginInlineEnd: 0 }}>
|
||||
{item}
|
||||
</Tag>
|
||||
))}
|
||||
</Space>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const DigitalEmployeeOps: React.FC = () => {
|
||||
const { hasPermission } = useModel('menuModel');
|
||||
const actionRef = useRef<ActionType>();
|
||||
const formRef = useRef<FormInstance>();
|
||||
const [activeView, setActiveView] =
|
||||
useState<DigitalEmployeeAdminWorkbenchView>('business');
|
||||
const [detailRecord, setDetailRecord] =
|
||||
useState<DigitalEmployeeAdminWorkbenchRow | null>(null);
|
||||
const businessViewJson = useMemo(
|
||||
() => jsonText(detailRecord?.business_view),
|
||||
[detailRecord],
|
||||
);
|
||||
const payloadJson = useMemo(
|
||||
() => jsonText(detailRecord?.payload),
|
||||
[detailRecord],
|
||||
);
|
||||
|
||||
const columns: ProColumns<DigitalEmployeeAdminWorkbenchRow>[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: '客户端 Key',
|
||||
dataIndex: 'client_key',
|
||||
hideInTable: true,
|
||||
fieldProps: { placeholder: '输入完整客户端 Key', allowClear: true },
|
||||
},
|
||||
{
|
||||
title: '设备 ID',
|
||||
dataIndex: 'device_id',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
fieldProps: { placeholder: '输入设备 ID', allowClear: true },
|
||||
renderText: (value) => value || '--',
|
||||
},
|
||||
{
|
||||
title: '实体类型',
|
||||
dataIndex: 'entity_type',
|
||||
width: 120,
|
||||
valueType: 'select',
|
||||
valueEnum: ENTITY_TYPE_OPTIONS,
|
||||
fieldProps: { placeholder: '选择实体类型', allowClear: true },
|
||||
render: (_, record) => <Tag>{record.entity_type || '--'}</Tag>,
|
||||
},
|
||||
{
|
||||
title: '实体 ID',
|
||||
dataIndex: 'entity_id',
|
||||
width: 180,
|
||||
ellipsis: true,
|
||||
fieldProps: { placeholder: '输入实体 ID', allowClear: true },
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 110,
|
||||
fieldProps: { placeholder: '输入状态', allowClear: true },
|
||||
render: (_, record) => (
|
||||
<Tag color={tone(record.status)}>{record.status || '--'}</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '类型/分类',
|
||||
dataIndex: 'kind',
|
||||
width: 190,
|
||||
ellipsis: true,
|
||||
fieldProps: { placeholder: '输入 kind', allowClear: true },
|
||||
render: (_, record) => (
|
||||
<Space size={4} wrap>
|
||||
{record.kind && <Tag color="blue">{record.kind}</Tag>}
|
||||
{record.category && <Tag>{record.category}</Tag>}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '分类',
|
||||
dataIndex: 'category',
|
||||
hideInTable: true,
|
||||
fieldProps: { placeholder: '输入 category', allowClear: true },
|
||||
},
|
||||
{
|
||||
title: '等级',
|
||||
dataIndex: 'level',
|
||||
width: 90,
|
||||
fieldProps: { placeholder: '等级', allowClear: true },
|
||||
renderText: (value) => value || '--',
|
||||
},
|
||||
{
|
||||
title: '优先级',
|
||||
dataIndex: 'priority',
|
||||
width: 90,
|
||||
fieldProps: { placeholder: '优先级', allowClear: true },
|
||||
renderText: (value) => value || '--',
|
||||
},
|
||||
{
|
||||
title: '业务摘要',
|
||||
dataIndex: 'summary',
|
||||
width: 400,
|
||||
hideInSearch: true,
|
||||
render: (_, record) => renderSummary(record),
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'updated_range',
|
||||
valueType: 'dateTimeRange',
|
||||
hideInTable: true,
|
||||
},
|
||||
{
|
||||
title: '发生时间',
|
||||
dataIndex: 'occurred_at',
|
||||
width: 170,
|
||||
hideInSearch: true,
|
||||
renderText: (value) => value || '--',
|
||||
},
|
||||
{
|
||||
title: '同步时间',
|
||||
dataIndex: 'synced_at',
|
||||
width: 170,
|
||||
hideInSearch: true,
|
||||
valueType: 'dateTime',
|
||||
},
|
||||
{
|
||||
title: '详情',
|
||||
valueType: 'option',
|
||||
fixed: 'right',
|
||||
width: 120,
|
||||
render: (_, record) => (
|
||||
<Space size={4}>
|
||||
<Button type="link" onClick={() => setDetailRecord(record)}>
|
||||
查看
|
||||
</Button>
|
||||
{record.sync_record_url && (
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => history.push(record.sync_record_url || '')}
|
||||
>
|
||||
同步记录
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const request = async (params: Record<string, any>) => {
|
||||
const updatedRange = params.updated_range || [];
|
||||
const response = await apiDigitalEmployeeAdminWorkbenchList({
|
||||
view: activeView,
|
||||
pageNo: params.current || 1,
|
||||
pageSize: params.pageSize || 15,
|
||||
clientKey: params.client_key,
|
||||
deviceId: params.device_id,
|
||||
entityType: params.entity_type,
|
||||
entityId: params.entity_id,
|
||||
status: params.status,
|
||||
kind: params.kind,
|
||||
category: params.category,
|
||||
level: params.level,
|
||||
priority: params.priority,
|
||||
updatedFrom: updatedRange[0],
|
||||
updatedTo: updatedRange[1],
|
||||
});
|
||||
|
||||
if (response.code !== SUCCESS_CODE) {
|
||||
message.error(response.message || '查询数字员工运营台失败');
|
||||
}
|
||||
|
||||
return {
|
||||
data: response.data?.records || [],
|
||||
total: response.data?.total || 0,
|
||||
success: response.code === SUCCESS_CODE,
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<WorkspaceLayout title="数字员工运营台" hideScroll>
|
||||
<Tabs
|
||||
activeKey={activeView}
|
||||
items={WORKBENCH_TABS}
|
||||
onChange={(key) => {
|
||||
setActiveView(key as DigitalEmployeeAdminWorkbenchView);
|
||||
formRef.current?.resetFields();
|
||||
actionRef.current?.setPageInfo?.({ current: 1, pageSize: 15 });
|
||||
actionRef.current?.reload();
|
||||
}}
|
||||
/>
|
||||
<XProTable<DigitalEmployeeAdminWorkbenchRow>
|
||||
key={activeView}
|
||||
actionRef={actionRef}
|
||||
formRef={formRef}
|
||||
rowKey={(record) =>
|
||||
`${activeView}:${record.sync_record_id || record.entity_id}`
|
||||
}
|
||||
columns={columns}
|
||||
request={request}
|
||||
showQueryButtons={hasPermission(
|
||||
'content_digital_employee_ops_query_list',
|
||||
)}
|
||||
/>
|
||||
<Drawer
|
||||
width={760}
|
||||
title="运营详情"
|
||||
open={Boolean(detailRecord)}
|
||||
onClose={() => setDetailRecord(null)}
|
||||
>
|
||||
{detailRecord && (
|
||||
<>
|
||||
<div style={{ marginBottom: 16 }}>{renderSummary(detailRecord)}</div>
|
||||
<Typography.Paragraph>
|
||||
<Typography.Text strong>客户端:</Typography.Text>
|
||||
{detailRecord.client_key_masked || '--'} /{' '}
|
||||
{detailRecord.device_id || '--'}
|
||||
</Typography.Paragraph>
|
||||
<Typography.Paragraph>
|
||||
<Typography.Text strong>实体:</Typography.Text>
|
||||
{detailRecord.entity_type || '--'} / {detailRecord.entity_id || '--'}
|
||||
</Typography.Paragraph>
|
||||
<Typography.Paragraph>
|
||||
<Typography.Text strong>关联:</Typography.Text>
|
||||
{[
|
||||
detailRecord.plan_id && `Plan ${detailRecord.plan_id}`,
|
||||
detailRecord.task_id && `Task ${detailRecord.task_id}`,
|
||||
detailRecord.run_id && `Run ${detailRecord.run_id}`,
|
||||
detailRecord.approval_id && `Approval ${detailRecord.approval_id}`,
|
||||
detailRecord.artifact_id && `Artifact ${detailRecord.artifact_id}`,
|
||||
detailRecord.notification_id &&
|
||||
`Notification ${detailRecord.notification_id}`,
|
||||
detailRecord.report_id && `Report ${detailRecord.report_id}`,
|
||||
detailRecord.route_decision_id &&
|
||||
`Route ${detailRecord.route_decision_id}`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' / ') || '--'}
|
||||
</Typography.Paragraph>
|
||||
<Typography.Title level={5}>Business View</Typography.Title>
|
||||
<Typography.Paragraph copyable={{ text: businessViewJson }}>
|
||||
<pre
|
||||
style={{
|
||||
maxHeight: 260,
|
||||
overflow: 'auto',
|
||||
margin: 0,
|
||||
padding: 16,
|
||||
background: '#f6f8fa',
|
||||
border: '1px solid #e5e7eb',
|
||||
borderRadius: 6,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
}}
|
||||
>
|
||||
{businessViewJson}
|
||||
</pre>
|
||||
</Typography.Paragraph>
|
||||
<Typography.Title level={5}>Payload</Typography.Title>
|
||||
<Typography.Paragraph copyable={{ text: payloadJson }}>
|
||||
<pre
|
||||
style={{
|
||||
maxHeight: 260,
|
||||
overflow: 'auto',
|
||||
margin: 0,
|
||||
padding: 16,
|
||||
background: '#f6f8fa',
|
||||
border: '1px solid #e5e7eb',
|
||||
borderRadius: 6,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
}}
|
||||
>
|
||||
{payloadJson}
|
||||
</pre>
|
||||
</Typography.Paragraph>
|
||||
</>
|
||||
)}
|
||||
</Drawer>
|
||||
</WorkspaceLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default DigitalEmployeeOps;
|
||||
@@ -357,6 +357,12 @@ const routes = [
|
||||
component:
|
||||
'@/pages/SystemManagement/Content/DigitalEmployeeSync',
|
||||
},
|
||||
{
|
||||
path: 'content-digital-employee-ops',
|
||||
name: '数字员工运营台',
|
||||
component:
|
||||
'@/pages/SystemManagement/Content/DigitalEmployeeOps',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -55,6 +55,8 @@ const MENU_ICON_MAP: Record<string, string> = {
|
||||
content_manage: 'icons-nav-cube',
|
||||
// 数字员工同步记录
|
||||
content_digital_employee_sync: 'icons-nav-robot',
|
||||
// 数字员工运营台
|
||||
content_digital_employee_ops: 'icons-nav-robot',
|
||||
// 支付与收益(开发者)
|
||||
dev_payment_earnings: 'icons-nav-my-earnings',
|
||||
// 订阅与积分(管理员)
|
||||
|
||||
@@ -8,6 +8,8 @@ import type {
|
||||
AccessStatsResult,
|
||||
AddSystemUserParams,
|
||||
ConversationStatsResult,
|
||||
DigitalEmployeeAdminWorkbenchListParams,
|
||||
DigitalEmployeeAdminWorkbenchPage,
|
||||
DigitalEmployeeSyncRecordListParams,
|
||||
DigitalEmployeeSyncRecordPage,
|
||||
ModelConfigDto,
|
||||
@@ -284,6 +286,30 @@ export async function apiDigitalEmployeeSyncRecordList(
|
||||
});
|
||||
}
|
||||
|
||||
// 查询数字员工正式运营台
|
||||
export async function apiDigitalEmployeeAdminWorkbenchList(
|
||||
data: DigitalEmployeeAdminWorkbenchListParams,
|
||||
): Promise<RequestResponse<DigitalEmployeeAdminWorkbenchPage>> {
|
||||
return request(`/api/digital-employee/admin/workbench/${data.view}`, {
|
||||
method: 'GET',
|
||||
params: {
|
||||
client_key: data.clientKey,
|
||||
device_id: data.deviceId,
|
||||
entity_type: data.entityType,
|
||||
entity_id: data.entityId,
|
||||
status: data.status,
|
||||
kind: data.kind,
|
||||
category: data.category,
|
||||
level: data.level,
|
||||
priority: data.priority,
|
||||
updated_from: data.updatedFrom,
|
||||
updated_to: data.updatedTo,
|
||||
page_no: data.pageNo,
|
||||
page_size: data.pageSize,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 查询网页应用列表
|
||||
export async function apiSystemResourceWebappList(
|
||||
data: SystemWebappListParams,
|
||||
|
||||
@@ -556,6 +556,68 @@ export interface DigitalEmployeeSyncRecordPage {
|
||||
records: DigitalEmployeeSyncRecordInfo[];
|
||||
}
|
||||
|
||||
export type DigitalEmployeeAdminWorkbenchView =
|
||||
| 'business'
|
||||
| 'interventions'
|
||||
| 'artifact_lifecycle'
|
||||
| 'notifications'
|
||||
| 'audits'
|
||||
| 'daily_reports'
|
||||
| 'route_governance';
|
||||
|
||||
export interface DigitalEmployeeAdminWorkbenchRow {
|
||||
view: DigitalEmployeeAdminWorkbenchView | string;
|
||||
sync_record_id: number;
|
||||
sync_record_url?: string;
|
||||
entity_type?: string;
|
||||
entity_id?: string;
|
||||
operation?: string;
|
||||
title?: string;
|
||||
status?: string;
|
||||
kind?: string;
|
||||
category?: string;
|
||||
level?: string;
|
||||
priority?: string;
|
||||
summary?: string;
|
||||
plan_id?: string;
|
||||
task_id?: string;
|
||||
run_id?: string;
|
||||
approval_id?: string;
|
||||
artifact_id?: string;
|
||||
notification_id?: string;
|
||||
report_id?: string;
|
||||
route_decision_id?: string;
|
||||
occurred_at?: string;
|
||||
synced_at?: string;
|
||||
client_key_masked?: string;
|
||||
device_id?: string;
|
||||
business_view?: Record<string, unknown>;
|
||||
payload?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface DigitalEmployeeAdminWorkbenchListParams
|
||||
extends SystemPaginationParams {
|
||||
view: DigitalEmployeeAdminWorkbenchView;
|
||||
clientKey?: string;
|
||||
deviceId?: string;
|
||||
entityType?: string;
|
||||
entityId?: string;
|
||||
status?: string;
|
||||
kind?: string;
|
||||
category?: string;
|
||||
level?: string;
|
||||
priority?: string;
|
||||
updatedFrom?: string;
|
||||
updatedTo?: string;
|
||||
}
|
||||
|
||||
export interface DigitalEmployeeAdminWorkbenchPage {
|
||||
total: number;
|
||||
page_no: number;
|
||||
page_size: number;
|
||||
records: DigitalEmployeeAdminWorkbenchRow[];
|
||||
}
|
||||
|
||||
// 网页应用信息
|
||||
export type SystemWebappInfo = SystemResourceInfo;
|
||||
|
||||
|
||||
@@ -609,8 +609,8 @@ GET /api/digital-employee/approvals
|
||||
- 建议:下一轮围绕管理端正式审计视图和诊断修复动作,把本地可追溯审计继续推进到可运营处置。
|
||||
|
||||
12. **管理端业务查询模型**
|
||||
- 已吸收:管理端已有通用 sync record 接收、查询、payload 摘要和深链;后端 sync record 已开始保存客户端顶层 `contract_version`、`entity_summary` 和 `business_view`,并基于现有 `digital_employee_sync_record` 拆出 `/api/digital-employee/plans`、`/plans/{plan_id}`、`/tasks`、`/runs`、`/events`、`/artifacts`、`/approvals` 管理端业务查询模型;列表支持客户端 Key、设备、实体 ID、状态、同步状态和同步时间范围过滤,业务 row 会提炼 title/status/summary、Plan/Task/Run 关联、发生/开始/结束时间、sync record 深链和脱敏客户端 Key;Plan 视图会聚合 task/run 计数与最近事件,Plan 详情会返回关联 tasks/runs/events/artifacts/approvals 摘要。embedded adapter 已拆出同名只读业务查询接口,从 qimingclaw runtime、artifact、approval 和 canonical event 投影生成统一分页视图;embedded 数字员工页已新增“业务视图”入口,支持计划/任务/运行/事件/产物/审批分段、实体/状态/同步状态/时间筛选、分页和详情 JSON 查看;客户端 outbox 的 event `business_view` 已开始按 route decision、governance command、approval action、artifact lifecycle/access、skill call audit 和 ready PlanStep dispatch 细分常用字段,本地与管理端业务视图投影已开始闭环。
|
||||
- 缺口:复杂组合检索、跨设备聚合图、正式外部管理端菜单整合和物化业务表仍待吸收。
|
||||
- 已吸收:管理端已有通用 sync record 接收、查询、payload 摘要和深链;后端 sync record 已开始保存客户端顶层 `contract_version`、`entity_summary` 和 `business_view`,并基于现有 `digital_employee_sync_record` 拆出 `/api/digital-employee/plans`、`/plans/{plan_id}`、`/tasks`、`/runs`、`/events`、`/artifacts`、`/approvals` 管理端业务查询模型;列表支持客户端 Key、设备、实体 ID、状态、同步状态和同步时间范围过滤,业务 row 会提炼 title/status/summary、Plan/Task/Run 关联、发生/开始/结束时间、sync record 深链和脱敏客户端 Key;Plan 视图会聚合 task/run 计数与最近事件,Plan 详情会返回关联 tasks/runs/events/artifacts/approvals 摘要。embedded adapter 已拆出同名只读业务查询接口,从 qimingclaw runtime、artifact、approval 和 canonical event 投影生成统一分页视图;embedded 数字员工页已新增“业务视图”入口,支持计划/任务/运行/事件/产物/审批分段、实体/状态/同步状态/时间筛选、分页和详情 JSON 查看;客户端 outbox 的 event `business_view` 已开始按 route decision、governance command、approval action、artifact lifecycle/access、skill call audit 和 ready PlanStep dispatch 细分常用字段,本地与管理端业务视图投影已开始闭环。正式外部管理端台面已开始吸收:qiming-backend 新增 `/api/digital-employee/admin/workbench/{view}`,qiming 管理端新增“数字员工运营台”,统一展示业务视图、介入台、产物治理、通知运营、审计诊断、日报运营和路由治理,只读消费 sync record `business_view` 并提供同步记录深链。
|
||||
- 缺口:复杂组合检索、跨设备聚合图、远程裁决/策略编辑动作和物化业务表仍待吸收。
|
||||
- 建议:先使用 sync record 派生业务查询模型和 embedded 业务视图预览承接联调,再按容量和查询压力决定是否拆 Plan / Task / Run / Event 物理业务表。
|
||||
|
||||
13. **日报实体与导出交付**
|
||||
|
||||
Reference in New Issue
Block a user