Convert one-dimensional array to two-dimensional to realize the pop-up window style on the right

Finally achieve the effect

insert image description here

As shown in the figure, the data of this pop-up window has three levels, the first level is easy to do, and the second and third levels are difficult. It is necessary to control the style of the third level relative to the dynamic display of the second level. Click on the divs of the second and third levels There is no way to display a whole row with the width of the box, only that column can be displayed. Therefore, the final effect requires absolute positioning based on the second level. Due to absolute positioning, the third-level box does not occupy space, and the position is calculated according to the current selected row. One is set for the next row of the currently selected row, and then the height is dynamically margin topobtained margintop.

main code

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

The most important step is to convert the one-dimensional array into a two-dimensional array, because the one-dimensional array directly loops the box, and the obtained style can only be in columns, so the array needs to be processed into one row, which is four ( 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 []
  }
})

All code including styles

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

Guess you like

Origin blog.csdn.net/weixin_46319117/article/details/130193938