前言
- 在网上搜索行政区划(省市区)级联选择的时候没有发现数据源是异步请求接口来的(有的例子是是异步但是没有交代设置默认值如何操作 所以自己应公司需求改装了一下)
- 此组件依赖于ant design of vuehttps://www.antdv.com/components/cascader-cn/
-
效果
介绍
- 接口格式
{
"id": "1194501203665408001",
"createBy": "admin",
"createTime": "2019-11-13 14:24:14",
"updateBy": null,
"updateTime": null,
"sysOrgCode": "A01",
"hasChild": "0",
"code": "460101000000",
"name": "市辖区",
"pcode": "460100000000",
"remarks": null,
"zeroReport": "0"
},
{
"id": "1194501203665408002",
"createBy": "admin",
"createTime": "2019-11-13 14:24:14",
"updateBy": null,
"updateTime": null,
"sysOrgCode": "A01",
"hasChild": "1",
"code": "460105000000",
"name": "秀英区",
"pcode": "460100000000",
"remarks": null,
"zeroReport": "0"
}
通过code 代表自身所在行政区代码 pcode 是上一级代码 haschild 代表是否有下一级(0无 1有)
- 组件
首先字段值和cascader要求的对应字段是不一致的 我们需要自定义字段名更多请了解原组件介绍
<template>
<a-cascader
//数据源
:options="options"
//选中项改变时触发事件
@change="onChange"
//在选中当前项的时候做的事情
:loadData="loadData"
//当前无选中的时候显示的值
:placeholder="placeholder"
//对应接口字段构建树
:fieldNames="{ label: 'name', value: 'code', children: 'children' }"
//设置默认值 类似于v-model
:value="values"
//设置key 用于更新dom
:key="type"
//选择即改变的关键词
changeOnSelect
//是否禁用
:disabled="disabled"
/>
</template>
相应的我们要在data上绑定相应的值
data() {
return {
options: [],
fieldNames: ['city', 'county', 'township', 'village'],
values: [],
type: true
}
}
接收项
props: {
placeholder: { type: String, default: '请选择' },
//接收默认值数组与filedNames中的字段对应的值
defaultvalue: {
type: Array,
default: () => []
},
disabled: {
type: Boolean,
default: false
}
}
在method中
//此方法只运行一次
loadfisrtData() {
//首先先请求第一级的所需数据源
axios.get('/dict/nhDictXzq/getList', { params: { pcode: 460000000000 } }).then(res => {
let arr = res.result
arr.forEach(element => {
//每一项添加isleaf
element.isLeaf = false
})
//赋值给数据源
this.options = arr
})
},
//选中项改变时触发事件 [460300000000,460300000000,460300000000] 类似这样 选择几级对应长度就有多少
onChange(arr) {
// 设置默认值
this.values = [...arr]
// 我的业务需求 每次点击后返回相应的{key:value} 如{city:460300000000,460300000000,460300000000} 这也是设置filedNames的原因
let obj = {}
if (arr.length == 0) {
obj = null
this.loadfisrtData()
this.$emit('onChanged', obj)
return
}
const length = arr.length
// 取出所得数组 最后一位;
const value = arr[length - 1]
const key = this.fieldNames[length - 1]
obj[key] = value
// 返回最后一位对应结果
this.$emit('onChanged', obj)
// 返回对应关系结果
let newObj = {}
let nameArray = {}
let keyarr = [...this.fieldNames]
//数据库中特殊情况 因为个别市的级联只有两级 或者三级(一般为四级)做特别处理
if (arr[0] == 460300000000 || arr[0] == 460100000000 || arr[0] == 460200000000) {
//取出对应汉字(这个组件没有取出组件上显示的值的方法 所以只能自己从数据源中取 用对应的级联等级的code使用递归取)
this.getCodeName(nameArray, this.options, 0, keyarr, arr)
// {'cityname':'', 'countyname':'', 'townshipname':'', 'villagename':''}
keyarr.forEach((ins, index) => {
newObj[ins] = arr[index]
})
arr.length == 4 ? this.$emit('resultCodeArray', { nameArray, newObj }) : this.$emit('resultCodeArray', null)
} else if (arr[0] == 460400499000) {
keyarr.splice(1, 2)
this.getCodeName(nameArray, this.options, 0, keyarr, arr)
// this.getCodeName(nameArray, this.options, 0, keyarr)
keyarr.forEach((ins, index) => {
newObj[ins] = arr[index]
})
arr.length == 2 ? this.$emit('resultCodeArray', { nameArray, newObj }) : this.$emit('resultCodeArray', null)
} else {
// let keys = ['city', 'township', 'village']
//{'cityname':'','townshipname':'', 'villagename':''}
keyarr.splice(1, 1)
this.getCodeName(nameArray, this.options, 0, keyarr, arr)
keyarr.forEach((ins, index) => {
newObj[ins] = arr[index]
})
arr.length == 3 ? this.$emit('resultCodeArray', { nameArray, newObj }) : this.$emit('resultCodeArray', null)
}*/
},
loadData(selectedOptions) {
//官方方法 取出当前项
const targetOption = selectedOptions[selectedOptions.length - 1]
//判断是否还有下一级
if (targetOption.hasChild == 0) {
return
}
targetOption.loading = true
axios.get('/dict/nhDictXzq/getList', { params: { pcode: targetOption.code } }).then(res => {
targetOption.loading = false
let arr = res.result
arr.forEach(element => {
element.isLeaf = false
})
targetOption.children = arr
//重新更新数据源
this.options = [...this.options]
})
}
到这里就已经完成了选择了
- 根据设置默认值显示组件
在这边有个坑 这个组件是多个页面公用的 所以不会一直触发生命周期的钩子我们只能去监视默认值的变化 因为有的情景是需要设置默认值(取)有的不用的
watch: {
defaultvalue: function(value) {
//过滤掉默认值中的空值 原因就是有的是三级 有的是两级
this.values = value.filter(ins => {
return ins !== ''
})
this.values.length > 0 && this.setDefault()
}
},
mounted() {
//挂载后执行获取数据源操作
this.loadfisrtData()
//把props的值给到data上面的值 后面要对values改变props传来的值不推荐直接改变所以使用变量存起来
this.values = [...this.defaultvalue]
},
设置默认值到组件上
setDefault() {
// console.log('访问的values', this.values)
//最初的方法 获取到所有的对应级联数据在对静态数据操作 格式转换
// let promiseArr = []
// this.values.forEach(ins => {
// promiseArr.push(
// new Promise((resolve, reject) => {
// axios.get('/dict/nhDictXzq/getList', { params: { pcode: ins } }).then(res => {
// resolve(res.result)
// })
// })
// )
// })
// Promise.all(promiseArr).then(result => {
// console.log('result',JSON.stringify(result))
// // 先把result的数据结果集组装
// let res = []
// this.values.forEach((ins, index) => {
// if (index + 1 < this.values.length) {
// result.forEach((c, index) => {
// let a = c.filter(i => {
// return i.code == this.values[index + 1]
// })
// if (a && a.length > 0) {
// a.isLeaf = false
// a.loading = false
// res.push(a)
// // 合并数组
// res = Array.prototype.concat.apply([], res)
// res = Array.from(new Set(res))
// }
// })
// }
// })
// for (var i = res.length; i > 0; i--) {
// if (res[i]) {
// res[i - 1].children = [res[i]]
// }
// }
// res = res[0]
// res.isLeaf = false
// res.loading = false
// let d = this.options.find(ins => {
// return ins.code == this.values[0]
// })
// d && (d.children = [res])
// // 更新dom
// this.type = !this.type
// })
//第二种使用递归方法 直接为他添加children属性
const p = this.options.find(ins => {
return ins.code == this.values[0]
})
p && this.setChildren(p, 1, this.values, () => {
// 更新dom
this.type = !this.type
})
},
//parmas 父级 当前层级 传入的默认值 遍历后回调
setChildren(parent, index, values, cb) {
axios.get('/dict/nhDictXzq/getList', { params: { pcode: parent.code } }).then(res => {
//结束条件 最后一级是没有子集的
if (res.result.length == 0) {
cb()
return
}
parent.children = res.result
res.result.forEach(ins => {
ins.isLeaf = false
ins.loading = false
if (ins.code === values[index]) {
index++
this.setChildren(ins, index, values, cb)
}
})
})
}
到这里 这个组件就全部都介绍完了 因为是临时做的所以没有做的很通用如果没有理解的可以联系我....
- 完整的代码
<template>
<a-cascader
:options="options"
@change="onChange"
:loadData="loadData"
:placeholder="placeholder"
:fieldNames="{ label: 'name', value: 'code', children: 'children' }"
:value="values"
:key="type"
changeOnSelect
:disabled="disabled"
/>
</template>
<script>
import { axios } from '@/utils/request'
export default {
props: {
placeholder: { type: String, default: '请选择' },
defaultvalue: {
type: Array,
default: () => []
},
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
options: [],
fieldNames: ['city', 'county', 'township', 'village'],
values: [],
type: true
}
},
watch: {
defaultvalue: function(value) {
this.values = value.filter(ins => {
return ins !== ''
})
this.values.length > 0 && this.setDefault()
}
},
mounted() {
this.loadfisrtData()
this.values = [...this.defaultvalue]
},
methods: {
loadfisrtData() {
axios.get('/dict/nhDictXzq/getList', { params: { pcode: 460000000000 } }).then(res => {
let arr = res.result
arr.forEach(element => {
element.isLeaf = false
})
this.options = arr
})
},
onChange(arr) {
this.values = [...arr]
let obj = {}
if (arr.length == 0) {
obj = null
this.loadfisrtData()
this.$emit('onChanged', obj)
return
}
const length = arr.length
// 取出所得数组 最后一位;
const value = arr[length - 1]
const key = this.fieldNames[length - 1]
obj[key] = value
// 返回最后一位对应结果
this.$emit('onChanged', obj)
// 返回对应关系结果
let newObj = {}
let nameArray = {}
let keyarr = [...this.fieldNames]
if (arr[0] == 460300000000 || arr[0] == 460100000000 || arr[0] == 460200000000) {
this.getCodeName(nameArray, this.options, 0, keyarr, arr)
// {'cityname':'', 'countyname':'', 'townshipname':'', 'villagename':''}
keyarr.forEach((ins, index) => {
newObj[ins] = arr[index]
})
arr.length == 4 ? this.$emit('resultCodeArray', { nameArray, newObj }) : this.$emit('resultCodeArray', null)
} else if (arr[0] == 460400499000) {
keyarr.splice(1, 2)
this.getCodeName(nameArray, this.options, 0, keyarr, arr)
// this.getCodeName(nameArray, this.options, 0, keyarr)
keyarr.forEach((ins, index) => {
newObj[ins] = arr[index]
})
arr.length == 2 ? this.$emit('resultCodeArray', { nameArray, newObj }) : this.$emit('resultCodeArray', null)
} else {
// let keys = ['city', 'township', 'village']
//{'cityname':'','townshipname':'', 'villagename':''}
keyarr.splice(1, 1)
this.getCodeName(nameArray, this.options, 0, keyarr, arr)
keyarr.forEach((ins, index) => {
newObj[ins] = arr[index]
})
arr.length == 3 ? this.$emit('resultCodeArray', { nameArray, newObj }) : this.$emit('resultCodeArray', null)
}
},
loadData(selectedOptions) {
const targetOption = selectedOptions[selectedOptions.length - 1]
if (targetOption.hasChild == 0) {
return
}
this.finaly = false
targetOption.loading = true
axios.get('/dict/nhDictXzq/getList', { params: { pcode: targetOption.code } }).then(res => {
targetOption.loading = false
let arr = res.result
arr.forEach(element => {
element.isLeaf = false
})
targetOption.children = arr
this.options = [...this.options]
})
},
getCodeName(nameArray, parent, index, values, arr) {
// console.log('parent',parent)
let node = parent.find(node => {
return node.code == arr[index]
})
if (node) {
nameArray[values[index] + 'name'] = node['name']
if ('children' in node) {
index++
this.getCodeName(nameArray, node.children, index, values, arr)
}
}
},
setDefault() {
// console.log('访问的values', this.values)
// let promiseArr = []
// this.values.forEach(ins => {
// promiseArr.push(
// new Promise((resolve, reject) => {
// axios.get('/dict/nhDictXzq/getList', { params: { pcode: ins } }).then(res => {
// resolve(res.result)
// })
// })
// )
// })
// Promise.all(promiseArr).then(result => {
// console.log('result',JSON.stringify(result))
// // 先把result的数据结果集组装
// let res = []
// this.values.forEach((ins, index) => {
// if (index + 1 < this.values.length) {
// result.forEach((c, index) => {
// let a = c.filter(i => {
// return i.code == this.values[index + 1]
// })
// if (a && a.length > 0) {
// a.isLeaf = false
// a.loading = false
// res.push(a)
// // 合并数组
// res = Array.prototype.concat.apply([], res)
// res = Array.from(new Set(res))
// }
// })
// }
// })
// for (var i = res.length; i > 0; i--) {
// if (res[i]) {
// res[i - 1].children = [res[i]]
// }
// }
// res = res[0]
// res.isLeaf = false
// res.loading = false
// let d = this.options.find(ins => {
// return ins.code == this.values[0]
// })
// d && (d.children = [res])
// // 更新dom
// this.type = !this.type
// })
const p = this.options.find(ins => {
return ins.code == this.values[0]
})
p &&
this.setChildren(p, 1, this.values, () => {
// 更新dom
this.type = !this.type
})
},
setChildren(parent, index, values, cb) {
axios.get('/dict/nhDictXzq/getList', { params: { pcode: parent.code } }).then(res => {
if (res.result.length == 0) {
cb()
return
}
parent.children = res.result
res.result.forEach(ins => {
ins.isLeaf = false
ins.loading = false
if (ins.code === values[index]) {
index++
this.setChildren(ins, index, values, cb)
}
})
})
}
}
}
</script>