| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791 |
- <template>
- <div>
- <!-- <el-button type="primary" @click="saveFlow">测试</el-button> -->
- <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;">
- <el-icon size="large" style="cursor: pointer" @click="back"><Back /></el-icon>
- <el-button @click="delWholeFlow" style="margin-left: auto;" type="danger" size="mini">删除</el-button>
- <!-- <el-button style="margin-left: 1%;" type="info" size="mini" plain @click="toImage">导出为图片</el-button> -->
- <el-button style="margin-left: 1%;" type="primary" size="mini" @click="saveFlow">保存</el-button>
- <el-button style="margin-left: 1%;" type="primary" size="mini" @click="startTest">试运行</el-button>
- </div>
- <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;">
- <div style="display: flex;margin-left: 3%;margin-top: 3%;align-items: center;justify-content: space-between;width: 95%;">
- <el-tag class="ml-2" style="" type="warning">服务</el-tag>
- <div style="margin-left: 4%;">
- {{servieName}}
- </div>
- <el-icon @click="closeDe" style="margin-left: auto;cursor: pointer;"><Close /></el-icon>
- </div>
- <div>
- <div style="display: flex;width: 90%;margin-left: 5%;margin-top:10%;align-items: center;justify-content: space-between;">
- <el-input v-model="serviceUrl" style="width: 90%;" placeholder="Please input" disabled>
- <template #prepend>
- <div v-if="serviceRqtype==='GET'" style="color: #67C23A;background-color: transparent;">
- GET
- </div>
- <div v-if="serviceRqtype==='POST'" style="color: #409EFF;background-color: transparent;">
- POST
- </div>
- </template>
- </el-input>
- <svg-icon @click="testOne" icon-class="startTest" style="margin-left: auto;width: 50px;height: 25px;cursor: pointer;"/>
- </div>
- <div style="display: flex;width: 90%;margin-left: 5%;margin-top:10%;align-items: center;justify-content: space-between;">
- <el-table :data="tableDataCan" border style="width: 100%">
- <el-table-column prop="paramCode" label="参数名" width="" />
- <el-table-column prop="paramValue" label="参数值" width="">
- <template #default="scope">
- <div style="width:100%;">
- <el-select
- v-model="scope.row.paramValue"
- filterable
- @change="handleSelectChange"
- allow-create
- default-first-option
- :reserve-keyword="false"
- style="width: 100%"
- >
- <el-option
- v-for="item in optionsCan"
- :label="item.name"
- :value="item.ref"
- />
- </el-select>
- </div>
- </template>
- </el-table-column>
- </el-table>
- </div>
- <div style="display: flex;width: 90%;margin-left: 5%;margin-top:10%;align-items: center;justify-content: space-between;">
- <el-form ref="deptRef" :model="form" label-width="80px" label-position="top">
- <el-form-item label="BODY">
- <el-radio-group v-model="form.bodyType">
- <el-radio value="none">none</el-radio>
- <el-radio value="form-data">form-data</el-radio>
- <el-radio value="x-www-form-urlencoded">x-www-form-urlencoded</el-radio>
- <el-radio value="JSON">JSON</el-radio>
- <el-radio value="raw">raw</el-radio>
- </el-radio-group>
- </el-form-item>
- <el-row :gutter="48">
- <el-col :span="12">
- <el-form-item label="失败处理:" prop="name" style="">
- <div style="display: flex;width: 100%;justify-content: space-between;">
- <el-select v-model="form.errorPolicy" style="width: 100%">
- <el-option label="报错" value="ABORT"></el-option>
- <el-option label="忽视" value="IGNORE"></el-option>
- <el-option label="重连" value="RETRY"></el-option>
- </el-select>
- </div>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="失败重连次数:" prop="" style="">
- <div style="display: flex;width: 100%;justify-content: space-between;">
- <el-input-number v-model="form.retryCount" :min="1" style="width: 100%" :max="30"/>
- <div style="display: flex;">
- </div>
- </div>
- </el-form-item>
- </el-col>
- </el-row>
- <el-form-item label="输出">
- <el-table :data="tableDataCanOut" border style="width: 100%">
- <el-table-column prop="paramCode" label="参数名" width="" />
- <el-table-column prop="paramValue" label="参数值" width="">
- <template #default="scope">
- <div style="width: 100%;">
- <el-input placeholder="" type="primary" class="noBor" v-model="scope.row.paramValue" size="mini" text style="margin-left: 0%;"></el-input>
- </div>
- </template>
- </el-table-column>
- </el-table>
- </el-form-item>
- </el-form>
- </div>
- </div>
- </div>
- <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;">
- <div style="display: flex;margin-left: 3%;margin-top: 3%;align-items: center;justify-content: space-between;width: 95%;">
- <el-tag class="ml-2" style="" type="warning">开始节点</el-tag>
- <el-icon @click="saveStartNode" style="margin-left: auto;cursor: pointer;"><Close /></el-icon>
- </div>
- <div>
- <el-button @click="addStart" style="margin-top: 5%;margin-left: 1%;" type="success" size="mini" :disabled="isEdit" plain>新增参数</el-button>
- <div style="display: flex;width: 98%;margin-left: 1%;margin-top:2%;align-items: center;justify-content: space-between;">
- <el-table
- style="margin-top: 1%;width: 100%;"
- :data="tableDataCanStart"
- :cell-style="{ textAlign: 'center',padding:'2px 0' }"
- :header-cell-style="{ textAlign: 'center'}"
- :row-style="{ height: heightAll*0.01+'px',fontSize: '16px',textAlign:'center' }"
- border >
- <el-table-column prop="itemName" label="参数名称(必填)">
- <template #default="scope">
- <div style="width: 100%;">
- <el-input placeholder="请填写参数名称" type="primary" class="noBor" v-model="scope.row.name" size="mini" text style="margin-left: 0%;"></el-input>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="itemName" label="参数类型(必填)" width="200">
- <template #default="scope">
- <div style="width: 100%;">
- <el-select
- v-model="scope.row.dataType"
- class="noBorSel"
-
- placeholder=""
- style="width: 100%;margin-left: 0%;"
- >
- <el-option
- v-for="item in optionsType"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="itemName" label="参数说明" >
- <template #default="scope">
- <div style="width: 100%;">
- <el-input type="primary" class="noBor" v-model="scope.row.description" size="mini" text style="margin-left: 0%;"></el-input>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="itemName" label="是否必填" >
- <template #default="scope">
- <div style="width: 100%;">
- <el-checkbox v-model="scope.row.required" size="large" />
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="itemName" label="参数值" >
- <template #default="scope">
- <div style="width: 100%;">
- <el-input type="primary" class="noBor" v-model="scope.row.paramValue" size="mini" text style="margin-left: 0%;"></el-input>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="address" label="操作" width="100">
- <template #default="scope">
- <div style="width: 100%;">
- <el-button type="danger" @click="delCanStart(scope.$index)" size="mini" text style="margin-left: 0%;">删除</el-button>
- </div>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </div>
- </div>
- <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;">
- <div style="display: flex;margin-left: 3%;margin-top: 3%;align-items: center;justify-content: space-between;width: 95%;">
- <el-tag class="ml-2" style="" type="warning">结束节点</el-tag>
- <el-icon @click="saveEndNode" style="margin-left: auto;cursor: pointer;"><Close /></el-icon>
- </div>
- <div>
- <el-button @click="addEnd" style="margin-top: 5%;margin-left: 1%;" type="success" size="mini" :disabled="isEdit" plain>新增参数</el-button>
- <div style="display: flex;width: 98%;margin-left: 1%;margin-top:2%;align-items: center;justify-content: space-between;">
- <el-table :data="tableDataCanEnd" border style="width: 100%">
- <el-table-column prop="paramCode" label="参数名(必填)" width="">
- <template #default="scope">
- <div style="width: 100%;">
- <el-input placeholder="" type="primary" class="noBor" v-model="scope.row.name" size="mini" text style="margin-left: 0%;"></el-input>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="itemName" label="参数类型(必填)">
- <template #default="scope">
- <div style="width: 100%;">
- <el-select
- v-model="scope.row.dataType"
- class="noBorSel"
- placeholder=""
- style="width: 100%;margin-left: 0%;"
- >
- <el-option
- v-for="item in optionsType"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="paramValue" label="参数值" width="">
- <template #default="scope">
- <div style="width:100%;">
- <el-select
- v-model="scope.row.paramValue"
- filterable
- default-first-option
- :reserve-keyword="false"
- style="width: 100%"
- >
- <el-option
- v-for="item in optionsCan"
- :label="item.name"
- :value="item.ref"
- />
- </el-select>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="address" label="操作" width="100">
- <template #default="scope">
- <div style="width: 100%;">
- <el-button type="danger" @click="delEnd(scope.$index)" size="mini" text style="margin-left: 0%;">删除</el-button>
- </div>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </div>
- </div>
- <div style="display: flex;height: 87vh;width: 100%;padding-top:2%;justify-items: center;">
- <div style="width: 20%;margin-left: 1%;overflow-y: auto;margin-top: 0.5%;">
- <el-checkbox-group v-model="checkboxGroup1" size="" style="margin-left: 5%;" @change="oneSel">
- <el-checkbox-button key="service" label="service">
- 服务
- </el-checkbox-button>
- <el-checkbox-button key="tool" label="tool">
- 工具
- </el-checkbox-button>
- </el-checkbox-group>
- <el-input
- v-if="checkboxGroup1.includes('service')"
- v-model="inputNode"
- style="width:90%;margin-left: 5%;background-color: #ebeef5;margin-top: 1%;"
- class="w-50 m-2"
- :prefix-icon="Search"
- />
- <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"
- @node-click="handleNodeClick" node-key="id" style="margin-left: 5%;margin-top: 3%;width: 90%;background-color: transparent;" default-expand-all :key="valueKet">
- <template #default="{ node, data }">
- <span style="justify-content: space-between;display: flex;width: 100%;align-items: center;" :draggable="true" @dragstart="onDragStart($event,data)">
- <div class="custom-tree-node">
- <!-- <el-tag v-if="data.nodeType=='MODEL'" class="ml-2" type="warning">模型</el-tag> -->
- <svg-icon icon-class="model2" style="color: #eebe77;" v-if="data.nodeType=='MODEL'"/>
- <!-- <el-tag class="ml-2">
- 服务
- </el-tag> -->
- <svg-icon icon-class="model" dstyle="color: #13E03B;" v-if="data.nodeType=='SERVICE'"/>
- <svg-icon svg-icon icon-class="cate" style="color: red;" v-if="data.nodeType=='TREE'"/>
- <span>{{ node.label }}</span>
- </div>
- </span>
- </template>
- </el-tree>
- <el-tree v-if="!checkboxGroup1.includes('service')" :expand-on-click-node="false" ref="treeRef" :current-node-key="currentNodeKey" class="treeLeft" :data="dataTreeTool"
- @node-click="handleNodeClick" node-key="id" style="margin-left: 5%;margin-top: 3%;width: 90%;background-color: transparent;" default-expand-all :key="valueKet">
- <template #default="{ node, data }">
- <span style="justify-content: space-between;display: flex;width: 120%;align-items: center;" :draggable="true" @dragstart="onDragStart($event,data)">
- <div class="custom-tree-node">
- <svg-icon icon-class="liuchengshuru" style="color: #eebe77;" v-if="data.label=='输入'"/>
- <svg-icon icon-class="liuchengEx" dstyle="color: #13E03B;" v-if="data.label=='Excel文件输入组件'"/>
- <svg-icon icon-class="liuchengbiao" dstyle="color: #13E03B;" v-if="data.label=='表输入组件'"/>
- <svg-icon icon-class="liuchengcsv" dstyle="color: #13E03B;" v-if="data.label=='csv输入组件'"/>
- <svg-icon icon-class="lczh" dstyle="color: #13E03B;" v-if="data.label=='转换'"/>
- <svg-icon icon-class="lczhzj" dstyle="color: #13E03B;" v-if="data.label=='转换组件'"/>
- <svg-icon icon-class="lcpx" dstyle="color: #13E03B;" v-if="data.label=='排序记录'"/>
- <svg-icon icon-class="lczd" dstyle="color: #13E03B;" v-if="data.label=='字段派生器'"/>
- <svg-icon icon-class="lcsc" dstyle="color: #13E03B;" v-if="data.label=='输出'"/>
- <svg-icon icon-class="lcbsc" dstyle="color: #13E03B;" v-if="data.label=='表输出组件'"/>
- <span style="margin-left: 2%;"> {{ node.label }}</span>
- </div>
- </span>
- </template>
- </el-tree>
- </div>
- <div ref="flowContainer" style="width: 80%;">
- <VueFlow ref="vueFlowRef" style="background-color: #EFEFF4;margin-top: 0.5%;" :nodes="nodes" :viewport="zoom" :edges="edges" @drop="onDrop" @dragover="onDragOver" @dragleave="onDragLeave"
- @node-click="onNodeClick" @connect="onConnect" fit-view-on-init>
- <Controls :showInteractive="false" :showFitView="false"/>
- <template #node-special="specialNodeProps">
- <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">
- <div style='width:100%;font-size:15px;display:flex;align-items:center;height: 3vh;margin-top: 2%;'>
- <div style="margin-left: 0%;font-weight: 500;">
- <svg-icon icon-class="liuchengshuru" style="color: #eebe77;height:20px;width:20px;" v-if="specialNodeProps.data.label=='输入'"/>
- <svg-icon icon-class="liuchengEx" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='Excel文件输入组件'"/>
- <svg-icon icon-class="liuchengbiao" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='表输入组件'"/>
- <svg-icon icon-class="liuchengcsv" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='csv输入组件'"/>
- <svg-icon icon-class="lczh" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='转换'"/>
- <svg-icon icon-class="lczhzj" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='转换组件'"/>
- <svg-icon icon-class="lcpx" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='排序记录'"/>
- <svg-icon icon-class="lczd" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='字段派生器'"/>
- <svg-icon icon-class="lcsc" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='输出'"/>
- <svg-icon icon-class="lcbsc" style="color: #13E03B;height:20px;width:20px;" v-if="specialNodeProps.data.label=='表输出组件'"/>
- </div>
- <div style="margin-left: 10%;">
- <el-input class="custom-no-border" @click.stop="handleInputClick" type="primary" v-model="specialNodeProps.data.label" size="mini" text
- style="margin-left: -20%;font-size: 15px;height: 15px;width: 100%;" ></el-input>
- </div>
- </div>
- <Handle type="source" :position="Position.Right"/>
- <Handle type="target" :position="Position.Left"/>
- </div>
- <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">
- <div style='width:100%;font-size:10px;display:flex;align-items:flex-end;height: 10px;margin-top: 2%;'>
- <img style="width: 15px;height:15px;border-radius: 12px;" src="@/assets/images/icon-Start-v2.jpg" alt="">
- <div style="margin-left: 3%;font-weight: 500;">
- 开始
- </div>
- <el-tag class="ml-2" style="width: 30px;height: 15px;font-size: 7px;margin-left: 6%;" type="info">触发器</el-tag>
- </div>
- <Handle type="source" :position="Position.Right"/>
- </div>
- <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">
- <div style='width:100%;font-size:10px;display:flex;align-items:flex-end;height: 10px;margin-top: 2%;'>
- <img style="width: 15px;height:15px;border-radius: 12px;" src="@/assets/images/icon-Start-v2.jpg" alt="">
- <div style="margin-left: 3%;font-weight: 500;">
- 结束
- </div>
- <div>
- </div>
- </div>
- <Handle type="target" :position="Position.Left"/>
- </div>
- <div v-if="specialNodeProps.data.nodeType=='SERVICE'"
- class="vue-flow__node-default"
- v-loading="specialNodeProps.data.loading"
- style="border: 0.5px solid #c8c9cc;border-radius: 6px;border-radius: 6px;min-height: 8vh;min-width: 13vw">
- <div style='width:100%;font-size:10px;display:flex;align-items:flex-end;height: 10px;margin-top: 2%;justify-content: space-between;'>
- <img style="width: 15px;height:15px;border-radius: 12px;" src="@/assets/images/icon-HTTP.png" alt="">
- <div style="margin-left:3%;font-weight: 500;">
- <el-input class="custom-no-border" placeholder="" type="primary" v-model="specialNodeProps.data.label" size="mini" text
- style="margin-left: 0%;font-size: 10px;height: 15px;width: 100%;" ></el-input>
- <!-- {{ specialNodeProps.data.label}} -->
- </div>
- <el-icon v-if="specialNodeProps.data.isSuccess" style="cursor: pointer;margin-left: auto;color: #67C23A;"><SuccessFilled /></el-icon>
- <el-tooltip :content="specialNodeProps.data.erMsg" placement="top" effect="light">
- <el-icon v-if="specialNodeProps.data.isFail" style="cursor: pointer;margin-left: auto;color: #F56C6C;"><CircleCloseFilled /></el-icon>
- </el-tooltip>
- <!-- <el-icon @click.stop="testNode(specialNodeProps)" style="cursor: pointer;margin-left: 2%;"><CaretRight /></el-icon> -->
- <el-icon @click.stop="delNode(specialNodeProps)" style="cursor: pointer;color: #F56C6C;margin-left: 2%;"><Delete /></el-icon>
- </div>
- <div style="display: flex;margin-top: 3%;">
- <el-tag class="ml-2" style="width: 35px;height: 20px;font-size: 10px;" type="warning">服务</el-tag>
- </div>
- <div style="display: flex;margin-top: 3%;font-size: 9px;color: #b1b3b8;flex-wrap: wrap;align-items: flex-start;">
- <div style="word-break: break-all;min-width: 0;width: 100%;text-align: left;">
- {{ specialNodeProps.data.service.rqtype+ ':' }}{{ specialNodeProps.data.service.url }}
- </div>
- </div>
- <div style="display: flex;margin-top: 3%;font-size: 9px;color: #b1b3b8;align-items: center;">
- <div>
- 说明:
- </div>
- <div>
- {{ specialNodeProps.data.service.intro }}
- </div>
- </div>
- <Handle type="source" :position="Position.Right"/>
- <Handle type="target" :position="Position.Left"/>
- </div>
- </template>
- <template #edge-custom="specialEdgeProps">
- <div style="height: 1px;color: red;">
- </div>
- </template>
- </VueFlow>
- </div>
- <el-dialog @close="clearFromLev" title="" v-model="dialogVisibleBiao" width="50%" destroy-on-close :key="tableKey">
- <el-form size="mini" :key="tableKey" style="margin-top: 1%;width: 98%;" :model="formJi" label-position="right" ref="formRefJi" label-width="120px" :rules="rulesJi">
- <el-form-item label="连接方式:" prop="" style="display: flex; align-items: center;">
- <el-radio-group v-model="formJi.rptype" class="ml-4" style="display: inline-flex; align-items: center;">
- <el-radio label="1" size="large" style="display: inline-flex; align-items: center;">
- <span style="position: relative; top: -1px">资产表</span>
- </el-radio>
- <el-radio label="2" size="large" style="display: inline-flex; align-items: center;">
- <span style="position: relative; top: -1px">sql</span>
- </el-radio>
- <el-radio label="3" size="large" style="display: inline-flex; align-items: center;">
- <span style="position: relative; top: -1px">数据连接</span>
- </el-radio>
- </el-radio-group>
- </el-form-item>
- <el-form-item label="数据源:" prop="" style="">
- <div style="display: flex;width: 30%;justify-content: space-between;">
- <el-select
- v-model="formJi.type"
-
- style="width: 100%;margin-left: 0%;"
- >
- <el-option
- v-for="item in optionsType"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- </div>
- </el-form-item>
- <el-form-item label="读取模式:" prop="" style="display: flex; align-items: center;">
- <el-radio-group v-model="formJi.rptype" class="ml-4" style="display: inline-flex; align-items: center;">
- <el-radio label="1" size="large" style="display: inline-flex; align-items: center;">
- <span style="position: relative; top: -1px">全量</span>
- </el-radio>
- <el-radio label="2" size="large" style="display: inline-flex; align-items: center;">
- <span style="position: relative; top: -1px">id增量</span>
- </el-radio>
- <el-radio label="3" size="large" style="display: inline-flex; align-items: center;">
- <span style="position: relative; top: -1px">时间范围增量</span>
- </el-radio>
- </el-radio-group>
- </el-form-item>
- <el-form-item label="where条件:">
- <el-input v-model="formJi.intro" style="width: 100%;" :rows="3" resize="none" type="textarea"/>
- </el-form-item>
- </el-form>
- <div style="font-size: 16px;margin-left: 1%">
- 属性字段
- </div>
- <el-table
- style="margin-top: 2%;width: 100%;margin-left: 1%;overflow: auto;"
- :data="tableDataCan"
- :cell-style="{ textAlign: 'center',padding:'3px 0px' }"
- :header-cell-style="{ textAlign: 'center', }"
- max-height="45vh"
- :row-style="{ height: heightAll*0.01+'px',fontSize: '16px',textAlign:'center' }"
- border>
- <el-table-column prop="parName" label="参数英文名">
- <template #default="scope" style="width: 120%;">
- <el-input v-model="scope.row.parEnname" style="width: 120%;margin-left: -10%;"/>
- </template>
- </el-table-column>
- <el-table-column prop="parName" label="参数名称">
- <template #default="scope">
- <el-input v-model="scope.row.parName" style="width: 120%;margin-left: -10%;"/>
- </template>
- </el-table-column>
- <el-table-column prop="parType" label="参数类型" >
- <template #default="scope">
- <el-input v-model="scope.row.parType" style="width: 120%;margin-left: -10%;"/>
- </template>
- </el-table-column>
- <el-table-column prop="parNote" label="操作" width="85">
- <template #default="scope">
- <el-button type="danger" @click="delCan(scope.$index, scope.row)" text size="mini" style="margin-left: 0%;">删除</el-button>
- </template>
- </el-table-column>
- </el-table>
- <template #footer>
- <span class="dialog-footer">
- <el-button size="mini" @click="dialogVisibleBiao = false">取消</el-button>
- <el-button type="primary" @click="saveAdd1Level" size="mini" v-if="show1Lev">
- 提交
- </el-button>
- <el-button type="primary" @click="saveAddNextLevel" size="mini" v-if="!show1Lev">
- 提交
- </el-button>
- </span>
- </template>
- </el-dialog>
- <el-dialog
- :title="title"
- v-model="dialogVisibleExcel"
- width="50%">
- <div>
- <el-form ref="formAddref" :model="formAdd" label-width="100px" class="coz-mg-card" :rules="rulesAdd">
- <el-row :gutter="24">
- <el-col :span="8">
- <el-form-item label="选择附件:">
- <el-upload
- ref="uploadRef"
- style="margin-top: 0%;"
- :limit="1"
- :show-file-list="false"
- :file-list="fileList"
- :headers="upload.headers"
- :on-progress="handlepro"
- :on-success="handleFileSuccess"
- :on-change="handleChange"
- :action="upload.url + '?file=' + upload.updateSupport"
- :auto-upload="false">
- <el-button @click="clearDefault" plain type="primary" size="mini" style="margin-left:auto;width: 80px;" :icon="Upload">上传文件</el-button>
- </el-upload>
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="起始行:" prop="appTitle">
- <el-input-number
- v-model="num"
- class="mx-4"
- :min="1"
- controls-position="right"
- @change="handleChange"
- />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="起始列:" prop="appNote">
- <el-input-number
- v-model="num"
- class="mx-4"
- :min="1"
- controls-position="right"
- @change="handleChange"
- />
- </el-form-item>
- </el-col>
- </el-row>
- </el-form>
- </div>
- <div style="font-size: 16px;margin-left: 1%">
- 属性字段
- </div>
- <el-table
- style="margin-top: 2%;width: 100%;margin-left: 1%;overflow: auto;"
- :data="tableDataCan"
- :cell-style="{ textAlign: 'center',padding:'3px 0px' }"
- :header-cell-style="{ textAlign: 'center', }"
- max-height="45vh"
- :row-style="{ height: heightAll*0.01+'px',fontSize: '16px',textAlign:'center' }"
- border>
- <el-table-column prop="parName" label="参数英文名">
- <template #default="scope" style="width: 120%;">
- <el-input v-model="scope.row.parEnname" style="width: 120%;margin-left: -10%;"/>
- </template>
- </el-table-column>
- <el-table-column prop="parName" label="参数名称">
- <template #default="scope">
- <el-input v-model="scope.row.parName" style="width: 120%;margin-left: -10%;"/>
- </template>
- </el-table-column>
- <el-table-column prop="parType" label="参数类型" >
- <template #default="scope">
- <el-input v-model="scope.row.parType" style="width: 120%;margin-left: -10%;"/>
- </template>
- </el-table-column>
- <el-table-column prop="parNote" label="操作" width="85">
- <template #default="scope">
- <el-button type="danger" @click="delCan(scope.$index, scope.row)" text size="mini" style="margin-left: 0%;">删除</el-button>
- </template>
- </el-table-column>
- </el-table>
- <template #footer>
- <el-button @click="dialogVisibleExcel = false">取消</el-button>
- <el-button v-if="isAdd" type="primary" @click="submitAdd">确定</el-button>
- <el-button v-if="!isAdd" type="primary" @click="subEdit">确定</el-button>
- </template>
- </el-dialog>
- <el-dialog @close="clearFromLev" title="" v-model="dialogVisibleCsv" width="50%" destroy-on-close :key="tableKey">
- <el-form ref="formAddref" :model="formAdd" label-width="100px" class="coz-mg-card" :rules="rulesAdd">
- <el-row :gutter="24">
- <el-col :span="8">
- <el-form-item label="上传附件:">
- <el-upload
- ref="uploadRef"
- style="margin-top: 0%;"
- :limit="1"
- :show-file-list="false"
- :file-list="fileList"
- :headers="upload.headers"
- :on-progress="handlepro"
- :on-success="handleFileSuccess"
- :on-change="handleChange"
- :action="upload.url + '?file=' + upload.updateSupport"
- :auto-upload="false">
- <el-button @click="clearDefault" plain type="primary" size="mini" style="margin-left:auto;width: 80px;" :icon="Upload">选择文件</el-button>
- </el-upload>
- <el-button @click="clearDefault" plain type="primary" size="mini" style="margin-left:auto;width: 80px;" :icon="Upload">解析文件</el-button>
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="" prop="">
-
- </el-form-item>
- </el-col>
- <el-col :span="8">
- </el-col>
- </el-row>
- </el-form>
- <div style="font-size: 16px;margin-left: 1%">
- 属性字段
- </div>
- <el-table
- style="margin-top: 2%;width: 100%;margin-left: 1%;overflow: auto;"
- :data="tableDataCan"
- :cell-style="{ textAlign: 'center',padding:'3px 0px' }"
- :header-cell-style="{ textAlign: 'center', }"
- max-height="45vh"
- :row-style="{ height: heightAll*0.01+'px',fontSize: '16px',textAlign:'center' }"
- border>
- <el-table-column prop="parName" label="参数英文名">
- <template #default="scope" style="width: 120%;">
- <el-input v-model="scope.row.parEnname" style="width: 120%;margin-left: -10%;"/>
- </template>
- </el-table-column>
- <el-table-column prop="parName" label="参数名称">
- <template #default="scope">
- <el-input v-model="scope.row.parName" style="width: 120%;margin-left: -10%;"/>
- </template>
- </el-table-column>
- <el-table-column prop="parType" label="参数类型" >
- <template #default="scope">
- <el-input v-model="scope.row.parType" style="width: 120%;margin-left: -10%;"/>
- </template>
- </el-table-column>
- <el-table-column prop="parNote" label="操作" width="85">
- <template #default="scope">
- <el-button type="danger" @click="delCan(scope.$index, scope.row)" text size="mini" style="margin-left: 0%;">删除</el-button>
- </template>
- </el-table-column>
- </el-table>
- <template #footer>
- <span class="dialog-footer">
- <el-button size="mini" @click="dialogVisibleBiao = false">取消</el-button>
- <el-button type="primary" @click="saveAdd1Level" size="mini" v-if="show1Lev">
- 提交
- </el-button>
- <el-button type="primary" @click="saveAddNextLevel" size="mini" v-if="!show1Lev">
- 提交
- </el-button>
- </span>
- </template>
- </el-dialog>
- <el-dialog @close="clearFromLev" :title="titleTest" v-model="dialogVisibleTest" width="50%" destroy-on-close :key="tableKey">
- <el-form ref="formAddref" :model="formAdd" label-width="100px" class="coz-mg-card" :rules="rulesAdd">
- <el-form-item label="输入测试值:">
- <el-input v-model="formJi.intro" :placeholder="testAttention" style="width: 100%;" :rows="15" resize="none" type="textarea"/>
- </el-form-item>
- </el-form>
- <div style="font-size: 16px;margin-left: 1%">
- 属性字段
- </div>
- <template #footer>
- <span class="dialog-footer">
- <el-button size="mini" @click="dialogVisibleBiao = false">取消</el-button>
- <el-button type="primary" @click="saveAdd1Level" size="mini" v-if="show1Lev">
- 提交
- </el-button>
- <el-button type="primary" @click="saveAddNextLevel" size="mini" v-if="!show1Lev">
- 提交
- </el-button>
- </span>
- </template>
- </el-dialog>
- <el-dialog @close="" title="试运行结果" v-model="dialogVisibleSart" width="50%" destroy-on-close :key="tableKey">
- <el-input
- v-model="outputData"
- :autosize="{ minRows: 10, maxRows: 15 }"
- type="textarea"
- placeholder="Please input"
- />
- <template #footer>
- <span class="dialog-footer">
- <el-button type="primary" @click="dialogVisibleSart = false" size="mini">
- 确定
- </el-button>
- </span>
- </template>
- </el-dialog>
- </div>
- </div>
- </template>
- <script setup>
- import html2canvas from 'html2canvas';
- import { Controls } from '@vue-flow/controls'
- import '@vue-flow/controls/dist/style.css'
- import '@vue-flow/core/dist/theme-default.css';
- import { Plus,Search } from '@element-plus/icons-vue'
- import { toRaw, isReactive, isProxy,onUnmounted } from 'vue';
- import beautify from 'json-beautify';
- import DynamicMap from '@/components/DynamicMap/index.vue'
- import {Promotion} from '@element-plus/icons-vue'
- import { onMounted, ref,onBeforeUnmount } from 'vue'
- import {useVueFlow, VueFlow ,MarkerType } from '@vue-flow/core'
- import SpecialNode from './components/SpecialNode.vue'
- import SpecialEdge from './components/SpecialEdge.vue'
- import {getPtServiceList,getSerDe} from "@/api/service/info.js";
- import { getToken } from '@/utils/auth'
- import {getModelList2} from "@/api/register/regCom.js";
- import {copyObject} from "@/utils/index.js";
- import {getModelingDe,addModelingFlow,editModelingFlow,delFlow,runflow} from "@/api/standardization/modeling.js";
- import { useStore } from 'vuex';
- import {Handle, Position} from '@vue-flow/core'
- import { computed } from 'vue';
- import { modelTreeSelect,testService } from "@/api/service/info";
- // import { toRaw, isReactive, isProxy } from 'vue';
- const {
- snapToGrid,
- addEdges,
- onEdgeClick,
- addNodes,
- removeNodes,
- updateNodeInternals,
- screenToFlowCoordinate,
- onNodesInitialized,
- updateNode,
- onNodeClick,
- getNodes,
- zoomTo,
- toObject,
- getEdges,
- findNode
- } = useVueFlow()
- snapToGrid.value = true
- const parNodeid = ref('')
- const formAdd = ref({
- appTitle: '',
- appNote: '',
- });
- const parSerList = ref([])
- const tableDataCanStart = ref([])
- const dialogVisibleSart = ref(false)
- const testAttention = ref()
- const tableDataCanEnd = ref([])
- const outputData = ref()
- const dialogVisibleTest = ref(false)
- const dialogVisibleCsv = ref(false)
- const formAddref = ref()
- const dialogVisibleExcel = ref(false)
- const rulesAdd = reactive({
- appTitle: [{ required: true, message: '必填', trigger: 'blur' }],
- appNote: [{ required: true, message: '必填', trigger: 'blur' }],
- });
- const titleTest = ref('')
- const upload = reactive({
- // 是否显示弹出层(用户导入)
- open: false,
- // 弹出层标题(用户导入)
- title: "",
- // 是否禁用上传
- isUploading: false,
- // 是否更新已经存在的用户数据
- updateSupport: '',
- // 设置上传的请求头部
- headers: { Authorization: "Bearer " + getToken() },
- // 上传的地址
- url: import.meta.env.VITE_APP_BASE_API + "/common/upload"
- });
- const dialogVisibleBiao = ref(false)
- const vueFlowModel = ref();
- const formJi = ref({
- name:'',
- cateCode:'',
- type:'',
- url:'',
- rqtype:'',
- rptype:'',
- intro:'',
- });
- const nodeEnd = ref(false)
- const nodeStart = ref(false)
- const optionsCan = ref()
- const parTitle = ref()
- const rulesJi = reactive({
- name: [{ required: true, message: '必填', trigger: 'blur' }],
- url: [{ required: true, message: '必填', trigger: 'blur' }],
- cateCode: [{ required: true, message: '必填', trigger: 'blur' }],
- });
- const optionsType = ref([
- {
- label:"string",
- value:'string'
- },
- {
- label:"int",
- value:'int'
- },
- {
- label:"boolean",
- value:'boolean'
- },
- {
- label:"array",
- value:'array'
- },
- {
- label:"object",
- value:'object'
- },
- {
- label:"number",
- value:'number'
- },
- {
- label:"null",
- value:'null'
- },
- {
- label:"any",
- value:'any'
- },
-
- ])
- const dataTreeTool = ref([
- {
- label:'输入',
- value:'输入',
- children:[
- {
- label:'表输入组件',
- nodeType:'tool',
- value:'表输入组件',
- },
- {
- label:'Excel文件输入组件',
- value:'Excel文件输入组件',
- nodeType:'tool',
- },
- {
- label:'csv输入组件',
- nodeType:'tool',
- value:'csv输入组件',
- },
- ]
- },
- {
- label:'转换',
- value:'转换',
- children:[
- {
- label:'转换组件',
- nodeType:'tool',
- value:'转换组件',
- },
- {
- label:'排序记录',
- nodeType:'tool',
- value:'排序记录',
- },
- {
- label:'字段派生器',
- nodeType:'tool',
- value:'字段派生器',
- },
- ]
- },
- {
- label:'输出',
- value:'输出',
- children:[
- {
- label:'表输出组件',
- nodeType:'tool',
- value:'表输出组件',
- },
- ]
- },
- ])
- const tableDataCanOut = ref([]);
- const flowContainer = ref(null);
- const status = ref('就绪');
- const zoom = ref();
- const inputNode = ref('');
- const vueFlowRef = ref(null);
- const treeRef = ref(null);
- const servieName = ref()
- const serviceRqtype = ref()
- const serviceUrl = ref()
- const parFlowId = ref()
- const tableDataCan = ref()
- const nodeDeSer = ref(false)
- const isAdd = ref(false)
- const {proxy} = getCurrentInstance();
- const modelQueryParams = ref({
- name: undefined,
- isPublic: '0',
- });
- const toolQueryParams = ref({
- name: undefined,
- });
- const dataTree = ref([]);
- const modelOptions = ref(undefined);
- const modelId = ref(undefined);
- const loading = ref(true);
- const checkboxGroup1 = ref(['service'])
- const toolType = ref('0');
- const serviceList = ref([]);
- const serInfo = ref()
- const defaultEdgeStyle = {
- style: {
- stroke: '#79bbff',
- strokeWidth: 2,
- type: 'bezier',
- fill: 'none' // 避免截图时出现黑色背景 [4](@ref)
- },
- markerEnd: {
- type: MarkerType.Arrow,
- color: '#79bbff', // 箭头颜色
- width: 15, // 箭头宽度
- height: 15 // 箭头高度
- }
- };
- const draggedData = ref(undefined);
- const isDragging = ref(false);
- const isDragOver = ref(false);
- const nodes = ref([
- { id: '1', position: { x: -600, y: -300 }, data: { label: '开始' }, type: 'special' },
- { id: '2', position: { x: 150, y: 100 }, data: { label: '结束' }, type: 'special' },
- ]);
- const edges = ref([]);
- const form = ref({
- bodyType: 'none',
- errorPolicy: 'ABORT',
- retryCount: 0,
- });
- const title = ref('')
- const open = ref(false)
- const store = useStore();
- watch(inputNode, (val) => {
- treeRef.value?.filter(val); // 调用树的过滤方法
- });
- function clearSt(){
- tableDataCanStart.value = []
- }
- function showData(){
- dialogVisibleSart.value = true
- }
- function handleInputClick(){
- console.log(nodes.value)
- }
- const handleSelectChange = (selectedValue) => {
- const lastValue = Array.isArray(selectedValue)
- ? selectedValue[selectedValue.length - 1]
- : selectedValue;
- const isNewOption = !optionsCan.value.some(item => item.ref === lastValue);
-
- if (isNewOption) {
- const newValue = `${lastValue}:fixed`; // 生成新值
- // 1. 更新数据源
- optionsCan.value.push({ name: lastValue, ref: newValue });
- // 2. ⭐ 关键:手动更新 v-model 绑定值
- scope.row.paramValue = newValue; // 同步更新绑定值[10](@ref)
- }
- saveNode()
- };
- function addEnd(){
- console.log(tableDataCanEnd.value)
- tableDataCanEnd.value.push({})
- }
- function delEnd(index){
- tableDataCanEnd.value.splice(index,1)
- }
- function testOne(){
- var par = serInfo.value
- par.params = tableDataCan.value
- testService(par).then(res=>{
- dialogVisibleSart.value = true
- outputData.value = res.msg
- })
- }
- const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
- function saveStartNode(){
- nodeStart.value = false
-
- }
- function saveEndNode(){
- nodeEnd.value = false
- var par = tableDataCanEnd.value
- const node = findNode('2')
- node.data.data = par
- console.log(nodes)
- }
- function addStart(){
- tableDataCanStart.value.push({})
- }
- function delCanStart(index){
- tableDataCanStart.value.splice(index,1)
- }
- function closeDe(){
- nodeDeSer.value = false
- }
- function saveNode(){
- // console.log(tableDataCan.value)
- var par = {
- title:parTitle.value,
- parameters:[]
- }
- tableDataCan.value.forEach(item=>{
- if(item.paramValue!==null){
- var parSplit = item.paramValue.split('.')
- if(parSplit[1]==='fixed'){
- var par1 = {
- name:item.paramCode,
- value:parSplit[0],
- dataType:item.paramType,
- refType:'fixed'
- }
- par.parameters.push(par1)
- }
- else{
- var par1 = {
- name:item.paramCode,
- ref:item.paramValue,
- dataType:item.paramType,
- refType:'ref'
- }
- par.parameters.push(par1)
- }
- }
- else{
- var par1 = {
- name:item.paramCode,
- value:null,
- dataType:item.paramType,
- refType:'fixed'
- }
- par.parameters.push(par1)
- }
-
- })
- nodes.value.forEach(item=>{
- if(item.id===parNodeid.value){
- // console.log(item.id)
- item.data.parameters = par.parameters
- item.data.service = serInfo.value
- item.data.service.params = parSerList.value
- }
- })
- console.log(toObject())
- // nodeDeSer.value = false
- // const node = findNode(parNodeid.value)
- // node.data.data = par
- // updateNode(node.id, { data: { ...node.data, ...par } });
- // updateNodeInternals(node.id);
- // updateNode(parNodeid.value, {
- // data: par
- // });
- }
- function delWholeFlow(){
- proxy.$confirm('是否删除该模型流程?', '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- }).then(() => {
- delFlow(parFlowId.value).then(res => {
- if(res.code === 200){
- proxy.$message({
- message: '删除成功',
- type: 'success'
- });
- getList();
- } else {
- proxy.$message.error('删除失败');
- }
- })
- })
- }
- const toImage = async () => {
- console.log(flowContainer.value)
- await nextTick();
- status.value = '生成图片中...';
- try {
- const canvas = await html2canvas(flowContainer.value, {
- backgroundColor: '#fff',
- scale: 2,
- useCORS: true,
- });
-
- // 生成下载链接
- const dataUrl = canvas.toDataURL('image/png');
- const link = document.createElement('a');
- link.href = dataUrl;
- link.download = 'vueflow-diagram.png'; // 文件名
- link.click();
-
- status.value = '导出成功!';
- setTimeout(() => status.value = '就绪', 3000); // 重置状态
- } catch (error) {
- status.value = `导出失败: ${error.message}`;
- console.error('导出错误:', error);
- }
- };
- function testNode(node){
- dialogVisibleTest.value = true
- }
- function oneSel(){
- if(checkboxGroup1.value.length>1){
- checkboxGroup1.value.splice(0,1)
- }
- }
- async function delNode(node){
- removeNodes([node])
- await nextTick()
-
- }
- function generateTimestampId() {
- const timestamp = Date.now(); // 13位毫秒时间戳
- const randomNum = Math.floor(Math.random() * 1000); // 3位随机数
- return parseInt(`${timestamp}${randomNum}`); // 拼接后转为整数
- }
- onEdgeClick(({ edge }) => {
- console.log(edges.value,edge)
- edges.value = edges.value.filter(item => item.id !== edge.id)
- });
- async function getTreeLeft(){
- var par = {
- params:{
- serviceState:'1'
- }
- }
- await modelTreeSelect(par).then(res=>{
- dataTree.value = (res.data)
- })
- }
- function flatArrayToTree(flatArray, rootPid = '1') {
- if (!Array.isArray(flatArray) || flatArray.length === 0) {
- return [];
- }
- const nodeMap = new Map();
- const tree = [];
- // 第一遍循环:建立id到节点的映射,并添加children属性
- flatArray.forEach(item => {
- nodeMap.set(item.id, {
- ...item,
- children: []
- });
- });
- // 第二遍循环:建立父子关系
- flatArray.forEach(item => {
- const currentNode = nodeMap.get(item.id);
-
- if (item.pid === rootPid) {
- // 根节点直接加入结果数组
- tree.push(currentNode);
- } else {
- // 非根节点:找到父节点并加入其children数组
- const parentNode = nodeMap.get(item.pid);
- if (parentNode) {
- parentNode.children.push(currentNode);
- } else {
- // 如果找不到父节点,作为孤儿节点直接放在根级
- tree.push(currentNode);
- }
- }
- });
- return tree;
- }
- function deepToRaw(obj) {
- // 如果是数组,遍历每个元素并递归处理
- if (Array.isArray(obj)) {
- return obj.map(item => deepToRaw(item));
- }
- // 如果是对象且可能是响应式代理,获取其原始对象并递归处理其属性
- else if (obj !== null && typeof obj === 'object' && (isProxy(obj) || isReactive(obj))) {
- const rawObj = toRaw(obj);
- return Object.fromEntries(
- Object.entries(rawObj).map(([key, value]) => [key, deepToRaw(value)])
- );
- }
- // 如果是普通对象,直接返回
- return obj;
- }
- async function startTest(){
- const baseUrl = window.location.origin.toString().substring(6)
- messages.value = []
- const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
- const host = window.location.host;
- ws.value = new WebSocket('ws://' + '192.168.2.119:8082/websocket/message');
- ws.value.onopen = () => {
- connected.value = true;
- };
-
- ws.value.onmessage = (event) => {
- messages.value.push(event.data); // 存储接收到的消息
- };
- await delay(1000)
- if(messages.value.length>0){
- var param = []
- var paramEnd = []
- if(tableDataCanStart.value.length>0&&tableDataCanEnd.value.length>0){
- tableDataCanStart.value.forEach(item=>{
- var par = {
- name:item.name,
- dataType:item.dataType,
- refType:'input',
- description:item.description,
- required:item.required
- }
- param.push(par)
- })
- tableDataCanEnd.value.forEach(item=>{
- var par = {
- name:item.name,
- dataType:item.dataType,
- refType:'ref',
- ref:item.paramValue
- }
- paramEnd.push(par)
- })
- nodes.value.forEach(item=>{
- if(item.id==='1'){
- item.data.parameters = param
- }
- if(item.id==='2'){
- item.data.outputDefs = paramEnd
- }
- })
- var a = JSON.parse(JSON.stringify(toObject()))
- a.nodes.forEach(item=>{
- if(item.id==='1'){
- item.type = 'startNode'
- }
- else if(item.id==='2'){
- item.type = 'endNode'
- }
- else{
- item.type = 'serviceNode'
- }
- })
- var par = {}
- par.flowGraph = JSON.stringify(a)
- par.params = {}
- par.webSocketId = messages.value[0]
- tableDataCanStart.value.forEach(item=>{
- // par.set(item.name,item.value)
- par.params[item.name] = item.paramValue
- })
- console.log(par.params)
- await runflow(par).then(res=>{
- if(res.code === 200){
- proxy.$message({
- message: res.msg,
- type: 'success'
- });
- dialogVisibleSart.value = true
- outputData.value = beautify(res.data, null, 2, 80)
- ws.value.close()
- ws.value = null
- }
- })
- }
- else if(tableDataCanStart.value.length===0){
- proxy.$message({
- message: '请设置输入值!',
- type: 'warning'
- });
- }
- else if(tableDataCanEnd.value.length===0){
- proxy.$message({
- message: '请设置输出值!',
- type: 'warning'
- });
- }
- }
-
-
- }
- async function saveFlow(){
- if(tableDataCanStart.value.length>0&&tableDataCanEnd.value.length>0){
- var param = []
- var paramEnd = []
- tableDataCanStart.value.forEach(item=>{
- var par = {
- name:item.name,
- dataType:item.dataType,
- refType:'input',
- description:item.description,
- required:item.required,
- // paramValue:item.paramValue
- }
- param.push(par)
- })
- tableDataCanEnd.value.forEach(item=>{
- var par = {
- name:item.name,
- dataType:item.dataType,
- refType:'ref',
- ref:item.paramValue,
- paramValue:item.paramValue
- }
- paramEnd.push(par)
- })
- nodes.value.forEach(item=>{
- if(item.id==='1'){
- item.data.parameters = param
- if(item.erMsg){
- delete item.erMsg
- }
- }
- if(item.id==='2'){
- item.data.outputDefs = paramEnd
- if(item.erMsg){
- delete item.erMsg
- }
- }
- })
- var a = JSON.parse(JSON.stringify(toObject()))
- a.nodes.forEach(item=>{
- if(item.id==='1'){
- item.type = 'startNode'
- }
- else if(item.id==='2'){
- item.type = 'endNode'
- }
- else{
- item.type = 'serviceNode'
- }
- })
- if(isAdd.value){
- const count = computed(() => store.getters.id)
- var par = {
- appId: count.value,
- flowName:'default',
- flowGraph: JSON.stringify(a)
- }
- addModelingFlow(par).then(res=>{
- if(res.code===200){
- proxy.$message({
- message: '保存成功',
- type: 'success'
- });
- }
- getList()
- })
- }
- else{
- const count = computed(() => store.getters.id)
- var par = {
- flowId: parFlowId.value,
- appId: count.value,
- flowName:'default',
- flowGraph: JSON.stringify(a)
- }
- console.log(par)
- editModelingFlow(par).then(res=>{
- if(res.code===200){
- proxy.$message({
- message: '保存成功',
- type: 'success'
- });
- getList()
- }
- })
- }
- }
- else if(tableDataCanStart.value.length===0){
- proxy.$message({
- message: '请设置输入值!',
- type: 'warning'
- });
- }
- else if(tableDataCanEnd.value.length===0){
- proxy.$message({
- message: '请设置输出值!',
- type: 'warning'
- });
- }
- var parFlow = toRaw(toObject())
- parFlow.nodes.forEach(item => {
- if(item.id==='1'){
- item.type='startNode'
- item.data.parameters = tableDataCanStart.value;
- }
- if(item.id==='2'){
- item.type='endNode'
- item.data.parameters = tableDataCanEnd.value;
- console.log(item)
- }
- item.title = item.label
- });
-
- }
- function getPredecessorsNodes(nodeId, edges = getEdges.value, allNodes = getNodes.value){
- // 1. 找到所有直接指向目标节点的边(即 target 为 nodeId 的边)
- const incomingEdges = edges.filter(edge => edge.target === nodeId);
-
- // 2. 从这些边中提取所有源节点的 ID
- const sourceNodeIds = incomingEdges.map(edge => edge.source);
-
- // 3. 根据源节点 ID 获取完整的节点对象
- const directPredecessors = allNodes.filter(node => sourceNodeIds.includes(node.id));
-
- // 4. 递归获取这些直接前置节点的前置节点
- const allPredecessors = [];
- for (const predecessor of directPredecessors) {
- // 将直接前置节点加入结果数组
- allPredecessors.push(predecessor);
- // 递归获取该前置节点的前置节点,并合并到结果数组中
- const predecessorsOfPredecessor = getPredecessorsNodes(predecessor.id, edges, allNodes);
- allPredecessors.push(...predecessorsOfPredecessor);
- }
- // 5. 使用 Set 或其他方法去重(根据节点 ID),因为一个节点可能通过不同路径被多次访问
- const uniquePredecessors = Array.from(new Map(allPredecessors.map(node => [node.id, node])).values());
-
- return uniquePredecessors;
- };
- function removeDuplicatesAndEmptyItems(arr) {
- const map = new Map();
- return arr.filter(item => {
- // 过滤 null 和 undefined
- if (item === null || item === undefined) return false;
-
- // 处理对象类型:转为字符串标识去重
- const key = typeof item === 'object'
- ? JSON.stringify(item)
- : item;
-
- // 若标识不存在于 Map 中,则保留并记录
- if (!map.has(key)) {
- map.set(key, true);
- return true;
- }
- return false;
- });
- }
- function parseJSONWithComments(jsonString) {
- // 移除单行注释(//)和多行注释(/* */)
- const cleanedJson = jsonString.replace(/\/\/.*|\/\*[\s\S]*?\*\//g, '');
- try {
- return JSON.parse(cleanedJson);
- } catch (error) {
- console.error("解析失败:", error.message);
- return null;
- }
- }
- onNodeClick(({event, node}) => {
- nodeEnd.value = false
- nodeStart.value = false
- nodeDeSer.value = false
- parNodeid.value = node.id
- parTitle.value = node.data.name
- tableDataCan.value = []
- optionsCan.value = []
- tableDataCanOut.value = []
- var parNode = getPredecessorsNodes(node.id)
- var a = JSON.parse(JSON.stringify(parNode))
- // console.log(a)
- a.forEach((item,index) => {
- console.log(item.data)
- if(item.data.label!=='开始'&&item.data.service&&item.data.service.rpcontent){
- item.data.service.rpcontent = parseJSONWithComments(item.data.service.rpcontent)
- console.log((item.data.service.rpcontent))
- var parShu = Object.keys((item.data.service.rpcontent))
- parShu.forEach(item1=>{
- var par = {
- ref:item.id + '.' + item1,
- name:item.data.label + ':' + item1
- }
- optionsCan.value.push(par)
- })
- }
- if(tableDataCanStart.value.length>0){
- tableDataCanStart.value.forEach(item=>{
- var par = {
- ref:'1' + '.' + item.name,
- name:item.name
- }
- optionsCan.value.push(par)
- })
- }
- })
- console.log(optionsCan.value)
- if(node.data.label==='结束'){
- nodeEnd.value = true
- }
- if(node.data.label==='开始'){
- nodeStart.value = true
- }
- if(node.data.nodeType==='SERVICE'){
- getSerDe(node.data.id).then(res=>{
- serviceRqtype.value = res.data.ptService.rqtype
- servieName.value = res.data.ptService.name
- tableDataCan.value = res.data.list
- serInfo.value = res.data.ptService
- parSerList.value = JSON.parse(JSON.stringify(res.data.list))
- var par1 = parseJSONWithComments(res.data.ptService.rpcontent)
- var parShu = Object.keys((par1))
- console.log(parShu)
- parShu.forEach(item1=>{
- var par = {
- paramCode:item1,
- paramValue:''
- }
- tableDataCanOut.value.push(par)
- })
- // tableDataCanOut.value = parseJSONWithComments(res.data.ptService.rpcontent)
- serviceUrl.value = res.data.ptService.url
- nodeDeSer.value = true
- })
- }
- if(node.data.nodeType==='tool'){
- console.log(node.data.value)
- if(node.data.value==='表输入组件'){
- dialogVisibleBiao.value = true
- }
- if(node.data.value==='Excel文件输入组件'){
- dialogVisibleExcel.value = true
- }
- if(node.data.value==='csv输入组件'){
- dialogVisibleCsv.value = true
- }
- }
-
- });
- /**
- * 开始拖拽选项的事件
- * @param event
- * @param data
- */
- function onDragStart(event, data) {
- if(data.nodeType==='SERVICE'||data.nodeType==='tool'){
- if (event.dataTransfer) {
- event.dataTransfer.setData('application/vueflow', data)
- event.dataTransfer.effectAllowed = 'move'
- }
- draggedData.value = data
- isDragging.value = true
-
- document.addEventListener('drop', onDragEnd)
- }
-
- }
- /**
- * 拖拽到画布vueflow的事件
- * @param event
- */
- function onDragOver(event) {
- event.preventDefault()
-
- if (draggedData.value) {
- isDragOver.value = true
- if (event.dataTransfer) {
- event.dataTransfer.dropEffect = 'move'
- }
- }
- }
- /**
- * 拖拽放下的事件
- * @param event
- */
- async function onDrop(event) {
- const position = screenToFlowCoordinate({ x: event.clientX, y: event.clientY });
- const nodeId = generateTimestampId();
-
- // 1. 同步创建节点
- const newNode = {
- id: nodeId,
- type: 'special',
- position,
- data: copyObject(draggedData.value) // 先使用本地数据
- };
- addNodes(newNode);
- // 2. 异步获取数据并更新
- const res = await getSerDe(draggedData.value.id);
- const fullData = {
- ...res.data.ptService,
- returnList: res.data.returnList
- };
- // 3. 等待节点渲染完成后再修正位置
- nextTick(() => {
- updateNode(nodeId, { data: { ...newNode.data, ...fullData } });
- updateNodeInternals(nodeId); // 强制更新节点布局[1](@ref)
- });
- // console.log(toObject())
- }
- /**
- * 拖拽到画布外面的的事件
- */
- function onDragLeave() {
- isDragOver.value = false
- }
- /**
- * 拖拽结束
- */
- function onDragEnd() {
- isDragging.value = false
- isDragOver.value = false
- draggedData.value = null
- document.removeEventListener('drop', onDragEnd)
- }
- function onConnect(params) {
- addEdges({ ...params, ...defaultEdgeStyle })
- }
- function back(){
- proxy.$router.push({ path: '/standardization/modelUsing' });
- }
- /** 查询模型列表 */
- function getModelList() {
- getModelList2(modelQueryParams.value).then(res => {
- loading.value = false;
- modelOptions.value = res.data;
- });
- }
- /** 通过条件过滤节点 */
- const filterNode = (value, data) => {
- if (!value) return true;
- return data.label.indexOf(value) !== -1;
- };
- /** 查询流程图 */
- function getList() {
- const count = computed(() => store.getters.id)
- console.log(count.value);
- var par = {
- appId: count.value
- }
- getModelingDe(par).then(res => {
- if(res.data.length===0){
- isAdd.value = true
- nodes.value = [
- { id: '1', position: { x: -600, y: -300 }, data: { label: '开始' }, type: 'special' },
- { id: '2', position: { x: 150, y: 100 }, data: { label: '结束' }, type: 'special' },
- ]
- }
- else{
- isAdd.value = false
- var a = JSON.parse(JSON.parse(JSON.stringify(res.data[0].flowGraph)))
- console.log(a)
- a.nodes.forEach(item=>{
- item.type = 'special'
- if(item.id==='2'&&item.data){
- tableDataCanEnd.value = item.data.outputDefs
- }
- if(item.id==='1'&&item.data.parameters){
- tableDataCanStart.value = item.data.parameters
- }
- })
- nodes.value = a.nodes
- edges.value = a.edges
- zoom.value = a.zoom
- zoomTo(zoom.value)
- parFlowId.value = res.data[0].flowId
- }
- if(tableDataCanEnd.value.length===0){
- tableDataCanEnd.value = []
- }
- });
-
- }
- const ws = ref(null);
- const connected = ref(false);
- const messages = ref([]);
- const connect = () => {
- ws.value = new WebSocket('ws://127.0.0.1:8082/websocket/message'); // 替换为你的 WS 地址
-
- ws.value.onopen = () => {
- connected.value = true;
- };
-
- ws.value.onmessage = (event) => {
- messages.value.push(event.data); // 存储接收到的消息
- };
-
- ws.value.onerror = (error) => {
- console.error('WebSocket 错误:', error);
- };
-
- ws.value.onclose = () => {
- connected.value = false;
- console.log('WebSocket 连接已关闭');
- };
- };
- const sendMessage = () => {
- if (ws.value && ws.value.readyState === WebSocket.OPEN) {
- const message = 'Hello, Server!'; // 实际发送的消息内容或格式可与后端约定
- ws.value.send(message);
- }
- };
- const disconnect = () => {
- if (ws.value) {
- ws.value.close();
- ws.value = null;
- }
- };
- // 组件卸载时自动断开连接
- onUnmounted(() => {
- disconnect();
- });
- onMounted(() => {
- // connect()
- getTreeLeft()
- getList();
- })
- watch(messages, (newMessages) => {
- newMessages.forEach(item=>{
- item = JSON.parse(item)
- nodes.value.forEach(item1=>{
- if(item1.id===item.id){
- if(item.nodeState&&item.nodeState==='START'){
- item1.data.loading = true
- item1.data.isSuccess = true
- item1.data.isFail = false
- }
- if(item.nodeState&&item.nodeState==='ERROR'){
- console.log(3)
- item1.data.loading = false
- item1.data.isSuccess = false
- item1.data.isFail = true
- item1.data.erMsg = item.errorMessage
- // throw new TypeError(`期望传入一个数字,但收到的是`)
- }
- if(item.nodeState&&item.nodeState==='END'){
- console.log(2)
- item1.data.loading = false
- // item1.data.isSuccess = true
- // item1.data.isFail = false
- }
- }
- })
- })
- }, { deep: true })
- // watch(nodes, (newNodes) => {
- // console.log('当前节点列表:', newNodes) // 实时输出最新数据
- // }, { deep: true })
- </script>
- <style>
- @import '@vue-flow/core/dist/style.css';
- @import '@vue-flow/core/dist/theme-default.css';
- </style>
- <style scoped>
- .custom-no-border :deep(.el-input__wrapper) {
- box-shadow: none !important; /* 移除默认阴影(即边框) */
- border: none !important; /* 双重保障 */
- }
- /* 处理悬停和聚焦状态 */
- .custom-no-border :deep(.el-input__wrapper:hover),
- .custom-no-border :deep(.el-input__wrapper:focus-within) {
- box-shadow: none !important;
- border: none !important;
- }
- :deep(.treeLeft) .el-tree-node__content {
- display: flex !important;
- height: 28px; /* 按设计稿调整高度 */
- align-items: center;
- padding-top: 0 !important;
- }
- :deep(.treeLeft) .el-tree-node__content:hover {
- background-color: #e9e9eb;
- }
- :deep(.treeLeft) .el-tree-node__content:active {
- background-color: rgka(69,157,255,0.1) !important;
- }
- /* 选中态(Active) */
- :deep(.treeLeft) .el-tree-node.is-current > .el-tree-node__content {
- background-color: #c6e2ff !important;
- }
- </style>
|