oxy-uni-ui 1.2.3 → 2.1.0

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 (246) hide show
  1. package/attributes.json +1 -1
  2. package/components/common/abstracts/variable.scss +512 -343
  3. package/components/common/util.ts +185 -32
  4. package/components/composables/index.ts +1 -0
  5. package/components/composables/usePopover.ts +24 -20
  6. package/components/composables/useVirtualScroll.ts +48 -21
  7. package/components/composables/useWindowResize.ts +35 -0
  8. package/components/oxy-action-sheet/index.scss +24 -11
  9. package/components/oxy-action-sheet/oxy-action-sheet.vue +27 -19
  10. package/components/oxy-action-sheet/types.ts +7 -0
  11. package/components/oxy-backtop/index.scss +4 -4
  12. package/components/oxy-backtop/oxy-backtop.vue +9 -6
  13. package/components/oxy-backtop/types.ts +7 -7
  14. package/components/oxy-badge/index.scss +4 -4
  15. package/components/oxy-badge/oxy-badge.vue +3 -3
  16. package/components/oxy-badge/types.ts +2 -2
  17. package/components/oxy-button/index.scss +5 -5
  18. package/components/oxy-button/oxy-button.vue +5 -1
  19. package/components/oxy-calendar/index.scss +15 -15
  20. package/components/oxy-calendar/oxy-calendar.vue +1 -0
  21. package/components/oxy-calendar/types.ts +5 -0
  22. package/components/oxy-calendar-view/month/index.scss +4 -4
  23. package/components/oxy-calendar-view/month/types.ts +36 -0
  24. package/components/oxy-calendar-view/monthPanel/index.scss +7 -8
  25. package/components/oxy-calendar-view/monthPanel/month-panel.vue +14 -8
  26. package/components/oxy-calendar-view/year/index.scss +5 -5
  27. package/components/oxy-calendar-view/yearPanel/index.scss +4 -4
  28. package/components/oxy-calendar-view/yearPanel/year-panel.vue +21 -5
  29. package/components/oxy-card/index.scss +2 -2
  30. package/components/oxy-cell/index.scss +8 -8
  31. package/components/oxy-checkbox/index.scss +12 -12
  32. package/components/oxy-checkbox-group/index.scss +2 -2
  33. package/components/oxy-circle/oxy-circle.vue +10 -7
  34. package/components/oxy-circle/types.ts +5 -5
  35. package/components/oxy-col/oxy-col.vue +2 -2
  36. package/components/oxy-col-picker/index.scss +4 -4
  37. package/components/oxy-col-picker/oxy-col-picker.vue +6 -5
  38. package/components/oxy-col-picker/types.ts +7 -2
  39. package/components/oxy-collapse/index.scss +2 -2
  40. package/components/oxy-collapse-item/oxy-collapse-item.vue +3 -3
  41. package/components/oxy-corner/index.scss +33 -33
  42. package/components/oxy-count-to/oxy-count-to.vue +3 -3
  43. package/components/oxy-curtain/index.scss +15 -15
  44. package/components/oxy-curtain/oxy-curtain.vue +4 -2
  45. package/components/oxy-curtain/types.ts +6 -1
  46. package/components/oxy-date-strip/oxy-date-strip.vue +2 -2
  47. package/components/oxy-date-strip/types.ts +1 -1
  48. package/components/oxy-date-strip-item/index.scss +3 -3
  49. package/components/oxy-datetime-picker/index.scss +11 -11
  50. package/components/oxy-datetime-picker/oxy-datetime-picker.vue +1 -0
  51. package/components/oxy-datetime-picker/types.ts +5 -0
  52. package/components/oxy-drop-menu/index.scss +5 -5
  53. package/components/oxy-drop-menu/oxy-drop-menu.vue +3 -3
  54. package/components/oxy-drop-menu-item/index.scss +3 -3
  55. package/components/oxy-drop-menu-item/oxy-drop-menu-item.vue +4 -3
  56. package/components/oxy-drop-menu-item/types.ts +5 -0
  57. package/components/oxy-echarts/types.ts +6 -0
  58. package/components/oxy-fab/index.scss +8 -8
  59. package/components/oxy-fab/oxy-fab.vue +22 -3
  60. package/components/oxy-file-list/index.scss +30 -29
  61. package/components/oxy-file-list/oxy-file-list.vue +2 -2
  62. package/components/oxy-floating-panel/oxy-floating-panel.vue +13 -9
  63. package/components/oxy-floating-panel/{type.ts → types.ts} +8 -8
  64. package/components/oxy-footer/index.scss +19 -0
  65. package/components/oxy-footer/oxy-footer.vue +78 -0
  66. package/components/oxy-footer/types.ts +17 -0
  67. package/components/oxy-form-item/types.ts +22 -1
  68. package/components/oxy-gap/oxy-gap.vue +2 -2
  69. package/components/oxy-gap/types.ts +2 -2
  70. package/components/oxy-grid/oxy-grid.vue +1 -1
  71. package/components/oxy-grid/types.ts +1 -1
  72. package/components/oxy-grid-item/index.scss +1 -1
  73. package/components/oxy-grid-item/oxy-grid-item.vue +7 -5
  74. package/components/oxy-grid-item/types.ts +1 -1
  75. package/components/oxy-guidance/index.scss +75 -0
  76. package/components/oxy-guidance/oxy-guidance.vue +201 -0
  77. package/components/oxy-guidance/types.ts +33 -0
  78. package/components/oxy-icon/oxy-icon.vue +2 -2
  79. package/components/oxy-icon/types.ts +1 -1
  80. package/components/oxy-img/oxy-img.vue +4 -4
  81. package/components/oxy-img/types.ts +3 -3
  82. package/components/oxy-img-cropper/index.scss +23 -23
  83. package/components/oxy-img-cropper/oxy-img-cropper.vue +97 -52
  84. package/components/oxy-img-cropper/types.ts +2 -2
  85. package/components/oxy-img-lazy/oxy-img-lazy.vue +3 -3
  86. package/components/oxy-img-lazy/types.ts +3 -3
  87. package/components/oxy-index-anchor/index.scss +2 -2
  88. package/components/oxy-index-anchor/oxy-index-anchor.vue +2 -2
  89. package/components/oxy-index-anchor/{type.ts → types.ts} +3 -0
  90. package/components/oxy-index-bar/index.scss +3 -3
  91. package/components/oxy-index-bar/oxy-index-bar.vue +3 -3
  92. package/components/oxy-index-bar/{type.ts → types.ts} +2 -2
  93. package/components/oxy-input/index.scss +1 -1
  94. package/components/oxy-input-number/index.scss +5 -5
  95. package/components/oxy-input-number/oxy-input-number.vue +2 -2
  96. package/components/oxy-input-number/types.ts +3 -2
  97. package/components/oxy-keyboard/index.scss +5 -5
  98. package/components/oxy-keyboard/key/index.scss +3 -3
  99. package/components/oxy-keyboard/key/index.vue +2 -2
  100. package/components/oxy-keyboard/key/types.ts +15 -0
  101. package/components/oxy-keyboard/oxy-keyboard.vue +1 -0
  102. package/components/oxy-keyboard/types.ts +5 -0
  103. package/components/oxy-link/index.scss +2 -2
  104. package/components/oxy-list/oxy-list.vue +4 -3
  105. package/components/oxy-loading/oxy-loading.vue +8 -4
  106. package/components/oxy-loading/types.ts +1 -1
  107. package/components/oxy-loadmore/index.scss +3 -3
  108. package/components/oxy-long-press-menu/index.scss +93 -0
  109. package/components/oxy-long-press-menu/oxy-long-press-menu.vue +338 -0
  110. package/components/oxy-long-press-menu/types.ts +34 -0
  111. package/components/oxy-message-box/index.scss +12 -11
  112. package/components/oxy-message-box/oxy-message-box.vue +9 -2
  113. package/components/oxy-message-box/types.ts +9 -0
  114. package/components/oxy-navbar/index.scss +2 -2
  115. package/components/oxy-navbar/oxy-navbar.vue +58 -13
  116. package/components/oxy-navbar/types.ts +8 -1
  117. package/components/oxy-navbar-capsule/types.ts +3 -0
  118. package/components/oxy-notice-bar/index.scss +3 -3
  119. package/components/oxy-notice-bar/oxy-notice-bar.vue +9 -5
  120. package/components/oxy-notice-bar/types.ts +3 -3
  121. package/components/oxy-notify/index.ts +1 -0
  122. package/components/oxy-notify/oxy-notify.vue +3 -2
  123. package/components/oxy-notify/types.ts +7 -0
  124. package/components/oxy-pagination/index.scss +6 -5
  125. package/components/oxy-password-input/oxy-password-input.vue +2 -2
  126. package/components/oxy-password-input/types.ts +1 -1
  127. package/components/oxy-picker/index.scss +45 -2
  128. package/components/oxy-picker/oxy-picker.vue +100 -14
  129. package/components/oxy-picker/types.ts +29 -1
  130. package/components/oxy-picker-view/index.scss +4 -4
  131. package/components/oxy-picker-view/oxy-picker-view.vue +4 -4
  132. package/components/oxy-popover/index.scss +13 -13
  133. package/components/oxy-popup/index.scss +4 -4
  134. package/components/oxy-popup/oxy-popup.vue +35 -2
  135. package/components/oxy-popup/types.ts +8 -1
  136. package/components/oxy-progress/index.scss +3 -3
  137. package/components/oxy-qrcode/draw.ts +398 -0
  138. package/components/oxy-qrcode/index.scss +2 -0
  139. package/components/oxy-qrcode/oxy-qrcode.vue +124 -0
  140. package/components/oxy-qrcode/qrcode.ts +936 -0
  141. package/components/oxy-qrcode/types.ts +42 -0
  142. package/components/oxy-radio/index.scss +25 -19
  143. package/components/oxy-radio-group/index.scss +2 -2
  144. package/components/oxy-rate/types.ts +4 -4
  145. package/components/oxy-resize/index.scss +2 -2
  146. package/components/oxy-resize/oxy-resize.vue +4 -4
  147. package/components/oxy-resize/types.ts +3 -0
  148. package/components/oxy-rich-text/index.scss +37 -36
  149. package/components/oxy-rich-text/mp-html/card/card.vue +3 -3
  150. package/components/oxy-rich-text/mp-html/mp-html.vue +33 -24
  151. package/components/oxy-rich-text/mp-html/node/node.vue +30 -19
  152. package/components/oxy-rich-text/oxy-rich-text.vue +31 -31
  153. package/components/oxy-rich-text/types.ts +6 -1
  154. package/components/oxy-row/oxy-row.vue +3 -3
  155. package/components/oxy-row/types.ts +1 -1
  156. package/components/oxy-search/index.scss +7 -7
  157. package/components/oxy-segmented/index.scss +19 -16
  158. package/components/oxy-segmented/oxy-segmented.vue +23 -3
  159. package/components/oxy-select/index.scss +213 -89
  160. package/components/oxy-select/oxy-select.vue +106 -58
  161. package/components/oxy-select/types.ts +13 -1
  162. package/components/oxy-select-picker/index.scss +7 -7
  163. package/components/oxy-select-picker/oxy-select-picker.vue +1 -0
  164. package/components/oxy-select-picker/types.ts +2 -0
  165. package/components/oxy-sidebar-item/index.scss +2 -2
  166. package/components/oxy-signature/oxy-signature.vue +18 -10
  167. package/components/oxy-signature/types.ts +106 -13
  168. package/components/oxy-skeleton/index.scss +1 -1
  169. package/components/oxy-skeleton/oxy-skeleton.vue +6 -6
  170. package/components/oxy-skeleton/types.ts +1 -1
  171. package/components/oxy-slider/index.scss +6 -6
  172. package/components/oxy-sort-button/index.scss +8 -8
  173. package/components/oxy-splitter/index.scss +19 -0
  174. package/components/oxy-splitter/oxy-splitter.vue +409 -0
  175. package/components/oxy-splitter/types.ts +75 -0
  176. package/components/oxy-splitter-panel/index.scss +366 -0
  177. package/components/oxy-splitter-panel/oxy-splitter-panel.vue +432 -0
  178. package/components/oxy-splitter-panel/types.ts +63 -0
  179. package/components/oxy-status-tip/index.scss +4 -4
  180. package/components/oxy-status-tip/oxy-status-tip.vue +5 -5
  181. package/components/oxy-status-tip/types.ts +3 -3
  182. package/components/oxy-step/index.scss +16 -16
  183. package/components/oxy-sticky/oxy-sticky.vue +6 -6
  184. package/components/oxy-stream-render/oxy-stream-render.vue +230 -4
  185. package/components/oxy-stream-render/types.ts +4 -1
  186. package/components/oxy-swipe-action/oxy-swipe-action.vue +27 -2
  187. package/components/oxy-swiper/oxy-swiper.vue +6 -6
  188. package/components/oxy-swiper/types.ts +5 -5
  189. package/components/oxy-swiper-nav/index.scss +3 -3
  190. package/components/oxy-switch/index.scss +10 -10
  191. package/components/oxy-switch/oxy-switch.vue +2 -2
  192. package/components/oxy-switch/types.ts +1 -1
  193. package/components/oxy-tab/index.scss +11 -1
  194. package/components/oxy-tabbar/index.scss +2 -2
  195. package/components/oxy-tabbar/oxy-tabbar.vue +39 -10
  196. package/components/oxy-table/index.scss +8 -8
  197. package/components/oxy-table/oxy-table.vue +8 -6
  198. package/components/oxy-table/types.ts +2 -2
  199. package/components/oxy-table-col/index.scss +3 -3
  200. package/components/oxy-table-col/oxy-table-col.vue +3 -3
  201. package/components/oxy-table-col/types.ts +2 -2
  202. package/components/oxy-tabs/index.scss +52 -22
  203. package/components/oxy-tabs/oxy-tabs.vue +53 -19
  204. package/components/oxy-tabs/types.ts +15 -3
  205. package/components/oxy-tag/index.scss +111 -36
  206. package/components/oxy-text/index.scss +5 -1
  207. package/components/oxy-text/oxy-text.vue +76 -7
  208. package/components/oxy-text/types.ts +12 -0
  209. package/components/oxy-textarea/index.scss +6 -6
  210. package/components/oxy-toast/oxy-toast.vue +24 -8
  211. package/components/oxy-tooltip/index.scss +9 -9
  212. package/components/oxy-tree/index.scss +51 -15
  213. package/components/oxy-tree/oxy-tree.vue +13 -9
  214. package/components/oxy-tree/types.ts +12 -9
  215. package/components/oxy-upload/index.scss +23 -23
  216. package/components/oxy-upload/types.ts +2 -2
  217. package/components/oxy-verification-code/index.scss +6 -0
  218. package/components/oxy-verification-code/oxy-verification-code.vue +187 -0
  219. package/components/oxy-verification-code/types.ts +82 -0
  220. package/components/oxy-video-preview/index.scss +4 -4
  221. package/components/oxy-virtual-scroll/index.scss +5 -5
  222. package/components/oxy-virtual-scroll/oxy-virtual-scroll.vue +11 -7
  223. package/components/oxy-virtual-scroll/types.ts +14 -14
  224. package/components/oxy-voice-player/index.scss +937 -0
  225. package/components/oxy-voice-player/oxy-voice-player.vue +821 -0
  226. package/components/oxy-voice-player/types.ts +567 -0
  227. package/components/oxy-waterfall/oxy-waterfall.vue +6 -6
  228. package/components/oxy-waterfall/types.ts +6 -6
  229. package/components/oxy-watermark/oxy-watermark.vue +35 -13
  230. package/components/oxy-watermark/types.ts +14 -14
  231. package/global.d.ts +4 -0
  232. package/locale/lang/ar-SA.ts +3 -0
  233. package/locale/lang/en-US.ts +3 -0
  234. package/locale/lang/zh-CN.ts +3 -0
  235. package/package.json +97 -1
  236. package/tags.json +1 -1
  237. package/web-types.json +1 -1
  238. package/components/oxy-number-keyboard/index.scss +0 -78
  239. package/components/oxy-number-keyboard/key/index.scss +0 -81
  240. package/components/oxy-number-keyboard/key/index.vue +0 -78
  241. package/components/oxy-number-keyboard/key/types.ts +0 -11
  242. package/components/oxy-number-keyboard/oxy-number-keyboard.vue +0 -151
  243. package/components/oxy-number-keyboard/types.ts +0 -83
  244. package/components/oxy-tree/components/tree-node-content.vue +0 -72
  245. package/components/oxy-tree/index.ts +0 -51
  246. package/oxy-uni-ui.zip +0 -0
@@ -16,46 +16,199 @@ function s4() {
16
16
  .substring(1)
17
17
  }
18
18
 
19
+ type UnitConvertOutput = 'number' | 'px'
20
+ type DefaultUnit = 'px' | 'rpx'
21
+
19
22
  /**
20
- * @description 对num自动填充px
21
- * @param {Number} num
22
- * @return {string} num+px
23
+ * 通用单位转换函数。
24
+ *
25
+ * 支持输入:
26
+ * - `number`、纯数字字符串(如 `'12'`)
27
+ * - 带单位字符串:`px` / `rpx` / `%` / `em`
28
+ * - CSS 关键字(仅在 `output='px'` 且 `preserveCssKeyword=true` 时原样透传)
29
+ *
30
+ * 转换规则:
31
+ * - `output='number'`(默认):返回数值(px 语义)。
32
+ * - `rpx` -> `uni.upx2px`
33
+ * - `px` -> 原数值
34
+ * - `%` -> 按 `base` 计算
35
+ * - `output='px'`:返回可直接用于内联样式的字符串。
36
+ * - 数字/纯数字字符串 -> `${value}px`
37
+ * - 可识别单位按规则换算后输出 `${n}px`
38
+ * - 关键字(如 `auto`)按配置透传
39
+ *
40
+ * @param value 待转换的值。
41
+ * @param base 百分比计算基准,仅在输入为 `%` 时生效。
42
+ * @param options 转换配置:
43
+ * - `output`:`'number' | 'px'`
44
+ * - `preserveCssKeyword`:`output='px'` 时是否透传 CSS 关键字,默认 `true`
45
+ * @returns `output='number'` 时返回 `number`;`output='px'` 时返回 `string`。
46
+ */
47
+ export function unitConvert<T extends UnitConvertOutput = 'number'>(
48
+ value: string | number | null | undefined,
49
+ base: number = 0,
50
+ options: { output?: T; preserveCssKeyword?: boolean } = {}
51
+ ): T extends 'px' ? string : number {
52
+ const output = options.output ?? 'number'
53
+ const preserveCssKeyword = options.preserveCssKeyword ?? true
54
+
55
+ const toNumber = (input: string | number | null | undefined): number => {
56
+ // 如果是字符串数字
57
+ if (isNumeric(input)) {
58
+ return Number(input)
59
+ }
60
+ // 如果有单位
61
+ if (isString(input)) {
62
+ const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g
63
+ const results = reg.exec(input)
64
+ if (!input || !results) {
65
+ return 0
66
+ }
67
+ const unit = results[3]
68
+ const _value = parseFloat(input)
69
+ if (unit === 'rpx') {
70
+ return uni.upx2px(_value)
71
+ }
72
+ if (unit === 'px') {
73
+ return _value * 1
74
+ }
75
+ if (unit == '%') {
76
+ return (_value / 100) * base
77
+ }
78
+ // 如果是其他单位,可以继续添加对应的转换逻辑
79
+ }
80
+ return 0
81
+ }
82
+
83
+ if (output === 'px') {
84
+ if (value === null || value === undefined || value === '') {
85
+ return '0px' as T extends 'px' ? string : number
86
+ }
87
+
88
+ if (isNumber(value)) {
89
+ return `${value}px` as T extends 'px' ? string : number
90
+ }
91
+
92
+ if (isString(value)) {
93
+ const trimmed = value.trim()
94
+
95
+ if (!trimmed) {
96
+ return '0px' as T extends 'px' ? string : number
97
+ }
98
+
99
+ if (
100
+ preserveCssKeyword &&
101
+ (trimmed === 'auto' || trimmed === 'inherit' || trimmed === 'initial' || trimmed === 'unset' || trimmed.endsWith('%'))
102
+ ) {
103
+ return trimmed as T extends 'px' ? string : number
104
+ }
105
+
106
+ if (isNumeric(trimmed)) {
107
+ return `${Number(trimmed)}px` as T extends 'px' ? string : number
108
+ }
109
+
110
+ const convertedValue = toNumber(trimmed)
111
+
112
+ if (convertedValue || /^-?0(\.0+)?(rpx|px)?$/.test(trimmed)) {
113
+ return `${convertedValue}px` as T extends 'px' ? string : number
114
+ }
115
+
116
+ return trimmed as T extends 'px' ? string : number
117
+ }
118
+
119
+ return `${toNumber(value)}px` as T extends 'px' ? string : number
120
+ }
121
+
122
+ return toNumber(value) as T extends 'px' ? string : number
123
+ }
124
+
125
+ /**
126
+ * 为“无单位值”补充默认单位。
127
+ *
128
+ * 行为说明:
129
+ * - `number` -> `${value}${defaultUnit}`
130
+ * - 纯数字字符串 -> `${value}${defaultUnit}`
131
+ * - `null/undefined/''` -> `0${defaultUnit}`
132
+ * - 已带单位或其他字符串(如 `300rpx`、`20px`、`50%`、`auto`)原样返回
133
+ *
134
+ * 典型用途:
135
+ * - 统一“number 与纯数字字符串”的默认单位语义(如统一按 `rpx`)。
136
+ *
137
+ * @param value 输入值。
138
+ * @param defaultUnit 默认单位,支持 `'px' | 'rpx'`,默认 `'rpx'`。
139
+ * @returns 补全单位后的字符串结果。
140
+ */
141
+ export function withDefaultUnit(value: number | string | null | undefined, defaultUnit: DefaultUnit = 'rpx') {
142
+ if (value === null || value === undefined) {
143
+ return `0${defaultUnit}`
144
+ }
145
+
146
+ if (isNumber(value)) {
147
+ return `${value}${defaultUnit}`
148
+ }
149
+
150
+ const trimmed = value.trim()
151
+
152
+ if (!trimmed) {
153
+ return `0${defaultUnit}`
154
+ }
155
+
156
+ if (isNumeric(trimmed)) {
157
+ return `${trimmed}${defaultUnit}`
158
+ }
159
+
160
+ return trimmed
161
+ }
162
+
163
+ /**
164
+ * 先补默认单位,再执行 `unitConvert`,用于得到稳定的数值结果。
165
+ *
166
+ * 等价逻辑:
167
+ * `unitConvert(withDefaultUnit(value, defaultUnit), base)`
168
+ *
169
+ * 适用场景:
170
+ * - 需要做数值比较(如滚动阈值判断),同时希望 `number`/纯数字字符串有明确默认单位语义。
171
+ *
172
+ * @param value 输入值。
173
+ * @param options 配置项:
174
+ * - `defaultUnit`:无单位值补充的默认单位,默认 `'rpx'`
175
+ * - `base`:百分比换算基准,默认 `0`
176
+ * @returns 转换后的数值(px 语义)。
23
177
  */
24
- export function addUnit(num: number | string) {
25
- return Number.isNaN(Number(num)) ? `${num}` : `${num}px`
178
+ export function unitConvertWithDefault(value: number | string | null | undefined, options: { defaultUnit?: DefaultUnit; base?: number } = {}) {
179
+ const { defaultUnit = 'rpx', base = 0 } = options
180
+ return unitConvert(withDefaultUnit(value, defaultUnit), base)
26
181
  }
27
182
 
28
183
  /**
29
- * 单位转换函数,将字符串数字或带有单位的字符串转换为数字
30
- * @param value 要转换的值,可以是字符串数字或带有单位的字符串
31
- * @returns 转换后的数字,如果无法转换则返回0
184
+ * 将长度值解析为样式字符串,仅对 `%` 做屏宽换算。
185
+ *
186
+ * 行为说明:
187
+ * - `number`/纯数字字符串:按 `defaultUnit` 补单位(默认补 `rpx`)
188
+ * - `%`:按 `uni.getSystemInfoSync().windowWidth` 换算后转为 `px`
189
+ * - 其他带单位值(如 `px/rpx`):原样返回
190
+ *
191
+ * @param value 输入长度值。
192
+ * @param options 配置项:
193
+ * - `defaultUnit`:无单位值的默认单位,默认 `'rpx'`
194
+ * @returns 解析后的样式值字符串;当输入为空时返回空字符串。
32
195
  */
33
- export function unitConvert(value: string | number | null | undefined, base: number = 0): number {
34
- // 如果是字符串数字
35
- if (isNumeric(value)) {
36
- return Number(value)
196
+ export function resolveSizeWithScreenWidth(value: number | string | null | undefined, options: { defaultUnit?: DefaultUnit } = {}) {
197
+ if (value === '' || value === undefined || value === null) {
198
+ return ''
37
199
  }
38
- // 如果有单位
39
- if (isString(value)) {
40
- const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g
41
- const results = reg.exec(value)
42
- if (!value || !results) {
43
- return 0
44
- }
45
- const unit = results[3]
46
- const _value = parseFloat(value)
47
- if (unit === 'rpx') {
48
- return uni.upx2px(_value)
49
- }
50
- if (unit === 'px') {
51
- return _value * 1
52
- }
53
- if (unit == '%') {
54
- return (_value / 100) * base
55
- }
56
- // 如果是其他单位,可以继续添加对应的转换逻辑
200
+
201
+ const { defaultUnit = 'rpx' } = options
202
+ const normalized = withDefaultUnit(value, defaultUnit)
203
+ const trimmed = normalized.trim()
204
+
205
+ if (trimmed.endsWith('%')) {
206
+ const { windowWidth = 0 } = uni.getSystemInfoSync()
207
+ const percentInPx = unitConvert(trimmed, windowWidth)
208
+ return `${percentInPx * 2}rpx`
57
209
  }
58
- return 0
210
+
211
+ return normalized
59
212
  }
60
213
 
61
214
  /**
@@ -10,3 +10,4 @@ export { useTouch } from './useTouch'
10
10
  export { useTranslate } from './useTranslate'
11
11
  export { useUpload } from './useUpload'
12
12
  export { useVirtualScroll } from './useVirtualScroll'
13
+ export { useWindowResize } from './useWindowResize'
@@ -1,5 +1,5 @@
1
1
  import { getCurrentInstance, ref } from 'vue'
2
- import { getRect, isObj } from '../common/util'
2
+ import { getRect, isObj, unitConvert } from '../common/util'
3
3
 
4
4
  export function usePopover(visibleArrow = true) {
5
5
  const { proxy } = getCurrentInstance() as any
@@ -14,6 +14,7 @@ export function usePopover(visibleArrow = true) {
14
14
  const width = ref<number>(0)
15
15
  const height = ref<number>(0)
16
16
  const top = ref<number>(0)
17
+ const toPx = (value: number) => unitConvert(value, 0, { output: 'px' })
17
18
 
18
19
  function noop() {}
19
20
 
@@ -107,64 +108,67 @@ export function usePopover(visibleArrow = true) {
107
108
 
108
109
  const placements = new Map([
109
110
  // 上
110
- ['top', [`left: ${verticalX}px; bottom: ${verticalY}px; transform: translateX(-50%);`, 'left: 50%;']],
111
+ ['top', [`left: ${toPx(verticalX)}; bottom: ${toPx(verticalY)}; transform: translateX(-50%);`, 'left: 50%;']],
111
112
  [
112
113
  'top-start',
113
114
  [
114
- `left: ${offsetX}px; bottom: ${verticalY}px;`,
115
- `left: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px;`
115
+ `left: ${toPx(offsetX)}; bottom: ${toPx(verticalY)};`,
116
+ `left: ${toPx((popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX)};`
116
117
  ]
117
118
  ],
118
119
  [
119
120
  'top-end',
120
121
  [
121
- `right: ${offsetX}px; bottom: ${verticalY}px;`,
122
- `right: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px; transform: translateX(50%);`
122
+ `right: ${toPx(offsetX)}; bottom: ${toPx(verticalY)};`,
123
+ `right: ${toPx((popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX)}; transform: translateX(50%);`
123
124
  ]
124
125
  ],
125
126
  // 下
126
- ['bottom', [`left: ${verticalX}px; top: ${verticalY}px; transform: translateX(-50%);`, 'left: 50%;']],
127
+ ['bottom', [`left: ${toPx(verticalX)}; top: ${toPx(verticalY)}; transform: translateX(-50%);`, 'left: 50%;']],
127
128
  [
128
129
  'bottom-start',
129
- [`left: ${offsetX}px; top: ${verticalY}px;`, `left: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px;`]
130
+ [
131
+ `left: ${toPx(offsetX)}; top: ${toPx(verticalY)};`,
132
+ `left: ${toPx((popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX)};`
133
+ ]
130
134
  ],
131
135
  [
132
136
  'bottom-end',
133
137
  [
134
- `right: ${offsetX}px; top: ${verticalY}px;`,
135
- `right: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px; transform: translateX(50%);`
138
+ `right: ${toPx(offsetX)}; top: ${toPx(verticalY)};`,
139
+ `right: ${toPx((popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX)}; transform: translateX(50%);`
136
140
  ]
137
141
  ],
138
142
  // 左
139
- ['left', [`right: ${horizontalX}px; top: ${horizontalY}px; transform: translateY(-50%);`, 'top: 50%']],
143
+ ['left', [`right: ${toPx(horizontalX)}; top: ${toPx(horizontalY)}; transform: translateY(-50%);`, 'top: 50%']],
140
144
  [
141
145
  'left-start',
142
146
  [
143
- `right: ${horizontalX}px; top: ${offsetY}px;`,
144
- `top: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px;`
147
+ `right: ${toPx(horizontalX)}; top: ${toPx(offsetY)};`,
148
+ `top: ${toPx((popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY)};`
145
149
  ]
146
150
  ],
147
151
  [
148
152
  'left-end',
149
153
  [
150
- `right: ${horizontalX}px; bottom: ${offsetY}px;`,
151
- `bottom: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px; transform: translateY(50%);`
154
+ `right: ${toPx(horizontalX)}; bottom: ${toPx(offsetY)};`,
155
+ `bottom: ${toPx((popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY)}; transform: translateY(50%);`
152
156
  ]
153
157
  ],
154
158
  // 右
155
- ['right', [`left: ${horizontalX}px; top: ${horizontalY}px; transform: translateY(-50%);`, 'top: 50%']],
159
+ ['right', [`left: ${toPx(horizontalX)}; top: ${toPx(horizontalY)}; transform: translateY(-50%);`, 'top: 50%']],
156
160
  [
157
161
  'right-start',
158
162
  [
159
- `left: ${horizontalX}px; top: ${offsetY}px;`,
160
- `top: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px;`
163
+ `left: ${toPx(horizontalX)}; top: ${toPx(offsetY)};`,
164
+ `top: ${toPx((popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY)};`
161
165
  ]
162
166
  ],
163
167
  [
164
168
  'right-end',
165
169
  [
166
- `left: ${horizontalX}px; bottom: ${offsetY}px;`,
167
- `bottom: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px; transform: translateY(50%);`
170
+ `left: ${toPx(horizontalX)}; bottom: ${toPx(offsetY)};`,
171
+ `bottom: ${toPx((popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY)}; transform: translateY(50%);`
168
172
  ]
169
173
  ]
170
174
  ])
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { ref, computed, watch, nextTick, type Ref } from 'vue'
6
6
  import { VirtualScrollEngine } from '../oxy-virtual-scroll/virtual-scroll'
7
+ import { unitConvert, unitConvertWithDefault } from '../common/util'
7
8
 
8
9
  export interface UseVirtualScrollOptions {
9
10
  data: Ref<any[]>
@@ -52,7 +53,10 @@ export function useVirtualScroll(options: UseVirtualScrollOptions): UseVirtualSc
52
53
  const displayData = computed<any[]>(() => data.value)
53
54
 
54
55
  const totalHeight = computed(() => {
55
- return displayData.value.length * parseFloat(itemHeight.value)
56
+ return displayData.value.length * unitConvertWithDefault(itemHeight.value, { defaultUnit: 'rpx' })
57
+ })
58
+ const backToTopThresholdInPx = computed(() => {
59
+ return unitConvertWithDefault(backToTopThreshold.value, { defaultUnit: 'rpx' })
56
60
  })
57
61
 
58
62
  // 监听数据变化
@@ -72,13 +76,14 @@ export function useVirtualScroll(options: UseVirtualScrollOptions): UseVirtualSc
72
76
  if (!virtual.value) {
73
77
  // 非虚拟滚动模式:直接使用全部数据
74
78
  virtualData.value = displayData.value
79
+ startIndex.value = 0
75
80
  virtualOffsetY.value = 0
76
81
  return
77
82
  }
78
83
 
79
84
  virtualEngine.value = new VirtualScrollEngine({
80
- containerHeight: parseFloat(height.value),
81
- itemHeight: parseFloat(itemHeight.value),
85
+ containerHeight: unitConvert(height.value),
86
+ itemHeight: unitConvertWithDefault(itemHeight.value, { defaultUnit: 'rpx' }),
82
87
  data: displayData.value
83
88
  })
84
89
  updateVisibleData()
@@ -89,8 +94,8 @@ export function useVirtualScroll(options: UseVirtualScrollOptions): UseVirtualSc
89
94
  if (!virtual.value) return
90
95
 
91
96
  virtualEngine.value = new VirtualScrollEngine({
92
- containerHeight: parseFloat(height.value),
93
- itemHeight: parseFloat(itemHeight.value),
97
+ containerHeight: unitConvert(height.value),
98
+ itemHeight: unitConvertWithDefault(itemHeight.value, { defaultUnit: 'rpx' }),
94
99
  data: displayData.value
95
100
  })
96
101
  }
@@ -100,52 +105,74 @@ export function useVirtualScroll(options: UseVirtualScrollOptions): UseVirtualSc
100
105
  if (!virtual.value) return // 非虚拟模式不处理
101
106
 
102
107
  if (virtualEngine.value) {
103
- const { visibleData, offsetY } = virtualEngine.value.updateVisibleData(_scrollTop.value || 0)
108
+ const { visibleData, offsetY, startIndex: engineStartIndex } = virtualEngine.value.updateVisibleData(_scrollTop.value || 0)
104
109
  virtualData.value = visibleData
110
+ startIndex.value = engineStartIndex
105
111
  virtualOffsetY.value = offsetY
106
112
  }
107
113
  }
108
114
 
115
+ function syncProgrammaticScrollState(scrollTopValue: number) {
116
+ _scrollTop.value = scrollTopValue
117
+ showBackTopBtn.value = scrollTopValue > backToTopThresholdInPx.value
118
+ updateVisibleData()
119
+ }
120
+
109
121
  // 滚动事件处理
110
122
  function onScroll(scrollTopValue: number) {
111
123
  _scrollTop.value = scrollTopValue
112
- showBackTopBtn.value = scrollTopValue > parseFloat(backToTopThreshold.value)
124
+ showBackTopBtn.value = scrollTopValue > backToTopThresholdInPx.value
113
125
  updateVisibleData()
114
126
  }
115
127
 
116
128
  // 滚动到顶部
117
129
  function scrollToTop() {
118
- scrollTop.value = 0
119
- nextTick(() => {
120
- scrollTop.value = 0
121
- })
130
+ scrollToPosition('0px')
122
131
  }
123
132
 
124
133
  // 滚动到底部
125
134
  function scrollToBottom() {
126
- scrollToPosition(totalHeight.value)
135
+ scrollToPosition(`${totalHeight.value}px`)
127
136
  }
128
137
 
129
138
  // 滚动到指定位置
130
139
  function scrollToPosition(position: number | string) {
131
- scrollTop.value = typeof position === 'number' ? position : parseFloat(position)
140
+ const targetScrollTop = unitConvertWithDefault(position, { defaultUnit: 'rpx' })
141
+
142
+ // scroll-view 在绑定值重复时可能不触发滚动,先同步当前真实滚动值再设置目标值
143
+ if (scrollTop.value === targetScrollTop) {
144
+ scrollTop.value = _scrollTop.value
145
+ nextTick(() => {
146
+ scrollTop.value = targetScrollTop
147
+ syncProgrammaticScrollState(targetScrollTop)
148
+ })
149
+ return
150
+ }
151
+
152
+ scrollTop.value = targetScrollTop
153
+ syncProgrammaticScrollState(targetScrollTop)
132
154
  }
133
155
 
134
156
  // 滚动到指定元素
135
157
  function scrollToElement(item: any) {
136
- const index = data.value.findIndex((o) => item[idKey.value] && o[idKey.value] && o[idKey.value] === item[idKey.value])
137
- if (index > 0) {
138
- const scrollDistance = parseFloat(itemHeight.value) * index
139
- scrollToPosition(scrollDistance)
158
+ const targetId = item?.[idKey.value]
159
+ if (targetId === undefined || targetId === null) return
160
+
161
+ const index = data.value.findIndex((o) => o?.[idKey.value] === targetId)
162
+ if (index >= 0) {
163
+ const scrollDistance = unitConvertWithDefault(itemHeight.value, { defaultUnit: 'rpx' }) * index
164
+ scrollToPosition(`${scrollDistance}px`)
140
165
  }
141
166
  }
142
167
 
143
168
  // 根据ID滚动到指定元素
144
169
  function scrollToElementById(id: string | number) {
145
- const index = data.value.findIndex((o) => id && o[idKey.value] && o[idKey.value] === id)
146
- if (index > 0) {
147
- const scrollDistance = parseFloat(itemHeight.value) * index
148
- scrollToPosition(scrollDistance)
170
+ if (id === undefined || id === null) return
171
+
172
+ const index = data.value.findIndex((o) => o?.[idKey.value] === id)
173
+ if (index >= 0) {
174
+ const scrollDistance = unitConvertWithDefault(itemHeight.value, { defaultUnit: 'rpx' }) * index
175
+ scrollToPosition(`${scrollDistance}px`)
149
176
  }
150
177
  }
151
178
 
@@ -0,0 +1,35 @@
1
+ import { onBeforeUnmount, onMounted } from 'vue'
2
+
3
+ export interface WindowResizeResult {
4
+ size?: {
5
+ windowWidth?: number
6
+ windowHeight?: number
7
+ }
8
+ windowWidth?: number
9
+ windowHeight?: number
10
+ }
11
+
12
+ export type WindowResizeCallback = (result?: WindowResizeResult) => void | Promise<void>
13
+
14
+ interface UseWindowResizeOptions {
15
+ immediate?: boolean
16
+ }
17
+
18
+ export function useWindowResize(callback: WindowResizeCallback, options: UseWindowResizeOptions = {}) {
19
+ const { immediate = false } = options
20
+
21
+ onMounted(() => {
22
+ if (immediate) {
23
+ callback()
24
+ }
25
+ if (typeof uni.onWindowResize === 'function') {
26
+ uni.onWindowResize(callback)
27
+ }
28
+ })
29
+
30
+ onBeforeUnmount(() => {
31
+ if (typeof uni.offWindowResize === 'function') {
32
+ uni.offWindowResize(callback)
33
+ }
34
+ })
35
+ }
@@ -50,16 +50,29 @@
50
50
  border-radius: $-action-sheet-radius $-action-sheet-radius 0 0;
51
51
  }
52
52
 
53
+ :deep(.oxy-action-sheet__popup--float) {
54
+ border-radius: 0 !important;
55
+ background: transparent !important;
56
+ overflow: visible !important;
57
+ }
58
+
53
59
  @include b(action-sheet) {
54
60
  background-color: $-color-white;
55
- padding-bottom: 1px;
61
+ padding-bottom: 2rpx;
62
+ margin-bottom: var(--window-bottom);
63
+
64
+ @include when(float) {
65
+ margin: 0 20rpx calc(var(--window-bottom) + 20rpx);
66
+ border-radius: $-action-sheet-radius;
67
+ overflow: hidden;
68
+ }
56
69
 
57
70
  @include edeep(popup) {
58
71
  border-radius: $-action-sheet-radius $-action-sheet-radius 0 0;
59
72
  }
60
73
 
61
74
  @include e(actions) {
62
- padding: 8px 0;
75
+ padding: 20rpx 0;
63
76
  max-height: 50vh;
64
77
  overflow-y: auto;
65
78
  -webkit-overflow-scrolling: touch;
@@ -110,14 +123,14 @@
110
123
 
111
124
  @include e(subname) {
112
125
  display: inline-block;
113
- margin-left: 4px;
126
+ margin-left: 12rpx;
114
127
  font-size: $-action-sheet-subname-fs;
115
128
  color: $-action-sheet-subname-color;
116
129
  }
117
130
 
118
131
  @include e(cancel) {
119
132
  display: block;
120
- width: calc(100% - 48px);
133
+ width: calc(100% - 96rpx);
121
134
  line-height: $-action-sheet-cancel-height;
122
135
  padding: 0;
123
136
  color: $-action-sheet-cancel-color;
@@ -127,7 +140,7 @@
127
140
  border: none;
128
141
  background: $-action-sheet-cancel-bg;
129
142
  outline: none;
130
- margin: 0 auto 24px;
143
+ margin: 0 auto 48rpx;
131
144
  font-weight: $-action-sheet-weight;
132
145
 
133
146
  &:active {
@@ -160,15 +173,15 @@
160
173
  }
161
174
 
162
175
  @include e(panels) {
163
- height: 84px;
176
+ height: 168rpx;
164
177
  overflow-y: hidden;
165
178
 
166
179
  &:first-of-type {
167
- margin-top: 20px;
180
+ margin-top: 40rpx;
168
181
  }
169
182
 
170
183
  &:last-of-type {
171
- margin-bottom: 12px;
184
+ margin-bottom: 24rpx;
172
185
  }
173
186
  }
174
187
 
@@ -179,7 +192,7 @@
179
192
  }
180
193
 
181
194
  @include e(panel) {
182
- width: 88px;
195
+ width: 176rpx;
183
196
  flex: 0 0 auto;
184
197
  display: inline-block;
185
198
  padding: $-action-sheet-panel-padding;
@@ -190,7 +203,7 @@
190
203
  width: $-action-sheet-panel-img-fs;
191
204
  height: $-action-sheet-panel-img-fs;
192
205
  margin: 0 auto;
193
- margin-bottom: 7px;
206
+ margin-bottom: 16rpx;
194
207
  border-radius: $-action-sheet-panel-img-radius;
195
208
  }
196
209
 
@@ -201,4 +214,4 @@
201
214
  color: $-action-sheet-color;
202
215
  @include lineEllipsis;
203
216
  }
204
- }
217
+ }