From 844bf54fc403cef99f901ee6a9a3c57d7c840036 Mon Sep 17 00:00:00 2001 From: lyh <liuyuanheng@xalxzn.com> Date: 星期一, 24 二月 2025 10:06:52 +0800 Subject: [PATCH] 新增flowable --- src/views/flowable/components/HistoricDetail.vue | 343 +++++++++ src/views/flowable/components/ActHandleBtn.vue | 235 ++++++ src/views/flowable/test_demo/modules/TestDemoModal.vue | 60 + src/views/flowable/mixin/FlowableMixin.js | 68 + src/views/flowable/api/todo.js | 65 + src/views/flowable/components/ActHistoricDetailBtn.vue | 53 + src/views/flowable/test_demo/TestDemoList.vue | 311 ++++++++ src/views/flowable/test_demo/modules/TestDemoForm.vue | 172 ++++ src/views/flowable/components/ActCancelBtn.vue | 74 ++ src/views/flowable/api/process.js | 34 src/views/flowable/api/definition.js | 134 +++ src/views/flowable/components/ActApplyBtn.vue | 73 ++ src/main.js | 3 package.json | 2 src/views/flowable/api/finished.js | 37 + src/views/flowable/modeler/modelerDesign.vue | 400 +++++++++++ 16 files changed, 2,063 insertions(+), 1 deletions(-) diff --git a/package.json b/package.json index e15155c..ee25293 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "ant-design-vue": "^1.7.2", "axios": "^0.18.0", "china-area-data": "^5.0.1", + "element-ui": "^2.15.14", "clipboard": "^2.0.4", "codemirror": "^5.46.0", "cron-parser": "^2.10.0", @@ -51,6 +52,7 @@ "vuex": "^3.1.0", "vxe-table": "2.9.13", "vxe-table-plugin-antd": "1.8.10", + "workflow-bpmn-modeler": "^0.2.8", "xe-utils": "2.4.8", "xss": "^1.0.13" }, diff --git a/src/main.js b/src/main.js index 5ce1354..29a973e 100644 --- a/src/main.js +++ b/src/main.js @@ -50,7 +50,8 @@ //琛ㄥ崟楠岃瘉 import { rules } from '@/utils/rules' import * as echarts from 'echarts' - +import ElementUI from 'element-ui'; +Vue.use(ElementUI); // 灏嗚嚜鍔ㄦ敞鍐屾墍鏈夌粍浠朵负鍏ㄥ眬缁勪欢 import dataV from '@jiaminghi/data-view' Vue.use(dataV) diff --git a/src/views/flowable/api/definition.js b/src/views/flowable/api/definition.js new file mode 100644 index 0000000..918fdb2 --- /dev/null +++ b/src/views/flowable/api/definition.js @@ -0,0 +1,134 @@ +import {axios as request} from '@/utils/request' + +// 鏌ヨ娴佺▼瀹氫箟鍒楄〃 +export function listDefinition(query) { + return request({ + url: '/flowable/definition/list', + method: 'get', + params: query + }) +} + +// 閮ㄧ讲娴佺▼瀹炰緥 +export function definitionStartByDefId(procDefId,data) { + return request({ + url: '/flowable/definition/startByProcDefId/' + procDefId, + method: 'post', + data: JSON.stringify(data) + }) +} +// 閮ㄧ讲娴佺▼瀹炰緥 +export function definitionStartByDefKey(procDefKey,data) { + return request({ + url: '/flowable/definition/startByProcDefKey/' + procDefKey, + method: 'post', + data: JSON.stringify(data) + }) +} +// 閮ㄧ讲娴佺▼瀹炰緥 +export function definitionStartByDataId(dataId,data) { + return request({ + url: '/flowable/definition/startByDataId/' + dataId, + method: 'post', + data: data + }) +} + +// 鑾峰彇娴佺▼鍙橀噺 +export function getProcessVariables(taskId) { + return request({ + url: '/flowable/task/processVariables/' + taskId, + method: 'get' + }) +} + +// 婵�娲�/鎸傝捣娴佺▼ +export function updateState(params) { + return request({ + url: '/flowable/definition/updateState', + method: 'put', + params: params + }) +} + +// 鎸囧畾娴佺▼鍔炵悊浜哄憳鍒楄〃 +export function userList(query) { + return request({ + url: '/flowable/definition/userList', + method: 'get', + params: query + }) +} + +// 鎸囧畾娴佺▼鍔炵悊缁勫垪琛� +export function roleList(query) { + return request({ + url: '/flowable/definition/roleList', + method: 'get', + params: query + }) +} +// 鎸囧畾娴佺▼鍒嗙被鍒楄〃 +export function categoryList(query) { + return request({ + url: '/flowable/definition/categoryList', + method: 'get', + params: query + }) +} + +// 璇诲彇xml鏂囦欢 +export function readXml(deployId) { + return request({ + url: '/flowable/definition/readXml/' + deployId, + method: 'get' + }) +} +// 璇诲彇xml鏂囦欢 +export function readXmlByDataId(dataId) { + return request({ + url: '/flowable/definition/readXmlByDataId/' + dataId, + method: 'get' + }) +} +// 璇诲彇image鏂囦欢 +export function readImage(deployId) { + return request({ + url: '/flowable/definition/readImage/' + deployId, + method: 'get' + }) +} + +// 璇诲彇image鏂囦欢 +export function getFlowViewer(procInsId) { + return request({ + url: '/flowable/task/flowViewer/' + procInsId, + method: 'get' + }) +} +// 璇诲彇image鏂囦欢 +export function getFlowViewerByDataId(dataId) { + return request({ + url: '/flowable/task/flowViewerByDataId/' + dataId, + method: 'get' + }) +} + +// 璇诲彇xml鏂囦欢 +export function saveXml(data) { + return request({ + url: '/flowable/definition/save', + method: 'post', + data: data + }) +} + +// 鍒犻櫎娴佺▼瀹氫箟 +export function delDeployment(query) { + return request({ + url: '/flowable/definition/delete/', + method: 'delete', + params: query + }) +} + diff --git a/src/views/flowable/api/finished.js b/src/views/flowable/api/finished.js new file mode 100644 index 0000000..bbab6da --- /dev/null +++ b/src/views/flowable/api/finished.js @@ -0,0 +1,37 @@ +import {axios as request} from '@/utils/request' + +// 鏌ヨ宸插姙浠诲姟鍒楄〃 +export function finishedList(query) { + return request({ + url: '/flowable/task/finishedList', + method: 'get', + params: query + }) +} + +// 浠诲姟娴佽浆璁板綍 +export function flowRecord(query) { + return request({ + url: '/flowable/task/flowRecord', + method: 'get', + params: query + }) +} + +// 鎾ゅ洖浠诲姟 +export function revokeProcess(data) { + return request({ + url: '/flowable/task/revokeProcess', + method: 'post', + data: data + }) +} + +// 閮ㄧ讲娴佺▼瀹炰緥 +export function deployStart(deployId) { + return request({ + url: '/flowable/process/startFlow/' + deployId, + method: 'get', + }) +} + diff --git a/src/views/flowable/api/process.js b/src/views/flowable/api/process.js new file mode 100644 index 0000000..87a3346 --- /dev/null +++ b/src/views/flowable/api/process.js @@ -0,0 +1,34 @@ +import {axios as request} from '@/utils/request' + +// 鎴戠殑鍙戣捣鐨勬祦绋� +export function myProcessList(query) { + return request({ + url: '/flowable/task/myProcess', + method: 'get', + params: query + }) +} + + +// 鍙栨秷鐢宠 +export function deleteByDataId(dataId,deleteReason) { + const data = { + dataId:dataId, + deleteReason:deleteReason + } + return request({ + url: '/flowable/instance/deleteByDataId', + method: 'post', + params: data + }) +} + + +// 閮ㄧ讲娴佺▼瀹炰緥 +export function deployStart(deployId) { + return request({ + url: '/flowable/process/startFlow/' + deployId, + method: 'get', + }) +} + diff --git a/src/views/flowable/api/todo.js b/src/views/flowable/api/todo.js new file mode 100644 index 0000000..107bb4a --- /dev/null +++ b/src/views/flowable/api/todo.js @@ -0,0 +1,65 @@ +import {axios as request} from '@/utils/request' + +// 鏌ヨ寰呭姙浠诲姟鍒楄〃 +export function todoList(query) { + return request({ + url: '/flowable/task/todoList', + method: 'get', + params: query + }) +} + +// 瀹屾垚浠诲姟 +export function completeTask(data) { + return request({ + url: '/flowable/task/completeByDateId', + method: 'post', + data: data + }) +} + +// 濮旀淳浠诲姟 +export function delegate(data) { + return request({ + url: '/flowable/task/delegate', + method: 'post', + data: data + }) +} + +// 閫�鍥炰换鍔� +export function returnTask(data) { + return request({ + url: '/flowable/task/taskReturnByDataId', + method: 'post', + data: data + }) +} + +// 椹冲洖浠诲姟 +export function rejectTask(data) { + return request({ + url: '/flowable/task/taskRejectByDataId', + method: 'post', + data: data + }) +} + +// 鍙��鍥炰换鍔″垪琛� +export function returnList(data) { + return request({ + url: '/flowable/task/findReturnTaskListByDataId', + method: 'post', + data: data + }) +} + +// 涓嬩竴鑺傜偣 todo 鐩墠鐩存帴鑷姩鍒嗛厤鍒板�欓�変汉锛屼笉鐢ㄤ富鍔ㄩ�変汉锛屽鏈夐渶瑕佸啀寮�鍙� +export function getNextFlowNode(data) { + return request({ + url: '/flowable/task/nextFlowNode', + method: 'post', + data: data + }) +} + diff --git a/src/views/flowable/components/ActApplyBtn.vue b/src/views/flowable/components/ActApplyBtn.vue new file mode 100644 index 0000000..7e5a39c --- /dev/null +++ b/src/views/flowable/components/ActApplyBtn.vue @@ -0,0 +1,73 @@ +<style lang="less"> +</style> +<template> + <span> + <a-button :loading="submitLoading" :type="btnType" @click="applySubmit()" >{{text}}</a-button> + </span> +</template> + +<script> + import {definitionStartByDataId} from "@views/flowable/api/definition"; + +export default { + name: 'ActApplyBtn', + components: {}, + props: { + btnType: { type: String, default: 'link', required: false }, + /**/ + dataId: { + type: String, + default: '', + required: true + }, + variables:{ + type: Object, + default: {}, + }, + text: { + type: String, + default: '鎻愪氦鐢宠', + required: false + } + + }, + data() { + return { + modalVisible: false, + submitLoading: false, + form: { + }, + }; + }, + created() { + }, + watch: { + }, + methods: { + applySubmit() { + if (this.dataId && this.dataId.length < 1) { + this.error = '蹇呴』浼犲叆鍙傛暟dataId'; + this.$message.error(this.error); + return; + } else { + this.error = ''; + } + this.submitLoading = true; + var params = Object.assign({ + dataId: this.dataId + }, this.variables); + definitionStartByDataId(this.dataId, params) + .then(res => { + if (res.success) { + this.$message.success('鎿嶄綔鎴愬姛'); + this.$emit('success'); + } else { + this.$message.error(res.message); + } + }) + .finally(() => (this.submitLoading = false)); + } + } + +}; +</script> diff --git a/src/views/flowable/components/ActCancelBtn.vue b/src/views/flowable/components/ActCancelBtn.vue new file mode 100644 index 0000000..02af66f --- /dev/null +++ b/src/views/flowable/components/ActCancelBtn.vue @@ -0,0 +1,74 @@ +<style lang="less"> +</style> +<template> + <span> + <a-button :type="btnType" @click="cancel()" >{{text}}</a-button> + <a-modal title="纭鎾ゅ洖" v-model="modalCancelVisible" :mask-closable="false" :width="500"> + <a-form ref="delForm" v-model="cancelForm" :label-width="70" v-if="modalCancelVisible"> + <a-form-item label="鎾ゅ洖鍘熷洜" prop="reason"> + <a-input type="textarea" v-model="cancelForm.reason" :rows="4" /> + </a-form-item> + </a-form> + <div slot="footer"> + <a-button type="text" @click="modalCancelVisible = false">鍙栨秷</a-button> + <a-button type="primary" :disabled="submitLoading" @click="handelSubmitCancel">鎻愪氦</a-button> + </div> + </a-modal> + </span> +</template> + +<script> +import {deleteByDataId} from "@views/flowable/api/process"; + +export default { + name: 'ActCancelBtn', + components: {}, + props: { + btnType: { type: String, default: 'link', required: false }, + /**/ + dataId: { + type: String, + default: '', + required: true + }, + text: { + type: String, + default: '鎾ゅ洖', + required: false + } + }, + data() { + return { + modalCancelVisible: false, + cancelForm: { + reason: '' + }, + submitLoading: false, + }; + }, + created() { + }, + watch: { + }, + methods: { + cancel() { + this.modalCancelVisible = true; + }, + handelSubmitCancel() { + this.submitLoading = true; + deleteByDataId(this.dataId, this.cancelForm.reason) + .then(res => { + if (res.success) { + this.$message.success('鎿嶄綔鎴愬姛'); + this.modalCancelVisible = false; + this.$emit('success'); + } else { + this.$message.error(res.message); + } + }) + .finally(() => (this.submitLoading = false)); + } + } + +}; +</script> diff --git a/src/views/flowable/components/ActHandleBtn.vue b/src/views/flowable/components/ActHandleBtn.vue new file mode 100644 index 0000000..a820ea6 --- /dev/null +++ b/src/views/flowable/components/ActHandleBtn.vue @@ -0,0 +1,235 @@ +<style lang="less"> +</style> +<template> + <span> + <a-button :type="btnType" @click="handle()" >{{text}}</a-button> + <a-modal :title="modalTaskTitle" v-model="modalTaskVisible" :mask-closable="false" :width="500"> + + <div v-if="modalTaskVisible"> + <div v-if="type==handleType.reApply"> + 纭鏃犺骞堕噸鏂版彁浜わ紵 + </div> + <a-form ref="form" :model="form" :label-width="85" > + <a-form-item v-if="type!==handleType.reApply" label="澶勭悊鎰忚" prop="reason"> + <a-input type="textarea" v-model="form.comment" :rows="4" /> + </a-form-item> + <div v-show="type==2"> + <a-form-item label="閫�鍥炶妭鐐�" prop="targetKey" v-if="returnTaskList.length"> + <a-radio-group v-model="form.targetKey" @change="targetKeyChange"> + <a-radio-button + v-for="item in returnTaskList" + :key="item.id" + :value="item.id" + >{{item.name}}</a-radio-button> + </a-radio-group> + </a-form-item> + <span v-else>鏃犲彲閫�鍥炶妭鐐癸紒</span> + </div> + <div v-if="form.targetKey !== 'start' && candidateUsers.length"> + <a-form-item label="涓嬩釜鑺傜偣瀹℃壒鍊欓�変汉"> + <a-select + mode="multiple" + v-model="candidateUsersSelecteds" + style="width: 100%" + placeholder="璇烽�夋嫨涓嬩釜鑺傜偣瀹℃壒鍊欓�変汉" + > + <a-select-option v-for="user in candidateUsers" :key="user.username" :value="user.username"> + {{user.realname}} + </a-select-option> + </a-select> + </a-form-item> + </div> + </a-form> + </div> + <div slot="footer"> + <a-button type="text" @click="modalTaskVisible=false">鍙栨秷</a-button> + <a-button type="primary" :loading="submitLoading" @click="handelSubmit">鎻愪氦</a-button> + </div> + </a-modal> + </span> +</template> + +<script> + import {completeTask, rejectTask, returnList, returnTask} from "@views/flowable/api/todo"; + +export default { + name: 'ActHandleBtn', + components: {}, + props: { + btnType: { type: String, default: 'link', required: false }, + /* handleType 0閫氳繃 1椹冲洖 2閫�鍥� */ + type: { + type: String|Number, + default: '0', + required: true + }, + dataId: { + type: String, + default: '', + required: true + }, + /*娴佺▼鍙橀噺*/ + variables:{ + type: Object, + default: ()=>{}, + }, + candidateUsers:{ + type: Array, + default: ()=>[], + }, + text: { + type: String, + default: '澶勭悊', + required: false + } + }, + data() { + return { + handleType:{ + // 閫氳繃 + pass: 0, + // 椹冲洖 + back: 1, + // 閫�鍥� + return: 2, + // 閲嶆柊鎻愪氦 + reApply: 3 + }, + returnTaskList: [], + candidateUsersSelecteds:[], + modalTaskVisible: false, + submitLoading: false, + form: { + comment:'', + targetKey:'' + }, + modalTaskTitle: '', + + }; + }, + created() { + }, + watch: { + }, + methods: { + handle() { + this.form.comment = '' + this.candidateUsersSelecteds = [] + if (this.type === this.handleType.delegate) { + // this.delegateTask(); + } else if (this.type === this.handleType.pass) { + this.passTask(); + } else if (this.type === this.handleType.back) { + this.backTask(); + } else if(this.type === this.handleType.return){ + this.returnTask(); + } else if(this.type === this.handleType.reApply){ + this.reApply(); + } + else { + this.$message.warn('鏈煡绫诲瀷type锛屽弬瑙� handleType'); + } + }, + reApply() { + const v = this; + this.modalTaskTitle = '纭閲嶆柊鎻愪氦'; + this.modalTaskVisible = true; + }, + passTask() { + const v = this; + this.modalTaskTitle = '瀹℃壒閫氳繃'; + this.modalTaskVisible = true; + }, + backTask() { + const v = this; + this.modalTaskTitle = '瀹℃壒椹冲洖'; + this.modalTaskVisible = true; + }, + returnTask() { + const v = this; + this.modalTaskTitle = '瀹℃壒閫�鍥�'; + this.modalTaskVisible = true; + returnList({dataId:this.dataId}).then(res => { + this.returnTaskList = res.result||[]; + // console.log(this.returnTaskList) + }) + }, + + handelSubmit() { + console.log('鎻愪氦'); + this.submitLoading = true; + var formData = Object.assign({ + dataId:this.dataId, + candidateUsers:this.candidateUsersSelecteds, + values:Object.assign({dataId:this.dataId},this.variables) + }, this.form); + if (this.type==this.handleType.reApply){ + formData.comment = '閲嶆柊鎻愪氦' + } + if (!formData.comment){ + this.$message.error('璇疯緭鍏ュ鎵规剰瑙侊紒'); + this.submitLoading=false + return; + } + // 鏈変笅涓妭鐐瑰鎵逛汉閫夋嫨锛屼絾鏄湭閫� + if (this.candidateUsers.length && + this.candidateUsersSelecteds.length==0 && + this.form.targetKey !== 'start' + ){ + this.$message.error('璇烽�夋嫨涓嬩釜鑺傜偣瀹℃壒浜猴紒'); + this.submitLoading=false + return; + } + if (this.type == this.handleType.reApply || this.type == this.handleType.pass) { + // 閫氳繃 + completeTask(formData).then(res => { + this.submitLoading = false; + if (res.success) { + this.$message.success('鎿嶄綔鎴愬姛'); + this.modalTaskVisible = false; + this.$emit('success'); + } else { + this.$message.error('鎿嶄綔澶辫触'); + } + }).finally(()=>{this.submitLoading=false}); + } else if (this.type == this.handleType.back) { + // 椹冲洖 + rejectTask(formData).then(res => { + this.submitLoading = false; + if (res.success) { + this.$message.success('鎿嶄綔鎴愬姛'); + this.modalTaskVisible = false; + this.$emit('success'); + } else { + this.$message.error('鎿嶄綔澶辫触'); + } + }).finally(()=>{this.submitLoading=false}); + + } else if (this.type == this.handleType.return){ + if (!formData.targetKey){ + this.$message.error('璇烽�夋嫨閫�鍥炶妭鐐癸紒'); + this.submitLoading=false + return; + } + //閫�鍥� + returnTask(formData).then(res => { + this.submitLoading = false; + if (res.success) { + this.$message.success('鎿嶄綔鎴愬姛'); + this.modalTaskVisible = false; + this.$emit('success'); + } else { + this.$message.error('鎿嶄綔澶辫触'); + } + }).finally(()=>{this.submitLoading=false}); + } + }, + + targetKeyChange() { + this.candidateUsersSelecteds = [] + this.$emit('targetKeyChange',this.form.targetKey) + } + } + +}; +</script> diff --git a/src/views/flowable/components/ActHistoricDetailBtn.vue b/src/views/flowable/components/ActHistoricDetailBtn.vue new file mode 100644 index 0000000..c4a69f5 --- /dev/null +++ b/src/views/flowable/components/ActHistoricDetailBtn.vue @@ -0,0 +1,53 @@ +<style lang="less"> +</style> +<template> + <span> + <a-button :type="btnType" @click="history()" >{{text}}</a-button> + <a-modal title="瀹℃壒鍘嗗彶" v-model="modalLsVisible" :mask-closable="true" :width="'80%'" :footer="null"> + <div v-if="modalLsVisible"> + <HistoricDetail ref="historicDetail" :data-id="dataId"></HistoricDetail> + </div> + </a-modal> + </span> +</template> + +<script> +import HistoricDetail from './HistoricDetail'; +export default { + name: 'ActHistoricDetailBtn', + components: { HistoricDetail }, + props: { + btnType: { type: String, default: 'link', required: false }, + /**/ + dataId: { + type: String, + default: '', + required: true + }, + text: { + type: String, + default: '瀹℃壒鍘嗗彶', + required: false + } + }, + data() { + return { + modalLsVisible: false + }; + }, + created() { + }, + watch: { + }, + methods: { + history() { + if (!this.dataId) { + this.$message.error('娴佺▼瀹炰緥ID涓嶅瓨鍦�'); + return; + } + this.modalLsVisible = true; + } + } + +}; +</script> diff --git a/src/views/flowable/components/HistoricDetail.vue b/src/views/flowable/components/HistoricDetail.vue new file mode 100644 index 0000000..be707cc --- /dev/null +++ b/src/views/flowable/components/HistoricDetail.vue @@ -0,0 +1,343 @@ +<style lang="less"> +</style> +<template> + <div class="search"> + <a-card> + <p slot="title"> + <span>娴佺▼鍥�</span> + </p> + <div :style="{height: svgHeight}" v-if="svgShow"> + <bpmnModeler class="svg" ref="bpm" :xml="xmlData" :is-view="true"></bpmnModeler> + </div> + </a-card> + <a-card style="margin-top:10px;"> + <p slot="title"> + <span>娴佺▼瀹℃壒杩涘害鍘嗗彶</span> + </p> + <a-row style="position:relative"> + <div class="block"> + <a-timeline> + <a-timeline-item + v-for="(item,index ) in flowRecordList" + :key="index" + :color="setColor(item.finishTime)" + > + <p style="font-weight: 700;">{{item.taskName}} + <i v-if="!item.finishTime" style="color: orange">(寰呭姙涓�傘�傘��)</i> + </p> + + <a-card :body-style="{ padding: '10px' }"> + <label v-if="item.assigneeName&&item.finishTime" style="font-weight: normal;margin-right: 30px;">瀹為檯鍔炵悊浜猴細 {{item.assigneeName}} <a-tag type="info" size="mini">{{item.deptName}}</a-tag></label> + <label v-if="item.candidate" style="font-weight: normal;margin-right: 30px;">鍊欓�夊姙鐞嗕汉锛� {{item.candidate}}</label> + <label style="font-weight: normal">鎺ユ敹鏃堕棿锛� </label><label style="color:#8a909c;font-weight: normal">{{item.createTime}}</label> + <label v-if="item.finishTime" style="margin-left: 30px;font-weight: normal">鍔炵粨鏃堕棿锛� </label><label style="color:#8a909c;font-weight: normal">{{item.finishTime}}</label> + <label v-if="item.duration" style="margin-left: 30px;font-weight: normal">鑰楁椂锛� </label><label style="color:#8a909c;font-weight: normal">{{item.duration}}</label> + + <p v-if="item.comment"> +<!-- 1 姝e父鎰忚 2 閫�鍥炴剰瑙� 3 椹冲洖鎰忚 --> + <a-tag color="green" v-if="item.comment.type === '1'"> + <span v-if="item.comment.comment!='閲嶆柊鎻愪氦'">閫氳繃锛�</span> + {{item.comment.comment}} + </a-tag> + <a-tag color="orange" v-if="item.comment.type === '2'">閫�鍥烇細 {{item.comment.comment}}</a-tag> + <a-tag color="red" v-if="item.comment.type === '3'">椹冲洖锛� {{item.comment.comment}}</a-tag> + </p> + </a-card> + </a-timeline-item> + </a-timeline> + </div> + </a-row> + </a-card> + </div> +</template> + +<script> +import {flowRecord} from "@views/flowable/api/finished"; +import {getFlowViewerByDataId, readXmlByDataId} from "@views/flowable/api/definition"; +import bpmnModeler from "workflow-bpmn-modeler"; + +export default { + name: 'HistoricDetail', + components: { + bpmnModeler, + }, + props: { + /**/ + dataId: { + type: String, + default: '', + required: true + }, + + }, + data() { + return { + taskList:[], + flowRecordList: [], // 娴佺▼娴佽浆鏁版嵁 + formData:{}, + xmlData:'', + type: 0, + loading: false, // 琛ㄥ崟鍔犺浇鐘舵�� + loadingImg: false, + data: [], + id: '', + imgUrl: '', + backRoute: '', + svgHeight:'', + svgShow: true + }; + }, + created() { + this.init(); + }, + watch: { + dataId: function(newval, oldName) { + this.init(); + } + }, + + methods: { + init() { + this.getFlowRecordList() + this.getModelDetail() + }, + /** xml 鏂囦欢 */ + getModelDetail() { + // 鍙戦�佽姹傦紝鑾峰彇xml + readXmlByDataId(this.dataId).then(res => { + this.xmlData = res.result + this.getFlowViewer() + setTimeout(()=>{ + this.fitViewport() + }) + }) + }, + // 娴佺▼杩涜鎯呭喌 + getFlowViewer() { + getFlowViewerByDataId(this.dataId).then(res => { + this.taskList = res.result || [] + this.fillColor(); + }) + }, + /** 娴佺▼娴佽浆璁板綍 */ + getFlowRecordList() { + const params = {dataId: this.dataId} + flowRecord(params).then(res => { + // console.log(res) + this.flowRecordList = res.result.flowList; + this.finishOrder() + // 娴佺▼杩囩▼涓笉瀛樺湪鍒濆鍖栬〃鍗� 鐩存帴璇诲彇鐨勬祦绋嬪彉閲忎腑瀛樺偍鐨勮〃鍗曞�� + if (res.result.formData) { + this.formData = res.result.formData; + } + }).catch(res => { + console.log(res) + }) + }, + //鏁寸悊椤哄簭锛屾妸寰呭姙鏀炬渶涓婇潰锛屽苟涓斿彧鐣欎竴涓紙涓嶇劧浼氱鏃朵細涔憋級 + finishOrder(){ + const list = [] + let noFinish = null + for (const flow of this.flowRecordList) { + if (flow.finishTime){ + // 鍔炵粨鐨勮妭鐐瑰悓鏃跺彇鏈夊疄闄呭姙鐞嗕汉鐨勶紝鍥犱负浼氱浼氬皢鎵�鏈夌殑澶氬疄渚嬮兘杩斿洖锛岄渶瑕佽繃婊� + if (flow.assigneeId){ + list.push(flow) + } + } else { + noFinish = flow + } + } + if (noFinish){ + const find = list.find(obj=>obj.taskDefKey == noFinish.taskDefKey); + if (find){ + noFinish.taskName = '銆愪細绛句腑銆�'+noFinish.taskName + } + this.flowRecordList = [noFinish,...list]; + } else { + this.flowRecordList = list; + } + + }, + setColor(val) { + if (val) { + return "#2bc418"; + } else { + return "#b3bdbb"; + } + }, + fillColor() { + const modeler = this.$refs.bpm.modeler; + const canvas = modeler.get('canvas') + modeler._definitions.rootElements[0].flowElements.forEach(n => { + const completeTask = this.taskList.find(m => m.key === n.id) + const todoTask = this.taskList.find(m => !m.completed) + const endTask = this.taskList[this.taskList.length - 1] + //鐢ㄦ埛浠诲姟 + if (n.$type === 'bpmn:UserTask') { + if (completeTask) { + canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo') + canvas.addMarker(n.id, completeTask.back ? 'highlight-back' : 'highlight-noback') + n.outgoing.forEach(nn => { + const targetTask = this.taskList.find(m => m.key === nn.targetRef.id) + if (targetTask) { + if (todoTask && completeTask.key === todoTask.key && !todoTask.completed){ + canvas.addMarker(nn.id, todoTask.completed ? 'highlight' : 'highlight-todo') + canvas.addMarker(nn.targetRef.id, todoTask.completed ? 'highlight' : 'highlight-todo') + }else { + canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo') + canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' : 'highlight-todo') + } + } + }) + } + } + // 鎺掍粬缃戝叧 + else if (n.$type === 'bpmn:ExclusiveGateway') { + if (completeTask) { + canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo') + n.outgoing.forEach(nn => { + const targetTask = this.taskList.find(m => m.key === nn.targetRef.id) + if (targetTask) { + + canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo') + canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' : 'highlight-todo') + } + + }) + } + + } + // 骞惰缃戝叧 + else if (n.$type === 'bpmn:ParallelGateway') { + if (completeTask) { + canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo') + n.outgoing.forEach(nn => { + debugger + const targetTask = this.taskList.find(m => m.key === nn.targetRef.id) + if (targetTask) { + canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo') + canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' : 'highlight-todo') + } + }) + } + } + else if (n.$type === 'bpmn:StartEvent') { + n.outgoing.forEach(nn => { + const completeTask = this.taskList.find(m => m.key === nn.targetRef.id) + if (completeTask) { + canvas.addMarker(nn.id, 'highlight') + canvas.addMarker(n.id, 'highlight') + return + } + }) + } + else if (n.$type === 'bpmn:EndEvent') { + if (endTask.key === n.id && endTask.completed) { + canvas.addMarker(n.id, 'highlight') + return + } + } + }) + }, + // 璁╁浘鑳借嚜閫傚簲灞忓箷 + fitViewport() { + const modeler = this.$refs.bpm.modeler; + const canvas = modeler.get('canvas') + // this.zoom = this.modeler.get('canvas').zoom('fit-viewport') + if (this.svgHeight){ + document.querySelector('.canvas').style.height = this.svgHeight; + } + const bbox = document.querySelector('.flow-containers .viewport').getBBox() + const currentViewbox = modeler.get('canvas').viewbox() + if (!this.svgHeight){ + this.svgHeight = currentViewbox.inner.height + 'px' + this.svgShow = false + this.$nextTick(()=>{ + this.svgShow = true + }) + // this.fitViewport() + setTimeout(()=>{ + this.fitViewport() + }) + } + + const elementMid = { + x: bbox.x + bbox.width / 2 - 65, + y: bbox.y + bbox.height / 2 + } + // 璋冭妭浣嶇疆 + modeler.get('canvas').viewbox({ + x: elementMid.x - currentViewbox.width / 2 + 70, + y: elementMid.y - currentViewbox.height/2, + width: currentViewbox.width, + height: currentViewbox.height + }) + // 璋冭妭澶у皬缂╂斁 + const zoom = currentViewbox.outer.width /(currentViewbox.inner.width+200) + console.log('********',zoom,elementMid,currentViewbox.inner,currentViewbox.outer) + // modeler.get('canvas').zoom(zoom) + + + }, + } + +}; +</script> +<style lang="less"> + .highlight.djs-shape .djs-visual > :nth-child(1) { + fill: green !important; + stroke: green !important; + fill-opacity: 0.2 !important; + } + .highlight.djs-shape .djs-visual > :nth-child(2) { + fill: green !important; + } + .highlight.djs-shape .djs-visual > path { + fill: green !important; + fill-opacity: 0.2 !important; + stroke: green !important; + } + .highlight.djs-connection > .djs-visual > path { + stroke: green !important; + } + // .djs-connection > .djs-visual > path { + // stroke: orange !important; + // stroke-dasharray: 4px !important; + // fill-opacity: 0.2 !important; + // } + // .djs-shape .djs-visual > :nth-child(1) { + // fill: orange !important; + // stroke: orange !important; + // stroke-dasharray: 4px !important; + // fill-opacity: 0.2 !important; + // } + .highlight-todo.djs-connection > .djs-visual > path { + stroke: orange !important; + stroke-dasharray: 4px !important; + fill-opacity: 0.2 !important; + } + .highlight-todo.djs-shape .djs-visual > :nth-child(1) { + fill: orange !important; + stroke: orange !important; + stroke-dasharray: 4px !important; + fill-opacity: 0.2 !important; + } + .highlight-back.djs-connection > .djs-visual > path { + stroke: red !important; + stroke-dasharray: 4px !important; + fill-opacity: 0.2 !important; + } + .highlight-back.djs-shape .djs-visual > :nth-child(1) { + fill: red !important; + stroke: red !important; + stroke-dasharray: 4px !important; + fill-opacity: 0.2 !important; + } + .overlays-div { + font-size: 10px; + color: red; + width: 100px; + top: -20px !important; + } +</style> \ No newline at end of file diff --git a/src/views/flowable/mixin/FlowableMixin.js b/src/views/flowable/mixin/FlowableMixin.js new file mode 100644 index 0000000..50a76a4 --- /dev/null +++ b/src/views/flowable/mixin/FlowableMixin.js @@ -0,0 +1,68 @@ +import Vue from "vue"; +import {USER_INFO} from "@/store/mutation-types"; + +/** + * + */ + +export const FlowableMixin = { + data(){ + return { + loginUser:{} + } + }, + created() { + this.loginUser = Vue.ls.get(USER_INFO); + }, + methods:{ + // 褰撳墠鏁版嵁鏄惁鍙彁浜� + isCanApply(row){ + // 娌℃湁娴佺▼瀹炰緥鐨勫嵆鍙彁浜� + return !Boolean(row.processInstanceId); + }, + // 褰撳墠鏁版嵁鏄惁鍙挙鍥� + isCanRecall(row){ + // 杩涜涓殑娴佺▼&&褰撳墠鑺傜偣涓嶆槸寮�濮嬭妭鐐�&&鐘舵�佷笉鏄�氳繃 + return Boolean(row.processInstanceId)&&row.taskNameId!=='start'&&row.actStatus!=='瀹℃壒閫氳繃'; + }, + // 閲嶆柊鎻愪氦鎸夐挳 + isCanReApply(row){ + return row.taskNameId=='start'&&this.isTodoUsers(row); + }, + // 閫氳繃鎸夐挳 + isCanPass(row){ + return row.taskNameId!=='start'&& this.isTodoUsers(row); + }, + // 椹冲洖閫�鍥炴寜閽� + isCanBacke(row){ + // 涓嶆槸start鑺傜偣&&鍦ㄥ彲鎿嶄綔浜哄憳鍒楄〃 + return row.taskNameId!=='start'&&this.isTodoUsers(row); + }, + // 鏌ョ湅瀹℃壒鍘嗗彶鎸夐挳 + isCanHistoric(row){ + // 鏈夊疄渚媔d灏辫兘鏌ョ湅 + return Boolean(row.processInstanceId); + }, + // 褰撳墠鐧诲綍浜烘槸鍚﹀湪澶勭悊浜哄垪琛� + isTodoUsers(row){ + const todoUsers = row.todoUsers; + if (todoUsers&&todoUsers.length){ + const parse = JSON.parse(todoUsers)||[]; + return parse.includes(this.loginUser.username); + }else { + return false; + } + }, + // 褰撳墠鐧诲綍浜烘槸鍚︽槸澶勭悊杩囩殑浜哄垪琛� + isDoneUsers(row){ + const doneUsers = row.doneUsers; + if (doneUsers&&doneUsers.length){ + const parse = JSON.parse(doneUsers)||[]; + return parse.includes(this.loginUser.username); + }else { + return false; + } + }, + } + +} \ No newline at end of file diff --git a/src/views/flowable/modeler/modelerDesign.vue b/src/views/flowable/modeler/modelerDesign.vue new file mode 100644 index 0000000..a3233e3 --- /dev/null +++ b/src/views/flowable/modeler/modelerDesign.vue @@ -0,0 +1,400 @@ +<template> + <div> +<!-- ==================娴佺▼瀹氫箟鍒楄〃=============== --> + <a-card v-if="!xmlFrame.open||xmlView"> + <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px"> + <el-form-item label="娴佺▼鍚嶇О" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="璇疯緭鍏ュ悕绉�" + clearable + size="small" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item label="娴佺▼鍒嗙被" prop="category"> + <el-select @change="handleQuery" v-model="queryParams.category" placeholder="璇烽�夋嫨娴佺▼鍒嗙被" clearable prop="category"> + <el-option label="璇烽�夋嫨" value="" /> + <el-option v-for="category in categorys" :key="category.id" :label="category.name" :value="category.id" /> + </el-select> + </el-form-item> + <el-form-item label="婵�娲�" prop="active"> + <el-switch + v-model="queryParams.active" + active-color="#13ce66" + inactive-color="#ff4949" + @change="handleQuery" + > + </el-switch> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">鎼滅储</el-button> + <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + <el-form-item style="float:right"> + <el-button + type="primary" + icon="el-icon-plus" + size="mini" + @click="handleLoadXml" + >鏂板娴佺▼瀹氫箟</el-button> + </el-form-item> + </el-form> + + <el-table + v-loading="loading" fit + :data="definitionList" + row-key="id" + border + lazy + :load="load" + :tree-props="{children: 'children', hasChildren: 'hasChildren'}"> + <el-table-column label="娴佺▼瀹氫箟id" align="center" prop="id" /> + <el-table-column label="娴佺▼鏍囪瘑Key" align="center" prop="key" /> + <el-table-column label="娴佺▼鍒嗙被" align="center" > + <template slot-scope="scope"> + <span>{{ getCategoryName(scope.row.category) }}</span> + </template> + </el-table-column> + <el-table-column label="娴佺▼鍚嶇О" align="center" :show-overflow-tooltip="true"> + <template slot-scope="scope"> + <el-button type="text" @click="handleReadImage(scope.row.deploymentId)"> + <span>{{ scope.row.name }}</span> + </el-button> + </template> + </el-table-column> + <el-table-column label="娴佺▼鐗堟湰" align="center"> + <template slot-scope="scope"> + <el-tag size="medium" >v{{ scope.row.version }}</el-tag> + </template> + </el-table-column> + <el-table-column label="鐘舵��" align="center"> + <template slot-scope="scope"> + <el-tag type="success" v-if="scope.row.suspensionState === 1">婵�娲�</el-tag> + <el-tag type="warning" v-if="scope.row.suspensionState === 2">鎸傝捣</el-tag> + </template> + </el-table-column> + <el-table-column label="閮ㄧ讲鏃堕棿" align="center" prop="deploymentTime" width="180"/> + <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width"> + <template slot-scope="scope"> + <el-dropdown> + <span class="el-dropdown-link"> + 鏇村鎿嶄綔<i class="el-icon-arrow-down el-icon--right"></i> + </span> + <el-dropdown-menu slot="dropdown"> + <el-dropdown-item icon="el-icon-edit-outline" @click.native="handleLoadXml(scope.row)"> + 缂栬緫 + </el-dropdown-item> +<!-- <el-dropdown-item icon="el-icon-connection" @click.native="handleAddForm(scope.row)" v-if="scope.row.formId == null"> + 閰嶇疆琛ㄥ崟 + </el-dropdown-item>--> + <el-dropdown-item icon="el-icon-video-pause" @click.native="handleUpdateSuspensionState(scope.row)" v-if="scope.row.suspensionState === 1"> + 鎸傝捣 + </el-dropdown-item> + <el-dropdown-item icon="el-icon-video-play" @click.native="handleUpdateSuspensionState(scope.row)" v-if="scope.row.suspensionState === 2"> + 婵�娲� + </el-dropdown-item> + <el-dropdown-item icon="el-icon-delete" @click.native="handleDelete(scope.row)" > + 鍒犻櫎 + </el-dropdown-item> + </el-dropdown-menu> + </el-dropdown> + </template> + </el-table-column> + </el-table> + <el-pagination + v-show="total>0" + :total="total" + :current-page.sync="queryParams.pageNum" + :page-size.sync="queryParams.pageSize" + @size-change="getList" + @current-change="getList" + /> + </a-card> + <!-- 娴佺▼鍥� --> + <a-card v-if="xmlFrame.open&&!xmlView" :title="xmlFrame.title"> + <a slot="extra" href="#" @click="()=>{xmlFrame.open=false}">杩斿洖</a> + <bpmn-modeler + v-if="xmlShow" + ref="refNode" + :xml="xmlData" + :users="users" + :groups="groups" + :categorys="categorys" + :is-view="xmlView" + @save="save" + /> + </a-card> +<!-- 寮圭獥棰勮 --> + <a-modal :title="xmlFrame.title" :visible.sync="xmlView&&xmlFrame.open" :width="xmlFrame.width" + :footer="null" closable @cancel="()=>{xmlView=false,xmlFrame.open=false}" + > + <bpmn-modeler + v-if="xmlShow" + ref="refNode" + :xml="xmlData" + :users="users" + :groups="groups" + :categorys="categorys" + :is-view="xmlView" + @save="save" + /> + </a-modal> + + </div> +</template> + +<script> +import bpmnModeler from "workflow-bpmn-modeler"; +import { + categoryList, + delDeployment, + listDefinition, + readXml, + roleList, + saveXml, + updateState, + userList +} from "@views/flowable/api/definition"; + +export default { + components: { + bpmnModeler, + }, + data() { + return { + /*===================璁捐鍣ㄥ睘鎬�======================*/ + users: [], + groups: [], + categorys: [], + /*=================椤甸潰灞炴��===================*/ + loading: true, + // 鎬绘潯鏁� + total: 0, + // 娴佺▼瀹氫箟琛ㄦ牸鏁版嵁 + definitionList: [], + allDefinitionList: [], + // 鏌ヨ鍙傛暟 + queryParams: { + pageNum: 1, + pageSize: 10, + name: null, + category: null, + key: null, + tenantId: null, + deployTime: null, + derivedFrom: null, + derivedFromRoot: null, + parentDeploymentId: null, + engineVersion: null + }, + xmlFrame:{ + width:'70%', + title:'娴佺▼鍥�', + open: false, + src: "", + }, + // xml + xmlData:"", + xmlShow: true, + xmlView: false, + + }; + }, + created() { + this.initUserAndRole(); + this.getList(); + }, + methods: { + /*===============璁捐鍣�===============*/ + initUserAndRole(){ + userList({}).then(res=>{ + this.users = res.result||[] + this.users.map(o=>{ + o.id = o.username + o.name = o.realname + }) + }) + roleList({}).then(res=>{ + this.groups = res.result||[] + this.groups.map(o=>{ + o.name = o.roleName + }) + }) + categoryList({}).then(res=>{ + this.categorys = res.result||[] + }) + }, + getModelDetail(deployId) { + // 鍙戦�佽姹傦紝鑾峰彇xml + readXml(deployId).then(res =>{ + this.xmlData = res.result; + }) + }, + getCategoryName(category){ + let find = this.categorys.find(o=>o.id==category); + if (find){ + return find.name + } + return '' + }, + /*淇濆瓨娴佺▼瀹氫箟*/ + save(data) { + console.log(data); // { process: {...}, xml: '...', svg: '...' } + const params = { + name: data.process.name, + category: data.process.category, + xml: data.xml + } + saveXml(params).then(res => { + this.$message.success(res.message) + // 鍏抽棴褰撳墠鏍囩椤靛苟杩斿洖涓婁釜椤甸潰 + this.getList() + this.xmlFrame.open = false + }) + }, + /*================椤甸潰===============*/ + handleQuery() { + this.queryParams.pageNum = 1; + this.queryParams.suspensionState = this.queryParams.active?1:0; + this.getList(); + }, + /** 鏌ヨ娴佺▼瀹氫箟鍒楄〃 */ + getList() { + this.loading = true; + // 鏈�鏂扮増鏈� + const param1 = Object.assign({ + isLastVersion:1, + },this.queryParams) + listDefinition(param1).then(response => { + this.definitionList = response.result.records; + this.total = response.result.total; + this.loading = false; + for (const definition of this.definitionList) { + definition.hasChildren = true + } + }); + // 鎵�鏈� + const param2 = Object.assign({ + isLastVersion:0 + },this.queryParams,{ + pageSize: 9999, + pageNum:1 + }) + listDefinition(param2).then(response => { + console.log(response) + this.allDefinitionList = response.result.records; + }); + }, + /** 閲嶇疆鎸夐挳鎿嶄綔 */ + resetQuery() { + this.resetForm("queryForm"); + this.handleQuery(); + }, + resetForm(formName) { + this.$refs[formName].resetFields() + }, + /** 鎵撳紑娴佺▼璁捐寮圭獥椤甸潰 */ + handleLoadXml(row){ + if (row&&row.deploymentId){ + console.log(row.deploymentId) + this.handleReadImage(row.deploymentId) + this.xmlView = false + this.xmlFrame.title = "缂栬緫娴佺▼鍥�"; + } else { + //鏂板 + this.xmlData = '' + this.xmlView = false + this.xmlFrame.open = true + this.xmlFrame.title = '鏂板娴佺▼' + this.xmlShow = false + this.$nextTick(()=>{ + this.xmlShow = true + }) + } + this.xmlFrame.width = '90%' + }, + /** 娴佺▼鍥炬煡鐪� */ + handleReadImage(deploymentId){ + this.xmlFrame.title = "娴佺▼鍥�"; + this.xmlFrame.open = true; + this.xmlFrame.width = '70%'; + // this.xmlFrame.src = process.env.VUE_APP_BASE_API + "/flowable/definition/xmlFrame/" + deploymentId; + // 鍙戦�佽姹傦紝鑾峰彇xml + this.xmlView = true + readXml(deploymentId).then(res =>{ + if (res.success){ + this.xmlData = res.result + /*this.xmlShow = false + this.$nextTick(()=>{ + this.xmlShow = true + })*/ + } else { + this.$message.error("鑾峰彇娴佺▼鍥惧け璐ワ紒") + } + }) + }, + // 鎵撳紑涓氬姟琛ㄥ崟 + handleForm() { + }, + // 閰嶇疆涓氬姟琛ㄥ崟 + handleAddForm(row) { + }, + /** 鎸傝捣/婵�娲绘祦绋� */ + handleUpdateSuspensionState(row){ + let state = 1; + if (row.suspensionState === 1) { + state = 2 + } + const params = { + deployId: row.deploymentId, + state: state + } + updateState(params).then(res => { + this.$message.success(res.message); + this.getList(); + }); + }, + /** 鍒犻櫎鎸夐挳鎿嶄綔 */ + handleDelete(row) { + // const ids = row.deploymentId || this.ids; + const params = { + deployId: row.deploymentId + } + this.$confirm({ + title:"璀﹀憡", + content:'鏄惁纭鍒犻櫎娴佺▼瀹氫箟缂栧彿涓�"' + params.deployId + '"鐨勬暟鎹」?', + confirmButtonText: "纭畾", + cancelButtonText: "鍙栨秷", + type: "warning", + onOk:()=>{ + delDeployment(params).then(res=>{ + this.getList(); + if (res.success){ + this.$message.success('鍒犻櫎鎴愬姛'); + } else { + this.$message.success('鍒犻櫎澶辫触'); + } + }) + } + }) + }, + load(tree, treeNode, resolve) { + const key = tree.key; + const childrens = [] + for (const one of this.allDefinitionList) { + if (one.key==key&&one.id!=tree.id){ + childrens.push(one) + } + } + console.log(tree, treeNode,this.allDefinitionList,childrens) + resolve(childrens) + } + }, + computed: { + getContainer() { + return document.querySelector('#app') + } + } +}; +</script> \ No newline at end of file diff --git a/src/views/flowable/test_demo/TestDemoList.vue b/src/views/flowable/test_demo/TestDemoList.vue new file mode 100644 index 0000000..a53ace4 --- /dev/null +++ b/src/views/flowable/test_demo/TestDemoList.vue @@ -0,0 +1,311 @@ +<template> + <a-card :bordered="false"> + <!-- 鏌ヨ鍖哄煙 --> + <div class="table-page-search-wrapper"> + <a-form layout="inline" @keyup.enter.native="searchQuery"> + <a-row :gutter="24"> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <a-form-item label="鐢ㄦ埛鍚�"> + <a-input placeholder="璇疯緭鍏ョ敤鎴峰悕" v-model="queryParam.name"></a-input> + </a-form-item> + </a-col> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <a-form-item label="鎬у埆"> + <j-dict-select-tag placeholder="璇烽�夋嫨鎬у埆" v-model="queryParam.sex" dictCode="sex"/> + </a-form-item> + </a-col> + <template v-if="toggleSearchStatus"> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <a-form-item label="鐢熸棩"> + <j-date placeholder="璇烽�夋嫨鐢熸棩" v-model="queryParam.birthday"></j-date> + </a-form-item> + </a-col> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <a-form-item label="鐢ㄦ埛缂栫爜"> + <a-input placeholder="璇疯緭鍏ョ敤鎴风紪鐮�" v-model="queryParam.userCode"></a-input> + </a-form-item> + </a-col> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <a-form-item label="鍩庡競"> + <j-area-linkage type="cascader" v-model="queryParam.chegnshi" placeholder="璇烽�夋嫨鐪佸競鍖�"/> + </a-form-item> + </a-col> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <a-form-item label="checkbox"> + <j-dict-select-tag placeholder="璇烽�夋嫨checkbox" v-model="queryParam.ceck" dictCode="sex"/> + </a-form-item> + </a-col> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <a-form-item label="涓嬫媺澶氶��"> + <j-multi-select-tag placeholder="璇烽�夋嫨涓嬫媺澶氶��" dictCode="sex" v-model="queryParam.xiamuti"/> + </a-form-item> + </a-col> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <a-form-item label="鎼滅储涓嬫媺"> + <j-search-select-tag placeholder="璇烽�夋嫨鎼滅储涓嬫媺" v-model="queryParam.searchSel" dict="sys_role,role_name,role_code"/> + </a-form-item> + </a-col> + </template> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons"> + <a-button type="primary" @click="searchQuery" icon="search">鏌ヨ</a-button> + <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">閲嶇疆</a-button> + <a @click="handleToggleSearch" style="margin-left: 8px"> + {{ toggleSearchStatus ? '鏀惰捣' : '灞曞紑' }} + <a-icon :type="toggleSearchStatus ? 'up' : 'down'"/> + </a> + </span> + </a-col> + </a-row> + </a-form> + </div> + <!-- 鏌ヨ鍖哄煙-END --> + + <!-- 鎿嶄綔鎸夐挳鍖哄煙 --> + <div class="table-operator"> + <a-button @click="handleAdd" type="primary" icon="plus">鏂板</a-button> + <a-button type="primary" icon="download" @click="handleExportXls('娴嬭瘯鐢ㄦ埛琛�')">瀵煎嚭</a-button> + <a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" @change="handleImportExcel"> + <a-button type="primary" icon="import">瀵煎叆</a-button> + </a-upload> + <!-- 楂樼骇鏌ヨ鍖哄煙 --> + <j-super-query :fieldList="superFieldList" ref="superQueryModal" @handleSuperQuery="handleSuperQuery"></j-super-query> + <a-dropdown v-if="selectedRowKeys.length > 0"> + <a-menu slot="overlay"> + <a-menu-item key="1" @click="batchDel"><a-icon type="delete"/>鍒犻櫎</a-menu-item> + </a-menu> + <a-button style="margin-left: 8px"> 鎵归噺鎿嶄綔 <a-icon type="down" /></a-button> + </a-dropdown> + </div> + + <!-- table鍖哄煙-begin --> + <div> + <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;"> + <i class="anticon anticon-info-circle ant-alert-icon"></i> 宸查�夋嫨 <a style="font-weight: 600">{{ selectedRowKeys.length }}</a>椤� + <a style="margin-left: 24px" @click="onClearSelected">娓呯┖</a> + </div> + + <a-table + ref="table" + size="middle" + bordered + rowKey="id" + :columns="columns" + :dataSource="dataSource" + :pagination="ipagination" + :loading="loading" + :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}" + class="j-table-force-nowrap" + @change="handleTableChange"> + + <template slot="htmlSlot" slot-scope="text"> + <div v-html="text"></div> + </template> + <template slot="imgSlot" slot-scope="text"> + <span v-if="!text" style="font-size: 12px;font-style: italic;">鏃犲浘鐗�</span> + <img v-else :src="getImgView(text)" height="25px" alt="" style="max-width:80px;font-size: 12px;font-style: italic;"/> + </template> + <template slot="pcaSlot" slot-scope="text"> + <div>{{ getPcaText(text) }}</div> + </template> + <template slot="fileSlot" slot-scope="text"> + <span v-if="!text" style="font-size: 12px;font-style: italic;">鏃犳枃浠�</span> + <a-button + v-else + :ghost="true" + type="primary" + icon="download" + size="small" + @click="downloadFile(text)"> + 涓嬭浇 + </a-button> + </template> + + <span slot="action" slot-scope="text, record"> + <a @click="handleEdit(record)">缂栬緫</a> + <a-divider type="vertical" ></a-divider> + <a @click="relationAct(record)">鍏宠仈娴佺▼</a> + <act-apply-btn @success="loadData" :data-id="record.id" + :variables="{ assigneeList:[]}"></act-apply-btn> + {{isCanApply(record)}} + <act-handle-btn @success="loadData" :data-id="record.id" :type="3" text="閲嶆柊鎻愪氦"></act-handle-btn> + {{isCanReApply(record)}} + <act-handle-btn @success="loadData" :data-id="record.id" :type="0" text="閫氳繃" + :candidate-users="[{username:'admin',realname:'绠$悊鍛�'},{username:'jeecg',realname:'jeecg璐﹀彿'}]" + ></act-handle-btn> + {{isCanPass(record)}} + <act-handle-btn @success="loadData" :data-id="record.id" :type="1" text="椹冲洖"></act-handle-btn> + {{isCanBacke(record)}} + <act-handle-btn @success="loadData" :data-id="record.id" :type="2" text="閫�鍥�" + @targetKeyChange="targetKeyChange" + :candidate-users="[{username:'admin',realname:'绠$悊鍛�'},{username:'jeecg',realname:'jeecg璐﹀彿'}]" + ></act-handle-btn> + {{isCanBacke(record)}} + <act-cancel-btn @success="loadData" :data-id="record.id"></act-cancel-btn> + {{isCanRecall(record)}} + <act-historic-detail-btn :data-id="record.id"></act-historic-detail-btn> + {{isCanHistoric(record)}} + <a-divider type="vertical" /> + <a-dropdown> + <a class="ant-dropdown-link">鏇村 <a-icon type="down" /></a> + <a-menu slot="overlay"> + <a-menu-item> + <a @click="handleDetail(record)">璇︽儏</a> + </a-menu-item> + <a-menu-item> + <a-popconfirm title="纭畾鍒犻櫎鍚�?" @confirm="() => handleDelete(record.id)"> + <a>鍒犻櫎</a> + </a-popconfirm> + </a-menu-item> + </a-menu> + </a-dropdown> + </span> + + </a-table> + </div> + + <test-demo-modal ref="modalForm" @ok="modalFormOk"></test-demo-modal> + </a-card> +</template> + +<script> + + import '@/assets/less/TableExpand.less' + import { mixinDevice } from '@/utils/mixin' + import { JeecgListMixin } from '@/mixins/JeecgListMixin' + import TestDemoModal from './modules/TestDemoModal' + import {filterMultiDictText} from '@/components/dict/JDictSelectUtil' + import Area from '@/components/_util/Area' + import {getAction} from "@api/manage"; + import ActApplyBtn from "@views/flowable/components/ActApplyBtn"; + import ActCancelBtn from "@views/flowable/components/ActCancelBtn"; + import ActHandleBtn from "@views/flowable/components/ActHandleBtn"; + import ActHistoricDetailBtn from "@views/flowable/components/ActHistoricDetailBtn"; + import {FlowableMixin} from "@views/flowable/mixin/FlowableMixin"; + + export default { + name: 'TestDemoList', + mixins:[JeecgListMixin, mixinDevice,FlowableMixin], + components: { + TestDemoModal, + ActApplyBtn, + ActCancelBtn, + ActHandleBtn, + ActHistoricDetailBtn + }, + data () { + return { + description: '娴嬭瘯鐢ㄦ埛琛ㄧ鐞嗛〉闈�', + // 琛ㄥご + columns: [ + { + title: '#', + dataIndex: '', + key:'rowIndex', + width:60, + align:"center", + customRender:function (t,r,index) { + return parseInt(index)+1; + } + }, + { + title:'鐢ㄦ埛鍚�', + align:"center", + sorter: true, + dataIndex: 'name' + }, + { + title:'鎬у埆', + align:"center", + sorter: true, + dataIndex: 'sex_dictText' + }, + { + title:'骞撮緞', + align:"center", + dataIndex: 'age' + }, + { + title:'娴佺▼鐘舵��', + align:"center", + dataIndex: 'actStatus' + }, + { + title:'寰呭鐞嗚妭鐐�', + align:"center", + dataIndex: 'taskName' + }, + { + title: '鎿嶄綔', + dataIndex: 'action', + align:"center", + scopedSlots: { customRender: 'action' } + } + ], + url: { + list: "/test_demo/testDemo/list", + delete: "/test_demo/testDemo/delete", + deleteBatch: "/test_demo/testDemo/deleteBatch", + exportXlsUrl: "/test_demo/testDemo/exportXls", + importExcelUrl: "test_demo/testDemo/importExcel", + + }, + dictOptions:{}, + pcaData:'', + superFieldList:[], + } + }, + created() { + this.pcaData = new Area() + this.getSuperFieldList(); + }, + computed: { + importExcelUrl: function(){ + return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`; + }, + }, + methods: { + getPcaText(code){ + return this.pcaData.getText(code); + }, + initDictConfig(){ + }, + getSuperFieldList(){ + let fieldList=[]; + fieldList.push({type:'string',value:'id',text:'涓婚敭',dictCode:''}) + fieldList.push({type:'string',value:'name',text:'鐢ㄦ埛鍚�',dictCode:''}) + fieldList.push({type:'string',value:'sex',text:'鎬у埆',dictCode:'sex'}) + fieldList.push({type:'int',value:'age',text:'骞撮緞',dictCode:''}) + fieldList.push({type:'string',value:'descc',text:'鎻忚堪',dictCode:''}) + fieldList.push({type:'date',value:'birthday',text:'鐢熸棩'}) + fieldList.push({type:'string',value:'userCode',text:'鐢ㄦ埛缂栫爜',dictCode:''}) + fieldList.push({type:'string',value:'topPic',text:'澶村儚',dictCode:''}) + fieldList.push({type:'string',value:'fileKk',text:'闄勪欢',dictCode:''}) + fieldList.push({type:'pca',value:'chegnshi',text:'鍩庡競'}) + fieldList.push({type:'string',value:'pop',text:'寮圭獥',dictCode:''}) + fieldList.push({type:'string',value:'ceck',text:'checkbox',dictCode:'sex'}) + fieldList.push({type:'list_multi',value:'xiamuti',text:'涓嬫媺澶氶��',dictTable:'', dictText:'', dictCode:'sex'}) + fieldList.push({type:'sel_search',value:'searchSel',text:'鎼滅储涓嬫媺',dictTable:'sys_role', dictText:'role_name', dictCode:'role_code'}) + fieldList.push({type:'sel_search',value:'selTable',text:'涓嬫媺瀛楀吀琛�',dictTable:'sys_user', dictText:'realname', dictCode:'username'}) + this.superFieldList = fieldList + }, + relationAct(r) { + getAction("/test_demo/testDemo/relationAct",{dataId:r.id}).then(res=>{ + if (res.success){ + this.$message.success("鎿嶄綔鎴愬姛") + this.loadData() + } else { + this.$message.error("鎿嶄綔澶辫触") + } + }) + }, + targetKeyChange(targetKey) { + // todo + console.log('targetKey鏀瑰彉锛屾敼鍙� :candidate-users 鍊欓�変汉',targetKey) + } + } + } +</script> +<style scoped> + @import '~@assets/less/common.less'; +</style> \ No newline at end of file diff --git a/src/views/flowable/test_demo/modules/TestDemoForm.vue b/src/views/flowable/test_demo/modules/TestDemoForm.vue new file mode 100644 index 0000000..b7dde82 --- /dev/null +++ b/src/views/flowable/test_demo/modules/TestDemoForm.vue @@ -0,0 +1,172 @@ +<template> + <a-spin :spinning="confirmLoading"> + <j-form-container :disabled="formDisabled"> + <a-form-model ref="form" :model="model" :rules="validatorRules" slot="detail"> + <a-row> + <a-col :span="12"> + <a-form-model-item label="鐢ㄦ埛鍚�" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="name"> + <a-input v-model="model.name" placeholder="璇疯緭鍏ョ敤鎴峰悕" ></a-input> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="鎬у埆" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="sex"> + <j-dict-select-tag type="list" v-model="model.sex" dictCode="sex" placeholder="璇烽�夋嫨鎬у埆" /> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="骞撮緞" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="age"> + <a-input-number v-model="model.age" placeholder="璇疯緭鍏ュ勾榫�" style="width: 100%" /> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="鎻忚堪" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="descc"> + <j-editor v-model="model.descc" /> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="鐢熸棩" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="birthday"> + <j-date placeholder="璇烽�夋嫨鐢熸棩" v-model="model.birthday" style="width: 100%" /> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="鐢ㄦ埛缂栫爜" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="userCode"> + <a-input v-model="model.userCode" placeholder="璇疯緭鍏ョ敤鎴风紪鐮�" ></a-input> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="澶村儚" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="topPic"> + <j-image-upload isMultiple v-model="model.topPic" ></j-image-upload> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="闄勪欢" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="fileKk"> + <j-upload v-model="model.fileKk" ></j-upload> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="鍩庡競" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="chegnshi"> + <j-area-linkage type="cascader" v-model="model.chegnshi" placeholder="璇疯緭鍏ョ渷甯傚尯" /> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="寮圭獥" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="pop"> + <a-input v-model="model.pop" placeholder="璇疯緭鍏ュ脊绐�" ></a-input> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="checkbox" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="ceck"> + <j-multi-select-tag type="checkbox" v-model="model.ceck" dictCode="sex" placeholder="璇烽�夋嫨checkbox" /> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="涓嬫媺澶氶��" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="xiamuti"> + <j-multi-select-tag type="list_multi" v-model="model.xiamuti" dictCode="sex" placeholder="璇烽�夋嫨涓嬫媺澶氶��" /> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="鎼滅储涓嬫媺" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="searchSel"> + <j-search-select-tag v-model="model.searchSel" dict="sys_role,role_name,role_code" /> + </a-form-model-item> + </a-col> + <a-col :span="12"> + <a-form-model-item label="涓嬫媺瀛楀吀琛�" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="selTable"> + <j-search-select-tag v-model="model.selTable" dict="sys_user,realname,username" /> + </a-form-model-item> + </a-col> + </a-row> + </a-form-model> + </j-form-container> + </a-spin> +</template> + +<script> + + import { httpAction, getAction } from '@/api/manage' + import { validateDuplicateValue } from '@/utils/util' + + export default { + name: 'TestDemoForm', + components: { + }, + props: { + //琛ㄥ崟绂佺敤 + disabled: { + type: Boolean, + default: false, + required: false + } + }, + data () { + return { + model:{ + }, + labelCol: { + xs: { span: 24 }, + sm: { span: 5 }, + }, + wrapperCol: { + xs: { span: 24 }, + sm: { span: 16 }, + }, + confirmLoading: false, + validatorRules: { + name: [ + { required: true, message: '璇疯緭鍏ョ敤鎴峰悕!'}, + ], + }, + url: { + add: "/test_demo/testDemo/add", + edit: "/test_demo/testDemo/edit", + queryById: "/test_demo/testDemo/queryById" + } + } + }, + computed: { + formDisabled(){ + return this.disabled + }, + }, + created () { + //澶囦唤model鍘熷鍊� + this.modelDefault = JSON.parse(JSON.stringify(this.model)); + }, + methods: { + add () { + this.edit(this.modelDefault); + }, + edit (record) { + this.model = Object.assign({}, record); + this.visible = true; + }, + submitForm () { + const that = this; + // 瑙﹀彂琛ㄥ崟楠岃瘉 + this.$refs.form.validate(valid => { + if (valid) { + that.confirmLoading = true; + let httpurl = ''; + let method = ''; + if(!this.model.id){ + httpurl+=this.url.add; + method = 'post'; + }else{ + httpurl+=this.url.edit; + method = 'put'; + } + httpAction(httpurl,this.model,method).then((res)=>{ + if(res.success){ + that.$message.success(res.message); + that.$emit('ok'); + }else{ + that.$message.warning(res.message); + } + }).finally(() => { + that.confirmLoading = false; + }) + } + + }) + }, + } + } +</script> \ No newline at end of file diff --git a/src/views/flowable/test_demo/modules/TestDemoModal.vue b/src/views/flowable/test_demo/modules/TestDemoModal.vue new file mode 100644 index 0000000..9fd874f --- /dev/null +++ b/src/views/flowable/test_demo/modules/TestDemoModal.vue @@ -0,0 +1,60 @@ +<template> + <j-modal + :title="title" + :width="width" + :visible="visible" + switchFullscreen + @ok="handleOk" + :okButtonProps="{ class:{'jee-hidden': disableSubmit} }" + @cancel="handleCancel" + cancelText="鍏抽棴"> + <test-demo-form ref="realForm" @ok="submitCallback" :disabled="disableSubmit"></test-demo-form> + </j-modal> +</template> + +<script> + + import TestDemoForm from './TestDemoForm' + export default { + name: 'TestDemoModal', + components: { + TestDemoForm + }, + data () { + return { + title:'', + width:896, + visible: false, + disableSubmit: false + } + }, + methods: { + add () { + this.visible=true + this.$nextTick(()=>{ + this.$refs.realForm.add(); + }) + }, + edit (record) { + this.visible=true + this.$nextTick(()=>{ + this.$refs.realForm.edit(record); + }) + }, + close () { + this.$emit('close'); + this.visible = false; + }, + handleOk () { + this.$refs.realForm.submitForm(); + }, + submitCallback(){ + this.$emit('ok'); + this.visible = false; + }, + handleCancel () { + this.close() + } + } + } +</script> \ No newline at end of file -- Gitblit v1.9.3