不知道怎么描述,反正是折腾了大半天,前端也不是很精通,看一下文档,如果有人遇到和我一样的问题,希望能给到一点启发吧
刚开始的需求描述
最近项目上需要使用级联选择器,绑定组织机构,组件需要满足以下要求:
- 在编辑详情时只能进行单选,在单选情况下,可以选择任何一级,如下图所示
- 在列表查询时支持多选,在 多选情况下,选择父节点可以全选子节点,父子节点一起作为参数过滤查询列表,如下图所示
以上功能看el-cascader的官方说明文档,是能直接实现的,上一部分代码:
1)封装组件部分
<el-cascader
:options="information"
ref="cascaderAddr"
v-model="dwCodes"
:show-all-levels="false"
:props="propsOpyions"
@change="handleChange"
collapse-tags
collapse-tags-tooltip
clearable
filterable
:disabled="disabled"
>
</el-cascader>
export default {
props: {
value: {
type: null
},
// 是否禁用
disabled: {
type: Boolean,
default: false,
required: false
},
// 是否多选
multiple: {
type: Boolean,
default: false,
required: false
}
},
data() {
return {
dwCodes: [], //回显绑定的值
information: [], // 选择器数据源
propsOpyions: {
multiple: true, // 是否允许多选
emitPath: false, // 是否展示完整路径
checkStrictly: true, // 是否可选择任意一级
expandTrigger: 'click' // 展开事件
}
};
},
created() {
this.init()
this.propsOpyions.multiple = this.multiple
// 多选时,若checkStrictly为true,则选择父节点时,只能选到父节点
// 不能全选它下面的所有子节点,所以需要设置为false
if(this.multiple) {
this.$set(this.propsOpyions, 'checkStrictly', false)
}
},
watch: {
value: {
// 数据回显,很重要
handler(newVal, oldVal) {
this.dwCodes = newVal
if(this.multiple && this.dwCodes) {
this.dwCodes = this.dwCodes.split(',')
}
},
immediate: false,
deep: false
}
},
methods: {
async init() {
// 初始化数据源
},
// 值改变
handleChange(checked) {
if(this.multiple && checked) {
let checknodes = this.$refs['cascaderAddr'].getCheckedNodes()
let checkNodeids = checknodes.map((obj,index) => {
return obj.value
});
this.$emit("input", checkNodeids.join());
} else {
this.$emit("input", checked);
}
}
}
}
2)引入组件部分
// 单选时
<dw-Cascader :multiple="false" v-model="gldw" :disabled="disabledFlag">
</dw-Cascader>
// 多选时
<dw-Cascader :multiple="true" v-model="gldw" ></dw-Cascader>
问题来了
- 在允许多选时,子节点全部取消的情况下,父节点也会取消选中状态,但是目前的需求是,有些数据是直接归属到父节点单位下面的,如果只想查父节点下的数据,是实现不了的。(checkStrictly为true,虽然可以选到父节点,但是这个时候父节点勾选,子节点全选就不起作用了)
- 在搜索时,只能检索到最后一级,同样也是受checkStrictly为true的限制,在用户在搜索父级节点时是不显示的,只能搜索最后一级
搜索【长沙市】结果,只显示了子节点
解决
所以在保持原有需求不变的情况下,在面向百度查询了一番,结合互联网好人提供的思路(已经找不到出处了,搜一搜应该也能找到),解决上面说的两个问题。
第一步:在多选情况下,也设置checkStrictly为true,先保证父节点能单独勾选;
created() {
this.init()
this.propsOpyions.multiple = this.multiple
// if(this.multiple) {
// this.$set(this.propsOpyions, 'checkStrictly', false)
// }
},
第二步,监听级联选择框绑定值的变化,这个示例中v-model绑定的是dwcodes,就监听这个值,实现全选和取消全选,取消子节点勾选,父节点保留勾选状态
watch: {
value: {
handler(newVal, oldVal) {
this.dwCodes = newVal
if(this.multiple && this.dwCodes) {
this.dwCodes = this.dwCodes.split(',')
}
},
immediate: false,
deep: false
},
dwCodes: {
handler(newVal, oldVal) {
this.dwCodes = newVal
if(this.multiple && this.dwCodes) {
// 判断当前是添加勾选还是取消勾选,并获取当前操作的节点值
const current = this.findCurrentDepartment(newVal, oldVal)
if (!current) return
this.$nextTick(() => {
if (current.type === 'checked') {
// 级联选择器当前勾选的节点数组
this.currentCheckedNodes = this.$refs.cascaderAddr.checkedNodes
// 从级联选择器中取出内部的节点数组,找到当前节点
const targetNode = this.$refs.cascaderAddr.checkedNodes.find((item) => {
return item.value === current.value
})
if (targetNode) {
// 递归找出所有子孙节点,并手动勾选
this.checkedChildrenDepartment([targetNode])
// 更新视图
this.$refs.cascaderAddr.$refs.panel.calculateMultiCheckedValue()
}
} else if (current.type === "cancel") {
// 从当前勾选的节点数组中递归找出所有子孙节点,并取消勾选
this.cancelChildrenDepartment(current.value, this.currentCheckedNodes)
// 更新视图
this.$refs.cascaderAddr.$refs.panel.calculateMultiCheckedValue()
}
})
}
},
immediate: false,
deep: true
}
},
methods: {
async init() {
// 初始化数据源
},
// 值改变
handleChange(checked) {
if(this.multiple && checked) {
let checknodes = this.$refs['cascaderAddr'].getCheckedNodes()
let checkNodeids = checknodes.map((obj,index) => {
return obj.value
});
// 回传给父组件的值
this.$emit("input", checkNodeids.join());
} else {
this.$emit("input", checked);
}
},
checkedChildrenDepartment(list = []) {
list.forEach((item) => {
item.doCheck(true)
// 勾选选中当前值,若有子节点,子节点遍历全选
if (item.children.length > 0) {
this.checkedChildrenDepartment(item.children)
}
})
},
cancelChildrenDepartment(cancelNodeValue, list = []) {
list.forEach((item) => {
// 取消勾选时,取消当前值,若当前值有子节点时,实现取消父节点勾选,子节点取消全选的功能,同时实现子节点取消勾选,不会影响父节点勾选状态
if(cancelNodeValue == item.value || cancelNodeValue == item.parent?.value) {
item.doCheck(false)
if (item.children.length > 0) {
// 遍历
this.cancelChildrenDepartment(item.value, item.children)
}
}
})
},
findCurrentDepartment(newArr, oldArr) {
const catchNewArr = [...newArr]
const catchOldArr = [...oldArr]
if (catchNewArr.length > catchOldArr.length) {
for (let i = 0; i < catchNewArr.length; i++) {
const targetIndex = catchOldArr.indexOf(catchNewArr[i])
if (targetIndex === -1) {
return {
value: catchNewArr[i],
type: 'checked',
}
}
}
} else {
for (let i = 0; i < catchOldArr.length; i++) {
const targetIndex = catchNewArr.indexOf(catchOldArr[i])
if (targetIndex === -1) {
return {
value: catchOldArr[i],
type: 'cancel',
}
}
}
}
}
}
实现效果
1.有全选功能
取消子节点选择,父节点保持选择状态
2.搜索能检索到父节点
3.绑定数据回显
其他
不知道还有没有其他遗漏的地方,反正是实现了,前端开发忙的马不停蹄,一个后端开发又又又在前端挣扎了一下,再次感谢互联网好人们!
我翻了一下历史记录: 参考代码