列表翻页多选全选
操作过程,每页选中的选项会在右侧显示,切换每一页选项正确回显,全选按钮正确回显,删除右侧数据左侧不选中,左右两边的数据能正确交互。
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>