xydata-tools 1.1.21 → 1.1.23
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.
|
@@ -6,19 +6,36 @@
|
|
|
6
6
|
<view v-if="actualButtonPosition === 'vertical'" :class="['vertical-layout']">
|
|
7
7
|
<!-- 图片区域 -->
|
|
8
8
|
<view v-if="enableImage && showFileList" class="media-section">
|
|
9
|
-
<shmily-drag-media
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
<shmily-drag-media
|
|
10
|
+
v-if="imageFileList.length > 0"
|
|
11
|
+
v-model="imageUrls"
|
|
12
|
+
:number="imageLimit"
|
|
13
|
+
:cols="cols"
|
|
14
|
+
:imageWidth="itemWidth"
|
|
15
|
+
:padding="10"
|
|
16
|
+
:borderRadius="itemBorderRadius"
|
|
17
|
+
:draggable="draggable"
|
|
18
|
+
moveType="image"
|
|
19
|
+
@input="handleImageDragChange"
|
|
20
|
+
>
|
|
12
21
|
</shmily-drag-media>
|
|
13
22
|
</view>
|
|
14
23
|
|
|
15
24
|
<!-- 视频区域 -->
|
|
16
|
-
<view v-if="enableVideo && showFileList" class="media-section"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
:
|
|
21
|
-
|
|
25
|
+
<view v-if="enableVideo && showFileList" class="media-section" :style="{ marginTop: enableImage && videoFileList.length > 0 ? '20rpx' : '0' }">
|
|
26
|
+
<shmily-drag-media
|
|
27
|
+
v-if="videoFileList.length > 0"
|
|
28
|
+
v-model="videoUrls"
|
|
29
|
+
:number="videoLimit"
|
|
30
|
+
:cols="cols"
|
|
31
|
+
:imageWidth="itemWidth"
|
|
32
|
+
:padding="10"
|
|
33
|
+
:borderRadius="itemBorderRadius"
|
|
34
|
+
:draggable="draggable"
|
|
35
|
+
moveType="video"
|
|
36
|
+
@input="handleVideoDragChange"
|
|
37
|
+
@videoClick="handleVideoItemClick"
|
|
38
|
+
>
|
|
22
39
|
</shmily-drag-media>
|
|
23
40
|
</view>
|
|
24
41
|
|
|
@@ -26,10 +43,13 @@
|
|
|
26
43
|
<view class="upload-buttons-row">
|
|
27
44
|
<!-- 图片上传按钮 -->
|
|
28
45
|
<view v-if="enableImage" class="upload-button-wrapper">
|
|
29
|
-
<slot name="image-upload-button" :select="selectAndUploadImage" :limit="imageLimit"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
46
|
+
<slot name="image-upload-button" :select="selectAndUploadImage" :limit="imageLimit" :count="imageFileList.length">
|
|
47
|
+
<view
|
|
48
|
+
class="upload-btn-vertical"
|
|
49
|
+
:style="{ borderRadius: itemBorderRadius + 'rpx' }"
|
|
50
|
+
v-if="imageFileList.length < imageLimit"
|
|
51
|
+
@click="selectAndUploadImage"
|
|
52
|
+
>
|
|
33
53
|
<image :src="imageUploadIcon" class="upload-icon"></image>
|
|
34
54
|
<view class="upload-text">{{ imageButtonTitle }}</view>
|
|
35
55
|
</view>
|
|
@@ -38,10 +58,13 @@
|
|
|
38
58
|
|
|
39
59
|
<!-- 视频上传按钮 -->
|
|
40
60
|
<view v-if="enableVideo" class="upload-button-wrapper">
|
|
41
|
-
<slot name="video-upload-button" :select="selectAndUploadVideo" :limit="videoLimit"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
61
|
+
<slot name="video-upload-button" :select="selectAndUploadVideo" :limit="videoLimit" :count="videoFileList.length">
|
|
62
|
+
<view
|
|
63
|
+
class="upload-btn-vertical"
|
|
64
|
+
:style="{ borderRadius: itemBorderRadius + 'rpx' }"
|
|
65
|
+
v-if="videoFileList.length < videoLimit"
|
|
66
|
+
@click="selectAndUploadVideo"
|
|
67
|
+
>
|
|
45
68
|
<image :src="videoUploadIcon" class="upload-icon"></image>
|
|
46
69
|
<view class="upload-text">{{ videoButtonTitle }}</view>
|
|
47
70
|
</view>
|
|
@@ -53,36 +76,51 @@
|
|
|
53
76
|
<!-- 水平布局:上传按钮跟随媒体(仅单一媒体类型时使用) -->
|
|
54
77
|
<view v-else class="horizontal-layout">
|
|
55
78
|
<!-- 图片水平布局 -->
|
|
56
|
-
<shmily-drag-media
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
79
|
+
<shmily-drag-media
|
|
80
|
+
v-if="enableImage && !enableVideo && showFileList"
|
|
81
|
+
v-model="imageUrls"
|
|
82
|
+
:number="imageLimit"
|
|
83
|
+
:cols="cols"
|
|
84
|
+
:imageWidth="itemWidth"
|
|
85
|
+
:padding="10"
|
|
86
|
+
:borderRadius="itemBorderRadius"
|
|
87
|
+
:align="align"
|
|
88
|
+
:draggable="draggable"
|
|
89
|
+
:enableAddButton="true"
|
|
90
|
+
moveType="image"
|
|
91
|
+
@input="handleImageDragChange"
|
|
92
|
+
>
|
|
93
|
+
<template #add-button>
|
|
94
|
+
<slot name="image-upload-button" :select="selectAndUploadImage" :limit="imageLimit" :count="imageFileList.length">
|
|
63
95
|
<view class="drag-upload-btn" @click="selectAndUploadImage">
|
|
64
|
-
<image :src="imageUploadIcon" class="upload-icon"
|
|
65
|
-
|
|
66
|
-
<view class="upload-text" :style="getUploadTextStyle(childWidth)">{{ imageButtonTitle }}
|
|
67
|
-
</view>
|
|
96
|
+
<image :src="imageUploadIcon" class="upload-icon" :style="dragIconStyle"></image>
|
|
97
|
+
<view class="upload-text" :style="dragTextStyle">{{ imageButtonTitle }} </view>
|
|
68
98
|
</view>
|
|
69
99
|
</slot>
|
|
70
100
|
</template>
|
|
71
101
|
</shmily-drag-media>
|
|
72
102
|
|
|
73
103
|
<!-- 视频水平布局 -->
|
|
74
|
-
<shmily-drag-media
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
104
|
+
<shmily-drag-media
|
|
105
|
+
v-else-if="enableVideo && !enableImage && showFileList"
|
|
106
|
+
v-model="videoUrls"
|
|
107
|
+
:number="videoLimit"
|
|
108
|
+
:cols="cols"
|
|
109
|
+
:imageWidth="itemWidth"
|
|
110
|
+
:padding="10"
|
|
111
|
+
:borderRadius="itemBorderRadius"
|
|
112
|
+
:align="align"
|
|
113
|
+
:draggable="draggable"
|
|
114
|
+
:enableAddButton="true"
|
|
115
|
+
moveType="video"
|
|
116
|
+
@input="handleVideoDragChange"
|
|
117
|
+
@videoClick="handleVideoItemClick"
|
|
118
|
+
>
|
|
119
|
+
<template #add-button>
|
|
120
|
+
<slot name="video-upload-button" :select="selectAndUploadVideo" :limit="videoLimit" :count="videoFileList.length">
|
|
81
121
|
<view class="drag-upload-btn" @click="selectAndUploadVideo">
|
|
82
|
-
<image :src="videoUploadIcon" class="upload-icon"
|
|
83
|
-
|
|
84
|
-
<view class="upload-text" :style="getUploadTextStyle(childWidth)">{{ videoButtonTitle }}
|
|
85
|
-
</view>
|
|
122
|
+
<image :src="videoUploadIcon" class="upload-icon" :style="dragIconStyle"></image>
|
|
123
|
+
<view class="upload-text" :style="dragTextStyle">{{ videoButtonTitle }} </view>
|
|
86
124
|
</view>
|
|
87
125
|
</slot>
|
|
88
126
|
</template>
|
|
@@ -92,32 +130,43 @@
|
|
|
92
130
|
|
|
93
131
|
<!-- ==================== 只读模式(混合展示图片和视频) ==================== -->
|
|
94
132
|
<block v-else-if="allFileList.length && showFileList">
|
|
95
|
-
<view class="readonly-grid" :class="{ 'align-right': align === 'right' && itemWidth > 0 }"
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
133
|
+
<view class="readonly-grid" :class="{ 'align-right': align === 'right' && itemWidth > 0 }" :style="readonlyGridStyle">
|
|
134
|
+
<view
|
|
135
|
+
v-for="(file, i) in readonlyDisplayList"
|
|
136
|
+
:key="file.id || file.url || i"
|
|
137
|
+
:style="{ borderRadius: itemBorderRadius + 'rpx' }"
|
|
138
|
+
:class="['readonly-grid-item', { 'placeholder-item': file.type === 'placeholder' }, { 'filter-avatar': file.type !== 'placeholder' && isLastRealItem(i) }]"
|
|
139
|
+
:data-count="file.type !== 'placeholder' && isLastRealItem(i) ? '+' + (allFileList.length - maxShowCount) : ''"
|
|
140
|
+
@click="handleReadonlyItemClick(i, file)"
|
|
141
|
+
>
|
|
105
142
|
<!-- 占位元素(不显示任何内容) -->
|
|
106
143
|
<view v-if="file.type === 'placeholder'" class="placeholder-content"></view>
|
|
107
144
|
|
|
108
145
|
<!-- 图片 -->
|
|
109
146
|
<template v-else>
|
|
110
|
-
<image
|
|
111
|
-
|
|
112
|
-
|
|
147
|
+
<image
|
|
148
|
+
v-if="file.type === 'image'"
|
|
149
|
+
:src="lazyLoadImageSrcs[i]"
|
|
150
|
+
mode="aspectFill"
|
|
151
|
+
:style="{ borderRadius: itemBorderRadius + 'rpx' }"
|
|
152
|
+
:id="componentId + '-imgview-' + i"
|
|
153
|
+
@load="onMediaLoad('readonly-' + i)"
|
|
154
|
+
@error="onMediaError('readonly-' + i)"
|
|
155
|
+
>
|
|
113
156
|
</image>
|
|
114
157
|
|
|
115
158
|
<!-- 视频 -->
|
|
116
|
-
<video
|
|
117
|
-
|
|
159
|
+
<video
|
|
160
|
+
v-else-if="file.type === 'video'"
|
|
161
|
+
:id="'readonlyVideo' + i"
|
|
162
|
+
class="readonly-video"
|
|
163
|
+
:src="file.url"
|
|
164
|
+
:controls="false"
|
|
165
|
+
:show-center-play-btn="false"
|
|
118
166
|
:style="{ borderRadius: itemBorderRadius + 'rpx' }"
|
|
119
|
-
@loadedmetadata="onMediaLoad('readonly-' + i)"
|
|
120
|
-
|
|
167
|
+
@loadedmetadata="onMediaLoad('readonly-' + i)"
|
|
168
|
+
@error="onMediaError('readonly-' + i)"
|
|
169
|
+
></video>
|
|
121
170
|
|
|
122
171
|
<!-- 视频播放图标覆盖层 -->
|
|
123
172
|
<view v-if="file.type === 'video'" class="video-play-overlay">
|
|
@@ -134,8 +183,7 @@
|
|
|
134
183
|
</block>
|
|
135
184
|
|
|
136
185
|
<!-- ==================== 自定义弹窗展示所有媒体(从底部滑出) ==================== -->
|
|
137
|
-
<view v-if="popupVisible" class="popup-overlay" @click="closePopup" @touchmove.stop.prevent
|
|
138
|
-
:class="{ 'popup-overlay-show': popupAnimated }">
|
|
186
|
+
<view v-if="popupVisible" class="popup-overlay" @click="closePopup" @touchmove.stop.prevent :class="{ 'popup-overlay-show': popupAnimated }">
|
|
139
187
|
<view class="popup-content" @click.stop @touchmove.stop :class="{ 'popup-content-show': popupAnimated }">
|
|
140
188
|
<view class="popup-header">
|
|
141
189
|
<view class="popup-title">{{ popupTitle }}</view>
|
|
@@ -143,21 +191,30 @@
|
|
|
143
191
|
</view>
|
|
144
192
|
<scroll-view class="popup-scroll" scroll-y :show-scrollbar="false">
|
|
145
193
|
<view class="popup-grid" :style="popupGridStyle">
|
|
146
|
-
<view v-for="(file, i) in allFileList" :key="i" class="popup-grid-item"
|
|
147
|
-
@click="handlePopupItemClick(file, i)">
|
|
148
|
-
|
|
194
|
+
<view v-for="(file, i) in allFileList" :key="i" class="popup-grid-item" @click="handlePopupItemClick(file, i)">
|
|
149
195
|
<!-- 图片 -->
|
|
150
|
-
<image
|
|
151
|
-
|
|
152
|
-
|
|
196
|
+
<image
|
|
197
|
+
v-if="file.type === 'image'"
|
|
198
|
+
:src="popupLazyLoadImageSrcs[i]"
|
|
199
|
+
mode="aspectFill"
|
|
200
|
+
:style="{ borderRadius: itemBorderRadius + 'rpx' }"
|
|
201
|
+
:id="componentId + '-popup-imgview-' + i"
|
|
202
|
+
@load="onMediaLoad('popup-' + i)"
|
|
203
|
+
@error="onMediaError('popup-' + i)"
|
|
204
|
+
>
|
|
153
205
|
</image>
|
|
154
206
|
|
|
155
207
|
<!-- 视频 -->
|
|
156
|
-
<video
|
|
157
|
-
|
|
208
|
+
<video
|
|
209
|
+
v-else-if="file.type === 'video'"
|
|
210
|
+
class="popup-video"
|
|
211
|
+
:src="file.url"
|
|
212
|
+
:controls="false"
|
|
213
|
+
:show-center-play-btn="false"
|
|
158
214
|
:style="{ borderRadius: itemBorderRadius + 'rpx' }"
|
|
159
|
-
@loadedmetadata="onMediaLoad('popup-' + i)"
|
|
160
|
-
|
|
215
|
+
@loadedmetadata="onMediaLoad('popup-' + i)"
|
|
216
|
+
@error="onMediaError('popup-' + i)"
|
|
217
|
+
></video>
|
|
161
218
|
|
|
162
219
|
<!-- 视频播放图标 -->
|
|
163
220
|
<view v-if="file.type === 'video'" class="video-play-overlay">
|
|
@@ -175,10 +232,16 @@
|
|
|
175
232
|
</view>
|
|
176
233
|
|
|
177
234
|
<!-- 专门用于全屏预览的 video,隐藏在视图外 -->
|
|
178
|
-
<video
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
235
|
+
<video
|
|
236
|
+
v-if="enableVideo"
|
|
237
|
+
id="fullscreenPreviewVideo"
|
|
238
|
+
class="fullscreen-preview-video"
|
|
239
|
+
:src="fullscreenVideoSrc"
|
|
240
|
+
:controls="true"
|
|
241
|
+
:show-center-play-btn="true"
|
|
242
|
+
object-fit="contain"
|
|
243
|
+
style="position: fixed; left: -9999px; top: -9999px; width: 1px; height: 1px"
|
|
244
|
+
></video>
|
|
182
245
|
</view>
|
|
183
246
|
</template>
|
|
184
247
|
|
|
@@ -189,7 +252,7 @@ import shmilyDragMedia from './shmily-drag-media.vue';
|
|
|
189
252
|
export default {
|
|
190
253
|
name: 'MediaPicker',
|
|
191
254
|
components: {
|
|
192
|
-
shmilyDragMedia
|
|
255
|
+
shmilyDragMedia
|
|
193
256
|
},
|
|
194
257
|
props: {
|
|
195
258
|
// ==================== 通用配置 ====================
|
|
@@ -338,8 +401,8 @@ export default {
|
|
|
338
401
|
observer: null,
|
|
339
402
|
componentId: null, // 组件实例的唯一ID
|
|
340
403
|
popupLazyLoadImageSrcs: [], // 用于弹窗中图片懒加载,存储进入视图的图片src
|
|
341
|
-
popupObserver: null
|
|
342
|
-
}
|
|
404
|
+
popupObserver: null // 弹窗图片懒加载观察器
|
|
405
|
+
};
|
|
343
406
|
},
|
|
344
407
|
created() {
|
|
345
408
|
this.componentId = 'mp-' + Date.now() + Math.random().toString(36).substr(2, 9);
|
|
@@ -348,65 +411,67 @@ export default {
|
|
|
348
411
|
// ==================== 媒体类型判断 ====================
|
|
349
412
|
/** 是否启用图片上传 */
|
|
350
413
|
enableImage() {
|
|
351
|
-
return ['image', 'both'].includes(this.uploadType)
|
|
414
|
+
return ['image', 'both'].includes(this.uploadType);
|
|
352
415
|
},
|
|
353
416
|
/** 是否启用视频上传 */
|
|
354
417
|
enableVideo() {
|
|
355
|
-
return ['video', 'both'].includes(this.uploadType)
|
|
418
|
+
return ['video', 'both'].includes(this.uploadType);
|
|
356
419
|
},
|
|
357
420
|
/** 实际的布局方式(两种类型时强制为 vertical) */
|
|
358
421
|
actualButtonPosition() {
|
|
359
422
|
if (this.enableImage && this.enableVideo) {
|
|
360
|
-
return 'vertical'
|
|
423
|
+
return 'vertical';
|
|
361
424
|
}
|
|
362
|
-
return this.buttonPosition
|
|
425
|
+
return this.buttonPosition;
|
|
363
426
|
},
|
|
364
427
|
|
|
365
428
|
/** 只读模式:需要显示的文件列表(右对齐时包含占位元素) */
|
|
366
429
|
readonlyDisplayList() {
|
|
367
|
-
const displayList = this.allFileList.slice(0, this.maxShowCount)
|
|
430
|
+
const displayList = this.allFileList.slice(0, this.maxShowCount);
|
|
368
431
|
|
|
369
432
|
// 如果是右对齐,需要在最后一行前面添加占位元素
|
|
370
433
|
if (this.readonly && this.align === 'right' && displayList.length > 0) {
|
|
371
|
-
const totalCount = displayList.length
|
|
372
|
-
const lastRowCount = totalCount % this.cols
|
|
434
|
+
const totalCount = displayList.length;
|
|
435
|
+
const lastRowCount = totalCount % this.cols;
|
|
373
436
|
|
|
374
437
|
// 如果最后一行不满,需要在最后一行开始位置插入占位元素
|
|
375
438
|
if (lastRowCount !== 0) {
|
|
376
|
-
const placeholderCount = this.cols - lastRowCount
|
|
377
|
-
const rows = Math.ceil(totalCount / this.cols)
|
|
378
|
-
const lastRowStartIndex = (rows - 1) * this.cols
|
|
439
|
+
const placeholderCount = this.cols - lastRowCount;
|
|
440
|
+
const rows = Math.ceil(totalCount / this.cols);
|
|
441
|
+
const lastRowStartIndex = (rows - 1) * this.cols;
|
|
379
442
|
|
|
380
443
|
// 创建占位元素
|
|
381
|
-
const placeholders = Array(placeholderCount)
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
444
|
+
const placeholders = Array(placeholderCount)
|
|
445
|
+
.fill(null)
|
|
446
|
+
.map((_, i) => ({
|
|
447
|
+
type: 'placeholder',
|
|
448
|
+
id: `placeholder-${i}`
|
|
449
|
+
}));
|
|
385
450
|
|
|
386
451
|
// 将列表分为:前面几行(满的) + 最后一行
|
|
387
|
-
const beforeLastRow = displayList.slice(0, lastRowStartIndex)
|
|
388
|
-
const lastRow = displayList.slice(lastRowStartIndex)
|
|
452
|
+
const beforeLastRow = displayList.slice(0, lastRowStartIndex);
|
|
453
|
+
const lastRow = displayList.slice(lastRowStartIndex);
|
|
389
454
|
|
|
390
455
|
// 在最后一行前面插入占位元素
|
|
391
|
-
return [...beforeLastRow, ...placeholders, ...lastRow]
|
|
456
|
+
return [...beforeLastRow, ...placeholders, ...lastRow];
|
|
392
457
|
}
|
|
393
458
|
}
|
|
394
459
|
|
|
395
|
-
return displayList
|
|
460
|
+
return displayList;
|
|
396
461
|
},
|
|
397
462
|
|
|
398
463
|
// ==================== 图标配置 ====================
|
|
399
464
|
imageUploadIcon() {
|
|
400
|
-
return this.imageButtonIcon || this.defaultImageIcon
|
|
465
|
+
return this.imageButtonIcon || this.defaultImageIcon;
|
|
401
466
|
},
|
|
402
467
|
videoUploadIcon() {
|
|
403
|
-
return this.videoButtonIcon || this.defaultVideoIcon
|
|
468
|
+
return this.videoButtonIcon || this.defaultVideoIcon;
|
|
404
469
|
},
|
|
405
470
|
|
|
406
471
|
// ==================== URL 列表 ====================
|
|
407
472
|
imageUrls: {
|
|
408
473
|
get() {
|
|
409
|
-
return this.imageFileList.map(file => file.url)
|
|
474
|
+
return this.imageFileList.map((file) => file.url);
|
|
410
475
|
},
|
|
411
476
|
set(newUrls) {
|
|
412
477
|
// setter 用于拖拽组件更新时,由 handleImageDragChange 处理
|
|
@@ -414,7 +479,7 @@ export default {
|
|
|
414
479
|
},
|
|
415
480
|
videoUrls: {
|
|
416
481
|
get() {
|
|
417
|
-
return this.videoFileList.map(file => file.url)
|
|
482
|
+
return this.videoFileList.map((file) => file.url);
|
|
418
483
|
},
|
|
419
484
|
set(newUrls) {
|
|
420
485
|
// setter 用于拖拽组件更新时,由 handleVideoDragChange 处理
|
|
@@ -422,45 +487,57 @@ export default {
|
|
|
422
487
|
},
|
|
423
488
|
/** 合并的所有媒体列表(用于只读模式和弹窗) */
|
|
424
489
|
allFileList() {
|
|
425
|
-
const combined = []
|
|
490
|
+
const combined = [];
|
|
426
491
|
// 标记每个文件的类型
|
|
427
|
-
this.imageFileList.forEach(file => {
|
|
428
|
-
combined.push({ ...file, type: 'image' })
|
|
429
|
-
})
|
|
430
|
-
this.videoFileList.forEach(file => {
|
|
431
|
-
combined.push({ ...file, type: 'video' })
|
|
432
|
-
})
|
|
433
|
-
return combined
|
|
492
|
+
this.imageFileList.forEach((file) => {
|
|
493
|
+
combined.push({ ...file, type: 'image' });
|
|
494
|
+
});
|
|
495
|
+
this.videoFileList.forEach((file) => {
|
|
496
|
+
combined.push({ ...file, type: 'video' });
|
|
497
|
+
});
|
|
498
|
+
return combined;
|
|
434
499
|
},
|
|
435
500
|
|
|
436
501
|
// ==================== 上传地址 ====================
|
|
437
502
|
finalUploadUrl() {
|
|
438
|
-
return this.actualUploadUrl || this.uploadUrl || (getApp().globalData && getApp().globalData.uploadUrl) || ''
|
|
503
|
+
return this.actualUploadUrl || this.uploadUrl || (getApp().globalData && getApp().globalData.uploadUrl) || '';
|
|
439
504
|
},
|
|
440
505
|
|
|
441
506
|
// ==================== 动态尺寸计算 ====================
|
|
442
507
|
uploadIconSize() {
|
|
443
508
|
if (this.itemWidth > 0) {
|
|
444
|
-
return Math.floor((this.itemWidth - 20) * 0.3)
|
|
509
|
+
return Math.floor((this.itemWidth - 20) * 0.3);
|
|
445
510
|
}
|
|
446
|
-
const screenWidth = 750
|
|
447
|
-
const gap = 20
|
|
448
|
-
const containerWidth = (screenWidth - gap * (this.cols + 1)) / this.cols
|
|
449
|
-
return Math.floor(containerWidth * 0.3)
|
|
511
|
+
const screenWidth = 750;
|
|
512
|
+
const gap = 20;
|
|
513
|
+
const containerWidth = (screenWidth - gap * (this.cols + 1)) / this.cols;
|
|
514
|
+
return Math.floor(containerWidth * 0.3);
|
|
450
515
|
},
|
|
451
516
|
uploadTextSize() {
|
|
452
|
-
return Math.max(20, Math.floor(this.uploadIconSize * 0.38))
|
|
517
|
+
return Math.max(20, Math.floor(this.uploadIconSize * 0.38));
|
|
518
|
+
},
|
|
519
|
+
// 拖拽模式下的图标和文字样式(预计算好,避免模板中使用方法调用报错)
|
|
520
|
+
dragIconStyle() {
|
|
521
|
+
return {
|
|
522
|
+
width: this.uploadIconSize + 'rpx',
|
|
523
|
+
height: this.uploadIconSize + 'rpx'
|
|
524
|
+
};
|
|
525
|
+
},
|
|
526
|
+
dragTextStyle() {
|
|
527
|
+
return {
|
|
528
|
+
fontSize: this.uploadTextSize + 'rpx'
|
|
529
|
+
};
|
|
453
530
|
},
|
|
454
531
|
|
|
455
532
|
// ==================== 只读模式网格样式 ====================
|
|
456
533
|
readonlyGridStyle() {
|
|
457
534
|
// 如果指定了 itemWidth,使用固定宽度;否则使用 1fr 自动分配
|
|
458
|
-
const columnWidth = this.itemWidth ? `${this.itemWidth}rpx` : '1fr'
|
|
535
|
+
const columnWidth = this.itemWidth ? `${this.itemWidth}rpx` : '1fr';
|
|
459
536
|
|
|
460
537
|
return {
|
|
461
538
|
gridTemplateColumns: `repeat(${this.cols}, ${columnWidth})`,
|
|
462
539
|
gap: '10rpx'
|
|
463
|
-
}
|
|
540
|
+
};
|
|
464
541
|
},
|
|
465
542
|
|
|
466
543
|
// ==================== 弹窗模式网格样式 ====================
|
|
@@ -468,19 +545,19 @@ export default {
|
|
|
468
545
|
return {
|
|
469
546
|
gridTemplateColumns: `repeat(${this.cols}, 1fr)`,
|
|
470
547
|
gap: '10rpx'
|
|
471
|
-
}
|
|
548
|
+
};
|
|
472
549
|
}
|
|
473
550
|
},
|
|
474
551
|
watch: {
|
|
475
552
|
imageFiles: {
|
|
476
553
|
handler(val) {
|
|
477
|
-
this.imageFileList = val || []
|
|
554
|
+
this.imageFileList = val || [];
|
|
478
555
|
},
|
|
479
556
|
immediate: true
|
|
480
557
|
},
|
|
481
558
|
videoFiles: {
|
|
482
559
|
handler(val) {
|
|
483
|
-
this.videoFileList = val || []
|
|
560
|
+
this.videoFileList = val || [];
|
|
484
561
|
},
|
|
485
562
|
immediate: true
|
|
486
563
|
},
|
|
@@ -492,7 +569,7 @@ export default {
|
|
|
492
569
|
},
|
|
493
570
|
mounted() {
|
|
494
571
|
if (!this.uploadUrl) {
|
|
495
|
-
this.actualUploadUrl = (getApp().globalData && getApp().globalData.uploadUrl) || ''
|
|
572
|
+
this.actualUploadUrl = (getApp().globalData && getApp().globalData.uploadUrl) || '';
|
|
496
573
|
}
|
|
497
574
|
this.initLazyObserver(); // 在mounted时初始化观察器
|
|
498
575
|
},
|
|
@@ -513,49 +590,7 @@ export default {
|
|
|
513
590
|
* @returns {Boolean} - 是否被监听
|
|
514
591
|
*/
|
|
515
592
|
hasEventListener(eventName) {
|
|
516
|
-
return !!(this.$listeners && this.$listeners[eventName])
|
|
517
|
-
},
|
|
518
|
-
parseChildWidth(childWidth) {
|
|
519
|
-
if (typeof childWidth === 'number') {
|
|
520
|
-
return childWidth
|
|
521
|
-
}
|
|
522
|
-
if (typeof childWidth === 'string') {
|
|
523
|
-
const parsed = parseFloat(childWidth)
|
|
524
|
-
return Number.isFinite(parsed) ? parsed : null
|
|
525
|
-
}
|
|
526
|
-
return null
|
|
527
|
-
},
|
|
528
|
-
pxToRpx(px) {
|
|
529
|
-
if (!px || !this.windowWidth) {
|
|
530
|
-
return 0
|
|
531
|
-
}
|
|
532
|
-
return px / this.windowWidth * 750
|
|
533
|
-
},
|
|
534
|
-
computeUploadIconSize(childWidth) {
|
|
535
|
-
const parsedPx = this.parseChildWidth(childWidth)
|
|
536
|
-
if (!parsedPx) {
|
|
537
|
-
return this.uploadIconSize
|
|
538
|
-
}
|
|
539
|
-
const widthRpx = this.pxToRpx(parsedPx)
|
|
540
|
-
const size = Math.floor(widthRpx * 0.3)
|
|
541
|
-
return Math.max(20, size)
|
|
542
|
-
},
|
|
543
|
-
computeUploadTextSize(childWidth) {
|
|
544
|
-
const iconSize = this.computeUploadIconSize(childWidth)
|
|
545
|
-
const size = Math.floor(iconSize * 0.4)
|
|
546
|
-
return Math.max(20, size)
|
|
547
|
-
},
|
|
548
|
-
getUploadIconStyle(childWidth) {
|
|
549
|
-
const size = this.computeUploadIconSize(childWidth)
|
|
550
|
-
return {
|
|
551
|
-
width: size + 'rpx',
|
|
552
|
-
height: size + 'rpx'
|
|
553
|
-
}
|
|
554
|
-
},
|
|
555
|
-
getUploadTextStyle(childWidth) {
|
|
556
|
-
return {
|
|
557
|
-
fontSize: this.computeUploadTextSize(childWidth) + 'rpx'
|
|
558
|
-
}
|
|
593
|
+
return !!(this.$listeners && this.$listeners[eventName]);
|
|
559
594
|
},
|
|
560
595
|
|
|
561
596
|
// ==================== 图片相关方法 ====================
|
|
@@ -566,10 +601,10 @@ export default {
|
|
|
566
601
|
*/
|
|
567
602
|
checkImageSize(size) {
|
|
568
603
|
if (this.imageMaxSize <= 0) {
|
|
569
|
-
return true
|
|
604
|
+
return true;
|
|
570
605
|
}
|
|
571
|
-
const maxSizeInBytes = this.imageMaxSize * 1024 * 1024
|
|
572
|
-
return size <= maxSizeInBytes
|
|
606
|
+
const maxSizeInBytes = this.imageMaxSize * 1024 * 1024;
|
|
607
|
+
return size <= maxSizeInBytes;
|
|
573
608
|
},
|
|
574
609
|
|
|
575
610
|
/**
|
|
@@ -579,11 +614,11 @@ export default {
|
|
|
579
614
|
*/
|
|
580
615
|
formatFileSize(size) {
|
|
581
616
|
if (size < 1024) {
|
|
582
|
-
return size + 'B'
|
|
617
|
+
return size + 'B';
|
|
583
618
|
} else if (size < 1024 * 1024) {
|
|
584
|
-
return (size / 1024).toFixed(2) + 'KB'
|
|
619
|
+
return (size / 1024).toFixed(2) + 'KB';
|
|
585
620
|
} else {
|
|
586
|
-
return (size / (1024 * 1024)).toFixed(2) + 'MB'
|
|
621
|
+
return (size / (1024 * 1024)).toFixed(2) + 'MB';
|
|
587
622
|
}
|
|
588
623
|
},
|
|
589
624
|
|
|
@@ -596,31 +631,31 @@ export default {
|
|
|
596
631
|
this.$emit('imageLimitReached', {
|
|
597
632
|
current: this.imageFileList.length,
|
|
598
633
|
limit: this.imageLimit
|
|
599
|
-
})
|
|
634
|
+
});
|
|
600
635
|
|
|
601
636
|
// 如果用户未监听事件,显示默认提示
|
|
602
637
|
if (!this.hasEventListener('imageLimitReached')) {
|
|
603
638
|
uni.showToast({
|
|
604
639
|
icon: 'none',
|
|
605
640
|
title: `上传最大限制${this.imageLimit}张`
|
|
606
|
-
})
|
|
641
|
+
});
|
|
607
642
|
}
|
|
608
|
-
return
|
|
643
|
+
return;
|
|
609
644
|
}
|
|
610
645
|
|
|
611
646
|
if (!this.finalUploadUrl) {
|
|
612
647
|
uni.showToast({
|
|
613
648
|
title: '请先配置上传地址',
|
|
614
649
|
icon: 'none'
|
|
615
|
-
})
|
|
616
|
-
return
|
|
650
|
+
});
|
|
651
|
+
return;
|
|
617
652
|
}
|
|
618
653
|
|
|
619
|
-
const remainCount = this.imageLimit - this.imageFileList.length
|
|
654
|
+
const remainCount = this.imageLimit - this.imageFileList.length;
|
|
620
655
|
|
|
621
656
|
uni.chooseImage({
|
|
622
657
|
count: remainCount,
|
|
623
|
-
sizeType: [
|
|
658
|
+
sizeType: ['original', 'compressed'],
|
|
624
659
|
sourceType: this.imageSourceType,
|
|
625
660
|
// #ifndef MP-WEIXIN
|
|
626
661
|
success: (chooseImageRes) => {
|
|
@@ -630,23 +665,23 @@ export default {
|
|
|
630
665
|
current: this.imageFileList.length,
|
|
631
666
|
limit: this.imageLimit,
|
|
632
667
|
attempted: chooseImageRes.tempFiles.length
|
|
633
|
-
})
|
|
668
|
+
});
|
|
634
669
|
|
|
635
670
|
// 如果用户未监听事件,显示默认提示
|
|
636
671
|
if (!this.hasEventListener('imageLimitReached')) {
|
|
637
672
|
uni.showToast({
|
|
638
673
|
icon: 'none',
|
|
639
674
|
title: `上传最大限制${this.imageLimit}张`
|
|
640
|
-
})
|
|
675
|
+
});
|
|
641
676
|
}
|
|
642
|
-
return
|
|
677
|
+
return;
|
|
643
678
|
}
|
|
644
679
|
|
|
645
680
|
// 检查图片大小
|
|
646
681
|
if (this.imageMaxSize > 0) {
|
|
647
|
-
const oversizedFiles = chooseImageRes.tempFiles.filter(file => !this.checkImageSize(file.size))
|
|
682
|
+
const oversizedFiles = chooseImageRes.tempFiles.filter((file) => !this.checkImageSize(file.size));
|
|
648
683
|
if (oversizedFiles.length > 0) {
|
|
649
|
-
const firstOversized = oversizedFiles[0]
|
|
684
|
+
const firstOversized = oversizedFiles[0];
|
|
650
685
|
|
|
651
686
|
// 触发图片大小超限事件
|
|
652
687
|
this.$emit('imageSizeExceed', {
|
|
@@ -654,7 +689,7 @@ export default {
|
|
|
654
689
|
maxSize: this.imageMaxSize * 1024 * 1024,
|
|
655
690
|
file: firstOversized,
|
|
656
691
|
message: `图片大小不能超过${this.imageMaxSize}MB(当前:${this.formatFileSize(firstOversized.size)})`
|
|
657
|
-
})
|
|
692
|
+
});
|
|
658
693
|
|
|
659
694
|
// 如果用户未监听事件,显示默认提示
|
|
660
695
|
if (!this.hasEventListener('imageSizeExceed')) {
|
|
@@ -662,34 +697,20 @@ export default {
|
|
|
662
697
|
icon: 'none',
|
|
663
698
|
title: `图片大小不能超过${this.imageMaxSize}MB(当前:${this.formatFileSize(firstOversized.size)})`,
|
|
664
699
|
duration: 3000
|
|
665
|
-
})
|
|
700
|
+
});
|
|
666
701
|
}
|
|
667
|
-
return
|
|
702
|
+
return;
|
|
668
703
|
}
|
|
669
704
|
}
|
|
670
705
|
|
|
671
706
|
uni.showLoading({
|
|
672
707
|
title: '上传中',
|
|
673
708
|
mask: true
|
|
674
|
-
})
|
|
709
|
+
});
|
|
675
710
|
|
|
676
|
-
chooseImageRes.
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
new Compressor(tempFile, {
|
|
680
|
-
quality: 0.8,
|
|
681
|
-
success: (compressedFile) => {
|
|
682
|
-
this.uploadImageFile(this.finalUploadUrl, compressedFile)
|
|
683
|
-
},
|
|
684
|
-
error: (err) => {
|
|
685
|
-
uni.hideLoading()
|
|
686
|
-
uni.showToast({
|
|
687
|
-
icon: 'none',
|
|
688
|
-
title: '图片压缩失败'
|
|
689
|
-
})
|
|
690
|
-
},
|
|
691
|
-
})
|
|
692
|
-
})
|
|
711
|
+
chooseImageRes.tempFiles.forEach((fileItem) => {
|
|
712
|
+
this.uploadImageFile(this.finalUploadUrl, fileItem);
|
|
713
|
+
});
|
|
693
714
|
},
|
|
694
715
|
// #endif
|
|
695
716
|
|
|
@@ -701,23 +722,23 @@ export default {
|
|
|
701
722
|
current: this.imageFileList.length,
|
|
702
723
|
limit: this.imageLimit,
|
|
703
724
|
attempted: chooseImageRes.tempFiles.length
|
|
704
|
-
})
|
|
725
|
+
});
|
|
705
726
|
|
|
706
727
|
// 如果用户未监听事件,显示默认提示
|
|
707
728
|
if (!this.hasEventListener('imageLimitReached')) {
|
|
708
729
|
uni.showToast({
|
|
709
730
|
icon: 'none',
|
|
710
731
|
title: `上传最大限制${this.imageLimit}张`
|
|
711
|
-
})
|
|
732
|
+
});
|
|
712
733
|
}
|
|
713
|
-
return
|
|
734
|
+
return;
|
|
714
735
|
}
|
|
715
736
|
|
|
716
737
|
// 检查图片大小
|
|
717
738
|
if (this.imageMaxSize > 0) {
|
|
718
|
-
const oversizedFiles = chooseImageRes.tempFiles.filter(file => !this.checkImageSize(file.size))
|
|
739
|
+
const oversizedFiles = chooseImageRes.tempFiles.filter((file) => !this.checkImageSize(file.size));
|
|
719
740
|
if (oversizedFiles.length > 0) {
|
|
720
|
-
const firstOversized = oversizedFiles[0]
|
|
741
|
+
const firstOversized = oversizedFiles[0];
|
|
721
742
|
|
|
722
743
|
// 触发图片大小超限事件
|
|
723
744
|
this.$emit('imageSizeExceed', {
|
|
@@ -725,7 +746,7 @@ export default {
|
|
|
725
746
|
maxSize: this.imageMaxSize * 1024 * 1024,
|
|
726
747
|
file: firstOversized,
|
|
727
748
|
message: `图片大小不能超过${this.imageMaxSize}MB(当前:${this.formatFileSize(firstOversized.size)})`
|
|
728
|
-
})
|
|
749
|
+
});
|
|
729
750
|
|
|
730
751
|
// 如果用户未监听事件,显示默认提示
|
|
731
752
|
if (!this.hasEventListener('imageSizeExceed')) {
|
|
@@ -733,20 +754,20 @@ export default {
|
|
|
733
754
|
icon: 'none',
|
|
734
755
|
title: `图片大小不能超过${this.imageMaxSize}MB(当前:${this.formatFileSize(firstOversized.size)})`,
|
|
735
756
|
duration: 3000
|
|
736
|
-
})
|
|
757
|
+
});
|
|
737
758
|
}
|
|
738
|
-
return
|
|
759
|
+
return;
|
|
739
760
|
}
|
|
740
761
|
}
|
|
741
762
|
|
|
742
763
|
uni.showLoading({
|
|
743
764
|
title: '上传中',
|
|
744
765
|
mask: true
|
|
745
|
-
})
|
|
766
|
+
});
|
|
746
767
|
|
|
747
768
|
chooseImageRes.tempFilePaths.forEach((tempPath, index) => {
|
|
748
|
-
const tempFile = chooseImageRes.tempFiles[index]
|
|
749
|
-
const lowerPath = tempPath.toLowerCase()
|
|
769
|
+
const tempFile = chooseImageRes.tempFiles[index];
|
|
770
|
+
const lowerPath = tempPath.toLowerCase();
|
|
750
771
|
|
|
751
772
|
// 对 jpg/jpeg 格式的图片进行压缩
|
|
752
773
|
if (lowerPath.indexOf('.jpg') !== -1 || lowerPath.indexOf('.jpeg') !== -1) {
|
|
@@ -754,16 +775,16 @@ export default {
|
|
|
754
775
|
src: tempFile.path,
|
|
755
776
|
quality: 100,
|
|
756
777
|
success: (res) => {
|
|
757
|
-
this.uploadImageFileByPath(this.finalUploadUrl, res.tempFilePath, tempPath)
|
|
778
|
+
this.uploadImageFileByPath(this.finalUploadUrl, res.tempFilePath, tempPath);
|
|
758
779
|
}
|
|
759
|
-
})
|
|
780
|
+
});
|
|
760
781
|
} else {
|
|
761
|
-
this.uploadImageFileByPath(this.finalUploadUrl, tempPath, tempPath)
|
|
782
|
+
this.uploadImageFileByPath(this.finalUploadUrl, tempPath, tempPath);
|
|
762
783
|
}
|
|
763
|
-
})
|
|
764
|
-
}
|
|
784
|
+
});
|
|
785
|
+
}
|
|
765
786
|
// #endif
|
|
766
|
-
})
|
|
787
|
+
});
|
|
767
788
|
},
|
|
768
789
|
|
|
769
790
|
/**
|
|
@@ -773,41 +794,63 @@ export default {
|
|
|
773
794
|
uni.uploadFile({
|
|
774
795
|
url,
|
|
775
796
|
file,
|
|
776
|
-
name:
|
|
797
|
+
name: 'file',
|
|
777
798
|
success: (uploadFileRes) => {
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
url: result.data.pathUrl,
|
|
784
|
-
type: 'image'
|
|
799
|
+
// 1. 先检查 HTTP 状态码
|
|
800
|
+
if (uploadFileRes.statusCode !== 200) {
|
|
801
|
+
console.error('HTTP状态码错误:', uploadFileRes.statusCode);
|
|
802
|
+
uni.showToast({ title: `服务器响应异常(${uploadFileRes.statusCode})`, icon: 'none' });
|
|
803
|
+
return;
|
|
785
804
|
}
|
|
786
|
-
this.imageFileList.push(fileInfo)
|
|
787
805
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
806
|
+
try {
|
|
807
|
+
// 2. 尝试解析 JSON
|
|
808
|
+
const result = JSON.parse(uploadFileRes.data);
|
|
809
|
+
|
|
810
|
+
// 3. 检查后端定义的业务 Code (假设 200 为成功)
|
|
811
|
+
if (result.rspCode === '000000') {
|
|
812
|
+
const fileInfo = {
|
|
813
|
+
fileName: result.data.fileName,
|
|
814
|
+
name: result.data.originFileName,
|
|
815
|
+
url: result.data.pathUrl,
|
|
816
|
+
type: 'image'
|
|
817
|
+
};
|
|
818
|
+
this.imageFileList.push(fileInfo);
|
|
819
|
+
|
|
820
|
+
this.$emit('imageUploadSuccess', {
|
|
821
|
+
uploadFileRes: uploadFileRes,
|
|
822
|
+
file: fileInfo,
|
|
823
|
+
response: result
|
|
824
|
+
});
|
|
825
|
+
} else {
|
|
826
|
+
// 后端业务层报错(如:格式不支持、权限不足)
|
|
827
|
+
uni.showToast({ title: result.rspMsg || '业务处理失败', icon: 'none' });
|
|
828
|
+
this.$emit('imageUploadFail', { error: result, message: result.rspMsg });
|
|
829
|
+
}
|
|
830
|
+
} catch (error) {
|
|
831
|
+
// 解析 JSON 失败,通常是后端挂了返回了非 JSON 格式
|
|
832
|
+
console.error('返回数据解析异常:', uploadFileRes.data);
|
|
833
|
+
uni.showToast({ title: '服务器数据解析失败', icon: 'none' });
|
|
834
|
+
this.$emit('imageUploadFail', { error: error, message: '解析失败' });
|
|
835
|
+
}
|
|
793
836
|
},
|
|
794
837
|
fail: (error) => {
|
|
795
838
|
uni.showToast({
|
|
796
839
|
icon: 'none',
|
|
797
840
|
title: '上传失败'
|
|
798
|
-
})
|
|
841
|
+
});
|
|
799
842
|
|
|
800
843
|
// 触发图片上传失败事件
|
|
801
844
|
this.$emit('imageUploadFail', {
|
|
802
845
|
error: error,
|
|
803
846
|
message: '图片上传失败'
|
|
804
|
-
})
|
|
847
|
+
});
|
|
805
848
|
},
|
|
806
849
|
complete: () => {
|
|
807
|
-
uni.hideLoading()
|
|
808
|
-
this.emitImageChange()
|
|
850
|
+
uni.hideLoading();
|
|
851
|
+
this.emitImageChange();
|
|
809
852
|
}
|
|
810
|
-
})
|
|
853
|
+
});
|
|
811
854
|
},
|
|
812
855
|
|
|
813
856
|
/**
|
|
@@ -817,53 +860,55 @@ export default {
|
|
|
817
860
|
uni.uploadFile({
|
|
818
861
|
url,
|
|
819
862
|
filePath,
|
|
820
|
-
name:
|
|
863
|
+
name: 'file',
|
|
821
864
|
success: (uploadFileRes) => {
|
|
822
|
-
const result = JSON.parse(uploadFileRes.data)
|
|
865
|
+
const result = JSON.parse(uploadFileRes.data);
|
|
823
866
|
|
|
824
867
|
const fileInfo = {
|
|
825
868
|
fileName: result.data.fileName,
|
|
826
869
|
name: result.data.originFileName,
|
|
827
870
|
url: result.data.pathUrl,
|
|
828
871
|
type: 'image'
|
|
829
|
-
}
|
|
830
|
-
this.imageFileList.push(fileInfo)
|
|
872
|
+
};
|
|
873
|
+
this.imageFileList.push(fileInfo);
|
|
831
874
|
|
|
832
875
|
// 触发图片上传成功事件
|
|
833
876
|
this.$emit('imageUploadSuccess', {
|
|
834
877
|
file: fileInfo,
|
|
835
878
|
response: result
|
|
836
|
-
})
|
|
879
|
+
});
|
|
837
880
|
},
|
|
838
881
|
fail: (error) => {
|
|
839
882
|
uni.showToast({
|
|
840
883
|
icon: 'none',
|
|
841
884
|
title: '上传失败'
|
|
842
|
-
})
|
|
885
|
+
});
|
|
843
886
|
|
|
844
887
|
// 触发图片上传失败事件
|
|
845
888
|
this.$emit('imageUploadFail', {
|
|
846
889
|
error: error,
|
|
847
890
|
message: '图片上传失败'
|
|
848
|
-
})
|
|
891
|
+
});
|
|
849
892
|
},
|
|
850
893
|
complete: () => {
|
|
851
|
-
uni.hideLoading()
|
|
852
|
-
this.emitImageChange()
|
|
894
|
+
uni.hideLoading();
|
|
895
|
+
this.emitImageChange();
|
|
853
896
|
}
|
|
854
|
-
})
|
|
897
|
+
});
|
|
855
898
|
},
|
|
856
899
|
|
|
857
900
|
/**
|
|
858
901
|
* 处理图片拖拽排序变化
|
|
859
902
|
*/
|
|
860
|
-
handleImageDragChange(newUrls) {
|
|
861
|
-
const newFileList = newUrls
|
|
862
|
-
|
|
863
|
-
|
|
903
|
+
handleImageDragChange(newUrls) {
|
|
904
|
+
const newFileList = newUrls
|
|
905
|
+
.map((url) => {
|
|
906
|
+
return this.imageFileList.find((file) => file.url === url);
|
|
907
|
+
})
|
|
908
|
+
.filter(Boolean);
|
|
864
909
|
|
|
865
|
-
this.imageFileList = newFileList
|
|
866
|
-
this.emitImageChange()
|
|
910
|
+
this.imageFileList = newFileList;
|
|
911
|
+
this.emitImageChange();
|
|
867
912
|
},
|
|
868
913
|
|
|
869
914
|
/**
|
|
@@ -873,15 +918,15 @@ export default {
|
|
|
873
918
|
uni.previewImage({
|
|
874
919
|
current,
|
|
875
920
|
urls,
|
|
876
|
-
indicator:
|
|
877
|
-
})
|
|
921
|
+
indicator: 'number'
|
|
922
|
+
});
|
|
878
923
|
},
|
|
879
924
|
|
|
880
925
|
/**
|
|
881
926
|
* 触发图片变更事件
|
|
882
927
|
*/
|
|
883
928
|
emitImageChange() {
|
|
884
|
-
this.$emit('imageChange', this.imageFileList)
|
|
929
|
+
this.$emit('imageChange', this.imageFileList);
|
|
885
930
|
},
|
|
886
931
|
|
|
887
932
|
// ==================== 视频相关方法 ====================
|
|
@@ -893,8 +938,8 @@ export default {
|
|
|
893
938
|
uni.showToast({
|
|
894
939
|
title: '请先配置上传地址',
|
|
895
940
|
icon: 'none'
|
|
896
|
-
})
|
|
897
|
-
return
|
|
941
|
+
});
|
|
942
|
+
return;
|
|
898
943
|
}
|
|
899
944
|
|
|
900
945
|
if (this.videoFileList.length >= this.videoLimit) {
|
|
@@ -902,16 +947,16 @@ export default {
|
|
|
902
947
|
this.$emit('videoLimitReached', {
|
|
903
948
|
current: this.videoFileList.length,
|
|
904
949
|
limit: this.videoLimit
|
|
905
|
-
})
|
|
950
|
+
});
|
|
906
951
|
|
|
907
952
|
// 如果用户未监听事件,显示默认提示
|
|
908
953
|
if (!this.hasEventListener('videoLimitReached')) {
|
|
909
954
|
uni.showToast({
|
|
910
955
|
title: `最多上传${this.videoLimit}个视频`,
|
|
911
956
|
icon: 'none'
|
|
912
|
-
})
|
|
957
|
+
});
|
|
913
958
|
}
|
|
914
|
-
return
|
|
959
|
+
return;
|
|
915
960
|
}
|
|
916
961
|
|
|
917
962
|
uni.chooseVideo({
|
|
@@ -926,20 +971,20 @@ export default {
|
|
|
926
971
|
maxDuration: this.videoMaxDuration,
|
|
927
972
|
file: res,
|
|
928
973
|
message: `视频时长不能超过${this.videoMaxDuration}秒`
|
|
929
|
-
})
|
|
974
|
+
});
|
|
930
975
|
|
|
931
976
|
// 如果用户未监听事件,显示默认提示
|
|
932
977
|
if (!this.hasEventListener('videoDurationExceed')) {
|
|
933
978
|
uni.showToast({
|
|
934
979
|
title: `视频时长不能超过${this.videoMaxDuration}秒`,
|
|
935
980
|
icon: 'none'
|
|
936
|
-
})
|
|
981
|
+
});
|
|
937
982
|
}
|
|
938
|
-
return
|
|
983
|
+
return;
|
|
939
984
|
}
|
|
940
|
-
this.uploadVideo(res.tempFilePath)
|
|
985
|
+
this.uploadVideo(res.tempFilePath);
|
|
941
986
|
}
|
|
942
|
-
})
|
|
987
|
+
});
|
|
943
988
|
},
|
|
944
989
|
|
|
945
990
|
/**
|
|
@@ -949,57 +994,57 @@ export default {
|
|
|
949
994
|
const uploadTask = uni.uploadFile({
|
|
950
995
|
url: this.finalUploadUrl,
|
|
951
996
|
filePath,
|
|
952
|
-
name:
|
|
997
|
+
name: 'file',
|
|
953
998
|
success: (uploadFileRes) => {
|
|
954
|
-
const result = JSON.parse(uploadFileRes.data)
|
|
999
|
+
const result = JSON.parse(uploadFileRes.data);
|
|
955
1000
|
|
|
956
1001
|
const fileInfo = {
|
|
957
1002
|
fileName: result.data.fileName,
|
|
958
1003
|
name: result.data.originFileName || 'video',
|
|
959
1004
|
url: result.data.pathUrl,
|
|
960
1005
|
type: 'video'
|
|
961
|
-
}
|
|
1006
|
+
};
|
|
962
1007
|
|
|
963
|
-
this.videoFileList.push(fileInfo)
|
|
964
|
-
this.emitVideoChange()
|
|
1008
|
+
this.videoFileList.push(fileInfo);
|
|
1009
|
+
this.emitVideoChange();
|
|
965
1010
|
|
|
966
1011
|
// 触发视频上传成功事件
|
|
967
1012
|
this.$emit('videoUploadSuccess', {
|
|
968
1013
|
file: fileInfo,
|
|
969
1014
|
response: result
|
|
970
|
-
})
|
|
1015
|
+
});
|
|
971
1016
|
},
|
|
972
1017
|
fail: (err) => {
|
|
973
1018
|
uni.showToast({
|
|
974
1019
|
icon: 'none',
|
|
975
1020
|
title: '视频上传失败'
|
|
976
|
-
})
|
|
1021
|
+
});
|
|
977
1022
|
|
|
978
1023
|
// 触发视频上传失败事件
|
|
979
1024
|
this.$emit('videoUploadFail', {
|
|
980
1025
|
error: err,
|
|
981
1026
|
message: '视频上传失败'
|
|
982
|
-
})
|
|
1027
|
+
});
|
|
983
1028
|
},
|
|
984
1029
|
complete: () => {
|
|
985
|
-
uni.hideLoading()
|
|
1030
|
+
uni.hideLoading();
|
|
986
1031
|
}
|
|
987
|
-
})
|
|
1032
|
+
});
|
|
988
1033
|
|
|
989
1034
|
uploadTask.onProgressUpdate((res) => {
|
|
990
1035
|
uni.showLoading({
|
|
991
|
-
title: '已上传' + res.progress +
|
|
992
|
-
mask: true
|
|
1036
|
+
title: '已上传' + res.progress + '%',
|
|
1037
|
+
mask: true
|
|
993
1038
|
});
|
|
994
|
-
console.log(
|
|
995
|
-
console.log(
|
|
996
|
-
console.log(
|
|
1039
|
+
console.log('上传进度' + res.progress);
|
|
1040
|
+
console.log('已经上传的数据长度' + res.totalBytesSent);
|
|
1041
|
+
console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend);
|
|
997
1042
|
// // 测试条件,取消上传任务。
|
|
998
1043
|
if (res.progress === 100) {
|
|
999
1044
|
uni.hideLoading();
|
|
1000
1045
|
uni.showLoading({
|
|
1001
|
-
title:
|
|
1002
|
-
mask: true
|
|
1046
|
+
title: '确认中...',
|
|
1047
|
+
mask: true
|
|
1003
1048
|
});
|
|
1004
1049
|
}
|
|
1005
1050
|
});
|
|
@@ -1009,42 +1054,44 @@ export default {
|
|
|
1009
1054
|
* 处理视频拖拽排序变化
|
|
1010
1055
|
*/
|
|
1011
1056
|
handleVideoDragChange(newUrls) {
|
|
1012
|
-
const newFileList = newUrls
|
|
1013
|
-
|
|
1014
|
-
|
|
1057
|
+
const newFileList = newUrls
|
|
1058
|
+
.map((url) => {
|
|
1059
|
+
return this.videoFileList.find((file) => file.url === url);
|
|
1060
|
+
})
|
|
1061
|
+
.filter(Boolean);
|
|
1015
1062
|
|
|
1016
|
-
this.videoFileList = newFileList
|
|
1017
|
-
this.emitVideoChange()
|
|
1063
|
+
this.videoFileList = newFileList;
|
|
1064
|
+
this.emitVideoChange();
|
|
1018
1065
|
},
|
|
1019
1066
|
|
|
1020
1067
|
/**
|
|
1021
1068
|
* 触发视频变更事件
|
|
1022
1069
|
*/
|
|
1023
1070
|
emitVideoChange() {
|
|
1024
|
-
this.$emit('videoChange', this.videoFileList)
|
|
1071
|
+
this.$emit('videoChange', this.videoFileList);
|
|
1025
1072
|
},
|
|
1026
1073
|
|
|
1027
1074
|
/**
|
|
1028
1075
|
* 处理拖拽组件中的视频点击
|
|
1029
1076
|
*/
|
|
1030
1077
|
handleVideoItemClick(item) {
|
|
1031
|
-
console.log('点击视频:', item)
|
|
1078
|
+
console.log('点击视频:', item);
|
|
1032
1079
|
// shmily-drag-media 传递的是 item.src
|
|
1033
|
-
this.playVideo(item.src)
|
|
1080
|
+
this.playVideo(item.src);
|
|
1034
1081
|
},
|
|
1035
1082
|
|
|
1036
1083
|
/**
|
|
1037
|
-
* 播放视频(统一播放方法)
|
|
1084
|
+
* 播放视频(统一播放方法)
|
|
1038
1085
|
*/
|
|
1039
1086
|
playVideo(url) {
|
|
1040
1087
|
// 设置视频源
|
|
1041
|
-
this.fullscreenVideoSrc = url
|
|
1088
|
+
this.fullscreenVideoSrc = url;
|
|
1042
1089
|
// 等待视频组件更新
|
|
1043
1090
|
this.$nextTick(() => {
|
|
1044
|
-
const videoContext = uni.createVideoContext('fullscreenPreviewVideo', this)
|
|
1045
|
-
videoContext.requestFullScreen()
|
|
1046
|
-
videoContext.play()
|
|
1047
|
-
})
|
|
1091
|
+
const videoContext = uni.createVideoContext('fullscreenPreviewVideo', this);
|
|
1092
|
+
videoContext.requestFullScreen();
|
|
1093
|
+
videoContext.play();
|
|
1094
|
+
});
|
|
1048
1095
|
},
|
|
1049
1096
|
|
|
1050
1097
|
// ==================== 只读模式交互 ====================
|
|
@@ -1052,15 +1099,15 @@ export default {
|
|
|
1052
1099
|
* 判断是否是最后一个真实元素(用于显示 +N 遮罩)
|
|
1053
1100
|
*/
|
|
1054
1101
|
isLastRealItem(displayIndex) {
|
|
1055
|
-
const file = this.readonlyDisplayList[displayIndex]
|
|
1056
|
-
if (!file || file.type === 'placeholder') return false
|
|
1102
|
+
const file = this.readonlyDisplayList[displayIndex];
|
|
1103
|
+
if (!file || file.type === 'placeholder') return false;
|
|
1057
1104
|
|
|
1058
1105
|
// 计算占位元素数量
|
|
1059
|
-
const placeholderCount = this.readonlyDisplayList.filter(f => f.type === 'placeholder').length
|
|
1060
|
-
const realIndex = displayIndex - placeholderCount
|
|
1106
|
+
const placeholderCount = this.readonlyDisplayList.filter((f) => f.type === 'placeholder').length;
|
|
1107
|
+
const realIndex = displayIndex - placeholderCount;
|
|
1061
1108
|
|
|
1062
1109
|
// 判断是否是最后一个真实元素,且总数超过限制
|
|
1063
|
-
return this.allFileList.length > this.maxShowCount && realIndex === this.maxShowCount - 1
|
|
1110
|
+
return this.allFileList.length > this.maxShowCount && realIndex === this.maxShowCount - 1;
|
|
1064
1111
|
},
|
|
1065
1112
|
|
|
1066
1113
|
/**
|
|
@@ -1069,7 +1116,7 @@ export default {
|
|
|
1069
1116
|
handleReadonlyItemClick(index, file) {
|
|
1070
1117
|
// 如果点击的是占位元素,不做任何处理
|
|
1071
1118
|
if (!file || file.type === 'placeholder') {
|
|
1072
|
-
return
|
|
1119
|
+
return;
|
|
1073
1120
|
}
|
|
1074
1121
|
|
|
1075
1122
|
// 判断是否点击的是遮罩层(最后一项且超出限制)
|
|
@@ -1079,19 +1126,19 @@ export default {
|
|
|
1079
1126
|
showCount: this.maxShowCount,
|
|
1080
1127
|
hiddenCount: this.allFileList.length - this.maxShowCount,
|
|
1081
1128
|
allFiles: this.allFileList
|
|
1082
|
-
})
|
|
1129
|
+
});
|
|
1083
1130
|
|
|
1084
1131
|
if (!this.hasEventListener('moreClick')) {
|
|
1085
|
-
this.openPopup()
|
|
1132
|
+
this.openPopup();
|
|
1086
1133
|
}
|
|
1087
1134
|
} else {
|
|
1088
1135
|
if (file.type === 'image') {
|
|
1089
1136
|
// 预览图片(只提取图片列表)
|
|
1090
|
-
const imageUrls = this.allFileList.filter(f => f.type === 'image').map(f => f.url)
|
|
1091
|
-
const imageIndex = this.imageFileList.findIndex(f => f.url === file.url)
|
|
1092
|
-
this.previewImage(imageUrls, imageIndex)
|
|
1137
|
+
const imageUrls = this.allFileList.filter((f) => f.type === 'image').map((f) => f.url);
|
|
1138
|
+
const imageIndex = this.imageFileList.findIndex((f) => f.url === file.url);
|
|
1139
|
+
this.previewImage(imageUrls, imageIndex);
|
|
1093
1140
|
} else if (file.type === 'video') {
|
|
1094
|
-
this.playVideo(file.url)
|
|
1141
|
+
this.playVideo(file.url);
|
|
1095
1142
|
}
|
|
1096
1143
|
}
|
|
1097
1144
|
},
|
|
@@ -1101,43 +1148,43 @@ export default {
|
|
|
1101
1148
|
*/
|
|
1102
1149
|
handlePopupItemClick(file, index) {
|
|
1103
1150
|
if (file.type === 'image') {
|
|
1104
|
-
const imageUrls = this.allFileList.filter(f => f.type === 'image').map(f => f.url)
|
|
1105
|
-
const imageIndex = this.imageFileList.findIndex(f => f.url === file.url)
|
|
1106
|
-
this.previewImage(imageUrls, imageIndex)
|
|
1151
|
+
const imageUrls = this.allFileList.filter((f) => f.type === 'image').map((f) => f.url);
|
|
1152
|
+
const imageIndex = this.imageFileList.findIndex((f) => f.url === file.url);
|
|
1153
|
+
this.previewImage(imageUrls, imageIndex);
|
|
1107
1154
|
} else if (file.type === 'video') {
|
|
1108
|
-
this.playVideo(file.url)
|
|
1155
|
+
this.playVideo(file.url);
|
|
1109
1156
|
}
|
|
1110
1157
|
},
|
|
1111
1158
|
|
|
1112
1159
|
// ==================== 弹窗控制 ====================
|
|
1113
1160
|
openPopup() {
|
|
1114
|
-
this.popupVisible = true
|
|
1161
|
+
this.popupVisible = true;
|
|
1115
1162
|
this.$nextTick(() => {
|
|
1116
1163
|
setTimeout(() => {
|
|
1117
|
-
this.popupAnimated = true
|
|
1164
|
+
this.popupAnimated = true;
|
|
1118
1165
|
this.initPopupLazyObserver(); // 弹窗打开时初始化懒加载观察器
|
|
1119
|
-
}, 50)
|
|
1120
|
-
})
|
|
1166
|
+
}, 50);
|
|
1167
|
+
});
|
|
1121
1168
|
},
|
|
1122
1169
|
|
|
1123
1170
|
closePopup() {
|
|
1124
|
-
this.popupAnimated = false
|
|
1171
|
+
this.popupAnimated = false;
|
|
1125
1172
|
setTimeout(() => {
|
|
1126
|
-
this.popupVisible = false
|
|
1173
|
+
this.popupVisible = false;
|
|
1127
1174
|
if (this.popupObserver) {
|
|
1128
1175
|
this.popupObserver.disconnect(); // 弹窗关闭时断开观察器
|
|
1129
1176
|
this.popupObserver = null;
|
|
1130
1177
|
}
|
|
1131
|
-
}, 300)
|
|
1178
|
+
}, 300);
|
|
1132
1179
|
},
|
|
1133
1180
|
|
|
1134
1181
|
// ==================== 加载状态管理 ====================
|
|
1135
1182
|
onMediaLoad(key) {
|
|
1136
|
-
this.$set(this.mediaLoadingStatus, key, true)
|
|
1183
|
+
this.$set(this.mediaLoadingStatus, key, true);
|
|
1137
1184
|
},
|
|
1138
1185
|
|
|
1139
1186
|
onMediaError(key) {
|
|
1140
|
-
this.$set(this.mediaLoadingStatus, key, true)
|
|
1187
|
+
this.$set(this.mediaLoadingStatus, key, true);
|
|
1141
1188
|
},
|
|
1142
1189
|
|
|
1143
1190
|
// ==================== 初始化图片懒加载观察器 ====================
|
|
@@ -1152,18 +1199,18 @@ export default {
|
|
|
1152
1199
|
this.observer = null;
|
|
1153
1200
|
}
|
|
1154
1201
|
|
|
1155
|
-
this.lazyLoadImageSrcs = Array(this.allFileList.length).fill(null);
|
|
1202
|
+
this.lazyLoadImageSrcs = Array(this.allFileList.length).fill(null);
|
|
1156
1203
|
|
|
1157
1204
|
this.$nextTick(() => {
|
|
1158
1205
|
const currentObserver = uni.createIntersectionObserver(this, { observeAll: true });
|
|
1159
|
-
this.observer = currentObserver;
|
|
1206
|
+
this.observer = currentObserver;
|
|
1160
1207
|
|
|
1161
1208
|
if (!this.observer) {
|
|
1162
|
-
console.warn(
|
|
1209
|
+
console.warn('IntersectionObserver failed to create.');
|
|
1163
1210
|
return;
|
|
1164
1211
|
}
|
|
1165
1212
|
|
|
1166
|
-
this.observer.relativeToViewport({ top: 0, bottom: 50 });
|
|
1213
|
+
this.observer.relativeToViewport({ top: 0, bottom: 50 });
|
|
1167
1214
|
|
|
1168
1215
|
this.allFileList.forEach((file, index) => {
|
|
1169
1216
|
if (file.type === 'image') {
|
|
@@ -1171,7 +1218,7 @@ export default {
|
|
|
1171
1218
|
currentObserver.observe(sel, (res) => {
|
|
1172
1219
|
const isVisible = res.intersectionRatio > 0;
|
|
1173
1220
|
if (isVisible && !this.lazyLoadImageSrcs[index]) {
|
|
1174
|
-
this.$set(this.lazyLoadImageSrcs, index, file.url);
|
|
1221
|
+
this.$set(this.lazyLoadImageSrcs, index, file.url);
|
|
1175
1222
|
}
|
|
1176
1223
|
});
|
|
1177
1224
|
}
|
|
@@ -1200,7 +1247,7 @@ export default {
|
|
|
1200
1247
|
this.popupObserver = currentPopupObserver;
|
|
1201
1248
|
|
|
1202
1249
|
if (!this.popupObserver) {
|
|
1203
|
-
console.warn(
|
|
1250
|
+
console.warn('Popup IntersectionObserver failed to create.');
|
|
1204
1251
|
return;
|
|
1205
1252
|
}
|
|
1206
1253
|
|
|
@@ -1234,12 +1281,12 @@ export default {
|
|
|
1234
1281
|
if (this.uploadType === 'both') {
|
|
1235
1282
|
return {
|
|
1236
1283
|
images: this.imageFileList,
|
|
1237
|
-
videos: this.videoFileList
|
|
1238
|
-
}
|
|
1284
|
+
videos: this.videoFileList
|
|
1285
|
+
};
|
|
1239
1286
|
} else if (this.uploadType === 'image') {
|
|
1240
|
-
return this.imageFileList
|
|
1287
|
+
return this.imageFileList;
|
|
1241
1288
|
} else if (this.uploadType === 'video') {
|
|
1242
|
-
return this.videoFileList
|
|
1289
|
+
return this.videoFileList;
|
|
1243
1290
|
}
|
|
1244
1291
|
},
|
|
1245
1292
|
|
|
@@ -1247,31 +1294,31 @@ export default {
|
|
|
1247
1294
|
* 获取参数(不包含 url 字段)
|
|
1248
1295
|
*/
|
|
1249
1296
|
getParams() {
|
|
1250
|
-
const imageParams = this.imageFileList.map(file => {
|
|
1251
|
-
const { url, type, ...rest } = file
|
|
1252
|
-
return rest
|
|
1253
|
-
})
|
|
1254
|
-
const videoParams = this.videoFileList.map(file => {
|
|
1255
|
-
const { url, type, ...rest } = file
|
|
1256
|
-
return rest
|
|
1257
|
-
})
|
|
1297
|
+
const imageParams = this.imageFileList.map((file) => {
|
|
1298
|
+
const { url, type, ...rest } = file;
|
|
1299
|
+
return rest;
|
|
1300
|
+
});
|
|
1301
|
+
const videoParams = this.videoFileList.map((file) => {
|
|
1302
|
+
const { url, type, ...rest } = file;
|
|
1303
|
+
return rest;
|
|
1304
|
+
});
|
|
1258
1305
|
if (this.uploadType === 'both') {
|
|
1259
1306
|
return {
|
|
1260
1307
|
images: JSON.stringify(imageParams),
|
|
1261
1308
|
videos: JSON.stringify(videoParams)
|
|
1262
|
-
}
|
|
1309
|
+
};
|
|
1263
1310
|
} else if (this.uploadType === 'image') {
|
|
1264
1311
|
return {
|
|
1265
1312
|
images: JSON.stringify(imageParams)
|
|
1266
|
-
}
|
|
1313
|
+
};
|
|
1267
1314
|
} else if (this.uploadType === 'video') {
|
|
1268
1315
|
return {
|
|
1269
1316
|
videos: JSON.stringify(videoParams)
|
|
1270
|
-
}
|
|
1317
|
+
};
|
|
1271
1318
|
}
|
|
1272
1319
|
}
|
|
1273
1320
|
}
|
|
1274
|
-
}
|
|
1321
|
+
};
|
|
1275
1322
|
</script>
|
|
1276
1323
|
|
|
1277
1324
|
<style lang="scss">
|