import { acc, IPSDEFormDetail } from '@ibizstudio/api'; import { isNilOrEmpty } from 'qx-util'; import { CreateElement } from 'vue'; import { Vue, Component, Prop, Watch } from 'vue-property-decorator'; import { DesignDefaultContext } from '../../entities'; import { getFormItemIcon } from '../../utils'; import './design-structure-tree.less'; /** * 设计结构树 * * @export * @class DesignStructureTree * @extends {Vue} */ @Component({}) export class DesignStructureTree extends Vue { /** * 设计上下文 * * @type {DesignContext} * @memberof DesignStructureTree */ @Prop() ctx!: DesignDefaultContext; /** * 数据集接口 * * @type {string} * @memberof DesignStructureTree */ @Prop({ default: 'FetchDefault' }) datasetInterface!: string; /** * 是否是表单设计 * * @type {string} * @memberof DesignStructureTree */ @Prop({ default: false }) isFormDesign!: boolean; @Prop() searchValue = ''; @Watch('searchValue') searchValueChange(val: any) { const tree: any = this.$refs.tree; tree?.filter(val); } /** * 树数据 * * @type {any[]} * @memberof DesignStructureTree */ items: any[] = []; /** * 当前选中节点 * * @type {*} * @memberof DesignStructureTree */ selectNodeData: any; /** * 树组件ref * * @type {*} * @memberof DesignStructureTree */ tree: any; subs: string[] = []; created(): void { this.nodeSelect = this.nodeSelect.bind(this); this.masterLoaded = this.masterLoaded.bind(this); this.ctx.evt.on('loaded', this.masterLoaded); this.ctx.ctrl.select.evt.on('change', this.selectChange); this.subs.push( ...acc.commandLocals( () => { this.load(); }, ['update', 'create', 'remove'], this.ctx.ctrl.itemEntity.codeName.toUpperCase(), ), ); } mounted(): void { this.tree = this.$refs.tree; } destroyed(): void { this.ctx.evt.off('loaded', this.masterLoaded); this.ctx.ctrl.select.evt.off('change', this.selectChange); this.subs.forEach(str => { acc.unsubscribeLocal(str); }); } /** * 选中数据变更 * * @param {*} data * @memberof DesignStructureTree */ selectChange(data: any): void { if (!this.tree.getNode(data.srfkey)) { return; } this.selectNodeData = this.generateTreeNode(data); this.tree.setCurrentKey(data.srfkey); } /** * 主数据加载完毕 * * @memberof DesignStructureTree */ masterLoaded(): void { this.load(); } /** * 加载数据 * * @return {*} {Promise} * @memberof DesignStructureTree */ async load(): Promise { const { ctrl } = this.ctx; const s: any = await ctrl.itemService; if (s && s[this.datasetInterface]) { const res = await s[this.datasetInterface](this.ctx.context); if (res.ok) { this.formatData(res.data); const { select } = ctrl; if (select.data) { this.$nextTick(() => { this.selectChange(select.data); }); } } } else { console.warn(`指定数据集接口「${this.datasetInterface}」不存在!`); } } /** * 格式化数据 * * @param {any[]} items * @memberof DesignStructureTree */ formatData(items: any[]): void { this.items = []; items.forEach((item: any) => { // 表单看是否是表单分页,别的看srfpkey if (this.isFormDesign ? item.itemtype == "FORMPAGE" : isNilOrEmpty(item.srfpkey)) { const node = this.generateTreeNode(item); this.calcTreeLevel(node, items); this.items.push(node); } }); } /** * 计算树层级 * * @param {any} node * @param {any[]} items * @memberof DesignStructureTree */ calcTreeLevel(node: any, items: any[]): void { node.children = items .filter((item: any) => item.srfpkey === node.id) .map((item: any) => { const childNode = this.generateTreeNode(item); this.calcTreeLevel(childNode, items); return childNode; }); } /** * 生成树节点数据 * * @param {*} data * @return {*} {*} * @memberof DesignStructureTree */ generateTreeNode(data: any): any { return { text: data.text, id: data.srfkey, data: data, }; } filterTreeNode(value: any, data: any, node: any) { if (value) { const regExp = value .trimStart() .trimEnd() .toLowerCase(); const text = data.text.toLowerCase(); if (text.indexOf(regExp) >= 0) { return true; } else { return false; } } else { return true; } } /** * 树选中 * * @param {*} data * @param {*} node * @memberof DesignStructureTree */ nodeSelect(data: any, _node: any): void { this.ctx.ctrl.select.set(data.data); } /** * @description 判断节点是否可拖拽 * @param {*} node * @return {*} {boolean} * @memberof DesignStructureTree */ allowDrag(node: any): boolean { if (node.level === 0 || Object.is(node.data.data.detailtype, 'FORMPAGE')) { return false; } return true; } /** * @description 是否可放置 * @param {*} draggingNode * @param {*} dropNode * @param {*} type * @return {*} {boolean} * @memberof DesignStructureTree */ allowDrop(draggingNode: any, dropNode: any, type: any): boolean { if (Object.is(type, 'inner')) { if (Object.is(dropNode.data.data.detailtype, 'GROUPPANEL') || Object.is(dropNode.data.data.detailtype, 'FORMPAGE') || Object.is(dropNode.data.data.pvparttype, 'CONTAINER')) { return true; } return false; } else { if (dropNode.level === 0) { return false; } if (Object.is(dropNode.data.data.detailtype, 'FORMPAGE') && !Object.is(draggingNode.data.data.detailtype, 'FORMPAGE')) { return false; } return true; } } /** * @description 树节点拖拽 * @param {*} before * @param {*} after * @param {*} inner * @param {*} event * @memberof DesignStructureTree */ async treeNodeChange(before: any, after: any, inner: any, event: any) { const beforeData: any = before.data.data; const afterData: any = after.data.data; let parent = after.parent.data.data; if (afterData.srfdename == 'PSAPPPVPART') { beforeData.ppsapppvpartid = afterData.ppsapppvpartid || null; beforeData.ppsapppvpartname = afterData.ppsapppvpartname || null; if (inner === 'inner') { parent = after.data.data; beforeData.ppsapppvpartid = afterData.psapppvpartid || null; beforeData.ppsapppvpartname = afterData.psapppvpartname || null; } } else { beforeData.ppsdeformdetailid = afterData.ppsdeformdetailid || null; beforeData.ppsdeformdetailname = afterData.ppsdeformdetailname || null; if (inner === 'inner') { parent = after.data.data; beforeData.ppsdeformdetailid = afterData.psdeformdetailid || null; beforeData.ppsdeformdetailname = afterData.psdeformdetailname || null; } } const srfpkey = parent ? parent.srfkey : null; const items: any[] = await this.ctx.ctrl.itemService.selectLocal(this.ctx.context, { srfpkey, }); const index = items.findIndex((item: any) => { if (afterData.srfdename == 'PSAPPPVPART') { return Object.is(item.psapppvpartid, beforeData.psapppvpartid) } return Object.is(item.psdeformdetailid, beforeData.psdeformdetailid); }); if (!Object.is(index, -1)) { const beforeIndex: number = items.findIndex((item: any) => { if (afterData.srfdename == 'PSAPPPVPART') { return Object.is(item.psapppvpartid, beforeData.psapppvpartid) } return Object.is(item.psdeformdetailid, beforeData.psdeformdetailid); }) || 0; items.splice(beforeIndex, 1); } const afterIndex: number = items.findIndex((item: any) => { if (afterData.srfdename == 'PSAPPPVPART') { return Object.is(item.psapppvpartid, afterData.psapppvpartid) } return Object.is(item.psdeformdetailid, afterData.psdeformdetailid) }) || 0; items.splice(Object.is(inner, 'before') ? afterIndex : afterIndex + 1, 0, beforeData); this.ctx.ctrl.itemService.disableAcc(); for (let i = 0; i < items.length; i++) { items[i].ordervalue = (i + 1) * 100; await this.ctx.ctrl.itemService.UpdateTemp(this.ctx.context, items[i]); } this.ctx.ctrl.itemService.enableAcc(); this.$emit('update-order'); } /** * 绘制树节点 * * @param {CreateElement} _h * @param {*} context * @memberof LogicTree */ renderTreeItem(_h: CreateElement, context: any) { const { data } = context; return (
{this.isFormDesign ? this.getTreeNodeIcon(data?.data) : null}
{data?.text}
); } /** * 获取树节点对应icon图标 * * @author zhanghengfeng * @date 2023-03-28 11:03:24 * @param {IPSDEFormDetail} data * @return {*} */ getTreeNodeIcon(data: IPSDEFormDetail) { if (!data || !data.itemtype) { return null; } const type = data.itemtype; switch (type) { case 'FORMPAGE': case 'TABPANEL': return ; case 'GROUPPANEL': return ; case 'RAWITEM': return this.getRawItemIcon(data); case 'BUTTON': return ; case 'IFRAME': return ; case 'FORMITEMEX': return ; case 'DRUIPART': return ; case 'MDCTRL': return ; case 'FORMITEM': return ; default: return null; } } /** * 获取数据itemtype属性值为RAWITEM的icon图标 * * @author zhanghengfeng * @date 2023-03-28 11:03:54 * @param {IPSDEFormDetail} data * @return {*} */ getRawItemIcon(data: IPSDEFormDetail) { if (!data.contenttype) { return null; } const type: string = data.contenttype.toLowerCase(); const rawItemIcon: Record = { html: './assets/svg/raw-html.svg', image: './assets/svg/raw-image.svg', video: './assets/svg/raw-video.svg', placeholder: './assets/svg/raw-placeholder.svg', divider: './assets/svg/raw-divider.svg', info: './assets/svg/raw-info.svg', warning: './assets/svg/raw-warning.svg', error: './assets/svg/raw-error.svg', }; if (type === 'raw') { return ; } else if (Object.prototype.hasOwnProperty.call(rawItemIcon, type) && rawItemIcon[type]) { return ; } return null; } render(h: any): any { return (
{h('el-tree', { ref: 'tree', class: 'design-tree', props: { data: this.items, draggable: true, 'allow-drag': this.allowDrag, 'allow-drop': this.allowDrop, 'node-key': 'id', 'highlight-current': true, 'default-expand-all': true, 'expand-on-click-node': false, 'auto-expand-parent ': false, indent: 8, 'render-content': this.renderTreeItem, 'filter-node-method': this.filterTreeNode, }, on: { 'current-change': this.nodeSelect, 'node-drop': this.treeNodeChange, }, })}
); } }