From 2a2bba8539f56c4ac999b3be435e3aab51478b2c Mon Sep 17 00:00:00 2001 From: hechang27-sprt Date: Wed, 25 Mar 2026 11:47:07 +0800 Subject: [PATCH] feat: add list-table-v2 component with el-table-v2 virtual scrolling - Create ListTableV2 component using Element Plus el-table-v2 for performance - Use ElAutoResizer for automatic container height sizing - Support auto-distributed column widths via flexGrow - Convert parent slots to cellRenderer functions using renderSlot - Fix incorrect import path in useListTable.ts and plugs/store/index.ts - Add Vue DevTools plugin for debugging - Add demo page with 100 rows of test data - Add menu entry and i18n translations - Update component guidelines with el-table-v2 usage notes Co-Authored-By: Claude Opus 4.6 --- .../spec/frontend/component-guidelines.md | 157 +++++++-- examples/App.vue | 51 ++- examples/config/language/en.ts | 1 + examples/config/language/zh.ts | 1 + examples/config/router.ts | 6 + examples/view/base/table-v2.vue | 104 ++++++ packages/base/data/list-table-v2.vue | 319 ++++++++++++++++++ packages/base/index.ts | 3 +- plugs/composables/useListTable.ts | 2 +- plugs/store/index.ts | 2 +- vite.config.ts | 3 + 11 files changed, 613 insertions(+), 36 deletions(-) create mode 100644 examples/view/base/table-v2.vue create mode 100644 packages/base/data/list-table-v2.vue diff --git a/.trellis/spec/frontend/component-guidelines.md b/.trellis/spec/frontend/component-guidelines.md index 6836c3f..c9daf77 100644 --- a/.trellis/spec/frontend/component-guidelines.md +++ b/.trellis/spec/frontend/component-guidelines.md @@ -6,54 +6,161 @@ ## Overview - +--- -(To be filled by the team) +## Element Plus el-table-v2 Usage ---- +**IMPORTANT**: `el-table-v2` has a different API than `el-table`. Key differences: -## Component Structure +### Auto-resizing with ElAutoResizer - +`el-table-v2` does NOT have an `autosize` prop. Use `ElAutoResizer` wrapper instead: -(To be filled by the team) +```vue + + + +``` ---- +**Note**: Parent container of `ElAutoResizer` must have a fixed height (e.g., `height: 100%` or explicit pixel value). -## Props Conventions +### el-table-v2 Required Props + +`el-table-v2` requires both `width` and `height` as mandatory props - they cannot be omitted. + +### Slot-to-CellRenderer Conversion + +`el-table-v2` uses `cellRenderer` functions instead of Vue slots. To render parent slots: + +```typescript +import { useSlots, renderSlot } from 'vue'; - +const slots = useSlots(); -(To be filled by the team) +// In column definition: +col.cellRenderer = ({ cellData, rowData }) => { + if (slots[slotName]) { + return renderSlot(slots, slotName, { row: rowData }); + } + return h('span', {}, formattedValue); +}; +``` + +### Column Flex Distribution + +For auto-distributed column widths (no explicit width), use `flexGrow: 1`: + +```typescript +const col = { + key: 'code', + title: 'Name', + dataKey: 'code', + width: 120, // minimum width required + flexGrow: 1, // expands to fill available space + align: 'center', +}; +``` --- -## Styling Patterns +## Import Path Conventions + +### Correct Import Patterns + +```typescript +// ✅ CORRECT: Use relative paths for internal modules +import { clearAndAssign, deepCopy } from "../util/objectUtil"; +import { ElAutoResizer, ElTableV2 } from "element-plus"; - +// ❌ WRONG: noob-mengyxu/utils does not exist +import { clearAndAssign } from "noob-mengyxu/utils"; +``` -(To be filled by the team) +**Rule**: For utility functions within the project, always use relative paths (`../util/`, `./`). The `noob-mengyxu` namespace is only for exporting packages. --- -## Accessibility +## Component Structure + +```vue + + + + + +``` + +--- + +## Props Conventions + +1. Use `withDefaults(defineProps())` for optional props with defaults +2. Always provide default values for array/object props +3. Use TypeScript interfaces for complex prop types --- ## Common Mistakes - +### 1. Timestamp Formatting Assumptions + +The `formatStamp` function expects Unix timestamps in **seconds** (not milliseconds): + +```typescript +const formatStamp = (value: any) => { + const date = new Date(value * 1000); // expects seconds + // ... +}; +``` + +When generating test data, use `Math.floor(Date.now() / 1000)` or `Date.now() / 1000`. + +### 2. Element Plus Version Mismatch + +Element Plus `el-table` (v1) and `el-table-v2` (virtualized) have completely different APIs: +- v1: uses `prop` for columns, slots for cell rendering +- v2: uses `columns` prop, `cellRenderer` functions, separate `ElAutoResizer` + +### 3. Forgetting Required Props + +When using new Element Plus components, verify required props - they will cause runtime errors if omitted. + +--- + +## Accessibility -(To be filled by the team) +- Use semantic HTML elements +- Ensure keyboard navigation works +- Add ARIA labels where necessary diff --git a/examples/App.vue b/examples/App.vue index c98ef86..4bb94f8 100644 --- a/examples/App.vue +++ b/examples/App.vue @@ -1,11 +1,13 @@ @@ -13,7 +15,7 @@ diff --git a/packages/base/data/list-table-v2.vue b/packages/base/data/list-table-v2.vue new file mode 100644 index 0000000..da5efd2 --- /dev/null +++ b/packages/base/data/list-table-v2.vue @@ -0,0 +1,319 @@ + + + + + diff --git a/packages/base/index.ts b/packages/base/index.ts index 8ca70ab..4eb1b2c 100644 --- a/packages/base/index.ts +++ b/packages/base/index.ts @@ -11,6 +11,7 @@ import TzDateTime from "./item/tzDateTime.vue"; import WsMonitorToggle from "./item/ws-monitor-toggle.vue"; import SearchRow from "./data/search-row.vue"; import ListTable from "./data/list-table.vue"; +import ListTableV2 from "./data/list-table-v2.vue"; import Infomation from "./data/infomation.vue"; import ModifyForm from "./data/modify-form.vue"; import Descriptions from "./data/descriptions.vue"; @@ -31,4 +32,4 @@ export { WsMonitorToggle, }; -export { SearchRow, ListTable, ListTableDialog, Infomation, ModifyForm, Descriptions, TableAction }; +export { SearchRow, ListTable, ListTableV2, ListTableDialog, Infomation, ModifyForm, Descriptions, TableAction }; diff --git a/plugs/composables/useListTable.ts b/plugs/composables/useListTable.ts index 2843689..f631468 100644 --- a/plugs/composables/useListTable.ts +++ b/plugs/composables/useListTable.ts @@ -3,7 +3,7 @@ import { reactive, ref, shallowRef, toRaw, watchEffect } from "vue"; import * as Element from "../element"; import { useI18n } from "vue3-i18n"; import { PageResponse } from "../http"; -import { clearAndAssign, deepCopy } from "noob-mengyxu/utils"; +import { clearAndAssign, deepCopy } from "../util/objectUtil"; const { showMessage } = Element; diff --git a/plugs/store/index.ts b/plugs/store/index.ts index 64ac684..380602b 100644 --- a/plugs/store/index.ts +++ b/plugs/store/index.ts @@ -2,7 +2,7 @@ import { createStore as create } from "vuex"; import { Styles, Size } from "../config"; import { getByCodes, getMenus, logout, getActions } from "../api/public"; import { mapping } from "../api/role"; -import { clearAndAssign } from "noob-mengyxu/utils"; +import { clearAndAssign } from "../util/objectUtil"; export class State { dict = { diff --git a/vite.config.ts b/vite.config.ts index 95bd64a..bb9efea 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; import legacy from "@vitejs/plugin-legacy"; import dts from "vite-plugin-dts"; +import VueDevTools from "vite-plugin-vue-devtools"; import { resolve } from "path"; export default defineConfig(({ command, mode }) => { @@ -14,6 +15,8 @@ export default defineConfig(({ command, mode }) => { }, plugins: [ vue(), + // Vue DevTools for debugging (only in non-lib builds) + !isLibBuild && VueDevTools(), // Only use legacy plugin for app builds, not library builds !isLibBuild && legacy({