@kine-design/crud 0.0.1-beta.23 → 0.0.1-beta.24

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.
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * 一个配置出一整个页面:标题 + 筛选 + 表格 + 分页 + 状态标签。
10
10
  */
11
- import { defineComponent, type PropType } from 'vue';
11
+ import { defineComponent, ref, computed, type PropType } from 'vue';
12
12
  import KTableColumn from 'kine-ui/components/tableColumn/KTableColumn.tsx';
13
13
  import KTag from 'kine-ui/components/tag/KTag.tsx';
14
14
  import KImage from 'kine-ui/components/image/KImage.tsx';
@@ -31,10 +31,15 @@ export default defineComponent({
31
31
  },
32
32
 
33
33
  setup(props, { slots }) {
34
+ const rootRef = ref<HTMLElement>();
35
+ const tableBodyRef = computed(
36
+ () => rootRef.value?.querySelector('.k-search-table-body') as HTMLElement | undefined,
37
+ );
38
+
34
39
  const {
35
40
  page, pageSize, total, list, loading,
36
41
  filters, onPageChange, onSearch, onReset,
37
- } = useCrudPage(props.config);
42
+ } = useCrudPage(props.config, tableBodyRef);
38
43
 
39
44
  /** 格式化日期 */
40
45
  const formatDate = (val: unknown) => {
@@ -131,7 +136,7 @@ export default defineComponent({
131
136
  };
132
137
 
133
138
  return () => (
134
- <div class="k-crud-page">
139
+ <div class="k-crud-page" ref={rootRef}>
135
140
  <KPageHeader title={props.config.title}>
136
141
  {{ extra: slots.headerExtra }}
137
142
  </KPageHeader>
@@ -0,0 +1,194 @@
1
+ /**
2
+ * @description useAutoPageSize 单元测试
3
+ * @author 阿怪
4
+ * @date 2026/5/7
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
10
+ import { ref } from 'vue';
11
+ import { useAutoPageSize } from '../useAutoPageSize';
12
+
13
+ function mockContainer(clientHeight: number) {
14
+ return ref({ clientHeight } as HTMLElement);
15
+ }
16
+
17
+ // Fix #5: use queueMicrotask to preserve one async tick, return incrementing ID
18
+ let rafId = 0;
19
+ beforeEach(() => {
20
+ rafId = 0;
21
+ vi.stubGlobal('requestAnimationFrame', (cb: FrameRequestCallback) => {
22
+ const id = ++rafId;
23
+ queueMicrotask(() => cb(0));
24
+ return id;
25
+ });
26
+ });
27
+
28
+ afterEach(() => {
29
+ vi.unstubAllGlobals();
30
+ });
31
+
32
+ describe('useAutoPageSize', () => {
33
+ describe('初始状态', () => {
34
+ it('pageSize 初始为 0,resolved 初始为 false', () => {
35
+ const container = mockContainer(500);
36
+ const { pageSize, resolved } = useAutoPageSize(container);
37
+ expect(pageSize.value).toBe(0);
38
+ expect(resolved.value).toBe(false);
39
+ });
40
+ });
41
+
42
+ describe('calculate 默认参数', () => {
43
+ it('标准高度:(500 - 40) / 48 = 9', async () => {
44
+ const container = mockContainer(500);
45
+ const { waitForLayout } = useAutoPageSize(container);
46
+ const size = await waitForLayout();
47
+ expect(size).toBe(9);
48
+ });
49
+
50
+ it('大屏高度:(900 - 40) / 48 = 17', async () => {
51
+ const container = mockContainer(900);
52
+ const { waitForLayout } = useAutoPageSize(container);
53
+ const size = await waitForLayout();
54
+ expect(size).toBe(17);
55
+ });
56
+
57
+ it('刚好整除:(520 - 40) / 48 = 10', async () => {
58
+ const container = mockContainer(520);
59
+ const { waitForLayout } = useAutoPageSize(container);
60
+ expect(await waitForLayout()).toBe(10);
61
+ });
62
+ });
63
+
64
+ describe('自定义 rowHeight / headerHeight', () => {
65
+ it('自定义行高:(500 - 40) / 60 = 7', async () => {
66
+ const container = mockContainer(500);
67
+ const { waitForLayout } = useAutoPageSize(container, { rowHeight: 60 });
68
+ expect(await waitForLayout()).toBe(7);
69
+ });
70
+
71
+ it('自定义表头高度:(500 - 56) / 48 = 9', async () => {
72
+ const container = mockContainer(500);
73
+ const { waitForLayout } = useAutoPageSize(container, { headerHeight: 56 });
74
+ expect(await waitForLayout()).toBe(9);
75
+ });
76
+
77
+ it('同时自定义:(600 - 50) / 55 = 10', async () => {
78
+ const container = mockContainer(600);
79
+ const { waitForLayout } = useAutoPageSize(container, { rowHeight: 55, headerHeight: 50 });
80
+ expect(await waitForLayout()).toBe(10);
81
+ });
82
+ });
83
+
84
+ // Fix #2: nullish-coalescing fallback paths
85
+ describe('nullish 参数回退到默认值', () => {
86
+ it('rowHeight 为 undefined 时回退默认 48', async () => {
87
+ const container = mockContainer(500);
88
+ const { waitForLayout } = useAutoPageSize(container, { rowHeight: undefined });
89
+ // (500 - 40) / 48 = 9.58 → floor = 9, same as no options
90
+ expect(await waitForLayout()).toBe(9);
91
+ });
92
+
93
+ it('headerHeight 为 undefined 时回退默认 40', async () => {
94
+ const container = mockContainer(500);
95
+ const { waitForLayout } = useAutoPageSize(container, { headerHeight: undefined });
96
+ // (500 - 40) / 48 = 9.58 → floor = 9, same as no options
97
+ expect(await waitForLayout()).toBe(9);
98
+ });
99
+ });
100
+
101
+ describe('边界:MIN_PAGE_SIZE 兜底', () => {
102
+ it('容器 ref 为 undefined → 返回 5', async () => {
103
+ const container = ref<HTMLElement | undefined>(undefined);
104
+ const { waitForLayout } = useAutoPageSize(container);
105
+ expect(await waitForLayout()).toBe(5);
106
+ });
107
+
108
+ it('容器高度为 0 → 返回 5', async () => {
109
+ const container = mockContainer(0);
110
+ const { waitForLayout } = useAutoPageSize(container);
111
+ expect(await waitForLayout()).toBe(5);
112
+ });
113
+
114
+ it('可用高度为负(容器比表头还矮)→ 返回 5', async () => {
115
+ const container = mockContainer(20);
116
+ const { waitForLayout } = useAutoPageSize(container);
117
+ expect(await waitForLayout()).toBe(5);
118
+ });
119
+
120
+ it('可用高度不够一行但大于 0 → 返回 5', async () => {
121
+ const container = mockContainer(60);
122
+ const { waitForLayout } = useAutoPageSize(container);
123
+ // (60 - 40) / 48 = 0.41 → floor = 0 → max(0, 5) = 5
124
+ expect(await waitForLayout()).toBe(5);
125
+ });
126
+
127
+ // Fix #3: near-integer floor truncation
128
+ it('接近整数但不到:(519 - 40) / 48 = 9.979 → floor 为 9', async () => {
129
+ const container = mockContainer(519);
130
+ const { waitForLayout } = useAutoPageSize(container);
131
+ // (519 - 40) / 48 = 479 / 48 = 9.979… → floor = 9, not rounded to 10
132
+ expect(await waitForLayout()).toBe(9);
133
+ });
134
+ });
135
+
136
+ describe('waitForLayout 状态更新', () => {
137
+ // Fix #1: concrete assertion instead of self-referential check
138
+ it('resolve 后 pageSize 和 resolved 同步更新', async () => {
139
+ const container = mockContainer(500);
140
+ const { pageSize, resolved, waitForLayout } = useAutoPageSize(container);
141
+
142
+ const size = await waitForLayout();
143
+ // (500 - 40) / 48 = 9.58 → floor = 9
144
+ expect(size).toBe(9);
145
+ expect(pageSize.value).toBe(9);
146
+ expect(resolved.value).toBe(true);
147
+ });
148
+
149
+ // Fix #1: test a different container height with concrete value
150
+ it('不同容器高度 resolve 值正确:(700 - 40) / 48 = 13', async () => {
151
+ const container = mockContainer(700);
152
+ const { pageSize, waitForLayout } = useAutoPageSize(container);
153
+
154
+ const size = await waitForLayout();
155
+ // (700 - 40) / 48 = 13.75 → floor = 13
156
+ expect(size).toBe(13);
157
+ expect(pageSize.value).toBe(13);
158
+ });
159
+ });
160
+
161
+ // Fix #4: multiple waitForLayout calls
162
+ describe('多次调用 waitForLayout', () => {
163
+ it('同一实例多次调用 waitForLayout 会重新计算', async () => {
164
+ const container = mockContainer(500);
165
+ const { pageSize, waitForLayout } = useAutoPageSize(container);
166
+
167
+ const first = await waitForLayout();
168
+ expect(first).toBe(9);
169
+ expect(pageSize.value).toBe(9);
170
+
171
+ // second call on same instance, same container → same result
172
+ const second = await waitForLayout();
173
+ expect(second).toBe(9);
174
+ expect(pageSize.value).toBe(9);
175
+ });
176
+
177
+ it('容器高度变化后再次调用 waitForLayout 得到新值', async () => {
178
+ const container = ref({ clientHeight: 500 } as HTMLElement);
179
+ const { pageSize, waitForLayout } = useAutoPageSize(container);
180
+
181
+ const first = await waitForLayout();
182
+ expect(first).toBe(9);
183
+ expect(pageSize.value).toBe(9);
184
+
185
+ // mutate container height
186
+ container.value = { clientHeight: 900 } as HTMLElement;
187
+
188
+ const second = await waitForLayout();
189
+ // (900 - 40) / 48 = 17.91 → floor = 17
190
+ expect(second).toBe(17);
191
+ expect(pageSize.value).toBe(17);
192
+ });
193
+ });
194
+ });
@@ -8,4 +8,6 @@
8
8
  */
9
9
 
10
10
  export { useCrudPage } from './useCrudPage';
11
+ export { useAutoPageSize } from './useAutoPageSize';
12
+ export type { AutoPageSizeOptions } from './useAutoPageSize';
11
13
  export type { CrudPageConfig, CrudColumnConfig, CrudFilterConfig, StatusMapItem } from './types';
@@ -53,6 +53,13 @@ export interface CrudPageConfig {
53
53
  statusMap?: Record<string, StatusMapItem>;
54
54
  /** 每页条数,默认 20 */
55
55
  pageSize?: number;
56
+ /**
57
+ * 根据表格容器高度自动计算 pageSize(首次计算,后续锁定)。
58
+ * - true: 使用默认行高(48px)
59
+ * - { rowHeight, headerHeight }: 自定义行高参数
60
+ * 启用后 pageSize 字段作为 fallback 使用。
61
+ */
62
+ autoPageSize?: boolean | { rowHeight?: number; headerHeight?: number };
56
63
  /** 行主键字段,默认 'id' */
57
64
  rowKey?: string;
58
65
  /** 详情页路由前缀,用于构建查看/编辑路由:`${detailPath}/${row[rowKey]}` */
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @description 根据容器可用高度自动计算 pageSize(首次计算,后续锁定)
3
+ * @author 阿怪
4
+ * @date 2026/5/7
5
+ * @version v0.0.1
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+
10
+ import { ref, type Ref } from 'vue';
11
+
12
+ export interface AutoPageSizeOptions {
13
+ rowHeight?: number;
14
+ headerHeight?: number;
15
+ }
16
+
17
+ const DEFAULT_ROW_HEIGHT = 48;
18
+ const DEFAULT_HEADER_HEIGHT = 40;
19
+ const MIN_PAGE_SIZE = 5;
20
+
21
+ export function useAutoPageSize(
22
+ containerRef: Ref<HTMLElement | undefined>,
23
+ options: AutoPageSizeOptions = {},
24
+ ) {
25
+ const rowHeight = options.rowHeight ?? DEFAULT_ROW_HEIGHT;
26
+ const headerHeight = options.headerHeight ?? DEFAULT_HEADER_HEIGHT;
27
+
28
+ const pageSize = ref(0);
29
+ const resolved = ref(false);
30
+
31
+ function calculate(): number {
32
+ const el = containerRef.value;
33
+ if (!el) return MIN_PAGE_SIZE;
34
+
35
+ const available = el.clientHeight - headerHeight;
36
+ return Math.max(Math.floor(available / rowHeight), MIN_PAGE_SIZE);
37
+ }
38
+
39
+ function waitForLayout(): Promise<number> {
40
+ return new Promise<number>(resolve => {
41
+ requestAnimationFrame(() => {
42
+ const size = calculate();
43
+ pageSize.value = size;
44
+ resolved.value = true;
45
+ resolve(size);
46
+ });
47
+ });
48
+ }
49
+
50
+ return {
51
+ pageSize,
52
+ resolved,
53
+ waitForLayout,
54
+ };
55
+ }
@@ -7,11 +7,12 @@
7
7
  * 江湖的业务千篇一律,复杂的代码好几百行。
8
8
  */
9
9
 
10
- import { ref, reactive, computed, onMounted } from 'vue';
10
+ import { ref, reactive, computed, onMounted, type Ref } from 'vue';
11
11
  import { useRequestClient } from '../../setup';
12
12
  import type { CrudPageConfig } from './types';
13
+ import { useAutoPageSize, type AutoPageSizeOptions } from './useAutoPageSize';
13
14
 
14
- export function useCrudPage(config: CrudPageConfig) {
15
+ export function useCrudPage(config: CrudPageConfig, tableBodyRef?: Ref<HTMLElement | undefined>) {
15
16
  const client = useRequestClient();
16
17
 
17
18
  const page = ref(1);
@@ -20,6 +21,14 @@ export function useCrudPage(config: CrudPageConfig) {
20
21
  const list = ref<Record<string, unknown>[]>([]);
21
22
  const loading = ref(false);
22
23
 
24
+ const autoOptions: AutoPageSizeOptions | undefined = config.autoPageSize
25
+ ? (typeof config.autoPageSize === 'object' ? config.autoPageSize : {})
26
+ : undefined;
27
+
28
+ const auto = autoOptions && tableBodyRef
29
+ ? useAutoPageSize(tableBodyRef, autoOptions)
30
+ : undefined;
31
+
23
32
  // 筛选表单状态
24
33
  const filters = reactive<Record<string, unknown>>({});
25
34
  if (config.filters) {
@@ -70,7 +79,13 @@ export function useCrudPage(config: CrudPageConfig) {
70
79
  fetchData();
71
80
  }
72
81
 
73
- onMounted(fetchData);
82
+ onMounted(async () => {
83
+ if (auto) {
84
+ const size = await auto.waitForLayout();
85
+ pageSize.value = size;
86
+ }
87
+ fetchData();
88
+ });
74
89
 
75
90
  return {
76
91
  page,
@@ -7,4 +7,6 @@
7
7
  * 江湖的业务千篇一律,复杂的代码好几百行。
8
8
  */
9
9
  export { useCrudPage } from './useCrudPage';
10
+ export { useAutoPageSize } from './useAutoPageSize';
11
+ export type { AutoPageSizeOptions } from './useAutoPageSize';
10
12
  export type { CrudPageConfig, CrudColumnConfig, CrudFilterConfig, StatusMapItem } from './types';
@@ -52,6 +52,16 @@ export interface CrudPageConfig {
52
52
  statusMap?: Record<string, StatusMapItem>;
53
53
  /** 每页条数,默认 20 */
54
54
  pageSize?: number;
55
+ /**
56
+ * 根据表格容器高度自动计算 pageSize(首次计算,后续锁定)。
57
+ * - true: 使用默认行高(48px)
58
+ * - { rowHeight, headerHeight }: 自定义行高参数
59
+ * 启用后 pageSize 字段作为 fallback 使用。
60
+ */
61
+ autoPageSize?: boolean | {
62
+ rowHeight?: number;
63
+ headerHeight?: number;
64
+ };
55
65
  /** 行主键字段,默认 'id' */
56
66
  rowKey?: string;
57
67
  /** 详情页路由前缀,用于构建查看/编辑路由:`${detailPath}/${row[rowKey]}` */
@@ -0,0 +1,10 @@
1
+ import { Ref } from 'vue';
2
+ export interface AutoPageSizeOptions {
3
+ rowHeight?: number;
4
+ headerHeight?: number;
5
+ }
6
+ export declare function useAutoPageSize(containerRef: Ref<HTMLElement | undefined>, options?: AutoPageSizeOptions): {
7
+ pageSize: Ref<number, number>;
8
+ resolved: Ref<boolean, boolean>;
9
+ waitForLayout: () => Promise<number>;
10
+ };
@@ -1,11 +1,12 @@
1
+ import { Ref } from 'vue';
1
2
  import { CrudPageConfig } from './types';
2
- export declare function useCrudPage(config: CrudPageConfig): {
3
- page: import('vue').Ref<number, number>;
4
- pageSize: import('vue').Ref<number, number>;
5
- total: import('vue').Ref<number, number>;
3
+ export declare function useCrudPage(config: CrudPageConfig, tableBodyRef?: Ref<HTMLElement | undefined>): {
4
+ page: Ref<number, number>;
5
+ pageSize: Ref<number, number>;
6
+ total: Ref<number, number>;
6
7
  totalPages: import('vue').ComputedRef<number>;
7
- list: import('vue').Ref<Record<string, unknown>[], Record<string, unknown>[]>;
8
- loading: import('vue').Ref<boolean, boolean>;
8
+ list: Ref<Record<string, unknown>[], Record<string, unknown>[]>;
9
+ loading: Ref<boolean, boolean>;
9
10
  filters: Record<string, unknown>;
10
11
  fetchData: () => Promise<void>;
11
12
  onPageChange: (p: number) => void;
package/dist/crud.js CHANGED
@@ -8894,6 +8894,46 @@ function createCrudAppWithOptions(rootComponent, options) {
8894
8894
  return enhancedApp;
8895
8895
  }
8896
8896
  //#endregion
8897
+ //#region composables/page/useAutoPageSize.ts
8898
+ /**
8899
+ * @description 根据容器可用高度自动计算 pageSize(首次计算,后续锁定)
8900
+ * @author 阿怪
8901
+ * @date 2026/5/7
8902
+ * @version v0.0.1
8903
+ *
8904
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8905
+ */
8906
+ var DEFAULT_ROW_HEIGHT = 48;
8907
+ var DEFAULT_HEADER_HEIGHT = 40;
8908
+ var MIN_PAGE_SIZE = 5;
8909
+ function useAutoPageSize(containerRef, options = {}) {
8910
+ const rowHeight = options.rowHeight ?? DEFAULT_ROW_HEIGHT;
8911
+ const headerHeight = options.headerHeight ?? DEFAULT_HEADER_HEIGHT;
8912
+ const pageSize = ref(0);
8913
+ const resolved = ref(false);
8914
+ function calculate() {
8915
+ const el = containerRef.value;
8916
+ if (!el) return MIN_PAGE_SIZE;
8917
+ const available = el.clientHeight - headerHeight;
8918
+ return Math.max(Math.floor(available / rowHeight), MIN_PAGE_SIZE);
8919
+ }
8920
+ function waitForLayout() {
8921
+ return new Promise((resolve) => {
8922
+ requestAnimationFrame(() => {
8923
+ const size = calculate();
8924
+ pageSize.value = size;
8925
+ resolved.value = true;
8926
+ resolve(size);
8927
+ });
8928
+ });
8929
+ }
8930
+ return {
8931
+ pageSize,
8932
+ resolved,
8933
+ waitForLayout
8934
+ };
8935
+ }
8936
+ //#endregion
8897
8937
  //#region composables/page/useCrudPage.ts
8898
8938
  /**
8899
8939
  * @description CrudPage composable — 配置驱动的列表页数据层
@@ -8903,13 +8943,15 @@ function createCrudAppWithOptions(rootComponent, options) {
8903
8943
  *
8904
8944
  * 江湖的业务千篇一律,复杂的代码好几百行。
8905
8945
  */
8906
- function useCrudPage(config) {
8946
+ function useCrudPage(config, tableBodyRef) {
8907
8947
  const client = useRequestClient();
8908
8948
  const page = ref(1);
8909
8949
  const pageSize = ref(config.pageSize ?? 20);
8910
8950
  const total = ref(0);
8911
8951
  const list = ref([]);
8912
8952
  const loading = ref(false);
8953
+ const autoOptions = config.autoPageSize ? typeof config.autoPageSize === "object" ? config.autoPageSize : {} : void 0;
8954
+ const auto = autoOptions && tableBodyRef ? useAutoPageSize(tableBodyRef, autoOptions) : void 0;
8913
8955
  const filters = reactive({});
8914
8956
  if (config.filters) for (const f of config.filters) filters[f.param] = void 0;
8915
8957
  const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)));
@@ -8942,7 +8984,10 @@ function useCrudPage(config) {
8942
8984
  page.value = 1;
8943
8985
  fetchData();
8944
8986
  }
8945
- onMounted(fetchData);
8987
+ onMounted(async () => {
8988
+ if (auto) pageSize.value = await auto.waitForLayout();
8989
+ fetchData();
8990
+ });
8946
8991
  return {
8947
8992
  page,
8948
8993
  pageSize,
@@ -8976,7 +9021,9 @@ var KCrudPage_default = /* @__PURE__ */ defineComponent({
8976
9021
  required: true
8977
9022
  } },
8978
9023
  setup(props, { slots }) {
8979
- const { page, pageSize, total, list, loading, filters, onPageChange, onSearch, onReset } = useCrudPage(props.config);
9024
+ const rootRef = ref();
9025
+ const tableBodyRef = computed(() => rootRef.value?.querySelector(".k-search-table-body"));
9026
+ const { page, pageSize, total, list, loading, filters, onPageChange, onSearch, onReset } = useCrudPage(props.config, tableBodyRef);
8980
9027
  /** 格式化日期 */
8981
9028
  const formatDate = (val) => {
8982
9029
  if (!val) return "";
@@ -9082,7 +9129,10 @@ var KCrudPage_default = /* @__PURE__ */ defineComponent({
9082
9129
  }
9083
9130
  }, null)]));
9084
9131
  };
9085
- return () => createVNode("div", { "class": "k-crud-page" }, [createVNode(KPageHeader_default, { "title": props.config.title }, { extra: slots.headerExtra }), createVNode(KSearchTable_default, {
9132
+ return () => createVNode("div", {
9133
+ "class": "k-crud-page",
9134
+ "ref": rootRef
9135
+ }, [createVNode(KPageHeader_default, { "title": props.config.title }, { extra: slots.headerExtra }), createVNode(KSearchTable_default, {
9086
9136
  "data": list.value,
9087
9137
  "loading": loading.value,
9088
9138
  "total": total.value,
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @description @kine-design/crud 单元测试配置
3
+ * @author 阿怪
4
+ * @date 2026/5/7
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ declare const _default: import('vite').UserConfig;
10
+ export default _default;
package/package.json CHANGED
@@ -1,16 +1,19 @@
1
1
  {
2
2
  "name": "@kine-design/crud",
3
- "version": "0.0.1-beta.23",
3
+ "version": "0.0.1-beta.24",
4
4
  "type": "module",
5
5
  "main": "./dist/crud.js",
6
6
  "types": "./dist/index.d.ts",
7
+ "devDependencies": {
8
+ "vitest": "^4.1.0"
9
+ },
7
10
  "dependencies": {
8
11
  "@tanstack/vue-query": "^5.92.10",
9
12
  "pinia": "^3.0.3",
10
13
  "vue": "^3.5.30",
11
14
  "vue-router": "^5.0.3",
12
- "@kine-design/core": "0.0.1-beta.7",
13
- "kine-ui": "0.0.1-beta.15"
15
+ "@kine-design/core": "0.0.1-beta.8",
16
+ "kine-ui": "0.0.1-beta.17"
14
17
  },
15
18
  "publishConfig": {
16
19
  "access": "public",
@@ -19,7 +22,8 @@
19
22
  ]
20
23
  },
21
24
  "scripts": {
22
- "build": "vite build --config vite.config.build.ts"
25
+ "build": "vite build --config vite.config.build.ts",
26
+ "test": "vitest --config vitest.config.ts --run"
23
27
  },
24
28
  "module": "./dist/crud.js",
25
29
  "exports": {
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @description @kine-design/crud 单元测试配置
3
+ * @author 阿怪
4
+ * @date 2026/5/7
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+
10
+ import { defineConfig } from 'vitest/config';
11
+
12
+ export default defineConfig({
13
+ root: __dirname,
14
+ test: {
15
+ include: ['**/__tests__/*.test.ts'],
16
+ },
17
+ });