index.vue 79 KB

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