Files
platforms/frontend/platform_admin/src/views/resource/use-resource-relation-linkage.ts

281 lines
9.7 KiB
TypeScript
Raw Normal View History

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,
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;
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);
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,
),
};
}
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() {
const childField = active?.childField;
const resource = childField?.relation;
2026-08-11 14:28:13 +08:00
if (!resource) return;
relations.cancelSearch(resource);
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) {
await relations.preload(
fields.filter((field) => field.key !== organizationStaffField?.key),
);
await relations.ensureValues(fields, form);
await reloadStaffOptions();
2026-08-11 14:28:13 +08:00
initialized = true;
return;
}
await relations.preload(
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);
}
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();
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]);
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 (
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;
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);
await synchronizeStaff();
2026-08-11 14:28:13 +08:00
} else if (field.key === active.childField.key) {
await handleChildChange(identity);
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) {
relations.searchField(field, keyword, childRequest());
2026-08-11 14:28:13 +08:00
return;
}
if (field.key === organizationStaffField?.key) {
relations.searchField(field, keyword, staffRequest());
return;
}
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;
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);
const linkedMessage = linkedRelationValidationMessage(
2026-08-11 14:28:13 +08:00
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),
);
2026-08-11 14:28:13 +08:00
}
return { preload, change, search, validationMessage };
}