需求
帮助不会写sql且或关系逻辑查询的用户,借助前端的且或过滤条件的交互,来实现类似后端的sql语句查询的功能。
前端数据结构分析
整个结构是一个条件数组,如下图是每一个条件的具体内容,即红色部分,分别为:event 、prop、condition、relation(且/或),condition为当前条件的核心,是这个过滤条件的细化,condition为一个条件数组,子数组的数据格式可以为A,也可以为B,也可以为任意个A和任意B的组合,其中组成B的最小单元又是A。
综上:可以得出如下的数据结构:
typescript
export interface IFilterData {
event: string // 事件
prop: string // 属性/指标
relation: string // condition的关系且/或
condition: (IConditionA | IConditionB)[]
}
export interface IConditionA {
prop: string // 属性/标签
operator: string //操作:等于、不等于、包含等
value: string // 值
}
export interface IConditionB {
relation: string // condition的关系且/或
condition?: IConditionA[]
}
组件拆分
1. 组件 1 (base-event-filter.vue)
红色部分为for循环一个过滤条件,蓝色部分为添加按钮,因此我们把红色拆为一个组件。整体HTML结构如下:
xml
<div class="base-event-filter">
<!-- 红色部分 -->
<ItemFilter :data="filterData" />
<!-- 蓝色部分 -->
<span class="add_icon" @click="handleAdd">
添加指标
</span>
</div>
typescript
<script lang="ts" setup>
import ItemFilter from './item-filter.vue'
interface IFilterData {
event: string // 事件
prop: string // 属性/指标
relation: string // condition的关系且/或
condition: (IConditionA | IConditionB)[]
}
type Props = {
modelValue: IFilterData[]
}
const props = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const filterData = ref(props.modelValue ?? [])
const handleAdd = () => {
filterData.value.push({ event: '',prop:'', condition: [], relation: '且' })
}
watch(filterData, (val) => {
emit('update:modelValue', val)
})
</script>
2. 组件拆分------ItemFilter.vue
仍重点讲红色部分,红色部分为我们数据结构的relation以及condition,relation比较好处理,就是一个css样式,点击用于切换条件直接的关系,核心是右侧的结构。
图片交互的数据结构可以描述如下:
csharp
{
event:'',
prop:'',
relation:'且',
condition:[
// IConditionB
{
relation:'或',
condition:[
{
prop:'',
operator:'',
value:''
},
{
prop:'',
operator:'',
value:''
}
]
},
// IConditionA
{
prop:'',
operator:'',
value:''
}
]
}
我们把整体的红色部分拆为一个组件,因为这个组件还做了组件嵌套,也就是组件递归,我们把这个组件称为item-filter-child.vue
。
3. 核心组件------item-filter-child.vue
HTML结构:
xml
<div class="item-filter-child">
<!-- relation -->
<div v-if="currentData.condition?.length > 1" class="relation">
<span>且</spa >
<span>或</span>
</div>
<!-- 过滤条件 -->
<div>
<div
v-for="(condition, conditionIndex) in currentData.condition"
:key="conditionIndex"
class="child-item"
>
<!-- IConditionA -->
<template v-if="!condition.condition?.length || condition.condition.length === 1">
<!-- prop、operator、value对应的HTML -->
</template>
<!-- IConditionB 递归组件本身 -->
<ItemFilterChild v-else :data="condition"></ItemFilterChild>
</div>
</div>
</div>
总结
到此整个组件就完成了,本文主要讲述组件的实现方法、组件的拆分逻辑,以及递归组件的实现,希望能帮助到你。