保存、预览流程

This commit is contained in:
cxc
2022-12-20 14:34:38 +08:00
parent 7726fcbf54
commit a0c3e7bd00
21 changed files with 1301 additions and 856 deletions

View File

@ -22,12 +22,11 @@
"axios": "0.27.2", "axios": "0.27.2",
"bpmn-js-token-simulation": "0.10.0", "bpmn-js-token-simulation": "0.10.0",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
"diagram-js": "^11.4.1",
"echarts": "5.4.0", "echarts": "5.4.0",
"element-plus": "2.2.21", "element-plus": "2.2.21",
"file-saver": "2.0.5", "file-saver": "2.0.5",
"fuse.js": "6.6.2", "fuse.js": "6.6.2",
"highlight.js": "^11.7.0", "highlight.js": "10.5.0",
"js-cookie": "3.0.1", "js-cookie": "3.0.1",
"jsencrypt": "3.3.1", "jsencrypt": "3.3.1",
"nprogress": "0.2.0", "nprogress": "0.2.0",
@ -47,8 +46,8 @@
"@vue/babel-helper-vue-transform-on": "^1.0.2", "@vue/babel-helper-vue-transform-on": "^1.0.2",
"@vue/compiler-sfc": "3.2.45", "@vue/compiler-sfc": "3.2.45",
"bpmn-js": "7.4.0", "bpmn-js": "7.4.0",
"bpmn-js-properties-panel": "^1.13.1", "bpmn-js-properties-panel": "0.37.2",
"camunda-bpmn-moddle": "^7.0.1", "camunda-bpmn-moddle": "4.4.1",
"sass": "1.56.1", "sass": "1.56.1",
"unplugin-auto-import": "0.11.4", "unplugin-auto-import": "0.11.4",
"vite": "3.2.3", "vite": "3.2.3",

View File

@ -5,7 +5,7 @@
v-bind="controlForm" v-bind="controlForm"
keyboard keyboard
ref="processDesigner" ref="processDesigner"
:events="[ :eventlist="[
'element.click', 'element.click',
'connection.added', 'connection.added',
'connection.removed', 'connection.removed',
@ -28,10 +28,13 @@
import BmpnProcessPenal from "@/plugins/package/penal"; import BmpnProcessPenal from "@/plugins/package/penal";
import BpmnProcessDesigner from "@/plugins/package/designer"; import BpmnProcessDesigner from "@/plugins/package/designer";
import "@/plugins/package/theme/index.scss"; import "@/plugins/package/theme/index.scss";
// import vuePlugin from "@/plugins/package/highlight";
// import "highlight.js/styles/atom-one-dark-reasonable.css";
// import "highlight.js/styles/atom-one-dark-reasonable.css"; // import "highlight.js/styles/atom-one-dark-reasonable.css";
import CustomContentPadProvider from "@/plugins/package/designer/plugins/content-pad"; import CustomContentPadProvider from "@/plugins/package/designer/plugins/content-pad";
import CustomPaletteProvider from "@/plugins/package/designer/plugins/palette"; import CustomPaletteProvider from "@/plugins/package/designer/plugins/palette";
import { ref } from "vue"; import { ref } from "vue";
const props = defineProps({ const props = defineProps({
bpmnXml: { bpmnXml: {
type: String, type: String,
@ -43,12 +46,12 @@ const props = defineProps({
}, },
}); });
const { bpmnXml, designerForm } = toRefs(props); const { bpmnXml, designerForm } = toRefs(props);
let element;
const modeler = ref(null); const modeler = ref(null);
const height = ref(document.documentElement.clientHeight - 94.5 + "px;"); const height = ref(document.documentElement.clientHeight - 94.5 + "px;");
const xmlString = ref(bpmnXml.value); const xmlString = ref(bpmnXml.value);
const emit = defineEmits(["save"]); const emit = defineEmits(["save"]);
const data = reactive({ const data = reactive({
element: null,
controlForm: { controlForm: {
processId: designerForm.value.processKey || "", processId: designerForm.value.processKey || "",
processName: designerForm.value.processName || "", processName: designerForm.value.processName || "",
@ -60,16 +63,14 @@ const data = reactive({
additionalModel: [CustomContentPadProvider, CustomPaletteProvider], additionalModel: [CustomContentPadProvider, CustomPaletteProvider],
}, },
}); });
const { controlForm, element } = toRefs(data); const { controlForm } = toRefs(data);
function elementClick(elementArgv) { function elementClick(elementArgv) {
element.value = elementArgv; element = elementArgv;
console.log(elementArgv);
} }
function initModeler(modelerArgv) { function initModeler(modelerArgv) {
setTimeout(() => { setTimeout(() => {
modeler.value = modelerArgv; modeler.value = modelerArgv;
console.log(modeler.value.get("eventBus"), "71");
}, 10); }, 10);
} }
function handlerEvent(eventName, element) {} function handlerEvent(eventName, element) {}

View File

@ -1,49 +1,183 @@
<template> <template>
<div class="process-viewer"> <div class="process-viewer">
<div class="process-canvas" style="height: 100%;" ref="processCanvas" v-show="!isLoading" /> <div
class="process-canvas"
style="height: 100%"
ref="processCanvas"
v-show="!isLoading"
/>
<!-- 自定义箭头样式用于成功状态下流程连线箭头 --> <!-- 自定义箭头样式用于成功状态下流程连线箭头 -->
<defs ref="customSuccessDefs"> <defs ref="customSuccessDefs">
<marker id="sequenceflow-end-white-success" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto"> <marker
<path class="success-arrow" d="M 1 5 L 11 10 L 1 15 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path> id="sequenceflow-end-white-success"
viewBox="0 0 20 20"
refX="11"
refY="10"
markerWidth="10"
markerHeight="10"
orient="auto"
>
<path
class="success-arrow"
d="M 1 5 L 11 10 L 1 15 Z"
style="
stroke-width: 1px;
stroke-linecap: round;
stroke-dasharray: 10000, 1;
"
></path>
</marker> </marker>
<marker id="conditional-flow-marker-white-success" viewBox="0 0 20 20" refX="-1" refY="10" markerWidth="10" markerHeight="10" orient="auto"> <marker
<path class="success-conditional" d="M 0 10 L 8 6 L 16 10 L 8 14 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path> id="conditional-flow-marker-white-success"
viewBox="0 0 20 20"
refX="-1"
refY="10"
markerWidth="10"
markerHeight="10"
orient="auto"
>
<path
class="success-conditional"
d="M 0 10 L 8 6 L 16 10 L 8 14 Z"
style="
stroke-width: 1px;
stroke-linecap: round;
stroke-dasharray: 10000, 1;
"
></path>
</marker> </marker>
</defs> </defs>
<!-- 自定义箭头样式用于失败状态下流程连线箭头 --> <!-- 自定义箭头样式用于失败状态下流程连线箭头 -->
<defs ref="customFailDefs"> <defs ref="customFailDefs">
<marker id="sequenceflow-end-white-fail" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto"> <marker
<path class="fail-arrow" d="M 1 5 L 11 10 L 1 15 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path> id="sequenceflow-end-white-fail"
viewBox="0 0 20 20"
refX="11"
refY="10"
markerWidth="10"
markerHeight="10"
orient="auto"
>
<path
class="fail-arrow"
d="M 1 5 L 11 10 L 1 15 Z"
style="
stroke-width: 1px;
stroke-linecap: round;
stroke-dasharray: 10000, 1;
"
></path>
</marker> </marker>
<marker id="conditional-flow-marker-white-fail" viewBox="0 0 20 20" refX="-1" refY="10" markerWidth="10" markerHeight="10" orient="auto"> <marker
<path class="fail-conditional" d="M 0 10 L 8 6 L 16 10 L 8 14 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path> id="conditional-flow-marker-white-fail"
viewBox="0 0 20 20"
refX="-1"
refY="10"
markerWidth="10"
markerHeight="10"
orient="auto"
>
<path
class="fail-conditional"
d="M 0 10 L 8 6 L 16 10 L 8 14 Z"
style="
stroke-width: 1px;
stroke-linecap: round;
stroke-dasharray: 10000, 1;
"
></path>
</marker> </marker>
</defs> </defs>
<!-- 已完成节点悬浮弹窗 --> <!-- 已完成节点悬浮弹窗 -->
<el-dialog class="comment-dialog" :title="dlgTitle || '审批记录'" :visible.sync="dialogVisible"> <el-dialog
class="comment-dialog"
:title="dlgTitle || '审批记录'"
v-model="dialogVisible"
>
<el-row> <el-row>
<el-table :data="taskCommentList" size="mini" border header-cell-class-name="table-header-gray"> <el-table
<el-table-column label="序号" header-align="center" align="center" type="index" width="55px" /> :data="taskCommentList"
<el-table-column label="候选办理" prop="candidate" width="150px" align="center"/> size="mini"
<el-table-column label="实际办理" prop="assigneeName" width="100px" align="center"/> border
<el-table-column label="处理时间" prop="createTime" width="140px" align="center"/> header-cell-class-name="table-header-gray"
<el-table-column label="办结时间" prop="finishTime" width="140px" align="center" /> >
<el-table-column label="耗时" prop="duration" width="100px" align="center"/> <el-table-column
label="序号"
header-align="center"
align="center"
type="index"
width="55px"
/>
<el-table-column
label="候选办理"
prop="candidate"
width="150px"
align="center"
/>
<el-table-column
label="实际办理"
prop="assigneeName"
width="100px"
align="center"
/>
<el-table-column
label="处理时间"
prop="createTime"
width="140px"
align="center"
/>
<el-table-column
label="办结时间"
prop="finishTime"
width="140px"
align="center"
/>
<el-table-column
label="耗时"
prop="duration"
width="100px"
align="center"
/>
<el-table-column label="审批意见" align="center"> <el-table-column label="审批意见" align="center">
<template slot-scope="scope"> <template #default="{ row }">
{{scope.row.commentList&&scope.row.commentList[0]?scope.row.commentList[0].fullMessage:''}} {{
row.commentList && row.commentList[0]
? row.commentList[0].fullMessage
: ""
}}
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-row> </el-row>
</el-dialog> </el-dialog>
<div style="position: absolute; top: 0px; left: 0px; width: 100%;"> <div style="position: absolute; top: 0px; left: 0px; width: 100%">
<el-row type="flex" justify="end"> <el-row type="flex" justify="end">
<el-button-group key="scale-control" size="medium"> <el-button-group key="scale-control" size="default">
<el-button size="medium" type="default" :plain="true" :disabled="defaultZoom <= 0.3" icon="el-icon-zoom-out" @click="processZoomOut()" /> <el-button
<el-button size="medium" type="default" style="width: 90px;">{{ Math.floor(this.defaultZoom * 10 * 10) + "%" }}</el-button> size="default"
<el-button size="medium" type="default" :plain="true" :disabled="defaultZoom >= 3.9" icon="el-icon-zoom-in" @click="processZoomIn()" /> type="default"
<el-button size="medium" type="default" icon="el-icon-c-scale-to-original" @click="processReZoom()" /> :plain="true"
:disabled="defaultZoom <= 0.3"
icon="zoom-out"
@click="processZoomOut()"
/>
<el-button size="default" type="default" style="width: 90px">{{
Math.floor(defaultZoom * 10 * 10) + "%"
}}</el-button>
<el-button
size="default"
type="default"
:plain="true"
:disabled="defaultZoom >= 3.9"
icon="zoom-in"
@click="processZoomIn()"
/>
<el-button
size="default"
type="default"
icon="scale-to-original"
@click="processReZoom()"
/>
<slot /> <slot />
</el-button-group> </el-button-group>
</el-row> </el-row>
@ -51,209 +185,215 @@
</div> </div>
</template> </template>
<script> <script setup name="">
import '@/plugins/package/theme/index.scss'; import "@/plugins/package/theme/index.scss";
import BpmnViewer from 'bpmn-js/lib/Viewer'; import BpmnViewer from "bpmn-js/lib/Viewer";
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas'; import MoveCanvasModule from "diagram-js/lib/navigation/movecanvas";
import { nextTick, onBeforeUnmount, toRefs } from "vue";
export default { const props = defineProps({
props: { xml: {
xml: { type: String,
type: String
},
finishedInfo: {
type: Object
},
// 所有节点审批记录
allCommentList: {
type: Array
}
}, },
data () { finishedInfo: {
return { type: Object,
dialogVisible: false,
dlgTitle: undefined,
defaultZoom: 1,
// 是否正在加载流程图
isLoading: false,
bpmnViewer: undefined,
// 已完成流程元素
processNodeInfo: undefined,
// 当前任务id
selectTaskId: undefined,
// 任务节点审批记录
taskCommentList: [],
// 已完成任务悬浮延迟Timer
hoverTimer: null
}
}, },
watch: { // 所有节点审批记录
xml: { allCommentList: {
handler(newXml) { type: Array,
this.importXML(newXml);
},
immediate: true
},
finishedInfo: {
handler(newInfo) {
this.setProcessStatus(newInfo);
},
immediate: true
}
}, },
created() { });
this.$nextTick(() => {
this.importXML(this.xml) const { xml, finishedInfo, allCommentList } = toRefs(props);
this.setProcessStatus(this.finishedInfo); const dialogVisible = ref(false);
}) const dlgTitle = ref(undefined);
const defaultZoom = ref(1);
// 是否正在加载流程图
const isLoading = ref(false);
const bpmnViewer = ref(undefined);
// 已完成流程元素
const processNodeInfo = ref(undefined);
// 当前任务id
const selectTaskId = ref(undefined);
// 任务节点审批记录
const taskCommentList = ref([]);
// 已完成任务悬浮延迟Timer
const hoverTimer = ref(null);
const processCanvas = ref();
const customFailDefs = ref();
const customSuccessDefs = ref();
watch(
xml,
(newXml) => {
importXML(newXml);
}, },
methods: { { immediate: true }
processReZoom() { );
this.defaultZoom = 1;
this.bpmnViewer.get('canvas').zoom('fit-viewport', 'auto');
},
processZoomIn(zoomStep = 0.1) {
let newZoom = Math.floor(this.defaultZoom * 100 + zoomStep * 100) / 100;
if (newZoom > 4) {
throw new Error('[Process Designer Warn ]: The zoom ratio cannot be greater than 4');
}
this.defaultZoom = newZoom;
this.bpmnViewer.get('canvas').zoom(this.defaultZoom);
},
processZoomOut(zoomStep = 0.1) {
let newZoom = Math.floor(this.defaultZoom * 100 - zoomStep * 100) / 100;
if (newZoom < 0.2) {
throw new Error('[Process Designer Warn ]: The zoom ratio cannot be less than 0.2');
}
this.defaultZoom = newZoom;
this.bpmnViewer.get('canvas').zoom(this.defaultZoom);
},
getOperationTagType(type) {
return 'success';
// switch (type) {
// case this.SysFlowTaskOperationType.AGREE:
// case this.SysFlowTaskOperationType.MULTI_AGREE:
// return 'success';
// case this.SysFlowTaskOperationType.REFUSE:
// case this.SysFlowTaskOperationType.PARALLEL_REFUSE:
// case this.SysFlowTaskOperationType.MULTI_REFUSE:
// return 'warning';
// case this.SysFlowTaskOperationType.STOP:
// return 'danger'
// default:
// return 'primary';
// }
},
// 流程图预览清空
clearViewer() {
if (this.$refs.processCanvas) {
this.$refs.processCanvas.innerHTML = '';
}
if (this.bpmnViewer) {
this.bpmnViewer.destroy();
}
this.bpmnViewer = null;
},
// 添加自定义箭头
addCustomDefs() {
const canvas = this.bpmnViewer.get('canvas');
const svg = canvas._svg;
const customSuccessDefs = this.$refs.customSuccessDefs;
const customFailDefs = this.$refs.customFailDefs;
svg.appendChild(customSuccessDefs);
svg.appendChild(customFailDefs);
},
// 任务悬浮弹窗
onSelectElement(element) {
this.selectTaskId = undefined;
this.dlgTitle = undefined;
if (this.processNodeInfo == null || this.processNodeInfo.finishedTaskSet == null) return; watch(
finishedInfo,
(newInfo) => {
setProcessStatus(newInfo);
},
{ immediate: true }
);
nextTick(() => {
importXML(xml.value);
setProcessStatus(finishedInfo.value);
});
function processReZoom() {
defaultZoom.value = 1;
bpmnViewer.value.get("canvas").zoom("fit-viewport", "auto");
}
function processZoomIn(zoomStep = 0.1) {
let newZoom = Math.floor(defaultZoom.value * 100 + zoomStep * 100) / 100;
if (newZoom > 4) {
throw new Error(
"[Process Designer Warn ]: The zoom ratio cannot be greater than 4"
);
}
defaultZoom.value = newZoom;
bpmnViewer.value.get("canvas").zoom(defaultZoom.value);
}
function processZoomOut(zoomStep = 0.1) {
let newZoom = Math.floor(defaultZoom.value * 100 - zoomStep * 100) / 100;
if (newZoom < 0.2) {
throw new Error(
"[Process Designer Warn ]: The zoom ratio cannot be less than 0.2"
);
}
defaultZoom.value = newZoom;
bpmnViewer.value.get("canvas").zoom(defaultZoom.value);
}
if (element == null || this.processNodeInfo.finishedTaskSet.indexOf(element.id) === -1) { // 流程图预览清空
return; function clearViewer() {
} if (processCanvas.value) {
processCanvas.value.innerHTML = "";
}
if (bpmnViewer.value) {
bpmnViewer.value.destroy();
}
bpmnViewer.value = null;
}
// 添加自定义箭头
function addCustomDefs() {
const canvas = bpmnViewer.value.get("canvas");
const svg = canvas._svg;
// const customSuccessDefs = customSuccessDefs.value;
// const customFailDefs = customFailDefs.value;
svg.appendChild(customSuccessDefs.value);
svg.appendChild(customFailDefs.value);
}
// 任务悬浮弹窗
function onSelectElement(element) {
selectTaskId.value = undefined;
dlgTitle.value = undefined;
this.selectTaskId = element.id; if (
this.dlgTitle = element.businessObject ? element.businessObject.name : undefined; processNodeInfo.value == null ||
// 计算当前悬浮任务审批记录,如果记录为空不显示弹窗 processNodeInfo.value.finishedTaskSet == null
this.taskCommentList = (this.allCommentList || []).filter(item => { )
return item.activityId === this.selectTaskId; return;
if (
element == null ||
processNodeInfo.value.finishedTaskSet.indexOf(element.id) === -1
) {
return;
}
selectTaskId.value = element.id;
dlgTitle.value = element.businessObject
? element.businessObject.name
: undefined;
// 计算当前悬浮任务审批记录,如果记录为空不显示弹窗
taskCommentList.value = (allCommentList.value || []).filter((item) => {
return item.activityId === selectTaskId.value;
});
dialogVisible.value = true;
}
// 显示流程图
async function importXML(xml) {
clearViewer();
if (xml != null && xml !== "") {
try {
bpmnViewer.value = new BpmnViewer({
additionalModules: [
// 移动整个画布
MoveCanvasModule,
],
container: processCanvas.value,
});
// 任务节点悬浮事件
bpmnViewer.value.on("element.click", ({ element }) => {
onSelectElement(element);
}); });
this.dialogVisible = true;
},
// 显示流程图
async importXML(xml) {
this.clearViewer();
if (xml != null && xml !== '') {
try {
this.bpmnViewer = new BpmnViewer({
additionalModules: [
// 移动整个画布
MoveCanvasModule
],
container: this.$refs.processCanvas,
});
// 任务节点悬浮事件
this.bpmnViewer.on('element.click', ({ element }) => {
this.onSelectElement(element);
});
this.isLoading = true; isLoading.value = true;
await this.bpmnViewer.importXML(xml); await bpmnViewer.value.importXML(xml);
this.addCustomDefs(); addCustomDefs();
} catch (e) { } catch (e) {
this.clearViewer(); clearViewer();
} finally { } finally {
this.isLoading = false; isLoading.value = false;
this.setProcessStatus(this.processNodeInfo); setProcessStatus(processNodeInfo.value);
}
}
},
// 设置流程图元素状态
setProcessStatus (processNodeInfo) {
this.processNodeInfo = processNodeInfo;
if (this.isLoading || this.processNodeInfo == null || this.bpmnViewer == null) return;
let { finishedTaskSet, rejectedTaskSet, unfinishedTaskSet, finishedSequenceFlowSet } = this.processNodeInfo;
const canvas = this.bpmnViewer.get('canvas');
const elementRegistry = this.bpmnViewer.get('elementRegistry');
if (Array.isArray(finishedSequenceFlowSet)) {
finishedSequenceFlowSet.forEach(item => {
if (item != null) {
canvas.addMarker(item, 'success');
let element = elementRegistry.get(item);
const conditionExpression = element.businessObject.conditionExpression;
if (conditionExpression) {
canvas.addMarker(item, 'condition-expression');
}
}
});
}
if (Array.isArray(finishedTaskSet)) {
finishedTaskSet.forEach(item => canvas.addMarker(item, 'success'));
}
if (Array.isArray(unfinishedTaskSet)) {
unfinishedTaskSet.forEach(item => canvas.addMarker(item, 'primary'));
}
if (Array.isArray(rejectedTaskSet)) {
rejectedTaskSet.forEach(item => {
if (item != null) {
let element = elementRegistry.get(item);
if (element.type.includes('Task')) {
canvas.addMarker(item, 'danger');
} else {
canvas.addMarker(item, 'warning');
}
}
})
}
} }
},
destroyed() {
this.clearViewer();
} }
} }
// 设置流程图元素状态
function setProcessStatus(processNodeInfoArgv) {
processNodeInfo.value = processNodeInfoArgv;
if (
isLoading.value ||
processNodeInfo.value == null ||
bpmnViewer.value == null
)
return;
let {
finishedTaskSet,
rejectedTaskSet,
unfinishedTaskSet,
finishedSequenceFlowSet,
} = processNodeInfo.value;
const canvas = bpmnViewer.value.get("canvas");
const elementRegistry = bpmnViewer.value.get("elementRegistry");
if (Array.isArray(finishedSequenceFlowSet)) {
finishedSequenceFlowSet.forEach((item) => {
if (item != null) {
canvas.addMarker(item, "success");
let element = elementRegistry.get(item);
const conditionExpression = element.businessObject.conditionExpression;
if (conditionExpression) {
canvas.addMarker(item, "condition-expression");
}
}
});
}
if (Array.isArray(finishedTaskSet)) {
finishedTaskSet.forEach((item) => canvas.addMarker(item, "success"));
}
if (Array.isArray(unfinishedTaskSet)) {
unfinishedTaskSet.forEach((item) => canvas.addMarker(item, "primary"));
}
if (Array.isArray(rejectedTaskSet)) {
rejectedTaskSet.forEach((item) => {
if (item != null) {
let element = elementRegistry.get(item);
if (element.type.includes("Task")) {
canvas.addMarker(item, "danger");
} else {
canvas.addMarker(item, "warning");
}
}
});
}
}
onBeforeUnmount(() => {
clearViewer();
});
</script> </script>
<style scoped> <style scoped></style>
</style>

View File

@ -259,7 +259,8 @@ import { ElMessage, ElMessageBox } from "element-plus";
const bpmnCanvasRef = ref(); const bpmnCanvasRef = ref();
const refFile = ref(); const refFile = ref();
const props = defineProps({ const props = defineProps({
value: String, // xml 字符串 modelValue: String, // xml 字符串
// value: String,
processId: String, processId: String,
processName: String, processName: String,
translations: Object, // 自定义的翻译文件 translations: Object, // 自定义的翻译文件
@ -285,7 +286,7 @@ const props = defineProps({
type: String, type: String,
default: "flowable", default: "flowable",
}, },
events: { eventlist: {
type: Array, type: Array,
default: () => ["element.click"], default: () => ["element.click"],
}, },
@ -305,7 +306,8 @@ const props = defineProps({
}, },
}); });
const { const {
value, // value,
modelValue,
processId, processId,
processName, processName,
translations, translations,
@ -316,7 +318,7 @@ const {
simulation, simulation,
keyboard, keyboard,
prefix, prefix,
events, eventlist,
headerButtonSize, headerButtonSize,
headerButtonType, headerButtonType,
} = toRefs(props); } = toRefs(props);
@ -375,22 +377,21 @@ const additionalModules = computed(() => {
} }
// 根据需要的流程类型设置扩展元素构建模块 // 根据需要的流程类型设置扩展元素构建模块
// if (prefix.value === "bpmn") { // // if (prefix.value === "bpmn") {
// Modules.push(bpmnModdleExtension); // // Modules.push(bpmnModdleExtension);
// } // // }
if (prefix.value === "camunda") { if (prefix.value === "camunda") {
Modules.push(camundaModdleExtension); Modules.push(camundaModdleExtension);
} }
// FIXME: Function.prototype.bind.apply(...) is not a constructor
// if (prefix.value === "flowable") { if (prefix.value === "flowable") {
// Modules.push(flowableModdleExtension); Modules.push(flowableModdleExtension);
// } }
if (prefix.value === "activiti") { if (prefix.value === "activiti") {
Modules.push(activitiModdleExtension); Modules.push(activitiModdleExtension);
} }
return Modules; return Modules;
}); });
console.log(additionalModules.value);
const moddleExtensions = computed(() => { const moddleExtensions = computed(() => {
const Extensions = {}; const Extensions = {};
// 仅使用用户自定义模块 // 仅使用用户自定义模块
@ -421,7 +422,7 @@ const moddleExtensions = computed(() => {
onMounted(() => { onMounted(() => {
initBpmnModeler(); initBpmnModeler();
createNewDiagram(value.value); createNewDiagram(modelValue.value);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
if (bpmnModeler) bpmnModeler.destroy(); if (bpmnModeler) bpmnModeler.destroy();
@ -447,19 +448,14 @@ function initBpmnModeler() {
additionalModules: additionalModules.value, additionalModules: additionalModules.value,
moddleExtensions: moddleExtensions.value, moddleExtensions: moddleExtensions.value,
}); });
console.log("init-finished");
emit("init-finished", bpmnModeler); emit("init-finished", bpmnModeler);
initModelListeners(); initModelListeners();
} }
function initModelListeners() { function initModelListeners() {
const EventBus = bpmnModeler.get("eventBus"); const EventBus = bpmnModeler.get("eventBus");
console.log(EventBus);
// const that = this;
// 注册需要的监听事件, 将. 替换为 - , 避免解析异常 // 注册需要的监听事件, 将. 替换为 - , 避免解析异常
events.value.forEach((event) => { eventlist.value.forEach((event) => {
bpmnModeler.on(event, function (eventObj) { EventBus.on(event, function (eventObj) {
console.log("element-click", eventObj);
let eventName = event.replace(/\./g, "-"); let eventName = event.replace(/\./g, "-");
let element = eventObj ? eventObj.element : null; let element = eventObj ? eventObj.element : null;
emit(eventName, element, eventObj); emit(eventName, element, eventObj);
@ -467,18 +463,18 @@ function initModelListeners() {
}); });
}); });
//FIXME: 监听图形改变返回xml //FIXME: 监听图形改变返回xml
// EventBus.on("commandStack.changed", async (event) => { EventBus.on("commandStack.changed", async (event) => {
// try { try {
// recoverable.value = bpmnModeler.get("commandStack").canRedo(); recoverable.value = bpmnModeler.get("commandStack").canRedo();
// revocable.value = bpmnModeler.get("commandStack").canUndo(); revocable.value = bpmnModeler.get("commandStack").canUndo();
// let { xml } = await bpmnModeler.saveXML({ format: true }); let { xml } = await bpmnModeler.saveXML({ format: true });
// emit("commandStack-changed", event); emit("commandStack-changed", event);
// emit("input", xml); emit("input", xml);
// emit("change", xml); emit("change", xml);
// } catch (e) { } catch (e) {
// console.error(`[Process Designer Warn]: ${e.message || e}`); console.error(`[Process Designer Warn]: ${e.message || e}`);
// } }
// }); });
// 监听视图缩放变化 // 监听视图缩放变化
bpmnModeler.on("canvas.viewbox.changed", ({ viewbox }) => { bpmnModeler.on("canvas.viewbox.changed", ({ viewbox }) => {
emit("canvas-viewbox-changed", { viewbox }); emit("canvas-viewbox-changed", { viewbox });

View File

@ -2,8 +2,8 @@
* @author igdianov * @author igdianov
* address https://github.com/igdianov/activiti-bpmn-moddle * address https://github.com/igdianov/activiti-bpmn-moddle
* */ * */
import flowableExtension from './flowableExtension'
export default { export default {
__init__: ["FlowableModdleExtension"], __init__: ["FlowableModdleExtension"],
FlowableModdleExtension: ["type", () => import("./flowableExtension")], FlowableModdleExtension: ["type", flowableExtension],
}; };

View File

@ -1,5 +1,5 @@
// import hljs from "highlight.js/lib/core"; import hljs from "highlight.js/lib/core";
// hljs.registerLanguage("xml", () => import("highlight.js/lib/languages/xml")); hljs.registerLanguage("xml", () => import("highlight.js/lib/languages/xml"));
// hljs.registerLanguage("json", () => import("highlight.js/lib/languages/json")); hljs.registerLanguage("json", () => import("highlight.js/lib/languages/json"));
// export default hljs; export default hljs;

View File

@ -2,9 +2,11 @@
<div class="process-panel__container" :style="{ width: `${this.width}px` }"> <div class="process-panel__container" :style="{ width: `${this.width}px` }">
<el-collapse v-model="activeTab"> <el-collapse v-model="activeTab">
<el-collapse-item name="base"> <el-collapse-item name="base">
<div slot="title" class="panel-tab__title"> <template #title>
<i class="el-icon-info"></i>常规 <div class="panel-tab__title">
</div> <el-icon><InfoFilled /></el-icon>常规
</div>
</template>
<element-base-info <element-base-info
:id-edit-disabled="idEditDisabled" :id-edit-disabled="idEditDisabled"
:business-object="elementBusinessObject" :business-object="elementBusinessObject"
@ -16,9 +18,11 @@
v-if="elementType === 'Process'" v-if="elementType === 'Process'"
key="message" key="message"
> >
<div slot="title" class="panel-tab__title"> <template #title>
<i class="el-icon-s-comment"></i>消息与信号 <div class="panel-tab__title">
</div> <el-icon><Comment /></el-icon>消息与信号
</div>
</template>
<signal-and-massage /> <signal-and-massage />
</el-collapse-item> </el-collapse-item>
<el-collapse-item <el-collapse-item
@ -26,18 +30,22 @@
v-if="conditionFormVisible" v-if="conditionFormVisible"
key="condition" key="condition"
> >
<div slot="title" class="panel-tab__title"> <template #title>
<i class="el-icon-s-promotion"></i>流转条件 <div class="panel-tab__title">
</div> <el-icon><Promotion /></el-icon>流转条件
</div>
</template>
<flow-condition <flow-condition
:business-object="elementBusinessObject" :business-object="elementBusinessObject"
:type="elementType" :type="elementType"
/> />
</el-collapse-item> </el-collapse-item>
<el-collapse-item name="condition" v-if="formVisible" key="form"> <el-collapse-item name="condition" v-if="formVisible" key="form">
<div slot="title" class="panel-tab__title"> <template #title>
<i class="el-icon-s-order"></i>表单 <div class="panel-tab__title">
</div> <el-icon><Document /></el-icon>表单
</div>
</template>
<element-form :id="elementId" :type="elementType" /> <element-form :id="elementId" :type="elementType" />
</el-collapse-item> </el-collapse-item>
<el-collapse-item <el-collapse-item
@ -45,9 +53,11 @@
v-if="elementType.indexOf('Task') !== -1" v-if="elementType.indexOf('Task') !== -1"
key="task" key="task"
> >
<div slot="title" class="panel-tab__title"> <template #title
<i class="el-icon-s-claim"></i>任务 ><div class="panel-tab__title">
</div> <el-icon><Checked /></el-icon>任务
</div>
</template>
<element-task :id="elementId" :type="elementType" /> <element-task :id="elementId" :type="elementType" />
</el-collapse-item> </el-collapse-item>
<el-collapse-item <el-collapse-item
@ -55,18 +65,22 @@
v-if="elementType.indexOf('Task') !== -1 && elementType !== 'UserTask'" v-if="elementType.indexOf('Task') !== -1 && elementType !== 'UserTask'"
key="multiInstance" key="multiInstance"
> >
<div slot="title" class="panel-tab__title"> <template #title
<i class="el-icon-s-help"></i>多实例 ><div class="panel-tab__title">
</div> <el-icon><Help /></el-icon>多实例
</div>
</template>
<element-multi-instance <element-multi-instance
:business-object="elementBusinessObject" :business-object="elementBusinessObject"
:type="elementType" :type="elementType"
/> />
</el-collapse-item> </el-collapse-item>
<el-collapse-item name="listeners" key="listeners"> <el-collapse-item name="listeners" key="listeners">
<div slot="title" class="panel-tab__title"> <template #title
<i class="el-icon-message-solid"></i>执行监听器 ><div class="panel-tab__title">
</div> <el-icon><Message /></el-icon>执行监听器
</div>
</template>
<element-listeners :id="elementId" :type="elementType" /> <element-listeners :id="elementId" :type="elementType" />
</el-collapse-item> </el-collapse-item>
<el-collapse-item <el-collapse-item
@ -74,21 +88,27 @@
v-if="elementType === 'UserTask'" v-if="elementType === 'UserTask'"
key="taskListeners" key="taskListeners"
> >
<div slot="title" class="panel-tab__title"> <template #title
<i class="el-icon-message-solid"></i>任务监听器 ><div class="panel-tab__title">
</div> <el-icon><Message /></el-icon>任务监听器
</div></template
>
<user-task-listeners :id="elementId" :type="elementType" /> <user-task-listeners :id="elementId" :type="elementType" />
</el-collapse-item> </el-collapse-item>
<el-collapse-item name="extensions" key="extensions"> <el-collapse-item name="extensions" key="extensions">
<div slot="title" class="panel-tab__title"> <template #title
<i class="el-icon-circle-plus"></i>扩展属性 ><div class="panel-tab__title">
</div> <el-icon><CirclePlus /></el-icon>扩展属性
</div></template
>
<element-properties :id="elementId" :type="elementType" /> <element-properties :id="elementId" :type="elementType" />
</el-collapse-item> </el-collapse-item>
<el-collapse-item name="other" key="other"> <el-collapse-item name="other" key="other">
<div slot="title" class="panel-tab__title"> <template #title
<i class="el-icon-s-promotion"></i>其他 ><div class="panel-tab__title">
</div> <el-icon><Promotion /></el-icon>其他
</div></template
>
<element-other-config :id="elementId" /> <element-other-config :id="elementId" />
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
@ -128,11 +148,6 @@ const props = defineProps({
const { bpmnModeler, prefix, width, idEditDisabled } = toRefs(props); const { bpmnModeler, prefix, width, idEditDisabled } = toRefs(props);
provide("prefix", prefix.value); provide("prefix", prefix.value);
provide("width", width.value); provide("width", width.value);
const bpmnStore = useBpmnStore();
// const data = reactive({
// timer: undefined,
// });
// const { timer } = toRefs(data);
const activeTab = ref("base"); const activeTab = ref("base");
const elementId = ref(""); const elementId = ref("");
@ -143,49 +158,15 @@ const formVisible = ref(false); // 表单配置
let timer; let timer;
let bpmnElement; let bpmnElement;
function initModels() { function initModels() {
// bpmnStore.count = 123;
console.log(bpmnModeler.value);
// 初始化 modeler 以及其他 moddle // 初始化 modeler 以及其他 moddle
if (!bpmnModeler.value) { if (!bpmnModeler.value) {
// 避免加载时 流程图 并未加载完成 // 避免加载时 流程图 并未加载完成
timer = setTimeout(() => initModels(), 10); timer = setTimeout(() => initModels(), 10);
return; return;
} }
// console.log(timer);
if (timer) clearTimeout(timer); if (timer) clearTimeout(timer);
// timer = null;
console.log(bpmnModeler.value);
// bpmnStore.bpmnInstances = {
// test: bpmnModeler.value,
// };
// bpmnModeler.value.get = undefined;
// console.log(JSON.stringify(bpmnModeler.value));
// bpmnStore.setBpmnInstances({
// modeler: { _container: bpmnModeler.value._container },
// // modeling: bpmnModeler.value.get("modeling"),
// // moddle: bpmnModeler.value.get("moddle"),
// // eventBus: bpmnModeler.value.get("eventBus"),
// // bpmnFactory: bpmnModeler.value.get("bpmnFactory"),
// // elementFactory: bpmnModeler.value.get("elementFactory"),
// elementRegistry: {
// _elements: bpmnModeler.value.get("elementRegistry")._elements,
// },
// // replace: bpmnModeler.value.get("replace"),
// // selection: bpmnModeler.value.get("selection"),
// });
// bpmnStore.bpmnInstances =
// {
// modeler: bpmnModeler.value,
// modeling: bpmnModeler.value.get("modeling"),
// moddle: bpmnModeler.value.get("moddle"),
// eventBus: bpmnModeler.value.get("eventBus"),
// bpmnFactory: bpmnModeler.value.get("bpmnFactory"),
// elementFactory: bpmnModeler.value.get("elementFactory"),
// elementRegistry: bpmnModeler.value.get("elementRegistry"),
// replace: bpmnModeler.value.get("replace"),
// selection: bpmnModeler.value.get("selection"),
// };
console.log(bpmnStore.bpmnInstances, "167");
window.bpmnInstances = { window.bpmnInstances = {
modeler: bpmnModeler.value, modeler: bpmnModeler.value,
modeling: bpmnModeler.value.get("modeling"), modeling: bpmnModeler.value.get("modeling"),
@ -200,7 +181,6 @@ function initModels() {
getActiveElement(); getActiveElement();
} }
function getActiveElement() { function getActiveElement() {
console.log("select first");
// 初始第一个选中元素 bpmn:Process // 初始第一个选中元素 bpmn:Process
initFormOnChanged(null); initFormOnChanged(null);
bpmnModeler.value.on("import.done", (e) => { bpmnModeler.value.on("import.done", (e) => {
@ -208,7 +188,6 @@ function getActiveElement() {
}); });
// 监听选择事件,修改当前激活的元素以及表单 // 监听选择事件,修改当前激活的元素以及表单
bpmnModeler.value.on("selection.changed", ({ newSelection }) => { bpmnModeler.value.on("selection.changed", ({ newSelection }) => {
console.log("selection.changed");
initFormOnChanged(newSelection[0] || null); initFormOnChanged(newSelection[0] || null);
}); });
bpmnModeler.value.on("element.changed", ({ element }) => { bpmnModeler.value.on("element.changed", ({ element }) => {
@ -254,9 +233,9 @@ function initFormOnChanged(element) {
formVisible.value = formVisible.value =
elementType.value === "UserTask" || elementType.value === "StartEvent"; elementType.value === "UserTask" || elementType.value === "StartEvent";
} }
function beforeDestroy() { onBeforeUnmount(() => {
window.bpmnInstances = null; window.bpmnInstances = null;
} });
watch(elementId, (val) => { watch(elementId, (val) => {
activeTab.value = "base"; activeTab.value = "base";

View File

@ -40,8 +40,7 @@
<script setup name="ElementBaseInfo"> <script setup name="ElementBaseInfo">
import { nextTick, onBeforeUnmount, toRefs } from "vue"; import { nextTick, onBeforeUnmount, toRefs } from "vue";
import useBpmnStore from "@/store/modules/bpmn"; let bpmnElement;
const bpmnStore = useBpmnStore();
const props = defineProps({ const props = defineProps({
businessObject: Object, businessObject: Object,
type: String, type: String,
@ -55,7 +54,7 @@ const data = reactive({
elementBaseInfo: {}, elementBaseInfo: {},
bpmnElement: undefined, bpmnElement: undefined,
}); });
const { elementBaseInfo, bpmnElement } = toRefs(data); const { elementBaseInfo } = toRefs(data);
watch( watch(
businessObject, businessObject,
(val) => { (val) => {
@ -69,24 +68,24 @@ watch(
); );
function resetBaseInfo() { function resetBaseInfo() {
bpmnElement.value = window?.bpmnInstances?.bpmnElement; bpmnElement = window?.bpmnInstances?.bpmnElement;
elementBaseInfo.value = JSON.parse( elementBaseInfo.value = JSON.parse(
JSON.stringify(bpmnElement.value.businessObject) JSON.stringify(bpmnElement.businessObject)
); );
} }
function updateBaseInfo(key) { function updateBaseInfo(key) {
const attrObj = Object.create(null); const attrObj = Object.create(null);
attrObj[key] = elementBaseInfo.value[key]; attrObj[key] = elementBaseInfo.value[key];
if (key === "id") { if (key === "id") {
window.bpmnInstances.modeling.updateProperties(bpmnElement.value, { window.bpmnInstances.modeling.updateProperties(bpmnElement, {
id: elementBaseInfo.value[key], id: elementBaseInfo.value[key],
di: { id: `${elementBaseInfo.value[key]}_di` }, di: { id: `${elementBaseInfo.value[key]}_di` },
}); });
} else { } else {
window.bpmnInstances.modeling.updateProperties(bpmnElement.value, attrObj); window.bpmnInstances.modeling.updateProperties(bpmnElement, attrObj);
} }
} }
onBeforeUnmount(() => { onBeforeUnmount(() => {
bpmnElement.value = null; bpmnElement = null;
}); });
</script> </script>

View File

@ -181,7 +181,7 @@
<el-input v-model="fieldOptionForm.value" clearable /> <el-input v-model="fieldOptionForm.value" clearable />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template slot="footer"> <template #footer>
<el-button size="small" @click="fieldOptionModelVisible = false" <el-button size="small" @click="fieldOptionModelVisible = false"
> </el-button > </el-button
> >

View File

@ -11,16 +11,13 @@
/> />
<el-table-column label="操作" width="90px"> <el-table-column label="操作" width="90px">
<template slot-scope="{ row, $index }"> <template slot-scope="{ row, $index }">
<el-button <el-button size="small" link @click="openListenerForm(row, $index)"
size="small"
type="text"
@click="openListenerForm(row, $index)"
>编辑</el-button >编辑</el-button
> >
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button <el-button
size="small" size="small"
type="text" link
style="color: #ff4d4f" style="color: #ff4d4f"
@click="removeListener(row, $index)" @click="removeListener(row, $index)"
>移除</el-button >移除</el-button
@ -196,14 +193,14 @@
<template slot-scope="{ row, $index }"> <template slot-scope="{ row, $index }">
<el-button <el-button
size="small" size="small"
type="text" link
@click="openListenerFieldForm(row, $index)" @click="openListenerFieldForm(row, $index)"
>编辑</el-button >编辑</el-button
> >
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button <el-button
size="small" size="small"
type="text" link
style="color: #ff4d4f" style="color: #ff4d4f"
@click="removeListenerField(row, $index)" @click="removeListenerField(row, $index)"
>移除</el-button >移除</el-button
@ -278,7 +275,7 @@
<el-input v-model="listenerFieldForm.expression" clearable /> <el-input v-model="listenerFieldForm.expression" clearable />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template slot="footer"> <template #footer>
<el-button size="small" @click="listenerFieldFormModelVisible = false" <el-button size="small" @click="listenerFieldFormModelVisible = false"
> </el-button > </el-button
> >

View File

@ -2,35 +2,98 @@
<div class="panel-tab__content"> <div class="panel-tab__content">
<el-table :data="elementListenersList" size="small" border> <el-table :data="elementListenersList" size="small" border>
<el-table-column label="序号" width="50px" type="index" /> <el-table-column label="序号" width="50px" type="index" />
<el-table-column label="事件类型" min-width="80px" show-overflow-tooltip :formatter="row => listenerEventTypeObject[row.event]" /> <el-table-column
<el-table-column label="事件id" min-width="80px" prop="id" show-overflow-tooltip /> label="事件类型"
<el-table-column label="监听器类型" min-width="80px" show-overflow-tooltip :formatter="row => listenerTypeObject[row.listenerType]" /> min-width="80px"
show-overflow-tooltip
:formatter="(row) => listenerEventTypeObject[row.event]"
/>
<el-table-column
label="事件id"
min-width="80px"
prop="id"
show-overflow-tooltip
/>
<el-table-column
label="监听器类型"
min-width="80px"
show-overflow-tooltip
:formatter="(row) => listenerTypeObject[row.listenerType]"
/>
<el-table-column label="操作" width="90px"> <el-table-column label="操作" width="90px">
<template slot-scope="{ row, $index }"> <template slot-scope="{ row, $index }">
<el-button size="small" type="text" @click="openListenerForm(row, $index)">编辑</el-button> <el-button size="small" link @click="openListenerForm(row, $index)"
>编辑</el-button
>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button size="small" type="text" style="color: #ff4d4f" @click="removeListener(row, $index)">移除</el-button> <el-button
size="small"
link
style="color: #ff4d4f"
@click="removeListener(row, $index)"
>移除</el-button
>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<div class="element-drawer__button"> <div class="element-drawer__button">
<el-button size="small" type="primary" icon="el-icon-plus" @click="openListenerForm(null)">添加监听器</el-button> <el-button
size="small"
type="primary"
icon="el-icon-plus"
@click="openListenerForm(null)"
>添加监听器</el-button
>
</div> </div>
<!-- 监听器 编辑/创建 部分 --> <!-- 监听器 编辑/创建 部分 -->
<el-drawer v-model="listenerFormModelVisible" title="任务监听器" :size="`${width}px`" append-to-body destroy-on-close> <el-drawer
<el-form size="small" :model="listenerForm" label-width="96px" ref="listenerFormRef" @submit.native.prevent> v-model="listenerFormModelVisible"
<el-form-item label="事件类型" prop="event" :rules="{ required: true, trigger: ['blur', 'change'] }"> title="任务监听器"
:size="`${width}px`"
append-to-body
destroy-on-close
>
<el-form
size="small"
:model="listenerForm"
label-width="96px"
ref="listenerFormRef"
@submit.native.prevent
>
<el-form-item
label="事件类型"
prop="event"
:rules="{ required: true, trigger: ['blur', 'change'] }"
>
<el-select v-model="listenerForm.event"> <el-select v-model="listenerForm.event">
<el-option v-for="i in Object.keys(listenerEventTypeObject)" :key="i" :label="listenerEventTypeObject[i]" :value="i" /> <el-option
v-for="i in Object.keys(listenerEventTypeObject)"
:key="i"
:label="listenerEventTypeObject[i]"
:value="i"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="监听器ID" prop="id" :rules="{ required: true, trigger: ['blur', 'change'] }"> <el-form-item
label="监听器ID"
prop="id"
:rules="{ required: true, trigger: ['blur', 'change'] }"
>
<el-input v-model="listenerForm.id" clearable /> <el-input v-model="listenerForm.id" clearable />
</el-form-item> </el-form-item>
<el-form-item label="监听器类型" prop="listenerType" :rules="{ required: true, trigger: ['blur', 'change'] }"> <el-form-item
label="监听器类型"
prop="listenerType"
:rules="{ required: true, trigger: ['blur', 'change'] }"
>
<el-select v-model="listenerForm.listenerType"> <el-select v-model="listenerForm.listenerType">
<el-option v-for="i in Object.keys(listenerTypeObject)" :key="i" :label="listenerTypeObject[i]" :value="i" /> <el-option
v-for="i in Object.keys(listenerTypeObject)"
:key="i"
:label="listenerTypeObject[i]"
:value="i"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
@ -65,7 +128,11 @@
label="脚本格式" label="脚本格式"
prop="scriptFormat" prop="scriptFormat"
key="listener-script-format" key="listener-script-format"
:rules="{ required: true, trigger: ['blur', 'change'], message: '请填写脚本格式' }" :rules="{
required: true,
trigger: ['blur', 'change'],
message: '请填写脚本格式',
}"
> >
<el-input v-model="listenerForm.scriptFormat" clearable /> <el-input v-model="listenerForm.scriptFormat" clearable />
</el-form-item> </el-form-item>
@ -73,7 +140,11 @@
label="脚本类型" label="脚本类型"
prop="scriptType" prop="scriptType"
key="listener-script-type" key="listener-script-type"
:rules="{ required: true, trigger: ['blur', 'change'], message: '请选择脚本类型' }" :rules="{
required: true,
trigger: ['blur', 'change'],
message: '请选择脚本类型',
}"
> >
<el-select v-model="listenerForm.scriptType"> <el-select v-model="listenerForm.scriptType">
<el-option label="内联脚本" value="inlineScript" /> <el-option label="内联脚本" value="inlineScript" />
@ -85,7 +156,11 @@
label="脚本内容" label="脚本内容"
prop="value" prop="value"
key="listener-script" key="listener-script"
:rules="{ required: true, trigger: ['blur', 'change'], message: '请填写脚本内容' }" :rules="{
required: true,
trigger: ['blur', 'change'],
message: '请填写脚本内容',
}"
> >
<el-input v-model="listenerForm.value" clearable /> <el-input v-model="listenerForm.value" clearable />
</el-form-item> </el-form-item>
@ -94,14 +169,22 @@
label="资源地址" label="资源地址"
prop="resource" prop="resource"
key="listener-resource" key="listener-resource"
:rules="{ required: true, trigger: ['blur', 'change'], message: '请填写资源地址' }" :rules="{
required: true,
trigger: ['blur', 'change'],
message: '请填写资源地址',
}"
> >
<el-input v-model="listenerForm.resource" clearable /> <el-input v-model="listenerForm.resource" clearable />
</el-form-item> </el-form-item>
</template> </template>
<template v-if="listenerForm.event === 'timeout'"> <template v-if="listenerForm.event === 'timeout'">
<el-form-item label="定时器类型" prop="eventDefinitionType" key="eventDefinitionType"> <el-form-item
label="定时器类型"
prop="eventDefinitionType"
key="eventDefinitionType"
>
<el-select v-model="listenerForm.eventDefinitionType"> <el-select v-model="listenerForm.eventDefinitionType">
<el-option label="日期" value="date" /> <el-option label="日期" value="date" />
<el-option label="持续时长" value="duration" /> <el-option label="持续时长" value="duration" />
@ -110,11 +193,18 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="!!listenerForm.eventDefinitionType && listenerForm.eventDefinitionType !== 'null'" v-if="
!!listenerForm.eventDefinitionType &&
listenerForm.eventDefinitionType !== 'null'
"
label="定时器" label="定时器"
prop="eventTimeDefinitions" prop="eventTimeDefinitions"
key="eventTimeDefinitions" key="eventTimeDefinitions"
:rules="{ required: true, trigger: ['blur', 'change'], message: '请填写定时器配置' }" :rules="{
required: true,
trigger: ['blur', 'change'],
message: '请填写定时器配置',
}"
> >
<el-input v-model="listenerForm.eventTimeDefinitions" clearable /> <el-input v-model="listenerForm.eventTimeDefinitions" clearable />
</el-form-item> </el-form-item>
@ -124,37 +214,100 @@
<el-divider /> <el-divider />
<p class="listener-filed__title"> <p class="listener-filed__title">
<span><i class="el-icon-menu"></i>注入字段:</span> <span><i class="el-icon-menu"></i>注入字段:</span>
<el-button size="small" type="primary" @click="openListenerFieldForm(null)">添加字段</el-button> <el-button
size="small"
type="primary"
@click="openListenerFieldForm(null)"
>添加字段</el-button
>
</p> </p>
<el-table :data="fieldsListOfListener" size="small" max-height="240" border fit style="flex: none"> <el-table
:data="fieldsListOfListener"
size="small"
max-height="240"
border
fit
style="flex: none"
>
<el-table-column label="序号" width="50px" type="index" /> <el-table-column label="序号" width="50px" type="index" />
<el-table-column label="字段名称" min-width="100px" prop="name" /> <el-table-column label="字段名称" min-width="100px" prop="name" />
<el-table-column label="字段类型" min-width="80px" show-overflow-tooltip :formatter="row => fieldTypeObject[row.fieldType]" /> <el-table-column
<el-table-column label="字段值/表达式" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" /> label="字段类型"
min-width="80px"
show-overflow-tooltip
:formatter="(row) => fieldTypeObject[row.fieldType]"
/>
<el-table-column
label="字段值/表达式"
min-width="100px"
show-overflow-tooltip
:formatter="(row) => row.string || row.expression"
/>
<el-table-column label="操作" width="100px"> <el-table-column label="操作" width="100px">
<template slot-scope="{ row, $index }"> <template slot-scope="{ row, $index }">
<el-button size="small" type="text" @click="openListenerFieldForm(row, $index)">编辑</el-button> <el-button
size="small"
link
@click="openListenerFieldForm(row, $index)"
>编辑</el-button
>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button size="small" type="text" style="color: #ff4d4f" @click="removeListenerField(row, $index)">移除</el-button> <el-button
size="small"
link
style="color: #ff4d4f"
@click="removeListenerField(row, $index)"
>移除</el-button
>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<div class="element-drawer__button"> <div class="element-drawer__button">
<el-button size="small" @click="listenerFormModelVisible = false"> </el-button> <el-button size="small" @click="listenerFormModelVisible = false"
<el-button size="small" type="primary" @click="saveListenerConfig"> </el-button> >取 消</el-button
>
<el-button size="small" type="primary" @click="saveListenerConfig"
>保 存</el-button
>
</div> </div>
</el-drawer> </el-drawer>
<!-- 注入西段 编辑/创建 部分 --> <!-- 注入西段 编辑/创建 部分 -->
<el-dialog title="字段配置" v-model="listenerFieldFormModelVisible" width="600px" append-to-body destroy-on-close> <el-dialog
<el-form :model="listenerFieldForm" size="small" label-width="96px" ref="listenerFieldFormRef" style="height: 136px" @submit.native.prevent> title="字段配置"
<el-form-item label="字段名称:" prop="name" :rules="{ required: true, trigger: ['blur', 'change'] }"> v-model="listenerFieldFormModelVisible"
width="600px"
append-to-body
destroy-on-close
>
<el-form
:model="listenerFieldForm"
size="small"
label-width="96px"
ref="listenerFieldFormRef"
style="height: 136px"
@submit.native.prevent
>
<el-form-item
label="字段名称:"
prop="name"
:rules="{ required: true, trigger: ['blur', 'change'] }"
>
<el-input v-model="listenerFieldForm.name" clearable /> <el-input v-model="listenerFieldForm.name" clearable />
</el-form-item> </el-form-item>
<el-form-item label="字段类型:" prop="fieldType" :rules="{ required: true, trigger: ['blur', 'change'] }"> <el-form-item
label="字段类型:"
prop="fieldType"
:rules="{ required: true, trigger: ['blur', 'change'] }"
>
<el-select v-model="listenerFieldForm.fieldType"> <el-select v-model="listenerFieldForm.fieldType">
<el-option v-for="i in Object.keys(fieldTypeObject)" :key="i" :label="fieldTypeObject[i]" :value="i" /> <el-option
v-for="i in Object.keys(fieldTypeObject)"
:key="i"
:label="fieldTypeObject[i]"
:value="i"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
@ -176,26 +329,37 @@
<el-input v-model="listenerFieldForm.expression" clearable /> <el-input v-model="listenerFieldForm.expression" clearable />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template slot="footer"> <template #footer>
<el-button size="small" @click="listenerFieldFormModelVisible = false"> </el-button> <el-button size="small" @click="listenerFieldFormModelVisible = false"
<el-button size="small" type="primary" @click="saveListenerFiled"> </el-button> > </el-button
>
<el-button size="small" type="primary" @click="saveListenerFiled"
> </el-button
>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { ElMessageBox } from "element-plus";
import { createListenerObject, updateElementExtensions } from "../../utils"; import { createListenerObject, updateElementExtensions } from "../../utils";
import { initListenerForm, initListenerType, eventType, listenerType, fieldType } from "./utilSelf"; import {
initListenerForm,
initListenerType,
eventType,
listenerType,
fieldType,
} from "./utilSelf";
export default { export default {
name: "UserTaskListeners", name: "UserTaskListeners",
props: { props: {
id: String, id: String,
type: String type: String,
}, },
inject: { inject: {
prefix: "prefix", prefix: "prefix",
width: "width" width: "width",
}, },
data() { data() {
return { return {
@ -209,7 +373,7 @@ export default {
listenerFieldFormModelVisible: false, // 监听器 注入字段表单弹窗 显示状态 listenerFieldFormModelVisible: false, // 监听器 注入字段表单弹窗 显示状态
editingListenerIndex: -1, // 监听器所在下标,-1 为新增 editingListenerIndex: -1, // 监听器所在下标,-1 为新增
editingListenerFieldIndex: -1, // 字段所在下标,-1 为新增 editingListenerFieldIndex: -1, // 字段所在下标,-1 为新增
listenerFieldForm: {} // 监听器 注入字段 详情表单 listenerFieldForm: {}, // 监听器 注入字段 详情表单
}; };
}, },
watch: { watch: {
@ -217,15 +381,20 @@ export default {
immediate: true, immediate: true,
handler(val) { handler(val) {
val && val.length && this.$nextTick(() => this.resetListenersList()); val && val.length && this.$nextTick(() => this.resetListenersList());
} },
} },
}, },
methods: { methods: {
resetListenersList() { resetListenersList() {
this.bpmnElement = window.bpmnInstances.bpmnElement; this.bpmnElement = window.bpmnInstances.bpmnElement;
this.otherExtensionList = []; this.otherExtensionList = [];
this.bpmnElementListeners = this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type === `${this.prefix}:TaskListener`) ?? []; this.bpmnElementListeners =
this.elementListenersList = this.bpmnElementListeners.map(listener => initListenerType(listener)); this.bpmnElement.businessObject?.extensionElements?.values?.filter(
(ex) => ex.$type === `${this.prefix}:TaskListener`
) ?? [];
this.elementListenersList = this.bpmnElementListeners.map((listener) =>
initListenerType(listener)
);
}, },
openListenerForm(listener, index) { openListenerForm(listener, index) {
if (listener) { if (listener) {
@ -236,7 +405,10 @@ export default {
this.editingListenerIndex = -1; // 标记为新增 this.editingListenerIndex = -1; // 标记为新增
} }
if (listener && listener.fields) { if (listener && listener.fields) {
this.fieldsListOfListener = listener.fields.map(field => ({ ...field, fieldType: field.string ? "string" : "expression" })); this.fieldsListOfListener = listener.fields.map((field) => ({
...field,
fieldType: field.string ? "string" : "expression",
}));
} else { } else {
this.fieldsListOfListener = []; this.fieldsListOfListener = [];
this.$set(this.listenerForm, "fields", []); this.$set(this.listenerForm, "fields", []);
@ -244,19 +416,23 @@ export default {
// 打开侧边栏并清楚验证状态 // 打开侧边栏并清楚验证状态
this.listenerFormModelVisible = true; this.listenerFormModelVisible = true;
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs["listenerFormRef"]) this.$refs["listenerFormRef"].clearValidate(); if (this.$refs["listenerFormRef"])
this.$refs["listenerFormRef"].clearValidate();
}); });
}, },
// 移除监听器 // 移除监听器
removeListener(listener, index) { removeListener(listener, index) {
this.$confirm("确认移除该监听器吗?", "提示", { ElMessageBox.confirm("确认移除该监听器吗?", "提示", {
confirmButtonText: "确 认", confirmButtonText: "确 认",
cancelButtonText: "取 消" cancelButtonText: "取 消",
}) })
.then(() => { .then(() => {
this.bpmnElementListeners.splice(index, 1); this.bpmnElementListeners.splice(index, 1);
this.elementListenersList.splice(index, 1); this.elementListenersList.splice(index, 1);
updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners)); updateElementExtensions(
this.bpmnElement,
this.otherExtensionList.concat(this.bpmnElementListeners)
);
}) })
.catch(() => console.info("操作取消")); .catch(() => console.info("操作取消"));
}, },
@ -264,17 +440,35 @@ export default {
async saveListenerConfig() { async saveListenerConfig() {
let validateStatus = await this.$refs["listenerFormRef"].validate(); let validateStatus = await this.$refs["listenerFormRef"].validate();
if (!validateStatus) return; // 验证不通过直接返回 if (!validateStatus) return; // 验证不通过直接返回
const listenerObject = createListenerObject(this.listenerForm, true, this.prefix); const listenerObject = createListenerObject(
this.listenerForm,
true,
this.prefix
);
if (this.editingListenerIndex === -1) { if (this.editingListenerIndex === -1) {
this.bpmnElementListeners.push(listenerObject); this.bpmnElementListeners.push(listenerObject);
this.elementListenersList.push(this.listenerForm); this.elementListenersList.push(this.listenerForm);
} else { } else {
this.bpmnElementListeners.splice(this.editingListenerIndex, 1, listenerObject); this.bpmnElementListeners.splice(
this.elementListenersList.splice(this.editingListenerIndex, 1, this.listenerForm); this.editingListenerIndex,
1,
listenerObject
);
this.elementListenersList.splice(
this.editingListenerIndex,
1,
this.listenerForm
);
} }
// 保存其他配置 // 保存其他配置
this.otherExtensionList = this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type !== `${this.prefix}:TaskListener`) ?? []; this.otherExtensionList =
updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners)); this.bpmnElement.businessObject?.extensionElements?.values?.filter(
(ex) => ex.$type !== `${this.prefix}:TaskListener`
) ?? [];
updateElementExtensions(
this.bpmnElement,
this.otherExtensionList.concat(this.bpmnElementListeners)
);
// 4. 隐藏侧边栏 // 4. 隐藏侧边栏
this.listenerFormModelVisible = false; this.listenerFormModelVisible = false;
this.listenerForm = {}; this.listenerForm = {};
@ -285,7 +479,8 @@ export default {
this.editingListenerFieldIndex = field ? index : -1; this.editingListenerFieldIndex = field ? index : -1;
this.listenerFieldFormModelVisible = true; this.listenerFieldFormModelVisible = true;
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs["listenerFieldFormRef"]) this.$refs["listenerFieldFormRef"].clearValidate(); if (this.$refs["listenerFieldFormRef"])
this.$refs["listenerFieldFormRef"].clearValidate();
}); });
}, },
// 保存监听器注入字段 // 保存监听器注入字段
@ -296,8 +491,16 @@ export default {
this.fieldsListOfListener.push(this.listenerFieldForm); this.fieldsListOfListener.push(this.listenerFieldForm);
this.listenerForm.fields.push(this.listenerFieldForm); this.listenerForm.fields.push(this.listenerFieldForm);
} else { } else {
this.fieldsListOfListener.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm); this.fieldsListOfListener.splice(
this.listenerForm.fields.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm); this.editingListenerFieldIndex,
1,
this.listenerFieldForm
);
this.listenerForm.fields.splice(
this.editingListenerFieldIndex,
1,
this.listenerFieldForm
);
} }
this.listenerFieldFormModelVisible = false; this.listenerFieldFormModelVisible = false;
this.$nextTick(() => (this.listenerFieldForm = {})); this.$nextTick(() => (this.listenerFieldForm = {}));
@ -306,14 +509,14 @@ export default {
removeListenerField(field, index) { removeListenerField(field, index) {
this.$confirm("确认移除该字段吗?", "提示", { this.$confirm("确认移除该字段吗?", "提示", {
confirmButtonText: "确 认", confirmButtonText: "确 认",
cancelButtonText: "取 消" cancelButtonText: "取 消",
}) })
.then(() => { .then(() => {
this.fieldsListOfListener.splice(index, 1); this.fieldsListOfListener.splice(index, 1);
this.listenerForm.fields.splice(index, 1); this.listenerForm.fields.splice(index, 1);
}) })
.catch(() => console.info("操作取消")); .catch(() => console.info("操作取消"));
} },
} },
}; };
</script> </script>

View File

@ -7,9 +7,9 @@ export const template = isTaskListener => {
<el-table-column label="监听器类型" min-width="100px" show-overflow-tooltip :formatter="row => listenerTypeObject[row.listenerType]" /> <el-table-column label="监听器类型" min-width="100px" show-overflow-tooltip :formatter="row => listenerTypeObject[row.listenerType]" />
<el-table-column label="操作" width="90px"> <el-table-column label="操作" width="90px">
<template slot-scope="{ row, $index }"> <template slot-scope="{ row, $index }">
<el-button size="small" type="text" @click="openListenerForm(row, $index)">编辑</el-button> <el-button size="small" link @click="openListenerForm(row, $index)">编辑</el-button>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button size="small" type="text" style="color: #ff4d4f" @click="removeListener(row, $index)">移除</el-button> <el-button size="small" link style="color: #ff4d4f" @click="removeListener(row, $index)">移除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -97,21 +97,20 @@ export const template = isTaskListener => {
<el-input v-model="listenerForm.resource" clearable /> <el-input v-model="listenerForm.resource" clearable />
</el-form-item> </el-form-item>
</template> </template>
${ ${isTaskListener
isTaskListener ? "<el-form-item label='定时器类型' prop='eventDefinitionType' key='eventDefinitionType'>" +
? "<el-form-item label='定时器类型' prop='eventDefinitionType' key='eventDefinitionType'>" + "<el-select v-model='listenerForm.eventDefinitionType'>" +
"<el-select v-model='listenerForm.eventDefinitionType'>" + "<el-option label='日期' value='date' />" +
"<el-option label='日期' value='date' />" + "<el-option label='持续时长' value='duration' />" +
"<el-option label='持续时长' value='duration' />" + "<el-option label='循环' value='cycle' />" +
"<el-option label='循环' value='cycle' />" + "<el-option label='' value='' />" +
"<el-option label='无' value='' />" + "</el-select>" +
"</el-select>" + "</el-form-item>" +
"</el-form-item>" + "<el-form-item v-if='!!listenerForm.eventDefinitionType' label='定时器' prop='eventDefinitions' key='eventDefinitions'>" +
"<el-form-item v-if='!!listenerForm.eventDefinitionType' label='定时器' prop='eventDefinitions' key='eventDefinitions'>" + "<el-input v-model='listenerForm.eventDefinitions' clearable />" +
"<el-input v-model='listenerForm.eventDefinitions' clearable />" + "</el-form-item>"
"</el-form-item>" : ""
: "" }
}
</el-form> </el-form>
<el-divider /> <el-divider />
<p class="listener-filed__title"> <p class="listener-filed__title">
@ -125,9 +124,9 @@ export const template = isTaskListener => {
<el-table-column label="字段值/表达式" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" /> <el-table-column label="字段值/表达式" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" />
<el-table-column label="操作" width="100px"> <el-table-column label="操作" width="100px">
<template slot-scope="{ row, $index }"> <template slot-scope="{ row, $index }">
<el-button size="small" type="text" @click="openListenerFieldForm(row, $index)">编辑</el-button> <el-button size="small" link @click="openListenerFieldForm(row, $index)">编辑</el-button>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button size="small" type="text" style="color: #ff4d4f" @click="removeListenerField(row, $index)">移除</el-button> <el-button size="small" link style="color: #ff4d4f" @click="removeListenerField(row, $index)">移除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -168,7 +167,7 @@ export const template = isTaskListener => {
<el-input v-model="listenerFieldForm.expression" clearable /> <el-input v-model="listenerFieldForm.expression" clearable />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template slot="footer"> <template #footer>
<el-button size="small" @click="listenerFieldFormModelVisible = false">取 消</el-button> <el-button size="small" @click="listenerFieldFormModelVisible = false">取 消</el-button>
<el-button size="small" type="primary" @click="saveListenerFiled">确 定</el-button> <el-button size="small" type="primary" @click="saveListenerFiled">确 定</el-button>
</template> </template>

View File

@ -16,44 +16,47 @@
</div> </div>
</div> </div>
</template> </template>
<script setup name="ElementOtherConfig">
import { toRefs } from "vue";
<script> const props = defineProps({
export default { id: String,
name: "ElementOtherConfig", });
props: { const { id } = toRefs(props);
id: String let bpmnElement;
}, const documentation = ref("");
data() {
return { watch(
documentation: "" id,
}; (val) => {
}, if (id && id.length) {
watch: { nextTick(() => {
id: { const documentations =
immediate: true, window.bpmnInstances.bpmnElement.businessObject?.documentation;
handler: function(id) { documentation.value =
if (id && id.length) { documentations && documentations.length ? documentations[0].text : "";
this.$nextTick(() => {
const documentations = window.bpmnInstances.bpmnElement.businessObject?.documentation;
this.documentation = documentations && documentations.length ? documentations[0].text : "";
});
} else {
this.documentation = "";
}
}
}
},
methods: {
updateDocumentation() {
(this.bpmnElement && this.bpmnElement.id === this.id) || (this.bpmnElement = window.bpmnInstances.elementRegistry.get(this.id));
const documentation = window.bpmnInstances.bpmnFactory.create("bpmn:Documentation", { text: this.documentation });
window.bpmnInstances.modeling.updateProperties(this.bpmnElement, {
documentation: [documentation]
}); });
} else {
documentation.value = "";
} }
}, },
beforeDestroy() { {
this.bpmnElement = null; immediate: true,
} }
}; );
function updateDocumentation() {
(bpmnElement && bpmnElement.id === this.id) ||
(bpmnElement = window.bpmnInstances.elementRegistry.get(this.id));
const documentation = window.bpmnInstances.bpmnFactory.create(
"bpmn:Documentation",
{ text: this.documentation }
);
window.bpmnInstances.modeling.updateProperties(bpmnElement, {
documentation: [documentation],
});
}
onBeforeUnmount(() => {
bpmnElement = null;
});
</script> </script>

View File

@ -1,23 +1,65 @@
<template> <template>
<div class="panel-tab__content"> <div class="panel-tab__content">
<el-table :data="elementPropertyList" size="small" max-height="240" border fit> <el-table
:data="elementPropertyList"
size="small"
max-height="240"
border
fit
>
<el-table-column label="序号" width="50px" type="index" /> <el-table-column label="序号" width="50px" type="index" />
<el-table-column label="属性名" prop="name" min-width="100px" show-overflow-tooltip /> <el-table-column
<el-table-column label="属性值" prop="value" min-width="100px" show-overflow-tooltip /> label="属性名"
prop="name"
min-width="100px"
show-overflow-tooltip
/>
<el-table-column
label="属性值"
prop="value"
min-width="100px"
show-overflow-tooltip
/>
<el-table-column label="操作" width="90px"> <el-table-column label="操作" width="90px">
<template slot-scope="{ row, $index }"> <template slot-scope="{ row, $index }">
<el-button size="small" type="text" @click="openAttributesForm(row, $index)">编辑</el-button> <el-button size="small" link @click="openAttributesForm(row, $index)"
>编辑</el-button
>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button size="small" type="text" style="color: #ff4d4f" @click="removeAttributes(row, $index)">移除</el-button> <el-button
size="small"
link
style="color: #ff4d4f"
@click="removeAttributes(row, $index)"
>移除</el-button
>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<div class="element-drawer__button"> <div class="element-drawer__button">
<el-button size="small" type="primary" icon="el-icon-plus" @click="openAttributesForm(null, -1)">添加属性</el-button> <el-button
size="small"
type="primary"
icon="el-icon-plus"
@click="openAttributesForm(null, -1)"
>添加属性</el-button
>
</div> </div>
<el-dialog v-model="propertyFormModelVisible" title="属性配置" width="600px" append-to-body destroy-on-close> <el-dialog
<el-form :model="propertyForm" label-width="80px" size="small" ref="attributeFormRef" @submit.native.prevent> v-model="propertyFormModelVisible"
title="属性配置"
width="600px"
append-to-body
destroy-on-close
>
<el-form
:model="propertyForm"
label-width="80px"
size="small"
ref="attributeFormRef"
@submit.native.prevent
>
<el-form-item label="属性名:" prop="name"> <el-form-item label="属性名:" prop="name">
<el-input v-model="propertyForm.name" clearable /> <el-input v-model="propertyForm.name" clearable />
</el-form-item> </el-form-item>
@ -25,110 +67,139 @@
<el-input v-model="propertyForm.value" clearable /> <el-input v-model="propertyForm.value" clearable />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template slot="footer"> <template #footer>
<el-button size="small" @click="propertyFormModelVisible = false"> </el-button> <el-button size="small" @click="propertyFormModelVisible = false"
<el-button size="small" type="primary" @click="saveAttribute"> </el-button> > </el-button
>
<el-button size="small" type="primary" @click="saveAttribute"
> </el-button
>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup name="ElementProperties">
import { ElMessageBox } from "element-plus";
import { toRefs } from "vue";
<script> const props = defineProps({
export default { id: String,
name: "ElementProperties", type: String,
props: { });
id: String, const { id, type } = toRefs(props);
type: String const prefix = inject("prefix");
}, const width = inject("width");
inject: { const data = reactive({
prefix: "prefix", propertyForm: {},
width: "width" });
}, const { propertyForm } = toRefs(data);
data() {
return {
elementPropertyList: [],
propertyForm: {},
editingPropertyIndex: -1,
propertyFormModelVisible: false
};
},
watch: {
id: {
immediate: true,
handler(val) {
val && val.length && this.resetAttributesList();
}
}
},
methods: {
resetAttributesList() {
this.bpmnElement = window.bpmnInstances.bpmnElement;
this.otherExtensionList = []; // 其他扩展配置
this.bpmnElementProperties =
this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => {
if (ex.$type !== `${this.prefix}:Properties`) {
this.otherExtensionList.push(ex);
}
return ex.$type === `${this.prefix}:Properties`;
}) ?? [];
// 保存所有的 扩展属性字段 let bpmnElement;
this.bpmnElementPropertyList = this.bpmnElementProperties.reduce((pre, current) => pre.concat(current.values), []); let otherExtensionList;
// 复制 显示 let bpmnElementPropertyList;
this.elementPropertyList = JSON.parse(JSON.stringify(this.bpmnElementPropertyList ?? [])); let bpmnElementProperties;
}, const elementPropertyList = ref([]);
openAttributesForm(attr, index) { const editingPropertyIndex = ref(-1);
this.editingPropertyIndex = index; const propertyFormModelVisible = ref(false);
this.propertyForm = index === -1 ? {} : JSON.parse(JSON.stringify(attr));
this.propertyFormModelVisible = true; watch(
this.$nextTick(() => { id,
if (this.$refs["attributeFormRef"]) this.$refs["attributeFormRef"].clearValidate(); (val) => {
}); val && val.length && resetAttributesList();
}, },
removeAttributes(attr, index) { { immediate: true }
this.$confirm("确认移除该属性吗?", "提示", { );
confirmButtonText: "确 认",
cancelButtonText: "取 消" function resetAttributesList() {
}) bpmnElement = window.bpmnInstances.bpmnElement;
.then(() => { otherExtensionList = []; // 其他扩展配置
this.elementPropertyList.splice(index, 1); bpmnElementProperties =
this.bpmnElementPropertyList.splice(index, 1); bpmnElement.businessObject?.extensionElements?.values?.filter((ex) => {
// 新建一个属性字段的保存列表 if (ex.$type !== `${prefix}:Properties`) {
const propertiesObject = window.bpmnInstances.moddle.create(`${this.prefix}:Properties`, { otherExtensionList.push(ex);
values: this.bpmnElementPropertyList
});
this.updateElementExtensions(propertiesObject);
this.resetAttributesList();
})
.catch(() => console.info("操作取消"));
},
saveAttribute() {
const { name, value } = this.propertyForm;
if (this.editingPropertyIndex !== -1) {
window.bpmnInstances.modeling.updateModdleProperties(this.bpmnElement, this.bpmnElementPropertyList[this.editingPropertyIndex], {
name,
value
});
} else {
// 新建属性字段
const newPropertyObject = window.bpmnInstances.moddle.create(`${this.prefix}:Property`, { name, value });
// 新建一个属性字段的保存列表
const propertiesObject = window.bpmnInstances.moddle.create(`${this.prefix}:Properties`, {
values: this.bpmnElementPropertyList.concat([newPropertyObject])
});
this.updateElementExtensions(propertiesObject);
} }
this.propertyFormModelVisible = false; return ex.$type === `${prefix}:Properties`;
this.resetAttributesList(); }) ?? [];
},
updateElementExtensions(properties) { // 保存所有的 扩展属性字段
const extensions = window.bpmnInstances.moddle.create("bpmn:ExtensionElements", { bpmnElementPropertyList = bpmnElementProperties.reduce(
values: this.otherExtensionList.concat([properties]) (pre, current) => pre.concat(current.values),
}); []
window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { );
extensionElements: extensions // 复制 显示
}); elementPropertyList.value = JSON.parse(
} JSON.stringify(bpmnElementPropertyList ?? [])
);
}
const attributeFormRef = ref();
function openAttributesForm(attr, index) {
editingPropertyIndex.value = index;
propertyForm.value = index === -1 ? {} : JSON.parse(JSON.stringify(attr));
propertyFormModelVisible.value = true;
nextTick(() => {
if (attributeFormRef.value) attributeFormRef.value.clearValidate();
});
}
function removeAttributes(attr, index) {
ElMessageBox.confirm("确认移除该属性吗?", "提示", {
confirmButtonText: "确 认",
cancelButtonText: "取 消",
})
.then(() => {
elementPropertyList.value.splice(index, 1);
bpmnElementPropertyList.splice(index, 1);
// 新建一个属性字段的保存列表
const propertiesObject = window.bpmnInstances.moddle.create(
`${prefix}:Properties`,
{
values: bpmnElementPropertyList,
}
);
updateElementExtensions(propertiesObject);
resetAttributesList();
})
.catch(() => console.info("操作取消"));
}
function saveAttribute() {
const { name, value } = propertyForm.value;
if (editingPropertyIndex.value !== -1) {
window.bpmnInstances.modeling.updateModdleProperties(
bpmnElement,
bpmnElementPropertyList[editingPropertyIndex.value],
{
name,
value,
}
);
} else {
// 新建属性字段
const newPropertyObject = window.bpmnInstances.moddle.create(
`${prefix}:Property`,
{ name, value }
);
// 新建一个属性字段的保存列表
const propertiesObject = window.bpmnInstances.moddle.create(
`${prefix}:Properties`,
{
values: bpmnElementPropertyList.concat([newPropertyObject]),
}
);
updateElementExtensions(propertiesObject);
} }
}; propertyFormModelVisible.value = false;
resetAttributesList();
}
function updateElementExtensions(properties) {
const extensions = window.bpmnInstances.moddle.create(
"bpmn:ExtensionElements",
{
values: otherExtensionList.concat([properties]),
}
);
window.bpmnInstances.modeling.updateProperties(bpmnElement, {
extensionElements: extensions,
});
}
</script> </script>

View File

@ -8,7 +8,7 @@
<el-button <el-button
size="small" size="small"
type="primary" type="primary"
icon="el-icon-plus" icon="plus"
@click="openModel('message')" @click="openModel('message')"
>创建新消息</el-button >创建新消息</el-button
> >
@ -39,7 +39,7 @@
<el-button <el-button
size="small" size="small"
type="primary" type="primary"
icon="el-icon-plus" icon="plus"
@click="openModel('signal')" @click="openModel('signal')"
>创建新信号</el-button >创建新信号</el-button
> >
@ -81,7 +81,7 @@
<el-input v-model="modelObjectForm.name" clearable /> <el-input v-model="modelObjectForm.name" clearable />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template slot="footer"> <template #footer>
<el-button size="small" @click="modelVisible = false"> </el-button> <el-button size="small" @click="modelVisible = false"> </el-button>
<el-button size="small" type="primary" @click="addNewObject" <el-button size="small" type="primary" @click="addNewObject"
> </el-button > </el-button
@ -90,77 +90,77 @@
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script setup name="SignalAndMassage">
export default { import { ElMessage } from "element-plus";
name: "SignalAndMassage", import { ref } from "vue";
data() {
return { let rootElements;
signalList: [], let messageIdMap;
messageList: [], let signalIdMap;
modelVisible: false, const signalList = ref([]);
modelType: "", const messageList = ref([]);
modelObjectForm: {}, const modelVisible = ref(false);
}; const modelType = ref("");
}, const data = reactive({
computed: { modelObjectForm: {},
modelConfig() { });
if (this.modelType === "message") { const { modelObjectForm } = toRefs(data);
return { title: "创建消息", idLabel: "消息ID", nameLabel: "消息名称" };
} else { const modelConfig = computed(() => {
return { title: "创建信号", idLabel: "信号ID", nameLabel: "信号名称" }; if (modelType.value === "message") {
} return { title: "创建消息", idLabel: "消息ID", nameLabel: "消息名称" };
}, } else {
}, return { title: "创建信号", idLabel: "信号ID", nameLabel: "信号名称" };
mounted() { }
this.initDataList(); });
},
methods: { function initDataList() {
initDataList() { rootElements = window.bpmnInstances.modeler.getDefinitions().rootElements;
this.rootElements = messageIdMap = {};
window.bpmnInstances.modeler.getDefinitions().rootElements; signalIdMap = {};
this.messageIdMap = {}; messageList.value = [];
this.signalIdMap = {}; signalList.value = [];
this.messageList = []; rootElements.forEach((el) => {
this.signalList = []; if (el.$type === "bpmn:Message") {
this.rootElements.forEach((el) => { messageIdMap[el.id] = true;
if (el.$type === "bpmn:Message") { messageList.value.push({ ...el });
this.messageIdMap[el.id] = true; }
this.messageList.push({ ...el }); if (el.$type === "bpmn:Signal") {
} signalIdMap[el.id] = true;
if (el.$type === "bpmn:Signal") { signalList.value.push({ ...el });
this.signalIdMap[el.id] = true; }
this.signalList.push({ ...el }); });
} }
}); function openModel(type) {
}, modelType.value = type;
openModel(type) { modelObjectForm.value = {};
this.modelType = type; modelVisible.value = true;
this.modelObjectForm = {}; }
this.modelVisible = true; function addNewObject() {
}, if (modelType.value === "message") {
addNewObject() { if (messageIdMap[modelObjectForm.value.id]) {
if (this.modelType === "message") { return ElMessage.error("该消息已存在请修改id后重新保存");
if (this.messageIdMap[this.modelObjectForm.id]) { }
return ElMessage.error("该消息已存在请修改id后重新保存"); const messageRef = window.bpmnInstances.moddle.create(
} "bpmn:Message",
const messageRef = window.bpmnInstances.moddle.create( modelObjectForm.value
"bpmn:Message", );
this.modelObjectForm rootElements.push(messageRef);
); } else {
this.rootElements.push(messageRef); if (signalIdMap[modelObjectForm.value.id]) {
} else { return ElMessage.error("该信号已存在请修改id后重新保存");
if (this.signalIdMap[this.modelObjectForm.id]) { }
return this.$message.error("该信号已存在请修改id后重新保存"); const signalRef = window.bpmnInstances.moddle.create(
} "bpmn:Signal",
const signalRef = window.bpmnInstances.moddle.create( modelObjectForm.value
"bpmn:Signal", );
this.modelObjectForm rootElements.push(signalRef);
); }
this.rootElements.push(signalRef); modelVisible.value = false;
} initDataList();
this.modelVisible = false; }
this.initDataList();
}, onMounted(() => {
}, initDataList();
}; });
</script> </script>

View File

@ -17,7 +17,7 @@
<el-input v-model="newMessageForm.name" clearable /> <el-input v-model="newMessageForm.name" clearable />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template slot="footer"> <template #footer>
<el-button size="small" type="primary" @click="createNewMessage"> </el-button> <el-button size="small" type="primary" @click="createNewMessage"> </el-button>
</template> </template>
</el-dialog> </el-dialog>

View File

@ -8,6 +8,8 @@
overflow-y: scroll; overflow-y: scroll;
} }
.panel-tab__title { .panel-tab__title {
display: flex;
align-items: center;
font-weight: 600; font-weight: 600;
padding: 0 8px; padding: 0 8px;
font-size: 1.1em; font-size: 1.1em;
@ -107,4 +109,3 @@
} }
} }
} }

View File

@ -1,108 +1,146 @@
import axios from 'axios' import axios from "axios";
import { ElNotification , ElMessageBox, ElMessage, ElLoading } from 'element-plus' import {
import { getToken } from '@/utils/auth' ElNotification,
import errorCode from '@/utils/errorCode' ElMessageBox,
import { tansParams, blobValidate } from '@/utils/ruoyi' ElMessage,
import cache from '@/plugins/cache' ElLoading,
import { saveAs } from 'file-saver' } from "element-plus";
import useUserStore from '@/store/modules/user' import { getToken } from "@/utils/auth";
import errorCode from "@/utils/errorCode";
import { tansParams, blobValidate } from "@/utils/ruoyi";
import cache from "@/plugins/cache";
import { saveAs } from "file-saver";
import useUserStore from "@/store/modules/user";
let downloadLoadingInstance; let downloadLoadingInstance;
// 是否显示重新登录 // 是否显示重新登录
export let isRelogin = { show: false }; export let isRelogin = { show: false };
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' axios.defaults.headers["Content-Type"] = "application/json;charset=utf-8";
// 创建axios实例 // 创建axios实例
const service = axios.create({ const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分 // axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: import.meta.env.VITE_APP_BASE_API, baseURL: import.meta.env.VITE_APP_BASE_API,
// 超时 // 超时
timeout: 10000 timeout: 10000,
}) });
// request拦截器 // request拦截器
service.interceptors.request.use(config => { service.interceptors.request.use(
// 是否需要设置 token (config) => {
const isToken = (config.headers || {}).isToken === false // 是否需要设置 token
// 是否需要防止数据重复提交 const isToken = (config.headers || {}).isToken === false;
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false // 是否需要防止数据重复提交
if (getToken() && !isToken) { const isRepeatSubmit = (config.headers || {}).repeatSubmit === false;
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 if (getToken() && !isToken) {
} config.headers["Authorization"] = "Bearer " + getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params);
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime()
} }
const sessionObj = cache.session.getJSON('sessionObj') // get请求映射params参数
if (sessionObj === undefined || sessionObj === null || sessionObj === '') { if (config.method === "get" && config.params) {
cache.session.setJSON('sessionObj', requestObj) let url = config.url + "?" + tansParams(config.params);
} else { url = url.slice(0, -1);
const s_url = sessionObj.url; // 请求地址 config.params = {};
const s_data = sessionObj.data; // 请求数据 config.url = url;
const s_time = sessionObj.time; // 请求时间 }
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交 if (
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { !isRepeatSubmit &&
const message = '数据正在处理,请勿重复提交'; (config.method === "post" || config.method === "put")
console.warn(`[${s_url}]: ` + message) ) {
return Promise.reject(new Error(message)) const requestObj = {
url: config.url,
data:
typeof config.data === "object"
? JSON.stringify(config.data)
: config.data,
time: new Date().getTime(),
};
const sessionObj = cache.session.getJSON("sessionObj");
if (
sessionObj === undefined ||
sessionObj === null ||
sessionObj === ""
) {
cache.session.setJSON("sessionObj", requestObj);
} else { } else {
cache.session.setJSON('sessionObj', requestObj) const s_url = sessionObj.url; // 请求地址
const s_data = sessionObj.data; // 请求数据
const s_time = sessionObj.time; // 请求时间
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
if (
s_data === requestObj.data &&
requestObj.time - s_time < interval &&
s_url === requestObj.url
) {
const message = "数据正在处理,请勿重复提交";
console.warn(`[${s_url}]: ` + message);
return Promise.reject(new Error(message));
} else {
cache.session.setJSON("sessionObj", requestObj);
}
} }
} }
return config;
},
(error) => {
console.log(error);
Promise.reject(error);
} }
return config );
}, error => {
console.log(error)
Promise.reject(error)
})
// 响应拦截器 // 响应拦截器
service.interceptors.response.use(res => { service.interceptors.response.use(
(res) => {
// 未设置状态码则默认成功状态 // 未设置状态码则默认成功状态
const code = res.data.code || 200; const code = res.data.code || 200;
// 获取错误信息 // 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default'] const msg = errorCode[code] || res.data.msg || errorCode["default"];
// 二进制数据则直接返回 // 二进制数据则直接返回
if(res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer'){ if (
return res.data res.request.responseType === "blob" ||
res.request.responseType === "arraybuffer"
) {
return res.data;
} }
if (code === 401) { if (code === 401) {
if (!isRelogin.show) { if (!isRelogin.show) {
isRelogin.show = true; isRelogin.show = true;
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { ElMessageBox.confirm(
isRelogin.show = false; "登录状态已过期,您可以继续留在该页面,或者重新登录",
useUserStore().logOut().then(() => { "系统提示",
location.href = '/index'; {
confirmButtonText: "重新登录",
cancelButtonText: "取消",
type: "warning",
}
)
.then(() => {
isRelogin.show = false;
useUserStore()
.logOut()
.then(() => {
location.href = "/index";
});
}) })
}).catch(() => { .catch(() => {
isRelogin.show = false; isRelogin.show = false;
}); });
} }
return Promise.reject('无效的会话,或者会话已过期,请重新登录。') return Promise.reject("无效的会话,或者会话已过期,请重新登录。");
} else if (code === 500) { } else if (code === 500) {
ElMessage({ message: msg, type: 'error' }) ElMessage({ message: msg, type: "error" });
return Promise.reject(new Error(msg)) return Promise.reject(new Error(msg));
} else if (code === 601) { } else if (code === 601) {
ElMessage({ message: msg, type: 'warning' }) ElMessage({ message: msg, type: "warning" });
return Promise.reject(new Error(msg)) return Promise.reject(new Error(msg));
} else if (code !== 200) { } else if (code !== 200) {
ElNotification.error({ title: msg }) ElNotification.error({ title: msg });
return Promise.reject('error') return Promise.reject("error");
} else { } else {
return Promise.resolve(res.data) return Promise.resolve(res.data);
} }
}, },
error => { (error) => {
console.log('err' + error) console.log("err" + error);
let { message } = error; let { message } = error;
if (message == "Network Error") { if (message == "Network Error") {
message = "后端接口连接异常"; message = "后端接口连接异常";
@ -111,36 +149,47 @@ service.interceptors.response.use(res => {
} else if (message.includes("Request failed with status code")) { } else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常"; message = "系统接口" + message.substr(message.length - 3) + "异常";
} }
ElMessage({ message: message, type: 'error', duration: 5 * 1000 }) ElMessage({ message: message, type: "error", duration: 5 * 1000 });
return Promise.reject(error) return Promise.reject(error);
} }
) );
// 通用下载方法 // 通用下载方法
export function download(url, params, filename, config) { export function download(url, params, filename, config) {
downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", }) downloadLoadingInstance = ElLoading.service({
return service.post(url, params, { text: "正在下载数据,请稍候",
transformRequest: [(params) => { return tansParams(params) }], background: "rgba(0, 0, 0, 0.7)",
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, });
responseType: 'blob', return service
...config .post(url, params, {
}).then(async (data) => { transformRequest: [
const isLogin = await blobValidate(data); (params) => {
if (isLogin) { return tansParams(params);
const blob = new Blob([data]) },
saveAs(blob, filename) ],
} else { headers: { "Content-Type": "application/x-www-form-urlencoded" },
const resText = await data.text(); responseType: "blob",
const rspObj = JSON.parse(resText); ...config,
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] })
ElMessage.error(errMsg); .then(async (data) => {
} const isLogin = await blobValidate(data);
downloadLoadingInstance.close(); if (isLogin) {
}).catch((r) => { const blob = new Blob([data]);
console.error(r) saveAs(blob, filename);
ElMessage.error('下载文件出现错误,请联系管理员!') } else {
downloadLoadingInstance.close(); const resText = await data.text();
}) const rspObj = JSON.parse(resText);
const errMsg =
errorCode[rspObj.code] || rspObj.msg || errorCode["default"];
ElMessage.error(errMsg);
}
downloadLoadingInstance.close();
})
.catch((r) => {
console.error(r);
ElMessage.error("下载文件出现错误,请联系管理员!");
downloadLoadingInstance.close();
});
} }
export default service export default service;

View File

@ -100,14 +100,14 @@
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button
size="mini" size="mini"
type="text" link
icon="el-icon-view" icon="el-icon-view"
@click="handleDetail(scope.row)" @click="handleDetail(scope.row)"
>详情</el-button >详情</el-button
> >
<el-button <el-button
size="mini" size="mini"
type="text" link
icon="el-icon-edit" icon="el-icon-edit"
@click="handleUpdate(scope.row)" @click="handleUpdate(scope.row)"
v-hasPermi="['workflow:form:edit']" v-hasPermi="['workflow:form:edit']"
@ -115,7 +115,7 @@
> >
<el-button <el-button
size="mini" size="mini"
type="text" link
icon="el-icon-delete" icon="el-icon-delete"
@click="handleDelete(scope.row)" @click="handleDelete(scope.row)"
v-hasPermi="['workflow:form:remove']" v-hasPermi="['workflow:form:remove']"

View File

@ -125,7 +125,7 @@
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button link @click="handleProcessView(row)"> <el-button type="primary" link @click="handleProcessView(row)">
<span>{{ row.modelName }}</span> <span>{{ row.modelName }}</span>
</el-button> </el-button>
</template> </template>
@ -159,60 +159,62 @@
class-name="small-padding fixed-width" class-name="small-padding fixed-width"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button <div class="model-operation">
size="small" <el-button
link size="small"
icon="edit" link
@click="handleUpdate(row)" icon="edit"
v-hasPermi="['flowable:model:edit']" @click="handleUpdate(row)"
>修改</el-button v-hasPermi="['flowable:model:edit']"
> >修改</el-button
<el-button >
link <el-button
size="small" link
icon="brush" size="small"
@click="handleDesigner(row)" icon="brush"
v-hasPermi="['flowable:model:designer']" @click="handleDesigner(row)"
>设计</el-button v-hasPermi="['flowable:model:designer']"
> >设计</el-button
<el-button >
link <el-button
size="small" link
icon="video-play" size="small"
v-hasPermi="['flowable:model:deploy']" icon="video-play"
@click.native="handleDeploy(row)" v-hasPermi="['flowable:model:deploy']"
>部署</el-button @click.native="handleDeploy(row)"
> >部署</el-button
<el-dropdown> >
<span class="el-dropdown-link"> <el-dropdown>
<el-icon> <span class="el-dropdown-link">
<ArrowDown /> <el-icon>
</el-icon> <ArrowDown />
更多 </el-icon>
</span> 更多
<template #dropdown> </span>
<el-dropdown-menu> <template #dropdown>
<!-- TODO: v-hasPermi="['flowable:model:query']" --> <el-dropdown-menu>
<el-dropdown-item <!-- TODO: v-hasPermi="['flowable:model:query']" -->
icon="view" <el-dropdown-item
@click.native="handleProcessView(row)" icon="view"
>流程图</el-dropdown-item @click.native="handleProcessView(row)"
> >流程图</el-dropdown-item
<!-- TODO: v-hasPermi="['flowable:model:list']" --> >
<el-dropdown-item <!-- TODO: v-hasPermi="['flowable:model:list']" -->
icon="price-tag" <el-dropdown-item
@click.native="handleHistory(row)" icon="price-tag"
>历史</el-dropdown-item @click.native="handleHistory(row)"
> >历史</el-dropdown-item
<!-- TODO: v-hasPermi="['flowable:model:remove']" --> >
<el-dropdown-item <!-- TODO: v-hasPermi="['flowable:model:remove']" -->
icon="delete" <el-dropdown-item
@click.native="handleDelete(row)" icon="delete"
>删除</el-dropdown-item @click.native="handleDelete(row)"
> >删除</el-dropdown-item
</el-dropdown-menu> >
</template> </el-dropdown-menu>
</el-dropdown> </template>
</el-dropdown>
</div>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -323,16 +325,16 @@
<!-- 流程图 --> <!-- 流程图 -->
<el-dialog <el-dialog
:title="processView.title" :title="processView.title"
:visible.sync="processView.open" v-model="processView.open"
width="70%" width="70%"
append-to-body append-to-body
> >
<!-- TODO: --> <!-- TODO: -->
<!-- <process-viewer <process-viewer
:key="`designer-${processView.index}`" :key="`designer-${processView.index}`"
:xml="processView.xmlData" :xml="processView.xmlData"
:style="{ height: '400px' }" :style="{ height: '400px' }"
/> --> />
</el-dialog> </el-dialog>
<el-dialog title="模型历史" v-model="history.open" width="70%"> <el-dialog title="模型历史" v-model="history.open" width="70%">
@ -452,10 +454,11 @@ import {
import { listCategory } from "@/api/flowable/category"; import { listCategory } from "@/api/flowable/category";
import { ArrowDown } from "@element-plus/icons-vue"; import { ArrowDown } from "@element-plus/icons-vue";
import ProcessDesigner from "@/components/ProcessDesigner"; import ProcessDesigner from "@/components/ProcessDesigner";
// import ProcessViewer from "@/components/ProcessViewer"; import ProcessViewer from "@/components/ProcessViewer";
import { getToken } from "@/utils/auth"; import { getToken } from "@/utils/auth";
import { toRefs } from "vue"; import { toRefs } from "vue";
import { ElMessage } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
let bpmnXml;
// 显示搜索条件 // 显示搜索条件
const showSearch = ref(true); const showSearch = ref(true);
// 遮罩层 // 遮罩层
@ -598,12 +601,12 @@ function handleSelectionChange(selection) {
} }
/** 部署流程 */ /** 部署流程 */
function handleDeploy(row) { function handleDeploy(row) {
this.loading = true; loading.value = true;
deployModel({ deployModel({
modelId: row.modelId, modelId: row.modelId,
}) })
.then((response) => { .then((response) => {
this.$modal.msgSuccess(response.msg); ElMessage.success(response.msg);
let obj = { name: "Deploy", path: "/workflow/deploy" }; let obj = { name: "Deploy", path: "/workflow/deploy" };
return this.$store return this.$store
.dispatch("tagsView/delCachedView", obj) .dispatch("tagsView/delCachedView", obj)
@ -613,36 +616,36 @@ function handleDeploy(row) {
}); });
}) })
.finally(() => { .finally(() => {
this.loading = false; loading.value = false;
}); });
} }
/** 查看流程图 */ /** 查看流程图 */
function handleProcessView(row) { function handleProcessView(row) {
let modelId = row.modelId; let modelId = row.modelId;
this.processView.title = "流程图"; processView.value.title = "流程图";
this.processView.index = modelId; processView.value.index = modelId;
// 发送请求获取xml // 发送请求获取xml
getBpmnXml(modelId).then((response) => { getBpmnXml(modelId).then((response) => {
this.processView.xmlData = response.data; processView.value.xmlData = response.data;
}); });
this.processView.open = true; processView.value.open = true;
} }
function getHistoryList() { function getHistoryList() {
this.history.loading = true; history.value.loading = true;
historyModel(this.queryHistoryParams).then((response) => { historyModel(queryHistoryParams.value).then((response) => {
this.historyTotal = response.total; historyTotal.value = response.total;
this.historyList = response.rows; historyList.value = response.rows;
this.history.loading = false; history.value.loading = false;
}); });
} }
function handleHistory(row) { function handleHistory(row) {
this.history.open = true; history.value.open = true;
this.queryHistoryParams.modelKey = row.modelKey; queryHistoryParams.value.modelKey = row.modelKey;
this.getHistoryList(); getHistoryList();
} }
/** 设为最新版 */ /** 设为最新版 */
function handleLatest(row) { function handleLatest(row) {
this.$modal.confirm("是否确认将此版本设为最新?").then(() => { ElMessageBox.confirm("是否确认将此版本设为最新?").then(() => {
this.history.loading = true; this.history.loading = true;
latestModel({ latestModel({
modelId: row.modelId, modelId: row.modelId,
@ -650,7 +653,7 @@ function handleLatest(row) {
.then((response) => { .then((response) => {
this.history.open = false; this.history.open = false;
this.getList(); this.getList();
this.$modal.msgSuccess(response.msg); ElMessage.success(response.msg);
}) })
.finally(() => { .finally(() => {
this.history.loading = false; this.history.loading = false;
@ -719,59 +722,58 @@ function handleDesigner(row) {
}); });
} }
} }
function onSaveDesigner(bpmnXml) { function onSaveDesigner(bpmnXmlArgv) {
this.bpmnXml = bpmnXml; // bpmnXml = bpmnXmlArgv;
let dataBody = { let dataBody = {
modelId: this.designerData.modelId, modelId: designerData.value.modelId,
bpmnXml: this.bpmnXml, bpmnXml: bpmnXmlArgv,
}; };
this.$confirm("是否将此模型保存为新版本?", "提示", { ElMessageBox.confirm("是否将此模型保存为新版本?", "提示", {
distinguishCancelAndClose: true, distinguishCancelAndClose: true,
confirmButtonText: "是", confirmButtonText: "是",
cancelButtonText: "否", cancelButtonText: "否",
}) })
.then(() => { .then(() => {
this.confirmSave(dataBody, true); confirmSave(dataBody, true);
}) })
.catch((action) => { .catch((action) => {
if (action === "cancel") { if (action === "cancel") {
this.confirmSave(dataBody, false); confirmSave(dataBody, false);
} }
}); });
} }
function confirmSave(body, newVersion) { function confirmSave(body, newVersion) {
this.designerData.loading = true; designerData.value.loading = true;
saveModel( saveModel(
Object.assign(body, { Object.assign(body, {
newVersion: newVersion, newVersion: newVersion,
}) })
) )
.then((res) => { .then((res) => {
this.$modal.msgSuccess(res.msg); ElMessage.success(res.msg);
this.designerOpen = false; designerOpen.value = false;
this.getList(); getList();
}) })
.catch((res) => { .catch((res) => {
this.$modal.msgError(res.msg); ElMessage.error(res.msg);
this.designerData.loading = false; designerData.value.loading = false;
}); });
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
function handleDelete(row) { function handleDelete(row) {
const modelIds = row.modelId || this.ids; const modelIds = row.modelId || ids.value;
this.$modal ElMessageBox.confirm('是否确认删除模型编号为"' + modelIds + '"的数据项?')
.confirm('是否确认删除模型编号为"' + modelIds + '"的数据项?')
.then(() => { .then(() => {
this.loading = true; loading.value = true;
return delModel(modelIds); return delModel(modelIds);
}) })
.then(() => { .then(() => {
this.loading = false; loading.value = false;
this.getList(); getList();
this.$modal.msgSuccess("删除成功"); ElMessage.success("删除成功");
}) })
.finally(() => { .finally(() => {
this.loading = false; loading.value = false;
}); });
} }
/** 导出按钮操作 */ /** 导出按钮操作 */
@ -821,6 +823,12 @@ function submitSave() {
getList(); getList();
getCategoryList(); getCategoryList();
</script> </script>
<style scoped>
.model-operation {
display: flex;
align-items: center;
}
</style>
<!-- <!--
<script> <script>