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

1257 lines
38 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="choose-deps-page">
<zPaging ref="zPaging" :show-scrollbar="false">
<template #top>
<div :class="state.chooseMode === 3 ? 'top_bg' : ''">
<customNavbar
:class="state.chooseMode === 3 ? 'index_top_navbar' : ''"
:title="pageTitle"
></customNavbar>
<div class="pl-[32rpx] pr-[32rpx] pt-[32rpx] pb-[32rpx]">
<customInput
:searchText="searchVal"
@inputSearchText="inputSearchText"
></customInput>
</div>
</div>
</template>
<div class="choose-deps">
<div class="w-full pl-[32rpx] pr-[32rpx]">
<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
v-if="
(state.chooseMode === 1 || state.chooseMode === 2) &&
((currentCrumbs?.sons?.length > 0 &&
((state.chooseMode === 2 && !searchVal) ||
state.chooseMode !== 2)) ||
currentMembers?.length > 0)
"
class="pl-[32rpx] bg-[#FFFFFF] mt-[20rpx] h-[110rpx] flex items-center"
@click="() => allCheck(allCheckStatus)"
>
<div class="mr-[20rpx]">
<checkBox @change="(val) => allCheck(allCheckStatus)" :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
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]"
>
<div
class="vDivider mr-[32rpx]"
v-if="state.chooseMode === 1"
></div>
<div
v-if="state.chooseMode === 1"
@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
:disabled="isPreSelectedMember(item)"
: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 { handleSetWebviewStyle } from '@/utils/common'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const {
groupActiveIndex,
getDepsTreeMy2,
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=通讯录
preSelectedMembers: [], // 已在群里的成员数据
})
onLoad((options) => {
if (options.chooseMode) {
state.chooseMode = Number(options.chooseMode)
}
// 如果有预选成员数据,初始化选中状态
if (options.preSelectedMembers) {
try {
const preSelectedMembers = JSON.parse(
decodeURIComponent(options.preSelectedMembers),
)
state.preSelectedMembers = preSelectedMembers
} catch (e) {
console.error('解析预选成员数据失败:', e)
}
}
})
// 添加判断是否为预选成员的方法
const isPreSelectedMember = (member) => {
return state.preSelectedMembers.some(
(preMember) => preMember.erp_user_id === member.ID,
)
}
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 = []) => {
// 如果节点被选中且是单独选中的情况
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(() => {
if (
currentCrumbs?.value?.sons?.length === 0 &&
currentMembers.value.length === 0
) {
return 'noChecked'
}
if (state.chooseMode === 2) {
// 检查部门选择状态
const allDepsChecked =
currentCrumbs?.value?.sons?.length > 0
? currentCrumbs?.value?.sons?.every(
(son) => son.checkStatus === 'checked',
)
: true
// 检查人员选择状态,需要排除已在群里的成员
const selectableMembers = currentMembers.value.filter(
(member) => !isPreSelectedMember(member),
)
const allMembersChecked =
selectableMembers.length > 0 &&
selectableMembers.every((member) =>
membersCheckedKeys.value.some((checked) => checked.ID === member.ID),
)
// 综合判断全选状态
if (
// 如果有可选部门,必须全部选中
(currentCrumbs?.value?.sons?.length === 0 || allDepsChecked) &&
// 如果有可选成员,必须全部选中
(selectableMembers.length === 0 || allMembersChecked)
) {
return 'checked'
} else if (
// 只要有部门或成员被选中就是半选状态
currentCrumbs?.value?.sons?.some(
(son) =>
son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
) ||
selectableMembers.some((member) =>
membersCheckedKeys.value.some((checked) => checked.ID === member.ID),
)
) {
return 'halfChecked'
}
return 'noChecked'
}
// 部门选择模式的原有逻辑保持不变
const allChecked = currentCrumbs?.value?.sons?.every(
(son) => son.checkStatus === 'checked',
)
const someChecked = currentCrumbs?.value?.sons?.some(
(son) => son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
)
if (allChecked) {
return 'checked'
} else if (someChecked) {
return 'halfChecked'
}
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
// 当状态为 noChecked 时,同时取消当前按钮的选中状态
if (status === 'noChecked') {
node.currentChecked = false
}
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)
// 更新当前按钮状态
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
treeNode.checkStatus = 'noChecked'
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'
}
// 处理部门选择
currentCrumbs?.value?.sons?.forEach((item) => {
const itemT = findNodeById(depTreeMyList.value[0], item.ID)
if (!itemT) return
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
}
})
// 人员选择模式下处理成员
if (state.chooseMode === 2) {
if (statusT === 'checked') {
// 全选时,添加所有当前显示的成员(排除已在群里的成员)
const newMembers = currentMembers.value.filter(
(member) =>
!membersCheckedKeys.value.some(
(existing) => existing.ID === member.ID,
) && !isPreSelectedMember(member),
)
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.code == 200 && 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
// 先清空之前的数据
depTreeMyList.value = []
crumbs.value = []
await getDepsTreeMy2(state.chooseMode)
if (depTreeMyList.value.length) {
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]]
}
}
if (state.chooseMode === 2) {
// 恢复人员选择模式下的部门状态,但只显示父部门
memberDepCheckedKeys.value.forEach((item) => {
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)
}
})
} else {
initCheckedKeys()
}
}
const checkCurrentItem = (item) => {
// 如果节点已经被完全选中,则不允许点击当前按钮
if (item.checkStatus === 'checked') {
return
}
const treeNode = findNodeById(depTreeMyList.value[0], item.ID)
if (!treeNode) return
// 检查子节点状态
const allSonsChecked =
treeNode.sons &&
Array.isArray(treeNode.sons) &&
treeNode.sons.every((son) => son.checkStatus === 'checked')
// 切换当前节点的单独选中状态
treeNode.currentChecked = !treeNode.currentChecked
// 更新节点状态
if (treeNode.currentChecked) {
// 如果子节点全部选中,则当前节点也应该完全选中
if (allSonsChecked) {
treeNode.checkStatus = 'checked'
} else {
treeNode.checkStatus = 'halfChecked'
}
} else {
// 如果取消单独选中,但子节点全选,保持半选状态
if (allSonsChecked) {
treeNode.checkStatus = 'halfChecked'
} else {
treeNode.checkStatus = 'noChecked'
}
}
// 更新父节点状态
updateParentStatus(treeNode)
}
onMounted(() => {
handleSetWebviewStyle()
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) => {
if (state.chooseMode === 1 || !depItem?.ID) {
currentMembers.value = []
return
}
// 使用更明确的参数结构
let params = { status: 'notactive' }
if (searchVal.value) {
params = {
fatherDepartmentId: depItem.ID,
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.code === 200) {
currentMembers.value = res?.data?.data?.length
? res.data.data.map((v) => {
return {
...v,
isMember: true,
}
})
: []
} else {
currentMembers.value = []
}
}
const checkMember = (item, val) => {
if (isPreSelectedMember(item)) {
return
}
if (val === 'checked') {
membersCheckedKeys.value.push(item)
} else {
membersCheckedKeys.value = membersCheckedKeys.value.filter(
(v) => v.ID !== item.ID,
)
}
}
watch(
() => currentCrumbs.value,
(newValue, oldValue) => {
if (newValue?.ID && (state.chooseMode === 2 || state.chooseMode === 3)) {
getCurrentMembers(newValue)
}
},
{ 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;
}
::v-deep .index_top_navbar .tmicon-angle-left {
color: #fff !important;
}
::v-deep .index_top_navbar .text-weight-b {
color: #fff !important;
}
::v-deep .index_top_navbar .statusHeightTop > .noNvueBorder:first-child {
background: transparent !important;
border: none !important;
}
.top_bg {
background: url('@/static/image/mine/page_top.png') no-repeat;
background-size: cover;
background-position: bottom center;
}
.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;
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>