Files
platforms/frontend/platform_admin/src/views/resource/use-resource-relation-linkage.ts
czl231 6bcde94388 功能:完善用户服务关系展示与组织联动
统一平台、气站和配送点后台的服务关系业务名称展示。
新增气站、配送点、服务人员三级联动及暂未分配语义。
后端补充组织归属、人员角色和启用状态的严格校验。
补充前后端测试、项目文档、操作日志及本机日志忽略规则。
2026-08-12 00:37:58 +08:00

275 lines
9.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 功能:协调资源表单中显式启用的父子关联下拉、搜索和保存前校验。
* 版本v1.0.0
*/
import { Message } from '@arco-design/web-vue';
import type { ResourceField } from '@/api/resources';
import {
linkedRelationFilters,
linkedRelationValidationMessage,
optionParentIdentity,
relationIdentity,
shouldClearLinkedOption,
staffOrganizationFilters,
staffOrganizationValidationMessage,
} from './resource-relation-linkage-policy';
import type { ResourceRelations } from './use-resource-relations';
type ActiveLinkage = {
childField: ResourceField;
parentField?: ResourceField;
};
export function useResourceRelationLinkage(
form: Record<string, any>,
relations: ResourceRelations,
) {
let active: ActiveLinkage | undefined;
let organizationStaffField: ResourceField | undefined;
let initialized = false;
function findOption(resource: string, identity: string) {
return (relations.options[resource] ?? []).find(
(row) => relationIdentity(row.identity) === identity,
);
}
function configure(fields: ResourceField[]) {
const childField = fields.find((field) => field.relationLinkage);
organizationStaffField = fields.find(
(field) => field.staffRelation?.organizationScoped,
);
if (!childField?.relationLinkage) {
active = undefined;
return;
}
active = {
childField,
parentField: fields.find(
(field) => field.key === childField.relationLinkage?.parentKey,
),
};
}
function staffRequest() {
const staffIdentity = relationIdentity(
organizationStaffField ? form[organizationStaffField.key] : '',
);
return {
filters: staffOrganizationFilters(
relationIdentity(form.gas_basic_identity),
relationIdentity(form.delivery_basic_identity),
),
preserveIdentities: staffIdentity ? [staffIdentity] : [],
};
}
async function reloadStaffOptions() {
const field = organizationStaffField;
if (!field?.relation) return;
relations.cancelSearch(field.relation);
await relations.loadField(field, '', staffRequest());
}
/** 组织变化后清理不再匹配的服务人员,并刷新精确范围选项。 */
async function synchronizeStaff() {
const field = organizationStaffField;
if (!field?.relation) return;
const staffIdentity = relationIdentity(form[field.key]);
if (staffIdentity) {
const option =
findOption(field.relation, staffIdentity) ??
(await relations.ensure(field.relation, staffIdentity));
const warning = staffOrganizationValidationMessage(
relationIdentity(form.gas_basic_identity),
relationIdentity(form.delivery_basic_identity),
staffIdentity,
option,
);
if (warning && relationIdentity(form[field.key]) === staffIdentity) {
form[field.key] = '';
Message.info(`${warning},已清空原服务人员`);
}
}
await reloadStaffOptions();
}
function childRequest() {
if (!active?.childField.relationLinkage) return {};
const linkage = active.childField.relationLinkage;
const parentIdentity = relationIdentity(form[linkage.parentKey]);
const childIdentity = relationIdentity(form[active.childField.key]);
return {
filters: linkedRelationFilters(parentIdentity, linkage.filterKey),
preserveIdentities: childIdentity ? [childIdentity] : [],
};
}
async function reloadChildOptions() {
const childField = active?.childField;
const resource = childField?.relation;
if (!resource) return;
relations.cancelSearch(resource);
await relations.loadField(childField, '', childRequest());
}
/** 预加载普通关联项,并单独按父级条件加载启用联动的子选项。 */
async function preload(fields: ResourceField[]) {
initialized = false;
configure(fields);
if (!active?.childField.relationLinkage || !active.childField.relation) {
await relations.preload(
fields.filter((field) => field.key !== organizationStaffField?.key),
);
await relations.ensureValues(fields, form);
await reloadStaffOptions();
initialized = true;
return;
}
await relations.preload(
fields.filter(
(field) =>
field.key !== active?.childField.key &&
field.key !== organizationStaffField?.key,
),
);
const linkage = active.childField.relationLinkage;
const parentIdentity = relationIdentity(form[linkage.parentKey]);
const childIdentity = relationIdentity(form[active.childField.key]);
if (parentIdentity && active.parentField?.relation) {
await relations.ensure(active.parentField.relation, parentIdentity);
}
if (childIdentity) {
await relations.ensure(active.childField.relation, childIdentity);
}
const staffIdentity = relationIdentity(
organizationStaffField ? form[organizationStaffField.key] : '',
);
if (staffIdentity && organizationStaffField?.relation) {
await relations.ensure(organizationStaffField.relation, staffIdentity);
}
await reloadChildOptions();
await reloadStaffOptions();
initialized = true;
const warning = validationMessage();
if (warning) Message.warning(`当前组织归属需要确认:${warning}`);
}
async function handleParentChange(parentIdentity: string) {
if (!active?.childField.relationLinkage || !active.childField.relation)
return;
const linkage = active.childField.relationLinkage;
const childIdentity = relationIdentity(form[active.childField.key]);
if (childIdentity) {
const option =
findOption(active.childField.relation, childIdentity) ??
(await relations.ensure(active.childField.relation, childIdentity));
if (
relationIdentity(form[linkage.parentKey]) !== parentIdentity ||
relationIdentity(form[active.childField.key]) !== childIdentity
) {
return;
}
if (
shouldClearLinkedOption(parentIdentity, option, linkage.optionParentKey)
) {
form[active.childField.key] = '';
Message.info('所属气站已变更,请重新选择配送点');
}
}
await reloadChildOptions();
}
async function handleChildChange(childIdentity: string) {
if (!active?.childField.relationLinkage || !active.childField.relation)
return;
const linkage = active.childField.relationLinkage;
if (!childIdentity) return;
const option =
findOption(active.childField.relation, childIdentity) ??
(await relations.ensure(active.childField.relation, childIdentity));
if (
!option ||
relationIdentity(form[active.childField.key]) !== childIdentity
) {
return;
}
if (linkage.backfillParent) {
const nextParent = optionParentIdentity(option, linkage.optionParentKey);
const currentParent = relationIdentity(form[linkage.parentKey]);
if (nextParent && active.parentField?.relation) {
await relations.ensure(active.parentField.relation, nextParent);
if (relationIdentity(form[active.childField.key]) !== childIdentity) {
return;
}
}
if (currentParent !== nextParent) {
form[linkage.parentKey] = nextParent;
if (!nextParent && currentParent) {
Message.info('平台主管配送点不隶属于气站,已清空所属气站');
}
}
}
await reloadChildOptions();
}
/** 响应用户主动修改关联字段;初始化回显不会触发自动清理。 */
async function change(field: ResourceField, value: unknown) {
if (!initialized || !active?.childField.relationLinkage) return;
const identity = relationIdentity(value);
if (field.key === active.childField.relationLinkage.parentKey) {
await handleParentChange(identity);
await synchronizeStaff();
} else if (field.key === active.childField.key) {
await handleChildChange(identity);
await synchronizeStaff();
} else if (field.key === organizationStaffField?.key) {
await synchronizeStaff();
}
}
/** 搜索联动子项时始终保留父级过滤和当前选项。 */
function search(field: ResourceField, keyword: string) {
if (!field.relation) return;
if (field.key === active?.childField.key) {
relations.searchField(field, keyword, childRequest());
return;
}
if (field.key === organizationStaffField?.key) {
relations.searchField(field, keyword, staffRequest());
return;
}
relations.searchField(field, keyword);
}
/** 返回保存前的父子关系错误,空字符串代表当前组合可提交。 */
function validationMessage() {
if (!active?.childField.relationLinkage || !active.childField.relation)
return '';
const linkage = active.childField.relationLinkage;
const parentIdentity = relationIdentity(form[linkage.parentKey]);
const childIdentity = relationIdentity(form[active.childField.key]);
const option = findOption(active.childField.relation, childIdentity);
const linkedMessage = linkedRelationValidationMessage(
parentIdentity,
childIdentity,
option,
linkage.optionParentKey,
);
if (linkedMessage) return linkedMessage;
const staffField = organizationStaffField;
if (!staffField?.relation) return '';
const staffIdentity = relationIdentity(form[staffField.key]);
return staffOrganizationValidationMessage(
parentIdentity,
childIdentity,
staffIdentity,
findOption(staffField.relation, staffIdentity),
);
}
return { preload, change, search, validationMessage };
}