2025-02-06 09:01:28 +00:00
|
|
|
|
<template>
|
|
|
|
|
<div class="choose-deps-page">
|
|
|
|
|
<zPaging ref="zPaging" :show-scrollbar="false">
|
|
|
|
|
<template #top>
|
2025-03-14 09:05:05 +00:00
|
|
|
|
<div :class="state.chooseMode === 3 ? 'top_bg' : ''">
|
|
|
|
|
<customNavbar
|
|
|
|
|
:class="state.chooseMode === 3 ? 'index_top_navbar' : ''"
|
|
|
|
|
:title="pageTitle"
|
|
|
|
|
></customNavbar>
|
2025-03-05 05:14:54 +00:00
|
|
|
|
<div class="pl-[32rpx] pr-[32rpx] pt-[32rpx] pb-[32rpx]">
|
2025-02-06 09:01:28 +00:00
|
|
|
|
<customInput
|
|
|
|
|
:searchText="searchVal"
|
|
|
|
|
@inputSearchText="inputSearchText"
|
|
|
|
|
></customInput>
|
2025-03-05 05:14:54 +00:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<div class="choose-deps">
|
|
|
|
|
<div class="w-full pl-[32rpx] pr-[32rpx]">
|
2025-03-14 09:05:05 +00:00
|
|
|
|
<div
|
|
|
|
|
v-if="crumbs.length"
|
|
|
|
|
class="w-full overflow-x-auto mt-[22rpx] leading-[48rpx] text-[#2F2F2F] flex items-center no-scrollbar"
|
|
|
|
|
ref="crumbsContainer"
|
|
|
|
|
>
|
2025-02-06 09:01:28 +00:00
|
|
|
|
<div
|
2025-03-14 09:05:05 +00:00
|
|
|
|
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)"
|
2025-02-06 09:01:28 +00:00
|
|
|
|
>
|
2025-03-14 09:05:05 +00:00
|
|
|
|
<div>{{ item.name }}</div>
|
2025-02-06 09:01:28 +00:00
|
|
|
|
<div
|
2025-03-14 09:05:05 +00:00
|
|
|
|
v-if="index !== crumbs.length - 1"
|
|
|
|
|
class="ml-[20rpx] flex items-center mb-[2rpx]"
|
2025-02-06 09:01:28 +00:00
|
|
|
|
>
|
2025-03-14 09:05:05 +00:00
|
|
|
|
<tm-icon
|
|
|
|
|
name="tmicon-angle-right"
|
|
|
|
|
:font-size="20"
|
|
|
|
|
:color="index !== crumbs.length - 1 ? '#7A58DE' : '#C1B4EA'"
|
|
|
|
|
></tm-icon>
|
2025-02-06 09:01:28 +00:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-03-14 09:05:05 +00:00
|
|
|
|
</div>
|
2025-02-06 09:01:28 +00:00
|
|
|
|
<div
|
2025-02-08 09:09:57 +00:00
|
|
|
|
v-if="
|
|
|
|
|
(state.chooseMode === 1 || state.chooseMode === 2) &&
|
2025-03-04 08:38:44 +00:00
|
|
|
|
((currentCrumbs?.sons?.length > 0 &&
|
|
|
|
|
((state.chooseMode === 2 && !searchVal) ||
|
|
|
|
|
state.chooseMode !== 2)) ||
|
|
|
|
|
currentMembers?.length > 0)
|
2025-02-08 09:09:57 +00:00
|
|
|
|
"
|
2025-02-06 09:01:28 +00:00
|
|
|
|
class="pl-[32rpx] bg-[#FFFFFF] mt-[20rpx] h-[110rpx] flex items-center"
|
|
|
|
|
@click="() => allCheck(allCheckStatus)"
|
|
|
|
|
>
|
|
|
|
|
<div class="mr-[20rpx]">
|
2025-03-04 08:38:44 +00:00
|
|
|
|
<checkBox :modelValue="allCheckStatus"></checkBox>
|
2025-02-06 09:01:28 +00:00
|
|
|
|
</div>
|
|
|
|
|
<div class="font-medium text-[28rpx] leading-[40rpx]">
|
|
|
|
|
{{ $t('radio.btn.selectAll') }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
2025-02-18 11:51:23 +00:00
|
|
|
|
v-if="
|
|
|
|
|
currentCrumbs?.sons?.length > 0 &&
|
|
|
|
|
((state.chooseMode === 2 && !searchVal) || state.chooseMode !== 2)
|
|
|
|
|
"
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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
|
2025-02-07 09:00:57 +00:00
|
|
|
|
v-if="
|
|
|
|
|
(item.sons?.length && state.chooseMode === 1) ||
|
|
|
|
|
((state.chooseMode === 2 || state.chooseMode === 3) &&
|
|
|
|
|
getDepTotalMembers(item) > 0)
|
|
|
|
|
"
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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>
|
2025-02-06 09:01:28 +00:00
|
|
|
|
<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
|
2025-03-04 08:38:44 +00:00
|
|
|
|
:disabled="isPreSelectedMember(item)"
|
2025-02-06 09:01:28 +00:00
|
|
|
|
: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>
|
2025-02-07 09:00:57 +00:00
|
|
|
|
<tm-popover
|
|
|
|
|
position="tc"
|
|
|
|
|
style="max-width: calc(100% - 64rpx - 112rpx - 20rpx);"
|
|
|
|
|
>
|
2025-02-06 09:01:28 +00:00
|
|
|
|
<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"
|
|
|
|
|
>
|
2025-02-18 11:51:23 +00:00
|
|
|
|
<span v-if="item.showInPopup">
|
|
|
|
|
{{ item.name }}({{ getDepTotalMembers(item) }})
|
|
|
|
|
</span>
|
2025-02-06 09:01:28 +00:00
|
|
|
|
</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
|
2025-02-18 11:51:23 +00:00
|
|
|
|
v-if="
|
|
|
|
|
(item.showInPopup && state.chooseMode === 2) ||
|
|
|
|
|
state.chooseMode !== 2
|
|
|
|
|
"
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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>
|
2025-02-18 11:51:23 +00:00
|
|
|
|
<div
|
|
|
|
|
class="divider"
|
|
|
|
|
v-if="
|
|
|
|
|
(item.showInPopup && state.chooseMode === 2) ||
|
|
|
|
|
state.chooseMode !== 2
|
|
|
|
|
"
|
|
|
|
|
></div>
|
2025-02-06 09:01:28 +00:00
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
v-for="(item, index) in membersCheckedKeys"
|
|
|
|
|
class="flex flex-col"
|
|
|
|
|
>
|
|
|
|
|
<div
|
2025-03-04 06:10:25 +00:00
|
|
|
|
v-if="
|
|
|
|
|
index === 0 &&
|
|
|
|
|
allCheckedList.length == 0 &&
|
|
|
|
|
state.chooseMode === 2
|
|
|
|
|
"
|
2025-02-06 09:01:28 +00:00
|
|
|
|
class="divider"
|
|
|
|
|
></div>
|
|
|
|
|
<div
|
2025-03-04 06:10:25 +00:00
|
|
|
|
v-if="state.chooseMode === 2"
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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>
|
2025-03-04 06:10:25 +00:00
|
|
|
|
<div class="divider" v-if="state.chooseMode === 2"></div>
|
2025-02-06 09:01:28 +00:00
|
|
|
|
</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'
|
2025-03-14 09:05:05 +00:00
|
|
|
|
import { handleSetWebviewStyle } from '@/utils/common'
|
2025-02-06 09:01:28 +00:00
|
|
|
|
|
|
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
|
const { t } = useI18n()
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
groupActiveIndex,
|
2025-03-17 10:35:19 +00:00
|
|
|
|
getDepsTreeMy,
|
2025-02-06 09:01:28 +00:00
|
|
|
|
depTreeMyList,
|
|
|
|
|
crumbs,
|
|
|
|
|
crumbsIndex,
|
|
|
|
|
depCheckedKeys,
|
|
|
|
|
getDepMembers,
|
2025-03-04 06:10:25 +00:00
|
|
|
|
memberDepCheckedKeys,
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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=通讯录
|
2025-03-04 08:38:44 +00:00
|
|
|
|
preSelectedMembers: [], // 已在群里的成员数据
|
2025-02-06 09:01:28 +00:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onLoad((options) => {
|
|
|
|
|
if (options.chooseMode) {
|
|
|
|
|
state.chooseMode = Number(options.chooseMode)
|
|
|
|
|
}
|
2025-03-04 08:38:44 +00:00
|
|
|
|
|
|
|
|
|
// 如果有预选成员数据,初始化选中状态
|
|
|
|
|
if (options.preSelectedMembers) {
|
|
|
|
|
try {
|
|
|
|
|
const preSelectedMembers = JSON.parse(
|
|
|
|
|
decodeURIComponent(options.preSelectedMembers),
|
|
|
|
|
)
|
|
|
|
|
state.preSelectedMembers = preSelectedMembers
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('解析预选成员数据失败:', e)
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-06 09:01:28 +00:00
|
|
|
|
})
|
|
|
|
|
|
2025-03-04 08:38:44 +00:00
|
|
|
|
// 添加判断是否为预选成员的方法
|
|
|
|
|
const isPreSelectedMember = (member) => {
|
|
|
|
|
return state.preSelectedMembers.some(
|
|
|
|
|
(preMember) => preMember.erp_user_id === member.ID,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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') {
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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 !== '') {
|
2025-02-07 09:00:57 +00:00
|
|
|
|
// 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) =>
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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 {}
|
|
|
|
|
})
|
|
|
|
|
|
2025-02-07 09:00:57 +00:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-06 09:01:28 +00:00
|
|
|
|
const allCheckStatus = computed(() => {
|
2025-03-04 03:43:08 +00:00
|
|
|
|
if (
|
|
|
|
|
currentCrumbs?.value?.sons?.length === 0 &&
|
|
|
|
|
currentMembers.value.length === 0
|
|
|
|
|
) {
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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',
|
|
|
|
|
)
|
2025-03-04 08:38:44 +00:00
|
|
|
|
: true
|
2025-03-03 09:05:22 +00:00
|
|
|
|
|
2025-03-04 08:38:44 +00:00
|
|
|
|
// 检查人员选择状态,需要排除已在群里的成员
|
|
|
|
|
const selectableMembers = currentMembers.value.filter(
|
|
|
|
|
(member) => !isPreSelectedMember(member),
|
|
|
|
|
)
|
2025-03-03 09:05:22 +00:00
|
|
|
|
const allMembersChecked =
|
2025-03-04 08:38:44 +00:00
|
|
|
|
selectableMembers.length > 0 &&
|
|
|
|
|
selectableMembers.every((member) =>
|
2025-03-03 09:05:22 +00:00
|
|
|
|
membersCheckedKeys.value.some((checked) => checked.ID === member.ID),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 综合判断全选状态
|
2025-03-04 03:43:08 +00:00
|
|
|
|
if (
|
2025-03-04 08:38:44 +00:00
|
|
|
|
// 如果有可选部门,必须全部选中
|
|
|
|
|
(currentCrumbs?.value?.sons?.length === 0 || allDepsChecked) &&
|
|
|
|
|
// 如果有可选成员,必须全部选中
|
|
|
|
|
(selectableMembers.length === 0 || allMembersChecked)
|
2025-03-04 03:43:08 +00:00
|
|
|
|
) {
|
2025-03-03 09:05:22 +00:00
|
|
|
|
return 'checked'
|
2025-03-04 03:43:08 +00:00
|
|
|
|
} else if (
|
2025-03-04 08:38:44 +00:00
|
|
|
|
// 只要有部门或成员被选中就是半选状态
|
2025-03-04 03:43:08 +00:00
|
|
|
|
currentCrumbs?.value?.sons?.some(
|
|
|
|
|
(son) =>
|
|
|
|
|
son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
|
|
|
|
|
) ||
|
2025-03-04 08:38:44 +00:00
|
|
|
|
selectableMembers.some((member) =>
|
2025-03-04 03:43:08 +00:00
|
|
|
|
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
|
|
|
|
// 部门选择模式的原有逻辑保持不变
|
2025-02-18 11:51:23 +00:00
|
|
|
|
const allChecked = currentCrumbs?.value?.sons?.every(
|
2025-02-06 09:01:28 +00:00
|
|
|
|
(son) => son.checkStatus === 'checked',
|
|
|
|
|
)
|
2025-02-18 11:51:23 +00:00
|
|
|
|
const someChecked = currentCrumbs?.value?.sons?.some(
|
2025-02-06 09:01:28 +00:00
|
|
|
|
(son) => son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
|
|
|
|
|
)
|
2025-03-03 09:05:22 +00:00
|
|
|
|
|
2025-02-06 09:01:28 +00:00
|
|
|
|
if (allChecked) {
|
|
|
|
|
return 'checked'
|
|
|
|
|
} else if (someChecked) {
|
|
|
|
|
return 'halfChecked'
|
|
|
|
|
}
|
2025-03-03 09:05:22 +00:00
|
|
|
|
return 'noChecked'
|
2025-02-06 09:01:28 +00:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
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'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新当前节点及其所有子节点的状态
|
2025-02-18 11:51:23 +00:00
|
|
|
|
const updateCheckStatus = (node, status, doShow) => {
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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
|
|
|
|
|
2025-02-18 11:51:23 +00:00
|
|
|
|
if (state.chooseMode === 2 && doShow) {
|
|
|
|
|
node.showInPopup = true
|
|
|
|
|
if (state.searchVal) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-06 09:01:28 +00:00
|
|
|
|
if (node.sons && Array.isArray(node.sons) && node.sons.length > 0) {
|
|
|
|
|
node.sons.forEach((son) => updateCheckStatus(son, status))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const checkItemChange = (item, val) => {
|
|
|
|
|
// 更新当前节点及其子节点的状态
|
2025-02-18 11:51:23 +00:00
|
|
|
|
updateCheckStatus(item, val, true)
|
2025-02-06 09:01:28 +00:00
|
|
|
|
|
2025-03-03 07:22:53 +00:00
|
|
|
|
// 更新当前按钮状态
|
|
|
|
|
if (val === 'checked') {
|
|
|
|
|
item.currentChecked = true
|
|
|
|
|
} else if (val === 'noChecked') {
|
|
|
|
|
item.currentChecked = false
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-06 09:01:28 +00:00
|
|
|
|
// 更新父节点的状态
|
|
|
|
|
updateParentStatus(item)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const deleteNode = (item) => {
|
|
|
|
|
const treeNode = findNodeById(depTreeMyList.value[0], item.ID)
|
|
|
|
|
if (!treeNode) return
|
2025-03-03 07:22:53 +00:00
|
|
|
|
|
2025-02-06 09:01:28 +00:00
|
|
|
|
treeNode.checkStatus = 'noChecked'
|
2025-03-03 07:22:53 +00:00
|
|
|
|
treeNode.currentChecked = false
|
|
|
|
|
|
2025-02-18 11:51:23 +00:00
|
|
|
|
if (
|
|
|
|
|
state.chooseMode === 2 &&
|
|
|
|
|
treeNode?.sons?.length > 0 &&
|
|
|
|
|
Array.isArray(treeNode?.sons)
|
|
|
|
|
) {
|
|
|
|
|
updateCheckStatus(item, false)
|
|
|
|
|
}
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
// 处理部门选择
|
2025-02-18 11:51:23 +00:00
|
|
|
|
currentCrumbs?.value?.sons?.forEach((item) => {
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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-02-06 09:01:28 +00:00
|
|
|
|
})
|
2025-03-03 07:22:53 +00:00
|
|
|
|
|
2025-03-03 09:05:22 +00:00
|
|
|
|
// 人员选择模式下处理成员
|
2025-02-18 11:51:23 +00:00
|
|
|
|
if (state.chooseMode === 2) {
|
2025-03-03 09:05:22 +00:00
|
|
|
|
if (statusT === 'checked') {
|
2025-03-04 08:38:44 +00:00
|
|
|
|
// 全选时,添加所有当前显示的成员(排除已在群里的成员)
|
2025-03-03 09:05:22 +00:00
|
|
|
|
const newMembers = currentMembers.value.filter(
|
|
|
|
|
(member) =>
|
|
|
|
|
!membersCheckedKeys.value.some(
|
|
|
|
|
(existing) => existing.ID === member.ID,
|
2025-03-04 08:38:44 +00:00
|
|
|
|
) && !isPreSelectedMember(member),
|
2025-03-03 09:05:22 +00:00
|
|
|
|
)
|
|
|
|
|
membersCheckedKeys.value = [...membersCheckedKeys.value, ...newMembers]
|
|
|
|
|
} else {
|
2025-03-04 08:38:44 +00:00
|
|
|
|
// 取消全选时,移除当前显示的所有成员(已在群里的成员本来就是禁用的,不用特殊处理)
|
2025-03-03 09:05:22 +00:00
|
|
|
|
const currentMemberIds = new Set(
|
|
|
|
|
currentMembers.value.map((member) => member.ID),
|
|
|
|
|
)
|
|
|
|
|
membersCheckedKeys.value = membersCheckedKeys.value.filter(
|
|
|
|
|
(member) => !currentMemberIds.has(member.ID),
|
|
|
|
|
)
|
2025-02-18 11:51:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-06 09:01:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
2025-02-07 09:00:57 +00:00
|
|
|
|
if (allCheckedList?.value?.length > 0) {
|
|
|
|
|
const res = await getDepMembers({
|
|
|
|
|
departmentIds: allCheckedList.value.map((v) => v.ID),
|
|
|
|
|
status: 'notactive',
|
2025-02-06 09:01:28 +00:00
|
|
|
|
})
|
2025-02-07 09:00:57 +00:00
|
|
|
|
if (res.status == 0 && res.data?.data?.length) {
|
|
|
|
|
res.data?.data.forEach((v) => {
|
2025-03-04 06:10:25 +00:00
|
|
|
|
// 检查是否已存在相同 ID 的成员
|
|
|
|
|
if (!listT.some((item) => item.ID === v.ID)) {
|
|
|
|
|
listT.push(v)
|
|
|
|
|
}
|
2025-02-07 09:00:57 +00:00
|
|
|
|
})
|
|
|
|
|
}
|
2025-02-06 09:01:28 +00:00
|
|
|
|
}
|
|
|
|
|
allChooseMembers.value = listT
|
2025-03-04 06:10:25 +00:00
|
|
|
|
memberDepCheckedKeys.value = lodash.cloneDeep(allCheckedList.value) // 存储人员选择模式下的部门
|
|
|
|
|
} else {
|
|
|
|
|
depCheckedKeys.value = lodash.cloneDeep(allCheckedList.value) // 存储部门选择模式下的部门
|
2025-02-06 09:01:28 +00:00
|
|
|
|
}
|
|
|
|
|
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 = []
|
|
|
|
|
|
2025-03-17 10:35:19 +00:00
|
|
|
|
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) {
|
2025-03-04 06:10:25 +00:00
|
|
|
|
// 恢复人员选择模式下的部门状态,但只显示父部门
|
|
|
|
|
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-02-06 09:01:28 +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)
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-06 09:01:28 +00:00
|
|
|
|
onMounted(() => {
|
2025-03-14 09:05:05 +00:00
|
|
|
|
handleSetWebviewStyle()
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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) {
|
2025-02-07 09:00:57 +00:00
|
|
|
|
currentMembers.value = []
|
|
|
|
|
return
|
|
|
|
|
}
|
2025-03-04 03:43:08 +00:00
|
|
|
|
// 使用更明确的参数结构
|
|
|
|
|
let params = { status: 'notactive' }
|
2025-02-07 09:00:57 +00:00
|
|
|
|
if (searchVal.value) {
|
2025-03-04 03:43:08 +00:00
|
|
|
|
params = {
|
2025-02-07 09:00:57 +00:00
|
|
|
|
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
|
2025-02-07 09:00:57 +00:00
|
|
|
|
}
|
|
|
|
|
const res = await getDepMembers(params)
|
2025-02-06 09:01:28 +00:00
|
|
|
|
if (res.status === 0) {
|
2025-02-07 09:00:57 +00:00
|
|
|
|
currentMembers.value = res?.data?.data?.length
|
2025-02-06 09:01:28 +00:00
|
|
|
|
? res.data.data.map((v) => {
|
|
|
|
|
return {
|
|
|
|
|
...v,
|
|
|
|
|
isMember: true,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
: []
|
|
|
|
|
} else {
|
|
|
|
|
currentMembers.value = []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const checkMember = (item, val) => {
|
2025-03-04 08:38:44 +00:00
|
|
|
|
if (isPreSelectedMember(item)) {
|
|
|
|
|
return
|
|
|
|
|
}
|
2025-02-06 09:01:28 +00:00
|
|
|
|
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-02-06 09:01:28 +00:00
|
|
|
|
},
|
2025-03-04 03:43:08 +00:00
|
|
|
|
{ immediate: true },
|
2025-02-06 09:01:28 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2025-03-14 09:05:05 +00:00
|
|
|
|
::v-deep .index_top_navbar .tmicon-angle-left {
|
|
|
|
|
color: #fff !important;
|
2025-03-05 05:14:54 +00:00
|
|
|
|
}
|
2025-03-14 09:05:05 +00:00
|
|
|
|
::v-deep .index_top_navbar .text-weight-b {
|
|
|
|
|
color: #fff !important;
|
2025-03-05 05:14:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::v-deep .index_top_navbar .statusHeightTop > .noNvueBorder:first-child {
|
|
|
|
|
background: transparent !important;
|
|
|
|
|
border: none !important;
|
|
|
|
|
}
|
2025-03-14 09:05:05 +00:00
|
|
|
|
.top_bg {
|
2025-03-05 05:14:54 +00:00
|
|
|
|
background: url('@/static/image/mine/page_top.png') no-repeat;
|
|
|
|
|
background-size: cover;
|
|
|
|
|
background-position: bottom center;
|
|
|
|
|
}
|
2025-02-06 09:01:28 +00:00
|
|
|
|
.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%;
|
2025-02-06 09:01:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.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>
|