index.vue 66 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791
  1. <template>
  2. <div>
  3. <!-- <el-button type="primary" @click="saveFlow">测试</el-button> -->
  4. <div style="display: flex;margin-left: 1%;padding-top: 0.5%;position: absolute;z-index: 1000;justify-content: space-between;width: 98%;align-items: center;">
  5. <el-icon size="large" style="cursor: pointer" @click="back"><Back /></el-icon>
  6. <el-button @click="delWholeFlow" style="margin-left: auto;" type="danger" size="mini">删除</el-button>
  7. <!-- <el-button style="margin-left: 1%;" type="info" size="mini" plain @click="toImage">导出为图片</el-button> -->
  8. <el-button style="margin-left: 1%;" type="primary" size="mini" @click="saveFlow">保存</el-button>
  9. <el-button style="margin-left: 1%;" type="primary" size="mini" @click="startTest">试运行</el-button>
  10. </div>
  11. <div v-if="nodeDeSer" style="height: 82vh;overflow-y: auto;width: 20vw;position: absolute;float: right;z-index: 1000;right: 1%;top:6%;border: 0.1px solid #dedfe0;border-radius: 6px;background-color: white;">
  12. <div style="display: flex;margin-left: 3%;margin-top: 3%;align-items: center;justify-content: space-between;width: 95%;">
  13. <el-tag class="ml-2" style="" type="warning">服务</el-tag>
  14. <div style="margin-left: 4%;">
  15. {{servieName}}
  16. </div>
  17. <el-icon @click="closeDe" style="margin-left: auto;cursor: pointer;"><Close /></el-icon>
  18. </div>
  19. <div>
  20. <div style="display: flex;width: 90%;margin-left: 5%;margin-top:10%;align-items: center;justify-content: space-between;">
  21. <el-input v-model="serviceUrl" style="width: 90%;" placeholder="Please input" disabled>
  22. <template #prepend>
  23. <div v-if="serviceRqtype==='GET'" style="color: #67C23A;background-color: transparent;">
  24. GET
  25. </div>
  26. <div v-if="serviceRqtype==='POST'" style="color: #409EFF;background-color: transparent;">
  27. POST
  28. </div>
  29. </template>
  30. </el-input>
  31. <svg-icon @click="testOne" icon-class="startTest" style="margin-left: auto;width: 50px;height: 25px;cursor: pointer;"/>
  32. </div>
  33. <div style="display: flex;width: 90%;margin-left: 5%;margin-top:10%;align-items: center;justify-content: space-between;">
  34. <el-table :data="tableDataCan" border style="width: 100%">
  35. <el-table-column prop="paramCode" label="参数名" width="" />
  36. <el-table-column prop="paramValue" label="参数值" width="">
  37. <template #default="scope">
  38. <div style="width:100%;">
  39. <el-select
  40. v-model="scope.row.paramValue"
  41. filterable
  42. @change="handleSelectChange"
  43. allow-create
  44. default-first-option
  45. :reserve-keyword="false"
  46. style="width: 100%"
  47. >
  48. <el-option
  49. v-for="item in optionsCan"
  50. :label="item.name"
  51. :value="item.ref"
  52. />
  53. </el-select>
  54. </div>
  55. </template>
  56. </el-table-column>
  57. </el-table>
  58. </div>
  59. <div style="display: flex;width: 90%;margin-left: 5%;margin-top:10%;align-items: center;justify-content: space-between;">
  60. <el-form ref="deptRef" :model="form" label-width="80px" label-position="top">
  61. <el-form-item label="BODY">
  62. <el-radio-group v-model="form.bodyType">
  63. <el-radio value="none">none</el-radio>
  64. <el-radio value="form-data">form-data</el-radio>
  65. <el-radio value="x-www-form-urlencoded">x-www-form-urlencoded</el-radio>
  66. <el-radio value="JSON">JSON</el-radio>
  67. <el-radio value="raw">raw</el-radio>
  68. </el-radio-group>
  69. </el-form-item>
  70. <el-row :gutter="48">
  71. <el-col :span="12">
  72. <el-form-item label="失败处理:" prop="name" style="">
  73. <div style="display: flex;width: 100%;justify-content: space-between;">
  74. <el-select v-model="form.errorPolicy" style="width: 100%">
  75. <el-option label="报错" value="ABORT"></el-option>
  76. <el-option label="忽视" value="IGNORE"></el-option>
  77. <el-option label="重连" value="RETRY"></el-option>
  78. </el-select>
  79. </div>
  80. </el-form-item>
  81. </el-col>
  82. <el-col :span="12">
  83. <el-form-item label="失败重连次数:" prop="" style="">
  84. <div style="display: flex;width: 100%;justify-content: space-between;">
  85. <el-input-number v-model="form.retryCount" :min="1" style="width: 100%" :max="30"/>
  86. <div style="display: flex;">
  87. </div>
  88. </div>
  89. </el-form-item>
  90. </el-col>
  91. </el-row>
  92. <el-form-item label="输出">
  93. <el-table :data="tableDataCanOut" border style="width: 100%">
  94. <el-table-column prop="paramCode" label="参数名" width="" />
  95. <el-table-column prop="paramValue" label="参数值" width="">
  96. <template #default="scope">
  97. <div style="width: 100%;">
  98. <el-input placeholder="" type="primary" class="noBor" v-model="scope.row.paramValue" size="mini" text style="margin-left: 0%;"></el-input>
  99. </div>
  100. </template>
  101. </el-table-column>
  102. </el-table>
  103. </el-form-item>
  104. </el-form>
  105. </div>
  106. </div>
  107. </div>
  108. <div v-if="nodeStart" style="height: 82vh;overflow-y: auto;width: 40vw;position: absolute;float: right;z-index: 1000;right: 1%;top:6%;border: 0.1px solid #dedfe0;border-radius: 6px;background-color: white;">
  109. <div style="display: flex;margin-left: 3%;margin-top: 3%;align-items: center;justify-content: space-between;width: 95%;">
  110. <el-tag class="ml-2" style="" type="warning">开始节点</el-tag>
  111. <el-icon @click="saveStartNode" style="margin-left: auto;cursor: pointer;"><Close /></el-icon>
  112. </div>
  113. <div>
  114. <el-button @click="addStart" style="margin-top: 5%;margin-left: 1%;" type="success" size="mini" :disabled="isEdit" plain>新增参数</el-button>
  115. <div style="display: flex;width: 98%;margin-left: 1%;margin-top:2%;align-items: center;justify-content: space-between;">
  116. <el-table
  117. style="margin-top: 1%;width: 100%;"
  118. :data="tableDataCanStart"
  119. :cell-style="{ textAlign: 'center',padding:'2px 0' }"
  120. :header-cell-style="{ textAlign: 'center'}"
  121. :row-style="{ height: heightAll*0.01+'px',fontSize: '16px',textAlign:'center' }"
  122. border >
  123. <el-table-column prop="itemName" label="参数名称(必填)">
  124. <template #default="scope">
  125. <div style="width: 100%;">
  126. <el-input placeholder="请填写参数名称" type="primary" class="noBor" v-model="scope.row.name" size="mini" text style="margin-left: 0%;"></el-input>
  127. </div>
  128. </template>
  129. </el-table-column>
  130. <el-table-column prop="itemName" label="参数类型(必填)" width="200">
  131. <template #default="scope">
  132. <div style="width: 100%;">
  133. <el-select
  134. v-model="scope.row.dataType"
  135. class="noBorSel"
  136. placeholder=""
  137. style="width: 100%;margin-left: 0%;"
  138. >
  139. <el-option
  140. v-for="item in optionsType"
  141. :key="item.value"
  142. :label="item.label"
  143. :value="item.value"
  144. />
  145. </el-select>
  146. </div>
  147. </template>
  148. </el-table-column>
  149. <el-table-column prop="itemName" label="参数说明" >
  150. <template #default="scope">
  151. <div style="width: 100%;">
  152. <el-input type="primary" class="noBor" v-model="scope.row.description" size="mini" text style="margin-left: 0%;"></el-input>
  153. </div>
  154. </template>
  155. </el-table-column>
  156. <el-table-column prop="itemName" label="是否必填" >
  157. <template #default="scope">
  158. <div style="width: 100%;">
  159. <el-checkbox v-model="scope.row.required" size="large" />
  160. </div>
  161. </template>
  162. </el-table-column>
  163. <el-table-column prop="itemName" label="参数值" >
  164. <template #default="scope">
  165. <div style="width: 100%;">
  166. <el-input type="primary" class="noBor" v-model="scope.row.paramValue" size="mini" text style="margin-left: 0%;"></el-input>
  167. </div>
  168. </template>
  169. </el-table-column>
  170. <el-table-column prop="address" label="操作" width="100">
  171. <template #default="scope">
  172. <div style="width: 100%;">
  173. <el-button type="danger" @click="delCanStart(scope.$index)" size="mini" text style="margin-left: 0%;">删除</el-button>
  174. </div>
  175. </template>
  176. </el-table-column>
  177. </el-table>
  178. </div>
  179. </div>
  180. </div>
  181. <div v-show="nodeEnd" style="height: 82vh;overflow-y: auto;width: 25vw;position: absolute;float: right;z-index: 1000;right: 1%;top:6%;border: 0.1px solid #dedfe0;border-radius: 6px;background-color: white;">
  182. <div style="display: flex;margin-left: 3%;margin-top: 3%;align-items: center;justify-content: space-between;width: 95%;">
  183. <el-tag class="ml-2" style="" type="warning">结束节点</el-tag>
  184. <el-icon @click="saveEndNode" style="margin-left: auto;cursor: pointer;"><Close /></el-icon>
  185. </div>
  186. <div>
  187. <el-button @click="addEnd" style="margin-top: 5%;margin-left: 1%;" type="success" size="mini" :disabled="isEdit" plain>新增参数</el-button>
  188. <div style="display: flex;width: 98%;margin-left: 1%;margin-top:2%;align-items: center;justify-content: space-between;">
  189. <el-table :data="tableDataCanEnd" border style="width: 100%">
  190. <el-table-column prop="paramCode" label="参数名(必填)" width="">
  191. <template #default="scope">
  192. <div style="width: 100%;">
  193. <el-input placeholder="" type="primary" class="noBor" v-model="scope.row.name" size="mini" text style="margin-left: 0%;"></el-input>
  194. </div>
  195. </template>
  196. </el-table-column>
  197. <el-table-column prop="itemName" label="参数类型(必填)">
  198. <template #default="scope">
  199. <div style="width: 100%;">
  200. <el-select
  201. v-model="scope.row.dataType"
  202. class="noBorSel"
  203. placeholder=""
  204. style="width: 100%;margin-left: 0%;"
  205. >
  206. <el-option
  207. v-for="item in optionsType"
  208. :key="item.value"
  209. :label="item.label"
  210. :value="item.value"
  211. />
  212. </el-select>
  213. </div>
  214. </template>
  215. </el-table-column>
  216. <el-table-column prop="paramValue" label="参数值" width="">
  217. <template #default="scope">
  218. <div style="width:100%;">
  219. <el-select
  220. v-model="scope.row.paramValue"
  221. filterable
  222. default-first-option
  223. :reserve-keyword="false"
  224. style="width: 100%"
  225. >
  226. <el-option
  227. v-for="item in optionsCan"
  228. :label="item.name"
  229. :value="item.ref"
  230. />
  231. </el-select>
  232. </div>
  233. </template>
  234. </el-table-column>
  235. <el-table-column prop="address" label="操作" width="100">
  236. <template #default="scope">
  237. <div style="width: 100%;">
  238. <el-button type="danger" @click="delEnd(scope.$index)" size="mini" text style="margin-left: 0%;">删除</el-button>
  239. </div>
  240. </template>
  241. </el-table-column>
  242. </el-table>
  243. </div>
  244. </div>
  245. </div>
  246. <div style="display: flex;height: 87vh;width: 100%;padding-top:2%;justify-items: center;">
  247. <div style="width: 20%;margin-left: 1%;overflow-y: auto;margin-top: 0.5%;">
  248. <el-checkbox-group v-model="checkboxGroup1" size="" style="margin-left: 5%;" @change="oneSel">
  249. <el-checkbox-button key="service" label="service">
  250. 服务
  251. </el-checkbox-button>
  252. <el-checkbox-button key="tool" label="tool">
  253. 工具
  254. </el-checkbox-button>
  255. </el-checkbox-group>
  256. <el-input
  257. v-if="checkboxGroup1.includes('service')"
  258. v-model="inputNode"
  259. style="width:90%;margin-left: 5%;background-color: #ebeef5;margin-top: 1%;"
  260. class="w-50 m-2"
  261. :prefix-icon="Search"
  262. />
  263. <el-tree v-if="checkboxGroup1.includes('service')" :expand-on-click-node="false" ref="treeRef" :filter-node-method="filterNode" :current-node-key="currentNodeKey" class="treeLeft" :data="dataTree"
  264. @node-click="handleNodeClick" node-key="id" style="margin-left: 5%;margin-top: 3%;width: 90%;background-color: transparent;" default-expand-all :key="valueKet">
  265. <template #default="{ node, data }">
  266. <span style="justify-content: space-between;display: flex;width: 100%;align-items: center;" :draggable="true" @dragstart="onDragStart($event,data)">
  267. <div class="custom-tree-node">
  268. <!-- <el-tag v-if="data.nodeType=='MODEL'" class="ml-2" type="warning">模型</el-tag> -->
  269. <svg-icon icon-class="model2" style="color: #eebe77;" v-if="data.nodeType=='MODEL'"/>
  270. <!-- <el-tag class="ml-2">
  271. 服务
  272. </el-tag> -->
  273. <svg-icon icon-class="model" dstyle="color: #13E03B;" v-if="data.nodeType=='SERVICE'"/>
  274. <svg-icon svg-icon icon-class="cate" style="color: red;" v-if="data.nodeType=='TREE'"/>
  275. <span>{{ node.label }}</span>
  276. </div>
  277. </span>
  278. </template>
  279. </el-tree>
  280. <el-tree v-if="!checkboxGroup1.includes('service')" :expand-on-click-node="false" ref="treeRef" :current-node-key="currentNodeKey" class="treeLeft" :data="dataTreeTool"
  281. @node-click="handleNodeClick" node-key="id" style="margin-left: 5%;margin-top: 3%;width: 90%;background-color: transparent;" default-expand-all :key="valueKet">
  282. <template #default="{ node, data }">
  283. <span style="justify-content: space-between;display: flex;width: 120%;align-items: center;" :draggable="true" @dragstart="onDragStart($event,data)">
  284. <div class="custom-tree-node">
  285. <svg-icon icon-class="liuchengshuru" style="color: #eebe77;" v-if="data.label=='输入'"/>
  286. <svg-icon icon-class="liuchengEx" dstyle="color: #13E03B;" v-if="data.label=='Excel文件输入组件'"/>
  287. <svg-icon icon-class="liuchengbiao" dstyle="color: #13E03B;" v-if="data.label=='表输入组件'"/>
  288. <svg-icon icon-class="liuchengcsv" dstyle="color: #13E03B;" v-if="data.label=='csv输入组件'"/>
  289. <svg-icon icon-class="lczh" dstyle="color: #13E03B;" v-if="data.label=='转换'"/>
  290. <svg-icon icon-class="lczhzj" dstyle="color: #13E03B;" v-if="data.label=='转换组件'"/>
  291. <svg-icon icon-class="lcpx" dstyle="color: #13E03B;" v-if="data.label=='排序记录'"/>
  292. <svg-icon icon-class="lczd" dstyle="color: #13E03B;" v-if="data.label=='字段派生器'"/>
  293. <svg-icon icon-class="lcsc" dstyle="color: #13E03B;" v-if="data.label=='输出'"/>
  294. <svg-icon icon-class="lcbsc" dstyle="color: #13E03B;" v-if="data.label=='表输出组件'"/>
  295. <span style="margin-left: 2%;">&nbsp;{{ node.label }}</span>
  296. </div>
  297. </span>
  298. </template>
  299. </el-tree>
  300. </div>
  301. <div ref="flowContainer" style="width: 80%;">
  302. <VueFlow ref="vueFlowRef" style="background-color: #EFEFF4;margin-top: 0.5%;" :nodes="nodes" :viewport="zoom" :edges="edges" @drop="onDrop" @dragover="onDragOver" @dragleave="onDragLeave"
  303. @node-click="onNodeClick" @connect="onConnect" fit-view-on-init>
  304. <Controls :showInteractive="false" :showFitView="false"/>
  305. <template #node-special="specialNodeProps">
  306. <div v-if="specialNodeProps.data.nodeType=='tool'" class="vue-flow__node-default" style="border: 0.2px solid #c8c9cc;border-radius: 6px;min-height: 6vh;min-width: 11vw">
  307. <div style='width:100%;font-size:15px;display:flex;align-items:center;height: 3vh;margin-top: 2%;'>
  308. <div style="margin-left: 0%;font-weight: 500;">
  309. <svg-icon icon-class="liuchengshuru" style="color: #eebe77;height:20px;width:20px;" v-if="specialNodeProps.data.label=='输入'"/>
  310. <svg-icon icon-class="liuchengEx" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='Excel文件输入组件'"/>
  311. <svg-icon icon-class="liuchengbiao" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='表输入组件'"/>
  312. <svg-icon icon-class="liuchengcsv" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='csv输入组件'"/>
  313. <svg-icon icon-class="lczh" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='转换'"/>
  314. <svg-icon icon-class="lczhzj" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='转换组件'"/>
  315. <svg-icon icon-class="lcpx" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='排序记录'"/>
  316. <svg-icon icon-class="lczd" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='字段派生器'"/>
  317. <svg-icon icon-class="lcsc" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='输出'"/>
  318. <svg-icon icon-class="lcbsc" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='表输出组件'"/>
  319. </div>
  320. <div style="margin-left: 10%;">
  321. <el-input class="custom-no-border" @click.stop="handleInputClick" type="primary" v-model="specialNodeProps.data.label" size="mini" text
  322. style="margin-left: -20%;font-size: 15px;height: 15px;width: 100%;" ></el-input>
  323. </div>
  324. </div>
  325. <Handle type="source" :position="Position.Right"/>
  326. <Handle type="target" :position="Position.Left"/>
  327. </div>
  328. <div v-if="specialNodeProps.data.label=='开始'" class="vue-flow__node-default" style="border: 0.2px solid #c8c9cc;border-radius: 6px;min-height: 6vh;min-width: 11vw">
  329. <div style='width:100%;font-size:10px;display:flex;align-items:flex-end;height: 10px;margin-top: 2%;'>
  330. <img style="width: 15px;height:15px;border-radius: 12px;" src="@/assets/images/icon-Start-v2.jpg" alt="">
  331. <div style="margin-left: 3%;font-weight: 500;">
  332. 开始
  333. </div>
  334. <el-tag class="ml-2" style="width: 30px;height: 15px;font-size: 7px;margin-left: 6%;" type="info">触发器</el-tag>
  335. </div>
  336. <Handle type="source" :position="Position.Right"/>
  337. </div>
  338. <div v-if="specialNodeProps.data.label=='结束'" class="vue-flow__node-default" style="border: 0.2px solid #c8c9cc;border-radius: 6px;min-height: 6vh;min-width: 11vw">
  339. <div style='width:100%;font-size:10px;display:flex;align-items:flex-end;height: 10px;margin-top: 2%;'>
  340. <img style="width: 15px;height:15px;border-radius: 12px;" src="@/assets/images/icon-Start-v2.jpg" alt="">
  341. <div style="margin-left: 3%;font-weight: 500;">
  342. 结束
  343. </div>
  344. <div>
  345. </div>
  346. </div>
  347. <Handle type="target" :position="Position.Left"/>
  348. </div>
  349. <div v-if="specialNodeProps.data.nodeType=='SERVICE'"
  350. class="vue-flow__node-default"
  351. v-loading="specialNodeProps.data.loading"
  352. style="border: 0.5px solid #c8c9cc;border-radius: 6px;border-radius: 6px;min-height: 8vh;min-width: 13vw">
  353. <div style='width:100%;font-size:10px;display:flex;align-items:flex-end;height: 10px;margin-top: 2%;justify-content: space-between;'>
  354. <img style="width: 15px;height:15px;border-radius: 12px;" src="@/assets/images/icon-HTTP.png" alt="">
  355. <div style="margin-left:3%;font-weight: 500;">
  356. <el-input class="custom-no-border" placeholder="" type="primary" v-model="specialNodeProps.data.label" size="mini" text
  357. style="margin-left: 0%;font-size: 10px;height: 15px;width: 100%;" ></el-input>
  358. <!-- {{ specialNodeProps.data.label}} -->
  359. </div>
  360. <el-icon v-if="specialNodeProps.data.isSuccess" style="cursor: pointer;margin-left: auto;color: #67C23A;"><SuccessFilled /></el-icon>
  361. <el-tooltip :content="specialNodeProps.data.erMsg" placement="top" effect="light">
  362. <el-icon v-if="specialNodeProps.data.isFail" style="cursor: pointer;margin-left: auto;color: #F56C6C;"><CircleCloseFilled /></el-icon>
  363. </el-tooltip>
  364. <!-- <el-icon @click.stop="testNode(specialNodeProps)" style="cursor: pointer;margin-left: 2%;"><CaretRight /></el-icon> -->
  365. <el-icon @click.stop="delNode(specialNodeProps)" style="cursor: pointer;color: #F56C6C;margin-left: 2%;"><Delete /></el-icon>
  366. </div>
  367. <div style="display: flex;margin-top: 3%;">
  368. <el-tag class="ml-2" style="width: 35px;height: 20px;font-size: 10px;" type="warning">服务</el-tag>
  369. </div>
  370. <div style="display: flex;margin-top: 3%;font-size: 9px;color: #b1b3b8;flex-wrap: wrap;align-items: flex-start;">
  371. <div style="word-break: break-all;min-width: 0;width: 100%;text-align: left;">
  372. {{ specialNodeProps.data.service.rqtype+ ':' }}{{ specialNodeProps.data.service.url }}
  373. </div>
  374. </div>
  375. <div style="display: flex;margin-top: 3%;font-size: 9px;color: #b1b3b8;align-items: center;">
  376. <div>
  377. 说明:
  378. </div>
  379. <div>
  380. {{ specialNodeProps.data.service.intro }}
  381. </div>
  382. </div>
  383. <Handle type="source" :position="Position.Right"/>
  384. <Handle type="target" :position="Position.Left"/>
  385. </div>
  386. </template>
  387. <template #edge-custom="specialEdgeProps">
  388. <div style="height: 1px;color: red;">
  389. </div>
  390. </template>
  391. </VueFlow>
  392. </div>
  393. <el-dialog @close="clearFromLev" title="" v-model="dialogVisibleBiao" width="50%" destroy-on-close :key="tableKey">
  394. <el-form size="mini" :key="tableKey" style="margin-top: 1%;width: 98%;" :model="formJi" label-position="right" ref="formRefJi" label-width="120px" :rules="rulesJi">
  395. <el-form-item label="连接方式:" prop="" style="display: flex; align-items: center;">
  396. <el-radio-group v-model="formJi.rptype" class="ml-4" style="display: inline-flex; align-items: center;">
  397. <el-radio label="1" size="large" style="display: inline-flex; align-items: center;">
  398. <span style="position: relative; top: -1px">资产表</span>
  399. </el-radio>
  400. <el-radio label="2" size="large" style="display: inline-flex; align-items: center;">
  401. <span style="position: relative; top: -1px">sql</span>
  402. </el-radio>
  403. <el-radio label="3" size="large" style="display: inline-flex; align-items: center;">
  404. <span style="position: relative; top: -1px">数据连接</span>
  405. </el-radio>
  406. </el-radio-group>
  407. </el-form-item>
  408. <el-form-item label="数据源:" prop="" style="">
  409. <div style="display: flex;width: 30%;justify-content: space-between;">
  410. <el-select
  411. v-model="formJi.type"
  412. style="width: 100%;margin-left: 0%;"
  413. >
  414. <el-option
  415. v-for="item in optionsType"
  416. :key="item.value"
  417. :label="item.label"
  418. :value="item.value"
  419. />
  420. </el-select>
  421. </div>
  422. </el-form-item>
  423. <el-form-item label="读取模式:" prop="" style="display: flex; align-items: center;">
  424. <el-radio-group v-model="formJi.rptype" class="ml-4" style="display: inline-flex; align-items: center;">
  425. <el-radio label="1" size="large" style="display: inline-flex; align-items: center;">
  426. <span style="position: relative; top: -1px">全量</span>
  427. </el-radio>
  428. <el-radio label="2" size="large" style="display: inline-flex; align-items: center;">
  429. <span style="position: relative; top: -1px">id增量</span>
  430. </el-radio>
  431. <el-radio label="3" size="large" style="display: inline-flex; align-items: center;">
  432. <span style="position: relative; top: -1px">时间范围增量</span>
  433. </el-radio>
  434. </el-radio-group>
  435. </el-form-item>
  436. <el-form-item label="where条件:">
  437. <el-input v-model="formJi.intro" style="width: 100%;" :rows="3" resize="none" type="textarea"/>
  438. </el-form-item>
  439. </el-form>
  440. <div style="font-size: 16px;margin-left: 1%">
  441. 属性字段
  442. </div>
  443. <el-table
  444. style="margin-top: 2%;width: 100%;margin-left: 1%;overflow: auto;"
  445. :data="tableDataCan"
  446. :cell-style="{ textAlign: 'center',padding:'3px 0px' }"
  447. :header-cell-style="{ textAlign: 'center', }"
  448. max-height="45vh"
  449. :row-style="{ height: heightAll*0.01+'px',fontSize: '16px',textAlign:'center' }"
  450. border>
  451. <el-table-column prop="parName" label="参数英文名">
  452. <template #default="scope" style="width: 120%;">
  453. <el-input v-model="scope.row.parEnname" style="width: 120%;margin-left: -10%;"/>
  454. </template>
  455. </el-table-column>
  456. <el-table-column prop="parName" label="参数名称">
  457. <template #default="scope">
  458. <el-input v-model="scope.row.parName" style="width: 120%;margin-left: -10%;"/>
  459. </template>
  460. </el-table-column>
  461. <el-table-column prop="parType" label="参数类型" >
  462. <template #default="scope">
  463. <el-input v-model="scope.row.parType" style="width: 120%;margin-left: -10%;"/>
  464. </template>
  465. </el-table-column>
  466. <el-table-column prop="parNote" label="操作" width="85">
  467. <template #default="scope">
  468. <el-button type="danger" @click="delCan(scope.$index, scope.row)" text size="mini" style="margin-left: 0%;">删除</el-button>
  469. </template>
  470. </el-table-column>
  471. </el-table>
  472. <template #footer>
  473. <span class="dialog-footer">
  474. <el-button size="mini" @click="dialogVisibleBiao = false">取消</el-button>
  475. <el-button type="primary" @click="saveAdd1Level" size="mini" v-if="show1Lev">
  476. 提交
  477. </el-button>
  478. <el-button type="primary" @click="saveAddNextLevel" size="mini" v-if="!show1Lev">
  479. 提交
  480. </el-button>
  481. </span>
  482. </template>
  483. </el-dialog>
  484. <el-dialog
  485. :title="title"
  486. v-model="dialogVisibleExcel"
  487. width="50%">
  488. <div>
  489. <el-form ref="formAddref" :model="formAdd" label-width="100px" class="coz-mg-card" :rules="rulesAdd">
  490. <el-row :gutter="24">
  491. <el-col :span="8">
  492. <el-form-item label="选择附件:">
  493. <el-upload
  494. ref="uploadRef"
  495. style="margin-top: 0%;"
  496. :limit="1"
  497. :show-file-list="false"
  498. :file-list="fileList"
  499. :headers="upload.headers"
  500. :on-progress="handlepro"
  501. :on-success="handleFileSuccess"
  502. :on-change="handleChange"
  503. :action="upload.url + '?file=' + upload.updateSupport"
  504. :auto-upload="false">
  505. <el-button @click="clearDefault" plain type="primary" size="mini" style="margin-left:auto;width: 80px;" :icon="Upload">上传文件</el-button>
  506. </el-upload>
  507. </el-form-item>
  508. </el-col>
  509. <el-col :span="8">
  510. <el-form-item label="起始行:" prop="appTitle">
  511. <el-input-number
  512. v-model="num"
  513. class="mx-4"
  514. :min="1"
  515. controls-position="right"
  516. @change="handleChange"
  517. />
  518. </el-form-item>
  519. </el-col>
  520. <el-col :span="8">
  521. <el-form-item label="起始列:" prop="appNote">
  522. <el-input-number
  523. v-model="num"
  524. class="mx-4"
  525. :min="1"
  526. controls-position="right"
  527. @change="handleChange"
  528. />
  529. </el-form-item>
  530. </el-col>
  531. </el-row>
  532. </el-form>
  533. </div>
  534. <div style="font-size: 16px;margin-left: 1%">
  535. 属性字段
  536. </div>
  537. <el-table
  538. style="margin-top: 2%;width: 100%;margin-left: 1%;overflow: auto;"
  539. :data="tableDataCan"
  540. :cell-style="{ textAlign: 'center',padding:'3px 0px' }"
  541. :header-cell-style="{ textAlign: 'center', }"
  542. max-height="45vh"
  543. :row-style="{ height: heightAll*0.01+'px',fontSize: '16px',textAlign:'center' }"
  544. border>
  545. <el-table-column prop="parName" label="参数英文名">
  546. <template #default="scope" style="width: 120%;">
  547. <el-input v-model="scope.row.parEnname" style="width: 120%;margin-left: -10%;"/>
  548. </template>
  549. </el-table-column>
  550. <el-table-column prop="parName" label="参数名称">
  551. <template #default="scope">
  552. <el-input v-model="scope.row.parName" style="width: 120%;margin-left: -10%;"/>
  553. </template>
  554. </el-table-column>
  555. <el-table-column prop="parType" label="参数类型" >
  556. <template #default="scope">
  557. <el-input v-model="scope.row.parType" style="width: 120%;margin-left: -10%;"/>
  558. </template>
  559. </el-table-column>
  560. <el-table-column prop="parNote" label="操作" width="85">
  561. <template #default="scope">
  562. <el-button type="danger" @click="delCan(scope.$index, scope.row)" text size="mini" style="margin-left: 0%;">删除</el-button>
  563. </template>
  564. </el-table-column>
  565. </el-table>
  566. <template #footer>
  567. <el-button @click="dialogVisibleExcel = false">取消</el-button>
  568. <el-button v-if="isAdd" type="primary" @click="submitAdd">确定</el-button>
  569. <el-button v-if="!isAdd" type="primary" @click="subEdit">确定</el-button>
  570. </template>
  571. </el-dialog>
  572. <el-dialog @close="clearFromLev" title="" v-model="dialogVisibleCsv" width="50%" destroy-on-close :key="tableKey">
  573. <el-form ref="formAddref" :model="formAdd" label-width="100px" class="coz-mg-card" :rules="rulesAdd">
  574. <el-row :gutter="24">
  575. <el-col :span="8">
  576. <el-form-item label="上传附件:">
  577. <el-upload
  578. ref="uploadRef"
  579. style="margin-top: 0%;"
  580. :limit="1"
  581. :show-file-list="false"
  582. :file-list="fileList"
  583. :headers="upload.headers"
  584. :on-progress="handlepro"
  585. :on-success="handleFileSuccess"
  586. :on-change="handleChange"
  587. :action="upload.url + '?file=' + upload.updateSupport"
  588. :auto-upload="false">
  589. <el-button @click="clearDefault" plain type="primary" size="mini" style="margin-left:auto;width: 80px;" :icon="Upload">选择文件</el-button>
  590. </el-upload>
  591. <el-button @click="clearDefault" plain type="primary" size="mini" style="margin-left:auto;width: 80px;" :icon="Upload">解析文件</el-button>
  592. </el-form-item>
  593. </el-col>
  594. <el-col :span="8">
  595. <el-form-item label="" prop="">
  596. </el-form-item>
  597. </el-col>
  598. <el-col :span="8">
  599. </el-col>
  600. </el-row>
  601. </el-form>
  602. <div style="font-size: 16px;margin-left: 1%">
  603. 属性字段
  604. </div>
  605. <el-table
  606. style="margin-top: 2%;width: 100%;margin-left: 1%;overflow: auto;"
  607. :data="tableDataCan"
  608. :cell-style="{ textAlign: 'center',padding:'3px 0px' }"
  609. :header-cell-style="{ textAlign: 'center', }"
  610. max-height="45vh"
  611. :row-style="{ height: heightAll*0.01+'px',fontSize: '16px',textAlign:'center' }"
  612. border>
  613. <el-table-column prop="parName" label="参数英文名">
  614. <template #default="scope" style="width: 120%;">
  615. <el-input v-model="scope.row.parEnname" style="width: 120%;margin-left: -10%;"/>
  616. </template>
  617. </el-table-column>
  618. <el-table-column prop="parName" label="参数名称">
  619. <template #default="scope">
  620. <el-input v-model="scope.row.parName" style="width: 120%;margin-left: -10%;"/>
  621. </template>
  622. </el-table-column>
  623. <el-table-column prop="parType" label="参数类型" >
  624. <template #default="scope">
  625. <el-input v-model="scope.row.parType" style="width: 120%;margin-left: -10%;"/>
  626. </template>
  627. </el-table-column>
  628. <el-table-column prop="parNote" label="操作" width="85">
  629. <template #default="scope">
  630. <el-button type="danger" @click="delCan(scope.$index, scope.row)" text size="mini" style="margin-left: 0%;">删除</el-button>
  631. </template>
  632. </el-table-column>
  633. </el-table>
  634. <template #footer>
  635. <span class="dialog-footer">
  636. <el-button size="mini" @click="dialogVisibleBiao = false">取消</el-button>
  637. <el-button type="primary" @click="saveAdd1Level" size="mini" v-if="show1Lev">
  638. 提交
  639. </el-button>
  640. <el-button type="primary" @click="saveAddNextLevel" size="mini" v-if="!show1Lev">
  641. 提交
  642. </el-button>
  643. </span>
  644. </template>
  645. </el-dialog>
  646. <el-dialog @close="clearFromLev" :title="titleTest" v-model="dialogVisibleTest" width="50%" destroy-on-close :key="tableKey">
  647. <el-form ref="formAddref" :model="formAdd" label-width="100px" class="coz-mg-card" :rules="rulesAdd">
  648. <el-form-item label="输入测试值:">
  649. <el-input v-model="formJi.intro" :placeholder="testAttention" style="width: 100%;" :rows="15" resize="none" type="textarea"/>
  650. </el-form-item>
  651. </el-form>
  652. <div style="font-size: 16px;margin-left: 1%">
  653. 属性字段
  654. </div>
  655. <template #footer>
  656. <span class="dialog-footer">
  657. <el-button size="mini" @click="dialogVisibleBiao = false">取消</el-button>
  658. <el-button type="primary" @click="saveAdd1Level" size="mini" v-if="show1Lev">
  659. 提交
  660. </el-button>
  661. <el-button type="primary" @click="saveAddNextLevel" size="mini" v-if="!show1Lev">
  662. 提交
  663. </el-button>
  664. </span>
  665. </template>
  666. </el-dialog>
  667. <el-dialog @close="" title="试运行结果" v-model="dialogVisibleSart" width="50%" destroy-on-close :key="tableKey">
  668. <el-input
  669. v-model="outputData"
  670. :autosize="{ minRows: 10, maxRows: 15 }"
  671. type="textarea"
  672. placeholder="Please input"
  673. />
  674. <template #footer>
  675. <span class="dialog-footer">
  676. <el-button type="primary" @click="dialogVisibleSart = false" size="mini">
  677. 确定
  678. </el-button>
  679. </span>
  680. </template>
  681. </el-dialog>
  682. </div>
  683. </div>
  684. </template>
  685. <script setup>
  686. import html2canvas from 'html2canvas';
  687. import { Controls } from '@vue-flow/controls'
  688. import '@vue-flow/controls/dist/style.css'
  689. import '@vue-flow/core/dist/theme-default.css';
  690. import { Plus,Search } from '@element-plus/icons-vue'
  691. import { toRaw, isReactive, isProxy,onUnmounted } from 'vue';
  692. import beautify from 'json-beautify';
  693. import DynamicMap from '@/components/DynamicMap/index.vue'
  694. import {Promotion} from '@element-plus/icons-vue'
  695. import { onMounted, ref,onBeforeUnmount } from 'vue'
  696. import {useVueFlow, VueFlow ,MarkerType } from '@vue-flow/core'
  697. import SpecialNode from './components/SpecialNode.vue'
  698. import SpecialEdge from './components/SpecialEdge.vue'
  699. import {getPtServiceList,getSerDe} from "@/api/service/info.js";
  700. import { getToken } from '@/utils/auth'
  701. import {getModelList2} from "@/api/register/regCom.js";
  702. import {copyObject} from "@/utils/index.js";
  703. import {getModelingDe,addModelingFlow,editModelingFlow,delFlow,runflow} from "@/api/standardization/modeling.js";
  704. import { useStore } from 'vuex';
  705. import {Handle, Position} from '@vue-flow/core'
  706. import { computed } from 'vue';
  707. import { modelTreeSelect,testService } from "@/api/service/info";
  708. // import { toRaw, isReactive, isProxy } from 'vue';
  709. const {
  710. snapToGrid,
  711. addEdges,
  712. onEdgeClick,
  713. addNodes,
  714. removeNodes,
  715. updateNodeInternals,
  716. screenToFlowCoordinate,
  717. onNodesInitialized,
  718. updateNode,
  719. onNodeClick,
  720. getNodes,
  721. zoomTo,
  722. toObject,
  723. getEdges,
  724. findNode
  725. } = useVueFlow()
  726. snapToGrid.value = true
  727. const parNodeid = ref('')
  728. const formAdd = ref({
  729. appTitle: '',
  730. appNote: '',
  731. });
  732. const parSerList = ref([])
  733. const tableDataCanStart = ref([])
  734. const dialogVisibleSart = ref(false)
  735. const testAttention = ref()
  736. const tableDataCanEnd = ref([])
  737. const outputData = ref()
  738. const dialogVisibleTest = ref(false)
  739. const dialogVisibleCsv = ref(false)
  740. const formAddref = ref()
  741. const dialogVisibleExcel = ref(false)
  742. const rulesAdd = reactive({
  743. appTitle: [{ required: true, message: '必填', trigger: 'blur' }],
  744. appNote: [{ required: true, message: '必填', trigger: 'blur' }],
  745. });
  746. const titleTest = ref('')
  747. const upload = reactive({
  748. // 是否显示弹出层(用户导入)
  749. open: false,
  750. // 弹出层标题(用户导入)
  751. title: "",
  752. // 是否禁用上传
  753. isUploading: false,
  754. // 是否更新已经存在的用户数据
  755. updateSupport: '',
  756. // 设置上传的请求头部
  757. headers: { Authorization: "Bearer " + getToken() },
  758. // 上传的地址
  759. url: import.meta.env.VITE_APP_BASE_API + "/common/upload"
  760. });
  761. const dialogVisibleBiao = ref(false)
  762. const vueFlowModel = ref();
  763. const formJi = ref({
  764. name:'',
  765. cateCode:'',
  766. type:'',
  767. url:'',
  768. rqtype:'',
  769. rptype:'',
  770. intro:'',
  771. });
  772. const nodeEnd = ref(false)
  773. const nodeStart = ref(false)
  774. const optionsCan = ref()
  775. const parTitle = ref()
  776. const rulesJi = reactive({
  777. name: [{ required: true, message: '必填', trigger: 'blur' }],
  778. url: [{ required: true, message: '必填', trigger: 'blur' }],
  779. cateCode: [{ required: true, message: '必填', trigger: 'blur' }],
  780. });
  781. const optionsType = ref([
  782. {
  783. label:"string",
  784. value:'string'
  785. },
  786. {
  787. label:"int",
  788. value:'int'
  789. },
  790. {
  791. label:"boolean",
  792. value:'boolean'
  793. },
  794. {
  795. label:"array",
  796. value:'array'
  797. },
  798. {
  799. label:"object",
  800. value:'object'
  801. },
  802. {
  803. label:"number",
  804. value:'number'
  805. },
  806. {
  807. label:"null",
  808. value:'null'
  809. },
  810. {
  811. label:"any",
  812. value:'any'
  813. },
  814. ])
  815. const dataTreeTool = ref([
  816. {
  817. label:'输入',
  818. value:'输入',
  819. children:[
  820. {
  821. label:'表输入组件',
  822. nodeType:'tool',
  823. value:'表输入组件',
  824. },
  825. {
  826. label:'Excel文件输入组件',
  827. value:'Excel文件输入组件',
  828. nodeType:'tool',
  829. },
  830. {
  831. label:'csv输入组件',
  832. nodeType:'tool',
  833. value:'csv输入组件',
  834. },
  835. ]
  836. },
  837. {
  838. label:'转换',
  839. value:'转换',
  840. children:[
  841. {
  842. label:'转换组件',
  843. nodeType:'tool',
  844. value:'转换组件',
  845. },
  846. {
  847. label:'排序记录',
  848. nodeType:'tool',
  849. value:'排序记录',
  850. },
  851. {
  852. label:'字段派生器',
  853. nodeType:'tool',
  854. value:'字段派生器',
  855. },
  856. ]
  857. },
  858. {
  859. label:'输出',
  860. value:'输出',
  861. children:[
  862. {
  863. label:'表输出组件',
  864. nodeType:'tool',
  865. value:'表输出组件',
  866. },
  867. ]
  868. },
  869. ])
  870. const tableDataCanOut = ref([]);
  871. const flowContainer = ref(null);
  872. const status = ref('就绪');
  873. const zoom = ref();
  874. const inputNode = ref('');
  875. const vueFlowRef = ref(null);
  876. const treeRef = ref(null);
  877. const servieName = ref()
  878. const serviceRqtype = ref()
  879. const serviceUrl = ref()
  880. const parFlowId = ref()
  881. const tableDataCan = ref()
  882. const nodeDeSer = ref(false)
  883. const isAdd = ref(false)
  884. const {proxy} = getCurrentInstance();
  885. const modelQueryParams = ref({
  886. name: undefined,
  887. isPublic: '0',
  888. });
  889. const toolQueryParams = ref({
  890. name: undefined,
  891. });
  892. const dataTree = ref([]);
  893. const modelOptions = ref(undefined);
  894. const modelId = ref(undefined);
  895. const loading = ref(true);
  896. const checkboxGroup1 = ref(['service'])
  897. const toolType = ref('0');
  898. const serviceList = ref([]);
  899. const serInfo = ref()
  900. const defaultEdgeStyle = {
  901. style: {
  902. stroke: '#79bbff',
  903. strokeWidth: 2,
  904. type: 'bezier',
  905. fill: 'none' // 避免截图时出现黑色背景 [4](@ref)
  906. },
  907. markerEnd: {
  908. type: MarkerType.Arrow,
  909. color: '#79bbff', // 箭头颜色
  910. width: 15, // 箭头宽度
  911. height: 15 // 箭头高度
  912. }
  913. };
  914. const draggedData = ref(undefined);
  915. const isDragging = ref(false);
  916. const isDragOver = ref(false);
  917. const nodes = ref([
  918. { id: '1', position: { x: -600, y: -300 }, data: { label: '开始' }, type: 'special' },
  919. { id: '2', position: { x: 150, y: 100 }, data: { label: '结束' }, type: 'special' },
  920. ]);
  921. const edges = ref([]);
  922. const form = ref({
  923. bodyType: 'none',
  924. errorPolicy: 'ABORT',
  925. retryCount: 0,
  926. });
  927. const title = ref('')
  928. const open = ref(false)
  929. const store = useStore();
  930. watch(inputNode, (val) => {
  931. treeRef.value?.filter(val); // 调用树的过滤方法
  932. });
  933. function clearSt(){
  934. tableDataCanStart.value = []
  935. }
  936. function showData(){
  937. dialogVisibleSart.value = true
  938. }
  939. function handleInputClick(){
  940. console.log(nodes.value)
  941. }
  942. const handleSelectChange = (selectedValue) => {
  943. const lastValue = Array.isArray(selectedValue)
  944. ? selectedValue[selectedValue.length - 1]
  945. : selectedValue;
  946. const isNewOption = !optionsCan.value.some(item => item.ref === lastValue);
  947. if (isNewOption) {
  948. const newValue = `${lastValue}:fixed`; // 生成新值
  949. // 1. 更新数据源
  950. optionsCan.value.push({ name: lastValue, ref: newValue });
  951. // 2. ⭐ 关键:手动更新 v-model 绑定值
  952. scope.row.paramValue = newValue; // 同步更新绑定值[10](@ref)
  953. }
  954. saveNode()
  955. };
  956. function addEnd(){
  957. console.log(tableDataCanEnd.value)
  958. tableDataCanEnd.value.push({})
  959. }
  960. function delEnd(index){
  961. tableDataCanEnd.value.splice(index,1)
  962. }
  963. function testOne(){
  964. var par = serInfo.value
  965. par.params = tableDataCan.value
  966. testService(par).then(res=>{
  967. dialogVisibleSart.value = true
  968. outputData.value = res.msg
  969. })
  970. }
  971. const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
  972. function saveStartNode(){
  973. nodeStart.value = false
  974. }
  975. function saveEndNode(){
  976. nodeEnd.value = false
  977. var par = tableDataCanEnd.value
  978. const node = findNode('2')
  979. node.data.data = par
  980. console.log(nodes)
  981. }
  982. function addStart(){
  983. tableDataCanStart.value.push({})
  984. }
  985. function delCanStart(index){
  986. tableDataCanStart.value.splice(index,1)
  987. }
  988. function closeDe(){
  989. nodeDeSer.value = false
  990. }
  991. function saveNode(){
  992. // console.log(tableDataCan.value)
  993. var par = {
  994. title:parTitle.value,
  995. parameters:[]
  996. }
  997. tableDataCan.value.forEach(item=>{
  998. if(item.paramValue!==null){
  999. var parSplit = item.paramValue.split('.')
  1000. if(parSplit[1]==='fixed'){
  1001. var par1 = {
  1002. name:item.paramCode,
  1003. value:parSplit[0],
  1004. dataType:item.paramType,
  1005. refType:'fixed'
  1006. }
  1007. par.parameters.push(par1)
  1008. }
  1009. else{
  1010. var par1 = {
  1011. name:item.paramCode,
  1012. ref:item.paramValue,
  1013. dataType:item.paramType,
  1014. refType:'ref'
  1015. }
  1016. par.parameters.push(par1)
  1017. }
  1018. }
  1019. else{
  1020. var par1 = {
  1021. name:item.paramCode,
  1022. value:null,
  1023. dataType:item.paramType,
  1024. refType:'fixed'
  1025. }
  1026. par.parameters.push(par1)
  1027. }
  1028. })
  1029. nodes.value.forEach(item=>{
  1030. if(item.id===parNodeid.value){
  1031. // console.log(item.id)
  1032. item.data.parameters = par.parameters
  1033. item.data.service = serInfo.value
  1034. item.data.service.params = parSerList.value
  1035. }
  1036. })
  1037. console.log(toObject())
  1038. // nodeDeSer.value = false
  1039. // const node = findNode(parNodeid.value)
  1040. // node.data.data = par
  1041. // updateNode(node.id, { data: { ...node.data, ...par } });
  1042. // updateNodeInternals(node.id);
  1043. // updateNode(parNodeid.value, {
  1044. // data: par
  1045. // });
  1046. }
  1047. function delWholeFlow(){
  1048. proxy.$confirm('是否删除该模型流程?', '提示', {
  1049. confirmButtonText: '确定',
  1050. cancelButtonText: '取消',
  1051. type: 'warning'
  1052. }).then(() => {
  1053. delFlow(parFlowId.value).then(res => {
  1054. if(res.code === 200){
  1055. proxy.$message({
  1056. message: '删除成功',
  1057. type: 'success'
  1058. });
  1059. getList();
  1060. } else {
  1061. proxy.$message.error('删除失败');
  1062. }
  1063. })
  1064. })
  1065. }
  1066. const toImage = async () => {
  1067. console.log(flowContainer.value)
  1068. await nextTick();
  1069. status.value = '生成图片中...';
  1070. try {
  1071. const canvas = await html2canvas(flowContainer.value, {
  1072. backgroundColor: '#fff',
  1073. scale: 2,
  1074. useCORS: true,
  1075. });
  1076. // 生成下载链接
  1077. const dataUrl = canvas.toDataURL('image/png');
  1078. const link = document.createElement('a');
  1079. link.href = dataUrl;
  1080. link.download = 'vueflow-diagram.png'; // 文件名
  1081. link.click();
  1082. status.value = '导出成功!';
  1083. setTimeout(() => status.value = '就绪', 3000); // 重置状态
  1084. } catch (error) {
  1085. status.value = `导出失败: ${error.message}`;
  1086. console.error('导出错误:', error);
  1087. }
  1088. };
  1089. function testNode(node){
  1090. dialogVisibleTest.value = true
  1091. }
  1092. function oneSel(){
  1093. if(checkboxGroup1.value.length>1){
  1094. checkboxGroup1.value.splice(0,1)
  1095. }
  1096. }
  1097. async function delNode(node){
  1098. removeNodes([node])
  1099. await nextTick()
  1100. }
  1101. function generateTimestampId() {
  1102. const timestamp = Date.now(); // 13位毫秒时间戳
  1103. const randomNum = Math.floor(Math.random() * 1000); // 3位随机数
  1104. return parseInt(`${timestamp}${randomNum}`); // 拼接后转为整数
  1105. }
  1106. onEdgeClick(({ edge }) => {
  1107. console.log(edges.value,edge)
  1108. edges.value = edges.value.filter(item => item.id !== edge.id)
  1109. });
  1110. async function getTreeLeft(){
  1111. var par = {
  1112. params:{
  1113. serviceState:'1'
  1114. }
  1115. }
  1116. await modelTreeSelect(par).then(res=>{
  1117. dataTree.value = (res.data)
  1118. })
  1119. }
  1120. function flatArrayToTree(flatArray, rootPid = '1') {
  1121. if (!Array.isArray(flatArray) || flatArray.length === 0) {
  1122. return [];
  1123. }
  1124. const nodeMap = new Map();
  1125. const tree = [];
  1126. // 第一遍循环:建立id到节点的映射,并添加children属性
  1127. flatArray.forEach(item => {
  1128. nodeMap.set(item.id, {
  1129. ...item,
  1130. children: []
  1131. });
  1132. });
  1133. // 第二遍循环:建立父子关系
  1134. flatArray.forEach(item => {
  1135. const currentNode = nodeMap.get(item.id);
  1136. if (item.pid === rootPid) {
  1137. // 根节点直接加入结果数组
  1138. tree.push(currentNode);
  1139. } else {
  1140. // 非根节点:找到父节点并加入其children数组
  1141. const parentNode = nodeMap.get(item.pid);
  1142. if (parentNode) {
  1143. parentNode.children.push(currentNode);
  1144. } else {
  1145. // 如果找不到父节点,作为孤儿节点直接放在根级
  1146. tree.push(currentNode);
  1147. }
  1148. }
  1149. });
  1150. return tree;
  1151. }
  1152. function deepToRaw(obj) {
  1153. // 如果是数组,遍历每个元素并递归处理
  1154. if (Array.isArray(obj)) {
  1155. return obj.map(item => deepToRaw(item));
  1156. }
  1157. // 如果是对象且可能是响应式代理,获取其原始对象并递归处理其属性
  1158. else if (obj !== null && typeof obj === 'object' && (isProxy(obj) || isReactive(obj))) {
  1159. const rawObj = toRaw(obj);
  1160. return Object.fromEntries(
  1161. Object.entries(rawObj).map(([key, value]) => [key, deepToRaw(value)])
  1162. );
  1163. }
  1164. // 如果是普通对象,直接返回
  1165. return obj;
  1166. }
  1167. async function startTest(){
  1168. const baseUrl = window.location.origin.toString().substring(6)
  1169. messages.value = []
  1170. const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
  1171. const host = window.location.host;
  1172. ws.value = new WebSocket('ws://' + '192.168.2.119:8082/websocket/message');
  1173. ws.value.onopen = () => {
  1174. connected.value = true;
  1175. };
  1176. ws.value.onmessage = (event) => {
  1177. messages.value.push(event.data); // 存储接收到的消息
  1178. };
  1179. await delay(1000)
  1180. if(messages.value.length>0){
  1181. var param = []
  1182. var paramEnd = []
  1183. if(tableDataCanStart.value.length>0&&tableDataCanEnd.value.length>0){
  1184. tableDataCanStart.value.forEach(item=>{
  1185. var par = {
  1186. name:item.name,
  1187. dataType:item.dataType,
  1188. refType:'input',
  1189. description:item.description,
  1190. required:item.required
  1191. }
  1192. param.push(par)
  1193. })
  1194. tableDataCanEnd.value.forEach(item=>{
  1195. var par = {
  1196. name:item.name,
  1197. dataType:item.dataType,
  1198. refType:'ref',
  1199. ref:item.paramValue
  1200. }
  1201. paramEnd.push(par)
  1202. })
  1203. nodes.value.forEach(item=>{
  1204. if(item.id==='1'){
  1205. item.data.parameters = param
  1206. }
  1207. if(item.id==='2'){
  1208. item.data.outputDefs = paramEnd
  1209. }
  1210. })
  1211. var a = JSON.parse(JSON.stringify(toObject()))
  1212. a.nodes.forEach(item=>{
  1213. if(item.id==='1'){
  1214. item.type = 'startNode'
  1215. }
  1216. else if(item.id==='2'){
  1217. item.type = 'endNode'
  1218. }
  1219. else{
  1220. item.type = 'serviceNode'
  1221. }
  1222. })
  1223. var par = {}
  1224. par.flowGraph = JSON.stringify(a)
  1225. par.params = {}
  1226. par.webSocketId = messages.value[0]
  1227. tableDataCanStart.value.forEach(item=>{
  1228. // par.set(item.name,item.value)
  1229. par.params[item.name] = item.paramValue
  1230. })
  1231. console.log(par.params)
  1232. await runflow(par).then(res=>{
  1233. if(res.code === 200){
  1234. proxy.$message({
  1235. message: res.msg,
  1236. type: 'success'
  1237. });
  1238. dialogVisibleSart.value = true
  1239. outputData.value = beautify(res.data, null, 2, 80)
  1240. ws.value.close()
  1241. ws.value = null
  1242. }
  1243. })
  1244. }
  1245. else if(tableDataCanStart.value.length===0){
  1246. proxy.$message({
  1247. message: '请设置输入值!',
  1248. type: 'warning'
  1249. });
  1250. }
  1251. else if(tableDataCanEnd.value.length===0){
  1252. proxy.$message({
  1253. message: '请设置输出值!',
  1254. type: 'warning'
  1255. });
  1256. }
  1257. }
  1258. }
  1259. async function saveFlow(){
  1260. if(tableDataCanStart.value.length>0&&tableDataCanEnd.value.length>0){
  1261. var param = []
  1262. var paramEnd = []
  1263. tableDataCanStart.value.forEach(item=>{
  1264. var par = {
  1265. name:item.name,
  1266. dataType:item.dataType,
  1267. refType:'input',
  1268. description:item.description,
  1269. required:item.required,
  1270. // paramValue:item.paramValue
  1271. }
  1272. param.push(par)
  1273. })
  1274. tableDataCanEnd.value.forEach(item=>{
  1275. var par = {
  1276. name:item.name,
  1277. dataType:item.dataType,
  1278. refType:'ref',
  1279. ref:item.paramValue,
  1280. paramValue:item.paramValue
  1281. }
  1282. paramEnd.push(par)
  1283. })
  1284. nodes.value.forEach(item=>{
  1285. if(item.id==='1'){
  1286. item.data.parameters = param
  1287. if(item.erMsg){
  1288. delete item.erMsg
  1289. }
  1290. }
  1291. if(item.id==='2'){
  1292. item.data.outputDefs = paramEnd
  1293. if(item.erMsg){
  1294. delete item.erMsg
  1295. }
  1296. }
  1297. })
  1298. var a = JSON.parse(JSON.stringify(toObject()))
  1299. a.nodes.forEach(item=>{
  1300. if(item.id==='1'){
  1301. item.type = 'startNode'
  1302. }
  1303. else if(item.id==='2'){
  1304. item.type = 'endNode'
  1305. }
  1306. else{
  1307. item.type = 'serviceNode'
  1308. }
  1309. })
  1310. if(isAdd.value){
  1311. const count = computed(() => store.getters.id)
  1312. var par = {
  1313. appId: count.value,
  1314. flowName:'default',
  1315. flowGraph: JSON.stringify(a)
  1316. }
  1317. addModelingFlow(par).then(res=>{
  1318. if(res.code===200){
  1319. proxy.$message({
  1320. message: '保存成功',
  1321. type: 'success'
  1322. });
  1323. }
  1324. getList()
  1325. })
  1326. }
  1327. else{
  1328. const count = computed(() => store.getters.id)
  1329. var par = {
  1330. flowId: parFlowId.value,
  1331. appId: count.value,
  1332. flowName:'default',
  1333. flowGraph: JSON.stringify(a)
  1334. }
  1335. console.log(par)
  1336. editModelingFlow(par).then(res=>{
  1337. if(res.code===200){
  1338. proxy.$message({
  1339. message: '保存成功',
  1340. type: 'success'
  1341. });
  1342. getList()
  1343. }
  1344. })
  1345. }
  1346. }
  1347. else if(tableDataCanStart.value.length===0){
  1348. proxy.$message({
  1349. message: '请设置输入值!',
  1350. type: 'warning'
  1351. });
  1352. }
  1353. else if(tableDataCanEnd.value.length===0){
  1354. proxy.$message({
  1355. message: '请设置输出值!',
  1356. type: 'warning'
  1357. });
  1358. }
  1359. var parFlow = toRaw(toObject())
  1360. parFlow.nodes.forEach(item => {
  1361. if(item.id==='1'){
  1362. item.type='startNode'
  1363. item.data.parameters = tableDataCanStart.value;
  1364. }
  1365. if(item.id==='2'){
  1366. item.type='endNode'
  1367. item.data.parameters = tableDataCanEnd.value;
  1368. console.log(item)
  1369. }
  1370. item.title = item.label
  1371. });
  1372. }
  1373. function getPredecessorsNodes(nodeId, edges = getEdges.value, allNodes = getNodes.value){
  1374. // 1. 找到所有直接指向目标节点的边(即 target 为 nodeId 的边)
  1375. const incomingEdges = edges.filter(edge => edge.target === nodeId);
  1376. // 2. 从这些边中提取所有源节点的 ID
  1377. const sourceNodeIds = incomingEdges.map(edge => edge.source);
  1378. // 3. 根据源节点 ID 获取完整的节点对象
  1379. const directPredecessors = allNodes.filter(node => sourceNodeIds.includes(node.id));
  1380. // 4. 递归获取这些直接前置节点的前置节点
  1381. const allPredecessors = [];
  1382. for (const predecessor of directPredecessors) {
  1383. // 将直接前置节点加入结果数组
  1384. allPredecessors.push(predecessor);
  1385. // 递归获取该前置节点的前置节点,并合并到结果数组中
  1386. const predecessorsOfPredecessor = getPredecessorsNodes(predecessor.id, edges, allNodes);
  1387. allPredecessors.push(...predecessorsOfPredecessor);
  1388. }
  1389. // 5. 使用 Set 或其他方法去重(根据节点 ID),因为一个节点可能通过不同路径被多次访问
  1390. const uniquePredecessors = Array.from(new Map(allPredecessors.map(node => [node.id, node])).values());
  1391. return uniquePredecessors;
  1392. };
  1393. function removeDuplicatesAndEmptyItems(arr) {
  1394. const map = new Map();
  1395. return arr.filter(item => {
  1396. // 过滤 null 和 undefined
  1397. if (item === null || item === undefined) return false;
  1398. // 处理对象类型:转为字符串标识去重
  1399. const key = typeof item === 'object'
  1400. ? JSON.stringify(item)
  1401. : item;
  1402. // 若标识不存在于 Map 中,则保留并记录
  1403. if (!map.has(key)) {
  1404. map.set(key, true);
  1405. return true;
  1406. }
  1407. return false;
  1408. });
  1409. }
  1410. function parseJSONWithComments(jsonString) {
  1411. // 移除单行注释(//)和多行注释(/* */)
  1412. const cleanedJson = jsonString.replace(/\/\/.*|\/\*[\s\S]*?\*\//g, '');
  1413. try {
  1414. return JSON.parse(cleanedJson);
  1415. } catch (error) {
  1416. console.error("解析失败:", error.message);
  1417. return null;
  1418. }
  1419. }
  1420. onNodeClick(({event, node}) => {
  1421. nodeEnd.value = false
  1422. nodeStart.value = false
  1423. nodeDeSer.value = false
  1424. parNodeid.value = node.id
  1425. parTitle.value = node.data.name
  1426. tableDataCan.value = []
  1427. optionsCan.value = []
  1428. tableDataCanOut.value = []
  1429. var parNode = getPredecessorsNodes(node.id)
  1430. var a = JSON.parse(JSON.stringify(parNode))
  1431. // console.log(a)
  1432. a.forEach((item,index) => {
  1433. console.log(item.data)
  1434. if(item.data.label!=='开始'&&item.data.service&&item.data.service.rpcontent){
  1435. item.data.service.rpcontent = parseJSONWithComments(item.data.service.rpcontent)
  1436. console.log((item.data.service.rpcontent))
  1437. var parShu = Object.keys((item.data.service.rpcontent))
  1438. parShu.forEach(item1=>{
  1439. var par = {
  1440. ref:item.id + '.' + item1,
  1441. name:item.data.label + ':' + item1
  1442. }
  1443. optionsCan.value.push(par)
  1444. })
  1445. }
  1446. if(tableDataCanStart.value.length>0){
  1447. tableDataCanStart.value.forEach(item=>{
  1448. var par = {
  1449. ref:'1' + '.' + item.name,
  1450. name:item.name
  1451. }
  1452. optionsCan.value.push(par)
  1453. })
  1454. }
  1455. })
  1456. console.log(optionsCan.value)
  1457. if(node.data.label==='结束'){
  1458. nodeEnd.value = true
  1459. }
  1460. if(node.data.label==='开始'){
  1461. nodeStart.value = true
  1462. }
  1463. if(node.data.nodeType==='SERVICE'){
  1464. getSerDe(node.data.id).then(res=>{
  1465. serviceRqtype.value = res.data.ptService.rqtype
  1466. servieName.value = res.data.ptService.name
  1467. tableDataCan.value = res.data.list
  1468. serInfo.value = res.data.ptService
  1469. parSerList.value = JSON.parse(JSON.stringify(res.data.list))
  1470. var par1 = parseJSONWithComments(res.data.ptService.rpcontent)
  1471. var parShu = Object.keys((par1))
  1472. console.log(parShu)
  1473. parShu.forEach(item1=>{
  1474. var par = {
  1475. paramCode:item1,
  1476. paramValue:''
  1477. }
  1478. tableDataCanOut.value.push(par)
  1479. })
  1480. // tableDataCanOut.value = parseJSONWithComments(res.data.ptService.rpcontent)
  1481. serviceUrl.value = res.data.ptService.url
  1482. nodeDeSer.value = true
  1483. })
  1484. }
  1485. if(node.data.nodeType==='tool'){
  1486. console.log(node.data.value)
  1487. if(node.data.value==='表输入组件'){
  1488. dialogVisibleBiao.value = true
  1489. }
  1490. if(node.data.value==='Excel文件输入组件'){
  1491. dialogVisibleExcel.value = true
  1492. }
  1493. if(node.data.value==='csv输入组件'){
  1494. dialogVisibleCsv.value = true
  1495. }
  1496. }
  1497. });
  1498. /**
  1499. * 开始拖拽选项的事件
  1500. * @param event
  1501. * @param data
  1502. */
  1503. function onDragStart(event, data) {
  1504. if(data.nodeType==='SERVICE'||data.nodeType==='tool'){
  1505. if (event.dataTransfer) {
  1506. event.dataTransfer.setData('application/vueflow', data)
  1507. event.dataTransfer.effectAllowed = 'move'
  1508. }
  1509. draggedData.value = data
  1510. isDragging.value = true
  1511. document.addEventListener('drop', onDragEnd)
  1512. }
  1513. }
  1514. /**
  1515. * 拖拽到画布vueflow的事件
  1516. * @param event
  1517. */
  1518. function onDragOver(event) {
  1519. event.preventDefault()
  1520. if (draggedData.value) {
  1521. isDragOver.value = true
  1522. if (event.dataTransfer) {
  1523. event.dataTransfer.dropEffect = 'move'
  1524. }
  1525. }
  1526. }
  1527. /**
  1528. * 拖拽放下的事件
  1529. * @param event
  1530. */
  1531. async function onDrop(event) {
  1532. const position = screenToFlowCoordinate({ x: event.clientX, y: event.clientY });
  1533. const nodeId = generateTimestampId();
  1534. // 1. 同步创建节点
  1535. const newNode = {
  1536. id: nodeId,
  1537. type: 'special',
  1538. position,
  1539. data: copyObject(draggedData.value) // 先使用本地数据
  1540. };
  1541. addNodes(newNode);
  1542. // 2. 异步获取数据并更新
  1543. const res = await getSerDe(draggedData.value.id);
  1544. const fullData = {
  1545. ...res.data.ptService,
  1546. returnList: res.data.returnList
  1547. };
  1548. // 3. 等待节点渲染完成后再修正位置
  1549. nextTick(() => {
  1550. updateNode(nodeId, { data: { ...newNode.data, ...fullData } });
  1551. updateNodeInternals(nodeId); // 强制更新节点布局[1](@ref)
  1552. });
  1553. // console.log(toObject())
  1554. }
  1555. /**
  1556. * 拖拽到画布外面的的事件
  1557. */
  1558. function onDragLeave() {
  1559. isDragOver.value = false
  1560. }
  1561. /**
  1562. * 拖拽结束
  1563. */
  1564. function onDragEnd() {
  1565. isDragging.value = false
  1566. isDragOver.value = false
  1567. draggedData.value = null
  1568. document.removeEventListener('drop', onDragEnd)
  1569. }
  1570. function onConnect(params) {
  1571. addEdges({ ...params, ...defaultEdgeStyle })
  1572. }
  1573. function back(){
  1574. proxy.$router.push({ path: '/standardization/modelUsing' });
  1575. }
  1576. /** 查询模型列表 */
  1577. function getModelList() {
  1578. getModelList2(modelQueryParams.value).then(res => {
  1579. loading.value = false;
  1580. modelOptions.value = res.data;
  1581. });
  1582. }
  1583. /** 通过条件过滤节点 */
  1584. const filterNode = (value, data) => {
  1585. if (!value) return true;
  1586. return data.label.indexOf(value) !== -1;
  1587. };
  1588. /** 查询流程图 */
  1589. function getList() {
  1590. const count = computed(() => store.getters.id)
  1591. console.log(count.value);
  1592. var par = {
  1593. appId: count.value
  1594. }
  1595. getModelingDe(par).then(res => {
  1596. if(res.data.length===0){
  1597. isAdd.value = true
  1598. nodes.value = [
  1599. { id: '1', position: { x: -600, y: -300 }, data: { label: '开始' }, type: 'special' },
  1600. { id: '2', position: { x: 150, y: 100 }, data: { label: '结束' }, type: 'special' },
  1601. ]
  1602. }
  1603. else{
  1604. isAdd.value = false
  1605. var a = JSON.parse(JSON.parse(JSON.stringify(res.data[0].flowGraph)))
  1606. console.log(a)
  1607. a.nodes.forEach(item=>{
  1608. item.type = 'special'
  1609. if(item.id==='2'&&item.data){
  1610. tableDataCanEnd.value = item.data.outputDefs
  1611. }
  1612. if(item.id==='1'&&item.data.parameters){
  1613. tableDataCanStart.value = item.data.parameters
  1614. }
  1615. })
  1616. nodes.value = a.nodes
  1617. edges.value = a.edges
  1618. zoom.value = a.zoom
  1619. zoomTo(zoom.value)
  1620. parFlowId.value = res.data[0].flowId
  1621. }
  1622. if(tableDataCanEnd.value.length===0){
  1623. tableDataCanEnd.value = []
  1624. }
  1625. });
  1626. }
  1627. const ws = ref(null);
  1628. const connected = ref(false);
  1629. const messages = ref([]);
  1630. const connect = () => {
  1631. ws.value = new WebSocket('ws://127.0.0.1:8082/websocket/message'); // 替换为你的 WS 地址
  1632. ws.value.onopen = () => {
  1633. connected.value = true;
  1634. };
  1635. ws.value.onmessage = (event) => {
  1636. messages.value.push(event.data); // 存储接收到的消息
  1637. };
  1638. ws.value.onerror = (error) => {
  1639. console.error('WebSocket 错误:', error);
  1640. };
  1641. ws.value.onclose = () => {
  1642. connected.value = false;
  1643. console.log('WebSocket 连接已关闭');
  1644. };
  1645. };
  1646. const sendMessage = () => {
  1647. if (ws.value && ws.value.readyState === WebSocket.OPEN) {
  1648. const message = 'Hello, Server!'; // 实际发送的消息内容或格式可与后端约定
  1649. ws.value.send(message);
  1650. }
  1651. };
  1652. const disconnect = () => {
  1653. if (ws.value) {
  1654. ws.value.close();
  1655. ws.value = null;
  1656. }
  1657. };
  1658. // 组件卸载时自动断开连接
  1659. onUnmounted(() => {
  1660. disconnect();
  1661. });
  1662. onMounted(() => {
  1663. // connect()
  1664. getTreeLeft()
  1665. getList();
  1666. })
  1667. watch(messages, (newMessages) => {
  1668. newMessages.forEach(item=>{
  1669. item = JSON.parse(item)
  1670. nodes.value.forEach(item1=>{
  1671. if(item1.id===item.id){
  1672. if(item.nodeState&&item.nodeState==='START'){
  1673. item1.data.loading = true
  1674. item1.data.isSuccess = true
  1675. item1.data.isFail = false
  1676. }
  1677. if(item.nodeState&&item.nodeState==='ERROR'){
  1678. console.log(3)
  1679. item1.data.loading = false
  1680. item1.data.isSuccess = false
  1681. item1.data.isFail = true
  1682. item1.data.erMsg = item.errorMessage
  1683. // throw new TypeError(`期望传入一个数字,但收到的是`)
  1684. }
  1685. if(item.nodeState&&item.nodeState==='END'){
  1686. console.log(2)
  1687. item1.data.loading = false
  1688. // item1.data.isSuccess = true
  1689. // item1.data.isFail = false
  1690. }
  1691. }
  1692. })
  1693. })
  1694. }, { deep: true })
  1695. // watch(nodes, (newNodes) => {
  1696. // console.log('当前节点列表:', newNodes) // 实时输出最新数据
  1697. // }, { deep: true })
  1698. </script>
  1699. <style>
  1700. @import '@vue-flow/core/dist/style.css';
  1701. @import '@vue-flow/core/dist/theme-default.css';
  1702. </style>
  1703. <style scoped>
  1704. .custom-no-border :deep(.el-input__wrapper) {
  1705. box-shadow: none !important; /* 移除默认阴影(即边框) */
  1706. border: none !important; /* 双重保障 */
  1707. }
  1708. /* 处理悬停和聚焦状态 */
  1709. .custom-no-border :deep(.el-input__wrapper:hover),
  1710. .custom-no-border :deep(.el-input__wrapper:focus-within) {
  1711. box-shadow: none !important;
  1712. border: none !important;
  1713. }
  1714. :deep(.treeLeft) .el-tree-node__content {
  1715. display: flex !important;
  1716. height: 28px; /* 按设计稿调整高度 */
  1717. align-items: center;
  1718. padding-top: 0 !important;
  1719. }
  1720. :deep(.treeLeft) .el-tree-node__content:hover {
  1721. background-color: #e9e9eb;
  1722. }
  1723. :deep(.treeLeft) .el-tree-node__content:active {
  1724. background-color: rgka(69,157,255,0.1) !important;
  1725. }
  1726. /* 选中态(Active) */
  1727. :deep(.treeLeft) .el-tree-node.is-current > .el-tree-node__content {
  1728. background-color: #c6e2ff !important;
  1729. }
  1730. </style>