DataSource.vue 68 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615
  1. <template>
  2. <div
  3. class="content"
  4. style="height: calc(100vh - 82px); overflow-y: auto"
  5. v-if="group === '数据'"
  6. >
  7. <div
  8. class="flex mt-16 mb-16"
  9. style="justify-content: space-between; padding-right: 8px"
  10. >
  11. <div style="line-height: 32px">数据列表</div>
  12. <t-dropdown :minColumnWidth="168">
  13. <div class="icon-box">
  14. <AddIcon />
  15. </div>
  16. <t-dropdown-menu>
  17. <t-dropdown-item @click="onShowIot"> 物联网平台 </t-dropdown-item>
  18. <t-dropdown-item @click="addSql"> sql数据源 </t-dropdown-item>
  19. <t-dropdown-item @click="addNetwork('mqtt')"> MQTT </t-dropdown-item>
  20. <t-dropdown-item @click="addNetwork('websocket')"> Websocket </t-dropdown-item>
  21. <t-dropdown-item @click="addNetwork('http')"> HTTP </t-dropdown-item>
  22. <t-dropdown-item @click="addNetwork('SSE')"> SSE </t-dropdown-item>
  23. </t-dropdown-menu>
  24. </t-dropdown>
  25. </div>
  26. <div class="flex between mt-8">
  27. <t-tooltip content="可批量导入数据图元到画布" placement="top">
  28. <t-checkbox v-model="data.checkAll" @change="onCheckAllChange"
  29. >批量导入到画布</t-checkbox
  30. >
  31. </t-tooltip>
  32. <!-- <t-tooltip content="开启全局数据模拟" placement="top">
  33. <t-checkbox v-model="data.enableMock" @change="onChangeMock"
  34. >开启</t-checkbox
  35. >
  36. </t-tooltip> -->
  37. </div>
  38. <div v-if="data.iotTree?.length">
  39. <div class="flex mt-16 between" style="height: 32px; line-height: 32px">
  40. <div class="flex">
  41. <ApplicationIcon class="tree-icon mt-8" />
  42. <div class="ml-8">物联网平台</div>
  43. </div>
  44. <!-- <div>
  45. <Edit2Icon class="mr-12 hover" style="width: 14px;height: 14px;" @click="onShowIot" />
  46. </div> -->
  47. </div>
  48. <!-- <div> -->
  49. <div
  50. :draggable="data.checkAll ? true : false"
  51. @dragstart="onAddShape($event, data.iotTree,'iot')"
  52. @dragend="onAddShapeEnd"
  53. >
  54. <t-tree
  55. :draggable="false"
  56. v-model="allChecked"
  57. class="ml-16"
  58. style="overflow-y: hidden"
  59. activeMultiple
  60. :data="data.iotTree"
  61. :expand-parent="true"
  62. :checkable="data.checkAll"
  63. :checkStrictly="false"
  64. />
  65. </div>
  66. <!-- </div> -->
  67. <div
  68. id="dragElem-iot"
  69. style="position: absolute; left: 0; z-index: -999"
  70. v-if="data.iotTree.length"
  71. >
  72. <div v-for="device in data.iotTree">
  73. <div
  74. class="flex mt-4"
  75. v-for="(prop, i) in device.children"
  76. v-show="allChecked.includes(prop.value)"
  77. >
  78. <div style="width: 100px; text-align: end">{{ prop.label }}:</div>
  79. <div class="ml-4">{{ prop.mock }}</div>
  80. </div>
  81. </div>
  82. </div>
  83. </div>
  84. <div v-if="data.sqls?.length">
  85. <div class="flex mt-16" style="height: 32px">
  86. <DataIcon class="tree-icon" />
  87. <div class="ml-8">SQL数据源</div>
  88. </div>
  89. <div
  90. :draggable="data.checkAll ? true : false"
  91. @dragstart="onAddShape($event, data.sqls,'sql')"
  92. @dragend="onAddShapeEnd"
  93. >
  94. <t-tree
  95. :draggable="false"
  96. class="ml-16"
  97. :key="sqlTreeKey"
  98. hover
  99. style="overflow-y: hidden"
  100. activeMultiple
  101. :data="data.sqls"
  102. :expand-parent="true"
  103. :checkable="data.checkAll"
  104. :checkStrictly="false"
  105. >
  106. <template #operations="{ node }">
  107. <tepmlate v-if="!node.getParent()">
  108. <Edit2Icon
  109. class="mr-12"
  110. @click="editSql(node.data, node.getIndex())"
  111. />
  112. <DeleteIcon class="mr-8" @click="deleteSql(node.getIndex())" />
  113. </tepmlate>
  114. </template>
  115. </t-tree>
  116. </div>
  117. <div
  118. id="dragElem-sql"
  119. style="position: absolute; left: 0; z-index: -999"
  120. v-if="data.sqls.length"
  121. >
  122. <div v-for="item in data.sqls">
  123. <div
  124. class="flex mt-4"
  125. v-for="(prop, i) in item.children"
  126. v-show="sqlsCheked.includes(prop.value)"
  127. >
  128. <div style="width: 100px; text-align: end">{{ prop.label }}:</div>
  129. <div class="ml-4">{{ prop.mock }}</div>
  130. </div>
  131. </div>
  132. </div>
  133. </div>
  134. <!-- <div v-if="data.networks?.length">
  135. <div class="flex mt-16 between" style="height: 32px; line-height: 32px">
  136. <div class="flex">
  137. <ControlPlatformIcon class="tree-icon mt-8" />
  138. <div class="ml-8">Mqtt</div>
  139. </div>
  140. <div>
  141. </div>
  142. </div>
  143. <div
  144. :draggable="data.checkAll ? true : false"
  145. @dragstart="onAddShape($event, data.networks,'network')"
  146. @dragend="onAddShapeEnd"
  147. >
  148. <t-tree
  149. :draggable="false"
  150. class="ml-16"
  151. v-model="networksCheked"
  152. ref="networkTree"
  153. :key="networkTreeKey"
  154. style="overflow-y: hidden"
  155. activeMultiple
  156. :data="data.networks"
  157. :expand-parent="true"
  158. :checkable="data.checkAll"
  159. :checkStrictly="false"
  160. >
  161. <template #operations="{ node }">
  162. <template v-if="node.getParent()">
  163. <Edit2Icon
  164. class="mr-12"
  165. @click="showAddData(node.getParent().data, node)"
  166. />
  167. <DeleteIcon
  168. class="mr-8"
  169. @click="
  170. deleteData(node.getParent().data, node.getIndex());
  171. node.remove();
  172. "
  173. />
  174. </template>
  175. <template v-else>
  176. <t-dropdown :minColumnWidth="168" :hide-after-item-click="false">
  177. <AddIcon class="mr-12" />
  178. <t-dropdown-menu>
  179. <t-dropdown-item
  180. :value="2"
  181. :divider="true"
  182. @click="showAddData(node.data)"
  183. >
  184. 新建属性
  185. </t-dropdown-item>
  186. <t-dropdown-item :value="2" @click="importDataset(node.data)">
  187. 从Excel导入
  188. </t-dropdown-item>
  189. <t-dropdown-item :value="4">
  190. <a
  191. :href="
  192. isDownload
  193. ? '/v/data.xlsx'
  194. : cdn
  195. ? cdn + '/v/data.xlsx?r=' + Math.random()
  196. : '/data.xlsx'
  197. "
  198. style="color: var(--td-text-color-primary)"
  199. @click.stop
  200. >
  201. 下载Excel示例
  202. </a>
  203. </t-dropdown-item>
  204. </t-dropdown-menu>
  205. </t-dropdown>
  206. <Edit2Icon
  207. class="mr-12"
  208. @click="editNetwork(node.data, node.getIndex())"
  209. />
  210. <DeleteIcon
  211. class="mr-8"
  212. @click="
  213. deleteNetwork(node.getIndex());
  214. node.remove();
  215. "
  216. />
  217. </template>
  218. </template>
  219. </t-tree>
  220. </div>
  221. <div
  222. id="dragElem-network"
  223. style="position: absolute; left: 0; z-index: -999"
  224. v-if="data.networks.length"
  225. >
  226. <div v-for="item in data.networks">
  227. <div
  228. class="flex mt-4"
  229. v-for="(prop, i) in item.children"
  230. v-show="networksCheked.includes(prop.value)"
  231. >
  232. <div style="width: 100px; text-align: end">{{ prop.label }}:</div>
  233. <div class="ml-4">{{ prop.mock }}</div>
  234. </div>
  235. </div>
  236. </div>
  237. </div> -->
  238. <div v-if="data.mqtt_networks?.length">
  239. <div class="flex mt-16 between" style="height: 32px; line-height: 32px">
  240. <div class="flex">
  241. <ControlPlatformIcon class="tree-icon mt-8" />
  242. <div class="ml-8">MQTT</div>
  243. </div>
  244. <div>
  245. </div>
  246. </div>
  247. <div
  248. :draggable="data.checkAll ? true : false"
  249. @dragstart="onAddShape($event, data.networks,'network')"
  250. @dragend="onAddShapeEnd"
  251. >
  252. <t-tree
  253. :draggable="false"
  254. class="ml-16"
  255. v-model="networksCheked"
  256. ref="mqttTree"
  257. :key="mqttTreeKey"
  258. style="overflow-y: hidden"
  259. activeMultiple
  260. :data="data.mqtt_networks"
  261. :expand-parent="true"
  262. :checkable="data.checkAll"
  263. :checkStrictly="false"
  264. >
  265. <template #operations="{ node }">
  266. <template v-if="node.getParent()">
  267. <Edit2Icon
  268. class="mr-12"
  269. @click="showAddData(node.getParent().data, node)"
  270. />
  271. <DeleteIcon
  272. class="mr-8"
  273. @click="
  274. deleteData(node.getParent().data, node.getIndex());
  275. node.remove();
  276. "
  277. />
  278. </template>
  279. <template v-else>
  280. <t-dropdown :minColumnWidth="168" :hide-after-item-click="false">
  281. <AddIcon class="mr-12" />
  282. <t-dropdown-menu>
  283. <t-dropdown-item
  284. :value="2"
  285. :divider="true"
  286. @click="showAddData(node.data)"
  287. >
  288. 新建属性
  289. </t-dropdown-item>
  290. <t-dropdown-item :value="2" @click="importDataset(node.data)">
  291. 从Excel导入
  292. </t-dropdown-item>
  293. <t-dropdown-item :value="4">
  294. <a
  295. :href="
  296. isDownload
  297. ? '/v/data.xlsx'
  298. : cdn
  299. ? cdn + '/v/data.xlsx?r=' + Math.random()
  300. : '/data.xlsx'
  301. "
  302. style="color: var(--td-text-color-primary)"
  303. @click.stop
  304. >
  305. 下载Excel示例
  306. </a>
  307. </t-dropdown-item>
  308. </t-dropdown-menu>
  309. </t-dropdown>
  310. <Edit2Icon
  311. class="mr-12"
  312. @click="editNetwork(node.data, node.data.tem_index)"
  313. />
  314. <DeleteIcon
  315. class="mr-8"
  316. @click="
  317. deleteNetwork(node.data.tem_index);
  318. node.remove();
  319. "
  320. />
  321. </template>
  322. </template>
  323. </t-tree>
  324. </div>
  325. <div
  326. id="dragElem-network"
  327. style="position: absolute; left: 0; z-index: -999"
  328. v-if="data.networks.length"
  329. >
  330. <div v-for="item in data.networks">
  331. <div
  332. class="flex mt-4"
  333. v-for="(prop, i) in item.children"
  334. v-show="networksCheked.includes(prop.value)"
  335. >
  336. <div style="width: 100px; text-align: end">{{ prop.label }}:</div>
  337. <div class="ml-4">{{ prop.mock }}</div>
  338. </div>
  339. </div>
  340. </div>
  341. </div>
  342. <div v-if="data.ws_networks?.length">
  343. <div class="flex mt-16 between" style="height: 32px; line-height: 32px">
  344. <div class="flex">
  345. <ControlPlatformIcon class="tree-icon mt-8" />
  346. <div class="ml-8">Websocket</div>
  347. </div>
  348. <div>
  349. </div>
  350. </div>
  351. <div
  352. :draggable="data.checkAll ? true : false"
  353. @dragstart="onAddShape($event, data.networks,'network')"
  354. @dragend="onAddShapeEnd"
  355. >
  356. <t-tree
  357. :draggable="false"
  358. class="ml-16"
  359. v-model="networksCheked"
  360. ref="wsTree"
  361. :key="wsTreeKey"
  362. style="overflow-y: hidden"
  363. activeMultiple
  364. :data="data.ws_networks"
  365. :expand-parent="true"
  366. :checkable="data.checkAll"
  367. :checkStrictly="false"
  368. >
  369. <template #operations="{ node }">
  370. <template v-if="node.getParent()">
  371. <Edit2Icon
  372. class="mr-12"
  373. @click="showAddData(node.getParent().data, node)"
  374. />
  375. <DeleteIcon
  376. class="mr-8"
  377. @click="
  378. deleteData(node.getParent().data, node.getIndex());
  379. node.remove();
  380. "
  381. />
  382. </template>
  383. <template v-else>
  384. <t-dropdown :minColumnWidth="168" :hide-after-item-click="false">
  385. <AddIcon class="mr-12" />
  386. <t-dropdown-menu>
  387. <t-dropdown-item
  388. :value="2"
  389. :divider="true"
  390. @click="showAddData(node.data)"
  391. >
  392. 新建属性
  393. </t-dropdown-item>
  394. <t-dropdown-item :value="2" @click="importDataset(node.data)">
  395. 从Excel导入
  396. </t-dropdown-item>
  397. <t-dropdown-item :value="4">
  398. <a
  399. :href="
  400. isDownload
  401. ? '/v/data.xlsx'
  402. : cdn
  403. ? cdn + '/v/data.xlsx?r=' + Math.random()
  404. : '/data.xlsx'
  405. "
  406. style="color: var(--td-text-color-primary)"
  407. @click.stop
  408. >
  409. 下载Excel示例
  410. </a>
  411. </t-dropdown-item>
  412. </t-dropdown-menu>
  413. </t-dropdown>
  414. <Edit2Icon
  415. class="mr-12"
  416. @click="editNetwork(node.data, node.data.tem_index)"
  417. />
  418. <DeleteIcon
  419. class="mr-8"
  420. @click="
  421. deleteNetwork(node.data.tem_index);
  422. node.remove();
  423. "
  424. />
  425. </template>
  426. </template>
  427. </t-tree>
  428. </div>
  429. <div
  430. id="dragElem-network"
  431. style="position: absolute; left: 0; z-index: -999"
  432. v-if="data.networks.length"
  433. >
  434. <div v-for="item in data.networks">
  435. <div
  436. class="flex mt-4"
  437. v-for="(prop, i) in item.children"
  438. v-show="networksCheked.includes(prop.value)"
  439. >
  440. <div style="width: 100px; text-align: end">{{ prop.label }}:</div>
  441. <div class="ml-4">{{ prop.mock }}</div>
  442. </div>
  443. </div>
  444. </div>
  445. </div>
  446. <div v-if="data.http_networks?.length">
  447. <div class="flex mt-16 between" style="height: 32px; line-height: 32px">
  448. <div class="flex">
  449. <ControlPlatformIcon class="tree-icon mt-8" />
  450. <div class="ml-8">HTTP</div>
  451. </div>
  452. <div>
  453. </div>
  454. </div>
  455. <div
  456. :draggable="data.checkAll ? true : false"
  457. @dragstart="onAddShape($event, data.networks,'network')"
  458. @dragend="onAddShapeEnd"
  459. >
  460. <t-tree
  461. :draggable="false"
  462. class="ml-16"
  463. v-model="networksCheked"
  464. ref="httpTree"
  465. :key="httpTreeKey"
  466. style="overflow-y: hidden"
  467. activeMultiple
  468. :data="data.http_networks"
  469. :expand-parent="true"
  470. :checkable="data.checkAll"
  471. :checkStrictly="false"
  472. >
  473. <template #operations="{ node }">
  474. <template v-if="node.getParent()">
  475. <Edit2Icon
  476. class="mr-12"
  477. @click="showAddData(node.getParent().data, node)"
  478. />
  479. <DeleteIcon
  480. class="mr-8"
  481. @click="
  482. deleteData(node.getParent().data, node.getIndex());
  483. node.remove();
  484. "
  485. />
  486. </template>
  487. <template v-else>
  488. <t-dropdown :minColumnWidth="168" :hide-after-item-click="false">
  489. <AddIcon class="mr-12" />
  490. <t-dropdown-menu>
  491. <t-dropdown-item
  492. :value="2"
  493. :divider="true"
  494. @click="showAddData(node.data)"
  495. >
  496. 新建属性
  497. </t-dropdown-item>
  498. <t-dropdown-item :value="2" @click="importDataset(node.data)">
  499. 从Excel导入
  500. </t-dropdown-item>
  501. <t-dropdown-item :value="4">
  502. <a
  503. :href="
  504. isDownload
  505. ? '/v/data.xlsx'
  506. : cdn
  507. ? cdn + '/v/data.xlsx?r=' + Math.random()
  508. : '/data.xlsx'
  509. "
  510. style="color: var(--td-text-color-primary)"
  511. @click.stop
  512. >
  513. 下载Excel示例
  514. </a>
  515. </t-dropdown-item>
  516. </t-dropdown-menu>
  517. </t-dropdown>
  518. <Edit2Icon
  519. class="mr-12"
  520. @click="editNetwork(node.data, node.data.tem_index)"
  521. />
  522. <DeleteIcon
  523. class="mr-8"
  524. @click="
  525. deleteNetwork(node.data.tem_index);
  526. node.remove();
  527. "
  528. />
  529. </template>
  530. </template>
  531. </t-tree>
  532. </div>
  533. <div
  534. id="dragElem-network"
  535. style="position: absolute; left: 0; z-index: -999"
  536. v-if="data.networks.length"
  537. >
  538. <div v-for="item in data.networks">
  539. <div
  540. class="flex mt-4"
  541. v-for="(prop, i) in item.children"
  542. v-show="networksCheked.includes(prop.value)"
  543. >
  544. <div style="width: 100px; text-align: end">{{ prop.label }}:</div>
  545. <div class="ml-4">{{ prop.mock }}</div>
  546. </div>
  547. </div>
  548. </div>
  549. </div>
  550. <div v-if="data.SSE_networks?.length">
  551. <div class="flex mt-16 between" style="height: 32px; line-height: 32px">
  552. <div class="flex">
  553. <ControlPlatformIcon class="tree-icon mt-8" />
  554. <div class="ml-8">SSE</div>
  555. </div>
  556. <div>
  557. </div>
  558. </div>
  559. <div
  560. :draggable="data.checkAll ? true : false"
  561. @dragstart="onAddShape($event, data.networks,'network')"
  562. @dragend="onAddShapeEnd"
  563. >
  564. <t-tree
  565. :draggable="false"
  566. class="ml-16"
  567. v-model="networksCheked"
  568. ref="SSETree"
  569. :key="SSETreeKey"
  570. style="overflow-y: hidden"
  571. activeMultiple
  572. :data="data.SSE_networks"
  573. :expand-parent="true"
  574. :checkable="data.checkAll"
  575. :checkStrictly="false"
  576. >
  577. <template #operations="{ node }">
  578. <template v-if="node.getParent()">
  579. <Edit2Icon
  580. class="mr-12"
  581. @click="showAddData(node.getParent().data, node)"
  582. />
  583. <DeleteIcon
  584. class="mr-8"
  585. @click="
  586. deleteData(node.getParent().data, node.getIndex());
  587. node.remove();
  588. "
  589. />
  590. </template>
  591. <template v-else>
  592. <t-dropdown :minColumnWidth="168" :hide-after-item-click="false">
  593. <AddIcon class="mr-12" />
  594. <t-dropdown-menu>
  595. <t-dropdown-item
  596. :value="2"
  597. :divider="true"
  598. @click="showAddData(node.data)"
  599. >
  600. 新建属性
  601. </t-dropdown-item>
  602. <t-dropdown-item :value="2" @click="importDataset(node.data)">
  603. 从Excel导入
  604. </t-dropdown-item>
  605. <t-dropdown-item :value="4">
  606. <a
  607. :href="
  608. isDownload
  609. ? '/v/data.xlsx'
  610. : cdn
  611. ? cdn + '/v/data.xlsx?r=' + Math.random()
  612. : '/data.xlsx'
  613. "
  614. style="color: var(--td-text-color-primary)"
  615. @click.stop
  616. >
  617. 下载Excel示例
  618. </a>
  619. </t-dropdown-item>
  620. </t-dropdown-menu>
  621. </t-dropdown>
  622. <Edit2Icon
  623. class="mr-12"
  624. @click="editNetwork(node.data, node.data.tem_index)"
  625. />
  626. <DeleteIcon
  627. class="mr-8"
  628. @click="
  629. deleteNetwork(node.data.tem_index);
  630. node.remove();
  631. "
  632. />
  633. </template>
  634. </template>
  635. </t-tree>
  636. </div>
  637. <div
  638. id="dragElem-network"
  639. style="position: absolute; left: 0; z-index: -999"
  640. v-if="data.networks.length"
  641. >
  642. <div v-for="item in data.networks">
  643. <div
  644. class="flex mt-4"
  645. v-for="(prop, i) in item.children"
  646. v-show="networksCheked.includes(prop.value)"
  647. >
  648. <div style="width: 100px; text-align: end">{{ prop.label }}:</div>
  649. <div class="ml-4">{{ prop.mock }}</div>
  650. </div>
  651. </div>
  652. </div>
  653. </div>
  654. <div
  655. class="flex column middle nodata"
  656. v-if="!data.iotTree.length && !data.sqls.length && !data.networks.length"
  657. >
  658. <img src="/img/no-data.png" />
  659. <div class="gray center">暂无数据</div>
  660. <!-- <div class="mt-20">
  661. <t-button theme="primary" @click="addNetwork()">
  662. 添加数据源
  663. </t-button>
  664. </div> -->
  665. </div>
  666. </div>
  667. <div class="content" v-if="group === '解析'">
  668. <div class="flex between">
  669. <div class="title">数据{{ group }}</div>
  670. </div>
  671. <div class="mt-8">
  672. <CodeEditor
  673. :key="data.randomkey"
  674. v-model="data.socketCbJs"
  675. style="min-height: 300px"
  676. @change="onSocketCbJsChange"
  677. />
  678. <div class="data-full-icon hover">
  679. <Fullscreen2Icon @click="showDataTransformation" />
  680. </div>
  681. </div>
  682. <div class="mt-16">
  683. 参考文档:
  684. <a
  685. target="_blank"
  686. 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"
  687. >
  688. 解析自定义格式数据</a
  689. >
  690. </div>
  691. </div>
  692. <t-dialog
  693. v-if="addDataDialog.show"
  694. :visible="true"
  695. class="data-dialog"
  696. :header="addDataDialog.header"
  697. @close="addDataDialog.show = false"
  698. @confirm="onOkAddData"
  699. >
  700. <!-- <div class="form-item mt-16">
  701. <label>设备</label>
  702. <t-input v-model="addDataDialog.data.device" placeholder="设备名称" />
  703. </div> -->
  704. <div class="form-item mt-16">
  705. <label>显示名称</label>
  706. <t-input
  707. @change="changeDataLabel($event)"
  708. :value="addDataDialog.data.label"
  709. placeholder="属性简短描述"
  710. />
  711. </div>
  712. <div class="form-item mt-16">
  713. <label>属性名</label>
  714. <t-input
  715. @change="changeDataID($event)"
  716. :value="addDataDialog.data.id"
  717. placeholder="属性名"
  718. />
  719. </div>
  720. <!-- <div class="form-item mt-16">
  721. <label>类型</label>
  722. <t-select
  723. class="w-full"
  724. :options="typeOptions"
  725. v-model="addDataDialog.data.type"
  726. placeholder="字符串"
  727. @change="addDataDialog.data.value = null"
  728. />
  729. </div>
  730. <div class="form-item mt-16">
  731. <label
  732. >值范围
  733. <t-tooltip content="可用做数据模拟" placement="top">
  734. <HelpCircleIcon style="font-size: 12px" />
  735. </t-tooltip>
  736. </label>
  737. <div class="w-full">
  738. <t-input v-model="addDataDialog.data.mock" placeholder="值范围" />
  739. <h6 class="desc mt-8" style="font-size: 12px">值范围说明</h6>
  740. <ul class="desc ml-16" style="font-size: 12px">
  741. <li>
  742. <label class="inline" style="width: 80px">固定值:</label>
  743. 直接填写,例如:10
  744. </li>
  745. <li>
  746. <label class="inline" style="width: 80px">随机值:</label>
  747. 值1,值2,...。例如:1,2,3,4,5
  748. </li>
  749. <li>
  750. <label class="inline" style="width: 80px">范围数字:</label>
  751. 最小值-最大值。例如:0-1.0 或 0-100
  752. </li>
  753. <li>
  754. <label class="inline" style="width: 80px">随机字符串:</label>
  755. [长度]。例如:[8]
  756. </li>
  757. </ul>
  758. </div>
  759. </div> -->
  760. </t-dialog>
  761. <t-dialog
  762. v-if="networkDialog.show"
  763. :visible="true"
  764. width="800px"
  765. class="data-dialog"
  766. :header="networkDialog.header"
  767. @close="networkDialog.show = false"
  768. @confirm="onOkNetwork"
  769. >
  770. <template #footer>
  771. <div class="flex mr-8" style="justify-content: end">
  772. <!-- <t-checkbox v-model="networkDialog.save" class="mr-12">
  773. 同时保存到我的数据源
  774. </t-checkbox> -->
  775. <t-button @click="onOkNetwork">确定</t-button>
  776. </div>
  777. </template>
  778. <div style="max-height: 450px; padding: 8px; overflow-y: auto">
  779. <Net v-model="networkDialog.network" />
  780. </div>
  781. </t-dialog>
  782. <t-dialog
  783. v-if="dataTransformationDialog.show"
  784. :visible="true"
  785. header="数据监听"
  786. @confirm="onOkDataTransformation"
  787. @close="dataTransformationDialog.show = false"
  788. :width="800"
  789. >
  790. <CodeEditor v-model="dataTransformationDialog.data" style="height: 300px" />
  791. <div class="mt-8">
  792. 参考文档:
  793. <a
  794. target="_blank"
  795. 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"
  796. >
  797. 解析自定义格式数据</a
  798. >
  799. </div>
  800. </t-dialog>
  801. <t-dialog
  802. v-if="sqlDialog.show"
  803. :visible="true"
  804. width="800px"
  805. class="data-dialog"
  806. :header="sqlDialog.header"
  807. @close="sqlDialog.show = false"
  808. @confirm="onOkSql"
  809. >
  810. <div style="max-height: 450px; padding: 8px">
  811. <div class="form-item mt-8">
  812. <label>sql数据源</label>
  813. <t-select v-model="sqlDialog.sql.dbid" placeholder="请选择数据源">
  814. <t-option
  815. v-for="sql in sqlList"
  816. @click="sqlChange(sql)"
  817. :key="sql.id"
  818. :value="sql.id"
  819. :label="sql.name + '(' + sql.dbType + ')'"
  820. />
  821. </t-select>
  822. </div>
  823. <div class="form-item mt-8">
  824. <label>sql轮询间隔</label>
  825. <t-input-number
  826. theme="column"
  827. v-model="sqlDialog.sql.interval"
  828. placeholder="不填,仅初始执行一次"
  829. />
  830. </div>
  831. <div class="form-item mt-8">
  832. <label>查询方式</label>
  833. <t-select v-model="sqlDialog.sql.method">
  834. <t-option key="get" value="get" label="单条" />
  835. <t-option key="list" value="list" label="列表" />
  836. </t-select>
  837. </div>
  838. <div class="form-item mt-8">
  839. <label>sql语句</label>
  840. <CodeEditor
  841. :json="false"
  842. :language="'sql'"
  843. v-model="sqlDialog.sql.sql"
  844. class="mt-4"
  845. style="height: 100px"
  846. />
  847. </div>
  848. <div v-if="sqlDialog.sql.method === 'list'" class="form-item mt-8">
  849. <label>第几页</label>
  850. <t-input-number
  851. v-model="sqlDialog.sql.current"
  852. theme="normal"
  853. :min="1"
  854. placeholder="默认1"
  855. />
  856. </div>
  857. <div v-if="sqlDialog.sql.method === 'list'" class="form-item mt-8">
  858. <label>每页数量</label>
  859. <t-input-number
  860. v-model="sqlDialog.sql.pageSize"
  861. theme="normal"
  862. placeholder="默认20"
  863. :min="1"
  864. />
  865. </div>
  866. <div class="form-item mt-8">
  867. <label>关联属性名</label>
  868. <t-input v-model="sqlDialog.sql.bindId" placeholder="关联属性名" />
  869. </div>
  870. <div class="flex mt-8">
  871. <!-- <label> -->
  872. <t-button style="width: 75px" @click="sqlTest">连接测试</t-button>
  873. <!-- </label> -->
  874. <p class="ml-8" style="width: 700px">{{ sqlDialog.result }}</p>
  875. </div>
  876. </div>
  877. </t-dialog>
  878. <t-dialog
  879. v-if="iotDialog.show"
  880. :visible="true"
  881. width="472px"
  882. class="data-dialog"
  883. :header="iotDialog.header"
  884. @close="iotDialog.show = false"
  885. @confirm="onOkIot"
  886. >
  887. <!-- <t-input
  888. v-model="iotSearch"
  889. @change="onSearchIot"
  890. @enter="onSearchIot"
  891. placeholder="设备属性搜索"
  892. /> -->
  893. <div class="input-search" style="padding: 0px 4px">
  894. <div class="btn" style="left: 14px">
  895. <img src="/img/icon_search_gray.svg" />
  896. </div>
  897. <t-input
  898. style="height: 32px"
  899. v-model="iotSearch"
  900. @change="onSearchIot"
  901. @enter="onSearchIot"
  902. placeholder="搜索设备属性"
  903. />
  904. </div>
  905. <div style="height: 320px; margin-top: 8px; overflow-y: scroll">
  906. <t-tree
  907. style="overflow-y: hidden"
  908. activeMultiple
  909. v-model="checkedIots"
  910. :data="iots"
  911. :expand-parent="true"
  912. :checkable="true"
  913. :checkStrictly="false"
  914. allow-fold-node-on-filter
  915. :filter="iotFilter"
  916. :scroll="{
  917. // rowHeight: 34,
  918. bufferSize: 10,
  919. threshold: 10,
  920. type: 'virtual',
  921. }"
  922. />
  923. </div>
  924. </t-dialog>
  925. </template>
  926. <script lang="ts" setup>
  927. import { reactive, defineComponent, ref, onMounted, toRaw, watch } from 'vue';
  928. import {
  929. FileImportIcon,
  930. FileExportIcon,
  931. DeleteIcon,
  932. AddIcon,
  933. AddCircleIcon,
  934. MinusCircleIcon,
  935. Edit2Icon,
  936. FileExcelIcon,
  937. CloudDownloadIcon,
  938. SearchIcon,
  939. Fullscreen2Icon,
  940. FullscreenExit1Icon,
  941. HelpCircleIcon,
  942. CaretDownSmallIcon,
  943. CaretRightSmallIcon,
  944. RouterWaveIcon,
  945. ArrowUpDown3Icon,
  946. Download1Icon,
  947. ApplicationIcon,
  948. DataIcon,
  949. ControlPlatformIcon,
  950. } from 'tdesign-icons-vue-next';
  951. import { typeOptions } from '@/services/common';
  952. import { MessagePlugin } from 'tdesign-vue-next';
  953. import { Pen, deepClone } from '@meta2d/core';
  954. import { useDot } from '@/services/common';
  955. import { isDownload } from '@/services/defaults';
  956. import { cdn } from '@/services/api';
  957. import { importExcel, saveAsExcel } from '@/services/excel';
  958. import axios from 'axios';
  959. import { transformData } from '@/services/utils';
  960. import { debounce } from '@/services/debouce';
  961. import Net from '@/views/components/Net.vue';
  962. import CodeEditor from '@/views/components/common/CodeEditor.vue';
  963. import { s8 } from '@/services/random';
  964. import { useUser } from '@/services/user';
  965. import {
  966. getSqlSourceList,
  967. getDevices,
  968. getDeviceProperties,
  969. getMqttUrl,
  970. doSqlCode,
  971. } from '@/services/iot';
  972. const props = defineProps<{
  973. group: string;
  974. }>();
  975. const { user } = useUser();
  976. const { dot, setDot } = useDot();
  977. const data = reactive({
  978. //列表
  979. datesetBak: {},
  980. datasetId: '',
  981. networks: [],
  982. http_networks:[],
  983. ws_networks:[],
  984. mqtt_networks:[],
  985. SSE_networks:[],
  986. datasetList: [],
  987. dataset: {
  988. name: '',
  989. id: '',
  990. url: '',
  991. devices: [],
  992. } as any,
  993. checkAll: false,
  994. //获取
  995. networkList: [],
  996. input: '',
  997. popupVisible: false,
  998. networkId: '',
  999. //监听
  1000. socketCbJs: '',
  1001. full: false,
  1002. dataMocks: [
  1003. // {
  1004. // name: '',
  1005. // id: '',
  1006. // enableMock: true,
  1007. // mock: 'aaa',
  1008. // type: 'string',
  1009. // expend: true,
  1010. // show: false,
  1011. // },
  1012. ],
  1013. enableMock: false,
  1014. randomkey: s8(),
  1015. sqls: [],
  1016. iotList: [],
  1017. iotTree: [],
  1018. });
  1019. onMounted(() => {
  1020. meta2d.store.data.networks?.forEach((network:any)=>
  1021. {
  1022. if(!network.label){
  1023. network.label = network.name;
  1024. }
  1025. if(!network.value){
  1026. network.value = s8();
  1027. }
  1028. network.checkable = false
  1029. });
  1030. data.networks = meta2d.store.data.networks || [];
  1031. data.dataset = (meta2d.store.data as any).dataset || {};
  1032. if( data.networks?.length&&data.dataset?.devices){
  1033. if(!data.networks[0].children?.length){
  1034. data.dataset.devices.forEach((item)=>item.value = item.id);
  1035. data.networks[0].children = deepClone(data.dataset.devices);
  1036. }
  1037. }
  1038. data.socketCbJs = meta2d.store.data.socketCbJs || '';
  1039. // data.dataMocks = meta2d.store.data.dataMocks || [];
  1040. data.enableMock = meta2d.store.data.enableMock || false;
  1041. // getNetworks();
  1042. // getDatasets();
  1043. // getIot();
  1044. data.sqls = meta2d.store.data.sqls || [];
  1045. data.iotList = meta2d.store.data.iot?.list || [];
  1046. data.iotTree = meta2d.store.data.iot?.tree || [];
  1047. getThirdNetwork();
  1048. });
  1049. const getThirdNetwork = (key?:string)=>{
  1050. if(!meta2d.store.data.networks?.length){
  1051. return;
  1052. }
  1053. meta2d.store.data.networks.forEach((item:any,index)=>{
  1054. item.tem_index = index;
  1055. });
  1056. if(!key||key==='http'){
  1057. data.http_networks = meta2d.store.data.networks.filter((item)=>item.protocol==='http') || [];
  1058. }
  1059. if(!key||key==='websocket'){
  1060. data.ws_networks = meta2d.store.data.networks.filter((item)=>item.protocol==='websocket') || [];
  1061. }
  1062. if(!key||key==='mqtt'){
  1063. data.mqtt_networks = meta2d.store.data.networks.filter((item)=>item.protocol==='mqtt') || [];
  1064. }
  1065. if(!key||key==='SSE'){
  1066. data.SSE_networks = meta2d.store.data.networks.filter((item)=>item.protocol==='SSE') || [];
  1067. }
  1068. }
  1069. const iotDialog = ref({
  1070. show: false,
  1071. header: '添加物联网平台',
  1072. });
  1073. const onOkIot = () => {
  1074. let _iots = [];
  1075. iots.value.forEach((item) => {
  1076. if (checkedIots.value.includes(item.value)) {
  1077. _iots.push(deepClone(item));
  1078. } else {
  1079. if (item.children?.length) {
  1080. const child = item.children.filter((child) =>
  1081. checkedIots.value.includes(child.value)
  1082. );
  1083. if (child.length) {
  1084. _iots.push({
  1085. label: item.label,
  1086. value: item.value,
  1087. deviceId: item.deviceId, //item.id
  1088. token: item.token,
  1089. children: deepClone(child),
  1090. });
  1091. }
  1092. }
  1093. }
  1094. });
  1095. data.iotTree = _iots;
  1096. if (!meta2d.store.data.iot) {
  1097. meta2d.store.data.iot = {};
  1098. }
  1099. meta2d.store.data.iot.tree = _iots;
  1100. iotDialog.value.show = false;
  1101. };
  1102. const onShowIot = async () => {
  1103. await getIotTree();
  1104. getCheckedIots();
  1105. iotDialog.value.show = true;
  1106. };
  1107. const iots = ref([]);
  1108. const getIotTree = async () => {
  1109. if (iots.value.length) {
  1110. return;
  1111. }
  1112. let ret = await getDevices();
  1113. const type = ret.type;
  1114. const list = ret.list;
  1115. for (let i = 0; i < list.length; i++) {
  1116. const item = list[i];
  1117. item.label = item.name;
  1118. item.value = item.id;
  1119. item.deviceId = item.id; //item.id
  1120. item.token = item.token;
  1121. // item.children = true;
  1122. // item.checkable = false;
  1123. let properties = await getDeviceProperties(item.id);
  1124. item.children = properties.map((prop: any) => {
  1125. return {
  1126. label: prop.name,
  1127. value: type?prop.key:item.deviceId + '#' + prop.key,
  1128. _label: item.name + '#' + prop.name,
  1129. token: item.token,
  1130. class: 'iot',
  1131. };
  1132. });
  1133. if (!item.children.length) {
  1134. item.checkable = false;
  1135. }
  1136. }
  1137. // setTimeout(()=>{
  1138. iots.value = deepClone(list);
  1139. // },3000);
  1140. };
  1141. const getCheckedIots = ()=>{
  1142. let arr = [];
  1143. meta2d.store.data?.iot?.tree.forEach((item)=>{
  1144. arr.push(...item.children?.map((_item)=>_item.value));
  1145. });
  1146. checkedIots.value = arr;
  1147. }
  1148. const iotSearch = ref('');
  1149. const iotFilter = ref(null);
  1150. const onSearchIot = () => {
  1151. iotFilter.value = iotSearch.value
  1152. ? (node) =>
  1153. node.data.label.indexOf(iotSearch.value) >= 0 ||
  1154. node.data.value.indexOf(iotSearch.value) >= 0
  1155. : null;
  1156. };
  1157. const checkedIots = ref([]);
  1158. const onShowSql = async () => {
  1159. addSql();
  1160. };
  1161. const iotLoad = async (node) => {
  1162. let properties = await getDeviceProperties(node.value);
  1163. return properties.map((item: any) => {
  1164. item.label = item.name;
  1165. item.value = node.data.deviceId + '#' + item.key;
  1166. item._label = node.data.name + '#' + item.name;
  1167. // item.sn = item.sn;
  1168. item.token = node.data.token;
  1169. return item;
  1170. });
  1171. };
  1172. const doBind = (node) => {
  1173. };
  1174. const iotMqtt = ref({});
  1175. const iotProtocol = ref('');
  1176. const getIot = async () => {
  1177. let canmqtt: any = await getMqttUrl();
  1178. if (canmqtt && canmqtt.host) {
  1179. iotMqtt.value = canmqtt;
  1180. }
  1181. if (meta2d.store.data.iot) {
  1182. iotProtocol.value = meta2d.store.data.iot.protocol;
  1183. }
  1184. };
  1185. const selectIot = (protocol: any) => {
  1186. if (iotProtocol.value === protocol) {
  1187. meta2d.store.data.iot = {};
  1188. iotProtocol.value = '';
  1189. } else {
  1190. if (protocol === 'mqtt') {
  1191. meta2d.store.data.iot = {
  1192. protocol: protocol,
  1193. ...iotMqtt.value,
  1194. };
  1195. } else {
  1196. meta2d.store.data.iot = {
  1197. protocol: protocol,
  1198. // host: iotMqtt.value.host
  1199. };
  1200. }
  1201. iotProtocol.value = protocol;
  1202. }
  1203. meta2d.connectNetwork();
  1204. };
  1205. const addSql = async () => {
  1206. sqlList.value = await getSqlSourceList();
  1207. sqlDialog.header = '添加sql数据源';
  1208. sqlDialog.show = true;
  1209. sqlDialog.edit = false;
  1210. sqlDialog.sql = {
  1211. interval: undefined,
  1212. sql: '-- eg:SELECT * FROM "directory"',
  1213. };
  1214. sqlDialog.result = '';
  1215. };
  1216. const editSql = (sql: any, index: number) => {
  1217. sqlDialog.header = '编辑sql数据源';
  1218. sqlDialog.show = true;
  1219. sqlDialog.edit = true;
  1220. sqlDialog.index = index;
  1221. sqlDialog.sql = deepClone(sql);
  1222. };
  1223. const deleteSql = (index: number) => {
  1224. data.sqls.splice(index, 1);
  1225. (meta2d.store.data as any).sqls = data.sqls;
  1226. sqlTreeKey.value = s8();
  1227. setDot(true);
  1228. };
  1229. const sqlTreeKey = ref(s8());
  1230. const onOkSql = async () => {
  1231. if (!sqlDialog.sql.dbid) {
  1232. MessagePlugin.error('请选择数据源');
  1233. return;
  1234. }
  1235. if (!sqlDialog.sql.sql) {
  1236. MessagePlugin.error('请填写sql语句');
  1237. return;
  1238. }
  1239. if (!sqlDialog.sql.interval) {
  1240. sqlDialog.sql.interval = undefined;
  1241. }
  1242. if (!sqlDialog.sql.bindId) {
  1243. MessagePlugin.error('关联属性名必填');
  1244. return;
  1245. }
  1246. sqlDialog.sql.label = sqlDialog.sql.bindId;
  1247. sqlDialog.sql.value = sqlDialog.sql.bindId;
  1248. // let sql = sqlDialog.value.sql+' LIMIT '+(sqlDialog.value.pageSize||20)+(sqlDialog.value.current>1?(' OFFSET '+(sqlDialog.value.current-1)*sqlDialog.value.pageSize):'');
  1249. if (!sqlDialog.sql.children) {
  1250. await sqlTest();
  1251. sqlTreeKey.value = s8();
  1252. }
  1253. if (!sqlDialog.edit) {
  1254. data.sqls.push(sqlDialog.sql);
  1255. // meta2d.store.data.sqls = data.sqls;
  1256. } else {
  1257. data.sqls[sqlDialog.index] = sqlDialog.sql;
  1258. }
  1259. meta2d.store.data.sqls = toRaw(data.sqls);
  1260. sqlTreeKey.value = s8();
  1261. // data.sqls = deepClone(data.sqls);
  1262. meta2d.connectNetwork();
  1263. sqlDialog.show = false;
  1264. };
  1265. const sqlDialog = reactive<any>({
  1266. show: false,
  1267. edit: false,
  1268. index: -1,
  1269. header: '添加sql数据源',
  1270. sql: {
  1271. interval: undefined,
  1272. sql: '',
  1273. },
  1274. });
  1275. const sqlList = ref([]);
  1276. const sqlChange = (sql: any) => {
  1277. sqlDialog.sql.dbid = sql.id;
  1278. sqlDialog.sql.dbType = sql.dbType;
  1279. sqlDialog.sql.name = sql.name;
  1280. };
  1281. const sqlTest = async () => {
  1282. let ret: any = await doSqlCode(sqlDialog.sql);
  1283. if (ret.error) {
  1284. MessagePlugin.error('连接错误:' + ret.error);
  1285. sqlDialog.result = '连接错误:' + ret.error;
  1286. } else {
  1287. if (sqlDialog.sql.method === 'list') {
  1288. sqlDialog.result = '连接成功:[' + JSON.stringify(ret[0]) + ',...]';
  1289. // sqlDialog.sql.columns = ret[0];
  1290. const columnsKeys = Object.keys(ret[0]);
  1291. const children = new Array(ret.length).fill(0).map((item, index) => {
  1292. return {
  1293. label: index + 1 + '',
  1294. value: sqlDialog.sql.bindId + '#' + index,
  1295. _label: sqlDialog.sql.bindId + '#' + index + 1,
  1296. class: 'sql',
  1297. children: columnsKeys.map((key) => {
  1298. return {
  1299. label: key,
  1300. value: sqlDialog.sql.bindId + '#' + index + '#' + key,
  1301. _label: sqlDialog.sql.bindId + '#' + (index + 1) + '#' + key,
  1302. class: 'sql',
  1303. };
  1304. }),
  1305. };
  1306. });
  1307. sqlDialog.sql.class = 'sql';
  1308. sqlDialog.sql.children = children;
  1309. } else {
  1310. sqlDialog.result = '连接成功:' + JSON.stringify(ret);
  1311. // sqlDialog.sql.columns = ret;
  1312. const children = [];
  1313. for (let key in ret) {
  1314. children.push({
  1315. label: key,
  1316. value: sqlDialog.sql.bindId + '#' + key,
  1317. _label: sqlDialog.sql.bindId + '#' + key,
  1318. class: 'sql',
  1319. });
  1320. }
  1321. sqlDialog.sql.class = 'sql';
  1322. sqlDialog.sql.children = children;
  1323. }
  1324. }
  1325. };
  1326. const addDataDialog = reactive<any>({});
  1327. const showAddData = (network: any, node?: any) => {
  1328. addDataDialog.network = network;
  1329. const row = node?.data;
  1330. const index = node?.getIndex();
  1331. addDataDialog.node = node;
  1332. if (row) {
  1333. addDataDialog.header = '编辑数据';
  1334. addDataDialog.data = JSON.parse(JSON.stringify(row));
  1335. addDataDialog.index = index;
  1336. } else {
  1337. addDataDialog.header = '添加数据';
  1338. addDataDialog.data = { type: 'string', expend: true };
  1339. }
  1340. addDataDialog.show = true;
  1341. };
  1342. const deleteData = (network: any, index: number) => {
  1343. network.children.splice(index, 1);
  1344. meta2d.store.data.networks = toRaw(data.networks);
  1345. // data.dataset.devices.splice(row, 1);
  1346. // (meta2d.store.data as any).dataset = data.dataset;
  1347. setDot(true);
  1348. };
  1349. const clearData = () => {
  1350. data.datasetId = undefined;
  1351. data.dataset.id = undefined;
  1352. data.dataset.name = undefined;
  1353. data.dataset.url = undefined;
  1354. data.dataset.devices = [];
  1355. setDot(true);
  1356. };
  1357. const changeDataLabel = (value) => {
  1358. if (!value) {
  1359. MessagePlugin.error('显示名称不能为空!');
  1360. return;
  1361. }
  1362. let item = data.dataset.devices?.filter((item) => item.label === value);
  1363. if (item && item.length) {
  1364. MessagePlugin.error('显示名称重复!');
  1365. return;
  1366. }
  1367. addDataDialog.data.label = value;
  1368. };
  1369. const changeDataID = (value) => {
  1370. if (!value) {
  1371. MessagePlugin.error('属性名不能为空!');
  1372. return;
  1373. }
  1374. let item = data.dataset.devices?.filter((item) => item.id === value);
  1375. if (item && item.length) {
  1376. MessagePlugin.error('属性名重复!');
  1377. return;
  1378. }
  1379. addDataDialog.data.id = value;
  1380. };
  1381. const onOkAddData = () => {
  1382. if (!addDataDialog.data.label) {
  1383. MessagePlugin.error('请填写名称');
  1384. return;
  1385. }
  1386. if (!addDataDialog.data.id) {
  1387. MessagePlugin.error('请填写数据ID');
  1388. return;
  1389. }
  1390. if (!addDataDialog.network.children) {
  1391. addDataDialog.network.children = [];
  1392. }
  1393. addDataDialog.data.value = addDataDialog.data.id;
  1394. if (addDataDialog.header === '添加数据') {
  1395. addDataDialog.network.children.push(addDataDialog.data);
  1396. if(addDataDialog.network.protocol === 'http'){
  1397. httpTree.value.appendTo(addDataDialog.network.value, addDataDialog.data);
  1398. }else if(addDataDialog.network.protocol === 'websocket'){
  1399. wsTree.value.appendTo(addDataDialog.network.value, addDataDialog.data);
  1400. }else if(addDataDialog.network.protocol === 'mqtt'){
  1401. mqttTree.value.appendTo(addDataDialog.network.value, addDataDialog.data);
  1402. }else if(addDataDialog.network.protocol === 'SSE'){
  1403. SSETree.value.appendTo(addDataDialog.network.value, addDataDialog.data);
  1404. }
  1405. } else {
  1406. // addDataDialog.network.children[addDataDialog.index] = addDataDialog.data;
  1407. addDataDialog.network.children.splice(
  1408. addDataDialog.index,
  1409. 1,
  1410. addDataDialog.data
  1411. );
  1412. addDataDialog.node?.insertAfter(deepClone(addDataDialog.data));
  1413. addDataDialog.node?.remove();
  1414. // mqttTreeKey.value = s8();
  1415. // wsTreeKey.value = s8();
  1416. // httpTreeKey.value = s8();
  1417. // networkTree.value.setItem(addDataDialog.network.value, {children:deepClone(addDataDialog.network.children)});
  1418. // addDataDialog.node?.setData(deepClone(addDataDialog.data));
  1419. //更新所有绑定该id的pen label
  1420. // let binds = meta2d.store.bind[addDataDialog.data.id];
  1421. // if (binds) {
  1422. // binds.forEach((item) => {
  1423. // const pen: Pen = meta2d.findOne(item.id);
  1424. // pen.realTimes &&
  1425. // pen.realTimes.forEach((_realTime) => {
  1426. // if (_realTime.key === item.key) {
  1427. // _realTime.bind.label = addDataDialog.data.label;
  1428. // }
  1429. // });
  1430. // });
  1431. // }
  1432. }
  1433. // (meta2d.store.data as any).dataset = data.dataset;
  1434. meta2d.store.data.networks = toRaw(data.networks);
  1435. addDataDialog.show = false;
  1436. };
  1437. const importDataset = async (network) => {
  1438. let columns: any = [
  1439. {
  1440. header: '设备',
  1441. key: 'device',
  1442. },
  1443. {
  1444. header: '显示名称',
  1445. key: 'label',
  1446. },
  1447. {
  1448. header: '属性名',
  1449. key: 'id',
  1450. },
  1451. {
  1452. header: '类型',
  1453. key: 'type',
  1454. },
  1455. {
  1456. header: '值范围',
  1457. key: 'mock',
  1458. },
  1459. ];
  1460. const _data: any = await importExcel(columns);
  1461. _data.forEach((item) => {
  1462. if (item.device) {
  1463. item.label = item.device + '-' + item.label;
  1464. delete item.device;
  1465. }
  1466. item.value = item.id;
  1467. });
  1468. if (!network.children) {
  1469. network.children = [];
  1470. }
  1471. mergeDataset(network.children, _data);
  1472. if(network.protocol === 'http'){
  1473. httpTree.value.appendTo(network.value, network.children);
  1474. }else if(network.protocol === 'websocket'){
  1475. wsTree.value.appendTo(network.value, network.children);
  1476. }else if(network.protocol === 'mqtt'){
  1477. mqttTree.value.appendTo(network.value, network.children);
  1478. }else if(network.protocol === 'SSE'){
  1479. SSETree.value.appendTo(network.value, network.children);
  1480. }
  1481. // networkTree.value.appendTo(network.value, network.children);
  1482. // (meta2d.store.data as any).dataset = data.dataset;
  1483. };
  1484. const downloadAsExcel = () => {
  1485. if (!(data.dataset.devices && data.dataset.devices.length)) {
  1486. MessagePlugin.error('属性列表不能为空!');
  1487. return;
  1488. }
  1489. const name = meta2d.store.data.name;
  1490. const columns: any[] = [
  1491. {
  1492. key: 'label',
  1493. header: '显示名称',
  1494. },
  1495. {
  1496. key: 'id',
  1497. header: '属性名',
  1498. },
  1499. {
  1500. key: 'type',
  1501. header: '类型',
  1502. },
  1503. {
  1504. key: 'mock',
  1505. header: '值范围',
  1506. },
  1507. ];
  1508. saveAsExcel(name, columns, data.dataset.devices);
  1509. };
  1510. const downloadAsJson = () => {
  1511. if (!(data.dataset.devices && data.dataset.devices.length)) {
  1512. MessagePlugin.error('属性列表不能为空!');
  1513. return;
  1514. }
  1515. import('file-saver').then(({ saveAs }) => {
  1516. saveAs(
  1517. new Blob([JSON.stringify(data.dataset)], {
  1518. type: 'text/plain;charset=utf-8',
  1519. }),
  1520. `${data.dataset.name || '未命名'}.json`
  1521. );
  1522. });
  1523. };
  1524. const onOkDataset = async (saveas = false) => {
  1525. // if (!dataDialog.dataset.name) {
  1526. // MessagePlugin.error('名称不能为空');
  1527. // return;
  1528. // }
  1529. if (!(data.dataset.devices && data.dataset.devices.length)) {
  1530. MessagePlugin.error('属性列表不能为空');
  1531. return;
  1532. }
  1533. const dataset = JSON.parse(JSON.stringify(data.dataset));
  1534. let _data = dataset;
  1535. _data.type = 'dataset';
  1536. if (!_data.name) {
  1537. _data.name = meta2d.store.data.name;
  1538. }
  1539. _data = transformData(dataset, 'toNetwork');
  1540. // 保存到我的数据源
  1541. if (saveas || !dataset.id) {
  1542. delete dataset.id;
  1543. delete dataset._id;
  1544. dataset.type = 'dataset';
  1545. const ret: any = await axios.post(`/api/data/datasource/add`, _data);
  1546. if (!ret) {
  1547. return;
  1548. }
  1549. ret.id = ret.id || ret._id;
  1550. data.datasetId = ret.id;
  1551. dataset.id = ret.id;
  1552. data.dataset.id = ret.id;
  1553. data.datasetList.push(dataset);
  1554. } else {
  1555. const ret: any = await axios.post(`/api/data/datasource/update`, _data);
  1556. if (!ret) {
  1557. return;
  1558. }
  1559. data.datasetList.forEach((item: any, index: number) => {
  1560. if (
  1561. item.id === dataset.id ||
  1562. (item._id === dataset._id && dataset._id != undefined)
  1563. ) {
  1564. data.datasetList.splice(index, 1, dataset);
  1565. }
  1566. });
  1567. }
  1568. delete dataset.devices;
  1569. // @ts-ignore
  1570. // meta2d.store.data.dataset = dataset;
  1571. // setDot(true);
  1572. data.editDataset = false;
  1573. delete data.datesetBak;
  1574. };
  1575. const mergeDataset = (arr1: any, arr2: any[]) => {
  1576. if (!(arr2 && arr2.length)) {
  1577. return;
  1578. }
  1579. if (!arr1) {
  1580. arr1 = [];
  1581. }
  1582. arr2.forEach((item) => {
  1583. let index = arr1.findIndex((elem) => elem.id === item.id);
  1584. if (index >= 0) {
  1585. Object.assign(arr1[index], item);
  1586. } else {
  1587. arr1.push(item);
  1588. }
  1589. });
  1590. };
  1591. const getDatas = async () => {
  1592. if (!data.dataset.url) {
  1593. return;
  1594. }
  1595. const ret = await axios.get(data.dataset.url);
  1596. let flattenRet = flattenTree(ret);
  1597. if (flattenRet) {
  1598. mergeDataset(data.dataset, flattenRet);
  1599. (meta2d.store.data as any).dataset = data.dataset;
  1600. }
  1601. };
  1602. //展开树
  1603. const flattenTree = (root) => {
  1604. if (!root) return []; // 空树返回空数组
  1605. const result = []; // 存储展开后的数组
  1606. // 递归遍历树
  1607. function dfs(node) {
  1608. result.push({
  1609. id: node.id,
  1610. label: node.label || node.name,
  1611. device: node.device || node.id,
  1612. type: node.type,
  1613. mock: node.mock,
  1614. // children: node.children,
  1615. }); // 将当前节点值添加到结果数组
  1616. // 遍历当前节点的子节点
  1617. if (node.children) {
  1618. for (let child of node.children) {
  1619. dfs(child); // 递归调用DFS
  1620. }
  1621. }
  1622. }
  1623. root.forEach((item) => {
  1624. dfs(item);
  1625. });
  1626. // dfs(root); // 从根节点开始遍历
  1627. return result;
  1628. };
  1629. const onSelDataset = async (datasetId = false) => {
  1630. if (datasetId) {
  1631. const dataset = data.datasetList.find((item: any) => {
  1632. return item.id === datasetId;
  1633. });
  1634. if (!dataset) {
  1635. return;
  1636. }
  1637. if (dataset.url) {
  1638. const ret = await axios.get(dataset.url);
  1639. if (ret) {
  1640. dataset.devices = ret;
  1641. }
  1642. } else {
  1643. const ret = await axios.post(`/api/data/datasource/get`, {
  1644. id: dataset.id,
  1645. });
  1646. if (ret?.data) {
  1647. Object.assign(dataset, { ...ret.data });
  1648. }
  1649. }
  1650. mergeDataset(data.dataset, dataset.devices);
  1651. (meta2d.store.data as any).dataset = data.dataset;
  1652. // dataDialog.dataset = dataset;
  1653. // if (!init) {
  1654. // const d = JSON.parse(JSON.stringify(dataset));
  1655. // delete d.devices;
  1656. // // @ts-ignore
  1657. // meta2d.store.data.dataset = d;
  1658. // setDot(true);
  1659. // }
  1660. }
  1661. };
  1662. // 请求我的数据模型
  1663. const getDatasets = async (name?: string) => {
  1664. if (!user.id) {
  1665. MessagePlugin.error('请先登录');
  1666. return;
  1667. }
  1668. const body: any = {
  1669. type: 'dataset',
  1670. projection: 'id,data,name,type',
  1671. };
  1672. if (name) {
  1673. body.name = name;
  1674. }
  1675. const ret: any = await axios.post(`/api/data/datasource/list`, body, {
  1676. params: {
  1677. current: 1,
  1678. pageSize: 10,
  1679. },
  1680. });
  1681. if (ret?.list) {
  1682. const list = [];
  1683. let found = false;
  1684. for (const item of ret.list) {
  1685. item.id = item.id || item._id;
  1686. list.push(transformData(item, 'toMetaNetwork'));
  1687. if (data.dataset?.id === item.id) {
  1688. found = true;
  1689. }
  1690. }
  1691. if (data.dataset?.id && !found) {
  1692. list.push(data.dataset);
  1693. }
  1694. data.datasetList = list;
  1695. }
  1696. };
  1697. const onDelDataset = async (item: any, i: number) => {
  1698. const ret: any = await axios.post(`/api/data/datasource/delete`, {
  1699. id: item.id,
  1700. });
  1701. if (
  1702. (meta2d.store.data as any).dataset &&
  1703. (meta2d.store.data as any).dataset.id === item.id
  1704. ) {
  1705. //@ts-ignore
  1706. meta2d.store.data.dataset = {};
  1707. data.dataset = {};
  1708. data.datasetId = undefined;
  1709. }
  1710. if (ret) {
  1711. data.datasetList.splice(i, 1);
  1712. }
  1713. };
  1714. const search = ref('');
  1715. const onSearch = () => {
  1716. // if (!search.value) {
  1717. // return;
  1718. // }
  1719. data.dataset.devices.forEach((item) => {
  1720. if (
  1721. item.label.indexOf(search.value) !== -1 ||
  1722. item.id.indexOf(search.value) !== -1
  1723. ) {
  1724. item.show = true;
  1725. } else {
  1726. item.show = false;
  1727. }
  1728. });
  1729. };
  1730. const onInputNetwork = (e) => {
  1731. debounce(getNetworks, 300, e);
  1732. };
  1733. const onSelectNetWork = (value) => {
  1734. if (!value) {
  1735. return;
  1736. }
  1737. const network: any = data.networks.find((elem: any) => value === elem.id);
  1738. if (!network) {
  1739. const item = data.networkList.find((elem: any) => value === elem.id);
  1740. data.networks.push(item);
  1741. meta2d.store.data.networks = toRaw(data.networks);
  1742. meta2d.connectNetwork();
  1743. setDot(true);
  1744. }
  1745. // data.input = null;
  1746. data.popupVisible = false;
  1747. };
  1748. // 请求我的实时数据
  1749. const getNetworks = async (e?) => {
  1750. const ret: any = await axios.post(
  1751. `/api/data/datasource/list`,
  1752. {
  1753. // q: {
  1754. name: e,
  1755. // },
  1756. type: 'subscribe',
  1757. projection: 'id,data,name,type',
  1758. },
  1759. {
  1760. params: {
  1761. current: 1,
  1762. pageSize: 10,
  1763. },
  1764. }
  1765. );
  1766. if (ret?.list) {
  1767. const list = [];
  1768. for (const item of ret.list) {
  1769. item.id = item.id || item._id;
  1770. list.push(transformData(item, 'toMetaNetwork'));
  1771. }
  1772. data.networkList = list;
  1773. }
  1774. };
  1775. const onDelNetWork = async (item: any, i: number) => {
  1776. const ret: any = await axios.post(`/api/data/datasource/delete`, {
  1777. id: item.id || item._id,
  1778. });
  1779. if (ret) {
  1780. data.networkList.splice(i, 1);
  1781. }
  1782. };
  1783. const networkTree = ref();
  1784. const networkTreeKey = ref(s8());
  1785. const mqttTree = ref();
  1786. const mqttTreeKey = ref(s8());
  1787. const wsTree = ref();
  1788. const wsTreeKey = ref(s8());
  1789. const httpTree = ref();
  1790. const httpTreeKey = ref(s8());
  1791. const SSETree = ref();
  1792. const SSETreeKey = ref(s8());
  1793. const deleteNetwork = (index: number) => {
  1794. data.networks.splice(index, 1);
  1795. meta2d.store.data.networks = toRaw(data.networks);
  1796. getThirdNetwork('xxx');
  1797. meta2d.connectNetwork();
  1798. setDot(true);
  1799. };
  1800. const networkDialog = reactive<any>({
  1801. save: true,
  1802. });
  1803. const editNetwork = (network: any, index: number) => {
  1804. networkDialog.network = JSON.parse(JSON.stringify(data.networks[index]));
  1805. networkDialog.editNetwork = 2;
  1806. networkDialog.editNetworkIndex = index;
  1807. networkDialog.header = `编辑${networkDialog.network.protocol}数据源`;
  1808. networkDialog.show = true;
  1809. };
  1810. const addNetwork = (protocol:string) => {
  1811. networkDialog.network = {
  1812. name: '',
  1813. type: 'subscribe',
  1814. protocol,
  1815. url: '',
  1816. // options: {
  1817. // clientId: '',
  1818. // username: '',
  1819. // password: '',
  1820. // customClientId: false,
  1821. // },
  1822. };
  1823. if(protocol === 'mqtt'){
  1824. networkDialog.network.options = {
  1825. clientId: '',
  1826. username: '',
  1827. password: '',
  1828. customClientId: false,
  1829. };
  1830. }else if(protocol === 'websocket'){
  1831. networkDialog.network.options = {protocols:''}
  1832. }
  1833. networkDialog.editNetwork = 1;
  1834. networkDialog.header = `添加${protocol}数据源`;
  1835. networkDialog.show = true;
  1836. };
  1837. const onOkNetwork = async () => {
  1838. const _data = transformData(networkDialog.network, 'toNetwork');
  1839. networkDialog.network.label = networkDialog.network.name;
  1840. // networkDialog.network.value = networkDialog.network.url;
  1841. networkDialog.network.value = s8();
  1842. networkDialog.network.checkable = false;
  1843. if (networkDialog.editNetwork === 1) {
  1844. if (
  1845. ['mqtt', 'websocket', 'http'].includes(networkDialog.network.protocol) &&
  1846. !networkDialog.network.url
  1847. ) {
  1848. MessagePlugin.error('URL地址不能为空!');
  1849. return;
  1850. }
  1851. if (!networkDialog.network.name) {
  1852. MessagePlugin.error('名称不能为空!');
  1853. return;
  1854. }
  1855. // if (networkDialog.save) {
  1856. // const ret: any = await axios.post(`/api/data/datasource/add`, _data);
  1857. // if (!ret) {
  1858. // return;
  1859. // }
  1860. // ret.id = ret.id || ret._id;
  1861. // networkDialog.network.id = ret.id;
  1862. // }
  1863. data.networks.push(networkDialog.network);
  1864. // getThirdNetwork(networkDialog.network.protocol);
  1865. // data.networkList.push(networkDialog.network);
  1866. // networkTree.value?.appendTo('', networkDialog.network);
  1867. } else if (networkDialog.editNetwork === 2) {
  1868. // if (networkDialog.save) {
  1869. // const ret: any = await axios.post(`/api/data/datasource/update`, _data);
  1870. // if (!ret) {
  1871. // return;
  1872. // }
  1873. // }
  1874. //替换
  1875. let index = networkDialog.editNetworkIndex;
  1876. if (index !== undefined) {
  1877. data.networks.splice(index, 1, networkDialog.network);
  1878. // getThirdNetwork(networkDialog.network.protocol);
  1879. // networkTreeKey.value = s8();
  1880. if(networkDialog.network.protocol==='http'){
  1881. httpTreeKey.value = s8();
  1882. }else if(networkDialog.network.protocol==='websocket'){
  1883. wsTreeKey.value = s8();
  1884. }else if(networkDialog.network.protocol==='mqtt'){
  1885. mqttTreeKey.value = s8();
  1886. }else if(networkDialog.network.protocol==='SSE'){
  1887. SSETreeKey.value = s8();
  1888. }
  1889. // networkTree.value.setItem(networkDialog.network.value,networkDialog.network);
  1890. }
  1891. }
  1892. networkDialog.show = false;
  1893. networkDialog.editNetwork = 0;
  1894. meta2d.store.data.networks = toRaw(data.networks);
  1895. getThirdNetwork(networkDialog.network.protocol);
  1896. meta2d.connectNetwork();
  1897. setDot(true);
  1898. };
  1899. const dataTransformationDialog = reactive<any>({
  1900. show: false,
  1901. data: '',
  1902. });
  1903. const showDataTransformation = () => {
  1904. dataTransformationDialog.data = meta2d.store.data.socketCbJs;
  1905. dataTransformationDialog.show = true;
  1906. };
  1907. const onOkDataTransformation = () => {
  1908. meta2d.store.data.socketCbJs = dataTransformationDialog.data;
  1909. data.randomkey = s8();
  1910. data.socketCbJs = dataTransformationDialog.data;
  1911. meta2d.listenSocket();
  1912. dataTransformationDialog.show = false;
  1913. };
  1914. let timer: any = 0;
  1915. const onSocketCbJsChange = (e) => {
  1916. clearTimeout(timer);
  1917. timer = setTimeout(() => {
  1918. meta2d.store.data.socketCbJs = data.socketCbJs;
  1919. meta2d.listenSocket();
  1920. }, 3000);
  1921. };
  1922. const onChangeMock = () => {
  1923. // @ts-ignore
  1924. data.enableMock = !data.enableMock;
  1925. meta2d.store.data.enableMock = data.enableMock;
  1926. if (data.enableMock) {
  1927. meta2d.startDataMock();
  1928. } else {
  1929. meta2d.stopDataMock();
  1930. }
  1931. };
  1932. const onCheckAllChange = (e) => {
  1933. if (!e) {
  1934. data.dataset?.devices?.forEach((item) => {
  1935. item.checked = false;
  1936. });
  1937. }
  1938. };
  1939. const allChecked = ref([]);
  1940. const networksCheked = ref([]);
  1941. const sqlsCheked = ref([]);
  1942. const onAddShape = (e, _data,type) => {
  1943. e.stopPropagation();
  1944. let data: any;
  1945. if (Array.isArray(_data)) {
  1946. const dragElem = document.getElementById(`dragElem-${type}`);
  1947. let width = dragElem.offsetWidth;
  1948. e.dataTransfer.setDragImage(dragElem, width / 2, 10);
  1949. const checked = [];
  1950. if(type === 'iot'){
  1951. _data.forEach((item) => {
  1952. item.children.forEach((_item) => {
  1953. if (allChecked.value.includes(_item.value)) {
  1954. checked.push(_item);
  1955. }
  1956. });
  1957. });
  1958. }else if(type === 'network'){
  1959. _data.forEach((item) => {
  1960. item.children.forEach((_item) => {
  1961. if (networksCheked.value.includes(_item.value)) {
  1962. checked.push(_item);
  1963. }
  1964. });
  1965. });
  1966. }else if(type === 'sql'){
  1967. _data.forEach((item) => {
  1968. item.children.forEach((_item) => {
  1969. if (sqlsCheked.value.includes(_item.value)) {
  1970. checked.push(_item);
  1971. }
  1972. });
  1973. });
  1974. }
  1975. // const checked = _data.filter((item) => item.checked);
  1976. if (!checked.length) {
  1977. MessagePlugin.error('请先选择数据');
  1978. return;
  1979. }
  1980. data = [];
  1981. checked.forEach((item) => {
  1982. let bind:any = {
  1983. class: 'iot',
  1984. id: item.value,
  1985. label: item._label,
  1986. token: item.token,
  1987. };
  1988. if(type === 'network'){
  1989. bind = {
  1990. id: item.id||item.value,
  1991. label: item.label,
  1992. }
  1993. }else if(type === 'sql'){
  1994. //TODO
  1995. bind = {
  1996. id: item.id||item.value,
  1997. label: item.label,
  1998. }
  1999. }
  2000. if (globalThis.style1) {
  2001. data.push(
  2002. ...[
  2003. {
  2004. text: item.label,
  2005. width: 150,
  2006. height: 20,
  2007. name: 'text',
  2008. dataset: true,
  2009. textAlign: 'right',
  2010. },
  2011. {
  2012. text: item.mock || 0,
  2013. width: 58,
  2014. height: 28,
  2015. name: 'rectangle',
  2016. dataset: true,
  2017. // textAlign: 'left',
  2018. color: '#478BFFFF',
  2019. textColor: '#A9C9FFFF',
  2020. background: '#478BFF1F',
  2021. fontWeight: 'bold',
  2022. borderRadius: 0.1,
  2023. lineWidth: 1,
  2024. realTimes: [
  2025. {
  2026. label: '文字',
  2027. key: 'text',
  2028. type: 'string',
  2029. bind: deepClone(item),
  2030. },
  2031. ],
  2032. },
  2033. ]
  2034. );
  2035. } else {
  2036. data.push(
  2037. ...[
  2038. {
  2039. text: item.label + ':',
  2040. width: 150,
  2041. height: 28,
  2042. name: 'text',
  2043. dataset: true,
  2044. textAlign: 'right',
  2045. },
  2046. {
  2047. text: item.mock,
  2048. width: 58,
  2049. height: 20,
  2050. name: 'text',
  2051. dataset: true,
  2052. textAlign: 'left',
  2053. realTimes: [
  2054. {
  2055. label: '文字',
  2056. key: 'text',
  2057. type: 'string',
  2058. bind: bind,
  2059. },
  2060. ],
  2061. },
  2062. ]
  2063. );
  2064. }
  2065. });
  2066. } else {
  2067. if (globalThis.style1) {
  2068. data = [
  2069. {
  2070. text: _data.label,
  2071. width: 150,
  2072. height: 28,
  2073. name: 'text',
  2074. dataset: true,
  2075. textAlign: 'right',
  2076. },
  2077. {
  2078. text: _data.mock || 0,
  2079. width: 58,
  2080. height: 28,
  2081. name: 'rectangle',
  2082. dataset: true,
  2083. // textAlign: 'left',
  2084. color: '#478BFFFF',
  2085. textColor: '#A9C9FFFF',
  2086. background: '#478BFF1F',
  2087. fontWeight: 'bold',
  2088. borderRadius: 0.1,
  2089. lineWidth: 1,
  2090. realTimes: [
  2091. {
  2092. label: '文字',
  2093. key: 'text',
  2094. type: 'string',
  2095. bind: deepClone(_data),
  2096. },
  2097. ],
  2098. },
  2099. ];
  2100. } else {
  2101. data = [
  2102. {
  2103. text: _data.label + ':',
  2104. width: 150,
  2105. height: 20,
  2106. name: 'text',
  2107. dataset: true,
  2108. textAlign: 'right',
  2109. },
  2110. {
  2111. text: _data.mock,
  2112. width: 58,
  2113. height: 20,
  2114. name: 'text',
  2115. dataset: true,
  2116. textAlign: 'left',
  2117. realTimes: [
  2118. {
  2119. label: '文字',
  2120. key: 'text',
  2121. type: 'string',
  2122. bind: deepClone(_data),
  2123. },
  2124. ],
  2125. },
  2126. ];
  2127. }
  2128. }
  2129. meta2d.canvas.addCaches = deepClone(data);
  2130. };
  2131. const onAddShapeEnd = () => {
  2132. setTimeout(() => {
  2133. meta2d.initBinds();
  2134. }, 1000);
  2135. };
  2136. let lastIndex = -1;
  2137. const onCheckChange = (e, i) => {
  2138. if (lastIndex > -1) {
  2139. if (e.shiftKey) {
  2140. let start = Math.min(i, lastIndex);
  2141. let end = Math.max(i, lastIndex);
  2142. data.dataset?.devices?.forEach((item, index) => {
  2143. if (index >= start && index <= end) {
  2144. item.checked = true;
  2145. }
  2146. });
  2147. e.stopPropagation();
  2148. e.preventDefault();
  2149. }
  2150. }
  2151. lastIndex = i;
  2152. };
  2153. const checkChange = (e, device, i) => {
  2154. data.checkAll && (device.checked = !device.checked);
  2155. if (lastIndex > -1) {
  2156. if (e.shiftKey) {
  2157. let start = Math.min(i, lastIndex);
  2158. let end = Math.max(i, lastIndex);
  2159. data.dataset?.devices?.forEach((item, index) => {
  2160. if (index >= start && index <= end) {
  2161. item.checked = true;
  2162. }
  2163. });
  2164. }
  2165. }
  2166. lastIndex = i;
  2167. };
  2168. const onAddmock = (item: any) => {
  2169. if (data.dataMocks.find((elem) => elem.id === item.id)) {
  2170. return;
  2171. }
  2172. item.name = item.label;
  2173. data.dataMocks.push(deepClone(item));
  2174. (meta2d.store.data as any).dataMocks = data.dataMocks;
  2175. meta2d.startDataMock();
  2176. };
  2177. const onAddAllMock = () => {
  2178. if (!data.dataset.devices) {
  2179. return;
  2180. }
  2181. data.dataset.devices.forEach((item) => {
  2182. if (data.dataMocks.find((elem) => elem.id === item.id)) {
  2183. return;
  2184. }
  2185. item.name = item.label;
  2186. data.dataMocks.push(deepClone(item));
  2187. });
  2188. (meta2d.store.data as any).dataMocks = data.dataMocks;
  2189. meta2d.startDataMock();
  2190. };
  2191. const deleteMock = (index: number) => {
  2192. data.dataMocks.splice(index, 1);
  2193. (meta2d.store.data as any).dataMocks = data.dataMocks;
  2194. setDot(true);
  2195. };
  2196. </script>
  2197. <style lang="postcss" scoped>
  2198. /* :deep(.data-input-box){
  2199. position: absolute;
  2200. left: 193px;
  2201. width: 501px;
  2202. top: 80px;
  2203. } */
  2204. .content {
  2205. background: var(--color-background-active);
  2206. padding: 16px;
  2207. .tree-icon {
  2208. width: 14px;
  2209. height: 14px;
  2210. }
  2211. .prop-title {
  2212. white-space: nowrap;
  2213. overflow: hidden;
  2214. text-overflow: ellipsis;
  2215. }
  2216. :deep(.t-collapse) {
  2217. border: none;
  2218. }
  2219. :deep(.t-collapse-panel__header) {
  2220. border: none;
  2221. font-size: 12px;
  2222. font-weight: 400;
  2223. padding: 8px 0px;
  2224. background: var(--color-background-active);
  2225. }
  2226. :deep(.t-collapse-panel__body) {
  2227. border: none;
  2228. }
  2229. :deep(.t-collapse-panel__content) {
  2230. background-color: var(--color-background-active);
  2231. color: var(--color);
  2232. padding: 8px 0px;
  2233. }
  2234. .btn-select {
  2235. height: 32px;
  2236. line-height: 32px;
  2237. color: #fff;
  2238. border: 1px solid var(--color-border);
  2239. border-radius: 4px;
  2240. padding: 0px 8px;
  2241. background-color: var(--color-background-input);
  2242. cursor: pointer;
  2243. }
  2244. .btn-select-active {
  2245. background-color: var(--color-primary);
  2246. }
  2247. .input-search {
  2248. flex-shrink: 0;
  2249. color: #878f9c;
  2250. padding: 0px;
  2251. margin-top: 10px;
  2252. .btn {
  2253. background: #fff0;
  2254. left: 12px;
  2255. img {
  2256. background-color: #fff0;
  2257. margin-top: 9px;
  2258. }
  2259. }
  2260. :deep(.t-select) {
  2261. .t-input {
  2262. padding-left: 8px !important;
  2263. }
  2264. }
  2265. }
  2266. .select-search {
  2267. .t-input {
  2268. padding-left: 8px !important;
  2269. .t-icon {
  2270. font-size: 12px;
  2271. }
  2272. }
  2273. }
  2274. .title {
  2275. height: 32px;
  2276. line-height: 32px;
  2277. }
  2278. .title-span {
  2279. display: inline-block;
  2280. margin-left: 4px;
  2281. max-width: 108px;
  2282. overflow: hidden;
  2283. white-space: nowrap;
  2284. text-overflow: ellipsis;
  2285. }
  2286. .data-list {
  2287. /* padding: 12px 6px; */
  2288. /* background: var(--color-background-active); */
  2289. max-height: calc(100vh - 200px);
  2290. margin-bottom: 24px;
  2291. overflow-y: auto;
  2292. .data-title {
  2293. /* padding: 4px; */
  2294. }
  2295. .data-body {
  2296. margin-bottom: 8px;
  2297. }
  2298. .data-title-hover {
  2299. &:hover {
  2300. background: var(--color-background-input);
  2301. border-radius: 4px;
  2302. }
  2303. }
  2304. width: 234px;
  2305. & > div {
  2306. width: 218px;
  2307. }
  2308. }
  2309. .data-mock-list {
  2310. height: auto;
  2311. max-height: calc(100vh - 220px);
  2312. }
  2313. .icon-box {
  2314. width: 24px;
  2315. height: 24px;
  2316. margin: 4px;
  2317. text-align: center;
  2318. line-height: 24px;
  2319. border-radius: 4px;
  2320. &:hover {
  2321. background: var(--td-brand-color-light);
  2322. }
  2323. .t-icon {
  2324. width: 14px;
  2325. height: 14px;
  2326. }
  2327. }
  2328. .icon-item-box {
  2329. height: 30px;
  2330. line-height: 30px;
  2331. margin: 1px;
  2332. }
  2333. .nodata {
  2334. padding-top: 50px;
  2335. img {
  2336. width: 80px;
  2337. }
  2338. .gray {
  2339. margin-top: 8px;
  2340. }
  2341. .t-button {
  2342. height: 24px;
  2343. padding-left: 8px;
  2344. padding-right: 8px;
  2345. }
  2346. }
  2347. .form-line {
  2348. margin-left: 12px;
  2349. width: 11px;
  2350. /* height: 120px; */
  2351. /* border-bottom: 1px solid var(--color-background-input); */
  2352. /* border-left: 1px solid var(--color-background-input); */
  2353. }
  2354. .form-data-item {
  2355. margin-left: 4px;
  2356. margin-top: 4px;
  2357. /* margin-right: 12px; */
  2358. label {
  2359. /* color: #ffffff80; */
  2360. height: 24px;
  2361. line-height: 24px;
  2362. width: 60px;
  2363. }
  2364. & > div {
  2365. width: 118px;
  2366. height: 24px;
  2367. line-height: 24px;
  2368. /* background: var(--color-background-input); */
  2369. border-radius: 4px;
  2370. /* color: #e3e8f459; */
  2371. padding-left: 8px;
  2372. padding-right: 8px;
  2373. overflow: hidden;
  2374. white-space: nowrap;
  2375. text-overflow: ellipsis;
  2376. }
  2377. }
  2378. .form-mock-item {
  2379. & > div {
  2380. padding: 0px;
  2381. :deep(.t-input) {
  2382. height: 24px !important;
  2383. border: 0px;
  2384. }
  2385. }
  2386. }
  2387. .form-swicth-item {
  2388. margin-left: 4px;
  2389. label {
  2390. /* color: #ffffff80; */
  2391. height: 24px;
  2392. width: 56px;
  2393. }
  2394. }
  2395. .data-code {
  2396. width: 218px;
  2397. .code-editor {
  2398. height: 126px;
  2399. }
  2400. }
  2401. .data-code-full {
  2402. position: absolute;
  2403. /* right: calc(50% - 250px); */
  2404. width: 100%;
  2405. height: 100vh;
  2406. background: var(--td-mask-active);
  2407. right: 0px;
  2408. top: 0px;
  2409. .code-editor {
  2410. width: 800px;
  2411. margin: 0 auto;
  2412. height: 80%;
  2413. margin-top: 16vh;
  2414. }
  2415. .data-full-icon {
  2416. .t-icon {
  2417. margin-right: calc(50% - 384px);
  2418. }
  2419. }
  2420. }
  2421. .data-full-icon {
  2422. margin-top: -20px;
  2423. /* z-index: 10000; */
  2424. position: relative;
  2425. display: flex;
  2426. justify-content: flex-end;
  2427. width: 100%;
  2428. .t-icon {
  2429. margin-right: 16px;
  2430. cursor: pointer;
  2431. height: 16px;
  2432. width: 16px;
  2433. }
  2434. }
  2435. ul {
  2436. list-style-type: none;
  2437. }
  2438. }
  2439. :deep(.t-tree) {
  2440. .t-tree__label {
  2441. font-size: 12px;
  2442. color: #bdc7db;
  2443. }
  2444. .t-tree__item[data-level='1'] {
  2445. padding: 0 0 0 16px;
  2446. .t-checkbox__label {
  2447. /* max-width: 50px; */
  2448. overflow: hidden;
  2449. white-space: nowrap;
  2450. text-overflow: ellipsis;
  2451. }
  2452. }
  2453. .t-tree__item[data-level='2'] {
  2454. padding: 0 0 0 16px;
  2455. .t-checkbox__label {
  2456. max-width: 50px;
  2457. overflow: hidden;
  2458. white-space: nowrap;
  2459. text-overflow: ellipsis;
  2460. }
  2461. }
  2462. .t-tree__item .t-icon {
  2463. color: #bdc7db;
  2464. }
  2465. .t-tree__label.t-is-checked {
  2466. background: none;
  2467. }
  2468. }
  2469. </style>
  2470. <style lang="postcss">
  2471. .menu-item-a {
  2472. &:hover {
  2473. color: var(--td-brand-color);
  2474. }
  2475. }
  2476. .network-option {
  2477. height: 100%;
  2478. padding: 0px;
  2479. }
  2480. </style>