修复合同详情字段中文显示

通用详情页复用中文字段字典,并为合同气瓶快照补充上下文名称。
合同状态按模型常量显示中文,数据库字段和 API 保持不变。
已通过资源页面检查、类型检查和生产构建。
This commit is contained in:
czl231
2026-08-12 19:49:41 +08:00
parent 3cad8fd0ba
commit deea31d79e
6 changed files with 154 additions and 6 deletions

View File

@@ -0,0 +1,46 @@
# 合同详情字段中文化操作日志
操作时间2026-08-12
操作类型:修改、扩展
影响模块:平台总后台通用资源详情页、配送合同详情页
## 操作前状态
通用详情页只从当前资源的表单字段配置中查找中文名称。接口返回但未列入表单配置的只读字段会直接显示英文键名;聚合子表也没有携带业务上下文,导致合同状态显示为 `contract_status 11`,合同气瓶快照显示英文表头。
## 具体操作
- 让通用详情字段名称解析复用现有全局中文字段字典。
- 为合同气瓶聚合集合增加基于模型语义的上下文字段名称。
- 根据后端合同状态常量增加前端只读显示映射。
- 聚合表格传入集合名称,确保上下文规则可以生效。
- 在标准资源页面静态检查中增加中文字段与合同状态映射断言。
## 行为变化
- 变更前:未显式配置的详情字段回显英文键名,合同状态 `11` 显示为数字。
- 变更后:字段名使用中文兜底;合同状态 `11` 显示为“生效中”;合同气瓶字段按气瓶快照语义展示。
- 数据库字段、API 字段、状态原始值及业务操作判断均未改变。
## 代码变更
- `frontend/platform_admin/src/api/resources.ts`:导出全局中文字段名称查询,并补充快照字段名称。
- `frontend/platform_admin/src/api/resource-display.ts`:增加合同气瓶上下文字段名称和合同状态显示映射。
- `frontend/platform_admin/src/views/resource/ResourceDetailContent.vue`:聚合表头传入集合上下文。
- `frontend/platform_admin/scripts/check-resource-pages.mjs`:增加静态回归断言。
## 验证结果
- `npm run resource-pages:check`:通过,覆盖详情 46 类、新建 25 类、编辑 23 类。
- `npm run type:check`:通过。
- `npm run build`通过Vite 成功构建 2615 个模块。
- 页面自动打开后被重定向到登录页;应用内浏览器没有现有登录会话,因此未执行登录后的视觉检查。
## 风险评估
- 修改仅影响前端显示,不改变接口兼容性和业务状态判断。
- 上下文覆盖仅作用于 `gasorder_contract.products`,不会把商城商品编码误改为气瓶编码。
- 后续增加新的聚合详情集合时,仍需补充对应的上下文字段名称,否则无法准确区分同名字段的业务语义。

View File

@@ -0,0 +1,43 @@
# 配送合同关联信息展示项目文档 v1.1
## 项目概述
本版本修复平台总后台通用资源详情页在字段未直接列入资源表单配置时回显英文键名的问题。配送合同及其合同气瓶关联记录根据数据库模型语义展示中文字段名称,底层数据库字段、接口字段和原始值保持兼容。
## 主要功能
- 通用详情页使用平台资源中文字段字典作为只读字段的兜底名称。
- 配送合同状态按模型常量显示为草稿、待处理、生效中、已过期或已终止。
- 合同气瓶关联表按快照语义显示“气瓶编码”“气瓶类型名称”“气瓶规格参数”等名称。
- 同名字段仍可在其他业务上下文使用原有名称,例如商城的 `product_code` 继续显示“商品编码”。
## 目录与核心文件
```text
platforms/
├── frontend/platform_admin/
│ ├── src/api/resources.ts # 全局字段中文名称配置
│ ├── src/api/resource-display.ts # 详情字段与状态值格式化
│ ├── src/views/resource/ResourceDetailContent.vue # 聚合详情表格展示
│ └── scripts/check-resource-pages.mjs # 静态回归检查
└── docs/ # 项目文档与操作日志
```
## 核心规则
1. 显式资源字段配置优先于全局字段名称。
2. 聚合子表的上下文名称优先级最高,避免同名字段在不同业务中被错误翻译。
3. 合同状态显示映射为:`0=草稿``10=待处理``11=生效中``12=已过期``13=已终止`
4. 未知状态显示为“未知(原始值)”,避免隐藏异常数据。
5. 本次仅修改展示层不修改数据库、Go 模型和 API 响应结构。
## 维护指南
- 新增普通只读字段时,优先在 `resources.ts` 的全局字段字典补充中文名称。
- 同一字段在聚合子表中具有特殊语义时,在 `resource-display.ts` 中增加“父资源.集合名”上下文覆盖。
- 修改详情字段解析后运行 `npm run resource-pages:check``npm run type:check``npm run build`
## 变更记录
- v1.12026-08-12增加通用字段中文兜底、合同状态中文化和合同气瓶上下文字段名称。

View File

@@ -1,6 +1,6 @@
/**
* 功能:静态检查平台总后台全部标准资源是否具备独立记录页配置。
* 版本v1.1.0
* 版本v1.2.0
*/
import { readFileSync, existsSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
@@ -15,6 +15,8 @@ const routeSource = [
const routeBuilderSource = readFileSync(resolve(root, 'src/router/routes/modules/resource-route-builder.ts'), 'utf8');
const ruleSource = readFileSync(resolve(root, 'src/api/resource-page-rules.ts'), 'utf8');
const listSource = readFileSync(resolve(root, 'src/views/shared/CrudListPage.vue'), 'utf8');
const detailSource = readFileSync(resolve(root, 'src/views/resource/ResourceDetailContent.vue'), 'utf8');
const displaySource = readFileSync(resolve(root, 'src/api/resource-display.ts'), 'utf8');
const treeResources = new Set(['ec_category', 'platform_menu']);
const listResources = contract.resources.filter((item) => !treeResources.has(item.name));
const createModes = new Set(['writable', 'editable', 'append_only', 'managed']);
@@ -49,6 +51,17 @@ if (
if (existsSync(resolve(root, 'src/views/account/AccountProfilePage.vue'))) {
fail('旧账户资料组件仍存在,未统一到共享记录页');
}
if (!detailSource.includes('resourceFieldLabel(definition, column, collection.key)')) {
fail('聚合详情表格未按集合上下文解析中文字段名称');
}
for (const marker of [
"'gasorder_contract.products'",
"product_info_identity: '气瓶唯一标识'",
"product_code: '气瓶编码'",
"if (key === 'contract_status') return contractStatusLabel(Number(value))",
]) {
if (!displaySource.includes(marker)) fail(`合同详情中文展示缺少 ${marker}`);
}
const createCount = listResources.filter((item) => createModes.has(item.mode)).length;
console.log(

View File

@@ -1,10 +1,14 @@
/**
* 功能:统一资源列表与详情页面的字段名称、关系、金额、状态和时间展示。
* 版本v1.1.0
* 版本v1.2.0
*/
import dayjs from 'dayjs';
import { staffRoleLabel } from './resource-staff-relation';
import type { ResourceField, ResourceUiDefinition } from './resources';
import {
defaultResourceFieldLabel,
type ResourceField,
type ResourceUiDefinition,
} from './resources';
import type { RecordPageMode, ResourceRow } from './resource-page-rules';
const aliases: Record<string, string> = {
@@ -30,6 +34,21 @@ const aliases: Record<string, string> = {
is_system: '系统内置',
};
/** 聚合子表字段可能与全局同名字段具有不同业务语义,按父资源和集合名称覆盖。 */
const collectionFieldAliases: Record<string, Record<string, string>> = {
'gasorder_contract.products': {
gasorder_contract_identity: '配送合同唯一标识',
product_info_identity: '气瓶唯一标识',
product_code: '气瓶编码',
product_type_name: '气瓶类型名称',
product_params: '气瓶规格参数',
bound_at: '绑定时间',
unbound_at: '解绑时间',
unbind_reason: '解绑原因',
unit_price: '约定充装单价(元)',
},
};
const amountKeys = new Set([
'amount',
'unit_price',
@@ -72,11 +91,16 @@ export function primaryRecord(detail: ResourceRow): ResourceRow {
export function resourceFieldLabel(
definition: ResourceUiDefinition,
key: string,
collectionKey = '',
) {
const normalized = key.endsWith('_masked') ? key.slice(0, -7) : key;
const collectionAliases =
collectionFieldAliases[`${definition.name}.${collectionKey}`];
return (
collectionAliases?.[normalized] ??
definition.fields.find((field) => field.key === normalized)?.label ??
aliases[key] ??
defaultResourceFieldLabel(normalized) ??
aliases[normalized] ??
key
);
}
@@ -96,6 +120,19 @@ export function recordStatusColor(status: number) {
);
}
/** 将合同业务状态转换为模型常量对应的中文名称。 */
export function contractStatusLabel(status: number) {
return (
{
0: '草稿',
10: '待处理',
11: '生效中',
12: '已过期',
13: '已终止',
}[status] ?? `未知(${status}`
);
}
/** 选择关系记录的首选可读名称。 */
export function optionLabel(option: ResourceRow) {
return String(
@@ -141,6 +178,7 @@ 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 (key === 'contract_status') return contractStatusLabel(Number(value));
if (
amountKeys.has(key) ||
key.endsWith('_amount') ||

View File

@@ -184,6 +184,7 @@ const fieldLabels: Record<string, string> = {
default_delivery_fee: '默认配送费(元)',
signed_at: '签署时间',
effective_at: '生效时间',
bound_at: '绑定时间',
unit_price: '单价(元)',
unbound_at: '解绑时间',
revision_no: '修订号',
@@ -203,6 +204,8 @@ const fieldLabels: Record<string, string> = {
confirmed_at: '确认时间',
sort_no: '排序',
product_code: '商品编码',
product_type_name: '产品类型名称',
product_params: '产品规格参数',
price_amount: '售价(元)',
stock_quantity: '库存',
value: '属性值',
@@ -258,6 +261,11 @@ const fieldLabels: Record<string, string> = {
withdrawable: '计入可提现余额',
};
/** 返回字段的全局中文名称,供未显式声明字段的只读页面统一兜底。 */
export function defaultResourceFieldLabel(key: string) {
return fieldLabels[key];
}
const numbers = new Set([
'sort_no', 'stock_quantity', 'quantity', 'score', 'version_no',
'attempt_no', 'target_product_status',

View File

@@ -1,6 +1,6 @@
<!--
功能以响应式信息卡和子表页签展示标准资源详情
版本v1.2.0
版本v1.3.0
-->
<template>
<div class="detail-stack">
@@ -27,7 +27,7 @@
<a-table-column
v-for="column in collection.columns"
:key="column"
:title="resourceFieldLabel(definition, column)"
:title="resourceFieldLabel(definition, column, collection.key)"
:width="column.includes('identity') ? 220 : 160"
ellipsis
tooltip