2026-08-11 14:28:13 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 功能:协调资源表单中显式启用的父子关联下拉、搜索和保存前校验。
|
|
|
|
|
|
* 版本:v1.0.0
|
|
|
|
|
|
*/
|
|
|
|
|
|
import { Message } from '@arco-design/web-vue';
|
|
|
|
|
|
import type { ResourceField } from '@/api/resources';
|
|
|
|
|
|
import {
|
|
|
|
|
|
linkedRelationFilters,
|
|
|
|
|
|
linkedRelationValidationMessage,
|
|
|
|
|
|
optionParentIdentity,
|
|
|
|
|
|
relationIdentity,
|
|
|
|
|
|
shouldClearLinkedOption,
|
2026-08-12 00:37:58 +08:00
|
|
|
|
staffOrganizationFilters,
|
|
|
|
|
|
staffOrganizationValidationMessage,
|
2026-08-11 14:28:13 +08:00
|
|
|
|
} 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;
|
2026-08-12 00:37:58 +08:00
|
|
|
|
let organizationStaffField: ResourceField | undefined;
|
2026-08-11 14:28:13 +08:00
|
|
|
|
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);
|
2026-08-12 00:37:58 +08:00
|
|
|
|
organizationStaffField = fields.find(
|
|
|
|
|
|
(field) => field.staffRelation?.organizationScoped,
|
|
|
|
|
|
);
|
2026-08-11 14:28:13 +08:00
|
|
|
|
if (!childField?.relationLinkage) {
|
|
|
|
|
|
active = undefined;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
active = {
|
|
|
|
|
|
childField,
|
|
|
|
|
|
parentField: fields.find(
|
|
|
|
|
|
(field) => field.key === childField.relationLinkage?.parentKey,
|
|
|
|
|
|
),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-12 00:37:58 +08:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-11 14:28:13 +08:00
|
|
|
|
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() {
|
2026-08-11 19:15:53 +08:00
|
|
|
|
const childField = active?.childField;
|
|
|
|
|
|
const resource = childField?.relation;
|
2026-08-11 14:28:13 +08:00
|
|
|
|
if (!resource) return;
|
|
|
|
|
|
relations.cancelSearch(resource);
|
2026-08-11 19:15:53 +08:00
|
|
|
|
await relations.loadField(childField, '', childRequest());
|
2026-08-11 14:28:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 预加载普通关联项,并单独按父级条件加载启用联动的子选项。 */
|
|
|
|
|
|
async function preload(fields: ResourceField[]) {
|
|
|
|
|
|
initialized = false;
|
|
|
|
|
|
configure(fields);
|
|
|
|
|
|
if (!active?.childField.relationLinkage || !active.childField.relation) {
|
2026-08-12 00:37:58 +08:00
|
|
|
|
await relations.preload(
|
|
|
|
|
|
fields.filter((field) => field.key !== organizationStaffField?.key),
|
|
|
|
|
|
);
|
2026-08-11 19:15:53 +08:00
|
|
|
|
await relations.ensureValues(fields, form);
|
2026-08-12 00:37:58 +08:00
|
|
|
|
await reloadStaffOptions();
|
2026-08-11 14:28:13 +08:00
|
|
|
|
initialized = true;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await relations.preload(
|
2026-08-12 00:37:58 +08:00
|
|
|
|
fields.filter(
|
|
|
|
|
|
(field) =>
|
|
|
|
|
|
field.key !== active?.childField.key &&
|
|
|
|
|
|
field.key !== organizationStaffField?.key,
|
|
|
|
|
|
),
|
2026-08-11 14:28:13 +08:00
|
|
|
|
);
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
2026-08-12 00:37:58 +08:00
|
|
|
|
const staffIdentity = relationIdentity(
|
|
|
|
|
|
organizationStaffField ? form[organizationStaffField.key] : '',
|
|
|
|
|
|
);
|
|
|
|
|
|
if (staffIdentity && organizationStaffField?.relation) {
|
|
|
|
|
|
await relations.ensure(organizationStaffField.relation, staffIdentity);
|
|
|
|
|
|
}
|
2026-08-11 14:28:13 +08:00
|
|
|
|
await reloadChildOptions();
|
2026-08-12 00:37:58 +08:00
|
|
|
|
await reloadStaffOptions();
|
2026-08-11 14:28:13 +08:00
|
|
|
|
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]);
|
2026-08-13 00:10:42 +08:00
|
|
|
|
if (linkage.filterOnly && childIdentity) {
|
|
|
|
|
|
form[active.childField.key] = '';
|
|
|
|
|
|
Message.info('合同已变更,请重新选择该合同可绑定的气瓶');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (childIdentity && !linkage.filterOnly) {
|
2026-08-11 14:28:13 +08:00
|
|
|
|
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 (
|
2026-08-11 19:15:53 +08:00
|
|
|
|
shouldClearLinkedOption(parentIdentity, option, linkage.optionParentKey)
|
2026-08-11 14:28:13 +08:00
|
|
|
|
) {
|
|
|
|
|
|
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;
|
2026-08-13 00:10:42 +08:00
|
|
|
|
if (linkage.filterOnly) return;
|
2026-08-11 14:28:13 +08:00
|
|
|
|
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);
|
2026-08-12 00:37:58 +08:00
|
|
|
|
await synchronizeStaff();
|
2026-08-11 14:28:13 +08:00
|
|
|
|
} else if (field.key === active.childField.key) {
|
|
|
|
|
|
await handleChildChange(identity);
|
2026-08-12 00:37:58 +08:00
|
|
|
|
await synchronizeStaff();
|
|
|
|
|
|
} else if (field.key === organizationStaffField?.key) {
|
|
|
|
|
|
await synchronizeStaff();
|
2026-08-11 14:28:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 搜索联动子项时始终保留父级过滤和当前选项。 */
|
|
|
|
|
|
function search(field: ResourceField, keyword: string) {
|
|
|
|
|
|
if (!field.relation) return;
|
|
|
|
|
|
if (field.key === active?.childField.key) {
|
2026-08-11 19:15:53 +08:00
|
|
|
|
relations.searchField(field, keyword, childRequest());
|
2026-08-11 14:28:13 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-08-12 00:37:58 +08:00
|
|
|
|
if (field.key === organizationStaffField?.key) {
|
|
|
|
|
|
relations.searchField(field, keyword, staffRequest());
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-08-11 19:15:53 +08:00
|
|
|
|
relations.searchField(field, keyword);
|
2026-08-11 14:28:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 返回保存前的父子关系错误,空字符串代表当前组合可提交。 */
|
|
|
|
|
|
function validationMessage() {
|
|
|
|
|
|
if (!active?.childField.relationLinkage || !active.childField.relation)
|
|
|
|
|
|
return '';
|
|
|
|
|
|
const linkage = active.childField.relationLinkage;
|
2026-08-13 00:10:42 +08:00
|
|
|
|
if (linkage.filterOnly) return '';
|
2026-08-11 14:28:13 +08:00
|
|
|
|
const parentIdentity = relationIdentity(form[linkage.parentKey]);
|
|
|
|
|
|
const childIdentity = relationIdentity(form[active.childField.key]);
|
|
|
|
|
|
const option = findOption(active.childField.relation, childIdentity);
|
2026-08-12 00:37:58 +08:00
|
|
|
|
const linkedMessage = linkedRelationValidationMessage(
|
2026-08-11 14:28:13 +08:00
|
|
|
|
parentIdentity,
|
|
|
|
|
|
childIdentity,
|
|
|
|
|
|
option,
|
|
|
|
|
|
linkage.optionParentKey,
|
|
|
|
|
|
);
|
2026-08-12 00:37:58 +08:00
|
|
|
|
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),
|
|
|
|
|
|
);
|
2026-08-11 14:28:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return { preload, change, search, validationMessage };
|
|
|
|
|
|
}
|