diff --git a/.trellis/spec/frontend/quality-guidelines.md b/.trellis/spec/frontend/quality-guidelines.md index f8b9728..937c794 100644 --- a/.trellis/spec/frontend/quality-guidelines.md +++ b/.trellis/spec/frontend/quality-guidelines.md @@ -167,6 +167,29 @@ queueMicrotask(() => { --- +### Vue 3 Template: VNode Rendering + +**Problem**: Using `{renderFunction()}` in Vue template renders the function's string representation, not the returned VNode. + +**Why**: In Vue 3 templates, `{xxx}` is treated as literal text interpolation, not JSX expression. + +**Correct Pattern**: Use functional components that delegate to render functions: + +```typescript +// Define functional component that delegates to render function +const MiniTableCell = (params: CellRendererParams) => renderCellContent(params); +const MiniTableHeader = (params: HeaderCellRendererParams) => renderHeaderCellContent(params); + +// Use in template as component (NOT as {MiniTableCell(...)}) + +``` + +**Why this works**: Vue functional components receive VNode params and return VNodes, which Vue renders correctly. Calling `renderFunction()` directly in template gives `{[object Promise]}` or stringified function output. + +--- + ## Code Review Checklist diff --git a/bun.lock b/bun.lock index b7e773a..58ddbf4 100644 --- a/bun.lock +++ b/bun.lock @@ -11,6 +11,7 @@ "dayjs": "^1.11.19", "element-plus": "2.13.0", "lodash-es": "^4.17.23", + "ts-pattern": "^5.9.0", "vue": "^3.5.26", "vue-class-component": "^8.0.0-0", "vue-router": "^4.6.4", @@ -1698,6 +1699,8 @@ "ts-loader": ["ts-loader@9.5.4", "", { "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", "semver": "^7.3.4", "source-map": "^0.7.4" }, "peerDependencies": { "typescript": "*", "webpack": "^5.0.0" } }, "sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ=="], + "ts-pattern": ["ts-pattern@5.9.0", "", {}, "sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg=="], + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "type-fest": ["type-fest@0.6.0", "", {}, "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="], diff --git a/examples/view/base/table-v2.vue b/examples/view/base/table-v2.vue index c25a97f..2c07af5 100644 --- a/examples/view/base/table-v2.vue +++ b/examples/view/base/table-v2.vue @@ -37,7 +37,7 @@ const example = reactive({ // Generate 100 rows of data across multiple pages const generateData = () => { - const rows = []; + const rows: Array> = []; const now = Math.floor(Date.now() / 1000); // Unix timestamp in seconds const lorem = new LoremIpsum({ sentencesPerParagraph: { min: 2, max: 5 }, wordsPerSentence: { min: 5, max: 20 } }); for (let i = 1; i <= 100; i++) { diff --git a/package.json b/package.json index 27e48e9..741cb3b 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "dayjs": "^1.11.19", "element-plus": "2.13.0", "lodash-es": "^4.17.23", + "ts-pattern": "^5.9.0", "vue": "^3.5.26", "vue-class-component": "^8.0.0-0", "vue-router": "^4.6.4", diff --git a/packages/base/data/list-table-v2.vue b/packages/base/data/list-table-v2.vue index a6909fc..f70365f 100644 --- a/packages/base/data/list-table-v2.vue +++ b/packages/base/data/list-table-v2.vue @@ -7,18 +7,30 @@
- { item.name || (item.i18n ? t(item.i18n) : item.key) } +
-
-
- {renderCellContent(item, lodash.get(row, item.dataKey || item.key), row, slots)} +
+
+
@@ -61,13 +73,34 @@
- @@ -514,6 +570,7 @@ const getMiniCellStyle = (item: TableColumn): Record => { left: -9999px; top: 0; width: 100%; + font-size: var(--el-font-size-base); } .mini-table-inner { @@ -524,14 +581,17 @@ const getMiniCellStyle = (item: TableColumn): Record => { .mini-row { display: flex; - align-items: center; + align-items: stretch; padding: v-bind("state.size.tablePad"); border-right: var(--el-table-border); border-bottom: var(--el-table-border); background: v-bind("state.style.tableBg"); color: v-bind("state.style.tableColor"); + font-size: var(--el-font-size-base); box-sizing: border-box; overflow: hidden; + width: 100%; + min-height: min-content; } .mini-header { @@ -539,28 +599,27 @@ const getMiniCellStyle = (item: TableColumn): Record => { font-weight: bold; } -.mini-header-content { - overflow: visible; - text-overflow: clip; - white-space: normal; - word-break: break-word; +.mini-header-cell { + justify-content: center; } -.mini-header-cell { - overflow: visible; - align-self: stretch; +.mini-header-cell-text { + display: block; } .mini-cell { + display: flex; + flex-direction: row; + align-items: center; + padding: 4px; box-sizing: border-box; + white-space: normal; + word-break: normal; overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; } -.mini-cell-content { - overflow: hidden; - text-overflow: ellipsis; +.mini-cell-text { + display: block; } .my-table { @@ -615,12 +674,6 @@ const getMiniCellStyle = (item: TableColumn): Record => { background: v-bind("state.style.tableChildBg") !important; } -.my-table :deep(.cell-text) { - display: block; - overflow: hidden; - text-overflow: ellipsis; -} - .my-pagination { display: flex; justify-content: center; diff --git a/tsconfig.json b/tsconfig.json index 5f2d19a..7b1aa34 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,7 @@ "sourceMap": true, "baseUrl": "./", "types": [ - "webpack-env" + "webpack-env", "element-plus/global" ], "paths": { "@/": [