完善气站客服工单详情展示

This commit is contained in:
czl231
2026-08-22 10:48:36 +08:00
parent b85e879130
commit 79749685ee
9 changed files with 252 additions and 10 deletions

View File

@@ -17,6 +17,10 @@ const fieldFormSource = readFileSync(
resolve(root, 'src/views/resource/ResourceFieldForm.vue'),
'utf8',
);
const relationLoaderSource = readFileSync(
resolve(root, 'src/views/resource/load-resource-record-relations.ts'),
'utf8',
);
const contract = JSON.parse(
readFileSync(resolve(root, 'src/contracts/gas-resources.json'), 'utf8'),
);
@@ -89,6 +93,25 @@ for (const option of [
]) {
if (!source.includes(option)) throw new Error(`客服工单枚举缺失:${option}`);
}
for (const display of [
"label: '配送点'",
"emptyText: '尚未分配配送点'",
"label: '处理人员'",
"emptyText: '尚未分配处理人员'",
"label: '服务气站'",
"emptyText: '尚未关联服务气站'",
"label: '问题描述'",
"emptyText: '未填写问题描述'",
"label: '预约时间'",
"emptyText: '未预约'",
"label: '处理结果'",
"emptyText: '尚未提交处理结果'",
]) {
if (!source.includes(display))
throw new Error(`客服工单详情展示契约缺失:${display}`);
}
if (!relationLoaderSource.includes('relationDisplayName(field, options.record)'))
throw new Error('详情关系已有可读名称时不得重复请求关联资源');
const forbidden = [
'/platform/platform_', '/gas/gas_', '/delivery/delivery_', '/staff/',

View File

@@ -85,6 +85,7 @@ const collectionFieldAliases: Record<string, Record<string, string>> = {
attempt_no: '配送尝试次数',
staff_account_display_name: '配送人员',
staff_account_identity: '配送人员唯一标识',
appointment_at: '预约时间',
started_at: '开始时间',
completed_at: '完成时间',
},

View File

@@ -162,6 +162,7 @@ const fieldLabels: Record<string, string> = {
reconciliation_status: '对账状态',
repair_no: '检修单号',
repair_type: '检修类型',
appointment_at: '预约时间',
started_at: '开始时间',
completed_at: '完成时间',
result: '检修结果',
@@ -309,7 +310,7 @@ const money = new Set([
const booleans = new Set(['is_default', 'is_cover', 'selected', 'withdrawable']);
const dates = new Set(['bill_date']);
const datetimes = new Set([
'expired_at', 'produced_at', 'enabled_at', 'started_at', 'completed_at',
'expired_at', 'produced_at', 'enabled_at', 'appointment_at', 'started_at', 'completed_at',
'occurred_at', 'reviewed_at', 'signed_at', 'effective_at', 'unbound_at',
'assigned_at', 'confirmed_at', 'paid_at', 'period_start', 'period_end',
]);
@@ -429,6 +430,91 @@ const ticketPriorityField = f('priority', {
],
});
/** 客服工单详情字段完整沿用平台口径,辅助处理字段只在详情页展示。 */
const ticketFields = [
relation('user_account_identity', '/user_account', true, {
label: '用户',
listLabel: '用户',
listRelationNameOnly: true,
listDisplayIdentityCopy: true,
displayRelationLabel: true,
showIdentityCopy: true,
}),
f('ticket_no', { required: true }),
ticketCategoryField,
ticketPriorityField,
relation('delivery_basic_identity', '/delivery_basic', false, {
label: '配送点',
listHidden: true,
displayRelationLabel: true,
showIdentityCopy: true,
emptyText: '尚未分配配送点',
}),
relation('staff_account_identity', '/staff_account', false, {
label: '处理人员',
listHidden: true,
displayRelationLabel: true,
showIdentityCopy: true,
emptyText: '尚未分配处理人员',
staffRelation: { roles: ['installer', 'delivery', 'operations'] },
}),
relation('gas_basic_identity', '/gas_basic', false, {
label: '服务气站',
listHidden: true,
displayRelationLabel: true,
showIdentityCopy: true,
emptyText: '尚未关联服务气站',
}),
f('operator_identity', {
label: '最近操作人标识',
listHidden: true,
listCopyable: true,
emptyText: '暂无操作记录',
}),
f('description', {
label: '问题描述',
type: 'textarea',
listHidden: true,
emptyText: '未填写问题描述',
}),
f('request_no', {
label: '请求流水号',
listHidden: true,
listCopyable: true,
emptyText: '未记录请求流水号',
}),
f('address', {
label: '上门地址',
type: 'textarea',
listHidden: true,
emptyText: '未填写上门地址',
}),
f('appointment_at', {
label: '预约时间',
type: 'datetime',
listHidden: true,
emptyText: '未预约',
}),
f('started_at', {
label: '开始时间',
type: 'datetime',
listHidden: true,
emptyText: '尚未开始处理',
}),
f('completed_at', {
label: '完成时间',
type: 'datetime',
listHidden: true,
emptyText: '尚未完成',
}),
f('result', {
label: '处理结果',
type: 'textarea',
listHidden: true,
emptyText: '尚未提交处理结果',
}),
];
/** 提现渠道保留未来选项,但当前仅开放银行卡提现。 */
const withdrawalChannelField = f('channel', {
required: true,
@@ -538,7 +624,7 @@ const platformResources: ResourceUiDefinition[] = [
define('fin_settlement', '财务结算', 'writable', [f('settlement_no', { required: true }), f('subject_type', { required: true }), f('subject_identity', { required: true }), f('period_start', { required: true }), f('period_end', { required: true })]),
define('fin_reconciliation', '财务对账', 'readonly', []),
define('cms_content', '内容', 'writable', [f('content_type', { required: true }), f('title', { required: true }), f('body', { required: true }), f('version_no'), f('publish_status')]),
define('cs_ticket', '客服工单', 'writable', [relation('user_account_identity', '/user_account', true), f('ticket_no', { required: true }), ticketCategoryField, ticketPriorityField]),
define('cs_ticket', '客服工单', 'writable', ticketFields),
define('platform_account', '平台账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('display_name'), f('avatar'), f('platform_role_code', { required: true }), f('phone')]),
define('platform_role', '平台角色', 'writable', [f('role_code', { required: true }), f('name', { required: true }), f('location_scope', { required: true, type: 'select', options: [{ label: '脱敏坐标', value: 'standard' }, { label: '精确坐标', value: 'precise' }] })], 'list', [
{ name: '分配菜单', resource: '/platform_role/:identity/menu', method: 'PUT', fields: [f('menu_identities', { type: 'identity-list', relation: '/platform_menu' })] },
@@ -585,7 +671,7 @@ const gasOverrides: ResourceUiDefinition[] = [
define('wallet_apply_cash', '提现申请', 'append_only', [relation('wallet_bank_identity', '/wallet_bank'), f('request_no', { required: true }), f('amount', { required: true }), withdrawalChannelField, f('remark')]),
define('fin_settlement', '财务结算', 'readonly', []),
define('fin_reconciliation', '财务对账', 'readonly', []),
define('cs_ticket', '客服工单', 'writable', [relation('user_account_identity', '/user_account', true), f('ticket_no', { required: true }), ticketCategoryField, ticketPriorityField]),
define('cs_ticket', '客服工单', 'writable', ticketFields),
];
const gasResourceNames = new Set(gasOverrides.map((item) => item.name));

View File

@@ -4,9 +4,10 @@
*/
import { Message } from '@arco-design/web-vue';
import type { Ref } from 'vue';
import { platformApi, type PlatformRole } from '@/api/platform';
import type { ResourceField, ResourceUiDefinition } from '@/api/resources';
import { type PlatformRole, platformApi } from '@/api/platform';
import type { ResourceRow } from '@/api/resource-page-rules';
import type { ResourceField, ResourceUiDefinition } from '@/api/resources';
import { relationDisplayName } from '../shared/resource-list-field-display';
import type { ResourceRelations } from './use-resource-relations';
type LoaderOptions = {
@@ -26,7 +27,15 @@ type LoaderOptions = {
/** 加载当前页面实际使用的关系和动态平台角色。 */
export async function loadResourceRecordRelations(options: LoaderOptions) {
if (options.detailMode) {
await options.relations.preload(options.definition.fields);
// 详情只加载实际存在且服务端未提供可读名称的关系,避免为空字段发起无意义请求。
const unresolvedFields = options.definition.fields.filter((field) => {
const value = options.record[field.key];
const hasValue = Array.isArray(value)
? value.some(Boolean)
: String(value ?? '').trim() !== '';
return hasValue && !relationDisplayName(field, options.record);
});
await options.relations.preload(unresolvedFields);
await options.relations.ensureValues(
options.definition.fields,
options.record,