当前位置: 首页 > news >正文

【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>

相关文章:

  • Vue3 界面设计插件 microi-pageengine 入门教程一
  • MyBatis 学习经验分享
  • 责任链模式:优雅处理请求的设计艺术
  • Docker运行Mysql异常:Operation not permitted
  • OceanBase 读写分离最佳实践
  • ADB三个模块介绍
  • C# HTTP认证方式详解与代码实现
  • Docker 最佳实践(MySQL)
  • [spring] Spring JPA - Hibernate 多表联查 1
  • K8S学习之基础三十三:K8S之监控Prometheus部署程序版
  • 【蓝桥杯python研究生组备赛】005 数学与简单DP
  • windows安装金仓V9初始化数据库失败
  • Grid 布局实现三栏布局
  • Vue3:构建高效用户界面的利器
  • VUE中使用路由router跳转页面
  • HTTP请求方法详解:GET、POST、PUT在微信小程序中的应用
  • uni-app——计时器和界面交互API
  • Python 整数类型(int)大全
  • 大数据在人力资源管理中的洞察与决策
  • [动手学习深度学习] 27.含并行连结的网络 GoogLeNet/Inception v3
  • 万能险新规落地:保险期限不得低于五年,明确万能险销售“负面清单”
  • 博物馆有一项活动40岁以上不能参加?馆方回应
  • 美总统批准海底采矿,外交部:擅自授权开发损害国际社会共同利益
  • 政治局会议:要提高中低收入群体收入,设立服务消费与养老再贷款
  • 建设高标准农田主要目标是什么?有哪些安排?两部门有关负责人答问
  • 长三角议事厅|国际产业转移对中国产业链韧性的影响与对策