oxy-uni-ui 1.0.1 → 1.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.
- package/LICENSE +1 -1
- package/attributes.json +1 -1
- package/components/common/abstracts/variable.scss +32 -4
- package/components/common/util.ts +44 -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 +138 -0
- package/components/oxy-corner/oxy-corner.vue +66 -0
- package/components/oxy-corner/types.ts +43 -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 +4 -0
- package/components/oxy-list/oxy-list.vue +125 -0
- package/components/oxy-list/types.ts +50 -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 +61 -0
- package/components/oxy-tree/index.ts +51 -0
- package/components/oxy-tree/oxy-tree.vue +289 -0
- package/components/oxy-tree/types.ts +48 -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 +184 -0
- package/components/oxy-virtual-scroll/types.ts +65 -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 +2 -1
- package/locale/lang/zh-CN.ts +2 -1
- package/package.json +1 -1
- package/tags.json +1 -1
- package/web-types.json +1 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
.oxy-theme-dark {
|
|
5
5
|
@include b(drop-menu) {
|
|
6
6
|
color: $-dark-color;
|
|
7
|
+
|
|
7
8
|
@include e(list) {
|
|
8
9
|
background-color: $-dark-background2;
|
|
9
10
|
}
|
|
@@ -27,6 +28,7 @@
|
|
|
27
28
|
text-align: center;
|
|
28
29
|
background-color: #fff;
|
|
29
30
|
}
|
|
31
|
+
|
|
30
32
|
@include e(item) {
|
|
31
33
|
flex: 1;
|
|
32
34
|
min-width: 0;
|
|
@@ -40,11 +42,13 @@
|
|
|
40
42
|
.oxy-drop-menu__item-title::after {
|
|
41
43
|
opacity: 1;
|
|
42
44
|
}
|
|
45
|
+
|
|
43
46
|
:deep(.oxy-drop-menu__arrow) {
|
|
44
47
|
transform: rotate(-180deg);
|
|
45
48
|
transform-origin: center center;
|
|
46
49
|
}
|
|
47
50
|
}
|
|
51
|
+
|
|
48
52
|
@include when(disabled) {
|
|
49
53
|
color: $-drop-menu-disabled-color;
|
|
50
54
|
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
v-for="(child, index) in children"
|
|
19
19
|
:key="index"
|
|
20
20
|
@click="toggle(child)"
|
|
21
|
-
:class="`oxy-drop-menu__item ${child.disabled ? 'is-disabled' : ''} ${child.$.exposed
|
|
21
|
+
:class="`oxy-drop-menu__item ${child.disabled ? 'is-disabled' : ''} ${child.$.exposed?.getShowPop() ? 'is-active' : ''}`"
|
|
22
22
|
>
|
|
23
23
|
<view class="oxy-drop-menu__item-title">
|
|
24
24
|
<view class="oxy-drop-menu__item-title-text">{{ getDisplayTitle(child) }}</view>
|
|
@@ -60,6 +60,8 @@ const windowHeight = ref<number>(0)
|
|
|
60
60
|
const modalStyle = computed(() => {
|
|
61
61
|
return props.direction === 'down'
|
|
62
62
|
? `top: calc(var(--window-top) + ${offset.value}px); bottom: 0;`
|
|
63
|
+
: props.direction === 'bottom'
|
|
64
|
+
? 'top: var(--window-top); bottom: 0;'
|
|
63
65
|
: `top: 0; bottom: calc(var(--window-bottom) + ${offset.value}px)`
|
|
64
66
|
})
|
|
65
67
|
|
|
@@ -95,7 +97,7 @@ linkChildren({ props, fold, offset })
|
|
|
95
97
|
watch(
|
|
96
98
|
() => props.direction,
|
|
97
99
|
(newValue) => {
|
|
98
|
-
if (!['up', 'down'].includes(newValue)) {
|
|
100
|
+
if (!['up', 'down', 'bottom'].includes(newValue)) {
|
|
99
101
|
// eslint-disable-next-line quotes
|
|
100
102
|
console.error("[Oxy ui] warning(oxy-drop-menu): direction must be 'up' or 'down'")
|
|
101
103
|
}
|
|
@@ -142,7 +144,7 @@ function fold(child: any) {
|
|
|
142
144
|
getRect(`#${dropMenuId.value}`, false, proxy).then((rect) => {
|
|
143
145
|
if (!rect) return
|
|
144
146
|
const { top, bottom } = rect
|
|
145
|
-
if (props.direction === 'down') {
|
|
147
|
+
if (props.direction === 'down' || props.direction === 'bottom') {
|
|
146
148
|
offset.value = Number(bottom)
|
|
147
149
|
} else {
|
|
148
150
|
offset.value = windowHeight.value - Number(top)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ExtractPropTypes, type InjectionKey, type Ref } from 'vue'
|
|
2
2
|
import { baseProps, makeBooleanProp, makeNumberProp, makeStringProp } from '../common/props'
|
|
3
3
|
|
|
4
|
-
export type DropDirection = 'up' | 'down'
|
|
4
|
+
export type DropDirection = 'up' | 'down' | 'bottom'
|
|
5
5
|
|
|
6
6
|
export type DropMenuProvide = {
|
|
7
7
|
props: Partial<DropMenuProps>
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
color: $-drop-menu-item-color;
|
|
21
21
|
width: 100%;
|
|
22
22
|
z-index: 101;
|
|
23
|
-
|
|
24
|
-
@include e(popup){
|
|
23
|
+
|
|
24
|
+
@include e(popup) {
|
|
25
25
|
position: absolute;
|
|
26
26
|
max-height: 80%;
|
|
27
27
|
}
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
@include e(title){
|
|
43
|
+
@include e(title) {
|
|
44
44
|
display: block;
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
margin-left: 2px;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
@include edeep(icon){
|
|
54
|
+
@include edeep(icon) {
|
|
55
55
|
display: block;
|
|
56
56
|
font-size: $-drop-menu-option-check-size;
|
|
57
57
|
}
|
|
@@ -91,6 +91,8 @@ const positionStyle = computed(() => {
|
|
|
91
91
|
style =
|
|
92
92
|
dropMenu.props.direction === 'down'
|
|
93
93
|
? `top: calc(var(--window-top) + ${dropMenu.offset.value}px); bottom: 0;`
|
|
94
|
+
: dropMenu.props.direction === 'bottom'
|
|
95
|
+
? 'top: calc(var(--window-top); bottom: 0;'
|
|
94
96
|
: `top: 0; bottom: calc(var(--window-bottom) + ${dropMenu.offset.value}px)`
|
|
95
97
|
} else {
|
|
96
98
|
style = ''
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
@import '../common/abstracts/variable';
|
|
2
|
+
@import '../common/abstracts/mixin';
|
|
3
|
+
|
|
4
|
+
@include b(file-list) {
|
|
5
|
+
position: relative;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-wrap: wrap;
|
|
8
|
+
margin: 0 -8px;
|
|
9
|
+
@include e(item) {
|
|
10
|
+
position: relative;
|
|
11
|
+
width: calc(33.33% - 16px);
|
|
12
|
+
height: $-file-list-height;
|
|
13
|
+
margin: 0 8px 32px;
|
|
14
|
+
}
|
|
15
|
+
@include e(content) {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
align-items: center;
|
|
20
|
+
width: 100%;
|
|
21
|
+
height: 100%;
|
|
22
|
+
border: 1px solid $-color-border;
|
|
23
|
+
border-radius: 8px;
|
|
24
|
+
}
|
|
25
|
+
@include e(picture) {
|
|
26
|
+
position: relative;
|
|
27
|
+
display: block;
|
|
28
|
+
width: 100%;
|
|
29
|
+
height: 100%;
|
|
30
|
+
border-radius: 8px;
|
|
31
|
+
}
|
|
32
|
+
@include e(icon) {
|
|
33
|
+
width: 32px;
|
|
34
|
+
height: 32px;
|
|
35
|
+
}
|
|
36
|
+
@include e(name) {
|
|
37
|
+
display: block;
|
|
38
|
+
width: 100%;
|
|
39
|
+
font-size: $-upload-file-fs;
|
|
40
|
+
color: $-upload-file-color;
|
|
41
|
+
padding: 0 4px;
|
|
42
|
+
text-align: center;
|
|
43
|
+
margin-top: 8px;
|
|
44
|
+
white-space: nowrap;
|
|
45
|
+
overflow: hidden;
|
|
46
|
+
text-overflow: ellipsis;
|
|
47
|
+
}
|
|
48
|
+
:deep(.oxy-tooltip) {
|
|
49
|
+
width: 100%;
|
|
50
|
+
.oxy-tooltip__inner {
|
|
51
|
+
width: fit-content;
|
|
52
|
+
white-space: pre-wrap;
|
|
53
|
+
word-break: break-all;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
&.oxy-file-list__list {
|
|
57
|
+
display: block;
|
|
58
|
+
margin: 0;
|
|
59
|
+
.oxy-file-list__list--item {
|
|
60
|
+
width: 100%;
|
|
61
|
+
background: #f8f9ff;
|
|
62
|
+
padding: 8px 12px;
|
|
63
|
+
border-radius: 4px;
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-direction: row;
|
|
66
|
+
align-items: center;
|
|
67
|
+
.oxy-file-list__icon {
|
|
68
|
+
width: 24px;
|
|
69
|
+
height: 24px;
|
|
70
|
+
margin-right: 8px;
|
|
71
|
+
}
|
|
72
|
+
.oxy-file-list__name {
|
|
73
|
+
flex: 1;
|
|
74
|
+
text-align: left;
|
|
75
|
+
margin: 0;
|
|
76
|
+
padding: 0;
|
|
77
|
+
}
|
|
78
|
+
& + .oxy-file-list__list--item {
|
|
79
|
+
margin-top: 8px;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -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,125 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view :class="['oxy-list', customClass]" :style="customStyle">
|
|
3
|
+
<oxy-virtual-scroll
|
|
4
|
+
ref="virtualScrollRef"
|
|
5
|
+
:is-virtual="virtual"
|
|
6
|
+
:data="data"
|
|
7
|
+
:height="height"
|
|
8
|
+
:item-height="itemHeight"
|
|
9
|
+
:id-key="idKey"
|
|
10
|
+
:show-back-to-top="showBackToTop"
|
|
11
|
+
:back-to-top-threshold="backToTopThreshold"
|
|
12
|
+
:refresher-triggered="triggered"
|
|
13
|
+
v-bind="{ ...defaultscrollConfig, ...scrollConfig }"
|
|
14
|
+
@scroll-to-upper="onScrollToUpper"
|
|
15
|
+
@scroll-to-lower="onScrollToLower"
|
|
16
|
+
@scroll="onScroll"
|
|
17
|
+
@refresherrefresh="onRefresh"
|
|
18
|
+
@refresherrestore="onRestore"
|
|
19
|
+
@refresherabort="onAbort"
|
|
20
|
+
>
|
|
21
|
+
<template #item="{ item }">
|
|
22
|
+
<slot name="item" :item="item"></slot>
|
|
23
|
+
</template>
|
|
24
|
+
<template #bottom>
|
|
25
|
+
<oxy-loadmore
|
|
26
|
+
v-if="loadmoreState"
|
|
27
|
+
:state="loadmoreState"
|
|
28
|
+
:loading-text="loadingText"
|
|
29
|
+
:finished-text="finishedText"
|
|
30
|
+
:error-text="errorText"
|
|
31
|
+
:loading-props="loadingProps"
|
|
32
|
+
@reload="onReload"
|
|
33
|
+
/>
|
|
34
|
+
</template>
|
|
35
|
+
</oxy-virtual-scroll>
|
|
36
|
+
</view>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<script lang="ts">
|
|
40
|
+
export default {
|
|
41
|
+
name: 'oxy-list',
|
|
42
|
+
options: {
|
|
43
|
+
addGlobalClass: true,
|
|
44
|
+
virtualHost: true,
|
|
45
|
+
styleIsolation: 'shared'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<script lang="ts" setup>
|
|
51
|
+
import { computed, ref } from 'vue'
|
|
52
|
+
import { type ListExpose, listProps } from './types'
|
|
53
|
+
import type {
|
|
54
|
+
ScrollViewOnRefresherabortEvent,
|
|
55
|
+
ScrollViewOnRefresherrefreshEvent,
|
|
56
|
+
ScrollViewOnRefresherrestoreEvent,
|
|
57
|
+
ScrollViewOnScrollEvent
|
|
58
|
+
} from '@uni-helper/uni-app-types/index'
|
|
59
|
+
import type { VirtualScrollInstance } from '../oxy-virtual-scroll/types'
|
|
60
|
+
|
|
61
|
+
const props = defineProps(listProps)
|
|
62
|
+
|
|
63
|
+
const emit = defineEmits(['scroll-to-lower', 'reload', 'scroll-to-upper', 'scroll', 'refresh', 'restore', 'abort'])
|
|
64
|
+
const virtualScrollRef = ref<VirtualScrollInstance | null>(null)
|
|
65
|
+
|
|
66
|
+
const defaultscrollConfig = {
|
|
67
|
+
refresherEnabled: true,
|
|
68
|
+
refresherThreshold: 60,
|
|
69
|
+
scrollWithAnimation: false
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const onScrollToLower = async () => {
|
|
73
|
+
emit('scroll-to-lower')
|
|
74
|
+
}
|
|
75
|
+
const onScrollToUpper = async () => {
|
|
76
|
+
emit('scroll-to-upper')
|
|
77
|
+
}
|
|
78
|
+
const onReload = async () => {
|
|
79
|
+
emit('reload')
|
|
80
|
+
}
|
|
81
|
+
const onScroll = async (event: ScrollViewOnScrollEvent) => {
|
|
82
|
+
emit('scroll', event)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 下拉刷新
|
|
86
|
+
const onRefresh = (e: ScrollViewOnRefresherrefreshEvent) => {
|
|
87
|
+
emit('refresh', e)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 下拉刷新被复位
|
|
91
|
+
const onRestore = (e: ScrollViewOnRefresherrestoreEvent) => {
|
|
92
|
+
emit('restore', e)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 下拉刷新被中止
|
|
96
|
+
const onAbort = (e: ScrollViewOnRefresherabortEvent) => {
|
|
97
|
+
emit('abort', e)
|
|
98
|
+
}
|
|
99
|
+
const scrollToTop = () => {
|
|
100
|
+
virtualScrollRef.value?.scrollToTop()
|
|
101
|
+
}
|
|
102
|
+
const scrollToBottom = () => {
|
|
103
|
+
virtualScrollRef.value?.scrollToBottom()
|
|
104
|
+
}
|
|
105
|
+
const scrollToPosition = (position: number | string) => {
|
|
106
|
+
virtualScrollRef.value?.scrollToPosition(position)
|
|
107
|
+
}
|
|
108
|
+
const scrollToElement = (item: any) => {
|
|
109
|
+
virtualScrollRef.value?.scrollToElement(item)
|
|
110
|
+
}
|
|
111
|
+
const scrollToElementById = (id: string | number) => {
|
|
112
|
+
virtualScrollRef.value?.scrollToElementById(id)
|
|
113
|
+
}
|
|
114
|
+
defineExpose<ListExpose>({
|
|
115
|
+
scrollToTop,
|
|
116
|
+
scrollToBottom,
|
|
117
|
+
scrollToPosition,
|
|
118
|
+
scrollToElement,
|
|
119
|
+
scrollToElementById
|
|
120
|
+
})
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<style lang="scss" scoped>
|
|
124
|
+
@import './index.scss';
|
|
125
|
+
</style>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ComponentPublicInstance, ExtractPropTypes, PropType } from 'vue'
|
|
2
|
+
import { makeBooleanProp } 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
|
+
triggered: makeBooleanProp(false),
|
|
30
|
+
scrollConfig: {
|
|
31
|
+
type: Object as PropType<ScrollConfig>,
|
|
32
|
+
default: () => ({})
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export type ScrollConfig = {
|
|
36
|
+
refresherEnabled: boolean
|
|
37
|
+
refresherThreshold: number
|
|
38
|
+
refresherBackground: string
|
|
39
|
+
animation: boolean
|
|
40
|
+
[key: string]: any
|
|
41
|
+
}
|
|
42
|
+
export type ListExpose = {
|
|
43
|
+
scrollToTop: () => void
|
|
44
|
+
scrollToBottom: () => void
|
|
45
|
+
scrollToPosition: (position: number | string) => void
|
|
46
|
+
scrollToElement: (item: any) => void
|
|
47
|
+
scrollToElementById: (id: string | number) => void
|
|
48
|
+
}
|
|
49
|
+
export type ListProps = ExtractPropTypes<typeof listProps>
|
|
50
|
+
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
|
|