chat-app/src/pages/chooseByDeps/index.vue

1208 lines
36 KiB
Vue
Raw Normal View History

<template>
<div class="choose-deps-page">
<zPaging ref="zPaging" :show-scrollbar="false">
<template #top>
<customNavbar :title="pageTitle"></customNavbar>
</template>
<div class="choose-deps">
<div class="w-full pl-[32rpx] pr-[32rpx]">
<div class="pl-[16rpx] pr-[16rpx] pt-[22rpx] pb-[24rpx] bg-[#FFFFFF]">
<customInput
:searchText="searchVal"
@inputSearchText="inputSearchText"
></customInput>
<div
v-if="crumbs.length"
class="w-full overflow-x-auto mt-[22rpx] leading-[48rpx] text-[#2F2F2F] flex items-center no-scrollbar"
ref="crumbsContainer"
>
<div
v-for="(item, index) in crumbs"
class="flex items-center text-[28rpx] leading-[48rpx] whitespace-nowrap"
:class="[
index === crumbsIndex ? 'text-[#747474]' : 'text-[#46299D]',
index === 0 ? '' : 'ml-[12rpx]',
]"
@click="handleCrumbsClick(index)"
>
<div>{{ item.name }}</div>
<div
v-if="index !== crumbs.length - 1"
class="ml-[20rpx] flex items-center mb-[2rpx]"
>
<tm-icon
name="tmicon-angle-right"
:font-size="20"
:color="index !== crumbs.length - 1 ? '#7A58DE' : '#C1B4EA'"
></tm-icon>
</div>
</div>
</div>
</div>
<div
2025-02-08 09:09:57 +00:00
v-if="
(state.chooseMode === 1 || state.chooseMode === 2) &&
(currentCrumbs?.sons?.length > 0 || currentMembers?.length > 0)
"
class="pl-[32rpx] bg-[#FFFFFF] mt-[20rpx] h-[110rpx] flex items-center"
@click="() => allCheck(allCheckStatus)"
>
<div class="mr-[20rpx]">
<checkBox
:disabled="currentCrumbs?.sons?.length === 0"
:modelValue="allCheckStatus"
></checkBox>
</div>
<div class="font-medium text-[28rpx] leading-[40rpx]">
{{ $t('radio.btn.selectAll') }}
</div>
</div>
<div
v-if="
currentCrumbs?.sons?.length > 0 &&
((state.chooseMode === 2 && !searchVal) || state.chooseMode !== 2)
"
v-for="item in currentCrumbs?.sons"
@click="
state.chooseMode === 1 || state.chooseMode === 2
? checkItemChange(
item,
item.checkStatus
? item.checkStatus === 'checked'
? 'noChecked'
: 'checked'
: 'checked',
)
: ''
"
class="pl-[32rpx] bg-[#FFFFFF] mt-[20rpx] h-[110rpx] flex items-center"
>
<div class="w-full flex items-center justify-between">
<div class="flex items-center">
<div
class="mr-[20rpx]"
v-if="state.chooseMode === 1 || state.chooseMode === 2"
>
<checkBox
:disabled="!item?.sons?.length"
v-model="item.checkStatus"
@change="(val) => checkItemChange(item, val)"
></checkBox>
</div>
<div class="font-medium text-[28rpx] leading-[40rpx]">
<span>{{ item.name }}</span>
<span v-if="state.chooseMode === 2 || state.chooseMode === 3">
{{ getDepTotalMembers(item) }}
</span>
</div>
</div>
<div
v-if="
(item.sons?.length && state.chooseMode === 1) ||
((state.chooseMode === 2 || state.chooseMode === 3) &&
getDepTotalMembers(item) > 0)
"
class="flex items-center mr-[32rpx]"
>
2025-03-03 07:22:53 +00:00
<div
2025-03-03 09:05:22 +00:00
class="vDivider mr-[32rpx]"
v-if="state.chooseMode === 1"
></div>
<div
v-if="state.chooseMode === 1"
2025-03-03 07:22:53 +00:00
@click.stop="checkCurrentItem(item)"
class="flex items-center mr-[32rpx]"
:class="{
'opacity-50 cursor-not-allowed':
item.checkStatus === 'checked',
}"
>
<div class="mr-[20rpx]">
<checkBox
:modelValue="
item.currentChecked ? 'checked' : 'noChecked'
"
@change="() => checkCurrentItem(item)"
></checkBox>
</div>
<div
class="text-[28rpx] leading-[40rpx] font-medium text-[#46299D]"
>
{{ $t('choose.deps.current') }}
</div>
</div>
<div class="vDivider mr-[32rpx]"></div>
<div
@click.stop="() => toNextLevel(item)"
class="flex items-center"
>
<div class="mr-[12rpx]">
<tm-image
:width="26"
:height="26"
:src="
item.checkStatus !== 'checked' ? downDep : downDepDis
"
></tm-image>
</div>
<div
class="text-[28rpx] leading-[40rpx] font-medium"
:class="
item.checkStatus !== 'checked'
? 'text-[#46299D]'
: 'text-[#C1B4EA]'
"
>
{{ $t('choose.deps.nextLevel') }}
</div>
</div>
</div>
</div>
</div>
<div
v-if="
currentMembers.length &&
(state.chooseMode === 2 || state.chooseMode === 3)
"
v-for="item in currentMembers"
@click="
state.chooseMode === 1 || state.chooseMode === 2
? checkMember(
item,
membersCheckedKeys.filter((v) => v.ID === item.ID).length >
0
? 'noChecked'
: 'checked',
)
: toUserDetail(item)
"
class="pl-[32rpx] pr-[32rpx] bg-[#FFFFFF] mt-[20rpx] h-[110rpx] flex items-center"
>
<div class="w-full flex items-center justify-between">
<div class="w-full flex items-center">
<div
class="mr-[20rpx]"
v-if="state.chooseMode === 1 || state.chooseMode === 2"
>
<checkBox
:modelValue="
membersCheckedKeys.filter((v) => v.ID === item.ID)
.length > 0
? 'checked'
: 'noChecked'
"
@change="(val) => checkMember(item, val)"
></checkBox>
</div>
<div class="w-full flex items-center">
<div
class="userAvatar flex items-center justify-center"
style="flex-shrink: 0;"
>
{{ item.nickName.slice(-2) }}
</div>
<div
class="ml-[20rpx] flex flex-col justify-center w-[112rpx]"
style="word-break: break-all; flex-shrink: 0;"
>
<div class="text-[28rpx] font-medium leading-[40rpx]">
{{ item.nickName }}
</div>
<div
class="text-[20rpx] text-[#747474] leading-[28rpx] font-regular"
>
{{ item.jobNum }}
</div>
</div>
<tm-popover
position="tc"
style="max-width: calc(100% - 64rpx - 112rpx - 20rpx);"
>
<div
class="max-w-full ml-[6rpx] flex max-h-[68rpx] flex-wrap line-clamp-2"
>
<div
v-for="post in item.positions"
class="postTag truncate mb-[4rpx] ml-[14rpx] max-w-[164rpx]"
>
{{ post.name }}
</div>
</div>
<template v-slot:label>
<div
class="max-h-[250rpx] overflow-y-auto pt-[10rpx] pl-[18rpx] pr-[18rpx] pb-[12rpx]"
>
<div
v-for="post in item.positions"
class="postTag truncate mb-[10rpx]"
>
{{ post.name }}
</div>
</div>
</template>
</tm-popover>
</div>
</div>
</div>
</div>
</div>
</div>
<template #bottom v-if="state.chooseMode === 1 || state.chooseMode === 2">
<div
class="h-[162rpx] pl-[32rpx] pr-[32rpx] bg-[#FFFFFF] confirm-btn-area"
>
<div class="mt-[2rpx] flex justify-between">
<div class="flex flex-col mt-[32rpx]">
<div
@click="openDrawer"
class="flex items-center text-[28rpx] leading-[40rpx] text-[#000000] font-medium"
>
<div>
<span v-if="state.chooseMode === 1">
{{ $t('statistics.selected.deps') }}
</span>
<span v-if="state.chooseMode === 2">
{{ $t('select.member.num') }}
</span>
</div>
<div>
<span v-if="state.chooseMode === 1">
{{ allCheckedList.length }}
</span>
<span v-if="state.chooseMode === 2">
{{ totalMembers + $t('statistic.unit.person') }}
</span>
</div>
<div class="ml-[28rpx]">
<tm-icon
:fontSize="24"
color="#46299D"
name="tmicon-angle-up"
></tm-icon>
</div>
</div>
<div
class="text-[24rpx] leading-[34rpx] text-[#7A58DE] w-[280rpx] truncate"
>
<span v-if="state.chooseMode === 1">
{{ allCheckedList.map((v) => v.name).toString() }}
</span>
<span
v-if="state.chooseMode === 2"
v-for="item in allCheckedList"
>
<span v-if="item.showInPopup">
{{ item.name }}({{ getDepTotalMembers(item) }})
</span>
</span>
<span
v-if="state.chooseMode === 2"
v-for="(item, index) in membersCheckedKeys"
>
{{ item.nickName }}
{{ index !== membersCheckedKeys.length - 1 ? ',' : '' }}
</span>
</div>
</div>
<div class="btnBox">
<customBtn
:btnText="$t('ok')"
@clickBtn="handleConfirm"
:disabled="
state.chooseMode === 1
? !allCheckedList.length
: state.chooseMode === 2
? !allCheckedList.length && !membersCheckedKeys.length
: true
"
></customBtn>
</div>
</div>
</div>
<tm-drawer
placement="bottom"
v-model:show="showWin"
:height="800"
:hideHeader="true"
:round="5"
>
<div
class="flex flex-col w-full h-full pt-[36rpx] pl-[32rpx] pr-[32rpx] leading-[60rpx]"
>
<div
class="text-[32rpx] font-medium leading-[44rpx] flex items-center justify-between"
>
<div class="flex items-center ml-[10rpx]">
<div>
<span v-if="state.chooseMode === 1">
{{ $t('statistics.selected.deps') }}
</span>
<span v-if="state.chooseMode === 2">
{{ $t('select.member.num') }}
</span>
</div>
<div>
<span v-if="state.chooseMode === 1">
{{ allCheckedList.length }}
</span>
<span v-if="state.chooseMode === 2">
{{ totalMembers + $t('statistic.unit.person') }}
</span>
</div>
</div>
<div
class="text-[#7A58DE] mr-[10rpx]"
@click="() => (showWin = false)"
>
{{ $t('ok') }}
</div>
</div>
<scroll-view
scroll-y
class="h-[690rpx] mt-[30rpx] scroll-view-style"
>
<div class="flex-1 pb-[20rpx]">
<div
v-for="(item, index) in allCheckedList"
class="flex flex-col"
>
<div v-if="index === 0" class="divider"></div>
<div
v-if="
(item.showInPopup && state.chooseMode === 2) ||
state.chooseMode !== 2
"
class="flex items-center justify-between mt-[36rpx] font-medium text-[#000000] leading-[40rpx] mb-[34rpx]"
>
<div class="text-[28rpx] ml-[10rpx]">
<span>{{ item.name }}</span>
<span v-if="state.chooseMode === 2">
{{ getDepTotalMembers(item) }}
</span>
</div>
<div class="diyBtn">
<customBtn
:plain="true"
:btnText="$t('chatSettings.btn.removeAdmin')"
@clickBtn="deleteNode(item)"
></customBtn>
</div>
</div>
<div
class="divider"
v-if="
(item.showInPopup && state.chooseMode === 2) ||
state.chooseMode !== 2
"
></div>
</div>
<div
v-for="(item, index) in membersCheckedKeys"
class="flex flex-col"
>
<div
v-if="
index === 0 &&
allCheckedList.length == 0 &&
state.chooseMode === 2
"
class="divider"
></div>
<div
v-if="state.chooseMode === 2"
class="flex items-center justify-between mt-[36rpx] font-medium text-[#000000] leading-[40rpx] mb-[34rpx]"
>
<div class="text-[28rpx] ml-[10rpx] flex">
<div>{{ item.nickName }}({{ item.jobNum }})</div>
<div
class="ml-[20rpx] flex h-[68rpx] flex-wrap line-clamp-2 max-w-[342rpx]"
>
<div
v-for="post in item.positions"
class="postTag truncate mb-[4rpx] mr-[14rpx] max-w-[164rpx]"
>
{{ post.name }}
</div>
</div>
</div>
<div class="diyBtn">
<customBtn
:plain="true"
:btnText="$t('chatSettings.btn.removeAdmin')"
@clickBtn="deleteMember(item)"
></customBtn>
</div>
</div>
<div class="divider" v-if="state.chooseMode === 2"></div>
</div>
</div>
</scroll-view>
</div>
</tm-drawer>
</template>
</zPaging>
</div>
</template>
<script setup>
import customInput from '@/components/custom-input/custom-input.vue'
import customBtn from '@/components/custom-btn/custom-btn.vue'
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import { ref, watch, computed, onMounted, nextTick, reactive } from 'vue'
import { onShow, onLoad } from '@dcloudio/uni-app'
import { useChatList } from '@/store/chatList/index.js'
import { useAuth } from '@/store/auth'
import { useTalkStore, useUserStore } from '@/store'
import { useGroupTypeStore } from '@/store/groupType'
import downDep from '@/static/image/chatList/downDep.png'
import downDepDis from '@/static/image/chatList/downDepDis.png'
import checkBox from '@/components/checkBox/index.vue'
import lodash from 'lodash'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const {
groupActiveIndex,
getDepsTreeMy,
depTreeMyList,
crumbs,
crumbsIndex,
depCheckedKeys,
getDepMembers,
memberDepCheckedKeys,
membersCheckedKeys,
allChooseMembers,
} = useGroupTypeStore()
const userStore = useUserStore()
const searchVal = ref('')
const crumbsContainer = ref(null)
const showWin = ref(false)
const currentMembers = ref([])
const state = reactive({
chooseMode: 1, //1=选择部门2=选择人员; 3=通讯录
})
onLoad((options) => {
if (options.chooseMode) {
state.chooseMode = Number(options.chooseMode)
}
})
const pageTitle = computed(() => {
let page_title = ''
if (state.chooseMode === 1) {
page_title = t('pageTitle.select.department')
} else if (state.chooseMode === 2) {
page_title = t('chat.manage.addMembers')
} else if (state.chooseMode === 3) {
page_title = t('index.mine.addressBook')
}
return page_title
})
const getAllCheckedNodes = (node, checkedNodes = []) => {
2025-03-03 07:22:53 +00:00
// 如果节点被选中且是单独选中的情况
if (node.currentChecked) {
checkedNodes.push(node)
}
// 如果节点被完全选中(包括子节点)
else if (node.checkStatus === 'checked') {
checkedNodes.push(node)
}
if (node.sons && Array.isArray(node.sons)) {
node.sons.forEach((son) => getAllCheckedNodes(son, checkedNodes))
}
return checkedNodes
}
const userDepIds = computed(() => {
return userStore.deps.map((v) => v.dept_id)
})
const allCheckedList = computed(() => {
const checkedNodes = []
depTreeMyList.value.forEach((node) => getAllCheckedNodes(node, checkedNodes))
console.log('checkedNodes', checkedNodes)
return checkedNodes
})
const currentCrumbs = computed(() => {
if (crumbs.value[crumbsIndex.value]) {
if (searchVal.value && searchVal.value !== '') {
// let searchDepKeysArr = []
// let searchDepsArr = []
// searchDepKeysArr = Array.from(
// buildDepsMap(crumbs.value).keys(),
// ).filter((key) => key.includes(searchVal.value))
// console.log(searchDepKeysArr)
// if (searchDepKeysArr.length > 0) {
// searchDepKeysArr.forEach((key) => {
// searchDepsArr.push(buildDepsMap(crumbs.value).get(key))
// })
// }
// return {
// ...crumbs.value[crumbsIndex.value],
// sons: searchDepsArr,
// }
let filterSons = crumbs.value[crumbsIndex.value].sons?.filter((item) =>
item.name.includes(searchVal.value),
)
return {
...crumbs.value[crumbsIndex.value],
sons: filterSons,
}
}
console.log('allCheckedList', crumbs.value[crumbsIndex.value])
return crumbs.value[crumbsIndex.value]
}
return {}
})
const buildDepsMap = (departments) => {
const index = new Map()
function traverse(deps) {
for (let dep of deps) {
index.set(`${dep.name}`, dep)
if (dep?.sons?.length > 0) {
traverse(dep.sons)
}
}
}
traverse(departments)
return index
}
const allCheckStatus = computed(() => {
2025-03-04 03:43:08 +00:00
if (
currentCrumbs?.value?.sons?.length === 0 &&
currentMembers.value.length === 0
) {
return 'noChecked'
}
2025-03-03 09:05:22 +00:00
if (state.chooseMode === 2) {
// 检查部门选择状态
2025-03-04 03:43:08 +00:00
const allDepsChecked =
currentCrumbs?.value?.sons?.length > 0
? currentCrumbs?.value?.sons?.every(
(son) => son.checkStatus === 'checked',
)
: true // 如果没有子部门,则视为部门全选
2025-03-03 09:05:22 +00:00
// 检查人员选择状态
const allMembersChecked =
currentMembers.value.length > 0 &&
currentMembers.value.every((member) =>
membersCheckedKeys.value.some((checked) => checked.ID === member.ID),
)
// 综合判断全选状态
2025-03-04 03:43:08 +00:00
// 1. 如果有子部门和成员,则需要都选中才是全选
// 2. 如果只有成员没有子部门,则只需要成员全选即可
// 3. 如果只有子部门没有成员,则只需要子部门全选即可
if (
(currentCrumbs?.value?.sons?.length > 0 && allDepsChecked) ||
(currentMembers.value.length > 0 && allMembersChecked)
) {
2025-03-03 09:05:22 +00:00
return 'checked'
2025-03-04 03:43:08 +00:00
} else if (
currentCrumbs?.value?.sons?.some(
(son) =>
son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
) ||
currentMembers.value.some((member) =>
membersCheckedKeys.value.some((checked) => checked.ID === member.ID),
)
) {
2025-03-03 09:05:22 +00:00
return 'halfChecked'
}
return 'noChecked'
}
2025-03-04 03:43:08 +00:00
// 部门选择模式的原有逻辑保持不变
const allChecked = currentCrumbs?.value?.sons?.every(
(son) => son.checkStatus === 'checked',
)
const someChecked = currentCrumbs?.value?.sons?.some(
(son) => son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
)
2025-03-03 09:05:22 +00:00
if (allChecked) {
return 'checked'
} else if (someChecked) {
return 'halfChecked'
}
2025-03-03 09:05:22 +00:00
return 'noChecked'
})
const findNodeById = (node, targetId) => {
if (node.ID === targetId) {
return node
}
if (node.sons && Array.isArray(node.sons)) {
for (const son of node.sons) {
const found = findNodeById(son, targetId)
if (found) {
return found
}
}
}
return null
}
const findParentNode = (node, targetId) => {
if (!node.sons || !Array.isArray(node.sons)) return null
for (const son of node.sons) {
if (son.ID === targetId) {
return node
}
const parent = findParentNode(son, targetId)
if (parent) {
return parent
}
}
return null
}
// 检查并更新父节点的状态
const updateParentStatus = (node) => {
const parent = findParentNode(depTreeMyList.value[0], node.ID)
if (!parent) return
if (parent.checkStatus !== 'checked') {
const allChecked =
parent.sons &&
Array.isArray(parent.sons) &&
parent.sons.every((son) => son.checkStatus === 'checked')
const someChecked =
parent.sons &&
Array.isArray(parent.sons) &&
parent.sons.some(
(son) =>
son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
)
if (allChecked) {
parent.checkStatus = 'halfChecked'
} else if (someChecked) {
parent.checkStatus = 'halfChecked'
} else {
parent.checkStatus = 'noChecked'
}
}
updateParentStatus(parent)
}
//根据子节点状态更新当前节点状态
const updateNodeStatus = (node) => {
if (!node.sons || !Array.isArray(node.sons)) return
const allChecked = node.sons.every((son) => son.checkStatus === 'checked')
const someChecked = node.sons.some(
(son) => son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
)
if (allChecked) {
node.checkStatus = 'halfChecked'
} else if (someChecked) {
node.checkStatus = 'halfChecked'
} else {
node.checkStatus = 'noChecked'
}
}
// 更新当前节点及其所有子节点的状态
const updateCheckStatus = (node, status, doShow) => {
node.checkStatus = status
2025-03-04 03:43:08 +00:00
// 当状态为 noChecked 时,同时取消当前按钮的选中状态
if (status === 'noChecked') {
node.currentChecked = false
}
2025-03-03 07:22:53 +00:00
if (state.chooseMode === 2 && doShow) {
node.showInPopup = true
if (state.searchVal) {
return
}
}
if (node.sons && Array.isArray(node.sons) && node.sons.length > 0) {
node.sons.forEach((son) => updateCheckStatus(son, status))
}
}
const checkItemChange = (item, val) => {
// 更新当前节点及其子节点的状态
updateCheckStatus(item, val, true)
2025-03-03 07:22:53 +00:00
// 更新当前按钮状态
if (val === 'checked') {
item.currentChecked = true
} else if (val === 'noChecked') {
item.currentChecked = false
}
// 更新父节点的状态
updateParentStatus(item)
}
const deleteNode = (item) => {
const treeNode = findNodeById(depTreeMyList.value[0], item.ID)
if (!treeNode) return
2025-03-03 07:22:53 +00:00
treeNode.checkStatus = 'noChecked'
2025-03-03 07:22:53 +00:00
treeNode.currentChecked = false
if (
state.chooseMode === 2 &&
treeNode?.sons?.length > 0 &&
Array.isArray(treeNode?.sons)
) {
updateCheckStatus(item, false)
}
updateNodeStatus(treeNode)
updateParentStatus(treeNode)
}
const toNextLevel = async (item) => {
if (item.checkStatus !== 'checked') {
crumbs.value.push(item)
crumbsIndex.value++
await nextTick()
if (crumbsContainer.value) {
crumbsContainer.value.scrollLeft = crumbsContainer.value.scrollWidth
}
}
}
const handleCrumbsClick = (index) => {
crumbsIndex.value = index
crumbs.value = crumbs.value.slice(0, index + 1)
}
const allCheck = (status) => {
let statusT = 'noChecked'
if (status === 'checked') {
statusT = 'noChecked'
} else {
statusT = 'checked'
}
2025-03-03 09:05:22 +00:00
// 处理部门选择
currentCrumbs?.value?.sons?.forEach((item) => {
const itemT = findNodeById(depTreeMyList.value[0], item.ID)
if (!itemT) return
2025-03-03 07:22:53 +00:00
2025-03-03 09:05:22 +00:00
if (state.chooseMode === 1) {
// 部门选择模式的逻辑保持不变
checkItemChange(itemT, statusT)
if (statusT === 'checked') {
itemT.currentChecked = true
} else {
itemT.currentChecked = false
}
} else if (state.chooseMode === 2) {
// 人员选择模式下的部门处理
checkItemChange(itemT, statusT)
itemT.showInPopup = true
2025-03-03 07:22:53 +00:00
}
})
2025-03-03 07:22:53 +00:00
2025-03-03 09:05:22 +00:00
// 人员选择模式下处理成员
if (state.chooseMode === 2) {
2025-03-03 09:05:22 +00:00
if (statusT === 'checked') {
// 全选时,添加所有当前显示的成员
const newMembers = currentMembers.value.filter(
(member) =>
!membersCheckedKeys.value.some(
(existing) => existing.ID === member.ID,
),
)
membersCheckedKeys.value = [...membersCheckedKeys.value, ...newMembers]
} else {
// 取消全选时,移除当前显示的所有成员
const currentMemberIds = new Set(
currentMembers.value.map((member) => member.ID),
)
membersCheckedKeys.value = membersCheckedKeys.value.filter(
(member) => !currentMemberIds.has(member.ID),
)
}
}
}
const openDrawer = () => {
showWin.value = true
if (allCheckedList.length > 0) {
}
}
// watch(() => depTreeMyList.value, (newValue, oldValue) => {
// console.log("depTreeMyList", newValue);
// }, { deep: true });
// watch(() => searchVal.value, (newValue, oldValue) => {
// console.log("searchVal", newValue);
// });
const handleConfirm = async () => {
if (state.chooseMode === 2) {
const listT = membersCheckedKeys.value.map((v) => v)
if (allCheckedList?.value?.length > 0) {
const res = await getDepMembers({
departmentIds: allCheckedList.value.map((v) => v.ID),
status: 'notactive',
})
if (res.status == 0 && res.data?.data?.length) {
res.data?.data.forEach((v) => {
// 检查是否已存在相同 ID 的成员
if (!listT.some((item) => item.ID === v.ID)) {
listT.push(v)
}
})
}
}
allChooseMembers.value = listT
memberDepCheckedKeys.value = lodash.cloneDeep(allCheckedList.value) // 存储人员选择模式下的部门
} else {
depCheckedKeys.value = lodash.cloneDeep(allCheckedList.value) // 存储部门选择模式下的部门
}
uni.navigateBack()
}
const initCheckedKeys = () => {
depCheckedKeys.value.forEach((item) => {
const node = findNodeById(depTreeMyList.value[0], item.ID)
if (node) {
node.checkStatus = 'checked'
updateParentStatus(node)
}
})
}
const init = async () => {
crumbsIndex.value = 0
2025-03-03 09:05:22 +00:00
// 先清空之前的数据
depTreeMyList.value = []
crumbs.value = []
await getDepsTreeMy()
2025-03-03 07:22:53 +00:00
if (depTreeMyList.value.length) {
2025-03-03 09:05:22 +00:00
if (state.chooseMode === 1) {
// 部门选择模式才创建 all 节点
const allNode = {
ID: 'all',
name: t('choose.deps.all'),
sons: [...depTreeMyList.value], // 使用浅拷贝避免引用问题
staffNum: calculateTotalStaffNum({ sons: depTreeMyList.value }),
}
depTreeMyList.value = [allNode]
crumbs.value = [allNode]
} else {
// 其他模式直接使用原始数据
crumbs.value = [depTreeMyList.value[0]]
2025-03-03 07:22:53 +00:00
}
2025-03-03 09:05:22 +00:00
}
if (state.chooseMode === 2) {
// 恢复人员选择模式下的部门状态,但只显示父部门
memberDepCheckedKeys.value.forEach((item) => {
2025-03-03 09:05:22 +00:00
const node = findNodeById(depTreeMyList.value[0], item.ID)
if (node) {
node.checkStatus = 'checked'
// 只有当该节点没有父节点被选中时,才显示在列表中
const parentNode = findParentNode(depTreeMyList.value[0], item.ID)
if (!parentNode || parentNode.checkStatus !== 'checked') {
node.showInPopup = true
}
updateParentStatus(node)
}
})
2025-03-03 07:22:53 +00:00
} else {
2025-03-03 09:05:22 +00:00
initCheckedKeys()
2025-03-03 07:22:53 +00:00
}
}
2025-03-03 07:22:53 +00:00
const checkCurrentItem = (item) => {
// 如果节点已经被完全选中,则不允许点击当前按钮
if (item.checkStatus === 'checked') {
return
}
const treeNode = findNodeById(depTreeMyList.value[0], item.ID)
if (!treeNode) return
2025-03-04 03:43:08 +00:00
// 检查子节点状态
const allSonsChecked =
treeNode.sons &&
Array.isArray(treeNode.sons) &&
treeNode.sons.every((son) => son.checkStatus === 'checked')
2025-03-03 07:22:53 +00:00
// 切换当前节点的单独选中状态
treeNode.currentChecked = !treeNode.currentChecked
// 更新节点状态
if (treeNode.currentChecked) {
2025-03-04 03:43:08 +00:00
// 如果子节点全部选中,则当前节点也应该完全选中
if (allSonsChecked) {
treeNode.checkStatus = 'checked'
} else {
treeNode.checkStatus = 'halfChecked'
}
2025-03-03 07:22:53 +00:00
} else {
2025-03-04 03:43:08 +00:00
// 如果取消单独选中,但子节点全选,保持半选状态
if (allSonsChecked) {
treeNode.checkStatus = 'halfChecked'
} else {
treeNode.checkStatus = 'noChecked'
}
2025-03-03 07:22:53 +00:00
}
// 更新父节点状态
updateParentStatus(treeNode)
}
onMounted(() => {
init()
})
//输入搜索内容
const inputSearchText = (e) => {
searchVal.value = e
}
const calculateTotalStaffNum = (node) => {
let total = node.staffNum || 0
if (node.sons && Array.isArray(node.sons)) {
node.sons.forEach((son) => {
total += calculateTotalStaffNum(son)
})
}
return total
}
const getCurrentMembers = async (depItem) => {
2025-03-04 03:43:08 +00:00
if (state.chooseMode === 1 || !depItem?.ID) {
currentMembers.value = []
return
}
2025-03-04 03:43:08 +00:00
// 使用更明确的参数结构
let params = { status: 'notactive' }
if (searchVal.value) {
2025-03-04 03:43:08 +00:00
params = {
fatherDepartmentId: depItem.ID,
2025-03-04 03:43:08 +00:00
nickName: searchVal.value.trim(),
}
} else {
params.departmentId = depItem.ID
}
// 添加请求拦截
if (!params.departmentId && !params.fatherDepartmentId) {
console.error('无效的部门ID参数')
return
}
const res = await getDepMembers(params)
if (res.status === 0) {
currentMembers.value = res?.data?.data?.length
? res.data.data.map((v) => {
return {
...v,
isMember: true,
}
})
: []
} else {
currentMembers.value = []
}
}
const checkMember = (item, val) => {
if (val === 'checked') {
membersCheckedKeys.value.push(item)
} else {
membersCheckedKeys.value = membersCheckedKeys.value.filter(
(v) => v.ID !== item.ID,
)
}
}
watch(
() => currentCrumbs.value,
(newValue, oldValue) => {
2025-03-04 03:43:08 +00:00
if (newValue?.ID && (state.chooseMode === 2 || state.chooseMode === 3)) {
getCurrentMembers(newValue)
}
},
2025-03-04 03:43:08 +00:00
{ immediate: true },
)
const deleteMember = (item) => {
membersCheckedKeys.value = membersCheckedKeys.value.filter(
(v) => v.ID !== item.ID,
)
}
const getDepTotalMembers = (item) => {
const rootNode = depTreeMyList.value[0]
const targetNode = findNodeById(rootNode, item.ID)
if (targetNode) {
return calculateTotalStaffNum(targetNode)
}
return 0
}
const totalMembers = computed(() => {
const depMembers = allCheckedList.value.reduce(
(sum, item) => sum + item.staffNum,
0,
)
const memberCount = membersCheckedKeys.value.length
return depMembers + memberCount
})
//点击跳转到用户详情页面
const toUserDetail = (userItem) => {
console.log(userItem)
uni.navigateTo({
url:
'/pages/dialog/dialogDetail/userDetail??erpUserId=' +
(userItem.erp_user_id || userItem.ID),
})
}
</script>
<style scoped lang="scss">
::v-deep .zp-paging-container-content {
height: 100%;
display: flex;
}
.choose-deps-page {
.choose-deps {
flex: 1;
display: flex;
flex-direction: column;
background-image: url('@/static/image/clockIn/z3280@3x.png');
background-size: cover;
background-position: bottom center;
padding: 20rpx 0;
background-attachment: fixed;
2025-02-08 09:09:57 +00:00
width: 100%;
}
}
.divider {
height: 1rpx;
background-color: #707070;
opacity: 0.1;
}
.vDivider {
width: 1rpx;
height: 48rpx;
background-color: #b4b4b4;
}
.avatar-placeholder {
width: 192rpx;
height: 192rpx;
background-color: #e0e0e0;
border-radius: 50%;
margin-bottom: 40rpx;
}
.groupCard {
height: 272rpx;
width: 100%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border-radius: 12rpx;
&.firstPanel {
background-image: url('@/static/image/chatList/zu6033@2x.png');
}
&.secondPanel {
background-image: url('@/static/image/chatList/zu6031@2x.png');
margin-top: 28rpx;
margin-bottom: 28rpx;
}
&.thirdPanel {
background-image: url('@/static/image/chatList/zu6032@2x.png');
}
&.activePanel {
box-shadow: 0 0 0 3rpx #46299d;
}
}
.btnBox {
margin: 14rpx 0 0;
::v-deep .custom-btn-class {
padding: 18rpx 104rpx !important;
width: unset !important;
}
::v-deep .is-disabled {
background-color: #e6e6e6 !important;
color: #bebebe !important;
}
::v-deep .wd-button__text {
font-size: 28rpx !important;
font-weight: 500 !important;
line-height: 40rpx !important;
}
}
.no-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.no-scrollbar::-webkit-scrollbar {
display: none; /* Chrome, Safari, and Opera */
}
.diyBtn {
::v-deep .custom-btn-class {
padding: 8rpx 30rpx !important;
width: unset !important;
min-width: unset !important;
height: unset !important;
}
::v-deep .wd-button__text {
font-size: 24rpx !important;
font-weight: 400 !important;
line-height: 34rpx !important;
color: #191919;
}
::v-deep .custom-btn-class {
background-color: #fff !important;
border: 2rpx solid #d6d6d8 !important;
}
}
.confirm-btn-area {
box-shadow: 0 1px 0 2px rgba(231, 231, 231, 1);
}
.scroll-view-style {
-ms-overflow-style: none;
overflow: -moz-scrollbars-none;
::-webkit-scrollbar {
width: 0 !important;
}
}
.userAvatar {
background: linear-gradient(#674bbc, #46299d);
width: 72rpx;
height: 72rpx;
border-radius: 36rpx;
color: #fff;
font-size: 24rpx;
font-weight: bold;
}
.postTag {
background-color: #eee9f8;
height: 32rpx;
line-height: 32rpx;
font-size: 20rpx;
padding: 0 12rpx 0 12rpx;
color: #46299d;
}
</style>