547 lines
16 KiB
Vue
547 lines
16 KiB
Vue
<template>
|
|
<div class="choose-deps-page">
|
|
<zPaging ref="zPaging" :show-scrollbar="false">
|
|
<template #top>
|
|
<customNavbar :title="$t('pageTitle.select.department')"></customNavbar>
|
|
</template>
|
|
<div class="choose-deps">
|
|
<div class="w-full pl-[32rpx] pr-[32rpx] overflow-y-auto">
|
|
<div class="pl-[16rpx] pr-[16rpx] pt-[22rpx] pb-[24rpx] bg-[#FFFFFF]">
|
|
<tm-input
|
|
placeholder="请输入…"
|
|
color="#F9F9FD"
|
|
:round="1"
|
|
prefix="tmicon-search"
|
|
prefixColor="#46299D"
|
|
searchBgColor="#F9F9FD"
|
|
focusColor="#F9F9FD"
|
|
v-model="searchVal"
|
|
></tm-input>
|
|
<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
|
|
class="pl-[32rpx] bg-[#FFFFFF] mt-[20rpx] h-[110rpx] flex items-center"
|
|
@click="() => allCheck(allCheckStatus)"
|
|
>
|
|
<div>
|
|
<checkBox
|
|
:disabled="currentCrumbs?.sons?.length === 0"
|
|
:modelValue="allCheckStatus"
|
|
></checkBox>
|
|
</div>
|
|
<div class="font-bold text-[28rpx] leading-[54rpx] ml-[20rpx]">
|
|
全选
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="currentCrumbs?.sons?.length"
|
|
v-for="item in currentCrumbs?.sons"
|
|
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>
|
|
<checkBox
|
|
:disabled="!item?.sons?.length"
|
|
v-model="item.checkStatus"
|
|
@change="(val) => checkItemChange(item, val)"
|
|
></checkBox>
|
|
</div>
|
|
<div class="font-bold text-[28rpx] leading-[54rpx] ml-[20rpx]">
|
|
{{ item.name }}
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="item.sons?.length"
|
|
class="flex items-center mr-[32rpx]"
|
|
>
|
|
<div class="vDivider mr-[32rpx]"></div>
|
|
<div @click="() => 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-[54rpx] font-bold"
|
|
:class="
|
|
item.checkStatus !== 'checked'
|
|
? 'text-[#46299D]'
|
|
: 'text-[#C1B4EA]'
|
|
"
|
|
>
|
|
下级
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<template #bottom>
|
|
<div class="h-[162rpx] pl-[32rpx] pr-[32rpx] bg-[#FFFFFF]">
|
|
<div class="mt-[14rpx] flex justify-between">
|
|
<div class="flex flex-col">
|
|
<div
|
|
@click="openDrawer"
|
|
class="flex items-center text-[28rpx] leading-[60rpx] text-[#000000] font-bold"
|
|
>
|
|
<div>已选择部门数:</div>
|
|
<div>{{ allCheckedList.length }}</div>
|
|
<div class="ml-[28rpx]">
|
|
<tm-icon
|
|
:fontSize="24"
|
|
color="#46299D"
|
|
name="tmicon-angle-up"
|
|
></tm-icon>
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="text-[24rpx] leading-[24rpx] text-[#7A58DE] w-[280rpx] truncate"
|
|
>
|
|
{{ allCheckedList.map((v) => v.name).toString() }}
|
|
</div>
|
|
</div>
|
|
<div class="btnBox">
|
|
<tm-button
|
|
@click="handleConfirm"
|
|
color="#46299D"
|
|
:disabled="!allCheckedList.length"
|
|
disabledColor="#E6E6E6"
|
|
:margin="[0]"
|
|
:shadow="0"
|
|
:width="264"
|
|
:height="76"
|
|
size="large"
|
|
label="确定"
|
|
></tm-button>
|
|
</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-bold flex items-center justify-between"
|
|
>
|
|
<div class="flex items-center ml-[10rpx]">
|
|
<div>已选择部门数:</div>
|
|
<div>{{ allCheckedList.length }}</div>
|
|
</div>
|
|
<div
|
|
class="text-[#7A58DE] mr-[10rpx]"
|
|
@click="() => (showWin = false)"
|
|
>
|
|
确定
|
|
</div>
|
|
</div>
|
|
<div class="flex-1 pb-[20rpx] overflow-y-auto pt-[30rpx]">
|
|
<div
|
|
v-for="(item, index) in allCheckedList"
|
|
class="flex flex-col"
|
|
>
|
|
<div v-if="index === 0" class="divider"></div>
|
|
<div
|
|
class="flex items-center justify-between mt-[36rpx] font-bold text-[#000000] leading-[54rpx] mb-[34rpx]"
|
|
>
|
|
<div class="text-[28rpx] ml-[10rpx]">{{ item.name }}</div>
|
|
<div class="diyBtn">
|
|
<tm-button
|
|
@click="() => deleteNode(item)"
|
|
:disabled="userDepIds.includes(item.ID)"
|
|
:margin="[10]"
|
|
:shadow="0"
|
|
text
|
|
size="small"
|
|
:width="106"
|
|
:height="50"
|
|
outlined
|
|
label="移除"
|
|
></tm-button>
|
|
</div>
|
|
</div>
|
|
<div class="divider"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</tm-drawer>
|
|
</template>
|
|
</zPaging>
|
|
</div>
|
|
</template>
|
|
<script setup>
|
|
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
|
import { ref, watch, computed, onMounted, nextTick } 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'
|
|
|
|
const {
|
|
groupActiveIndex,
|
|
getDepsTreeMy,
|
|
depTreeMyList,
|
|
crumbs,
|
|
crumbsIndex,
|
|
depCheckedKeys,
|
|
} = useGroupTypeStore()
|
|
const userStore = useUserStore()
|
|
|
|
const searchVal = ref('')
|
|
const crumbsContainer = ref(null)
|
|
const showWin = ref(false)
|
|
|
|
const getAllCheckedNodes = (node, checkedNodes = []) => {
|
|
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 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 allCheckStatus = computed(() => {
|
|
if (!currentCrumbs.value.sons) {
|
|
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'
|
|
} else {
|
|
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) => {
|
|
node.checkStatus = status
|
|
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)
|
|
|
|
// 更新父节点的状态
|
|
updateParentStatus(item)
|
|
}
|
|
|
|
const deleteNode = (item) => {
|
|
const treeNode = findNodeById(depTreeMyList.value[0], item.ID)
|
|
if (!treeNode) return
|
|
treeNode.checkStatus = 'noChecked'
|
|
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
|
|
checkItemChange(itemT, statusT)
|
|
})
|
|
}
|
|
|
|
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 = () => {
|
|
depCheckedKeys.value = lodash.cloneDeep(allCheckedList.value)
|
|
console.log('depCheckedKeys', depCheckedKeys.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
|
|
await getDepsTreeMy()
|
|
crumbs.value = depTreeMyList.value.length ? [depTreeMyList.value[0]] : []
|
|
initCheckedKeys()
|
|
}
|
|
|
|
onMounted(() => {
|
|
init()
|
|
})
|
|
</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;
|
|
padding: 20rpx 0;
|
|
}
|
|
}
|
|
|
|
.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 {
|
|
:deep(uni-button[disabled='true']) {
|
|
color: #bebebe !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 {
|
|
:deep(uni-button) {
|
|
color: #191919 !important;
|
|
border: 1rpx solid #d6d6d8 !important;
|
|
background-color: #ffffff !important;
|
|
&[disabled='true'] {
|
|
color: #bebebe !important;
|
|
border: 1rpx solid #e6e6e6 !important;
|
|
background-color: #e6e6e6 !important;
|
|
}
|
|
}
|
|
}
|
|
</style>
|