Files
dsProject/dsApp/uni_modules/all-speech/components/all-speech/all-speech.vue

390 lines
9.9 KiB
Vue
Raw Normal View History

2025-08-14 15:45:08 +08:00
<template>
<view>
<!-- #ifdef APP-PLUS ||MP-WEIXIN -->
<view class="record-btn" @longpress="startRecord" @touchend="endRecord" hover-class="record-btn-hover"
hover-start-time="200" hover-stay-time="150" :style="[
btnStyle,
{
'--btn-hover-fontcolor': btnHoverFontcolor,
'--btn-hover-bgcolor': btnHoverBgcolor
}
]">
{{ btnTextContent }}
</view>
<!-- #endif -->
<view class="record-popup" :style="{
'--popup-height': popupHeight,
'--popup-width': upx2px(popupMaxWidth),
'--popup-bottom': upx2px(popupFixBottom),
'--popup-bg-color': popupBgColor
}">
<view class="inner-content" v-if="recordPopupShow">
<view class="title">{{ popupTitle }}</view>
<view class="voice-line-wrap" v-if="recording" :style="{
'--line-height': upx2px(lineHeight),
'--line-start-color': lineStartColor,
'--line-end-color': lineEndColor
}">
<view class="voice-line one"></view>
<view class="voice-line two"></view>
<view class="voice-line three"></view>
<view class="voice-line four"></view>
<view class="voice-line five"></view>
<view class="voice-line six"></view>
<view class="voice-line seven"></view>
<view class="voice-line six"></view>
<view class="voice-line five"></view>
<view class="voice-line four"></view>
<view class="voice-line three"></view>
<view class="voice-line two"></view>
<view class="voice-line one"></view>
</view>
<view class="cancel-icon" v-if="!recording">+</view>
<view class="tips">{{ recording ? popupDefaultTips : popupCancelTips }}</view>
</view>
</view>
</view>
</template>
<script>
var that;
// #ifdef APP-PLUS || MP-WEIXIN
const recorderManager = uni.getRecorderManager();
// #endif
// #ifdef APP-PLUS
// 引入权限判断
import permision from '../../js_sdk/wa-permission/permission.js';
// #endif
export default {
name: 'nbVoiceRecord',
/**
* 录音交互动效组件
* @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: '#409EFF',
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: false
},
// #ifdef APP-PLUS || MP-WEIXIN
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进制色值
}
},
data() {
return {
stopStatus: true, // 是否已被父页面通知主动结束录音
btnTextContent: this.btnDefaultText,
startTouchData: {},
popupHeight: '0px', // 这是初始的高度
recording: true, // 录音中
recordPopupShow: false,
recordTimeout: null, // 录音定时器
h5start: false
};
},
created() {
// 请求权限
this.checkPermission();
},
computed: {},
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 MP-WEIXIN
uni.authorize({
scope: 'scope.record',
success(e) {
that.permisionState = true;
// that.startRecord();
},
fail() {
uni.showToast({
title: '请授权使用录音',
icon: 'none'
});
}
});
// #endif
},
startRecord() {
if (!this.permisionState) {
this.checkPermission();
return;
}
this.stopStatus = false;
setTimeout(() => {
this.popupHeight = this.upx2px(this.popupMaxHeight);
setTimeout(() => {
this.recordPopupShow = true;
this.btnTextContent = this.btnRecordingText;
// 开始录音
// recorderManager.start(this.recordOptions);
// 设置定时器
this.recordTimeout = setTimeout(
() => {
// 如果定时器先结束,则说明此时录音时间超限
this.stopRecord(); // 结束录音动画实际上录音的end回调已经先执行
this.recordTimeout = null; // 重置
},
this.recordOptions.duration ? this.recordOptions.duration : 600000
);
this.$emit('startRecord');
}, 100);
}, 200);
},
endRecord() {
var that = this;
if (this.stopStatus) {
return;
}
this.popupHeight = '0px';
this.recordPopupShow = false;
this.btnTextContent = this.btnDefaultText;
this.$emit('endRecord');
},
stopRecord() {
// 用法如你录音限制了时间,那么将在结束时强制停止组件的显示
this.endRecord();
this.stopStatus = true;
}
}
};
</script>
<style lang="scss">
.record-btn {
color: #fff;
font-size: 24rpx;
display: flex;
align-items: center;
justify-content: center;
transition: 0.25s all;
border: 1rpx solid whitesmoke;
}
.record-btn-hover {
color: var(--btn-hover-fontcolor) !important;
background-color: var(--btn-hover-bgcolor) !important;
}
.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;
}
}
}
.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>