多模块重构 12:修改项目名字,按照新的规则

This commit is contained in:
YunaiV
2022-02-02 22:33:39 +08:00
parent 352a67c530
commit 0773a4c4d7
1040 changed files with 12 additions and 190 deletions

View File

@@ -0,0 +1,173 @@
<template>
<div class="app-container">
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="定义编号" align="center" prop="id" width="400" />
<el-table-column label="定义名称" align="center" prop="name" width="100">
<template slot-scope="scope">
<el-button type="text" @click="handleBpmnDetail(scope.row)">
<span>{{ scope.row.name }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="定义分类" align="center" prop="category" width="100">
<template slot-scope="scope">
<span>{{ getDictDataLabel(DICT_TYPE.BPM_MODEL_CATEGORY, scope.row.category) }}</span>
</template>
</el-table-column>
<el-table-column label="表单信息" align="center" prop="formType" width="200">
<template slot-scope="scope">
<el-button v-if="scope.row.formId" type="text" @click="handleFormDetail(scope.row)">
<span>{{ scope.row.formName }}</span>
</el-button>
<el-button v-else-if="scope.row.formCustomCreatePath" type="text" @click="handleFormDetail(scope.row)">
<span>{{ scope.row.formCustomCreatePath }}</span>
</el-button>
<label v-else>暂无表单</label>
</template>
</el-table-column>
<el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
<template slot-scope="scope">
<el-tag size="medium" v-if="scope.row">v{{ scope.row.version }}</el-tag>
<el-tag size="medium" type="warning" v-else>未部署</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="version" width="80">
<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">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.deploymentTime) }}</span>
</template>
</el-table-column>
<el-table-column label="定义描述" align="center" prop="description" width="300" show-overflow-tooltip />
<el-table-column label="操作" align="center" width="150" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-s-custom" @click="handleAssignRule(scope.row)"
v-hasPermi="['bpm:task-assign-rule:update']">分配规则</el-button>
</template>
</el-table-column>
</el-table>
<!-- 流程表单配置详情 -->
<el-dialog title="表单详情" :visible.sync="detailOpen" width="50%" append-to-body>
<parser :key="new Date().getTime()" :form-conf="detailForm" />
</el-dialog>
<!-- 流程模型图的预览 -->
<el-dialog title="流程图" :visible.sync="showBpmnOpen" width="80%" append-to-body>
<my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" />
</el-dialog>
<!-- 分页组件 -->
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- ========== 流程任务分配规则 ========== -->
<taskAssignRuleDialog ref="taskAssignRuleDialog" />
</div>
</template>
<script>
import {getProcessDefinitionBpmnXML, getProcessDefinitionPage} from "@/api/bpm/definition";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {getForm} from "@/api/bpm/form";
import {decodeFields} from "@/utils/formGenerator";
import Parser from '@/components/parser/Parser'
import taskAssignRuleDialog from "../taskAssignRule/taskAssignRuleDialog";
export default {
name: "processDefinition",
components: {
Parser,
taskAssignRuleDialog
},
data() {
return {
// 遮罩层
loading: true,
// 总条数
total: 0,
// 表格数据
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10
},
// 流程表单详情
detailOpen: false,
detailForm: {
fields: []
},
// BPMN 数据
showBpmnOpen: false,
bpmnXML: null,
bpmnControlForm: {
prefix: "activiti"
},
// 数据字典
categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
};
},
created() {
const key = this.$route.query && this.$route.query.key
if (key) {
this.queryParams['key'] = key
}
this.getList();
},
methods: {
/** 查询流程定义列表 */
getList() {
this.loading = true;
getProcessDefinitionPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
}
);
},
/** 流程表单的详情按钮操作 */
handleFormDetail(row) {
// 流程表单
if (row.formId) {
// 设置值
this.detailForm = {
...JSON.parse(row.formConf),
fields: decodeFields(row.formFields)
}
// 弹窗打开
this.detailOpen = true
// 业务表单
} else if (row.formCustomCreatePath) {
this.$router.push({ path: row.formCustomCreatePath});
}
},
/** 流程图的详情按钮操作 */
handleBpmnDetail(row) {
getProcessDefinitionBpmnXML(row.id).then(response => {
this.bpmnXML = response.data
// 弹窗打开
this.showBpmnOpen = true
})
},
/** 处理任务分配规则列表的按钮操作 */
handleAssignRule(row) {
this.$refs['taskAssignRuleDialog'].initProcessDefinition(row.id);
},
}
};
</script>
<style lang="scss">
.my-process-designer {
height: calc(100vh - 200px);
}
</style>

View File

@@ -0,0 +1,570 @@
<template>
<div class="container">
<div class="left-board">
<div class="logo-wrapper">
<div class="logo">流程表单</div>
</div>
<el-scrollbar class="left-scrollbar">
<!-- 左边表单项 -->
<div class="components-list">
<div v-for="(item, listIndex) in leftComponents" :key="listIndex">
<div class="components-title">
<svg-icon icon-class="component" />
{{ item.title }}
</div>
<draggable
class="components-draggable"
:list="item.list"
:group="{ name: 'componentsGroup', pull: 'clone', put: false }"
:clone="cloneComponent"
draggable=".components-item"
:sort="false"
@end="onEnd"
>
<div
v-for="(element, index) in item.list"
:key="index"
class="components-item"
@click="addComponent(element)"
>
<div class="components-body">
<svg-icon :icon-class="element.__config__.tagIcon" />
{{ element.__config__.label }}
</div>
</div>
</draggable>
</div>
<!-- 左边动态表单 -->
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="表单名" prop="name">
<el-input v-model="form.name" placeholder="请输入表单名" />
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
</div>
</el-scrollbar>
</div>
<div class="center-board">
<div class="action-bar">
<el-button icon="el-icon-check" type="text" @click="save">保存</el-button>
<!-- <el-button icon="el-icon-video-play" type="text" @click="run">-->
<!-- 运行-->
<!-- </el-button>-->
<el-button icon="el-icon-view" type="text" @click="showJson">
查看json
</el-button>
<!-- <el-button icon="el-icon-download" type="text" @click="download">-->
<!-- 导出vue文件-->
<!-- </el-button>-->
<!-- <el-button class="copy-btn-main" icon="el-icon-document-copy" type="text" @click="copy">-->
<!-- 复制代码-->
<!-- </el-button>-->
<el-button class="delete-btn" icon="el-icon-delete" type="text" @click="empty">
清空
</el-button>
</div>
<!-- 中间表单项 -->
<el-scrollbar class="center-scrollbar">
<el-row class="center-board-row" :gutter="formConf.gutter">
<el-form
:size="formConf.size"
:label-position="formConf.labelPosition"
:disabled="formConf.disabled"
:label-width="formConf.labelWidth + 'px'"
>
<draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup">
<draggable-item
v-for="(item, index) in drawingList"
:key="item.renderKey"
:drawing-list="drawingList"
:current-item="item"
:index="index"
:active-id="activeId"
:form-conf="formConf"
@activeItem="activeFormItem"
@copyItem="drawingItemCopy"
@deleteItem="drawingItemDelete"
/>
</draggable>
<div v-show="!drawingList.length" class="empty-info">
从左侧拖入或点选组件进行表单设计
</div>
</el-form>
</el-row>
</el-scrollbar>
</div>
<!-- 右边组件属性/表单属性 -->
<right-panel
:active-data="activeData"
:form-conf="formConf"
:show-field="!!drawingList.length"
@tag-change="tagChange"
@fetch-data="fetchData"
/>
<!-- <form-drawer-->
<!-- :visible.sync="drawerVisible"-->
<!-- :form-data="formData"-->
<!-- size="100%"-->
<!-- :generate-conf="generateConf"-->
<!-- />-->
<json-drawer
size="60%"
:visible.sync="jsonDrawerVisible"
:json-str="JSON.stringify(formData)"
@refresh="refreshJson"
/>
<!-- <code-type-dialog-->
<!-- :visible.sync="dialogVisible"-->
<!-- title="选择生成类型"-->
<!-- :show-file-name="showFileName"-->
<!-- @confirm="generate"-->
<!-- />-->
<!-- <input id="copyNode" type="hidden">-->
</div>
</template>
<script>
import draggable from 'vuedraggable'
import { debounce } from 'throttle-debounce'
import { saveAs } from 'file-saver'
import ClipboardJS from 'clipboard'
import render from '@/components/render/render'
import FormDrawer from '@/views/tool/build/FormDrawer'
import JsonDrawer from '@/views/tool/build/JsonDrawer'
import RightPanel from '@/views/tool/build/RightPanel'
import {
inputComponents, selectComponents, layoutComponents, formConf
} from '@/components/generator/config'
import {
exportDefault, beautifierConf, isNumberStr, titleCase, deepClone, isObjectObject
} from '@/utils/index'
import {
makeUpHtml, vueTemplate, vueScript, cssStyle
} from '@/components/generator/html'
import { makeUpJs } from '@/components/generator/js'
import { makeUpCss } from '@/components/generator/css'
import drawingDefalut from '@/components/generator/drawingDefalut'
import logo from '@/assets/logo/logo.png'
import CodeTypeDialog from '@/views/tool/build/CodeTypeDialog'
import DraggableItem from '@/views/tool/build/DraggableItem'
import {
getDrawingList, saveDrawingList, getIdGlobal, saveIdGlobal, getFormConf
} from '@/utils/db'
import loadBeautifier from '@/utils/loadBeautifier'
import {CommonStatusEnum} from "@/utils/constants";
import {createForm, getForm, updateForm} from "@/api/bpm/form";
import {decodeFields} from "@/utils/formGenerator";
let beautifier
const emptyActiveData = { style: {}, autosize: {} }
let oldActiveId
let tempActiveData
const drawingListInDB = getDrawingList()
const formConfInDB = getFormConf()
const idGlobal = getIdGlobal()
export default {
components: {
draggable,
render,
FormDrawer,
JsonDrawer,
RightPanel,
CodeTypeDialog,
DraggableItem
},
data() {
return {
logo,
idGlobal,
formConf,
inputComponents,
selectComponents,
layoutComponents,
labelWidth: 100,
// drawingList: drawingDefalut,
drawingData: {}, // 生成后的表单数据
activeId: drawingDefalut[0].__config__.formId,
drawingList: [], // 表单项的数组
// activeId: undefined,
// activeData: {},
drawerVisible: false,
formData: {},
dialogVisible: false,
jsonDrawerVisible: false,
generateConf: null,
showFileName: false,
activeData: drawingDefalut[0], // 右边编辑器激活的表单项
saveDrawingListDebounce: debounce(340, saveDrawingList),
saveIdGlobalDebounce: debounce(340, saveIdGlobal),
leftComponents: [
{
title: '输入型组件',
list: inputComponents
},
{
title: '选择型组件',
list: selectComponents
},
{
title: '布局型组件',
list: layoutComponents
}
],
// 表单参数
form: {
status: CommonStatusEnum.ENABLE,
},
// 表单校验
rules: {
name: [{ required: true, message: "表单名不能为空", trigger: "blur" }],
status: [{ required: true, message: "开启状态不能为空", trigger: "blur" }],
}
}
},
computed: {
},
watch: {
// eslint-disable-next-line func-names
'activeData.__config__.label': function (val, oldVal) {
if (
this.activeData.placeholder === undefined
|| !this.activeData.__config__.tag
|| oldActiveId !== this.activeId
) {
return
}
this.activeData.placeholder = this.activeData.placeholder.replace(oldVal, '') + val
},
activeId: {
handler(val) {
oldActiveId = val
},
immediate: true
},
drawingList: {
handler(val) {
this.saveDrawingListDebounce(val)
if (val.length === 0) this.idGlobal = 100
},
deep: true
},
idGlobal: {
handler(val) {
this.saveIdGlobalDebounce(val)
},
immediate: true
}
},
mounted() {
// 【add by 芋道源码】不读缓存
// if (Array.isArray(drawingListInDB) && drawingListInDB.length > 0) {
// this.drawingList = drawingListInDB
// } else {
// this.drawingList = drawingDefalut
// }
// this.activeFormItem(this.drawingList[0])
// if (formConfInDB) {
// this.formConf = formConfInDB
// }
loadBeautifier(btf => {
beautifier = btf
})
const clipboard = new ClipboardJS('#copyNode', {
text: trigger => {
const codeStr = this.generateCode()
this.$notify({
title: '成功',
message: '代码已复制到剪切板,可粘贴。',
type: 'success'
})
return codeStr
}
})
clipboard.on('error', e => {
this.$message.error('代码复制失败')
})
},
created() {
// 读取表单配置
const formId = this.$route.query && this.$route.query.formId
if (formId) {
getForm(formId).then(response => {
const data = response.data
this.form = {
id: data.id,
name: data.name,
status: data.status,
remark: data.remark
}
this.formConf = JSON.parse(data.conf)
this.drawingList = decodeFields(data.fields)
// 设置激活的表单项
this.activeData = this.drawingList[0]
this.activeId = this.activeData.__config__.formId
// 设置 idGlobal避免重复
this.idGlobal += this.drawingList.length
});
}
},
methods: {
setObjectValueReduce(obj, strKeys, data) {
const arr = strKeys.split('.')
arr.reduce((pre, item, i) => {
if (arr.length === i + 1) {
pre[item] = data
} else if (!isObjectObject(pre[item])) {
pre[item] = {}
}
return pre[item]
}, obj)
},
setRespData(component, resp) {
const { dataPath, renderKey, dataConsumer } = component.__config__
if (!dataPath || !dataConsumer) return
const respData = dataPath.split('.').reduce((pre, item) => pre[item], resp)
// 将请求回来的数据,赋值到指定属性。
// 以el-tabel为例根据Element文档应该将数据赋值给el-tabel的data属性所以dataConsumer的值应为'data';
// 此时赋值代码可写成 component[dataConsumer] = respData
// 但为支持更深层级的赋值dataConsumer的值为'options.data',使用setObjectValueReduce
this.setObjectValueReduce(component, dataConsumer, respData)
const i = this.drawingList.findIndex(item => item.__config__.renderKey === renderKey)
if (i > -1) this.$set(this.drawingList, i, component)
},
fetchData(component) {
const { dataType, method, url } = component.__config__
if (dataType === 'dynamic' && method && url) {
this.setLoading(component, true)
this.$axios({
method,
url
}).then(resp => {
this.setLoading(component, false)
this.setRespData(component, resp.data)
})
}
},
setLoading(component, val) {
const { directives } = component
if (Array.isArray(directives)) {
const t = directives.find(d => d.name === 'loading')
if (t) t.value = val
}
},
activeFormItem(currentItem) {
this.activeData = currentItem
this.activeId = currentItem.__config__.formId
},
onEnd(obj) {
if (obj.from !== obj.to) {
this.fetchData(tempActiveData)
this.activeData = tempActiveData
this.activeId = this.idGlobal
}
},
addComponent(item) {
const clone = this.cloneComponent(item)
this.fetchData(clone)
this.drawingList.push(clone)
this.activeFormItem(clone)
},
cloneComponent(origin) {
const clone = deepClone(origin)
const config = clone.__config__
config.span = this.formConf.span // 生成代码时会根据span做精简判断
this.createIdAndKey(clone)
clone.placeholder !== undefined && (clone.placeholder += config.label)
tempActiveData = clone
return tempActiveData
},
createIdAndKey(item) {
const config = item.__config__
config.formId = ++this.idGlobal
config.renderKey = `${config.formId}${+new Date()}` // 改变renderKey后可以实现强制更新组件
if (config.layout === 'colFormItem') {
item.__vModel__ = `field${this.idGlobal}`
} else if (config.layout === 'rowFormItem') {
config.componentName = `row${this.idGlobal}`
!Array.isArray(config.children) && (config.children = [])
delete config.label // rowFormItem无需配置label属性
}
if (Array.isArray(config.children)) {
config.children = config.children.map(childItem => this.createIdAndKey(childItem))
}
return item
},
// 获得表单数据
AssembleFormData() {
this.formData = {
fields: deepClone(this.drawingList),
...this.formConf
}
},
save() {
// this.AssembleFormData()
// console.log(this.formData)
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
const form = {
conf: JSON.stringify(this.formConf), // 表单配置
fields: this.encodeFields(), // 表单项的数组
...this.form // 表单名等
}
// 修改的提交
if (this.form.id != null) {
updateForm(form).then(response => {
this.msgSuccess("修改成功");
this.close()
});
return;
}
// 添加的提交
createForm(form).then(response => {
this.msgSuccess("新增成功");
this.close()
});
});
},
/** 关闭按钮 */
close() {
this.$store.dispatch("tagsView/delView", this.$route);
this.$router.push({ path: "/bpm/manager/form", query: { t: Date.now()}})
},
encodeFields() {
const fields = []
this.drawingList.forEach(item => {
fields.push(JSON.stringify(item))
})
return fields
},
generate(data) {
const func = this[`exec${titleCase(this.operationType)}`]
this.generateConf = data
func && func(data)
},
execRun(data) {
this.AssembleFormData()
this.drawerVisible = true
},
execDownload(data) {
const codeStr = this.generateCode()
const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
saveAs(blob, data.fileName)
},
execCopy(data) {
document.getElementById('copyNode').click()
},
empty() {
this.$confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then(
() => {
this.drawingList = []
this.idGlobal = 100
}
)
},
drawingItemCopy(item, list) {
let clone = deepClone(item)
clone = this.createIdAndKey(clone)
list.push(clone)
this.activeFormItem(clone)
},
drawingItemDelete(index, list) {
list.splice(index, 1)
this.$nextTick(() => {
const len = this.drawingList.length
if (len) {
this.activeFormItem(this.drawingList[len - 1])
}
})
},
generateCode() {
const { type } = this.generateConf
this.AssembleFormData()
const script = vueScript(makeUpJs(this.formData, type))
const html = vueTemplate(makeUpHtml(this.formData, type))
const css = cssStyle(makeUpCss(this.formData))
return beautifier.html(html + script + css, beautifierConf.html)
},
showJson() {
this.AssembleFormData()
this.jsonDrawerVisible = true
},
download() {
this.dialogVisible = true
this.showFileName = true
this.operationType = 'download'
},
run() {
this.dialogVisible = true
this.showFileName = false
this.operationType = 'run'
},
copy() {
this.dialogVisible = true
this.showFileName = false
this.operationType = 'copy'
},
tagChange(newTag) {
newTag = this.cloneComponent(newTag)
const config = newTag.__config__
newTag.__vModel__ = this.activeData.__vModel__
config.formId = this.activeId
config.span = this.activeData.__config__.span
this.activeData.__config__.tag = config.tag
this.activeData.__config__.tagIcon = config.tagIcon
this.activeData.__config__.document = config.document
if (typeof this.activeData.__config__.defaultValue === typeof config.defaultValue) {
config.defaultValue = this.activeData.__config__.defaultValue
}
Object.keys(newTag).forEach(key => {
if (this.activeData[key] !== undefined) {
newTag[key] = this.activeData[key]
}
})
this.activeData = newTag
this.updateDrawingList(newTag, this.drawingList)
},
updateDrawingList(newTag, list) {
const index = list.findIndex(item => item.__config__.formId === this.activeId)
if (index > -1) {
list.splice(index, 1, newTag)
} else {
list.forEach(item => {
if (Array.isArray(item.__config__.children)) this.updateDrawingList(newTag, item.__config__.children)
})
}
},
refreshJson(data) {
this.drawingList = deepClone(data.fields)
delete data.fields
this.formConf = data
}
}
}
</script>
<style lang='scss'>
@import '@/styles/home';
</style>

View File

@@ -0,0 +1,166 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" 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>
<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>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['bpm:form:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="表单名" align="center" prop="name" />
<el-table-column label="开启状态" align="center" prop="status">
<template slot-scope="scope">
<span>{{ getDictDataLabel(DICT_TYPE.COMMON_STATUS, scope.row.status) }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleDetail(scope.row)"
v-hasPermi="['bpm:form:query']">详情</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['bpm:form:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['bpm:form:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!--表单配置详情-->
<el-dialog title="表单详情" :visible.sync="detailOpen" width="50%" append-to-body>
<div class="test-form">
<parser :key="new Date().getTime()" :form-conf="detailForm" />
</div>
</el-dialog>
</div>
</template>
<script>
import {deleteForm, getForm, getFormPage} from "@/api/bpm/form";
import Parser from '@/components/parser/Parser'
import {decodeFields} from "@/utils/formGenerator";
export default {
name: "Form",
components: {
Parser
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 工作流的列表
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
},
// 表单详情
detailOpen: false,
detailForm: {
fields: []
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
// 执行查询
getFormPage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 详情按钮操作 */
handleDetail(row) {
getForm(row.id).then(response => {
// 设置值
const data = response.data
this.detailForm = {
...JSON.parse(data.conf),
fields: decodeFields(data.fields)
}
// 弹窗打开
this.detailOpen = true
})
},
/** 新增按钮操作 */
handleAdd() {
this.$router.push({
path:"/bpm/manager/form/edit"
});
},
/** 修改按钮操作 */
handleUpdate(row) {
this.$router.push({
path:"/bpm/manager/form/edit",
query:{
formId: row.id
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$confirm('是否确认删除工作流的编号为"' + id + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return deleteForm(id);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}
}
};
</script>

View File

@@ -0,0 +1,254 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" 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="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</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>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['bpm:user-group:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="组名" align="center" prop="name" />
<el-table-column label="描述" align="center" prop="description" />
<el-table-column label="成员" align="center">
<template slot-scope="scope">
<span v-for="userId in scope.row.memberUserIds">
{{ getUserNickname(userId) }}
</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template slot-scope="scope">
<span>{{ getDictDataLabel(DICT_TYPE.COMMON_STATUS, scope.row.status) }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['bpm:user-group:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['bpm:user-group:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="组名" prop="name">
<el-input v-model="form.name" placeholder="请输入组名" />
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input v-model="form.description" placeholder="请输入描述" />
</el-form-item>
<el-form-item label="成员" prop="memberUserIds">
<el-select v-model="form.memberUserIds" multiple placeholder="请选择成员">
<el-option v-for="user in users" :key="parseInt(user.id)" :label="user.nickname" :value="parseInt(user.id)"/>
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { createUserGroup, updateUserGroup, deleteUserGroup, getUserGroup, getUserGroupPage } from "@/api/bpm/userGroup";
import {CommonStatusEnum} from "@/utils/constants";
import {listSimpleUsers} from "@/api/system/user";
export default {
name: "UserGroup",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 用户组列表
list: [],
// 用户列表
users: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
dateRangeCreateTime: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
tenantId: null,
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [{ required: true, message: "组名不能为空", trigger: "blur" }],
description: [{ required: true, message: "描述不能为空", trigger: "blur" }],
memberUserIds: [{ required: true, message: "成员不能为空", trigger: "change" }],
status: [{ required: true, message: "状态不能为空", trigger: "blur" }],
}
};
},
created() {
this.getList();
// 获得用户列表
listSimpleUsers().then(response => {
this.users = response.data;
})
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
// 执行查询
getUserGroupPage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
description: undefined,
memberUserIds: [],
status: CommonStatusEnum.ENABLE,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加用户组";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getUserGroup(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改用户组";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateUserGroup(this.form).then(response => {
this.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createUserGroup(this.form).then(response => {
this.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$confirm('是否确认删除用户组编号为"' + id + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return deleteUserGroup(id);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
},
getUserNickname(userId) {
for (const user of this.users) {
if (user.id === userId) {
return user.nickname;
}
}
return '未知(' + userId + ')';
},
}
};
</script>

View File

@@ -0,0 +1,556 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="流程标识" prop="key">
<el-input v-model="queryParams.key" placeholder="请输入流程标识" clearable style="width: 240px;" size="small"
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="流程名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程名称" clearable style="width: 240px;" size="small"
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" placeholder="流程分类" clearable size="small" style="width: 240px">
<el-option v-for="dict in categoryDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="cyan" 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>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['bpm:model:create']">新建流程</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" icon="el-icon-upload2" size="mini" @click="handleImport"
v-hasPermi="['bpm:model:import']">导入流程</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="流程标识" align="center" prop="key" />
<el-table-column label="流程名称" align="center" prop="name" width="200">
<template slot-scope="scope">
<el-button type="text" @click="handleBpmnDetail(scope.row)">
<span>{{ scope.row.name }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="流程分类" align="center" prop="category" width="100">
<template slot-scope="scope">
<span>{{ getDictDataLabel(DICT_TYPE.BPM_MODEL_CATEGORY, scope.row.category) }}</span>
</template>
</el-table-column>
<el-table-column label="表单信息" align="center" prop="formType" width="200">
<template slot-scope="scope">
<el-button v-if="scope.row.formId" type="text" @click="handleFormDetail(scope.row)">
<span>{{ scope.row.formName }}</span>
</el-button>
<el-button v-else-if="scope.row.formCustomCreatePath" type="text" @click="handleFormDetail(scope.row)">
<span>{{ scope.row.formCustomCreatePath }}</span>
</el-button>
<label v-else>暂无表单</label>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="最新部署的流程定义" align="center">
<el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
<template slot-scope="scope">
<el-tag size="medium" v-if="scope.row.processDefinition">v{{ scope.row.processDefinition.version }}</el-tag>
<el-tag size="medium" type="warning" v-else>未部署</el-tag>
</template>
</el-table-column>
<el-table-column label="激活状态" align="center" prop="processDefinition.version" width="80">
<template slot-scope="scope">
<el-switch v-if="scope.row.processDefinition" v-model="scope.row.processDefinition.suspensionState"
:active-value="1" :inactive-value="2" @change="handleChangeState(scope.row)" />
</template>
</el-table-column>
<el-table-column label="部署时间" align="center" prop="deploymentTime" width="180">
<template slot-scope="scope">
<span v-if="scope.row.processDefinition">{{ parseTime(scope.row.processDefinition.deploymentTime) }}</span>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="操作" align="center" width="450" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['bpm:model:update']">修改流程</el-button>
<el-button size="mini" type="text" icon="el-icon-setting" @click="handleDesign(scope.row)"
v-hasPermi="['bpm:model:update']">设计流程</el-button>
<el-button size="mini" type="text" icon="el-icon-s-custom" @click="handleAssignRule(scope.row)"
v-hasPermi="['bpm:task-assign-rule:query']">分配规则</el-button>
<el-button size="mini" type="text" icon="el-icon-thumb" @click="handleDeploy(scope.row)"
v-hasPermi="['bpm:model:deploy']">发布流程</el-button>
<el-button size="mini" type="text" icon="el-icon-ice-cream-round" @click="handleDefinitionList(scope.row)"
v-hasPermi="['bpm:process-definition:query']">流程定义</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['bpm:model:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 流程表单配置详情 -->
<el-dialog title="表单详情" :visible.sync="detailOpen" width="50%" append-to-body>
<parser :key="new Date().getTime()" :form-conf="detailForm" />
</el-dialog>
<!-- 流程模型图的预览 -->
<el-dialog title="流程图" :visible.sync="showBpmnOpen" width="80%" append-to-body>
<my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" />
</el-dialog>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="110px">
<el-form-item label="流程标识" prop="key">
<el-input v-model="form.key" placeholder="请输入流标标识" style="width: 330px;" :disabled="form.id" />
<el-tooltip v-if="!form.id" class="item" effect="light" content="新建后,流程标识不可修改!" placement="top">
<i style="padding-left: 5px;" class="el-icon-question" />
</el-tooltip>
<el-tooltip v-else class="item" effect="light" content="流程标识不可修改!" placement="top">
<i style="padding-left: 5px;" class="el-icon-question" />
</el-tooltip>
</el-form-item>
<el-form-item label="流程名称" prop="name">
<el-input v-model="form.name" placeholder="请输入流程名称" :disabled="form.id" clearable />
</el-form-item>
<el-form-item v-if="form.id" label="流程分类" prop="category">
<el-select v-model="form.category" placeholder="请选择流程分类" clearable style="width: 100%">
<el-option v-for="dict in categoryDictDatas" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="流程描述" prop="description">
<el-input type="textarea" v-model="form.description" clearable />
</el-form-item>
<div v-if="form.id">
<el-form-item label="表单类型" prop="formType">
<el-radio-group v-model="form.formType">
<el-radio v-for="dict in modelFormTypeDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
{{dict.label}}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.formType === 10" label="流程表单" prop="formId">
<el-select v-model="form.formId" clearable style="width: 100%">
<el-option v-for="form in forms" :key="form.id" :label="form.name" :value="form.id"/>
</el-select>
</el-form-item>
<el-form-item v-if="form.formType === 20" label="表单提交路由" prop="formCustomCreatePath" >
<el-input v-model="form.formCustomCreatePath" placeholder="请输入表单提交路由" style="width: 330px;" />
<el-tooltip class="item" effect="light" content="自定义表单的提交路径,使用 Vue 的路由地址例如说bpm/oa/leave/create" placement="top">
<i style="padding-left: 5px;" class="el-icon-question" />
</el-tooltip>
</el-form-item>
<el-form-item v-if="form.formType === 20" label="表单查看路由" prop="formCustomViewPath">
<el-input v-model="form.formCustomViewPath" placeholder="请输入表单查看路由" style="width: 330px;" />
<el-tooltip class="item" effect="light" content="自定义表单的查看路径,使用 Vue 的路由地址例如说bpm/oa/leave/view" placement="top">
<i style="padding-left: 5px;" class="el-icon-question" />
</el-tooltip>
</el-form-item>
</div>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<!-- 用户导入对话框 -->
<el-dialog title="导入流程" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".bpmn, .xml" :headers="upload.headers" :action="upload.url"
:disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess"
:auto-upload="false" name="bpmnFile" :data="upload.form" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处
<em>点击上传</em>
</div>
<div class="el-upload__tip" style="color:red" slot="tip">提示仅允许导入bpmxml格式文件</div>
<div class="el-upload__tip" slot="tip">
<el-form ref="uploadForm" size="mini" label-width="90px" :model="upload.form" :rules="upload.rules" @submit.native.prevent>
<el-form-item label="流程标识" prop="key">
<el-input v-model="upload.form.key" placeholder="请输入流标标识" style="width: 250px;" />
<el-tooltip class="item" effect="light" content="新建后,流程标识不可修改!" placement="top">
<i style="padding-left: 5px;" class="el-icon-question" />
</el-tooltip>
</el-form-item>
<el-form-item label="流程名称" prop="name">
<el-input v-model="upload.form.name" placeholder="请输入流程名称" clearable />
</el-form-item>
<el-form-item label="流程描述" prop="description">
<el-input type="textarea" v-model="upload.form.description" clearable />
</el-form-item>
</el-form>
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="uploadClose"> </el-button>
</div>
</el-dialog>
<!-- ========== 流程任务分配规则 ========== -->
<taskAssignRuleDialog ref="taskAssignRuleDialog" />
</div>
</template>
<script>
import {
deleteModel,
deployModel,
getModelPage,
getModel,
updateModelState,
createModel,
updateModel
} from "@/api/bpm/model";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {getForm, getSimpleForms} from "@/api/bpm/form";
import {decodeFields} from "@/utils/formGenerator";
import Parser from '@/components/parser/Parser'
import {getBaseHeader} from "@/utils/request";
import taskAssignRuleDialog from "../taskAssignRule/taskAssignRuleDialog";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
name: "model",
components: {
Parser,
Treeselect,
taskAssignRuleDialog
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 表格数据
list: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10
},
// BPMN 数据
showBpmnOpen: false,
bpmnXML: null,
bpmnControlForm: {
prefix: "activiti"
},
// 流程表单详情
detailOpen: false,
detailForm: {
fields: []
},
// 流程表单
title: "",
open: false,
form: {},
// 表单校验
rules: {
key: [{ required: true, message: "流程标识不能为空", trigger: "blur" }],
name: [{ required: true, message: "流程名称不能为空", trigger: "blur" }],
formType: [{ required: true, message: "流程名称不能为空", trigger: "blur" }],
formId: [{ required: true, message: "业务表单不能为空", trigger: "blur" }],
category: [{ required: true, message: "流程分类不能为空", trigger: "blur" }],
formCustomCreatePath: [{ required: true, message: "表单提交路由不能为空", trigger: "blur" }],
formCustomViewPath: [{ required: true, message: "表单查看路由不能为空", trigger: "blur" }],
},
// 流程导入参数
upload: {
// 是否显示弹出层(用户导入)
open: false,
// 是否禁用上传
isUploading: false,
// 设置上传的请求头部
headers: getBaseHeader(),
// 上传的地址
url: process.env.VUE_APP_BASE_API + '/api/' + "/bpm/model/import",
// 表单
form: {},
// 校验规则
rules: {
key: [{ required: true, message: "流程标识不能为空", trigger: "blur" }],
name: [{ required: true, message: "流程名称不能为空", trigger: "blur" }],
},
},
// 流程表单的下拉框的数据
forms: [],
// 数据字典
categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
modelFormTypeDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_FORM_TYPE),
taskAssignRuleDictDatas: getDictDatas(DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE),
};
},
created() {
this.getList();
// 获得流程表单的下拉框的数据
getSimpleForms().then(response => {
this.forms = response.data
})
},
methods: {
/** 查询流程模型列表 */
getList() {
this.loading = true;
getModelPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
}
);
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
id: undefined,
key: undefined,
name: undefined,
description: undefined,
category: undefined,
formType: undefined,
formId: undefined,
formCustomCreatePath: undefined,
formCustomViewPath: undefined
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.title = "新建模型";
this.open = true;
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
this.title = "修改模型";
this.open = true;
// 设置 form
this.form = {
...row
};
// 触发一次校验
// this.$refs["form"].validate();
},
/** 设计按钮操作 */
handleDesign(row) {
this.$router.push({
path:"/bpm/manager/model/design",
query:{
modelId: row.id
}
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 更新
if (this.form.id) {
updateModel({
...this.form,
formId: this.form.formType === 10 ? this.form.formId : undefined,
formCustomCreatePath: this.form.formType === 20 ? this.form.formCustomCreatePath : undefined,
formCustomViewPath: this.form.formType === 20 ? this.form.formCustomViewPath : undefined,
}).then(response => {
this.msgSuccess("修改模型成功");
this.open = false;
this.getList();
});
return;
}
// 创建
createModel(this.form).then(response => {
this.open = false;
this.getList();
this.$alert('<strong>新建模型成功!</strong>后续需要执行如下 4 个步骤:' +
'<div>1. 点击【修改流程】按钮,配置流程的分类、表单信息</div>' +
'<div>2. 点击【设计流程】按钮,绘制流程图</div>' +
'<div>3. 点击【分配规则】按钮,设置每个用户任务的审批人</div>' +
'<div>4. 点击【发布流程】按钮,完成流程的最终发布</div>' +
'另外,每次流程修改后,都需要点击【发布流程】按钮,才能正式生效!!!',
'重要提示', {
dangerouslyUseHTMLString: true,
type: 'success'
});
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const that = this;
this.$confirm('是否删除该流程!!', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
deleteModel(row.id).then(response => {
that.getList();
that.msgSuccess("删除成功");
})
})
},
/** 部署按钮操作 */
handleDeploy(row) {
const that = this;
this.$confirm('是否部署该流程!!', "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "success"
}).then(function() {
deployModel(row.id).then(response => {
that.getList();
that.msgSuccess("部署成功");
})
})
},
/** 流程表单的详情按钮操作 */
handleFormDetail(row) {
// 流程表单
if (row.formId) {
getForm(row.formId).then(response => {
// 设置值
const data = response.data
this.detailForm = {
...JSON.parse(data.conf),
fields: decodeFields(data.fields)
}
// 弹窗打开
this.detailOpen = true
})
// 业务表单
} else if (row.formCustomCreatePath) {
this.$router.push({ path: row.formCustomCreatePath});
}
},
/** 流程图的详情按钮操作 */
handleBpmnDetail(row) {
getModel(row.id).then(response => {
this.bpmnXML = response.data.bpmnXml
// 弹窗打开
this.showBpmnOpen = true
})
},
/** 跳转流程定义的列表 */
handleDefinitionList(row) {
this.$router.push({
path:"/bpm/manager/definition",
query:{
key: row.key
}
});
},
/** 更新状态操作 */
handleChangeState(row) {
const id = row.id;
let state = row.processDefinition.suspensionState;
let statusState = state === 1 ? '激活' : '挂起';
this.$confirm('是否确认' + statusState + '流程名字为"' + row.name + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return updateModelState(id, state);
}).then(() => {
this.getList();
this.msgSuccess(statusState + "成功");
})
},
/** 导入按钮操作 */
handleImport() {
this.upload.open = true;
},
// 文件上传中处理
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true;
},
// 文件上传成功处理
handleFileSuccess(response, file, fileList) {
if (response.code !== 0) {
this.msgError(response.msg)
return;
}
// 重置表单
this.uploadClose();
// 提示,并刷新
this.msgSuccess("导入流程成功!请点击【设计流程】按钮,进行编辑保存后,才可以进行【发布流程】");
this.getList();
},
uploadClose() {
// 关闭弹窗
this.upload.open = false;
// 重置上传状态和文件
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
// 重置表单
this.upload.form = {};
this.resetForm("uploadForm");
},
/** 提交上传文件 */
submitFileForm() {
this.$refs["uploadForm"].validate(valid => {
if (!valid) {
return;
}
this.$refs.upload.submit();
})
},
/** 处理任务分配规则列表的按钮操作 */
handleAssignRule(row) {
this.$refs['taskAssignRuleDialog'].initModel(row.id);
},
}
};
</script>
<style lang="scss">
.my-process-designer {
height: calc(100vh - 200px);
}
</style>

View File

@@ -0,0 +1,186 @@
<template>
<div class="app-container">
<!-- 流程设计器负责绘制流程等 -->
<my-process-designer :key="`designer-${reloadIndex}`" v-model="xmlString" v-bind="controlForm"
keyboard ref="processDesigner" @init-finished="initModeler"
@save="save"/>
<!-- 流程属性器负责编辑每个流程节点的属性 -->
<my-properties-panel :key="`penal-${reloadIndex}`" :bpmn-modeler="modeler" :prefix="controlForm.prefix" class="process-panel"
:model="model" />
</div>
</template>
<script>
import translations from "@/components/bpmnProcessDesigner/src/translations";
// 自定义元素选中时的弹出菜单(修改 默认任务 为 用户任务)
import CustomContentPadProvider from "@/components/bpmnProcessDesigner/package/designer/plugins/content-pad";
// 自定义左侧菜单(修改 默认任务 为 用户任务)
import CustomPaletteProvider from "@/components/bpmnProcessDesigner/package/designer/plugins/palette";
// import xmlObj2json from "./utils/xml2json";
import MyProcessPalette from "@/components/bpmnProcessDesigner/package/palette/ProcessPalette";
import {createModel, getModel, updateModel} from "@/api/bpm/model";
// 自定义侧边栏
// import MyProcessPanel from "../package/process-panel/ProcessPanel";
export default {
name: "App",
components: { MyProcessPalette },
data() {
return {
xmlString: "", // BPMN XML
modeler: null,
reloadIndex: 0,
controlDrawerVisible: false,
translationsSelf: translations,
controlForm: {
simulation: true,
labelEditing: false,
labelVisible: false,
prefix: "activiti",
headerButtonSize: "mini",
additionalModel: [CustomContentPadProvider, CustomPaletteProvider]
},
addis: {
CustomContentPadProvider,
CustomPaletteProvider
},
// 流程模型的信息
model: {},
};
},
created() {
// 如果 modelId 非空,说明是修改流程模型
const modelId = this.$route.query && this.$route.query.modelId
if (modelId) {
getModel(modelId).then(response => {
this.xmlString = response.data.bpmnXml
this.model = {
...response.data,
bpmnXml: undefined, // 清空 bpmnXml 属性
}
// this.controlForm.processId = response.data.key
})
}
},
methods: {
initModeler(modeler) {
setTimeout(() => {
this.modeler = modeler;
console.log(modeler);
}, 10);
},
reloadProcessDesigner(deep) {
this.controlForm.additionalModel = [];
for (let key in this.addis) {
if (this.addis[key]) {
this.controlForm.additionalModel.push(this.addis[key]);
}
}
deep && (this.xmlString = undefined);
this.reloadIndex += 1;
this.modeler = null; // 避免 panel 异常
// if (deep) {
// this.xmlString = undefined;
// this.$refs.processDesigner.processRestart();
// }
},
save(bpmnXml) {
const data = {
...this.model,
bpmnXml: bpmnXml, // this.bpmnXml 只是初始化流程图,后续修改无法通过它获得
}
// 修改的提交
if (data.id) {
updateModel(data).then(response => {
this.msgSuccess("修改成功")
// 跳转回去
this.close()
})
return
}
// 添加的提交
createModel(data).then(response => {
this.msgSuccess("保存成功")
// 跳转回去
this.close()
})
},
/** 关闭按钮 */
close() {
this.$store.dispatch("tagsView/delView", this.$route);
this.$router.push({ path: "/bpm/manager/model", query: { t: Date.now()}})
},
}
};
</script>
<style lang="scss">
//body {
// overflow: hidden;
// margin: 0;
// box-sizing: border-box;
//}
//.app {
// width: 100%;
// height: 100%;
// box-sizing: border-box;
// display: inline-grid;
// grid-template-columns: 100px auto max-content;
//}
.demo-control-bar {
position: fixed;
right: 8px;
bottom: 8px;
z-index: 1;
.open-control-dialog {
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
font-size: 32px;
background: rgba(64, 158, 255, 1);
color: #ffffff;
cursor: pointer;
}
}
// TODO 芋艿:去掉多余的 faq
//.info-tip {
// position: fixed;
// top: 40px;
// right: 500px;
// z-index: 10;
// color: #999999;
//}
.control-form {
.el-radio {
width: 100%;
line-height: 32px;
}
}
.element-overlays {
box-sizing: border-box;
padding: 8px;
background: rgba(0, 0, 0, 0.6);
border-radius: 4px;
color: #fafafa;
}
.my-process-designer {
height: calc(100vh - 84px);
}
.process-panel__container {
position: absolute;
right: 0;
top: 55px;
height: calc(100vh - 84px);
}
</style>

View File

@@ -0,0 +1,76 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="开始时间" prop="startTime">
<el-date-picker clearable size="small" v-model="form.startTime" type="date" value-format="timestamp" placeholder="选择开始时间" />
</el-form-item>
<el-form-item label="结束时间" prop="endTime">
<el-date-picker clearable size="small" v-model="form.endTime" type="date" value-format="timestamp" placeholder="选择结束时间" />
</el-form-item>
<el-form-item label="请假类型" prop="type">
<el-select v-model="form.type" placeholder="请选择">
<el-option v-for="dict in typeDictData" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item label="原因" prop="reason">
<el-col :span="10">
<el-input type="textarea" :rows="3" v-model="form.reason" placeholder="请输入原因" />
</el-col>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm"> </el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { createLeave} from "@/api/bpm/leave"
import { getDictDatas, DICT_TYPE } from '@/utils/dict'
export default {
name: "LeaveCreate",
components: {
},
data() {
return {
// 表单参数
form: {
startTime: undefined,
endTime: undefined,
type: undefined,
reason: undefined,
},
// 表单校验
rules: {
startTime: [{ required: true, message: "开始时间不能为空", trigger: "blur" }],
endTime: [{ required: true, message: "结束时间不能为空", trigger: "blur" }],
type: [{ required: true, message: "请假类型不能为空", trigger: "change" }],
reason: [{ required: true, message: "请假原因不能为空", trigger: "change" }],
},
typeDictData: getDictDatas(DICT_TYPE.BPM_OA_LEAVE_TYPE),
};
},
created() {
},
methods: {
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 添加的提交
createLeave(this.form).then(response => {
this.msgSuccess("发起成功");
this.$store.dispatch("tagsView/delView", this.$route);
this.$router.push({ path: "/bpm/oa/leave"});
});
});
}
}
};
</script>

View File

@@ -0,0 +1,54 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item label="开始时间:" prop="startTime"> {{parseTime(form.startTime, '{y}-{m}-{d}')}} </el-form-item>
<el-form-item label="结束时间:" prop="endTime"> {{parseTime(form.endTime, '{y}-{m}-{d}')}} </el-form-item>
<el-form-item label="请假类型:" prop="type">{{ typeFormat(form) }}</el-form-item>
<el-form-item label="原因:" prop="reason"> {{ form.reason }}</el-form-item>
</el-form>
</div>
</template>
<script>
import { getLeave} from "@/api/bpm/leave"
import {getDictDatas, DICT_TYPE, getDictDataLabel} from '@/utils/dict'
export default {
name: "LeaveDetail",
components: {
},
data() {
return {
id: undefined, // 请假编号
// 表单参数
form: {
startTime: undefined,
endTime: undefined,
type: undefined,
reason: undefined,
},
typeDictData: getDictDatas(DICT_TYPE.BPM_OA_LEAVE_TYPE),
};
},
created() {
this.id = this.$route.query.id;
if (!this.id) {
this.$message.error('未传递 id 参数,无法查看 OA 请假信息');
return;
}
this.getDetail();
},
methods: {
/** 获得请假信息 */
getDetail() {
getLeave(this.id).then(response => {
this.form = response.data;
});
},
typeFormat(row) {
return getDictDataLabel(DICT_TYPE.BPM_OA_LEAVE_TYPE, row.type)
}
}
};
</script>

View File

@@ -0,0 +1,174 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="请假类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择请假类型" clearable>
<el-option v-for="dict in leaveTypeDictData" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="申请时间">
<el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item label="结果" prop="result">
<el-select v-model="queryParams.result" placeholder="请选择流结果" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="原因" prop="reason">
<el-input v-model="queryParams.reason" placeholder="请输入原因" clearable size="small" @keyup.enter.native="handleQuery"/>
</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>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" icon="el-icon-plus" size="mini" v-hasPermi="['bpm:oa-leave:create']" @click="handleAdd">发起请假</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="申请编号" align="center" prop="id" />
<el-table-column label="状态" align="center" prop="result" :formatter="resultFormat" />
<el-table-column label="开始时间" align="center" prop="startTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.startTime) }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="endTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column label="请假类型" align="center" prop="type" :formatter="typeFormat" />
<el-table-column label="原因" align="center" prop="reason" />
<el-table-column label="申请时间" align="center" prop="applyTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleCancel(scope.row)"
v-hasPermi="['bpm:oa-leave:create']" v-if="scope.row.result === 1">取消请假</el-button>
<el-button size="mini" type="text" icon="el-icon-view" @click="handleDetail(scope.row)"
v-hasPermi="['bpm:oa-leave:query']">详情</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleProcessDetail(scope.row)">审批进度</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { getLeavePage } from "@/api/bpm/leave"
import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
import {cancelProcessInstance} from "@/api/bpm/processInstance";
export default {
name: "Leave",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 请假申请列表
list: [],
//审批进度弹出层
dateRangeCreateTime: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
result: null,
type: null,
reason: null,
},
leaveTypeDictData: getDictDatas(DICT_TYPE.BPM_OA_LEAVE_TYPE),
leaveResultData: getDictDatas(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT),
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
// 执行查询
getLeavePage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.$router.push({ path: "/bpm/oa/leave/create"});
},
/** 详情按钮操作 */
handleDetail(row) {
this.$router.push({ path: "/bpm/oa/leave/detail", query: { id: row.id}});
},
/** 查看审批进度的操作 */
handleProcessDetail(row) {
this.$router.push({ path: "/bpm/process-instance/detail", query: { id: row.processInstanceId}});
},
/** 取消请假 */
handleCancel(row) {
const id = row.processInstanceId;
this.$prompt('请输入取消原因?', "取消流程", {
type: 'warning',
confirmButtonText: "确定",
cancelButtonText: "取消",
inputPattern: /^[\s\S]*.*[^\s][\s\S]*$/, // 判断非空,且非空格
inputErrorMessage: "取消原因不能为空",
}).then(({ value }) => {
return cancelProcessInstance(id, value);
}).then(() => {
this.getList();
this.msgSuccess("取消成功");
})
},
resultFormat(row, column) {
return getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, row.result)
},
typeFormat(row, column) {
return getDictDataLabel(DICT_TYPE.BPM_OA_LEAVE_TYPE, row.type)
},
}
};
</script>

View File

@@ -0,0 +1,169 @@
<template>
<div class="app-container">
<!-- 第一步通过流程定义的列表选择对应的流程 -->
<div v-if="!selectProcessInstance">
<el-table v-loading="loading" :data="list">
<el-table-column label="流程名称" align="center" prop="name" width="200">
<template slot-scope="scope">
<el-button type="text" @click="handleBpmnDetail(scope.row)">
<span>{{ scope.row.name }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="流程分类" align="center" prop="category" width="100">
<template slot-scope="scope">
<span>{{ getDictDataLabel(DICT_TYPE.BPM_MODEL_CATEGORY, scope.row.category) }}</span>
</template>
</el-table-column>
<el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
<template slot-scope="scope">
<el-tag size="medium" v-if="scope.row">v{{ scope.row.version }}</el-tag>
</template>
</el-table-column>
<el-table-column label="流程描述" align="center" prop="description" width="300" show-overflow-tooltip />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="text" size="small" icon="el-icon-plus" @click="handleSelect(scope.row)">选择</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 第二步填写表单进行流程的提交 -->
<div v-else>
<el-card class="box-card" >
<div slot="header" class="clearfix">
<span class="el-icon-document">申请信息{{ selectProcessInstance.name }}</span>
<el-button style="float: right;" type="primary" @click="selectProcessInstance = undefined">选择其它流程</el-button>
</div>
<el-col :span="16" :offset="6">
<div>
<parser :key="new Date().getTime()" :form-conf="detailForm" @submit="submitForm" />
</div>
</el-col>
</el-card>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span class="el-icon-picture-outline">流程图</span>
</div>
<my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" />
</el-card>
</div>
</div>
</template>
<script>
import {getProcessDefinitionBpmnXML, getProcessDefinitionList} from "@/api/bpm/definition";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {getForm} from "@/api/bpm/form";
import {decodeFields} from "@/utils/formGenerator";
import Parser from '@/components/parser/Parser'
import {createProcessInstance, getMyProcessInstancePage} from "@/api/bpm/processInstance";
// 流程实例的发起
export default {
name: "ProcessInstanceCreate",
components: {
Parser
},
data() {
return {
// 遮罩层
loading: true,
// 表格数据
list: [],
// 流程表单详情
detailForm: {
fields: []
},
// BPMN 数据
bpmnXML: null,
bpmnControlForm: {
prefix: "activiti"
},
// 流程表单
selectProcessInstance: undefined, // 选择的流程实例
// 数据字典
categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
};
},
created() {
this.getList();
},
methods: {
/** 查询流程定义列表 */
getList() {
this.loading = true;
getProcessDefinitionList({
suspensionState: 1
}).then(response => {
this.list = response.data
this.loading = false
}
);
},
/** 处理选择流程的按钮操作 **/
handleSelect(row) {
// 设置选择的流程
this.selectProcessInstance = row;
// 流程表单
if (row.formId) {
// 设置对应的表单
this.detailForm = {
...JSON.parse(row.formConf),
fields: decodeFields(row.formFields)
}
// 加载流程图
getProcessDefinitionBpmnXML(row.id).then(response => {
this.bpmnXML = response.data
})
} else if (row.formCustomCreatePath) {
this.$router.push({ path: row.formCustomCreatePath});
// 这里暂时无需加载流程图,因为跳出到另外个 Tab
}
},
/** 提交按钮 */
submitForm(params) {
if (!params) {
return;
}
// 设置表单禁用
const conf = params.conf;
conf.disabled = true; // 表单禁用
conf.formBtns = false; // 按钮隐藏
// 提交表单,创建流程
const variables = params.values;
createProcessInstance({
processDefinitionId: this.selectProcessInstance.id,
variables: variables
}).then(response => {
this.msgSuccess("发起流程成功");
// 关闭当前窗口
this.$store.dispatch("tagsView/delView", this.$route);
this.$router.go(-1);
}).catch(() => {
conf.disabled = false; // 表单开启
conf.formBtns = true; // 按钮展示
})
},
}
};
</script>
<style lang="scss">
.my-process-designer {
height: calc(100vh - 200px);
}
.box-card {
width: 100%;
margin-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,424 @@
<template>
<div class="app-container">
<!-- 审批信息 -->
<el-card class="box-card" v-loading="processInstanceLoading" v-for="(item, index) in runningTasks" :key="index">
<div slot="header" class="clearfix">
<span class="el-icon-picture-outline">审批任务{{ item.name }}</span>
</div>
<el-col :span="16" :offset="6" >
<el-form :ref="'form' + index" :model="auditForms[index]" :rules="auditRule" label-width="100px">
<el-form-item label="流程名" v-if="processInstance && processInstance.name">
{{ processInstance.name }}
</el-form-item>
<el-form-item label="流程发起人" v-if="processInstance && processInstance.startUser">
{{ processInstance.startUser.nickname }}
<el-tag type="info" size="mini">{{ processInstance.startUser.deptName }}</el-tag>
</el-form-item>
<el-form-item label="审批建议" prop="comment">
<el-input type="textarea" v-model="auditForms[index].comment" placeholder="请输入审批建议" />
</el-form-item>
</el-form>
<div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px;">
<el-button icon="el-icon-edit-outline" type="success" size="mini" @click="handleAudit(item, true)">通过</el-button>
<el-button icon="el-icon-circle-close" type="danger" size="mini" @click="handleAudit(item, false)">不通过</el-button>
<el-button icon="el-icon-edit-outline" type="primary" size="mini" @click="handleUpdateAssignee(item)">转办</el-button>
<el-button icon="el-icon-edit-outline" type="primary" size="mini" @click="handleDelegate(item)">委派</el-button>
<el-button icon="el-icon-refresh-left" type="warning" size="mini" @click="handleBack(item)">退回</el-button>
</div>
</el-col>
</el-card>
<!-- 申请信息 -->
<el-card class="box-card" v-loading="processInstanceLoading">
<div slot="header" class="clearfix">
<span class="el-icon-document">申请信息{{ processInstance.name }}</span>
</div>
<el-col v-if="this.processInstance.processDefinition && this.processInstance.processDefinition.formType === 10"
:span="16" :offset="6">
<div >
<parser :key="new Date().getTime()" :form-conf="detailForm" @submit="submitForm" />
</div>
</el-col>
<div v-if="this.processInstance.processDefinition && this.processInstance.processDefinition.formType === 20">
<router-link :to="this.processInstance.processDefinition.formCustomViewPath + '?id='
+ this.processInstance.businessKey">
<el-button type="primary">点击查看</el-button>
</router-link>
</div>
</el-card>
<el-card class="box-card" v-loading="tasksLoad">
<div slot="header" class="clearfix">
<span class="el-icon-picture-outline">审批记录</span>
</div>
<el-col :span="16" :offset="4" >
<div class="block">
<el-timeline>
<el-timeline-item v-for="(item, index) in tasks" :key="index"
:icon="getTimelineItemIcon(item)" :type="getTimelineItemType(item)">
<p style="font-weight: 700">任务{{ item.name }}</p>
<el-card :body-style="{ padding: '10px' }">
<label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px;">
审批人{{ item.assigneeUser.nickname }}
<el-tag type="info" size="mini">{{ item.assigneeUser.deptName }}</el-tag>
</label>
<label style="font-weight: normal">创建时间</label>
<label style="color:#8a909c; font-weight: normal">{{ parseTime(item.createTime) }}</label>
<label v-if="item.endTime" style="margin-left: 30px;font-weight: normal">审批时间</label>
<label v-if="item.endTime" style="color:#8a909c;font-weight: normal"> {{ parseTime(item.endTime) }}</label>
<label v-if="item.durationInMillis" style="margin-left: 30px;font-weight: normal">耗时</label>
<label v-if="item.durationInMillis" style="color:#8a909c;font-weight: normal"> {{ getDateStar(item.durationInMillis) }} </label>
<p v-if="item.comment">
<el-tag :type="getTimelineItemType(item)">{{ item.comment }}</el-tag>
</p>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
</el-col>
</el-card>
<!-- 高亮流程图 -->
<el-card class="box-card" v-loading="processInstanceLoading">
<div slot="header" class="clearfix">
<span class="el-icon-picture-outline">流程图</span>
</div>
<my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" :activityData="activityList"
:processInstanceData="processInstance" :taskData="tasks" />
</el-card>
<!-- 对话框(转派审批人) -->
<el-dialog title="转派审批人" :visible.sync="updateAssignee.open" width="500px" append-to-body>
<el-form ref="updateAssigneeForm" :model="updateAssignee.form" :rules="updateAssignee.rules" label-width="110px">
<el-form-item label="新审批人" prop="assigneeUserId">
<el-select v-model="updateAssignee.form.assigneeUserId" clearable style="width: 100%">
<el-option v-for="item in userOptions" :key="parseInt(item.id)" :label="item.nickname" :value="parseInt(item.id)" />
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitUpdateAssigneeForm"> </el-button>
<el-button @click="cancelUpdateAssigneeForm"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {getProcessDefinitionBpmnXML} from "@/api/bpm/definition";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import store from "@/store";
import {decodeFields} from "@/utils/formGenerator";
import Parser from '@/components/parser/Parser'
import {createProcessInstance, getProcessInstance} from "@/api/bpm/processInstance";
import {approveTask, getTaskListByProcessInstanceId, rejectTask, updateTaskAssignee} from "@/api/bpm/task";
import {getDate} from "@/utils/dateUtils";
import {listSimpleUsers} from "@/api/system/user";
import {getActivityList} from "@/api/bpm/activity";
// 流程实例的详情页,可用于审批
export default {
name: "ProcessInstanceDetail",
components: {
Parser
},
data() {
return {
// 遮罩层
processInstanceLoading: true,
// 流程实例
id: undefined, // 流程实例的编号
processInstance: {},
// 流程表单详情
detailForm: {
fields: []
},
// BPMN 数据
bpmnXML: null,
bpmnControlForm: {
prefix: "activiti"
},
activityList: [],
// 审批记录
tasksLoad: true,
tasks: [],
// 审批表单
runningTasks: [],
auditForms: [],
auditRule: {
comment: [{ required: true, message: "审批建议不能为空", trigger: "blur" }],
},
// 转派审批人
userOptions: [],
updateAssignee: {
open: false,
form: {
assigneeUserId: undefined,
},
rules: {
assigneeUserId: [{ required: true, message: "新审批人不能为空", trigger: "change" }],
}
},
// 数据字典
categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
};
},
created() {
this.id = this.$route.query.id;
if (!this.id) {
this.$message.error('未传递 id 参数,无法查看流程信息');
return;
}
this.getDetail();
// 获得用户列表
this.userOptions = [];
listSimpleUsers().then(response => {
this.userOptions.push(...response.data);
});
},
methods: {
/** 获得流程实例 */
getDetail() {
// 获得流程实例相关
this.processInstanceLoading = true;
getProcessInstance(this.id).then(response => {
if (!response.data) {
this.$message.error('查询不到流程信息!');
return;
}
// 设置流程信息
this.processInstance = response.data;
// 设置表单信息
if (this.processInstance.processDefinition.formType === 10) {
this.detailForm = {
...JSON.parse(this.processInstance.processDefinition.formConf),
disabled: true, // 表单禁用
formBtns: false, // 按钮隐藏
fields: decodeFields(this.processInstance.processDefinition.formFields)
}
// 设置表单的值
this.detailForm.fields.forEach(item => {
const val = this.processInstance.formVariables[item.__vModel__]
if (val) {
item.__config__.defaultValue = val
}
});
}
// 加载流程图
getProcessDefinitionBpmnXML(this.processInstance.processDefinition.id).then(response => {
this.bpmnXML = response.data
});
// 加载活动列表
getActivityList({
processInstanceId: this.processInstance.id
}).then(response => {
this.activityList = response.data;
});
// 取消加载中
this.processInstanceLoading = false;
});
// 获得流程任务列表(审批记录)
this.tasksLoad = true;
this.runningTasks = [];
this.auditForms = [];
getTaskListByProcessInstanceId(this.id).then(response => {
// 审批记录
this.tasks = response.data;
// 排序,将未完成的排在前面,已完成的排在后面;
this.tasks.sort((a, b) => {
// 有已完成的情况,按照完成时间倒序
if (a.endTime && b.endTime) {
return b.endTime - a.endTime;
} else if (a.endTime) {
return 1;
} else if (b.endTime) {
return -1;
// 都是未完成,按照创建时间倒序
} else {
return b.createTime - a.createTime;
}
});
// 需要审核的记录
const userId = store.getters.userId;
this.tasks.forEach(task => {
if (task.result !== 1) { // 只有待处理才需要
return;
}
if (!task.assigneeUser || task.assigneeUser.id !== userId) { // 自己不是处理人
return;
}
this.runningTasks.push({...task});
this.auditForms.push({
comment: ''
})
});
// 取消加载中
this.tasksLoad = false;
});
},
/** 处理选择流程的按钮操作 **/
handleSelect(row) {
// 设置选择的流程
this.selectProcessInstance = row;
// 流程表单
if (row.formId) {
// 设置对应的表单
this.detailForm = {
...JSON.parse(row.formConf),
fields: decodeFields(row.formFields)
}
} else if (row.formCustomCreatePath) {
this.$router.push({ path: row.formCustomCreatePath});
// 这里暂时无需加载流程图,因为跳出到另外个 Tab
}
},
/** 提交按钮 */
submitForm(params) {
if (!params) {
return;
}
// 设置表单禁用
const conf = params.conf;
conf.disabled = true; // 表单禁用
conf.formBtns = false; // 按钮隐藏
// 提交表单,创建流程
const variables = params.values;
createProcessInstance({
processDefinitionId: this.selectProcessInstance.id,
variables: variables
}).then(response => {
this.msgSuccess("发起流程成功");
// 关闭当前窗口
this.$store.dispatch("tagsView/delView", this.$route);
this.$router.go(-1);
}).catch(() => {
conf.disabled = false; // 表单开启
conf.formBtns = true; // 按钮展示
})
},
getDateStar(ms) {
return getDate(ms);
},
getTimelineItemIcon(item) {
if (item.result === 1) {
return 'el-icon-time';
}
if (item.result === 2) {
return 'el-icon-check';
}
if (item.result === 3) {
return 'el-icon-close';
}
if (item.result === 4) {
return 'el-icon-remove-outline';
}
return '';
},
getTimelineItemType(item) {
if (item.result === 1) {
return 'primary';
}
if (item.result === 2) {
return 'success';
}
if (item.result === 3) {
return 'danger';
}
if (item.result === 4) {
return 'info';
}
return '';
},
/** 处理审批通过和不通过的操作 */
handleAudit(task, pass) {
const index = this.runningTasks.indexOf(task);
this.$refs['form' + index][0].validate(valid => {
if (!valid) {
return;
}
const data = {
id: task.id,
comment: this.auditForms[index].comment
}
if (pass) {
approveTask(data).then(response => {
this.msgSuccess("审批通过成功!");
this.getDetail(); // 获得最新详情
});
} else {
rejectTask(data).then(response => {
this.msgSuccess("审批不通过成功!");
this.getDetail(); // 获得最新详情
});
}
});
},
/** 处理转派审批人 */
handleUpdateAssignee(task) {
// 设置表单
this.resetUpdateAssigneeForm();
this.updateAssignee.form.id = task.id;
// 设置为打开
this.updateAssignee.open = true;
},
/** 提交转派审批人 */
submitUpdateAssigneeForm() {
this.$refs['updateAssigneeForm'].validate(valid => {
if (!valid) {
return;
}
updateTaskAssignee(this.updateAssignee.form).then(response => {
this.msgSuccess("转派任务成功!");
this.updateAssignee.open = false;
this.getDetail(); // 获得最新详情
});
});
},
/** 取消转派审批人 */
cancelUpdateAssigneeForm() {
this.updateAssignee.open = false;
this.resetUpdateAssigneeForm();
},
/** 重置转派审批人 */
resetUpdateAssigneeForm() {
this.updateAssignee.form = {
id: undefined,
assigneeUserId: undefined,
};
this.resetForm("updateAssigneeForm");
},
/** 处理审批退回的操作 */
handleDelegate(task) {
this.msgError("暂不支持【委派】功能,可以使用【转派】替代!");
},
/** 处理审批退回的操作 */
handleBack(task) {
this.msgError("暂不支持【退回】功能!");
}
}
};
</script>
<style lang="scss">
.my-process-designer {
height: calc(100vh - 200px);
}
.box-card {
width: 100%;
margin-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,215 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" 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="processDefinitionId">
<el-input v-model="queryParams.processDefinitionId" placeholder="请输入流程定义的编号" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" placeholder="请选择流程分类" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="提交时间">
<el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="结果" prop="result">
<el-select v-model="queryParams.result" placeholder="请选择流结果" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</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>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['bpm:process-instance:query']">发起流程</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" width="320" />
<el-table-column label="流程名" align="center" prop="name" />
<el-table-column label="流程分类" align="center" prop="category">
<template slot-scope="scope">
<span>{{ getDictDataLabel(DICT_TYPE.BPM_MODEL_CATEGORY, scope.row.category) }}</span>
</template>
</el-table-column>
<el-table-column label="当前审批任务" align="center" prop="tasks">
<template slot-scope="scope">
<el-button v-for="task in scope.row.tasks" type="text" @click="handleFormDetail(task.id)">
<span>{{ task.name }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template slot-scope="scope">
<span>
<el-tag type="primary" v-if="scope.row.status === 1"> <!-- 进行中 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS, scope.row.status) }}
</el-tag>
<el-tag type="success" v-if="scope.row.status === 2"> <!-- 已结束 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS, scope.row.status) }}
</el-tag>
</span>
</template>
</el-table-column>
<el-table-column label="结果" align="center" prop="result">
<template slot-scope="scope">
<span>
<el-tag type="primary" v-if="scope.row.result === 1"> <!-- 进行中 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
<el-tag type="success" v-if="scope.row.result === 2"> <!-- 通过 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
<el-tag type="danger" v-if="scope.row.result === 3"> <!-- 不通过 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
<el-tag type="info" v-if="scope.row.result === 4"> <!-- 撤回 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
</span>
</template>
</el-table-column>
<el-table-column label="提交时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="text" size="small" icon="el-icon-delete" v-if="scope.row.result === 1"
v-hasPermi="['bpm:process-instance:cancel']" @click="handleCancel(scope.row)">取消</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleDetail(scope.row)"
v-hasPermi="['bpm:process-instance:query']">详情</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import {
getMyProcessInstancePage,
createProcessInstanceExt,
updateProcessInstanceExt,
deleteProcessInstanceExt,
getProcessInstanceExt,
getProcessInstanceExtPage,
exportProcessInstanceExtExcel, cancelProcessInstance
} from "@/api/bpm/processInstance";
import {deleteErrorCode} from "@/api/system/errorCode";
export default {
name: "ProcessInstance",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 工作流的流程实例的拓展列表
list: [],
// 是否显示弹出层
dateRangeCreateTime: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
processDefinitionId: null,
category: null,
status: null,
result: null,
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
// 执行查询
getMyProcessInstancePage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 **/
handleAdd() {
this.$router.push({ path: "/bpm/process-instance/create"})
},
/** 取消按钮操作 */
handleCancel(row) {
const id = row.id;
this.$prompt('请输入取消原因?', "取消流程", {
type: 'warning',
confirmButtonText: "确定",
cancelButtonText: "取消",
inputPattern: /^[\s\S]*.*[^\s][\s\S]*$/, // 判断非空,且非空格
inputErrorMessage: "取消原因不能为空",
}).then(({ value }) => {
return cancelProcessInstance(id, value);
}).then(() => {
this.getList();
this.msgSuccess("取消成功");
})
},
/** 处理详情按钮 */
handleDetail(row) {
this.$router.push({ path: "/bpm/process-instance/detail", query: { id: row.id}});
},
}
};
</script>

View File

@@ -0,0 +1,136 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" 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="创建时间">
<el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</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>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="任务编号" align="center" prop="id" width="320" fixed />
<el-table-column label="任务名称" align="center" prop="name" width="200" />
<el-table-column label="所属流程" align="center" prop="processInstance.name" width="200" />
<el-table-column label="流程发起人" align="center" prop="processInstance.startUserNickname" width="120" />
<el-table-column label="结果" align="center" prop="result">
<template slot-scope="scope">
<span>
<el-tag type="primary" v-if="scope.row.result === 1"> <!-- 进行中 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
<el-tag type="success" v-if="scope.row.result === 2"> <!-- 通过 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
<el-tag type="danger" v-if="scope.row.result === 3"> <!-- 不通过 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
<el-tag type="info" v-if="scope.row.result === 4"> <!-- 撤回 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
</span>
</template>
</el-table-column>
<el-table-column label="审批意见" align="center" prop="comment" width="200" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批时间" align="center" prop="endTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column label="耗时" align="center" prop="durationInMillis" width="180">
<template slot-scope="scope">
<span>{{ getDateStar(scope.row.durationInMillis) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleAudit(scope.row)"
v-hasPermi="['bpm:task:query']">详情</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import {getDoneTaskPage} from '@/api/bpm/task'
import {getDate} from "@/utils/dateUtils";
export default {
name: "Done",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 已办任务列表
list: [],
// 查询参数
dateRangeCreateTime: [],
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
getDoneTaskPage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
getDateStar(ms) {
return getDate(ms);
},
/** 处理审批按钮 */
handleAudit(row) {
this.$router.push({ path: "/bpm/process-instance/detail", query: { id: row.processInstance.id}});
},
}
};
</script>

View File

@@ -0,0 +1,110 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" 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="创建时间">
<el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</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>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="任务编号" align="center" prop="id" width="320" />
<el-table-column label="任务名称" align="center" prop="name" />
<el-table-column label="所属流程" align="center" prop="processInstance.name" />
<el-table-column label="流程发起人" align="center" prop="processInstance.startUserNickname" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="version" width="80">
<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" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleAudit(scope.row)"
v-hasPermi="['bpm:task:update']">审批</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import {getTodoTaskPage} from '@/api/bpm/task'
import {listSimpleUsers} from "@/api/system/user";
export default {
name: "Todo",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 待办任务列表
list: [],
// 查询参数
dateRangeCreateTime: [],
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
getTodoTaskPage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 处理审批按钮 */
handleAudit(row) {
this.$router.push({ path: "/bpm/process-instance/detail", query: { id: row.processInstance.id}});
},
}
};
</script>

View File

@@ -0,0 +1,337 @@
<template>
<div>
<!-- 列表弹窗 -->
<el-dialog title="任务分配规则" :visible.sync="visible" width="800px" append-to-body>
<el-table v-loading="loading" :data="list">
<el-table-column label="任务名" align="center" prop="taskDefinitionName" width="120" fixed />
<el-table-column label="任务标识" align="center" prop="taskDefinitionKey" width="120" show-tooltip-when-overflow />
<el-table-column label="规则类型" align="center" prop="type" width="120">
<template slot-scope="scope">
<span>{{ getDictDataLabel(DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE, scope.row.type) }}</span>
</template>
</el-table-column>
<el-table-column label="规则范围" align="center" prop="options" width="440px">
<template slot-scope="scope">
<el-tag size="medium" v-if="scope.row.options" v-for="option in scope.row.options">
{{ getAssignRuleOptionName(scope.row.type, option) }}
</el-tag>
</template>
</el-table-column>
<el-table-column v-if="modelId" label="操作" align="center" width="80" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdateTaskAssignRule(scope.row)"
v-hasPermi="['bpm:task-assign-rule:update']">修改</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
<!-- 添加/修改弹窗 -->
<el-dialog title="修改任务规则" :visible.sync="open" width="500px" append-to-body>
<el-form ref="taskAssignRuleForm" :model="form" :rules="rules" label-width="110px">
<el-form-item label="任务名称" prop="taskDefinitionName">
<el-input v-model="form.taskDefinitionName" disabled />
</el-form-item>
<el-form-item label="任务标识" prop="taskDefinitionKey">
<el-input v-model="form.taskDefinitionKey" disabled />
</el-form-item>
<el-form-item label="规则类型" prop="type">
<el-select v-model="form.type" clearable style="width: 100%">
<el-option v-for="dict in taskAssignRuleTypeDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
<el-form-item v-if="form.type === 10" label="指定角色" prop="roleIds">
<el-select v-model="form.roleIds" multiple clearable style="width: 100%">
<el-option v-for="item in roleOptions" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
</el-select>
</el-form-item>
<el-form-item v-if="form.type === 20 || form.type === 21" label="指定部门" prop="deptIds">
<treeselect v-model="form.deptIds" :options="deptTreeOptions" multiple flat :defaultExpandLevel="3"
placeholder="请选择指定部门" :normalizer="normalizer"/>
</el-form-item>
<el-form-item v-if="form.type === 22" label="指定岗位" prop="postIds">
<el-select v-model="form.postIds" multiple clearable style="width: 100%">
<el-option v-for="item in postOptions" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
</el-select>
</el-form-item>
<el-form-item v-if="form.type === 30" label="指定用户" prop="userIds">
<el-select v-model="form.userIds" multiple clearable style="width: 100%">
<el-option v-for="item in userOptions" :key="parseInt(item.id)" :label="item.nickname" :value="parseInt(item.id)" />
</el-select>
</el-form-item>
<el-form-item v-if="form.type === 40" label="指定用户组" prop="userGroupIds">
<el-select v-model="form.userGroupIds" multiple clearable style="width: 100%">
<el-option v-for="item in userGroupOptions" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
</el-select>
</el-form-item>
<el-form-item v-if="form.type === 50" label="指定脚本" prop="scripts">
<el-select v-model="form.scripts" multiple clearable style="width: 100%">
<el-option v-for="dict in taskAssignScriptDictDatas" :key="parseInt(dict.value)"
:label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitAssignRuleForm"> </el-button>
<el-button @click="cancelAssignRuleForm"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {createTaskAssignRule, getTaskAssignRuleList, updateTaskAssignRule} from "@/api/bpm/taskAssignRule";
import {listSimpleRoles} from "@/api/system/role";
import {listSimpleDepts} from "@/api/system/dept";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import {listSimplePosts} from "@/api/system/post";
import {listSimpleUsers} from "@/api/system/user";
import {listSimpleUserGroups} from "@/api/bpm/userGroup";
export default {
name: "taskAssignRuleDialog",
components: {
Treeselect
},
data() {
return {
// 如下参数,可传递
modelId: undefined, // 流程模型的编号。如果 modelId 非空,则用于流程模型的查看与配置
processDefinitionId: undefined, // 流程定义的编号。如果 processDefinitionId 非空,则用于流程定义的查看,不支持配置
visible: false,
// 任务分配规则表单
row: undefined, // 选中的流程模型
list: [], // 选中流程模型的任务分配规则们
loading: false, // 加载中
open: false, // 是否打开
form: {}, // 表单
rules: { // 表单校验规则
type: [{ required: true, message: "规则类型不能为空", trigger: "change" }],
roleIds: [{required: true, message: "指定角色不能为空", trigger: "change" }],
deptIds: [{required: true, message: "指定部门不能为空", trigger: "change" }],
postIds: [{required: true, message: "指定岗位不能为空", trigger: "change"}],
userIds: [{required: true, message: "指定用户不能为空", trigger: "change"}],
userGroupIds: [{required: true, message: "指定用户组不能为空", trigger: "change"}],
scripts: [{required: true, message: "指定脚本不能为空", trigger: "change"}],
},
// 各种下拉框
roleOptions: [],
deptOptions: [],
deptTreeOptions: [],
postOptions: [],
userOptions: [],
userGroupOptions: [],
// 数据字典
modelFormTypeDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_FORM_TYPE),
taskAssignRuleTypeDictDatas: getDictDatas(DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE),
taskAssignScriptDictDatas: getDictDatas(DICT_TYPE.BPM_TASK_ASSIGN_SCRIPT),
};
},
methods: {
initModel(modelId) {
this.modelId = modelId;
this.processDefinitionId = undefined;
// 初始化所有下拉框
this.init0();
},
initProcessDefinition(processDefinitionId) {
this.modelId = undefined;
this.processDefinitionId = processDefinitionId;
// 初始化所有下拉框
this.init0();
},
/** 初始化 */
init0() {
// 设置可见
this.visible = true;
// 获得列表
this.getList();
// 获得角色列表
this.roleOptions = [];
listSimpleRoles().then(response => {
this.roleOptions.push(...response.data);
});
// 获得部门列表
this.deptOptions = [];
this.deptTreeOptions = [];
listSimpleDepts().then(response => {
this.deptOptions.push(...response.data);
this.deptTreeOptions.push(...this.handleTree(response.data, "id"));
});
// 获得岗位列表
this.postOptions = [];
listSimplePosts().then(response => {
this.postOptions.push(...response.data);
});
// 获得用户列表
this.userOptions = [];
listSimpleUsers().then(response => {
this.userOptions.push(...response.data);
});
// 获得用户组列表
this.userGroupOptions = [];
listSimpleUserGroups().then(response => {
this.userGroupOptions.push(...response.data);
});
},
/** 获得任务分配规则列表 */
getList() {
this.loading = true;
getTaskAssignRuleList({
modelId: this.modelId,
processDefinitionId: this.processDefinitionId,
}).then(response => {
this.loading = false;
this.list = response.data;
})
},
/** 处理修改任务分配规则的按钮操作 */
handleUpdateTaskAssignRule(row) {
// 先重置标识
this.resetAssignRuleForm();
// 设置表单
this.form = {
...row,
options: [],
roleIds: [],
deptIds: [],
postIds: [],
userIds: [],
userGroupIds: [],
scripts: [],
};
// 将 options 赋值到对应的 roleIds 等选项
if (row.type === 10) {
this.form.roleIds.push(...row.options);
} else if (row.type === 20 || row.type === 21) {
this.form.deptIds.push(...row.options);
} else if (row.type === 22) {
this.form.postIds.push(...row.options);
} else if (row.type === 30) {
this.form.userIds.push(...row.options);
} else if (row.type === 40) {
this.form.userGroupIds.push(...row.options);
} else if (row.type === 50) {
this.form.scripts.push(...row.options);
}
this.open = true;
},
/** 提交任务分配规则的表单 */
submitAssignRuleForm() {
this.$refs["taskAssignRuleForm"].validate(valid => {
if (valid) {
// 构建表单
let form = {
...this.form,
taskDefinitionName: undefined,
};
// 将 roleIds 等选项赋值到 options 中
if (form.type === 10) {
form.options = form.roleIds;
} else if (form.type === 20 || form.type === 21) {
form.options = form.deptIds;
} else if (form.type === 22) {
form.options = form.postIds;
} else if (form.type === 30) {
form.options = form.userIds;
} else if (form.type === 40) {
form.options = form.userGroupIds;
} else if (form.type === 50) {
form.options = form.scripts;
}
form.roleIds = undefined;
form.deptIds = undefined;
form.postIds = undefined;
form.userIds = undefined;
form.userGroupIds = undefined;
form.scripts = undefined;
// 新增
if (!form.id) {
form.modelId = this.modelId; // 模型编号
createTaskAssignRule(form).then(response => {
this.msgSuccess("修改成功");
this.open = false;
this.getList();
});
// 修改
} else {
form.taskDefinitionKey = undefined; // 无法修改
updateTaskAssignRule(form).then(response => {
this.msgSuccess("修改成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 取消任务分配规则的表单 */
cancelAssignRuleForm() {
this.open = false;
this.resetAssignRuleForm();
},
/** 表单重置 */
resetAssignRuleForm() {
this.form = {};
this.resetForm("taskAssignRuleForm");
},
getAssignRuleOptionName(type, option) {
if (type === 10) {
for (const roleOption of this.roleOptions) {
if (roleOption.id === option) {
return roleOption.name;
}
}
} else if (type === 20 || type === 21) {
for (const deptOption of this.deptOptions) {
if (deptOption.id === option) {
return deptOption.name;
}
}
} else if (type === 22) {
for (const postOption of this.postOptions) {
if (postOption.id === option) {
return postOption.name;
}
}
} else if (type === 30) {
for (const userOption of this.userOptions) {
if (userOption.id === option) {
return userOption.nickname;
}
}
} else if (type === 40) {
for (const userGroupOption of this.userGroupOptions) {
if (userGroupOption.id === option) {
return userGroupOption.name;
}
}
} else if (type === 50) {
option = option + ''; // 转换成 string
for (const dictData of this.taskAssignScriptDictDatas) {
if (dictData.value === option) {
return dictData.label;
}
}
}
return '未知(' + option + ')';
},
// 格式化部门的下拉框
normalizer(node) {
return {
id: node.id,
label: node.name,
children: node.children
}
}
}
};
</script>