vue3+Ts+elementPlus二次封装Table分页表格,表格内展示图片、switch开关、支持

发布于:2025-03-19 ⋅ 阅读:(26) ⋅ 点赞:(0)

目录

一.项目文件结构

二.实现代码

1.子组件(表格组件)

 2.父组件(使用表格)


一.项目文件结构

1.表格组件(子组件)位置

2.使用表格组件的页面文件(父组件)位置

3.演示图片位置

elementPlus表格 Table 表格 | Element Plus

4.笑果演示

表格笑果

点击图片放大显示笑果

二.实现代码

1.子组件(表格组件)

1. src/views/Table.vue html部分

<!-- 表格区域 传值格式 -->
<!-- 单行文字:{ prop: 'inventory', label: '库存', width: '90' }, -->
<!-- 图片格式:{ prop: 'profile_picture_url', label: '商品主图', width: '110', isImg: true, height: 50 }, -->
<!-- 双行文字格式:{ prop: 'information', label: '商品信息', width: '140', doubleRow: true, text1: '货号', text2: '售价' }, -->
<!-- 开关格式:{ prop: 'listed', label: '是否上架', width: '110', isSwitch: true, }, -->
<el-table class="" v-loading="tableLoading" element-loading-text="Loading..."
            :element-loading-spinner="svg" element-loading-svg-view-box="-10, -10, 50, 50"
            element-loading-background="rgba(122, 122, 122, 0.8)" border :data="paginatedData"
            :default-sort="{ prop: 'date', order: 'descending' }" height="calc(100vh - 235px)"
            :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
            <el-table-column v-if="isSelected" type="selection" :selectable="selectable" width="55" />
            <el-table-column fixed align="center" label="序号" width="60">
                <template #default="scope">
                    {{ scope.$index + 1 }}
                </template>
            </el-table-column>
            <el-table-column align="center" sortable v-for="(i, n) in columns" :key="n" :prop="i.prop" :label="i.label"
                :formatter="i.formatter" :width="i.width">
                <template #default="scope">
                    <!-- 当表头数据中有isImg为true时,使单元格展示图片(点击事件为放大显示) -->
                    <img class="image" v-if="i.isImg && scope.row[i.prop]" :src="scope.row[i.prop]" alt=""
                        draggable="false" :style="`width: ${i.height}px; height: ${i.height}px`"
                        @click="handleImageClick(scope.row[i.prop])" />
                    <img v-else-if="i.isImg && !scope.row[i.prop]" src="@/assets/image/giegie.jpg" alt=""
                        draggable="false" :style="`width: ${i.height}px; height: ${i.height}px`" />
                    <span v-else-if="i.doubleRow">
                        <span class="doubleRow">{{ i.text1 + ': ' }}</span> {{ scope.row.information?.text1 ?
                            scope.row.information.text1 : '-' }}<br>
                        <span class="doubleRow">{{ i.text2 + ': ' }}</span> {{ scope.row.information?.text2 ?
                            scope.row.information.text2 : '-' }}
                    </span>
                    <el-switch v-else-if="i.isSwitch" active-text="" inactive-text="" active-color="#2fa1f1"
                        inactive-color="#9c9c9c" v-model="scope.row[i.prop]" @change="handleStatusChange(scope.row)" />
                    <span v-else>
                        {{ formatCell(i, scope.row) }}
                    </span>
                </template>
            </el-table-column>
            <!-- 固定列 -->
            <el-table-column v-if="isFixedColumn" fixed="right" align="center" label="操作" width="100">
                <template #default="scope">
                    <el-button link type="primary" size="small" @click="btnListTable[0].click(scope.row, 1)">{{
                        btnListTable[0].name
                        }}</el-button>
                    <el-popover placement="bottom-start" trigger="click"
                        :popper-style="{ minWidth: '55px', padding: '10', width: 'auto', cursor: 'pointer' }">
                        <template #reference>
                            <el-button link type="primary" size="small">更多</el-button>
                        </template>
                        <div>
                            <el-button v-for="(i, n) in btnListTable2" :key="n" link type="primary" size="small"
                                @click="i.click(scope.row, 1)">{{
                                    i.name }}</el-button>
                        </div>
                    </el-popover>
                </template>
            </el-table-column>
        </el-table>

js部分

<script setup lang='ts'>
import { ref, computed, defineProps, onMounted, defineEmits } from 'vue';
// 父传子
const props = defineProps({
    columns: {// 表头数据
        type: Array,
        validator: () => {
            return [];
        }
    },
    paginatedData: {// 表格数据
        type: Array,
        validator: () => {
            return [];
        }
    },
    btnListTable: {// 按钮组
        type: Array,
        validator: () => {
            return [];
        }
    },
    isFixedColumn: {// 是否有操作列
        type: Boolean,
        default: true
    },
    isSelected: {// 是否有选择框
        type: Boolean,
        default: false
    },
    tableLoading: {// 是否加载中
        type: Boolean,
        default: false,
    },
});

// 操作列 更多按钮组
const btnListTable2 = ref(props.btnListTable.slice(1))
// 多选
interface User {
    id: number
    date: string
    name: string
    address: string
}
// 选择的内容
const multipleSelection = ref<User[]>([])
const selectable = (row: User) => ![1, 2].includes(row.id)
const handleSelectionChange = (val: User[]) => {
    multipleSelection.value = val
}
// 分页
const currentPage = ref(1);
const pageSize = ref(10);
const disabled = ref(false)
const background = ref(false)
const enlargedImageUrl = ref('');
const dialogVisible = ref(false);

// 点击图片事件
const handleImageClick = (imageUrl: any) => {
    enlargedImageUrl.value = imageUrl;
    dialogVisible.value = true;
}

// 子传父
const def = defineEmits(['pageSize', 'currentPage', 'switch']);

// 分页事件 val: number
const handleSizeChange = () => {
    def('pageSize', pageSize.value)
}
const handleCurrentChange = () => {
    def('currentPage', currentPage.value)
}

// 计算总数据条数
const totalData = computed(() => props.paginatedData.length);
// 开关事件
const handleStatusChange = ((row: any) => {
    def('switch', row)
})
// 加载中
const svg = `
        <path class="path" d="
          M 30 15
          L 28 17
          M 25.61 25.61
          A 15 15, 0, 0, 1, 15 30
          A 15 15, 0, 1, 1, 27.99 7.5
          L 15 15
        " style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
      `

// 格式化单元格内容
const formatCell = (column: any, row: string) => {
    return column.formatter ? column.formatter(row) : row[column.prop];
}
</script>

 2.父组件(使用表格)

1.src/views/Dashboard.vue html部分

<template>
  <div>
    <Table :columns="columns" :paginatedData="paginatedData" :btnListTable="btnListTable"
      :isFixedColumn="true" :tableLoading="loading" @pageSize="handlePageSize" @currentPage="handleCurrentPage">
    </Table>
  </div>
</template>

 2.js部分

<script setup lang='ts'>
import { ref, reactive } from 'vue'
import type { FormRules } from 'element-plus'
import Table from '../components/Table.vue';
import DialogCom from '../components/DialogCom.vue';
import { list } from '../api/api.ts'
import { require } from '@/utils/require';
// 表格相关 开始
const loading = false
// 表头数据
const columns = ref([
  { prop: 'user_id', label: '用户ID', width: 130 },
  { prop: 'username', label: '用户名', width: 120 },
  { prop: 'email', label: '邮件地址', width: 200 },
  { prop: 'phone_number', label: '手机号码', width: 170 },
  { prop: 'full_name', label: '真实姓名', width: 120 },
  { prop: 'date_of_birth', label: '生日', width: 140 },
  { prop: 'gender', label: '性别', width: 100 },
  { prop: 'listed', label: '是否打篮球', width: '130', isSwitch: true, },
  { prop: 'profile_picture_url', label: '头像', width: 90, isImg: true, height: 20 },
  // 当是否激活为true时,显示"是"
  { prop: 'is_active', label: '是否激活', width: 120, formatter: (row: any) => row.is_active ? '是' : '否' },
  { prop: 'created_at', label: '创建时间', width: 180, formatter: (row: any) => formattedDateTime(row.created_at) },
  { prop: 'updated_at', label: '更新时间', width: 180 },
]);
// 表格数据
const paginatedData = ref([
  { user_id: 'ID', username: '苏珊', email: 'singJumpRapBasketball@ikun.com', is_active: true, created_at: '2023-10-05T14:48:00.000Z', listed: true },
  { user_id: 'ID', username: '打球被笑两年半', email: 'kunkun@ikun.com', is_active: false, created_at: '2023-10-05T14:48:00.000Z', profile_picture_url: require('@/assets/image/giegie.jpg') },
]);
// 查询条件
const formBtnList = reactive({
  pageSize: 10,
  currentPage: 1,
})
// 操作列按钮事件
const pricingDetail = () => {
  console.log('pricingDetail')
}
const reject = () => {
  console.log('reject')
}
// 操作列按钮组
const btnListTable = ref([
  { name: '编辑', type: 'primary', click: pricingDetail },
  { name: '添加账户', type: 'primary', click: reject },
  { name: '导出', type: 'primary', click: reject },
])
// 处理日期时间 (ISO 8601 格式 如:2023-10-05T14:48:00.000Z )
const formattedDateTime = (dateData: string) => {
  const date = new Date(dateData);
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要加1
  const day = String(date.getDate()).padStart(2, '0');
  const hours = String(date.getHours()).padStart(2, '0');
  const minutes = String(date.getMinutes()).padStart(2, '0');
  const seconds = String(date.getSeconds()).padStart(2, '0');
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// 分页事件触发
const handlePageSize = (pageSize: number) => {// 每页大小
  // console.log('handlePageSize', pageSize);
  formBtnList.pageSize = pageSize
  handInquire()
};
const handleCurrentPage = (currentPage: number) => {// 页码
  // console.log('handleCurrentPage', currentPage);
  formBtnList.currentPage = currentPage
  handInquire()
};
// 表格相关 结束

以上,感谢观看,欢迎指正