import { IContext, ICtrlActionResult, IHttpResponse, IParam, ITreeAbility, ITreeController, ITreeControllerParams, ITreeModel, ITreeStore, TreeActionType, } from '@/core/interface'; import { ControlVOBase } from '@/core/modules'; import TreeService from '@/core/modules/ctrl-service/tree-service'; import { deepCopy } from '@/core/utils'; import { MDCtrlController } from './md-ctrl-controller'; /** * 树部件控制器 * * @export * @class TreeController * @extends {(MDExCtrlController)} * @implements {ITreeController} * @template T */ export class TreeController extends MDCtrlController implements ITreeController { /** * 树部件服务 * * @protected * @type {TreeService} * @memberof TreeController */ declare ctrlService: TreeService; /** * 树模型 * * @protected * @type {ITreeModel} * @memberof TreeController */ protected declare readonly model: ITreeModel; protected tileData: IParam[] = []; /** * 节点过滤 * * @private * @type {string} * @memberof TreeController */ private nodeFilter: string = ''; /** * Creates an instance of TreeController. * @param {ITreeControllerParams} params * @memberof TreeController */ public constructor( params: ITreeControllerParams ) { super(params); this.ctrlInit(params); } /** * 处理部件初始化 * * @protected * @param {ITreeControllerParams} params * @memberof TreeController */ protected processCtrlInit( params: ITreeControllerParams ) { super.processCtrlInit(params); Object.assign(this.store, { expandedKeys: [], selectedKeys: [], loadedKeys: [], selections: params.selections, initTree: true, }); } /** * 加载数据 * * @param {IParam} [node={}] * @return {*} {Promise} * @memberof TreeController */ public async load(node: IParam = {}): Promise { const params: IParam = { srfnodeid: node && node.id ? node.id : '#', srfnodefilter: this.nodeFilter, parentData: node?.curData, }; const { tempContext, tempViewParams } = this.computeNodeParams(node); const parentData: IParam = {}; this.emit('beforeLoad', [parentData]); Object.assign(params, { viewParams: { ...tempViewParams, ...parentData } }); const isRoot = this.store.data.length == 0; if (isRoot) { this.beforeAsyncAction('load', tempContext, params); } try { const response: IHttpResponse = await this.ctrlService.getNodes( tempContext, params ); if (response.success) { const data = response.data; this.addTileData(data); this.tileData.push(...data); this.setExpandedNode(data); this.setAppendCaption(data); if (isRoot) { this.store.data = data; } else { const nodeData = this.getTreeNodeByKey(node.id); data.forEach((item: any) => { item.parentNodeId = node.id; }); if (nodeData) { if (data && data.length > 0) { nodeData.children = data; } else { nodeData.isLeaf = true; } } } this.setDefaultSelection(data, isRoot, node.checked); this.emit('load', this.getData()); if (isRoot) { this.afterAsyncAction('load', response); } return { ok: true, data: this.getData(), rowData: response.data }; } if (isRoot) { this.afterAsyncAction('load', response); } return { ok: false, data: this.getData(), rowData: response.data }; } catch (error: any) { if (isRoot) { this.afterAsyncAction('load', error); } return { ok: false, data: this.getData(), rowData: error }; } } private addTileData(items: IParam[]) { items.forEach((item: IParam) => { if ( this.tileData.findIndex((td: IParam) => td.srfkey !== item.srfkey) === -1 ) { this.tileData.push(item); } }); } /** * 处理值改变 * * @param {IParam} node * @memberof TreeController */ public handleValueChange(node: IParam) { } /** * 刷新所有节点 * * @memberof TreeController */ public async refreshAllNode(opt?: IParam): Promise { this.store.data = []; this.store.initTree = false; if (opt?.query) { this.nodeFilter = opt.query; } else { this.nodeFilter = ''; } setTimeout(async () => { this.store.initTree = true; }, 0); return await this.load(); } /** * 刷新父节点 * * @memberof TreeController */ public async refreshParentNode() { if (this.store.selections.length > 0) { const curNode = this.store.selections[0]; const parentNodeId = curNode.parentNodeId; if (parentNodeId) { const { tempContext, tempViewParams } = this.computeNodeParams(curNode); const param = { srfnodeid: parentNodeId, viewParams: tempViewParams, }; await this.refreshNode(tempContext, param); } else { await this.refreshAllNode(); } } } /** * 刷新当前节点 * * @memberof TreeController */ public refreshCurrentNode() { if (this.store.selections.length > 0) { const curNode = this.store.selections[0]; const curNodeId = curNode.id; const { tempContext, tempViewParams } = this.computeNodeParams(curNode); const param = { srfnodeid: curNodeId, viewParams: tempViewParams, }; this.refreshNode(tempContext, param); } } /** * 刷新节点 * * @param {string} context 上下文 * @param {IContext} params 参数 * @memberof TreeController */ private async refreshNode(context: IContext, params: IParam) { try { this.beforeAsyncAction('load', context, params); const response: IHttpResponse = await this.ctrlService.getNodes( context, params ); if (response.success) { const data = response.data; this.setExpandedNode(data); this.setAppendCaption(data); const nodeData = this.getTreeNodeByKey(params.srfnodeid); data.forEach((item: any) => { item.parentNodeId = params.srfnodeid; }); if (nodeData) { nodeData.children = data; } // 树视图组件删除加载缓存,否则该节点下的子节点无法刷新 this.store.loadedKeys = this.store.loadedKeys.filter(item => item !== params.srfnodeid); this.setDefaultSelection(data, false, nodeData.selected); this.emit('load', this.getData()); this.afterAsyncAction('load', response); return { ok: true, data: this.getData(), rowData: response.data }; } this.afterAsyncAction('load', response); return { ok: false, data: this.getData(), rowData: response.data }; } catch (error: any) { this.afterAsyncAction('load', error); return { ok: false, data: this.getData(), rowData: error }; } } /** * 处理选中数据改变 * * @param {IParam[]} selections * @memberof TreeController */ public handleSelectionChange(selections: IParam[]) { this.store.selections = selections; const selectedKeys: string[] = []; selections.forEach((selection) => { selectedKeys.push(selection.id); }); this.store.selectedKeys = selectedKeys; this.emit('selectionChange', this.getData()); } /** * 处理展开变化 * * @param {string[]} expandedKeys * @memberof TreeController */ public handleExpandChange(expandedKeys: string[]) { this.store.expandedKeys = expandedKeys; } /** * 设置默认选中 * * @private * @param {IParam[]} items * @param {boolean} [isRoot=false] * @param {boolean} [isSelectedAll=false] * @return {*} {void} * @memberof TreeController */ private setDefaultSelection( items: IParam[], isRoot = false, isSelectedAll = false ): void { // 如果选中有值则不设置默认选中 if (items.length === 0 || this.store.selections.length > 0) { return; } const { viewParams } = this.store; // 导航视图中选中优先级高于其他 if (this.selectFirstDefault) { let index = 0; if (isRoot) { if (viewParams && viewParams.srfnavtag) { const activate = viewParams.srfnavtag; index = items.findIndex((item: any) => { return ( item.id && item.id.split(';') && item.id.split(';')[0] == activate ); }); if (index === -1) index = 0; } else { index = 0; } } else { return; } this.store.selectedKeys = [items[index].id]; this.store.selections = [items[index]]; this.emit('selectionChange', this.getData()); return; } // 存在选中数据,以选中数据优先 if ( viewParams && viewParams.selectedData && viewParams.selectedData.length > 0 ) { const selections: IParam[] = []; viewParams.selectedData.forEach((select: IParam) => { if (select.srfkey) { const item = this.tileData.find( (i: IParam) => i.srfkey === select.srfkey ); if (item) { selections.push(item); } } }); if (selections.length > 0) { this.handleSelectionChange(selections); return; } } // 如果父节点选中则选中所有子节点 const defaultSelect = isSelectedAll ? items : items.filter((item: any) => item.selected); const selections: IParam[] = []; selections.push(...this.store.selections); if (this.singleSelect) { if (selections.length == 0 && defaultSelect.length > 0) { selections.push(defaultSelect[0]); } } else { defaultSelect.forEach((select: IParam) => { const index = selections.findIndex( (selected: IParam) => select.id === selected.id ); if (index === -1) { selections.push(select); } }); } selections.forEach((selectNode: IParam) => { this.store.selectedKeys.push(selectNode.id); }); this.emit('selectionChange', this.getData()); } /** * 设置默认展开节点 * * @private * @param {IParam[]} items * @memberof TreeController */ private setExpandedNode(items: IParam[]) { items.forEach((item: any) => { if ( item.expanded && this.store.expandedKeys.findIndex((key: string) => key === item.id) === -1 ) { this.store.expandedKeys.push(item.id); } }); } /** * 设置附加标题 * * @private * @param {IParam[]} items * @memberof TreeController */ private setAppendCaption(items: IParam[]) { items.forEach((item: IParam) => { if (item.appendCaption && item.textFormat) { item.text = item.textFormat + item.text; } }); } /** * 计算节点参数 * * @private * @param {IParam} curNode * @return {*} {{ tempContext: IParam, tempViewParams: IParam }} * @memberof TreeController */ private computeNodeParams(curNode: IParam): { tempContext: IParam; tempViewParams: IParam; } { const { context, viewParams } = this.store; let tempContext: IParam = {}; const tempViewParams: any = deepCopy(viewParams); if (curNode && curNode.srfappctx) { tempContext = deepCopy(curNode.srfappctx); } else { tempContext = deepCopy(context); } return { tempContext, tempViewParams }; } /** * 根据节点标识获取父节点 * * @private * @param {string} id * @param {string} [key='id'] * @return {*} {*} * @memberof TreeController */ private getTreeNodeByKey(id: string, key = 'id'): any { const { data } = this.store; let node = null; if (data.length > 0 && id) { const getNode = (item: any): boolean => { let flag = false; if (item[key] === id) { node = item; flag = true; } if (item.children && item.children.length > 0) { for (const child of item.children) { if (getNode(child)) { flag = true; break; } } } return flag; }; for (const item of data) { if (getNode(item)) { break; } } } return node; } /** * 选择全部 * * @memberof GridController */ public selectAll(): void { // TODO 暂不考虑单选情况 this.handleSelectionChange(this.store.data); // this.store.selections = this.store.data; // this.emit('selectionChange', this.getData()); } /** * 选中数据 * * @param {IParam[]} items 选中数据 * @param {boolean} [reverse] 是否反选 * @memberof TreeController */ public selectItems(items: IParam[], reverse?: boolean) { // TODO 暂不考虑单选情况 const selections: IParam[] = []; this.tileData.forEach((item: IParam) => { const index = items.findIndex((i: IParam) => i.srfkey === item.srfkey); // 反选时未在items中选中,正选时在items中选中 if ((reverse && index === -1) || (!reverse && index !== -1)) { selections.push(item); } }); this.handleSelectionChange(selections); } /** * 处理上下文菜单选中 * * @memberof TreeController */ public handleContextMenuItemClick(node: IParam, args: IParam) { this.store.selections = [node]; const { context, viewParams } = this.store; const tempContext = deepCopy(context); const tempViewParams = deepCopy(viewParams); const data = this.getData(); const event = args.event; const ability = this.getAbility(); const logic = args.item.uiAction; // 执行视图逻辑 App.getViewLogicHelper().executeViewLogic( tempContext, tempViewParams, data, event, ability, logic ); } /** * 刷新 * * @param {(IParam | undefined)} [args] * @return {*} {Promise} * @memberof TreeController */ public async refresh(args?: IParam | undefined): Promise { try { this.store.selections = []; const result = await this.refreshAllNode(); if (result.ok) { return true; } } catch (error) { return false; } return false; } /** * 获取能力 * * @return {*} {ITreeAbility} * @memberof TreeController */ public getAbility(): ITreeAbility { return { ...super.getAbility(), refreshAllNode: this.refreshAllNode.bind(this), refreshParentNode: this.refreshParentNode.bind(this), refreshCurrentNode: this.refreshCurrentNode.bind(this), selectAll: this.selectAll.bind(this), selectItems: this.selectItems.bind(this), }; } }