vue2 ant-design记忆表头列表页面组件

文章展示了如何使用Vue.js和antd库创建一个记忆表头组件,该组件利用localStorage存储用户选择的表头显示,实现页面刷新后仍能保持用户自定义的表头设置。同时,文章还提及了一个包含树结构、搜索、表格和筛选功能的表格页面组件,实现了响应式布局。
摘要由CSDN通过智能技术生成

git地址:项目git地址(https://github.com/hgg111/vue2AntdTablePage.git)
实现效果:
在这里插入图片描述
代码:
RemberTableHeader.vue组件: 记忆表头组件。将用户选择展示的表头数组存储在 localStorage 中,进入页面时读取,实现记忆表头功能。

<template>
    <div style="position: absolute;right: -5px;width: 30px;background-color: #f5f7fd;">
        <a-popover placement="bottomRight" :arrow="true">
            <a><a-icon type="setting"></a-icon></a>
            <template slot="title">
                表头显示字段
            </template>
            <template slot="content">
                <div style="max-height: 200px;overflow-y: auto;">
                    <div>
                        <a-checkbox value="all" :checked="checkedAll" @change="allChange">全选</a-checkbox>
                    </div>
                    <a-checkbox-group v-model="tableHeader" style="display:flex;flex-direction: column;align-items: flex-start;"
                        @change="tableHeaderChange">
                        <a-checkbox v-for="item in tableHeaderOptions" :key="item" :value="item">{{
                            item
                        }}</a-checkbox>
                    </a-checkbox-group>
                </div>
            </template>
        </a-popover>
    </div>
</template>
<script>

export default {
    props: {
        // 表头
        allColumns: {
            type: Array,
            default: ()=>[],
        },
        // 表名(唯一)
        tableName: {
            type: String,
            default: ''
        },
        // 是否有报告操作列
        hasReportAction: {
            type: Boolean,
            default: false
        },
        // 是否有基本操作列
        hasAction: {
            type: Boolean,
            default: true
        },
        // 是否有序号列
        hasXuhao: {
            type: Boolean,
            default: true
        },
    },
    data() {
        return {
            // 全选框的状态
            checkedAll: true,
            // 所有可选择的表头
            tableHeaderOptions: [],
            // 选择的表头
            tableHeader: [],
            // 操作列内容
            actionContent: [],
            // 报告操作列内容
            reportActionContent: [],
            // 序号列内容
            xuhaoContent: [],
        };
    },
    watch:{
        allColumns: {
            handler(newVal) {
                if(newVal.length != 0){
                    Object.assign(this.allColumns,newVal)
                    this.init()
                }
            },
            deep: true,
            immediate: true
        },
    },
    mounted() {
        if(this.allColumns.length != 0){
            this.init()
        }
    },
    methods: {
        init() {
            this.tableHeaderOptions = []
            // 获取除了序号列和操作列的其他列
            this.allColumns.forEach(item => {
                if (item.title && item.title != '序号' && item.title != '报告操作' && item.title != '授权操作') {
                    this.tableHeaderOptions.push(item.title)
                }
                if (item.key == 'xuHao') {
                    this.xuhaoContent = item
                }
                if (item.key == 'reportAction' || item.key == 'authorization') {
                    this.reportActionContent = item
                }
                if (item.key == 'action') {
                    this.actionContent = item
                }
            })
            this.tableHeader = this.tableHeaderOptions
            // 从localStorage中获取当前表格之前存储的表头
            if (localStorage.getItem(this.tableName) != null) {
                this.tableHeader = localStorage.getItem(this.tableName).split(',')
                this.tableHeaderChange()
            }
            this.tableHeaderChange()
        },
        // 点击全选框
        allChange(value) {
            this.checkedAll = value.target.checked
            if (this.checkedAll == false) {
                this.tableHeader = []
            } else {
                this.tableHeader = this.tableHeaderOptions
            }
            this.tableHeaderChange()
        },
        // 表头选择改变
        tableHeaderChange() {
            // 向 localStorage 中存储当前选择的表头
            localStorage.setItem(this.tableName,this.tableHeader)
            var length = 2
            if(this.hasReportAction){
                length = 3
            }else if(!this.hasAction){
                length = 1
            }
            if(!this.hasXuhao){
                length -= 1
            }
            // 判断是否全选,设置选择框的选中状态
            if (this.tableHeader.length < this.allColumns.length - length) {
                this.checkedAll = false
            } else {
                this.checkedAll = true
            }
            var header = this.allColumns.filter(item => this.tableHeader.indexOf(item.title) > -1)
            // 向表头中添加序号列和操作列
            if(this.hasXuhao){
                header.unshift(this.xuhaoContent)
            }
            if(this.hasReportAction){
                header.push(this.reportActionContent)
            }
            if(this.hasAction){
                header.push(this.actionContent)
            }
            this.$emit('columnChange',header)
        },
    },
};
</script>
<style>
.ant-checkbox-group :first-child {
    margin-left: 8px;
}
</style>

记忆表头功能效果:
请添加图片描述

TablePage.vue:表格页面组件。页面包括树结构数据展示,树搜索,表格展示,表格筛选功能,并且实现响应式布局。

<template>
    <div id="page">
        <a-layout>
            <!-- 树搜索 -->
            <a-layout-sider v-if='treeDisplay'>
                <a-input-search v-if="hasTreeSearch" style="margin-bottom: 8px" placeholder="请输入名称"
                    @search="onSearch" />
                <a-tree expand-action="doubleclick" :tree-data="treeData" :expandedKeys="expandedKeys" defaultExpandAll
                    @select="select" @expand="onExpand" show-icon>
                    <template slot="title" slot-scope="{title}">
                        <span>{{ title }}</span>
                    </template>
                </a-tree>
            </a-layout-sider>
            <a-layout-content :class="{ 'tableContent': !treeDisplay }">
                <div classs="layout">
                    <div class="header">
                        <a-row>
                            <slot name="topOptionButton"></slot>
                        </a-row>
                        <a-row style="display: flex;justify-content: flex-end;align-items: center;margin-top: 5px;">
                            <slot name="filter"></slot>
                        </a-row>
                    </div>
                    <div style="position: relative;">
                        <div class="setHeaderIcon">
                            <rember-table-header :allColumns="allColumns" :tableName="tableName" :hasReportAction="hasReportAction" :hasAction="hasAction"
                                        @columnChange="columnChange"></rember-table-header>
                        </div>
                        <!-- 表格列表 -->
                        <a-table id="tableDom" :columns="columns" :scroll="{ x: nowTableWidth,y: tableHeight}" :data-source="tableData"
                            :row-key="(record) => record.id ? record.id : record.taskId"
                            :row-selection="{ selectedRowKeys: tableSelectedRowKeys, onChange: onSelectChange, }"
                            :pagination="{ defaultPageSize: requestParams.pageSize, current: requestParams.pageNo, total: tableTotal, showTotal: (total) => `共 ${Math.ceil(total / requestParams.pageSize)} 页,共 ${total} 条数据`, }"
                            @change="pageCurrentChange">
                            <div slot="actionTitle">
                                <div style="display: flex;">
                                    <span>基本操作</span>
                                </div>
                            </div>
                            <span class="indexColor resolveResult" slot="xuHao" slot-scope="record, id, index">
                                {{ (index + 1) + (requestParams.pageNo - 1) * requestParams.pageSize }}
                            </span>
                            <a slot="name" slot-scope="text">{{ text }}</a>
                            <div slot="action" slot-scope="text, record">
                                <a-space>
                                    <slot name="tableOpreation" :record="record"></slot>
                                </a-space>
                            </div>
                        </a-table>
                    </div>
                </div>
            </a-layout-content>
        </a-layout>
    </div>
</template>

<script>
import RemberTableHeader from '@/components/RemberTableHeader.vue'

export default {
    props: {
        // 表头
        allColumns: {
            type: Array,
            default: ()=>[],
        },
        // 树数据
        treeData: {
            type: Array,
            default: ()=>[],
        },
        // 表数据总数
        tableTotal: {
            type: Number,
            default: 0,
        },
        // 表数据
        tableData: {
            type: Array,
            default: ()=>[],
        },
        // 树结构是否展示
        treeDisplay: {
            type: Boolean,
            default: true
        },
        // 表选择项
        selectedRowKeys: {
            type: Array,
            default: ()=>[],
        },
        // 是否有其他操作项
        hasReportAction: {
            type: Boolean,
            default: false
        },
        // 是否有树搜索框
        hasTreeSearch: {
            type: Boolean,
            default: true
        },
        // 表名(用来存储,唯一)
        tableName: {
            type: String,
            default: ''
        },
        // 表格宽度
        tableWidth : {
            type: Number,
            default: 3200
        },
        // 表格高度
        tableHeight: {
            type: String,
            default: 'calc(100vh - 420px)'
        },
        // 是否有基本操作项
        hasAction: {
            type: Boolean,
            default: true
        },
    },
    data() {
        return {
            selectedStatusKeys: [],
            requestParams: {
                pageNo: 1,
                pageSize: 10
            },
            columns: this.allColumns,
            expandedKeys: [],
            searchValue: '',
            tableSelectedRowKeys: this.selectedRowKeys,
            nowTableWidth: 0,
            allTableWidth: 0
        };
    },
    watch: {
        selectedRowKeys: {
            handler(newVal) {
                this.tableSelectedRowKeys = newVal
            },
            deep: true,
            immediate: true
        },
        tableTotal: {
            handler() {
                this.requestParams.pageNo = 1
            },
            deep: true,
            immediate: true
        },
        allTableWidth(){
            this.getTableWidth()
        }
    },
    mounted() {
        this.expandedKeys.push('0')
        this.getTableData();
        this.getTreeData();
        // 延迟,等dom加载完毕,获取当前表格宽度
        setTimeout(() => {
            var tableDom = document.getElementById('tableDom')
            this.allTableWidth = tableDom.clientWidth - 20
        }, 100)
        // 响应式,分辨率改变时重新获取当前表格宽度
        window.addEventListener('resize',() => {
            var tableDom1 = document.getElementById('tableDom')
            if(tableDom1){
                this.allTableWidth = tableDom1.clientWidth - 20
                this.getTableWidth()
            }
        })
    },
    components: {
        RemberTableHeader
    },
    methods: {
        // 调用父组件函数获取表格数据
        getTableData(val) {
            if (val == 1) {
                this.requestParams.pageNo = 1;
            }
            this.$emit('getTableData', this.requestParams)
        },
        // 表格页码切换
        pageCurrentChange({ current }) {
            this.requestParams.pageNo = current;
            this.getTableData();
        },
        // 表格选中项改变
        onSelectChange(selectedRowKeys) {
            this.tableSelectedRowKeys = selectedRowKeys;
            this.$emit('getSelectedRowKeys', this.tableSelectedRowKeys)
            this.selectedStatusKeys = [];
            this.tableData.forEach(dt => {
                if (dt.status == 0) {
                    const filt = this.tableSelectedRowKeys.filter(ft => dt.id == ft)
                    if (filt && filt.length != 0) {
                        this.selectedStatusKeys.push(dt.id);
                    }
                }
            })
        },
        // 点击选择树结构
        select(selectId) { 
            this.$emit('select', selectId)
            if (selectId.length === 0) {
                return;
            }
            this.selectedKeys = selectId;
            this.tableSelectedRowKeys = [];
            if (selectId[0] == 'root') {
                this.selectedKeys = []
                return;
            }
            this.requestParams.pageNo = 1
            this.getTableData()
            this.total = 0
        },
        onExpand(expandedKeys) {
            this.expandedKeys = expandedKeys;
            this.autoExpandParent = false;
        },
        // 调用父组件函数获取树数据
        getTreeData(params) {
            this.searchValue = params;
            this.$emit('getTreeData', this.searchValue)
        },
        // 树搜索
        onSearch(value) {
            this.getTreeData(value)
        },
        // 展示列改变
        columnChange(columns) {
            this.columns = columns
            this.getTableWidth()
        },
        // 获取当前表格宽度
        getTableWidth() {
            var allColumnsWidth = 0
            // 获取当前所有列宽度之和,如果当前列没有设宽度则默认200 
            this.columns.forEach(item => {
                if(item.width){
                    allColumnsWidth += item.width
                }else{
                    allColumnsWidth += 200
                }
            })
            // 如果总列宽大于表格dom宽度,则取总列宽;如果总列宽小于表格dom宽度,则取表格dom宽度
            if (allColumnsWidth > this.allTableWidth) {
                this.nowTableWidth = allColumnsWidth
            } else {
                this.nowTableWidth = this.allTableWidth
            }
        },
    },
};
</script>

<style lang="scss" scoped>
#page {
    padding: 20px;
}

.header {
    padding-bottom: 10px;
}

.tableContent {
    margin: 0;
}

.layout {
    background: #fff !important;

}

.ant-layout-sider {
    background: #fff;
    padding-right: 5px;
    border-right: 1px solid #eee;
}

.ant-layout-content {
    background: transparent;
    padding: 10px;
}

.ant-table-tbody {
    background: #fff !important;
    padding: 10px;
}

.outBoxStyle {
    background: blue !important;
}

.text {
    width: 100%;
    line-height: 15px;
    outLine: none;
}

/deep/.ant-table-fixed-header .ant-table-scroll .ant-table-header {
    background-color: #f5f7fd;
}

/deep/.ant-select-selection--multiple .ant-select-selection__rendered {
    height: 40px;
}

/deep/.ant-modal-body {
    max-height: 370px;
    overflow-y: auto;
}

/deep/.ant-table table {
    background-color: #f5f7fd;
}

/deep/.ant-transfer-customize-list .ant-transfer-list {
    width: 45%;
}

/deep/.ant-tree.ant-tree-block-node li span.ant-tree-checkbox+.ant-tree-node-content-wrapper {
    overflow: hidden;
    text-overflow: ellipsis;
}

/deep/.ant-table-fixed-header .ant-table-body-inner {
    background-color: #ffffff;
}

/deep/.ant-select-auto-complete.ant-select .ant-select-selection__rendered {
    line-height: 30px;
}

/deep/ .ant-table-thead>tr>th,
/deep/ .ant-table-tbody>tr>td {
    text-align: center;
}

/deep/.ant-table {
    height: calc(100vh - 300px);
}

/deep/.layout-dashboard .ant-tree {
    height: calc(100vh - 200px);
    overflow: scroll !important;
}

.setHeaderIcon{
    position: absolute;
    z-index: 999;
    right: 10px;
    top: 17px;
}
</style>

实例 tableTest.vue

<template>
    <div id = 'page'>
        <table-page 
            :allColumns="columns" 
            :treeData="treeData" 
            :tableTotal="tableTotal"
            :tableData="tableData" 
            :treeDisplay="true" 
            :selectedRowKeys="selectedRowKeys"
            tableName="testTable"
            :tableWidth="1700"
            tableHeight="calc(100vh - 350px)"
            :hasTreeSearch="true"
            :hasReportAction="false"
            :hasAction="true"
            @getTableData="getTableData"
            @getTreeData="getTreeData"
            @getSelectedRowKeys="getSelectedRowKeys"
            @select="select"
            >
            <!-- 顶部按钮 -->
            <template v-slot:topOptionButton>
                <a-button type="primary" style="margin-right: 10px">新增</a-button>
            </template>
            <!-- 顶部筛选 -->
            <template slot="filter">
                <a-input placeholder="请输入名称" style="width: 120px;margin-right: 10px;"></a-input>
                <a-input placeholder="请输入年龄" style="width: 120px;"></a-input>
            </template>
            <!-- 表格操作 -->
            <template v-slot:tableOpreation="itemProps">
                <a @click="editRecord(itemProps.record)">编辑</a>
                <a-popconfirm title="确定删除?" ok-text="" cancel-text="" @confirm="() => deleteRecord(itemProps.record.id)">
                    <a class="deleText">删除</a>
                </a-popconfirm>
            </template>
        </table-page>
    </div>
</template>

<script>
import TablePage from '@/components/TablePage.vue'

const columns = [
    {
        title: "序号",
        scopedSlots: { customRender: "xuHao" },
        key:"xuHao",
        width: 80,
        fixed: 'left',
    },
    {
        title: '姓名',
        dataIndex: 'name',
        key: 'name',
    },
    {
        title: '年龄',
        dataIndex: 'age',
        key: 'age',
    },
    {
        title: '电话',
        dataIndex: 'phone',
        key: 'phone',
    },
    {
        title: '学历',
        dataIndex: 'education',
        key: 'education',
    },
    {
        title: '职业',
        dataIndex: 'occupation',
        key: 'occupation',
    },
    {
        title: '职业1',
        dataIndex: 'occupation1',
        key: 'occupation1',
    },
    {
        title: '职业2',
        dataIndex: 'occupation2',
        key: 'occupation2',
    },
    {
        title: '职业3',
        dataIndex: 'occupation3',
        key: 'occupation3',
    },
    {
        slots: { title: 'actionTitle'},
        key: 'action',
        scopedSlots: { customRender: 'action' },
        width: 100,
        fixed: 'right',
    },
]

export default{
    data(){
        return {
            tableData: [],
            tableTotal: 0,
            treeData: [],
            columns,
            selectedRowKeys: [],
            pageParams: {
                pageNo: 1,
                pageSize: 10
            }
        }
    },
    components:{
        TablePage
    },
    methods:{
        // 获取表格数据
        getTableData(params){
            if(params){
                this.pageParams.pageNo = params.pageNo
            }
            this.tableData = [
                {
                    id: 1,
                    name: '一',
                    age: 12,
                    phone: '12238746545',
                    education: '本科',
                    occupation: '学生'
                },
                {
                    id: 2,
                    name: '二',
                    age: 12,
                    phone: '12238746545',
                    education: '本科',
                    occupation: '学生'
                },
                {
                    id: 3,
                    name: '三',
                    age: 12,
                    phone: '12238746545',
                    education: '本科',
                    occupation: '学生'
                },
                {
                    id: 4,
                    name: '一',
                    age: 12,
                    phone: '12238746545',
                    education: '本科',
                    occupation: '学生'
                },
                {
                    id: 5,
                    name: '二',
                    age: 12,
                    phone: '12238746545',
                    education: '本科',
                    occupation: '学生'
                },
                {
                    id: 6,
                    name: '三',
                    age: 12,
                    phone: '12238746545',
                    education: '本科',
                    occupation: '学生'
                },
                {
                    id: 7,
                    name: '一',
                    age: 12,
                    phone: '12238746545',
                    education: '本科',
                    occupation: '学生'
                },
                {
                    id: 8,
                    name: '二',
                    age: 12,
                    phone: '12238746545',
                    education: '本科',
                    occupation: '学生'
                },
                {
                    id: 9,
                    name: '三',
                    age: 12,
                    phone: '12238746545',
                    education: '本科',
                    occupation: '学生'
                },
                {
                    id: 10,
                    name: '一',
                    age: 12,
                    phone: '12238746545',
                    education: '本科',
                    occupation: '学生'
                },
                {
                    id: 11,
                    name: '二',
                    age: 12,
                    phone: '12238746545',
                    education: '本科',
                    occupation: '学生'
                },
                {
                    id: 12,
                    name: '三',
                    age: 12,
                    phone: '12238746545',
                    education: '本科',
                    occupation: '学生'
                }
            ]
            this.tableTotal = this.tableData.length
        },
        // 获取树数据
        getTreeData(params){
            if(params){
                console.log("树搜索:",params)
            }
            this.treeData = [
                {
                    title: 'parent 1',
                    key: '0-0',
                    children: [
                        {
                            title: 'parent 1-0',
                            key: '0-0-0',
                            disabled: true,
                            children: [
                                {
                                    title: 'leaf1',
                                    key: '0-0-0-0',
                                    disableCheckbox: true,
                                },
                                {
                                    title: 'leaf2',
                                    key: '0-0-0-1',
                                },
                            ],
                        },
                        {
                            title: 'parent 1-1',
                            key: '0-0-1',
                            children: [
                                {
                                    title: 'leaf3',
                                    key: '0-0-1-0',
                                    disableCheckbox: true,
                                },
                                {
                                    title: 'leaf4',
                                    key: '0-0-1-1',
                                },
                            ],
                        },
                    ],
                },
            ]
        },
        // 表格选中项
        getSelectedRowKeys(selectedRowKeys){
            console.log("表格选中: ",selectedRowKeys)
        },
        // 编辑操作
        editRecord(record){
            console.log("编辑的表数据:",record)
        },
        // 删除操作
        deleteRecord(id){
            console.log("删除id为:",id,"的数据")
        },
        // 选择的树节点
        select(key){
            console.log("选择的树节点key:",key[0])
        }
    }
}
</script>
<style>
body{
    background-color: rgb(207, 212, 212,0.42);
}
 #page{
    width: 100%;
    height: 100%;

 }

.ant-layout-sider{
    padding: 10px;
}
.deleText{
    color: red;
}
</style>
Ant Design Vue 中,如果你想实现大屏轮播效果(类似于无限滚动或无缝滚动)并且同时保持表格(Table)的固定表头功能,你可以使用 `vue-seamless-scroll` 这个插件来处理分页和滚动的平滑衔接。下面是简单的步骤: 1. 安装依赖:首先安装 `vue-seamless-scroll` 和可能的其他 UI 组件库如 `@antv/vue-g2` 或者 Ant Design Vue 的 Table 组件。 ```bash npm install vue-seamless-scroll @antv/vue-g2 --save // 如果使用 G2图表 npm install antd vue --save // 如果只用Ant Design Vue ``` 2. 引入组件:在你的项目中引入 `SeamlessScroll` 组件,并将它应用到需要无缝滚动的父容器上。 ```html <template> <div ref="seamlessContainer"> <ul :data-source="dataSource" seamless></ul> </div> </template> <script> import { Table } from 'antd'; import SeamlessScroll from 'vue-seamless-scroll'; export default { components: { Table, SeamlessScroll, }, // 其他代码... } </script> ``` 3. 数据源管理:你需要为 `data-source` 设置动态的数据,这可以是一个包含多个页面数据的数组,这样当用户滚动到底部时,会自动加载更多数据。 ```js data() { return { dataSource: [], // 初始数据 pageSize: 10, // 每页显示数量 currentPage: 1, // 当前页码 }; }, methods: { fetchMoreData() { // 假设你有一个 API 来获取更多数据,这里只是一个示例 this.$axios.get('api/data', { params: { page: this.currentPage + 1, size: this.pageSize, }, }).then(response => { this.dataSource.push(...response.data); // 将新数据添加到现有数据源 }); }, }, mounted() { this.initScroll(); }, destroyed() { this.scroll.destroy(); // 移除滚动监听 }, computed: { // 使用虚拟列表技术,只渲染当前可见的部分 virtualizedData() { const start = (this.scroll.yAxis.scrollTop / this.scroll.yAxis.clientHeight) * this.dataSource.length; const end = start + this.scroll.yAxis.clientHeight / this.scroll.yAxis.clientHeight; return this.dataSource.slice(Math.floor(start), Math.ceil(end)); }, }, mounted() { this.scroll = new SeamlessScroll(this.$refs.seamlessContainer, { containerHeight: this.$el.offsetHeight, // 自定义容器高度 itemHeight: this.getItemHeight(), // 根据实际元素计算行高 onScroll: this.fetchMoreData, // 触发滚动事件时加载更多数据 }); }, beforeDestroy() { this.scroll.destroy(); }, // ...其他方法 } ``` 4. 根据需求调整样式:如果需要使表头始终保持在顶部,可以在 CSS 中设置 `table-fixed-header` 类,确保表头不会随滚动而移动。 以上就是一个基本的大屏轮播并保持固定表头Vue 实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

格格不入ち

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值