From b725cca0ff60728be04ef2ddb4cde5fcdf80bf8e Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期一, 14 九月 2026 16:15:24 +0800
Subject: [PATCH] feat(bpm): 流程模型列表新增「免审批」开关与全局总开关

---
 src/api/bpm/model/index.ts                               |    2 
 src/api/bpm/autoApprove/index.ts                         |   59 ++++++++++++++
 src/views/bpm/model/index.vue                            |   87 ++++++++++++++++++++-
 src/views/bpm/model/modules/category-draggable-model.vue |   55 +++++++++++++
 src/views/bpm/model/data.ts                              |    6 +
 5 files changed, 205 insertions(+), 4 deletions(-)

diff --git a/src/api/bpm/autoApprove/index.ts b/src/api/bpm/autoApprove/index.ts
new file mode 100644
index 0000000..def749a
--- /dev/null
+++ b/src/api/bpm/autoApprove/index.ts
@@ -0,0 +1,59 @@
+import { requestClient } from '#/api/request';
+
+export namespace BpmAutoApproveApi {
+  /** 娴佺▼鍏嶅鎵归厤缃� */
+  export interface AutoApproveConfig {
+    id: number;
+    /** 娴佺▼瀹氫箟鐨� Key */
+    processDefinitionKey: string;
+    /** 鏄惁鍏嶅鎵癸細true-鎻愪氦鍚庣郴缁熻嚜鍔ㄩ�氳繃锛宖alse-姝e父浜哄伐瀹℃壒 */
+    autoApprove: boolean;
+    /** 澶囨敞 */
+    remark?: string;
+    createTime: string;
+  }
+}
+
+/** 鑾峰彇鍏ㄩ儴娴佺▼鍏嶅鎵归厤缃� */
+export async function getAutoApproveList() {
+  return requestClient.get<BpmAutoApproveApi.AutoApproveConfig[]>(
+    '/bpm/process-auto-approve/list',
+  );
+}
+
+/** 鑾峰緱鍏ㄥ眬鍏嶅鎵规�诲紑鍏崇姸鎬� */
+export async function getGlobalAutoApprove() {
+  return requestClient.get<boolean>('/bpm/process-auto-approve/global');
+}
+
+/** 璁剧疆鍏ㄥ眬鍏嶅鎵规�诲紑鍏筹細寮�鍚悗鎵�鏈夋祦绋嬩竴寰嬪厤瀹℃壒 */
+export async function updateGlobalAutoApprove(
+  autoApprove: boolean,
+  remark?: string,
+) {
+  const params = new URLSearchParams({ autoApprove: String(autoApprove) });
+  if (remark) {
+    params.append('remark', remark);
+  }
+  return requestClient.put<boolean>(
+    `/bpm/process-auto-approve/update-global?${params.toString()}`,
+  );
+}
+
+/** 鎸夋祦绋嬪畾涔夌殑 Key 璁剧疆鍏嶅鎵瑰紑鍏筹紙涓嶅瓨鍦ㄥ垯鏂板銆佸瓨鍦ㄥ垯鏇存柊锛� */
+export async function updateAutoApproveByKey(
+  processDefinitionKey: string,
+  autoApprove: boolean,
+  remark?: string,
+) {
+  const params = new URLSearchParams({
+    processDefinitionKey,
+    autoApprove: String(autoApprove),
+  });
+  if (remark) {
+    params.append('remark', remark);
+  }
+  return requestClient.put<boolean>(
+    `/bpm/process-auto-approve/update-by-key?${params.toString()}`,
+  );
+}
diff --git a/src/api/bpm/model/index.ts b/src/api/bpm/model/index.ts
index e0c033a..8c83a1c 100644
--- a/src/api/bpm/model/index.ts
+++ b/src/api/bpm/model/index.ts
@@ -20,6 +20,8 @@
     createTime: string;
     bpmnXml: string;
     startUsers?: UserInfo[];
+    /** 鏄惁鍏嶅鎵癸紙鐢遍〉闈㈡牴鎹祦绋嬪厤瀹℃壒閰嶇疆鍚堝苟锛岄潪鍚庣杩斿洖瀛楁锛� */
+    autoApprove?: boolean;
   }
 
   /** 娴佺▼瀹氫箟 */
diff --git a/src/views/bpm/model/data.ts b/src/views/bpm/model/data.ts
index 59405e7..c1be1bb 100644
--- a/src/views/bpm/model/data.ts
+++ b/src/views/bpm/model/data.ts
@@ -40,6 +40,12 @@
       slots: { default: 'deploymentTime' },
     },
     {
+      field: 'autoApprove',
+      title: '鍏嶅鎵�',
+      minWidth: 110,
+      slots: { default: 'autoApprove' },
+    },
+    {
       title: '鎿嶄綔',
       width: 150,
       fixed: 'right',
diff --git a/src/views/bpm/model/index.vue b/src/views/bpm/model/index.vue
index d7b2b7d..2ed2171 100644
--- a/src/views/bpm/model/index.vue
+++ b/src/views/bpm/model/index.vue
@@ -1,15 +1,37 @@
 <script lang="ts" setup>
 import type { ModelCategoryInfo } from '#/api/bpm/model';
 
-import { onActivated, onMounted, reactive, ref, useTemplateRef, watch } from 'vue';
+import {
+  computed,
+  onActivated,
+  onMounted,
+  reactive,
+  ref,
+  useTemplateRef,
+  watch,
+} from 'vue';
 
+import { useAccess } from '@vben/access';
 import { Page, useVbenModal } from '@vben/common-ui';
 import { IconifyIcon } from '@vben/icons';
 import { cloneDeep } from '@vben/utils';
 
 import { useSortable } from '@vueuse/integrations/useSortable';
-import { Button, Card, Dropdown, Input, Menu, message } from 'ant-design-vue';
+import {
+  Button,
+  Card,
+  Dropdown,
+  Input,
+  Menu,
+  message,
+  Switch,
+  Tooltip,
+} from 'ant-design-vue';
 
+import {
+  getAutoApproveList,
+  updateGlobalAutoApprove,
+} from '#/api/bpm/autoApprove';
 import {
   getCategorySimpleList,
   updateCategorySortBatch,
@@ -26,6 +48,17 @@
 });
 
 const modelListSpinning = ref(false); // 妯″瀷鍒楄〃鍔犺浇鐘舵��
+
+/** 鍏ㄥ眬鍏嶅鎵规�诲紑鍏冲崰鐢ㄧ殑娴佺▼ Key锛堜笌鍚庣 BpmProcessAutoApproveService.GLOBAL_KEY 淇濇寔涓�鑷达級 */
+const GLOBAL_AUTO_APPROVE_KEY = '*';
+const globalAutoApprove = ref(false); // 鍏ㄥ眬鍏嶅鎵规�诲紑鍏�
+const globalAutoApproveLoading = ref(false); // 鎬诲紑鍏虫彁浜ょ姸鎬�
+
+const { hasAccessByCodes } = useAccess();
+/** 鍏嶅鎵瑰紑鍏崇殑鏉冮檺锛氭棤鏉冮檺鏃跺紑鍏崇疆鐏帮紝閬垮厤鐐逛簡鎵嶆姤閿� */
+const hasPermiAutoApprove = computed(() => {
+  return hasAccessByCodes(['bpm:process-auto-approve:update']);
+});
 
 const saveSortLoading = ref(false); // 淇濆瓨鎺掑簭鐘舵��
 const categoryGroup = ref<ModelCategoryInfo[]>([]); // 鎸夌収 category 鍒嗙粍鐨勬暟鎹�
@@ -59,8 +92,22 @@
 async function getList() {
   modelListSpinning.value = true;
   try {
-    const modelList = await getModelList(queryParams.name);
-    const categoryList = await getCategorySimpleList();
+    const [modelList, categoryList, autoApproveList] = await Promise.all([
+      getModelList(queryParams.name),
+      getCategorySimpleList(),
+      getAutoApproveList(),
+    ]);
+    // 鍏嶅鎵归厤缃寜銆屾祦绋� Key銆嶅缓绔嬫槧灏勶紝鍚堝苟鍒版ā鍨嬭鏁版嵁涓婏紝渚涘垪琛ㄥ唴鐨勫厤瀹℃壒寮�鍏冲垪鐩存帴璇诲彇
+    const autoApproveMap = new Map<string, boolean>();
+    autoApproveList.forEach((config) => {
+      autoApproveMap.set(config.processDefinitionKey, config.autoApprove);
+    });
+    modelList.forEach((model: any) => {
+      model.autoApprove = autoApproveMap.get(model.key) ?? false;
+    });
+    // 鍏ㄥ眬鎬诲紑鍏充互鐗规畩 Key 瀛樻斁鍦ㄥ悓涓�鏉¢厤缃噷
+    globalAutoApprove.value =
+      autoApproveMap.get(GLOBAL_AUTO_APPROVE_KEY) ?? false;
     // 鎸夌収 category 鑱氬悎
     categoryGroup.value = categoryList.map((category: any) => ({
       ...category,
@@ -142,6 +189,28 @@
     sortableInstance.value.option('disabled', true);
   }
 }
+
+/** 鍒囨崲鍏ㄥ眬鍏嶅鎵规�诲紑鍏筹細寮�鍚悗鎵�鏈夋祦绋嬩竴寰嬪厤瀹℃壒 */
+async function handleGlobalAutoApproveChange(checked: boolean) {
+  globalAutoApprove.value = checked;
+  globalAutoApproveLoading.value = true;
+  try {
+    await updateGlobalAutoApprove(checked);
+    message.success(
+      checked
+        ? '宸插紑鍚叏灞�鍏嶅鎵癸細鎵�鏈夋祦绋嬫彁浜ゅ悗灏嗙洿鎺ラ�氳繃'
+        : '宸插叧闂叏灞�鍏嶅鎵癸細鎭㈠涓烘寜鍚勬祦绋嬪紑鍏冲垽瀹�',
+    );
+    // 閲嶆柊鎷夊彇鍒楄〃锛屽悓姝ュ悇娴佺▼寮�鍏崇殑灞曠ず
+    await getList();
+  } catch (error) {
+    // 澶辫触鍥炴粴
+    globalAutoApprove.value = !checked;
+    console.error('璁剧疆鍏ㄥ眬鍏嶅鎵瑰紑鍏冲け璐�', error);
+  } finally {
+    globalAutoApproveLoading.value = false;
+  }
+}
 </script>
 
 <template>
@@ -156,6 +225,16 @@
     >
       <template #extra>
         <div v-if="!isCategorySorting">
+          <Tooltip title="寮�鍚悗鎵�鏈夋祦绋嬩竴寰嬪厤瀹℃壒锛堜紭鍏堜簬鍚勬祦绋嬭嚜宸辩殑寮�鍏筹級">
+            <span class="mr-1">鍏ㄩ儴鍏嶅鎵�</span>
+          </Tooltip>
+          <Switch
+            :checked="globalAutoApprove"
+            :loading="globalAutoApproveLoading"
+            :disabled="!hasPermiAutoApprove"
+            class="mr-3"
+            @change="(checked: any) => handleGlobalAutoApproveChange(checked)"
+          />
           <Input
             v-model:value="queryParams.name"
             placeholder="鎼滅储娴佺▼"
diff --git a/src/views/bpm/model/modules/category-draggable-model.vue b/src/views/bpm/model/modules/category-draggable-model.vue
index 4071c1e..4950b9c 100644
--- a/src/views/bpm/model/modules/category-draggable-model.vue
+++ b/src/views/bpm/model/modules/category-draggable-model.vue
@@ -21,11 +21,13 @@
   Dropdown,
   Menu,
   message,
+  Switch,
   Tag,
   Tooltip,
 } from 'ant-design-vue';
 
 import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { updateAutoApproveByKey } from '#/api/bpm/autoApprove';
 import { deleteCategory } from '#/api/bpm/category';
 import {
   cleanModel,
@@ -64,6 +66,7 @@
 const isModelSorting = ref(false);
 const originalData = ref<BpmModelApi.Model[]>([]);
 const modelList = ref<BpmModelApi.Model[]>([]);
+const autoApproveLoadingId = ref<null | number>(null); // 鍏嶅鎵瑰紑鍏筹細姝e湪鎻愪氦鐨勬ā鍨嬬紪鍙�
 // TODO @jason锛氬彲浠ュ叏閮ㄥ睍寮�涔堬紵 @鑺嬭壙 涓婃璁ㄨ銆傚ソ鍍忔槸鍥犱负鎬ц兘闂鎵嶅彧灞曞紑绗竴涓垎绫�
 const isExpand = ref(props.isFirst); // 鏍规嵁鏄惁涓虹涓�涓垎绫�, 鏉ヨ缃垵濮嬪睍寮�鐘舵��
 
@@ -81,6 +84,10 @@
 });
 const hasPermiDeploy = computed(() => {
   return hasAccessByCodes(['bpm:model:deploy']);
+});
+/** 鍏嶅鎵瑰紑鍏崇殑鏉冮檺锛氭棤鏉冮檺鏃跺紑鍏崇疆鐏帮紝閬垮厤鐐逛簡鎵嶆姤閿� */
+const hasPermiAutoApprove = computed(() => {
+  return hasAccessByCodes(['bpm:process-auto-approve:update']);
 });
 
 const [Grid, gridApi] = useVbenVxeGrid({
@@ -287,6 +294,32 @@
   message.success(`鍙戝竷[${row.name}]娴佺▼鎴愬姛`);
   // 鍒锋柊鍒楄〃
   emit('success');
+}
+
+/** 鍒囨崲鍏嶅鎵瑰紑鍏筹細寮�鍚悗璇ユ祦绋嬫彁浜ゆ椂涓嶅啀杩涘叆浜哄伐瀹℃壒锛岀敱绯荤粺鑷姩閫氳繃 */
+async function handleAutoApproveChange(checked: boolean, row: any) {
+  const oldValue = row.autoApprove;
+  // 涔愯鏇存柊锛岄伩鍏嶇瓑寰呮帴鍙f湡闂村紑鍏崇姸鎬佸洖璺�
+  row.autoApprove = checked;
+  gridApi.setGridOptions({ data: modelList.value });
+  autoApproveLoadingId.value = row.id;
+  try {
+    await updateAutoApproveByKey(row.key, checked);
+    message.success(
+      checked
+        ? `宸插紑鍚痆${row.name}]鍏嶅鎵癸細璇ユ祦绋嬫彁浜ゅ悗灏嗙敱绯荤粺鑷姩閫氳繃`
+        : `宸插叧闂璠${row.name}]鍏嶅鎵癸細鎭㈠涓烘甯镐汉宸ュ鎵筦,
+    );
+    // 閲嶆柊鎷夊彇鍒楄〃锛屼繚璇佸墠绔姸鎬佷笌鍚庣涓�鑷�
+    emit('success');
+  } catch (error) {
+    // 澶辫触鍥炴粴
+    row.autoApprove = oldValue;
+    gridApi.setGridOptions({ data: modelList.value });
+    console.error('璁剧疆鍏嶅鎵瑰紑鍏冲け璐�', error);
+  } finally {
+    autoApproveLoadingId.value = null;
+  }
 }
 
 /** '鏇村'鎿嶄綔鎸夐挳 */
@@ -661,6 +694,28 @@
                 </Tag>
               </div>
             </template>
+            <template #autoApprove="{ row }">
+              <div class="flex items-center gap-1">
+                <Switch
+                  :checked="row.autoApprove"
+                  :loading="autoApproveLoadingId === row.id"
+                  :disabled="!hasPermiAutoApprove"
+                  size="small"
+                  @change="
+                    (checked: any) => handleAutoApproveChange(checked, row)
+                  "
+                />
+                <Tooltip
+                  v-if="!row.processDefinition"
+                  title="娴佺▼灏氭湭鍙戝竷锛屽厤瀹℃壒寮�鍏充笉浼氱敓鏁堬紝璇峰厛鍙戝竷娴佺▼"
+                >
+                  <IconifyIcon
+                    icon="lucide:alert-circle"
+                    class="text-base text-orange-400"
+                  />
+                </Tooltip>
+              </div>
+            </template>
             <template #actions="{ row }">
               <div class="flex items-center space-x-0">
                 <Button

--
Gitblit v1.9.3