|
@@ -10,11 +10,17 @@
|
|
|
<t-icon name="save" @click="save(SaveType.Save)" /></t-badge
|
|
|
></a>
|
|
|
</t-tooltip>
|
|
|
- <t-tooltip content="保存为模板" placement="bottom">
|
|
|
+ <t-tooltip content="保存为我的组件" placement="bottom">
|
|
|
<a><t-icon name="layers" @click="save(SaveType.Save, true)" /></a>
|
|
|
</t-tooltip>
|
|
|
<t-tooltip content="格式化" placement="bottom">
|
|
|
- <a>
|
|
|
+ <a
|
|
|
+ @click="oneFormat"
|
|
|
+ @dblclick="alwaysFormat"
|
|
|
+ :style="{
|
|
|
+ color: one || always ? ' #1677ff' : '',
|
|
|
+ }"
|
|
|
+ >
|
|
|
<svg
|
|
|
width="1em"
|
|
|
height="1em"
|
|
@@ -38,7 +44,7 @@
|
|
|
</a>
|
|
|
</t-tooltip>
|
|
|
<t-tooltip content="清除格式" placement="bottom">
|
|
|
- <a>
|
|
|
+ <a @click="clearFormat">
|
|
|
<svg
|
|
|
width="1em"
|
|
|
height="1em"
|
|
@@ -59,34 +65,101 @@
|
|
|
<div class="flex-grow"></div>
|
|
|
|
|
|
<t-tooltip content="直线" placement="bottom">
|
|
|
- <a><t-icon name="slash" /></a>
|
|
|
+ <a
|
|
|
+ :draggable="true"
|
|
|
+ @dragstart="onAddShape($event, 'line')"
|
|
|
+ @click.stop="onAddShape($event, 'line')"
|
|
|
+ ><t-icon name="slash"
|
|
|
+ /></a>
|
|
|
</t-tooltip>
|
|
|
+ <!-- <t-tooltip content="连线" placement="top"> -->
|
|
|
+ <t-dropdown
|
|
|
+ :minColumnWidth="200"
|
|
|
+ :maxHeight="560"
|
|
|
+ :delay2="[10, 150]"
|
|
|
+ overlayClassName="header-dropdown"
|
|
|
+ >
|
|
|
+ <a
|
|
|
+ @click="oneDraw"
|
|
|
+ @dblclick="alwaysDraw"
|
|
|
+ :style="{
|
|
|
+ color: oneD || alwaysD ? ' #1677ff' : '',
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <svg
|
|
|
+ width="1em"
|
|
|
+ height="1em"
|
|
|
+ viewBox="0 0 1024 1024"
|
|
|
+ xmlns="http://www.w3.org/2000/svg"
|
|
|
+ >
|
|
|
+ <path
|
|
|
+ d="M192 64a128 128 0 0 1 123.968 96H384a160 160 0 0 1 159.68 149.504L544 320v384a96 96 0 0 0 86.784 95.552L640 800h68.032a128 128 0 1 1 0 64.064L640 864a160 160 0 0 1-159.68-149.504L480 704V320a96 96 0 0 0-86.784-95.552L384 224l-68.032 0.064A128 128 0 1 1 192 64z m640 704a64 64 0 1 0 0 128 64 64 0 0 0 0-128zM192 128a64 64 0 1 0 0 128 64 64 0 0 0 0-128z"
|
|
|
+ fill="currentColor"
|
|
|
+ ></path>
|
|
|
+ </svg>
|
|
|
+ </a>
|
|
|
+ <t-dropdown-menu>
|
|
|
+ <t-dropdown-item v-for="item in lineTypes">
|
|
|
+ <div class="flex middle" @click="changeLineType(item.value)">
|
|
|
+ {{ item.name }} <span class="flex-grow"></span>
|
|
|
+ <t-icon v-show="item.value === currentLineType" name="check" />
|
|
|
+ </div>
|
|
|
+ </t-dropdown-item>
|
|
|
+ </t-dropdown-menu>
|
|
|
+ </t-dropdown>
|
|
|
+ <!-- </t-tooltip> -->
|
|
|
<t-tooltip content="文字" placement="bottom">
|
|
|
- <a>T</a>
|
|
|
- </t-tooltip>
|
|
|
- <t-tooltip content="图片" placement="bottom">
|
|
|
- <a><t-icon name="image" /></a>
|
|
|
+ <a
|
|
|
+ :draggable="true"
|
|
|
+ @dragstart="onAddShape($event, 'text')"
|
|
|
+ @click.stop="onAddShape($event, 'text')"
|
|
|
+ >T</a
|
|
|
+ >
|
|
|
</t-tooltip>
|
|
|
<t-tooltip content="视图大小" placement="bottom">
|
|
|
- <div style="line-height: 40px; margin-left: 8px">100%</div>
|
|
|
+ <div style="line-height: 40px; margin-left: 8px">{{ scale }}%</div>
|
|
|
</t-tooltip>
|
|
|
|
|
|
<t-tooltip content="100%视图" placement="bottom">
|
|
|
- <a><t-icon name="refresh" /></a>
|
|
|
+ <a @click="onScaleView"><t-icon name="refresh" /></a>
|
|
|
</t-tooltip>
|
|
|
<t-tooltip content="窗口大小" placement="bottom">
|
|
|
- <a><t-icon name="minus-rectangle" /></a>
|
|
|
+ <a @click="onScaleWindow"><t-icon name="minus-rectangle" /></a>
|
|
|
</t-tooltip>
|
|
|
<t-tooltip content="数据源" placement="bottom">
|
|
|
- <a><t-icon name="server" /></a>
|
|
|
+ <a @click="connectShow"><t-icon name="server" /></a>
|
|
|
</t-tooltip>
|
|
|
|
|
|
<div class="flex-grow"></div>
|
|
|
<t-tooltip content="预览" placement="bottom">
|
|
|
- <a><t-icon name="browse" /></a>
|
|
|
+ <a @click="preview"><t-icon name="browse" /></a>
|
|
|
</t-tooltip>
|
|
|
- <t-tooltip content="运行" placement="bottom">
|
|
|
- <a><t-icon name="caret-right" /></a>
|
|
|
+ <t-tooltip
|
|
|
+ :content="isLock === 2 ? '锁定' : isLock === 1 ? '预览' : '编辑'"
|
|
|
+ placement="bottom"
|
|
|
+ >
|
|
|
+ <a>
|
|
|
+ <!-- <t-icon name="caret-right" /> -->
|
|
|
+ <svg
|
|
|
+ v-if="isLock === 1"
|
|
|
+ class="l-icon"
|
|
|
+ aria-hidden="true"
|
|
|
+ @click="onLock"
|
|
|
+ >
|
|
|
+ <use xlink:href="#l-lock"></use>
|
|
|
+ </svg>
|
|
|
+ <svg
|
|
|
+ v-else-if="isLock === 2"
|
|
|
+ class="l-icon"
|
|
|
+ aria-hidden="true"
|
|
|
+ @click="onLock"
|
|
|
+ >
|
|
|
+ <use xlink:href="#l-wufayidong"></use>
|
|
|
+ </svg>
|
|
|
+ <svg v-else class="l-icon" aria-hidden="true" @click="onLock">
|
|
|
+ <use xlink:href="#l-unlock"></use>
|
|
|
+ </svg>
|
|
|
+ </a>
|
|
|
</t-tooltip>
|
|
|
<t-tooltip content="手机查看" placement="bottom">
|
|
|
<a><t-icon name="qrcode" /></a>
|
|
@@ -99,20 +172,34 @@
|
|
|
</t-tooltip>
|
|
|
</div>
|
|
|
<div id="meta2d"></div>
|
|
|
+
|
|
|
+ <t-dialog v-model:visible="connectVisible">
|
|
|
+ <p>这是通信对话框</p>
|
|
|
+ </t-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import { Meta2d, Options, Pen } from '@meta2d/core';
|
|
|
-import { onMounted, onUnmounted, watch } from 'vue';
|
|
|
-import { registerBasicDiagram } from '@/services/register';
|
|
|
-import { useRouter, useRoute } from 'vue-router';
|
|
|
-import { useUser } from '@/services/user';
|
|
|
-import { getLe5le2d } from '@/services/api';
|
|
|
-import { useDot } from '@/services/common';
|
|
|
-import { save, newFile } from './Header.vue';
|
|
|
-import { useSelection, SelectionMode } from '@/services/selections';
|
|
|
-
|
|
|
+import { Meta2d, Options, Pen, deepClone, LockState } from "@meta2d/core";
|
|
|
+import { onMounted, onUnmounted, watch, ref, reactive } from "vue";
|
|
|
+import { registerBasicDiagram } from "@/services/register";
|
|
|
+import { useRouter, useRoute } from "vue-router";
|
|
|
+import { useUser } from "@/services/user";
|
|
|
+import { getLe5le2d } from "@/services/api";
|
|
|
+import { useDot } from "@/services/common";
|
|
|
+import {
|
|
|
+ save,
|
|
|
+ newFile,
|
|
|
+ SaveType,
|
|
|
+ onScaleView,
|
|
|
+ onScaleWindow,
|
|
|
+} from "./Header.vue";
|
|
|
+import { useSelection, SelectionMode } from "@/services/selections";
|
|
|
+import { defaultFormat } from "@/services/defaults";
|
|
|
+import { MessagePlugin } from "tdesign-vue-next";
|
|
|
+import { localMeta2dDataName } from "@/services/utils";
|
|
|
+import localforage from "localforage";
|
|
|
+import { checkData } from "@/services/utils";
|
|
|
const router = useRouter();
|
|
|
const route = useRoute();
|
|
|
const { user, message, getUser, getMessage, signout } = useUser();
|
|
@@ -120,22 +207,27 @@ const { dot, setDot, getDot } = useDot();
|
|
|
const { selections } = useSelection();
|
|
|
|
|
|
const meta2dOptions: Options = {
|
|
|
- cdn: 'https://assets.le5lecdn.com',
|
|
|
+ cdn: "https://assets.le5lecdn.com",
|
|
|
rule: true,
|
|
|
- background: '#1e2430',
|
|
|
+ background: "#1e2430",
|
|
|
x: 32,
|
|
|
y: 32,
|
|
|
width: 1920,
|
|
|
height: 1080,
|
|
|
+ defaultFormat: { ...defaultFormat },
|
|
|
};
|
|
|
onMounted(() => {
|
|
|
- meta2d = new Meta2d('meta2d', meta2dOptions);
|
|
|
+ meta2d = new Meta2d("meta2d", meta2dOptions);
|
|
|
registerBasicDiagram();
|
|
|
open();
|
|
|
// @ts-ignore
|
|
|
- meta2d.on('active', active);
|
|
|
+ meta2d.on("active", active);
|
|
|
// @ts-ignore
|
|
|
- meta2d.on('inactive', inactive);
|
|
|
+ meta2d.on("inactive", inactive);
|
|
|
+ // @ts-ignore
|
|
|
+ meta2d.on("scale", scaleListener);
|
|
|
+ // @ts-ignore
|
|
|
+ meta2d.on("add", lineAdd);
|
|
|
});
|
|
|
|
|
|
const watcher = watch(
|
|
@@ -147,20 +239,26 @@ const watcher = watch(
|
|
|
|
|
|
const open = async () => {
|
|
|
if (route.query.id) {
|
|
|
- const ret: any = getLe5le2d(route.query.id + '');
|
|
|
+ const ret: any = getLe5le2d(route.query.id + "");
|
|
|
ret && meta2d.open(ret);
|
|
|
} else {
|
|
|
meta2d.open();
|
|
|
}
|
|
|
+ meta2d.store.data.x = meta2d.store.options.x;
|
|
|
+ meta2d.store.data.y = meta2d.store.options.y;
|
|
|
};
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
watcher();
|
|
|
if (meta2d) {
|
|
|
// @ts-ignore
|
|
|
- meta2d.off('active', active);
|
|
|
+ meta2d.off("active", active);
|
|
|
+ // @ts-ignore
|
|
|
+ meta2d.off("inactive", inactive);
|
|
|
// @ts-ignore
|
|
|
- meta2d.off('inactive', inactive);
|
|
|
+ meta2d.off("scale", scaleListener);
|
|
|
+ // @ts-ignore
|
|
|
+ meta2d.off("add", lineAdd);
|
|
|
meta2d.destroy();
|
|
|
}
|
|
|
});
|
|
@@ -187,6 +285,12 @@ const active = (oldPens: Pen[]) => {
|
|
|
checkPropType(pens);
|
|
|
selections.pens = oldPens;
|
|
|
}, 10);
|
|
|
+
|
|
|
+ //格式刷处理
|
|
|
+ if (one.value || always.value) {
|
|
|
+ meta2d.formatPainter();
|
|
|
+ one.value = false;
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
/**
|
|
@@ -205,6 +309,156 @@ const checkPropType = (pens: Pen[]) => {
|
|
|
selections.mode = SelectionMode.File;
|
|
|
}
|
|
|
};
|
|
|
+
|
|
|
+const one = ref(false);
|
|
|
+const always = ref(false);
|
|
|
+
|
|
|
+const oneFormat = () => {
|
|
|
+ if (one.value) {
|
|
|
+ one.value = false;
|
|
|
+ } else {
|
|
|
+ one.value = true;
|
|
|
+ meta2d.setFormatPainter();
|
|
|
+ }
|
|
|
+ if (always.value) {
|
|
|
+ always.value = false;
|
|
|
+ one.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const alwaysFormat = () => {
|
|
|
+ always.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+const clearFormat = () => {
|
|
|
+ always.value = false;
|
|
|
+ one.value = false;
|
|
|
+ meta2d.clearFormatPainter();
|
|
|
+};
|
|
|
+
|
|
|
+const scale = ref(100);
|
|
|
+function scaleListener(newScale: number) {
|
|
|
+ scale.value = Math.round(newScale * 100);
|
|
|
+}
|
|
|
+
|
|
|
+const connectVisible = ref(false);
|
|
|
+const connectShow = () => {
|
|
|
+ connectVisible.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+const currentLineType = ref("curve");
|
|
|
+const lineTypes = reactive([
|
|
|
+ { name: "曲线", icon: "t-icon t-curve2", value: "curve" },
|
|
|
+ { name: "线段", icon: "t-icon t-polyline", value: "polyline" },
|
|
|
+ { name: "直线", icon: "t-icon t-line", value: "line" },
|
|
|
+ { name: "脑图曲线", icon: "t-icon t-mind", value: "mind" },
|
|
|
+]);
|
|
|
+
|
|
|
+const changeLineType = (value: string) => {
|
|
|
+ currentLineType.value = value;
|
|
|
+ if (meta2d) {
|
|
|
+ meta2d.store.options.drawingLineName = value;
|
|
|
+ meta2d.canvas.drawingLineName && (meta2d.canvas.drawingLineName = value);
|
|
|
+ meta2d.store.active?.forEach((pen) => {
|
|
|
+ meta2d.updateLineType(pen, value);
|
|
|
+ });
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const oneD = ref<boolean>(false);
|
|
|
+const alwaysD = ref<boolean>(false);
|
|
|
+const oneDraw = () => {
|
|
|
+ if (oneD.value) {
|
|
|
+ oneD.value = false;
|
|
|
+ } else {
|
|
|
+ oneD.value = true;
|
|
|
+ meta2d.drawLine(meta2d.store.options.drawingLineName);
|
|
|
+ }
|
|
|
+ if (alwaysD.value) {
|
|
|
+ meta2d.finishDrawLine();
|
|
|
+ meta2d.drawLine();
|
|
|
+ oneD.value = false;
|
|
|
+ alwaysD.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+const alwaysDraw = () => {
|
|
|
+ alwaysD.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+const lineAdd = (pens: Pen[]) => {
|
|
|
+ if (pens.length === 1 && pens[0].name === "line") {
|
|
|
+ //连线类型
|
|
|
+ if (oneD.value && !alwaysD.value) {
|
|
|
+ if (meta2d.canvas.drawingLineName) {
|
|
|
+ oneD.value = false;
|
|
|
+ setTimeout(() => {
|
|
|
+ meta2d.finishDrawLine();
|
|
|
+ meta2d.drawLine();
|
|
|
+ }, 100);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const onAddShape = (event: DragEvent, name: string) => {
|
|
|
+ let data: any;
|
|
|
+ if (name === "text") {
|
|
|
+ data = {
|
|
|
+ text: "le5le Meta2d",
|
|
|
+ width: 100,
|
|
|
+ height: 20,
|
|
|
+ name: "text",
|
|
|
+ };
|
|
|
+ } else if (name === "line") {
|
|
|
+ data = {
|
|
|
+ anchors: [
|
|
|
+ { id: "0", x: 0, y: 0.5 },
|
|
|
+ { id: "1", x: 1, y: 0.5 },
|
|
|
+ ],
|
|
|
+ width: 100,
|
|
|
+ height: 1,
|
|
|
+ name: "line",
|
|
|
+ };
|
|
|
+ }
|
|
|
+ if (!event.dataTransfer) {
|
|
|
+ meta2d.canvas.addCaches = deepClone([data]);
|
|
|
+ } else {
|
|
|
+ event.dataTransfer.setData("Meta2d", JSON.stringify(data));
|
|
|
+ }
|
|
|
+ event.stopPropagation();
|
|
|
+};
|
|
|
+
|
|
|
+const isLock = ref(0);
|
|
|
+function onLock() {
|
|
|
+ !isLock.value && (isLock.value = 0);
|
|
|
+ if (isLock.value === LockState.DisableMove) {
|
|
|
+ isLock.value = LockState.None;
|
|
|
+ } else {
|
|
|
+ isLock.value++;
|
|
|
+ }
|
|
|
+ meta2d.lock(isLock.value);
|
|
|
+ meta2d.hideInput();
|
|
|
+}
|
|
|
+
|
|
|
+const preview = async () => {
|
|
|
+ meta2d.stopAnimate();
|
|
|
+ const data: Meta2dBackData = meta2d.data();
|
|
|
+ checkData(data);
|
|
|
+ if (dot && user && data._id) {
|
|
|
+ // 有 id ,是修改后保存
|
|
|
+ await save(SaveType.Save);
|
|
|
+ }
|
|
|
+ if (!data._id) {
|
|
|
+ await localforage.setItem(localMeta2dDataName, JSON.stringify(data));
|
|
|
+ }
|
|
|
+ router.push({
|
|
|
+ path: "/preview",
|
|
|
+ query: {
|
|
|
+ r: Date.now() + "",
|
|
|
+ id: data._id,
|
|
|
+ },
|
|
|
+ });
|
|
|
+};
|
|
|
</script>
|
|
|
<style lang="postcss" scoped>
|
|
|
.meta2d {
|