Data.vue 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675
  1. <template>
  2. <div class="content" :draggable="false" v-if="group === '列表'">
  3. <div class="flex between">
  4. <div class="title">数据{{ group }}</div>
  5. <div class="flex between">
  6. <t-dropdown :minColumnWidth="168" :hide-after-item-click="false">
  7. <t-tooltip content="导入数据列表" placement="top">
  8. <div class="icon-box" @mouseenter="getDatasets()">
  9. <!-- <FileImportIcon /> -->
  10. <AddIcon />
  11. </div>
  12. </t-tooltip>
  13. <t-dropdown-menu>
  14. <t-dropdown-item :value="2" :divider="true" @click="showAddData()">
  15. 新建变量
  16. </t-dropdown-item>
  17. <t-dropdown-item :value="1">
  18. <!-- <t-dropdown
  19. :minColumnWidth="168"
  20. :maxColumnWidth="300"
  21. :trigger="'click'"
  22. :hide-after-item-click="false"
  23. > -->
  24. 我的变量列表
  25. <!-- </t-dropdown> -->
  26. <t-dropdown-menu>
  27. <t-dropdown-item v-if="!user.id"> 请先登录! </t-dropdown-item>
  28. <t-dropdown-item v-else-if="!data.datasetList.length">
  29. 暂无数据
  30. </t-dropdown-item>
  31. <template v-else>
  32. <t-dropdown-item v-for="(dataset, i) in data.datasetList">
  33. <div
  34. class="hover-background item"
  35. @click="onSelDataset(dataset.id)"
  36. >
  37. <div style="font-size: 14px">{{ dataset.name }}</div>
  38. <div v-if="dataset.url" class="desc">
  39. {{ dataset.url }}
  40. </div>
  41. <div v-else class="desc">自定义</div>
  42. <!-- <t-popconfirm
  43. content="确认删除吗?"
  44. @confirm.stop="onDelDataset(dataset, i)"
  45. @enter.stop
  46. @mouseenter.stop
  47. > -->
  48. <span class="del" @click.stop="onDelDataset(dataset, i)">
  49. <delete-icon />
  50. </span>
  51. <!-- </t-popconfirm> -->
  52. </div>
  53. </t-dropdown-item>
  54. </template>
  55. </t-dropdown-menu>
  56. </t-dropdown-item>
  57. <t-dropdown-item :value="3" :divider="true">
  58. 在线接口
  59. <t-dropdown-menu
  60. class="menu-item-input"
  61. style="max-width: 216px !important"
  62. >
  63. <t-dropdown-item
  64. :value="3 - 1"
  65. style="max-width: 216px !important"
  66. >
  67. <div
  68. class="data-input-box"
  69. style="max-width: 216px !important"
  70. >
  71. <t-input
  72. label="地址:"
  73. style="width: 216px"
  74. placeholder="请输入地址"
  75. v-model="data.dataset.url"
  76. @blur="getDatas"
  77. @enter="getDatas"
  78. >
  79. <template #suffixIcon>
  80. <CloudDownloadIcon
  81. @click="getDatas"
  82. :style="{ cursor: 'pointer' }"
  83. />
  84. </template>
  85. </t-input>
  86. </div>
  87. </t-dropdown-item>
  88. </t-dropdown-menu>
  89. </t-dropdown-item>
  90. <t-dropdown-item :value="2" @click="importDataset">
  91. 从Excel导入
  92. </t-dropdown-item>
  93. <t-dropdown-item :value="4">
  94. <a
  95. :href="
  96. isDownload
  97. ? '/v/data.xlsx'
  98. : cdn
  99. ? cdn + '/v/data.xlsx'
  100. : '/data.xlsx'
  101. "
  102. style="color: var(--td-text-color-primary)"
  103. @click.stop
  104. >
  105. 下载Excel示例
  106. </a>
  107. </t-dropdown-item>
  108. </t-dropdown-menu>
  109. </t-dropdown>
  110. <t-dropdown :minColumnWidth="168">
  111. <t-tooltip content="导出数据列表" placement="top">
  112. <div class="icon-box">
  113. <!-- <FileExportIcon /> -->
  114. <Download1Icon />
  115. </div>
  116. </t-tooltip>
  117. <t-dropdown-menu>
  118. <t-dropdown-item @click="downloadAsJson"
  119. >下载为JSON</t-dropdown-item
  120. >
  121. <t-dropdown-item @click="downloadAsExcel"
  122. >下载为Excel</t-dropdown-item
  123. >
  124. <t-dropdown-item @click="onOkDataset()"
  125. >保存为我的数据列表</t-dropdown-item
  126. >
  127. </t-dropdown-menu>
  128. </t-dropdown>
  129. <t-popconfirm
  130. content="确认清空数据列表吗?"
  131. placement="bottom"
  132. @confirm="clearData"
  133. >
  134. <t-tooltip content="清空列表" placement="top">
  135. <div class="icon-box">
  136. <DeleteIcon />
  137. </div>
  138. </t-tooltip>
  139. </t-popconfirm>
  140. <!-- <t-tooltip content="新建变量" placement="top">
  141. <div class="icon-box">
  142. <AddIcon @click="showAddData()" />
  143. </div>
  144. </t-tooltip> -->
  145. </div>
  146. </div>
  147. <div class="flex between mt-8">
  148. <t-tooltip content="可批量导入数据图元到画布" placement="top">
  149. <t-checkbox v-model="data.checkAll">批量导入到画布</t-checkbox>
  150. </t-tooltip>
  151. <t-tooltip content="开启全局数据模拟" placement="top">
  152. <t-checkbox v-model="data.enableMock" @change="onChangeMock"
  153. >开启模拟</t-checkbox
  154. >
  155. </t-tooltip>
  156. </div>
  157. <div class="input-search">
  158. <div class="btn">
  159. <search-icon class="hover" />
  160. <!-- <img src="/img/icon_search_gray.svg" /> -->
  161. </div>
  162. <t-input
  163. v-model="search"
  164. @change="onSearch"
  165. @enter="onSearch"
  166. placeholder="搜索我的数据列表"
  167. />
  168. </div>
  169. <div
  170. id="dragElem"
  171. style="position: absolute; left: 0; z-index: -999"
  172. v-if="data.dataset?.devices?.length"
  173. >
  174. <div
  175. class="flex mt-4"
  176. v-for="(device, i) in data.dataset.devices"
  177. v-show="device.checked"
  178. >
  179. <div style="width: 150px; text-align: end">{{ device.label }}:</div>
  180. <div class="ml-4">{{ device.mock }}</div>
  181. </div>
  182. </div>
  183. <div
  184. class="mt-16 data-list"
  185. v-if="data.dataset?.devices?.length"
  186. :draggable="data.checkAll ? true : false"
  187. @dragstart="onAddShape($event, data.dataset?.devices)"
  188. @dragend="onAddShapeEnd"
  189. >
  190. <div
  191. v-for="(device, i) in data.dataset.devices"
  192. v-show="!(device.show === false)"
  193. >
  194. <div
  195. class="flex between data-title"
  196. :class="
  197. (device.expend ? '' : 'data-title-hover') +
  198. ' ' +
  199. (device.checked ? 'drag-checked' : '')
  200. "
  201. :draggable="data.checkAll ? false : true"
  202. @dragstart="onAddShape($event, device)"
  203. @dragend="onAddShapeEnd"
  204. >
  205. <div
  206. class="flex"
  207. style="height: 32px; line-height: 32px; cursor: pointer"
  208. >
  209. <!-- <MinusCircleIcon
  210. v-if="device.expend"
  211. @click="device.expend = false"
  212. />
  213. <AddCircleIcon v-else @click="device.expend = true" /> -->
  214. <t-checkbox
  215. v-if="data.checkAll"
  216. v-model="device.checked"
  217. ></t-checkbox>
  218. <template v-else>
  219. <CaretDownSmallIcon
  220. style="height: auto"
  221. v-if="device.expend"
  222. @click="device.expend = false"
  223. />
  224. <CaretRightSmallIcon
  225. style="height: auto"
  226. v-else
  227. @click="device.expend = true"
  228. />
  229. </template>
  230. <p class="title-span">{{ device.label }}</p>
  231. </div>
  232. <div class="flex">
  233. <t-tooltip content="开启单个模拟" placement="top">
  234. <div
  235. class="icon-box icon-item-box"
  236. @click="device.enableMock = !device.enableMock"
  237. >
  238. <RouterWaveIcon
  239. :style="{
  240. color: device.enableMock ? 'var(--color-primary)' : '',
  241. }"
  242. />
  243. </div>
  244. </t-tooltip>
  245. <div class="icon-box icon-item-box" @click="showAddData(device, i)">
  246. <Edit2Icon />
  247. </div>
  248. <t-popconfirm content="确认删除吗?" @confirm="deleteData(i)">
  249. <div class="icon-box icon-item-box">
  250. <DeleteIcon />
  251. </div>
  252. </t-popconfirm>
  253. </div>
  254. </div>
  255. <div class="data-body">
  256. <div v-if="device.expend" class="flex">
  257. <span class="form-line"></span>
  258. <div>
  259. <!-- <div class="form-item form-data-item mt-8">
  260. <label>显示名称</label>
  261. <div :title="device.label">{{ device.label }}</div>
  262. </div> -->
  263. <div class="form-item form-data-item">
  264. <label>变量名:</label>
  265. <div :title="device.id">{{ device.id }}</div>
  266. </div>
  267. <div class="form-item form-data-item">
  268. <label>类型:</label>
  269. <div :title="device.type">{{ device.type }}</div>
  270. </div>
  271. <div class="form-item form-data-item">
  272. <label>值范围:</label>
  273. <div :title="device.mock">{{ device.mock }}</div>
  274. </div>
  275. </div>
  276. </div>
  277. </div>
  278. </div>
  279. </div>
  280. <div class="flex column middle nodata" v-else>
  281. <img src="/img/no-data.png" />
  282. <div class="gray center">暂无数据</div>
  283. <div class="mt-20">
  284. <t-button theme="primary" @click="showAddData()"> 新建变量 </t-button>
  285. </div>
  286. </div>
  287. </div>
  288. <div class="content" v-if="group === '获取'">
  289. <div class="flex between">
  290. <div class="title">数据{{ group }}</div>
  291. <div class="flex between">
  292. <t-tooltip content="添加数据获取" placement="top">
  293. <div class="icon-box" @click="addNetwork()">
  294. <AddIcon />
  295. </div>
  296. </t-tooltip>
  297. </div>
  298. </div>
  299. <div class="input-search select-search">
  300. <t-select
  301. v-model="data.networkId"
  302. filterable
  303. clearable
  304. placeholder="搜索我的数据获取"
  305. @focus="onInputNetwork"
  306. :on-search="onInputNetwork"
  307. @change="onSelectNetWork"
  308. >
  309. <template #prefixIcon>
  310. <SearchIcon />
  311. </template>
  312. <t-option
  313. class="network-option"
  314. v-for="(item, i) in data.networkList"
  315. :key="item.id"
  316. :value="item.id"
  317. :label="item.name"
  318. >
  319. <div class="hover-background item">
  320. <div style="font-size: 14px">{{ item.name }}</div>
  321. <div class="desc">
  322. {{ item.url }}
  323. </div>
  324. <span class="del" @click.stop="onDelNetWork(item, i)">
  325. <delete-icon />
  326. </span>
  327. </div>
  328. </t-option>
  329. </t-select>
  330. </div>
  331. <div class="mt-16 data-list" v-if="data.networks.length">
  332. <div
  333. v-for="(network, i) in data.networks"
  334. v-show="!(network.show === false)"
  335. >
  336. <div
  337. class="flex between data-title"
  338. :class="network.expend ? '' : 'data-title-hover'"
  339. >
  340. <div style="height: 32px; line-height: 32px; cursor: pointer">
  341. <CaretDownSmallIcon
  342. style="height: auto"
  343. v-if="network.expend"
  344. @click="network.expend = false"
  345. />
  346. <CaretRightSmallIcon
  347. style="height: auto"
  348. v-else
  349. @click="network.expend = true"
  350. />
  351. {{ network.name }}
  352. </div>
  353. <div class="flex">
  354. <div class="icon-box" @click="editNetwork(network, i)">
  355. <Edit2Icon />
  356. </div>
  357. <t-popconfirm content="确认删除吗?" @confirm="deleteNetwork(i)">
  358. <div class="icon-box">
  359. <DeleteIcon />
  360. </div>
  361. </t-popconfirm>
  362. </div>
  363. </div>
  364. <div class="data-body">
  365. <div v-if="network.expend" class="flex">
  366. <span class="form-line" style="height: 88px"></span>
  367. <div>
  368. <div class="form-item form-data-item mt-8">
  369. <label>显示名称:</label>
  370. <div :title="network.name">{{ network.name }}</div>
  371. </div>
  372. <div class="form-item form-data-item mt-8">
  373. <label>通信方式:</label>
  374. <div :title="network.protocol">{{ network.protocol }}</div>
  375. </div>
  376. <div class="form-item form-data-item mt-8">
  377. <label>URL地址:</label>
  378. <div :title="network.url">{{ network.url }}</div>
  379. </div>
  380. </div>
  381. </div>
  382. </div>
  383. </div>
  384. </div>
  385. <div class="flex column middle nodata" v-else>
  386. <img src="/img/no-data.png" />
  387. <div class="gray center">暂无数据</div>
  388. <div class="mt-20">
  389. <t-button theme="primary" @click="addNetwork()">
  390. 添加数据获取
  391. </t-button>
  392. </div>
  393. </div>
  394. </div>
  395. <div class="content" v-if="group === '监听'">
  396. <div class="flex between">
  397. <div class="title">数据{{ group }}</div>
  398. </div>
  399. <div class="mt-8">
  400. <CodeEditor
  401. :key="data.randomkey"
  402. v-model="data.socketCbJs"
  403. style="min-height: 300px"
  404. @change="onSocketCbJsChange"
  405. />
  406. <div class="data-full-icon hover">
  407. <Fullscreen2Icon @click="showDataTransformation" />
  408. </div>
  409. </div>
  410. <div class="mt-16">
  411. 参考文档:
  412. <a
  413. target="_blank"
  414. href="https://doc.le5le.com/document/136233394#%E8%A7%A3%E6%9E%90%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F"
  415. >
  416. 解析自定义格式数据</a
  417. >
  418. </div>
  419. </div>
  420. <!-- <div class="content" v-show="group === '数据模拟'">
  421. <div class="flex between">
  422. <div class="title">{{ group }}</div>
  423. <div class="flex between">
  424. <t-dropdown :minColumnWidth="168">
  425. <t-tooltip
  426. :content="
  427. data.dataset?.devices?.length
  428. ? '添加一条数据模拟'
  429. : '请先添加数据列表'
  430. "
  431. placement="top"
  432. >
  433. <div class="icon-box">
  434. <AddIcon />
  435. </div>
  436. </t-tooltip>
  437. <t-dropdown-menu v-if="data.dataset?.devices?.length">
  438. <t-dropdown-item >
  439. <div class="hover-background item" @click="onAddAllMock">导入所有数据列表</div>
  440. </t-dropdown-item>
  441. <t-dropdown-item v-for="(dataset, i) in data.dataset?.devices">
  442. <div class="hover-background item" @click="onAddmock(dataset)">
  443. <div style="font-size: 14px">{{ dataset.label }}</div>
  444. <div class="desc">
  445. {{ dataset.id }}
  446. </div>
  447. </div>
  448. </t-dropdown-item>
  449. </t-dropdown-menu>
  450. </t-dropdown>
  451. </div>
  452. </div>
  453. <div class="mt-16 data-list data-mock-list" v-if="data.dataMocks.length">
  454. <div v-for="(item, i) in data.dataMocks" v-show="!(item.show === false)">
  455. <div
  456. class="flex between data-title"
  457. :class="item.expend ? '' : 'data-title-hover'"
  458. >
  459. <div style="height: 32px; line-height: 32px; cursor: pointer">
  460. <MinusCircleIcon v-if="item.expend" @click="item.expend = false" />
  461. <AddCircleIcon v-else @click="item.expend = true" />
  462. {{ item.name }}
  463. </div>
  464. <div class="flex">
  465. <t-popconfirm content="确认删除吗?" @confirm="deleteMock(i)">
  466. <div class="icon-box">
  467. <DeleteIcon />
  468. </div>
  469. </t-popconfirm>
  470. </div>
  471. </div>
  472. <div class="data-body">
  473. <div v-if="item.expend" class="flex">
  474. <span class="form-line" style="height: 150px"></span>
  475. <div>
  476. <div class="form-item form-data-item mt-8">
  477. <label>显示名称</label>
  478. <div :title="item.name">{{ item.name }}</div>
  479. </div>
  480. <div class="form-item form-data-item mt-8">
  481. <label>变量名</label>
  482. <div :title="item.id">{{ item.id }}</div>
  483. </div>
  484. <div class="form-item form-data-item form-mock-item mt-8">
  485. <label>模拟值
  486. <t-tooltip placement="top">
  487. <template #content>
  488. <div style="font-size:10px;">
  489. 模拟值说明<br/>
  490. 固定值:直接填写,例如:10<br/>
  491. 随机值:值1,值2,...。例如:1,2,3,4,5<br/>
  492. 范围数字:最小值-最大值。例如:0-1.0 或 0-100<br/>
  493. 随机字符串: [长度]。例如:[8]<br/>
  494. </div>
  495. </template>
  496. <HelpCircleIcon style="font-size: 12px;"/>
  497. </t-tooltip>
  498. </label>
  499. <t-input v-model="item.mock" />
  500. </div>
  501. <div class="form-item form-data-item form-mock-item mt-8">
  502. <label>类型</label>
  503. <t-select
  504. class="w-full"
  505. :options="typeOptions"
  506. v-model="item.type"
  507. placeholder="字符串"
  508. />
  509. </div>
  510. <div class="form-item form-swicth-item mt-8">
  511. <label>开启</label>
  512. <t-switch class="mt-8" size="small" v-model="item.enableMock" />
  513. </div>
  514. </div>
  515. </div>
  516. </div>
  517. </div>
  518. </div>
  519. <div class="flex column middle nodata" v-else>
  520. <img src="/img/no-data.png" />
  521. <div class="gray center">暂无数据</div>
  522. </div>
  523. <div class="flex between mt-24">
  524. <t-checkbox
  525. style="display: inline"
  526. v-model="data.enableMock"
  527. @change="onChangeMock"
  528. >
  529. 开启模拟数据
  530. </t-checkbox>
  531. </div>
  532. </div> -->
  533. <t-dialog
  534. v-if="addDataDialog.show"
  535. :visible="true"
  536. class="data-dialog"
  537. :header="addDataDialog.header"
  538. @close="addDataDialog.show = false"
  539. @confirm="onOkAddData"
  540. >
  541. <!-- <div class="form-item mt-16">
  542. <label>设备</label>
  543. <t-input v-model="addDataDialog.data.device" placeholder="设备名称" />
  544. </div> -->
  545. <div class="form-item mt-16">
  546. <label>显示名称</label>
  547. <t-input
  548. @change="changeDataLabel($event)"
  549. :value="addDataDialog.data.label"
  550. placeholder="变量简短描述"
  551. />
  552. </div>
  553. <div class="form-item mt-16">
  554. <label>变量名</label>
  555. <t-input
  556. @change="changeDataID($event)"
  557. :value="addDataDialog.data.id"
  558. placeholder="变量名"
  559. />
  560. </div>
  561. <div class="form-item mt-16">
  562. <label>类型</label>
  563. <t-select
  564. class="w-full"
  565. :options="typeOptions"
  566. v-model="addDataDialog.data.type"
  567. placeholder="字符串"
  568. @change="addDataDialog.data.value = null"
  569. />
  570. </div>
  571. <div class="form-item mt-16">
  572. <label
  573. >值范围
  574. <t-tooltip content="可用做数据模拟" placement="top">
  575. <HelpCircleIcon style="font-size: 12px" />
  576. </t-tooltip>
  577. </label>
  578. <div class="w-full">
  579. <t-input v-model="addDataDialog.data.mock" placeholder="值范围" />
  580. <h6 class="desc mt-8" style="font-size: 12px">值范围说明</h6>
  581. <ul class="desc ml-16" style="font-size: 12px">
  582. <li>
  583. <label class="inline" style="width: 80px">固定值:</label>
  584. 直接填写,例如:10
  585. </li>
  586. <li>
  587. <label class="inline" style="width: 80px">随机值:</label>
  588. 值1,值2,...。例如:1,2,3,4,5
  589. </li>
  590. <li>
  591. <label class="inline" style="width: 80px">范围数字:</label>
  592. 最小值-最大值。例如:0-1.0 或 0-100
  593. </li>
  594. <li>
  595. <label class="inline" style="width: 80px">随机字符串:</label>
  596. [长度]。例如:[8]
  597. </li>
  598. </ul>
  599. </div>
  600. </div>
  601. </t-dialog>
  602. <t-dialog
  603. v-if="networkDialog.show"
  604. :visible="true"
  605. width="800px"
  606. class="data-dialog"
  607. :header="networkDialog.header"
  608. @close="networkDialog.show = false"
  609. @confirm="onOkNetwork"
  610. >
  611. <template #footer>
  612. <div class="flex mr-8" style="justify-content: end">
  613. <t-checkbox v-model="networkDialog.save" class="mr-12">
  614. 同时保存到我的数据获取
  615. </t-checkbox>
  616. <t-button @click="onOkNetwork">确定</t-button>
  617. </div>
  618. </template>
  619. <div style="height: 300px; padding: 8px; overflow-y: auto">
  620. <Network v-model="networkDialog.network" />
  621. </div>
  622. </t-dialog>
  623. <t-dialog
  624. v-if="dataTransformationDialog.show"
  625. :visible="true"
  626. header="数据监听"
  627. @confirm="onOkDataTransformation"
  628. @close="dataTransformationDialog.show = false"
  629. :width="800"
  630. >
  631. <CodeEditor v-model="dataTransformationDialog.data" style="height: 300px" />
  632. <div class="mt-8">
  633. 参考文档:
  634. <a
  635. target="_blank"
  636. href="https://doc.le5le.com/document/136233394#%E8%A7%A3%E6%9E%90%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F"
  637. >
  638. 解析自定义格式数据</a
  639. >
  640. </div>
  641. </t-dialog>
  642. </template>
  643. <script lang="ts" setup>
  644. import { reactive, defineComponent, ref, onMounted, toRaw } from 'vue';
  645. import {
  646. FileImportIcon,
  647. FileExportIcon,
  648. DeleteIcon,
  649. AddIcon,
  650. AddCircleIcon,
  651. MinusCircleIcon,
  652. Edit2Icon,
  653. FileExcelIcon,
  654. CloudDownloadIcon,
  655. SearchIcon,
  656. Fullscreen2Icon,
  657. FullscreenExit1Icon,
  658. HelpCircleIcon,
  659. CaretDownSmallIcon,
  660. CaretRightSmallIcon,
  661. RouterWaveIcon,
  662. ArrowUpDown3Icon,
  663. Download1Icon
  664. } from 'tdesign-icons-vue-next';
  665. import { typeOptions } from '@/services/common';
  666. import { MessagePlugin } from 'tdesign-vue-next';
  667. import { Pen, deepClone } from '@meta2d/core';
  668. import { useDot } from '@/services/common';
  669. import { isDownload } from '@/services/defaults';
  670. import { cdn } from '@/services/api';
  671. import { importExcel, saveAsExcel } from '@/services/excel';
  672. import axios from 'axios';
  673. import { transformData } from '@/services/utils';
  674. import { debounce } from '@/services/debouce';
  675. import Network from '@/views/components/Network.vue';
  676. import CodeEditor from '@/views/components/common/CodeEditor.vue';
  677. import { s8 } from '@/services/random';
  678. import { useUser } from '@/services/user';
  679. const props = defineProps<{
  680. group: string;
  681. }>();
  682. const { user } = useUser();
  683. const { dot, setDot } = useDot();
  684. const data = reactive({
  685. //列表
  686. datesetBak: {},
  687. datasetId: '',
  688. networks: [],
  689. datasetList: [],
  690. dataset: {
  691. name: '',
  692. id: '',
  693. url: '',
  694. devices: [],
  695. } as any,
  696. checkAll: false,
  697. //获取
  698. networkList: [],
  699. input: '',
  700. popupVisible: false,
  701. networkId: '',
  702. //监听
  703. socketCbJs: '',
  704. full: false,
  705. dataMocks: [
  706. // {
  707. // name: '',
  708. // id: '',
  709. // enableMock: true,
  710. // mock: 'aaa',
  711. // type: 'string',
  712. // expend: true,
  713. // show: false,
  714. // },
  715. ],
  716. enableMock: false,
  717. randomkey: s8(),
  718. });
  719. onMounted(() => {
  720. data.networks = meta2d.store.data.networks || [];
  721. data.dataset = (meta2d.store.data as any).dataset || {};
  722. data.socketCbJs = meta2d.store.data.socketCbJs || '';
  723. // data.dataMocks = meta2d.store.data.dataMocks || [];
  724. data.enableMock = meta2d.store.data.enableMock || false;
  725. // getNetworks();
  726. // getDatasets();
  727. });
  728. const addDataDialog = reactive<any>({});
  729. const showAddData = (row?: any, index?: number) => {
  730. if (row) {
  731. addDataDialog.header = '编辑数据';
  732. addDataDialog.data = JSON.parse(JSON.stringify(row));
  733. addDataDialog.index = index;
  734. } else {
  735. addDataDialog.header = '添加数据';
  736. addDataDialog.data = { type: 'string', expend: true };
  737. }
  738. addDataDialog.show = true;
  739. };
  740. const deleteData = (row: any) => {
  741. data.dataset.devices.splice(row, 1);
  742. (meta2d.store.data as any).dataset = data.dataset;
  743. setDot(true);
  744. };
  745. const clearData = () => {
  746. data.datasetId = undefined;
  747. data.dataset.id = undefined;
  748. data.dataset.name = undefined;
  749. data.dataset.url = undefined;
  750. data.dataset.devices = [];
  751. setDot(true);
  752. };
  753. const changeDataLabel = (value) => {
  754. if (!value) {
  755. MessagePlugin.error('显示名称不能为空!');
  756. return;
  757. }
  758. let item = data.dataset.devices?.filter((item) => item.label === value);
  759. if (item && item.length) {
  760. MessagePlugin.error('显示名称重复!');
  761. return;
  762. }
  763. addDataDialog.data.label = value;
  764. };
  765. const changeDataID = (value) => {
  766. if (!value) {
  767. MessagePlugin.error('变量名不能为空!');
  768. return;
  769. }
  770. let item = data.dataset.devices?.filter((item) => item.id === value);
  771. if (item && item.length) {
  772. MessagePlugin.error('变量名重复!');
  773. return;
  774. }
  775. addDataDialog.data.id = value;
  776. };
  777. const onOkAddData = () => {
  778. if (!addDataDialog.data.label) {
  779. MessagePlugin.error('请填写名称');
  780. return;
  781. }
  782. if (!addDataDialog.data.id) {
  783. MessagePlugin.error('请填写数据ID');
  784. return;
  785. }
  786. if (!data.dataset.devices) {
  787. data.dataset.devices = [];
  788. }
  789. if (addDataDialog.header === '添加数据') {
  790. data.dataset.devices.push(addDataDialog.data);
  791. } else {
  792. data.dataset.devices[addDataDialog.index] = addDataDialog.data;
  793. //更新所有绑定该id的pen label
  794. let binds = meta2d.store.bind[addDataDialog.data.id];
  795. if (binds) {
  796. binds.forEach((item) => {
  797. const pen: Pen = meta2d.findOne(item.id);
  798. pen.realTimes &&
  799. pen.realTimes.forEach((_realTime) => {
  800. if (_realTime.key === item.key) {
  801. _realTime.bind.label = addDataDialog.data.label;
  802. }
  803. });
  804. });
  805. }
  806. }
  807. (meta2d.store.data as any).dataset = data.dataset;
  808. addDataDialog.show = false;
  809. };
  810. const importDataset = async () => {
  811. let columns: any = [
  812. {
  813. header: '设备',
  814. key: 'device',
  815. },
  816. {
  817. header: '显示名称',
  818. key: 'label',
  819. },
  820. {
  821. header: '变量名',
  822. key: 'id',
  823. },
  824. {
  825. header: '类型',
  826. key: 'type',
  827. },
  828. {
  829. header: '值范围',
  830. key: 'mock',
  831. },
  832. ];
  833. const _data: any = await importExcel(columns);
  834. _data.forEach((item) => {
  835. if (item.device) {
  836. item.label = item.device + '-' + item.label;
  837. delete item.device;
  838. }
  839. });
  840. mergeDataset(data.dataset, _data);
  841. (meta2d.store.data as any).dataset = data.dataset;
  842. };
  843. const downloadAsExcel = () => {
  844. if (!(data.dataset.devices && data.dataset.devices.length)) {
  845. MessagePlugin.error('变量列表不能为空!');
  846. return;
  847. }
  848. const name = meta2d.store.data.name;
  849. const columns: any[] = [
  850. {
  851. key: 'label',
  852. header: '显示名称',
  853. },
  854. {
  855. key: 'id',
  856. header: '变量名',
  857. },
  858. {
  859. key: 'type',
  860. header: '类型',
  861. },
  862. {
  863. key: 'mock',
  864. header: '值范围',
  865. },
  866. ];
  867. saveAsExcel(name, columns, data.dataset.devices);
  868. };
  869. const downloadAsJson = () => {
  870. if (!(data.dataset.devices && data.dataset.devices.length)) {
  871. MessagePlugin.error('变量列表不能为空!');
  872. return;
  873. }
  874. import('file-saver').then(({ saveAs }) => {
  875. saveAs(
  876. new Blob([JSON.stringify(data.dataset)], {
  877. type: 'text/plain;charset=utf-8',
  878. }),
  879. `${data.dataset.name || '未命名'}.json`
  880. );
  881. });
  882. };
  883. const onOkDataset = async (saveas = false) => {
  884. // if (!dataDialog.dataset.name) {
  885. // MessagePlugin.error('名称不能为空');
  886. // return;
  887. // }
  888. if (!(data.dataset.devices && data.dataset.devices.length)) {
  889. MessagePlugin.error('变量列表不能为空');
  890. return;
  891. }
  892. const dataset = JSON.parse(JSON.stringify(data.dataset));
  893. let _data = dataset;
  894. _data.type = 'dataset';
  895. if (!_data.name) {
  896. _data.name = meta2d.store.data.name;
  897. }
  898. _data = transformData(dataset, 'toNetwork');
  899. // 保存到我的数据源
  900. if (saveas || !dataset.id) {
  901. delete dataset.id;
  902. delete dataset._id;
  903. dataset.type = 'dataset';
  904. const ret: any = await axios.post(`/api/data/datasource/add`, _data);
  905. if (!ret) {
  906. return;
  907. }
  908. ret.id = ret.id || ret._id;
  909. data.datasetId = ret.id;
  910. dataset.id = ret.id;
  911. data.dataset.id = ret.id;
  912. data.datasetList.push(dataset);
  913. } else {
  914. const ret: any = await axios.post(`/api/data/datasource/update`, _data);
  915. if (!ret) {
  916. return;
  917. }
  918. data.datasetList.forEach((item: any, index: number) => {
  919. if (
  920. item.id === dataset.id ||
  921. (item._id === dataset._id && dataset._id != undefined)
  922. ) {
  923. data.datasetList.splice(index, 1, dataset);
  924. }
  925. });
  926. }
  927. delete dataset.devices;
  928. // @ts-ignore
  929. // console.log("dataset",dataset);
  930. // meta2d.store.data.dataset = dataset;
  931. // setDot(true);
  932. data.editDataset = false;
  933. delete data.datesetBak;
  934. };
  935. const mergeDataset = (arr1: any, arr2: any[]) => {
  936. if (!(arr2 && arr2.length)) {
  937. return;
  938. }
  939. if (!arr1.devices) {
  940. arr1.devices = [];
  941. }
  942. arr2.forEach((item) => {
  943. let index = arr1.devices.findIndex((elem) => elem.id === item.id);
  944. if (index >= 0) {
  945. Object.assign(arr1.devices[index], item);
  946. } else {
  947. arr1.devices.push(item);
  948. }
  949. });
  950. };
  951. const getDatas = async () => {
  952. if (!data.dataset.url) {
  953. return;
  954. }
  955. const ret = await axios.get(data.dataset.url);
  956. let flattenRet = flattenTree(ret);
  957. if (flattenRet) {
  958. mergeDataset(data.dataset, flattenRet);
  959. (meta2d.store.data as any).dataset = data.dataset;
  960. }
  961. };
  962. //展开树
  963. const flattenTree = (root) => {
  964. if (!root) return []; // 空树返回空数组
  965. const result = []; // 存储展开后的数组
  966. // 递归遍历树
  967. function dfs(node) {
  968. result.push({
  969. id: node.id,
  970. label: node.label || node.name,
  971. device: node.device || node.id,
  972. type: node.type,
  973. mock: node.mock,
  974. // children: node.children,
  975. }); // 将当前节点值添加到结果数组
  976. // 遍历当前节点的子节点
  977. if (node.children) {
  978. for (let child of node.children) {
  979. dfs(child); // 递归调用DFS
  980. }
  981. }
  982. }
  983. root.forEach((item) => {
  984. dfs(item);
  985. });
  986. // dfs(root); // 从根节点开始遍历
  987. return result;
  988. };
  989. const onSelDataset = async (datasetId = false) => {
  990. if (datasetId) {
  991. const dataset = data.datasetList.find((item: any) => {
  992. return item.id === datasetId;
  993. });
  994. if (!dataset) {
  995. return;
  996. }
  997. if (dataset.url) {
  998. const ret = await axios.get(dataset.url);
  999. if (ret) {
  1000. dataset.devices = ret;
  1001. }
  1002. } else {
  1003. const ret = await axios.post(`/api/data/datasource/get`, {
  1004. id: dataset.id,
  1005. });
  1006. if (ret?.data) {
  1007. Object.assign(dataset, { ...ret.data });
  1008. }
  1009. }
  1010. mergeDataset(data.dataset, dataset.devices);
  1011. (meta2d.store.data as any).dataset = data.dataset;
  1012. // dataDialog.dataset = dataset;
  1013. // if (!init) {
  1014. // const d = JSON.parse(JSON.stringify(dataset));
  1015. // delete d.devices;
  1016. // // @ts-ignore
  1017. // meta2d.store.data.dataset = d;
  1018. // setDot(true);
  1019. // }
  1020. }
  1021. };
  1022. // 请求我的数据模型
  1023. const getDatasets = async (name?: string) => {
  1024. // console.log("进入")
  1025. if (!user.id) {
  1026. MessagePlugin.error('请先登录');
  1027. return;
  1028. }
  1029. const body: any = {
  1030. type: 'dataset',
  1031. projection: 'id,data,name,type',
  1032. };
  1033. if (name) {
  1034. body.name = name;
  1035. }
  1036. const ret: any = await axios.post(`/api/data/datasource/list`, body, {
  1037. params: {
  1038. current: 1,
  1039. pageSize: 10,
  1040. },
  1041. });
  1042. if (ret?.list) {
  1043. const list = [];
  1044. let found = false;
  1045. for (const item of ret.list) {
  1046. item.id = item.id || item._id;
  1047. list.push(transformData(item, 'toMetaNetwork'));
  1048. if (data.dataset?.id === item.id) {
  1049. found = true;
  1050. }
  1051. }
  1052. if (data.dataset?.id && !found) {
  1053. list.push(data.dataset);
  1054. }
  1055. data.datasetList = list;
  1056. }
  1057. };
  1058. const onDelDataset = async (item: any, i: number) => {
  1059. const ret: any = await axios.post(`/api/data/datasource/delete`, {
  1060. id: item.id,
  1061. });
  1062. if (
  1063. (meta2d.store.data as any).dataset &&
  1064. (meta2d.store.data as any).dataset.id === item.id
  1065. ) {
  1066. //@ts-ignore
  1067. meta2d.store.data.dataset = {};
  1068. data.dataset = {};
  1069. data.datasetId = undefined;
  1070. }
  1071. if (ret) {
  1072. data.datasetList.splice(i, 1);
  1073. }
  1074. };
  1075. const search = ref('');
  1076. const onSearch = () => {
  1077. // if (!search.value) {
  1078. // return;
  1079. // }
  1080. data.dataset.devices.forEach((item) => {
  1081. if (
  1082. item.label.indexOf(search.value) !== -1 ||
  1083. item.id.indexOf(search.value) !== -1
  1084. ) {
  1085. item.show = true;
  1086. } else {
  1087. item.show = false;
  1088. }
  1089. });
  1090. };
  1091. const onInputNetwork = (e) => {
  1092. debounce(getNetworks, 300, e);
  1093. };
  1094. const onSelectNetWork = (value) => {
  1095. if (!value) {
  1096. return;
  1097. }
  1098. const network: any = data.networks.find((elem: any) => value === elem.id);
  1099. if (!network) {
  1100. const item = data.networkList.find((elem: any) => value === elem.id);
  1101. data.networks.push(item);
  1102. meta2d.store.data.networks = toRaw(data.networks);
  1103. meta2d.connectNetwork();
  1104. setDot(true);
  1105. }
  1106. // data.input = null;
  1107. data.popupVisible = false;
  1108. };
  1109. // 请求我的实时数据
  1110. const getNetworks = async (e?) => {
  1111. const ret: any = await axios.post(
  1112. `/api/data/datasource/list`,
  1113. {
  1114. // q: {
  1115. name: e,
  1116. // },
  1117. type: 'subscribe',
  1118. projection: 'id,data,name,type',
  1119. },
  1120. {
  1121. params: {
  1122. current: 1,
  1123. pageSize: 10,
  1124. },
  1125. }
  1126. );
  1127. if (ret?.list) {
  1128. const list = [];
  1129. for (const item of ret.list) {
  1130. item.id = item.id || item._id;
  1131. list.push(transformData(item, 'toMetaNetwork'));
  1132. }
  1133. data.networkList = list;
  1134. }
  1135. };
  1136. const onDelNetWork = async (item: any, i: number) => {
  1137. const ret: any = await axios.post(`/api/data/datasource/delete`, {
  1138. id: item.id || item._id,
  1139. });
  1140. if (ret) {
  1141. data.networkList.splice(i, 1);
  1142. }
  1143. };
  1144. const deleteNetwork = (index: number) => {
  1145. data.networks.splice(index, 1);
  1146. meta2d.store.data.networks = toRaw(data.networks);
  1147. meta2d.connectNetwork();
  1148. setDot(true);
  1149. };
  1150. const networkDialog = reactive<any>({
  1151. save: true,
  1152. });
  1153. const editNetwork = (network: any, index: number) => {
  1154. networkDialog.network = JSON.parse(JSON.stringify(network));
  1155. networkDialog.editNetwork = 2;
  1156. networkDialog.editNetworkIndex = index;
  1157. networkDialog.header = '编辑数据获取';
  1158. networkDialog.show = true;
  1159. };
  1160. const addNetwork = () => {
  1161. networkDialog.network = {
  1162. name: '',
  1163. type: 'subscribe',
  1164. protocol: 'mqtt',
  1165. url: '',
  1166. options: {
  1167. clientId: '',
  1168. username: '',
  1169. password: '',
  1170. customClientId: false,
  1171. },
  1172. };
  1173. networkDialog.editNetwork = 1;
  1174. networkDialog.header = '添加数据获取';
  1175. networkDialog.show = true;
  1176. };
  1177. const onOkNetwork = async () => {
  1178. const _data = transformData(networkDialog.network, 'toNetwork');
  1179. if (networkDialog.editNetwork === 1) {
  1180. if (!networkDialog.network.url) {
  1181. MessagePlugin.error('URL地址不能为空!');
  1182. return;
  1183. }
  1184. if (!networkDialog.network.name) {
  1185. MessagePlugin.error('名称不能为空!');
  1186. return;
  1187. }
  1188. if (networkDialog.save) {
  1189. const ret: any = await axios.post(`/api/data/datasource/add`, _data);
  1190. if (!ret) {
  1191. return;
  1192. }
  1193. ret.id = ret.id || ret._id;
  1194. networkDialog.network.id = ret.id;
  1195. }
  1196. data.networks.push(networkDialog.network);
  1197. data.networkList.push(networkDialog.network);
  1198. } else if (networkDialog.editNetwork === 2) {
  1199. if (networkDialog.save) {
  1200. const ret: any = await axios.post(`/api/data/datasource/update`, _data);
  1201. if (!ret) {
  1202. return;
  1203. }
  1204. }
  1205. //替换
  1206. let index = networkDialog.editNetworkIndex;
  1207. if (index !== undefined) {
  1208. data.networks.splice(index, 1, networkDialog.network);
  1209. }
  1210. }
  1211. networkDialog.show = false;
  1212. networkDialog.editNetwork = 0;
  1213. meta2d.store.data.networks = toRaw(data.networks);
  1214. meta2d.connectNetwork();
  1215. setDot(true);
  1216. };
  1217. const dataTransformationDialog = reactive<any>({
  1218. show: false,
  1219. data: '',
  1220. });
  1221. const showDataTransformation = () => {
  1222. dataTransformationDialog.data = meta2d.store.data.socketCbJs;
  1223. dataTransformationDialog.show = true;
  1224. };
  1225. const onOkDataTransformation = () => {
  1226. meta2d.store.data.socketCbJs = dataTransformationDialog.data;
  1227. data.randomkey = s8();
  1228. data.socketCbJs = dataTransformationDialog.data;
  1229. meta2d.listenSocket();
  1230. dataTransformationDialog.show = false;
  1231. };
  1232. let timer: any = 0;
  1233. const onSocketCbJsChange = (e) => {
  1234. clearTimeout(timer);
  1235. timer = setTimeout(() => {
  1236. meta2d.store.data.socketCbJs = data.socketCbJs;
  1237. meta2d.listenSocket();
  1238. }, 3000);
  1239. };
  1240. const onChangeMock = () => {
  1241. // @ts-ignore
  1242. meta2d.store.data.enableMock = data.enableMock;
  1243. if (data.enableMock) {
  1244. meta2d.startDataMock();
  1245. } else {
  1246. meta2d.stopDataMock();
  1247. }
  1248. };
  1249. const onAddShape = (e, _data) => {
  1250. e.stopPropagation();
  1251. let data: any;
  1252. if (Array.isArray(_data)) {
  1253. const dragElem = document.getElementById('dragElem');
  1254. let width = dragElem.offsetWidth;
  1255. e.dataTransfer.setDragImage(dragElem, width / 2, 10);
  1256. const checked = _data.filter((item) => item.checked);
  1257. if(!checked.length){
  1258. MessagePlugin.error('请先选择数据');
  1259. return;
  1260. }
  1261. data = [];
  1262. checked.forEach((item) => {
  1263. data.push(
  1264. ...[
  1265. {
  1266. text: item.label + ':',
  1267. width: 150,
  1268. height: 20,
  1269. name: 'text',
  1270. dataset: true,
  1271. textAlign: 'right',
  1272. },
  1273. {
  1274. text: item.mock,
  1275. width: 100,
  1276. height: 20,
  1277. name: 'text',
  1278. dataset: true,
  1279. textAlign: 'left',
  1280. realTimes: [
  1281. {
  1282. label: '文字',
  1283. key: 'text',
  1284. type: 'string',
  1285. bind: deepClone(item),
  1286. },
  1287. ],
  1288. },
  1289. ]
  1290. );
  1291. });
  1292. } else {
  1293. data = [
  1294. {
  1295. text: _data.label + ':',
  1296. width: 150,
  1297. height: 20,
  1298. name: 'text',
  1299. dataset: true,
  1300. textAlign: 'right',
  1301. },
  1302. {
  1303. text: _data.mock,
  1304. width: 100,
  1305. height: 20,
  1306. name: 'text',
  1307. dataset: true,
  1308. textAlign: 'left',
  1309. realTimes: [
  1310. {
  1311. label: '文字',
  1312. key: 'text',
  1313. type: 'string',
  1314. bind: deepClone(_data),
  1315. },
  1316. ],
  1317. },
  1318. ];
  1319. }
  1320. meta2d.canvas.addCaches = deepClone(data);
  1321. };
  1322. const onAddShapeEnd = () => {
  1323. setTimeout(()=>{
  1324. meta2d.initBinds();
  1325. },1000);
  1326. }
  1327. const onAddmock = (item: any) => {
  1328. if (data.dataMocks.find((elem) => elem.id === item.id)) {
  1329. return;
  1330. }
  1331. item.name = item.label;
  1332. data.dataMocks.push(deepClone(item));
  1333. (meta2d.store.data as any).dataMocks = data.dataMocks;
  1334. meta2d.startDataMock();
  1335. };
  1336. const onAddAllMock = () => {
  1337. if (!data.dataset.devices) {
  1338. return;
  1339. }
  1340. data.dataset.devices.forEach((item) => {
  1341. if (data.dataMocks.find((elem) => elem.id === item.id)) {
  1342. return;
  1343. }
  1344. item.name = item.label;
  1345. data.dataMocks.push(deepClone(item));
  1346. });
  1347. (meta2d.store.data as any).dataMocks = data.dataMocks;
  1348. meta2d.startDataMock();
  1349. };
  1350. const deleteMock = (index: number) => {
  1351. data.dataMocks.splice(index, 1);
  1352. (meta2d.store.data as any).dataMocks = data.dataMocks;
  1353. setDot(true);
  1354. };
  1355. </script>
  1356. <style lang="postcss" scoped>
  1357. /* :deep(.data-input-box){
  1358. position: absolute;
  1359. left: 193px;
  1360. width: 501px;
  1361. top: 80px;
  1362. } */
  1363. .content {
  1364. background: var(--color-background-active);
  1365. padding: 16px;
  1366. .input-search {
  1367. flex-shrink: 0;
  1368. color: #878f9c;
  1369. padding: 0px;
  1370. margin-top: 10px;
  1371. .btn {
  1372. background: #fff0;
  1373. left: 12px;
  1374. img {
  1375. background-color: #fff0;
  1376. margin-top: 9px;
  1377. }
  1378. }
  1379. :deep(.t-select) {
  1380. .t-input {
  1381. padding-left: 8px !important;
  1382. }
  1383. }
  1384. }
  1385. .select-search {
  1386. .t-input {
  1387. padding-left: 8px !important;
  1388. .t-icon {
  1389. font-size: 12px;
  1390. }
  1391. }
  1392. }
  1393. .title {
  1394. height: 32px;
  1395. line-height: 32px;
  1396. }
  1397. .title-span {
  1398. display: inline-block;
  1399. margin-left: 4px;
  1400. max-width: 108px;
  1401. overflow: hidden;
  1402. white-space: nowrap;
  1403. text-overflow: ellipsis;
  1404. }
  1405. .data-list {
  1406. /* padding: 12px 6px; */
  1407. /* background: var(--color-background-active); */
  1408. max-height: calc(100vh - 200px);
  1409. margin-bottom: 24px;
  1410. overflow-y: auto;
  1411. .data-title {
  1412. /* padding: 4px; */
  1413. }
  1414. .data-body {
  1415. margin-bottom: 8px;
  1416. }
  1417. .data-title-hover {
  1418. &:hover {
  1419. background: var(--color-background-input);
  1420. border-radius: 4px;
  1421. }
  1422. }
  1423. width: 234px;
  1424. & > div {
  1425. width: 218px;
  1426. }
  1427. }
  1428. .data-mock-list {
  1429. height: auto;
  1430. max-height: calc(100vh - 220px);
  1431. }
  1432. .icon-box {
  1433. width: 24px;
  1434. height: 24px;
  1435. margin: 4px;
  1436. text-align: center;
  1437. line-height: 24px;
  1438. border-radius: 4px;
  1439. &:hover {
  1440. background: var(--td-brand-color-light);
  1441. }
  1442. .t-icon {
  1443. width: 14px;
  1444. height: 14px;
  1445. }
  1446. }
  1447. .icon-item-box {
  1448. height: 30px;
  1449. line-height: 30px;
  1450. margin: 1px;
  1451. }
  1452. .nodata {
  1453. padding-top: 80px;
  1454. img {
  1455. width: 80px;
  1456. }
  1457. .gray {
  1458. margin-top: 8px;
  1459. }
  1460. .t-button {
  1461. //width: 64px;
  1462. height: 24px;
  1463. padding-left: 8px;
  1464. padding-right: 8px;
  1465. }
  1466. }
  1467. .form-line {
  1468. margin-left: 12px;
  1469. width: 11px;
  1470. /* height: 120px; */
  1471. /* border-bottom: 1px solid var(--color-background-input); */
  1472. /* border-left: 1px solid var(--color-background-input); */
  1473. }
  1474. .form-data-item {
  1475. margin-left: 4px;
  1476. margin-top: 4px;
  1477. /* margin-right: 12px; */
  1478. label {
  1479. /* color: #ffffff80; */
  1480. height: 24px;
  1481. line-height: 24px;
  1482. width: 60px;
  1483. }
  1484. & > div {
  1485. width: 118px;
  1486. height: 24px;
  1487. line-height: 24px;
  1488. /* background: var(--color-background-input); */
  1489. border-radius: 4px;
  1490. /* color: #e3e8f459; */
  1491. padding-left: 8px;
  1492. padding-right: 8px;
  1493. overflow: hidden;
  1494. white-space: nowrap;
  1495. text-overflow: ellipsis;
  1496. }
  1497. }
  1498. .form-mock-item {
  1499. & > div {
  1500. padding: 0px;
  1501. :deep(.t-input) {
  1502. height: 24px !important;
  1503. border: 0px;
  1504. }
  1505. }
  1506. }
  1507. .form-swicth-item {
  1508. margin-left: 4px;
  1509. label {
  1510. /* color: #ffffff80; */
  1511. height: 24px;
  1512. width: 56px;
  1513. }
  1514. }
  1515. .data-code {
  1516. width: 218px;
  1517. .code-editor {
  1518. height: 126px;
  1519. }
  1520. }
  1521. .data-code-full {
  1522. position: absolute;
  1523. /* right: calc(50% - 250px); */
  1524. width: 100%;
  1525. height: 100vh;
  1526. background: var(--td-mask-active);
  1527. right: 0px;
  1528. top: 0px;
  1529. .code-editor {
  1530. width: 800px;
  1531. margin: 0 auto;
  1532. height: 80%;
  1533. margin-top: 16vh;
  1534. }
  1535. .data-full-icon {
  1536. .t-icon {
  1537. margin-right: calc(50% - 384px);
  1538. }
  1539. }
  1540. }
  1541. .data-full-icon {
  1542. margin-top: -20px;
  1543. /* z-index: 10000; */
  1544. position: relative;
  1545. display: flex;
  1546. justify-content: flex-end;
  1547. width: 100%;
  1548. .t-icon {
  1549. margin-right: 16px;
  1550. cursor: pointer;
  1551. height: 16px;
  1552. width: 16px;
  1553. }
  1554. }
  1555. ul {
  1556. list-style-type: none;
  1557. }
  1558. }
  1559. </style>
  1560. <style lang="postcss">
  1561. .menu-item-a {
  1562. &:hover {
  1563. color: var(--td-brand-color);
  1564. }
  1565. }
  1566. .network-option {
  1567. height: 100%;
  1568. padding: 0px;
  1569. }
  1570. </style>