oxy-uni-ui 2.1.2 → 2.1.4

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 (261) hide show
  1. package/attributes.json +1 -1
  2. package/components/common/AbortablePromise.ts +4 -0
  3. package/components/common/abstracts/variable.scss +172 -66
  4. package/components/common/base64.ts +6 -0
  5. package/components/common/clickoutside.ts +5 -0
  6. package/components/common/interceptor.ts +9 -0
  7. package/components/common/path.ts +4 -0
  8. package/components/common/props.ts +23 -0
  9. package/components/common/util.ts +18 -1
  10. package/components/composables/index.ts +2 -0
  11. package/components/composables/useCell.ts +8 -1
  12. package/components/composables/useChildren.ts +27 -1
  13. package/components/composables/useCountDown.ts +10 -1
  14. package/components/composables/useDynamicVirtualScroll.ts +9 -0
  15. package/components/composables/useGlobalLoading.ts +6 -0
  16. package/components/composables/useGlobalMessage.ts +6 -0
  17. package/components/composables/useGlobalToast.ts +6 -0
  18. package/components/composables/useLockScroll.ts +9 -0
  19. package/components/composables/useParent.ts +65 -14
  20. package/components/composables/usePopover.ts +9 -0
  21. package/components/composables/useQueue.ts +8 -0
  22. package/components/composables/useRaf.ts +10 -0
  23. package/components/composables/useSharedState.ts +284 -0
  24. package/components/composables/useTouch.ts +8 -0
  25. package/components/composables/useTranslate.ts +9 -0
  26. package/components/composables/useUpload.ts +83 -11
  27. package/components/composables/useVirtualScroll.ts +7 -0
  28. package/components/composables/useWindowResize.ts +9 -0
  29. package/components/oxy-action-sheet/index.scss +5 -5
  30. package/components/oxy-action-sheet/oxy-action-sheet.vue +11 -0
  31. package/components/oxy-backtop/index.scss +2 -2
  32. package/components/oxy-backtop/oxy-backtop.vue +2 -0
  33. package/components/oxy-badge/oxy-badge.vue +2 -0
  34. package/components/oxy-badge/types.ts +3 -0
  35. package/components/oxy-button/index.scss +13 -13
  36. package/components/oxy-button/oxy-button.vue +11 -0
  37. package/components/oxy-button/types.ts +3 -3
  38. package/components/oxy-calendar/index.scss +17 -17
  39. package/components/oxy-calendar/oxy-calendar.vue +31 -0
  40. package/components/oxy-calendar/types.ts +3 -0
  41. package/components/oxy-calendar-view/month/month.vue +2 -0
  42. package/components/oxy-calendar-view/monthPanel/month-panel.vue +3 -1
  43. package/components/oxy-calendar-view/monthPanel/types.ts +10 -7
  44. package/components/oxy-calendar-view/oxy-calendar-view.vue +2 -0
  45. package/components/oxy-calendar-view/types.ts +3 -1
  46. package/components/oxy-calendar-view/year/types.ts +33 -1
  47. package/components/oxy-calendar-view/year/year.vue +2 -0
  48. package/components/oxy-calendar-view/yearPanel/year-panel.vue +2 -0
  49. package/components/oxy-card/index.scss +1 -1
  50. package/components/oxy-card/oxy-card.vue +2 -0
  51. package/components/oxy-cell/index.scss +3 -1
  52. package/components/oxy-cell/oxy-cell.vue +6 -4
  53. package/components/oxy-cell-group/oxy-cell-group.vue +2 -0
  54. package/components/oxy-checkbox/index.scss +7 -7
  55. package/components/oxy-checkbox/oxy-checkbox.vue +22 -16
  56. package/components/oxy-checkbox/types.ts +9 -0
  57. package/components/oxy-checkbox-group/index.scss +1 -1
  58. package/components/oxy-checkbox-group/oxy-checkbox-group.vue +2 -0
  59. package/components/oxy-circle/oxy-circle.vue +2 -0
  60. package/components/oxy-col/oxy-col.vue +3 -1
  61. package/components/oxy-col-picker/index.scss +3 -3
  62. package/components/oxy-col-picker/oxy-col-picker.vue +17 -1
  63. package/components/oxy-col-picker/types.ts +3 -0
  64. package/components/oxy-collapse/index.scss +2 -2
  65. package/components/oxy-collapse/oxy-collapse.vue +9 -0
  66. package/components/oxy-collapse-item/oxy-collapse-item.vue +4 -2
  67. package/components/oxy-config-provider/oxy-config-provider.vue +18 -0
  68. package/components/oxy-config-provider/types.ts +18 -0
  69. package/components/oxy-corner/index.scss +50 -32
  70. package/components/oxy-corner/oxy-corner.vue +2 -0
  71. package/components/oxy-count-down/oxy-count-down.vue +2 -0
  72. package/components/oxy-count-to/oxy-count-to.vue +12 -1
  73. package/components/oxy-count-to/types.ts +14 -4
  74. package/components/oxy-curtain/index.scss +10 -9
  75. package/components/oxy-curtain/oxy-curtain.vue +2 -0
  76. package/components/oxy-date-strip/oxy-date-strip.vue +22 -1
  77. package/components/oxy-date-strip-item/index.scss +4 -4
  78. package/components/oxy-date-strip-item/oxy-date-strip-item.vue +2 -0
  79. package/components/oxy-datetime-picker/index.scss +3 -3
  80. package/components/oxy-datetime-picker/oxy-datetime-picker.vue +11 -1
  81. package/components/oxy-datetime-picker-view/oxy-datetime-picker-view.vue +2 -0
  82. package/components/oxy-divider/oxy-divider.vue +2 -0
  83. package/components/oxy-drop-menu/index.scss +2 -2
  84. package/components/oxy-drop-menu/oxy-drop-menu.vue +3 -0
  85. package/components/oxy-drop-menu-item/index.scss +1 -1
  86. package/components/oxy-drop-menu-item/oxy-drop-menu-item.vue +17 -9
  87. package/components/oxy-echarts/oxy-echarts.vue +4 -2
  88. package/components/oxy-fab/index.scss +1 -1
  89. package/components/oxy-fab/oxy-fab.vue +27 -0
  90. package/components/oxy-file-list/index.scss +9 -9
  91. package/components/oxy-file-list/oxy-file-list.vue +29 -17
  92. package/components/oxy-floating-panel/oxy-floating-panel.vue +34 -10
  93. package/components/oxy-footer/index.scss +7 -0
  94. package/components/oxy-footer/oxy-footer.vue +4 -2
  95. package/components/oxy-footer/types.ts +9 -6
  96. package/components/oxy-form/oxy-form.vue +13 -1
  97. package/components/oxy-form-item/oxy-form-item.vue +5 -3
  98. package/components/oxy-gap/oxy-gap.vue +2 -0
  99. package/components/oxy-global-loading/oxy-global-loading.vue +18 -5
  100. package/components/oxy-global-message/oxy-global-message.vue +30 -17
  101. package/components/oxy-global-toast/oxy-global-toast.vue +18 -5
  102. package/components/oxy-grid/oxy-grid.vue +6 -0
  103. package/components/oxy-grid-item/index.scss +1 -1
  104. package/components/oxy-grid-item/oxy-grid-item.vue +22 -20
  105. package/components/oxy-guidance/index.scss +9 -9
  106. package/components/oxy-guidance/oxy-guidance.vue +22 -16
  107. package/components/oxy-icon/oxy-icon.vue +2 -0
  108. package/components/oxy-icon/types.ts +3 -0
  109. package/components/oxy-image-preview/index.scss +75 -0
  110. package/components/oxy-image-preview/index.ts +56 -0
  111. package/components/oxy-image-preview/oxy-image-preview.vue +221 -0
  112. package/components/oxy-image-preview/types.ts +164 -0
  113. package/components/oxy-img/oxy-img.vue +2 -0
  114. package/components/oxy-img-cropper/index.scss +2 -2
  115. package/components/oxy-img-cropper/oxy-img-cropper.vue +6 -0
  116. package/components/oxy-img-lazy/oxy-img-lazy.vue +9 -0
  117. package/components/oxy-index-anchor/index.scss +1 -1
  118. package/components/oxy-index-anchor/oxy-index-anchor.vue +2 -2
  119. package/components/oxy-index-bar/index.scss +1 -1
  120. package/components/oxy-index-bar/oxy-index-bar.vue +18 -0
  121. package/components/oxy-input/index.scss +2 -1
  122. package/components/oxy-input/oxy-input.vue +18 -4
  123. package/components/oxy-input/types.ts +6 -0
  124. package/components/oxy-input-number/index.scss +3 -1
  125. package/components/oxy-input-number/oxy-input-number.vue +2 -0
  126. package/components/oxy-keyboard/index.scss +3 -3
  127. package/components/oxy-keyboard/key/index.vue +2 -0
  128. package/components/oxy-keyboard/oxy-keyboard.vue +11 -2
  129. package/components/oxy-link/index.scss +8 -4
  130. package/components/oxy-link/oxy-link.vue +9 -3
  131. package/components/oxy-link/types.ts +2 -2
  132. package/components/oxy-list/oxy-list.vue +2 -0
  133. package/components/oxy-list/types.ts +7 -0
  134. package/components/oxy-loading/oxy-loading.vue +6 -0
  135. package/components/oxy-loadmore/index.scss +1 -1
  136. package/components/oxy-loadmore/oxy-loadmore.vue +2 -0
  137. package/components/oxy-long-press-menu/index.scss +1 -1
  138. package/components/oxy-long-press-menu/oxy-long-press-menu.vue +42 -13
  139. package/components/oxy-message-box/index.scss +223 -36
  140. package/components/oxy-message-box/index.ts +49 -23
  141. package/components/oxy-message-box/oxy-message-box.vue +181 -56
  142. package/components/oxy-message-box/types.ts +60 -2
  143. package/components/oxy-navbar/index.scss +1 -1
  144. package/components/oxy-navbar/oxy-navbar.vue +9 -6
  145. package/components/oxy-navbar-capsule/oxy-navbar-capsule.vue +2 -0
  146. package/components/oxy-notice-bar/index.scss +1 -1
  147. package/components/oxy-notice-bar/oxy-notice-bar.vue +29 -0
  148. package/components/oxy-notify/index.ts +16 -12
  149. package/components/oxy-notify/oxy-notify.vue +11 -4
  150. package/components/oxy-overlay/oxy-overlay.vue +34 -2
  151. package/components/oxy-pagination/oxy-pagination.vue +2 -0
  152. package/components/oxy-password-input/oxy-password-input.vue +2 -0
  153. package/components/oxy-picker/index.scss +7 -7
  154. package/components/oxy-picker/oxy-picker.vue +11 -0
  155. package/components/oxy-picker-view/index.scss +1 -1
  156. package/components/oxy-picker-view/oxy-picker-view.vue +2 -0
  157. package/components/oxy-popover/index.scss +2 -2
  158. package/components/oxy-popover/oxy-popover.vue +4 -1
  159. package/components/oxy-popup/oxy-popup.vue +8 -0
  160. package/components/oxy-progress/index.scss +2 -2
  161. package/components/oxy-progress/oxy-progress.vue +7 -0
  162. package/components/oxy-qrcode/draw.ts +15 -1
  163. package/components/oxy-qrcode/oxy-qrcode.vue +10 -3
  164. package/components/oxy-radio/index.scss +8 -8
  165. package/components/oxy-radio/oxy-radio.vue +13 -11
  166. package/components/oxy-radio-group/index.scss +1 -1
  167. package/components/oxy-radio-group/oxy-radio-group.vue +2 -0
  168. package/components/oxy-rate/oxy-rate.vue +5 -0
  169. package/components/oxy-rate/types.ts +2 -2
  170. package/components/oxy-resize/oxy-resize.vue +12 -0
  171. package/components/oxy-rich-text/index.scss +9 -9
  172. package/components/oxy-rich-text/oxy-rich-text.vue +6 -0
  173. package/components/oxy-rich-text/types.ts +15 -0
  174. package/components/oxy-root-portal/oxy-root-portal.vue +23 -15
  175. package/components/oxy-row/oxy-row.vue +2 -0
  176. package/components/oxy-search/index.scss +3 -3
  177. package/components/oxy-search/oxy-search.vue +19 -0
  178. package/components/oxy-segmented/index.scss +4 -4
  179. package/components/oxy-segmented/oxy-segmented.vue +2 -0
  180. package/components/oxy-select/index.scss +25 -25
  181. package/components/oxy-select/oxy-select.vue +27 -0
  182. package/components/oxy-select/types.ts +3 -0
  183. package/components/oxy-select-picker/index.scss +5 -5
  184. package/components/oxy-select-picker/oxy-select-picker.vue +7 -0
  185. package/components/oxy-sidebar/oxy-sidebar.vue +2 -0
  186. package/components/oxy-sidebar-item/index.scss +3 -3
  187. package/components/oxy-sidebar-item/oxy-sidebar-item.vue +17 -31
  188. package/components/oxy-signature/oxy-signature.vue +17 -0
  189. package/components/oxy-skeleton/oxy-skeleton.vue +7 -1
  190. package/components/oxy-slide-verify/index.scss +95 -0
  191. package/components/oxy-slide-verify/oxy-slide-verify.vue +244 -0
  192. package/components/oxy-slide-verify/types.ts +98 -0
  193. package/components/oxy-slider/oxy-slider.vue +3 -0
  194. package/components/oxy-sort-button/index.scss +10 -8
  195. package/components/oxy-sort-button/oxy-sort-button.vue +6 -0
  196. package/components/oxy-splitter/index.scss +9 -0
  197. package/components/oxy-splitter/oxy-splitter.vue +23 -2
  198. package/components/oxy-splitter-panel/index.scss +7 -5
  199. package/components/oxy-splitter-panel/oxy-splitter-panel.vue +93 -89
  200. package/components/oxy-status-tip/index.scss +3 -3
  201. package/components/oxy-status-tip/oxy-status-tip.vue +2 -0
  202. package/components/oxy-step/index.scss +4 -4
  203. package/components/oxy-step/oxy-step.vue +15 -13
  204. package/components/oxy-steps/oxy-steps.vue +2 -0
  205. package/components/oxy-sticky/oxy-sticky.vue +5 -3
  206. package/components/oxy-stream-render/oxy-stream-render.vue +29 -0
  207. package/components/oxy-swipe-action/oxy-swipe-action.vue +8 -0
  208. package/components/oxy-swiper/oxy-swiper.vue +6 -0
  209. package/components/oxy-swiper-nav/index.scss +4 -4
  210. package/components/oxy-switch/index.scss +6 -3
  211. package/components/oxy-switch/oxy-switch.vue +10 -0
  212. package/components/oxy-tab/index.scss +1 -7
  213. package/components/oxy-tab/oxy-tab.vue +12 -10
  214. package/components/oxy-tabbar/oxy-tabbar.vue +5 -0
  215. package/components/oxy-tabbar-item/oxy-tabbar-item.vue +11 -9
  216. package/components/oxy-table/index.scss +1 -1
  217. package/components/oxy-table/oxy-table.vue +8 -0
  218. package/components/oxy-table/types.ts +3 -0
  219. package/components/oxy-table-col/oxy-table-col.vue +21 -19
  220. package/components/oxy-tabs/index.scss +11 -11
  221. package/components/oxy-tabs/oxy-tabs.vue +17 -0
  222. package/components/oxy-tag/index.scss +24 -5
  223. package/components/oxy-tag/oxy-tag.vue +17 -2
  224. package/components/oxy-tag/types.ts +7 -0
  225. package/components/oxy-text/oxy-text.vue +12 -1
  226. package/components/oxy-textarea/index.scss +3 -2
  227. package/components/oxy-textarea/oxy-textarea.vue +10 -4
  228. package/components/oxy-toast/index.ts +21 -20
  229. package/components/oxy-toast/oxy-toast.vue +30 -5
  230. package/components/oxy-toast/types.ts +20 -9
  231. package/components/oxy-tooltip/index.scss +1 -1
  232. package/components/oxy-tooltip/oxy-tooltip.vue +2 -0
  233. package/components/oxy-transition/index.scss +3 -1
  234. package/components/oxy-transition/oxy-transition.vue +14 -0
  235. package/components/oxy-tree/index.scss +3 -3
  236. package/components/oxy-tree/oxy-tree.vue +28 -5
  237. package/components/oxy-tree/types.ts +57 -1
  238. package/components/oxy-upload/index.scss +9 -9
  239. package/components/oxy-upload/oxy-upload.vue +5 -3
  240. package/components/oxy-verification-code/oxy-verification-code.vue +24 -0
  241. package/components/oxy-video-preview/index.scss +12 -7
  242. package/components/oxy-video-preview/index.ts +64 -0
  243. package/components/oxy-video-preview/oxy-video-preview.vue +102 -31
  244. package/components/oxy-video-preview/types.ts +54 -8
  245. package/components/oxy-virtual-scroll/index.scss +1 -1
  246. package/components/oxy-virtual-scroll/oxy-virtual-scroll.vue +2 -0
  247. package/components/oxy-voice-player/index.scss +64 -64
  248. package/components/oxy-voice-player/oxy-voice-player.vue +38 -10
  249. package/components/oxy-waterfall/oxy-waterfall.vue +13 -0
  250. package/components/oxy-waterfall/types.ts +7 -0
  251. package/components/oxy-waterfall-item/oxy-waterfall-item.vue +9 -0
  252. package/components/oxy-watermark/oxy-watermark.vue +2 -0
  253. package/global.d.ts +2 -0
  254. package/index.ts +2 -0
  255. package/locale/lang/en-US.ts +51 -2
  256. package/locale/lang/zh-CN.ts +51 -2
  257. package/package.json +2 -2
  258. package/styles/all.scss +125 -0
  259. package/tags.json +1 -1
  260. package/vite-plugins/vite-plugin-host-class.ts +166 -0
  261. package/web-types.json +1 -1
@@ -0,0 +1,284 @@
1
+ import { ref, type Ref } from 'vue'
2
+
3
+ /**
4
+ * 作用域解析器。
5
+ *
6
+ * 返回值用于隔离不同页面或不同运行上下文下的同名 selector。
7
+ * 默认实现会优先使用当前页面实例生成页面级 scope。
8
+ */
9
+ type SharedStateScopeResolver = () => string
10
+
11
+ /**
12
+ * 状态 key 解析器。
13
+ *
14
+ * @param selector - 组件实例标识,例如 toast 的 selector。
15
+ * @param scopeId - 当前作用域标识,通常是页面级 scope。
16
+ * @returns 存入 stateMap 的最终 key。
17
+ */
18
+ type SharedStateKeyResolver = (selector: string, scopeId: string) => string
19
+
20
+ type CreateSharedStateOptions = {
21
+ /**
22
+ * 状态命名空间。
23
+ *
24
+ * 同一个 namespace 会复用同一个全局状态 Map,不同 namespace 之间完全隔离。
25
+ * 例如 toast、message-box、notify 使用不同 namespace,避免默认 selector 相同时状态结构互相覆盖。
26
+ */
27
+ namespace?: string
28
+ /**
29
+ * 自定义作用域解析逻辑。
30
+ *
31
+ * 未传入时默认使用当前页面实例生成页面级 scope,无法获取页面时退回 defaultScopeId。
32
+ */
33
+ resolveScopeId?: SharedStateScopeResolver
34
+ /**
35
+ * 自定义最终状态 key 的生成逻辑。
36
+ *
37
+ * 未传入时默认使用 `${scopeId}_${selector}`;selector 为空时直接使用 scopeId。
38
+ */
39
+ resolveKey?: SharedStateKeyResolver
40
+ }
41
+
42
+ type SharedStateStore = {
43
+ /**
44
+ * 全局 fallback scope 种子。
45
+ *
46
+ * 用于生成页面信息不可用时的默认 scope,放在全局 store 中可避免 H5 多份组件库实例各自计数。
47
+ */
48
+ scopeSeed: number
49
+ /**
50
+ * 页面 scope 种子。
51
+ *
52
+ * 每遇到一个新的 uni-app 页面对象递增一次,用于生成稳定的页面级 scope。
53
+ */
54
+ pageScopeSeed: number
55
+ /**
56
+ * 页面对象到页面 scope 的映射。
57
+ *
58
+ * 使用 WeakMap 可以让页面对象卸载后被正常回收,不需要手动删除历史页面记录。
59
+ */
60
+ pageScopeMap: WeakMap<object, string>
61
+ /**
62
+ * namespace 到 fallback scope 的映射。
63
+ *
64
+ * 当 getCurrentPages 不可用时,同一个 namespace 仍能拿到稳定默认 scope,
65
+ * 保证 hook 侧和组件侧不会因为各自生成随机 scope 而读写不同状态。
66
+ */
67
+ scopeIds: Map<string, string>
68
+ /**
69
+ * namespace 到状态 Map 的映射。
70
+ *
71
+ * 每个组件类型拥有独立状态容器,Map 内部再通过 scope + selector 区分具体实例。
72
+ */
73
+ stateMaps: Map<string, Map<string, Ref<Record<string, any>>>>
74
+ }
75
+
76
+ /**
77
+ * 当前模块实例内的本地 store。
78
+ *
79
+ * 在不支持 globalThis 的运行环境中作为兜底;
80
+ * 正常情况下会挂到 globalThis,供可能存在的多份组件库代码共同复用。
81
+ */
82
+ const localSharedStateStore: SharedStateStore = {
83
+ scopeSeed: 0,
84
+ pageScopeSeed: 0,
85
+ pageScopeMap: new WeakMap<object, string>(),
86
+ scopeIds: new Map(),
87
+ stateMaps: new Map()
88
+ }
89
+
90
+ /**
91
+ * 获取全局共享状态容器。
92
+ *
93
+ * 为什么放到 globalThis:
94
+ * - H5 或业务项目中可能同时存在组件库源码和打包产物,导致 useSharedState 模块被加载多份。
95
+ * - 如果每份模块都维护自己的 Map,`useToast()` 和 `<oxy-toast>` 可能读写到不同容器。
96
+ * - 挂到 globalThis 后,多份模块实例会落到同一个 store,从而保持状态一致。
97
+ *
98
+ * 如果当前运行环境没有可用的 globalThis,则退回模块内的 localSharedStateStore。
99
+ */
100
+ function getSharedStateStore() {
101
+ const globalScope = typeof globalThis === 'object' ? (globalThis as Record<string, any>) : null
102
+ if (!globalScope) return localSharedStateStore
103
+
104
+ const storeKey = '__OXY_UNI_UI_SHARED_STATE__'
105
+ if (!globalScope[storeKey]) {
106
+ globalScope[storeKey] = localSharedStateStore
107
+ }
108
+ return globalScope[storeKey] as SharedStateStore
109
+ }
110
+
111
+ /**
112
+ * 创建一个随机 fallback scope。
113
+ *
114
+ * fallback scope 只用于无法解析当前页面的场景,例如:
115
+ * - getCurrentPages 不存在
116
+ * - getCurrentPages 返回空数组
117
+ * - 获取页面信息时出现异常
118
+ *
119
+ * 正常页面内调用时,会优先使用 createPageScopeId 生成页面级 scope。
120
+ */
121
+ function createRandomScopeId() {
122
+ const store = getSharedStateStore()
123
+ store.scopeSeed += 1
124
+ return `shared_state_${Date.now()}_${Math.random().toString(36).slice(2, 10)}_${store.scopeSeed}`
125
+ }
126
+
127
+ /**
128
+ * 获取 namespace 对应的默认 fallback scope。
129
+ *
130
+ * 有 namespace 时,同一个组件类型复用同一个 fallback scope;
131
+ * 没有 namespace 时,每次 createSharedState 都生成独立 fallback scope,保持旧的隔离行为。
132
+ */
133
+ function createDefaultScopeId(namespace?: string) {
134
+ if (!namespace) return createRandomScopeId()
135
+
136
+ const store = getSharedStateStore()
137
+ const scopeId = store.scopeIds.get(namespace)
138
+ if (scopeId) return scopeId
139
+
140
+ const newScopeId = createRandomScopeId()
141
+ store.scopeIds.set(namespace, newScopeId)
142
+ return newScopeId
143
+ }
144
+
145
+ /**
146
+ * 根据 uni-app 页面对象创建页面级 scope。
147
+ *
148
+ * 同一个页面对象会通过 WeakMap 复用同一个 scope;
149
+ * 不同页面对象即使 route 相同,也会分配不同 scope,避免 navigateTo 多次进入同一路由时状态串用。
150
+ */
151
+ function createPageScopeId(page: object) {
152
+ const store = getSharedStateStore()
153
+ const scopeId = store.pageScopeMap.get(page)
154
+ if (scopeId) return scopeId
155
+
156
+ store.pageScopeSeed += 1
157
+ const route = 'route' in page && typeof page.route === 'string' ? page.route : 'unknown'
158
+ const pageScopeId = `shared_page_${route}_${store.pageScopeSeed}`
159
+ store.pageScopeMap.set(page, pageScopeId)
160
+ return pageScopeId
161
+ }
162
+
163
+ /**
164
+ * 获取 namespace 对应的状态 Map。
165
+ *
166
+ * 传入 namespace 时使用全局 store 中的共享 Map;
167
+ * 不传 namespace 时返回新的本地 Map,表示该 createSharedState 调用完全自持状态。
168
+ */
169
+ function getStateMap<T extends Record<string, any>>(namespace?: string) {
170
+ if (!namespace) return new Map<string, Ref<T>>()
171
+
172
+ const store = getSharedStateStore()
173
+ const stateMap = store.stateMaps.get(namespace)
174
+ if (stateMap) return stateMap as Map<string, Ref<T>>
175
+
176
+ const newStateMap = new Map<string, Ref<T>>()
177
+ store.stateMaps.set(namespace, newStateMap as Map<string, Ref<Record<string, any>>>)
178
+ return newStateMap
179
+ }
180
+
181
+ /**
182
+ * 解析当前页面 scope。
183
+ *
184
+ * 正常情况下取页面栈最后一个页面作为当前页面,并为其生成页面级 scope。
185
+ * 任何平台 API 不存在或异常的情况都会回退到 defaultScopeId,保证调用方始终有可用 scope。
186
+ */
187
+ function resolveCurrentPageScopeId(defaultScopeId: string) {
188
+ try {
189
+ if (typeof getCurrentPages !== 'function') return defaultScopeId
190
+ const pages = getCurrentPages()
191
+ const currentPage = pages[pages.length - 1]
192
+ return currentPage && typeof currentPage === 'object' ? createPageScopeId(currentPage) : defaultScopeId
193
+ } catch {
194
+ return defaultScopeId
195
+ }
196
+ }
197
+
198
+ /**
199
+ * 创建默认状态 key 解析器。
200
+ *
201
+ * 默认 key 由 scopeId 和 selector 拼接:
202
+ * - selector 为空:使用 scopeId,代表当前页面默认实例。
203
+ * - selector 非空:使用 `${scopeId}_${selector}`,代表当前页面指定实例。
204
+ */
205
+ function createDefaultKeyResolver(resolveScopeId: SharedStateScopeResolver): SharedStateKeyResolver {
206
+ return (selector: string, scopeId: string) => {
207
+ const stableScopeId = scopeId || resolveScopeId()
208
+ return selector ? `${stableScopeId}_${selector}` : stableScopeId
209
+ }
210
+ }
211
+
212
+ /**
213
+ * 基于 Map 的模块级共享状态管理,用于替代 inject/provide 模式。
214
+ *
215
+ * 适用于 toast、message、notify 等需要基于 selector 多实例隔离的弹窗组件。
216
+ * 与 inject/provide 相比:
217
+ * - 不依赖组件树,可在任意位置(如 Pinia actions、工具函数)直接读写状态
218
+ * - 通过 selector 区分多实例,与原有 API 兼容
219
+ * - 提供显式清理方法,支持组件卸载时释放资源
220
+ *
221
+ * @param defaultFactory - 默认状态工厂函数,用于初始化新 selector 的状态
222
+ * @returns { getState, initState, removeState }
223
+ *
224
+ * @example
225
+ * ```ts
226
+ * const { initState, getState, removeState } = createSharedState(() => ({ show: false }))
227
+ *
228
+ * // 组件挂载时初始化
229
+ * const state = initState('mySelector')
230
+ *
231
+ * // 外部只读获取
232
+ * const current = getState('mySelector')
233
+ *
234
+ * // 组件卸载时清理
235
+ * onUnmounted(() => removeState('mySelector'))
236
+ * ```
237
+ */
238
+ export function createSharedState<T extends Record<string, any>>(defaultFactory: () => T, options: CreateSharedStateOptions = {}) {
239
+ // 按 namespace 取得组件类型级别的状态容器,例如 toast 和 message-box 分属不同容器。
240
+ const stateMap = getStateMap<T>(options.namespace)
241
+ // 页面 scope 解析失败时使用的默认 scope;有 namespace 时同一组件类型保持稳定。
242
+ const defaultScopeId = createDefaultScopeId(options.namespace)
243
+ // 默认按当前页面对象生成 scope,确保不同页面的默认 selector 不会互相污染。
244
+ const resolveScopeId = options.resolveScopeId || (() => resolveCurrentPageScopeId(defaultScopeId))
245
+ // 默认按 scope + selector 生成最终状态 key,也允许调用方完全自定义。
246
+ const resolveKey = options.resolveKey || createDefaultKeyResolver(resolveScopeId)
247
+ // 统一收口 key 生成逻辑,保证 get/init/remove 使用完全一致的 key。
248
+ const resolveStateKey = (selector: string = '', scopeId: string = resolveScopeId()) => resolveKey(selector, scopeId)
249
+
250
+ /**
251
+ * 获取指定 selector 的共享状态(纯读,不存在返回 undefined)
252
+ * @param selector - 实例标识,空字符串表示默认实例
253
+ * @param scopeId - 指定作用域;不传时自动解析当前页面作用域
254
+ */
255
+ const getState = (selector: string = '', scopeId: string = resolveScopeId()): Ref<T> | undefined => {
256
+ const key = resolveStateKey(selector, scopeId)
257
+ return stateMap.get(key)
258
+ }
259
+
260
+ /**
261
+ * 初始化指定 selector 的共享状态(仅首次创建,已存在则复用)
262
+ * @param selector - 实例标识,空字符串表示默认实例
263
+ * @param scopeId - 指定作用域;不传时自动解析当前页面作用域
264
+ */
265
+ const initState = (selector: string = '', scopeId: string = resolveScopeId()): Ref<T> => {
266
+ const key = resolveStateKey(selector, scopeId)
267
+ if (!stateMap.has(key)) {
268
+ stateMap.set(key, ref(defaultFactory()) as Ref<T>)
269
+ }
270
+ return stateMap.get(key)!
271
+ }
272
+
273
+ /**
274
+ * 移除指定 selector 的共享状态
275
+ * @param selector - 实例标识
276
+ * @param scopeId - 指定作用域;不传时自动解析当前页面作用域
277
+ */
278
+ const removeState = (selector: string = '', scopeId: string = resolveScopeId()): void => {
279
+ const key = resolveStateKey(selector, scopeId)
280
+ stateMap.delete(key)
281
+ }
282
+
283
+ return { getState, initState, removeState, resolveScopeId, resolveStateKey }
284
+ }
@@ -1,5 +1,13 @@
1
1
  import { ref } from 'vue'
2
2
 
3
+ /**
4
+ * 触摸事件处理 Hook,提供触摸方向、位移量的计算能力。
5
+ *
6
+ * 常用于滑动操作组件(如 swipe-action、slider、tabs),
7
+ * 通过 `touchStart` / `touchMove` 记录触摸位移,自动判断滑动方向。
8
+ *
9
+ * @returns 返回触摸状态、方向、位移量及事件处理方法。
10
+ */
3
11
  export function useTouch() {
4
12
  const direction = ref<string>('')
5
13
  const deltaX = ref<number>(0)
@@ -1,6 +1,15 @@
1
1
  import { camelCase, getPropByPath, isDef, isFunction } from '../common/util'
2
2
  import Locale from '../../locale'
3
3
 
4
+ /**
5
+ * 国际化翻译 Hook。
6
+ *
7
+ * 根据传入的组件名称读取 locale 模块中的翻译文案,
8
+ * 支持带参数的动态消息(如 `translate('weekFormat', year, week)`)。
9
+ *
10
+ * @param name 组件名称(驼峰),用作翻译 key 的前缀
11
+ * @returns `{ translate }` 翻译函数
12
+ */
4
13
  export const useTranslate = (name?: string) => {
5
14
  const prefix = name ? camelCase(name) + '.' : ''
6
15
  const translate = (key: string, ...args: unknown[]) => {
@@ -48,6 +48,11 @@ export interface UseUploadOptions {
48
48
  extension?: string[]
49
49
  }
50
50
 
51
+ /**
52
+ * 文件上传 Composable,封装了文件选择、上传任务管理、进度回调等能力
53
+ * 支持自定义上传方法、自动中断前一个上传任务
54
+ * @returns 包含 startUpload / abort / chooseFile / UPLOAD_STATUS 的返回对象
55
+ */
51
56
  export function useUpload(): UseUploadReturn {
52
57
  let currentTask: UniApp.UploadTask | null = null
53
58
 
@@ -207,13 +212,16 @@ export function useUpload(): UseUploadReturn {
207
212
  * 格式化媒体信息
208
213
  */
209
214
  function formatMedia(res: UniApp.ChooseMediaSuccessCallbackResult): ChooseFile[] {
210
- return res.tempFiles.map((item) => ({
211
- type: item.fileType,
212
- path: item.tempFilePath,
213
- thumb: item.fileType === 'video' ? item.thumbTempFilePath : item.tempFilePath,
214
- size: item.size,
215
- duration: item.duration
216
- }))
215
+ return res.tempFiles.map((item) => {
216
+ const mediaType = ((item as any).fileType || (item as any).mediaType || 'image') as 'image' | 'video'
217
+ return {
218
+ type: mediaType,
219
+ path: item.tempFilePath,
220
+ thumb: mediaType === 'video' ? item.thumbTempFilePath : item.tempFilePath,
221
+ size: item.size,
222
+ duration: item.duration
223
+ }
224
+ })
217
225
  }
218
226
 
219
227
  /**
@@ -258,7 +266,7 @@ export function useUpload(): UseUploadReturn {
258
266
  // #endif
259
267
  break
260
268
  case 'video':
261
- // #ifdef MP-WEIXIN
269
+ // #ifdef MP-WEIXIN || MP-TOUTIAO
262
270
  uni.chooseMedia({
263
271
  count: multiple ? maxCount : 1,
264
272
  mediaType: ['video'],
@@ -269,7 +277,7 @@ export function useUpload(): UseUploadReturn {
269
277
  fail: reject
270
278
  })
271
279
  // #endif
272
- // #ifndef MP-WEIXIN
280
+ // #ifndef MP-WEIXIN || MP-TOUTIAO
273
281
  uni.chooseVideo({
274
282
  sourceType,
275
283
  compressed,
@@ -283,10 +291,22 @@ export function useUpload(): UseUploadReturn {
283
291
  })
284
292
  // #endif
285
293
  break
286
- // #ifdef MP-WEIXIN
287
294
  case 'media':
295
+ // #ifdef MP-WEIXIN
296
+ uni.chooseMedia({
297
+ count: multiple ? maxCount : 1,
298
+ sourceType,
299
+ sizeType,
300
+ camera,
301
+ maxDuration,
302
+ success: (res) => resolve(formatMedia(res)),
303
+ fail: reject
304
+ })
305
+ // #endif
306
+ // #ifdef MP-TOUTIAO
288
307
  uni.chooseMedia({
289
308
  count: multiple ? maxCount : 1,
309
+ mediaType: ['image', 'video'],
290
310
  sourceType,
291
311
  sizeType,
292
312
  camera,
@@ -294,8 +314,22 @@ export function useUpload(): UseUploadReturn {
294
314
  success: (res) => resolve(formatMedia(res)),
295
315
  fail: reject
296
316
  })
317
+ // #endif
318
+ // #ifndef MP-WEIXIN || MP-TOUTIAO
319
+ uni.chooseImage({
320
+ count: multiple ? maxCount : 1,
321
+ sizeType,
322
+ sourceType,
323
+ // #ifdef H5
324
+ extension,
325
+ // #endif
326
+ success: (res) => resolve(formatImage(res)),
327
+ fail: reject
328
+ })
329
+ // #endif
297
330
  break
298
331
  case 'file':
332
+ // #ifdef MP-WEIXIN
299
333
  uni.chooseMessageFile({
300
334
  count: multiple ? (isDef(maxCount) ? maxCount : 100) : 1,
301
335
  type: accept,
@@ -303,8 +337,25 @@ export function useUpload(): UseUploadReturn {
303
337
  success: (res) => resolve(res.tempFiles),
304
338
  fail: reject
305
339
  })
340
+ // #endif
341
+ // #ifdef MP-TOUTIAO
342
+ reject({
343
+ errMsg: 'chooseMessageFile is not supported in mp-toutiao'
344
+ })
345
+ // #endif
346
+ // #ifndef MP-WEIXIN || MP-TOUTIAO
347
+ uni.chooseImage({
348
+ count: multiple ? maxCount : 1,
349
+ sizeType,
350
+ sourceType,
351
+ // #ifdef H5
352
+ extension,
353
+ // #endif
354
+ success: (res) => resolve(formatImage(res)),
355
+ fail: reject
356
+ })
357
+ // #endif
306
358
  break
307
- // #endif
308
359
  case 'all':
309
360
  // #ifdef H5
310
361
  uni.chooseFile({
@@ -324,6 +375,27 @@ export function useUpload(): UseUploadReturn {
324
375
  fail: reject
325
376
  })
326
377
  // #endif
378
+ // #ifdef MP-TOUTIAO
379
+ uni.chooseMedia({
380
+ count: multiple ? maxCount : 1,
381
+ mediaType: ['image', 'video'],
382
+ sourceType,
383
+ sizeType,
384
+ camera,
385
+ maxDuration,
386
+ success: (res) => resolve(formatMedia(res)),
387
+ fail: reject
388
+ })
389
+ // #endif
390
+ // #ifndef H5 || MP-WEIXIN || MP-TOUTIAO
391
+ uni.chooseImage({
392
+ count: multiple ? maxCount : 1,
393
+ sizeType,
394
+ sourceType,
395
+ success: (res) => resolve(formatImage(res)),
396
+ fail: reject
397
+ })
398
+ // #endif
327
399
 
328
400
  break
329
401
  default:
@@ -37,6 +37,12 @@ export interface UseVirtualScrollReturn {
37
37
  onScroll: (scrollTopValue: number) => void
38
38
  }
39
39
 
40
+ /**
41
+ * 虚拟滚动 Composable,基于 VirtualScrollEngine 实现高性能长列表渲染
42
+ * 支持虚拟/非虚拟模式切换、滚动到指定位置/元素/ID
43
+ * @param options - 配置项,包含 data、virtual、height、itemHeight、idKey、backToTopThreshold
44
+ * @returns 滚动状态、可见数据及控制方法
45
+ */
40
46
  export function useVirtualScroll(options: UseVirtualScrollOptions): UseVirtualScrollReturn {
41
47
  const { data, virtual, height, itemHeight, idKey, backToTopThreshold } = options
42
48
 
@@ -112,6 +118,7 @@ export function useVirtualScroll(options: UseVirtualScrollOptions): UseVirtualSc
112
118
  }
113
119
  }
114
120
 
121
+ /** 同步程序化滚动状态,更新内部滚动位置、回顶按钮显隐和可见数据 */
115
122
  function syncProgrammaticScrollState(scrollTopValue: number) {
116
123
  _scrollTop.value = scrollTopValue
117
124
  showBackTopBtn.value = scrollTopValue > backToTopThresholdInPx.value
@@ -15,6 +15,15 @@ interface UseWindowResizeOptions {
15
15
  immediate?: boolean
16
16
  }
17
17
 
18
+ /**
19
+ * 监听窗口大小变化。
20
+ *
21
+ * 封装 `uni.onWindowResize` / `uni.offWindowResize`,
22
+ * 在组件挂载时注册监听,卸载时自动移除。
23
+ *
24
+ * @param callback 窗口大小变化时的回调
25
+ * @param options 配置项:`immediate` 是否立即调用一次回调
26
+ */
18
27
  export function useWindowResize(callback: WindowResizeCallback, options: UseWindowResizeOptions = {}) {
19
28
  const { immediate = false } = options
20
29
 
@@ -62,7 +62,7 @@
62
62
  margin-bottom: var(--window-bottom);
63
63
 
64
64
  @include when(float) {
65
- margin: 0 20rpx calc(var(--window-bottom) + 20rpx);
65
+ margin: 0 $-spacing-20 calc(var(--window-bottom) + $-spacing-20);
66
66
  border-radius: $-action-sheet-radius;
67
67
  overflow: hidden;
68
68
  }
@@ -72,7 +72,7 @@
72
72
  }
73
73
 
74
74
  @include e(actions) {
75
- padding: 20rpx 0;
75
+ padding: $-spacing-20 0;
76
76
  max-height: 50vh;
77
77
  overflow-y: auto;
78
78
  -webkit-overflow-scrolling: touch;
@@ -123,7 +123,7 @@
123
123
 
124
124
  @include e(subname) {
125
125
  display: inline-block;
126
- margin-left: 12rpx;
126
+ margin-left: $-spacing-12;
127
127
  font-size: $-action-sheet-subname-fs;
128
128
  color: $-action-sheet-subname-color;
129
129
  }
@@ -140,7 +140,7 @@
140
140
  border: none;
141
141
  background: $-action-sheet-cancel-bg;
142
142
  outline: none;
143
- margin: 0 auto 48rpx;
143
+ margin: 0 auto $-spacing-48;
144
144
  font-weight: $-action-sheet-weight;
145
145
 
146
146
  &:active {
@@ -177,7 +177,7 @@
177
177
  overflow-y: hidden;
178
178
 
179
179
  &:first-of-type {
180
- margin-top: 40rpx;
180
+ margin-top: $-spacing-40;
181
181
  }
182
182
 
183
183
  &:last-of-type {
@@ -59,7 +59,9 @@ export default {
59
59
  name: 'oxy-action-sheet',
60
60
  options: {
61
61
  addGlobalClass: true,
62
+ // #ifndef MP-TOUTIAO
62
63
  virtualHost: true,
64
+ // #endif
63
65
  styleIsolation: 'shared'
64
66
  }
65
67
  }
@@ -105,6 +107,15 @@ function computedValue() {
105
107
  formatPanels.value = isPanelArray() ? [props.panels as Panel[]] : (props.panels as Panel[][])
106
108
  }
107
109
 
110
+ /**
111
+ * 选择操作项/面板项。
112
+ * action 模式:跳过 disabled/loading 项,emit select 事件。
113
+ * panels 模式:区分单层面板和嵌套面板,分别 emit 对应 index。
114
+ * 若 closeOnClickAction 为 true,选择后自动关闭。
115
+ * @param rowIndex - 行索引
116
+ * @param type - 选择类型('action' | 'panels')
117
+ * @param colIndex - 列索引(panels 模式)
118
+ */
108
119
  function select(rowIndex: number, type: 'action' | 'panels', colIndex?: number) {
109
120
  if (type === 'action') {
110
121
  if (props.actions[rowIndex].disabled || props.actions[rowIndex].loading) {
@@ -4,8 +4,8 @@
4
4
  @include b(backtop) {
5
5
  position: fixed;
6
6
  background-color: $-backtop-bg;
7
- width: 80rpx;
8
- height: 80rpx;
7
+ width: $-backtop-size;
8
+ height: $-backtop-size;
9
9
  display: flex;
10
10
  justify-content: center;
11
11
  align-items: center;
@@ -12,7 +12,9 @@ export default {
12
12
  name: 'oxy-backtop',
13
13
  options: {
14
14
  addGlobalClass: true,
15
+ // #ifndef MP-TOUTIAO
15
16
  virtualHost: true,
17
+ // #endif
16
18
  styleIsolation: 'shared'
17
19
  }
18
20
  }
@@ -15,7 +15,9 @@ export default {
15
15
  name: 'oxy-badge',
16
16
  options: {
17
17
  addGlobalClass: true,
18
+ // #ifndef MP-TOUTIAO
18
19
  virtualHost: true,
20
+ // #endif
19
21
  styleIsolation: 'shared'
20
22
  }
21
23
  }
@@ -11,6 +11,9 @@ export const badgeProps = {
11
11
  modelValue: numericProp,
12
12
  /** 当数值为 0 时,是否展示徽标 */
13
13
  showZero: makeBooleanProp(false),
14
+ /**
15
+ * 背景颜色
16
+ */
14
17
  bgColor: String,
15
18
  /**
16
19
  * 最大值,超过最大值会显示 '{max}+',要求 value 是 Number 类型