zoukankan      html  css  js  c++  java
  • vue 自定义 移动端筛选条件

    1.创建组件

    components/FilterBar/FilterBar.vue

    <template>
      <div class="filterbar" :style="{'top': top + 'px'}">
        <div class="container">
          <div class="row">
            <div
              class="col"
              :class="{'selected': index == selectedIndexMenu}"
              @click="handleShowDialog(barMenu, index)"
              v-for="(barMenu, index) in barMenus"
              :key="index"
            >
              {{barMenu.name}}<span :class="index == selectedIndexMenu ? barMenu.selectIcon : barMenu.defaultIcon"></span>
            </div>
          </div>
          <filter-bar-pop
            :filterTop="top"
            :show-dialog="isShow"
            :hasTabHeader="hasTabHeader"
            :menu="selectedMenu"
            @changeTab="handleChangeTab"
            @changeMainItem="handleChangeMainItem"
            @changeSelect="changeSelect"
            @closeDialog="handleCloseDialog"
          ></filter-bar-pop>
        </div>
      </div>
    </template>
    
    <script>
      import FilterBarPop from './FilterBarPop'
      export default {
        props: {
          barMenus: {
            type: Array,
            required: true,
            validator: function (value) {
              //TODO:验证数据有效性
              return true;
            }
          },
          top: String
        },
        data() {
          return {
            isShow: false,
            hasTabHeader: false,
            selectedMenu: {},
            selectedIndexMenu: undefined
          }
        },
        methods: {
          handleShowDialog(menu, index) {
            this.isShow = true;
            this.selectedMenu = menu;
            this.selectedIndexMenu = index;
            if (menu.showTabHeader) {
              this.hasTabHeader = true;
            } else {
              this.hasTabHeader = false;
            }
            let _menu = JSON.parse(JSON.stringify(menu));
            _menu.tabs = {};
            this.$emit('showDialog', _menu);
          },
          handleChangeTab(tab) {
            this.$emit('changeTab', tab.index);
          },
          handleChangeMainItem(mainItem) {
            let _mainItem = JSON.parse(JSON.stringify(mainItem));
            this.$emit('changeMainItem', _mainItem);
          },
          handleCloseDialog() {
            this.isShow = false;
            this.selectedIndexMenu = -1;
            this.$emit('closeDialog');
          },
          changeSelect() {
            var selectData = [];
            this.barMenus.forEach(function (barMenu, index, arr) {
              let _selectBarData = {};
              // console.log("barMenu.name: " + barMenu.name);
              _selectBarData.name = barMenu.name;
              _selectBarData.value = barMenu.value;
              _selectBarData.tab = {};
              let tab = barMenu.tabs[barMenu.selectIndex];
              // console.log("tab.name: " + tab.name);
              _selectBarData.tab.name = tab.name;
              _selectBarData.tab.value = tab.value;
              let mainItem = tab.detailList[tab.selectIndex];
              _selectBarData.tab.mainItem = {}
              // console.log("mainItem.name: " + mainItem.name);
              _selectBarData.tab.mainItem.name = mainItem.name;
              _selectBarData.tab.mainItem.value = mainItem.vaule;
              let subItem = false;
              if (mainItem.list) {
                subItem = mainItem.list[mainItem.selectIndex];
                _selectBarData.tab.mainItem.subItem = {};
                // console.log("subItem.name: " + subItem.name);
                _selectBarData.tab.mainItem.subItem.name = subItem.name;
                _selectBarData.tab.mainItem.subItem.value = subItem.value;
              } else {
                _selectBarData.tab.mainItem.subItem = subItem;
              }
              selectData.push(_selectBarData);
            });
            this.$emit('changeSelect', selectData);
          }
        },
        components: {
          'filter-bar-pop': FilterBarPop
        }
      }
    </script>
    
    <style lang="scss">
      .filterbar {
         100%;
        background: #fff;
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        .container {
           100%;
          outline: 1px solid #DBDCDE;
          position: relative;
          .row {
            display: flex;
            display: -ms-flexbox;
            display: -moz-box;
            display: -webkit-box;
            display: -webkit-flex;
            flex-direction: row;
            -webkit-flex-direction: row;
            justify-content: space-around;
            -webkit-box-pack: space-around;
            -moz-box-pack: space-around;
            -ms-flex-pack: space-around;
             90%;
            height: 40px;
            margin: 0 auto;
            line-height: 40px;
            .selected {
              color: orange;
            }
            .col {
              span {
                margin-left: 5px;
                vertical-align: middle;
              }
            }
          }
        }
      }
    </style>

    components/FilterBar/FilterBarPop.vue

    <template>
      <transition name="fade">
        <div class="filterbarpop-wrap" v-if="visible" :style="{'top': bgTop + 'px'}">
          <div class="filterbarpop-bg" @click="closeDialog" :style="{'top': bgTop + 'px'}"></div>
          <div class="filterbarpop">
            <div class="tab-bar" v-show="hasTabHeader">
              <a href="javascript:;" :style="{'flex': column}" role="button" @click="clickTab(tab, index)" v-for="(tab, index) in menu.tabs"
                :class="{'selected': selectIndexTab == index}"><span :class="tab.icon"></span>{{tab.name}}</a>
            </div>
            <div class="main">
              <div class="main-sidebar" :class="{'full-line': !items,'bg-style':items,'line-style':!items,}">
                <div v-if="menu.type !== 'filter'" class="item" @click="clickSidebar(sidemenu, index)" v-for="(sidemenu, index) in sideMenus.detailList"
                  :class="{'selected': currentSelectIndex == index}">
                  <span :class="sidemenu.icon"></span>{{ sidemenu.name }}
                </div>
                <div v-if="menu.type == 'filter'" v-for="(sm, _index) in menu.tabs">
                  <div class="filter-name">{{sm.name}}</div>
                  <div class="filter-item">
                    <span v-for="(sidemenu, index) in sm.detailList" class="item-operation" @click="clickFilterbar(sm, _index, index)" :class="{'multi-selected': sidemenu.selectIndex == index}">
                    {{ sidemenu.name }}
                    </span>
                  </div>
                </div>
                <div v-if="menu.type == 'filter'" class="filter-btns">
                  <a href="javascript:;" role="button" @click="handleClean">取消</a>
                  <a href="javascript:;" role="button" @click="handleEnsure">确认</a>
                </div>
              </div>
              <div class="main-list line-style" v-if="items">
                <span class="item" @click="clickItem(item, index)" v-for="(item, index) in items.list" :class="{'selected': currentSelectIndex == sideMenus.selectIndex && items.selectIndex == index}">{{item.name}}</span>
              </div>
            </div>
          </div>
        </div>
      </transition>
    </template>
    
    <script>
      export default {
        props: {
          menu: {
            type: Object
          },
          showDialog: {
            type: Boolean,
            default: true
          },
          hasTabHeader: {
            type: Boolean,
            default: true
          },
          filterTop: {
            type: String
          }
        },
        data() {
          return {
            selectIndexTab: 0,
            currentSelectIndex: 0,
            sideMenus: {},
            items: {},
            column: '',
            visible: false,
            top: 1,
            bgTop: 0,
            range: {}
          }
        },
        mounted() {
          this.bgTop = document.querySelector('.filterbar').offsetHeight + this.filterTop / 1;
        },
        watch: {
          showDialog(v) {
            this.visible = v;
            if (v) {
              //初始化数据
              this.initData();
            }
          },
          menu(m) {
            //根据tabs数量计算列宽
            this.column = '0 0 ' + 100 / m.tabs.length + '%';
            //初始化数据
            this.initData();
          }
        },
        methods: {
          //初始化数据
          initData(tabIndex) {
            var tmpTabIndx = 0;
            tabIndex === undefined ? tmpTabIndx = this.menu.selectIndex : tmpTabIndx = tabIndex
            //判断tabindex的范围是否在数组内
            if (tmpTabIndx >= 0 && tmpTabIndx < this.menu.tabs.length) {
              this.selectIndexTab = tmpTabIndx;
            } else {
              this.selectIndexTab = 0;
            }
            //确认选中tab的一级列表
            this.sideMenus = this.menu.tabs[this.selectIndexTab];
            //如果当前选中tab是对应选中结果的tab
            // debugger;
            if (this.selectIndexTab == this.menu.selectIndex) {
              this.currentSelectIndex = this.sideMenus.selectIndex;
            }
            // else{
            //   this.sideMenus.selectIndex = -1;
            //   this.currentSelectIndex = -1;
            // }
            //判断是否包含二级列表,包含则赋值
            //如果一级列表的选中状态正确,则查询二级列表
            if (this.currentSelectIndex >= 0 && this.currentSelectIndex < this.sideMenus.detailList.length) {
              //判断是否有二级列表
              if (this.sideMenus.detailList[this.currentSelectIndex].list) {
                this.items = this.sideMenus.detailList[this.currentSelectIndex];
              } else {
                //不显示二级列表
                this.items = false;
              }
            } else { //如果一级列表选中状态不正确,按第一项的的数据判断
              //判断是否有二级列表
              if (this.sideMenus.detailList[0].list) {
                //显示空的二级列表
                this.items = [];
              } else {
                //不显示二级列表
                this.items = false;
              }
            }
          },
          //修改选项
          changeSelect(index) {
            //记录tabIndex
            this.menu.selectIndex = this.selectIndexTab;
            //记录一级列表选项
            this.sideMenus.selectIndex = this.currentSelectIndex;
            if (this.items) {
              //确认二级列表选项
              this.items.selectIndex = index;
              //显示名称
              this.menu.name = this.items.list[this.items.selectIndex].name;
              this.menu.value = this.items.list[this.items.selectIndex].value;
            } else {
              //显示名称
              this.menu.name = this.sideMenus.detailList[this.sideMenus.selectIndex].name;
              this.menu.value = this.sideMenus.detailList[this.sideMenus.selectIndex].value;
            }
            this.$emit('changeSelect');
            this.closeDialog();
          },
          // 帅选修改选项
          changeRangeSelect() {
            this.menu.name = '筛选';
            for(var i in this.range){
              if(Object.keys(this.range[i].value).length == 0){
                delete this.range[i]
              }
            }
    
            this.menu.value = Object.keys(this.range).length > 0 ? this.range : '';
            this.$emit('changeSelect');
            this.closeDialog();
          },
          // 选择Tab菜单
          clickTab(tab, index) {
            if (index !== this.selectIndexTab) {
              //根据选中的tab初始化数据
              this.initData(index);
              this.$emit('changeTab', {
                tab,
                index
              })
            }
          },
          // 筛选方法
          clickFilterbar(v, I, i) {
            v.detailList[i].selectIndex = i;
            // debugger
            if(!this.range[I]){
              this.range[I] = {name: v.name, value: {}};
              this.range[I].value[i] = v.detailList[i].value;
            } else {
              if(!this.range[I].value[i]){
                this.range[I].value[i] = v.detailList[i].value;
              } else {
                delete this.range[I].value[i];
                v.detailList[i].selectIndex = -1;
              }
            }
          },
          // 点击左侧列表
          clickSidebar(v, i) {
            if (this.currentSelectIndex !== i) {
              this.currentSelectIndex = i;
              //存在二级列表
              if (this.sideMenus.detailList[this.currentSelectIndex].list) {
                this.items = this.sideMenus.detailList[this.currentSelectIndex];
              } else {
                //只有一级列表,记录选项,退出
                this.changeSelect();
              }
              this.$emit('changeMainItem', {
                v,
                i
              });
            }
          },
          // 点击右侧列表
          clickItem(v, i) {
            //只有一级列表,记录选项,退出
            this.changeSelect(i);
          },
          // 关闭弹框
          closeDialog() {
            this.visible = false;
            this.$emit('closeDialog');
          },
          // 提交已选内容
          handleEnsure() {
            this.changeRangeSelect();
            this.$emit('changeMainItem', this.range);
            // this.closeDialog();
          },
          // 清除已选内容
          handleClean() {
            this.menu.tabs.map(item => {
              item.detailList.map(_item => {
                _item.selectIndex = -1;
              })
            });
            this.range = {};
          }
        }
      }
      /**
        TODOS:
        1. 需要一个属性去辨别帅选项
        2. 多选
        3. 添加多选框
    
       */
    </script>
    
    <style lang="scss">
      .fade-enter-active,
      .fade-leave-active {
        transition: opacity .5s
      }
      .fade-enter,
      .fade-leave-active {
        opacity: 0
      }
      .filterbarpop-wrap {
        position: fixed;
         100%;
        top: 0;
        bottom: 0;
        left: 0;
        overflow: hidden;
        max-height: 100%;
        .filterbarpop-bg {
          position: fixed;
          top: 0;
          bottom: 0;
          left: 0;
           100%;
          background: rgba(0, 0, 0, .6);
        }
        .filterbarpop {
          position: absolute;
           100%;
          border-top: 1px solid #ccc;
          .tab-bar {
             100%;
            display: flex;
            display: -ms-flexbox;
            display: -moz-box;
            display: -webkit-box;
            display: -webkit-flex;
            flex-directives: row;
            -webkit-flex-direction: row;
            align-items: center;
            -webkit-align-items: center;
            -webkit-box-align: center;
            -moz-box-align: center;
            -ms-flex-align: center;
            height: 40px;
            .selected {
              border-bottom: 2px solid orange;
              box-sizing: border-box;
            }
            a {
              background: #fff;
              height: 100%;
              line-height: 40px;
              text-decoration: none;
              color: #323232;
              text-align: center;
            }
          }
          .main {
            display: flex;
            display: -webkit-flex;
            flex-direction: row;
            -webkit-flex-direction: row;
            height: 250px;
            background: #fff;
            .main-sidebar {
              flex: 0 0 50%;
              overflow: auto;
               100%;
            }
            .full-line {
              flex: 0 0 100%;
              div {
                text-align: left; // text-indent: 1.5em;
              }
            }
            .item-operation {
              display: inline-block;
              padding: 10px 4px 10px 4px;
              border: 1px solid rgb(91, 149, 255);
              border-radius: 3px;
              height: 0;
              line-height: 1px;
            }
            .multi-selected {
              background: rgb(91, 149, 255);
              color: #fff !important;
            }
            .filter-item {
              border-top: 1px solid #ccc;
              border-bottom: 1px solid #ccc;
              padding: 13px 0 5px 10px;
              span {
                margin-right: 8px;
                margin-bottom: 8px;
              }
            }
            .filter-name {
              padding: 10px 0 10px 10px;
            }
            .filter-btns {
              display: flex;
              display: -webkit-flex;
              flex-direction: row;
              -webkit-flex-direction: row;
              justify-content: space-around;
              -webkit-box-pack: space-around;
              -moz-box-pack: space-around;
              -ms-flex-pack: space-around;
              position: absolute;
              bottom: -40px;
               100%;
              line-height: 40px;
              z-index: 100;
              background: #fff;
              a {
                display: block;
                 100%;
                text-align: center;
                text-decoration: none;
                color: #ccc;
                border-top: 1px solid #ccc;
                &:last-child {
                  background: #39f;
                  color: #fff;
                }
              }
            }
            .main-list {
              flex: 0 0 50%;
              overflow: auto;
              span:active {
                background: #f5f5f5;
              }
            }
            .line-style {
              .item {
                text-align: left;
                margin-left: 10px;
                padding-left: 15px;
                border-bottom: 1px solid #ccc;
                position: relative;
                &.selected {
                  color: orange;
                  border-color: orange;
                  span {
                    color: orange;
                  }
                }
                .checkbox {
                  position: absolute;
                  right: 50px;
                  top: 10px;
                }
              }
            }
            .bg-style {
              .item {
                background-color: #f5f5f5;
                &.selected {
                  background-color: #FFF;
                }
              }
            }
            .item {
              display: inline-block;
              height: 40px;
              background: #fff;
              line-height: 40px;
               100%;
              text-decoration: none;
              color: #444;
              span {
                font-size: 14px;
                color: #888;
                margin-right: 10px;
                vertical-align: middle;
              }
              &:active {
                color: #fff;
              }
            }
          }
        }
      }
    </style>

    2.页面调用

    pages/FilterBarTest

    <!-- 移动端筛选条件 测试页 -->
    <template>
      <div>
        <!-- 标题栏 -->
        <x-header title="移动端筛选条件 测试页"></x-header>
        <!-- 内容部分 -->
        <FilterBar
          top="40"
          :barMenus="barMenus"
          @showDialog="handleShowDialog"
          @closeDialog="handleCloseDialog"
          @changeTab="handleChangeTab"
          @changeMainItem="handleChangeMainItem"
          @changeSelect="changeData">
        </FilterBar>
      </div>
    </template>
    
    <script>
      import { XHeader } from 'vux'
      // 引入组件
      import FilterBar from '../../components/FilterBar/FilterBar.vue'
      // 引入假数据
      import barMenus from './data.js';
    
      export default {
        name: 'FilterBarTest',
        components: {
          XHeader,
          FilterBar,
        },
        data(){
          return {
            barMenus: barMenus
          }
        },
        methods: {
          handleShowDialog(v) {
            // console.log(v);
          },
          handleCloseDialog(v) {
            // console.log(v);
          },
          handleChangeTab(v) {
            // console.log(v);
          },
          handleChangeMainItem(v) {
            // console.log(v)
          },
          changeData(v) {
            console.log(v);
          }
        }
      }
    </script>
    
    <style lang="scss" scoped>
      //
    </style>
    

    data.js

    export default [
    {
      name: '附近',
      icon: '',
      value: 'area',
      showTabHeader: true,
      defaultIcon: '',
      selectIcon: '',
      selectIndex: 0,
      tabs: [
        {
          icon: '',
          name: '商圈',
          selectIndex: 0,
          detailList: [
            {
              name: '附近',
              icon: '',
              selectIndex: 0,
              list: [{
                name: '默认',
                value: 'all'
              }, {
                name: '500米',
                value: '500'
              }, {
                name: '1000米',
                value: '1000'
              }]
            },
            {
              name: '朝阳区',
              icon: '',
              selectIndex: 1,
              list: [{
                name: '全部',
                value: 'all'
              }, {
                name: '建国门',
                value: 'jianguomen'
              }, {
                name: '亚运村',
                value: 'yayuncun'
              }]
            },
            {
              name: '海淀区',
              icon: '',
              selectIndex: 2,
              list: [{
                name: '全部',
                value: 'all'
              }, {
                name: '中关村',
                value: 'zhongguancun'
              }, {
                name: '五道口',
                value: 'wudaokou'
              }]
            }
          ]
        },
        {
          icon: '',
          name: '地铁沿线',
          selectIndex: 1,
          detailList: [
            {
              name: '1号线',
              icon: '',
              selectIndex: 0,
              list: [{
                name: '平果圆',
                value: 'pingguoyuan'
              }, {
                name: '古城',
                value: 'gucheng'
              }, {
                name: '八角游乐园',
                value: 'bajiaoyouleyuan'
              }]
            },
            {
              name: '2号线',
              icon: '',
              selectIndex: 1,
              list: [{
                name: '积水潭',
                value: 'jishuitan'
              }, {
                name: '鼓楼大街',
                value: 'guloudajie'
              }, {
                name: '安定门',
                value: 'andingmen'
              }]
            },
            {
              name: '4号线',
              icon: '',
              selectIndex: 2,
              list: [{
                name: '安和桥北',
                value: 'anheqiaobei'
              }, {
                name: '北宫门',
                value: 'beigongmen'
              }, {
                name: '西宛',
                value: 'xiwan'
              }]
            }
          ]
        }
      ]
    },
    {
      name: '菜系',
      icon: '',
      value: 'food',
      showTabHeader: false,
      defaultIcon: '',
      selectIcon: '',
      selectIndex: 0,
      tabs: [
        {
          icon: '',
          name: '',
          selectIndex: 0,
          detailList: [
            {
              name: '全部',
              icon: '',
              value: '全部',
              selectIndex: 0,
              list: [{
                name: "全部",
                value: 'all'
              }]
            },
            {
              name: '中餐馆',
              icon: '',
              value: '中餐馆',
              selectIndex: 1,
              list: [{
                name: '全部',
                value: 'all'
              }, {
                name: '火锅',
                value: 'hot pot'
              }, {
                name: '川菜',
                value: 'Sichuan cuisine'
              }]
            },
            {
              name: '西餐馆',
              icon: '',
              value: '西餐管',
              selectIndex: 2,
              list: [{
                name: '全部',
                value: 'all'
              }, {
                name: '披萨',
                value: 'pizza'
              }, {
                name: '牛排',
                value: 'steak'
              }]
            }
          ]
        }
      ]
    },
    {
      name: '排序',
      icon: '',
      value: 'compositor',
      showTabHeader: false,
      defaultIcon: '',
      selectIcon: '',
      selectIndex: 0,
      tabs: [
        {
          icon: '',
          name: '',
          selectIndex: 0,
          detailList: [
            {
              name: '只能排序',
              icon: '',
              value: '0',
              selectIndex: 0
            },
            {
              name: '离我最近',
              icon: '',
              value: '1',
              selectIndex: 1
            },
            {
              name: '评价最好',
              icon: '',
              value: '2',
              selectIndex: 2
            }
          ]
        }
      ]
    },
    {
      name: '筛选',
      icon: '',
      value: 'filter',
      type: 'filter',
      showTabHeader: false,
      defaultIcon: '',
      selectIcon: '',
      selectIndex: 0,
      tabs: [
        {
          icon: '',
          name: '价格',
          selectIndex: 0,
          detailList: [
            {
              name: '0-50',
              value: '0-50',
              selectIndex: -1
            },
            {
              name: '50-100',
              value: '50-100',
              selectIndex: -1
            },
            {
              name: '100-150',
              value: '100-150',
              selectIndex: -1
            },
            {
              name: '150-200',
              value: '150-200',
              selectIndex: -1
            },
            {
              name: '200-250',
              value: '200-250',
              selectIndex: -1
            },
            {
              name: '300-350',
              value: '300-350',
              selectIndex: -1
            }
          ]
        },{
          icon: '',
          name: '入住类型',
          selectIndex: 1,
          detailList: [
            {
              name: '不限',
              value: 'all',
              selectIndex: -1
            }, {
                name: '全日房',
                value: 'daily',
                selectIndex: -1
            }, {
                name: '钟点房',
                value: 'time',
                selectIndex: -1
            },
            {
              name: '支持团购',
              value: 'group buy',
              selectIndex: -1
            }
          ]
        }
      ]
    }]
    

    3.效果图

      

    .

  • 相关阅读:
    WebClient设置Expect: 100-continue
    ActiveX控件注册不起作用的解决办法
    RadioButtonFor值为false.默认选中的问题
    Ueditor插入script标签
    Ueditor上传图片到本地改造到上传图片到七牛云存储
    让网页显示ajax的查询数据
    今天升级win10.vs调试程序各种崩溃
    visual assist x vs2012不智能提示
    几道 javascript 题,你全对了吗?
    Node.js中使用TCP套接字编程
  • 原文地址:https://www.cnblogs.com/crazycode2/p/8849448.html
Copyright © 2011-2022 走看看