hookehuyr

控制结束节点不能有连接线,控制一个节点不能同时连接同样的节点

......@@ -222,36 +222,39 @@
</template>
<script lang="ts">
import { reactive, onMounted, watch, nextTick } from "vue";
import { AppData } from "./data.js";
import { staticPath } from "./utils";
import { ElNotification } from "element-plus";
import axios from "./axios";
import $ from "jquery";
import { Calendar, Search } from "@element-plus/icons-vue";
import SelectUserView from "./selectUserView.vue";
import { Function } from 'lodash';
import { extend } from '@vue/shared';
const G6 = (window as any).G6.default as any;
import { reactive, onMounted, watch, nextTick } from 'vue'
import { AppData } from './data.js'
import { staticPath } from './utils'
import { ElNotification } from 'element-plus'
import axios from './axios'
import $ from 'jquery'
import { Calendar, Search } from '@element-plus/icons-vue'
import SelectUserView from './selectUserView.vue'
import { Function } from 'lodash'
import { extend } from '@vue/shared'
const G6 = (window as any).G6.default as any
function delay(time: number) {
return new Promise(resolve => setTimeout(resolve, time));
return new Promise((resolve) => setTimeout(resolve, time))
}
interface myObj {
source: string;
id: string;
label: string;
control: string;
source: string
id: string
label: string
control: string
target: string
}
interface myEvent {
item: {
get(T: string): {
source: any,
target: any,
style: any,
get(
T: string,
): {
source: any
target: any
style: any
labelCfg: any
label: any
}
......@@ -262,7 +265,7 @@ export default {
components: {
Calendar,
Search,
SelectUserView
SelectUserView,
},
setup(props, context) {
const state = reactive({
......@@ -270,32 +273,32 @@ export default {
detailModel: null,
editorLoading: false, // 开始编辑器的loading状态
selectOptions: [
{ label: "待确认", value: "0" },
{ label: "填写表单", value: "1" },
{ label: "部门负责人审批", value: "2" },
{ label: "总经理审批", value: "3" }
{ label: '待确认', value: '0' },
{ label: '填写表单', value: '1' },
{ label: '部门负责人审批', value: '2' },
{ label: '总经理审批', value: '3' },
],
menuData: [
{
label: "流程节点",
label: '流程节点',
menus: [
{ label: "开始", shape: "ellipse", id: "start-node" },
{ label: "结束", shape: "ellipse", id: "end-node" },
{ label: "审批节点", busType: "123" },
{ label: "判断节点", shape: "diamond" }
]
{ label: '开始', shape: 'ellipse', id: 'start-node' },
{ label: '结束', shape: 'ellipse', id: 'end-node' },
{ label: '审批节点', busType: '123' },
{ label: '判断节点', shape: 'diamond' },
],
},
{
label: "其他形状节点",
label: '其他形状节点',
menus: [
{ label: "矩形节点", shape: "rect" },
{ label: "圆形节点", shape: "circle" },
{ label: "椭圆节点", shape: "ellipse" },
{ label: "菱形节点", shape: "diamond" },
{ label: "三角形节点", shape: "triangle" },
{ label: "星形节点", shape: "star" }
]
}
{ label: '矩形节点', shape: 'rect' },
{ label: '圆形节点', shape: 'circle' },
{ label: '椭圆节点', shape: 'ellipse' },
{ label: '菱形节点', shape: 'diamond' },
{ label: '三角形节点', shape: 'triangle' },
{ label: '星形节点', shape: 'star' },
],
},
],
// activityConfig: {
// advertisement: {
......@@ -319,130 +322,142 @@ export default {
// },
controlConfig: {
start: {
id: "start-node",
text: "开始",
desc: "开始",
color: "#9283ed",
img: "https://cdn.ipadbiz.cn/oa/advertisement-node.svg"
id: 'start-node',
text: '开始',
desc: '开始',
color: '#9283ed',
img: 'https://cdn.ipadbiz.cn/oa/advertisement-node.svg',
},
flow: {
text: "流程节点",
desc: "流程节点",
color: "#ed8383",
img: "https://cdn.ipadbiz.cn/oa/coupon-node.svg"
text: '流程节点',
desc: '流程节点',
color: '#ed8383',
img: 'https://cdn.ipadbiz.cn/oa/coupon-node.svg',
},
end: {
id: "end-node",
text: "结束",
desc: "结束",
color: "#92dba8",
img: "https://cdn.ipadbiz.cn/oa/crowd-node.svg"
}
id: 'end-node',
text: '结束',
desc: '结束',
color: '#92dba8',
img: 'https://cdn.ipadbiz.cn/oa/crowd-node.svg',
},
},
dialogUserFormVisible: false,
activeName: "node",
attr_radio: "基础属性",
activeName: 'node',
attr_radio: '基础属性',
auth_all_checked: false,
auth_all_edit: false,
field_auths: [
{
name: "字段1",
name: '字段1',
visible: {
checked: true,
disabled: false
disabled: false,
},
editable: {
checked: false,
disabled: true
}
disabled: true,
},
},
{
name: "字段2",
name: '字段2',
visible: {
checked: true,
disabled: false
disabled: false,
},
editable: {
checked: false,
disabled: false
}
disabled: false,
},
},
{
name: "字段3",
name: '字段3',
visible: {
checked: true,
disabled: false
disabled: false,
},
editable: {
checked: false,
disabled: false
}
}
disabled: false,
},
},
],
});
})
onMounted(() => {
document.title = '可视化流程设计器';
// // 显示提示框的标志位
// var showConfirmation = true;
// // 监听 beforeunload 事件
// window.addEventListener('beforeunload', function (event) {
// if (showConfirmation) {
// // 取消事件的默认行为(弹出确认对话框)
// event.preventDefault();
// // Chrome 和 Firefox 需要返回一个值以显示确认对话框
// event.returnValue = '';
// // 显示自定义的提示信息
// var confirmationMessage = '确定要离开此页面吗?';
// (event || window.event).returnValue = confirmationMessage; // 兼容旧版浏览器
// return confirmationMessage;
// }
// });
// // 监听 unload 事件
// window.addEventListener('unload', function () {
// // 设置标志位为 false,避免在刷新页面时再次显示提示框
// showConfirmation = false;
// });
});
document.title = '可视化流程设计器'
// // 显示提示框的标志位
// var showConfirmation = true;
// // 监听 beforeunload 事件
// window.addEventListener('beforeunload', function (event) {
// if (showConfirmation) {
// // 取消事件的默认行为(弹出确认对话框)
// event.preventDefault();
// // Chrome 和 Firefox 需要返回一个值以显示确认对话框
// event.returnValue = '';
// // 显示自定义的提示信息
// var confirmationMessage = '确定要离开此页面吗?';
// (event || window.event).returnValue = confirmationMessage; // 兼容旧版浏览器
// return confirmationMessage;
// }
// });
// // 监听 unload 事件
// window.addEventListener('unload', function () {
// // 设置标志位为 false,避免在刷新页面时再次显示提示框
// showConfirmation = false;
// });
})
function handleActiveChange(name) {
console.warn(name);
console.warn(name)
}
const onAuthAllChange = (val: any) => { // 全选可见按钮回调
const onAuthAllChange = (val: any) => {
// 全选可见按钮回调
if (val) {
// 全部选中
state.field_auths.forEach(ele => {
ele.visible.checked = true;
});
state.field_auths.forEach((ele) => {
ele.visible.checked = true
})
} else {
// 全部取消选中
state.field_auths.forEach(ele => {
ele.visible.checked = false;
});
state.field_auths.forEach((ele) => {
ele.visible.checked = false
})
}
};
const onAuthAllEditChange = (val: any) => { // 全选可编辑按钮回调
console.warn(val);
};
}
const onAuthAllEditChange = (val: any) => {
// 全选可编辑按钮回调
console.warn(val)
}
/****** 用户选择控件弹框 ******/
const openUserForm = () => { // 打开设置用户弹框
state.dialogUserFormVisible = true;
};
const openUserForm = () => {
// 打开设置用户弹框
state.dialogUserFormVisible = true
}
const onCloseUserView = (status: boolean) => {
state.dialogUserFormVisible = status;
state.dialogUserFormVisible = status
}
const onConfirmUserView = (data: any) => {
console.log(data);
console.log(data)
}
/******************* END *******************/
/********** 流程图功能函数 **********/
let editor: { openModel: () => void; closeModel: () => void; updateModel: (arg0: any) => void; editorState: { graph: { save: () => { nodes: any; edges: any; }; }; }; };
let editor: {
openModel: () => void
closeModel: () => void
updateModel: (arg0: any) => void
editorState: {
graph: {
removeItem: any
save: () => { nodes: any; edges: any }
}
}
}
/**
* 双击节点回调
......@@ -450,23 +465,23 @@ export default {
* @param {Object} e - The event object
*/
function onDblClickNode(e: myEvent) {
const model = G6.Util.clone(e.item.get("model"));
model.style = model.style || {};
model.labelCfg = model.labelCfg || { style: {} };
const model = G6.Util.clone(e.item.get('model'))
model.style = model.style || {}
model.labelCfg = model.labelCfg || { style: {} }
model.data = model.data ? model.data : {};
state.detailModel = model;
editor.openModel();
model.data = model.data ? model.data : {}
state.detailModel = model
editor.openModel()
}
function onClickNode(e: myEvent) {
const model = G6.Util.clone(e.item.get("model"));
model.style = model.style || {};
model.labelCfg = model.labelCfg || { style: {} };
const model = G6.Util.clone(e.item.get('model'))
model.style = model.style || {}
model.labelCfg = model.labelCfg || { style: {} }
model.data = model.data ? model.data : {};
state.detailModel = model;
editor.openModel();
model.data = model.data ? model.data : {}
state.detailModel = model
editor.openModel()
}
/**
......@@ -475,7 +490,7 @@ export default {
* @param {Event} e - The event object representing the double click event.
*/
function onDblClickEdge(e: myEvent) {
const { source, target, style, labelCfg, label } = e.item.get("model");
const { source, target, style, labelCfg, label } = e.item.get('model')
const model = {
label,
source,
......@@ -483,13 +498,13 @@ export default {
style: style || {},
labelCfg: labelCfg || { style: {} },
type: null,
id: null
};
model.type = e.item.get("type");
model.id = e.item.get("id");
id: null,
}
model.type = e.item.get('type')
model.id = e.item.get('id')
state.detailModel = model;
editor.openModel();
state.detailModel = model
editor.openModel()
}
/**
......@@ -497,7 +512,7 @@ export default {
*
*/
function cancel() {
editor.closeModel();
editor.closeModel()
}
/**
......@@ -505,8 +520,8 @@ export default {
*
*/
function save() {
editor.updateModel(state.detailModel);
editor.closeModel();
editor.updateModel(state.detailModel)
editor.closeModel()
}
/**
......@@ -516,14 +531,17 @@ export default {
* @param {string} type - The type of the model.
* @return {Promise} A promise that resolves when the event is handled.
*/
async function handleBeforeDelete(model: myObj, type: string): Promise<any> {
if (type === "node") {
if (model.label === "开始") {
state.editorLoading = true;
await delay(1000);
state.editorLoading = false;
ElNotification.error("不可以删除【开始】节点");
return Promise.reject("reject");
async function handleBeforeDelete(
model: myObj,
type: string,
): Promise<any> {
if (type === 'node') {
if (model.label === '开始') {
state.editorLoading = true
await delay(1000)
state.editorLoading = false
ElNotification.error('不可以删除【开始】节点')
return Promise.reject('reject')
}
}
}
......@@ -535,10 +553,10 @@ export default {
* @param {string} type - The type of the model being deleted.
*/
function handleAfterDelete(model: myObj, type: string) {
if (type === "edge") {
console.log("delete edge");
if (type === 'edge') {
console.log('delete edge')
} else {
console.log("after delete", model.label, { ...model });
console.log('after delete', model.label, { ...model })
}
}
......@@ -550,22 +568,33 @@ export default {
* @return {Promise} A promise that resolves to a result or rejects with an error.
*/
function handleBeforeAdd(model: myObj, type: string): Promise<any> {
if (type === "edge") {
if (model.source === "end-node") {
ElNotification.error("结束节点不能输出连线其他节点");
return Promise.reject("reject");
const source = model.source;
const target = model.target;
let { nodes, edges } = editor.editorState.graph.save();
if (type === 'edge') {
if (model.source === 'end-node') {
ElNotification.error('结束节点不能输出连线其他节点')
return Promise.reject('reject')
}
for (let index = 0; index < edges.length; index++) {
const element = edges[index]
if (element.source === source && element.target === target) {
ElNotification.error('不可以重复添加连线')
return Promise.reject('reject')
}
}
}
if (type === "node") {
if (model.control === "start" || model.control === "end") {
const data = editor.editorState.graph.save();
if (type === 'node') {
if (model.control === 'start' || model.control === 'end') {
const data = editor.editorState.graph.save()
for (let i = 0; i < data.nodes.length; i++) {
const node = data.nodes[i];
const node = data.nodes[i]
if (node.control === model.control) {
ElNotification.error(
`只能有一个${model.control === "start" ? "开始" : "结束"}节点`
);
return Promise.reject("reject");
`只能有一个${model.control === 'start' ? '开始' : '结束'}节点`,
)
return Promise.reject('reject')
}
}
}
......@@ -579,15 +608,18 @@ export default {
* @param {type} type - The type of the event.
*/
function handleAfterAdd(model: myObj, type: string) {
console.log('handleAfterAdd', model);
// console.log('handleAfterAdd', model);
// TODO: 因为resize会重新绘制,所以可能需要保存操作
if (type === 'node') {
console.log(`新增节点`);
console.log(`新增节点`)
}
if (type === "edge") {
console.log(`新增连接线`);
if (type === 'edge') {
console.log(`新增连接线`)
}
state.data.nodes = editor.editorState.graph.save().nodes
state.data.edges = editor.editorState.graph.save().edges
}
/**
......@@ -597,8 +629,8 @@ export default {
*/
function onDragEndNode(e: myEvent) {
// TODO:可能需要接口保存相应位置,避免拖动窗口时数据丢失
const model = e.item.get("model");
console.log("onDragEndNode", model);
const model = e.item.get('model')
console.log('onDragEndNode', model)
}
/**
......@@ -607,18 +639,18 @@ export default {
* @return {void} No return value.
*/
function logData(): void {
let { nodes, edges } = editor.editorState.graph.save();
let { nodes, edges } = editor.editorState.graph.save()
// console.log("nodes", nodes);
// console.log("edges", edges);
// 使用时需要把自定义节点的类型带过去 activity/control
nodes.forEach((node: { [x: string]: string; shape: string; }) => {
if (node.shape === "activity") {
node['shape'] = 'activity_' + node['activity'];
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'];
if (node.shape === 'control') {
node['shape'] = 'control_' + node['control']
}
});
})
nodes = nodes.map(
({ data, id, label, shape, x, y, text, desc, img }) => ({
......@@ -630,18 +662,18 @@ export default {
y,
text,
desc,
img
})
);
img,
}),
)
edges = edges.map(({ source, sourceAnchor, target, targetAnchor }) => ({
source,
sourceAnchor,
target,
targetAnchor
}));
targetAnchor,
}))
// console.log(JSON.stringify({ nodes, edges }, null, 2));
console.log(nodes);
console.log(edges);
console.log(nodes)
console.log(edges)
}
/**
......@@ -652,8 +684,8 @@ export default {
*/
function toolbarButtonHandler(buttons: any[]): Array<any> {
// TAG:测试隐藏缩略图
let map = buttons.filter(item => item.key !== "miniMapSwitcher");
return map;
let map = buttons.filter((item) => item.key !== 'miniMapSwitcher')
return map
}
return {
......@@ -686,10 +718,10 @@ export default {
toolbarButtonHandler,
onRef: (e: any) => (editor = e),
staticPath
};
}
};
staticPath,
}
},
}
</script>
<style lang="scss">
......
/*
* @Date: 2023-10-27 09:29:48
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2023-11-15 15:56:03
* @LastEditTime: 2023-11-15 16:16:04
* @FilePath: /vue-flow-editor/doc/data.js
* @Description: 初始化结构,数据都是固定的
*/
......
......@@ -130,7 +130,7 @@ export function dragEdge(G6, option: OptionType) {
async _addEdge() {
if (this.origin.targetNode) {
const addModel = {
clazz: 'flow',
class: 'flow',
source: this.origin.sourceNode.get('id'),
target: this.origin.targetNode.get('id'),
sourceAnchor: this.origin.sourceAnchor,
......@@ -159,4 +159,4 @@ export function dragEdge(G6, option: OptionType) {
}
}
});
}
\ No newline at end of file
}
......