将一维数组转化成二维实现右侧弹窗样式

最后实现效果

在这里插入图片描述

如图所示,这个弹窗的数据有三级,其中一级很好做,难在二级和三级,需要控制三级的样式相对于二级动态展示,点击二级,三级的div盒子宽度没办法显示一整行,只能展示那一列。所以最后实现效果需要根据二级进行绝对定位,由于绝对定位,三级盒子不占位,位置根据当前的选中行来计算,当前选中行的下一行设置一个margin top,然后margintop的高度动态获取。

主要代码

<div class="right">
          <div class="right_box">
            <div v-for="array, index in convertList" :key="`array-${index}`"
              :class="['row', currentRowIndex === index && isShowThree ? 'row_space' : '']">
              <div class="text_box" v-for="item in array" :key="item.id">
                <div class="text_icon flex" @click.stop="twoClick(item, index)"
                  :class="{ 'marginBottom': currentItemId === item.id }">
                  <img class="position_icon pointer" src="@/assets/image/position_jian.png" width="15" height="14" alt=""
                    v-if="currentItemId === item.id && isShowThree">
                  <img class="position_icon pointer" src="@/assets/image/position_jia.png" width="15" height="14" alt=""
                    v-else>
                  <div class="ellipsis" :class="{ 'relative_box': currentItemId === item.id }">{
    
    {
    
     item.displayName }}
                  </div>
                </div>
              </div>
            </div>
            <!-- 第三级的高度,用第三级item的行数*行高来计算,动态替换参考上面 -->
            <div v-if="currentRowIndex >= 0 && isShowThree" class="three_tree"
              :style="{ top: `${currentRowIndex * 40 + 60}px` }">
              <div>
                <el-row :gutter="20">
                  <el-col class="three_item" v-for="item in threeItemList" :key="item.id" @click="threeClick(item)"
                    :span="6">
                    <div class="pointer" :class="{ 'three_selected': currentItemThree === item.id }">
                      {
    
    {
    
     item.displayName }}
                    </div>
                  </el-col>
                </el-row>
              </div>
            </div>
          </div>
        </div>

最重要的一个步骤就是将一维数组转化成二维数组,因为用一维数组直接循环盒子,得到的样式只能是按列来的,所以需要将数组处理成一行,就是四个(index % 4 === 0

/**
 * 一维数组转换成展示的二维数组
 */
const convertList = computed(() => {
    
    
  if (twoItemList.value && twoItemList.value.length) {
    
    
    return twoItemList.value.reduce((acc, cur, index) => {
    
    
      if (index % 4 === 0) {
    
     // 每行展示4个
        acc.push(twoItemList.value.slice(index, index + 4))
      }
      return acc
    }, [])
  } else {
    
    
    return []
  }
})

所有代码包括样式

<template>
  <!-- 职位类型 -->
  <div class="dialog">
    <el-dialog :model-value="positionShow" destroy-on-close width="910" v-bind="$attrs" title="请选择职类" @close="closeBtn">
      <template #header>
        <div class="flex header_box">
          <div class="header_title">请选择职类</div>
          <el-select v-model="positionName" placeholder="请输入职位名称" clearable filterable remote :teleported="false"
            :remote-method="remoteMethod" :suffix-icon="Search" @change="searchChange" @keyup.enter="getSearch()">
            <el-option style="padding: 0;width: 100%;" v-for="item in positionSearchData" :key="item.id"
              :label="item.displayName" :value="item.id">
                <span style="float: left; font-size: 15px;margin-top:-6px;">{
    
    {
    
     item.displayName }}</span>
                <span style="float: left;display:block;position:absolute;margin-top:15px; font-size: 14px;color: #999999;">
                  {
    
    {
    
     item.parentDisplayName }}
                </span>
            </el-option>
          </el-select>
        </div>
      </template>
      <div class="container">
        <div class="left">
          <div v-for="(item, index) in positionTypeData" :key="index"
            :class="['item', 'ellipsis', item.id === parentId && 'actived']" @click="oneClick(item)">
            {
    
    {
    
     item.displayName }}
          </div>
        </div>
        <div class="right">
          <div class="right_box">
            <div v-for="array, index in convertList" :key="`array-${index}`"
              :class="['row', currentRowIndex === index && isShowThree ? 'row_space' : '']">
              <div class="text_box" v-for="item in array" :key="item.id">
                <div class="text_icon flex" @click.stop="twoClick(item, index)"
                  :class="{ 'marginBottom': currentItemId === item.id }">
                  <img class="position_icon pointer" src="@/assets/image/position_jian.png" width="15" height="14" alt=""
                    v-if="currentItemId === item.id && isShowThree">
                  <img class="position_icon pointer" src="@/assets/image/position_jia.png" width="15" height="14" alt=""
                    v-else>
                  <div class="ellipsis" :class="{ 'relative_box': currentItemId === item.id }">{
    
    {
    
     item.displayName }}
                  </div>
                </div>
              </div>
            </div>
            <!-- 第三级的高度,用第三级item的行数*行高来计算,动态替换参考上面 -->
            <div v-if="currentRowIndex >= 0 && isShowThree" class="three_tree"
              :style="{ top: `${currentRowIndex * 40 + 60}px` }">
              <div>
                <el-row :gutter="20">
                  <el-col class="three_item" v-for="item in threeItemList" :key="item.id" @click="threeClick(item)"
                    :span="6">
                    <div class="pointer" :class="{ 'three_selected': currentItemThree === item.id }">
                      {
    
    {
    
     item.displayName }}
                    </div>
                  </el-col>
                </el-row>
              </div>
            </div>
          </div>
        </div>
      </div>
    </el-dialog>
  </div>
</template>
<script setup>
import {
    
     computed, ref, toRefs, watch } from 'vue'
import {
    
     useRequest } from 'vue-request'
import {
    
     Search } from '@element-plus/icons-vue'
import {
    
     getPositionCategoryTree, positionSearch } from '@/apis/company/job'

const props = defineProps({
    
    
  positionShow: {
    
    
    type: Boolean,
    default: () => {
    
     return false }
  },
  positionId: {
    
    
    type: String,
    default: () => {
    
     return null }
  }
})
const {
    
     positionShow, positionId } = toRefs(props)
const emit = defineEmits(['positionChange', 'update:positionShow'])

// 二三级数据处理
const currentItemId = ref(-1) // 选中ItemID
const currentRowIndex = ref(-1) // 选中项目所在行
const currentItemThree = ref() // 选中的第三级item
const twoItemList = ref([]) // 右侧二级数据
const threeItemList = ref([]) // 三级数据
const isShowThree = ref(false) // 是否展示三级

/**
 * 一维数组转换成展示的二维数组
 */
const convertList = computed(() => {
    
    
  if (twoItemList.value && twoItemList.value.length) {
    
    
    return twoItemList.value.reduce((acc, cur, index) => {
    
    
      if (index % 4 === 0) {
    
     // 每行展示4个
        acc.push(twoItemList.value.slice(index, index + 4))
      }
      return acc
    }, [])
  } else {
    
    
    return []
  }
})

const oneClick = (item) => {
    
    
  isShowThree.value = false
  parentId.value = item.id
  if (item.children && item.children.length > 0) {
    
    
    twoItemList.value = item.children
  }
}

const twoClick = (item, index) => {
    
    
  if (currentRowIndex.value === index && currentItemId.value === item.id) {
    
    
    isShowThree.value = !isShowThree.value
  } else {
    
    
    isShowThree.value = true
  }
  currentItemId.value = item.id
  currentRowIndex.value = index
  if (item.children && item.children.length > 0) {
    
    
    threeItemList.value = item.children
  }
}

const threeClick = (item) => {
    
    
  currentItemThree.value = item.id
  emit('positionChange', {
    
     id: item.id, displayName: item.displayName })
  emit('update:positionShow', false)
}

// 职位类型选择树结构
const parentId = ref('1')
const {
    
     data: positionTypeData, run: getSearchData } = useRequest(getPositionCategoryTree, {
    
    
  defaultParams: [{
    
    }],
  onSuccess (data) {
    
    
    console.log(twoItemList.value)
    twoItemList.value = data?.filter(item => item?.id === parentId.value)[0]?.children
    if (positionId.value && data?.length > 0) {
    
    
      console.log('nininniinni')
      const array = positionTypeData.value.map(item => item.children?.map(e => e.children.filter(l => l.id === positionId.value)))
      // 合并数组
      const newArr = array?.reduce((pre, curr, index, array) => {
    
    
        return pre?.concat(curr)
      })
      // 去除空数组
      const result = newArr?.find(item => item && item.length !== 0)
      // 二级id 回显
      currentItemId.value = result[0].parentId
      // 三级id回显
      currentItemThree.value = positionId.value
      const threeArray = positionTypeData.value.map(item => item?.children?.filter(e => e.id === currentItemId.value))
      console.log(threeArray, 'threeArray')
      // 一级id 回显
      const oneArray = positionTypeData.value.map(item => item?.children?.filter(e => e.id === currentItemId.value))
      if (oneArray && oneArray.length > 0) {
    
    
        const oneResult = oneArray?.find(item => item && item.length !== 0)
        if (oneResult && oneResult.length > 0) {
    
    
          parentId.value = oneResult[0]?.parentId
          twoItemList.value = positionTypeData.value.filter(item => item.id === parentId.value)[0]?.children
          threeItemList.value = oneResult[0]?.children
          if (threeItemList.value && threeItemList.value.length > 0) {
    
    
            isShowThree.value = true
          }
        }
      }
      convertList.value.forEach((item, index) => {
    
    
        item.forEach((i) => {
    
    
          i.children.forEach(l => {
    
    
            if (l.id === positionId.value) {
    
    
              currentRowIndex.value = index
            }
          })
        })
      })
    }
    console.log(currentRowIndex.value, ' currentRowIndex.value')
  }
})

// 搜索职位类型
const positionSearchData = ref([])
const {
    
     run: getSearchHandler } = useRequest(positionSearch, {
    
    
  manual: true,
  onSuccess (data) {
    
    
    positionSearchData.value = data
  }
})

// 模糊搜索框
const positionName = ref(null)
const searchChange = (val) => {
    
    
  if (val) {
    
    
    const obj = positionSearchData.value.find(e => e.id === val)
    emit('positionChange', {
    
     id: obj.id, displayName: obj.displayName })
    emit('update:positionShow', false)
  }
}

const remoteMethod = (val) => {
    
    
  if (val) {
    
    
    getSearchHandler({
    
     keyword: val })
  }
}

const getSearch = () => {
    
    
  console.log('搜索')
}

watch(() => positionId.value,
  (val, oldVal) => {
    
    
    if (val) {
    
    
      getSearchData({
    
    })
    }
  }, {
    
     immediate: true, deep: true })

// 关闭弹窗
const closeBtn = () => {
    
    
  positionName.value = null
  currentRowIndex.value = -1
  isShowThree.value = false
  emit('update:positionShow', false)
}
</script>
<style lang="scss" scoped>
.container {
    
    
  width: 100%;
  height: auto;
  border-top: 1px solid rgba(0, 0, 0, 0.04);
  border-bottom: 1px solid rgba(0, 0, 0, 0.04);
  position: relative;
  margin-top: 15px;
  box-sizing: border-box;

  display: flex;

  .left {
    
    
    width: 217px;
    z-index: 10;

    font-size: 16px;
    font-weight: 500;
    color: #333333;

    max-height: 473px;
    overflow: hidden;
    overflow-y: scroll;

    &::-webkit-scrollbar {
    
    
      background-color: #FFFFFF;
      width: 5px;
    }

    /* 滚动条的滑轨背景颜色 */

    &::-webkit-scrollbar-thumb {
    
    
      background-color: var(--el-color-primary);
      border-radius: 7px;
      width: 5px;
    }

    .item {
    
    
      box-sizing: border-box;
      background-color: rgba(36, 182, 167, 0.1);
      margin-bottom: 1px;
      width: 100%;
      height: 54px;
      line-height: 54px;
      overflow: hidden;
      padding-left: 30px;
      padding-right: 20px;
      cursor: pointer;

      &.actived {
    
    
        background-color: #fff;
        position: relative;
        color: var(--el-color-primary);

        &::before {
    
    
          content: ' ';
          width: 4px;
          height: 100%;
          position: absolute;
          background-color: var(--el-color-primary);
          left: 0px;
          top: 0px
        }
      }
    }
  }

  .right {
    
    
    padding: 24px 29px;
    position: relative;
    left: 6px;
    top: 0px;
    width: calc(100% - 217px);
    height: 100%;
    padding-left: 24px;
    box-sizing: border-box;
    overflow: auto;

    :deep(.zlzp-checkbox) {
    
    
      width: 100%;

      .zlzp-checkbox__label {
    
    
        width: 100%;
        box-sizing: border-box;
      }
    }

    .checkbox {
    
    
      margin-bottom: 20px;
    }
  }

}

.select_text {
    
    
  &.selected {
    
    
    color: var(--el-color-primary);
  }
}

.select_text:hover {
    
    
  cursor: pointer;
  color: var(--el-color-primary);
}

.flex {
    
    
  display: flex;
  align-items: center;
}

.two_tree {
    
    
  position: relative;
}

.right_box {
    
    
  display: flex;
  align-items: center;
  flex-wrap: wrap;

  width: 632px;

  .row {
    
    
    display: flex;
    padding-bottom: 20px;
  }

  .row_space {
    
    
    margin-bottom: 225px;
  }

  .text_box {
    
    
    width: 150px;
  }

  .relative_box {
    
    
    position: relative;
    color: var(--el-color-primary);
  }

  .text_icon {
    
    
    width: 130px;
    font-size: 14px;
    font-weight: 500;
    color: #666666;

    text-overflow: ellipsis;
    overflow: hidden;
    cursor: pointer;
  }

  .position_icon {
    
    
    padding-right: 11px;
  }

  .three_tree {
    
    
    position: absolute;
    top: 50px;
    height: 211px;
    width: 632px;
    border: 1px solid #EDEDED;
    border-radius: 4px;

    font-size: 14px;
    font-weight: 500;
    color: #999999;
    box-sizing: border-box;

    padding: 26px 25px 0;

    overflow: hidden;
    overflow-y: scroll;

    &::-webkit-scrollbar {
    
    
      background-color: #FFFFFF;
      width: 5px;
    }

    /* 滚动条的滑轨背景颜色 */

    &::-webkit-scrollbar-thumb {
    
    
      background-color: #EBEBEB;
      border-radius: 7px;
      width: 5px;
    }

    .three_item {
    
    
      font-size: 14px;
      font-weight: 500;
      color: #999999;
      padding-bottom: 38px;
    }

    .three_selected {
    
    
      color: var(--el-color-primary);
    }
  }
}

.header_box {
    
    
  .header_title {
    
    
    padding-right: 70px;
    font-size: 28px;
    font-weight: bold;
    color: #333333;
  }

  :deep(.zlzp-select .zlzp-input) {
    
    
    width: 281px;
    height: 39px;
  }

  :deep(.zlzp-select-dropdown__item) {
    
    
    height: 50px;
    line-height: 50px;
    padding: 0 10px !important;
  }

  :deep(.zlzp-popper) {
    
    
    padding: 0 !important;
  }
}

.dialog {
    
    
  :deep(.zlzp-dialog__footer) {
    
    
    text-align: center;
  }

  :deep(.zlzp-dialog__body) {
    
    
    padding: 0px
  }

  :deep(.zlzp-dialog__headerbtn) {
    
    
    top: 13px;
    right: 20px;
  }
}
</style>

猜你喜欢

转载自blog.csdn.net/weixin_46319117/article/details/130193938