|
|
@@ -0,0 +1,490 @@
|
|
|
+<template>
|
|
|
+ <view class="container">
|
|
|
+ <view class="textbox">
|
|
|
+ <view class="header-tips">请朗读以下文字</view>
|
|
|
+ <view class="textbox-con" :style="{height: textHeight}">{{voiceText || ''}}</view>
|
|
|
+ </view>
|
|
|
+ <view class="voice-footer">
|
|
|
+ <view class="voice-footer-tips">1、选择安静的录音环境,可在房间或车内录音。</view>
|
|
|
+ <view class="voice-footer-tips">2、保持20cm距离,避免手机太远录音不清晰。</view>
|
|
|
+ <view class="voice-footer-tips">3、使用普通话朗读,语速适中,吐字清晰。</view>
|
|
|
+ <view class="voice-footer-btnbox">
|
|
|
+ <view class="tabs" v-if="type!=2">
|
|
|
+ <u-tabs
|
|
|
+ :scrollable="false"
|
|
|
+ :list="tabs"
|
|
|
+ :current="current"
|
|
|
+ lineColor="#FF5C03"
|
|
|
+ @change="tabChange">
|
|
|
+ </u-tabs>
|
|
|
+ <view class="mask" v-if="status=='start'||status=='end'" @click.stop>
|
|
|
+ <view @click.stop="tabclick(0)"></view>
|
|
|
+ <view @click.stop="tabclick(1)"></view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="voice-footer-con">
|
|
|
+ <view class="btnbox-item" :style="{visibility: voicePath ? 'visible' : 'hidden',color: status=='start' ? '#ccc !important':''}">
|
|
|
+ <view class="iconsbox" @click="playVoice" :style="{borderColor: status=='start' ? '#ccc' :isVoicePlay ? 'red':''}">
|
|
|
+ <u-icon name="volume-fill" size="30" :color="status=='start' ? '#ccc' :isVoicePlay ? 'red':'#757575'"></u-icon>
|
|
|
+ <!-- <uni-icons type="sound-filled" size="30" :color="status=='start' ? '#ccc' :isVoicePlay ? 'red':'#757575'"></uni-icons> -->
|
|
|
+ </view>
|
|
|
+ <view>试听录音</view>
|
|
|
+ </view>
|
|
|
+ <view class="btnbox-item">
|
|
|
+ <view class="iconsbox iconsbox-voice" :style="{backgroundColor: status=='end'|| status=='ok' ? 'red':''}" @click="handleRecord">
|
|
|
+ <view v-show="status=='stop'|| status=='end' || status=='ok'">
|
|
|
+ <view v-show="current==1" class="smallcircle-filled"></view>
|
|
|
+ <u-icon v-show="current!=1" name="mic" size="35" color="#fff"></u-icon>
|
|
|
+ </view>
|
|
|
+ <!-- <uni-icons v-show="status=='stop'|| status=='end' || status=='ok'" :type="current==1?'smallcircle-filled':'mic-filled'" size="35" color="#fff"></uni-icons> -->
|
|
|
+ <image v-show="current!=1&&status=='start'" src="https://obs.jnmyunl.com/fs/20250813/1755047925090.png" mode="aspectFill"></image>
|
|
|
+ <view v-show="current==1&&status=='start'"><u-loading-icon color="#fff"></u-loading-icon></view>
|
|
|
+ </view>
|
|
|
+ <view v-show="status=='stop'">{{current==1?'点击生成':'点击录制'}}</view>
|
|
|
+ <view v-show="status=='start'">{{current==1?'生成中':'点击停止'}}</view>
|
|
|
+ <view v-show="status=='end'|| status=='ok'">{{current==1?'重新生成':'重新录制'}}</view>
|
|
|
+ </view>
|
|
|
+ <view class="btnbox-item" :style="{visibility: voicePath&&status!='ok'&¤t!=1 ? 'visible' : 'hidden',color: status=='start' ? '#ccc !important':''}">
|
|
|
+ <button class="iconsbox" :disabled="btnLoading" @click="onSubmit">
|
|
|
+ <u-icon name="checkmark" size="30" :color="status=='start' ? '#ccc' : '#757575'"></u-icon>
|
|
|
+ <!-- <uni-icons type="checkmarkempty" size="30" :color="status=='start' ? '#ccc' : '#757575'"></uni-icons> -->
|
|
|
+ </button>
|
|
|
+ <view>提交</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import {companyUserVoiceNew,companyUserVoice,queryDetail} from '@/api/companyUser'
|
|
|
+ let innerAudioContext = null
|
|
|
+ export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ recorderManager: null,
|
|
|
+ // innerAudioContext: null,
|
|
|
+ textHeight: '',
|
|
|
+ statusBarHeight: uni.getSystemInfoSync().statusBarHeight + 'px',
|
|
|
+ screenHeight: uni.getSystemInfoSync().windowHeight + 'px',
|
|
|
+ voicePath: '',
|
|
|
+ status: "stop",
|
|
|
+ isVoicePlay: false,
|
|
|
+ btnLoading: false,
|
|
|
+ tabs:[
|
|
|
+ {
|
|
|
+ id:1,
|
|
|
+ name:'自己录制'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id:2,
|
|
|
+ name:'AI生成'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ current: 0,
|
|
|
+ id: null,
|
|
|
+ voiceText: '',
|
|
|
+ recordType: 0, // 是否已采集
|
|
|
+ type: 0, // 2表示只能自己录制
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onLoad(option) {
|
|
|
+ this.type = option.type || 0
|
|
|
+ this.recordType = option.recordType || 0
|
|
|
+ this.id = option.id || null
|
|
|
+ this.getDetail()
|
|
|
+ this.initDate()
|
|
|
+ },
|
|
|
+ onHide() {
|
|
|
+ if(innerAudioContext){
|
|
|
+ innerAudioContext.pause();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onUnload() {
|
|
|
+ this.recorderManager = null
|
|
|
+ if(innerAudioContext) {
|
|
|
+ innerAudioContext.destroy()
|
|
|
+ innerAudioContext = null
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ tabclick(type) {
|
|
|
+ if(type == this.current) return
|
|
|
+ if(this.btnLoading || this.status == 'start') {
|
|
|
+ uni.showToast({
|
|
|
+ title: '生成中,请勿进行其他操作!',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ } else if(this.status == 'end') {
|
|
|
+ const that = this
|
|
|
+ uni.showModal({
|
|
|
+ title: '提示',
|
|
|
+ content: '当亲录制已完成,切换类型需要重新录制,确认切换吗?',
|
|
|
+ success: function (res) {
|
|
|
+ if (res.confirm) {
|
|
|
+ const item = {
|
|
|
+ index: type,
|
|
|
+ ...that.tabs[type]
|
|
|
+ }
|
|
|
+ that.tabChange(item)
|
|
|
+ } else if (res.cancel) {
|
|
|
+ console.log('用户点击取消');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ tabChange(item){
|
|
|
+ console.log("tabChange")
|
|
|
+ if(this.current == item.index) return
|
|
|
+ if(this.btnLoading || this.status == 'start') {
|
|
|
+ uni.showToast({
|
|
|
+ title: '生成中,请勿进行其他操作!',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ this.current = item.index
|
|
|
+ this.recorderManager = null
|
|
|
+ if(innerAudioContext) {
|
|
|
+ innerAudioContext.stop();
|
|
|
+ innerAudioContext.destroy()
|
|
|
+ innerAudioContext = null
|
|
|
+ }
|
|
|
+ this.voicePath = this.recordType == 1 ? this.voicePath : ''
|
|
|
+ this.status = this.recordType == 1 ? this.status : "stop"
|
|
|
+ this.isVoicePlay = false
|
|
|
+ this.btnLoading = false
|
|
|
+ this.initDate()
|
|
|
+ },
|
|
|
+ initDate() {
|
|
|
+ // #ifndef H5
|
|
|
+ this.recorderManager = uni.getRecorderManager();
|
|
|
+ innerAudioContext = uni.createInnerAudioContext();
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ let self = this;
|
|
|
+ if(this.recorderManager) {
|
|
|
+ this.recorderManager.onStart(()=>{
|
|
|
+ this.status = 'start'
|
|
|
+ this.voicePath = ''
|
|
|
+ })
|
|
|
+ this.recorderManager.onStop((res)=> {
|
|
|
+ // console.log('recorder stop' + JSON.stringify(res));
|
|
|
+ this.status = 'end'
|
|
|
+ self.voicePath = res.tempFilePath;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if(innerAudioContext) {
|
|
|
+ // innerAudioContext.autoplay = true;
|
|
|
+ innerAudioContext.onPlay(() => {
|
|
|
+ // console.log('开始播放');
|
|
|
+ this.isVoicePlay = true
|
|
|
+ });
|
|
|
+ innerAudioContext.onStop(() => {
|
|
|
+ // console.log('停止播放');
|
|
|
+ this.isVoicePlay = false
|
|
|
+ });
|
|
|
+ innerAudioContext.onEnded(() => {
|
|
|
+ // console.log('播放结束');
|
|
|
+ this.isVoicePlay = false
|
|
|
+ });
|
|
|
+ innerAudioContext.onError((res) => {
|
|
|
+ this.isVoicePlay = false
|
|
|
+ });
|
|
|
+ innerAudioContext.onPause(() => {
|
|
|
+ // console.log('暂停播放');
|
|
|
+ this.isVoicePlay = false
|
|
|
+ });
|
|
|
+ }
|
|
|
+ // #endif
|
|
|
+ },
|
|
|
+ handleRecord() {
|
|
|
+ if(innerAudioContext){
|
|
|
+ innerAudioContext.pause();
|
|
|
+ }
|
|
|
+ if(this.current == 1) {
|
|
|
+ if(this.status == 'stop') {
|
|
|
+ this.creatVoice()
|
|
|
+ } else if(this.status == 'start') {
|
|
|
+ uni.showToast({
|
|
|
+ title: '生成中,请勿进行其他操作!',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ } else if(this.status == 'end'||this.status == 'ok') {
|
|
|
+ this.creatVoice()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if(this.status == 'stop') {
|
|
|
+ this.startRecord()
|
|
|
+ } else if(this.status == 'start') {
|
|
|
+ this.endRecord()
|
|
|
+ } else if(this.status == 'end'||this.status == 'ok') {
|
|
|
+ this.startRecord()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ startRecord() {
|
|
|
+ // console.log('开始录音');
|
|
|
+ this.recorderManager.start({
|
|
|
+ format: 'mp3',
|
|
|
+ });
|
|
|
+ },
|
|
|
+ endRecord() {
|
|
|
+ // console.log('录音结束');
|
|
|
+ this.recorderManager.stop();
|
|
|
+ },
|
|
|
+ playVoice() {
|
|
|
+ if(this.status == "start") return
|
|
|
+ if (this.voicePath) {
|
|
|
+ if(this.isVoicePlay == false) {
|
|
|
+ innerAudioContext.src = this.voicePath;
|
|
|
+ innerAudioContext.play();
|
|
|
+ } else {
|
|
|
+ innerAudioContext.stop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onSubmit() {
|
|
|
+ if(this.status == "start") return
|
|
|
+ this.btnLoading = true
|
|
|
+ uni.showLoading({
|
|
|
+ title:"提交中..."
|
|
|
+ })
|
|
|
+ if(this.current == 1) {
|
|
|
+ this.creatVoice()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ uni.uploadFile({
|
|
|
+ url: uni.getStorageSync('requestPath')+'/app/common/uploadOSS', //仅为示例,非真实的接口地址
|
|
|
+ filePath: this.voicePath,
|
|
|
+ name: 'file',
|
|
|
+ success: (uploadFileRes) => {
|
|
|
+ console.log("companyUserVoiceNew==",JSON.parse(uploadFileRes.data).url)
|
|
|
+ let voicePrintUrl = JSON.parse(uploadFileRes.data).url
|
|
|
+ companyUserVoiceNew({userVoiceUrl: voicePrintUrl,id:this.id,companyUserId:uni.getStorageSync('CompanyUserInfoId')}).then(res=>{
|
|
|
+ uni.hideLoading()
|
|
|
+ this.btnLoading = false
|
|
|
+ if(res.code==200){
|
|
|
+ uni.showToast({
|
|
|
+ icon:'none',
|
|
|
+ title: '提交成功',
|
|
|
+ });
|
|
|
+ uni.$emit('refreshVoiceList')
|
|
|
+ setTimeout(()=>{
|
|
|
+ uni.navigateBack()
|
|
|
+ },2000)
|
|
|
+ }else{
|
|
|
+ uni.showToast({
|
|
|
+ icon:'none',
|
|
|
+ title: res.msg,
|
|
|
+ duration: 2000
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }).catch(()=>{
|
|
|
+ uni.hideLoading()
|
|
|
+ this.btnLoading = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ fail: ()=>{
|
|
|
+ uni.hideLoading()
|
|
|
+ this.btnLoading = false
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ getDetail() {
|
|
|
+ queryDetail(this.id).then(res=>{
|
|
|
+ if(res.code==200){
|
|
|
+ this.voiceText = res.data.voiceTxt
|
|
|
+ this.recordType = res.data.recordType
|
|
|
+ if(this.recordType == 1) {
|
|
|
+ this.voicePath = res.data.wavUrl||''
|
|
|
+ this.status = 'ok'
|
|
|
+ if(this.voicePath&&this.voicePath.match(/\.mp3$/)) {
|
|
|
+ this.current = 0
|
|
|
+ } else {
|
|
|
+ this.current = 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.$nextTick(()=>{
|
|
|
+ const query = uni.createSelectorQuery().in(this);
|
|
|
+ query
|
|
|
+ .select(".voice-footer")
|
|
|
+ .boundingClientRect((data) => {
|
|
|
+ this.textHeight = `calc(${this.screenHeight} - ${data.height}px - ${this.statusBarHeight} - 45px - ${uni.upx2px(120)}px)`
|
|
|
+ })
|
|
|
+ .exec();
|
|
|
+ })
|
|
|
+ }else{
|
|
|
+ uni.showToast({
|
|
|
+ icon:'none',
|
|
|
+ title: res.msg,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 智能生成
|
|
|
+ creatVoice() {
|
|
|
+ if(this.status == "start") return
|
|
|
+ this.btnLoading = true
|
|
|
+ this.status = "start"
|
|
|
+ this.voicePath = ''
|
|
|
+ uni.showLoading({
|
|
|
+ title:"生成中..."
|
|
|
+ })
|
|
|
+ companyUserVoice({
|
|
|
+ id: this.id,
|
|
|
+ companyUserId:uni.getStorageSync('CompanyUserInfoId')
|
|
|
+ }).then(res=>{
|
|
|
+ uni.hideLoading()
|
|
|
+ this.btnLoading = false
|
|
|
+ if(res.code==200){
|
|
|
+ this.status = "end"
|
|
|
+ this.voicePath = res.data.wavUrl
|
|
|
+ uni.showToast({
|
|
|
+ icon:'none',
|
|
|
+ title: '生成成功',
|
|
|
+ });
|
|
|
+ uni.$emit('refreshVoiceList')
|
|
|
+ setTimeout(()=>{
|
|
|
+ uni.navigateBack()
|
|
|
+ },2000)
|
|
|
+ }else{
|
|
|
+ this.status = "stop"
|
|
|
+ uni.showToast({
|
|
|
+ icon:'none',
|
|
|
+ title: res.msg,
|
|
|
+ duration: 2000
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }).catch(()=>{
|
|
|
+ uni.hideLoading()
|
|
|
+ this.btnLoading = false
|
|
|
+ this.status = "stop"
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+ @mixin u-flex($flexD, $alignI, $justifyC) {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: $flexD;
|
|
|
+ align-items: $alignI;
|
|
|
+ justify-content: $justifyC;
|
|
|
+ }
|
|
|
+ .smallcircle-filled {
|
|
|
+ background-color: #fff;
|
|
|
+ width: 70rpx;
|
|
|
+ height: 70rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ }
|
|
|
+ .tabs {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ .mask {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ z-index: 3;
|
|
|
+ @include u-flex(row,flex-start,flex-start);
|
|
|
+ view {
|
|
|
+ flex: 1;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .container {
|
|
|
+ position: relative;
|
|
|
+ .header-tips {
|
|
|
+ padding-bottom: 24rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ font-family: PingFang SC, PingFang SC;
|
|
|
+ font-weight: 400;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #757575;
|
|
|
+ // color: $mainThemeHColor;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ .textbox {
|
|
|
+ padding: 24rpx 50rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ &-con {
|
|
|
+ padding: 32rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 16rpx 16rpx 16rpx 16rpx;
|
|
|
+ font-family: PingFang SC, PingFang SC;
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 40rpx;
|
|
|
+ color: #222222;
|
|
|
+ overflow-y: auto;
|
|
|
+ line-height: 60rpx;
|
|
|
+ letter-spacing: 4rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .voice-footer {
|
|
|
+ position: fixed;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ box-sizing: border-box;
|
|
|
+ &-tips {
|
|
|
+ padding: 0 50rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ font-family: PingFang SC, PingFang SC;
|
|
|
+ font-weight: 400;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #757575;
|
|
|
+ margin-bottom: 10rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .voice-footer-btnbox {
|
|
|
+ margin-top: 100rpx;
|
|
|
+ background-color: #fff;
|
|
|
+ text-align: center;
|
|
|
+ font-family: PingFang SC, PingFang SC;
|
|
|
+ font-weight: 400;
|
|
|
+ font-size: 30rpx;
|
|
|
+ color: #333;
|
|
|
+ .voice-footer-con {
|
|
|
+ padding: 50rpx 24rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ @include u-flex(row,flex-end,space-evenly);
|
|
|
+ }
|
|
|
+ .iconsbox {
|
|
|
+ height: 110rpx;
|
|
|
+ width: 110rpx;
|
|
|
+ margin: 0;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ @include u-flex(row,center,center);
|
|
|
+ border-radius: 50%;
|
|
|
+ background-color: #fff;
|
|
|
+ border: 1rpx solid #CCCCCC;
|
|
|
+ &::after {
|
|
|
+ border: none;
|
|
|
+ }
|
|
|
+ &-voice {
|
|
|
+ height: 150rpx;
|
|
|
+ width: 150rpx;
|
|
|
+ background-color: #FF5C03;
|
|
|
+ border: 1rpx solid #FF5C03;
|
|
|
+ image {
|
|
|
+ height: 60rpx;
|
|
|
+ width: 60rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .btnbox-item {
|
|
|
+ flex: 1;
|
|
|
+ @include u-flex(column,center,center);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+</style>
|