Compare commits
145 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 52058d5ebc | |||
| 6f5f407565 | |||
| 564a7fab9b | |||
| 6913f79e3b | |||
|
bd0d1912d2
|
|||
| b58e92381c | |||
|
8a33d29b54
|
|||
|
cfc2b60f71
|
|||
| 1e4efc75e3 | |||
|
b339604347
|
|||
|
d88448060f
|
|||
|
cc18504959
|
|||
|
5c7ff10cc8
|
|||
|
5e2db3ad91
|
|||
|
224bacbb2a
|
|||
|
da5ad1202c
|
|||
|
60d7a9905e
|
|||
|
90ce114a7f
|
|||
|
da8c001b44
|
|||
|
849b04a1ee
|
|||
|
f2b63a2fcc
|
|||
|
49edb01a3f
|
|||
|
97a5090e0f
|
|||
|
c73f45d27e
|
|||
|
0d6a2329d4
|
|||
|
9f75d4a2f8
|
|||
|
bff5eef8fa
|
|||
|
64ee8b8a4f
|
|||
|
5b4a1efb39
|
|||
|
5b1f165528
|
|||
|
9dad777061
|
|||
|
6e19aa583d
|
|||
|
8371a7188f
|
|||
|
a340c38e79
|
|||
|
168daa14f9
|
|||
|
fc9a05636c
|
|||
|
cb814e4fe2
|
|||
|
1242882785
|
|||
|
edcee2f592
|
|||
|
1bf49e37b4
|
|||
|
c493582885
|
|||
|
15903d525e
|
|||
|
bb6012b01d
|
|||
| 5fdd0c3fc6 | |||
|
d1a3115ed4
|
|||
|
25c2f49576
|
|||
|
a104d4bcd7
|
|||
|
0359178f17
|
|||
|
aae6ac3937
|
|||
| 6bafbdef55 | |||
|
8b6342eed2
|
|||
|
5d893d26b5
|
|||
|
3b2c47e0ec
|
|||
|
bc46dde11c
|
|||
| fe80512d46 | |||
|
0d29e223e6
|
|||
| 612f5c81f1 | |||
|
d51732f348
|
|||
|
9702477c23
|
|||
| b694dc1a1c | |||
|
06ecc8e678
|
|||
| 146b59fcbc | |||
|
ad9707ddbe
|
|||
| 975c4f2ab6 | |||
|
5a7f0527b0
|
|||
| c43c74e967 | |||
|
dcf70059fa
|
|||
|
aaf8655db8
|
|||
|
fb173c548d
|
|||
|
77664c98e3
|
|||
|
47e05ec6ff
|
|||
|
4b83d61df7
|
|||
|
84d3e45292
|
|||
|
1f2cae25fd
|
|||
|
d9e0899264
|
|||
| 1971fb8370 | |||
|
26b815e046
|
|||
| d96bce28b1 | |||
|
da50f3a8dc
|
|||
|
00a4dee4ec
|
|||
|
6d410c2a03
|
|||
|
1cca2b754c
|
|||
|
9f03f3b3cc
|
|||
| 162e566a32 | |||
|
5590866254
|
|||
| 11d6ff4be4 | |||
|
d536156625
|
|||
|
d6ceb62b3d
|
|||
|
ff3fe09026
|
|||
|
929d60b478
|
|||
|
9e128db2c6
|
|||
|
b0a6592373
|
|||
|
e515b8266d
|
|||
|
e7b3f694b0
|
|||
| eb799090b2 | |||
|
82d7a69123
|
|||
| 4e06d7011c | |||
|
6f7322628e
|
|||
| 10561b7a7d | |||
|
73eceefbf0
|
|||
|
3874f6ab2e
|
|||
|
fe3e929898
|
|||
|
c9fb33cd13
|
|||
|
151ff96a25
|
|||
|
9c24b82cf2
|
|||
|
3ece8985ca
|
|||
|
b683a58354
|
|||
|
7e576ce029
|
|||
|
244011e3e4
|
|||
| f9c7d799aa | |||
|
945a99d4ab
|
|||
|
6b8ca9b797
|
|||
|
86121ed4f0
|
|||
|
739c31116e
|
|||
|
cf1e2ed768
|
|||
|
15e2acbbe0
|
|||
|
013cae84af
|
|||
|
45180ae026
|
|||
|
133048364f
|
|||
|
a75e5a3c02
|
|||
| 2a2476cb5e | |||
|
8e146606da
|
|||
| d46e11a51a | |||
|
f85fc8a926
|
|||
| 206db77c8c | |||
|
2d13a4500a
|
|||
|
07d5ee35fc
|
|||
|
289938ec46
|
|||
|
52f58a846a
|
|||
|
dd08807e8e
|
|||
| 63d3598a2c | |||
|
817a67b1ca
|
|||
| 69d349e82c | |||
|
f567cac0b6
|
|||
| 5e7886fd50 | |||
|
56e8571d6d
|
|||
| 121be56e98 | |||
|
f12be561c3
|
|||
|
6f202a0ae5
|
|||
| a56ba496d6 | |||
|
e08b778260
|
|||
|
81c33e9c3c
|
|||
|
131f7e36cd
|
|||
|
f1667c865e
|
|||
|
81895edf3d
|
@@ -1,5 +1,10 @@
|
||||
let build = []
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
build = ['transform-remove-console']
|
||||
}
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
],
|
||||
plugins: [...build]
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"trtc-js-sdk": "^4.6.5",
|
||||
"vod-js-sdk-v6": "^1.4.10",
|
||||
"vue": "^3.0.0-0",
|
||||
"vue-cropper": "^0.5.5",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"vuex": "^4.0.0-0"
|
||||
},
|
||||
@@ -33,6 +34,7 @@
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"@vue/compiler-sfc": "^3.0.0-0",
|
||||
"@vue/eslint-config-typescript": "^5.0.2",
|
||||
"babel-plugin-transform-remove-console": "^6.9.4",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^7.0.0-0",
|
||||
"node-sass": "^4.14.1",
|
||||
|
||||
287
public/CHANGELOG.md
Normal file
@@ -0,0 +1,287 @@
|
||||
# TRTC Web SDK 版本发布日志
|
||||
|
||||
- 版本号规则:[major.minor.patch]
|
||||
- major:主版本号,如有重大版本重构则该字段递增,通常各主版本间接口不兼容。
|
||||
- minor:次版本号,各次版本号间接口保持兼容,如有接口新增或优化则该字段递增。
|
||||
- patch:补丁号,如有功能改善或缺陷修复则该字段递增。
|
||||
- 我们建议您及时更新到最新版本以便获得更好的产品稳定性及在线支持!
|
||||
|
||||
## 4.6.6 (2020-10-23)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 优化上行 peerConnection 重连逻辑
|
||||
- 优化下行 peerConnection 重连逻辑
|
||||
- 优化 TRTC.checkSystemRequirements 检测逻辑
|
||||
- 支持 Safari 屏幕分享,参考:[屏幕分享使用教程](https://trtc-1252463788.file.myqcloud.com/web/docs/tutorial-06-advanced-screencast.html)
|
||||
|
||||
**Bug Fixed**
|
||||
|
||||
- 修复因自动播放策略限制,手动恢复音频播放后,getAudioLevel 值为0的问题
|
||||
|
||||
## 4.6.5(2020-10-14)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 优化 WebSocket 信令通道重连逻辑,提升连接稳定性
|
||||
- 优化日志输出逻辑
|
||||
|
||||
**Bug Fixed**
|
||||
|
||||
- Chrome 重新订阅后,getAudioLevel 接口返回值为0的问题
|
||||
- Safari 重新订阅后,播放无声的问题
|
||||
- 使用 replaceTrack 替换上行音频轨道后,getLocalVideoStats 接口返回 undefined 的问题
|
||||
- 移动设备通话过程中,切换网络类型,偶现 WebSocket 连接断开的问题
|
||||
|
||||
## 4.6.4(2020-9-24)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 退房后停止网络质量统计
|
||||
|
||||
**Bug Fixed**
|
||||
|
||||
- 修复 Chrome 56 进房报错的问题
|
||||
- 修复移动端推旁路出现画面旋转的问题
|
||||
- 修复纯音频推流时云端录制异常的问题
|
||||
- 修复因分辨率不一致导致摄像头拔出后,自动恢复推流失败的问题
|
||||
|
||||
## 4.6.3 (2020-8-28)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 优化兼容性检测逻辑
|
||||
- 优化日志上报逻辑
|
||||
- 优化上行码率控制逻辑
|
||||
|
||||
## 4.6.2 (2020-8-14)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 优化上行码率调控逻辑
|
||||
- 优化 switchRole 参数校验逻辑
|
||||
- 优化上行网络质量计算逻辑
|
||||
- 优化错误提示信息
|
||||
- 检测当前推流采集设备变更时,自动恢复推流状态
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 修复 unpublish 成功后,立即重新 publish 失败报错的问题
|
||||
|
||||
## 4.6.1 (2020-7-28)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- [TRTC.isScreenShareSupported](https://trtc-1252463788.file.myqcloud.com/web/docs/TRTC.html#.isScreenShareSupported) Safari 不支持屏幕分享
|
||||
- 完善 subscribe & unsubscribe 接口的参数校验逻辑
|
||||
- 增加网络质量日志
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 修复当未授权媒体设备,且 TRTC.createStream 接口传入的设备 ID 为空串时,SDK 报 OverconstrainedError 的问题
|
||||
- 修复上行 peerConnection 断开时没有打印日志的问题
|
||||
|
||||
## 4.6.0 (2020-7-16)
|
||||
|
||||
**Feature**
|
||||
|
||||
- 增加 NETWORK_QUALITY 事件
|
||||
|
||||
## 4.5.0 (2020-7-2)
|
||||
|
||||
**Feature**
|
||||
|
||||
- createStream 接口增加 screenAudio 参数
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 修复 Android 浏览器中回声消除不起作用的问题
|
||||
- 修复 getTransportStats 接口返回的 rtt 值为 NAN 的问题
|
||||
|
||||
## 4.4.0 (2020-5-28)
|
||||
|
||||
**Feature**
|
||||
|
||||
- 支持 Chrome >= 74 屏幕分享采集系统(windows)或者当前 Tab 页面(Mac)的声音
|
||||
|
||||
## 4.3.14 (2020-4-29)
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 修复小程序音频 muted unmute 事件。
|
||||
|
||||
## 4.3.13 (2020-4-13)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 优化浏览器可用性检测
|
||||
|
||||
## 4.3.12 (2020-4-13)
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 修复一个潜在的RTCPeerConnection状态变化异常
|
||||
|
||||
## 4.3.11 (2020-3-28)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 增加手机 QQ 浏览器检测,手机 QQ 浏览器暂时无法支持 WebRTC
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 修复 Boolean 返回值类型
|
||||
|
||||
## 4.3.10 (2020-3-17)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 优化环境检测逻辑
|
||||
- RtcError 增加 name code
|
||||
|
||||
## 4.3.9 (2020-3-13)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 增加部署环境自动检测
|
||||
- 优化日志
|
||||
|
||||
## 4.3.8 (2020-2-24)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- createClient 增加 streamId userdefinerecordid 字段
|
||||
|
||||
## 4.3.7 (2020-2-21)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 屏幕分享时切换设备抛出异常。
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 切换设备时释放 MediaStream,解决设备占用问题。
|
||||
- 订阅接口增加处理潜在错误。
|
||||
|
||||
## 4.3.6 (2020-2-5)
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 调整 Stream.resume() 音视频播放顺序,修复 iOS 上微信浏览器自动播放异常问题。
|
||||
|
||||
## 4.3.5 (2020-2-5)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 增加 publish 超时检查,提高信令发送成功率。
|
||||
|
||||
## 4.3.4 (2020-1-6)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 升级 core-js 至 v3.6.1。
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- unpublish 超时后向外部抛出异常事件。
|
||||
- 修复第三方库引起 V8 负优化问题。
|
||||
|
||||
## 4.3.3 (2019-12-25)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 增加主动检测环境是否支持 webrtc 能力。
|
||||
- 优化 sdp 响应机制。
|
||||
- 优化上报逻辑。
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 修复 turn url 协议格式。
|
||||
|
||||
## 4.3.2 (2019-12-09)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 增加下行连接 ICE 断开自动重连机制。
|
||||
- 去除 STUN 打洞环节,增加内网用户连接成功率及提高连接速度。
|
||||
- 日志上报时间戳统一使用服务器校正后的 UTC 时间。
|
||||
- 优化 ICE 错误上报。
|
||||
- 增加更多关键事件上报到 avmonitor 监控。
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 修复 WebSocket 信令通道 1005 异常重连及重连错误处理。
|
||||
- 修复下行丢包率上报问题。
|
||||
|
||||
## 4.3.1 (2019-11-23)
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 增加通话过程中上行链路 ICE 断开自动重连机制。
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 修复 STUN 打洞失败后 host 公网 IP 类型 ICE Candidate 不生效问题。
|
||||
|
||||
## 4.3.0(2019-11-15)
|
||||
|
||||
**Feature**
|
||||
|
||||
增加 Client.getTransportStats() API。
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 增加更详细的上报日志。
|
||||
- 事件解除绑定支持通配符。
|
||||
- 增加连接超时时间至 5s。
|
||||
- 增加发布超时时间至 5s。
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
修复因 zone.js 修改原型链导致 SDK 判断异常的问题。
|
||||
|
||||
## 4.2.0(2019-11-04)
|
||||
|
||||
**Feature**
|
||||
|
||||
- 增加 Client.off() 接口取消客户端事件绑定。
|
||||
|
||||
**Improvement**
|
||||
|
||||
- 通话状态统计优化。
|
||||
- Client.publish() 增加权限检查。
|
||||
- Stream.play()/resume() 增加自动播放错误提示。
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- LocalStream.switchDevice() 切换摄像头黑屏问题修复。
|
||||
|
||||
## 4.1.1(2019-10-24)
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 修复日志丢失问题。
|
||||
- 修复断网重连远端用户丢失问题。
|
||||
|
||||
## 4.1.0(2019-10-17)
|
||||
|
||||
**Feature**
|
||||
|
||||
- Stream.play() 接口支持传入 HTMLDivElement 对象。
|
||||
- 增加音频码率调控设置,开发者可通过 LocalStream.setAudioProfile() 设置音频属性,目前支持两种 Profile:standard 和 high。
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 修复旧版本 Chrome 上的 WebAudio Context 数量受限问题。
|
||||
- 修复 replaceTrack() 未重启本地音视频播放器问题。
|
||||
- 修复 LocalStream.setScreenProfile() 自定义属性设置未生效问题。
|
||||
- 修复 audio/video player 重启及状态上报问题。
|
||||
|
||||
## 4.0.0(2019-10-11)
|
||||
|
||||
TRTC Web SDK (WebRTC) 重构版本,提供 Client/Stream 模式的接口,各对象职责更明确,语义更简洁明了。
|
||||
重构版本与旧版本不兼容,除接口改动之外,还提供如下功能:
|
||||
|
||||
- 视频属性 (分辨率、帧率及码率)控制完全由 App 通过 SDK 的 LocalStream.setVideoProfile() 接口设置,不再支持老版本通过腾讯云控制台的“画面设定 (Spear Role)”。
|
||||
- SDK 在 Stream 对象中封装了音视频播放器,音视频播放完全由 SDK 控制。
|
||||
- 提供远端流的订阅与取消订阅功能,开发者可以通过 Client.subscribe()/unsubscribe() 接口灵活控制远端流的音频、视频或音视频数据流的接收。
|
||||
98
public/README.md
Normal file
@@ -0,0 +1,98 @@
|
||||
本文主要介绍如何快速运行腾讯云 TRTC Web SDK Demo。
|
||||
|
||||
## 支持的平台
|
||||
|
||||
WebRTC 技术由 Google 最先提出,目前主要在桌面版 Chrome 浏览器、桌面版 Safari 浏览器以及移动版的 Safari 浏览器上有较为完整的支持,其他平台(例如 Android 平台的浏览器)支持情况均比较差。
|
||||
- 在移动端推荐使用 [小程序](https://cloud.tencent.com/document/product/647/32399) 解决方案,微信和手机 QQ 小程序均已支持,都是由各平台的 Native 技术实现,音视频性能更好,且针对主流手机品牌进行了定向适配。
|
||||
- 如果您的应用场景主要为教育场景,那么教师端推荐使用稳定性更好的 [Electron](https://cloud.tencent.com/document/product/647/38549) 解决方案,支持大小双路画面,更灵活的屏幕分享方案以及更强大而弱网络恢复能力。
|
||||
|
||||
| 操作系统 | 浏览器类型 | 浏览器最低版本要求 | 接收(播放) | 发送(上麦) | 屏幕分享 |
|
||||
| :------: | :------------------: | :----------------: | :----------: | :----------: | :-----------------------: |
|
||||
| Mac OS | 桌面版 Safari 浏览器 | 11+ | 支持 | 支持 | 不支持 |
|
||||
| Mac OS | 桌面版 Chrome 浏览器 | 56+ | 支持 | 支持 | 支持(需要chrome72+版本) |
|
||||
| Windows | 桌面版 Chrome 浏览器 | 56+ | 支持 | 支持 | 支持(需要chrome72+版本) |
|
||||
| Windows | 桌面版 QQ 浏览器 | 10.4 | 支持 | 支持 | 不支持 |
|
||||
| iOS | 移动版 Safari 浏览器 | 11.1.2 | 支持 | 支持 | 不支持 |
|
||||
| iOS | 微信内嵌网页 | 12.1.4 | 支持 | 不支持 | 不支持 |
|
||||
| Android | 移动版 QQ 浏览器 | - | 不支持 | 不支持 | 不支持 |
|
||||
| Android | 移动版 UC 浏览器 | - | 不支持 | 不支持 | 不支持 |
|
||||
| Android | 微信内嵌网页 | - | 不支持 | 不支持 | 不支持 |
|
||||
|
||||
>!
|
||||
>- 您可以在浏览器中打开 [WebRTC 能力测试](https://www.qcloudtrtc.com/webrtc-samples/abilitytest/index.html) 页面进行检测是否完整支持 WebRTC。例如公众号等浏览器环境。
|
||||
>- 由于 H.264 版权限制,华为系统的 Chrome 浏览器和以 Chrome WebView 为内核的浏览器均不支持 TRTC 的 Web 版 SDK 的正常运行。
|
||||
|
||||
<span id="requirements"></span>
|
||||
## 环境要求
|
||||
- 请使用最新版本的 Chrome 浏览器。
|
||||
- TRTC Web SDK 依赖以下端口进行数据传输,请将其加入防火墙白名单,配置完成后,您可以通过访问并体验 [官网 Demo](https://trtc-1252463788.file.myqcloud.com/web/demo/official-demo/index.html) 检查配置是否生效。
|
||||
- TCP 端口:8687
|
||||
- UDP 端口:8000;8080;8800;843;443;16285
|
||||
- 域名:qcloud.rtc.qq.com
|
||||
|
||||
## 前提条件
|
||||
您已 [注册腾讯云](https://cloud.tencent.com/document/product/378/17985) 账号,并完成 [实名认证](https://cloud.tencent.com/document/product/378/3629)。
|
||||
|
||||
## 操作步骤
|
||||
<span id="step1"></span>
|
||||
### 步骤1:创建新的应用
|
||||
1. 登录实时音视频控制台,选择【开发辅助】>【[快速跑通Demo](https://console.cloud.tencent.com/trtc/quickstart)】。
|
||||
2. 单击【立即开始】,输入应用名称,例如`TestTRTC`,单击【创建应用】。
|
||||
|
||||
<span id="step2"></span>
|
||||
### 步骤2:下载 SDK 和 Demo 源码
|
||||
1. 鼠标移动至对应卡片,单击【[Github](https://github.com/tencentyun/TRTCSDK/tree/master/Web/TRTCSimpleDemo)】跳转至 Github(或单击【[ZIP](https://liteavsdk-1252463788.cos.ap-guangzhou.myqcloud.com/H5_latest.zip?_ga=1.195966252.185644906.1567570704)】),下载相关 SDK 及配套的 Demo 源码。
|
||||

|
||||
2. 下载完成后,返回实时音视频控制台,单击【我已下载,下一步】,可以查看 SDKAppID 和密钥信息。
|
||||
|
||||
<span id="step3"></span>
|
||||
### 步骤3:配置 Demo 工程文件
|
||||
1. 解压 [步骤2](#step2) 中下载的源码包。
|
||||
2. 找到并打开`Web/TRTCSimpleDemo/js/debug/GenerateTestUserSig.js`文件。
|
||||
3. 设置`GenerateTestUserSig.js`文件中的相关参数:
|
||||
<ul><li>SDKAPPID:默认为0,请设置为实际的 SDKAppID。</li>
|
||||
<li>SECRETKEY:默认为空字符串,请设置为实际的密钥信息。</li></ul>
|
||||
<img src="https://main.qcloudimg.com/raw/1732ea2401af6111b41259a78b5330a4.png">
|
||||
4. 返回实时音视频控制台,单击【粘贴完成,下一步】。
|
||||
5. 单击【关闭指引,进入控制台管理应用】。
|
||||
|
||||
>!本文提到的生成 UserSig 的方案是在客户端代码中配置 SECRETKEY,该方法中 SECRETKEY 很容易被反编译逆向破解,一旦您的密钥泄露,攻击者就可以盗用您的腾讯云流量,因此**该方法仅适合本地跑通 Demo 和功能调试**。
|
||||
>正确的 UserSig 签发方式是将 UserSig 的计算代码集成到您的服务端,并提供面向 App 的接口,在需要 UserSig 时由您的 App 向业务服务器发起请求获取动态 UserSig。更多详情请参见 [服务端生成 UserSig](https://cloud.tencent.com/document/product/647/17275#Server)。
|
||||
|
||||
### 步骤4:运行 Demo
|
||||
使用 Chrome 浏览器打开 Demo 根目录下的`index.html`文件即可运行 Demo。
|
||||
|
||||
>!
|
||||
> - 一般情况下体验 Demo 需要部署至服务器,通过`https://域名/xxx`访问,或者直接在本地搭建服务器,通过`localhost:端口`访问。
|
||||
> - 目前桌面端 Chrome 浏览器支持 TRTC Web SDK 的相关特性比较完整,因此建议使用 Chrome 浏览器进行体验。
|
||||
|
||||
Demo 运行界面如图所示:
|
||||

|
||||
- 单击【加入房间】加入音视频通话房间并且发布本地音视频流。
|
||||
您可以打开多个页面,每个页面都单击 【加入房间】,正常情况下可以看到多个画面并模拟实时音视频通话。
|
||||
- 单击摄像头图标可以选择摄像头设备。
|
||||
- 单击麦克风图表可以选择麦克风设备。
|
||||
|
||||
>?WebRTC 需要使用摄像头和麦克风采集音视频,在体验过程中您可能会收到来自 Chrome 浏览器的相关提示,单击【允许】。
|
||||
> 
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 1. 查看密钥时只能获取公钥和私钥信息,要如何获取密钥?
|
||||
TRTC SDK 6.6 版本(2019年08月)开始启用新的签名算法 HMAC-SHA256。在此之前已创建的应用,需要先升级签名算法才能获取新的加密密钥。如不升级,您也可以继续使用 [老版本算法 ECDSA-SHA256](https://cloud.tencent.com/document/product/647/17275#.E8.80.81.E7.89.88.E6.9C.AC.E7.AE.97.E6.B3.95)。
|
||||
|
||||
升级操作:
|
||||
1. 登录 [实时音视频控制台](https://console.cloud.tencent.com/trtc)。
|
||||
2. 在左侧导航栏选择【应用管理】,单击目标应用所在行的【应用信息】。
|
||||
3. 选择【快速上手】页签,单击【第二步 获取签发UserSig的密钥】区域的【点此升级】。
|
||||
|
||||
### 2. 出现客户端错误:“RtcError: no valid ice candidate found”该如何处理?
|
||||
出现该错误说明 TRTC Web SDK 在 STUN 打洞失败,请根据 [环境要求](#requirements) 检查防火墙配置。
|
||||
|
||||
### 3. 出现客户端错误:"RtcError: ICE/DTLS Transport connection failed" 或 “RtcError: DTLS Transport connection timeout”该如何处理?
|
||||
出现该错误说明 TRTC Web SDK 在建立媒体传输通道时失败,请根据 [环境要求](#requirements) 检查防火墙配置。
|
||||
|
||||
### 4. 出现10006 error 该如何处理?
|
||||
如果出现"Join room failed result: 10006 error: service is suspended,if charge is overdue,renew it",请确认您的实时音视频应用的服务状态是否为可用状态。
|
||||
登录 [实时音视频控制台](https://console.cloud.tencent.com/rav),单击您创建的应用,单击【帐号信息】,在帐号信息面板即可确认服务状态。
|
||||

|
||||
8
public/css/bootstrap-material-design.min.css
vendored
Normal file
530
public/css/index.css
Normal file
@@ -0,0 +1,530 @@
|
||||
*{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body{
|
||||
font-family: PingFangSC-Regular !important;
|
||||
}
|
||||
html{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.icon {
|
||||
width: 1em; height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
button{
|
||||
margin: 0 !important;
|
||||
color: #fff !important;
|
||||
background-color: #006EFF !important
|
||||
}
|
||||
div{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.row-div{
|
||||
flex-direction: row;
|
||||
}
|
||||
.col-div{
|
||||
flex-direction: column;
|
||||
}
|
||||
#root{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgb(250, 250, 250);
|
||||
position: absolute;
|
||||
display: block;
|
||||
}
|
||||
#login-root{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
#login-card{
|
||||
width: 450px;
|
||||
height: 500px;
|
||||
display: flex;
|
||||
/* justify-content: center; */
|
||||
align-items: center;
|
||||
background-color: white;
|
||||
}
|
||||
.login-card{
|
||||
width: 360px;
|
||||
height: 450px;
|
||||
background-color: white;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
/* justify-content: center; */
|
||||
align-items: center;
|
||||
}
|
||||
.popover{
|
||||
min-width: 300px;
|
||||
max-width: 1000px;
|
||||
border: 0;
|
||||
white-space: nowrap;
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
.popover-body{
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
}
|
||||
.popover-body>div{
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
justify-content: center;
|
||||
cursor: default;
|
||||
}
|
||||
.popover-body>div:hover{
|
||||
background-color: #F7F7F7;
|
||||
}
|
||||
|
||||
.icon-gray{
|
||||
color: #bfbfbf;
|
||||
}
|
||||
.icon-normal{
|
||||
color: #515151;
|
||||
}
|
||||
.icon-blue{
|
||||
color: #006EFF;
|
||||
}
|
||||
.device-testing-btn{
|
||||
color: #515151;
|
||||
cursor: pointer;
|
||||
margin-top: -14px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.device-connect-list{
|
||||
width: 310px;
|
||||
height: 70px;
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||
justify-content: space-around;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.device-connect-list::before{
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 8px transparent solid;
|
||||
border-top-color: rgba(0, 0, 0, 0.2);
|
||||
opacity: 0.6;
|
||||
position: absolute;
|
||||
bottom: -16px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.device-connect-list::after{
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 7px transparent solid;
|
||||
border-top-color: #fff;
|
||||
position: absolute;
|
||||
bottom: -14px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.connect{
|
||||
width: 28px;
|
||||
height: 64px;
|
||||
font-size: 28px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.device-icon{
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 3px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
#device-testing-root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-self: center;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.device-testing-card{
|
||||
width: 640px;
|
||||
height: 480px;
|
||||
background: #F7F7F7;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
.device-testing-prepare,.device-testing,.device-testing-report{
|
||||
display: block;
|
||||
}
|
||||
.testing-title{
|
||||
font-size: 34px;
|
||||
justify-content: center;
|
||||
margin-top: 55px;
|
||||
color: #201e1ee5;
|
||||
}
|
||||
.testing-prepare-info{
|
||||
font-size: 16px;
|
||||
justify-content: center;
|
||||
margin-top: 25px;
|
||||
color: #585656e5;
|
||||
}
|
||||
.device-testing-close-btn{
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
}
|
||||
.device-display{
|
||||
margin-top: 40px;
|
||||
justify-content: center;
|
||||
}
|
||||
.device{
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
font-size: 38px;
|
||||
}
|
||||
.device:not(:first-child){
|
||||
margin-left: 60px;
|
||||
}
|
||||
.device:not(:first-child).safari{
|
||||
margin-left: 100px;
|
||||
}
|
||||
.device::before{
|
||||
content: '';
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
position: absolute;
|
||||
bottom: -34px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.connect-success::before{
|
||||
background: url('../img/success.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.connect-fail::before{
|
||||
background: url('../img/fail.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
@keyframes device-loading {
|
||||
0%{
|
||||
width: 0%;
|
||||
border-radius: 6px 0 0 6px;
|
||||
}
|
||||
50%{
|
||||
width: 50%;
|
||||
border-radius: 6px 0 0 6px;
|
||||
}
|
||||
100%{
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
.loading-background{
|
||||
width: 350px;
|
||||
height: 3px;
|
||||
border-radius: 6px;
|
||||
margin: 20px auto 0;
|
||||
background: #bfbfbf;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.device-loading{
|
||||
height: 3px;
|
||||
background-color: #808080;
|
||||
animation: device-loading 2s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
.connect-info{
|
||||
margin-top: 60px;
|
||||
display: flex;
|
||||
height: 48px;
|
||||
justify-content: center;
|
||||
}
|
||||
.connect-attention-container{
|
||||
position: relative;
|
||||
margin-left: 3px;
|
||||
}
|
||||
.connect-attention-icon{
|
||||
font-size: 20px;
|
||||
color: red;
|
||||
}
|
||||
.connect-attention-info{
|
||||
padding: 8px 12px;
|
||||
min-width: 120px;
|
||||
min-height: 50px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
border-radius: 10px;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 100%;
|
||||
transform: translate(20px, -10px);
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
}
|
||||
.connect-attention-info::after{
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 10px transparent solid;
|
||||
border-top-color: rgba(0, 0, 0, 0.6);
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 100%;
|
||||
transform: translateX(-40px);
|
||||
}
|
||||
.testing-btn-display{
|
||||
justify-content: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
.test-btn{
|
||||
width: 200px;
|
||||
height: 44px;
|
||||
background: #006EFF;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.start-gray{
|
||||
background: #dddddd;
|
||||
color: #fff;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.device-testing-title{
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
margin-top: 40px;
|
||||
}
|
||||
.testing{
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.testing:not(:first-child){
|
||||
margin-left: 90px;
|
||||
}
|
||||
.testing:not(:first-child)::before {
|
||||
content: '';
|
||||
width: 70px;
|
||||
height: 2px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: -80px;
|
||||
background: #bfbfbf;
|
||||
}
|
||||
.testing:not(:first-child).safari{
|
||||
margin-left: 150px;
|
||||
}
|
||||
.testing:not(:first-child).safari::before{
|
||||
width: 130px;
|
||||
left: -140px;
|
||||
}
|
||||
.testing.complete {
|
||||
cursor: pointer;
|
||||
}
|
||||
.testing.complete:not(:first-child)::before {
|
||||
background: #006EFF;
|
||||
}
|
||||
.testing-body{
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
.device-list{
|
||||
margin-left: 140px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
.device-select{
|
||||
width: 260px;
|
||||
height: 30px;
|
||||
margin-left: 20px;
|
||||
padding: 0 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.camera-video{
|
||||
width: 300px;
|
||||
height: 180px;
|
||||
display: block;
|
||||
margin: 30px auto 0;
|
||||
}
|
||||
.testing-info-container{
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
margin-top: 24px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.testing-info{
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
.button-list{
|
||||
margin-top: 20px;
|
||||
width: 300px;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.fail-button{
|
||||
border: 1px solid #006EFF;
|
||||
border-radius: 8px;
|
||||
color: #006EFF;
|
||||
padding: 6px 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.success-button{
|
||||
border: 1px solid #006EFF;
|
||||
border-radius: 8px;
|
||||
background: #006EFF;
|
||||
color: #fff;
|
||||
padding: 6px 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.audio-control{
|
||||
width: 320px;
|
||||
display: block;
|
||||
margin: 40px auto 0;
|
||||
}
|
||||
.audio-control-info{
|
||||
margin: 0px auto 20px 6px;
|
||||
color: #5f5f5f;
|
||||
}
|
||||
.mic-testing-container{
|
||||
display: block;
|
||||
margin-top: 30px;
|
||||
}
|
||||
.mic-testing-info{
|
||||
margin-left: 140px;
|
||||
color: #bbbbbb;
|
||||
font-size: 14px;
|
||||
}
|
||||
.mic-bar-container{
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.mic-bar{
|
||||
width: 10px;
|
||||
height: 30px;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 1px;
|
||||
}
|
||||
.mic-bar:not(:first-child){
|
||||
margin-left: 3px;
|
||||
}
|
||||
.mic-bar.active{
|
||||
background: #006EFF;
|
||||
}
|
||||
.testing-index-list{
|
||||
margin-top: 40px;
|
||||
display: block;
|
||||
}
|
||||
.testing-index-group{
|
||||
width: 55%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 14px;
|
||||
margin: 10px auto 0;
|
||||
}
|
||||
@keyframes loading-circle{
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
25% {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
50% {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
75% {
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.network-loading::before{
|
||||
content: '';
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url('../img/loading.png');
|
||||
background-size: 100% 100%;
|
||||
animation: loading-circle 2s linear infinite;
|
||||
}
|
||||
.testing-footer{
|
||||
margin-top: 70px;
|
||||
justify-content: center;
|
||||
}
|
||||
.device-report-list{
|
||||
display: block;
|
||||
margin-top: 40px;
|
||||
}
|
||||
.device-report{
|
||||
width: 60%;
|
||||
margin: 20px auto 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.report-icon{
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 20px;
|
||||
justify-content: center;
|
||||
font-size: 22px;
|
||||
line-height: 22px;
|
||||
color: #515151;
|
||||
}
|
||||
.device-name{
|
||||
width: 280px;
|
||||
height: 24px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.device-report-footer{
|
||||
margin-top: 50px;
|
||||
justify-content: center;
|
||||
}
|
||||
.device-report-btn{
|
||||
width: 160px;
|
||||
height: 40px;
|
||||
border: 1px solid;
|
||||
border-radius: 6px;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.testing-agin{
|
||||
border-color: #006EFF;
|
||||
color: #006EFF;
|
||||
}
|
||||
.testing-finish{
|
||||
background: #006EFF;
|
||||
color: #fff;
|
||||
margin-left: 60px;
|
||||
}
|
||||
92
public/css/room.css
Normal file
@@ -0,0 +1,92 @@
|
||||
*{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
html{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
body{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #F5F5F5;
|
||||
font-family: Futura,sans-serif;
|
||||
}
|
||||
video{
|
||||
background-color: #d8d8d8;
|
||||
}
|
||||
/* @media screen and (min-width:960px) {
|
||||
video{
|
||||
width: auto !important;
|
||||
height: 100% !important;
|
||||
position: inherit !important;
|
||||
}
|
||||
.video-div{
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width:960px) {
|
||||
video{
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
position: inherit !important;
|
||||
}
|
||||
.video-div{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
} */
|
||||
#room-root{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 1500px;
|
||||
min-height: 700px;
|
||||
display: none;
|
||||
align-items: flex-start;
|
||||
background-color: #f0f0f0
|
||||
}
|
||||
.member{
|
||||
cursor: default;
|
||||
border-bottom-style: solid;
|
||||
border-width: 1px;
|
||||
border-bottom-color: #f0f0f0;
|
||||
}
|
||||
#video-grid{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
grid-template-rows: repeat(3, 1fr);
|
||||
grid-template-areas: 'm m m a b'
|
||||
'm m m c d'
|
||||
'e f g h i';
|
||||
}
|
||||
#video-grid>div{
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
}
|
||||
#main-video{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
grid-area: 1/1/3/4;
|
||||
}
|
||||
.video-box{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.mask{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
font-size: 14px;
|
||||
color: #888888;
|
||||
z-index: 9;
|
||||
justify-content: center
|
||||
}
|
||||
div[id^=player] {
|
||||
background-color: #d8d8d8 !important;
|
||||
}
|
||||
BIN
public/img/big-camera-off.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
public/img/big-camera-on.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/img/big-mic-off.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
public/img/big-mic-on.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/img/camera-max.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/img/camera-off.png
Normal file
|
After Width: | Height: | Size: 794 B |
BIN
public/img/camera-on.png
Normal file
|
After Width: | Height: | Size: 774 B |
BIN
public/img/camera.png
Normal file
|
After Width: | Height: | Size: 796 B |
BIN
public/img/code.jpg
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
public/img/fail.png
Normal file
|
After Width: | Height: | Size: 533 B |
BIN
public/img/loading.png
Normal file
|
After Width: | Height: | Size: 701 B |
BIN
public/img/logo.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
public/img/logout.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
public/img/mic-off.png
Normal file
|
After Width: | Height: | Size: 800 B |
BIN
public/img/mic-on.png
Normal file
|
After Width: | Height: | Size: 761 B |
BIN
public/img/mic.png
Normal file
|
After Width: | Height: | Size: 809 B |
BIN
public/img/screen-off.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
public/img/screen-on.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
public/img/shot.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
public/img/success.png
Normal file
|
After Width: | Height: | Size: 569 B |
@@ -7,6 +7,15 @@
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<link href="https://imgcache.qq.com/open/qcloud/video/tcplayer/tcplayer.css" rel="stylesheet">
|
||||
<!-- 如需在IE8、9浏览器中初始化播放器,浏览器需支持Flash并在页面中引入 -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//imgcache.qq.com/open/qcloud/video/tcplayer/ie8/videojs-ie8.js"></script>
|
||||
<![endif]-->
|
||||
<!-- 如果需要在 Chrome Firefox 等现代浏览器中通过H5播放hls,需要引入 hls.js -->
|
||||
<script src="https://imgcache.qq.com/open/qcloud/video/tcplayer/lib/hls.min.0.8.8.js"></script>
|
||||
<!-- 引入播放器 js 文件 -->
|
||||
<script src="https://imgcache.qq.com/open/qcloud/video/tcplayer/tcplayer.min.js"></script>
|
||||
<script>
|
||||
//designWidth:设计稿的实际宽度值,需要根据实际设置
|
||||
//maxWidth:制作稿的最大宽度值,需要根据实际设置
|
||||
@@ -70,7 +79,7 @@
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
z-index: 1001;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
6939
public/js/bootstrap-material-design.js
vendored
Normal file
656
public/js/common.js
Normal file
@@ -0,0 +1,656 @@
|
||||
/* eslint-disable no-cond-assign */
|
||||
/* global $ TRTC presetting RtcClient ShareClient */
|
||||
/* eslint-disable require-jsdoc */
|
||||
let isCamOn = true;
|
||||
let isMicOn = true;
|
||||
let isScreenOn = false;
|
||||
let isJoined = true;
|
||||
let rtc = null;
|
||||
let share = null;
|
||||
let shareUserId = '';
|
||||
let cameraId = '';
|
||||
let micId = '';
|
||||
|
||||
function login() {
|
||||
|
||||
presetting.login(false, options => {
|
||||
rtc = new RtcClient(options);
|
||||
join();
|
||||
});
|
||||
presetting.login(true, options => {
|
||||
shareUserId = options.userId;
|
||||
share = new ShareClient(options);
|
||||
});
|
||||
}
|
||||
$(function (){
|
||||
login();
|
||||
|
||||
})
|
||||
function join() {
|
||||
rtc.join();
|
||||
$('#login-root').hide();
|
||||
$('#room-root').show();
|
||||
$('#header-roomId').html('房间号: ' + $('#roomId').val());
|
||||
$('#member-me')
|
||||
.find('.member-id')
|
||||
.html($('#userId').val() + '(我)');
|
||||
}
|
||||
|
||||
function leave() {
|
||||
$('#mask_main').appendTo($('#main-video'));
|
||||
rtc.leave();
|
||||
share.leave();
|
||||
}
|
||||
|
||||
function publish() {
|
||||
rtc.publish();
|
||||
}
|
||||
|
||||
function unpublish() {
|
||||
rtc.unpublish();
|
||||
}
|
||||
|
||||
function muteAudio() {
|
||||
rtc.muteLocalAudio();
|
||||
}
|
||||
|
||||
function unmuteAudio() {
|
||||
rtc.unmuteLocalAudio();
|
||||
}
|
||||
|
||||
function muteVideo() {
|
||||
$('#mask_main').show();
|
||||
rtc.muteLocalVideo();
|
||||
}
|
||||
|
||||
function unmuteVideo() {
|
||||
rtc.unmuteLocalVideo();
|
||||
$('#mask_main').hide();
|
||||
}
|
||||
|
||||
function startSharing() {
|
||||
share.join();
|
||||
}
|
||||
|
||||
function stopSharing() {
|
||||
share.leave();
|
||||
}
|
||||
|
||||
function setBtnClickFuc() {
|
||||
//userid roomid规格
|
||||
//$('#userId').on('input', function(e) {
|
||||
// e.preventDefault();
|
||||
// console.log('userId input ' + e.target.value);
|
||||
// let val = $('#userId').val().slice(5);
|
||||
// $('#userId').val('user_'+val.replace(/[^\d]/g,''));
|
||||
//});
|
||||
$('#roomId').on('input', function(e) {
|
||||
e.preventDefault();
|
||||
console.log('roomId input ' + e.target.value);
|
||||
let val = $('#roomId').val();
|
||||
$('#roomId').val(val.replace(/[^\d]/g, ''));
|
||||
});
|
||||
//login
|
||||
$('#login-btn').click(() => {
|
||||
login();
|
||||
});
|
||||
//open or close camera
|
||||
$('#video-btn').on('click', () => {
|
||||
if (isCamOn) {
|
||||
$('#video-btn').attr('src', './img/big-camera-off.png');
|
||||
$('#video-btn').attr('title', '打开摄像头');
|
||||
$('#member-me')
|
||||
.find('.member-video-btn')
|
||||
.attr('src', 'img/camera-off.png');
|
||||
isCamOn = false;
|
||||
muteVideo();
|
||||
} else {
|
||||
$('#video-btn').attr('src', './img/big-camera-on.png');
|
||||
$('#video-btn').attr('title', '关闭摄像头');
|
||||
$('#member-me')
|
||||
.find('.member-video-btn')
|
||||
.attr('src', 'img/camera-on.png');
|
||||
isCamOn = true;
|
||||
unmuteVideo();
|
||||
}
|
||||
});
|
||||
//open or close microphone
|
||||
$('#mic-btn').on('click', () => {
|
||||
if (isMicOn) {
|
||||
$('#mic-btn').attr('src', './img/big-mic-off.png');
|
||||
$('#mic-btn').attr('title', '打开麦克风');
|
||||
$('#member-me')
|
||||
.find('.member-audio-btn')
|
||||
.attr('src', 'img/mic-off.png');
|
||||
isMicOn = false;
|
||||
muteAudio();
|
||||
} else {
|
||||
$('#mic-btn').attr('src', './img/big-mic-on.png');
|
||||
$('#mic-btn').attr('title', '关闭麦克风');
|
||||
$('#member-me')
|
||||
.find('.member-audio-btn')
|
||||
.attr('src', 'img/mic-on.png');
|
||||
isMicOn = true;
|
||||
unmuteAudio();
|
||||
}
|
||||
});
|
||||
//share screen or not
|
||||
$('#screen-btn').on(
|
||||
'click',
|
||||
throttle(() => {
|
||||
if (!TRTC.isScreenShareSupported()) {
|
||||
alert('当前浏览器不支持屏幕分享!');
|
||||
return;
|
||||
}
|
||||
if ($('#screen-btn').attr('src') == './img/screen-on.png') {
|
||||
$('#screen-btn').attr('src', './img/screen-off.png');
|
||||
stopSharing();
|
||||
isScreenOn = false;
|
||||
} else {
|
||||
$('#screen-btn').attr('src', './img/screen-on.png');
|
||||
startSharing();
|
||||
isScreenOn = true;
|
||||
}
|
||||
}, 2000)
|
||||
);
|
||||
//logout
|
||||
$('#logout-btn').on('click', () => {
|
||||
leave();
|
||||
$('#room-root').hide();
|
||||
$('#login-root').show();
|
||||
});
|
||||
//switch main video
|
||||
$('#main-video').on('click', () => {
|
||||
let mainVideo = $('.video-box').first();
|
||||
if ($('#main-video').is(mainVideo)) {
|
||||
return;
|
||||
}
|
||||
//释放main-video grid-area
|
||||
mainVideo.css('grid-area', 'auto/auto/auto/auto');
|
||||
exchangeView($('#main-video'), mainVideo);
|
||||
//将video-grid中第一个div设为main-video
|
||||
$('.video-box')
|
||||
.first()
|
||||
.css('grid-area', '1/1/3/4');
|
||||
//chromeM71以下会自动暂停,手动唤醒
|
||||
if (getBroswer().broswer == 'Chrome' && getBroswer().version < '72') {
|
||||
rtc.resumeStreams();
|
||||
}
|
||||
});
|
||||
|
||||
//chrome60以下不支持popover,防止error
|
||||
if (getBroswer().broswer == 'Chrome' && getBroswer().version < '60') return;
|
||||
//开启popover
|
||||
$(function() {
|
||||
$('[data-toggle="popover"]').popover();
|
||||
});
|
||||
$('#camera').popover({
|
||||
html: true,
|
||||
content: () => {
|
||||
return $('#camera-option').html();
|
||||
}
|
||||
});
|
||||
$('#microphone').popover({
|
||||
html: true,
|
||||
content: () => {
|
||||
return $('#mic-option').html();
|
||||
}
|
||||
});
|
||||
|
||||
$('#camera').on('click', () => {
|
||||
$('#microphone').popover('hide');
|
||||
$('.popover-body')
|
||||
.find('div')
|
||||
.attr('onclick', 'setCameraId(this)');
|
||||
});
|
||||
|
||||
$('#microphone').on('click', () => {
|
||||
$('#camera').popover('hide');
|
||||
$('.popover-body')
|
||||
.find('div')
|
||||
.attr('onclick', 'setMicId(this)');
|
||||
});
|
||||
|
||||
//点击body关闭popover
|
||||
$('body').click(() => {
|
||||
$('#camera').popover('hide');
|
||||
$('#microphone').popover('hide');
|
||||
});
|
||||
|
||||
//popover事件
|
||||
$('#camera').on('show.bs.popover', () => {
|
||||
$('#camera').attr('src', './img/camera-on.png');
|
||||
});
|
||||
$('#camera').on('hide.bs.popover', () => {
|
||||
$('#camera').attr('src', './img/camera.png');
|
||||
});
|
||||
|
||||
$('#microphone').on('show.bs.popover', () => {
|
||||
$('#microphone').attr('src', './img/mic-on.png');
|
||||
});
|
||||
$('#microphone').on('hide.bs.popover', () => {
|
||||
$('#microphone').attr('src', './img/mic.png');
|
||||
});
|
||||
}
|
||||
|
||||
function setCameraId(thisDiv) {
|
||||
cameraId = $(thisDiv).attr('id');
|
||||
console.log('setCameraId: ' + cameraId);
|
||||
}
|
||||
|
||||
function setMicId(thisDiv) {
|
||||
micId = $(thisDiv).attr('id');
|
||||
console.log('setMicId: ' + micId);
|
||||
}
|
||||
|
||||
function addVideoView(id, isLocal = false) {
|
||||
let div = $('<div/>', {
|
||||
id: id,
|
||||
class: 'video-box',
|
||||
style: 'justify-content: center'
|
||||
});
|
||||
div.appendTo('#video-grid');
|
||||
//设置监听
|
||||
div.click(() => {
|
||||
let mainVideo = $('.video-box').first();
|
||||
if (div.is(mainVideo)) {
|
||||
return;
|
||||
}
|
||||
//释放main-video grid-area
|
||||
mainVideo.css('grid-area', 'auto/auto/auto/auto');
|
||||
exchangeView(div, mainVideo);
|
||||
//将video-grid中第一个div设为main-video
|
||||
$('.video-box')
|
||||
.first()
|
||||
.css('grid-area', '1/1/3/4');
|
||||
//chromeM71以下会自动暂停,手动唤醒
|
||||
if (getBroswer().broswer == 'Chrome' && getBroswer().version < '72') {
|
||||
rtc.resumeStreams();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addMemberView(id) {
|
||||
let memberElm = $('#member-me').clone();
|
||||
memberElm.attr('id', id);
|
||||
memberElm.find('div.member-id').html(id);
|
||||
memberElm.css('display', 'flex');
|
||||
memberElm.appendTo($('#member-list'));
|
||||
}
|
||||
|
||||
function removeView(id) {
|
||||
if ($('#' + id)[0]) {
|
||||
$('#' + id).remove();
|
||||
//将video-grid中第一个div设为main-video
|
||||
$('.video-box')
|
||||
.first()
|
||||
.css('grid-area', '1/1/3/4');
|
||||
}
|
||||
}
|
||||
|
||||
function exchangeView(a, b) {
|
||||
var $div1 = $(a);
|
||||
var $div3 = $(b);
|
||||
var $temobj1 = $('<div></div>');
|
||||
var $temobj2 = $('<div></div>');
|
||||
$temobj1.insertBefore($div1);
|
||||
$temobj2.insertBefore($div3);
|
||||
$div1.insertAfter($temobj2);
|
||||
$div3.insertAfter($temobj1);
|
||||
$temobj1.remove();
|
||||
$temobj2.remove();
|
||||
}
|
||||
|
||||
function isPC() {
|
||||
var userAgentInfo = navigator.userAgent;
|
||||
var Agents = new Array('Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod');
|
||||
var flag = true;
|
||||
for (var v = 0; v < Agents.length; v++) {
|
||||
if (userAgentInfo.indexOf(Agents[v]) > 0) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
function getCameraId() {
|
||||
console.log('selected cameraId: ' + cameraId);
|
||||
return cameraId;
|
||||
}
|
||||
|
||||
function getMicrophoneId() {
|
||||
console.log('selected microphoneId: ' + micId);
|
||||
return micId;
|
||||
}
|
||||
|
||||
function throttle(func, delay) {
|
||||
var timer = null;
|
||||
var startTime = Date.now();
|
||||
return function() {
|
||||
var curTime = Date.now();
|
||||
var remaining = delay - (curTime - startTime);
|
||||
var context = this;
|
||||
var args = arguments;
|
||||
clearTimeout(timer);
|
||||
if (remaining <= 0) {
|
||||
func.apply(context, args);
|
||||
startTime = Date.now();
|
||||
} else {
|
||||
timer = setTimeout(() => {
|
||||
console.log('duplicate click');
|
||||
}, remaining);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function resetView() {
|
||||
isCamOn = true;
|
||||
isMicOn = true;
|
||||
isScreenOn = false;
|
||||
isJoined = true;
|
||||
$('#main-video-btns').hide();
|
||||
$('#video-btn').attr('src', './img/big-camera-on.png');
|
||||
$('#mic-btn').attr('src', './img/big-mic-on.png');
|
||||
$('#screen-btn').attr('src', './img/screen-off.png');
|
||||
$('#member-me')
|
||||
.find('.member-video-btn')
|
||||
.attr('src', 'img/camera-on.png');
|
||||
$('#member-me')
|
||||
.find('.member-audio-btn')
|
||||
.attr('src', 'img/mic-on.png');
|
||||
$('.mask').hide();
|
||||
//清空member-list
|
||||
if ($('#member-list')) {
|
||||
$('#member-list')
|
||||
.find('.member')
|
||||
.each((index, element) => {
|
||||
if (
|
||||
$(element)
|
||||
.parent()
|
||||
.attr('id') != 'member-me'
|
||||
) {
|
||||
$(element)
|
||||
.parent()
|
||||
.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getBroswer() {
|
||||
var sys = {};
|
||||
var ua = navigator.userAgent.toLowerCase();
|
||||
var s;
|
||||
(s = ua.match(/edge\/([\d.]+)/))
|
||||
? (sys.edge = s[1])
|
||||
: (s = ua.match(/rv:([\d.]+)\) like gecko/))
|
||||
? (sys.ie = s[1])
|
||||
: (s = ua.match(/msie ([\d.]+)/))
|
||||
? (sys.ie = s[1])
|
||||
: (s = ua.match(/firefox\/([\d.]+)/))
|
||||
? (sys.firefox = s[1])
|
||||
: (s = ua.match(/chrome\/([\d.]+)/))
|
||||
? (sys.chrome = s[1])
|
||||
: (s = ua.match(/opera.([\d.]+)/))
|
||||
? (sys.opera = s[1])
|
||||
: (s = ua.match(/version\/([\d.]+).*safari/))
|
||||
? (sys.safari = s[1])
|
||||
: 0;
|
||||
|
||||
if (sys.edge) return { broswer: 'Edge', version: sys.edge };
|
||||
if (sys.ie) return { broswer: 'IE', version: sys.ie };
|
||||
if (sys.firefox) return { broswer: 'Firefox', version: sys.firefox };
|
||||
if (sys.chrome) return { broswer: 'Chrome', version: sys.chrome };
|
||||
if (sys.opera) return { broswer: 'Opera', version: sys.opera };
|
||||
if (sys.safari) return { broswer: 'Safari', version: sys.safari };
|
||||
|
||||
return { broswer: '', version: '0' };
|
||||
}
|
||||
|
||||
function isHidden() {
|
||||
var hidden, visibilityChange;
|
||||
if (typeof document.hidden !== 'undefined') {
|
||||
hidden = 'hidden';
|
||||
visibilityChange = 'visibilitychange';
|
||||
} else if (typeof document.msHidden !== 'undefined') {
|
||||
hidden = 'msHidden';
|
||||
visibilityChange = 'msvisibilitychange';
|
||||
} else if (typeof document.webkitHidden !== 'undefined') {
|
||||
hidden = 'webkitHidden';
|
||||
visibilityChange = 'webkitvisibilitychange';
|
||||
}
|
||||
return document[hidden];
|
||||
}
|
||||
|
||||
function getIPAddress() {
|
||||
return new Promise(resolve => {
|
||||
window.RTCPeerConnection =
|
||||
window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //compatibility for firefox and chrome
|
||||
let pc = new RTCPeerConnection({ iceServers: [] });
|
||||
let noop = function() {};
|
||||
let IPAddress = '';
|
||||
let ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/;
|
||||
pc.createDataChannel(''); //create a bogus data channel
|
||||
pc.createOffer(pc.setLocalDescription.bind(pc), noop); // create offer and set local description
|
||||
//listen for candidate events
|
||||
pc.onicecandidate = function(ice) {
|
||||
if (
|
||||
!ice ||
|
||||
!ice.candidate ||
|
||||
!ice.candidate.candidate ||
|
||||
!ipRegex.exec(ice.candidate.candidate)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
IPAddress = ipRegex.exec(ice.candidate.candidate)[1];
|
||||
pc.onicecandidate = noop;
|
||||
resolve(IPAddress);
|
||||
};
|
||||
});
|
||||
}
|
||||
let isMobile = {
|
||||
Android: function() {
|
||||
return navigator.userAgent.match(/Android/i);
|
||||
},
|
||||
BlackBerry: function() {
|
||||
return navigator.userAgent.match(/BlackBerry|BB10/i);
|
||||
},
|
||||
iOS: function() {
|
||||
return navigator.userAgent.match(/iPhone|iPad|iPod/i);
|
||||
},
|
||||
Opera: function() {
|
||||
return navigator.userAgent.match(/Opera Mini/i);
|
||||
},
|
||||
Windows: function() {
|
||||
return navigator.userAgent.match(/IEMobile/i);
|
||||
},
|
||||
any: function() {
|
||||
return (
|
||||
isMobile.Android() ||
|
||||
isMobile.BlackBerry() ||
|
||||
isMobile.iOS() ||
|
||||
isMobile.Opera() ||
|
||||
isMobile.Windows()
|
||||
);
|
||||
},
|
||||
getOsName: function() {
|
||||
var osName = 'Unknown OS';
|
||||
if (isMobile.Android()) {
|
||||
osName = 'Android';
|
||||
}
|
||||
if (isMobile.BlackBerry()) {
|
||||
osName = 'BlackBerry';
|
||||
}
|
||||
if (isMobile.iOS()) {
|
||||
osName = 'iOS';
|
||||
}
|
||||
if (isMobile.Opera()) {
|
||||
osName = 'Opera Mini';
|
||||
}
|
||||
if (isMobile.Windows()) {
|
||||
osName = 'Windows';
|
||||
}
|
||||
return osName;
|
||||
}
|
||||
};
|
||||
function detectDesktopOS() {
|
||||
var unknown = '-';
|
||||
var nVer = navigator.appVersion;
|
||||
var nAgt = navigator.userAgent;
|
||||
var os = unknown;
|
||||
var clientStrings = [
|
||||
{
|
||||
s: 'Chrome OS',
|
||||
r: /CrOS/
|
||||
},
|
||||
{
|
||||
s: 'Windows 10',
|
||||
r: /(Windows 10.0|Windows NT 10.0)/
|
||||
},
|
||||
{
|
||||
s: 'Windows 8.1',
|
||||
r: /(Windows 8.1|Windows NT 6.3)/
|
||||
},
|
||||
{
|
||||
s: 'Windows 8',
|
||||
r: /(Windows 8|Windows NT 6.2)/
|
||||
},
|
||||
{
|
||||
s: 'Windows 7',
|
||||
r: /(Windows 7|Windows NT 6.1)/
|
||||
},
|
||||
{
|
||||
s: 'Windows Vista',
|
||||
r: /Windows NT 6.0/
|
||||
},
|
||||
{
|
||||
s: 'Windows Server 2003',
|
||||
r: /Windows NT 5.2/
|
||||
},
|
||||
{
|
||||
s: 'Windows XP',
|
||||
r: /(Windows NT 5.1|Windows XP)/
|
||||
},
|
||||
{
|
||||
s: 'Windows 2000',
|
||||
r: /(Windows NT 5.0|Windows 2000)/
|
||||
},
|
||||
{
|
||||
s: 'Windows ME',
|
||||
r: /(Win 9x 4.90|Windows ME)/
|
||||
},
|
||||
{
|
||||
s: 'Windows 98',
|
||||
r: /(Windows 98|Win98)/
|
||||
},
|
||||
{
|
||||
s: 'Windows 95',
|
||||
r: /(Windows 95|Win95|Windows_95)/
|
||||
},
|
||||
{
|
||||
s: 'Windows NT 4.0',
|
||||
r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/
|
||||
},
|
||||
{
|
||||
s: 'Windows CE',
|
||||
r: /Windows CE/
|
||||
},
|
||||
{
|
||||
s: 'Windows 3.11',
|
||||
r: /Win16/
|
||||
},
|
||||
{
|
||||
s: 'Android',
|
||||
r: /Android/
|
||||
},
|
||||
{
|
||||
s: 'Open BSD',
|
||||
r: /OpenBSD/
|
||||
},
|
||||
{
|
||||
s: 'Sun OS',
|
||||
r: /SunOS/
|
||||
},
|
||||
{
|
||||
s: 'Linux',
|
||||
r: /(Linux|X11)/
|
||||
},
|
||||
{
|
||||
s: 'iOS',
|
||||
r: /(iPhone|iPad|iPod)/
|
||||
},
|
||||
{
|
||||
s: 'Mac OS X',
|
||||
r: /Mac OS X/
|
||||
},
|
||||
{
|
||||
s: 'Mac OS',
|
||||
r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/
|
||||
},
|
||||
{
|
||||
s: 'QNX',
|
||||
r: /QNX/
|
||||
},
|
||||
{
|
||||
s: 'UNIX',
|
||||
r: /UNIX/
|
||||
},
|
||||
{
|
||||
s: 'BeOS',
|
||||
r: /BeOS/
|
||||
},
|
||||
{
|
||||
s: 'OS/2',
|
||||
r: /OS\/2/
|
||||
},
|
||||
{
|
||||
s: 'Search Bot',
|
||||
r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/
|
||||
}
|
||||
];
|
||||
for (var i = 0, cs; (cs = clientStrings[i]); i++) {
|
||||
if (cs.r.test(nAgt)) {
|
||||
os = cs.s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var osVersion = unknown;
|
||||
if (/Windows/.test(os)) {
|
||||
if (/Windows (.*)/.test(os)) {
|
||||
osVersion = /Windows (.*)/.exec(os)[1];
|
||||
}
|
||||
os = 'Windows';
|
||||
}
|
||||
switch (os) {
|
||||
case 'Mac OS X':
|
||||
if (/Mac OS X (10[/._\d]+)/.test(nAgt)) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1];
|
||||
}
|
||||
break;
|
||||
case 'Android':
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
if (/Android ([\.\_\d]+)/.test(nAgt)) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
osVersion = /Android ([\.\_\d]+)/.exec(nAgt)[1];
|
||||
}
|
||||
break;
|
||||
case 'iOS':
|
||||
if (/OS (\d+)_(\d+)_?(\d+)?/.test(nAgt)) {
|
||||
osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer);
|
||||
osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return {
|
||||
osName: os + osVersion
|
||||
};
|
||||
}
|
||||
function getOS() {
|
||||
if (isMobile.any()) {
|
||||
return isMobile.getOsName();
|
||||
} else {
|
||||
return detectDesktopOS();
|
||||
}
|
||||
}
|
||||
73
public/js/debug/GenerateTestUserSig.js
Normal file
@@ -0,0 +1,73 @@
|
||||
/* eslint-disable require-jsdoc */
|
||||
/*
|
||||
* Module: GenerateTestUserSig
|
||||
*
|
||||
* Function: 用于生成测试用的 UserSig,UserSig 是腾讯云为其云服务设计的一种安全保护签名。
|
||||
* 其计算方法是对 SDKAppID、UserID 和 EXPIRETIME 进行加密,加密算法为 HMAC-SHA256。
|
||||
*
|
||||
* Attention: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
|
||||
*
|
||||
* 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
|
||||
* 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
|
||||
* 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
|
||||
*
|
||||
* 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
|
||||
* 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
|
||||
*
|
||||
* Reference:https://cloud.tencent.com/document/product/647/17275#Server
|
||||
*/
|
||||
function genTestUserSig(userID) {
|
||||
/**
|
||||
* 腾讯云 SDKAppId,需要替换为您自己账号下的 SDKAppId。
|
||||
*
|
||||
* 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ) 创建应用,即可看到 SDKAppId,
|
||||
* 它是腾讯云用于区分客户的唯一标识。
|
||||
*/
|
||||
// const SDKAPPID = 1400435767;
|
||||
|
||||
// /**
|
||||
// * 签名过期时间,建议不要设置的过短
|
||||
// * <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 = 'dzrUpsgeMo0ygiSmqeDVqxnLbdT3Lbbh';
|
||||
|
||||
// // 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 = window.istow ? "eJwtzdEKgjAUBuB32XXYdJubQhcV2IWBkGYEgohOO1SypoYQvXtLvTzff85-Pig5xtZbauQjx8JoNc1QybaHGibuboWW*dBJnRNMMcYeX9a66l4oBRXybeOUMO7yOZGjAi2NM8YcczJrD8*-uZhhYRPBlhZozJfW4*f9y7EPafmgTXjdnYQagjIRY7Ye4qim20sNaRsGEWk26PsDMPg1Xg__" : "eJwtzE8LgkAQBfDvstdCJt3RTeigl6gMw-7oLUK3GCNZ1Foj*u5t6vH93sz7sEO0t16yZj6zLWDTPlMhq5au1POzkfXZAQ4Ac288aIr7RSkqmD8zzh30XG9oZKeolsYR0TYvg7b0*JsLCAIcjuMK3cx*LmyV5GFXBWGZ6m4VVaejzjax3K3fehsTpk2QJKVeTsSCfX94djN6";
|
||||
// console.log(userSig)
|
||||
// window.istow = true
|
||||
// return {
|
||||
// sdkAppId: SDKAPPID,
|
||||
// userSig:userSig
|
||||
// };
|
||||
return new Promise((res)=>{
|
||||
$.ajax({
|
||||
url: window.url + 'userSig?userid=' + userID,
|
||||
headers: "Bearer " + localStorage.getItem("token"),
|
||||
success(data){
|
||||
res({data: data.data, id: 1400435767})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
838
public/js/device-testing.js
Normal file
@@ -0,0 +1,838 @@
|
||||
/**
|
||||
* 设备检测demo
|
||||
*/
|
||||
/* global $ TRTC presetting getOS getBroswer cameraId micId */
|
||||
|
||||
// 用于记录检测结果,生成检测报告
|
||||
let hasCameraDevice = false,
|
||||
hasMicAndVoiceDevice = false,
|
||||
hasCameraConnect,
|
||||
hasVoiceConnect,
|
||||
hasMicConnect,
|
||||
hasNetworkConnect;
|
||||
let cameraTestingResult = {};
|
||||
let voiceTestingResult = {};
|
||||
let micTestingResult = {};
|
||||
let networkTestingResult = {};
|
||||
|
||||
// 记录检测步骤,用于关闭时清空弹窗
|
||||
let completedTestingPageIdList = [];
|
||||
let curTestingPageId = '';
|
||||
let localStream = null;
|
||||
let client = null;
|
||||
let timeout = null;
|
||||
// 监听到network-quality事件的次数
|
||||
let networkQualityNum = 0;
|
||||
|
||||
const deviceFailAttention =
|
||||
'1. 若浏览器弹出提示,请选择“允许”<br>' +
|
||||
'2. 若杀毒软件弹出提示,请选择“允许”<br>' +
|
||||
'3. 检查浏览器设置,允许网页访问摄像头及麦克风<br>' +
|
||||
'4. 检查摄像头/麦克风是否正确连接并开启<br>' +
|
||||
'5. 尝试重新连接摄像头/麦克风<br>' +
|
||||
'6. 尝试重启电脑后重新检测';
|
||||
const networkFailAttention =
|
||||
'1. 请检查设备是否联网<br>' + '2. 请刷新网页后再次检测<br>' + '3. 请尝试更换网络后再次检测';
|
||||
|
||||
// 网络参数对照表
|
||||
const NETWORK_QUALITY = {
|
||||
'0': '未知',
|
||||
'1': '极佳',
|
||||
'2': '较好',
|
||||
'3': '一般',
|
||||
'4': '差',
|
||||
'5': '极差',
|
||||
'6': '断开'
|
||||
};
|
||||
|
||||
// 设备检测tab页签对应的执行方法
|
||||
const pageCallbackConfig = {
|
||||
'camera-testing-body': 'startCameraTesting',
|
||||
'voice-testing-body': 'startVoiceTesting',
|
||||
'mic-testing-body': 'startMicTesting',
|
||||
'network-testing-body': 'startNetworkTesting'
|
||||
};
|
||||
|
||||
// 判断是否为safari浏览器
|
||||
let isSafari = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
|
||||
hideVoiceForSafari();
|
||||
/**
|
||||
* safari浏览器中隐藏扬声器相关检测
|
||||
*/
|
||||
function hideVoiceForSafari() {
|
||||
if (!isSafari) return;
|
||||
$('#connect-voice').hide();
|
||||
$('#device-voice').hide();
|
||||
$('#voice-testing').hide();
|
||||
$('#voice-report').hide();
|
||||
$('#device-mic').addClass('safari');
|
||||
$('#device-network').addClass('safari');
|
||||
$('#mic-testing').addClass('safari');
|
||||
$('#network-testing').addClass('safari');
|
||||
}
|
||||
|
||||
// 是否是本地路径打开
|
||||
let isFilePath = location.href.indexOf('file://') > -1;
|
||||
|
||||
/**
|
||||
* 设备检测初始化
|
||||
*/
|
||||
async function deviceTestingInit() {
|
||||
// 点击【设备检测】文字, 点击 【重新连接】按钮
|
||||
$('#device-testing-btn, #connect-again-btn').on('click', () => {
|
||||
startDeviceConnect();
|
||||
});
|
||||
// 连接设备错误icon
|
||||
$('#connect-attention-icon').on('mouseover', () => {
|
||||
$('#connect-attention-info').show();
|
||||
});
|
||||
// 连接设备错误icon
|
||||
$('#connect-attention-icon').on('mouseout', () => {
|
||||
$('#connect-attention-info').hide();
|
||||
});
|
||||
// 【开始检测】开始设备检测按钮
|
||||
$('#start-test-btn').on('click', function() {
|
||||
if ($(this).hasClass('start-gray')) return;
|
||||
$('#device-testing-prepare').hide();
|
||||
$('#device-testing').show();
|
||||
startCameraTesting();
|
||||
});
|
||||
// 摄像头检测失败/成功
|
||||
$('#camera-fail, #camera-success').on('click', function() {
|
||||
cameraTestingResult.statusResult = $(this).attr('id') === 'camera-success';
|
||||
$('#camera-testing-body').hide();
|
||||
localStream.close();
|
||||
// safari浏览器跳过扬声器检测
|
||||
isSafari ? startMicTesting() : startVoiceTesting();
|
||||
});
|
||||
// 播放器检测失败/成功
|
||||
$('#voice-fail, #voice-success').on('click', function() {
|
||||
voiceTestingResult.statusResult = $(this).attr('id') === 'voice-success';
|
||||
$('#voice-testing-body').hide();
|
||||
let audioPlayer = document.querySelector('#audio-player');
|
||||
if (!audioPlayer.paused) {
|
||||
audioPlayer.pause();
|
||||
}
|
||||
startMicTesting();
|
||||
});
|
||||
// 麦克风测试失败/成功
|
||||
$('#mic-fail, #mic-success').on('click', function() {
|
||||
micTestingResult.statusResult = $(this).attr('id') === 'mic-success';
|
||||
$('#mic-testing-body').hide();
|
||||
localStream.close();
|
||||
startNetworkTesting();
|
||||
});
|
||||
// 点击【查看检测报告】按钮
|
||||
$('#testing-report-btn').on('click', () => {
|
||||
showTestingReport();
|
||||
localStream.close();
|
||||
client && client.leave();
|
||||
client && client.off('network-quality');
|
||||
});
|
||||
// 点击【重新测试】按钮
|
||||
$('#testing-again').on('click', () => {
|
||||
$('#device-testing-report').hide();
|
||||
startDeviceConnect();
|
||||
completedTestingPageIdList = [];
|
||||
});
|
||||
// 点击【测试完成】按钮 / 点击关闭图标
|
||||
$('#testing-finish, #device-testing-close-btn').on('click', () => {
|
||||
finishDeviceTesting();
|
||||
});
|
||||
// 测试tab页切换
|
||||
$('#camera-testing, #voice-testing, #mic-testing, #network-testing').on('click', function() {
|
||||
let targetPageId = $(this).attr('id') + '-body';
|
||||
if (
|
||||
targetPageId !== curTestingPageId &&
|
||||
completedTestingPageIdList.indexOf(targetPageId) > -1
|
||||
) {
|
||||
$(`#${curTestingPageId}`).hide();
|
||||
localStream && localStream.close();
|
||||
client && client.leave();
|
||||
client && client.off('network-quality');
|
||||
// 停止播放器的音乐
|
||||
let audioPlayer = document.querySelector('#audio-player');
|
||||
if (!audioPlayer.paused) {
|
||||
audioPlayer.pause();
|
||||
}
|
||||
// 展示要切换的设备检测tab页面
|
||||
$(`#${targetPageId}`).show();
|
||||
window[pageCallbackConfig[targetPageId]] && window[pageCallbackConfig[targetPageId]]();
|
||||
}
|
||||
});
|
||||
// 摄像头设备切换
|
||||
$('#camera-select').change(async function() {
|
||||
let newCameraId = $(this)
|
||||
.children('option:selected')
|
||||
.val();
|
||||
localStorage.setItem('txy_webRTC_cameraId', newCameraId);
|
||||
cameraTestingResult.device = {
|
||||
label: $(this)
|
||||
.children('option:selected')
|
||||
.text(),
|
||||
deviceId: $(this)
|
||||
.children('option:selected')
|
||||
.val(),
|
||||
kind: 'videoinput'
|
||||
};
|
||||
await localStream.switchDevice('video', newCameraId);
|
||||
});
|
||||
// 扬声器设备切换
|
||||
$('#voice-select').change(async function() {
|
||||
let newVoiceId = $(this)
|
||||
.children('option:selected')
|
||||
.val();
|
||||
localStorage.setItem('txy_webRTC_voiceId', newVoiceId);
|
||||
voiceTestingResult.device = {
|
||||
label: $(this)
|
||||
.children('option:selected')
|
||||
.text(),
|
||||
deviceId: $(this)
|
||||
.children('option:selected')
|
||||
.val(),
|
||||
kind: 'audiooutput'
|
||||
};
|
||||
|
||||
let audioPlayer = document.querySelector('#audio-player');
|
||||
await audioPlayer.setSinkId(newVoiceId);
|
||||
});
|
||||
// 麦克风设备切换
|
||||
$('#mic-select').change(async function() {
|
||||
let newMicID = $(this)
|
||||
.children('option:selected')
|
||||
.val();
|
||||
localStorage.setItem('txy_webRTC_micId', newMicID);
|
||||
micTestingResult.device = {
|
||||
label: $(this)
|
||||
.children('option:selected')
|
||||
.text(),
|
||||
deviceId: $(this)
|
||||
.children('option:selected')
|
||||
.val(),
|
||||
kind: 'audioinput'
|
||||
};
|
||||
await localStream.switchDevice('audio', newMicID);
|
||||
});
|
||||
|
||||
$('body').on('click', function() {
|
||||
$('#device-connect-list').hide();
|
||||
});
|
||||
|
||||
// 获取设备信息
|
||||
await getDevicesInfo();
|
||||
// 初始化设备弹窗信息
|
||||
deviceDialogInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备信息及网络连接信息
|
||||
*/
|
||||
async function getDevicesInfo() {
|
||||
let micList = await TRTC.getMicrophones();
|
||||
let voiceList = await TRTC.getSpeakers();
|
||||
let cameraList = await TRTC.getCameras();
|
||||
let index = isFilePath ? 'label' : 'deviceId';
|
||||
if (cameraList.length > 0) {
|
||||
hasCameraDevice = true;
|
||||
}
|
||||
if (micList.length > 0) {
|
||||
hasMicAndVoiceDevice = true;
|
||||
}
|
||||
cameraList.forEach(camera => {
|
||||
if (camera[index].length > 0) {
|
||||
hasCameraConnect = true;
|
||||
}
|
||||
});
|
||||
micList.forEach(mic => {
|
||||
if (mic[index].length > 0) {
|
||||
hasMicConnect = true;
|
||||
}
|
||||
});
|
||||
if (isSafari) {
|
||||
hasVoiceConnect = true;
|
||||
} else {
|
||||
voiceList.forEach(voice => {
|
||||
if (voice[index].length > 0) {
|
||||
hasVoiceConnect = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
hasNetworkConnect = !!navigator.onLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否展示弹窗
|
||||
*/
|
||||
function deviceDialogInit() {
|
||||
if (!localStorage.getItem('txy_device_testing')) {
|
||||
localStorage.setItem('txy_device_testing', Date.now());
|
||||
startDeviceConnect();
|
||||
} else {
|
||||
// 在首页展示设备连接结果
|
||||
let showDeviceStatus = function() {
|
||||
$('#device-connect-list').show();
|
||||
timeout = setTimeout(() => {
|
||||
$('#device-connect-list').hide();
|
||||
}, 3000);
|
||||
$('#connect-camera').css('color', `${hasCameraConnect ? 'green' : 'red'}`);
|
||||
$('#connect-voice').css('color', `${hasVoiceConnect ? 'green' : 'red'}`);
|
||||
$('#connect-mic').css('color', `${hasMicConnect ? 'green' : 'red'}`);
|
||||
$('#connect-network').css('color', `${hasNetworkConnect ? 'green' : 'red'}`);
|
||||
if (!(hasCameraConnect && hasVoiceConnect && hasMicConnect && hasNetworkConnect)) {
|
||||
$('#device-testing-btn').css('color', 'red');
|
||||
} else {
|
||||
$('#device-testing-btn').css('color', 'green');
|
||||
}
|
||||
};
|
||||
showDeviceStatus();
|
||||
|
||||
if (!(hasCameraConnect && hasVoiceConnect && hasMicConnect)) {
|
||||
navigator.mediaDevices
|
||||
.getUserMedia({ video: hasCameraDevice, audio: hasMicAndVoiceDevice })
|
||||
.then(async () => {
|
||||
// 重新获取设备信息
|
||||
await getDevicesInfo();
|
||||
// 更新首页popover的option list
|
||||
getDevicesList();
|
||||
// 展示连接结果
|
||||
showDeviceStatus();
|
||||
})
|
||||
.catch(err => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹窗-设备连接检查
|
||||
*/
|
||||
function startDeviceConnect() {
|
||||
// 显示设备检测弹窗
|
||||
$('#device-testing-root').show();
|
||||
// 设备检测弹窗-设备连接页
|
||||
$('#device-testing-prepare').show();
|
||||
|
||||
curTestingPageId = 'device-testing-prepare';
|
||||
initTestingTabTitle();
|
||||
|
||||
// 在设备检测弹窗显示设备连接信息
|
||||
let showDeviceConnectInfo = function() {
|
||||
if (!(hasCameraConnect && hasVoiceConnect && hasMicConnect && hasNetworkConnect)) {
|
||||
$('#device-testing-btn').css('color', 'red');
|
||||
} else {
|
||||
$('#device-testing-btn').css('color', 'green');
|
||||
}
|
||||
// 隐藏设备连接失败提示
|
||||
$('#connect-attention-container').hide();
|
||||
|
||||
// 设备连接中
|
||||
$('#device-loading').show();
|
||||
$('#connect-info')
|
||||
.text('设备正在连接中,请稍等')
|
||||
.css('color', '#cccccc');
|
||||
$('#device-camera, #device-voice, #device-mic, #device-network').removeClass(
|
||||
'connect-success connect-fail'
|
||||
);
|
||||
$('#connect-again-btn').hide();
|
||||
$('#start-test-btn')
|
||||
.addClass('start-gray')
|
||||
.show();
|
||||
|
||||
// 设备连接结束,展示连接结果
|
||||
setTimeout(() => {
|
||||
$('#device-loading').hide();
|
||||
$('#device-camera')
|
||||
.removeClass('connect-success connect-fail')
|
||||
.addClass(`${hasCameraConnect ? 'connect-success' : 'connect-fail'}`);
|
||||
$('#device-voice')
|
||||
.removeClass('connect-success connect-fail')
|
||||
.addClass(`${hasVoiceConnect ? 'connect-success' : 'connect-fail'}`);
|
||||
$('#device-mic')
|
||||
.removeClass('connect-success connect-fail')
|
||||
.addClass(`${hasMicConnect ? 'connect-success' : 'connect-fail'}`);
|
||||
$('#device-network')
|
||||
.removeClass('connect-success connect-fail')
|
||||
.addClass(`${hasNetworkConnect ? 'connect-success' : 'connect-fail'}`);
|
||||
|
||||
if (!(hasCameraConnect && hasVoiceConnect && hasMicConnect)) {
|
||||
let connectInfo = hasNetworkConnect
|
||||
? '设备连接失败,请允许网页访问摄像头及麦克风'
|
||||
: '设备及网络连接失败,请允许网页访问摄像头及麦克风并检查网络连接';
|
||||
$('#connect-info')
|
||||
.text(connectInfo)
|
||||
.css('color', 'red');
|
||||
// 显示设备连接失败引导
|
||||
$('#connect-attention-container').show();
|
||||
$('#connect-attention-info').html(deviceFailAttention);
|
||||
// 切换按钮状态
|
||||
$('#start-test-btn').hide();
|
||||
$('#connect-again-btn').show();
|
||||
}
|
||||
if (hasCameraConnect && hasVoiceConnect && hasMicConnect && !hasNetworkConnect) {
|
||||
$('#connect-info')
|
||||
.text('网络连接失败,请检查网络连接')
|
||||
.css('color', 'red');
|
||||
// 显示网络连接失败引导
|
||||
$('#connect-attention-container').show();
|
||||
$('#connect-attention-info').html(networkFailAttention);
|
||||
// 切换按钮状态
|
||||
$('#start-test-btn').hide();
|
||||
$('#connect-again-btn').show();
|
||||
}
|
||||
if (hasCameraConnect && hasVoiceConnect && hasMicConnect && hasNetworkConnect) {
|
||||
$('#connect-info')
|
||||
.text('设备及网络连接成功,请开始设备检测')
|
||||
.css('color', '#32CD32');
|
||||
$('#connect-again-btn').hide();
|
||||
$('#start-test-btn')
|
||||
.removeClass('start-gray')
|
||||
.show();
|
||||
}
|
||||
}, 2000);
|
||||
};
|
||||
showDeviceConnectInfo();
|
||||
|
||||
// 如果有设备未连接,唤起请求弹窗
|
||||
if (!(hasCameraConnect && hasVoiceConnect && hasMicConnect)) {
|
||||
navigator.mediaDevices
|
||||
.getUserMedia({ video: hasCameraDevice, audio: hasMicAndVoiceDevice })
|
||||
.then(async () => {
|
||||
// 重新获取设备信息
|
||||
await getDevicesInfo();
|
||||
// 更新首页popover的option list
|
||||
getDevicesList();
|
||||
// 显示设备连接信息
|
||||
showDeviceConnectInfo();
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('err', err.name);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新首页popover的option list
|
||||
*/
|
||||
function getDevicesList() {
|
||||
// populate camera options
|
||||
TRTC.getCameras().then(devices => {
|
||||
$('#camera-option').empty();
|
||||
devices.forEach(device => {
|
||||
if (!cameraId) {
|
||||
// eslint-disable-next-line no-global-assign
|
||||
cameraId = device.deviceId;
|
||||
}
|
||||
let div = $('<div></div>');
|
||||
div.attr('id', device.deviceId);
|
||||
div.html(device.label);
|
||||
div.appendTo('#camera-option');
|
||||
});
|
||||
});
|
||||
|
||||
// populate microphone options
|
||||
TRTC.getMicrophones().then(devices => {
|
||||
$('#mic-option').empty();
|
||||
devices.forEach(device => {
|
||||
if (!micId) {
|
||||
// eslint-disable-next-line no-global-assign
|
||||
micId = device.deviceId;
|
||||
}
|
||||
let div = $('<div></div>');
|
||||
div.attr('id', device.deviceId);
|
||||
div.html(device.label);
|
||||
div.appendTo('#mic-option');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 摄像头检测页-检测展示摄像头设备选择列表
|
||||
*/
|
||||
async function updateCameraDeviceList() {
|
||||
let cameraDevices = await TRTC.getCameras();
|
||||
cameraDevices.filter(camera => camera.deviceId !== 'default');
|
||||
$('#camera-select').empty();
|
||||
cameraDevices.forEach(camera => {
|
||||
let option = $('<option></option>');
|
||||
option.attr('value', camera.deviceId);
|
||||
option.html(camera.label);
|
||||
option.appendTo('#camera-select');
|
||||
});
|
||||
|
||||
// 如果有用户设备选择缓存,优先使用缓存的deviceId
|
||||
let cacheCameraDevice = cameraDevices.filter(
|
||||
camera => camera.deviceId === localStorage.getItem('txy_webRTC_cameraId')
|
||||
);
|
||||
if (cacheCameraDevice.length > 0) {
|
||||
$('#camera-select').val(localStorage.getItem('txy_webRTC_cameraId'));
|
||||
cameraTestingResult.device = cacheCameraDevice[0];
|
||||
} else {
|
||||
$('#camera-select').val(cameraDevices[0].deviceId);
|
||||
cameraTestingResult.device = cameraDevices[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 摄像头设备测试
|
||||
*/
|
||||
async function startCameraTesting() {
|
||||
$('#camera-testing-body').show();
|
||||
curTestingPageId = 'camera-testing-body';
|
||||
$('#camera-testing')
|
||||
.removeClass('icon-normal')
|
||||
.addClass('icon-blue complete');
|
||||
completedTestingPageIdList.push('camera-testing-body');
|
||||
completedTestingPageIdList = [...new Set(completedTestingPageIdList)];
|
||||
|
||||
await updateCameraDeviceList();
|
||||
|
||||
// 创建本地视频流
|
||||
await createLocalStream(
|
||||
{
|
||||
audio: false,
|
||||
video: true,
|
||||
cameraId: cameraTestingResult.device.deviceId
|
||||
},
|
||||
'camera-video'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化/更新扬声器设备数组
|
||||
*/
|
||||
async function updateVoiceDeviceList() {
|
||||
// 获取扬声器设备并展示在界面中
|
||||
let voiceDevices = await TRTC.getSpeakers();
|
||||
voiceDevices = voiceDevices.filter(voice => voice.deviceId !== 'default');
|
||||
$('#voice-select').empty();
|
||||
voiceDevices.forEach(voice => {
|
||||
let option = $('<option></option>');
|
||||
option.attr('value', voice.deviceId);
|
||||
option.html(voice.label);
|
||||
option.appendTo('#voice-select');
|
||||
});
|
||||
|
||||
// 如果有用户设备选择缓存,优先使用缓存的deviceId
|
||||
let cacheVoiceDevice = voiceDevices.filter(
|
||||
mic => mic.deviceId === localStorage.getItem('txy_webRTC_voiceId')
|
||||
);
|
||||
if (cacheVoiceDevice.length > 0) {
|
||||
$('#voice-select').val(localStorage.getItem('txy_webRTC_voiceId'));
|
||||
voiceTestingResult.device = cacheVoiceDevice[0];
|
||||
} else {
|
||||
$('#voice-select').val(voiceDevices[0].deviceId);
|
||||
voiceTestingResult.device = voiceDevices[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放器设备测试
|
||||
*/
|
||||
async function startVoiceTesting() {
|
||||
$('#voice-testing-body').show();
|
||||
curTestingPageId = 'voice-testing-body';
|
||||
$('#voice-testing')
|
||||
.removeClass('icon-gray')
|
||||
.addClass('icon-blue complete');
|
||||
completedTestingPageIdList.push('voice-testing-body');
|
||||
completedTestingPageIdList = [...new Set(completedTestingPageIdList)];
|
||||
|
||||
await updateVoiceDeviceList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新/初始化麦克风设备
|
||||
*/
|
||||
async function updateMicDeviceList() {
|
||||
// 展示麦克风设备选择
|
||||
let micDevices = await TRTC.getMicrophones();
|
||||
micDevices = micDevices.filter(mic => mic.deviceId !== 'default');
|
||||
$('#mic-select').empty();
|
||||
micDevices.forEach(mic => {
|
||||
let option = $('<option></option>');
|
||||
option.attr('value', mic.deviceId);
|
||||
option.html(mic.label);
|
||||
option.appendTo('#mic-select');
|
||||
});
|
||||
|
||||
// 如果有用户设备选择缓存,优先使用缓存的deviceId
|
||||
let cacheMicDevice = micDevices.filter(
|
||||
mic => mic.deviceId === localStorage.getItem('txy_webRTC_micId')
|
||||
);
|
||||
if (cacheMicDevice.length > 0) {
|
||||
$('#mic-select').val(localStorage.getItem('txy_webRTC_micId'));
|
||||
micTestingResult.device = cacheMicDevice[0];
|
||||
} else {
|
||||
$('#mic-select').val(micDevices[0].deviceId);
|
||||
micTestingResult.device = micDevices[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 麦克风设备测试
|
||||
*/
|
||||
async function startMicTesting() {
|
||||
$('#mic-testing-body').show();
|
||||
curTestingPageId = 'mic-testing-body';
|
||||
$('#mic-testing')
|
||||
.removeClass('icon-gray')
|
||||
.addClass('icon-blue complete');
|
||||
completedTestingPageIdList.push('mic-testing-body');
|
||||
completedTestingPageIdList = [...new Set(completedTestingPageIdList)];
|
||||
|
||||
await updateMicDeviceList();
|
||||
|
||||
// 展示麦克风的声音大小显示
|
||||
if ($('#mic-bar-container').children().length === 0) {
|
||||
for (let index = 0; index < 28; index++) {
|
||||
$('<div></div>')
|
||||
.addClass('mic-bar')
|
||||
.appendTo('#mic-bar-container');
|
||||
}
|
||||
}
|
||||
|
||||
// 创建本地音频流
|
||||
await createLocalStream(
|
||||
{
|
||||
audio: true,
|
||||
microphoneId: micTestingResult.device.deviceId,
|
||||
video: false
|
||||
},
|
||||
'audio-container'
|
||||
);
|
||||
|
||||
// 监听音量,并量化显示出来
|
||||
setInterval(() => {
|
||||
let volume = localStream.getAudioLevel();
|
||||
let num = Math.ceil(28 * volume);
|
||||
$('#mic-bar-container')
|
||||
.children('.active')
|
||||
.removeClass('active');
|
||||
for (let i = 0; i < num; i++) {
|
||||
$('#mic-bar-container')
|
||||
.children()
|
||||
.slice(0, i)
|
||||
.addClass('active');
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统信息展示
|
||||
*/
|
||||
async function startNetworkTesting() {
|
||||
$('#network-testing-body').show();
|
||||
$('#testing-report-btn').hide();
|
||||
curTestingPageId = 'network-testing-body';
|
||||
$('#network-testing')
|
||||
.removeClass('icon-gray')
|
||||
.addClass('icon-blue complete');
|
||||
completedTestingPageIdList.push('network-testing-body');
|
||||
completedTestingPageIdList = [...new Set(completedTestingPageIdList)];
|
||||
|
||||
networkQualityNum = 0;
|
||||
$('#uplink-network')
|
||||
.addClass('network-loading')
|
||||
.text('');
|
||||
|
||||
// 获取系统信息
|
||||
$('#system').empty();
|
||||
let OSInfo = getOS();
|
||||
$('<div></div>')
|
||||
.text(OSInfo.osName)
|
||||
.appendTo('#system');
|
||||
|
||||
// 获取浏览器及版本信息
|
||||
$('#browser').empty();
|
||||
let browser = getBroswer();
|
||||
$('<div></div>')
|
||||
.text(`${browser.broswer} ${browser.version}`)
|
||||
.appendTo('#browser');
|
||||
|
||||
// 获取ip地址信息
|
||||
// $('#ip').empty();
|
||||
// let IPAddress = await getIPAddress();
|
||||
// $('<div></div>').text(IPAddress).appendTo('#ip');
|
||||
// networkTestingResult.IPAddress = IPAddress;
|
||||
|
||||
// 是否支持屏幕分享能力
|
||||
$('#screen-share').empty();
|
||||
let isScreenShareSupported = TRTC.isScreenShareSupported();
|
||||
$('<div></div>')
|
||||
.text(isScreenShareSupported ? '支持' : '不支持')
|
||||
.appendTo('#screen-share');
|
||||
|
||||
// 上下行网络质量
|
||||
presetting.login(false, async options => {
|
||||
client = TRTC.createClient({ mode: 'rtc', ...options });
|
||||
client.on('network-quality', event => {
|
||||
networkQualityNum++;
|
||||
// 收到3次'network-quality'事件的时候认为拿到了网络实际质量
|
||||
if (networkQualityNum === 3) {
|
||||
networkTestingResult.upLinkNetwork = event.uplinkNetworkQuality;
|
||||
networkTestingResult.downLinkNetwork = event.downlinkNetworkQuality;
|
||||
$('#uplink-network')
|
||||
.removeClass('network-loading')
|
||||
.text(NETWORK_QUALITY[String(networkTestingResult.upLinkNetwork)]);
|
||||
$('#testing-report-btn').show();
|
||||
client && client.leave();
|
||||
client && client.off('network-quality');
|
||||
}
|
||||
});
|
||||
await client.join({
|
||||
roomId: options.roomId
|
||||
});
|
||||
await createLocalStream(
|
||||
{
|
||||
audio: true,
|
||||
video: false
|
||||
},
|
||||
'audio-container'
|
||||
);
|
||||
await client.publish(localStream);
|
||||
// 音频轨道静音
|
||||
localStream.muteAudio();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示检测报告
|
||||
*/
|
||||
function showTestingReport() {
|
||||
$('#device-testing').hide();
|
||||
$('#network-testing-body').hide();
|
||||
$('#device-testing-report').show();
|
||||
curTestingPageId = 'device-testing-report';
|
||||
|
||||
// 摄像头检测结果
|
||||
$('#camera-name').text(cameraTestingResult.device.label);
|
||||
if (cameraTestingResult.statusResult) {
|
||||
$('#camera-testing-result')
|
||||
.text('正常')
|
||||
.css('color', 'green');
|
||||
} else {
|
||||
$('#camera-testing-result')
|
||||
.text('异常')
|
||||
.css('color', 'red');
|
||||
}
|
||||
|
||||
// 扬声器检测结果(safari浏览器不显示扬声器检测结果)
|
||||
if (!isSafari) {
|
||||
$('#voice-name').text(voiceTestingResult.device.label);
|
||||
if (voiceTestingResult.statusResult) {
|
||||
$('#voice-testing-result')
|
||||
.text('正常')
|
||||
.css('color', 'green');
|
||||
} else {
|
||||
$('#voice-testing-result')
|
||||
.text('异常')
|
||||
.css('color', 'red');
|
||||
}
|
||||
}
|
||||
|
||||
// 麦克风检测结果
|
||||
$('#mic-name').text(micTestingResult.device.label);
|
||||
if (micTestingResult.statusResult) {
|
||||
$('#mic-testing-result')
|
||||
.text('正常')
|
||||
.css('color', 'green');
|
||||
} else {
|
||||
$('#mic-testing-result')
|
||||
.text('异常')
|
||||
.css('color', 'red');
|
||||
}
|
||||
|
||||
// 网络检测结果
|
||||
// $('#network-name').text(networkTestingResult.IPAddress);
|
||||
$('#network-name').text('网络质量');
|
||||
$('#network-testing-result')
|
||||
.html(`${NETWORK_QUALITY[String(networkTestingResult.upLinkNetwork)]}`)
|
||||
.css('color', `${Number(networkTestingResult.upLinkNetwork) > 3 ? 'red' : 'green'}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束设备检测,隐藏设备检测弹窗
|
||||
*/
|
||||
function finishDeviceTesting() {
|
||||
$('#device-testing-root').hide();
|
||||
$('#device-testing').hide();
|
||||
$(`#${curTestingPageId}`).hide();
|
||||
curTestingPageId = '';
|
||||
completedTestingPageIdList = [];
|
||||
|
||||
// 停止摄像头/麦克风的流采集并释放摄像头/麦克风设备
|
||||
localStream && localStream.close();
|
||||
client && client.leave();
|
||||
client && client.off('network-quality');
|
||||
// 停止播放器的音乐
|
||||
let audioPlayer = document.querySelector('#audio-player');
|
||||
if (!audioPlayer.paused) {
|
||||
audioPlayer.pause();
|
||||
}
|
||||
audioPlayer.currentTime = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复检测页面头部图标的状态
|
||||
*/
|
||||
function initTestingTabTitle() {
|
||||
['camera', 'voice', 'mic', 'network'].forEach(item => {
|
||||
$(`#${item}-testing`)
|
||||
.removeClass('icon-blue complete')
|
||||
.addClass('icon-gray');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听设备变化
|
||||
*/
|
||||
navigator.mediaDevices.ondevicechange = async function(event) {
|
||||
// 当前在摄像头检测页
|
||||
if (curTestingPageId === 'camera-testing-body') {
|
||||
await updateCameraDeviceList();
|
||||
return;
|
||||
}
|
||||
// 当前在扬声器检测页
|
||||
if (curTestingPageId === 'voice-testing-body') {
|
||||
await updateVoiceDeviceList();
|
||||
return;
|
||||
}
|
||||
// 当前在麦克风检测页
|
||||
if (curTestingPageId === 'mic-testing-body') {
|
||||
await updateMicDeviceList();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 抽离createStream的公共处理函数
|
||||
*/
|
||||
async function createLocalStream(constraints, container) {
|
||||
localStream = TRTC.createStream(constraints);
|
||||
try {
|
||||
await localStream.initialize();
|
||||
} catch (error) {
|
||||
switch (error.name) {
|
||||
case 'NotReadableError':
|
||||
// 当系统或浏览器异常的时候,可能会出现此错误,您可能需要引导用户重启电脑/浏览器来尝试恢复。
|
||||
alert('暂时无法访问摄像头/麦克风,请确保当前没有其他应用请求访问摄像头/麦克风,并重试');
|
||||
return;
|
||||
case 'NotAllowedError':
|
||||
// 用户拒绝授权访问摄像头或麦克风 | 屏幕分享,您需要引导客户来授权访问
|
||||
alert('用户已拒绝授权访问摄像头或麦克风');
|
||||
return;
|
||||
case 'NotFoundError':
|
||||
// 找不到摄像头或麦克风设备
|
||||
alert('找不到摄像头或麦克风设备');
|
||||
return;
|
||||
case 'OverConstrainedError':
|
||||
alert(
|
||||
'采集属性设置错误,如果您指定了 cameraId/microphoneId,请确保它们是一个有效的非空字符串'
|
||||
);
|
||||
return;
|
||||
default:
|
||||
alert('未知错误');
|
||||
return;
|
||||
}
|
||||
}
|
||||
container && localStream.play(container);
|
||||
}
|
||||
1
public/js/iconfont.js
Normal file
51
public/js/index.js
Normal file
@@ -0,0 +1,51 @@
|
||||
/* eslint-disable no-global-assign */
|
||||
/* global $ TRTC Presetting deviceTestingInit cameraId micId */
|
||||
const presetting = new Presetting();
|
||||
presetting.init();
|
||||
deviceTestingInit();
|
||||
|
||||
// check if browser is compatible with TRTC
|
||||
TRTC.checkSystemRequirements().then(result => {
|
||||
if (!result) {
|
||||
alert('您的浏览器不兼容此应用!\n建议下载最新版Chrome浏览器');
|
||||
window.location.href = 'http://www.google.cn/chrome/';
|
||||
}
|
||||
});
|
||||
|
||||
// setup logging stuffs
|
||||
TRTC.Logger.setLogLevel(TRTC.Logger.LogLevel.DEBUG);
|
||||
TRTC.Logger.enableUploadLog();
|
||||
|
||||
TRTC.getDevices()
|
||||
.then(devices => {
|
||||
devices.forEach(item => {
|
||||
console.log('device: ' + item.kind + ' ' + item.label + ' ' + item.deviceId);
|
||||
});
|
||||
})
|
||||
.catch(error => console.error('getDevices error observed ' + error));
|
||||
|
||||
// populate camera options
|
||||
TRTC.getCameras().then(devices => {
|
||||
devices.forEach(device => {
|
||||
if (!cameraId) {
|
||||
cameraId = device.deviceId;
|
||||
}
|
||||
let div = $('<div></div>');
|
||||
div.attr('id', device.deviceId);
|
||||
div.html(device.label);
|
||||
div.appendTo('#camera-option');
|
||||
});
|
||||
});
|
||||
|
||||
// populate microphone options
|
||||
TRTC.getMicrophones().then(devices => {
|
||||
devices.forEach(device => {
|
||||
if (!micId) {
|
||||
micId = device.deviceId;
|
||||
}
|
||||
let div = $('<div></div>');
|
||||
div.attr('id', device.deviceId);
|
||||
div.html(device.label);
|
||||
div.appendTo('#mic-option');
|
||||
});
|
||||
});
|
||||
4
public/js/jquery-3.2.1.min.js
vendored
Normal file
2
public/js/lib-generate-test-usersig.min.js
vendored
Normal file
2442
public/js/popper.js
Normal file
51
public/js/presetting.js
Normal file
@@ -0,0 +1,51 @@
|
||||
/* global $ setBtnClickFuc genTestUserSig */
|
||||
// preset before starting RTC
|
||||
class Presetting {
|
||||
init() {
|
||||
// populate userId/roomId
|
||||
$('#userId').val('user_30400097');
|
||||
$('#roomId').val(parseInt(Math.random() * 100000));
|
||||
const roomId = this.query('roomId');
|
||||
const userId = this.query('userId');
|
||||
if (roomId) {
|
||||
$('#roomId').val(roomId);
|
||||
}
|
||||
if (userId) {
|
||||
$('#userId').val(userId);
|
||||
}
|
||||
|
||||
$('#main-video-btns').hide();
|
||||
$('.mask').hide();
|
||||
setBtnClickFuc();
|
||||
}
|
||||
|
||||
query(name) {
|
||||
const match = window.location.search.match(new RegExp('(\\?|&)' + name + '=([^&]*)(&|$)'));
|
||||
return !match ? '' : decodeURIComponent(match[2]);
|
||||
}
|
||||
|
||||
login(share, callback) {
|
||||
let userId = window.mid;
|
||||
if (share) {
|
||||
userId = 'share_' + userId;
|
||||
}
|
||||
console.log(userId)
|
||||
|
||||
let sdkAppId;
|
||||
let userSig;
|
||||
let roomId = window.roomid;
|
||||
genTestUserSig(userId).then((res)=>{
|
||||
sdkAppId = res.id;
|
||||
userSig = res.data
|
||||
callback({
|
||||
sdkAppId,
|
||||
userId,
|
||||
userSig,
|
||||
roomId
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
341
public/js/rtc-client.js
Normal file
@@ -0,0 +1,341 @@
|
||||
/* global $ TRTC getCameraId getMicrophoneId resetView isHidden shareUserId addMemberView removeView addVideoView */
|
||||
class RtcClient {
|
||||
constructor(options) {
|
||||
this.sdkAppId_ = options.sdkAppId;
|
||||
this.userId_ = options.userId;
|
||||
this.userSig_ = options.userSig;
|
||||
this.roomId_ = options.roomId;
|
||||
|
||||
this.isJoined_ = false;
|
||||
this.isPublished_ = false;
|
||||
this.isAudioMuted = false;
|
||||
this.isVideoMuted = false;
|
||||
this.localStream_ = null;
|
||||
this.remoteStreams_ = [];
|
||||
this.members_ = new Map();
|
||||
|
||||
// create a client for RtcClient
|
||||
this.client_ = TRTC.createClient({
|
||||
mode: 'rtc',
|
||||
sdkAppId: this.sdkAppId_,
|
||||
userId: this.userId_,
|
||||
userSig: this.userSig_
|
||||
});
|
||||
this.handleEvents();
|
||||
}
|
||||
|
||||
async join() {
|
||||
if (this.isJoined_) {
|
||||
console.warn('duplicate RtcClient.join() observed');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// join the room
|
||||
await this.client_.join({
|
||||
roomId: this.roomId_
|
||||
});
|
||||
console.log('join room success');
|
||||
this.isJoined_ = true;
|
||||
|
||||
// create a local stream with audio/video from microphone/camera
|
||||
if (getCameraId() && getMicrophoneId()) {
|
||||
this.localStream_ = TRTC.createStream({
|
||||
audio: true,
|
||||
video: true,
|
||||
userId: this.userId_,
|
||||
cameraId: getCameraId(),
|
||||
microphoneId: getMicrophoneId(),
|
||||
mirror: true
|
||||
});
|
||||
} else {
|
||||
// not to specify cameraId/microphoneId to avoid OverConstrainedError
|
||||
this.localStream_ = TRTC.createStream({
|
||||
audio: true,
|
||||
video: true,
|
||||
userId: this.userId_,
|
||||
mirror: true
|
||||
});
|
||||
}
|
||||
try {
|
||||
// initialize the local stream and the stream will be populated with audio/video
|
||||
await this.localStream_.initialize();
|
||||
console.log('initialize local stream success');
|
||||
|
||||
this.localStream_.on('player-state-changed', event => {
|
||||
console.log(`local stream ${event.type} player is ${event.state}`);
|
||||
});
|
||||
|
||||
// publish the local stream
|
||||
await this.publish();
|
||||
|
||||
this.localStream_.play('main-video');
|
||||
$('#main-video-btns').show();
|
||||
$('#mask_main').appendTo($('#player_' + this.localStream_.getId()));
|
||||
} catch (e) {
|
||||
console.error('failed to initialize local stream - ' + e);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('join room failed! ' + e);
|
||||
}
|
||||
//更新成员状态
|
||||
let states = this.client_.getRemoteMutedState();
|
||||
for (let state of states) {
|
||||
if (state.audioMuted) {
|
||||
$('#' + state.userId)
|
||||
.find('.member-audio-btn')
|
||||
.attr('src', './img/mic-off.png');
|
||||
}
|
||||
if (state.videoMuted) {
|
||||
$('#' + state.userId)
|
||||
.find('.member-video-btn')
|
||||
.attr('src', './img/camera-off.png');
|
||||
$('#mask_' + this.members_.get(state.userId).getId()).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async leave() {
|
||||
if (!this.isJoined_) {
|
||||
console.warn('leave() - please join() firstly');
|
||||
return;
|
||||
}
|
||||
// ensure the local stream is unpublished before leaving.
|
||||
await this.unpublish();
|
||||
|
||||
// leave the room
|
||||
await this.client_.leave();
|
||||
|
||||
this.localStream_.stop();
|
||||
this.localStream_.close();
|
||||
this.localStream_ = null;
|
||||
this.isJoined_ = false;
|
||||
resetView();
|
||||
}
|
||||
|
||||
async publish() {
|
||||
if (!this.isJoined_) {
|
||||
console.warn('publish() - please join() firstly');
|
||||
return;
|
||||
}
|
||||
if (this.isPublished_) {
|
||||
console.warn('duplicate RtcClient.publish() observed');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this.client_.publish(this.localStream_);
|
||||
} catch (e) {
|
||||
console.error('failed to publish local stream ' + e);
|
||||
this.isPublished_ = false;
|
||||
}
|
||||
|
||||
this.isPublished_ = true;
|
||||
}
|
||||
|
||||
async unpublish() {
|
||||
if (!this.isJoined_) {
|
||||
console.warn('unpublish() - please join() firstly');
|
||||
return;
|
||||
}
|
||||
if (!this.isPublished_) {
|
||||
console.warn('RtcClient.unpublish() called but not published yet');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.client_.unpublish(this.localStream_);
|
||||
this.isPublished_ = false;
|
||||
}
|
||||
|
||||
muteLocalAudio() {
|
||||
this.localStream_.muteAudio();
|
||||
}
|
||||
|
||||
unmuteLocalAudio() {
|
||||
this.localStream_.unmuteAudio();
|
||||
}
|
||||
|
||||
muteLocalVideo() {
|
||||
this.localStream_.muteVideo();
|
||||
}
|
||||
|
||||
unmuteLocalVideo() {
|
||||
this.localStream_.unmuteVideo();
|
||||
}
|
||||
|
||||
resumeStreams() {
|
||||
this.localStream_.resume();
|
||||
for (let stream of this.remoteStreams_) {
|
||||
stream.resume();
|
||||
}
|
||||
}
|
||||
|
||||
handleEvents() {
|
||||
this.client_.on('error', err => {
|
||||
console.error(err);
|
||||
alert(err);
|
||||
location.reload();
|
||||
});
|
||||
this.client_.on('client-banned', err => {
|
||||
console.error('client has been banned for ' + err);
|
||||
if (!isHidden()) {
|
||||
alert('您已被踢出房间');
|
||||
location.reload();
|
||||
} else {
|
||||
document.addEventListener(
|
||||
'visibilitychange',
|
||||
() => {
|
||||
if (!isHidden()) {
|
||||
alert('您已被踢出房间');
|
||||
location.reload();
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
});
|
||||
// fired when a remote peer is joining the room
|
||||
this.client_.on('peer-join', evt => {
|
||||
const userId = evt.userId;
|
||||
console.log('peer-join ' + userId);
|
||||
if (userId !== shareUserId) {
|
||||
addMemberView(userId);
|
||||
}
|
||||
});
|
||||
// fired when a remote peer is leaving the room
|
||||
this.client_.on('peer-leave', evt => {
|
||||
const userId = evt.userId;
|
||||
removeView(userId);
|
||||
console.log('peer-leave ' + userId);
|
||||
});
|
||||
// fired when a remote stream is added
|
||||
this.client_.on('stream-added', evt => {
|
||||
const remoteStream = evt.stream;
|
||||
const id = remoteStream.getId();
|
||||
const userId = remoteStream.getUserId();
|
||||
this.members_.set(userId, remoteStream);
|
||||
console.log(`remote stream added: [${userId}] ID: ${id} type: ${remoteStream.getType()}`);
|
||||
if (remoteStream.getUserId() === shareUserId) {
|
||||
// don't need screen shared by us
|
||||
this.client_.unsubscribe(remoteStream);
|
||||
} else {
|
||||
console.log('subscribe to this remote stream');
|
||||
this.client_.subscribe(remoteStream);
|
||||
}
|
||||
});
|
||||
// fired when a remote stream has been subscribed
|
||||
this.client_.on('stream-subscribed', evt => {
|
||||
const uid = evt.userId;
|
||||
const remoteStream = evt.stream;
|
||||
const id = remoteStream.getId();
|
||||
this.remoteStreams_.push(remoteStream);
|
||||
remoteStream.on('player-state-changed', event => {
|
||||
console.log(`${event.type} player is ${event.state}`);
|
||||
if (event.type == 'video' && event.state == 'STOPPED') {
|
||||
$('#mask_' + remoteStream.getId()).show();
|
||||
$('#' + remoteStream.getUserId())
|
||||
.find('.member-video-btn')
|
||||
.attr('src', 'img/camera-off.png');
|
||||
}
|
||||
if (event.type == 'video' && event.state == 'PLAYING') {
|
||||
$('#mask_' + remoteStream.getId()).hide();
|
||||
$('#' + remoteStream.getUserId())
|
||||
.find('.member-video-btn')
|
||||
.attr('src', 'img/camera-on.png');
|
||||
}
|
||||
});
|
||||
addVideoView(id);
|
||||
// objectFit 为播放的填充模式,详细参考:https://trtc-1252463788.file.myqcloud.com/web/docs/Stream.html#play
|
||||
remoteStream.play(id, { objectFit: 'contain' });
|
||||
//添加“摄像头未打开”遮罩
|
||||
let mask = $('#mask_main').clone();
|
||||
mask.attr('id', 'mask_' + id);
|
||||
mask.appendTo($('#player_' + id));
|
||||
mask.hide();
|
||||
if (!remoteStream.hasVideo()) {
|
||||
mask.show();
|
||||
$('#' + remoteStream.getUserId())
|
||||
.find('.member-video-btn')
|
||||
.attr('src', 'img/camera-off.png');
|
||||
}
|
||||
console.log('stream-subscribed ID: ', id);
|
||||
});
|
||||
// fired when the remote stream is removed, e.g. the remote user called Client.unpublish()
|
||||
this.client_.on('stream-removed', evt => {
|
||||
const remoteStream = evt.stream;
|
||||
const id = remoteStream.getId();
|
||||
remoteStream.stop();
|
||||
this.remoteStreams_ = this.remoteStreams_.filter(stream => {
|
||||
return stream.getId() !== id;
|
||||
});
|
||||
removeView(id);
|
||||
console.log(`stream-removed ID: ${id} type: ${remoteStream.getType()}`);
|
||||
});
|
||||
|
||||
this.client_.on('stream-updated', evt => {
|
||||
const remoteStream = evt.stream;
|
||||
let uid = this.getUidByStreamId(remoteStream.getId());
|
||||
if (!remoteStream.hasVideo()) {
|
||||
$('#' + uid)
|
||||
.find('.member-video-btn')
|
||||
.attr('src', 'img/camera-off.png');
|
||||
}
|
||||
console.log(
|
||||
'type: ' +
|
||||
remoteStream.getType() +
|
||||
' stream-updated hasAudio: ' +
|
||||
remoteStream.hasAudio() +
|
||||
' hasVideo: ' +
|
||||
remoteStream.hasVideo() +
|
||||
' uid: ' +
|
||||
uid
|
||||
);
|
||||
});
|
||||
|
||||
this.client_.on('mute-audio', evt => {
|
||||
console.log(evt.userId + ' mute audio');
|
||||
$('#' + evt.userId)
|
||||
.find('.member-audio-btn')
|
||||
.attr('src', 'img/mic-off.png');
|
||||
});
|
||||
this.client_.on('unmute-audio', evt => {
|
||||
console.log(evt.userId + ' unmute audio');
|
||||
$('#' + evt.userId)
|
||||
.find('.member-audio-btn')
|
||||
.attr('src', 'img/mic-on.png');
|
||||
});
|
||||
this.client_.on('mute-video', evt => {
|
||||
console.log(evt.userId + ' mute video');
|
||||
$('#' + evt.userId)
|
||||
.find('.member-video-btn')
|
||||
.attr('src', 'img/camera-off.png');
|
||||
let streamId = this.members_.get(evt.userId).getId();
|
||||
if (streamId) {
|
||||
$('#mask_' + streamId).show();
|
||||
}
|
||||
});
|
||||
this.client_.on('unmute-video', evt => {
|
||||
console.log(evt.userId + ' unmute video');
|
||||
$('#' + evt.userId)
|
||||
.find('.member-video-btn')
|
||||
.attr('src', 'img/camera-on.png');
|
||||
const stream = this.members_.get(evt.userId);
|
||||
if (stream) {
|
||||
let streamId = stream.getId();
|
||||
if (streamId) {
|
||||
$('#mask_' + streamId).hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
showStreamState(stream) {
|
||||
console.log('has audio: ' + stream.hasAudio() + ' has video: ' + stream.hasVideo());
|
||||
}
|
||||
|
||||
getUidByStreamId(streamId) {
|
||||
for (let [uid, stream] of this.members_) {
|
||||
if (stream.getId() == streamId) {
|
||||
return uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
163
public/js/share-client.js
Normal file
@@ -0,0 +1,163 @@
|
||||
/* global $ TRTC */
|
||||
class ShareClient {
|
||||
constructor(options) {
|
||||
this.sdkAppId_ = options.sdkAppId;
|
||||
this.userId_ = options.userId;
|
||||
this.userSig_ = options.userSig;
|
||||
this.roomId_ = options.roomId;
|
||||
|
||||
this.isJoined_ = false;
|
||||
this.isPublished_ = false;
|
||||
this.localStream_ = null;
|
||||
|
||||
this.client_ = TRTC.createClient({
|
||||
mode: 'rtc',
|
||||
sdkAppId: this.sdkAppId_,
|
||||
userId: this.userId_,
|
||||
userSig: this.userSig_,
|
||||
/**
|
||||
* disable receivers to avoid receiving remote streams as we only want to
|
||||
* publish the screen stream
|
||||
*/
|
||||
disableReceiver: true
|
||||
});
|
||||
|
||||
this.client_.setDefaultMuteRemoteStreams(true);
|
||||
this.handleEvents();
|
||||
}
|
||||
|
||||
async join() {
|
||||
if (this.isJoined_) {
|
||||
console.warn('duplicate RtcClient.join() observed');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this.client_.join({
|
||||
roomId: this.roomId_
|
||||
});
|
||||
console.log('ShareClient join room success');
|
||||
this.isJoined_ = true;
|
||||
|
||||
// create a local stream for screen share
|
||||
this.localStream_ = TRTC.createStream({
|
||||
// disable audio as RtcClient already enable audio
|
||||
audio: false,
|
||||
// enable screen share
|
||||
screen: true,
|
||||
userId: this.userId_
|
||||
});
|
||||
try {
|
||||
// initialize the local stream to populate the screen stream
|
||||
await this.localStream_.initialize();
|
||||
console.log('ShareClient initialize local stream for screen share success');
|
||||
|
||||
this.localStream_.on('player-state-changed', event => {
|
||||
console.log(`local stream ${event.type} player is ${event.state}`);
|
||||
});
|
||||
this.localStream_.on('screen-sharing-stopped', event => {
|
||||
console.log('share stream video track enned');
|
||||
this.leave();
|
||||
$('#screen-btn').attr('src', './img/screen-off.png');
|
||||
});
|
||||
|
||||
// publish the screen share stream
|
||||
await this.client_.publish(this.localStream_);
|
||||
} catch (e) {
|
||||
console.error('ShareClient failed to initialize local stream - ' + e);
|
||||
//用户取消分享屏幕导致推流失败
|
||||
await this.client_.leave();
|
||||
this.isJoined_ = false;
|
||||
$('#screen-btn').attr('src', 'img/screen-off.png');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('ShareClient join room failed! ' + e);
|
||||
}
|
||||
}
|
||||
|
||||
async leave() {
|
||||
if (!this.isJoined_) {
|
||||
console.warn('leave() - please join() firstly');
|
||||
return;
|
||||
}
|
||||
if (this.isPublished_) {
|
||||
await this.client_.unpublish(this.localStream_);
|
||||
this.isPublished_ = false;
|
||||
}
|
||||
await this.client_.leave();
|
||||
if (this.localStream_) {
|
||||
this.localStream_.close();
|
||||
this.localStream_ = null;
|
||||
}
|
||||
this.isJoined_ = false;
|
||||
}
|
||||
|
||||
handleEvents() {
|
||||
this.client_.on('error', err => {
|
||||
console.error(err);
|
||||
alert(err);
|
||||
});
|
||||
this.client_.on('client-banned', err => {
|
||||
console.error('client has been banned for ' + err);
|
||||
});
|
||||
// fired when a remote peer is joining the room
|
||||
this.client_.on('peer-join', evt => {
|
||||
const userId = evt.userId;
|
||||
console.log('peer-join ' + userId);
|
||||
});
|
||||
// fired when a remote peer is leaving the room
|
||||
this.client_.on('peer-leave', evt => {
|
||||
const userId = evt.userId;
|
||||
console.log('peer-leave ' + userId);
|
||||
});
|
||||
// fired when a remote stream is added
|
||||
this.client_.on('stream-added', evt => {
|
||||
const remoteStream = evt.stream;
|
||||
const id = remoteStream.getId();
|
||||
const userId = remoteStream.getUserId();
|
||||
console.log(`remote stream added: [${userId}] ID: ${id} type: ${remoteStream.getType()}`);
|
||||
console.log('subscribe to this remote stream');
|
||||
});
|
||||
// fired when a remote stream has been subscribed
|
||||
this.client_.on('stream-subscribed', evt => {
|
||||
const remoteStream = evt.stream;
|
||||
const id = remoteStream.getId();
|
||||
remoteStream.on('player-state-changed', event => {
|
||||
console.log(`${event.type} player is ${event.state}`);
|
||||
});
|
||||
console.log('stream-subscribed ID: ', id);
|
||||
});
|
||||
// fired when the remote stream is removed, e.g. the remote user called Client.unpublish()
|
||||
this.client_.on('stream-removed', evt => {
|
||||
const remoteStream = evt.stream;
|
||||
const id = remoteStream.getId();
|
||||
console.log(`stream-removed ID: ${id} type: ${remoteStream.getType()}`);
|
||||
});
|
||||
|
||||
this.client_.on('stream-updated', evt => {
|
||||
const remoteStream = evt.stream;
|
||||
console.log(
|
||||
'type: ' +
|
||||
remoteStream.getType() +
|
||||
' stream-updated hasAudio: ' +
|
||||
remoteStream.hasAudio() +
|
||||
' hasVideo: ' +
|
||||
remoteStream.hasVideo() +
|
||||
' uid: ' +
|
||||
remoteStream.getUserId()
|
||||
);
|
||||
});
|
||||
|
||||
this.client_.on('mute-audio', evt => {
|
||||
console.log(evt.userId + ' mute audio');
|
||||
});
|
||||
this.client_.on('unmute-audio', evt => {
|
||||
console.log(evt.userId + ' unmute audio');
|
||||
});
|
||||
this.client_.on('mute-video', evt => {
|
||||
console.log(evt.userId + ' mute video');
|
||||
});
|
||||
this.client_.on('unmute-video', evt => {
|
||||
console.log(evt.userId + ' unmute video');
|
||||
});
|
||||
}
|
||||
}
|
||||
1
public/js/trtc.js
Normal file
357
public/zhibo.html
Normal file
@@ -0,0 +1,357 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>TRTC实时音视频通话</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.7, user-scalable=no, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="./css/bootstrap-material-design.min.css">
|
||||
<link rel="stylesheet" href="./css/index.css">
|
||||
<link rel="stylesheet" href="./css/room.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root">
|
||||
|
||||
|
||||
<!-- 设备检测界面弹窗 -->
|
||||
<div id="device-testing-root" style="display: none;">
|
||||
<!-- 设备检测卡片 -->
|
||||
<div class="device-testing-card">
|
||||
<!-- 设备检测准备界面 -->
|
||||
<div id="device-testing-prepare" class="device-testing-prepare">
|
||||
<div class="testing-title">设备连接</div>
|
||||
<div class="testing-prepare-info">设备检测前请务必给当前页面开放摄像头,麦克风权限哦~</div>
|
||||
<div class="device-display">
|
||||
<div id="device-camera" class="device icon-normal">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shiping-xue"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="device-voice" class="device icon-normal">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shengyin"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="device-mic" class="device icon-normal">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-maikefeng-xue"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="device-network" class="device icon-normal">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-wangluo"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-loading" class="loading-background">
|
||||
<div class="device-loading"></div>
|
||||
</div>
|
||||
<!-- 连接结果提示 -->
|
||||
<div class="connect-info">
|
||||
<!-- 连接结果 -->
|
||||
<div id="connect-info"></div>
|
||||
<!-- 错误icon及错误解决指引 -->
|
||||
<div id="connect-attention-container" class="connect-attention-container" style="display: none;">
|
||||
<div id="connect-attention-icon" class="connect-attention-icon">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-warn"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="connect-attention-info" class="connect-attention-info" style="display: none;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 设备连接页面button -->
|
||||
<div class="testing-btn-display">
|
||||
<div id="start-test-btn" class="test-btn start-test start-gray">开始检测</div>
|
||||
<div id="connect-again-btn" class="test-btn connect-again" style="display: none;">重新连接</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 设备检测tab页 -->
|
||||
<div id="device-testing" class="device-testing" style="display: none;">
|
||||
<div class="device-testing-title">
|
||||
<div id="camera-testing" class="testing icon-gray">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shiping-xue"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="voice-testing" class="testing icon-gray">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shengyin"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="mic-testing" class="testing icon-gray">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-maikefeng-xue"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="network-testing" class="testing icon-gray">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-wangluo"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 设备检测-摄像头检测 -->
|
||||
<div id="camera-testing-body" class="testing-body" style="display: none;">
|
||||
<div class="device-list camera-device-list">
|
||||
<div class="select-title" style="display: block;">摄像头选择</div>
|
||||
<div class="select-list" style="display: block;">
|
||||
<select name="select" id="camera-select" class="device-select"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="camera-video" class="camera-video"></div>
|
||||
<div class="testing-info-container">
|
||||
<div class="testing-info">是否可以清楚的看到自己?</div>
|
||||
<div class="button-list">
|
||||
<div id="camera-fail" class="fail-button">看不到</div>
|
||||
<div id="camera-success" class="success-button">可以看到</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 设备检测-播放器检测 -->
|
||||
<div id="voice-testing-body" class="testing-body" style="display: none;">
|
||||
<div class="device-list camera-device-list">
|
||||
<div class="select-title" style="display: block;">扬声器选择</div>
|
||||
<div class="select-list" style="display: block;">
|
||||
<select name="select" id="voice-select" class="device-select"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="audio-control">
|
||||
<div class="audio-control-info">请调高设备音量, 点击播放下面的音频试试~</div>
|
||||
<audio id="audio-player" src="https://trtc-1252463788.cos.ap-guangzhou.myqcloud.com/web/assets/bgm-test.mp3" controls></audio>
|
||||
</div>
|
||||
<div class="testing-info-container">
|
||||
<div class="testing-info">是否可以听到声音?</div>
|
||||
<div class="button-list">
|
||||
<div id="voice-fail" class="fail-button">听不到</div>
|
||||
<div id="voice-success" class="success-button">可以听到</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 设备检测-麦克风检测 -->
|
||||
<div id="mic-testing-body" class="testing-body" style="display: none;">
|
||||
<div class="device-list camera-device-list">
|
||||
<div class="select-title" style="display: block;">麦克风选择</div>
|
||||
<div class="select-list" style="display: block;">
|
||||
<select name="select" id="mic-select" class="device-select"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mic-testing-container">
|
||||
<div class="mic-testing-info">对着麦克风说'哈喽'试试~</div>
|
||||
<div id="mic-bar-container" class="mic-bar-container"></div>
|
||||
<div id="audio-container"></div>
|
||||
</div>
|
||||
<div class="testing-info-container">
|
||||
<div class="testing-info">是否可以看到音量图标跳动?</div>
|
||||
<div class="button-list">
|
||||
<div id="mic-fail" class="fail-button">看不到</div>
|
||||
<div id="mic-success" class="success-button">可以看到</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 设备检测-硬件及网速检测 -->
|
||||
<div id="network-testing-body" class="testing-body" style="display: none;">
|
||||
<div class="testing-index-list">
|
||||
<div class="testing-index-group">
|
||||
<div class="testing-index">操作系统</div>
|
||||
<div id="system"></div>
|
||||
</div>
|
||||
<div class="testing-index-group">
|
||||
<div class="testing-index">浏览器版本</div>
|
||||
<div id="browser"></div>
|
||||
</div>
|
||||
<!-- <div class="testing-index-group">
|
||||
<div class="testing-index">IP地址</div>
|
||||
<div id="ip"></div>
|
||||
</div> -->
|
||||
<div class="testing-index-group">
|
||||
<div class="testing-index">屏幕共享能力</div>
|
||||
<div id="screen-share"></div>
|
||||
</div>
|
||||
<div class="testing-index-group">
|
||||
<div class="testing-index">网络质量</div>
|
||||
<div id="uplink-network" class="network-loading"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="testing-footer">
|
||||
<div id="testing-report-btn" class="test-btn">查看检测报告</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 设备检测报告 -->
|
||||
<div id="device-testing-report" class="device-testing-report" style="display: none;">
|
||||
<div class="testing-title">检测报告</div>
|
||||
<!-- 检测报告内容 -->
|
||||
<div class="device-report-list">
|
||||
<!-- 摄像头报告信息 -->
|
||||
<div class="device-report camera-report">
|
||||
<div class="device-info">
|
||||
<div class="report-icon">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shiping-xue"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="camera-name" class="device-name"></div>
|
||||
</div>
|
||||
<div id="camera-testing-result" class="camera-testing-result"></div>
|
||||
</div>
|
||||
<!-- 扬声器报告信息 -->
|
||||
<div id="voice-report" class="device-report voice-report">
|
||||
<div class="device-info">
|
||||
<div class="report-icon">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shengyin"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="voice-name" class="device-name"></div>
|
||||
</div>
|
||||
<div id="voice-testing-result" class="voice-testing-result"></div>
|
||||
</div>
|
||||
<!-- 麦克风报告信息 -->
|
||||
<div class="device-report mic-report">
|
||||
<div class="device-info">
|
||||
<div class="report-icon">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-maikefeng-xue"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="mic-name" class="device-name"></div>
|
||||
</div>
|
||||
<div id="mic-testing-result" class="mic-testing-result"></div>
|
||||
</div>
|
||||
<!-- 网络报告信息 -->
|
||||
<div class="device-report network-report">
|
||||
<div class="device-info">
|
||||
<div class="report-icon">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-wangluo"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="network-name" class="device-name"></div>
|
||||
</div>
|
||||
<div id="network-testing-result" class="network-testing-result"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="device-report-footer">
|
||||
<div id="testing-again" class="device-report-btn testing-agin">重新检测</div>
|
||||
<div id="testing-finish" class="device-report-btn testing-finish">完成检测</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 设备检测关闭按钮 -->
|
||||
<div id="device-testing-close-btn" class="device-testing-close-btn">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-baseline-close-px"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 聊天室页面 -->
|
||||
<div id="room-root" class="col-div">
|
||||
<!-- header -->
|
||||
<div class="row-div card" style="width: 100%; height: 65px; justify-content: space-between;border-radius: 17px;">
|
||||
<!-- 腾讯云logo -->
|
||||
<div class="row-div" style="height: 100%; width: 230px; justify-content: center">
|
||||
|
||||
<div style="width: 86px; height: 23px; font-size: 18px; color: #333333">直播页面</div>
|
||||
</div>
|
||||
<!-- 分享屏幕 退出 按钮 -->
|
||||
<div class="row-div" style="height: 100%; width: auto;margin-right: 28px;">
|
||||
<img id="screen-btn" style="width: 45px; height: 45px" src="./img/screen-off.png" alt="">
|
||||
<div style="width: 20px"></div>
|
||||
<img id="logout-btn" style="width: 45px; height: 45px" src="./img/logout.png" alt="">
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- content -->
|
||||
<div class="row-div" style="height: 100%; width: 100%; padding: 10px">
|
||||
<div class="col-div" style="width: 340px; height: 100%; padding: 10px">
|
||||
<div class="col-div card" style="width: 100%; height: 100%; padding: 23px">
|
||||
<!-- 成员列表 -->
|
||||
<div style="width: 100%;">上课人员</div>
|
||||
<div id="member-list" class="col-div" style="width: 100%; justify-content: flex-start; flex: 1">
|
||||
<!-- member -->
|
||||
<!-- <div id="member-me" style="width: 100%; padding-left: 20px">
|
||||
<div class="row-div member"
|
||||
style="width: 100%; height: 50px; justify-content: space-between">
|
||||
<div class="member-id">(我)</div>
|
||||
<div class="row-div" style="width:100px; height: 27px; justify-content: center">
|
||||
<img class="member-video-btn" style="height: 100%" src="./img/camera-on.png"
|
||||
alt="">
|
||||
<div style="width: 18px"></div>
|
||||
<img class="member-audio-btn" style="height: 100%" src="./img/mic-on.png"
|
||||
alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- 视频网格 -->
|
||||
<div id="video-grid" style="height: 100%; flex: 1">
|
||||
<!-- 主视频 -->
|
||||
<div id="main-video" class="video-box col-div" style="justify-content: flex-end">
|
||||
<!-- 主视频控制按钮 -->
|
||||
<div id="main-video-btns" class="row-div"
|
||||
style="width: 156px; position: absolute; z-index: 10; justify-content: center; align-self: flex-end">
|
||||
<img id="video-btn" style="width: 68px; height: 68px" onClick="event.cancelBubble = true"
|
||||
src="./img/big-camera-on.png" alt="" title="关闭摄像头">
|
||||
<img id="mic-btn" style="width: 68px; height: 68px" onClick="event.cancelBubble = true"
|
||||
src="./img/big-mic-on.png" alt="" title="关闭麦克风">
|
||||
</div>
|
||||
<div id="mask_main" class="mask col-div">
|
||||
<!-- “摄像头未开启”遮罩 -->
|
||||
<div style="height: 100%; width: 100%; position: absolute; background-color: #D8D8D8"></div>
|
||||
<img style="width: 63px; height: 69px; z-index: 10;" src="./img/camera-max.png" alt="">
|
||||
<div style="height: 10px"></div>
|
||||
<div style="z-index: 10">摄像头未打开</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 小视频 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
function GetRequest() {
|
||||
var url = location.search; //获取url中"?"符后的字串
|
||||
var theRequest = new Object();
|
||||
if(url.indexOf("?") != -1) {
|
||||
var str = url.substr(1);
|
||||
strs = str.split("&");
|
||||
for(var i = 0; i < strs.length; i++) {
|
||||
theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]);
|
||||
}
|
||||
}
|
||||
return theRequest;
|
||||
};
|
||||
window.url = 'https://case.sy-my.net/beelink/public/home/'
|
||||
const urldata = GetRequest('roomid')
|
||||
window.roomid = urldata.roomid
|
||||
window.mid = urldata.memberid
|
||||
</script>
|
||||
<script src="./js/jquery-3.2.1.min.js"></script>
|
||||
<script src="./js/popper.js"></script>
|
||||
<script src="./js/bootstrap-material-design.js"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('body').bootstrapMaterialDesign();
|
||||
});
|
||||
</script>
|
||||
<script src="./js/lib-generate-test-usersig.min.js"></script>
|
||||
<script src="./js/debug/GenerateTestUserSig.js"></script>
|
||||
<script src="./js/iconfont.js"></script>
|
||||
<script src="./js/trtc.js"></script>
|
||||
<script src="./js/common.js"></script>
|
||||
<script src="./js/rtc-client.js"></script>
|
||||
<script src="./js/share-client.js"></script>
|
||||
<script src="./js/presetting.js"></script>
|
||||
<script src="./js/device-testing.js"></script>
|
||||
<script src="./js/index.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
12
src/App.vue
@@ -16,21 +16,33 @@ import { provideI18n } from "@/utils/i18n"
|
||||
import i18ninit from "@/i18n/init"
|
||||
import enUS from 'ant-design-vue/es/locale/en_US';
|
||||
import zhCN from 'ant-design-vue/es/locale/zh_CN';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export default defineComponent({
|
||||
setup(){
|
||||
console.log(i18ninit)
|
||||
const len = provideI18n(i18ninit);
|
||||
// len.locale.value = !getValue("Lanvuage") ? 'zh' : getValue("Lanvuage");
|
||||
if(getValue('token')){
|
||||
store.commit("login", true)
|
||||
store.dispatch("setUserInfo");
|
||||
}else{
|
||||
|
||||
console.log('ip')
|
||||
store.dispatch("getip");
|
||||
store.commit('setWlan')
|
||||
router.push("/")
|
||||
}
|
||||
const zh = zhCN
|
||||
const en = enUS
|
||||
/* eslint-disable */
|
||||
const utc = require('dayjs/plugin/utc') // dependent on utc plugin
|
||||
/* eslint-disable */
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const days: any = dayjs;
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
console.log(days.tz.guess())
|
||||
return{
|
||||
zh,
|
||||
en,
|
||||
|
||||
@@ -14,6 +14,25 @@ export interface Get {
|
||||
let login: MessageType;
|
||||
let count = 0;
|
||||
const div: any = document.getElementById("make");
|
||||
export function countadd(){
|
||||
if(count == 0){
|
||||
login = message.loading('加载中..', 0)
|
||||
div.style.display = "block"
|
||||
}
|
||||
count++;
|
||||
}
|
||||
export function countdel(){
|
||||
if(count != 0){
|
||||
setTimeout(()=>{
|
||||
count--;
|
||||
if(count == 0){
|
||||
login();
|
||||
div.style.display = "none"
|
||||
}
|
||||
console.log(count)
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
axios.interceptors.request.use((config)=>{
|
||||
if(count == 0){
|
||||
login = message.loading('加载中..', 0)
|
||||
@@ -80,11 +99,15 @@ const put: Get = async function (url: string, data?: unknown){
|
||||
function setToken(){
|
||||
axios.defaults.headers.common['Authorization'] = "Bearer " + getValue("token");
|
||||
}
|
||||
function setLanvuage(yuyan: string){
|
||||
axios.defaults.headers.common['Language'] = yuyan;
|
||||
}
|
||||
|
||||
export {
|
||||
get,
|
||||
post,
|
||||
del,
|
||||
put,
|
||||
setToken
|
||||
setToken,
|
||||
setLanvuage
|
||||
}
|
||||
122
src/api/index.ts
@@ -1,8 +1,9 @@
|
||||
import router from '@/router';
|
||||
import store from '@/store';
|
||||
import { LiveList, LivelistInfo, LoginData, UserInfo } from '@/types';
|
||||
import { saveValue } from '@/utils/common';
|
||||
import { getValue, saveValue } from '@/utils/common';
|
||||
import { message } from 'ant-design-vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { del, get, post, put, setToken } from './base'
|
||||
|
||||
|
||||
@@ -31,8 +32,9 @@ export async function loginpass(phone: string, password: string,type?: number,sm
|
||||
}else{
|
||||
setToken();
|
||||
store.commit("login", true);
|
||||
store.dispatch("setUserInfo");
|
||||
router.push("/mine/archives")
|
||||
await store.dispatch("setUserInfo");
|
||||
await router.push("/mine/archives")
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +46,9 @@ export async function userinfo(){
|
||||
const user = await get<UserInfo>('personalInfo');
|
||||
// console.log(user.data.img)
|
||||
if(user.code == 1001){
|
||||
saveValue("token","")
|
||||
router.push("/")
|
||||
|
||||
return '未登录';
|
||||
}
|
||||
return user.data;
|
||||
@@ -204,6 +208,7 @@ export async function setvideo(data?: any) {
|
||||
const res=await put<Liveaddrule>('video/'+data.id,data)
|
||||
if(res.code==0){
|
||||
message.success(res.msg)
|
||||
router.push("/regime/video")
|
||||
}else{
|
||||
message.error(res.msg)
|
||||
}
|
||||
@@ -228,6 +233,8 @@ interface VideoDetail{
|
||||
updated_at: string;
|
||||
share: number;
|
||||
watch: number;
|
||||
score: string;
|
||||
statusdesc: string;
|
||||
}
|
||||
|
||||
export async function videodetail(data?: any,ifupdate?: number) {
|
||||
@@ -258,7 +265,9 @@ export async function videodetail(data?: any,ifupdate?: number) {
|
||||
createdAt: res.data.created_at,
|
||||
updatedAt: res.data.updated_at,
|
||||
watch: res.data.watch,
|
||||
share: res.data.share
|
||||
share: res.data.share,
|
||||
score: res.data.score,
|
||||
statusdesc: res.data.statusdesc
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,6 +295,7 @@ export async function accountadd(data?: any) {
|
||||
const res = await post<Liveaddrule>('wallect',data);
|
||||
if(res.code==0){
|
||||
message.success("新增成功")
|
||||
router.push("/mine/wallet")
|
||||
}
|
||||
|
||||
console.log(res)
|
||||
@@ -337,10 +347,11 @@ export async function cashout(data?: any,accountinfo?: any){
|
||||
|
||||
console.log(data,'tixian')
|
||||
console.log(accountinfo)
|
||||
data.account=accountinfo.account
|
||||
data.bankcode=accountinfo.bankcode
|
||||
data.bankname=accountinfo.bankname
|
||||
data.mname=accountinfo.mname
|
||||
// data.account=accountinfo.account
|
||||
// data.bankcode=accountinfo.bankcode
|
||||
// data.bankname=accountinfo.bankname
|
||||
// data.mname=accountinfo.mname
|
||||
data.wallectid=accountinfo.wallectid
|
||||
console.log(data)
|
||||
const res = await post<Liveaddrule>('withdrawal',data);
|
||||
if(res.code==0){
|
||||
@@ -379,8 +390,8 @@ export async function getaccountinfo(data?: any){
|
||||
/**
|
||||
* 账户编辑
|
||||
*/
|
||||
export async function editaccount(data?: any){
|
||||
const res=await put<Liveaddrule>('wallect/'+data);
|
||||
export async function editaccount( id: any,data?: any){
|
||||
const res=await put<Liveaddrule>('wallect/' +id ,data);
|
||||
if(res.code==0){
|
||||
message.success("修改成功")
|
||||
}
|
||||
@@ -393,6 +404,7 @@ export async function deleteaccount(data: any) {
|
||||
const res = await del<Liveaddrule>('wallect/' + data);
|
||||
if(res.code==0){
|
||||
message.success("删除成功")
|
||||
router.push("/mine/wallet")
|
||||
}
|
||||
|
||||
console.log(res)
|
||||
@@ -427,18 +439,20 @@ export async function transactioninfo(data?: any){
|
||||
export async function editpassword(data?: any): Promise<any> {
|
||||
console.log(data,111)
|
||||
const newdata={
|
||||
memberid:0,
|
||||
password: "",
|
||||
password:"",
|
||||
newpassword: "",
|
||||
topassword: ""
|
||||
}
|
||||
newdata.memberid=data.memberid
|
||||
newdata.password=data.password
|
||||
newdata.topassword=data.repassword
|
||||
newdata.password = data.original
|
||||
newdata.newpassword=data.password
|
||||
newdata.topassword=data.topassword
|
||||
console.log(newdata)
|
||||
const res = await post<Liveaddrule>('resetPassword',newdata)
|
||||
const res = await put<Liveaddrule>('/member/' + data.memberid,newdata)
|
||||
if(res.code==0){
|
||||
message.success("修改成功")
|
||||
|
||||
}else{
|
||||
message.error(res.msg)
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -632,9 +646,9 @@ export async function editsystemsetting(e?: any): Promise<boolean> {
|
||||
const res = await put(`member/${store.state.userinfo.memberid}`,newdata);
|
||||
console.log(res)
|
||||
if(res.code == 0){
|
||||
location.reload();
|
||||
message.success("修改成功")
|
||||
store.dispatch("setUserInfo");
|
||||
location.reload();
|
||||
return true;
|
||||
}else{
|
||||
message.error(res.msg);
|
||||
@@ -669,8 +683,9 @@ interface SendSms{
|
||||
msg: string;
|
||||
}
|
||||
|
||||
export async function sendsms(phone: string, type: number): Promise<boolean>{
|
||||
const res = await post<SendSms>("SendSms", {phone, type});
|
||||
export async function sendsms(code: string ,phone: string): Promise<boolean>{
|
||||
const type = code == '86' ? 0 : 1;
|
||||
const res = await post<SendSms>("SendSms", {phone: code + phone, type});
|
||||
console.log(res);
|
||||
if(res.code == 0){
|
||||
message.success(res.msg);
|
||||
@@ -755,7 +770,8 @@ export async function putmember(data: any): Promise<any>{
|
||||
language: data.languageValue,
|
||||
tlanguage: data.tlanguageValue,
|
||||
video: data.video,
|
||||
desc: data.desc
|
||||
desc: data.desc,
|
||||
videoid: data.videoid
|
||||
}
|
||||
console.log(newdata)
|
||||
const res = await put<Liveaddrule>(`member/${store.state.userinfo.memberid}`, newdata)
|
||||
@@ -787,6 +803,7 @@ interface LiveInfo {
|
||||
deleted_at: null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
livestatus: number;
|
||||
}
|
||||
|
||||
interface StudentList {
|
||||
@@ -839,8 +856,8 @@ export async function cancellive(id: number, status: number){
|
||||
/**
|
||||
* 修改手机号
|
||||
*/
|
||||
export async function changetel(e: string) {
|
||||
const res = await put(`member/${store.state.userinfo.memberid}`,{mobile:e});
|
||||
export async function changetel(code: string,e: string) {
|
||||
const res = await put(`member/${store.state.userinfo.memberid}`,{code: code, mobile:e});
|
||||
console.log(res)
|
||||
if(res.code == 0){
|
||||
message.success("修改成功")
|
||||
@@ -869,6 +886,14 @@ export async function checksmscode(phone: string, smscode: string){
|
||||
|
||||
|
||||
export async function register(data: any){
|
||||
/* eslint-disable */
|
||||
const utc = require('dayjs/plugin/utc') // dependent on utc plugin
|
||||
/* eslint-disable */
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const days: any = dayjs;
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
const res = await post<any>("register",{
|
||||
mobile: data.phone,
|
||||
code: data.quhao,
|
||||
@@ -877,7 +902,10 @@ export async function register(data: any){
|
||||
name: data.name,
|
||||
email: data.emil,
|
||||
mtongue: data.muyu,
|
||||
tlanguage: data.jiaoshou
|
||||
tlanguage: data.jiaoshou,
|
||||
language: getValue("Lanvuage") || 'zh',
|
||||
zoneid: days.tz.guess()
|
||||
|
||||
})
|
||||
if(res.code == 0){
|
||||
message.success(res.msg)
|
||||
@@ -938,13 +966,22 @@ export async function liveinfo(id: number): Promise<any>{
|
||||
dateline: liveinfo.dateline,
|
||||
livetime: liveinfo.livetime,
|
||||
livenumber: liveinfo.livenumber,
|
||||
desc: liveinfo.desc
|
||||
desc: liveinfo.desc,
|
||||
status: liveinfo.status
|
||||
}
|
||||
}
|
||||
|
||||
export async function setlive(data: any){
|
||||
// data.status = data.livestatus;
|
||||
console.log(data)
|
||||
const info = await put("live/" + data.id, data)
|
||||
console.log(info.data)
|
||||
if(info.code==0){
|
||||
message.success(info.msg)
|
||||
router.push("/regime/live")
|
||||
}else{
|
||||
message.error(info.msg)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -996,11 +1033,26 @@ export async function luzhi(roomid: string){
|
||||
export async function getaddr() {
|
||||
const res = await get<any>('ip');
|
||||
const gj = res.data.address.split("|")[0];
|
||||
if(gj == "CN"){
|
||||
return ["zh", "中文", '人民币¥'];
|
||||
}else {
|
||||
return ['en', 'English', '美元$']
|
||||
const lan = getValue("Lanvuage");
|
||||
const qh = await get<any>('countryCode', {
|
||||
longitude: res.data.content.point.x,
|
||||
latitude: res.data.content.point.y
|
||||
});
|
||||
console.log(qh)
|
||||
if(lan != null && lan){
|
||||
if(gj == "CN"){
|
||||
return {hb: '人民币¥', qh};
|
||||
}else {
|
||||
return {hb: '美元$'}
|
||||
}
|
||||
}else{
|
||||
if(gj == "CN"){
|
||||
return {yy: "zh", yyx: "中文", hb: '人民币¥', qh};
|
||||
}else {
|
||||
return {yy: 'en', yyx: 'English', hb: '美元$', qh}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// export async function StopMCUMixTranscode(roomid: number) {
|
||||
@@ -1013,3 +1065,17 @@ export async function interests() {
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function getset() {
|
||||
const res = await get('getset');
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function setheadimg(src: string){
|
||||
const res = await put(`member/${store.state.userinfo.memberid}`,{img: src});
|
||||
console.log(res)
|
||||
}
|
||||
|
||||
export async function setusername(src: string){
|
||||
const res = await put(`member/${store.state.userinfo.memberid}`,{name: src});
|
||||
console.log(res)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div style="display: flex">
|
||||
<img :src="i.img" alt="" />
|
||||
<div class="stuinfo">
|
||||
<div>{{ i.name }} {{zid}}</div>
|
||||
<div>{{ i.name }}</div>
|
||||
<div class="lessonname">{{ i.interest }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -35,8 +35,8 @@
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.videoitem{
|
||||
width: 226px;
|
||||
height: 198px;
|
||||
min-width: 226px;
|
||||
// height: 198px;
|
||||
background-color: #fff;
|
||||
border-radius: 17px;
|
||||
overflow: hidden;
|
||||
@@ -46,7 +46,8 @@
|
||||
position: relative;
|
||||
box-shadow: 0px 5px 6px 0px rgba(158, 158, 158, 0.11);
|
||||
.cover{
|
||||
width: 100%;
|
||||
width: 226px;
|
||||
min-width: 100%;
|
||||
height: 127px;
|
||||
}
|
||||
.play{
|
||||
@@ -75,7 +76,8 @@
|
||||
.info{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 18px;
|
||||
margin-top: 9px;
|
||||
margin-bottom: 9px;
|
||||
margin-left: 18px;
|
||||
|
||||
.datetime{
|
||||
@@ -174,15 +176,12 @@ export default defineComponent({
|
||||
const lan: any = useI18n();
|
||||
function navto(){
|
||||
let url = '';
|
||||
switch (props.type) {
|
||||
switch (props.status) {
|
||||
case 1:
|
||||
url = '/regime/livedetail';
|
||||
url = '/regime/liveing';
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
url = '/regime/livedetail';
|
||||
break;
|
||||
case 3:
|
||||
url = '/regeime/liveing';
|
||||
}
|
||||
console.log(props.zid);
|
||||
if(props.zid != undefined){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="video">
|
||||
<video :controls="true" :src="info.livestatus == 0 ? info.fileurl : info.vodurl"></video>
|
||||
<video :id=" 'a' + time" style="width:100%;height:100%"></video>
|
||||
<div class="liveinfo">
|
||||
<div class="left">
|
||||
<div>
|
||||
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<img src="@/static/images/livewatch.png" alt="" class="icon">
|
||||
<span>{{info.dateline}}</span>
|
||||
<span>{{info.tlanguage}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<img src="@/static/images/livetimetake.png" alt="" class="icon">
|
||||
@@ -17,14 +17,14 @@
|
||||
</div>
|
||||
<div>
|
||||
<img src="@/static/images/shoucang.png" alt="" class="icon">
|
||||
<span class="score">5.0{{lan.$t('fen')}}</span>
|
||||
<span class="score">{{info.score}}{{lan.$t('fen')}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex">
|
||||
<div class="right" @click="bianji">
|
||||
<div class="right" @click="bianji" v-if="info.livestatus == 0">
|
||||
{{lan.$t('bianjixinxi')}}
|
||||
</div>
|
||||
<div class="right" @click="kaishi">
|
||||
<div class="right" @click="kaishi" v-if="info.livestatus == 0">
|
||||
{{lan.$t('kaishizhibo')}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -38,11 +38,14 @@
|
||||
height: 563px;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
> video {
|
||||
width: 100%;
|
||||
height: 505px;
|
||||
}
|
||||
.liveinfo{
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 58px;
|
||||
@@ -87,7 +90,8 @@
|
||||
import { livestart } from '@/api';
|
||||
import router from '@/router';
|
||||
import { useI18n } from '@/utils/i18n';
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { defineComponent, onUpdated, ref } from "vue";
|
||||
import { onBeforeRouteLeave } from 'vue-router';
|
||||
|
||||
export default defineComponent({
|
||||
props:{
|
||||
@@ -110,12 +114,30 @@ export default defineComponent({
|
||||
})
|
||||
}
|
||||
}
|
||||
const time = ref(new Date().getTime())
|
||||
let play: any;
|
||||
onUpdated(()=>{
|
||||
// console.log(props.url)
|
||||
if(props.info){
|
||||
play = window.TCPlayer('a' + time.value, {
|
||||
fileID: (props.info.livestatus == 0 ? props.info.fileid : props.info.vodid),
|
||||
appID: '1303872925'
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
console.log(121)
|
||||
play.dispose()
|
||||
next()
|
||||
})
|
||||
// const liveinfo = ref(props.liveinfo)
|
||||
return {
|
||||
bianji,
|
||||
kaishi,
|
||||
lan
|
||||
lan,
|
||||
time
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -157,7 +157,7 @@
|
||||
width: 57px;
|
||||
height: 57px;
|
||||
border-radius: 50%;
|
||||
background-color: #0f0;
|
||||
// background-color: #0f0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,56 +5,20 @@
|
||||
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="item">
|
||||
<div class="item" v-for="(item,index) in list" :key="index">
|
||||
<div style="display:flex">
|
||||
<div class="stuinfo">
|
||||
<div>andy</div>
|
||||
<div>{{item.name}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="icons">
|
||||
<img src="@/static/images/camera.png" alt="" class="icon">
|
||||
<img src="@/static/images/vol.png" alt="" class="icon">
|
||||
<img src="@/static/images/camera.png" @click="cameta(item.memberid)" alt="" class="icon">
|
||||
<img src="@/static/images/vol.png" alt="" class="icon" @click="vol(item.memberid)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div style="display:flex">
|
||||
<div class="stuinfo">
|
||||
<div>andy</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="icons">
|
||||
<img src="@/static/images/camera.png" alt="" class="icon">
|
||||
<img src="@/static/images/vol.png" alt="" class="icon">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div style="display:flex">
|
||||
<div class="stuinfo">
|
||||
<div>andy</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="icons">
|
||||
<img src="@/static/images/camera.png" alt="" class="icon">
|
||||
<img src="@/static/images/vol.png" alt="" class="icon">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div style="display:flex">
|
||||
<div class="stuinfo">
|
||||
<div>andy</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="icons">
|
||||
<img src="@/static/images/camera.png" alt="" class="icon">
|
||||
<img src="@/static/images/vol.png" alt="" class="icon">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -147,5 +111,23 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({});
|
||||
export default defineComponent({
|
||||
props:{
|
||||
list:{
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
setup(prop,context){
|
||||
function cameta(id: number){
|
||||
context.emit("cameta", id)
|
||||
}
|
||||
function vol(id: number){
|
||||
context.emit("vol", id)
|
||||
}
|
||||
return {
|
||||
cameta,
|
||||
vol
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -2,7 +2,10 @@
|
||||
<div class="menu">
|
||||
<div class="user" style="overflow: hidden;">
|
||||
<div class="user" :class="{'seltop': selnum == 0}">
|
||||
<img :src="userinfo.img" alt="" class="head">
|
||||
<!-- <img :src="userinfo.img" alt="" class="head"> -->
|
||||
<a-avatar :size="85" shape="circle" class="head" :src="userinfo.img">
|
||||
<template v-slot:icon><UserOutlined /></template>
|
||||
</a-avatar>
|
||||
<div class="name">{{userinfo.name}}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -20,8 +23,8 @@
|
||||
<div style="overflow: hidden;">
|
||||
<div class="item" :class="{'selbottom': selnum == list.length - 1}"></div>
|
||||
</div>
|
||||
<div class="item loginout">
|
||||
<div class="route" @click="logout">
|
||||
<div class="item loginout" @click="visible = true">
|
||||
<div class="route">
|
||||
<img src="../static/images/tuichu.png" alt="" class="icon">
|
||||
<div class="title">
|
||||
{{lan.$t('tuichu')}}
|
||||
@@ -29,6 +32,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-modal v-model:visible="visible" :title="lan.$t('tishi')" @ok="logout">
|
||||
<p>{{lan.$t('querentuichu')}}</p>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
@@ -185,11 +191,22 @@ export default defineComponent({
|
||||
|
||||
const userinfo = computed(() => store.state.userinfo)
|
||||
// 设置当前路由
|
||||
for(const i in list){
|
||||
console.log(list[i].route==useRoute().path)
|
||||
if(list[i].route == useRoute().path){
|
||||
selnum.value = parseInt(i);
|
||||
const routelist = [
|
||||
["/mine/archives"],
|
||||
['/mine/webcast'],
|
||||
['/mine/video'],
|
||||
['/mine/wallet', '/mine/cashout', '/mine/addaccount', '/mine/transaction', '/mine/transactionxq'],
|
||||
['/mine/liststatistic'],
|
||||
['/mine/aboutus']
|
||||
]
|
||||
for(const i in routelist){
|
||||
for(const j in routelist[i]){
|
||||
console.log(routelist[i][j]==useRoute().path)
|
||||
if(routelist[i][j] == useRoute().path){
|
||||
selnum.value = parseInt(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 跳转路由与赋值对应的下标
|
||||
@@ -209,7 +226,10 @@ export default defineComponent({
|
||||
selnum.value = index;
|
||||
}
|
||||
|
||||
const visible = ref(false);
|
||||
|
||||
function logout(): void{
|
||||
console.log("退出")
|
||||
store.commit("login", false)
|
||||
saveValue("token", "");
|
||||
setToken();
|
||||
@@ -225,6 +245,7 @@ export default defineComponent({
|
||||
logout,
|
||||
mouse,
|
||||
lan,
|
||||
visible,
|
||||
jiantou: require('../static/images/jiantou.png'),
|
||||
jiantous: require('../static/images/kuozhan1.png')
|
||||
}
|
||||
|
||||
@@ -41,34 +41,7 @@
|
||||
</template>
|
||||
</a-dropdown>
|
||||
|
||||
<a-dropdown :trigger="['click']" :getPopupContainer="triggerNode => triggerNode.parentNode" v-if="islogin">
|
||||
<div class="item" @click="e => e.preventDefault()">
|
||||
<img src="@/static/images/qianbi.png" alt="" class="icon">
|
||||
<div class="name">{{userinfo.currency}}</div>
|
||||
<img src="@/static/images/jiantou2.png" alt="" class="down">
|
||||
</div>
|
||||
<template v-slot:overlay>
|
||||
<a-menu style="max-height:70vh;overflow: auto;">
|
||||
<!-- 货币 -->
|
||||
<a-menu-item v-for="(i,j) in currencylist" :key="j" style="position: relative;">
|
||||
<div class="selitem" @click="currencychange(i.value)">
|
||||
<span :style="{'color': i.name == userinfo.currency ? '#06C7AE' : ''}">{{i.name}} </span>
|
||||
<img src="@/static/images/duihao.png" alt="" v-if="i.name == userinfo.currency" class="duihao">
|
||||
</div>
|
||||
</a-menu-item>
|
||||
<!-- <a-menu-item key="1">
|
||||
<div class="selitem">
|
||||
<span>时区2b</span>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="3">
|
||||
<div class="selitem">
|
||||
<span>时区3b</span>
|
||||
</div>
|
||||
</a-menu-item> -->
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
|
||||
<a-dropdown :trigger="['click']" :getPopupContainer="triggerNode => triggerNode.parentNode">
|
||||
<div class="item" @click="e => e.preventDefault()">
|
||||
<img src="@/static/images/yuyan.png" alt="" class="icon">
|
||||
@@ -208,6 +181,8 @@ import { computed, defineComponent, onMounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import axios from 'axios'
|
||||
import { useI18n } from '@/utils/i18n';
|
||||
import { setLanvuage } from '@/api/base';
|
||||
import { saveValue } from '@/utils/common';
|
||||
|
||||
export default defineComponent({
|
||||
props:{
|
||||
@@ -305,7 +280,7 @@ export default defineComponent({
|
||||
languagelist.value=await getlanguages()
|
||||
console.log(routes.path)
|
||||
nowroute.value=routes.path
|
||||
lan.locale.value = userinfo.value.languageValue
|
||||
// lan.locale.value = userinfo.value.languageValue
|
||||
})
|
||||
|
||||
function zonechange(e?: any){
|
||||
@@ -318,11 +293,21 @@ export default defineComponent({
|
||||
}
|
||||
function setlanguage(e?: any){
|
||||
console.log(e)
|
||||
editsystemsetting({language:e})
|
||||
lan.locale.value = e
|
||||
saveValue("Lanvuage", e)
|
||||
if(store.state.islogin){
|
||||
editsystemsetting({language:e})
|
||||
}else {
|
||||
location.reload();
|
||||
// setLanvuage(e)
|
||||
}
|
||||
// lan.locale.value = e
|
||||
|
||||
}
|
||||
function toindex(){
|
||||
router.push("/")
|
||||
if(!store.state.islogin){
|
||||
return ;
|
||||
}
|
||||
router.push("/mine/archives")
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
<div class="num">{{(score+'').split('.')[1]?score:score+'.0'}} {{lan.$t('fen')}}</div>
|
||||
</div>
|
||||
<div class="all" @click="findall(replyid)" >
|
||||
<span>={{lan.$t('suoyouhuifu')}}</span>
|
||||
<img src="@/static/images/arrowdownblue.png" alt="">
|
||||
<span>{{lan.$t('suoyouhuifu')}}</span>
|
||||
<img v-show="!iszk" src="@/static/images/arrowdownblue.png" alt="">
|
||||
<img v-show="iszk" src="@/static/images/arrowdownblueup.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="cont">
|
||||
@@ -23,7 +24,13 @@
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="date">{{date}}</div>
|
||||
<div class="reply" @click="reply(username)">{{lan.$t('huifu')}}</div>
|
||||
<div style="display: flex">
|
||||
<div class="del" @click="del(replyid)">
|
||||
{{lan.$t('shanchu')}}
|
||||
</div>
|
||||
<div class="reply" @click="reply(username)">{{lan.$t('huifu')}}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="huifu" v-if="ifshow">
|
||||
@@ -35,7 +42,8 @@
|
||||
:date="i.created_at"
|
||||
:memberid="i.memberid"
|
||||
:replyid="i.commentid"
|
||||
@replying="reply"
|
||||
:info="i"
|
||||
@replying="replytow"
|
||||
@reload="refresh"
|
||||
></ReviewItemtwo>
|
||||
</div>
|
||||
@@ -63,7 +71,7 @@
|
||||
width: 57px;
|
||||
height: 57px;
|
||||
border-radius: 50%;
|
||||
background-color: #0f0;
|
||||
// background-color: #0f0;
|
||||
|
||||
}
|
||||
.name{
|
||||
@@ -113,16 +121,24 @@
|
||||
font-size: 10px;
|
||||
color: #08AE98;
|
||||
}
|
||||
.del{
|
||||
font-size: 10px;
|
||||
color:#D12C2D!important;
|
||||
flex-shrink: 0;
|
||||
margin-right: 28px;
|
||||
}
|
||||
}
|
||||
.huifu{
|
||||
border-top: solid 1px #eee;
|
||||
margin-left: 56px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script lang="ts">
|
||||
import { getcommentlist } from '@/api';
|
||||
import { delreply, getcommentlist } from '@/api';
|
||||
import { useI18n } from '@/utils/i18n';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { defineComponent, onMounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import ReviewItemtwo from "./ReviewItemtwo.vue"
|
||||
@@ -160,14 +176,22 @@ export default defineComponent({
|
||||
const reviewlist=ref({})
|
||||
const videoid=ref(useRoute().query.id)
|
||||
const ifshow=ref(false)
|
||||
onMounted(async () => {
|
||||
reviewlist.value=await getcommentlist({type: 2,id: videoid.value})
|
||||
})
|
||||
const iszk = ref(false)
|
||||
const url = useRoute().path
|
||||
let type = 1;
|
||||
if(url == '/regime/livedetail'){
|
||||
type = 1;
|
||||
}else{
|
||||
type = 2;
|
||||
}
|
||||
// onMounted(async () => {
|
||||
// reviewlist.value=await getcommentlist({type: type,id: videoid.value})
|
||||
// })
|
||||
|
||||
async function refresh(e?: any){
|
||||
console.log("rekload")
|
||||
reviewlist.value=await getcommentlist({type: 2,id: videoid.value})
|
||||
replylist.value=await getcommentlist({type: 3,id: e})
|
||||
// reviewlist.value=await getcommentlist({type: type,id: videoid.value})
|
||||
replylist.value=await getcommentlist({type: 3,id: prop.replyid})
|
||||
}
|
||||
const stars=ref<Array<number>>([])
|
||||
console.log(prop.score)
|
||||
@@ -191,13 +215,47 @@ export default defineComponent({
|
||||
|
||||
function reply(e?: string){
|
||||
console.log(155)
|
||||
context.emit("replying",{name: e,replyid: prop.replyid,score: prop.score})
|
||||
context.emit("replying",{name: e,cid: prop.replyid,score: prop.score})
|
||||
}
|
||||
function replytow(e: any){
|
||||
console.log(1556)
|
||||
e.cid = prop.replyid
|
||||
context.emit("replying", e)
|
||||
}
|
||||
async function findall(e: number){
|
||||
console.log("all")
|
||||
if(iszk.value){
|
||||
iszk.value = false;
|
||||
ifshow.value= false;
|
||||
return ;
|
||||
}
|
||||
iszk.value = true
|
||||
replylist.value =await getcommentlist({type: 3,id: e})
|
||||
ifshow.value=ifshow.value==false?true:false
|
||||
ifshow.value = true;
|
||||
}
|
||||
async function reload() {
|
||||
if(iszk.value == true){
|
||||
console.log(iszk.value,121212)
|
||||
iszk.value = false;
|
||||
if(prop.replyid){
|
||||
findall(prop.replyid)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
async function del(e?: number){
|
||||
console.log(e,1212)
|
||||
const res=await delreply(e)
|
||||
if(res.code != 0){
|
||||
message.error(res.msg)
|
||||
}
|
||||
context.emit("reload")
|
||||
// reload()
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
return {
|
||||
stars,
|
||||
reply,
|
||||
@@ -206,7 +264,11 @@ export default defineComponent({
|
||||
reviewlist,
|
||||
refresh,
|
||||
ifshow,
|
||||
lan
|
||||
lan,
|
||||
del,
|
||||
iszk,
|
||||
reload,
|
||||
replytow
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,18 +5,20 @@
|
||||
<div class="name myname" v-if="parseInt(memberid)==myid">{{username}}</div>
|
||||
<div class="name" v-else>{{username}}</div>
|
||||
</div>
|
||||
<div class="cont">
|
||||
{{content}}
|
||||
<div class="cont" v-html="inhtml()">
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="date">{{date}}</div>
|
||||
<div class="operate">
|
||||
<!-- <div class="reply" @click="reply(username)">
|
||||
回复
|
||||
</div> -->
|
||||
<div class="del" @click="del(replyid)" v-if="parseInt(memberid)==myid">
|
||||
|
||||
|
||||
<div class="del" @click="del(replyid)">
|
||||
{{lan.$t('shanchu')}}
|
||||
</div>
|
||||
<div class="reply" @click="reply(username)">
|
||||
{{lan.$t('huifu')}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -33,7 +35,7 @@
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 50%;
|
||||
background-color: #0f0;
|
||||
// background-color: #0f0;
|
||||
|
||||
}
|
||||
.name{
|
||||
@@ -69,7 +71,9 @@
|
||||
margin-left: 67px;
|
||||
font-size: 11px;
|
||||
line-height: 1.2;
|
||||
|
||||
::v-deep(span){
|
||||
color: #2581D0;
|
||||
}
|
||||
}
|
||||
.bottom{
|
||||
display: flex;
|
||||
@@ -91,7 +95,8 @@
|
||||
}
|
||||
.del{
|
||||
color:#D12C2D!important;
|
||||
margin-left: 28px;
|
||||
font-size: 10px;
|
||||
margin-right: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,10 +105,14 @@
|
||||
import { delreply } from '@/api';
|
||||
import store from '@/store';
|
||||
import { useI18n } from '@/utils/i18n';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
info:{
|
||||
type: Object
|
||||
},
|
||||
photo: {
|
||||
type: String
|
||||
},
|
||||
@@ -139,15 +148,25 @@ export default defineComponent({
|
||||
const res=await delreply(e)
|
||||
if(res.code==0){
|
||||
context.emit("reload",prop.replyid)
|
||||
}else {
|
||||
message.error(res.msg)
|
||||
}
|
||||
console.log(res)
|
||||
|
||||
}
|
||||
function inhtml(){
|
||||
if(prop.info){
|
||||
return prop.content + (prop.info.replyid != 0 ? '//<span>' + prop.info.with + '</span>' + prop.info.replyContent : '')
|
||||
}
|
||||
// {{content}}{{info.replyid != 0 ? '//' + info.with + info.replyContent : ''}}
|
||||
|
||||
}
|
||||
return {
|
||||
reply,
|
||||
myid,
|
||||
del,
|
||||
lan
|
||||
lan,
|
||||
inhtml
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -23,14 +23,20 @@
|
||||
<span>{{lan.$t('zhuangtai')}}</span>
|
||||
<span class="status">{{lan.$t('shenheing')}}</span>
|
||||
</div>
|
||||
<!-- <div class="item item1" v-if="status==1">
|
||||
<span>{{lan.$t('zhuangtai')}}</span>
|
||||
<span class="status1">{{lan.$t('shenheing')}}</span>
|
||||
|
||||
</div> -->
|
||||
<div class="item item1" v-if="status==2">
|
||||
<span>{{lan.$t('zhuangtai')}}</span>
|
||||
<span class="status1">{{lan.$t('shenheweitongguo')}}</span>
|
||||
<span class="status1" style="color: #D12C2E">{{lan.$t('shenheweitongguo')}}</span>
|
||||
</div>
|
||||
<div class="item item1" v-if="status==1">
|
||||
<span style="flex-shrink:0">{{lan.$t('yuanyin')}}</span>
|
||||
<span class="status"> {{lan.$t('yuanyintext')}} </span>
|
||||
</div>
|
||||
<div class="item item1" v-if="status==2">
|
||||
<span style="flex-shrink:0">{{lan.$t('yuanyin')}}</span>
|
||||
<span> {{yuanyin}} </span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="button">
|
||||
<div class="modify" @click="update(videoid)">{{lan.$t('xiugaishipin')}}</div>
|
||||
@@ -123,20 +129,23 @@ import { useRoute } from 'vue-router';
|
||||
|
||||
export default defineComponent({
|
||||
props:{
|
||||
date:{
|
||||
date: {
|
||||
type:String
|
||||
},
|
||||
watch:{
|
||||
watch: {
|
||||
type:Number
|
||||
},
|
||||
share:{
|
||||
share: {
|
||||
type:Number
|
||||
},
|
||||
status:{
|
||||
status: {
|
||||
type:Number
|
||||
},
|
||||
videoid:{
|
||||
videoid: {
|
||||
type:Number
|
||||
},
|
||||
yuanyin: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
setup(){
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="state audit" v-if="status==0">
|
||||
{{lan.$t('shenhezhong')}}
|
||||
{{transcoding == 0 ? lan.$t('zhuanmazhong') : lan.$t('shenhezhong')}}
|
||||
</div>
|
||||
<div class="state audit fail" v-if="status==2">
|
||||
{{lan.$t('weitongguo')}}
|
||||
@@ -36,8 +36,8 @@
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.videoitem{
|
||||
width: 226px;
|
||||
height: 198px;
|
||||
min-width: 226px;
|
||||
// height: 198px;
|
||||
background-color: #fff;
|
||||
border-radius: 17px;
|
||||
overflow: hidden;
|
||||
@@ -47,7 +47,8 @@
|
||||
box-shadow: 0px 5px 6px 0px rgba(158, 158, 158, 0.11);
|
||||
cursor: pointer;
|
||||
.cover{
|
||||
width: 100%;
|
||||
width: 226px;
|
||||
min-width: 100%;
|
||||
height: 127px;
|
||||
// background-color: #0f0;
|
||||
}
|
||||
@@ -75,7 +76,8 @@
|
||||
.info{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 18px;
|
||||
margin-top: 9px;
|
||||
margin-bottom: 9px;
|
||||
margin-left: 18px;
|
||||
|
||||
.datetime{
|
||||
@@ -166,6 +168,9 @@ export default defineComponent({
|
||||
},
|
||||
status:{
|
||||
type:Number
|
||||
},
|
||||
transcoding: {
|
||||
type: Number
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="video">
|
||||
|
||||
<video :src="url" :controls="true"></video>
|
||||
<div class="title">{{title}}</div>
|
||||
<video style="width:100%; height:100%;" :id="'a' + url" ></video>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
@@ -10,25 +10,53 @@
|
||||
height: 563px;
|
||||
border-radius: 17px;
|
||||
background: white;
|
||||
position: relative;
|
||||
// background-color: #0f0;
|
||||
overflow: hidden;
|
||||
>video{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.title{
|
||||
position: absolute;
|
||||
top: 23px;
|
||||
left: 51px;
|
||||
font-size: 13px;
|
||||
color: #fff;
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import router from '@/router';
|
||||
import { defineComponent, onMounted, onUpdated } from 'vue';
|
||||
import { onBeforeRouteLeave, useRouter } from 'vue-router';
|
||||
|
||||
export default defineComponent({
|
||||
props:{
|
||||
url:{
|
||||
type:String
|
||||
url: {
|
||||
type: String
|
||||
},
|
||||
title: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
setup(){
|
||||
setup(props, ctx){
|
||||
console.log(1)
|
||||
let play: any;
|
||||
onUpdated(()=>{
|
||||
console.log(props.url)
|
||||
play = window.TCPlayer('a' + props.url, {
|
||||
fileID: props.url,
|
||||
appID: '1303872925'
|
||||
});
|
||||
})
|
||||
onBeforeRouteLeave((to, from, next) => {
|
||||
console.log(121)
|
||||
play.dispose()
|
||||
next()
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="review">
|
||||
<div class="review" v-if="reviewlist.data.length != 0">
|
||||
<div class="top">
|
||||
<div class="title">
|
||||
{{lan.$t('shipinpingjia')}}
|
||||
<span>8.0{{lan.$t('fen')}}</span>
|
||||
<span>{{videoinfo}}{{lan.$t('fen')}}</span>
|
||||
</div>
|
||||
<div class="score">8.0{{lan.$t('fen')}}</div>
|
||||
<div class="score">{{videoinfo}}{{lan.$t('fen')}}</div>
|
||||
</div>
|
||||
<div class="list" v-for="(i,j) in reviewlist.data" :key="j" >
|
||||
<ReviewItem
|
||||
@@ -18,6 +18,8 @@
|
||||
:replyid="i.commentid"
|
||||
@replying="reply"
|
||||
@findall="findreply"
|
||||
@reload="getlist"
|
||||
:ref="el => {list[j] = el}"
|
||||
></ReviewItem>
|
||||
|
||||
|
||||
@@ -111,18 +113,34 @@ export default defineComponent({
|
||||
props: {
|
||||
videoid: {
|
||||
type: Number
|
||||
},
|
||||
videoinfo: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
setup(prop,context){
|
||||
const lan: any = useI18n();
|
||||
const reviewlist=ref({})
|
||||
const reviewlist=ref<any>({data: []})
|
||||
const commentval=ref<string>('')
|
||||
const uinfo=ref<any>({})
|
||||
const replylist =ref({})
|
||||
const videoid=ref(useRoute().query.id)
|
||||
const url = useRoute().path
|
||||
const list = ref<any>([])
|
||||
let type = 1;
|
||||
if(url == '/regime/livedetail'){
|
||||
type = 1;
|
||||
}else{
|
||||
type = 2;
|
||||
}
|
||||
async function getlist(){
|
||||
reviewlist.value=await getcommentlist({type: type,id: videoid.value})
|
||||
}
|
||||
onMounted(async () => {
|
||||
reviewlist.value=await getcommentlist({type: 2,id: videoid.value})
|
||||
|
||||
getlist()
|
||||
})
|
||||
|
||||
console.log(useRoute().query)
|
||||
console.log(store.state.userinfo.memberid,"userifno")
|
||||
interface SendData{
|
||||
@@ -131,17 +149,24 @@ export default defineComponent({
|
||||
teacherid?: number;
|
||||
score?: number;
|
||||
content?: string;
|
||||
replyid?: number;
|
||||
}
|
||||
function send(){
|
||||
const data = ref<SendData>({})
|
||||
data.value.type=3;
|
||||
data.value.cid=uinfo.value.replyid
|
||||
data.value.cid=uinfo.value.cid
|
||||
data.value.replyid = uinfo.value.replyid
|
||||
// data.value.teacherid=uinfo.value.memberid
|
||||
// data.value.score=uinfo.value.score
|
||||
data.value.content=commentval.value
|
||||
console.log(data.value,2221)
|
||||
if(uinfo.value.name){
|
||||
addcomment(toRaw(data.value))
|
||||
addcomment(toRaw(data.value)).then(()=>{
|
||||
for(const i in list.value){
|
||||
list.value[i].reload()
|
||||
}
|
||||
})
|
||||
|
||||
}else{
|
||||
message.error(lan.$t('xuanzehuifuxuesheng'))
|
||||
}
|
||||
@@ -173,7 +198,9 @@ export default defineComponent({
|
||||
haslist,
|
||||
reviewlist,
|
||||
refresh,
|
||||
lan
|
||||
lan,
|
||||
list,
|
||||
getlist
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { getValue } from '@/utils/common';
|
||||
import axios from 'axios'
|
||||
axios.defaults.baseURL = '/beelink/public/home/';
|
||||
axios.defaults.baseURL = 'https://case.sy-my.net/beelink/public/home/';
|
||||
axios.defaults.headers.common['Authorization'] = "Bearer " + getValue("token");
|
||||
axios.defaults.headers.common['Language'] = !getValue("Lanvuage") ? 'zh' : getValue("Lanvuage");
|
||||
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
|
||||
export default axios
|
||||
@@ -42,6 +42,7 @@ export default {
|
||||
banquan: "Copyright Beelink Inc. All rights reserved 2019-2022",
|
||||
zhanghao: "accounts",
|
||||
shuruzhanghao: "Please enter your email or mobile phone number",
|
||||
shuruzhanghaol: "Please enter your email address or country number + mobile phone number(34690xxx)",
|
||||
mima: "password",
|
||||
shurumima: "Please enter your password",
|
||||
wangjimima: "Forget the password?",
|
||||
@@ -83,18 +84,18 @@ export default {
|
||||
xiugai: "modify",
|
||||
shuruxinnicheng: "Please enter a new nickname",
|
||||
jibenxinxi: "Basic information",
|
||||
laiziguojia: "From the country",
|
||||
laiziguojia: "From a country or region",
|
||||
juzhudi: "Place of residence",
|
||||
shurujuzhudi: "Enter residence",
|
||||
wohaihuishuo: "I would also say",
|
||||
shuliandu: "Proficiency",
|
||||
jixutianjia: "Continue adding",
|
||||
xindemuyu: "Please enter your new mother tongue",
|
||||
duanshipin: "Short video",
|
||||
duanshipin: "Short video introduction",
|
||||
shipinyaoqiu: "Video requirements:",
|
||||
shipinyaoqiu1: "The time required for uploading video is within 30s",
|
||||
shipinyaoqiu2: "Support file size 100m",
|
||||
shipinyaoqiu3: "File extension: FIV, MP4",
|
||||
shipinyaoqiu3: "File extension: FLV, MP4, MOV, AVI, WAV",
|
||||
ziwojieshao: "self-introduction",
|
||||
lianxifangshi: "contact information",
|
||||
genghuanshoujihao: "Change mobile phone number",
|
||||
@@ -189,9 +190,8 @@ export default {
|
||||
xuanzezhanghu: "Select account",
|
||||
tixianjine: "Withdrawal amount",
|
||||
quanbujine: "Total amount",
|
||||
tixianzhu: "Note: 0.1% service fee will be charged for each withdrawal, with a minimum of ¥ 0.1",
|
||||
yueshu: "Your balance is only",
|
||||
zuiditixian: "Minimum withdrawal amount ¥ 100",
|
||||
zuiditixian: "Minimum withdrawal amount",
|
||||
mingxichaxun: "Details inquiry",
|
||||
kaishiriqi: "Please select the start date",
|
||||
jieshuriqi: "Please select the end date",
|
||||
@@ -295,5 +295,24 @@ export default {
|
||||
fengmianyaoqiu2:"File size limit: 2m",
|
||||
kahaoweikong:"Card number cannot be empty",
|
||||
kaihuhangweikong:"Swiftcode cannot be empty",
|
||||
shensu:"Appeal"
|
||||
shensu:"Appeal",
|
||||
querenquxiao: "Are you sure to cancel the live broadcast?",
|
||||
tianjiatixian: "Add a withdrawal account",
|
||||
shijirenshu: "Actual number of live broadcast",
|
||||
shijishichang: "Actual live broadcast duration",
|
||||
cshipinyaoqiu1: "The video should be no longer than 10 minutes",
|
||||
cshipinyaoqiu2: "Video size cannot exceed 500M",
|
||||
shangchuanwancheng: "Please wait for the file to be uploaded",
|
||||
zhanghaocunzai: "account already exists",
|
||||
shouruguize: "Description of revenue rules",
|
||||
shouru1: "Live Revenue=Actual attendess x Actual duration x unit price",
|
||||
shouru2: "1 on 1, 20€/hour; 1 on N, 10€/hour/person, (1<N<=4)",
|
||||
shouru3: "Attention! You can only change the currency once!",
|
||||
tishi: "tip",
|
||||
querentuichu: "You confirm to exit?",
|
||||
huobitishi: "Attention! You can only change the currency once!",
|
||||
zhuanmazhong: "Transcoding in",
|
||||
shichangtishi:"",
|
||||
renshutishi: '',
|
||||
tixianzhu: "",
|
||||
}
|
||||
@@ -1,9 +1,31 @@
|
||||
import zh from "./zh"
|
||||
import en from "./en"
|
||||
import { getset } from '@/api';
|
||||
import { geti18n } from '@/utils/i18n';
|
||||
import store from '@/store';
|
||||
import { getValue } from '@/utils/common';
|
||||
getset().then((res: any)=>{
|
||||
zh.shichangtishi = `最短${res.timeLowerLimit}min, 最长${res.timeCeiling}min`
|
||||
en.shichangtishi = `The shortest is ${res.timeLowerLimit}min and the longest is ${res.timeCeiling}min`
|
||||
zh.renshutishi = `最少${res.lowerLimit}人, 最多${res.numberCeiling}人`
|
||||
en.renshutishi = `At least ${res.lowerLimit}, at most ${res.numberCeiling}`
|
||||
// en.shichangtishi = `Minimum ${res.lowerLimit} person, maximum ${res.numberCeiling} people`
|
||||
zh.tixianzhu = `注:每笔提现收取${res.sxf}服务费,最低${ res.symbol + res.minmoney }`
|
||||
en.tixianzhu = `Note: ${res.sxf} service fee will be charged for each withdrawal, with a minimum of ${ res.symbol + res.minmoney }`
|
||||
zh.zuiditixian = "最低提现金额" + res.symbol + res.minwithdraw
|
||||
en.zuiditixian = "Minimum withdrawal amount " + res.symbol + res.minwithdraw
|
||||
const i18n = geti18n();
|
||||
const loc = i18n.locale.value;
|
||||
i18n.locale.value = '';
|
||||
i18n.locale.value = loc;
|
||||
console.log('i18n')
|
||||
store.commit("setseting", res)
|
||||
})
|
||||
|
||||
export default {
|
||||
locale: "zh", //默认语言
|
||||
locale: getValue("Lanvuage") || 'zh', //默认语言
|
||||
messages: {
|
||||
zh,
|
||||
en
|
||||
zh: zh,
|
||||
en: en
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ export default {
|
||||
banquan:"Beelink公司版权所有 2019—2022",
|
||||
zhanghao:"帐号",
|
||||
shuruzhanghao:"请输入您的邮箱或者手机号",
|
||||
shuruzhanghaol:"请输入您的邮箱或者国家号+手机号(86186xxx)",
|
||||
mima:"密码",
|
||||
shurumima:"请输入您的密码",
|
||||
wangjimima:"忘记密码?",
|
||||
@@ -83,18 +84,18 @@ export default {
|
||||
xiugai:"修改",
|
||||
shuruxinnicheng:"请输入新的昵称",
|
||||
jibenxinxi:"基本信息",
|
||||
laiziguojia:"来自国家",
|
||||
laiziguojia:"来自国家或地区",
|
||||
juzhudi:"居住地",
|
||||
shurujuzhudi:"输入居住地",
|
||||
wohaihuishuo:"我还会说",
|
||||
shuliandu:"熟练度",
|
||||
jixutianjia:"继续添加",
|
||||
xindemuyu:"请输入新的母语",
|
||||
duanshipin:"短视频",
|
||||
duanshipin:"短视频介绍",
|
||||
shipinyaoqiu:"视频要求:",
|
||||
shipinyaoqiu1:"上传视频时间要求为30s之内",
|
||||
shipinyaoqiu2:"支持文件大小100M",
|
||||
shipinyaoqiu3:"文件扩展名:fiv、mp4",
|
||||
shipinyaoqiu3:"文件扩展名:flv、mp4、mov、avi、wav",
|
||||
fengmianyaoqiu:"封面要求:",
|
||||
fengmianyaoqiu1:"文件扩展名:jpg,png",
|
||||
fengmianyaoqiu2:"文件大小限制:2M",
|
||||
@@ -192,9 +193,8 @@ export default {
|
||||
xuanzezhanghu:"选择账户",
|
||||
tixianjine:"提现金额",
|
||||
quanbujine:"全部金额",
|
||||
tixianzhu:"注:每笔提现收取0.1%服务费,最低¥0.1",
|
||||
yueshu:"您的余额只有",
|
||||
zuiditixian:"最低提现金额¥100",
|
||||
zuiditixian:"最低提现金额",
|
||||
mingxichaxun:"明细查询",
|
||||
kaishiriqi:"请选择开始日期",
|
||||
jieshuriqi:"请选择结束日期",
|
||||
@@ -295,5 +295,24 @@ export default {
|
||||
guanbishibai:"关闭失败",
|
||||
kahaoweikong:"卡号不能为空",
|
||||
kaihuhangweikong:"开户行不能为空",
|
||||
shensu:"申诉"
|
||||
shensu:"申诉",
|
||||
querenquxiao: "您确认取消直播吗?",
|
||||
tianjiatixian: "添加提现账户",
|
||||
shijirenshu: "实际直播人数",
|
||||
shijishichang: "实际直播时长",
|
||||
cshipinyaoqiu1: "视频长度不能超过10分钟",
|
||||
cshipinyaoqiu2: "视频大小不能超过500m",
|
||||
shangchuanwancheng: "请等待文件上传完成",
|
||||
zhanghaocunzai: "帐号已存在",
|
||||
shuoruguize: "收入规则说明",
|
||||
shouru1: "直播收入=实际参加学生数x参加时长x单价",
|
||||
shouru2: "单价:1对1,20欧/小时;1对N,10欧/小时/人(1<N<=4)",
|
||||
shouru3: "请注意,货币一旦修改,不能变动",
|
||||
tishi: "提示",
|
||||
querentuichu: "您确认退出?",
|
||||
huobitishi: "请注意,货币一旦修改,不能变动。",
|
||||
zhuanmazhong: "转码中",
|
||||
shichangtishi:"",
|
||||
renshutishi: '',
|
||||
tixianzhu:"",
|
||||
}
|
||||
17
src/import-png.d.ts
vendored
@@ -13,4 +13,19 @@ declare module "ant-design-vue/es/locale/zh_CN" {
|
||||
export default value;
|
||||
}
|
||||
|
||||
declare var FB: any;
|
||||
declare module 'tim-js-sdk'{
|
||||
const value: any;
|
||||
export default value;
|
||||
}
|
||||
|
||||
declare module 'vue-cropper'{
|
||||
const VueCropper: any
|
||||
export {
|
||||
VueCropper
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
declare var FB: any;
|
||||
|
||||
declare var TCPlayer: any;
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="mine" :style="{height:height + 'px'}">
|
||||
<NavTop :type="1" style="flex-shrink:0"></NavTop>
|
||||
<div class="body">
|
||||
<div class="body" id="rbody">
|
||||
<router-view/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
BIN
src/static/images/arrowdownblueup.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 116 KiB |
BIN
src/static/images/wenhao.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
@@ -1,4 +1,7 @@
|
||||
import { getaddr, userinfo } from '@/api';
|
||||
import { setLanvuage } from '@/api/base';
|
||||
import { getValue, saveValue } from '@/utils/common';
|
||||
import { geti18n } from '@/utils/i18n';
|
||||
import { isProxy } from 'vue';
|
||||
import { createStore } from 'vuex'
|
||||
|
||||
@@ -43,14 +46,37 @@ export default createStore({
|
||||
uid: "",
|
||||
updatedAt: "",
|
||||
video: "",
|
||||
videoid: 0,
|
||||
willsay: [{name: "请选择", level: 0}],
|
||||
willsayValue: [{name: "0", level: 0}],
|
||||
zoneStr: "中途岛GMT-11:00",
|
||||
zoneid: 1,
|
||||
symbol: "$",
|
||||
zoneValue:"",
|
||||
currencytag: 0
|
||||
},
|
||||
islogin: false
|
||||
islogin: false,
|
||||
seting:{
|
||||
lowerLimit: "",
|
||||
minmoney: 0,
|
||||
minwithdraw: "",
|
||||
numberCeiling: "",
|
||||
sxf: "",
|
||||
symbol: "",
|
||||
timeCeiling: "",
|
||||
timeLowerLimit: "",
|
||||
},
|
||||
qh: {
|
||||
code: "86",
|
||||
ename: "China",
|
||||
name: "中国",
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
setseting(state, info){
|
||||
console.log(info, 112)
|
||||
state.seting = info
|
||||
},
|
||||
setUserInfo(state, userinfo){
|
||||
userinfo.money = userinfo.money.toString()
|
||||
state.userinfo = userinfo
|
||||
@@ -62,16 +88,40 @@ export default createStore({
|
||||
setLanguage(state, data){
|
||||
const split = new Date().toString().split(" ");
|
||||
const timeZoneFormatted = split[split.length - 2] + " " + split[split.length - 1];
|
||||
state.userinfo.language = data[1] // English 中文
|
||||
state.userinfo.languageValue = data[0] // 'en' 'zh'
|
||||
state.userinfo.zoneStr = timeZoneFormatted;
|
||||
state.userinfo.currency = data[2];
|
||||
const lan = getValue("Lanvuage");
|
||||
state.qh = data.qh.data;
|
||||
if(lan != null && lan){
|
||||
// state.userinfo.language = data[1] // English 中文
|
||||
// state.userinfo.languageValue = data[0] // 'en' 'zh'
|
||||
state.userinfo.zoneStr = timeZoneFormatted;
|
||||
state.userinfo.currency = data.hb;
|
||||
}else{
|
||||
console.log(data, 111)
|
||||
state.userinfo.language = data.yyx // English 中文
|
||||
state.userinfo.languageValue = data.yy // 'en' 'zh'
|
||||
state.userinfo.zoneStr = timeZoneFormatted;
|
||||
state.userinfo.currency = data.hb;
|
||||
saveValue("Lanvuage", data.yy);
|
||||
if( data.yy != geti18n().$s()){
|
||||
location.reload()
|
||||
}
|
||||
// setLanvuage(data[0]);
|
||||
}
|
||||
|
||||
},
|
||||
setWlan(state){
|
||||
const lan = getValue("Lanvuage");
|
||||
if(lan != null && lan){
|
||||
state.userinfo.languageValue = !getValue("Lanvuage") ? 'zh' : getValue("Lanvuage");
|
||||
state.userinfo.language = (!getValue("Lanvuage") ? 'zh' : getValue("Lanvuage")) == 'zh' ? '中文' : 'English';
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async setUserInfo({ commit }){
|
||||
const user = await userinfo();
|
||||
if(user != '未登录'){
|
||||
saveValue("Lanvuage", user.languageValue);
|
||||
commit('setUserInfo', user);
|
||||
} else {
|
||||
const info = await getaddr();
|
||||
|
||||
2
src/types/index.d.ts
vendored
@@ -58,7 +58,7 @@ export interface UserInfo {
|
||||
video: string;
|
||||
desc: string;
|
||||
money: string;
|
||||
|
||||
languageValue: number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
/**
|
||||
* 图片转Base64
|
||||
*/
|
||||
@@ -52,4 +54,59 @@ export function getValue(key: string): any{
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证图片是否为对应类型
|
||||
* @param name 图片名字
|
||||
*/
|
||||
|
||||
export function provenimg(file: any): boolean | void{
|
||||
const type = ['png', 'jpg'];
|
||||
const ntypearr = file.name.split('.');
|
||||
const ntype = ntypearr[ntypearr.length - 1];
|
||||
console.log(ntype)
|
||||
const size = 2 * 1024 * 1024;
|
||||
if(file.size > size){
|
||||
message.error("最大2MB")
|
||||
return false;
|
||||
}
|
||||
let istype = false
|
||||
for(const i in type){
|
||||
if(type[i] == ntype){
|
||||
istype = true
|
||||
}
|
||||
}
|
||||
console.log(istype)
|
||||
return istype;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证视频是否为对应类型
|
||||
* @param name 图片名字
|
||||
*/
|
||||
|
||||
export function provenvideo(file: any, isvideo?: boolean): boolean{
|
||||
const type = ['flv', 'mp4', 'wmv', 'mov', 'avi'];
|
||||
const ntypearr = file.name.split('.');
|
||||
const ntype = ntypearr[ntypearr.length - 1];
|
||||
if(isvideo){
|
||||
const size = 500 * 1024 * 1024;
|
||||
if(file.size > size){
|
||||
message.error("最大500MB")
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
const size = 100 * 1024 * 1024;
|
||||
if(file.size > size){
|
||||
message.error("最大100MB")
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for(const i in type){
|
||||
if(type[i] == ntype){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -64,8 +64,18 @@ export function getdate(yue?: number): GetDate{
|
||||
}
|
||||
|
||||
|
||||
export function getweek(time: string,zhou?: number){
|
||||
let now = dayjs((!time ? undefined : time))
|
||||
export function getweek(time: string, id: string,zhou?: number){
|
||||
/* eslint-disable */
|
||||
const utc = require('dayjs/plugin/utc') // dependent on utc plugin
|
||||
/* eslint-disable */
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
const days: any = dayjs;
|
||||
console.log(id, 11111)
|
||||
let now = days((!time ? undefined : time)).tz(id)
|
||||
console.log(now, 11111)
|
||||
|
||||
if(zhou != undefined){
|
||||
now = now.day(now.day() + (zhou * 6));
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ const createI18n = (config: Config) => ({
|
||||
});
|
||||
|
||||
const i18nSymbol = Symbol();
|
||||
|
||||
let i18n: any;
|
||||
export function provideI18n(i18nConfig: Config) {
|
||||
const i18n = createI18n(i18nConfig);
|
||||
i18n = createI18n(i18nConfig);
|
||||
provide(i18nSymbol, i18n);
|
||||
return i18n;
|
||||
}
|
||||
@@ -33,5 +33,9 @@ export function useI18n() {
|
||||
const i18n = inject(i18nSymbol);
|
||||
if (!i18n) throw new Error("No i18n provided!!!");
|
||||
|
||||
return i18n;
|
||||
}
|
||||
|
||||
export function geti18n(){
|
||||
return i18n;
|
||||
}
|
||||
@@ -6,7 +6,7 @@ interface OnFunctio {
|
||||
}
|
||||
|
||||
interface UploaderDone {
|
||||
fileId: string;
|
||||
fileId: number;
|
||||
video: {
|
||||
url: string;
|
||||
verify_content: string;
|
||||
|
||||
@@ -124,6 +124,7 @@ import store from '@/store';
|
||||
import router from '@/router';
|
||||
import { useI18n } from '@/utils/i18n';
|
||||
import { editsystemsetting, getlanguages } from '@/api';
|
||||
import { saveValue } from '@/utils/common';
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NavBottom
|
||||
@@ -153,8 +154,13 @@ export default defineComponent({
|
||||
}
|
||||
function setlanguage(e?: any){
|
||||
console.log(e)
|
||||
editsystemsetting({language:e})
|
||||
lan.locale.value = e
|
||||
saveValue("Lanvuage", e)
|
||||
if(store.state.islogin){
|
||||
editsystemsetting({language:e})
|
||||
}else {
|
||||
location.reload();
|
||||
// setLanvuage(e)
|
||||
}
|
||||
}
|
||||
return {
|
||||
languagelist,
|
||||
@@ -195,7 +201,7 @@ export default defineComponent({
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 9999;
|
||||
z-index: 99;
|
||||
.navcontent{
|
||||
width: 910px;
|
||||
margin: 0 auto;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</a-select-option>
|
||||
<a-select-option value="Jiangsu"> Jiangsu </a-select-option>
|
||||
</a-select> -->
|
||||
<a-select :default-value="quhaolist[0].name + '+' + quhaolist[0].code" size="small" option-label-prop="label" @change="getquhao" class="getcode" show-search >
|
||||
<a-select :default-value="mrqh.name + '+' + mrqh.code" size="small" option-label-prop="label" @change="getquhao" class="getcode" show-search >
|
||||
<a-select-option v-for="(i,j) in quhaolist" :key="j" :value="i.name + '+' + i.code" :label="'+' + i.code">
|
||||
{{i.name}}+{{i.code}}
|
||||
</a-select-option>
|
||||
@@ -53,7 +53,7 @@
|
||||
<a-input-group compact>
|
||||
<a-input
|
||||
style="width: 80%"
|
||||
:placeholder="lan.$t('shuruzhanghao')"
|
||||
:placeholder="lan.$t('shuruzhanghaol')"
|
||||
v-model:value="userinfo.phone"
|
||||
/>
|
||||
</a-input-group>
|
||||
@@ -117,13 +117,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, reactive, ref } from "vue";
|
||||
import { computed, defineComponent, onMounted, reactive, ref } from "vue";
|
||||
import LoginTab from "@/components/login/LoginTab.vue";
|
||||
import NavTop from "@/components/NavTop.vue"
|
||||
import { checksmscode, getquhaolist, getwebvideolist, loginpass, sendsms } from '@/api';
|
||||
import { message } from 'ant-design-vue';
|
||||
import router from '@/router';
|
||||
import { useI18n } from '@/utils/i18n';
|
||||
import store from '@/store';
|
||||
|
||||
export default defineComponent({
|
||||
name: "Login",
|
||||
@@ -146,6 +147,7 @@ export default defineComponent({
|
||||
phone: '13152639856',
|
||||
password: '123456'
|
||||
})
|
||||
const mrqh = computed(() => store.state.qh)
|
||||
const quhaolist = ref<any>([
|
||||
{
|
||||
code: "86",
|
||||
@@ -188,7 +190,7 @@ export default defineComponent({
|
||||
}
|
||||
lock = true;
|
||||
console.log(myquhao.value,"quhao")
|
||||
sendsms(myquhao.value + phone.value, 0);
|
||||
sendsms(myquhao.value, phone.value);
|
||||
const timestep = setInterval(() => {
|
||||
console.log(11112);
|
||||
|
||||
@@ -224,12 +226,13 @@ export default defineComponent({
|
||||
|
||||
function sublogin(){
|
||||
console.log(11)
|
||||
// else if(!(/^1[3-9]\d{9}$/.test(phone.value))){
|
||||
// message.error(lan.$t('shoujihaoyouwu'));
|
||||
// return
|
||||
// }
|
||||
if(phone.value==''){
|
||||
message.error(lan.$t('shoujihaoweikong'))
|
||||
return
|
||||
}else if(!(/^1[3-9]\d{9}$/.test(phone.value))){
|
||||
message.error(lan.$t('shoujihaoyouwu'));
|
||||
return
|
||||
} else if(code.value==""){
|
||||
// console.log(phone.value)
|
||||
console.log((/^1[3-9]\d{9}$/.test(phone.value)))
|
||||
@@ -280,7 +283,8 @@ export default defineComponent({
|
||||
tovideoxq,
|
||||
navto,
|
||||
lan,
|
||||
slogin
|
||||
slogin,
|
||||
mrqh
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -138,7 +138,7 @@ export default defineComponent({
|
||||
return;
|
||||
}
|
||||
lock = true;
|
||||
sendsms(uinfo.value.quhao + uinfo.value.phone, 0);
|
||||
sendsms(uinfo.value.quhao, uinfo.value.phone);
|
||||
const timestep = setInterval(() => {
|
||||
console.log(11112);
|
||||
time.value = time.value - 1;
|
||||
|
||||
@@ -23,16 +23,19 @@
|
||||
>
|
||||
<a-input-group compact>
|
||||
<a-select
|
||||
:default-value="quhaolist[0].code"
|
||||
:default-value="mrqh.name + '+' + mrqh.code"
|
||||
size="small"
|
||||
@change="getquhao"
|
||||
class="getcode"
|
||||
style="width: 50%"
|
||||
option-label-prop="label"
|
||||
show-search
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(i, j) in quhaolist"
|
||||
:key="j"
|
||||
:value="i.code"
|
||||
:value="i.name + '+' + i.code"
|
||||
:label="'+' + i.code"
|
||||
>
|
||||
{{ i.name }}+{{ i.code }}
|
||||
</a-select-option>
|
||||
@@ -146,7 +149,7 @@
|
||||
<a-select-option
|
||||
v-for="(item, index) in willsay"
|
||||
:key="index"
|
||||
:value="item.languageid"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.name }}
|
||||
</a-select-option>
|
||||
@@ -218,18 +221,21 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, reactive, ref, toRaw } from "vue";
|
||||
import { computed, defineComponent, onMounted, reactive, ref, toRaw } from "vue";
|
||||
import NavTop from "@/components/NavTop.vue";
|
||||
import {
|
||||
checksmscode,
|
||||
checkuser,
|
||||
getquhaolist,
|
||||
getwillsay,
|
||||
interests,
|
||||
register,
|
||||
sendsms,
|
||||
} from "@/api";
|
||||
import { message } from "ant-design-vue";
|
||||
import router from "@/router";
|
||||
import { useI18n } from "@/utils/i18n";
|
||||
import store from '@/store';
|
||||
|
||||
export default defineComponent({
|
||||
name: "Sign",
|
||||
@@ -255,6 +261,7 @@ export default defineComponent({
|
||||
jiaoshou: "",
|
||||
});
|
||||
const willsay = ref<any>();
|
||||
const mrqh = computed(() => store.state.qh)
|
||||
const quhaolist = ref<any>([
|
||||
{
|
||||
code: "86",
|
||||
@@ -262,7 +269,7 @@ export default defineComponent({
|
||||
name: "中国",
|
||||
},
|
||||
]);
|
||||
getwillsay().then((res) => {
|
||||
interests().then((res) => {
|
||||
willsay.value = res;
|
||||
});
|
||||
onMounted(async () => {
|
||||
@@ -274,8 +281,9 @@ export default defineComponent({
|
||||
* 点击获取验证码 触发60S倒计时
|
||||
*/
|
||||
let lock = false;
|
||||
const getcode: () => void = () => {
|
||||
const getcode = async () => {
|
||||
console.log(11111);
|
||||
|
||||
if (lock) {
|
||||
return;
|
||||
}
|
||||
@@ -283,8 +291,13 @@ export default defineComponent({
|
||||
message.error(lan.$t('shoujihaoweikong'));
|
||||
return;
|
||||
}
|
||||
const iszc = await checkuser({phone: phone.value.phone})
|
||||
if(iszc.code == 0){
|
||||
message.error(lan.$t('zhanghaocunzai'))
|
||||
return ;
|
||||
}
|
||||
lock = true;
|
||||
sendsms(phone.value.quhao + phone.value.phone, 0);
|
||||
sendsms(phone.value.quhao, phone.value.phone);
|
||||
const timestep = setInterval(() => {
|
||||
console.log(phone);
|
||||
time.value = time.value - 1;
|
||||
@@ -367,7 +380,9 @@ export default defineComponent({
|
||||
phone.value.quhao + phone.value.phone,
|
||||
phone.value.code
|
||||
);
|
||||
|
||||
if (res) {
|
||||
|
||||
stepnow.value = e;
|
||||
}
|
||||
|
||||
@@ -392,6 +407,13 @@ export default defineComponent({
|
||||
function navto(url: string) {
|
||||
router.push(url);
|
||||
}
|
||||
|
||||
function getquhao(e?: any){
|
||||
console.log(e)
|
||||
phone.value.quhao = e.toString()
|
||||
phone.value.quhao = phone.value.quhao.split("+")[1];
|
||||
}
|
||||
|
||||
return {
|
||||
formLayout,
|
||||
getcode,
|
||||
@@ -406,6 +428,8 @@ export default defineComponent({
|
||||
navto,
|
||||
lan,
|
||||
quhaolist,
|
||||
getquhao,
|
||||
mrqh
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -482,7 +506,7 @@ export default defineComponent({
|
||||
position: relative;
|
||||
z-index: 999;
|
||||
}
|
||||
.getcode {
|
||||
::v-deep(.getcode) {
|
||||
font-size: 15px;
|
||||
color: #08ae98;
|
||||
width: 313px;
|
||||
@@ -490,6 +514,9 @@ export default defineComponent({
|
||||
line-height: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
::v-deep(.ant-select-selection){
|
||||
background-color: unset;
|
||||
}
|
||||
.orginfo {
|
||||
margin-top: 178px;
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ export default defineComponent({
|
||||
} else {
|
||||
if (toRaw(accountinfo.value).accountid) {
|
||||
console.log(300);
|
||||
editaccount(toRaw(accountinfo.value).accountid);
|
||||
editaccount(toRaw(accountinfo.value).accountid, accountinfo.value);
|
||||
} else {
|
||||
console.log(111);
|
||||
accountadd(toRaw(accountinfo.value));
|
||||
@@ -286,7 +286,7 @@ export default defineComponent({
|
||||
} else {
|
||||
if (toRaw(accountinfo.value).accountid) {
|
||||
console.log(300);
|
||||
editaccount(toRaw(accountinfo.value).accountid);
|
||||
editaccount(toRaw(accountinfo.value).accountid, accountinfo.value);
|
||||
} else {
|
||||
console.log(111);
|
||||
accountadd(toRaw(accountinfo.value));
|
||||
@@ -295,7 +295,7 @@ export default defineComponent({
|
||||
} else if (accountinfofin.type == 1) {
|
||||
if (toRaw(accountinfo.value).accountid) {
|
||||
console.log(300);
|
||||
editaccount(toRaw(accountinfo.value).accountid);
|
||||
editaccount(toRaw(accountinfo.value).accountid, accountinfo.value);
|
||||
} else {
|
||||
console.log(111);
|
||||
accountadd(toRaw(accountinfo.value));
|
||||
@@ -313,7 +313,8 @@ export default defineComponent({
|
||||
message.error(lan.$t("kaihuhangweikong"));
|
||||
} else {
|
||||
if (toRaw(accountinfo.value).accountid) {
|
||||
editaccount(toRaw(accountinfo.value).accountid);
|
||||
editaccount(toRaw(accountinfo.value).accountid, accountinfo.value);
|
||||
|
||||
} else {
|
||||
accountadd(toRaw(accountinfo.value));
|
||||
}
|
||||
@@ -325,7 +326,7 @@ export default defineComponent({
|
||||
} else {
|
||||
if (toRaw(accountinfo.value).accountid) {
|
||||
console.log(300);
|
||||
editaccount(toRaw(accountinfo.value).accountid);
|
||||
editaccount(toRaw(accountinfo.value).accountid, accountinfo.value);
|
||||
} else {
|
||||
console.log(111);
|
||||
accountadd(toRaw(accountinfo.value));
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
class="avatar-uploader"
|
||||
:show-upload-list="false"
|
||||
:customRequest="uploadspic"
|
||||
:beforeUpload="imgs"
|
||||
@change="handleChange"
|
||||
>
|
||||
<a-avatar :size="85" shape="circle" :src="userinfo.img">
|
||||
@@ -37,6 +38,7 @@
|
||||
size="small"
|
||||
v-model:value="userinfo.name"
|
||||
:placeholder="lan.$t('shuruxinnicheng')"
|
||||
@pressEnter="setname"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,7 +49,7 @@
|
||||
<div class="label">{{ lan.$t("laiziguojia") }}</div>
|
||||
<a-select
|
||||
v-model:value="userinfo.country"
|
||||
style="width: 171px"
|
||||
style="width: 1.71rem"
|
||||
size="small"
|
||||
ref="select"
|
||||
show-search
|
||||
@@ -76,7 +78,7 @@
|
||||
<div class="label">{{ lan.$t("jiaoshou") }}</div>
|
||||
<a-select
|
||||
v-model:value="userinfo.tlanguageValue"
|
||||
style="width: 171px"
|
||||
style="width: 1.71rem"
|
||||
size="small"
|
||||
show-search
|
||||
ref="select"
|
||||
@@ -104,7 +106,7 @@
|
||||
>
|
||||
<a-select
|
||||
v-model:value="lang.name"
|
||||
style="width: 171px"
|
||||
style="width: 1.71rem"
|
||||
size="small"
|
||||
ref="select"
|
||||
show-search
|
||||
@@ -119,6 +121,7 @@
|
||||
index) in chiveslist[1]"
|
||||
:key="index"
|
||||
:value="item.name"
|
||||
:disabled="isdisabled(item.name)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</a-select-option>
|
||||
@@ -155,7 +158,7 @@
|
||||
<div class="label">{{ lan.$t("muyu") }}</div>
|
||||
<!-- <a-select
|
||||
v-model:value="userinfo.mtongue"
|
||||
style="width: 171px"
|
||||
style="width: 1.71rem"
|
||||
size="small"
|
||||
ref="select"
|
||||
:getPopupContainer="triggerNode => triggerNode.parentNode"
|
||||
@@ -175,7 +178,7 @@
|
||||
<a-upload
|
||||
list-type="picture"
|
||||
:customRequest="uploads"
|
||||
:beforeUpload="beforeVideoUpload"
|
||||
:beforeUpload="video"
|
||||
>
|
||||
<div class="upload-image">
|
||||
<PlaySquareOutlined
|
||||
@@ -228,7 +231,7 @@
|
||||
</div>
|
||||
<div class="input-box phone-box">
|
||||
<div class="label">{{ lan.$t("shoujihao") }}</div>
|
||||
<div class="phone">{{ userinfo.mobile }}</div>
|
||||
<div class="phone">{{ userinfo.code }}{{ userinfo.mobile }}</div>
|
||||
<div class="update-btn" @click="togglePhoneModal(true)">
|
||||
{{ lan.$t("genghuanshoujihao") }}
|
||||
</div>
|
||||
@@ -252,7 +255,7 @@
|
||||
<div class="label">{{ lan.$t("shiqu") }}</div>
|
||||
<a-select
|
||||
v-model:value="userinfo.zoneStr"
|
||||
style="width: 171px"
|
||||
style="width: 1.71rem"
|
||||
size="small"
|
||||
ref="select"
|
||||
show-search
|
||||
@@ -269,10 +272,11 @@
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
|
||||
<div class="input-box currency-box">
|
||||
<div class="label">{{ lan.$t("huobi") }}</div>
|
||||
<a-select
|
||||
style="width: 171px"
|
||||
<div><a-select
|
||||
style="width: 1.71rem"
|
||||
size="small"
|
||||
ref="select"
|
||||
:getPopupContainer="
|
||||
@@ -280,6 +284,7 @@
|
||||
"
|
||||
@change="currencychange"
|
||||
v-model:value="currencyindex"
|
||||
:disabled="userinfo.currencytag == 1"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(item, index) in currencylist"
|
||||
@@ -289,12 +294,14 @@
|
||||
{{ item.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<div style="font-size:18px;color: red;margin-top:0.05rem">{{lan.$t('huobitishi')}}</div></div>
|
||||
|
||||
</div>
|
||||
<div class="input-box time-zone">
|
||||
<div class="label">{{ lan.$t("yuyan") }}</div>
|
||||
<a-select
|
||||
v-model:value="userinfo.languageValue"
|
||||
style="width: 171px"
|
||||
style="width: 1.71rem"
|
||||
size="small"
|
||||
ref="select"
|
||||
:getPopupContainer="
|
||||
@@ -333,7 +340,7 @@
|
||||
{{ lan.$t("wanchengrenzheng") }}
|
||||
</div>
|
||||
<div class="title sub-title">
|
||||
{{ lan.$t("shuru") }} {{ userinfo.mobile }}
|
||||
{{ lan.$t("shuru") }}{{ userinfo.code }} {{ userinfo.mobile }}
|
||||
{{ lan.$t("shoudaodeyzm") }}
|
||||
</div>
|
||||
<div class="form-box">
|
||||
@@ -348,7 +355,7 @@
|
||||
<div
|
||||
@click="
|
||||
sendVerificationCode(
|
||||
userinfo.code + userinfo.mobile
|
||||
userinfo.code , userinfo.mobile
|
||||
)
|
||||
"
|
||||
class="confirm-btn"
|
||||
@@ -380,14 +387,19 @@
|
||||
<!-- <a-input size="small" v-model:value="bindPhone.number" /> -->
|
||||
<a-input-group compact class="telbox">
|
||||
<a-select
|
||||
:default-value="quhaolist[0].code"
|
||||
:default-value="mrqh.name + '+' + mrqh.code"
|
||||
size="small"
|
||||
@change="getquhao"
|
||||
option-label-prop="label"
|
||||
style="width: 1rem;"
|
||||
show-search
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(i, j) in quhaolist"
|
||||
:key="j"
|
||||
:value="i.code"
|
||||
:value="i.name + '+' + i.code"
|
||||
|
||||
:label="'+' + i.code"
|
||||
>
|
||||
{{ i.name }}+{{ i.code }}
|
||||
</a-select-option>
|
||||
@@ -411,7 +423,7 @@
|
||||
/>
|
||||
<div
|
||||
@click="
|
||||
sendVerificationCode(myquhao + mynewtel)
|
||||
sendVerificationCode(myquhao , mynewtel)
|
||||
"
|
||||
class="confirm-btn"
|
||||
>
|
||||
@@ -485,6 +497,9 @@
|
||||
{{ lan.$t("baocun") }}
|
||||
</div>
|
||||
</div>
|
||||
<a-modal v-model:visible="huobi" :footer="null" :title="lan.$t('tishi')" >
|
||||
<p>{{lan.$t('huobitishi')}}</p>
|
||||
</a-modal>
|
||||
<nav-bottom></nav-bottom>
|
||||
</div>
|
||||
</template>
|
||||
@@ -519,9 +534,12 @@ import {
|
||||
interests,
|
||||
putmember,
|
||||
sendsms,
|
||||
setheadimg,
|
||||
setusername,
|
||||
} from "@/api/index";
|
||||
import { message } from "ant-design-vue";
|
||||
import { useI18n } from "@/utils/i18n";
|
||||
import { provenimg, provenvideo } from '@/utils/common';
|
||||
|
||||
export default defineComponent({
|
||||
name: "Archives",
|
||||
@@ -531,6 +549,7 @@ export default defineComponent({
|
||||
NavBottom,
|
||||
},
|
||||
setup() {
|
||||
let issum = true;
|
||||
const lan: any = useI18n();
|
||||
interface SpeakItem {
|
||||
lang: string;
|
||||
@@ -545,15 +564,14 @@ export default defineComponent({
|
||||
const userinfo = computed(() => {
|
||||
return store.state.userinfo;
|
||||
});
|
||||
const huobi = ref(false);
|
||||
// 表单数据
|
||||
const currencyindex = ref<string>(userinfo.value.currency);
|
||||
const formData = ref(toRaw(userinfo.value));
|
||||
watch(userinfo, () => {
|
||||
formData.value = toRaw(userinfo.value);
|
||||
// console.log(lan.$s(), formData.value.languageValue)
|
||||
if(lan.$s() != formData.value.languageValue){
|
||||
location.reload();
|
||||
}
|
||||
|
||||
console.log(userinfo.value.currencyValue, "listsssss");
|
||||
currencyindex.value = userinfo.value.currency;
|
||||
console.log(currencyindex.value, "listsssss");
|
||||
@@ -564,6 +582,8 @@ export default defineComponent({
|
||||
const chiveslist = ref<any>([[], []]);
|
||||
const languages = ref<unknown>([]);
|
||||
const quhaolist = ref<any>([]);
|
||||
const mrqh = computed(() => store.state.qh)
|
||||
|
||||
const myquhao = ref<string>("");
|
||||
|
||||
const mynewtel = ref<string>("");
|
||||
@@ -678,11 +698,11 @@ export default defineComponent({
|
||||
/**
|
||||
* 发送验证码
|
||||
*/
|
||||
function sendVerificationCode(tel: string): void {
|
||||
function sendVerificationCode(code: string ,tel: string): void {
|
||||
if (remainTime.value === 0) {
|
||||
computedVerificationCode();
|
||||
console.log(tel, "send");
|
||||
sendsms(tel, 0);
|
||||
sendsms(code, tel);
|
||||
}
|
||||
}
|
||||
// 绑定手机号是否是第二步
|
||||
@@ -730,7 +750,7 @@ export default defineComponent({
|
||||
);
|
||||
console.log(res, "xiugai");
|
||||
if (res) {
|
||||
const res1 = await changetel(mynewtel.value);
|
||||
const res1 = await changetel(myquhao.value, mynewtel.value);
|
||||
if (res1) {
|
||||
store.dispatch("setUserInfo");
|
||||
updatePhoneVisible.value = false;
|
||||
@@ -740,6 +760,7 @@ export default defineComponent({
|
||||
function getquhao(e?: any) {
|
||||
console.log(e);
|
||||
myquhao.value = e.toString();
|
||||
myquhao.value = myquhao.value.split("+")[1];
|
||||
}
|
||||
// 是否显示修改密码框
|
||||
const updatePasswordVisible: Ref<boolean> = ref(false);
|
||||
@@ -747,11 +768,14 @@ export default defineComponent({
|
||||
original?: string;
|
||||
password?: string;
|
||||
topassword?: string;
|
||||
memberid?: number;
|
||||
}
|
||||
const passwordForm: PassWord = reactive({
|
||||
original: "",
|
||||
password: "",
|
||||
topassword: "",
|
||||
memberid:0
|
||||
|
||||
});
|
||||
/**
|
||||
* 密码对话框清空数据
|
||||
@@ -775,6 +799,7 @@ export default defineComponent({
|
||||
* @return { void }
|
||||
*/
|
||||
function updateUserPassword(): void {
|
||||
passwordForm.memberid = userinfo.value.memberid
|
||||
console.log(toRaw(passwordForm));
|
||||
if (
|
||||
toRaw(passwordForm).password === toRaw(passwordForm).topassword
|
||||
@@ -798,6 +823,10 @@ export default defineComponent({
|
||||
// for(let i in toRaw(formData.value).willsay){
|
||||
// console.log(toRaw(formData.value).willsay[i])
|
||||
// }
|
||||
if(!issum){
|
||||
message.error(lan.$t("shangchuanwancheng"))
|
||||
return;
|
||||
}
|
||||
const uesrinfo = toRaw(formData.value);
|
||||
// for (let m = 0; m < toRaw(chiveslist.value).length; m++) {
|
||||
// for (const i in uesrinfo.willsayValue) {
|
||||
@@ -885,7 +914,10 @@ export default defineComponent({
|
||||
// console.log(zonelist.value[i].city + zonelist.value[i].gmt, uesrinfo.zoneStr)
|
||||
}
|
||||
if (reg.test(userinfo.value.email)) {
|
||||
putmember(uesrinfo);
|
||||
await putmember(uesrinfo);
|
||||
if(lan.$s() != uesrinfo.languageValue){
|
||||
location.reload();
|
||||
}
|
||||
} else {
|
||||
message.error(lan.$t('youxiangcuowu'));
|
||||
}
|
||||
@@ -907,13 +939,17 @@ export default defineComponent({
|
||||
}
|
||||
const uploadprogress: Ref<number> = ref(0);
|
||||
async function uploads(file: AntUpload) {
|
||||
uploadprogress.value = 0;
|
||||
|
||||
uploadprogress.value = 1;
|
||||
issum = false;
|
||||
const res = await uploadflie(file.file, (info: any) => {
|
||||
console.log(info);
|
||||
uploadprogress.value = info.percent.toFixed(2) * 100;
|
||||
const jindu = info.percent.toFixed(2) * 100
|
||||
uploadprogress.value = jindu > 0 ? jindu : 1;
|
||||
});
|
||||
userinfo.value.video = res.video.url;
|
||||
userinfo.value.videoid = res.fileId;
|
||||
issum = true;
|
||||
}
|
||||
|
||||
if (formData.value.video != "") {
|
||||
@@ -928,11 +964,23 @@ export default defineComponent({
|
||||
|
||||
function choosewillsay(e?: any) {
|
||||
console.log(formData.value.willsayValue);
|
||||
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
function isdisabled(name: string){
|
||||
for(const i in formData.value.willsay){
|
||||
if(formData.value.willsay[i].name == name){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function currencychange(e?: any) {
|
||||
console.log(e);
|
||||
huobi.value = true;
|
||||
userinfo.value.currencyValue = e;
|
||||
// editsystemsetting({currency:e})
|
||||
}
|
||||
@@ -946,7 +994,29 @@ export default defineComponent({
|
||||
// picinfo.fileId=res.fileId
|
||||
// picinfo.url=res.video.url
|
||||
formData.value.img = res.video.url;
|
||||
setheadimg(res.video.url);
|
||||
}
|
||||
|
||||
// function beforeVideoUpload(file: any){
|
||||
// console.log(file)
|
||||
// const type = provenvideo(file.name);
|
||||
// // if(!type){
|
||||
// // message.error("文件类型错误,请重新选择")
|
||||
// // }
|
||||
// return type;
|
||||
// }
|
||||
function video(file: any){
|
||||
return provenvideo(file)
|
||||
}
|
||||
function imgs(file: any){
|
||||
return provenimg(file)
|
||||
}
|
||||
function setname(){
|
||||
setusername(formData.value.name).then(()=>{
|
||||
showname.value = true;
|
||||
})
|
||||
}
|
||||
|
||||
// function selguojia(e: any){
|
||||
// userinfo.value.countryValue = e;
|
||||
// console.log(e)
|
||||
@@ -993,7 +1063,13 @@ export default defineComponent({
|
||||
showname,
|
||||
uploadspic,
|
||||
lan,
|
||||
interestslist
|
||||
interestslist,
|
||||
isdisabled,
|
||||
setname,
|
||||
video,
|
||||
imgs,
|
||||
mrqh,
|
||||
huobi
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -1010,6 +1086,7 @@ export default defineComponent({
|
||||
.telbox {
|
||||
margin-left: 15px;
|
||||
font-size: 12px;
|
||||
// width: 200px;
|
||||
}
|
||||
.update-btn {
|
||||
font-size: 11px;
|
||||
@@ -1054,7 +1131,7 @@ export default defineComponent({
|
||||
align-items: center;
|
||||
margin-bottom: 28px;
|
||||
.label {
|
||||
width: 60px;
|
||||
width: 90px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: #808080;
|
||||
@@ -1063,7 +1140,7 @@ export default defineComponent({
|
||||
align-self: flex-start;
|
||||
}
|
||||
.ant-input {
|
||||
width: 171px;
|
||||
width: 1.71rem;
|
||||
padding: 6px 11px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #dcdfe0;
|
||||
@@ -1119,7 +1196,7 @@ export default defineComponent({
|
||||
}
|
||||
.video-lang {
|
||||
.upload-image {
|
||||
width: 171px;
|
||||
width: 1.71rem;
|
||||
height: 96px;
|
||||
border: 1px solid #dcdfe0;
|
||||
border-radius: 3px;
|
||||
@@ -1283,6 +1360,9 @@ export default defineComponent({
|
||||
line-height: 23px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
position: fixed;
|
||||
bottom: 100px;
|
||||
right: 300px;
|
||||
}
|
||||
// .submit-btn:hover {
|
||||
// background: #08ae98;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<div class="mingxilist">
|
||||
<div class="tabs">
|
||||
<div class="beforetab">{{lan.$t('tixianzhanghu')}}</div>
|
||||
<span class="residue">{{lan.$t('yue')}}:{{ yue }}</span>
|
||||
<div class="topbtn topbtn2" @click="navto('/mine/addaccount')">{{lan.$t('tixianzhanghu')}}</div>
|
||||
<span class="residue">{{lan.$t('yue')}}:{{ danwei + yue }}</span>
|
||||
<div class="topbtn topbtn2" @click="navto('/mine/addaccount')">{{lan.$t('tianjiatixian')}}</div>
|
||||
<div class="topbtn topbtn1" @click="navto('/mine/transaction')">{{lan.$t('tixianjilu')}}</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="zhanghao">
|
||||
{{lan.$t('zhanghao')}}:6217 **** **** **** 175
|
||||
{{lan.$t('zhanghao')}}:{{i.account}}
|
||||
</div>
|
||||
</div>
|
||||
</a-radio>
|
||||
@@ -103,7 +103,7 @@
|
||||
<div class="label">{{lan.$t('tixianjine')}}</div>
|
||||
<div class="moneynum">
|
||||
<a-input v-model:value="payinfo.money" class="shuru" />
|
||||
<div>¥</div>
|
||||
<div>{{danwei}}</div>
|
||||
<div class="cashoutall" @click="all">{{lan.$t('quanbujine')}}</div>
|
||||
<div class="desc">
|
||||
{{lan.$t('tixianzhu')}}
|
||||
@@ -116,7 +116,7 @@
|
||||
payinfo.money < 100 || payinfo.money > parseFloat(yue)
|
||||
"
|
||||
>
|
||||
*{{lan.$t('yueshu')}} {{ parseFloat(yue) }},{{lan.$t('zuiditixian')}}
|
||||
*{{lan.$t('yueshu')}} {{danwei + parseFloat(yue) }},{{lan.$t('zuiditixian')}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="cashoutall submit" @click="sub">{{lan.$t('lijitixian')}}</div>
|
||||
@@ -126,7 +126,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref, toRaw } from "vue";
|
||||
import { computed, defineComponent, onMounted, ref, toRaw } from "vue";
|
||||
import NavBottom from "@/components/NavBottom.vue";
|
||||
import { cashout, getwallect } from "@/api";
|
||||
import store from "@/store";
|
||||
@@ -153,7 +153,8 @@ export default defineComponent({
|
||||
const moneychange: (e: number) => void = (e: number) => {
|
||||
console.log(e);
|
||||
};
|
||||
const yue=ref<number>(store.state.userinfo.moneyValue)
|
||||
const yue=computed( () => store.state.userinfo.moneyValue)
|
||||
const danwei = computed(() => store.state.userinfo.symbol)
|
||||
// const yue = ref<number>(10000);
|
||||
// yue.value=store.state.userinfo.money
|
||||
const accountlist = ref<Array<any>>([]);
|
||||
@@ -179,7 +180,7 @@ export default defineComponent({
|
||||
if (accountlist.value.length == 0) {
|
||||
message.error(lan.$t('kongzhanghaoliebiao'));
|
||||
return;
|
||||
} else if (payinfo.value.money < 100) {
|
||||
} else if (payinfo.value.money < store.state.seting.minwithdraw) {
|
||||
message.error(lan.$t('zuiditixian'));
|
||||
return;
|
||||
} else if (payinfo.value.money > yue.value) {
|
||||
@@ -212,7 +213,8 @@ export default defineComponent({
|
||||
accountlist,
|
||||
store,
|
||||
lan,
|
||||
navto
|
||||
navto,
|
||||
danwei
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -37,11 +37,11 @@
|
||||
</div>
|
||||
<div class="data">
|
||||
<div class="label">{{lan.$t('zongguankanshu')}}</div>
|
||||
<div class="right">{{statistics.videoInfo?statistics.videoInfo.sum:0}}</div>
|
||||
<div class="right">{{statistics.videoInfo?statistics.videoInfo.watch:0}}</div>
|
||||
</div>
|
||||
<div class="data">
|
||||
<div class="label">{{lan.$t('pingjundefen')}}</div>
|
||||
<div class="right">{{statistics.videoInfo?statistics.videoInfo.sum:0}}</div>
|
||||
<div class="right">{{statistics.videoInfo?statistics.videoInfo.avg:0}}</div>
|
||||
</div>
|
||||
<img src="@/static/images/shipintj.png" alt="" class="zhuzi" />
|
||||
</div>
|
||||
|
||||
@@ -6,27 +6,27 @@
|
||||
<div class="hits">视频点击量</div>
|
||||
</div>
|
||||
<div class="list-body">
|
||||
<div class="rank-item" v-for="item in rankData" :key="item.uid" :class="{'mine-item': item.uid === 5 }">
|
||||
<div class="other-rank" :class="{'mine-rank': item.uid === 5 }">
|
||||
<div class="rank-item" v-for="(item,index) in newList" :key="index" :class="{'mine-item': item.isme }">
|
||||
<div class="other-rank" :class="{'mine-rank': item.isme }">
|
||||
<div class="ranking-number">
|
||||
<span v-if="item.uid === 5" class="mine">我的成绩</span>
|
||||
<span v-if="item.isme" class="mine">我的成绩</span>
|
||||
<div v-else>
|
||||
<img src="@/static/images/rank_first.png" class="rank-img" v-if="item.id === 1" />
|
||||
<img src="@/static/images/rank_second.png" class="rank-img" v-else-if="item.id === 2" />
|
||||
<img src="@/static/images/rank_third.png" class="rank-img" v-else-if="item.id === 3" />
|
||||
<span class="other" v-else>{{ item.id }}</span>
|
||||
<img src="@/static/images/rank_first.png" class="rank-img" v-if="index === 0" />
|
||||
<img src="@/static/images/rank_second.png" class="rank-img" v-else-if="index === 1" />
|
||||
<img src="@/static/images/rank_third.png" class="rank-img" v-else-if="index === 2" />
|
||||
<span class="other" v-else>{{ item.rank }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<a-avatar :size="34">
|
||||
<a-avatar :size="34" :src="item.img">
|
||||
<template v-slot:icon><UserOutlined /></template>
|
||||
</a-avatar>
|
||||
<span class="name">{{ item.name }}</span>
|
||||
</div>
|
||||
<div class="hits">{{ item.value }}</div>
|
||||
<div class="hits">{{ item.clicks }}</div>
|
||||
</div>
|
||||
<div class="third-ellipsis" v-if="item.id === 3">...</div>
|
||||
<div class="twenty-ellipsis" v-if="item.id === 20">
|
||||
<div class="third-ellipsis" v-if="item.rank == 3">...</div>
|
||||
<div class="twenty-ellipsis" v-if="item.rank === deadLine">
|
||||
<div class="third-ellipsis">
|
||||
<div>...</div>
|
||||
</div>
|
||||
@@ -37,7 +37,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive } from 'vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { UserOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
export default defineComponent({
|
||||
@@ -45,59 +45,28 @@ export default defineComponent({
|
||||
components: {
|
||||
UserOutlined
|
||||
},
|
||||
setup() {
|
||||
interface RankItem {
|
||||
uid: number;
|
||||
id: number;
|
||||
avatar: string;
|
||||
name: string;
|
||||
value: number | string;
|
||||
props: {
|
||||
list: {
|
||||
type: Array
|
||||
}
|
||||
const rankData: Array<RankItem> = reactive([{
|
||||
uid: 1,
|
||||
id: 1,
|
||||
avatar: '',
|
||||
name: '1',
|
||||
value: '123456',
|
||||
}, {
|
||||
uid: 2,
|
||||
id: 2,
|
||||
avatar: '',
|
||||
name: '12',
|
||||
value: '12345',
|
||||
}, {
|
||||
uid: 3,
|
||||
id: 3,
|
||||
avatar: '',
|
||||
name: '123',
|
||||
value: '1234',
|
||||
}, {
|
||||
uid: 4,
|
||||
id: 20,
|
||||
avatar: '',
|
||||
name: '1234',
|
||||
value: '123',
|
||||
}, {
|
||||
uid: 15,
|
||||
id: 21,
|
||||
avatar: '',
|
||||
name: '15',
|
||||
value: '12',
|
||||
}, {
|
||||
uid: 5,
|
||||
id: 22,
|
||||
avatar: '',
|
||||
name: '61',
|
||||
value: '1',
|
||||
}, {
|
||||
uid: 51,
|
||||
id: 23,
|
||||
avatar: '',
|
||||
name: '61',
|
||||
value: '1',
|
||||
}]);
|
||||
},
|
||||
setup(props) {
|
||||
const deadLine = ref(4); // 写死的合格线
|
||||
const list = ref(props.list);
|
||||
let mineRank = 0; // 自己的排名
|
||||
list.value!.forEach((element: any) => {
|
||||
if(element.isme) {
|
||||
mineRank = element.rank;
|
||||
}
|
||||
});
|
||||
// 过滤数据
|
||||
const newList = list.value!.filter((item: any) => {
|
||||
return item.rank <= 3 || item.rank == deadLine.value || item.rank === mineRank || item.rank === mineRank - 1 || item.rank === mineRank + 1;
|
||||
})
|
||||
|
||||
return {
|
||||
rankData
|
||||
newList,
|
||||
deadLine,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<a-upload
|
||||
list-type="picture"
|
||||
:customRequest="uploadspic"
|
||||
:before-upload="beforeUploadpic"
|
||||
:beforeUpload="imgs"
|
||||
>
|
||||
<div
|
||||
class="upload-image"
|
||||
@@ -59,7 +59,7 @@
|
||||
<a-upload
|
||||
list-type="picture"
|
||||
:customRequest="uploads"
|
||||
:before-upload="beforeUpload"
|
||||
:beforeUpload="video"
|
||||
>
|
||||
<div class="upload-image" v-if="form.fileurl.length == 0">
|
||||
<PlaySquareOutlined
|
||||
@@ -95,8 +95,8 @@
|
||||
<p>
|
||||
{{ lan.$t("shipinyaoqiu") }}
|
||||
</p>
|
||||
<p>1.{{ lan.$t("shipinyaoqiu1") }}</p>
|
||||
<p>2.{{ lan.$t("shipinyaoqiu2") }}</p>
|
||||
<p>1.{{ lan.$t("cshipinyaoqiu1") }}</p>
|
||||
<p>2.{{ lan.$t("cshipinyaoqiu2") }}</p>
|
||||
<p>3.{{ lan.$t("shipinyaoqiu3") }}</p>
|
||||
</div>
|
||||
<!-- <div class="demand">
|
||||
@@ -131,7 +131,7 @@
|
||||
import { defineComponent, onMounted, reactive, Ref, ref, toRaw } from "vue";
|
||||
import { PlaySquareOutlined, PlusOutlined } from "@ant-design/icons-vue";
|
||||
import NavBottom from "@/components/NavBottom.vue";
|
||||
import { previewCover } from "@/utils/common";
|
||||
import { previewCover, provenimg, provenvideo } from "@/utils/common";
|
||||
import { FromSend, ImgInfo, VideoInfo } from "@/types";
|
||||
import { uploadflie } from "@/utils/vod";
|
||||
import { setvideo, videoadd, videodetail } from "@/api";
|
||||
@@ -147,6 +147,7 @@ export default defineComponent({
|
||||
NavBottom,
|
||||
},
|
||||
setup() {
|
||||
let issum= true;
|
||||
const lan: any = useI18n();
|
||||
interface FileItem {
|
||||
video: Array<string>;
|
||||
@@ -191,6 +192,10 @@ export default defineComponent({
|
||||
*/
|
||||
const beforeCoverUpload = (file: File): boolean => {
|
||||
console.log(file);
|
||||
const type = provenimg(file.name);
|
||||
if(!type){
|
||||
message.error("请重新选择")
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -223,16 +228,14 @@ export default defineComponent({
|
||||
file: File;
|
||||
}
|
||||
async function uploadspic(file: AntUpload) {
|
||||
if (ifalowupload.value) {
|
||||
|
||||
const res = await uploadflie(file.file, (info: any) => {
|
||||
console.log(info);
|
||||
uploadpicprogress.value = info.percent.toFixed(2) * 100;
|
||||
});
|
||||
console.log(res);
|
||||
form.value.img = res.video.url;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function beforeUploadpic(info?: any) {
|
||||
@@ -252,22 +255,24 @@ export default defineComponent({
|
||||
const uploadprogress: Ref<number> = ref(0);
|
||||
const ifallowvideo = ref<boolean>(false);
|
||||
async function uploads(file: AntUpload) {
|
||||
if (ifallowvideo.value) {
|
||||
issum = false;
|
||||
console.log(file);
|
||||
videofile.value = file.file;
|
||||
uploadprogress.value = 1;
|
||||
videos.value[0].addEventListener("durationchange", () => {
|
||||
console.log(videos.value[0].duration);
|
||||
form.value.fileduration = videos.value[0].duration;
|
||||
});
|
||||
const res = await uploadflie(file.file, (info: any) => {
|
||||
console.log(info);
|
||||
uploadprogress.value = info.percent.toFixed(2) * 100;
|
||||
const jindu = info.percent.toFixed(2) * 100
|
||||
uploadprogress.value = jindu > 0 ? jindu : 1;
|
||||
});
|
||||
console.log(res);
|
||||
|
||||
form.value.fileid = res.fileId;
|
||||
form.value.fileurl = res.video.url;
|
||||
}
|
||||
issum = true;
|
||||
}
|
||||
|
||||
function beforeVideoUpload(info?: any) {
|
||||
@@ -285,6 +290,10 @@ export default defineComponent({
|
||||
*/
|
||||
const routes = useRoute();
|
||||
const onSubmit = async (e: FromSend) => {
|
||||
if(!issum){
|
||||
message.error(lan.$t("shangchuanwancheng"))
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
console.log(toRaw(form.value), 111);
|
||||
console.log(toRaw(form.value).video[0].length);
|
||||
@@ -322,6 +331,13 @@ export default defineComponent({
|
||||
ifallowvideo.value = true;
|
||||
}
|
||||
}
|
||||
function video(file: any){
|
||||
return provenvideo(file, true)
|
||||
}
|
||||
function imgs(file: any){
|
||||
return provenimg(file)
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
labelCol: { span: 4 },
|
||||
@@ -345,7 +361,9 @@ export default defineComponent({
|
||||
ifalowupload,
|
||||
beforeVideoUpload,
|
||||
lan,
|
||||
beforeUploadpic
|
||||
beforeUploadpic,
|
||||
video,
|
||||
imgs
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
size="small"
|
||||
v-model:value="form.title"
|
||||
:placeholder="lan.$t('shuruzhibobiaoti')"
|
||||
@click="isEntitled = jinzhi"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="lan.$t('zhibofengmian')" class="item-cover" :rules="{ required: true, message: 'Please input Activity name', trigger: 'blur'}">
|
||||
@@ -14,7 +15,7 @@
|
||||
<a-upload
|
||||
list-type="picture"
|
||||
:customRequest="uploadspic"
|
||||
:before-upload="beforeUploadpic"
|
||||
:before-upload="imgs"
|
||||
>
|
||||
<div
|
||||
class="upload-image"
|
||||
@@ -61,7 +62,7 @@
|
||||
<a-upload
|
||||
list-type="picture"
|
||||
:customRequest="uploads"
|
||||
:before-upload="beforeUpload"
|
||||
:before-upload="video"
|
||||
>
|
||||
<div class="upload-image" v-if="form.fileurl.length == 0">
|
||||
<PlaySquareOutlined
|
||||
@@ -94,7 +95,7 @@
|
||||
</div>
|
||||
</a-upload>
|
||||
<div class="demand">
|
||||
<p class="one-line-hide">
|
||||
<p>
|
||||
{{ lan.$t("shipinyaoqiu") }}
|
||||
</p>
|
||||
<p>1.{{ lan.$t("shipinyaoqiu1") }}</p>
|
||||
@@ -110,7 +111,7 @@
|
||||
</p>
|
||||
</div> -->
|
||||
</a-form-item>
|
||||
<a-form-item :label="lan.$t('kaishishijian')" :rules="{ required: true, message: 'Please input Activity name', trigger: 'blur'}">
|
||||
<a-form-item :label="lan.$t('kaishishijian')" @click="isEntitled = jinzhi" :rules="{ required: true, message: 'Please input Activity name', trigger: 'blur'}">
|
||||
|
||||
<!-- <a-input
|
||||
size="small"
|
||||
@@ -123,6 +124,7 @@
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
:value="form.dateline"
|
||||
:disabled-date="disabledDate"
|
||||
@click="isEntitled = jinzhi"
|
||||
@change="startchange"
|
||||
:placeholder="lan.$t('shezhikaishishijian')"
|
||||
:getCalendarContainer="
|
||||
@@ -139,11 +141,12 @@
|
||||
<a-input
|
||||
size="small"
|
||||
v-model:value="form.livetime"
|
||||
@click="isEntitled = jinzhi"
|
||||
:placeholder="lan.$t('shuruzhiboshijian')"
|
||||
type="number"
|
||||
/>
|
||||
<span class="unit">{{ lan.$t("fenzhong") }}</span>
|
||||
<div style="color: red;font-size: 0.12rem;line-height: 1.3;">*最短30min, 最长120min</div>
|
||||
<div style="color: red;font-size: 0.12rem;line-height: 1.3;">*{{ lan.$t('shichangtishi') }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:label="lan.$t('zhiborenshu')"
|
||||
@@ -153,15 +156,17 @@
|
||||
<a-input
|
||||
size="small"
|
||||
v-model:value="form.livenumber"
|
||||
@click="isEntitled = jinzhi"
|
||||
:placeholder="lan.$t('shuruzhiborenshu')"
|
||||
type="number"
|
||||
/>
|
||||
<div style="color: red;font-size: 0.12rem;line-height: 1.3;">*最少1人, 最多4人</div>
|
||||
<div style="color: red;font-size: 0.12rem;line-height: 1.3;">*{{ lan.$t('renshutishi') }}</div>
|
||||
|
||||
</a-form-item>
|
||||
<a-form-item :label="lan.$t('zhibojianjie')" class="brief">
|
||||
<a-textarea
|
||||
v-model:value="form.desc"
|
||||
@click="isEntitled = jinzhi"
|
||||
:autoSize="true"
|
||||
class="brief-textarea"
|
||||
:maxlength="200"
|
||||
@@ -171,7 +176,9 @@
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{ span: 4, offset: 0 }">
|
||||
<a-button @click="onSubmit">{{ lan.$t("fabuzhibo") }}</a-button>
|
||||
<a-button style="margin-left:0.15rem;background-color: red;" @click="isquxiao = true" v-if="isbianji">{{ lan.$t("quxiaozhibo") }}</a-button>
|
||||
</a-form-item>
|
||||
|
||||
</a-form>
|
||||
<div class="modal-container">
|
||||
<a-modal
|
||||
@@ -206,11 +213,20 @@
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
<a-modal
|
||||
:title="lan.$t('quxiaozhibo')"
|
||||
v-model:visible="isquxiao"
|
||||
:confirm-loading="confirmLoading"
|
||||
@ok="onquxiao()"
|
||||
>
|
||||
<p>{{ lan.$t("querenquxiao") }}</p>
|
||||
</a-modal>
|
||||
<nav-bottom></nav-bottom>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
onBeforeUpdate,
|
||||
onMounted,
|
||||
@@ -223,15 +239,16 @@ import { PlaySquareOutlined, PlusOutlined } from "@ant-design/icons-vue";
|
||||
import { useForm } from "@ant-design-vue/use";
|
||||
import NavBottom from "@/components/NavBottom.vue";
|
||||
import RankList from "./RankList.vue";
|
||||
import { previewCover } from "@/utils/common";
|
||||
import { previewCover, provenimg, provenvideo } from "@/utils/common";
|
||||
import { FromSend, ImgInfo } from "@/types/index";
|
||||
import { uploadflie } from "@/utils/vod";
|
||||
import { getlivest, liveadd, liveinfo, setlive } from "@/api";
|
||||
import { cancellive, getlivest, liveadd, liveinfo, setlive } from "@/api";
|
||||
import { useRoute } from "vue-router";
|
||||
import dayjs from "dayjs";
|
||||
import { message } from "ant-design-vue";
|
||||
import router from "@/router";
|
||||
import { useI18n } from "@/utils/i18n";
|
||||
import store from '@/store';
|
||||
|
||||
export default defineComponent({
|
||||
name: "ReleaseWebcast",
|
||||
@@ -242,12 +259,13 @@ export default defineComponent({
|
||||
RankList,
|
||||
},
|
||||
setup() {
|
||||
let issum = true;
|
||||
const lan: any = useI18n();
|
||||
// 表单数据
|
||||
const form = ref({
|
||||
title: "",
|
||||
img: "",
|
||||
fileid: "",
|
||||
fileid: 0,
|
||||
fileurl: "",
|
||||
fileduration: 0,
|
||||
dateline: "",
|
||||
@@ -255,12 +273,15 @@ export default defineComponent({
|
||||
livenumber: "",
|
||||
desc: "",
|
||||
});
|
||||
|
||||
const isquxiao = ref(false)
|
||||
const uploadprogress: Ref<number> = ref(0);
|
||||
const uploadpicprogress: Ref<number> = ref(0);
|
||||
const videofile = ref<File>();
|
||||
const videos = ref<Array<any>>([]);
|
||||
const lives = ref<any>({});
|
||||
const jinzhi = ref(false)
|
||||
const isEntitled: Ref<boolean> = ref(false);
|
||||
|
||||
/**
|
||||
* 验证直播时间
|
||||
*/
|
||||
@@ -344,15 +365,27 @@ export default defineComponent({
|
||||
* todo 需要后台返回年份
|
||||
*/
|
||||
const id = useRoute().query.id;
|
||||
const isbianji = ref(id);
|
||||
if (id != null && typeof id == "string") {
|
||||
liveinfo(parseInt(id)).then((res) => {
|
||||
form.value = res;
|
||||
});
|
||||
}
|
||||
const seting = computed(() => store.state.seting)
|
||||
const onSubmit = (e: FromSend) => {
|
||||
if(jinzhi.value){
|
||||
isEntitled.value = true;
|
||||
|
||||
}
|
||||
if(!issum){
|
||||
message.error(lan.$t("shangchuanwancheng"))
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
validate()
|
||||
.then(() => {
|
||||
console.log(seting.value)
|
||||
|
||||
console.log(toRaw(form), 111);
|
||||
const subdata: any = toRaw(form.value);
|
||||
if (subdata.title == "") {
|
||||
@@ -374,12 +407,13 @@ export default defineComponent({
|
||||
message.error(lan.$t('zhiborenshuweikong'));
|
||||
return;
|
||||
} else {
|
||||
if(subdata.livetime < 30 || subdata.livetime > 120){
|
||||
message.error("直播时长最短30min, 最长120min");
|
||||
if(subdata.livetime < parseInt(seting.value.timeLowerLimit) || subdata.livetime > parseInt(seting.value.timeCeiling)){
|
||||
// console.log(subdata.livetime, subdata.livetime < seting.value.timeLowerLimit || subdata.livetime > seting.value.timeCeiling)
|
||||
message.error(lan.$t('shichangtishi'));
|
||||
return ;
|
||||
}
|
||||
if(subdata.livenumber > 4 || subdata.livenumber < 1){
|
||||
message.error("直播人数最少1人, 最多4人");
|
||||
if(subdata.livenumber > parseInt(seting.value.numberCeiling) || subdata.livenumber < parseInt(seting.value.lowerLimit)){
|
||||
message.error(lan.$t('renshutishi'));
|
||||
return ;
|
||||
}
|
||||
if (!lives.value.status) {
|
||||
@@ -404,7 +438,6 @@ export default defineComponent({
|
||||
console.log("error", err);
|
||||
});
|
||||
};
|
||||
const isEntitled: Ref<boolean> = ref(false);
|
||||
/**
|
||||
* 隐藏无资格提示
|
||||
*/
|
||||
@@ -464,26 +497,27 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
async function uploads(file: AntUpload) {
|
||||
if (ifallowupload.value) {
|
||||
issum = false;
|
||||
console.log(file);
|
||||
videofile.value = file.file;
|
||||
videos.value[0].addEventListener("durationchange", () => {
|
||||
console.log(videos.value[0].duration);
|
||||
form.value.fileduration = videos.value[0].duration;
|
||||
});
|
||||
uploadprogress.value = 1;
|
||||
const res = await uploadflie(file.file, (info: any) => {
|
||||
console.log(info);
|
||||
uploadprogress.value = info.percent.toFixed(2) * 100;
|
||||
const jindu = info.percent.toFixed(2) * 100
|
||||
uploadprogress.value = jindu > 0 ? jindu : 1;
|
||||
});
|
||||
console.log(res);
|
||||
|
||||
form.value.fileid = res.fileId;
|
||||
form.value.fileurl = res.video.url;
|
||||
}
|
||||
issum = true;
|
||||
}
|
||||
const ifallowpic = ref<boolean>(false);
|
||||
async function uploadspic(file: AntUpload) {
|
||||
if (ifallowpic.value) {
|
||||
const res = await uploadflie(file.file, (info: any) => {
|
||||
console.log(info);
|
||||
uploadpicprogress.value = info.percent.toFixed(2) * 100;
|
||||
@@ -493,13 +527,12 @@ export default defineComponent({
|
||||
// picinfo.fileId=res.fileId
|
||||
// picinfo.url=res.video.url
|
||||
form.value.img = res.video.url;
|
||||
}
|
||||
}
|
||||
|
||||
getlivest().then((res) => {
|
||||
if (res) {
|
||||
isEntitled.value = true;
|
||||
lives.value = res;
|
||||
jinzhi.value = true;
|
||||
}
|
||||
});
|
||||
function beforeUploadpic(info?: any) {
|
||||
@@ -519,6 +552,31 @@ export default defineComponent({
|
||||
return current && current < now;
|
||||
}
|
||||
|
||||
function onquxiao(){
|
||||
isquxiao.value = false;
|
||||
if (id != null && typeof id == "string") {
|
||||
cancellive(parseInt(id), 3).then((res)=>{
|
||||
if(res){
|
||||
router.push("/regime/live")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
function video(file: any){
|
||||
if(jinzhi.value){
|
||||
isEntitled.value = true;
|
||||
return false;
|
||||
}
|
||||
return provenvideo(file)
|
||||
}
|
||||
function imgs(file: any){
|
||||
if(jinzhi.value){
|
||||
isEntitled.value = true;
|
||||
return false;
|
||||
}
|
||||
return provenimg(file)
|
||||
}
|
||||
|
||||
return {
|
||||
labelCol: { span: 4 },
|
||||
wrapperCol: { span: 14 },
|
||||
@@ -549,7 +607,13 @@ export default defineComponent({
|
||||
ifallowupload,
|
||||
fankui,
|
||||
lan,
|
||||
disabledDate
|
||||
disabledDate,
|
||||
isbianji,
|
||||
onquxiao,
|
||||
isquxiao,
|
||||
video,
|
||||
imgs,
|
||||
jinzhi
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -647,7 +711,7 @@ export default defineComponent({
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
color: #808080;
|
||||
width: 134px;
|
||||
// width: 134px;
|
||||
> p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -20,12 +20,11 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(i,j) in withdrawallist.data" :key="j">
|
||||
<td>{{i.typename}} {{i.account}}</td>
|
||||
<td>{{i.typename}} {{i.account}}</td>
|
||||
<td>{{i.created_at}}</td>
|
||||
<td class="moneyadd">{{i.statusname}}</td>
|
||||
<td>
|
||||
<span v-if="international==1">$</span>
|
||||
<span v-else>¥</span>
|
||||
|
||||
{{i.money}}
|
||||
</td>
|
||||
<td @click="navto(3,i.withdrawalid)">{{lan.$t('chakanxiangqing')}}</td>
|
||||
@@ -34,8 +33,11 @@
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<template v-if="!withdrawallist.total">
|
||||
<a-empty />
|
||||
</template>
|
||||
<div class="pages">
|
||||
<a-pagination v-model:current="page" :total="withdrawallist.total" :showLessItems="true" @change="pagechange"/>
|
||||
<a-pagination v-if="withdrawallist.total" v-model:current="page" :total="withdrawallist.total" :showLessItems="true" @change="pagechange"/>
|
||||
</div>
|
||||
</div>
|
||||
<NavBottom class="navbottom"></NavBottom>
|
||||
|
||||
@@ -29,9 +29,17 @@
|
||||
<div class="right">{{accountinfo.created_at}}</div>
|
||||
</div>
|
||||
|
||||
<div class="infoitem">
|
||||
<div class="infoitem" v-if="accountinfo.type != 2">
|
||||
<div class="left">{{lan.$t('laiyuan')}}</div>
|
||||
<div class="right">{{accountinfo.typename}}</div>
|
||||
<div class="right" style="cursor:pointer" @click="toinfo(accountinfo.source)">{{accountinfo.sourcetitle}}</div>
|
||||
</div>
|
||||
<div class="infoitem" v-if="accountinfo.type != 2">
|
||||
<div class="left">{{lan.$t('shijishichang')}}</div>
|
||||
<div class="right">{{accountinfo.livetime}}</div>
|
||||
</div>
|
||||
<div class="infoitem" v-if="accountinfo.type != 2">
|
||||
<div class="left">{{lan.$t('shijirenshu')}}</div>
|
||||
<div class="right">{{accountinfo.count}}</div>
|
||||
</div>
|
||||
<div class="back" @click="navto(1,2)">{{lan.$t('fanhui')}}</div>
|
||||
|
||||
@@ -58,7 +66,7 @@
|
||||
</div>
|
||||
<div class="infoitem">
|
||||
<div class="left">{{lan.$t('tixianzhuangtai')}}</div>
|
||||
<div class="right" >{{accountinfo.statusname}}</div>
|
||||
<div class="right" style="color: red;" >{{accountinfo.statusname}}</div>
|
||||
<!-- <div class="right" v-if="accountinfo.status==1">直播收入</div>
|
||||
<div class="right" v-if="accountinfo.status==2">提现</div>
|
||||
<div class="right" v-if="accountinfo.status==3">后台充值</div> -->
|
||||
@@ -83,7 +91,7 @@
|
||||
|
||||
<div class="infoitem">
|
||||
<div class="left">{{lan.$t('shoukuanzhanghu')}}</div>
|
||||
<div class="right">{{accountinfo.typename}}</div>
|
||||
<div class="right">{{accountinfo.typename}} {{accountinfo.account}}</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex">
|
||||
@@ -145,14 +153,20 @@ export default defineComponent({
|
||||
router.push({
|
||||
path: url
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function toinfo(id: string){
|
||||
router.push({path: '/regime/livedetail', query: {id: id}})
|
||||
}
|
||||
|
||||
return {
|
||||
accountinfo,
|
||||
query,
|
||||
navto,
|
||||
lan
|
||||
lan,
|
||||
toinfo
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
<div class="mingxilist" v-if="ifchina && listindex==2">
|
||||
<div class="mingxitop">
|
||||
<div class="tabs">
|
||||
<span class="tabtitle">{{lan.$t('mingxichaxun')}}</span>
|
||||
<span class="tabtitle">{{lan.$t('mingxichaxun')}}<img src="@/static/images/wenhao.png" @click="visible = true" /></span>
|
||||
|
||||
<div :class="tabindex == 0 ? 'on' : ''" @click="tabchange(0)">
|
||||
{{lan.$t('quanbu')}}
|
||||
@@ -186,18 +186,27 @@
|
||||
<tr v-for="(i,j) in salelist.data" :key="j">
|
||||
<td>{{i.created_at}}</td>
|
||||
<td>{{i.typename}}</td>
|
||||
<td class="moneyadd moneyreverse" v-if="i.type==2">-¥{{i.money}}</td>
|
||||
<td class="moneyadd " v-else>+¥{{i.money}}</td>
|
||||
<td class="moneyadd moneyreverse" v-if="i.type==2">-{{i.money}}</td>
|
||||
<td class="moneyadd " v-else>+{{i.money}}</td>
|
||||
<td style="cursor: pointer;" @click="navto(4,i.accountid)">查看详情</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<template v-if="!salelist.total">
|
||||
<a-empty />
|
||||
</template>
|
||||
</div>
|
||||
<div class="pages">
|
||||
<a-pagination v-model:current="page" :total="salelist.total" :showLessItems="true" @change="pagechange"/>
|
||||
<a-pagination v-if="salelist.total" v-model:current="page" :total="salelist.total" :showLessItems="true" @change="pagechange"/>
|
||||
</div>
|
||||
</div>
|
||||
<a-modal v-model:visible="visible" :footer="null" :title="lan.$t('shuoruguize')" @cancel="visible = false">
|
||||
<p>{{lan.$t('shouru1')}}</p>
|
||||
<p>{{lan.$t('shouru2')}}</p>
|
||||
<p style="color:#D12C2E">{{lan.$t('shouru3')}}</p>
|
||||
|
||||
</a-modal>
|
||||
<NavBottom class="navbottom"></NavBottom>
|
||||
</div>
|
||||
</template>
|
||||
@@ -225,6 +234,7 @@ export default defineComponent({
|
||||
const state=ref<number>(0)
|
||||
const dates=ref<Array<string>>(["",""])
|
||||
const page = ref(1);
|
||||
const visible = ref(false);
|
||||
onMounted(async () => {
|
||||
console.log(useRoute().query)
|
||||
listindex.value=1
|
||||
@@ -308,7 +318,8 @@ export default defineComponent({
|
||||
del,
|
||||
store,
|
||||
pagechange,
|
||||
lan
|
||||
lan,
|
||||
visible
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -338,7 +349,7 @@ export default defineComponent({
|
||||
color: #111;
|
||||
padding: 11px 0;
|
||||
.tabtitle {
|
||||
width: 60px;
|
||||
// width: 60px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
> div {
|
||||
@@ -351,6 +362,15 @@ export default defineComponent({
|
||||
color: #08ae98;
|
||||
}
|
||||
}
|
||||
>span{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
>img{
|
||||
margin-left: 8px;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
}
|
||||
}
|
||||
.on {
|
||||
color: #08ae98;
|
||||
position: relative;
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
:img="i.img"
|
||||
:title="i.title"
|
||||
:score="i.score"
|
||||
:date="i.starttime"
|
||||
:takehour="i.vodduration"
|
||||
:livenum="i.livenumber"
|
||||
:date="i.dateline"
|
||||
:takehour="i.livetime"
|
||||
:livenum="i.count"
|
||||
:status="i.livestatus"
|
||||
:zid="i.liveid"
|
||||
></LiveItem>
|
||||
@@ -41,9 +41,9 @@
|
||||
:img="i.img"
|
||||
:title="i.title"
|
||||
:score="i.score"
|
||||
:date="i.starttime"
|
||||
:takehour="i.vodduration"
|
||||
:livenum="i.livenumber"
|
||||
:date="i.dateline"
|
||||
:takehour="i.livetime"
|
||||
:livenum="i.count"
|
||||
:status="i.livestatus"
|
||||
:zid="i.liveid"
|
||||
></LiveItem>
|
||||
@@ -56,15 +56,18 @@
|
||||
:img="i.img"
|
||||
:title="i.title"
|
||||
:score="i.score"
|
||||
:date="i.starttime"
|
||||
:takehour="i.vodduration"
|
||||
:livenum="i.livenumber"
|
||||
:date="i.dateline"
|
||||
:takehour="i.livetime"
|
||||
:livenum="i.count"
|
||||
:status="i.livestatus"
|
||||
:zid="i.liveid"
|
||||
></LiveItem>
|
||||
</div>
|
||||
<template v-if="!livelist.total">
|
||||
<a-empty />
|
||||
</template>
|
||||
<div class="pages">
|
||||
<a-pagination v-model:current="page" :total="livelist.total" :showLessItems="true" />
|
||||
<a-pagination v-if="livelist.total" v-model:current="page" :total="livelist.total" :showLessItems="true" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -184,7 +187,7 @@ export default defineComponent({
|
||||
setup() {
|
||||
const lan: any = useI18n();
|
||||
const page = ref(1);
|
||||
const tabindex = ref(1);
|
||||
const tabindex = ref<number | string>(1);
|
||||
const livelist = ref<LivelistInfo>({
|
||||
code: 0,
|
||||
total: 0,
|
||||
@@ -197,7 +200,11 @@ export default defineComponent({
|
||||
});
|
||||
async function tab(){
|
||||
input.value = '';
|
||||
livelist.value = await getlivelist({ status: tabindex.value});
|
||||
let index: string | number = '';
|
||||
if(tabindex.value != 1){
|
||||
index = tabindex.value
|
||||
}
|
||||
livelist.value = await getlivelist({ status: index});
|
||||
|
||||
}
|
||||
function tabchange(e: number): void {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<LiveCount :info="liveinfo.studentlist" :livestatus="liveinfo.livestatus" :zid="liveinfo.liveid"></LiveCount>
|
||||
</div>
|
||||
<VideoReview class="review" v-if="liveinfo.livestatus == 2"></VideoReview>
|
||||
<VideoReview class="review" v-if="liveinfo.livestatus == 2" :videoinfo="liveinfo.score"></VideoReview>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,412 +1,30 @@
|
||||
<template>
|
||||
<div class="liveing">
|
||||
|
||||
<div class="top">
|
||||
<div class="left">{{lan.$t('zhiboyemian')}}</div>
|
||||
<div class="right">
|
||||
<img src="@/static/images/liveshare.png" alt="" @click="fenxiang()" />
|
||||
<img src="@/static/images/liveend.png" alt="" @click="visible = true" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<LiveingWatcher></LiveingWatcher>
|
||||
<div class="LivePlaying">
|
||||
<LivePlaying></LivePlaying>
|
||||
<div class="comment">
|
||||
<div class="commentitem">
|
||||
<span>13:32:30</span>
|
||||
<span class="name"> Andy : </span>
|
||||
<span> 老师,这个部分可以讲的慢一些吗? </span>
|
||||
</div>
|
||||
<div class="commentitem">
|
||||
<span>13:32:30</span>
|
||||
<span class="name"> Andy : </span>
|
||||
<span> 老师,这个部分可以讲的慢一些吗? </span>
|
||||
</div>
|
||||
<div class="commentitem">
|
||||
<span>13:32:30</span>
|
||||
<span class="name"> Andy : </span>
|
||||
<span> 老师,这个部分可以讲的慢一些吗? </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="others">
|
||||
<div :id="'s-' + item.memberid" class="othersitem" v-for="(item, index) in roominfo.studentlist" :key="index">
|
||||
<div class="watcher"></div>
|
||||
<div class="name">{{item.name}}</div>
|
||||
<!-- <img src="" alt="" /> -->
|
||||
</div>
|
||||
<!-- <div class="othersitem">
|
||||
<div class="watcher"></div>
|
||||
<div class="name">asdsada</div>
|
||||
<img src="" alt="" />
|
||||
</div>
|
||||
<div class="othersitem">
|
||||
<div class="watcher"></div>
|
||||
<div class="name">asdsada</div>
|
||||
<img src="" alt="" />
|
||||
</div>
|
||||
<div class="othersitem">
|
||||
<div class="watcher"></div>
|
||||
<div class="name">asdsada</div>
|
||||
<img src="" alt="" />
|
||||
</div> -->
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<a-modal v-model:visible="visible" title="Basic Modal" @ok="guanbi">
|
||||
<p>{{lan.$t('querenguanbi')}}</p>
|
||||
</a-modal>
|
||||
</div>
|
||||
<iframe style="width:100%;height: 100%" :src="url" frameborder="0"></iframe>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.liveing ::v-deep(.ant-breadcrumb) > span:last-child {
|
||||
color: #08ae98;
|
||||
}
|
||||
.liveing {
|
||||
width: 1320px;
|
||||
height: 563px;
|
||||
.top {
|
||||
width: 1321px;
|
||||
height: 57px;
|
||||
background: white;
|
||||
border-radius: 18px;
|
||||
margin-top: 23px;
|
||||
margin-bottom: 29px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #121212;
|
||||
font-size: 13px;
|
||||
align-items: center;
|
||||
.left {
|
||||
margin-left: 40px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.right {
|
||||
margin-right: 27px;
|
||||
> img {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.LivePlaying {
|
||||
border-radius: 18px;
|
||||
margin: 0 35px;
|
||||
.comment {
|
||||
width: 797px;
|
||||
height: 153px;
|
||||
background: white;
|
||||
border-radius: 18px;
|
||||
margin-top: 29px;
|
||||
padding: 30px;
|
||||
font-size: 12px;
|
||||
|
||||
color: #121212;
|
||||
|
||||
.commentitem {
|
||||
margin-bottom: 17px;
|
||||
.name {
|
||||
margin-left: 28px;
|
||||
color: #08ae98;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
.liveinfo {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 58px;
|
||||
align-items: center;
|
||||
.left {
|
||||
display: flex;
|
||||
color: #121212;
|
||||
font-size: 13px;
|
||||
margin-left: 29px;
|
||||
> div {
|
||||
margin-right: 57px;
|
||||
}
|
||||
.icon {
|
||||
width: 25px;
|
||||
height: 24px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
width: 74px;
|
||||
height: 29px;
|
||||
border: 1px solid #08ae98;
|
||||
border-radius: 3px;
|
||||
margin-right: 29px;
|
||||
color: #08ae98;
|
||||
font-size: 13px;
|
||||
line-height: 29px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.others {
|
||||
width: 252px;
|
||||
height: 630px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.othersitem {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 132px;
|
||||
margin-bottom: 35px;
|
||||
background-color: #eee;
|
||||
border-radius: 18px 18px 0px 0px;
|
||||
overflow: hidden;
|
||||
.watcher {
|
||||
width: 100%;
|
||||
height: 29px;
|
||||
background: #000000;
|
||||
color: white;
|
||||
position: absolute;
|
||||
opacity: 0.1;
|
||||
border-radius: 18px 18px 0px 0px;
|
||||
top: 0;
|
||||
}
|
||||
> img {
|
||||
width: 228px;
|
||||
height: 132px;
|
||||
border-radius: 18px;
|
||||
}
|
||||
.name {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: 28px;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref } from "vue";
|
||||
import LivePlaying from "@/components/LivePlaying.vue";
|
||||
import LiveingWatcher from "@/components/LiveingWatcher.vue";
|
||||
import TRTC from "trtc-js-sdk"
|
||||
import { getliveinfo, livestop, luzhi, usersig } from '@/api';
|
||||
import { getliveinfo } from '@/api';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import store from '@/store';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { useI18n } from '@/utils/i18n';
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
LivePlaying,
|
||||
LiveingWatcher,
|
||||
},
|
||||
setup() {
|
||||
const lan: any = useI18n();
|
||||
console.log(1);
|
||||
let client: any;
|
||||
let localStream: any;
|
||||
let statie = true;
|
||||
let userSing = '';
|
||||
let type = false;
|
||||
const visible = ref(false);
|
||||
const roominfo = ref<any>([]);
|
||||
console.log(useRoute())
|
||||
setup(){
|
||||
const id = useRoute().query.id;
|
||||
TRTC.checkSystemRequirements().then((result: any) => {
|
||||
if(!result) {
|
||||
message.error(lan.$t('buzhichitonghua'))
|
||||
}
|
||||
})
|
||||
async function qiehuan(){
|
||||
// 1 屏幕分享 2 摄像头
|
||||
client.unpublish(localStream)
|
||||
localStream = type ? TRTC.createStream({ userid: store.state.userinfo.memberid, audio: true, screen: true }) : TRTC.createStream({ userId: 10, audio: false, video: true });
|
||||
type = !type;
|
||||
localStream.initialize().then(()=>{
|
||||
client
|
||||
.publish(localStream)
|
||||
.catch((error: string) => {
|
||||
console.error('本地流发布失败 ' + error);
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
localStream.play('local_stream');
|
||||
console.log('本地流发布成功');
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
async function shexiang(){
|
||||
localStream = TRTC.createStream({ userId: store.state.userinfo.memberid, audio: true, video: true });
|
||||
const id = localStream.getId();
|
||||
await localStream
|
||||
.initialize()
|
||||
.catch((error: string) => {
|
||||
console.error('初始化本地流失败 ' + error);
|
||||
const url = ref<string>()
|
||||
if(id && typeof id == 'string'){
|
||||
getliveinfo(parseInt(id)).then((res: any)=>{
|
||||
url.value = `/zhibo.html?roomid=${res.roomid}&memberid=${res.memberid}`
|
||||
})
|
||||
.then(() => {
|
||||
console.log('初始化本地流成功');
|
||||
client
|
||||
.publish(localStream)
|
||||
.catch((error: string) => {
|
||||
console.error('本地流发布失败 ' + error);
|
||||
})
|
||||
.then(() => {
|
||||
const el = document.querySelector("#local_stream");
|
||||
if(el){
|
||||
el.innerHTML = ""
|
||||
}
|
||||
localStream.play('local_stream');
|
||||
console.log('本地流发布成功');
|
||||
console.log(id, 123)
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
async function pingmu(){
|
||||
const result = await TRTC.checkSystemRequirements()
|
||||
console.log(result,11111)
|
||||
if(!result) {
|
||||
message.error(lan.$t('buzhichifenxiang'));
|
||||
shexiang()
|
||||
return ;
|
||||
}
|
||||
|
||||
localStream = TRTC.createStream({ userid: store.state.userinfo.memberid, audio: true, screen: true });
|
||||
const id = localStream.getId();
|
||||
await localStream
|
||||
.initialize()
|
||||
.catch((error: string) => {
|
||||
console.error('初始化本地流失败 ' + error);
|
||||
message.error(lan.$t('xuanzefenxiangneirong'))
|
||||
|
||||
setTimeout(()=>{
|
||||
pingmu()
|
||||
}, 1000)
|
||||
})
|
||||
.then(() => {
|
||||
console.log('初始化本地流成功');
|
||||
client
|
||||
.publish(localStream)
|
||||
.catch((error: string) => {
|
||||
console.log('本地流发布失败 ' + error);
|
||||
|
||||
})
|
||||
.then(() => {
|
||||
const el = document.querySelector("#local_stream");
|
||||
if(el){
|
||||
el.innerHTML = ""
|
||||
}
|
||||
localStream.play('local_stream');
|
||||
console.log('本地流发布成功');
|
||||
console.log(id , 123)
|
||||
});
|
||||
});
|
||||
return {
|
||||
url
|
||||
}
|
||||
|
||||
async function init(fun: any, userSig: string): Promise<void>{
|
||||
console.log(userSig)
|
||||
const el = document.querySelector("#local_stream");
|
||||
if(el){
|
||||
el.innerHTML = ""
|
||||
}
|
||||
if (typeof id == "string") {
|
||||
roominfo.value = await getliveinfo(parseInt(id))
|
||||
console.log(roominfo.value)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
client = TRTC.createClient({
|
||||
mode: 'rtc',
|
||||
sdkAppId: '1400400340',
|
||||
userId: store.state.userinfo.memberid,
|
||||
userSig: userSig
|
||||
});
|
||||
client.on('stream-added', (event: { stream: any }) => {
|
||||
const remoteStream = event.stream;
|
||||
console.log('远端流增加: ' + remoteStream.getId());
|
||||
//订阅远端流
|
||||
client.subscribe(remoteStream);
|
||||
});
|
||||
client.on('stream-subscribed', (event: { stream: any }) => {
|
||||
const remoteStream = event.stream;
|
||||
console.log(remoteStream);
|
||||
// 播放远端流
|
||||
remoteStream.play('s-' + remoteStream.userId_);
|
||||
});
|
||||
client
|
||||
.join({ roomId: roominfo.value.roomid})
|
||||
.catch((error: string) => {
|
||||
console.error('进房失败 ' + error);
|
||||
})
|
||||
.then(() => {
|
||||
console.log('进房成功');
|
||||
// if(typeof id == "string"){
|
||||
luzhi(roominfo.value.roomid)
|
||||
// }
|
||||
fun()
|
||||
});
|
||||
}
|
||||
async function fenxiang(){
|
||||
console.log(localStream)
|
||||
|
||||
|
||||
await client.unpublish(localStream).then(() => {
|
||||
// 关闭屏幕分享流
|
||||
console.log("关闭")
|
||||
// client.leave().then(() => {
|
||||
// // leaving room success
|
||||
// console.log("关闭成功")
|
||||
// }).catch((error: string) => {
|
||||
// console.error('leaving room failed: ' + error);
|
||||
// });
|
||||
const el = document.querySelector("#local_stream");
|
||||
if(el){
|
||||
el.innerHTML = ""
|
||||
}
|
||||
});
|
||||
statie ? await shexiang() : await pingmu();
|
||||
statie = !statie;
|
||||
console.log(localStream.getId())
|
||||
}
|
||||
|
||||
function guanbi(){
|
||||
client.leave().then(() => {
|
||||
// leaving room success
|
||||
visible.value = false;
|
||||
if(typeof id == "string"){
|
||||
livestop(id, roominfo.value.roomid)
|
||||
}
|
||||
}).catch((error: string) => {
|
||||
message.error(lan.$t('guanbishibai')+':' + error);
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(async ()=>{
|
||||
const si = setInterval(async ()=>{
|
||||
if(store.state.userinfo.memberid != 0 && store.state.userinfo.memberid){
|
||||
clearInterval(si);
|
||||
userSing = await usersig(store.state.userinfo.memberid);
|
||||
init(pingmu, userSing);
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
return{
|
||||
fenxiang,
|
||||
qiehuan,
|
||||
roominfo,
|
||||
guanbi,
|
||||
visible,
|
||||
lan
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
587
src/views/regime/LiveingBACK.vue
Normal file
@@ -0,0 +1,587 @@
|
||||
<template>
|
||||
<div class="liveing">
|
||||
|
||||
<div class="top">
|
||||
<div class="left">{{lan.$t('zhiboyemian')}}</div>
|
||||
<div class="right">
|
||||
<img src="@/static/images/liveshare.png" alt="" @click="fenxiang()" />
|
||||
<img src="@/static/images/liveend.png" alt="" @click="visible = true" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<LiveingWatcher :list="roominfo.studentlist" @cameta="sendtext" @vol="senvol"></LiveingWatcher>
|
||||
<div class="LivePlaying">
|
||||
<LivePlaying></LivePlaying>
|
||||
<div class="comment">
|
||||
<div class="commentitem" v-for="(item,index) in imlist" :key="index">
|
||||
<span>{{item.time}}</span>
|
||||
<span class="name"> {{item.name}} : </span>
|
||||
<span> {{item.text}} </span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="others">
|
||||
<div class="othersitem" v-for="(item, index) in roominfo.studentlist" :key="index">
|
||||
<div class="watcher"></div>
|
||||
<div class="name">{{item.name}}</div>
|
||||
<div class="sbox" :id="'s-' + item.memberid"></div>
|
||||
<!-- <img src="" alt="" /> -->
|
||||
</div>
|
||||
<!-- <div class="othersitem">
|
||||
<div class="watcher"></div>
|
||||
<div class="name">asdsada</div>
|
||||
<img src="" alt="" />
|
||||
</div>
|
||||
<div class="othersitem">
|
||||
<div class="watcher"></div>
|
||||
<div class="name">asdsada</div>
|
||||
<img src="" alt="" />
|
||||
</div>
|
||||
<div class="othersitem">
|
||||
<div class="watcher"></div>
|
||||
<div class="name">asdsada</div>
|
||||
<img src="" alt="" />
|
||||
</div> -->
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<a-modal v-model:visible="visible" title="Basic Modal" @ok="guanbi">
|
||||
<p>{{lan.$t('querenguanbi')}}</p>
|
||||
</a-modal>
|
||||
<a-modal v-model:visible="xuanze" title="提示" okText="摄像头" cancelText="屏幕分享" @ok="xianze(1)" @cancel="xianze(0)" :closable="false" :maskClosable="false">
|
||||
<p>请选择开播方式</p>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.liveing ::v-deep(.ant-breadcrumb) > span:last-child {
|
||||
color: #08ae98;
|
||||
}
|
||||
.liveing {
|
||||
width: 1320px;
|
||||
height: 563px;
|
||||
.top {
|
||||
width: 1321px;
|
||||
height: 57px;
|
||||
background: white;
|
||||
border-radius: 18px;
|
||||
margin-top: 23px;
|
||||
margin-bottom: 29px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #121212;
|
||||
font-size: 13px;
|
||||
align-items: center;
|
||||
.left {
|
||||
margin-left: 40px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.right {
|
||||
margin-right: 27px;
|
||||
> img {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.LivePlaying {
|
||||
border-radius: 18px;
|
||||
margin: 0 35px;
|
||||
.comment {
|
||||
width: 797px;
|
||||
height: 153px;
|
||||
background: white;
|
||||
border-radius: 18px;
|
||||
margin-top: 29px;
|
||||
padding: 30px;
|
||||
font-size: 12px;
|
||||
overflow-y: auto;
|
||||
color: #121212;
|
||||
|
||||
.commentitem {
|
||||
margin-bottom: 17px;
|
||||
.name {
|
||||
margin-left: 28px;
|
||||
color: #08ae98;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
.liveinfo {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 58px;
|
||||
align-items: center;
|
||||
.left {
|
||||
display: flex;
|
||||
color: #121212;
|
||||
font-size: 13px;
|
||||
margin-left: 29px;
|
||||
> div {
|
||||
margin-right: 57px;
|
||||
}
|
||||
.icon {
|
||||
width: 25px;
|
||||
height: 24px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
width: 74px;
|
||||
height: 29px;
|
||||
border: 1px solid #08ae98;
|
||||
border-radius: 3px;
|
||||
margin-right: 29px;
|
||||
color: #08ae98;
|
||||
font-size: 13px;
|
||||
line-height: 29px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.others {
|
||||
width: 252px;
|
||||
height: 630px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.othersitem {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 132px;
|
||||
margin-bottom: 35px;
|
||||
background-color: #eee;
|
||||
border-radius: 18px 18px 0px 0px;
|
||||
overflow: hidden;
|
||||
.watcher {
|
||||
width: 100%;
|
||||
height: 29px;
|
||||
background: #000000;
|
||||
color: white;
|
||||
position: absolute;
|
||||
opacity: 0.1;
|
||||
border-radius: 18px 18px 0px 0px;
|
||||
top: 0;
|
||||
}
|
||||
> img {
|
||||
width: 228px;
|
||||
height: 132px;
|
||||
border-radius: 18px;
|
||||
}
|
||||
.name {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: 28px;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
}
|
||||
.sbox{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref, resolveComponent } from "vue";
|
||||
import LivePlaying from "@/components/LivePlaying.vue";
|
||||
import LiveingWatcher from "@/components/LiveingWatcher.vue";
|
||||
import TRTC from "trtc-js-sdk"
|
||||
import { getliveinfo, livestop, luzhi, usersig } from '@/api';
|
||||
import { useRoute } from 'vue-router';
|
||||
import store from '@/store';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { useI18n } from '@/utils/i18n';
|
||||
import TIM from 'tim-js-sdk';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
LivePlaying,
|
||||
LiveingWatcher,
|
||||
},
|
||||
setup() {
|
||||
const lan: any = useI18n();
|
||||
console.log(1);
|
||||
let client: any;
|
||||
let localStream: any;
|
||||
let statie = true;
|
||||
let userSing = '';
|
||||
let type = false;
|
||||
const visible = ref(false);
|
||||
const roominfo = ref<any>([]);
|
||||
console.log(useRoute())
|
||||
const id = useRoute().query.id;
|
||||
let tim: any;
|
||||
TRTC.checkSystemRequirements().then((result: any) => {
|
||||
if(!result) {
|
||||
message.error(lan.$t('buzhichitonghua'))
|
||||
}
|
||||
})
|
||||
async function qiehuan(){
|
||||
// 1 屏幕分享 2 摄像头
|
||||
client.unpublish(localStream)
|
||||
localStream = type ? TRTC.createStream({ userid: store.state.userinfo.memberid, audio: true, screen: true }) : TRTC.createStream({ userId: 10, audio: false, video: true });
|
||||
type = !type;
|
||||
localStream.initialize().then(()=>{
|
||||
client
|
||||
.publish(localStream)
|
||||
.catch((error: string) => {
|
||||
console.error('本地流发布失败 ' + error);
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
localStream.play('local_stream');
|
||||
console.log('本地流发布成功');
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
async function shexiang(){
|
||||
localStream = TRTC.createStream({ userId: store.state.userinfo.memberid, audio: true, video: true });
|
||||
const id = localStream.getId();
|
||||
await localStream
|
||||
.initialize()
|
||||
.catch((error: string) => {
|
||||
console.error('初始化本地流失败 ' + error);
|
||||
})
|
||||
.then(() => {
|
||||
console.log('初始化本地流成功');
|
||||
client
|
||||
.publish(localStream)
|
||||
.catch((error: string) => {
|
||||
console.error('本地流发布失败 ' + error);
|
||||
})
|
||||
.then(() => {
|
||||
const el = document.querySelector("#local_stream");
|
||||
if(el){
|
||||
el.innerHTML = ""
|
||||
}
|
||||
localStream.play('local_stream');
|
||||
console.log('本地流发布成功');
|
||||
console.log(id, 123)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function pingmu(){
|
||||
const result = await TRTC.checkSystemRequirements()
|
||||
console.log(result,11111)
|
||||
if(!result) {
|
||||
message.error(lan.$t('buzhichifenxiang'));
|
||||
shexiang()
|
||||
return ;
|
||||
}
|
||||
|
||||
localStream = TRTC.createStream({ userid: store.state.userinfo.memberid, audio: true, screen: true });
|
||||
const id = localStream.getId();
|
||||
await localStream
|
||||
.initialize()
|
||||
.catch((error: string) => {
|
||||
console.error('初始化本地流失败 ' + error);
|
||||
message.error(lan.$t('xuanzefenxiangneirong'))
|
||||
|
||||
setTimeout(()=>{
|
||||
pingmu()
|
||||
}, 1000)
|
||||
})
|
||||
.then(() => {
|
||||
console.log('初始化本地流成功');
|
||||
client
|
||||
.publish(localStream)
|
||||
.catch((error: string) => {
|
||||
console.log('本地流发布失败 ' + error);
|
||||
|
||||
})
|
||||
.then(() => {
|
||||
const el = document.querySelector("#local_stream");
|
||||
if(el){
|
||||
el.innerHTML = ""
|
||||
}
|
||||
localStream.play('local_stream');
|
||||
console.log('本地流发布成功');
|
||||
console.log(id , 123)
|
||||
});
|
||||
});
|
||||
}
|
||||
const imlist = ref<any>([])
|
||||
async function init(fun: any, userSig: string): Promise<void>{
|
||||
console.log(userSig)
|
||||
const el = document.querySelector("#local_stream");
|
||||
if(el){
|
||||
el.innerHTML = ""
|
||||
}
|
||||
if (typeof id == "string") {
|
||||
roominfo.value = await getliveinfo(parseInt(id))
|
||||
console.log(roominfo.value)
|
||||
}
|
||||
|
||||
|
||||
client = TRTC.createClient({
|
||||
mode: 'rtc',
|
||||
sdkAppId: '1400435767',
|
||||
userId: store.state.userinfo.memberid,
|
||||
userSig: userSig
|
||||
});
|
||||
// 监听远端开启推流
|
||||
client.on('stream-added', (event: { stream: any }) => {
|
||||
const remoteStream = event.stream;
|
||||
console.log('远端流增加: ' + remoteStream.getId());
|
||||
//订阅远端流
|
||||
client.subscribe(remoteStream);
|
||||
});
|
||||
// 远端流初始化成功 本地播放
|
||||
client.on('stream-subscribed', (event: { stream: any }) => {
|
||||
const remoteStream = event.stream;
|
||||
console.log(remoteStream);
|
||||
// 播放远端流
|
||||
const el = document.querySelector('#s-' + remoteStream.userId_);
|
||||
if(el){
|
||||
el.innerHTML = ""
|
||||
}
|
||||
remoteStream.play('s-' + remoteStream.userId_);
|
||||
});
|
||||
// 远端关闭麦克风
|
||||
client.on('mute-audio', (event: any) => {
|
||||
const userId = event.userId;
|
||||
console.log(userId, '远端关闭麦克风')
|
||||
});
|
||||
// 远端关闭摄像头
|
||||
client.on('mute-video', (event: any) => {
|
||||
const userId = event.userId;
|
||||
console.log(userId, '远端关闭摄像头')
|
||||
|
||||
});
|
||||
// 远端打开麦克风
|
||||
client.on('unmute-audio', (event: any) => {
|
||||
const userId = event.userId;
|
||||
console.log(userId, '远端打开麦克风')
|
||||
|
||||
});
|
||||
// 远端打开摄像头
|
||||
client.on('unmute-video', (event: any) => {
|
||||
const userId = event.userId;
|
||||
console.log(userId, '远端打开摄像头')
|
||||
|
||||
});
|
||||
client
|
||||
.join({ roomId: roominfo.value.roomid})
|
||||
.catch((error: string) => {
|
||||
console.error('进房失败 ' + error);
|
||||
})
|
||||
.then(() => {
|
||||
console.log('进房成功');
|
||||
// if(typeof id == "string"){
|
||||
luzhi(roominfo.value.roomid)
|
||||
// }
|
||||
fun()
|
||||
});
|
||||
|
||||
// im 初始化
|
||||
tim = TIM.create({
|
||||
SDKAppID: 1400435767
|
||||
}); // SDK 实例通常用 tim 表示
|
||||
tim.setLogLevel(0);
|
||||
tim.on(TIM.EVENT.MESSAGE_RECEIVED, function(event: any) {
|
||||
// 收到推送的单聊、群聊、群提示、群系统通知的新消息,可通过遍历 event.data 获取消息列表数据并渲染到页面
|
||||
// event.name - TIM.EVENT.MESSAGE_RECEIVED
|
||||
// event.data - 存储 Message 对象的数组 - [Message]
|
||||
for(const i in event.data){
|
||||
console.log(event.data[i])
|
||||
const now = dayjs(event.data[i].time)
|
||||
if(!event.data[i].payload.text){
|
||||
break;
|
||||
}
|
||||
imlist.value.push({
|
||||
name: event.data[i].nick,
|
||||
text: event.data[i].payload.text,
|
||||
time: `${now.hour()}:${now.minute()}:${now.second()}`
|
||||
})
|
||||
const div = document.querySelector(".comment")
|
||||
if(div){
|
||||
div.scrollTop = div.scrollHeight
|
||||
}
|
||||
}
|
||||
});
|
||||
tim.on(TIM.EVENT.GROUP_LIST_UPDATED, function(event: any) {
|
||||
// 收到群组列表更新通知,可通过遍历 event.data 获取群组列表数据并渲染到页面
|
||||
// event.name - TIM.EVENT.GROUP_LIST_UPDATED
|
||||
// event.data - 存储 Group 对象的数组 - [Group]
|
||||
console.log(event.data)
|
||||
});
|
||||
tim.login({userID: store.state.userinfo.memberid.toString(), userSig: userSig}).then((res: any)=>{
|
||||
console.log(res.data); // 登录成功
|
||||
if (res.data.repeatLogin === true) {
|
||||
// 标识账号已登录,本次登录操作为重复登录。v2.5.1 起支持
|
||||
console.log(res.data.errorInfo);
|
||||
}
|
||||
|
||||
}).catch(function(imError: any) {
|
||||
console.warn('login error:', imError); // 登录失败的相关信息
|
||||
});
|
||||
tim.on(TIM.EVENT.SDK_READY, function (){
|
||||
const promise = tim.createGroup({
|
||||
type: TIM.TYPES.GRP_AVCHATROOM,
|
||||
name: 'live',
|
||||
groupID: roominfo.value.roomid
|
||||
});
|
||||
promise.then(function(imResponse: any) { // 创建成功
|
||||
console.log(imResponse.data.group); // 创建的群的资料
|
||||
tim.joinGroup({
|
||||
groupID: roominfo.value.roomid,
|
||||
type: TIM.TYPES.GRP_AVCHATROOM
|
||||
}).then((res: any)=>{
|
||||
switch (res.data.status) {
|
||||
case TIM.TYPES.JOIN_STATUS_WAIT_APPROVAL: // 等待管理员同意
|
||||
break;
|
||||
case TIM.TYPES.JOIN_STATUS_SUCCESS: // 加群成功
|
||||
console.log(res.data.group); // 加入的群组资料
|
||||
break;
|
||||
case TIM.TYPES.JOIN_STATUS_ALREADY_IN_GROUP: // 已经在群中
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}).catch((err: any)=>{
|
||||
console.log(err)
|
||||
})
|
||||
}).catch(function(imError: any) {
|
||||
console.warn('createGroup error:', imError); // 创建群组失败的相关信息
|
||||
tim.joinGroup({
|
||||
groupID: roominfo.value.roomid,
|
||||
type: TIM.TYPES.GRP_AVCHATROOM
|
||||
}).then((res: any)=>{
|
||||
switch (res.data.status) {
|
||||
case TIM.TYPES.JOIN_STATUS_WAIT_APPROVAL: // 等待管理员同意
|
||||
break;
|
||||
case TIM.TYPES.JOIN_STATUS_SUCCESS: // 加群成功
|
||||
console.log(res.data.group); // 加入的群组资料
|
||||
break;
|
||||
case TIM.TYPES.JOIN_STATUS_ALREADY_IN_GROUP: // 已经在群中
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}).catch((err: any)=>{
|
||||
console.log(err)
|
||||
})
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
async function fenxiang(){
|
||||
console.log(localStream)
|
||||
|
||||
|
||||
await client.unpublish(localStream).then(() => {
|
||||
// 关闭屏幕分享流
|
||||
console.log("关闭")
|
||||
// client.leave().then(() => {
|
||||
// // leaving room success
|
||||
// console.log("关闭成功")
|
||||
// }).catch((error: string) => {
|
||||
// console.error('leaving room failed: ' + error);
|
||||
// });
|
||||
const el = document.querySelector("#local_stream");
|
||||
if(el){
|
||||
el.innerHTML = ""
|
||||
}
|
||||
});
|
||||
statie ? await shexiang() : await pingmu();
|
||||
statie = !statie;
|
||||
console.log(localStream.getId())
|
||||
}
|
||||
|
||||
function guanbi(){
|
||||
client.leave().then(() => {
|
||||
// leaving room success
|
||||
visible.value = false;
|
||||
if(typeof id == "string"){
|
||||
livestop(id, roominfo.value.roomid)
|
||||
}
|
||||
}).catch((error: string) => {
|
||||
message.error(lan.$t('guanbishibai')+':' + error);
|
||||
});
|
||||
}
|
||||
|
||||
function sendtext(id: number){
|
||||
const m = tim.createTextMessage({
|
||||
to: roominfo.value.roomid,
|
||||
conversationType: TIM.TYPES.CONV_GROUP,
|
||||
payload: {
|
||||
text: `beelinkMuteUserId:${id},isClose:0`
|
||||
}
|
||||
});
|
||||
const promise = tim.sendMessage(m);
|
||||
promise.then(function(imResponse: any) {
|
||||
// 发送成功
|
||||
console.log(imResponse);
|
||||
message.success("发送命令成功")
|
||||
}).catch(function(imError: any) {
|
||||
// 发送失败
|
||||
message.error("发送命令失败")
|
||||
console.warn('sendMessage error:', imError);
|
||||
});
|
||||
}
|
||||
function sendvol(id: number){
|
||||
const m = tim.createTextMessage({
|
||||
to: roominfo.value.roomid,
|
||||
conversationType: TIM.TYPES.CONV_GROUP,
|
||||
payload: {
|
||||
text: `beelinkTurnOffTheCameraUserId:${id},isClose:1
|
||||
0`
|
||||
}
|
||||
});
|
||||
const promise = tim.sendMessage(m);
|
||||
promise.then(function(imResponse: any) {
|
||||
// 发送成功
|
||||
console.log(imResponse);
|
||||
message.success("发送命令成功")
|
||||
}).catch(function(imError: any) {
|
||||
// 发送失败
|
||||
message.error("发送命令失败")
|
||||
console.warn('sendMessage error:', imError);
|
||||
});
|
||||
}
|
||||
const xuanze = ref(true)
|
||||
async function xianze(index: number){
|
||||
if(store.state.userinfo.memberid != 0 && store.state.userinfo.memberid){
|
||||
// clearInterval(si);
|
||||
userSing = await usersig(store.state.userinfo.memberid);
|
||||
init(index == 0 ? pingmu : shexiang, userSing);
|
||||
xuanze.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// onMounted(async ()=>{
|
||||
// const si = setInterval(async ()=>{
|
||||
|
||||
// })
|
||||
|
||||
// })
|
||||
|
||||
|
||||
|
||||
return{
|
||||
fenxiang,
|
||||
qiehuan,
|
||||
roominfo,
|
||||
guanbi,
|
||||
visible,
|
||||
lan,
|
||||
sendtext,
|
||||
imlist,
|
||||
xianze,
|
||||
xuanze
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="title">直播报名学生</div>
|
||||
<div class="sel">
|
||||
<img src="@/static/images/sousuo.png" alt="" class="icon" />
|
||||
<input type="text" placeholder="请输入想要搜索的学生姓名" v-model="condition.title" @keyup.enter="search(condition)"/>
|
||||
<input type="text" placeholder="请输入想要搜索的学生姓名" v-model="condition.keyword" @keyup.enter="search(condition)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mid">
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<div class="infoitem">
|
||||
<span class="label">所在国家:</span>
|
||||
<span class="one-line-hide">{{i.live}}</span>
|
||||
<span class="one-line-hide">{{i.country}}</span>
|
||||
</div>
|
||||
|
||||
<div class="infoitem">
|
||||
@@ -49,7 +49,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="!teacherlikedlist.length">
|
||||
<a-empty />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="pages">
|
||||
@@ -177,7 +179,7 @@ export default defineComponent({
|
||||
});
|
||||
const title=ref('')
|
||||
const condition = ref<any>({
|
||||
title:"",
|
||||
keyword:"",
|
||||
id:Number(useRoute().query.liveid)
|
||||
})
|
||||
onMounted(async () => {
|
||||
|
||||
@@ -50,11 +50,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="!teacherlikedlist.total">
|
||||
<a-empty />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pages">
|
||||
<a-pagination v-model:current="page" :total="teacherlikedlist.total" :showLessItems="true" @change="pagechange"/>
|
||||
<a-pagination v-if="teacherlikedlist.total" v-model:current="page" :total="teacherlikedlist.total" :showLessItems="true" @change="pagechange"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -20,7 +20,11 @@
|
||||
<input type="text" :placeholder="lan.$t('shipinsousuo')" @keyup.enter="sel()" v-model="input"/>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="!videolist.total">
|
||||
<a-empty />
|
||||
</template>
|
||||
<div class="list" v-if="tabindex == 4">
|
||||
|
||||
<VideoItem
|
||||
v-for="(i, j) in videolist.data"
|
||||
:key="j"
|
||||
@@ -34,6 +38,7 @@
|
||||
:status="i.status"
|
||||
:watch="i.watch"
|
||||
:share="i.share"
|
||||
:transcoding="i.transcoding"
|
||||
></VideoItem>
|
||||
</div>
|
||||
|
||||
@@ -50,6 +55,7 @@
|
||||
:livenum="i.statusname"
|
||||
:status="i.status"
|
||||
:watch="i.watch"
|
||||
:transcoding="i.transcoding"
|
||||
:share="i.share"
|
||||
></VideoItem>
|
||||
</div>
|
||||
@@ -66,6 +72,7 @@
|
||||
:livenum="i.statusname"
|
||||
:status="i.status"
|
||||
:watch="i.watch"
|
||||
:transcoding="i.transcoding"
|
||||
:share="i.share"
|
||||
></VideoItem>
|
||||
</div>
|
||||
@@ -82,11 +89,12 @@
|
||||
:livenum="i.statusname"
|
||||
:status="i.status"
|
||||
:watch="i.watch"
|
||||
:transcoding="i.transcoding"
|
||||
:share="i.share"
|
||||
></VideoItem>
|
||||
</div>
|
||||
<div class="pages">
|
||||
<a-pagination v-model:current="page" :total="videolist.total" :showLessItems="true" @change="pagechange" />
|
||||
<a-pagination v-if="videolist.total" v-model:current="page" :total="videolist.total" :showLessItems="true" @change="pagechange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="videoinfo">
|
||||
<div class="info">
|
||||
<VideoPlay :url="result.fileurl"></VideoPlay>
|
||||
<VideoCont :videoid="result.videoid" :date="result.createdAt" :watch="result.watch" :share="result.share" :status="result.status"></VideoCont>
|
||||
<VideoPlay :title="result.title" :url="result.fileid"></VideoPlay>
|
||||
<VideoCont :videoid="result.videoid" :yuanyin="result.statusdesc" :date="result.createdAt" :watch="result.watch" :share="result.share" :status="result.status"></VideoCont>
|
||||
</div>
|
||||
<VideoReview class="review"></VideoReview>
|
||||
<VideoReview :videoinfo="result.score" class="review" v-if="result.status == 1"></VideoReview>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
<div class="row" v-for="(item,index) in month.date" :key="index">
|
||||
<div v-for="(i,j) in item" :key="j">
|
||||
<div class="day">
|
||||
<div :class="{ing: yue == 0 && month.day == i.day,old: (yue < 0 || (yue == 0 && month.day > i.day)) && i.list.length != 0 ,next: (yue > 0 || (yue == 0 && month.day < i.day)) && i.list.length != 0 }" @click="navto(i.time)">
|
||||
<div :class="{ing:i.s == 1, old: i.s == 2 ,next: i.s == 0 }" @click="navto(i.time)">
|
||||
{{i.day}}
|
||||
<div class="item" v-for="(i,j) in i.list" :key="j">
|
||||
<div></div><p>{{i}}</p>
|
||||
<div class="item" @click.stop="gotolive(i.id, i.statit)" v-for="(i,j) in i.list" :key="j">
|
||||
<div></div><p>{{i.title}}</p>
|
||||
</div>
|
||||
<!-- <span class="ing"></span> -->
|
||||
<!-- <span class="next"></span> -->
|
||||
@@ -99,6 +99,9 @@
|
||||
width: 1320px;
|
||||
height: 63px;
|
||||
display: flex;
|
||||
position: sticky;
|
||||
top: -23px;
|
||||
z-index: 1000;
|
||||
>div{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -214,11 +217,20 @@ export default defineComponent({
|
||||
const day = getDay(res[i].dateline)
|
||||
for(const j in month.value.date){
|
||||
for(const k in month.value.date[j]){
|
||||
if(yue.value == 0 && month.value.date[j][k].day == month.value.day){
|
||||
month.value.date[j][k].s = 1
|
||||
}
|
||||
if(month.value.date[j][k].day == day){
|
||||
if(month.value.date[j][k].list == undefined){
|
||||
month.value.date[j][k].list = [];
|
||||
}
|
||||
month.value.date[j][k].list.push(res[i].title);
|
||||
|
||||
if(!month.value.date[j][k].s){
|
||||
month.value.date[j][k].s = res[i].livestatus
|
||||
}else if(month.value.date[j][k].s != 1){
|
||||
res[i].livestatus == 0 && month.value.date[j][k].s == 2 ? month.value.date[j][k].s = 0 : month.value.date[j][k].s = 2
|
||||
}
|
||||
month.value.date[j][k].list.push({title: res[i].title, id: res[i].liveid, statit: res[i].livestatus});
|
||||
console.log(day)
|
||||
}
|
||||
}
|
||||
@@ -245,13 +257,25 @@ export default defineComponent({
|
||||
function navto(date: string){
|
||||
router.push("/regime/week?time=" + (!date? '' : date) )
|
||||
}
|
||||
function gotolive(id: string, s: number){
|
||||
let url = '';
|
||||
switch (s) {
|
||||
case 1:
|
||||
url = '/regime/liveing';
|
||||
break;
|
||||
default:
|
||||
url = '/regime/livedetail';
|
||||
}
|
||||
router.push({path: url,query: { id: id }})
|
||||
}
|
||||
getdates(userid.value);
|
||||
return {
|
||||
month,
|
||||
xia,
|
||||
shang,
|
||||
yue,
|
||||
navto
|
||||
navto,
|
||||
gotolive
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
上一周
|
||||
</div>
|
||||
{{week.year}}年{{week.yue}}月
|
||||
<a-button type="primary" class="button" @click="navto()"> 月日历 </a-button>
|
||||
<a-button type="primary" class="button" @click="navto('/regime/date')"> 月日历 </a-button>
|
||||
<div @click="zhou++">
|
||||
下一周
|
||||
<img src="../../static/images/right.png" alt="" />
|
||||
@@ -40,13 +40,13 @@
|
||||
<div class="body">
|
||||
<div class="row" v-for="item in 24" :key="item">
|
||||
<div class="day date" :style="{'background-color': item - 1 == xs ? '#0DBBA4' : '', 'color': item - 1 == xs ? '#fff' : ''}">
|
||||
{{ item > 10 ? item - 1 : "0" + (item - 1) }}:00-{{
|
||||
{{ item > 9 ? item - 1 : "0" + (item - 1) }}:00-{{
|
||||
item > 9 ? item : "0" + item
|
||||
}}:00
|
||||
</div>
|
||||
<div v-for="i in 7" :key="i" >
|
||||
<div class="day">
|
||||
<div :class="zhuangtai(week.date[i -1].list[item - 1].zhuangtai)" v-if="week.date[i -1].list[item - 1].title != ''" :style="{'top': (week.date[i -1].list[item - 1].start / 60 * 0.63) + 'rem', 'min-height': (week.date[i -1].list[item - 1].num / 60 * 0.63) + 'rem' }">
|
||||
<div :class="zhuangtai(week.date[i -1].list[item - 1].zhuangtai)" v-if="week.date[i -1].list[item - 1].title != ''" :style="{'top': (week.date[i -1].list[item - 1].start / 60 * 0.63) + 'rem', 'min-height': (week.date[i -1].list[item - 1].num / 60 * 0.63) + 'rem' }" @click="tolive(week.date[i -1].list[item - 1])">
|
||||
<div class="one-line-hide" style="max-width: 1.5rem">
|
||||
{{week.date[i -1].list[item - 1].title}}
|
||||
</div>
|
||||
@@ -122,6 +122,9 @@
|
||||
width: 1320px;
|
||||
height: 63px;
|
||||
display: flex;
|
||||
position: sticky;
|
||||
top: -23px;
|
||||
z-index: 1000;
|
||||
> div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -189,6 +192,7 @@
|
||||
justify-content: center;
|
||||
font-size: 11px;
|
||||
border-radius: 6px;
|
||||
z-index: 999;
|
||||
overflow: hidden;
|
||||
> div:last-child {
|
||||
font-size: 11px;
|
||||
@@ -238,7 +242,7 @@
|
||||
}
|
||||
.times{
|
||||
font-size: 11px;
|
||||
color: #FFFA18;
|
||||
color: #D12C2E;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,7 +262,7 @@ export default defineComponent({
|
||||
setup() {
|
||||
const zhou = ref(0);
|
||||
const time: any = useRoute().query.time;
|
||||
const week = ref<any>(getweek(time));
|
||||
const week = ref<any>(getweek(time, store.state.userinfo.zoneValue));
|
||||
const userid = store.state.userinfo.memberid;
|
||||
console.log(week.value);
|
||||
function getdates(userid: number){
|
||||
@@ -276,6 +280,7 @@ export default defineComponent({
|
||||
week.value.date[j].list[gethour(res[i].dateline)].title = res[i].title
|
||||
week.value.date[j].list[gethour(res[i].dateline)].time = gettime(res[i].dateline, res[i].livetime)
|
||||
week.value.date[j].list[gethour(res[i].dateline)].zhuangtai = res[i].livestatus
|
||||
week.value.date[j].list[gethour(res[i].dateline)].zid = res[i].liveid
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,7 +294,14 @@ export default defineComponent({
|
||||
const times = ref('');
|
||||
const xs = ref(0);
|
||||
setInterval(()=>{
|
||||
const now = dayjs();
|
||||
/* eslint-disable */
|
||||
const utc = require('dayjs/plugin/utc') // dependent on utc plugin
|
||||
/* eslint-disable */
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
const days: any = dayjs;
|
||||
const now = days().tz(store.state.userinfo.zoneValue)
|
||||
const xiaoshi = now.hour()
|
||||
const fenzhong = now.minute()
|
||||
top.value = (xiaoshi + (fenzhong / 60)) * 0.63;
|
||||
@@ -298,7 +310,7 @@ export default defineComponent({
|
||||
}, 2000)
|
||||
|
||||
watch(zhou, (value) => {
|
||||
week.value = getweek(time, value);
|
||||
week.value = getweek(time, store.state.userinfo.zoneValue, value);
|
||||
console.log(week.value);
|
||||
getdates(userid)
|
||||
});
|
||||
@@ -315,8 +327,19 @@ export default defineComponent({
|
||||
return 'old'
|
||||
}
|
||||
}
|
||||
function navto(){
|
||||
router.push("/regime/date")
|
||||
function navto(url: string){
|
||||
router.push(url)
|
||||
}
|
||||
function tolive(data: any){
|
||||
let url = '';
|
||||
console.log(data)
|
||||
if(data.zhuangtai != 1){
|
||||
url = '/regime/livedetail?id=' + data.zid
|
||||
}else{
|
||||
url = '/regime/liveing?id=' + data.zid
|
||||
}
|
||||
console.log(url)
|
||||
router.push(url)
|
||||
}
|
||||
return {
|
||||
zhou,
|
||||
@@ -325,7 +348,8 @@ export default defineComponent({
|
||||
zhuangtai,
|
||||
top,
|
||||
times,
|
||||
xs
|
||||
xs,
|
||||
tolive
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
@@ -35,6 +36,10 @@
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
"node_modules",
|
||||
"tslint:recommended"
|
||||
],
|
||||
"rules": {
|
||||
"no-var-requires": false
|
||||
}
|
||||
}
|
||||
|
||||
10
yarn.lock
@@ -2151,6 +2151,11 @@ babel-plugin-dynamic-import-node@^2.3.3:
|
||||
dependencies:
|
||||
object.assign "^4.1.0"
|
||||
|
||||
babel-plugin-transform-remove-console@^6.9.4:
|
||||
version "6.9.4"
|
||||
resolved "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz#b980360c067384e24b357a588d807d3c83527780"
|
||||
integrity sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A=
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
@@ -9240,6 +9245,11 @@ vod-js-sdk-v6@^1.4.10:
|
||||
js-sha1 "^0.6.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
vue-cropper@^0.5.5:
|
||||
version "0.5.5"
|
||||
resolved "https://registry.npmjs.org/vue-cropper/-/vue-cropper-0.5.5.tgz#9bd1ba563c7faa268abd52fb2af4c6c28d33c962"
|
||||
integrity sha512-5mGaBlS1EwLxUFwHHX2Q8zOZSiVfBUjOfolR+ZNKwu7Rh3u+GhwHYOyFkgZHhhoQBBNdyVB28O6W+MpMimhCbA==
|
||||
|
||||
vue-eslint-parser@^7.0.0, vue-eslint-parser@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.1.1.tgz#c43c1c715ff50778b9a7e9a4e16921185f3425d3"
|
||||
|
||||