hookehuyr

API联调

......@@ -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,91 +774,149 @@ 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');
// TODO: 需要处理更多属性数据,节点类型是抄送时不显示节点操作
state.more_attr = [ // 更多属性
{
id: 'no-1',
label: '审批意见',
desc: '开启审批意见后,节点负责人处理流程时须按要求填写审批意见',
data: [
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 = [ // 字段权限
{
id: 'text-1',
label: '文本意见',
name: '字段1',
visible: {
checked: false,
disabled: true,
},
editable: {
checked: false,
disabled: true,
},
show: true,
desc: '用户在处理流程时,可通过输入框或快捷选项录入文本意见。',
btnText: '',
},
{
id: 'signature-1',
label: '手写签名',
show: false,
desc: '用户在处理流程时,需要签名确认。',
btnText: ''
},
]
},
{
id: 'no-2',
label: '节点操作',
desc: '定义流程负责人在处理流程时可以进行的操作',
data: [
{
id: 'node-1',
label: '提交',
name: '字段2',
visible: {
checked: true,
disabled: false,
},
editable: {
checked: false,
disabled: false,
},
show: true,
desc: '用户在处理流程时点击此按钮,将保存用户在此节点中对数据的更改,流程数据流转至后续节点。',
btnText: '提交'
},
{
id: 'node-2',
label: '暂存',
show: false,
desc: '暂存后将保存在此节点中对数据的更改,流程不发生流转。',
btnText: '暂存'
name: '字段3',
visible: {
checked: true,
disabled: false,
},
editable: {
checked: false,
disabled: false,
},
show: true,
},
]
state.more_attr = [ // 更多属性
{
id: 'node-3',
label: '撤回',
show: false,
desc: '开启后,用户在处理流程时点击此按钮,将保存用户在此节点中对数据的更改,同时流程退回到指定的节点中。',
btnText: '撤回'
id: 'no-1',
label: '审批意见',
desc: '开启审批意见后,节点负责人处理流程时须按要求填写审批意见',
data: [
{
id: 'text-1',
label: '文本意见',
show: true,
desc: '用户在处理流程时,可通过输入框或快捷选项录入文本意见。',
btnText: '',
},
{
id: 'signature-1',
label: '手写签名',
show: false,
desc: '用户在处理流程时,需要签名确认。',
btnText: ''
},
]
},
{
id: 'node-4',
label: '回退',
show: false,
desc: '开启后,用户在处理流程时点击此按钮,将保存用户在此节点中对数据的更改,同时流程退回到指定的节点中。',
btnText: '回退'
},
],
id: 'no-2',
label: '节点操作',
desc: '定义流程负责人在处理流程时可以进行的操作',
data: [
{
id: 'node-1',
label: '提交',
show: true,
desc: '用户在处理流程时点击此按钮,将保存用户在此节点中对数据的更改,流程数据流转至后续节点。',
btnText: '提交'
},
{
id: 'node-2',
label: '暂存',
show: false,
desc: '暂存后将保存在此节点中对数据的更改,流程不发生流转。',
btnText: '暂存'
},
{
id: 'node-3',
label: '撤回',
show: false,
desc: '开启后,用户在处理流程时点击此按钮,将保存用户在此节点中对数据的更改,同时流程退回到指定的节点中。',
btnText: '撤回'
},
{
id: 'node-4',
label: '回退',
show: false,
desc: '开启后,用户在处理流程时点击此按钮,将保存用户在此节点中对数据的更改,同时流程退回到指定的节点中。',
btnText: '回退'
},
],
}
];
// 检查字段权限选中情况
checkAuthAll('visible');
checkAuthAll('editable');
if (state.detailModel.control === 'cc') {
state.more_attr = state.more_attr.filter((ele: any) => {
return ele.label !== '节点操作'
});
}
// 打开属性表单
state.attr_radio = '基础属性'; // 还原tab默认值
editor.openModel();
loading.close();
} else {
loading.close();
ElMessage({
type: 'error',
message: res.data.msg,
});
}
];
if (state.detailModel.control === 'cc') {
state.more_attr = state.more_attr.filter((ele: any) => {
return ele.label !== '节点操作'
});
}
// 打开属性表单
state.attr_radio = '基础属性'; // 还原tab默认值
editor.openModel();
})
.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
// }
// })
state.detailModel.text = 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);
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
// 更新流程图信息
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);
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,14 +1268,31 @@ export default {
)
.then(() => {
// 检查路径有效性
// let { edges } = editor.editorState.graph.save()
const paths = [];
findPathsToEndNode(edges, 'start-node', [], paths);
console.log(paths); // 输出满足条件的路径结果数组
if (paths.length) {
ElMessage({
type: 'success',
message: '保存流程图成功',
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('缺少一条从开始节点到结束节点的完整流程!');
......
<!--
* @Date: 2023-11-01 10:18:53
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2023-11-22 17:29:49
* @LastEditTime: 2023-11-23 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切换回调
......
......@@ -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",
......
This diff could not be displayed because it is too large.
......@@ -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"
......