核电App

This commit is contained in:
2023-10-16 09:48:54 +08:00
parent 19a8d267a5
commit 0496e564d2
29 changed files with 22558 additions and 2 deletions

23
hd-glasses-app/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

8
hd-glasses-app/README.md Normal file
View File

@@ -0,0 +1,8 @@
https://blog.csdn.net/weixin_53934815/article/details/129093796
https://blog.csdn.net/qq_51055690/article/details/126948901
https://blog.csdn.net/qq_40323256/article/details/120019157
- 点位沿轨迹运动
https://blog.csdn.net/qq_40323256/article/details/119955805

View File

@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

20772
hd-glasses-app/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
{
"name": "hd-glasses-app",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"amfe-flexible": "^2.2.1",
"axios": "^1.5.1",
"core-js": "^3.8.3",
"less": "^4.2.0",
"less-loader": "^11.1.3",
"lib-flexible": "^0.3.2",
"ol": "^8.1.0",
"postcss-pxtorem": "^5.1.1",
"vant": "^2.13.1",
"vue": "^2.6.14",
"vue-router": "^3.6.5"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"postcss-px2rem-exclude": "^0.0.6",
"vue-template-compiler": "^2.6.14"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

View File

@@ -0,0 +1,20 @@
module.exports = {
plugins: {
"postcss-pxtorem": {
// 设计稿 375:37.5
// 设计稿750:75
// Vant 是基于 375
rootValue: 75, // ps设计图纸宽度为750px时 就写上面的比例75 设计稿宽度的1/10
// rootValue根据设计稿宽度除以10进行设置
propList: ["*"],
selectorBlackList: ['van',"ol"],
'postcss-px2rem-exclude': {
remUnit: 16,
propList: ['*'],
exclude: /aaa|bbb/i //排除转换aaa 或者 bbb文件夹下匹配到的文件
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<script type="text/javascript" src="srs/adapter-7.4.0.min.js"></script>
<script type="text/javascript" src="srs/srs.sdk.js"></script>
<script type="text/javascript" src="srs/srs.sig.js"></script>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,535 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2021 Winlin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-awat-prmise based SRS RTC Publisher.
function SrsRtcPublisherAsync() {
var self = {};
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
self.constraints = {
audio: true,
video: {
width: {ideal: 320, max: 576}
}
};
// @see https://github.com/rtcdn/rtcdn-draft
// @url The WebRTC url to play with, for example:
// webrtc://r.ossrs.net/live/livestream
// or specifies the API port:
// webrtc://r.ossrs.net:11985/live/livestream
// or autostart the publish:
// webrtc://r.ossrs.net/live/livestream?autostart=true
// or change the app from live to myapp:
// webrtc://r.ossrs.net:11985/myapp/livestream
// or change the stream from livestream to mystream:
// webrtc://r.ossrs.net:11985/live/mystream
// or set the api server to myapi.domain.com:
// webrtc://myapi.domain.com/live/livestream
// or set the candidate(ip) of answer:
// webrtc://r.ossrs.net/live/livestream?eip=39.107.238.185
// or force to access https API:
// webrtc://r.ossrs.net/live/livestream?schema=https
// or use plaintext, without SRTP:
// webrtc://r.ossrs.net/live/livestream?encrypt=false
// or any other information, will pass-by in the query:
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
// webrtc://r.ossrs.net/live/livestream?token=xxx
self.publish = async function (url) {
var conf = self.__internal.prepareUrl(url);
self.pc.addTransceiver("audio", {direction: "sendonly"});
self.pc.addTransceiver("video", {direction: "sendonly"});
var stream = await navigator.mediaDevices.getUserMedia(self.constraints);
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
stream.getTracks().forEach(function (track) {
self.pc.addTrack(track);
// Notify about local track when stream is ok.
self.ontrack && self.ontrack({track: track});
});
var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
var session = await new Promise(function (resolve, reject) {
// @see https://github.com/rtcdn/rtcdn-draft
var data = {
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
clientip: null, sdp: offer.sdp
};
console.log("Generated offer: ", data);
$.ajax({
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
contentType: 'application/json', dataType: 'json'
}).done(function (data) {
console.log("Got answer: ", data);
if (data.code) {
reject(data);
return;
}
resolve(data);
}).fail(function (reason) {
reject(reason);
});
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
);
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
return session;
};
// Close the publisher.
self.close = function () {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got local stream.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
self.ontrack = function (event) {
// Add track to stream of SDK.
self.stream.addTrack(event.track);
};
// Internal APIs.
self.__internal = {
defaultPath: '/rtc/v1/publish/',
prepareUrl: function (webrtcUrl) {
var urlObject = self.__internal.parse(webrtcUrl);
// If user specifies the schema, use it as API schema.
var schema = urlObject.user_query.schema;
schema = schema ? schema + ':' : window.location.protocol;
var port = urlObject.port || 1985;
if (schema === 'https:') {
port = urlObject.port || 443;
}
// @see https://github.com/rtcdn/rtcdn-draft
var api = urlObject.user_query.play || self.__internal.defaultPath;
if (api.lastIndexOf('/') !== api.length - 1) {
api += '/';
}
apiUrl = schema + '//' + urlObject.server + ':' + port + api;
for (var key in urlObject.user_query) {
if (key !== 'api' && key !== 'play') {
apiUrl += '&' + key + '=' + urlObject.user_query[key];
}
}
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
var apiUrl = apiUrl.replace(api + '&', api + '?');
var streamUrl = urlObject.url;
return {
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7)
};
},
parse: function (url) {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = url.replace("rtmp://", "http://")
.replace("webrtc://", "http://")
.replace("rtc://", "http://");
var vhost = a.hostname;
var app = a.pathname.substring(1, a.pathname.lastIndexOf("/"));
var stream = a.pathname.slice(a.pathname.lastIndexOf("/") + 1);
// parse the vhost in the params of app, that srs supports.
app = app.replace("...vhost...", "?vhost=");
if (app.indexOf("?") >= 0) {
var params = app.slice(app.indexOf("?"));
app = app.slice(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.slice(0, vhost.indexOf("&"));
}
}
}
// when vhost equals to server, and server is ip,
// the vhost is __defaultVhost__
if (a.hostname === vhost) {
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
if (re.test(a.hostname)) {
vhost = "__defaultVhost__";
}
}
// parse the schema
var schema = "rtmp";
if (url.indexOf("://") > 0) {
schema = url.slice(0, url.indexOf("://"));
}
var port = a.port;
if (!port) {
if (schema === 'http') {
port = 80;
} else if (schema === 'https') {
port = 443;
} else if (schema === 'rtmp') {
port = 1935;
}
}
var ret = {
url: url,
schema: schema,
server: a.hostname, port: port,
vhost: vhost, app: app, stream: stream
};
self.__internal.fill_query(a.search, ret);
// For webrtc API, we use 443 if page is https, or schema specified it.
if (!ret.port) {
if (schema === 'webrtc' || schema === 'rtc') {
if (ret.user_query.schema === 'https') {
ret.port = 443;
} else if (window.location.href.indexOf('https://') === 0) {
ret.port = 443;
} else {
// For WebRTC, SRS use 1985 as default API port.
ret.port = 1985;
}
}
}
return ret;
},
fill_query: function (query_string, obj) {
// pure user query object.
obj.user_query = {};
if (query_string.length === 0) {
return;
}
// split again for angularjs.
if (query_string.indexOf("?") >= 0) {
query_string = query_string.split("?")[1];
}
var queries = query_string.split("&");
for (var i = 0; i < queries.length; i++) {
var elem = queries[i];
var query = elem.split("=");
obj[query[0]] = query[1];
obj.user_query[query[0]] = query[1];
}
// alias domain for vhost.
if (obj.domain) {
obj.vhost = obj.domain;
}
}
};
self.pc = new RTCPeerConnection(null);
// To keep api consistent between player and publisher.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
// @see https://webrtc.org/getting-started/media-devices
self.stream = new MediaStream();
return self;
}
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-await-promise based SRS RTC Player.
function SrsRtcPlayerAsync() {
var self = {};
// @see https://github.com/rtcdn/rtcdn-draft
// @url The WebRTC url to play with, for example:
// webrtc://r.ossrs.net/live/livestream
// or specifies the API port:
// webrtc://r.ossrs.net:11985/live/livestream
// or autostart the play:
// webrtc://r.ossrs.net/live/livestream?autostart=true
// or change the app from live to myapp:
// webrtc://r.ossrs.net:11985/myapp/livestream
// or change the stream from livestream to mystream:
// webrtc://r.ossrs.net:11985/live/mystream
// or set the api server to myapi.domain.com:
// webrtc://myapi.domain.com/live/livestream
// or set the candidate(ip) of answer:
// webrtc://r.ossrs.net/live/livestream?eip=39.107.238.185
// or force to access https API:
// webrtc://r.ossrs.net/live/livestream?schema=https
// or use plaintext, without SRTP:
// webrtc://r.ossrs.net/live/livestream?encrypt=false
// or any other information, will pass-by in the query:
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
// webrtc://r.ossrs.net/live/livestream?token=xxx
self.play = async function(url) {
var conf = self.__internal.prepareUrl(url);
self.pc.addTransceiver("audio", {direction: "recvonly"});
self.pc.addTransceiver("video", {direction: "recvonly"});
var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
var session = await new Promise(function(resolve, reject) {
// @see https://github.com/rtcdn/rtcdn-draft
var data = {
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
clientip: null, sdp: offer.sdp
};
console.log("Generated offer: ", data);
$.ajax({
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
contentType:'application/json', dataType: 'json'
}).done(function(data) {
console.log("Got answer: ", data);
if (data.code) {
reject(data); return;
}
resolve(data);
}).fail(function(reason){
reject(reason);
});
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
);
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
return session;
};
// Close the player.
self.close = function() {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got remote track.
// Note that the onaddstream is deprecated, @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/onaddstream
self.ontrack = function (event) {
// https://webrtc.org/getting-started/remote-streams
self.stream.addTrack(event.track);
};
// Internal APIs.
self.__internal = {
defaultPath: '/rtc/v1/play/',
prepareUrl: function (webrtcUrl) {
var urlObject = self.__internal.parse(webrtcUrl);
// If user specifies the schema, use it as API schema.
var schema = urlObject.user_query.schema;
schema = schema ? schema + ':' : window.location.protocol;
var port = urlObject.port || 1985;
if (schema === 'https:') {
port = urlObject.port || 443;
}
// @see https://github.com/rtcdn/rtcdn-draft
var api = urlObject.user_query.play || self.__internal.defaultPath;
if (api.lastIndexOf('/') !== api.length - 1) {
api += '/';
}
apiUrl = schema + '//' + urlObject.server + ':' + port + api;
for (var key in urlObject.user_query) {
if (key !== 'api' && key !== 'play') {
apiUrl += '&' + key + '=' + urlObject.user_query[key];
}
}
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
var apiUrl = apiUrl.replace(api + '&', api + '?');
var streamUrl = urlObject.url;
return {
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7)
};
},
parse: function (url) {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = url.replace("rtmp://", "http://")
.replace("webrtc://", "http://")
.replace("rtc://", "http://");
var vhost = a.hostname;
var app = a.pathname.substring(1, a.pathname.lastIndexOf("/"));
var stream = a.pathname.slice(a.pathname.lastIndexOf("/") + 1);
// parse the vhost in the params of app, that srs supports.
app = app.replace("...vhost...", "?vhost=");
if (app.indexOf("?") >= 0) {
var params = app.slice(app.indexOf("?"));
app = app.slice(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.slice(0, vhost.indexOf("&"));
}
}
}
// when vhost equals to server, and server is ip,
// the vhost is __defaultVhost__
if (a.hostname === vhost) {
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
if (re.test(a.hostname)) {
vhost = "__defaultVhost__";
}
}
// parse the schema
var schema = "rtmp";
if (url.indexOf("://") > 0) {
schema = url.slice(0, url.indexOf("://"));
}
var port = a.port;
if (!port) {
if (schema === 'http') {
port = 80;
} else if (schema === 'https') {
port = 443;
} else if (schema === 'rtmp') {
port = 1935;
}
}
var ret = {
url: url,
schema: schema,
server: a.hostname, port: port,
vhost: vhost, app: app, stream: stream
};
self.__internal.fill_query(a.search, ret);
// For webrtc API, we use 443 if page is https, or schema specified it.
if (!ret.port) {
if (schema === 'webrtc' || schema === 'rtc') {
if (ret.user_query.schema === 'https') {
ret.port = 443;
} else if (window.location.href.indexOf('https://') === 0) {
ret.port = 443;
} else {
// For WebRTC, SRS use 1985 as default API port.
ret.port = 1985;
}
}
}
return ret;
},
fill_query: function (query_string, obj) {
// pure user query object.
obj.user_query = {};
if (query_string.length === 0) {
return;
}
// split again for angularjs.
if (query_string.indexOf("?") >= 0) {
query_string = query_string.split("?")[1];
}
var queries = query_string.split("&");
for (var i = 0; i < queries.length; i++) {
var elem = queries[i];
var query = elem.split("=");
obj[query[0]] = query[1];
obj.user_query[query[0]] = query[1];
}
// alias domain for vhost.
if (obj.domain) {
obj.vhost = obj.domain;
}
}
};
self.pc = new RTCPeerConnection(null);
// Create a stream to add track to the stream, @see https://webrtc.org/getting-started/remote-streams
self.stream = new MediaStream();
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
self.pc.ontrack = function(event) {
if (self.ontrack) {
self.ontrack(event);
}
};
return self;
}
// Format the codec of RTCRtpSender, kind(audio/video) is optional filter.
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs
function SrsRtcFormatSenders(senders, kind) {
var codecs = [];
senders.forEach(function (sender) {
var params = sender.getParameters();
params && params.codecs && params.codecs.forEach(function(c) {
if (kind && sender.track.kind !== kind) {
return;
}
if (c.mimeType.indexOf('/red') > 0 || c.mimeType.indexOf('/rtx') > 0 || c.mimeType.indexOf('/fec') > 0) {
return;
}
var s = '';
s += c.mimeType.replace('audio/', '').replace('video/', '');
s += ', ' + c.clockRate + 'HZ';
if (sender.track.kind === "audio") {
s += ', channels: ' + c.channels;
}
s += ', pt: ' + c.payloadType;
codecs.push(s);
});
});
return codecs.join(", ");
}

View File

@@ -0,0 +1,148 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2021 Winlin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
// Async-await-promise based SRS RTC Signaling.
function SrsRtcSignalingAsync() {
var self = {};
// The schema is ws or wss, host is ip or ip:port, display is nickname
// of user to join the room.
self.connect = async function (schema, host, room, display) {
var url = schema + '://' + host + '/sig/v1/rtc';
self.ws = new WebSocket(url + '?room=' + room + '&display=' + display);
self.ws.onmessage = function(event) {
var r = JSON.parse(event.data);
var promise = self._internals.msgs[r.tid];
if (promise) {
promise.resolve(r.msg);
delete self._internals.msgs[r.tid];
} else {
self.onmessage(r.msg);
}
};
return new Promise(function (resolve, reject) {
self.ws.onopen = function (event) {
resolve(event);
};
self.ws.onerror = function (event) {
reject(event);
};
});
};
// The message is a json object.
self.send = async function (message) {
return new Promise(function (resolve, reject) {
var r = {tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7), msg: message};
self._internals.msgs[r.tid] = {resolve: resolve, reject: reject};
self.ws.send(JSON.stringify(r));
});
};
self.close = function () {
self.ws && self.ws.close();
self.ws = null;
for (const tid in self._internals.msgs) {
var promise = self._internals.msgs[tid];
promise.reject('close');
}
};
// The callback when got messages from signaling server.
self.onmessage = function (msg) {
};
self._internals = {
// Key is tid, value is object {resolve, reject, response}.
msgs: {}
};
return self;
}
// Parse params in query string.
function SrsRtcSignalingParse(location) {
let query = location.href.split('?')[1];
query = query? '?' + query : null;
let wsSchema = location.href.split('wss=')[1];
wsSchema = wsSchema? wsSchema.split('&')[0] : (location.protocol === 'http:'? 'ws' : 'wss');
let wsHost = location.href.split('wsh=')[1];
wsHost = wsHost? wsHost.split('&')[0] : location.hostname;
let wsPort = location.href.split('wsp=')[1];
wsPort = wsPort? wsPort.split('&')[0] : location.host.split(':')[1];
let host = location.href.split('host=')[1];
host = host? host.split('&')[0] : location.hostname;
let room = location.href.split('room=')[1];
room = room? room.split('&')[0] : null;
let display = location.href.split('display=')[1];
display = display? display.split('&')[0] : Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).toString(16).slice(0, 7);
let autostart = location.href.split('autostart=')[1];
autostart = autostart && autostart.split('&')[0] === 'true';
// Remove data in query.
let rawQuery = query;
if (query) {
query = query.replace('wss=' + wsSchema, '');
query = query.replace('wsh=' + wsHost, '');
query = query.replace('wsp=' + wsPort, '');
query = query.replace('host=' + host, '');
if (room) {
query = query.replace('room=' + room, '');
}
query = query.replace('display=' + display, '');
query = query.replace('autostart=' + autostart, '');
while (query.indexOf('&&') >= 0) {
query = query.replace('&&', '&');
}
query = query.replace('?&', '?');
if (query.lastIndexOf('?') === query.length - 1) {
query = query.slice(0, query.length - 1);
}
if (query.lastIndexOf('&') === query.length - 1) {
query = query.slice(0, query.length - 1);
}
}
// Regenerate the host of websocket.
wsHost = wsPort? wsHost.split(':')[0] + ':' + wsPort : wsHost;
return {
query: query, rawQuery: rawQuery, wsSchema: wsSchema, wsHost: wsHost, host: host,
room: room, display: display, autostart: autostart,
};
}

View File

@@ -0,0 +1,29 @@
<template>
<div id="app">
<!-- <p>-->
<!-- <router-link to="/hello">Go to hello</router-link>-->
<!-- <router-link to="/about">Go to about</router-link>-->
<!-- </p>-->
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,25 @@
@charset "utf-8";html{background-color:#fff;color:#000;font-size:12px}
body,ul,ol,dl,dd,h1,h2,h3,h4,h5,h6,figure,form,fieldset,legend,input,textarea,button,p,blockquote,th,td,pre,xmp{margin:0;padding:0}
body,input,textarea,button,select,pre,xmp,tt,code,kbd,samp{line-height:1.5;font-family:tahoma,arial,"Hiragino Sans GB",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,small,big,input,textarea,button,select{font-size:100%}
h1,h2,h3,h4,h5,h6{font-family:tahoma,arial,"Hiragino Sans GB","微软雅黑",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,b,strong{font-weight:normal}
address,cite,dfn,em,i,optgroup,var{font-style:normal}
table{border-collapse:collapse;border-spacing:0;text-align:left}
caption,th{text-align:inherit}
ul,ol,menu{list-style:none}
fieldset,img{border:0}
img,object,input,textarea,button,select{vertical-align:middle}
article,aside,footer,header,section,nav,figure,figcaption,hgroup,details,menu{display:block}
audio,canvas,video{display:inline-block;*display:inline;*zoom:1}
blockquote:before,blockquote:after,q:before,q:after{content:"\0020"}
textarea{overflow:auto;resize:vertical}
input,textarea,button,select,a{outline:0 none;border: none;}
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}
mark{background-color:transparent}
a,ins,s,u,del{text-decoration:none}
sup,sub{vertical-align:baseline}
html {overflow-x: hidden;height: 100%;font-size: 50px;-webkit-tap-highlight-color: transparent;}
body {font-family: Arial, "Microsoft Yahei", "Helvetica Neue", Helvetica, sans-serif;color: #333;font-size: .28em;line-height: 1;-webkit-text-size-adjust: none;}
hr {height: .02rem;margin: .1rem 0;border: medium none;border-top: .02rem solid #cacaca;}
a {color: #25a4bb;text-decoration: none;}

View File

@@ -0,0 +1,58 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@@ -0,0 +1,20 @@
<template>
<div>
<div>MyHello</div>
<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
<van-button type="default">默认按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>
</div>
</template>
<script>
export default {
name: "MyHello"
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,22 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import axios from 'axios'
Vue.prototype.$http = axios.create();
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);
import 'amfe-flexible';
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router //添加路由对象
}).$mount('#app')

View File

@@ -0,0 +1,37 @@
<template>
<div>
<h1>call_room</h1>
<video id="rtc_media_player" width="310" autoplay muted controls></video>
</div>
</template>
<script>
export default {
name: "call_room",
data() {
return {};
},
mounted() {
// eslint-disable-next-line no-undef
let player = new SrsRtcPlayerAsync();
var url = 'webrtc://192.168.2.180/live/test123';
player.play(url).then(function(session){
console.log(session);
// ui.children('#peer').text('Peer: ' + url);
// video.prop('muted', false);
}).catch(function (reason) {
// player.close();
// video.hide();
console.error(reason);
});
},
methods: {}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,277 @@
<template>
<div id="content">
<div id="map" ref="map" style="width: 100vw; height: 100vh"/>
<div id="overlay-box"/>
<div class="actionList">
<van-grid :column-num="3">
<van-grid-item>
<van-button size="mini" type="primary" @click="canAddPoints=true">开始新增点位</van-button>
</van-grid-item>
<van-grid-item>
<van-button size="mini" type="primary" @click="canAddPoints=false">结束新增点位</van-button>
</van-grid-item>
<van-grid-item>
<van-button size="mini" type="primary" @click="clearPointsLayers">清除点位</van-button>
</van-grid-item>
<van-grid-item>
<van-button size="mini" type="primary" @click="startAnimation()">开始沿轨迹运动</van-button>
</van-grid-item>
<van-grid-item>
<van-button size="mini" type="primary" @click="stopAnimation()">结束沿轨迹运动</van-button>
</van-grid-item>
</van-grid>
<br/>
</div>
</div>
</template>
<script>
import "ol/ol.css";
import TileLayer from "ol/layer/Tile";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import XYZ from "ol/source/XYZ";
import {Map, View, Feature} from "ol";
import { Icon, Fill, Stroke, Style, Circle } from "ol/style";
import Overlay from "ol/Overlay";
import {Point, LineString} from "ol/geom.js";
import { getVectorContext } from "ol/render";
export default {
data() {
return {
map: {},
pointLayer: {},
canAddPoints: false,
coordinate: [],
route: new LineString([[113.95113841271974,22.568523240293505],[113.94452944970705,22.563351941312792],[113.95188943124391,22.559317898954394],[113.94467965341188,22.555326771940233]]),
geometryMove: {},
featureMove: {},
styles: {
route: new Style({
stroke: new Stroke({
width: 6,
color: [237, 212, 0, 0.8],
}),
}),
icon: new Style({
image: new Icon({
anchor: [0.5, 1],
src: "https://openlayers.org/en/v4.6.5/examples/data/icon.png",
scale: 1, //设置大小
}),
}),
featureMove: new Style({
image: new Circle({
radius: 7,
fill: new Fill({color: "black"}),
stroke: new Stroke({
color: "white",
width: 2,
}),
}),
}),
},
vectorLayer: {},
distance: 0,
lastTime: 0,
speed: 0.1
};
},
mounted() {
this.initMap();
this.clickMap();
},
methods: {
showHistory() {
var _list = [[113.94755498147583, 22.565597285534384], [113.9468468782959, 22.563151110912802], [113.9487566111145, 22.56018995216036], [113.95017281747437, 22.55748628547335]]
console.log(_list)
},
clearPointsLayers() {
this.coordinate = []
this.map.removeLayer(this.pointLayer);
this.pointLayer = {};
if (Object.keys(this.pointLayer).length == 0) {
// 创建图层
this.pointLayer = new VectorLayer({
source: new VectorSource(),
});
// 图层添加到地图上
this.map.addLayer(this.pointLayer);
}
},
initMap() {
this.geometryMove = new Point(this.route.getFirstCoordinate());
this.featureMove = new Feature({
type: "featureMove",
geometry: this.geometryMove,
});
this.vectorLayer = new VectorLayer({
source: new VectorSource({
features: [
new Feature({
type: "route",
geometry: this.route,
}),
this.featureMove,
new Feature({
type: "icon",
geometry: new Point(this.route.getFirstCoordinate()),
}),
new Feature({
type: "icon",
geometry: new Point(this.route.getLastCoordinate()),
}),
],
}),
style: (feature) => {
return this.styles[feature.get("type")];
},
});
this.map = new Map({
target: "map",
layers: [
new TileLayer({
source: new XYZ({
url: "http://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}",
}),
}),
],
view: new View({
projection: "EPSG:4326",
center: [113.948306, 22.562039],
zoom: 16,
}),
});
this.map.addLayer(this.vectorLayer);
},
/**
* 点击地图添加摄像头要素
*/
clickMap() {
this.map.on("click", (e) => {
if (this.canAddPoints) {
this.addPoints(e.coordinate);
}
});
},
/**
* 根据经纬度坐标添加摄像头要素
*/
addPoints(coordinate) {
this.coordinate.push(coordinate)
console.log(JSON.stringify(this.coordinate))
if (Object.keys(this.pointLayer).length == 0) {
// 创建图层
this.pointLayer = new VectorLayer({
source: new VectorSource(),
});
// 图层添加到地图上
this.map.addLayer(this.pointLayer);
}
// 创建feature要素一个feature就是一个点坐标信息
const feature = new Feature({
geometry: new Point(coordinate),
});
// 设置要素的图标
feature.setStyle(
new Style({
// 设置图片效果
image: new Icon({
src: "https://smart-garden-manage.oss-cn-chengdu.aliyuncs.com/shexiangtou.png",
// anchor: [0.5, 0.5],
scale: 0.3,
}),
})
);
// 要素添加到地图图层上
this.pointLayer.getSource().addFeature(feature);
// 设置文字信息
// this.addText(coordinate);
},
addText(coordinate) {
const overlayBox = document.getElementById("overlay-box"); //获取一个div
const oSpan = document.createElement("span"); //创建一个span
oSpan.contentEditable = true; //设置文字是否可编辑
oSpan.id = coordinate[0]; //创建一个id
let pText = document.createTextNode("摄像头" + coordinate[0].toFixed(2)); //创建span的文本信息
oSpan.appendChild(pText); //将文本信息添加到span
overlayBox.appendChild(oSpan); //将span添加到div中
let textInfo = new Overlay({
position: coordinate, //设置位置
element: document.getElementById(coordinate[0]),
offset: [-25, 30], //设置偏移
});
this.map.addOverlay(textInfo);
},
moveFeature(e) {
let time = e.frameState.time;
this.distance =
(this.distance + (this.speed * (time - this.lastTime)) / 1000) % 1; //%2表示起止止起%1表示起止起止
this.lastTime = time;
const currentCoordinate = this.route.getCoordinateAt(
this.distance > 1 ? 2 - this.distance : this.distance
);
this.geometryMove.setCoordinates(currentCoordinate);
const vectorContext = getVectorContext(e);
vectorContext.setStyle(this.styles.featureMove);
vectorContext.drawGeometry(this.geometryMove);
this.map.render();
},
startAnimation() {
this.lastTime = Date.now();
this.vectorLayer.on("postrender", this.moveFeature);
this.featureMove.setGeometry(null); //必须用null不能用{}
},
stopAnimation() {
this.featureMove.setGeometry(this.geometryMove);
this.vectorLayer.un("postrender", this.moveFeature);
}
},
};
</script>
<style lang="less">
// 非核心已删除
#content {
width: 100%;
height: 100vh;
#map {
width: 100%;
height: 100%;
}
}
.ol-touch .ol-control button {
font-size: 26px;
}
.actionList {
position: fixed;
bottom: 0px;
left: 0px;
height: 200px;
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-around;
flex-wrap: wrap;
}
</style>

View File

@@ -0,0 +1,29 @@
import Vue from 'vue';//引入Vue对象因为要用它的use
import Router from 'vue-router'//引入vue-router
//引入你需要配置的组件
import MyHello from '../components/MyHello.vue'
//使用路由插件
Vue.use(Router);
//配置路由
const router = new Router({
routes: [
{
path:"/hello",
component:MyHello
},
//路由懒加载(按需加载)
{
path: '/map',
name: 'map',
component: () => import('../pages/map.vue')
},{
path: '/call_room',
name: 'call_room',
component: () => import('../pages/call_room.vue')
}
]
});
//导出
export default router;

View File

@@ -0,0 +1,34 @@
import axios from "axios";
let service = axios.create({
baseURL:"",//相同绝对路径
timeout: 100000,//超过这么多时间,则请求终止
headers: {//请求头携带数据的格式
"Content-Type": "application/json;charset=UTF-8",
// "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
}
});
// 添加请求拦截器Interceptors
service.interceptors.request.use(function (config) {
// 发送请求之前做写什么
let token = localStorage.getItem("token");
// 如果有
if(token){
// 放在请求头token跟后端沟通他需要什么该成什么就可以了
config.headers.authorization = token;
}
return config;
}, function (error) {
// 请求错误的时候做些什么
return Promise.reject(error);
});
// 添加响应拦截器
service.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export default service;

View File

@@ -0,0 +1,5 @@
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave:false //添加该行代码
})