Pārlūkot izejas kodu

update: 添加初版流程编排功能

liuyang 2 gadi atpakaļ
vecāks
revīzija
d856784a72

+ 8 - 0
package-lock.json

@ -7657,6 +7657,14 @@
7657 7657
      "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.1.1.tgz",
7658 7658
      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
7659 7659
    },
7660
    "qs": {
7661
      "version": "6.11.1",
7662
      "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz",
7663
      "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==",
7664
      "requires": {
7665
        "side-channel": "^1.0.4"
7666
      }
7667
    },
7660 7668
    "query-string": {
7661 7669
      "version": "4.3.4",
7662 7670
      "resolved": "https://registry.npmmirror.com/query-string/-/query-string-4.3.4.tgz",

+ 1 - 0
package.json

@ -33,6 +33,7 @@
33 33
    "json-editor-vue3": "^1.0.8",
34 34
    "mitt": "^3.0.0",
35 35
    "prettier": "^2.7.1",
36
    "qs": "^6.11.1",
36 37
    "sass": "^1.56.1",
37 38
    "uuid": "^9.0.0",
38 39
    "vite-plugin-eslint": "^1.8.1",

+ 14 - 15
src/App.vue

@ -5,6 +5,18 @@
5 5
 * @LastEditTime: 2023-02-23 16:39:57
6 6
 * @Description: demo入口文件
7 7
-->
8
<template>
9
  <common-layer
10
    :menu-list="menuList"
11
    :user-info="userInfo"
12
    :header-info="headerInfo"
13
    :commonds="commonds"
14
    :is-collapse="isCollapse"
15
    :aside-width="asideWidth"
16
    :breads="route.meta.breadcrumb"
17
    @handle-change-collapse="handleChangeCollapse"
18
  ></common-layer>
19
</template>
8 20
<script setup lang="ts">
9 21
import { ref } from 'vue';
10 22
import { useRoute } from 'vue-router';
@ -13,10 +25,10 @@ import logoImg from './assets/images/logo.svg';
13 25
const menuList = ref<any>([
14 26
  {
15 27
    label: '流程编排',
16
    name: 'system',
28
    name: 'flow-system',
17 29
    icon: 'common-jihuoshebei',
18 30
    level: 1,
19
    path: '/system'
31
    path: '/flow-system'
20 32
  },
21 33
  {
22 34
    label: '子流程管理',
@ -53,19 +65,6 @@ function handleChangeCollapse(collapse: any) {
53 65
}
54 66
</script>
55 67
56
<template>
57
  <common-layer
58
    :menu-list="menuList"
59
    :user-info="userInfo"
60
    :header-info="headerInfo"
61
    :commonds="commonds"
62
    :is-collapse="isCollapse"
63
    :aside-width="asideWidth"
64
    :breads="route.meta.breadcrumb"
65
    @handle-change-collapse="handleChangeCollapse"
66
  ></common-layer>
67
</template>
68
69 68
<style>
70 69
html,
71 70
body {

+ 87 - 2
src/api/flow.ts

@ -53,16 +53,82 @@ export function deleteSystemById(data: any) {
53 53
    data
54 54
  });
55 55
}
56
// 复制系统
56
// 克隆系统
57 57
export function copySystemById(data: any) {
58 58
  return AIotRequest.post({
59 59
    url: flow.systemMgmt,
60 60
    data
61 61
  });
62 62
}
63
// 根据ID获取系统详情
64
export function getSystemDetailById(id: any) {
65
  return AIotRequest.get({
66
    url: `${flow.systemMgmt}/${id}`
67
  });
68
}
63 69
70
/**
71
 * 版本 start
72
 */
73
export function getVersionList(id: any) {
74
  return AIotRequest.get({
75
    url: `${flow.versionMgmt}?systemId=${id}`
76
  });
77
}
78
79
/**
80
 * 版本 end
81
 */
82
/**
83
 * 模块start
84
 */
85
// 获取模块列表
86
export function getModuleList(data: any) {
87
  return AIotRequest.get({
88
    url: flow.moduleMgmt,
89
    data
90
  });
91
}
92
// 新增模块
93
export function createModule(data: any) {
94
  return AIotRequest.post({
95
    url: flow.moduleMgmt,
96
    data
97
  });
98
}
99
// 修改模块
100
export function updateModuleById(data: any, id: any) {
101
  return AIotRequest.patch({
102
    url: `${flow.moduleMgmt}/${id}`,
103
    data
104
  });
105
}
106
// 克隆模块
107
export function cloneModuleById(data: any, id: any) {
108
  return AIotRequest.post({
109
    url: `${flow.modclone}/${id}`,
110
    data
111
  });
112
}
113
// 删除模块
114
export function deleteModuleById(id: any) {
115
  return AIotRequest.delete({
116
    url: `${flow.moduleMgmt}/${id}`
117
  });
118
}
119
/**
120
 * 模块end
121
 */
64 122
/** 接口start */
65
// 流程数据暂存
123
// 新增接口
124
export function createLogic(data: any) {
125
  return AIotRequest.post({
126
    url: flow.logicMgmt,
127
    data
128
  });
129
}
130
131
// 编辑接口
66 132
export function updateFlowDataById(data: any, id: any) {
67 133
  return AIotRequest.patch({
68 134
    url: `${flow.logicMgmt}/${id}`,
@ -70,4 +136,23 @@ export function updateFlowDataById(data: any, id: any) {
70 136
  });
71 137
}
72 138
139
// 删除接口
140
export function deleteLogicById(id: any) {
141
  return AIotRequest.delete({
142
    url: `${flow.logicMgmt}/${id}`
143
  });
144
}
145
// 克隆接口
146
export function cloneLogicById(data: any, id: any) {
147
  return AIotRequest.post({
148
    url: `${flow.modlogic}/${id}`,
149
    data
150
  });
151
}
152
// 获取接详情
153
export function getLogicDetailById(id: any) {
154
  return AIotRequest.get({
155
    url: `${flow.logicMgmt}/${id}`
156
  });
157
}
73 158
/** 接口end */

+ 4 - 1
src/api/request.js

@ -1,6 +1,7 @@
1 1
import axios from 'axios';
2 2
3 3
// import { ElLoading, ElMessage } from 'element-plus';
4
import qs from 'qs';
4 5
5 6
const DEAFULT_LOADING = false;
6 7
@ -119,7 +120,9 @@ class AiotRequest {
119 120
  }
120 121
121 122
  get(config) {
122
    console.log(config);
123
    if (config.data && Object.keys(config.data)?.length) {
124
      config.url = `${config.url}?${qs.stringify(config.data)}`;
125
    }
123 126
    return this.request({ ...config, method: 'GET' });
124 127
  }
125 128

+ 51 - 12
src/router.ts

@ -10,21 +10,60 @@ import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
10 10
11 11
const routes: Array<RouteRecordRaw> = [
12 12
  {
13
    path: '/system',
14
    name: 'SystemList',
15
    component: () => import('./views/flow/SystemList.vue'),
16
    meta: {
17
      breadcrumb: [{ name: '系统管理' }]
18
    },
19
    children: []
13
    path: '',
14
    component: () => import('./views/flow/Container.vue'),
15
    children: [
16
      {
17
        path: '/flow-system',
18
        name: 'SystemList',
19
        component: () => import('./views/flow/SystemList.vue'),
20
        meta: {
21
          breadcrumb: [{ name: '系统管理' }]
22
        }
23
      },
24
      {
25
        path: '/flow-module',
26
        name: 'SystemDetail',
27
        component: () => import('./views/flow/SystemDetail.vue'),
28
        meta: {
29
          breadcrumb: [
30
            {
31
              name: '系统管理',
32
              to: {
33
                name: 'SystemList'
34
              }
35
            },
36
            { name: '模块' }
37
          ]
38
        }
39
      },
40
      {
41
        path: '/flow-detail',
42
        name: 'Flow',
43
        component: () => import('./views/flow/Flow.vue'),
44
        meta: {
45
          breadcrumb: [
46
            {
47
              name: '系统管理',
48
              to: {
49
                name: 'SystemList'
50
              }
51
            },
52
            {
53
              name: '模块',
54
              to: {
55
                name: 'SystemDetail'
56
              }
57
            },
58
            { name: '逻辑详情' }
59
          ]
60
        }
61
      }
62
    ]
20 63
  },
21 64
  {
22 65
    path: '',
23
    name: 'Container',
24 66
    component: () => import('./views/sub-flow/Container.vue'),
25
    meta: {
26
      breadcrumb: [{ name: '子流程管理' }]
27
    },
28 67
    children: [
29 68
      {
30 69
        path: '/sub-flow',
@ -35,7 +74,7 @@ const routes: Array<RouteRecordRaw> = [
35 74
        }
36 75
      },
37 76
      {
38
        path: '/sub-flow/flow',
77
        path: '/sub-flow-detail',
39 78
        name: 'SubFlow',
40 79
        meta: {
41 80
          breadcrumb: [

+ 2 - 2
src/views/_components/panel/PanelStart.vue

@ -28,8 +28,8 @@
28 28
  <dialog-panel
29 29
    v-if="showImport"
30 30
    :dialog-data="dialogData"
31
    @confirmDialog="confirmModelDialog"
32
    @closeDialog="closeModelDialog"
31
    @confirm-dialog="confirmModelDialog"
32
    @close-dialog="closeModelDialog"
33 33
  >
34 34
    <!-- 选择 -->
35 35
    <el-radio-group v-model="selectTab">

+ 9 - 0
src/views/flow/Container.vue

@ -0,0 +1,9 @@
1
<template>
2
  <keep-alive>
3
    <router-view></router-view>
4
  </keep-alive>
5
</template>
6
7
<script setup lang="ts"></script>
8
9
<style scoped lang="scss"></style>

+ 175 - 0
src/views/flow/Flow.vue

@ -0,0 +1,175 @@
1
<template>
2
  <div class="flow-logic">
3
    <sub-flow
4
      :nodes="nodeList"
5
      :tool-list="toolList"
6
      :flow-data="subFlowDetail"
7
      @tool-handel-click="toolHandelClick"
8
    ></sub-flow>
9
  </div>
10
11
  <!-- 弹窗区域 -->
12
  <right-panel></right-panel>
13
</template>
14
15
<script setup>
16
import { ref, getCurrentInstance, onMounted } from 'vue';
17
18
/** x6相关end */
19
import { useRoute } from 'vue-router';
20
import SubFlow from '../_components/_flow/Index.vue';
21
import nodes from '../_components/_flow/nodes.js';
22
23
import * as api from '../../api/flow';
24
import NodeDataFormate from '../_components/_flow/nodeDataFormate.js';
25
26
const nodeList = ref(nodes.nodes);
27
28
const { proxy } = getCurrentInstance();
29
const route = useRoute();
30
const subFlowDetail = ref(null);
31
const subFlowId = ref(route.query.id);
32
33
// 获取流程数据
34
async function getLogicDetailById() {
35
  await api.getLogicDetailById(subFlowId.value).then((res) => {
36
    if (res.resultCode === '0') {
37
      subFlowDetail.value = res.result;
38
    } else {
39
      proxy.$message.error(res.resultMsg);
40
    }
41
  });
42
}
43
// 获取所有节点信息
44
function getNodesData(graph) {
45
  return NodeDataFormate.nodeDataFormate(graph);
46
}
47
48
// 接口调用
49
async function updateFlowData(graph, type) {
50
  const databusData = JSON.parse(localStorage.getItem('panelGlobal')) || {};
51
  const params = {
52
    jsonData: JSON.stringify(graph),
53
    databusData: JSON.stringify(databusData[subFlowId.value]),
54
    stateValue: type,
55
    nodesData: getNodesData(graph)
56
  };
57
  await api
58
    .updateFlowDataById(params, subFlowId.value)
59
    .then((res) => {
60
      if (res.resultCode === '0') {
61
        proxy.$message.success('流程保存成功!');
62
      } else {
63
        proxy.$message.error(res.resultMsg);
64
      }
65
    })
66
    .catch((err) => {
67
      proxy.$message.error(err);
68
    });
69
}
70
71
const toolList = ref([
72
  [
73
    {
74
      name: 'tool-undo',
75
      label: '撤销(meta+shift+z或ctrl+shift+z)'
76
    },
77
    {
78
      name: 'tool-redo',
79
      label: '重做(meta+z或ctrl+z)'
80
    }
81
  ],
82
  [
83
    {
84
      name: 'tool-copy',
85
      label: '复制(meta+c或ctrl+c)'
86
    },
87
    {
88
      name: 'tool-cut',
89
      label: '剪切(meta+x或ctrl+X)'
90
    },
91
    {
92
      name: 'tool-paste',
93
      label: '粘贴(x或ctrl + V)'
94
    },
95
    {
96
      name: 'tool-delete',
97
      label: '删除(meta+d或ctrl+d)'
98
    }
99
  ],
100
  [
101
    {
102
      name: 'tool-save',
103
      label: '保存'
104
      // disabeld: true
105
    },
106
    {
107
      name: 'tool-send',
108
      label: '推送'
109
      // disabeld: true
110
    }
111
  ],
112
  [
113
    {
114
      name: 'tool-var',
115
      label: '全局变量'
116
    }
117
  ]
118
]);
119
// toolbar 点击回调事件
120
function toolHandelClick({ name, graph }) {
121
  const cells = graph.getSelectedCells();
122
  console.log(cells);
123
  switch (name) {
124
    case 'tool-undo':
125
      graph.undo();
126
      break;
127
    case 'tool-redo':
128
      graph.redo();
129
      break;
130
    case 'tool-copy':
131
      proxy.$message.success('已复制至剪切板!');
132
      if (cells.length) {
133
        graph.copy(cells);
134
      }
135
      break;
136
    case 'tool-cut':
137
      if (cells.length) {
138
        proxy.$message.success('已剪切至剪切板!');
139
        graph.cut(cells);
140
      }
141
      break;
142
    case 'tool-paste':
143
      if (!graph.isClipboardEmpty()) {
144
        const cells = graph.paste({ offset: 100 });
145
        graph.cleanClipboard();
146
        graph.select(cells);
147
        proxy.$message.success('已粘贴至画板!');
148
      }
149
      break;
150
    case 'tool-delete':
151
      if (cells.length) {
152
        graph.cut(cells);
153
        graph.cleanClipboard();
154
      }
155
      break;
156
    case 'tool-save':
157
      updateFlowData(graph, '1');
158
      break;
159
    case 'tool-send':
160
      updateFlowData(graph, '2');
161
      break;
162
    case 'tool-var':
163
      proxy.$eBus.emit('open:right-panel');
164
      break;
165
    default:
166
      break;
167
  }
168
}
169
170
onMounted(() => {
171
  getLogicDetailById();
172
});
173
</script>
174
175
<style scoped lang="scss"></style>

+ 554 - 3
src/views/flow/SystemDetail.vue

@ -1,12 +1,563 @@
1 1
<template>
2
  <div class="demo">
2
  <common-panel>
3
    <!-- 按钮区域 -->
4
    <div class="system-detail-btns">
5
      <common-select
6
        v-model="curVersionId"
7
        size="small"
8
        :style="{ width: '260px' }"
9
        :props-map="{ label: 'version', value: 'id' }"
10
        :options="versionList"
11
      ></common-select>
12
      <div class="btns">
13
        <template v-for="btn in btns" :key="btn.name">
14
          <el-button
15
            size="small"
16
            class="btn"
17
            :type="btn.type"
18
            @click.stop="btn.onClick"
19
          >
20
            {{ btn.label }}
21
          </el-button>
22
        </template>
23
      </div>
24
    </div>
25
    <!-- 工作区 -->
26
    <common-panel :layer="'row'">
27
      <template #left>
28
        <div class="system-tree">
29
          <common-tree
30
            :tree-data="moduleTreeData"
31
            :actions="actions"
32
            :attrs="defaultProps"
33
            :is-icon="true"
34
            :btn-text="'新增模块'"
35
            @add-root-level="createModule"
36
            @node-click="handelNodeClick"
37
          ></common-tree>
38
        </div>
39
      </template>
40
      <template #right>
41
        <div class="content">
42
          <api-detail-page
43
            v-if="apiDetail"
44
            :api-detail="apiDetail"
45
            @edit-logic-api="editLogicApi"
46
            @init-api-info="initApiInfo"
47
          ></api-detail-page>
48
        </div>
49
      </template>
50
    </common-panel>
51
  </common-panel>
3 52
4
  </div>
53
  <!-- 弹窗信息 -->
54
  <common-drawer
55
    v-if="showDrawer"
56
    :title="moduleTitle"
57
    @close-dialog="closeDialog"
58
    @confirm-dialog="confirmDialog"
59
  >
60
    <!-- 模型 -->
61
    <common-form
62
      v-if="drawerType === 'module'"
63
      :forms="formConfig.module.forms"
64
      :form-values="moduleValues"
65
    ></common-form>
66
67
    <!-- 接口 -->
68
    <common-form
69
      v-if="drawerType === 'logic'"
70
      :forms="logicForms"
71
      :form-values="logicValues"
72
    ></common-form>
73
  </common-drawer>
5 74
</template>
6 75
7 76
<script setup>
77
import { ref, onMounted, getCurrentInstance, computed, watch } from 'vue';
78
import { useRoute, useRouter } from 'vue-router';
79
import { formConfig } from './_config';
80
import ApiDetailPage from './_components/ApiDetailPage.vue';
81
import * as api from '../../api/flow';
82
83
const { proxy } = getCurrentInstance();
84
const router = useRouter();
85
const route = useRoute();
86
const showDrawer = ref(false);
87
const drawerType = ref('module');
88
const operateType = ref('create');
89
const apiDetail = ref(null);
90
91
// 编辑版本
92
function editVersion() {}
93
// 发布版本
94
function publishVersion() {}
95
const btns = ref([
96
  { label: '编辑', name: 'edit', onClick: editVersion, type: 'primary' },
97
  { label: '发布', name: 'publish', onClick: publishVersion, type: 'primary' }
98
]);
99
// 获取系统详情
100
const systemId = computed(() => route.query.id);
101
const curVersionId = ref('');
102
const systemInfo = ref(null);
103
async function getSystemDetail() {
104
  await api.getSystemDetailById(systemId.value).then((res) => {
105
    if (res.resultCode === '0') {
106
      systemInfo.value = res.result;
107
      curVersionId.value = systemInfo.value.version.id;
108
    } else {
109
      systemInfo.value = null;
110
    }
111
  });
112
}
113
114
const logicForms = ref([...formConfig.value.logic.forms]);
115
116
/**
117
 * 版本 start
118
 */
119
// 获取版本列表
120
const versionList = ref([]);
121
function getVersionList() {
122
  api
123
    .getVersionList(systemId.value)
124
    .then((res) => {
125
      if (res.resultCode === '0') {
126
        versionList.value = res.result;
127
      } else {
128
        versionList.value = [];
129
      }
130
    })
131
    .catch((err) => {
132
      console.log(err);
133
    });
134
}
135
/**
136
 * 版本 end
137
 */
138
/**
139
 * 模块相关 start
140
 */
141
142
const moduleTitle = ref('新增模块');
143
const moduleTreeData = ref([]);
144
// 模块表单
145
const curModule = ref('');
146
const curLogic = ref('');
147
const moduleValues = ref({
148
  name: '',
149
  code: '',
150
  desc: ''
151
});
152
153
// 接口表单
154
const logicValues = ref({
155
  name: '',
156
  requestUrl: '',
157
  requestType: '',
158
  desc: ''
159
});
160
const actions = ref([
161
  {
162
    label: '新增逻辑',
163
    name: 'common-yuanxingxinzeng',
164
    iconType: '',
165
    type: 'primary',
166
    size: 14,
167
    // eslint-disable-next-line no-use-before-define
168
    onClick: create
169
  },
170
  {
171
    label: '编辑',
172
    name: 'common-bianji',
173
    iconType: '',
174
    type: 'primary',
175
    size: 14,
176
    // eslint-disable-next-line no-use-before-define
177
    onClick: update
178
  },
179
  {
180
    label: '复制',
181
    name: 'common-fuzhi',
182
    iconType: '',
183
    type: 'primary',
184
    size: 14,
185
    // eslint-disable-next-line no-use-before-define
186
    onClick: clone
187
  },
188
  {
189
    label: '删除',
190
    name: 'common-shanchu',
191
    iconType: '',
192
    type: 'danger',
193
    size: 14,
194
    // eslint-disable-next-line no-use-before-define
195
    onClick: deletItem
196
  }
197
]);
198
199
// 树结构映射
200
const defaultProps = ref({
201
  props: {
202
    children: 'logicServices',
203
    label: 'name',
204
    id: 'id',
205
    'default-expand-all': true
206
  }
207
});
208
// 获取模型树列表
209
function getModuleData() {
210
  const params = {
211
    pageSize: 1000,
212
    pageNumber: 1,
213
    versionId: curVersionId.value
214
  };
215
  api.getModuleList(params).then((res) => {
216
    if (res.resultCode === '0') {
217
      moduleTreeData.value = res.result;
218
    } else {
219
      proxy.$message.error('');
220
    }
221
  });
222
}
223
// 删除模块
224
function deleteModuleById(data) {
225
  api.deleteModuleById(data.id).then((res) => {
226
    if (res.resultCode === '0') {
227
      proxy.$message.success('删除模块成功!');
228
      getModuleData();
229
    } else {
230
      proxy.$message.error(res.resultMsg);
231
    }
232
  });
233
}
234
// 删除接口
235
function deleteLogic(data) {
236
  api.deleteLogicById(data.id).then((res) => {
237
    if (res.resultCode === '0') {
238
      getModuleData();
239
      proxy.$message.success('删除API成功!');
240
      // eslint-disable-next-line no-use-before-define
241
      closeDialog();
242
    } else {
243
      proxy.$message.error(res.resultMsg);
244
    }
245
  });
246
}
247
// 新增模块
248
function createModuleApi(data) {
249
  api.createModule(data).then((res) => {
250
    if (res.resultCode === '0') {
251
      proxy.$message.success('新增模块成功!');
252
      getModuleData();
253
      // eslint-disable-next-line no-use-before-define
254
      closeDialog();
255
    } else {
256
      proxy.$message.error(res.resultMsg);
257
    }
258
  });
259
}
260
// 新增逻辑
261
function createLogic(data) {
262
  api.createLogic(data).then((res) => {
263
    if (res.resultCode === '0') {
264
      proxy.$message.success('新增API成功!');
265
      getModuleData();
266
      // eslint-disable-next-line no-use-before-define
267
      closeDialog();
268
    } else {
269
      proxy.$message.error(res.resultMsg);
270
    }
271
  });
272
}
273
// 克隆模块
274
function cloneModuleById(data) {
275
  api.cloneModuleById(data, curModule.value).then((res) => {
276
    if (res.resultCode === '0') {
277
      proxy.$message.success('克隆模块成功!');
278
      getModuleData();
279
      // eslint-disable-next-line no-use-before-define
280
      closeDialog();
281
    } else {
282
      proxy.$message.error(res.resultMsg);
283
    }
284
  });
285
}
286
287
// 克隆API
288
function cloneLogicById(data) {
289
  api.cloneLogicById(data, curLogic.value).then((res) => {
290
    if (res.resultCode === '0') {
291
      proxy.$message.success('克隆API成功!');
292
      getModuleData();
293
      // eslint-disable-next-line no-use-before-define
294
      closeDialog();
295
    } else {
296
      proxy.$message.error(res.resultMsg);
297
    }
298
  });
299
}
300
// 编辑模块
301
function updateModule(data) {
302
  api.updateModuleById(data, curModule.value).then((res) => {
303
    if (res.resultCode === '0') {
304
      proxy.$message.success('修改模块成功!');
305
      getModuleData();
306
      // eslint-disable-next-line no-use-before-define
307
      closeDialog();
308
    } else {
309
      proxy.$message.error(res.resultMsg);
310
    }
311
  });
312
}
313
// 编辑逻辑
314
function updateLogic(data) {
315
  api.updateFlowDataById(data, curLogic.value).then((res) => {
316
    if (res.resultCode === '0') {
317
      proxy.$message.success('修改API成功!');
318
      getModuleData();
319
      // eslint-disable-next-line no-use-before-define
320
      closeDialog();
321
    } else {
322
      proxy.$message.error(res.resultMsg);
323
    }
324
  });
325
}
326
// 删除模块/API
327
function deletItem(node, data) {
328
  let text = '';
329
  if (node.level === 1) {
330
    text = '确定删除该服务分类吗?';
331
  } else if (node.level === 2) {
332
    text = '确定删除该API吗?';
333
  }
334
  proxy
335
    .$confirm(text, '警告', {
336
      confirmButtonText: '确定',
337
      cancelButtonText: '取消',
338
      type: 'warning'
339
    })
340
    .then(() => {
341
      if (node.level === 1) {
342
        deleteModuleById(data);
343
      } else if (node.level === 2) {
344
        deleteLogic(data);
345
      }
346
    });
347
}
348
349
// 新增模块
350
function createModule() {
351
  drawerType.value = 'module';
352
  operateType.value = 'create';
353
  moduleTitle.value = '新增模块';
354
  showDrawer.value = true;
355
}
356
357
// 修改模块/API
358
function update(node, data) {
359
  console.log(node);
360
  showDrawer.value = true;
361
  operateType.value = 'edit';
362
  if (node.level === 1) {
363
    drawerType.value = 'module';
364
365
    moduleTitle.value = '编辑模块';
366
    curModule.value = data.id;
367
    moduleValues.value = {
368
      name: data.name,
369
      code: data.code,
370
      desc: data.desc
371
    };
372
  } else if (node.level === 2) {
373
    drawerType.value = 'logic';
374
    moduleTitle.value = '编辑API';
375
    logicForms.value = [...formConfig.value.logic.forms];
376
    curLogic.value = data.id;
377
    logicValues.value = {
378
      name: data.name,
379
      requestUrl: data.requestUrl,
380
      requestType: data.requestType,
381
      desc: data.desc
382
    };
383
  }
384
}
385
386
// 复制模块/API
387
function clone(node, data) {
388
  showDrawer.value = true;
389
  operateType.value = 'clone';
390
  if (node.level === 1) {
391
    drawerType.value = 'module';
392
    moduleTitle.value = '克隆模块';
393
    curModule.value = data.id;
394
    moduleValues.value = {
395
      name: data.name,
396
      code: data.code,
397
      desc: data.desc
398
    };
399
  } else if (node.level === 2) {
400
    drawerType.value = 'logic';
401
    moduleTitle.value = '克隆API';
402
    curLogic.value = data.id;
403
    logicForms.value = [
404
      {
405
        name: 'moduleId',
406
        type: 'select',
407
        label: '选择模块',
408
409
        attrs: {
410
          col: 1,
411
          style: {
412
            width: '276px'
413
          },
414
          propsMap: {
415
            label: 'name',
416
            value: 'id'
417
          },
418
          options: moduleTreeData.value,
419
          clearable: false
420
        }
421
      },
422
      ...logicForms.value
423
    ];
424
425
    logicValues.value = {
426
      name: data.name,
427
      requestUrl: data.requestUrl,
428
      requestType: data.requestType,
429
      desc: data.desc
430
    };
431
  }
432
}
433
// 获取接口详情
434
async function getApiDetail() {
435
  await api.getLogicDetailById(curLogic.value).then((res) => {
436
    if (res.resultCode === '0') {
437
      apiDetail.value = res.result;
438
    } else {
439
      proxy.$message.error(res.resultMsg);
440
    }
441
  });
442
}
443
// 节点点击
444
function handelNodeClick(data) {
445
  const isLeaf = 'totalPath' in data;
446
  if (isLeaf) {
447
    curLogic.value = data.id;
448
    getApiDetail();
449
  }
450
}
451
452
/**
453
 * 模块相关 end
454
 */
455
456
/**
457
 * logic start
458
 */
459
function create(node, data) {
460
  operateType.value = 'create';
461
  if (node.level === 1) {
462
    drawerType.value = 'logic';
463
    logicForms.value = [...formConfig.value.logic.forms];
464
    moduleTitle.value = '新增API';
465
    showDrawer.value = true;
466
    curModule.value = data.id;
467
  }
468
}
469
470
/**
471
 * logic end
472
 */
473
474
// 弹窗
475
function closeDialog() {
476
  showDrawer.value = false;
477
  drawerType.value = '';
478
  operateType.value = '';
479
}
480
481
// 确认
482
function confirmDialog() {
483
  if (drawerType.value === 'module') {
484
    const params = { ...moduleValues.value };
485
    switch (operateType.value) {
486
      case 'create':
487
        params.versionId = curVersionId.value;
488
        createModuleApi(params);
489
        break;
490
      case 'clone':
491
        params.versionId = curVersionId.value;
492
        cloneModuleById(params);
493
        break;
494
      case 'edit':
495
        updateModule(params);
496
        break;
497
      default:
498
        break;
499
    }
500
  } else if (drawerType.value === 'logic') {
501
    const params = {
502
      ...logicValues.value
503
    };
504
    switch (operateType.value) {
505
      case 'create':
506
        params.moduleId = curModule.value;
507
        createLogic(params);
508
        break;
509
      case 'clone':
510
        cloneLogicById(params);
511
        break;
512
      case 'edit':
513
        params.moduleId = curModule.value;
514
        updateLogic(params);
515
        break;
516
      default:
517
        break;
518
    }
519
  }
520
}
521
522
// 编辑api
523
function editLogicApi() {
524
  router.push({
525
    name: 'Flow',
526
    query: {
527
      id: curLogic.value,
528
      sysId: systemId.value
529
    }
530
  });
531
}
532
533
// 测试
534
function initApiInfo() {}
535
536
watch(curLogic.value, () => {
537
  getApiDetail();
538
});
539
// watch 监听
540
onMounted(async () => {
541
  await getSystemDetail();
542
  await getVersionList();
543
  await getModuleData();
544
});
8 545
</script>
9 546
10 547
<style scoped lang="scss">
11
548
.system-tree {
549
  height: 100%;
550
  background: #fff;
551
}
552
.system-detail-btns {
553
  display: flex;
554
  justify-content: space-between;
555
  padding: 0 12px;
556
  background: #fff;
557
  box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.06);
558
  height: 50px;
559
  align-items: center;
560
  width: calc(100% - 48px);
561
  margin-left: 24px;
562
}
12 563
</style>

+ 13 - 11
src/views/flow/SystemList.vue

@ -24,9 +24,11 @@
24 24
25 25
<script setup>
26 26
import { ref, onMounted, getCurrentInstance } from 'vue';
27
import { useRouter } from 'vue-router';
27 28
import * as api from '../../api/flow';
28 29
import { formConfig } from './_config';
29 30
31
const router = useRouter();
30 32
const { proxy } = getCurrentInstance();
31 33
const dialogTitle = ref('新增系统');
32 34
const showDialog = ref(false);
@ -39,17 +41,6 @@ const cardNumbers = ref([
39 41
]);
40 42
const cardInfos = ref([]);
41 43
42
// 新增系统
43
// function createSystem() {}
44
45
// 编辑系统
46
// function updateSystemById() {}
47
48
// 删除系统
49
// function deleteSystemById() {
50
//   proxy.$confirm('1231');
51
// }
52
53 44
// 复制系统
54 45
// function copySystemById() {}
55 46
// 编辑
@ -74,9 +65,20 @@ function deleteSystem() {
74 65
}
75 66
// 版本切换
76 67
function changeVersion() {}
68
69
// 查看详情
70
function viewDetail(item) {
71
  router.push({
72
    name: 'SystemDetail',
73
    query: {
74
      id: item.id
75
    }
76
  });
77
}
77 78
// 复制
78 79
function copySystem() {}
79 80
const btns = ref([
81
  { label: '查看', name: 'view', onClick: viewDetail, icon: 'common-chakan' },
80 82
  { label: '编辑', name: 'edit', onClick: updateSystem, icon: 'common-bianji' },
81 83
  {
82 84
    label: '删除',

+ 417 - 0
src/views/flow/_components/ApiDetailPage.vue

@ -0,0 +1,417 @@
1
<template>
2
  <div class="api-detail">
3
    <h4 class="title">接口使用示例</h4>
4
    <div class="card info">
5
      <!-- card-hedaer -->
6
      <div class="card-header">
7
        <h4 class="sub-title">基本信息</h4>
8
        <div v-if="props.type !== '0'">
9
          <!--
10
            UNTESTED("1", "待测试"),
11
            TESTED("2", "已测试"),
12
            STARTED("3", "启用"),
13
            STOPPED("4", "停用"),
14
            DELETED("5", "废弃");
15
          -->
16
          <el-button
17
            :disabled="props.versionState === '0'"
18
            @click.stop="stopTest"
19
          >
20
            测试
21
          </el-button>
22
          <el-button
23
            :disabled="
24
              apiDetail?.state !== 'STARTED' || props.versionState === '0'
25
            "
26
            @click.stop="stopLogic"
27
          >
28
            停用
29
          </el-button>
30
          <el-button type="success" @click.stop="editLogicApi">
31
            编辑逻辑流
32
          </el-button>
33
        </div>
34
      </div>
35
36
      <!-- card-info -->
37
      <el-row :gutter="18">
38
        <el-col :span="5">
39
          <div class="row">
40
            <span class="row-title">接口名称</span>
41
            <p class="row-content">{{ apiDetail?.name }}</p>
42
          </div>
43
        </el-col>
44
45
        <el-col :span="5">
46
          <div class="row">
47
            <span class="row-title">所属模块</span>
48
            <p class="row-content">{{ apiDetail?.name }}</p>
49
          </div>
50
        </el-col>
51
        <el-col :span="5">
52
          <div class="row">
53
            <span class="row-title">接口状态</span>
54
            <p class="row-content">{{ apiDetail?.stateDesc }}</p>
55
          </div>
56
        </el-col>
57
        <el-col :span="9">
58
          <div class="row">
59
            <span class="row-title">请求url</span>
60
            <p class="row-content">
61
              {{ apiDetail?.totalPath }}
62
            </p>
63
          </div>
64
        </el-col>
65
      </el-row>
66
      <div class="row">
67
        <span class="row-title">接口描述</span>
68
        <p class="row-content desc">
69
          {{ apiDetail?.desc }}
70
        </p>
71
      </div>
72
    </div>
73
74
    <div class="tabs">
75
      <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
76
        <el-tab-pane label="接口信息" name="info"></el-tab-pane>
77
        <el-tab-pane label="接口测试" name="test"></el-tab-pane>
78
      </el-tabs>
79
    </div>
80
81
    <!-- 接口信息 -->
82
    <div v-show="activeName === 'info'" class="api-info">
83
      <div class="card">
84
        <h4 class="sub-title">请求参数</h4>
85
        <common-table
86
          :table-header="request.header"
87
          :table-data="request?.data"
88
        ></common-table>
89
      </div>
90
      <div class="card">
91
        <h4 class="sub-title">响应参数</h4>
92
        <common-table
93
          :table-header="request.header"
94
          :table-data="response?.data"
95
        ></common-table>
96
      </div>
97
    </div>
98
99
    <!-- 接口测试 -->
100
    <div v-show="activeName === 'test'" class="api-text">
101
      <div class="card">
102
        <h4 class="sub-title">请求URL</h4>
103
        <el-input
104
          v-model="testRequest.url"
105
          placeholder="请输入请求URL"
106
          class="input-with-select"
107
        >
108
          <template #prepend>
109
            <common-select
110
              v-model="testRequest.method"
111
              disabled
112
              class="select"
113
              :options="methodOptions"
114
            ></common-select>
115
          </template>
116
          <template #append>
117
            <el-button @click.stop="test">提交</el-button>
118
          </template>
119
        </el-input>
120
      </div>
121
      <div class="card">
122
        <h4 class="sub-title">请求参数</h4>
123
        <!-- <ipu-input
124
          v-model="testRequest.params"
125
          class="result"
126
          v-bind="attributes"
127
        ></ipu-input> -->
128
        <div>
129
          <JsonEditorVue
130
            v-model="testRequest.params"
131
            class="editor"
132
            @blur="validate"
133
          ></JsonEditorVue>
134
        </div>
135
      </div>
136
137
      <div class="card">
138
        <h4 class="sub-title">响应参数</h4>
139
        <!-- <ipu-input
140
          v-model="testResponse"
141
          class="result"
142
          v-bind="attributes"
143
          disabled
144
        ></ipu-input> -->
145
        <div>
146
          <!-- <JsonEditorVue
147
            v-model="testResponse"
148
            class="editor"
149
            @blur="validate"
150
          ></JsonEditorVue> -->
151
        </div>
152
      </div>
153
    </div>
154
  </div>
155
</template>
156
157
<script setup>
158
// import JsonEditorVue from 'json-editor-vue3';
159
import { getCurrentInstance, onMounted, ref, watch } from 'vue';
160
161
const { proxy } = getCurrentInstance();
162
const emits = defineEmits(['editLogicApi', 'initApiInfo']);
163
164
const activeName = ref('info');
165
166
const props = defineProps({
167
  apiDetail: {
168
    type: Object,
169
    default: () => {}
170
  },
171
  versionState: {
172
    type: String,
173
    default: '0'
174
  },
175
  type: {
176
    type: String,
177
    default: '1'
178
  }
179
});
180
181
function editLogicApi() {
182
  emits('editLogicApi');
183
}
184
185
function initApiInfo() {
186
  emits('initApiInfo');
187
}
188
// 测试按钮
189
function stopTest() {
190
  proxy.$utils.confirmMessage('确定测试该接口吗?').then(() => {
191
    const url = proxy.$api.logicMgmt;
192
    const params = {
193
      stateValue: 3
194
    };
195
    proxy
196
      .$reqPatch(`${url}/${props.apiDetail?.id}`, params)
197
      .then((res) => {
198
        if (res.data.resultCode === '0') {
199
          proxy.$utils.ipuMessage('测试成功');
200
        } else {
201
          proxy.$utils.ipuMessage(res.data.resultMsg, 'error');
202
        }
203
      })
204
      .catch((err) => {
205
        console.log(err);
206
      })
207
      .finally(() => {
208
        initApiInfo();
209
      });
210
  });
211
}
212
// 停用
213
function stopLogic() {
214
  proxy.$utils.confirmMessage('确定停用该api吗?').then(() => {
215
    const url = proxy.$api.logicMgmt;
216
    const params = {
217
      stateValue: '4'
218
    };
219
    proxy
220
      .$reqPatch(`${url}/${props.apiDetail?.id}`, params)
221
      .then((res) => {
222
        if (res.data.resultCode === '0') {
223
          proxy.$utils.ipuMessage('api已下架!');
224
        } else {
225
          proxy.$utils.ipuMessage(res.data.resultMsg, 'error');
226
        }
227
      })
228
      .catch((err) => {
229
        console.log(err);
230
      });
231
  });
232
}
233
// 接口详情
234
function apiDetailHooks() {
235
  const request = ref({
236
    header: [
237
      { label: '参数', name: 'code', 'min-width': '150' },
238
      { label: '名称', name: 'name', 'min-width': '150' },
239
      { label: '类型', name: 'type', 'min-width': '150' },
240
      { label: '是否必传', name: 'required', 'min-width': '150' },
241
      { label: '描述', name: 'desc', 'min-width': '150' }
242
    ],
243
    data: props.apiDetail?.request
244
  });
245
246
  const response = ref({
247
    data: props.apiDetail?.response
248
  });
249
  // 数据初始化
250
  function initDetailData() {
251
    response.value = {
252
      data: props.apiDetail?.response
253
    };
254
    request.value.data = props.apiDetail?.request;
255
  }
256
257
  return {
258
    request,
259
    response,
260
    initDetailData
261
  };
262
}
263
264
const { request, response, initDetailData } = apiDetailHooks();
265
266
const dataTypeMap = {
267
  int: 1,
268
  string: '',
269
  array: [],
270
  object: {},
271
  decimal: 0.1,
272
  date: '2022-10-10',
273
  datetime: '2022-10-10 12:10:10',
274
  boolean: true,
275
  long: 0
276
};
277
// 接口测试
278
function testHooks() {
279
  const testRequest = ref({});
280
  const testResponse = ref({});
281
  // 数据初始化
282
  function updateTestData() {
283
    // if (!request.value.data) {
284
    //   return
285
    // }
286
    const params = {};
287
    (request.value?.data || [])?.forEach((item) => {
288
      params[item.code] = dataTypeMap[item.type];
289
    });
290
    testRequest.value = {
291
      url: props.apiDetail?.totalPath,
292
      method: props.apiDetail?.requestType,
293
      params,
294
      headers: {}
295
    };
296
    testResponse.value = {};
297
  }
298
  updateTestData();
299
  function test() {
300
    const { testLogic } = proxy.$api;
301
    const { params } = testRequest.value;
302
    proxy
303
      .$reqPost(`${testLogic}/${props.apiDetail.id}`, params)
304
      .then((res) => {
305
        if (res.data.resultCode === '0') {
306
          proxy.$utils.ipuMessage('请求成功!');
307
          testResponse.value = res.data.result;
308
        } else {
309
          proxy.$utils.ipuMessage(res.data.resultMsg, 'error');
310
        }
311
      })
312
      .catch((err) => {
313
        console.log(err);
314
      });
315
  }
316
  return {
317
    testRequest,
318
    test,
319
    testResponse,
320
    updateTestData
321
  };
322
}
323
324
const { testRequest, test, updateTestData } = testHooks();
325
function validate() {}
326
watch(
327
  props,
328
  () => {
329
    initDetailData();
330
    updateTestData();
331
  },
332
  { deep: true }
333
);
334
335
onMounted(() => {});
336
</script>
337
338
<style scoped lang="scss">
339
.api-detail {
340
  padding: 24px;
341
  width: auto;
342
  height: calc(100vh - 250px);
343
  overflow: auto;
344
  .card {
345
    width: 100%;
346
    background: #fff;
347
    padding: 24px;
348
    border-radius: 3px;
349
    box-sizing: border-box;
350
    .card-header {
351
      display: flex;
352
      justify-content: space-between;
353
    }
354
    .row {
355
      margin-top: 15px;
356
      .row-title {
357
        color: #909399;
358
        font-size: 14px;
359
        line-height: 28px;
360
      }
361
      .row-content {
362
        background: #f5f7fa;
363
        border-radius: 3px;
364
        height: 32px;
365
        padding: 5px 10px;
366
        box-sizing: border-box;
367
        border-radius: 3px;
368
      }
369
      .desc {
370
        height: 70px;
371
      }
372
    }
373
    .result {
374
      :deep(.el-textarea__inner) {
375
        padding: 20px;
376
      }
377
    }
378
    .select {
379
      width: 90px;
380
    }
381
  }
382
  .tabs {
383
    width: 100%;
384
    background: #fff;
385
    // padding: 24px;
386
    padding: 0 24px;
387
    border-radius: 3px;
388
    box-sizing: border-box;
389
    margin-top: 15px;
390
  }
391
}
392
.title {
393
  font-size: 18px;
394
  color: #303133;
395
  font-weight: 500;
396
  margin-bottom: 10px;
397
}
398
.sub-title {
399
  font-size: 16px;
400
  color: #303133;
401
  font-weight: 500;
402
  margin-bottom: 10px;
403
}
404
.editor {
405
  height: 300px;
406
}
407
</style>
408
409
<style lang="scss">
410
.jsoneditor {
411
  border: 1px solid rgba(0, 0, 0, 0.4);
412
}
413
.jsoneditor-menu {
414
  background: rgba(0, 0, 0, 0.4);
415
  border-bottom: 1px solid rgba(0, 0, 0, 0.4);
416
}
417
</style>

+ 117 - 0
src/views/flow/_config.js

@ -41,5 +41,122 @@ export const formConfig = ref({
41 41
      desc: '',
42 42
      code: ''
43 43
    }
44
  },
45
  module: {
46
    forms: [
47
      {
48
        name: 'name',
49
        type: 'input',
50
        label: '名称',
51
        span: 24,
52
        attrs: {
53
          style: {
54
            width: '276px'
55
          },
56
          clearable: true,
57
          placeholder: '请输入名称'
58
        }
59
      },
60
      {
61
        name: 'code',
62
        type: 'input',
63
        label: '编码',
64
        span: 24,
65
        attrs: {
66
          style: {
67
            width: '276px'
68
          },
69
          clearable: true,
70
          placeholder: '请输入编码'
71
        }
72
      },
73
      {
74
        name: 'desc',
75
        type: 'input',
76
        label: '描述',
77
        span: 24,
78
        attrs: {
79
          type: 'textarea',
80
          maxlength: 300,
81
          clearable: true,
82
          placeholder: '请输入描述信息,最多可输入300字',
83
          minRows: 5,
84
          maxRows: 8,
85
          rows: 6,
86
          style: {
87
            width: '100%'
88
          }
89
        }
90
      }
91
    ]
92
  },
93
  logic: {
94
    forms: [
95
      {
96
        name: 'name',
97
        type: 'input',
98
        label: '名称',
99
        span: 24,
100
        attrs: {
101
          style: {
102
            width: '276px'
103
          },
104
          clearable: true,
105
          placeholder: '请输入名称'
106
        }
107
      },
108
      {
109
        name: 'requestUrl',
110
        type: 'input',
111
        label: 'URL',
112
        span: 24,
113
        attrs: {
114
          style: {
115
            width: '276px'
116
          },
117
          clearable: true,
118
          placeholder: '请输入URL'
119
        }
120
      },
121
      {
122
        name: 'requestType',
123
        type: 'select',
124
        label: '请求方法',
125
        span: 24,
126
        attrs: {
127
          style: {
128
            width: '276px'
129
          },
130
          options: [
131
            { label: 'GET', value: 'GET' },
132
            { label: 'POST', value: 'POST' },
133
            { label: 'PUT', value: 'PUT' },
134
            { label: 'DELETE', value: 'DELETE' },
135
            { label: 'PATCH', value: 'PATCH' },
136
            { label: 'WS', value: 'WS' }
137
          ],
138
          clearable: true,
139
          placeholder: '请输入URL'
140
        }
141
      },
142
      {
143
        name: 'desc',
144
        type: 'input',
145
        label: '描述',
146
        span: 24,
147
        attrs: {
148
          type: 'textarea',
149
          maxlength: 300,
150
          clearable: true,
151
          placeholder: '请输入描述信息,最多可输入300字',
152
          minRows: 5,
153
          maxRows: 8,
154
          rows: 6,
155
          style: {
156
            width: '100%'
157
          }
158
        }
159
      }
160
    ]
44 161
  }
45 162
});

+ 9 - 1
src/views/sub-flow/SubFlow.vue

@ -1,7 +1,7 @@
1 1
<template>
2 2
  <div class="subflow-logic">
3 3
    <sub-flow
4
      :nodes="nodes.nodes"
4
      :nodes="nodeList"
5 5
      :tool-list="toolList"
6 6
      :flow-data="subFlowDetail"
7 7
      @tool-handel-click="toolHandelClick"
@ -23,6 +23,14 @@ import nodes from '../_components/_flow/nodes.js';
23 23
import * as api from '../../api/subflow';
24 24
import NodeDataFormate from '../_components/_flow/nodeDataFormate.js';
25 25
26
const nodeList = ref(nodes.nodes);
27
nodeList.value.map((item) => {
28
  if (item.nodes?.length) {
29
    item.nodes = item.nodes.filter((el) => el.name !== 'subprocess');
30
  }
31
  return item;
32
});
33
26 34
const { proxy } = getCurrentInstance();
27 35
const route = useRoute();
28 36
const subFlowDetail = ref(null);