@@ -1,6 +1,6 @@
/**
* 功能:协调资源表单中显式启用的父子关联下拉、搜索和保存前校验。
* 版本: v1.0 .0
* 功能:协调资源表单中显式启用的多组 父子关联下拉、搜索和保存前校验。
* 版本: v1.1 .0
*/
import { Message } from '@arco-design/web-vue' ;
import type { ResourceField } from '@/api/resources' ;
@@ -24,7 +24,7 @@ export function useResourceRelationLinkage(
form : Record < string , any > ,
relations : ResourceRelations ,
) {
let active : ActiveLinkage | undefined ;
let active : ActiveLinkage [ ] = [ ] ;
let organizationStaffField : ResourceField | undefined ;
let initialized = false ;
@@ -34,21 +34,19 @@ export function useResourceRelationLinkage(
) ;
}
/** 收集页面内全部显式联动,允许一个父字段同时驱动多个业务子项。 */
function configure ( fields : ResourceField [ ] ) {
const childField = fields . find ( ( field ) = > field . relationLinkage ) ;
active = fields
. filter ( ( field ) = > field . relationLinkage )
. map ( ( childField ) = > ( {
childField ,
parentField : fields.find (
( field ) = > field . key === childField . relationLinkage ? . parentKey ,
) ,
} ) ) ;
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() {
@@ -94,62 +92,63 @@ export function useResourceRelationLinkage(
await reloadStaffOptions ( ) ;
}
function childRequest() {
if ( ! active ? . childField . relationLinkage ) return { } ;
const linkage = active . childField . relationLinkage ;
function childRequest( linkageItem : ActiveLinkage ) {
const linkage = linkageItem . childField . relationLinkage ;
if ( ! linkage ) return { } ;
const parentIdentity = relationIdentity ( form [ linkage . parentKey ] ) ;
const childIdentity = relationIdentity ( form [ active . childField . key ] ) ;
const rawChildValue = form [ linkageItem . childField . key ] ;
const childIdentities = ( Array . isArray ( rawChildValue )
? rawChildValue
: [ rawChildValue ]
)
. map ( relationIdentity )
. filter ( Boolean ) ;
return {
filters : linkedRelationFilters ( parentIdentity , linkage . filterKey ) ,
preserveIdentities : childIdentity ? [ childIdentity ] : [ ] ,
preserveIdentities : childIdentities ,
} ;
}
async function reloadChildOptions() {
const childField = active ? . childField ;
const resource = childField ? . relation ;
if ( ! resource ) return ;
/** 加载一组子选项,并在配置要求时自动选中默认业务记录。 */
async function reloadChildOptions ( linkageItem : ActiveLinkage ) {
const childField = linkageItem . childField ;
const linkage = childField . relationLinkage ;
const resource = childField . relation ;
if ( ! linkage || ! resource ) return ;
const parentIdentity = relationIdentity ( form [ linkage . parentKey ] ) ;
if ( linkage . requiresParent && ! parentIdentity ) {
relations . clear ( resource ) ;
return ;
}
relations . cancelSearch ( resource ) ;
await relations . loadField ( childField , '' , childRequest ( ) ) ;
await relations . loadField ( childField , '' , childRequest ( linkageItem ) ) ;
if (
! relationIdentity ( form [ childField . key ] ) &&
childField . relationAutoSelectKey
) {
const defaultOption = ( relations . options [ resource ] ? ? [ ] ) . find (
( option ) = > Boolean ( option [ childField . relationAutoSelectKey as string ] ) ,
) ;
if ( defaultOption ) {
form [ childField . key ] = relationIdentity ( defaultOption . identity ) ;
}
}
}
/** 预加载普通关联项,并单独 按父级条件加载启用联动的子选项。 */
/** 预加载普通关联项,并分别 按父级条件加载所有 启用联动的子选项。 */
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 ;
}
const linkedChildKeys = new Set ( active . map ( ( item ) = > item . childField . key ) ) ;
await relations . preload (
fields . filter (
( field ) = >
field . key !== active ? . childF ield. key &&
! linkedChildKeys . has ( f ield. key ) &&
field . key !== organizationStaffField ? . key ,
) ,
) ;
const linkage = active . childF ield. 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 relations . ensureValues ( f ields , form ) ;
await Promise . all ( active . map ( ( item ) = > reloadChildOptions ( item ) ) ) ;
await reloadStaffOptions ( ) ;
initialized = true ;
@@ -157,58 +156,61 @@ export function useResourceRelationLinkage(
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 ] ) ;
async function handleParentChange (
linkageItem : ActiveLinkage ,
parentIdentity : string ,
) {
const childField = linkageItem . childField ;
const linkage = childField . relationLinkage ;
if ( ! linkage || ! childField . relation ) return ;
const childIdentity = relationIdentity ( form [ childField . key ] ) ;
if ( linkage . filterOnly ) relations . clear ( childField . relation ) ;
if ( linkage . filterOnly && childIdentity ) {
form [ active . childField. key ] = '' ;
Message . info ( '合同已变更,请重新选择该合同可绑定的气瓶' ) ;
form [ childField . key] = childField . type === 'identity-list' ? [ ] : '' ;
Message . info (
linkage . parentChangeMessage ? ? '上级数据已变更,请重新选择关联数据' ,
) ;
}
if ( childIdentity && ! linkage . filterOnly ) {
const option =
findOption ( active . childField. relation , childIdentity ) ? ?
( await relations . ensure ( active . childField. relation , childIdentity ) ) ;
findOption ( childField . relation , childIdentity ) ? ?
( await relations . ensure ( childField . relation , childIdentity ) ) ;
if (
relationIdentity ( form [ linkage . parentKey ] ) !== parentIdentity ||
relationIdentity ( form [ active . childField. key ] ) !== childIdentity
relationIdentity ( form [ childField . key ] ) !== childIdentity
) {
return ;
}
if (
shouldClearLinkedOption ( parentIdentity , option , linkage . optionParentKey )
) {
form [ active . childField. key ] = '' ;
Message . info ( '所属气站已变更,请重新选择配送点' ) ;
form [ childField . key ] = '' ;
Message . info (
linkage . parentChangeMessage ? ? '所属气站已变更,请重新选择配送点' ,
) ;
}
}
await reloadChildOptions ( ) ;
await reloadChildOptions ( linkageItem ) ;
}
async function handleChildChange ( childIdentity : string ) {
if ( ! active ? . childField . relationLinkage || ! active . childField . relation )
async function handleChildChange (
linkageItem : ActiveLinkage ,
childIdentity : string ,
) {
const childField = linkageItem . childField ;
const linkage = childField . relationLinkage ;
if ( ! linkage || ! childField . relation || ! childIdentity || linkage . filterOnly )
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 ;
}
findOption ( childField . relation , childIdentity ) ? ?
( await relations . ensure ( childField . relation , childIdentity ) ) ;
if ( ! option || relationIdentity ( form [ 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 ( nextParent && linkageItem . parentField ? . relation ) {
await relations . ensure ( linkageItem . parentField . relation , nextParent ) ;
if ( relationIdentity ( form [ childField . key ] ) !== childIdentity ) return ;
}
if ( currentParent !== nextParent ) {
form [ linkage . parentKey ] = nextParent ;
@@ -217,29 +219,41 @@ export function useResourceRelationLinkage(
}
}
}
await reloadChildOptions ( ) ;
await reloadChildOptions ( linkageItem ) ;
}
/** 响应用户主动修改关联字段;初始化回显不会触发自动清理。 */
async function change ( field : ResourceField , value : unknown ) {
if ( ! initialized || ! active ? . childField . relationLinkage ) return ;
if ( ! initialized ) 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 handleChild Change ( identity ) ;
await synchronizeStaff ( ) ;
} else if ( f ield. key === organizationStaffF ield? . key ) {
const parentLinkages = active . filter (
( item ) = > item . childField . relationLinkage ? . parentKey === field . key ,
) ;
await Promise . all (
parentLinkages . map ( ( item ) = > handleParent Change ( item , identity) ) ,
) ;
const childLinkage = active . find ( ( item ) = > item . childF ield. key === f ield. key ) ;
if ( childLinkage ) await handleChildChange ( childLinkage , identity ) ;
if (
field . key === 'gas_basic_identity' ||
field . key === 'delivery_basic_identity' ||
field . key === organizationStaffField ? . key
) {
await synchronizeStaff ( ) ;
}
}
/** 搜索联动子项时始终保留父级过滤和当前选项。 */
/** 搜索联动子项时始终保留对应 父级过滤和当前选项。 */
function search ( field : ResourceField , keyword : string ) {
if ( ! field . relation ) return ;
if ( fiel d . key === active ? . childField . key ) {
relations . searchField ( field , keyword , childRequest ( ) ) ;
const linkageItem = active . fin d ( ( item ) = > item . childField . key === field . key ) ;
if ( linkageItem ) {
const linkage = linkageItem . childField . relationLinkage ;
if ( linkage ? . requiresParent && ! relationIdentity ( form [ linkage . parentKey ] ) ) {
relations . clear ( field . relation ) ;
return ;
}
relations . searchField ( field , keyword , childRequest ( linkageItem ) ) ;
return ;
}
if ( field . key === organizationStaffField ? . key ) {
@@ -251,30 +265,52 @@ export function useResourceRelationLinkage(
/** 返回保存前的父子关系错误,空字符串代表当前组合可提交。 */
function validationMessage() {
i f ( ! 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 = f indOption ( active . childField . relation , childIdentity ) ;
const linkedMessage = linkedRelationValidationMessage (
parent Identity,
childIdentity ,
option ,
linkage . optionParentKey ,
) ;
if ( linkedMessage ) return linkedMessage ;
for ( const linkageItem of active ) {
const childField = linkageItem . childField ;
const linkage = childField . relationLinkage ;
if ( ! linkage || ! childField . relation || linkage . filterOnly ) continue ;
const parentIdentity = relationIdentity ( form [ linkage . parentKey ] ) ;
const childIdentity = relationIdentity ( form [ childField . key ] ) ;
const linkedMessage = l inkedRelationValidationMessage (
parentIdentity ,
child Identity ,
findOption ( childField . relation , childIdentity ) ,
linkage . optionParentKey ,
) ;
if ( linkedMessage ) return linkedMessage ;
}
const staffField = organizationStaffField ;
if ( ! staffField ? . relation ) return '' ;
const staffIdentity = relationIdentity ( form [ staffField . key ] ) ;
return staffOrganizationValidationMessage (
parentI dentity,
childI dentity,
relationIdentity ( form . gas_basic_i dentity) ,
relationIdentity ( form . delivery_basic_i dentity) ,
staffIdentity ,
findOption ( staffField . relation , staffIdentity ) ,
) ;
}
return { preload , change , search , validationMessage } ;
/** 必填联动候选为空时返回业务引导,用于阻止提交无效表单。 */
function unavailableMessage() {
for ( const linkageItem of active ) {
const field = linkageItem . childField ;
const linkage = field . relationLinkage ;
if (
! field . required ||
! field . relation ||
! field . relationEmptyText ||
! linkage ? . requiresParent ||
! relationIdentity ( form [ linkage . parentKey ] ) ||
relations . loading [ field . relation ]
) {
continue ;
}
if ( ( relations . options [ field . relation ] ? ? [ ] ) . length === 0 ) {
return field . relationEmptyText ;
}
}
return '' ;
}
return { preload , change , search , validationMessage , unavailableMessage } ;
}