提交 641f096e 编写于 作者: Z zorro

增加Vlc网页播放器VUE3范例及修改升级提示文字等

上级 64189e55
......@@ -114,7 +114,9 @@
components: {
},
data() {
data()
{
return {
aid: 0, // 第一个办公网页组件实例ID
aid2: 0, // 第二个办公网页组件实例ID
......@@ -148,11 +150,13 @@
result: [] //日志结果数组
}
},
computed: {
DebugLog() {
return this.result.join("\n")
}
},
mounted(){
//初始化配置
this.init()
......@@ -162,13 +166,16 @@
_this.pageResize()
}
},
destroyed(){
window.onresize = null
},
beforeDestroy() {
//关闭所有websocket链接以及浏览器监听
this.close()
},
methods: {
init() {
//监听浏览器切换标签页面
......@@ -185,6 +192,7 @@
//先获取本机Office软件安装信息
this.GetOfficeInfo()
},
windowScroll() {
// 滚动条距离页面顶部的距离
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
......@@ -193,6 +201,7 @@
if (this.aid2 > 0)
this.appScroll(2, this.aid2, scrollTop)
},
appScroll(si, id, scrollTop) {
if(id)
{
......@@ -214,6 +223,7 @@
this.socket[si].sendObj(msg)
}
},
GetAppletPosition() {
//获取网页组件位置节点信息
let nScrollTop = 0
......@@ -243,6 +253,7 @@
this.left = Math.round(react.left) + nScrollLeft
this.top = Math.round(react.top) + nScrollTop
},
handleVisiable(e) {
//浏览器页面切换侦听回调函数
if (e.target.visibilityState == 'hidden') {
......@@ -253,22 +264,26 @@
this.showApp()
}
},
hasVerticalScrollbar(){
if(document.documentElement.clientHeight)
return document.body.scrollHeight > document.documentElement.clientHeight
return document.body.scrollHeight > window.innerHeight
},
hasHorizontalScrollbar(){
if(document.documentElement.clientWidth)
return document.body.scrollWidth > document.documentElement.clientWidth
return document.body.scrollWidth > window.innerWidth
},
pageResize(){
if(this.aid > 0)
this.SendScrollInfo(0,this.aid)
if(this.aid2 > 0)
this.SendScrollInfo(2,this.aid2)
},
SendScrollInfo(si,id){
let nScrollTop = 0
let nScrollLeft = 0
......@@ -314,10 +329,12 @@
console.log(msg)
this.socket[si].sendObj(msg)
},
unloadHandler() {
//关闭所有websoket链接
this.close()
},
close() {
//关闭网页组件实例
this.CloseAllApplet()
......@@ -336,6 +353,7 @@
//关闭侦听滚动条
window.removeEventListener('unload', this.unloadHandler,false)
},
GetOfficeInfo()
{
this.isConnService = true
......@@ -357,6 +375,7 @@
}
this.socket[0].sendObj(msg)
},
StartOfficeApplet() {
//启动第一个办公网页组件
// Web节点中参数可自行配置,目前支持这些参数:
......@@ -389,6 +408,7 @@
}
this.socket[0].sendObj(msg)
},
openSecondApplet() {
if(this.aid)
{
......@@ -437,6 +457,7 @@
this.$message.success('请先启动第一个网页组件')
}
},
CloseSecondApplet() {
if (this.StartSecond) {
this.isDisConnect = true
......@@ -458,6 +479,7 @@
this.resize(0)
}
},
CloseFirstApplet()
{
if (this.aid > 0)
......@@ -484,6 +506,7 @@
this.isDisConnect = false
}
},
AppletFullEdit()
{
this.rid++ // 增加请求序号
......@@ -497,6 +520,7 @@
else
this.socket[1].sendObj(msg)
},
ReLoadFirst()
{
this.CloseFirstApplet()
......@@ -525,6 +549,7 @@
}
this.socket[0].sendObj(Msg)
},
CloseAllApplet()
{
/// 先关闭第二个实例,否则socket中保存的连接序号会不正常
......@@ -679,17 +704,18 @@
if(!this.ReStartLoad && this.isConnService && !this.isDisConnect)
{
//连接不上,认为还没有安装办公网页组件 没有安装时提示安装
this.$confirm('PageHi办公网页组件 尚未安装,是否马上下载', '提示', {
this.$confirm('PageHi办公网页组件 服务端口连接失败,可能是尚未安装,是否马上下载安装', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
window.open('http://local.zorrosoft.com/Files/PageHiOfficeIns.exe') // 建议改为zip等格式下载,解压后安装,EXE格式浏览器会提示是否保留
window.open('http://local.zorrosoft.com/Files/PageHiOfficeIns.exe') // 建议打包为zip等格式下载,解压后安装,EXE文件下载浏览器会提示是否保留
}).catch(() => {
})
}
}
},
resize(position) {
//请求改变网页组件实例显示位置或大小,如不需要改变显示位置,不传X和Y
if(this.aid2 > 0 && this.aid2 == this.curID){
......@@ -772,6 +798,7 @@
else
this.socket[1].sendObj(msg)
},
AddMark()
{
// 请求插入书签
......@@ -786,6 +813,7 @@
else
this.socket[1].sendObj(msg)
},
MarkRePlace()
{
// 请求替换书签内容
......@@ -800,6 +828,7 @@
else
this.socket[1].sendObj(msg)
},
InsertDJSign()
{
// 请求电子签章 Type默认0 支持北京点聚签章系统
......@@ -814,6 +843,7 @@
else
this.socket[1].sendObj(msg)
},
SaveFile()
{
// 请求保存文档
......@@ -828,6 +858,7 @@
else
this.socket[1].sendObj(msg)
},
SaveAsFile()
{
this.rid++ // 增加请求序号
......@@ -847,6 +878,7 @@
else
this.socket[0].sendObj(msg)
},
BeginSaveAsFile(LocalFilePath)
{
// 请求开始另存文档
......@@ -863,6 +895,7 @@
else
this.socket[1].sendObj(msg)
},
ExpportFile()
{
// 请求导出文档
......@@ -883,6 +916,7 @@
else
this.socket[0].sendObj(msg)
},
BeginExpportFile(LocalFilePath)
{
// 请求开始另存文档
......@@ -899,6 +933,7 @@
else
this.socket[1].sendObj(msg)
},
InsertImg()
{
// 请求当前光标位置插入图片 先让用户选择图片文件
......@@ -919,6 +954,7 @@
else
this.socket[0].sendObj(msg)
},
BeginInsertImg(LocalFilePath)
{
// Save为1时自动保存文档
......@@ -937,6 +973,7 @@
else
this.socket[1].sendObj(msg)
},
GetFirstImg()
{
// 提取文档中页码序号Index的图片,先设置图片保存位置
......@@ -956,6 +993,7 @@
else
this.socket[0].sendObj(msg)
},
BeginGetFirstImg(LocalFilePath)
{
// 提取文档中序号Index的Base64编码数据,如指定本地保存文件名File,则保存到本地文件中
......@@ -987,6 +1025,7 @@
else
this.socket[1].sendObj(msg)
},
ConvertFirstPage()
{
// 转换文档中序号为Index页码内容成图片,先设置转换图片存放位置
......@@ -1006,6 +1045,7 @@
else
this.socket[0].sendObj(msg)
},
BeginConvertFirstPage(LocalFilePath)
{
// 转换文档中页码序号Index的Base64编码数据,如指定本地保存文件名File,则保存到本地文件中
......@@ -1037,6 +1077,7 @@
else
this.socket[1].sendObj(msg)
},
PrintFile()
{
// 请求打印当前文档
......@@ -1054,6 +1095,7 @@
else
this.socket[1].sendObj(msg)
},
EnableRevision()
{
// 请求留痕,就是修订模式
......@@ -1071,6 +1113,7 @@
this.socket[1].sendObj(msg)
IsRevision = true
},
DisableRevision()
{
// 关闭留痕,就是关闭修订模式
......@@ -1088,6 +1131,7 @@
this.socket[1].sendObj(msg)
IsRevision = false
},
ShowRevision()
{
// 显示留痕信息,就是显示修订内容
......@@ -1104,6 +1148,7 @@
else
this.socket[1].sendObj(msg)
},
AcceptRevision()
{
// 接受留痕,就是接受修订内容
......@@ -1120,6 +1165,7 @@
else
this.socket[1].sendObj(msg)
},
changeOpen() {
//重新打开文档
this.rid++ // 增加请求序号
......@@ -1135,6 +1181,7 @@
else
this.socket[1].sendObj(msg)
},
showApp() {
//显示办公网页组件
if (this.aid > 0) {
......@@ -1163,6 +1210,7 @@
this.socket[2].sendObj(msg)
}
},
hideApp(code) {
//隐藏办公网页组件 Code设置4是自动隐藏,如需强制隐藏,设置为32
if (this.aid > 0)
......@@ -1193,6 +1241,7 @@
this.socket[2].sendObj(msg)
}
},
CheckUpdate() {
//校验中间件版本是不是需要升级,如果额外指定PID参数,代表校验PID代表的网页组件,Wrl_Version功能多
this.rid++ // 增加请求序号
......@@ -1205,6 +1254,7 @@
}
this.socket[0].sendObj(msg)
},
SendUpdateJson() {
// 发送中间件的升级命令,实现自动升级,同时升级微软及金山办公等网页组件
// 注意:Wrl_Update中的请求参数如MD5 TK Size等,请根据文档“中间件制作升级包说明.pdf”中的打包工具生成
......
......@@ -144,9 +144,11 @@ let isConnService = false //是否连接的中间件服务
let isDisConnect = false // 是否处于断开连接过程
let socket =[] //websocket对象数组,采用数组需要注意维护索引号,建议改为根据连接的SID参数存到对应的集合,需要用到连接时根据SID提取
let result = reactive([]) //日志结果数组
const DebugLog = computed(() => {
return result.join("\n")
})
onMounted(()=>{
//初始化配置
init()
......@@ -155,11 +157,13 @@ onMounted(()=>{
pageResize()
}
})
onUnmounted(()=>{
//关闭所有websocket链接以及浏览器监听
close()
window.onresize = null
})
function init() {
//监听浏览器切换标签页面
if(document.addEventListener)
......@@ -175,6 +179,7 @@ function init() {
//先获取本机Office软件安装信息
GetOfficeInfo()
}
function windowScroll() {
// 滚动条距离页面顶部的距离
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
......@@ -183,6 +188,7 @@ function windowScroll() {
if (aid2 > 0)
appScroll(2, aid2, scrollTop)
}
function appScroll(si, id, scrollTop) {
if(id)
{
......@@ -204,6 +210,7 @@ function appScroll(si, id, scrollTop) {
socket[si].sendObj(msg)
}
}
function GetAppletPosition() {
//获取网页组件位置节点信息
let nScrollTop = 0
......@@ -236,6 +243,7 @@ function GetAppletPosition() {
width.value = Math.round(react.width)
height.value = Math.round(react.height)
}
function handleVisiable(e) {
//浏览器页面切换侦听回调函数
if (e.target.visibilityState == 'hidden') {
......@@ -246,22 +254,26 @@ function handleVisiable(e) {
showApp()
}
}
function hasVerticalScrollbar(){
if(document.documentElement.clientHeight)
return document.body.scrollHeight > document.documentElement.clientHeight
return document.body.scrollHeight > window.innerHeight
}
function hasHorizontalScrollbar(){
if(document.documentElement.clientWidth)
return document.body.scrollWidth > document.documentElement.clientWidth
return document.body.scrollWidth > window.innerWidth
}
function pageResize(){
if(aid > 0)
SendScrollInfo(0,aid)
if(aid2 > 0)
SendScrollInfo(2,aid2)
}
function SendScrollInfo(si,id){
let nScrollTop = 0
let nScrollLeft = 0
......@@ -307,10 +319,12 @@ function SendScrollInfo(si,id){
console.log(msg)
socket[si].sendObj(msg)
}
function unloadHandler() {
//关闭所有websoket链接
close()
}
function close() {
//关闭网页组件实例
CloseAllApplet()
......@@ -329,6 +343,7 @@ function close() {
//关闭侦听滚动条
window.removeEventListener('unload', unloadHandler,false)
}
function GetOfficeInfo()
{
isConnService = true
......@@ -349,8 +364,8 @@ function GetOfficeInfo()
"para": {}
}
socket[0].sendObj(msg)
}
function StartOfficeApplet() {
//启动第一个办公网页组件
// Web节点中参数可自行配置,目前支持这些参数:
......@@ -384,6 +399,7 @@ function StartOfficeApplet() {
console.log(msg)
socket[0].sendObj(msg)
}
function openSecondApplet() {
if(aid)
{
......@@ -432,6 +448,7 @@ function openSecondApplet() {
ElMessage('请先启动第一个网页组件')
}
}
function CloseSecondApplet() {
if (StartSecond.value) {
isDisConnect = true
......@@ -453,6 +470,7 @@ function CloseSecondApplet() {
resize(0)
}
}
function CloseFirstApplet()
{
if (aid > 0)
......@@ -479,6 +497,7 @@ function CloseFirstApplet()
isDisConnect = false
}
}
function AppletFullEdit()
{
rid++ // 增加请求序号
......@@ -492,6 +511,7 @@ function AppletFullEdit()
else
socket[1].sendObj(msg)
}
function ReLoadFirst()
{
CloseFirstApplet()
......@@ -520,6 +540,7 @@ function ReLoadFirst()
}
socket[0].sendObj(Msg)
}
function CloseAllApplet()
{
/// 先关闭第二个实例,否则socket中保存的连接序号会不正常
......@@ -675,18 +696,19 @@ function openWebsocket(port,type) {
// 避免IE中点击重复播放及firefox断开连接提示等问题
if(!ReStartLoad && isConnService && !isDisConnect)
{
//连接不上,认为还没有安装办公网页组件 没有安装时提示安装
ElMessageBox.confirm('PageHi办公网页组件 尚未安装,是否马上下载', '提示', {
// 连接不上,认为还没有安装PageHiOffice 没有安装时提示安装
ElMessageBox.confirm('PageHiOffice-佐罗软件Office网页组件 服务端口连接失败,可能是尚未安装,是否马上下载安装', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
window.open('http://local.zorrosoft.com/Files/PageHiOfficeIns.exe') // 建议改为zip等格式下载,解压后安装,EXE格式浏览器会提示是否保留
window.open('http://local.zorrosoft.com/Files/PageHiOfficeIns.exe') // 建议打包为zip等格式下载,解压后安装,EXE文件下载浏览器会提示是否保留
}).catch(() => {
})
}
}
}
function resize(position) {
//请求改变网页组件实例显示位置或大小,如不需要改变显示位置,不传X和Y
if(aid2 > 0 && aid2 == curID){
......@@ -730,7 +752,7 @@ function resize(position) {
"ID": aid,
"X": left.value,
"Y": top.value,
"Width": width,
"Width": width.value,
"Height": height.value
}
}
......@@ -751,6 +773,7 @@ function resize(position) {
}
}
}
function InsertContent()
{
// 请求插入指定的文字或链接
......@@ -768,6 +791,7 @@ function InsertContent()
else
socket[1].sendObj(msg)
}
function AddMark()
{
// 请求插入书签
......@@ -782,6 +806,7 @@ function AddMark()
else
socket[1].sendObj(msg)
}
function MarkRePlace()
{
// 请求替换书签内容
......@@ -796,6 +821,7 @@ function MarkRePlace()
else
socket[1].sendObj(msg)
}
function InsertDJSign()
{
// 请求电子签章 Type默认0 支持北京点聚签章系统
......@@ -810,6 +836,7 @@ function InsertDJSign()
else
socket[1].sendObj(msg)
}
function SaveFile()
{
// 请求保存文档
......@@ -824,6 +851,7 @@ function SaveFile()
else
socket[1].sendObj(msg)
}
function SaveAsFile()
{
rid++ // 增加请求序号
......@@ -843,6 +871,7 @@ function SaveAsFile()
else
socket[0].sendObj(msg)
}
function BeginSaveAsFile(LocalFilePath)
{
// 请求开始另存文档
......@@ -859,6 +888,7 @@ function BeginSaveAsFile(LocalFilePath)
else
socket[1].sendObj(msg)
}
function ExpportFile()
{
// 请求导出文档
......@@ -879,6 +909,7 @@ function ExpportFile()
else
socket[0].sendObj(msg)
}
function BeginExpportFile(LocalFilePath)
{
// 请求开始另存文档
......@@ -895,6 +926,7 @@ function BeginExpportFile(LocalFilePath)
else
socket[1].sendObj(msg)
}
function InsertImg()
{
// 请求当前光标位置插入图片 先让用户选择图片文件
......@@ -915,6 +947,7 @@ function InsertImg()
else
socket[0].sendObj(msg)
}
function BeginInsertImg(LocalFilePath)
{
// Save为1时自动保存文档
......@@ -933,6 +966,7 @@ function BeginInsertImg(LocalFilePath)
else
socket[1].sendObj(msg)
}
function GetFirstImg()
{
// 提取文档中页码序号Index的图片,先设置图片保存位置
......@@ -952,6 +986,7 @@ function GetFirstImg()
else
socket[0].sendObj(msg)
}
function BeginGetFirstImg(LocalFilePath)
{
// 提取文档中序号Index的Base64编码数据,如指定本地保存文件名File,则保存到本地文件中
......@@ -983,6 +1018,7 @@ function BeginGetFirstImg(LocalFilePath)
else
socket[1].sendObj(msg)
}
function ConvertFirstPage()
{
// 转换文档中序号为Index页码内容成图片,先设置转换图片存放位置
......@@ -1002,6 +1038,7 @@ function ConvertFirstPage()
else
socket[0].sendObj(msg)
}
function BeginConvertFirstPage(LocalFilePath)
{
// 转换文档中页码序号Index的Base64编码数据,如指定本地保存文件名File,则保存到本地文件中
......@@ -1033,6 +1070,7 @@ function BeginConvertFirstPage(LocalFilePath)
else
socket[1].sendObj(msg)
}
function PrintFile()
{
// 请求打印当前文档
......@@ -1050,6 +1088,7 @@ function PrintFile()
else
socket[1].sendObj(msg)
}
function EnableRevision()
{
// 请求留痕,就是修订模式
......@@ -1067,6 +1106,7 @@ function EnableRevision()
socket[1].sendObj(msg)
IsRevision.value = true
}
function DisableRevision()
{
// 关闭留痕,就是关闭修订模式
......@@ -1084,6 +1124,7 @@ function DisableRevision()
socket[1].sendObj(msg)
IsRevision.value = false
}
function ShowRevision()
{
// 显示留痕信息,就是显示修订内容
......@@ -1100,6 +1141,7 @@ function ShowRevision()
else
socket[1].sendObj(msg)
}
function AcceptRevision()
{
// 接受留痕,就是接受修订内容
......@@ -1116,6 +1158,7 @@ function AcceptRevision()
else
socket[1].sendObj(msg)
}
function changeOpen() {
//重新打开文档
rid++ // 增加请求序号
......@@ -1131,6 +1174,7 @@ function changeOpen() {
else
socket[1].sendObj(msg)
}
function showApp() {
//显示办公网页组件
if (aid > 0) {
......@@ -1159,6 +1203,7 @@ function showApp() {
socket[2].sendObj(msg)
}
}
function hideApp(code) {
//隐藏办公网页组件 Code设置4是自动隐藏,如需强制隐藏,设置为32
if (aid > 0)
......@@ -1189,6 +1234,7 @@ function hideApp(code) {
socket[2].sendObj(msg)
}
}
function CheckUpdate() {
//校验中间件版本是不是需要升级,如果额外指定PID参数,代表校验PID代表的网页组件,Wrl_Version功能多
rid++ // 增加请求序号
......@@ -1201,6 +1247,7 @@ function CheckUpdate() {
}
socket[0].sendObj(msg)
}
function SendUpdateJson() {
// 发送中间件的升级命令,实现自动升级,同时升级微软及金山办公等网页组件
// 注意:Wrl_Update中的请求参数如MD5 TK Size等,请根据文档“中间件制作升级包说明.pdf”中的打包工具生成,此处举例的升级包是在线公测版的,正式版需要自己制作
......@@ -1225,6 +1272,7 @@ function SendUpdateJson() {
}
socket[0].sendObj(msg)
}
</script>
<style scoped>
.mainContainer {
......
......@@ -186,11 +186,14 @@
import GetDefaultConn from "../common/base.js"
//加载websoket类
import websocket from '../common/websocket'
export default {
components: {
},
data() {
data()
{
return {
aid: 0, // 第一个播放实例实例ID
aid2: 0, // 保存启动的第二个播放实例ID
......@@ -234,11 +237,13 @@
version: '2.2.12.3' //版本信息
}
},
computed: {
DebugLog() {
return this.result.join("\n")
}
},
mounted(){
//初始化配置
this.init()
......@@ -248,14 +253,18 @@
_this.pageResize()
}
},
destroyed(){
window.onresize = null
},
beforeDestroy() {
//关闭所有websocket链接以及浏览器监听
this.close()
},
methods: {
methods:
{
init() {
//监听浏览器切换标签页面
if(document.addEventListener)
......@@ -272,6 +281,7 @@
this.StartRtspPlayer()
},
windowScroll() {
// 滚动条距离页面顶部的距离
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
......@@ -280,6 +290,7 @@
if (this.aid2 > 0)
this.appScroll(2, this.aid2, scrollTop)
},
appScroll(si, id, scrollTop) {
if(id)
{
......@@ -301,6 +312,7 @@
this.socket[si].sendObj(msg)
}
},
getPlayerPosition() {
//获取播放器位置节点信息
let nScrollTop = 0
......@@ -330,6 +342,7 @@
this.left = Math.round(react.left) + nScrollLeft
this.top = Math.round(react.top) + nScrollTop
},
handleVisiable(e) {
//浏览器页面切换侦听回调函数
if (e.target.visibilityState == 'hidden') {
......@@ -340,22 +353,26 @@
this.showApp()
}
},
hasVerticalScrollbar(){
if(document.documentElement.clientHeight)
return document.body.scrollHeight > document.documentElement.clientHeight
return document.body.scrollHeight > window.innerHeight
},
hasHorizontalScrollbar(){
if(document.documentElement.clientWidth)
return document.body.scrollWidth > document.documentElement.clientWidth
return document.body.scrollWidth > window.innerWidth
},
pageResize(){
if(this.aid > 0)
this.SendScrollInfo(0,this.aid)
if(this.aid2 > 0)
this.SendScrollInfo(2,this.aid2)
},
SendScrollInfo(si,id){
let nScrollTop = 0
let nScrollLeft = 0
......@@ -401,10 +418,12 @@
console.log(msg)
this.socket[si].sendObj(msg)
},
unloadHandler() {
//关闭所有websoket链接
this.close()
},
close() {
//关闭播放器
this.CloseAllPlayer()
......@@ -423,6 +442,7 @@
//关闭侦听滚动条
window.removeEventListener('unload', this.unloadHandler,false)
},
StartRtspPlayer() {
this.isConnService = true
//启动第一个多引擎网页小程序 Open为播放源,播放源也可以放到Web节点中,参考RePlayFirst中实现,差异在于,Open中指定对所有分屏有效,而在Web节点中可指定更多播放参数
......@@ -461,6 +481,7 @@
}
this.socket[0].sendObj(msg)
},
openSecondPlayer() {
if(this.aid)
{
......@@ -508,6 +529,7 @@
this.$message.success('请先启动第一个播放器')
}
},
CloseSecondPlayer() {
if (this.StartSecond) {
this.isDisConnect = true
......@@ -529,6 +551,7 @@
this.resize(0)
}
},
CloseFirstPlayer()
{
if (this.aid > 0)
......@@ -555,6 +578,7 @@
this.isDisConnect = false
}
},
RePlayFirst()
{
this.CloseFirstPlayer()
......@@ -584,12 +608,14 @@
}
this.socket[0].sendObj(Msg)
},
CloseAllPlayer()
{
/// 先关闭第二个实例,否则socket连接序号会错
this.CloseSecondPlayer()
this.CloseFirstPlayer()
},
StopSecondPlayer()
{
if (this.StartSecond)
......@@ -616,6 +642,7 @@
this.socket[3].sendObj(msg)
}
},
StopFirstPlayer()
{
if (this.aid > 0)
......@@ -657,11 +684,13 @@
this.socket[1].sendObj(msg)
}
},
StopAllPlay()
{
this.StopSecondPlayer()
this.StopFirstPlayer()
},
openWebsocket(port,type) {
//打开websocket服务
let ws = GetDefaultConn(port,type)
......@@ -791,18 +820,19 @@
if(!this.ReStartPlay && this.isConnService && !this.isDisConnect)
{
//连接不上,认为还没有安装VLC网页播放程序 没有安装时提示安装
this.$confirm('RTSP多引擎网页播放器 尚未安装,是否马上下载', '提示', {
this.$confirm('RTSP多引擎网页播放器 服务端口连接失败,可能尚未安装,是否马上下载安装', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
window.open('http://local.zorrosoft.com/Files/RtspWebPlayerIns.exe') // 建议改为zip等格式下载,解压后安装,EXE格式浏览器会提示是否保留
window.open('http://local.zorrosoft.com/Files/RtspWebPlayerIns.exe') // 建议打包为zip等格式下载,解压后安装,EXE文件下载浏览器会提示是否保留
}).catch(() => {
})
}
}
},
resize(position) {
//请求改变播放实例小程序显示位置或大小,如不需要改变显示位置,不传X和Y
if(this.aid2 > 0 && this.aid2 == this.curID){
......@@ -867,6 +897,7 @@
}
}
},
getCapture() {
//请求截图,固定请求ID处理,也可以是建立一个map记录每个rid对应的含义,请求返回里再处理
let msg = {
......@@ -888,6 +919,7 @@
else
this.socket[1].sendObj(msg)
},
setText() {
//发送字幕到指定的窗口ID
this.rid++ // 增加请求序号
......@@ -912,6 +944,7 @@
else
this.socket[1].sendObj(msg)
},
setType() {
//动态改变屏幕数量
this.rid++ // 增加请求序号
......@@ -928,6 +961,7 @@
else
this.socket[1].sendObj(msg)
},
NextFrame()
{
//请求显示下一帧 请求后播放进入暂停状态,可继续请求下一帧,或请求播放恢复正常播放状态
......@@ -946,6 +980,7 @@
else
this.socket[1].sendObj(msg)
},
PlayPause()
{
//请求播放进入暂停状态
......@@ -964,6 +999,7 @@
else
this.socket[1].sendObj(msg)
},
PlayContinue()
{
//请求播放继续,或请求播放恢复正常播放状态
......@@ -982,6 +1018,7 @@
else
this.socket[1].sendObj(msg)
},
PlayMute()
{
// 设置选中分屏窗口视频静音,1静音 0不静音
......@@ -1000,6 +1037,7 @@
else
this.socket[1].sendObj(msg)
},
PlaySpeed()
{
// 调整播放速度,仅限于文件或回放流,支持批量操作,当前演示针对选中分屏窗口设置2倍数播放
......@@ -1018,6 +1056,7 @@
else
this.socket[1].sendObj(msg)
},
PlayFullScreen()
{
// 设置选中分屏窗口全屏显示 全屏后按ESC 字幕F\、双击或点击工具栏全屏图标可退出全屏状态
......@@ -1035,6 +1074,7 @@
else
this.socket[1].sendObj(msg)
},
SetSelWnd() {
//设置选中分屏子窗口
this.rid++ // 增加请求序号
......@@ -1054,6 +1094,7 @@
else
this.socket[1].sendObj(msg)
},
AppletSnap() {
// 整个播放窗口抓图,包含所有分屏子窗口,发送到中间件侦听端口执行
this.rid++ // 增加请求序号
......@@ -1072,6 +1113,7 @@
else
this.socket[0].sendObj(msg)
},
FullApplet() {
// 整个播放窗口全屏,包含所有分屏子窗口,发送到中间件侦听端口执行
this.rid++ // 增加请求序号
......@@ -1089,6 +1131,7 @@
else
this.socket[0].sendObj(msg)
},
RecordToFile() {
//对指定的窗口ID进行录像 固定请求ID处理,也可以是建立一个map记录每个rid对应的含义,请求返回里再处理
if (this.isRecordFile) {
......@@ -1122,6 +1165,7 @@
this.socket[0].sendObj(msg)
}
},
BeginRecordFile(LocalFilePath)
{
let msg = {
......@@ -1141,6 +1185,7 @@
else
this.socket[1].sendObj(msg)
},
watermask() {
//对指定的窗口ID发送水印,只支持VLC引擎播放,其它引擎播放,可采用叠加透明网页的方式,参考FloatWebInfo的实现
this.rid++ // 增加请求序号
......@@ -1162,6 +1207,7 @@
else
this.socket[1].sendObj(msg)
},
changePlay() {
//动态改变指定的窗口ID的播放源,Player_ChangePlay本身支持同时改变多个分屏窗口播放源,Play支持传数组 ForceDestroy指定是否先销毁原来播放引擎 默认不销毁
this.rid++ // 增加请求序号
......@@ -1257,6 +1303,7 @@
else
this.socket[1].sendObj(msg)
},
FloatWebInfo() {
//对指定的窗口ID叠加网页内容显示
this.rid++ // 增加请求序号
......@@ -1274,6 +1321,7 @@
else
this.socket[1].sendObj(msg)
},
showApp() {
//显示VLC小程序
if (this.aid > 0) {
......@@ -1302,6 +1350,7 @@
this.socket[2].sendObj(msg)
}
},
hideApp(code) {
//隐藏VLC小程序 Code设置4是自动隐藏,如需强制隐藏,设置为32
if (this.aid > 0)
......@@ -1332,6 +1381,7 @@
this.socket[2].sendObj(msg)
}
},
CheckUpdate() {
//校验中间件版本是不是需要升级,如果额外指定PID参数,代表校验PID代表的小程序,Wrl_Version功能多
this.rid++ // 增加请求序号
......@@ -1344,6 +1394,7 @@
}
this.socket[0].sendObj(msg)
},
SendUpdateJson() {
// 发送中间件的升级命令,实现自动升级,同时升级海康及VLC网页播放等小程序
// 注意:Wrl_Update中的请求参数如MD5 TK Size等,请根据文档“中间件制作升级包说明.pdf”中的打包工具生成,此处举例的升级包是在线公测版的,正式版需要自己制作
......
......@@ -182,7 +182,7 @@
//集成开发说明:所有交互都是通过JS建立websocket连接后,发送或接收JSON包进行,都是异步请求,发送和接收包都携带rid用来标识请求返回包对应是哪个请求的结果
//所有发送的msg中用到的长整形rid 可以自己指定 不同的发送请求定义唯一的rid 这样可以在回调用轻松判断
//针对一些特殊请求,可以根据自己的实际情况把rid固定,如启动网页播放器时指定固定rid获得启动网页播放器的实例ID
//首先建立第一个websocket连接到中间件服务端口启动RTSP网页播放器 然后获得启动RTSP网页播放器的侦听端口建立第二个websocket连接 主要用来进行控制保存文档、提取图片、切换打开文档等操作
//首先建立第一个websocket连接到中间件服务端口启动RTSP多引擎网页播放器,然后获得启动的网页播放器的侦听端口建立第二个websocket连接,主要用来进行控制播放、抓图、录像等操作
//释放网页播放器,直接关闭建立的第一个websocket连接即可
import GetDefaultConn from "./common/base.js"
//加载websoket类
......@@ -209,7 +209,7 @@ let SetPlayerEngine = ref('LibVLC')
let Transform = ref('none')
let recordid = 0 //录像时返回的录像进程PID
let isRecordFile = false //记录录像的状态
let danmu = ref({
let danmu = {
//需要发送的字幕配置信息
text: '您好呀,O(∩_∩)O哈哈~',
position: 'TOP',
......@@ -218,7 +218,7 @@ let danmu = ref({
size: 50,
x: 0,
y: 0
})
}
let StartSecond = ref(0) // 是否启动了第二个网页播放器实例
let ReStartLoad = ref(0) // 第一个网页播放器实例是否重新打开状态
let width = ref(960) // 网页播放器窗口显示宽度
......@@ -235,6 +235,7 @@ let result = reactive([]) //日志结果数组
const DebugLog = computed(() => {
return result.join("\n")
})
onMounted(()=>{
//初始化配置
init()
......@@ -243,12 +244,15 @@ onMounted(()=>{
pageResize()
}
})
onUnmounted(()=>{
//关闭所有websocket链接以及浏览器监听
close()
window.onresize = null
})
function init() {
function init()
{
//监听浏览器切换标签页面
if(document.addEventListener)
document.addEventListener('visibilitychange', handleVisiable,false)
......@@ -263,7 +267,9 @@ function init() {
//先获取本机VLC软件安装信息
GetPlayerInfo()
}
function windowScroll() {
function windowScroll()
{
// 滚动条距离页面顶部的距离
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
if(aid > 0)
......@@ -271,7 +277,9 @@ function windowScroll() {
if (aid2 > 0)
appScroll(2, aid2, scrollTop)
}
function appScroll(si, id, scrollTop) {
function appScroll(si, id, scrollTop)
{
if(id)
{
// 默认纵向滚动网页播放器实例,如需要横向滚动,Code设置为1,修改Left的值
......@@ -292,7 +300,9 @@ function appScroll(si, id, scrollTop) {
socket[si].sendObj(msg)
}
}
function GetAppletPosition() {
function GetAppletPosition()
{
//获取网页播放器位置节点信息
let nScrollTop = 0
let nScrollLeft = 0
......@@ -324,7 +334,9 @@ function GetAppletPosition() {
width.value = Math.round(react.width)
height.value = Math.round(react.height)
}
function handleVisiable(e) {
function handleVisiable(e)
{
//浏览器页面切换侦听回调函数
if (e.target.visibilityState == 'hidden') {
//切离该页面时执行 当前页自己实现标签切换或需要在RTSP网页播放器程序窗口区域临时显示内容时,设置32强制隐藏
......@@ -334,23 +346,31 @@ function handleVisiable(e) {
showApp()
}
}
function hasVerticalScrollbar(){
function hasVerticalScrollbar()
{
if(document.documentElement.clientHeight)
return document.body.scrollHeight > document.documentElement.clientHeight
return document.body.scrollHeight > window.innerHeight
}
function hasHorizontalScrollbar(){
function hasHorizontalScrollbar()
{
if(document.documentElement.clientWidth)
return document.body.scrollWidth > document.documentElement.clientWidth
return document.body.scrollWidth > window.innerWidth
}
function pageResize(){
function pageResize()
{
if(aid > 0)
SendScrollInfo(0,aid)
if(aid2 > 0)
SendScrollInfo(2,aid2)
}
function SendScrollInfo(si,id){
function SendScrollInfo(si,id)
{
let nScrollTop = 0
let nScrollLeft = 0
let BarCode = 0
......@@ -395,13 +415,17 @@ function SendScrollInfo(si,id){
console.log(msg)
socket[si].sendObj(msg)
}
function unloadHandler() {
function unloadHandler()
{
//关闭所有websoket链接
close()
}
function close() {
function close()
{
//关闭网页播放器实例
CloseAllApplet()
CloseAllPlayer()
isDisConnect = true
//关闭第一个websoket链接
socket[0].disconnect()
......@@ -417,6 +441,7 @@ function close() {
//关闭侦听滚动条
window.removeEventListener('unload', unloadHandler,false)
}
function GetPlayerInfo()
{
isConnService = true
......@@ -439,9 +464,10 @@ function GetPlayerInfo()
"para": {}
}
socket[0].sendObj(msg)
}
function StartPlayerApplet() {
function StartPlayerApplet()
{
//启动第一个多引擎网页播放器 Open为播放源,播放源也可以放到Web节点中,参考RePlayFirst中实现,差异在于,Open中指定对所有分屏有效,而在Web节点中可指定更多播放参数
let msg = {
"req": "Wrl_VideoWebPlayer",
......@@ -467,7 +493,9 @@ function StartPlayerApplet() {
console.log(msg)
socket[0].sendObj(msg)
}
function openSecondApplet() {
function openSecondPlayer()
{
if(aid)
{
//演示加载第二个RTSP网页播放器实例打开表格程序 先改变第一个网页播放器位置 Flag值需要在原来基础上+512以支持多实例加载
......@@ -515,7 +543,9 @@ function openSecondApplet() {
ElMessage('请先启动第一个网页播放器')
}
}
function CloseSecondApplet() {
function CloseSecondPlayer()
{
if (StartSecond.value) {
isDisConnect = true
/// 每个网页播放器实例占用2个连接,一个到中间件、一个到网页播放器,分别断开连接并释放
......@@ -536,7 +566,8 @@ function CloseSecondApplet() {
resize(0)
}
}
function CloseFirstApplet()
function CloseFirstPlayer()
{
if (aid > 0)
{
......@@ -554,7 +585,7 @@ function CloseFirstApplet()
socket[1].disconnect()
socket[0].sendObj(msg)
aid = 0
ReStartLoad = 0
ReStartLoad.value = 0
if(StartSecond.value)
socket.splice(1,1)
else
......@@ -562,44 +593,47 @@ function CloseFirstApplet()
isDisConnect = false
}
}
function RePlayFirst()
{
CloseFirstApplet()
ReStartLoad = 1
CloseFirstPlayer()
ReStartLoad.value = 1
//重新启动播,Web节点配置播放源,设置分屏风格4,播放引擎从VLC改为FFPlayer
ShowType = 4
let Msg = {
"req": "Wrl_VideoWebPlayer",
"rid": run1,
"rid": RunFirst,
"para": {
"Type": "0",
"Title": "RTSP多引擎网页播放器",
"Version": 0,
"Flag": 578,
"Left": left,
"Top": top,
"Width": width,
"Height": height,
"IframeX": IframeX,
"IframeY": IframeY,
"BarW": 0,
"BarH": 0,
"ScrollTop": 0,
"Web": JSON.parse(WebCfg2.value),
"ShowType": ShowType
"Type": "0",
"Title": "RTSP多引擎网页播放器",
"Version": 0,
"Flag": 578,
"Left": left.value,
"Top": top.value,
"Width": width.value,
"Height": height.value,
"IframeX": IframeX,
"IframeY": IframeY,
"BarW": 0,
"BarH": 0,
"ScrollTop": 0,
"Web": JSON.parse(WebCfg2.value),
"ShowType": ShowType
}
}
socket[0].sendObj(Msg)
}
function CloseAllApplet()
function CloseAllPlayer()
{
/// 先关闭第二个实例,否则socket中保存的连接序号会不正常
CloseSecondApplet()
CloseFirstApplet()
CloseSecondPlayer()
CloseFirstPlayer()
}
function StopSecondPlayer()
{
if (StartSecond)
if (StartSecond.value)
{
rid++ // 增加序号
let msg = {
......@@ -623,6 +657,7 @@ function StopSecondPlayer()
socket[3].sendObj(msg)
}
}
function StopFirstPlayer()
{
if (aid > 0)
......@@ -632,7 +667,7 @@ function StopFirstPlayer()
/// ID是分屏窗口序号,从1开始 序号原则是从左向右开始编号,然后从上到下开始顺序编号,如遇右侧多层排列窗口时,直到右侧窗口序号排序完成
/// Type可以是这些值:Pause,Play,Stop,StopAsync,Next,Prev,Clear,TogglePause 分别对应暂停播放 播放 停止播放 异步停止播放 下一个播放源 上一个播放源 清理播放列表 切换暂停状态
let msg = {}
if(ReStartPlay)
if(ReStartLoad.value)
{
msg = {
"req": "Player_Control",
......@@ -664,12 +699,15 @@ function StopFirstPlayer()
socket[1].sendObj(msg)
}
}
function StopAllPlay()
{
StopSecondPlayer()
StopFirstPlayer()
}
function openWebsocket(port,type) {
function openWebsocket(port,type)
{
//打开websocket服务
let ws = GetDefaultConn(port,type)
const socketClient = new websocket(ws, {
......@@ -677,15 +715,15 @@ function openWebsocket(port,type) {
})
socketClient.connect()
if(1 == ReStartLoad && StartSecond.value)
if(1 == ReStartLoad.value && StartSecond.value)
{
ReStartLoad = 2
ReStartLoad.value = 2
socket.splice(1,0,socketClient) // 重新加载时已经启动第二个实例,采用插入方式,避免WS下标错误,确保第一个实例的WS连接下标是0和1,第二个实例是3和4
}
else
{
if(1 == ReStartLoad)
ReStartLoad = 2
if(1 == ReStartLoad.value)
ReStartLoad.value = 2
socket.push(socketClient)
}
socketClient.onMessage = (msg) =>
......@@ -793,7 +831,7 @@ function openWebsocket(port,type) {
type: 'warning'
}).then(() => {
/// 先提前关闭网页播放器
CloseAllApplet()
CloseAllPlayer()
SendUpdateJson()
}).catch(() => {
showApp()
......@@ -811,10 +849,10 @@ function openWebsocket(port,type) {
}
socketClient.onError = (msg) => {
// 避免IE中点击重复播放及firefox断开连接提示等问题
if(!ReStartLoad && isConnService && !isDisConnect)
if(!ReStartLoad.value && isConnService && !isDisConnect)
{
//连接不上,认为还没有安装 PageHiPlayer-RTSP多引擎网页播放器 没有安装时提示安装
ElMessageBox.confirm('RTSP多引擎网页播放器 尚未安装,是否马上下载安装?', '提示', {
ElMessageBox.confirm('PageHiPlayer-RTSP多引擎网页播放器 服务端口连接失败,可能是尚未安装,是否马上下载安装?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
......@@ -825,6 +863,7 @@ function openWebsocket(port,type) {
}
}
}
function resize(position) {
//请求改变网页播放器实例显示位置或大小,如不需要改变显示位置,不传X和Y
if(aid2 > 0 && aid2 == curID){
......@@ -868,7 +907,7 @@ function resize(position) {
"ID": aid,
"X": left.value,
"Y": top.value,
"Width": width,
"Width": width.value,
"Height": height.value
}
}
......@@ -889,6 +928,7 @@ function resize(position) {
}
}
}
function getCapture()
{
//请求截图,固定请求ID处理,也可以是建立一个map记录每个rid对应的含义,请求返回里再处理
......@@ -910,6 +950,7 @@ function getCapture()
else
socket[1].sendObj(msg)
}
function setText()
{
//发送字幕到指定的窗口ID
......@@ -935,6 +976,7 @@ function setText()
else
socket[1].sendObj(msg)
}
function setType()
{
//动态改变屏幕数量
......@@ -951,6 +993,7 @@ function setType()
else
socket[1].sendObj(msg)
}
function NextFrame()
{
//请求显示下一帧 请求后播放进入暂停状态,可继续请求下一帧,或请求播放恢复正常播放状态
......@@ -969,6 +1012,7 @@ function NextFrame()
else
socket[1].sendObj(msg)
}
function PlayPause()
{
//请求播放进入暂停状态
......@@ -987,6 +1031,7 @@ function PlayPause()
else
socket[1].sendObj(msg)
}
function PlayContinue()
{
//请求播放继续,或请求播放恢复正常播放状态
......@@ -1005,6 +1050,7 @@ function PlayContinue()
else
socket[1].sendObj(msg)
}
function PlayMute()
{
// 设置选中分屏窗口视频静音,1静音 0不静音
......@@ -1023,6 +1069,7 @@ function PlayMute()
else
socket[1].sendObj(msg)
}
function PlaySpeed()
{
// 调整播放速度,仅限于文件或回放流,支持批量操作,当前演示针对选中分屏窗口设置2倍数播放
......@@ -1040,6 +1087,7 @@ function PlaySpeed()
else
socket[1].sendObj(msg)
}
function PlayFullScreen()
{
// 设置选中分屏窗口全屏显示 全屏后按ESC 字幕F\、双击或点击工具栏全屏图标可退出全屏状态
......@@ -1057,6 +1105,7 @@ function PlayFullScreen()
else
socket[1].sendObj(msg)
}
function SetSelWnd() {
//设置选中分屏子窗口
rid++ // 增加请求序号
......@@ -1076,6 +1125,7 @@ function SetSelWnd() {
else
socket[1].sendObj(msg)
}
function AppletSnap()
{
// 整个播放窗口抓图,包含所有分屏子窗口,发送到中间件侦听端口执行
......@@ -1095,6 +1145,7 @@ function AppletSnap()
else
socket[0].sendObj(msg)
}
function FullApplet()
{
// 整个播放窗口全屏,包含所有分屏子窗口,发送到中间件侦听端口执行
......@@ -1113,6 +1164,7 @@ function FullApplet()
else
socket[0].sendObj(msg)
}
function RecordToFile()
{
//对指定的窗口ID进行录像 固定请求ID处理,也可以是建立一个map记录每个rid对应的含义,请求返回里再处理
......@@ -1147,6 +1199,7 @@ function RecordToFile()
socket[0].sendObj(msg)
}
}
function BeginRecordFile(LocalFilePath)
{
let msg = {
......@@ -1166,6 +1219,7 @@ function BeginRecordFile(LocalFilePath)
else
socket[1].sendObj(msg)
}
function watermask()
{
//对指定的窗口ID发送水印,只支持VLC引擎播放,其它引擎播放,可采用叠加透明网页的方式,参考FloatWebInfo的实现
......@@ -1188,6 +1242,7 @@ function watermask()
else
socket[1].sendObj(msg)
}
function changePlay()
{
//动态改变指定的窗口ID的播放源,Player_ChangePlay本身支持同时改变多个分屏窗口播放源,Play支持传数组 ForceDestroy指定是否先销毁原来播放引擎 默认不销毁
......@@ -1283,6 +1338,7 @@ function changePlay()
else
socket[1].sendObj(msg)
}
function FloatWebInfo()
{
//对指定的窗口ID叠加网页内容显示
......@@ -1448,7 +1504,7 @@ function SendUpdateJson() {
.video-container {
position: relative;
margin-top: 8px;
height: 480px;
height: 320px;
border: #ddd 1px dashed;
display: flex;
display: -ms-flexbox;
......
VUE_APP_API_URL = ''
\ No newline at end of file
VUE_APP_API_URL = ''
\ No newline at end of file
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
plugins: [
[
'import',
{ libraryName: 'vant', libraryDirectory: 'es', style: true },
'vant'
]
]
};
因为 它太大了无法显示 source diff 。你可以改为 查看blob
因为 它太大了无法显示 source diff 。你可以改为 查看blob
{
"name": "vant-demo-vue2",
"version": "1.0.0",
"description": "Collection of vant demos.",
"author": "neverland <chenjiahan@neverl.com>",
"license": "MIT",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.21.0",
"core-js": "^3.4.3",
"element-ui": "^2.15.6",
"qs": "^6.9.4",
"vant": "^2.12.30",
"vue": "^2.6.10",
"vue-router": "^3.0.5",
"vuex": "^3.6.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.1.1",
"@vue/cli-plugin-eslint": "^4.1.1",
"@vue/cli-service": "^4.1.1",
"babel-eslint": "^10.0.3",
"babel-plugin-import": "^1.12.0",
"eslint": "^4.19.1",
"eslint-plugin-vue": "^6.0.1",
"less": "^3.8.1",
"less-loader": "^5.0.0",
"postcss-pxtorem": "^5.1.1",
"vue-template-compiler": "^2.6.10"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
}
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"Android >= 4.0",
"iOS >= 7"
]
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
<title></title>
</head>
<body>
<div id="app"></div>
</body>
</html>
<!DOCTYPE html>
<html manifest="/websocket.appcache">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>当前页内嵌VLC多媒体播放小程序测试(PluginOK)</title>
<style>
html,body {
margin: 0;
padding: 0;
}
.VLCApplet{
width: 1100px;
height: 500px;
margin: 0 auto;
}
</style>
</head>
<body>
<object ID="WrlWS" CLASSID="CLSID:C0971B90-4513-4E2D-A0B6-15B915FE748A" width="0" height="0"></object>
<div id="VLCApplet" class="VLCApplet">
<param ID="2" Play="http://www.zorrosoft.com/Files/PluginOKBrowserApplet.mp4" />
<param ID="3" Play="rtsp://wzh:test123456@192.168.1.8:554/h264/ch1/main/av_stream" Caching="500" />
</div>
</body>
</html>
<!DOCTYPE html>
<html manifest="/websocket.appcache">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>当前页内嵌VLC Web Player小程序体验(PluginOK)</title>
<style>
html,
body {
margin: 0;
padding: 0;
}
.VLCWebPlayer {
margin: 0;
width: 550px;
height: 500px;
border:none;
}
</style>
</head>
<body>
<object ID="WrlWS" CLASSID="CLSID:C0971B90-4513-4E2D-A0B6-15B915FE748A" width="0" height="0"></object>
<br />
<div id="VLCApplet" class="VLCApplet"></div>
<div id="VLCWebPlayer" class="VLCWebPlayer"></div>
</body>
</html>
<template>
<div id="app">
<router-view />
</div>
</template>
<style>
body {
-webkit-font-smoothing: antialiased;
padding: 0;
margin: 0;
font-family: 'PingFang SC';
height: 100%;
position: relative;
}
#app {
height: 100%;
padding: 0;
margin: 0;
}
.el-message-box__wrapper{
z-index: 99999999!important;
}
</style>
import Vue from "vue";
function GetDefaultConn(port,type) {
/// flag为1代表启用日志输出,系统正式上线后设置0可提高运行速度
/// sid代表本次连接的会话ID,必须保证唯一
let Protocol = location.protocol;
if (Protocol.toUpperCase().indexOf('HTTPS') > -1)
{
/// HTTPS网站,需要申请自己主站下其它地方都不会用到的一个子域名来做SSL证书,如域名在阿里云上,请下载Apache类型的证书并提交给客服制作授权
if(1 == type)
return 'wss://wrl.zorrosoft.com:'+port+'?sid=' + getrandom(5).toLocaleString() + '&flag=1&cid=zorrosoft&tk=8C4560272C8A38C32EF6102CAB6B4D886504F06C63202A316B3FD88381FC5491704DA444156B9F6FDA313843E412F1E1DC414A7899399F14D76688090FC7DCE11DA121CB2B0E819B2B7080DB9CF09D4D66192C5893ABE182DA38DF8A02EFAACB304BF9A242ADEBFAA09FC0304918895DE3B56E30A17AA8D92E3D61C1AC2453E6C1C637C3E260FE9A445EC858BADEB9312A43DD99323EF5D63414B9BC7D3F4004C7E109ADD5A6289ADAB004A2A544D312BB84E467DAC4C9449418F3FCCC9529049DCFD562B77EF2CE429B242C23975E6EA922E0564B6507177187E92F254EC2678A795B5D2EC92F818A7364FB7CA3E553D4F94119F868261E5A0A8E7EBE841CF7'; // 这里注意test.yuanmaster.com替换为自己的子域名
else
return 'wss://wrl.zorrosoft.com:'+port+'?sid=' + getrandom(5).toLocaleString() + '&flag=1'; // 这里注意wrl.zorrosoft.com替换为自己的子域名
}
else
{
/// type为1时代表中间件启用了安全校验模式,需要按文档生成tk,具体方法参考SDK包中的文档:中间件安全解决方案.pdf
if(1 == type)
return 'ws://127.0.0.1:'+port+'?sid=' + getrandom(5).toLocaleString() + '?flag=1&cid=zorrosoft&tk=8C4560272C8A38C32EF6102CAB6B4D886504F06C63202A316B3FD88381FC5491704DA444156B9F6FDA313843E412F1E1DC414A7899399F14D76688090FC7DCE11DA121CB2B0E819B2B7080DB9CF09D4D66192C5893ABE182DA38DF8A02EFAACB304BF9A242ADEBFAA09FC0304918895DE3B56E30A17AA8D92E3D61C1AC2453E6C1C637C3E260FE9A445EC858BADEB9312A43DD99323EF5D63414B9BC7D3F4004C7E109ADD5A6289ADAB004A2A544D312BB84E467DAC4C9449418F3FCCC9529049DCFD562B77EF2CE429B242C23975E6EA922E0564B6507177187E92F254EC2678A795B5D2EC92F818A7364FB7CA3E553D4F94119F868261E5A0A8E7EBE841CF7';
else
return 'ws://127.0.0.1:'+port+'?sid=' + getrandom(5).toLocaleString() + '&flag=1';
}
}
///Vue.prototype.$rules = validate;
// 获取随机数
function getrandom(nums) {
return ('000000' + Math.floor(Math.random() * 999999)).slice(-6);
}
export default GetDefaultConn;
export default class websocket {
constructor(url, options) {
this.instance = null
this.token = null
this.isConnected = false
this.url = url
this.options = options || this.defaultOptions()
if (this.options) {
this.reconnectEnabled = options.reconnectEnabled || false
if (this.reconnectEnabled) {
this.reconnectInterval = options.reconnectInterval
}
// Token
//this.token = options?.token || null
}
// These methods should be defined by components
this.onOpen = null
this.onMessage = null
this.onClose = null
this.onError = null
}
defaultOptions() {
return {
reconnectEnabled: false,
reconnectInterval: 0,
token: null
}
}
connect() {
const token = this.token || null
let url = this.url
if (token !== null) {
url += `?token=${token}`
}
this.instance = new WebSocket(url)
// Socket event listeners
// Each event handler also calls the corresponding class method,
// which can be defined by the component
this.instance.onopen = () => {
this.isConnected = true
console.log('链接成功')
if (typeof this.onOpen === 'function') {
this.onOpen()
}
}
this.instance.onmessage = (msg) => {
if (typeof this.onMessage === 'function') {
this.onMessage(msg)
}
}
this.instance.onclose = (evt) => {
this.isConnected = false
if (typeof this.onClose === 'function') {
this.onClose(evt)
}
if (this.reconnectEnabled) {
this.reconnect()
}
}
this.instance.onerror = (evt) => {
if (typeof this.onError === 'function') {
this.onError(evt)
}
}
}
disconnect() {
try {
this.instance.close()
} catch (e) {
console.warn(`${e} ${this.instance}`)
}
delete this.instance
}
reconnect() {
try {
this.instance.close()
} catch (e) {
console.warn(`${e} ${this.instance}`)
}
delete this.instance
setTimeout(() => {
this.connect()
}, this.reconnectInterval)
}
sendObj(data) {
if (this.instance.readyState === this.instance.OPEN) {
//若是ws开启状态
this.instance.send(JSON.stringify(data))
} else if (this.instance.readyState === this.instance.CONNECTING) {
// 若是 正在开启状态,则等待1s后重新调用
setTimeout(() => {
this.sendObj(data);
}, 1000);
} else {
// 若未开启 ,则等待1s后重新调用
setTimeout(() => {
this.sendObj(data);
}, 1000);
}
}
removeListeners() {
// removeListeners
this.onOpen = null
this.onMessage = null
this.onClose = null
this.onError = null
}
}
import Vue from 'vue';
import App from './App';
import {
router
} from './router';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
let qs = require('qs');
Vue.prototype.$qs = qs;
new Vue({
router,
el: '#app',
render: h => h(App)
});
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router);
const routes = [{
path: '/',
name: 'index',
component: () => import('./view/index'),
meta: {
title: '首页'
},
}
];
// add route path
routes.forEach(route => {
route.path = route.path || '/' + (route.name || '');
});
const router = new Router({
routes
});
router.beforeEach((to, from, next) => {
const title = to.meta && to.meta.title;
if (title) {
document.title = title;
}
next();
});
export {
router
};
<template>
<div class="mainContainer">
<div class="tool">
<div class="item">窗口宽度</div>
<div class="item">
<el-input v-model="width" size="small" class="input" @blur="resize(0)"></el-input>
</div>
<div class="item">窗口高度</div>
<div class="item">
<el-input v-model="height" size="small" class="input" @blur="resize(0)"></el-input>
</div>
<div class="item">左边距</div>
<div class="item">
<el-input v-model="left" size="small" class="input" @blur="resize(1)"></el-input>
</div>
<div class="item">上边距</div>
<div class="item">
<el-input v-model="top" size="small" class="input" @blur="resize(1)"></el-input>
</div>
<div class="item">分屏样式</div>
<div class="item">
<el-input-number v-model="ShowType" :step="1" size="small" @change="setType()" :min="1">
</el-input-number>
</div>
<el-button size="small" v-if="StartSecond" @click="CloseSecondPlayer">
关闭第二个播放器
</el-button>
<el-button size="small" @click="openSecondPlayer" v-else>
启动第二个播放器
</el-button>
</div>
<div class="tool">
<div class="item">子窗口</div>
<div class="item">
<el-input-number v-model="win" :step="1" size="small" @change="SetSelWnd()" :min="1"></el-input-number>
</div>
<div class="item">
<el-button size="small" @click="getCapture">抓图</el-button>
</div>
<el-button size="small" @click="watermask">设置水印</el-button>
<el-button size="small" @click="RecordToFile">
<span v-if="isRecordFile">停止录像</span>
<span v-else>本地录像</span>
</el-button>
<div class="item" style="margin-left: 10px;">
<el-button size="small" @click="NextFrame()" type="primary">下一帧</el-button>
</div>
<div class="item">
<el-button size="small" @click="PlayPause()" type="primary">暂停播放</el-button>
</div>
<div class="item">
<el-button size="small" @click="PlayContinue()" type="primary">继续播放</el-button>
</div>
<div class="item">
<el-button size="small" @click="PlaySpeed()" type="primary">加速播放</el-button>
</div>
<div class="item">
<el-button size="small" @click="PlayMute()" type="primary">播放静音</el-button>
</div>
<div class="item">
<el-button size="small" @click="PlayFullScreen()" type="primary">全屏播放</el-button>
</div>
</div>
<div class="video-container" ref="player">
播放器区域
</div>
<div class="urlbox">
<div class="item">视频地址</div>
<div class="item input">
<el-input v-model="newrtsp" placeholder="这里演示动态切换播放源" size="small"></el-input>
</div>
<div class="item">旋转</div>
<div class="item">
<el-select size="small" v-model="Transform" placeholder="请选择" style="width: 100PX;">
<el-option label="none" value="none">
</el-option>
<el-option label="90" value="90">
</el-option>
<el-option label="180" value="180">
</el-option>
<el-option label="270" value="270">
</el-option>
<el-option label="hflip" value="hflip">
</el-option>
<el-option label="vflip" value="vflip">
</el-option>
<el-option label="transpose" value="transpose">
</el-option>
<el-option label="antitranspose" value="antitranspose">
</el-option>
</el-select>
</div>
<div class="item" style="margin-left: 10px;">
<el-button type="primary" @click="changUrl()" size="small">切换源播放</el-button>
</div>
</div>
<div class="urlbox">
<div class="item">叠加透明网页地址</div>
<div class="item input">
<el-input v-model="newfloatweb" placeholder="这里演示叠加网页内容显示" size="small"></el-input>
</div>
<div class="item" style="margin-left: 10px;">
<el-button type="primary" @click="FloatWebInfo()" size="small">叠加显示</el-button>
</div>
</div>
<div class="tool">
<div class="item">字幕</div>
<div class="item" style="flex: 1;">
<el-input size="small" placeholder="请输入内容" v-model="danmu.text"></el-input>
</div>
<div class="item">
<el-select size="small" v-model="danmu.position" placeholder="请选择" style="width: 100PX;">
<el-option label="顶部" value="TOP">
</el-option>
<el-option label="底部" value="BOTTOM">
</el-option>
</el-select>
</div>
<div class="item">位置(x)</div>
<div class="item">
<el-input v-model="danmu.x" size="small" class="input"></el-input>
</div>
<div class="item">位置(y)</div>
<div class="item">
<el-input v-model="danmu.y" size="small" class="input"></el-input>
</div>
<div class="item">透明度</div>
<div class="item">
<el-input v-model="danmu.opacity" size="small" class="input"></el-input>
</div>
<div class="item">大小</div>
<div class="item">
<el-input v-model="danmu.size" size="small" class="input"></el-input>
</div>
<div class="item">
<el-button size="small" @click="setText()" type="primary">设置</el-button>
</div>
</div>
<div class="tool">
<div class="item">多源播放JSON</div>
<el-input v-model="WebCfg2" size="small"></el-input>
<div class="item">
<el-button size="small" @click="RePlayFirst()" type="primary">重新启动播放</el-button>
</div>
</div>
<div class="tool">
<div class="item" style="margin-left: 10px;">
<el-button type="primary" @click="StopAllPlay()" size="small">停止所有播放</el-button>
</div>
<div class="item" style="margin-left: 10px;">
<el-button type="primary" @click="CloseAllPlayer()" size="small">关闭所有播放器</el-button>
</div>
<div class="item">最新版本号</div>
<div class="item">
<el-input v-model="version" size="small"></el-input>
</div>
<el-button size="small" @click="CheckUpdate" type="primary">校验升级</el-button>
</div>
<el-input type="textarea" :rows="5" placeholder="调试日志" v-model="DebugLog" class="DebugLog">
</el-input>
</div>
</template>
<script>
//集成开发说明:所有交互都是通过JS建立websocket连接后,发送或接收JSON包进行,都是异步请求,发送和接收包都携带rid用来标识请求返回包对应是哪个请求的结果
//所有发送的msg中用到的长整形rid 可以自己指定 不同的发送请求定义唯一的rid 这样可以在回调用轻松判断
//针对一些特殊请求,可以根据自己的实际情况把rid固定,如启动播放时指定固定rid获得启动播放的实例ID
//首先建立第一个websocket连接到中间件服务端口启动vlc播放小程序 然后获得启动小程序的侦听端口建立第二个websocket连接 主要用来进行控制播放、截图、录像、设置字幕等操作
//多路播放时分屏窗口id 是固定的参数 从1开始计数 计数顺序为 从左到右,从上到下
//释放播放小程序,直接关闭建立的第一个websocket连接即可
import GetDefaultConn from "../common/base.js"
//加载websoket类
import websocket from '../common/websocket'
export default {
components: {
},
data() {
return {
aid: 0, // 第一个播放实例实例ID
aid2: 0, // 保存启动的第二个播放实例ID
curID:0, // 当前操作实例ID
rid: 10, // 起始请求序号
run1:1, // 请求启动播放实例序号1
run2:2, // 请求启动播放实例序号2
win: 1, // 播放实例中当前操作的分屏窗口序号ID
newrtsp: 'http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8',
newfloatweb: 'https://output.jsbin.com/dopavun',
StartSecond: false, // 是否启动了第二个播放实例
ReStartPlay: 0, // 第一个播放实例是否重新播放状态
width: 960, // 播放窗口显示宽度
height: 320, // 播放窗口显示高度
left: 0, // 播放窗口以网页左上角为基点显示的X位置
top: 0, // 播放窗口以网页左上角为基点显示的Y位置
IframeX: -10, // 播放窗口模拟iFrame用的X坐标
IframeY: 0, // 播放窗口模拟iFrame用的Y坐标
ShowType: 1, //分屏样式
WebCfg:'[{"ID":2,"Uri":"https://vjs.zencdn.net/v/oceans.mp4"},{"ID":1,"Uri":"https://media.w3.org/2010/05/sintel/trailer.mp4","Option":":rtsp-tcp"},{"ID":4,"Uri":"http://www.zorrosoft.com/Files/PluginOKBrowserApplet.mp4","Option":":file-caching=300"},{"ID":3,"Uri":"http://www.zorrosoft.com/Files/h265.mkv","Option":":network-caching=500"}]',
WebCfg2:'[{"ID":1,"Uri":"https://vjs.zencdn.net/v/oceans.mp4"},{"ID":2,"Uri":"https://media.w3.org/2010/05/sintel/trailer.mp4","Option":":rtsp-tcp"},{"ID":3,"Uri":"http://www.zorrosoft.com/Files/PluginOKBrowserApplet.mp4","Option":":file-caching=300"},{"ID":4,"Uri":"rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp","Option":":network-caching=500"}]',
result: [], //日志结果数组
socket: [], //websocket对象数组,采用数组需要注意维护索引号,建议改为根据连接的SID参数存到对应的集合,需要用到连接时根据SID提取
isRecordFile: false, //记录录像的状态
isConnService:false, //是否连接的中间件服务
isDisConnect:false, // 是否处于断开连接过程
RecordFilePath: 'C:/Zorro/test.mp4',
Transform: 'none',
recordid: 0, //录像时返回的录像进程PID
danmu: {
//需要发送的字幕配置信息
text: '您好呀,O(∩_∩)O哈哈~',
position: 'TOP',
color: "#ff0000",
opacity: 128,
size: 50,
x: 0,
y: 0
},
version: '2.2.12.3' //版本信息
}
},
computed: {
DebugLog() {
return this.result.join("\n")
}
},
mounted(){
//初始化配置
this.init()
/// 响应改变浏览器窗口大小
let _this = this
window.onresize = ()=>{
_this.pageResize()
}
},
destroyed(){
window.onresize = null
},
beforeDestroy() {
//关闭所有websocket链接以及浏览器监听
this.close()
},
methods:
{
init()
{
//监听浏览器切换标签页面
if(document.addEventListener)
document.addEventListener('visibilitychange', this.handleVisiable,false)
else
document.attachEvent('visibilitychange',this.handleVisiable,false) /// 老版本浏览器
//侦听滚动条
window.addEventListener('scroll', this.windowScroll, true)
//监听当页面离开时候
window.addEventListener('unload', this.unloadHandler,false)
//获取播放器位置节点信息
this.getPlayerPosition()
//启动VLC小程序
this.StartVlcPlayer()
},
windowScroll() {
// 滚动条距离页面顶部的距离
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
if(this.aid > 0)
this.appScroll(0, this.aid, scrollTop)
if (this.aid2 > 0)
this.appScroll(2, this.aid2, scrollTop)
},
appScroll(si, id, scrollTop) {
if(id)
{
// 默认纵向滚动小程序实例,如需要横向滚动,Code设置为1,修改Left的值
// NoLog指示服务日志不输出相关日志,因为时间比较多,输出日志导致日志文件信息过多
this.rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletScroll",
"rid": this.rid,
"para": {
"ID": id,
"NoLog": 1,
"Code": 2,
"Left": 0,
"Top": Math.round(scrollTop)
}
}
this.socket[si].sendObj(msg)
}
},
getPlayerPosition() {
//获取播放器位置节点信息
let nScrollTop = 0
let nScrollLeft = 0
let bHorizontalBar =this.hasHorizontalScrollbar()
let bVerticalBar = this.hasVerticalScrollbar()
if(bHorizontalBar){
if(window.pageXOffset != undefined)
nScrollLeft = Math.round(window.pageXOffset)
else
{
//获取页面的scrollLeft(兼容写法)
nScrollLeft = Math.round(document.documentElement.scrollLeft || document.body.scrollLeft)
}
}
if(bVerticalBar){
if(window.pageYOffset != undefined)
nScrollTop = Math.round(window.pageYOffset)
else
{
//获取页面的scrollTop(兼容写法)
nScrollTop = Math.round(document.documentElement.scrollTop || document.body.scrollTop)
}
}
let react = this.$refs.player.getBoundingClientRect()
this.left = Math.round(react.left) + nScrollLeft
this.top = Math.round(react.top) + nScrollTop
},
handleVisiable(e) {
//浏览器页面切换侦听回调函数
//this.$message.success(e.target.visibilityState)
//this.result.push("visibilityState " + e.target.visibilityState)
if (e.target.visibilityState == 'hidden') {
//切离该页面时执行
this.hideApp(4)
} else if (e.target.visibilityState == 'visible') {
//切换到该页面时执行
this.showApp()
}
},
hasVerticalScrollbar(){
if(document.documentElement.clientHeight)
return document.body.scrollHeight > document.documentElement.clientHeight
return document.body.scrollHeight > window.innerHeight
},
hasHorizontalScrollbar(){
if(document.documentElement.clientWidth)
return document.body.scrollWidth > document.documentElement.clientWidth
return document.body.scrollWidth > window.innerWidth
},
pageResize(){
if(this.aid > 0)
this.SendScrollInfo(0,this.aid)
if(this.aid2 > 0)
this.SendScrollInfo(2,this.aid2)
},
SendScrollInfo(si,id){
let nScrollTop = 0
let nScrollLeft = 0
let BarCode = 0
let bHorizontalBar = this.hasHorizontalScrollbar()
let bVerticalBar = this.hasVerticalScrollbar()
if(bHorizontalBar){
if(window.pageXOffset != undefined)
nScrollLeft = Math.round(window.pageXOffset)
else
{
//获取页面的scrollLeft(兼容写法)
nScrollLeft = Math.round(document.documentElement.scrollLeft || document.body.scrollLeft)
}
}
if(bVerticalBar){
if(window.pageYOffset != undefined)
nScrollTop = Math.round(window.pageYOffset)
else
{
//获取页面的scrollTop(兼容写法)
nScrollTop = Math.round(document.documentElement.scrollTop || document.body.scrollTop)
}
}
if(bHorizontalBar)
BarCode = 1
if(bVerticalBar)
BarCode += 2
this.rid++ // 增加序号
/// 设置页码滚动信息,BarW BarH分别为预留右侧宽度和底部高度
let msg = {
"req": "Wrl_ScrollBar",
"rid": this.rid,
"para": {
"ID": id,
"BarW": 0,
"BarH": 0,
"Code": BarCode,
"Left": nScrollLeft,
"Top": nScrollTop
}
}
console.log(msg)
this.socket[si].sendObj(msg)
},
unloadHandler() {
//关闭所有websoket链接
this.close()
},
close() {
//关闭播放器
this.CloseAllPlayer()
this.isDisConnect = true
//关闭第一个websoket链接
this.socket[0].disconnect()
this.socket.pop()
this.isDisConnect = false
//关闭侦听浏览器页面切换
if(document.addEventListener)
document.removeEventListener('visibilitychange', this.handleVisiable,false)
else
document.detachEvent('visibilitychange',this.handleVisiable,false) /// 老版本浏览器
//关闭侦听滚动条
window.removeEventListener('scroll', this.windowScroll, true)
//关闭侦听滚动条
window.removeEventListener('unload', this.unloadHandler,false)
},
StartVlcPlayer() {
//启动第一个vlc网页小程序 Open为播放源,播放源也可以放到Web节点中,参考RePlayFirst中实现,差异在于,Open中指定对所有分屏有效,而在Web节点中可指定更多播放参数
this.isConnService = true
let Protocol = location.protocol;
if (Protocol.toUpperCase().indexOf('HTTPS') > -1)
{
// HTTPS网站,连接WSS侦听端口
this.openWebsocket(453,0)
}
else
{
this.openWebsocket(83,0)
//this.openWebsocket(83,1) 支持安全校验机制
}
let msg = {
"req": "Wrl_VLCWebPlayer",
"rid": this.run1,
"para": {
"Type": "0",
"Title": "VLC网页播放器",
"Version": 0,
"Flag": 578,
"Left": this.left,
"Top": this.top,
"Width": this.width,
"Height": this.height,
"IframeX": this.IframeX,
"IframeY": this.IframeY,
"BarW": 0,
"BarH": 0,
"ScrollTop": 0,
"Web": [{"ID":1,"Uri":"http%3A%2F%2Fwww.zorrosoft.com%2FFiles%2FPluginOKBrowserApplet.mp4","Option":":rtsp-tcp :network-caching=500"}],
"ShowType": this.ShowType
}
}
this.socket[0].sendObj(msg)
},
openSecondPlayer() {
if(this.aid)
{
//演示打开第二个播放小程序实例 先改变第一个小程序位置 Flag值需要在原来基础上+512以支持多实例播放,Version设置1启用多进程播放模式
this.width = 475
this.resize(0)
this.isConnService = true
let Protocol = location.protocol;
if (Protocol.toUpperCase().indexOf('HTTPS') > -1)
{
// HTTPS网站,连接WSS侦听端口
this.openWebsocket(453,0)
}
else
{
this.openWebsocket(83,0)
//this.openWebsocket(83,1) 支持安全校验机制
}
let msg = {
"req": "Wrl_VLCWebPlayer",
"rid": this.run2,
"para": {
"Type": "0",
"Title": "VLC Web Player",
"Version": 1,
"Flag": 578,
"Left": this.left + 485,
"Top": this.top,
"Width": this.width,
"Height": this.height,
"IframeX": -10,
"IframeY": 0,
"BarW": 0,
"BarH": 0,
"ScrollTop": 0,
"Web": JSON.parse(this.WebCfg),
"ShowType": 4
}
}
this.socket[2].sendObj(msg)
this.StartSecond = true
}
else{
this.$message.success('请先启动第一个播放器')
}
},
CloseSecondPlayer() {
if (this.StartSecond) {
this.isDisConnect = true
/// 每个播放实例占用2个连接,一个到中间件、一个到播放小程序,分别断开连接并释放
this.socket[3].disconnect()
this.socket[2].disconnect()
this.socket.pop()
this.socket.pop()
this.isDisConnect = false
this.StartSecond = false
this.aid2 = 0
//还原第一个小程序位置
this.width = 960
this.height = 320
//获取播放器位置节点信息
let react = this.$refs.player.getBoundingClientRect()
this.left = react.left
this.top = react.top
this.resize(0)
}
},
CloseFirstPlayer()
{
if (this.aid > 0)
{
this.rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletControl",
"rid": this.rid,
"para": {
"ID": this.aid,
"Code": 1
}
}
this.isDisConnect = true
/// 释放到播放小程序的连接
this.socket[1].disconnect()
this.socket[0].sendObj(msg)
this.aid = 0
this.ReStartPlay = 0
if(this.StartSecond)
this.socket.splice(1,1)
else
this.socket.pop()
this.isDisConnect = false
}
},
RePlayFirst()
{
this.CloseFirstPlayer()
this.ReStartPlay = 1
//重新启动播放小程序,Web节点配置播放源,设置分屏风格4
this.ShowType = 4
let Msg = {
"req": "Wrl_VLCWebPlayer",
"rid": this.run1,
"para": {
"Type": "0",
"Title": "VLC网页播放器",
"Version": 0,
"Flag": 578,
"Left": this.left,
"Top": this.top,
"Width": this.width,
"Height": this.height,
"IframeX": this.IframeX,
"IframeY": this.IframeY,
"BarW": 0,
"BarH": 0,
"ScrollTop": 0,
"Web": JSON.parse(this.WebCfg2),
"ShowType": this.ShowType
}
}
this.socket[0].sendObj(Msg)
},
CloseAllPlayer()
{
/// 先关闭第二个实例,否则socket连接序号会错
this.CloseSecondPlayer()
this.CloseFirstPlayer()
},
StopSecondPlayer()
{
if (this.StartSecond)
{
this.rid++ // 增加序号
let msg = {
"req": "VLC_Control",
"rid": this.rid,
"para": [{
"ID": 1,
"Type": "Stop"
},{
"ID": 2,
"Type": "Stop"
},{
"ID": 3,
"Type": "Stop"
},{
"ID": 4,
"Type": "Stop"
}]
}
console.log(msg)
this.socket[3].sendObj(msg)
}
},
StopFirstPlayer()
{
if (this.aid > 0)
{
this.rid++ // 增加序号
/// 支持批量停止播放 暂停播放等操作 para中具体传多少个分屏窗口数据自己定,分屏窗口数量由ShowType决定
/// ID是分屏窗口序号,从1开始 序号原则是从左向右开始编号,然后从上到下开始顺序编号,如遇右侧多层排列窗口时,直到右侧窗口序号排序完成
/// Type可以是这些值:Pause,Play,Stop,StopAsync,Next,Prev,Clear,TogglePause 分别对应暂停播放 播放 停止播放 异步停止播放 下一个播放源 上一个播放源 清理播放列表 切换暂停状态
let msg = {}
if(this.ReStartPlay)
{
msg = {
"req": "VLC_Control",
"rid": this.rid,
"para": [{
"ID": 1,
"Type": "Stop"
},{
"ID": 2,
"Type": "Stop"
},{
"ID": 3,
"Type": "Stop"
},{
"ID": 4,
"Type": "Stop"
}]
}
}
else{
/// 不指定分屏信息时,停止所有播放
msg = {
"req": "VLC_Control",
"rid": this.rid,
"para": []
}
}
console.log(msg)
this.socket[1].sendObj(msg)
}
},
StopAllPlay()
{
this.StopSecondPlayer()
this.StopFirstPlayer()
},
openWebsocket(port,type) {
//打开websocket服务
let ws = GetDefaultConn(port,type)
const socketClient = new websocket(ws, {
reconnectEnabled: false
})
socketClient.connect()
if(1 == this.ReStartPlay && this.StartSecond)
{
this.ReStartPlay = 2
this.socket.splice(1,0,socketClient) // 重新播放时已经启动第二个播放实例,采用插入方式,避免WS下标错误,确保第一个播放实例的WS连接下标是0和1,第二个播放实例是3和4
}
else
{
if(1 == this.ReStartPlay)
this.ReStartPlay = 2
this.socket.push(socketClient)
}
socketClient.onMessage = (msg) =>
{
console.log(msg.data)
let res = JSON.parse(msg.data)
if (res.event == 'Wrl_AppletOK') {
//小程序创建成功后根据事件名称获取当前小程序id
if (res.rid == this.run1) {
this.aid = res.aid
this.SendScrollInfo(0,this.aid)
console.log(this.aid)
}
if (res.rid == this.run2) {
this.aid2 = res.aid
this.SendScrollInfo(2,this.aid2)
console.log(this.aid2)
}
}
if (res.event == 'Wrl_Listen') {
//小程序建立侦听成功 这里得到当前小程序返回的端口
//这里创建另外一个websocket到播放程序端口 来实现重新指定播放源,暂停播放,抓图等
let _this = this
setTimeout(function() {
_this.isConnService = false
_this.openWebsocket(res.data.port,0)
}, 200);
if(res.aid > 0)
{
this.curID = res.aid
//this.result.push("当前操作播放器实例 " + res.aid)
}
}
//切换窗口的时候 设定当前窗口为此窗口
if (res.event == 'VLC_Selected') {
this.win = res.ID // 选中的分屏窗口序号
if(res.aid > 0)
{
this.curID = res.aid
//this.result.push("当前操作播放器实例 " + res.aid)
}
}
// 播放窗口收到鼠标按下通知
if (res.event == 'VLC_MouseDown') {
if(res.aid > 0)
{
this.curID = res.aid
//this.result.push("当前操作播放器实例 " + res.aid)
}
}
// 定时录像结束通知
if (res.event == 'VLC_StopRecord') {
this.isRecordFile = false;
this.recordid = 0;
this.$message.success("定时录像成功\n" + res.data.File);
}
// 设置文件保存位置结果通知
if (res.event == 'Wrl_SelectFile') {
if(res.data.length)
{
this.BeginRecordFile(res.data[0].File) /// 使用返回的文件位置
}
else
this.BeginRecordFile(this.RecordFilePath) // 取消指定,使用默认位置
}
//请求录像返回
if (res.rid == 90000) {
//记录录像的pid
this.recordid = res.data.PID
}
//请求停止录像返回
if (res.rid == 90001) {
this.recordid = 0;
this.$message.success("录像成功\n" + res.data.File)
//console.log(res.data.File)
}
//请求截图返回
if (res.rid == 90002) {
this.$message.success("截图成功\n" + res.data.Img[0].File)
//console.log(res.data.Img[0].File)
}
if (res.err)
{
// 请求返回错误
this.$message.success(res.err)
}
if (res.req == 'Wrl_Version')
{
if (res.data.Update == 1) {
/// 先强制隐藏播放小程序,避免弹框显示不出来
this.hideApp(32);
this.$confirm('有新版本发布, 是否马上升级?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
/// 先提前关闭播放小程序
this.CloseAllPlayer()
this.SendUpdateJson()
}).catch(() => {
this.showApp()
})
} else {
this.$message.success('已经是最新版本!')
/// 说明:即使版本号是最新的,也是可以发起升级请求SendUpdateJson()的,相当于覆盖安装,用于更新比如中间件或播放小程序的配置文件等
}
}
//记录日志
this.result.push(msg.data)
}
socketClient.onClose = (msg) => {
console.log(msg)
}
socketClient.onError = (msg) => {
// 避免IE中点击重复播放及firefox断开连接提示等问题
if(!this.ReStartPlay && this.isConnService && !this.isDisConnect)
{
//连接不上,认为还没有安装PageHiPlayer-VLC网页播放器 没有安装时提示安装
this.$confirm('PageHiPlayer-VLC网页播放器 服务端口连接失败,可能是尚未安装,是否马上下载安装?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
window.open('http://local.zorrosoft.com/Files/RtspWebPlayerIns.exe') // 建议打包为zip等格式下载,解压后安装,EXE文件下载浏览器会提示是否保留
}).catch(() => {
})
}
}
},
resize(position) {
//请求改变播放实例小程序显示位置或大小,如不需要改变显示位置,不传X和Y
if(this.aid2 > 0 && this.aid2 == this.curID){
this.rid++ // 增加请求序号
if(position){
let msg = {
"req": "Wrl_AppletResize",
"rid": this.rid,
"para": {
"ID": this.aid2,
"X": this.left,
"Y": this.top,
"Width": this.width,
"Height": this.height
}
}
this.socket[2].sendObj(msg)
}
else{
let msg = {
"req": "Wrl_AppletResize",
"rid": this.rid,
"para": {
"ID": this.aid2,
"Width": this.width,
"Height": this.height
}
}
this.socket[2].sendObj(msg)
}
}
else
{
if(this.aid > 0){
this.rid++ // 增加请求序号
if(position){
let msg = {
"req": "Wrl_AppletResize",
"rid": this.rid,
"para": {
"ID": this.aid,
"X": this.left,
"Y": this.top,
"Width": this.width,
"Height": this.height
}
}
this.socket[0].sendObj(msg)
}
else{
let msg = {
"req": "Wrl_AppletResize",
"rid": this.rid,
"para": {
"ID": this.aid,
"Width": this.width,
"Height": this.height
}
}
this.socket[0].sendObj(msg)
}
}
}
},
getCapture() {
//请求截图,固定请求ID处理,也可以是建立一个map记录每个rid对应的含义,请求返回里再处理
let msg = {
"req": "VLC_VideoSnapshot",
"rid": 90002,
"para": [{
"ID": this.win,
"Type": 4,
"Count": 1,
"Delay": 1000,
"Interval": 200,
"PathType": 1
}]
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
setText() {
//发送字幕到指定的窗口ID
this.rid++ // 增加请求序号
let msg = {
"req": "VLC_MarqueePut",
"rid": this.rid,
"para": [{
"ID": this.win,
"Text": this.danmu.text,
"Position": this.danmu.position,
"Timeout": 0,
"Color": this.danmu.color,
"Opacity": this.danmu.opacity,
"Refresh": 1,
"Size": this.danmu.size,
"X": this.danmu.x,
"Y": this.danmu.y
}]
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
setType() {
//动态改变屏幕数量
this.rid++ // 增加请求序号
let msg = {
"req": "VLC_ChangePlay",
"rid": this.rid,
"para": {
"ShowType": this.ShowType
}
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
NextFrame()
{
//请求显示下一帧 请求后播放进入暂停状态,可继续请求下一帧,或请求播放恢复正常播放状态
this.rid++ // 增加请求序号
let msg = {
"req": "VLC_NextFrame",
"rid": this.rid,
"para": [{
"ID":this.win,
"Count": 1
}]
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
PlayPause()
{
//请求播放进入暂停状态
this.rid++ // 增加请求序号
let msg = {
"req": "VLC_Control",
"rid": this.rid,
"para": [{
"ID":this.win,
"Type": "Pause"
}]
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
PlayContinue()
{
//请求播放继续,或请求播放恢复正常播放状态
this.rid++ // 增加请求序号
let msg = {
"req": "VLC_Control",
"rid": this.rid,
"para": [{
"ID":this.win,
"Type": "Play"
}]
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
PlayMute()
{
// 设置选中分屏窗口视频静音,1静音 0不静音
this.rid++ // 增加请求序号
let msg = {
"req": "VLC_AudioPut",
"rid": this.rid,
"para": [{
"ID":this.win,
"Mute": 1
}]
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
PlaySpeed()
{
// 调整播放速度,仅限于文件或回放流,支持批量操作,当前演示针对选中分屏窗口设置2倍数播放
this.rid++ // 增加请求序号
let msg = {
"req": "VLC_PutInputInfo",
"rid": this.rid,
"para": [{
"ID":this.win,
"Rate": 2
}]
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
PlayFullScreen()
{
// 设置选中分屏窗口全屏显示 全屏后按ESC 字幕F\、双击或点击工具栏全屏图标可退出全屏状态
this.rid++ // 增加请求序号
let msg = {
"req": "VLC_VideoToggleFullscreen",
"rid": this.rid,
"para": [{
"ID":this.win
}]
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
SetSelWnd() {
//设置选中分屏子窗口
this.rid++ // 增加请求序号
let msg = {
"req": "VLC_SetSelect",
"rid": this.rid,
"para": {
"ID": this.win
}
}
if(this.aid2 > 0 && this.aid2 == this.curID)
{
this.result.push("执行播放器实例 " + this.curID)
this.socket[3].sendObj(msg)
}
else
this.socket[1].sendObj(msg)
},
AppletSnap() {
// 整个播放窗口抓图,包含所有分屏子窗口,发送到中间件侦听端口执行
this.rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletSnap",
"rid": this.rid,
"para": {
"ID": this.curID,
"File": ".jpg",
"Base64": 1
}
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[2].sendObj(msg)
else
this.socket[0].sendObj(msg)
},
FullApplet() {
// 整个播放窗口全屏,包含所有分屏子窗口,发送到中间件侦听端口执行
this.rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletControl",
"rid": this.rid,
"para": {
"ID": this.curID,
"Code": 2
}
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[2].sendObj(msg)
else
this.socket[0].sendObj(msg)
},
RecordToFile() {
//对指定的窗口ID进行录像 固定请求ID处理,也可以是建立一个map记录每个rid对应的含义,请求返回里再处理
if (this.isRecordFile)
{
let msg = {
"req": "VLC_StopRecord",
"rid": 90001,
"para": {
"PID": this.recordid
}
}
this.isRecordFile = false
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
}
else
{
this.rid++ // 增加请求序号
/// 先设置保存位置再录像
let msg = {
"req": "Wrl_SelectFile",
"rid": this.rid,
"para": {
"Type": 1,
"Title" : "请设置录像文件存放位置",
"Ext" : "录像文件(*.mp4)\r*.mp4"
}
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[2].sendObj(msg)
else
this.socket[0].sendObj(msg)
}
},
BeginRecordFile(LocalFilePath)
{
let msg = {
"req": "VLC_RecordFile",
"rid": 90000,
"para": {
//不指定Url时取当前焦点分屏窗口源进行录像
//"Url": encodeURIComponent(this.newrtsp),
"File": encodeURIComponent(LocalFilePath),
"Second": 30
}
}
this.isRecordFile = true
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
watermask() {
//对指定的窗口ID发送水印
this.rid++ // 增加请求序号
let msg = {
"req": "VLC_PutLogoShow",
"rid": this.rid,
"para": [{
"ID": this.win,
"File": "VLC.png",
"Delay": 20,
"Repeat": -1,
"Opacity": 128,
"X": 100,
"Y": 100
}]
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
changUrl() {
//动态改变指定的窗口ID的播放源,VLC_ChangePlay本身支持同时改变多个分屏窗口播放源,Play支持传数组 ForceDestroy 指定是否先销毁原来播放引擎 默认不销毁
this.rid++ // 增加请求序号
let msg = {
"req": "VLC_ChangePlay",
"rid": this.rid,
"para": {
"ForceDestroy":0,
"Play": [{
"ID": this.win,
"Uri": encodeURIComponent(this.newrtsp),
"Name": "BrowserApplet1",
"Option": `:rtsp-tcp Transform=${this.Transform}`
}]
}
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
FloatWebInfo() {
//对指定的窗口ID叠加网页内容显示
this.rid++ // 增加请求序号
let msg = {
"req": "VLC_FloatWebInfo",
"rid": this.rid,
"para": [{
"ID": this.win,
"Url": encodeURIComponent(this.newfloatweb),"Rect":{"P":4,"W":300,"H":300}
}]
}
if(this.aid2 > 0 && this.aid2 == this.curID)
this.socket[3].sendObj(msg)
else
this.socket[1].sendObj(msg)
},
showApp() {
//显示VLC小程序
if (this.aid > 0) {
this.rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletControl",
"rid": this.rid,
"para": {
"ID": this.aid,
"Code": 8
}
}
this.socket[0].sendObj(msg)
}
//如果启动了第二个小程序 也显示
if (this.aid2 > 0) {
this.rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletControl",
"rid": this.rid,
"para": {
"ID": this.aid2,
"Code": 8
}
}
this.socket[2].sendObj(msg)
}
},
hideApp(code) {
//隐藏VLC小程序 Code设置4是自动隐藏,如需强制隐藏,设置为32
if (this.aid > 0)
{
this.rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletControl",
"rid": this.rid,
"para": {
"ID": this.aid,
"Code": code
}
}
this.socket[0].sendObj(msg)
}
//如果启动了第二个小程序 也同时隐藏
if (this.aid2 > 0)
{
this.rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletControl",
"rid": this.rid,
"para": {
"ID": this.aid2,
"Code": code
}
}
this.socket[2].sendObj(msg)
}
},
CheckUpdate() {
//校验中间件版本是不是需要升级,如果额外指定PID参数,代表校验PID代表的小程序,Wrl_Version功能多
this.rid++ // 增加请求序号
let msg = {
"req": "Wrl_Version",
"rid":this.rid,
"para": {
"Version": this.version
}
}
this.socket[0].sendObj(msg)
},
SendUpdateJson() {
// 发送中间件的升级命令,实现自动升级,同时升级海康及VLC网页播放等小程序
// 注意:Wrl_Update中的请求参数如MD5 TK Size等,请根据文档“中间件制作升级包说明.pdf”中的打包工具生成,此处举例的升级包是在线公测版的,正式版需要自己制作
this.rid++ // 增加请求序号
let msg = {
"req":"Wrl_Update",
"rid":this.rid,
"para":{
"Name":"PageHi视频网页播放器升级包",
"Date":"2023-12-31",
"Desc":"1、中间件高级版内嵌小程序支持联想浏览器中加载使用;2、优化中间件访问数据文件操作及服务状态监控方式;3、PageHiPlayer海康私有协议及VLC播放引擎支持框选区域放大显示并支持前端接口调用,解决在X64系统下安装32位VLC无法使用问题,解决VLC切换播放源时可能没有成功通知;4、PageHiPlayer网页播放器增加视频长宽信息通知到前端,完善热键处理,视频画面全屏时实现自动隐藏任务栏,播放错误日志输出到单独的文件中...",
"DownAddr":"http://local.zorrosoft.com/Files/Update/RTSP_Update.pid",
"Open":"http://local.zorrosoft.com/vlc",
"MD5":"0BAB22C1631E508EC0C8E000FBF80AE1",
"Version":"2.2.12.3",
"Size":48463872,
"HideIns":0,
"Cookie":"",
"Auth":"",
"TK":"AC3B7B95CF42EE17939C1CEB5F6F1C9F0CE97DE39C80F51A0E710D876401802E1252665005EF2DF7230C0995D7787BB7D3568AE435D1160571F6CC7E40EEE56911A47467D6D9E3BCE74284E4BB6AB848B96C950EAB762CB106C0125B8EF28918A5555C5F1B2B9E0C648D8D8AB1B08C69436BED3B827068651A4A159EEC069543D7C0054B1B775212D20E5135E5CAA2BF4ACFEBF1348C3FAB3263221871F0F29D51F7F79DBA0A51C23872774551B009A727036A49CBFDCB44CC1E1252206ADED55826B549C5787360902619530E70572EAD0E79124692ADECBAC284A84D68357ADE4154CC73DDE94266B3BFD1F71006B50B32687F4C8867C801CE79FDA257AB30"
}
}
this.socket[0].sendObj(msg)
}
}
};
</script>
<style scoped>
.mainContainer {
display: block;
width: 960px;
margin-left: 20px;
margin-right: auto;
box-sizing: border-box;
}
.urlbox {
display: flex;
display: -ms-flexbox;
align-items: center;
-ms-flex-align: center;
justify-content: space-between;
margin: 15px 0;
}
.urlbox .input {
flex: 1;
margin: 0 15px;
}
.tool {
display: flex;
display: -ms-flexbox;
align-items: center;
-ms-flex-align: center;
margin: 15px 0;
}
.tool .item {
margin-right: 15px;
}
.tool .item:last-child {
margin-right: 0;
}
.tool .input {
width: 60px;
}
.video-container {
position: relative;
margin-top: 8px;
height: 320px;
border: #ddd 1px dashed;
display: flex;
display: -ms-flexbox;
align-items: center;
-ms-flex-align: center;
justify-content: center;
font-size: 24px;
color: #ddd;
}
</style>
module.exports = {
outputDir: 'dist',
lintOnSave: false,
publicPath: process.env.NODE_ENV === 'production' ? './' : '/',
css: {
},
productionSourceMap: false,
// devServer: {
// proxy: { //配置跨域
// '/api': {
// target:process.env.VUE_APP_API_URL, //这里后台的地址模拟的;应该填写你们真实的后台接口
// changOrigin: true, //允许跨域
// pathRewrite: {
// '^/api': ''
// }
// },
// }
// },
};
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
{
"name": "vue3_cli_default",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@babel/parser": {
"version": "7.23.6",
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.6.tgz",
"integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ=="
},
"@ctrl/tinycolor": {
"version": "3.6.1",
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA=="
},
"@element-plus/icons-vue": {
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
"integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg=="
},
"@esbuild/linux-loong64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz",
"integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==",
"dev": true,
"optional": true
},
"@floating-ui/core": {
"version": "1.5.2",
"resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.5.2.tgz",
"integrity": "sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==",
"requires": {
"@floating-ui/utils": "^0.1.3"
}
},
"@floating-ui/dom": {
"version": "1.5.3",
"resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.5.3.tgz",
"integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==",
"requires": {
"@floating-ui/core": "^1.4.2",
"@floating-ui/utils": "^0.1.3"
}
},
"@floating-ui/utils": {
"version": "0.1.6",
"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.1.6.tgz",
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
},
"@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
},
"@popperjs/core": {
"version": "npm:@sxzz/popperjs-es@2.11.7",
"resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ=="
},
"@types/lodash": {
"version": "4.14.202",
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.202.tgz",
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ=="
},
"@types/lodash-es": {
"version": "4.17.12",
"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"requires": {
"@types/lodash": "*"
}
},
"@types/web-bluetooth": {
"version": "0.0.16",
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
},
"@vitejs/plugin-vue": {
"version": "1.10.2",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-1.10.2.tgz",
"integrity": "sha512-/QJ0Z9qfhAFtKRY+r57ziY4BSbGUTGsPRMpB/Ron3QPwBZM4OZAZHdTa4a8PafCwU5DTatXG8TMDoP8z+oDqJw==",
"dev": true
},
"@vue/compiler-core": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.4.3.tgz",
"integrity": "sha512-u8jzgFg0EDtSrb/hG53Wwh1bAOQFtc1ZCegBpA/glyvTlgHl+tq13o1zvRfLbegYUw/E4mSTGOiCnAJ9SJ+lsg==",
"requires": {
"@babel/parser": "^7.23.6",
"@vue/shared": "3.4.3",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
},
"dependencies": {
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
}
}
},
"@vue/compiler-dom": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.4.3.tgz",
"integrity": "sha512-oGF1E9/htI6JWj/lTJgr6UgxNCtNHbM6xKVreBWeZL9QhRGABRVoWGAzxmtBfSOd+w0Zi5BY0Es/tlJrN6WgEg==",
"requires": {
"@vue/compiler-core": "3.4.3",
"@vue/shared": "3.4.3"
}
},
"@vue/compiler-sfc": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.4.3.tgz",
"integrity": "sha512-NuJqb5is9I4uzv316VRUDYgIlPZCG8D+ARt5P4t5UDShIHKL25J3TGZAUryY/Aiy0DsY7srJnZL5ryB6DD63Zw==",
"requires": {
"@babel/parser": "^7.23.6",
"@vue/compiler-core": "3.4.3",
"@vue/compiler-dom": "3.4.3",
"@vue/compiler-ssr": "3.4.3",
"@vue/shared": "3.4.3",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5",
"postcss": "^8.4.32",
"source-map-js": "^1.0.2"
},
"dependencies": {
"nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g=="
},
"postcss": {
"version": "8.4.32",
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.32.tgz",
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
"requires": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
}
},
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
}
}
},
"@vue/compiler-ssr": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.4.3.tgz",
"integrity": "sha512-wnYQtMBkeFSxgSSQbYGQeXPhQacQiog2c6AlvMldQH6DB+gSXK/0F6DVXAJfEiuBSgBhUc8dwrrG5JQcqwalsA==",
"requires": {
"@vue/compiler-dom": "3.4.3",
"@vue/shared": "3.4.3"
}
},
"@vue/devtools-api": {
"version": "6.5.1",
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz",
"integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA=="
},
"@vue/reactivity": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.4.3.tgz",
"integrity": "sha512-q5f9HLDU+5aBKizXHAx0w4whkIANs1Muiq9R5YXm0HtorSlflqv9u/ohaMxuuhHWCji4xqpQ1eL04WvmAmGnFg==",
"requires": {
"@vue/shared": "3.4.3"
}
},
"@vue/runtime-core": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.4.3.tgz",
"integrity": "sha512-C1r6QhB1qY7D591RCSFhMULyzL9CuyrGc+3PpB0h7dU4Qqw6GNyo4BNFjHZVvsWncrUlKX3DIKg0Y7rNNr06NQ==",
"requires": {
"@vue/reactivity": "3.4.3",
"@vue/shared": "3.4.3"
}
},
"@vue/runtime-dom": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.4.3.tgz",
"integrity": "sha512-wrsprg7An5Ec+EhPngWdPuzkp0BEUxAKaQtN9dPU/iZctPyD9aaXmVtehPJerdQxQale6gEnhpnfywNw3zOv2A==",
"requires": {
"@vue/runtime-core": "3.4.3",
"@vue/shared": "3.4.3",
"csstype": "^3.1.3"
}
},
"@vue/server-renderer": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.4.3.tgz",
"integrity": "sha512-BUxt8oVGMKKsqSkM1uU3d3Houyfy4WAc2SpSQRebNd+XJGATVkW/rO129jkyL+kpB/2VRKzE63zwf5RtJ3XuZw==",
"requires": {
"@vue/compiler-ssr": "3.4.3",
"@vue/shared": "3.4.3"
}
},
"@vue/shared": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.4.3.tgz",
"integrity": "sha512-rIwlkkP1n4uKrRzivAKPZIEkHiuwY5mmhMJ2nZKCBLz8lTUlE73rQh4n1OnnMurXt1vcUNyH4ZPfdh8QweTjpQ=="
},
"@vueuse/core": {
"version": "9.13.0",
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz",
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
"requires": {
"@types/web-bluetooth": "^0.0.16",
"@vueuse/metadata": "9.13.0",
"@vueuse/shared": "9.13.0",
"vue-demi": "*"
}
},
"@vueuse/metadata": {
"version": "9.13.0",
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz",
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ=="
},
"@vueuse/shared": {
"version": "9.13.0",
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz",
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
"requires": {
"vue-demi": "*"
}
},
"async-validator": {
"version": "4.2.5",
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
},
"available-typed-arrays": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
"dev": true
},
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
}
},
"colorette": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz",
"integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w=="
},
"csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"dayjs": {
"version": "1.11.10",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz",
"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
},
"element-plus": {
"version": "2.4.4",
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.4.4.tgz",
"integrity": "sha512-TlKubXJgxwhER0dw+8ULn9hr9kZjraV4R6Q/eidwWUwCKxwXYPBGmMKsZ/85tlxlhMYbcLZd/YZh6G3QkHX4fg==",
"requires": {
"@ctrl/tinycolor": "^3.4.1",
"@element-plus/icons-vue": "^2.3.1",
"@floating-ui/dom": "^1.0.1",
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
"@types/lodash": "^4.14.182",
"@types/lodash-es": "^4.17.6",
"@vueuse/core": "^9.1.0",
"async-validator": "^4.2.5",
"dayjs": "^1.11.3",
"escape-html": "^1.0.3",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"lodash-unified": "^1.0.2",
"memoize-one": "^6.0.0",
"normalize-wheel-es": "^1.2.0"
}
},
"entities": {
"version": "4.5.0",
"resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
},
"esbuild": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.54.tgz",
"integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==",
"dev": true,
"requires": {
"@esbuild/linux-loong64": "0.14.54",
"esbuild-android-64": "0.14.54",
"esbuild-android-arm64": "0.14.54",
"esbuild-darwin-64": "0.14.54",
"esbuild-darwin-arm64": "0.14.54",
"esbuild-freebsd-64": "0.14.54",
"esbuild-freebsd-arm64": "0.14.54",
"esbuild-linux-32": "0.14.54",
"esbuild-linux-64": "0.14.54",
"esbuild-linux-arm": "0.14.54",
"esbuild-linux-arm64": "0.14.54",
"esbuild-linux-mips64le": "0.14.54",
"esbuild-linux-ppc64le": "0.14.54",
"esbuild-linux-riscv64": "0.14.54",
"esbuild-linux-s390x": "0.14.54",
"esbuild-netbsd-64": "0.14.54",
"esbuild-openbsd-64": "0.14.54",
"esbuild-sunos-64": "0.14.54",
"esbuild-windows-32": "0.14.54",
"esbuild-windows-64": "0.14.54",
"esbuild-windows-arm64": "0.14.54"
}
},
"esbuild-android-64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz",
"integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==",
"dev": true,
"optional": true
},
"esbuild-android-arm64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz",
"integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==",
"dev": true,
"optional": true
},
"esbuild-darwin-64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz",
"integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==",
"dev": true,
"optional": true
},
"esbuild-darwin-arm64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz",
"integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==",
"dev": true,
"optional": true
},
"esbuild-freebsd-64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz",
"integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==",
"dev": true,
"optional": true
},
"esbuild-freebsd-arm64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz",
"integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==",
"dev": true,
"optional": true
},
"esbuild-linux-32": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz",
"integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==",
"dev": true,
"optional": true
},
"esbuild-linux-64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz",
"integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==",
"dev": true,
"optional": true
},
"esbuild-linux-arm": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz",
"integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==",
"dev": true,
"optional": true
},
"esbuild-linux-arm64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz",
"integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==",
"dev": true,
"optional": true
},
"esbuild-linux-mips64le": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz",
"integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==",
"dev": true,
"optional": true
},
"esbuild-linux-ppc64le": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz",
"integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==",
"dev": true,
"optional": true
},
"esbuild-linux-riscv64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz",
"integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==",
"dev": true,
"optional": true
},
"esbuild-linux-s390x": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz",
"integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==",
"dev": true,
"optional": true
},
"esbuild-netbsd-64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz",
"integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==",
"dev": true,
"optional": true
},
"esbuild-openbsd-64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz",
"integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==",
"dev": true,
"optional": true
},
"esbuild-sunos-64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz",
"integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==",
"dev": true,
"optional": true
},
"esbuild-windows-32": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz",
"integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==",
"dev": true,
"optional": true
},
"esbuild-windows-64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz",
"integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==",
"dev": true,
"optional": true
},
"esbuild-windows-arm64": {
"version": "0.14.54",
"resolved": "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz",
"integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==",
"dev": true,
"optional": true
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"events": {
"version": "3.3.0",
"resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"dev": true
},
"for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.3.tgz",
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
"dev": true,
"requires": {
"is-callable": "^1.1.3"
}
},
"fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"optional": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"get-intrinsic": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.3"
}
},
"gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dev": true,
"requires": {
"get-intrinsic": "^1.1.3"
}
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
},
"has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"dev": true
},
"has-tostringtag": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
"dev": true,
"requires": {
"has-symbols": "^1.0.2"
}
},
"hasown": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.0.tgz",
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
"dev": true,
"requires": {
"function-bind": "^1.1.2"
},
"dependencies": {
"function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true
}
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"is-arguments": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/is-arguments/-/is-arguments-1.1.1.tgz",
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"has-tostringtag": "^1.0.0"
}
},
"is-callable": {
"version": "1.2.7",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz",
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"dev": true
},
"is-core-module": {
"version": "2.13.1",
"resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.13.1.tgz",
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
"dev": true,
"requires": {
"hasown": "^2.0.0"
}
},
"is-generator-function": {
"version": "1.0.10",
"resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.0.10.tgz",
"integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
"dev": true,
"requires": {
"has-tostringtag": "^1.0.0"
}
},
"is-typed-array": {
"version": "1.1.10",
"resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.10.tgz",
"integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
"dev": true,
"requires": {
"available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2",
"for-each": "^0.3.3",
"gopd": "^1.0.1",
"has-tostringtag": "^1.0.0"
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"lodash-unified": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz",
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ=="
},
"magic-string": {
"version": "0.30.5",
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.5.tgz",
"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
"requires": {
"@jridgewell/sourcemap-codec": "^1.4.15"
}
},
"memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
},
"nanoid": {
"version": "3.1.25",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz",
"integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q=="
},
"normalize-wheel-es": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
},
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"postcss": {
"version": "8.3.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz",
"integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==",
"requires": {
"colorette": "^1.2.2",
"nanoid": "^3.1.23",
"source-map-js": "^0.6.2"
}
},
"postcss-px-to-viewport": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/postcss-px-to-viewport/-/postcss-px-to-viewport-1.1.1.tgz",
"integrity": "sha512-2x9oGnBms+e0cYtBJOZdlwrFg/mLR4P1g2IFu7jYKvnqnH/HLhoKyareW2Q/x4sg0BgklHlP1qeWo2oCyPm8FQ==",
"requires": {
"object-assign": ">=4.0.1",
"postcss": ">=5.0.2"
}
},
"resolve": {
"version": "1.22.8",
"resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz",
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dev": true,
"requires": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
}
},
"rollup": {
"version": "2.77.3",
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-2.77.3.tgz",
"integrity": "sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==",
"dev": true,
"requires": {
"fsevents": "~2.3.2"
}
},
"source-map-js": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz",
"integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug=="
},
"supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true
},
"util": {
"version": "0.12.5",
"resolved": "https://registry.npmmirror.com/util/-/util-0.12.5.tgz",
"integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"is-arguments": "^1.0.4",
"is-generator-function": "^1.0.7",
"is-typed-array": "^1.1.3",
"which-typed-array": "^1.1.2"
}
},
"vite": {
"version": "2.9.16",
"resolved": "https://registry.npmmirror.com/vite/-/vite-2.9.16.tgz",
"integrity": "sha512-X+6q8KPyeuBvTQV8AVSnKDvXoBMnTx8zxh54sOwmmuOdxkjMmEJXH2UEchA+vTMps1xw9vL64uwJOWryULg7nA==",
"dev": true,
"requires": {
"esbuild": "^0.14.27",
"fsevents": "~2.3.2",
"postcss": "^8.4.13",
"resolve": "^1.22.0",
"rollup": ">=2.59.0 <2.78.0"
},
"dependencies": {
"nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"dev": true
},
"postcss": {
"version": "8.4.32",
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.32.tgz",
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
"dev": true,
"requires": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
}
},
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true
}
}
},
"vue": {
"version": "3.4.3",
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.4.3.tgz",
"integrity": "sha512-GjN+culMAGv/mUbkIv8zMKItno8npcj5gWlXkSxf1SPTQf8eJ4A+YfHIvQFyL1IfuJcMl3soA7SmN1fRxbf/wA==",
"requires": {
"@vue/compiler-dom": "3.4.3",
"@vue/compiler-sfc": "3.4.3",
"@vue/runtime-dom": "3.4.3",
"@vue/server-renderer": "3.4.3",
"@vue/shared": "3.4.3"
}
},
"vue-demi": {
"version": "0.14.6",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.6.tgz",
"integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w=="
},
"vue-router": {
"version": "4.2.5",
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.2.5.tgz",
"integrity": "sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==",
"requires": {
"@vue/devtools-api": "^6.5.0"
}
},
"which-typed-array": {
"version": "1.1.9",
"resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.9.tgz",
"integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
"dev": true,
"requires": {
"available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2",
"for-each": "^0.3.3",
"gopd": "^1.0.1",
"has-tostringtag": "^1.0.0",
"is-typed-array": "^1.1.10"
}
}
}
}
{
"name": "vue3_cli_default",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"element-plus": "^2.4.4",
"postcss-px-to-viewport": "^1.1.1",
"vue": "^3.4.3",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.10.2",
"@vue/compiler-sfc": "^3.4.3",
"events": "^3.3.0",
"util": "^0.12.4",
"vite": "^2.9.16"
}
}
<template>
<div class="mainContainer">
<div class="tool">
<div class="item">窗口宽度</div>
<div class="item">
<el-input v-model="width" size="small" class="input" @blur="resize(0)"></el-input>
</div>
<div class="item">窗口高度</div>
<div class="item">
<el-input v-model="height" size="small" class="input" @blur="resize(0)"></el-input>
</div>
<div class="item">左边距</div>
<div class="item">
<el-input v-model="left" size="small" class="input" @blur="resize(1)"></el-input>
</div>
<div class="item">上边距</div>
<div class="item">
<el-input v-model="top" size="small" class="input" @blur="resize(1)"></el-input>
</div>
<div class="item">分屏样式</div>
<div class="item">
<el-input-number v-model="ShowType" :step="1" size="small" @change="setType()" :min="1">
</el-input-number>
</div>
<el-button size="small" v-if="StartSecond" @click="CloseSecondPlayer">
关闭第二个播放器
</el-button>
<el-button size="small" @click="openSecondPlayer" v-else>
启动第二个播放器
</el-button>
</div>
<div class="tool">
<div class="item">子窗口</div>
<div class="item">
<el-input-number v-model="win" :step="1" size="small" @change="SetSelWnd()" :min="1"></el-input-number>
</div>
<div class="item">
<el-button size="small" @click="getCapture">抓图</el-button>
</div>
<el-button size="small" @click="watermask">设置水印</el-button>
<el-button size="small" @click="RecordToFile">
<span v-if="isRecordFile">停止录像</span>
<span v-else>本地录像</span>
</el-button>
<div class="item" style="margin-left: 10px;">
<el-button size="small" @click="NextFrame()" type="primary">下一帧</el-button>
</div>
<div class="item">
<el-button size="small" @click="PlayPause()" type="primary">暂停播放</el-button>
</div>
<div class="item">
<el-button size="small" @click="PlayContinue()" type="primary">继续播放</el-button>
</div>
<div class="item">
<el-button size="small" @click="PlaySpeed()" type="primary">加速播放</el-button>
</div>
<div class="item">
<el-button size="small" @click="PlayMute()" type="primary">播放静音</el-button>
</div>
<div class="item">
<el-button size="small" @click="PlayFullScreen()" type="primary">全屏播放</el-button>
</div>
</div>
<div class="video-container" ref="player">
播放器区域
</div>
<div class="urlbox">
<div class="item">视频地址</div>
<div class="item input">
<el-input v-model="newrtsp" placeholder="这里演示动态切换播放源" size="small"></el-input>
</div>
<div class="item">旋转</div>
<div class="item">
<el-select size="small" v-model="Transform" placeholder="请选择" style="width: 100PX;">
<el-option label="none" value="none">
</el-option>
<el-option label="90" value="90">
</el-option>
<el-option label="180" value="180">
</el-option>
<el-option label="270" value="270">
</el-option>
<el-option label="hflip" value="hflip">
</el-option>
<el-option label="vflip" value="vflip">
</el-option>
<el-option label="transpose" value="transpose">
</el-option>
<el-option label="antitranspose" value="antitranspose">
</el-option>
</el-select>
</div>
<div class="item" style="margin-left: 10px;">
<el-button type="primary" @click="changUrl()" size="small">切换源播放</el-button>
</div>
</div>
<div class="urlbox">
<div class="item">叠加透明网页地址</div>
<div class="item input">
<el-input v-model="newfloatweb" placeholder="这里演示叠加网页内容显示" size="small"></el-input>
</div>
<div class="item" style="margin-left: 10px;">
<el-button type="primary" @click="FloatWebInfo()" size="small">叠加显示</el-button>
</div>
</div>
<div class="tool">
<div class="item">字幕</div>
<div class="item" style="flex: 1;">
<el-input size="small" placeholder="请输入内容" v-model="danmu.text"></el-input>
</div>
<div class="item">
<el-select size="small" v-model="danmu.position" placeholder="请选择" style="width: 100PX;">
<el-option label="顶部" value="TOP">
</el-option>
<el-option label="底部" value="BOTTOM">
</el-option>
</el-select>
</div>
<div class="item">位置(x)</div>
<div class="item">
<el-input v-model="danmu.x" size="small" class="input"></el-input>
</div>
<div class="item">位置(y)</div>
<div class="item">
<el-input v-model="danmu.y" size="small" class="input"></el-input>
</div>
<div class="item">透明度</div>
<div class="item">
<el-input v-model="danmu.opacity" size="small" class="input"></el-input>
</div>
<div class="item">大小</div>
<div class="item">
<el-input v-model="danmu.size" size="small" class="input"></el-input>
</div>
<div class="item">
<el-button size="small" @click="setText()" type="primary">设置</el-button>
</div>
</div>
<div class="tool">
<div class="item">多源播放JSON</div>
<el-input v-model="WebCfg2" size="small"></el-input>
<div class="item">
<el-button size="small" @click="RePlayFirst()" type="primary">重新启动播放</el-button>
</div>
</div>
<div class="tool">
<div class="item" style="margin-left: 10px;">
<el-button type="primary" @click="StopAllPlay()" size="small">停止所有播放</el-button>
</div>
<div class="item" style="margin-left: 10px;">
<el-button type="primary" @click="CloseAllPlayer()" size="small">关闭所有播放器</el-button>
</div>
<div class="item">最新版本号</div>
<div class="item">
<el-input v-model="version" size="small"></el-input>
</div>
<el-button size="small" @click="CheckUpdate" type="primary">校验升级</el-button>
</div>
<el-input type="textarea" :rows="5" placeholder="调试日志" v-model="DebugLog" class="DebugLog">
</el-input>
</div>
</template>
<script setup>
//集成开发说明:所有交互都是通过JS建立websocket连接后,发送或接收JSON包进行,都是异步请求,发送和接收包都携带rid用来标识请求返回包对应是哪个请求的结果
//所有发送的msg中用到的长整形rid 可以自己指定 不同的发送请求定义唯一的rid 这样可以在回调用轻松判断
//针对一些特殊请求,可以根据自己的实际情况把rid固定,如启动网页播放器时指定固定rid获得启动网页播放器的实例ID
//首先建立第一个websocket连接到中间件服务端口启动RTSP网页播放器 然后获得启动RTSP网页播放器的侦听端口建立第二个websocket连接 主要用来进行控制播放、抓图、录像等操作
//释放网页播放器,直接关闭建立的第一个websocket连接即可
import GetDefaultConn from "./common/base.js"
//加载websoket类
import websocket from './common/websocket'
import {ref,reactive, computed,onMounted,onUnmounted} from 'vue'
import { ElMessage,ElMessageBox } from 'element-plus'
const player = ref()
let version = ref('2.2.12.3') //中间件版本信息
let aid = 0 // 第一个播放器实例ID
let aid2 = 0 // 第二个播放器实例ID
let curID = 0 // 当前操作播放器实例ID
let rid = 10 // 其它请求起始序号
let runInfo = 1 // 获取本机VLC安装信息 一般只需要在启动时获取一次
let RunFirst = 2 // 播放器启动实例1序号
let RunSecond = 3 // 播放器启动实例2序号
let win = 1 // 播放实例中当前操作的分屏窗口序号ID
let newrtsp = 'http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8'
let newfloatweb = ref('https://output.jsbin.com/dopavun')
let WebCfg = ref('[{"ID":2,"Uri":"https://vjs.zencdn.net/v/oceans.mp4"},{"ID":1,"Uri":"https://media.w3.org/2010/05/sintel/trailer.mp4","Option":":rtsp-tcp"},{"ID":4,"Uri":"http://www.zorrosoft.com/Files/PluginOKBrowserApplet.mp4","Option":":file-caching=300"},{"ID":3,"Uri":"http://www.zorrosoft.com/Files/h265.mkv","Option":":network-caching=500"}]')
let WebCfg2 = ref('[{"ID":1,"Uri":"https://vjs.zencdn.net/v/oceans.mp4"},{"ID":2,"Uri":"https://media.w3.org/2010/05/sintel/trailer.mp4","Option":":rtsp-tcp"},{"ID":3,"Uri":"http://www.zorrosoft.com/Files/PluginOKBrowserApplet.mp4","Option":":file-caching=300"},{"ID":4,"Uri":"rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp","Option":":network-caching=500"}]')
let RecordFilePath = ref('C:/Zorro/test.mp4')
let Transform = 'none'
let recordid = 0 //录像时返回的录像进程PID
let isRecordFile = false //记录录像的状态
let danmu = {
//需要发送的字幕配置信息
text: '您好呀,O(∩_∩)O哈哈~',
position: 'TOP',
color: "#ff0000",
opacity: 128,
size: 50,
x: 0,
y: 0
}
let StartSecond = ref(0) // 是否启动了第二个网页播放器实例
let ReStartLoad = ref(0) // 第一个网页播放器实例是否重新打开状态
let width = ref(960) // 网页播放器窗口显示宽度
let height = ref(480) // 网页播放器窗口显示高度
let left = ref(0) // 网页播放器窗口以网页左上角为基点显示的X位置
let top = ref(0) // 网页播放器窗口以网页左上角为基点显示的Y位置
let IframeX = -10 // 网页播放器窗口模拟iFrame用的X坐标
let IframeY = 0 // 网页播放器窗口模拟iFrame用的Y坐标
let ShowType = 1 //分屏样式
let isConnService = false //是否连接的中间件服务
let isDisConnect = false // 是否处于断开连接过程
let socket = [] //websocket对象数组,采用数组需要注意维护索引号,可以改为根据连接的唯一SID参数存到对应的集合,需要用到连接时根据SID提取,这样可支持启动更多实例
let result = reactive([]) //日志结果数组
const DebugLog = computed(() => {
return result.join("\n")
})
onMounted(()=>{
//初始化配置
init()
/// 响应改变浏览器窗口大小
window.onresize = ()=>{
pageResize()
}
})
onUnmounted(()=>{
//关闭所有websocket链接以及浏览器监听
close()
window.onresize = null
})
function init() {
//监听浏览器切换标签页面
if(document.addEventListener)
document.addEventListener('visibilitychange', handleVisiable,false)
else
document.attachEvent('visibilitychange',handleVisiable,false) /// 老版本浏览器
//侦听滚动条
window.addEventListener('scroll', windowScroll, true)
//监听当页面离开时候
window.addEventListener('unload', unloadHandler,false)
//获取网页播放器位置节点信息
GetAppletPosition()
//先获取本机VLC软件安装信息
GetPlayerInfo()
}
function windowScroll() {
// 滚动条距离页面顶部的距离
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
if(aid > 0)
appScroll(0, aid, scrollTop)
if (aid2 > 0)
appScroll(2, aid2, scrollTop)
}
function appScroll(si, id, scrollTop) {
if(id)
{
// 默认纵向滚动网页播放器实例,如需要横向滚动,Code设置为1,修改Left的值
// NoLog指示服务日志不输出相关日志,因为时间比较多,输出日志导致日志文件信息过多
rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletScroll",
"rid": rid,
"para": {
"ID": id,
"NoLog": 1,
"Code": 2,
"Left": 0,
"Top": Math.round(scrollTop)
}
}
socket[si].sendObj(msg)
}
}
function GetAppletPosition() {
//获取网页播放器位置节点信息
let nScrollTop = 0
let nScrollLeft = 0
let bHorizontalBar =hasHorizontalScrollbar()
let bVerticalBar = hasVerticalScrollbar()
if(bHorizontalBar){
if(window.pageXOffset != undefined)
nScrollLeft = Math.round(window.pageXOffset)
else
{
//获取页面的scrollLeft(兼容写法)
nScrollLeft = Math.round(document.documentElement.scrollLeft || document.body.scrollLeft)
}
}
if(bVerticalBar){
if(window.pageYOffset != undefined)
nScrollTop = Math.round(window.pageYOffset)
else
{
//获取页面的scrollTop(兼容写法)
nScrollTop = Math.round(document.documentElement.scrollTop || document.body.scrollTop)
}
}
let react = player.value.getBoundingClientRect()
console.log(react)
left.value = Math.round(react.left + nScrollLeft)
top.value = Math.round(react.top + nScrollTop)
width.value = Math.round(react.width)
height.value = Math.round(react.height)
}
function handleVisiable(e) {
//浏览器页面切换侦听回调函数
if (e.target.visibilityState == 'hidden') {
//切离该页面时执行 当前页自己实现标签切换或需要在RTSP网页播放器程序窗口区域临时显示内容时,设置32强制隐藏
hideApp(4)
} else if (e.target.visibilityState == 'visible') {
//切换到该页面时执行
showApp()
}
}
function hasVerticalScrollbar(){
if(document.documentElement.clientHeight)
return document.body.scrollHeight > document.documentElement.clientHeight
return document.body.scrollHeight > window.innerHeight
}
function hasHorizontalScrollbar(){
if(document.documentElement.clientWidth)
return document.body.scrollWidth > document.documentElement.clientWidth
return document.body.scrollWidth > window.innerWidth
}
function pageResize(){
if(aid > 0)
SendScrollInfo(0,aid)
if(aid2 > 0)
SendScrollInfo(2,aid2)
}
function SendScrollInfo(si,id){
let nScrollTop = 0
let nScrollLeft = 0
let BarCode = 0
let bHorizontalBar = hasHorizontalScrollbar()
let bVerticalBar = hasVerticalScrollbar()
if(bHorizontalBar){
if(window.pageXOffset != undefined)
nScrollLeft = Math.round(window.pageXOffset)
else
{
//获取页面的scrollLeft(兼容写法)
nScrollLeft = Math.round(document.documentElement.scrollLeft || document.body.scrollLeft)
}
}
if(bVerticalBar){
if(window.pageYOffset != undefined)
nScrollTop = Math.round(window.pageYOffset)
else
{
//获取页面的scrollTop(兼容写法)
nScrollTop = Math.round(document.documentElement.scrollTop || document.body.scrollTop)
}
}
if(bHorizontalBar)
BarCode = 1
if(bVerticalBar)
BarCode += 2
rid++ // 增加序号
/// 设置页码滚动信息,BarW BarH分别为预留右侧宽度和底部高度
let msg = {
"req": "Wrl_ScrollBar",
"rid": rid,
"para": {
"ID": id,
"BarW": 0,
"BarH": 0,
"Code": BarCode,
"Left": nScrollLeft,
"Top": nScrollTop
}
}
console.log(msg)
socket[si].sendObj(msg)
}
function unloadHandler() {
//关闭所有websoket链接
close()
}
function close() {
//关闭网页播放器实例
CloseAllPlayer()
isDisConnect = true
//关闭第一个websoket链接
socket[0].disconnect()
socket.pop()
isDisConnect = false
//关闭侦听浏览器页面切换
if(document.addEventListener)
document.removeEventListener('visibilitychange', handleVisiable,false)
else
document.detachEvent('visibilitychange',handleVisiable,false) /// 老版本浏览器
//关闭侦听滚动条
window.removeEventListener('scroll', windowScroll), true
//关闭侦听滚动条
window.removeEventListener('unload', unloadHandler,false)
}
function GetPlayerInfo()
{
isConnService = true
let Protocol = location.protocol;
if (Protocol.toUpperCase().indexOf('HTTPS') > -1)
{
// HTTPS网站,连接WSS侦听端口
openWebsocket(453,0)
//openWebsocket(453,1) 支持安全校验机制
}
else
{
openWebsocket(83,0)
//openWebsocket(83,1) 支持安全校验机制
}
let msg = {
// 这里如果没有用到LibVLC引擎播放,也可以调用指令 Wrl_GetVideoCard 获取本机显卡的信息,返回包括是否有独立显卡,显卡名称及型号等,用于多路播放时决策可以播多少路
"req": "Wrl_PlayerInfo",
"rid": runInfo,
"para": {}
}
socket[0].sendObj(msg)
}
function StartPlayerApplet() {
//启动第一个VLC网页播放器 Open为播放源,播放源也可以放到Web节点中,参考RePlayFirst中实现,差异在于,Open中指定对所有分屏有效,而在Web节点中可指定更多播放参数
let msg = {
"req": "Wrl_VLCWebPlayer",
"rid": RunFirst,
"para": {
"Type": "0",
"Title": "VLC网页播放器",
"Version": 0,// 设置1时代表启用独立进程来播放,独立进程播放好处是播放时如果出现进程崩溃,不会导致播放组件进程退出
"Flag": 578,
"Left": left.value,
"Top": top.value,
"Width": width.value,
"Height": height.value,
"IframeX": IframeX,
"IframeY": IframeY,
"BarW": 0,
"BarH": 0,
"ScrollTop": 0,
"Web": [{"ID":1,"Uri":"http%3A%2F%2Fwww.zorrosoft.com%2FFiles%2FPluginOKBrowserApplet.mp4","Option":":rtsp-tcp :network-caching=500"}],
"ShowType": ShowType
}
}
console.log(msg)
socket[0].sendObj(msg)
}
function openSecondPlayer() {
if(aid)
{
//演示加载第二个RTSP网页播放器实例打开表格程序 先改变第一个网页播放器位置 Flag值需要在原来基础上+512以支持多实例加载
width.value = 475
resize(0)
isConnService = true
let Protocol = location.protocol;
if (Protocol.toUpperCase().indexOf('HTTPS') > -1)
{
// HTTPS网站,连接WSS侦听端口
openWebsocket(453,0)
//openWebsocket(453,1) 支持安全校验机制
}
else
{
openWebsocket(83,0)
//openWebsocket(83,1) 支持安全校验机制
}
let msg = {
"req": "Wrl_VLCWebPlayer",
"rid": RunSecond,
"para": {
"Type": "0",
"Title": "VLC网页播放器2",
"Version": 1,
"Flag": 578,
"Left": left.value + 485,
"Top": top.value,
"Width": width.value,
"Height": height.value,
"IframeX": -10,
"IframeY": 0,
"BarW": 0,
"BarH": 0,
"ScrollTop": 0,
"Web": JSON.parse(WebCfg.value),
"ShowType": 4
}
}
socket[2].sendObj(msg)
StartSecond.value = 1
}
else{
ElMessage('请先启动第一个网页播放器')
}
}
function CloseSecondPlayer() {
if (StartSecond.value) {
isDisConnect = true
/// 每个网页播放器实例占用2个连接,一个到中间件、一个到网页播放器,分别断开连接并释放
socket[3].disconnect()
socket[2].disconnect()
socket.pop()
socket.pop()
isDisConnect = false
StartSecond.value = 0
aid2 = 0
//还原第一个网页播放器位置
width.value = 960
height.value = 480
//获取网页播放器位置节点信息
let react = player.value.getBoundingClientRect()
left.value = react.left
top.value = react.top
resize(0)
}
}
function CloseFirstPlayer()
{
if (aid > 0)
{
rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletControl",
"rid": rid,
"para": {
"ID": aid,
"Code": 1
}
}
isDisConnect = true
/// 释放网页播放器的连接
socket[1].disconnect()
socket[0].sendObj(msg)
aid = 0
ReStartLoad.value = 0
if(StartSecond.value)
socket.splice(1,1)
else
socket.pop()
isDisConnect = false
}
}
function RePlayFirst()
{
CloseFirstPlayer()
ReStartLoad.value = 1
//重新启动播,Web节点配置播放源,设置分屏风格4,播放引擎从VLC改为FFPlayer
ShowType = 4
let Msg = {
"req": "Wrl_VLCWebPlayer",
"rid": RunFirst,
"para": {
"Type": "0",
"Title": "VLC网页播放器",
"Version": 0,
"Flag": 578,
"Left": left.value,
"Top": top.value,
"Width": width.value,
"Height": height.value,
"IframeX": IframeX,
"IframeY": IframeY,
"BarW": 0,
"BarH": 0,
"ScrollTop": 0,
"Web": JSON.parse(WebCfg2.value),
"ShowType": ShowType
}
}
socket[0].sendObj(Msg)
}
function CloseAllPlayer()
{
/// 先关闭第二个实例,否则socket中保存的连接序号会不正常
CloseSecondPlayer()
CloseFirstPlayer()
}
function StopSecondPlayer()
{
if (StartSecond.value)
{
rid++ // 增加序号
let msg = {
"req": "VLC_Control",
"rid": rid,
"para": [{
"ID": 1,
"Type": "Stop"
},{
"ID": 2,
"Type": "Stop"
},{
"ID": 3,
"Type": "Stop"
},{
"ID": 4,
"Type": "Stop"
}]
}
console.log(msg)
socket[3].sendObj(msg)
}
}
function StopFirstPlayer()
{
if (aid > 0)
{
rid++ // 增加序号
/// 支持批量停止播放 暂停播放等操作 para中具体传多少个分屏窗口数据自己定,分屏窗口数量由ShowType决定
/// ID是分屏窗口序号,从1开始 序号原则是从左向右开始编号,然后从上到下开始顺序编号,如遇右侧多层排列窗口时,直到右侧窗口序号排序完成
/// Type可以是这些值:Pause,Play,Stop,StopAsync,Next,Prev,Clear,TogglePause 分别对应暂停播放 播放 停止播放 异步停止播放 下一个播放源 上一个播放源 清理播放列表 切换暂停状态
let msg = {}
if(ReStartLoad.value)
{
msg = {
"req": "VLC_Control",
"rid": rid,
"para": [{
"ID": 1,
"Type": "Stop"
},{
"ID": 2,
"Type": "Stop"
},{
"ID": 3,
"Type": "Stop"
},{
"ID": 4,
"Type": "Stop"
}]
}
}
else{
/// 不指定分屏信息时,停止所有播放
msg = {
"req": "VLC_Control",
"rid": rid,
"para": []
}
}
console.log(msg)
socket[1].sendObj(msg)
}
}
function StopAllPlay()
{
StopSecondPlayer()
StopFirstPlayer()
}
function openWebsocket(port,type) {
//打开websocket服务
let ws = GetDefaultConn(port,type)
const socketClient = new websocket(ws, {
reconnectEnabled: false
})
socketClient.connect()
if(1 == ReStartLoad.value && StartSecond.value)
{
ReStartLoad.value = 2
socket.splice(1,0,socketClient) // 重新加载时已经启动第二个实例,采用插入方式,避免WS下标错误,确保第一个实例的WS连接下标是0和1,第二个实例是3和4
}
else
{
if(1 == ReStartLoad.value)
ReStartLoad.value = 2
socket.push(socketClient)
}
socketClient.onMessage = (msg) =>
{
let res = JSON.parse(msg.data)
console.log(res)
if(res.rid == runInfo)
{
// 解析当前VLC桌面软件安装情况
if(res.data.Info.size)
{
result.push("VLC安装路径:" + res.data.Info[0].Path)
}
StartPlayerApplet()
}
if (res.event == 'Wrl_AppletOK') {
//网页播放器创建成功后根据事件名称获取当前网页播放器id
if (res.rid == RunFirst) {
aid = res.aid
SendScrollInfo(0,aid)
console.log(aid)
}
if (res.rid == RunSecond) {
aid2 = res.aid
SendScrollInfo(2,aid2)
console.log(aid2)
}
}
if (res.event == 'Wrl_Listen') {
//网页播放器建立侦听成功 这里得到当前网页播放器返回的端口
//这里创建另外一个websocket到RTSP网页播放器端口 来实现重新打开文档、提取图片、保存、书签等操作
setTimeout( ()=>{
isConnService = false
openWebsocket(res.data.port,0)
}, 200);
if(res.aid > 0)
{
curID = res.aid
//result.push("当前操作网页播放器实例 " + res.aid)
}
}
//切换窗口的时候 设定当前窗口为此窗口
if (res.event == 'VLC_Selected') {
win = res.ID // 选中的分屏窗口序号
if(res.aid > 0)
{
curID = res.aid
//result.push("当前操作播放器实例 " + res.aid)
}
}
// 播放窗口收到鼠标按下通知
if (res.event == 'VLC_MouseDown') {
if(res.aid > 0)
{
curID = res.aid
//result.push("当前操作播放器实例 " + res.aid)
}
}
// 定时录像结束通知
if (res.event == 'VLC_StopRecord') {
isRecordFile = false;
recordid = 0;
$message.success("定时录像成功\n" + res.data.File);
}
// 设置文件保存位置结果通知
if (res.event == 'Wrl_SelectFile') {
if(res.data.length)
{
BeginRecordFile(res.data[0].File) /// 使用返回的文件位置
}
else
BeginRecordFile(RecordFilePath.value) // 取消指定,使用默认位置
}
//请求录像返回
if (res.rid == 90000) {
//记录录像的pid
recordid = res.data.PID
}
//请求停止录像返回
if (res.rid == 90001) {
recordid = 0;
$message.success("录像成功\n" + res.data.File)
//console.log(res.data.File)
}
//请求截图返回
if (res.rid == 90002) {
$message.success("截图成功\n" + res.data.Img[0].File)
//console.log(res.data.Img[0].File)
}
if (res.err)
{
// 请求返回错误
$message.success(res.err)
}
if (res.req == 'Wrl_Version')
{
if (res.data.Update == 1) {
/// 先强制隐藏网页播放器,避免弹框显示不出来
hideApp(32);
ElMessageBox.confirm('有新版本发布, 是否马上升级?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
/// 先提前关闭网页播放器
CloseAllPlayer()
SendUpdateJson()
}).catch(() => {
showApp()
})
} else {
ElMessage('已经是最新版本!')
/// 说明:即使版本号是最新的,也是可以发起升级请求SendUpdateJson()的,相当于覆盖安装,用于更新比如中间件或网页播放器的配置文件等
}
}
//记录日志
result.push(msg.data)
}
socketClient.onClose = (msg) => {
console.log(msg)
}
socketClient.onError = (msg) => {
// 避免IE中点击重复播放及firefox断开连接提示等问题
if(!ReStartLoad.value && isConnService && !isDisConnect)
{
//连接不上,认为还没有安装 PageHiPlayer-VLC网页播放器 没有安装时提示安装
ElMessageBox.confirm('PageHiPlayer-VLC网页播放器 服务端口连接失败,可能是尚未安装,是否马上下载安装?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
window.open('http://local.zorrosoft.com/Files/RtspWebPlayerIns.exe') // 建议打包为zip等格式下载,解压后安装,EXE文件下载浏览器会提示是否保留
}).catch(() => {
})
}
}
}
function resize(position) {
//请求改变网页播放器实例显示位置或大小,如不需要改变显示位置,不传X和Y
if(aid2 > 0 && aid2 == curID){
rid++ // 增加请求序号
if(position){
let msg = {
"req": "Wrl_AppletResize",
"rid": rid,
"para": {
"ID": aid2,
"X": left.value,
"Y": top.value,
"Width": width.value,
"Height": height.value
}
}
socket[2].sendObj(msg)
}
else{
let msg = {
"req": "Wrl_AppletResize",
"rid": rid,
"para": {
"ID": aid2,
"Width": width.value,
"Height": height.value
}
}
socket[2].sendObj(msg)
}
}
else
{
if(aid > 0){
rid++ // 增加请求序号
if(position){
let msg = {
"req": "Wrl_AppletResize",
"rid": rid,
"para": {
"ID": aid,
"X": left.value,
"Y": top.value,
"Width": width.value,
"Height": height.value
}
}
socket[0].sendObj(msg)
}
else{
let msg = {
"req": "Wrl_AppletResize",
"rid": rid,
"para": {
"ID": aid,
"Width": width.value,
"Height": height.value
}
}
socket[0].sendObj(msg)
}
}
}
}
function getCapture()
{
//请求截图,固定请求ID处理,也可以是建立一个map记录每个rid对应的含义,请求返回里再处理
let msg = {
"req": "VLC_VideoSnapshot",
"rid": 90002,
"para": [{
"ID": win,
"Type": 4,
"Count": 1,
"Base64":0,
"Delay": 1000,
"Interval": 200,
"PathType": 1
}]
}
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function setText()
{
//发送字幕到指定的窗口ID
rid++ // 增加请求序号
let msg = {
"req": "VLC_MarqueePut",
"rid": rid,
"para": [{
"ID": win,
"Text": danmu.text,
"Position": danmu.position,
"Timeout": 0,
"Color": danmu.color,
"Opacity": danmu.opacity,
"Refresh": 1,
"Size": danmu.size,
"X": danmu.x,
"Y": danmu.y
}]
}
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function setType()
{
//动态改变屏幕数量
rid++ // 增加请求序号
let msg = {
"req": "VLC_ChangePlay",
"rid": rid,
"para": {
"ShowType": ShowType
}
}
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function NextFrame()
{
//请求显示下一帧 请求后播放进入暂停状态,可继续请求下一帧,或请求播放恢复正常播放状态
rid++ // 增加请求序号
let msg = {
"req": "VLC_NextFrame",
"rid": rid,
"para": [{
"ID":win,
"Count": 1
}]
}
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function PlayPause()
{
//请求播放进入暂停状态
rid++ // 增加请求序号
let msg = {
"req": "VLC_Control",
"rid": rid,
"para": [{
"ID":win,
"Type": "Pause"
}]
}
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function PlayContinue()
{
//请求播放继续,或请求播放恢复正常播放状态
rid++ // 增加请求序号
let msg = {
"req": "VLC_Control",
"rid": rid,
"para": [{
"ID":win,
"Type": "Play"
}]
}
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function PlayMute()
{
// 设置选中分屏窗口视频静音,1静音 0不静音
rid++ // 增加请求序号
let msg = {
"req": "VLC_AudioPut",
"rid": rid,
"para": [{
"ID":win,
"Mute": 1
}]
}
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function PlaySpeed()
{
// 调整播放速度,仅限于文件或回放流,支持批量操作,当前演示针对选中分屏窗口设置2倍数播放
rid++ // 增加请求序号
let msg = {
"req": "VLC_PutInputInfo",
"rid": rid,
"para": [{
"ID":win,
"Rate": 2
}]
}
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function PlayFullScreen()
{
// 设置选中分屏窗口全屏显示 全屏后按ESC 字幕F\、双击或点击工具栏全屏图标可退出全屏状态
rid++ // 增加请求序号
let msg = {
"req": "VLC_VideoToggleFullscreen",
"rid": rid,
"para": [{
"ID":win
}]
}
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function SetSelWnd() {
//设置选中分屏子窗口
rid++ // 增加请求序号
let msg = {
"req": "VLC_SetSelect",
"rid": rid,
"para": {
"ID": win
}
}
if(aid2 > 0 && aid2 == curID)
{
result.push("执行播放器实例 " + curID)
socket[3].sendObj(msg)
}
else
socket[1].sendObj(msg)
}
function AppletSnap()
{
// 整个播放窗口抓图,包含所有分屏子窗口,发送到中间件侦听端口执行
rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletSnap",
"rid": rid,
"para": {
"ID": curID,
"File": ".jpg",
"Base64": 1
}
}
if(aid2 > 0 && aid2 == curID)
socket[2].sendObj(msg)
else
socket[0].sendObj(msg)
}
function FullApplet()
{
// 整个播放窗口全屏,包含所有分屏子窗口,发送到中间件侦听端口执行
rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletControl",
"rid": rid,
"para": {
"ID": curID,
"Code": 2
}
}
if(aid2 > 0 && aid2 == curID)
socket[2].sendObj(msg)
else
socket[0].sendObj(msg)
}
function RecordToFile()
{
//对指定的窗口ID进行录像 固定请求ID处理,也可以是建立一个map记录每个rid对应的含义,请求返回里再处理
if (isRecordFile) {
let msg = {
"req": "VLC_StopRecord",
"rid": 90001,
"para": {
"ID": win
}
}
isRecordFile = false
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
} else {
rid++ // 增加请求序号
/// 先设置保存位置再录像
let msg = {
"req": "Wrl_SelectFile",
"rid": rid,
"para": {
"Type": 1,
"Title" : "请设置录像文件存放位置",
"Ext" : "录像文件(*.mp4)\r*.mp4"
}
}
if(aid2 > 0 && aid2 == curID)
socket[2].sendObj(msg)
else
socket[0].sendObj(msg)
}
}
function BeginRecordFile(LocalFilePath)
{
let msg = {
"req": "VLC_RecordFile",
"rid": 90000,
"para": {
"ID": win,
//不指定Url时取当前焦点分屏窗口源进行录像
//"Url": encodeURIComponent(newrtsp),
"File": encodeURIComponent(LocalFilePath),
"Second": 30
}
}
isRecordFile = true
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function watermask()
{
//对指定的窗口ID发送水印,只支持VLC引擎播放,其它引擎播放,可采用叠加透明网页的方式,参考FloatWebInfo的实现
rid++ // 增加请求序号
let msg = {
"req": "VLC_PutLogoShow",
"rid": rid,
"para": [{
"ID": win,
"File": "VLC.png",
"Delay": 20,
"Repeat": -1,
"Opacity": 128,
"X": 100,
"Y": 100
}]
}
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function changUrl()
{
//动态改变指定的窗口ID的播放源,VLC_ChangePlay本身支持同时改变多个分屏窗口播放源,Play支持传数组 ForceDestroy 指定是否先销毁原来播放引擎 默认不销毁
rid++ // 增加请求序号
let msg = {
"req": "VLC_ChangePlay",
"rid": rid,
"para": {
"ForceDestroy":0,
"Play": [{
"ID": win,
"Uri": encodeURIComponent(newrtsp),
"Name": "new rtsp",
"Option": `:rtsp-tcp Transform=${Transform}`
}]
}
}
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function FloatWebInfo()
{
//对指定的窗口ID叠加网页内容显示
rid++ // 增加请求序号
let msg = {
"req": "VLC_FloatWebInfo",
"rid": rid,
"para": [{
"ID": win,
"Url": encodeURIComponent(newfloatweb.value),
"Rect":{"P":4,"W":300,"H":300}
}]
}
if(aid2 > 0 && aid2 == curID)
socket[3].sendObj(msg)
else
socket[1].sendObj(msg)
}
function showApp() {
//显示RTSP网页播放器
if (aid > 0) {
rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletControl",
"rid": rid,
"para": {
"ID": aid,
"Code": 8
}
}
socket[0].sendObj(msg)
}
//如果启动了第二个网页播放器 也显示
if (aid2 > 0) {
rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletControl",
"rid": rid,
"para": {
"ID": aid2,
"Code": 8
}
}
socket[2].sendObj(msg)
}
}
function hideApp(code) {
//隐藏网页播放器 Code设置4是自动隐藏,如需强制隐藏,设置为32
if (aid > 0)
{
rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletControl",
"rid": rid,
"para": {
"ID": aid,
"Code": code
}
}
socket[0].sendObj(msg)
}
//如果启动了第二个网页播放器 也同时隐藏
if (aid2 > 0)
{
rid++ // 增加请求序号
let msg = {
"req": "Wrl_AppletControl",
"rid": rid,
"para": {
"ID": aid2,
"Code": code
}
}
socket[2].sendObj(msg)
}
}
function CheckUpdate() {
//校验中间件版本是不是需要升级,如果额外指定PID参数,代表校验PID代表的网页播放器,Wrl_Version功能多
rid++ // 增加请求序号
let msg = {
"req": "Wrl_Version",
"rid":rid,
"para": {
"Version": version.value
}
}
socket[0].sendObj(msg)
}
function SendUpdateJson() {
// 发送中间件的升级命令,实现自动升级,同时升级网页播放器
// 注意:Wrl_Update中的请求参数如MD5 TK Size等,请根据文档“中间件制作升级包说明.pdf”中的打包工具生成,此处举例的升级包是在线公测版的,正式版需要自己制作
rid++ // 增加请求序号
let msg = {
"req":"Wrl_Update",
"rid":rid,
"para":{
"Name":"VLC网页播放器升级包",
"Date":"2023-12-31",
"Desc":"1、中间件高级版内嵌小程序支持联想浏览器中加载使用;2、优化中间件访问数据文件操作及服务状态监控方式;3、PageHiPlayer海康私有协议及VLC播放引擎支持框选区域放大显示并支持前端接口调用,解决在X64系统下安装32位VLC无法使用问题,解决VLC切换播放源时可能没有成功通知;4、PageHiPlayer网页播放器增加视频长宽信息通知到前端,完善热键处理,视频画面全屏时实现自动隐藏任务栏,播放错误日志输出到单独的文件中...",
"DownAddr":"http://local.zorrosoft.com/Files/Update/RTSP_Update.pid",
"Open":"http://local.zorrosoft.com/vlc",
"MD5":"0BAB22C1631E508EC0C8E000FBF80AE1",
"Version":"2.2.12.3",
"Size":48463872,
"HideIns":0,
"Cookie":"",
"Auth":"",
"TK":"AC3B7B95CF42EE17939C1CEB5F6F1C9F0CE97DE39C80F51A0E710D876401802E1252665005EF2DF7230C0995D7787BB7D3568AE435D1160571F6CC7E40EEE56911A47467D6D9E3BCE74284E4BB6AB848B96C950EAB762CB106C0125B8EF28918A5555C5F1B2B9E0C648D8D8AB1B08C69436BED3B827068651A4A159EEC069543D7C0054B1B775212D20E5135E5CAA2BF4ACFEBF1348C3FAB3263221871F0F29D51F7F79DBA0A51C23872774551B009A727036A49CBFDCB44CC1E1252206ADED55826B549C5787360902619530E70572EAD0E79124692ADECBAC284A84D68357ADE4154CC73DDE94266B3BFD1F71006B50B32687F4C8867C801CE79FDA257AB30"
}
}
socket[0].sendObj(msg)
}
</script>
<style scoped>
.mainContainer {
display: block;
width: 960px;
margin-left: 20px;
margin-right: auto;
box-sizing: border-box;
}
.urlbox {
display: flex;
display: -ms-flexbox;
align-items: center;
-ms-flex-align: center;
justify-content: space-between;
margin: 15px 0;
}
.urlbox .input {
flex: 1;
margin: 0 15px;
}
.tool {
display: flex;
display: -ms-flexbox;
align-items: center;
-ms-flex-align: center;
margin: 15px 0;
}
.tool .item {
margin-right: 15px;
font-size: 12px;
}
.tool .item:last-child {
margin-right: 0;
}
.tool .input {
width: 60px;
}
.video-container {
position: relative;
margin-top: 8px;
height: 320px;
border: #ddd 1px dashed;
display: flex;
display: -ms-flexbox;
align-items: center;
-ms-flex-align: center;
justify-content: center;
font-size: 24px;
color: #ddd;
}
</style>
\ No newline at end of file
function GetDefaultConn(port,type) {
/// flag为1代表启用日志输出,系统正式上线后设置0可提高运行速度
/// sid代表本次连接的会话ID,必须保证唯一
let Protocol = location.protocol;
if (Protocol.toUpperCase().indexOf('HTTPS') > -1)
{
/// HTTPS网站,需要申请自己主站下其它地方都不会用到的一个子域名来做SSL证书,如域名在阿里云上,请下载Apache类型的证书并提交给客服制作授权
if(1 == type)
return 'wss://wrl.zorrosoft.com:'+port+'?sid=' + getrandom(5).toLocaleString() + '&flag=1&cid=zorrosoft&tk=8C4560272C8A38C32EF6102CAB6B4D886504F06C63202A316B3FD88381FC5491704DA444156B9F6FDA313843E412F1E1DC414A7899399F14D76688090FC7DCE11DA121CB2B0E819B2B7080DB9CF09D4D66192C5893ABE182DA38DF8A02EFAACB304BF9A242ADEBFAA09FC0304918895DE3B56E30A17AA8D92E3D61C1AC2453E6C1C637C3E260FE9A445EC858BADEB9312A43DD99323EF5D63414B9BC7D3F4004C7E109ADD5A6289ADAB004A2A544D312BB84E467DAC4C9449418F3FCCC9529049DCFD562B77EF2CE429B242C23975E6EA922E0564B6507177187E92F254EC2678A795B5D2EC92F818A7364FB7CA3E553D4F94119F868261E5A0A8E7EBE841CF7'; // 这里注意test.yuanmaster.com替换为自己的子域名
else
return 'wss://wrl.zorrosoft.com:'+port+'?sid=' + getrandom(5).toLocaleString() + '&flag=1'; // 这里注意wrl.zorrosoft.com替换为自己的子域名
}
else
{
/// type为1时代表中间件启用了安全校验模式,需要按文档生成tk,具体方法参考SDK包中的文档:中间件安全解决方案.pdf
if(1 == type)
return 'ws://127.0.0.1:'+port+'?sid=' + getrandom(5).toLocaleString() + '?flag=1&cid=zorrosoft&tk=8C4560272C8A38C32EF6102CAB6B4D886504F06C63202A316B3FD88381FC5491704DA444156B9F6FDA313843E412F1E1DC414A7899399F14D76688090FC7DCE11DA121CB2B0E819B2B7080DB9CF09D4D66192C5893ABE182DA38DF8A02EFAACB304BF9A242ADEBFAA09FC0304918895DE3B56E30A17AA8D92E3D61C1AC2453E6C1C637C3E260FE9A445EC858BADEB9312A43DD99323EF5D63414B9BC7D3F4004C7E109ADD5A6289ADAB004A2A544D312BB84E467DAC4C9449418F3FCCC9529049DCFD562B77EF2CE429B242C23975E6EA922E0564B6507177187E92F254EC2678A795B5D2EC92F818A7364FB7CA3E553D4F94119F868261E5A0A8E7EBE841CF7';
else
return 'ws://127.0.0.1:'+port+'?sid=' + getrandom(5).toLocaleString() + '&flag=1';
}
}
// 获取随机数
function getrandom(nums) {
return ('000000' + Math.floor(Math.random() * 999999)).slice(-6);
}
export default GetDefaultConn;
export default class websocket {
constructor(url, options) {
this.instance = null
this.token = null
this.isConnected = false
this.url = url
this.options = options || this.defaultOptions()
if (this.options) {
this.reconnectEnabled = options.reconnectEnabled || false
if (this.reconnectEnabled) {
this.reconnectInterval = options.reconnectInterval
}
// Token
//this.token = options?.token || null
}
// These methods should be defined by components
this.onOpen = null
this.onMessage = null
this.onClose = null
this.onError = null
}
defaultOptions() {
return {
reconnectEnabled: false,
reconnectInterval: 0,
token: null
}
}
connect() {
const token = this.token || null
let url = this.url
if (token !== null) {
url += `?token=${token}`
}
this.instance = new WebSocket(url)
// Socket event listeners
// Each event handler also calls the corresponding class method,
// which can be defined by the component
this.instance.onopen = () => {
this.isConnected = true
if (typeof this.onOpen === 'function') {
this.onOpen()
}
}
this.instance.onmessage = (msg) => {
if (typeof this.onMessage === 'function') {
this.onMessage(msg)
}
}
this.instance.onclose = (evt) => {
this.isConnected = false
if (typeof this.onClose === 'function') {
this.onClose(evt)
}
if (this.reconnectEnabled) {
this.reconnect()
}
}
this.instance.onerror = (evt) => {
if (typeof this.onError === 'function') {
this.onError(evt)
}
}
}
disconnect() {
try {
this.instance.close()
} catch (e) {
console.warn(`${e} ${this.instance}`)
}
delete this.instance
}
reconnect() {
try {
this.instance.close()
} catch (e) {
console.warn(`${e} ${this.instance}`)
}
delete this.instance
setTimeout(() => {
this.connect()
}, this.reconnectInterval)
}
sendObj(data) {
if (this.instance.readyState === this.instance.OPEN) {
//若是ws开启状态
this.instance.send(JSON.stringify(data))
} else if (this.instance.readyState === this.instance.CONNECTING) {
// 若是 正在开启状态,则等待1s后重新调用
setTimeout(() => {
this.sendObj(data);
}, 1000);
} else {
// 若未开启 ,则等待1s后重新调用
setTimeout(() => {
this.sendObj(data);
}, 1000);
}
}
removeListeners() {
// removeListeners
this.onOpen = null
this.onMessage = null
this.onClose = null
this.onError = null
}
}
import {createApp} from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
\ No newline at end of file
import {
defineConfig
} from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
base: './',
css: {},
define: {
'process.env': {},
}
})
\ No newline at end of file
1.打开控制台 cmd
2.cd到VUE源码工程文件夹子目录OfficeVue2或OfficeVue3 运行 npm i 安装依赖
3.VUE3项目跳过,VUE2使用NODEJS 大于等于17版本时执行:set NODE_OPTIONS=--openssl-legacy-provider https://blog.csdn.net/zjjxxh/article/details/127173968
4.打包 npm run build
5.本地调试运行 npm run serve 或 npm run dev vue3
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册