1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384 |
- <template>
- <div class="meta2d">
- <div class="tools">
- <t-tooltip content="新建" placement="bottom">
- <a><t-icon name="add" @click="newFile" /></a>
- </t-tooltip>
- <t-tooltip content="保存" placement="bottom">
- <a>
- <t-badge dot :showZero="false" :count="dot ? 1 : 0">
- <t-icon name="save" @click="save(SaveType.Save)" /></t-badge
- ></a>
- </t-tooltip>
- <t-tooltip content="保存为我的组件" placement="bottom">
- <a><t-icon name="layers" @click="save(SaveType.Save, true)" /></a>
- </t-tooltip>
- <t-tooltip content="格式化(双击可连续使用)" placement="bottom">
- <a
- @click="oneFormat"
- @dblclick="alwaysFormat"
- :style="{
- color: one || always ? ' #1677ff' : '',
- }"
- >
- <svg
- width="1em"
- height="1em"
- viewBox="0 0 256 256"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- >
- <defs>
- <path id="77456364" d="M0 0h256v256H0z"></path>
- </defs>
- <g fill="none" fill-rule="evenodd">
- <mask id="3804093554b" fill="#fff">
- <use xlink:href="#77456364"></use>
- </mask>
- <path
- d="M213 77c0 14.36-11.64 26-26 26H69c-14.36 0-26-11.64-26-26V51c0-14.36 11.64-26 26-26h118c14.36 0 26 11.64 26 26v2h9.125c9.83 0 17.82 7.88 17.997 17.67l.003.33v50c0 9.83-7.88 17.82-17.67 17.997l-.33.003h-84.626v18H139c9.83 0 17.82 7.88 17.997 17.67l.003.33v35c0 16.016-12.984 29-29 29-15.856 0-28.74-12.725-28.996-28.52L99 210v-35c0-9.83 7.88-17.82 17.67-17.997L117 157h.499l.001-20c0-9.83 7.88-17.82 17.67-17.997l.33-.003h84.625V73H213Zm-76 100h-18v33a9 9 0 0 0 8.471 8.985l.264.011.265.004a9 9 0 0 0 8.996-8.735L137 210v-33Zm50-132H69a6 6 0 0 0-6 6v26a6 6 0 0 0 6 6h118a6 6 0 0 0 6-6V51a6 6 0 0 0-6-6Z"
- fill="currentColor"
- fill-rule="nonzero"
- mask="url(#3804093554b)"
- ></path>
- </g>
- </svg>
- </a>
- </t-tooltip>
- <t-tooltip content="清除格式" placement="bottom">
- <a @click="clearFormat">
- <svg
- width="1em"
- height="1em"
- viewBox="0 0 1024 1024"
- xmlns="http://www.w3.org/2000/svg"
- >
- <path
- d="M889.186 384.07 677.671 172.56c-53.063-53.063-139.094-53.063-192.157 0L134.617 523.457c-53.063 53.063-53.063 139.099 0 192.158l170.196 170.2a41.354 41.354 0 0 0 29.243 12.11h215.001a41.354 41.354 0 0 0 29.184-12.05L889.155 576.26c53.094-53.09 53.094-139.126.031-192.19zM830.7 442.558c20.48 20.472 20.764 53.492.855 74.319l-.961.984-298.618 297.358H351.185l-158.09-158.086c-20.76-20.763-20.76-54.43 0-75.193l350.901-350.897c20.764-20.764 54.43-20.764 75.193 0l211.515 211.511z"
- fill="currentColor"
- ></path>
- <path
- d="m685.505 678.754-58.19 58.77-317.587-314.43 58.191-58.774zm197.55 136.508c23.46 0 42.483 18.514 42.483 41.353 0 22.45-18.38 40.724-41.294 41.338l-1.19.016h-454.6c-23.462 0-42.485-18.514-42.485-41.354 0-22.449 18.381-40.723 41.295-41.338l1.19-.015h454.6z"
- fill="currentColor"
- ></path>
- </svg>
- </a>
- </t-tooltip>
- <div class="flex-grow"></div>
- <t-tooltip content="直线" placement="bottom">
- <a
- :draggable="true"
- @dragstart="onAddShape($event, 'line')"
- @click.stop="onAddShape($event, 'line')"
- ><t-icon name="slash"
- /></a>
- </t-tooltip>
- <t-tooltip content="文字" placement="bottom">
- <a
- :draggable="true"
- @dragstart="onAddShape($event, 'text')"
- @click.stop="onAddShape($event, 'text')"
- >T</a
- >
- </t-tooltip>
- <t-tooltip content="连线(双击可连续使用)" placement="bottom">
- <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-tooltip>
- <!-- <t-tooltip content="连线" placement="top"> -->
- <t-dropdown
- :minColumnWidth="200"
- :maxHeight="560"
- :delay2="[10, 150]"
- overlayClassName="header-dropdown"
- >
- <a>
- <svg class="l-icon" aria-hidden="true">
- <use
- :xlink:href="
- lineTypes.find((item) => item.value === currentLineType)?.icon
- "
- ></use>
- </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" /> -->
- <svg class="l-icon" aria-hidden="true">
- <use :xlink:href="item.icon"></use>
- </svg>
- </div>
- </t-dropdown-item>
- </t-dropdown-menu>
- </t-dropdown>
- <t-dropdown
- :minColumnWidth="200"
- :maxHeight="560"
- :delay2="[10, 150]"
- overlayClassName="header-dropdown"
- >
- <a>
- <svg class="l-icon" aria-hidden="true">
- <use
- :xlink:href="
- fromArrows.find((item) => item.value === fromArrow)?.icon
- "
- ></use>
- </svg>
- </a>
- <t-dropdown-menu>
- <t-dropdown-item v-for="item in fromArrows">
- <div
- class="flex middle"
- style="height: 30px"
- @click="changeFromArrow(item.value)"
- >
- <svg class="l-icon" aria-hidden="true">
- <use :xlink:href="item.icon"></use>
- </svg>
- </div>
- </t-dropdown-item>
- </t-dropdown-menu>
- </t-dropdown>
- <t-dropdown
- :minColumnWidth="200"
- :maxHeight="560"
- :delay2="[10, 150]"
- overlayClassName="header-dropdown"
- >
- <a>
- <svg class="l-icon" aria-hidden="true">
- <use
- :xlink:href="
- toArrows.find((item) => item.value === toArrow)?.icon
- "
- ></use>
- </svg>
- </a>
- <t-dropdown-menu>
- <t-dropdown-item v-for="item in toArrows">
- <div
- class="flex middle"
- style="height: 30px"
- @click="changeToArrow(item.value)"
- >
- <svg class="l-icon" aria-hidden="true">
- <use :xlink:href="item.icon"></use>
- </svg>
- </div>
- </t-dropdown-item>
- </t-dropdown-menu>
- </t-dropdown>
- <!-- </t-tooltip> -->
- <t-tooltip content="视图大小" placement="bottom">
- <div style="line-height: 40px; margin-left: 8px">{{ scale }}%</div>
- </t-tooltip>
- <t-tooltip content="100%视图" placement="bottom">
- <a @click="onScaleView"><t-icon name="refresh" /></a>
- </t-tooltip>
- <t-tooltip content="窗口大小" placement="bottom">
- <a @click="onScaleWindow"><t-icon name="minus-rectangle" /></a>
- </t-tooltip>
- <t-tooltip content="数据源" placement="bottom">
- <a @click="connectShow"><t-icon name="server" /></a>
- </t-tooltip>
- <div class="flex-grow"></div>
- <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 @click="preview"><t-icon name="caret-right" /></a>
- </t-tooltip>
- <t-tooltip content="手机查看" placement="bottom">
- <a><t-icon name="qrcode" /></a>
- </t-tooltip>
- <t-tooltip content="分享" placement="bottom">
- <a><t-icon name="share" /></a>
- </t-tooltip>
- <t-tooltip content="发布" placement="bottom">
- <a><t-icon name="cloud" /></a>
- </t-tooltip>
- </div>
- <div id="meta2d"></div>
- <ContextMenu
- :style="{
- left: contextMenuValue.left,
- top: contextMenuValue.top,
- }"
- v-show="contextMenuVisible"
- :type="contextMenuType"
- @changeVisible="changeContextMenuVisible"
- />
- <t-dialog
- width="800px"
- header="数据源管理"
- v-model:visible="connectVisible"
- :footer="false"
- >
- <t-tabs :default-value="1">
- <t-tab-panel :value="1" label="通信" :destroy-on-hide="false">
- <template #panel>
- <div v-show="comShow">
- <t-row class="mt-8" justify="end">
- <t-space :size="8">
- <t-select-input
- placeholder="搜索我的数据源"
- allow-input
- clearable
- :popup-visible="popupVisible"
- :popup-props="{ overlayInnerStyle: { padding: '6px' } }"
- @input-change="onInputChange"
- @popup-visible-change="onPopupVisibleChange"
- >
- <template #panel>
- <ul>
- <li
- style="line-height: 14px; margin: 8px 4px"
- v-for="item in comOptions"
- :key="item.url"
- @click="() => onOptionClick(item)"
- >
- 名称: {{ item.name }}<br />
- 地址: {{ item.url }}
- </li>
- </ul>
- </template>
- <template #suffixIcon>
- <t-icon name="search"></t-icon
- ></template>
- </t-select-input>
- <t-button style="height: 30px" @click="addCom"
- >添加数据源</t-button
- >
- </t-space>
- </t-row>
- <t-table
- class="mt-8"
- row-key="id"
- :data="comData"
- :columns="comColumns"
- >
- <template #operation="{ row, rowIndex }">
- <t-link theme="primary" hover="color" @click="editCom(row)">
- 编辑
- </t-link>
- <t-divider layout="vertical" />
- <t-popconfirm
- content="确认删除吗"
- @confirm="deleteCom(rowIndex)"
- >
- <t-link theme="primary" hover="color"> 删除 </t-link>
- </t-popconfirm>
- </template>
- </t-table>
- </div>
- <div v-show="!comShow">
- <t-row class="mt-8">
- <t-col :span="4">
- <t-button
- theme="primary"
- variant="text"
- ghost
- @click="comBack"
- >
- <template #icon>
- <t-icon name="rollback"></t-icon>
- </template>
- 返回
- </t-button>
- </t-col>
- <t-col :span="4" :offset="4">
- <t-button @click="saveForCurrent" theme="primary"
- >仅当前页面使用</t-button
- >
- <t-button @click="saveToServer" theme="primary" class="ml-4"
- >保存到服务器</t-button
- >
- </t-col>
- </t-row>
- <div class="form-item mt-16">
- <label>名称</label>
- <t-input v-model="com.name" style="width: 200px" />
- </div>
- <div class="form-item">
- <label>连接类型</label>
- <t-select
- v-model="com.type"
- placeholder="请选择"
- style="width: 200px"
- :popup-props="{ overlayInnerStyle: { width: '200px' } }"
- :disabled="comType === 'edit'"
- @change="comTypeChange"
- >
- <t-option key="mqtt" value="mqtt" class="overlay-options">
- </t-option>
- <t-option
- key="websocket"
- value="websocket"
- class="overlay-options"
- >
- </t-option>
- <t-option key="http" value="http" class="overlay-options">
- </t-option>
- </t-select>
- </div>
- <template v-if="com.type === 'mqtt'">
- <div class="form-item">
- <label>接口地址</label>
- <t-input v-model="com.url" style="width: 200px" />
- </div>
- <div class="form-item">
- <label>Client Id</label>
- <t-input
- v-model="com.options.clientId"
- style="width: 200px"
- />
- </div>
- <div class="form-item">
- <label>关闭自动生成</label>
- <t-switch v-model="com.options.customClientId" />
- </div>
- <div class="form-item">
- <label>用户名</label>
- <t-input
- v-model="com.options.username"
- style="width: 200px"
- />
- </div>
- <div class="form-item">
- <label>密码</label>
- <t-input
- v-model="com.options.password"
- style="width: 200px"
- />
- </div>
- <div class="form-item">
- <label>Topics</label>
- <t-input v-model="com.topics" style="width: 200px" />
- </div>
- </template>
- <template v-else-if="com.type === 'http'">
- <div class="form-item">
- <label>请求方式</label>
- <t-select
- v-model="com.method"
- placeholder="请选择"
- style="width: 200px"
- :popup-props="{ overlayInnerStyle: { width: '200px' } }"
- @change="comHttpMethodChange"
- >
- <t-option key="GET" value="GET"> </t-option>
- <t-option key="POST" value="POST"> </t-option>
- </t-select>
- </div>
- <div class="form-item">
- <label>请求头</label>
- <t-textarea
- v-model="com.httpHeaders"
- placeholder="请输入请求头"
- name="description"
- :autosize="{ minRows: 3, maxRows: 5 }"
- />
- </div>
- <div v-if="com.method === 'POST'" class="form-item">
- <label>请求体</label>
- <t-textarea
- v-model="com.body"
- placeholder="请输入请求体"
- name="description"
- :autosize="{ minRows: 3, maxRows: 5 }"
- />
- </div>
- </template>
- <template v-else>
- <div class="form-item">
- <label>protocol</label>
- <t-input v-model="com.protocols" style="width: 200px" />
- </div>
- </template>
- </div>
- </template>
- </t-tab-panel>
- <t-tab-panel :value="2" :destroy-on-hide="false">
- <template #label
- >数据集 <span><label class="vip-label">VIP</label></span></template
- >
- <template #panel>
- <t-row class="mt-8" style="height: 32px; line-height: 32px">
- <t-col flex="60px"> 网络接口 </t-col>
- <t-col flex="auto">
- <t-input class="ml-8" style="width: 200px" />
- </t-col>
- </t-row>
- <t-row class="mt-8">
- <t-col flex="60px">自定义</t-col>
- <t-col flex="auto">
- <t-button
- @click="importDataSet"
- class="ml-8"
- style="height: 30px"
- >从Excel导入</t-button
- >
- <t-button @click="downloadDataSet" variant="text">
- <template #icon><t-icon name="download"></t-icon> </template>
- 下载示例
- </t-button>
- </t-col>
- </t-row>
- <t-table
- class="mt-8"
- row-key="id"
- :data="dsData"
- :columns="dsColumns"
- >
- <template #label="{ row }">
- {{ `${row.label}(${row.key})` }}
- </template>
- <template #operation="{ row }"> </template>
- </t-table>
- </template>
- </t-tab-panel>
- </t-tabs>
- </t-dialog>
- </div>
- </template>
- <script lang="ts" setup>
- import {
- Meta2d,
- Options,
- Pen,
- deepClone,
- LockState,
- PenType,
- HoverType,
- } 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 "@/services/common";
- 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, Meta2dBackData } from "@/services/utils";
- import { cdn } from "@/services/api";
- import dayjs from "dayjs";
- import ContextMenu from "./ContextMenu.vue";
- import { importExcel, saveAsExcel } from "@/services/excel";
- import { it } from "node:test";
- const router = useRouter();
- const route = useRoute();
- const { user, message, getUser, getMessage, signout } = useUser();
- const { dot, setDot, getDot } = useDot();
- const { select } = useSelection();
- const meta2dOptions: Options = {
- cdn,
- rule: true,
- background: "#1e2430",
- x: 32,
- y: 32,
- width: 1920,
- height: 1080,
- color: "#bdc7db",
- disableAnchor: true,
- defaultFormat: { ...defaultFormat },
- };
- onMounted(() => {
- meta2d = new Meta2d("meta2d", meta2dOptions);
- registerBasicDiagram();
- open();
- // @ts-ignore
- meta2d.on("active", active);
- // @ts-ignore
- meta2d.on("inactive", inactive);
- // @ts-ignore
- meta2d.on("scale", scaleListener);
- // @ts-ignore
- meta2d.on("add", lineAdd);
- meta2d.on("opened", openedListener);
- meta2d.on("undo", autoSave);
- meta2d.on("redo", autoSave);
- meta2d.on("add", autoSave);
- meta2d.on("delete", autoSave);
- meta2d.on("rotatePens", autoSave);
- meta2d.on("translatePens", autoSave);
- //TODO所有编辑栏所做修改
- meta2d.on("components-update-value", autoSave);
- // @ts-ignore
- meta2d.on("contextmenu", contextmenu);
- meta2d.on("click", canvasClick);
- meta2d.on("opened", dataSourceManage);
- });
- const watcher = watch(
- () => route.query,
- async () => {
- open();
- }
- );
- const open = async () => {
- if (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 || 0;
- meta2d.store.data.y = meta2d.store.options.y || 0;
- };
- const openedListener = () => {
- const {
- locked,
- scale: canvasScale,
- fromArrow: canvasFromArrow,
- toArrow: canvasToArrow,
- } = meta2d.store.data;
- isLock.value = locked || 0;
- scale.value = Math.round(canvasScale * 100);
- fromArrow.value = canvasFromArrow || "";
- toArrow.value = canvasToArrow || "";
- };
- onUnmounted(() => {
- watcher();
- if (meta2d) {
- // @ts-ignore
- meta2d.off("active", active);
- // @ts-ignore
- meta2d.off("inactive", inactive);
- // @ts-ignore
- meta2d.off("scale", scaleListener);
- // @ts-ignore
- meta2d.off("add", lineAdd);
- meta2d.on("opened", openedListener);
- meta2d.off("undo", autoSave);
- meta2d.off("redo", autoSave);
- meta2d.off("add", autoSave);
- meta2d.off("delete", autoSave);
- meta2d.off("rotatePens", autoSave);
- meta2d.off("translatePens", autoSave);
- meta2d.off("components-update-value", autoSave);
- // @ts-ignore
- meta2d.off("contextmenu", contextmenu);
- meta2d.off("click", canvasClick);
- meta2d.off("opened", dataSourceManage);
- meta2d.destroy();
- }
- });
- let localSaveTimer: any = 0;
- let saveTimer: any = 0;
- const autoSave = () => {
- setDot(true);
- localSaveTimer && clearTimeout(localSaveTimer);
- localSaveTimer = setTimeout(() => {
- const data: Meta2dBackData = meta2d.data();
- let _localMeta2dDataName = data._id
- ? localMeta2dDataName + "-" + data._id
- : localMeta2dDataName;
- (data as any).localSaveAt = dayjs().format();
- localforage.setItem(_localMeta2dDataName, JSON.stringify(data));
- localSaveTimer = undefined;
- }, 3000);
- autoSaveServer();
- };
- //运行 在新标签页查看
- function autoSaveServer() {
- //会员享受自动保存
- if (saveTimer) {
- return;
- }
- saveTimer = setTimeout(() => {
- const data: Meta2dBackData = meta2d.data();
- if (
- user &&
- user.id &&
- user.vipExpired &&
- data._id &&
- !data.component &&
- data.owner &&
- data.owner?.id === user.id
- ) {
- save(SaveType.Save);
- }
- saveTimer = null;
- }, 60000);
- }
- const inactive = () => {
- select();
- };
- const active = (pens: Pen[]) => {
- select(pens);
- //格式刷处理
- if (one.value || always.value) {
- meta2d.formatPainter();
- one.value = false;
- }
- };
- 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: "#l-curve2", value: "curve" },
- { name: "线段", icon: "#l-polyline", value: "polyline" },
- { name: "直线", icon: "#l-line", value: "line" },
- { name: "脑图曲线", icon: "#l-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 fromArrow = ref("");
- const fromArrows = [
- { icon: "#l-line", value: "" },
- { icon: "#l-from-triangle", value: "triangle" },
- { icon: "#l-from-diamond", value: "diamond" },
- { icon: "#l-from-circle", value: "circle" },
- { icon: "#l-from-lineDown", value: "lineDown" },
- { icon: "#l-from-lineUp", value: "lineUp" },
- { icon: "#l-from-triangleSolid", value: "triangleSolid" },
- { icon: "#l-from-diamondSolid", value: "diamondSolid" },
- { icon: "#l-from-circleSolid", value: "circleSolid" },
- { icon: "#l-from-line", value: "line" },
- ];
- const toArrow = ref("");
- const toArrows = [
- { icon: "#l-line", value: "" },
- { icon: "#l-to-triangle", value: "triangle" },
- { icon: "#l-to-diamond", value: "diamond" },
- { icon: "#l-to-circle", value: "circle" },
- { icon: "#l-to-lineDown", value: "lineDown" },
- { icon: "#l-to-lineUp", value: "lineUp" },
- { icon: "#l-to-triangleSolid", value: "triangleSolid" },
- { icon: "#l-to-diamondSolid", value: "diamondSolid" },
- { icon: "#l-to-circleSolid", value: "circleSolid" },
- { icon: "#l-to-line", value: "line" },
- ];
- const changeFromArrow = (value: string) => {
- fromArrow.value = value;
- // 画布默认值
- meta2d.store.data.fromArrow = value;
- // 活动层的箭头都变化
- if (meta2d.store.active) {
- meta2d.store.active.forEach((pen: Pen) => {
- if (pen.type === PenType.Line) {
- pen.fromArrow = value;
- meta2d.setValue(
- {
- id: pen.id,
- fromArrow: pen.fromArrow,
- },
- {
- render: false,
- }
- );
- }
- });
- meta2d.render();
- }
- };
- const changeToArrow = (value: string) => {
- toArrow.value = value;
- // 画布默认值
- meta2d.store.data.toArrow = value;
- // 活动层的箭头都变化
- if (meta2d.store.active) {
- meta2d.store.active.forEach((pen: Pen) => {
- if (pen.type === PenType.Line) {
- pen.toArrow = value;
- meta2d.setValue(
- {
- id: pen.id,
- toArrow: pen.toArrow,
- },
- {
- render: false,
- }
- );
- }
- });
- meta2d.render();
- }
- };
- const oneD = ref<boolean>(false);
- const alwaysD = ref<boolean>(false);
- const oneDraw = () => {
- if (oneD.value) {
- oneD.value = false;
- if (!alwaysD.value) {
- meta2d.finishDrawLine();
- meta2d.drawLine();
- meta2d.store.options.disableAnchor = true;
- }
- } else {
- oneD.value = true;
- meta2d.drawLine(meta2d.store.options.drawingLineName);
- meta2d.store.options.disableAnchor = false;
- }
- if (alwaysD.value) {
- meta2d.finishDrawLine();
- meta2d.drawLine();
- oneD.value = false;
- alwaysD.value = false;
- meta2d.store.options.disableAnchor = true;
- }
- };
- const alwaysDraw = () => {
- alwaysD.value = true;
- meta2d.drawLine(meta2d.store.options.drawingLineName);
- meta2d.store.options.disableAnchor = false;
- };
- 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();
- meta2d.store.options.disableAnchor = true;
- }, 100);
- }
- }
- }
- };
- const onAddShape = (event: DragEvent | MouseEvent, name: string) => {
- let data: any;
- if (name === "text") {
- data = {
- text: "text",
- 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",
- lineName: "line",
- type: 1,
- };
- }
- if (!(event as DragEvent).dataTransfer) {
- meta2d.canvas.addCaches = deepClone([data]);
- } else {
- (event as DragEvent).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();
- // @ts-ignore
- 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,
- },
- });
- };
- const contextMenuVisible = ref(false);
- const contextMenuValue = ref({
- left: "0px",
- top: "0px",
- });
- const contextMenuType = ref("");
- const contextmenu = ({ e, rect }: { e: any; rect: any }) => {
- contextMenuType.value = "";
- contextMenuValue.value.left = e.clientX + "px";
- contextMenuValue.value.top = e.clientY + "px";
- if (
- meta2d.store.hoverAnchor &&
- meta2d.canvas.hoverType === HoverType.LineAnchor
- ) {
- contextMenuType.value = "anchor";
- } else {
- //其他右键菜单
- }
- if (!contextMenuVisible.value && contextMenuType.value) {
- contextMenuVisible.value = true;
- }
- if (!contextMenuType.value) {
- contextMenuVisible.value = false;
- }
- };
- const canvasClick = () => {
- contextMenuVisible.value = false;
- };
- const changeContextMenuVisible = (e: boolean) => {
- contextMenuVisible.value = e;
- };
- // const comConfirm = () => {
- // connectVisible.value = false;
- // };
- interface Com {
- name?: string;
- type: "mqtt" | "websocket" | "http";
- url?: string;
- //websocket
- protocols?: string;
- //mqtt
- topics?: string;
- options?: {
- clientId?: string;
- username?: string;
- password?: string;
- customClientId?: boolean;
- };
- //http
- http?: string; // http 请求 Url
- httpTimeInterval?: number; // http 请求间隔
- httpHeaders?: HeadersInit; //请求头
- method?: string;
- body?: BodyInit | null;
- }
- const comData = ref<Com[]>([
- {
- name: "连接1",
- type: "mqtt",
- url: "wss://1",
- topics: "",
- options: {
- clientId: "",
- username: "",
- password: "",
- customClientId: false,
- },
- },
- {
- name: "连接2",
- type: "websocket",
- url: "wss://2",
- },
- {
- name: "连接3",
- type: "http",
- url: "wss://3",
- },
- ]);
- const comColumns = ref([
- {
- colKey: "name",
- title: "名称",
- ellipsis: true,
- },
- {
- colKey: "type",
- title: "类型",
- ellipsis: true,
- },
- {
- colKey: "url",
- title: "接口地址",
- ellipsis: true,
- },
- { colKey: "operation", title: "操作", width: 120, foot: "-" },
- ]);
- const comOptions = ref<Com[]>([
- {
- name: "连接A",
- type: "mqtt",
- url: "ws://A",
- },
- {
- name: "连接B",
- type: "mqtt",
- url: "ws://B",
- },
- {
- name: "连接C",
- type: "mqtt",
- url: "ws://C",
- },
- {
- name: "连接D",
- type: "mqtt",
- url: "ws://D",
- },
- {
- name: "连接E",
- type: "mqtt",
- url: "ws://E",
- },
- ]);
- const dataSourceManage = () => {
- comData.value = (meta2d.store.data as any).comData; //确定字段
- dsData.value = (meta2d.store.data as any).dsData;
- };
- const onOptionClick = (item: Com) => {
- comData.value.push(item);
- popupVisible.value = false;
- };
- const popupVisible = ref(false);
- const onInputChange = (e) => {
- console.log("change", e);
- };
- const onPopupVisibleChange = (val: boolean) => {
- popupVisible.value = val;
- };
- const comType = ref("add"); //or edit
- const addCom = () => {
- comType.value = "add";
- com.value = {
- name: "",
- type: "mqtt", //默认
- url: "",
- options: {
- clientId: "",
- username: "",
- password: "",
- customClientId: false,
- },
- };
- comShow.value = false;
- };
- const editCom = (row: Com) => {
- comType.value = "edit";
- com.value = row;
- comShow.value = false;
- };
- const deleteCom = (index: number) => {
- comData.value.splice(index, 1);
- dataToCom(comData.value);
- };
- //将配置的通信列表转成核心库能识别的格式
- const dataToCom = (data: Com[]) => {
- const mqtts: any = [];
- const https: any = [];
- const websockets: any = [];
- data.forEach((item) => {
- if (item.type === "http") {
- https.push({
- http: item.http,
- httpTimeInterval: item.httpTimeInterval,
- httpHeaders: item.httpHeaders,
- method: item.method,
- body: item.body,
- });
- } else if (item.type === "mqtt") {
- mqtts.push({
- url: item.url,
- options: {
- clientId: item.options?.clientId,
- username: item.options?.username,
- password: item.options?.password,
- customClientId: item.options?.customClientId,
- },
- topics: item.topics,
- });
- } else if (item.type === "websocket") {
- websockets.push({
- url: item.url,
- protocols: item.protocols,
- });
- }
- });
- meta2d.store.data.mqtts = mqtts;
- meta2d.store.data.https = https;
- meta2d.store.data.websockets = websockets;
- //TODO 建立通信连接
- meta2d.connectSocket();
- };
- const comShow = ref(true);
- const comBack = () => {
- comShow.value = true;
- };
- const com = ref<Com>({
- name: "连接E",
- type: "mqtt",
- url: "ws://E",
- options: {
- clientId: "",
- username: "",
- password: "",
- customClientId: false,
- },
- });
- const comTypeChange = (e: string) => {
- console.log(e, com.value.type);
- if (e === "mqtt") {
- Object.assign(com.value, {
- options: {
- clientId: "",
- username: "",
- password: "",
- customClientId: false,
- },
- });
- } else if (e === "websocket") {
- Object.assign(com.value, {
- protocols: "",
- });
- } else {
- Object.assign(com.value, {
- http: "",
- httpTimeInterval: 1000,
- httpHeaders: "",
- method: "GET",
- body: "",
- });
- }
- };
- const comHttpMethodChange = (e: string) => {
- if (e === "GET") {
- com.value.body = null;
- }
- };
- const dsData = ref([
- {
- label: "数据点1",
- key: "a-1",
- value: "12",
- type: "number/select/bool/color",
- },
- {
- label: "数据点2",
- key: "a-2",
- value: "12",
- type: "number/select/bool/color",
- },
- {
- label: "数据点3",
- key: "a-3",
- value: "12",
- type: "number/select/bool/color",
- },
- ]);
- const dsColumns = ref([
- {
- colKey: "label",
- title: "名称(数据点ID)",
- ellipsis: true,
- },
- {
- colKey: "type",
- title: "类型",
- ellipsis: true,
- },
- {
- colKey: "value",
- title: "值",
- ellipsis: true,
- },
- ]);
- const saveForCurrent = () => {
- comData.value.push(com.value);
- comShow.value = true;
- dataToCom(comData.value);
- };
- const saveToServer = () => {
- //TODO 将com保存到服务器
- comData.value.push(com.value);
- comShow.value = true;
- dataToCom(comData.value);
- };
- const importDataSet = async () => {
- let columns: any = [
- {
- header: "名称",
- key: "label",
- },
- {
- header: "数据ID",
- key: "key",
- },
- {
- header: "类型",
- key: "type",
- },
- {
- header: "值",
- key: "value",
- },
- ];
- // dsData.value = [];
- let data = await importExcel(columns);
- console.log("data", data);
- // setTimeout(() => {
- dsData.value = data;
- // }, 2000);
- };
- const downloadDataSet = () => {
- let name = "数据集示例";
- let columns = [
- {
- header: "名称",
- key: "label",
- },
- {
- header: "数据ID",
- key: "id",
- },
- {
- header: "类型",
- key: "type",
- },
- {
- header: "值",
- key: "value",
- },
- ];
- let data = [
- ["数据集1", "dataId-1", "string", "zzz"],
- ["数据集2", "dataId-2", "boolean", true],
- ["数据集3", "dataId-3", "number", 12],
- ];
- saveAsExcel(name, columns, data);
- };
- </script>
- <style lang="postcss" scoped>
- .meta2d {
- display: flex;
- flex-direction: column;
- background-color: var(--color-background-editor);
- border-left: 1px solid var(--color-border);
- .tools {
- display: flex;
- font-size: 12px;
- background-color: var(--color-background);
- height: 40px;
- flex-shrink: 0;
- padding: 0 12px;
- a {
- display: flex;
- align-items: center;
- height: 100%;
- padding: 0 10px;
- color: var(--color);
- text-decoration: none;
- .l-icon {
- width: 16px;
- height: 16px;
- }
- &:hover {
- color: var(--color-primary);
- }
- }
- .t-icon {
- font-size: 16px;
- }
- }
- #meta2d {
- border-top: 1px solid var(--color-background-input);
- height: calc(100vh - 81px);
- :deep(.meta2d-map) {
- background: var(--color-background);
- }
- }
- }
- </style>
- <style lang="postcss">
- .t-dialog {
- .t-dialog__body {
- height: 450px;
- /* overflow: auto; */
- }
- .form-item {
- display: flex;
- margin-bottom: 8px;
- label {
- width: 100px;
- }
- }
- .t-textarea {
- width: calc(100% - 150px);
- }
- .vip-label {
- font-size: 10px;
- background-color: #ff400030;
- color: var(--color-bland);
- padding: 0 6px;
- margin-left: 4px;
- border-radius: 2px;
- }
- .t-tab-panel {
- overflow: auto;
- height: 395px;
- }
- }
- </style>
|