feat: 增加工作人员组织联动

This commit is contained in:
czl231
2026-08-11 14:28:13 +08:00
parent 4d79a4917e
commit 4b1f045806
11 changed files with 626 additions and 22 deletions

View File

@@ -0,0 +1,195 @@
/**
* 功能:协调资源表单中显式启用的父子关联下拉、搜索和保存前校验。
* 版本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 };
}