Vue Hook 封裝通用型表格

最小生成树發表於2024-08-01

一、建立通用型表格的需求

實現一個通用型表格元件,具備以下功能:

  1. 動態列配置。
  2. 分頁功能。
  3. 排序功能。
  4. 可擴充套件的行操作功能。

二、設計通用型表格元件

首先,需要設計一個基礎的表格元件,它接受列配置、資料和分頁資訊等引數。

1. 建立 useTable Hook

src/hooks 目錄下建立 useTable.js 檔案:

import { ref, reactive, onMounted, toRefs } from 'vue';

export function useTable(fetchData) {
  const state = reactive({
    loading: false,
    data: [],
    pagination: {
      currentPage: 1,
      pageSize: 10,
      total: 0,
    },
    sort: {
      field: '',
      order: '',
    },
  });

  const loadData = async () => {
    state.loading = true;
    const { currentPage, pageSize } = state.pagination;
    const { field, order } = state.sort;
    const result = await fetchData(currentPage, pageSize, field, order);
    state.data = result.data;
    state.pagination.total = result.total;
    state.loading = false;
  };

  const changePage = (page) => {
    state.pagination.currentPage = page;
    loadData();
  };

  const changePageSize = (size) => {
    state.pagination.pageSize = size;
    loadData();
  };

  const changeSort = (field, order) => {
    state.sort.field = field;
    state.sort.order = order;
    loadData();
  };

  onMounted(() => {
    loadData();
  });

  return {
    ...toRefs(state),
    loadData,
    changePage,
    changePageSize,
    changeSort,
  };
}

2. 建立 TableComponent.vue

src/components 目錄下建立 TableComponent.vue 檔案:

<template>
  <div>
    <table>
      <thead>
        <tr>
          <th v-for="col in columns" :key="col.key" @click="changeSort(col.key)">
            {{ col.title }}
            <span v-if="sort.field === col.key">{{ sort.order === 'asc' ? '↑' : '↓' }}</span>
          </th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="row in data" :key="row.id">
          <td v-for="col in columns" :key="col.key">{{ row[col.key] }}</td>
        </tr>
      </tbody>
    </table>
    <div class="pagination">
      <button @click="changePage(pagination.currentPage - 1)" :disabled="pagination.currentPage === 1">Previous</button>
      <span>{{ pagination.currentPage }} / {{ Math.ceil(pagination.total / pagination.pageSize) }}</span>
      <button @click="changePage(pagination.currentPage + 1)" :disabled="pagination.currentPage === Math.ceil(pagination.total / pagination.pageSize)">Next</button>
    </div>
  </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import { useTable } from '@/hooks/useTable';

export default {
  props: {
    fetchData: {
      type: Function,
      required: true,
    },
    columns: {
      type: Array,
      required: true,
    },
  },
  setup(props) {
    const { data, loading, pagination, sort, loadData, changePage, changePageSize, changeSort } = useTable(props.fetchData);

    return {
      data,
      loading,
      pagination,
      sort,
      loadData,
      changePage,
      changePageSize,
      changeSort,
    };
  },
};
</script>

<style scoped>
.pagination {
  display: flex;
  justify-content: center;
  margin-top: 10px;
}
</style>

三、使用通用型表格元件

在實際專案中,可以這樣使用這個通用型表格元件:

1. 建立 ExampleTable.vue 元件

src/views 目錄下建立 ExampleTable.vue 檔案:

<template>
  <div>
    <TableComponent :fetchData="fetchData" :columns="columns" />
  </div>
</template>

<script>
import TableComponent from '@/components/TableComponent.vue';

export default {
  components: {
    TableComponent,
  },
  setup() {
    const columns = [
      { key: 'name', title: 'Name' },
      { key: 'age', title: 'Age' },
      { key: 'email', title: 'Email' },
    ];

    const fetchData = async (page, pageSize, sortField, sortOrder) => {
      // 模擬資料獲取
      const total = 100;
      const data = Array.from({ length: pageSize }, (v, i) => ({
        id: (page - 1) * pageSize + i + 1,
        name: `Name ${(page - 1) * pageSize + i + 1}`,
        age: 20 + ((page - 1) * pageSize + i + 1) % 30,
        email: `user${(page - 1) * pageSize + i + 1}@example.com`,
      }));
      return { data, total };
    };

    return {
      columns,
      fetchData,
    };
  },
};
</script>

四、解釋程式碼

  1. 定義 useTable Hook

    • 使用 Vue 的 refreactive 定義表格狀態。
    • 定義 loadDatachangePagechangePageSizechangeSort 函式來處理資料載入和分頁、排序變化。
    • 使用 onMounted 生命週期鉤子在元件掛載時載入資料。
  2. 定義 TableComponent 元件

    • 接受 fetchDatacolumns 作為元件屬性。
    • 使用 useTable Hook 獲取表格資料和操作函式。
    • 渲染表格頭部、主體和分頁元件,並繫結相關事件。
  3. 使用通用型表格元件

    • ExampleTable.vue 中定義列配置和資料獲取函式。
    • 使用 TableComponent 並傳遞 fetchDatacolumns 屬性。

相關文章