vue2 elementUI 封装列表多选组件,翻页回显选择项,全选非全选正确显示

列表翻页多选全选

操作过程,每页选中的选项会在右侧显示,切换每一页选项正确回显,全选按钮正确回显,删除右侧数据左侧不选中,左右两边的数据能正确交互。

1. 弹窗组件

  • 使用了 el-dialog 组件作为弹窗容器,支持关闭、取消、确定等操作。

  • 弹窗的显示状态由 dialogVisible 控制,并通过 visible 属性从父组件传入。

  • 弹窗关闭时会触发 closed 事件,调用 closeDialog 方法。

2. 数据列表展示

  • 使用 el-checkbox-group 和 el-checkbox 组件展示指标列表,支持多选。

4. 全选功能

  • 提供“全选”功能,点击全选复选框会选中当前页的所有指标。

  • 全选状态由 checkAll 和 isIndeterminate 控制,分别表示是否全选和是否为部分选中状态。

  • 全选操作会更新 multipleSelection(当前页选中的指标)和 pagesSelectsAll(所有选中的指标)。

5. 选择与删除功能

  • 用户可以通过复选框选择指标,选中的指标会显示在右侧的“我的选择”区域。

  • 右侧的“我的选择”区域展示所有已选中的指标,支持删除单个指标。

  • 删除操作会从 pagesSelectsAll 和 multipleSelection 中移除对应的指标。

6. 分页功能

  • 使用 pagination 组件实现分页功能,支持切换页码和每页显示条数。

  • 分页参数包括 page(当前页码)和 pageSize(每页条数),分页变化时会重新获取数据。

代码中api请求的数据结构

{
  "total": 81,
  "items": [
[
  {
    "id": "1",
    "name": "家校沟通1",
    "status": "0",
    "sort": 1,
    "remark": "家校沟通1",
    "code": "code11",
    "type": "1",
    "accuracy": "1",
  },
   {
    "id": "12",
    "name": "家校沟通2",
    "status": "0",
    "sort": 1,
    "remark": "家校沟通2",
    "code": "code12",
    "type": "1",
    "accuracy": "1",
  },
 {
    "id": "13",
    "name": "家校沟通3",
    "status": "0",
    "sort": 1,
    "remark": "家校沟通3",
    "code": "code13",
    "type": "1",
    "accuracy": "1",
  },
 {
    "id": "14",
    "name": "家校沟通4",
    "status": "0",
    "sort": 1,
    "remark": "家校沟通4",
    "code": "code14",
    "type": "1",
    "accuracy": "1",
  },
]
]
  "code": 0,
  "msg": null
}

完整代码,代码已经经过测试完成

<template>
  <el-dialog
    title="选择指标"
    :visible.sync="dialogVisible"
    width="85%"
    append-to-body
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    :show-close="true"
    @closed="closeDialog"
  >
    <div class="mb-12">
      <el-form
        :model="queryParams"
        ref="queryForm"
        size="small"
        :inline="true"
        label-width="68px"
      >
        <el-form-item label="指标名称" prop="name">
          <el-input
            v-model="queryParams.name"
            placeholder="请输入关键字"
            clearable
            @keyup.enter.native="handleQuery"
          />
        </el-form-item>
        <el-form-item label="" prop="type">
          <el-select
            v-model="queryParams.type"
            placeholder="指标类型"
            clearable
            class="selectStyle"
          >
            <el-option
              v-for="dict in dict.type.model_index_type"
              :key="dict.value"
              :label="dict.label"
              :value="dict.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button
            type="primary"
            :icon="IconEnum.SEARCH"
            size="mini"
            @click="handleQuery"
            >查询</el-button
          >
          <el-button :icon="IconEnum.RESET" size="mini" @click="resetQuery"
            >重置</el-button
          >
        </el-form-item>
      </el-form>
    </div>
    <div class="flex" style="box-sizing: border-box; width: calc(100% - 20px)">
      <div
        class="flex flex-col"
        style="border: 1px solid #ebeef5; border-radius: 4px"
      >
        <div
          class="flex items-center fw-600 fs-14"
          style="background: #f8f8f9; border-bottom: 1px solid #ebeef5"
        >
          <span
            class="inline-block align-center"
            style="width: 80px; padding: 10px 0"
          >
            <el-checkbox
              :indeterminate="isIndeterminate"
              v-model="checkAll"
              @change="handleCheckAllChange"
            >
              <span class="fw-400 fs-14">全选</span>
            </el-checkbox>
          </span>
          <span class="inline-block font-e ml-12" style="width: 140px">
            编码
          </span>
          <span class="inline-block" style="width: 206px"> 指标名称 </span>
          <span class="inline-block font-e ml-12" style="width: 90px">
            指标类型
          </span>
          <span class="inline-block font-e ml-12" style="width: 90px">
            指标值
          </span>
        </div>
        <div class="table-block">
          <el-checkbox-group
            v-model="multipleSelection"
            @change="handleSelectionChange"
          >
            <div class="flex flex-col">
              <el-checkbox
                v-for="item in dataList"
                :label="item"
                :value="item"
                :key="item.id"
                style="border-bottom: 1px solid rgba(0, 0, 0, 0.2)"
              >
                <div style="padding: 12px 0">
                  <span
                    class="inline-block font-e ml-12"
                    style="width: 180px; padding-left: 40px"
                  >
                    {{ item.code }}
                  </span>
                  <span class="inline-block font-e" style="width: 206px">
                    {{ item.name }}
                  </span>
                  <span class="inline-block font-e ml-12" style="width: 90px">
                    <span>{{ modelIndexTypeFormat(item.type) }}</span>
                  </span>
                  <span class="inline-block font-e ml-12" style="width: 90px">
                    {{ item.indexValue }}
                  </span>
                </div>
              </el-checkbox>
            </div>
          </el-checkbox-group>
        </div>
      </div>
      <div class="flex-1 flex flex-col">
        <div
          style="
            box-sizing: border-box;
            width: 100%;
            margin-left: 20px;
            border: 1px solid #ebeef5;
            border-radius: 4px;
          "
        >
          <div
            class="fw-600 fs-14"
            style="
              padding: 10px 16px;
              border-bottom: 1px solid #ebeef5;
              background: #f5f7fa;
            "
          >
            我的选择:
          </div>
          <div style="height: 400px; overflow-y: auto">
            <el-scrollbar style="height: 100%; overflow-y: auto">
              <span
                v-for="(item, index) in pagesSelectsAll"
                :key="index"
                class="flex items-center w-full"
                style="padding: 10px 16px"
              >
                <span
                  class="inline-block font-e"
                  style="width: 220px; color: #606266"
                >
                  {{ item.name }}
                </span>
                <span class="cursor-pointer" @click="onDelete(index, item)">
                  <i class="el-icon-circle-close" style="font-size: 18px"></i>
                </span>
              </span>
            </el-scrollbar>
          </div>
        </div>
      </div>
    </div>

    <div style="padding-bottom: 12px">
      <pagination
        v-show="total > 0"
        :total="total"
        :page.sync="queryParams.page"
        :limit.sync="queryParams.pageSize"
        @pagination="getList"
      />
    </div>

    <div class="flex justify-end mt-16">
      <el-button size="medium" @click="closeDialog"> 取消 </el-button>
      <el-button type="primary" size="medium" @click="onSelectOk">
        确定
      </el-button>
    </div>
  </el-dialog>
</template>

<script>
import { queryIndexListApi } from "@/api/system/modelManagement/targetManage";
import { IconEnum } from "@enums";
export default {
  dicts: ["model_index_type", "unit_type"],
  props: { visible: Boolean, list: Array },
  watch: {
    visible: {
      handler(curVal) {
        if (curVal) {
          if (this.list.length) {
            this.pagesSelectsAll = this.list;
          } else {
            this.pagesSelectsAll = [];
          }
          this.getList();
          this.dialogVisible = curVal;
        }
      },
      deep: false,
      immediate: true,
    },
  },
  data() {
    return {
      IconEnum: IconEnum, // 图标标识
      queryId: undefined,
      dialogVisible: true,
      loading: false,
      total: 0,
      // 查询参数
      queryParams: {
        page: 1,
        pageSize: 10,
        name: "",
        type: "",
      },
      dataList: [],
      multipleSelection: [],
      checkAll: false,
      isIndeterminate: false,
      pagesSelectsAll: [],
    };
  },
  methods: {
    //  指标类型格式化
    modelIndexTypeFormat(value) {
      return this.selectDictLabel(this.dict.type.model_index_type, value);
    },
    handleQuery() {
      this.getList();
    },
    resetQuery() {
      this.resetForm("queryForm");
      this.getList();
    },
    getList() {
      this.loading = true;
      // 刷新页面之前把本页面的值multipleSelection 给存储所有的选项的参数pagesSelectsAll
      if (this.multipleSelection?.length) {
        this.multipleSelection.forEach((item) => {
          let getIndex = this.pagesSelectsAll.findIndex(
            (v) => v.id === item.id
          );
          if (getIndex < 0) {
            this.pagesSelectsAll.push(item);
          }
        });
      }
      queryIndexListApi({ ...this.queryParams, status: "0" })
        .then((res) => {
          this.dataList = res.data.items;
          this.multipleSelection = []; // 本页面选项置空
          if (this.pagesSelectsAll?.length) {
            // 存储所有的选项中有值,判断是否含有本页的值,
            // 有值还原给本页面的multipleSelection并且在pagesSelectsAll里面删除
            this.dataList.forEach((item) => {
              let getIndex = this.pagesSelectsAll.findIndex(
                (v) => v.id === item.id
              );
              if (getIndex > -1) {
                this.multipleSelection.push(item); // 本页面选项还原
              }
            });
            this.onChangeCheckStyle(this.multipleSelection);
          }
          this.total = res.data.total;
          this.loading = false;
        })
        .catch(() => {
          this.loading = false;
        });
    },
    onChapterChange(val) {
      this.getList();
      this.chapterOptions = [];
      if (val) {
        repoChapterList({ repoId: val }).then((res) => {
          this.chapterOptions = res.data.items;
          this.queryParams.chapterKeyId = [];
        });
      } else {
        this.chapterOptions = [];
        this.queryParams.chapterKeyId = [];
      }
    },
    handleCourseClass(val) {
      if (val?.length) {
        let getIndex = val.length - 1;
        this.queryParams.chapterKeyId = val[getIndex];
      }
      this.getList();
    },
    // 全选
    handleCheckAllChange(val) {
      // this.multipleSelection = val ? this.dataList : [];// 此处错误,这样写右侧删除会把原数据dataList删除掉
      this.isIndeterminate = false;
      if (val) {
        this.dataList.forEach((item) => {
          let getIndex = this.pagesSelectsAll.findIndex(
            (v) => v.id === item.id
          );
          if (getIndex < 0) {
            this.multipleSelection.push(item);
            this.pagesSelectsAll.push(item);
          }
        });
      } else {
        this.multipleSelection = [];
        //  循环this.multipleSelection的从pagesSelectsAll删除
        this.dataList.forEach((item) => {
          let getIndex = this.pagesSelectsAll.findIndex(
            (v) => v.id === item.id
          );
          if (getIndex > -1) {
            this.pagesSelectsAll.splice(getIndex, 1);
          }
        });
      }
    },
    // 单个选择
    handleSelectionChange(value) {
      // 循环整个页面的datalist
      let okList = []; // 本页面选中的【给右侧使用】
      let noList = []; // 本页面没有选中的【给右侧使用】
      for (let i = 0; i < this.dataList.length; i++) {
        let id = this.dataList[i]["id"];
        let getIndex = value.findIndex((v) => v.id === id);
        if (getIndex > -1) {
          okList.push(this.dataList[i]);
        } else {
          noList.push(this.dataList[i]);
        }
      }
      // 循环选中的放进pagesSelectsAll
      okList.forEach((item) => {
        let getIndex = this.pagesSelectsAll.findIndex((v) => v.id === item.id);
        if (getIndex < 0) {
          this.pagesSelectsAll.push(item);
        }
      });
      //  循环未选中的从pagesSelectsAll删除
      noList.forEach((item) => {
        let getIndex = this.pagesSelectsAll.findIndex((v) => v.id === item.id);
        if (getIndex > -1) {
          this.pagesSelectsAll.splice(getIndex, 1);
        }
      });
      this.onChangeCheckStyle(value);
    },
    // 控制全选样式
    onChangeCheckStyle(value) {
      let checkedCount = value.length;
      this.checkAll = checkedCount === this.dataList.length;
      this.isIndeterminate =
        checkedCount > 0 && checkedCount < this.dataList.length;
    },
    onDelete(index, item) {
      this.pagesSelectsAll.splice(index, 1);
      let getIndex = this.multipleSelection.findIndex((v) => v.id === item.id);
      if (getIndex > -1) {
        this.multipleSelection.splice(getIndex, 1);
      }
      // --start--控制一下左侧全选按钮的样式
      let getCurrentMultipleSelection = [];
      for (let i = 0; i < this.dataList.length; i++) {
        let id = this.dataList[i]["id"];
        let getIndex = this.multipleSelection.findIndex((v) => v.id === id);
        if (getIndex > -1) {
          getCurrentMultipleSelection.push(this.dataList[i]);
        }
      }
      this.onChangeCheckStyle(getCurrentMultipleSelection);
      // --end--控制一下左侧全选按钮的样式
      this.$forceUpdate();
    },
    // 进入文件
    onSelectOk() {
      if (this.pagesSelectsAll.length) {
        this.$emit("targetClose", this.pagesSelectsAll);
        this.closeDialog();
      } else {
        this.$modal.msgSuccess("请选择指标");
      }
    },
    closeDialog() {
      this.dialogVisible = false;
      this.$emit("update:visible", false);
    },
  },
};
</script>

<style lang="scss" scoped>
.table-block {
  height: 400px;
  overflow-y: auto;
}
.lable-block {
  cursor: pointer;
}
.lable-block:hover {
  color: #409eff;
}
::v-deep .el-checkbox {
  display: flex !important;
  align-items: center;
  margin-right: 0 !important;
  .el-checkbox__input {
    padding-left: 16px !important;
  }
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值