首先给大家看看效果
看到这个原型之后,再去看数据结构就容易懂了。
[{
"key": "AND", (且、或)
"type": "group",(条件组)
"children": [{
"type": "condition",
"children": [],
"indexCode": "2",
"indexName": "",
"value": "123",
"tagsAll": [],
"operator": "5",
"del": false
}, {
"type": "condition",
"children": [],
"indexCode": "",
"indexName": "",
"value": "",
"tagsAll": [],
"operator": "",
"del": false
}, {
"key": "AND",
"type": "group",
"children": [{
"type": "condition",
"children": [],
"indexCode": "3",
"indexName": "",
"value": "111",
"tagsAll": [],
"operator": "4",
"del": false
}],
"del": false
}, {
"type": "condition",
"children": [],
"indexCode": "4",
"indexName": "",
"value": "0",
"tagsAll": [],
"operator": "1",
"del": false
}],
"del": false
}]
之前想的过于复杂,导致浪费了很多时间。话不多说上代码:
<template>
<div class="filter-condition">
<el-tree
:data="ruleSetInfos"
default-expand-all
:expand-on-click-node="false"
>
<span
class="custom-tree-node display"
style="width: 100%"
slot-scope="{ node, data }"
@mouseenter="mouseenter(data)"
@mouseleave="mouseleave(data)"
>
<!-- 条件组 -->
<div class="display" v-if="data.type === commandTypeEnum.GROUP">
<el-button
class="round"
type="success"
round
size="mini"
@click="updateRuleKey(data)"
>
{{ ruleNameItem[data.key] }}
</el-button>
<el-dropdown
trigger="click"
size="mini"
@command="addRuleItem($event, data, node)"
>
<span class="el-dropdown-link">
<i class="el-icon-circle-plus-outline add-rules-icon"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="commandTypeEnum.CONDITION"
>条件</el-dropdown-item
>
<el-dropdown-item :command="commandTypeEnum.GROUP"
>条件组</el-dropdown-item
>
</el-dropdown-menu>
</el-dropdown>
</div>
<!-- 条件 -->
<div
class="displaydisplay"
v-if="data.type === commandTypeEnum.CONDITION"
>
{{ node.data.indexName }}
<el-select v-model="node.data.indexCode" size="mini" placeholder="选择字段" class="simple-type-select">
<el-option v-for="item in timeFil" :key="item.id" :label="item.modelName" :value="item.id">
</el-option>
</el-select>
</div>
<!-- 运算符 -->
<div
class="displaydisplay"
v-if="data.type === commandTypeEnum.CONDITION"
>
<el-select v-model="node.data.operator" size="mini" placeholder="选择比较符" class="simple-type-select">
<el-option v-for="item in OperationList" :key="item.value" :label="item.name" :value="item.value">
</el-option>
</el-select>
</div>
<!-- 输入框(属于、不属于) -->
<div
class="displaydisplay"
v-if="data.type === commandTypeEnum.CONDITION && (node.data.operator == 'IN' || node.data.operator == 'NOT IN' )"
>
<!-- 父盒子 -->
<div class="father_box" @click="onclick">
<!-- 生成的标签 -->
<div v-for="(item, index) in node.data.TagsAll" :key="index" class="spanbox">
<span class="tagspan">{{ item }}</span>
<i class="span_close" @click="removeTag(index, item, node.data)"></i>
</div>
<!-- 输入框
@keyup.delete="deleteTags"-->
<input
placeholder="输入后按<回车>创建"
v-model="node.data.value"
@keyup.enter="addTags(node)"
:style="inputStyle"
class="inputTag"
ref="inputTag"
type="text"
/>
</div>
</div>
<!-- 输入框 -->
<div
class="displaydisplay"
v-if="data.type === commandTypeEnum.CONDITION && (node.data.operator !== 'IN' && node.data.operator !== 'NOT IN' )"
>
<el-input v-model="node.data.value" size="small"></el-input>
</div>
<!-- 删除图标 -->
<i
v-show="data.del && node.parent.level !== 0"
class="el-icon-delete-solid delete-icon"
@click="remove(node, data)"
>
</i>
</span>
</el-tree>
</div>
</template>
<script>
import {
commandTypeEnum,
ruleNameItem,
ruleNameTypeEnum,
OperationList,
} from "../processjos/model";
export default {
name: "filterCondition",
props: {
timeFil: {
type: Array,
default: null,
},
},
data() {
return {
flag: [],
//输入框宽度
inputLength: "",
// 树节点数据
ruleSetInfos: [{
key: "AND", // 且/或 AND/OR
type: "group", // 条件/条件组 group/condition
children: [],
}],
// 筛选条件
ruleNameItem: ruleNameItem,
// 筛选条件常量
ruleNameTypeEnum: ruleNameTypeEnum,
// 条件/条件组常量
commandTypeEnum: commandTypeEnum,
OperationList: OperationList,
};
},
watch: {
//监听输入的值越多,输入框越长
currentval(val) {
// 实时改变input输入框宽度,防止输入内容超出input默认宽度显示不全
this.inputLength = this.$refs.inputTag.value.length * 12 + 50;
},
},
computed: {
//计算属性:计算出动态输入框宽度
inputStyle() {
let style = {};
style.width = `${this.inputLength}px`;
return style;
},
},
methods: {
//删除tag
removeTag(index, item, node) {
node.TagsAll.splice(index, 1);
},
//回车增加tag
addTags(node) {
if (node.data.value) {
//添加tag
node.data.TagsAll.push(node.data.value);
//清空输入框
node.data.value = "";
}
},
//点击父盒子输入框获取焦点
onclick() {
// console.log(2345);
},
/**
* 父组件调用
*/
clickQ() {
this.ruleFlag(this.ruleSetInfos)
return this.ruleSetInfos
},
/**
* 校验过滤条件非空
*/
ruleFlag(val) {
if (val) {
// 每一层添加 flag
for (let index = 0; index < val.length; index++) {
if (val[index].type == 'group') {
val[index].children && val[index].children.forEach(item => {
if (item.type == 'condition') {
if (!item.indexCode || !item.operator || ((!item.value && item.TagsAll.length < 1))) {
this.$message.warning('将条件输入完整')
this.flag.push(false)
return false
} else {
this.flag.push(true)
}
} else {
if (item.type == 'group') {
item.children && item.children.forEach(el => {
if (el.type == 'condition') {
if (!el.indexCode || !el.operator || (!el.value && el.TagsAll.length < 1)) {
this.$message.warning('将条件输入完整')
this.flag.push(false)
return false
} else {
this.flag.push(true)
}
}
})
}
}
});
}
}
}
},
/**
* 修改规则key
* @param index 数组的某一项
*/
updateRuleKey(item) {
if (item.key === this.ruleNameTypeEnum.AND) {
item.key = this.ruleNameTypeEnum.OR;
return;
}
item.key = this.ruleNameTypeEnum.AND;
},
/**
* 添加规则
* @param type 添加规则的类型
* @param data 点击的当前数据
*/
addRuleItem(type, data, node) {
// 限制添加层级
if (node.level == 3) {
this.$message.warning('只可添加三层')
return
}
// 添加条件组
let ruleItem = { key: 'AND', type: 'group', children: [] };
if (type === this.commandTypeEnum.CONDITION) {
ruleItem = { type: 'condition', children: [], indexCode: '', indexName: '', value: '', operator: '=', TagsAll: [] };
}
this.append(data, ruleItem);
},
/**
* 在树中添加数据
* @param data 当前数据
* @param ruleItem 添加的数据
*/
append(data, ruleItem) {
if (!data.children) {
this.$set(data, "children", []);
}
data.children.push(ruleItem);
},
/**
* 删除节点
*/
remove(node, data) {
const parent = node.parent;
const children = parent.data.children ? parent.data.children : parent.data;
const index = children.findIndex((d) => d.$treeNodeId === data.$treeNodeId);
children.splice(index, 1);
},
/**
* 选择字段值
*/
handleCommand(value, data, node) {
node.data.indexCode = value;
node.data.indexName = timeFil.find(item => item.value === value).name;
},
/**
* 运算符
*/
commandOperator(command) {
node.data.OperationVal = OperationList.find(item => item.value === command).name;
},
/**
* 清除数据
*/
clearValue() {
this.ruleSetInfos = [{ key: 'AND', type: 'group', children: [] }]
},
},
};
</script>
<style lang="scss">
.filter-condition {
margin-left: 50px;
.displaydisplay {
// margin-top: 10px;
// margin-bottom: 10px;
margin-left: 15px;
}
.el-tree-node__content {
margin-bottom: 10px;
height: 34px !important;
padding-left: 3px !important;
}
.add-rules-icon {
color: #ff8e18;
margin-left: 6px;
margin-top: 5px;
font-size: 14px;
}
.display {
display: flex;
height: 100%;
align-items: center;
}
.round {
padding: 5px !important;
}
.el-dropdown-link {
color: #409eff;
font-size: 13px;
}
::v-deep.el-tree > .el-tree-node:after {
border-top: none;
}
.el-tree-node {
position: relative;
padding-left: 16px;
}
.el-tree-node__expand-icon.is-leaf {
display: none;
}
.el-tree-node__children {
padding-left: 16px;
}
.el-tree-node :last-child:before {
height: 38px;
}
.el-tree > .el-tree-node:before {
border-left: none;
}
.el-tree > .el-tree-node:after {
border-top: none;
}
.el-tree-node:before {
content: "";
left: -4px;
position: absolute;
right: auto;
border-width: 1px;
}
.el-tree-node:after {
content: "";
left: -4px;
position: absolute;
right: auto;
border-width: 1px;
}
.el-tree-node:before {
border-left: 2px dashed #ddd;
bottom: 0px;
height: 100%;
top: -26px;
width: 1px;
}
.el-tree-node:after {
border-top: 2px dashed #ddd;
height: 20px;
top: 16px;
width: 20px;
}
.delete-icon {
color: #f56c6c;
margin-top: 3px;
margin-left: 12px;
}
.simple-type-select .el-input__inner {
width: 105px;
font-size: 12px;
color: rgb(93, 160, 210);
position: relative;
bottom: 1px;
border: none;
background: unset;
}
.el-tag {
margin-right: 5px;
}
}
</style>
<style scoped>
/* 外层div */
.father_box {
/* width: 600px; */
box-sizing: border-box;
background-color: white;
border: 1px solid #409EFF;
border-radius: 4px;
font-size: 12px;
text-align: left;
padding-left: 5px;
word-wrap: break-word;
overflow: hidden;
}
/* 标签 */
.spanbox {
display: inline-block;
font-size: 14px;
margin: 3px 4px 3px 0;
background-color: #ecf5ff;
border: 1px solid #e8eaec;
border-radius: 3px;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04)
}
/* 标签文字 */
.tagspan {
height: 24px;
line-height: 22px;
max-width: 99%;
position: relative;
display: inline-block;
padding-left: 8px;
color: #409EFF;
font-size: 14px;
opacity: 1;
vertical-align: middle;
overflow: hidden;
transition: 0.25s linear;
}
/* tag的叉叉 */
.span_close {
padding: 0 6px 0 4px;
opacity: 1;
-webkit-filter: none;
filter: none;
color: #409EFF;
/* font-weight: 600; */
cursor:pointer;
}
/* 鼠标经过叉叉 */
.span_close:hover{
background-color: #409EFF;
border-radius: 50%;
color: #fff;
}
.span_close:after {
content: "\00D7";
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* line-height: 27px; */
transition: 0.3s, color 0s;
}
/* input */
.inputTag {
margin-right: 50px;
font-size: 16px;
border: none;
box-shadow: none;
outline: none;
background-color: transparent;
padding: 0;
width: auto;
min-width: 150px;
vertical-align: top;
height: 32px;
color: #495060;
line-height: 32px;
}
/* 输入框提示文字大小 */
input:placeholder-shown {
font-size: 0.6rem;
}
</style>
非空校验可以写成轮询,我这里只有三层就没有修改,有兴趣的朋友可以修改下。
另外如果你不止一个地方使用了el-tree,要修改样式且只在当前页面有效,直接scoped是不行的,最好是在外层加一层div,使用scss。
js代码
// 筛选条件
export const ruleNameItem = {
'AND': '且',
'OR': '或'
}
// 筛选条件常量
export const ruleNameTypeEnum = {
AND: 'AND',
OR: 'OR'
}
// 条件/条件组常量
export const commandTypeEnum ={
GROUP: 'group',
CONDITION: 'condition'
}
/**
* 运算符
*/
export const OperationList = [
{name: '等于', value: '='},
{name: '不等于', value: '<>'},
{name: '包含', value: 'LIKE'},
{name: '不包含', value: 'NOT LIKE'},
{name: '属于', value: 'IN'},
{name: '不属于', value: 'NOT IN'},
]
写的不好,有什么问题,欢迎指出~