diff --git a/packages/base/data/list-table-v2/list-table-v2.vue b/packages/base/data/list-table-v2/list-table-v2.vue
index b8148e8..dbb8d3b 100644
--- a/packages/base/data/list-table-v2/list-table-v2.vue
+++ b/packages/base/data/list-table-v2/list-table-v2.vue
@@ -19,13 +19,7 @@
Column widths: {{ columnWidths.map((w) => Math.round(w)).join(",") }}
Column configs:
- {{
- computedConfigs.map((c) => ({
- key: c.key,
- flexBasis: Math.round(c.flexBasis),
- flexGrow: c.flexGrow.toFixed(1),
- }))
- }}
+ {{ computedConfigs }}
@@ -35,7 +29,7 @@
@@ -63,7 +57,9 @@
@click="handleCellClick(row.index, col)"
>
- {{ getRow(row.index) ? formatCellValue(getRow(row.index)!, col) : '--' }}
+ {{
+ getRow(row.index) ? formatCellValue(getRow(row.index)!, col) : "--"
+ }}
@@ -88,18 +84,20 @@
diff --git a/plugs/composables/index.ts b/plugs/composables/index.ts
index 0a0ce04..50fe08b 100644
--- a/plugs/composables/index.ts
+++ b/plugs/composables/index.ts
@@ -5,3 +5,4 @@ export * from "./useModifyForm";
export * from "./useSysDict";
export * from "./useWatchOnce";
export * from "./useRefreshFlags";
+export * from "./useTimestampDisplay";
diff --git a/plugs/composables/useTimestampDisplay.ts b/plugs/composables/useTimestampDisplay.ts
new file mode 100644
index 0000000..4c393fa
--- /dev/null
+++ b/plugs/composables/useTimestampDisplay.ts
@@ -0,0 +1,123 @@
+import dayjs, { type Dayjs } from "dayjs";
+import customParseFormat from "dayjs/plugin/customParseFormat";
+import timezone from "dayjs/plugin/timezone";
+import utc from "dayjs/plugin/utc";
+import { match, P } from "ts-pattern";
+import { computed, toValue, type MaybeRefOrGetter } from "vue";
+
+dayjs.extend(customParseFormat);
+dayjs.extend(utc);
+dayjs.extend(timezone);
+
+export type TimestampDisplayType = "iso8601" | "unix" | "unixMillis";
+
+export interface TimestampDisplayConfig {
+ valueFormat?: string;
+ valueTz?: string;
+ displayFormat?: string;
+ locale?: string;
+ type?: TimestampDisplayType;
+}
+
+export interface TimestampDisplayInput extends TimestampDisplayConfig {
+ value: string | number;
+}
+
+export type TimestampSimpleConfig = undefined | boolean | string | TimestampDisplayConfig;
+
+export const DEFAULT_TIMESTAMP_DISPLAY_FORMAT = "YYYY-MM-DD HH:mm:ss";
+
+function withDefaultDisplayFormat(
+ config: TimestampDisplayConfig,
+ defaultDisplayFormat?: string
+): TimestampDisplayConfig {
+ if (!defaultDisplayFormat || config.displayFormat) {
+ return config;
+ }
+
+ return {
+ ...config,
+ displayFormat: defaultDisplayFormat,
+ };
+}
+
+export function normalizeTimestampValue(
+ timestamp: TimestampSimpleConfig,
+ defaultDisplayFormat?: string
+): TimestampDisplayConfig | null {
+ return match(timestamp)
+ .with(P.nullish, () => null)
+ .with(true, () => withDefaultDisplayFormat({ type: "unixMillis" }, defaultDisplayFormat))
+ .with(P.union("iso8601", "unix", "unixMillis"), (type) => withDefaultDisplayFormat({ type }, defaultDisplayFormat))
+ .with(P.string, (valueFormat) => withDefaultDisplayFormat({ valueFormat }, defaultDisplayFormat))
+ .otherwise((config) => withDefaultDisplayFormat({ ...config }, defaultDisplayFormat));
+}
+
+export function parseTimestampValue(value: string | number, config: TimestampDisplayConfig): Dayjs {
+ const numericValue = match(value)
+ .with(P.string, parseFloat)
+ .otherwise((n) => n);
+
+ if (!isNaN(numericValue)) {
+ if (config.type == "unix") return dayjs.unix(numericValue);
+ if (config.type == "unixMillis") return dayjs(numericValue);
+ }
+
+ if (config.valueFormat && config.valueTz) {
+ return dayjs.tz(value, config.valueFormat, config.valueTz);
+ }
+
+ if (config.valueTz) {
+ return dayjs.tz(value, config.valueTz);
+ }
+
+ if (config.valueFormat) {
+ return dayjs(value, config.valueFormat);
+ }
+
+ return dayjs(value);
+}
+
+export function formatTimestampDisplay(value: string | number, config: TimestampDisplayConfig): string {
+ let parsed = parseTimestampValue(value, config);
+
+ if (!parsed.isValid()) {
+ return String(value);
+ }
+
+ if (config.locale) {
+ parsed = parsed.locale(config.locale);
+ }
+
+ return config.displayFormat ? parsed.format(config.displayFormat) : parsed.format();
+}
+
+export function formatTimestampFromValue(
+ value: string | number,
+ timestamp: TimestampSimpleConfig,
+ defaultDisplayFormat = DEFAULT_TIMESTAMP_DISPLAY_FORMAT
+): string {
+ const config = normalizeTimestampValue(timestamp, defaultDisplayFormat);
+ if (!config) {
+ return String(value);
+ }
+
+ return formatTimestampDisplay(value, config);
+}
+
+export function useTimestampDisplay(input: MaybeRefOrGetter) {
+ const parsed = computed(() => {
+ const resolved = toValue(input);
+ return parseTimestampValue(resolved.value, resolved);
+ });
+
+ const display = computed(() => {
+ const resolved = toValue(input);
+ return formatTimestampDisplay(resolved.value, resolved);
+ });
+
+ return {
+ parsed,
+ display,
+ };
+}