index.vue 57 KB

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