项目初始化

This commit is contained in:
jerry
2025-01-21 01:46:34 +08:00
parent 364021b042
commit 48153e7761
962 changed files with 172070 additions and 0 deletions

View File

@@ -0,0 +1,652 @@
<template>
<view>
<view class="cbb-record">
<view class="conbox record">
<!-- 此处可放置倒计时可根据需要自行添加 -->
<view class="time">
{{showRecordTime}}
</view>
<view class="c999">
最短{{minTime}}最长{{maxTime}}
</view>
<view class="record-box">
<view class="stop" @tap.stop="stopVoice" v-if="_voicePath && playing==1 && recordTime1 > minTime-1">
<u-icon color="#3cc9a4" name="pause-circle" size="80"></u-icon>
</view>
<view class="paly" @tap.stop="playVoice" v-if="_voicePath && playing==0 && recordTime1 > minTime-1">
<u-icon color="#3cc9a4" name="play-circle" size="80"></u-icon>
</view>
<!-- #ifdef APP-PLUS ||MP-WEIXIN -->
<view class="circle-box"
@longpress="startRecord"
@touchend="endRecord">
<view class="voice-btn">
<view class="progress-icon">
<u-icon name="mic" color="#fff" size="70"></u-icon>
</view>
</view>
<!-- <u-circle-progress width="170" active-color="#2979ff" :percent="percent" :duration="duration">
<view class="u-progress-content">
<view class="u-progress-dot">
<u-icon name="mic" size="60"></u-icon>
</view>
</view>
</u-circle-progress> -->
</view>
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="circle-box" @click="startRecord">
<view class="voice-btn">
<view class="progress-icon">
<u-icon name="mic" color="#fff" size="70"></u-icon>
</view>
</view>
<!-- <u-circle-progress width="170" active-color="#2979ff" :percent="percent" :duration="duration">
<view class="u-progress-content">
<view class="u-progress-dot">
<u-icon name="mic" size="60"></u-icon>
</view>
</view>
</u-circle-progress> -->
</view>
<!-- #endif -->
<view class="confirm" @tap.stop="okClick" v-if="_voicePath && recordTime1 > minTime-1">
<u-icon color="#3cc9a4" name="checkmark-circle" size="80"></u-icon>
</view>
</view>
<view class="c666 fz32 domess">{{ btnTextContent }}</view>
</view>
</view>
</view>
</template>
<script>
var that;
var innerAudioContext;//播放
// #ifdef APP-PLUS || MP-WEIXIN
const recorderManager = uni.getRecorderManager();
// #endif
// #ifdef H5
import speech from '../../js_sdk/h5-speech/speech.js';
const recorderManager = new speech(8000);
// #endif
// #ifdef APP-PLUS
// 引入权限判断
import permision from '../../js_sdk/wa-permission/permission.js';
// #endif
import tuiCircularProgress from "@/components/thorui/tui-circular-progress/tui-circular-progress.vue"
export default {
name: 'nbVoiceRecord',
components:{
tuiCircularProgress,
},
/**
* 录音交互动效组件
* @property {Object} recordOptions 录音配置
* @property {Object} btnStyle 按钮样式
* @property {Object} btnHoverFontcolor 按钮长按时字体颜色
* @property {String} btnHoverBgcolor 按钮长按时背景颜色
* @property {String} btnDefaultText 按钮初始文字
* @property {String} btnRecordingText 录制时按钮文字
* @property {Boolean} vibrate 弹窗时是否震动
* @property {String} popupTitle 弹窗顶部文字
* @property {String} popupDefaultTips 录制时弹窗底部提示
* @property {String} popupCancelTips 滑动取消时弹窗底部提示
* @property {String} popupMaxWidth 弹窗展开后宽度
* @property {String} popupMaxHeight 弹窗展开后高度
* @property {String} popupFixBottom 弹窗展开后距底部高度
* @property {String} popupBgColor 弹窗背景颜色
* @property {String} lineHeight 声波高度
* @property {String} lineStartColor 声波波谷时颜色色值
* @property {String} lineEndColor 声波波峰时颜色色值
* @event {Function} startRecord 开始录音回调
* @event {Function} endRecord 结束录音回调
* @event {Function} cancelRecord 滑动取消录音回调
* @event {Function} stopRecord 主动停止录音
*/
props: {
recordOptions: {
type: Object,
default() {
return {
duration: 600000
}; // 请自行查看各端的的支持情况,这里全部使用默认配置
}
},
btnStyle: {
type: Object,
default() {
return {
width: '300rpx',
height: '80rpx',
borderRadius: '20rpx',
backgroundColor: '#fff',
border: '1rpx solid whitesmoke',
permisionState: false
};
}
},
btnHoverFontcolor: {
type: String,
default: '#000' // 颜色名称或16进制色值
},
btnHoverBgcolor: {
type: String,
default: 'whitesmoke' // 颜色名称或16进制色值
},
btnDefaultText: {
type: String,
default: '长按开始录音'
},
btnRecordingText: {
type: String,
default: '录音中'
},
vibrate: {
type: Boolean,
default: true
},
// #ifdef APP-PLUS || MP-WEIXIN
popupTitle: {
type: String,
default: '正在录制音频'
},
popupDefaultTips: {
type: String,
default: '左右滑动后松手完成录音'
},
// #endif
// #ifdef H5
popupTitle: {
type: String,
default: '点击录制音频'
},
popupDefaultTips: {
type: String,
default: '点击完成录音'
},
// #endif
popupCancelTips: {
type: String,
default: '松手取消录音'
},
popupMaxWidth: {
type: Number,
default: 600 // 单位为rpx
},
popupMaxHeight: {
type: Number,
default: 300 // 单位为rpx
},
popupFixBottom: {
type: Number,
default: 200 // 单位为rpx
},
popupBgColor: {
type: String,
default: 'whitesmoke'
},
lineHeight: {
type: Number,
default: 50 // 单位为rpx
},
lineStartColor: {
type: String,
default: 'royalblue' // 颜色名称或16进制色值
},
lineEndColor: {
type: String,
default: 'indianred' // 颜色名称或16进制色值
},
voicePath: { //默认地址
type: String,
default: ''
},
maxTime: { // 录音最大时长,单位秒
type: Number,
default: 15
},
minTime: { // 录音最小时长,单位毫秒
type:Number ,
default: 5
},
},
data() {
return {
stopStatus: true, // 是否已被父页面通知主动结束录音
btnTextContent: this.btnDefaultText,
startTouchData: {},
popupHeight: '0px', // 这是初始的高度
recording: true, // 录音中
recordPopupShow: false,
recordTimeout: null, // 录音定时器
h5start: false,
isShow:false,
playing:0,//是否播放中
timeObj: null, //计时id
countdownObj: null, //倒计时id
recordTime: 0,//录音时长
recordTime1:0,//播放录音倒计时
percent: 0,
duration1: true,
newViocePath: '',
};
},
created() {
that = this;
innerAudioContext = uni.createInnerAudioContext();//播放
// 请求权限
this.checkPermission();
// #ifdef APP-PLUS || MP-WEIXIN
recorderManager.onStop((res) => {
that.newViocePath = res.tempFilePath;
that.endRecord();
that.$emit('endRecord', res);
});
recorderManager.onStart((err) => {
console.log('开始:', err);
});
recorderManager.onError((err) => {
console.log('err:', err);
});
// #endif
},
computed: {
showRecordTime() {
var strs = "";
var m = Math.floor(this.recordTime/60);
if(m<10) strs = "0"+m;
var s = this.recordTime%60;
strs += (s<10) ? ":0"+s : ":"+s;
return strs
},
duration() {
return this.duration1 ? this.maxTime*1000 : 0;
},
_voicePath(){
return this.newViocePath || this.voicePath;
}
},
methods: {
upx2px(upx) {
return uni.upx2px(upx) + 'px';
},
async checkPermission() {
var that = this;
// #ifdef APP-PLUS
// 先判断os
let os = uni.getSystemInfoSync().osName;
if (os == 'ios') {
this.permisionState = await permision.judgeIosPermission('record');
} else {
this.permisionState = await permision.requestAndroidPermission('android.permission.RECORD_AUDIO');
}
if (this.permisionState !== true && this.permisionState !== 1) {
uni.showToast({
title: '请先授权使用录音',
icon: 'none'
});
return;
}
// #endif
// #ifdef H5
if (!window.navigator?.mediaDevices?.getUserMedia) {
this.permisionState = false;
uni.showToast({
title: '请先授权使用录音',
icon: 'none'
});
return;
} else {
this.permisionState = true;
}
// #endif
// #ifdef MP-WEIXIN
uni.authorize({
scope: 'scope.record',
success(e) {
that.permisionState = true;
// that.startRecord();
},
fail() {
uni.showToast({
title: '请授权使用录音',
icon: 'none'
});
}
});
// #endif
},
startRecord() {
this.percent = 0;
this.duration1 = true;
if (!this.permisionState) {
this.checkPermission();
return;
}
if (this.h5start) {
this.duration1 = false;
this.h5start = false;
this.endRecord();
return;
}
this.h5start = true;
this.stopStatus = false;
this.stopVoice();
this.percent = 100;
this.recordTime = 0;
this.newViocePath = "";//音频地址
this.btnTextContent = this.btnRecordingText;
this.timeObj = setInterval(() => {
this.recordTime ++;
if(this.recordTime == this.maxTime) {
this.endRecord();
}
},1000);
setTimeout(() => {
setTimeout(() => {
if (this.vibrate) {
// #ifdef APP-PLUS
// 震动
plus.device.vibrate(35);
// #endif
// #ifdef MP-WEIXIN
uni.vibrateShort();
// #endif
}
// 开始录音
recorderManager.start(this.recordOptions);
this.$emit('startRecord');
}, 100);
}, 200);
},
endRecord() {
this.percent = 0;
this.h5start = false;
let recordTime = this.recordTime;
this.recordTime1 = this.recordTime;
clearInterval(this.timeObj); //清除计时器
var that = this;
if (this.stopStatus) {
return;
}
//this.popupHeight = '0px';
//this.recordPopupShow = false;
this.btnTextContent = this.btnDefaultText;
// #ifdef APP-PLUS || MP-WEIXIN
recorderManager.stop();
// #endif
// #ifdef H5
const res = recorderManager.stop();
that.newViocePath = res;
that.$emit('endRecord', res);
// #endif
},
stopRecord() {
// 用法如你录音限制了时间,那么将在结束时强制停止组件的显示
this.endRecord();
this.stopStatus = true;
},
touchStart(e) {
this.startTouchData.clientX = e.changedTouches[0].clientX; //手指按下时的X坐标
this.startTouchData.clientY = e.changedTouches[0].clientY; //手指按下时的Y坐标
},
touchMove(e) {
let touchData = e.touches[0]; //滑动过程中,手指滑动的坐标信息 返回的是Objcet对象
let moveX = touchData.clientX - this.startTouchData.clientX;
let moveY = touchData.clientY - this.startTouchData.clientY;
if (moveY < -50) {
if (this.vibrate && this.recording) {
// #ifdef APP-PLUS
plus.device.vibrate(35);
// #endif
// #ifdef MP-WEIXIN
uni.vibrateShort();
// #endif
}
this.recording = false;
} else {
this.recording = true;
}
},
playVoice() {
if (this._voicePath && this.playing === 0) {
console.log('playVoice', this._voicePath)
innerAudioContext.src = this._voicePath;
innerAudioContext.stop(); //todo 第一次play时若不先stop则播放不出来,未知原因
innerAudioContext.play();
this.playing = 1;
this.recordTime = this.recordTime1;
this.countdownObj = setInterval(() => {
this.recordTime--;
if(this.recordTime === 0){
this.recordTime = this.recordTime1;
this.stopVoice()
return;
}
}, 1000)
}
},
startVoice() {
this.isShow = true;
this.percent = 0;
this.recordTime = 0;
this.newViocePath = "";//音频地址
},
//关闭组件
closePicker(){
this.isShow = false;
this.endRecord();
this.stopVoice();
},
stopVoice() {
innerAudioContext.stop();
this.playing = 0;
//this.recordTime = 0;
clearInterval(this.countdownObj);
},
//点击确定
okClick(){
var data = {
path: this._voicePath,
sec: this.recordTime1,
}
this.stopVoice();
this.$emit('okClick', data);
},
}
};
</script>
<style lang="scss">
.cbb-record {
.conbox{
background: #fff;
}
.record{
text-align: center;
.time {
text-align: center;
font-size: 60upx;
color: #000;
line-height: 100upx;
//margin-top:50upx;
}
.domess{margin-bottom:50upx;}
.c666{color:#aaa;}
.c999{color:#999;}
.fz28{font-size: 28upx;}
.fz32{font-size: 32upx;}
.record-box {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 10px 0;
}
.btncom{
width: 80upx;
height: 80upx;
border-radius: 80upx;
}
.stop{
@extend .btncom;
}
.paly{
@extend .btncom;
}
.confirm{
@extend .btncom;
}
}
.circle-box {
margin: 0 80rpx;
}
}
.record-popup {
position: absolute;
bottom: var(--popup-bottom);
left: calc(50vw - calc(var(--popup-width) / 2));
z-index: 1;
width: var(--popup-width);
height: var(--popup-height);
display: flex;
align-items: center;
justify-content: center;
border-radius: 10rpx;
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
background: var(--popup-bg-color);
color: #000;
transition: 0.2s height;
.inner-content {
height: var(--popup-height);
font-size: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
.title {
font-weight: bold;
padding: 20rpx 0;
}
.tips {
color: #999;
padding: 20rpx 0;
}
}
}
.voice-btn {
width: 170rpx;
height: 170rpx;
}
.progress-icon {
display: flex;
justify-content: center;
align-items: center;
background-color: #3cc9a4;
border-radius: 100%;
width: 100%;
height: 100%;
}
.cancel-icon {
width: 100rpx;
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 44rpx;
line-height: 44rpx;
background-color: pink;
border-radius: 50%;
transform: rotate(45deg);
}
.voice-line-wrap {
display: flex;
align-items: center;
.voice-line {
width: 5rpx;
height: var(--line-height);
border-radius: 3rpx;
margin: 0 5rpx;
}
.one {
animation: wave 0.4s 1s linear infinite alternate;
}
.two {
animation: wave 0.4s 0.9s linear infinite alternate;
}
.three {
animation: wave 0.4s 0.8s linear infinite alternate;
}
.four {
animation: wave 0.4s 0.7s linear infinite alternate;
}
.five {
animation: wave 0.4s 0.6s linear infinite alternate;
}
.six {
animation: wave 0.4s 0.5s linear infinite alternate;
}
.seven {
animation: wave 0.4s linear infinite alternate;
}
}
@keyframes wave {
0% {
transform: scale(1, 1);
background-color: var(--line-start-color);
}
100% {
transform: scale(1, 0.2);
background-color: var(--line-end-color);
}
}
</style>