Toggle navigation
Toggle navigation
This project
Loading...
Sign in
Hooke
/
vue-flow-editor
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Builds
Commits
Issue Boards
Authored by
hookehuyr
2023-11-23 14:09:39 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
dab01c575138f012b35744d30660839bf59e56da
dab01c57
1 parent
865c1ed5
API联调
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
247 additions
and
98 deletions
doc/App.vue
doc/selectUserView.vue
package.json
yarn-error.log
yarn.lock
doc/App.vue
View file @
dab01c5
...
...
@@ -314,6 +314,7 @@
<select-user-view
:visible="state.dialogUserFormVisible"
:list="state.dialogUserTags"
@close="onCloseUserView"
@confirm="onConfirmUserView"
/>
...
...
@@ -323,7 +324,7 @@
import { ref, reactive, onMounted, watch, nextTick } from 'vue'
import { AppData } from './data.js'
import { staticPath } from './utils'
import { ElNotification, ElMessage, ElMessageBox } from 'element-plus'
import { ElNotification, ElMessage, ElMessageBox
, ElLoading
} from 'element-plus'
import axios from './axios'
import $ from 'jquery'
import { Calendar, Search } from '@element-plus/icons-vue'
...
...
@@ -332,6 +333,7 @@ import { Function } from 'lodash'
import { extend } from '@vue/shared'
import { v4 as uuidv4 } from 'uuid';
import type { FormInstance, FormRules } from 'element-plus'
import qs from 'qs'
const G6 = (window as any).G6.default as any
...
...
@@ -452,6 +454,7 @@ export default {
},
search_auth_value: '',
dialogUserFormVisible: false,
dialogUserTags: [],
activeName: 'node',
attr_radio: '基础属性',
main_attr_set: true,
...
...
@@ -467,53 +470,47 @@ export default {
userTags: [], // 节点负责人,
auth_all_checked: false,
auth_all_edit: false,
field_auths: [ // 字段权限
{
name: '字段1',
visible: {
checked: false,
disabled: true,
},
editable: {
checked: false,
disabled: true,
},
show: true,
},
{
name: '字段2',
visible: {
checked: true,
disabled: false,
},
editable: {
checked: false,
disabled: false,
},
show: true,
},
{
name: '字段3',
visible: {
checked: true,
disabled: false,
},
editable: {
checked: false,
disabled: false,
},
show: true,
},
],
field_auths: [],
})
/**
* 更新URL
* @param flowId
*/
const updateUrl = (flowId: string) => {
// 获取当前 URL
const url = new URL(window.location.href);
// 获取 flow_id 的值(可以是一个变量)
// const flowId = 'some_value';
// 获取 URL 中的查询参数对象
const searchParams = url.searchParams;
// 检查是否存在 form_id 参数
if (!searchParams.has('form_id')) {
// 如果不存在 form_id 参数,则添加 form_id 和 flow_id 参数
searchParams.append('flow_id', flowId);
} else {
// 如果存在 form_id 参数,则更新 flow_id 参数的值
searchParams.set('flow_id', flowId);
}
// 将更新后的查询参数设置回 URL 对象
url.search = searchParams.toString();
// 修改完 URL 后,更新浏览器地址栏显示的 URL
window.history.replaceState(null, '', url.toString());
}
/**
* 获取url参数
* @param url
*/
function getQueryParams(url: string) {
const params = {
flow_id: '',
form_id: '',
};
// 将url以问号为分隔符拆分为两部分
const parts = url.split("?");
...
...
@@ -533,9 +530,13 @@ export default {
return params;
}
const urlQuery = getQueryParams(location.href);
let flow_id = urlQuery.flow_id ? urlQuery.flow_id : ''; // 流程id,如果是新创建的流程,则为空
let form_id = urlQuery.form_id? urlQuery.form_id : ''; // 表单id
// TAG: 接口获取流程图数据
const flowData = ref<any>(null);
axios.get('/admin/?a=flow_nodes&flow_id=' +
getQueryParams(location.href).
flow_id)
axios.get('/admin/?a=flow_nodes&flow_id=' + flow_id)
.then(res => {
if (res.data.code) {
let nodes = res.data.data.nodes;
...
...
@@ -543,6 +544,21 @@ export default {
// 没有流程图数据
if (!nodes.length && !edges.length) {
flowData.value = AppData; // 设置默认的数据
// 马上保存一次
axios.post('/admin/?a=save_flow', qs.stringify({
form_id: +form_id,
flow_id: '',
data: JSON.stringify(AppData)
}))
.then(res => {
if (res.data.code) {
flow_id = res.data.data; // 更新flow_id
updateUrl(flow_id); // 更新url
}
})
.catch(err => {
console.log(err);
});
} else {
flowData.value = res.data.data; // 获取已存在的数据
}
...
...
@@ -698,7 +714,7 @@ export default {
/****** 用户选择控件弹框 ******/
const openUserForm = () => {
// 打开设置用户弹框
state.dialogUserFormVisible = true
state.dialogUserFormVisible = true
;
}
const onCloseUserView = (status: boolean) => {
...
...
@@ -758,25 +774,67 @@ export default {
model.labelCfg = model.labelCfg || { style: {} }
model.data = model.data ? model.data : {};
// 查询节点的属性
// 节点名称 state.node_name,节点负责人 state.userTags,基础属性 state.field_auths,更多属性 state.more_attr
console.warn('节点名称', state.node_name);
console.warn('节点负责人', state.userTags);
console.warn('基础属性', state.field_auths);
console.warn('更多属性', state.more_attr); // 非结束节点才显示
state.detailModel = model
// 判断是否是流程节点
let model_id = model.id
if (model_id!== 'end-node') {
state.detailModel = model;
// 获取节点名称
state.node_name = state.detailModel.text;
// 检查字段权限选中情况
checkAuthAll('visible');
checkAuthAll('editable');
if (model_id !== 'end-node') {
// TODO: 接口查询节点的属性
// 节点名称 state.node_name,节点负责人 state.userTags,基础属性 state.field_auths,更多属性 state.more_attr
console.warn('基础属性', state.field_auths);
console.warn('更多属性', state.more_attr);
const loading = ElLoading.service({
target: document.getElementById('app'),
lock: true,
text: '加载中',
// background: 'rgba(f, f, f, 0.7)',
});
axios.get('/admin/?a=flow_node_property&node_code=' + model.id + '&flow_id=' + flow_id)
.then((res: any) => {
if (res.data.code) {
state.node_name = res.data.data.name ? res.data.data.name : model.text;
state.userTags = res.data.data.user;
state.dialogUserTags = state.userTags;
// TODO: 需要处理更多属性数据,节点类型是抄送时不显示节点操作
state.field_auths = [ // 字段权限
{
name: '字段1',
visible: {
checked: false,
disabled: true,
},
editable: {
checked: false,
disabled: true,
},
show: true,
},
{
name: '字段2',
visible: {
checked: true,
disabled: false,
},
editable: {
checked: false,
disabled: false,
},
show: true,
},
{
name: '字段3',
visible: {
checked: true,
disabled: false,
},
editable: {
checked: false,
disabled: false,
},
show: true,
},
]
state.more_attr = [ // 更多属性
{
id: 'no-1',
...
...
@@ -835,6 +893,9 @@ export default {
],
}
];
// 检查字段权限选中情况
checkAuthAll('visible');
checkAuthAll('editable');
if (state.detailModel.control === 'cc') {
state.more_attr = state.more_attr.filter((ele: any) => {
return ele.label !== '节点操作'
...
...
@@ -843,6 +904,19 @@ export default {
// 打开属性表单
state.attr_radio = '基础属性'; // 还原tab默认值
editor.openModel();
loading.close();
} else {
loading.close();
ElMessage({
type: 'error',
message: res.data.msg,
});
}
})
.catch((err: any) => {
loading.close();
console.log(err);
});
} else {
editor.closeModel()
}
...
...
@@ -900,21 +974,70 @@ export default {
*
*/
async function saveForm() {
// if (!formRef.value) return
// await formRef.value.validate((valid) => {
// if (!valid) {
// return false
// }
// })
if (state.node_name === '') {
ElMessage({
type: 'error',
message: '节点名称不能为空',
});
return;
}
if (state.userTags.length === 0) {
ElMessage({
type: 'error',
message: '节点负责人不能为空',
});
return;
}
let avail_visible_count = state.field_auths.filter((ele) => {
if (ele.visible.checked && !ele.visible.disabled) {
return ele;
}
});
let avail_editable_count = state.field_auths.filter((ele) => {
if (ele.editable.checked && !ele.editable.disabled) {
return ele;
}
});
if (avail_visible_count.length === 0 && avail_editable_count.length === 0) {
ElMessage({
type: 'error',
message: '请至少选择一个字段权限',
});
return;
}
// TAG: 保存表单信息
axios.post('/admin/?a=save_node_property', qs.stringify({
flow_id: +flow_id,
node_code: state.detailModel.id,
// data: JSON.stringify({ name: state.node_name, users: state.userTags, field_auths: state.field_auths, more_attr: state.more_attr })
data: JSON.stringify({ name: state.node_name, users: state.userTags })
}))
.then(res => {
if (res.data.code) {
// console.log(state.detailModel.id)
state.detailModel.text = state.node_name;
// state.detailModel.label = state.node_name
//
// state.detailModel.label = state.node_name
// 更新流程图信息
editor.updateModel(state.detailModel);
editor.closeModel();
console.log('节点名称', state.node_name);
console.log('节点负责人', state.userTags);
console.log('字段权限', state.field_auths);
console.log('更多属性', state.more_attr);
// console.log('节点名称', state.node_name);
// console.log('节点负责人', state.userTags);
// console.log('字段权限', state.field_auths);
// console.log('更多属性', state.more_attr);
ElMessage({
type:'success',
message: '保存成功',
});
} else {
ElMessage({
type: 'error',
message: res.data.msg,
});
}
})
.catch(err => {
console.error(err);
});
}
/**
...
...
@@ -1104,19 +1227,14 @@ export default {
* @return {void} No return value.
*/
function saveData(): void {
let { nodes, edges } = editor.editorState.graph.save()
// console.log("nodes", nodes);
// console.log("edges", edges);
let { nodes, edges } = editor.editorState.graph.save();
// 使用时需要把自定义节点的类型带过去 activity/control
nodes.forEach((node: { [x: string]: string; shape: string }) => {
// if (node.shape === 'activity') {
// node['shape'] = 'activity_' + node['activity']
// }
if (node.shape === 'control') {
// node['shape'] = 'control_' + node['control']
node['control'] = node['control']
}
})
})
;
nodes = nodes.map(
({ data, id, label, shape, x, y, text, desc, img, control }) => ({
...
...
@@ -1131,14 +1249,13 @@ export default {
img,
control,
}),
)
)
;
edges = edges.map(({ source, sourceAnchor, target, targetAnchor }) => ({
source,
sourceAnchor,
target,
targetAnchor,
}))
// console.log(JSON.stringify({ nodes, edges }, null, 2));
}));
ElMessageBox.confirm(
'是否确定保存流程图?',
...
...
@@ -1151,15 +1268,32 @@ export default {
)
.then(() => {
// 检查路径有效性
// let { edges } = editor.editorState.graph.save()
const paths = [];
findPathsToEndNode(edges, 'start-node', [], paths);
console.log(paths); // 输出满足条件的路径结果数组
if (paths.length) {
axios.post('/admin/?a=save_flow', qs.stringify({
form_id: +form_id,
flow_id: +flow_id,
data: JSON.stringify({ nodes, edges })
}))
.then(res => {
if (res.data.code) {
ElMessage({
type: 'success',
message: '保存流程图成功',
});
flow_id = res.data.data.flow_id; // 更新flow_id
console.log(paths); // 输出满足条件的路径结果数组
} else {
ElMessage({
type: 'error',
message: res.data.msg,
});
}
})
.catch(err => {
console.log(err);
});
} else {
ElNotification.error('缺少一条从开始节点到结束节点的完整流程!');
}
...
...
doc/selectUserView.vue
View file @
dab01c5
<!--
* @Date: 2023-11-01 10:18:53
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2023-11-2
2 17:29:49
* @LastEditTime: 2023-11-2
3 10:24:27
* @FilePath: /vue-flow-editor/doc/selectUserView.vue
* @Description: 成员列表选择控件
-->
...
...
@@ -232,7 +232,8 @@ import _ from "lodash";
import { ElNotification, ElMessage, ElMessageBox } from 'element-plus'
const props = defineProps({
visible: Boolean
visible: Boolean,
list: Array,
});
const emit = defineEmits(["close", "confirm"]);
...
...
@@ -316,8 +317,6 @@ onMounted(async () => {
message: role_list.data.msg,
});
}
// TODO: 查询到的用户选中列表数据,等待接口数据
userTags.value = [{ id: 35697, name: "西园寺" },{ id: 492081, name: "营员" }];
// Tabs菜单的完整数据结构
userTabs.value.forEach(item => {
if (item.type === 'corp-tree') {
...
...
@@ -330,17 +329,6 @@ onMounted(async () => {
item.data = roleList.value;
}
});
// 第一项目是组织结构树,默认展开第一个节点
if (userTabs.value[activeTabIdx.value]['type'] === 'corp-tree') {
if (userTabs.value[activeTabIdx.value]['data'].length) {
// 默认展开第一个节点
defaultExpandedKeys.value = [userTabs.value[activeTabIdx.value]['data'][0]['id']];
// 把用户选中的节点注入树结构显示选中状态
nextTick(() => {
currentCheckedNodeKey.value = userTags.value.map(ele => ele.id);
});
}
}
});
watch(() => {
...
...
@@ -369,6 +357,25 @@ watch(
}
}
);
watch(
() => props.list,
val => {
if (val) {
userTags.value = val;
// 第一项目是组织结构树,默认展开第一个节点
if (userTabs.value[activeTabIdx.value]['type'] === 'corp-tree') {
if (userTabs.value[activeTabIdx.value]['data'].length) {
// 默认展开第一个节点
defaultExpandedKeys.value = [userTabs.value[activeTabIdx.value]['data'][0]['id']];
// 把用户选中的节点注入树结构显示选中状态
nextTick(() => {
currentCheckedNodeKey.value = userTags.value.map(ele => ele.id);
});
}
}
}
}
);
/**
* 点击Tab切换回调
...
...
package.json
View file @
dab01c5
...
...
@@ -26,9 +26,10 @@
"@vue/composition-api"
:
"^1.7.2"
,
"axios"
:
"^1.6.0"
,
"echarts"
:
"^5.1.2"
,
"element-plus"
:
"^2.4.
1
"
,
"element-plus"
:
"^2.4.
2
"
,
"jquery"
:
"^3.7.1"
,
"lodash"
:
"^4.17.21"
,
"qs"
:
"^6.11.2"
,
"sass"
:
"^1.69.4"
,
"sass-loader"
:
"10.1.1"
,
"uuid"
:
"^9.0.1"
,
...
...
yarn-error.log
0 → 100644
View file @
dab01c5
This diff could not be displayed because it is too large.
yarn.lock
View file @
dab01c5
...
...
@@ -3787,10 +3787,10 @@ electron-to-chromium@^1.3.793:
resolved "https://registry.nlark.com/electron-to-chromium/download/electron-to-chromium-1.3.806.tgz?cache=0&sync_timestamp=1628906712766&other_urls=https%3A%2F%2Fregistry.nlark.com%2Felectron-to-chromium%2Fdownload%2Felectron-to-chromium-1.3.806.tgz"
integrity sha1-IVAhAPEa6tbFAdHNfyUE8WyTZkI=
element-plus@^2.4.
1
:
version "2.4.
1
"
resolved "https://mirrors.cloud.tencent.com/npm/element-plus/-/element-plus-2.4.
1.tgz#8a5faa69e856d82494b94d77fb485d9e727c8bc1
"
integrity sha512-
t7nl+vQlkBKVk1Ag6AufSDyFV8YIXxTFsaya4Nz/0tiRlcz65WPN4WMFeNURuFJleu1HLNtP4YyQKMuS7El8uA
==
element-plus@^2.4.
2
:
version "2.4.
2
"
resolved "https://mirrors.cloud.tencent.com/npm/element-plus/-/element-plus-2.4.
2.tgz#2a24632e0904ccd7bbbd64c269704f6b9969833c
"
integrity sha512-
E/HwXX7JF1LPvQSjs0fZ8WblIoc0quoXsRXQZiL7QDq7xJdNGSUaXtdk7xiEv7axPmLfEFtxE5du9fFspDrmJw
==
dependencies:
"@ctrl/tinycolor" "^3.4.1"
"@element-plus/icons-vue" "^2.0.6"
...
...
@@ -7185,6 +7185,13 @@ qs@6.7.0:
resolved "https://registry.npm.taobao.org/qs/download/qs-6.7.0.tgz?cache=0&sync_timestamp=1616385248556&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fqs%2Fdownload%2Fqs-6.7.0.tgz"
integrity sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=
qs@^6.11.2:
version "6.11.2"
resolved "https://mirrors.cloud.tencent.com/npm/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9"
integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==
dependencies:
side-channel "^1.0.4"
qs@~6.5.2:
version "6.5.2"
resolved "https://registry.npm.taobao.org/qs/download/qs-6.5.2.tgz?cache=0&sync_timestamp=1616385248556&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fqs%2Fdownload%2Fqs-6.5.2.tgz"
...
...
Please
register
or
login
to post a comment