vue 使用餓了么UI仿寫teambition的篩選功能
問題描述
teambition軟件是企業(yè)辦公協(xié)同軟件,相信部分朋友的公司應(yīng)該用過這款軟件。里面的篩選功能挺有意思,本篇文章,就是仿寫其功能。我們先看一下最終做出來的效果圖

大致的功能效果有如下
- 需求一:常用篩選條件放在上面直接看到,不常用篩選條件放在添加篩選條件里面
- 需求二:篩選的方式有輸入框篩選、下拉框篩選、時(shí)間選擇器篩選等
- 需求三:如果覺得常用篩選條件比較多的話,可以鼠標(biāo)移入點(diǎn)擊刪除,使之進(jìn)入不常用的篩選條件里
- 需求四:也可以從不常用的篩選條件里面點(diǎn)擊對(duì)應(yīng)篩選條件使之“蹦到”常用篩選條件里
- 需求五:點(diǎn)擊重置使之恢復(fù)到初試的篩選條件
- 需求六:用戶若是沒輸入內(nèi)容點(diǎn)擊確認(rèn)按鈕,就提示用戶要輸入篩選條件
思路分析
對(duì)于需求一和需求二,我們首先要搞兩個(gè)全屏幕彈框,然后在data中定義兩個(gè)數(shù)組,一個(gè)是放常用條件的數(shù)組,另外一個(gè)是放不常用條件的數(shù)組,常用條件v-for到第一個(gè)彈框里面,不常用條件v-for到第二個(gè)彈框里面。數(shù)組里面的每一項(xiàng)都要配置好對(duì)應(yīng)內(nèi)容,比如要有篩選字段名字,比如姓名、年齡什么的。有了篩選篩選字段名字以后,還有有一個(gè)類型type,在html中我們要寫三個(gè)類型的組件、比如input輸入框組件,select組件,時(shí)間選擇器組件。使用根據(jù)type類型通過v-show顯示對(duì)應(yīng)字段,比如input的type為1,select的type為2,時(shí)間選擇器的type為3。是哪個(gè)type,就顯示哪個(gè)組件。
對(duì)應(yīng)兩個(gè)數(shù)組如下:
topData: [ // 配置常用的篩選項(xiàng)
{
wordTitle: "姓名",
type: 1, // 1 為input 2為select 3為DatePicker
content: "", // content為輸入框綁定的輸入數(shù)據(jù)
options: [], // options為所有的下拉框內(nèi)容,可以發(fā)請(qǐng)求拿到存進(jìn)來,這里是模擬
optionArr: [], // optionArr為選中的下拉框內(nèi)容
timeArr: [], // timeArr為日期選擇區(qū)間
},
{
wordTitle: "年齡",
type: 1,
content: "",
options: [],
optionArr: [],
timeArr: [],
},
{
wordTitle: "授課班級(jí)",
type: 2,
content: "",
options: [ // 發(fā)請(qǐng)求獲取下拉框選項(xiàng)
{
id: 1,
value: "一班",
},
{
id: 2,
value: "二班",
},
{
id: 3,
value: "三班",
},
],
optionArr: [],
timeArr: [],
},
{
wordTitle: "入職時(shí)間",
type: 3,
content: "",
options: [],
optionArr: [],
timeArr: [],
},
],
bottomData: [ // 配置不常用的篩選項(xiàng)
{
wordTitle: "工號(hào)",
type: 1,
content: "",
options: [],
optionArr: [],
timeArr: [],
},
{
wordTitle: "性別",
type: 2,
content: "",
options: [
{
id: 1,
value: "男",
},
{
id: 2,
value: "女",
},
],
optionArr: [],
timeArr: [],
},
],
對(duì)應(yīng)html代碼如下:
<div class="rightright">
<el-input
v-model.trim="item.content"
clearable
v-show="item.type == 1"
placeholder="請(qǐng)輸入"
size="small"
:popper-append-to-body="false"
></el-input>
<el-select
v-model="item.optionArr"
v-show="item.type == 2"
multiple
placeholder="請(qǐng)選擇"
>
<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="結(jié)束日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
>
</el-date-picker>
</div>
完整代碼在最后,大家先順著思路看哦
對(duì)于需求三需求四,可描述為,刪除上面的掉到下面。點(diǎn)擊下面的蹦到上面。所以對(duì)應(yīng)操作就是把上面數(shù)組某一項(xiàng)追加到下面數(shù)組,然后把上面數(shù)組的這一項(xiàng)刪掉;把下面數(shù)組的某一項(xiàng)追加到上面數(shù)組,然后把這一行刪掉。(注意還有一個(gè)索引)對(duì)應(yīng)代碼如下:
/* 點(diǎn)擊某一項(xiàng)的刪除小圖標(biāo),把這一項(xiàng)添加到bottomData數(shù)組中
然后把這一項(xiàng)從topData數(shù)組中刪除掉(根據(jù)索引判別是哪一項(xiàng))
最后刪除一個(gè)就把索引置為初始索引 -1 */
clickIcon(i) {
this.bottomData.push(this.topData[i]);
this.topData.splice(i, 1);
this.whichIndex = -1;
},
// 點(diǎn)擊底部的項(xiàng)的時(shí)候,通過事件對(duì)象,看看點(diǎn)擊的是底部的哪一項(xiàng)
// 然后把對(duì)應(yīng)的那一項(xiàng)追加到topData中用于展示,同時(shí)把bottom數(shù)組
// 中的哪一項(xiàng)進(jìn)行刪除
clickBottomItem(event) {
this.bottomData.forEach((item, index) => {
if (item.wordTitle == event.target.innerText) {
this.topData.push(item);
this.bottomData.splice(index, 1);
}
});
},
對(duì)于需求五需求六就簡(jiǎn)單了,對(duì)應(yīng)代碼如下,完整代碼注釋中已經(jīng)寫好了
完整代碼
<template>
<div id="app">
<div class="filterBtn">
<el-button type="primary" size="small" @click="filterMaskOne = true">
數(shù)據(jù)篩選<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>數(shù)據(jù)篩選</span>
</div>
<div class="filterBody">
<div class="outPrompt" v-show="topData.length == 0">
暫無篩選條件,請(qǐng)?zhí)砑雍Y選條件...
</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="請(qǐng)輸入"
size="small"
:popper-append-to-body="false"
></el-input>
<el-select
v-model="item.optionArr"
v-show="item.type == 2"
multiple
placeholder="請(qǐng)選擇"
>
<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="結(jié)束日期"
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">
暫無內(nèi)容...
</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"
>確認(rèn)</el-button
>
</div>
</div>
</div>
</div>
</transition>
</div>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
filterMaskOne: false, // 分別用于控制兩個(gè)彈框的顯示與隱藏
filterMaskTwo: false,
whichIndex: -1, // 用于記錄點(diǎn)擊的索引
apiFilterArr:[], //存儲(chǔ)用戶填寫的篩選內(nèi)容
topData: [ // 配置常用的篩選項(xiàng)
{
wordTitle: "姓名",
type: 1, // 1 為input 2為select 3為DatePicker
content: "", // content為輸入框綁定的輸入數(shù)據(jù)
options: [], // options為所有的下拉框內(nèi)容
optionArr: [], // optionArr為選中的下拉框內(nèi)容
timeArr: [], // timeArr為日期選擇區(qū)間
},
{
wordTitle: "年齡",
type: 1,
content: "",
options: [],
optionArr: [],
timeArr: [],
},
{
wordTitle: "授課班級(jí)",
type: 2,
content: "",
options: [ // 發(fā)請(qǐng)求獲取下拉框選項(xiàng)
{
id: 1,
value: "一班",
},
{
id: 2,
value: "二班",
},
{
id: 3,
value: "三班",
},
],
optionArr: [],
timeArr: [],
},
{
wordTitle: "入職時(shí)間",
type: 3,
content: "",
options: [],
optionArr: [],
timeArr: [],
},
],
bottomData: [ // 配置不常用的篩選項(xiàng)
{
wordTitle: "工號(hào)",
type: 1,
content: "",
options: [],
optionArr: [],
timeArr: [],
},
{
wordTitle: "性別",
type: 2,
content: "",
options: [
{
id: 1,
value: "男",
},
{
id: 2,
value: "女",
},
],
optionArr: [],
timeArr: [],
},
],
};
},
mounted() {
// 在初始化加載的時(shí)候,我們就把我們配置的常用和不常用的篩選項(xiàng)保存一份
// 當(dāng)用戶點(diǎn)擊重置按鈕的時(shí)候,再取出來使其恢復(fù)到最初的篩選條件狀態(tài)
sessionStorage.setItem("topData",JSON.stringify(this.topData))
sessionStorage.setItem("bottomData",JSON.stringify(this.bottomData))
},
methods: {
//鼠標(biāo)移入顯示刪除小圖標(biāo)
mouseEnterItem(index) {
this.whichIndex = index;
},
// 鼠標(biāo)離開將索引回復(fù)到默認(rèn)-1
mouseLeaveItem() {
this.whichIndex = -1;
},
/* 點(diǎn)擊某一項(xiàng)的刪除小圖標(biāo),把這一項(xiàng)添加到bottomData數(shù)組中
然后把這一項(xiàng)從topData數(shù)組中刪除掉(根據(jù)索引判別是哪一項(xiàng))
最后刪除一個(gè)就把索引置為初始索引 -1 */
clickIcon(i) {
this.bottomData.push(this.topData[i]);
this.topData.splice(i, 1);
this.whichIndex = -1;
},
// 點(diǎn)擊底部的項(xiàng)的時(shí)候,通過事件對(duì)象,看看點(diǎn)擊的是底部的哪一項(xiàng)
// 然后把對(duì)應(yīng)的那一項(xiàng)追加到topData中用于展示,同時(shí)把bottom數(shù)組
// 中的哪一項(xiàng)進(jìn)行刪除
clickBottomItem(event) {
this.bottomData.forEach((item, index) => {
if (item.wordTitle == event.target.innerText) {
this.topData.push(item);
this.bottomData.splice(index, 1);
}
});
},
// 點(diǎn)擊確認(rèn)篩選
async confirmFilter() {
// 如果所有的輸入框的content內(nèi)容為空,且選中的下拉框數(shù)組為空,且時(shí)間選擇器選中的數(shù)組為空
// 就說明用戶沒有輸入內(nèi)容,那么我們就提示用戶要輸入內(nèi)容以后再進(jìn)行篩選
let isEmpty = this.topData.every((item)=>{
return (item.content == "") && (item.optionArr.length == 0) && (item.timeArr.length == 0)
})
if(isEmpty == true){
this.$alert('請(qǐng)輸入內(nèi)容以后再進(jìn)行篩選', '篩選提示', {
confirmButtonText: '確定'
});
}else{
// 收集參數(shù)發(fā)篩選請(qǐng)求,這里要分類型,把不為空的既有用戶輸入內(nèi)容的
// 存到存到數(shù)據(jù)篩選的數(shù)組中去,然后發(fā)請(qǐng)求給后端。
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)
}
}
})
// 把篩選的內(nèi)容放到一個(gè)數(shù)組里面,傳遞給后端(當(dāng)然不一定把參數(shù)放到數(shù)組里面)
// 具體以怎樣的形式傳遞給后端,可以具體商量
console.log("帶著篩選內(nèi)容發(fā)請(qǐng)求",this.apiFilterArr);
}
},
// 重置時(shí),再把最初的配置篩選項(xiàng)取出來賦給對(duì)應(yīng)的兩個(gè)數(shù)組
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>
總結(jié)
這里面需要注意的就是鼠標(biāo)移入移出顯示對(duì)應(yīng)的刪除小圖標(biāo)。思路大致就這樣,敲代碼不易,咱們共同努力。
以上就是vue 使用餓了么UI仿寫teambition的篩選功能的詳細(xì)內(nèi)容,更多關(guān)于vue 仿寫teambition的篩選功能的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue+axios實(shí)現(xiàn)post文件下載
這篇文章主要為大家詳細(xì)介紹了vue+axios實(shí)現(xiàn)post文件下載,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09
vue實(shí)現(xiàn)excel文件導(dǎo)入導(dǎo)出操作示例
這篇文章主要為大家介紹了vue實(shí)現(xiàn)excel文件的導(dǎo)入導(dǎo)出實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
vue如何實(shí)現(xiàn)動(dòng)態(tài)的選中狀態(tài)切換效果
這篇文章主要介紹了vue如何實(shí)現(xiàn)動(dòng)態(tài)的選中狀態(tài)切換效果,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
vue和thymeleaf相結(jié)合的注意事項(xiàng)詳解
這篇文章主要介紹了vue和thymeleaf相結(jié)合的注意事項(xiàng)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
Vue+SpringBoot開發(fā)V部落博客管理平臺(tái)
V部落是一個(gè)多用戶博客管理平臺(tái)。這篇文章主要介紹了Vue+SpringBoot開發(fā)V部落博客管理平臺(tái),需要的朋友可以參考下2017-12-12
element table跨分頁多選及回顯的實(shí)現(xiàn)示例
本文主要介紹了element table跨分頁多選及回顯的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
關(guān)于Vue中img動(dòng)態(tài)拼接src圖片地址的問題
這篇文章主要介紹了關(guān)于Vue中img動(dòng)態(tài)拼接src圖片地址的問題,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10
使用vue初用antd 用v-model來雙向綁定Form表單問題
這篇文章主要介紹了使用vue初用antd 用v-model來雙向綁定Form表單問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
Vue項(xiàng)目總結(jié)之webpack常規(guī)打包優(yōu)化方案
這篇文章主要介紹了vue項(xiàng)目總結(jié)之webpack常規(guī)打包優(yōu)化方案,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-06-06

