vue+element之一个table为载体的穿梭框

有个类似穿梭框的弹窗需求,看了下element-ui的Transfer组件,和原型查了很多。所以自己写了一个,原型图如下:

image

估计也没人看,直接放源码防止以后遇到同样需求:

<template>
  <div class="select-people">
    <div class="left">
      <div class="title-box">
        <span class="tit">待选人员</span>
        <el-input
          style="width: 280px"
          placeholder="搜索人员姓名"
          v-model="params.realName"
          class="input-with-select"
        >
          <el-button
            @click="getTeacher(true)"
            slot="append"
            icon="el-icon-search"
          ></el-button>
        </el-input>
      </div>
      <div class="b-cont">
        <div class="l-tree">
          <el-tree
            ref="tree"
            @node-click="checkChange"
            check-on-click-node
            :props="props"
            :load="loadNode"
            highlight-current
            node-key="id"
            lazy
          >
          </el-tree>
        </div>
        <div class="r-table">
          <el-table
            row-class-name="row-cn"
            header-row-class-name="no-bg"
            s
            :data="tableData"
          >
            <el-table-column type="index" width="50" label="序号">
            </el-table-column>
            <el-table-column show-overflow-tooltip prop="realName" label="姓名">
            </el-table-column>
            <el-table-column
              show-overflow-tooltip
              width="140"
              prop="serialNo"
              label="学工号"
            >
            </el-table-column>
            <el-table-column label="操作">
              <template slot-scope="scope">
                <el-button
                  type="text"
                  :disabled="
                    scope.row.selected || (scope.row.status === 1 && multi)
                  "
                  :class="
                    scope.row.selected || (scope.row.status === 1 && multi)
                      ? 'txt-info'
                      : 'txt-primary'
                  "
                  @click="addPeople(scope.row)"
                  >添加</el-button
                >
              </template>
            </el-table-column>
          </el-table>
          <div class="page-box">
            <el-pagination
              @current-change="pageChange"
              layout="prev, pager, next"
              :total="total"
            >
            </el-pagination>
          </div>
        </div>
      </div>
    </div>
    <div class="right">
      <div class="title-box">已选人员({{ allSelectedData.length }})</div>
      <div class="b-table">
        <el-table
          row-class-name="row-cn"
          header-row-class-name="no-bg"
          :data="selectedData"
        >
          <el-table-column type="index" width="50" label="序号">
          </el-table-column>
          <el-table-column show-overflow-tooltip prop="realName" label="姓名">
          </el-table-column>
          <el-table-column
            show-overflow-tooltip
            width="120"
            prop="serialNo"
            label="学工号"
          >
          </el-table-column>
          <el-table-column label="操作">
            <template slot-scope="scope">
              <el-button
                type="text"
                class="txt-danger"
                @click="delPeople(scope)"
                >删除</el-button
              >
            </template>
          </el-table-column>
        </el-table>
        <div class="page-box">
          <el-pagination
            @current-change="selectedPageChange"
            layout="prev, pager, next"
            :total="selectedTotal"
          >
          </el-pagination>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import {
  getTreeData,
  getTeacherList,
  getCTeacherOrManagerList,
} from "@/api/application/school-manage.js";

export default {
  props: {
    multi: {
      default: true,
    },
    url: {
      default: "",
    },
  },
  data() {
    return {
      props: {
        label: "name",
        children: "children",
      },
      tableData: [],
      allSelectedData: [],
      selectedData: [],
      selectedTotal: 0,
      selectedCurrentPage: 1,
      total: 0,
      params: {
        id: "",
        pageNo: 1,
        pageSize: 10,
        realName: "",
      },
    };
  },
  methods: {
    async loadNode(node, resolve) {
      const data = node?.data || { id: 0 };
      console.log("one time");
      const { id } = data;
      if (node.level === 0) {
        const child = await this.handleGetTreeData(id);
        child.unshift({
          id: "all",
          name: "全部",
          leaf: true,
        });
        return resolve(child);
      }
      if (node.level === 1) {
        console.log("node", node);
        if (node.data.id === "all") {
          this.handleAllClick();
          return resolve([]);
        } else {
          const child = await this.handleGetTreeData(id);
          return resolve(child);
        }
      } else if (node.level === 2) {
        this.params.pageNo = 1;
        const child = await this.handleGetTreeData(id, true);
        return resolve(child);
      } else {
        resolve([]);
      }
    },
    pageChange(v) {
      this.params.pageNo = v;
      this.getTeacher();
    },
    addPeople(row) {
      {
        // 判断是否多选
        if (!this.multi && this.allSelectedData.length >= 1) {
          this.$message({
            message: "学工管理员只能选择一个",
            type: "warning",
          });
          return;
        }
      }
      row.selected = true;
      this.allSelectedData.push({
        ...row,
        selected: false,
      });
      this.selectedTotal = this.allSelectedData.length;
      this.getCurrentPageSelected();
    },
    // 用于截取指定页数的已选择数据
    getCurrentPageSelected() {
      const allSelectedData = JSON.parse(JSON.stringify(this.allSelectedData));
      this.selectedData = allSelectedData.splice(
        (this.selectedCurrentPage - 1) * 10,
        10
      );
    },
    delPeople(scope) {
      const { $index, row } = scope;
      const { serialNo } = row;
      this.allSelectedData.splice($index, 1);
      // 判断已搜索的人员是否包含删除的这条
      const i = this.tableData.findIndex((e) => e.serialNo === serialNo);
      if (i >= 0) {
        this.tableData[i].selected = false;
      }
      this.selectedTotal = this.allSelectedData.length;
      // 若删除以后所有已选择的数据长度小于等于10,则直接将所有已选数据复制给当前页的已选择数据
      if (this.selectedTotal <= 10) {
        this.selectedData = this.allSelectedData.slice();
        // 如果所有已选数据长度小于等于10,但是已选表格的pageNo大于1时,将pageNo置为1
        if (this.selectedCurrentPage > 1) {
          this.selectedCurrentPage = 1;
        }
      } else {
        // 如果当前已选择的页码pageNo与所有的已选择数据长度/10不一致时,将其减1再执行数据截取操作
        if (this.selectedCurrentPage > Math.ceil(this.selectedTotal / 10)) {
          this.selectedCurrentPage = this.selectedCurrentPage - 1;
        }
        this.getCurrentPageSelected();
      }
    },
    async handleGetTreeData(pid, leaf) {
      const res = await getTreeData({ id: pid });
      const data = res?.data;
      if (!leaf) leaf = false;
      return data.map((e) => {
        const { name, id } = e;
        return {
          name,
          id,
          leaf,
        };
      });
    },
    checkChange(v) {
      const { id } = v;
      if (id === "all") return;
      this.params.id = id;
      this.getTeacher();
    },
    // 获取教师列表
    getTeacher() {
      this.tableData = [];
      // 处理一下已经选择的
      const selectedSerialNo = this.allSelectedData.map((e) => e.serialNo);
      getCTeacherOrManagerList(this.params, this.url).then((res) => {
        const data = res?.data || {};
        this.total = data.total;
        const realD = data?.data || [];
        this.tableData = realD.map((e) => {
          return {
            ...e,
            selected: false,
          };
        });
        this.tableData.forEach((e) => {
          if (selectedSerialNo.indexOf(e.serialNo) >= 0) {
            e.selected = true;
          }
        });
      });
    },
    // 已选择的改变
    selectedPageChange(v) {
      this.selectedCurrentPage = v;
      this.getCurrentPageSelected();
    },
    handleAllClick() {
      this.params.id = "";
      this.params.realName = "";
      this.params.pageNo = 1;
      this.getTeacher();
    },
    clear() {
      this.allSelectedData = [];
      this.selectedData = [];
      this.selectedTotal = 0;
      this.tableData = [];
      this.total = 0;
      this.params.pageNo = 1;
      this.params.realName = "";
      this.params.id = "";
      this.selectedCurrentPage = 1;
      this.$refs.tree.setCurrentKey(null);
    },
  },
};
</script>

<style lang="scss">
.select-people {
  height: 504px;
  display: flex;
  justify-content: space-between;
  .left {
    width: 580px;
    .title-box {
      justify-content: space-between;
    }
    .b-cont {
      display: flex;
      justify-content: space-between;
      height: calc(100% - 50px);
      border: 1px solid rgba(228, 231, 237, 1);
      border-top: none;
      > div {
        height: 100%;
      }
      .l-tree {
        width: 230px;
        border-right: 1px solid rgba(228, 231, 237, 1);
        overflow-y: auto;
      }
      .r-table {
        width: 350px;
        position: relative;
      }
    }
  }
  .page-box {
    display: flex;
    align-items: center;
    height: 39px;
    flex-direction: row-reverse;
    position: absolute;
    width: 100%;
    bottom: 0;
  }
  .right {
    width: 336px;
    .b-table {
      height: calc(100% - 50px);
      border: 1px solid rgba(228, 231, 237, 1);
      border-top: none;
      position: relative;
    }
  }
  .title-box {
    font-size: 16px;
    border: 1px solid rgba(228, 231, 237, 1);
    color: rgba(48, 49, 51, 1);
    display: flex;
    align-items: center;
    height: 50px;
    background-color: rgba(245, 247, 250, 1);
    padding: 0 14px;
  }
  .no-bg {
    th {
      background-color: white;
    }
  }
  .row-cn {
    td {
      padding: 0;
      height: 36px;
    }
  }
  .txt-info {
    color: rgba(235, 238, 245, 1);
  }
  .txt-danger {
    color: #f56c6c;
  }
  .txt-primary {
    color: #409eff;
  }
}
</style>

posted @ 2022-06-24 15:57  俄罗斯方块  阅读(884)  评论(1编辑  收藏  举报