@zfqh/uview-x 0.0.0-stage → 0.0.1

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 (93) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +21 -0
  3. package/README.md +127 -3
  4. package/components/uv-badge/props.ts +83 -0
  5. package/components/uv-badge/uv-badge.vue +186 -0
  6. package/components/uv-button/props.ts +148 -0
  7. package/components/uv-button/uv-button.vue +565 -0
  8. package/components/uv-checkbox/props.ts +83 -0
  9. package/components/uv-checkbox/uv-checkbox.vue +403 -0
  10. package/components/uv-checkbox-group/props.ts +97 -0
  11. package/components/uv-checkbox-group/uv-checkbox-group.vue +163 -0
  12. package/components/uv-code/props.ts +48 -0
  13. package/components/uv-code/uv-code.vue +156 -0
  14. package/components/uv-dialog/props.ts +56 -0
  15. package/components/uv-dialog/uv-dialog.vue +492 -0
  16. package/components/uv-form/props.ts +59 -0
  17. package/components/uv-form/uv-form.vue +256 -0
  18. package/components/uv-form/valid.ts +1344 -0
  19. package/components/uv-form-item/props.ts +62 -0
  20. package/components/uv-form-item/uv-form-item.vue +335 -0
  21. package/components/uv-icon/icons.ts +161 -0
  22. package/components/uv-icon/props.ts +91 -0
  23. package/components/uv-icon/uv-icon.vue +204 -0
  24. package/components/uv-icon/uvicons.ttf +0 -0
  25. package/components/uv-input/props.ts +185 -0
  26. package/components/uv-input/uv-input.vue +431 -0
  27. package/components/uv-line/props.ts +42 -0
  28. package/components/uv-line/uv-line.vue +75 -0
  29. package/components/uv-line-progress/props.ts +43 -0
  30. package/components/uv-line-progress/uv-line-progress.vue +166 -0
  31. package/components/uv-loading-icon/props.ts +78 -0
  32. package/components/uv-loading-icon/uv-loading-icon.vue +226 -0
  33. package/components/uv-overlay/props.ts +38 -0
  34. package/components/uv-overlay/uv-overlay.vue +120 -0
  35. package/components/uv-popup/props.ts +101 -0
  36. package/components/uv-popup/uv-popup.vue +371 -0
  37. package/components/uv-safe-bottom/props.ts +18 -0
  38. package/components/uv-safe-bottom/uv-safe-bottom.vue +120 -0
  39. package/components/uv-status-bar/props.ts +22 -0
  40. package/components/uv-status-bar/uv-status-bar.vue +121 -0
  41. package/components/uv-tabs/props.ts +84 -0
  42. package/components/uv-tabs/uv-tabs.vue +432 -0
  43. package/components/uv-transition/props.ts +38 -0
  44. package/components/uv-transition/uv-transition.vue +170 -0
  45. package/hooks/index.ts +5 -0
  46. package/hooks/useDebounce.ts +35 -0
  47. package/hooks/useI18n.ts +95 -0
  48. package/hooks/useThrottle.ts +38 -0
  49. package/index.ts +23 -0
  50. package/libs/dayjs/index.ts +217 -0
  51. package/libs/index.ts +4 -0
  52. package/libs/luch-request/adapters/index.ts +133 -0
  53. package/libs/luch-request/core/InterceptorManager.ts +52 -0
  54. package/libs/luch-request/core/Request.ts +202 -0
  55. package/libs/luch-request/core/buildFullPath.ts +21 -0
  56. package/libs/luch-request/core/defaults.ts +34 -0
  57. package/libs/luch-request/core/dispatchRequest.ts +7 -0
  58. package/libs/luch-request/core/mergeConfig.ts +127 -0
  59. package/libs/luch-request/core/settle.ts +17 -0
  60. package/libs/luch-request/helpers/buildURL.ts +65 -0
  61. package/libs/luch-request/helpers/combineURLs.ts +15 -0
  62. package/libs/luch-request/helpers/isAbsoluteURL.ts +15 -0
  63. package/libs/luch-request/index.d.ts +197 -0
  64. package/libs/luch-request/index.ts +3 -0
  65. package/libs/luch-request/utils/clone.ts +265 -0
  66. package/libs/luch-request/utils.ts +136 -0
  67. package/locales/en.ts +78 -0
  68. package/locales/index.ts +169 -0
  69. package/locales/zh-Hans.ts +78 -0
  70. package/package.json +42 -4
  71. package/styles/color.scss +33 -0
  72. package/styles/common.scss +134 -0
  73. package/styles/components.scss +19 -0
  74. package/styles/index.scss +7 -0
  75. package/styles/theme.scss +44 -0
  76. package/styles/var.module.scss +32 -0
  77. package/styles/vue.scss +39 -0
  78. package/theme.scss +2 -0
  79. package/types/index.d.ts +66 -0
  80. package/utils/colorGradient.ts +139 -0
  81. package/utils/compat.ts +165 -0
  82. package/utils/compress.ts +34 -0
  83. package/utils/config.ts +95 -0
  84. package/utils/debounce.ts +7 -0
  85. package/utils/digit.ts +157 -0
  86. package/utils/function.ts +771 -0
  87. package/utils/http.ts +5 -0
  88. package/utils/index.ts +57 -0
  89. package/utils/locale.ts +296 -0
  90. package/utils/platform.ts +71 -0
  91. package/utils/route.ts +142 -0
  92. package/utils/test.ts +256 -0
  93. package/utils/throttle.ts +7 -0
package/utils/http.ts ADDED
@@ -0,0 +1,5 @@
1
+ import Request from '../libs/luch-request'
2
+
3
+ const http = new Request()
4
+
5
+ export default http
package/utils/index.ts ADDED
@@ -0,0 +1,57 @@
1
+ import * as utils from './function';
2
+ import * as test from './test';
3
+ import * as colorGradientModule from './colorGradient';
4
+ import { route } from './route';
5
+ import config from './config';
6
+ import http from './http';
7
+ import compress from './compress';
8
+ import platform from './platform';
9
+ import debounce from './debounce';
10
+ import throttle from './throttle';
11
+
12
+ /**
13
+ * uview-x 工具聚合对象。
14
+ *
15
+ * - 组件通过 `import { $uv } from '../../utils'` 使用,模板中沿用 `$uv.addStyle()` 等写法;
16
+ * - 包入口(`index.ts`)通过 `export * from './utils'` 统一出口,并负责全局注入:
17
+ * 入口把 `$uv` 挂到 `uni` 全局对象,`install` 内再挂载到 `app.config.globalProperties.$uv`;
18
+ * - 组件/工具内部仍必须显式具名导入,不依赖全局对象;
19
+ * - 单位读取 `$uv.config.unit`,运行时修改 `config.unit` 后立即生效。
20
+ */
21
+ export const $uv = {
22
+ route,
23
+ config,
24
+ test,
25
+ compress,
26
+ date: utils.timeFormat,
27
+ ...utils,
28
+ colorGradient: colorGradientModule.colorGradient,
29
+ hexToRgb: colorGradientModule.hexToRgb,
30
+ rgbToHex: colorGradientModule.rgbToHex,
31
+ colorToRgba: colorGradientModule.colorToRgba,
32
+ http,
33
+ debounce,
34
+ throttle,
35
+ platform,
36
+ };
37
+
38
+ // 保持原有入口的具名导出(命名空间形式)
39
+ export { utils, test, colorGradientModule, route, config, http, compress, platform, debounce, throttle };
40
+
41
+ // 所有工具方法扁平导出,可直接从 `@zfqh/uview-x` 具名导入
42
+ export * from './function';
43
+ export * from './test';
44
+ export * from './digit';
45
+ export * from './compat';
46
+ // i18n 只对外暴露 4 个公共 API,其余 helper / 内部状态不导出(见 utils/locale.ts)
47
+ export { createI18n, getLocale, getLocaleMessage, setLocale } from './locale';
48
+ export { colorGradient, hexToRgb, rgbToHex, colorToRgba } from './colorGradient';
49
+ // `utils/function.ts` 与 `utils/test.ts` 都存在 `range`:入口取主工具实现,
50
+ // `test.range` 仍可通过 `test` 命名空间或深引用 `utils/test.ts` 使用。
51
+ export { range } from './function';
52
+
53
+ // 对外类型:工具配置、路由、压缩与平台
54
+ export type { UviewXConfig } from './config';
55
+ export type { CompressOptions } from './compress';
56
+ export type { RouteFunction, RouteOptions, RouteParams, RouteType } from './route';
57
+ export type { UviewXPlatform } from './platform';
@@ -0,0 +1,296 @@
1
+ import { ref } from 'vue';
2
+ import type { Ref } from 'vue';
3
+ import { createI18n as _createI18n } from 'vue-i18n';
4
+ import type { I18n, I18nOptions } from 'vue-i18n';
5
+ import { getBuiltinMessage, mergeComponentMessages } from '../locales';
6
+
7
+ /**
8
+ * 国际化能力(组件内置语言包 + 4 个公共 API)。
9
+ *
10
+ * 公共 API(由 `utils/index.ts` 导出到包入口):
11
+ * `createI18n`、`getLocale`、`getLocaleMessage`、`setLocale`。
12
+ *
13
+ * 其余导出(`localeState`、`getI18nGlobal`、`getI18nMessages`、`appTranslate`、`unwrapI18nValue`
14
+ * 与 `I18nMessageValue` / `I18nMessages` / `I18nGlobalLike`)是包内实现的 internal helper,
15
+ * 供 components / hooks / locales 深引用,不进入 barrel 与 `types/index.d.ts`。
16
+ */
17
+
18
+ /** i18n 消息树:值为字符串,或继续嵌套的对象 */
19
+ export type I18nMessageValue = string | { [key: string]: I18nMessageValue };
20
+
21
+ /** 语言包:语言 → 消息树 */
22
+ export type I18nMessages = Record<string, I18nMessageValue>;
23
+
24
+ /** 兼容 vue-i18n legacy / composition 两种 global 形态的运行时视图 */
25
+ export interface I18nGlobalLike {
26
+ t: (key: string, values?: Record<string, string | number>) => string;
27
+ locale: string | { value: string };
28
+ messages: I18nMessages | { value: I18nMessages };
29
+ }
30
+
31
+ interface RefLike<T> {
32
+ value: T;
33
+ }
34
+
35
+ /**
36
+ * `uni` 全局对象的最小运行时视图(含 i18n 实例挂载点)。
37
+ *
38
+ * 对外 `$i18n` 声明在 `types/index.d.ts`;这里再留一份最小结构,保证其他包深引用
39
+ * `@zfqh/uview-x/utils/locale`(不会加载本包 types 入口)时仍能编译。
40
+ * 纯 Node / 单测环境可能没有 `uni` 全局,所有访问都要容错。
41
+ */
42
+ interface UniWithI18n {
43
+ $i18n?: I18n;
44
+ getStorageSync?: (key: string) => unknown;
45
+ setStorageSync?: (key: string, value: unknown) => void;
46
+ getLocale?: () => string;
47
+ setLocale?: (locale: string) => void;
48
+ getAppBaseInfo?: () => { appLanguage?: string; language?: string };
49
+ }
50
+
51
+ /** 读取 uni 全局对象;不存在时返回 undefined */
52
+ function getUni(): UniWithI18n | undefined {
53
+ return typeof uni === 'undefined' ? undefined : (uni as unknown as UniWithI18n);
54
+ }
55
+
56
+ /** 读取挂到 `uni.$i18n` 上的 i18n 实例 */
57
+ function getUniI18n(): I18n | undefined {
58
+ const uniApi = getUni();
59
+ return uniApi ? uniApi.$i18n : undefined;
60
+ }
61
+
62
+ /** 语言持久化 key:App 端切换语言可能触发重启,必须先落盘 */
63
+ const STORAGE_KEY = 'UNI_LOCALE';
64
+
65
+ /** 回退语言(内部使用,不对外导出) */
66
+ const fallbackLocale = 'zh-Hans';
67
+
68
+ /** 读取上次选择的语言;storage 不可用或无有效值时返回空字符串 */
69
+ function readStorageLocale(): string {
70
+ const uniApi = getUni();
71
+ if (uniApi && typeof uniApi.getStorageSync === 'function') {
72
+ try {
73
+ const stored: unknown = uniApi.getStorageSync(STORAGE_KEY);
74
+ if (typeof stored === 'string' && stored) {
75
+ return stored;
76
+ }
77
+ } catch (e) {
78
+ // storage 不可用时忽略,继续走系统语言
79
+ }
80
+ }
81
+ // #ifdef WEB || H5
82
+ // H5 nvue 页面上下文可能拿到非共享的 uni 存储视图,浏览器 localStorage 仍为同一来源
83
+ if (typeof localStorage !== 'undefined') {
84
+ try {
85
+ const stored = localStorage.getItem(STORAGE_KEY);
86
+ if (stored) {
87
+ return stored;
88
+ }
89
+ } catch (e) {
90
+ // localStorage 不可用时忽略
91
+ }
92
+ }
93
+ // #endif
94
+ return '';
95
+ }
96
+
97
+ /**
98
+ * 获取当前语言。
99
+ *
100
+ * 顺序:
101
+ * 1. 本地存储 `UNI_LOCALE`(用户上次通过 `setLocale` 选择的语言);
102
+ * 2. `uni.getLocale()`(H5 / 小程序 / 经典 App);
103
+ * 3. `uni.getAppBaseInfo().appLanguage`(uni-app x App);
104
+ * 4. `uni.getAppBaseInfo().language`(经典端已废弃字段,兜底);
105
+ * 5. `zh-Hans`。
106
+ */
107
+ export function getLocale(): string {
108
+ const stored = readStorageLocale();
109
+ if (stored) {
110
+ return stored;
111
+ }
112
+ const uniApi = getUni();
113
+ if (uniApi && typeof uniApi.getLocale === 'function') {
114
+ const systemLocale = uniApi.getLocale();
115
+ if (systemLocale) {
116
+ return systemLocale;
117
+ }
118
+ }
119
+ if (uniApi && typeof uniApi.getAppBaseInfo === 'function') {
120
+ const info = uniApi.getAppBaseInfo();
121
+ const systemLocale = info.appLanguage || info.language;
122
+ if (systemLocale) {
123
+ return systemLocale;
124
+ }
125
+ }
126
+ return fallbackLocale;
127
+ }
128
+
129
+ /** 初始语言(内部使用,不对外导出) */
130
+ const defaultLocale: string = getLocale();
131
+
132
+ /**
133
+ * 当前语言的内部响应式状态(internal,不进 barrel)。
134
+ *
135
+ * - 无 i18n 实例时是 `appTranslate` / `useI18n` 的唯一语言来源;
136
+ * - 创建实例时用 `opts.locale` 初始化,`setLocale` 时同步更新,保证语言切换后组件重新渲染。
137
+ */
138
+ export const localeState: Ref<string> = ref<string>(defaultLocale);
139
+
140
+ /** 兼容 vue-i18n legacy(字符串)与 composition(Ref)两种模式,取出实际值 */
141
+ export function unwrapI18nValue<T>(value: T | { value: T }): T {
142
+ if (value !== null && typeof value === 'object' && 'value' in value) {
143
+ return (value as RefLike<T>).value;
144
+ }
145
+ return value as T;
146
+ }
147
+
148
+ function isI18nGlobalLike(value: unknown): value is I18nGlobalLike {
149
+ if (!value || typeof value !== 'object') {
150
+ return false;
151
+ }
152
+ return typeof (value as I18nGlobalLike).t === 'function';
153
+ }
154
+
155
+ /** 当前 `uni.$i18n.global` 的运行时视图;实例不存在时返回 undefined */
156
+ export function getI18nGlobal(): I18nGlobalLike | undefined {
157
+ const global = getUniI18n()?.global;
158
+ return isI18nGlobalLike(global) ? global : undefined;
159
+ }
160
+
161
+ /** APP 下不支持动态变量,需要重写占位符替换 */
162
+ function replacePlaceholders(str: string, values?: Record<string, string | number>): string {
163
+ if (!values || typeof str !== 'string') {
164
+ return str;
165
+ }
166
+ let out = str;
167
+ for (const k of Object.keys(values)) {
168
+ out = out.split(`{${k}}`).join(String(values[k]));
169
+ }
170
+ return out;
171
+ }
172
+
173
+ /** 按 `a.b.c` 路径从语言包中取消息 */
174
+ function getMessage(messages: I18nMessages, locale: string, key: string): string | undefined {
175
+ const segments = String(key).split('.');
176
+ let current: I18nMessageValue | undefined = messages[locale];
177
+ for (const segment of segments) {
178
+ if (current == null || typeof current === 'string') {
179
+ return undefined;
180
+ }
181
+ current = current[segment];
182
+ }
183
+ return typeof current === 'string' ? current : undefined;
184
+ }
185
+
186
+ /**
187
+ * 翻译(App / 小程序 / 无 i18n 实例时的统一实现)。
188
+ *
189
+ * locale 优先级:storage `UNI_LOCALE` → `uni.$i18n.global.locale` → 内部 `localeState` → `zh-Hans`;
190
+ * 消息优先级:全局语言包(当前语言 → 回退语言)→ 组件内置语言包(当前语言 → 回退语言)→ key。
191
+ * storage 优先用于 nvue 等页面上下文模块副本不同、实例未共享的场景(setLocale 已先落盘)。
192
+ */
193
+ export function appTranslate(key: string, values?: Record<string, string | number>): string {
194
+ const global = getI18nGlobal();
195
+ const messages = unwrapI18nValue(global?.messages) || {};
196
+ const globalLocale = String(unwrapI18nValue(global?.locale) || '');
197
+ const locale = readStorageLocale() || globalLocale || localeState.value || fallbackLocale;
198
+ const message = getMessage(messages, locale, key) || getMessage(messages, fallbackLocale, key);
199
+ if (message) {
200
+ return replacePlaceholders(message, values);
201
+ }
202
+ const builtin = getBuiltinMessage(locale, key, values) || getBuiltinMessage(fallbackLocale, key, values);
203
+ return builtin || key;
204
+ }
205
+
206
+ /**
207
+ * 创建 i18n 实例并挂到 `uni.$i18n`。
208
+ *
209
+ * - 自动把组件内置语言包合并进 `opts.messages`(用户同名 key 优先);
210
+ * - App 端不依赖 vue-i18n 注入,直接把 `global.t` 替换为包内 `appTranslate`;
211
+ * - 用 `opts.locale` 初始化内部 `localeState`,无实例时也能保持语言一致。
212
+ */
213
+ export function createI18n(opts: I18nOptions): I18n {
214
+ const mergedMessages: I18nMessages = mergeComponentMessages(opts.messages as I18nMessages | undefined);
215
+ const mergedOptions: I18nOptions = {
216
+ ...opts,
217
+ // #ifdef WEB || H5
218
+ // vue-i18n 9.1.9 的 globalInjection 默认 false;H5 开启后 app.use(i18n) 会注入
219
+ // `app.config.globalProperties.$i18n`,useI18n 才能检测到实例并走 vue-i18n 分支
220
+ //(可显式传 globalInjection: false 关闭;App / 小程序不需要,保持 vue-i18n 默认)
221
+ globalInjection: opts.globalInjection !== false,
222
+ // #endif
223
+ messages: mergedMessages,
224
+ } as unknown as I18nOptions;
225
+ const i18n: I18n = _createI18n(mergedOptions);
226
+ // #ifdef APP
227
+ i18n.global.t = appTranslate as unknown as typeof i18n.global.t;
228
+ // #endif
229
+ const uniApi = getUni();
230
+ if (uniApi) {
231
+ uniApi.$i18n = i18n;
232
+ }
233
+ if (typeof opts.locale === 'string' && opts.locale) {
234
+ localeState.value = opts.locale;
235
+ }
236
+ return i18n;
237
+ }
238
+
239
+ /** 当前语言包(兼容 composition 模式的 Ref;internal,不进 barrel) */
240
+ export function getI18nMessages(): I18nMessages {
241
+ const messages = unwrapI18nValue(getI18nGlobal()?.messages);
242
+ return messages || {};
243
+ }
244
+
245
+ /**
246
+ * 设置当前语言。
247
+ *
248
+ * 顺序(关键,防止 App 端重启后丢失语言):
249
+ * 1. 更新内部 `localeState`;
250
+ * 2. 更新项目自己的 `uni.$i18n.global.locale`(兼容 WritableComputedRef 写 `.value` / legacy 字符串);
251
+ * 3. 写入 storage 的 `UNI_LOCALE`;
252
+ * 4. 最后才调用 `uni.setLocale`(App 端可能引起重启,此时前两步已落盘)。
253
+ *
254
+ * 注意:`useI18n()` 返回的 `locale` 的 setter 只更新 `global.locale` / `localeState`,不会调用本函数;
255
+ * 需要持久化 / 通知系统时请显式调用 `setLocale`。
256
+ */
257
+ export function setLocale(locale: string): void {
258
+ localeState.value = locale;
259
+ const global = getI18nGlobal();
260
+ if (global) {
261
+ try {
262
+ const target: unknown = global.locale;
263
+ if (target && typeof target === 'object' && 'value' in target) {
264
+ (target as RefLike<string>).value = locale;
265
+ } else {
266
+ global.locale = locale;
267
+ }
268
+ } catch (e) {
269
+ // 实例不可写时忽略,继续持久化
270
+ }
271
+ }
272
+ const uniApi = getUni();
273
+ if (uniApi && typeof uniApi.setStorageSync === 'function') {
274
+ try {
275
+ uniApi.setStorageSync(STORAGE_KEY, locale);
276
+ } catch (e) {
277
+ // storage 不可用时忽略
278
+ }
279
+ }
280
+ if (uniApi && typeof uniApi.setLocale === 'function') {
281
+ try {
282
+ uniApi.setLocale(locale);
283
+ } catch (e) {
284
+ // App 端 setLocale 可能触发重启 / 平台不支持,忽略
285
+ }
286
+ }
287
+ }
288
+
289
+ /**
290
+ * 按当前语言取词(公共 API)。
291
+ * 统一走 `appTranslate`(优先读 `uni.$i18n` 语言包,其次组件内置语言包),
292
+ * 避免小程序端 vue-i18n runtime-only 构建缺少消息编译器导致翻译空白 / 占位符不插值。
293
+ */
294
+ export function getLocaleMessage(key: string, values?: Record<string, string | number>): string {
295
+ return appTranslate(key, values);
296
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * 运行平台判断(uni-app x 蒸汽模式 / H5 / 小程序)。
3
+ *
4
+ * 仅保留 x 支持的平台条件;旧版 VUE2、APP-PLUS、APP-NVUE、H5 宏不再使用。
5
+ */
6
+ export type UviewXPlatform =
7
+ | 'none'
8
+ | 'vue3'
9
+ | 'app'
10
+ | 'app-nvue'
11
+ | 'h5'
12
+ | 'mp'
13
+ | 'weixin'
14
+ | 'alipay'
15
+ | 'baidu'
16
+ | 'toutiao'
17
+ | 'qq'
18
+ | 'kuaishou'
19
+ | '360'
20
+
21
+ let platform: UviewXPlatform = 'none'
22
+
23
+ // #ifdef VUE3
24
+ platform = 'vue3'
25
+ // #endif
26
+
27
+ // #ifdef APP
28
+ platform = 'app'
29
+ // #endif
30
+
31
+ // #ifdef APP-NVUE
32
+ platform = 'app-nvue'
33
+ // #endif
34
+
35
+ // #ifdef WEB || H5
36
+ platform = 'h5'
37
+ // #endif
38
+
39
+ // #ifdef MP
40
+ platform = 'mp'
41
+ // #endif
42
+
43
+ // #ifdef MP-WEIXIN
44
+ platform = 'weixin'
45
+ // #endif
46
+
47
+ // #ifdef MP-ALIPAY
48
+ platform = 'alipay'
49
+ // #endif
50
+
51
+ // #ifdef MP-BAIDU
52
+ platform = 'baidu'
53
+ // #endif
54
+
55
+ // #ifdef MP-TOUTIAO
56
+ platform = 'toutiao'
57
+ // #endif
58
+
59
+ // #ifdef MP-QQ
60
+ platform = 'qq'
61
+ // #endif
62
+
63
+ // #ifdef MP-KUAISHOU
64
+ platform = 'kuaishou'
65
+ // #endif
66
+
67
+ // #ifdef MP-360
68
+ platform = '360'
69
+ // #endif
70
+
71
+ export default platform
package/utils/route.ts ADDED
@@ -0,0 +1,142 @@
1
+ /**
2
+ * 路由跳转方法,该方法相对于直接使用 uni.xxx 更简单快捷,
3
+ * 并支持路由拦截。
4
+ */
5
+ import { queryParams, deepMerge, page } from './function'
6
+
7
+ export type RouteType =
8
+ | 'navigateTo'
9
+ | 'to'
10
+ | 'redirectTo'
11
+ | 'redirect'
12
+ | 'switchTab'
13
+ | 'tab'
14
+ | 'reLaunch'
15
+ | 'launch'
16
+ | 'navigateBack'
17
+ | 'back'
18
+
19
+ export interface RouteParams {
20
+ [key: string]: any
21
+ intercept?: boolean | ((config: RouteOptions, resolve: (next: boolean) => void) => void)
22
+ }
23
+
24
+ export interface RouteOptions {
25
+ type?: RouteType
26
+ url?: string
27
+ delta?: number
28
+ params?: RouteParams
29
+ animationType?: string
30
+ animationDuration?: number
31
+ intercept?: boolean | ((config: RouteOptions, resolve: (next: boolean) => void) => void)
32
+ events?: Record<string, (...args: any[]) => void>
33
+ }
34
+
35
+ export type RouteFunction = (
36
+ options?: string | RouteOptions,
37
+ params?: RouteParams
38
+ ) => Promise<void>
39
+
40
+ type RouterConfig = Required<Pick<RouteOptions,
41
+ 'type' | 'url' | 'delta' | 'params' | 'animationType' | 'animationDuration' | 'intercept' | 'events'>>
42
+
43
+ class Router {
44
+ config: RouterConfig
45
+
46
+ constructor() {
47
+ this.config = {
48
+ type: 'navigateTo',
49
+ url: '',
50
+ delta: 1,
51
+ params: {},
52
+ animationType: 'pop-in',
53
+ animationDuration: 300,
54
+ intercept: false,
55
+ events: {}
56
+ }
57
+ // route 需要作为独立方法传递,先绑定 this
58
+ this.route = this.route.bind(this)
59
+ }
60
+
61
+ // 判断 url 前面是否有 "/",如果没有则加上,否则无法跳转
62
+ addRootPath(url: string): string {
63
+ return url[0] === '/' ? url : `/${url}`
64
+ }
65
+
66
+ // 整合路由参数
67
+ mixinParam(url: string, params: RouteParams): string {
68
+ url = url && this.addRootPath(url)
69
+
70
+ let query = ''
71
+ if (/.*\/.*\?.*=.*/.test(url)) {
72
+ query = queryParams(params, false)
73
+ return url += `&${query}`
74
+ }
75
+ query = queryParams(params)
76
+ return url += query
77
+ }
78
+
79
+ // 对外的方法名称
80
+ async route(options: string | RouteOptions = {}, params: RouteParams = {}): Promise<void> {
81
+ let mergeConfig: RouteOptions = {}
82
+
83
+ if (typeof options === 'string') {
84
+ mergeConfig.url = this.mixinParam(options, params)
85
+ mergeConfig.type = 'navigateTo'
86
+ } else {
87
+ mergeConfig = deepMerge(this.config as RouteOptions, options)
88
+ mergeConfig.url = this.mixinParam(options.url as string, options.params as RouteParams)
89
+ }
90
+ if (mergeConfig.url === page()) return
91
+
92
+ if (params.intercept) {
93
+ mergeConfig.intercept = params.intercept
94
+ }
95
+ mergeConfig.params = params
96
+ mergeConfig = deepMerge(this.config as RouteOptions, mergeConfig)
97
+
98
+ if (typeof mergeConfig.intercept === 'function') {
99
+ const interceptor = mergeConfig.intercept
100
+ const isNext = await new Promise<boolean>((resolve) => {
101
+ interceptor(mergeConfig, resolve)
102
+ })
103
+ isNext && this.openPage(mergeConfig)
104
+ } else {
105
+ this.openPage(mergeConfig)
106
+ }
107
+ }
108
+
109
+ // 执行路由跳转
110
+ openPage(config: RouteOptions): void {
111
+ const {
112
+ url = '',
113
+ type,
114
+ delta,
115
+ animationType,
116
+ animationDuration,
117
+ events
118
+ } = config
119
+ if (type == 'navigateTo' || type == 'to') {
120
+ uni.navigateTo({
121
+ url,
122
+ animationType: animationType as any,
123
+ animationDuration,
124
+ events: events as any
125
+ })
126
+ }
127
+ if (type == 'redirectTo' || type == 'redirect') {
128
+ uni.redirectTo({ url })
129
+ }
130
+ if (type == 'switchTab' || type == 'tab') {
131
+ uni.switchTab({ url })
132
+ }
133
+ if (type == 'reLaunch' || type == 'launch') {
134
+ uni.reLaunch({ url })
135
+ }
136
+ if (type == 'navigateBack' || type == 'back') {
137
+ uni.navigateBack({ delta })
138
+ }
139
+ }
140
+ }
141
+
142
+ export const route: RouteFunction = (new Router()).route