Ver Fonte

feat(risk/inspectionTask): 巡检任务责任人/审核人改为系统用户选择器(本部门及下级部门,单选)

- 新增 API: listByCurrentDeptChainUsers (后端 /system/user/listByCurrentDeptChain)
- 责任人/审核人表单字段改为 el-select 单选,filterable
- 选项数据源 = 当前登录用户所在部门及其所有下级部门用户
- 选择后自动回填 inspectorName / auditorName 字段,便于提交后端
- 校验规则改为 inspectorId / auditorId 必填
- onMounted 加载选项;编辑时若选项未加载则补加载一次
klxj001 há 3 dias atrás
pai
commit
a1a9538a2a
2 ficheiros alterados com 86 adições e 7 exclusões
  1. 15 1
      src/api/system/user/index.ts
  2. 71 6
      src/views/risk/inspectionTask/index.vue

+ 15 - 1
src/api/system/user/index.ts

@@ -199,6 +199,19 @@ export const listUserByDeptId = (deptId: string | number): AxiosPromise<UserVO[]
   });
 };
 
+/**
+ * 查询当前登录用户所在部门及其所有下级部门的用户列表
+ * <p>
+ * 后端自动以 LoginHelper.getLoginUser().getDeptId() 为根节点递归查所有下级部门,
+ * 合并去重后返回(已过滤 delFlag)。典型场景:业务模块"责任人/审核人"选择器。
+ */
+export const listByCurrentDeptChainUsers = (): AxiosPromise<UserVO[]> => {
+  return request({
+    url: '/system/user/listByCurrentDeptChain',
+    method: 'get'
+  });
+};
+
 /**
  * 查询部门下拉树结构
  */
@@ -225,5 +238,6 @@ export default {
   getAuthRole,
   updateAuthRole,
   deptTreeSelect,
-  listUserByDeptId
+  listUserByDeptId,
+  listByCurrentDeptChainUsers
 };

+ 71 - 6
src/views/risk/inspectionTask/index.vue

@@ -214,13 +214,31 @@
 
         <el-row :gutter="20">
           <el-col :span="12">
-            <el-form-item label="责任人" prop="inspectorName">
-              <el-input v-model="form.inspectorName" placeholder="选择/输入责任人姓名" maxlength="50" />
+            <el-form-item label="责任人" prop="inspectorId">
+              <el-select
+                v-model="form.inspectorId"
+                placeholder="请选择责任人(本部门及下级部门)"
+                style="width: 100%"
+                filterable
+                clearable
+                @change="onInspectorChange"
+              >
+                <el-option v-for="u in deptChainUserOptions" :key="String(u.userId)" :label="u.nickName" :value="u.userId" />
+              </el-select>
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="审核人" prop="auditorName">
-              <el-input v-model="form.auditorName" placeholder="选择/输入审核人姓名" maxlength="50" />
+            <el-form-item label="审核人" prop="auditorId">
+              <el-select
+                v-model="form.auditorId"
+                placeholder="请选择审核人(本部门及下级部门)"
+                style="width: 100%"
+                filterable
+                clearable
+                @change="onAuditorChange"
+              >
+                <el-option v-for="u in deptChainUserOptions" :key="String(u.userId)" :label="u.nickName" :value="u.userId" />
+              </el-select>
             </el-form-item>
           </el-col>
         </el-row>
@@ -377,6 +395,8 @@ import type {
   RiskInspectionRecordVo,
   InspectionStatsVo
 } from '@/api/risk/types';
+import { listByCurrentDeptChainUsers } from '@/api/system/user';
+import type { UserVO } from '@/api/system/user/types';
 import { FormInstance } from 'element-plus';
 import type { ComponentInternalInstance } from 'vue';
 import { Calendar, Check, Warning, Clock } from '@element-plus/icons-vue';
@@ -399,6 +419,8 @@ const queryFormRef = ref<FormInstance>();
 const formRef = ref<FormInstance>();
 const recordList = ref<RiskInspectionRecordVo[]>([]);
 const recordLoading = ref(false);
+// 责任人/审核人选择器数据(当前登录用户所在部门及所有下级部门用户)
+const deptChainUserOptions = ref<UserVO[]>([]);
 // 字典
 const {
   risk_inspection_type: inspection_type_dict,
@@ -477,8 +499,8 @@ const data = reactive<any>({
     cycle: [{ required: true, message: '请选择巡检周期', trigger: 'change' }],
     storeName: [{ required: true, message: '请输入门店', trigger: 'blur' }],
     location: [{ required: true, message: '请输入位置区域', trigger: 'blur' }],
-    inspectorName: [{ required: true, message: '请输入责任人', trigger: 'blur' }],
-    auditorName: [{ required: true, message: '请输入审核人', trigger: 'blur' }]
+    inspectorId: [{ required: true, message: '请选择责任人', trigger: 'change' }],
+    auditorId: [{ required: true, message: '请选择审核人', trigger: 'change' }]
   }
 });
 
@@ -533,7 +555,9 @@ const resetForm = () => {
     checkMethod: '常规',
     lng: undefined,
     lat: undefined,
+    inspectorId: undefined,
     inspectorName: '',
+    auditorId: undefined,
     auditorName: '',
     dayOfWeek: '',
     dayOfMonth: '',
@@ -546,6 +570,42 @@ const resetForm = () => {
   formRef.value?.resetFields();
 };
 
+/**
+ * 拉取当前登录用户所在部门及所有下级部门的用户列表(责任人/审核人选择器数据源)
+ */
+const loadDeptChainUsers = async () => {
+  try {
+    const res = await listByCurrentDeptChainUsers();
+    deptChainUserOptions.value = res.data || [];
+  } catch (e) {
+    deptChainUserOptions.value = [];
+  }
+};
+
+/**
+ * 选择责任人后,同步回填 inspectorName 字段(便于提交到后端)
+ */
+const onInspectorChange = (val: string | number | undefined) => {
+  if (val === undefined || val === null || val === '') {
+    form.value.inspectorName = '';
+    return;
+  }
+  const user = deptChainUserOptions.value.find((u) => String(u.userId) === String(val));
+  form.value.inspectorName = user?.nickName || user?.userName || '';
+};
+
+/**
+ * 选择审核人后,同步回填 auditorName 字段
+ */
+const onAuditorChange = (val: string | number | undefined) => {
+  if (val === undefined || val === null || val === '') {
+    form.value.auditorName = '';
+    return;
+  }
+  const user = deptChainUserOptions.value.find((u) => String(u.userId) === String(val));
+  form.value.auditorName = user?.nickName || user?.userName || '';
+};
+
 const onCycleChange = (val: string) => {
   timeSlotList.value = [];
   weekDayList.value = [];
@@ -596,6 +656,10 @@ const handleAdd = () => {
 
 const handleUpdate = async (row: RiskInspectionTaskVo) => {
   resetForm();
+  // 编辑时,确保选择器数据源已加载(避免选择器中无对应选项)
+  if (deptChainUserOptions.value.length === 0) {
+    await loadDeptChainUsers();
+  }
   const res = await getInspectionTask(row.id);
   Object.assign(form.value, res.data);
   // 恢复时间配置
@@ -716,6 +780,7 @@ const getResultTag = (r: string): 'primary' | 'success' | 'warning' | 'info' | '
 
 onMounted(async () => {
   await loadAll();
+  await loadDeptChainUsers();
 });
 </script>