sean.zhou
3 years ago
93 changed files with 23360 additions and 0 deletions
@ -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,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" |
||||||
|
] |
||||||
|
} |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 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,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,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); |
||||||
|
} |
File diff suppressed because one or more lines are too long
@ -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 |
||||||
|
} |
||||||
|
}) |
Loading…
Reference in new issue