uview-pro 0.5.4 → 0.5.5

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.
package/changelog.md CHANGED
@@ -1,24 +1,40 @@
1
- ## 0.5.4(2026-01-30
2
-
3
- ### 🐛 Bug Fixes | Bug 修复
4
-
5
- - **types:** 优化组件库类型定义和代码健壮性 ([e83eac8](https://github.com/anyup/uView-Pro/commit/e83eac8192476dea5e2d4e5b8b3c68b7da992673))
6
- - **u-navbar:** 修复状态栏高度获取的条件判断,区分鸿蒙、微信小程序和其他 ([b13d112](https://github.com/anyup/uView-Pro/commit/b13d112be80c46f62bfa343a089f2de70a00f774))
1
+ ## 0.5.5(2026-02-02
7
2
 
8
3
  ### ✨ Features | 新功能
9
4
 
10
- - **u-loading:** 增强u-loading的自定义样式功能 ([c9151ce](https://github.com/anyup/uView-Pro/commit/c9151ce4bee44c762bd7970ab280184c730e97b1))
11
- - **u-toast:** 新增函数式调用API和全局toast功能(#101) ([b8c8fbf](https://github.com/anyup/uView-Pro/commit/b8c8fbff70c14fe0a6106794a638e450cecbeddf))
12
- - **components:** 优化组件类型安全 ([02b638f](https://github.com/anyup/uView-Pro/commit/02b638f6e251c9b52e5e3ebb1e40d644d895308a))
5
+ - **u-toast:** 新增页面级toast功能并优化loading逻辑 ([4eae5b7](https://github.com/anyup/uView-Pro/commit/4eae5b7ef5d4494c4619bee383e62490994b4317))
6
+ - **useModal:** 新增useModal函数式调用API和全局modal功能(#101) ([724edf3](https://github.com/anyup/uView-Pro/commit/724edf3cd69607a6d4e33954f8d2d701f9502ff3))
7
+ - **locale:** 新增语言配置的标签字段label和区域设置字段locale ([49ba6cb](https://github.com/anyup/uView-Pro/commit/49ba6cbea6b53712496e465dd40654af7608a02d))
13
8
 
14
9
  ### ♻️ Code Refactoring | 代码重构
15
10
 
16
- - **demo:** 移除多余的toast引用 ([67b2677](https://github.com/anyup/uView-Pro/commit/67b26772eb67e9e2b830c95359688b0116395452))
11
+ - **demo:** 调整demo演示中吸顶组件默认偏移量为200 ([165da80](https://github.com/anyup/uView-Pro/commit/165da80eb9eec54cf1a5fa8511d82e2b3d11fed1))
17
12
 
18
13
  ### 👥 Contributors
19
14
 
20
15
  <a href="https://github.com/anyup"><img src="https://github.com/anyup.png?size=40" width="40" height="40" alt="anyup" title="anyup"/></a>
21
16
 
17
+ ## 0.5.4(2026-01-30)
18
+
19
+ ### 🐛 Bug Fixes | Bug 修复
20
+
21
+ - **types:** 优化组件库类型定义和代码健壮性 ([e83eac8](https://github.com/anyup/uView-Pro/commit/e83eac8192476dea5e2d4e5b8b3c68b7da992673))
22
+ - **u-navbar:** 修复状态栏高度获取的条件判断,区分鸿蒙、微信小程序和其他 ([b13d112](https://github.com/anyup/uView-Pro/commit/b13d112be80c46f62bfa343a089f2de70a00f774))
23
+
24
+ ### ✨ Features | 新功能
25
+
26
+ - **u-loading:** 增强u-loading的自定义样式功能 ([c9151ce](https://github.com/anyup/uView-Pro/commit/c9151ce4bee44c762bd7970ab280184c730e97b1))
27
+ - **u-toast:** 新增函数式调用API和全局toast功能(#101) ([b8c8fbf](https://github.com/anyup/uView-Pro/commit/b8c8fbff70c14fe0a6106794a638e450cecbeddf))
28
+ - **components:** 优化组件类型安全 ([02b638f](https://github.com/anyup/uView-Pro/commit/02b638f6e251c9b52e5e3ebb1e40d644d895308a))
29
+
30
+ ### ♻️ Code Refactoring | 代码重构
31
+
32
+ - **demo:** 移除多余的toast引用 ([67b2677](https://github.com/anyup/uView-Pro/commit/67b26772eb67e9e2b830c95359688b0116395452))
33
+
34
+ ### 👥 Contributors
35
+
36
+ <a href="https://github.com/anyup"><img src="https://github.com/anyup.png?size=40" width="40" height="40" alt="anyup" title="anyup"/></a>
37
+
22
38
  ## 0.5.3(2026-01-26)
23
39
 
24
40
  ### ♻️ Code Refactoring | 代码重构
@@ -0,0 +1,29 @@
1
+ /**
2
+ * u-modal 函数式调用事件名(全平台)
3
+ * @description
4
+ * - useModal() 通过 uni.$emit 派发事件
5
+ * - <u-modal /> 通过 uni.$on 监听事件并转调自身 show/hide
6
+ */
7
+
8
+ import type { ModalProps } from './types';
9
+
10
+ // 普通(页面级)modal 事件
11
+ export const U_MODAL_EVENT_SHOW = 'uview-pro:u-modal:show';
12
+ export const U_MODAL_EVENT_HIDE = 'uview-pro:u-modal:hide';
13
+ export const U_MODAL_EVENT_CLEAR_LOADING = 'uview-pro:u-modal:clear-loading';
14
+
15
+ // 全局(App 根部)modal 事件,供 useModal() 使用
16
+ export const U_MODAL_GLOBAL_EVENT_SHOW = 'uview-pro:u-modal:global:show';
17
+ export const U_MODAL_GLOBAL_EVENT_HIDE = 'uview-pro:u-modal:global:hide';
18
+ export const U_MODAL_GLOBAL_EVENT_CLEAR_LOADING = 'uview-pro:u-modal:global:clear-loading';
19
+
20
+ /**
21
+ * u-modal 函数式调用载荷类型
22
+ * @description 完整覆盖 u-modal 的所有 props(除 modelValue 外)
23
+ */
24
+ export type ModalPayload = Partial<Omit<ModalProps, 'modelValue'>> & {
25
+ /** 确认回调 */
26
+ onConfirm?: () => void;
27
+ /** 取消回调 */
28
+ onCancel?: () => void;
29
+ };
@@ -33,7 +33,7 @@ export const ModalProps = {
33
33
  /** 弹窗内容 */
34
34
  content: {
35
35
  type: String,
36
- default: () => t('uModal.content')
36
+ default: ''
37
37
  },
38
38
  /** 是否显示标题 */
39
39
  showTitle: {
@@ -114,6 +114,16 @@ export const ModalProps = {
114
114
  negativeTop: {
115
115
  type: [String, Number] as PropType<number | string>,
116
116
  default: 0
117
+ },
118
+ /** 是否作为全局根部 modal(通常放在 App.vue 中,给 useModal() 使用) */
119
+ global: {
120
+ type: Boolean,
121
+ default: false
122
+ },
123
+ /** 是否作为页面级 modal(通常放在页面中,给 useModal({ page: true }) 使用) */
124
+ page: {
125
+ type: Boolean,
126
+ default: false
117
127
  }
118
128
  };
119
129
 
@@ -1,50 +1,61 @@
1
1
  <template>
2
2
  <view>
3
3
  <u-popup
4
- :zoom="zoom"
4
+ v-model="popupValue"
5
5
  mode="center"
6
+ :zoom="effectiveConfig.zoom"
6
7
  :popup="false"
7
8
  :z-index="uZIndex"
8
- v-model="popupValue"
9
- :length="width"
10
- :mask-close-able="maskCloseAble"
11
- :border-radius="borderRadius"
9
+ :length="effectiveConfig.width"
10
+ :mask-close-able="effectiveConfig.maskCloseAble"
11
+ :border-radius="effectiveConfig.borderRadius"
12
+ :negative-top="effectiveConfig.negativeTop"
13
+ :custom-class="effectiveConfig.customClass"
12
14
  @close="popupClose"
13
- :negative-top="negativeTop"
14
- :custom-class="customClass"
15
15
  >
16
- <view class="u-model" :style="$u.toStyle(customStyle)">
17
- <view v-if="showTitle" class="u-model__title u-line-1" :style="[titleStyle]">{{ title }}</view>
16
+ <view class="u-model" :style="$u.toStyle(effectiveConfig.customStyle)">
17
+ <view
18
+ v-if="effectiveConfig.showTitle"
19
+ class="u-model__title u-line-1"
20
+ :style="$u.toStyle(effectiveConfig.titleStyle)"
21
+ >
22
+ {{ effectiveConfig.title }}
23
+ </view>
18
24
  <view class="u-model__content">
19
- <view :style="[contentStyle]" v-if="slots.default">
25
+ <view v-if="slots.default" :style="$u.toStyle(effectiveConfig.contentStyle)">
20
26
  <slot />
21
27
  </view>
22
- <view v-else class="u-model__content__message" :style="[contentStyle]">{{ content }}</view>
28
+ <view v-else class="u-model__content__message" :style="$u.toStyle(effectiveConfig.contentStyle)">
29
+ {{ effectiveConfig.content }}
30
+ </view>
23
31
  </view>
24
- <view class="u-model__footer u-border-top" v-if="showCancelButton || showConfirmButton">
32
+ <view
33
+ v-if="effectiveConfig.showCancelButton || effectiveConfig.showConfirmButton"
34
+ class="u-model__footer u-border-top"
35
+ >
25
36
  <view
26
- v-if="showCancelButton"
37
+ v-if="effectiveConfig.showCancelButton"
27
38
  :hover-stay-time="100"
28
39
  hover-class="u-model__btn--hover"
29
40
  class="u-model__footer__button"
30
- :style="[cancelBtnStyle]"
41
+ :style="$u.toStyle(cancelBtnStyle)"
31
42
  @tap="cancel"
32
43
  >
33
- {{ cancelText }}
44
+ {{ effectiveConfig.cancelText }}
34
45
  </view>
35
46
  <view
36
- v-if="showConfirmButton || slots['confirm-button']"
37
- :hover-stay-time="100"
38
- :hover-class="asyncClose ? 'none' : 'u-model__btn--hover'"
47
+ v-if="effectiveConfig.showConfirmButton || slots['confirm-button']"
39
48
  class="u-model__footer__button hairline-left"
49
+ :hover-stay-time="100"
50
+ :hover-class="effectiveConfig.asyncClose ? 'none' : 'u-model__btn--hover'"
40
51
  :style="[confirmBtnStyle]"
41
52
  @tap="confirm"
42
53
  >
43
54
  <slot v-if="slots['confirm-button']" name="confirm-button"></slot>
44
55
  <template v-else>
45
- <u-loading mode="circle" :color="confirmColor" v-if="loading"></u-loading>
56
+ <u-loading mode="circle" :color="effectiveConfig.confirmColor" v-if="loading"></u-loading>
46
57
  <template v-else>
47
- {{ confirmText }}
58
+ {{ effectiveConfig.confirmText }}
48
59
  </template>
49
60
  </template>
50
61
  </view>
@@ -68,9 +79,18 @@ export default {
68
79
  </script>
69
80
 
70
81
  <script setup lang="ts">
71
- import { ref, computed, watch, useSlots } from 'vue';
82
+ import { ref, computed, watch, onMounted, onBeforeUnmount, useSlots } from 'vue';
72
83
  import { $u } from '../..';
73
84
  import { ModalProps } from './types';
85
+ import {
86
+ U_MODAL_EVENT_CLEAR_LOADING,
87
+ U_MODAL_EVENT_HIDE,
88
+ U_MODAL_EVENT_SHOW,
89
+ U_MODAL_GLOBAL_EVENT_CLEAR_LOADING,
90
+ U_MODAL_GLOBAL_EVENT_HIDE,
91
+ U_MODAL_GLOBAL_EVENT_SHOW,
92
+ type ModalPayload
93
+ } from './service';
74
94
 
75
95
  /**
76
96
  * modal 模态框
@@ -108,23 +128,99 @@ const slots = useSlots();
108
128
 
109
129
  // 确认按钮是否正在加载中
110
130
  const loading = ref(false);
131
+ const isGlobal = computed(() => props.global);
132
+ const isPage = computed(() => props.page);
133
+ const showEvent = computed(() => (isGlobal.value ? U_MODAL_GLOBAL_EVENT_SHOW : isPage.value ? U_MODAL_EVENT_SHOW : ''));
134
+ const hideEvent = computed(() => (isGlobal.value ? U_MODAL_GLOBAL_EVENT_HIDE : isPage.value ? U_MODAL_EVENT_HIDE : ''));
135
+ const clearLoadingEvent = computed(() =>
136
+ isGlobal.value ? U_MODAL_GLOBAL_EVENT_CLEAR_LOADING : isPage.value ? U_MODAL_EVENT_CLEAR_LOADING : ''
137
+ );
138
+
139
+ // 存储用户传入的回调函数
140
+ let userOnConfirm: (() => void) | null = null;
141
+ let userOnCancel: (() => void) | null = null;
142
+
143
+ // 需要与 effectiveConfig 合并的 props 键名列表(用于函数式调用)
144
+ const MERGE_PROPS_KEYS = [
145
+ 'title',
146
+ 'content',
147
+ 'showTitle',
148
+ 'showConfirmButton',
149
+ 'showCancelButton',
150
+ 'confirmText',
151
+ 'cancelText',
152
+ 'confirmColor',
153
+ 'cancelColor',
154
+ 'confirmStyle',
155
+ 'cancelStyle',
156
+ 'titleStyle',
157
+ 'contentStyle',
158
+ 'asyncClose',
159
+ 'borderRadius',
160
+ 'width',
161
+ 'zoom',
162
+ 'maskCloseAble',
163
+ 'negativeTop',
164
+ 'zIndex',
165
+ 'customStyle',
166
+ 'customClass'
167
+ ] as const;
168
+
169
+ // 函数式调用时的临时配置
170
+ const tempConfig = ref<Partial<ModalPayload>>({});
171
+
172
+ // 函数式调用时的内部显示状态(用于 global 模式)
173
+ const internalShow = ref(false);
111
174
 
175
+ // 有效的配置(函数式调用时合并 tempConfig 和 props,v-model 时使用 props)
176
+ const effectiveConfig = computed(() => {
177
+ // 如果有临时配置(函数式调用),合并用户配置与 props 默认值
178
+ if (Object.keys(tempConfig.value).length > 0) {
179
+ const result: Record<string, any> = {};
180
+ for (const key of MERGE_PROPS_KEYS) {
181
+ // 用户配置优先,否则使用 props 默认值
182
+ result[key] = (tempConfig.value as Record<string, any>)[key] ?? (props as Record<string, any>)[key];
183
+ }
184
+ result.zIndex = tempConfig.value.zIndex ?? props.zIndex ?? $u.zIndex.popup;
185
+ return result;
186
+ }
187
+ // v-model 直接控制时使用 props
188
+ return props;
189
+ });
190
+ // 取消按钮样式
112
191
  const cancelBtnStyle = computed(() => {
113
- return Object.assign({ color: props.cancelColor }, props.cancelStyle);
192
+ return Object.assign({ color: effectiveConfig.value.cancelColor }, effectiveConfig.value.cancelStyle);
114
193
  });
194
+ // 确认按钮样式
115
195
  const confirmBtnStyle = computed(() => {
116
- return Object.assign({ color: props.confirmColor }, props.confirmStyle);
196
+ return Object.assign({ color: effectiveConfig.value.confirmColor }, effectiveConfig.value.confirmStyle);
117
197
  });
118
- const uZIndex = computed(() => (props.zIndex ? props.zIndex : $u.zIndex.popup));
119
-
198
+ // 弹窗的样式
199
+ const uZIndex = computed(() => effectiveConfig.value.zIndex ?? $u.zIndex.popup);
200
+ // 是否使用内部状态控制显示(global 模式)
201
+ const useInternalShow = computed(() => isGlobal.value || isPage.value);
202
+ // 最终显示状态
203
+ const finalShow = computed(() => {
204
+ if (useInternalShow.value) {
205
+ return internalShow.value;
206
+ }
207
+ return props.modelValue;
208
+ });
209
+ // u-popup 绑定的值
120
210
  const popupValue = computed({
121
- get: () => props.modelValue,
122
- set: (val: boolean) => emit('update:modelValue', val)
211
+ get: () => finalShow.value,
212
+ set: (val: boolean) => {
213
+ if (useInternalShow.value) {
214
+ internalShow.value = val;
215
+ } else {
216
+ emit('update:modelValue', val);
217
+ }
218
+ }
123
219
  });
124
220
 
125
221
  // 如果是异步关闭时,外部修改v-model的值为false时,重置内部的loading状态,避免下次打开的时候,状态混乱
126
222
  watch(
127
- () => props.modelValue,
223
+ () => popupValue.value,
128
224
  n => {
129
225
  if (n === true) loading.value = false;
130
226
  }
@@ -134,21 +230,33 @@ watch(
134
230
  * 确认按钮点击事件
135
231
  */
136
232
  function confirm() {
233
+ // 先调用回调,再重置配置
234
+ const onConfirm = userOnConfirm;
137
235
  // 异步关闭
138
- if (props.asyncClose) {
236
+ if (effectiveConfig.value.asyncClose) {
139
237
  loading.value = true;
140
238
  } else {
141
- emit('update:modelValue', false);
239
+ popupValue.value = false;
240
+ // 延迟重置配置,避免闪屏
241
+ setTimeout(() => resetTempConfig(), 300);
142
242
  }
143
243
  emit('confirm');
244
+ // 调用函数式调用时用户传入的回调
245
+ onConfirm?.();
144
246
  }
145
247
 
146
248
  /**
147
249
  * 取消按钮点击事件
148
250
  */
149
251
  function cancel() {
252
+ // 先调用回调,再重置配置
253
+ const onCancel = userOnCancel;
254
+ // 延迟重置配置,避免闪屏
255
+ setTimeout(() => resetTempConfig(), 300);
150
256
  emit('cancel');
151
- emit('update:modelValue', false);
257
+ popupValue.value = false;
258
+ // 调用函数式调用时用户传入的回调
259
+ onCancel?.();
152
260
  // 目前popup弹窗关闭有一个延时操作,此处做一个延时
153
261
  // 避免确认按钮文字变成了"确定"字样,modal还没消失,造成视觉不好的效果
154
262
  setTimeout(() => {
@@ -160,7 +268,8 @@ function cancel() {
160
268
  * 点击遮罩关闭modal,设置v-model的值为false,否则无法第二次弹起modal
161
269
  */
162
270
  function popupClose() {
163
- emit('update:modelValue', false);
271
+ popupValue.value = false;
272
+ resetTempConfig();
164
273
  }
165
274
 
166
275
  /**
@@ -170,6 +279,63 @@ function clearLoading() {
170
279
  loading.value = false;
171
280
  }
172
281
 
282
+ /**
283
+ * 函数式调用显示 modal
284
+ */
285
+ function onServiceShow(payload: ModalPayload) {
286
+ // 保存回调函数
287
+ userOnConfirm = payload.onConfirm ?? null;
288
+ userOnCancel = payload.onCancel ?? null;
289
+
290
+ // 只保存用户传入的配置(过滤掉回调)
291
+ const { onConfirm, onCancel, ...rest } = payload;
292
+ tempConfig.value = rest;
293
+
294
+ // 使用内部状态控制显示
295
+ internalShow.value = true;
296
+ }
297
+
298
+ /**
299
+ * 函数式调用关闭 modal
300
+ */
301
+ function onServiceHide() {
302
+ internalShow.value = false;
303
+ resetTempConfig();
304
+ }
305
+
306
+ /**
307
+ * 重置临时配置
308
+ */
309
+ function resetTempConfig() {
310
+ tempConfig.value = {};
311
+ userOnConfirm = null;
312
+ userOnCancel = null;
313
+ }
314
+
315
+ onMounted(() => {
316
+ if (showEvent.value) {
317
+ uni?.$on && uni.$on(showEvent.value, onServiceShow);
318
+ }
319
+ if (hideEvent.value) {
320
+ uni?.$on && uni.$on(hideEvent.value, onServiceHide);
321
+ }
322
+ if (clearLoadingEvent.value) {
323
+ uni?.$on && uni.$on(clearLoadingEvent.value, clearLoading);
324
+ }
325
+ });
326
+
327
+ onBeforeUnmount(() => {
328
+ if (showEvent.value) {
329
+ uni?.$off && uni.$off(showEvent.value, onServiceShow);
330
+ }
331
+ if (hideEvent.value) {
332
+ uni?.$off && uni.$off(hideEvent.value, onServiceHide);
333
+ }
334
+ if (clearLoadingEvent.value) {
335
+ uni?.$off && uni.$off(clearLoadingEvent.value, clearLoading);
336
+ }
337
+ });
338
+
173
339
  defineExpose({
174
340
  clearLoading
175
341
  });
@@ -13,7 +13,7 @@ export const ToastProps = {
13
13
  zIndex: { type: [Number, String] as PropType<number | string>, default: zIndex.toast },
14
14
  /** 提示类型,success/warning/error/loading 等 */
15
15
  type: { type: String as PropType<ThemeType | 'default'>, default: '' },
16
- /** 显示时长,单位ms */
16
+ /** 显示时长,单位ms。设为 0 表示不自动关闭,需手动调用 hide/close 方法 */
17
17
  duration: { type: Number, default: 2000 },
18
18
  /** 是否显示图标 */
19
19
  icon: { type: Boolean, default: true },
@@ -31,6 +31,8 @@ export const ToastProps = {
31
31
  params: { type: Object as PropType<Record<string, any>>, default: () => ({}) },
32
32
  /** 是否作为全局根部 toast(通常放在 App.vue 中,给 useToast() 使用) */
33
33
  global: { type: Boolean, default: false },
34
+ /** 是否作为页面级 toast(通常放在页面中,给 useToast({ page: true }) 使用) */
35
+ page: { type: Boolean, default: false },
34
36
  /** 是否为loading “常驻” */
35
37
  loading: { type: Boolean, default: false }
36
38
  };
@@ -112,6 +112,7 @@ const uZIndex = computed(() => {
112
112
 
113
113
  /**
114
114
  * 显示toast组件,由父组件通过ref.show(options)形式调用
115
+ * @description 当 duration 为 0 或不传时,表示不自动关闭,需要手动调用 hide/close 方法关闭
115
116
  */
116
117
  function show(options: any) {
117
118
  // 不将结果合并到config变量,避免多次调用u-toast,前后的配置造成混乱
@@ -122,15 +123,18 @@ function show(options: any) {
122
123
  timer = null;
123
124
  }
124
125
  isShow.value = true;
125
- timer = setTimeout(() => {
126
- // 倒计时结束,清除定时器,隐藏toast组件
127
- isShow.value = false;
128
- clearTimeout(timer!);
129
- timer = null;
130
- // 判断是否存在callback方法,如果存在就执行
131
- typeof tmpConfig.value.callback === 'function' && tmpConfig.value.callback();
132
- timeEnd();
133
- }, tmpConfig.value.duration);
126
+ // duration 0、undefined 或小于等于 0 时,表示不自动关闭,需要手动调用 hide/close
127
+ if (tmpConfig.value.duration > 0) {
128
+ timer = setTimeout(() => {
129
+ // 倒计时结束,清除定时器,隐藏toast组件
130
+ isShow.value = false;
131
+ clearTimeout(timer!);
132
+ timer = null;
133
+ // 判断是否存在callback方法,如果存在就执行
134
+ typeof tmpConfig.value.callback === 'function' && tmpConfig.value.callback();
135
+ timeEnd();
136
+ }, tmpConfig.value.duration);
137
+ }
134
138
  }
135
139
  /**
136
140
  * 隐藏toast组件,由父组件通过ref.hide()形式调用
@@ -194,20 +198,28 @@ function onServiceHide() {
194
198
 
195
199
  // 是否为 App 根部的“全局 toast”
196
200
  const isGlobal = computed(() => props.global);
201
+ // 是否为页面级 toast
202
+ const isPage = computed(() => props.page);
197
203
 
198
- const showEvent = computed(() => (isGlobal.value ? U_TOAST_GLOBAL_EVENT_SHOW : U_TOAST_EVENT_SHOW));
199
- const hideEvent = computed(() => (isGlobal.value ? U_TOAST_GLOBAL_EVENT_HIDE : U_TOAST_EVENT_HIDE));
204
+ // 显示事件
205
+ const showEvent = computed(() => (isGlobal.value ? U_TOAST_GLOBAL_EVENT_SHOW : isPage.value ? U_TOAST_EVENT_SHOW : ''));
206
+ // 隐藏事件
207
+ const hideEvent = computed(() => (isGlobal.value ? U_TOAST_GLOBAL_EVENT_HIDE : isPage.value ? U_TOAST_EVENT_HIDE : ''));
200
208
 
201
209
  onMounted(() => {
202
- if (isGlobal.value) {
210
+ if (showEvent.value) {
203
211
  uni?.$on && uni.$on(showEvent.value, onServiceShow);
212
+ }
213
+ if (hideEvent.value) {
204
214
  uni?.$on && uni.$on(hideEvent.value, onServiceHide);
205
215
  }
206
216
  });
207
217
 
208
218
  onBeforeUnmount(() => {
209
- if (isGlobal.value) {
219
+ if (showEvent.value) {
210
220
  uni?.$off && uni.$off(showEvent.value, onServiceShow);
221
+ }
222
+ if (hideEvent.value) {
211
223
  uni?.$off && uni.$off(hideEvent.value, onServiceHide);
212
224
  }
213
225
  });
@@ -7,3 +7,4 @@ export * from './useLocale';
7
7
  export * from './useDebounce';
8
8
  export * from './useThrottle';
9
9
  export * from './useToast';
10
+ export * from './useModal';
@@ -0,0 +1,105 @@
1
+ import {
2
+ U_MODAL_EVENT_SHOW,
3
+ U_MODAL_EVENT_HIDE,
4
+ U_MODAL_EVENT_CLEAR_LOADING,
5
+ U_MODAL_GLOBAL_EVENT_SHOW,
6
+ U_MODAL_GLOBAL_EVENT_HIDE,
7
+ U_MODAL_GLOBAL_EVENT_CLEAR_LOADING,
8
+ type ModalPayload
9
+ } from '../../components/u-modal/service';
10
+
11
+ export type UseModalShowOptions = ModalPayload;
12
+
13
+ export type UseModal = {
14
+ /**
15
+ * 显示 modal
16
+ * - show('标题')
17
+ * - show({ title, content, showCancelButton, ... })
18
+ */
19
+ show: (contentOrOptions: string | UseModalShowOptions) => void;
20
+ /**
21
+ * 显示 confirm 类型的 modal(带确认和取消按钮)
22
+ * - confirm('标题')
23
+ * - confirm({ title, content, onConfirm, onCancel })
24
+ */
25
+ confirm: (contentOrOptions: string | UseModalShowOptions) => void;
26
+ /** 关闭 modal */
27
+ close: () => void;
28
+ /** 清除 loading 状态 */
29
+ clearLoading: () => void;
30
+ };
31
+
32
+ export type UseModalOptions = {
33
+ /** 是否使用全局根部 <u-modal global /> */
34
+ global?: boolean;
35
+ /** 是否使用页面级 <u-modal page /> */
36
+ page?: boolean;
37
+ };
38
+
39
+ function normalize(contentOrOptions: string | UseModalShowOptions): UseModalShowOptions {
40
+ if (typeof contentOrOptions === 'string') {
41
+ // 如果是简单的字符串,可能是标题或内容
42
+ return { content: contentOrOptions };
43
+ }
44
+ return contentOrOptions || {};
45
+ }
46
+
47
+ /**
48
+ * Modal 函数式调用
49
+ * @description 需要页面/应用中至少存在一个 <u-modal global /> 或 <u-modal page /> 实例用于承接事件;不影响原调用方式。
50
+ * 支持两种调用方式:应用级 useModal() / useModal({ global: true }) 页面级 useModal({ page: true }) / useModal(false)
51
+ */
52
+ export function useModal(optionsOrGlobal: UseModalOptions | boolean = true): UseModal {
53
+ const isGlobal = typeof optionsOrGlobal === 'boolean' ? optionsOrGlobal !== false : optionsOrGlobal.global === true;
54
+ const isPage = typeof optionsOrGlobal === 'boolean' ? optionsOrGlobal === false : optionsOrGlobal.page === true;
55
+
56
+ const showEvent = isGlobal ? U_MODAL_GLOBAL_EVENT_SHOW : isPage ? U_MODAL_EVENT_SHOW : '';
57
+ const hideEvent = isGlobal ? U_MODAL_GLOBAL_EVENT_HIDE : isPage ? U_MODAL_EVENT_HIDE : '';
58
+ const clearLoadingEvent = isGlobal ? U_MODAL_GLOBAL_EVENT_CLEAR_LOADING : isPage ? U_MODAL_EVENT_CLEAR_LOADING : '';
59
+
60
+ function emitShow(payload: UseModalShowOptions) {
61
+ if (showEvent) {
62
+ uni?.$emit && uni.$emit(showEvent, payload);
63
+ }
64
+ }
65
+
66
+ function emitHide() {
67
+ if (hideEvent) {
68
+ uni?.$emit && uni.$emit(hideEvent);
69
+ }
70
+ }
71
+
72
+ function emitClearLoading() {
73
+ if (clearLoadingEvent) {
74
+ uni?.$emit && uni.$emit(clearLoadingEvent);
75
+ }
76
+ }
77
+
78
+ function show(contentOrOptions: string | UseModalShowOptions) {
79
+ emitShow(normalize(contentOrOptions));
80
+ }
81
+
82
+ function confirm(contentOrOptions: string | UseModalShowOptions) {
83
+ const options = normalize(contentOrOptions);
84
+ emitShow({
85
+ ...options,
86
+ showCancelButton: options.showCancelButton ?? true,
87
+ showConfirmButton: options.showConfirmButton ?? true
88
+ });
89
+ }
90
+
91
+ function clearLoading() {
92
+ emitClearLoading();
93
+ }
94
+
95
+ function close() {
96
+ emitHide();
97
+ }
98
+
99
+ return {
100
+ show,
101
+ confirm,
102
+ close,
103
+ clearLoading
104
+ };
105
+ }
@@ -33,6 +33,8 @@ export type UseToast = {
33
33
  export type UseToastOptions = {
34
34
  /** 是否使用全局根部 <u-toast global />,默认 true;为 false 时走页面级 <u-toast /> */
35
35
  global?: boolean;
36
+ /** 是否使用页面级 <u-toast page /> */
37
+ page?: boolean;
36
38
  };
37
39
 
38
40
  function normalize(titleOrOptions: string | UseToastShowOptions): UseToastShowOptions {
@@ -42,24 +44,25 @@ function normalize(titleOrOptions: string | UseToastShowOptions): UseToastShowOp
42
44
 
43
45
  /**
44
46
  * Toast 函数式调用
45
- * @description 需要页面/应用中至少存在一个 <u-toast /> 实例用于承接事件;不影响原 ref 调用方式。
46
- * 支持两种调用方式:useToast() / useToast({ global: false }) / useToast(false)
47
+ * @description 需要页面/应用中至少存在一个 <u-toast global /> 或 <u-toast page /> 实例用于承接事件;不影响原 ref 调用方式。
48
+ * 支持两种调用方式:应用级 useToast() / useToast({ global: true }) 页面级 useToast({ page: true }) / useToast(false)
47
49
  */
48
- export function useToast(): UseToast;
49
- export function useToast(options: UseToastOptions): UseToast;
50
- export function useToast(global: boolean): UseToast;
51
- export function useToast(optionsOrGlobal: UseToastOptions | boolean = {}): UseToast {
52
- const isGlobal =
53
- typeof optionsOrGlobal === 'boolean' ? optionsOrGlobal !== false : optionsOrGlobal.global !== false;
54
- const showEvent = isGlobal ? U_TOAST_GLOBAL_EVENT_SHOW : U_TOAST_EVENT_SHOW;
55
- const hideEvent = isGlobal ? U_TOAST_GLOBAL_EVENT_HIDE : U_TOAST_EVENT_HIDE;
50
+ export function useToast(optionsOrGlobal: UseToastOptions | boolean = true): UseToast {
51
+ const isGlobal = typeof optionsOrGlobal === 'boolean' ? optionsOrGlobal !== false : optionsOrGlobal.global === true;
52
+ const isPage = typeof optionsOrGlobal === 'boolean' ? optionsOrGlobal === false : optionsOrGlobal.page === true;
53
+ const showEvent = isGlobal ? U_TOAST_GLOBAL_EVENT_SHOW : isPage ? U_TOAST_EVENT_SHOW : '';
54
+ const hideEvent = isGlobal ? U_TOAST_GLOBAL_EVENT_HIDE : isPage ? U_TOAST_EVENT_HIDE : '';
56
55
 
57
56
  function emitShow(payload: UseToastShowOptions) {
58
- uni?.$emit && uni.$emit(showEvent, payload);
57
+ if (showEvent) {
58
+ uni?.$emit && uni.$emit(showEvent, payload);
59
+ }
59
60
  }
60
61
 
61
62
  function emitHide() {
62
- uni?.$emit && uni.$emit(hideEvent);
63
+ if (hideEvent) {
64
+ uni?.$emit && uni.$emit(hideEvent);
65
+ }
63
66
  }
64
67
  function show(titleOrOptions: string | UseToastShowOptions) {
65
68
  emitShow(normalize(titleOrOptions));
@@ -84,7 +87,7 @@ export function useToast(optionsOrGlobal: UseToastOptions | boolean = {}): UseTo
84
87
  loading: (v: any) => {
85
88
  const options = normalize(v);
86
89
  // loading 通常需要常驻,除非用户显式传 duration
87
- emitShow({ ...options, loading: true, duration: options.duration ?? 1e9 });
90
+ emitShow({ ...options, loading: true, duration: options.duration ?? 0 });
88
91
  }
89
92
  };
90
93
  }
@@ -1,5 +1,7 @@
1
1
  export default {
2
2
  name: 'en-US',
3
+ label: 'English',
4
+ locale: 'en',
3
5
  uActionSheet: {
4
6
  cancelText: 'Cancel'
5
7
  },
@@ -1,5 +1,7 @@
1
1
  export default {
2
2
  name: 'zh-CN',
3
+ label: '简体中文',
4
+ locale: 'zh-Hans',
3
5
  uActionSheet: {
4
6
  cancelText: '取消'
5
7
  },
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "id": "uview-pro",
3
3
  "name": "uview-pro",
4
4
  "displayName": "【支持鸿蒙】uView Pro|基于Vue3+TS的高质量UI组件库,支持多主题、暗黑模式、多语言",
5
- "version": "0.5.4",
5
+ "version": "0.5.5",
6
6
  "description": "uView Pro是基于Vue3+TS的多平台UI框架,提供80+高质量组件、便捷工具和常用模板,支持多主题、暗黑模式、多语言,支持H5/APP/鸿蒙/小程序多端开发。已在鸿蒙应用商店上架,欢迎体验!",
7
7
  "main": "index.ts",
8
8
  "module": "index.ts",