@melt-js/libs-vben 0.0.0-stage → 2026.11.8-2

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 (174) hide show
  1. package/LICENSE +10 -0
  2. package/README.md +86 -2
  3. package/package.json +98 -3
  4. package/src/access/access-control.vue +47 -0
  5. package/src/access/accessible.ts +236 -0
  6. package/src/access/directive.ts +42 -0
  7. package/src/access/index.ts +4 -0
  8. package/src/access/use-access.ts +53 -0
  9. package/src/composables/index.ts +14 -0
  10. package/src/composables/use-is-mobile.ts +7 -0
  11. package/src/composables/use-layout-style.ts +51 -0
  12. package/src/composables/use-layout-viewport-height.ts +71 -0
  13. package/src/composables/use-namespace.ts +106 -0
  14. package/src/composables/use-priority-value.ts +96 -0
  15. package/src/composables/use-scroll-lock.ts +151 -0
  16. package/src/composables/use-simple-locale/README.md +3 -0
  17. package/src/composables/use-simple-locale/index.ts +27 -0
  18. package/src/composables/use-simple-locale/messages.ts +31 -0
  19. package/src/composables/use-sortable.ts +32 -0
  20. package/src/constants/core.ts +76 -0
  21. package/src/constants/index.ts +2 -0
  22. package/src/core-icons/create-icon.ts +14 -0
  23. package/src/core-icons/index.ts +11 -0
  24. package/src/core-icons/lucide.ts +95 -0
  25. package/src/core-preferences/config.ts +172 -0
  26. package/src/core-preferences/constants.ts +116 -0
  27. package/src/core-preferences/index.ts +24 -0
  28. package/src/core-preferences/preferences.ts +503 -0
  29. package/src/core-preferences/types.ts +471 -0
  30. package/src/core-preferences/update-css-variables.ts +129 -0
  31. package/src/core-preferences/use-preferences.ts +282 -0
  32. package/src/design/css/global.css +1 -0
  33. package/src/design/css/nprogress.css +61 -0
  34. package/src/design/css/transition.css +238 -0
  35. package/src/design/css/ui.css +101 -0
  36. package/src/design/design-tokens/dark.css +446 -0
  37. package/src/design/design-tokens/default.css +491 -0
  38. package/src/design/design-tokens/index.ts +2 -0
  39. package/src/design/index.ts +6 -0
  40. package/src/design/scss-bem/bem.scss +34 -0
  41. package/src/design/scss-bem/constants.scss +5 -0
  42. package/src/hooks/index.ts +9 -0
  43. package/src/hooks/use-app-config.ts +39 -0
  44. package/src/hooks/use-content-maximize.ts +24 -0
  45. package/src/hooks/use-design-tokens.ts +421 -0
  46. package/src/hooks/use-hover-toggle.ts +163 -0
  47. package/src/hooks/use-pagination.ts +72 -0
  48. package/src/hooks/use-refresh.ts +16 -0
  49. package/src/hooks/use-tabs.ts +143 -0
  50. package/src/hooks/use-watermark.ts +84 -0
  51. package/src/icons/iconify/index.ts +5 -0
  52. package/src/icons/icons/empty-icon.vue +27 -0
  53. package/src/icons/index.ts +3 -0
  54. package/src/icons/svg/icons/antdv-logo.svg +29 -0
  55. package/src/icons/svg/icons/antdv-next-logo.svg +1 -0
  56. package/src/icons/svg/icons/avatar-1.svg +1 -0
  57. package/src/icons/svg/icons/avatar-2.svg +1 -0
  58. package/src/icons/svg/icons/avatar-3.svg +1 -0
  59. package/src/icons/svg/icons/avatar-4.svg +1 -0
  60. package/src/icons/svg/icons/bell.svg +1 -0
  61. package/src/icons/svg/icons/cake.svg +1 -0
  62. package/src/icons/svg/icons/card.svg +1 -0
  63. package/src/icons/svg/icons/dingding.svg +1 -0
  64. package/src/icons/svg/icons/download.svg +1 -0
  65. package/src/icons/svg/icons/github.svg +1 -0
  66. package/src/icons/svg/icons/google.svg +1 -0
  67. package/src/icons/svg/icons/qqchat.svg +1 -0
  68. package/src/icons/svg/icons/tdesign-logo.svg +39 -0
  69. package/src/icons/svg/icons/wechat.svg +1 -0
  70. package/src/icons/svg/index.ts +39 -0
  71. package/src/icons/svg/load.ts +81 -0
  72. package/src/locales/i18n.ts +147 -0
  73. package/src/locales/index.ts +30 -0
  74. package/src/locales/langs/en-US/authentication.json +65 -0
  75. package/src/locales/langs/en-US/common.json +27 -0
  76. package/src/locales/langs/en-US/preferences.json +245 -0
  77. package/src/locales/langs/en-US/profile.json +4 -0
  78. package/src/locales/langs/en-US/ui.json +165 -0
  79. package/src/locales/langs/zh-CN/authentication.json +65 -0
  80. package/src/locales/langs/zh-CN/common.json +27 -0
  81. package/src/locales/langs/zh-CN/preferences.json +245 -0
  82. package/src/locales/langs/zh-CN/profile.json +4 -0
  83. package/src/locales/langs/zh-CN/ui.json +165 -0
  84. package/src/locales/langs/zh-TW/authentication.json +65 -0
  85. package/src/locales/langs/zh-TW/common.json +27 -0
  86. package/src/locales/langs/zh-TW/preferences.json +245 -0
  87. package/src/locales/langs/zh-TW/profile.json +4 -0
  88. package/src/locales/langs/zh-TW/ui.json +165 -0
  89. package/src/locales/typing.ts +27 -0
  90. package/src/preferences/index.ts +42 -0
  91. package/src/request/index.ts +2 -0
  92. package/src/request/request-client/index.ts +3 -0
  93. package/src/request/request-client/modules/downloader.ts +60 -0
  94. package/src/request/request-client/modules/interceptor.ts +40 -0
  95. package/src/request/request-client/modules/sse.ts +134 -0
  96. package/src/request/request-client/modules/uploader.ts +79 -0
  97. package/src/request/request-client/preset-interceptors.ts +178 -0
  98. package/src/request/request-client/request-client.ts +163 -0
  99. package/src/request/request-client/types.ts +90 -0
  100. package/src/shared/cache/README.md +418 -0
  101. package/src/shared/cache/index.ts +5 -0
  102. package/src/shared/cache/indexeddb-driver.ts +137 -0
  103. package/src/shared/cache/local-storage-driver.ts +70 -0
  104. package/src/shared/cache/memory-storage-driver.ts +32 -0
  105. package/src/shared/cache/storage-manager.ts +146 -0
  106. package/src/shared/cache/types.ts +39 -0
  107. package/src/shared/color/color.ts +9 -0
  108. package/src/shared/color/convert.ts +62 -0
  109. package/src/shared/color/generator.ts +45 -0
  110. package/src/shared/color/index.ts +3 -0
  111. package/src/shared/constants/globals.ts +20 -0
  112. package/src/shared/constants/index.ts +2 -0
  113. package/src/shared/constants/vben.ts +30 -0
  114. package/src/shared/global-state.ts +45 -0
  115. package/src/shared/store.ts +1 -0
  116. package/src/shared/utils/cn.ts +10 -0
  117. package/src/shared/utils/date.ts +80 -0
  118. package/src/shared/utils/diff.ts +114 -0
  119. package/src/shared/utils/dom.ts +126 -0
  120. package/src/shared/utils/download.ts +186 -0
  121. package/src/shared/utils/error.ts +19 -0
  122. package/src/shared/utils/index.ts +22 -0
  123. package/src/shared/utils/inference.ts +164 -0
  124. package/src/shared/utils/letter.ts +47 -0
  125. package/src/shared/utils/merge.ts +11 -0
  126. package/src/shared/utils/nprogress.ts +52 -0
  127. package/src/shared/utils/resources.ts +21 -0
  128. package/src/shared/utils/stack.ts +103 -0
  129. package/src/shared/utils/state-handler.ts +61 -0
  130. package/src/shared/utils/to.ts +21 -0
  131. package/src/shared/utils/tree.ts +159 -0
  132. package/src/shared/utils/unique.ts +15 -0
  133. package/src/shared/utils/update-css-variables.ts +47 -0
  134. package/src/shared/utils/util.ts +50 -0
  135. package/src/shared/utils/window.ts +37 -0
  136. package/src/stores/index.ts +3 -0
  137. package/src/stores/modules/access.ts +130 -0
  138. package/src/stores/modules/dict.ts +244 -0
  139. package/src/stores/modules/index.ts +5 -0
  140. package/src/stores/modules/tabbar.ts +797 -0
  141. package/src/stores/modules/timezone.ts +132 -0
  142. package/src/stores/modules/user.ts +42 -0
  143. package/src/stores/setup.ts +79 -0
  144. package/src/stores/shim-pinia.d.ts +11 -0
  145. package/src/styles/antd/index.css +78 -0
  146. package/src/styles/antdv-next/index.css +90 -0
  147. package/src/styles/ele/index.css +44 -0
  148. package/src/styles/global/index.scss +1 -0
  149. package/src/styles/index.ts +1 -0
  150. package/src/styles/naive/index.css +26 -0
  151. package/src/styles/style-exports.d.ts +3 -0
  152. package/src/theme.css +592 -0
  153. package/src/types/global.d.ts +28 -0
  154. package/src/types/index.ts +2 -0
  155. package/src/types/shims.d.ts +1 -0
  156. package/src/types/user.ts +20 -0
  157. package/src/typings/app.d.ts +128 -0
  158. package/src/typings/basic.d.ts +42 -0
  159. package/src/typings/helper.d.ts +147 -0
  160. package/src/typings/index.ts +16 -0
  161. package/src/typings/languages.ts +28 -0
  162. package/src/typings/menu-record.ts +82 -0
  163. package/src/typings/tabs.ts +8 -0
  164. package/src/typings/vue-router.d.ts +169 -0
  165. package/src/utils/helpers/find-menu-by-path.ts +37 -0
  166. package/src/utils/helpers/generate-menus.ts +89 -0
  167. package/src/utils/helpers/generate-routes-backend.ts +108 -0
  168. package/src/utils/helpers/generate-routes-frontend.ts +58 -0
  169. package/src/utils/helpers/get-popup-container.ts +10 -0
  170. package/src/utils/helpers/index.ts +8 -0
  171. package/src/utils/helpers/merge-route-modules.ts +28 -0
  172. package/src/utils/helpers/reset-routes.ts +30 -0
  173. package/src/utils/helpers/unmount-global-loading.ts +31 -0
  174. package/src/utils/index.ts +4 -0
@@ -0,0 +1,421 @@
1
+ import { reactive, watch } from 'vue';
2
+
3
+ import { preferences, usePreferences } from '@melt-js/libs-vben/preferences';
4
+ import { convertToRgb, updateCSSVariables } from '@melt-js/libs-vben/utils';
5
+
6
+ /**
7
+ * 用于适配各个框架的设计系统
8
+ */
9
+
10
+ export function useAntdDesignTokens() {
11
+ const rootStyles = getComputedStyle(document.documentElement);
12
+
13
+ const tokens = reactive({
14
+ borderRadius: '' as any,
15
+ colorBgBase: '',
16
+ colorBgContainer: '',
17
+ colorBgElevated: '',
18
+ colorBgLayout: '',
19
+ colorBgMask: '',
20
+ colorBorder: '',
21
+ colorBorderSecondary: '',
22
+ colorError: '',
23
+ colorInfo: '',
24
+ colorPrimary: '',
25
+ colorSuccess: '',
26
+ colorTextBase: '',
27
+ colorWarning: '',
28
+ zIndexPopupBase: 2000, // 调整基础弹层层级,避免下拉等组件被弹窗或者最大化状态下的表格遮挡
29
+ });
30
+
31
+ const getCssVariableValue = (variable: string, isColor: boolean = true) => {
32
+ const value = rootStyles.getPropertyValue(variable);
33
+ return isColor ? `hsl(${value})` : value;
34
+ };
35
+
36
+ watch(
37
+ () => preferences.theme,
38
+ () => {
39
+ tokens.colorPrimary = getCssVariableValue('--primary');
40
+
41
+ tokens.colorInfo = getCssVariableValue('--primary');
42
+
43
+ tokens.colorError = getCssVariableValue('--destructive');
44
+
45
+ tokens.colorWarning = getCssVariableValue('--warning');
46
+
47
+ tokens.colorSuccess = getCssVariableValue('--success');
48
+
49
+ tokens.colorTextBase = getCssVariableValue('--foreground');
50
+
51
+ getCssVariableValue('--primary-foreground');
52
+
53
+ tokens.colorBorderSecondary = tokens.colorBorder =
54
+ getCssVariableValue('--border');
55
+
56
+ tokens.colorBgElevated = getCssVariableValue('--popover');
57
+
58
+ tokens.colorBgContainer = getCssVariableValue('--card');
59
+
60
+ tokens.colorBgBase = getCssVariableValue('--background');
61
+
62
+ const radius = Number.parseFloat(getCssVariableValue('--radius', false));
63
+ // 1rem = 16px
64
+ tokens.borderRadius = radius * 16;
65
+
66
+ tokens.colorBgLayout = getCssVariableValue('--background-deep');
67
+ tokens.colorBgMask = getCssVariableValue('--overlay');
68
+ },
69
+ { immediate: true },
70
+ );
71
+
72
+ return {
73
+ tokens,
74
+ };
75
+ }
76
+
77
+ export function useNaiveDesignTokens() {
78
+ const rootStyles = getComputedStyle(document.documentElement);
79
+
80
+ const commonTokens = reactive({
81
+ baseColor: '',
82
+ bodyColor: '',
83
+ borderColor: '',
84
+ borderRadius: '',
85
+ cardColor: '',
86
+ dividerColor: '',
87
+ errorColor: '',
88
+ errorColorHover: '',
89
+ errorColorPressed: '',
90
+ errorColorSuppl: '',
91
+ invertedColor: '',
92
+ modalColor: '',
93
+ popoverColor: '',
94
+ primaryColor: '',
95
+ primaryColorHover: '',
96
+ primaryColorPressed: '',
97
+ primaryColorSuppl: '',
98
+ successColor: '',
99
+ successColorHover: '',
100
+ successColorPressed: '',
101
+ successColorSuppl: '',
102
+ tableColor: '',
103
+ textColorBase: '',
104
+ warningColor: '',
105
+ warningColorHover: '',
106
+ warningColorPressed: '',
107
+ warningColorSuppl: '',
108
+ });
109
+
110
+ const getCssVariableValue = (variable: string, isColor: boolean = true) => {
111
+ const value = rootStyles.getPropertyValue(variable);
112
+ return isColor ? convertToRgb(`hsl(${value})`) : value;
113
+ };
114
+
115
+ watch(
116
+ () => preferences.theme,
117
+ () => {
118
+ commonTokens.primaryColor = getCssVariableValue('--primary');
119
+ commonTokens.primaryColorHover = getCssVariableValue('--primary-600');
120
+ commonTokens.primaryColorPressed = getCssVariableValue('--primary-700');
121
+ commonTokens.primaryColorSuppl = getCssVariableValue('--primary-800');
122
+
123
+ commonTokens.errorColor = getCssVariableValue('--destructive');
124
+ commonTokens.errorColorHover = getCssVariableValue('--destructive-600');
125
+ commonTokens.errorColorPressed = getCssVariableValue('--destructive-700');
126
+ commonTokens.errorColorSuppl = getCssVariableValue('--destructive-800');
127
+
128
+ commonTokens.warningColor = getCssVariableValue('--warning');
129
+ commonTokens.warningColorHover = getCssVariableValue('--warning-600');
130
+ commonTokens.warningColorPressed = getCssVariableValue('--warning-700');
131
+ commonTokens.warningColorSuppl = getCssVariableValue('--warning-800');
132
+
133
+ commonTokens.successColor = getCssVariableValue('--success');
134
+ commonTokens.successColorHover = getCssVariableValue('--success-600');
135
+ commonTokens.successColorPressed = getCssVariableValue('--success-700');
136
+ commonTokens.successColorSuppl = getCssVariableValue('--success-800');
137
+
138
+ commonTokens.textColorBase = getCssVariableValue('--foreground');
139
+
140
+ commonTokens.baseColor = getCssVariableValue('--primary-foreground');
141
+
142
+ commonTokens.dividerColor = commonTokens.borderColor =
143
+ getCssVariableValue('--border');
144
+
145
+ commonTokens.modalColor = commonTokens.popoverColor =
146
+ getCssVariableValue('--popover');
147
+
148
+ commonTokens.tableColor = commonTokens.cardColor =
149
+ getCssVariableValue('--card');
150
+
151
+ commonTokens.bodyColor = getCssVariableValue('--background');
152
+ commonTokens.invertedColor = getCssVariableValue('--background-deep');
153
+
154
+ commonTokens.borderRadius = getCssVariableValue('--radius', false);
155
+ },
156
+ { immediate: true },
157
+ );
158
+ return {
159
+ commonTokens,
160
+ };
161
+ }
162
+
163
+ export function useElementPlusDesignTokens() {
164
+ const { isDark } = usePreferences();
165
+ const rootStyles = getComputedStyle(document.documentElement);
166
+
167
+ const getCssVariableValueRaw = (variable: string) => {
168
+ return rootStyles.getPropertyValue(variable);
169
+ };
170
+
171
+ const getCssVariableValue = (variable: string, isColor: boolean = true) => {
172
+ const value = getCssVariableValueRaw(variable);
173
+ return isColor ? convertToRgb(`hsl(${value})`) : value;
174
+ };
175
+
176
+ watch(
177
+ () => preferences.theme,
178
+ () => {
179
+ const background = getCssVariableValue('--background');
180
+ const border = getCssVariableValue('--border');
181
+ const accent = getCssVariableValue('--accent');
182
+
183
+ const variables: Record<string, string> = {
184
+ '--el-bg-color': background,
185
+ '--el-bg-color-overlay': getCssVariableValue('--popover'),
186
+ '--el-bg-color-page': getCssVariableValue('--background-deep'),
187
+ '--el-border-color': border,
188
+ '--el-border-color-dark': border,
189
+ '--el-border-color-extra-light': border,
190
+ '--el-border-color-hover': accent,
191
+ '--el-border-color-light': border,
192
+ '--el-border-color-lighter': border,
193
+
194
+ '--el-border-radius-base': getCssVariableValue('--radius', false),
195
+ '--el-color-danger': getCssVariableValue('--destructive-500'),
196
+ '--el-color-danger-dark-2': isDark.value
197
+ ? getCssVariableValue('--destructive-400')
198
+ : getCssVariableValue('--destructive-600'),
199
+ '--el-color-danger-light-3': isDark.value
200
+ ? getCssVariableValue('--destructive-600')
201
+ : getCssVariableValue('--destructive-400'),
202
+ '--el-color-danger-light-5': isDark.value
203
+ ? getCssVariableValue('--destructive-700')
204
+ : getCssVariableValue('--destructive-300'),
205
+ '--el-color-danger-light-7': isDark.value
206
+ ? getCssVariableValue('--destructive-800')
207
+ : getCssVariableValue('--destructive-200'),
208
+ '--el-color-danger-light-8': isDark.value
209
+ ? getCssVariableValue('--destructive-900')
210
+ : getCssVariableValue('--destructive-100'),
211
+ '--el-color-danger-light-9': isDark.value
212
+ ? getCssVariableValue('--destructive-950')
213
+ : getCssVariableValue('--destructive-50'),
214
+
215
+ '--el-color-error': getCssVariableValue('--destructive-500'),
216
+ '--el-color-error-dark-2': isDark.value
217
+ ? getCssVariableValue('--destructive-400')
218
+ : getCssVariableValue('--destructive-600'),
219
+ '--el-color-error-light-3': isDark.value
220
+ ? getCssVariableValue('--destructive-600')
221
+ : getCssVariableValue('--destructive-400'),
222
+ '--el-color-error-light-5': isDark.value
223
+ ? getCssVariableValue('--destructive-700')
224
+ : getCssVariableValue('--destructive-300'),
225
+ '--el-color-error-light-7': isDark.value
226
+ ? getCssVariableValue('--destructive-800')
227
+ : getCssVariableValue('--destructive-200'),
228
+ '--el-color-error-light-8': isDark.value
229
+ ? getCssVariableValue('--destructive-900')
230
+ : getCssVariableValue('--destructive-100'),
231
+ '--el-color-error-light-9': isDark.value
232
+ ? getCssVariableValue('--destructive-950')
233
+ : getCssVariableValue('--destructive-50'),
234
+
235
+ '--el-color-info-light-5': border,
236
+ '--el-color-info-light-8': border,
237
+ '--el-color-info-light-9': getCssVariableValue('--info'), // getCssVariableValue('--secondary'),
238
+
239
+ '--el-color-primary': getCssVariableValue('--primary-500'),
240
+ '--el-color-primary-dark-2': isDark.value
241
+ ? getCssVariableValue('--primary-400')
242
+ : getCssVariableValue('--primary-600'),
243
+ '--el-color-primary-light-3': isDark.value
244
+ ? getCssVariableValue('--primary-600')
245
+ : getCssVariableValue('--primary-400'),
246
+ '--el-color-primary-light-5': isDark.value
247
+ ? getCssVariableValue('--primary-700')
248
+ : getCssVariableValue('--primary-300'),
249
+ '--el-color-primary-light-7': isDark.value
250
+ ? getCssVariableValue('--primary-800')
251
+ : getCssVariableValue('--primary-200'),
252
+ '--el-color-primary-light-8': isDark.value
253
+ ? getCssVariableValue('--primary-900')
254
+ : getCssVariableValue('--primary-100'),
255
+ '--el-color-primary-light-9': isDark.value
256
+ ? getCssVariableValue('--primary-950')
257
+ : getCssVariableValue('--primary-50'),
258
+
259
+ '--el-color-success': getCssVariableValue('--success-500'),
260
+ '--el-color-success-dark-2': isDark.value
261
+ ? getCssVariableValue('--success-400')
262
+ : getCssVariableValue('--success-600'),
263
+ '--el-color-success-light-3': isDark.value
264
+ ? getCssVariableValue('--success-600')
265
+ : getCssVariableValue('--success-400'),
266
+ '--el-color-success-light-5': isDark.value
267
+ ? getCssVariableValue('--success-700')
268
+ : getCssVariableValue('--success-300'),
269
+ '--el-color-success-light-7': isDark.value
270
+ ? getCssVariableValue('--success-800')
271
+ : getCssVariableValue('--success-200'),
272
+ '--el-color-success-light-8': isDark.value
273
+ ? getCssVariableValue('--success-900')
274
+ : getCssVariableValue('--success-100'),
275
+ '--el-color-success-light-9': isDark.value
276
+ ? getCssVariableValue('--success-950')
277
+ : getCssVariableValue('--success-50'),
278
+
279
+ '--el-color-warning': getCssVariableValue('--warning-500'),
280
+ '--el-color-warning-dark-2': isDark.value
281
+ ? getCssVariableValue('--warning-400')
282
+ : getCssVariableValue('--warning-600'),
283
+ '--el-color-warning-light-3': isDark.value
284
+ ? getCssVariableValue('--warning-600')
285
+ : getCssVariableValue('--warning-400'),
286
+ '--el-color-warning-light-5': isDark.value
287
+ ? getCssVariableValue('--warning-700')
288
+ : getCssVariableValue('--warning-300'),
289
+ '--el-color-warning-light-7': isDark.value
290
+ ? getCssVariableValue('--warning-800')
291
+ : getCssVariableValue('--warning-200'),
292
+ '--el-color-warning-light-8': isDark.value
293
+ ? getCssVariableValue('--warning-900')
294
+ : getCssVariableValue('--warning-100'),
295
+ '--el-color-warning-light-9': isDark.value
296
+ ? getCssVariableValue('--warning-950')
297
+ : getCssVariableValue('--warning-50'),
298
+
299
+ '--el-fill-color': getCssVariableValue('--accent'),
300
+ '--el-fill-color-blank': background,
301
+ '--el-fill-color-light': getCssVariableValue('--accent'),
302
+ '--el-fill-color-lighter': getCssVariableValue('--accent-lighter'),
303
+
304
+ '--el-fill-color-dark': getCssVariableValue('--accent-dark'),
305
+ '--el-fill-color-darker': getCssVariableValue('--accent-darker'),
306
+
307
+ // 解决ElLoading背景色问题
308
+ '--el-mask-color': isDark.value
309
+ ? 'rgba(0,0,0,.8)'
310
+ : 'rgba(255,255,255,.9)',
311
+
312
+ '--el-text-color-primary': getCssVariableValue('--foreground'),
313
+
314
+ '--el-text-color-regular': getCssVariableValue('--foreground'),
315
+ };
316
+
317
+ updateCSSVariables(variables, `__vben_design_styles__`);
318
+ },
319
+ { immediate: true },
320
+ );
321
+ }
322
+
323
+ export function useTDesignDesignTokens() {
324
+ const { isDark } = usePreferences();
325
+ const rootStyles = getComputedStyle(document.documentElement);
326
+
327
+ const getCssVariableValue = (variable: string, isColor: boolean = true) => {
328
+ const value = rootStyles.getPropertyValue(variable);
329
+ return isColor ? convertToRgb(`hsl(${value})`) : value;
330
+ };
331
+
332
+ /**
333
+ * 生成某个语义色对应的 TDesign 变量(语义变体 + 1~10 色阶)。
334
+ * TDesign 的色阶在亮色模式下 1 最浅、10 最深,暗色模式下方向相反,
335
+ * 这里根据当前模式把 Vben 的 50~900 色板映射到对应方向。
336
+ * @param tdName TDesign 颜色名,如 `brand`、`error`
337
+ * @param vbenName Vben 颜色名,如 `primary`、`destructive`
338
+ */
339
+ const getColorTokens = (tdName: string, vbenName: string) => {
340
+ const dark = isDark.value;
341
+ const getColor = (level?: number) =>
342
+ getCssVariableValue(
343
+ level === undefined ? `--${vbenName}` : `--${vbenName}-${level}`,
344
+ );
345
+
346
+ // 亮色模式 1 最浅、10 最深;暗色模式相反
347
+ const lightScale = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
348
+ const scaleLevels = dark ? [...lightScale].toReversed() : lightScale;
349
+ const scale: Record<string, string> = {};
350
+ scaleLevels.forEach((level, index) => {
351
+ scale[`--td-${tdName}-color-${index + 1}`] = getColor(level);
352
+ });
353
+
354
+ return {
355
+ [`--td-${tdName}-color`]: getColor(),
356
+ [`--td-${tdName}-color-active`]: getColor(dark ? 300 : 700),
357
+ [`--td-${tdName}-color-disabled`]: getColor(dark ? 800 : 200),
358
+ [`--td-${tdName}-color-focus`]: getColor(dark ? 900 : 100),
359
+ [`--td-${tdName}-color-hover`]: getColor(dark ? 400 : 600),
360
+ [`--td-${tdName}-color-light`]: getColor(dark ? 950 : 50),
361
+ [`--td-${tdName}-color-light-hover`]: getColor(dark ? 900 : 100),
362
+ ...scale,
363
+ };
364
+ };
365
+
366
+ watch(
367
+ () => preferences.theme,
368
+ () => {
369
+ const variables: Record<string, string> = {
370
+ // 品牌色(主题色)、功能色
371
+ ...getColorTokens('brand', 'primary'),
372
+ ...getColorTokens('error', 'destructive'),
373
+ ...getColorTokens('warning', 'warning'),
374
+ ...getColorTokens('success', 'success'),
375
+
376
+ // 文字颜色
377
+ '--td-text-color-anti': getCssVariableValue('--primary-foreground'),
378
+ '--td-text-color-brand': getCssVariableValue('--primary'),
379
+ '--td-text-color-disabled': getCssVariableValue('--muted-foreground'),
380
+ '--td-text-color-link': getCssVariableValue('--primary'),
381
+ '--td-text-color-placeholder': getCssVariableValue(
382
+ '--input-placeholder',
383
+ ),
384
+ '--td-text-color-primary': getCssVariableValue('--foreground'),
385
+ '--td-text-color-secondary': getCssVariableValue('--muted-foreground'),
386
+
387
+ // 背景颜色
388
+ '--td-bg-color-component': getCssVariableValue('--accent'),
389
+ '--td-bg-color-component-active':
390
+ getCssVariableValue('--accent-darker'),
391
+ '--td-bg-color-component-disabled': getCssVariableValue('--muted'),
392
+ '--td-bg-color-component-hover': getCssVariableValue('--accent-hover'),
393
+ '--td-bg-color-container': getCssVariableValue('--background'),
394
+ '--td-bg-color-container-active':
395
+ getCssVariableValue('--accent-darker'),
396
+ '--td-bg-color-container-hover': getCssVariableValue('--accent'),
397
+ '--td-bg-color-container-select': getCssVariableValue('--accent'),
398
+ '--td-bg-color-page': getCssVariableValue('--background-deep'),
399
+ '--td-bg-color-secondarycontainer': getCssVariableValue('--card'),
400
+
401
+ // 边框颜色
402
+ '--td-border-level-1-color': getCssVariableValue('--border'),
403
+ '--td-border-level-2-color': getCssVariableValue('--border'),
404
+ '--td-component-border': getCssVariableValue('--border'),
405
+ '--td-component-stroke': getCssVariableValue('--border'),
406
+
407
+ // 圆角
408
+ '--td-radius-default': getCssVariableValue('--radius', false),
409
+ };
410
+
411
+ // TDesign 将亮/暗变量定义在 `:root[theme-mode='light'|'dark']` 下,
412
+ // 需要写入相同优先级的选择器才能覆盖默认值
413
+ const selector = isDark.value
414
+ ? `:root[theme-mode='dark']`
415
+ : `:root[theme-mode='light']`;
416
+
417
+ updateCSSVariables(variables, `__vben_design_tdesign_styles__`, selector);
418
+ },
419
+ { immediate: true },
420
+ );
421
+ }
@@ -0,0 +1,163 @@
1
+ import type { Arrayable, MaybeElementRef } from '@vueuse/core';
2
+
3
+ import type { Ref } from 'vue';
4
+
5
+ import { computed, effectScope, ref, unref, watch } from 'vue';
6
+
7
+ import { isFunction } from '@melt-js/libs-vben/utils';
8
+
9
+ import { tryOnScopeDispose, useElementHover } from '@vueuse/core';
10
+
11
+ interface HoverDelayOptions {
12
+ /** 鼠标进入延迟时间 */
13
+ enterDelay?: (() => number) | number;
14
+ /** 鼠标离开延迟时间 */
15
+ leaveDelay?: (() => number) | number;
16
+ }
17
+
18
+ const DEFAULT_LEAVE_DELAY = 500; // 鼠标离开延迟时间,默认为 500ms
19
+ const DEFAULT_ENTER_DELAY = 0; // 鼠标进入延迟时间,默认为 0(立即响应)
20
+
21
+ /**
22
+ * 监测鼠标是否在元素内部,如果在元素内部则返回 true,否则返回 false
23
+ * @param refElement 所有需要检测的元素。支持单个元素、元素数组或响应式引用的元素数组。如果鼠标在任何一个元素内部都会返回 true
24
+ * @param delay 延迟更新状态的时间,可以是数字或包含进入/离开延迟的配置对象
25
+ * @returns 返回一个数组,第一个元素是一个 ref,表示鼠标是否在元素内部,第二个元素是一个控制器,可以通过 enable 和 disable 方法来控制监听器的启用和禁用
26
+ */
27
+ export function useHoverToggle(
28
+ refElement: Arrayable<MaybeElementRef> | Ref<HTMLElement[] | null>,
29
+ delay: (() => number) | HoverDelayOptions | number = DEFAULT_LEAVE_DELAY,
30
+ ) {
31
+ // 兼容旧版本API
32
+ const normalizedOptions: HoverDelayOptions =
33
+ typeof delay === 'number' || isFunction(delay)
34
+ ? { enterDelay: DEFAULT_ENTER_DELAY, leaveDelay: delay }
35
+ : {
36
+ enterDelay: DEFAULT_ENTER_DELAY,
37
+ leaveDelay: DEFAULT_LEAVE_DELAY,
38
+ ...delay,
39
+ };
40
+
41
+ const value = ref(false);
42
+ const enterTimer = ref<ReturnType<typeof setTimeout> | undefined>();
43
+ const leaveTimer = ref<ReturnType<typeof setTimeout> | undefined>();
44
+ const hoverScopes = ref<ReturnType<typeof effectScope>[]>([]);
45
+
46
+ // 使用计算属性包装 refElement,使其响应式变化
47
+ const refs = computed(() => {
48
+ const raw = unref(refElement);
49
+ if (raw === null) return [];
50
+ return Array.isArray(raw) ? raw : [raw];
51
+ });
52
+ // 存储所有 hover 状态
53
+ const isHovers = ref<Array<Ref<boolean>>>([]);
54
+
55
+ // 更新 hover 监听的函数
56
+ function updateHovers() {
57
+ // 停止并清理之前的作用域
58
+ hoverScopes.value.forEach((scope) => scope.stop());
59
+ hoverScopes.value = [];
60
+
61
+ isHovers.value = refs.value.map((refEle) => {
62
+ if (!refEle) {
63
+ return ref(false);
64
+ }
65
+ const eleRef = computed(() => {
66
+ const ele = unref(refEle);
67
+ return ele instanceof Element ? ele : (ele?.$el as Element);
68
+ });
69
+
70
+ // 为每个元素创建独立的作用域
71
+ const scope = effectScope();
72
+ const hoverRef = scope.run(() => useElementHover(eleRef)) || ref(false);
73
+ hoverScopes.value.push(scope);
74
+
75
+ return hoverRef;
76
+ });
77
+ }
78
+
79
+ // 监听元素数量变化,避免过度执行
80
+ const elementsCount = computed(() => {
81
+ const raw = unref(refElement);
82
+ if (raw === null) return 0;
83
+ return Array.isArray(raw) ? raw.length : 1;
84
+ });
85
+
86
+ // 初始设置
87
+ updateHovers();
88
+
89
+ // 只在元素数量变化时重新设置监听器
90
+ const stopWatcher = watch(elementsCount, updateHovers, { deep: false });
91
+
92
+ const isOutsideAll = computed(() => isHovers.value.every((v) => !v.value));
93
+
94
+ function clearTimers() {
95
+ if (enterTimer.value) {
96
+ clearTimeout(enterTimer.value);
97
+ enterTimer.value = undefined;
98
+ }
99
+ if (leaveTimer.value) {
100
+ clearTimeout(leaveTimer.value);
101
+ leaveTimer.value = undefined;
102
+ }
103
+ }
104
+
105
+ function setValueDelay(val: boolean) {
106
+ clearTimers();
107
+
108
+ if (val) {
109
+ // 鼠标进入
110
+ const enterDelay = normalizedOptions.enterDelay ?? DEFAULT_ENTER_DELAY;
111
+ const delayTime = isFunction(enterDelay) ? enterDelay() : enterDelay;
112
+
113
+ if (delayTime <= 0) {
114
+ value.value = true;
115
+ } else {
116
+ enterTimer.value = setTimeout(() => {
117
+ value.value = true;
118
+ enterTimer.value = undefined;
119
+ }, delayTime);
120
+ }
121
+ } else {
122
+ // 鼠标离开
123
+ const leaveDelay = normalizedOptions.leaveDelay ?? DEFAULT_LEAVE_DELAY;
124
+ const delayTime = isFunction(leaveDelay) ? leaveDelay() : leaveDelay;
125
+
126
+ if (delayTime <= 0) {
127
+ value.value = false;
128
+ } else {
129
+ leaveTimer.value = setTimeout(() => {
130
+ value.value = false;
131
+ leaveTimer.value = undefined;
132
+ }, delayTime);
133
+ }
134
+ }
135
+ }
136
+
137
+ const hoverWatcher = watch(
138
+ isOutsideAll,
139
+ (val) => {
140
+ setValueDelay(!val);
141
+ },
142
+ { immediate: true },
143
+ );
144
+
145
+ const controller = {
146
+ enable() {
147
+ hoverWatcher.resume();
148
+ },
149
+ disable() {
150
+ hoverWatcher.pause();
151
+ },
152
+ };
153
+
154
+ tryOnScopeDispose(() => {
155
+ clearTimers();
156
+ // 停止监听器
157
+ stopWatcher();
158
+ // 停止所有剩余的作用域
159
+ hoverScopes.value.forEach((scope) => scope.stop());
160
+ });
161
+
162
+ return [value, controller] as [typeof value, typeof controller];
163
+ }
@@ -0,0 +1,72 @@
1
+ import type { Ref } from 'vue';
2
+
3
+ import { computed, ref, unref, watch } from 'vue';
4
+
5
+ /**
6
+ * Paginates an array of items
7
+ * @param list The array to paginate
8
+ * @param pageNo The current page number (1-based)
9
+ * @param pageSize Number of items per page
10
+ * @returns Paginated array slice
11
+ * @throws {Error} If pageNo or pageSize are invalid
12
+ */
13
+ function pagination<T = any>(list: T[], pageNo: number, pageSize: number): T[] {
14
+ if (pageNo < 1) throw new Error('Page number must be positive');
15
+ if (pageSize < 1) throw new Error('Page size must be positive');
16
+
17
+ const offset = (pageNo - 1) * pageSize;
18
+ const ret =
19
+ offset + pageSize >= list.length
20
+ ? list.slice(offset)
21
+ : list.slice(offset, offset + pageSize);
22
+ return ret;
23
+ }
24
+
25
+ export function usePagination<T = any>(
26
+ list: Ref<T[]>,
27
+ pageSize: number,
28
+ totalChangeToFirstPage = true,
29
+ ) {
30
+ const currentPage = ref(1);
31
+ const pageSizeRef = ref(pageSize);
32
+
33
+ const totalPages = computed(() =>
34
+ Math.ceil(unref(list).length / unref(pageSizeRef)),
35
+ );
36
+
37
+ const paginationList = computed(() => {
38
+ return pagination(unref(list), unref(currentPage), unref(pageSizeRef));
39
+ });
40
+
41
+ const total = computed(() => {
42
+ return unref(list).length;
43
+ });
44
+
45
+ if (totalChangeToFirstPage) {
46
+ watch(total, () => {
47
+ setCurrentPage(1);
48
+ });
49
+ }
50
+
51
+ function setCurrentPage(page: number) {
52
+ if (page === 1 && unref(totalPages) === 0) {
53
+ currentPage.value = 1;
54
+ } else {
55
+ if (page < 1 || page > unref(totalPages)) {
56
+ throw new Error('Invalid page number');
57
+ }
58
+ currentPage.value = page;
59
+ }
60
+ }
61
+
62
+ function setPageSize(pageSize: number) {
63
+ if (pageSize < 1) {
64
+ throw new Error('Page size must be positive');
65
+ }
66
+ pageSizeRef.value = pageSize;
67
+ // Reset to first page to prevent invalid state
68
+ currentPage.value = 1;
69
+ }
70
+
71
+ return { setCurrentPage, total, setPageSize, paginationList, currentPage };
72
+ }
@@ -0,0 +1,16 @@
1
+ import { useRouter } from 'vue-router';
2
+
3
+ import { useTabbarStore } from '@melt-js/libs-vben/stores';
4
+
5
+ export function useRefresh() {
6
+ const router = useRouter();
7
+ const tabbarStore = useTabbarStore();
8
+
9
+ async function refresh() {
10
+ await tabbarStore.refresh(router);
11
+ }
12
+
13
+ return {
14
+ refresh,
15
+ };
16
+ }