vue3中,封装表格组件——包括表格模块、关闭按钮和分页组件 & vue3传值父子用法 & ts中interface和type的用法
效果-弹框1表格
效果-弹框2表格
代码
1、主页面
index.vue
<template> <div class="content"> <span style="color: #fff; font-size: 18px" @click="handleNumber">查看表格弹框数据</span> <div ref="echartsRef" class="attendance-data"></div> <!-- 二级页面 --> <monthly-number :is-show="showNumber" @is-close="handleCloseNumber"></monthly-number> </div> </template> <script setup lang="ts" name="absenteeism"> import monthlyNumber from "./modules/monthlyNumber.vue"; import { ref, onMounted, nextTick } from "vue"; const echartsRef = ref<HTMLElement>(); // 二级页面 const showNumber = ref(false); const handleCloseNumber = () => { showNumber.value = false; }; const handleNumber = () => { showNumber.value = true; }; onMounted(() => {}); </script> <style scoped lang="scss"> .content { margin-bottom: 8px; background: rgb(0 0 0 / 30%); border-radius: 10px; span { font-size: 16px; color: #ffffff; } } </style>
2、弹框页面
src\views\materials\centerArea\modules\monthlyNumber.vue
<template> <div class="root-second-box" v-show="props.isShow"> <Handle @handle-close="handleClose"></Handle> <div class="table-model"> <div class="query-model"> <div class="query-title"> <el-icon><ArrowLeftBold /></el-icon> <span>8月物资用途消耗数量分析</span> <el-icon><ArrowRightBold /></el-icon> </div> </div> <!-- 弹框1 物资用途分类 --> <Tables :columns="columnsList" :tableData="tableDataList" :handleCurrentChange="handleCurrentChange"> <template #operation="scope"> <span @click="details(scope.row)" class="root-table-btn" link>查看详情</span> </template> </Tables> <el-dialog v-model="dialogVisible" width="100%" :before-close="close" :show-close="false" :fullscreen="true"> <div class="handle-box"> <img src="@/assets/svg/close.svg" alt="" srcset="" @click="detailsClose" /> </div> <div class="table-model"> <div class="query-model"> <div class="title"> <span class="title-name">物资用途:{
{ purpose }}</span> </div> </div> </div> <!-- 弹框2 物资用途中的材料明细--> <Tables :columns="columnsThreeList" :tableData="tableDataThreeList" :handleCurrentChange="handleCurrentChange" class="table" > </Tables> </el-dialog> </div> </div> </template> <script setup lang="ts" name="historyAlarmList"> import Handle from "@/components/common/handle.vue"; import { onMounted, ref } from "vue"; import Tables from "@/components/Tables/index.vue"; import { ColumnProps } from "@/components/ProTable/interface"; const purpose = ref("安全措施"); const props = defineProps({ isShow: Boolean }); onMounted(() => { // dateTime = new Date(); }); // 二级弹框 const dialogVisible = ref(false); const details = (scope: any) => { console.log(scope); dialogVisible.value = true; }; const detailsClose = () => { dialogVisible.value = false; }; // 1、物资用途分类 const columnsList: Partial<ColumnProps>[] = [ // { label: "全选", type: "selection", width: "60" }, { label: "序号", prop: "index", width: "120" }, { label: "物资用途", prop: "names" }, { label: "计划金额(万元)", prop: "names" }, { label: "消耗金额(万元)", prop: "names" }, { label: "消耗数量", prop: "names" }, { label: "消耗占比(%)", prop: "names" }, { label: "达成率(%)", prop: "names" }, { label: "操作", prop: "operation" } ]; const tableDataList: any = [ { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" } ]; // 2、物资用途的材料明细 const columnsThreeList: Partial<ColumnProps>[] = [ // { label: "全选", type: "selection", width: "60" }, { label: "序号", prop: "index", width: "120" }, { label: "材料名称", prop: "names" }, { label: "计划金额(万元)", prop: "names" }, { label: "消耗金额(万元)", prop: "names" }, { label: "消耗数量", prop: "names" }, { label: "消耗占比(%)", prop: "names" }, { label: "达成率(%)", prop: "names" } ]; const tableDataThreeList: any = [ { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" }, { names: "--" } ]; // 页码发生变化 const handleCurrentChange = (val: number) => { console.log(`current page: ${val}`); }; // 关闭页面 const emit = defineEmits(["isClose"]); const handleClose = (val: boolean) => { emit("isClose", val); }; </script> <style scoped lang="scss"> .root-table-btn { color: #00f0ff; } .title { width: 100%; margin-top: 76px; margin-bottom: 20px; color: #ffffff; text-align: left; display: flex; align-items: center; .title-name { font-size: 24px; margin-right: 30px; } .date { font-size: 18px; margin-top: 4px; } } .table-dec { color: #fff; font-size: 14px; margin-bottom: 10px; > span { margin-right: 30px; } } :deep(.el-dialog) { .el-dialog__header { display: none; } .dj-dialog-content { padding: 0; overflow: unset; } } :deep(.el-dialog.is-fullscreen) { background: linear-gradient(#, #), linear-gradient(#010a1a, #010a1a), linear-gradient(#000000, #000000), #030e1a; } .handle-box { display: flex; position: fixed; z-index: 100; right: 172px; top: 12px; > img { width: 40px; cursor: pointer; padding: 12px; } > img:hover { background-color: #2a2d36; } } .table { padding: 0 100px; } </style>
3、关闭组件
src\components\common\handle.vue
<template> <div class="handle-box"> <img src="@/assets/svg/close.svg" alt="" srcset="" @click="handleClose" /> </div> </template> <script setup lang="ts" name="handle"> const emit = defineEmits(["handleClose"]); const handleClose = () => { emit("handleClose", false); }; </script> <style scoped lang="scss"> // @import "./index.scss"; .handle-box{ display: flex; position: fixed; z-index: 99; right: 36px; top: 12px; > img{ width: 40px; cursor: pointer; padding: 12px; } > img:hover{ background-color: #2A2D36; } } </style>
4、table组件
4.1、主界面
src\components\Tables\index.vue
<template> <div class="table-box"> <!-- 表格主体 --> <el-table ref="tableRef" :data="tableData" :border="border" @selection-change="selectionChange" :row-key="getRowKeys" :row-style="tableRowStyle" :stripe="stripe" :show-summary="showSummary" :sum-text="'本页合计'" :tree-props="{ children: childrenName }" > <template v-for="item in tableColumns" :key="item"> <!-- selection || index --> <el-table-column v-if="item.type == 'selection' || item.type == 'index'" :type="item.type" :reserve-selection="item.type == 'selection'" :label="item.label" :width="item.width" :fixed="item.fixed" :align="item.align" > </el-table-column> <!-- expand(展开查看详情,请使用作用域插槽) --> <el-table-column v-if="item.type == 'expand'" :type="item.type" :label="item.label" :width="item.width" :fixed="item.fixed" v-slot="scope" :align="item.align" > <slot :name="item.type" :row="scope.row"></slot> </el-table-column> <!-- other --> <el-table-column v-if="!item.type && item.prop && item.isShow" :prop="item.prop" :label="item.label" :width="item.width" :sortable="item.sortable" :show-overflow-tooltip="item.prop !== 'operation'" :resizable="true" :fixed="item.fixed" :align="item.align" > <!-- 自定义 header (使用组件渲染 tsx 语法) --> <template #header v-if="item.renderHeader"> <component :is="item.renderHeader" :row="item"> </component> </template> <!-- 自定义配置每一列 slot(使用作用域插槽) --> <template #default="scope"> <slot :name="item.prop" :row="scope.row"> <!-- 图片(自带预览) --> <el-image v-if="item.image" :src="scope.row[item.prop!]" :preview-src-list="[scope.row[item.prop!]]" fit="cover" class="table-image" preview-teleported /> <!-- tag 标签(自带格式化内容) --> <el-tag v-else-if="item.tag" :type="filterEnum(scope.row[item.prop!],item.enum,'tag')"> {
{ item.enum?.length ? filterEnum(scope.row[item.prop!], item.enum) : defaultFormat(0, 0, scope.row[item.prop!]) }} </el-tag> <!-- 文字(自带格式化内容) --> <span v-else> {
{ item.enum?.length ? filterEnum(scope.row[item.prop!], item.enum) : defaultFormat(0, 0, scope.row[item.prop!]) }} </span> </slot> </template> </el-table-column> </template> <!-- <template #empty> <div class="table-empty"> <img src="@/assets/images/notData.png" alt="notData" /> <div>暂无数据</div> </div> </template> --> </el-table> <!-- 分页 --> <Pagination v-if="pagination" layout="prev, pager, next, jumper" :pageable="pageable" :handleSizeChange="handleSizeChange" :handleCurrentChange="handleCurrentChange" ></Pagination> </div> </template> <script setup lang="ts" name="proTable"> import { ref } from "vue"; import { useSelection } from "@/hooks/useSelection"; import { ColumnProps } from "@/components/ProTable/interface"; import { filterEnum, defaultFormat } from "@/utils/util"; import Pagination from "@/components/Pagination/index.vue"; // 表格 DOM 元素 const tableRef = ref(); const tableRowStyle = (arg: any) => { if (arg.rowIndex % 2 == 0) { return { backgroundColor: "rgb(18, 19, 24)", color: "#fff", border: "0px" }; } else { return { backgroundColor: "rgba(42, 45, 54, 0.4)", color: "#fff", border: "0px" }; } }; // 是否显示搜索模块 interface ProTableProps { tableData?: any; // 数据 columns: Partial<ColumnProps>[]; // 列配置项 pagination?: boolean; // 是否需要分页组件 ==> 非必传(默认为true) border?: boolean; // 表格是否显示边框 ==> 非必传(默认为true) stripe?: boolean; // 是否带斑马纹表格 ==> 非必传(默认为false) toolButton?: boolean; // 是否显示表格功能按钮 ==> 非必传(默认为true) childrenName?: string; // 当数据存在 children 时,指定 children key 名字 ==> 非必传(默认为"children") showSummary?: boolean; // 是否显示合计行 pageable: any; // 分页数据 } // 接受父组件参数,配置默认值 const props = withDefaults(defineProps<ProTableProps>(), { tableData: () => [], columns: () => [], pagination: true, border: true, stripe: false, toolButton: true, childrenName: "children", showSummary: false, pageable: { // 当前页数 pageNum: 1, // 每页显示条数 pageSize: 10, // 总条数 total: 0 } }); // 表格多选 Hooks const { selectionChange, getRowKeys } = useSelection(); // 表格列配置项处理(添加 isShow 属性,控制显示/隐藏) const tableColumns = ref<Partial<ColumnProps>[]>(); tableColumns.value = props.columns.map(item => { return { ...item, isShow: item.isShow ?? true }; }); const emit = defineEmits(["handleSizeChange", "handleCurrentChange"]); const handleSizeChange = (val: number) => { // console.log(`${val} items per page`); emit("handleSizeChange", val); }; // 页码发生改变时触发 const handleCurrentChange = (val: number) => { // console.log(`current page: ${val}`); emit("handleCurrentChange", val); }; </script> <style scoped lang="scss"></style>
4.2、表格多选方法
src\hooks\useSelection.ts
import {
ref, computed } from "vue"; / * @description 表格多选数据操作 * */ export const useSelection = () => {
// 是否选中数据 const isSelected = ref<boolean>(false); // 选中的数据列表 const selectedList = ref([]); // 当前选中的所有ids(数组),可根据项目自行配置id字段 const selectedListIds = computed((): string[] => {
let ids: string[] = []; selectedList.value.forEach(item => {
ids.push(item["id"]); }); return ids; }); // 获取行数据的 Key,用来优化 Table 的渲染;在使用跨页多选时,该属性是必填的 const getRowKeys = (row: {
id: string }) => {
return row.id; }; / * @description 多选操作 * @param {Array} rowArr 当前选择的所有数据 * @return void */ const selectionChange = (rowArr: any) => {
rowArr.length === 0 ? (isSelected.value = false) : (isSelected.value = true); selectedList.value = rowArr; }; return {
isSelected, selectedList, selectedListIds, selectionChange, getRowKeys }; };
4.3、定义类型
src\components\ProTable\interface\index.ts
export interface EnumProps {
label: string; // 选项框显示的文字 value: any; // 选项框值 disabled?: boolean; // 是否禁用此选项 tagType?: string; // 当 tag 为 true 时,此选择会指定 tag 显示类型 children?: EnumProps[]; // 为树形选择时,可以通过 children 属性指定子选项 } export type SearchType = | "text" | "select" | "multipleSelect" | "treeSelect" | "multipleTreeSelect" | "date" | "daterange" | "timerange" | "datetimerange"; export type TypeProp = "index" | "selection" | "expand"; export type FixedProp = "left" | "right"; export interface ColumnProps {
type: TypeProp; // index | selection | expand(特殊类型) prop: string; // 单元格数据(非特殊类型必填) label: string; // 单元格标题(非特殊类型必填) width: number | string; // 列宽 isShow: boolean; // 是否显示在表格当中 sortable: boolean; // 是否可排序(静态排序) fixed: FixedProp; // 固定列 tag: boolean; // 是否是标签展示 image: boolean; // 是否是图片展示 search: boolean; // 是否为搜索项 searchType: SearchType; // 搜索项类型 searchProps: {
[key: string]: any }; // 搜索项参数,根据 element 文档来,标签自带属性 > props 属性 searchInitParam: string | number | boolean | any[]; // 搜索项初始值 enum: EnumProps[]; // 枚举类型(渲染值的字典) renderHeader: (params: any) => any; // 自定义表头 align: string; }
4.4、用到的公共方法
src\utils\util.ts
import {
isArray } from "@/utils/is"; / * @description 格式化表格单元格默认值 * @param {Number} row 行 * @param {Number} col 列 * @param {String} callValue 当前单元格值 * @return string * */ export function defaultFormat(row: number, col: number, callValue: any) {
// 如果当前值为数组,使用 / 拼接(根据需求自定义) if (isArray(callValue)) return callValue.length ? callValue.join(" / ") : "--"; return callValue ?? "--"; } / * @description 根据枚举列表查询当需要的数据 * @param {String} callValue 当前单元格值 * @param {Array} enumData 枚举列表 * @param {String} type 过滤类型(目前只有 tag) * @return string * */ export function filterEnum(callValue: any, enumData: any[] = [], type?: string): string {
let filterData = enumData.find(item => item.value === callValue); if (type == "tag") return filterData?.tagType ? filterData.tagType : ""; return filterData ? filterData.label : "--"; }
src\utils\is\index.ts
/ * @description: 是否为数组 */ export function isArray(val: any): val is Array<any> {
return val && Array.isArray(val); }
5、分页组件
src\components\Pagination\index.vue
<template> <el-pagination :currentPage="pageable.pageNum" :page-size="pageable.pageSize" :page-sizes="[10, 25, 50, 100]" :background="true" layout="total, sizes, prev, pager, next, jumper" :total="pageable.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" ></el-pagination> </template> <script setup lang="ts" name="pagination"> interface Pageable { pageNum: number; pageSize: number; total: number; } interface PaginationProps { pageable: Pageable; handleSizeChange: (size: number) => void; handleCurrentChange: (currentPage: number) => void; } defineProps<PaginationProps>(); </script>
到此这篇vue3中,封装表格组件——包括表格模块、关闭按钮和分页组件 & vue3传值父子用法 & ts中interface和type的用法的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/qdvuejs/10869.html