element plus封装动态生成的搜索框,带校验

本文介绍了如何使用Vue的el-form组件结合动态生成的搜索输入项,包括el-input和el-radio,并强调了初始化值设置、ref引用以及form的prop和验证规则的正确应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用el-form封装动态生成的搜索框,带校验

效果如下
在这里插入图片描述

el-form 结合 el-input,el-radio…常见搜索框

重点:el-form需要初始化值,form需要定义对应的ref,初次加载需要清空validate。prop,form的值需对应。
//使用
import searchComponent from "@/[组件的位置]";

 <searchComponent            :searchItems="Object.values(searchItems)"
                             :defaultSpan="24"
                             :form-config="{labelPosition:'left'}"
                             @handleSelect="handleSelect"
                             ref="searchComponentRef"
                             class="searchComponent"/>
 //searchItems定义为对象,方便后续获取和修改值           
const searchItems = ref({
[自己定义的key,很重要]: {
            //外面表单层的组件
            type: 'select',  //控件类型
            labelName: '',
            key: '',    //[自己定义的key,需要在el-form和获取搜索框值时都会使用到
            rules: [],  //el-form中的rules
            config: {   //传到组件最里边的配置数据
                key: 'TrainingDegree',
                options: radioArray,
                value:''  //初始值
            }
        }})
/**fake data*/
const searchItems = ref({
StaffTeam: {
                    type: 'select',
                    labelName: 'Staff Team',
                    key: 'StaffTeam',
                    rules: [{
                        required: true,
                        message: 'please select Staff Team',
                        trigger: 'change'
                    }],
                    config: {
                        key: 'StaffTeam',
                        options: [],
                        cascader: 'Skill',
                        disabled: false,
                        multiple: true,
                    }
                }})
        
/**
获取搜索条件的值的方法**/
 const handleSelect = ({val, key}) => {
            //searchItems中自己定义的key
            searchValue.value[key] = val
            // setTrainingInfor(searchValue.value)
        }

searchComponent 的 index.vue文件

<template>
    <el-form
            ref="ruleFormRef"
            :model="form"
            :rules="rules"
            :label-width="formConfig.labelWidth || 'auto'"
            :label-position="formConfig.labelPosition || 'right'"
            class="demo-ruleForm searchComponent"
            status-icon>
        <el-col v-for="(item,index) in searchItems"
                :key="index"
                :span="!!item.defaultSpan ? item.defaultSpan : defaultSpan"
                :style="item.style"
                class="flex-row">
            <el-form-item :label="item.labelName" class="labelName"
                          :prop="item.key">
                <component :is="item.type"
                           :config="item.config"
                           @handleSelect="handleSelect"/>
            </el-form-item>
        </el-col>
    </el-form>
</template>

<script lang="ts">
import select from "../searchComponent/component/select.vue"
import input from "../searchComponent/component/input.vue"
import button from "../searchComponent/component/button.vue"
import daterange from "../searchComponent/component/daterange.vue"
import radio from "../searchComponent/component/radio.vue"
import inputNumber from "../searchComponent/component/inputNumber.vue"
import {nextTick, onMounted, reactive, ref,onUpdated, watch} from "vue";

interface SearchItems {
    type: string,  //控件类型
    labelName: string,  //控件显示名称
    defaultSpan?: number,
    style?: string,   //控件特殊的css
}

export default {
    name: "index",
    components: {select, input, button, daterange, radio, inputNumber},
    props: {
        defaultSpan: {  //默认占的格子数
            type: Number,
            default: 6
        },
        searchItems: { //生成的搜索框基本配置
            type: Array,
            default: () => ([])
        },
        formConfig: {  //表格的基本配置,遵循原本el-form的参数
            type: Object,
            default: () => ({
                labelWidth: '',
                labelPosition: 'right',
            })
        },
    },
    emits: ['btnClick', 'handleSelect'],
    setup(props: any, {emit}: any) {
        const ruleFormRef = ref()
        const rules = ref({})
        const form = ref({})

        onMounted(() => {
            props.searchItems?.map((v: any) => {
                rules.value[v.key] = v.rules
            })
        })
        onUpdated(()=>{
          /**
             解决首次空值自动校验的问题
            **/
            ruleFormRef.value.clearValidate()
            ruleFormRef.value.resetFields()
        })
        const btnClick = (data: any) => {
            emit('btnClick', data)
        }
        const handleSelect = (data: any) => {
            form.value[data.key] = data.val
            /**
             解决改变值后,不自动校验的问题
            **/
            ruleFormRef.value.validateField(data.key)
            emit('handleSelect', data)
        }
        return {
            btnClick,
            handleSelect,
            ruleFormRef,
            rules,
            form,
        }
    }
}
</script>

<style scoped lang="scss">
.flex-row {
  display: flex;
  justify-content: flex-start;
  align-items: center;
}

.searchComponent {
  flex-wrap: wrap !important;
  display: flex;

}

.searchComponent > > > label.el-form-item__label {
  display: flex;
  align-items: center;
  justify-content: flex-end !important;
}

.searchComponent > > > .el-form-item__content {
  width: inherit;
}
</style>

以select组件为例子

<template>
    <el-select v-model="val"
               clearable
               filterable
               @change="handleSelect"
               style=" width: inherit;"
               :allow-create="config.allowCreate"
               :collapse-tags="config.multiple"
               :placeholder="config.placeholder"
               :disabled="config.disabled"
               :multiple="config.multiple">
        <el-option
            v-for="item in config.options"
            :key="item.value"
            :label="item.label"
            :value="item.value"
            :disabled="item.disabled"
        />
    </el-select>
</template>

<script>
import {onMounted, ref, watch} from "vue";

export default {
    name: "select",
    props: {
        item: Object, //组件外层基本数据
        config: {  //基本配置,与element-plus一样
            type: Object,
            default: () => ({
                value: '',  //初始值
                key: '',
                options: Array,   //选项下拉值
                placeholder: 'please select',
                disabled: false,  //禁用
                multiple: false,  //开启多选
                allowCreate: false,  //允许自定义选项
                cascader: null,   //级联控制的组件的key
            })
        }
    },
    emits: ['handleSelect'],
    setup(props, {emit}) {
        const val = ref('')
        watch(() => props.config.value, newVal => {
            val.value = newVal
        }, {immediate: true})

        watch(() => props.config.value, newVal => {
            val.value = newVal
            let obj = {
                key: props.config.key,
                item: props.item,
                config: {
                    cascader: props.config.cascader
                }
            }
            emit("handleSelect", obj)
        }, {immediate: true})

        const handleSelect = () => {
            let obj = {
                val: val.value,
                key: props.config.key,
                item: props.item,  //外层组件的数据,比如这个select套在table里面,这里就是table的行内数据
                config: {
                    cascader: props.config.cascader,
                    value:val.value
                }
            }
            props.config.value = val.value
            emit("handleSelect", obj)
        }
        onMounted(() => {
        })
        return {
            val,
            handleSelect
        }
    }
}
</script>

Thanks~

<think>我们面对的是一个在详情页顶部添加多个搜索框的需求,需要根据问卷标题(dcWjTitle)、被测评人ID(dcid)、人员部门(dcDept)和提交状态(state)进行多条件过滤。 步骤分析: 1. 前端部分:在详情页顶部添加搜索表单,包含四个输入字段(dcWjTitle, dcid, dcDept, state)和一个提交按钮。 2. 后端部分:接收前端传递的搜索条件,构建查询语句,过滤数据并返回结果。 具体实现: 前端实现(以Vue.js + Element UI为例): 在详情页顶部添加一个表单,包含四个输入框和一个搜索按钮。 ```html <template> <div class="filter-container"> <el-form :inline="true" :model="filterForm" @submit.native.prevent="handleFilter"> <el-form-item label="问卷标题"> <el-input v-model="filterForm.dcWjTitle" placeholder="请输入问卷标题" clearable></el-input> </el-form-item> <el-form-item label="被测评人ID"> <el-input v-model="filterForm.dcid" placeholder="请输入被测评人ID" clearable></el-input> </el-form-item> <el-form-item label="人员部门"> <el-input v-model="filterForm.dcDept" placeholder="请输入人员部门" clearable></el-input> </el-form-item> <el-form-item label="提交状态"> <el-select v-model="filterForm.state" placeholder="请选择提交状态" clearable> <el-option label="已提交" value="1"></el-option> <el-option label="未提交" value="0"></el-option> </el-select> </el-form-item> <el-form-item> <el-button type="primary" @click="handleFilter">搜索</el-button> </el-form-item> </el-form> </div> <!-- 其他内容,比如表格展示数据 --> </template> ``` 在Vue的data中定义filterForm对象: ```javascript data() { return { filterForm: { dcWjTitle: '', dcid: '', dcDept: '', state: '' }, // 其他数据 } }, methods: { handleFilter() { // 触发搜索事件,可以在这里发送请求,传递filterForm的参数 this.$emit('filter', this.filterForm); // 如果使用组件通信 // 或者直接调用获取数据的方法 this.fetchData(); }, fetchData() { // 根据filterForm中的条件请求数据 let params = { dcWjTitle: this.filterForm.dcWjTitle, dcid: this.filterForm.dcid, dcDept: this.filterForm.dcDept, state: this.filterForm.state }; // 使用axios或其他库发送请求 axios.get('/api/your-endpoint', { params }) .then(response => { // 处理返回的数据,更新表格等 }) .catch(error => { console.error(error); }); } } ``` 后端实现(以Spring Boot为例): 在Controller中接收参数,并构建查询条件。 ```java @RestController @RequestMapping("/api/your-endpoint") public class YourController { @Autowired private YourService yourService; @GetMapping public ResponseEntity<?> getFilteredData( @RequestParam(required = false) String dcWjTitle, @RequestParam(required = false) String dcid, @RequestParam(required = false) String dcDept, @RequestParam(required = false) String state) { // 构建查询条件,可以使用MyBatis Plus等工具 QueryWrapper<YourEntity> queryWrapper = new QueryWrapper<>(); if (StringUtils.isNotBlank(dcWjTitle)) { queryWrapper.like("dc_wj_title", dcWjTitle); } if (StringUtils.isNotBlank(dcid)) { queryWrapper.eq("dcid", dcid); } if (StringUtils.isNotBlank(dcDept)) { queryWrapper.like("dc_dept", dcDept); } if (StringUtils.isNotBlank(state)) { queryWrapper.eq("state", state); } List<YourEntity> list = yourService.list(queryWrapper); return ResponseEntity.ok(list); } } ``` 注意:这里的字段名(如dc_wj_title)需要与数据库表中的列名一致,或者根据ORM框架的映射规则调整。 安全与优化考虑: 1. 参数校验:确保传入的参数符合预期,避免SQL注入(使用预编译的查询条件可以避免)。 2. 性能:如果数据量很大,考虑分页。 3. 缓存:对于不常变的数据,可以考虑使用缓存(如Redis)[^5]。 根据引用[4]中的优化建议,我们还可以考虑: - 参数序列化优化:统一使用Jackson。 - 异常处理:对参数类型转换异常进行捕获(例如state转换为数字时)。 - 日志脱敏:在日志中避免打印敏感参数。 因此,在后端代码中,我们可以增强异常处理: ```java try { // 构建查询条件并查询 } catch (Exception e) { // 记录日志,注意脱敏 log.error("查询异常, 参数: dcWjTitle={}, dcid={}, dcDept={}, state={}", LogUtil.mask(dcWjTitle), LogUtil.mask(dcid), LogUtil.mask(dcDept), state, e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("查询失败"); } ``` 同时,对于分页,可以在前端传递分页参数(当前页、每页条数),后端使用分页查询。 总结:通过前后端配合,我们可以实现多条件搜索功能。前端提供输入界面,后端根据条件过滤数据并返回。 相关问题: 1. 如何在前端实现搜索框的实时搜索(防抖)? 2. 后端如何构建动态查询条件(使用QueryDSL等)? 3. 如何对多条件搜索进行性能优化(如数据库索引)?</think>### 实现多条件搜索功能的步骤 #### 1. 前端实现(Vue.js + Element UI 示例) ```html <template> <!-- 顶部搜索区域 --> <div class="filter-container"> <el-form :inline="true" :model="searchForm"> <!-- 问卷标题搜索 --> <el-form-item label="问卷标题"> <el-input v-model="searchForm.dcWjTitle" placeholder="输入问卷标题" clearable /> </el-form-item> <!-- 被测评人ID搜索 --> <el-form-item label="被测评人ID"> <el-input v-model="searchForm.dcid" placeholder="输入被测评人ID" clearable /> </el-form-item> <!-- 部门选择 --> <el-form-item label="人员部门"> <el-select v-model="searchForm.dcDept" placeholder="选择部门" clearable > <el-option v-for="dept in departments" :key="dept.value" :label="dept.label" :value="dept.value" /> </el-select> </el-form-item> <!-- 状态选择 --> <el-form-item label="提交状态"> <el-select v-model="searchForm.state" placeholder="选择状态" clearable > <el-option label="已提交" value="1" /> <el-option label="未提交" value="0" /> </el-select> </el-form-item> <el-form-item> <el-button type="primary" @click="handleSearch" icon="el-icon-search" >搜索</el-button> </el-form-item> </el-form> </div> <!-- 数据表格 --> <el-table :data="filteredData"> <!-- 表格列定义 --> </el-table> </template> <script> export default { data() { return { searchForm: { dcWjTitle: '', dcid: '', dcDept: '', state: '' }, rawData: [], // 原始数据 departments: [ // 部门选项 { label: '技术部', value: 'tech' }, { label: '市场部', value: 'market' } ] } }, computed: { // 过滤后的数据 filteredData() { return this.rawData.filter(item => (!this.searchForm.dcWjTitle || item.title.includes(this.searchForm.dcWjTitle)) && (!this.searchForm.dcid || item.dcid === this.searchForm.dcid) && (!this.searchForm.dcDept || item.dept === this.searchForm.dcDept) && (!this.searchForm.state || item.state === this.searchForm.state) ) } }, methods: { // 触发搜索 handleSearch() { this.fetchFilteredData() }, // 从后端获取数据 async fetchFilteredData() { try { const params = { title: this.searchForm.dcWjTitle, dcid: this.searchForm.dcid, dept: this.searchForm.dcDept, state: this.searchForm.state } // 调用后端API(示例) const { data } = await axios.get('/api/questionnaires', { params }) this.rawData = data } catch (error) { console.error('获取数据失败', error) } } } } </script> ``` #### 2. 后端实现(Spring Boot 示例) ```java @RestController @RequestMapping("/api/questionnaires") public class QuestionnaireController { @Autowired private QuestionnaireService questionnaireService; @GetMapping public ResponseEntity<List<QuestionnaireDTO>> getFilteredQuestionnaires( @RequestParam(required = false) String title, @RequestParam(required = false) String dcid, @RequestParam(required = false) String dept, @RequestParam(required = false) String state) { // 构建查询条件 QuestionnaireQuery query = new QuestionnaireQuery(); query.setTitle(title); query.setDcid(dcid); query.setDept(dept); query.setState(state); // 执行查询(使用MyBatis Plus示例) List<QuestionnaireDTO> result = questionnaireService.queryByConditions(query); return ResponseEntity.ok(result); } } // 查询条件封装类 public class QuestionnaireQuery { private String title; private String dcid; private String dept; private String state; // getters/setters } // MyBatis Plus 查询实现 @Service public class QuestionnaireServiceImpl extends ServiceImpl<QuestionnaireMapper, Questionnaire> implements QuestionnaireService { public List<QuestionnaireDTO> queryByConditions(QuestionnaireQuery query) { QueryWrapper<Questionnaire> wrapper = new QueryWrapper<>(); if (StringUtils.isNotBlank(query.getTitle())) { wrapper.like("dc_wj_title", query.getTitle()); } if (StringUtils.isNotBlank(query.getDcid())) { wrapper.eq("dcid", query.getDcid()); } if (StringUtils.isNotBlank(query.getDept())) { wrapper.eq("dc_dept", query.getDept()); } if (StringUtils.isNotBlank(query.getState())) { wrapper.eq("state", query.getState()); } return baseMapper.selectList(wrapper) .stream() .map(this::toDTO) .collect(Collectors.toList()); } } ``` #### 3. 关键优化建议(参考引用[4][^4]) 1. **参数处理**: ```java // 特殊字符处理 String processedTitle = StringUtils.trimToEmpty(title).replace("%", "\\%"); wrapper.like("dc_wj_title", processedTitle); ``` 2. **缓存优化**: ```java @Cacheable(value = "deptCache", key = "#dept") public List<Questionnaire> getByDept(String dept) { // 数据库查询 } ``` 3. **安全增强**: ```java // 防止SQL注入 wrapper.apply("dc_wj_title = {0}", title); // 添加请求频率限制 @RateLimiter(name = "searchLimit", rate = 10) // 每秒10次 ``` 4. **日志脱敏**: ```java log.info("搜索条件 - 标题:{} 部门:{}", LogUtils.maskSensitive(title), LogUtils.maskSensitive(dept)); ``` #### 4. 完整工作流程 ```mermaid sequenceDiagram participant 用户 participant 前端 participant 后端 participant 数据库 用户->>前端: 填写搜索条件并提交 前端->>后端: 发送GET请求/api/questionnaires?title=XX&dcid=XX 后端->>后端: 构建动态查询条件 后端->>数据库: 执行SQL查询 数据库-->>后端: 返回结果集 后端->>前端: 返回JSON格式数据 前端->>用户: 渲染过滤后的列表 ``` ### 注意事项 1. **空值处理**:明确空字符串和`null`的处理逻辑,建议统一转换为`null` 2. **编码规范**:所有参数统一使用UTF-8编码 3. **性能优化**: - 为常用搜索字段(如`dcid`, `state`)添加数据库索引 - 使用分页查询避免大数据量加载 4. **安全防护**: - 对输入参数进行XSS过滤 - 添加请求签名验证(参考引用[4][^4]) 5. **用户体验**: - 添加搜索条件记忆功能 - 实现实时搜索建议(debounce优化) [^1]: 本项目以DCID2022赛题为背景... [^4]: GET请求处理...优化建议... --- ### 相关问题 1. 如何实现前端搜索框的实时自动补全功能? 2. 后端如何处理多条件搜索的分页查询? 3. 在大数据量下如何优化多条件搜索的查询性能? 4. 如何防止通过搜索接口进行数据爬取? 5. 多条件搜索场景下数据库索引应该如何设计?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值