sean.zhou
3 years ago
93 changed files with 23360 additions and 0 deletions
@ -0,0 +1,22 @@
@@ -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 @@
@@ -0,0 +1,25 @@
|
||||
node_modules |
||||
.DS_Store |
||||
dist |
||||
dist-ssr |
||||
*.local |
||||
node_modules/ |
||||
|
||||
# Log files |
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
|
||||
# Editor directories and files |
||||
.idea |
||||
# .vscode |
||||
*.suo |
||||
*.ntvs* |
||||
*.njsproj |
||||
*.sln |
||||
*.sw? |
||||
|
||||
.history |
||||
/coverage |
||||
/backup |
||||
node_modules |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
node_modules |
||||
.DS_Store |
||||
dist |
||||
dist-ssr |
||||
*.local |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
VITE_APP_ENVIRONMENT=DEV |
||||
VITE_APP_APIGATEWAY_BACKEND_HOST='' |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
VITE_APP_ENVIRONMENT=production |
||||
VITE_APP_APIGATEWAY_BACKEND_HOST='' |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
VITE_APP_ENVIRONMENT=STAG |
||||
VITE_APP_APIGATEWAY_BACKEND_HOST='' |
@ -0,0 +1,13 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,5 @@
|
||||
declare module '*.vue' { |
||||
import { DefineComponent } from 'vue' |
||||
const component: DefineComponent<{}, {}, any> |
||||
export default component |
||||
} |
@ -0,0 +1,135 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,3 @@
|
||||
@import './common.scss'; |
||||
@import 'flex.style.scss'; |
||||
@import 'fonts.scss'; |
@ -0,0 +1,164 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,6 @@
|
||||
export enum MapDoodleEnum { |
||||
PIN = 'pin', |
||||
POLYLINE = 'polyline', |
||||
POLYGON = 'polygon', |
||||
Close = 'off' |
||||
} |
@ -0,0 +1,100 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
@ -0,0 +1,29 @@
@@ -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 @@
@@ -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