【vue3+vant】移动端 - 部门树下拉选择组件 DeptTreeSelect 开发
目录
- 效果展示
- 代码
- 父组件
- 子组件 DeptTreeSelect
效果展示
代码
父组件
父组件使用子组件
<template>
<div class="supervision-report">
<van-field
v-model="infoView.ssbm"
name="ssbm"
label="督察对象部门"
placeholder="请选择督查对象部门"
readonly
right-icon="arrow-down"
required
:rules="[{ required: true, message: '请选择督察对象部门' }]"
@click="showPickerHandle('ssbm')"
/>
<!-- 省略无关代码 -->
<van-popup
v-model:show="isShowPicker"
position="bottom"
:close-on-click-overlay="curColumnsKey !== 'ssbm'"
>
<DetpTreeSelect
:init-dept-tree="columnsObj.ssbm"
@close="isShowPicker = false"
@setValue="setSsbm"
/>
</van-popup>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
import { getDeptTree } from '@/api/global-dict-service'
export default {
setup() {
const info = reactive({
ssbm: ''
})
const infoView = reactive({
ssbm: ''
})
const isShowPicker = ref(false)
const curColumnsKey = ref('')
const columnsObj = reactive({
ssbm: []
})
onMounted(() => {
getDept()
})
const getDept = () => {
getDeptTree({}).then(res => {
columnsObj.ssbm = res.data || [] // 数据格式在子组件中有
})
}
const showPickerHandle = key => {
curColumnsKey.value = key
if (['fssj'].includes(key)) {
const curTime = info.fssj
curTime ? (currentDate.value = new Date(curTime)) : new Date()
}
isShowPicker.value = true
}
const setSsbm = val => {
console.log('val----打印', val)
info.ssbm = val.dwdm
infoView.ssbm = val.dwjc
}
return {
info,
infoView,
showPickerHandle,
columnsObj,
isShowPicker,
curColumnsKey,
setSsbm
}
}
}
</script>
<style lang="less" scoped>
@bgc: #f3f4f6;
.supervision-report {
height: 100vh;
background-color: #fff;
.van-form {
overflow: auto;
}
:deep(.van-cell) {
@lineHeight: 40px;
padding: 10px;
&:nth-child(-n + 8) {
label {
line-height: @lineHeight;
}
}
input {
height: @lineHeight;
}
}
:deep(.van-field__body) {
background-color: @bgc;
border-radius: 5px;
padding: 0 10px;
}
.iconfont,
:deep(.van-field__right-icon .van-icon) {
font-size: 14px;
}
.field_block {
display: block;
}
:deep(.van-uploader) {
width: 100%;
.van-uploader__input-wrapper {
width: 100%;
border-top: 1px dashed #ccc;
.upload_btn {
height: 100px;
width: 100%;
border-radius: 5px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #9ca3af;
font-size: 14px;
}
}
}
.upload_tips {
margin: 0 20px 10px;
color: #9ca3af;
word-break: break-all;
font-size: 14px;
}
.btns {
display: flex;
justify-content: space-between;
margin: 0 20px 20px;
.van-button {
flex: 1;
border-radius: 5px;
&:first-child {
margin-right: 10px;
}
}
}
.dialog_father {
width: 100%;
height: 100vh;
position: fixed;
top: 0;
left: 0;
background-color: #423e3e39;
display: flex;
align-items: center;
justify-content: center;
.dialog {
background-color: #fff;
width: 95%;
padding: 10px;
border-radius: 10px;
box-sizing: border-box;
&_header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
font-size: 18px;
.title {
}
.close {
padding: 5px;
font-size: 22px;
color: #9ca3af;
}
}
&_body {
.TMap {
width: 100%;
height: 70vh;
}
}
&_footer {
display: flex;
gap: 10px;
margin-top: 10px;
.van-button {
flex: 1;
}
}
}
}
}
</style>
子组件 DeptTreeSelect
@/components/DeptTreeSelect.vue
<!-- 部门树下拉选择组件 -->
<template>
<div class="area-picker">
<div class="btns">
<span @click="reset">取消</span>
<span class="blue" @click="handleConfirm">确定</span>
</div>
<!-- 已选择的部门数据 -->
<div class="header">
<span
v-for="(selectedArea, index) in selectedAreas"
:key="selectedArea.data.dwdm"
@click="toggleArea(selectedArea, index)"
>
{{ selectedArea.data?.dwjc }}
<i v-if="index < selectedAreas.length - 1" class="arrow"> > </i>
</span>
</div>
<!-- 当前可选择的部门数据 -->
<div class="body">
<div
v-for="area in renderAreaList"
:key="area.data.dwdm"
class="body_item"
@click="selectDeptFun(area)"
>
{{ area.data?.dwjc }}
<span v-if="selectedAreas.includes(area)" class="blue font_weight">✓</span>
</div>
</div>
</div>
</template>
<script setup name="DeptTreeSelect">
import { ref } from 'vue'
import { Toast } from 'vant'
// initDeptTree 为父组件传的部门树数据
const { initDeptTree } = defineProps({
initDeptTree: {
type: Array,
default: () => [
{
data: {
dwdm: '330000000000',
dwmc: '台州市公安局',
dwjc: '台州市局',
sjdwdm: null
},
childrenNode: [
{
data: {
dwdm: '33100000011X',
dwmc: '仙居县局',
dwjc: '台州市局-仙居县局',
sjdwdm: '330000000000'
},
childrenNode: []
},
{
data: {
dwdm: '33100000012X',
dwmc: '天台县局',
dwjc: '台州市局-天台县局',
sjdwdm: '330000000000'
},
childrenNode: []
},
{
data: {
dwdm: '33100000013X',
dwmc: '三门县局',
dwjc: '台州市局-三门县局',
sjdwdm: '330000000000'
},
childrenNode: []
},
{
data: {
dwdm: '331000290000',
dwmc: '市局各部门',
dwjc: '台州市局-市局各部门',
sjdwdm: '330000000000'
},
childrenNode: []
}
]
},
{
data: {
dwdm: '335401000010',
dwmc: '高一大队',
dwjc: '高一大队',
sjdwdm: null
},
childrenNode: [
{
data: {
dwdm: '33540100001A',
dwmc: '一中队',
dwjc: '高一大队-一中队',
sjdwdm: '335401000010'
},
childrenNode: []
},
{
data: {
dwdm: '33540100001B',
dwmc: ' 二中队',
dwjc: '高一大队- 二中队',
sjdwdm: '335401000010'
},
childrenNode: []
},
{
data: {
dwdm: '33540100001C',
dwmc: '三中队',
dwjc: '高一大队-三中队',
sjdwdm: '335401000010'
},
childrenNode: []
},
{
data: {
dwdm: '33540100001D',
dwmc: '四中队',
dwjc: '高一大队-四中队',
sjdwdm: '335401000010'
},
childrenNode: []
}
]
}
]
}
})
const emit = defineEmits(['setValue', 'close'])
const renderAreaList = ref([...initDeptTree]) // 存储渲染的区域数据
const selectedAreas = ref([]) // 已选择的部门数据 -- header 部分渲染的数据
// 点击 header 部分切换已选择部门
const toggleArea = (area, toggleClickIndex) => {
renderAreaList.value =
toggleClickIndex === 0
? [...initDeptTree]
: selectedAreas.value[toggleClickIndex - 1].childrenNode
selectedAreas.value = selectedAreas.value.slice(0, toggleClickIndex)
}
// 选中部门时处理
const selectDeptFun = area => {
if (area.childrenNode.length > 0) {
renderAreaList.value = area.childrenNode
} else {
const index = selectedAreas.value.findIndex(item => item.data.sjdwdm === area.data.sjdwdm)
if (index !== -1) {
selectedAreas.value.splice(index, 1)
}
}
selectedAreas.value.push(area)
}
// 重置部门数据
const reset = () => {
selectedAreas.value = []
renderAreaList.value = [...initDeptTree]
emit('close')
}
// 确定选择部门
const handleConfirm = () => {
if (!selectedAreas.value.length) {
Toast('请选择部门')
return
}
const value = selectedAreas.value[selectedAreas.value.length - 1].data
emit('setValue', value)
reset()
}
</script>
<style scoped lang="less">
.area-picker {
background-color: #e8e7ea;
max-height: 50vh;
min-height: 30vh;
// overflow: auto;
display: flex;
flex-direction: column;
.blue {
color: #3970e3;
}
.font_weight {
font-weight: bold;
}
.btns {
display: flex;
justify-content: space-between;
padding: 10px;
background-color: #fff;
border-bottom: 1px solid #ccc;
font-size: 18px;
}
.header {
background-color: #fff;
margin-bottom: 10px;
padding: 5px 10px;
font-size: 16px;
.arrow {
margin: 0 5px;
}
}
.body {
flex: 1;
overflow: auto;
padding: 0 8px;
background-color: #fff;
font-size: 16px;
&_item {
display: flex;
padding: 8px;
justify-content: space-between;
border-bottom: 1px solid #eee;
}
}
}
</style>