问题形容
teambition软件是企业办公协同软件,置信局部敌人的公司应该用过这款软件。外面的筛选性能挺有意思,本篇文章,就是仿写其性能。咱们先看一下最终做进去的效果图
大抵的性能成果有如下
- 需要一:罕用筛选条件放在下面间接看到,不罕用筛选条件放在增加筛选条件外面
- 需要二:筛选的形式有输入框筛选、下拉框筛选、工夫选择器筛选等
- 需要三:如果感觉罕用筛选条件比拟多的话,能够鼠标移入点击删除,使之进入不罕用的筛选条件里
- 需要四:也能够从不罕用的筛选条件外面点击对应筛选条件使之“蹦到”罕用筛选条件里
- 需要五:点击重置使之复原到初试的筛选条件
- 需要六:用户若是没输出内容点击确认按钮,就提醒用户要输出筛选条件
思路剖析
- 对于需要一和需要二,咱们首先要搞两个全屏幕弹框,而后在data中定义两个数组,一个是放罕用条件的数组,另外一个是放不罕用条件的数组,罕用条件v-for到第一个弹框外面,不罕用条件v-for到第二个弹框外面。数组外面的每一项都要配置好对应内容,比方要有筛选字段名字,比方姓名、年龄什么的。有了筛选筛选字段名字当前,还有有一个类型type,在html中咱们要写三个类型的组件、比方input输入框组件,select组件,工夫选择器组件。应用依据type类型通过v-show显示对应字段,比方input的type为1,select的type为2,工夫选择器的type为3。是哪个type,就显示哪个组件。
对应两个数组如下:
topData: [ // 配置罕用的筛选项 { wordTitle: "姓名", type: 1, // 1 为input 2为select 3为DatePicker content: "", // content为输入框绑定的输出数据 options: [], // options为所有的下拉框内容,能够发申请拿到存进来,这里是模仿 optionArr: [], // optionArr为选中的下拉框内容 timeArr: [], // timeArr为日期抉择区间 }, { wordTitle: "年龄", type: 1, content: "", options: [], optionArr: [], timeArr: [], }, { wordTitle: "授课班级", type: 2, content: "", options: [ // 发申请获取下拉框选项 { id: 1, value: "一班", }, { id: 2, value: "二班", }, { id: 3, value: "三班", }, ], optionArr: [], timeArr: [], }, { wordTitle: "入职工夫", type: 3, content: "", options: [], optionArr: [], timeArr: [], }, ], bottomData: [ // 配置不罕用的筛选项 { wordTitle: "工号", type: 1, content: "", options: [], optionArr: [], timeArr: [], }, { wordTitle: "性别", type: 2, content: "", options: [ { id: 1, value: "男", }, { id: 2, value: "女", }, ], optionArr: [], timeArr: [], }, ],
对应html代码如下:
<div class="rightright"> <el-input v-model.trim="item.content" clearable v-show="item.type == 1" placeholder="请输出" size="small" :popper-append-to-body="false" ></el-input> <el-select v-model="item.optionArr" v-show="item.type == 2" multiple placeholder="请抉择" > <el-option v-for="whatItem in item.options" :key="whatItem.id" :label="whatItem.value" :value="whatItem.id" size="small" > </el-option> </el-select> <el-date-picker v-model="item.timeArr" v-show="item.type == 3" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="完结日期" format="yyyy-MM-dd" value-format="yyyy-MM-dd" > </el-date-picker> </div>
残缺代码在最初,大家先顺着思路看哦
- 对于需要三需要四,可形容为,删除下面的掉到上面。点击上面的蹦到下面。所以对应操作就是把下面数组某一项追加到上面数组,而后把下面数组的这一项删掉;把上面数组的某一项追加到下面数组,而后把这一行删掉。(留神还有一个索引)对应代码如下:
/* 点击某一项的删除小图标,把这一项增加到bottomData数组中 而后把这一项从topData数组中删除掉(依据索引判断是哪一项) 最初删除一个就把索引置为初始索引 -1 */ clickIcon(i) { this.bottomData.push(this.topData[i]); this.topData.splice(i, 1); this.whichIndex = -1; }, // 点击底部的项的时候,通过事件对象,看看点击的是底部的哪一项 // 而后把对应的那一项追加到topData中用于展现,同时把bottom数组 // 中的哪一项进行删除 clickBottomItem(event) { this.bottomData.forEach((item, index) => { if (item.wordTitle == event.target.innerText) { this.topData.push(item); this.bottomData.splice(index, 1); } }); },
- 对于需要五需要六就简略了,对应代码如下,残缺代码正文中曾经写好了
残缺代码
<template> <div id="app"> <div class="filterBtn"> <el-button type="primary" size="small" @click="filterMaskOne = true"> 数据筛选<i class="el-icon-s-operation el-icon--right"></i> </el-button> <transition name="fade"> <div class="filterMaskOne" v-show="filterMaskOne" @click="filterMaskOne = false" > <div class="filterMaskOneContent" @click.stop> <div class="filterHeader"> <span>数据筛选</span> </div> <div class="filterBody"> <div class="outPrompt" v-show="topData.length == 0"> 暂无筛选条件,请增加筛选条件... </div> <div class="filterBodyCondition" v-for="(item, index) in topData" :key="index" > <div class="leftleft" @mouseenter="mouseEnterItem(index)" @mouseleave="mouseLeaveItem(index)" > <span >{{ item.wordTitle }}: <i class="el-icon-error" v-show="whichIndex == index" @click="clickIcon(index)" ></i> </span> </div> <div class="rightright"> <el-input v-model.trim="item.content" clearable v-show="item.type == 1" placeholder="请输出" size="small" :popper-append-to-body="false" ></el-input> <el-select v-model="item.optionArr" v-show="item.type == 2" multiple placeholder="请抉择" > <el-option v-for="whatItem in item.options" :key="whatItem.id" :label="whatItem.value" :value="whatItem.id" size="small" > </el-option> </el-select> <el-date-picker v-model="item.timeArr" v-show="item.type == 3" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="完结日期" format="yyyy-MM-dd" value-format="yyyy-MM-dd" > </el-date-picker> </div> </div> </div> <div class="filterFooter"> <div class="filterBtn"> <el-button type="text" icon="el-icon-circle-plus-outline" @click="filterMaskTwo = true" >增加筛选条件</el-button > <transition name="fade"> <div class="filterMaskTwo" v-show="filterMaskTwo" @click="filterMaskTwo = false" > <div class="filterMaskContentTwo" @click.stop> <div class="innerPrompt" v-show="bottomData.length == 0"> 暂无内容... </div> <div class="contentTwoItem" @click="clickBottomItem" v-for="(item, index) in bottomData" :key="index" > <div class="mingzi"> {{ item.wordTitle }} </div> </div> </div> </div> </transition> </div> <div class="resetAndConfirmBtns"> <el-button size="small" @click="resetFilter">重置</el-button> <el-button type="primary" size="small" @click="confirmFilter" >确认</el-button > </div> </div> </div> </div> </transition> </div> </div></template><script>export default { name: "app", data() { return { filterMaskOne: false, // 别离用于管制两个弹框的显示与暗藏 filterMaskTwo: false, whichIndex: -1, // 用于记录点击的索引 apiFilterArr:[], //存储用户填写的筛选内容 topData: [ // 配置罕用的筛选项 { wordTitle: "姓名", type: 1, // 1 为input 2为select 3为DatePicker content: "", // content为输入框绑定的输出数据 options: [], // options为所有的下拉框内容 optionArr: [], // optionArr为选中的下拉框内容 timeArr: [], // timeArr为日期抉择区间 }, { wordTitle: "年龄", type: 1, content: "", options: [], optionArr: [], timeArr: [], }, { wordTitle: "授课班级", type: 2, content: "", options: [ // 发申请获取下拉框选项 { id: 1, value: "一班", }, { id: 2, value: "二班", }, { id: 3, value: "三班", }, ], optionArr: [], timeArr: [], }, { wordTitle: "入职工夫", type: 3, content: "", options: [], optionArr: [], timeArr: [], }, ], bottomData: [ // 配置不罕用的筛选项 { wordTitle: "工号", type: 1, content: "", options: [], optionArr: [], timeArr: [], }, { wordTitle: "性别", type: 2, content: "", options: [ { id: 1, value: "男", }, { id: 2, value: "女", }, ], optionArr: [], timeArr: [], }, ], }; }, mounted() { // 在初始化加载的时候,咱们就把咱们配置的罕用和不罕用的筛选项保留一份 // 当用户点击重置按钮的时候,再取出来使其复原到最后的筛选条件状态 sessionStorage.setItem("topData",JSON.stringify(this.topData)) sessionStorage.setItem("bottomData",JSON.stringify(this.bottomData)) }, methods: { //鼠标移入显示删除小图标 mouseEnterItem(index) { this.whichIndex = index; }, // 鼠标来到将索引回复到默认-1 mouseLeaveItem() { this.whichIndex = -1; }, /* 点击某一项的删除小图标,把这一项增加到bottomData数组中 而后把这一项从topData数组中删除掉(依据索引判断是哪一项) 最初删除一个就把索引置为初始索引 -1 */ clickIcon(i) { this.bottomData.push(this.topData[i]); this.topData.splice(i, 1); this.whichIndex = -1; }, // 点击底部的项的时候,通过事件对象,看看点击的是底部的哪一项 // 而后把对应的那一项追加到topData中用于展现,同时把bottom数组 // 中的哪一项进行删除 clickBottomItem(event) { this.bottomData.forEach((item, index) => { if (item.wordTitle == event.target.innerText) { this.topData.push(item); this.bottomData.splice(index, 1); } }); }, // 点击确认筛选 async confirmFilter() { // 如果所有的输入框的content内容为空,且选中的下拉框数组为空,且工夫选择器选中的数组为空 // 就阐明用户没有输出内容,那么咱们就提醒用户要输出内容当前再进行筛选 let isEmpty = this.topData.every((item)=>{ return (item.content == "") && (item.optionArr.length == 0) && (item.timeArr.length == 0) }) if(isEmpty == true){ this.$alert('请输出内容当前再进行筛选', '筛选提醒', { confirmButtonText: '确定' }); }else{ // 收集参数发筛选申请,这里要分类型,把不为空的既有用户输出内容的 // 存到存到数据筛选的数组中去,而后发申请给后端。 this.topData.forEach((item)=>{ if(item.type == 1){ if(item.content != ""){ let filterItem = { field:item.wordTitle, value:item.content } this.apiFilterArr.push(filterItem) } }else if(item.type == 2){ if(item.optionArr.length > 0){ let filterItem = { field:item.wordTitle, value:item.optionArr } this.apiFilterArr.push(filterItem) } }else if(item.type == 3){ if(item.timeArr.length > 0){ let filterItem = { field:item.wordTitle, value:item.timeArr } this.apiFilterArr.push(filterItem) } } }) // 把筛选的内容放到一个数组外面,传递给后端(当然不肯定把参数放到数组外面) // 具体以怎么的模式传递给后端,能够具体磋商 console.log("带着筛选内容发申请",this.apiFilterArr); } }, // 重置时,再把最后的配置筛选项取出来赋给对应的两个数组 resetFilter() { this.topData = JSON.parse(sessionStorage.getItem("topData")) this.bottomData = JSON.parse(sessionStorage.getItem("bottomData")) }, },};</script><style lang="less" scoped>.filterBtn { width: 114px; height: 40px; .filterMaskOne { top: 0; left: 0; position: fixed; width: 100%; height: 100%; z-index: 999; background-color: rgba(0, 0, 0, 0.3); .filterMaskOneContent { position: absolute; top: 152px; right: 38px; width: 344px; height: 371px; background-color: #fff; box-shadow: 0px 0px 4px 3px rgba(194, 194, 194, 0.25); border-radius: 4px; .filterHeader { width: 344px; height: 48px; border-bottom: 1px solid #e9e9e9; span { display: inline-block; font-weight: 600; font-size: 16px; margin-left: 24px; margin-top: 16px; } } .filterBody { width: 344px; height: 275px; overflow-y: auto; overflow-x: hidden; box-sizing: border-box; padding: 12px 24px 0 24px; .outPrompt { color: #666; } .filterBodyCondition { width: 100%; min-height: 40px; display: flex; margin-bottom: 14px; .leftleft { width: 88px; height: 40px; display: flex; align-items: center; margin-right: 20px; span { position: relative; font-size: 14px; color: #333; i { color: #666; right: -8px; top: -8px; position: absolute; font-size: 15px; cursor: pointer; } i:hover { color: #5f95f7; } } } .rightright { width: calc(100% - 70px); height: 100%; /deep/ input::placeholder { color: rgba(0, 0, 0, 0.25); font-size: 13px; } /deep/ .el-input__inner { height: 40px; line-height: 40px; } /deep/ .el-select { .el-input--suffix { /deep/ input::placeholder { color: rgba(0, 0, 0, 0.25); font-size: 13px; } .el-input__inner { border: none; } .el-input__inner:hover { background: rgba(95, 149, 247, 0.05); } } } .el-date-editor { width: 100%; font-size: 12px; } .el-range-editor.el-input__inner { padding-left: 2px; padding-right: 0; } /deep/.el-range-input { font-size: 13px !important; } /deep/ .el-range-separator { padding: 0 !important; font-size: 12px !important; width: 8% !important; margin: 0; } /deep/ .el-range__close-icon { width: 16px; } } } } .filterFooter { width: 344px; height: 48px; display: flex; justify-content: space-between; align-items: center; box-sizing: border-box; padding-left: 24px; padding-right: 12px; border-top: 1px solid #e9e9e9; .filterBtn { .filterMaskTwo { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.3); z-index: 1000; .filterMaskContentTwo { width: 240px; height: 320px; background: #ffffff; box-shadow: 0px 0px 4px 3px rgba(194, 194, 194, 0.25); border-radius: 4px; position: absolute; top: 360px; right: 180px; overflow-y: auto; box-sizing: border-box; padding: 12px 0 18px 0; overflow-x: hidden; .innerPrompt { color: #666; width: 100%; padding-left: 20px; margin-top: 12px; } .contentTwoItem { width: 100%; height: 36px; line-height: 36px; font-size: 14px; color: #333333; cursor: pointer; .mingzi { width: 100%; height: 36px; box-sizing: border-box; padding-left: 18px; } } .contentTwoItem:hover { background: rgba(95, 149, 247, 0.05); } } } } } } }}// 管制淡入淡出成果.fade-enter-active,.fade-leave-active { transition: opacity 0.3s;}.fade-enter,.fade-leave-to { opacity: 0;}</style>
总结
这外面须要留神的就是鼠标移入移出显示对应的删除小图标。思路大抵就这样,敲代码不易,咱们共同努力。