<template>
<div class="meetingRoom-content">
<div class="meetingRoom-content-box" v-if="domReady">
<div
class="meeting-box"
:style="`width: ${videoStyle.width + '%'};height: ${
videoStyle.height + '%'
}`"
v-for="(i, index) in participants"
:key="'participants' + index"
:id="i.userId"
@mouseover="
() => {
participants[index].hoverButtonShow = true;
}
"
@mouseleave="
() => {
participants[index].hoverButtonShow = false;
}
"
>
<el-card>
<div class="camera-box" v-loading="i.loading">
<div
class="camera-box-inner"
v-for="(x, y) in i.children"
:key="y + 'children' + x.featureCode"
v-show="x.isShow"
:style="`width: ${i.videoInnerStyle!.width + '%'};max-height: ${i.videoInnerStyle!.height + '%'
};`"
>
<div class="userName-box" v-if="!i.isApp">
{{
`${i.userName} — ${
x.cameraName || i.cameraName
}`
}}
</div>
<div class="userName-box" v-else>
{{ `${i.userName}` }}
</div>
<video
:class="{ appVideo: i.isApp }"
:id="x.featureCode"
muted
autoplay
controls
v-show="x.camera"
></video>
</div>
</div>
<div class="camera-mask-box" v-show="!i.camera">
<div class="userName-icon-box">
<div class="userName-icon">
{{ formatterUserName(i.userName!) }}
</div>
</div>
</div>
<div
class="btn-gray-small cusp"
@click="fullScreen(i.userId)"
style="right: 16px; z-index: 999; padding: 0 10px"
v-if="i.isMultiple && i.hoverButtonShow"
>
<el-icon>
<FullScreen />
</el-icon>
</div>
<div
class="btn-gray-small cusp"
@click="openPicInPic(i.userId)"
style="right: 64px; z-index: 999; padding: 0 10px"
v-if="i.isMultiple && i.hoverButtonShow"
>
<el-icon>
<CopyDocument />
</el-icon>
</div>
<div
class="btn-gray-small cusp"
@click="captureParticipantFrame(i.userId)"
style="right: 112px; z-index: 999; padding: 0 10px"
v-if="i.hoverButtonShow"
>
<el-icon>
<Camera />
</el-icon>
</div>
</el-card>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
function formatterUserName(userName: string) {
if (userName.length > 4) {
return userName.slice(0, 3) + "...";
} else {
return userName;
}
}
// 截图并保存到本地的工具函数
const captureAndSaveFrame = (video, fileName) => {
// 创建canvas元素
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d")!;
// 设置canvas尺寸与视频一致
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
// 将视频帧绘制到canvas
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
// 生成图片数据URL(支持PNG格式)
const imageDataUrl = canvas.toDataURL("image/png");
// 创建下载链接
const link = document.createElement("a");
link.href = imageDataUrl;
const imageName = `${fileName}_${Date.now()}.png`;
link.download = imageName; // 带时间戳的文件名
//上传到系统管理图片管理
const imgFile = base64ToFile(imageDataUrl, imageName);
// 模拟点击下载
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
const formData = new FormData()[图片];
// 将文件添加到 FormData 中,以便后续发送请求
formData.append("file", imgFile);
// 添加额外的请求参数,这里 dir 为空字符串
formData.append("dir", "");
// 添加额外的请求参数,这里 type 为 20
formData.append("type", "10");
request({
url: "/admin/sys-file/upload",
method: "post",
headers: {
"Content-Type": "multipart/form-data",
Authorization: "Bearer " + Session.get("token"),
"TENANT-ID": Session.getTenant(),
},
data: formData,
})
.then((data) => {
// 请求成功时的处理,打印返回的数据
console.log("success", data);
})
.catch((err) => {
// 请求失败时的处理,打印错误信息
console.log("error", err);
});
// 清理canvas
canvas.remove();
};
/**
* 将Base64字符串转换为File对象
* @param {string} base64 - Base64编码的字符串
* @param {string} filename - 生成文件的名称
* @param {string} [mimeType] - 文件的MIME类型,默认为'image/png'
* @returns {File} - 返回的File对象
*/
function base64ToFile(base64, filename, mimeType = "image/png") {
// 1. 移除Base64前缀(如果有)
const base64WithoutPrefix = base64.replace(/^data:.+;base64,/, "");
// 2. 将Base64字符串转换为字节数组
const byteCharacters = atob(base64WithoutPrefix);
const byteArrays = [] as any;
for (let offset = 0; offset < byteCharacters.length; offset += 512) {
const slice = byteCharacters.slice(offset, offset + 512);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
// 3. 创建Blob对象
const blob = new Blob(byteArrays, { type: mimeType });
// 4. 将Blob转换为File对象
return new File([blob], filename, { type: mimeType });
}
// 为单个参与者截图并保存
const captureParticipantFrame = (userId) => {
const participantElement = document.getElementById(userId);
if (!participantElement) return;
const videoElements = participantElement.querySelectorAll("video");
videoElements.forEach((video, index) => {
if (video.videoWidth > 0 && video.videoHeight > 0) {
const fileName = `${userId}_camera_${index + 1}`;
captureAndSaveFrame(video, fileName);
}
});
};
</script>