import { type ActiveRecord, type ActiveRecords, MemoryListSearchCache, type RuntimeRelationField, UnsupportedOperationException } from '@oinone/kunlun-engine'; import { BooleanHelper, CastHelper, type OioTreeNode } from '@oinone/kunlun-shared'; import { SelectMode } from '@oinone/kunlun-vue-ui-common'; import { Widget } from '@oinone/kunlun-vue-widget'; import type { FormComplexFieldProps } from '../../basic'; import { type TreeNodeResponseBody, TreeService } from '../../service'; import type { TreeData, TreeNodeMetadata } from '../../typing'; import { AbstractTreeFieldWidget, type BackfillDataParameters } from '../tree-select'; import DefaultFieldTree from './DefaultFieldTree.vue'; export class FormTreeFieldWidget< Value = ActiveRecords, Field extends RuntimeRelationField = RuntimeRelationField, Props extends FormComplexFieldProps = FormComplexFieldProps > extends AbstractTreeFieldWidget { @Widget.Reactive() protected selectedValue: OioTreeNode | OioTreeNode[] | undefined; public initialize(props) { super.initialize(props); this.setComponent(DefaultFieldTree); return this; } @Widget.Reactive() protected get onlySelectedLeaf(): boolean { return this.getDsl().onlySelectedLeaf || false; } @Widget.Reactive() protected get checkable(): boolean { return true; } @Widget.Reactive() protected get checkStrictly(): boolean { return BooleanHelper.toBoolean(this.getDsl().checkStrictly) || false; } @Widget.Method() protected async onSelected(node: OioTreeNode, selected: boolean): Promise { if (this.selectMode === SelectMode.multiple) { await this.onChecked(node, selected); } else { throw new UnsupportedOperationException(); } } @Widget.Method() protected async onChecked(node: OioTreeNode, selected: boolean): Promise { const currentSelectedValue = this.getCurrentSelectedValue(); const targetSelectedValues = this.collectionSelectedNodes(node); this.onSelectedValueChange(currentSelectedValue, targetSelectedValues, selected); } @Widget.Method() protected nodeCheckedAll(node: OioTreeNode) { return BooleanHelper.toBoolean(this.getDsl().nodeCheckedAll) || false; } @Widget.Reactive() protected get nodeCheckedAllLabel(): string | undefined { return this.getDsl().nodeCheckedAllLabel; } @Widget.Reactive() protected get nodeUncheckedAllLabel(): string | undefined { return this.getDsl().nodeUncheckedAllLabel; } @Widget.Method() public onNodeCheckedAll(node: OioTreeNode, selected: boolean) { const currentSelectedValue = this.getCurrentSelectedValue(); const targetSelectedValues = this.collectionSelectedAllNodes(node); this.onSelectedValueChange(currentSelectedValue, targetSelectedValues, selected); } protected getCurrentSelectedValue(): OioTreeNode[] { const { selectedValue } = this; let currentSelectedValue: OioTreeNode[]; if (selectedValue) { if (Array.isArray(selectedValue)) { currentSelectedValue = selectedValue; } else { currentSelectedValue = [selectedValue]; } } else { currentSelectedValue = []; } return currentSelectedValue; } protected collectionSelectedNodes(selectedNode: OioTreeNode): OioTreeNode[] { if (this.checkStrictly) { return this.collectionSelectedAllNodes(selectedNode); } const model = this.field.references; if (this.collectionSelectedNodesFilter(selectedNode, model)) { return [selectedNode]; } return []; } protected collectionSelectedAllNodes(selectedNode: OioTreeNode): OioTreeNode[] { const model = this.field.references; const selectedNodes: OioTreeNode[] = []; this.$collectionSelectedNodes(selectedNodes, [selectedNode], (v) => this.collectionSelectedNodesFilter(v, model)); return selectedNodes; } protected collectionSelectedNodesFilter(node: OioTreeNode, model: string) { return node.value.metadata.model === model && !node.disabled; } protected $collectionSelectedNodes( selectedNodes: OioTreeNode[], targetSelectedNodes: OioTreeNode[], filter: (value: OioTreeNode) => boolean ): void { for (const selectedNode of targetSelectedNodes) { if (filter(selectedNode)) { selectedNodes.push(selectedNode); } this.$collectionSelectedNodes(selectedNodes, selectedNode.children, filter); } } protected onSelectedValueChange( currentSelectedValue: OioTreeNode[], targetSelectedValues: OioTreeNode[], selected: boolean ) { let isChange = false; if (selected) { const currentSelectedValueCache = new MemoryListSearchCache(currentSelectedValue, (v) => v.key); for (const targetSelectedValue of targetSelectedValues) { if (currentSelectedValueCache.get(targetSelectedValue.key) == null) { currentSelectedValue = [...currentSelectedValue, targetSelectedValue]; isChange = true; } } } else { const targetSelectedValueCache = new MemoryListSearchCache(targetSelectedValues, (v) => v.key); const ubs = currentSelectedValue.length; currentSelectedValue = currentSelectedValue.filter((v) => targetSelectedValueCache.get(v.key) == null); isChange = ubs !== currentSelectedValue.length; } if (isChange) { this.setSelectedValue(currentSelectedValue); this.$onSelectedChange(currentSelectedValue); } } protected generatorNewTreeNode( parent: OioTreeNode, key: string, title: string | undefined, metadata: TreeNodeMetadata, data: ActiveRecord ): OioTreeNode { const node = super.generatorNewTreeNode(parent, key, title, metadata, data); this.generatorTreeNodeAfterProperties(node); return node; } /** * 获取回填数据 * @protected */ protected async fetchBackfillData( currentValues: ActiveRecord[], metadataList: TreeNodeMetadata[] ): Promise { this.enableLoadData = false; return TreeService.fetchAll(metadataList, { expressionParameters: this.generatorExpressionParameters() }); } protected backfillDataProcess({ selectedNodes }: BackfillDataParameters) { if (this.selectMode === SelectMode.multiple) { this.setSelectedValue(selectedNodes); } else { this.setSelectedValue(selectedNodes[0]); } } protected async backfillDataByValue(): Promise { const res = await super.backfillDataByValue(); if (!res) { const { treeDefinition: rootMetadata } = this; if (rootMetadata) { this.rootNode = this.generatorRootNode(rootMetadata); await this.loadAllData(); return true; } } return res; } protected async clearBackfillDataProcess() { const { treeDefinition: rootMetadata } = this; if (rootMetadata) { this.rootNode = this.generatorRootNode(rootMetadata); } else { this.rootNode = undefined; } this.expandedKeys = []; this.setSelectedValue(undefined); } protected convertBackfillData( currentValues: ActiveRecord[], metadataList: TreeNodeMetadata[], list: TreeNodeResponseBody[], customCreateNodeCallback?: (node: OioTreeNode, value: TreeData | undefined) => void, customUpdateNodeCallback?: (node: OioTreeNode, value: TreeData) => void ): BackfillDataParameters | undefined { return super.convertBackfillData( currentValues, metadataList, list, customCreateNodeCallback || ((node, value) => { if (value) { this.generatorTreeNodeAfterProperties(node); } }), customUpdateNodeCallback || ((node) => { this.generatorTreeNodeAfterProperties(node); }) ); } protected generatorTreeNodeAfterProperties(node: OioTreeNode) { node.selectable = node.value.metadata.model === this.field.references; } protected setSelectedValue(selectedValue: OioTreeNode | OioTreeNode[] | undefined) { if (!selectedValue) { this.selectedValue = undefined; this.checkedKeys = undefined; return; } this.selectedValue = selectedValue; if (Array.isArray(selectedValue)) { this.checkedKeys = selectedValue.map((v) => v.key); } else { this.checkedKeys = [selectedValue.key]; } } protected $onSelectedChange(selectedNodes: OioTreeNode[] | null | undefined) { if (selectedNodes === undefined) { return; } if (selectedNodes === null) { this.change(null); return; } const data: ActiveRecord[] = selectedNodes.map((v) => v.value.data!).filter((v) => !!v) || []; if (this.selectMode === SelectMode.multiple) { this.change(CastHelper.cast(data)); } else { this.change(CastHelper.cast(data[0])); } } }