Vue中级联选择器实现数据回显有哪些事项要注意的
Admin 2022-06-08 群英技术资讯 863 次浏览
业务场景
由于项目需求,需要对相关类目进行多选,类目数据量又特别大,业务逻辑是使用懒加载方式加载各级类目数据,编辑时回显用户选择的类目。
问题描述
使用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);
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了事件循环的相关内容,下面一起来看一下,希望对大家有帮助。
VUE中的proxy和defineproperty有何区别?proxy和defineproperty的区别有:Proxy可以劫持的数组的改变,defineProperty 需要变异、Proxy代理可以劫持对象的改变,defineProperty需要遍历、Proxy代理可以劫持对象属性的添加,defineProperty用this.$set来实现等等,下面我们详细的了解看看。
使用js删除javascript对象的某个属性的方法有3种,我们使用的了js的delete函数,下面是我对js删除对象属性的方法总结,是本人在项目中经过测试的。 js代码 script//定义一个对象varmyObject={username:zhangsan,password:123456,address:hubei};//方式一//de
这篇文章给大家分享的是用JavaScript怎样实现一个日期倒计时的功能。小编觉得挺实用的,因此分享给大家做个参考,文中的示例代码介绍得很详细,有需要的朋友可以参考,接下来就跟随小编一起学习一下吧。
这篇文章主要为大家详细介绍了JavaScript实现京东秒杀效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008