feat: 完善基本地图

pull/1/head
独树的风 10 months ago
parent 4620093e14
commit f33cfd80df

@ -0,0 +1,3 @@
> 1%
last 2 versions
not dead

@ -0,0 +1,35 @@
module.exports = {
root: true,
env: {
node: true
},
extends: [
'plugin:vue/essential',
'eslint:recommended',
'plugin:prettier/recommended'
],
parserOptions: {
parser: '@babel/eslint-parser'
},
rules: {
'prettier/prettier': [
'warn',
{
singleQuote: true, // 单引号
semi: false, // 无分号
printWidth: 80, // 每行宽度至多80字符
trailingComma: 'none', // 不加对象|数组最后逗号
endOfLine: 'auto' // 换行符号不限制win mac 不一致)
}
],
'vue/multi-word-component-names': [
'warn',
{
ignores: ['index'] // vue组件名称多单词组成忽略index.vue
}
],
'vue/no-setup-props-destructure': ['off'], // 关闭 props 解构的校验
// 💡 添加未定义变量错误提示create-vue@3.6.3 关闭,这里加上是为了支持下一个章节演示。
'no-undef': 'error'
}
}

23
.gitignore vendored

@ -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?

@ -0,0 +1,24 @@
# sheyang-minedata
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

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

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

21370
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,38 @@
{
"name": "sheyang-minedata",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@liveqing/liveplayer": "^2.6.9",
"axios": "^1.6.0",
"copy-webpack-plugin": "^4.6.0",
"core-js": "^3.8.3",
"echarts": "^5.4.3",
"element-ui": "^2.15.14",
"vue": "^2.6.14",
"vue-router": "^3.5.1",
"vuex": "^3.6.2"
},
"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-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.0.3",
"prettier": "^2.4.1",
"sass": "^1.32.7",
"sass-loader": "^12.0.0",
"vue-template-compiler": "^2.6.14"
}
}

@ -0,0 +1,18 @@
<!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.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<script type="text/javascript" src="<%= BASE_URL %>js/liveplayer-lib.min.js"></script>
</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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

@ -0,0 +1,132 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义点标记内容</title>
<link rel="stylesheet" href="http://50.144.11.244:21009/support/static/api/demo/js-api/zh/css/demo.css">
<!-- 引入MineMap API插件 -->
<link rel="stylesheet" href="http://50.144.11.244:21009/minemapapi/v2.1.0/minemap.css">
<script src="http://50.144.11.244:21009/minemapapi/v2.1.0/minemap.js"></script>
<!-- 引入 popup 样式 -->
<link rel="stylesheet" href="./popup.css">
<style>
#map {
margin: 0;
padding: 0;
box-sizing: border-box;
width: 100vw;
height: 100vh;
}
/* 原始标注图片 */
.old_back {
background-image: url(./background-img//icon_company_blue.png);
}
/* 点击标注图片 */
.new_back {
background-image: url(./background-img/icon_company_yellow.png);
}
/* 监控设备不正常标注图片 */
.error_back {
background-image: url('../../src/assets/icon_company_yellow.png');
}
</style>
</head>
<body>
<div id="map"></div>
<script>
// * 地图全局参数设置
minemap.domainUrl = 'http://50.144.11.244:21009';
minemap.dataDomainUrl = 'http://50.144.11.244:21009';
minemap.serverDomainUrl = 'http://50.144.11.244:21009';
minemap.spriteUrl = 'http://50.144.11.244:21009/minemapapi/v2.1.0/sprite/sprite';
minemap.serviceUrl = 'http://50.144.11.244:21009/service/';
minemap.key = '16be596e00c44c86bb1569cb53424dc9';
minemap.solution = 12877;
// todo 获取vue中的设备数据
window.addEventListener('message', (event) => {
const deviceData = event.data
// 筛选经纬度为0的数组
const deviceDatas = deviceData.filter(item => item.Longitude !== 0)
// 设备编号的数组
// const deviceId = deviceDatas.map(item => item.DeviceID)
// console.log(deviceId);
console.log(deviceDatas);
// * map 实例化
const map = new minemap.Map({
container: 'map',
style: 'http://50.144.11.244:21009/service/solu/style/id/2365',
center: [120.336617, 33.766037],
zoom: 14,
pitch: 0,
maxZoom: 17,
minZoom: 3,
projection: 'LATLON'
});
// 循环设备数组进行打点
for (let i = 0; i < deviceDatas.length; i++) {
// 自定义点标记的内部DOM元素
const el = document.createElement('div');
el.id = 'marker';
// 自定义DOM样式 或者通过css类设置 (http://50.144.11.244:21009/support/static/api/demo/js-api/zh/images/park.png)
// el.style["background-image"] = "url(http://50.144.11.244:21009/support/static/api/demo/js-api/zh/images/park.png)";
el.className = 'old_back'
el.style["background-size"] = "cover";
el.style.width = "20px";
el.style.height = "30px";
el.style["border-radius"] = "50%";
// Marker构造函数接收两个参数一个为自定义的DOM元素一个是Object参数其中包括偏移量等
// offset参数为标注点相对于其左上角偏移像素大小
// 调用setLngLat方法指定Marker的坐标位置
const point = deviceDatas[i]
const Longitude = point.Longitude
const Latitude = point.Latitude
const popup = new minemap.Popup({ offset: [0, -30] }).setHTML(`
<div class="box">
<div class="state state_a">人脸抓拍</div>
<div class="state state_b">人脸抓拍</div>
<div class="state state_c">人脸抓拍</div>
<div class="state state_d">人脸抓拍</div>
<button class="button_plus">
<span class="plus">+</span>
</button>
</div>`)
const marker = new minemap.Marker(el, { offset: [-25, -25] }).setPopup(popup).setLngLat([Longitude, Latitude]).addTo(map);
// 视频
const video = document.querySelector('video')
// 点击监控标记时,发送 设备id 通知 vue 页面接受并通过设备 id 发起请求获取视频url并显示
marker.getElement().addEventListener('click', function () {
if (el.classList.contains('old_back')) {
el.classList.remove('old_back')
el.classList.add('new_back')
window.parent.postMessage({
cmd: 'myIframe',
params: {
channel: point.Channel,
}
}, '*')
console.log(point.Channel, '点击标记播放视频');
} else if (el.classList.contains('new_back')) {
el.classList.remove('new_back')
el.classList.add('old_back')
// 隐藏视频时关闭视频流
window.parent.postMessage({
cmd: 'closeIframe',
params: {
channel: point.Channel,
close: true
}
}, '*')
console.log(point.Channel, '点击标记关闭视频');
}
// console.log(el.className);
})
}
})
</script>
</body>

@ -0,0 +1,58 @@
.box {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 5px;
width: 220px;
height: 100px;
background-color: #fff;
border-radius: 5px;
}
.button_plus,
.state {
margin-top: 5px;
margin-left: 5px;
width: 60px;
height: 30px;
background-color: #2e92f7;
border-radius: 5px;
border: none;
outline: none;
cursor: pointer;
font-size: 0.5rem;
line-height: 30px;
text-align: center;
}
.button_plus:active {
transform: translateY(2px);
}
.state {
width: 60px;
height: 30px;
border-radius: 5px;
color: #fff;
font-size: 0.1rem;
}
.state_a {
background-color: #ff6600;
}
.state_b {
background-color: #188801;
}
.state_c {
background-color: #2278f8;
}
.state_d {
background-color: #f74242;
}
.plus {
font-size: 2rem;
color: #fff;
}

@ -0,0 +1,11 @@
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {}
</script>
<style lang="scss" scoped></style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

@ -0,0 +1,5 @@
html,body {
margin: 0;
padding: 0;
box-sizing: border-box;
}

@ -0,0 +1,13 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '../src/assets/main.scss'
Vue.config.productionTip = false
new Vue({
router,
store,
render: (h) => h(App)
}).$mount('#app')

@ -0,0 +1,18 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'map',
component: () => import('@/views/MinedataMap.vue')
}
]
const router = new VueRouter({
routes
})
export default router

@ -0,0 +1,12 @@
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {},
getters: {},
mutations: {},
actions: {},
modules: {},
});

@ -0,0 +1,47 @@
import request from './request'
import { getToken } from '@/utils/token'
const token = getToken()
// * 登录接口获取 token
export const login = () => {
return request.get('/api/v1/login', {
params: {
username: 'admin',
password: 'AF7548EEDFF8e737C0E4B2A669497290',
url_token_only: true
}
})
}
// * 获取设备信息列表
export const getDeviceListAPI = (params) => {
return request.get(
`/api/v1/device/channellist?start=${params.start}&limit=${params.limit}&online=${params.online}&token=${token}`
)
}
// * 获取视频列表
export const getVideoListAPI = (channel) => {
return request.get(
`/api/v1/stream/start?serial=${'32092400002005063063'}&channel=${channel}&timeout=${500}&token=${token}`
)
}
// * 退出登录
export const logoutAPI = () => {
return request.get('/api/v1/logout')
}
// // * 获取视频列表
// export const getVideoChannelAPI = () => {
// return request.get(
// `/api/v1/stream/start?serial=${'32092400002005063063'}&code=${'32092400001310088291'}&token=${getToken()}`
// )
// }
// * 关闭视频流
export const getCloseVideoAPI = (channel) => {
return request.get(
`/api/v1/stream/stop?serial=${'32092400002005063063'}&channel=${channel}&token=${token}`
)
}

@ -0,0 +1,41 @@
import axios from 'axios'
// import { getToken } from '@/utils/token'
const baseURL = 'http://50.146.63.43:20000/'
// const baseURL = ''
// axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
const request = axios.create({
baseURL,
timeout: 1000
})
// todo 封装基本拦截
// * 请求拦截器
request.interceptors.request.use(
(config) => {
if (config) {
// config.headers['Authorization'] = 'Bearer ' + getToken()
return config
}
},
(error) => {
return Promise.reject(error)
}
)
// * 响应拦截器
request.interceptors.response.use(
(response) => {
if (response.status !== 200) {
return Promise.reject(response.message)
}
return response.data
},
(error) => {
if (axios.isCancel(error)) {
console.log(error)
}
return Promise.reject(error)
}
)
export default request

@ -0,0 +1,17 @@
const URL_TOKEN = 'url_token'
// * 存储 token
export const setToken = (token) => {
localStorage.setItem(URL_TOKEN, JSON.stringify(token))
}
// * 获取 token
export const getToken = () => {
const res = localStorage.getItem(URL_TOKEN)
return res ? JSON.parse(res) : ''
}
// * 移除token
export const removeToken = () => {
localStorage.removeItem(URL_TOKEN)
}

@ -0,0 +1,159 @@
<template>
<div class="minemap">
<iframe
id="map"
name="iframe-map"
:src="iframeSrc"
frameborder="0"
allowfullscreen
sandbox="allow-scripts"
class="iframe_map"
scrolling="no"
ref="iframeRef"
></iframe>
<!-- <div class="btn" @click="onLogout">退</div> -->
<div class="isShow" v-if="block"></div>
<LivePlayer
:videoUrl="videoUrl"
fluent
autoplay
live
stretch
class="video"
v-else
ref="liveRef"
:hasvideo="true"
></LivePlayer>
</div>
</template>
<script>
import LivePlayer from '@liveqing/liveplayer'
import {
getDeviceListAPI,
login,
getVideoListAPI,
getCloseVideoAPI
} from '@/utils/facility'
import { setToken } from '@/utils/token'
export default {
components: {
LivePlayer
},
data() {
return {
iframeSrc: 'static/map.html',
// *
deviceData: [],
// *
videoUrl: '',
// *
block: true,
// * - iframe
deviceId: ''
}
},
mounted() {
// this.iframeSrc = process.env.BASE_URL + 'map.html'
},
created() {
this.onGetDevice()
this.onLogin()
// this.onGetVideoList()
window.addEventListener('message', this.getIframeData)
},
methods: {
// *
async onGetDevice() {
const params = {
start: 0,
limit: 10000,
online: true
}
const res = await getDeviceListAPI(params)
this.deviceData = res.ChannelList
console.log(this.deviceData)
// * loaded map.html
this.loaded()
},
// * token
async onLogin() {
const res = await login()
// * token
setToken(res.URLToken)
},
// * map
loaded() {
// console.log('', this.deviceData)
const iframe = document.getElementById('map')
const iframeWindow = iframe.contentWindow
iframeWindow.postMessage(this.deviceData, '*')
},
// * map
getIframeData(event) {
const res = event.data
this.videoUrl = ''
console.log(this.videoUrl, 'videoUrl地址')
if (res.cmd === 'myIframe') {
// data id
const channel = res.params.channel
this.block = !this.block
const findchannel = this.deviceData.find(
(item) => item.Channel === channel
)
console.log(findchannel.Channel, '寻找到的 channel')
// * - 使 nextTick DOM
// this.$nextTick(() => {
// todo
getVideoListAPI(findchannel.Channel)
.then((res) => (this.videoUrl = res.WS_FLV))
.catch((error) => {
if (error.code === 'ECONNABORTED') {
console.log('停止播放视频')
this.$refs.liveRef.pause()
}
})
// })
}
if (res.cmd === 'closeIframe') {
this.block = !this.block
const close = res.params.close
const channel = res.params.channel
if (close) {
getCloseVideoAPI(channel)
.then((res) => console.log(res))
.catch((error) => console.log(error))
}
console.log(close)
}
}
// *
// // * 退
// async onLogout() {
// await logoutAPI()
// removeToken()
// }
}
}
</script>
<style lang="scss" scoped>
.iframe_map {
width: 100%;
height: 99.5vh;
}
.video {
position: absolute;
left: 10px;
top: 10px;
width: 600px;
height: 300px;
}
.btn {
position: absolute;
left: 50px;
top: 100px;
width: 20px;
height: 10px;
}
</style>

@ -0,0 +1,23 @@
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
configureWebpack: {
plugins: [
// eslint-disable-next-line no-undef
new CopyWebpackPlugin([
// patterns:[
{
from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'
},
{
from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'
},
{
from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js',
to: 'js/'
}
// ]
])
]
}
}
Loading…
Cancel
Save