vue实现前台列表数据过滤搜索(多条件模糊),分页效果

这里写图片描述

job.vue页面

<style lang="scss">
.job-wrapper {
  padding-top: 50px;
}
.job-left {
  float: left;
  margin-right: 20px;
  padding: 20px;
  width: 310px;
  background: #fff;
}
.job-serach-title {
  margin: 8px 0 10px 0;
  color: rgb(51, 51, 51);
  font-size: 16px;
}

.job-search-input {
  display: flex;
}
.job-keywords {
  width: 400px;
}
.job-search-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-left: 10px;
  width: 50px;
  height: 40px;
  border-radius: 4px;
  background-color: rgb(254, 62, 98);
}
.line {
  margin: 25px 0 0 0;
  width: 100%;
  height: 1px;
  background: #dfdfdf;
}

.halogg {
  margin-top: 30px;
  color: rgb(102, 102, 102);
  font-weight: 300;
  font-size: 14px;
}
.job-right {
  float: right;
  width: 870px;
  background: #fff;
}
</style>

<style lang="scss">
// 重置样式
#job-select-1,
#job-select-2 {
  margin-top: 20px;
  select { width: 100%;
  }
}
</style>
<template>
  <article class="job">

    <div class="job-content layout">
      <div class="job-wrapper">
        <div class="job-left">
          <div class="job-serach-title">搜索更多职位</div>
          <div class="job-search-input">
            <input v-model.trim="formData.keywords" @change="searchData" placeholder="搜索更多职位" class="job-keywords" />
            <div class="job-search-btn pointer" @click="searchData"></div>
          </div>
          <div class="line"></div>
          <div class="job-select" id="job-select-1">
            <select v-model="formData.address" @change="searchData">
              <option v-for="item,index in regionArr" :key="index">{{item.name}}</option>
            </select>
          </div>
          <div class="job-select" id="job-select-2">
            <select v-model="formData.title" @change="searchData">
              <option v-for="(item,index) in searchList" :key="index">{{item}}</option>
            </select>
          </div>
        </div>
        <div class="job-right">
          <joblist></joblist>
        </div>
      </div>

    </div>
  </article>
</template>
<script>
import joblist from 'src/components/job/list';
import { mapGetters, mapActions, mapMutations } from 'vuex';

export default {
  name: 'reportFormIndex',
  data() {
    return {
      formData: {
        title: '',
        address: '',
        keywords: '',
      },
    };
  },
  computed: {
    ...mapGetters(['searchList', 'regionArr', 'show']),
  },
  watch: {},
  title() {
    return '行业';
  },
  methods: {
    ...mapActions(['getData']),
    // select 选中后的回调数据
    searchData() {
      const payload = {
        formData: Object.assign({}, this.formData),
        pageIndex: 0, // 每次搜索后, 显示搜索结果的第一页
      };
      this.$store.commit('setState', payload);
    },
  },
  mounted() {
    this.$nextTick(() => {
      this.getData();
    });
  },
  components: {
    joblist,
  },
};
</script>

组件list.vue

<style lang="scss">
.list-header {
  position: relative;
  display: flex;
  padding: 25px 30px 20px 30px;
  color: rgb(153, 153, 153);
  font-size: 14px;
  &:after { position: absolute;
    right: 0;
    bottom: 0;
    left: 0;
    display: inline-block;
    width: 100%;
    height: 1px;
    background-color: #dfdfdf;
    content: '';
  }
}
.l-header-item-1 {
  padding-left: 20px;
  width: 37.3%;
}
.l-header-item-2 {
  padding-left: 10px;
  width: 32.7%;
}
.l-header-item-3 {
  padding-left: 10px;
  width: 18.7%;
}
.l-header-item-4 {
  display: flex;
  width: 11.3%;
  .open { color: #3e8bf5;
    text-decoration: underline;
    font-size: 14px;
  }
  .arrow-open {
    margin-top: 5px;
    margin-left: 5px;
    width: 11px;
    height: 7px;
    transition: all 0.5s linear;
  }
}
.inner-item {
  padding: 0 30px;
}
.inner-box {
  position: relative;
  display: flex;
  padding: 25px 0;
  color: rgb(51, 51, 51);
  font-size: 16px;
  transition: all 0.5s linear;
  &:after { position: absolute;
    right: 0px;
    bottom: 0;
    left: 0px;
    display: inline-block;
    height: 1px;
    background-color: #dfdfdf;
    content: '';
  }
}
//
.list-item {
  &.active { .list-show-detail { visibility: visible;
      padding: 0 50px;
      max-height: 1000px;
      transition: all 0.5s linear;
    }
    .inner-box {
      background: #f2f2f2;
      transition: all 0.5s linear;
      &:after { background-color: transparent;
      }
    }
    .arrow-open {
      transition: all 0.5s linear;
      transform: rotate(-180deg);
    }
  }
}
.list-show-detail {
  visibility: hidden;
  max-height: 0;
  transition: all 0.5s linear;
}
.list-task-title {
  margin: 25px 0 15px 0;
  color: rgb(51, 51, 51);
  font-size: 14px;
}

.list-task-item {
  color: rgb(102, 102, 102);
  font-size: 14px;
  line-height: 1.714;
}
.list-apply {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 25px 0 30px 0;
  width: 140px;
  height: 50px;
  border-radius: 4px;
  background-color: rgb(254, 62, 98);
  color: rgb(255, 255, 255);
  font-size: 16px;
}

/////pagination
.job-pagination {
  padding: 50px 0;
  .pagination-wrapper { display: flex;
    justify-content: center;
    margin: 0 auto;
    width: 100%;
    .subscript { display: flex;
      align-items: center;
      justify-content: center;
      margin: 0 5px;
      width: 28px;
      height: 28px;
      border: 1px solid rgb(223, 223, 223);
      border-radius: 4px;
      color: blue;
      color: rgb(102, 102, 102);
      text-align: center;
      font-size: 14px;

      &.active { border: 1px solid rgb(254, 62, 98);
        background-color: rgb(254, 62, 98);
        color: #fff;
      }
    }
    .pagination-page {
      display: inline-block;
      width: 7px;
      height: 11px;
      background-image: url('./images/arrow.png');
      &.pagination-next { transform: rotate(180deg);
      }
    }
  }
}
////
.job-no-data {
  padding: 100px 0;
  .job-no-data-img { margin: 0 auto;
    width: 170px;
    height: 170px;
    background-image: url('./images/[email protected]');
    background-size: cover;
    background-repeat: no-repeat;
  }
  .job-no-data-msg {
    margin-top: 10px;
    color: rgb(51, 51, 51);
    text-align: center;
    font-size: 18px;
    line-height: 2.778;
  }
}

@media only screen and (max-width: 1200px) {
  .list-header {
    padding: 25px 30px 20px 30px;
  }
}

@media only screen and (max-width: 767px) {
  .list-header {
    padding: 20px 15px 15px 15px;
  }
  .inner-item {
    padding: 0px 15px 0 15px;
    &:after { right: 15px;
      left: 15px;
      transform: scaleY(0.5);
    }
  }

  .l-header-item-1 {
    padding-left: 10px;
  }
  .l-header-item-2 {
    padding-left: 10px;
    width: 28.7%;
  }
  .l-header-item-3 {
    padding-left: 10px;
    width: 19.7%;
  }
  .l-header-item-4 {
    width: 14.3%;
  }
}
</style>
<template>
  <article id="list">
    <ul class="list-wrapper">
      <li class="list-header">
        <div class="l-header-item-1">职位名称</div>
        <div class="l-header-item-2">职位分类</div>
        <div class="l-header-item-3">所在地区</div>
        <div class="l-header-item-4"></div>
      </li>
      <li class="list-item" v-for="(item,index) in curList" :key="index" :class="{'active':item.show}" v-show="curList.length">
        <div class="inner-item">
          <div class="inner-box">
            <div class="list-position l-header-item-1">{{item.position}}</div>
            <div class="list-title l-header-item-2">{{item.title}}</div>
            <div class="list-address l-header-item-3">{{item.address}}</div>
            <div class="list-action l-header-item-4 pointer" @click="showHandler(item.id)">
              <span class="open">展开</span>
              <img src="./images/arrow-open.png" alt="" class="arrow-open">
            </div>
          </div>
        </div>
        <transition name="el-zoom-in-top">
          <div class="list-show-detail" v-show="item.show">
            <div class="list-task-title">岗位职责:</div>
            <div class="list-task-wrapper">
              <div class="list-task-item" v-for="(item2,index2) in item.task" :key="index2">{{item2}}</div>
            </div>
            <div class="list-task-title">岗位要求:</div>
            <div class="list-task-wrapper">
              <div class="list-task-item" v-for="(item3,index3) in item.rule" :key="index3">{{item3}}</div>
            </div>
          </div>
        </transition>
      </li>
      <li class="job-no-data" v-show="!curList.length">
        <div class="job-no-data-img"></div>
        <div class="job-no-data-msg">暂未合适的职位</div>
      </li>
      <li class="job-pagination" v-show="curList.length">
        <div class="pagination-wrapper">
          <span class="subscript pointer" @click="prev">
            <span class="pagination-prev pagination-page"></span>
          </span>
          <span class="subscript pointer" @click="selectPageHandler(index - 1)" v-for="index in pageLength" :key="index" :class="{active: pageIndex === index - 1}">{{index}}</span>
          <span class="subscript pointer" @click="next">
            <span class="pagination-next pagination-page"></span>
          </span>
        </div>
      </li>
    </ul>

  </article>
</template>
<script>
import { mapState, mapGetters, mapActions, mapMutations } from 'vuex';

const PER_PAGE = 8; // 每页显示多少个

export default {
  name: 'list',
  data() {
    return {};
  },
  computed: {
    ...mapState({
      // pageIndex: state => state.job.pageIndex,
    }),
    ...mapGetters(['filterJobList', 'pageIndex']),
    curList() {
      const { filterJobList, pageIndex } = this;
      const startIndex = pageIndex * PER_PAGE;
      const endIndex = startIndex + PER_PAGE;
      return filterJobList.slice(startIndex, endIndex);
    },
    pageLength() {
      const { filterJobList } = this;
      if (filterJobList.length) {
        return Math.ceil(filterJobList.length / PER_PAGE);
      }
      return 0;
    },
  },
  methods: {
    ...mapActions(['showAndHide']),
    // 操作 展开 隐藏
    showHandler(id) {
      this.showAndHide(id);
    },
    selectPageHandler(pageIndex) {
      this.$store.commit('setState', {
        pageIndex,
      });
      //同时关闭已经打开的职位详情页
      this.$store.commit('hideAllDetailMutations');
    },
    // 上一页
    prev() {
      this.$store.commit('prevMutations');
    },
    // 下一页
    next() {
      this.$store.commit('nextMutations', this.pageLength);
    },
  },
  mounted() {},
  components: {},
};
</script>

store/job.js

import { unique } from 'src/assets/script/util.js';
import jobData from 'src/views/job/data.js';

// 初始状态
const state = {
    realData: [],
    searchList: [],
    regionArr: [{
            name: '上海',
            id: 1,
        },
        {
            name: '武汉',
            id: 2,
        },
    ],
    // 右侧搜索,用户输入
    formData: {
        title: '', // 职位分类
        address: '', // 地区
        keywords: '', // 搜索更多职位
    },
    pageIndex: 0, //0show: false, // 申请工作的 modal
    ApplyJobPosition: '' // 申请工作的职位
};

// 读取数据
const getters = {
    ApplyJobPosition: state => state.ApplyJobPosition,
    show: state => state.show,
    pageIndex: state => state.pageIndex,
    regionArr: state => state.regionArr,
    searchList: state => {
        const cache = [];
        state.realData.forEach(n => {
            cache.push(n.title);
        });
        return unique(cache);
    },
    //   符合条件的职位
    filterJobList({ realData, formData }) {
        const { title, address, keywords } = formData;

        return (
            realData
            // 职位筛选逻辑
            .filter(item => {
                let matchAddress = true; // 地区筛选
                let matchPosition = true; // 职位筛选
                let matchKeywrod = true; // 关键字 筛选
                if (title) {
                    matchPosition = item.title === title;
                }
                if (address) {
                    matchAddress = item.address === address;
                }
                if (keywords) {
                    // 模糊搜索;
                    const keys = keywords
                        .toUpperCase() // 转大写
                        .replace(' ', '') // 删掉空格
                        .split(''); // 切割成 单个字

                    matchKeywrod = keys.every(key => item.position.toUpperCase().includes(key));
                }
                return matchAddress && matchPosition && matchKeywrod;
            })
        );
    },
};

// 数据改变
const mutations = {

    // 从json文件直接获取元数据
    getDataMutations(state, jobData) {
        state.realData = jobData;
    },
    // 职位详情 显示/隐藏
    showAndHideMutations(state, id) {
        state.realData.forEach((n, i) => {
            if (id === n.id) {
                n.show = !n.show;
            }
        });
    },
    // 职位详情 全部隐藏
    hideAllDetailMutations(state) {
        state.realData.forEach((n, i) => {
            n.show = false;
        });
    },
    setState(state, payload = {}) {
        // console.log('payload', payload);
        Object.entries(payload).forEach(([key, value]) => {
            state[key] = value;
        });
    },
    // prev
    prevMutations(state, payload = {}) {
        if (!state.pageIndex) {
            return;
        }
        state.pageIndex--
    },
    // next
    nextMutations(state, payload = {}) {
        // console.info(state.pageIndex, payload)
        if (state.pageIndex < payload - 1) {
            state.pageIndex++
        }

    },
    // open modal
    openApplyJobModal(state, payload = {}) {
        state.show = true
        state.ApplyJobPosition = payload
    },
    //close modal
    closeApplyJobModal(state) {
        state.show = false
    },
};

// 逻辑响应
const actions = {
    getData({ commit }) {
        commit('getDataMutations', jobData);
    },
    // 显示 隐藏
    showAndHide({ commit }, id) {
        commit('showAndHideMutations', id);
    },
};

export default {
    state,
    getters,
    actions,
    mutations,
};

util.js

// 数组去重
export function unique(arr) {  
    var newArr = [arr[0]];  
    for (var i = 1; i < arr.length; i++) {    
        if (newArr.indexOf(arr[i]) == -1) {   newArr.push(arr[i]);   }
    }
    return newArr;
}

猜你喜欢

转载自blog.csdn.net/zhooson/article/details/80057184