Commit e6fadf073af25eee020d390d337d1aaf55857984

Authored by YunaiV
1 parent 2f46e230

bpm:初始化流程实例的代码

yudao-ui-admin-vue3/src/views/bpm/processInstance/create.vue 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 第一步,通过流程定义的列表,选择对应的流程 -->
  4 + <div v-if="!selectProcessInstance">
  5 + <XTable @register="registerTable">
  6 + <template #version_default="{ row }">
  7 + <el-tag v-if="row">v{{ row.version }}</el-tag>
  8 + </template>
  9 + <template #actionbtns_default="{ row }">
  10 + <el-button type="text" size="small" icon="el-icon-plus" @click="handleSelect(row)"
  11 + >选择</el-button
  12 + >
  13 + </template>
  14 + </XTable>
  15 + </div>
  16 + <!-- 第二步,填写表单,进行流程的提交 -->
  17 + <div v-else>
  18 + <el-card class="box-card">
  19 + <div class="clearfix">
  20 + <span class="el-icon-document">申请信息【{{ selectProcessInstance.name }}】</span>
  21 + <el-button style="float: right" type="primary" @click="selectProcessInstance = undefined"
  22 + >选择其它流程</el-button
  23 + >
  24 + </div>
  25 + <el-col :span="16" :offset="6">
  26 + <div>
  27 + <!-- <parser :key="new Date().getTime()" :form-conf="detailForm" @submit="submitForm" /> -->
  28 + </div>
  29 + </el-col>
  30 + </el-card>
  31 + <el-card class="box-card">
  32 + <div class="clearfix">
  33 + <span class="el-icon-picture-outline">流程图</span>
  34 + </div>
  35 + <!-- <my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" /> -->
  36 + </el-card>
  37 + </div>
  38 + </div>
  39 +</template>
  40 +
  41 +<script setup lang="ts">
  42 +import { reactive, ref } from 'vue'
  43 +import { useXTable } from '@/hooks/web/useXTable'
  44 +import * as definitionApi from '@/api/bpm/definition'
  45 +// import {DICT_TYPE, getDictDatas} from "@/utils/dict";
  46 +import { decodeFields } from '@/utils/formGenerator'
  47 +// import Parser from '@/components/parser/Parser'
  48 +// import * as processInstanceApi from "@/api/bpm/processInstance";
  49 +import { allSchemas } from './process.create'
  50 +import { useRouter } from 'vue-router'
  51 +const router = useRouter()
  52 +
  53 +const queryParams = reactive({
  54 + suspensionState: 1
  55 +})
  56 +const [registerTable] = useXTable({
  57 + allSchemas: allSchemas,
  58 + params: queryParams,
  59 + getListApi: definitionApi.getProcessDefinitionListApi
  60 +})
  61 +
  62 +// 流程表单详情
  63 +const detailForm = ref({
  64 + fields: []
  65 +})
  66 +
  67 +// // BPMN 数据
  68 +const bpmnXML = ref(null)
  69 +// const bpmnControlForm=ref( {
  70 +// prefix: "flowable"
  71 +// })
  72 +
  73 +// 流程表单
  74 +const selectProcessInstance = ref(undefined) // 选择的流程实例
  75 +/** 处理选择流程的按钮操作 **/
  76 +const handleSelect = (row) => {
  77 + // 设置选择的流程
  78 + selectProcessInstance.value = row
  79 +
  80 + // 流程表单
  81 + if (row.formId) {
  82 + // 设置对应的表单
  83 + detailForm.value = {
  84 + ...JSON.parse(row.formConf),
  85 + fields: decodeFields([], row.formFields)
  86 + }
  87 +
  88 + // 加载流程图
  89 + definitionApi.getProcessDefinitionBpmnXMLApi(row.id).then((response) => {
  90 + bpmnXML.value = response.data
  91 + })
  92 + } else if (row.formCustomCreatePath) {
  93 + router.push({ path: row.formCustomCreatePath })
  94 + // 这里暂时无需加载流程图,因为跳出到另外个 Tab;
  95 + }
  96 +}
  97 +/** 提交按钮 */
  98 +// const submitForm=(params)=> {
  99 +// if (!params) {
  100 +// return;
  101 +// }
  102 +// // 设置表单禁用
  103 +// const conf = params.conf;
  104 +// conf.disabled = true; // 表单禁用
  105 +// conf.formBtns = false; // 按钮隐藏
  106 +
  107 +// // 提交表单,创建流程
  108 +// const variables = params.values;
  109 +// await processInstanceApi.createProcessInstanceApi({
  110 +// processDefinitionId: this.selectProcessInstance.id,
  111 +// variables: variables
  112 +// }).then(response => {
  113 +// this.$modal.msgSuccess("发起流程成功");
  114 +// // 关闭当前窗口
  115 +// this.$tab.closeOpenPage();
  116 +// this.$router.go(-1);
  117 +// }).catch(() => {
  118 +// conf.disabled = false; // 表单开启
  119 +// conf.formBtns = true; // 按钮展示
  120 +// })
  121 +// }
  122 +</script>
  123 +
  124 +<style lang="scss">
  125 +.my-process-designer {
  126 + height: calc(100vh - 200px);
  127 +}
  128 +
  129 +.box-card {
  130 + width: 100%;
  131 + margin-bottom: 20px;
  132 +}
  133 +</style>
... ...
yudao-ui-admin-vue3/src/views/bpm/processInstance/detail.vue 0 → 100644
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 审批信息 -->
  4 + <el-card
  5 + class="box-card"
  6 + v-loading="processInstanceLoading"
  7 + v-for="(item, index) in runningTasks"
  8 + :key="index"
  9 + >
  10 + <div slot="header" class="clearfix">
  11 + <span class="el-icon-picture-outline">审批任务【{{ item.name }}】</span>
  12 + </div>
  13 + <el-col :span="16" :offset="6">
  14 + <el-form
  15 + :ref="'form' + index"
  16 + :model="auditForms[index]"
  17 + :rules="auditRule"
  18 + label-width="100px"
  19 + >
  20 + <el-form-item label="流程名" v-if="processInstance && processInstance.name">
  21 + {{ processInstance.name }}
  22 + </el-form-item>
  23 + <el-form-item label="流程发起人" v-if="processInstance && processInstance.startUser">
  24 + {{ processInstance.startUser.nickname }}
  25 + <el-tag type="info" size="mini">{{ processInstance.startUser.deptName }}</el-tag>
  26 + </el-form-item>
  27 + <el-form-item label="审批建议" prop="reason">
  28 + <el-input
  29 + type="textarea"
  30 + v-model="auditForms[index].reason"
  31 + placeholder="请输入审批建议"
  32 + />
  33 + </el-form-item>
  34 + </el-form>
  35 + <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">
  36 + <el-button
  37 + icon="el-icon-edit-outline"
  38 + type="success"
  39 + size="mini"
  40 + @click="handleAudit(item, true)"
  41 + >通过</el-button
  42 + >
  43 + <el-button
  44 + icon="el-icon-circle-close"
  45 + type="danger"
  46 + size="mini"
  47 + @click="handleAudit(item, false)"
  48 + >不通过</el-button
  49 + >
  50 + <el-button
  51 + icon="el-icon-edit-outline"
  52 + type="primary"
  53 + size="mini"
  54 + @click="handleUpdateAssignee(item)"
  55 + >转办</el-button
  56 + >
  57 + <el-button
  58 + icon="el-icon-edit-outline"
  59 + type="primary"
  60 + size="mini"
  61 + @click="handleDelegate(item)"
  62 + >委派</el-button
  63 + >
  64 + <el-button
  65 + icon="el-icon-refresh-left"
  66 + type="warning"
  67 + size="mini"
  68 + @click="handleBack(item)"
  69 + >退回</el-button
  70 + >
  71 + </div>
  72 + </el-col>
  73 + </el-card>
  74 + <!-- 申请信息 -->
  75 + <el-card class="box-card" v-loading="processInstanceLoading">
  76 + <div slot="header" class="clearfix">
  77 + <span class="el-icon-document">申请信息【{{ processInstance.name }}】</span>
  78 + </div>
  79 + <el-col
  80 + v-if="
  81 + this.processInstance.processDefinition &&
  82 + this.processInstance.processDefinition.formType === 10
  83 + "
  84 + :span="16"
  85 + :offset="6"
  86 + >
  87 + <div>
  88 + <parser :key="new Date().getTime()" :form-conf="detailForm" />
  89 + </div>
  90 + </el-col>
  91 + <div
  92 + v-if="
  93 + this.processInstance.processDefinition &&
  94 + this.processInstance.processDefinition.formType === 20
  95 + "
  96 + >
  97 + <router-link
  98 + :to="
  99 + this.processInstance.processDefinition.formCustomViewPath +
  100 + '?id=' +
  101 + this.processInstance.businessKey
  102 + "
  103 + >
  104 + <el-button type="primary">点击查看</el-button>
  105 + </router-link>
  106 + </div>
  107 + </el-card>
  108 +
  109 + <!-- 审批记录 -->
  110 + <el-card class="box-card" v-loading="tasksLoad">
  111 + <div slot="header" class="clearfix">
  112 + <span class="el-icon-picture-outline">审批记录</span>
  113 + </div>
  114 + <el-col :span="16" :offset="4">
  115 + <div class="block">
  116 + <el-timeline>
  117 + <el-timeline-item
  118 + v-for="(item, index) in tasks"
  119 + :key="index"
  120 + :icon="getTimelineItemIcon(item)"
  121 + :type="getTimelineItemType(item)"
  122 + >
  123 + <p style="font-weight: 700">任务:{{ item.name }}</p>
  124 + <el-card :body-style="{ padding: '10px' }">
  125 + <label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px">
  126 + 审批人:{{ item.assigneeUser.nickname }}
  127 + <el-tag type="info" size="mini">{{ item.assigneeUser.deptName }}</el-tag>
  128 + </label>
  129 + <label style="font-weight: normal" v-if="item.createTime">创建时间:</label>
  130 + <label style="color: #8a909c; font-weight: normal">{{
  131 + parseTime(item.createTime)
  132 + }}</label>
  133 + <label v-if="item.endTime" style="margin-left: 30px; font-weight: normal"
  134 + >审批时间:</label
  135 + >
  136 + <label v-if="item.endTime" style="color: #8a909c; font-weight: normal">
  137 + {{ parseTime(item.endTime) }}</label
  138 + >
  139 + <label v-if="item.durationInMillis" style="margin-left: 30px; font-weight: normal"
  140 + >耗时:</label
  141 + >
  142 + <label v-if="item.durationInMillis" style="color: #8a909c; font-weight: normal">
  143 + {{ getDateStar(item.durationInMillis) }}
  144 + </label>
  145 + <p v-if="item.reason">
  146 + <el-tag :type="getTimelineItemType(item)">{{ item.reason }}</el-tag>
  147 + </p>
  148 + </el-card>
  149 + </el-timeline-item>
  150 + </el-timeline>
  151 + </div>
  152 + </el-col>
  153 + </el-card>
  154 +
  155 + <!-- 高亮流程图 -->
  156 + <el-card class="box-card" v-loading="processInstanceLoading">
  157 + <div slot="header" class="clearfix">
  158 + <span class="el-icon-picture-outline">流程图</span>
  159 + </div>
  160 + <my-process-viewer
  161 + key="designer"
  162 + v-model="bpmnXML"
  163 + v-bind="bpmnControlForm"
  164 + :activityData="activityList"
  165 + :processInstanceData="processInstance"
  166 + :taskData="tasks"
  167 + />
  168 + </el-card>
  169 +
  170 + <!-- 对话框(转派审批人) -->
  171 + <el-dialog title="转派审批人" :visible.sync="updateAssignee.open" width="500px" append-to-body>
  172 + <el-form
  173 + ref="updateAssigneeForm"
  174 + :model="updateAssignee.form"
  175 + :rules="updateAssignee.rules"
  176 + label-width="110px"
  177 + >
  178 + <el-form-item label="新审批人" prop="assigneeUserId">
  179 + <el-select v-model="updateAssignee.form.assigneeUserId" clearable style="width: 100%">
  180 + <el-option
  181 + v-for="item in userOptions"
  182 + :key="parseInt(item.id)"
  183 + :label="item.nickname"
  184 + :value="parseInt(item.id)"
  185 + />
  186 + </el-select>
  187 + </el-form-item>
  188 + </el-form>
  189 + <div slot="footer" class="dialog-footer">
  190 + <el-button type="primary" @click="submitUpdateAssigneeForm">确 定</el-button>
  191 + <el-button @click="cancelUpdateAssigneeForm">取 消</el-button>
  192 + </div>
  193 + </el-dialog>
  194 + </div>
  195 +</template>
  196 +
  197 +<script>
  198 +import { getProcessDefinitionBpmnXML } from "@/api/bpm/definition"
  199 +import { DICT_TYPE, getDictOptions } from "@/utils/dict"
  200 +import store from "@/store"
  201 +import { decodeFields } from "@/utils/formGenerator"
  202 +import Parser from '@/components/parser/Parser'
  203 +import { getProcessInstanceApi } from "@/api/bpm/processInstance"
  204 +import { approveTask, getTaskListByProcessInstanceId, rejectTask, updateTaskAssignee } from "@/api/bpm/task"
  205 +import { getDate } from "@/utils/dateUtils"
  206 +import { getListSimpleUsersApi } from "@/api/system/user"
  207 +import { getActivityList } from "@/api/bpm/activity"
  208 +
  209 +// 流程实例的详情页,可用于审批
  210 +export default {
  211 + name: "ProcessInstanceDetail",
  212 + components: {
  213 + Parser
  214 + },
  215 + data () {
  216 + return {
  217 + // 遮罩层
  218 + processInstanceLoading: true,
  219 + // 流程实例
  220 + id: undefined, // 流程实例的编号
  221 + processInstance: {},
  222 +
  223 + // 流程表单详情
  224 + detailForm: {
  225 + fields: []
  226 + },
  227 +
  228 + // BPMN 数据
  229 + bpmnXML: null,
  230 + bpmnControlForm: {
  231 + prefix: "flowable"
  232 + },
  233 + activityList: [],
  234 +
  235 + // 审批记录
  236 + tasksLoad: true,
  237 + tasks: [],
  238 +
  239 + // 审批表单
  240 + runningTasks: [],
  241 + auditForms: [],
  242 + auditRule: {
  243 + reason: [{ required: true, message: "审批建议不能为空", trigger: "blur" }],
  244 + },
  245 +
  246 + // 转派审批人
  247 + userOptions: [],
  248 + updateAssignee: {
  249 + open: false,
  250 + form: {
  251 + assigneeUserId: undefined,
  252 + },
  253 + rules: {
  254 + assigneeUserId: [{ required: true, message: "新审批人不能为空", trigger: "change" }],
  255 + }
  256 + },
  257 +
  258 + // 数据字典
  259 + categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
  260 + }
  261 + },
  262 + created () {
  263 + this.id = this.$route.query.id
  264 + if (!this.id) {
  265 + this.$message.error('未传递 id 参数,无法查看流程信息')
  266 + return
  267 + }
  268 + this.getDetail()
  269 +
  270 + // 获得用户列表
  271 + this.userOptions = []
  272 + getListSimpleUsersApi().then(response => {
  273 + this.userOptions.push(...response.data)
  274 + })
  275 + },
  276 + methods: {
  277 + /** 获得流程实例 */
  278 + getDetail () {
  279 + // 获得流程实例相关
  280 + this.processInstanceLoading = true
  281 + getProcessInstanceApi(this.id).then(response => {
  282 + if (!response.data) {
  283 + this.$message.error('查询不到流程信息!')
  284 + return
  285 + }
  286 + // 设置流程信息
  287 + this.processInstance = response.data
  288 +
  289 + // 设置表单信息
  290 + if (this.processInstance.processDefinition.formType === 10) {
  291 + this.detailForm = {
  292 + ...JSON.parse(this.processInstance.processDefinition.formConf),
  293 + disabled: true, // 表单禁用
  294 + formBtns: false, // 按钮隐藏
  295 + fields: decodeFields(this.processInstance.processDefinition.formFields)
  296 + }
  297 + // 设置表单的值
  298 + this.detailForm.fields.forEach(item => {
  299 + const val = this.processInstance.formVariables[item.__vModel__]
  300 + if (val) {
  301 + item.__config__.defaultValue = val
  302 + }
  303 + })
  304 + }
  305 +
  306 + // 加载流程图
  307 + getProcessDefinitionBpmnXML(this.processInstance.processDefinition.id).then(response => {
  308 + this.bpmnXML = response.data
  309 + })
  310 + // 加载活动列表
  311 + getActivityList({
  312 + processInstanceId: this.processInstance.id
  313 + }).then(response => {
  314 + this.activityList = response.data
  315 + })
  316 +
  317 + // 取消加载中
  318 + this.processInstanceLoading = false
  319 + })
  320 +
  321 + // 获得流程任务列表(审批记录)
  322 + this.tasksLoad = true
  323 + this.runningTasks = []
  324 + this.auditForms = []
  325 + getTaskListByProcessInstanceId(this.id).then(response => {
  326 + // 审批记录
  327 + this.tasks = []
  328 + // 移除已取消的审批
  329 + response.data.forEach(task => {
  330 + if (task.result !== 4) {
  331 + this.tasks.push(task)
  332 + }
  333 + })
  334 + // 排序,将未完成的排在前面,已完成的排在后面;
  335 + this.tasks.sort((a, b) => {
  336 + // 有已完成的情况,按照完成时间倒序
  337 + if (a.endTime && b.endTime) {
  338 + return b.endTime - a.endTime
  339 + } else if (a.endTime) {
  340 + return 1
  341 + } else if (b.endTime) {
  342 + return -1
  343 + // 都是未完成,按照创建时间倒序
  344 + } else {
  345 + return b.createTime - a.createTime
  346 + }
  347 + })
  348 +
  349 + // 需要审核的记录
  350 + const userId = store.getters.userId
  351 + this.tasks.forEach(task => {
  352 + if (task.result !== 1) { // 只有待处理才需要
  353 + return
  354 + }
  355 + if (!task.assigneeUser || task.assigneeUser.id !== userId) { // 自己不是处理人
  356 + return
  357 + }
  358 + this.runningTasks.push({ ...task })
  359 + this.auditForms.push({
  360 + reason: ''
  361 + })
  362 + })
  363 +
  364 + // 取消加载中
  365 + this.tasksLoad = false
  366 + })
  367 + },
  368 + /** 处理选择流程的按钮操作 **/
  369 + handleSelect (row) {
  370 + // 设置选择的流程
  371 + this.selectProcessInstance = row
  372 +
  373 + // 流程表单
  374 + if (row.formId) {
  375 + // 设置对应的表单
  376 + this.detailForm = {
  377 + ...JSON.parse(row.formConf),
  378 + fields: decodeFields(row.formFields)
  379 + }
  380 + } else if (row.formCustomCreatePath) {
  381 + this.$router.push({ path: row.formCustomCreatePath })
  382 + // 这里暂时无需加载流程图,因为跳出到另外个 Tab;
  383 + }
  384 + },
  385 + getDateStar (ms) {
  386 + return getDate(ms)
  387 + },
  388 + getTimelineItemIcon (item) {
  389 + if (item.result === 1) {
  390 + return 'el-icon-time'
  391 + }
  392 + if (item.result === 2) {
  393 + return 'el-icon-check'
  394 + }
  395 + if (item.result === 3) {
  396 + return 'el-icon-close'
  397 + }
  398 + if (item.result === 4) {
  399 + return 'el-icon-remove-outline'
  400 + }
  401 + return ''
  402 + },
  403 + getTimelineItemType (item) {
  404 + if (item.result === 1) {
  405 + return 'primary'
  406 + }
  407 + if (item.result === 2) {
  408 + return 'success'
  409 + }
  410 + if (item.result === 3) {
  411 + return 'danger'
  412 + }
  413 + if (item.result === 4) {
  414 + return 'info'
  415 + }
  416 + return ''
  417 + },
  418 + /** 处理审批通过和不通过的操作 */
  419 + handleAudit (task, pass) {
  420 + const index = this.runningTasks.indexOf(task)
  421 + this.$refs['form' + index][0].validate(valid => {
  422 + if (!valid) {
  423 + return
  424 + }
  425 + const data = {
  426 + id: task.id,
  427 + reason: this.auditForms[index].reason
  428 + }
  429 + if (pass) {
  430 + approveTask(data).then(response => {
  431 + this.$modal.msgSuccess("审批通过成功!")
  432 + this.getDetail() // 获得最新详情
  433 + })
  434 + } else {
  435 + rejectTask(data).then(response => {
  436 + this.$modal.msgSuccess("审批不通过成功!")
  437 + this.getDetail() // 获得最新详情
  438 + })
  439 + }
  440 + })
  441 + },
  442 + /** 处理转派审批人 */
  443 + handleUpdateAssignee (task) {
  444 + // 设置表单
  445 + this.resetUpdateAssigneeForm()
  446 + this.updateAssignee.form.id = task.id
  447 + // 设置为打开
  448 + this.updateAssignee.open = true
  449 + },
  450 + /** 提交转派审批人 */
  451 + submitUpdateAssigneeForm () {
  452 + this.$refs['updateAssigneeForm'].validate(valid => {
  453 + if (!valid) {
  454 + return
  455 + }
  456 + updateTaskAssignee(this.updateAssignee.form).then(response => {
  457 + this.$modal.msgSuccess("转派任务成功!")
  458 + this.updateAssignee.open = false
  459 + this.getDetail() // 获得最新详情
  460 + })
  461 + })
  462 + },
  463 + /** 取消转派审批人 */
  464 + cancelUpdateAssigneeForm () {
  465 + this.updateAssignee.open = false
  466 + this.resetUpdateAssigneeForm()
  467 + },
  468 + /** 重置转派审批人 */
  469 + resetUpdateAssigneeForm () {
  470 + this.updateAssignee.form = {
  471 + id: undefined,
  472 + assigneeUserId: undefined,
  473 + }
  474 + this.resetForm("updateAssigneeForm")
  475 + },
  476 + /** 处理审批退回的操作 */
  477 + handleDelegate (task) {
  478 + this.$modal.msgError("暂不支持【委派】功能,可以使用【转派】替代!")
  479 + },
  480 + /** 处理审批退回的操作 */
  481 + handleBack (task) {
  482 + this.$modal.msgError("暂不支持【退回】功能!")
  483 + // 可参考 http://blog.wya1.com/article/636697030/details/7296
  484 + // const data = {
  485 + // id: task.id,
  486 + // assigneeUserId: 1
  487 + // }
  488 + // backTask(data).then(response => {
  489 + // this.$modal.msgSuccess("回退成功!");
  490 + // this.getDetail(); // 获得最新详情
  491 + // });
  492 + }
  493 + }
  494 +};
  495 +</script>
  496 +
  497 +<style lang="scss">
  498 +.my-process-designer {
  499 + height: calc(100vh - 200px);
  500 +}
  501 +
  502 +.box-card {
  503 + width: 100%;
  504 + margin-bottom: 20px;
  505 +}
  506 +</style>
... ...
yudao-ui-admin-vue3/src/views/bpm/processInstance/index.vue
1   -<script setup lang="ts" name="ProcessInstance">
2   -import dayjs from 'dayjs'
3   -import { DICT_TYPE } from '@/utils/dict'
4   -import { useTable } from '@/hooks/web/useTable'
5   -import type { ProcessInstanceVO } from '@/api/bpm/processInstance/types'
6   -import { allSchemas } from './process.data'
  1 +<template>
  2 + <ContentWrap>
  3 + <!-- 列表 -->
  4 + <XTable @register="registerTable">
  5 + <template #toolbar_buttons>
  6 + <!-- 操作:新增 -->
  7 + <XButton
  8 + type="primary"
  9 + preIcon="ep:zoom-in"
  10 + title="新建流程"
  11 + v-hasPermi="['bpm:process-instance:query']"
  12 + @click="handleCreate"
  13 + />
  14 + </template>
  15 + <!-- 当前审批任务 -->
  16 + <template #tasks_default="{ row }">
  17 + <el-button
  18 + v-for="task in row.tasks"
  19 + :key="task.id"
  20 + type="text"
  21 + @click="handleFormDetail(task.id)"
  22 + >
  23 + <span>{{ task.name }}</span>
  24 + </el-button>
  25 + </template>
  26 + <!-- 操作 -->
  27 + <template #actionbtns_default="{ row }">
  28 + <XButton
  29 + type="primary"
  30 + title="取消"
  31 + v-if="row.result === 1"
  32 + preIcon="ep:delete"
  33 + @click="handleCancel(row)"
  34 + />
  35 + <XButton type="primary" title="详情" preIcon="ep:edit-pen" @click="handleDetail(row)" />
  36 + </template>
  37 + </XTable>
  38 + </ContentWrap>
  39 +</template>
  40 +<script setup lang="ts">
  41 +// 全局相关的 import
  42 +import { ref } from 'vue'
  43 +import { ElMessage, ElMessageBox } from 'element-plus'
  44 +import { useXTable } from '@/hooks/web/useXTable'
  45 +import { useRouter } from 'vue-router'
  46 +
  47 +// 业务相关的 import
7 48 import * as ProcessInstanceApi from '@/api/bpm/processInstance'
8   -import { ElMessageBox } from 'element-plus'
9   -const { t } = useI18n() // 国际化
10   -const message = useMessage()
  49 +// import { decodeFields } from '@/utils/formGenerator' // TODO 芋艿:后续根据情况清理
  50 +import { allSchemas } from './process.data'
  51 +
  52 +const router = useRouter() // 路由
11 53  
12 54 // ========== 列表相关 ==========
13   -const { register, tableObject, methods } = useTable<ProcessInstanceVO>({
  55 +const [registerTable] = useXTable({
  56 + allSchemas: allSchemas,
14 57 getListApi: ProcessInstanceApi.getMyProcessInstancePageApi
15 58 })
16   -const { getList, setSearchParams } = methods
  59 +// 流程表单的弹出框
  60 +const detailOpen = ref(false)
  61 +const detailForm = ref()
  62 +/** 发起流程操作 **/
  63 +const handleCreate = () => {
  64 + router.push({
  65 + name: 'BpmProcessInstanceCreate'
  66 + })
  67 +}
  68 +/** 流程表单的详情按钮操作 */
  69 +const handleFormDetail = (row) => {
  70 + // 情况一:使用流程表单
  71 + if (row.formId) {
  72 + // 设置值 TODO 芋艿:动态表单做完后,需要测试下
  73 + detailForm.value = {
  74 + ...JSON.parse(row.formConf),
  75 + fields: decodeFields([], row.formFields)
  76 + }
  77 + // 弹窗打开
  78 + detailOpen.value = true
  79 + // 情况二:使用业务表单
  80 + } else if (row.formCustomCreatePath) {
  81 + router.push({ path: row.formCustomCreatePath })
  82 + }
  83 +}
17 84  
18   -// ========== CRUD 相关 ==========
19   -const dialogVisible = ref(false) // 是否显示弹出层
20   -// 发起流程
21   -const handleAdd = () => {
22   - console.info('add')
  85 +// 列表操作
  86 +const handleDetail = (row) => {
  87 + console.log(row, 'row')
  88 + router.push({ path: '/process-instance/detail', query: { id: row.id } })
23 89 }
24   -// 取消操作
25   -const handleCancel = (row: ProcessInstanceVO) => {
  90 +/** 取消按钮操作 */
  91 +const handleCancel = (row) => {
  92 + const id = row.id
26 93 ElMessageBox.prompt('请输入取消原因?', '取消流程', {
27   - confirmButtonText: t('common.ok'),
28   - cancelButtonText: t('common.cancel'),
29 94 type: 'warning',
30   - inputPattern: /^[\s\S]*.*[^\s][\s\S]*$/, // 判断非空,且非空格
  95 + confirmButtonText: '确定',
  96 + cancelButtonText: '取消',
  97 + inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
31 98 inputErrorMessage: '取消原因不能为空'
32   - }).then(async ({ value }) => {
33   - await ProcessInstanceApi.cancelProcessInstanceApi(row.id, value)
34   - message.success('取消成功')
35   - getList()
36 99 })
  100 + .then(({ value }) => {
  101 + return ProcessInstanceApi.cancelProcessInstanceApi(id, value)
  102 + })
  103 + .then(() => {
  104 + ElMessage({
  105 + message: '取消成功',
  106 + type: 'success'
  107 + })
  108 + })
37 109 }
38   -
39   -// ========== 详情相关 ==========
40   -const detailData = ref() // 详情 Ref
41   -
42   -// 详情操作
43   -const handleDetail = async (row: ProcessInstanceVO) => {
44   - // 设置数据
45   - detailData.value = row
46   - dialogVisible.value = true
47   -}
48   -
49   -// ========== 初始化 ==========
50   -getList()
51 110 </script>
52   -
53   -<template>
54   - <!-- 搜索工作区 -->
55   - <ContentWrap>
56   - <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
57   - </ContentWrap>
58   - <ContentWrap>
59   - <!-- 操作工具栏 -->
60   - <div class="mb-10px">
61   - <el-button type="primary" v-hasPermi="['bpm:process-instance:query']" @click="handleAdd">
62   - <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
63   - </el-button>
64   - </div>
65   - <!-- 列表 -->
66   - <Table
67   - :columns="allSchemas.tableColumns"
68   - :selection="false"
69   - :data="tableObject.tableList"
70   - :loading="tableObject.loading"
71   - :pagination="{
72   - total: tableObject.total
73   - }"
74   - v-model:pageSize="tableObject.pageSize"
75   - v-model:currentPage="tableObject.currentPage"
76   - @register="register"
77   - >
78   - <template #status="{ row }">
79   - <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
80   - </template>
81   - <template #createTime="{ row }">
82   - <span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
83   - </template>
84   - <template #action="{ row }">
85   - <el-button
86   - link
87   - type="primary"
88   - v-hasPermi="['bpm:process-instance:query']"
89   - @click="handleDetail(row)"
90   - >
91   - <Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}
92   - </el-button>
93   - <el-button
94   - link
95   - type="primary"
96   - v-hasPermi="['bpm:process-instance:cancel']"
97   - @click="handleCancel(row)"
98   - >
99   - <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
100   - </el-button>
101   - </template>
102   - </Table>
103   - </ContentWrap>
104   -
105   - <XModal v-model="dialogVisible" :title="t('action.detail')">
106   - <!-- 对话框(详情) -->
107   - <Descriptions :schema="allSchemas.detailSchema" :data="detailData" />
108   - <!-- 操作按钮 -->
109   - <template #footer>
110   - <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
111   - </template>
112   - </XModal>
113   -</template>
... ...
yudao-ui-admin-vue3/src/views/bpm/processInstance/process.create.ts 0 → 100644
  1 +import { reactive } from 'vue'
  2 +// import { useI18n } from '@/hooks/web/useI18n'
  3 +import { VxeCrudSchema, useVxeCrudSchemas } from '@/hooks/web/useVxeCrudSchemas'
  4 +import { DICT_TYPE } from '@/utils/dict'
  5 +// const { t } = useI18n() // 国际化
  6 +
  7 +// CrudSchema
  8 +const crudSchemas = reactive<VxeCrudSchema>({
  9 + primaryKey: 'id',
  10 + primaryType: 'id',
  11 + primaryTitle: '流程名称',
  12 + action: true,
  13 + actionWidth: '200px',
  14 + columns: [
  15 + {
  16 + title: '流程分类',
  17 + field: 'category',
  18 + dictType: DICT_TYPE.BPM_MODEL_CATEGORY,
  19 + dictClass: 'number'
  20 + },
  21 + {
  22 + title: '流程版本',
  23 + field: 'processDefinition.version',
  24 + table: {
  25 + slots: {
  26 + default: 'version_default'
  27 + }
  28 + }
  29 + },
  30 + {
  31 + title: '流程描述',
  32 + field: 'description'
  33 + }
  34 + ]
  35 +})
  36 +export const { allSchemas } = useVxeCrudSchemas(crudSchemas)
... ...
yudao-ui-admin-vue3/src/views/bpm/processInstance/process.data.ts
1   -import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
  1 +import { reactive } from 'vue'
  2 +import { useI18n } from '@/hooks/web/useI18n'
  3 +import { VxeCrudSchema, useVxeCrudSchemas } from '@/hooks/web/useVxeCrudSchemas'
  4 +import { DICT_TYPE } from '@/utils/dict'
2 5 const { t } = useI18n() // 国际化
3 6  
4 7 // CrudSchema
5   -const crudSchemas = reactive<CrudSchema[]>([
6   - {
7   - label: t('common.index'),
8   - field: 'id',
9   - type: 'index',
10   - form: {
11   - show: false
  8 +const crudSchemas = reactive<VxeCrudSchema>({
  9 + primaryKey: 'id',
  10 + primaryType: null,
  11 + primaryTitle: '编号',
  12 + action: true,
  13 + actionWidth: '200px',
  14 + columns: [
  15 + {
  16 + title: '编号',
  17 + field: 'id',
  18 + table: {
  19 + width: 320
  20 + }
12 21 },
13   - detail: {
14   - show: false
15   - }
16   - },
17   - {
18   - label: '流程名',
19   - field: 'name',
20   - search: {
21   - show: true
22   - }
23   - },
24   - {
25   - label: '流程分类',
26   - field: 'category',
27   - dictType: DICT_TYPE.BPM_MODEL_CATEGORY,
28   - dictClass: 'number',
29   - search: {
30   - show: true
31   - }
32   - },
33   - {
34   - label: '当前审批任务',
35   - field: 'tasks'
36   - },
37   - {
38   - label: t('common.status'),
39   - field: 'status',
40   - dictType: DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS,
41   - dictClass: 'number',
42   - search: {
43   - show: true
44   - }
45   - },
46   - {
47   - label: '结果',
48   - field: 'result',
49   - dictType: DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT,
50   - dictClass: 'number',
51   - search: {
52   - show: true
53   - }
54   - },
55   - {
56   - label: '提交时间',
57   - field: 'createTime',
58   - form: {
59   - show: false
  22 + {
  23 + title: '流程名',
  24 + field: 'name',
  25 + isSearch: true
60 26 },
61   - search: {
62   - show: true
63   - }
64   - },
65   - {
66   - label: '结束时间',
67   - field: 'endTime',
68   - form: {
69   - show: false
70   - }
71   - },
72   - {
73   - label: t('table.action'),
74   - field: 'action',
75   - width: '240px',
76   - form: {
77   - show: false
  27 + {
  28 + title: '所属流程',
  29 + field: 'processDefinitionId',
  30 + isSearch: true,
  31 + isTable: false
  32 + },
  33 + {
  34 + title: '流程分类',
  35 + field: 'category',
  36 + dictType: DICT_TYPE.BPM_MODEL_CATEGORY,
  37 + dictClass: 'number',
  38 + isSearch: true
  39 + },
  40 + {
  41 + title: '当前审批任务',
  42 + field: 'tasks',
  43 + table: {
  44 + width: 100,
  45 + slots: {
  46 + default: 'tasks_default'
  47 + }
  48 + }
  49 + },
  50 + {
  51 + title: t('common.status'),
  52 + field: 'status',
  53 + dictType: DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS,
  54 + dictClass: 'number',
  55 + isSearch: true
  56 + },
  57 + {
  58 + title: '结果',
  59 + field: 'result',
  60 + dictType: DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT,
  61 + dictClass: 'number',
  62 + isSearch: true
  63 + },
  64 + {
  65 + title: '提交时间',
  66 + field: 'createTime',
  67 + formatter: 'formatDate',
  68 + table: {
  69 + width: 180
  70 + },
  71 + isForm: false,
  72 + isSearch: true,
  73 + search: {
  74 + show: true,
  75 + itemRender: {
  76 + name: 'XDataTimePicker'
  77 + }
  78 + }
78 79 },
79   - detail: {
80   - show: false
  80 + {
  81 + title: '结束时间',
  82 + field: 'endTime',
  83 + formatter: 'formatDate',
  84 + table: {
  85 + width: 180
  86 + },
  87 + isForm: false
81 88 }
82   - }
83   -])
84   -export const { allSchemas } = useCrudSchemas(crudSchemas)
  89 + ]
  90 +})
  91 +export const { allSchemas } = useVxeCrudSchemas(crudSchemas)
... ...
yudao-ui-admin-vue3/src/views/bpm/processInstance/process.detail.ts 0 → 100644
  1 +import { reactive } from 'vue'
  2 +// import { useI18n } from '@/hooks/web/useI18n'
  3 +import { VxeCrudSchema, useVxeCrudSchemas } from '@/hooks/web/useVxeCrudSchemas'
  4 +import { DICT_TYPE } from '@/utils/dict'
  5 +// const { t } = useI18n() // 国际化
  6 +
  7 +// CrudSchema
  8 +const crudSchemas = reactive<VxeCrudSchema>({
  9 + primaryKey: 'id',
  10 + primaryType: 'id',
  11 + primaryTitle: '流程名称',
  12 + action: true,
  13 + actionWidth: '200px',
  14 + columns: [
  15 + {
  16 + title: '流程分类',
  17 + field: 'category',
  18 + dictType: DICT_TYPE.BPM_MODEL_CATEGORY,
  19 + dictClass: 'number'
  20 + },
  21 + {
  22 + title: '流程版本',
  23 + field: 'processDefinition.version',
  24 + table: {
  25 + slots: {
  26 + default: 'version_default'
  27 + }
  28 + }
  29 + },
  30 + {
  31 + title: '流程描述',
  32 + field: 'description'
  33 + }
  34 + ]
  35 +})
  36 +export const { allSchemas } = useVxeCrudSchemas(crudSchemas)
... ...