@kine-design/crud 0.0.1-beta.2 → 0.0.1-beta.21

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 (118) hide show
  1. package/.vlaude/last-session-id +1 -0
  2. package/components/crudPage/KCrudPage.tsx +178 -0
  3. package/components/crudPage/crudPage.css +64 -0
  4. package/components/crudPage/index.ts +10 -0
  5. package/components/editableTable/KEditableTable.tsx +281 -0
  6. package/components/editableTable/editableTable.css +268 -0
  7. package/components/editableTable/index.ts +10 -0
  8. package/components/formPage/KApprovalDialog.tsx +142 -0
  9. package/components/formPage/KFormCard.tsx +65 -0
  10. package/components/formPage/KFormPage.tsx +128 -0
  11. package/components/formPage/KMasterDetailPage.tsx +205 -0
  12. package/components/formPage/KStickyActionBar.tsx +33 -0
  13. package/components/formPage/formPage.css +629 -0
  14. package/components/formPage/index.ts +14 -0
  15. package/components/layout/KContent.tsx +20 -0
  16. package/components/layout/KHeader.tsx +37 -0
  17. package/components/layout/KLayout.tsx +82 -0
  18. package/components/layout/KSider.tsx +80 -0
  19. package/components/layout/index.ts +18 -0
  20. package/components/layout/layout.css +262 -0
  21. package/components/login/KLoginPage.tsx +129 -0
  22. package/components/login/index.ts +10 -0
  23. package/components/login/login.css +118 -0
  24. package/components/navMenu/KNavMenu.tsx +175 -0
  25. package/components/navMenu/index.ts +2 -0
  26. package/components/navMenu/navMenu.css +197 -0
  27. package/components/pageHeader/KPageHeader.tsx +85 -0
  28. package/components/pageHeader/index.ts +9 -0
  29. package/components/pageHeader/pageHeader.css +93 -0
  30. package/components/searchTable/KSearchTable.tsx +138 -0
  31. package/components/searchTable/index.ts +10 -0
  32. package/components/searchTable/searchTable.css +121 -0
  33. package/components/upload/KFileList.tsx +95 -0
  34. package/components/upload/KImageUpload.tsx +286 -0
  35. package/components/upload/KUpload.tsx +206 -0
  36. package/components/upload/index.ts +13 -0
  37. package/components/upload/types.ts +26 -0
  38. package/components/upload/upload.css +345 -0
  39. package/composables/auth/authGuard.ts +128 -0
  40. package/composables/auth/index.ts +23 -0
  41. package/composables/auth/types.ts +109 -0
  42. package/composables/auth/useAuth.ts +278 -0
  43. package/composables/auth/vCan.ts +95 -0
  44. package/composables/defineRepository.ts +224 -0
  45. package/composables/error/createErrorHandler.ts +46 -0
  46. package/composables/error/defaultFeedbackHandler.ts +76 -0
  47. package/composables/error/dispatchError.ts +70 -0
  48. package/composables/error/index.ts +32 -0
  49. package/composables/error/types.ts +57 -0
  50. package/composables/error/useErrorHandler.ts +41 -0
  51. package/composables/form/index.ts +18 -0
  52. package/composables/form/renderFormField.tsx +119 -0
  53. package/composables/form/types.ts +129 -0
  54. package/composables/form/useFormPage.ts +183 -0
  55. package/composables/index.ts +62 -0
  56. package/composables/page/index.ts +11 -0
  57. package/composables/page/types.ts +62 -0
  58. package/composables/page/useCrudPage.ts +88 -0
  59. package/composables/request/composables.ts +206 -0
  60. package/composables/request/controlGate.ts +143 -0
  61. package/composables/request/createRequest.ts +173 -0
  62. package/composables/request/index.ts +71 -0
  63. package/composables/request/orchestrator.ts +145 -0
  64. package/composables/request/requestBuilder.ts +418 -0
  65. package/composables/request/transport/fetchTransport.ts +79 -0
  66. package/composables/request/transport/xhrTransport.ts +100 -0
  67. package/composables/request/types.ts +226 -0
  68. package/composables/request/upload.ts +146 -0
  69. package/composables/router/createRouterGuard.ts +134 -0
  70. package/composables/router/defineCrudRoutes.ts +116 -0
  71. package/composables/router/index.ts +22 -0
  72. package/composables/router/types.ts +128 -0
  73. package/composables/router/useMenuFromRoutes.ts +109 -0
  74. package/composables/router/useTabStore.ts +183 -0
  75. package/composables/search/index.ts +11 -0
  76. package/composables/search/useAutoCompleteSearch.ts +161 -0
  77. package/composables/setupCrud.ts +43 -0
  78. package/composables/storage/createStorageAdapter.ts +72 -0
  79. package/composables/storage/index.ts +13 -0
  80. package/composables/storage/types.ts +30 -0
  81. package/composables/storage/useStorage.ts +108 -0
  82. package/composables/store/defineUserStore.ts +122 -0
  83. package/composables/store/index.ts +11 -0
  84. package/composables/types.ts +118 -0
  85. package/dist/components/crudPage/KCrudPage.d.ts +14 -0
  86. package/dist/components/crudPage/index.d.ts +9 -0
  87. package/dist/components/editableTable/KEditableTable.d.ts +146 -0
  88. package/dist/components/editableTable/index.d.ts +10 -0
  89. package/dist/components/formPage/KApprovalDialog.d.ts +99 -0
  90. package/dist/components/formPage/KFormCard.d.ts +49 -0
  91. package/dist/components/formPage/KFormPage.d.ts +14 -0
  92. package/dist/components/formPage/KMasterDetailPage.d.ts +14 -0
  93. package/dist/components/formPage/KStickyActionBar.d.ts +16 -0
  94. package/dist/components/formPage/index.d.ts +14 -0
  95. package/dist/components/layout/KLayout.d.ts +7 -4
  96. package/dist/composables/auth/useAuth.d.ts +5 -5
  97. package/dist/composables/error/types.d.ts +2 -1
  98. package/dist/composables/form/index.d.ts +12 -0
  99. package/dist/composables/form/renderFormField.d.ts +11 -0
  100. package/dist/composables/form/types.d.ts +104 -0
  101. package/dist/composables/form/useFormPage.d.ts +38 -0
  102. package/dist/composables/index.d.ts +2 -0
  103. package/dist/composables/page/index.d.ts +10 -0
  104. package/dist/composables/page/types.d.ts +61 -0
  105. package/dist/composables/page/useCrudPage.d.ts +14 -0
  106. package/dist/composables/request/createRequest.d.ts +2 -0
  107. package/dist/composables/request/requestBuilder.d.ts +2 -0
  108. package/dist/composables/search/index.d.ts +10 -0
  109. package/dist/composables/search/useAutoCompleteSearch.d.ts +50 -0
  110. package/dist/crud.css +2499 -663
  111. package/dist/crud.js +11512 -2910
  112. package/dist/index.d.ts +11 -0
  113. package/dist/setup.d.ts +2 -2
  114. package/index.ts +144 -0
  115. package/package.json +20 -19
  116. package/setup.ts +288 -0
  117. package/tsconfig.json +12 -0
  118. package/vite.config.build.ts +52 -0
@@ -0,0 +1,629 @@
1
+ /**
2
+ * @description KFormCard + KStickyActionBar 样式
3
+ * @author 阿怪
4
+ * @date 2026/3/22
5
+ * @version v0.0.1
6
+ */
7
+
8
+ /* ================================================================
9
+ KFormCard
10
+ ================================================================ */
11
+
12
+ .k-form-card {
13
+ background: var(--kine-color-bg-tertiary, #fff);
14
+ border: 1px solid var(--kine-color-border-default, #e5e7eb);
15
+ border-radius: var(--kine-radius-md, 8px);
16
+ margin-bottom: var(--kine-spacing-8);
17
+ }
18
+
19
+ .k-form-card-header {
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: space-between;
23
+ padding: var(--kine-spacing-8) var(--kine-spacing-10);
24
+ gap: var(--kine-spacing-6);
25
+ user-select: none;
26
+ }
27
+
28
+ .k-form-card-header--clickable {
29
+ cursor: pointer;
30
+ }
31
+
32
+ .k-form-card-header--clickable:hover {
33
+ background: var(--kine-color-bg-hover, rgba(0, 0, 0, 0.02));
34
+ border-radius: var(--kine-radius-md, 8px) var(--kine-radius-md, 8px) 0 0;
35
+ }
36
+
37
+ .k-form-card--collapsed .k-form-card-header--clickable:hover {
38
+ border-radius: var(--kine-radius-md, 8px);
39
+ }
40
+
41
+ .k-form-card-header-left {
42
+ display: flex;
43
+ align-items: baseline;
44
+ gap: var(--kine-spacing-4);
45
+ min-width: 0;
46
+ }
47
+
48
+ .k-form-card-title {
49
+ font-size: var(--kine-font-size-xl);
50
+ font-weight: var(--kine-font-weight-semibold);
51
+ color: var(--kine-color-text-primary, #111827);
52
+ margin: 0;
53
+ white-space: nowrap;
54
+ }
55
+
56
+ .k-form-card-subtitle {
57
+ font-size: var(--kine-font-size-md);
58
+ color: var(--kine-color-text-muted, #9ca3af);
59
+ }
60
+
61
+ .k-form-card-header-right {
62
+ display: flex;
63
+ align-items: center;
64
+ gap: var(--kine-spacing-4);
65
+ flex-shrink: 0;
66
+ }
67
+
68
+ .k-form-card-collapse-icon {
69
+ display: inline-flex;
70
+ align-items: center;
71
+ font-size: var(--kine-font-size-2xl);
72
+ color: var(--kine-color-text-muted, #9ca3af);
73
+ transition: transform var(--kine-motion-duration-fast) var(--kine-motion-easing-default);
74
+ transform: rotate(-90deg);
75
+ }
76
+
77
+ .k-form-card-collapse-icon--collapsed {
78
+ transform: rotate(-180deg);
79
+ }
80
+
81
+ .k-form-card-body {
82
+ padding: var(--kine-spacing-10);
83
+ }
84
+
85
+ /* 有 header 时,body 顶部不需要额外间距(header 自带 padding-bottom) */
86
+ .k-form-card-header + .k-form-card-body {
87
+ padding-top: 0;
88
+ }
89
+
90
+ /* ================================================================
91
+ KStickyActionBar
92
+ ================================================================ */
93
+
94
+ .k-sticky-action-bar {
95
+ position: sticky;
96
+ bottom: 0;
97
+ z-index: 10;
98
+ display: flex;
99
+ align-items: center;
100
+ justify-content: flex-end;
101
+ padding: var(--kine-spacing-6) var(--kine-spacing-12);
102
+ background: var(--kine-color-bg-tertiary, #fff);
103
+ border-top: 1px solid var(--kine-color-border-default, #e5e7eb);
104
+ backdrop-filter: blur(8px);
105
+ margin: 0 calc(-1 * var(--kine-spacing-12)) calc(-1 * var(--kine-spacing-10));
106
+ }
107
+
108
+ .k-sticky-action-bar-hint {
109
+ font-size: var(--kine-font-size-md);
110
+ color: var(--kine-color-text-muted, #9ca3af);
111
+ }
112
+
113
+ .k-sticky-action-bar-actions {
114
+ display: flex;
115
+ align-items: center;
116
+ gap: var(--kine-spacing-4);
117
+ }
118
+
119
+ /* ================================================================
120
+ KApprovalDialog
121
+ ================================================================ */
122
+
123
+ .k-approval-overlay {
124
+ position: fixed;
125
+ inset: 0;
126
+ z-index: 1000;
127
+ display: flex;
128
+ align-items: center;
129
+ justify-content: center;
130
+ background: rgba(0, 0, 0, 0.4);
131
+ backdrop-filter: blur(2px);
132
+ }
133
+
134
+ .k-approval-dialog {
135
+ width: 420px;
136
+ max-width: calc(100vw - 48px);
137
+ background: var(--kine-color-bg-tertiary, #fff);
138
+ border-radius: var(--kine-radius-lg, 12px);
139
+ box-shadow: 0 16px 48px rgba(0, 0, 0, 0.12), 0 4px 12px rgba(0, 0, 0, 0.06);
140
+ overflow: hidden;
141
+ }
142
+
143
+ .k-approval-dialog-header {
144
+ display: flex;
145
+ align-items: center;
146
+ justify-content: space-between;
147
+ padding: var(--kine-spacing-10) var(--kine-spacing-12) 0;
148
+ }
149
+
150
+ .k-approval-dialog-title {
151
+ font-size: var(--kine-font-size-2xl);
152
+ font-weight: var(--kine-font-weight-semibold);
153
+ color: var(--kine-color-text-primary, #111827);
154
+ }
155
+
156
+ .k-approval-dialog-close {
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: center;
160
+ width: 28px;
161
+ height: 28px;
162
+ border: none;
163
+ border-radius: var(--kine-radius-sm, 6px);
164
+ background: transparent;
165
+ color: var(--kine-color-text-muted, #9ca3af);
166
+ font-size: 18px; /* 18px 无对应 token,保留 */
167
+ cursor: pointer;
168
+ transition: background var(--kine-motion-duration-fast) var(--kine-motion-easing-default), color var(--kine-motion-duration-fast) var(--kine-motion-easing-default);
169
+ }
170
+
171
+ .k-approval-dialog-close:hover {
172
+ background: var(--kine-color-bg-hover, rgba(0, 0, 0, 0.04));
173
+ color: var(--kine-color-text-primary, #111827);
174
+ }
175
+
176
+ .k-approval-dialog-body {
177
+ padding: var(--kine-spacing-8) var(--kine-spacing-12);
178
+ }
179
+
180
+ .k-approval-dialog-summary {
181
+ padding: var(--kine-spacing-5) var(--kine-spacing-7);
182
+ background: var(--kine-color-bg-primary, #f8f9fb);
183
+ border-radius: var(--kine-radius-sm, 6px);
184
+ font-size: var(--kine-font-size-lg);
185
+ color: var(--kine-color-text-secondary, #6b7280);
186
+ margin-bottom: var(--kine-spacing-6);
187
+ font-variant-numeric: tabular-nums;
188
+ }
189
+
190
+ .k-approval-dialog-desc {
191
+ font-size: var(--kine-font-size-lg);
192
+ color: var(--kine-color-text-secondary, #6b7280);
193
+ line-height: 1.6;
194
+ margin: 0 0 var(--kine-spacing-6);
195
+ }
196
+
197
+ .k-approval-dialog-remark {
198
+ display: block;
199
+ width: 100%;
200
+ padding: var(--kine-spacing-5) var(--kine-spacing-6);
201
+ border: 1px solid var(--kine-color-border-default, #e5e7eb);
202
+ border-radius: var(--kine-radius-sm, 6px);
203
+ font-family: inherit;
204
+ font-size: var(--kine-font-size-lg);
205
+ color: var(--kine-color-text-primary, #111827);
206
+ resize: vertical;
207
+ min-height: 72px;
208
+ transition: border-color var(--kine-motion-duration-fast) var(--kine-motion-easing-default), box-shadow var(--kine-motion-duration-fast) var(--kine-motion-easing-default);
209
+ box-sizing: border-box;
210
+ }
211
+
212
+ .k-approval-dialog-remark::placeholder {
213
+ color: var(--kine-color-text-muted, #9ca3af);
214
+ }
215
+
216
+ .k-approval-dialog-remark:focus {
217
+ outline: none;
218
+ border-color: var(--kine-color-accent-default, #4f46e5);
219
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.06);
220
+ }
221
+
222
+ .k-approval-dialog-remark--required {
223
+ border-color: var(--kine-color-semantic-error, #dc2626);
224
+ }
225
+
226
+ .k-approval-dialog-remark--required:focus {
227
+ border-color: var(--kine-color-semantic-error, #dc2626);
228
+ box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.06);
229
+ }
230
+
231
+ .k-approval-dialog-required-hint {
232
+ display: block;
233
+ font-size: var(--kine-font-size-sm);
234
+ color: var(--kine-color-semantic-error, #dc2626);
235
+ margin-top: var(--kine-spacing-2);
236
+ }
237
+
238
+ .k-approval-dialog-footer {
239
+ display: flex;
240
+ align-items: center;
241
+ justify-content: flex-end;
242
+ gap: var(--kine-spacing-4);
243
+ padding: 0 var(--kine-spacing-12) var(--kine-spacing-10);
244
+ }
245
+
246
+ /* ================================================================
247
+ KFormPage / KMasterDetailPage — 表单页布局
248
+ ================================================================ */
249
+
250
+ /* wrapper: 全宽容器,承载 sticky bar 出血 */
251
+ .k-form-page-wrapper {
252
+ display: flex;
253
+ flex-direction: column;
254
+ min-height: 100%;
255
+ }
256
+
257
+ .k-form-page {
258
+ max-width: 960px;
259
+ width: 100%;
260
+ margin: 0 auto;
261
+ padding: 0 0 var(--kine-spacing-10);
262
+ flex: 1;
263
+ }
264
+
265
+ .k-master-detail-page {
266
+ max-width: 1120px;
267
+ }
268
+
269
+ /* 页头 */
270
+ .k-fp-header {
271
+ display: flex;
272
+ align-items: center;
273
+ justify-content: space-between;
274
+ margin-bottom: var(--kine-spacing-10);
275
+ gap: var(--kine-spacing-6);
276
+ }
277
+
278
+ .k-fp-header-left {
279
+ display: flex;
280
+ align-items: center;
281
+ gap: var(--kine-spacing-5);
282
+ min-width: 0;
283
+ }
284
+
285
+ .k-fp-header-right {
286
+ display: flex;
287
+ align-items: center;
288
+ gap: var(--kine-spacing-4);
289
+ flex-shrink: 0;
290
+ }
291
+
292
+ .k-fp-title {
293
+ font-size: var(--kine-font-size-3xl);
294
+ font-weight: 650;
295
+ color: var(--kine-color-text-primary, #111827);
296
+ margin: 0;
297
+ letter-spacing: -0.3px;
298
+ }
299
+
300
+ /* 面包屑 */
301
+ .k-fp-breadcrumb {
302
+ display: flex;
303
+ align-items: center;
304
+ gap: var(--kine-spacing-2);
305
+ font-size: var(--kine-font-size-lg);
306
+ }
307
+
308
+ .k-fp-breadcrumb-sep {
309
+ color: var(--kine-color-text-muted, #9ca3af);
310
+ margin: 0 var(--kine-spacing-1);
311
+ }
312
+
313
+ .k-fp-breadcrumb-link {
314
+ color: var(--kine-color-text-secondary, #6b7280);
315
+ cursor: pointer;
316
+ text-decoration: none;
317
+ transition: color var(--kine-motion-duration-fast) var(--kine-motion-easing-default);
318
+ }
319
+
320
+ .k-fp-breadcrumb-link:hover {
321
+ color: var(--kine-color-accent-default, #4f46e5);
322
+ }
323
+
324
+ .k-fp-breadcrumb-text {
325
+ color: var(--kine-color-text-primary, #111827);
326
+ font-weight: var(--kine-font-weight-medium);
327
+ }
328
+
329
+ /* 字段网格 */
330
+ .k-fp-grid {
331
+ display: grid;
332
+ gap: var(--kine-spacing-8) var(--kine-spacing-10);
333
+ }
334
+
335
+ .k-fp-grid--1 { grid-template-columns: 1fr; }
336
+ .k-fp-grid--2 { grid-template-columns: repeat(2, 1fr); }
337
+ .k-fp-grid--3 { grid-template-columns: repeat(3, 1fr); }
338
+
339
+ .k-fp-field {
340
+ display: flex;
341
+ flex-direction: column;
342
+ gap: var(--kine-spacing-2);
343
+ }
344
+
345
+ .k-fp-label {
346
+ font-size: var(--kine-font-size-md);
347
+ font-weight: var(--kine-font-weight-medium);
348
+ color: var(--kine-color-text-secondary, #6b7280);
349
+ text-transform: uppercase;
350
+ letter-spacing: 0.03em;
351
+ }
352
+
353
+ .k-fp-required {
354
+ color: var(--kine-color-semantic-error, #dc2626);
355
+ margin-left: var(--kine-spacing-1);
356
+ }
357
+
358
+ /* 输入框 */
359
+ .k-fp-input {
360
+ height: 34px;
361
+ padding: 0 var(--kine-spacing-5);
362
+ border: 1px solid var(--kine-color-border-default, #e5e7eb);
363
+ border-radius: var(--kine-radius-sm, 6px);
364
+ font-size: var(--kine-font-size-lg);
365
+ font-family: inherit;
366
+ color: var(--kine-color-text-primary, #111827);
367
+ background: var(--kine-color-bg-tertiary, #fff);
368
+ transition: border-color var(--kine-motion-duration-fast) var(--kine-motion-easing-default), box-shadow var(--kine-motion-duration-fast) var(--kine-motion-easing-default);
369
+ box-sizing: border-box;
370
+ width: 100%;
371
+ }
372
+
373
+ .k-fp-input:focus {
374
+ outline: none;
375
+ border-color: var(--kine-color-accent-default, #4f46e5);
376
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.06);
377
+ }
378
+
379
+ .k-fp-input::placeholder {
380
+ color: var(--kine-color-text-muted, #9ca3af);
381
+ }
382
+
383
+ .k-fp-input--error {
384
+ border-color: var(--kine-color-semantic-error, #dc2626);
385
+ }
386
+
387
+ .k-fp-input--error:focus {
388
+ box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.06);
389
+ }
390
+
391
+ .k-fp-input--disabled {
392
+ background: var(--kine-color-bg-inset, #f3f4f6);
393
+ color: var(--kine-color-text-muted, #9ca3af);
394
+ cursor: not-allowed;
395
+ }
396
+
397
+ .k-fp-input--readonly {
398
+ background: transparent;
399
+ border-color: transparent;
400
+ padding-left: 0;
401
+ }
402
+
403
+ /* textarea */
404
+ .k-fp-textarea {
405
+ height: auto;
406
+ min-height: 72px;
407
+ padding: var(--kine-spacing-4) var(--kine-spacing-5);
408
+ resize: vertical;
409
+ }
410
+
411
+ /* number input wrapper with prefix/suffix */
412
+ .k-fp-input-wrapper {
413
+ display: flex;
414
+ align-items: center;
415
+ border: 1px solid var(--kine-color-border-default, #e5e7eb);
416
+ border-radius: var(--kine-radius-sm, 6px);
417
+ overflow: hidden;
418
+ background: var(--kine-color-bg-tertiary, #fff);
419
+ transition: border-color var(--kine-motion-duration-fast) var(--kine-motion-easing-default), box-shadow var(--kine-motion-duration-fast) var(--kine-motion-easing-default);
420
+ }
421
+
422
+ .k-fp-input-wrapper:focus-within {
423
+ border-color: var(--kine-color-accent-default, #4f46e5);
424
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.06);
425
+ }
426
+
427
+ .k-fp-input-wrapper .k-fp-input {
428
+ border: none;
429
+ box-shadow: none;
430
+ background: transparent;
431
+ }
432
+
433
+ .k-fp-input-wrapper .k-fp-input:focus {
434
+ box-shadow: none;
435
+ }
436
+
437
+ .k-fp-input-prefix,
438
+ .k-fp-input-suffix {
439
+ display: flex;
440
+ align-items: center;
441
+ padding: 0 var(--kine-spacing-4);
442
+ font-size: var(--kine-font-size-md);
443
+ font-weight: var(--kine-font-weight-medium);
444
+ color: var(--kine-color-text-muted, #9ca3af);
445
+ background: var(--kine-color-bg-inset, #f3f4f6);
446
+ white-space: nowrap;
447
+ height: 32px;
448
+ }
449
+
450
+ /* switch */
451
+ .k-fp-switch {
452
+ position: relative;
453
+ display: inline-flex;
454
+ cursor: pointer;
455
+ }
456
+
457
+ .k-fp-switch input {
458
+ position: absolute;
459
+ opacity: 0;
460
+ width: 0;
461
+ height: 0;
462
+ }
463
+
464
+ .k-fp-switch-track {
465
+ display: inline-block;
466
+ width: 36px;
467
+ height: 20px;
468
+ border-radius: 10px;
469
+ background: var(--kine-color-border-default, #d1d5db);
470
+ transition: background var(--kine-motion-duration-fast) var(--kine-motion-easing-default);
471
+ position: relative;
472
+ }
473
+
474
+ .k-fp-switch-track::after {
475
+ content: '';
476
+ position: absolute;
477
+ top: var(--kine-spacing-1);
478
+ left: var(--kine-spacing-1);
479
+ width: 16px;
480
+ height: 16px;
481
+ border-radius: 50%;
482
+ background: #fff;
483
+ transition: transform var(--kine-motion-duration-fast) var(--kine-motion-easing-default);
484
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
485
+ }
486
+
487
+ .k-fp-switch input:checked + .k-fp-switch-track {
488
+ background: var(--kine-color-accent-default, #4f46e5);
489
+ }
490
+
491
+ .k-fp-switch input:checked + .k-fp-switch-track::after {
492
+ transform: translateX(16px);
493
+ }
494
+
495
+ /* 错误提示 */
496
+ .k-fp-error {
497
+ font-size: var(--kine-font-size-sm);
498
+ color: var(--kine-color-semantic-error, #dc2626);
499
+ margin-top: var(--kine-spacing-1);
500
+ }
501
+
502
+ /* 加载中 */
503
+ .k-fp-loading {
504
+ padding: 64px var(--kine-spacing-12);
505
+ text-align: center;
506
+ font-size: var(--kine-font-size-lg);
507
+ color: var(--kine-color-text-muted, #9ca3af);
508
+ }
509
+
510
+ /* ================================================================
511
+ Dark Mode
512
+ ================================================================ */
513
+
514
+ @media (prefers-color-scheme: dark) {
515
+ .k-form-card {
516
+ background: var(--kine-color-bg-tertiary, #1e1e2e);
517
+ border-color: var(--kine-color-border-default, #2e2e3e);
518
+ }
519
+
520
+ .k-form-card-title {
521
+ color: var(--kine-color-text-primary, #e5e5e5);
522
+ }
523
+
524
+ .k-form-card-header--clickable:hover {
525
+ background: var(--kine-color-bg-hover, rgba(255, 255, 255, 0.04));
526
+ }
527
+
528
+ .k-sticky-action-bar {
529
+ background: var(--kine-color-bg-tertiary, #1e1e2e);
530
+ border-top-color: var(--kine-color-border-default, #2e2e3e);
531
+ }
532
+
533
+ .k-approval-overlay {
534
+ background: rgba(0, 0, 0, 0.6);
535
+ }
536
+
537
+ .k-approval-dialog {
538
+ background: var(--kine-color-bg-tertiary, #1e1e2e);
539
+ box-shadow: 0 16px 48px rgba(0, 0, 0, 0.4);
540
+ }
541
+
542
+ .k-approval-dialog-title {
543
+ color: var(--kine-color-text-primary, #e5e5e5);
544
+ }
545
+
546
+ .k-approval-dialog-summary {
547
+ background: var(--kine-color-bg-primary, #161625);
548
+ }
549
+
550
+ .k-approval-dialog-remark {
551
+ background: var(--kine-color-bg-primary, #161625);
552
+ border-color: var(--kine-color-border-default, #2e2e3e);
553
+ color: var(--kine-color-text-primary, #e5e5e5);
554
+ }
555
+
556
+ }
557
+
558
+ /* 同时支持 class 切换暗色模式 */
559
+ .dark .k-form-card,
560
+ [data-theme="dark"] .k-form-card {
561
+ background: var(--kine-color-bg-tertiary, #1e1e2e);
562
+ border-color: var(--kine-color-border-default, #2e2e3e);
563
+ }
564
+
565
+ .dark .k-form-card-title,
566
+ [data-theme="dark"] .k-form-card-title {
567
+ color: var(--kine-color-text-primary, #e5e5e5);
568
+ }
569
+
570
+ .dark .k-sticky-action-bar,
571
+ [data-theme="dark"] .k-sticky-action-bar {
572
+ background: var(--kine-color-bg-tertiary, #1e1e2e);
573
+ border-top-color: var(--kine-color-border-default, #2e2e3e);
574
+ }
575
+
576
+ .dark .k-approval-dialog,
577
+ [data-theme="dark"] .k-approval-dialog {
578
+ background: var(--kine-color-bg-tertiary, #1e1e2e);
579
+ }
580
+
581
+ .dark .k-approval-dialog-remark,
582
+ [data-theme="dark"] .k-approval-dialog-remark {
583
+ background: var(--kine-color-bg-primary, #161625);
584
+ border-color: var(--kine-color-border-default, #2e2e3e);
585
+ color: var(--kine-color-text-primary, #e5e5e5);
586
+ }
587
+
588
+ /* ================================================================
589
+ 移动端(<768px)
590
+ ================================================================ */
591
+
592
+ @media (max-width: 767px) {
593
+ .k-form-page {
594
+ padding: 0 0 var(--kine-spacing-6);
595
+ }
596
+
597
+ /* 多列网格在移动端强制单列 */
598
+ .k-fp-grid--2,
599
+ .k-fp-grid--3 {
600
+ grid-template-columns: 1fr;
601
+ }
602
+
603
+ /* 跨列字段在单列模式下不需要 span */
604
+ .k-fp-field[style*="grid-column"] {
605
+ grid-column: span 1 !important;
606
+ }
607
+
608
+ .k-form-card-body {
609
+ padding: var(--kine-spacing-6);
610
+ }
611
+
612
+ .k-form-card-header {
613
+ padding: var(--kine-spacing-6) var(--kine-spacing-6);
614
+ }
615
+
616
+ .k-fp-header {
617
+ margin-bottom: var(--kine-spacing-6);
618
+ }
619
+
620
+ .k-sticky-action-bar {
621
+ padding: var(--kine-spacing-4) var(--kine-spacing-6);
622
+ margin: 0 calc(-1 * var(--kine-spacing-6)) calc(-1 * var(--kine-spacing-6));
623
+ }
624
+
625
+ .k-approval-dialog {
626
+ width: calc(100vw - 32px);
627
+ }
628
+ }
629
+
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @description formPage components barrel export
3
+ * @author 阿怪
4
+ * @date 2026/3/22
5
+ * @version v0.0.1
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ export { default as KFormCard } from './KFormCard';
10
+ export { default as KStickyActionBar } from './KStickyActionBar';
11
+ export { default as KApprovalDialog } from './KApprovalDialog';
12
+ export type { ApprovalType } from './KApprovalDialog';
13
+ export { default as KFormPage } from './KFormPage';
14
+ export { default as KMasterDetailPage } from './KMasterDetailPage';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @description KContent 主内容区组件
3
+ * @author 阿怪
4
+ * @date 2026/2/26
5
+ * @version v0.0.1
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { defineComponent } from 'vue';
10
+
11
+ export default defineComponent({
12
+ name: 'KContent',
13
+ setup(_, ctx) {
14
+ return () => (
15
+ <main class="k-crud-content">
16
+ {ctx.slots.default?.()}
17
+ </main>
18
+ );
19
+ },
20
+ });
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @description KHeader 顶部栏组件
3
+ * @author 阿怪
4
+ * @date 2026/2/26
5
+ * @version v0.0.1
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { defineComponent, inject } from 'vue';
10
+ import { LAYOUT_TOGGLE_DRAWER_KEY } from './KLayout';
11
+
12
+ export default defineComponent({
13
+ name: 'KHeader',
14
+ setup(_, ctx) {
15
+ const toggleDrawer = inject(LAYOUT_TOGGLE_DRAWER_KEY, () => {});
16
+
17
+ return () => (
18
+ <header class="k-crud-header">
19
+ {/* 汉堡按钮(移动端可见,桌面端 CSS 隐藏) */}
20
+ <button class="k-crud-header-hamburger" onClick={toggleDrawer}>
21
+ <span class="k-crud-header-hamburger-icon" />
22
+ </button>
23
+
24
+ {/* 左侧/默认内容区(面包屑等) */}
25
+ <div class="k-crud-header-main">
26
+ {ctx.slots.default?.()}
27
+ </div>
28
+ {/* 右侧操作区(用户头像等) */}
29
+ {ctx.slots.extra && (
30
+ <div class="k-crud-header-extra">
31
+ {ctx.slots.extra()}
32
+ </div>
33
+ )}
34
+ </header>
35
+ );
36
+ },
37
+ });