需求: 视频通话
想法: 不会做,笑死 (采用腾讯云第三方,或者使用vue + webRtc + websocket) 暂时只做到一对一,而且还有一些问题,求指教
有想法的可以加个好友互相讨论一下
1. 接入腾讯云 - 直接在mounted里引入createClient方法就可以了
<template>
<!-- 视频会诊 -->
<div class="videoCon">
<div class="topBar">
<el-form
:inline="true"
:model="formInline"
class="demo-form-inline"
:label-position="labelPosition"
label-width="100px"
>
<el-form-item label="申请医生:">
<el-input v-model="formInline.doctor" size="mini"></el-input>
</el-form-item>
<el-form-item label="手机号:">
<el-input v-model="formInline.phone" size="mini"></el-input>
</el-form-item>
<el-form-item label="请求时间:">
<el-input v-model="formInline.time" size="mini"></el-input>
</el-form-item>
<el-form-item label="申请医院:">
<el-input v-model="formInline.hospital" size="mini"></el-input>
</el-form-item>
</el-form>
<div class="rightBox">
<div class="recording">
<i></i>
<span>录制中 :</span>
<span>1:20:02</span>
</div>
<el-button type="primary" @click="signOut">退出</el-button>
</div>
</div>
<div class="main">
<div class="video">
<div class="bigScreen">
<span class="iconfont iconicon-03-17"></span>
</div>
<div style="width: 22px"></div>
<div class="smallScreem">
<div>
<span class="iconfont iconicon-03-17"></span>
<span class="iconfont iconicon-03-16"></span>
<div class="center-page">
<div v-html="remoteStream"
:class="remoteStream?'distant-stream':''">
</div>
</div>
</div>
<!-- <div>
<span class="iconfont iconicon-03-17"></span>
<span class="iconfont iconicon-03-16"></span>
</div>
<div>
<span class="iconfont iconicon-03-17"></span>
<span class="iconfont iconicon-03-16"></span>
</div>
<div>
<span class="iconfont iconicon-03-16"></span>
<span class="iconfont iconicon-03-17"></span>
</div>
<div>
<span class="iconfont iconicon-03-16"></span>
<span class="iconfont iconicon-03-17"></span>
</div>
<div>
<span class="iconfont iconicon-03-17"></span>
<span class="iconfont iconicon-03-16"></span>
</div>
<div>
<span class="iconfont iconicon-03-17"></span>
<span class="iconfont iconicon-03-16"></span>
</div>
<div>
<span class="iconfont iconicon-03-17"></span>
<span class="iconfont iconicon-03-16"></span>
</div>
<div>
<span class="iconfont iconicon-03-17"></span>
<span class="iconfont iconicon-03-16"></span>
</div> -->
<div style="marginTop: 10px;">
<span class="iconfont iconicon-03-17"></span>
<span class="iconfont iconicon-03-16" @click="snapshot"></span>
<div id='local_stream'
class="local-stream">
</div>
</div>
</div>
</div>
<div class="operation">
<div>
<span class="iconfont iconicon-03-31"></span>
<p>控制</p>
</div>
<div>
<span class="iconfont iconicon-03-30"></span>
<p>测量</p>
</div>
<div>
<span class="iconfont iconicon-03-42"></span>
<p>注释</p>
</div>
<div>
<span class="iconfont iconicon-03-34"></span>
<p>截图</p>
</div>
<div>
<span class="iconfont iconicon-03-32"></span>
<p>截图附件</p>
</div>
<div>
<span class="iconfont iconicon-03-33"></span>
<p>患者查看详情</p>
</div>
<div>
<span class="iconfont iconicon-03-15"></span>
<p>邀请</p>
</div>
<div>
<span class="iconfont iconicon-03-29"></span>
<p>聊天</p>
</div>
<div>
<span class="iconfont iconicon-03-28"></span>
<p>声音</p>
</div>
<div>
<span class="iconfont iconicon-03-14"></span>
<p>麦克风</p>
</div>
<div>
<span class="iconfont iconicon-03-10"></span>
<p>视频</p>
</div>
<div>
<span class="iconfont iconicon-03-18"></span>
<p>录制</p>
</div>
</div>
</div>
<img src="" alt="" ref="picture">
</div>
</template>
<script>
// 一定要引入这个-去下个demo里面就有
import LibGenerateTestUserSig from '@/utils/lib-generate-test-usersig.min';
import TRTC from 'trtc-js-sdk';
export default {
data() {
return {
formInline: {
doctor: "",
phone: "",
time: "",
hospital: "",
},
labelPosition: "right",
userId: 'user_' + parseInt(Math.random() * 100000000),//用户id --可更改
roomId: 88888,//房间号--加入相同房间才能聊
client: '',//客户端服务
remoteStream: '',//远方播放流
localStream: '',//本地流
};
},
methods: {
// 退出
signOut() {
Bus.$emit('code', "true");
this.$router.go(-1);
},
//创建链接
createClient (userId) {
//获取签名
const config = this.genTestUserSig(userId);
const sdkAppId = config.sdkAppId;
const userSig = config.userSig;
this.client = TRTC.createClient({
mode: 'videoCall',
sdkAppId,
userId,
userSig
});
//注册远程监听,要放在加入房间前--这里用了发布订阅模式
this.subscribeStream(this.client);
//初始化后才能加入房间
this.joinRoom(this.client, this.roomId);
},
//加入房间
joinRoom (client, roomId) {
client.join({ roomId }).then(() => {
console.log('进房成功');
//创建本地流
this.createStream(this.userId);
//播放远端流
this.playStream(this.client);
}).catch(error => {
console.error('进房失败 ' + error);
})
},
//创建本地音视频流
createStream (userId) {
const localStream = TRTC.createStream({ userId, audio: false, video: true });
this.localStream =localStream;
localStream
.initialize().then(() => {
console.log('初始化本地流成功');
// 创建好后才能播放 本地流播放 local_stream 是div的id
localStream.play('local_stream');
//创建好后才能发布
// this.$nextTick(() => {
this.publishStream(localStream, this.client);
// });
}).catch(error => {
console.error('初始化本地流失败 ' + error);
})
},
//发布本地音视频流
publishStream (localStream, client) {
client.publish(localStream).then(() => {
console.log('本地流发布成功');
}).catch(error => {
console.error('本地流发布失败 ' + error);
});
},
//订阅远端流--加入房间之前
subscribeStream (client) {
client.on('stream-added', event => {
console.log(client,'client9696');
const remoteStream = event.stream;
console.log('远端流增加: ' + remoteStream.getId());
//订阅远端流
client.subscribe(remoteStream);
});
},
//播放远端流
playStream (client) {
client.on('stream-subscribed', event => {
const remoteStream = event.stream;
console.log('远端流订阅成功:' + remoteStream.getId());
// 创建远端流标签,因为id是动态的,所以动态创建,用了v-html
this.remoteStream = `<view id="${'remote_stream-' + remoteStream.getId()}" ></view>`;
//做了dom操作 需要使用$nextTick(),否则找不到创建的标签无法进行播放
this.$nextTick(() => {
//播放
remoteStream.play('remote_stream-' + remoteStream.getId());
})
});
},
//退出音视频
leaveRoom (client) {
client
.leave()
.then(() => {
console.log('退房成功')
// 停止本地流,关闭本地流内部的音视频播放器
this.localStream.stop();
// 关闭本地流,释放摄像头和麦克风访问权限
this.localStream.close();
this.localStream = null;
this.client = null
// 退房成功,可再次调用client.join重新进房开启新的通话。
})
.catch(error => {
console.error('退房失败 ' + error);
// 错误不可恢复,需要刷新页面。
});
},
//获取用户签名--前端测试用
genTestUserSig (userID) {
/**
* 腾讯云 SDKAppId,需要替换为您自己账号下的 SDKAppId。
*
* 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ) 创建应用,即可看到 SDKAppId,
* 它是腾讯云用于区分客户的唯一标识。
*/
const SDKAPPID = 1400371155;
/**
* 签名过期时间,建议不要设置的过短
* <p>
* 时间单位:秒
* 默认时间:7 x 24 x 60 x 60 = 604800 = 7 天
*/
const EXPIRETIME = 604800;
/**
* 计算签名用的加密密钥,获取步骤如下:
*
* step1. 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ),如果还没有应用就创建一个,
* step2. 单击“应用配置”进入基础配置页面,并进一步找到“帐号体系集成”部分。
* step3. 点击“查看密钥”按钮,就可以看到计算 UserSig 使用的加密的密钥了,请将其拷贝并复制到如下的变量中
*
* 注意:该方案仅适用于调试Demo,正式上线前请将 UserSig 计算代码和密钥迁移到您的后台服务器上,以避免加密密钥泄露导致的流量盗用。
* 文档:https://cloud.tencent.com/document/product/647/17275#Server
*/
const SECRETKEY = "41b790cbcc2e6672ebd921abb4a2ad8a75e1c80705f787926ba5a77425f772af";
// a soft reminder to guide developer to configure sdkAppId/secretKey
if (SDKAPPID === "" || SECRETKEY === "") {
alert(
"请先配置好您的账号信息: SDKAPPID 及 SECRETKEY " +
"\r\n\r\nPlease configure your SDKAPPID/SECRETKEY in js/debug/GenerateTestUserSig.js"
);
}
const generator = new LibGenerateTestUserSig(SDKAPPID, SECRETKEY, EXPIRETIME);
const userSig = generator.genTestUserSig(userID);
return {
sdkAppId: SDKAPPID,
userSig: userSig
};
},
// 获取详情
async getDetails() {
// this.roomId = this.$route.query.id;
let id = this.$route.query.id;
let res = await meetApi.meetInfoApply(id);
if(res.code != 200) return;
let s = res.data;
this.formInline = {
doctor: s.applyDoctorDicName,
phone: s.applyDoctorPhone,
time: s.applyDateTime,
hospital: s.applyHospitalDicName
};
this.createClient(this.userId)
},
//截图
snapshot() {
let canvas = this.$refs.picture;
canvas.width = 400;
canvas.height = 300;
canvas.getContext("2d").drawImage(this.localVideo, 0, 0, canvas.width, canvas.height);
},
},
mounted() {
//测试用,所以直接创建了,其他需求可自行更改
this.getDetails();
}
};
</script>
<style lang="scss" scoped>
.videoCon::-webkit-scrollbar {
display: none;
}
.videoCon {
height: 100%;
width: 100%;
overflow-y: scroll;
.topBar {
width: 100%;
height: 135px;
// opacity: 0.25;
background: #42464d;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.25);
padding: 30px 32px;
display: flex;
justify-content: space-between;
.demo-form-inline {
width: 65%;
.el-form-item {
margin: 0 15px;
width: 375px;
.el-input__inner {
background-color: #42464d;
color: #fff;
}
}
}
.rightBox {
width: 35%;
display: flex;
justify-content: space-between;
.recording {
font-size: 20px;
color: #fff;
font-weight: 700;
margin-top: 52px;
i {
display: inline-block;
width: 15px;
height: 15px;
background: #e60012;
border-radius: 15px;
margin-right: 16px;
}
span {
margin-right: 5px;
}
}
}
}
.main {
// height: calc(100% - 135px);
// height: 100%;
width: 100%;
background-color: #272e43;
padding: 30px;
.video {
padding: 0 15px;
display: flex;
// justify-content: space-between;
margin-bottom: 40px;
.bigScreen {
width: 1290px;
height: 720px;
background-color: #ccc;
span {
color: #a0a0a0;
font-size: 48px;
cursor: pointer;
}
}
.smallScreem {
width: 492px;
height: 720px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
// align-content: space-between;
div {
width: 492px;
height: 355px;
background-color: #ccc;
position: relative;
span {
color: #a0a0a0;
font-size: 24px;
cursor: pointer;
float: right;
z-index: 2;
}
//远端流
.distant-stream {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
.iconicon-03-17 {
position: absolute;
z-index: 2;
}
.iconicon-03-16 {
position: absolute;
z-index: 2;
left: 30px;
}
//本地流
.local-stream {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
}
}
}
.operation {
height: 106px;
width: 100%;
background-color: #1d2335;
border-radius: 20px;
display: flex;
align-items: center;
justify-content: space-around;
font-size: 20px;
font-weight: 400;
color: #fff;
text-align: center;
div {
cursor: pointer;
span {
font-size: 36px;
}
p {
margin-top: 12px;
}
}
}
}
}
</style>
<style lang="scss">
.videoCon {
.demo-form-inline {
.el-form-item {
.el-form-item__label {
color: #fff;
font-size: 20px;
}
.el-input__inner {
background-color: #42464d;
border-color: #42464d;
color: #fff;
font-size: 20px;
height: 40px;
}
}
}
.rightBox {
.el-button--primary {
background-color: #d33594;
border-color: #d33594;
width: 116px;
height: 55px;
margin-top: 10px;
padding-top: 10px;
span {
font-size: 20px;
color: #e6e6e6;
font-weight: 700;
}
}
}
}
</style>
2. 采用vue + webRtc + websocket制作
<template>
<!-- 视频会诊 -->
<div class="startConsultation" ref="imageWrapper">
<div class="topBar">
<el-form :inline="true" :model="formInline" class="demo-form-inline" :label-position="labelPosition" label-width="100px">
<el-form-item label="申请医生:">
<!-- <el-input v-model="formInline.doctor" size="mini"></el-input> -->
<div>{{formInline.doctor}}</div>
</el-form-item>
<el-form-item label="手机号:">
<!-- <el-input v-model="formInline.phone" size="mini"></el-input> -->
<div>{{formInline.phone}}</div>
</el-form-item>
<el-form-item label="请求时间:">
<!-- <el-input v-model="formInline.time" size="mini"></el-input> -->
<div>{{formInline.time}}</div>
</el-form-item>
<el-form-item label="申请医院:">
<!-- <el-input v-model="formInline.hospital" size="mini"></el-input> -->
<div>{{formInline.hospital}}</div>
</el-form-item>
</el-form>
<div class="rightBox">
<div class="recording">
<i></i>
<span>录制中 :</span>
<span>1:20:02</span>
</div>
<el-button type="primary" @click="signOut">退出</el-button>
</div>
</div>
<div class="main">
<div class="video">
<div class="bigScreen">
<span class="iconfont iconicon-03-17"></span>
</div>
<div style="width: 22px"></div>
<div class="smallScreem">
<div class="bigBox">
<div class="icon">
<span class="iconfont iconicon-03-16"></span>
<span class="iconfont iconicon-03-17"></span>
</div>
<video id="remoteVideo"></video>
</div>
<div class="bigBox">
<div class="icon">
<span class="iconfont iconicon-03-16"></span>
<span class="iconfont iconicon-03-17"></span>
</div>
<video id="localVideo" autoplay muted></video>
</div>
</div>
</div>
<div class="operation">
<div>
<span class="iconfont iconicon-03-31"></span>
<p>控制</p>
</div>
<div>
<span class="iconfont iconicon-03-30"></span>
<p>测量</p>
</div>
<div>
<span class="iconfont iconicon-03-42"></span>
<p>注释</p>
</div>
<div @click="snapshot">
<span class="iconfont iconicon-03-34"></span>
<p>截图</p>
</div>
<div>
<span class="iconfont iconicon-03-32"></span>
<p>截图附件</p>
</div>
<div>
<span class="iconfont iconicon-03-33"></span>
<p>患者查看详情</p>
</div>
<div>
<span class="iconfont iconicon-03-15"></span>
<p>邀请</p>
</div>
<div>
<span class="iconfont iconicon-03-29"></span>
<p>聊天</p>
</div>
<div>
<span class="iconfont iconicon-03-28"></span>
<p>声音</p>
</div>
<div>
<span class="iconfont iconicon-03-14"></span>
<p>麦克风</p>
</div>
<div>
<span class="iconfont iconicon-03-10"></span>
<p>视频</p>
</div>
<div>
<span class="iconfont iconicon-03-18"></span>
<p>录制</p>
</div>
</div>
<div class="logger"></div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
formInline: {
doctor: "潘远航",
phone: "13066990173",
time: "2021-03-08 12:00:11",
hospital: "普博医院",
},
labelPosition: "right",
//websocket
socket: null,
//webrtc
peer: null,
//send data
sendData: {
Id: "1",
SenderId: "1",
Type: "join"
},
target: null,
// videos
localVideo: {},
remoteVideo: {},
logger: {},
//source
audioSourceOption: [],
audioOutputOption: [],
videoSourceOption: [],
//Constraints(媒体约束)
videoConstraints: '',
// Media config
constraints: {
audio: {
noiseSuppression: true,
echoCancellation: true
},
video: {
width: 1920,
height: 1080,
frameRate: 30,
facingMode: "environment"
}
},
// local video stream
localStream: undefined,
isOpenMediaStream: false
}
},
async created() {
this.sendData.Id = this.$route.query.id;
this.sendData.SenderId = this.$route.query.sid;
if (!this.sendData.SenderId) {
this.sendData.SenderId = "1";
}
this.target = this.$route.query.t > 1 ? "answer" : "offer";
console.info("target:", this.target);
await this.getUserMedia();
await this.getDevices();
await this.getAudioVideo();
},
watch: {
},
mounted() {
this.localVideo = document.getElementById("localVideo");
this.remoteVideo = document.getElementById("remoteVideo");
this.logger = document.querySelector('.logger');
//获取浏览器支持的连接
const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
if (!PeerConnection) {
this.error('浏览器不支持WebRTC!');
return;
}
// stun 服务,如果要做到 NAT 穿透,还需要 turn 中转服务
let iceServer = {
"iceServers": [
{
"url": "stun:stun.l.google.com:19302"
},
// {
// "url": "stun:192.168.1.162:3478"
// },
]
};
this.peer = new PeerConnection(iceServer);
this.peer.ontrack = e => {
if (e && e.streams) {
this.log('收到对方音频/视频流数据...');
remoteVideo.srcObject = e.streams[0];
setTimeout(() => {
remoteVideo.play();
}, 150);
}
};
this.peer.onicecandidate = e => {
if (e.candidate) {
this.log('搜集并发送候选人');
this.sendData.Type = "send";
this.sendData.Data = JSON.stringify({
type: `${this.target}_ice`,
iceCandidate: e.candidate
});
this.socket.send(JSON.stringify(this.sendData));
} else {
this.log('候选人收集完成!');
}
};
this.log('信令通道(WebSocket)创建中......');
var socketUrl = location.protocol + "//" + location.host + "/ws";
socketUrl = socketUrl.replace("https", "wss").replace("http", "ws");
this.log("连接地址:" + socketUrl);
this.socket = new WebSocket(socketUrl);
this.socket.onopen = () => {
this.log('信令通道已打开!');
this.log("当前角色: " + this.target);
this.sendData.Type = "join";
this.socket.send(JSON.stringify(this.sendData));
}
this.socket.onclose = (e) => {
this.error("断开连接");
console.log('断开连接', e);
}
this.socket.onerror = () => this.error('信令通道创建失败!');
//设置别名
let _this = this;
this.socket.onmessage = e => {
const { Type, Code, Msg, Data } = JSON.parse(e.data);
if (Code != 200) {
console.warn(Msg);
return;
}
//加入
if (Type === "join") {
this.log("当前工作间在线人数:" + Data);
if (!this.target) {
this.target = Data > 1 ? "answer" : "offer";
}
this.startLive();
return;
}
let resultData;
if (typeof Data !== "object") {
resultData = JSON.parse(Data);
} else {
resultData = Data;
}
const { type, sdp, iceCandidate } = resultData;
if (type === 'answer') {
_this.peer.setRemoteDescription(new RTCSessionDescription({ type, sdp }));
} else if (type === 'answer_ice') {
_this.peer.addIceCandidate(iceCandidate);
} else if (type === 'offer') {
this.startLive(new RTCSessionDescription({ type, sdp }));
} else if (type === 'offer_ice') {
_this.peer.addIceCandidate(iceCandidate);
}
};
},
beforeDestroy() {
if (this.socket.readyState == WebSocket.OPEN) {
this.sendData.Type = "leave";
this.socket.send(JSON.stringify(this.sendData));
this.socket.close();
}
if (this.isOpenMediaStream) {
this.localStream.getVideoTracks()[0].stop();
this.localStream.getAudioTracks()[0].stop();
}
},
methods: {
//拉起本地音视频流
async startLocalMedia() {
await this.getUserMedia();
await this.getAudioVideo();
},
//拿到媒体流
async getUserMedia() {
// log(`Requesting video stream`);
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
if ("mediaDevices" in navigator) {
try {
const stream = await navigator.mediaDevices.getUserMedia(this.constraints);
this.localVideo.srcObject = stream;
this.localStream = stream;
// log("Received local video stream");
} catch (error) {
// log(`getUserMedia error: ${error}`);
}
}
},
//拿到音视频轨道
async getAudioVideo() {
if (this.localStream == undefined) {
await this.getUserMedia();
}
console.log("localStream", this.localStream);
if (this.localStream !== undefined && this.localStream != null) {
this.isOpenMediaStream = true;
} else {
this.isOpenMediaStream = false;
return;
}
let videoTrack = this.localStream.getVideoTracks()[0];
let audioTrack = this.localStream.getAudioTracks();
console.log(videoTrack);
console.log(audioTrack);
let videoConstraintsData = videoTrack.getSettings();
console.log(videoConstraintsData)
this.videoConstraints = JSON.stringify(videoConstraintsData, null, 4)
console.log(this.videoConstraints)
},
//获取设备信息
getDevices() {
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
console.log('不支持获取设备信息!');
this.isOpenMediaStream = false;
} else {
navigator.mediaDevices.enumerateDevices().then(this.showDevice).catch((err) => {
this.error(err.name + ":" + err.message);
});
}
},
//展示设备信息
showDevice(deviceInfos) {
let _this = this;
deviceInfos.forEach(function (deviceinfo) {
var option = {
text: deviceinfo.label,
value: deviceinfo.deviceId
}
if (deviceinfo.kind === "audioinput") {
_this.audioSourceOption.push(option);
} else if (deviceinfo.kind === "audiooutput") {
_this.audioOutputOption.push(option);
} else if (deviceinfo.kind === "videoinput") {
_this.videoSourceOption.push(option);
}
})
},
// 退出
signOut() {
Bus.$emit('code', "true");
this.$router.go(-1);
},
async startLive(offerSdp) {
let stream = this.localStream;
let _this = this;
// if (!this.isOpenMediaStream) {
// this.error("尚未开启媒体流!");
// return;
// }
if (stream == undefined) {
stream = await navigator.mediaDevices.getUserMedia(this.constraints);
}
this.log('将媒体轨道添加到轨道集');
stream.getTracks().forEach(track => {
//console.info("startLive", _this.peer, track, stream);
_this.peer.addTrack(track, stream);
});
this.sendData.Type = "send";
if (!offerSdp) {
this.log('创建本地SDP');
const offer = await this.peer.createOffer();
await this.peer.setLocalDescription(offer);
offer.sdp = offer.sdp;
this.log(`传输本地SDP`);
this.sendData.Data = offer;
this.socket.send(JSON.stringify(this.sendData));
} else {
this.log('接收远程SDP');
//设置接收到的远端offer
await this.peer.setRemoteDescription(offerSdp);
this.log('创建回复(应答)SDP');
//创建answer并发送给对方。
const answer = await this.peer.createAnswer();
console.warn(answer);
this.log(`传输回复接收(应答)SDP`);
this.sendData.Data = answer;
this.socket.send(JSON.stringify(this.sendData));
await this.peer.setLocalDescription(answer);
}
},
log(msg) {
this.logger.innerHTML += `<span>${new Date().toLocaleTimeString()}:${msg}</span><br/>`;
},
error(msg) {
this.logger.innerHTML += `<span class="error">${new Date().toLocaleTimeString()}:${msg}</span><br/>`;
}
}
};
</script>
<style lang="scss" scoped>
.startConsultation::-webkit-scrollbar {
display: none;
}
.startConsultation {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
// overflow-y: scroll;
.topBar {
width: 100%;
height: 105px;
// opacity: 0.25;
background: #42464d;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.25);
padding: 15px 32px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
.demo-form-inline {
width: 65%;
.el-form-item {
margin: 0 15px;
width: 375px;
div {
color: #fff;
font-size: 20px;
}
// .el-input__inner {
// background-color: #42464d;
// color: #fff;
// }
}
}
.rightBox {
width: 35%;
display: flex;
justify-content: space-between;
.recording {
font-size: 20px;
color: #fff;
font-weight: 700;
margin-top: 52px;
i {
display: inline-block;
width: 15px;
height: 15px;
background: #e60012;
border-radius: 15px;
margin-right: 16px;
}
span {
margin-right: 5px;
}
}
}
}
.main {
// height: calc(100% - 135px);
// height: 100%;
width: 100%;
background-color: #272e43;
padding: 30px;
box-sizing: border-box;
flex: 1;
display: flex;
flex-direction: column;
.video {
flex: 1;
padding: 0 15px;
display: flex;
// justify-content: space-between;
margin-bottom: 40px;
.bigScreen {
width: 1290px;
height: 100%;
background-color: #ccc;
span {
color: #a0a0a0;
font-size: 48px;
cursor: pointer;
}
}
.smallScreem {
width: 492px;
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-content: space-between;
.bigBox {
width: 500px;
height: 48%;
background-color: #ccc;
position: relative;
border: 1px solid #ccc;
.icon {
position: absolute;
right: 5px;
top: 5px;
}
video {
width: 100%;
height: 100%;
}
span {
color: #a0a0a0;
font-size: 24px;
cursor: pointer;
}
}
}
}
.operation {
// height: 106px;
width: 100%;
flex-basis: 106px;
background-color: #1d2335;
border-radius: 20px;
display: flex;
align-items: center;
justify-content: space-around;
font-size: 20px;
font-weight: 400;
color: #fff;
text-align: center;
div {
cursor: pointer;
span {
font-size: 36px;
}
p {
margin-top: 12px;
}
}
}
}
}
</style>
<style lang="scss">
.startConsultation {
.demo-form-inline {
.el-form-item {
.el-form-item__label {
color: #fff;
font-size: 20px;
}
.el-input__inner {
background-color: #42464d;
border-color: #42464d;
color: #fff;
font-size: 20px;
height: 40px;
}
}
}
.rightBox {
.el-button--primary {
background-color: #d33594;
border-color: #d33594;
width: 116px;
height: 55px;
margin-top: 10px;
padding-top: 10px;
span {
font-size: 20px;
color: #e6e6e6;
font-weight: 700;
}
}
}
.logger {
// width: 40%;
// padding: 14px;
// line-height: 1.5;
// color: #4fbf40;
// border-radius: 6px;
// background-color: #272727;
width: 400px;
height: 600px;
padding: 14px;
line-height: 1.5;
color: #4fbf40;
border-radius: 6px;
left: 100px;
top: 160px;
z-index: 1;
position: absolute;
// transform: translateY(-800px);
background-color: #272727;
}
.logger .error {
color: #dd4a68;
}
}
.vue-cropper {
position: absolute !important;
top: 0;
left: 0;
z-index: -1;
}
</style>