v-nuxt-ui 0.2.33 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/AsyncSelect.vue +13 -2
  3. package/dist/runtime/components/ScrollArea.vue +2 -2
  4. package/dist/runtime/components/Select.vue +13 -2
  5. package/dist/runtime/components/Watermark.d.vue.ts +2 -2
  6. package/dist/runtime/components/Watermark.vue.d.ts +2 -2
  7. package/dist/runtime/components/button/Confirm.d.vue.ts +22 -0
  8. package/dist/runtime/components/button/Confirm.vue +47 -0
  9. package/dist/runtime/components/button/Confirm.vue.d.ts +22 -0
  10. package/dist/runtime/components/button/Theme.vue +7 -13
  11. package/dist/runtime/components/date-picker/Input.d.vue.ts +1 -0
  12. package/dist/runtime/components/date-picker/Input.vue +12 -3
  13. package/dist/runtime/components/date-picker/Input.vue.d.ts +1 -0
  14. package/dist/runtime/components/form/save-modal-template/WithApi.vue +1 -2
  15. package/dist/runtime/components/form/save-modal-template/index.vue +23 -23
  16. package/dist/runtime/components/layout/button/ThemePicker.vue +2 -2
  17. package/dist/runtime/components/layout/default.vue +10 -4
  18. package/dist/runtime/components/simple-table/index.d.vue.ts +1 -1
  19. package/dist/runtime/components/simple-table/index.vue +72 -1
  20. package/dist/runtime/components/simple-table/index.vue.d.ts +1 -1
  21. package/dist/runtime/components/sys/department/SaveModal.vue +0 -1
  22. package/dist/runtime/components/sys/flow/EditNodeModal.vue +5 -4
  23. package/dist/runtime/components/sys/flow/SaveModal.vue +0 -1
  24. package/dist/runtime/components/sys/menu/SaveModal.vue +0 -1
  25. package/dist/runtime/components/sys/role/SaveModal.vue +0 -1
  26. package/dist/runtime/components/sys/table/SaveModal.vue +0 -1
  27. package/dist/runtime/components/sys/table/TableColumnModal.vue +5 -4
  28. package/dist/runtime/components/sys/user/SaveModal.vue +0 -1
  29. package/dist/runtime/components/sys/user/Table.vue +19 -29
  30. package/dist/runtime/components/table/Page.vue +23 -2
  31. package/dist/runtime/components/table/Pagination.vue +1 -1
  32. package/dist/runtime/components/table/header/index.vue +231 -84
  33. package/dist/runtime/components/table/header/settings/columns/DndList.d.vue.ts +1 -0
  34. package/dist/runtime/components/table/header/settings/columns/DndList.vue +3 -1
  35. package/dist/runtime/components/table/header/settings/columns/DndList.vue.d.ts +1 -0
  36. package/dist/runtime/components/table/header/settings/columns/Item.d.vue.ts +1 -0
  37. package/dist/runtime/components/table/header/settings/columns/Item.vue +7 -3
  38. package/dist/runtime/components/table/header/settings/columns/Item.vue.d.ts +1 -0
  39. package/dist/runtime/components/table/header/settings/columns/index.d.vue.ts +3 -3
  40. package/dist/runtime/components/table/header/settings/columns/index.vue +7 -5
  41. package/dist/runtime/components/table/header/settings/columns/index.vue.d.ts +3 -3
  42. package/dist/runtime/components/table/header/settings/index.d.vue.ts +3 -2
  43. package/dist/runtime/components/table/header/settings/index.vue +5 -4
  44. package/dist/runtime/components/table/header/settings/index.vue.d.ts +3 -2
  45. package/dist/runtime/components/table/query/order/Newer.d.vue.ts +1 -0
  46. package/dist/runtime/components/table/query/order/Newer.vue +9 -2
  47. package/dist/runtime/components/table/query/order/Newer.vue.d.ts +1 -0
  48. package/dist/runtime/components/table/query/order/index.vue +46 -54
  49. package/dist/runtime/components/table/query/order/{Item.d.vue.ts → item.d.vue.ts} +2 -0
  50. package/dist/runtime/components/table/query/order/{Item.vue → item.vue} +25 -17
  51. package/dist/runtime/components/table/query/order/{Item.vue.d.ts → item.vue.d.ts} +2 -0
  52. package/dist/runtime/components/table/query/where/Newer.vue +2 -2
  53. package/dist/runtime/components/table/query/where/index.vue +90 -77
  54. package/dist/runtime/components/table/query/where/simple/item/ColumnPicker.vue +8 -7
  55. package/dist/runtime/components/table/query/where/simple/item/OprPicker.d.vue.ts +1 -0
  56. package/dist/runtime/components/table/query/where/simple/item/OprPicker.vue +6 -1
  57. package/dist/runtime/components/table/query/where/simple/item/OprPicker.vue.d.ts +1 -0
  58. package/dist/runtime/components/table/query/where/simple/item/index.d.vue.ts +3 -1
  59. package/dist/runtime/components/table/query/where/simple/item/index.vue +84 -39
  60. package/dist/runtime/components/table/query/where/simple/item/index.vue.d.ts +3 -1
  61. package/dist/runtime/components/table/query/where/simple/item/opr/AsyncSelect.vue +0 -1
  62. package/dist/runtime/components/table/query/where/simple/item/opr/DatePicker.d.vue.ts +1 -0
  63. package/dist/runtime/components/table/query/where/simple/item/opr/DatePicker.vue +42 -40
  64. package/dist/runtime/components/table/query/where/simple/item/opr/DatePicker.vue.d.ts +1 -0
  65. package/dist/runtime/components/table/query/where/simple/item/opr/Input.vue +3 -0
  66. package/dist/runtime/components/table/query/where/simple/item/opr/InputNumber.vue +0 -1
  67. package/dist/runtime/components/table/query/where/simple/item/opr/Select.vue +0 -1
  68. package/dist/runtime/components/table/query/where/simple/item/opr/index.vue +3 -0
  69. package/dist/runtime/composables/api/useApi.js +1 -0
  70. package/dist/runtime/composables/form/useForm.d.ts +2 -2
  71. package/dist/runtime/composables/form/useForm.js +10 -9
  72. package/dist/runtime/composables/table/useTable.js +9 -1
  73. package/dist/runtime/composables/table/useTableColumns.d.ts +1 -1
  74. package/dist/runtime/composables/table/useTableColumns.js +2 -2
  75. package/dist/runtime/composables/table/useTableQuery.d.ts +1 -0
  76. package/dist/runtime/composables/table/useTableQuery.js +8 -3
  77. package/dist/runtime/composables/table/useTableView.js +209 -11
  78. package/dist/runtime/composables/useTheme.js +10 -3
  79. package/dist/runtime/constants/columns.js +6 -6
  80. package/dist/runtime/types/components/form/index.d.ts +1 -1
  81. package/dist/runtime/types/components/table/column.d.ts +3 -1
  82. package/dist/runtime/types/components/table/header.d.ts +3 -1
  83. package/dist/runtime/types/components/table/query/order.d.ts +2 -0
  84. package/dist/runtime/types/components/table/query/where.d.ts +0 -1
  85. package/dist/runtime/types/query.d.ts +1 -0
  86. package/dist/runtime/types/storage.d.ts +1 -0
  87. package/package.json +1 -1
  88. package/dist/runtime/components/form/save-model-template/ConfirmUpdateModal.d.vue.ts +0 -19
  89. package/dist/runtime/components/form/save-model-template/ConfirmUpdateModal.vue +0 -111
  90. package/dist/runtime/components/form/save-model-template/ConfirmUpdateModal.vue.d.ts +0 -19
  91. package/dist/runtime/components/form/save-model-template/WithApi.d.vue.ts +0 -19
  92. package/dist/runtime/components/form/save-model-template/WithApi.vue +0 -37
  93. package/dist/runtime/components/form/save-model-template/WithApi.vue.d.ts +0 -19
  94. package/dist/runtime/components/form/save-model-template/index.d.vue.ts +0 -21
  95. package/dist/runtime/components/form/save-model-template/index.vue +0 -123
  96. package/dist/runtime/components/form/save-model-template/index.vue.d.ts +0 -21
@@ -49,6 +49,9 @@ defineExpose({
49
49
  variant="link"
50
50
  size="sm"
51
51
  icon="i-lucide-circle-x"
52
+ :ui="{
53
+ leadingIcon: 'size-3 text-dimmed'
54
+ }"
52
55
  aria-label="Clear input"
53
56
  @click="() => {
54
57
  queryValue = '';
@@ -37,7 +37,6 @@ defineExpose({
37
37
  color="neutral"
38
38
  :increment="false"
39
39
  :decrement="false"
40
- class="max-w-16"
41
40
  @keyup.enter="async () => {
42
41
  await triggerFetching?.();
43
42
  }"
@@ -37,6 +37,5 @@ defineExpose({
37
37
  v-bind="props"
38
38
  v-model="modelValue"
39
39
  :placeholder="`\u8BF7\u9009\u62E9${label ?? ''}`"
40
- rounded-none
41
40
  />
42
41
  </template>
@@ -49,12 +49,14 @@ defineExpose({
49
49
  v-model:where-query-item="whereQueryItem"
50
50
  :disabled="fetching"
51
51
  v-bind="option"
52
+ rounded-none
52
53
  />
53
54
  <TableQueryWhereSimpleItemOprDatePicker
54
55
  v-else-if="option.type === 'date-picker'"
55
56
  ref="item"
56
57
  v-model:where-query-item="whereQueryItem"
57
58
  :disabled="fetching"
59
+ rounded-none
58
60
  />
59
61
  <TableQueryWhereSimpleItemOprAsyncSelect
60
62
  v-else-if="option.type === 'async-select'"
@@ -62,5 +64,6 @@ defineExpose({
62
64
  v-model:where-query-item="whereQueryItem"
63
65
  :disabled="fetching"
64
66
  v-bind="option"
67
+ rounded-none
65
68
  />
66
69
  </template>
@@ -82,6 +82,7 @@ export function pruneQueryTemplate(payload) {
82
82
  const newPayload = cloneJson(payload);
83
83
  newPayload.whereQuery?.items?.forEach((item) => {
84
84
  delete item.extraData;
85
+ delete item.whereQuerySection;
85
86
  });
86
87
  return newPayload;
87
88
  }
@@ -5,8 +5,8 @@ export declare const useFormValues: <T>(raw: Ref<T>, defaultValues?: Partial<T>)
5
5
  oldValues: Ref<T>;
6
6
  newValues: Ref<T>;
7
7
  };
8
- export declare const useFormSubmission: <T extends BaseModel>(oldValues: Ref<T>, newValues: Ref<T>, close: (ok: boolean) => void, save: (model: T) => void, apiGroup: () => ApiGroup<T>, arrayTypeFieldKeys?: (keyof T)[], rowKey?: keyof T, versionKey?: keyof T, getExtraFields?: () => Record<string, any>) => {
9
- onSubmit: () => Promise<void>;
8
+ export declare const useFormSubmission: <T extends BaseModel>(oldValues: Ref<T>, newValues: Ref<T>, save: (model: T) => void | Promise<void>, apiGroup: () => ApiGroup<T>, arrayTypeFieldKeys?: (keyof T)[], rowKey?: keyof T, versionKey?: keyof T, getExtraFields?: () => Record<string, any>) => {
9
+ onSubmit: () => Promise<boolean>;
10
10
  };
11
11
  export declare const useConfirmDiff: (fields: MaybeRefOrGetter<VFormFieldProps[]>, diffItems: MaybeRefOrGetter<ConfirmDiffItem[]>, oldModelValue: MaybeRefOrGetter<Record<string, unknown>>, newModelValue: MaybeRefOrGetter<Record<string, unknown>>) => {
12
12
  diffedItems: import("vue").ComputedRef<{
@@ -19,23 +19,23 @@ export const useFormValues = (raw, defaultValues) => {
19
19
  );
20
20
  return { oldValues, newValues };
21
21
  };
22
- export const useFormSubmission = (oldValues, newValues, close, save, apiGroup, arrayTypeFieldKeys = [], rowKey = "id", versionKey = "version", getExtraFields) => {
22
+ export const useFormSubmission = (oldValues, newValues, save, apiGroup, arrayTypeFieldKeys = [], rowKey = "id", versionKey = "version", getExtraFields) => {
23
23
  const apiFns = apiGroup();
24
24
  async function onSubmit() {
25
25
  if (oldValues.value[rowKey] === 0) {
26
- await onCreate();
27
- } else {
28
- await onUpdate();
26
+ return await onCreate();
29
27
  }
28
+ return await onUpdate();
30
29
  }
31
30
  async function onCreate() {
32
31
  const extraFields = getExtraFields ? getExtraFields() : void 0;
33
32
  const payload = extraFields ? { ...apiFns.prune(newValues.value), ...extraFields } : apiFns.prune(newValues.value);
34
33
  const { data } = await apiFns.create(payload);
35
34
  if (!data.value.error) {
36
- save(data.value.data);
37
- close(true);
35
+ await save(data.value.data);
36
+ return true;
38
37
  }
38
+ return false;
39
39
  }
40
40
  async function onUpdate() {
41
41
  const [objWithModifiedFields, modified] = getObjWithModifiedFields(
@@ -51,15 +51,16 @@ export const useFormSubmission = (oldValues, newValues, close, save, apiGroup, a
51
51
  color: "warning",
52
52
  icon: "i-lucide-triangle-alert"
53
53
  });
54
- return;
54
+ return false;
55
55
  }
56
56
  const extraFields = getExtraFields ? getExtraFields() : void 0;
57
57
  const payload = extraFields ? { ...apiFns.prune(objWithModifiedFields), ...extraFields } : apiFns.prune(objWithModifiedFields);
58
58
  const { data } = await apiFns.update(payload);
59
59
  if (!data.value.error) {
60
- save(data.value.data);
61
- close(true);
60
+ await save(data.value.data);
61
+ return true;
62
62
  }
63
+ return false;
63
64
  }
64
65
  return { onSubmit };
65
66
  };
@@ -10,6 +10,7 @@ export function useTable(props) {
10
10
  const {
11
11
  // table-level props
12
12
  name,
13
+ cnName,
13
14
  disableCreation,
14
15
  disableRefresh,
15
16
  disableBatchDeletion,
@@ -67,6 +68,7 @@ export function useTable(props) {
67
68
  whereQuery,
68
69
  whereQueryOpen,
69
70
  orderQuery,
71
+ orderQueryOpen,
70
72
  isWhereQueryValueEmpty,
71
73
  pruneWhereQuery
72
74
  } = queryComposable;
@@ -297,16 +299,22 @@ export function useTable(props) {
297
299
  defaultOrderQuery: orderQueryInitValues.value,
298
300
  orderQuery: orderQuery.value,
299
301
  onUpdateOrderQuery: (query) => orderQuery.value = query,
302
+ orderQueryOpen: orderQueryOpen.value,
303
+ onUpdateOrderQueryOpen: (open) => orderQueryOpen.value = open,
300
304
  fetching: fetching.value,
301
305
  triggerFetching: debouncedFetchList,
302
306
  bizColumns
303
307
  }));
304
308
  const tblHeaderProps = computed(() => ({
305
309
  name,
310
+ tblName: cnName,
306
311
  fetching: fetching.value,
307
312
  rawBizColumns: bizColumns,
308
- onUpdateBizColumns: (columns2) => {
313
+ onUpdateBizColumns: (columns2, storageColumns) => {
309
314
  clonedBizColumns.value = columns2;
315
+ if (storageColumns) {
316
+ localStgSettings.value = { ...localStgSettings.value, columns: storageColumns };
317
+ }
310
318
  },
311
319
  initStorageColumns: initStorageColumns.value,
312
320
  onNew,
@@ -5,7 +5,7 @@ interface UseTableColumnsReturn<T> {
5
5
  selectionColumn: VColumn<T>;
6
6
  expandColumn: VColumn<T>;
7
7
  clonedBizColumns: ShallowRef<VColumn<T>[]>;
8
- columnsWithCommonProps: ShallowRef<VColumn<T>[]>;
8
+ columnsWithCommonProps: ComputedRef<VColumn<T>[]>;
9
9
  sortedColumns: ComputedRef<VColumn<T>[]>;
10
10
  columnsWithFilterOptions: ComputedRef<VColumn<T>[]>;
11
11
  columnsWithSortableHeader: ComputedRef<VColumn<T>[]>;
@@ -63,8 +63,8 @@ export function useTableColumns(props) {
63
63
  const mergeColumnWithDefaults = (col, defaults) => {
64
64
  return defu(col, defaults);
65
65
  };
66
- const columnsWithCommonProps = shallowRef(
67
- clonedBizColumns.value.map((col) => mergeColumnWithDefaults(col, cloneJson(commonColumnProps)))
66
+ const columnsWithCommonProps = computed(
67
+ () => clonedBizColumns.value.map((col) => mergeColumnWithDefaults(col, cloneJson(commonColumnProps)))
68
68
  );
69
69
  const sortedColumns = computed(() => {
70
70
  const sortedKeys = (localStgSettings.value.columns ?? initStorageColumns.value).filter((c) => c.checked).map((c) => c.accessorKey);
@@ -15,6 +15,7 @@ export declare function useTableQuery<T>(props: {
15
15
  whereQuery: import("vue").WritableComputedRef<WhereQuery<T> | undefined, WhereQuery<T> | undefined>;
16
16
  whereQueryOpen: import("vue").WritableComputedRef<boolean, boolean>;
17
17
  orderQuery: import("vue").WritableComputedRef<OrderQuery<T>, OrderQuery<T>>;
18
+ orderQueryOpen: import("vue").WritableComputedRef<boolean, boolean>;
18
19
  isWhereQueryValueEmpty: import("vue").ComputedRef<boolean>;
19
20
  pruneWhereQuery: (query: WhereQuery<T> | undefined) => {
20
21
  items: WhereQueryItem<T>[];
@@ -10,7 +10,6 @@ export function useTableQuery(props) {
10
10
  const options = bizColumns.filter((col) => col.filterOption).map((col) => ({
11
11
  field: col.accessorKey,
12
12
  label: col.header,
13
- preferred: col.preferred,
14
13
  ...col.filterOption
15
14
  }));
16
15
  extraWhereQueryOptions?.forEach((option) => {
@@ -20,7 +19,7 @@ export function useTableQuery(props) {
20
19
  });
21
20
  const whereQueryInitValues = computed(() => {
22
21
  const initValues = {
23
- items: whereQueryOptions.value.map((option) => ({
22
+ items: whereQueryOptions.value.filter((option) => option.initHide !== true).map((option) => ({
24
23
  field: option.field,
25
24
  opr: option.defaultOpr ?? useTableOpr().getDefaultOprByType(option.type),
26
25
  value: option.initValues ?? null,
@@ -48,7 +47,7 @@ export function useTableQuery(props) {
48
47
  accessorKey: col["accessorKey"],
49
48
  fixed: "unfixed",
50
49
  checked: !col.initHide,
51
- preferred: col.preferred
50
+ preferred: col.filterOption?.preferred
52
51
  }))
53
52
  );
54
53
  const localStgSettings = useLocalStorage(`${name}-table-settings`, {
@@ -69,6 +68,10 @@ export function useTableQuery(props) {
69
68
  get: () => localStgSettings.value.orderQuery ?? [],
70
69
  set: (query) => localStgSettings.value = { ...localStgSettings.value, orderQuery: query }
71
70
  });
71
+ const orderQueryOpen = computed({
72
+ get: () => localStgSettings.value.orderQueryOpen ?? false,
73
+ set: (open) => localStgSettings.value = { ...localStgSettings.value, orderQueryOpen: open }
74
+ });
72
75
  const checkIfWhereQueryItemsValueEmpty = (items) => {
73
76
  const itemsWithOprNoValues = items.filter((item) => noValueOprList.includes(item.opr));
74
77
  if (itemsWithOprNoValues.length > 0) {
@@ -107,6 +110,7 @@ export function useTableQuery(props) {
107
110
  }).map((item) => {
108
111
  const newItem = { ...item };
109
112
  delete newItem.extraData;
113
+ delete newItem.whereQuerySection;
110
114
  return newItem;
111
115
  });
112
116
  const prunedGroups = clonedQuery.groups?.map(
@@ -128,6 +132,7 @@ export function useTableQuery(props) {
128
132
  whereQuery,
129
133
  whereQueryOpen,
130
134
  orderQuery,
135
+ orderQueryOpen,
131
136
  isWhereQueryValueEmpty,
132
137
  pruneWhereQuery
133
138
  };
@@ -2,17 +2,19 @@ import { ref, computed, watch, onMounted, onUnmounted, nextTick, useTemplateRef
2
2
  import { useTable } from "./useTable.js";
3
3
  const PINNED_SHADOW_CLASSES = {
4
4
  left: {
5
- base: "[--pinned-shadow-color:rgba(0,0,0,0.15)] dark:[--pinned-shadow-color:rgba(0,0,0,0.45)] [&_th[data-pinned=left]]:after:absolute [&_th[data-pinned=left]]:after:top-0 [&_th[data-pinned=left]]:after:right-0 [&_th[data-pinned=left]]:after:bottom-0 [&_th[data-pinned=left]]:after:w-[30px] [&_th[data-pinned=left]]:after:translate-x-full [&_th[data-pinned=left]]:after:transition-opacity [&_th[data-pinned=left]]:after:duration-300 [&_th[data-pinned=left]]:after:pointer-events-none [&_th[data-pinned=left]]:after:shadow-[inset_10px_0_8px_-8px_var(--pinned-shadow-color)] [&_td[data-pinned=left]]:after:absolute [&_td[data-pinned=left]]:after:top-0 [&_td[data-pinned=left]]:after:right-0 [&_td[data-pinned=left]]:after:bottom-0 [&_td[data-pinned=left]]:after:w-[30px] [&_td[data-pinned=left]]:after:translate-x-full [&_td[data-pinned=left]]:after:transition-opacity [&_td[data-pinned=left]]:after:duration-200 [&_td[data-pinned=left]]:after:pointer-events-none [&_td[data-pinned=left]]:after:shadow-[inset_10px_0_8px_-8px_var(--pinned-shadow-color)]",
6
- show: "[&_th[data-pinned=left]]:after:opacity-100 [&_td[data-pinned=left]]:after:opacity-100",
7
- hide: "[&_th[data-pinned=left]]:after:opacity-0 [&_td[data-pinned=left]]:after:opacity-0"
5
+ base: "[--pinned-shadow-color:rgba(0,0,0,0.15)] dark:[--pinned-shadow-color:rgba(0,0,0,0.45)] [&_th[data-pinned=left]:not(:has(+th[data-pinned=left]))]:after:absolute [&_th[data-pinned=left]:not(:has(+th[data-pinned=left]))]:after:top-0 [&_th[data-pinned=left]:not(:has(+th[data-pinned=left]))]:after:right-0 [&_th[data-pinned=left]:not(:has(+th[data-pinned=left]))]:after:bottom-0 [&_th[data-pinned=left]:not(:has(+th[data-pinned=left]))]:after:w-[30px] [&_th[data-pinned=left]:not(:has(+th[data-pinned=left]))]:after:translate-x-full [&_th[data-pinned=left]:not(:has(+th[data-pinned=left]))]:after:transition-opacity [&_th[data-pinned=left]:not(:has(+th[data-pinned=left]))]:after:duration-300 [&_th[data-pinned=left]:not(:has(+th[data-pinned=left]))]:after:pointer-events-none [&_th[data-pinned=left]:not(:has(+th[data-pinned=left]))]:after:shadow-[inset_10px_0_8px_-8px_var(--pinned-shadow-color)] [&_td[data-pinned=left]:not(:has(+td[data-pinned=left]))]:after:absolute [&_td[data-pinned=left]:not(:has(+td[data-pinned=left]))]:after:top-0 [&_td[data-pinned=left]:not(:has(+td[data-pinned=left]))]:after:right-0 [&_td[data-pinned=left]:not(:has(+td[data-pinned=left]))]:after:bottom-0 [&_td[data-pinned=left]:not(:has(+td[data-pinned=left]))]:after:w-[30px] [&_td[data-pinned=left]:not(:has(+td[data-pinned=left]))]:after:translate-x-full [&_td[data-pinned=left]:not(:has(+td[data-pinned=left]))]:after:transition-opacity [&_td[data-pinned=left]:not(:has(+td[data-pinned=left]))]:after:duration-200 [&_td[data-pinned=left]:not(:has(+td[data-pinned=left]))]:after:pointer-events-none [&_td[data-pinned=left]:not(:has(+td[data-pinned=left]))]:after:shadow-[inset_10px_0_8px_-8px_var(--pinned-shadow-color)]",
6
+ show: "[&_th[data-pinned=left]:not(:has(+th[data-pinned=left]))]:after:opacity-100 [&_td[data-pinned=left]:not(:has(+td[data-pinned=left]))]:after:opacity-100",
7
+ hide: "[&_th[data-pinned=left]:not(:has(+th[data-pinned=left]))]:after:opacity-0 [&_td[data-pinned=left]:not(:has(+td[data-pinned=left]))]:after:opacity-0"
8
8
  },
9
9
  right: {
10
- base: "[&_th[data-pinned=right]]:before:absolute [&_th[data-pinned=right]]:before:top-0 [&_th[data-pinned=right]]:before:left-0 [&_th[data-pinned=right]]:before:bottom-0 [&_th[data-pinned=right]]:before:w-[30px] [&_th[data-pinned=right]]:before:-translate-x-full [&_th[data-pinned=right]]:before:transition-opacity [&_th[data-pinned=right]]:before:duration-300 [&_th[data-pinned=right]]:before:pointer-events-none [&_th[data-pinned=right]]:before:shadow-[inset_-10px_0_8px_-8px_var(--pinned-shadow-color)] [&_td[data-pinned=right]]:before:absolute [&_td[data-pinned=right]]:before:top-0 [&_td[data-pinned=right]]:before:left-0 [&_td[data-pinned=right]]:before:bottom-0 [&_td[data-pinned=right]]:before:w-[30px] [&_td[data-pinned=right]]:before:-translate-x-full [&_td[data-pinned=right]]:before:transition-opacity [&_td[data-pinned=right]]:before:duration-200 [&_td[data-pinned=right]]:before:pointer-events-none [&_td[data-pinned=right]]:before:shadow-[inset_-10px_0_8px_-8px_var(--pinned-shadow-color)]",
11
- show: "[&_th[data-pinned=right]]:before:opacity-100 [&_td[data-pinned=right]]:before:opacity-100",
12
- hide: "[&_th[data-pinned=right]]:before:opacity-0 [&_td[data-pinned=right]]:before:opacity-0"
10
+ base: "[&_th:not([data-pinned=right])+th[data-pinned=right]]:before:absolute [&_th:not([data-pinned=right])+th[data-pinned=right]]:before:top-0 [&_th:not([data-pinned=right])+th[data-pinned=right]]:before:left-0 [&_th:not([data-pinned=right])+th[data-pinned=right]]:before:bottom-0 [&_th:not([data-pinned=right])+th[data-pinned=right]]:before:w-[30px] [&_th:not([data-pinned=right])+th[data-pinned=right]]:before:-translate-x-full [&_th:not([data-pinned=right])+th[data-pinned=right]]:before:transition-opacity [&_th:not([data-pinned=right])+th[data-pinned=right]]:before:duration-300 [&_th:not([data-pinned=right])+th[data-pinned=right]]:before:pointer-events-none [&_th:not([data-pinned=right])+th[data-pinned=right]]:before:shadow-[inset_-10px_0_8px_-8px_var(--pinned-shadow-color)] [&_td:not([data-pinned=right])+td[data-pinned=right]]:before:absolute [&_td:not([data-pinned=right])+td[data-pinned=right]]:before:top-0 [&_td:not([data-pinned=right])+td[data-pinned=right]]:before:left-0 [&_td:not([data-pinned=right])+td[data-pinned=right]]:before:bottom-0 [&_td:not([data-pinned=right])+td[data-pinned=right]]:before:w-[30px] [&_td:not([data-pinned=right])+td[data-pinned=right]]:before:-translate-x-full [&_td:not([data-pinned=right])+td[data-pinned=right]]:before:transition-opacity [&_td:not([data-pinned=right])+td[data-pinned=right]]:before:duration-200 [&_td:not([data-pinned=right])+td[data-pinned=right]]:before:pointer-events-none [&_td:not([data-pinned=right])+td[data-pinned=right]]:before:shadow-[inset_-10px_0_8px_-8px_var(--pinned-shadow-color)]",
11
+ show: "[&_th:not([data-pinned=right])+th[data-pinned=right]]:before:opacity-100 [&_td:not([data-pinned=right])+td[data-pinned=right]]:before:opacity-100",
12
+ hide: "[&_th:not([data-pinned=right])+th[data-pinned=right]]:before:opacity-0 [&_td:not([data-pinned=right])+td[data-pinned=right]]:before:opacity-0"
13
13
  }
14
14
  };
15
15
  const EXPANDED_STICKY_CLASS = "[&_tr[data-expanded=true]]:sticky [&_tr[data-expanded=true]]:top-[calc(var(--ui-table-header-height)+1px)] [&_tr[data-expanded=true]]:z-10 [&_tr[data-expanded=true]]:bg-default";
16
+ const PINNED_POSITION_CLASS = "[&_th[data-pinned=left]]:!transition-colors [&_th[data-pinned=right]]:!transition-colors [&_td[data-pinned=left]]:!transition-colors [&_td[data-pinned=right]]:!transition-colors [&_th[data-pinned=left]]:!duration-150 [&_th[data-pinned=right]]:!duration-150 [&_td[data-pinned=left]]:!duration-150 [&_td[data-pinned=right]]:!duration-150";
17
+ const PINNED_HOVER_CLASS = "[&_tbody_tr:hover_td[data-pinned=left]]:!bg-muted [&_tbody_tr:hover_td[data-pinned=right]]:!bg-muted";
16
18
  export function useProTableView(props) {
17
19
  const {
18
20
  data,
@@ -23,7 +25,7 @@ export function useProTableView(props) {
23
25
  fetchList,
24
26
  rowSelection,
25
27
  onUpdateRowSelection,
26
- tblProps,
28
+ tblProps: baseTblProps,
27
29
  tblWhereQueryProps,
28
30
  tblHeaderProps,
29
31
  tblPaginationProps,
@@ -56,8 +58,14 @@ export function useProTableView(props) {
56
58
  });
57
59
  const tableWidth = ref(0);
58
60
  const tableDiv = useTemplateRef("table");
61
+ const columnSizing = ref({});
59
62
  let resizeObserver = null;
63
+ let mutationObserver = null;
60
64
  let scrollContainer = null;
65
+ let pinnedOffsetFrame = null;
66
+ let tableDomUpdateQueued = false;
67
+ let postRenderPinnedOffsetQueued = false;
68
+ let renderedTableStateQueued = false;
61
69
  const showLeftPinnedShadow = ref(false);
62
70
  const showRightPinnedShadow = ref(false);
63
71
  const hasVerticalOverflow = ref(false);
@@ -68,8 +76,169 @@ export function useProTableView(props) {
68
76
  showRightPinnedShadow.value ? PINNED_SHADOW_CLASSES.right.show : PINNED_SHADOW_CLASSES.right.hide
69
77
  ]);
70
78
  const HIDE_LAST_ROW_BORDER_CLASS = "[&_tbody_tr:last-child_td]:border-b-0";
71
- const tblClasses = computed(() => [pinnedShadowClasses.value, EXPANDED_STICKY_CLASS, hasVerticalOverflow.value && HIDE_LAST_ROW_BORDER_CLASS]);
79
+ const tblClasses = computed(() => [pinnedShadowClasses.value, EXPANDED_STICKY_CLASS, PINNED_POSITION_CLASS, PINNED_HOVER_CLASS, hasVerticalOverflow.value && HIDE_LAST_ROW_BORDER_CLASS]);
72
80
  const tblUi = computed(() => ({ root: "relative overflow-clip", th: thClass.value, td: tdClass.value }));
81
+ const tblProps = computed(() => ({
82
+ ...baseTblProps.value,
83
+ columnSizing: columnSizing.value
84
+ }));
85
+ function getColumnId(column) {
86
+ if (!column || typeof column !== "object") return void 0;
87
+ const col = column;
88
+ return col.id ?? col.accessorKey;
89
+ }
90
+ function getLeafColumnIds(columns) {
91
+ return columns.flatMap((column) => {
92
+ if (!column || typeof column !== "object") return [];
93
+ const col = column;
94
+ if (Array.isArray(col.columns) && col.columns.length) {
95
+ return getLeafColumnIds(col.columns);
96
+ }
97
+ const columnId = getColumnId(column);
98
+ return columnId ? [columnId] : [];
99
+ });
100
+ }
101
+ function getRenderedLeafColumnIds(columns, columnPinning) {
102
+ const columnIds = getLeafColumnIds(columns);
103
+ const columnIdSet = new Set(columnIds);
104
+ const leftIds = columnPinning?.left?.filter((id) => columnIdSet.has(id)) ?? [];
105
+ const rightIds = columnPinning?.right?.filter((id) => columnIdSet.has(id)) ?? [];
106
+ const pinnedIds = /* @__PURE__ */ new Set([...leftIds, ...rightIds]);
107
+ const centerIds = columnIds.filter((id) => !pinnedIds.has(id));
108
+ return [...leftIds, ...centerIds, ...rightIds];
109
+ }
110
+ function isSameColumnSizing(prev, next) {
111
+ const prevKeys = Object.keys(prev);
112
+ const nextKeys = Object.keys(next);
113
+ return prevKeys.length === nextKeys.length && nextKeys.every((key) => prev[key] === next[key]);
114
+ }
115
+ function getElementWidth(element) {
116
+ return element.getBoundingClientRect().width;
117
+ }
118
+ function getDirectTableCells(row) {
119
+ return Array.from(row.children).filter((cell) => cell instanceof HTMLTableCellElement && (cell.dataset.slot === "th" || cell.dataset.slot === "td"));
120
+ }
121
+ function updateColumnSizing() {
122
+ if (!tableDiv.value) return false;
123
+ const leafColumns = getRenderedLeafColumnIds(baseTblProps.value.columns ?? [], baseTblProps.value.columnPinning);
124
+ const headers = Array.from(tableDiv.value.querySelectorAll('thead tr[data-slot="tr"] th[data-slot="th"]'));
125
+ const bodyRows = Array.from(tableDiv.value.querySelectorAll('tbody tr[data-slot="tr"]'));
126
+ if (!leafColumns.length || !headers.length) return false;
127
+ const nextSizing = {};
128
+ headers.slice(0, leafColumns.length).forEach((header, index) => {
129
+ const columnId = leafColumns[index];
130
+ let width = getElementWidth(header);
131
+ bodyRows.forEach((row) => {
132
+ const cell = row.children[index];
133
+ if (cell instanceof HTMLElement) {
134
+ width = Math.max(width, getElementWidth(cell));
135
+ }
136
+ });
137
+ if (columnId && width > 0) {
138
+ nextSizing[columnId] = Math.ceil(width);
139
+ }
140
+ });
141
+ if (isSameColumnSizing(columnSizing.value, nextSizing)) return false;
142
+ columnSizing.value = nextSizing;
143
+ return true;
144
+ }
145
+ function updatePinnedColumnOffsets() {
146
+ if (!tableDiv.value) return;
147
+ const headerRow = tableDiv.value.querySelector('thead tr[data-slot="tr"]');
148
+ if (!headerRow) return;
149
+ const headerCells = getDirectTableCells(headerRow);
150
+ if (!headerCells.length) return;
151
+ const leftOffsets = [];
152
+ const rightOffsets = [];
153
+ let leftOffset = 0;
154
+ headerCells.forEach((cell, index) => {
155
+ if (cell.dataset.pinned !== "left") return;
156
+ leftOffsets.push([index, leftOffset]);
157
+ leftOffset += getElementWidth(cell);
158
+ });
159
+ let rightOffset = 0;
160
+ for (let index = headerCells.length - 1; index >= 0; index--) {
161
+ const cell = headerCells[index];
162
+ if (!cell || cell.dataset.pinned !== "right") continue;
163
+ rightOffsets.push([index, rightOffset]);
164
+ rightOffset += getElementWidth(cell);
165
+ }
166
+ if (!leftOffsets.length && !rightOffsets.length) return;
167
+ const setCellOffset = (cell, side, offset) => {
168
+ const value = `${offset}px`;
169
+ if (cell.style[side] !== value) {
170
+ cell.style[side] = value;
171
+ }
172
+ };
173
+ const rows = Array.from(tableDiv.value.querySelectorAll('tr[data-slot="tr"]'));
174
+ rows.forEach((row) => {
175
+ const cells = getDirectTableCells(row);
176
+ if (!cells.length) return;
177
+ leftOffsets.forEach(([index, offset]) => {
178
+ const cell = cells[index];
179
+ if (cell?.dataset.pinned === "left") {
180
+ setCellOffset(cell, "left", offset);
181
+ }
182
+ });
183
+ rightOffsets.forEach(([index, offset]) => {
184
+ const cell = cells[index];
185
+ if (cell?.dataset.pinned === "right") {
186
+ setCellOffset(cell, "right", offset);
187
+ }
188
+ });
189
+ });
190
+ }
191
+ function queuePinnedColumnOffsetUpdate(immediate = true) {
192
+ if (immediate) {
193
+ updatePinnedColumnOffsets();
194
+ }
195
+ if (typeof requestAnimationFrame !== "function") return;
196
+ if (pinnedOffsetFrame !== null) {
197
+ return;
198
+ }
199
+ pinnedOffsetFrame = requestAnimationFrame(() => {
200
+ pinnedOffsetFrame = null;
201
+ updatePinnedColumnOffsets();
202
+ });
203
+ }
204
+ function queuePostRenderPinnedColumnOffsetUpdate() {
205
+ if (postRenderPinnedOffsetQueued) return;
206
+ postRenderPinnedOffsetQueued = true;
207
+ nextTick(() => {
208
+ postRenderPinnedOffsetQueued = false;
209
+ queuePinnedColumnOffsetUpdate();
210
+ checkShadowVisibility();
211
+ });
212
+ }
213
+ function updateRenderedTableState() {
214
+ const columnSizingChanged = updateColumnSizing();
215
+ queuePinnedColumnOffsetUpdate();
216
+ if (columnSizingChanged) {
217
+ queuePostRenderPinnedColumnOffsetUpdate();
218
+ }
219
+ checkShadowVisibility();
220
+ }
221
+ function queueRenderedTableStateUpdate() {
222
+ if (renderedTableStateQueued) return;
223
+ renderedTableStateQueued = true;
224
+ nextTick(() => {
225
+ renderedTableStateQueued = false;
226
+ updateRenderedTableState();
227
+ });
228
+ }
229
+ function queueTableDomUpdate() {
230
+ if (tableDomUpdateQueued) return;
231
+ tableDomUpdateQueued = true;
232
+ const runUpdate = () => {
233
+ tableDomUpdateQueued = false;
234
+ updateRenderedTableState();
235
+ };
236
+ if (typeof queueMicrotask === "function") {
237
+ queueMicrotask(runUpdate);
238
+ return;
239
+ }
240
+ Promise.resolve().then(runUpdate);
241
+ }
73
242
  function updateTableWidth() {
74
243
  if (tableDiv.value) {
75
244
  tableWidth.value = tableDiv.value.offsetWidth;
@@ -87,6 +256,7 @@ export function useProTableView(props) {
87
256
  const { scrollLeft, scrollWidth, clientWidth } = target;
88
257
  showLeftPinnedShadow.value = scrollLeft > 0;
89
258
  showRightPinnedShadow.value = scrollLeft + clientWidth < scrollWidth - 1;
259
+ queuePinnedColumnOffsetUpdate(false);
90
260
  }
91
261
  onMounted(() => {
92
262
  nextTick(() => {
@@ -102,27 +272,55 @@ export function useProTableView(props) {
102
272
  if (tableDiv.value) {
103
273
  resizeObserver = new ResizeObserver(() => {
104
274
  updateTableWidth();
105
- nextTick(() => checkShadowVisibility());
275
+ queueRenderedTableStateUpdate();
106
276
  });
107
277
  resizeObserver.observe(tableDiv.value);
278
+ mutationObserver = new MutationObserver(() => {
279
+ queueTableDomUpdate();
280
+ });
281
+ const observedTableBody = tableDiv.value.querySelector("tbody") ?? tableDiv.value;
282
+ mutationObserver.observe(observedTableBody, {
283
+ childList: true,
284
+ subtree: true,
285
+ attributes: true,
286
+ attributeFilter: ["data-expanded"]
287
+ });
108
288
  updateTableWidth();
109
- checkShadowVisibility();
289
+ updateRenderedTableState();
110
290
  }
111
291
  });
112
292
  });
113
293
  watch(data, () => {
114
- nextTick(() => checkShadowVisibility());
294
+ queueRenderedTableStateUpdate();
115
295
  }, { flush: "post" });
296
+ watch(
297
+ () => [baseTblProps.value.columns, baseTblProps.value.columnPinning],
298
+ () => {
299
+ queueRenderedTableStateUpdate();
300
+ },
301
+ { flush: "post" }
302
+ );
116
303
  onUnmounted(() => {
117
304
  if (resizeObserver && tableDiv.value) {
118
305
  resizeObserver.unobserve(tableDiv.value);
119
306
  resizeObserver.disconnect();
120
307
  resizeObserver = null;
121
308
  }
309
+ if (mutationObserver) {
310
+ mutationObserver.disconnect();
311
+ mutationObserver = null;
312
+ }
122
313
  if (scrollContainer) {
123
314
  scrollContainer.removeEventListener("scroll", handleScroll);
124
315
  scrollContainer = null;
125
316
  }
317
+ if (pinnedOffsetFrame !== null) {
318
+ cancelAnimationFrame(pinnedOffsetFrame);
319
+ pinnedOffsetFrame = null;
320
+ }
321
+ tableDomUpdateQueued = false;
322
+ postRenderPinnedOffsetQueued = false;
323
+ renderedTableStateQueued = false;
126
324
  });
127
325
  return {
128
326
  // data
@@ -1,5 +1,5 @@
1
1
  import { computed } from "vue";
2
- import { createSharedComposable, useColorMode } from "@vueuse/core";
2
+ import { createSharedComposable, useColorMode, usePreferredDark } from "@vueuse/core";
3
3
  import { en, zh_cn } from "@nuxt/ui/locale";
4
4
  import { useApp } from "./useApp.js";
5
5
  import { useAppConfig } from "nuxt/app";
@@ -25,8 +25,15 @@ const twPrimaryColorNames = [
25
25
  const twNeutralColorNames = ["slate", "gray", "zinc", "neutral", "stone", "taupe", "mauve", "mist", "olive"];
26
26
  const _useTheme = () => {
27
27
  const appConfig = useAppConfig();
28
- const colorMode = useColorMode();
28
+ const colorMode = useColorMode({ emitAuto: true });
29
29
  const app = useApp();
30
+ const isDark = usePreferredDark();
31
+ const resolvedColorMode = computed(() => {
32
+ if (colorMode.value === "auto") {
33
+ return isDark.value ? "dark" : "light";
34
+ }
35
+ return colorMode.value;
36
+ });
30
37
  const neutral = computed({
31
38
  get() {
32
39
  return appConfig.ui?.colors?.neutral;
@@ -56,7 +63,7 @@ const _useTheme = () => {
56
63
  }
57
64
  });
58
65
  const chartColorVars = computed(() => ["chart-1", "chart-2", "chart-3", "chart-4", "chart-5"].map((c) => `var(--${c})`));
59
- const primaryColorVars = computed(() => twPrimaryColorNames.map((c) => `var(--color-${c}-${colorMode.value === "light" ? "400" : "500"})`));
66
+ const primaryColorVars = computed(() => twPrimaryColorNames.map((c) => `var(--color-${c}-${resolvedColorMode.value === "light" ? "400" : "500"})`));
60
67
  const radiuses = [0, 0.125, 0.25, 0.375, 0.5];
61
68
  const radius = computed({
62
69
  get() {
@@ -5,9 +5,9 @@ export const getCreateAtColumn = (createdAtSortOpr = "desc") => ({
5
5
  accessorKey: "createdAt",
6
6
  header: "\u521B\u5EFA\u65F6\u95F4",
7
7
  cell: ({ row }) => dayjs(row.original.createdAt).format(dateTimeFormat),
8
- preferred: false,
9
8
  filterOption: {
10
- type: "date-picker"
9
+ type: "date-picker",
10
+ preferred: false
11
11
  },
12
12
  sortOption: {
13
13
  defaultOpr: createdAtSortOpr
@@ -18,9 +18,9 @@ export const getOprColumns = (createdAtSortOpr = "desc") => [
18
18
  accessorKey: "createdBy",
19
19
  header: "\u521B\u5EFA\u4EBA",
20
20
  cell: ({ row }) => row.original.creator?.nickname || "/",
21
- preferred: false,
22
21
  filterOption: {
23
22
  type: "async-select",
23
+ preferred: false,
24
24
  listApi: useUserApi().list,
25
25
  likeSearchFields: ["nickname"],
26
26
  labelField: "nickname",
@@ -34,9 +34,9 @@ export const getOprColumns = (createdAtSortOpr = "desc") => [
34
34
  accessorKey: "updatedBy",
35
35
  header: "\u66F4\u65B0\u4EBA",
36
36
  cell: ({ row }) => row.original.updater?.nickname || "/",
37
- preferred: false,
38
37
  filterOption: {
39
38
  type: "async-select",
39
+ preferred: false,
40
40
  listApi: useUserApi().list,
41
41
  likeSearchFields: ["nickname"],
42
42
  labelField: "nickname",
@@ -49,9 +49,9 @@ export const getOprColumns = (createdAtSortOpr = "desc") => [
49
49
  accessorKey: "updatedAt",
50
50
  header: "\u66F4\u65B0\u65F6\u95F4",
51
51
  cell: ({ row }) => dayjs(row.original.updatedAt).format(dateTimeFormat),
52
- preferred: false,
53
52
  filterOption: {
54
- type: "date-picker"
53
+ type: "date-picker",
54
+ preferred: false
55
55
  },
56
56
  sortOption: true
57
57
  }
@@ -12,7 +12,7 @@ export type SaveModalFormTemplateProps<T> = {
12
12
  title: string;
13
13
  description?: string;
14
14
  onClose: (ok: boolean) => void;
15
- onSubmit: () => Promise<void>;
15
+ onSubmit: () => Promise<boolean | undefined>;
16
16
  fields: VFormFieldProps[];
17
17
  oldModelValue: Partial<T>;
18
18
  modelValue: Partial<T>;
@@ -6,7 +6,9 @@ export type WhereQueryColumnOption<T> = {
6
6
  defaultOpr?: WhereQueryOpr;
7
7
  custom?: boolean;
8
8
  initValues?: any;
9
+ /** 查询条件是否默认隐藏(initHide=true 时不在 where 面板显示) */
9
10
  initHide?: boolean;
11
+ preferred?: boolean;
10
12
  disableOprSelector?: boolean;
11
13
  } & ({
12
14
  type: 'input';
@@ -29,8 +31,8 @@ export type OrderQueryColumnOption = {
29
31
  export type VColumn<T> = {
30
32
  filterOption?: WhereQueryColumnOption<any>;
31
33
  sortOption?: OrderQueryColumnOption | true;
34
+ /** 列是否默认隐藏(控制表格列可见性) */
32
35
  initHide?: boolean;
33
36
  checked?: boolean;
34
- preferred?: boolean;
35
37
  exportCell?(row: T): string | string[];
36
38
  } & TableColumn<T>;