| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- <template>
- <div class="checker-select-container" :class="{ 'opacity-50': disabled }">
- <div class="checker-list" v-if="!disabled && hasData">
- <template v-if="processedDeptData.length">
- <div v-for="dept in processedDeptData" :key="dept.dept?.id || dept.deptGroupId" class="dept-section">
- <div v-for="team in dept.teamList" :key="team.id || team.deptGroupId" class="group-section">
- <div class="group-content">
- <template v-for="subTeam in team.memberList" :key="subTeam.id || subTeam.deptGroupId">
- <div class="group-members">
- <!-- 单选模式下隐藏小组全选复选框 -->
- <div class="label" v-if="props.multiple">
- <el-checkbox
- v-model="subTeam.checked"
- @change="(val: boolean) => handleSubTeamCheckChange(val, subTeam)"
- :disabled="disabled"
- >
- {{ subTeam.name }}
- </el-checkbox>
- </div>
- <div class="label" v-else>
- <span>{{ subTeam.name }}</span>
- </div>
- <div class="members-list">
- <!-- 多选模式 -->
- <el-checkbox-group
- v-if="props.multiple"
- v-model="selectedCheckerIds"
- @change="handleMemberChange"
- :disabled="disabled"
- >
- <el-checkbox
- v-for="member in subTeam.memberList"
- :key="member.memberId"
- :value="subTeam.id + ':' + member.memberId"
- >
- <span v-if="member.memberId === member.leaderId" class="leader-tag">组</span>
- {{ member.member?.nickname }}
- </el-checkbox>
- </el-checkbox-group>
- <!-- 单选模式:使用复选框但实现单选逻辑 -->
- <div v-else class="single-select-mode">
- <el-checkbox
- v-for="member in subTeam.memberList"
- :key="member.memberId"
- :model-value="isCheckerSelected(subTeam.id, member.memberId)"
- @change="(val: boolean) => handleSingleSelectChange(val, subTeam.id, member.memberId)"
- :disabled="disabled"
- >
- <span v-if="member.memberId === member.leaderId" class="leader-tag">组</span>
- {{ member.member?.nickname }}
- </el-checkbox>
- </div>
- </div>
- </div>
- </template>
- </div>
- </div>
- </div>
- </template>
- <el-empty v-else description="暂无检验员数据" />
- </div>
- <div v-if="disabled || !hasData" class="text-gray-400 py-4 text-center">
- {{ emptyText || '无需安排或无待检设备' }}
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, watch, computed } from 'vue'
- import type { ProcessedDeptData } from '@/views/pressure2/equipboilerscheduling/components/inspector'
- import { DeptGroupTeamApi } from '@/api/pressure2/deptGroupTeam'
- import { processInspectorGroups } from '@/views/pressure2/equipboilerscheduling/components/inspector'
- export interface CheckerItem {
- groupTeamId: string
- memberId: string
- leaderId: string
- member: any
- isLeader: boolean
- }
- const props = defineProps({
- // 部门ID
- deptId: {
- type: String,
- required: true
- },
- // 是否禁用
- disabled: {
- type: Boolean,
- default: false
- },
- // 是否有数据(控制显示)
- hasData: {
- type: Boolean,
- default: true
- },
- // 空状态文本
- emptyText: {
- type: String,
- default: ''
- },
- // 已选中的检验员列表(用于初始化)
- modelValue: {
- type: Array as PropType<CheckerItem[]>,
- default: () => []
- },
- // 是否多选,默认为true
- multiple: {
- type: Boolean,
- default: true
- }
- })
- const emit = defineEmits(['update:modelValue', 'change'])
- // 处理后的部门数据
- const processedDeptData = ref<ProcessedDeptData[]>([])
- // 选中的检验员对象列表
- const selectedCheckers = ref<CheckerItem[]>([...props.modelValue])
- // 选中的检验员ID列表(用于checkbox-group绑定)
- const selectedCheckerIds = ref<string[]>([])
- /** 获取检验员列表 */
- const getCheckerList = async (deptId?: string) => {
- try {
- const targetDeptId = deptId || props.deptId
- const originalData = await DeptGroupTeamApi.getDeptGroupTeamMembers({
- deptIds: targetDeptId
- })
-
- processedDeptData.value = processInspectorGroups(originalData)
-
- // 更新选中状态
- updateAllSubTeamCheckStatus()
- } catch (error) {
- console.error('获取检验员列表失败:', error)
- }
- }
- /** 更新所有小组的选中状态 */
- const updateAllSubTeamCheckStatus = () => {
- processedDeptData.value.forEach(dept => {
- dept.teamList.forEach(team => {
- team.memberList.forEach(subTeam => {
- updateSubTeamCheckStatus(subTeam)
- })
- })
- })
- }
- /** 更新单个小组的选中状态 */
- const updateSubTeamCheckStatus = (subTeam: any) => {
- const availableMembers = subTeam.memberList
- const allSubTeamMembers = availableMembers.map(m => m.id + ':' + m.memberId)
-
- if (allSubTeamMembers.length === 0) {
- subTeam.checked = false
- return
- }
-
- const selectedSubTeamMembers = selectedCheckers.value.filter(c =>
- allSubTeamMembers.includes(c.groupTeamId + ':' + c.memberId)
- )
-
- subTeam.checked = selectedSubTeamMembers.length === allSubTeamMembers.length
- }
- /** 处理小组全选 */
- const handleSubTeamCheckChange = (val: boolean, subTeam: any) => {
- const availableMembers = subTeam.memberList
- const memberIds = availableMembers.map(member => ({
- groupTeamId: subTeam.id,
- memberId: member.memberId,
- leaderId: subTeam.leaderId,
- member: member.member,
- isLeader: member.memberId === subTeam.leaderId
- }))
-
- if (val) {
- // 选中:添加所有可用成员
- const existingIds = new Set(selectedCheckers.value.map(c => c.groupTeamId + ':' + c.memberId))
- memberIds.forEach(member => {
- if (!existingIds.has(member.groupTeamId + ':' + member.memberId)) {
- selectedCheckers.value.push(member)
- }
- })
- } else {
- // 取消选中:移除当前小组的所有成员
- const memberIdsSet = new Set(memberIds.map(m => m.groupTeamId + ':' + m.memberId))
- selectedCheckers.value = selectedCheckers.value.filter(c =>
- !memberIdsSet.has(c.groupTeamId + ':' + c.memberId)
- )
- }
-
- // 更新小组选中状态
- updateSubTeamCheckStatus(subTeam)
-
- // 触发更新
- emitUpdate()
- }
- /** 判断检验员是否被选中 */
- const isCheckerSelected = (subTeamId: string, memberId: string): boolean => {
- return selectedCheckers.value.some(c =>
- c.groupTeamId === subTeamId && c.memberId.toString() === memberId.toString()
- )
- }
- /** 处理单选模式下的选择变化 */
- const handleSingleSelectChange = (val: boolean, subTeamId: string, memberId: string) => {
- if (!val) {
- // 取消选中:清空所有选中项
- selectedCheckers.value = []
- } else {
- // 选中:先清空其他选项,只保留当前选中的
- const allMembers = getAllMembersFromData()
- const selectedMember = allMembers.find(m =>
- m.groupTeamId === subTeamId && m.memberId.toString() === memberId.toString()
- )
-
- if (selectedMember) {
- selectedCheckers.value = [selectedMember]
- }
- }
-
- // 更新所有小组的选中状态
- updateAllSubTeamCheckStatus()
-
- // 触发更新
- emitUpdate()
- }
- /** 处理成员选择变化 */
- const handleMemberChange = (values: string[] | string) => {
- const allMembers = getAllMembersFromData()
-
- // 单选模式下,values 是单个字符串;多选模式下是数组
- const valueArray = Array.isArray(values) ? values : [values]
-
- selectedCheckers.value = valueArray.map(id => {
- const [groupTeamId, memberId] = id.split(':')
- return allMembers.find(m => m.groupTeamId === groupTeamId && m.memberId.toString() === memberId)
- }).filter(Boolean)
-
- // 更新所有小组的选中状态
- updateAllSubTeamCheckStatus()
-
- // 触发更新
- emitUpdate()
- }
- /** 从处理后的数据中获取所有成员 */
- const getAllMembersFromData = () => {
- const members: CheckerItem[] = []
- processedDeptData.value.forEach(dept => {
- dept.teamList.forEach(team => {
- team.memberList.forEach(subTeam => {
- subTeam.memberList.forEach(member => {
- members.push({
- groupTeamId: subTeam.id,
- memberId: member.memberId,
- leaderId: subTeam.leaderId,
- member: member.member,
- isLeader: member.memberId === subTeam.leaderId
- })
- })
- })
- })
- })
- return members
- }
- /** 触发更新事件 */
- const emitUpdate = () => {
- emit('update:modelValue', selectedCheckers.value)
- emit('change', selectedCheckers.value)
- }
- // 监听检验员对象变化,同步更新ID列表
- watch(() => selectedCheckers.value, (newVal) => {
- selectedCheckerIds.value = newVal.map(c => c.groupTeamId + ':' + c.memberId)
- }, { deep: true })
- // 监听外部传入的modelValue变化
- watch(() => props.modelValue, (newVal) => {
- selectedCheckers.value = [...newVal]
- }, { deep: true })
- // 监听多选模式变化,如果是单选且有多个选中项,只保留第一个
- watch(() => props.multiple, (isMultiple) => {
- if (!isMultiple && selectedCheckers.value.length > 1) {
- selectedCheckers.value = [selectedCheckers.value[0]]
- emitUpdate()
- }
- })
- // 暴露方法供父组件调用
- defineExpose({
- getCheckerList,
- processedDeptData,
- selectedCheckers,
- getAllMembersFromData
- })
- </script>
- <style lang="scss" scoped>
- .checker-select-container {
- width: 100%;
-
- .checker-list {
- border: 1px solid #EBEEF5;
- border-radius: 4px;
-
- .dept-section {
- .group-section {
- margin-bottom: 0;
- border-bottom: 1px solid #EBEEF5;
- &:last-child {
- border-bottom: none;
- }
- .group-content {
- padding: 6px 0 0 16px;
- .group-members {
- display: flex;
- margin-bottom: 6px;
- align-items: flex-start;
-
- &:last-child {
- margin-bottom: 0;
- }
-
- .label {
- flex: 0 0 100px;
- font-weight: bold;
-
- :deep(.el-checkbox) {
- margin-right: 0;
- display: inline-flex;
- align-items: center;
- font-weight: bold;
-
- .el-checkbox__label {
- padding-left: 6px;
- font-weight: bold !important;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- }
- }
-
- .members-list {
- flex: 1;
- display: flex;
- flex-wrap: wrap;
- gap: 0 12px;
-
- // 单选模式下的样式
- &.single-select-mode {
- display: flex;
- flex-wrap: wrap;
- gap: 0 12px;
- }
-
- :deep(.el-checkbox) {
- margin-right: 0;
- min-width: 90px;
- margin-bottom: 8px;
- display: inline-flex;
- align-items: center;
-
- .el-checkbox__label {
- padding-left: 6px;
- display: inline-block;
- width: 80px;
- text-align: left;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- .leader-tag {
- display: inline-block;
- width: 14px;
- height: 14px;
- line-height: 12px;
- text-align: center;
- border: 1px solid #4475d6;
- font-size: 10px;
- margin-right: 4px;
- color: #4475d6;
- }
- </style>
|