/** * 功能:协调资源表单中显式启用的父子关联下拉、搜索和保存前校验。 * 版本: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, 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 (linkage.filterOnly && childIdentity) { form[active.childField.key] = ''; Message.info('合同已变更,请重新选择该合同可绑定的气瓶'); } if (childIdentity && !linkage.filterOnly) { 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; if (linkage.filterOnly) 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; if (linkage.filterOnly) return ''; 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 }; }