瀏覽代碼

【功能调整】工作流:工作流程的流转记录,不区分父子任务

YunaiV 10 月之前
父節點
當前提交
43b7dff0d0

+ 10 - 8
src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue

@@ -53,11 +53,14 @@
           />
           <el-table-column
             label="审批人"
-            prop="assigneeUser.nickname"
             min-width="100"
             align="center"
             v-if="selectActivityType === 'bpmn:UserTask'"
-          />
+          >
+            <template #default="scope">
+              {{ scope.row.assigneeUser?.nickname || scope.row.ownerUser?.nickname }}
+            </template>
+          </el-table-column>
           <el-table-column
             label="发起人"
             prop="assigneeUser.nickname"
@@ -65,12 +68,11 @@
             align="center"
             v-else
           />
-          <el-table-column
-            label="部门"
-            prop="assigneeUser.deptName"
-            min-width="100"
-            align="center"
-          />
+          <el-table-column label="部门" min-width="100" align="center">
+            <template #default="scope">
+              {{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
+            </template>
+          </el-table-column>
           <el-table-column
             :formatter="dateFormatter"
             align="center"

+ 1 - 2
src/router/modules/remaining.ts

@@ -292,8 +292,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
       },
       {
         path: 'process-instance/detail',
-        component: () => import('@/views/bpm/processInstance/detail/index_new.vue'),
-        //component: () => import('@/views/bpm/processInstance/detail/index.vue'),
+        component: () => import('@/views/bpm/processInstance/detail/index.vue'),
         name: 'BpmProcessInstanceDetail',
         meta: {
           noCache: true,

+ 159 - 335
src/views/bpm/model/index.vue

@@ -1,216 +1,95 @@
 <template>
-  <doc-alert title="流程设计器(BPMN)" url="https://doc.iocoder.cn/bpm/model-designer-dingding/" />
-  <doc-alert
-    title="流程设计器(钉钉、飞书)"
-    url="https://doc.iocoder.cn/bpm/model-designer-bpmn/"
-  />
-  <doc-alert title="选择审批人、发起人自选" url="https://doc.iocoder.cn/bpm/assignee/" />
-  <doc-alert title="会签、或签、依次审批" url="https://doc.iocoder.cn/bpm/multi-instance/" />
-
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="流程标识" prop="key">
-        <el-input
-          v-model="queryParams.key"
-          placeholder="请输入流程标识"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item label="流程名称" prop="name">
-        <el-input
-          v-model="queryParams.name"
-          placeholder="请输入流程名称"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item label="流程分类" prop="category">
-        <el-select
-          v-model="queryParams.category"
-          placeholder="请选择流程分类"
-          clearable
-          class="!w-240px"
-        >
-          <el-option
-            v-for="category in categoryList"
-            :key="category.code"
-            :label="category.name"
-            :value="category.code"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-        <el-button
-          type="primary"
-          plain
-          @click="openForm('create')"
-          v-hasPermi="['bpm:model:create']"
-        >
-          <Icon icon="ep:plus" class="mr-5px" /> 新建
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
   <ContentWrap>
-    <el-table v-loading="loading" :data="list">
-      <el-table-column label="流程名称" align="center" prop="name" min-width="200" />
-      <el-table-column label="流程图标" align="center" prop="icon" min-width="100">
-        <template #default="scope">
-          <el-image :src="scope.row.icon" class="h-32px w-32px" />
-        </template>
-      </el-table-column>
-      <el-table-column label="可见范围" align="center" prop="startUserIds" min-width="100">
-        <template #default="scope">
-          <el-text v-if="!scope.row.startUsers || scope.row.startUsers.length === 0">
-            全部可见
-          </el-text>
-          <el-text v-else-if="scope.row.startUsers.length == 1">
-            {{ scope.row.startUsers[0].nickname }}
-          </el-text>
-          <el-text v-else>
-            <el-tooltip
-              class="box-item"
-              effect="dark"
-              placement="top"
-              :content="scope.row.startUsers.map((user: any) => user.nickname).join('、')"
-            >
-              {{ scope.row.startUsers[0].nickname }}等 {{ scope.row.startUsers.length }} 人可见
-            </el-tooltip>
-          </el-text>
-        </template>
-      </el-table-column>
-      <el-table-column label="流程分类" align="center" prop="categoryName" min-width="100" />
-      <el-table-column label="表单信息" align="center" prop="formType" min-width="200">
-        <template #default="scope">
-          <el-button
-            v-if="scope.row.formType === 10"
-            type="primary"
-            link
-            @click="handleFormDetail(scope.row)"
-          >
-            <span>{{ scope.row.formName }}</span>
-          </el-button>
-          <el-button
-            v-else-if="scope.row.formType === 20"
-            type="primary"
-            link
-            @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="deploymentTime" min-width="250">
-        <template #default="scope">
-          <span v-if="scope.row.processDefinition">
-            {{ formatDate(scope.row.processDefinition.deploymentTime) }}
-          </span>
-          <el-tag v-if="scope.row.processDefinition" class="ml-10px">
-            v{{ scope.row.processDefinition.version }}
-          </el-tag>
-          <el-tag v-else type="warning">未部署</el-tag>
-          <el-tag
-            v-if="scope.row.processDefinition?.suspensionState === 2"
-            type="warning"
-            class="ml-10px"
+    <div class="flex justify-between pl-20px items-center">
+      <h3 class="font-extrabold">流程模型</h3>
+      <!-- 搜索工作栏 -->
+      <el-form
+        v-if="!isCategorySorting"
+        class="-mb-15px flex mr-10px"
+        :model="queryParams"
+        ref="queryFormRef"
+        :inline="true"
+        label-width="68px"
+        @submit.prevent
+      >
+        <el-form-item align="right" prop="key" class="ml-auto">
+          <el-input
+            v-model="queryParams.key"
+            placeholder="搜索流程"
+            clearable
+            @keyup.enter="handleQuery"
+            class="!w-240px"
           >
-            已停用
-          </el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="操作" align="center" width="200" fixed="right">
-        <template #default="scope">
-          <el-button
-            link
-            type="primary"
-            @click="openForm('update', scope.row.id)"
-            v-hasPermi="['bpm:model:update']"
-            :disabled="!isManagerUser(scope.row)"
-          >
-            修改
-          </el-button>
-          <el-button
-            link
-            class="!ml-5px"
-            type="primary"
-            @click="handleDesign(scope.row)"
-            v-hasPermi="['bpm:model:update']"
-            :disabled="!isManagerUser(scope.row)"
-          >
-            设计
-          </el-button>
-          <el-button
-            link
-            class="!ml-5px"
-            type="primary"
-            @click="handleDeploy(scope.row)"
-            v-hasPermi="['bpm:model:deploy']"
-            :disabled="!isManagerUser(scope.row)"
-          >
-            发布
+            <template #prefix>
+              <Icon icon="ep:search" class="mx-10px" />
+            </template>
+          </el-input>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="openForm('create')" v-hasPermi="['bpm:model:create']">
+            <Icon icon="ep:plus" class="mr-5px" /> 新建模型
           </el-button>
-          <el-dropdown
-            class="!align-middle ml-5px"
-            @command="(command) => handleCommand(command, scope.row)"
-            v-hasPermi="['bpm:process-definition:query', 'bpm:model:update', 'bpm:model:delete']"
-          >
-            <el-button type="primary" link>更多</el-button>
+        </el-form-item>
+
+        <el-form-item>
+          <el-dropdown @command="(command) => handleCommand(command)" placement="bottom-end">
+            <el-button class="w-30px" plain>
+              <Icon icon="ep:setting" />
+            </el-button>
             <template #dropdown>
               <el-dropdown-menu>
-                <el-dropdown-item
-                  command="handleDefinitionList"
-                  v-if="checkPermi(['bpm:process-definition:query'])"
-                >
-                  历史
-                </el-dropdown-item>
-                <el-dropdown-item
-                  command="handleChangeState"
-                  v-if="checkPermi(['bpm:model:update']) && scope.row.processDefinition"
-                  :disabled="!isManagerUser(scope.row)"
-                >
-                  {{ scope.row.processDefinition.suspensionState === 1 ? '停用' : '启用' }}
+                <el-dropdown-item command="handleAddCategory">
+                  <Icon icon="ep:circle-plus" :size="13" class="mr-5px" />
+                  新建分类
                 </el-dropdown-item>
-                <el-dropdown-item
-                  type="danger"
-                  command="handleDelete"
-                  v-if="checkPermi(['bpm:model:delete'])"
-                  :disabled="!isManagerUser(scope.row)"
-                >
-                  删除
+                <el-dropdown-item command="handleSort">
+                  <Icon icon="fa:sort-amount-desc" :size="13" class="mr-5px" />
+                  分类排序
                 </el-dropdown-item>
               </el-dropdown-menu>
             </template>
           </el-dropdown>
+        </el-form-item>
+      </el-form>
+      <div class="mr-20px" v-else>
+        <el-button @click="cancelSort"> 取 消 </el-button>
+        <el-button type="primary" @click="saveSort"> 保存排序 </el-button>
+      </div>
+    </div>
+
+    <el-divider />
+
+    <!-- 分类卡片组 -->
+    <div class="px-15px">
+      <draggable
+        :disabled="!isCategorySorting"
+        v-model="categoryGroup"
+        item-key="id"
+        :animation="400"
+      >
+        <template #item="{ element }">
+          <ContentWrap
+            class="rounded-lg transition-all duration-300 ease-in-out hover:shadow-xl"
+            v-loading="loading"
+            :body-style="{ padding: 0 }"
+            :key="element.id"
+          >
+            <CategoryDraggableModel
+              ref="categoryDraggableModelRef"
+              :isCategorySorting="isCategorySorting"
+              :categoryInfo="element"
+              @success="getList"
+            />
+          </ContentWrap>
         </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
+      </draggable>
+    </div>
   </ContentWrap>
 
   <!-- 表单弹窗:添加/修改流程 -->
   <ModelForm ref="formRef" @success="getList" />
-
+  <!-- 表单弹窗:添加/修改分类 -->
+  <CategoryForm ref="categoryFormRef" @success="getList" />
   <!-- 弹窗:表单详情 -->
   <Dialog title="表单详情" v-model="formDetailVisible" width="800">
     <form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
@@ -218,26 +97,20 @@
 </template>
 
 <script lang="ts" setup>
-import { formatDate } from '@/utils/formatTime'
+import draggable from 'vuedraggable'
+import { CategoryApi } from '@/api/bpm/category'
 import * as ModelApi from '@/api/bpm/model'
-import * as FormApi from '@/api/bpm/form'
 import ModelForm from './ModelForm.vue'
-import { setConfAndFields2 } from '@/utils/formCreate'
-import { CategoryApi } from '@/api/bpm/category'
-import { BpmModelType } from '@/utils/constants'
-import { checkPermi } from '@/utils/permission'
-import { useUserStoreWithOut } from '@/store/modules/user'
+import CategoryForm from '../category/CategoryForm.vue'
+import { groupBy, cloneDeep } from 'lodash-es'
+import CategoryDraggableModel from './CategoryDraggableModel.vue'
 
 defineOptions({ name: 'BpmModel' })
 
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-const { push } = useRouter() // 路由
-const userStore = useUserStoreWithOut() // 用户信息缓存
-
+const categoryDraggableModelRef = ref()
+const categoryFormRef = ref()
 const loading = ref(true) // 列表的加载中
-const total = ref(0) // 列表的总页数
-const list = ref([]) // 列表的数据
+const isCategorySorting = ref(false) // 是否正处于排序状态
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,
@@ -246,18 +119,26 @@ const queryParams = reactive({
   category: undefined
 })
 const queryFormRef = ref() // 搜索的表单
-const categoryList = ref([]) // 流程分类列表
+const categoryGroup: any = ref([]) // 按照category分组的数据
+const originalData: any = ref([])
+// 查询所有分类数据
+const getAllCategory = async () => {
+  // TODO 芋艿:这里需要一个不分页查全部的流程分类接口
+  const data = await CategoryApi.getCategoryPage(queryParams)
+  categoryGroup.value = data.list.map((item) => ({ ...item, modelList: [] }))
+}
 
-/** 查询列表 */
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await ModelApi.getModelPage(queryParams)
-    list.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
+/** 查询所有流程模型接口 */
+const getAllModel = async () => {
+  // TODO 芋艿:这里需要一个不分页查全部的流程模型接口
+  const data = await ModelApi.getModelPage(queryParams)
+  const groupedData = groupBy(data.list, 'categoryName')
+  Object.keys(groupedData).forEach((key) => {
+    const category = categoryGroup.value.find((item) => item.name === key)
+    if (category) {
+      category.modelList = groupedData[key]
+    }
+  })
 }
 
 /** 搜索按钮操作 */
@@ -266,139 +147,82 @@ const handleQuery = () => {
   getList()
 }
 
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value.resetFields()
-  handleQuery()
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
 }
+/** 流程表单的详情按钮操作 */
+const formDetailVisible = ref(false)
+const formDetailPreview = ref({
+  rule: [],
+  option: {}
+})
 
-/** '更多'操作按钮 */
-const handleCommand = (command: string, row: any) => {
+/** 右上角设置按钮 */
+const handleCommand = (command: string) => {
   switch (command) {
-    case 'handleDefinitionList':
-      handleDefinitionList(row)
-      break
-    case 'handleDelete':
-      handleDelete(row)
+    case 'handleAddCategory':
+      handleAddCategory()
       break
-    case 'handleChangeState':
-      handleChangeState(row)
+    case 'handleSort':
+      handleSort()
       break
     default:
       break
   }
 }
 
-/** 添加/修改操作 */
-const formRef = ref()
-const openForm = (type: string, id?: number) => {
-  formRef.value.open(type, id)
+// 新建分类
+const handleAddCategory = () => {
+  categoryFormRef.value.open('create')
 }
-
-/** 删除按钮操作 */
-const handleDelete = async (row: any) => {
-  try {
-    // 删除的二次确认
-    await message.delConfirm()
-    // 发起删除
-    await ModelApi.deleteModel(row.id)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    await getList()
-  } catch {}
+// 分类排序
+const handleSort = () => {
+  // 保存初始数据
+  originalData.value = cloneDeep(categoryGroup.value)
+  isCategorySorting.value = true
 }
-
-/** 更新状态操作 */
-const handleChangeState = async (row: any) => {
-  const state = row.processDefinition.suspensionState
-  const newState = state === 1 ? 2 : 1
-  try {
-    // 修改状态的二次确认
-    const id = row.id
-    debugger
-    const statusState = state === 1 ? '停用' : '启用'
-    const content = '是否确认' + statusState + '流程名字为"' + row.name + '"的数据项?'
-    await message.confirm(content)
-    // 发起修改状态
-    await ModelApi.updateModelState(id, newState)
-    message.success(statusState + '成功')
-    // 刷新列表
-    await getList()
-  } catch {}
+// 取消排序
+const cancelSort = () => {
+  // 恢复初始数据
+  categoryGroup.value = cloneDeep(originalData.value)
+  isCategorySorting.value = false
 }
-
-/** 设计流程 */
-const handleDesign = (row: any) => {
-  if (row.type == BpmModelType.BPMN) {
-    push({
-      name: 'BpmModelEditor',
-      query: {
-        modelId: row.id
-      }
-    })
-  } else {
-    push({
-      name: 'SimpleWorkflowDesignEditor',
-      query: {
-        modelId: row.id
-      }
-    })
-  }
+// 保存排序
+const saveSort = () => {
+  // TODO 芋艿:这里需要一个保存分类排序接口
 }
 
-/** 发布流程 */
-const handleDeploy = async (row: any) => {
+const getList = async () => {
+  loading.value = true
   try {
-    // 删除的二次确认
-    await message.confirm('是否部署该流程!!')
-    // 发起部署
-    await ModelApi.deployModel(row.id)
-    message.success(t('部署成功'))
-    // 刷新列表
-    await getList()
-  } catch {}
-}
-
-/** 跳转到指定流程定义列表 */
-const handleDefinitionList = (row) => {
-  push({
-    name: 'BpmProcessDefinition',
-    query: {
-      key: row.key
-    }
-  })
-}
-
-/** 流程表单的详情按钮操作 */
-const formDetailVisible = ref(false)
-const formDetailPreview = ref({
-  rule: [],
-  option: {}
-})
-const handleFormDetail = async (row: any) => {
-  if (row.formType == 10) {
-    // 设置表单
-    const data = await FormApi.getForm(row.formId)
-    setConfAndFields2(formDetailPreview, data.conf, data.fields)
-    // 弹窗打开
-    formDetailVisible.value = true
-  } else {
-    await push({
-      path: row.formCustomCreatePath
-    })
+    await getAllCategory()
+    await getAllModel()
+  } finally {
+    loading.value = false
   }
 }
 
-/** 判断是否可以操作 */
-const isManagerUser = (row: any) => {
-  const userId = userStore.getUser.id
-  return row.managerUserIds && row.managerUserIds.includes(userId)
-}
-
 /** 初始化 **/
 onMounted(async () => {
-  await getList()
-  // 查询流程分类列表
-  categoryList.value = await CategoryApi.getCategorySimpleList()
+  getList()
 })
 </script>
+
+<style lang="scss" scoped>
+:deep() {
+  .el-table--fit .el-table__inner-wrapper:before {
+    height: 0;
+  }
+  .el-card {
+    border-radius: 8px;
+  }
+  .el-form--inline .el-form-item {
+    margin-right: 10px;
+  }
+  .el-divider--horizontal {
+    margin-top: 6px;
+  }
+}
+</style>

+ 0 - 228
src/views/bpm/model/index_new.vue

@@ -1,228 +0,0 @@
-<template>
-  <ContentWrap>
-    <div class="flex justify-between pl-20px items-center">
-      <h3 class="font-extrabold">流程模型</h3>
-      <!-- 搜索工作栏 -->
-      <el-form
-        v-if="!isCategorySorting"
-        class="-mb-15px flex mr-10px"
-        :model="queryParams"
-        ref="queryFormRef"
-        :inline="true"
-        label-width="68px"
-        @submit.prevent
-      >
-        <el-form-item align="right" prop="key" class="ml-auto">
-          <el-input
-            v-model="queryParams.key"
-            placeholder="搜索流程"
-            clearable
-            @keyup.enter="handleQuery"
-            class="!w-240px"
-          >
-            <template #prefix>
-              <Icon icon="ep:search" class="mx-10px" />
-            </template>
-          </el-input>
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" @click="openForm('create')" v-hasPermi="['bpm:model:create']">
-            <Icon icon="ep:plus" class="mr-5px" /> 新建模型
-          </el-button>
-        </el-form-item>
-
-        <el-form-item>
-          <el-dropdown @command="(command) => handleCommand(command)" placement="bottom-end">
-            <el-button class="w-30px" plain>
-              <Icon icon="ep:setting" />
-            </el-button>
-            <template #dropdown>
-              <el-dropdown-menu>
-                <el-dropdown-item command="handleAddCategory">
-                  <Icon icon="ep:circle-plus" :size="13" class="mr-5px" />
-                  新建分类
-                </el-dropdown-item>
-                <el-dropdown-item command="handleSort">
-                  <Icon icon="fa:sort-amount-desc" :size="13" class="mr-5px" />
-                  分类排序
-                </el-dropdown-item>
-              </el-dropdown-menu>
-            </template>
-          </el-dropdown>
-        </el-form-item>
-      </el-form>
-      <div class="mr-20px" v-else>
-        <el-button @click="cancelSort"> 取 消 </el-button>
-        <el-button type="primary" @click="saveSort"> 保存排序 </el-button>
-      </div>
-    </div>
-
-    <el-divider />
-
-    <!-- 分类卡片组 -->
-    <div class="px-15px">
-      <draggable
-        :disabled="!isCategorySorting"
-        v-model="categoryGroup"
-        item-key="id"
-        :animation="400"
-      >
-        <template #item="{ element }">
-          <ContentWrap
-            class="rounded-lg transition-all duration-300 ease-in-out hover:shadow-xl"
-            v-loading="loading"
-            :body-style="{ padding: 0 }"
-            :key="element.id"
-          >
-            <CategoryDraggableModel
-              ref="categoryDraggableModelRef"
-              :isCategorySorting="isCategorySorting"
-              :categoryInfo="element"
-              @success="getList"
-            />
-          </ContentWrap>
-        </template>
-      </draggable>
-    </div>
-  </ContentWrap>
-
-  <!-- 表单弹窗:添加/修改流程 -->
-  <ModelForm ref="formRef" @success="getList" />
-  <!-- 表单弹窗:添加/修改分类 -->
-  <CategoryForm ref="categoryFormRef" @success="getList" />
-  <!-- 弹窗:表单详情 -->
-  <Dialog title="表单详情" v-model="formDetailVisible" width="800">
-    <form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
-  </Dialog>
-</template>
-
-<script lang="ts" setup>
-import draggable from 'vuedraggable'
-import { CategoryApi } from '@/api/bpm/category'
-import * as ModelApi from '@/api/bpm/model'
-import ModelForm from './ModelForm.vue'
-import CategoryForm from '../category/CategoryForm.vue'
-import { groupBy, cloneDeep } from 'lodash-es'
-import CategoryDraggableModel from './CategoryDraggableModel.vue'
-
-defineOptions({ name: 'BpmModel' })
-
-const categoryDraggableModelRef = ref()
-const categoryFormRef = ref()
-const loading = ref(true) // 列表的加载中
-const isCategorySorting = ref(false) // 是否正处于排序状态
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  key: undefined,
-  name: undefined,
-  category: undefined
-})
-const queryFormRef = ref() // 搜索的表单
-const categoryGroup: any = ref([]) // 按照category分组的数据
-const originalData: any = ref([])
-// 查询所有分类数据
-const getAllCategory = async () => {
-  // TODO 芋艿:这里需要一个不分页查全部的流程分类接口
-  const data = await CategoryApi.getCategoryPage(queryParams)
-  categoryGroup.value = data.list.map((item) => ({ ...item, modelList: [] }))
-}
-
-/** 查询所有流程模型接口 */
-const getAllModel = async () => {
-  // TODO 芋艿:这里需要一个不分页查全部的流程模型接口
-  const data = await ModelApi.getModelPage(queryParams)
-  const groupedData = groupBy(data.list, 'categoryName')
-  Object.keys(groupedData).forEach((key) => {
-    const category = categoryGroup.value.find((item) => item.name === key)
-    if (category) {
-      category.modelList = groupedData[key]
-    }
-  })
-}
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.pageNo = 1
-  getList()
-}
-
-/** 添加/修改操作 */
-const formRef = ref()
-const openForm = (type: string, id?: number) => {
-  formRef.value.open(type, id)
-}
-/** 流程表单的详情按钮操作 */
-const formDetailVisible = ref(false)
-const formDetailPreview = ref({
-  rule: [],
-  option: {}
-})
-
-/** 右上角设置按钮 */
-const handleCommand = (command: string) => {
-  switch (command) {
-    case 'handleAddCategory':
-      handleAddCategory()
-      break
-    case 'handleSort':
-      handleSort()
-      break
-    default:
-      break
-  }
-}
-
-// 新建分类
-const handleAddCategory = () => {
-  categoryFormRef.value.open('create')
-}
-// 分类排序
-const handleSort = () => {
-  // 保存初始数据
-  originalData.value = cloneDeep(categoryGroup.value)
-  isCategorySorting.value = true
-}
-// 取消排序
-const cancelSort = () => {
-  // 恢复初始数据
-  categoryGroup.value = cloneDeep(originalData.value)
-  isCategorySorting.value = false
-}
-// 保存排序
-const saveSort = () => {
-  // TODO 芋艿:这里需要一个保存分类排序接口
-}
-
-const getList = async () => {
-  loading.value = true
-  try {
-    await getAllCategory()
-    await getAllModel()
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 初始化 **/
-onMounted(async () => {
-  getList()
-})
-</script>
-
-<style lang="scss" scoped>
-:deep() {
-  .el-table--fit .el-table__inner-wrapper:before {
-    height: 0;
-  }
-  .el-card {
-    border-radius: 8px;
-  }
-  .el-form--inline .el-form-item {
-    margin-right: 10px;
-  }
-  .el-divider--horizontal {
-    margin-top: 6px;
-  }
-}
-</style>

+ 404 - 0
src/views/bpm/model/index_old.vue

@@ -0,0 +1,404 @@
+<template>
+  <doc-alert title="流程设计器(BPMN)" url="https://doc.iocoder.cn/bpm/model-designer-dingding/" />
+  <doc-alert
+    title="流程设计器(钉钉、飞书)"
+    url="https://doc.iocoder.cn/bpm/model-designer-bpmn/"
+  />
+  <doc-alert title="选择审批人、发起人自选" url="https://doc.iocoder.cn/bpm/assignee/" />
+  <doc-alert title="会签、或签、依次审批" url="https://doc.iocoder.cn/bpm/multi-instance/" />
+
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="流程标识" prop="key">
+        <el-input
+          v-model="queryParams.key"
+          placeholder="请输入流程标识"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
+        />
+      </el-form-item>
+      <el-form-item label="流程名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入流程名称"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
+        />
+      </el-form-item>
+      <el-form-item label="流程分类" prop="category">
+        <el-select
+          v-model="queryParams.category"
+          placeholder="请选择流程分类"
+          clearable
+          class="!w-240px"
+        >
+          <el-option
+            v-for="category in categoryList"
+            :key="category.code"
+            :label="category.name"
+            :value="category.code"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        <el-button
+          type="primary"
+          plain
+          @click="openForm('create')"
+          v-hasPermi="['bpm:model:create']"
+        >
+          <Icon icon="ep:plus" class="mr-5px" /> 新建
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="流程名称" align="center" prop="name" min-width="200" />
+      <el-table-column label="流程图标" align="center" prop="icon" min-width="100">
+        <template #default="scope">
+          <el-image :src="scope.row.icon" class="h-32px w-32px" />
+        </template>
+      </el-table-column>
+      <el-table-column label="可见范围" align="center" prop="startUserIds" min-width="100">
+        <template #default="scope">
+          <el-text v-if="!scope.row.startUsers || scope.row.startUsers.length === 0">
+            全部可见
+          </el-text>
+          <el-text v-else-if="scope.row.startUsers.length == 1">
+            {{ scope.row.startUsers[0].nickname }}
+          </el-text>
+          <el-text v-else>
+            <el-tooltip
+              class="box-item"
+              effect="dark"
+              placement="top"
+              :content="scope.row.startUsers.map((user: any) => user.nickname).join('、')"
+            >
+              {{ scope.row.startUsers[0].nickname }}等 {{ scope.row.startUsers.length }} 人可见
+            </el-tooltip>
+          </el-text>
+        </template>
+      </el-table-column>
+      <el-table-column label="流程分类" align="center" prop="categoryName" min-width="100" />
+      <el-table-column label="表单信息" align="center" prop="formType" min-width="200">
+        <template #default="scope">
+          <el-button
+            v-if="scope.row.formType === 10"
+            type="primary"
+            link
+            @click="handleFormDetail(scope.row)"
+          >
+            <span>{{ scope.row.formName }}</span>
+          </el-button>
+          <el-button
+            v-else-if="scope.row.formType === 20"
+            type="primary"
+            link
+            @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="deploymentTime" min-width="250">
+        <template #default="scope">
+          <span v-if="scope.row.processDefinition">
+            {{ formatDate(scope.row.processDefinition.deploymentTime) }}
+          </span>
+          <el-tag v-if="scope.row.processDefinition" class="ml-10px">
+            v{{ scope.row.processDefinition.version }}
+          </el-tag>
+          <el-tag v-else type="warning">未部署</el-tag>
+          <el-tag
+            v-if="scope.row.processDefinition?.suspensionState === 2"
+            type="warning"
+            class="ml-10px"
+          >
+            已停用
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" width="200" fixed="right">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+            v-hasPermi="['bpm:model:update']"
+            :disabled="!isManagerUser(scope.row)"
+          >
+            修改
+          </el-button>
+          <el-button
+            link
+            class="!ml-5px"
+            type="primary"
+            @click="handleDesign(scope.row)"
+            v-hasPermi="['bpm:model:update']"
+            :disabled="!isManagerUser(scope.row)"
+          >
+            设计
+          </el-button>
+          <el-button
+            link
+            class="!ml-5px"
+            type="primary"
+            @click="handleDeploy(scope.row)"
+            v-hasPermi="['bpm:model:deploy']"
+            :disabled="!isManagerUser(scope.row)"
+          >
+            发布
+          </el-button>
+          <el-dropdown
+            class="!align-middle ml-5px"
+            @command="(command) => handleCommand(command, scope.row)"
+            v-hasPermi="['bpm:process-definition:query', 'bpm:model:update', 'bpm:model:delete']"
+          >
+            <el-button type="primary" link>更多</el-button>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <el-dropdown-item
+                  command="handleDefinitionList"
+                  v-if="checkPermi(['bpm:process-definition:query'])"
+                >
+                  历史
+                </el-dropdown-item>
+                <el-dropdown-item
+                  command="handleChangeState"
+                  v-if="checkPermi(['bpm:model:update']) && scope.row.processDefinition"
+                  :disabled="!isManagerUser(scope.row)"
+                >
+                  {{ scope.row.processDefinition.suspensionState === 1 ? '停用' : '启用' }}
+                </el-dropdown-item>
+                <el-dropdown-item
+                  type="danger"
+                  command="handleDelete"
+                  v-if="checkPermi(['bpm:model:delete'])"
+                  :disabled="!isManagerUser(scope.row)"
+                >
+                  删除
+                </el-dropdown-item>
+              </el-dropdown-menu>
+            </template>
+          </el-dropdown>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改流程 -->
+  <ModelForm ref="formRef" @success="getList" />
+
+  <!-- 弹窗:表单详情 -->
+  <Dialog title="表单详情" v-model="formDetailVisible" width="800">
+    <form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
+  </Dialog>
+</template>
+
+<script lang="ts" setup>
+import { formatDate } from '@/utils/formatTime'
+import * as ModelApi from '@/api/bpm/model'
+import * as FormApi from '@/api/bpm/form'
+import ModelForm from './ModelForm.vue'
+import { setConfAndFields2 } from '@/utils/formCreate'
+import { CategoryApi } from '@/api/bpm/category'
+import { BpmModelType } from '@/utils/constants'
+import { checkPermi } from '@/utils/permission'
+import { useUserStoreWithOut } from '@/store/modules/user'
+
+defineOptions({ name: 'BpmModel' })
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+const { push } = useRouter() // 路由
+const userStore = useUserStoreWithOut() // 用户信息缓存
+
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  key: undefined,
+  name: undefined,
+  category: undefined
+})
+const queryFormRef = ref() // 搜索的表单
+const categoryList = ref([]) // 流程分类列表
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await ModelApi.getModelPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** '更多'操作按钮 */
+const handleCommand = (command: string, row: any) => {
+  switch (command) {
+    case 'handleDefinitionList':
+      handleDefinitionList(row)
+      break
+    case 'handleDelete':
+      handleDelete(row)
+      break
+    case 'handleChangeState':
+      handleChangeState(row)
+      break
+    default:
+      break
+  }
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (row: any) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await ModelApi.deleteModel(row.id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 更新状态操作 */
+const handleChangeState = async (row: any) => {
+  const state = row.processDefinition.suspensionState
+  const newState = state === 1 ? 2 : 1
+  try {
+    // 修改状态的二次确认
+    const id = row.id
+    debugger
+    const statusState = state === 1 ? '停用' : '启用'
+    const content = '是否确认' + statusState + '流程名字为"' + row.name + '"的数据项?'
+    await message.confirm(content)
+    // 发起修改状态
+    await ModelApi.updateModelState(id, newState)
+    message.success(statusState + '成功')
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 设计流程 */
+const handleDesign = (row: any) => {
+  if (row.type == BpmModelType.BPMN) {
+    push({
+      name: 'BpmModelEditor',
+      query: {
+        modelId: row.id
+      }
+    })
+  } else {
+    push({
+      name: 'SimpleWorkflowDesignEditor',
+      query: {
+        modelId: row.id
+      }
+    })
+  }
+}
+
+/** 发布流程 */
+const handleDeploy = async (row: any) => {
+  try {
+    // 删除的二次确认
+    await message.confirm('是否部署该流程!!')
+    // 发起部署
+    await ModelApi.deployModel(row.id)
+    message.success(t('部署成功'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 跳转到指定流程定义列表 */
+const handleDefinitionList = (row) => {
+  push({
+    name: 'BpmProcessDefinition',
+    query: {
+      key: row.key
+    }
+  })
+}
+
+/** 流程表单的详情按钮操作 */
+const formDetailVisible = ref(false)
+const formDetailPreview = ref({
+  rule: [],
+  option: {}
+})
+const handleFormDetail = async (row: any) => {
+  if (row.formType == 10) {
+    // 设置表单
+    const data = await FormApi.getForm(row.formId)
+    setConfAndFields2(formDetailPreview, data.conf, data.fields)
+    // 弹窗打开
+    formDetailVisible.value = true
+  } else {
+    await push({
+      path: row.formCustomCreatePath
+    })
+  }
+}
+
+/** 判断是否可以操作 */
+const isManagerUser = (row: any) => {
+  const userId = userStore.getUser.id
+  return row.managerUserIds && row.managerUserIds.includes(userId)
+}
+
+/** 初始化 **/
+onMounted(async () => {
+  await getList()
+  // 查询流程分类列表
+  categoryList.value = await CategoryApi.getCategorySimpleList()
+})
+</script>

+ 1 - 5
src/views/bpm/processInstance/detail/ProcessInstanceBpmnViewer.vue

@@ -1,8 +1,5 @@
 <template>
   <el-card v-loading="loading" class="box-card">
-    <template #header v-if="showHeader">
-      <span class="el-icon-picture-outline">流程图</span>
-    </template>
     <MyProcessViewer key="designer" :xml="view.bpmnXml" :view="view" class="h-700px" />
   </el-card>
 </template>
@@ -16,8 +13,7 @@ defineOptions({ name: 'BpmProcessInstanceBpmnViewer' })
 const props = defineProps({
   loading: propTypes.bool.def(false), // 是否加载中
   id: propTypes.string, // 流程实例的编号
-  bpmnXml: propTypes.string, // BPMN XML
-  showHeader: propTypes.bool.def(true), // 是否显示头
+  bpmnXml: propTypes.string // BPMN XML
 })
 
 const view = ref({

+ 2 - 24
src/views/bpm/processInstance/detail/ProcessInstanceTaskList.vue

@@ -1,9 +1,6 @@
 <template>
   <el-card v-loading="loading" class="box-card">
-    <template #header v-if="showHeader">
-      <span class="el-icon-picture-outline">审批记录</span>
-    </template>
-    <el-col :offset="3" :span="17">
+    <el-col>
       <div class="block">
         <el-timeline>
           <el-timeline-item
@@ -26,14 +23,6 @@
             <p style="font-weight: 700">
               审批任务:{{ item.name }}
               <dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="item.status" />
-              <el-button
-                class="ml-10px"
-                v-if="!isEmpty(item.children)"
-                @click="openChildrenTask(item)"
-                size="small"
-              >
-                <Icon icon="ep:memo" /> 子任务
-              </el-button>
               <el-button
                 class="ml-10px"
                 size="small"
@@ -78,8 +67,6 @@
     </el-col>
   </el-card>
 
-  <!-- 弹窗:子任务  -->
-  <TaskSignList ref="taskSignListRef" @success="refresh" />
   <!-- 弹窗:表单 -->
   <Dialog title="表单详情" v-model="taskFormVisible" width="600">
     <form-create
@@ -94,8 +81,6 @@
 import { formatDate, formatPast2 } from '@/utils/formatTime'
 import { propTypes } from '@/utils/propTypes'
 import { DICT_TYPE } from '@/utils/dict'
-import { isEmpty } from '@/utils/is'
-import TaskSignList from './dialog/TaskSignList.vue'
 import type { ApiAttrs } from '@form-create/element-ui/types/config'
 import { setConfAndFields2 } from '@/utils/formCreate'
 
@@ -104,8 +89,7 @@ defineOptions({ name: 'BpmProcessInstanceTaskList' })
 defineProps({
   loading: propTypes.bool, // 是否加载中
   processInstance: propTypes.object, // 流程实例
-  tasks: propTypes.arrayOf(propTypes.object), // 流程任务的数组
-  showHeader: propTypes.bool.def(true), // 是否显示头
+  tasks: propTypes.arrayOf(propTypes.object) // 流程任务的数组
 })
 
 /** 获得流程实例对应的颜色 */
@@ -142,12 +126,6 @@ const getTaskTimelineItemType = (item: any) => {
   return ''
 }
 
-/** 子任务 */
-const taskSignListRef = ref()
-const openChildrenTask = (item: any) => {
-  taskSignListRef.value.open(item)
-}
-
 /** 查看表单 */
 const fApi = ref<ApiAttrs>() // form-create 的 API 操作类
 const taskForm = ref({

+ 0 - 89
src/views/bpm/processInstance/detail/dialog/TaskDelegateForm.vue

@@ -1,89 +0,0 @@
-<template>
-  <Dialog v-model="dialogVisible" title="委派任务" width="500">
-    <el-form
-      ref="formRef"
-      v-loading="formLoading"
-      :model="formData"
-      :rules="formRules"
-      label-width="110px"
-    >
-      <el-form-item label="接收人" prop="delegateUserId">
-        <el-select v-model="formData.delegateUserId" clearable style="width: 100%">
-          <el-option
-            v-for="item in userList"
-            :key="item.id"
-            :label="item.nickname"
-            :value="item.id"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="委派理由" prop="reason">
-        <el-input v-model="formData.reason" clearable placeholder="请输入委派理由" />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
-      <el-button @click="dialogVisible = false">取 消</el-button>
-    </template>
-  </Dialog>
-</template>
-<script lang="ts" setup>
-import * as TaskApi from '@/api/bpm/task'
-import * as UserApi from '@/api/system/user'
-
-defineOptions({ name: 'BpmTaskDelegateForm' })
-
-const dialogVisible = ref(false) // 弹窗的是否展示
-const formLoading = ref(false) // 表单的加载中
-const formData = ref({
-  id: '',
-  delegateUserId: undefined,
-  reason: ''
-})
-const formRules = ref({
-  delegateUserId: [{ required: true, message: '接收人不能为空', trigger: 'change' }],
-  reason: [{ required: true, message: '委派理由不能为空', trigger: 'blur' }]
-})
-
-const formRef = ref() // 表单 Ref
-const userList = ref<any[]>([]) // 用户列表
-
-/** 打开弹窗 */
-const open = async (id: string) => {
-  dialogVisible.value = true
-  resetForm()
-  formData.value.id = id
-  // 获得用户列表
-  userList.value = await UserApi.getSimpleUserList()
-}
-defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
-
-/** 提交表单 */
-const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
-const submitForm = async () => {
-  // 校验表单
-  if (!formRef) return
-  const valid = await formRef.value.validate()
-  if (!valid) return
-  // 提交请求
-  formLoading.value = true
-  try {
-    await TaskApi.delegateTask(formData.value)
-    dialogVisible.value = false
-    // 发送操作成功的事件
-    emit('success')
-  } finally {
-    formLoading.value = false
-  }
-}
-
-/** 重置表单 */
-const resetForm = () => {
-  formData.value = {
-    id: '',
-    delegateUserId: undefined,
-    reason: ''
-  }
-  formRef.value?.resetFields()
-}
-</script>

+ 0 - 90
src/views/bpm/processInstance/detail/dialog/TaskReturnForm.vue

@@ -1,90 +0,0 @@
-<template>
-  <Dialog v-model="dialogVisible" title="退回任务" width="500">
-    <el-form
-      ref="formRef"
-      v-loading="formLoading"
-      :model="formData"
-      :rules="formRules"
-      label-width="110px"
-    >
-      <el-form-item label="退回节点" prop="targetTaskDefinitionKey">
-        <el-select v-model="formData.targetTaskDefinitionKey" clearable style="width: 100%">
-          <el-option
-            v-for="item in returnList"
-            :key="item.taskDefinitionKey"
-            :label="item.name"
-            :value="item.taskDefinitionKey"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="退回理由" prop="reason">
-        <el-input v-model="formData.reason" clearable placeholder="请输入退回理由" />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
-      <el-button @click="dialogVisible = false">取 消</el-button>
-    </template>
-  </Dialog>
-</template>
-<script lang="ts" name="TaskRollbackDialogForm" setup>
-import * as TaskApi from '@/api/bpm/task'
-
-const message = useMessage() // 消息弹窗
-const dialogVisible = ref(false) // 弹窗的是否展示
-const formLoading = ref(false) // 表单的加载中
-const formData = ref({
-  id: '',
-  targetTaskDefinitionKey: undefined,
-  reason: ''
-})
-const formRules = ref({
-  targetTaskDefinitionKey: [{ required: true, message: '必须选择退回节点', trigger: 'change' }],
-  reason: [{ required: true, message: '退回理由不能为空', trigger: 'blur' }]
-})
-
-const formRef = ref() // 表单 Ref
-const returnList = ref([] as any)
-/** 打开弹窗 */
-const open = async (id: string) => {
-  returnList.value = await TaskApi.getTaskListByReturn(id)
-  if (returnList.value.length === 0) {
-    message.warning('当前没有可退回的节点')
-    return false
-  }
-  dialogVisible.value = true
-  resetForm()
-  formData.value.id = id
-}
-defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
-
-/** 提交表单 */
-const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
-const submitForm = async () => {
-  // 校验表单
-  if (!formRef) return
-  const valid = await formRef.value.validate()
-  if (!valid) return
-  // 提交请求
-  formLoading.value = true
-  try {
-    await TaskApi.returnTask(formData.value)
-    message.success('退回成功')
-    dialogVisible.value = false
-    // 发送操作成功的事件
-    emit('success')
-  } finally {
-    formLoading.value = false
-  }
-}
-
-/** 重置表单 */
-const resetForm = () => {
-  formData.value = {
-    id: '',
-    targetTaskDefinitionKey: undefined,
-    reason: ''
-  }
-  formRef.value?.resetFields()
-}
-</script>

+ 0 - 99
src/views/bpm/processInstance/detail/dialog/TaskSignCreateForm.vue

@@ -1,99 +0,0 @@
-<template>
-  <Dialog v-model="dialogVisible" title="加签" width="500">
-    <el-form
-      ref="formRef"
-      v-loading="formLoading"
-      :model="formData"
-      :rules="formRules"
-      label-width="110px"
-    >
-      <el-form-item label="加签处理人" prop="userIds">
-        <el-select v-model="formData.userIds" multiple clearable style="width: 100%">
-          <el-option
-            v-for="item in userList"
-            :key="item.id"
-            :label="item.nickname"
-            :value="item.id"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="加签理由" prop="reason">
-        <el-input v-model="formData.reason" clearable placeholder="请输入加签理由" />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <el-button :disabled="formLoading" type="primary" @click="submitForm('before')">
-        向前加签
-      </el-button>
-      <el-button :disabled="formLoading" type="primary" @click="submitForm('after')">
-        向后加签
-      </el-button>
-      <el-button @click="dialogVisible = false">取 消</el-button>
-    </template>
-  </Dialog>
-</template>
-<script lang="ts" setup>
-import * as TaskApi from '@/api/bpm/task'
-import * as UserApi from '@/api/system/user'
-
-defineOptions({ name: 'TaskSignCreateForm' })
-
-const message = useMessage() // 消息弹窗
-const dialogVisible = ref(false) // 弹窗的是否展示
-const formLoading = ref(false) // 表单的加载中
-const formData = ref({
-  id: '',
-  userIds: [],
-  type: '',
-  reason: ''
-})
-const formRules = ref({
-  userIds: [{ required: true, message: '加签处理人不能为空', trigger: 'change' }],
-  reason: [{ required: true, message: '加签理由不能为空', trigger: 'change' }]
-})
-
-const formRef = ref() // 表单 Ref
-const userList = ref<any[]>([]) // 用户列表
-
-/** 打开弹窗 */
-const open = async (id: string) => {
-  dialogVisible.value = true
-  resetForm()
-  formData.value.id = id
-  // 获得用户列表
-  userList.value = await UserApi.getSimpleUserList()
-}
-defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
-
-/** 提交表单 */
-const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
-const submitForm = async (type: string) => {
-  // 校验表单
-  if (!formRef) return
-  const valid = await formRef.value.validate()
-  if (!valid) return
-  // 提交请求
-  formLoading.value = true
-  formData.value.type = type
-  try {
-    await TaskApi.signCreateTask(formData.value)
-    message.success('加签成功')
-    dialogVisible.value = false
-    // 发送操作成功的事件
-    emit('success')
-  } finally {
-    formLoading.value = false
-  }
-}
-
-/** 重置表单 */
-const resetForm = () => {
-  formData.value = {
-    id: '',
-    userIds: [],
-    type: '',
-    reason: ''
-  }
-  formRef.value?.resetFields()
-}
-</script>

+ 1 - 0
src/views/bpm/processInstance/detail/dialog/TaskSignDeleteForm.vue

@@ -86,4 +86,5 @@ const resetForm = () => {
   }
   formRef.value?.resetFields()
 }
+// TODO @jason:新界面搞完,可以删除
 </script>

+ 1 - 0
src/views/bpm/processInstance/detail/dialog/TaskSignList.vue

@@ -103,4 +103,5 @@ const handleSignDeleteSuccess = () => {
 const isSignDeleteButtonVisible = (task: any) => {
   return task && task.children && !isEmpty(task.children)
 }
+// TODO @jason:新界面搞完,可以删除
 </script>

+ 0 - 89
src/views/bpm/processInstance/detail/dialog/TaskTransferForm.vue

@@ -1,89 +0,0 @@
-<template>
-  <Dialog v-model="dialogVisible" title="转派任务" width="500">
-    <el-form
-      ref="formRef"
-      v-loading="formLoading"
-      :model="formData"
-      :rules="formRules"
-      label-width="110px"
-    >
-      <el-form-item label="新审批人" prop="assigneeUserId">
-        <el-select v-model="formData.assigneeUserId" clearable style="width: 100%">
-          <el-option
-            v-for="item in userList"
-            :key="item.id"
-            :label="item.nickname"
-            :value="item.id"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="转派理由" prop="reason">
-        <el-input v-model="formData.reason" clearable placeholder="请输入转派理由" />
-      </el-form-item>
-    </el-form>
-    <template #footer>
-      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
-      <el-button @click="dialogVisible = false">取 消</el-button>
-    </template>
-  </Dialog>
-</template>
-<script lang="ts" setup>
-import * as TaskApi from '@/api/bpm/task'
-import * as UserApi from '@/api/system/user'
-
-defineOptions({ name: 'TaskTransferForm' })
-
-const dialogVisible = ref(false) // 弹窗的是否展示
-const formLoading = ref(false) // 表单的加载中
-const formData = ref({
-  id: '',
-  assigneeUserId: undefined,
-  reason: ''
-})
-const formRules = ref({
-  assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }],
-  reason: [{ required: true, message: '转派理由不能为空', trigger: 'blur' }]
-})
-
-const formRef = ref() // 表单 Ref
-const userList = ref<any[]>([]) // 用户列表
-
-/** 打开弹窗 */
-const open = async (id: string) => {
-  dialogVisible.value = true
-  resetForm()
-  formData.value.id = id
-  // 获得用户列表
-  userList.value = await UserApi.getSimpleUserList()
-}
-defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
-
-/** 提交表单 */
-const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
-const submitForm = async () => {
-  // 校验表单
-  if (!formRef) return
-  const valid = await formRef.value.validate()
-  if (!valid) return
-  // 提交请求
-  formLoading.value = true
-  try {
-    await TaskApi.transferTask(formData.value)
-    dialogVisible.value = false
-    // 发送操作成功的事件
-    emit('success')
-  } finally {
-    formLoading.value = false
-  }
-}
-
-/** 重置表单 */
-const resetForm = () => {
-  formData.value = {
-    id: '',
-    assigneeUserId: undefined,
-    reason: ''
-  }
-  formRef.value?.resetFields()
-}
-</script>

+ 240 - 371
src/views/bpm/processInstance/detail/index.vue

@@ -1,216 +1,163 @@
 <template>
-  <ContentWrap>
-    <!-- 审批信息 -->
-    <el-card
-      v-for="(item, index) in runningTasks"
-      :key="index"
-      v-loading="processInstanceLoading"
-      class="box-card"
-    >
-      <template #header>
-        <span class="el-icon-picture-outline">审批任务【{{ item.name }}】</span>
-      </template>
-      <el-col :offset="6" :span="16">
-        <el-form
-          :ref="'form' + index"
-          :model="auditForms[index]"
-          :rules="auditRule"
-          label-width="100px"
-        >
-          <el-form-item v-if="processInstance && processInstance.name" label="流程名">
-            {{ processInstance.name }}
-          </el-form-item>
-          <el-form-item v-if="processInstance && processInstance.startUser" label="流程发起人">
-            {{ processInstance?.startUser.nickname }}
-            <el-tag size="small" type="info">{{ processInstance?.startUser.deptName }}</el-tag>
-          </el-form-item>
-          <el-card v-if="runningTasks[index].formId > 0" class="mb-15px !-mt-10px">
-            <template #header>
-              <span class="el-icon-picture-outline">
-                填写表单【{{ runningTasks[index]?.formName }}】
-              </span>
-            </template>
-            <form-create
-              v-model="approveForms[index].value"
-              v-model:api="approveFormFApis[index]"
-              :option="approveForms[index].option"
-              :rule="approveForms[index].rule"
-            />
-          </el-card>
-          <el-form-item label="审批建议" prop="reason">
-            <el-input
-              v-model="auditForms[index].reason"
-              placeholder="请输入审批建议"
-              type="textarea"
-            />
-          </el-form-item>
-          <el-form-item label="抄送人" prop="copyUserIds">
-            <el-select v-model="auditForms[index].copyUserIds" multiple placeholder="请选择抄送人">
-              <el-option
-                v-for="itemx in userOptions"
-                :key="itemx.id"
-                :label="itemx.nickname"
-                :value="itemx.id"
-              />
-            </el-select>
-          </el-form-item>
-        </el-form>
-        <div style="margin-bottom: 20px; margin-left: 10%; font-size: 14px">
-          <!-- TODO @jason:建议搞个 if 来判断,替代现有的 !item.buttonsSetting || item.buttonsSetting[OpsButtonType.APPROVE]?.enable -->
-          <el-button
-            type="success"
-            v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.APPROVE]?.enable"
-            @click="handleAudit(item, true)"
-          >
-            <Icon icon="ep:select" />
-            <!-- TODO @jason:这个也是类似哈,搞个方法来生成名字 -->
-            {{
-              item.buttonsSetting?.[OperationButtonType.APPROVE]?.displayName ||
-              OPERATION_BUTTON_NAME.get(OperationButtonType.APPROVE)
-            }}
-          </el-button>
-          <el-button
-            v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.REJECT]?.enable"
-            type="danger"
-            @click="handleAudit(item, false)"
-          >
-            <Icon icon="ep:close" />
-            {{
-              item.buttonsSetting?.[OperationButtonType.REJECT].displayName ||
-              OPERATION_BUTTON_NAME.get(OperationButtonType.REJECT)
-            }}
-          </el-button>
-          <el-button
-            v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.TRANSFER]?.enable"
-            type="primary"
-            @click="openTaskUpdateAssigneeForm(item.id)"
-          >
-            <Icon icon="ep:edit" />
-            {{
-              item.buttonsSetting?.[OperationButtonType.TRANSFER]?.displayName ||
-              OPERATION_BUTTON_NAME.get(OperationButtonType.TRANSFER)
-            }}
-          </el-button>
-          <el-button
-            v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.DELEGATE]?.enable"
-            type="primary"
-            @click="handleDelegate(item)"
-          >
-            <Icon icon="ep:position" />
-            {{
-              item.buttonsSetting?.[OperationButtonType.DELEGATE]?.displayName ||
-              OPERATION_BUTTON_NAME.get(OperationButtonType.DELEGATE)
-            }}
-          </el-button>
-          <el-button
-            v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.ADD_SIGN]?.enable"
-            type="primary"
-            @click="handleSign(item)"
-          >
-            <Icon icon="ep:plus" />
-            {{
-              item.buttonsSetting?.[OperationButtonType.ADD_SIGN]?.displayName ||
-              OPERATION_BUTTON_NAME.get(OperationButtonType.ADD_SIGN)
-            }}
-          </el-button>
-          <el-button
-            v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.RETURN]?.enable"
-            type="warning"
-            @click="handleBack(item)"
+  <ContentWrap :bodyStyle="{ padding: '10px 20px 0' }" class="position-relative">
+    <div class="processInstance-wrap-main">
+      <el-scrollbar>
+        <img
+          class="position-absolute right-20px"
+          width="150"
+          :src="auditIcons[processInstance.status]"
+          alt=""
+        />
+        <div class="text-#878c93 h-15px">编号:{{ id }}</div>
+        <el-divider class="!my-8px" />
+        <div class="flex items-center gap-5 mb-10px h-40px">
+          <div class="text-26px font-bold mb-5px">{{ processInstance.name }}</div>
+          <dict-tag
+            v-if="processInstance.status"
+            :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS"
+            :value="processInstance.status"
+          />
+        </div>
+
+        <div class="flex items-center gap-5 mb-10px text-13px h-35px">
+          <div
+            class="bg-gray-100 h-35px rounded-3xl flex items-center p-8px gap-2 dark:color-gray-600"
           >
-            <Icon icon="ep:back" />
-            {{
-              item.buttonsSetting?.[OperationButtonType.RETURN]?.displayName ||
-              OPERATION_BUTTON_NAME.get(OperationButtonType.RETURN)
-            }}
-          </el-button>
+            <el-avatar
+              :size="28"
+              v-if="processInstance?.startUser?.avatar"
+              :src="processInstance?.startUser?.avatar"
+            />
+            <el-avatar :size="28" v-else-if="processInstance?.startUser?.nickname">
+              {{ processInstance?.startUser?.nickname.substring(0, 1) }}
+            </el-avatar>
+            {{ processInstance?.startUser?.nickname }}
+          </div>
+          <div class="text-#878c93"> {{ formatDate(processInstance.startTime) }} 提交 </div>
         </div>
-      </el-col>
-    </el-card>
 
-    <!-- 申请信息 -->
-    <el-card v-loading="processInstanceLoading" class="box-card">
-      <template #header>
-        <span class="el-icon-document">申请信息【{{ processInstance.name }}】</span>
-      </template>
-      <!-- 情况一:流程表单 -->
-      <el-col v-if="processInstance?.processDefinition?.formType === 10" :offset="6" :span="16">
-        <form-create
-          v-model="detailForm.value"
-          v-model:api="fApi"
-          :option="detailForm.option"
-          :rule="detailForm.rule"
-        />
-      </el-col>
-      <!-- 情况二:业务表单 -->
-      <div v-if="processInstance?.processDefinition?.formType === 20">
-        <BusinessFormComponent :id="processInstance.businessKey" />
-      </div>
-    </el-card>
+        <el-tabs v-model="activeTab">
+          <!-- 表单信息 -->
+          <el-tab-pane label="审批详情" name="form">
+            <div class="form-scroll-area">
+              <el-scrollbar>
+                <el-row>
+                  <el-col :span="18" class="!flex !flex-col formCol">
+                    <!-- 表单信息 -->
+                    <div
+                      v-loading="processInstanceLoading"
+                      class="form-box flex flex-col mb-30px flex-1"
+                    >
+                      <!-- 情况一:流程表单 -->
+                      <el-col v-if="processDefinition?.formType === 10">
+                        <form-create
+                          v-model="detailForm.value"
+                          v-model:api="fApi"
+                          :option="detailForm.option"
+                          :rule="detailForm.rule"
+                        />
+                      </el-col>
+                      <!-- 情况二:业务表单 -->
+                      <div v-if="processDefinition?.formType === 20">
+                        <BusinessFormComponent :id="processInstance.businessKey" />
+                      </div>
+                    </div>
+                  </el-col>
+                  <el-col :span="6">
+                    <!-- 审批记录时间线 -->
+                    <ProcessInstanceTimeline ref="timelineRef" :approve-nodes="approveNodes" />
+                  </el-col>
+                </el-row>
+              </el-scrollbar>
+            </div>
+          </el-tab-pane>
+
+          <!-- 流程图 -->
+          <el-tab-pane label="流程图" name="diagram">
+            <div class="form-scroll-area">
+              <ProcessInstanceBpmnViewer
+                :id="`${id}`"
+                :loading="processInstanceLoading"
+                :show-header="false"
+              />
+            </div>
+          </el-tab-pane>
 
-    <!-- 审批记录 -->
-    <ProcessInstanceTaskList
-      :loading="tasksLoad"
-      :process-instance="processInstance"
-      :tasks="tasks"
-      @refresh="getTaskList"
-    />
+          <!-- 流转记录 -->
+          <el-tab-pane label="流转记录" name="record">
+            <div class="form-scroll-area">
+              <el-scrollbar>
+                <ProcessInstanceTaskList
+                  :loading="tasksLoad"
+                  :process-instance="processInstance"
+                  :tasks="tasks"
+                  :show-header="false"
+                />
+              </el-scrollbar>
+            </div>
+          </el-tab-pane>
 
-    <!-- 高亮流程图 -->
-    <ProcessInstanceBpmnViewer :id="`${id}`" :loading="processInstanceLoading" />
+          <!-- 流转评论 TODO 待开发 -->
+          <el-tab-pane label="流转评论" name="comment" v-if="false">
+            <div class="form-scroll-area">
+              <el-scrollbar> 流转评论 </el-scrollbar>
+            </div>
+          </el-tab-pane>
+        </el-tabs>
 
-    <!-- 弹窗:转派审批人 -->
-    <TaskTransferForm ref="taskTransferFormRef" @success="getDetail" />
-    <!-- 弹窗:退回节点 -->
-    <TaskReturnForm ref="taskReturnFormRef" @success="getDetail" />
-    <!-- 弹窗:委派,将任务委派给别人处理,处理完成后,会重新回到原审批人手中-->
-    <TaskDelegateForm ref="taskDelegateForm" @success="getDetail" />
-    <!-- 弹窗:加签,当前任务审批人为A,向前加签选了一个C,则需要C先审批,然后再是A审批,向后加签B,A审批完,需要B再审批完,才算完成这个任务节点 -->
-    <TaskSignCreateForm ref="taskSignCreateFormRef" @success="getDetail" />
+        <div class="b-t-solid border-t-1px border-[var(--el-border-color)]">
+          <!-- 操作栏按钮 -->
+          <ProcessInstanceOperationButton
+            ref="operationButtonRef"
+            :process-instance="processInstance"
+            :process-definition="processDefinition"
+            :userOptions="userOptions"
+            @success="refresh"
+          />
+        </div>
+      </el-scrollbar>
+    </div>
   </ContentWrap>
 </template>
 <script lang="ts" setup>
-import { useUserStore } from '@/store/modules/user'
+import { formatDate } from '@/utils/formatTime'
+import { DICT_TYPE } from '@/utils/dict'
 import { setConfAndFields2 } from '@/utils/formCreate'
 import type { ApiAttrs } from '@form-create/element-ui/types/config'
-import * as DefinitionApi from '@/api/bpm/definition'
 import * as ProcessInstanceApi from '@/api/bpm/processInstance'
 import * as TaskApi from '@/api/bpm/task'
 import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
 import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
-import TaskReturnForm from './dialog/TaskReturnForm.vue'
-import TaskDelegateForm from './dialog/TaskDelegateForm.vue'
-import TaskTransferForm from './dialog/TaskTransferForm.vue'
-import TaskSignCreateForm from './dialog/TaskSignCreateForm.vue'
-import { registerComponent } from '@/utils/routerHelper'
-import { isEmpty } from '@/utils/is'
+import ProcessInstanceOperationButton from './ProcessInstanceOperationButton.vue'
+import ProcessInstanceTimeline from './ProcessInstanceTimeline.vue'
 import * as UserApi from '@/api/system/user'
-import {
-  OperationButtonType,
-  OPERATION_BUTTON_NAME
-} from '@/components/SimpleProcessDesignerV2/src/consts'
+import { FieldPermissionType } from '@/components/SimpleProcessDesignerV2/src/consts'
+import audit1 from '@/assets/svgs/bpm/audit1.svg'
+import audit2 from '@/assets/svgs/bpm/audit2.svg'
+import audit3 from '@/assets/svgs/bpm/audit3.svg'
+import audit4 from '@/assets/svgs/bpm/audit4.svg'
 
 defineOptions({ name: 'BpmProcessInstanceDetail' })
-
-const { query } = useRoute() // 查询参数
+const props = defineProps<{
+  id: string // 流程实例的编号
+  taskId?: string // 任务编号
+  activityId?: string //流程活动编号,用于抄送查看
+}>()
 const message = useMessage() // 消息弹窗
-const { proxy } = getCurrentInstance() as any
-
-const userId = useUserStore().getUser.id // 当前登录的编号
-const id = query.id as unknown as string // 流程实例的编号
 const processInstanceLoading = ref(false) // 流程实例的加载中
 const processInstance = ref<any>({}) // 流程实例
-const bpmnXml = ref('') // BPMN XML
+const processDefinition = ref<any>({}) // 流程定义
+const timelineRef = ref()
+// 操作按钮组件 ref
+const operationButtonRef = ref()
 const tasksLoad = ref(true) // 任务的加载中
 const tasks = ref<any[]>([]) // 任务列表
-// ========== 审批信息 ==========
-const runningTasks = ref<any[]>([]) // 运行中的任务
-const auditForms = ref<any[]>([]) // 审批任务的表单
-const auditRule = reactive({
-  reason: [{ required: true, message: '审批建议不能为空', trigger: 'blur' }]
-})
-const approveForms = ref<any[]>([]) // 审批通过时,额外的补充信息
-const approveFormFApis = ref<ApiAttrs[]>([]) // approveForms 的 fAPi
+const auditIcons = {
+  1: audit1,
+  2: audit2,
+  3: audit3,
+  4: audit4
+}
 
 // ========== 申请信息 ==========
 const fApi = ref<ApiAttrs>() //
@@ -220,163 +167,106 @@ const detailForm = ref({
   value: {}
 }) // 流程实例的表单详情
 
-/** 监听 approveFormFApis,实现它对应的 form-create 初始化后,隐藏掉对应的表单提交按钮 */
-watch(
-  () => approveFormFApis.value,
-  (value) => {
-    value?.forEach((api) => {
-      api.btn.show(false)
-      api.resetBtn.show(false)
-    })
-  },
-  {
-    deep: true
-  }
-)
-
-/** 处理审批通过和不通过的操作 */
-const handleAudit = async (task, pass) => {
-  // 1.1 获得对应表单
-  const index = runningTasks.value.indexOf(task)
-  const auditFormRef = proxy.$refs['form' + index][0]
-  // 1.2 校验表单
-  const elForm = unref(auditFormRef)
-  if (!elForm) return
-  let valid = await elForm.validate()
-  if (!valid) return
-  // 校验申请表单(可编辑字段)
-  // TODO @jason:之前这里是 if (!fApi.value) return;针对业务表单的情况下,会导致没办法审核,可能要看下。我这里改了点,看看是不是还有别的地方兼容性
-  if (fApi.value) {
-    valid = await fApi.value.validate()
-    if (!valid) return
-  }
-
-  // 2.1 提交审批
-  const data = {
-    id: task.id,
-    reason: auditForms.value[index].reason,
-    copyUserIds: auditForms.value[index].copyUserIds
-  }
-  if (pass) {
-    // 审批通过,并且有额外的 approveForm 表单,需要校验 + 拼接到 data 表单里提交
-    const formCreateApi = approveFormFApis.value[index]
-    if (formCreateApi) {
-      await formCreateApi.validate()
-      data.variables = approveForms.value[index].value
-    }
-    // 获取表单可编辑字段的值
-    if (fApi.value) {
-      data.variables = getWritableValueOfForm(task.fieldsPermission)
-    }
-
-    await TaskApi.approveTask(data)
-    message.success('审批通过成功')
-  } else {
-    await TaskApi.rejectTask(data)
-    message.success('审批不通过成功')
-  }
-  // 2.2 加载最新数据
-  getDetail()
-}
-
-/** 转派审批人 */
-const taskTransferFormRef = ref()
-const openTaskUpdateAssigneeForm = (id: string) => {
-  taskTransferFormRef.value.open(id)
-}
-
-/** 处理审批退回的操作 */
-const taskDelegateForm = ref()
-const handleDelegate = async (task) => {
-  taskDelegateForm.value.open(task.id)
-}
-
-/** 处理审批退回的操作 */
-const taskReturnFormRef = ref()
-const handleBack = async (task: any) => {
-  taskReturnFormRef.value.open(task.id)
-}
-
-/** 处理审批加签的操作 */
-const taskSignCreateFormRef = ref()
-const handleSign = async (task: any) => {
-  taskSignCreateFormRef.value.open(task.id)
-}
-
 /** 获得详情 */
-const getDetail = async () => {
-  // 1. 获得流程任务列表(审批记录)。 需要先获取任务,表单的权限设置需要根据任务来设置
-  await getTaskList()
-  // 2. 获得流程实例相关
-  getProcessInstance()
+const getDetail = () => {
+  // 1. 获取审批详情
+  getApprovalDetail()
+  // 2. 获得流程任务列表
+  getTaskList()
 }
 
 /** 加载流程实例 */
-const BusinessFormComponent = ref(null) // 异步组件
-const getProcessInstance = async () => {
+const BusinessFormComponent = ref<any>(null) // 异步组件
+/** 获取审批详情 */
+const getApprovalDetail = async () => {
+  processInstanceLoading.value = true
   try {
-    processInstanceLoading.value = true
-    const data = await ProcessInstanceApi.getProcessInstance(id)
+    const param = {
+      processInstanceId: props.id,
+      activityId: props.activityId,
+      taskId: props.taskId
+    }
+    const data = await ProcessInstanceApi.getApprovalDetail(param)
     if (!data) {
+      message.error('查询不到审批详情信息!')
+      return
+    }
+    if (!data.processDefinition || !data.processInstance) {
       message.error('查询不到流程信息!')
       return
     }
-    processInstance.value = data
+    processInstance.value = data.processInstance
+    processDefinition.value = data.processDefinition
 
     // 设置表单信息
-    const processDefinition = data.processDefinition
-    if (processDefinition.formType === 10) {
+    if (processDefinition.value.formType === 10) {
+      // 获取表单字段权限
+      const formFieldsPermission = data.formFieldsPermission
+
       if (detailForm.value.rule.length > 0) {
-        detailForm.value.value = data.formVariables
+        // 避免刷新 form-create 显示不了,
+        detailForm.value.value = processInstance.value.formVariables
       } else {
         setConfAndFields2(
           detailForm,
-          processDefinition.formConf,
-          processDefinition.formFields,
-          data.formVariables
+          processDefinition.value.formConf,
+          processDefinition.value.formFields,
+          processInstance.value.formVariables
         )
       }
       nextTick().then(() => {
         fApi.value?.btn.show(false)
         fApi.value?.resetBtn.show(false)
+        //@ts-ignore
         fApi.value?.disabled(true)
-        // 设置表单权限。后续需要改造成。只处理一个运行中的任务
-        if (runningTasks.value.length > 0) {
-          const task = runningTasks.value.at(0)
-          if (task.fieldsPermission) {
-            Object.keys(task.fieldsPermission).forEach((item) => {
-              setFieldPermission(item, task.fieldsPermission[item])
-            })
-          }
+        // 设置表单字段权限
+        if (formFieldsPermission) {
+          Object.keys(data.formFieldsPermission).forEach((item) => {
+            setFieldPermission(item, formFieldsPermission[item])
+          })
         }
       })
-    } else {
-      // 注意:data.processDefinition.formCustomViewPath 是组件的全路径,例如说:/crm/contract/detail/index.vue
-      BusinessFormComponent.value = registerComponent(data.processDefinition.formCustomViewPath)
     }
 
-    // 加载流程图
-    bpmnXml.value = (
-      await DefinitionApi.getProcessDefinition(processDefinition.id as number)
-    )?.bpmnXml
+    // 获取审批节点,显示 Timeline 的数据
+    approveNodes.value = data.approveNodes
+
+    // 获取待办任务显示操作按钮
+    operationButtonRef.value?.loadTodoTask(data.todoTask)
   } finally {
     processInstanceLoading.value = false
   }
 }
 
+// 审批节点信息
+const approveNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([])
+/**
+ * 设置表单权限
+ */
+const setFieldPermission = (field: string, permission: string) => {
+  if (permission === FieldPermissionType.READ) {
+    //@ts-ignore
+    fApi.value?.disabled(true, field)
+  }
+  if (permission === FieldPermissionType.WRITE) {
+    //@ts-ignore
+    fApi.value?.disabled(false, field)
+  }
+  if (permission === FieldPermissionType.NONE) {
+    //@ts-ignore
+    fApi.value?.hidden(true, field)
+  }
+}
+
 /** 加载任务列表 */
 const getTaskList = async () => {
-  runningTasks.value = []
-  auditForms.value = []
-  approveForms.value = []
-  approveFormFApis.value = []
   try {
     // 获得未取消的任务
     tasksLoad.value = true
-    const data = await TaskApi.getTaskListByProcessInstanceId(id)
+    const data = await TaskApi.getTaskListByProcessInstanceId(props.id)
     tasks.value = []
     // 1.1 移除已取消的审批
-    data.forEach((task) => {
+    data.forEach((task: any) => {
       if (task.status !== 4) {
         tasks.value.push(task)
       }
@@ -395,77 +285,21 @@ const getTaskList = async () => {
         return b.createTime - a.createTime
       }
     })
-
-    // 获得需要自己审批的任务
-    loadRunningTask(tasks.value)
   } finally {
     tasksLoad.value = false
   }
 }
 
 /**
- * 设置 runningTasks 中的任务
+ * 操作成功后刷新
  */
-const loadRunningTask = (tasks) => {
-  tasks.forEach((task) => {
-    if (!isEmpty(task.children)) {
-      loadRunningTask(task.children)
-    }
-    // 2.1 只有待处理才需要
-    if (task.status !== 1 && task.status !== 6) {
-      return
-    }
-    // 2.2 自己不是处理人
-    if (!task.assigneeUser || task.assigneeUser.id !== userId) {
-      return
-    }
-
-    // 2.3 添加到处理任务
-    runningTasks.value.push({ ...task })
-    auditForms.value.push({
-      reason: '',
-      copyUserIds: []
-    })
-
-    // 2.4 处理 approve 表单
-    if (task.formId && task.formConf) {
-      const approveForm = {}
-      setConfAndFields2(approveForm, task.formConf, task.formFields, task.formVariables)
-      approveForms.value.push(approveForm)
-    } else {
-      approveForms.value.push({}) // 占位,避免为空
-    }
-  })
+const refresh = () => {
+  // 重新获取详情
+  getDetail()
 }
 
-/**
- * 设置表单权限
- */
-const setFieldPermission = (field: string, permission: string) => {
-  if (permission === '1') {
-    fApi.value?.disabled(true, field)
-  }
-  if (permission === '2') {
-    fApi.value?.disabled(false, field)
-  }
-  if (permission === '3') {
-    fApi.value?.hidden(true, field)
-  }
-}
-/**
- * 获取可以编辑字段的值
- */
-const getWritableValueOfForm = (fieldsPermission: Object) => {
-  const fieldsValue = {}
-  if (fieldsPermission && fApi.value) {
-    Object.keys(fieldsPermission).forEach((item) => {
-      if (fieldsPermission[item] === '2') {
-        fieldsValue[item] = fApi.value.getValue(item)
-      }
-    })
-  }
-  return fieldsValue
-}
+/** 当前的Tab */
+const activeTab = ref('form')
 
 /** 初始化 */
 const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
@@ -475,3 +309,38 @@ onMounted(async () => {
   userOptions.value = await UserApi.getSimpleUserList()
 })
 </script>
+
+<style lang="scss" scoped>
+$wrap-padding-height: 20px;
+$wrap-margin-height: 15px;
+$button-height: 51px;
+$process-header-height: 194px;
+
+.processInstance-wrap-main {
+  height: calc(
+    100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
+  );
+  max-height: calc(
+    100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
+  );
+  overflow: auto;
+
+  .form-scroll-area {
+    height: calc(
+      100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
+        $process-header-height - 40px
+    );
+    max-height: calc(
+      100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
+        $process-header-height - 40px
+    );
+    overflow: auto;
+  }
+}
+
+.form-box {
+  :deep(.el-card) {
+    border: none;
+  }
+}
+</style>

+ 0 - 341
src/views/bpm/processInstance/detail/index_new.vue

@@ -1,341 +0,0 @@
-<template>
-  <ContentWrap :bodyStyle="{ padding: '10px 20px 0' }" class="position-relative">
-    <div class="processInstance-wrap-main">
-      <el-scrollbar>
-        <img
-          class="position-absolute right-20px"
-          width="150"
-          :src="auditIcons[processInstance.status]"
-          alt=""
-        />
-        <div class="text-#878c93 h-15px">编号:{{ id }}</div>
-        <el-divider class="!my-8px" />
-        <div class="flex items-center gap-5 mb-10px h-40px">
-          <div class="text-26px font-bold mb-5px">{{ processInstance.name }}</div>
-          <dict-tag
-            v-if="processInstance.status"
-            :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS"
-            :value="processInstance.status"
-          />
-        </div>
-
-        <div class="flex items-center gap-5 mb-10px text-13px h-35px">
-          <div class="bg-gray-100 h-35px rounded-3xl flex items-center p-8px gap-2 dark:color-gray-600">
-            <el-avatar
-              :size="28"
-              v-if="processInstance?.startUser?.avatar"
-              :src="processInstance?.startUser?.avatar"
-            />
-            <el-avatar :size="28" v-else-if="processInstance?.startUser?.nickname">
-              {{ processInstance?.startUser?.nickname.substring(0, 1) }}
-            </el-avatar>
-            {{ processInstance?.startUser?.nickname }}
-          </div>
-          <div class="text-#878c93"> {{ formatDate(processInstance.startTime) }} 提交 </div>
-        </div>
-
-        <el-tabs v-model="activeTab">
-          <!-- 表单信息 -->
-          <el-tab-pane label="审批详情" name="form">
-            <div class="form-scroll-area">
-              <el-scrollbar>
-                <el-row>
-                  <el-col :span="18" class="!flex !flex-col formCol">
-                    <!-- 表单信息 -->
-                    <div
-                      v-loading="processInstanceLoading"
-                      class="form-box flex flex-col mb-30px flex-1"
-                    >
-                      <!-- 情况一:流程表单 -->
-                      <el-col v-if="processDefinition?.formType === 10">
-                        <form-create
-                          v-model="detailForm.value"
-                          v-model:api="fApi"
-                          :option="detailForm.option"
-                          :rule="detailForm.rule"
-                        />
-                      </el-col>
-                      <!-- 情况二:业务表单 -->
-                      <div v-if="processDefinition?.formType === 20">
-                        <BusinessFormComponent :id="processInstance.businessKey" />
-                      </div>
-                    </div>
-                  </el-col>
-                  <el-col :span="6">
-                    <!-- 审批记录时间线 -->
-                    <ProcessInstanceTimeline ref="timelineRef" :approve-nodes="approveNodes" />
-                  </el-col>
-                </el-row>
-              </el-scrollbar>
-            </div>
-          </el-tab-pane>
-
-          <!-- 流程图 -->
-          <el-tab-pane label="流程图" name="diagram">
-            <div class="form-scroll-area">
-              <ProcessInstanceBpmnViewer :id="`${id}`" :loading="processInstanceLoading" :show-header="false"/>
-            </div>
-          </el-tab-pane>
-
-          <!-- 流转记录 -->
-          <el-tab-pane label="流转记录" name="record">
-            <div class="form-scroll-area">
-              <el-scrollbar>
-                <ProcessInstanceTaskList
-                  :loading="tasksLoad"
-                  :process-instance="processInstance"
-                  :tasks="tasks"
-                  :show-header="false"
-                />
-              </el-scrollbar>
-            </div>
-          </el-tab-pane>
-
-          <!-- 流转评论 TODO 待开发 -->
-          <el-tab-pane label="流转评论" name="comment">
-            <div class="form-scroll-area">
-              <el-scrollbar> 流转评论 </el-scrollbar>
-            </div>
-          </el-tab-pane>
-        </el-tabs>
-
-        <div class="b-t-solid border-t-1px border-[var(--el-border-color)]">
-          <!-- 操作栏按钮 -->
-          <ProcessInstanceOperationButton
-            ref="operationButtonRef"
-            :process-instance="processInstance"
-            :process-definition="processDefinition"
-            :userOptions="userOptions"
-            @success="refresh"
-          />
-        </div>
-      </el-scrollbar>
-    </div>
-  </ContentWrap>
-</template>
-<script lang="ts" setup>
-import { formatDate } from '@/utils/formatTime'
-import { DICT_TYPE } from '@/utils/dict'
-import { setConfAndFields2 } from '@/utils/formCreate'
-import type { ApiAttrs } from '@form-create/element-ui/types/config'
-import * as ProcessInstanceApi from '@/api/bpm/processInstance'
-import * as TaskApi from '@/api/bpm/task'
-import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
-import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
-import ProcessInstanceOperationButton from './ProcessInstanceOperationButton.vue'
-import ProcessInstanceTimeline from './ProcessInstanceTimeline.vue'
-import * as UserApi from '@/api/system/user'
-import { FieldPermissionType } from '@/components/SimpleProcessDesignerV2/src/consts'
-import audit1 from '@/assets/svgs/bpm/audit1.svg'
-import audit2 from '@/assets/svgs/bpm/audit2.svg'
-import audit3 from '@/assets/svgs/bpm/audit3.svg'
-import audit4 from '@/assets/svgs/bpm/audit4.svg'
-
-defineOptions({ name: 'BpmProcessInstanceDetail' })
-const props = defineProps<{
-  id: string // 流程实例的编号
-  taskId?: string // 任务编号
-  activityId?: string //流程活动编号,用于抄送查看
-}>()
-const message = useMessage() // 消息弹窗
-const processInstanceLoading = ref(false) // 流程实例的加载中
-const processInstance = ref<any>({}) // 流程实例
-const processDefinition = ref<any>({}) // 流程定义
-const timelineRef = ref()
-// 操作按钮组件 ref
-const operationButtonRef = ref()
-const tasksLoad = ref(true) // 任务的加载中
-const tasks = ref<any[]>([]) // 任务列表
-const auditIcons = {
-  1: audit1,
-  2: audit2,
-  3: audit3,
-  4: audit4
-}
-
-// ========== 申请信息 ==========
-const fApi = ref<ApiAttrs>() //
-const detailForm = ref({
-  rule: [],
-  option: {},
-  value: {}
-}) // 流程实例的表单详情
-
-/** 获得详情 */
-const getDetail = async () => {
-  // 1. 获取审批详情
-  getApprovalDetail()
-  // 2. 获得流程任务列表
-  getTaskList()
-}
-
-/** 加载流程实例 */
-const BusinessFormComponent = ref<any>(null) // 异步组件
-/** 获取审批详情 */
-const getApprovalDetail = async () => {
-  processInstanceLoading.value = true
-  try {
-    const param = {
-      processInstanceId: props.id,
-      activityId: props.activityId,
-      taskId: props.taskId
-    }
-    const data = await ProcessInstanceApi.getApprovalDetail(param);
-    if (!data) {
-      message.error('查询不到审批详情信息!')
-      return
-    }
-    if(!data.processDefinition || !data.processInstance) {
-      message.error('查询不到流程信息!')
-      return
-    }
-    processInstance.value = data.processInstance
-    processDefinition.value = data.processDefinition
-
-    // 设置表单信息
-    if (processDefinition.value.formType === 10) {
-      // 获取表单字段权限
-      const formFieldsPermission = data.formFieldsPermission
-     
-      if (detailForm.value.rule.length > 0) {  // 避免刷新 form-create 显示不了,
-        detailForm.value.value = processInstance.value.formVariables
-      } else {
-        setConfAndFields2(
-          detailForm,
-          processDefinition.value.formConf,
-          processDefinition.value.formFields,
-          processInstance.value.formVariables
-        )
-      }
-      nextTick().then(() => {
-        fApi.value?.btn.show(false)
-        fApi.value?.resetBtn.show(false)
-        //@ts-ignore
-        fApi.value?.disabled(true)
-        // 设置表单字段权限
-        if (formFieldsPermission) {
-          Object.keys(data.formFieldsPermission).forEach((item) => {
-            setFieldPermission(item, formFieldsPermission[item])
-          })
-        }
-      })
-    }
-
-    // 获取审批节点,显示 Timeline 的数据
-    approveNodes.value = data.approveNodes
-
-    // 获取待办任务显示操作按钮
-    operationButtonRef.value?.loadTodoTask(data.todoTask)
-
-  } finally {
-    processInstanceLoading.value = false
-  }
-  
-}
-
-// 审批节点信息
-const approveNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([])
-/**
- * 设置表单权限
- */
-const setFieldPermission = (field: string, permission: string) => {
-  if (permission === FieldPermissionType.READ) {
-    //@ts-ignore
-    fApi.value?.disabled(true, field)
-  }
-  if (permission === FieldPermissionType.WRITE) {
-    //@ts-ignore
-    fApi.value?.disabled(false, field)
-  }
-  if (permission === FieldPermissionType.NONE) {
-    //@ts-ignore
-    fApi.value?.hidden(true, field)
-  }
-}
-
-/** 加载任务列表 */
-const getTaskList = async () => {
-  try {
-    // 获得未取消的任务
-    tasksLoad.value = true
-    const data = await TaskApi.getTaskListByProcessInstanceId(props.id)
-    tasks.value = []
-    // 1.1 移除已取消的审批
-    data.forEach((task: any) => {
-      if (task.status !== 4) {
-        tasks.value.push(task)
-      }
-    })
-    // 1.2 排序,将未完成的排在前面,已完成的排在后面;
-    tasks.value.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
-      }
-    })
-  } finally {
-    tasksLoad.value = false
-  }
-}
-
-/**
- * 操作成功后刷新
- */
-const refresh = () => {
-  // 重新获取详情
-  getDetail()
-}
-
-/** 当前的Tab */
-const activeTab = ref('form')
-
-/** 初始化 */
-const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
-onMounted(async () => {
-  getDetail()
-  // 获得用户列表
-  userOptions.value = await UserApi.getSimpleUserList()
-})
-</script>
-
-<style lang="scss" scoped>
-$wrap-padding-height: 20px;
-$wrap-margin-height: 15px;
-$button-height: 51px;
-$process-header-height: 194px;
-
-.processInstance-wrap-main {
-  height: calc(
-    100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
-  );
-  max-height: calc(
-    100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
-  );
-  overflow: auto;
-
-  .form-scroll-area {
-    height: calc(
-      100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
-        $process-header-height - 40px
-    );
-    max-height: calc(
-      100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
-        $process-header-height - 40px
-    );
-    overflow: auto;
-  }
-}
-
-.form-box {
-  :deep(.el-card) {
-    border: none;
-  }
-}
-</style>