此篇文章是对基于element的自定义组件实现前端自定义sql查询条件的优化改良版,优化后版本使用更简单,无需在父组件内创建方法处理数据变动
自定义输入选择框组件 (InputSelect.vue
)
此组件实现了一个自定义的输入选择框,允许用户选择一个条件类型并通过输入框输入对应的值。组件可以被多个实例化,并且所有的实例都会更新同一个数组对象,该数组对象在父组件中通过 v-model
进行双向绑定。
组件功能概述
- 条件选择:通过下拉菜单选择条件类型。
- 值输入:根据选择的条件类型,在输入框中输入对应的值。
- 禁用输入:对于某些条件类型(如
isNull
),输入框会被禁用。 - 动态更新:当条件类型或值发生变化时,自动更新父组件中的数组对象。
组件效果
数据请求格式
组件相关代码
<template>
<div>
<el-input placeholder="请输入"
v-model="columnValue"
class="input-with-select"
clearable
:disabled="disabledType.includes(columnType)"
@input="updateItem">
<el-select slot="prepend"
placeholder="请选择"
style="width: 110px"
clearable
v-model="columnType"
@change="updateItem">
<el-option-group v-for="group in options"
:key="group.label">
<el-option v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-option-group>
</el-select>
</el-input>
</div>
</template>
<script>
/**
* *****使用方法*****
*
* 在父组件中添加引用。columnName为查询的字段,v-model为父组件中绑定的变量
*
* <input-select columnName="status" v-model="queryParams.items" />
*
*/
export default {
name: 'InputSelect',
props: {
value: {
type: Array,
required: true,
default: [],
},
columnName: {
type: String,
required: true,
default: null,
}
},
data() {
return {
options: [
{label: 'e', options: [{value: 'eq', label: '精确匹配'}, {value: 'notEq', label: '不匹配'}]},
{label: 'l', options: [{value: 'like', label: '包含'}, {value: 'notLike', label: '不包含'}]},
{label: 'll', options: [{value: 'left', label: '开头是'}, {value: 'right', label: '结尾是'}]},
{label: 'n', options: [{value: 'isNull', label: '为空'}, {value: 'notNull', label: '不为空'}]},
{label: 'b', options: [{value: 'isBlank', label: '空文本'}, {value: 'notBlank', label: '非空文本'}]}
],
disabledType: ['isNull', 'notNull', 'isBlank', 'notBlank'],
columnType: 'eq',
columnValue: ''
}
},
watch: {
value: {
handler(newItems) {
// 当items发生变化时,更新内部的状态
const foundItem = newItems.find(item => item.columnName === this.columnName);
if (foundItem) {
this.columnType = foundItem.columnType;
this.columnValue = foundItem.columnValue;
} else {
this.columnType = 'eq';
this.columnValue = '';
}
},
deep: true
}
},
methods: {
updateItem() {
if (this.disabledType.includes(this.columnType)) {
this.columnValue = '';
}
const updateItems = [...this.value]
// 更新单个项的信息
const item = {columnName: this.columnName, columnValue: this.columnValue, columnType: this.columnType};
// 移除已存在的相同属性名称的项
const existingItemIndex = updateItems.findIndex(i => i.columnName === this.columnName);
if (existingItemIndex > -1) {
updateItems.splice(existingItemIndex, 1);
}
// 检查是否所有必需的属性都有值
if (item.columnName && item.columnType && (item.columnValue || this.disabledType.includes(item.columnType))) {
// 添加新的项
updateItems.push(item);
}
this.$emit('input', updateItems);
}
}
}
</script>
<style scoped>
</style>
后端逻辑可参考(以mybatis为例)
<if test="query.items!= null and query.items.size() > 0">
<foreach collection="query.items" separator="AND" item="condition">
<choose>
<!-- 精确匹配 -->
<when test="condition.columnType == 'eq'">
AND ${condition.columnName} = #{condition.columnValue}
</when>
<!-- 不匹配 -->
<when test="condition.columnType == 'notEq'">
AND ${condition.columnName} != #{condition.columnValue}
</when>
<!-- 包含 -->
<when test="condition.columnType == 'like'">
AND ${condition.columnName} LIKE CONCAT('%', #{condition.columnValue}, '%')
</when>
<!-- 不包含 -->
<when test="condition.columnType == 'notLike'">
AND ${condition.columnName} NOT LIKE CONCAT('%', #{condition.columnValue}, '%')
</when>
<!-- 开头是 -->
<when test="condition.columnType == 'left'">
AND ${condition.columnName} LIKE CONCAT(#{condition.columnValue}, '%')
</when>
<!-- 结尾是 -->
<when test="condition.columnType == 'right'">
AND ${condition.columnName} LIKE CONCAT('%', #{condition.columnValue})
</when>
<!-- 为空 -->
<when test="condition.columnType == 'isNull'">
AND ${condition.columnName} IS NULL
</when>
<!-- 不为空 -->
<when test="condition.columnType == 'notNull'">
AND ${condition.columnName} IS NOT NULL
</when>
<!-- 空文本 -->
<when test="condition.columnType == 'isBlank'">
AND (${condition.columnName} = '' OR ${columnName} IS NULL)
</when>
<!-- 非空文本 -->
<when test="condition.columnType == 'notBlank'">
AND (${condition.columnName} != '' AND ${columnName} IS NOT NULL)
</when>
<!-- 其他情况 -->
<otherwise>
<!-- 处理未匹配到的情况,通常为空或报错 -->
</otherwise>
</choose>
</foreach>
</if>
HTML 结构
- el-input:主输入框,用于输入值。
- el-select:条件选择器,用于选择条件类型。
- el-option-group 和 el-option:用于组织和展示条件选项。
<div>
<el-input placeholder="请输入"
v-model="columnValue"
class="input-with-select"
clearable
:disabled="disabledType.includes(columnType)"
@input="updateItem">
<el-select slot="prepend"
placeholder="请选择"
style="width: 110px"
clearable
v-model="columnType"
@change="updateItem">
<el-option-group v-for="group in options"
:key="group.label">
<el-option v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-option-group>
</el-select>
</el-input>
</div>
JavaScript 逻辑
- props:接收父组件传入的数组
value
和列名columnName
。 - data:存储组件内部状态,包括条件类型、值以及条件选项。
- watch:监听
value
的变化,更新组件内部状态。 - methods:
- updateItem:当条件类型或值发生变化时,更新父组件中的数组对象。
export default {
name: 'InputSelect',
props: {
value: {
type: Array,
required: true,
default: [],
},
columnName: {
type: String,
required: true,
default: null,
}
},
data() {
return {
options: [
{label: 'e', options: [{value: 'eq', label: '精确匹配'}, {value: 'notEq', label: '不匹配'}]},
{label: 'l', options: [{value: 'like', label: '包含'}, {value: 'notLike', label: '不包含'}]},
{label: 'll', options: [{value: 'left', label: '开头是'}, {value: 'right', label: '结尾是'}]},
{label: 'n', options: [{value: 'isNull', label: '为空'}, {value: 'notNull', label: '不为空'}]},
{label: 'b', options: [{value: 'isBlank', label: '空文本'}, {value: 'notBlank', label: '非空文本'}]}
],
disabledType: ['isNull', 'notNull', 'isBlank', 'notBlank'],
columnType: 'eq',
columnValue: ''
}
},
watch: {
value: {
handler(newItems) {
// 当items发生变化时,更新内部的状态
const foundItem = newItems.find(item => item.columnName === this.columnName);
if (foundItem) {
this.columnType = foundItem.columnType;
this.columnValue = foundItem.columnValue;
} else {
this.columnType = 'eq';
this.columnValue = '';
}
},
deep: true
}
},
methods: {
updateItem() {
if (this.disabledType.includes(this.columnType)) {
this.columnValue = '';
}
const updateItems = [...this.value]
// 更新单个项的信息
const item = {columnName: this.columnName, columnValue: this.columnValue, columnType: this.columnType};
// 移除已存在的相同属性名称的项
const existingItemIndex = updateItems.findIndex(i => i.columnName === this.columnName);
if (existingItemIndex > -1) {
updateItems.splice(existingItemIndex, 1);
}
// 检查是否所有必需的属性都有值
if (item.columnName && item.columnType && (item.columnValue || this.disabledType.includes(item.columnType))) {
// 添加新的项
updateItems.push(item);
}
this.$emit('input', updateItems);
}
}
}
使用示例
在父组件中,你可以像下面这样使用这个组件:
假设我们需要一个用于搜索备注和状态字段的输入选择组件,我们可以这样使用:
<el-form :model="queryParams" ref="queryForm" v-show="showSearch" inline>
<el-form-item label="备注" prop="items">
<input-select columnName="remark" v-model="queryParams.items"/>
</el-form-item>
<el-form-item label="状态" prop="items">
<input-select columnName="status" v-model="queryParams.items" />
</el-form-item>
<el-form-item class="queryBtn">
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
总结
这个组件允许用户通过选择条件类型和输入值来构建查询条件,并且可以被多个实例化,所有的实例都会更新同一个数组对象。这使得在前端构建复杂的查询条件变得更加简单和直观。