Browse Source

feat: enhance list-table-v2 with timestamp support and matches styling with list-table-v1

- Add Vue JSX support via @vitejs/plugin-vue-jsx
- Configure tsconfig for Vue JSX (jsx: preserve, jsxImportSource: vue)
- Add TzDateTime component integration for timestamp columns
- Support flexible timestamp type: undefined | boolean | string | object
- Add rowHeight and estimatedRowHeight props
- Replace previous column attribute `code` with `key` and `dataKey` (`key` for slot/display, `dataKey` for data access)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dev
hechang27-sprt 3 months ago
parent
commit
7c5326b7d2
  1. 14
      examples/view/base/table-v2.vue
  2. 1
      package.json
  3. 138
      packages/base/data/list-table-v2.vue
  4. 1
      tsconfig.json
  5. 2
      vite.config.ts

14
examples/view/base/table-v2.vue

@ -60,29 +60,29 @@ const data = reactive({
const columns = [ const columns = [
{ {
code: "id", key: "id",
name: "ID", name: "ID",
}, },
{ {
code: "caseName", key: "caseName",
i18n: "table.props.0", i18n: "table.props.0",
}, },
{ {
code: "taskName", key: "taskName",
i18n: "table.props.1", i18n: "table.props.1",
}, },
{ {
code: "userId", key: "userId",
i18n: "table.props.2", i18n: "table.props.2",
}, },
{ {
code: "content", key: "content",
i18n: "table.props.3", i18n: "table.props.3",
}, },
{ {
code: "createTime", key: "createTime",
i18n: "table.props.4", i18n: "table.props.4",
timestamp: true, timestamp: 'unix',
}, },
]; ];

1
package.json

@ -108,6 +108,7 @@
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@vitejs/plugin-legacy": "^7.2.1", "@vitejs/plugin-legacy": "^7.2.1",
"@vitejs/plugin-vue": "^6.0.3", "@vitejs/plugin-vue": "^6.0.3",
"@vitejs/plugin-vue-jsx": "^5.1.5",
"@vue/cli-plugin-babel": "~5.0.9", "@vue/cli-plugin-babel": "~5.0.9",
"@vue/cli-plugin-router": "~5.0.9", "@vue/cli-plugin-router": "~5.0.9",
"@vue/cli-plugin-typescript": "~5.0.9", "@vue/cli-plugin-typescript": "~5.0.9",

138
packages/base/data/list-table-v2.vue

@ -9,8 +9,9 @@
:data="pageData" :data="pageData"
:width="width" :width="width"
:height="resolvedHeight(height)" :height="resolvedHeight(height)"
:row-height="rowHeight" :row-height="prop.rowHeight"
:header-height="headerHeight" :header-height="rowHeight"
:estimated-row-height="prop.estimatedRowHeight"
:border="border" :border="border"
:row-key="rowKey" :row-key="rowKey"
:class="border ? 'has-border' : ''" :class="border ? 'has-border' : ''"
@ -36,12 +37,13 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="tsx" setup>
import { useStore } from "vuex"; import { useStore } from "vuex";
import { ref, computed, watch, h, onMounted, onUpdated, useSlots, renderSlot } from "vue"; import { ref, computed, watch, h, onMounted, onUpdated, useSlots, renderSlot } from "vue";
import { useI18n } from "vue3-i18n"; import { useI18n } from "vue3-i18n";
import { ElAutoResizer, ElTableV2 } from "element-plus"; import { ElAutoResizer, ElTableV2 } from "element-plus";
import * as lodash from "lodash-es"; import * as lodash from "lodash-es";
import TzDateTime from "../item/tzDateTime.vue";
const slots = useSlots(); const slots = useSlots();
@ -49,12 +51,39 @@ const { t } = useI18n();
const { state } = useStore(); const { state } = useStore();
const table = ref(); const table = ref();
// el-table-v2 uses fixed row heights for virtual scrolling by default type TimestampValue =
const ROW_HEIGHT = 50; | undefined
const HEADER_HEIGHT = 50; | boolean
| string
| {
valueFormat?: string;
valueTz?: string;
displayFormat?: string;
locale?: string;
type?: "iso8601" | "unix" | "unixMillis";
};
interface TzDateTimeProps {
value: string | number;
valueFormat?: string;
valueTz?: string;
displayFormat?: string;
locale?: string;
type?: "iso8601" | "unix" | "unixMillis";
slot?: boolean;
}
interface TzDateTimeConfig {
valueFormat?: string;
valueTz?: string;
displayFormat?: string;
locale?: string;
type?: "iso8601" | "unix" | "unixMillis";
}
interface TableColumn { interface TableColumn {
code: string; key: string;
dataKey?: string;
name?: string; name?: string;
i18n?: string; i18n?: string;
type?: string; type?: string;
@ -64,7 +93,7 @@ interface TableColumn {
align?: "left" | "center" | "right"; align?: "left" | "center" | "right";
slot?: boolean; slot?: boolean;
dict?: string; dict?: string;
timestamp?: boolean; timestamp?: TimestampValue;
filesize?: boolean; filesize?: boolean;
[others: string]: any; [others: string]: any;
@ -82,6 +111,9 @@ interface Props {
treeProps?: any; treeProps?: any;
lazy?: boolean; lazy?: boolean;
border?: boolean; border?: boolean;
timestampFormat?: string;
rowHeight?: number;
estimatedRowHeight?: number;
} }
const prop = withDefaults(defineProps<Props>(), { const prop = withDefaults(defineProps<Props>(), {
@ -96,13 +128,13 @@ const prop = withDefaults(defineProps<Props>(), {
treeProps: undefined, treeProps: undefined,
lazy: false, lazy: false,
border: false, border: false,
timestampFormat: "YYYY-MM-DD HH:mm:ss",
rowHeight: 30,
estimatedRowHeight: undefined,
}); });
const emit = defineEmits(["query", "selection-change", "row-click", "cell-click"]); const emit = defineEmits(["query", "selection-change", "row-click", "cell-click"]);
const rowHeight = ROW_HEIGHT;
const headerHeight = HEADER_HEIGHT;
const containerStyle = computed(() => { const containerStyle = computed(() => {
const style: Record<string, string> = {}; const style: Record<string, string> = {};
if (prop.height !== undefined) { if (prop.height !== undefined) {
@ -169,6 +201,26 @@ const formatFileSize = (value: any) => {
return g.toFixed(2) + "G"; return g.toFixed(2) + "G";
}; };
// Helper to resolve timestamp column config to TzDateTime props
const resolveTimestampProps = (ts: TimestampValue): TzDateTimeConfig | null => {
if (!ts) return null;
if (ts === true) return { type: "unixMillis", displayFormat: prop.timestampFormat };
if (typeof ts === "string") {
// If it's 'unix', 'iso8601', or 'unixMillis' treat as type
if (["unix", "iso8601", "unixMillis"].includes(ts)) {
return { type: ts as TzDateTimeConfig["type"], displayFormat: prop.timestampFormat };
}
// Otherwise treat as valueFormat
return { valueFormat: ts, displayFormat: prop.timestampFormat };
}
// For object config, apply default displayFormat if not specified
const config = ts as TzDateTimeConfig;
if (!config.displayFormat) {
config.displayFormat = prop.timestampFormat;
}
return config;
};
const formatterByDist = (dictKey: string, value: any) => { const formatterByDist = (dictKey: string, value: any) => {
if (!dictKey) { if (!dictKey) {
return getValue(value); return getValue(value);
@ -184,7 +236,7 @@ const formatCellValue = (value: any, item: TableColumn, row: any) => {
if (item.dict) return formatterByDist(item.dict, value); if (item.dict) return formatterByDist(item.dict, value);
if (item.timestamp) return formatStamp(value); if (item.timestamp) return formatStamp(value);
if (item.filesize) return formatFileSize(value); if (item.filesize) return formatFileSize(value);
if (row.scheme) return formatterByDist(row.scheme + "_" + item.code, value); if (row.scheme) return formatterByDist(row.scheme + "_" + (item.dataKey || item.key), value);
return getValue(value); return getValue(value);
}; };
@ -198,7 +250,14 @@ const handleCurrentChange = (val: number) => {
emit("query"); emit("query");
}; };
const onScroll = ({ scrollTop }: { scrollTop: number; scrollLeft?: number; horizontal?: boolean; vertical?: boolean }) => { const onScroll = ({
scrollTop,
}: {
scrollTop: number;
scrollLeft?: number;
horizontal?: boolean;
vertical?: boolean;
}) => {
// Can be used for lazy loading or other scroll-based features // Can be used for lazy loading or other scroll-based features
}; };
@ -214,9 +273,9 @@ const onCellClick = ({ row, column }: { row: any; column: any }) => {
const tableColumns = computed(() => { const tableColumns = computed(() => {
return prop.columns.map((item: TableColumn) => { return prop.columns.map((item: TableColumn) => {
const col: any = { const col: any = {
key: item.code, key: item.key,
title: item.name || (item.i18n ? t(item.i18n) : item.code), title: item.name || (item.i18n ? t(item.i18n) : item.key),
dataKey: item.code, dataKey: item.dataKey || item.key,
align: item.align || "center", align: item.align || "center",
fixed: item.fixed, fixed: item.fixed,
}; };
@ -234,25 +293,43 @@ const tableColumns = computed(() => {
col.minWidth = typeof item.minWidth === "number" ? item.minWidth : parseInt(String(item.minWidth)) || 120; col.minWidth = typeof item.minWidth === "number" ? item.minWidth : parseInt(String(item.minWidth)) || 120;
} }
// Cell renderer - el-table-v2 uses cellRenderer function, converts parent slots // Cell renderer - el-table-v2 uses cellRenderer function
col.cellRenderer = ({ cellData, rowData }: { cellData: any; rowData: any }) => { col.cellRenderer = ({ cellData, rowData }: { cellData: any; rowData: any }) => {
const slotName = item.code; const slotName = item.key;
// If column has slot=true, render the parent's slot content // If column has slot=true, render the parent's slot content
if (item.slot && slots[slotName]) { if (item.slot && slots[slotName]) {
return renderSlot(slots, slotName, { row: rowData }); return renderSlot(slots, slotName, { row: rowData });
} }
const value = lodash.get(rowData, item.code); const value = lodash.get(rowData, item.dataKey || item.key);
// Handle timestamp display using TzDateTime component
if (item.timestamp) {
const tzProps = resolveTimestampProps(item.timestamp);
if (tzProps) {
const { valueFormat, valueTz, displayFormat, locale, type } = tzProps;
return (
<TzDateTime
value={value}
valueFormat={valueFormat}
valueTz={valueTz}
displayFormat={displayFormat}
locale={locale}
type={type}
/>
);
}
}
// Handle dict display // Handle dict display
if (item.dict) { if (item.dict) {
return h("span", {}, formatterByDist(item.dict, value)); return <span>{formatterByDist(item.dict, value)}</span>;
} }
// Handle formatting // Handle formatting
const formatted = formatCellValue(value, item, rowData); const formatted = formatCellValue(value, item, rowData);
return h("span", {}, formatted); return <span>{formatted}</span>;
}; };
return col; return col;
@ -288,6 +365,9 @@ const tableColumns = computed(() => {
--el-table-current-row-bg-color: v-bind("state.style.tableCurBg"); --el-table-current-row-bg-color: v-bind("state.style.tableCurBg");
--el-table-header-bg-color: v-bind("state.style.tableHeadBg"); --el-table-header-bg-color: v-bind("state.style.tableHeadBg");
--el-bg-color: v-bind("state.style.tableBg") !important; --el-bg-color: v-bind("state.style.tableBg") !important;
// Border - el-table-v2 uses --el-table-border
--el-table-border: 1px solid var(--el-table-border-color);
} }
.my-table :deep(.el-table-v2__row) { .my-table :deep(.el-table-v2__row) {
@ -298,8 +378,22 @@ const tableColumns = computed(() => {
background: v-bind("state.style.tableCurBg") !important; background: v-bind("state.style.tableCurBg") !important;
} }
.my-table :deep(.el-table-v2__cell) { .my-table :deep(.el-table-v2__header-cell) {
padding: v-bind("state.size.tablePad"); padding: v-bind("state.size.tablePad");
border-right: var(--el-table-border);
color: v-bind("state.style.tableColor");
}
.my-table :deep(.el-table-v2__row-cell) {
padding: v-bind("state.size.tablePad");
border-right: var(--el-table-border);
color: v-bind("state.style.tableColor");
}
// Child row background for tree data
.my-table :deep(.el-table-v2__row[data-level="1"]),
.my-table :deep(.el-table-v2__row[data-level="3"]) {
background: v-bind("state.style.tableChildBg") !important;
} }
.my-pagination { .my-pagination {

1
tsconfig.json

@ -4,6 +4,7 @@
"module": "esnext", "module": "esnext",
"strict": true, "strict": true,
"jsx": "preserve", "jsx": "preserve",
"jsxImportSource": "vue",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"experimentalDecorators": true, "experimentalDecorators": true,
"skipLibCheck": true, "skipLibCheck": true,

2
vite.config.ts

@ -1,5 +1,6 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue"; import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import legacy from "@vitejs/plugin-legacy"; import legacy from "@vitejs/plugin-legacy";
import dts from "vite-plugin-dts"; import dts from "vite-plugin-dts";
import VueDevTools from "vite-plugin-vue-devtools"; import VueDevTools from "vite-plugin-vue-devtools";
@ -15,6 +16,7 @@ export default defineConfig(({ command, mode }) => {
}, },
plugins: [ plugins: [
vue(), vue(),
vueJsx(),
// Vue DevTools for debugging (only in non-lib builds) // Vue DevTools for debugging (only in non-lib builds)
!isLibBuild && VueDevTools(), !isLibBuild && VueDevTools(),
// Only use legacy plugin for app builds, not library builds // Only use legacy plugin for app builds, not library builds

Loading…
Cancel
Save