统一平台资源中文展示并修复模拟订单状态
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
功能:以响应式信息卡和子表页签展示标准资源详情。
|
||||
版本:v1.7.0
|
||||
版本:v1.8.0
|
||||
-->
|
||||
<template>
|
||||
<div class="detail-stack">
|
||||
@@ -8,12 +8,19 @@
|
||||
<div class="detail-grid">
|
||||
<div v-for="entry in entries" :key="entry.key" class="detail-item" :class="{ 'detail-item-wide': entry.wide }">
|
||||
<span class="detail-label">{{ entry.label }}</span>
|
||||
<pre v-if="entry.objectValue" class="json-value">{{ entry.value }}</pre>
|
||||
<a-popover v-if="entry.jsonValue" position="left">
|
||||
<a-button type="text" size="mini">查看内容</a-button>
|
||||
<template #content><pre class="json-value">{{ entry.value }}</pre></template>
|
||||
</a-popover>
|
||||
<IdentityText v-else-if="entry.key === 'identity'" :value="String(entry.value)" />
|
||||
<div v-else-if="entry.identityValue" class="detail-relation-value">
|
||||
<span class="detail-value">{{ entry.value }}</span>
|
||||
<IdentityText :value="entry.identityValue" />
|
||||
</div>
|
||||
<RelationNameText
|
||||
v-else-if="entry.identityValue"
|
||||
:name="entry.value"
|
||||
:identity="entry.identityValue"
|
||||
:identity-label="entry.label"
|
||||
:clickable="Boolean(entry.relationResource)"
|
||||
@open="openRelation(entry.relationResource, entry.identityValue)"
|
||||
/>
|
||||
<span v-else class="detail-value">{{ entry.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -29,20 +36,20 @@
|
||||
v-for="column in collection.columns"
|
||||
:key="column"
|
||||
:title="resourceFieldLabel(definition, column, collection.key)"
|
||||
:width="collectionColumnWidth(column)"
|
||||
:width="collectionColumnWidth(collection.key, column)"
|
||||
ellipsis
|
||||
tooltip
|
||||
>
|
||||
<template #cell="{ record }">
|
||||
<div v-if="collectionRelationIdentityKey(collection.key, column)" class="collection-relation-value">
|
||||
<div v-if="collectionRelationIdentityKey(definition.name, collection.key, column)" class="collection-relation-value">
|
||||
<span>{{ displayRawValue(column, record[column]) }}</span>
|
||||
<IdentityText
|
||||
v-if="record[collectionRelationIdentityKey(collection.key, column)]"
|
||||
:value="String(record[collectionRelationIdentityKey(collection.key, column)])"
|
||||
v-if="record[collectionRelationIdentityKey(definition.name, collection.key, column)]"
|
||||
:value="String(record[collectionRelationIdentityKey(definition.name, collection.key, column)])"
|
||||
/>
|
||||
</div>
|
||||
<IdentityText v-else-if="column.includes('identity') && record[column]" :value="String(record[column])" />
|
||||
<a-popover v-else-if="isCollectionJsonColumn(column)" position="left">
|
||||
<a-popover v-else-if="isCollectionJsonField(definition.name, collection.key, column)" position="left">
|
||||
<a-button type="text" size="mini">查看参数</a-button>
|
||||
<template #content>
|
||||
<pre class="collection-json-value">{{ formatCollectionJson(record[column]) }}</pre>
|
||||
@@ -62,16 +69,25 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import {
|
||||
displayRawValue,
|
||||
displayResourceField,
|
||||
hasResourceFieldLabel,
|
||||
isEmptyDeletedAt,
|
||||
primaryRecord,
|
||||
resourceFieldLabel,
|
||||
} from '@/api/resource-display';
|
||||
import {
|
||||
collectionRelationIdentityKey,
|
||||
isCollectionJsonField,
|
||||
isResourceJsonField,
|
||||
resourceDetailContract,
|
||||
} from '@/api/resource-detail-contract';
|
||||
import type { ResourceRow } from '@/api/resource-page-rules';
|
||||
import type { ResourceUiDefinition } from '@/api/resources';
|
||||
import IdentityText from '@/components/IdentityText.vue';
|
||||
import RelationNameText from '@/views/shared/RelationNameText.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
definition: ResourceUiDefinition;
|
||||
@@ -88,22 +104,34 @@ type DetailEntry = {
|
||||
key: string;
|
||||
label: string;
|
||||
value: string;
|
||||
objectValue: boolean;
|
||||
jsonValue: boolean;
|
||||
wide: boolean;
|
||||
identityValue: string;
|
||||
relationResource: string;
|
||||
};
|
||||
|
||||
/** 统一关联记录表列宽:日期时间列保持紧凑,唯一标识列保留完整复制空间。 */
|
||||
function collectionColumnWidth(column: string) {
|
||||
if (column.endsWith('_at')) return 150;
|
||||
if (column.includes('identity')) return 220;
|
||||
if (isCollectionJsonColumn(column)) return 100;
|
||||
return 160;
|
||||
const router = useRouter();
|
||||
|
||||
/** 打开关系资源详情;没有详情路由的树形资源保持只读。 */
|
||||
function openRelation(resource: string, identity: string) {
|
||||
if (!resource) return;
|
||||
const target = router
|
||||
.getRoutes()
|
||||
.find(
|
||||
(item) =>
|
||||
item.meta.resource === resource && item.meta.recordMode === 'detail',
|
||||
);
|
||||
if (!target?.name) return;
|
||||
void router.push({ name: target.name, params: { identity } });
|
||||
}
|
||||
|
||||
/** 标识关联子表中需要完整查看的 JSON 快照字段。 */
|
||||
function isCollectionJsonColumn(column: string) {
|
||||
return column === 'product_params';
|
||||
/** 统一关联记录表列宽:日期时间列保持紧凑,唯一标识列保留完整复制空间。 */
|
||||
function collectionColumnWidth(collectionKey: string, column: string) {
|
||||
if (column.endsWith('_at')) return 150;
|
||||
if (column.includes('identity')) return 220;
|
||||
if (isCollectionJsonField(props.definition.name, collectionKey, column))
|
||||
return 100;
|
||||
return 160;
|
||||
}
|
||||
|
||||
/** 将字符串或对象参数格式化为可读 JSON;历史非 JSON 文本保持原样。 */
|
||||
@@ -119,20 +147,16 @@ function formatCollectionJson(value: unknown) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 返回集合可读名称对应的稳定标识字段。 */
|
||||
function collectionRelationIdentityKey(collectionKey: string, column: string) {
|
||||
if (collectionKey !== 'assignments') return '';
|
||||
return (
|
||||
{
|
||||
gas_basic_display_name: 'gas_basic_identity',
|
||||
delivery_basic_display_name: 'delivery_basic_identity',
|
||||
staff_account_display_name: 'staff_account_identity',
|
||||
}[column] ?? ''
|
||||
);
|
||||
}
|
||||
|
||||
/** 返回订单详情关联标识对应的服务端可读名称字段。 */
|
||||
function detailRelationDisplayName(row: ResourceRow, key: string) {
|
||||
function detailRelationDisplayName(
|
||||
row: ResourceRow,
|
||||
key: string,
|
||||
configuredDisplayKey = '',
|
||||
) {
|
||||
const conventionalKey = key.endsWith('_identity')
|
||||
? `${key.slice(0, -9)}_display_name`
|
||||
: '';
|
||||
const displayKey =
|
||||
{
|
||||
creator_identity: 'creator_display_name',
|
||||
@@ -141,13 +165,16 @@ function detailRelationDisplayName(row: ResourceRow, key: string) {
|
||||
delivery_basic_identity: 'delivery_basic_display_name',
|
||||
staff_account_identity: 'staff_account_display_name',
|
||||
operator_identity: 'operator_name',
|
||||
assigner_identity: 'assigner_name',
|
||||
}[key] ?? '';
|
||||
if (!displayKey) return '';
|
||||
return String(row[displayKey] ?? '').trim();
|
||||
return String(
|
||||
row[configuredDisplayKey || displayKey || conventionalKey] ?? '',
|
||||
).trim();
|
||||
}
|
||||
|
||||
const entries = computed<DetailEntry[]>(() => {
|
||||
const row = primaryRecord(props.detail);
|
||||
const contract = resourceDetailContract(props.definition.name);
|
||||
const excluded = new Set([
|
||||
'id',
|
||||
'password',
|
||||
@@ -156,27 +183,12 @@ const entries = computed<DetailEntry[]>(() => {
|
||||
'attachment',
|
||||
// 合同可读名称只负责渲染“配送合同”,不作为独立业务字段重复展示。
|
||||
'contract_display_name',
|
||||
...(contract.hiddenKeys ?? []),
|
||||
]);
|
||||
if (props.accountSummary) {
|
||||
for (const key of ['username', 'identity', 'created_at']) excluded.add(key);
|
||||
}
|
||||
const leadingKeys =
|
||||
props.definition.name === 'gasorder_basic'
|
||||
? [
|
||||
'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',
|
||||
]
|
||||
: ['identity', 'status'];
|
||||
const leadingKeys = contract.leadingKeys ?? ['identity', 'status'];
|
||||
const preferred = [
|
||||
...leadingKeys,
|
||||
...props.definition.fields.map((field) => field.key),
|
||||
@@ -202,7 +214,7 @@ const entries = computed<DetailEntry[]>(() => {
|
||||
excluded.has(key) ||
|
||||
key.endsWith('_id') ||
|
||||
key.endsWith('_display_name') ||
|
||||
(props.definition.name === 'gasorder_basic' && key === 'operator_name')
|
||||
!hasResourceFieldLabel(props.definition, actualKey)
|
||||
)
|
||||
return [];
|
||||
const value = row[actualKey];
|
||||
@@ -210,7 +222,12 @@ const entries = computed<DetailEntry[]>(() => {
|
||||
if (['deleted_at', 'DeletedAt'].includes(key) && isEmptyDeletedAt(value))
|
||||
return [];
|
||||
const field = props.definition.fields.find((item) => item.key === key);
|
||||
const relationDisplayName = detailRelationDisplayName(row, key);
|
||||
// 资源已声明详情名称键时优先直接使用接口返回值,避免关系候选慢查询导致显示失败。
|
||||
const relationDisplayName = detailRelationDisplayName(
|
||||
row,
|
||||
key,
|
||||
field?.detailDisplayKey,
|
||||
);
|
||||
const display = relationDisplayName
|
||||
? relationDisplayName
|
||||
: field
|
||||
@@ -221,22 +238,32 @@ const entries = computed<DetailEntry[]>(() => {
|
||||
props.fieldOptions,
|
||||
)
|
||||
: displayRawValue(actualKey, value);
|
||||
const objectValue = typeof value === 'object' && value !== null;
|
||||
const jsonValue =
|
||||
isResourceJsonField(key) ||
|
||||
(typeof value === 'object' && value !== null);
|
||||
return [
|
||||
{
|
||||
key,
|
||||
label: resourceFieldLabel(props.definition, actualKey),
|
||||
value: display,
|
||||
objectValue,
|
||||
value: jsonValue ? formatCollectionJson(value) : display,
|
||||
jsonValue,
|
||||
identityValue:
|
||||
(relationDisplayName ||
|
||||
field?.type === 'identity' ||
|
||||
field?.showIdentityCopy ||
|
||||
field?.staffRelation?.showIdentityCopy) &&
|
||||
value
|
||||
? String(value)
|
||||
: '',
|
||||
relationResource:
|
||||
field?.relation ??
|
||||
field?.dynamicRelation?.resources[
|
||||
String(row[field.dynamicRelation.parentKey] ?? '')
|
||||
] ??
|
||||
contract.relationResources?.[key] ??
|
||||
'',
|
||||
wide:
|
||||
objectValue ||
|
||||
jsonValue ||
|
||||
/(address|terms|content|body|remark|reason|params|args)$/.test(key),
|
||||
},
|
||||
];
|
||||
@@ -246,7 +273,10 @@ const entries = computed<DetailEntry[]>(() => {
|
||||
const collections = computed(() =>
|
||||
Object.entries(props.detail)
|
||||
.filter(([, value]) => Array.isArray(value) && value.length > 0)
|
||||
.map(([key, value]) => {
|
||||
.flatMap(([key, value]) => {
|
||||
const contract = resourceDetailContract(props.definition.name)
|
||||
.collections?.[key];
|
||||
if (!contract) return [];
|
||||
const rows = value as ResourceRow[];
|
||||
const availableColumns = [
|
||||
...new Set(
|
||||
@@ -261,56 +291,15 @@ const collections = computed(() =>
|
||||
),
|
||||
),
|
||||
];
|
||||
const preferredColumns: Record<string, string[]> = {
|
||||
'gasorder_contract.products': [
|
||||
'bound_at',
|
||||
'product_name',
|
||||
'product_code',
|
||||
'product_type_name',
|
||||
'unit_price',
|
||||
'product_info_identity',
|
||||
'unbound_at',
|
||||
'unbind_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',
|
||||
],
|
||||
};
|
||||
const preferred =
|
||||
preferredColumns[`${props.definition.name}.${key}`] ?? [];
|
||||
const fixedColumns = ['assignments', 'items', 'statuses'].includes(key);
|
||||
const columns = (
|
||||
fixedColumns
|
||||
? preferred
|
||||
: [...new Set([...preferred, ...availableColumns])]
|
||||
)
|
||||
.filter((column) => availableColumns.includes(column))
|
||||
.slice(0, 8);
|
||||
return {
|
||||
const columns = contract.columns.filter((column) =>
|
||||
availableColumns.includes(column),
|
||||
);
|
||||
return [{
|
||||
key,
|
||||
title: resourceFieldLabel(props.definition, key),
|
||||
rows,
|
||||
columns,
|
||||
};
|
||||
}];
|
||||
}),
|
||||
);
|
||||
</script>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from '@/api/resource-staff-relation';
|
||||
import type { ResourceField } from '@/api/resources';
|
||||
import type { ResourceRow } from '@/api/resource-page-rules';
|
||||
import { fieldRelationResource } from '@/api/resource-display';
|
||||
import { createRelationRequestVersionGuard } from './resource-relation-linkage-policy';
|
||||
|
||||
export type ResourceRelationLoadOptions = {
|
||||
@@ -133,13 +134,14 @@ export function useResourceRelations(
|
||||
/** 补载表单或详情中已经保存的关系值,避免分页和筛选导致回显裸标识。 */
|
||||
async function ensureValues(fields: ResourceField[], values: ResourceRow) {
|
||||
const requests = fields.flatMap((field) => {
|
||||
if (!field.relation) return [];
|
||||
const resource = fieldRelationResource(field, values);
|
||||
if (!resource) return [];
|
||||
const value = values[field.key];
|
||||
const identities = Array.isArray(value) ? value : [value];
|
||||
return identities
|
||||
.map((identity) => String(identity ?? ''))
|
||||
.filter(Boolean)
|
||||
.map((identity) => ensure(field.relation as string, identity));
|
||||
.map((identity) => ensure(resource, identity));
|
||||
});
|
||||
await Promise.all(requests);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* 功能描述:标准资源列表工具栏、分页和辅助文本布局。版本:v1.0.0。 */
|
||||
/* 功能描述:标准资源列表工具栏、响应式表格、分页和辅助文本布局。版本:v1.1.0。 */
|
||||
.filters { flex: 1 1 420px; }
|
||||
.list-toolbar { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 12px 24px; margin-bottom: 16px; }
|
||||
.list-actions { margin-left: auto; }
|
||||
.workflow-alert { margin-bottom: 16px; }
|
||||
.pagination { display: flex; justify-content: flex-end; margin-top: 16px; }
|
||||
.muted-text { color: var(--color-text-3); }
|
||||
.resource-table-shell { width: 100%; min-width: 0; max-width: 100%; overflow-x: auto; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
功能:展示标准资源列表,并将新建、详情和编辑入口导航到独立页面。
|
||||
版本:v2.2.1
|
||||
版本:v2.3.0
|
||||
-->
|
||||
<template>
|
||||
<a-card :title="listTitle" :bordered="false">
|
||||
@@ -32,12 +32,9 @@
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<div class="resource-table-shell">
|
||||
<a-table :data="list" :loading="loading" :pagination="false" row-key="identity">
|
||||
<template #columns>
|
||||
<a-table-column title="ID" data-index="id" :width="80" />
|
||||
<a-table-column title="唯一标识" :width="150">
|
||||
<template #cell="{ record }"><IdentityText :value="String(record.identity)" /></template>
|
||||
</a-table-column>
|
||||
<a-table-column
|
||||
v-for="field in displayFields"
|
||||
:key="field.key"
|
||||
@@ -61,12 +58,16 @@
|
||||
:name="relationListName(field, record, relations.options)"
|
||||
:identity="identityFieldValue(field, record)"
|
||||
:identity-label="field.label"
|
||||
:clickable="Boolean(fieldRelationResource(field, record))"
|
||||
@open="openRelation(field, record)"
|
||||
/>
|
||||
<RelationNameText
|
||||
v-else-if="field.listDisplayIdentityCopy && identityFieldValue(field, record)"
|
||||
:name="displayListField(field, record)"
|
||||
:identity="identityFieldValue(field, record)"
|
||||
:identity-label="field.label"
|
||||
:clickable="Boolean(fieldRelationResource(field, record))"
|
||||
@open="openRelation(field, record)"
|
||||
/>
|
||||
<IdentityText
|
||||
v-else-if="field.type === 'identity' && !field.displayRelationLabel && identityFieldValue(field, record)"
|
||||
@@ -110,6 +111,9 @@
|
||||
</a-tag>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="系统唯一标识" :width="170">
|
||||
<template #cell="{ record }"><IdentityText :value="String(record.identity)" /></template>
|
||||
</a-table-column>
|
||||
<a-table-column title="操作" :width="definition.accountManagement ? 360 : 285" fixed="right">
|
||||
<template #cell="{ record }">
|
||||
<a-space>
|
||||
@@ -143,6 +147,7 @@
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
<a-pagination
|
||||
:total="total"
|
||||
@@ -182,6 +187,7 @@ import IdentityText from '@/components/IdentityText.vue';
|
||||
import {
|
||||
displayRawValue,
|
||||
displayResourceField,
|
||||
fieldRelationResource,
|
||||
recordStatusColor,
|
||||
recordStatusLabel,
|
||||
} from '@/api/resource-display';
|
||||
@@ -409,6 +415,24 @@ function openStatus(row: ResourceRow) {
|
||||
statusVisible.value = true;
|
||||
}
|
||||
|
||||
/** 打开列表关系字段对应的详情页;树形资源没有详情路由时保持只读展示。 */
|
||||
function openRelation(field: ResourceField, row: ResourceRow) {
|
||||
const resource = fieldRelationResource(field, row);
|
||||
if (!resource) return;
|
||||
const target = router
|
||||
.getRoutes()
|
||||
.find(
|
||||
(item) =>
|
||||
item.meta.resource === resource &&
|
||||
item.meta.recordMode === 'detail',
|
||||
);
|
||||
if (!target?.name) return;
|
||||
void router.push({
|
||||
name: target.name,
|
||||
params: { identity: identityFieldValue(field, row) },
|
||||
});
|
||||
}
|
||||
|
||||
async function saveStatus() {
|
||||
if (![1, 2].includes(Number(statusTarget.value))) {
|
||||
Message.warning('请选择目标状态');
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
<!-- 功能描述:列表以关系名称为主展示,并提供完整唯一标识的悬停查看与复制。版本:v1.0.0。 -->
|
||||
<!-- 功能描述:以关系名称为主展示,支持进入关联详情并复制完整唯一标识。版本:v1.1.0。 -->
|
||||
<template>
|
||||
<div class="relation-name-text">
|
||||
<a-tooltip :content="`${identityLabel}:${identity}`">
|
||||
<span class="relation-name">{{ displayName }}</span>
|
||||
<button
|
||||
v-if="clickable"
|
||||
class="relation-name relation-link"
|
||||
type="button"
|
||||
@click.stop="emit('open')"
|
||||
>{{ displayName }}</button>
|
||||
<span v-else class="relation-name">{{ displayName }}</span>
|
||||
</a-tooltip>
|
||||
<a-tooltip :content="`复制${identityLabel}`">
|
||||
<button
|
||||
@@ -26,7 +32,9 @@ const props = defineProps<{
|
||||
name: string;
|
||||
identity: string;
|
||||
identityLabel?: string;
|
||||
clickable?: boolean;
|
||||
}>();
|
||||
const emit = defineEmits<{ open: [] }>();
|
||||
const displayName = computed(() => props.name || props.identity);
|
||||
|
||||
/** 复制当前关联记录的完整唯一标识,并给出明确操作反馈。 */
|
||||
@@ -56,6 +64,16 @@ async function copyIdentity() {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.relation-link {
|
||||
padding: 0;
|
||||
color: rgb(var(--primary-6));
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
display: inline-flex;
|
||||
flex: 0 0 auto;
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
<a-tree :data="tree" :loading="loading" :field-names="{ key: 'identity', title: 'name', children: 'children' }">
|
||||
<template #title="node">
|
||||
<a-space>
|
||||
{{ node.title }}
|
||||
<span>{{ node.title }}</span>
|
||||
<span v-if="node.group_code || node.path" class="tree-business-key">
|
||||
{{ node.group_code || node.path }}
|
||||
</span>
|
||||
<IdentityText :value="String(node.identity)" />
|
||||
<a-button v-if="canEdit" size="mini" @click.stop="openEdit(node)">编辑</a-button>
|
||||
<a-button v-if="canChangeStatus" size="mini" @click.stop="confirmStatus(node)">{{ node.status === 1 ? '停用' : '启用' }}</a-button>
|
||||
<a-button v-if="canArchive" size="mini" status="danger" @click.stop="confirmArchive(node)">归档</a-button>
|
||||
@@ -40,6 +44,7 @@ import { resourceApi } from '@/api/resource';
|
||||
import { buildResourcePayload, isMissingField } from '@/api/resource-form';
|
||||
import type { ResourceUiDefinition } from '@/api/resources';
|
||||
import { useUserStore } from '@/store';
|
||||
import IdentityText from '@/components/IdentityText.vue';
|
||||
|
||||
type Node = Record<string, unknown> & {
|
||||
identity: string;
|
||||
@@ -185,3 +190,10 @@ async function load() {
|
||||
|
||||
onMounted(load);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tree-business-key {
|
||||
color: var(--color-text-3);
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,17 +1,82 @@
|
||||
/**
|
||||
* 功能描述:集中处理标准资源列表字段的关系名称、唯一标识和列宽展示。
|
||||
* 版本:v1.0.0
|
||||
* 版本:v1.1.0
|
||||
*/
|
||||
import { optionLabel } from '@/api/resource-display';
|
||||
import { fieldRelationResource, optionLabel } from '@/api/resource-display';
|
||||
import type { ResourceRow } from '@/api/resource-page-rules';
|
||||
import type { ResourceField, ResourceUiDefinition } from '@/api/resources';
|
||||
import { isProtectedListAvatarField } from './protected-list-avatar-loader';
|
||||
|
||||
const resourceListPriorities: Record<string, string[]> = {
|
||||
gasorder_basic: [
|
||||
'order_no', 'request_no', 'order_status', 'gasorder_contract_identity',
|
||||
'creator_identity', 'user_account_identity',
|
||||
],
|
||||
gasorder_contract: [
|
||||
'contract_no', 'contract_status', 'title', 'user_account_identity',
|
||||
'gas_basic_identity', 'delivery_basic_identity',
|
||||
],
|
||||
payment_order: [
|
||||
'payment_no', 'payment_status', 'business_type', 'business_identity',
|
||||
'amount', 'channel',
|
||||
],
|
||||
wallet_record: [
|
||||
'record_no', 'direction', 'trade_type', 'amount',
|
||||
'balance_after', 'operator_name',
|
||||
],
|
||||
ec_order: [
|
||||
'order_no', 'order_status', 'user_account_identity', 'payable_amount',
|
||||
'logistics_status', 'paid_at',
|
||||
],
|
||||
gasorder_item: [
|
||||
'product_name', 'product_code', 'product_type_name', 'unit_price',
|
||||
'gasorder_basic_identity', 'active',
|
||||
],
|
||||
gasorder_assign: [
|
||||
'assigned_at', 'gasorder_basic_identity', 'gas_basic_identity',
|
||||
'delivery_basic_identity', 'staff_account_identity', 'assigner_name',
|
||||
],
|
||||
gasorder_status: [
|
||||
'occurred_at', 'gasorder_basic_identity', 'from_status', 'to_status',
|
||||
'operator_name', 'reason',
|
||||
],
|
||||
gasorder_contract_revision: [
|
||||
'occurred_at', 'gasorder_contract_identity', 'action', 'contract_status',
|
||||
'operator_name', 'reason',
|
||||
],
|
||||
};
|
||||
|
||||
const generatedListFields: Record<string, ResourceField> = {
|
||||
// 订单号是业务单号,必须完整展示,不得复用只显示末 12 位的系统标识组件。
|
||||
order_no: { key: 'order_no', label: '订单号' },
|
||||
order_status: { key: 'order_status', label: '订单状态' },
|
||||
contract_status: { key: 'contract_status', label: '合同状态' },
|
||||
product_name: { key: 'product_name', label: '智能气阀名称' },
|
||||
user_account_identity: {
|
||||
key: 'user_account_identity', label: '用户账户', type: 'identity',
|
||||
relation: '/user_account', listRelationNameOnly: true,
|
||||
displayRelationLabel: true, showIdentityCopy: true,
|
||||
},
|
||||
};
|
||||
|
||||
/** 返回标准列表实际渲染的业务字段,搜索提示与表格共同复用该规则。 */
|
||||
export function resourceListDisplayFields(
|
||||
definition: ResourceUiDefinition,
|
||||
): ResourceField[] {
|
||||
return definition.fields
|
||||
const fieldsByKey = new Map(
|
||||
[...Object.values(generatedListFields), ...definition.fields].map(
|
||||
(field) => [field.key, field],
|
||||
),
|
||||
);
|
||||
const priority = resourceListPriorities[definition.name] ?? [];
|
||||
const ordered = [
|
||||
...priority.flatMap((key) => {
|
||||
const field = fieldsByKey.get(key);
|
||||
return field ? [field] : [];
|
||||
}),
|
||||
...definition.fields.filter((field) => !priority.includes(field.key)),
|
||||
];
|
||||
return ordered
|
||||
.filter((field) => field.key !== 'identity' && field.type !== 'password')
|
||||
.filter(
|
||||
(field) =>
|
||||
@@ -30,17 +95,18 @@ export function identityFieldValue(field: ResourceField, row: ResourceRow) {
|
||||
return String(row[field.key] ?? row[`${field.key}_masked`] ?? '');
|
||||
}
|
||||
|
||||
/** 获取列表关系的可读名称,关系未加载时降级为完整唯一标识。 */
|
||||
/** 获取列表关系的可读名称;加载失败时由唯一标识复制入口保留排障能力。 */
|
||||
export function relationListName(
|
||||
field: ResourceField,
|
||||
row: ResourceRow,
|
||||
relationOptions: Record<string, ResourceRow[]>,
|
||||
) {
|
||||
const identity = identityFieldValue(field, row);
|
||||
const match = (relationOptions[field.relation ?? ''] ?? []).find(
|
||||
const resource = fieldRelationResource(field, row);
|
||||
const match = (relationOptions[resource] ?? []).find(
|
||||
(option) => String(option.identity) === identity,
|
||||
);
|
||||
return match ? optionLabel(match) : identity;
|
||||
return match ? optionLabel(match) : '名称加载失败';
|
||||
}
|
||||
|
||||
/** 按字段类型返回标准列表列宽。 */
|
||||
|
||||
Reference in New Issue
Block a user