tuqin
2 天以前 5837ef4a400fe7852ce44a0cef51da797a1d6cad
fix(bpm): 流程管理员补写当前操作人,避免换账号后无法修改模型

问题:之前把 managerUserIds 硬编码为 [1](超级管理员),
后端 updateModel 是「先校验管理员、后写入」,
换另一个账号再修改该模型会报「操作流程失败:你不是该流程的管理员」且无法自愈。

改动(与中亿装配保持一致):
- index.vue:新建/修改时写入 [超级管理员, 当前操作人],不再硬编码 [1]
- basic-info.vue:watch 进入页面即强制回写同样两个 id,界面仍只读显示「超级管理员」

效果:任何管理员账号都能正常新建和反复修改流程模型。
已修改2个文件
110 ■■■■■ 文件已修改
src/views/bpm/model/form/index.vue 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bpm/model/form/modules/basic-info.vue 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/bpm/model/form/index.vue
@@ -16,6 +16,7 @@
} from '@vben/constants';
import { useTabs } from '@vben/hooks';
import { IconifyIcon } from '@vben/icons';
import { useUserStore } from '@vben/stores';
import { Button, Card, message } from 'ant-design-vue';
@@ -48,6 +49,7 @@
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
const tabs = useTabs();
const basicInfoRef = ref<InstanceType<typeof BasicInfo>>(); // 基础信息组件引用
@@ -186,10 +188,8 @@
  } else {
    // 情况三:新增场景
    formData.value.startUserType = 0; // 全体
    // 流程管理员默认值稍后设置(需要先加载用户列表才能定位超级管理员)
  }
  // 流程管理员固定为超级管理员(admin,系统账号 id=1),不允许选择/修改
  formData.value.managerUserIds = [1];
  // 获取表单列表
  formList.value = await getFormSimpleList();
@@ -198,6 +198,24 @@
  userList.value = await getSimpleUserList();
  // 获取部门列表
  deptList.value = await getSimpleDeptList();
  // 流程管理员固定为超级管理员(客户要求,界面上不给选择)。
  // 同时把「当前操作人」一并写入:后端 updateModel 是「先校验管理员、后写入」,
  // 只写超管的话,换一个账号再修改就会报「你不是该流程的管理员」,且无法自愈。
  const adminUser = userList.value.find(
    (user: SystemUserApi.User) => user.username === 'admin',
  );
  const currentUserId = userStore.userInfo?.id;
  const managerIds: number[] = [];
  if (adminUser?.id) {
    managerIds.push(adminUser.id);
  }
  if (currentUserId && !managerIds.includes(currentUserId)) {
    managerIds.push(currentUserId);
  }
  if (managerIds.length > 0) {
    formData.value.managerUserIds = managerIds;
  }
  // 最终,设置 currentStep 切换到第一步
  currentStep.value = 0;
@@ -353,8 +371,10 @@
  }
}
/** 步骤切换处理:允许自由前进/返回,不做步骤校验(保存/发布时才统一校验) */
function handleStepClick(index: number) {
/** 步骤切换处理 */
async function handleStepClick(index: number) {
  // 步骤之间允许自由切换(含前进和后退),切换时不做校验;
  // 必填信息的校验统一在「保存」「发布」时执行(validateAllSteps),避免卡在中间步骤出不去
  currentStep.value = index;
}
src/views/bpm/model/form/modules/basic-info.vue
@@ -14,6 +14,7 @@
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { IconifyIcon } from '@vben/icons';
import { useUserStore } from '@vben/stores';
import {
  Avatar,
@@ -56,11 +57,13 @@
const formRef = ref(); // 表单引用
const modelData = defineModel<any>(); // 创建本地数据副本
const userStore = useUserStore(); // 当前登录用户(写入流程管理员,避免换账号后无法再修改模型)
const selectedStartUsers = ref<SystemUserApi.User[]>([]); // 选中的发起人
const selectedStartDepts = ref<SystemDeptApi.Dept[]>([]); // 选中的发起部门
const selectedManagerUsers = ref<SystemUserApi.User[]>([]); // 选中的流程管理员
const currentSelectType = ref<'manager' | 'start'>('start');
const selectedUsers = ref<number[]>(); // 选中的用户
const rules: Record<string, Rule[]> = {
  name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }],
@@ -112,6 +115,28 @@
          newVal.managerUserIds.includes(user.id),
        ) as SystemUserApi.User[])
      : [];
    // 流程管理员固定为超级管理员:进入页面即强制回写,修改场景同样生效。
    // 注意:同时把「当前操作人」一并写入。因为后端 updateModel 是「先校验管理员、后写入」,
    // 一旦模型被保存成只包含超管,用其他账号再次修改就会报
    // 「操作流程失败,原因:你不是该流程(xxx)的管理员」,且无法自愈。
    const admin = props.userList.find(
      (user: SystemUserApi.User) => user.username === 'admin',
    );
    const currentUserId = userStore.userInfo?.id;
    const managerIds: number[] = [];
    if (admin) {
      managerIds.push(admin.id);
    }
    if (currentUserId && !managerIds.includes(currentUserId)) {
      managerIds.push(currentUserId);
    }
    if (managerIds.length > 0) {
      newVal.managerUserIds = managerIds;
      // 界面上仍然只展示「超级管理员」
      const displayUser =
        admin ?? props.userList.find((user) => user.id === managerIds[0]);
      selectedManagerUsers.value = displayUser ? [displayUser] : [];
    }
  },
  {
    immediate: true,
@@ -120,6 +145,7 @@
/** 打开发起人选择 */
function openStartUserSelect() {
  currentSelectType.value = 'start';
  selectedUsers.value = selectedStartUsers.value.map(
    (user) => user.id,
  ) as number[];
@@ -139,12 +165,27 @@
  };
}
/** 处理用户选择确认(发起人) */
/** 打开管理员选择 */
function openManagerUserSelect() {
  currentSelectType.value = 'manager';
  selectedUsers.value = selectedManagerUsers.value.map(
    (user) => user.id,
  ) as number[];
  userSelectModalApi.setData({ userIds: selectedUsers.value }).open();
}
/** 处理用户选择确认 */
function handleUserSelectConfirm(userList: SystemUserApi.User[]) {
  modelData.value = {
    ...modelData.value,
    startUserIds: userList.map((u) => u.id),
  };
  modelData.value =
    currentSelectType.value === 'start'
      ? {
          ...modelData.value,
          startUserIds: userList.map((u) => u.id),
        }
      : {
          ...modelData.value,
          managerUserIds: userList.map((u) => u.id),
        };
}
/** 用户选择弹窗关闭 */
@@ -202,6 +243,16 @@
    ...modelData.value,
    startDeptIds: modelData.value.startDeptIds.filter(
      (id: number) => id !== dept.id,
    ),
  };
}
/** 移除管理员 */
function handleRemoveManagerUser(user: SystemUserApi.User) {
  modelData.value = {
    ...modelData.value,
    managerUserIds: modelData.value.managerUserIds.filter(
      (id: number) => id !== user.id,
    ),
  };
}
@@ -371,21 +422,10 @@
        </div>
      </Form.Item>
      <Form.Item label="流程管理员" name="managerUserIds">
        <div class="flex flex-wrap gap-1">
          <div
            v-for="user in selectedManagerUsers"
            :key="user.id"
            class="relative flex h-9 items-center rounded-full bg-gray-100 pr-2 hover:bg-gray-200 dark:border dark:border-gray-500 dark:bg-gray-700 dark:hover:bg-gray-600"
          >
            <Avatar class="m-1 size-7" v-if="user.avatar" :src="user.avatar" />
            <Avatar class="m-1 size-7" v-else>
              {{ user.nickname?.substring(0, 1) }}
            </Avatar>
            <span class="text-gray-700 dark:text-gray-200">
              {{ user.nickname }}
            </span>
          </div>
        </div>
        <!-- 流程管理员固定为超级管理员,不允许选择 -->
        <span class="text-gray-700 dark:text-gray-200">
          {{ selectedManagerUsers[0]?.nickname || '超级管理员' }}
        </span>
      </Form.Item>
    </Form>