Bläddra i källkod

调整管道子报告修改检验员组件

yangguanjin 1 vecka sedan
förälder
incheckning
57e432ebd0

+ 325 - 0
yudao-ui-admin-vue3/src/views/pressure2/pipechecker/components/CheckerSelect.vue

@@ -0,0 +1,325 @@
+<template>
+  <div>
+    <div class="mb-4">
+      <el-input
+        v-model.trim="searchKeyword"
+        placeholder="请输入检验员姓名"
+        clearable
+        class="!w-240px"
+        @keyup.enter="handleSearch"
+      >
+        <template #prefix>
+          <Icon icon="ep:search" />
+        </template>
+      </el-input>
+      
+      <DeptSelect
+        v-model="selectedDept"
+        placeholder="请选择部门"
+        clearable
+        :multiple="true"
+        class="!w-240px ml-4"
+        @change="handleDeptChange"
+      />
+    </div>
+    <!-- 部门-分组-小组 -->
+    <div class="checker-list">
+      <template v-if="processedDeptData.length">
+        
+        <div v-for="dept in processedDeptData" :key="dept.dept.id" class="dept-section">
+          <div v-for="team in dept.teamList" :key="team.id || team.deptGroupId" class="group-section">
+            <div class="group-title">
+              {{ dept.dept.name }} - {{ team.deptGroupName }}
+            </div>
+            
+            <div class="group-content">
+              <template v-for="subTeam in team.memberList" :key="subTeam.id || subTeam.deptGroupId">
+                <div class="group-members">
+                  <div class="label">
+                    <el-checkbox
+                      v-model="subTeam.checked"
+                      @change="(val: boolean) => handleSubTeamCheckChange(val, team, subTeam)"
+                      :disabled="!max ? false : max === 1"
+                    >
+                      {{ subTeam.name }}
+                    </el-checkbox>
+                  </div>
+                  <div class="members-list">
+                    <el-checkbox-group 
+                      v-model="selectedMembers" 
+                      @change="(val: string[]) => handleMemberChange(val, team, subTeam)"
+                    >
+                      <el-checkbox 
+                        v-for="member in subTeam.memberList" 
+                        :key="member.memberId" 
+                        :value="member.id + ':' + member.memberId"
+                        :disabled="!max ? false : (max === 1 && selectedMembers?.length === max && selectedMembers[0] !== (member.id + ':' + member.memberId))"
+                      >
+                        <span v-if="member.memberId === subTeam.leaderId" class="leader-tag">组长</span>
+                        {{ member.member?.nickname }}
+                      </el-checkbox>
+                    </el-checkbox-group>
+                  </div>
+                </div>
+              </template>
+            </div>
+          </div>
+        </div>
+      </template>
+      <template v-else>
+        <el-empty description="未找到符合条件的数据" />
+      </template>
+    </div>
+  
+  </div>  
+     
+</template>
+
+<script setup lang="ts">
+import DeptSelect from './DeptSelect.vue'
+import { DeptGroupTeamApi } from '@/api/pressure2/deptGroupTeam'
+import { useUserStore, useUserStoreWithOut } from '@/store/modules/user'
+import { processInspectorGroups } from '@/views/pressure2/pipechecker/components/inspector'
+import type { DeptData, ProcessedDeptData, ProcessedTeam, ProcessedTeamMember } from '@/views/pressure2/pipechecker/components/inspector';
+const props = defineProps<{
+  modelValue: string[],
+  deptId?: string,
+  max: number
+}>()
+const emit = defineEmits(['update:modelValue', 'change'])
+const searchKeyword = ref('')
+const userStore = useUserStore()
+const {getUser} = useUserStoreWithOut()
+// const selectedDept = ref<string[]>([userStore.user.?])
+const selectedDept = ref<any[]>(props.deptId ? [props.deptId] : [getUser.deptId])
+const processedDeptData = ref<ProcessedDeptData[]>([])
+const selectedMembers = ref<string[]>(props.modelValue || [])
+const processedDeptDataCopy = ref<ProcessedDeptData[]>([])
+// 处理搜索
+const handleSearch = () => {
+  if (!searchKeyword.value) {
+    // 如果搜索关键字为空,恢复原始数据
+    // if (selectedDept.value.length > 0) {
+    //   getMemberList(selectedDept.value)
+    // }
+    getMemberList(selectedDept.value)
+    // return
+  }
+  
+  // 创建过滤后的数据副本
+  const filteredData = JSON.parse(JSON.stringify(processedDeptDataCopy.value))
+  
+  filteredData.forEach(dept => {
+    dept.teamList.forEach(team => {
+      team.memberList.forEach(subTeam => {
+        // 过滤成员列表
+        subTeam.memberList = subTeam.memberList.filter(member => 
+          member.member?.nickname?.includes(searchKeyword.value)
+        )
+        
+        // 更新小组选中状态
+        updateSubTeamCheckStatus(subTeam)
+      })
+      
+      // 过滤空的小组
+      team.memberList = team.memberList.filter(subTeam => subTeam.memberList.length > 0)
+    })
+    
+    // 过滤空的团队
+    dept.teamList = dept.teamList.filter(team => team.memberList.length > 0)
+  })
+  
+  // 更新显示的数据
+  processedDeptData.value = filteredData.filter(dept => dept.teamList.length > 0)
+  
+  // 触发change事件
+  handleChange(selectedMembers.value)
+}
+
+// 获取部门的分组-小组成员列表
+const getMemberList = async (deptIds: string[] | null) => {
+  const originalData: DeptData[] = await DeptGroupTeamApi.getDeptGroupTeamMembers({
+    deptIds: !deptIds?.length ? '' : deptIds?.join(',')
+  })
+
+  
+  // 前端处理部门-分组-小组
+  processedDeptData.value = processInspectorGroups(originalData)
+  processedDeptDataCopy.value = processInspectorGroups(originalData);
+
+  processedDeptData.value.forEach(dept => {
+    dept.teamList.forEach(team => {
+      team.memberList.forEach(subTeam => {
+        updateSubTeamCheckStatus(subTeam)
+      })
+    })
+  })
+}
+
+// 处理部门变化
+const handleDeptChange = (deptIds: string[]) => {
+  selectedDept.value = deptIds // 直接使用选中的数组
+  getMemberList(deptIds)
+}
+
+// 处理小组全选
+const handleSubTeamCheckChange = (val: boolean, team: ProcessedTeam, subTeam: ProcessedTeamMember) => {
+  // 获取当前小组所有可用成员的ID
+  const availableMembers = subTeam.memberList
+  const memberIds = availableMembers.map(member => member.id + ':' + member.memberId)
+  
+  if (val) {
+    // 选中:添加所有可用成员
+    selectedMembers.value = [...new Set([...selectedMembers.value, ...memberIds])]
+  } else {
+    // 取消选中:移除当前小组的所有成员
+    selectedMembers.value = selectedMembers.value.filter(id => !memberIds.includes(id))
+  }
+  
+  // 更新小组选中状态
+  updateSubTeamCheckStatus(subTeam)
+  
+  handleChange(selectedMembers.value)
+}
+
+// 处理成员选择变化
+const handleMemberChange = (val: string[], team: ProcessedTeam, subTeam: ProcessedTeamMember) => {
+  // 更新小组选中状态
+  updateSubTeamCheckStatus(subTeam)
+  handleChange(selectedMembers.value)
+}
+
+// 更新小组的选中状态
+const updateSubTeamCheckStatus = (subTeam: ProcessedTeamMember) => {
+  // 获取当前小组所有可用成员
+  const availableMembers = subTeam.memberList
+  const allSubTeamMembers = availableMembers.map(m => m.id + ':' + m.memberId)
+  
+  if (allSubTeamMembers.length === 0) {
+    subTeam.checked = false
+    return
+  }
+  
+  // 计算当前小组选中的成员
+  const selectedSubTeamMembers = selectedMembers.value.filter(id => allSubTeamMembers.includes(id))
+  
+  // 更新小组的选中状态
+  subTeam.checked = selectedSubTeamMembers.length === allSubTeamMembers.length
+}
+
+const handleChange = (value: string[]) => {
+  emit('update:modelValue', value)
+  // 获取选中成员的完整信息
+  const selectedCheckerInfo = processedDeptData.value.reduce((acc: any[], dept) => {
+    dept.teamList.forEach(team => {
+      team.memberList.forEach(subTeam => {
+        subTeam.memberList.forEach(member => {
+          if (value.includes(member.id + ':' + member.memberId)) {
+            acc.push({
+              memberId: member.memberId,
+              leaderId: subTeam.leaderId,
+              member: member.member,
+              isLeader: member.memberId === subTeam.leaderId,
+              groupTeamId: subTeam.id
+            })
+          }
+        })
+      })
+    })
+    return acc
+  }, [])
+  emit('change', selectedCheckerInfo)
+}
+
+// 监听 modelValue 变化
+watch(() => props.modelValue, (newVal) => {
+  selectedMembers.value = newVal || []
+  // 更新所有小组的选中状态
+  processedDeptData.value.forEach(dept => {
+    dept.teamList.forEach(team => {
+      team.memberList.forEach(subTeam => {
+        updateSubTeamCheckStatus(subTeam)
+      })
+    })
+  })
+}, { immediate: true })
+
+onMounted(() => {
+  if (selectedDept.value.length > 0) {
+    getMemberList(selectedDept.value)
+  }
+
+})
+</script>
+
+<style lang="scss" scoped>
+.checker-list {
+  max-height: 500px;
+  overflow-y: auto;
+  
+  .leader-tag {
+    display: inline-block;
+    // width: 16px;
+    height: 16px;
+    line-height: 14px;
+    text-align: center;
+    border: 1px solid #4475d6;
+    font-size: 12px;
+    // margin-left: 4px;
+    color: #4475d6;
+  }
+
+  .dept-section {
+    .group-section {
+      margin-bottom: 16px;
+      border: 1px solid #EBEEF5;
+      border-radius: 4px;
+      
+      .group-title {
+        padding: 8px 16px;
+        background-color: #F5F7FA;
+        border-bottom: 1px solid #EBEEF5;
+        font-weight: bold;
+      }
+      
+      .group-content {
+        padding: 16px;
+        
+        .group-members {
+          display: flex;
+          margin-bottom: 12px;
+          
+          &:last-child {
+            margin-bottom: 0;
+          }
+          
+          .label {
+            flex: 0 0 80px;
+            padding-top: 1px;
+            
+            :deep(.el-checkbox) {
+              margin-right: 0;
+              .el-checkbox__label {
+                padding-left: 6px;
+              }
+            }
+          }
+          
+          .members-list {
+            padding-left: 16px;
+            flex: 1;
+            
+            :deep(.el-checkbox) {
+              margin-right: 16px;
+              
+              &:last-child {
+                margin-right: 0;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style> 

+ 82 - 0
yudao-ui-admin-vue3/src/views/pressure2/pipechecker/components/DeptSelect.vue

@@ -0,0 +1,82 @@
+<template>
+    <el-select
+      :model-value="modelValue"
+      :placeholder="placeholder"
+      :clearable="clearable"
+      :multiple="multiple"
+      :collapse-tags="collapseTags"
+      :collapse-tags-tooltip="collapseTagsTooltip"
+      class="!w-240px"
+      @update:model-value="handleChange"
+      @clear="handleClear"
+    >
+      <el-option
+        v-for="dept in deptList"
+        :key="dept.id"
+        :label="dept.name"
+        :value="dept.id"
+      />
+    </el-select>
+  </template>
+  
+  <script setup lang="ts">
+  import { getSimpleDeptList } from '@/api/system/dept'
+  import type { DeptVO } from '@/api/system/dept'
+  import { PropType } from 'vue'
+  
+  const props = defineProps({
+    modelValue: {
+      type: [String, Array] as PropType<string | string[] | undefined>,
+      default: undefined
+    },
+    placeholder: {
+      type: String,
+      default: '请选择部门'
+    },
+    clearable: {
+      type: Boolean,
+      default: true
+    },
+    multiple: {
+      type: Boolean,
+      default: false
+    },
+    collapseTags: {
+      type: Boolean,
+      default: true
+    },
+    collapseTagsTooltip: {
+      type: Boolean,
+      default: true
+    }
+  })
+  
+  const emit = defineEmits(['update:modelValue', 'change', 'clear'])
+  
+  const deptList = ref<DeptVO[]>([])
+  
+  // 获取部门列表
+  const getDeptOptions = async () => {
+    const res = await getSimpleDeptList()
+    deptList.value = res
+  }
+  
+  // 处理选择变化
+  const handleChange = (value: string | string[]) => {
+    console.log(value)
+    emit('update:modelValue', value)
+    emit('change', value)
+  }
+  
+  // 处理清除
+  const handleClear = () => {
+    emit('update:modelValue', undefined)
+    emit('change', undefined)
+    emit('clear')
+  }
+  
+  // 组件挂载时获取部门列表
+  onMounted(() => {
+    getDeptOptions()
+  })
+  </script> 

+ 117 - 0
yudao-ui-admin-vue3/src/views/pressure2/pipechecker/components/inspector.ts

@@ -0,0 +1,117 @@
+import { DeptVO } from '@/api/system/dept';
+
+export interface Member {
+  id: string;
+  nickname: string;
+  status: number;
+  deptId: string;
+  postIds: string[];
+  mobile: string;
+  avatar: string;
+  transMap: Record<string, any>;
+}
+
+export interface TeamMember {
+  id: string;
+  name: string;
+  deptGroupId: string;
+  deptGroupName: string;
+  leaderId: string;
+  leader: any;
+  planSetterId: string;
+  planSetter: any;
+  createTime: number;
+  memberCount: number | null;
+  memberId: string;
+  member: Member;
+  deptId: string;
+  members: any;
+  memberList: TeamMember[] | null;
+  checked?: boolean;
+}
+
+export interface ProcessedTeamMember extends Omit<TeamMember, 'memberList'> {
+  memberList: TeamMember[];
+}
+
+export interface Team {
+  id: string | null;
+  deptGroupId: string;
+  deptGroupName: string | null;
+  leaderId: string | null;
+  leader: any;
+  planSetterId: string | null;
+  planSetter: any;
+  createTime: number | null;
+  memberCount: number | null;
+  memberId: string | null;
+  member: any;
+  deptId: string | null;
+  members: any;
+  memberList: TeamMember[];
+}
+
+export interface ProcessedTeam extends Omit<Team, 'memberList'> {
+  memberList: ProcessedTeamMember[];
+}
+
+export interface DeptData {
+  dept: DeptVO;
+  teamList: Team[];
+}
+
+export interface ProcessedDeptData extends Omit<DeptData, 'teamList'> {
+  teamList: ProcessedTeam[];
+}
+
+/**
+ * 处理检验员分组数据,将memberList按小组名称分组
+ * @param deptDataList 部门数据
+ * @returns 处理后的部门数据
+ */
+export function processInspectorGroups(deptDataList: DeptData[]): ProcessedDeptData[] {
+  return deptDataList.map(deptData => {
+    const processedTeamList = deptData.teamList.map(team => {
+      if (!team.memberList) return team as ProcessedTeam;
+
+      // 按小组名称重新分组
+      const groupedByTeam = team.memberList.reduce((acc: Record<string, TeamMember[]>, member: TeamMember) => {
+        const teamName = member.name || '未分组';
+        if (!acc[teamName]) {
+          acc[teamName] = [];
+        }
+        acc[teamName].push(member);
+        return acc;
+      }, {});
+
+      // 转换为数组格式
+      const processedMemberList = Object.entries(groupedByTeam).map(([_, members]) => {
+        // 按组长排序
+        const sortedMembers = [...members].sort((a, b) => {
+          if (a.memberId === a.leaderId) return -1;
+          if (b.memberId === b.leaderId) return 1;
+          return 0;
+        });
+        
+        // 使用第一个成员的信息作为小组信息
+        const firstMember = sortedMembers[0];
+        return {
+          ...firstMember,
+          memberCount: members.length,
+          memberList: sortedMembers,
+          checked: false,
+        };
+      });
+
+      return {
+        ...team,
+        memberList: processedMemberList
+      } as ProcessedTeam;
+    });
+
+    return {
+      ...deptData,
+      teamList: processedTeamList
+    } as ProcessedDeptData;
+  });
+} 

+ 15 - 25
yudao-ui-admin-vue3/src/views/pressure2/pipechecker/taskDetail.vue

@@ -102,12 +102,12 @@
       </div>
       <div class="text-sm text-gray-600">
         当前检验员:{{
-          selectedCheckersInfo.map((item) => item?.nickname).join('、') || '暂无'
+          selectedCheckersInfo.map((item) => item.member?.nickname).join('、') || '暂无'
         }}
       </div>
     </div>
 
-    <CheckerSelectBox :checkUsers="checkUsers" v-model="selectedCheckers" :max="1" @change="handleCheckerChange" />
+    <CheckerSelect v-model="selectedCheckers" :max="1" @change="handleCheckerChange" />
 
     <template #footer>
       <div class="dialog-footer">
@@ -191,7 +191,7 @@ import {
 } from '@/api/pressure2/pipetaskorder'
 import { UserVO } from '@/api/system/user'
 import { PressureCheckerMyTaskStatus, PressureReportType } from '@/utils/constants'
-import CheckerSelectBox from '@/views/pressure2/equipboilerscheduling/components/CheckerSelectBox.vue'
+import CheckerSelect from './components/CheckerSelect.vue'
 import InspectionItemList from './components/InspectionItemList.vue'
 import StatusOperationPanel from './components/StatusOperationPanel.vue'
 import { useUserStore } from '@/store/modules/user'
@@ -415,6 +415,7 @@ const handleRefresh = async () => {
 
 // 修改检验员
 const handleModifyChecker = (item?: ReportItemVO) => {
+  console.log("@@@@@@@@@@modifychecker......", item)
   const targetItem = item || selectedItem.value
   if (!targetItem) {
     ElMessage.warning('请先选择要修改检验员的项目')
@@ -426,19 +427,15 @@ const handleModifyChecker = (item?: ReportItemVO) => {
     selectedItem.value = item
   }
 
-  const currentCheckers: string[] = []
-  if (targetItem.checkUsers && targetItem.checkUsers.length > 0) {
-    const checkerIds = targetItem.checkUsers.map((user) => String(user.id))
-    currentCheckers.push(...checkerIds)
-  }
-
-  selectedCheckers.value = [...new Set(currentCheckers)]
-  // selectedCheckers.value = []
+  // 初始化当前检验员信息(用于显示)
+  selectedCheckersInfo.value = targetItem.checkUsers?.map(u => ({ memberId: u.id, member: u })) || []
+  // CheckerSelect 的 v-model 格式为 groupTeamId:memberId,当前仅有用户ID,无法预选
+  selectedCheckers.value = []
   modifyCheckerDialogVisible.value = true
 }
 
 const handleCheckerChange = (checkerInfo: any[]) => {
-  selectedCheckers.value = checkerInfo
+  selectedCheckersInfo.value = checkerInfo
 }
 
 const handleConfirmModifyChecker = async () => {
@@ -449,14 +446,14 @@ const handleConfirmModifyChecker = async () => {
 
   try {
 
-    if (selectedCheckers.value.length < 1){
+    if (selectedCheckersInfo.value.length < 1){
       ElMessage.error('请选择检验员!');
       return;
     }
 
     const updateData = {
       id: selectedItem.value.id,
-      userList: selectedCheckers.value.map(item => item.id)
+      userList: selectedCheckersInfo.value.map(item => item.memberId)
     }
 
     console.log('updateData', updateData)
@@ -549,17 +546,10 @@ const handleBack = () => {
 // 修改检验员(单个项目)
 const handleModifyCheckerForItem = (item: ReportItemVO) => {
   selectedItem.value = item
-
-  const currentCheckers: string[] = []
-  if (item.checkUsers && item.checkUsers.length > 0) {
-    const checkerIds = item.checkUsers.map((user) => String(user.id))
-    currentCheckers.push(...checkerIds)
-  }
-  if (selectedCheckersInfo.value.length == 0){
-    selectedCheckersInfo.value = item.checkUsers
-  }
-  selectedCheckers.value = [...new Set(currentCheckers)]
-  // selectedCheckers.value = []
+  // 初始化当前检验员信息(用于显示)
+  selectedCheckersInfo.value = item.checkUsers?.map(u => ({ memberId: u.id, member: u })) || []
+  // CheckerSelect 的 v-model 格式为 groupTeamId:memberId,当前仅有用户ID,无法预选
+  selectedCheckers.value = []
   modifyCheckerDialogVisible.value = true
 }