index.vue 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550
  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-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;">
  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 { toRaw, isReactive, isProxy } from 'vue';
  675. import DynamicMap from '@/components/DynamicMap/index.vue'
  676. import {Promotion} from '@element-plus/icons-vue'
  677. import { onMounted, ref,onBeforeUnmount } from 'vue'
  678. import {useVueFlow, VueFlow ,MarkerType } from '@vue-flow/core'
  679. import SpecialNode from './components/SpecialNode.vue'
  680. import SpecialEdge from './components/SpecialEdge.vue'
  681. import {getPtServiceList,getSerDe} from "@/api/service/info.js";
  682. import { getToken } from '@/utils/auth'
  683. import {getModelList2} from "@/api/register/regCom.js";
  684. import {copyObject} from "@/utils/index.js";
  685. import {getModelingDe,addModelingFlow,editModelingFlow,delFlow,runflow} from "@/api/standardization/modeling.js";
  686. import { useStore } from 'vuex';
  687. import {Handle, Position} from '@vue-flow/core'
  688. import { computed } from 'vue';
  689. import { modelTreeSelect } from "@/api/service/info";
  690. // import { toRaw, isReactive, isProxy } from 'vue';
  691. const {
  692. snapToGrid,
  693. addEdges,
  694. onEdgeClick,
  695. addNodes,
  696. removeNodes,
  697. updateNodeInternals,
  698. screenToFlowCoordinate,
  699. onNodesInitialized,
  700. updateNode,
  701. onNodeClick,
  702. getNodes,
  703. zoomTo,
  704. toObject,
  705. getEdges,
  706. findNode
  707. } = useVueFlow()
  708. snapToGrid.value = true
  709. const parNodeid = ref('')
  710. const formAdd = ref({
  711. appTitle: '',
  712. appNote: '',
  713. });
  714. const tableDataCanStart = ref([])
  715. const dialogVisibleSart = ref(false)
  716. const testAttention = ref()
  717. const tableDataCanEnd = ref([])
  718. const outputData = ref()
  719. const dialogVisibleTest = ref(false)
  720. const dialogVisibleCsv = ref(false)
  721. const formAddref = ref()
  722. const dialogVisibleExcel = ref(false)
  723. const rulesAdd = reactive({
  724. appTitle: [{ required: true, message: '必填', trigger: 'blur' }],
  725. appNote: [{ required: true, message: '必填', trigger: 'blur' }],
  726. });
  727. const titleTest = ref('')
  728. const upload = reactive({
  729. // 是否显示弹出层(用户导入)
  730. open: false,
  731. // 弹出层标题(用户导入)
  732. title: "",
  733. // 是否禁用上传
  734. isUploading: false,
  735. // 是否更新已经存在的用户数据
  736. updateSupport: '',
  737. // 设置上传的请求头部
  738. headers: { Authorization: "Bearer " + getToken() },
  739. // 上传的地址
  740. url: import.meta.env.VITE_APP_BASE_API + "/common/upload"
  741. });
  742. const dialogVisibleBiao = ref(false)
  743. const vueFlowModel = ref();
  744. const formJi = ref({
  745. name:'',
  746. cateCode:'',
  747. type:'',
  748. url:'',
  749. rqtype:'',
  750. rptype:'',
  751. intro:'',
  752. });
  753. const nodeEnd = ref(false)
  754. const nodeStart = ref(false)
  755. const optionsCan = ref()
  756. const parTitle = ref()
  757. const rulesJi = reactive({
  758. name: [{ required: true, message: '必填', trigger: 'blur' }],
  759. url: [{ required: true, message: '必填', trigger: 'blur' }],
  760. cateCode: [{ required: true, message: '必填', trigger: 'blur' }],
  761. });
  762. const optionsType = ref([
  763. {
  764. label:"string",
  765. value:'string'
  766. },
  767. {
  768. label:"int",
  769. value:'int'
  770. },
  771. {
  772. label:"boolean",
  773. value:'boolean'
  774. },
  775. {
  776. label:"array",
  777. value:'array'
  778. },
  779. {
  780. label:"object",
  781. value:'object'
  782. },
  783. {
  784. label:"number",
  785. value:'number'
  786. },
  787. {
  788. label:"null",
  789. value:'null'
  790. },
  791. {
  792. label:"any",
  793. value:'any'
  794. },
  795. ])
  796. const dataTreeTool = ref([
  797. {
  798. label:'输入',
  799. value:'输入',
  800. children:[
  801. {
  802. label:'表输入组件',
  803. nodeType:'tool',
  804. value:'表输入组件',
  805. },
  806. {
  807. label:'Excel文件输入组件',
  808. value:'Excel文件输入组件',
  809. nodeType:'tool',
  810. },
  811. {
  812. label:'csv输入组件',
  813. nodeType:'tool',
  814. value:'csv输入组件',
  815. },
  816. ]
  817. },
  818. {
  819. label:'转换',
  820. value:'转换',
  821. children:[
  822. {
  823. label:'转换组件',
  824. nodeType:'tool',
  825. value:'转换组件',
  826. },
  827. {
  828. label:'排序记录',
  829. nodeType:'tool',
  830. value:'排序记录',
  831. },
  832. {
  833. label:'字段派生器',
  834. nodeType:'tool',
  835. value:'字段派生器',
  836. },
  837. ]
  838. },
  839. {
  840. label:'输出',
  841. value:'输出',
  842. children:[
  843. {
  844. label:'表输出组件',
  845. nodeType:'tool',
  846. value:'表输出组件',
  847. },
  848. ]
  849. },
  850. ])
  851. const tableDataCanOut = ref([]);
  852. const flowContainer = ref(null);
  853. const status = ref('就绪');
  854. const zoom = ref();
  855. const inputNode = ref('');
  856. const vueFlowRef = ref(null);
  857. const treeRef = ref(null);
  858. const servieName = ref()
  859. const serviceRqtype = ref()
  860. const serviceUrl = ref()
  861. const parFlowId = ref()
  862. const tableDataCan = ref()
  863. const nodeDeSer = ref(false)
  864. const isAdd = ref(false)
  865. const {proxy} = getCurrentInstance();
  866. const modelQueryParams = ref({
  867. name: undefined,
  868. isPublic: '0',
  869. });
  870. const toolQueryParams = ref({
  871. name: undefined,
  872. });
  873. const dataTree = ref([]);
  874. const modelOptions = ref(undefined);
  875. const modelId = ref(undefined);
  876. const loading = ref(true);
  877. const checkboxGroup1 = ref(['service'])
  878. const toolType = ref('0');
  879. const serviceList = ref([]);
  880. const defaultEdgeStyle = {
  881. style: {
  882. stroke: '#79bbff',
  883. strokeWidth: 2,
  884. type: 'bezier',
  885. fill: 'none' // 避免截图时出现黑色背景 [4](@ref)
  886. },
  887. markerEnd: {
  888. type: MarkerType.Arrow,
  889. color: '#79bbff', // 箭头颜色
  890. width: 15, // 箭头宽度
  891. height: 15 // 箭头高度
  892. }
  893. };
  894. const draggedData = ref(undefined);
  895. const isDragging = ref(false);
  896. const isDragOver = ref(false);
  897. const nodes = ref([
  898. { id: '1', position: { x: -600, y: -300 }, data: { label: '开始' }, type: 'special' },
  899. { id: '2', position: { x: 150, y: 100 }, data: { label: '结束' }, type: 'special' },
  900. ]);
  901. const edges = ref([]);
  902. const form = ref({
  903. bodyType: 'none',
  904. errorPolicy: 'ABORT',
  905. retryCount: 0,
  906. });
  907. const title = ref('')
  908. const open = ref(false)
  909. const store = useStore();
  910. watch(inputNode, (val) => {
  911. treeRef.value?.filter(val); // 调用树的过滤方法
  912. });
  913. function clearSt(){
  914. tableDataCanStart.value = []
  915. }
  916. function showData(){
  917. dialogVisibleSart.value = true
  918. }
  919. function handleInputClick(){
  920. console.log(nodes.value)
  921. }
  922. const handleSelectChange = (selectedValue) => {
  923. const lastValue = Array.isArray(selectedValue)
  924. ? selectedValue[selectedValue.length - 1]
  925. : selectedValue;
  926. const isNewOption = !optionsCan.value.some(item => item.ref === lastValue);
  927. if (isNewOption) {
  928. const newValue = `${lastValue}:fixed`; // 生成新值
  929. // 1. 更新数据源
  930. optionsCan.value.push({ name: lastValue, ref: newValue });
  931. // 2. ⭐ 关键:手动更新 v-model 绑定值
  932. scope.row.paramValue = newValue; // 同步更新绑定值[10](@ref)
  933. }
  934. };
  935. function addEnd(){
  936. console.log(tableDataCanEnd.value)
  937. tableDataCanEnd.value.push({})
  938. }
  939. function delEnd(index){
  940. tableDataCanEnd.value.splice(index,1)
  941. }
  942. function startTest(){
  943. var param = []
  944. var paramEnd = []
  945. if(tableDataCanStart.value.length>0&&tableDataCanEnd.value.length>0){
  946. tableDataCanStart.value.forEach(item=>{
  947. var par = {
  948. name:item.name,
  949. dataType:item.dataType,
  950. refType:'input',
  951. description:item.description,
  952. required:item.required
  953. }
  954. param.push(par)
  955. })
  956. tableDataCanEnd.value.forEach(item=>{
  957. var par = {
  958. name:item.name,
  959. dataType:item.dataType,
  960. refType:'ref',
  961. ref:item.paramValue
  962. }
  963. paramEnd.push(par)
  964. })
  965. nodes.value.forEach(item=>{
  966. if(item.id==='1'){
  967. item.data.parameters = param
  968. }
  969. if(item.id==='2'){
  970. item.data.outputDefs = paramEnd
  971. }
  972. })
  973. var a = JSON.parse(JSON.stringify(toObject()))
  974. a.nodes.forEach(item=>{
  975. if(item.id==='1'){
  976. item.type = 'startNode'
  977. }
  978. else if(item.id==='2'){
  979. item.type = 'endNode'
  980. }
  981. else{
  982. item.type = 'serviceNode'
  983. }
  984. })
  985. var par = {}
  986. par.flowGraph = JSON.stringify(a)
  987. par.params = {}
  988. tableDataCanStart.value.forEach(item=>{
  989. // par.set(item.name,item.value)
  990. par.params[item.name] = item.value
  991. })
  992. console.log(a)
  993. runflow(par).then(res=>{
  994. if(res.code === 200){
  995. proxy.$message({
  996. message: res.msg,
  997. type: 'success'
  998. });
  999. dialogVisibleSart.value = true
  1000. outputData.value = JSON.stringify(res.data)
  1001. }
  1002. })
  1003. }
  1004. else if(tableDataCanStart.value.length===0){
  1005. proxy.$message({
  1006. message: '请设置输入值!',
  1007. type: 'warning'
  1008. });
  1009. }
  1010. else if(tableDataCanEnd.value.length===0){
  1011. proxy.$message({
  1012. message: '请设置输出值!',
  1013. type: 'warning'
  1014. });
  1015. }
  1016. }
  1017. function saveStartNode(){
  1018. nodeStart.value = false
  1019. }
  1020. function saveEndNode(){
  1021. nodeEnd.value = false
  1022. var par = tableDataCanEnd.value
  1023. const node = findNode('2')
  1024. node.data.data = par
  1025. console.log(nodes)
  1026. }
  1027. function addStart(){
  1028. tableDataCanStart.value.push({})
  1029. }
  1030. function delCanStart(index){
  1031. tableDataCanStart.value.splice(index,1)
  1032. }
  1033. function saveNode(){
  1034. // console.log(tableDataCan.value)
  1035. var par = {
  1036. title:parTitle.value,
  1037. parameters:[]
  1038. }
  1039. tableDataCan.value.forEach(item=>{
  1040. if(item.paramValue!==null){
  1041. var parSplit = item.paramValue.split(':')
  1042. if(parSplit[1]==='fixed'){
  1043. var par1 = {
  1044. name:item.paramName,
  1045. value:parSplit[0],
  1046. dataType:item.paramType,
  1047. refType:'fixed'
  1048. }
  1049. par.parameters.push(par1)
  1050. }
  1051. else{
  1052. var par1 = {
  1053. name:item.paramName,
  1054. ref:item.paramValue,
  1055. dataType:item.paramType,
  1056. refType:'ref'
  1057. }
  1058. par.parameters.push(par1)
  1059. }
  1060. }
  1061. else{
  1062. var par1 = {
  1063. name:item.paramName,
  1064. value:null,
  1065. dataType:item.paramType,
  1066. refType:'fixed'
  1067. }
  1068. par.parameters.push(par1)
  1069. }
  1070. })
  1071. nodeDeSer.value = false
  1072. const node = findNode(parNodeid.value)
  1073. node.data.data = par
  1074. // updateNode(node.id, { data: { ...node.data, ...par } });
  1075. // updateNodeInternals(node.id);
  1076. // updateNode(parNodeid.value, {
  1077. // data: par
  1078. // });
  1079. }
  1080. function delWholeFlow(){
  1081. proxy.$confirm('是否删除该模型流程?', '提示', {
  1082. confirmButtonText: '确定',
  1083. cancelButtonText: '取消',
  1084. type: 'warning'
  1085. }).then(() => {
  1086. delFlow(parFlowId.value).then(res => {
  1087. if(res.code === 200){
  1088. proxy.$message({
  1089. message: '删除成功',
  1090. type: 'success'
  1091. });
  1092. getList();
  1093. } else {
  1094. proxy.$message.error('删除失败');
  1095. }
  1096. })
  1097. })
  1098. }
  1099. const toImage = async () => {
  1100. console.log(flowContainer.value)
  1101. await nextTick();
  1102. status.value = '生成图片中...';
  1103. try {
  1104. const canvas = await html2canvas(flowContainer.value, {
  1105. backgroundColor: '#fff', // 白底背景
  1106. scale: 2, // 提高导出清晰度
  1107. useCORS: true, // 支持跨域图片
  1108. });
  1109. // 生成下载链接
  1110. const dataUrl = canvas.toDataURL('image/png');
  1111. const link = document.createElement('a');
  1112. link.href = dataUrl;
  1113. link.download = 'vueflow-diagram.png'; // 文件名
  1114. link.click();
  1115. status.value = '导出成功!';
  1116. setTimeout(() => status.value = '就绪', 3000); // 重置状态
  1117. } catch (error) {
  1118. status.value = `导出失败: ${error.message}`;
  1119. console.error('导出错误:', error);
  1120. }
  1121. };
  1122. function testNode(node){
  1123. dialogVisibleTest.value = true
  1124. }
  1125. function oneSel(){
  1126. if(checkboxGroup1.value.length>1){
  1127. checkboxGroup1.value.splice(0,1)
  1128. }
  1129. }
  1130. async function delNode(node){
  1131. removeNodes([node])
  1132. await nextTick()
  1133. }
  1134. function generateTimestampId() {
  1135. const timestamp = Date.now(); // 13位毫秒时间戳
  1136. const randomNum = Math.floor(Math.random() * 1000); // 3位随机数
  1137. return parseInt(`${timestamp}${randomNum}`); // 拼接后转为整数
  1138. }
  1139. onEdgeClick(({ edge }) => {
  1140. console.log(edges.value,edge)
  1141. edges.value = edges.value.filter(item => item.id !== edge.id)
  1142. });
  1143. async function getTreeLeft(){
  1144. await modelTreeSelect().then(res=>{
  1145. dataTree.value = res.data
  1146. })
  1147. }
  1148. function deepToRaw(obj) {
  1149. // 如果是数组,遍历每个元素并递归处理
  1150. if (Array.isArray(obj)) {
  1151. return obj.map(item => deepToRaw(item));
  1152. }
  1153. // 如果是对象且可能是响应式代理,获取其原始对象并递归处理其属性
  1154. else if (obj !== null && typeof obj === 'object' && (isProxy(obj) || isReactive(obj))) {
  1155. const rawObj = toRaw(obj);
  1156. return Object.fromEntries(
  1157. Object.entries(rawObj).map(([key, value]) => [key, deepToRaw(value)])
  1158. );
  1159. }
  1160. // 如果是普通对象,直接返回
  1161. return obj;
  1162. }
  1163. function saveFlow(){
  1164. var parFlow = toRaw(toObject())
  1165. parFlow.nodes.forEach(item => {
  1166. if(item.id==='1'){
  1167. item.type='startNode'
  1168. }
  1169. if(item.id==='2'){
  1170. item.type='endNode'
  1171. item.data.parameters = tableDataCanEnd.value;
  1172. // item.data.parameters.splice(0, 0, ...tableDataCanEnd.value);
  1173. console.log(item)
  1174. // item.data.parameters.splice(0, item.data.parameters.length, ...tableDataCanEnd.value);
  1175. // item.data.parameters = JSON.parse(JSON.stringify(item.data.parameters))
  1176. }
  1177. });
  1178. // console.log(JSON.parse(JSON.stringify(parFlow)))
  1179. if(isAdd.value){
  1180. const count = computed(() => store.getters.id)
  1181. var par = {
  1182. appId: count.value,
  1183. flowName:'default',
  1184. flowGraph: JSON.stringify(parFlow)
  1185. }
  1186. addModelingFlow(par).then(res=>{
  1187. if(res.code===200){
  1188. proxy.$message({
  1189. message: '保存成功',
  1190. type: 'success'
  1191. });
  1192. }
  1193. getList()
  1194. })
  1195. }
  1196. else{
  1197. const count = computed(() => store.getters.id)
  1198. var par = {
  1199. flowId: parFlowId.value,
  1200. appId: count.value,
  1201. flowName:'default',
  1202. flowGraph: JSON.stringify(parFlow)
  1203. }
  1204. console.log(par)
  1205. editModelingFlow(par).then(res=>{
  1206. if(res.code===200){
  1207. proxy.$message({
  1208. message: '保存成功',
  1209. type: 'success'
  1210. });
  1211. getList()
  1212. }
  1213. })
  1214. }
  1215. }
  1216. function getPredecessorsNodes(nodeId, edges = getEdges.value, allNodes = getNodes.value){
  1217. // 1. 找到所有直接指向目标节点的边(即 target 为 nodeId 的边)
  1218. const incomingEdges = edges.filter(edge => edge.target === nodeId);
  1219. // 2. 从这些边中提取所有源节点的 ID
  1220. const sourceNodeIds = incomingEdges.map(edge => edge.source);
  1221. // 3. 根据源节点 ID 获取完整的节点对象
  1222. const directPredecessors = allNodes.filter(node => sourceNodeIds.includes(node.id));
  1223. // 4. 递归获取这些直接前置节点的前置节点
  1224. const allPredecessors = [];
  1225. for (const predecessor of directPredecessors) {
  1226. // 将直接前置节点加入结果数组
  1227. allPredecessors.push(predecessor);
  1228. // 递归获取该前置节点的前置节点,并合并到结果数组中
  1229. const predecessorsOfPredecessor = getPredecessorsNodes(predecessor.id, edges, allNodes);
  1230. allPredecessors.push(...predecessorsOfPredecessor);
  1231. }
  1232. // 5. 使用 Set 或其他方法去重(根据节点 ID),因为一个节点可能通过不同路径被多次访问
  1233. const uniquePredecessors = Array.from(new Map(allPredecessors.map(node => [node.id, node])).values());
  1234. return uniquePredecessors;
  1235. };
  1236. function removeDuplicatesAndEmptyItems(arr) {
  1237. const map = new Map();
  1238. return arr.filter(item => {
  1239. // 过滤 null 和 undefined
  1240. if (item === null || item === undefined) return false;
  1241. // 处理对象类型:转为字符串标识去重
  1242. const key = typeof item === 'object'
  1243. ? JSON.stringify(item)
  1244. : item;
  1245. // 若标识不存在于 Map 中,则保留并记录
  1246. if (!map.has(key)) {
  1247. map.set(key, true);
  1248. return true;
  1249. }
  1250. return false;
  1251. });
  1252. }
  1253. function parseJSONWithComments(jsonString) {
  1254. // 移除单行注释(//)和多行注释(/* */)
  1255. const cleanedJson = jsonString.replace(/\/\/.*|\/\*[\s\S]*?\*\//g, '');
  1256. try {
  1257. return JSON.parse(cleanedJson);
  1258. } catch (error) {
  1259. console.error("解析失败:", error.message);
  1260. return null;
  1261. }
  1262. }
  1263. onNodeClick(({event, node}) => {
  1264. nodeEnd.value = false
  1265. nodeStart.value = false
  1266. nodeDeSer.value = false
  1267. parNodeid.value = node.id
  1268. parTitle.value = node.data.name
  1269. tableDataCan.value = []
  1270. optionsCan.value = []
  1271. tableDataCanOut.value = []
  1272. var parNode = getPredecessorsNodes(node.id)
  1273. var a = JSON.parse(JSON.stringify(parNode))
  1274. a.forEach((item,index) => {
  1275. console.log(item.data)
  1276. if(item.data.label!=='开始'&&item.data.service&&item.data.service.rpcontent){
  1277. item.data.service.rpcontent = parseJSONWithComments(item.data.service.rpcontent)
  1278. console.log((item.data.service.rpcontent))
  1279. var parShu = Object.keys((item.data.service.rpcontent))
  1280. parShu.forEach(item1=>{
  1281. var count = index+1
  1282. var par = {
  1283. ref:item.data.id + '.' + item1,
  1284. name:item.data.label + ':' + item1
  1285. }
  1286. optionsCan.value.push(par)
  1287. })
  1288. }
  1289. if(tableDataCanStart.value.length>0){
  1290. tableDataCanStart.value.forEach(item=>{
  1291. var par = {
  1292. ref:item.value,
  1293. name:item.name
  1294. }
  1295. optionsCan.value.push(par)
  1296. })
  1297. }
  1298. })
  1299. console.log(optionsCan.value)
  1300. if(node.data.label==='结束'){
  1301. nodeEnd.value = true
  1302. }
  1303. if(node.data.label==='开始'){
  1304. nodeStart.value = true
  1305. }
  1306. if(node.data.nodeType==='SERVICE'){
  1307. getSerDe(node.data.id).then(res=>{
  1308. serviceRqtype.value = res.data.ptService.rqtype
  1309. servieName.value = res.data.ptService.name
  1310. tableDataCan.value = res.data.list
  1311. var par1 = parseJSONWithComments(res.data.ptService.rpcontent)
  1312. var parShu = Object.keys((par1))
  1313. console.log(parShu)
  1314. parShu.forEach(item1=>{
  1315. var par = {
  1316. paramCode:item1,
  1317. paramValue:''
  1318. }
  1319. tableDataCanOut.value.push(par)
  1320. })
  1321. // tableDataCanOut.value = parseJSONWithComments(res.data.ptService.rpcontent)
  1322. serviceUrl.value = res.data.ptService.url
  1323. nodeDeSer.value = true
  1324. })
  1325. }
  1326. if(node.data.nodeType==='tool'){
  1327. console.log(node.data.value)
  1328. if(node.data.value==='表输入组件'){
  1329. dialogVisibleBiao.value = true
  1330. }
  1331. if(node.data.value==='Excel文件输入组件'){
  1332. dialogVisibleExcel.value = true
  1333. }
  1334. if(node.data.value==='csv输入组件'){
  1335. dialogVisibleCsv.value = true
  1336. }
  1337. }
  1338. });
  1339. /**
  1340. * 开始拖拽选项的事件
  1341. * @param event
  1342. * @param data
  1343. */
  1344. function onDragStart(event, data) {
  1345. if(data.nodeType==='SERVICE'||data.nodeType==='tool'){
  1346. if (event.dataTransfer) {
  1347. event.dataTransfer.setData('application/vueflow', data)
  1348. event.dataTransfer.effectAllowed = 'move'
  1349. }
  1350. draggedData.value = data
  1351. isDragging.value = true
  1352. document.addEventListener('drop', onDragEnd)
  1353. }
  1354. }
  1355. /**
  1356. * 拖拽到画布vueflow的事件
  1357. * @param event
  1358. */
  1359. function onDragOver(event) {
  1360. event.preventDefault()
  1361. if (draggedData.value) {
  1362. isDragOver.value = true
  1363. if (event.dataTransfer) {
  1364. event.dataTransfer.dropEffect = 'move'
  1365. }
  1366. }
  1367. }
  1368. /**
  1369. * 拖拽放下的事件
  1370. * @param event
  1371. */
  1372. async function onDrop(event) {
  1373. const position = screenToFlowCoordinate({ x: event.clientX, y: event.clientY });
  1374. const nodeId = generateTimestampId();
  1375. // 1. 同步创建节点
  1376. const newNode = {
  1377. id: nodeId,
  1378. type: 'special',
  1379. position,
  1380. data: copyObject(draggedData.value) // 先使用本地数据
  1381. };
  1382. addNodes(newNode);
  1383. // 2. 异步获取数据并更新
  1384. const res = await getSerDe(draggedData.value.id);
  1385. const fullData = {
  1386. ...res.data.ptService,
  1387. returnList: res.data.returnList
  1388. };
  1389. // 3. 等待节点渲染完成后再修正位置
  1390. nextTick(() => {
  1391. updateNode(nodeId, { data: { ...newNode.data, ...fullData } });
  1392. updateNodeInternals(nodeId); // 强制更新节点布局[1](@ref)
  1393. });
  1394. // console.log(toObject())
  1395. }
  1396. /**
  1397. * 拖拽到画布外面的的事件
  1398. */
  1399. function onDragLeave() {
  1400. isDragOver.value = false
  1401. }
  1402. /**
  1403. * 拖拽结束
  1404. */
  1405. function onDragEnd() {
  1406. isDragging.value = false
  1407. isDragOver.value = false
  1408. draggedData.value = null
  1409. document.removeEventListener('drop', onDragEnd)
  1410. }
  1411. function onConnect(params) {
  1412. addEdges({ ...params, ...defaultEdgeStyle })
  1413. }
  1414. function back(){
  1415. proxy.$router.push({ path: '/standardization/modelUsing' });
  1416. }
  1417. /** 查询模型列表 */
  1418. function getModelList() {
  1419. getModelList2(modelQueryParams.value).then(res => {
  1420. loading.value = false;
  1421. modelOptions.value = res.data;
  1422. });
  1423. }
  1424. /** 通过条件过滤节点 */
  1425. const filterNode = (value, data) => {
  1426. if (!value) return true;
  1427. return data.label.indexOf(value) !== -1;
  1428. };
  1429. /** 查询流程图 */
  1430. function getList() {
  1431. const count = computed(() => store.getters.id)
  1432. console.log(count.value);
  1433. var par = {
  1434. appId: count.value
  1435. }
  1436. getModelingDe(par).then(res => {
  1437. if(res.data.length===0){
  1438. isAdd.value = true
  1439. nodes.value = [
  1440. { id: '1', position: { x: -600, y: -300 }, data: { label: '开始' }, type: 'special' },
  1441. { id: '2', position: { x: 150, y: 100 }, data: { label: '结束' }, type: 'special' },
  1442. ]
  1443. }
  1444. else{
  1445. isAdd.value = false
  1446. var a = JSON.parse(JSON.parse(JSON.stringify(res.data[0].flowGraph)))
  1447. console.log(a)
  1448. a.nodes.forEach(item=>{
  1449. item.type = 'special'
  1450. if(item.id==='2'&&item.data){
  1451. tableDataCanEnd.value = item.data.parameters
  1452. }
  1453. })
  1454. nodes.value = a.nodes
  1455. edges.value = a.edges
  1456. zoom.value = a.zoom
  1457. zoomTo(zoom.value)
  1458. parFlowId.value = res.data[0].flowId
  1459. }
  1460. });
  1461. }
  1462. onMounted(() => {
  1463. getTreeLeft()
  1464. getList();
  1465. })
  1466. watch(nodes, (newNodes) => {
  1467. console.log('当前节点列表:', newNodes) // 实时输出最新数据
  1468. }, { deep: true })
  1469. </script>
  1470. <style>
  1471. @import '@vue-flow/core/dist/style.css';
  1472. @import '@vue-flow/core/dist/theme-default.css';
  1473. </style>
  1474. <style scoped>
  1475. .custom-no-border :deep(.el-input__wrapper) {
  1476. box-shadow: none !important; /* 移除默认阴影(即边框) */
  1477. border: none !important; /* 双重保障 */
  1478. }
  1479. /* 处理悬停和聚焦状态 */
  1480. .custom-no-border :deep(.el-input__wrapper:hover),
  1481. .custom-no-border :deep(.el-input__wrapper:focus-within) {
  1482. box-shadow: none !important;
  1483. border: none !important;
  1484. }
  1485. :deep(.treeLeft) .el-tree-node__content {
  1486. display: flex !important;
  1487. height: 28px; /* 按设计稿调整高度 */
  1488. align-items: center;
  1489. padding-top: 0 !important;
  1490. }
  1491. :deep(.treeLeft) .el-tree-node__content:hover {
  1492. background-color: #e9e9eb;
  1493. }
  1494. :deep(.treeLeft) .el-tree-node__content:active {
  1495. background-color: rgka(69,157,255,0.1) !important;
  1496. }
  1497. /* 选中态(Active) */
  1498. :deep(.treeLeft) .el-tree-node.is-current > .el-tree-node__content {
  1499. background-color: #c6e2ff !important;
  1500. }
  1501. </style>