196 lines
6.6 KiB
TypeScript
196 lines
6.6 KiB
TypeScript
|
|
/**
|
|||
|
|
* 功能:协调资源表单中显式启用的父子关联下拉、搜索和保存前校验。
|
|||
|
|
* 版本:v1.0.0
|
|||
|
|
*/
|
|||
|
|
import { Message } from '@arco-design/web-vue';
|
|||
|
|
import type { ResourceField } from '@/api/resources';
|
|||
|
|
import {
|
|||
|
|
linkedRelationFilters,
|
|||
|
|
linkedRelationValidationMessage,
|
|||
|
|
optionParentIdentity,
|
|||
|
|
relationIdentity,
|
|||
|
|
shouldClearLinkedOption,
|
|||
|
|
} 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 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);
|
|||
|
|
if (!childField?.relationLinkage) {
|
|||
|
|
active = undefined;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
active = {
|
|||
|
|
childField,
|
|||
|
|
parentField: fields.find(
|
|||
|
|
(field) => field.key === childField.relationLinkage?.parentKey,
|
|||
|
|
),
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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 resource = active?.childField.relation;
|
|||
|
|
if (!resource) return;
|
|||
|
|
relations.cancelSearch(resource);
|
|||
|
|
await relations.load(resource, '', childRequest());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 预加载普通关联项,并单独按父级条件加载启用联动的子选项。 */
|
|||
|
|
async function preload(fields: ResourceField[]) {
|
|||
|
|
initialized = false;
|
|||
|
|
configure(fields);
|
|||
|
|
if (!active?.childField.relationLinkage || !active.childField.relation) {
|
|||
|
|
await relations.preload(fields);
|
|||
|
|
initialized = true;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
await relations.preload(
|
|||
|
|
fields.filter((field) => field.key !== active?.childField.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);
|
|||
|
|
}
|
|||
|
|
await reloadChildOptions();
|
|||
|
|
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);
|
|||
|
|
} else if (field.key === active.childField.key) {
|
|||
|
|
await handleChildChange(identity);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 搜索联动子项时始终保留父级过滤和当前选项。 */
|
|||
|
|
function search(field: ResourceField, keyword: string) {
|
|||
|
|
if (!field.relation) return;
|
|||
|
|
if (field.key === active?.childField.key) {
|
|||
|
|
relations.search(field.relation, keyword, childRequest());
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
relations.search(field.relation, 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);
|
|||
|
|
return linkedRelationValidationMessage(
|
|||
|
|
parentIdentity,
|
|||
|
|
childIdentity,
|
|||
|
|
option,
|
|||
|
|
linkage.optionParentKey,
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return { preload, change, search, validationMessage };
|
|||
|
|
}
|