| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- <template>
- <div>
- <!-- <el-button type="primary" @click="showData">测试</el-button> -->
- <div style="display: flex;margin-left: 1%;padding-top: 1%;position: absolute;z-index: 1000;cursor: pointer;justify-content: space-between;width: 98%;" @click="back">
- <el-icon size="large"><Back /></el-icon>
- <el-button style="margin-left: auto;" type="danger" size="mini">删除</el-button>
- <el-button style="margin-left: 1%;" type="primary" size="mini">保存</el-button>
- </div>
- <div v-if="nodeDeSer" style="height: 85vh;width: 20vw;position: absolute;float: right;z-index: 1000;right: 1%;top: 1%;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="nodeDeSer = false" 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 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-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>
- </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" :rules="rules" 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-form-item label="失败处理">
- <el-select v-model="form.errorPolicy" style="width: 50%">
- <el-option label="报错" value="ABORT"></el-option>
- <el-option label="忽视" value="IGNORE"></el-option>
- <el-option label="重连" value="RETRY"></el-option>
- </el-select>
- </el-form-item>
- <el-form-item label="失败重连次数">
- <el-input-number v-model="form.retryCount" :min="1" style="width: 50%" :max="30"/>
- </el-form-item>
- </el-form>
- </div>
- </div>
- </div>
- <div style="display: flex;height: 85vh;width: 100%;padding-top:2%;">
- <div style="width: 20%;margin-left: 1%;overflow-y: auto;">
- <el-tree :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: 5%;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;">
- <div class="custom-tree-node":draggable="true"
- @dragstart="onDragStart($event,data)">
- <!-- <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>
- </div>
- <VueFlow :nodes="nodes" :edges="edges" @drop="onDrop" @dragover="onDragOver" @dragleave="onDragLeave"
- @node-click="onNodeClick" @connect="onConnect" fit-view-on-init>
- <template #node-special="specialNodeProps">
- <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.label!=='结束'&&specialNodeProps.data.label!=='开始'" class="vue-flow__node-default"
- 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;">
- {{ specialNodeProps.data.label }}
- </div>
- <el-icon style="cursor: pointer;margin-left: auto;"><CaretRight /></el-icon>
- <el-icon 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;align-items: center;">
- <div>
- GET:
- </div>
- <div>
- https://www.zhihu.com/
- </div>
- </div>
- <div style="display: flex;margin-top: 3%;font-size: 9px;color: #b1b3b8;align-items: center;">
- <div>
- 输出:
- </div>
- <div>
- https://www.zhihu.com/
- </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 :title="title" v-model="open" width="600px" append-to-body>
- <template #header="{ close, titleId, titleClass }">
- <div style="display: flex;align-items: center;">
- <el-tag>{{ form.type }}</el-tag>
- <h4 style="margin: 0 0 0 5px;" :id="titleId" :class="titleClass">{{ form.label }}</h4>
- </div>
- </template>
- <el-form ref="deptRef" :model="form" :rules="rules" label-width="80px" label-position="top">
- <el-form-item label="API">
- <el-row style="width: 100%;" :gutter="20">
- <el-col :span="4">
- <el-select v-model="form.config.method" style="width: 100px">
- <el-option label="GET" value="GET"></el-option>
- <el-option label="POST" value="POST"></el-option>
- <el-option label="HEAD" value="HEAD"></el-option>
- <el-option label="PATCH" value="PATCH"></el-option>
- <el-option label="PUT" value="PUT"></el-option>
- <el-option label="DELETE" value="DELETE"></el-option>
- </el-select>
- </el-col>
- <el-col :span="20">
- <el-input v-model="form.config.url" placeholder="请输入URL"/>
- </el-col>
- </el-row>
- </el-form-item>
- <el-form-item label="HEADERS">
- <dynamic-map v-model="form.config.headers"></dynamic-map>
- </el-form-item>
- <el-form-item label="PARAMS">
- <dynamic-map v-model="form.config.params"></dynamic-map>
- </el-form-item>
- <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>
- <dynamic-map v-if="form.bodyType === 'x-www-form-urlencoded'" v-model="form.config.body"></dynamic-map>
- </el-form-item>
- <el-row>
- <el-col :span="24">
- <el-form-item label="失败处理">
- <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>
- </el-form-item>
- </el-col>
- <el-col :span="24">
- <el-form-item v-if="form.errorPolicy === 'RETRY'" label="失败重连次数">
- <el-input-number v-model="form.retryCount" :min="1" :max="30"/>
- </el-form-item>
- </el-col>
- </el-row>
- </el-form>
- <template #footer>
- <div class="dialog-footer">
- <el-button type="primary" @click="submitForm">确 定</el-button>
- <el-button @click="cancel">取 消</el-button>
- </div>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup>
- import DynamicMap from '@/components/DynamicMap/index.vue'
- import {Promotion} from '@element-plus/icons-vue'
- import { onMounted, ref } 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 {getModelList2} from "@/api/register/regCom.js";
- import {copyObject} from "@/utils/index.js";
- import {getWorkflowByModelId, saveWorkflow} from "@/api/standardization/workflow.js";
- import { useStore } from 'vuex';
- import {Handle, Position} from '@vue-flow/core'
- import { computed } from 'vue';
- import { modelTreeSelect } from "@/api/service/info";
- const {
- snapToGrid,
- addEdges,
- onEdgeClick,
- removeElements,
- addNodes,
- screenToFlowCoordinate,
- onNodesInitialized,
- updateNode,
- onNodeClick,
- getNodes,
- getEdges,
- removeNodes,
- removeEdges,
- } = useVueFlow()
- snapToGrid.value = true
- const servieName = ref()
- const serviceRqtype = ref()
- const serviceUrl = ref()
- const tableDataCan = ref()
- const nodeDeSer = 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 toolType = ref('0');
- const serviceList = ref([]);
- const defaultEdgeStyle = {
- style: {
- stroke: '#6366f1',
- strokeWidth: 2,
- fill: 'none' // 避免截图时出现黑色背景 [4](@ref)
- },
- markerEnd: {
- type: MarkerType.ArrowClosed,
- color: '#ff0000', // 箭头颜色
- 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 title = ref('')
- const open = ref(false)
- const store = useStore();
- onEdgeClick(({ edge }) => {
- edges.value = edges.value.filter(edge => edge.id !== edgeId)
- });
- async function getTreeLeft(){
- await modelTreeSelect().then(res=>{
- dataTree.value = res.data
- })
- optionsMdid.value = filterModelNodes(par)
- console.log(optionsMdid.value)
- }
- function showData(){
- console.log(nodes.value,edges.value)
- }
- function handleModelClick(mid) {
- modelId.value = mid
- removeNodes(getNodes.value)
- removeEdges(getEdges.value)
- getWorkflow(mid)
- }
- onNodeClick(({event, node}) => {
- console.log(node)
- if(node.data.nodeType==='SERVICE'){
- getSerDe(node.data.id).then(res=>{
- servieName.value = res.data.ptService.name
- tableDataCan.value = res.data.list
- serviceUrl.value = res.data.ptService.url
- dataReturn.value = res.data.returnList
- })
- nodeDeSer.value = true
- }
-
- });
- const data = reactive({
- form: {config: {}},
- queryParams: {
- pageNum: 1,
- pageSize: 10,
- userName: undefined,
- phonenumber: undefined,
- status: undefined,
- deptId: undefined
- },
- rules: {
- userName: [{required: true, message: "用户名称不能为空", trigger: "blur"}, {
- min: 2,
- max: 20,
- message: "用户名称长度必须介于 2 和 20 之间",
- trigger: "blur"
- }],
- nickName: [{required: true, message: "用户昵称不能为空", trigger: "blur"}],
- password: [{required: true, message: "用户密码不能为空", trigger: "blur"}, {
- min: 5,
- max: 20,
- message: "用户密码长度必须介于 5 和 20 之间",
- trigger: "blur"
- }, {pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur"}],
- email: [{type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"]}],
- phonenumber: [{pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur"}]
- }
- });
- const {queryParams, form, rules} = toRefs(data);
- const nodeId = ref(null)
- /**
- * 开始拖拽选项的事件
- * @param event
- * @param data
- */
- function onDragStart(event, data) {
- console.log(data)
- if (event.dataTransfer) {
- event.dataTransfer.setData('application/vueflow', data)
- event.dataTransfer.effectAllowed = 'move'
- }
- draggedData.value = data
- console.log(draggedData.value)
- isDragging.value = true
- document.addEventListener('drop', onDragEnd)
- }
- function back(){
- proxy.$router.push({ path: '/standardization/modelUsing' });
- }
- /**
- * 拖拽到画布vueflow的事件
- * @param event
- */
- function onDragOver(event) {
- event.preventDefault()
- if (draggedData.value) {
- isDragOver.value = true
- if (event.dataTransfer) {
- event.dataTransfer.dropEffect = 'move'
- }
- }
- }
- /**
- * 拖拽放下的事件
- * @param event
- */
- function onDrop(event) {
- const position = screenToFlowCoordinate({
- x: event.clientX,
- y: event.clientY,
- })
- const nodeId = Math.random() + "id";
- const data = copyObject(draggedData.value)
- const newNode = {
- id: nodeId,
- type: 'special',
- position,
- data,
- }
- // 更新位置到鼠标中心
- const {off} = onNodesInitialized(() => {
- updateNode(nodeId, (node) => ({
- position: {x: node.position.x - node.dimensions.width / 2, y: node.position.y - node.dimensions.height / 2},
- }))
- off()
- })
- addNodes(newNode)
- }
- /**
- * 拖拽到画布外面的的事件
- */
- 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 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 handleQuery() {
- queryParams.value.pageNum = 1;
- getList();
- }
- /** 查询流程图 */
- function getList() {
- loading.value = true;
- // listUser(proxy.addDateRange(queryParams.value, dateRange.value)).then(res => {
- // loading.value = false;
- // userList.value = res.rows;
- // total.value = res.total;
- // });
- }
- /** 查询服务列表 */
- function getServiceList() {
- getPtServiceList(toolQueryParams.value).then(res => {
- serviceList.value = res.data.map(item => {
- return {
- id: item.id,
- label: item.name,
- type: 'API',
- config: {
- url: item.url,
- method: item.rqtype,
- params: item.params,
- headers: item.headers,
- body: item.body,
- },
- errorPolicy: 'ABORT',
- retryCount: 0,
- outputMapping: null,
- data: item,
- }
- })
- })
- }
- function submitForm() {
- updateNode(nodeId.value, (node) => ({
- data: form.value,
- }))
- cancel()
- }
- function cancel() {
- form.value = {config: {}};
- open.value = false;
- title.value = ''
- }
- function saveStep() {
- if (!modelId.value) {
- proxy.$modal.msgError("请选择模型");
- return
- }
- const nodes = getNodes.value.map(res => {
- return {
- id: res.id,
- type: res.type,
- position: res.position,
- data: res.data,
- }
- })
- const edges = getEdges.value.map(res => {
- return {
- source: res.source,
- target: res.target,
- }
- })
- const data = {
- mdid: modelId.value,
- graph: JSON.stringify({
- nodes: nodes,
- edges: edges,
- }),
- }
- saveWorkflow(data).then(res => {
- // 测试
- proxy.$modal.msgError("请输入建表语句");
- })
- }
- function getWorkflow(modelId) {
- getWorkflowByModelId(modelId).then(res => {
- if (res.data && res.data.graph) {
- const {nodes, edges} = JSON.parse(res.data.graph)
- addNodes(nodes)
- addEdges(edges)
- }
- })
- }
- onMounted(() => {
- getTreeLeft()
- getModelList()
- getServiceList()
- const count = computed(() => store.getters.id)
- console.log(count.value);
- })
- </script>
- <style>
- @import '@vue-flow/core/dist/style.css';
- @import '@vue-flow/core/dist/theme-default.css';
- </style>
- <style scoped>
- :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>
|