initial
@@ -0,0 +1 @@
|
||||
/src/vendors/**
|
||||
@@ -0,0 +1,22 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
commonjs: true,
|
||||
es2021: true,
|
||||
node: true
|
||||
},
|
||||
extends: ['standard', 'plugin:vue/vue3-essential'],
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
parser: '@typescript-eslint/parser'
|
||||
},
|
||||
plugins: ['vue', '@typescript-eslint'],
|
||||
rules: {
|
||||
'comma-dangle': 'off',
|
||||
'import/no-absolute-path': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
camelcase: 'off',
|
||||
'no-redeclare': 'off',
|
||||
'vue/no-unused-components': 'off'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
node_modules/
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
# .vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
.history
|
||||
/coverage
|
||||
/backup
|
||||
node_modules
|
||||
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
@@ -0,0 +1 @@
|
||||
registry=https://registry.npm.taobao.org/
|
||||
@@ -0,0 +1,2 @@
|
||||
# VUE3
|
||||
全系统选用 script setup 模式
|
||||
@@ -0,0 +1,2 @@
|
||||
VITE_APP_ENVIRONMENT=DEV
|
||||
VITE_APP_APIGATEWAY_BACKEND_HOST=''
|
||||
@@ -0,0 +1,2 @@
|
||||
VITE_APP_ENVIRONMENT=production
|
||||
VITE_APP_APIGATEWAY_BACKEND_HOST=''
|
||||
@@ -0,0 +1,2 @@
|
||||
VITE_APP_ENVIRONMENT=STAG
|
||||
VITE_APP_APIGATEWAY_BACKEND_HOST=''
|
||||
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>demo-web</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="demo-app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"name": "demo-web",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"serve": "vite",
|
||||
"build:test": "vite build --mode stag",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||
"@ant-design/icons-vue": "^6.0.1",
|
||||
"@vitejs/plugin-legacy": "^1.6.2",
|
||||
"agora-rtc-sdk-ng": "latest",
|
||||
"ant-design-vue": "^2.2.8",
|
||||
"axios": "^0.21.1",
|
||||
"query-string": "^7.0.1",
|
||||
"reconnecting-websocket": "^4.4.0",
|
||||
"vconsole": "^3.8.1",
|
||||
"vite-plugin-components": "^0.13.3",
|
||||
"vite-plugin-importer": "^0.2.5",
|
||||
"vite-plugin-optimize-persist": "^0.1.2",
|
||||
"vite-plugin-package-config": "^0.1.1",
|
||||
"vue": "^3.2.26",
|
||||
"vue-cookies": "^1.7.4",
|
||||
"vue-i18n": "^9.1.6",
|
||||
"vue-router": "4",
|
||||
"vuex": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.3.2",
|
||||
"@types/urlencode": "^1.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.8.1",
|
||||
"@typescript-eslint/parser": "^5.8.1",
|
||||
"@vitejs/plugin-vue": "^1.2.4",
|
||||
"@vue/compiler-sfc": "^3.0.5",
|
||||
"eslint": "^7.30.0",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
"eslint-plugin-import": "^2.23.4",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-vue": "^7.13.0",
|
||||
"rollup-plugin-external-globals": "^0.6.1",
|
||||
"sass": "^1.35.1",
|
||||
"typescript": "^4.5.4",
|
||||
"vite": "^2.4.0",
|
||||
"vite-plugin-eslint": "^1.3.0",
|
||||
"vite-plugin-style-import": "^1.0.1",
|
||||
"vite-plugin-svg-icons": "^1.0.5",
|
||||
"vite-plugin-vconsole": "^1.1.0",
|
||||
"vue-tsc": "^0.0.24"
|
||||
},
|
||||
"license": "ISC",
|
||||
"vite": {
|
||||
"optimizeDeps": {
|
||||
"include": [
|
||||
"@amap/amap-jsapi-loader",
|
||||
"@ant-design/icons-vue",
|
||||
"@vue/reactivity",
|
||||
"agora-rtc-sdk-ng",
|
||||
"ant-design-vue",
|
||||
"ant-design-vue/es",
|
||||
"ant-design-vue/es/button/style/css",
|
||||
"ant-design-vue/es/divider/style/css",
|
||||
"ant-design-vue/es/drawer/style/css",
|
||||
"ant-design-vue/es/form/style/css",
|
||||
"ant-design-vue/es/image/style/css",
|
||||
"ant-design-vue/es/input/style/css",
|
||||
"ant-design-vue/es/message/style/css",
|
||||
"ant-design-vue/es/modal/style/css",
|
||||
"ant-design-vue/es/radio/style/css",
|
||||
"ant-design-vue/es/select/style/css",
|
||||
"ant-design-vue/es/switch/style/css",
|
||||
"ant-design-vue/es/table/style/css",
|
||||
"ant-design-vue/es/tooltip/style/css",
|
||||
"ant-design-vue/es/tree/style/css",
|
||||
"axios",
|
||||
"reconnecting-websocket",
|
||||
"vconsole",
|
||||
"vue",
|
||||
"vue-router",
|
||||
"vuex"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Depois Largura: | Altura: | Tamanho: 6.4 KiB |
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div id="demo-app" class="demo-app">
|
||||
<router-view />
|
||||
<!-- <div class="map-wrapper">
|
||||
<GMap/>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from 'vue'
|
||||
import { useMyStore } from './store'
|
||||
import GMap from '/@/components/GMap.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
components: { GMap },
|
||||
|
||||
setup () {
|
||||
const store = useMyStore()
|
||||
return {}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.demo-app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.map-wrapper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,13 @@
|
||||
// import Icon from '@ant-design/icons-vue'
|
||||
import * as antDesign from 'ant-design-vue'
|
||||
import 'ant-design-vue/dist/antd.css'
|
||||
import { App } from 'vue'
|
||||
import svgIcon from '/@/components/svgIcon.vue'
|
||||
|
||||
export const antComponents = {
|
||||
install (app: App): void {
|
||||
app.use(antDesign)
|
||||
// app.component('Icon', Icon)
|
||||
app.component('svg-icon', svgIcon)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 职责声明:
|
||||
* 1.提供一个 单一的 axios 实例(方面进行统一拦截)
|
||||
* 2.允许调用方定制自己的配置(例如拦截器等),而不影响其他实例
|
||||
*
|
||||
* 暴露 API:
|
||||
* 1.一个统一的 axios 实例: singleAxiosInstance(绑定了统一的拦截器)
|
||||
* 2.创建 axios 实例的方法 createAxiosInstance,并在参数中允许配置是否绑定统一拦截器
|
||||
* 3.对外暴露统一拦截器绑定方案,允许外界进行定制: bindCommonRequestInterceptors、bindCommonResponseInterceptors
|
||||
*/
|
||||
|
||||
import Axios, { AxiosInstance, AxiosRequestConfig } from 'axios'
|
||||
|
||||
// 统一的 request 拦截器
|
||||
export function bindCommonRequestInterceptors (instance: AxiosInstance): void {
|
||||
instance.interceptors.request.use(config => {
|
||||
return config
|
||||
})
|
||||
}
|
||||
|
||||
// Unified response interceptor
|
||||
export function bindCommonResponseInterceptors (instance: AxiosInstance): void {
|
||||
instance.interceptors.response.use(config => {
|
||||
return config
|
||||
}, err => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
export function createAxiosInstance (config?: AxiosRequestConfig, commonInterceptorConf: { request?: boolean, response?: boolean } = {}): AxiosInstance {
|
||||
const instance = Axios.create(config)
|
||||
|
||||
// Binding a unified interceptor, binding by default
|
||||
commonInterceptorConf.request !== false && bindCommonRequestInterceptors(instance)
|
||||
commonInterceptorConf.response !== false && bindCommonResponseInterceptors(instance)
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
const singleAxios = createAxiosInstance({}, { request: true, response: false })
|
||||
|
||||
export default singleAxios
|
||||
@@ -0,0 +1,20 @@
|
||||
export const CURRENT_CONFIG = {
|
||||
|
||||
baseURL: 'Please enter the backend access address prefix.', // This url must end with "/". Example: 'http://192.168.1.1:6789/cloud/'
|
||||
websocketURL: 'Please enter the WebSocket access address.', // Example: 'ws://192.168.1.1:6789/api/v1/ws'
|
||||
|
||||
rtmpURL: 'Please enter the rtmp access address.', // Example: 'rtmp://192.168.1.1/live/'
|
||||
gb28181Para:
|
||||
'serverIP=Please enter the server ip.&serverPort=Please enter the server port.&serverID=Please enter the server id.' +
|
||||
'&agentID=Please enter the agent id.&agentPassword=Please enter the agent password' +
|
||||
'&localPort=Please enter the local port.&channel=Please enter the channel.',
|
||||
rtspPara: 'userName=Please enter the username.&password=Please enter the password&port=Please enter the port.',
|
||||
amapKey: 'Please enter the amap key.',
|
||||
agoraAPPID: 'Please enter the agora app id.',
|
||||
agoraToken: 'Please enter the agora token.',
|
||||
agoraChannel: 'Please enter the agora channel.',
|
||||
|
||||
appId: 'Please enter the app id.', // You need to go to the development website to apply.
|
||||
appKey: 'Please enter the app key.', // You need to go to the development website to apply.
|
||||
appLicense: 'Please enter the app license.' // You need to go to the development website to apply.
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import axios from 'axios'
|
||||
import { uuidv4 } from '/@/utils/uuid'
|
||||
import { CURRENT_CONFIG } from './config'
|
||||
export * from './type'
|
||||
const REQUEST_ID = 'X-Request-Id'
|
||||
function getAuthToken () {
|
||||
return localStorage.getItem('x-auth-token')
|
||||
}
|
||||
|
||||
const instance = axios.create({
|
||||
// withCredentials: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
// timeout: 12000,
|
||||
})
|
||||
|
||||
instance.interceptors.request.use(
|
||||
config => {
|
||||
config.headers['X-Auth-Token'] = getAuthToken()
|
||||
// config.headers[REQUEST_ID] = uuidv4()
|
||||
config.baseURL = CURRENT_CONFIG.baseURL
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
instance.interceptors.response.use(
|
||||
response => response,
|
||||
err => {
|
||||
const requestId = err?.config?.headers && err?.config?.headers[REQUEST_ID]
|
||||
console.info('')
|
||||
if (requestId) {
|
||||
console.info(REQUEST_ID, ':', requestId)
|
||||
}
|
||||
console.info('url: ', err?.config?.url, `【${err?.config?.method}】 \n>>>> err: `, err)
|
||||
|
||||
let description = '-'
|
||||
if (err.response?.data && err.response.data.message) {
|
||||
description = err.response.data.message
|
||||
}
|
||||
if (err.response?.data && err.response.data.result) {
|
||||
description = err.response.data.result.message
|
||||
}
|
||||
// @See: https://github.com/axios/axios/issues/383
|
||||
if (!err.response || !err.response.status) {
|
||||
console.log('The network is abnormal, please check the network and try again')
|
||||
} else if (err.response?.status !== 200) {
|
||||
console.log(`ERROR_CODE: ${err.response?.status}`)
|
||||
}
|
||||
if (err.response?.status === 403) {
|
||||
// window.location.href = '/'
|
||||
}
|
||||
if (err.response?.status === 401) {
|
||||
console.log(err.response)
|
||||
}
|
||||
|
||||
return Promise.reject(err)
|
||||
},
|
||||
)
|
||||
|
||||
export default instance
|
||||
@@ -0,0 +1,38 @@
|
||||
export interface IResult {
|
||||
code: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface IPage {
|
||||
page: number;
|
||||
total: number;
|
||||
page_size: number;
|
||||
}
|
||||
|
||||
export interface IListWorkspaceResponse<T> {
|
||||
code: number;
|
||||
message: string;
|
||||
data: {
|
||||
list: T[];
|
||||
pagination: IPage;
|
||||
};
|
||||
}
|
||||
// Workspace
|
||||
export interface IWorkspaceResponse<T> {
|
||||
[x: string]: number;
|
||||
code: number;
|
||||
data: T;
|
||||
message: string;
|
||||
}
|
||||
export type IStatus = 'WAITING' | 'DOING' | 'SUCCESS' | 'FAILED';
|
||||
|
||||
export interface CommonListResponse<T> extends IResult {
|
||||
data: {
|
||||
list: T[];
|
||||
pagination: IPage;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CommonResponse<T> extends IResult {
|
||||
data: T
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import request, { IWorkspaceResponse } from '/@/api/http/request'
|
||||
import { mapLayers } from '/@/constants/mock-layers'
|
||||
import { elementGroupsReq, PostElementsBody, PutElementsBody } from '/@/types/mapLayer'
|
||||
const PREFIX = '/map/api/v1'
|
||||
const workspace_id = localStorage.getItem('workspace-id')
|
||||
type UnknownResponse = Promise<IWorkspaceResponse<unknown>>
|
||||
// get elements group
|
||||
// export const getLayers = async (reqParams: elementGroupsReq): UnknownResponse => {
|
||||
// const url = `${PREFIX}/workspaces/${workspace_id}/element_groups`
|
||||
// const result = await request.get(url, {
|
||||
// params: {
|
||||
// group_id: reqParams.groupId,
|
||||
// is_distributed: reqParams.isDistributed
|
||||
// },
|
||||
// })
|
||||
// return result.data
|
||||
// }
|
||||
export const getLayers = async (reqParams: elementGroupsReq): UnknownResponse => {
|
||||
return mapLayers
|
||||
}
|
||||
|
||||
// Get elements groups request
|
||||
export const getElementGroupsReq = async (body: elementGroupsReq): Promise<IWorkspaceResponse<any>> => {
|
||||
const url = `${PREFIX}/workspaces/` + workspace_id + '/element-groups'
|
||||
const result = await request.get(url, body)
|
||||
return result.data
|
||||
}
|
||||
// add element
|
||||
export const postElementsReq = async (pid: string, body: PostElementsBody): Promise<IWorkspaceResponse<{ id: string }>> => {
|
||||
const url = `${PREFIX}/workspaces/` + workspace_id + `/element-groups/${pid}/elements`
|
||||
const result = await request.post(url, body)
|
||||
return result.data
|
||||
}
|
||||
// Update map element request
|
||||
export const updateElementsReq = async (id: string, body: PutElementsBody): Promise<IWorkspaceResponse<{ id: string }>> => {
|
||||
const url = `${PREFIX}/workspaces/` + workspace_id + `/elements/${id}`
|
||||
const result = await request.put(url, body)
|
||||
return result.data
|
||||
}
|
||||
// Delete map element
|
||||
export const deleteElementReq = async (id: string, body: {}): Promise<any> => {
|
||||
const url = `${PREFIX}/workspaces/` + workspace_id + `/elements/${id}`
|
||||
const result = await request.delete(url, body)
|
||||
return result.data
|
||||
}
|
||||
|
||||
// Delete layer elements
|
||||
export const deleteLayerEleReq = async (id: string, body: {}): Promise<any> => {
|
||||
const url = `${PREFIX}/workspaces/` + workspace_id + `/element-groups/${id}/elements`
|
||||
const result = await request.delete(url, body)
|
||||
return result.data
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import request, { IWorkspaceResponse } from '/@/api/http/request'
|
||||
const HTTP_PREFIX = '/manage/api/v1'
|
||||
|
||||
// login
|
||||
interface loginBody {
|
||||
username: string,
|
||||
password: string
|
||||
}
|
||||
export const login = async function (body: loginBody): Promise<IWorkspaceResponse<any>> {
|
||||
const url = `${HTTP_PREFIX}/login`
|
||||
const result = await request.post(url, body)
|
||||
return result.data
|
||||
}
|
||||
|
||||
// Refresh Token
|
||||
export const refreshToken = async function (body: {}): Promise<IWorkspaceResponse<any>> {
|
||||
const url = `${HTTP_PREFIX}/token/refresh`
|
||||
const result = await request.post(url, body)
|
||||
return result.data
|
||||
}
|
||||
|
||||
// Get Platform Info
|
||||
export const getPlatformInfo = async function (body: {}): Promise<IWorkspaceResponse<any>> {
|
||||
const url = `${HTTP_PREFIX}/workspaces/current`
|
||||
const result = await request.get(url, body)
|
||||
return result.data
|
||||
}
|
||||
|
||||
// Get User Info
|
||||
export const getUserInfo = async function (body: {}): Promise<IWorkspaceResponse<any>> {
|
||||
const url = `${HTTP_PREFIX}/users/current`
|
||||
const result = await request.get(url, body)
|
||||
return result.data
|
||||
}
|
||||
|
||||
// Get Device Topo
|
||||
export const getDeviceTopo = async function (body: {}): Promise<IWorkspaceResponse<any>> {
|
||||
const url = `${HTTP_PREFIX}/devices/devices`
|
||||
const result = await request.get(url, body)
|
||||
return result.data
|
||||
}
|
||||
|
||||
// Get Livestream Capacity
|
||||
export const getLiveCapacity = async function (body: {}): Promise<IWorkspaceResponse<any>> {
|
||||
const url = `${HTTP_PREFIX}/live/capacity`
|
||||
const result = await request.get(url, body)
|
||||
return result.data
|
||||
}
|
||||
|
||||
// Start Livestream
|
||||
export const startLivestream = async function (body: {}): Promise<IWorkspaceResponse<any>> {
|
||||
const url = `${HTTP_PREFIX}/live/streams/start`
|
||||
const result = await request.post(url, body)
|
||||
return result.data
|
||||
}
|
||||
|
||||
// Stop Livestream
|
||||
export const stopLivestream = async function (body: {}): Promise<IWorkspaceResponse<any>> {
|
||||
const url = `${HTTP_PREFIX}/live/streams/stop`
|
||||
const result = await request.post(url, body)
|
||||
return result.data
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import request from '/@/api/http/request'
|
||||
const HTTP_PREFIX = '/media/api/v1'
|
||||
|
||||
// Get Media Files
|
||||
export const getMediaFiles = async function (wid: string, body: {}): Promise<any> {
|
||||
const url = `${HTTP_PREFIX}/files/${wid}/files`
|
||||
const result = await request.get(url, body)
|
||||
return result.data
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
import { getRoot } from '/@/root'
|
||||
|
||||
const root = getRoot()
|
||||
const components = new Map()
|
||||
|
||||
declare let window:any
|
||||
|
||||
interface JsResponse{
|
||||
code:number,
|
||||
message:string,
|
||||
data:{}
|
||||
}
|
||||
|
||||
export default {
|
||||
init () {
|
||||
components.set('thing', {
|
||||
host: '',
|
||||
connectCallback: '',
|
||||
username: '',
|
||||
password: ''
|
||||
})
|
||||
components.set('liveshare', {
|
||||
videoPublishType: 'video-demand-aux-manual', // video-on-demand、video-by-manual、video-demand-aux-manual
|
||||
statusCallback: ''
|
||||
})
|
||||
components.set('map', {
|
||||
userName: '',
|
||||
elementPreName: ''
|
||||
})
|
||||
components.set('ws', {
|
||||
host: '',
|
||||
token: '',
|
||||
connectCallback: ''
|
||||
})
|
||||
components.set('api', {
|
||||
host: '',
|
||||
token: ''
|
||||
})
|
||||
components.set('tsa', {
|
||||
})
|
||||
components.set('media', {
|
||||
autoUploadPhoto: true, // 是否自动上传图片, 非必需
|
||||
autoUploadPhotoType: 1, // 自动上传的照片类型,0:原图, 1:缩略图, 非必需
|
||||
autoUploadVideo: true // 是否自动上传视频, 非必需
|
||||
})
|
||||
components.set('mission', {
|
||||
})
|
||||
},
|
||||
getComponentParam (key:string) {
|
||||
return components.get(key)
|
||||
},
|
||||
setComponentParam (key:string, value:any) {
|
||||
components.set(key, value)
|
||||
},
|
||||
loadComponent (name:string, param:any):string {
|
||||
return window.djiBridge.platformLoadComponent(name, JSON.stringify(param))
|
||||
},
|
||||
unloadComponent (name:string) :string {
|
||||
return window.djiBridge.platformUnloadComponent(name)
|
||||
},
|
||||
isComponentLoaded (module:string):string {
|
||||
return window.djiBridge.platformIsComponentLoaded(module)
|
||||
},
|
||||
setWorkspaceId (uuid:string):string {
|
||||
return window.djiBridge.platformSetWorkspaceId(uuid)
|
||||
},
|
||||
setPlatformMessage (platformName:string, title:string, desc:string):string {
|
||||
return window.djiBridge.platformSetInformation(platformName, title, desc)
|
||||
},
|
||||
getRemoteControllerSN () :string {
|
||||
return window.djiBridge.platformGetRemoteControllerSN()
|
||||
},
|
||||
getAircraftSN ():string {
|
||||
return window.djiBridge.platformGetAircraftSN()
|
||||
},
|
||||
stopwebview ():string {
|
||||
return window.djiBridge.platformStopSelf()
|
||||
},
|
||||
getToken () :string {
|
||||
const res:string = this.isComponentLoaded('api')
|
||||
const resObj = JSON.parse(res)
|
||||
console.log('api load status:', resObj)
|
||||
if (resObj.data === true) {
|
||||
const tokenRes = JSON.parse(window.djiBridge.apiGetToken())
|
||||
return tokenRes.data
|
||||
} else {
|
||||
console.warn('warning: not api component loaded!!')
|
||||
return ''
|
||||
}
|
||||
},
|
||||
setToken (token:string):string {
|
||||
return window.djiBridge.apiSetToken(token)
|
||||
},
|
||||
setLogEncryptKey (key:string):string {
|
||||
return window.djiBridge.platformSetLogEncryptKey(key)
|
||||
},
|
||||
clearLogEncryptKey ():string {
|
||||
return window.djiBridge.platformClearLogEncryptKey()
|
||||
},
|
||||
getLogPath ():string {
|
||||
return window.djiBridge.platformGetLogPath()
|
||||
},
|
||||
platformVerifyLicense (appId:string, appKey:string, appLicense:string):string {
|
||||
return window.djiBridge.platformVerifyLicense(appId, appKey, appLicense)
|
||||
},
|
||||
isPlatformVerifySuccess ():string {
|
||||
return window.djiBridge.platformIsVerified()
|
||||
},
|
||||
// liveshare
|
||||
/**
|
||||
*
|
||||
* @param type
|
||||
* video-on-demand: 服务器点播,依赖于thing模块,具体的点播命令参见设备物模型的直播服务
|
||||
* video-by-manual:手动点播,配置好直播类型参数之后,在图传页面可修改直播参数,停止直播
|
||||
* video-demand-aux-manual: 混合模式,支持服务器点播,以及图传页面修改直播参数,停止直播
|
||||
*/
|
||||
setVideoPublishType (type:string):string {
|
||||
return window.djiBridge.liveshareSetVideoPublishType(type)
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
* type: liveshare type, 0:unknown, 1:agora, 2:rtmp, 3:rtsp, 4:gb28181
|
||||
*/
|
||||
getLiveshareConfig () {
|
||||
return window.djiBridge.liveshareGetConfig()
|
||||
},
|
||||
|
||||
setLiveshareConfig (type:number, params:string):string {
|
||||
return window.djiBridge.liveshareSetConfig(type, params)
|
||||
},
|
||||
|
||||
setLiveshareStatusCallback (callbackFunc:string) :string {
|
||||
return window.djiBridge.liveshareSetStatusCallback(callbackFunc)
|
||||
},
|
||||
getLiveshareStatus () {
|
||||
return window.djiBridge.liveshareGetStatus()
|
||||
},
|
||||
startLiveshare ():string {
|
||||
return window.djiBridge.liveshareStartLive()
|
||||
},
|
||||
stopLiveshare ():string {
|
||||
return window.djiBridge.liveshareStopLive()
|
||||
},
|
||||
// media
|
||||
setAutoUploadPhoto (auto:boolean):string {
|
||||
return window.djiBridge.mediaSetAutoUploadPhoto(auto)
|
||||
},
|
||||
getAutoUploadPhoto () {
|
||||
return window.djiBridge.mediaGetAutoUploadPhoto()
|
||||
},
|
||||
setUploadPhotoType (type:number):string {
|
||||
return window.djiBridge.mediaSetUploadPhotoType(type)
|
||||
},
|
||||
getUploadPhotoType () {
|
||||
return window.djiBridge.mediaGetUploadPhotoType()
|
||||
},
|
||||
setAutoUploadVideo (auto:boolean):string {
|
||||
return window.djiBridge.mediaSetAutoUploadVideo(auto)
|
||||
},
|
||||
getAutoUploadVideo () {
|
||||
return window.djiBridge.mediaGetAutoUploadVideo()
|
||||
},
|
||||
setDownloadOwner (rcIndex:number):string {
|
||||
return window.djiBridge.mediaSetDownloadOwner(rcIndex)
|
||||
},
|
||||
getDownloadOwner () {
|
||||
return window.djiBridge.mediaGetDownloadOwner()
|
||||
},
|
||||
onBackClickReg () {
|
||||
window.djiBridge.onBackClick = () => {
|
||||
if (root.$router.currentRoute.value.path === '/pilot-home') {
|
||||
console.log(root.$router.currentRoute.value.path)
|
||||
return false
|
||||
} else {
|
||||
console.log(root.$router.currentRoute.value.path)
|
||||
history.go(-1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import request from '/@/api/http/request'
|
||||
const HTTP_PREFIX = '/wayline/api/v1'
|
||||
|
||||
// Get Wayline Files
|
||||
export const getWaylineFiles = async function (wid: string, body: {}): Promise<any> {
|
||||
const url = `${HTTP_PREFIX}/workspaces/${wid}/waylines?` + 'order_by=' + body.order_by + '&page=' + body.page + '&page_size=' + body.page_size
|
||||
const result = await request.get(url)
|
||||
return result.data
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||
import { CURRENT_CONFIG as config } from '/@/api/http/config'
|
||||
|
||||
let socket = {}
|
||||
|
||||
export default {
|
||||
init (getMsgFunc) {
|
||||
const token = localStorage.getItem('x-auth-token')
|
||||
const wspath =
|
||||
config.websocketURL + '?x-auth-token=' + escape(token)
|
||||
socket = new ReconnectingWebSocket(wspath)
|
||||
socket.onopen = this.onOpen
|
||||
socket.onerror = this.onError
|
||||
socket.onmessage = getMsgFunc
|
||||
socket.onclose = this.onClose
|
||||
return socket
|
||||
},
|
||||
onOpen () {
|
||||
console.log('ws opened')
|
||||
},
|
||||
onError (err) {
|
||||
console.error(err)
|
||||
},
|
||||
onClose () {
|
||||
console.log('ws closed')
|
||||
},
|
||||
sendMsg (data) {
|
||||
this.socket.send(data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
|
||||
<title>ic/panel/checkbox_active</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="ic/panel/checkbox_active" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Group-3" fill="#FFFFFF">
|
||||
<polygon id="Shape-Copy" points="11.2103387 4.23529412 6.47425817 8.97137463 4.50089127 6.99800776 2.82352941 8.65563591 4.79689632 10.6290028 6.37558979 12.2076964 6.47425817 12.286631 12.8877005 5.8929223"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 731 B |
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="layer" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 652 652" style="enable-background:new 0 0 652 652;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#241F1F;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M464.9,404.7l44.3-183.9h-95l-40.5,164.9c-5.9,32.2-40.4,47.2-64.9,47.6h-67.3l-22.9,66.3h141.5
|
||||
C395,499.6,446.5,481.8,464.9,404.7"/>
|
||||
<path class="st0" d="M265.5,339.9L310.1,153h97.8l-50.8,212.6c-9.8,41.1-40.3,50.9-68.5,50.9H63.2c-24.8,0-45.6-10.6-34.4-58
|
||||
l20.3-84.8c10.3-43,42.3-52.9,65.4-52.9h157.3l-12.7,53h-80.3c-11.8,0-18.3,2.6-21.6,16.3l-13,54.1c-4.6,19.4,2.2,20.8,16.4,20.8
|
||||
h73.6C247.8,365.2,259.6,364.3,265.5,339.9"/>
|
||||
<polygon class="st0" points="530.7,220.9 484.6,416.5 579.7,416.5 625.7,220.9 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 952 B |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs><path d="m14.042 0 14.043 8.055-5.128 2.941 5.128 2.941-5.127 2.94 5.127 2.942-14.043 8.055L0 19.82l5.126-2.942L0 13.937l5.127-2.941L0 8.056 14.042 0zM7.638 18.318l-2.614 1.5 9.018 5.175 9.017-5.174-2.614-1.5-6.403 3.674-6.404-3.675zm0-5.882-2.615 1.5 9.02 5.175 9.017-5.173-2.615-1.501-6.403 3.674-6.404-3.675zm6.404-9.554L5.024 8.056l9.018 5.174 9.017-5.174-9.017-5.174z" id="layer_a"></path></defs><g transform="translate(2 2)" fill-rule="evenodd"><mask id="layer_b"><use xlink:href="#layer_a"></use></mask><use fill-rule="nonzero" xlink:href="#layer_a"></use><g mask="url(#layer_b)"><path d="M-19-20h68v68h-68z"></path></g></g>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 830 B |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs><path d="M29.5 4v21.5h-4v2H3V8h3V4h23.5zM10.553 18.859 5.5 22.399V25H23v-6.088l-7.011 5.108-5.436-5.161zM27 6.5H8.499L8.5 8h17v15H27V6.5zm-4 4H5.5v8.847l5.293-3.707 5.406 5.133L23 15.819V10.5zm-6.362 1.956a2 2 0 1 1 0 4 2 2 0 0 1 0-4z" id="media_a"></path></defs><g fill-rule="evenodd"><mask id="media_b"><use xlink:href="#media_a"></use></mask><use fill-rule="nonzero" xlink:href="#media_a"></use><g mask="url(#media_b)"><path d="M-17-18h68v68h-68z"></path></g></g>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 666 B |
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
|
||||
<title>2图标/24px/pin</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M6.56239716,0 L13.1247943,9.89949494 L6.56239716,19.7989899 L0,9.89949494 L6.56239716,0 Z M6.56239716,3.61897251 L2.39965953,9.89878783 L6.56239716,16.1786032 L10.7251348,9.89878783 L6.56239716,3.61897251 Z" id="path-1"></path>
|
||||
</defs>
|
||||
<g id="2图标//24px/pin" stroke="none" stroke-width="1" fill="#19BE6B" fill-rule="evenodd">
|
||||
<g id="编组" transform="translate(5.000000, 2.000000)">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="形状" fill="#19BE6B" fill-rule="nonzero" xlink:href="#path-1"></use>
|
||||
<g id="1颜色/ic色/nor" mask="url(#mask-2)" fill="#19BE6B">
|
||||
<g transform="translate(-26.000000, -26.000000)">
|
||||
<rect x="0" y="0" width="68" height="68"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 1.2 KiB |
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
|
||||
<title>2图标/24px/pin</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M6.56239716,0 L13.1247943,9.89949494 L6.56239716,19.7989899 L0,9.89949494 L6.56239716,0 Z M6.56239716,3.61897251 L2.39965953,9.89878783 L6.56239716,16.1786032 L10.7251348,9.89878783 L6.56239716,3.61897251 Z" id="path-1"></path>
|
||||
</defs>
|
||||
<g id="2图标//24px/pin" stroke="none" stroke-width="1" fill="#212121" fill-rule="evenodd">
|
||||
<g id="编组" transform="translate(5.000000, 2.000000)">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="形状" fill="#212121" fill-rule="nonzero" xlink:href="#path-1"></use>
|
||||
<g id="1颜色/ic色/nor" mask="url(#mask-2)" fill="#212121">
|
||||
<g transform="translate(-26.000000, -26.000000)">
|
||||
<rect x="0" y="0" width="68" height="68"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 1.2 KiB |
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
|
||||
<title>2图标/24px/pin</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M6.56239716,0 L13.1247943,9.89949494 L6.56239716,19.7989899 L0,9.89949494 L6.56239716,0 Z M6.56239716,3.61897251 L2.39965953,9.89878783 L6.56239716,16.1786032 L10.7251348,9.89878783 L6.56239716,3.61897251 Z" id="path-1"></path>
|
||||
</defs>
|
||||
<g id="2图标//24px/pin" stroke="none" stroke-width="1" fill="#2D8CF0" fill-rule="evenodd">
|
||||
<g id="编组" transform="translate(5.000000, 2.000000)">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="形状" fill="#2D8CF0" fill-rule="nonzero" xlink:href="#path-1"></use>
|
||||
<g id="1颜色/ic色/nor" mask="url(#mask-2)" fill="#2D8CF0">
|
||||
<g transform="translate(-26.000000, -26.000000)">
|
||||
<rect x="0" y="0" width="68" height="68"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 1.2 KiB |
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
|
||||
<title>2图标/24px/pin</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M6.56239716,0 L13.1247943,9.89949494 L6.56239716,19.7989899 L0,9.89949494 L6.56239716,0 Z M6.56239716,3.61897251 L2.39965953,9.89878783 L6.56239716,16.1786032 L10.7251348,9.89878783 L6.56239716,3.61897251 Z" id="path-1"></path>
|
||||
</defs>
|
||||
<g id="2图标//24px/pin" stroke="none" stroke-width="1" fill="#b620e0" fill-rule="evenodd">
|
||||
<g id="编组" transform="translate(5.000000, 2.000000)">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="形状" fill="#b620e0" fill-rule="nonzero" xlink:href="#path-1"></use>
|
||||
<g id="1颜色/ic色/nor" mask="url(#mask-2)" fill="#b620e0">
|
||||
<g transform="translate(-26.000000, -26.000000)">
|
||||
<rect x="0" y="0" width="68" height="68"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 1.2 KiB |
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
|
||||
<title>2图标/24px/pin</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M6.56239716,0 L13.1247943,9.89949494 L6.56239716,19.7989899 L0,9.89949494 L6.56239716,0 Z M6.56239716,3.61897251 L2.39965953,9.89878783 L6.56239716,16.1786032 L10.7251348,9.89878783 L6.56239716,3.61897251 Z" id="path-1"></path>
|
||||
</defs>
|
||||
<g id="2图标//24px/pin" stroke="none" stroke-width="1" fill="#e23c39" fill-rule="evenodd">
|
||||
<g id="编组" transform="translate(5.000000, 2.000000)">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="形状" fill="#e23c39" fill-rule="nonzero" xlink:href="#path-1"></use>
|
||||
<g id="1颜色/ic色/nor" mask="url(#mask-2)" fill="#e23c39">
|
||||
<g transform="translate(-26.000000, -26.000000)">
|
||||
<rect x="0" y="0" width="68" height="68"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 1.2 KiB |
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
|
||||
<title>2图标/24px/pin</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M6.56239716,0 L13.1247943,9.89949494 L6.56239716,19.7989899 L0,9.89949494 L6.56239716,0 Z M6.56239716,3.61897251 L2.39965953,9.89878783 L6.56239716,16.1786032 L10.7251348,9.89878783 L6.56239716,3.61897251 Z" id="path-1"></path>
|
||||
</defs>
|
||||
<g id="2图标//24px/pin" stroke="none" stroke-width="1" fill="#FFBB00" fill-rule="evenodd">
|
||||
<g id="编组" transform="translate(5.000000, 2.000000)">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="形状" fill="#FFBB00" fill-rule="nonzero" xlink:href="#path-1"></use>
|
||||
<g id="1颜色/ic色/nor" mask="url(#mask-2)" fill="#FFBB00">
|
||||
<g transform="translate(-26.000000, -26.000000)">
|
||||
<rect x="0" y="0" width="68" height="68"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 1.2 KiB |
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<path d="m19.87 10.131-1.01 2.518-.86-.012v-.006l-7.045.002-.068.004a1.75 1.75 0 0 0-1.614 1.737l.006.144.656 8.388h4.804l-1.005 2.5h-6.11l-.168-2.137H.544l-.53-6.896a4.25 4.25 0 0 1 4.03-4.598l.206-.005h2.48a4.25 4.25 0 0 1 .873.09 4.238 4.238 0 0 1 3.089-1.716l.166-.01.166-.003h8.846zm1.106 1.7 5.476 13.566-5.454-1.781-5.497 1.78 5.475-13.565zm.008 4.028.011 6.18H21l2.854.931-2.87-7.111zm-14.21-1.588L4.25 14.27a1.75 1.75 0 0 0-1.75 1.75l.007.153.352 4.597 4.401-.001-.473-6.056a4.3 4.3 0 0 1-.012-.442zM6.193 3.748a3.25 3.25 0 1 1 0 6.5 3.25 3.25 0 0 1 0-6.5zM14.3 0a4.25 4.25 0 1 1 0 8.5 4.25 4.25 0 0 1 0-8.5zM6.192 5.748a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5zM14.3 2a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5z" id="team_a">
|
||||
</path>
|
||||
</defs>
|
||||
<g transform="translate(3 3)" fill-rule="evenodd">
|
||||
<mask id="team_b">
|
||||
<use xlink:href="#team_a"></use>
|
||||
</mask>
|
||||
<use fill-rule="nonzero" xlink:href="#team_a"></use>
|
||||
<g mask="url(#team_b)"><path d="M-20-21h68v68h-68z"></path>
|
||||
</g></g>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 1.2 KiB |
@@ -0,0 +1,311 @@
|
||||
<template>
|
||||
<div class="g-map-wrapper">
|
||||
<div id="g-container" :style="{ width: '100%', height: '100%' }" />
|
||||
<div
|
||||
class="g-action-panle"
|
||||
:style="{ right: drawVisible ? '316px' : '16px' }"
|
||||
>
|
||||
<div class="g-action-item" @click="draw('pin', true)">
|
||||
<a-button type="primary">PIN</a-button>
|
||||
</div>
|
||||
<div class="g-action-item" @click="draw('polyline', true)">
|
||||
<a-button type="primary">Line</a-button>
|
||||
</div>
|
||||
<div class="g-action-item" @click="draw('polygon', true)">
|
||||
<a-button type="primary">Poly</a-button>
|
||||
</div>
|
||||
<div v-if="mouseMode" class="g-action-item" @click="draw('off', false)">
|
||||
<a-button type="primary" danger>X</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onMounted, reactive, ref, watch } from 'vue'
|
||||
import {
|
||||
generateLineContent,
|
||||
generatePointContent,
|
||||
generatePolyContent
|
||||
} from '../utils/map-layer-utils'
|
||||
import { postElementsReq } from '/@/api/layer'
|
||||
import { MapDoodleType, MapElementEnum } from '/@/constants/map'
|
||||
import { useGMapManage } from '/@/hooks/use-g-map'
|
||||
import { useGMapCover } from '/@/hooks/use-g-map-cover'
|
||||
import { useMouseTool } from '/@/hooks/use-mouse-tool'
|
||||
import { getApp } from '/@/root'
|
||||
import { useMyStore } from '/@/store'
|
||||
import { GeojsonCoordinate } from '/@/types/map'
|
||||
import { MapDoodleEnum } from '/@/types/map-enum'
|
||||
import { PostElementsBody } from '/@/types/mapLayer'
|
||||
import { uuidv4 } from '/@/utils/uuid'
|
||||
import { gcj02towgs84, wgs84togcj02 } from '/@/vendors/coordtransform'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'GMap',
|
||||
props: {},
|
||||
setup () {
|
||||
const useMouseToolHook = useMouseTool()
|
||||
const useGMapManageHook = useGMapManage()
|
||||
|
||||
const mouseMode = ref(false)
|
||||
const store = useMyStore()
|
||||
const state = reactive({
|
||||
currentType: '',
|
||||
coverIndex: 0
|
||||
})
|
||||
const shareId = computed(() => {
|
||||
return store.state.layerBaseInfo.share
|
||||
})
|
||||
const defaultId = computed(() => {
|
||||
return store.state.layerBaseInfo.default
|
||||
})
|
||||
const drawVisible = computed(() => {
|
||||
return store.state.drawVisible
|
||||
})
|
||||
watch(
|
||||
() => store.state.wsEvent,
|
||||
newData => {
|
||||
const useGMapCoverHook = useGMapCover()
|
||||
const event = newData
|
||||
let exist = false
|
||||
if (Object.keys(event.mapElementCreat).length !== 0) {
|
||||
console.log(event.mapElementCreat)
|
||||
const ele = event.mapElementCreat
|
||||
store.state.Layers.forEach(layer => {
|
||||
layer.elements.forEach(e => {
|
||||
if (e.id === ele.id) {
|
||||
exist = true
|
||||
console.log('true')
|
||||
}
|
||||
})
|
||||
})
|
||||
if (exist === false) {
|
||||
setLayers({
|
||||
id: ele.id,
|
||||
name: ele.name,
|
||||
resource: ele.resource
|
||||
})
|
||||
|
||||
updateCoordinates('wgs84-gcj02', ele)
|
||||
useGMapCoverHook.init2DPin(
|
||||
ele.name,
|
||||
ele.resource.content.geometry.coordinates,
|
||||
ele.resource.content.properties.color,
|
||||
{
|
||||
id: ele.id,
|
||||
name: ele.name
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
store.state.wsEvent.mapElementCreat = {}
|
||||
}
|
||||
if (Object.keys(event.mapElementUpdate).length !== 0) {
|
||||
console.log(event.mapElementUpdate)
|
||||
console.log('该功能还未实现,请开发商自己增加')
|
||||
store.state.wsEvent.mapElementUpdate = {}
|
||||
}
|
||||
if (Object.keys(event.mapElementDelete).length !== 0) {
|
||||
console.log(event.mapElementDelete)
|
||||
console.log('该功能还未实现,请开发商自己增加')
|
||||
store.state.wsEvent.mapElementDelete = {}
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
function draw (type: MapDoodleType, bool: boolean) {
|
||||
state.currentType = type
|
||||
useMouseToolHook.mouseTool(type, getDrawCallback)
|
||||
mouseMode.value = bool
|
||||
}
|
||||
onMounted(() => {
|
||||
const app = getApp()
|
||||
useGMapManageHook.globalPropertiesConfig(app)
|
||||
})
|
||||
function getDrawCallback ({ obj }) {
|
||||
switch (state.currentType) {
|
||||
case MapDoodleEnum.PIN:
|
||||
postPinPositionResource(obj)
|
||||
break
|
||||
case MapDoodleEnum.POLYLINE:
|
||||
postPolylineResource(obj)
|
||||
break
|
||||
case MapDoodleEnum.POLYGON:
|
||||
postPolygonResource(obj)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
async function postPinPositionResource (obj) {
|
||||
const req = getPinPositionResource(obj)
|
||||
setLayers(req)
|
||||
updateCoordinates('gcj02-wgs84', req)
|
||||
const result = await postElementsReq(shareId.value, req)
|
||||
obj.setExtData({ id: req.id, name: req.name })
|
||||
store.state.coverList.push(obj)
|
||||
// console.log(store.state.coverList)
|
||||
}
|
||||
async function postPolylineResource (obj) {
|
||||
const req = getPolylineResource(obj)
|
||||
setLayers(req)
|
||||
updateCoordinates('gcj02-wgs84', req)
|
||||
const result = await postElementsReq(shareId.value, req)
|
||||
obj.setExtData({ id: req.id, name: req.name })
|
||||
store.state.coverList.push(obj)
|
||||
// console.log(store.state.coverList)
|
||||
}
|
||||
async function postPolygonResource (obj) {
|
||||
const req = getPoygonResource(obj)
|
||||
setLayers(req)
|
||||
updateCoordinates('gcj02-wgs84', req)
|
||||
const result = await postElementsReq(shareId.value, req)
|
||||
obj.setExtData({ id: req.id, name: req.name })
|
||||
store.state.coverList.push(obj)
|
||||
// console.log(store.state.coverList)
|
||||
}
|
||||
|
||||
function getPinPositionResource (obj) {
|
||||
const position = obj.getPosition()
|
||||
const resource = generatePointContent(position)
|
||||
const name = obj._originOpts.title
|
||||
const id = uuidv4()
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
resource
|
||||
}
|
||||
}
|
||||
function getPolylineResource (obj) {
|
||||
const path = obj.getPath()
|
||||
const resource = generateLineContent(path)
|
||||
const { name, id } = getBaseInfo(obj._opts)
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
resource
|
||||
}
|
||||
}
|
||||
function getPoygonResource (obj) {
|
||||
const path = obj.getPath()
|
||||
const resource = generatePolyContent(path)
|
||||
const { name, id } = getBaseInfo(obj._opts)
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
resource
|
||||
}
|
||||
}
|
||||
function getBaseInfo (obj) {
|
||||
const name = obj.title
|
||||
const id = uuidv4()
|
||||
return { name, id }
|
||||
}
|
||||
function setLayers (resource: PostElementsBody) {
|
||||
const layers = store.state.Layers
|
||||
const layer = layers.find(item => item.id.includes(shareId.value))
|
||||
// layer.id = 'private_layer' + uuidv4()
|
||||
// layer?.elements.push(resource)
|
||||
if (layer?.elements) {
|
||||
;(layer?.elements as any[]).push(resource)
|
||||
}
|
||||
console.log('layers', layers)
|
||||
store.commit('SET_LAYER_INFO', layers)
|
||||
}
|
||||
function updateCoordinates (transformType: string, element: any) {
|
||||
const geoType = element.resource?.content.geometry.type
|
||||
const type = element.resource?.type as number
|
||||
if (element.resource) {
|
||||
if (MapElementEnum.PIN === type) {
|
||||
const coordinates = element.resource?.content.geometry
|
||||
.coordinates as GeojsonCoordinate
|
||||
if (transformType === 'wgs84-gcj02') {
|
||||
const transResult = wgs84togcj02(
|
||||
coordinates[0],
|
||||
coordinates[1]
|
||||
) as GeojsonCoordinate
|
||||
element.resource.content.geometry.coordinates = transResult
|
||||
} else if (transformType === 'gcj02-wgs84') {
|
||||
const transResult = gcj02towgs84(
|
||||
coordinates[0],
|
||||
coordinates[1]
|
||||
) as GeojsonCoordinate
|
||||
element.resource.content.geometry.coordinates = transResult
|
||||
}
|
||||
} else if (MapElementEnum.LINE === type && geoType === 'LineString') {
|
||||
const coordinates = element.resource?.content.geometry
|
||||
.coordinates as GeojsonCoordinate[]
|
||||
if (transformType === 'wgs84-gcj02') {
|
||||
coordinates.forEach(coordinate => {
|
||||
coordinate = wgs84togcj02(
|
||||
coordinate[0],
|
||||
coordinate[1]
|
||||
) as GeojsonCoordinate
|
||||
})
|
||||
} else if (transformType === 'gcj02-wgs84') {
|
||||
coordinates.forEach(coordinate => {
|
||||
coordinate = gcj02towgs84(
|
||||
coordinate[0],
|
||||
coordinate[1]
|
||||
) as GeojsonCoordinate
|
||||
})
|
||||
}
|
||||
element.resource.content.geometry.coordinates = coordinates
|
||||
} else if (MapElementEnum.LINE === type && geoType === 'Polygon') {
|
||||
const coordinates = element.resource?.content.geometry
|
||||
.coordinates[0] as GeojsonCoordinate[]
|
||||
|
||||
if (transformType === 'wgs84-gcj02') {
|
||||
coordinates.forEach(coordinate => {
|
||||
coordinate = wgs84togcj02(
|
||||
coordinate[0],
|
||||
coordinate[1]
|
||||
) as GeojsonCoordinate
|
||||
})
|
||||
} else if (transformType === 'gcj02-wgs84') {
|
||||
coordinates.forEach(coordinate => {
|
||||
coordinate = gcj02towgs84(
|
||||
coordinate[0],
|
||||
coordinate[1]
|
||||
) as GeojsonCoordinate
|
||||
})
|
||||
}
|
||||
element.resource.content.geometry.coordinates = [coordinates]
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
draw,
|
||||
mouseMode,
|
||||
drawVisible
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.g-map-wrapper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.g-action-panle {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
.g-action-item {
|
||||
padding-top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.amap-logo {
|
||||
opacity: 0;
|
||||
}
|
||||
.amap-copyright {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<span>
|
||||
<a-tree
|
||||
draggable
|
||||
:defaultExpandAll="true"
|
||||
class="device-map-layers"
|
||||
@drop="onDrop"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<a-tree-node
|
||||
:title="layer.name"
|
||||
:id="layer.id"
|
||||
v-for="layer in getTreeData"
|
||||
:key="layer.id"
|
||||
>
|
||||
<!-- <template #title>
|
||||
{{layer.name}}
|
||||
</template> -->
|
||||
<template v-if="layer.elements">
|
||||
<a-tree-node
|
||||
v-for="resource in layer.elements"
|
||||
:id="getLayerTreeKey('resource', resource.id)"
|
||||
:key="getLayerTreeKey('resource', resource.id)"
|
||||
>
|
||||
<template #title>
|
||||
{{ resource.name }}
|
||||
</template>
|
||||
</a-tree-node>
|
||||
</template>
|
||||
</a-tree-node>
|
||||
</a-tree>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineProps, PropType, reactive } from 'vue'
|
||||
import { useMyStore } from '/@/store'
|
||||
import { DropEvent, mapLayer } from '/@/types/mapLayer'
|
||||
import { getLayerTreeKey } from '/@/utils/layer-tree'
|
||||
const store = useMyStore()
|
||||
const props = defineProps({
|
||||
layerData: Array as PropType<mapLayer[]>
|
||||
})
|
||||
const state = reactive({
|
||||
checkedKeys: [] as string[],
|
||||
expandedKeys: [] as string[]
|
||||
})
|
||||
const getTreeData = computed(() => {
|
||||
// console.log('props.treeData', JSON.parse(JSON.stringify(props.layerData)))
|
||||
return JSON.parse(JSON.stringify(props.layerData))
|
||||
})
|
||||
const shareId = computed(() => {
|
||||
return store.state.layerBaseInfo.share
|
||||
})
|
||||
const defaultId = computed(() => {
|
||||
return store.state.layerBaseInfo.default
|
||||
})
|
||||
async function onDrop ({ node, dragNode, dropPosition, dropToGap }: DropEvent) {
|
||||
let _treeData = props.layerData || []
|
||||
let dragKey = dragNode.eventKey
|
||||
dragKey = dragKey.replaceAll('resource__', '')
|
||||
const dropPos = node.pos.split('-')
|
||||
let dropKey =
|
||||
node.eventKey.includes(shareId.value) ||
|
||||
node.eventKey.includes(defaultId.value)
|
||||
? node.eventKey
|
||||
: node.$parent.eventKey
|
||||
if (!dragKey || !dropKey) return
|
||||
dropKey = dropKey.replaceAll('resource__', '')
|
||||
const loop = (data: mapLayer[], key: string, callback: Function) => {
|
||||
data.forEach((item, index, arr) => {
|
||||
if (item.id === key) {
|
||||
return callback(item, index, arr)
|
||||
}
|
||||
|
||||
if (item.elements) {
|
||||
return loop(item.elements, key, callback)
|
||||
}
|
||||
})
|
||||
}
|
||||
const data = [..._treeData] as mapLayer[]
|
||||
// Find dragObject
|
||||
let dragObj = {} as mapLayer
|
||||
loop(data, dragKey, (item: mapLayer, index: number, arr: mapLayer[]) => {
|
||||
arr.splice(index, 1)
|
||||
dragObj = item
|
||||
})
|
||||
if (!dropToGap) {
|
||||
// Drop on the content
|
||||
loop(data, dropKey, (item: mapLayer) => {
|
||||
item.elements = item.elements || []
|
||||
// where to insert 示例添加到尾部,可以是随意位置
|
||||
item.elements.push(dragObj)
|
||||
})
|
||||
}
|
||||
_treeData = data
|
||||
// console.log('_treeData', _treeData)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
$antPrefix: 'ant';
|
||||
.device-map-layers.#{$antPrefix}-tree {
|
||||
color: #fff;
|
||||
|
||||
.#{$antPrefix}-tree-checkbox:not(.#{$antPrefix}-tree-checkbox-checked)
|
||||
.#{$antPrefix}-tree-checkbox-inner {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
.anticon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
// 第一个层级的 li,有左边距 16px
|
||||
> li {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
&.#{$antPrefix}-tree-treenode-disabled
|
||||
> .#{$antPrefix}-tree-node-content-wrapper {
|
||||
height: 20px;
|
||||
span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
> ul {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.#{$antPrefix}-tree-switcher {
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.#{$antPrefix}-tree-checkbox {
|
||||
z-index: 1;
|
||||
}
|
||||
.#{$antPrefix}-tree-checkbox:hover::after,
|
||||
.#{$antPrefix}-tree-checkbox-wrapper:hover
|
||||
.#{$antPrefix}-tree-checkbox::after {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
.#{$antPrefix}-tree-title {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.#{$antPrefix}-tree-node-content-wrapper {
|
||||
color: #fff;
|
||||
width: calc(100% - 46px);
|
||||
flex: 1;
|
||||
box-sizing: content-box;
|
||||
height: 20px;
|
||||
min-width: 0; // 解决文字溢出不会省略的问题
|
||||
padding-right: 0;
|
||||
|
||||
&:not([draggable='true']) {
|
||||
border-top: 2px transparent solid;
|
||||
border-bottom: 2px transparent solid;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
> span {
|
||||
&::before {
|
||||
// position: absolute;
|
||||
// right: 0;
|
||||
// left: 0;
|
||||
height: 28px;
|
||||
transition: all 0.3s;
|
||||
content: '';
|
||||
}
|
||||
|
||||
// 进度条组件需要相对最外层定位,进度条组件的position不能设置为relative
|
||||
> *:not(.progress-wrapper) {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$antPrefix}-tree-node-selected {
|
||||
background-color: transparent;
|
||||
color: #2d8cf0;
|
||||
> span {
|
||||
&::before {
|
||||
background-color: #4f4f4f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
span.#{$antPrefix}-tree-switcher.#{$antPrefix}-tree-switcher_open
|
||||
.#{$antPrefix}-tree-switcher-icon {
|
||||
transform: rotate(0deg) !important;
|
||||
}
|
||||
span.#{$antPrefix}-tree-switcher.#{$antPrefix}-tree-switcher_close
|
||||
.#{$antPrefix}-tree-switcher-icon {
|
||||
transform: rotate(0deg) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="media-panel-wrapper">
|
||||
<div class="header">Media</div>
|
||||
<a-button type="primary" style="margin-top:20px" @click="onRefresh"
|
||||
>Refresh</a-button
|
||||
>
|
||||
<a-table class="media-table" :columns="columns" :data-source="data">
|
||||
<template #name="{ text, record }">
|
||||
<a :href="record.preview_url">{{ text }}</a>
|
||||
</template>
|
||||
<template #action>
|
||||
<span class="action-area">
|
||||
action
|
||||
</span>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from '@vue/reactivity'
|
||||
import { getMediaFiles } from '/@/api/media'
|
||||
const columns = [
|
||||
{
|
||||
title: 'FileName',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
slots: { customRender: 'name' }
|
||||
},
|
||||
{
|
||||
title: 'FileSize',
|
||||
dataIndex: 'size',
|
||||
key: 'size'
|
||||
},
|
||||
{
|
||||
title: 'PayloadType',
|
||||
dataIndex: 'payload_type',
|
||||
key: 'payload_type',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
key: 'action',
|
||||
slots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
const data = ref([
|
||||
{
|
||||
key: '1',
|
||||
name: 'name1',
|
||||
size: 32,
|
||||
payload_type: 'PM320_DUAL',
|
||||
preview_url: ''
|
||||
}
|
||||
])
|
||||
const onRefresh = async () => {
|
||||
const wid = localStorage.getItem('workspace-id')
|
||||
data.value = []
|
||||
const index = 1
|
||||
const res = await getMediaFiles(wid, {})
|
||||
res.data.forEach(ele => {
|
||||
data.value.push({
|
||||
key: index.toString(),
|
||||
name: ele.file_name
|
||||
})
|
||||
})
|
||||
console.log(res)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.media-panel-wrapper {
|
||||
width: 100%;
|
||||
.media-table {
|
||||
background: #fff;
|
||||
margin-top: 32px;
|
||||
}
|
||||
.header {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
background: #fff;
|
||||
padding: 16px 24px;
|
||||
font-size: 20px;
|
||||
text-align: start;
|
||||
color: #000;
|
||||
}
|
||||
.action-area {
|
||||
color: $primary;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<svg :class="svgClass" :aria-hidden="true" :style="{color: color, width:computedWidth, height:computedWidth}">
|
||||
<use :xlink:href="iconName" :fill="color"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, computed } from 'vue'
|
||||
const props = defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
},
|
||||
})
|
||||
const iconName = computed(() => `#icon-${props.name}`)
|
||||
const svgClass = computed(() => {
|
||||
console.log(props.name, 'props.name')
|
||||
if (props.name) {
|
||||
return `svg-icon icon-${props.name}`
|
||||
}
|
||||
return 'svg-icon'
|
||||
})
|
||||
const computedWidth = computed(() => {
|
||||
const result = props.width || props.size
|
||||
return result ? result + 'px' : '1em'
|
||||
})
|
||||
</script>
|
||||
<style lang='scss'>
|
||||
.svg-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
fill: currentColor;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div class="panel-wrapper">
|
||||
<div class="header">Wayline Library</div>
|
||||
<a-button type="primary" style="margin-top:20px" @click="onRefresh"
|
||||
>Refresh</a-button
|
||||
>
|
||||
<a-table class="table" :columns="columns" :data-source="data">
|
||||
<template #name="{ text, record }">
|
||||
<a :href="record.preview_url">{{ text }}</a>
|
||||
</template>
|
||||
<template #action>
|
||||
<span class="action-area">
|
||||
action
|
||||
</span>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from '@vue/reactivity'
|
||||
import { onMounted } from 'vue'
|
||||
import { getWaylineFiles } from '/@/api/wayline'
|
||||
const columns = [
|
||||
{
|
||||
title: 'FileName',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
slots: { customRender: 'name' }
|
||||
},
|
||||
{
|
||||
title: 'TemplateType',
|
||||
dataIndex: 'template_type',
|
||||
key: 'template_type'
|
||||
},
|
||||
{
|
||||
title: 'Favorited',
|
||||
dataIndex: 'favorite',
|
||||
key: 'favorite'
|
||||
},
|
||||
{
|
||||
title: 'DroneType',
|
||||
dataIndex: 'drone_type',
|
||||
key: 'drone_type'
|
||||
},
|
||||
{
|
||||
title: 'PayloadType',
|
||||
dataIndex: 'payload_type',
|
||||
key: 'payload_type'
|
||||
},
|
||||
{
|
||||
title: 'User',
|
||||
dataIndex: 'user',
|
||||
key: 'user'
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
key: 'action',
|
||||
slots: { customRender: 'action' }
|
||||
}
|
||||
]
|
||||
const data = ref([
|
||||
{
|
||||
key: '1',
|
||||
name: 'name1',
|
||||
template_type: '0',
|
||||
drone_type: '0-60-0',
|
||||
payload_type: 'PM320_DUAL',
|
||||
user: 'pilot',
|
||||
favorited: 'true'
|
||||
}
|
||||
])
|
||||
onMounted(() => {
|
||||
onRefresh()
|
||||
})
|
||||
const onRefresh = async () => {
|
||||
const wid: string = localStorage.getItem('workspace-id')
|
||||
data.value = []
|
||||
const index = 1
|
||||
const res = await getWaylineFiles(wid, {
|
||||
page: 1, // 页数
|
||||
page_size: 9, // 每页大小
|
||||
order_by: 'update_time desc' // 排序, xxx_column_desc, xxx_column_asc, xxx_column(default asc)
|
||||
})
|
||||
console.log(res)
|
||||
res.data.list.forEach(ele => {
|
||||
data.value.push({
|
||||
key: index.toString(),
|
||||
name: ele.name,
|
||||
template_type: ele.template_types[0],
|
||||
drone_type: ele.drone_model_key,
|
||||
payload_type: ele.payload_model_keys[0],
|
||||
user: ele.user_name,
|
||||
favorite: ele.favorited.toString()
|
||||
})
|
||||
})
|
||||
console.log('wayline files:', data.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.panel-wrapper {
|
||||
width: 100%;
|
||||
.table {
|
||||
background: #fff;
|
||||
margin-top: 32px;
|
||||
}
|
||||
.header {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
background: #fff;
|
||||
padding: 16px 24px;
|
||||
font-size: 20px;
|
||||
text-align: start;
|
||||
color: #000;
|
||||
}
|
||||
.action-area {
|
||||
color: $primary;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,19 @@
|
||||
export const AMapConfig = {
|
||||
key: '26d54da6733de88435c68d1a2e88b682',
|
||||
version: '2.0',
|
||||
plugins: [
|
||||
'AMap.Scale',
|
||||
'AMap.ToolBar',
|
||||
'AMap.ControlBar',
|
||||
'AMap.ElasticMarker',
|
||||
'AMap.MapType',
|
||||
'AMap.Geocoder',
|
||||
'AMap.CircleEditor',
|
||||
'AMap.PolygonEditor',
|
||||
'AMap.PolylineEditor',
|
||||
'AMap.PolyEditor',
|
||||
'AMap.RangingTool',
|
||||
'AMap.Weather',
|
||||
'AMap.MouseTool'
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
export enum MapElementColor {
|
||||
Blue = '#2D8CF0',
|
||||
Green = '#19BE6B',
|
||||
Yellow = '#FFBB00',
|
||||
Red = '#E23C39',
|
||||
Orange = '#B620E0',
|
||||
Default = '#212121'
|
||||
}
|
||||
export const MapElementDefaultColor = MapElementColor.Default
|
||||
|
||||
export enum MapDoodleColor {
|
||||
PinColor = '#2D8CF0',
|
||||
PolylineColor = '#3366FF',
|
||||
PolygonColor = '#FF33FF'
|
||||
}
|
||||
|
||||
export enum MapElementEnum {
|
||||
PIN = 0,
|
||||
LINE = 1,
|
||||
POLY = 2
|
||||
}
|
||||
export type MapDoodleType = 'pin' | 'polyline' | 'polygon' | 'off'
|
||||
@@ -0,0 +1,138 @@
|
||||
export const mapLayers = {
|
||||
code: 0,
|
||||
message: 'success',
|
||||
data: {
|
||||
list: [{
|
||||
id: 'private_layer',
|
||||
name: 'Private Layer',
|
||||
order: 0,
|
||||
is_distributed: false,
|
||||
type: 1,
|
||||
is_lock: false,
|
||||
create_time: 1634268707424,
|
||||
elements: [{
|
||||
id: 'b2370d29-be65-42b0-9224-4d816e86dc64',
|
||||
name: 'xuejia n. 1',
|
||||
order: 0,
|
||||
status: 1,
|
||||
display: 1,
|
||||
resource: {
|
||||
content: {
|
||||
type: 'Feature',
|
||||
properties: {
|
||||
color: '#2D8CF0'
|
||||
},
|
||||
geometry: {
|
||||
type: 'Polygon',
|
||||
coordinates: [
|
||||
[
|
||||
[114.156671, 38.468249],
|
||||
[114.139517, 37.372177],
|
||||
[115.52899, 37.712212]
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
type: 4,
|
||||
user_name: 'xuejia n.',
|
||||
user_id: '1402914943455727616'
|
||||
},
|
||||
update_time: 1636966336566,
|
||||
create_time: 1636966325700,
|
||||
elevation_load_status: 0,
|
||||
icon: 'area'
|
||||
}, {
|
||||
id: '768e9fcd-121f-47a6-96b9-f1aee27d32f0',
|
||||
name: 'xuejia n. 1',
|
||||
order: 0,
|
||||
status: 1,
|
||||
display: 1,
|
||||
resource: {
|
||||
content: {
|
||||
type: 'Feature',
|
||||
properties: {
|
||||
color: '#2D8CF0'
|
||||
},
|
||||
geometry: {
|
||||
type: 'LineString',
|
||||
coordinates: [
|
||||
[116.263962, 40.234929],
|
||||
[116.503006, 40.237026],
|
||||
[116.335465, 40.155206],
|
||||
[116.541458, 40.12371]
|
||||
]
|
||||
}
|
||||
},
|
||||
type: 4,
|
||||
user_name: 'xuejia n.',
|
||||
user_id: '1402914943455727616'
|
||||
},
|
||||
update_time: 1636966322636,
|
||||
create_time: 1636966316803,
|
||||
elevation_load_status: 0,
|
||||
icon: 'line'
|
||||
}, {
|
||||
id: '4e741a76-3600-4af5-ace8-d805e7cd31fa',
|
||||
name: 'xuejia n. 2',
|
||||
order: 0,
|
||||
status: 1,
|
||||
display: 1,
|
||||
resource: {
|
||||
content: {
|
||||
type: 'Feature',
|
||||
properties: {
|
||||
color: '#2D8CF0',
|
||||
clampToGround: true
|
||||
},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [116.098223, 39.976538, 104]
|
||||
}
|
||||
},
|
||||
type: 6,
|
||||
user_name: 'xuejia n.',
|
||||
user_id: '1402914943455727616'
|
||||
},
|
||||
update_time: 1636966305229,
|
||||
create_time: 1636966305229,
|
||||
elevation_load_status: 0,
|
||||
icon: 'pin'
|
||||
}, {
|
||||
id: 'efff2b5d-de22-4d48-8d92-4f53170668f6',
|
||||
name: 'xuejia n. 1',
|
||||
order: 0,
|
||||
status: 1,
|
||||
display: 1,
|
||||
resource: {
|
||||
content: {
|
||||
type: 'Feature',
|
||||
properties: {
|
||||
color: '#19BE6B',
|
||||
clampToGround: true
|
||||
},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [113.35367028239645, 23.755194000519843, 22]
|
||||
}
|
||||
},
|
||||
type: 6,
|
||||
user_name: 'xuejia n.',
|
||||
user_id: '1402914943455727616'
|
||||
},
|
||||
update_time: 1636966304432,
|
||||
create_time: 1636966299455,
|
||||
elevation_load_status: 0,
|
||||
icon: 'pin'
|
||||
}]
|
||||
}, {
|
||||
id: 'share_layer',
|
||||
name: 'Share Layer',
|
||||
order: 0,
|
||||
is_distributed: true,
|
||||
type: 2,
|
||||
is_lock: false,
|
||||
create_time: 1634268707414,
|
||||
elements: []
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Environment variable definition
|
||||
// https://cn.vitejs.dev/guide/env-and-mode.html#env-files
|
||||
|
||||
interface ImportMetaEnv {
|
||||
VITE_APP_ENVIRONMENT: 'DEV' | 'STAG' | 'UAT' | 'PROD',
|
||||
// api gateway
|
||||
VITE_APP_APIGATEWAY_BACKEND_HOST: string
|
||||
// More environment variables...
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import pin19be6b from '/@/assets/icons/pin-19be6b.svg'
|
||||
import pin212121 from '/@/assets/icons/pin-212121.svg'
|
||||
import pin2d8cf0 from '/@/assets/icons/pin-2d8cf0.svg'
|
||||
import pinb620e0 from '/@/assets/icons/pin-b620e0.svg'
|
||||
import pine23c39 from '/@/assets/icons/pin-e23c39.svg'
|
||||
import pineffbb00 from '/@/assets/icons/pin-ffbb00.svg'
|
||||
import { getRoot } from '/@/root'
|
||||
import rootStore from '/@/store'
|
||||
import { GeojsonCoordinate } from '/@/types/map'
|
||||
|
||||
export function useGMapCover () {
|
||||
const root = getRoot()
|
||||
const AMap = root.$aMapObj
|
||||
const normalColor = '#2D8CF0'
|
||||
const store = rootStore
|
||||
const coverList = store.state.coverList
|
||||
|
||||
function AddCoverToMap (cover :any) {
|
||||
root.$aMap.add(cover)
|
||||
coverList.push(cover)
|
||||
// console.log('coverList:', store.state.coverList)
|
||||
}
|
||||
function getPinIcon (color?:string) {
|
||||
// console.log('color', color)
|
||||
const colorObj: {
|
||||
[key: number| string]: any
|
||||
} = {
|
||||
'2d8cf0': pin2d8cf0,
|
||||
'19be6b': pin19be6b,
|
||||
212121: pin212121,
|
||||
b620e0: pinb620e0,
|
||||
e23c39: pine23c39,
|
||||
ffbb00: pineffbb00,
|
||||
|
||||
}
|
||||
const iconName = (color?.replaceAll('#', '') || '').toLocaleLowerCase()
|
||||
return new AMap.Icon({
|
||||
// size: new AMap.Size(40, 50),
|
||||
image: colorObj[iconName],
|
||||
// imageOffset: new AMap.Pixel(0, -60),
|
||||
// imageSize: new AMap.Size(40, 50)
|
||||
})
|
||||
}
|
||||
function init2DPin (name: string, coordinates:GeojsonCoordinate, color?:string, data?:{}) {
|
||||
console.log(name, coordinates[0], coordinates[1], color, data)
|
||||
const pin = new AMap.Marker({
|
||||
position: new AMap.LngLat(coordinates[0], coordinates[1]),
|
||||
title: name,
|
||||
icon: getPinIcon(color),
|
||||
// strokeColor: color || normalColor,
|
||||
// fillColor: color || normalColor,
|
||||
extData: data
|
||||
})
|
||||
// console.log('coordinates pin', pin)
|
||||
AddCoverToMap(pin)
|
||||
}
|
||||
function AddOverlayGroup (overlayGroup) {
|
||||
root.$aMap.add(overlayGroup)
|
||||
coverList.push(overlayGroup)
|
||||
}
|
||||
function initPolyline (name: string, coordinates:GeojsonCoordinate[], color?:string, data?:{}) {
|
||||
const path = [] as GeojsonCoordinate[]
|
||||
coordinates.forEach(coordinate => {
|
||||
path.push(new AMap.LngLat(coordinate[0], coordinate[1]))
|
||||
})
|
||||
const polyline = new AMap.Polyline({
|
||||
path: path,
|
||||
strokeColor: color || normalColor,
|
||||
strokeOpacity: 1,
|
||||
strokeWeight: 2,
|
||||
strokeStyle: 'solid',
|
||||
extData: data
|
||||
// draggable: true,
|
||||
})
|
||||
AddOverlayGroup(polyline)
|
||||
}
|
||||
function initPolygon (name: string, coordinates:GeojsonCoordinate[], color?:string, data?:{}) {
|
||||
const path = [] as GeojsonCoordinate[]
|
||||
coordinates.forEach(coordinate => {
|
||||
path.push(new AMap.LngLat(coordinate[0], coordinate[1]))
|
||||
})
|
||||
// console.log('Polygon', path)
|
||||
const Polygon = new AMap.Polygon({
|
||||
path: path,
|
||||
strokeOpacity: 1,
|
||||
strokeWeight: 2,
|
||||
fillColor: color || normalColor,
|
||||
fillOpacity: 0.4,
|
||||
// draggable: true,
|
||||
strokeColor: color || normalColor,
|
||||
extData: data
|
||||
})
|
||||
AddOverlayGroup(Polygon)
|
||||
}
|
||||
function removeCoverFromMap (id:string) {
|
||||
for (let i = 0; i < coverList.length; i++) {
|
||||
const ele = coverList[i]
|
||||
// console.log(ele)
|
||||
const extdata = ele?.getExtData()
|
||||
if (extdata?.id === id) {
|
||||
console.log(extdata)
|
||||
root.$aMap.remove(ele)
|
||||
coverList.slice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
function getElementFromMap (id:string) {
|
||||
// console.log('start', new Date().getTime())
|
||||
const ele = coverList.find(ele => ele?.getExtData().id === id)
|
||||
// console.log('end', new Date().getTime())
|
||||
return ele
|
||||
// coverList.forEach((ele:any) => {
|
||||
// const extdata = ele?.getExtData()
|
||||
// // console.log(extdata)
|
||||
// if (extdata?.id === id) {
|
||||
// return ele
|
||||
// }
|
||||
// })
|
||||
}
|
||||
function updatePinElement (id:string, name: string, coordinates:GeojsonCoordinate, color?:string) {
|
||||
const element = getElementFromMap(id) as any
|
||||
if (element) {
|
||||
const icon = getPinIcon(color)
|
||||
element.setPosition(new AMap.LngLat(coordinates[0], coordinates[1]))
|
||||
element.setIcon(icon)
|
||||
element.setTitle(name)
|
||||
} else {
|
||||
// console.log('into init PIN')
|
||||
init2DPin(name, coordinates, color, {
|
||||
id: id,
|
||||
name: name
|
||||
})
|
||||
}
|
||||
}
|
||||
return {
|
||||
init2DPin,
|
||||
initPolyline,
|
||||
initPolygon,
|
||||
removeCoverFromMap,
|
||||
getElementFromMap,
|
||||
updatePinElement
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import AMapLoader from '@amap/amap-jsapi-loader'
|
||||
import { App, reactive } from 'vue'
|
||||
import { AMapConfig } from '/@/constants/index'
|
||||
|
||||
export function useGMapManage () {
|
||||
const state = reactive({
|
||||
mapEntity: null,
|
||||
mapObj: null,
|
||||
mouseTool: null,
|
||||
})
|
||||
async function initMap (container: string, app:App) {
|
||||
AMapLoader.load({
|
||||
...AMapConfig
|
||||
}).then((AMap) => {
|
||||
state.mapObj = AMap
|
||||
state.mapEntity = new AMap.Map(container, {
|
||||
center: [113.935913, 22.525335],
|
||||
zoom: 15
|
||||
})
|
||||
state.mouseTool = new AMap.MouseTool(state.mapEntity)
|
||||
app.config.globalProperties.$aMap = state.mapEntity
|
||||
app.config.globalProperties.$aMapObj = state.mapObj
|
||||
app.config.globalProperties.$mouseTool = state.mouseTool
|
||||
}).catch(e => {
|
||||
console.log(e)
|
||||
})
|
||||
}
|
||||
function globalPropertiesConfig (app:App) {
|
||||
initMap('g-container', app)
|
||||
}
|
||||
return {
|
||||
globalPropertiesConfig,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { reactive } from 'vue'
|
||||
import pin2d8cf0 from '/@/assets/icons/pin-2d8cf0.svg'
|
||||
import { MapDoodleType } from '/@/constants/map'
|
||||
import { getRoot } from '/@/root'
|
||||
import { MapDoodleEnum } from '/@/types/map-enum'
|
||||
|
||||
export function useMouseTool () {
|
||||
const root = getRoot()
|
||||
const AMap = root.$aMapObj
|
||||
const state = reactive({
|
||||
pinNum: 0,
|
||||
polylineNum: 0,
|
||||
PolygonNum: 0,
|
||||
currentType: '',
|
||||
})
|
||||
function drawPin (type:MapDoodleType, getDrawCallback:Function) {
|
||||
root?.$mouseTool.marker({
|
||||
title: type + state.pinNum,
|
||||
icon: pin2d8cf0,
|
||||
})
|
||||
state.pinNum++
|
||||
root?.$mouseTool.on('draw', getDrawCallback)
|
||||
}
|
||||
function drawPolyline (type:MapDoodleType, getDrawCallback:Function) {
|
||||
root?.$mouseTool.polyline({
|
||||
strokeColor: '#2d8cf0',
|
||||
strokeOpacity: 1,
|
||||
strokeWeight: 2,
|
||||
strokeStyle: 'solid',
|
||||
draggable: true,
|
||||
title: type + state.polylineNum++
|
||||
})
|
||||
root?.$mouseTool.on('draw', getDrawCallback)
|
||||
}
|
||||
function drawPolygon (type:MapDoodleType, getDrawCallback:Function) {
|
||||
root?.$mouseTool.polygon({
|
||||
strokeColor: '#2d8cf0',
|
||||
strokeOpacity: 1,
|
||||
strokeWeight: 2,
|
||||
fillColor: '#1791fc',
|
||||
fillOpacity: 0.4,
|
||||
draggable: true,
|
||||
title: type + state.PolygonNum++
|
||||
})
|
||||
root?.$mouseTool.on('draw', getDrawCallback)
|
||||
}
|
||||
function drawOff (type:MapDoodleType) {
|
||||
root?.$mouseTool.close()
|
||||
root?.$mouseTool.off('draw')
|
||||
}
|
||||
function mouseTool (type: MapDoodleType, getDrawCallback: Function) {
|
||||
state.currentType = type
|
||||
switch (type) {
|
||||
case MapDoodleEnum.PIN:
|
||||
drawPin(type, getDrawCallback)
|
||||
break
|
||||
case MapDoodleEnum.POLYLINE:
|
||||
drawPolyline(type, getDrawCallback)
|
||||
break
|
||||
case MapDoodleEnum.POLYGON:
|
||||
drawPolygon(type, getDrawCallback)
|
||||
break
|
||||
case MapDoodleEnum.Close:
|
||||
drawOff(type)
|
||||
break
|
||||
}
|
||||
}
|
||||
return {
|
||||
mouseTool
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import { antComponents } from './antd'
|
||||
import { CommonComponents } from './use-common-components'
|
||||
import 'virtual:svg-icons-register'
|
||||
import store, { storeKey } from './store'
|
||||
import { createInstance } from '/@/root'
|
||||
import '/@/styles/index.scss'
|
||||
const app = createInstance(App)
|
||||
app.use(store, storeKey)
|
||||
app.use(router)
|
||||
app.use(CommonComponents)
|
||||
app.use(antComponents)
|
||||
app.mount('#demo-app')
|
||||
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="element-map-wrapper">
|
||||
<GMap/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted } from 'vue'
|
||||
import GMap from '/@/components/GMap.vue'
|
||||
export default defineComponent({
|
||||
name: 'Elements',
|
||||
components: { GMap },
|
||||
setup () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="left flex-column flex-justify-start flex-align-center">
|
||||
<p class="fz26 mb0 mt10" style="color: #727272">
|
||||
{{ platformName }}
|
||||
</p>
|
||||
<p class="fz16 ml10 mb0 mt10" style="color: #2d8cf0">
|
||||
status:{{ connect }}
|
||||
</p>
|
||||
<p class="fz32 mb0 mt50" style="color: #000000">{{ workspaceName }}</p>
|
||||
<a-button
|
||||
class="fz20 mt20 flex-column flex-justify-center flex-align-center"
|
||||
style="width: 30vw; height: 12vh;"
|
||||
type="default"
|
||||
@click="onOpen3rdApp"
|
||||
>Open 3rd Party APP</a-button
|
||||
>
|
||||
<a-button
|
||||
class="fz20"
|
||||
style="width: 15vw; height: 12vh; position: fixed; bottom: 7vh"
|
||||
type="primary"
|
||||
@click="onExit"
|
||||
>Quit</a-button
|
||||
>
|
||||
</div>
|
||||
<div class="right flex-column flex-justify-start flex-align-center">
|
||||
<p class="fz24 mb0 mt10 ">Setting</p>
|
||||
<a-button class="mt10 fz16" style="width:90%" @click="onMediaSetting"
|
||||
>Media File Upload Setting</a-button
|
||||
>
|
||||
<a-button class="mt10 fz16" style="width:90%" @click="onLiveshareSetting"
|
||||
>Manual Live Share Setting</a-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { message } from 'ant-design-vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { CURRENT_CONFIG } from '/@/api/http/config'
|
||||
import { getPlatformInfo, getUserInfo } from '/@/api/manage'
|
||||
import apiPilot from '/@/api/pilot-bridge'
|
||||
import { getRoot } from '/@/root'
|
||||
|
||||
const root = getRoot()
|
||||
const connect = ref('Disconnect')
|
||||
const platformName = ref('Unknown')
|
||||
const workspaceName = ref('Unknown')
|
||||
const workspaceDesc = ref('Unknown')
|
||||
const wsId = ref()
|
||||
|
||||
onMounted(() => {
|
||||
apiPilot.init()
|
||||
const token = apiPilot.getToken()
|
||||
if (token) {
|
||||
getPlatformInfo({}).then(res => {
|
||||
console.log(res)
|
||||
platformName.value = res.data.platform_name
|
||||
workspaceName.value = res.data.workspace_name
|
||||
workspaceDesc.value = res.data.workspace_desc
|
||||
wsId.value = res.data.workspace_id
|
||||
apiPilot.setPlatformMessage(
|
||||
platformName.value,
|
||||
workspaceName.value,
|
||||
workspaceDesc.value
|
||||
)
|
||||
apiPilot.setWorkspaceId(wsId.value)
|
||||
})
|
||||
}
|
||||
if (JSON.parse(apiPilot.isComponentLoaded('thing')).data === false || token) {
|
||||
getUserInfo({}).then(res => {
|
||||
const param = {
|
||||
host: res.data.mqtt_addr,
|
||||
username: res.data.mqtt_username,
|
||||
password: res.data.mqtt_password,
|
||||
connectCallback: 'connectCallback'
|
||||
}
|
||||
apiPilot.setComponentParam('thing', param)
|
||||
apiPilot.loadComponent('thing', apiPilot.getComponentParam('thing'))
|
||||
})
|
||||
} else {
|
||||
const connectState = JSON.parse(window.djiBridge.thingGetConnectState())
|
||||
if (connectState.code === 0 && connectState.data) {
|
||||
connect.value = 'Connected'
|
||||
} else {
|
||||
connect.value = 'Disconnect'
|
||||
}
|
||||
}
|
||||
apiPilot.loadComponent('liveshare', apiPilot.getComponentParam('liveshare'))
|
||||
console.log('ws token:', token)
|
||||
apiPilot.setComponentParam('ws', {
|
||||
host: CURRENT_CONFIG.websocketURL,
|
||||
token: token
|
||||
})
|
||||
apiPilot.loadComponent('ws', apiPilot.getComponentParam('ws'))
|
||||
apiPilot.setComponentParam('map', {
|
||||
userName: 'pilot1',
|
||||
elementPreName: 'PILOT'
|
||||
})
|
||||
apiPilot.loadComponent('map', apiPilot.getComponentParam('map'))
|
||||
apiPilot.loadComponent('tsa', apiPilot.getComponentParam('tsa'))
|
||||
apiPilot.loadComponent('media', apiPilot.getComponentParam('media'))
|
||||
apiPilot.loadComponent('mission', {})
|
||||
window.connectCallback = arg => {
|
||||
connectCallback(arg)
|
||||
}
|
||||
apiPilot.onBackClickReg()
|
||||
})
|
||||
const connectCallback = (arg: any) => {
|
||||
console.info('into callback', arg)
|
||||
if (arg) {
|
||||
connect.value = 'Connected'
|
||||
window.djiBridge.mediaSetDownloadOwner(0)
|
||||
window.djiBridge.mediaSetUploadPhotoType(1)
|
||||
} else {
|
||||
connect.value = 'Disconnect'
|
||||
}
|
||||
}
|
||||
const onExit = async (e: any) => {
|
||||
apiPilot.stopwebview()
|
||||
}
|
||||
const onMediaSetting = async (e: any) => {
|
||||
root.$router.push('/pilot-media')
|
||||
}
|
||||
const onLiveshareSetting = async (e: any) => {
|
||||
root.$router.push('/pilot-liveshare')
|
||||
}
|
||||
const onOpen3rdApp = () => {
|
||||
window.open('mydjischeme://www.dji.com')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '/@/styles/index.scss';
|
||||
.page {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
transition: width 0.2s ease;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.left {
|
||||
width: 50%;
|
||||
border-right: red solid 2px;
|
||||
}
|
||||
.right {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<div class="login flex-column flex-justify-center flex-align-center m0 b0">
|
||||
<a-image
|
||||
style="width: 17vw; height: 10vw; margin-bottom: 50px"
|
||||
src="http://lofrev.net/wp-content/photos/2016/09/dji_logo_png.png"
|
||||
/>
|
||||
<p class="logo fz35 pb50">Pilot Cloud API Demo</p>
|
||||
<a-form
|
||||
layout="inline"
|
||||
:model="formState"
|
||||
class="flex-row flex-justify-center flex-align-center"
|
||||
>
|
||||
<a-form-item>
|
||||
<a-input v-model:value="formState.user" placeholder="Username">
|
||||
<template #prefix
|
||||
><UserOutlined style="color: rgba(0, 0, 0, 0.25)"
|
||||
/></template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-input
|
||||
v-model:value="formState.password"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
>
|
||||
<template #prefix
|
||||
><LockOutlined style="color: rgba(0, 0, 0, 0.25)"
|
||||
/></template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button
|
||||
class="m0"
|
||||
type="primary"
|
||||
html-type="submit"
|
||||
:disabled="formState.user === '' || formState.password === ''"
|
||||
@click="onSubmit"
|
||||
>
|
||||
Login
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { LockOutlined, UserOutlined } from '@ant-design/icons-vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { onMounted, reactive, UnwrapRef } from 'vue'
|
||||
import { CURRENT_CONFIG } from '/@/api/http/config'
|
||||
import { login, refreshToken } from '/@/api/manage'
|
||||
import apiPilot from '/@/api/pilot-bridge'
|
||||
import { getRoot } from '/@/root'
|
||||
|
||||
interface FormState {
|
||||
user: string
|
||||
password: string
|
||||
}
|
||||
const root = getRoot()
|
||||
|
||||
const formState: UnwrapRef<FormState> = reactive({
|
||||
user: 'pilot',
|
||||
password: 'pilot123'
|
||||
})
|
||||
let isVerified:any
|
||||
onMounted(async () => {
|
||||
const verifyLicense = JSON.parse(apiPilot.platformVerifyLicense(CURRENT_CONFIG.appId,
|
||||
CURRENT_CONFIG.appKey, CURRENT_CONFIG.appLicense))
|
||||
const platformVerify = JSON.parse(apiPilot.isPlatformVerifySuccess())
|
||||
isVerified = platformVerify.data
|
||||
if (platformVerify.data === true) {
|
||||
message.success('The license verification is successful.')
|
||||
} else {
|
||||
message.error('Filed to verify the license. message is ' + verifyLicense.data)
|
||||
return
|
||||
}
|
||||
const token = apiPilot.getToken()
|
||||
console.log('api token:', token)
|
||||
apiPilot.setPlatformMessage('Cloud Api Platform', '', '')
|
||||
if (token && token !== undefined) {
|
||||
await refreshToken({})
|
||||
.then(res => {
|
||||
apiPilot.setComponentParam('api', {
|
||||
host: CURRENT_CONFIG.baseURL,
|
||||
token: res.data.access_token
|
||||
})
|
||||
const jsres = JSON.parse(
|
||||
apiPilot.loadComponent('api', apiPilot.getComponentParam('api'))
|
||||
)
|
||||
console.log('load api module status:', jsres)
|
||||
apiPilot.setToken(res.data.access_token)
|
||||
localStorage.setItem('x-auth-token', res.data.access_token)
|
||||
message.success('Login Success')
|
||||
root.$router.push('/pilot-home')
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
const onSubmit = async (e: any) => {
|
||||
await login({
|
||||
username: formState.user,
|
||||
password: formState.password
|
||||
})
|
||||
.then(res => {
|
||||
if (!isVerified) {
|
||||
message.error('Please verify the license firstly.')
|
||||
return
|
||||
}
|
||||
console.log('login res:', res)
|
||||
if (res.code === 0) {
|
||||
apiPilot.setComponentParam('api', {
|
||||
host: CURRENT_CONFIG.baseURL,
|
||||
token: res.data.access_token
|
||||
})
|
||||
const jsres = apiPilot.loadComponent(
|
||||
'api',
|
||||
apiPilot.getComponentParam('api')
|
||||
)
|
||||
console.log('load api module res:', jsres)
|
||||
apiPilot.setToken(res.data.access_token)
|
||||
localStorage.setItem('x-auth-token', res.data.access_token)
|
||||
localStorage.setItem('workspace-id', res.data.workspace_id)
|
||||
localStorage.setItem('username', res.data.username)
|
||||
message.success('Login Success')
|
||||
root.$router.push('/pilot-home')
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '/@/styles/index.scss';
|
||||
.login {
|
||||
// background-color: $dark-highlight;
|
||||
height: 100vh;
|
||||
}
|
||||
.logo {
|
||||
color: $primary;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<div
|
||||
class="width-100vw height-100vh flex-column flex-justify-start flex-align-start"
|
||||
>
|
||||
<div
|
||||
class="mt20 flex-row flex-align-center flex-justify-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<p class="ml10 mb0 fz16" style="color: black">
|
||||
Select Video Publish Mode:
|
||||
</p>
|
||||
<a-select
|
||||
style="width: 200px"
|
||||
placeholder="Select Mode"
|
||||
@select="onPublishModeSelect"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in publishModeList"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
|
||||
<a-divider dashed class="mt10 mb0"></a-divider>
|
||||
|
||||
<div
|
||||
class="flex-row flex-align-center flex-justify-between mt10"
|
||||
style="width: 100%"
|
||||
>
|
||||
<p class="ml10 mb0 fz16" style="color: black">Select Live Share Type:</p>
|
||||
<a-select
|
||||
style="width: 200px"
|
||||
placeholder="Select Live Type"
|
||||
@select="onLiveTypeSelect"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in liveTypeList"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
<a-divider dashed class="mt10 mb0"></a-divider>
|
||||
|
||||
<div
|
||||
class="flex-row flex-align-center flex-justify-center mt20"
|
||||
style="width: 100%"
|
||||
>
|
||||
<p>Live Share State: {{ liveState }}</p>
|
||||
</div>
|
||||
<div
|
||||
class="flex-row flex-align-center flex-justify-center mt20"
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-button type="primary" @click="onPlay">Play</a-button>
|
||||
<a-button class="ml20" type="primary" @click="onGetConfig"
|
||||
>Get Config</a-button
|
||||
>
|
||||
<a-button class="ml20" type="primary" @click="onGetStatus"
|
||||
>Get Status</a-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { message } from 'ant-design-vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { CURRENT_CONFIG as config } from '/@/api/http/config'
|
||||
import apiPilot from '/@/api/pilot-bridge'
|
||||
import { getRoot } from '/@/root'
|
||||
|
||||
const root = getRoot()
|
||||
|
||||
const publishModeList = [
|
||||
{
|
||||
value: 'video-on-demand',
|
||||
label: 'video-on-demand'
|
||||
},
|
||||
{
|
||||
value: 'video-by-manual',
|
||||
label: 'video-by-manual'
|
||||
},
|
||||
{
|
||||
value: 'video-demand-aux-manual',
|
||||
label: 'video-demand-aux-manual'
|
||||
}
|
||||
]
|
||||
const liveTypeList = [
|
||||
{
|
||||
value: 1,
|
||||
label: 'AGORA'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: 'RTMP'
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: 'RTSP'
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: 'GB28181'
|
||||
}
|
||||
]
|
||||
const agoraParam = {
|
||||
uid: config.agoraAPPID,
|
||||
token: config.agoraToken,
|
||||
channelId: config.agoraChannel
|
||||
}
|
||||
const rtmpParam = {
|
||||
url: config.rtmpURL + '12345'
|
||||
}
|
||||
const rtspParam = {
|
||||
userName: 'dji-live-share',
|
||||
password: '12345678',
|
||||
port: '8554'
|
||||
}
|
||||
const gb28181Param = {
|
||||
serverIp: '114.55.103.238',
|
||||
serverPort: '5060',
|
||||
serverId: '34020000002000000001',
|
||||
agentId: '34020000001310000004',
|
||||
password: '12345678',
|
||||
agentPort: '7060',
|
||||
agentChannel: '34020000001310000004'
|
||||
}
|
||||
const liveState = ref<string>('STOP')
|
||||
const livetypeSelected = ref<number>(1)
|
||||
const publishModeSelected = ref<string>('video-demand-aux-manual')
|
||||
|
||||
onMounted(() => {
|
||||
const status: any = apiPilot.getLiveshareStatus()
|
||||
console.log(status)
|
||||
// liveState.value =
|
||||
// status.status === 0
|
||||
// ? 'Cannot connect to server'
|
||||
// : status.status === 1
|
||||
// ? 'Connect to server'
|
||||
// : 'Playing'
|
||||
|
||||
// console.log(liveState.value)
|
||||
})
|
||||
const onLiveTypeSelect = (val: any) => {
|
||||
livetypeSelected.value = val
|
||||
message.info('set livetype:' + livetypeSelected.value, 5)
|
||||
}
|
||||
const onPublishModeSelect = (val: string) => {
|
||||
publishModeSelected.value = val
|
||||
message.info(
|
||||
'set publish mode res:' +
|
||||
apiPilot.setVideoPublishType(publishModeSelected.value),
|
||||
5
|
||||
)
|
||||
}
|
||||
const onPlay = () => {
|
||||
switch (livetypeSelected.value) {
|
||||
case 1: {
|
||||
message.info('agoraParam:' + JSON.stringify(agoraParam))
|
||||
apiPilot.setLiveshareConfig(1, JSON.stringify(agoraParam))
|
||||
apiPilot.startLiveshare()
|
||||
break
|
||||
}
|
||||
case 2: {
|
||||
message.info('rtmpParam:' + JSON.stringify(rtmpParam))
|
||||
apiPilot.setLiveshareConfig(2, JSON.stringify(rtmpParam))
|
||||
message.info(apiPilot.startLiveshare())
|
||||
break
|
||||
}
|
||||
case 3: {
|
||||
message.info('rtspParam:' + JSON.stringify(rtspParam))
|
||||
apiPilot.setLiveshareConfig(3, JSON.stringify(rtspParam))
|
||||
apiPilot.startLiveshare()
|
||||
break
|
||||
}
|
||||
case 4: {
|
||||
message.info('gb28181Param:' + JSON.stringify(gb28181Param))
|
||||
apiPilot.setLiveshareConfig(4, JSON.stringify(gb28181Param))
|
||||
apiPilot.startLiveshare()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
const onGetStatus = () => {
|
||||
const status = apiPilot.getLiveshareStatus()
|
||||
message.info(status, 5)
|
||||
}
|
||||
const onGetConfig = () => {
|
||||
const status = apiPilot.getLiveshareConfig()
|
||||
message.info(status, 5)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// @import '/@/styles/index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<div
|
||||
class="width-100vw height-100vh flex-column flex-justify-start flex-align-start"
|
||||
>
|
||||
<p class="fz16 ml10 mt10 mb10 color-text-title color-font-bold">
|
||||
If Enabled, Pilot will upload photos or videos to the server
|
||||
automatically.
|
||||
</p>
|
||||
<div
|
||||
class="flex-row flex-align-center flex-justify-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<p class="ml10 mb0 fz16" style="color: black">Auto Upload Photos</p>
|
||||
<a-switch
|
||||
class="mt0 mb0"
|
||||
v-model:checked="enablePhotoUpload"
|
||||
@change="onPhotoUpload"
|
||||
></a-switch>
|
||||
</div>
|
||||
<div
|
||||
class="flex-row flex-align-center flex-justify-between"
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-radio-group
|
||||
class="mt10 ml20"
|
||||
v-if="enablePhotoUpload == true"
|
||||
v-model:value="photoType"
|
||||
defaultChecked="0"
|
||||
@change="onPhototype"
|
||||
>
|
||||
<a-radio :value="0">Original Photo</a-radio>
|
||||
<a-radio class="ml20" :value="1">Preview Photo</a-radio>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<a-divider dashed class="mt10 mb0"></a-divider>
|
||||
|
||||
<div
|
||||
class="flex-row flex-align-center flex-justify-between mt10"
|
||||
style="width: 100%"
|
||||
>
|
||||
<p class="ml10 mb0 fz16" style="color: black">Auto Upload Video</p>
|
||||
<a-switch
|
||||
@change="onVideoUpload"
|
||||
v-model:checked="enableVideoUpload"
|
||||
></a-switch>
|
||||
</div>
|
||||
<a-divider dashed class="mt10 mb0"></a-divider>
|
||||
|
||||
<div
|
||||
class="flex-row flex-align-center flex-justify-between mt20"
|
||||
style="width: 100%"
|
||||
>
|
||||
<p class="ml10 mb0 fz16 color-font-bold" style="color: black">
|
||||
Path for uploading media resources in dual-controller mode
|
||||
</p>
|
||||
<a-radio-group
|
||||
class="mt0 mb0"
|
||||
v-model:value="uploadPath"
|
||||
button-style="solid"
|
||||
@change="onUploadPath"
|
||||
>
|
||||
<a-radio-button :value="0">Mine</a-radio-button>
|
||||
<a-radio-button :value="1">Another</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { message } from 'ant-design-vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import apiPilot from '/@/api/pilot-bridge'
|
||||
import { getRoot } from '/@/root'
|
||||
|
||||
const root = getRoot()
|
||||
|
||||
const enablePhotoUpload = ref<boolean>(true)
|
||||
const enableVideoUpload = ref<boolean>(false)
|
||||
const photoType = ref<number>(1)
|
||||
const uploadPath = ref<number>(0)
|
||||
|
||||
onMounted(() => {
|
||||
message.info('After setting, please use the physical button of the remote control to return, otherwise the setting is invalid.')
|
||||
|
||||
enablePhotoUpload.value =
|
||||
apiPilot.getAutoUploadPhoto() === undefined
|
||||
? true
|
||||
: apiPilot.getAutoUploadPhoto()
|
||||
enableVideoUpload.value =
|
||||
apiPilot.getAutoUploadVideo() === undefined
|
||||
? false
|
||||
: apiPilot.getAutoUploadVideo()
|
||||
photoType.value =
|
||||
apiPilot.getUploadPhotoType() === undefined
|
||||
? 1
|
||||
: apiPilot.getUploadPhotoType()
|
||||
uploadPath.value =
|
||||
apiPilot.getDownloadOwner() === undefined ? 0 : apiPilot.getDownloadOwner()
|
||||
|
||||
console.log(
|
||||
enablePhotoUpload.value,
|
||||
enableVideoUpload.value,
|
||||
photoType.value,
|
||||
uploadPath.value
|
||||
)
|
||||
apiPilot.setComponentParam('media', {
|
||||
autoUploadPhoto: enablePhotoUpload.value,
|
||||
autoUploadPhotoType: photoType.value,
|
||||
autoUploadVideo: enableVideoUpload.value
|
||||
})
|
||||
})
|
||||
const onPhotoUpload = () => {
|
||||
apiPilot.setAutoUploadPhoto(enablePhotoUpload.value)
|
||||
}
|
||||
const onVideoUpload = () => {
|
||||
console.log(enableVideoUpload.value)
|
||||
apiPilot.setAutoUploadVideo(enableVideoUpload.value)
|
||||
}
|
||||
const onPhototype = () => {
|
||||
console.log(photoType.value)
|
||||
apiPilot.setUploadPhotoType(photoType.value)
|
||||
}
|
||||
const onUploadPath = (e: any) => {
|
||||
apiPilot.setDownloadOwner(uploadPath.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// @import '/@/styles/index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,208 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="showLogin"
|
||||
class="login flex-column flex-justify-center flex-align-center m0 b0"
|
||||
>
|
||||
<a-image
|
||||
style="width: 17vw; height: 10vw; margin-bottom: 50px"
|
||||
src="http://lofrev.net/wp-content/photos/2016/09/dji_logo_png.png"
|
||||
/>
|
||||
<p class="logo fz35 pb50">Cloud API Demo</p>
|
||||
<a-form
|
||||
layout="inline"
|
||||
:model="formState"
|
||||
class="flex-row flex-justify-center flex-align-center"
|
||||
>
|
||||
<a-form-item>
|
||||
<a-input v-model:value="formState.user" placeholder="Username">
|
||||
<template #prefix
|
||||
><UserOutlined style="color: rgba(0, 0, 0, 0.25)"
|
||||
/></template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-input
|
||||
v-model:value="formState.password"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
>
|
||||
<template #prefix
|
||||
><LockOutlined style="color: rgba(0, 0, 0, 0.25)"
|
||||
/></template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button
|
||||
class="m0"
|
||||
type="primary"
|
||||
html-type="submit"
|
||||
:disabled="formState.user === '' || formState.password === ''"
|
||||
@click="onSubmit"
|
||||
>
|
||||
Login
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
<div v-else class="project-app-wrapper">
|
||||
<div class="left">
|
||||
<Sidebar />
|
||||
<div class="main-content uranus-scrollbar dark">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="map-wrapper">
|
||||
<GMap />
|
||||
</div>
|
||||
<div class="media-wrapper" v-if="getMediaRoute()">
|
||||
<MediaPanel />
|
||||
</div>
|
||||
<div class="wayline-wrapper" v-if="getWaylineRoute()">
|
||||
<WaylinePanel />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { LockOutlined, UserOutlined } from '@ant-design/icons-vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { reactive, ref, UnwrapRef } from 'vue'
|
||||
import Sidebar from './sidebar.vue'
|
||||
import { login } from '/@/api/manage'
|
||||
import websocket from '/@/api/websocket'
|
||||
import GMap from '/@/components/GMap.vue'
|
||||
import MediaPanel from '/@/components/MediaPanel.vue'
|
||||
import WaylinePanel from '/@/components/wayline-panel.vue'
|
||||
import { useGMapCover } from '/@/hooks/use-g-map-cover'
|
||||
import { getRoot } from '/@/root'
|
||||
import { useMyStore } from '/@/store'
|
||||
|
||||
interface FormState {
|
||||
user: string
|
||||
password: string
|
||||
}
|
||||
|
||||
const root = getRoot()
|
||||
const showLogin = ref(true)
|
||||
const store = useMyStore()
|
||||
const formState: UnwrapRef<FormState> = reactive({
|
||||
user: 'adminPC',
|
||||
password: 'adminPC'
|
||||
})
|
||||
let socket = {} as any
|
||||
const gMapCoverHook = useGMapCover()
|
||||
|
||||
const onSubmit = async (e: any) => {
|
||||
const result = await login({
|
||||
username: formState.user,
|
||||
password: formState.password
|
||||
})
|
||||
if (result.code === 0) {
|
||||
console.log(result)
|
||||
localStorage.setItem('x-auth-token', result.data.access_token)
|
||||
localStorage.setItem('workspace-id', result.data.workspace_id)
|
||||
localStorage.setItem('username', result.data.username)
|
||||
showLogin.value = false
|
||||
message.info('login success')
|
||||
socket = websocket.init(wsGetMsg)
|
||||
}
|
||||
}
|
||||
|
||||
// function wsInfo (data) {
|
||||
// store.commit('SET_DEVICE_INFO', data)
|
||||
// }
|
||||
// function getDeviceInfo () {
|
||||
// const info = store.state.DeviceInfo
|
||||
// console.log(info)
|
||||
const wsGetMsg = async (res: any) => {
|
||||
const payload = JSON.parse(res.data)
|
||||
// console.log(payload)
|
||||
switch (payload.biz_code) {
|
||||
case 'gateway_osd': {
|
||||
store.commit('SET_GATEWAY_INFO', payload.data)
|
||||
break
|
||||
}
|
||||
case 'device_osd': {
|
||||
store.commit('SET_DEVICE_INFO', payload.data)
|
||||
break
|
||||
}
|
||||
case 'map_element_create': {
|
||||
store.commit('SET_MAP_ELEMENT_CREATE', payload.data)
|
||||
break
|
||||
}
|
||||
case 'map_element_update': {
|
||||
store.commit('SET_MAP_ELEMENT_UPDATE', payload.data)
|
||||
break
|
||||
}
|
||||
case 'map_element_delete': {
|
||||
store.commit('SET_MAP_ELEMENT_DELETE', payload.data)
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function getMediaRoute () {
|
||||
return root.$route.name === 'media'
|
||||
}
|
||||
|
||||
function getWaylineRoute () {
|
||||
return root.$route.name === 'wayline'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '/@/styles/index.scss';
|
||||
.login {
|
||||
background-color: $dark-highlight;
|
||||
height: 100vh;
|
||||
}
|
||||
.logo {
|
||||
color: $primary;
|
||||
}
|
||||
.project-app-wrapper {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
transition: width 0.2s ease;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.left {
|
||||
width: 450px;
|
||||
display: flex;
|
||||
background-color: #232323;
|
||||
float: left;
|
||||
}
|
||||
.right {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.map-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.main-content {
|
||||
flex: 1;
|
||||
color: $text-white-basic;
|
||||
}
|
||||
.media-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 100;
|
||||
background: #f6f8fa;
|
||||
padding: 16px;
|
||||
}
|
||||
.wayline-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 100;
|
||||
background: #f6f8fa;
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,438 @@
|
||||
<template>
|
||||
<div class="project-layer-wrapper">
|
||||
<LayersTree
|
||||
:layer-data="mapLayers"
|
||||
class="project-layer-content"
|
||||
@check="checkLayer"
|
||||
@select="selectLayer"
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
v-model:checkedKeys="checkedKeys"
|
||||
/>
|
||||
<a-drawer
|
||||
title="Map Element"
|
||||
placement="right"
|
||||
:closable="true"
|
||||
v-model:visible="visible"
|
||||
:mask="false"
|
||||
wrapClassName="drawer-element-wrapper"
|
||||
@close="closeDrawer"
|
||||
width="300"
|
||||
>
|
||||
<div class="drawer-element-content">
|
||||
<div class="name element-item">
|
||||
<span class="title">Name:</span>
|
||||
<a-input
|
||||
v-model:value="layerState.layerName"
|
||||
style="width:120px"
|
||||
placeholder="element name"
|
||||
@change="changeLayer"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="longitude element-item"
|
||||
v-if="layerState.currentType === geoType.Point"
|
||||
>
|
||||
<span class="title">Longitude:</span>
|
||||
<a-input
|
||||
v-model:value="layerState.longitude"
|
||||
style="width:120px"
|
||||
placeholder="longitude"
|
||||
@change="changeLayer"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="latitude element-item"
|
||||
v-if="layerState.currentType === geoType.Point"
|
||||
>
|
||||
<span class="title">Latitude:</span>
|
||||
<a-input
|
||||
v-model:value="layerState.latitude"
|
||||
style="width:120px"
|
||||
placeholder="latitude"
|
||||
@change="changeLayer"
|
||||
/>
|
||||
</div>
|
||||
<div class="color-content">
|
||||
<span class="mr30">Color: </span>
|
||||
<div
|
||||
v-for="item in colors"
|
||||
:key="item.id"
|
||||
class="color-item"
|
||||
:style="'background:' + item.color"
|
||||
@click="changeColor(item)"
|
||||
>
|
||||
<svg-icon
|
||||
v-if="item.color === layerState.color"
|
||||
:size="18"
|
||||
name="check"
|
||||
></svg-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-row flex-justify-around flex-align-center mt20">
|
||||
<a-button type="primary" @click="deleteElement">Delete</a-button>
|
||||
</div>
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, ref, watch } from 'vue'
|
||||
import {
|
||||
deleteElementReq,
|
||||
getElementGroupsReq,
|
||||
updateElementsReq
|
||||
} from '/@/api/layer'
|
||||
import LayersTree from '/@/components/LayersTree.vue'
|
||||
import { MapDoodleColor, MapElementEnum } from '/@/constants/map'
|
||||
import { useGMapCover } from '/@/hooks/use-g-map-cover'
|
||||
import { getRoot } from '/@/root'
|
||||
import { useMyStore } from '/@/store'
|
||||
import { GeojsonCoordinate, LayerResource } from '/@/types/map'
|
||||
import { Color, GeoType } from '/@/types/mapLayer'
|
||||
import { generatePoint } from '/@/utils/genjson'
|
||||
import { gcj02towgs84, wgs84togcj02 } from '/@/vendors/coordtransform'
|
||||
|
||||
const root = getRoot()
|
||||
const store = useMyStore()
|
||||
let useGMapCoverHook = useGMapCover(store)
|
||||
console.log('store', store)
|
||||
const mapLayers = ref(store.state.Layers)
|
||||
const checkedKeys = ref<string[]>([])
|
||||
const selectedKeys = ref<string[]>([])
|
||||
const selectedKey = ref<string>('')
|
||||
const selectedLayer = ref<any>(null)
|
||||
const visible = ref<boolean>(false)
|
||||
store.commit('SET_DRAW_VISIBLE_INFO', visible.value)
|
||||
const geoType = GeoType
|
||||
const layerState = reactive({
|
||||
layerName: '',
|
||||
layerId: '',
|
||||
longitude: 0,
|
||||
latitude: 0,
|
||||
currentType: '', // “LineString”,"Polygon","Point"
|
||||
color: '#212121'
|
||||
})
|
||||
const colors = ref<Color[]>([
|
||||
{ id: 1, name: 'BLUE', color: '#2D8CF0', selected: true },
|
||||
{ id: 2, name: 'GREEN', color: '#19BE6B', selected: false },
|
||||
{ id: 3, name: 'YELLOW', color: '#FFBB00', selected: false },
|
||||
{ id: 4, name: 'ORANGE', color: '#B620E0', selected: false },
|
||||
{ id: 5, name: 'RED', color: '#E23C39', selected: false },
|
||||
{ id: 6, name: 'NAME_DEFAULT', color: '#212121', selected: false }
|
||||
])
|
||||
|
||||
async function getAllElement () {
|
||||
getElementGroups('init')
|
||||
setTimeout(() => {
|
||||
useGMapCoverHook = useGMapCover()
|
||||
initMapCover()
|
||||
}, 1000)
|
||||
}
|
||||
function initMapCover () {
|
||||
mapLayers.value.forEach(item => {
|
||||
if (item.elements) {
|
||||
setMapCoverByElement(item.elements)
|
||||
}
|
||||
})
|
||||
}
|
||||
watch(
|
||||
() => store.state.Layers,
|
||||
newData => {
|
||||
mapLayers.value = newData
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
function setMapCoverByElement (elements: LayerResource[]) {
|
||||
elements.forEach(element => {
|
||||
const name = element.name
|
||||
const color = element.resource?.content.properties.color
|
||||
const type = element.resource?.type as number
|
||||
updateMapElement(element, name, color)
|
||||
})
|
||||
}
|
||||
function updateMapElement (
|
||||
element: LayerResource,
|
||||
name: string,
|
||||
color: string | undefined
|
||||
) {
|
||||
const geoType = element.resource?.content.geometry.type
|
||||
const id = element.id
|
||||
const type = element.resource?.type as number
|
||||
|
||||
if (MapElementEnum.PIN === type) {
|
||||
const coordinates = element.resource?.content.geometry
|
||||
.coordinates as GeojsonCoordinate
|
||||
useGMapCoverHook.updatePinElement(id, name, coordinates, color)
|
||||
} else if (MapElementEnum.LINE === type && geoType === 'LineString') {
|
||||
const coordinates = element.resource?.content.geometry
|
||||
.coordinates as GeojsonCoordinate[]
|
||||
useGMapCoverHook.initPolyline(name, coordinates, color, {
|
||||
id: id,
|
||||
name: name
|
||||
})
|
||||
} else if (MapElementEnum.LINE === type && geoType === 'Polygon') {
|
||||
const coordinates = element.resource?.content.geometry
|
||||
.coordinates[0] as GeojsonCoordinate[]
|
||||
useGMapCoverHook.initPolygon(name, coordinates, color, {
|
||||
id: id,
|
||||
name: name
|
||||
})
|
||||
}
|
||||
}
|
||||
function checkLayer (keys: string[]) {
|
||||
console.log('checkLayer', keys, selectedKeys.value, checkedKeys.value)
|
||||
}
|
||||
function selectLayer (keys: string[], e) {
|
||||
// console.log('selectLayer', e.node.eventKey, e.selected)
|
||||
if (e.selected) {
|
||||
selectedKey.value = e.node.eventKey
|
||||
selectedLayer.value = getCurrentLayer(selectedKey.value)
|
||||
setBaseInfo()
|
||||
}
|
||||
visible.value = e.selected
|
||||
store.commit('SET_DRAW_VISIBLE_INFO', visible.value)
|
||||
// store.dispatch('updateElement', { type: 'is_select', id: e.node.eventKey, bool: e.selected })
|
||||
}
|
||||
function getCurrentLayer (id: string) {
|
||||
const Layers = store.state.Layers
|
||||
const key = id.replaceAll('resource__', '')
|
||||
// console.log('selectedKey.value', selectedKey.value)
|
||||
let layer = null
|
||||
const findCan = function (V) {
|
||||
V.forEach(item => {
|
||||
if (item.id === key) {
|
||||
layer = item
|
||||
}
|
||||
if (item.elements) {
|
||||
findCan(item.elements)
|
||||
}
|
||||
})
|
||||
}
|
||||
findCan(Layers)
|
||||
// const layer = Layers.find(item => item.elements.find(el => el.id === key))
|
||||
console.log('layer', layer)
|
||||
return layer
|
||||
}
|
||||
function setBaseInfo () {
|
||||
const layer = selectedLayer.value
|
||||
if (layer) {
|
||||
const geoType = layer.resource?.content.geometry.type
|
||||
// “LineString”,"Polygon","Point"
|
||||
layerState.currentType = geoType
|
||||
layerState.layerName = layer.name
|
||||
layerState.layerId = layer.id
|
||||
layerState.color = layer.resource?.content.properties.color
|
||||
switch (geoType) {
|
||||
case GeoType.Point:
|
||||
layerState.longitude = layer.resource?.content.geometry.coordinates[0]
|
||||
layerState.latitude = layer.resource?.content.geometry.coordinates[1]
|
||||
break
|
||||
case GeoType.LineString:
|
||||
break
|
||||
case GeoType.Polygon:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
getAllElement()
|
||||
})
|
||||
function closeDrawer () {
|
||||
store.commit('SET_DRAW_VISIBLE_INFO', false)
|
||||
selectedKeys.value = []
|
||||
}
|
||||
function changeColor (color: Color) {
|
||||
layerState.color = color.color
|
||||
|
||||
updateElements()
|
||||
}
|
||||
function changeLayer (val: string) {
|
||||
updateElements()
|
||||
}
|
||||
async function deleteElement () {
|
||||
const elementid = selectedLayer.value.id
|
||||
|
||||
await deleteElementReq(elementid, {}).then(async (res: any) => {
|
||||
// console.log('delete element res:', res)
|
||||
if (res.code !== 0) {
|
||||
console.warn(res)
|
||||
return
|
||||
}
|
||||
visible.value = false
|
||||
store.commit('SET_DRAW_VISIBLE_INFO', visible.value)
|
||||
useGMapCoverHook.removeCoverFromMap(elementid)
|
||||
getElementGroups()
|
||||
})
|
||||
}
|
||||
async function getElementGroups (type?: string) {
|
||||
const result = await getElementGroupsReq({
|
||||
groupId: '',
|
||||
isDistributed: true
|
||||
})
|
||||
mapLayers.value = result.data
|
||||
mapLayers.value = updateWgs84togcj02()
|
||||
if (type && type === 'init') {
|
||||
store.dispatch('setLayerInfo', mapLayers.value)
|
||||
}
|
||||
store.commit('SET_LAYER_INFO', mapLayers.value)
|
||||
}
|
||||
async function updateElements () {
|
||||
let content = null
|
||||
if (layerState.currentType === GeoType.Point) {
|
||||
const position = {
|
||||
height: 0,
|
||||
latitude: Number(layerState.latitude || 0),
|
||||
longitude: Number(layerState.longitude || 0)
|
||||
}
|
||||
const cxt = generatePoint(position, {
|
||||
color: layerState.color || MapDoodleColor.PinColor,
|
||||
clampToGround: true
|
||||
})
|
||||
content = {
|
||||
type: MapElementEnum.PIN,
|
||||
geometry: cxt.geometry,
|
||||
properties: cxt.properties
|
||||
}
|
||||
const currentLayer = selectedLayer.value
|
||||
currentLayer.resource.content = content
|
||||
selectedLayer.value = currentLayer
|
||||
} else {
|
||||
const currentLayer = selectedLayer.value
|
||||
content = currentLayer.resource.content
|
||||
content.properties.color = layerState.color
|
||||
}
|
||||
updateMapElement(selectedLayer.value, layerState.layerName, layerState.color)
|
||||
const result = await updateElementsReq(layerState.layerId, {
|
||||
name: layerState.layerName,
|
||||
content: content
|
||||
})
|
||||
getElementGroups()
|
||||
}
|
||||
|
||||
function updateWgs84togcj02 () {
|
||||
const layers = mapLayers.value
|
||||
layers.forEach(item => {
|
||||
if (item.elements) {
|
||||
item.elements.forEach(ele => {
|
||||
updateCoordinates('wgs84-gcj02', ele)
|
||||
})
|
||||
}
|
||||
})
|
||||
return layers
|
||||
}
|
||||
function updateCoordinates (transformType: string, element: LayerResource) {
|
||||
const geoType = element.resource?.content.geometry.type
|
||||
const type = element.resource?.type as number
|
||||
if (element.resource) {
|
||||
if (MapElementEnum.PIN === type) {
|
||||
const coordinates = element.resource?.content.geometry
|
||||
.coordinates as GeojsonCoordinate
|
||||
if (transformType === 'wgs84-gcj02') {
|
||||
const transResult = wgs84togcj02(
|
||||
coordinates[0],
|
||||
coordinates[1]
|
||||
) as GeojsonCoordinate
|
||||
element.resource.content.geometry.coordinates = transResult
|
||||
} else if (transformType === 'gcj02-wgs84') {
|
||||
const transResult = gcj02towgs84(
|
||||
coordinates[0],
|
||||
coordinates[1]
|
||||
) as GeojsonCoordinate
|
||||
element.resource.content.geometry.coordinates = transResult
|
||||
}
|
||||
} else if (MapElementEnum.LINE === type && geoType === 'LineString') {
|
||||
const coordinates = element.resource?.content.geometry
|
||||
.coordinates as GeojsonCoordinate[]
|
||||
if (transformType === 'wgs84-gcj02') {
|
||||
coordinates.forEach(coordinate => {
|
||||
coordinate = wgs84togcj02(
|
||||
coordinate[0],
|
||||
coordinate[1]
|
||||
) as GeojsonCoordinate
|
||||
})
|
||||
} else if (transformType === 'gcj02-wgs84') {
|
||||
coordinates.forEach(coordinate => {
|
||||
coordinate = gcj02towgs84(
|
||||
coordinate[0],
|
||||
coordinate[1]
|
||||
) as GeojsonCoordinate
|
||||
})
|
||||
}
|
||||
element.resource.content.geometry.coordinates = coordinates
|
||||
} else if (MapElementEnum.LINE === type && geoType === 'Polygon') {
|
||||
const coordinates = element.resource?.content.geometry
|
||||
.coordinates[0] as GeojsonCoordinate[]
|
||||
|
||||
if (transformType === 'wgs84-gcj02') {
|
||||
coordinates.forEach(coordinate => {
|
||||
coordinate = wgs84togcj02(
|
||||
coordinate[0],
|
||||
coordinate[1]
|
||||
) as GeojsonCoordinate
|
||||
})
|
||||
} else if (transformType === 'gcj02-wgs84') {
|
||||
coordinates.forEach(coordinate => {
|
||||
coordinate = gcj02towgs84(
|
||||
coordinate[0],
|
||||
coordinate[1]
|
||||
) as GeojsonCoordinate
|
||||
})
|
||||
}
|
||||
element.resource.content.geometry.coordinates = [coordinates]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '/@/styles/index.scss';
|
||||
.project-layer-wrapper {
|
||||
padding-top: 16px;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.drawer-element-wrapper {
|
||||
.ant-drawer-content {
|
||||
background-color: $dark-highlight;
|
||||
color: $text-white-basic;
|
||||
.ant-drawer-header {
|
||||
background-color: $dark-highlight;
|
||||
.ant-drawer-title {
|
||||
color: $text-white-basic;
|
||||
}
|
||||
.ant-drawer-close {
|
||||
color: $text-white-basic;
|
||||
}
|
||||
}
|
||||
.ant-input {
|
||||
background-color: #101010;
|
||||
border-color: $dark-border;
|
||||
color: $text-white-basic;
|
||||
}
|
||||
}
|
||||
.color-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
.color-item {
|
||||
cursor: pointer;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
display: inline-flex;
|
||||
width: 80px;
|
||||
}
|
||||
.element-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,327 @@
|
||||
<template>
|
||||
<div class="flex-column flex-justify-start flex-align-center">
|
||||
<p class="fz24">Live streaming source selection</p>
|
||||
<div class="flex-row flex-justify-center flex-align-center mt10">
|
||||
<a-select
|
||||
style="width:150px"
|
||||
placeholder="Select Drone"
|
||||
@select="onDroneSelect"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in dronePara.droneList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-select-option
|
||||
>
|
||||
</a-select>
|
||||
<a-select
|
||||
class="ml10"
|
||||
style="width:150px"
|
||||
placeholder="Select Camera"
|
||||
@select="onCameraSelect"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in dronePara.cameraList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-select-option
|
||||
>
|
||||
</a-select>
|
||||
<a-select
|
||||
class="ml10"
|
||||
style="width:150px"
|
||||
placeholder="Select Lens"
|
||||
@select="onVideoSelect"
|
||||
>
|
||||
<a-select-option
|
||||
class="ml10"
|
||||
v-for="item in dronePara.videoList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-select-option
|
||||
>
|
||||
</a-select>
|
||||
<a-select
|
||||
class="ml10"
|
||||
style="width:150px"
|
||||
placeholder="Select Clarity"
|
||||
@select="onClaritySelect"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in clarityList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-select-option
|
||||
>
|
||||
</a-select>
|
||||
</div>
|
||||
<p class="fz24 mt10">Agora Parameter</p>
|
||||
<p class="fz16">
|
||||
Note: Obtain The Following Parameters From https://console.agora.io
|
||||
</p>
|
||||
<div class="flex-row flex-justify-center flex-align-center">
|
||||
<a-input v-model:value="agoraPara.appid" placeholder="APP ID"></a-input>
|
||||
<a-input
|
||||
class="ml10"
|
||||
v-model:value="agoraPara.token"
|
||||
placeholder="Token"
|
||||
></a-input>
|
||||
<a-input
|
||||
class="ml10"
|
||||
v-model:value="agoraPara.channel"
|
||||
placeholder="Channel"
|
||||
></a-input>
|
||||
</div>
|
||||
<div class="mt20">
|
||||
<p class="fz20">
|
||||
Livestate:{{ livePara.liveState == false ? 'Stop' : 'Playing' }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt10 flex-row flex-justify-center flex-align-center">
|
||||
<a-button type="primary" large @click="onStart">Play</a-button>
|
||||
<a-button class="ml20" type="primary" large @click="onStop"
|
||||
>Stop</a-button
|
||||
>
|
||||
<a-button class="ml20" type="primary" large @click="onRefresh"
|
||||
>Refresh Live Capacity</a-button
|
||||
>
|
||||
</div>
|
||||
<div id="player"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import AgoraRTC from 'agora-rtc-sdk-ng'
|
||||
import { onMounted, reactive } from 'vue'
|
||||
import { CURRENT_CONFIG as config } from '/@/api/http/config'
|
||||
import { getLiveCapacity, startLivestream, stopLivestream } from '/@/api/manage'
|
||||
import { getRoot } from '/@/root'
|
||||
|
||||
const root = getRoot()
|
||||
|
||||
const clarityList = [
|
||||
{
|
||||
value: 0,
|
||||
label: 'Adaptive'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: 'Smooth'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: 'Standard'
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: 'HD'
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: 'Super Clear'
|
||||
}
|
||||
]
|
||||
|
||||
let agoraClient = {} as any
|
||||
const agoraPara = reactive({
|
||||
appid: config.agoraAPPID,
|
||||
token: config.agoraToken,
|
||||
channel: config.agoraChannel,
|
||||
uid: null,
|
||||
stream: {}
|
||||
})
|
||||
const dronePara = reactive({
|
||||
livestreamSource: [],
|
||||
droneList: [],
|
||||
cameraList: [],
|
||||
videoList: [],
|
||||
droneSelected: '',
|
||||
cameraSelected: '',
|
||||
videoSelected: '',
|
||||
claritySelected: ''
|
||||
})
|
||||
const livePara = reactive({
|
||||
url: '',
|
||||
webrtc: {} as any,
|
||||
videoId: '',
|
||||
liveState: false
|
||||
})
|
||||
|
||||
const onRefresh = async () => {
|
||||
await getLiveCapacity({})
|
||||
.then(res => {
|
||||
console.log(res)
|
||||
if (res.code === 0) {
|
||||
if (res.data === null) {
|
||||
console.warn('warning: get live capacity is null!!!')
|
||||
return
|
||||
}
|
||||
dronePara.livestreamSource = res.data
|
||||
dronePara.droneList = []
|
||||
|
||||
console.log('live_capacity:', dronePara.livestreamSource)
|
||||
|
||||
if (dronePara.livestreamSource) {
|
||||
dronePara.livestreamSource.forEach((ele: any) => {
|
||||
dronePara.droneList.push({ label: ele.sn, value: ele.sn })
|
||||
})
|
||||
console.log(dronePara.droneList)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onRefresh()
|
||||
agoraClient = AgoraRTC.createClient({ mode: 'live', codec: 'h264' })
|
||||
agoraClient.setClientRole('audience')
|
||||
|
||||
// Subscribe when a remote user publishes a stream
|
||||
agoraClient.on('user-published', async (user: any, mediaType: string) => {
|
||||
await agoraClient.subscribe(user, mediaType)
|
||||
if (mediaType === 'video') {
|
||||
console.log('subscribe success')
|
||||
// Get `RemoteVideoTrack` in the `user` object.
|
||||
const remoteVideoTrack = user.videoTrack
|
||||
// Dynamically create a container in the form of a DIV element for playing the remote video track.
|
||||
const remotePlayerContainer: any = document.getElementById('player')
|
||||
// Specify the ID of the DIV container. You can use the `uid` of the remote user.
|
||||
remotePlayerContainer.id = agoraPara.uid
|
||||
remotePlayerContainer.textContent = 'uid: ' + agoraPara.uid
|
||||
remotePlayerContainer.style.width = '640px'
|
||||
remotePlayerContainer.style.height = '480px'
|
||||
remoteVideoTrack.play(remotePlayerContainer)
|
||||
}
|
||||
})
|
||||
agoraClient.on('user-unpublished', async (user: any) => {
|
||||
console.log('unpublish live:', user)
|
||||
await agoraClient.leave()
|
||||
})
|
||||
})
|
||||
|
||||
const handleError = (err: any) => {
|
||||
console.error(err)
|
||||
}
|
||||
const handleJoinChannel = (uid: any) => {
|
||||
agoraPara.uid = uid
|
||||
}
|
||||
|
||||
const onStart = async () => {
|
||||
const that = this
|
||||
console.log(
|
||||
'drone parameter:',
|
||||
dronePara.droneSelected,
|
||||
dronePara.cameraSelected,
|
||||
dronePara.videoSelected,
|
||||
dronePara.claritySelected
|
||||
)
|
||||
const timestamp = new Date().getTime().toString()
|
||||
const liveTimestamp = timestamp
|
||||
if (
|
||||
dronePara.droneSelected == null ||
|
||||
dronePara.cameraSelected == null ||
|
||||
dronePara.videoSelected == null ||
|
||||
dronePara.claritySelected == null
|
||||
) {
|
||||
console.warn('waring: not select live para!!!')
|
||||
}
|
||||
livePara.videoId =
|
||||
dronePara.droneSelected +
|
||||
'/' +
|
||||
dronePara.cameraSelected +
|
||||
'/' +
|
||||
dronePara.videoSelected
|
||||
console.log(agoraPara)
|
||||
await agoraClient
|
||||
.join(agoraPara.appid, agoraPara.channel, agoraPara.token, null)
|
||||
.then((res: any) => {
|
||||
console.log('agora uid:', res)
|
||||
agoraPara.uid = res
|
||||
})
|
||||
console.log(agoraPara.token)
|
||||
agoraPara.token = encodeURIComponent(agoraPara.token)
|
||||
console.log('agoraToken:', agoraPara.token)
|
||||
livePara.url =
|
||||
'channel=' +
|
||||
agoraPara.channel +
|
||||
'&sn=' +
|
||||
dronePara.droneSelected +
|
||||
'&token=' +
|
||||
agoraPara.token +
|
||||
'&uid=' +
|
||||
agoraPara.uid
|
||||
|
||||
await startLivestream({
|
||||
url: livePara.url,
|
||||
video_id: livePara.videoId,
|
||||
url_type: 0,
|
||||
video_quality: dronePara.claritySelected
|
||||
})
|
||||
.then(res => {
|
||||
livePara.liveState = true
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
const onStop = async () => {
|
||||
stopLivestream({
|
||||
video_id: livePara.videoId
|
||||
}).then(res => {
|
||||
livePara.liveState = false
|
||||
console.log('stop play livestream')
|
||||
})
|
||||
}
|
||||
const onDroneSelect = (val: any) => {
|
||||
dronePara.droneSelected = val
|
||||
if (dronePara.droneSelected) {
|
||||
const droneTemp = dronePara.livestreamSource
|
||||
droneTemp.forEach(ele => {
|
||||
const drone = ele
|
||||
if (drone.sn === dronePara.droneSelected) {
|
||||
const cameraListTemp = drone.cameras_list
|
||||
dronePara.cameraList = []
|
||||
cameraListTemp.forEach((ele: any) => {
|
||||
dronePara.cameraList.push({ label: ele.name, value: ele.index })
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const onCameraSelect = (val: any) => {
|
||||
dronePara.cameraSelected = val
|
||||
|
||||
if (dronePara.cameraSelected) {
|
||||
const droneTemp = dronePara.livestreamSource
|
||||
droneTemp.forEach(ele => {
|
||||
const drone = ele
|
||||
if (drone.sn === dronePara.droneSelected) {
|
||||
const cameraListTemp = drone.cameras_list
|
||||
cameraListTemp.forEach((ele: any) => {
|
||||
const camera = ele
|
||||
if (camera.index === dronePara.cameraSelected) {
|
||||
const videoListTemp = camera.videos_list
|
||||
dronePara.videoList = []
|
||||
videoListTemp.forEach((ele: any) => {
|
||||
dronePara.videoList.push({ label: ele.type, value: ele.index })
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const onVideoSelect = (val: any) => {
|
||||
dronePara.videoSelected = val
|
||||
}
|
||||
const onClaritySelect = (val: any) => {
|
||||
dronePara.claritySelected = val
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '/@/styles/index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,351 @@
|
||||
<template>
|
||||
<div class="flex-column flex-justify-start flex-align-center">
|
||||
<video
|
||||
:style="{ width: '720px', height: '480px' }"
|
||||
id="video-webrtc"
|
||||
ref="videowebrtc"
|
||||
controls
|
||||
class="mt20"
|
||||
></video>
|
||||
<p class="fz24">Live streaming source selection</p>
|
||||
<div class="flex-row flex-justify-center flex-align-center mt10">
|
||||
<a-select
|
||||
style="width: 150px"
|
||||
placeholder="Select Live Type"
|
||||
@select="onLiveTypeSelect"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in liveTypeList"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<a-select
|
||||
class="ml10"
|
||||
style="width:150px"
|
||||
placeholder="Select Drone"
|
||||
@select="onDroneSelect"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in droneList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-select-option
|
||||
>
|
||||
</a-select>
|
||||
<a-select
|
||||
class="ml10"
|
||||
style="width:150px"
|
||||
placeholder="Select Camera"
|
||||
@select="onCameraSelect"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in cameraList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-select-option
|
||||
>
|
||||
</a-select>
|
||||
<a-select
|
||||
class="ml10"
|
||||
style="width:150px"
|
||||
placeholder="Select Lens"
|
||||
@select="onVideoSelect"
|
||||
>
|
||||
<a-select-option
|
||||
class="ml10"
|
||||
v-for="item in videoList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-select-option
|
||||
>
|
||||
</a-select>
|
||||
<a-select
|
||||
class="ml10"
|
||||
style="width:150px"
|
||||
placeholder="Select Clarity"
|
||||
@select="onClaritySelect"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in clarityList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>{{ item.label }}</a-select-option
|
||||
>
|
||||
</a-select>
|
||||
</div>
|
||||
<div class="mt20">
|
||||
<p class="fz20">Livestate:{{ liveState == 0 ? 'Stop' : 'Playing' }}</p>
|
||||
<p class="fz10" v-if="livetypeSelected == 2">
|
||||
Please use VLC media player to play the RTSP livestream !!!
|
||||
</p>
|
||||
<p class="fz10" v-if="livetypeSelected == 2">
|
||||
RTSP Parameter:{{ rtspData }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt10 flex-row flex-justify-center flex-align-center">
|
||||
<a-button type="primary" large @click="onStart">Play</a-button>
|
||||
<a-button class="ml20" type="primary" large @click="onStop"
|
||||
>Stop</a-button
|
||||
>
|
||||
<a-button class="ml20" type="primary" large @click="onRefresh"
|
||||
>Refresh Live Capacity</a-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { message } from 'ant-design-vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { CURRENT_CONFIG as config } from '/@/api/http/config'
|
||||
import { getLiveCapacity, startLivestream, stopLivestream } from '/@/api/manage'
|
||||
import { getRoot } from '/@/root'
|
||||
import jswebrtc from '/@/vendors/jswebrtc.min.js'
|
||||
const root = getRoot()
|
||||
|
||||
const liveTypeList = [
|
||||
{
|
||||
value: 1,
|
||||
label: 'RTMP'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: 'RTSP'
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: 'GB28181'
|
||||
}
|
||||
]
|
||||
const clarityList = [
|
||||
{
|
||||
value: 0,
|
||||
label: 'Adaptive'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: 'Smooth'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: 'Standard'
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: 'HD'
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: 'Super Clear'
|
||||
}
|
||||
]
|
||||
|
||||
const videowebrtc = ref(null)
|
||||
const livestreamSource = ref()
|
||||
const droneList = ref()
|
||||
const cameraList = ref()
|
||||
const videoList = ref()
|
||||
const droneSelected = ref()
|
||||
const cameraSelected = ref()
|
||||
const videoSeleted = ref()
|
||||
const claritySeleted = ref()
|
||||
const videoId = ref()
|
||||
const liveState = ref(0)
|
||||
const livetypeSelected = ref()
|
||||
const rtspData = ref()
|
||||
|
||||
const onRefresh = async () => {
|
||||
await getLiveCapacity({})
|
||||
.then(res => {
|
||||
console.log(res)
|
||||
if (res.code === 0) {
|
||||
if (res.data === null) {
|
||||
console.warn('warning: get live capacity is null!!!')
|
||||
return
|
||||
}
|
||||
const resData: Array<[]> = res.data
|
||||
console.log('live_capacity:', resData)
|
||||
livestreamSource.value = resData
|
||||
console.log(livestreamSource)
|
||||
|
||||
const temp: Array<{}> = []
|
||||
if (livestreamSource.value) {
|
||||
livestreamSource.value.forEach(ele => {
|
||||
temp.push({ label: ele.sn, value: ele.sn })
|
||||
})
|
||||
console.log(temp)
|
||||
droneList.value = temp
|
||||
console.log(droneList.value)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onRefresh()
|
||||
})
|
||||
const onStart = async () => {
|
||||
const that = this
|
||||
console.log(
|
||||
'直播参数:',
|
||||
livetypeSelected.value,
|
||||
droneSelected.value,
|
||||
cameraSelected.value,
|
||||
videoSeleted.value,
|
||||
claritySeleted.value
|
||||
)
|
||||
const timestamp = new Date().getTime().toString()
|
||||
const liveTimestamp = timestamp
|
||||
if (
|
||||
livetypeSelected.value == null ||
|
||||
droneSelected.value == null ||
|
||||
cameraSelected.value == null ||
|
||||
videoSeleted.value == null ||
|
||||
claritySeleted.value == null
|
||||
) {
|
||||
console.warn('waring: not select live para!!!')
|
||||
return
|
||||
}
|
||||
videoId.value =
|
||||
droneSelected.value + '/' + cameraSelected.value + '/' + videoSeleted.value
|
||||
let liveURL = ''
|
||||
switch (livetypeSelected.value) {
|
||||
case 1: {
|
||||
// RTMP
|
||||
liveURL = config.rtmpURL + timestamp
|
||||
break
|
||||
}
|
||||
case 2: {
|
||||
// RTSP
|
||||
liveURL = config.rtspPara
|
||||
break
|
||||
}
|
||||
case 3: {
|
||||
// GB28181
|
||||
liveURL = config.gb28181Para
|
||||
break
|
||||
}
|
||||
default:
|
||||
console.warn('warning: live type is not correct!!!')
|
||||
break
|
||||
}
|
||||
await startLivestream({
|
||||
url: liveURL,
|
||||
video_id: videoId.value,
|
||||
url_type: livetypeSelected.value,
|
||||
video_quality: claritySeleted.value
|
||||
})
|
||||
.then(res => {
|
||||
if (livetypeSelected.value === 3) {
|
||||
const url = res.data.url
|
||||
const videoElement = videowebrtc.value
|
||||
// gb28181,it will fail if not wait.
|
||||
message.loading({
|
||||
content: '直播等待中。。。',
|
||||
duration: 4,
|
||||
onClose () {
|
||||
const player = new jswebrtc.Player(url, {
|
||||
video: videoElement,
|
||||
autoplay: true,
|
||||
onPlay: obj => {
|
||||
console.log('start play livestream')
|
||||
}
|
||||
})
|
||||
liveState.value = 1
|
||||
}
|
||||
})
|
||||
} else if (livetypeSelected.value === 2) {
|
||||
console.log(res)
|
||||
rtspData.value =
|
||||
'url:' +
|
||||
res.data.url +
|
||||
'&username:' +
|
||||
res.data.username +
|
||||
'&password:' +
|
||||
res.data.password
|
||||
} else if (livetypeSelected.value === 1) {
|
||||
const url = res.data.url
|
||||
const videoElement = videowebrtc.value
|
||||
console.log('start live:', url)
|
||||
const player = new jswebrtc.Player(url, {
|
||||
video: videoElement,
|
||||
autoplay: true,
|
||||
onPlay: obj => {
|
||||
console.log('start play livestream')
|
||||
liveState.value = 1
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
const onStop = () => {
|
||||
stopLivestream({
|
||||
video_id: videoId.value
|
||||
}).then(res => {
|
||||
liveState.value = 0
|
||||
console.log('stop play livestream')
|
||||
})
|
||||
}
|
||||
const onLiveTypeSelect = (val: any) => {
|
||||
livetypeSelected.value = val
|
||||
}
|
||||
const onDroneSelect = (val: any) => {
|
||||
droneSelected.value = val
|
||||
const temp: Array<{}> = []
|
||||
if (droneSelected.value) {
|
||||
const droneTemp = livestreamSource.value
|
||||
droneTemp.forEach(ele => {
|
||||
const drone = ele
|
||||
if (drone.sn === droneSelected.value) {
|
||||
const cameraListTemp = drone.cameras_list
|
||||
cameraListTemp.forEach(ele => {
|
||||
temp.push({ label: ele.name, value: ele.index })
|
||||
})
|
||||
cameraList.value = temp
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const onCameraSelect = (val: any) => {
|
||||
cameraSelected.value = val
|
||||
const result: Array<{}> = []
|
||||
if (cameraSelected.value) {
|
||||
const droneTemp = livestreamSource.value
|
||||
droneTemp.forEach(ele => {
|
||||
const drone = ele
|
||||
if (drone.sn === droneSelected.value) {
|
||||
const cameraListTemp = drone.cameras_list
|
||||
cameraListTemp.forEach(ele => {
|
||||
const camera = ele
|
||||
if (camera.index === cameraSelected.value) {
|
||||
const videoListTemp = camera.videos_list
|
||||
videoListTemp.forEach(ele => {
|
||||
result.push({ label: ele.type, value: ele.index })
|
||||
})
|
||||
videoList.value = result
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const onVideoSelect = (val: any) => {
|
||||
videoSeleted.value = val
|
||||
}
|
||||
const onClaritySelect = (val: any) => {
|
||||
claritySeleted.value = val
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '/@/styles/index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="flex-column flex-justify-start flex-align-center">
|
||||
<a-button
|
||||
class="mt10 "
|
||||
style="width:90%"
|
||||
type="primary"
|
||||
@click="onAgoraLiveStream"
|
||||
>Agora Live</a-button
|
||||
>
|
||||
<a-button
|
||||
class="mt10"
|
||||
style="width:90%"
|
||||
type="primary"
|
||||
@click="onOthersLive"
|
||||
>RTMP/GB28181 Live</a-button
|
||||
>
|
||||
</div>
|
||||
<div v-if="enableAgoraLive">
|
||||
<a-modal
|
||||
style="top:0"
|
||||
v-model:visible="enableAgoraLive"
|
||||
title="Agora Live"
|
||||
width="100%"
|
||||
:maskClosable="false"
|
||||
wrapClassName="full-modal"
|
||||
:footer="null"
|
||||
>
|
||||
<LiveAgora />
|
||||
</a-modal>
|
||||
</div>
|
||||
<div v-if="enableOthersLive">
|
||||
<a-modal
|
||||
style="top:0"
|
||||
v-model:visible="enableOthersLive"
|
||||
title="RTMP/GB28181/RTSP Live"
|
||||
width="100%"
|
||||
:maskClosable="false"
|
||||
wrapClassName="full-modal"
|
||||
:footer="null"
|
||||
>
|
||||
<LiveOthers />
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import LiveAgora from './livestream-agora.vue'
|
||||
import LiveOthers from './livestream-others.vue'
|
||||
import { getRoot } from '/@/root'
|
||||
const root = getRoot()
|
||||
|
||||
const enableAgoraLive = ref(false)
|
||||
const enableOthersLive = ref(false)
|
||||
const onAgoraLiveStream = () => {
|
||||
console.log('agora')
|
||||
enableAgoraLive.value = true
|
||||
}
|
||||
const onOthersLive = () => {
|
||||
console.log('liveview')
|
||||
enableOthersLive.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.full-modal {
|
||||
.ant-modal {
|
||||
max-width: 100%;
|
||||
top: 0;
|
||||
padding-bottom: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.ant-modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh);
|
||||
}
|
||||
.ant-modal-body {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div class="project-media-wrapper">
|
||||
Media
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div class="project-tsa-wrapper">
|
||||
TSA
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div class="project-wayline-wrapper">
|
||||
wayline
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<div class="demo-project-sidebar-wrapper">
|
||||
<router-link
|
||||
v-for="item in options"
|
||||
:key="item.key"
|
||||
:to="item.path"
|
||||
:class="{
|
||||
'menu-item': true,
|
||||
selected: selectedRoute(item),
|
||||
disabled: item.key > 6
|
||||
}"
|
||||
>
|
||||
<a-tooltip :title="item.label" placement="right">
|
||||
<span>{{ item.label }}</span>
|
||||
</a-tooltip>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { getRoot } from '/@/root'
|
||||
interface IOptions {
|
||||
key: number
|
||||
label: string
|
||||
path:
|
||||
| string
|
||||
| {
|
||||
path: string
|
||||
query?: any
|
||||
}
|
||||
icon: string
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Sidebar',
|
||||
setup () {
|
||||
const root = getRoot()
|
||||
const options = [
|
||||
{ key: 0, label: 'livestream', path: '/livestream', icon: 'livestream' },
|
||||
{ key: 1, label: 'tsa', path: '/tsa', icon: 'tsa' },
|
||||
{ key: 2, label: 'layer', path: '/layer', icon: 'layer' },
|
||||
{ key: 3, label: 'media', path: '/media', icon: 'media' },
|
||||
{ key: 4, label: 'wayline', path: '/wayline', icon: 'wayline' }
|
||||
]
|
||||
|
||||
function selectedRoute (item: IOptions) {
|
||||
const path = typeof item.path === 'string' ? item.path : item.path.path
|
||||
return root.$route.path?.indexOf(path) === 0
|
||||
}
|
||||
|
||||
return {
|
||||
options,
|
||||
selectedRoute
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.demo-project-sidebar-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 80px;
|
||||
border-right: 1px solid #4f4f4f;
|
||||
color: $text-white-basic;
|
||||
// flex: 1;
|
||||
overflow: hidden;
|
||||
.menu-item {
|
||||
width: 100%;
|
||||
padding: 16px 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: $text-white-basic;
|
||||
cursor: pointer;
|
||||
&.selected {
|
||||
background-color: $dark-highlight;
|
||||
color: $primary;
|
||||
}
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.45;
|
||||
}
|
||||
}
|
||||
.filling {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.setting-icon {
|
||||
font-size: 24px;
|
||||
margin-bottom: 24px;
|
||||
color: $text-white-basic;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.ant-tooltip-open {
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
import { readFileSync, readdirSync } from 'fs'
|
||||
|
||||
let idPerfix = ''
|
||||
const svgTitle = /<svg([^>+].*?)>/
|
||||
const clearHeightWidth = /(width|height)="([^>+].*?)"/g
|
||||
const hasViewBox = /(viewBox="[^>+].*?")/g
|
||||
const clearReturn = /(\r)|(\n)/g
|
||||
|
||||
// Find the svg file
|
||||
function svgFind(e) {
|
||||
const arr = []
|
||||
const dirents = readdirSync(e, { withFileTypes: true })
|
||||
for (const dirent of dirents) {
|
||||
if (dirent.isDirectory()) arr.push(...svgFind(e + dirent.name + '/'))
|
||||
else {
|
||||
const svg = readFileSync(e + dirent.name)
|
||||
.toString()
|
||||
.replace(clearReturn, '')
|
||||
.replace(svgTitle, ($1, $2) => {
|
||||
let width = 0
|
||||
let height = 0
|
||||
let content = $2.replace(clearHeightWidth, (s1, s2, s3) => {
|
||||
if (s2 === 'width') width = s3
|
||||
else if (s2 === 'height') height = s3
|
||||
return ''
|
||||
})
|
||||
if (!hasViewBox.test($2)) content += `viewBox="0 0 ${width} ${height}"`
|
||||
return `<symbol id="${idPerfix}-${dirent.name.replace('.svg', '')}" ${content}>`
|
||||
}).replace('</svg>', '</symbol>')
|
||||
arr.push(svg)
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
export const svgBuilder = (path: any, perfix = 'icon') => {
|
||||
if (path === '') return
|
||||
idPerfix = perfix
|
||||
const res = svgFind(path)
|
||||
console.log(res)
|
||||
return {
|
||||
name: 'svg-transform',
|
||||
transformIndexHtml (dom: String) {
|
||||
return dom.replace(
|
||||
'<body>',
|
||||
`<body><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0" version="1.1">${res.join('')}</svg>`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { createApp, ComponentCustomProperties, App as VueApp } from 'vue'
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
$aMap: any
|
||||
$aMapObj: any
|
||||
$mouseTool: any
|
||||
}
|
||||
}
|
||||
let root: ComponentCustomProperties
|
||||
let app = null as any
|
||||
|
||||
export function createInstance (App: any): VueApp {
|
||||
app = createApp(App)
|
||||
root = app.config.globalProperties as ComponentCustomProperties
|
||||
return app
|
||||
}
|
||||
|
||||
export function getRoot (): ComponentCustomProperties {
|
||||
return root
|
||||
}
|
||||
|
||||
export function getApp (): VueApp {
|
||||
return app
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
|
||||
import { ERouterName } from '/@/types/index'
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/' + ERouterName.Project,
|
||||
name: ERouterName.Project,
|
||||
// redirect: {
|
||||
// name: ERouterName.Project
|
||||
// },
|
||||
component: () => import('/@/pages/project-app/index.vue'),
|
||||
children: [
|
||||
{
|
||||
path: '/' + ERouterName.Livestream,
|
||||
component: () => import('/@/pages/project-app/projects/livestream.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.Tsa,
|
||||
component: () => import('/@/pages/project-app/projects/tsa.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.Layer,
|
||||
name: ERouterName.Layer,
|
||||
component: () => import('/@/pages/project-app/projects/layer.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.Media,
|
||||
name: ERouterName.Media,
|
||||
component: () => import('/@/pages/project-app/projects/media.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.Wayline,
|
||||
name: ERouterName.Wayline,
|
||||
component: () => import('/@/pages/project-app/projects/wayline.vue')
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.Pilot,
|
||||
name: ERouterName.Pilot,
|
||||
component: () => import('/@/pages/page-pilot/pilot-index.vue'),
|
||||
children: [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.PilotHome,
|
||||
component: () => import('/@/pages/page-pilot/pilot-home.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.PilotMedia,
|
||||
component: () => import('/@/pages/page-pilot/pilot-media.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.PilotLiveshare,
|
||||
component: () => import('/@/pages/page-pilot/pilot-liveshare.vue')
|
||||
},
|
||||
{
|
||||
path: '/' + ERouterName.Element,
|
||||
name: ERouterName.Element,
|
||||
component: () => import('/@/pages/elements/elements.vue')
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -0,0 +1,5 @@
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import { InjectionKey } from 'vue'
|
||||
import { ActionTree, createStore, GetterTree, MutationTree, Store, StoreOptions, useStore } from 'vuex'
|
||||
import { getLayers } from '/@/api/layer'
|
||||
import { LayerType } from '/@/types/mapLayer'
|
||||
const initStateFunc = () => ({
|
||||
Layers: [
|
||||
{
|
||||
name: 'default',
|
||||
id: '',
|
||||
is_distributed: true,
|
||||
elements: [],
|
||||
is_check: false,
|
||||
is_select: false,
|
||||
type: 1
|
||||
},
|
||||
{
|
||||
name: 'share',
|
||||
id: '',
|
||||
is_distributed: true,
|
||||
elements: [],
|
||||
is_check: false,
|
||||
is_select: false,
|
||||
type: 2
|
||||
}
|
||||
],
|
||||
GatewayInfo: { // remote controller, dock
|
||||
|
||||
},
|
||||
DeviceInfo: { // drone
|
||||
|
||||
},
|
||||
layerBaseInfo: {} as {
|
||||
[key:string]:string
|
||||
},
|
||||
drawVisible: false,
|
||||
coverList: [
|
||||
|
||||
] as any,
|
||||
wsEvent: {
|
||||
mapElementCreat: {},
|
||||
mapElementUpdate: {},
|
||||
mapElementDelete: {}
|
||||
}
|
||||
})
|
||||
|
||||
export type RootStateType = ReturnType<typeof initStateFunc>
|
||||
|
||||
const getters: GetterTree<RootStateType, RootStateType> = {
|
||||
}
|
||||
const mutations: MutationTree<RootStateType> = {
|
||||
SET_LAYER_INFO (state, info) {
|
||||
state.Layers = info
|
||||
},
|
||||
SET_DEVICE_INFO (state, info) {
|
||||
state.DeviceInfo = info
|
||||
// console.log(state.DeviceInfo)
|
||||
},
|
||||
SET_GATEWAY_INFO (state, info) {
|
||||
state.GatewayInfo = info
|
||||
// console.log(state.GatewayInfo)
|
||||
},
|
||||
SET_DRAW_VISIBLE_INFO (state, bool) {
|
||||
state.drawVisible = bool
|
||||
},
|
||||
SET_MAP_ELEMENT_CREATE (state, info) {
|
||||
state.wsEvent.mapElementCreat = info
|
||||
},
|
||||
SET_MAP_ELEMENT_UPDATE (state, info) {
|
||||
state.wsEvent.mapElementUpdate = info
|
||||
},
|
||||
SET_MAP_ELEMENT_DELETE (state, info) {
|
||||
state.wsEvent.mapElementDelete = info
|
||||
},
|
||||
}
|
||||
|
||||
const actions: ActionTree<RootStateType, RootStateType> = {
|
||||
async getAllElement ({ commit }) {
|
||||
const result = await getLayers({
|
||||
groupId: '',
|
||||
isDistributed: true
|
||||
})
|
||||
commit('SET_LAYER_INFO', result.data?.list)
|
||||
console.log(result)
|
||||
},
|
||||
updateElement ({ state }, content: {type: 'is_check' | 'is_select', id: string, bool:boolean}) {
|
||||
const key = content.id.replaceAll('resource__', '')
|
||||
const type = content.type
|
||||
const layers = state.Layers
|
||||
const layer = layers.find(item => item.id === key)
|
||||
if (layer) {
|
||||
layer[type] = content.bool
|
||||
}
|
||||
},
|
||||
setLayerInfo ({ state }, layers) {
|
||||
// const layers = state.Layers
|
||||
const obj:{
|
||||
[key:string]:string
|
||||
} = {}
|
||||
layers.forEach(layer => {
|
||||
if (layer.type === LayerType.Default) {
|
||||
obj.default = layer.id
|
||||
} else {
|
||||
if (layer.type === LayerType.Share) {
|
||||
obj.share = layer.id
|
||||
}
|
||||
}
|
||||
})
|
||||
state.layerBaseInfo = obj
|
||||
console.log('state.layerBaseInfo', state.layerBaseInfo)
|
||||
},
|
||||
getLayerInfo ({ state }, id:string) {
|
||||
return state.layerBaseInfo[id]
|
||||
}
|
||||
}
|
||||
|
||||
const storeOptions: StoreOptions<RootStateType> = {
|
||||
state: initStateFunc,
|
||||
getters,
|
||||
mutations,
|
||||
actions
|
||||
}
|
||||
|
||||
const rootStore = createStore(storeOptions)
|
||||
|
||||
export default rootStore
|
||||
|
||||
export const storeKey: InjectionKey<Store<RootStateType>> = Symbol('')
|
||||
|
||||
type AllStateStoreTypes = RootStateType & {
|
||||
// moduleName: moduleType
|
||||
}
|
||||
|
||||
export function useMyStore<T = AllStateStoreTypes> () {
|
||||
return useStore<T>(storeKey)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
html, body, #app, #my-app {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f7f9fa;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
// Prevent font enlargement in horizontal screen
|
||||
text-size-adjust: 100%;
|
||||
|
||||
font-family: Roboto, sans-serif-medium, Arial, sans-serif;
|
||||
color: $main-text-color;
|
||||
font-size: 14px;
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
.flex-display {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-column {
|
||||
@extend .flex-display;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
@extend .flex-display;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flex-align-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.flex-align-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
.flex-align-baseline {
|
||||
align-items: baseline;
|
||||
}
|
||||
.flex-align-stretch {
|
||||
align-items: stretch;
|
||||
}
|
||||
.flex-align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-justify-start {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.flex-justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.flex-justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
.flex-justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.flex-justify-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
//width
|
||||
.width-100vw {
|
||||
width: 100vw;
|
||||
}
|
||||
.width-100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
//height
|
||||
.height-100vh {
|
||||
height: 100vh;
|
||||
}
|
||||
.height-100 {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
//margin
|
||||
m-5 {
|
||||
margin: -5px !important;
|
||||
}
|
||||
.mt-5 {
|
||||
margin-top: -5px !important;
|
||||
}
|
||||
|
||||
.mt100 {
|
||||
margin-top: 100px !important;
|
||||
}
|
||||
|
||||
.mt110 {
|
||||
margin-top: 110px !important;
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: -5px !important;
|
||||
}
|
||||
.ml-5 {
|
||||
margin-left: -5px !important;
|
||||
}
|
||||
.mr-5 {
|
||||
margin-right: -5px !important;
|
||||
}
|
||||
.m0 {
|
||||
margin: 0px !important;
|
||||
}
|
||||
.mt0 {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
.mb0 {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
.ml0 {
|
||||
margin-left: 0px !important;
|
||||
}
|
||||
.mr0 {
|
||||
margin-right: 0px !important;
|
||||
}
|
||||
.m5 {
|
||||
margin: 5px !important;
|
||||
}
|
||||
.mt5 {
|
||||
margin-top: 5px !important;
|
||||
}
|
||||
.mb5 {
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
.ml5 {
|
||||
margin-left: 5px !important;
|
||||
}
|
||||
.mr5 {
|
||||
margin-right: 5px !important;
|
||||
}
|
||||
.m10 {
|
||||
margin: 10px !important;
|
||||
}
|
||||
.mt10 {
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
.mb10 {
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
.ml10 {
|
||||
margin-left: 10px !important;
|
||||
}
|
||||
.mr10 {
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
.m15 {
|
||||
margin: 15px !important;
|
||||
}
|
||||
.mt15 {
|
||||
margin-top: 15px !important;
|
||||
}
|
||||
.mb15 {
|
||||
margin-bottom: 15px !important;
|
||||
}
|
||||
.ml15 {
|
||||
margin-left: 15px !important;
|
||||
}
|
||||
.mr15 {
|
||||
margin-right: 15px !important;
|
||||
}
|
||||
.m20 {
|
||||
margin: 20px !important;
|
||||
}
|
||||
.mt20 {
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
.mb20 {
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
.ml20 {
|
||||
margin-left: 20px !important;
|
||||
}
|
||||
.mr20 {
|
||||
margin-right: 20px !important;
|
||||
}
|
||||
.m25 {
|
||||
margin: 25px !important;
|
||||
}
|
||||
.mt25 {
|
||||
margin-top: 25px !important;
|
||||
}
|
||||
.mb25 {
|
||||
margin-bottom: 25px !important;
|
||||
}
|
||||
.ml25 {
|
||||
margin-left: 25px !important;
|
||||
}
|
||||
.mr25 {
|
||||
margin-right: 25px !important;
|
||||
}
|
||||
.m30 {
|
||||
margin: 30px !important;
|
||||
}
|
||||
.mt30 {
|
||||
margin-top: 30px !important;
|
||||
}
|
||||
.mb30 {
|
||||
margin-bottom: 30px !important;
|
||||
}
|
||||
.ml30 {
|
||||
margin-left: 30px !important;
|
||||
}
|
||||
.ml40 {
|
||||
margin-left: 40px !important;
|
||||
}
|
||||
.mr30 {
|
||||
margin-right: 30px !important;
|
||||
}
|
||||
.m50 {
|
||||
margin: 50px !important;
|
||||
}
|
||||
.mt50 {
|
||||
margin-top: 50px !important;
|
||||
}
|
||||
.mb50 {
|
||||
margin-bottom: 50px !important;
|
||||
}
|
||||
.ml50 {
|
||||
margin-left: 50px !important;
|
||||
}
|
||||
.mr50 {
|
||||
margin-right: 50px !important;
|
||||
}
|
||||
// padding值
|
||||
.p0 {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.pt0 {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
.pr0 {
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
.pb0 {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
.pl0 {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
.p5 {
|
||||
padding: 5px;
|
||||
}
|
||||
.pt5 {
|
||||
padding-top: 5px;
|
||||
}
|
||||
.pr5 {
|
||||
padding-right: 5px;
|
||||
}
|
||||
.pb5 {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.pl5 {
|
||||
padding-left: 5px;
|
||||
}
|
||||
.p10 {
|
||||
padding: 10px;
|
||||
}
|
||||
.pt10 {
|
||||
padding-top: 10px;
|
||||
}
|
||||
.pr10 {
|
||||
padding-right: 10px;
|
||||
}
|
||||
.pb10 {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.pl10 {
|
||||
padding-left: 10px;
|
||||
}
|
||||
.p15 {
|
||||
padding: 15px;
|
||||
}
|
||||
.pt15 {
|
||||
padding-top: 15px;
|
||||
}
|
||||
.pr15 {
|
||||
padding-right: 15px;
|
||||
}
|
||||
.pb15 {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
.pl15 {
|
||||
padding-left: 15px;
|
||||
}
|
||||
.p20 {
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.pt20 {
|
||||
padding-top: 20px;
|
||||
}
|
||||
.pr20 {
|
||||
padding-right: 20px;
|
||||
}
|
||||
.pb20 {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.pl20 {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.p30 {
|
||||
padding: 30px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.pt30 {
|
||||
padding-top: 30px;
|
||||
}
|
||||
.pr30 {
|
||||
padding-right: 30px;
|
||||
}
|
||||
.pb30 {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
.pl30 {
|
||||
padding-left: 30px;
|
||||
}
|
||||
.pb50 {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
.pl50 {
|
||||
padding-left: 50px;
|
||||
}
|
||||
.pl120 {
|
||||
padding-left: 120px;
|
||||
}
|
||||
.pl150 {
|
||||
padding-left: 150px;
|
||||
}
|
||||
.pt50 {
|
||||
padding-top: 50px;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
$font-family-sans-serif: 'Open Sans', BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
|
||||
'Microsoft YaHei', SimSun, sans-serif;
|
||||
|
||||
$line-heights: (
|
||||
12: 20px,
|
||||
14: 22px,
|
||||
16: 24px,
|
||||
18: 26px
|
||||
);
|
||||
|
||||
// 用法: @include text(12)
|
||||
@mixin text($size) {
|
||||
font-size: #{$size}px;
|
||||
line-height: map-get($line-heights, $size);
|
||||
}
|
||||
|
||||
// 常规体
|
||||
@mixin text-regular {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
// 中粗体
|
||||
@mixin text-semibold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@mixin ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.fz10 {
|
||||
font-size: 10;
|
||||
}
|
||||
.fz12 {
|
||||
font-size: 12px;
|
||||
}
|
||||
.fz14 {
|
||||
font-size: 14px;
|
||||
}
|
||||
.fz16 {
|
||||
font-size: 16px;
|
||||
}
|
||||
.fz18 {
|
||||
font-size: 18px;
|
||||
}
|
||||
.fz20 {
|
||||
font-size: 20px;
|
||||
}
|
||||
.fz22 {
|
||||
font-size: 22px;
|
||||
}
|
||||
.fz24 {
|
||||
font-size: 24px;
|
||||
}
|
||||
.fz26 {
|
||||
font-size: 26px;
|
||||
}
|
||||
.fz28 {
|
||||
font-size: 28px;
|
||||
}
|
||||
.fz30 {
|
||||
font-size: 30px;
|
||||
}
|
||||
.fz32 {
|
||||
font-size: 32px;
|
||||
}
|
||||
.fz35 {
|
||||
font-size: 35px;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
@import './common.scss';
|
||||
@import 'flex.style.scss';
|
||||
@import 'fonts.scss';
|
||||
@@ -0,0 +1,164 @@
|
||||
html,
|
||||
body,
|
||||
div,
|
||||
span,
|
||||
applet,
|
||||
object,
|
||||
iframe,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
blockquote,
|
||||
pre,
|
||||
a,
|
||||
abbr,
|
||||
acronym,
|
||||
address,
|
||||
big,
|
||||
cite,
|
||||
code,
|
||||
del,
|
||||
dfn,
|
||||
em,
|
||||
img,
|
||||
ins,
|
||||
kbd,
|
||||
q,
|
||||
s,
|
||||
samp,
|
||||
small,
|
||||
strike,
|
||||
strong,
|
||||
sub,
|
||||
sup,
|
||||
tt,
|
||||
var,
|
||||
b,
|
||||
u,
|
||||
i,
|
||||
center,
|
||||
dl,
|
||||
dt,
|
||||
dd,
|
||||
ol,
|
||||
ul,
|
||||
li,
|
||||
fieldset,
|
||||
form,
|
||||
label,
|
||||
legend,
|
||||
table,
|
||||
caption,
|
||||
tbody,
|
||||
tfoot,
|
||||
thead,
|
||||
tr,
|
||||
th,
|
||||
td,
|
||||
article,
|
||||
aside,
|
||||
canvas,
|
||||
details,
|
||||
embed,
|
||||
figure,
|
||||
figcaption,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
nav,
|
||||
output,
|
||||
ruby,
|
||||
section,
|
||||
summary,
|
||||
time,
|
||||
mark,
|
||||
audio,
|
||||
video {
|
||||
margin : 0;
|
||||
padding : 0;
|
||||
border : 0;
|
||||
font : inherit;
|
||||
font-size : 100%;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
html {
|
||||
line-height : 1;
|
||||
// -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing : 0;
|
||||
}
|
||||
|
||||
caption,
|
||||
th,
|
||||
td {
|
||||
font-weight : normal;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
q,
|
||||
blockquote {
|
||||
quotes: none;
|
||||
}
|
||||
|
||||
q::before,
|
||||
q::after,
|
||||
blockquote::before,
|
||||
blockquote::after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
a {
|
||||
background : transparent;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button,
|
||||
input[type='number'],
|
||||
input[type='text'],
|
||||
input[type='password'],
|
||||
input[type='email'],
|
||||
input[type='search'],
|
||||
select,
|
||||
textarea {
|
||||
font-family : inherit;
|
||||
margin : 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
$main-text-color: #000;
|
||||
$header-height: 52px;
|
||||
// Auxiliary color
|
||||
$info: #1fa3f6;
|
||||
$success: #28d445;
|
||||
$danger: #e70102;
|
||||
$alarm: #ffcc00;
|
||||
$warning: #ffcc00;
|
||||
$error: #e70102;
|
||||
|
||||
// 品牌色
|
||||
$primary: #2d8cf0;
|
||||
$primary-click: #2b85e4;
|
||||
$primary-hover: #5cadff;
|
||||
$primary-hover-dropdown: rgba($primary-hover, 0.2);
|
||||
$primary-disabled: #274d75;
|
||||
// 辅助色拓展
|
||||
$danger-hover: #ff4d4e;
|
||||
$danger-active: #d40001;
|
||||
$menu-primary: #464c5b; // tab菜单主题色
|
||||
// 图标颜色
|
||||
$ic-white-normal: #fff;
|
||||
$ic-black-normal: #4e4e4e;
|
||||
$ic-hover: #a7a7a7;
|
||||
$ic-disabled: #5f5f5f;
|
||||
$ic-selected: #1088f2;
|
||||
// 中性色-黑
|
||||
$dark-bg-light: #868688; // 背景色浅色
|
||||
$dark-btn-hover: #5d5f61; // 按钮 hover 色
|
||||
$dark-border: #4f4f4f; // 边框色
|
||||
$dark-btn-disabled: #3c3c3c; // 按钮主色禁用
|
||||
$dark-border-secondary: #393939; // 第二边框色
|
||||
$dark-basic-primary: #232323; // 第一基础底色
|
||||
$dark-basic-secondary: #282828; // 第二基础底色
|
||||
$dark-highlight: #232323; // 高亮色
|
||||
$dark-disable: #444444; // 置灰底色
|
||||
$dark-project-disabled: #292929;
|
||||
// 中性色-白
|
||||
$light-bg-primary: #fff; // 1 背景
|
||||
$light-bg-secondary: #f7f9fa; // 2 背景
|
||||
$light-divider: #e8eaec; // 3 分割线
|
||||
$light-border: #dcdee2; // 4 边框
|
||||
$light-disabled: #c5c8ce; // 5 失效
|
||||
$light-auxiliary: #808695; // 6 辅助图标
|
||||
$light-main-text: #515a6e; // 7 正文 ?用处
|
||||
$light-title: #17233d; // 8 标题 ?用处
|
||||
$light-bg-menu: #3b3e40; // 9 菜单栏背景色 ?用处
|
||||
$light-border-secondary: #e8e8e8; // 第二边框色
|
||||
|
||||
// 字体
|
||||
// 白色
|
||||
$text-white-basic: #fff; // 基础色
|
||||
$text-white-main: rgba($text-white-basic, 1); // 正文
|
||||
$text-white-secondary: rgba($text-white-basic, 0.45); // 次级
|
||||
$text-white-disabled: rgba($text-white-basic, 0.25); // 置灰
|
||||
// 黑色
|
||||
$text-black-basic: #000000; // 基础色
|
||||
$text-black-emphasize: rgba($text-black-basic, 0.85); // 强调
|
||||
$text-black-main: rgba($text-black-basic, 0.65); // 正文
|
||||
$text-black-secondary: rgba($text-black-basic, 0.45); // 次要
|
||||
$text-black-disabled: rgba($text-black-basic, 0.25); // 置灰
|
||||
$text-link: $primary;
|
||||
$text-danger: $danger;
|
||||
// 标签
|
||||
$tag-green: #19be6b;
|
||||
// 滚动条等颜色
|
||||
$scroll-bar: #c5c8ce;
|
||||
$scroll-bar-dark: #5f5f5f;
|
||||
|
||||
// 选择框
|
||||
|
||||
$select-disabled: #d8d8d8;
|
||||
@@ -0,0 +1,19 @@
|
||||
export enum ERouterName {
|
||||
Element = 'element',
|
||||
Project = 'project',
|
||||
Tsa = 'tsa',
|
||||
Layer = 'layer',
|
||||
Media = 'media',
|
||||
Wayline = 'wayline',
|
||||
Livestream = 'livestream',
|
||||
Pilot = 'pilot-login',
|
||||
PilotHome = 'pilot-home',
|
||||
PilotMedia = 'pilot-media',
|
||||
PilotLiveshare = 'pilot-liveshare'
|
||||
}
|
||||
|
||||
export enum EStorageKey {
|
||||
LANG_CODE = 'DJI_CREATE_VITE_H5_APP:lang_code',
|
||||
TEST_TOOLS_POSITION_STORAGE_KEY = 'DJI_CREATE_VITE_H5_APP:test_tools_position',
|
||||
SESSION_ID = 'DJI_CREATE_VITE_H5_APP:sess'
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './enums'
|
||||
@@ -0,0 +1,6 @@
|
||||
export enum MapDoodleEnum {
|
||||
PIN = 'pin',
|
||||
POLYLINE = 'polyline',
|
||||
POLYGON = 'polygon',
|
||||
Close = 'off'
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
|
||||
export interface MapGeographicPosition {
|
||||
longitude: number;
|
||||
latitude: number;
|
||||
height?: number;
|
||||
}
|
||||
export enum LayerType {
|
||||
Normal,
|
||||
Default,
|
||||
Share
|
||||
}
|
||||
export interface pinAMapPosition {
|
||||
KL: number
|
||||
className: string
|
||||
kT: number
|
||||
lng: number
|
||||
lat: number
|
||||
}
|
||||
export enum ResourceStatus {
|
||||
NotShow,
|
||||
Show
|
||||
}
|
||||
export type GeojsonCoordinate = [number, number, number?]
|
||||
|
||||
export interface GeojsonLine {
|
||||
type: 'Feature'
|
||||
properties: {
|
||||
color: string
|
||||
directConnected?: boolean
|
||||
}
|
||||
geometry: {
|
||||
type: 'LineString'
|
||||
coordinates: GeojsonCoordinate[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface GeojsonPolygon {
|
||||
type: 'Feature'
|
||||
properties: {
|
||||
color: string
|
||||
}
|
||||
geometry: {
|
||||
type: 'Polygon'
|
||||
coordinates: GeojsonCoordinate[][]
|
||||
}
|
||||
}
|
||||
|
||||
export interface GeojsonPoint {
|
||||
type: 'Feature'
|
||||
properties: {
|
||||
color: string
|
||||
clampToGround?: boolean
|
||||
}
|
||||
geometry: {
|
||||
type: 'Point'
|
||||
coordinates: GeojsonCoordinate
|
||||
}
|
||||
}
|
||||
export type GeojsonFeature = GeojsonLine | GeojsonPolygon | GeojsonPoint
|
||||
|
||||
interface ResourceObjectBasic {
|
||||
user_name: string
|
||||
user_id?: string
|
||||
type:0| 1 | 2
|
||||
content: unknown
|
||||
}
|
||||
export interface PinResource extends ResourceObjectBasic {
|
||||
type: 0
|
||||
content: GeojsonFeature
|
||||
}
|
||||
|
||||
export type ResourceObject = PinResource
|
||||
export enum LayerElevationLoadStatus {
|
||||
Unload,
|
||||
Load
|
||||
}
|
||||
|
||||
export interface LayerResource {
|
||||
id: string
|
||||
name: string
|
||||
order: number
|
||||
status: ResourceStatus
|
||||
resource: ResourceObject | null
|
||||
display: number
|
||||
create_time: number
|
||||
elevation_load_status?: LayerElevationLoadStatus //
|
||||
}
|
||||
export interface Layer {
|
||||
id: string
|
||||
name: string
|
||||
order: number
|
||||
create_time: number
|
||||
type: LayerType
|
||||
is_distributed: boolean
|
||||
is_lock: boolean
|
||||
elements: null | LayerResource[],
|
||||
is_check?: boolean
|
||||
is_select?: boolean
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import { MapElementEnum } from '/@/constants/map'
|
||||
|
||||
export interface mapLayerStyle {
|
||||
background: string
|
||||
}
|
||||
|
||||
export interface mapLayerChildren {
|
||||
key: string
|
||||
style: mapLayerStyle
|
||||
title: string
|
||||
obj: any
|
||||
}
|
||||
export interface mapLayerChildrenObj {
|
||||
className: string
|
||||
key: string
|
||||
name: string
|
||||
type: string
|
||||
}
|
||||
|
||||
// 拖拽事件
|
||||
export interface DropEvent {
|
||||
node: {
|
||||
eventKey: string
|
||||
pos: string
|
||||
$parent: any
|
||||
}
|
||||
dragNode: {
|
||||
eventKey: string
|
||||
}
|
||||
dropPosition: number
|
||||
dropToGap: boolean
|
||||
}
|
||||
export interface mapLayer {
|
||||
key?: string
|
||||
title: string
|
||||
id: string
|
||||
name: string
|
||||
style: mapLayerStyle
|
||||
elements: any
|
||||
}
|
||||
export interface elementGroupsReq{
|
||||
groupId: string
|
||||
isDistributed: boolean
|
||||
}
|
||||
export interface PostElementsBody {
|
||||
id: string
|
||||
name: string
|
||||
resource: {
|
||||
type: MapElementEnum,
|
||||
user_name?: string,
|
||||
content: {
|
||||
type:string,
|
||||
properties:{
|
||||
color:string,
|
||||
clampToGround:boolean
|
||||
},
|
||||
geometry:{
|
||||
type:string,
|
||||
coordinates:unknown
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export interface Color {
|
||||
id: number
|
||||
color: string
|
||||
selected: boolean,
|
||||
name: string
|
||||
}
|
||||
|
||||
export enum GeoType {
|
||||
LineString = 'LineString',
|
||||
Polygon = 'Polygon',
|
||||
Point = 'Point'
|
||||
}
|
||||
export enum ResourceStatus {
|
||||
NotShow,
|
||||
Show
|
||||
}
|
||||
|
||||
export enum LayerElevationLoadStatus {
|
||||
Unload,
|
||||
Load
|
||||
}
|
||||
export interface PutElementsBody {
|
||||
name?: string
|
||||
status?: ResourceStatus
|
||||
content?: unknown
|
||||
display?: number
|
||||
elevation_load_status?: LayerElevationLoadStatus
|
||||
}
|
||||
export enum LayerType {
|
||||
Normal,
|
||||
Default,
|
||||
Share,
|
||||
Reconstruction
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { App, DefineComponent } from 'vue'
|
||||
|
||||
const components: Record<string, DefineComponent<{}, {}, any>> = {
|
||||
|
||||
}
|
||||
|
||||
export const CommonComponents = {
|
||||
install (app: App): void {
|
||||
Object.keys(components).forEach(name => {
|
||||
app.component(name, components[name])
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export function formatPhoneNum (phoneNum: string | number) {
|
||||
const str = String(phoneNum)
|
||||
return str.substring(0, 3) + '****' + str.slice(-4)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import {
|
||||
MapGeographicPosition,
|
||||
} from '/@/types/map'
|
||||
|
||||
export type GeojsonCoordinate = [number, number, number?]
|
||||
|
||||
export interface GeojsonLine {
|
||||
type: 'Feature'
|
||||
properties: {
|
||||
color: string
|
||||
directConnected?: boolean
|
||||
}
|
||||
geometry: {
|
||||
type: 'LineString'
|
||||
coordinates: GeojsonCoordinate[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface GeojsonPolygon {
|
||||
type: 'Feature'
|
||||
properties: {
|
||||
color: string
|
||||
}
|
||||
geometry: {
|
||||
type: 'Polygon'
|
||||
coordinates: GeojsonCoordinate[][]
|
||||
}
|
||||
}
|
||||
|
||||
export interface GeojsonPoint {
|
||||
type: 'Feature'
|
||||
properties: {
|
||||
color: string
|
||||
clampToGround?: boolean
|
||||
}
|
||||
geometry: {
|
||||
type: 'Point'
|
||||
coordinates: GeojsonCoordinate
|
||||
}
|
||||
}
|
||||
|
||||
export type GeojsonFeature = GeojsonLine | GeojsonPolygon | GeojsonPoint
|
||||
|
||||
export function geographic2Coordinate (position: MapGeographicPosition): GeojsonCoordinate {
|
||||
const coordinates: GeojsonCoordinate = [position.longitude, position.latitude]
|
||||
if (position.height !== undefined) coordinates.push(position.height)
|
||||
return coordinates
|
||||
}
|
||||
|
||||
export function generateLine (coordinates: MapGeographicPosition[], properties: GeojsonLine['properties']): GeojsonFeature {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties,
|
||||
geometry: {
|
||||
type: 'LineString',
|
||||
coordinates: coordinates.map(geographic2Coordinate),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function generatePolygon (coordinates: MapGeographicPosition[], properties: GeojsonPolygon['properties']): GeojsonFeature {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties,
|
||||
geometry: {
|
||||
type: 'Polygon',
|
||||
coordinates: [coordinates.map(geographic2Coordinate)],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function generatePoint (position: MapGeographicPosition, properties: GeojsonPoint['properties']): GeojsonFeature {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties,
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: geographic2Coordinate(position),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
const layerTreeTypes = ['layer', 'resource'] as const
|
||||
type LayerTreeType = (typeof layerTreeTypes)[number]
|
||||
const Spliter = '__'
|
||||
|
||||
export function getLayerTreeKey (type: LayerTreeType, id: number | string) {
|
||||
return `${type}${Spliter}${id}`
|
||||
}
|
||||
|
||||
export function isLayerTreeKey (key: string, type?: LayerTreeType) {
|
||||
if (type) {
|
||||
return key.startsWith(`${type}${Spliter}`)
|
||||
} else {
|
||||
return layerTreeTypes.some(t => key.startsWith(`${t}${Spliter}`))
|
||||
}
|
||||
}
|
||||
|
||||
export function getIdFromLayerTreeKey (key: string) {
|
||||
return key.split(Spliter)[1]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
/**
|
||||
* Used for log printing in a non-production environment
|
||||
* @param args
|
||||
*/
|
||||
export function consoleLog (...args: Parameters<typeof console.log>) {
|
||||
if (import.meta.env.VITE_APP_ENVIRONMENT !== 'PROD') {
|
||||
window.console.log.apply(null, args) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
export function consoleWarn (...args: Parameters<typeof console.warn>) {
|
||||
if (import.meta.env.VITE_APP_ENVIRONMENT !== 'PROD') {
|
||||
console.warn.apply(null, args) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
export function consoleError (...args: Parameters<typeof console.error>) {
|
||||
if (import.meta.env.VITE_APP_ENVIRONMENT !== 'PROD') {
|
||||
console.error.apply(null, args) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
export function testEnvLog (...args: Parameters<typeof console.log>) {
|
||||
if (import.meta.env.VITE_APP_ENVIRONMENT !== 'PROD') {
|
||||
console.log.apply(null, args) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { pinAMapPosition, MapGeographicPosition, Layer, LayerType, LayerElevationLoadStatus } from '/@/types/map'
|
||||
import { generatePoint, generateLine, generatePolygon } from '/@/utils/genjson'
|
||||
import { MapDoodleColor, MapElementEnum } from '/@/constants/map'
|
||||
function getPinPosition (pinAMapPosition: pinAMapPosition):MapGeographicPosition {
|
||||
return { height: 0, latitude: pinAMapPosition.lat, longitude: pinAMapPosition.lng }
|
||||
}
|
||||
|
||||
export function generatePointContent (pinAMapPosition: pinAMapPosition) {
|
||||
const position = getPinPosition(pinAMapPosition)
|
||||
return {
|
||||
type: MapElementEnum.PIN,
|
||||
content: generatePoint(position, {
|
||||
color: MapDoodleColor.PinColor,
|
||||
clampToGround: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
function getLieOrPolyPosition (mapPosition: pinAMapPosition[]):MapGeographicPosition[] {
|
||||
const position = [] as MapGeographicPosition[]
|
||||
mapPosition.forEach(item => {
|
||||
position.push({ height: 0, latitude: item.lat, longitude: item.lng })
|
||||
})
|
||||
return position
|
||||
}
|
||||
export function generateLineContent (mapPosition: pinAMapPosition[]) {
|
||||
const position = getLieOrPolyPosition(mapPosition)
|
||||
return {
|
||||
type: MapElementEnum.LINE,
|
||||
content: generateLine(position, {
|
||||
color: MapDoodleColor.PolylineColor,
|
||||
directConnected: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function generatePolyContent (mapPosition: pinAMapPosition[]) {
|
||||
const position = getLieOrPolyPosition(mapPosition)
|
||||
return {
|
||||
type: MapElementEnum.POLY,
|
||||
content: generatePolygon(position, {
|
||||
color: MapDoodleColor.PolygonColor,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { EStorageKey } from '/@/types/enums'
|
||||
import { consoleWarn } from './logger'
|
||||
|
||||
function getStorageData (key: EStorageKey, parse?: boolean): string | null
|
||||
function getStorageData<T> (key: EStorageKey, parse?: boolean): T | null
|
||||
function getStorageData (key: EStorageKey, parse?: boolean): any {
|
||||
const value = window.localStorage.getItem(key)
|
||||
if (parse && value) {
|
||||
try {
|
||||
const result = JSON.parse(value)
|
||||
return result
|
||||
} catch (e) {
|
||||
consoleWarn('appStorage.get failed, err:', e)
|
||||
return null
|
||||
}
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
function clearStorageData (key: EStorageKey | EStorageKey[]) {
|
||||
let keyList: EStorageKey[] = []
|
||||
if (Array.isArray(key)) {
|
||||
keyList = key
|
||||
} else {
|
||||
keyList = [key]
|
||||
}
|
||||
keyList.forEach(item => {
|
||||
window.localStorage.removeItem(item)
|
||||
})
|
||||
}
|
||||
|
||||
const appStorage = {
|
||||
save (key: EStorageKey, value: string) {
|
||||
window.localStorage.setItem(key, value)
|
||||
},
|
||||
get: getStorageData,
|
||||
|
||||
clear: clearStorageData,
|
||||
}
|
||||
|
||||
export default appStorage
|
||||
@@ -0,0 +1,6 @@
|
||||
export function uuidv4 () {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8)
|
||||
return v.toString(16)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Conversion between the coordinates of the National Bureau of Survey and Measurement (Mars coordinates, GCJ02) and the WGS84 coordinate system
|
||||
*/
|
||||
const x_PI = 3.14159265358979324 * 3000.0 / 180.0;
|
||||
const PI = 3.1415926535897932384626;
|
||||
const a = 6378245.0;
|
||||
const ee = 0.00669342162296594323;
|
||||
|
||||
|
||||
/**
|
||||
* WGS84 to Mars coordinate system GCj02
|
||||
* @param lng
|
||||
* @param lat
|
||||
* @returns {*[]}
|
||||
*/
|
||||
export function wgs84togcj02(lng, lat) {
|
||||
if (out_of_china(lng, lat)) {
|
||||
return [lng, lat]
|
||||
}
|
||||
else {
|
||||
var dlat = transformlat(lng - 105.0, lat - 35.0);
|
||||
var dlng = transformlng(lng - 105.0, lat - 35.0);
|
||||
var radlat = lat / 180.0 * PI;
|
||||
var magic = Math.sin(radlat);
|
||||
magic = 1 - ee * magic * magic;
|
||||
var sqrtmagic = Math.sqrt(magic);
|
||||
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
|
||||
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
|
||||
var mglat = lat + dlat;
|
||||
var mglng = lng + dlng;
|
||||
return [mglng, mglat]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GCJ02 transform WGS84
|
||||
* @param lng
|
||||
* @param lat
|
||||
* @returns {*[]}
|
||||
*/
|
||||
export function gcj02towgs84(lng, lat) {
|
||||
var lat = +lat;
|
||||
var lng = +lng;
|
||||
if (out_of_china(lng, lat)) {
|
||||
return [lng, lat]
|
||||
} else {
|
||||
var dlat = transformlat(lng - 105.0, lat - 35.0);
|
||||
var dlng = transformlng(lng - 105.0, lat - 35.0);
|
||||
var radlat = lat / 180.0 * PI;
|
||||
var magic = Math.sin(radlat);
|
||||
magic = 1 - ee * magic * magic;
|
||||
var sqrtmagic = Math.sqrt(magic);
|
||||
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
|
||||
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
|
||||
var mglat = lat + dlat;
|
||||
var mglng = lng + dlng;
|
||||
return [lng * 2 - mglng, lat * 2 - mglat]
|
||||
}
|
||||
}
|
||||
|
||||
function transformlat(lng, lat) {
|
||||
var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
|
||||
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
|
||||
ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
|
||||
ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
|
||||
return ret
|
||||
}
|
||||
|
||||
export function transformlng(lng, lat) {
|
||||
var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
|
||||
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
|
||||
ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;
|
||||
ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* Judge whether you are in the country or not if you are not in the country
|
||||
* @param lng
|
||||
* @param lat
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function out_of_china(lng, lat) {
|
||||
return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"baseUrl": ".",
|
||||
"types": ["vite/client"],
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
],
|
||||
"paths": {
|
||||
"/@/*": [
|
||||
"src/*"
|
||||
],
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue"
|
||||
, "src/vendors/coordtransform.js" ]
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
// config alias
|
||||
import path from 'path'
|
||||
import { ConfigEnv, defineConfig, UserConfigExport } from 'vite'
|
||||
import ViteComponents, { AntDesignVueResolver } from 'vite-plugin-components'
|
||||
// Introduce eslint plugin
|
||||
import eslintPlugin from 'vite-plugin-eslint'
|
||||
import OptimizationPersist from 'vite-plugin-optimize-persist'
|
||||
import PkgConfig from 'vite-plugin-package-config'
|
||||
import viteSvgIcons from 'vite-plugin-svg-icons'
|
||||
import { viteVConsole } from 'vite-plugin-vconsole'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default ({ command, mode }: ConfigEnv): UserConfigExport => defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
eslintPlugin({
|
||||
fix: true
|
||||
}),
|
||||
ViteComponents({
|
||||
customComponentResolvers: [AntDesignVueResolver()],
|
||||
}),
|
||||
viteSvgIcons({
|
||||
// 指定需要缓存的图标文件夹
|
||||
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
|
||||
// 指定symbolId格式
|
||||
symbolId: 'icon-[dir]-[name]',
|
||||
}),
|
||||
viteVConsole({
|
||||
entry: path.resolve(__dirname, './src/main.ts'), // 入口文件
|
||||
// localEnabled: command === 'serve', // serve开发环境下
|
||||
// enabled: command !== 'serve' || mode === 'test', // 打包环境下/发布测试包,
|
||||
config: { // vconsole 配置项
|
||||
maxLogNumber: 1000,
|
||||
theme: 'light'
|
||||
}
|
||||
}),
|
||||
PkgConfig(),
|
||||
OptimizationPersist()
|
||||
// [svgBuilder('./src/assets/icons/')] // All svg under src/icons/svg/ have been imported here, no need to import separately
|
||||
],
|
||||
server: {
|
||||
open: true,
|
||||
host: '0.0.0.0',
|
||||
port: 8080
|
||||
},
|
||||
envDir: './env',
|
||||
resolve: {
|
||||
alias: [{
|
||||
// https://github.com/vitejs/vite/issues/279#issuecomment-635646269
|
||||
find: '/@',
|
||||
replacement: path.resolve(__dirname, './src'),
|
||||
}
|
||||
]
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
// example : additionalData: `@import "./src/design/styles/variables";`
|
||||
// dont need include file extend .scss
|
||||
additionalData: '@import "./src/styles/variables";'
|
||||
},
|
||||
}
|
||||
},
|
||||
base: '/',
|
||||
build: {
|
||||
target: ['es2015'], // 最低支持 es2015
|
||||
sourcemap: true
|
||||
}
|
||||
})
|
||||