完善配送端详情字段中文化
This commit is contained in:
186
frontend/delivery_admin/src/api/resource-detail-contract.ts
Normal file
186
frontend/delivery_admin/src/api/resource-detail-contract.ts
Normal 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),
|
||||
);
|
||||
}
|
||||
@@ -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 '';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user