import { immutable, circular, action, excludedInJSON } from '../decorators'; import { config, history, LEVEL_ENUM, Vertex, PackageJSON, App, Enum, Service, WebService, Structure, Page, Element, utils, ViewParam, ViewVariable, Lifecycle, genFinalCode, generator, Nuims, ActionOptions, ACTION_MODE, vertexsMap, updateDataTypeList, Interface } from '..'; import Block from './Block'; import { viewService } from '../../service/page'; import { Param } from '../logic/Param'; import Logic from '../logic/Logic'; import { logicService } from '../../service/logic'; import { blockService } from '../../service/assets'; import { TEMPLATE_MAP } from '../../service/page/templates'; import { ElementToVueOptions } from './Element'; import translator from '../logic/translator'; import { isPlainObject, throttle } from 'lodash'; import { traverse } from '../utils'; import lifecycle from '@/service/page/lifecycle'; import * as babel from '@babel/core'; import view from '@/service/page/view'; import Entity from '../data/Entity'; import { postServiceType } from '../../service/common/preprocess'; /** * 子页面类,前端路由控制 */ export class View extends Block { /** * 概念类型 */ @immutable() public readonly level: LEVEL_ENUM = LEVEL_ENUM.view; /** * 子页面 Id */ @immutable() public readonly id: string = undefined; /** * 子页面名称 */ @immutable() public readonly name: string = undefined; /** * 前端路径,不带 page 名 */ @immutable() public readonly path: string = undefined; /** * 前后端路径 * 由于 name 会改的关系,尽量不要用 * @deprecated */ @immutable() public readonly tempPath: string = undefined; /** * 唯一标识码 */ @immutable() public readonly code: string = undefined; /** * 标题 */ @immutable() public readonly title: string = undefined; /** * 面包屑 */ @immutable() public readonly crumb: string = undefined; /** * 是否为默认跳转页 */ @immutable() public readonly first: string = undefined; /** * 子页面 Id */ @immutable() public readonly parentId: string = undefined; /** * 附加的 script */ @immutable() public readonly script: string = undefined; /** * 页面 Id */ @immutable() public readonly pageId: string = undefined; /** * 页面模板 Id */ @immutable() public readonly pageTemplateId: number = undefined; /** * 所属页面 */ @circular() @immutable() public readonly page: Page = undefined; /** * 父级子页面 */ @circular() @immutable() public readonly parent: View = undefined; /** * 子页面 */ @immutable() public readonly children: Array = []; /** * 正在请求的 Promise * 前端 UI 状态 */ @excludedInJSON() public contentPromise: Promise = undefined; /** * 输入参数 */ // @immutable() // public readonly params: Array = []; /** * 逻辑列表 */ // @immutable() // public readonly logics: Array = []; // vueFile: VueFile; /** * 树组件的子节点字段 */ @immutable() public readonly moreChildrenFields: Array = ['$def.params', '$def.variables', '$def.logics']; /** * @param source 需要合并的部分参数 */ constructor(source?: Partial) { super(); source && this.assign(source); this.on('change', throttle( this._onChange.bind(this), config.throttleWait, { leading: true, trailing: true }, )); } private async _onChange() { // 在子页面修改时生成缓存,注意不需要 await if (config.webFileCache) { const tasks = []; // 保存 cache 之前需要保证 view 和下面的 logic 全部 load 完成 if (!this.isContentLoaded()) tasks.push(this.load()); this.$def.logics.forEach((logic) => { if (!logic.isContentLoaded()) tasks.push(logic.load()); }); await Promise.all(tasks); try { generator.saveViewCache(this); } catch (e) { this.emit('translate-error'); } } generator.saveViewDetailCache(this); } /** * 在 mergeBlock 的情况下使用 * 在 loadDetail 的情况下使用 * @returns */ load() { // 如果有正在进行的 Promise,则直接返回它 if (this.contentPromise) return this.contentPromise; return this.contentPromise = (async () => { try { const result: View = await viewService.load({ path: { id: this.id, }, }); const $def = result.$def; delete (result as any).$def; delete (result as any).children; const temp = View.from(result, this.parent, this.page, this); this.assign({ $html: temp.$html, script: temp.script, // style: temp.style, definition: temp.definition, }); // def 情况比较特殊,需要单独处理,防止逻辑重新加载 this._syncDef($def); this.attachNodePath(true); return this; } catch (err) { } })().finally(() => this.contentPromise = undefined); } loadAll() { // 如果有正在进行的 Promise,则直接返回它 if (this.contentPromise) return this.contentPromise; return this.contentPromise = (async () => { try { const result: View = await viewService.load({ path: { id: this.id, }, }); const temp = View.from(result, this.parent, this.page, this); this.assign(temp); utils.traverse((current) => { current.node.attachNodePath(true); }, { node: this }); return this; } catch (err) { } })().finally(() => this.contentPromise = undefined); } isContentLoaded() { return !!this.$html; } /** * 后续改为根据 id 查找 Element */ attachNodePath(clear?: boolean) { if (!this.$html) return; utils.traverse((current) => { current.node.nodePath = current.nodePath; if (clear && current.node.tag === 'd-progress') { current.parent.children.splice(current.index, 1); } }, { node: this.$html }); } /** * 根据路径查找子节点 * @param nodePath 节点路径,/1/2 表示根节点的第1个子节点的第2个子节点 * @param node 查找的起始节点 * @examples * - findElementByNodePath('', root) 指根节点本身 * - findElementByNodePath('/', root) 指根节点本身 * - findElementByNodePath('/0', root) 指第0个子节点 * - findElementByNodePath('/2/1', root) 指第2个子节点的第1个子节点 */ findElementByNodePath(nodePath: string, node: Element = this.$html): Element { if (nodePath[0] === '/') // 这个里边相对和绝对是一样的 nodePath = nodePath.slice(1); const arr = nodePath.split('/'); if (!nodePath || !arr.length) return node; else { const parent = node; return this.findElementByNodePath(arr.slice(1).join('/'), parent.children[+arr[0]]); } } /** * 根据标签查找元素 * @param tag */ findElementByTag(tag: string) { return this.$html.findElementByTag(tag); } /** * 根据属性查找元素 * @param name * @param value */ findElementByAttr(name: string, value: string) { return this.$html.findElementByAttr(name, value); } /* 绑定数据结构和接口 */ addData(res: any) { const microService = this.parent.page.service.app.firstMicroService; const interfaces = res.interfaces as Array; if (interfaces && interfaces.length) { interfaces.forEach((item, index) => { const itface = interfaces[index] = Interface.from(item, microService); postServiceType(itface); }); microService.interfaces.unshift(...interfaces); microService.emit('interfacesChange'); } const enums = res.enums as Array; if (enums && enums.length) { enums.forEach((item, index) => enums[index] = Enum.from(item, microService)); microService.data.enums.unshift(...enums); updateDataTypeList(); } const entities = res.entities as Array; if (entities && entities.length) { entities.forEach((item, index) => entities[index] = Entity.from(item, microService)); microService.data.entities.unshift(...entities); updateDataTypeList(); } const structures = res.structures as Array; if (structures && structures.length) { structures.forEach((item, index) => structures[index] = Structure.from(item, microService)); microService.data.structures.unshift(...structures); updateDataTypeList(); } } @action('添加子页面') async importViewCreate(none?: void, actionOptions?: ActionOptions, then?: () => Promise) { config.defaultApp?.emit('saving'); const body = this.toJSON(); utils.logger.debug('添加子页面', body); const result = await viewService.importView({ headers: { appId: config.defaultApp?.id, operationAction: actionOptions?.actionName || 'View.create', operationDesc: actionOptions?.actionDesc || `添加子页面"${this.name}${this.title ? `(${this.title})` : ''}"`, }, body, }); this.deepPick(result.view, ['id']); // 添加数据 this.addData(result); if (result.view) { const temp = View.from(result.view, this.parent, this.parent.page); this.assign({ $html: temp.$html, script: temp.script, $def: temp.$def, tempPath: temp.tempPath, code: temp.code, parent: temp.parent, page: temp.page, children: temp.children, definition: temp.definition, }); utils.traverse((current) => { current.node.attachNodePath(true); }, { node: this }); } this.attachNodePath(); await then?.(); // 在子页面修改时生成缓存,注意不需要 await this.page.service.emit('pageTreeChange'); const microService = this.page.service.app.firstMicroService; microService.emit('dataTypesChange'); microService.emit('enumsChange'); microService.emit('vertexIdToNameChange'); this.emit('change'); await config.defaultApp?.history.load(); config.defaultApp?.emit('saved'); return this; } /** * 添加子页面 */ @action('添加子页面') async create(none?: void, actionOptions?: ActionOptions, then?: () => Promise) { config.defaultApp?.emit('saving'); const body = this.toJSON(); body.serviceId = this.page.service.id; utils.logger.debug('添加子页面', body); const result = await viewService.create({ headers: { appId: config.defaultApp?.id, operationAction: actionOptions?.actionName || 'View.create', operationDesc: actionOptions?.actionDesc || `添加子页面"${this.name}${this.title ? `(${this.title})` : ''}"`, }, body, }); this.deepPick(result, ['id']); this.assign({ tempPath: this.parent ? `${this.parent.tempPath}/${this.name}` : `/${this.page.rootView.name}`, code: this.parent ? `${this.parent.code}/ID_${this.id}` : `/ID_${this.page.rootView.id}`, }); this.attachNodePath(); await then?.(); // 在子页面修改时生成缓存,注意不需要 await this.page.service.emit('pageTreeChange'); this.emit('change'); await config.defaultApp?.history.load(); config.defaultApp?.emit('saved'); return this; } /** * 删除子页面 */ @action('删除子页面') async delete(none?: void, actionOptions?: ActionOptions) { config.defaultApp?.emit('saving'); if (!this.parent) throw Error('该子页面为根节点!'); try { await viewService.delete({ headers: { appId: config.defaultApp?.id, operationAction: actionOptions?.actionName || 'View.delete', operationDesc: actionOptions?.actionDesc || `删除子页面"${this.name}${this.title ? `(${this.title})` : ''}"`, }, path: { id: this.id, }, }); } catch (err) { await config.defaultApp?.history.load(); throw err; } const index = this.parent.children.indexOf(this); ~index && this.parent.children.splice(index, 1); // 删除的子页面为默认跳转页时,取消默认跳转页面 if (this.parent.first && this.parent.first === this.name) { this.parent.assign({ first: null }); await this.parent.update(undefined, { actionDesc: `取消子页面"${this.name}"为默认跳转页`, }); } this.destroy(); this.page.service.emit('pageTreeChange'); await config.defaultApp?.history.load(); config.defaultApp?.emit('saved'); } /** * 修改子页面 */ async update(none?: void, actionOptions?: ActionOptions, then?: () => Promise) { config.defaultApp?.emit('saving'); const body = this.toPlainJSON(); utils.logger.debug('修改页面', body); const result = await viewService.update({ headers: { appId: config.defaultApp?.id, operationAction: actionOptions?.actionName || 'View.update', operationDesc: actionOptions?.actionDesc || `修改子页面"${this.name}${this.title ? `(${this.title})` : ''}"`, }, body, }); await then?.(); await config.defaultApp?.history.load(); config.defaultApp?.emit('saved'); return this; } /** * 设置子页面名称 * 如果为顶级子页面,会同时设置 Page 的属性 * @param name 名称 */ @action('设置子页面名称') async setName(name: string) { const oldName = this.name; const oldTempPath = this.tempPath; this.assign({ name }); this.assign({ tempPath: this.parent ? `${this.parent.tempPath}/${name}` : `/${name}`, }); await this.update(undefined, { actionDesc: `设置页面"${oldName}"的名称为"${name}"`, }); if (this.page.rootView === this) { this.page.assign({ name }); await this.page.update(); } // 同步权限 new Nuims({ domainName: this.page.service.app.name, view: this, }).editResourceFromResourceValue(oldTempPath); this.page.service.emit('pageTreeChange'); this.emit('change'); } /** * 设置子页面标题 * 如果为顶级子页面,会同时设置 Page 的属性 * @param title 标题 */ @action('设置子页面标题') async setTitle(title: string) { const oldTitle = this.title; this.assign({ title }); await this.update(undefined, { actionDesc: `设置页面"${oldTitle}"的标题为"${title}"`, }); if (this.page.rootView === this) { this.page.assign({ title }); await this.page.update(); } this.page.service.emit('pageTreeChange'); this.emit('change'); } /** * 设置子页面面包屑 * @param crumb 面包屑 */ @action('设置子页面面包屑') async setCrumb(crumb: string) { const oldCrumb = this.crumb; this.assign({ crumb }); await this.update(undefined, { actionDesc: `设置页面"${oldCrumb}"的面包屑为"${crumb}"`, }); this.page.service.emit('pageTreeChange'); this.emit('change'); } /** * 设置该子页面为默认跳转页 */ @action('设置该子页面为默认跳转页') async setAsFirst() { if (!this.parent) return; this.parent.assign({ first: this.name }); await this.parent.update(undefined, { actionDesc: `设置子页面"${this.name}"为默认跳转页`, }); this.page.service.emit('pageTreeChange'); this.parent.emit('change'); } @action('是否是默认跳转页') canCancelFirst() { return this.parent.first === this.name; } @action('设置该子页面为取消默认跳转页') async setAsFirstFalse() { if (!this.parent) return; if (this.parent.first && this.parent.first === this.name) { this.parent.assign({ first: null }); await this.parent.update(undefined, { actionDesc: `设置子页面"${this.name}"为取消默认跳转页`, }); this.page.service.emit('pageTreeChange'); this.parent.emit('change'); } } /** * 添加子页面 * @param view */ async importView(view: View) { view.assign({ pageId: this.page && this.page.id, parentId: this.id, parent: this, }); await view.importViewCreate(undefined, undefined, async () => { this.children.unshift(view); }); return view; } /** * 添加子页面 * @param view */ async addView(view: View) { view.assign({ pageId: this.page && this.page.id, parentId: this.id, parent: this, }); await view.create(undefined, undefined, async () => { this.children.unshift(view); }); return view; } /** * 从模板添加子页面 * @param view 页面名称或页面 Options * @param templateId 页面模板 Id */ async addViewFromTemplate(viewName: string | Partial, templateId: number) { let view: View; if (typeof viewName === 'string') { view = new View({ name: viewName, title: viewName, pageId: this.page.id, page: this.page, parentId: this.id, parent: this, pageTemplateId: templateId, }); } else { view = new View(Object.assign({ pageId: this.page.id, page: this.page, parentId: this.id, parent: this, pageTemplateId: templateId, }, viewName)); } await view.importViewCreate(undefined, undefined, async () => { this.children.unshift(view); }); return view; } /** * 添加页面事件 */ @action('添加页面事件') async addLifecycle(data: Lifecycle, actionOptions?: ActionOptions) { // (data as any).element = this; let lifecycle = new Lifecycle(data); await lifecycle.create(undefined, Object.assign({ actionDesc: `添加页面"${this.name}"事件"${lifecycle.name}"`, }, actionOptions)); lifecycle = Lifecycle.from(lifecycle, this); this.$def.lifecycles.push(lifecycle); this.emit('change'); return lifecycle; } /** * 删除页面属性 */ @action('删除页面事件') async removeLifecycle(lifecycle: Lifecycle, actionOptions?: ActionOptions) { if (!lifecycle) throw Error(`该页面"${this.name}"没有事件"${lifecycle.name}"`); if (isPlainObject(lifecycle)) lifecycle = this.$def.lifecycles.find((item) => item.id === lifecycle.id); await lifecycle.delete(undefined, Object.assign({ actionDesc: `删除页面"${this.name}"事件"${lifecycle.name}"`, }, actionOptions)); const index = this.$def.lifecycles.indexOf(lifecycle); ~index && this.$def.lifecycles.splice(index, 1); this.emit('change'); } /** * 转换成 Vue 中需要的 JS 代码 * @TODO 这一版先做成 componentOptions 式的,后面再美化 */ toScript(options?: ElementToVueOptions) { let componentOptions = 'const componentOptions = {};'; if (this.script) componentOptions = this.script.trim().replace(/export default |module\.exports +=/, 'const componentOptions = '); const $def = JSON.parse(JSON.stringify(this.$def)); $def.title = this.title; $def.crumb = this.crumb; $def.first = this.first; // utils.traverse((current) => { // const node = current.node; // if (node.type === 'Unparsed') { // // 借 Identifier 的鸡下个蛋 // node.type = 'Identifier'; // node.name = node.code; // }else if (node.type === 'BuiltInFunction') { // Object.assign( // node, // ((babel.parse(`this.$utils['${node.calleeCode}']()`, { // filename: 'file.js', // })as babel.types.File).program.body[0] as babel.types.ExpressionStatement).expression, // { // arguments: (node.builtInFuncParams || []).map((param: any) => { // return param.builtInFuncParamValue; // }), // }, // ); // } // }, { node: $def }, { mode: 'anyObject' }); return `${componentOptions} ${genFinalCode(translator($def), options && options.finalCode)} export default componentOptions; `; } /** * 转换成 Vue 文件 */ toVue(options?: ElementToVueOptions) { let result = ''; if (this.$html) result += `\n`; if (this.script || this.$def) result += `\n`; // if (this.$def) // result += ''; // `\n`; return result; } /** * 转换成设计器中使用的 Vue 文件 * @param options */ toDesignerVue(options?: ElementToVueOptions) { let result = ''; if (this.$html) result += `\n`; // if (this.script) // result += `\n`; if (this.$def) result += ''; // `\n`; return result; } /** * 转换成 VueOptions * @TODO 后面 template 可以优化成 render 函数 */ toVueOptions(options?: ElementToVueOptions) { return { template: this.$html.toVue(options), script: this.toScript(options), // definition: JSON.stringify(this.$def), }; } /** * 合并代码 */ @action('根据实体自动生成组件和逻辑') async mergeBlock({ code, nodePath, position, parentNodePath }: { code: string, nodePath: string, position: string, parentNodePath: string }, actionOptions?: ActionOptions, then?: () => Promise) { config.defaultApp?.emit('saving', true); type Replacement = { [name: string]: string }; /** * 本来的 Name 相关的集合 */ const thisNameSets: { viewVariables: Set, logics: Set, elements: Set, } = { viewVariables: new Set(), logics: new Set(), elements: new Set(), }; /** * 需要替换的 Map */ const replacements: { viewVariables: Replacement, logics: Replacement, elements: Replacement, } = { viewVariables: {}, logics: {}, elements: {}, }; this.$def.params.forEach((param) => thisNameSets.viewVariables.add(param.name)); this.$def.variables.forEach((param) => thisNameSets.viewVariables.add(param.name)); this.$def.logics.forEach((logic) => thisNameSets.logics.add(logic.name)); utils.traverse((current) => { if (current.node.level === 'element' && current.node.name) thisNameSets.elements.add(current.node.name); }, { node: this.$html }); const setNewNameIfNotUnique = (obj: any, key: string, type: 'viewVariables' | 'logics' | 'elements') => { // name 去重 const newName = utils.unique(obj[key], thisNameSets[type]); if (newName !== obj[key]) obj[key] = replacements[type][obj[key]] = newName; }; const body: any = {}; const template = utils.sliceTagContent(code, 'template'); const parentNode = this.findElementByNodePath(parentNodePath); const viewId = parentNode.view.id; const parentId = parentNode.id; body.id = viewId; const definition = utils.sliceTagContent(code, 'definition'); if (definition) { const $def: any = JSON.parse(definition); body.$def = $def; } if (template) { const newNode = Element.parse(template, body.$def); const paramObj = { viewId, parentId, }; newNode.assign(paramObj); body.$html = newNode.toJSON(); if (position === 'append') { // 父节点最后追加 body.$html._posIndex = parentNode.children && parentNode.children.length || 0; } else { // 添加目标标节点的位置 const posIndex = nodePath.split('/').pop(); body.$html._posIndex = +posIndex; } traverse((current) => { if (current.node.level === 'element' && current.node.name) setNewNameIfNotUnique(current.node, 'name', 'elements'); }, { node: body.$html }); } if (definition) { // Element 需要逻辑变量没改名字时处理 const $def = body.$def; delete $def.dataSchema; if ($def.interfaces) { body.interfaces = $def.interfaces; delete $def.interfaces; } if ($def.structures) { body.structures = $def.structures; delete $def.structures; } [].concat($def.params || [], $def.variables || []).forEach((param: any) => { param.viewId = viewId; setNewNameIfNotUnique(param, 'name', 'viewVariables'); }); $def.lifecycles = $def.lifecycles || []; $def.lifecycles.filter((lifecycle: any) => !this.$def.lifecycles.find((thisLifecycle) => thisLifecycle.name === lifecycle.name)); $def.lifecycles.forEach((lifecycle: any) => { lifecycle.viewId = viewId; }); ($def.logics || []).forEach((logic: any) => { logic.moduleType = 'view'; logic.moduleId = viewId; setNewNameIfNotUnique(logic, 'name', 'logics'); }); } const hasKeyThenReplace = (obj: any, key: string, test: RegExp, replacement: Replacement) => { if (obj[key] === undefined || !Object.keys(replacement).length) return; if (!test) test = /^(\w+)$/; const cap = obj[key].match(test); if (!cap) return; const oldName = cap[1]; const newName = replacement[oldName]; if (newName) obj[key] = obj[key].replace(test, () => test.toString().replace(/[\^\\/]/g, '').replace(/\$$/, '').replace(/\(w\+\)/, newName)); }; const { interfaces } = body; delete body.interfaces; traverse((current) => { if (current.node.level === 'lifecycle' || current.node.level === 'event') { hasKeyThenReplace(current.node, 'logicId', null, replacements.logics); hasKeyThenReplace(current.node, 'value', null, replacements.logics); hasKeyThenReplace(current.node, 'value', /^\$refs\.(\w+)\./, replacements.elements); } else if (current.node.level === 'logicNode' && current.node.type === 'CallLogic') { hasKeyThenReplace(current.node, 'callee', null, replacements.logics); hasKeyThenReplace(current.node, 'calleeCode', null, replacements.logics); hasKeyThenReplace(current.node, 'calleeCode', /^\$refs\.(\w+)\./, replacements.elements); } else if (current.node.level === 'expressionNode' && current.node.type === 'Identifier') { if (current.parent && (current.parent.type !== 'MemberExpression' || current.node === current.parent.object)) { hasKeyThenReplace(current.node, 'code', null, replacements.viewVariables); hasKeyThenReplace(current.node, 'name', null, replacements.viewVariables); hasKeyThenReplace(current.node, 'name', null, replacements.logics); // 处理表格 data-source 的问题 } } // else if (current.node.level === 'expressionNode') }, { node: body }, { mode: 'anyObject' }); body.interfaces = interfaces; const res = await viewService.mergeBlock({ body, headers: { appId: config.defaultApp?.id, operationAction: 'View.mergeBlock', operationDesc: '根据实体自动生成组件和逻辑', }, }); await then?.(); this.redoMergeBlock(res); // await config.defaultApp?.history.load(); // config.defaultApp?.emit('saved'); } _removeDef($def: View['$def']) { const removedParams = this.$def.params.filter((param) => $def.params?.find((item) => param.id === item.id)); this.$def.params = this.$def.params.filter((param) => !$def.params?.find((item) => param.id === item.id)); removedParams.forEach((param) => { param.destroy(); }); const removedVariables = this.$def.variables.filter((param) => $def.variables?.find((item) => param.id === item.id)); this.$def.variables = this.$def.variables.filter((variable) => !$def.variables?.find((item) => variable.id === item.id)); removedVariables.forEach((variable) => { variable.destroy(); }); const removedLifecycles = this.$def.lifecycles.filter((param) => $def.lifecycles?.find((item) => param.id === item.id)); this.$def.lifecycles = this.$def.lifecycles.filter((lifecycle) => !$def.lifecycles?.find((item) => lifecycle.id === item.id)); removedLifecycles.forEach((lifecycle) => { lifecycle.destroy(); }); const removedLogics = this.$def.logics.filter((logic) => $def.logics?.find((item) => logic.id === item.id)); this.$def.logics = this.$def.logics.filter((logic) => !$def.logics?.find((item) => logic.id === item.id)); removedLogics.forEach((logic) => { // @TODO: 目前只做 logic 的 remove,后面页面中的可以切过来 config.defaultApp?.emit('some-remove', logic); logic.destroy(); }); } async undoMergeBlock(res: any) { const microService = this.page.service.app.firstMicroService; const structures = res.structures as Array; if (structures && structures.length) { const allStructures = microService.data.structures; for (let i = allStructures.length - 1; i >= 0; i--) { if (structures.find((item) => item.id === allStructures[i].id)) allStructures.splice(i, 1); } updateDataTypeList(); } const interfaces = res.interfaces as Array; if (interfaces && interfaces.length) { const allInterfaces = microService.interfaces; for (let i = interfaces.length - 1; i >= 0; i--) { if (allInterfaces.find((item) => item.id === interfaces[i].id)) allInterfaces.splice(i, 1); } const allglobalLogics = microService.globalLogic.globalLogics; for (let i = interfaces.length - 1; i >= 0; i--) { if (allglobalLogics.find((item) => item.id === interfaces[i].id)) allglobalLogics.splice(i, 1); } microService.emit('interfacesChange'); } const parent = vertexsMap.get(res.$html.parentId) as Element; parent.removeChild(res.$html, { actionMode: ACTION_MODE.undoRedo, loadHistory: false }); this._removeDef(res.$def); this.page.service.emit('pageTreeChange'); this.emit('change'); this.page.service.emit('vertexIdToNameChange'); await config.defaultApp?.history.load(); config.defaultApp?.emit('saved'); } _addDef($def: View['$def']) { $def.params?.forEach((param) => { this.$def.params.push(ViewParam.from(param, this)); }); $def.variables?.forEach((variable) => { this.$def.variables.push(ViewVariable.from(variable, this)); }); $def.lifecycles?.forEach((lifecycle) => { this.$def.lifecycles.push(Lifecycle.from(lifecycle, this)); }); $def.logics?.forEach((logic) => { this.$def.logics.push(Logic.from(logic, this)); }); } async redoMergeBlock(res: any) { const microService = this.page.service.app.firstMicroService; const structures = res.structures as Array; if (structures && structures.length) { structures.forEach((item, index) => structures[index] = Structure.from(item, microService)); microService.data.structures.unshift(...structures); updateDataTypeList(); } const interfaces = res.interfaces as Array; if (interfaces && interfaces.length) { interfaces.forEach((item, index) => { const itface = interfaces[index] = Interface.from(item, microService); itface.assign({ serviceType: 'micro' }); }); microService.interfaces.unshift(...interfaces); microService.globalLogic.globalLogics.unshift(...interfaces); microService.emit('interfacesChange'); } const parent = vertexsMap.get(res.$html.parentId) as Element; parent.addChild(res.$html, { actionMode: ACTION_MODE.undoRedo, loadHistory: false }); this._addDef(res.$def); this.page.service.emit('pageTreeChange'); this.emit('change'); this.page.service.emit('vertexIdToNameChange'); await config.defaultApp?.history.load(); config.defaultApp?.emit('saved'); } private _syncDef($def: View['$def']) { $def.params?.forEach((param) => { if (!this.$def.params.find((thisParam) => thisParam.id === param.id)) this.$def.params.push(ViewParam.from(param, this)); }); $def.variables?.forEach((variable) => { if (!this.$def.variables.find((thisVariable) => thisVariable.id === variable.id)) this.$def.variables.push(ViewVariable.from(variable, this)); }); $def.lifecycles?.forEach((lifecycle) => { if (!this.$def.lifecycles.find((thisLifecycle) => thisLifecycle.id === lifecycle.id)) this.$def.lifecycles.push(Lifecycle.from(lifecycle, this)); }); $def.logics?.forEach((logic) => { if (!this.$def.logics.find((thisLogic) => thisLogic.id === logic.id)) this.$def.logics.push(Logic.from(logic, this)); }); } /** * 从后端 JSON 生成规范的 View 对象 * @param currentView 方便构建子对象时关联,主要在 load 中使用 */ public static from(source: any, parent: View, page: Page, currentView?: View) { const view = new View(source); currentView = currentView || view; if (page && page.rootView) { view.assign({ tempPath: parent ? `${parent.tempPath}/${view.name}` : `/${page.rootView.name}`, code: parent ? `${parent.code}/ID_${view.id}` : `/ID_${page.rootView.id}`, }); } view.assign({ parent, page, children: view.children?.map((child) => View.from(child, currentView, page)), }); view.$html && view.assign({ $html: Element.from(view.$html, null, currentView) }); view.$def.params = view.$def.params.map((param) => ViewParam.from(param, currentView)); view.$def.variables = view.$def.variables.map((param) => ViewVariable.from(param, currentView)); view.$def.lifecycles = view.$def.lifecycles.map((lifecycle) => Lifecycle.from(lifecycle, currentView)); view.$def.logics = view.$def.logics.map((param) => Logic.from(param, currentView)); return view; } /** * 按照模板创建 */ public static async fromTemplate(templateName: string, source: any, parent: View, page: Page, scope?: 'pc' | 'h5') { // const template = TEMPLATE_MAP[templateName]; // @TODO: 后面改到资产中心 const { rows } = await blockService.loadList({ query: { keyword: (scope || config.scope) === 'h5' ? `@lcap/mobile-ui/${templateName}.vue` : `@cloud-ui/s-${templateName}.vue`, }, }); if (!rows[0]) throw new Error('Cannot find block ' + templateName); const content = rows[0].content.replace(/https:\/\/static-vusion\.163yun\.com/g, config.staticURL); const html = utils.sliceTagContent(content, 'template'); const script = utils.sliceTagContent(content, 'script'); const view = this.from(source, parent, page); view.assign({ $html: Element.fromHTML(html, null, view), script, }); return view; } } export default View;