express+vue在线im实现【四】

news2024/11/24 18:58:45

往期内容

express+vue在线im实现【一】
express+vue在线im实现【二】
express+vue在线im实现【三】

本期示例

在这里插入图片描述
在这里插入图片描述

本期总结

  • 支持了音频的录制和发送,如果觉得对你有用,还请点个免费的收藏与关注

下期安排

  • 在线语音

具体实现

<template>
    <kl-dialog width="300px" center :header="false" :footer="false" :dialogVisible.sync="visable">
        <div class="flex-column-wrap p-20 flex-center-wrap pr p-t-40" @click.stop="() => {}">
            <i
                class="f-20 f-600 c-555 cu el-icon-close p-a el-icon-close-1"
                @click.stop="close"
            ></i>
            <e-image
                :height="80"
                :lazy="false"
                src="http://139.9.210.43:5000/netdist/kl1718850348458vjab00h8x4d-1718850348280~1~.png"
            ></e-image>

            <!-- 录制时长 -->
            <div class="m-t-20">录制时长{{ getAudioTime() }}</div>

            <div class="flex-wrap m-t-20">
                <el-button size="small" type="info" @click.stop="reload">重新录制</el-button>
                <el-button size="small" type="warning" @click.stop="stop">停止</el-button>
                <el-button size="small" type="success" @click.stop="play">播放</el-button>
                <el-button :disabled="audioTime == 0" size="small" type="danger" @click.stop="send"
                    >发送</el-button
                >
            </div>
        </div>

        <!-- 语音播放 -->
        <audioPlay
            v-model="isShowAudio"
            :url="parseResourceUrl(filePath)"
            @ended="isShowAudio = false"
        ></audioPlay>
    </kl-dialog>
</template>

<script>
export default {
    components: {
        audioPlay: () => import('@/components/audioPlay/index.vue'),
    },
    props: {
        value: {
            type: Boolean,
            default: false,
        },
    },
    data() {
        return {
            isShowAudio: false,
            filePath: '',
            file: null,
            mediaRecorder: null,
            isStart: false,
            audioTime: 0,
            timer: null,
        }
    },
    computed: {
        visable: {
            get() {
                return this.value
            },
            set() {
                return this.$emit('input', !this.value)
            },
        },
    },
    watch: {
        value(val) {
            if (val) {
                // 进入直接开始录音
                this.init()
                return
            }
        },
    },
    beforeDestroy() {
        this.clearTimer()
        this.audioTime = 0
    },
    methods: {
        close() {
            this.filePath = ''
            this.mediaRecorder = null
            this.file = null
            this.visable = false
            this.clearTimer()
            this.audioTime = 0
        },
        getAudioTime() {
            return (this.audioTime / 1000).toFixed(2) + 's'
        },
        reload() {
            this.filePath = ''
            this.mediaRecorder = null
            this.file = null
            this.init()
        },
        stop() {
            this.clearTimer()
            this.mediaRecorder.stop()
        },
        play() {
            if (!this.filePath) {
                this.stop()
            }

            this.isShowAudio = true
        },
        async send() {
            if (!this.filePath) {
                this.stop()

                await this.sleep()
            }

            this.commonUploadFile(this.file, 'im', 500)
                .then(({ url = '' }) => {
                    this.$emit('pushInfo', {
                        msg_type: '5',
                        content: url,
                        time: this.audioTime,
                    })
                    this.close()
                })
                .catch(() => {})
        },
        clearTimer() {
            clearInterval(this.timer)
            this.timer = null
        },
        init() {
            if (this.mediaRecorder) return

            this.clearTimer()
            this.audioTime = 0
            // 请求麦克风权限
            navigator.mediaDevices
                .getUserMedia({ audio: true })
                .then((stream) => {
                    // 创建MediaRecorder实例
                    const mediaRecorder = new MediaRecorder(stream)

                    // 处理录音数据
                    const recordedChunks = []
                    mediaRecorder.ondataavailable = (event) => {
                        if (event.data.size > 0) {
                            recordedChunks.push(event.data)
                        }
                    }

                    // 停止录音时的处理
                    mediaRecorder.onstop = () => {
                        // 将数据块转换为Blob对象
                        const blob = new Blob(recordedChunks, { type: 'audio/ogg; codecs=opus' })
                        const fileName = 'recordedAudio.ogg'

                        this.file = new File([blob], fileName, {
                            type: 'audio/ogg', // 这里不需要指定codecs,因为Blob已经包含了它
                        })
                        // TODO:还没做
                        this.filePath = this.getObjectURL(this.file)
                    }

                    // 开始录音
                    mediaRecorder.start()
                    this.mediaRecorder = mediaRecorder

                    // 计时器
                    this.timer = setInterval(() => {
                        // 最大60s
                        if (this.audioTime >= 60 * 1000) {
                            this.clearTimer()
                            return
                        }
                        this.audioTime += 50
                    }, 50)
                })
                .catch((err) => {
                    console.error('Error accessing the microphone:', err)
                })
        },
        // 获取视频的本地地址
        getObjectURL(file) {
            var url = null
            // 下面函数执行的效果是一样的,只是需要针对不同的浏览器执行不同的 js 函数而已
            if (window.createObjectURL !== undefined) {
                // basic
                url = window.createObjectURL(file)
            } else if (window.URL !== undefined) {
                // mozilla(firefox)
                url = window.URL.createObjectURL(file)
            } else if (window.webkitURL !== undefined) {
                // webkit or chrome
                url = window.webkitURL.createObjectURL(file)
            }
            return url
        },
    },
}
</script>

<style lang="scss" scoped>
.el-icon-close-1 {
    top: 5px;
    right: 5px;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1846227.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2025天津数控机床展(天津工业展)

2025第21届天津工博会—机床展 时间&#xff1a;2025年3月6-9日 地点&#xff1a;国家会展中心&#xff08;天津&#xff09; 达成交易&#xff0c;是我们唯一的追求&#xff01; Dealing Is Our Top Pursuit. 主办单位 振威会展集团 中国机械工业联合会 中国国际贸易促…

Vscode远程ubuntu

远程连接 到这里vscode远程到ubuntu和关闭远程连接&#xff0c;已完成 配置python环境 在远程目录下新建.vscode隐藏文件夹&#xff0c;文件夹里新建一个 settings.json 文件&#xff0c; 先远程服务器看下conda下的python虚拟环境位置 settings.json位置及内容如下 测试pyt…

【STM32-DAP 仿真器】

STM32-DAP 仿真器 ■ STM32-DAP仿真器介绍■ STM32-DAP仿真特点■ STM32-DAP仿真器实物图■ STM32-DAP高速 DAP 仿真器实物图■ STM32-DAP高速无线调试器 实物图■ STM32-DAP高速无线调试器示意图■ STM32-DAP高速无线调试器接线图■ STM32-DAP高速无线调试器接收端示意图 ■ S…

等保2.0对云计算有哪些特定的安全要求?

等保2.0针对云计算环境设定了特定的安全要求&#xff0c;这些要求是在原有的安全通用要求基础上的扩展&#xff0c;旨在确保云服务的安全性和合规性。以下是一些关键的云计算安全扩展要求&#xff1a; 1. 基础设施的位置&#xff1a;等保2.0要求云计算基础设施位于中国境内&am…

API低代码平台介绍5-数据库记录修改功能

数据库记录修改功能 在上篇文章中我们介绍了如何插入数据库记录&#xff0c;本篇文章会沿用上篇文章的测试数据&#xff0c;介绍如何使用ADI平台定义一个修改目标数据库记录的接口&#xff0c;包括 单主键单表修改、复合主键单表修改、多表修改&#xff08;整合前两者&#xff…

Java图形用户界面设计AWT事件处理

AWT事件处理 前言一、GUI事件处理机制定义使用步骤Swing事件处理机制与AWT的区别 二、GUI中常见事件和事件监听器事件低级事件高级事件 事件监听器AWT事件类的继承关系 三、事件适配器三、示例代码示例示例一示例二 示例三 前言 推荐一个网站给想要了解或者学习人工智能知识的…

Jmeter性能 之 “查看结果树” 界面功能介绍

前言 查看结果树 显示所有请求响应的树&#xff0c;通过它可以查看任何请求的响应。除了显示响应之外&#xff0c;还可以查看获取响应所花费的时间以及一些响应代码。需要通过"查看结果树"来查看服务器处理请求之后的返回结果&#xff0c;分析是否存在问题 注意&am…

算力服务先锋!和鲸科技入选《2024中国智算产业生态图谱》

2024 年 6 月 18 日&#xff0c;由科智咨询发起的《2024中国智算产业生态图谱》正式发布&#xff0c;依托 ModelWhale 构建的智算算力资源服务&#xff0c;以及深耕多年的 ModelWhale 数据科学协同平台优势&#xff0c;和鲸科技成功入选。 “智算时代”技术不断进步&#xff0c…

生产实习Day13 ---- 神经网络模型介绍

文章目录 传统的神经网络模型注意力机制的引入注意力机制的本质Encoder-Decoder 框架注意力机制在 Encoder-Decoder 中的应用Self-Attention 机制Transformer 模型注意力机制的优势总结 传统的神经网络模型 在深度学习中&#xff0c;传统的神经网络模型&#xff0c;如循环神经…

react学习——09react中props属性

1、基本使用 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><!-- 移动端适配--><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>1_props基…

【前端vue3】TypeScrip-基础类型和任意类型

安装TypeScript npm install typescript -g 运行tsc -v可查看当前版本 为了方便调试可以安装 先安装小满zs大神写的工具&#xff0c;可以切换下载源 npm i xmzs -g 安装成功后 使用mmp ls查看当前的源有哪些 使用 mmp use选择镜像源 切换成功后&#xff0c;安装TypeScrip…

【51单片机】按键的操作

文章目录 前言读取按键的原理proteus仿真示例代码 总结 前言 在现代电子产品中&#xff0c;按键是用户与设备之间交互的重要组成部分。它们允许用户通过简单的按下来触发特定的操作或命令。在微控制器的背景下&#xff0c;按键的设计和操作对于确保设备的响应性和用户体验至关…

「ClickHouse 极简教程」分布式下的 IN/JOIN 及 GLOBAL关键字

百度安全验证https://baijiahao.baidu.com/s?id1712073808973941124&wfrspider&forpc

NSSCTF-Web题目12

目录 [SWPUCTF 2021 新生赛]finalrce 1、题目 2、知识点 3、思路 [UUCTF 2022 新生赛]ez_rce 1、题目 2、知识点 3、思路 [羊城杯 2020]easycon 1、题目 2、知识点 3、思路 [SWPUCTF 2021 新生赛]finalrce 1、题目 2、知识点 命令执行&#xff0c;tee命令 3、思路…

springboot家乡特色推荐系统 LW +PPT+源码+讲解

3系统需求分析 3.1系统功能 通过前面的功能分析可以将家乡特色推荐系统的功能分为管理员和用户两个部分&#xff0c;系统的主要功能包括首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;文章分类管理&#xff0c;文章分享管理&#xff0c;系统管理等内容。任何用户…

五谷杂粮店铺小程序的作用是有什么

大米、面粉、荞麦、豆类等多种五谷杂粮商品可以说是必需&#xff0c;线下门店或摊位数量也不乏&#xff0c;但相比较流量较为固定且销售模式单一&#xff0c;想要进一步扩大生意效果却是不易。 线上是商家重点增长的场景&#xff0c;五谷杂粮商品自然也适合开展零售批发生意&a…

Leetcode - 周赛402

目录 一&#xff0c;3185. 构成整天的下标对数目 II 二&#xff0c;3186. 施咒的最大总伤害 三&#xff0c;3187. 数组中的峰值 一&#xff0c;3185. 构成整天的下标对数目 II 这里的T1&#xff0c;T2是一个题&#xff0c;这里直接一起讲了&#xff0c; 当我们已知 x%24 的值…

NGINX_十八 nginx 访问控制

十八 nginx 访问控制 1 nginx 访问控制模块 &#xff08;1&#xff09;基于IP的访问控制&#xff1a;http_access_module &#xff08;2&#xff09;基于用户的信任登录&#xff1a;http_auth_basic_module 2 基于IP的访问控制 2.1 配置语法 Syntax&#xff1a;allow addr…

实现一个动态规划算法,解决背包问题

public class Test_31 {// 动态规划解决0-1背包问题public int knapsack(int capacity, int[] weights, int[] values, int n) {// 创建一个二维数组dp&#xff0c;用于记录状态转移过程int[][] dp new int[n 1][capacity 1];// 遍历物品for (int i 1; i < n; i) {// 遍…

73. UE5 RPG 优化投射物以及敌人生成

解决发射物会与地面产生交互的问题 之前一直遇到发射物的体积过大会在发射时&#xff0c;和地面产生交互&#xff0c;我们可以调整小一些&#xff0c;然后为了防止它和自身产生交互事件。我们可以实现它在生成后&#xff0c;不会触发相关事件&#xff0c;而是在一定时间后。 对…