vue+element之一个table为载体的穿梭框
有个类似穿梭框的弹窗需求,看了下element-ui的Transfer
组件,和原型查了很多。所以自己写了一个,原型图如下:
估计也没人看,直接放源码防止以后遇到同样需求:
<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>