2023-09-20 17:32:11 +00:00
|
|
|
|
"use strict";
|
|
|
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
|
|
|
};
|
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
|
// @ts-nocheck
|
|
|
|
|
const path_1 = __importDefault(require("path"));
|
|
|
|
|
const lib_1 = require("../../../lib");
|
|
|
|
|
const node_1 = require("../../runtime/node");
|
|
|
|
|
const decorator_1 = require("../../runtime/core/decorator");
|
|
|
|
|
const utils_1 = require("../../runtime/core/utils");
|
|
|
|
|
// 画布宽高
|
2023-09-26 14:16:42 +00:00
|
|
|
|
const CANVAS_WIDTH = 2000;
|
|
|
|
|
const CANVAS_HEIGHT = 1400;
|
2023-09-20 17:32:11 +00:00
|
|
|
|
// 节点box属性
|
|
|
|
|
const BOX_WIDTH = 100;
|
|
|
|
|
// const BOX_HEIGHT = 64; //height通过getBoxHeight方法动态计算
|
|
|
|
|
const BOX_FILL = "#484848";
|
|
|
|
|
const BOX_FILL_ACTIVE = "#E8B116";
|
|
|
|
|
const BOX_BORDER_WIDTH = 4;
|
|
|
|
|
// 节点main属性
|
|
|
|
|
const MAIN_GAP = 4;
|
|
|
|
|
const MAIN_WIDTH = BOX_WIDTH - MAIN_GAP * 2;
|
|
|
|
|
const MAIN_HEIGHT = 42;
|
|
|
|
|
const MAIN_FILL = "#2b2b2b";
|
|
|
|
|
const MAIN_BORDER_COLOR = "#121212";
|
|
|
|
|
const MAIN_BORDER_WIDTH = 1;
|
|
|
|
|
const MAIN_BORDER_RADIUS = 2;
|
|
|
|
|
// 节点panel属性
|
|
|
|
|
const PANEL_WIDTH = BOX_WIDTH - MAIN_GAP * 2;
|
|
|
|
|
const PANEL_HEIGHT = 14;
|
|
|
|
|
const PANEL_FILL = "#2C2C2C";
|
|
|
|
|
const PANEL_BORDER_RADIUS = 2;
|
|
|
|
|
// 删除节点圆圈属性
|
|
|
|
|
const REMOVE_RADIUS = 8;
|
|
|
|
|
const REMOVE_PADDING = 5;
|
|
|
|
|
const REMOVE_CIRCLE_FILL = "#484848";
|
|
|
|
|
const REMOVE_LINE_FILL = "#CCCCCC";
|
|
|
|
|
const REMOVE_LINE_WIDTH = 2;
|
|
|
|
|
// 中断类型属性
|
|
|
|
|
const ABORT_RADIUS = 5;
|
|
|
|
|
const ABORT_FILL = "#642EA4";
|
|
|
|
|
const ABORT_LINE_SIZE = 4;
|
|
|
|
|
// 箭头属性
|
|
|
|
|
const ARROW_FILL = "#F2F2F2";
|
|
|
|
|
const ARROW_SIZE = 6;
|
|
|
|
|
const ARROW_LINE_SIZE = 2;
|
|
|
|
|
// 辅助线间隔
|
|
|
|
|
const LINE_GAP = 20;
|
|
|
|
|
// 文本属性
|
|
|
|
|
const TEXT_FILL = "#CCCCCC";
|
|
|
|
|
const NAME_SIZE = 14;
|
|
|
|
|
const TYPE_SIZE = 12;
|
|
|
|
|
const TEXT_PADDING = 5;
|
|
|
|
|
// 边框半径
|
|
|
|
|
const BORDER_RADIUS = 6;
|
|
|
|
|
// Root节点属性
|
|
|
|
|
const ROOT_X = (CANVAS_WIDTH - BOX_WIDTH) / 2;
|
|
|
|
|
const ROOT_Y = 300;
|
|
|
|
|
let stage;
|
|
|
|
|
let baseLayer;
|
|
|
|
|
let bgGroup;
|
|
|
|
|
let staticArrowGroup;
|
|
|
|
|
let dynamicArrowGroup;
|
|
|
|
|
let rootGroup;
|
|
|
|
|
let nodeGroup;
|
|
|
|
|
let selectionGroup;
|
|
|
|
|
// 拖拽panel时生成的动态箭头
|
|
|
|
|
let dynamicArrow;
|
|
|
|
|
// 当前框选的box(因为计算碰撞只能跟shape碰撞,不能跟group)
|
|
|
|
|
let selectedBoxes = [];
|
|
|
|
|
// 移动防抖存储相关;
|
|
|
|
|
let SAVE_DEBOUNCE_TIMER = null;
|
|
|
|
|
const DEBOUNCE_TIME = 200;
|
|
|
|
|
// 获取父类构造函数(ts:constructor === class)
|
|
|
|
|
const getParentCls = (cls) => { var _a; return cls && ((_a = cls.prototype.__proto__) === null || _a === void 0 ? void 0 : _a.constructor); };
|
|
|
|
|
// 生成节点uuid
|
|
|
|
|
const uuid = () => Editor.Utils.UUID.generate();
|
|
|
|
|
// 防止越界
|
|
|
|
|
const clamp = (value, max, min) => (value < min ? min : value > max ? max : value);
|
|
|
|
|
// 是否命中
|
|
|
|
|
const hit = (x, y, targetX, targetY, targetW, targetH) => x > targetX && y > targetY && x < targetX + targetW && y < targetY + targetH;
|
|
|
|
|
// 时间格式化
|
|
|
|
|
const getTime = () => {
|
|
|
|
|
const addZero = (value) => (value < 10 ? "0" + value : value);
|
|
|
|
|
const date = new Date();
|
|
|
|
|
let year = date.getFullYear();
|
|
|
|
|
let month = addZero(date.getMonth() + 1);
|
|
|
|
|
let weekday = addZero(date.getDate());
|
|
|
|
|
let hour = addZero(date.getHours());
|
|
|
|
|
let minute = addZero(date.getMinutes());
|
|
|
|
|
let second = addZero(date.getSeconds());
|
|
|
|
|
return year + "-" + month + "-" + weekday + " " + hour + ":" + minute + ":" + second;
|
|
|
|
|
};
|
|
|
|
|
const BTreeCompName = "BehaviorTree";
|
|
|
|
|
/***
|
|
|
|
|
* 通过btclass装饰器收集到所有节点类型,然后把节点按类型分组,组成以下结构
|
|
|
|
|
* [{
|
|
|
|
|
id: NodeCategory.Composite,
|
|
|
|
|
items: [ { type: NodeType.Selector }, { type: NodeType.Selector } ],
|
|
|
|
|
expand: true}];
|
|
|
|
|
*/
|
|
|
|
|
const nodeCategory = ((nodeClsMap) => {
|
|
|
|
|
var _a;
|
|
|
|
|
let temp = new Map([
|
|
|
|
|
[node_1.Composite.name, []],
|
|
|
|
|
[node_1.Decorator.name, []],
|
|
|
|
|
[node_1.Condition.name, []],
|
|
|
|
|
[node_1.Action.name, []],
|
|
|
|
|
]);
|
|
|
|
|
let clsList = [...nodeClsMap.values()];
|
|
|
|
|
for (const cls of clsList) {
|
|
|
|
|
// 获取node的父类cls名字
|
|
|
|
|
const parentCls = getParentCls(cls);
|
|
|
|
|
if (parentCls) {
|
|
|
|
|
const parentClsName = parentCls.name;
|
|
|
|
|
(_a = temp.get(parentClsName)) === null || _a === void 0 ? void 0 : _a.push({
|
|
|
|
|
type: cls.name,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const result = [...temp.entries()].map(([id, items]) => ({
|
|
|
|
|
id,
|
|
|
|
|
items,
|
|
|
|
|
expand: true,
|
|
|
|
|
}));
|
|
|
|
|
return result;
|
|
|
|
|
})(decorator_1.nodeClsMap);
|
|
|
|
|
/***
|
|
|
|
|
* 获取所有parentNode类型的节点
|
|
|
|
|
*/
|
|
|
|
|
const ParentNodes = [...decorator_1.nodeClsMap.values()]
|
|
|
|
|
.filter((cls) => {
|
|
|
|
|
let parentCls = getParentCls(cls);
|
|
|
|
|
while (parentCls) {
|
|
|
|
|
if (parentCls.name === node_1.ParentNode.name) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
parentCls = getParentCls(parentCls);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
})
|
|
|
|
|
.map((cls) => cls.name);
|
2023-09-26 14:16:42 +00:00
|
|
|
|
/***
|
|
|
|
|
* 获取所有Composite类型的节点
|
|
|
|
|
*/
|
|
|
|
|
const CompositeNodes = [...decorator_1.nodeClsMap.values()]
|
|
|
|
|
.filter((cls) => {
|
|
|
|
|
let parentCls = getParentCls(cls);
|
|
|
|
|
while (parentCls) {
|
|
|
|
|
if (parentCls.name === node_1.Composite.name) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
parentCls = getParentCls(parentCls);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
})
|
|
|
|
|
.map((cls) => cls.name);
|
2023-09-20 17:32:11 +00:00
|
|
|
|
const activeCursor = () => {
|
|
|
|
|
stage && (stage.container().style.cursor = "pointer");
|
|
|
|
|
};
|
|
|
|
|
const defaultCursor = () => {
|
|
|
|
|
stage && (stage.container().style.cursor = "default");
|
|
|
|
|
};
|
|
|
|
|
const component = lib_1.Vue.extend({
|
|
|
|
|
template: lib_1.fs.readFileSync(path_1.default.join(__dirname, "../../../src/panels/static/template/vue/app.html"), "utf-8"),
|
|
|
|
|
$: {
|
|
|
|
|
tree: "#tree",
|
2023-09-26 14:16:42 +00:00
|
|
|
|
scroll: "#scroll",
|
2023-09-20 17:32:11 +00:00
|
|
|
|
},
|
|
|
|
|
filters: {
|
|
|
|
|
toUpperCase(value) {
|
|
|
|
|
if (!value) {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
const str = value.toString();
|
|
|
|
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
onStart: "onStart",
|
|
|
|
|
onUpdate: "onUpdate",
|
|
|
|
|
onEnd: "onEnd",
|
|
|
|
|
// 为了让模板访问,
|
|
|
|
|
AbortType: node_1.AbortType,
|
|
|
|
|
panels: ["Nodes", "Inspector"],
|
|
|
|
|
// 当前选中的panel
|
|
|
|
|
currentPanel: 0,
|
|
|
|
|
// 节点面板
|
|
|
|
|
nodeCategory,
|
|
|
|
|
// json资源列表
|
|
|
|
|
assets: [],
|
|
|
|
|
// 当前选中的json资源
|
|
|
|
|
currentAsset: null,
|
|
|
|
|
// 当前选中的节点
|
|
|
|
|
currentNode: null,
|
|
|
|
|
// 事件相关节点查询的组件和方法信息
|
|
|
|
|
/***
|
|
|
|
|
* 结构如下 {
|
|
|
|
|
* [lifecycle]:[{ uuid, methods,compName }]
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
*/
|
|
|
|
|
nodeCompMethodInfo: {},
|
|
|
|
|
logs: [],
|
2023-09-26 14:16:42 +00:00
|
|
|
|
maskText: "",
|
2023-09-20 17:32:11 +00:00
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
nodes() {
|
|
|
|
|
if (this.currentAsset) {
|
|
|
|
|
return this.currentAsset.content.nodes;
|
|
|
|
|
}
|
|
|
|
|
return [];
|
|
|
|
|
},
|
|
|
|
|
nodeMap() {
|
|
|
|
|
const map = {};
|
|
|
|
|
for (const node of this.nodes) {
|
|
|
|
|
map[node.id] = node;
|
|
|
|
|
}
|
|
|
|
|
return map;
|
|
|
|
|
},
|
|
|
|
|
// key是节点id,value是前序索引
|
|
|
|
|
preOrderIndexMap() {
|
|
|
|
|
const root = (0, utils_1.buildTree)(this.nodes);
|
|
|
|
|
if (!root) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
const temp = (0, utils_1.preOrder)(root);
|
|
|
|
|
const map = temp.reduce((total, item, index) => {
|
|
|
|
|
total[item.id] = index;
|
|
|
|
|
return total;
|
|
|
|
|
}, {});
|
|
|
|
|
return map;
|
|
|
|
|
},
|
|
|
|
|
abortTypeList() {
|
|
|
|
|
// 解决数字枚举编译成js以后,生成两份key的问题
|
|
|
|
|
return Object.entries(this.AbortType)
|
|
|
|
|
.filter((e) => !Number.isInteger(Number(e[0])))
|
|
|
|
|
.map((e) => ({ label: e[0], value: e[1] }));
|
|
|
|
|
},
|
|
|
|
|
lifeCycleComponents() {
|
|
|
|
|
return [this.onStart, this.onUpdate, this.onEnd].reduce((total, lifeCycle) => {
|
|
|
|
|
let res = [];
|
|
|
|
|
if (this.currentNode) {
|
|
|
|
|
const isSelectNode = Boolean(this.currentNode.event[lifeCycle].node);
|
|
|
|
|
if (isSelectNode) {
|
|
|
|
|
res = this.nodeCompMethodInfo[lifeCycle] || [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
total[lifeCycle] = res;
|
|
|
|
|
return total;
|
|
|
|
|
}, {});
|
|
|
|
|
},
|
|
|
|
|
lifeCycleMethods() {
|
|
|
|
|
return [this.onStart, this.onUpdate, this.onEnd].reduce((total, lifeCycle) => {
|
|
|
|
|
var _a, _b;
|
|
|
|
|
let res = [];
|
|
|
|
|
if (this.currentNode) {
|
|
|
|
|
const compUuid = (_a = this.currentNode.event[lifeCycle]) === null || _a === void 0 ? void 0 : _a.comp;
|
|
|
|
|
if (compUuid && this.nodeCompMethodInfo[lifeCycle]) {
|
|
|
|
|
res = (_b = this.nodeCompMethodInfo[lifeCycle].find((e) => e.uuid === compUuid)) === null || _b === void 0 ? void 0 : _b.methods;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
total[lifeCycle] = res;
|
|
|
|
|
return total;
|
|
|
|
|
}, {});
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
async mounted() {
|
2023-09-26 14:16:42 +00:00
|
|
|
|
this.init();
|
2023-09-20 17:32:11 +00:00
|
|
|
|
},
|
|
|
|
|
methods: {
|
2023-09-26 14:16:42 +00:00
|
|
|
|
/***
|
|
|
|
|
* 初始化相关
|
|
|
|
|
*/
|
|
|
|
|
async init() {
|
|
|
|
|
// 初始化画布
|
|
|
|
|
this.initCanvas();
|
|
|
|
|
// 根据当前选中节点初始化行为树
|
|
|
|
|
await this.initSelection();
|
|
|
|
|
this.backRoot();
|
|
|
|
|
},
|
|
|
|
|
initCanvas() {
|
|
|
|
|
// 初始化舞台
|
|
|
|
|
stage = new lib_1.Konva.Stage({
|
|
|
|
|
container: this.$refs.tree,
|
|
|
|
|
width: CANVAS_WIDTH,
|
|
|
|
|
height: CANVAS_HEIGHT,
|
|
|
|
|
});
|
|
|
|
|
// 基础层,目前只有一层,每层都会生成一个canvas
|
|
|
|
|
baseLayer = new lib_1.Konva.Layer();
|
|
|
|
|
// 背景组
|
|
|
|
|
bgGroup = this.generateBg();
|
|
|
|
|
baseLayer.add(bgGroup);
|
|
|
|
|
// 静态箭头层
|
|
|
|
|
staticArrowGroup = new lib_1.Konva.Group();
|
|
|
|
|
baseLayer.add(staticArrowGroup);
|
|
|
|
|
// 动态箭头层(用户手动拉出来的箭头)
|
|
|
|
|
dynamicArrowGroup = this.generateDynamicArrowGroup();
|
|
|
|
|
baseLayer.add(dynamicArrowGroup);
|
|
|
|
|
// Root组
|
|
|
|
|
rootGroup = this.generateRoot();
|
|
|
|
|
baseLayer.add(rootGroup);
|
|
|
|
|
// 节点层
|
|
|
|
|
nodeGroup = new lib_1.Konva.Group();
|
|
|
|
|
baseLayer.add(nodeGroup);
|
|
|
|
|
// 框选层
|
|
|
|
|
selectionGroup = this.generatesSelectionGroup();
|
|
|
|
|
baseLayer.add(selectionGroup);
|
|
|
|
|
// 层添加到舞台
|
|
|
|
|
stage.add(baseLayer);
|
|
|
|
|
},
|
|
|
|
|
// 获取json文件
|
|
|
|
|
async initAssets() {
|
|
|
|
|
const behaviorTreeComponentUuids = [];
|
|
|
|
|
const dfs = (node) => {
|
|
|
|
|
if (!node) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (const comp of node.components) {
|
|
|
|
|
if (comp.type === BTreeCompName) {
|
|
|
|
|
behaviorTreeComponentUuids.push(comp.value);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const item of node.children) {
|
|
|
|
|
dfs(item);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
// 获取场景节点树
|
|
|
|
|
const sceneNode = await Editor.Message.request("scene", "query-node-tree");
|
|
|
|
|
// 收集场景上所有BehaviorTree组件uuid
|
|
|
|
|
dfs(sceneNode);
|
|
|
|
|
// 收集BehaviorTree组件上的json路径
|
|
|
|
|
const rawUrls = await Promise.all(behaviorTreeComponentUuids.map((uuid) => Editor.Message.request("scene", "execute-component-method", {
|
|
|
|
|
uuid: uuid,
|
|
|
|
|
name: "getAssetUrl",
|
|
|
|
|
})));
|
|
|
|
|
// 过滤空的并去重
|
|
|
|
|
const urls = [...new Set(rawUrls.filter(Boolean))];
|
|
|
|
|
// 根据url获取所有json文件信息
|
|
|
|
|
const assets = await Promise.all(urls.map((url) => Editor.Message.request("asset-db", "query-asset-info", url)));
|
|
|
|
|
this.assets = assets.map(({ name, source, file }) => ({ name, url: source, file: file, content: "" }));
|
|
|
|
|
},
|
|
|
|
|
async initSelection() {
|
|
|
|
|
// 找到当前选中的节点
|
|
|
|
|
const node = await Editor.Message.request("scene", "query-node", Editor.Selection.getSelected("node"));
|
|
|
|
|
// 未选中节点或者选中的是场景节点(场景节点不能添加组件)
|
|
|
|
|
if (!(node === null || node === void 0 ? void 0 : node.__comps__)) {
|
|
|
|
|
this.maskText = "请选中一个非场景根节点非空名称节点来开始行为树制作";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 找到BehaviorTree组件
|
|
|
|
|
const index = node.__comps__.findIndex((v) => v.type === BTreeCompName);
|
|
|
|
|
if (index === -1) {
|
|
|
|
|
this.maskText = "要制作行为树,需要先为当前节点添加行为树组件";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const comp = node.__comps__[index];
|
|
|
|
|
// 调用BehaviorTree组件上的方法
|
|
|
|
|
const url = await Editor.Message.request("scene", "execute-component-method", {
|
|
|
|
|
uuid: comp.value.uuid.value,
|
|
|
|
|
name: "getAssetUrl",
|
|
|
|
|
});
|
|
|
|
|
// JSON文件不存在
|
|
|
|
|
if (!url) {
|
|
|
|
|
this.maskText = "当前行为树组件缺少JSON资源,点击BehaviorEditor组件按钮即可创建";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.maskText = "";
|
|
|
|
|
// 选中此文件,设置好currentAsset才能把组件nodes数据同步到JSON
|
|
|
|
|
this.handleSelectAsset(url);
|
|
|
|
|
},
|
|
|
|
|
async handleSelectAsset(url) {
|
|
|
|
|
var _a;
|
|
|
|
|
// 实时获取当前场景所有behaviorTree组件上的json文件
|
|
|
|
|
await this.initAssets();
|
|
|
|
|
const json = this.assets.find((e) => e.url === url);
|
|
|
|
|
if (!json) {
|
|
|
|
|
this.showWarn("JSON文件不存在");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (((_a = this.currentAsset) === null || _a === void 0 ? void 0 : _a.url) === (json === null || json === void 0 ? void 0 : json.url)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.currentAsset = json;
|
|
|
|
|
try {
|
|
|
|
|
const content = await lib_1.fs.readJSONSync(json.file);
|
|
|
|
|
this.currentAsset.content = content;
|
|
|
|
|
this.render();
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
if (e instanceof SyntaxError) {
|
|
|
|
|
// JSON文件语法异常的情况下,初始化内容
|
|
|
|
|
this.showWarn("JSON文件内容初始化成功");
|
|
|
|
|
const content = { nodes: [] };
|
|
|
|
|
await lib_1.fs.writeJSONSync(json.file, content);
|
|
|
|
|
this.currentAsset.content = content;
|
|
|
|
|
this.render();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// 输出其他异常
|
|
|
|
|
this.showWarn(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
2023-09-20 17:32:11 +00:00
|
|
|
|
handlePanelChange(value) {
|
|
|
|
|
this.currentPanel = value;
|
|
|
|
|
},
|
|
|
|
|
handleNameChange(value) {
|
|
|
|
|
if (this.currentNode) {
|
|
|
|
|
this.currentNode.name = value;
|
|
|
|
|
// 需要立刻渲染节点,并防抖存储
|
|
|
|
|
this.render(true, true);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
handleAbortTypeChange(value) {
|
|
|
|
|
if (this.currentNode) {
|
|
|
|
|
this.currentNode.abortType = Number(value);
|
|
|
|
|
this.render();
|
|
|
|
|
}
|
|
|
|
|
},
|
2023-09-26 14:16:42 +00:00
|
|
|
|
async handleEventNodeChange(lifeCycle, uuid = "", shouldSave = true) {
|
2023-09-20 17:32:11 +00:00
|
|
|
|
if (!this.currentNode) {
|
|
|
|
|
this.showWarn("当前节点不存在");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.currentNode.event[lifeCycle].node = uuid;
|
|
|
|
|
if (uuid) {
|
|
|
|
|
await this.getNodeCompMethods(lifeCycle, uuid);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
this.currentNode.event[lifeCycle].comp = "";
|
|
|
|
|
this.currentNode.event[lifeCycle].method = "";
|
2023-09-26 14:16:42 +00:00
|
|
|
|
// 事件参数就不手动清空了
|
|
|
|
|
// this.currentNode.event[lifeCycle].data = "";
|
|
|
|
|
}
|
|
|
|
|
// 点击canvas某个节点的时候,会触发handleEventNodeChange获取场景节点数据,此时不保存数据
|
|
|
|
|
if (shouldSave) {
|
|
|
|
|
await this.saveAsset();
|
2023-09-20 17:32:11 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async getNodeCompMethods(lifeCycle, uuid) {
|
|
|
|
|
const [nodeInfo, compMethodInfo] = await Promise.all([
|
|
|
|
|
Editor.Message.request("scene", "query-node", uuid),
|
|
|
|
|
Editor.Message.request("scene", "query-component-function-of-node", uuid),
|
|
|
|
|
]);
|
|
|
|
|
if (!nodeInfo || !compMethodInfo) {
|
|
|
|
|
this.showWarn("节点信息不存在");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/***
|
|
|
|
|
* compMethodInfo的组件信息只有组件名,需要获取组件的uuid
|
|
|
|
|
* 组成这样的格式
|
|
|
|
|
*/
|
|
|
|
|
const compsMethods = nodeInfo.__comps__.map((comp) => {
|
|
|
|
|
const name = comp.type;
|
|
|
|
|
const uuid = comp.value.uuid.value;
|
|
|
|
|
const methods = compMethodInfo[name];
|
|
|
|
|
return {
|
|
|
|
|
name,
|
|
|
|
|
uuid,
|
|
|
|
|
methods,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
this.nodeCompMethodInfo = Object.assign(Object.assign({}, this.nodeCompMethodInfo), { [lifeCycle]: compsMethods });
|
|
|
|
|
},
|
|
|
|
|
async handleEventCompChange(lifeCycle, uuid) {
|
|
|
|
|
this.currentNode.event[lifeCycle].comp = uuid;
|
|
|
|
|
if (!uuid) {
|
|
|
|
|
this.currentNode.event[lifeCycle].method = "";
|
|
|
|
|
}
|
|
|
|
|
await this.saveAsset();
|
|
|
|
|
},
|
|
|
|
|
async handleEventMethodChange(lifeCycle, method) {
|
|
|
|
|
this.currentNode.event[lifeCycle].method = method;
|
|
|
|
|
await this.saveAsset();
|
|
|
|
|
},
|
|
|
|
|
async handleEventDataChange(lifeCycle, data) {
|
|
|
|
|
this.currentNode.event[lifeCycle].data = data;
|
|
|
|
|
await this.saveAsset();
|
|
|
|
|
},
|
|
|
|
|
async saveAsset() {
|
|
|
|
|
var _a;
|
2023-09-26 14:16:42 +00:00
|
|
|
|
const file = (_a = this.currentAsset) === null || _a === void 0 ? void 0 : _a.file;
|
|
|
|
|
if (!file) {
|
2023-09-20 17:32:11 +00:00
|
|
|
|
this.showWarn("数据存储失败,未指定JSON");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-09-26 14:16:42 +00:00
|
|
|
|
const content = this.currentAsset.content;
|
2023-09-20 17:32:11 +00:00
|
|
|
|
// 修改json文件
|
2023-09-26 14:16:42 +00:00
|
|
|
|
lib_1.fs.writeJSONSync(file, content);
|
2023-09-20 17:32:11 +00:00
|
|
|
|
},
|
|
|
|
|
/***
|
|
|
|
|
* 渲染画布
|
|
|
|
|
* renderNode:拖拽的时候不渲染节点
|
|
|
|
|
* debounceSave:防抖存储,用在移动和高频input中
|
|
|
|
|
*/
|
|
|
|
|
render(renderNode = true, debounceSave = false) {
|
|
|
|
|
staticArrowGroup.destroyChildren();
|
|
|
|
|
for (const node of this.nodes) {
|
|
|
|
|
this.renderArrow(node);
|
|
|
|
|
}
|
|
|
|
|
clearTimeout(SAVE_DEBOUNCE_TIMER);
|
|
|
|
|
// 渲染节点
|
|
|
|
|
if (renderNode) {
|
|
|
|
|
nodeGroup.destroyChildren();
|
|
|
|
|
for (const node of this.nodes) {
|
|
|
|
|
this.renderNode(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 防抖存储
|
|
|
|
|
if (debounceSave) {
|
|
|
|
|
SAVE_DEBOUNCE_TIMER = setTimeout(() => {
|
|
|
|
|
SAVE_DEBOUNCE_TIMER = null;
|
|
|
|
|
this.saveAsset();
|
|
|
|
|
}, DEBOUNCE_TIME);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
this.saveAsset();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
renderNode(node) {
|
|
|
|
|
// 节点组
|
|
|
|
|
const wrapper = this.generateWrapper(node);
|
|
|
|
|
// 节点盒子
|
|
|
|
|
const box = this.generateBox(node);
|
|
|
|
|
// 节点主要内容
|
|
|
|
|
const main = this.generateMain(node);
|
|
|
|
|
// 节点文本
|
|
|
|
|
const [name, type] = this.generateText(node);
|
|
|
|
|
// 节点上下面板
|
|
|
|
|
const [panelStatic, panelDynamic] = this.generatePanel(node);
|
|
|
|
|
// 中断类型icon
|
|
|
|
|
const abortGroup = this.generateAbort(node);
|
|
|
|
|
// 移除节点按钮
|
|
|
|
|
const removeGroup = this.generateRemove(node);
|
|
|
|
|
// 断开箭头按钮
|
|
|
|
|
const breakGroup = this.generateBreak(node);
|
|
|
|
|
wrapper.add(box);
|
|
|
|
|
wrapper.add(main);
|
|
|
|
|
wrapper.add(name);
|
|
|
|
|
wrapper.add(type);
|
|
|
|
|
// 继承ParentNode类型的节点才能显示设置子节点面板
|
|
|
|
|
if (ParentNodes.includes(node.type)) {
|
|
|
|
|
wrapper.add(panelStatic);
|
|
|
|
|
wrapper.add(panelDynamic);
|
|
|
|
|
}
|
|
|
|
|
wrapper.add(abortGroup);
|
|
|
|
|
wrapper.add(removeGroup);
|
|
|
|
|
wrapper.add(breakGroup);
|
|
|
|
|
removeGroup.visible(false);
|
|
|
|
|
breakGroup.visible(false);
|
|
|
|
|
const parentNode = this.getParentNode(node);
|
|
|
|
|
// 有父节点或者是Root节点的时候,渲染break按钮
|
|
|
|
|
const showBreak = Boolean(parentNode || node.isRoot);
|
|
|
|
|
wrapper.on("mouseover", () => {
|
|
|
|
|
!removeGroup.visible() && removeGroup.visible(true);
|
|
|
|
|
!breakGroup.visible() && showBreak && breakGroup.visible(true);
|
|
|
|
|
});
|
|
|
|
|
wrapper.on("mouseout", () => {
|
|
|
|
|
removeGroup.visible() && removeGroup.visible(false);
|
|
|
|
|
breakGroup.visible() && showBreak && breakGroup.visible(false);
|
|
|
|
|
});
|
|
|
|
|
nodeGroup.add(wrapper);
|
|
|
|
|
},
|
|
|
|
|
/***
|
|
|
|
|
* 渲染从父节点指向子节点的箭头
|
|
|
|
|
* getArrowStartXY传入父节点,getArrowEndXY传入子节点
|
|
|
|
|
*/
|
|
|
|
|
renderArrow(node) {
|
|
|
|
|
const commonCfg = {
|
|
|
|
|
pointerLength: ARROW_SIZE,
|
|
|
|
|
pointerWidth: ARROW_SIZE,
|
|
|
|
|
fill: ARROW_FILL,
|
|
|
|
|
stroke: ARROW_FILL,
|
|
|
|
|
strokeWidth: ARROW_LINE_SIZE,
|
|
|
|
|
};
|
|
|
|
|
// isRoot标识为true的节点,多渲染一根从Root指向该节点的箭头
|
|
|
|
|
if (node.isRoot) {
|
|
|
|
|
const arrowStartXY = this.getArrowStartXY({
|
|
|
|
|
x: ROOT_X,
|
|
|
|
|
y: ROOT_Y,
|
|
|
|
|
});
|
|
|
|
|
const arrowEndXY = this.getArrowEndXY(node);
|
|
|
|
|
const arrow = new lib_1.Konva.Arrow(Object.assign(Object.assign(Object.assign({}, arrowStartXY), { points: [0, 0, arrowEndXY.x - arrowStartXY.x, arrowEndXY.y - arrowStartXY.y] }), commonCfg));
|
|
|
|
|
staticArrowGroup.add(arrow);
|
|
|
|
|
}
|
|
|
|
|
for (const childId of node.children) {
|
|
|
|
|
const childNode = this.nodeMap[childId];
|
|
|
|
|
const arrowStartXY = this.getArrowStartXY(node);
|
|
|
|
|
const arrowEndXY = this.getArrowEndXY(childNode);
|
|
|
|
|
const arrow = new lib_1.Konva.Arrow(Object.assign(Object.assign(Object.assign({}, arrowStartXY), { points: [0, 0, arrowEndXY.x - arrowStartXY.x, arrowEndXY.y - arrowStartXY.y] }), commonCfg));
|
|
|
|
|
staticArrowGroup.add(arrow);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
addNode(type) {
|
|
|
|
|
const node = {
|
|
|
|
|
id: uuid(),
|
|
|
|
|
name: type,
|
|
|
|
|
type,
|
|
|
|
|
abortType: this.AbortType.None,
|
|
|
|
|
x: (CANVAS_WIDTH - BOX_WIDTH) / 2,
|
2023-09-26 14:16:42 +00:00
|
|
|
|
y: ROOT_Y + 100,
|
2023-09-20 17:32:11 +00:00
|
|
|
|
isRoot: false,
|
|
|
|
|
children: [],
|
|
|
|
|
event: {
|
|
|
|
|
onStart: { node: "", comp: "", method: "", data: "" },
|
|
|
|
|
onUpdate: { node: "", comp: "", method: "", data: "" },
|
|
|
|
|
onEnd: { node: "", comp: "", method: "", data: "" },
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
this.nodes.push(node);
|
|
|
|
|
this.render();
|
|
|
|
|
},
|
|
|
|
|
removeNode(node) {
|
|
|
|
|
const removeId = node.id;
|
|
|
|
|
// 倒序删除
|
|
|
|
|
for (let i = this.nodes.length - 1; i >= 0; i--) {
|
|
|
|
|
const item = this.nodes[i];
|
|
|
|
|
// 删除当前节点
|
|
|
|
|
if (item.id === removeId) {
|
|
|
|
|
this.nodes.splice(i, 1);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// 删除关系节点
|
|
|
|
|
item.children = item.children.filter((childId) => childId !== removeId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.render();
|
|
|
|
|
if (this.currentNode === node) {
|
|
|
|
|
this.currentNode = null;
|
|
|
|
|
}
|
|
|
|
|
defaultCursor();
|
|
|
|
|
},
|
|
|
|
|
removeParent(node) {
|
|
|
|
|
// 根节点的话,重置isRoot标识
|
|
|
|
|
if (node.isRoot) {
|
|
|
|
|
node.isRoot = false;
|
|
|
|
|
this.render();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const parentNode = this.getParentNode(node);
|
|
|
|
|
if (parentNode) {
|
|
|
|
|
const index = parentNode.children.findIndex((childId) => childId === node.id);
|
|
|
|
|
index > -1 && parentNode.children.splice(index, 1);
|
|
|
|
|
this.render();
|
|
|
|
|
}
|
|
|
|
|
defaultCursor();
|
|
|
|
|
},
|
|
|
|
|
/***
|
|
|
|
|
* 设置子节点
|
|
|
|
|
* node参数是父节点,通过e获取需要设置的子节点
|
|
|
|
|
*/
|
|
|
|
|
setChild(e, node) {
|
|
|
|
|
const { x, y } = stage.getPointerPosition();
|
|
|
|
|
// PS:hitNode不可能是Root UI节点,因为Root UI是虚构的,不存在 this.nodes中
|
|
|
|
|
const hitNode = this.nodes.find((node) => {
|
|
|
|
|
const targetX = node.x;
|
|
|
|
|
const targetY = node.y;
|
|
|
|
|
return hit(x, y, targetX, targetY, BOX_WIDTH, this.getBoxHeight(node));
|
|
|
|
|
});
|
|
|
|
|
// 没有命中节点
|
|
|
|
|
if (!hitNode) {
|
|
|
|
|
this.showWarn("未命中节点");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 从Root节点拉出来的箭头,命中普通节点
|
|
|
|
|
if (!node.id) {
|
|
|
|
|
// 命中节点已经是Root节点
|
|
|
|
|
if (hitNode.isRoot) {
|
|
|
|
|
this.showWarn("命中节点已经是Root节点");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 删除命中节点原来的关系
|
|
|
|
|
this.removeParent(hitNode);
|
|
|
|
|
// 重置所有isRoot标识
|
|
|
|
|
this.nodes.map((e) => (e.isRoot = false));
|
|
|
|
|
// 设置命中节点Root标识
|
|
|
|
|
hitNode.isRoot = true;
|
|
|
|
|
this.render();
|
|
|
|
|
// 普通节点拉出来的箭头命中普通节点
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// 命中节点的所有子孙节点(包括命中节点)包含当前节点,会造成循环
|
|
|
|
|
const dirty = this.getAllChildrenNode(hitNode).some((v) => v.id === node.id);
|
|
|
|
|
if (dirty) {
|
|
|
|
|
this.showWarn("存在循环");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 命中节点已是当前节点子节点
|
|
|
|
|
const parentHitNode = this.getParentNode(hitNode);
|
|
|
|
|
if ((parentHitNode === null || parentHitNode === void 0 ? void 0 : parentHitNode.id) === node.id) {
|
|
|
|
|
this.showWarn("命中节点已是当前节点子节点");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 删除命中节点原来的关系
|
|
|
|
|
this.removeParent(hitNode);
|
|
|
|
|
// 设置新关系
|
|
|
|
|
node.children.push(hitNode.id);
|
|
|
|
|
// 根据x坐标对子节点排序
|
|
|
|
|
this.sortNodeChildren(node);
|
|
|
|
|
this.render();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
getParentNode(node) {
|
|
|
|
|
return this.nodes.find((v) => v.children.includes(node.id));
|
|
|
|
|
},
|
|
|
|
|
sortNodeChildren(node) {
|
|
|
|
|
node.children.sort((a, b) => this.nodeMap[a].x - this.nodeMap[b].x);
|
|
|
|
|
},
|
|
|
|
|
// 获取所有子孙节点
|
|
|
|
|
getAllChildrenNode(node) {
|
|
|
|
|
if (!node) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
let result = [];
|
|
|
|
|
const dfs = (node) => {
|
|
|
|
|
if (!node) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
result.push(node);
|
|
|
|
|
for (const nodeId of node.children) {
|
|
|
|
|
const child = this.nodeMap[nodeId];
|
|
|
|
|
dfs(child);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
dfs(node);
|
|
|
|
|
return result;
|
|
|
|
|
},
|
|
|
|
|
/***
|
|
|
|
|
* 移动节点,把xy保存到数据中,并重新渲染箭头
|
|
|
|
|
*/
|
|
|
|
|
handleNodeMove(shape) {
|
|
|
|
|
const id = shape.attrs.id;
|
|
|
|
|
if (!id) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const node = this.nodeMap[id];
|
|
|
|
|
node.x = shape.attrs.x;
|
|
|
|
|
node.y = shape.attrs.y;
|
|
|
|
|
const parentNode = this.getParentNode(node);
|
|
|
|
|
// 排序
|
|
|
|
|
if (parentNode) {
|
|
|
|
|
this.sortNodeChildren(parentNode);
|
|
|
|
|
}
|
|
|
|
|
this.render(false, true);
|
|
|
|
|
},
|
|
|
|
|
getArrowStartXY(node) {
|
|
|
|
|
return {
|
|
|
|
|
x: node.x + BOX_WIDTH / 2,
|
|
|
|
|
y: node.y + this.getBoxHeight(node) - PANEL_HEIGHT / 2,
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
getArrowEndXY(childNode) {
|
|
|
|
|
const childCenter = childNode.x + BOX_WIDTH / 2;
|
|
|
|
|
return {
|
|
|
|
|
x: childCenter,
|
|
|
|
|
y: childNode.y - 2,
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
resetBoxBorder() {
|
|
|
|
|
for (const child of nodeGroup.children) {
|
|
|
|
|
child.children[0].setAttrs({
|
|
|
|
|
stroke: BOX_FILL,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
getNodeCenter(node) {
|
|
|
|
|
return {
|
|
|
|
|
x: node.x + BOX_WIDTH / 2,
|
|
|
|
|
y: node.y + this.getBoxHeight(node) / 2,
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
/***
|
|
|
|
|
* 设置背景和辅助线
|
|
|
|
|
*/
|
|
|
|
|
generateBg() {
|
|
|
|
|
const group = new lib_1.Konva.Group();
|
|
|
|
|
const bg = new lib_1.Konva.Rect({
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
fill: "#262626",
|
|
|
|
|
width: CANVAS_WIDTH,
|
|
|
|
|
height: CANVAS_HEIGHT,
|
|
|
|
|
id: "bg", //可以通过api find()找到这个节点
|
|
|
|
|
});
|
|
|
|
|
group.add(bg);
|
|
|
|
|
// 辅助线
|
|
|
|
|
for (let i = 0; i < CANVAS_WIDTH / LINE_GAP; i++) {
|
|
|
|
|
const y = i * LINE_GAP;
|
|
|
|
|
const x = i * LINE_GAP;
|
|
|
|
|
if (i % 8 === 0) {
|
|
|
|
|
const lineCfg = {
|
|
|
|
|
stroke: "#000000",
|
|
|
|
|
strokeWidth: 1,
|
|
|
|
|
};
|
|
|
|
|
const rowLine = new lib_1.Konva.Line(Object.assign(Object.assign({}, lineCfg), { points: [0, y, CANVAS_WIDTH, y] }));
|
|
|
|
|
const columnLine = new lib_1.Konva.Line(Object.assign(Object.assign({}, lineCfg), { points: [x, 0, x, CANVAS_HEIGHT] }));
|
|
|
|
|
group.add(rowLine);
|
|
|
|
|
group.add(columnLine);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const lineCfg = {
|
|
|
|
|
stroke: "#343434",
|
|
|
|
|
strokeWidth: 1,
|
|
|
|
|
};
|
|
|
|
|
const rowLine = new lib_1.Konva.Line(Object.assign(Object.assign({}, lineCfg), { points: [0, y, CANVAS_WIDTH, y] }));
|
|
|
|
|
const columnLine = new lib_1.Konva.Line(Object.assign(Object.assign({}, lineCfg), { points: [x, 0, x, CANVAS_HEIGHT] }));
|
|
|
|
|
group.add(rowLine);
|
|
|
|
|
group.add(columnLine);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return group;
|
|
|
|
|
},
|
|
|
|
|
generateDynamicArrowGroup() {
|
|
|
|
|
const group = new lib_1.Konva.Group();
|
|
|
|
|
dynamicArrow = new lib_1.Konva.Arrow({
|
|
|
|
|
points: [0, 0, 0, 30],
|
|
|
|
|
pointerLength: ARROW_SIZE,
|
|
|
|
|
pointerWidth: ARROW_SIZE,
|
|
|
|
|
fill: ARROW_FILL,
|
|
|
|
|
stroke: ARROW_FILL,
|
|
|
|
|
strokeWidth: ARROW_LINE_SIZE,
|
|
|
|
|
visible: false,
|
|
|
|
|
});
|
|
|
|
|
// 动态箭头离开了canvas,松手的时候隐藏箭头
|
|
|
|
|
stage.on("mouseleave", () => {
|
|
|
|
|
dynamicArrow.visible(false);
|
|
|
|
|
});
|
|
|
|
|
group.add(dynamicArrow);
|
|
|
|
|
return group;
|
|
|
|
|
},
|
|
|
|
|
generatesSelectionGroup() {
|
|
|
|
|
const group = new lib_1.Konva.Group();
|
|
|
|
|
const selection = new lib_1.Konva.Rect({
|
|
|
|
|
fill: "rgba(0,0,0,0.3)",
|
|
|
|
|
visible: false,
|
|
|
|
|
});
|
|
|
|
|
let x1, y1, x2, y2;
|
|
|
|
|
stage.on("mousedown", (e) => {
|
|
|
|
|
e.evt.preventDefault();
|
|
|
|
|
if (e.evt.button === 1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const source = e.target;
|
|
|
|
|
const target = stage.find("#bg")[0];
|
|
|
|
|
// 点击的是背景,则开始框选逻辑
|
|
|
|
|
if (source !== target) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
x1 = x2 = stage.getPointerPosition().x;
|
|
|
|
|
y1 = y2 = stage.getPointerPosition().y;
|
|
|
|
|
selection.visible(true);
|
|
|
|
|
selection.width(0);
|
|
|
|
|
selection.height(0);
|
|
|
|
|
});
|
|
|
|
|
stage.on("mousemove", (e) => {
|
|
|
|
|
e.evt.preventDefault();
|
|
|
|
|
// 按下中间则拖拽
|
|
|
|
|
if (e.evt.buttons === 4) {
|
|
|
|
|
const movementX = e.evt.movementX;
|
|
|
|
|
const movementY = e.evt.movementY;
|
2023-09-26 14:16:42 +00:00
|
|
|
|
this.$refs.scroll.scrollBy(-movementX, -movementY);
|
2023-09-20 17:32:11 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!selection.visible()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
e.evt.preventDefault();
|
|
|
|
|
x2 = stage.getPointerPosition().x;
|
|
|
|
|
y2 = stage.getPointerPosition().y;
|
|
|
|
|
selection.setAttrs({
|
|
|
|
|
x: Math.min(x1, x2),
|
|
|
|
|
y: Math.min(y1, y2),
|
|
|
|
|
width: Math.abs(x2 - x1),
|
|
|
|
|
height: Math.abs(y2 - y1),
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
stage.on("mouseup", (e) => {
|
|
|
|
|
e.evt.preventDefault();
|
|
|
|
|
// 中键
|
|
|
|
|
if (e.evt.button === 1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 所有点击都会冒泡上来
|
|
|
|
|
if (!selection.visible()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const boxes = stage.find(".box");
|
|
|
|
|
const clientRect = selection.getClientRect();
|
|
|
|
|
selectedBoxes = boxes.filter((box) => lib_1.Konva.Util.haveIntersection(clientRect, box.getClientRect({ skipShadow: true })));
|
|
|
|
|
this.resetBoxBorder();
|
|
|
|
|
if (selection.width() !== 0 && selection.height() !== 0 && selectedBoxes.length) {
|
|
|
|
|
selectedBoxes.forEach((node) => {
|
|
|
|
|
node.stroke(BOX_FILL_ACTIVE);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// 没有框选到目标重置激活状态
|
|
|
|
|
this.currentNode = null;
|
|
|
|
|
this.handlePanelChange(0);
|
|
|
|
|
// 每次点击画布都重新渲染,保证最新
|
|
|
|
|
this.render();
|
|
|
|
|
}
|
|
|
|
|
selection.visible(false);
|
|
|
|
|
});
|
|
|
|
|
group.add(selection);
|
|
|
|
|
return group;
|
|
|
|
|
},
|
|
|
|
|
/***
|
|
|
|
|
* Root节点本身并没有实际意义,只是会指向节点中isRoot标识为true的节点
|
|
|
|
|
*/
|
|
|
|
|
generateRoot() {
|
|
|
|
|
// 静态Root节点单独一组,因为要盖住staticArrowGroup,而且nodeGroup每次渲染都会清空
|
|
|
|
|
const group = new lib_1.Konva.Group();
|
|
|
|
|
// 根节点虚构一个node结构,node没有id的话,就是Root节点
|
|
|
|
|
const node = {
|
|
|
|
|
x: ROOT_X,
|
|
|
|
|
y: ROOT_Y,
|
|
|
|
|
};
|
|
|
|
|
const wrapper = new lib_1.Konva.Group(Object.assign({}, node));
|
|
|
|
|
const box = new lib_1.Konva.Rect({
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
width: BOX_WIDTH,
|
|
|
|
|
height: this.getBoxHeight(node),
|
|
|
|
|
fill: BOX_FILL,
|
|
|
|
|
stroke: BOX_FILL,
|
|
|
|
|
strokeWidth: BOX_BORDER_WIDTH,
|
|
|
|
|
cornerRadius: BORDER_RADIUS,
|
|
|
|
|
shadowColor: "#111111",
|
|
|
|
|
shadowBlur: 16,
|
|
|
|
|
shadowOffset: { x: 2, y: 2 },
|
|
|
|
|
});
|
|
|
|
|
const mainX = (BOX_WIDTH - MAIN_WIDTH) / 2;
|
|
|
|
|
const main = new lib_1.Konva.Rect({
|
|
|
|
|
x: mainX,
|
|
|
|
|
y: MAIN_GAP,
|
|
|
|
|
width: MAIN_WIDTH,
|
|
|
|
|
height: MAIN_HEIGHT,
|
|
|
|
|
fill: MAIN_FILL,
|
|
|
|
|
stroke: MAIN_BORDER_COLOR,
|
|
|
|
|
strokeWidth: MAIN_BORDER_WIDTH,
|
|
|
|
|
cornerRadius: MAIN_BORDER_RADIUS,
|
|
|
|
|
});
|
|
|
|
|
const name = new lib_1.Konva.Text({
|
|
|
|
|
x: mainX + TEXT_PADDING,
|
|
|
|
|
text: "Root",
|
|
|
|
|
fontSize: NAME_SIZE,
|
|
|
|
|
fill: TEXT_FILL,
|
|
|
|
|
width: MAIN_WIDTH,
|
|
|
|
|
height: NAME_SIZE,
|
|
|
|
|
});
|
|
|
|
|
// 文字的大小会影响高度,所以动态设置
|
|
|
|
|
name.setAttrs({
|
|
|
|
|
y: MAIN_GAP * 6 - name.height(),
|
|
|
|
|
});
|
|
|
|
|
const [panelStatic, panelDynamic] = this.generatePanel(Object.assign({}, node));
|
|
|
|
|
wrapper.add(box);
|
|
|
|
|
wrapper.add(main);
|
|
|
|
|
wrapper.add(name);
|
|
|
|
|
wrapper.add(panelStatic);
|
|
|
|
|
wrapper.add(panelDynamic);
|
|
|
|
|
group.add(wrapper);
|
|
|
|
|
return group;
|
|
|
|
|
},
|
|
|
|
|
generateWrapper(node) {
|
|
|
|
|
const BOX_HEIGHT = this.getBoxHeight(node);
|
|
|
|
|
const wrapper = new lib_1.Konva.Group({
|
|
|
|
|
id: node.id,
|
|
|
|
|
x: node.x,
|
|
|
|
|
y: node.y,
|
|
|
|
|
draggable: true,
|
|
|
|
|
dragBoundFunc(currentNodePos) {
|
|
|
|
|
return {
|
|
|
|
|
x: clamp(currentNodePos.x, CANVAS_WIDTH - BOX_WIDTH, 0),
|
|
|
|
|
y: clamp(currentNodePos.y, CANVAS_HEIGHT - BOX_HEIGHT, 0),
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
wrapper.on("dragmove", (e) => {
|
|
|
|
|
wrapper.zIndex(nodeGroup.children.length - 1);
|
|
|
|
|
const movementX = e.evt.movementX;
|
|
|
|
|
const movementY = e.evt.movementY;
|
|
|
|
|
const curMoveShape = e.target;
|
|
|
|
|
// selectedBoxes里存的是box,curMoveShape(e.target)是wrapper
|
|
|
|
|
const selectWrapper = selectedBoxes.map((e) => e.parent);
|
|
|
|
|
const isCurShapeInSelectWrapper = selectWrapper.some((e) => e.attrs.id === curMoveShape.attrs.id);
|
|
|
|
|
// 当前选中的节点是否在框选名单内
|
|
|
|
|
if (isCurShapeInSelectWrapper) {
|
|
|
|
|
// 在的话,所有节点同时移动
|
|
|
|
|
for (const item of selectWrapper) {
|
|
|
|
|
// 其他在框选中的节点,跟着被指定节点移动
|
|
|
|
|
if (item.attrs.id !== curMoveShape.attrs.id) {
|
|
|
|
|
item.setAttrs({
|
|
|
|
|
x: item.attrs.x + movementX,
|
|
|
|
|
y: item.attrs.y + movementY,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
this.handleNodeMove(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
this.handleNodeMove(curMoveShape);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
wrapper.on("click", (e) => {
|
|
|
|
|
// 只支持左键选中
|
|
|
|
|
if (e.evt.button !== 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.resetBoxBorder();
|
|
|
|
|
selectedBoxes = [];
|
|
|
|
|
this.currentNode = node;
|
|
|
|
|
this.handlePanelChange(1);
|
|
|
|
|
// 获取节点事件相关信息
|
2023-09-26 14:16:42 +00:00
|
|
|
|
this.handleEventNodeChange(this.onStart, this.currentNode.event[this.onStart].node, false);
|
|
|
|
|
this.handleEventNodeChange(this.onUpdate, this.currentNode.event[this.onUpdate].node, false);
|
|
|
|
|
this.handleEventNodeChange(this.onEnd, this.currentNode.event[this.onEnd].node, false);
|
|
|
|
|
this.render();
|
2023-09-20 17:32:11 +00:00
|
|
|
|
});
|
|
|
|
|
return wrapper;
|
|
|
|
|
},
|
|
|
|
|
generateBox(node) {
|
|
|
|
|
var _a;
|
|
|
|
|
const box = new lib_1.Konva.Rect({
|
|
|
|
|
name: "box",
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
fill: BOX_FILL,
|
|
|
|
|
stroke: ((_a = this.currentNode) === null || _a === void 0 ? void 0 : _a.id) === node.id ? BOX_FILL_ACTIVE : BOX_FILL,
|
|
|
|
|
strokeWidth: BOX_BORDER_WIDTH,
|
|
|
|
|
cornerRadius: BORDER_RADIUS,
|
|
|
|
|
width: BOX_WIDTH,
|
|
|
|
|
height: this.getBoxHeight(node),
|
|
|
|
|
shadowColor: "#111111",
|
|
|
|
|
shadowBlur: 16,
|
|
|
|
|
shadowOffset: { x: 2, y: 2 },
|
|
|
|
|
});
|
|
|
|
|
return box;
|
|
|
|
|
},
|
|
|
|
|
generateMain(node) {
|
|
|
|
|
const mainX = (BOX_WIDTH - MAIN_WIDTH) / 2;
|
|
|
|
|
const main = new lib_1.Konva.Rect({
|
|
|
|
|
x: mainX,
|
|
|
|
|
y: MAIN_GAP,
|
|
|
|
|
fill: MAIN_FILL,
|
|
|
|
|
stroke: MAIN_BORDER_COLOR,
|
|
|
|
|
strokeWidth: MAIN_BORDER_WIDTH,
|
|
|
|
|
cornerRadius: MAIN_BORDER_RADIUS,
|
|
|
|
|
width: MAIN_WIDTH,
|
|
|
|
|
height: MAIN_HEIGHT,
|
|
|
|
|
});
|
|
|
|
|
return main;
|
|
|
|
|
},
|
|
|
|
|
generateRemove(node) {
|
|
|
|
|
const removeGroup = new lib_1.Konva.Group({
|
|
|
|
|
x: BOX_WIDTH,
|
|
|
|
|
y: 0,
|
|
|
|
|
});
|
|
|
|
|
const removeCircle = new lib_1.Konva.Circle({
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
radius: REMOVE_RADIUS,
|
|
|
|
|
fill: REMOVE_CIRCLE_FILL,
|
|
|
|
|
shadowColor: "black",
|
|
|
|
|
shadowBlur: 8,
|
|
|
|
|
shadowOffset: { x: 4, y: 4 },
|
|
|
|
|
});
|
|
|
|
|
const removeLineLeft = new lib_1.Konva.Line({
|
|
|
|
|
stroke: REMOVE_LINE_FILL,
|
|
|
|
|
strokeWidth: REMOVE_LINE_WIDTH,
|
|
|
|
|
points: [
|
|
|
|
|
-REMOVE_RADIUS + REMOVE_PADDING,
|
|
|
|
|
-REMOVE_RADIUS + REMOVE_PADDING,
|
|
|
|
|
REMOVE_RADIUS - REMOVE_PADDING,
|
|
|
|
|
REMOVE_RADIUS - REMOVE_PADDING,
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
const removeLineRight = new lib_1.Konva.Line({
|
|
|
|
|
stroke: REMOVE_LINE_FILL,
|
|
|
|
|
strokeWidth: REMOVE_LINE_WIDTH,
|
|
|
|
|
points: [
|
|
|
|
|
REMOVE_RADIUS - REMOVE_PADDING,
|
|
|
|
|
-REMOVE_RADIUS + REMOVE_PADDING,
|
|
|
|
|
-REMOVE_RADIUS + REMOVE_PADDING,
|
|
|
|
|
REMOVE_RADIUS - REMOVE_PADDING,
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
removeGroup.add(removeCircle);
|
|
|
|
|
removeGroup.add(removeLineLeft);
|
|
|
|
|
removeGroup.add(removeLineRight);
|
|
|
|
|
removeGroup.on("mouseover", activeCursor);
|
|
|
|
|
removeGroup.on("mouseout", defaultCursor);
|
|
|
|
|
removeGroup.on("click", () => {
|
|
|
|
|
this.removeNode(node);
|
|
|
|
|
});
|
|
|
|
|
return removeGroup;
|
|
|
|
|
},
|
|
|
|
|
generatePanel(node) {
|
|
|
|
|
const panelX = (BOX_WIDTH - PANEL_WIDTH) / 2;
|
|
|
|
|
const panelStatic = new lib_1.Konva.Rect({
|
|
|
|
|
x: panelX,
|
|
|
|
|
y: MAIN_HEIGHT + MAIN_GAP * 2.2,
|
|
|
|
|
width: PANEL_WIDTH,
|
|
|
|
|
height: PANEL_HEIGHT,
|
|
|
|
|
fill: PANEL_FILL,
|
|
|
|
|
stroke: MAIN_BORDER_COLOR,
|
|
|
|
|
strokeWidth: MAIN_BORDER_WIDTH,
|
|
|
|
|
cornerRadius: PANEL_BORDER_RADIUS,
|
|
|
|
|
});
|
|
|
|
|
const panelDynamic = new lib_1.Konva.Rect({
|
|
|
|
|
x: panelX,
|
|
|
|
|
y: MAIN_HEIGHT + MAIN_GAP * 2,
|
|
|
|
|
width: PANEL_WIDTH,
|
|
|
|
|
height: PANEL_HEIGHT,
|
|
|
|
|
fill: "transparent",
|
|
|
|
|
cornerRadius: PANEL_BORDER_RADIUS,
|
|
|
|
|
draggable: true,
|
|
|
|
|
dragBoundFunc(currentNodePos) {
|
|
|
|
|
return {
|
|
|
|
|
x: clamp(currentNodePos.x, CANVAS_WIDTH - PANEL_WIDTH, 0),
|
|
|
|
|
y: clamp(currentNodePos.y, CANVAS_HEIGHT - PANEL_HEIGHT, 0),
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
panelDynamic.on("mouseover", activeCursor);
|
|
|
|
|
panelDynamic.on("mouseout", defaultCursor);
|
|
|
|
|
panelDynamic.on("dragstart", (e) => {
|
|
|
|
|
dynamicArrow.setAttrs({
|
|
|
|
|
x: node.x + BOX_WIDTH / 2,
|
|
|
|
|
y: node.y + this.getBoxHeight(node),
|
|
|
|
|
visible: true,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
panelDynamic.on("dragmove", (e) => {
|
|
|
|
|
const { x, y } = stage.getPointerPosition();
|
|
|
|
|
const centerX = node.x + BOX_WIDTH / 2;
|
|
|
|
|
const centerY = node.y + this.getBoxHeight(node);
|
|
|
|
|
dynamicArrow === null || dynamicArrow === void 0 ? void 0 : dynamicArrow.setAttrs({
|
|
|
|
|
points: [0, 0, clamp(x, CANVAS_WIDTH, 0) - centerX, clamp(y, CANVAS_HEIGHT, 0) - centerY],
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
panelDynamic.on("dragend", (e) => {
|
|
|
|
|
// 动态面板归位
|
|
|
|
|
const panelStaticAttrs = panelStatic.attrs;
|
|
|
|
|
panelDynamic.setAttrs({
|
|
|
|
|
x: panelStaticAttrs.x,
|
|
|
|
|
y: panelStaticAttrs.y,
|
|
|
|
|
});
|
|
|
|
|
// 隐藏动态箭头
|
|
|
|
|
dynamicArrow.visible(false);
|
|
|
|
|
defaultCursor();
|
|
|
|
|
this.setChild(e, node);
|
|
|
|
|
});
|
|
|
|
|
return [panelStatic, panelDynamic];
|
|
|
|
|
},
|
|
|
|
|
generateText(node) {
|
|
|
|
|
const mainX = (BOX_WIDTH - MAIN_WIDTH) / 2;
|
|
|
|
|
const name = new lib_1.Konva.Text({
|
|
|
|
|
x: mainX + TEXT_PADDING,
|
|
|
|
|
text: node.name,
|
|
|
|
|
fontSize: NAME_SIZE,
|
|
|
|
|
fill: TEXT_FILL,
|
|
|
|
|
width: MAIN_WIDTH,
|
|
|
|
|
height: NAME_SIZE,
|
|
|
|
|
ellipsis: true,
|
|
|
|
|
});
|
|
|
|
|
// 文字的大小会影响高度,所以动态设置
|
|
|
|
|
name.setAttrs({
|
|
|
|
|
y: MAIN_GAP * 6 - name.height(),
|
|
|
|
|
});
|
|
|
|
|
const index = this.preOrderIndexMap[node.id];
|
|
|
|
|
const prefix = index === undefined ? "" : `${index}:`;
|
|
|
|
|
const type = new lib_1.Konva.Text({
|
|
|
|
|
x: mainX + TEXT_PADDING,
|
|
|
|
|
text: `${prefix}${node.type}`,
|
|
|
|
|
fontSize: TYPE_SIZE,
|
|
|
|
|
fill: TEXT_FILL,
|
|
|
|
|
width: MAIN_WIDTH,
|
|
|
|
|
height: TYPE_SIZE,
|
|
|
|
|
ellipsis: true,
|
|
|
|
|
});
|
|
|
|
|
type.setAttrs({
|
|
|
|
|
y: MAIN_GAP * 8.6 - type.height() / 2,
|
|
|
|
|
});
|
|
|
|
|
return [name, type];
|
|
|
|
|
},
|
|
|
|
|
generateAbort(node) {
|
|
|
|
|
const abortGroup = new lib_1.Konva.Group();
|
|
|
|
|
const abortCircle = new lib_1.Konva.Circle({
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
radius: ABORT_RADIUS,
|
|
|
|
|
fill: ABORT_FILL,
|
|
|
|
|
});
|
|
|
|
|
const commonCfg = {
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
pointerLength: 5,
|
|
|
|
|
pointerWidth: 8,
|
|
|
|
|
stroke: ABORT_FILL,
|
|
|
|
|
strokeWidth: ABORT_LINE_SIZE,
|
|
|
|
|
};
|
|
|
|
|
const abortArrowDown = new lib_1.Konva.Arrow(Object.assign({ points: [0, -1, 0, 16] }, commonCfg));
|
|
|
|
|
const abortArrowRight = new lib_1.Konva.Arrow(Object.assign({ points: [-1, 0, 16, 0] }, commonCfg));
|
|
|
|
|
switch (node.abortType) {
|
|
|
|
|
case node_1.AbortType.LowerPriority:
|
|
|
|
|
abortGroup.add(abortCircle);
|
|
|
|
|
abortGroup.add(abortArrowRight);
|
|
|
|
|
break;
|
|
|
|
|
case node_1.AbortType.Self:
|
|
|
|
|
abortGroup.add(abortCircle);
|
|
|
|
|
abortGroup.add(abortArrowDown);
|
|
|
|
|
break;
|
|
|
|
|
case node_1.AbortType.Both:
|
|
|
|
|
abortGroup.add(abortCircle);
|
|
|
|
|
abortGroup.add(abortArrowRight);
|
|
|
|
|
abortGroup.add(abortArrowDown);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return abortGroup;
|
|
|
|
|
},
|
|
|
|
|
generateBreak(node) {
|
|
|
|
|
const breakGroup = new lib_1.Konva.Group({
|
|
|
|
|
x: BOX_WIDTH / 2,
|
|
|
|
|
y: 0,
|
|
|
|
|
});
|
|
|
|
|
const breakCircle = new lib_1.Konva.Circle({
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
|
|
|
|
radius: REMOVE_RADIUS,
|
|
|
|
|
fill: REMOVE_CIRCLE_FILL,
|
|
|
|
|
shadowColor: "black",
|
|
|
|
|
shadowBlur: 8,
|
|
|
|
|
shadowOffset: { x: 4, y: 4 },
|
|
|
|
|
});
|
|
|
|
|
const breakLineLeft = new lib_1.Konva.Line({
|
|
|
|
|
stroke: REMOVE_LINE_FILL,
|
|
|
|
|
strokeWidth: REMOVE_LINE_WIDTH,
|
|
|
|
|
points: [
|
|
|
|
|
-REMOVE_RADIUS + REMOVE_PADDING,
|
|
|
|
|
-REMOVE_RADIUS + REMOVE_PADDING,
|
|
|
|
|
REMOVE_RADIUS - REMOVE_PADDING,
|
|
|
|
|
REMOVE_RADIUS - REMOVE_PADDING,
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
const breakLineRight = new lib_1.Konva.Line({
|
|
|
|
|
stroke: REMOVE_LINE_FILL,
|
|
|
|
|
strokeWidth: REMOVE_LINE_WIDTH,
|
|
|
|
|
points: [
|
|
|
|
|
REMOVE_RADIUS - REMOVE_PADDING,
|
|
|
|
|
-REMOVE_RADIUS + REMOVE_PADDING,
|
|
|
|
|
-REMOVE_RADIUS + REMOVE_PADDING,
|
|
|
|
|
REMOVE_RADIUS - REMOVE_PADDING,
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
breakGroup.add(breakCircle);
|
|
|
|
|
breakGroup.add(breakLineLeft);
|
|
|
|
|
breakGroup.add(breakLineRight);
|
|
|
|
|
breakGroup.on("mouseover", activeCursor);
|
|
|
|
|
breakGroup.on("mouseout", defaultCursor);
|
|
|
|
|
breakGroup.on("click", () => {
|
|
|
|
|
this.removeParent(node);
|
|
|
|
|
});
|
|
|
|
|
return breakGroup;
|
|
|
|
|
},
|
|
|
|
|
// Action和Condition不需要Panel,所以height动态计算
|
|
|
|
|
getBoxHeight(node) {
|
|
|
|
|
let BOX_HEIGHT = 68;
|
|
|
|
|
// Root节点
|
|
|
|
|
if (node.type === undefined) {
|
|
|
|
|
return BOX_HEIGHT;
|
|
|
|
|
}
|
|
|
|
|
if (!ParentNodes.includes(node.type)) {
|
|
|
|
|
BOX_HEIGHT = BOX_HEIGHT - PANEL_HEIGHT - MAIN_GAP;
|
|
|
|
|
}
|
|
|
|
|
return BOX_HEIGHT;
|
|
|
|
|
},
|
2023-09-26 14:16:42 +00:00
|
|
|
|
isComposite(node) {
|
|
|
|
|
if (!node) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return CompositeNodes.includes(node.type);
|
|
|
|
|
},
|
2023-09-20 17:32:11 +00:00
|
|
|
|
showWarn(text) {
|
|
|
|
|
let time = getTime();
|
|
|
|
|
const content = `${time}:${text}`;
|
|
|
|
|
while (this.logs.length >= 10) {
|
|
|
|
|
this.logs.shift();
|
|
|
|
|
}
|
|
|
|
|
this.logs.push({ id: uuid(), content });
|
|
|
|
|
console.warn(content);
|
|
|
|
|
},
|
2023-09-26 14:16:42 +00:00
|
|
|
|
// 视角回到root节点
|
|
|
|
|
backRoot() {
|
|
|
|
|
var _a, _b;
|
|
|
|
|
(_b = (_a = this.$refs) === null || _a === void 0 ? void 0 : _a.scroll) === null || _b === void 0 ? void 0 : _b.scroll((CANVAS_WIDTH - this.$refs.scroll.getBoundingClientRect().width) / 2, ROOT_Y - 50);
|
|
|
|
|
},
|
2023-09-20 17:32:11 +00:00
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
const panelDataMap = new WeakMap();
|
|
|
|
|
/**
|
|
|
|
|
* @zh 如果希望兼容 3.3 之前的版本可以使用下方的代码
|
|
|
|
|
* @en You can add the code below if you want compatibility with versions prior to 3.3
|
|
|
|
|
*/
|
|
|
|
|
// Editor.Panel.define = Editor.Panel.define || function(options: any) { return options }
|
|
|
|
|
module.exports = Editor.Panel.define({
|
|
|
|
|
listeners: {
|
|
|
|
|
show() {
|
|
|
|
|
// console.log("show");
|
|
|
|
|
},
|
|
|
|
|
hide() {
|
|
|
|
|
// console.log("hide");
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
template: lib_1.fs.readFileSync(path_1.default.join(__dirname, "../../../src/panels/static/template/default/index.html"), "utf-8"),
|
|
|
|
|
style: lib_1.fs.readFileSync(path_1.default.join(__dirname, "../../../src/panels/static/style/default/index.css"), "utf-8"),
|
|
|
|
|
$: {
|
|
|
|
|
app: "#app",
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {};
|
|
|
|
|
},
|
2023-09-26 14:16:42 +00:00
|
|
|
|
methods: {
|
|
|
|
|
initSelection() {
|
|
|
|
|
const vm = panelDataMap.get(this);
|
|
|
|
|
if (vm) {
|
|
|
|
|
vm.initSelection();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
2023-09-20 17:32:11 +00:00
|
|
|
|
ready() {
|
|
|
|
|
if (this.$.app) {
|
|
|
|
|
const vm = new component();
|
|
|
|
|
panelDataMap.set(this, vm);
|
|
|
|
|
vm.$mount(this.$.app);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
beforeClose() { },
|
|
|
|
|
close() {
|
|
|
|
|
const vm = panelDataMap.get(this);
|
|
|
|
|
if (vm) {
|
|
|
|
|
vm.$destroy();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|