Vue3 + TypeScript 中 hook 优化记录

useElTableExtendedInstance.ts

TypeScript 复制代码
import type { ElTableExtendedInstance } from "@/interface";
import { ref, type Ref } from "vue";

/**
 * ElTable 扩展实例 hook
 * 该 hook 的作用是处理 ElTable 的列属性和列标签之间的映射关系
 * @param tableRef 表格实例的引用,用于访问和操作表格的属性和方法
 * @returns 返回一个对象,包含列属性和列标签的键值对以及获取这些键值对的方法
 */
export const useElTableExtendedInstance = (tableRef: Ref<ElTableExtendedInstance | null>) => {
  // 列属性和列标签的映射表
  const columnPropertyLabelMap = ref<Record<string, string>>({});

  /**
   * 将列信息提取为 Record 结构
   * 该函数接收一个列数组,每个列包含 property 和 label 属性,并将其转换为一个键值对对象
   * @param columns 列数组,包含每个列的 property 和 label 属性
   * @returns 返回一个 Record 对象,其中键是列属性,值是列标签
   */
  const extractColumnsToRecord = (columns: Array<{ property: string; label: string }>): Record<string, string> => {
    return columns.reduce((acc, column) => {
      acc[column.property] = column.label ?? "";
      return acc;
    }, {} as Record<string, string>);
  };

  /**
   * 更新列属性和列标签的键值对
   * 该函数通过访问表格实例的列信息,过滤出具有 property 属性的列,并调用 extractColumnsToRecord 函数
   * 将其转换为键值对对象,然后将其赋值给 columnPropertyToLabelMap
   */
  const updateColumnPropertyLabelMap = () => {
    const tableInstance = tableRef.value;
    if (!tableInstance || !tableInstance.store) return;

    const columns = tableInstance.store?.states?.columns?.value;
    if (!Array.isArray(columns)) return;

    const filteredColumns = columns.filter(
      (column) => column.property && typeof column.property === "string" && column.property.trim() !== ""
    );

    columnPropertyLabelMap.value = extractColumnsToRecord(filteredColumns);
  };

  // 返回列属性和列标签的键值对以及获取键值对的方法
  return {
    columnPropertyLabelMap,
    updateColumnPropertyLabelMap
  };
};

优化

TypeScript 复制代码
import type { ElTableExtendedInstance } from "@/interface";
import { exportExcelFile } from "@/utils/excelUtils";
import { ref, type Ref } from "vue";

/**
 * ElTable 扩展实例 hook
 * 该 hook 的作用是处理 ElTable 的列属性和列标签之间的映射关系
 * @param tableRef 表格实例的引用,用于访问和操作表格的属性和方法
 * @returns 返回一个对象,包含列属性和列标签的键值对以及获取这些键值对的方法
 */
export const useElTableExtendedInstance = (tableRef: Ref<ElTableExtendedInstance | null>) => {
  // 列属性和列标签的映射表
  const columnPropertyLabelMap = ref<Record<string, string>>({});

  /**
   * 获取当前表格的有效列数组
   * 如果表格实例或其状态无效,则返回 null
   */
  const getTableColumns = (): Array<{ property: string; label: string }> | null => {
    const instance = tableRef.value;
    if (!instance || !instance.store || !instance.store.states || !instance.store.states.columns) {
      return null;
    }
    const columns = instance.store.states.columns.value;
    return Array.isArray(columns) ? columns : null;
  };

  /**
   * 安全地从对象中获取指定属性的值
   * 避免访问 null 或非对象类型时出错
   */
  const getPropertySafely = <T extends Record<string, any>, K extends keyof T>(obj: T, key: K): T[K] | undefined => {
    if (obj === null || typeof obj !== "object" || Array.isArray(obj)) {
      return undefined;
    }
    return obj[key];
  };

  /**
   * 解析单个列对象,转换为 property 和 label
   * @param column 待解析的列对象
   * @returns 若合法则返回 { property, label },否则返回 null
   */
  const parseColumnToPropertyLabel = (column: any): { property: string; label: string } | null => {
    if (typeof column !== "object" || column === null) {
      return null;
    }

    const property = getPropertySafely(column, "property");
    const label = getPropertySafely(column, "label");

    if (typeof property !== "string" || property.trim() === "") {
      return null;
    }

    const normalizedLabel = label == null ? "" : String(label).trim();

    return {
      property: property.trim(),
      label: normalizedLabel
    };
  };

  /**
   * 将列信息提取为 Record 结构
   * 该函数接收一个列数组,每个列包含 property 和 label 属性,并将其转换为一个键值对对象
   * 注意:遇到重复 property 时,仅保留第一个出现的项
   * @param columns 列数组,包含每个列的 property 和 label 属性
   * @returns 返回一个 Record 对象,其中键是列属性,值是列标签
   */
  const extractColumnsToRecord = (columns: Array<{ property: string; label: string }>): Record<string, string> => {
    if (!Array.isArray(columns) || columns.length === 0) {
      return {};
    }

    const resultMap: Record<string, string> = {};

    for (const column of columns) {
      const parsed = parseColumnToPropertyLabel(column);
      if (!parsed) continue;

      const { property, label } = parsed;

      // 若有重复的 property,保留第一个出现的项
      if (!(property in resultMap)) {
        resultMap[property] = label;
      }
    }

    return resultMap;
  };

  /**
   * 更新列属性和列标签的键值对
   * 该函数通过访问表格实例的列信息,过滤出具有 property 属性的列,并调用 extractColumnsToRecord 函数
   * 将其转换为键值对对象,然后将其赋值给 columnPropertyToLabelMap
   */
  const updateColumnPropertyLabelMap = () => {
    const columns = getTableColumns();
    if (!columns) {
      columnPropertyLabelMap.value = {};
      return;
    }

    columnPropertyLabelMap.value = extractColumnsToRecord(columns);
  };

  /**
   * 表格数据导出为 Excel 文件
   * @param filename 文件名
   */
  const tableDataExportToExcelFile = (filename?: string) => {
    const tableData = tableRef.value?.data ?? [];
    if (Object.keys(columnPropertyLabelMap.value).length === 0) {
      updateColumnPropertyLabelMap();
    }
    exportExcelFile(tableData, columnPropertyLabelMap.value, filename);
  };

  return {
    tableDataExportToExcelFile
  };
};

在组件中使用

ReagentTransactionsDrawer.vue

TypeScript 复制代码
<script setup lang="ts" name="ReagentTransactionsDrawer">

import { useElTableExtendedInstance } from "@/hooks/useElTableExtendedInstance";
import type { ElTableExtendedInstance } from "@/interface";

// 表格实例对象
const tableRef = ref<ElTableExtendedInstance | null>(null);
const { tableDataExportToExcelFile } = useElTableExtendedInstance(tableRef);

// 导出
const onExportClick = () => {
  // 表格数据导出为 Excel 文件
  tableDataExportToExcelFile("流转记录");
};

</script>
相关推荐
华仔啊8 分钟前
前端登录token到底应该存在哪?LocalStorage、SessionStorage还是Cookie?一篇说透!
前端·javascript
周周记笔记19 分钟前
学习笔记:Python的起源
开发语言·python
懒大王952733 分钟前
uni-app + Vue3 + EZUIKit.js 播放视频流
开发语言·javascript·uni-app
_extraordinary_37 分钟前
Java 多线程进阶(四)-- 锁策略,CAS,synchronized的原理,JUC当中常见的类
java·开发语言
JasmineX-11 小时前
数据结构——顺序表(c语言笔记)
c语言·开发语言·数据结构·笔记
xkroy1 小时前
ajax
前端·javascript·ajax
Yvonne爱编码1 小时前
AJAX入门-URL、参数查询、案例查询
前端·javascript·ajax
java搬砖工-苤-初心不变1 小时前
OpenResty 配合 Lua 脚本的使用
开发语言·lua·openresty
IT灰猫1 小时前
C++轻量级配置管理器升级版
开发语言·c++·设计模式·配置管理·ini解析
Swift社区2 小时前
如何解决 Vue2 前端项目为何无法访问本地资源(chunk.js 加载一直 pending/转圈)
开发语言·前端·javascript