先上效果图:

elementui穿梭框宽度自适应 element ui穿梭框分页_前端

elementui穿梭框宽度自适应 element ui穿梭框分页_elementui穿梭框宽度自适应_02

问题描述:

1. 由于数据过多,后端对列表数据分页处理,前端一次性无法拿到所有分页,但是elementui自带的穿梭框无法满足分页的需求;

2. elementui 自带的穿梭框异步数据分页请求,切换页码时,选中的选项会被清掉,没办法记住选中的数据;

3. 涉及到编辑功能时,也需要做到数据的回显,这里起初后端给的数据仅有一个id列表,无法满足穿梭框分页、反显的功能条件,死磕许久,突然发现是后端接口数据格式不满足,需要去和后端同学沟通下。

解决方法:

1. 摒弃elementui框架自带的穿梭框,之前也有遇到改造elementui穿梭框的数据格式,前提是,展示的label就应该作为选中的key,这样如果移到右边就可以通过处理数据,将选中的非当前页的数据拼接到左侧的当前页列表中,这样就可以做到数据的反显了,不过这里也存在bug,规定一页展示10条,这样的话当前页的数据肯定是在10条之上的。所以考虑自己手动实现穿梭框的功能;

2. 由于需要勾选操作,采用elementui框架中的table实现,左右两个table,中间填充两个移动的按钮,通过点击确定添加、移除,更新两个table的数据、选中状态;

3. 这样的好处是,左右两侧数据独立,左边的翻页操作,对右边不会带来任何影响,都只涉及添加、移除等操作,不会有过多的处理数据、逻辑;

4. 为了能更好的呈现穿梭框的功能,这里考虑在对左侧移除、添加时,添加标识,动态控制当前行的显示与隐藏。即在table中定义  :row-class-name="rowClassName",通过动态复制,添加样式类名,控制行的显示隐藏,这样只要左边选择的数据移到右侧后,视觉上会有一种隐藏的效果。

3. 这样的好处是,左右两侧数据独立,左边的翻页操作,对右边不会带来任何影响,都只涉及添加、移除等操作,不会有过多的处理数据、逻辑;

4. 为了能更好的呈现穿梭框的功能,这里考虑在对左侧移除、添加时,添加标识,动态控制当前行的显示与隐藏。即在table中定义  :row-class-name="rowClassName",通过动态复制,添加样式类名,控制行的显示隐藏,这样只要左边选择的数据移到右侧后,视觉上会有一种隐藏的效果。

注意事项:

1. 后端需要保存你每次选中的数据,数据格式为一个对象数组,对象应该包含你列表需要展示的lable、id,如果后端只返回id数组,是没有办法实现分页、回现等功能的。

html代码实现如下:

<template>
    <div class="transfer__container">
        <div class="search__div">
            <el-input
                placeholder="请输入用例名称"
                size="small"
                v-model="caseName"
                @keyup.enter.native="getCaseList"
                clearable
                @change="getCaseList"
                @clear="getCaseList"
            >
                <el-button slot="append" icon="el-icon-search" @click="getCaseList"></el-button
            ></el-input>
        </div>
        <div class="table__container">
            <div class="left__table">
                <el-table
                    ref="tableRef"
                    :data="list"
                    tooltip-effect="dark"
                    style="width: 100%"
                    height="330"
                    :row-style="{ height: '20px' }"
                    @selection-change="handleSelectionChange"
                    :row-class-name="rowClassName"
                    :header-row-class-name="'tableHead'"
                >
                    <el-table-column type="selection" width="55" :selectable="checkSelectable"> </el-table-column>
                    <el-table-column label="未选用例" width="400" show-overflow-tooltip>
                        <template slot="header" slot-scope="scope">
                            <span>未选用例</span>
                            <!-- <span style="" class="case__select-num"
                                >{{ bindSelection.length }}/{{ pagination.total }}</span
                            > -->
                        </template>

                        <template slot-scope="scope">{{ scope.row.ruleName }}</template>
                    </el-table-column>
                </el-table>
                <div class="view__pagination">
                    <el-pagination
                        :current-page="pagination.currentPage"
                        :page-size="pagination.pageSize"
                        :total="pagination.total"
                        :page-sizes="pagination.pageSizes"
                        small
                        align="center"
                        layout="prev, pager, next"
                        @current-change="currentChangeHandler"
                    ></el-pagination>
                </div>
            </div>
            <div class="center__btn">
                <el-button type="primary" @click="comfirmDeleteCase" :disabled="!unbindSelection.length"
                    ><i class="el-icon-arrow-left"></i
                ></el-button>
                <el-button type="primary" @click="comfirmAddCase" :disabled="!bindSelection.length"
                    ><i class="el-icon-arrow-right"></i
                ></el-button>
            </div>
            <div class="right__table">
                <el-table
                    ref="bindTableRef"
                    :data="bindList"
                    tooltip-effect="dark"
                    style="width: 100%"
                    height="330"
                    @selection-change="handlUnbindeSelectionChange"
                >
                    <el-table-column type="selection" width="55"></el-table-column>
                    <el-table-column width="400" show-overflow-tooltip>
                        <template slot="header" slot-scope="scope">
                            <span>已选用例</span>
                            <!-- <span style="" class="case__select-num"
                                >{{ unbindSelection.length }}/{{ bindList.length }}</span
                            > -->
                        </template>
                        <template slot-scope="scope">{{ scope.row.ruleName }}</template>
                    </el-table-column>
                </el-table>
            </div>
        </div>
    </div>
</template>
<script>
import { getCaseList } from '@/api/test-case-manage';
import { cloneDeep } from 'lodash';

export default {
    name: 'CaseTransfer',
    props: {
        modelVersionId: {
            type: Number,
            default: null
        },
        cases: {
            type: Array,
            default: () => {
                return [];
            }
        }
    },
    data() {
        return {
            caseName: '', //搜索用例
            //分页
            pagination: {
                total: 0,
                currentPage: 1,
                pageSize: 10,
                pageSizes: [5, 10, 100]
            },
            list: [], // 全部的数据(包括已绑定和未绑定的数据)
            bindSelection: [], //选中即将绑定的数据
            bindList: [], //已绑定的数据
            unbindSelection: [] //选中即将解绑的数据
        };
    },

    methods: {
        //编辑时,初始化穿梭框,将已选中的设置在右边
        initTransfer() {
            if (!this.cases.length) return;
            this.bindList = cloneDeep(this.cases);

        },
        //根据选中的模型,获取用例
        getCaseList() {
            let params = {
                pageNum: this.pagination.currentPage,
                pageSize: this.pagination.pageSize
            };
            let data = {
                ruleName: this.caseName,
                levels: [],
                priorities: [],
                modelVersionId: this.modelVersionId
            };
            getCaseList(params, data)
                .then(res => {
                    if (res.code !== '0') {
                        this.$message.error(res.message || '获取用例配置列表失败');
                        return;
                    }
                    this.list = [];
                    res.data.records.forEach((item, index) => {
                        this.list.push({
                            id: item.id,
                            ruleName: item.ruleName
                        });
                    });
                    // this.dealTableData(this.list);
                    this.pagination.total = res.data.total;

                })
                .catch(error => {
                    this.$message.error(error.message || '获取用例配置列表失败');
                })
                .finally(() => {
                    this.$nextTick(() => {
                        this.selectCacheData(this.bindList);
                    });
                });
        },

        //绑定过的,需要在左边显示勾
        selectCacheData(selections) {
            const cacheIds = selections.map(item => item.id);
            this.list.map((row,index) => {
                if (cacheIds.includes(row.id)) {
                    // this.$refs.tableRef.toggleRowSelection(row);
                    row.disabled = true;
                    this.$set(this.list, index, row);
                }
                return row;
            });

        },
        /**
         * 是否可选
         */
        checkSelectable(row) {
            const state = this.bindList.some(el => {
                return el.id === row.id;
            });
            return !state;
        },

        //获取即将绑定的数据
        handleSelectionChange(val) {
            this.bindSelection = val;
        },

        //获取即将移除的数据
        handlUnbindeSelectionChange(val) {
            this.unbindSelection = val;
        },
        //确定添加
        comfirmAddCase() {
            // this.bindList = this.bindSelection;
            if (Array.isArray(this.bindSelection) && this.bindSelection.length > 0) {
                const bindIds = this.bindList.map(item => item.id);
                this.bindSelection.map(item => {
                    if (bindIds.includes(item.id) === false) {
                        this.$refs.tableRef.toggleRowSelection(item, true);
                        this.bindList = this.bindList.concat(item);
                    }
                });
                this.bindSelection = this.bindList.map(item => item.id);
                this.list.map(item => {
                    if (this.bindSelection.includes(item.id)) {
                        item.disabled = true;
                    }
                    return item;
                });
                this.bindSelection = [];
            }
        },

        //确定移除
        comfirmDeleteCase() {
            if (Array.isArray(this.unbindSelection) && this.unbindSelection.length > 0) {
                let oldBindIds = this.bindList.map(item => item.id);
                this.unbindSelection.map(item => {
                    if (oldBindIds.includes(item.id) === true) {
                        const index = oldBindIds.indexOf(item.id);
                        this.bindList.splice(index, 1);
                        oldBindIds.splice(index, 1);
                    }
                });
                // 对比 左右是否有交集
                let newBindIds = this.bindList.map(item => item.id);
                this.list.map(item => {
                    if (newBindIds.includes(item.id) === true) {
                        this.$refs.tableRef.toggleRowSelection(item, true);
                        item.disabled = true;
                    } else {
                        this.$refs.tableRef.toggleRowSelection(item, false);
                        item.disabled = false;
                    }
                    return item;
                });
            }
        },
        //根据标识disabled设置el-table列的动态显示,这里记得给table column添加key,这样可以保证列的显示隐藏正常。
        rowClassName({ row }) {
            if (!row.disabled) {
                return;
            }
            return 'showRow';
        },

        //分页
        currentChangeHandler(data) {
            this.pagination.currentPage = data;
            this.getCaseList();
        },

        //获取绑定数据的id
        getBindIds() {
            if (!this.bindList.length) {
                this.$message.error('请选择用例!');
                return false;
            }
            let ids = [];
            this.bindList.map(item => {
                ids.push(item.id);
            });
            return ids;
        }
    },
    watch: {
        modelVersionId: {
            handler(newVal, oldVal) {
                this.getCaseList();
                if (!oldVal) {
                    this.initTransfer();
                } else {
                    this.bindList = [];
                }
            },
            deep: true
        }
    }
};
</script>

<style lang="scss" scoped>
.transfer__container {
    display: flex;
    flex-direction: column;
    margin-left: 15px;

    .search__div {
        width: 457px;
        margin-bottom: 15px;
    }
    .table__container {
        display: flex;

        .center__btn {
            padding: 0 10px;
            height: 330px;
            display: flex;
            flex-direction: column;
            justify-items: center;
            justify-content: center;
            /deep/ .el-button--primary {
                margin-left: 0px;
                margin-top: 10px;
            }
        }
        /deep/.el-table td,
        .el-table th {
            padding: 5px 0;
        }
        /deep/ .case__select-num {
            margin-left: 10px;
            color: #909399;
            font-weight: 400;
        }
    }
    /deep/.showRow {
        display: none;
    }
}
</style>