MS-IOHQJGHXZHQD\Administrator 2 days ago
parent
commit
75020645d2

+ 7 - 2
src/components/cl-upload/cl-upload/cl-upload.vue

@@ -93,7 +93,12 @@
 			<view v-if="add && FileList.length < max" @tap="selectFileTypeOnAdd" :style="[rowStyle]" class="file-list-row">
 				<slot name="addImg">
 					<div class="add-image">
-						<image class="_image" :src="addImg" mode="widthFix"></image>
+						<view>
+							<image class="_image" style="width: 52rpx;height: 52rpx;" :src="addImg" mode="widthFix"></image>
+						  <view style="font-weight: 400;font-size: 26rpx;color: #C9CDD4;margin-top: -13rpx;">上传</view>
+
+						</view>
+						
 					</div>
 				</slot>
 			</view>
@@ -325,7 +330,7 @@ export default {
 			return style;
 		},
 		rowStyle() {
-			const { height = '140rpx', ratio } = this.listStyle || {};
+			const { height = '160rpx', ratio } = this.listStyle || {};
 			const style = {
 				'aspect-ratio': height ? '' : ratio || '1/1', // 图片比例
 				'height': height,

+ 1 - 1
src/components/upload/index.vue

@@ -1,6 +1,6 @@
 <template>
 	<u-upload :name="name" :disabled="disabled" :width="width" :height="height" :accept="accept" :fileList="fileList"
-		:multiple="multiple" :maxCount="maxCount"  previewFullImage="true"
+		:multiple="multiple" :maxCount="maxCount"  :previewFullImage="true"
 		@afterRead="afterRead" @delete="onDelete">
 		<slot name="content" :data="fileList"></slot>
 

+ 111 - 16
src/pages/join/applyJoin.vue

@@ -10,13 +10,26 @@
             </view>
             <uni-list>
                 <uni-list-item title="姓名" :rightText="baseInfo.name" />
-                <uni-list-item title="性别" :rightText="baseInfo.name" />
-                <uni-list-item title="手机号" :rightText="baseInfo.name" />
-                <uni-list-item showArrow title="昵称" :class="{ 'hasText': baseInfo.name }" clickable
+                <uni-list-item title="性别" :rightText="baseInfo.gender" />
+                <uni-list-item title="手机号" :rightText="baseInfo.phone" />
+                <view style="height: 1rpx;background-color: #e5e5e5c2;width: 100%;"></view>
+                <view class="input-box">
+                     
+                    <u-input v-model="baseInfo.teName" border="none" placeholder="昵称" class="input-item"
+                        placeholder-style="font-weight: 400;font-size: 30rpx;color: #C9CDD4;text-align: right;">
+                        <template slot="prefix">
+                            <view class="label">
+                                <text>昵称</text>
+                            </view>
+                        </template>
+                    </u-input>
+                    <!-- <view style="height: 1rpx;background-color: #E7E7E7;"></view> -->
+                </view>
+                <!-- <uni-list-item showArrow title="昵称" :class="{ 'hasText': baseInfo.nickName }" clickable
                     @click="introduceYourselfEdit(baseInfo.introduceYourself)"
-                    :rightText="baseInfo.name ? baseInfo.name : '昵称'" style="color: red!important;" />
+                    :rightText="baseInfo.name ? baseInfo.name : '昵称'" style="color: red!important;" /> -->
 
-                <uni-list-item title="入驻城市" :rightText="baseInfo.name" />
+                <uni-list-item title="入驻城市" :rightText="baseInfo.city" />
                 <uni-list-item showArrow title="简介" :class="{ 'hasText': baseInfo.introduceYourself }" clickable
                     @click="introduceYourselfEdit(baseInfo.introduceYourself)"
                     :rightText="baseInfo.introduceYourself ? baseInfo.introduceYourself : '简单的介绍您自己~'" />
@@ -108,17 +121,17 @@ export default {
         }
     },
     onLoad(options) {
-		console.log(options,'66666')
-		if (options.ShopInfo) {
+        console.log(options, '66666')
+        if (options.ShopInfo) {
             this.ShopInfo = JSON.parse(options.ShopInfo)
-            this.baseInfo.name=this.ShopInfo.teName
-            this.baseInfo.gender=this.ShopInfo.teSex==0?'女':'男'
-            this.baseInfo.phone=this.ShopInfo.tePhone
-            this.baseInfo.nickName=this.ShopInfo.teNickName
-            this.baseInfo.city=this.ShopInfo.teAddress
+            this.baseInfo.name = this.ShopInfo.teName
+            this.baseInfo.gender = this.ShopInfo.teSex == 0 ? '女' : '男'
+            this.baseInfo.phone = this.ShopInfo.tePhone
+            this.baseInfo.nickName = this.ShopInfo.teNickName
+            this.baseInfo.city = this.ShopInfo.teAddress
         }
-		
-	},
+
+    },
     onShow() {
     },
     methods: {
@@ -160,7 +173,7 @@ export default {
                 success: (res) => {
                     if (res && res.tempFilePaths.length > 0) {
                         const filePath = res.tempFilePaths[0]
-                        
+
 
                         // #ifdef MP-WEIXIN
                         // 微信小程序使用uni.cropImage裁剪
@@ -287,7 +300,7 @@ export default {
 ::v-deep .slot-image {
     width: 58px;
     height: 58px;
-    
+
     border-radius: 50%;
 
 }
@@ -299,6 +312,88 @@ export default {
 
 }
 
+.input-box {
+    width: 622rpx;
+    height: 105rpx;
+    background: #ffffff00 !important;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    //border-bottom: 0.2rpx solid #E7E7E7;
+
+
+    .u-input {
+        background: #ffffff00 !important;
+    }
+
+    .label {
+        font-weight: 400;
+        font-size: 30rpx;
+        color: #1D2129;
+        text-align: left;
+    }
+
+    .input-item {
+        flex: 1;
+    }
+
+    .captcha-img {
+        width: 30rpx;
+        height: 30rpx;
+    }
+
+    ::v-deep .uni-input-input {
+        font-weight: 400;
+        font-size: 30rpx;
+        color: #4E5969;
+        text-align: right;
+    }
+
+    .code-input {
+        margin-right: 42rpx;
+        border: 1px solid re;
+
+    }
+
+    .code-btn {
+        font-weight: 400;
+        font-size: 30rpx;
+        color: #19D29B;
+        text-align: center;
+        font-style: normal;
+        text-transform: none;
+    }
+
+    // .name {
+    // 	width: 31%;
+    // 	margin-bottom: 20rpx;
+    // }
+
+    .inp {
+        width: 100%;
+    }
+
+    .select {
+        width: 100%;
+
+        display: flex;
+        align-items: center;
+        justify-content: flex-end;
+
+        .text {
+            font-weight: 400;
+            font-size: 30rpx;
+            text-align: right;
+        }
+
+        img {
+            width: 22rpx;
+            height: 22rpx;
+        }
+    }
+}
+
+
 .applyJoin {
     background-color: #F7F8FA;
     height: 100vh;

+ 243 - 31
src/pages/join/commitDocumentEdit.vue

@@ -21,8 +21,26 @@
             </view>
             <view class="content">
                 <view class="title">承诺录音</view>
-                <view class="tip">请上传 大小不超过 10MB 格式为 mp3/wav/wma 的文件</view>
-                <Upload :accept="audio" :maxCount="1" @fileList="onUpload($event, 'recording')" :fileList="recording">
+                <view class="tip">请上传 大小不超过 10MB 格式为 mp3/wav/wma/m4a 的文件</view>
+                <!-- <view class="recorder-buttons">
+                    <button class="record-btn" :class="{ recording: isRecording }" @click="handleRecord"
+                        :disabled="isRecording">
+                        {{ isRecording ? '录音中...' : '开始录音' }}
+                    </button>
+
+                    <button class="stop-btn" @click="handleStop" :disabled="!isRecording">
+                        停止录音
+                    </button>
+
+                    <button class="play-btn" @click="handlePlay" :disabled="!localId">
+                        播放录音
+                    </button>
+                </view>
+
+                <view v-if="audioDuration" class="audio-info">
+                    录音时长: {{ audioDuration }}秒
+                </view> -->
+                <!-- <Upload accept="audio" :maxCount="1" @fileList="onUpload($event, 'recording')" :fileList="recording">
                     <view class="upload-block" slot="content">
                         <view class="icon-bg">
                             <image src="/static/login/cameraIcon.png" mode="widthFix"
@@ -35,32 +53,13 @@
                     </view>
 
 
-                </Upload>
+                </Upload> -->
             </view>
             <view class="content">
                 <view class="title">承诺录像</view>
                 <view class="tip">请上传 大小不超过 50MB 格式为 mp4/mov/mkv/wmv 的文件</view>
-                <view v-if="videoRecording.length > 0">
-                    <uni-icons class="icon iconfont icon-cuo" type="trash-filled" size="20"
-                        @tap="delectVideo"></uni-icons>
-                    <video ref="myVideo" :src="videoRecording[0].mediaUrl" class="video-container" controls></video>
-                </view>
-                <Upload v-else :accept="'video'" :maxCount="1" @fileList="onUpload($event, 'videoRecording')"
-                    :fileList="videoRecording">
-                    <view class="upload-block" slot="content">
-                        <view class="icon-bg">
-                            <image src="/static/login/cameraIcon.png" mode="widthFix"
-                                style="width: 52rpx;height: 52rpx;">
-                            </image>
-                            <view style="font-weight: 400;font-size: 26rpx;color: #C9CDD4;margin-top: -13rpx;">上传</view>
-
-                        </view>
-
-                    </view>
-
-
-                </Upload>
-                <!-- <view class="media-area">
+                
+                <view class="media-area">
                     <view v-if="formData.videoUrl">
                         <uni-icons class="icon iconfont icon-cuo" type="trash-filled" size="20"
                             @tap="delectVideo"></uni-icons>
@@ -76,12 +75,15 @@
                             count: 9,
                             size: 2,
                         }" :videoFromData="{
-                                count: 1,
-                                size: 50,
-                            }" :maxVideo="1" :fileType="mediaType" addImg="/static/discover/ic_upload.png"
-                        deleteImg="/static/discover/ic_delete.png" @onSuccess="onSuccess"
-                        @onMediaTypeSelect="selectMediaType" @onError="onCancel"></clUpload>
-                </view> -->
+                            count: 1,
+                            size: 50,
+                        }" :maxVideo="1" :fileType="mediaType" addImg="/static/login/cameraIcon.png"
+                        deleteImg="/static/login/cameraIcon.png" @onSuccess="onSuccess"
+                        @onMediaTypeSelect="selectMediaType" @onError="onCancel">
+
+
+                    </clUpload>
+                </view>
             </view>
 
             <view class="footer-bar">
@@ -102,13 +104,36 @@
 <script>
 import clUpload from '@/components/cl-upload/cl-upload/cl-upload.vue'
 import Upload from '@/components/upload/index.vue';
+import { initWechatSDKWithRecorder, wechatRecorder } from '@/utils/wechat';
+import { baseUrl } from '@/common/config.js'
 
 export default {
-    components: { Upload, clUpload },
+    components: { Upload, clUpload, },
     data() {
         return {
+            isRecording: false,
+            localId: null,
+            serverId: null,
+            audioDuration: 0,
+
+            headers: {
+                Authorization: `tf: ${uni.getStorageSync('access-token')}`,
+            },
+            uploadApi: baseUrl + '/common/upload', // 默认使用图片上传接口
             videoUrl: '',
             fileList: [],
+            formData: {
+                title: '',
+                content: '',
+                location: '',
+                address: '',
+                latitude: '',
+                longitude: '',
+                visibleRange: '',
+                videoUrl: '',
+                videoCoverUrl: '',
+                imageUrls: [],
+            },
             mediaType: 'all', // 默认为图片模式,上传第一个文件后确定类型
             uploadApi: baseUrl + '/common/upload', // 默认使用图片上传接口
             fieldName: '',
@@ -117,6 +142,11 @@ export default {
             videoRecording: [],
         }
     },
+    computed: {
+        maxCount() {
+            return this.mediaType === 'video' ? 1 : 9
+        },
+    },
 
     onLoad(query) {
         console.log(query)
@@ -128,6 +158,8 @@ export default {
             this.recording = fieldVal[1] ? fieldVal[1] : []
             this.videoRecording = fieldVal[2] ? fieldVal[2] : []
         }
+        // 初始化微信SDK(包含录音功能)
+        this.initRecorder();
 
         console.log(this.commitment, 'query')
         console.log(this.recording, 'query')
@@ -139,7 +171,162 @@ export default {
     },
     onShow() {
     },
+    beforeUnmount() {
+        this.stopDurationTimer();
+    },
     methods: {
+        async initRecorder() {
+            try {
+                await initWechatSDKWithRecorder();
+
+                // 设置录音事件监听
+                wechatRecorder.on('onStart', () => {
+                    this.isRecording = true;
+                    this.audioDuration = 0;
+                    // 计时器显示录音时长
+                    this.startDurationTimer();
+                });
+
+                wechatRecorder.on('onStop', (res) => {
+                    this.isRecording = false;
+                    this.localId = res.localId;
+                    this.stopDurationTimer();
+                });
+
+                wechatRecorder.on('onUpload', (res) => {
+                    this.serverId = res.serverId;
+                    console.log('录音上传成功:', this.serverId);
+                    // 可以在这里将serverId上传到自己的服务器
+                });
+
+                wechatRecorder.on('onError', (err) => {
+                    console.error('录音错误:', err);
+                    uni.showToast({ title: err.errMsg || '录音失败', icon: 'none' });
+                });
+
+            } catch (err) {
+                console.error('初始化录音失败:', err);
+                uni.showToast({ title: '无法使用录音功能', icon: 'none' });
+            }
+        },
+
+        handleRecord() {
+            if (this.isRecording) return;
+
+            wechatRecorder.startRecording((err, msg) => {
+                if (err) {
+                    uni.showToast({ title: err.errMsg, icon: 'none' });
+                }
+            });
+        },
+
+        handleStop() {
+            if (!this.isRecording) return;
+
+            wechatRecorder.stopRecording((err, res) => {
+                if (err) {
+                    uni.showToast({ title: err.errMsg, icon: 'none' });
+                } else {
+                    this.localId = res.localId;
+                    // 上传录音到微信服务器
+                    this.uploadAudio();
+                }
+            });
+        },
+
+        handlePlay() {
+            if (!this.localId) return;
+
+            wechatRecorder.playVoice(this.localId, (err) => {
+                if (err) {
+                    uni.showToast({ title: err.errMsg, icon: 'none' });
+                }
+            });
+        },
+
+        uploadAudio() {
+            wechatRecorder.uploadVoice(this.localId, (err, res) => {
+                if (err) {
+                    uni.showToast({ title: '上传失败', icon: 'none' });
+                } else {
+                    this.serverId = res.serverId;
+                    uni.showToast({ title: '录音上传成功', icon: 'success' });
+                    // 将serverId发送到后端保存
+                    this.saveRecording(res.serverId);
+                }
+            });
+        },
+
+        saveRecording(serverId) {
+            // 调用后端接口保存录音
+            // this.$api.saveRecording({ serverId }).then(res => {
+            //   console.log('录音保存成功');
+            // });
+        },
+
+        startDurationTimer() {
+            this.durationTimer = setInterval(() => {
+                this.audioDuration++;
+            }, 1000);
+        },
+
+        stopDurationTimer() {
+            if (this.durationTimer) {
+                clearInterval(this.durationTimer);
+                this.durationTimer = null;
+            }
+        },
+
+
+
+
+        onSuccess(reslut) {
+            // 把服务端返回的图片地址添加到list中与组件数据同步
+            if (reslut.code === 200) {
+                const isVideo =
+                    reslut.url.endsWith('.mp4') ||
+                    reslut.url.endsWith('.mov') ||
+                    reslut.url.endsWith('.mkv') ||
+                    reslut.url.endsWith('.wmv')
+                if (isVideo) {
+                    this.formData.videoUrl = baseUrl + reslut.url
+                    this.formData.videoCoverUrl = baseUrl + reslut.coverUrl
+                } else {
+                    this.fileList.push(baseUrl + reslut.url)
+                }
+            } else {
+                this.mediaType = 'all'
+            }
+        },
+        // 确定媒体类型
+        selectMediaType(type) {
+            if (type === 'image') {
+                this.uploadApi = baseUrl + '/common/uploads'
+                this.mediaType = 'image'
+            } else if (type === 'video') {
+                this.uploadApi = baseUrl + '/common/uploadVi'
+                this.mediaType = 'video'
+            }
+        },
+        // 处理媒体类型取消事件
+        onCancel() {
+            this.uploadApi = baseUrl + '/common/uploads'
+            this.mediaType = 'all'
+        },
+        delectVideo() {
+            uni.showModal({
+                title: '提示',
+                content: '是否要删除此视频',
+                success: res => {
+                    if (res.confirm) {
+                        this.formData.videoUrl = ''
+                        this.formData.videoCoverUrl = ''
+                        // 重置媒体类型
+                        this.mediaType = 'all'
+                    }
+                },
+            })
+        },
 
         // upload事件
         onUpload(e, t) {
@@ -206,4 +393,29 @@ export default {
     justify-content: space-between;
     margin-bottom: 20rpx;
 }
+
+::v-deep .file-list-row {
+    width: 160rpx;
+    height: 160rpx;
+    background: #F7F8FA;
+    border-radius: 12rpx;
+
+    .add-image {
+        border: none !important;
+    }
+}
+
+::v-deep .media-area,
+.video-container {
+    width: 160rpx;
+    height: 160rpx;
+
+    .uni-video-cover-duration {
+        font-size: 20rpx;
+        font-weight: 400;
+        position: absolute;
+        right: 8rpx;
+        bottom: 4rpx;
+    }
+}
 </style>

+ 192 - 0
src/utils/wechat.js

@@ -0,0 +1,192 @@
+// src/utils/wechat.js - 微信录音工具
+
+import wx from 'weixin-js-sdk'
+import { getSignature } from "@/api";
+import { h5Url } from '@/common/config.js';
+
+/**
+ * 初始化微信 JS-SDK(包含录音功能)
+ */
+export async function initWechatSDKWithRecorder() {
+  return new Promise((resolve, reject) => {
+    if (typeof wx === 'undefined') {
+      reject(new Error('微信 JS-SDK 未加载'));
+      return;
+    }
+    let url = `${h5Url}/`
+    getSignature({ url:url}).then(res => {
+      const config = res.data;
+      
+      wx.config({
+        debug: false,
+        appId: config.appId,
+        timestamp: config.timestamp,
+        nonceStr: config.nonceStr,
+        signature: config.signature,
+        jsApiList: [
+          // 分享功能
+          'updateAppMessageShareData',
+          'updateTimelineShareData',
+          // 录音功能
+          'startRecord',
+          'stopRecord',
+          'onVoiceRecordEnd',
+          'playVoice',
+          'pauseVoice',
+          'stopVoice',
+          'uploadVoice',
+          'downloadVoice'
+        ]
+      });
+
+      wx.ready(() => {
+        console.log('微信 JS-SDK 初始化成功(含录音)');
+        resolve();
+      });
+
+      wx.error((err) => {
+        console.error('微信 JS-SDK 初始化失败:', err);
+        reject(err);
+      });
+    }).catch(err => {
+      reject(err);
+    });
+  });
+}
+
+/**
+ * 微信录音管理器
+ */
+class WechatRecorder {
+  constructor() {
+    this.isRecording = false;
+    this.localId = null;
+    this.serverId = null;
+    
+    // 事件回调
+    this.callbacks = {
+      onStart: null,
+      onStop: null,
+      onError: null,
+      onUpload: null
+    };
+  }
+
+  /**
+   * 开始录音
+   */
+  startRecording(callback) {
+    if (this.isRecording) {
+      callback && callback({ errMsg: '录音已在进行中' });
+      return;
+    }
+
+    // 设置录音自动结束监听(最长60秒)
+    wx.onVoiceRecordEnd({
+      success: (res) => {
+        this.localId = res.localId;
+        this.isRecording = false;
+        this.callbacks.onStop && this.callbacks.onStop({ localId: this.localId });
+      }
+    });
+
+    wx.startRecord({
+      success: () => {
+        this.isRecording = true;
+        this.localId = null;
+        this.callbacks.onStart && this.callbacks.onStart();
+        callback && callback(null, '开始录音');
+      },
+      cancel: () => {
+        this.isRecording = false;
+        callback && callback({ errMsg: '用户取消录音' });
+      },
+      fail: (err) => {
+        this.isRecording = false;
+        this.callbacks.onError && this.callbacks.onError(err);
+        callback && callback(err);
+      }
+    });
+  }
+
+  /**
+   * 停止录音
+   */
+  stopRecording(callback) {
+    if (!this.isRecording) {
+      callback && callback({ errMsg: '未在录音中' });
+      return;
+    }
+
+    wx.stopRecord({
+      success: (res) => {
+        this.localId = res.localId;
+        this.isRecording = false;
+        this.callbacks.onStop && this.callbacks.onStop({ localId: this.localId });
+        callback && callback(null, { localId: this.localId });
+      },
+      fail: (err) => {
+        this.isRecording = false;
+        this.callbacks.onError && this.callbacks.onError(err);
+        callback && callback(err);
+      }
+    });
+  }
+
+  /**
+   * 播放录音
+   */
+  playVoice(localId, callback) {
+    if (!localId) {
+      callback && callback({ errMsg: '请先录制音频' });
+      return;
+    }
+
+    wx.playVoice({
+      localId: localId,
+      success: () => {
+        callback && callback(null);
+      },
+      fail: (err) => {
+        callback && callback(err);
+      }
+    });
+  }
+
+  /**
+   * 上传录音到微信服务器
+   */
+  uploadVoice(localId, callback) {
+    if (!localId) {
+      callback && callback({ errMsg: '请先录制音频' });
+      return;
+    }
+
+    wx.uploadVoice({
+      localId: localId,
+      isShowProgressTips: 1,
+      success: (res) => {
+        this.serverId = res.serverId;
+        this.callbacks.onUpload && this.callbacks.onUpload({ serverId: this.serverId });
+        callback && callback(null, { serverId: this.serverId });
+      },
+      fail: (err) => {
+        callback && callback(err);
+      }
+    });
+  }
+
+  /**
+   * 设置事件监听
+   */
+  on(event, callback) {
+    if (this.callbacks[event]) {
+      this.callbacks[event] = callback;
+    }
+  }
+}
+
+// 创建单例
+const wechatRecorder = new WechatRecorder();
+
+export { wechatRecorder };