From 79ee295c18e5c333da3f72ee4fd481e26758bb1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=9D=B0?= <727475508@qq.com> Date: Fri, 12 Jun 2026 13:59:52 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=86=E9=A2=91=E4=B8=8A=E4=BC=A0=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ali-oss-uploader/ali-oss-uploader.vue | 150 ++++++++++++----- components/dynamic-form/FormItemVideo.vue | 157 +++++++++++++++--- pages/shop/add-service.vue | 30 ++-- utils/request.js | 8 +- 4 files changed, 264 insertions(+), 81 deletions(-) diff --git a/components/ali-oss-uploader/ali-oss-uploader.vue b/components/ali-oss-uploader/ali-oss-uploader.vue index 1433694..dd4dde8 100644 --- a/components/ali-oss-uploader/ali-oss-uploader.vue +++ b/components/ali-oss-uploader/ali-oss-uploader.vue @@ -19,9 +19,13 @@ mode="aspectFill" @click="handlePreview(index)" /> - - × - + @@ -41,27 +45,34 @@ - - - - - × + + + + + - + + @@ -161,6 +172,7 @@ export default { return { fileList: [], oneFile: '', + videoList: [], loading: false, dragIndex: -1, isDragging: false, @@ -168,6 +180,12 @@ export default { } }, + computed: { + videoCount() { + return this.showPreview ? this.videoList.length : 0 + } + }, + watch: { value: { immediate: true, @@ -179,6 +197,7 @@ export default { immediate: true, handler(newVal) { this.oneFile = newVal + this.videoList = newVal ? [newVal] : [] } } }, @@ -284,12 +303,15 @@ export default { await this.uploadFile(file); } } else if (this.accept === 'video/*') { - const res = await uni.chooseVideo({ - sourceType: [sourceType], - compressed: true, - maxDuration: 60 - }); - await this.uploadFile(res); + const count = this.maxCount - this.videoCount; + if (count <= 0) { + uni.showToast({ title: `最多上传${this.maxCount}个文件`, icon: 'none' }); + return; + } + const files = await this.chooseVideoFiles(sourceType, count); + for (const file of files) { + await this.uploadFile(file); + } } } catch (error) { console.error('选择文件失败:', error); @@ -305,7 +327,7 @@ export default { }) return Promise.reject(new Error('文件大小超出限制')) } - let date = new Date().getTime() + let date = `${new Date().getTime()}_${Math.floor(Math.random() * 100000)}` const fileExt = this.getFileExt(file) let artisan = this.type==1 ? 'yh' : this.type==2 ? 'syr' : this.type==3 ? 'sj' : ''; @@ -327,8 +349,12 @@ export default { }else if(this.accept == 'video/*') { const newFile = fileUrl this.oneFile = fileUrl - this.$emit('input', this.oneFile) - this.$emit('change', this.oneFile) + if (this.showPreview) { + this.videoList = this.videoList.concat([newFile]).slice(0, this.maxCount) + } + const emitValue = this.maxCount > 1 && this.showPreview ? this.videoList : this.oneFile + this.$emit('input', emitValue) + this.$emit('change', emitValue) this.$emit('success', newFile) return newFile } @@ -369,6 +395,44 @@ export default { return dir ? `${dir}/${name}` : name }, + getVideoPoster(url) { + if (!url) return '' + if (String(url).indexOf('x-oss-process=') > -1) return url + const connector = String(url).indexOf('?') > -1 ? '&' : '?' + return `${url}${connector}x-oss-process=video/snapshot,t_2000,f_jpg,m_fast` + }, + + chooseVideoFiles(sourceType, count) { + if (uni.chooseMedia) { + return new Promise((resolve, reject) => { + uni.chooseMedia({ + count, + mediaType: ['video'], + sourceType: [sourceType], + maxDuration: 60, + success: (res) => { + const files = (res.tempFiles || []).map((item) => ({ + ...item, + tempFilePath: item.tempFilePath || item.path, + path: item.tempFilePath || item.path, + })); + resolve(files); + }, + fail: reject, + }); + }); + } + return new Promise((resolve, reject) => { + uni.chooseVideo({ + sourceType: [sourceType], + compressed: true, + maxDuration: 60, + success: (res) => resolve([res]), + fail: reject, + }); + }); + }, + // 上传到OSS uploadToOss(file, ossConfig, name) { return new Promise((resolve, reject) => { @@ -512,11 +576,14 @@ export default { this.$emit('change', this.fileList) this.$emit('delete', deletedFile, index) }else if(this.accept == 'video/*') { - const deletedFile = this.oneFile - this.oneFile = '' - this.$emit('input', this.oneFile) - this.$emit('change', this.oneFile) - this.$emit('delete', deletedFile) + const deleteIndex = index === undefined ? 0 : index + const deletedFile = this.videoList[deleteIndex] + this.videoList.splice(deleteIndex, 1) + this.oneFile = this.videoList[0] || '' + const emitValue = this.maxCount > 1 ? this.videoList : this.oneFile + this.$emit('input', emitValue) + this.$emit('change', emitValue) + this.$emit('delete', deletedFile, deleteIndex) } // } @@ -539,8 +606,11 @@ export default { .preview-container { display: grid; - grid-template-columns: repeat(3, 1fr); - justify-items: center; + grid-template-columns: repeat(3, 200rpx); + column-gap: 18rpx; + row-gap: 20rpx; + justify-content: flex-start; + justify-items: stretch; align-items: center; position: relative; } @@ -579,7 +649,6 @@ export default { position: relative; width: 200rpx; height: 200rpx; - margin-top: 20rpx; } .image-preview-item.dragging { @@ -597,15 +666,8 @@ export default { position: absolute; top: -10rpx; right: -10rpx; - width: 40rpx; - height: 40rpx; - background-color: #ff4d4f; - color: white; - border-radius: 50%; - display: flex; - justify-content: center; - align-items: center; - font-size: 32rpx; - z-index: 1; + width: 42rpx; + height: 42rpx; + z-index: 5; } diff --git a/components/dynamic-form/FormItemVideo.vue b/components/dynamic-form/FormItemVideo.vue index cd0bada..cfae73a 100644 --- a/components/dynamic-form/FormItemVideo.vue +++ b/components/dynamic-form/FormItemVideo.vue @@ -2,14 +2,23 @@ - @@ -17,9 +26,25 @@ :userId="uploadUserId" :type="ossType" :kind="2" width="100%" :file-string="currentValue" @input="onInput" @change="onChange" @success="onInput" @delete="deleteSingleVideo" /> - + + + @@ -41,7 +66,9 @@ export default { data: function () { return { currentValue: '', - currentList: [] + currentList: [], + showPlayer: false, + playingVideo: '' }; }, computed: { @@ -80,13 +107,33 @@ export default { }, onMultiInput: function (val) { var arr = Array.isArray(val) ? val : [val]; - var list = this.currentList.concat(arr).slice(0, this.maxCount); + var existing = this.currentList.filter(function (item) { + return arr.indexOf(item) === -1; + }); + var list = existing.concat(arr).filter(function (item, index, self) { + return item && self.indexOf(item) === index; + }).slice(0, this.maxCount); this.currentList = list; this.$emit('input', list); }, onChange: function (val) { this.$emit('change', val); }, + getVideoPoster: function (url) { + if (!url) return ''; + if (String(url).indexOf('x-oss-process=') > -1) return url; + var connector = String(url).indexOf('?') > -1 ? '&' : '?'; + return url + connector + 'x-oss-process=video/snapshot,t_2000,f_jpg,m_fast'; + }, + previewVideo: function (url) { + if (!url) return; + this.playingVideo = url; + this.showPlayer = true; + }, + closePlayer: function () { + this.showPlayer = false; + this.playingVideo = ''; + }, deleteSingleVideo: function () { this.currentValue = ''; this.$emit('input', ''); @@ -110,32 +157,100 @@ export default { position: relative; width: 200rpx; height: 200rpx; + overflow: visible; } .preview-video { width: 200rpx; height: 200rpx; border-radius: 8rpx; + background-color: #f5f5f5; +} + +.play-mask { + position: absolute; + left: 0; + top: 0; + width: 200rpx; + height: 200rpx; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(0, 0, 0, 0.15); + border-radius: 8rpx; + z-index: 2; +} + +.play-circle { + width: 56rpx; + height: 56rpx; + border-radius: 50%; + background-color: rgba(0, 0, 0, 0.55); + display: flex; + align-items: center; + justify-content: center; +} + +.play-icon { + width: 0; + height: 0; + border-top: 14rpx solid transparent; + border-bottom: 14rpx solid transparent; + border-left: 22rpx solid #ffffff; + margin-left: 6rpx; } .delete-btn { position: absolute; top: -10rpx; right: -10rpx; - width: 40rpx; - height: 40rpx; - background-color: #ff4d4f; - color: white; - border-radius: 50%; - display: flex; - justify-content: center; - align-items: center; - font-size: 32rpx; - z-index: 1; + width: 42rpx; + height: 42rpx; + z-index: 5; } -.delete-icon { +.player-mask { + position: fixed; + left: 0; + top: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.82); + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + padding: 32rpx; + box-sizing: border-box; +} + +.player-box { + position: relative; + width: 100%; +} + +.player-video { + width: 100%; + height: 420rpx; + background-color: #000000; +} + +.player-close { + position: absolute; + right: -12rpx; + top: -58rpx; + width: 48rpx; + height: 48rpx; + border-radius: 50%; + background-color: rgba(255, 255, 255, 0.22); + display: flex; + align-items: center; + justify-content: center; +} + +.player-close-text { color: #ffffff; - font-size: 32rpx; + font-size: 42rpx; + line-height: 48rpx; } diff --git a/pages/shop/add-service.vue b/pages/shop/add-service.vue index 9af466f..d7848d8 100644 --- a/pages/shop/add-service.vue +++ b/pages/shop/add-service.vue @@ -12,18 +12,9 @@ - - 服务分类 - - {{ firstClassName }} - - - - - 服务子类 - - {{ secondClassName }} - + + 服务分类: + {{ serviceClassName }} dynamic-form类型 @@ -1171,6 +1165,18 @@ export default { margin-right: 16rpx; } +.service-class-item { + justify-content: flex-start; + align-items: flex-start; +} + +.service-class-value { + flex: 1; + margin-right: 0; + line-height: 40rpx; + word-break: break-all; +} + .placeholder { font-weight: 400; font-size: 28rpx; diff --git a/utils/request.js b/utils/request.js index af48dd0..7fd430b 100644 --- a/utils/request.js +++ b/utils/request.js @@ -120,13 +120,13 @@ const request = async (options, isUpdate) => { if (process.env.NODE_ENV === "development") { baseURL = url; // 发布到生产环境时,此处代码会被摇树移除掉。 - // baseURL = "http://116.63.163.121:93"; // 发布到生产环境时,此处代码会被摇树移除掉。 - baseURL = "http://116.63.163.121:96"; // 发布到生产环境时,此处代码会被摇树移除掉。 + baseURL = "http://116.63.163.121:93"; // 发布到生产环境时,此处代码会被摇树移除掉。 + // baseURL = "http://116.63.163.121:96"; // 发布到生产环境时,此处代码会被摇树移除掉。 // baseURL = "https://app.mrrweb.com.cn"; } else { baseURL = url; // 发布到生产环境时,此处代码会被摇树移除掉。 - // baseURL = "http://116.63.163.121:93"; // 发布到生产环境时,此处代码会被摇树移除掉。 - baseURL = "http://116.63.163.121:96"; // 发布到生产环境时,此处代码会被摇树移除掉。 + baseURL = "http://116.63.163.121:93"; // 发布到生产环境时,此处代码会被摇树移除掉。 + // baseURL = "http://116.63.163.121:96"; // 发布到生产环境时,此处代码会被摇树移除掉。 // baseURL = "https://app.mrrweb.com.cn"; console.log("生产环境");