imeik-imui 2.0.5 → 2.0.7

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.
Binary file
@@ -41,6 +41,7 @@
41
41
  </FileLinePreview>
42
42
  </li>
43
43
  </ul>
44
+ <PdfPreviewDialog ref="PdfPreviewDialog"></PdfPreviewDialog>
44
45
  </div>
45
46
  </template>
46
47
 
@@ -48,11 +49,13 @@
48
49
  import { beforeUpload, fnUploadRequest } from './upload.js'
49
50
  import emitter from 'element-ui/src/mixins/emitter'
50
51
  import FileLinePreview from './widgets/FileLinePreview.vue'
52
+ import PdfPreviewDialog from './widgets/PdfPreviewDialog.vue'
51
53
 
52
54
  export default {
53
55
  name: 'FieldFileUpload',
54
56
  components: {
55
- FileLinePreview
57
+ FileLinePreview,
58
+ PdfPreviewDialog
56
59
  },
57
60
  mixins: [emitter],
58
61
  props: {
@@ -135,12 +138,30 @@ export default {
135
138
 
136
139
  // 预览pdf
137
140
  onPreviewPdf(url) {
138
- this.listeners.previewPdf && this.listeners.previewPdf(url)
141
+ this.listeners.previewPdf ? this.listeners.previewPdf(url) : this.previewPdf(url)
139
142
  },
140
143
 
141
144
  // 预览图片
142
145
  onPreviewImage(url) {
143
- this.listeners.previewImage && this.listeners.previewImage(url)
146
+ this.listeners.previewImage ? this.listeners.previewImage(url) : this.previewImage(url)
147
+ },
148
+
149
+ // 查看pdf
150
+ previewPdf(url) {
151
+ this.$refs.PdfPreviewDialog.show({
152
+ url
153
+ })
154
+ },
155
+
156
+ // 查看图片
157
+ previewImage(url) {
158
+ this.$viewerApi({
159
+ options: {
160
+ navbar: false,
161
+ initialViewIndex: 0
162
+ },
163
+ images: [url]
164
+ })
144
165
  },
145
166
 
146
167
  // 根据url和name判断前后数组是否一样
@@ -167,7 +188,7 @@ export default {
167
188
  return
168
189
  }
169
190
 
170
- if (!this.fileList.find(i => i.uid === file.uid)) {
191
+ if (!this.fileList.find((i) => i.uid === file.uid)) {
171
192
  this.fileList.push({
172
193
  uid: file.uid,
173
194
  name: file.name
@@ -178,7 +199,7 @@ export default {
178
199
  // 上传成功回调
179
200
  handleSuccess(res, file) {
180
201
  if (res) {
181
- const index = this.fileList.findIndex(i => i.uid === file.uid)
202
+ const index = this.fileList.findIndex((i) => i.uid === file.uid)
182
203
 
183
204
  // 通过uid找到对应的数据,更新其url字段
184
205
  if (index > -1) {
@@ -207,7 +228,7 @@ export default {
207
228
 
208
229
  if (!result) {
209
230
  const uid = file.uid
210
- this.fileList = this.fileList.filter(item => item.uid !== uid)
231
+ this.fileList = this.fileList.filter((item) => item.uid !== uid)
211
232
  }
212
233
 
213
234
  return result
@@ -1,6 +1,18 @@
1
1
  <template>
2
2
  <div>
3
- <div v-if="isView && !uploadedImageList.length"></div>
3
+ <div v-if="isView">
4
+ <!--提示信息-->
5
+ <div v-if="attrs.tipText && !isView" class="upload-tip" v-html="attrs.tipText"></div>
6
+ <!--文件列表-->
7
+ <div class="img-list">
8
+ <div v-for="(item, index) in uploadedImageList" :key="index" class="item-view">
9
+ <el-image :src="item" class="file-img-view" />
10
+ <div class="operate">
11
+ <i class="el-icon-view" @click="showImgView({url: item})"></i>
12
+ </div>
13
+ </div>
14
+ </div>
15
+ </div>
4
16
  <el-upload
5
17
  v-else
6
18
  ref="upload"
@@ -16,17 +28,8 @@
16
28
  :before-upload="onBeforeUpload"
17
29
  v-on="listeners"
18
30
  >
19
- <div v-if="isView" class="imageHolder" @click.stop="stopClick">
20
- <div v-for="(item, index) in uploadedImageList" :key="index">
21
- <el-image style="width: 146px; height: 146px" :src="item" fit="cover"></el-image>
22
- <div class="operate">
23
- <i class="el-icon-view" @click="showImgView({ url: item })"></i>
24
- <i v-if="!attrs.disabled && !isView" class="el-icon-delete" @click.stop="handleRemove"></i>
25
- </div>
26
- </div>
27
- </div>
28
- <i v-else class="el-icon-plus"></i>
29
- <div v-if="!attrs.disabled && !isView" slot="tip" class="el-upload__tip">
31
+ <i class="el-icon-plus"></i>
32
+ <div v-if="!attrs.disabled" slot="tip" class="el-upload__tip">
30
33
  {{ attrs.tipText || '请上传.png,.jpg,.jpeg,.gif,.svg格式文件,且不超过20M' }}
31
34
  </div>
32
35
  </el-upload>
@@ -161,37 +164,42 @@ export default {
161
164
  display: none;
162
165
  }
163
166
  }
164
-
165
- .imageHolder {
166
- position: relative;
167
+ .img-list {
168
+ display: flex;
169
+ flex-wrap: wrap;
170
+ gap: 8px;
167
171
  width: 100%;
168
- height: 100%;
169
-
170
- .operate {
171
- position: absolute;
172
- top: 0;
173
- left: 0;
174
- width: 100%;
175
- text-align: center;
176
- opacity: 0;
177
- transition: all 0.2s;
178
- height: 146px;
179
- line-height: 146px;
180
- cursor: pointer;
181
-
182
- i {
183
- padding: 4px;
184
- color: white;
185
- font-size: 24px;
172
+ .item-view {
173
+ position: relative;
174
+ width: 148px;
175
+ height: 148px;
176
+ .file-img-view {
177
+ width: 148px;
178
+ height: 148px;
186
179
  }
187
180
  }
181
+ }
182
+ .operate {
183
+ position: absolute;
184
+ top: 0;
185
+ left: 0;
186
+ width: 100%;
187
+ height: 100%;
188
+ text-align: center;
189
+ opacity: 0;
190
+ transition: all 0.2s;
188
191
 
189
- &:hover {
190
- .operate {
191
- display: block;
192
- background: rgba(0, 0, 0, 0.5);
193
- opacity: 1;
194
- }
192
+ i {
193
+ padding: 4px;
194
+ color: white;
195
+ font-size: 24px;
196
+ margin-top: 60px;
197
+ cursor: pointer;
195
198
  }
196
199
  }
200
+ .operate:hover {
201
+ display: block;
202
+ background: rgba(0, 0, 0, 0.5);
203
+ opacity: 1;
204
+ }
197
205
  </style>
@@ -0,0 +1,371 @@
1
+ <template>
2
+ <!-- 录屏组件容器 -->
3
+ <div class="record-screen">
4
+ <!-- 预览列表区域 -->
5
+ <div class="preview-list">
6
+ <!-- 遍历已录制视频列表,显示预览项 -->
7
+ <div v-for="(recordingUrl, index) in fileList" :key="index" class="preview-item">
8
+ <!-- 如果有视频URL则显示视频预览 -->
9
+ <video v-if="recordingUrl" autoplay :src="recordingUrl.url" class="video-preview" />
10
+ <!-- 加载中状态显示loading -->
11
+ <ImLoading v-else />
12
+ <!-- 播放按钮 -->
13
+ <div v-if="recordingUrl" class="play-btn" @click="playVideo(recordingUrl)">
14
+ <img src="https://udstatic.imeik.com/pcUploads/1732257019241/play-icon.png" alt="播放" />
15
+ </div>
16
+ <!-- 预览项操作按钮区域 -->
17
+ <div v-if="recordingUrl" class="preview-actions">
18
+ <!-- 删除按钮 -->
19
+ <span class="close-btn" @click="removeRecording(index)">
20
+ <img src="https://udstatic.imeik.com/pcUploads/1732247355378/delete-icon.png" alt="删除" />
21
+ </span>
22
+ </div>
23
+ </div>
24
+ <!-- 录制按钮,当录制数量小于最大限制时显示 -->
25
+ <el-upload
26
+ v-if="!isView && fileList.length < maxfileList"
27
+ action
28
+ :multiple="true"
29
+ :show-file-list="false"
30
+ accept=".mp4,.ogg,.flv,.avi,.wmv,.rmvb,.webm"
31
+ :http-request="fnUploadRequest"
32
+ :on-success="handleSuccess"
33
+ :before-upload="onBeforeUpload"
34
+ :on-change="handleChange"
35
+ v-on="listeners"
36
+ >
37
+ <div class="record-btn" @click="startRecording">
38
+ <i class="el-icon-plus" style="font-size: 28px; color:#8c939d"></i>
39
+ </div>
40
+ </el-upload>
41
+ </div>
42
+ <div v-if="!isView" slot="tip" class="el-upload__tip">请上传视频文件,文件小于3GB</div>
43
+ <!-- 视频播放弹窗 -->
44
+ <el-dialog title="视频播放" :visible.sync="dialogVisible" width="900px" append-to-body :before-close="handleClose">
45
+ <!-- 视频播放器 -->
46
+ <video v-if="currentVideoUrl" :src="currentVideoUrl" controls autoplay style="width: 100%"></video>
47
+ </el-dialog>
48
+ </div>
49
+ </template>
50
+
51
+ <script>
52
+ import { beforeUpload, fnUploadRequest } from './upload.js'
53
+ export default {
54
+ name: 'RecordScreen',
55
+ // 组件属性定义
56
+ props: {
57
+ value: {
58
+ type: Array,
59
+ default: () => [] // 默认空数组
60
+ },
61
+ attrs: {
62
+ type: Object,
63
+ default() {
64
+ return {}
65
+ }
66
+ },
67
+ listeners: {
68
+ type: Object,
69
+ default() {
70
+ return {}
71
+ }
72
+ }
73
+ },
74
+ // 组件内部数据
75
+ data() {
76
+ return {
77
+ fileList: [], // 录制视频列表
78
+ isRecording: false, // 是否正在录制
79
+ mediaRecorder: null, // 媒体录制器实例
80
+ recordedChunks: [], // 录制的视频数据块
81
+ error: '', // 错误信息
82
+ maxfileList: 5, // 最大录制数量限制
83
+ dialogVisible: false, // 视频播放弹窗显示状态
84
+ currentVideoUrl: '' // 当前播放的视频URL
85
+ }
86
+ },
87
+ computed: {
88
+ isView() {
89
+ return this.attrs.isView
90
+ }
91
+ },
92
+ // 监听属性变化
93
+ watch: {
94
+ value: {
95
+ handler(newVal) {
96
+ this.setMyValue(newVal)
97
+ },
98
+ immediate: true // 组件创建时立即执行一次
99
+ }
100
+ },
101
+ methods: {
102
+ // 设置组件内部的fileList值
103
+ setMyValue(newVal) {
104
+ try {
105
+ this.fileList = JSON.parse(JSON.stringify(newVal))
106
+ } catch (err) {
107
+ this.fileList = []
108
+ }
109
+ },
110
+
111
+ // 开始录制
112
+ async startRecording() {
113
+ this.error = ''
114
+ try {
115
+ // 上传文件
116
+ fnUploadRequest({
117
+ file,
118
+ onSuccess: (res) => {
119
+ console.log('res', res)
120
+ // 更新视频URL
121
+ this.$set(this.fileList, this.fileList.length - 1, res.url)
122
+ this.isRecording = false
123
+ },
124
+ onError: (err) => {
125
+ this.error = err.message || '截屏失败,请重试'
126
+ this.isRecording = false
127
+ }
128
+ })
129
+ // 检查录制数量限制
130
+ if (this.fileList.length >= this.maxfileList) {
131
+ throw new Error(`最多只能上传${this.maxfileList}个录屏`)
132
+ }
133
+ } catch (err) {
134
+ this.$emit('endRecord')
135
+ if (err.message === 'Permission denied') {
136
+ this.error = '操作权限被阻止'
137
+ } else {
138
+ this.error = err.message || '录屏失败,请重试'
139
+ }
140
+ this.isRecording = false
141
+ }
142
+ },
143
+ // 删除指定索引的录制视频
144
+ removeRecording(index) {
145
+ URL.revokeObjectURL(this.fileList[index].url) // 释放URL对象
146
+ this.fileList.splice(index, 1)
147
+ this.onUpdate() // 通知父组件值变化
148
+ },
149
+
150
+ // 播放视频
151
+ playVideo(videoUrl) {
152
+ this.currentVideoUrl = videoUrl
153
+ this.dialogVisible = true
154
+ },
155
+
156
+ // 关闭视频播放弹窗
157
+ handleClose() {
158
+ this.dialogVisible = false
159
+ this.currentVideoUrl = ''
160
+ },
161
+
162
+ onBeforeUpload(file) {
163
+ if (this.fileList.length > this.attrs.limit) {
164
+ this.$message.warning(`最多上传${this.attrs.limit}个文件`)
165
+ return false
166
+ }
167
+
168
+ // 默认上传文件限制2GB
169
+ const result = beforeUpload(file, this.attrs.fileSize || 2048)
170
+
171
+ if (!result) {
172
+ const uid = file.uid
173
+ this.fileList = this.fileList.filter((item) => item.uid !== uid)
174
+ }
175
+
176
+ return result
177
+ },
178
+ // 上传成功回调
179
+ handleSuccess(res, file) {
180
+ if (res) {
181
+ const index = this.fileList.findIndex((i) => i.uid === file.uid)
182
+
183
+ // 通过uid找到对应的数据,更新其url字段
184
+ if (index > -1) {
185
+ this.$set(this.fileList[index], 'url', res.url)
186
+ }
187
+
188
+ this.onUpdate()
189
+ }
190
+ },
191
+
192
+ onUpdate() {
193
+ const fileArr = JSON.parse(JSON.stringify(this.fileList))
194
+ console.log('input-fileArr', fileArr)
195
+ this.$emit('input', fileArr)
196
+ },
197
+
198
+ handleChange(file) {
199
+ if (this.fileList.length >= this.attrs.limit) {
200
+ return
201
+ }
202
+
203
+ if (!this.fileList.find((i) => i.uid === file.uid)) {
204
+ this.fileList.push({
205
+ uid: file.uid,
206
+ name: file.name
207
+ })
208
+ }
209
+ },
210
+ fnUploadRequest
211
+ }
212
+ }
213
+ </script>
214
+
215
+ <style scoped lang="scss">
216
+ .record-screen {
217
+ // 预览列表样式
218
+ .preview-list {
219
+ display: flex;
220
+ flex-wrap: wrap;
221
+ gap: 10px;
222
+ margin-top: 10px;
223
+
224
+ // 预览项样式
225
+ .preview-item {
226
+ position: relative;
227
+ width: 228px;
228
+ height: 148px;
229
+ border-radius: 5px;
230
+ border: 1px solid #d9d9d9;
231
+ text-align: center;
232
+ line-height: 128px;
233
+
234
+ // 视频预览样式
235
+ .video-preview {
236
+ position: relative;
237
+ z-index: 1;
238
+ width: 100%;
239
+ height: 100%;
240
+ object-fit: cover;
241
+ }
242
+
243
+ // 预览项遮罩层
244
+ &::after {
245
+ content: '';
246
+ position: absolute;
247
+ top: 0;
248
+ left: 0;
249
+ width: 100%;
250
+ height: 100%;
251
+ background-color: rgba(0, 0, 0, 0.3);
252
+ z-index: 1;
253
+ border-radius: 5px;
254
+ }
255
+
256
+ // 播放按钮样式
257
+ .play-btn {
258
+ position: absolute;
259
+ z-index: 3;
260
+ top: 50%;
261
+ left: 50%;
262
+ transform: translate(-50%, -50%);
263
+ width: 45px;
264
+ height: 45px;
265
+ cursor: pointer;
266
+ background: rgba(0, 0, 0, 0.2);
267
+ border-radius: 50%;
268
+ display: flex;
269
+ align-items: center;
270
+ justify-content: center;
271
+
272
+ img {
273
+ width: 100%;
274
+ height: 100%;
275
+ }
276
+
277
+ &:hover {
278
+ background: rgba(0, 0, 0, 0.5);
279
+ }
280
+ }
281
+
282
+ // 预览项操作按钮样式
283
+ .preview-actions {
284
+ position: absolute;
285
+ z-index: 3;
286
+ top: -8px;
287
+ right: -8px;
288
+
289
+ // 删除按钮样式
290
+ .close-btn {
291
+ display: flex;
292
+ align-items: center;
293
+ justify-content: center;
294
+ width: 16px;
295
+ height: 16px;
296
+ cursor: pointer;
297
+ padding: 0;
298
+
299
+ img {
300
+ width: 16px;
301
+ height: 16px;
302
+ }
303
+
304
+ &:hover {
305
+ opacity: 0.8;
306
+ }
307
+ }
308
+ }
309
+ }
310
+
311
+ // 录制按钮样式
312
+ .record-btn {
313
+ display: flex;
314
+ flex-direction: column;
315
+ gap: 13px;
316
+ align-items: center;
317
+ justify-content: center;
318
+ width: 148px;
319
+ height: 148px;
320
+ border: 1px dashed #c0ccda;
321
+ border-radius: 6px;
322
+ background-color: #fbfdff;
323
+ cursor: pointer;
324
+
325
+ &:hover {
326
+ border-color: #40a9ff;
327
+ }
328
+
329
+ // 录制中状态样式
330
+ &.is-recording {
331
+ border-color: #ff4d4f;
332
+ animation: pulse 2s infinite;
333
+ }
334
+
335
+ .icon-record {
336
+ width: 26px;
337
+ height: 20px;
338
+ display: block;
339
+ }
340
+
341
+ .text {
342
+ font-size: 12px;
343
+ color: #999999;
344
+ line-height: 14px;
345
+ display: block;
346
+ margin-top: 20px;
347
+ }
348
+ }
349
+ }
350
+
351
+ // 错误提示样式
352
+ .error-tip {
353
+ margin-top: 8px;
354
+ color: #ff4d4f;
355
+ font-size: 12px;
356
+ }
357
+ }
358
+
359
+ // 录制中动画
360
+ @keyframes pulse {
361
+ 0% {
362
+ border-color: #ff4d4f;
363
+ }
364
+ 50% {
365
+ border-color: #ffccc7;
366
+ }
367
+ 100% {
368
+ border-color: #ff4d4f;
369
+ }
370
+ }
371
+ </style>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <el-dialog title="文件预览" fullscreen :visible.sync="pdfDialogShow">
3
+ <div class="pdfContainer">
4
+ <VuePdfEmbed class="item" :source="url"></VuePdfEmbed>
5
+ </div>
6
+ </el-dialog>
7
+ </template>
8
+
9
+ <script>
10
+ import VuePdfEmbed from 'vue-pdf-embed/dist/vue2-pdf-embed'
11
+ export default {
12
+ components: {
13
+ VuePdfEmbed
14
+ },
15
+ data() {
16
+ return {
17
+ pdfDialogShow: false,
18
+ url: undefined
19
+ }
20
+ },
21
+ methods: {
22
+ show({ url }) {
23
+ this.url = url
24
+ this.pdfDialogShow = true
25
+ }
26
+ }
27
+ }
28
+ </script>
29
+
30
+ <style scoped lang="scss">
31
+ /deep/ .el-dialog__body {
32
+ padding: 0px !important;
33
+
34
+ iframe {
35
+ border: none;
36
+ height: calc(100vh - 45px);
37
+ }
38
+ }
39
+ /deep/ .el-dialog__header {
40
+
41
+ .el-dialog__close {
42
+ font-size: 24px;
43
+ display: block;
44
+ top: -5px;
45
+ right: 0px;
46
+ }
47
+ }
48
+ .pdfContainer {
49
+ width: 100%;
50
+ // header 高度 55px
51
+ height: calc(100vh - 55px);
52
+ overflow: auto;
53
+ }
54
+ </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imeik-imui",
3
- "version": "2.0.5",
3
+ "version": "2.0.7",
4
4
  "ch_name": "imeik基础UI组件库-PC端",
5
5
  "scripts": {
6
6
  "dev": "vue-cli-service serve --mode dev",