oxy-uni-ui 1.1.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 (123) hide show
  1. package/attributes.json +1 -1
  2. package/components/common/abstracts/variable.scss +59 -1
  3. package/components/common/path.ts +9 -0
  4. package/components/common/util.ts +42 -0
  5. package/components/composables/index.ts +1 -0
  6. package/components/composables/useGlobalLoading.ts +42 -0
  7. package/components/composables/useGlobalMessage.ts +48 -0
  8. package/components/composables/useGlobalToast.ts +84 -0
  9. package/components/composables/useVirtualScroll.ts +173 -0
  10. package/components/oxy-cell/oxy-cell.vue +15 -2
  11. package/components/oxy-cell/types.ts +4 -0
  12. package/components/oxy-checkbox/index.scss +1 -1
  13. package/components/oxy-checkbox/oxy-checkbox.vue +2 -2
  14. package/components/oxy-col-picker/oxy-col-picker.vue +3 -0
  15. package/components/oxy-col-picker/types.ts +5 -1
  16. package/components/oxy-corner/index.scss +121 -1
  17. package/components/oxy-corner/oxy-corner.vue +18 -5
  18. package/components/oxy-corner/types.ts +24 -3
  19. package/components/oxy-date-strip/index.scss +10 -0
  20. package/components/oxy-date-strip/oxy-date-strip.vue +198 -0
  21. package/components/oxy-date-strip/types.ts +98 -0
  22. package/components/oxy-date-strip/utils.ts +67 -0
  23. package/components/oxy-date-strip-item/index.scss +94 -0
  24. package/components/oxy-date-strip-item/oxy-date-strip-item.vue +102 -0
  25. package/components/oxy-date-strip-item/types.ts +53 -0
  26. package/components/oxy-datetime-picker/oxy-datetime-picker.vue +3 -1
  27. package/components/oxy-datetime-picker/types.ts +5 -1
  28. package/components/oxy-echarts/index.scss +17 -0
  29. package/components/oxy-echarts/index.ts +1 -0
  30. package/components/oxy-echarts/oxy-echarts.vue +32 -0
  31. package/components/oxy-echarts/types.ts +12 -0
  32. package/components/oxy-file-list/index.scss +26 -0
  33. package/components/oxy-file-list/oxy-file-list.vue +208 -34
  34. package/components/oxy-file-list/types.ts +58 -2
  35. package/components/oxy-global-loading/oxy-global-loading.vue +53 -0
  36. package/components/oxy-global-message/oxy-global-message.vue +64 -0
  37. package/components/oxy-global-toast/oxy-global-toast.vue +53 -0
  38. package/components/oxy-img-lazy/index.scss +17 -0
  39. package/components/oxy-img-lazy/oxy-img-lazy.vue +332 -0
  40. package/components/oxy-img-lazy/types.ts +69 -0
  41. package/components/oxy-link/index.scss +57 -0
  42. package/components/oxy-link/oxy-link.vue +130 -0
  43. package/components/oxy-link/types.ts +81 -0
  44. package/components/oxy-list/index.scss +8 -1
  45. package/components/oxy-list/oxy-list.vue +121 -40
  46. package/components/oxy-list/types.ts +3 -15
  47. package/components/oxy-picker/oxy-picker.vue +3 -0
  48. package/components/oxy-picker/types.ts +5 -1
  49. package/components/oxy-radio/index.scss +3 -3
  50. package/components/oxy-radio/oxy-radio.vue +1 -1
  51. package/components/oxy-rich-text/icon/emjio.svg +1 -0
  52. package/components/oxy-rich-text/icon/quote.svg +1 -0
  53. package/components/oxy-rich-text/icon/text.svg +1 -0
  54. package/components/oxy-rich-text/icon/title.svg +1 -0
  55. package/components/oxy-rich-text/index.scss +159 -0
  56. package/components/oxy-rich-text/mp-html/card/card.vue +122 -0
  57. package/components/oxy-rich-text/mp-html/card/index.js +7 -0
  58. package/components/oxy-rich-text/mp-html/editable/config.js +15 -0
  59. package/components/oxy-rich-text/mp-html/editable/index.js +553 -0
  60. package/components/oxy-rich-text/mp-html/emoji/index.js +203 -0
  61. package/components/oxy-rich-text/mp-html/highlight/config.js +5 -0
  62. package/components/oxy-rich-text/mp-html/highlight/index.js +96 -0
  63. package/components/oxy-rich-text/mp-html/highlight/prism.css +1 -0
  64. package/components/oxy-rich-text/mp-html/highlight/prism.min.js +7 -0
  65. package/components/oxy-rich-text/mp-html/img-cache/index.js +138 -0
  66. package/components/oxy-rich-text/mp-html/latex/index.js +80 -0
  67. package/components/oxy-rich-text/mp-html/latex/katex.css +1 -0
  68. package/components/oxy-rich-text/mp-html/latex/katex.min.js +1 -0
  69. package/components/oxy-rich-text/mp-html/markdown/index.js +50 -0
  70. package/components/oxy-rich-text/mp-html/markdown/marked.min.js +71 -0
  71. package/components/oxy-rich-text/mp-html/mp-html.d.ts +184 -0
  72. package/components/oxy-rich-text/mp-html/mp-html.vue +675 -0
  73. package/components/oxy-rich-text/mp-html/node/node.vue +1161 -0
  74. package/components/oxy-rich-text/mp-html/parser.js +1428 -0
  75. package/components/oxy-rich-text/mp-html/search/index.js +132 -0
  76. package/components/oxy-rich-text/mp-html/style/index.js +129 -0
  77. package/components/oxy-rich-text/mp-html/style/parser.js +175 -0
  78. package/components/oxy-rich-text/mp-html/template/index.js +67 -0
  79. package/components/oxy-rich-text/mp-html/txv-video/index.js +46 -0
  80. package/components/oxy-rich-text/oxy-rich-text.vue +642 -0
  81. package/components/oxy-rich-text/types.ts +71 -0
  82. package/components/oxy-select/index.scss +255 -0
  83. package/components/oxy-select/oxy-select.vue +421 -0
  84. package/components/oxy-select/types.ts +71 -0
  85. package/components/oxy-select-picker/oxy-select-picker.vue +3 -0
  86. package/components/oxy-select-picker/types.ts +5 -1
  87. package/components/oxy-stream-render/index.scss +6 -0
  88. package/components/oxy-stream-render/oxy-stream-render.vue +204 -0
  89. package/components/oxy-stream-render/types.ts +5 -0
  90. package/components/oxy-tree/index.scss +43 -5
  91. package/components/oxy-tree/oxy-tree.vue +233 -35
  92. package/components/oxy-tree/types.ts +54 -7
  93. package/components/oxy-tree/utils.ts +51 -0
  94. package/components/oxy-virtual-scroll/index.scss +1 -1
  95. package/components/oxy-virtual-scroll/oxy-virtual-scroll.vue +69 -110
  96. package/components/oxy-virtual-scroll/types.ts +95 -5
  97. package/components/oxy-waterfall/index.scss +18 -0
  98. package/components/oxy-waterfall/oxy-waterfall.vue +218 -0
  99. package/components/oxy-waterfall/types.ts +90 -0
  100. package/components/oxy-waterfall-item/index.scss +8 -0
  101. package/components/oxy-waterfall-item/oxy-waterfall-item.vue +89 -0
  102. package/components/oxy-waterfall-item/types.ts +16 -0
  103. package/global.d.ts +7 -0
  104. package/index.ts +3 -0
  105. package/locale/lang/en-US.ts +35 -9
  106. package/locale/lang/zh-CN.ts +31 -5
  107. package/oxy-uni-ui.zip +0 -0
  108. package/package.json +1 -1
  109. package/tags.json +1 -1
  110. package/uni-echarts/changelog.md +2 -0
  111. package/uni-echarts/components/index.js +1 -0
  112. package/uni-echarts/components/uni-echarts/events.js +95 -0
  113. package/uni-echarts/components/uni-echarts/types.d.ts +183 -0
  114. package/uni-echarts/components/uni-echarts/types.js +1 -0
  115. package/uni-echarts/components/uni-echarts/uni-echarts.vue +530 -0
  116. package/uni-echarts/components/uni-echarts/uni-echarts.vue.d.ts +19 -0
  117. package/uni-echarts/global.d.ts +7 -0
  118. package/uni-echarts/index.d.ts +440 -0
  119. package/uni-echarts/index.js +2 -0
  120. package/uni-echarts/package.json +105 -0
  121. package/uni-echarts/shared-core.d.ts +269 -0
  122. package/uni-echarts/shared-core.js +900 -0
  123. package/web-types.json +1 -1
@@ -1,26 +1,44 @@
1
1
  <template>
2
- <view :class="['oxy-virtual-scroll', customClass]" :style="{ height: height }">
2
+ <view :class="['oxy-virtual-scroll', customClass]" :style="customStyle">
3
3
  <!-- 滚动内容区域 -->
4
4
  <scroll-view
5
- ref="scrollView"
5
+ v-if="data.length"
6
+ :style="{ height: height }"
6
7
  class="oxy-virtual-scroll__view"
7
- :scroll-y="true"
8
+ :scroll-y="scrollY"
8
9
  :scroll-x="scrollX"
10
+ :upper-threshold="upperThreshold"
11
+ :lower-threshold="lowerThreshold"
9
12
  :scroll-top="scrollTop"
10
- :scroll-into-view="itemId"
11
- v-bind="$attrs"
13
+ :scroll-left="scrollLeft"
14
+ :scroll-with-animation="scrollWithAnimation"
15
+ :enable-back-to-top="enableBackToTop"
16
+ :show-scrollbar="showScrollbar"
17
+ :refresher-enabled="refresherEnabled"
18
+ :refresher-threshold="refresherThreshold"
19
+ :refresher-default-style="refresherDefaultStyle"
20
+ :refresher-background="refresherBackground"
21
+ :refresher-triggered="triggered"
22
+ :enable-flex="enableFlex"
23
+ :scroll-anchoring="scrollAnchoring"
12
24
  @scroll="onScroll"
13
25
  @scrolltoupper="onScrollUpper"
14
26
  @scrolltolower="onScrollLower"
15
27
  >
16
28
  <view class="oxy-virtual-scroll__container" :style="{ height: totalHeight + 'px' }">
17
29
  <view class="oxy-virtual-scroll__items" :style="{ transform: `translateY(${virtualOffsetY}px)` }">
18
- <slot name="item" v-for="(item, index) in virtualData" :item="item" :index="startIndex + index"></slot>
30
+ <view v-for="(item, index) in virtualData" :key="index">
31
+ <slot name="item" :item="item" :index="startIndex + index"></slot>
32
+ </view>
19
33
  <slot name="bottom"></slot>
20
34
  </view>
21
35
  </view>
22
36
  </scroll-view>
23
37
 
38
+ <view v-else>
39
+ <oxy-status-tip image="content" :tip="emptyText" />
40
+ </view>
41
+
24
42
  <!-- 回到顶部按钮 -->
25
43
  <view v-if="showBackToTop && showBackTopBtn" class="oxy-virtual-scroll__back-top" @click="scrollToTop">
26
44
  <oxy-icon name="backtop" color="#fff" size="20px"></oxy-icon>
@@ -40,91 +58,47 @@ export default {
40
58
  </script>
41
59
 
42
60
  <script lang="ts" setup>
43
- import { computed, onMounted, ref, watch, nextTick } from 'vue'
61
+ import { toRefs } from 'vue'
44
62
  import type { ScrollViewOnScrollEvent, ScrollViewOnScrolltolowerEvent, ScrollViewOnScrolltoupperEvent } from '@uni-helper/uni-app-types'
45
- import { VirtualScrollEngine } from './virtual-scroll'
46
- import { virtualScrollProps } from './types'
47
- defineOptions({
48
- inheritAttrs: false
49
- })
63
+ import { virtualScrollProps, type VirtualScrollExpose } from './types'
64
+ import { useVirtualScroll } from '../composables/useVirtualScroll'
65
+
50
66
  const props = defineProps(virtualScrollProps)
51
67
  const emit = defineEmits(['scroll', 'scroll-to-upper', 'scroll-to-lower'])
52
68
 
53
- const scrollView = ref(null)
54
- const itemId = ref<string>('')
55
- const scrollTop = ref<number>(0)
56
- const showBackTopBtn = ref<boolean>(false)
57
- const virtualData = ref<any[]>([])
58
- const startIndex = ref<number>(0)
59
- const virtualOffsetY = ref<number>(0)
60
- const virtualEngine = ref<any>(null)
61
-
62
- // 显示的数据(索引处理后的)
63
- const displayData = computed<any[]>(() => {
64
- return props.data
65
- })
66
-
67
- // 虚拟列表总高度
68
- const totalHeight = computed(() => {
69
- return displayData.value.length * parseFloat(props.itemHeight) // 假设每项高度50px
70
- })
71
-
72
- watch(
73
- () => props.data,
74
- () => {
75
- nextTick(initScrollData)
76
- },
77
- {
78
- immediate: true,
79
- deep: true
80
- }
81
- )
82
-
83
- onMounted(() => {
84
- initScrollEngine()
69
+ // 解构props用于组合式函数
70
+ const { data, virtual, height, itemHeight, idKey, showBackToTop, backToTopThreshold } = toRefs(props)
71
+
72
+ // 使用虚拟滚动组合式函数
73
+ const {
74
+ scrollTop,
75
+ showBackTopBtn,
76
+ virtualData,
77
+ startIndex,
78
+ virtualOffsetY,
79
+ totalHeight,
80
+ displayData,
81
+ initScrollData,
82
+ initScrollEngine,
83
+ updateVisibleData,
84
+ scrollToTop: virtualScrollToTop,
85
+ scrollToBottom: virtualScrollToBottom,
86
+ scrollToPosition: virtualScrollToPosition,
87
+ scrollToElement: virtualScrollToElement,
88
+ scrollToElementById: virtualScrollToElementById,
89
+ onScroll: handleVirtualScroll
90
+ } = useVirtualScroll({
91
+ data,
92
+ virtual,
93
+ height,
94
+ itemHeight,
95
+ idKey,
96
+ backToTopThreshold
85
97
  })
86
98
 
87
- // 初始化滚动数据
88
- function initScrollData() {
89
- if (!props.virtual) {
90
- // 非虚拟滚动模式:直接使用全部数据
91
- virtualData.value = displayData.value
92
- virtualOffsetY.value = 0
93
- return
94
- }
95
- virtualEngine.value = new VirtualScrollEngine({
96
- containerHeight: parseFloat(props.height),
97
- itemHeight: parseFloat(props.itemHeight),
98
- data: displayData.value
99
- })
100
- updateVisibleData()
101
- }
102
-
103
- // 初始化滚动引擎
104
- function initScrollEngine() {
105
- if (!props.virtual) return
106
- virtualEngine.value = new VirtualScrollEngine({
107
- containerHeight: parseFloat(props.height),
108
- itemHeight: parseFloat(props.itemHeight),
109
- data: displayData.value
110
- })
111
- }
112
-
113
- // 更新可见数据
114
- function updateVisibleData() {
115
- if (!props.virtual) return // 非虚拟模式不处理
116
- if (virtualEngine.value && scrollView.value) {
117
- const { visibleData, offsetY } = virtualEngine.value.updateVisibleData(scrollTop.value || 0)
118
- virtualData.value = visibleData
119
- virtualOffsetY.value = offsetY
120
- }
121
- }
122
-
123
99
  // 滚动事件
124
100
  function onScroll(event: ScrollViewOnScrollEvent) {
125
- scrollTop.value = event.detail.scrollTop
126
- showBackTopBtn.value = scrollTop.value > parseFloat(props.backToTopThreshold)
127
- updateVisibleData()
101
+ handleVirtualScroll(event.detail.scrollTop)
128
102
  emit('scroll', event)
129
103
  }
130
104
 
@@ -138,39 +112,24 @@ function onScrollLower(event: ScrollViewOnScrolltolowerEvent) {
138
112
  emit('scroll-to-lower', event)
139
113
  }
140
114
 
141
- // 回到顶部
142
- function scrollToTop() {
143
- scrollTop.value = 0
144
- nextTick(() => {
145
- scrollTop.value = 0
146
- })
115
+ // 滚动方法
116
+ const scrollToTop = () => {
117
+ virtualScrollToTop()
147
118
  }
148
- function scrollToBottom() {
149
- scrollToPosition(totalHeight.value)
119
+ const scrollToBottom = () => {
120
+ virtualScrollToBottom()
150
121
  }
151
-
152
- // 滚动到指定位置
153
- function scrollToPosition(position: number | string) {
154
- scrollTop.value = typeof position === 'number' ? position : parseFloat(position)
122
+ const scrollToPosition = (position: number | string) => {
123
+ virtualScrollToPosition(position)
155
124
  }
156
-
157
- // 滚动到指定元素
158
- function scrollToElement(item: any) {
159
- const index = props.data.findIndex((o) => item[props.idKey] && o[props.idKey] && o[props.idKey] === item[props.idKey])
160
- if (index > 0) {
161
- const scrollDistance = parseFloat(props.itemHeight) * index
162
- scrollToPosition(scrollDistance)
163
- }
125
+ const scrollToElement = (item: any) => {
126
+ virtualScrollToElement(item)
164
127
  }
165
- function scrollToElementById(id: string | number) {
166
- const index = props.data.findIndex((o) => id && o[props.idKey] && o[props.idKey] === id)
167
- if (index > 0) {
168
- const scrollDistance = parseFloat(props.itemHeight) * index
169
- scrollToPosition(scrollDistance)
170
- }
128
+ const scrollToElementById = (id: string | number) => {
129
+ virtualScrollToElementById(id)
171
130
  }
172
131
 
173
- defineExpose({
132
+ defineExpose<VirtualScrollExpose>({
174
133
  scrollToTop,
175
134
  scrollToBottom,
176
135
  scrollToPosition,
@@ -1,5 +1,5 @@
1
1
  import type { ComponentPublicInstance, ExtractPropTypes, PropType } from 'vue'
2
- import { baseProps, makeRequiredProp, makeBooleanProp, makeStringProp } from '../common/props'
2
+ import { baseProps, makeRequiredProp, makeBooleanProp, makeStringProp, makeNumericProp, makeNumberProp } from '../common/props'
3
3
 
4
4
  export const virtualScrollProps = {
5
5
  ...baseProps,
@@ -37,15 +37,105 @@ export const virtualScrollProps = {
37
37
  /**
38
38
  * 单个项目高度
39
39
  * 类型:number
40
- * 默认值:'50px'
40
+ * 默认值:'44px'
41
41
  */
42
- itemHeight: makeStringProp('50px'),
42
+ itemHeight: makeStringProp('44px'),
43
43
  /**
44
44
  * 是否开启水平滚动
45
45
  * 类型:boolean
46
46
  * 默认值:false
47
47
  */
48
48
  scrollX: makeBooleanProp(false),
49
+ /**
50
+ * 是否开启纵向滚动
51
+ * 类型:boolean
52
+ * 默认值:true
53
+ */
54
+ scrollY: makeBooleanProp(true),
55
+ /**
56
+ * 距顶部/左边多远时(单位px),触发 scrolltoupper 事件
57
+ * 类型:number | string
58
+ * 默认值:50
59
+ */
60
+ upperThreshold: makeNumericProp(50),
61
+ /**
62
+ * 距底部/右边多远时(单位px),触发 scrolltolower 事件
63
+ * 类型:number | string
64
+ * 默认值:50
65
+ */
66
+ lowerThreshold: makeNumericProp(50),
67
+ /**
68
+ * 设置横向滚动条位置
69
+ * 类型:number | string
70
+ * 默认值:0
71
+ */
72
+ scrollLeft: makeNumericProp(0),
73
+ /**
74
+ * 是否在设置滚动条位置时使用滚动动画
75
+ * 类型:boolean
76
+ * 默认值:false
77
+ */
78
+ scrollWithAnimation: makeBooleanProp(false),
79
+ /**
80
+ * iOS/Android 返回顶部功能,竖向有效
81
+ * 类型:boolean
82
+ * 默认值:false
83
+ */
84
+ enableBackToTop: makeBooleanProp(false),
85
+ /**
86
+ * 控制是否出现滚动条(App-nvue 有效)
87
+ * 类型:boolean
88
+ * 默认值:true
89
+ */
90
+ showScrollbar: makeBooleanProp(true),
91
+ /**
92
+ * 开启自定义下拉刷新
93
+ * 类型:boolean
94
+ * 默认值:false
95
+ */
96
+ refresherEnabled: makeBooleanProp(false),
97
+ /**
98
+ * 设置自定义下拉刷新阈值
99
+ * 类型:number
100
+ * 默认值:45
101
+ */
102
+ refresherThreshold: makeNumberProp(45),
103
+ /**
104
+ * 设置自定义下拉刷新默认样式
105
+ * 类型:'black' | 'white' | 'none'
106
+ * 默认值:'black'
107
+ */
108
+ refresherDefaultStyle: makeStringProp<'black' | 'white' | 'none'>('black'),
109
+ /**
110
+ * 设置自定义下拉刷新区域背景颜色
111
+ * 类型:string
112
+ * 默认值:'#FFF'
113
+ */
114
+ refresherBackground: makeStringProp('#FFF'),
115
+ /**
116
+ * 设置当前下拉刷新状态
117
+ * 类型:boolean
118
+ * 默认值:false
119
+ */
120
+ triggered: makeBooleanProp(false),
121
+ /**
122
+ * 启用 flexbox 布局
123
+ * 类型:boolean
124
+ * 默认值:false
125
+ */
126
+ enableFlex: makeBooleanProp(false),
127
+ /**
128
+ * 开启 scroll anchoring 特性
129
+ * 类型:boolean
130
+ * 默认值:false
131
+ */
132
+ scrollAnchoring: makeBooleanProp(false),
133
+ /**
134
+ * 暂无数据时的提示问题
135
+ * 类型:string
136
+ * 默认值:'暂无数据'
137
+ */
138
+ emptyText: makeStringProp('暂无数据'),
49
139
  /**
50
140
  * 是否开启虚拟滚动
51
141
  * 类型:boolean
@@ -54,7 +144,7 @@ export const virtualScrollProps = {
54
144
  virtual: makeBooleanProp(true)
55
145
  }
56
146
 
57
- export type CollapseExpose = {
147
+ export type VirtualScrollExpose = {
58
148
  scrollToTop: () => void
59
149
  scrollToBottom: () => void
60
150
  scrollToPosition: (position: number | string) => void
@@ -62,4 +152,4 @@ export type CollapseExpose = {
62
152
  scrollToElementById: (id: string | number) => void
63
153
  }
64
154
  export type VirtualScrollProps = ExtractPropTypes<typeof virtualScrollProps>
65
- export type VirtualScrollInstance = ComponentPublicInstance<VirtualScrollProps, CollapseExpose>
155
+ export type VirtualScrollInstance = ComponentPublicInstance<VirtualScrollProps, VirtualScrollExpose>
@@ -0,0 +1,18 @@
1
+ @import '../common/abstracts/variable';
2
+ @import '../common/abstracts/mixin';
3
+
4
+ @include b(waterfall) {
5
+ overflow: hidden;
6
+ @include e(wrapper) {
7
+ height: 100%;
8
+ overflow: hidden;
9
+ }
10
+ @include e(container) {
11
+ display: flex;
12
+ flex-direction: row;
13
+ overflow: hidden;
14
+ }
15
+ @include e(column) {
16
+ flex: 1;
17
+ }
18
+ }
@@ -0,0 +1,218 @@
1
+ <template>
2
+ <view :class="`oxy-waterfall ${customClass}`" :style="waterfallStyle">
3
+ <scroll-view
4
+ class="oxy-waterfall__wrapper"
5
+ :scroll-y="true"
6
+ :refresher-enabled="refresherEnabled"
7
+ :refresher-triggered="triggered"
8
+ @scroll="handleScroll"
9
+ @scrolltolower="handleScrolltolower"
10
+ @refresherrefresh="handleRefresherrefresh"
11
+ >
12
+ <view class="oxy-waterfall__container" :style="`${containerStyle}`">
13
+ <view v-for="(column, index) in columns" :key="index" class="oxy-waterfall__column" :style="index ? `${columnStyle}` : ''">
14
+ <oxy-waterfall-item
15
+ v-for="(item, cindex) in column"
16
+ :key="item[idKey]"
17
+ :col-index="index"
18
+ :custom-style="cindex ? `${itemStyle}` : ''"
19
+ :report-height-time="item.reportHeightTime"
20
+ @height="reportHeight"
21
+ >
22
+ <slot name="item" :item="item" :col-index="index" :item-index="cindex"></slot>
23
+ </oxy-waterfall-item>
24
+ </view>
25
+ </view>
26
+ <slot name="bottom">
27
+ <oxy-loadmore
28
+ v-if="loadmoreState"
29
+ :state="loadmoreState"
30
+ :loading-text="loadingText"
31
+ :finished-text="finishedText"
32
+ :error-text="errorText"
33
+ :loading-props="loadingProps"
34
+ @reload="onReload"
35
+ />
36
+ </slot>
37
+ </scroll-view>
38
+ </view>
39
+ </template>
40
+
41
+ <script lang="ts">
42
+ export default {
43
+ name: 'oxy-waterfall',
44
+ options: {
45
+ addGlobalClass: true,
46
+ virtualHost: true,
47
+ styleIsolation: 'shared'
48
+ }
49
+ }
50
+ </script>
51
+ <script lang="ts" setup>
52
+ import type { ScrollViewOnRefresherrefreshEvent, ScrollViewOnScrollEvent } from '@uni-helper/uni-app-types/index'
53
+ import { objToStyle, isDef, addUnit } from '../common/util'
54
+ import { computed, ref, watch, nextTick } from 'vue'
55
+ import { waterfallProps } from './types'
56
+
57
+ const emit = defineEmits(['scroll', 'scroll-to-lower', 'refresh', 'reload'])
58
+
59
+ const props = defineProps(waterfallProps)
60
+
61
+ const columns = ref<any>([])
62
+ const loadedIndex = ref(0)
63
+ const columnHeights = ref<any>([])
64
+
65
+ watch(
66
+ () => props.data,
67
+ () => {
68
+ loadColumn()
69
+ },
70
+ {
71
+ deep: true,
72
+ immediate: true
73
+ }
74
+ )
75
+
76
+ watch(
77
+ () => props.triggered,
78
+ (val) => {
79
+ if (val) {
80
+ loadedIndex.value = 0
81
+ }
82
+ },
83
+ {
84
+ deep: true,
85
+ immediate: true
86
+ }
87
+ )
88
+
89
+ function loadColumn() {
90
+ if (!loadedIndex.value || !columns.value.length) {
91
+ clearColumn()
92
+ columns.value = Array(props.columnCount)
93
+ .fill(null)
94
+ .map(() => [])
95
+ columnHeights.value = Array(props.columnCount)
96
+ .fill(null)
97
+ .map(() => 0)
98
+ }
99
+ nextTick(() => {
100
+ addRenderData()
101
+ })
102
+ }
103
+
104
+ function addRenderData() {
105
+ if (loadedIndex.value > props.data.length - 1) return
106
+ const current = props.data[loadedIndex.value]
107
+ pushRender(current)
108
+ loadedIndex.value += 1
109
+ }
110
+
111
+ function pushRender(data: Record<string, any>) {
112
+ const columnIndex = findSmallColIndex()
113
+ columns.value[columnIndex].push({
114
+ ...data,
115
+ reportHeightTime: Date.now()
116
+ })
117
+ }
118
+
119
+ function findSmallColIndex() {
120
+ let col = 0
121
+ let size = columnHeights.value[col]
122
+ columnHeights.value.forEach((item: number, index: number) => {
123
+ if (item < size) {
124
+ col = index
125
+ size = item
126
+ }
127
+ })
128
+ return col
129
+ }
130
+
131
+ function reportHeight(params: any) {
132
+ const { colIndex, height, reportHeightTimeChange } = params
133
+ columnHeights.value[colIndex] += height
134
+
135
+ if (reportHeightTimeChange) {
136
+ return
137
+ }
138
+
139
+ addRenderData()
140
+ }
141
+
142
+ // 更新
143
+ const update = (params: any) => {
144
+ const { colIndex, itemIndex, data = {}, reportHeight = false } = params
145
+ const item = columns.value[colIndex][itemIndex]
146
+ Object.keys(data).forEach((k) => {
147
+ item[k] = data[k]
148
+ })
149
+ // 当需要重新上报高度时,更新渲染时间
150
+ if (reportHeight) {
151
+ item.reportHeightTime = Date.now()
152
+ }
153
+ }
154
+
155
+ function clearColumn() {
156
+ loadedIndex.value = 0
157
+ columns.value = []
158
+ columnHeights.value = []
159
+ }
160
+
161
+ const waterfallStyle = computed(() => {
162
+ const style: Record<string, any> = {}
163
+ if (isDef(props.height)) {
164
+ style['height'] = addUnit(props.height)
165
+ }
166
+ return `${objToStyle(style)}${props.customStyle}`
167
+ })
168
+
169
+ const containerStyle = computed(() => {
170
+ const style: Record<string, any> = {}
171
+ if (props.leftGap) {
172
+ style['padding-left'] = addUnit(props.leftGap)
173
+ }
174
+ if (props.rightGap) {
175
+ style['padding-right'] = addUnit(props.rightGap)
176
+ }
177
+ return objToStyle(style)
178
+ })
179
+
180
+ const columnStyle = computed(() => {
181
+ const style: Record<string, any> = {}
182
+ if (props.columnGap) {
183
+ style['margin-left'] = addUnit(props.columnGap)
184
+ }
185
+ return objToStyle(style)
186
+ })
187
+
188
+ const itemStyle = computed(() => {
189
+ const style: Record<string, any> = {}
190
+ if (props.columnGap) {
191
+ style['margin-top'] = addUnit(props.columnGap)
192
+ }
193
+ return objToStyle(style)
194
+ })
195
+
196
+ const handleScroll = (event: ScrollViewOnScrollEvent) => {
197
+ emit('scroll', event)
198
+ }
199
+
200
+ const handleScrolltolower = () => {
201
+ emit('scroll-to-lower')
202
+ }
203
+
204
+ const handleRefresherrefresh = (e: ScrollViewOnRefresherrefreshEvent) => {
205
+ emit('refresh', e)
206
+ }
207
+
208
+ const onReload = () => {
209
+ emit('reload')
210
+ }
211
+
212
+ defineExpose({
213
+ update
214
+ })
215
+ </script>
216
+ <style lang="scss" scoped>
217
+ @import './index.scss';
218
+ </style>
@@ -0,0 +1,90 @@
1
+ import type { ExtractPropTypes, PropType } from 'vue'
2
+ import { baseProps, makeRequiredProp, makeStringProp, makeNumberProp, numericProp, makeBooleanProp } from '../common/props'
3
+ import { type LoadMoreState } from '../oxy-loadmore/types'
4
+ import { type LoadingProps } from '../oxy-loading/types'
5
+
6
+ export const waterfallProps = {
7
+ ...baseProps,
8
+
9
+ /**
10
+ * 瀑布流数据
11
+ * 类型:array
12
+ */
13
+ data: makeRequiredProp(Array<Record<string, any>>),
14
+
15
+ /**
16
+ * 数据的唯一标识
17
+ * 类型:string
18
+ * 默认值:'id'
19
+ */
20
+ idKey: makeStringProp('id'),
21
+
22
+ /**
23
+ * 容器的高度
24
+ */
25
+ height: numericProp,
26
+
27
+ /**
28
+ * 瀑布流的列数
29
+ * 类型:number
30
+ * 默认值:2
31
+ */
32
+ columnCount: makeNumberProp(2),
33
+
34
+ /**
35
+ * 列与列的间隙
36
+ * 类型:number
37
+ * 默认值:20
38
+ */
39
+ columnGap: makeNumberProp(20),
40
+
41
+ /**
42
+ * 左边和列表的间隙
43
+ * 类型:number
44
+ * 默认值:0
45
+ */
46
+ leftGap: makeNumberProp(0),
47
+
48
+ /**
49
+ * 右边和列表的间隙
50
+ * 类型:number
51
+ * 默认值:0
52
+ */
53
+ rightGap: makeNumberProp(0),
54
+
55
+ /**
56
+ * 开启下拉刷新
57
+ * 类型:boolean
58
+ * 默认值:false
59
+ */
60
+ refresherEnabled: makeBooleanProp(false),
61
+
62
+ /**
63
+ * 下拉刷新状态
64
+ * 类型:boolean
65
+ * 默认值:false
66
+ */
67
+ triggered: makeBooleanProp(false),
68
+
69
+ loadmoreState: String as PropType<LoadMoreState | ''>,
70
+
71
+ /**
72
+ * 加载提示文案
73
+ */
74
+ loadingText: String,
75
+ /**
76
+ * 全部加载完的提示文案
77
+ */
78
+ finishedText: String,
79
+ /**
80
+ * 加载失败的提示文案
81
+ */
82
+ errorText: String,
83
+ /**
84
+ * 加载中loading组件的属性
85
+ * 参考loading组件
86
+ */
87
+ loadingProps: Object as PropType<Partial<LoadingProps>>
88
+ }
89
+
90
+ export type WaterfallProps = ExtractPropTypes<typeof waterfallProps>
@@ -0,0 +1,8 @@
1
+ @import '../common/abstracts/variable';
2
+ @import '../common/abstracts/mixin';
3
+
4
+ @include b(waterfall-item) {
5
+ width: 100%;
6
+ overflow: hidden;
7
+ box-sizing: border-box;
8
+ }