需求
vue-treeselect 校验及清除校验 这篇介绍了用 @input
在校验失败时显示校验信息。但还需要同时显示红色边框。如下图所示:
解决办法
思路:动态绑定类名,校验失败时切换类名,更改边框颜色。
子组件 SelectTree
二次封装 vue-treeselect:组件 SelectTree
<template>
<div class="select-tree">
<treeselect
:class="{ error: isError === true }"
appendToBody
z-index="9000"
:flat="isFlat"
:multiple="isMultiple"
:noOptionsText="noOptionsText"
noResultsText="未搜索到匹配项"
:value-consists-of="valueConsistsOf"
:limit="limitNum"
:limitText="(count) => `+${count}`"
v-model="modelValue"
@input="performValidate"
:options="optionTree"
:show-count="showCount"
:normalizer="normalizerNode"
:placeholder="placeText ? placeText : `请选择上级${nodeLabel}(可搜索)`"
:disabled="isDisabled"
:disable-branch-nodes="disBranchNodes"
:clearable="showClearable"
>
<label
slot="option-label"
slot-scope="{ node, shouldShowCount, labelClassName, countClassName }"
:class="labelClassName"
>
<!-- 可自定义选项内容 -->
<slot
name="diy-option"
v-bind="{ node, shouldShowCount, countClassName }"
>
{{ node.label }}
<span v-if="shouldShowCount" :class="countClassName"
>({{ node.children.length }})</span
>
</slot>
</label>
<div slot="value-label" slot-scope="{ node }">
<!-- 可自定义值标签 -->
<slot name="diy-value" v-bind="{ node }">
{{ node.label }}
</slot>
</div>
</treeselect>
</div>
</template>
<script>
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
name: "SelectTree",
components: {
Treeselect,
},
computed: {
modelValue: {
get() {
// 若选项中没有为 0 的 id,则自动把 modelValue 置为 null
const flag = this.optionTree.find(
(ele) => ele[this.exchangeNode.id] === 0
);
if (!flag && this.modelVal === 0) {
return null;
} else {
return this.modelVal;
}
},
set(val) {
this.$emit("return-model", val);
return val;
},
},
},
props: {
// 校验失败:默认成功
isError: {
type: Boolean,
default: false,
},
appendToBody: {
type: Boolean,
default: false,
},
// 限制所选选项的显示
limitNum: {
type: Number,
default: 5,
},
// 防止价值组合:对于非固定和多选模式,如果选中了分支节点及其所有后代,则vue-treeselect会将它们组合到值数组中的单个项目中
valueConsistsOf: {
type: String,
default: "BRANCH_PRIORITY",
},
// 是否开启多选
isMultiple: {
type: Boolean,
default: false,
},
// 是否开启平面模式(父子节点不关联)
isFlat: {
type: Boolean,
default: false,
},
// 无选项内容时的提示文本
noOptionsText: {
type: String,
default: "暂无数据",
},
// 下拉框绑定值
modelVal: {
type: [String, Number, Array, null],
default: null,
},
// 是否显示清空按钮
showClearable: {
type: Boolean,
default: true,
},
// 自定义提示
placeText: {
type: [String, null],
default: null,
},
// 是否禁止选择有子节点的选项
disBranchNodes: {
type: Boolean,
default: false,
},
// 节点名称
nodeLabel: {
type: String,
default: "节点",
},
// 是否显示计数
showCount: {
type: Boolean,
default: true,
},
// 转换节点结构
exchangeNode: {
type: Object,
default: () => {
return {
id: "id",
label: "label",
children: "children",
};
},
},
// 是否禁用
isDisabled: {
type: Boolean,
default: false,
},
// 选项树:用于上级节点选项中选择
optionTree: {
type: Array,
default: () => {
let tree = [
{
id: 0,
label: "全部节点",
children: [],
},
];
tree[0].children = [];
return tree;
},
},
},
methods: {
// 进行校验
performValidate() {
this.$emit("perform-validate");
},
/* 转换节点数据结构 */
normalizerNode(node) {
if (node.children && !node.children.length) {
delete node.children;
}
let res = {
id: node[this.exchangeNode.id],
label: node[this.exchangeNode.label],
children: node[this.exchangeNode.children],
};
if (node[this.exchangeNode.isDisabled]) {
res["isDisabled"] = node[this.exchangeNode.isDisabled];
}
return res;
},
},
};
</script>
<style lang="scss" scoped>
// 调整 vue-treeselect 禁用样式
::v-deep .vue-treeselect.vue-treeselect--disabled .vue-treeselect__control {
background-color: #f5f7fa;
border-color: #dfe4ed;
cursor: not-allowed;
.vue-treeselect__single-value {
color: #c0c4cc;
}
}
// 校验不通过时,输入框边框变红
::v-deep .vue-treeselect.error .vue-treeselect__control {
border-color: #ff4949;
}
</style>
在 SelectTree
组件中,使用 :class="{ error: isError === true }"
动态绑定类名 error
,设置有此类名时,边框变红:
// 校验不通过时,输入框边框变红
::v-deep .vue-treeselect.error .vue-treeselect__control {
border-color: #ff4949;
}
父组件中使用
思考
以上这种传递变量更改动态类名的方式,虽然可以实现需求,但是有以下缺点:
- 组件耦合性太强,即使用时需要在多处书写代码;
- 若页面中有多处使用了子组件 SelectTree,某一处的校验会干扰其他处。
因此,此种方式不可取,需要进一步优化。