vue 使用餓瞭麼UI仿寫teambition的篩選功能
問題描述
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>
總結
這裡面需要註意的就是鼠標移入移出顯示對應的刪除小圖標。思路大致就這樣,敲代碼不易,咱們共同努力。
以上就是vue 使用餓瞭麼UI仿寫teambition的篩選功能的詳細內容,更多關於vue 仿寫teambition的篩選功能的資料請關註WalkonNet其它相關文章!