DataSource.vue 71 KB

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