table列表复现框【勾选-搜索-再勾选】功能

250 阅读1分钟
一、项目应用场景
  1. 最初需求:单纯要求封装【查询名称】的弹窗组件,能够进行勾选,名称展示到页面搜索条件的搜索框内。
  2. 实际考虑:在名称过多的情况下,用户不可能一页页点击table列表页码去查找对应的名称,如果能够直接搜索到想要的名称进行勾选,肯定能更好的提升用户体验。
二、代码实现

  第一步:封装查询名称弹窗组件

<template>
  <el-dialog
    width="70%"
    title="名称查询"
    append-to-body
    class="search-name"
    :visible.sync="dialogVisible"
    :before-close="closeDialog"
  >
    <div class="search-box">
      <div class="search-box-input">
        <span>名称:</span>
        <el-input
          clearable
          placeholder="请输入"
          v-model.trim="Name"
        ></el-input>
      </div>
      <el-button type="primary" class="search-btn" @click="handQuery">
        查 询
      </el-button>
    </div>
    <div class="tree-box" ref="treeBox" @scroll="handleScroll">
      <el-table
        :data="treedata"
        border
        ref="multipleTable"
        :row-style="rowStyle"
        :cell-style="cellStyle"
        @selection-change="handleChangeSelection"
        empty-text="暂无数据"
      >
        <el-table-column
          type="selection"
          width="55"
          align="center"
        ></el-table-column>
        <el-table-column
          type="index"
          label="序号"
          width="85"
          align="center"
          :index="(index) => indexrank(index, this.pagenumber, this.pagesize)"
        ></el-table-column>
        <el-table-column
          property="name"
          label="名称"
          align="center"
        ></el-table-column>
      </el-table>
    </div>
    <div class="list-pagination">
      <Paging
        ref="refPaging"
        :total="parseInt(palnTotal)"
        :pagesize="parseInt(pagesize)"
        @pageNumChange="pageNumChange"
      ></Paging>
    </div>
    <div slot="footer">
      <el-button @click="submitVal" type="primary" class="submit-btn">
        确 认
      </el-button>
      <el-button @click="closeDialog()" class="cancel-btn">取 消</el-button>
    </div>
  </el-dialog>
</template>
<script>
import commonService from '@/api/common.js';
import Paging from '@/components/paging.vue';

export default {
  props: {
    dialogVisible: {
      type: [Boolean],
      default: false,
    },
    // 重置时清空选中数据
    resetData: {
      type: Array,
      default: [],
    },
  },
  components: {
    Paging,
  },
  data() {
    return {
      stopReBack: false, // 防止回显成功前对表格进行操作的标识
      scrollTop: 0,
      palnTotal: 0,
      Name: '',
      treedata: [],
      pagesize: 10,
      pagenumber: 1,
      checkData: [], // 选中的名称集合
      rowStyle: { height: '32px' },
      cellStyle: { padding: '8px' },
    };
  },
  watch: {
    dialogVisible(val) {
      if (val) {
        this.$nextTick(() => {
          this.clearInfo();
          this.$refs.refPaging.pageChange(1);
        });
      }
    },
    resetData(val) {
      this.checkData = val;
    },
  },

  methods: {
    clearInfo() {
      this.Name = '';
    },
    // 手动搜索
    handQuery() {
      this.pagenumber = 1; // 重置页码
      this.queryName();
    },

    // 获取名称
    queryName() {
      let para = {
        pagesize: this.pagesize,
        pagenumber: this.pagenumber,
        Name: this.Name ? this.Name : ''
      };
      commonService.queryName(para).then((res) => {
        if (res && res.code == 200) {
          if (res.data) {
            this.treedata = res.data.list;
            this.stopReBack = true;
            setTimeout(() => {
              this.checkData.map((item) => {
                const b = this.treedata.find(
                  (it) => item.id == it.id,
                );
                if (b) {
                  this.$refs.multipleTable.toggleRowSelection(b, true);
                }
              });
              this.stopReBack = false;
            }, 50);
            this.Total = res.data.total;
          }
        } else {
          this.$message.error(res.message);
        }
      });
    },

    pageNumChange(val) {
      this.pagenumber = val;
      this.queryName();
    },

    // 选择方法
    handleChangeSelection(value) {
      if (this.stopReBack) {
        return false;
      }

      const userList = JSON.parse(JSON.stringify(this.treedata)); //当前页、接口返回的数组
      const selected = []; // 当前页选中数据
      const unSelected = []; // 当前页未选中数据

      // 遍历当前页数据,查找选中数据,分别放置不同的数组中
      userList.map((item) => {
        const b = value.find((it) => item.id == it.id);
        b ? selected.push(item) : unSelected.push(item);
      });

      // 当前页选中数据selected遍历,在全部选中数据匹配查找,全部选中数据checkData中没有的信息,就塞入
      selected.map((item) => {
        const b = this.checkData.find((it) => item.id == it.id);
        if (!b) {
          this.checkData.push(item);
        }
      });

      // 当前页未选中数据unSelected遍历,在全部选中数据匹配查找,全部选中数据checkData中有的信息,就删除
      unSelected.map((item) => {
        this.checkData.map((it, index) => {
          if (item.id == it.id) {
            this.checkData.splice(index, 1);
          }
        });
      });
    },

    // 确认选中的,回显在父组件输入框
    submitVal() {
      if (this.checkData.length === 0) {
        this.$message.error('请选择名称!');
        return false;
      } else {
        this.$emit('getData', this.checkData);
        this.closeDialog();
      }
    },

    // 关闭弹窗
    closeDialog() {
      this.$emit('close');
    },

    // 弹窗高度
    handleScroll() {
      this.scrollTop = this.$refs.treeBox.scrollTop;
    },
  },
};
</script>
<style scoped>
.search-name >>> .el-dialog__body {
  padding: 10px 30px 0px;
  border-top: 1px solid #ececec;
}
.search-name >>> .el-dialog__header {
  padding: 15px 20px;
}
.search-name >>> .el-dialog__headerbtn {
  top: 12px;
}

.search-name >>> .el-dialog__headerbtn .el-dialog__close {
  font-size: 25px;
  display: block;
}

.search-box >>> .el-input {
  width: 230px;
}
.search-box >>> .el-input--suffix .el-input__inner {
  height: 32px;
}

.search-box {
  display: flex;
  flex-direction: row;
  align-items: center;
  padding-left: 10px;
  justify-content: space-between;
}

.search-box >>> .el-input {
  width: 170px;
}

.search-box >>> .el-input input {
  width: 170px;
  height: 32px;
}

.search-box >>> .el-input__icon {
  line-height: 32px;
}
.search-box-input {
  display: flex;
  flex-direction: column;
}
.tree-box {
  margin-top: 16px;
  padding: 0 12px;
  max-height: 200px;
  overflow: auto;
}

.search-btn {
  width: 85px;
  height: 32px;
  padding: 0;
  margin-top: 14px;
  background-color: #2e78ff;
}

.submit-btn {
  width: 64px;
  height: 32px;
  padding: 0;
  background-color: #2e78ff;
}

.cancel-btn {
  width: 64px;
  height: 32px;
  padding: 0;
  background-color: #fafafa;
  border-color: #ddd;
  color: #666;
  margin-left: 4px;
}

.list-pagination {
  padding: 22px 0 10px;
  box-sizing: border-box;
  display: flex;
  justify-content: space-between;
}
</style>

  第二步:组件应用

<template>
<NameDialog
  :resetData="resetData" // 页面中点击重置按钮时,要清空弹窗中全选中数据
  :dialogVisible="dialogVisible"
  @getData="getData"
  @close="closeDialog"
></NameDialog>
</template>

  第三步:组件方法

<script>
import NameDialog from '@/components/searchName.vue';
export default {
 components: {
   NameDialog
 },
 data(){
  return {
    form: {
      name: ''
    },
    resetData: [], //重置选中数据
    dialogVisible: false, // 名称弹窗状态
  }
 },
 methods:{
     // 获取名称
    getData(val) {
      let Names = [];
      val.forEach((item) => {
        Names.push(item.name);
      });
      this.form.name = Names.join(',');
    },

    // 关闭计划名称弹窗
    closeDialog() {
      this.dialogVisible = false;
    },
 }
}
</script>

三、总结
  1. 名称组件封装,关注每次【打开弹窗】或点击【重置】按钮时,组件中接口查询或者页面展示数据的清除问题;
  2. 需求存在多级联动,例:省、市、区这种情况下更需要注意。