uniapp+vue3 固定列表格,左右可滑动

54 阅读3分钟

wechat_2025-08-11_174004_654.png

<template>
    <view class="page-container">
        <!-- 表格主容器 -->
        <view class="table-container">
            <!-- 滚动区域 -->
            <view class="scroll-area">
                <view class="table-content">
                    <!-- 完整表头 -->
                    <view class="table-row header-row">
                        <view class="table-cell" v-for="(col, index) in columns" :key="'h' + index" :style="{ width: col.width }">
                            {{ col.label }}
                        </view>
                    </view>

                    <!-- 完整表体 -->
                    <view class="table-row" v-for="(item, rowIndex) in tableData" :key="'r' + rowIndex">
                        <view class="table-cell" v-for="(col, index) in columns" :key="'c' + rowIndex + index" :style="{ width: col.width }">
                            <template v-if="col.field === 'status'">
                                <view class="status-tag" :class="item[col.field] === '在职' ? 'active' : ''">
                                    {{ item[col.field] }}
                                </view>
                            </template>
                            <template v-else>{{ item[col.field] }}</template>
                        </view>
                    </view>
                </view>
            </view>

            <!-- 固定列 -->
            <view class="fixed-columns">
                <!-- 固定列表头 -->
                <view class="table-row header-row">
                    <view class="table-cell" v-for="(col, index) in fixedColumns" :key="'fh' + index" :style="{ width: col.width }">
                        {{ col.label }}
                    </view>
                </view>

                <!-- 固定列内容 -->
                <view class="table-row" v-for="(item, rowIndex) in tableData" :key="'fr' + rowIndex">
                    <view class="table-cell" v-for="(col, index) in fixedColumns" :key="'fc' + rowIndex + index" :style="{ width: col.width }">
                        {{ item[col.field] }}
                    </view>
                </view>
            </view>
        </view>
    </view>
</template>

<script setup>
import { ref, computed } from 'vue';

// 配置项
const cellHeight = 56;
const fixedCount = 2; // 固定列数量

// 列配置
const columns = ref([
    { label: 'ID', field: 'id', width: '80px' },
    { label: '姓名', field: 'name', width: '120px' },
    { label: '年龄', field: 'age', width: '80px' },
    { label: '邮箱', field: 'email', width: '200px' },
    { label: '地址', field: 'address', width: '300px' },
    { label: '电话', field: 'phone', width: '150px' },
    { label: '职位', field: 'position', width: '180px' },
    { label: '入职日期', field: 'hireDate', width: '150px' },
    { label: '状态', field: 'status', width: '100px' },
]);

// 计算属性 - 固定列
const fixedColumns = computed(() => columns.value.slice(0, fixedCount));

// 表格数据
const tableData = ref([
    {
        id: 1,
        name: '张三',
        age: 28,
        email: 'zhangsan@example.com',
        address: '北京市朝阳区建国路88号',
        phone: '13800138000',
        position: '前端开发工程师',
        hireDate: '2020-03-15',
        status: '在职',
    },
    {
        id: 2,
        name: '李四',
        age: 32,
        email: 'lisi@example.com',
        address: '上海市浦东新区张江高科技园区',
        phone: '13900139000',
        position: '后端开发工程师',
        hireDate: '2019-07-22',
        status: '在职',
    },
    {
        id: 3,
        name: '王五',
        age: 25,
        email: 'wangwu@example.com',
        address: '广州市天河区珠江新城',
        phone: '13700137000',
        position: 'UI设计师',
        hireDate: '2021-01-10',
        status: '离职',
    },
    {
        id: 4,
        name: '赵六',
        age: 35,
        email: 'zhaoliu@example.com',
        address: '深圳市南山区科技园',
        phone: '13600136000',
        position: '产品经理',
        hireDate: '2018-05-30',
        status: '在职',
    },
    {
        id: 5,
        name: '孙七',
        age: 29,
        email: 'sunqi@example.com',
        address: '杭州市西湖区西溪路',
        phone: '13500135000',
        position: '测试工程师',
        hireDate: '2020-09-05',
        status: '在职',
    },
    {
        id: 6,
        name: '周八',
        age: 31,
        email: 'zhouba@example.com',
        address: '成都市高新区天府大道,这是一个比较长的地址用于测试单元格内容换行',
        phone: '13400134000',
        position: '数据分析师',
        hireDate: '2019-11-18',
        status: '在职',
    },
]);
</script>

<style scoped>
.page-container {
    padding: 16px;
    background-color: #f5f7fa;
    min-height: 100vh;
}

.page-title {
    font-size: 20px;
    font-weight: bold;
    color: #333;
    margin-bottom: 20px;
    padding-bottom: 10px;
    border-bottom: 1px solid #eee;
}

/* 表格主容器 */
.table-container {
    position: relative;
    width: 100%;
    max-height: 500px;
    border: 1px solid #e5e7eb;
    border-radius: 4px;
    overflow: hidden;
    background-color: #fff;
}

/* 滚动区域 */
.scroll-area {
    width: 100%;
    height: 100%;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
}

/* 表格内容 */
.table-content {
    width: 1360px; /* 列宽总和 */
}

/* 行样式 */
.table-row {
    display: flex;
}

/* 表头行 */
.header-row {
    background-color: #f9fafb;
    border-bottom: 1px solid #e5e7eb;
}

/* 单元格样式 */
.table-cell {
    width: 100px;
    height: v-bind(cellHeight + 'px');
    padding: 0 16px;
    display: flex;
    align-items: center;
    border-right: 1px solid #e5e7eb;
    border-bottom: 1px solid #e5e7eb;
    font-size: 14px;
    color: #333;
    word-break: break-all;
    box-sizing: border-box;
    line-height: 1.5;
}

/* 最后一列无边框 */
.table-row .table-cell:last-child {
    border-right: none;
}

/* 表头单元格 */
.header-row .table-cell {
    font-weight: 500;
    color: #666;
}

/* 交替行背景 */
.table-row:not(.header-row):nth-child(even) .table-cell {
    background-color: #fcfcfc;
}

/* 固定列 */
.fixed-columns {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    z-index: 10;
    background-color: #fff;
    border-right: 1px solid #e5e7eb;
    pointer-events: none;
}

.fixed-columns .table-cell {
    pointer-events: auto;
}

/* 状态标签 */
.status-tag {
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 12px;
    background-color: #fff2e8;
    color: #ff7d00;
}

.status-tag.active {
    background-color: #e6f7ed;
    color: #00875a;
}
</style>