Files
platforms/frontend/platform_admin/src/api/resource-display.ts

219 lines
6.5 KiB
TypeScript
Raw Normal View History

/**
*
* v1.1.0
*/
import dayjs from 'dayjs';
import { staffRoleLabel } from './resource-staff-relation';
import type { ResourceField, ResourceUiDefinition } from './resources';
import type { RecordPageMode, ResourceRow } from './resource-page-rules';
const aliases: Record<string, string> = {
identity: '唯一标识',
id: 'ID',
created_at: '创建时间',
updated_at: '更新时间',
deleted_at: '删除时间',
DeletedAt: '删除时间',
status: '状态',
version: '版本',
items: '订单明细',
assignments: '分配记录',
statuses: '状态记录',
tracks: '运行轨迹',
confirmations: '确认记录',
payments: '支付记录',
revisions: '修订记录',
products: '合同气瓶',
order: '订单',
contract: '合同',
wallet: '钱包',
is_system: '系统内置',
};
const amountKeys = new Set([
'amount',
'unit_price',
'balance',
'withdrawal_balance',
'default_delivery_fee',
'discount_amount',
'total_amount',
'delivery_fee',
'price_amount',
'sale_amount',
'difference_amount',
'fee',
]);
/** 返回资源独立页面的模式名称。 */
export function recordPageModeLabel(mode: RecordPageMode) {
return { create: '新建', detail: '详情', edit: '编辑' }[mode];
}
/** 返回资源独立页面的错误标题。 */
export function recordPageErrorTitle(status: '403' | '404' | 'error') {
return {
'403': '无法执行此操作',
'404': '记录不存在',
error: '页面加载失败',
}[status];
}
/** 返回聚合详情中的主记录。 */
export function primaryRecord(detail: ResourceRow): ResourceRow {
if (detail.order && typeof detail.order === 'object')
return detail.order as ResourceRow;
if (detail.contract && typeof detail.contract === 'object')
return detail.contract as ResourceRow;
return detail;
}
/** 返回字段中文名称,脱敏字段沿用原字段名称。 */
export function resourceFieldLabel(
definition: ResourceUiDefinition,
key: string,
) {
const normalized = key.endsWith('_masked') ? key.slice(0, -7) : key;
return (
definition.fields.find((field) => field.key === normalized)?.label ??
aliases[key] ??
key
);
}
/** 将标准实体状态转换为稳定的中文展示。 */
export function recordStatusLabel(status: number) {
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'
);
}
/** 选择关系记录的首选可读名称。 */
export function optionLabel(option: ResourceRow) {
return String(
option.name ??
option.title ??
option.display_name ??
option.code ??
option.username ??
option.contract_no ??
option.order_no ??
option.identity ??
'-',
);
}
/** 工作人员关系额外展示角色,其他关系保持原有可读名称。 */
export function relationOptionLabel(field: ResourceField, option: ResourceRow) {
const label = optionLabel(option);
return field.relation === '/staff_account'
? `${label}${staffRoleLabel(option.role_code)}`
: label;
}
function relationLabel(
field: ResourceField,
identity: string,
relationOptions: Record<string, ResourceRow[]>,
) {
const match = (relationOptions[field.relation ?? ''] ?? []).find(
(option) => String(option.identity) === identity,
);
if (!match) return identity;
if (field.relation === '/staff_account') {
return relationOptionLabel(field, match);
}
return field.displayRelationLabel
? optionLabel(match)
: `${optionLabel(match)} · ${identity}`;
}
/** 格式化不依赖字段定义的通用值。 */
export function displayRawValue(key: string, value: unknown) {
if (value == null || value === '') return '-';
if (typeof value === 'boolean') return value ? '是' : '否';
if (key === 'status') return recordStatusLabel(Number(value));
if (
amountKeys.has(key) ||
key.endsWith('_amount') ||
key.endsWith('_balance_after')
) {
const amount = Number(value);
return Number.isFinite(amount)
? `¥${(amount / 100).toFixed(2)}`
: String(value);
}
if (key.endsWith('_at') || ['created_at', 'updated_at'].includes(key)) {
const date = dayjs(String(value));
return date.isValid() ? date.format('YYYY-MM-DD HH:mm:ss') : String(value);
}
if (
['deleted_at', 'DeletedAt'].includes(key) &&
typeof value === 'object' &&
value &&
'Time' in value
) {
const date = dayjs(String((value as { Time?: unknown }).Time ?? ''));
return date.isValid() ? date.format('YYYY-MM-DD HH:mm:ss') : '-';
}
if (typeof value === 'object') return JSON.stringify(value, null, 2);
return String(value);
}
/** 按字段选项和关系配置格式化值。 */
export function displayResourceField(
field: ResourceField,
row: ResourceRow,
relationOptions: Record<string, ResourceRow[]>,
fieldOptions: Record<
string,
Array<{ label: string; value: string | number }>
> = {},
) {
const value = row[field.key] ?? row[`${field.key}_masked`];
if (value == null || value === '') return field.emptyText ?? '-';
const hasOptionSource =
Object.prototype.hasOwnProperty.call(fieldOptions, field.key) ||
Boolean(field.options);
const options = fieldOptions[field.key] ?? field.options;
const option = options?.find((item) => String(item.value) === String(value));
if (option) return option.label;
if (hasOptionSource && field.unknownValueLabel) {
return `${field.unknownValueLabel}${String(value)}`;
}
if (field.displayPrecision != null) {
const numericValue = Number(value);
return Number.isFinite(numericValue)
? numericValue.toFixed(field.displayPrecision)
: String(value);
}
if (field.type === 'identity' && typeof value === 'string') {
return relationLabel(field, value, relationOptions);
}
if (field.type === 'identity-list' && Array.isArray(value)) {
return value
.map((item) => relationLabel(field, String(item), relationOptions))
.join('、');
}
return displayRawValue(field.key, value);
}
/** 判断软删除结构是否为空。 */
export function isEmptyDeletedAt(value: unknown) {
if (value == null || value === '') return true;
return Boolean(
typeof value === 'object' &&
value &&
'Valid' in value &&
!(value as { Valid?: boolean }).Valid,
);
}