完善配送端详情字段中文化

This commit is contained in:
czl231
2026-08-23 09:24:47 +08:00
parent bebf9b52b7
commit cbaedafaf1
8 changed files with 757 additions and 117 deletions

View File

@@ -0,0 +1,186 @@
/**
* 功能描述:声明配送端聚合详情的固定字段顺序、关联表列和 JSON 展示规则。
* 版本v1.0.0。
*/
export type ResourceCollectionContract = {
columns: string[];
jsonColumns?: string[];
relationIdentityKeys?: Record<string, string>;
};
export type ResourceDetailContract = {
leadingKeys?: string[];
hiddenKeys?: string[];
collections?: Record<string, ResourceCollectionContract>;
};
const contracts: Record<string, ResourceDetailContract> = {
gasorder_contract: {
leadingKeys: [
'contract_no',
'contract_status',
'title',
'user_account_identity',
'user_service_active',
'gas_basic_identity',
'delivery_basic_identity',
'default_delivery_fee',
'signed_at',
'effective_at',
'expired_at',
'terms',
'identity',
'status',
'created_at',
'updated_at',
],
collections: {
products: {
columns: [
'bound_at',
'product_name',
'product_code',
'product_type_name',
'product_params',
'unit_price',
'unbound_at',
'unbind_reason',
],
jsonColumns: ['product_params'],
relationIdentityKeys: { product_name: 'product_info_identity' },
},
revisions: {
columns: [
'occurred_at',
'action',
'contract_status',
'effective_at',
'expired_at',
'operator_name',
'reason',
],
relationIdentityKeys: { operator_name: 'operator_identity' },
},
},
},
gasorder_basic: {
leadingKeys: [
'order_no',
'request_no',
'order_status',
'gasorder_contract_identity',
'creator_type',
'creator_identity',
'user_account_identity',
'gas_basic_identity',
'delivery_basic_identity',
'staff_account_identity',
'identity',
'status',
],
collections: {
items: {
columns: [
'product_name',
'product_code',
'product_type_name',
'product_params',
'unit_price',
],
jsonColumns: ['product_params'],
},
assignments: {
columns: [
'assigned_at',
'gas_basic_display_name',
'delivery_basic_display_name',
'staff_account_display_name',
'assigner_name',
'reason',
],
},
statuses: {
columns: [
'occurred_at',
'from_status',
'to_status',
'operator_name',
'reason',
],
},
tracks: {
columns: [
'attempt_no',
'staff_account_display_name',
'started_at',
'completed_at',
],
relationIdentityKeys: {
staff_account_display_name: 'staff_account_identity',
},
},
confirmations: {
columns: [
'confirmed_at',
'confirm_type',
'recipient_name',
'recipient_phone',
'remark',
],
},
payments: {
columns: [
'attempt_no',
'payment_no',
'payment_order_identity',
'amount',
'payment_status',
],
},
},
},
};
const jsonFieldKeys = new Set([
'params',
'product_params',
'product_snapshot',
'args',
'callback_msg',
]);
/** 返回资源专属详情契约;普通资源使用中文字段白名单动态排序。 */
export function resourceDetailContract(name: string): ResourceDetailContract {
return contracts[name] ?? {};
}
/** 判断主详情字段是否使用完整 JSON 查看器。 */
export function isResourceJsonField(key: string) {
return jsonFieldKeys.has(key);
}
/** 返回集合名称字段对应的稳定唯一标识字段。 */
export function collectionRelationIdentityKey(
resourceName: string,
collectionKey: string,
column: string,
) {
return (
contracts[resourceName]?.collections?.[collectionKey]
?.relationIdentityKeys?.[column] ?? ''
);
}
/** 判断集合字段是否使用完整 JSON 查看器。 */
export function isCollectionJsonField(
resourceName: string,
collectionKey: string,
column: string,
) {
return Boolean(
contracts[resourceName]?.collections?.[
collectionKey
]?.jsonColumns?.includes(column),
);
}

View File

@@ -1,10 +1,13 @@
/**
* 功能描述:统一配送点资源独立页面的字段名称和值展示。
* 版本v1.0.0。
* 版本v1.1.0。
*/
import dayjs from 'dayjs';
import { resourceFieldLabel, type ResourceUiDefinition } from './resources';
import type { ResourceRow } from './resource-record-form';
import {
resourceFieldLabel as defaultResourceFieldLabel,
type ResourceUiDefinition,
} from './resources';
const aliases: Record<string, string> = {
id: 'ID',
@@ -19,34 +22,175 @@ const aliases: Record<string, string> = {
payments: '支付记录',
revisions: '修订记录',
products: '合同气瓶',
addresses: '用户地址',
contract: '合同',
order: '订单',
user_service_active: '签约用户仍属合同气站',
user_account_display_name: '用户账户',
gas_basic_display_name: '气站',
delivery_basic_display_name: '配送点',
staff_account_display_name: '配送人员',
assigner_name: '分配人',
};
/** 聚合子表字段按父资源和集合覆盖全局同名字段语义。 */
const collectionAliases: Record<string, Record<string, string>> = {
'gasorder_contract.products': {
bound_at: '绑定时间',
product_name: '智能气阀名称',
product_code: '气瓶编码',
product_type_name: '气瓶类型名称',
product_params: '气瓶规格参数',
unit_price: '约定充装单价(元)',
unbound_at: '解绑时间',
unbind_reason: '解绑原因',
},
'gasorder_contract.revisions': {
occurred_at: '发生时间',
action: '合同动作',
contract_status: '合同状态',
effective_at: '生效时间',
expired_at: '到期时间',
operator_name: '操作人',
reason: '变更原因',
},
'gasorder_basic.items': {
product_name: '智能气阀名称',
product_code: '设备编码',
product_type_name: '设备类型',
product_params: '设备参数',
unit_price: '成交单价(元)',
},
'gasorder_basic.assignments': {
assigned_at: '分配时间',
gas_basic_display_name: '气站',
delivery_basic_display_name: '配送点',
staff_account_display_name: '配送人员',
assigner_name: '分配人',
reason: '分配原因',
},
'gasorder_basic.statuses': {
occurred_at: '发生时间',
from_status: '原状态',
to_status: '新状态',
operator_name: '操作人',
reason: '原因',
},
'gasorder_basic.tracks': {
attempt_no: '配送尝试次数',
staff_account_display_name: '配送人员',
started_at: '开始时间',
completed_at: '完成时间',
},
'gasorder_basic.confirmations': {
confirmed_at: '确认时间',
confirm_type: '确认方式',
recipient_name: '签收人姓名',
recipient_phone: '签收人电话',
remark: '备注',
},
'gasorder_basic.payments': {
attempt_no: '支付尝试次数',
payment_no: '支付单号',
payment_order_identity: '支付记录唯一标识',
amount: '支付金额(元)',
payment_status: '支付状态',
},
};
const moneyKeys = new Set([
'amount', 'unit_price', 'balance', 'withdrawal_balance', 'delivery_fee',
'discount_amount', 'total_amount', 'default_delivery_fee', 'fee',
'amount',
'unit_price',
'balance',
'withdrawal_balance',
'delivery_fee',
'discount_amount',
'total_amount',
'default_delivery_fee',
'fee',
]);
/** 返回聚合详情中的主记录。 */
export function primaryRecord(detail: ResourceRow): ResourceRow {
for (const key of ['order', 'contract', 'track', 'user']) {
if (detail[key] && typeof detail[key] === 'object') return detail[key] as ResourceRow;
if (detail[key] && typeof detail[key] === 'object')
return detail[key] as ResourceRow;
}
return detail;
}
/** 返回字段中文名称。 */
export function detailFieldLabel(key: string) {
return aliases[key] ?? resourceFieldLabel(key);
export function detailFieldLabel(
definition: ResourceUiDefinition,
key: string,
collectionKey = '',
) {
const normalized = key.endsWith('_masked') ? key.slice(0, -7) : key;
return (
collectionAliases[`${definition.name}.${collectionKey}`]?.[normalized] ??
definition.fields.find((field) => field.key === normalized)?.label ??
aliases[normalized] ??
defaultResourceFieldLabel(normalized)
);
}
/** 判断字段是否具有明确中文契约,未知响应键不得直接回显。 */
export function hasDetailFieldLabel(
definition: ResourceUiDefinition,
key: string,
collectionKey = '',
) {
const normalized = key.endsWith('_masked') ? key.slice(0, -7) : key;
return detailFieldLabel(definition, key, collectionKey) !== normalized;
}
/** 返回通用记录状态名称。 */
export function recordStatusLabel(status: number) {
return { 0: '待审核', 1: '启用', 2: '停用', 3: '已归档', 4: '已冻结' }[status] ?? '未知状态';
return (
{ 0: '待审核', 1: '启用', 2: '停用', 3: '已归档', 4: '已冻结' }[status] ??
`未知状态(${status}`
);
}
/** 返回通用记录状态颜色。 */
export function recordStatusColor(status: number) {
return { 0: 'orange', 1: 'green', 2: 'red', 3: 'gray', 4: 'purple' }[status] ?? 'gray';
return (
{ 0: 'orange', 1: 'green', 2: 'red', 3: 'gray', 4: 'purple' }[status] ??
'gray'
);
}
/** 返回合同业务状态名称。 */
export function contractStatusLabel(status: number) {
return (
{
0: '草稿',
10: '待处理',
11: '生效中',
12: '已过期',
13: '已终止',
}[status] ?? `未知状态(${status}`
);
}
/** 返回合同业务状态颜色。 */
export function contractStatusColor(status: number) {
return (
{ 0: 'gray', 10: 'orange', 11: 'green', 12: 'red', 13: 'gray' }[status] ??
'gray'
);
}
/** 返回合同修订动作名称。 */
export function contractActionLabel(action: unknown) {
const value = String(action);
return (
{
activate: '启用合同',
renew: '续签合同',
terminate: '终止合同',
}[value] ?? `未知动作(${value}`
);
}
/** 将接口字段值转换为中文可读内容。 */
@@ -57,17 +201,53 @@ export function displayResourceValue(
) {
if (value == null || value === '') return '-';
const field = definition.fields.find((item) => item.key === key);
const option = field?.options?.find((item) => String(item.value) === String(value));
const option = field?.options?.find(
(item) => String(item.value) === String(value),
);
if (option) return option.label;
if (key === 'role_code') {
return { delivery: '配送人员', installer: '安装人员', operations: '运维人员' }[String(value)]
?? String(value);
return (
{ delivery: '配送人员', installer: '安装人员', operations: '运维人员' }[
String(value)
] ?? String(value)
);
}
if (typeof value === 'boolean') return value ? '是' : '否';
if (key === 'status') return recordStatusLabel(Number(value));
if (moneyKeys.has(key) || key.endsWith('_amount') || key.endsWith('_balance_after')) {
if (key === 'contract_status') return contractStatusLabel(Number(value));
if (key === 'action') return contractActionLabel(value);
if (
[
'order_status',
'previous_order_status',
'from_status',
'to_status',
].includes(key)
) {
return (
{
0: '草稿',
16: '已创建',
18: '已分配',
19: '充装中',
20: '已就绪',
21: '异常',
22: '已取消',
23: '已完成',
33: '配送中',
34: '待确认',
}[Number(value)] ?? `未知状态(${String(value)}`
);
}
if (
moneyKeys.has(key) ||
key.endsWith('_amount') ||
key.endsWith('_balance_after')
) {
const amount = Number(value);
return Number.isFinite(amount) ? `¥${(amount / 100).toFixed(2)}` : String(value);
return Number.isFinite(amount)
? `¥${(amount / 100).toFixed(2)}`
: String(value);
}
if (key.endsWith('_at') || key === 'created_at' || key === 'updated_at') {
const date = dayjs(String(value));
@@ -77,10 +257,23 @@ export function displayResourceValue(
return String(value);
}
/** 判断软删除时间是否为空对象或空值。 */
export function isEmptyDeletedAt(value: unknown) {
if (value == null || value === '') return true;
if (typeof value !== 'object') return false;
return Object.values(value as Record<string, unknown>).every(
(item) => item == null || item === '' || item === false || item === 0,
);
}
/** 返回合同编辑阻止原因。 */
export function recordEditReason(definition: ResourceUiDefinition, row: ResourceRow) {
export function recordEditReason(
definition: ResourceUiDefinition,
row: ResourceRow,
) {
if (definition.name !== 'gasorder_contract') return '';
if (Number(row.contract_status) !== 0) return '只有草稿状态的合同可以编辑';
if (![0, 1].includes(Number(row.status))) return '停用、已归档或已冻结的合同不可编辑';
if (![0, 1].includes(Number(row.status)))
return '停用、已归档或已冻结的合同不可编辑';
return '';
}