Vue中级联选择器实现数据回显有哪些事项要注意的

Admin 2022-06-08 群英技术资讯 863 次浏览

很多朋友都对“Vue中级联选择器实现数据回显有哪些事项要注意的”的内容比较感兴趣,对此小编整理了相关的知识分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获,那么感兴趣的朋友就继续往下看吧!

业务场景

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

问题描述

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

补充知识:Ant Design 级联选择的一种写法

简单记录类似省、市、区或品牌、车系、车型等多级结构,级联选择添加并展示的一种写法:

import React from 'react';
import {Button, Form, message, Row, Tag,Select,Col} from 'antd';
import request from "../../../../utils/request";
const FormItem = Form.Item;
const Option = Select.Option;
 
class CarSeriesCascader extends React.Component {
 
 constructor(props) {
  super(props);
  this.state = {
   defaultBrandList:[],
   selectedCarModelList: props.carModelList ? props.carModelList : [],
   brandCode:null,
   carModelList:[],
   carId:null,
   modelCode:null,
   modelName:null
  }
 }
 
 componentDidMount() {
  let promise = request(`/car/getBrandList`);
  promise.then(result =>{
  if(result != null){
   this.setState({
   defaultBrandList:result
   });
  }else{
   message.error("获取品牌数据失败");
  }
  }).catch(err => {
   message.error("获取品牌数据失败");
  });
  // this.setState({
  // selectedCarModelList:(this.props.carModelList ? this.props.carModelList : [])
  // });
  this.handleChange(this.state.selectedCarModelList);
 }
 
 getLimitList = (selectedCarModelList) => {
  let limitList = selectedCarModelList.map((carModel,index) => {
   let limitItem = {};
   limitItem.modelName = carModel.modelName;
   limitItem.modelCode = carModel.modelCode;
   limitItem.carId = carModel.carId;
   return limitItem;
  });
  return limitList;
 } 
 
 addCarModel = () => {
  let addCarModel = {};
  let selectedCarModelList = this.state.selectedCarModelList;
  // 选中车型号
  if (this.state.carId !== null) {
   // 检查车型是否已选中
   for (let index = this.state.selectedCarModelList.length - 1; index >= 0; index--) {
    let carModel = this.state.selectedCarModelList[index];
    if (carModel.carId == this.state.carId) {
     message.error("车型已在已选车型中");
     return;
    }
   }
   addCarModel.carId = this.state.carId;
   addCarModel.modelCode = this.state.modelCode;
   addCarModel.modelName = this.state.modelName;
   selectedCarModelList.push(addCarModel);
  } else {
   return;
  }
  this.handleChange(selectedCarModelList);
  this.setState({
   selectedCarModelList
  });
 }
 
 handleChange = (selectedCarModelList) => {
  if (this.props.onChange) {
   let limitList = this.getLimitList(selectedCarModelList);
   this.props.onChange(limitList);
  }
 }
 
 deleteTag = (limitCode) => {
  debugger
  let selectedCarModelList = this.state.selectedCarModelList;
  selectedCarModelList = selectedCarModelList.filter(carModel => !(carModel.modelCode === limitCode));
  this.handleChange(selectedCarModelList);
  this.setState({selectedCarModelList});
 }
 
 //品牌变化
 brandChange = (brandName) => {
 this.state.defaultBrandList.map((item, index) => {
  if (item.brandName == brandName) {
  let promise = request(`/car/getModelList?brandCode=` + item.brandCode);
  promise.then(result =>{
   if(result != null){
   this.setState({
    brandCode:item.brandCode,
    carModelList:result
   });
   }else{
   message.error("获取车型数据失败");
   }
  }).catch(err => {
   message.error("获取车型数据失败:");
  });
  }
 });
 }
 
 //车型变化
 modelChange = (modelName) => {
 this.props.form.setFieldsValue({modelName: null});
 let _this = this;
 this.state.carModelList.map((item, index) => {
  if (item.modelName == modelName) {
  console.log(item);
  this.setState({
  modelCode : item.modelCode,
  carId : item.carId,
  modelName : item.modelName
  });
  }
 });
 }
 
 render() {
  const {getFieldDecorator} = this.props.form;
  //品牌名称列表
  let allBrandListOption = this.state.defaultBrandList != null ? this.state.defaultBrandList.map((item, index) => {
  return <Option value={item.brandName} key={index}>{item.brandName}</Option>;
  }) : null;
 
  //车型名称列表
  let allModelListOption = this.state.carModelList != null ? this.state.carModelList.map((item, index) => {
  return <Option value={item.modelName} key={index}>{item.modelName}</Option>;
  }) : null;
 
  const {
   closable=true,
  } = this.props;
 
  const existCarModel = [];
  const limitList = this.getLimitList(this.state.selectedCarModelList);
  for (let index = limitList.length - 1; index >= 0; index--) {
   let limitItem = limitList[index];
   existCarModel.push(<Tag
    key={limitItem.modelCode}
    closable={closable}
    onClose={(e) => {
     e.preventDefault();
     this.deleteTag(limitItem.modelCode);
    }}
   >{limitItem.modelName}</Tag>);
  }
 
  return (
   <div>
    <Row>
     <FormItem >
      {getFieldDecorator('brandName', {
      rules: [{
       message: '请选择品牌'
      }],
      })(
      <Select
       placeholder="品牌"
       dropdownMatchSelectWidth={false}
       onChange={this.brandChange}
       style={{ marginRight: 10, width: 100 }}>
       <Option value={null}>选择品牌</Option>
       {allBrandListOption}
      </Select>
      )}
      {getFieldDecorator('modelName', {
      rules: [{
       message: '请选择车型'
      }],
      })(
      <Select
       placeholder="车型"
       dropdownMatchSelectWidth={false}
       onChange={this.modelChange}
       style={{ marginRight: 10, width: 260 }}>
       <Option value={null}>选择车型</Option>
       {allModelListOption}
      </Select>
      )}
      <Button type={"primary"} icon={"plus"} onClick={this.addCarModel}>添加车型</Button>
     </FormItem>
    </Row>
    <Row>
     {existCarModel}
    </Row>
   </div>
  )
 }
} 
export default Form.create()(CarSeriesCascader);

以上就是关于“Vue中级联选择器实现数据回显有哪些事项要注意的”的介绍了,感谢各位的阅读,如果大家想要了解更多相关的内容,欢迎关注群英网络,小编每天都会为大家更新不同的知识。
群英智防CDN,智能加速解决方案

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。

猜你喜欢

成为群英会员,开启智能安全云计算之旅

立即注册
专业资深工程师驻守
7X24小时快速响应
一站式无忧技术支持
免费备案服务
免费拨打  400-678-4567
免费拨打  400-678-4567 免费拨打 400-678-4567 或 0668-2555555
在线客服
微信公众号
返回顶部
返回顶部 返回顶部
在线客服
在线客服