oxy-uni-ui 1.0.1 → 1.2.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.
- package/LICENSE +1 -1
- package/attributes.json +1 -1
- package/components/common/abstracts/variable.scss +40 -4
- package/components/common/util.ts +44 -0
- package/components/composables/index.ts +1 -0
- package/components/composables/useVirtualScroll.ts +172 -0
- package/components/oxy-checkbox/index.scss +36 -6
- package/components/oxy-checkbox/oxy-checkbox.vue +5 -4
- package/components/oxy-checkbox/types.ts +2 -1
- package/components/oxy-col-picker/index.scss +18 -15
- package/components/oxy-col-picker/oxy-col-picker.vue +28 -3
- package/components/oxy-col-picker/types.ts +12 -0
- package/components/oxy-corner/index.scss +258 -0
- package/components/oxy-corner/oxy-corner.vue +67 -0
- package/components/oxy-corner/types.ts +50 -0
- package/components/oxy-drop-menu/index.scss +4 -0
- package/components/oxy-drop-menu/oxy-drop-menu.vue +5 -3
- package/components/oxy-drop-menu/types.ts +1 -1
- package/components/oxy-drop-menu-item/index.scss +4 -4
- package/components/oxy-drop-menu-item/oxy-drop-menu-item.vue +2 -0
- package/components/oxy-file-list/index.scss +83 -0
- package/components/oxy-file-list/oxy-file-list.vue +213 -0
- package/components/oxy-file-list/types.ts +54 -0
- package/components/oxy-list/index.scss +5 -0
- package/components/oxy-list/oxy-list.vue +206 -0
- package/components/oxy-list/types.ts +38 -0
- package/components/oxy-slider/index.scss +2 -2
- package/components/oxy-swiper/index.scss +1 -2
- package/components/oxy-textarea/oxy-textarea.vue +0 -4
- package/components/oxy-tree/components/tree-node-content.vue +72 -0
- package/components/oxy-tree/index.scss +83 -0
- package/components/oxy-tree/index.ts +51 -0
- package/components/oxy-tree/oxy-tree.vue +406 -0
- package/components/oxy-tree/types.ts +85 -0
- package/components/oxy-tree/utils.ts +51 -0
- package/components/oxy-upload/images/audio.png +0 -0
- package/components/oxy-upload/images/excle.png +0 -0
- package/components/oxy-upload/images/other.png +0 -0
- package/components/oxy-upload/images/pdf.png +0 -0
- package/components/oxy-upload/images/pic.png +0 -0
- package/components/oxy-upload/images/txt.png +0 -0
- package/components/oxy-upload/images/video.png +0 -0
- package/components/oxy-upload/images/word.png +0 -0
- package/components/oxy-upload/index.scss +50 -0
- package/components/oxy-upload/oxy-upload.vue +93 -7
- package/components/oxy-upload/types.ts +22 -1
- package/components/oxy-virtual-scroll/index.scss +35 -0
- package/components/oxy-virtual-scroll/oxy-virtual-scroll.vue +143 -0
- package/components/oxy-virtual-scroll/types.ts +155 -0
- package/components/oxy-virtual-scroll/virtual-scroll.ts +81 -0
- package/global.d.ts +3 -0
- package/locale/lang/ar-SA.ts +2 -1
- package/locale/lang/en-US.ts +10 -9
- package/locale/lang/zh-CN.ts +7 -6
- package/package.json +1 -1
- package/tags.json +1 -1
- package/web-types.json +1 -1
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view :class="['oxy-file-list', { 'oxy-file-list__list': props.type === 'list' }, customClass]">
|
|
3
|
+
<block v-if="props.type === 'list'">
|
|
4
|
+
<view v-for="(file, index) in props.data" :key="index" class="oxy-file-list__list--item" @click="onPreview(file)">
|
|
5
|
+
<image :src="getUploadImage(file)" mode="aspectFill" class="oxy-file-list__icon" />
|
|
6
|
+
<text class="oxy-file-list__name">{{ file.name }}</text>
|
|
7
|
+
</view>
|
|
8
|
+
</block>
|
|
9
|
+
<block v-else>
|
|
10
|
+
<view v-for="(file, index) in props.data" :key="index" class="oxy-file-list__item">
|
|
11
|
+
<view class="oxy-file-list__content">
|
|
12
|
+
<image v-if="isImage(file)" :src="file.url" mode="aspectFill" class="oxy-file-list__picture" @click="onPreview(file)" />
|
|
13
|
+
<image v-else :src="getUploadImage(file)" mode="aspectFill" class="oxy-file-list__icon" @click="onPreview(file)" />
|
|
14
|
+
</view>
|
|
15
|
+
<oxy-tooltip placement="top" :content="file.name">
|
|
16
|
+
<text class="oxy-file-list__name">{{ file.name }}</text>
|
|
17
|
+
</oxy-tooltip>
|
|
18
|
+
</view>
|
|
19
|
+
</block>
|
|
20
|
+
</view>
|
|
21
|
+
<oxy-video-preview ref="videoPreview"></oxy-video-preview>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script lang="ts">
|
|
25
|
+
export default {
|
|
26
|
+
name: 'oxy-file-list',
|
|
27
|
+
options: {
|
|
28
|
+
addGlobalClass: true,
|
|
29
|
+
virtualHost: true,
|
|
30
|
+
styleIsolation: 'shared'
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
<script lang="ts" setup>
|
|
35
|
+
import ImgPdf from '../oxy-upload/images/pdf.png'
|
|
36
|
+
import ImgWord from '../oxy-upload/images/word.png'
|
|
37
|
+
import ImgAudio from '../oxy-upload/images/audio.png'
|
|
38
|
+
import ImgVideo from '../oxy-upload/images/video.png'
|
|
39
|
+
import ImgPic from '../oxy-upload/images/pic.png'
|
|
40
|
+
import ImgOther from '../oxy-upload/images/other.png'
|
|
41
|
+
import OxyVideoPreview from '../oxy-video-preview/oxy-video-preview.vue'
|
|
42
|
+
import { ref } from 'vue'
|
|
43
|
+
import { isImageUrl, isVideoUrl, isAudioUrl, isPdfUrl, isDocUrl } from '../common/util'
|
|
44
|
+
import { type FileListItem, fileListProps } from './types'
|
|
45
|
+
import type { VideoPreviewInstance } from '../oxy-video-preview/types'
|
|
46
|
+
|
|
47
|
+
const imgs: AnyObject = {
|
|
48
|
+
pdf: ImgPdf,
|
|
49
|
+
word: ImgWord,
|
|
50
|
+
audio: ImgAudio,
|
|
51
|
+
video: ImgVideo,
|
|
52
|
+
pic: ImgPic,
|
|
53
|
+
other: ImgOther
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const props = defineProps(fileListProps)
|
|
57
|
+
|
|
58
|
+
const videoPreview = ref<VideoPreviewInstance>()
|
|
59
|
+
/**
|
|
60
|
+
* 预览图片
|
|
61
|
+
* @param file
|
|
62
|
+
*/
|
|
63
|
+
function handlePreviewImage(file: FileListItem) {
|
|
64
|
+
const { onPreviewFail } = props
|
|
65
|
+
uni.previewImage({
|
|
66
|
+
urls: [file.url],
|
|
67
|
+
current: 0,
|
|
68
|
+
fail() {
|
|
69
|
+
if (onPreviewFail) {
|
|
70
|
+
onPreviewFail({
|
|
71
|
+
file
|
|
72
|
+
})
|
|
73
|
+
} else {
|
|
74
|
+
uni.showToast({ title: '预览图片失败', icon: 'none' })
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 预览视频
|
|
82
|
+
* @param file
|
|
83
|
+
*/
|
|
84
|
+
function handlePreviewVieo(file: FileListItem) {
|
|
85
|
+
const { onPreviewFail } = props
|
|
86
|
+
// #ifdef MP-WEIXIN
|
|
87
|
+
uni.previewMedia({
|
|
88
|
+
current: 0,
|
|
89
|
+
sources: [
|
|
90
|
+
{
|
|
91
|
+
url: file.url,
|
|
92
|
+
type: 'video',
|
|
93
|
+
poster: file.thumb
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
fail() {
|
|
97
|
+
if (onPreviewFail) {
|
|
98
|
+
onPreviewFail({
|
|
99
|
+
file
|
|
100
|
+
})
|
|
101
|
+
} else {
|
|
102
|
+
uni.showToast({ title: '预览视频失败', icon: 'none' })
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
// #endif
|
|
107
|
+
// #ifndef MP-WEIXIN
|
|
108
|
+
videoPreview.value?.open({ url: file.url, poster: file.thumb, title: file.name })
|
|
109
|
+
// #endif
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 预览文件
|
|
114
|
+
* @param file
|
|
115
|
+
*/
|
|
116
|
+
function handlePreviewFile(file: FileListItem) {
|
|
117
|
+
uni.openDocument({
|
|
118
|
+
filePath: file.url,
|
|
119
|
+
showMenu: true
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function onPreviewImage(file: FileListItem) {
|
|
124
|
+
const { beforePreview } = props
|
|
125
|
+
if (beforePreview) {
|
|
126
|
+
beforePreview({
|
|
127
|
+
file,
|
|
128
|
+
resolve: (isPass: boolean) => {
|
|
129
|
+
isPass && handlePreviewImage(file)
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
} else {
|
|
133
|
+
handlePreviewImage(file)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function onPreviewVideo(file: FileListItem) {
|
|
138
|
+
const { beforePreview } = props
|
|
139
|
+
if (beforePreview) {
|
|
140
|
+
beforePreview({
|
|
141
|
+
file,
|
|
142
|
+
resolve: (isPass: boolean) => {
|
|
143
|
+
isPass && handlePreviewVieo(file)
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
} else {
|
|
147
|
+
handlePreviewVieo(file)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function onPreviewFile(file: FileListItem) {
|
|
152
|
+
const { beforePreview } = props
|
|
153
|
+
if (beforePreview) {
|
|
154
|
+
beforePreview({
|
|
155
|
+
file,
|
|
156
|
+
resolve: (isPass: boolean) => {
|
|
157
|
+
isPass && handlePreviewFile(file)
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
} else {
|
|
161
|
+
handlePreviewFile(file)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function onPreview(file: FileListItem) {
|
|
166
|
+
if (isImage(file)) {
|
|
167
|
+
onPreviewImage(file)
|
|
168
|
+
} else if (isVideo(file)) {
|
|
169
|
+
onPreviewVideo(file)
|
|
170
|
+
} else {
|
|
171
|
+
onPreviewFile(file)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function isVideo(file: FileListItem) {
|
|
176
|
+
return (file.name && isVideoUrl(file.name)) || isVideoUrl(file.url)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function isImage(file: FileListItem) {
|
|
180
|
+
return (file.name && isImageUrl(file.name)) || isImageUrl(file.url)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function isAudio(file: FileListItem) {
|
|
184
|
+
return (file.name && isAudioUrl(file.name)) || isAudioUrl(file.url)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function isPdf(file: FileListItem) {
|
|
188
|
+
return (file.name && isPdfUrl(file.name)) || isPdfUrl(file.url)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function isDoc(file: FileListItem) {
|
|
192
|
+
return (file.name && isDocUrl(file.name)) || isDocUrl(file.url)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function getUploadImage(file: FileListItem) {
|
|
196
|
+
if (isPdf(file)) {
|
|
197
|
+
return imgs.pdf
|
|
198
|
+
} else if (isDoc(file)) {
|
|
199
|
+
return imgs.word
|
|
200
|
+
} else if (isAudio(file)) {
|
|
201
|
+
return imgs.audio
|
|
202
|
+
} else if (isVideo(file)) {
|
|
203
|
+
return imgs.video
|
|
204
|
+
} else if (isImage(file)) {
|
|
205
|
+
return imgs.pic
|
|
206
|
+
} else {
|
|
207
|
+
return imgs.other
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
</script>
|
|
211
|
+
<style lang="scss" scoped>
|
|
212
|
+
@import './index.scss';
|
|
213
|
+
</style>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ExtractPropTypes, PropType } from 'vue'
|
|
2
|
+
import { baseProps, makeArrayProp, makeStringProp } from '../common/props'
|
|
3
|
+
|
|
4
|
+
export type FileListItem = {
|
|
5
|
+
[key: string]: any
|
|
6
|
+
// 文件名称
|
|
7
|
+
name?: string
|
|
8
|
+
// 文件地址
|
|
9
|
+
url: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type FileListType = 'card' | 'list'
|
|
13
|
+
|
|
14
|
+
export type FileListBeforePreviewOption = {
|
|
15
|
+
file: FileListItem
|
|
16
|
+
resolve: (isPass: boolean) => void
|
|
17
|
+
}
|
|
18
|
+
export type FileListBeforePreview = (option: FileListBeforePreviewOption) => void
|
|
19
|
+
|
|
20
|
+
export type FileListOnPreviewFailOption = {
|
|
21
|
+
file: FileListItem
|
|
22
|
+
}
|
|
23
|
+
export type FileListOnPreviewFail = (option: FileListOnPreviewFailOption) => void
|
|
24
|
+
|
|
25
|
+
export const fileListProps = {
|
|
26
|
+
...baseProps,
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 文件列表
|
|
30
|
+
* 类型:array
|
|
31
|
+
* 默认值:[]
|
|
32
|
+
*/
|
|
33
|
+
data: makeArrayProp<FileListItem>(),
|
|
34
|
+
/**
|
|
35
|
+
* 文件列表类型
|
|
36
|
+
* 类型:string
|
|
37
|
+
* 默认值:'card'
|
|
38
|
+
*/
|
|
39
|
+
type: makeStringProp<FileListType>('list'),
|
|
40
|
+
/**
|
|
41
|
+
* 文件预览前的钩子,参数为预览的文件,若返回false或者返回Promise且被reject,则停止预览。
|
|
42
|
+
* 类型:function({file,resolve})
|
|
43
|
+
* 默认值:-
|
|
44
|
+
*/
|
|
45
|
+
beforePreview: Function as PropType<FileListBeforePreview>,
|
|
46
|
+
/**
|
|
47
|
+
* 预览失败执行操作
|
|
48
|
+
* 类型:function({file})
|
|
49
|
+
* 默认值:-
|
|
50
|
+
*/
|
|
51
|
+
onPreviewFail: Function as PropType<FileListOnPreviewFail>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type CornerProps = ExtractPropTypes<typeof fileListProps>
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view :class="['oxy-list', 'oxy-virtual-scroll', customClass]" :style="customStyle">
|
|
3
|
+
<!-- 滚动内容区域 -->
|
|
4
|
+
<scroll-view
|
|
5
|
+
v-if="data.length"
|
|
6
|
+
:style="{ height: height }"
|
|
7
|
+
class="oxy-virtual-scroll__view"
|
|
8
|
+
:scroll-y="true"
|
|
9
|
+
:scroll-x="scrollX"
|
|
10
|
+
:upper-threshold="upperThreshold"
|
|
11
|
+
:lower-threshold="lowerThreshold"
|
|
12
|
+
:scroll-top="scrollTop"
|
|
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"
|
|
24
|
+
@scroll="handleScroll"
|
|
25
|
+
@scrolltoupper="onScrollToUpper"
|
|
26
|
+
@scrolltolower="onScrollToLower"
|
|
27
|
+
@refresherrefresh="onRefresh"
|
|
28
|
+
@refresherrestore="onRestore"
|
|
29
|
+
@refresherabort="onAbort"
|
|
30
|
+
>
|
|
31
|
+
<view class="oxy-virtual-scroll__container" :style="{ height: totalHeight + 'px' }">
|
|
32
|
+
<view class="oxy-virtual-scroll__items" :style="{ transform: `translateY(${virtualOffsetY}px)` }">
|
|
33
|
+
<view v-for="(item, index) in virtualData" :key="index">
|
|
34
|
+
<slot name="item" :item="item" :index="startIndex + index"></slot>
|
|
35
|
+
</view>
|
|
36
|
+
<slot name="bottom">
|
|
37
|
+
<oxy-loadmore
|
|
38
|
+
v-if="loadmoreState"
|
|
39
|
+
:state="loadmoreState"
|
|
40
|
+
:loading-text="loadingText"
|
|
41
|
+
:finished-text="finishedText"
|
|
42
|
+
:error-text="errorText"
|
|
43
|
+
:loading-props="loadingProps"
|
|
44
|
+
@reload="onReload"
|
|
45
|
+
/>
|
|
46
|
+
</slot>
|
|
47
|
+
</view>
|
|
48
|
+
</view>
|
|
49
|
+
</scroll-view>
|
|
50
|
+
|
|
51
|
+
<view v-else>
|
|
52
|
+
<oxy-status-tip image="content" :tip="emptyText" />
|
|
53
|
+
</view>
|
|
54
|
+
|
|
55
|
+
<!-- 回到顶部按钮 -->
|
|
56
|
+
<view v-if="showBackToTop && showBackTopBtn" class="oxy-virtual-scroll__back-top" @click="scrollToTop">
|
|
57
|
+
<oxy-icon name="backtop" color="#fff" size="20px"></oxy-icon>
|
|
58
|
+
</view>
|
|
59
|
+
</view>
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<script lang="ts">
|
|
63
|
+
export default {
|
|
64
|
+
name: 'oxy-list',
|
|
65
|
+
options: {
|
|
66
|
+
addGlobalClass: true,
|
|
67
|
+
virtualHost: true,
|
|
68
|
+
styleIsolation: 'shared'
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<script lang="ts" setup>
|
|
74
|
+
import { computed, toRefs } from 'vue'
|
|
75
|
+
import { type ListExpose, listProps } from './types'
|
|
76
|
+
import type {
|
|
77
|
+
ScrollViewOnRefresherabortEvent,
|
|
78
|
+
ScrollViewOnRefresherrefreshEvent,
|
|
79
|
+
ScrollViewOnRefresherrestoreEvent,
|
|
80
|
+
ScrollViewOnScrollEvent
|
|
81
|
+
} from '@uni-helper/uni-app-types/index'
|
|
82
|
+
import { useVirtualScroll } from '../composables/useVirtualScroll'
|
|
83
|
+
|
|
84
|
+
const props = defineProps(listProps)
|
|
85
|
+
|
|
86
|
+
const emit = defineEmits(['scroll-to-lower', 'reload', 'scroll-to-upper', 'scroll', 'refresh', 'restore', 'abort'])
|
|
87
|
+
|
|
88
|
+
const defaultscrollConfig = {
|
|
89
|
+
refresherEnabled: false,
|
|
90
|
+
refresherThreshold: 60,
|
|
91
|
+
scrollWithAnimation: false
|
|
92
|
+
} as const
|
|
93
|
+
|
|
94
|
+
// 解构props用于组合式函数
|
|
95
|
+
const {
|
|
96
|
+
data,
|
|
97
|
+
virtual,
|
|
98
|
+
height,
|
|
99
|
+
itemHeight,
|
|
100
|
+
idKey,
|
|
101
|
+
showBackToTop,
|
|
102
|
+
backToTopThreshold,
|
|
103
|
+
scrollY,
|
|
104
|
+
scrollX,
|
|
105
|
+
upperThreshold,
|
|
106
|
+
lowerThreshold,
|
|
107
|
+
scrollLeft,
|
|
108
|
+
scrollWithAnimation,
|
|
109
|
+
enableBackToTop,
|
|
110
|
+
showScrollbar,
|
|
111
|
+
refresherEnabled,
|
|
112
|
+
refresherThreshold,
|
|
113
|
+
refresherDefaultStyle,
|
|
114
|
+
refresherBackground,
|
|
115
|
+
triggered,
|
|
116
|
+
enableFlex,
|
|
117
|
+
scrollAnchoring
|
|
118
|
+
} = toRefs(props)
|
|
119
|
+
|
|
120
|
+
// 使用虚拟滚动组合式函数
|
|
121
|
+
const {
|
|
122
|
+
scrollTop,
|
|
123
|
+
showBackTopBtn,
|
|
124
|
+
virtualData,
|
|
125
|
+
startIndex,
|
|
126
|
+
virtualOffsetY,
|
|
127
|
+
totalHeight,
|
|
128
|
+
displayData,
|
|
129
|
+
initScrollData,
|
|
130
|
+
initScrollEngine,
|
|
131
|
+
updateVisibleData,
|
|
132
|
+
scrollToTop: virtualScrollToTop,
|
|
133
|
+
scrollToBottom: virtualScrollToBottom,
|
|
134
|
+
scrollToPosition: virtualScrollToPosition,
|
|
135
|
+
scrollToElement: virtualScrollToElement,
|
|
136
|
+
scrollToElementById: virtualScrollToElementById,
|
|
137
|
+
onScroll: handleVirtualScroll
|
|
138
|
+
} = useVirtualScroll({
|
|
139
|
+
data,
|
|
140
|
+
virtual,
|
|
141
|
+
height,
|
|
142
|
+
itemHeight,
|
|
143
|
+
idKey,
|
|
144
|
+
backToTopThreshold
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const onScrollToLower = async () => {
|
|
148
|
+
emit('scroll-to-lower')
|
|
149
|
+
}
|
|
150
|
+
const onScrollToUpper = async () => {
|
|
151
|
+
emit('scroll-to-upper')
|
|
152
|
+
}
|
|
153
|
+
const onReload = async () => {
|
|
154
|
+
emit('reload')
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 滚动事件处理
|
|
158
|
+
const handleScroll = async (event: ScrollViewOnScrollEvent) => {
|
|
159
|
+
handleVirtualScroll(event.detail.scrollTop)
|
|
160
|
+
emit('scroll', event)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 下拉刷新
|
|
164
|
+
const onRefresh = (e: ScrollViewOnRefresherrefreshEvent) => {
|
|
165
|
+
emit('refresh', e)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 下拉刷新被复位
|
|
169
|
+
const onRestore = (e: ScrollViewOnRefresherrestoreEvent) => {
|
|
170
|
+
emit('restore', e)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// 下拉刷新被中止
|
|
174
|
+
const onAbort = (e: ScrollViewOnRefresherabortEvent) => {
|
|
175
|
+
emit('abort', e)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 滚动方法
|
|
179
|
+
const scrollToTop = () => {
|
|
180
|
+
virtualScrollToTop()
|
|
181
|
+
}
|
|
182
|
+
const scrollToBottom = () => {
|
|
183
|
+
virtualScrollToBottom()
|
|
184
|
+
}
|
|
185
|
+
const scrollToPosition = (position: number | string) => {
|
|
186
|
+
virtualScrollToPosition(position)
|
|
187
|
+
}
|
|
188
|
+
const scrollToElement = (item: any) => {
|
|
189
|
+
virtualScrollToElement(item)
|
|
190
|
+
}
|
|
191
|
+
const scrollToElementById = (id: string | number) => {
|
|
192
|
+
virtualScrollToElementById(id)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
defineExpose<ListExpose>({
|
|
196
|
+
scrollToTop,
|
|
197
|
+
scrollToBottom,
|
|
198
|
+
scrollToPosition,
|
|
199
|
+
scrollToElement,
|
|
200
|
+
scrollToElementById
|
|
201
|
+
})
|
|
202
|
+
</script>
|
|
203
|
+
|
|
204
|
+
<style lang="scss" scoped>
|
|
205
|
+
@import './index.scss';
|
|
206
|
+
</style>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ComponentPublicInstance, ExtractPropTypes, PropType } from 'vue'
|
|
2
|
+
import { makeBooleanProp, makeNumberProp, makeStringProp } from '../common/props'
|
|
3
|
+
import { type LoadMoreState } from '../oxy-loadmore/types'
|
|
4
|
+
import { type LoadingProps } from '../oxy-loading/types'
|
|
5
|
+
import { virtualScrollProps } from '../oxy-virtual-scroll/types'
|
|
6
|
+
|
|
7
|
+
export const listProps = {
|
|
8
|
+
...virtualScrollProps,
|
|
9
|
+
|
|
10
|
+
loadmoreState: String as PropType<LoadMoreState>,
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 加载提示文案
|
|
14
|
+
*/
|
|
15
|
+
loadingText: String,
|
|
16
|
+
/**
|
|
17
|
+
* 全部加载完的提示文案
|
|
18
|
+
*/
|
|
19
|
+
finishedText: String,
|
|
20
|
+
/**
|
|
21
|
+
* 加载失败的提示文案
|
|
22
|
+
*/
|
|
23
|
+
errorText: String,
|
|
24
|
+
/**
|
|
25
|
+
* 加载中loading组件的属性
|
|
26
|
+
* 参考loading组件
|
|
27
|
+
*/
|
|
28
|
+
loadingProps: Object as PropType<Partial<LoadingProps>>
|
|
29
|
+
}
|
|
30
|
+
export type ListExpose = {
|
|
31
|
+
scrollToTop: () => void
|
|
32
|
+
scrollToBottom: () => void
|
|
33
|
+
scrollToPosition: (position: number | string) => void
|
|
34
|
+
scrollToElement: (item: any) => void
|
|
35
|
+
scrollToElementById: (id: string | number) => void
|
|
36
|
+
}
|
|
37
|
+
export type ListProps = ExtractPropTypes<typeof listProps>
|
|
38
|
+
export type ListInstance = ComponentPublicInstance<ListProps, ListExpose>
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
display: flex;
|
|
26
26
|
flex-flow: row nowrap;
|
|
27
27
|
align-items: center;
|
|
28
|
-
height: calc($-slider-handle-radius * 3);
|
|
28
|
+
// height: calc($-slider-handle-radius * 3);
|
|
29
29
|
|
|
30
30
|
@include e(label-min, label-max) {
|
|
31
31
|
font-size: $-slider-fs;
|
|
@@ -95,4 +95,4 @@
|
|
|
95
95
|
color: $-slider-disabled-color;
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
-
}
|
|
98
|
+
}
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
padding: $-swiper-item-padding;
|
|
18
18
|
|
|
19
19
|
@include m(slot) {
|
|
20
|
-
// 问题来自 https://github.com/dcloudio/uni-app/issues/4629,支付宝小程序不支持属性选择器
|
|
21
20
|
/* #ifdef MP */
|
|
22
21
|
:deep() {
|
|
23
22
|
/* #ifdef MP-WEIXIN */
|
|
@@ -50,4 +49,4 @@
|
|
|
50
49
|
color: $-swiper-item-text-color;
|
|
51
50
|
font-size: $-swiper-item-text-fs;
|
|
52
51
|
}
|
|
53
|
-
}
|
|
52
|
+
}
|
|
@@ -179,10 +179,6 @@ const isRequired = computed(() => {
|
|
|
179
179
|
|
|
180
180
|
// 当前文本域文字长度
|
|
181
181
|
const currentLength = computed(() => {
|
|
182
|
-
/**
|
|
183
|
-
* 使用Array.from处理多码元字符以获取正确的长度
|
|
184
|
-
* @link https://github.com/Moonofweisheng/oxy-uni-ui/issues/933
|
|
185
|
-
*/
|
|
186
182
|
return Array.from(String(formatValue(props.modelValue))).length
|
|
187
183
|
})
|
|
188
184
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="oxy-tree-node-content" :style="getNodeStyle()" :class="computedClass" @click="handleNodeClick">
|
|
3
|
+
<oxy-icon @click.stop="toggleExpand" name="fill-arrow-down" class="oxy-tree-node-icon" size="22px"></oxy-icon>
|
|
4
|
+
<oxy-checkbox
|
|
5
|
+
v-if="treeProps?.showCheckbox"
|
|
6
|
+
:modelValue="node.checked"
|
|
7
|
+
:disabled="node.disabled"
|
|
8
|
+
:indeterminate="node.immediate"
|
|
9
|
+
shape="square"
|
|
10
|
+
></oxy-checkbox>
|
|
11
|
+
<view class="oxy-tree-node">
|
|
12
|
+
<slot :node="node" :data="node.data">
|
|
13
|
+
{{ node.label }}
|
|
14
|
+
</slot>
|
|
15
|
+
</view>
|
|
16
|
+
</view>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script lang="ts">
|
|
20
|
+
export default {
|
|
21
|
+
name: 'oxy-tree-node-content'
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<script lang="ts" setup>
|
|
26
|
+
import { computed, toRefs, type Ref } from 'vue'
|
|
27
|
+
import type { TextProps, TreeNode } from '../types'
|
|
28
|
+
import { inject } from 'vue'
|
|
29
|
+
// 获取组件的 props 和 emit 函数
|
|
30
|
+
const emit = defineEmits<{
|
|
31
|
+
(e: 'click', node: TreeNode): void
|
|
32
|
+
(e: 'toggle-expand', node: TreeNode, flag: boolean): void
|
|
33
|
+
(e: 'toggle-checked', node: TreeNode, flag: boolean, isClick: boolean): void
|
|
34
|
+
}>()
|
|
35
|
+
|
|
36
|
+
const props = defineProps<{
|
|
37
|
+
node: TreeNode
|
|
38
|
+
}>()
|
|
39
|
+
const treeProps = inject<TextProps>('treeProps')
|
|
40
|
+
const currentNode = inject<Ref<TreeNode | undefined>>('currentNode')
|
|
41
|
+
|
|
42
|
+
function getNodeStyle() {
|
|
43
|
+
return {
|
|
44
|
+
paddingLeft: `${(props.node.level - 1) * (treeProps?.indent || 16)}px`
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function toggleExpand() {
|
|
49
|
+
emit('toggle-expand', props.node, !props.node.expanded)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const computedClass = computed(() => {
|
|
53
|
+
const currentValue = currentNode?.value
|
|
54
|
+
return {
|
|
55
|
+
expanded: props.node.expanded,
|
|
56
|
+
checked: props.node.checked,
|
|
57
|
+
'is-leaf': props.node.isLeaf,
|
|
58
|
+
immediate: props.node.immediate,
|
|
59
|
+
'is-current': currentValue === props.node,
|
|
60
|
+
'is-disabled': props.node.disabled
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const handleNodeClick = () => {
|
|
65
|
+
emit('click', props.node)
|
|
66
|
+
emit('toggle-checked', props.node, !props.node.checked, true)
|
|
67
|
+
}
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<style lang="scss" scoped>
|
|
71
|
+
@import '../index.scss';
|
|
72
|
+
</style>
|