oxy-uni-ui 1.2.0 → 1.2.3

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 (116) hide show
  1. package/attributes.json +1 -1
  2. package/components/common/abstracts/variable.scss +51 -1
  3. package/components/common/path.ts +9 -0
  4. package/components/common/util.ts +42 -0
  5. package/components/composables/useGlobalLoading.ts +42 -0
  6. package/components/composables/useGlobalMessage.ts +48 -0
  7. package/components/composables/useGlobalToast.ts +84 -0
  8. package/components/composables/useVirtualScroll.ts +3 -2
  9. package/components/oxy-cell/oxy-cell.vue +15 -2
  10. package/components/oxy-cell/types.ts +4 -0
  11. package/components/oxy-checkbox/index.scss +1 -1
  12. package/components/oxy-checkbox/oxy-checkbox.vue +2 -2
  13. package/components/oxy-col-picker/oxy-col-picker.vue +3 -0
  14. package/components/oxy-col-picker/types.ts +5 -1
  15. package/components/oxy-corner/oxy-corner.vue +15 -3
  16. package/components/oxy-corner/types.ts +15 -1
  17. package/components/oxy-date-strip/index.scss +10 -0
  18. package/components/oxy-date-strip/oxy-date-strip.vue +198 -0
  19. package/components/oxy-date-strip/types.ts +98 -0
  20. package/components/oxy-date-strip/utils.ts +67 -0
  21. package/components/oxy-date-strip-item/index.scss +94 -0
  22. package/components/oxy-date-strip-item/oxy-date-strip-item.vue +102 -0
  23. package/components/oxy-date-strip-item/types.ts +53 -0
  24. package/components/oxy-datetime-picker/oxy-datetime-picker.vue +3 -1
  25. package/components/oxy-datetime-picker/types.ts +5 -1
  26. package/components/oxy-echarts/index.scss +17 -0
  27. package/components/oxy-echarts/index.ts +1 -0
  28. package/components/oxy-echarts/oxy-echarts.vue +32 -0
  29. package/components/oxy-echarts/types.ts +12 -0
  30. package/components/oxy-file-list/index.scss +26 -0
  31. package/components/oxy-file-list/oxy-file-list.vue +208 -34
  32. package/components/oxy-file-list/types.ts +58 -2
  33. package/components/oxy-global-loading/oxy-global-loading.vue +53 -0
  34. package/components/oxy-global-message/oxy-global-message.vue +64 -0
  35. package/components/oxy-global-toast/oxy-global-toast.vue +53 -0
  36. package/components/oxy-img-lazy/index.scss +17 -0
  37. package/components/oxy-img-lazy/oxy-img-lazy.vue +332 -0
  38. package/components/oxy-img-lazy/types.ts +69 -0
  39. package/components/oxy-link/index.scss +57 -0
  40. package/components/oxy-link/oxy-link.vue +130 -0
  41. package/components/oxy-link/types.ts +81 -0
  42. package/components/oxy-list/index.scss +7 -1
  43. package/components/oxy-list/types.ts +1 -1
  44. package/components/oxy-picker/oxy-picker.vue +3 -0
  45. package/components/oxy-picker/types.ts +5 -1
  46. package/components/oxy-radio/index.scss +3 -3
  47. package/components/oxy-radio/oxy-radio.vue +1 -1
  48. package/components/oxy-rich-text/icon/emjio.svg +1 -0
  49. package/components/oxy-rich-text/icon/quote.svg +1 -0
  50. package/components/oxy-rich-text/icon/text.svg +1 -0
  51. package/components/oxy-rich-text/icon/title.svg +1 -0
  52. package/components/oxy-rich-text/index.scss +159 -0
  53. package/components/oxy-rich-text/mp-html/card/card.vue +122 -0
  54. package/components/oxy-rich-text/mp-html/card/index.js +7 -0
  55. package/components/oxy-rich-text/mp-html/editable/config.js +15 -0
  56. package/components/oxy-rich-text/mp-html/editable/index.js +553 -0
  57. package/components/oxy-rich-text/mp-html/emoji/index.js +203 -0
  58. package/components/oxy-rich-text/mp-html/highlight/config.js +5 -0
  59. package/components/oxy-rich-text/mp-html/highlight/index.js +96 -0
  60. package/components/oxy-rich-text/mp-html/highlight/prism.css +1 -0
  61. package/components/oxy-rich-text/mp-html/highlight/prism.min.js +7 -0
  62. package/components/oxy-rich-text/mp-html/img-cache/index.js +138 -0
  63. package/components/oxy-rich-text/mp-html/latex/index.js +80 -0
  64. package/components/oxy-rich-text/mp-html/latex/katex.css +1 -0
  65. package/components/oxy-rich-text/mp-html/latex/katex.min.js +1 -0
  66. package/components/oxy-rich-text/mp-html/markdown/index.js +50 -0
  67. package/components/oxy-rich-text/mp-html/markdown/marked.min.js +71 -0
  68. package/components/oxy-rich-text/mp-html/mp-html.d.ts +184 -0
  69. package/components/oxy-rich-text/mp-html/mp-html.vue +675 -0
  70. package/components/oxy-rich-text/mp-html/node/node.vue +1161 -0
  71. package/components/oxy-rich-text/mp-html/parser.js +1428 -0
  72. package/components/oxy-rich-text/mp-html/search/index.js +132 -0
  73. package/components/oxy-rich-text/mp-html/style/index.js +129 -0
  74. package/components/oxy-rich-text/mp-html/style/parser.js +175 -0
  75. package/components/oxy-rich-text/mp-html/template/index.js +67 -0
  76. package/components/oxy-rich-text/mp-html/txv-video/index.js +46 -0
  77. package/components/oxy-rich-text/oxy-rich-text.vue +642 -0
  78. package/components/oxy-rich-text/types.ts +71 -0
  79. package/components/oxy-select/index.scss +255 -0
  80. package/components/oxy-select/oxy-select.vue +421 -0
  81. package/components/oxy-select/types.ts +71 -0
  82. package/components/oxy-select-picker/oxy-select-picker.vue +3 -0
  83. package/components/oxy-select-picker/types.ts +5 -1
  84. package/components/oxy-stream-render/index.scss +6 -0
  85. package/components/oxy-stream-render/oxy-stream-render.vue +204 -0
  86. package/components/oxy-stream-render/types.ts +5 -0
  87. package/components/oxy-tree/index.scss +17 -1
  88. package/components/oxy-tree/oxy-tree.vue +89 -8
  89. package/components/oxy-tree/types.ts +11 -1
  90. package/components/oxy-waterfall/index.scss +18 -0
  91. package/components/oxy-waterfall/oxy-waterfall.vue +218 -0
  92. package/components/oxy-waterfall/types.ts +90 -0
  93. package/components/oxy-waterfall-item/index.scss +8 -0
  94. package/components/oxy-waterfall-item/oxy-waterfall-item.vue +89 -0
  95. package/components/oxy-waterfall-item/types.ts +16 -0
  96. package/global.d.ts +7 -0
  97. package/index.ts +3 -0
  98. package/locale/lang/en-US.ts +26 -0
  99. package/locale/lang/zh-CN.ts +26 -0
  100. package/oxy-uni-ui.zip +0 -0
  101. package/package.json +1 -1
  102. package/tags.json +1 -1
  103. package/uni-echarts/changelog.md +2 -0
  104. package/uni-echarts/components/index.js +1 -0
  105. package/uni-echarts/components/uni-echarts/events.js +95 -0
  106. package/uni-echarts/components/uni-echarts/types.d.ts +183 -0
  107. package/uni-echarts/components/uni-echarts/types.js +1 -0
  108. package/uni-echarts/components/uni-echarts/uni-echarts.vue +530 -0
  109. package/uni-echarts/components/uni-echarts/uni-echarts.vue.d.ts +19 -0
  110. package/uni-echarts/global.d.ts +7 -0
  111. package/uni-echarts/index.d.ts +440 -0
  112. package/uni-echarts/index.js +2 -0
  113. package/uni-echarts/package.json +105 -0
  114. package/uni-echarts/shared-core.d.ts +269 -0
  115. package/uni-echarts/shared-core.js +900 -0
  116. package/web-types.json +1 -1
@@ -0,0 +1,332 @@
1
+ <template>
2
+ <view :class="`oxy-img-lazy ${customClass}`" :style="rootStyle">
3
+ <!-- 加载中 -->
4
+ <image
5
+ :src="loadSrc"
6
+ class="oxy-img-lazy__image oxy-img-lazy__load"
7
+ @load="init"
8
+ :show-menu-by-longpress="true"
9
+ :mode="mode"
10
+ :style="{
11
+ opacity: isShow ? '0' : '1',
12
+ borderRadius,
13
+ width,
14
+ height,
15
+ transition: `opacity ${duration / 1000}s ${effect}`
16
+ }"
17
+ />
18
+
19
+ <!-- 加载成功 -->
20
+ <image
21
+ class="oxy-img-lazy__image"
22
+ @load="load"
23
+ @error="error"
24
+ :show-menu-by-longpress="true"
25
+ :webp="true"
26
+ v-if="status === 1"
27
+ :src="imageSrc"
28
+ :mode="mode"
29
+ :style="{
30
+ opacity: isShow ? '1' : '0',
31
+ borderRadius,
32
+ width,
33
+ height,
34
+ transition: `opacity ${duration / 1000}s ${effect}`
35
+ }"
36
+ />
37
+ <!-- 加载失败 -->
38
+ <image
39
+ class="oxy-img-lazy__image"
40
+ v-if="status === 2"
41
+ :show-menu-by-longpress="true"
42
+ :src="errorSrc"
43
+ :mode="mode"
44
+ :style="{
45
+ opacity: isShow ? '1' : '0',
46
+ borderRadius,
47
+ width,
48
+ height,
49
+ transition: `opacity ${duration / 1000}s ${effect}`
50
+ }"
51
+ />
52
+ </view>
53
+ </template>
54
+ <script lang="ts">
55
+ export default {
56
+ name: 'oxy-img-lazy',
57
+ options: {
58
+ virtualHost: true,
59
+ addGlobalClass: true,
60
+ styleIsolation: 'shared'
61
+ }
62
+ }
63
+ </script>
64
+
65
+ <script lang="ts" setup>
66
+ import { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
67
+ import { addUnit, isDef, objToStyle } from '../common/util'
68
+ import { imgLazyProps } from './types'
69
+
70
+ const props = defineProps(imgLazyProps)
71
+ const emit = defineEmits(['show', 'load', 'error', 'destroyed'])
72
+
73
+ const rootStyle = computed(() => {
74
+ const style: Record<string, string | number> = {}
75
+ if (isDef(props.height)) {
76
+ style['height'] = addUnit(props.height)
77
+ }
78
+ if (isDef(props.width)) {
79
+ style['width'] = addUnit(props.width)
80
+ }
81
+ return `${objToStyle(style)}${props.customStyle}`
82
+ })
83
+
84
+ // 响应式数据
85
+ const status = ref<number>(0) // 0加载中 1加载成功 2加载失败
86
+ const isShow = ref<boolean>(false)
87
+ const loadTimer = ref<number | null>(null)
88
+ const observer = ref<any>(null)
89
+ const renderCount = ref<number>(0) // 添加渲染计数器
90
+ const lastSrc = ref<string>('') // 记录上一次的src值
91
+ const scrollTimeout = ref<number | null>(null) // 滚动检测定时器
92
+ const retryCount = ref<number>(0) // 重试次数计数器
93
+ const maxRetries = ref<number>(3) // 最大重试次数
94
+ const imageSrc = ref<string>('')
95
+ // 获取当前组件实例
96
+ const instance = getCurrentInstance()
97
+
98
+ // 重置状态方法
99
+ const resetStatus = () => {
100
+ status.value = 0
101
+ isShow.value = false
102
+ loadTimer.value = null
103
+ }
104
+
105
+ // 清理资源方法
106
+ const cleanup = () => {
107
+ if (observer.value) {
108
+ observer.value.disconnect()
109
+ observer.value = null
110
+ }
111
+ if (loadTimer.value) {
112
+ clearTimeout(loadTimer.value)
113
+ loadTimer.value = null
114
+ }
115
+ if (scrollTimeout.value) {
116
+ clearTimeout(scrollTimeout.value)
117
+ scrollTimeout.value = null
118
+ }
119
+ }
120
+
121
+ // 开始加载方法
122
+ const startLoad = () => {
123
+ status.value = 1
124
+ loadTimer.value = new Date().getTime()
125
+ }
126
+
127
+ // 图片加载成功
128
+ const load = (event: any) => {
129
+ const minTimeOut = props.minTimeOut
130
+
131
+ if (minTimeOut <= 0) {
132
+ isShow.value = true
133
+ } else {
134
+ // 确保loadTimer存在
135
+ const timerDiff = loadTimer.value ? new Date().getTime() - loadTimer.value : 0
136
+
137
+ if (timerDiff < minTimeOut) {
138
+ loadTimer.value = setTimeout(() => {
139
+ isShow.value = true
140
+ loadTimer.value = null
141
+ }, minTimeOut - timerDiff) as unknown as number
142
+ } else {
143
+ isShow.value = true
144
+ }
145
+ }
146
+
147
+ setTimeout(() => {
148
+ emit('load', event)
149
+ }, props.duration)
150
+ }
151
+
152
+ // 图片加载失败
153
+ const error = (event: any) => {
154
+ imageSrc.value = props.errorSrc
155
+ status.value = 2
156
+ isShow.value = true
157
+ cleanup() // 加载失败时清理资源
158
+ setTimeout(() => {
159
+ emit('error', event)
160
+ }, props.duration)
161
+ }
162
+
163
+ // 初始化懒加载
164
+ const init = async () => {
165
+ // 确保清理之前的观察者
166
+ cleanup()
167
+
168
+ // 如果没有src,不进行懒加载
169
+ if (!props.src) return
170
+
171
+ // 如果组件已经在视口内,直接开始加载
172
+ const isInViewport = await isComponentInViewport()
173
+ if (isInViewport) {
174
+ startLoad()
175
+ return
176
+ }
177
+
178
+ try {
179
+ // 使用组件实例创建IntersectionObserver
180
+ observer.value = uni.createIntersectionObserver(instance)
181
+ let load = false
182
+
183
+ observer.value.relativeToViewport(props.showDistance).observe('.oxy-img-lazy__load', (res: any) => {
184
+ if (!load && res.intersectionRatio > 0) {
185
+ emit('show')
186
+ load = true
187
+ startLoad()
188
+ cleanup()
189
+ }
190
+ })
191
+ } catch (error) {
192
+ console.error('懒加载初始化失败:', error)
193
+ // 降级处理:如果IntersectionObserver失败,直接加载
194
+ startLoad()
195
+ }
196
+ }
197
+
198
+ // 检查组件是否在视口内
199
+ const isComponentInViewport = (): Promise<boolean> => {
200
+ return new Promise((resolve) => {
201
+ try {
202
+ const query = uni.createSelectorQuery().in(instance)
203
+ query
204
+ .select('.oxy-img-lazy__load')
205
+ .boundingClientRect((rect: any) => {
206
+ if (rect) {
207
+ const windowHeight = uni.getSystemInfoSync().windowHeight
208
+ const windowWidth = uni.getSystemInfoSync().windowWidth
209
+
210
+ // 检查是否在可视区域内(考虑滚动容器的偏移)
211
+ const isInViewport = rect.top < windowHeight && rect.bottom > 0 && rect.left < windowWidth && rect.right > 0
212
+
213
+ resolve(isInViewport)
214
+ } else {
215
+ resolve(false)
216
+ }
217
+ })
218
+ .exec()
219
+ } catch (error) {
220
+ resolve(false)
221
+ }
222
+ })
223
+ }
224
+
225
+ // 监听src变化
226
+ watch(
227
+ () => props.src,
228
+ (newSrc, oldSrc) => {
229
+ // 无论isShow状态如何,都重置加载状态
230
+ if (newSrc) {
231
+ imageSrc.value = newSrc
232
+ }
233
+ if (newSrc !== oldSrc) {
234
+ resetStatus()
235
+ // 如果组件已经可见,直接开始加载
236
+ if (isShow.value) {
237
+ startLoad()
238
+ } else {
239
+ // 重新初始化懒加载观察者
240
+ nextTick(() => {
241
+ init()
242
+ })
243
+ }
244
+ }
245
+ },
246
+ {
247
+ immediate: true,
248
+ deep: true
249
+ }
250
+ )
251
+
252
+ // 添加一个强制刷新方法
253
+ const forceRefresh = () => {
254
+ resetStatus()
255
+ cleanup()
256
+ nextTick(() => {
257
+ init()
258
+ })
259
+ }
260
+
261
+ // 检查是否需要重新初始化
262
+ const checkAndReinit = () => {
263
+ // 如果src发生变化,或者这是第一次渲染,则重新初始化
264
+ if (props.src !== lastSrc.value || renderCount.value === 0) {
265
+ lastSrc.value = props.src || ''
266
+ resetStatus()
267
+ nextTick(() => {
268
+ init()
269
+ })
270
+ }
271
+ renderCount.value++
272
+ }
273
+
274
+ // 滚动检测和手动触发机制
275
+ const setupScrollDetection = () => {
276
+ // 监听页面滚动事件
277
+ uni.$on('pageScroll', () => {
278
+ if (scrollTimeout.value) {
279
+ clearTimeout(scrollTimeout.value)
280
+ }
281
+
282
+ // 防抖处理,滚动停止后检测可见性
283
+ scrollTimeout.value = setTimeout(() => {
284
+ checkVisibilityOnScroll()
285
+ }, 300) as unknown as number
286
+ })
287
+ }
288
+
289
+ // 滚动时检查可见性
290
+ const checkVisibilityOnScroll = async () => {
291
+ if (status.value === 0) {
292
+ // 只有在未加载状态下检查
293
+ const isInViewport = await isComponentInViewport()
294
+ if (isInViewport) {
295
+ startLoad()
296
+ cleanup() // 清理观察器
297
+ }
298
+ }
299
+ }
300
+
301
+ // 手动触发加载(用于scroll-view和swiper等特殊情况)
302
+ const manualTriggerLoad = () => {
303
+ if (status.value === 0) {
304
+ startLoad()
305
+ cleanup()
306
+ }
307
+ }
308
+
309
+ // 暴露方法给父组件
310
+ defineExpose({
311
+ forceRefresh,
312
+ manualTriggerLoad
313
+ })
314
+
315
+ // 组件挂载后初始化
316
+ onMounted(() => {
317
+ // 增加渲染计数并检查是否需要重新初始化
318
+ checkAndReinit()
319
+ // 设置滚动检测
320
+ setupScrollDetection()
321
+ })
322
+
323
+ // 组件销毁前清理资源
324
+ onBeforeUnmount(() => {
325
+ cleanup()
326
+ emit('destroyed')
327
+ })
328
+ </script>
329
+
330
+ <style lang="scss" scoped>
331
+ @import './index.scss';
332
+ </style>
@@ -0,0 +1,69 @@
1
+ import { baseProps, makeNumberProp, makeStringProp, numericProp } from '../common/props'
2
+ export type ImageMode =
3
+ | 'scaleToFill'
4
+ | 'aspectFit'
5
+ | 'aspectFill'
6
+ | 'widthFix'
7
+ | 'heightFix'
8
+ | 'top'
9
+ | 'bottom'
10
+ | 'center'
11
+ | 'left'
12
+ | 'right'
13
+ | 'top left'
14
+ | 'top right'
15
+ | 'bottom left'
16
+ | 'bottom right'
17
+
18
+ export type ImageEffect = 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out'
19
+
20
+ export const imgLazyProps = {
21
+ ...baseProps,
22
+ /**
23
+ * 图片链接
24
+ */
25
+ src: String,
26
+ /**
27
+ * 填充模式:'top left' / 'top right' / 'bottom left' / 'bottom right' / 'right' / 'left' / 'center' / 'bottom' / 'top' / 'heightFix' / 'widthFix' / 'aspectFill' / 'aspectFit' / 'scaleToFill'
28
+ */
29
+ mode: makeStringProp<ImageMode>('aspectFill'),
30
+ /**
31
+ * 宽度,默认单位为px
32
+ */
33
+ width: numericProp,
34
+ /**
35
+ * 高度,默认单位为px
36
+ */
37
+ height: numericProp,
38
+ /**
39
+ * 圆角大小,默认单位为px
40
+ */
41
+ borderRadius: makeStringProp('0'),
42
+ /**
43
+ * 懒加载延迟时间
44
+ */
45
+ minTimeOut: makeNumberProp(0),
46
+ /**
47
+ * 图片提前加载的距离阈值
48
+ */
49
+ showDistance: {
50
+ type: Object,
51
+ default: () => ({ bottom: 20 })
52
+ },
53
+ /**
54
+ * 图片懒加载时的视觉过渡效果
55
+ */
56
+ effect: makeStringProp<ImageEffect>('linear'),
57
+ /**
58
+ * 图片懒加载时的视觉过渡时间
59
+ */
60
+ duration: makeNumberProp(0),
61
+ /**
62
+ * 加载中时显示的图片链接
63
+ */
64
+ loadSrc: makeStringProp(''),
65
+ /**
66
+ * 加载失败时显示的图片链接
67
+ */
68
+ errorSrc: makeStringProp('')
69
+ }
@@ -0,0 +1,57 @@
1
+ @import '../common/abstracts/variable';
2
+ @import '../common/abstracts/mixin';
3
+
4
+ @include b(link) {
5
+ display: inline-block;
6
+ @include e(medium) {
7
+ font-size: $-link-font-size-medium;
8
+ }
9
+
10
+ @include e(small) {
11
+ font-size: $-link-font-size-small;
12
+ }
13
+
14
+ @include e(large) {
15
+ font-size: $-link-font-size-large;
16
+ }
17
+
18
+ @include e(icon) {
19
+ display: inline-block;
20
+ }
21
+
22
+ @include when(underline) {
23
+ text-decoration: underline;
24
+ }
25
+
26
+ @include when(default) {
27
+ color: $-link-info-color;
28
+ }
29
+
30
+ @include when(primary) {
31
+ color: $-link-primary-color;
32
+ }
33
+
34
+ @include when(error) {
35
+ color: $-link-error-color;
36
+ }
37
+
38
+ @include when(warning) {
39
+ color: $-link-warning-color;
40
+ }
41
+
42
+ @include when(success) {
43
+ color: $-link-success-color;
44
+ }
45
+
46
+ @include when(disabled) {
47
+ opacity: .6;
48
+ }
49
+
50
+ &__prefix-icon:not(:empty) + &__content:not(:empty) {
51
+ padding-left: 4px;
52
+ }
53
+
54
+ &__content:not(:empty) + &__suffix-icon:not(:empty) {
55
+ padding-left: 4px;
56
+ }
57
+ }
@@ -0,0 +1,130 @@
1
+ <template>
2
+ <navigator
3
+ :class="rootClass"
4
+ :style="rootStyle"
5
+ :url="!disabled ? navigatorProps?.['url'] || url : ''"
6
+ :target="navigatorProps?.['target']"
7
+ :open-type="navigatorProps?.['openType'] || openType"
8
+ :delta="navigatorProps?.['delta']"
9
+ :app-id="navigatorProps?.['appId']"
10
+ :path="navigatorProps?.['path']"
11
+ :extra-data="navigatorProps?.['extraData']"
12
+ :version="navigatorProps?.['version']"
13
+ :short-link="navigatorProps?.['shortLink']"
14
+ :hover-class="hover && !disabled ? 'oxy-link__hover' : ''"
15
+ :hover-stop-propagation="navigatorProps?.['hoverStopPropagation']"
16
+ :hover-start-time="navigatorProps?.['hoverStartTime']"
17
+ :hover-stay-time="navigatorProps?.['hoverStayTime']"
18
+ @click="handleClick"
19
+ >
20
+ <slot name="prefix-icon">
21
+ <view class="oxy-link__icon oxy-link__prefix-icon" v-if="prefixIcon">
22
+ <oxy-icon :size="fontSize" :color="color" :name="prefixIcon"></oxy-icon>
23
+ </view>
24
+ </slot>
25
+ <text class="oxy-link__content" ref="textRef">
26
+ <slot>{{ content }}</slot>
27
+ </text>
28
+ <slot name="suffix-icon">
29
+ <view class="oxy-link__icon oxy-link__suffix-icon" v-if="suffixIcon">
30
+ <oxy-icon :size="fontSize" :color="color" :name="suffixIcon"></oxy-icon>
31
+ </view>
32
+ </slot>
33
+ </navigator>
34
+ </template>
35
+
36
+ <script lang="ts">
37
+ export default {
38
+ name: 'oxy-link',
39
+ options: {
40
+ virtualHost: true,
41
+ addGlobalClass: true,
42
+ styleIsolation: 'shared'
43
+ }
44
+ }
45
+ </script>
46
+
47
+ <script lang="ts" setup>
48
+ import { computed, ref, watch } from 'vue'
49
+ import { objToStyle } from '../common/util'
50
+ import { linkProps } from './types'
51
+
52
+ // 获取组件的 props 和 emit 函数
53
+ const props = defineProps(linkProps)
54
+ const emit = defineEmits(['click', 'success', 'fail', 'complete'])
55
+
56
+ // 存储超链接类名的响应式变量
57
+ const linkClass = ref<string>('')
58
+ const fontSize = computed<string>(() => {
59
+ if (!['small', 'medium', 'large'].includes(props.size)) {
60
+ return props.size
61
+ }
62
+ return ''
63
+ })
64
+
65
+ // 监听 props 变化,合并 watch 逻辑
66
+ watch(
67
+ () => ({
68
+ size: props.size,
69
+ type: props.type,
70
+ disabled: props.disabled,
71
+ underline: props.underline
72
+ }),
73
+ ({ type }) => {
74
+ // 验证 type 属性
75
+ const types = ['primary', 'danger', 'warning', 'success', 'default']
76
+ if (type && !types.includes(type)) {
77
+ console.error(`type must be one of ${types.toString()}`)
78
+ }
79
+ computeLinkClass()
80
+ },
81
+ { deep: true, immediate: true }
82
+ )
83
+
84
+ // 计算根元素的类名
85
+ const rootClass = computed(() => {
86
+ return `oxy-link ${props.customClass} ${linkClass.value}`
87
+ })
88
+
89
+ // 计算根元素的样式
90
+ const rootStyle = computed(() => {
91
+ const rootStyle: Record<string, any> = {}
92
+ if (props.color) {
93
+ rootStyle['color'] = props.color
94
+ }
95
+ if (fontSize.value) {
96
+ rootStyle['font-size'] = fontSize.value
97
+ }
98
+ return `${objToStyle(rootStyle)}${props.customStyle}`
99
+ })
100
+
101
+ // 计算超链接类名的函数
102
+ function computeLinkClass() {
103
+ const { size, type, disabled, underline } = props
104
+ const linkClassList: string[] = []
105
+ size && linkClassList.push(`oxy-link__${size}`)
106
+ type && linkClassList.push(`is-${type}`)
107
+ disabled && linkClassList.push('is-disabled')
108
+ underline && linkClassList.push('is-underline')
109
+ linkClass.value = linkClassList.join(' ')
110
+ }
111
+
112
+ function handleClick(event: Event) {
113
+ if (!props.url && props.href) {
114
+ // #ifdef APP-PLUS
115
+ plus.runtime.openURL(props.href)
116
+ // #endif
117
+ // #ifdef H5
118
+ window.open(props.href)
119
+ // #endif
120
+ // #ifdef MP
121
+ uni.setClipboardData({ data: props.href })
122
+ // #endif
123
+ }
124
+ emit('click', event)
125
+ }
126
+ </script>
127
+
128
+ <style lang="scss" scoped>
129
+ @import './index.scss';
130
+ </style>
@@ -0,0 +1,81 @@
1
+ import type { ExtractPropTypes } from 'vue'
2
+ import { baseProps, makeBooleanProp, makeStringProp } from '../common/props'
3
+
4
+ export type OpenType = 'navigate' | 'redirect' | 'switchTab' | 'reLaunch' | 'navigateBack' | 'exit'
5
+ export type LinkType = 'default' | 'primary' | 'error' | 'warning' | 'success'
6
+ export type SizeEnum = 'small' | 'medium' | 'large'
7
+
8
+ export const linkProps = {
9
+ ...baseProps,
10
+ /**
11
+ * 应用内跳转的链接
12
+ */
13
+ url: makeStringProp(''),
14
+ /**
15
+ * 应用外跳转的链接
16
+ */
17
+ href: makeStringProp(''),
18
+ /**
19
+ * 跳转方式
20
+ */
21
+ openType: makeStringProp<OpenType>('navigate'),
22
+ /**
23
+ * 链接内容
24
+ * 类型:string
25
+ */
26
+ content: makeStringProp(''),
27
+ /**
28
+ * 是否禁用
29
+ * 类型:boolean
30
+ * 默认值:false
31
+ */
32
+ disabled: makeBooleanProp(false),
33
+ /**
34
+ * 是否开启点击反馈
35
+ * 类型:boolean
36
+ * 默认值:false
37
+ */
38
+ hover: makeBooleanProp(false),
39
+ /**
40
+ * 是否显示链接下划线
41
+ * 类型:boolean
42
+ * 默认值:false
43
+ */
44
+ underline: makeBooleanProp(false),
45
+ /**
46
+ * 主题类型
47
+ * 类型:string
48
+ * 可选值:'default' /'primary' / 'error' / 'warning' / 'success'
49
+ * 默认值:'default'
50
+ */
51
+ type: makeStringProp<LinkType>('default'),
52
+ /**
53
+ * 字体大小
54
+ * 类型:string
55
+ * 默认值:'空字符串'
56
+ */
57
+ size: makeStringProp<SizeEnum>('medium'),
58
+ /**
59
+ * 文本颜色
60
+ * 类型:string
61
+ * 默认值:''
62
+ */
63
+ color: makeStringProp(''),
64
+ /**
65
+ * 前置图标
66
+ */
67
+ prefixIcon: makeStringProp(''),
68
+ /**
69
+ * 后置图标
70
+ */
71
+ suffixIcon: makeStringProp(''),
72
+ /**
73
+ * 与 navigator 原生组件属性保持一致,具体使用参考:[微信开放文档](https://developers.weixin.qq.com/miniprogram/dev/component/navigator.html)。使用时请将形如 `open-type` 风格的属性名改为 `openType` 风格
74
+ */
75
+ navigatorProps: {
76
+ type: Object,
77
+ default: null
78
+ }
79
+ }
80
+
81
+ export type LinkProps = ExtractPropTypes<typeof linkProps>
@@ -2,4 +2,10 @@
2
2
  @import './../common/abstracts/variable.scss';
3
3
  @import './../oxy-virtual-scroll/index.scss';
4
4
 
5
- @include b(list) {}
5
+ @include b(list) {
6
+ :deep(.oxy-loadmore) {
7
+ .oxy-divider {
8
+ margin: 0
9
+ }
10
+ }
11
+ }
@@ -7,7 +7,7 @@ import { virtualScrollProps } from '../oxy-virtual-scroll/types'
7
7
  export const listProps = {
8
8
  ...virtualScrollProps,
9
9
 
10
- loadmoreState: String as PropType<LoadMoreState>,
10
+ loadmoreState: String as PropType<LoadMoreState | ''>,
11
11
 
12
12
  /**
13
13
  * 加载提示文案
@@ -293,6 +293,9 @@ function close() {
293
293
  */
294
294
  function showPopup() {
295
295
  if (props.disabled || props.readonly) return
296
+ if (isFunction(props.beforeOpen) && !props.beforeOpen()) {
297
+ return
298
+ }
296
299
 
297
300
  emit('open')
298
301
  popupShow.value = true