业务场景

由于项目需求,需要对相关类目进行多选,类目数据量又特别大,业务逻辑是使用懒加载方式加载各级类目数据,编辑时回显用户选择的类目。

问题描述

使用Cascader级联选择器过程中主要存在的应用问题如下:

1、由于在未渲染节点数据的情况下编辑时无法找到对应的类目数据导致无法回显,如何自动全部加载已选择类目的相关节点数据;

2、提前加载数据后,点击相应父级节点出现数据重复等;

3、使用多个数据源相同的级联选择器,产生只能成功响应一个加载子级节点数据;

4、Vue中级联选择器相应数据完成加载,依然无法回显。

解决思路

Cascader级联选择器在需要回显的节点数据都存在的情况下,方可完成回显,首先想到的是把选中节点相关的数据全部获取到即可,遍历已选择的节点数据,遍历加载相对应的数据。(如果多个级联选择器使用同一个数据源,使用深拷贝将数据分开,避免产生影响)

由于是级联的数据懒加载,需要每一级相应的节点数据加载完进行下一步,故使用ES6中的Promise,将子级节点数据加载封装成一个Promise,待Promise执行完成,对列表数据遍历获取完成后返回即可。

getChildrenList (fid, level = 0) {
      return new Promise((resolve, reject) => {
        API.getCategory({ fid: fid, level: level }).then(
          res => {
            if (res) {
              if (res.code === 0 && res.result) {
                resolve(res.result)
              }
            }
          }
        )
      })
    },
let twolist = this.getChildrenList(codeArr[0], 1)
let thirdlist = this.getChildrenList(codeArr[1], 2)
Promise.all([twolist, thirdlist]).then((data) => {
    ...
})

Vue2的双向数据绑定使用ES2015中的Object.defineProperty(),该方法无法检测到Array中的深层数据变化,需要使用$set来触发列表数据的更新。

一个三级级联选择器,首先获取全部一级类目,二级类目和三级类目采用懒加载,获取数据的步骤如下:

1、获取全部一级类目;

2、由于使用异步数据加载,使用Promise进行数据请求;

3、根据已选择的类目获取相关联的二级类目和三级类目;

4、数据请求完成,使用$set触发列表数据更新,在$nextTick中完成数据你回显。

 

相关代码

<template>
  <div>
    <el-cascader
      placeholder="请选择所属类目"
      :options="categoryList"
      :show-all-levels="false"
      v-model="category"
      collapse-tags
      :props="{
        multiple: true,
        value: 'code',
        label: 'name',
        children: 'children',
        ...props,
      }"
    />
    <el-cascader
      placeholder="请选择所属类目"
      :options="secondCategoryList"
      :show-all-levels="false"
      v-model="secondCategory"
      collapse-tags
      :props="{
        multiple: true,
        value: 'code',
        label: 'name',
        children: 'children',
        ...props,
      }"
    />
  </div>
</template>

<script>
export default {
  data () {
    return {
      categoryList: [],
      category: [],
      secondCategoryList: [],
      secondCategory: [],
      props: {
        lazy: true,
        // checkStrictly: true,  // 父子级节点关联
        async lazyLoad (node, reso) {
          const { level, data } = node
          if (data && data.children && data.children.length !== 0) {
            return reso(node)
          }
          if (data && data.leaf) {
            return reso([])
          }
          const lv3Code = data ? data.code : null
          setTimeout(() => {
            lv3Code && API.getCategory({ fid: lv3Code, level: level }).then(
              res => {
                if (res) {
                  if (res.code === 0 && res.result) {
                    const nodes = res.result.map(item => ({ leaf: level === 2, ...item, children: [] }))
                    data.children = nodes
                    reso(nodes)
                  } else {
                    reso([])
                  }
                }
              }
            )
          }, 500)
        }
      }
    }
  },
  mounted () {
    this.getCategory()
    this.initData()
  },
  methods: {
    initData () {
      let _that = this
      异步获取编辑数据。。。
      .then(result => {
        // 此处仅处理result中firstCategory和secondCategory均不为空的情况
        let firstTemp = _that.getCategoryListFormat(result.firstCategory, _that.categoryList)
        let secondTemp = _that.getCategoryListFormat(result.secondCategory, _that.secondCategoryList)
        let promiseArr = [firstTemp, secondTemp].filter(_ => _)
        Promise.all(promiseArr).then((formatRes) => {
          // 触发列表数据响应
          this.$set(_that.categoryList, formatRes[0].tragetCategoryList)
          this.$set(_that.secondCategoryList, formatRes[1].tragetCategoryList)
          _that.$nextTick(() => {
            // 数据加载完成后,在下一次循环中回显
            _that.category = formatRes[0].category
            _that.secondCategory = formatRes[1].category
          })
        })
      })
    },
    getCategoryListFormat (categorySelectList, tragetCategoryList) {
      return new Promise((resolve, reject) => {
        const category = []
        let flag = 0
        let counter = categorySelectList.length

        categorySelectList.forEach(v => { // 遍历已选择节点数据
          const oneNode = v
          const twoNode = v.children
          const threeNode = v.children.children
          const codeArr = [oneNode.code, twoNode.code, threeNode.code]
          category.push(codeArr)
          twoNode.children = twoNode.children ? twoNode.children : []
          let twolist = this.getChildrenList(codeArr[0], 1)
          let thirdlist = this.getChildrenList(codeArr[1], 2)
          Promise.all([twolist, thirdlist]).then((data) => {
            let twochildren = data[0]
            let threechildren = data[1]
            threechildren = threechildren.map(item => ({ leaf: true, ...item })) // 三级节点设置成叶子节点
            twoNode.children = threechildren
            tragetCategoryList.forEach(w => { // 遍历列表添加相应节点数据
              if (w.code === oneNode.code) {
                if (!w.children) {
                  w.children = twochildren
                }
                w.children.forEach(item => {
                  if (item.code === twoNode.code) {
                    item.children = twoNode.children
                  }
                })
              }
            })
            flag++
            if (flag === counter) {
              resolve({ tragetCategoryList, category })
            }
          })
        })
      })
    },
    getChildrenList (fid, level = 0) {
      return new Promise((resolve, reject) => {
        API.getCategory({ fid: fid, level: level }).then(
          res => {
            if (res) {
              if (res.code === 0 && res.result) {
                resolve(res.result)
              }
            }
          }
        )
      })
    },
    getCategory(fid = 0, level = 0) {
      API.getCategory({ fid: fid, level: level })
        .then(
          res => {
            if (res) {
              if (res.code == 0 && res.result) {
                this.categoryList = this.deepClone(res.result);
              }
            }
          }
        )
    },
    deepClone (source) { // 深拷贝
      if (!source && typeof source !== 'object') {
        throw new Error('error arguments', 'shallowClone')
      }
      const targetObj = source.constructor === Array ? [] : {}
      Object.keys(source).forEach(keys => {
        if (source[keys] && typeof source[keys] === 'object') {
          targetObj[keys] = source[keys].constructor === Array ? [] : {}
          targetObj[keys] = deepClone(source[keys])
        } else {
          targetObj[keys] = source[keys]
        }
      })
      return targetObj
    }
  }
}
</script>

<style lang="less" scoped>
  
</style>

 

GitHub 加速计划 / vu / vue
207.55 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:3 个月前 )
73486cb5 * chore: fix link broken Signed-off-by: snoppy <michaleli@foxmail.com> * Update packages/template-compiler/README.md [skip ci] --------- Signed-off-by: snoppy <michaleli@foxmail.com> Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 5 个月前
e428d891 Updated Browser Compatibility reference. The previous currently returns HTTP 404. 5 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐