Files
platforms/frontend/platform_admin/src/views/resource/use-resource-relation-linkage.ts
czl231 9937e3a508 完善合同气瓶绑定约束与候选过滤
- 明确合同状态、气瓶归属、停用、报废和重复绑定失败原因
- 根据当前合同过滤可绑定气瓶,排除无效及重复候选项
- 从合同列表进入绑定页时锁定预填合同,防止切换上下文
- 切换通用新建页合同后清空旧气瓶并重新加载候选项
- 增加前后端回归检查并同步中文操作日志
2026-08-13 00:10:42 +08:00

281 lines
9.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 功能:协调资源表单中显式启用的父子关联下拉、搜索和保存前校验。
* 版本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<string, any>,
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 };
}