功能:完善用户服务关系展示与组织联动
统一平台、气站和配送点后台的服务关系业务名称展示。 新增气站、配送点、服务人员三级联动及暂未分配语义。 后端补充组织归属、人员角色和启用状态的严格校验。 补充前后端测试、项目文档、操作日志及本机日志忽略规则。
This commit is contained in:
@@ -21,9 +21,15 @@ export type ResourceFieldType =
|
||||
export type ResourceField = {
|
||||
key: string;
|
||||
label: string;
|
||||
listLabel?: string;
|
||||
type?: ResourceFieldType;
|
||||
required?: boolean;
|
||||
relation?: string;
|
||||
listRelationNameOnly?: boolean;
|
||||
emptyText?: string;
|
||||
placeholder?: string;
|
||||
showIdentityCopy?: boolean;
|
||||
readonlyRelationText?: boolean;
|
||||
options?: Array<{ label: string; value: string | number }>;
|
||||
defaultValue?: string | number | boolean;
|
||||
readonly?: boolean;
|
||||
@@ -271,8 +277,13 @@ function f(key: string, options: Partial<ResourceField> = {}): ResourceField {
|
||||
return { key, label, type, ...options };
|
||||
}
|
||||
|
||||
function relation(key: string, resource: string, required = false): ResourceField {
|
||||
return f(key, { type: 'identity', relation: resource, required });
|
||||
function relation(
|
||||
key: string,
|
||||
resource: string,
|
||||
required = false,
|
||||
options: Partial<ResourceField> = {},
|
||||
): ResourceField {
|
||||
return f(key, { ...options, type: 'identity', relation: resource, required });
|
||||
}
|
||||
|
||||
/** 创建固定管理员角色字段,确保页面仅展示后端支持的角色。 */
|
||||
@@ -332,7 +343,12 @@ const platformResources: ResourceUiDefinition[] = [
|
||||
define('staff_credential', '工作人员资质', 'writable', [relation('staff_account_identity', '/staff_account', true), f('credential_type', { required: true }), f('credential_no'), f('expired_at')]),
|
||||
{ ...define('user_account', '用户账户', 'writable', [f('username', { required: true }), f('password', { required: true }), f('name', { required: true }), f('phone'), f('avatar'), f('real_name')]), walletOwnerType: 'user' },
|
||||
define('user_address', '用户地址', 'writable', [relation('user_account_identity', '/user_account', true), f('address', { required: true }), f('longitude'), f('latitude'), f('is_default')]),
|
||||
define('user_service_relation', '用户服务关系', 'writable', [relation('user_account_identity', '/user_account', true), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('staff_account_identity', '/staff_account')]),
|
||||
define('user_service_relation', '用户服务关系', 'writable', [
|
||||
relation('user_account_identity', '/user_account', true, { label: '用户账户', listLabel: '用户账户', listRelationNameOnly: true, showIdentityCopy: true, readonlyRelationText: true }),
|
||||
relation('gas_basic_identity', '/gas_basic', false, { label: '所属气站', listLabel: '所属气站', listRelationNameOnly: true, emptyText: '暂未分配', placeholder: '请选择所属气站', showIdentityCopy: true }),
|
||||
relation('delivery_basic_identity', '/delivery_basic', false, { label: '所属配送点', listLabel: '所属配送点', listRelationNameOnly: true, emptyText: '暂未分配', placeholder: '请选择所属配送点', showIdentityCopy: true }),
|
||||
relation('staff_account_identity', '/staff_account', false, { label: '服务人员', listLabel: '服务人员', listRelationNameOnly: true, emptyText: '暂未分配', placeholder: '请选择服务人员', showIdentityCopy: true }),
|
||||
]),
|
||||
|
||||
define('product_type', '智能气阀类型', 'editable', [f('code', { required: true }), f('name', { required: true })]),
|
||||
define('product_warehouse', '智能气阀库房', 'editable', [f('code', { required: true }), f('name', { required: true }), f('address'), f('manager'), f('phone')]),
|
||||
|
||||
@@ -34,9 +34,10 @@
|
||||
<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" :title="field.label" :width="columnWidth(field)" ellipsis tooltip>
|
||||
<a-table-column v-for="field in displayFields" :key="field.key" :title="field.listLabel ?? field.label" :width="columnWidth(field)" :ellipsis="!field.listRelationNameOnly" :tooltip="!field.listRelationNameOnly">
|
||||
<template #cell="{ record }">
|
||||
<IdentityText v-if="field.type === 'identity' && identityFieldValue(field, record)" :value="identityFieldValue(field, record)" />
|
||||
<RelationNameText v-if="field.listRelationNameOnly && identityFieldValue(field, record)" :name="relationListName(field, record)" :identity="identityFieldValue(field, record)" :identity-label="field.label" />
|
||||
<IdentityText v-else-if="field.type === 'identity' && identityFieldValue(field, record)" :value="identityFieldValue(field, record)" />
|
||||
<template v-else>{{ displayFieldValue(field, record) }}</template>
|
||||
</template>
|
||||
</a-table-column>
|
||||
@@ -269,6 +270,7 @@ import type {
|
||||
} from '@/api/resources';
|
||||
import { useUserStore } from '@/store';
|
||||
import IdentityText from '@/components/IdentityText.vue';
|
||||
import RelationNameText from './RelationNameText.vue';
|
||||
|
||||
type Row = Record<string, unknown>;
|
||||
const props = defineProps<{ definition: ResourceUiDefinition }>();
|
||||
@@ -1153,6 +1155,15 @@ function identityFieldValue(field: ResourceField, row: Row) {
|
||||
return String(row[field.key] ?? row[`${field.key}_masked`] ?? '');
|
||||
}
|
||||
|
||||
/** 获取关联记录的业务名称;关系尚未加载时降级显示完整唯一标识。 */
|
||||
function relationListName(field: ResourceField, row: Row) {
|
||||
const identity = identityFieldValue(field, row);
|
||||
const match = (relationOptions[field.relation ?? ''] ?? []).find(
|
||||
(option) => String(option.identity) === identity,
|
||||
);
|
||||
return match ? optionLabel(match) : identity;
|
||||
}
|
||||
|
||||
function columnWidth(field: ResourceField) {
|
||||
if (field.key === 'identity') return 280;
|
||||
if (field.type === 'datetime' || field.type === 'date') return 180;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<!-- 功能描述:以业务名称展示关联记录,并保留唯一标识的查看与复制能力。版本:v1.0.0。 -->
|
||||
<template>
|
||||
<div class="relation-name-text">
|
||||
<a-tooltip :content="`${identityLabel}:${identity}`">
|
||||
<span class="relation-name">{{ displayName }}</span>
|
||||
</a-tooltip>
|
||||
<a-tooltip :content="`复制${identityLabel}`">
|
||||
<button class="copy-button" type="button" :aria-label="`复制${identityLabel} ${identity}`" @click.stop="copyIdentity">
|
||||
<icon-copy />
|
||||
</button>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { IconCopy } from '@arco-design/web-vue/es/icon';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{ name: string; identity: string; identityLabel: string }>();
|
||||
const displayName = computed(() => props.name || props.identity);
|
||||
|
||||
/** 复制完整唯一标识,并给出明确的操作反馈。 */
|
||||
async function copyIdentity() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(props.identity);
|
||||
Message.success(`${props.identityLabel}已复制`);
|
||||
} catch {
|
||||
Message.error('复制失败,请从悬浮提示中复制');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.relation-name-text { display: flex; gap: 6px; align-items: center; min-width: 0; }
|
||||
.relation-name { min-width: 0; overflow: hidden; color: var(--color-text-1); text-overflow: ellipsis; white-space: nowrap; }
|
||||
.copy-button { display: inline-flex; flex: 0 0 auto; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; color: rgb(var(--primary-6)); font-size: 14px; background: transparent; border: 0; border-radius: 4px; cursor: pointer; }
|
||||
.copy-button:hover, .copy-button:focus-visible { background: var(--color-fill-2); outline: none; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user