element ui 表格扩展,支持移动端自适应卡片式布局渲染

默认的elment ui的table组件,在移动端体验很差

所以需要扩展一下,实现自适应,

pc端保持官方样式不变,移动端实现卡片式布局浏览

 

最终效果

pc端:

 

 

移动端:

 

 

 

相关组件代码:

<template>
    <div class="responsive-table-wrapper">
        <!-- PC端显示 -->
        <el-table v-if="!isMobile" :data="data" v-bind="$attrs" v-on="$listeners" ref="table" class="desktop-table">
            <slot></slot>
        </el-table>

        <!-- 移动端卡片显示 -->
        <div v-else class="mobile-cards">
            <div v-for="(row, rowIndex) in data" :key="getRowKey(row, rowIndex)" class="mobile-card"
                @click="handleRowClick(row, rowIndex)">
                <!-- 卡片头部 - 显示主要信息 -->
                <div class="card-header" v-if="primaryColumn">
                    <div class="card-title">
                        <span class="title-text">{{ getCellValue(row, primaryColumn) }}</span>

                    </div>
                </div>

                <!-- 卡片内容 -->
                <div class="card-content">
                    <div v-for="column in displayColumns" :key="column.prop || column.label" class="info-row"
                        :class="{ 'is-important': column.important }">
                        <span class="label" style="color: #c0c4cc;">{{ column.label }}:</span>
                        <div class="value">
                            <template v-if="column.scopedRender">
                                <div class="render-holder" :ref="`render-${rowIndex}-${column.prop}`"></div>
                            </template>

                            <!-- 普通文本显示 -->
                            <template v-else>
                                <span>{{ formatCellValue(row, column) }}</span>
                            </template>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 移动端无数据显示 -->
            <div v-if="!data || data.length === 0" class="mobile-empty">
                <slot name="empty">
                    <div class="empty-content">
                        <i class="el-icon-document"></i>
                        <p>暂无数据</p>
                    </div>
                </slot>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: 'FlexTable',
        inheritAttrs: false,
        props: {
            data: {
                type: Array,
                default: () => []
            },
            // 移动端主显示列(通常是标题或名称)
            primaryField: {
                type: String,
                default: 'deviceNumber'
            },
            // // 状态字段
            // statusField: {
            //     type: String,
            //     default: 'status'
            // },
            // 移动端隐藏的字段
            mobileHideFields: {
                type: Array,
                default: () => []
            },
            // 行点击事件
            rowClick: {
                type: Function,
                default: null
            },
            // 自定义断点
            mobileBreakpoint: {
                type: Number,
                default: 768
            }
        },
        data() {
            return {
                isMobile: false,
                columns: []
            }
        },
        computed: {
            primaryColumn() {
                if (this.primaryField) {
                    return this.columns.find(col => col.prop === this.primaryField)
                }
                // 默认取第一个有prop的列
                return this.columns.find(col => col.prop && col.type !== 'action')
            },
            // 移动端显示的列(排除主列和隐藏列)
            displayColumns() {
                return this.columns.filter(col => {
                    // 只排除明确隐藏的字段
                    if (this.mobileHideFields.includes(col.prop)) return false
                    return true
                })
            }
        },
        mounted() {
            this.parseColumns()
            this.checkDevice()
            this.renderAllScopedSlots(); // 加上
            window.addEventListener('resize', this.checkDevice)
        },
        updated() {
            this.renderAllScopedSlots(); // DOM更新后重新插入 VNode
        },
        beforeDestroy() {
            window.removeEventListener('resize', this.checkDevice)
        },
        methods: {
            // 检测设备类型
            checkDevice() {
                this.isMobile = window.innerWidth <= this.mobileBreakpoint
            },
            renderAllScopedSlots() {
                if (!this.isMobile || !this.columns.length || !this.data.length) return;

                this.$nextTick(() => {
                    this.data.forEach((row, rowIndex) => {
                        this.columns.forEach((column) => {
                            if (typeof column.scopedRender === 'function') {
                                const vnode = column.scopedRender({ row, column, $index: rowIndex });
                                const refName = `render-${rowIndex}-${column.prop}`;
                                const ref = this.$refs[refName];
                                const container = Array.isArray(ref) ? ref[0] : ref;

                                if (!container || !(container instanceof HTMLElement)) return;

                                container.innerHTML = '';

                            
                                const vnodeRaw = column.scopedRender({ row, column, $index: rowIndex });

                                const vnodeArray = Array.isArray(vnodeRaw) ? vnodeRaw : [vnodeRaw];
                                const needWrap =
                                    vnodeArray.length > 1 || (vnodeArray.length === 1 && vnodeArray[0].tag === undefined);

                                const renderFn = h =>
                                    needWrap ? h('div', {}, vnodeArray) : vnodeArray[0]; // 不包多余的 div


                                const vm = new this.$root.constructor({
                                    parent: this,
                                    render: renderFn
                                }).$mount();

                                container.appendChild(vm.$el);
                            }
                        });
                    });
                });
            },
            // 解析列配置
            parseColumns() {
                this.$nextTick(() => {
                    if (this.$slots.default) {
                        this.columns = this.extractColumns(this.$slots.default)
                    }
                })
            },

            // 提取列配置
            extractColumns(vnodes) {
                const columns = []

                const extractFromVNode = (vnode) => {
                    if (!vnode) return

                    if (vnode.componentOptions && vnode.componentOptions.tag === 'el-table-column') {
                        const props = vnode.componentOptions.propsData || {}
                        const children = vnode.componentOptions.children || []

                        const column = {
                            prop: props.prop || '',
                            label: props.label,
                            width: props.width,
                            minWidth: props.minWidth,
                            sortable: props.sortable,
                            align: props.align,
                            headerAlign: props.headerAlign,
                            className: props.className,
                            labelClassName: props.labelClassName,
                            formatter: props.formatter,
                            showOverflowTooltip: props.showOverflowTooltip,
                            fixed: props.fixed,
                            type: props.type,
                            index: props.index,
                            columnKey: props.columnKey,
                            renderHeader: props.renderHeader,
                            sortMethod: props.sortMethod,
                            sortBy: props.sortBy,
                            resizable: props.resizable,
                            // 检查是否有作用域插槽
                            // scopedSlot: this.getScopedSlotName(vnode),
                            // scopedSlot: vnode.data && vnode.data.scopedSlots && vnode.data.scopedSlots.default ? (column.prop || column.columnKey || column.label) : null,
                            scopedRender: vnode.data && vnode.data.scopedSlots && vnode.data.scopedSlots.default ? vnode.data.scopedSlots.default : null,


                            // 标记重要字段
                            important: props.important || false
                        }


                        // 如果没有prop但有操作,标记为操作列
                        if (!column.prop && children.length > 0) {
                            column.type = 'action'
                            // column.scopedSlot = 'action'
                        }

                        columns.push(column)
                    }

                    // 递归处理子节点
                    if (vnode.children) {
                        vnode.children.forEach(extractFromVNode)
                    }
                    if (vnode.componentOptions && vnode.componentOptions.children) {
                        vnode.componentOptions.children.forEach(extractFromVNode)
                    }
                }

                vnodes.forEach(extractFromVNode)
                return columns
            },
            // 获取单元格值
            getCellValue(row, column) {
                if (!column || !column.prop) return ''

                const keys = column.prop.split('.')
                let value = row

                for (const key of keys) {
                    if (value && typeof value === 'object') {
                        value = value[key]
                    } else {
                        value = ''
                        break
                    }
                }

                return value
            },

            // 格式化单元格值
            formatCellValue(row, column) {
                let value = this.getCellValue(row, column)

                // 如果有formatter函数
                if (column.formatter && typeof column.formatter === 'function') {
                    return column.formatter(row, column, value)
                }

                // 默认格式化
                if (value === null || value === undefined) {
                    return ''
                }

                return value
            },

            // 获取状态组件
            getStatusComponent(row, column) {
                // 默认返回 el-tag
                return 'el-tag'
            },

            // 获取状态属性
            getStatusProps(row, column) {
                const value = this.getCellValue(row, column)

                // 默认状态映射
                const statusMap = {
                    0: { type: 'success' },
                    1: { type: 'danger' },
                    2: { type: 'info' },
                    3: { type: 'warning' },
                    '正常': { type: 'success' },
                    '异常': { type: 'danger' },
                    '故障': { type: 'danger' },
                    '关闭': { type: 'info' },
                    '维修': { type: 'warning' }
                }

                return {
                    size: 'mini',
                    ...statusMap[value]
                }
            },

            // 获取状态文本
            getStatusText(row, column) {
                const value = this.getCellValue(row, column)

                // 状态文本映射
                const textMap = {
                    0: '正常',
                    1: '故障',
                    2: '关闭',
                    3: '维修'
                }

                return textMap[value] || value
            },

            // 获取行key
            getRowKey(row, index) {
                if (this.$attrs['row-key']) {
                    if (typeof this.$attrs['row-key'] === 'function') {
                        return this.$attrs['row-key'](row)
                    }
                    return row[this.$attrs['row-key']]
                }
                return index
            },

            // 行点击处理
            handleRowClick(row, index) {
                if (this.rowClick) {
                    this.rowClick(row, index)
                }
                this.$emit('row-click', row, index)
            },

            // 暴露表格方法
            clearSelection() {
                if (this.$refs.table) {
                    this.$refs.table.clearSelection()
                }
            },

            toggleRowSelection(row, selected) {
                if (this.$refs.table) {
                    this.$refs.table.toggleRowSelection(row, selected)
                }
            },

            toggleAllSelection() {
                if (this.$refs.table) {
                    this.$refs.table.toggleAllSelection()
                }
            },

            setCurrentRow(row) {
                if (this.$refs.table) {
                    this.$refs.table.setCurrentRow(row)
                }
            },

            clearSort() {
                if (this.$refs.table) {
                    this.$refs.table.clearSort()
                }
            },

            clearFilter(columnKey) {
                if (this.$refs.table) {
                    this.$refs.table.clearFilter(columnKey)
                }
            },

            doLayout() {
                if (this.$refs.table) {
                    this.$refs.table.doLayout()
                }
            },

            sort(prop, order) {
                if (this.$refs.table) {
                    this.$refs.table.sort(prop, order)
                }
            }
        },
        watch: {
            '$slots.default': {
                handler() {
                    this.parseColumns()
                },
                deep: true
            }
        }
    }
</script>

<style scoped>
    .responsive-table-wrapper {
        width: 100%;
    }

    /* 移动端样式 */
    .mobile-cards {
        display: flex;
        flex-direction: column;
        gap: 12px;
    }

    .mobile-card {
        background: #fff;
        border-radius: 8px;
        padding: 16px;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
        border: 1px solid #ebeef5;
        transition: all 0.3s ease;
        cursor: pointer;
    }

    .mobile-card:hover {
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        transform: translateY(-1px);
    }

    .mobile-card:active {
        transform: translateY(0);
    }

    .card-header {
        margin-bottom: 12px;
        padding-bottom: 12px;
        border-bottom: 1px solid #f0f0f0;
    }

    .card-title {
        display: flex;
        justify-content: space-between;
        align-items: center;
        flex-wrap: wrap;
        gap: 8px;
    }

    .title-text {
        font-weight: 600;
        font-size: 16px;
        color: #303133;
        flex: 1;
        min-width: 0;
        word-break: break-all;
    }

    .status-tag {
        flex-shrink: 0;
    }

    .card-content {
        display: flex;
        flex-direction: column;
        gap: 8px;
    }

    .info-row {
        display: flex;
        justify-content: space-between;
        align-items: flex-start;
        padding: 6px 0;
        border-bottom: 1px solid #f8f9fa;
        min-height: 32px;
    }

    .info-row:last-child {
        border-bottom: none;
    }

    .info-row.is-important {
        background: #f0f9ff;
        margin: 0 -8px;
        padding: 8px;
        border-radius: 4px;
        border-bottom: none;
    }

    .label {
        font-size: 14px;
        color: #606266;
        font-weight: 500;
        min-width: 80px;
        max-width: 120px;
        flex-shrink: 0;
        line-height: 1.4;
    }

    .value {
        font-size: 14px;
        color: #303133;
        text-align: right;
        flex: 1;
        margin-left: 12px;
        word-break: break-all;
        line-height: 1.4;
        min-width: 0;
    }

    .value>>>.el-button {
        margin-left: 8px;
        padding: 4px 8px;
        font-size: 12px;
    }

    .value>>>.el-button:first-child {
        margin-left: 0;
    }

    /* 空数据样式 */
    .mobile-empty {
        text-align: center;
        padding: 40px 20px;
        color: #909399;
    }

    .empty-content i {
        font-size: 48px;
        color: #c0c4cc;
        margin-bottom: 16px;
    }

    .empty-content p {
        margin: 0;
        font-size: 14px;
    }

    /* 桌面端保持原有样式 */
    .desktop-table {
        width: 100%;
    }

    /* 响应式断点 */
    @media (max-width: 480px) {
        .mobile-card {
            margin: 0 -8px;
            border-radius: 0;
            border-left: none;
            border-right: none;
        }

        .card-title {
            flex-direction: column;
            align-items: flex-start;
        }

        .title-text {
            margin-bottom: 8px;
        }

        .info-row {
            flex-direction: column;
            align-items: flex-start;
            gap: 4px;
        }

        .value {
            text-align: left;
            margin-left: 0;
        }
    }
</style>

 

使用示例

 <flex-table :data="dataList" style="width: 100%" v-loading="loading" primary-field="number">
                    <el-table-column prop="organization" label="机构" sortable align="center"></el-table-column>
                    <el-table-column prop="number" label="仪器编号" sortable align="center"></el-table-column>
                    <el-table-column prop="sn4g" label="4G模块编号" sortable align="center"></el-table-column>

                    <el-table-column prop="state" label="状态" align="center">
                        <template slot-scope="scope">
                            <el-tag-mid v-if="scope.row.state==0" type="success"">
                                正常
                            </el-tag-mid>
                            <el-tag-mid v-else-if=" scope.row.state==1" type="danger">
                                故障
                            </el-tag-mid>
                            <el-tag-mid v-else-if="scope.row.state==2" type="info">
                                关闭
                            </el-tag-mid>
                            <el-tag-mid v-else-if="scope.row.state==3" type="warning">
                                维修
                            </el-tag-mid>
                        </template>
                    </el-table-column>
                    <el-table-column prop="deliver_date" label="发货时间" align="center">
                    </el-table-column>
                    <el-table-column prop="socketHeartLastTime" label="心跳最后更新时间" align="center">
                    </el-table-column>
                    <el-table-column label="操作" width="240" type="action" >
                        <template slot-scope="scope">
                       
                                <el-button type="text" icon="el-icon-edit" @click="editInfo(scope.row)">编辑</el-button>
                                <el-button type="text" icon="el-icon-delete" @click="handelDelete(scope.row.id)"
                                    style="color:#F56C6C">删除</el-button>
                         
                        </template>

                    </el-table-column>



                </flex-table>

版权所有,转载请注明原始链接

谢绝转载至csdn

 

 

2025年7月2日更新:

修复了存在多个模板列的时候,无法正确渲染的问题

<template>
    <div class="responsive-table-wrapper">
        <!-- PC端显示 -->
        <el-table v-if="!isMobile" :data="data" v-bind="$attrs" v-on="$listeners" ref="table" class="desktop-table">
            <slot></slot>
        </el-table>

        <!-- 移动端卡片显示 -->
        <div v-else class="mobile-cards">
            <div v-for="(row, rowIndex) in data" :key="getRowKey(row, rowIndex)" class="mobile-card"
                @click="handleRowClick(row, rowIndex)">
                <!-- 卡片头部 - 显示主要信息 -->
                <div class="card-header" v-if="primaryColumn">
                    <div class="card-title">
                        <span class="title-text">{{ getCellValue(row, primaryColumn) }}</span>

                    </div>
                </div>

                <!-- 卡片内容 -->
                <div class="card-content">
                    <div v-for="column in displayColumns" :key="column.prop || column.label" class="info-row"
                        :class="{ 'is-important': column.important }">
                        <span class="label" style="color: #c0c4cc;">{{ column.label }}:</span>
                        <div class="value">
                            <template v-if="column.scopedRender">
                                <div class="render-holder" :ref="`render-${rowIndex}-${column.prop}`"></div>
                            </template>

                            <!-- 普通文本显示 -->
                            <template v-else>
                                <span>{{ formatCellValue(row, column) }}</span>
                            </template>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 移动端无数据显示 -->
            <div v-if="!data || data.length === 0" class="mobile-empty">
                <slot name="empty">
                    <div class="empty-content">
                        <i class="el-icon-document"></i>
                        <p>暂无数据</p>
                    </div>
                </slot>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: 'FlexTable',
        inheritAttrs: false,
        props: {
            data: {
                type: Array,
                default: () => []
            },
            // 移动端主显示列(通常是标题或名称)
            primaryField: {
                type: String,
                default: 'deviceNumber'
            },
            // // 状态字段
            // statusField: {
            //     type: String,
            //     default: 'status'
            // },
            // 移动端隐藏的字段
            mobileHideFields: {
                type: Array,
                default: () => []
            },
            // 行点击事件
            rowClick: {
                type: Function,
                default: null
            },
            // 自定义断点
            mobileBreakpoint: {
                type: Number,
                default: 768
            }
        },
        data() {
            return {
                isMobile: false,
                columns: []
            }
        },
        computed: {
            primaryColumn() {
                if (this.primaryField) {
                    return this.columns.find(col => col.prop === this.primaryField)
                }
                // 默认取第一个有prop的列
                return this.columns.find(col => col.prop)
            },
            // 移动端显示的列(排除主列和隐藏列)
            displayColumns() {
                return this.columns.filter(col => {
                    // 只排除明确隐藏的字段
                    if (this.mobileHideFields.includes(col.prop)) return false
                    return true
                })
            }
        },
        mounted() {
            this.parseColumns()
            this.checkDevice()
            this.renderAllScopedSlots(); // 加上
            window.addEventListener('resize', this.checkDevice)
        },
        updated() {
            this.renderAllScopedSlots(); // DOM更新后重新插入 VNode
        },
        beforeDestroy() {
            window.removeEventListener('resize', this.checkDevice)
        },
        methods: {
            // 检测设备类型
            checkDevice() {
                this.isMobile = window.innerWidth <= this.mobileBreakpoint
            },
            renderAllScopedSlots() {
                if (!this.isMobile || !this.columns.length || !this.data.length) return;

                this.$nextTick(() => {
                    console.log('this.columns', this.columns);

                    this.data.forEach((row, rowIndex) => {
                        this.columns.forEach((column, columnIndex) => {
                            if (typeof column.scopedRender === 'function') {
                                const vnode = column.scopedRender({ row, column, $index: rowIndex });
                                const refName = `render-${rowIndex}-${column.prop}`;
                                // console.log('refName',refName);
                                // console.log('columnIndex',columnIndex);
                                // console.log('rowIndex',columnIndex);


                                const ref = this.$refs[refName];
                                const container = Array.isArray(ref) ? ref[0] : ref;
                                if (!container || !(container instanceof HTMLElement)) return;
                                container.innerHTML = '';
                                const vnodeRaw = column.scopedRender({ row, column, $index: rowIndex });
                                const vnodeArray = Array.isArray(vnodeRaw) ? vnodeRaw : [vnodeRaw];
                                const needWrap =
                                    vnodeArray.length > 1 || (vnodeArray.length === 1 && vnodeArray[0].tag === undefined);
                                const renderFn = h =>
                                    needWrap ? h('div', {}, vnodeArray) : vnodeArray[0]; // 不包多余的 div
                                const vm = new this.$root.constructor({
                                    parent: this,
                                    render: renderFn
                                }).$mount();
                                console.log('vm.el', vm.$el);

                                container.appendChild(vm.$el);
                            }
                        });
                    });
                });
            },
            // 解析列配置
            parseColumns() {
                this.$nextTick(() => {
                    if (this.$slots.default) {
                        this.columns = this.extractColumns(this.$slots.default)
                    }
                })
            },

            // 提取列配置
            extractColumns(vnodes) {
                const columns = []
                let actionColumnIndex = 0; // 添加操作列索引计数器
                const extractFromVNode = (vnode) => {
                    if (!vnode) return

                    if (vnode.componentOptions && vnode.componentOptions.tag === 'el-table-column') {
                        const props = vnode.componentOptions.propsData || {}
                        const children = vnode.componentOptions.children || []

                        const column = {
                            prop: props.prop || '',
                            label: props.label,
                            width: props.width,
                            minWidth: props.minWidth,
                            sortable: props.sortable,
                            align: props.align,
                            headerAlign: props.headerAlign,
                            className: props.className,
                            labelClassName: props.labelClassName,
                            formatter: props.formatter,
                            showOverflowTooltip: props.showOverflowTooltip,
                            fixed: props.fixed,
                            type: props.type,
                            index: props.index,
                            columnKey: props.columnKey,
                            renderHeader: props.renderHeader,
                            sortMethod: props.sortMethod,
                            sortBy: props.sortBy,
                            resizable: props.resizable,
                            // 检查是否有作用域插槽
                            // scopedSlot: this.getScopedSlotName(vnode),
                            // scopedSlot: vnode.data && vnode.data.scopedSlots && vnode.data.scopedSlots.default ? (column.prop || column.columnKey || column.label) : null,
                            scopedRender: vnode.data && vnode.data.scopedSlots && vnode.data.scopedSlots.default ? vnode.data.scopedSlots.default : null,


                            // 标记重要字段
                            important: props.important || false
                        }



                        // 如果没有prop但有操作,标记为操作列并生成唯一标识
                        if (!column.prop) {
                            if (children.length > 0) {
                             //   column.type = 'action'
                                column.prop = `action_${actionColumnIndex++}` // 生成唯一的prop
                            }
                            else {
                                column.prop = `slot_${actionColumnIndex++}` // 生成唯一的prop
                            }
                        }
                        columns.push(column)
                    }

                    // 递归处理子节点
                    if (vnode.children) {
                        vnode.children.forEach(extractFromVNode)
                    }
                    if (vnode.componentOptions && vnode.componentOptions.children) {
                        vnode.componentOptions.children.forEach(extractFromVNode)
                    }
                }

                vnodes.forEach(extractFromVNode)
                return columns
            },
            // 获取单元格值
            getCellValue(row, column) {
                if (!column || !column.prop) return ''

                const keys = column.prop.split('.')
                let value = row

                for (const key of keys) {
                    if (value && typeof value === 'object') {
                        value = value[key]
                    } else {
                        value = ''
                        break
                    }
                }

                return value
            },

            // 格式化单元格值
            formatCellValue(row, column) {
                let value = this.getCellValue(row, column)

                // 如果有formatter函数
                if (column.formatter && typeof column.formatter === 'function') {
                    return column.formatter(row, column, value)
                }

                // 默认格式化
                if (value === null || value === undefined) {
                    return ''
                }

                return value
            },

            // 获取状态组件
            getStatusComponent(row, column) {
                // 默认返回 el-tag
                return 'el-tag'
            },

            // 获取状态属性
            getStatusProps(row, column) {
                const value = this.getCellValue(row, column)

                // 默认状态映射
                const statusMap = {
                    0: { type: 'success' },
                    1: { type: 'danger' },
                    2: { type: 'info' },
                    3: { type: 'warning' },
                    '正常': { type: 'success' },
                    '异常': { type: 'danger' },
                    '故障': { type: 'danger' },
                    '关闭': { type: 'info' },
                    '维修': { type: 'warning' }
                }

                return {
                    size: 'mini',
                    ...statusMap[value]
                }
            },

            // 获取状态文本
            getStatusText(row, column) {
                const value = this.getCellValue(row, column)

                // 状态文本映射
                const textMap = {
                    0: '正常',
                    1: '故障',
                    2: '关闭',
                    3: '维修'
                }

                return textMap[value] || value
            },

            // 获取行key
            getRowKey(row, index) {
                if (this.$attrs['row-key']) {
                    if (typeof this.$attrs['row-key'] === 'function') {
                        return this.$attrs['row-key'](row)
                    }
                    return row[this.$attrs['row-key']]
                }
                return index
            },

            // 行点击处理
            handleRowClick(row, index) {
                if (this.rowClick) {
                    this.rowClick(row, index)
                }
                this.$emit('row-click', row, index)
            },

            // 暴露表格方法
            clearSelection() {
                if (this.$refs.table) {
                    this.$refs.table.clearSelection()
                }
            },

            toggleRowSelection(row, selected) {
                if (this.$refs.table) {
                    this.$refs.table.toggleRowSelection(row, selected)
                }
            },

            toggleAllSelection() {
                if (this.$refs.table) {
                    this.$refs.table.toggleAllSelection()
                }
            },

            setCurrentRow(row) {
                if (this.$refs.table) {
                    this.$refs.table.setCurrentRow(row)
                }
            },

            clearSort() {
                if (this.$refs.table) {
                    this.$refs.table.clearSort()
                }
            },

            clearFilter(columnKey) {
                if (this.$refs.table) {
                    this.$refs.table.clearFilter(columnKey)
                }
            },

            doLayout() {
                if (this.$refs.table) {
                    this.$refs.table.doLayout()
                }
            },

            sort(prop, order) {
                if (this.$refs.table) {
                    this.$refs.table.sort(prop, order)
                }
            }
        },
        watch: {
            '$slots.default': {
                handler() {
                    this.parseColumns()
                },
                deep: true
            }
        }
    }
</script>

<style scoped>
    .responsive-table-wrapper {
        width: 100%;
    }

    /* 移动端样式 */
    .mobile-cards {
        display: flex;
        flex-direction: column;
        gap: 12px;
    }

    .mobile-card {
        background: #fff;
        border-radius: 8px;
        padding: 16px;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
        border: 1px solid #ebeef5;
        transition: all 0.3s ease;
        cursor: pointer;
    }

    .mobile-card:hover {
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        transform: translateY(-1px);
    }

    .mobile-card:active {
        transform: translateY(0);
    }

    .card-header {
        margin-bottom: 12px;
        padding-bottom: 12px;
        border-bottom: 1px solid #f0f0f0;
    }

    .card-title {
        display: flex;
        justify-content: space-between;
        align-items: center;
        flex-wrap: wrap;
        gap: 8px;
    }

    .title-text {
        font-weight: 600;
        font-size: 16px;
        color: #303133;
        flex: 1;
        min-width: 0;
        word-break: break-all;
    }

    .status-tag {
        flex-shrink: 0;
    }

    .card-content {
        display: flex;
        flex-direction: column;
        gap: 8px;
    }

    .info-row {
        display: flex;
        justify-content: space-between;
        align-items: flex-start;
        padding: 6px 0;
        border-bottom: 1px solid #f8f9fa;
        min-height: 32px;
    }

    .info-row:last-child {
        border-bottom: none;
    }

    .info-row.is-important {
        background: #f0f9ff;
        margin: 0 -8px;
        padding: 8px;
        border-radius: 4px;
        border-bottom: none;
    }

    .label {
        font-size: 14px;
        color: #606266;
        font-weight: 500;
        min-width: 80px;
        max-width: 120px;
        flex-shrink: 0;
        line-height: 1.4;
    }

    .value {
        font-size: 14px;
        color: #303133;
        text-align: right;
        flex: 1;
        margin-left: 12px;
        word-break: break-all;
        line-height: 1.4;
        min-width: 0;
    }

    .value>>>.el-button {
        margin-left: 8px;
        padding: 4px 8px;
        font-size: 12px;
    }

    .value>>>.el-button:first-child {
        margin-left: 0;
    }

    /* 空数据样式 */
    .mobile-empty {
        text-align: center;
        padding: 40px 20px;
        color: #909399;
    }

    .empty-content i {
        font-size: 48px;
        color: #c0c4cc;
        margin-bottom: 16px;
    }

    .empty-content p {
        margin: 0;
        font-size: 14px;
    }

    /* 桌面端保持原有样式 */
    .desktop-table {
        width: 100%;
    }

    /* 响应式断点 */
    @media (max-width: 480px) {
        .mobile-card {
            margin: 0 -8px;
            border-radius: 0;
            border-left: none;
            border-right: none;
        }

        .card-title {
            flex-direction: column;
            align-items: flex-start;
        }

        .title-text {
            margin-bottom: 8px;
        }

        .info-row {
            flex-direction: column;
            align-items: flex-start;
            gap: 4px;
        }

        .value {
            text-align: left;
            margin-left: 0;
        }
    }
</style>

 

posted on 2025-07-01 15:44  火星大能猫  阅读(327)  评论(0)    收藏  举报