commit
0e2b29a2c0
54 changed files with 8318 additions and 0 deletions
@ -0,0 +1,24 @@ |
|||
# Logs |
|||
logs |
|||
*.log |
|||
npm-debug.log* |
|||
yarn-debug.log* |
|||
yarn-error.log* |
|||
pnpm-debug.log* |
|||
lerna-debug.log* |
|||
|
|||
node_modules |
|||
dist |
|||
dist-ssr |
|||
*.local |
|||
|
|||
# Editor directories and files |
|||
.vscode/* |
|||
!.vscode/extensions.json |
|||
.idea |
|||
.DS_Store |
|||
*.suo |
|||
*.ntvs* |
|||
*.njsproj |
|||
*.sln |
|||
*.sw? |
@ -0,0 +1,3 @@ |
|||
{ |
|||
"recommendations": ["Vue.volar"] |
|||
} |
@ -0,0 +1,16 @@ |
|||
# Vue 3 + TypeScript + Vite |
|||
|
|||
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more. |
|||
|
|||
## Recommended IDE Setup |
|||
|
|||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) |
|||
|
|||
## Type Support For `.vue` Imports in TS |
|||
|
|||
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's Take Over mode by following these steps: |
|||
|
|||
1. Run `Extensions: Show Built-in Extensions` from VS Code's command palette, look for `TypeScript and JavaScript Language Features`, then right click and select `Disable (Workspace)`. By default, Take Over mode will enable itself if the default TypeScript extension is disabled. |
|||
2. Reload the VS Code window by running `Developer: Reload Window` from the command palette. |
|||
|
|||
You can learn more about Take Over mode [here](https://github.com/johnsoncodehk/volar/discussions/471). |
@ -0,0 +1,28 @@ |
|||
// generated by unplugin-vue-components
|
|||
// We suggest you to commit this file into source control
|
|||
// Read more: https://github.com/vuejs/core/pull/3399
|
|||
import '@vue/runtime-core' |
|||
|
|||
export {} |
|||
|
|||
declare module '@vue/runtime-core' { |
|||
export interface GlobalComponents { |
|||
ACol: typeof import('ant-design-vue/es')['Col'] |
|||
ACollapse: typeof import('ant-design-vue/es')['Collapse'] |
|||
ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel'] |
|||
ARow: typeof import('ant-design-vue/es')['Row'] |
|||
Aside: typeof import('./src/components/aside/index.vue')['default'] |
|||
ATabPane: typeof import('ant-design-vue/es')['TabPane'] |
|||
ATabs: typeof import('ant-design-vue/es')['Tabs'] |
|||
ATree: typeof import('ant-design-vue/es')['Tree'] |
|||
Basic: typeof import('./src/components/aside/leftMenu/leftMenuItem/basic.vue')['default'] |
|||
LeftMenu: typeof import('./src/components/aside/leftMenu/index.vue')['default'] |
|||
Models3d: typeof import('./src/components/aside/leftMenu/leftMenuItem/models3d.vue')['default'] |
|||
Modelsimages: typeof import('./src/components/aside/leftMenu/leftMenuItem/modelsimages.vue')['default'] |
|||
RightMenu: typeof import('./src/components/aside/rightMenu/index.vue')['default'] |
|||
RouterLink: typeof import('vue-router')['RouterLink'] |
|||
RouterView: typeof import('vue-router')['RouterView'] |
|||
Sytemcomponent: typeof import('./src/components/aside/leftMenu/leftMenuItem/sytemcomponent.vue')['default'] |
|||
Tools: typeof import('./src/components/aside/leftMenu/leftMenuItem/tools.vue')['default'] |
|||
} |
|||
} |
@ -0,0 +1,14 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="zh-CN"> |
|||
<head> |
|||
<meta charset="UTF-8" /> |
|||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|||
<title>Vite + Vue + TS</title> |
|||
</head> |
|||
<body> |
|||
<div id="app"></div> |
|||
<script type="module" src="/src/main.ts"></script> |
|||
|
|||
</body> |
|||
</html> |
File diff suppressed because it is too large
@ -0,0 +1,29 @@ |
|||
{ |
|||
"name": "hq-web", |
|||
"private": true, |
|||
"version": "0.0.0", |
|||
"type": "module", |
|||
"scripts": { |
|||
"serve": "vite", |
|||
"build": "vue-tsc && vite build", |
|||
"preview": "vite preview" |
|||
}, |
|||
"dependencies": { |
|||
"ant-design-vue": "^3.3.0-beta.3", |
|||
"axios": "^1.2.0", |
|||
"mqtt": "^4.3.7", |
|||
"pinia": "^2.0.25", |
|||
"qs": "^6.11.0", |
|||
"vue": "^3.2.41", |
|||
"vue-router": "^4.1.6", |
|||
"vue3-colorpicker": "^2.0.11" |
|||
}, |
|||
"devDependencies": { |
|||
"@vitejs/plugin-vue": "^3.2.0", |
|||
"less": "^4.1.3", |
|||
"typescript": "^4.6.4", |
|||
"unplugin-vue-components": "^0.22.11", |
|||
"vite": "^3.2.3", |
|||
"vue-tsc": "^1.0.9" |
|||
} |
|||
} |
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,58 @@ |
|||
|
|||
<template> |
|||
<router-view></router-view> |
|||
<audio |
|||
class="audio" |
|||
src="" |
|||
@timeupdate="timeupdatefn" |
|||
style="display:none;" |
|||
></audio> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
// @ts-nocheck |
|||
import { useStore } from '@/store'; |
|||
import { storeToRefs } from 'pinia'; |
|||
import MqttUnit from "@/utils/mqttunit"; |
|||
import { onMounted } from 'vue'; |
|||
import {WebRtcStreamer} from '@/utils/webrtc-streamer/webrtcstreamer.js' |
|||
let pinia = useStore() |
|||
onMounted(() => { |
|||
let client = new MqttUnit().mqttInit(); |
|||
let WebRtcStreamerRt=WebRtcStreamer() |
|||
if (typeof window !== 'undefined' && typeof window.document !== 'undefined') { |
|||
window.WebRtcStreamer = WebRtcStreamerRt; |
|||
} |
|||
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { |
|||
module.exports = WebRtcStreamerRt; |
|||
} |
|||
client.on('message', (topic: string, message: Uint8Array) => { |
|||
const utf8decoder = new TextDecoder(); |
|||
const msgString = utf8decoder.decode(message); |
|||
if (topic.startsWith("/server/warning")) { |
|||
// $mitt.emit("warnInfo", msgString); |
|||
console.log(msgString); |
|||
|
|||
} |
|||
}) |
|||
}) |
|||
function timeupdatefn(e:any) { |
|||
let {audioCount,audioCurrentCount}=storeToRefs(pinia) |
|||
if (e.target.currentTime >= e.target.duration) { |
|||
let count_ = audioCurrentCount.value + 1; |
|||
pinia.updateaudioCurrentCount(count_) |
|||
if (audioCurrentCount.value < audioCount.value) { |
|||
e.target.currentTime = 0; |
|||
e.target.play(); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
|
|||
|
|||
<style lang="less"> |
|||
body{ |
|||
height: 100vh; |
|||
overflow: hidden; |
|||
} |
|||
</style> |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,43 @@ |
|||
import { axios } from "@/axios/request"; |
|||
import { AxiosPromise } from "axios"; |
|||
|
|||
|
|||
//get
|
|||
export function getAction (url: string, parameter?: unknown): AxiosPromise |
|||
{ |
|||
return axios({ |
|||
url: url, |
|||
method: 'get', |
|||
params: parameter |
|||
}) |
|||
} |
|||
|
|||
//post
|
|||
export function postAction (url: string, parameter?: unknown): AxiosPromise |
|||
{ |
|||
return axios({ |
|||
url: url, |
|||
method: 'post', |
|||
data: parameter |
|||
}) |
|||
} |
|||
|
|||
//put
|
|||
export function putAction (url: string, parameter?: unknown): AxiosPromise |
|||
{ |
|||
return axios({ |
|||
url: url, |
|||
method: 'put', |
|||
data: parameter |
|||
}) |
|||
} |
|||
|
|||
//delete
|
|||
export function deleteAction (url: string, parameter?: unknown): AxiosPromise |
|||
{ |
|||
return axios({ |
|||
url: url, |
|||
method: 'delete', |
|||
params: parameter |
|||
}) |
|||
} |
@ -0,0 +1,436 @@ |
|||
import { deleteAction, getAction, postAction, putAction } from '@/axios/action' |
|||
import { AxiosPromise } from 'axios'; |
|||
// import {
|
|||
// DEVICE_STREAM_TYPE,
|
|||
// NET_PROTO_TYPE,
|
|||
// PTZ_CONTROL_ACTION,
|
|||
// STREAM_TRANS_TYPE,
|
|||
// STREAM_TYPE
|
|||
// } from '@/utils/emun/videoControlEnum';
|
|||
|
|||
const url = { |
|||
camera: { |
|||
getCameraList: '/area/api/getCameraList', |
|||
// getCameraList: '/area/api/cameraByToken?token=Bearer eyJ' +
|
|||
// 'hbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pb' +
|
|||
// 'iIsInN1YiI6InNvbWVib2R5IiwiYXVkIjpbInNvbWVib2R5X2Vs' +
|
|||
// 'c2UiXSwiZXhwIjoxNjYyNzE1MDYyLCJuYmYiOjE2NjI2Mjg2NjI' +
|
|||
// 'sImlhdCI6MTY2MjYyODY2MiwianRpIjoiMSJ9.0GyQ1qZZPcVOT' +
|
|||
// 'jNYe7NgsZYwKaMOGRkb-itK9fqC-Ug',
|
|||
}, |
|||
/* 监控点模块 */ |
|||
label: { |
|||
addLabel: '/military/msAreaLabel/add', |
|||
getLabel: '/military/msAreaLabel/list', |
|||
deleteCamera: '/military/msAreaLabel/deleteCamera', |
|||
editLabel: '/military/msAreaLabel/edit' |
|||
}, |
|||
areaFence: { |
|||
add: "/military/msAreaFence/add", |
|||
//添加防区及相机
|
|||
addAndDevice: "/military/msAreaFence/addFenceAndRelation", |
|||
//获取预置点接口
|
|||
selectPreset: "area/api/selectPreset", |
|||
fenceList: "/military/msAreaFence/list", |
|||
queryFenceById: "/military/msAreaFence/queryFenceAndeRelationById", |
|||
deleteFence: "/military/msAreaFence/deleteFenceAndRelation", |
|||
editFence: "/military/msAreaFence/editFenceAndRelation", |
|||
}, |
|||
cameraVideo: { |
|||
addCameraVideo: '/military/msAreaCameraVideo/add', |
|||
getCameraVideoList: '/military/msAreaCameraVideo/list', |
|||
getCameraVideoById: '/military/msAreaCameraVideo/queryByLabelId', |
|||
editCameraVideo: '/military/msAreaCameraVideo/edit', |
|||
}, |
|||
dict: { |
|||
getBuilding: '/sys/dict/getDictItems/ms_building_info', |
|||
}, |
|||
path: { |
|||
addRoamPath: '/military/msAreaRoamPath/add', |
|||
RoamPathList: '/military/msAreaRoamPath/list', |
|||
deleteRoamPath: '/military/msAreaRoamPath/delete', |
|||
editRoamPath: '/military/msAreaRoamPath/edit', |
|||
msAreaCustomPrimitiveList: '/military/msAreaCustomPrimitive/list', |
|||
msAreaCustomPrimitiveDel: '/military/msAreaCustomPrimitive/delete', |
|||
msAreaCustomPrimitiveEdit: '/military/msAreaCustomPrimitive/edit' |
|||
}, |
|||
patrolVideo: { |
|||
addPatrolVideo: '/military/msAreaCustomPrimitive/add', |
|||
}, |
|||
videoControl: { |
|||
getRealPlayUrl: 'area/api/getRealPlayUrl', |
|||
ptzControl: 'area/api/ptzControl', |
|||
}, |
|||
device: { |
|||
list: '/military/msDeviceInfo/list', |
|||
}, |
|||
warn: { |
|||
//监控报警信息(从中光学获取)
|
|||
getAlarmInfos: '/area/api/qryAlarmInfos', |
|||
getAlarmCount: '/area/api/countAlarm', |
|||
//报警信息(本地获取)
|
|||
getWarnList: '/warn/list', |
|||
getWarnCount: '/warn/getWarnCount', |
|||
getFenceWarn: "/military/msDeviceInfo/downCmd" |
|||
}, |
|||
haplochromatization: { |
|||
list: '/military/msAreaHaplochromatization/list', |
|||
add: '/military/msAreaHaplochromatization/add', |
|||
edit: '/military/msAreaHaplochromatization/edit', |
|||
delete: '/military/msAreaHaplochromatization/delete', |
|||
deleteBatch: '/military/msAreaHaplochromatization/deleteBatch' |
|||
}, |
|||
delCache: '/area/api/delCache', |
|||
// 添加模型
|
|||
models: { |
|||
addModel: "/models/msModels/add", |
|||
selectModel: "/models/msModels/list", |
|||
editModel: "/models/msModels/edit", |
|||
//批量删除
|
|||
deleteModels: "/models/msModels/deleteBatch", |
|||
deleteModel: "/models/msModels/delete", |
|||
}, |
|||
// 测试登录
|
|||
login: 'http://127.0.0.1:8088/', |
|||
loginChecked: 'http://127.0.0.1:8088/checked', |
|||
|
|||
/* 看板模块 */ |
|||
board: { |
|||
//看板
|
|||
getBoard: '/api/rpt/sensorbydevicecode2', |
|||
//看板编号
|
|||
getBoardType: "/api/rpt/boardtype", |
|||
//看板信息
|
|||
getBoardInfo: "/models/msModelsBoardApi/list", |
|||
//看板数据接口
|
|||
getBoardData: '/api/rpt/boarddata', |
|||
}, |
|||
/* iot设备 */ |
|||
iotDevice: { |
|||
getNode: "/api/iot/nodelist", |
|||
getSensor: "/api/iot/sensorlist", |
|||
getSensorInfo: "/api/iot/sensorbyid", |
|||
}, |
|||
|
|||
modelsinfo: { |
|||
getModels3DGroup: '/api/models/models3dgroup', |
|||
getModels3D: '/api/models/modelsinfo3d', |
|||
getModelsImageGroup: '/api/models/modelsimagegroup', |
|||
getModelsImage: '/api/models/modelsinfoimage', |
|||
|
|||
} |
|||
|
|||
} |
|||
|
|||
interface LabelEntity { |
|||
labelAttr: number, |
|||
labelName: string, |
|||
labelLon: number, |
|||
labelLat: number, |
|||
labelHeight: number, |
|||
labelStatus: number, |
|||
labelImgUrl: string, |
|||
deviceUid: string, |
|||
cameraVideoFlag: number, |
|||
belongTo: string, |
|||
} |
|||
|
|||
interface LabelQueryList { |
|||
labelStatus: number, |
|||
id?: string, |
|||
belongTo?: string, |
|||
pageNo?: number, |
|||
pageSize?: number, |
|||
// selectArea?: string,
|
|||
} |
|||
|
|||
interface CameraVideoEntity { |
|||
lon: number, |
|||
lat: number, |
|||
height: number, |
|||
yaw: number, |
|||
pitch: number, |
|||
roll: number, |
|||
fovh: number, |
|||
fovv: number, |
|||
near: number, |
|||
far: number, |
|||
videourl: string, |
|||
maskurl: string, |
|||
// 相机id
|
|||
labelId: string, |
|||
} |
|||
|
|||
interface CameraVideoQueryList { |
|||
labelId: string, |
|||
} |
|||
|
|||
type PatrolVideo = { |
|||
/**经度*/ |
|||
lon: number, |
|||
/**纬度*/ |
|||
lat: number; |
|||
/**高度*/ |
|||
height: number; |
|||
/**偏航角*/ |
|||
yaw: number; |
|||
/**俯仰角*/ |
|||
pitch: number; |
|||
/**翻转角*/ |
|||
roll: number; |
|||
/**漫游路径id*/ |
|||
pathId: string; |
|||
/**相机设备Id*/ |
|||
deviceUid: string; |
|||
} |
|||
|
|||
// 云台控制
|
|||
// 获取摄像头实时视频播放地址 参数
|
|||
// interface GetRealPlayUrlParams {
|
|||
// // 摄像机UID
|
|||
// deviceId: string,
|
|||
// // 传输协议
|
|||
// streamTransType: STREAM_TRANS_TYPE,
|
|||
// // 码流
|
|||
// streamType: STREAM_TYPE,
|
|||
// // 是否转hls 默认false
|
|||
// enableHls: boolean,
|
|||
// // 是否mp4录制 默认false
|
|||
// enableMp4: boolean,
|
|||
// // 拉流时,拉流方式 默认 0:tcp
|
|||
// netProto: NET_PROTO_TYPE,
|
|||
// // 拉流超时时间,单位秒 默认10
|
|||
// timeoutSec: number,
|
|||
// // 失败时重试的次数
|
|||
// retryCount: number,
|
|||
// // 传输协议 (添加海康私有流时使用该字段:6-TRANS_PROTO_PRIVATE)
|
|||
// deviceStreamType: DEVICE_STREAM_TYPE,
|
|||
// }
|
|||
|
|||
// // 云台控制 参数
|
|||
// interface PtzControlParams {
|
|||
// deviceId: string,
|
|||
// // 云台控制命令 (0-199)
|
|||
// ptzCommand: PTZ_CONTROL_ACTION,
|
|||
// // 云台控制参数(云台速度)
|
|||
// arguments: number,
|
|||
// }
|
|||
|
|||
interface AlarmInfoParams { |
|||
deviceId: string, |
|||
startTime: string, |
|||
stopTime: string, |
|||
eventType?: number, |
|||
targetType?: number, |
|||
pageIndex: number, |
|||
pageSize: number, |
|||
alarmLevel?: number, |
|||
alarmMode?: number |
|||
} |
|||
|
|||
interface AlarmCountParams { |
|||
deviceId: string, |
|||
startTime: string, |
|||
stopTime: string, |
|||
eventType: number, |
|||
targetType: number, |
|||
dateUnit: number |
|||
} |
|||
|
|||
interface DeviceParams { |
|||
deviceType: number, |
|||
} |
|||
interface WarnInfoParams { |
|||
cameraId: string, |
|||
column?: string, |
|||
order?: string, |
|||
pageNo?: number, |
|||
pageSize?: number |
|||
} |
|||
|
|||
interface WarnCountParams { |
|||
cameraId: string, |
|||
startTime: string, |
|||
endTime: string, |
|||
warnType?: number, |
|||
warnLevel?: number |
|||
} |
|||
|
|||
interface HaplochromatizationParams { |
|||
id: string, |
|||
name: string, |
|||
positions: string, |
|||
height: number, |
|||
extrudedHeight: number, |
|||
color: string, |
|||
showobj: number, |
|||
showhelper: number, |
|||
belongTo: string | null, |
|||
} |
|||
interface Models { |
|||
"createBy"?: string, |
|||
"createTime"?: string, |
|||
"height"?: number, |
|||
"id"?: string, |
|||
"isShow"?: number, |
|||
"lat"?: number, |
|||
"lon"?: number, |
|||
"modelCode"?: string, |
|||
"modelGroup"?: string, |
|||
"modelName"?: string, |
|||
"modelSceneCode"?: string, |
|||
"modelType"?: string, |
|||
"modelUrl1"?: string, |
|||
"modelUrl2"?: string, |
|||
"modelX"?: number, |
|||
"modelY"?: number, |
|||
"modelZ"?: number, |
|||
"pitch"?: number, |
|||
"roll"?: number, |
|||
"sysOrgCode"?: string, |
|||
"template"?: string, |
|||
"updateBy"?: string, |
|||
"updateTime"?: string, |
|||
"url"?: string, |
|||
"urlcondition"?: string, |
|||
"yaw"?: number, |
|||
"pageNo"?: number, |
|||
"pageSize"?: number |
|||
"boardNo"?: string |
|||
"boardType"?: string |
|||
} |
|||
|
|||
|
|||
|
|||
class Api { |
|||
/* 相机模块 */ |
|||
public static camera = { |
|||
getCameraList: (params?: { token: string }): AxiosPromise => getAction(url.camera.getCameraList, params), |
|||
} |
|||
/* 监控点模块 */ |
|||
public static label = { |
|||
addLabel: (params: LabelEntity | any): AxiosPromise => postAction(url.label.addLabel, params), |
|||
getLabel: (params: LabelQueryList): AxiosPromise => getAction(url.label.getLabel, params), |
|||
deleteLabel: (params: { id: string }): AxiosPromise => deleteAction(url.label.deleteCamera, params), |
|||
editLabel: (params: LabelEntity): AxiosPromise => putAction(url.label.editLabel, params), |
|||
} |
|||
/* 视频融合模块 */ |
|||
public static cameraVideo = { |
|||
addCameraVideo: (params?: CameraVideoEntity): AxiosPromise => postAction(url.cameraVideo.addCameraVideo, params), |
|||
getCameraVideoById: (params?: { labelId: string }): AxiosPromise => getAction(url.cameraVideo.getCameraVideoById, params), |
|||
getCameraVideoList: (params?: CameraVideoQueryList): AxiosPromise => getAction(url.cameraVideo.getCameraVideoList, params), |
|||
editCameraVideo: (params?: CameraVideoEntity): AxiosPromise => putAction(url.cameraVideo.editCameraVideo, params), |
|||
deleteCameraVideo: (params?: { id: string }): AxiosPromise => deleteAction(url.cameraVideo.editCameraVideo, params), |
|||
} |
|||
/* 数据字典 */ |
|||
public static dict = { |
|||
getBuilding: (): AxiosPromise => getAction(url.dict.getBuilding), |
|||
} |
|||
/* 巡检模块 */ |
|||
public static path = { |
|||
addRoamPath: (params?: unknown): AxiosPromise => postAction(url.path.addRoamPath, params), |
|||
RoamPathList: (params?: { pageNo: number, pageSize: number, selectArea: string }): AxiosPromise => getAction(url.path.RoamPathList, params), |
|||
deleteRoamPath: (params?: unknown): AxiosPromise => deleteAction(url.path.deleteRoamPath, params), |
|||
editRoamPath: (params?: unknown): AxiosPromise => putAction(url.path.editRoamPath, params), |
|||
msAreaCustomPrimitiveList: (params?: unknown): AxiosPromise => getAction(url.path.msAreaCustomPrimitiveList, params), |
|||
msAreaCustomPrimitiveDel: (params?: unknown): AxiosPromise => deleteAction(url.path.msAreaCustomPrimitiveDel, params), |
|||
msAreaCustomPrimitiveEdit: (params?: unknown): AxiosPromise => putAction(url.path.msAreaCustomPrimitiveEdit, params), |
|||
} |
|||
|
|||
/*巡检视频模块*/ |
|||
public static patrolVideo = { |
|||
addPatrolVideo: (params?: PatrolVideo): AxiosPromise => postAction(url.patrolVideo.addPatrolVideo, params), |
|||
} |
|||
/* 云台控制模块 */ |
|||
// public static videoControl = {
|
|||
// // 获取摄像头实时视频播放地址
|
|||
// getRealPlayUrl: (params?: GetRealPlayUrlParams): AxiosPromise => getAction(url.videoControl.getRealPlayUrl, params),
|
|||
// // 云台控制
|
|||
// ptzControl: (params?: PtzControlParams): AxiosPromise => getAction(url.videoControl.ptzControl, params),
|
|||
// }
|
|||
/* 设备信息模块*/ |
|||
public static device = { |
|||
getDeviceList: (params?: DeviceParams): AxiosPromise => getAction(url.device.list, params) |
|||
} |
|||
/* 警示模块*/ |
|||
public static warn = { |
|||
getAlarmInfos: (params?: AlarmInfoParams): AxiosPromise => getAction(url.warn.getAlarmInfos, params), |
|||
getAlarmCount: (params?: AlarmCountParams): AxiosPromise => getAction(url.warn.getAlarmCount, params), |
|||
getWarnList: (params?: WarnInfoParams): AxiosPromise => getAction(url.warn.getWarnList, params), |
|||
getWarnCount: (params?: WarnCountParams): AxiosPromise => getAction(url.warn.getWarnCount, params), |
|||
getFenceWarn: (params?: WarnCountParams): AxiosPromise => getAction(url.warn.getFenceWarn, params), |
|||
} |
|||
/* 缓存 */ |
|||
public static cache = { |
|||
delCache: (): AxiosPromise => deleteAction(url.delCache), |
|||
} |
|||
/* 单体化 */ |
|||
public static haplochromatization = { |
|||
list: (params?: unknown): AxiosPromise => getAction(url.haplochromatization.list, params), |
|||
add: (params?: HaplochromatizationParams): AxiosPromise => postAction(url.haplochromatization.add, params), |
|||
edit: (params?: HaplochromatizationParams): AxiosPromise => putAction(url.haplochromatization.edit, params), |
|||
delete: (params: { id: string }): AxiosPromise => deleteAction(url.haplochromatization.delete, params), |
|||
deleteBatch: (params: { ids: string }): AxiosPromise => deleteAction(url.haplochromatization.delete, params), |
|||
} |
|||
/* 模型 */ |
|||
public static models = { |
|||
add: (params: Models): AxiosPromise => postAction(url.models.addModel, params), |
|||
list: (params: Models): AxiosPromise => getAction(url.models.selectModel, params), |
|||
edit: (params: Models): AxiosPromise => postAction(url.models.editModel, params), |
|||
deleteBatch: (params: { ids: string }): AxiosPromise => deleteAction(url.models.deleteModels, params), |
|||
delete: (params: { id: string }): AxiosPromise => deleteAction(url.models.deleteModel, params), |
|||
} |
|||
/* 用户登录 */ |
|||
public static Login = { |
|||
login: (params: any): AxiosPromise => getAction(url.login, params), |
|||
loginChecked: (params: any): AxiosPromise => getAction(url.loginChecked, params), |
|||
} |
|||
/* 看板模块 */ |
|||
public static board = { |
|||
getBoardList: (params?: any): AxiosPromise => getAction(url.board.getBoard, params), |
|||
getBoardTypeList: (params?: any): AxiosPromise => getAction(url.board.getBoardType, params), |
|||
getBoardInfo: (params?: any): AxiosPromise => getAction(url.board.getBoardInfo, params), |
|||
getBoardDataList: (params: { boardtype: string }): AxiosPromise => getAction(url.board.getBoardData, params), |
|||
} |
|||
/* 防区 */ |
|||
public static areaFence = { |
|||
add: (params?: any): AxiosPromise => postAction(url.areaFence.add, params), |
|||
addAndDevice: (params?: any): AxiosPromise => postAction(url.areaFence.addAndDevice, params), |
|||
selectPreset: (params?: any): AxiosPromise => getAction(url.areaFence.selectPreset, params), |
|||
getFenceList: (params?: any): AxiosPromise => getAction(url.areaFence.fenceList, params), |
|||
queryFenceById: (params?: { id: string }): AxiosPromise => getAction(url.areaFence.queryFenceById, params), |
|||
deleteFence: (params: { id: string }): AxiosPromise => deleteAction(url.areaFence.deleteFence, params), |
|||
editFence: (params: any): AxiosPromise => putAction(url.areaFence.editFence, params), |
|||
} |
|||
/* 相机模块 */ |
|||
public static modelsinfo = { |
|||
getModels3DGroup: (params?: { token: string }): AxiosPromise => getAction(url.modelsinfo.getModels3DGroup, params), |
|||
getModels3D: (params?: { token: string }): AxiosPromise => getAction(url.modelsinfo.getModels3D, params), |
|||
getModelsImageGroup: (params?: { token: string }): AxiosPromise => getAction(url.modelsinfo.getModelsImageGroup, params), |
|||
getModelsImage: (params?: { token: string }): AxiosPromise => getAction(url.modelsinfo.getModelsImage, params), |
|||
|
|||
} |
|||
/*iot设备*/ |
|||
public static iotDevice = { |
|||
//设备列表
|
|||
getNodeList: (params?: any): AxiosPromise => getAction(url.iotDevice.getNode, params), |
|||
//传感器列表信息
|
|||
getSensorList: (params?: { nodeid: string }): AxiosPromise => getAction(url.iotDevice.getSensor, params), |
|||
//传感器
|
|||
getSensorInfo: (params?: { id: string }): AxiosPromise => getAction(url.iotDevice.getSensorInfo, params), |
|||
} |
|||
} |
|||
|
|||
export default Api |
|||
export { |
|||
Api, |
|||
// LabelEntity,
|
|||
// LabelQueryList,
|
|||
// CameraVideoEntity,
|
|||
// CameraVideoQueryList,
|
|||
// GetRealPlayUrlParams,
|
|||
// PtzControlParams,
|
|||
// HaplochromatizationParams,
|
|||
// AlarmInfoParams,
|
|||
// AlarmCountParams,
|
|||
} |
@ -0,0 +1,26 @@ |
|||
import {axios} from '@/utils/axios'; |
|||
import {apiUrl} from "@/axios"; |
|||
import qs from "qs"; |
|||
|
|||
enum Api { |
|||
Add = '/cbCamera/add', |
|||
Update = '/cbCamera/update', |
|||
Delete = '/cbCamera/delete', |
|||
Get = '/cbCamera/get', |
|||
GetList = '/cbCamera/getList', |
|||
GetPageList = '/cbCamera/getPageList', |
|||
} |
|||
|
|||
const CisApiUrl = apiUrl.CisApiUrl; |
|||
|
|||
export const Add = (params?: any) => axios.post(CisApiUrl + Api.Add, qs.stringify(params)) |
|||
|
|||
export const Update = (params?: any) => axios.post(CisApiUrl + Api.Update, qs.stringify(params)) |
|||
|
|||
export const Delete = (params?: any) => axios.post(CisApiUrl + Api.Delete, qs.stringify(params)) |
|||
|
|||
export const Get = (params?: any) => axios.get(CisApiUrl + Api.Get, {params: params}) |
|||
|
|||
export const GetList = (params?: any) => axios.get(CisApiUrl + Api.GetList, {params: params}) |
|||
|
|||
export const GetPageList = (params?: any) => axios.get(CisApiUrl + Api.GetPageList, {params: params}) |
@ -0,0 +1,26 @@ |
|||
import {axios} from '@/utils/axios'; |
|||
import {apiUrl} from "@/axios"; |
|||
import qs from "qs"; |
|||
|
|||
enum Api { |
|||
Add = '/cmMarkGroup/add', |
|||
Update = '/cmMarkGroup/update', |
|||
Delete = '/cmMarkGroup/delete', |
|||
Get = '/cmMarkGroup/get', |
|||
GetList = '/cmMarkGroup/getList', |
|||
GetPageList = '/cmMarkGroup/getPageList', |
|||
} |
|||
|
|||
const CisApiUrl = apiUrl.CisApiUrl; |
|||
|
|||
export const Add = (params?: any) => axios.post(CisApiUrl + Api.Add, qs.stringify(params)) |
|||
|
|||
export const Update = (params?: any) => axios.post(CisApiUrl + Api.Update, qs.stringify(params)) |
|||
|
|||
export const Delete = (params?: any) => axios.post(CisApiUrl + Api.Delete, qs.stringify(params)) |
|||
|
|||
export const Get = (params?: any) => axios.get(CisApiUrl + Api.Get, {params: params}) |
|||
|
|||
export const GetList = (params?: any) => axios.get(CisApiUrl + Api.GetList, {params: params}) |
|||
|
|||
export const GetPageList = (params?: any) => axios.get(CisApiUrl + Api.GetPageList, {params: params}) |
@ -0,0 +1,29 @@ |
|||
import {axios} from '@/utils/axios'; |
|||
import {apiUrl} from "@/axios"; |
|||
import qs from 'qs'; |
|||
|
|||
enum Api { |
|||
Add = '/cmMarkLabel/add', |
|||
AddReturnId = '/cmMarkLabel/addReturnId', |
|||
Update = '/cmMarkLabel/update', |
|||
Delete = '/cmMarkLabel/delete', |
|||
Get = '/cmMarkLabel/get', |
|||
GetList = '/cmMarkLabel/getList', |
|||
GetPageList = '/cmMarkLabel/getPageList', |
|||
} |
|||
|
|||
const CisApiUrl = apiUrl.CisApiUrl; |
|||
|
|||
export const Add = (params?: any) => axios.post(CisApiUrl + Api.Add, qs.stringify(params)) |
|||
|
|||
export const AddReturnId = (params?: any) => axios.post(CisApiUrl + Api.AddReturnId, qs.stringify(params)) |
|||
|
|||
export const Update = (params?: any) => axios.post(CisApiUrl + Api.Update, qs.stringify(params)) |
|||
|
|||
export const Delete = (params?: any) => axios.post(CisApiUrl + Api.Delete+"?"+qs.stringify(params)) |
|||
|
|||
export const Get = (params?: any) => axios.get(CisApiUrl + Api.Get, {params: params}) |
|||
|
|||
export const GetList = (params?: any) => axios.get(CisApiUrl + Api.GetList, {params: params}) |
|||
|
|||
export const GetPageList = (params?: any) => axios.get(CisApiUrl + Api.GetPageList, {params: params}) |
@ -0,0 +1,29 @@ |
|||
import {axios} from '@/utils/axios'; |
|||
import {apiUrl} from "@/axios"; |
|||
import qs from 'qs'; |
|||
|
|||
enum Api { |
|||
ActivateSearcher = '/markSearch/activateSearcher', |
|||
DeActiveSearcher = '/markSearch/deActiveSearcher', |
|||
IsExistsSearcher = '/markSearch/isExistsSearcher', |
|||
ActivateMarkLabel = '/markSearch/activateMarkLabel', |
|||
DeactivateMarkLabel = '/markSearch/deactivateMarkLabel', |
|||
IsExistsMarkLabel = '/markSearch/isExistsMarkLabel', |
|||
GetMarkLabelCalcResultList = '/markSearch/getMarkLabelCalcResultList', |
|||
} |
|||
|
|||
const CisApiUrl = apiUrl.CisApiUrl; |
|||
|
|||
export const ActiveSearcher = (params?: any) => axios.post(CisApiUrl + Api.ActivateSearcher, qs.stringify(params)) |
|||
|
|||
export const DeActiveSearcher = (params?: any) => axios.post(CisApiUrl + Api.DeActiveSearcher, qs.stringify(params)) |
|||
|
|||
export const IsExistsSearcher = (params?: any) => axios.get(CisApiUrl + Api.IsExistsSearcher, {params: params}) |
|||
|
|||
export const ActivateMarkLabel = (params?: any) => axios.post(CisApiUrl + Api.ActivateMarkLabel, qs.stringify(params)) |
|||
|
|||
export const DeactivateMarkLabel = (params?: any) => axios.post(CisApiUrl + Api.DeactivateMarkLabel, qs.stringify(params)) |
|||
|
|||
export const IsExistsMarkLabel = (params?: any) => axios.get(CisApiUrl + Api.IsExistsMarkLabel, {params: params}) |
|||
|
|||
export const GetMarkLabelCalcResultList = (params?: any) => axios.get(CisApiUrl + Api.GetMarkLabelCalcResultList, {params: params}) |
@ -0,0 +1,6 @@ |
|||
export const apiUrl = { |
|||
CisApiUrl: 'http://192.168.1.119:800/api', |
|||
WebRtcUrl: 'http://192.168.1.119:8000', |
|||
OnvifApiUrl: '' |
|||
// CisApiUrl: 'https://192.168.1.119:5001/api'
|
|||
} |
@ -0,0 +1 @@ |
|||
export default{} |
@ -0,0 +1,114 @@ |
|||
import axios, { AxiosResponse, AxiosRequestConfig, Canceler } from 'axios' |
|||
import { message, notification } from 'ant-design-vue' |
|||
|
|||
/** |
|||
* http请求工具类 |
|||
* |
|||
* 请求拦截器 负责将客户端标识token值存储并放置在头部提交给服务端 |
|||
* 响应拦截器 负责全局处理业务请求的网络或者业务错误 |
|||
*/ |
|||
// 开启跨域
|
|||
axios.defaults.withCredentials = true |
|||
// 创建 axios 实例
|
|||
const service = axios.create({ |
|||
baseURL: process.env.VUE_APP_API_BASE_URL, // api base_url
|
|||
timeout: 30 * 1000, // 请求超时时间
|
|||
// withCredentials: true, // 跨域请求时发送 cookies
|
|||
// headers: {
|
|||
// "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
|
|||
// },
|
|||
}); |
|||
const ACCESS_TOKEN = 'Access-Token' |
|||
|
|||
//请求失败后的错误统一处理
|
|||
const errorHandle = (error: any) => { |
|||
if (error.response) { |
|||
const data: any = error.response.data |
|||
const token: string | null = localStorage.getItem(ACCESS_TOKEN) |
|||
console.log("------异常响应------", token) |
|||
console.log("------异常响应------", error.response.status) |
|||
switch (error.response.status) { |
|||
case 401: |
|||
message.error("登录状态失效,请重新登录"); |
|||
break |
|||
case 403: |
|||
message.error("拒绝访问"); |
|||
break |
|||
case 404: |
|||
message.error("资源不存在"); |
|||
break |
|||
case 405: |
|||
message.error("请求类型有误"); |
|||
break |
|||
case 408: |
|||
message.error("请求超时"); |
|||
break |
|||
case 415: |
|||
message.error("服务器无法处理请求附带的媒体格式"); |
|||
break |
|||
case 500: |
|||
message.error("服务器内部错误"); |
|||
break |
|||
case 502: |
|||
message.error("网关错误"); |
|||
break; |
|||
case 503: |
|||
message.error("服务不可用"); |
|||
break; |
|||
case 504: |
|||
message.error("网关超时"); |
|||
break |
|||
default: |
|||
notification.error({ |
|||
message: '系统提示', |
|||
description: data.message, |
|||
duration: 4 |
|||
}) |
|||
break |
|||
} |
|||
} |
|||
return Promise.reject(error) |
|||
}; |
|||
|
|||
// 取消请求 cancelToken
|
|||
const CancelToken = axios.CancelToken; |
|||
// 请求拦截
|
|||
service.interceptors.request.use((config: AxiosRequestConfig) => { |
|||
// 设置 token 到请求头
|
|||
const token: string | null = localStorage.getItem(ACCESS_TOKEN) |
|||
if (token) { |
|||
axios.defaults.headers.post['X-Access-Token'] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
|
|||
} |
|||
// 设置取消请求
|
|||
config.cancelToken = new CancelToken((cancel: Canceler) => { |
|||
const url = config.url; |
|||
let str = url?.split('/'); |
|||
str = Array.from(new Set(str)); |
|||
const index = str.length - 1; |
|||
const funName = str[index]; |
|||
|
|||
// store.dispatch('setCancel', { cancel, funName: funName }).catch(err => {
|
|||
// console.log('存储 CancelToken 出错:', err);
|
|||
// });
|
|||
}) |
|||
return config; |
|||
}, err => { |
|||
Promise.reject(err).catch(r => { |
|||
console.log('请求拦截器出错:', r); |
|||
}); |
|||
}); |
|||
|
|||
// 响应拦截
|
|||
service.interceptors.response.use((response: AxiosResponse) => { |
|||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|||
// @ts-ignore
|
|||
|
|||
// store.dispatch("response", response).catch(err => {
|
|||
// console.log('移除 CancelToken 出错:', err);
|
|||
// });
|
|||
return response.data |
|||
}, errorHandle) |
|||
|
|||
export { |
|||
service as axios |
|||
} |
@ -0,0 +1,34 @@ |
|||
<!-- |
|||
*@描述: admin主页构建 |
|||
*@作者: |
|||
*@日期: 9/27 |
|||
*@版本:1.0 |
|||
*/ |
|||
--> |
|||
<template> |
|||
<a-row> |
|||
<a-col :span="3" class="left-com" > |
|||
<comLeftMenu></comLeftMenu> |
|||
</a-col> |
|||
<a-col :span="18" class=" com-setup-center "> |
|||
<slot></slot> |
|||
</a-col> |
|||
<a-col :span="3" class="right-com" ><comRightMenu></comRightMenu></a-col> |
|||
</a-row> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import comLeftMenu from './leftMenu/index.vue'; |
|||
import comRightMenu from './rightMenu/index.vue'; |
|||
</script> |
|||
<style lang="less" scoped> |
|||
.com-setup-center { |
|||
height: 100vh; |
|||
width: 100%; |
|||
flex-wrap: nowrap; |
|||
} |
|||
.left-com,.right-com{ |
|||
position:relative; |
|||
z-index:8; |
|||
} |
|||
|
|||
</style> |
@ -0,0 +1,124 @@ |
|||
<!-- |
|||
*@描述: 左侧组件 |
|||
*@作者: |
|||
*@日期: 9/27 |
|||
*@版本:1.0 |
|||
*/ |
|||
--> |
|||
<template> |
|||
<a-row class="com-left-menu-header"> |
|||
<span>工具栏</span> |
|||
</a-row> |
|||
<hr style="border:1px solid #00ffe4;" width="100%" /> |
|||
<a-row class="com-left-menu"> |
|||
|
|||
<a-tabs v-model:activeKey="activeKey" :tab-position="'left'"> |
|||
<a-tab-pane key="1"> |
|||
<template #tab> |
|||
<!-- <span class="icon iconfont icon-gongneng iconfontsize"></span> --> |
|||
<div class="menu-title"> |
|||
功能 |
|||
</div> |
|||
</template> |
|||
<Basic /> |
|||
</a-tab-pane> |
|||
<a-tab-pane key="2"> |
|||
<template #tab> |
|||
<!-- <span class="icon iconfont icon-chanpinmoxing iconfontsize"></span> --> |
|||
<div class="menu-title"> |
|||
3D模型 |
|||
</div> |
|||
</template> |
|||
<Models3D /> |
|||
</a-tab-pane> |
|||
<a-tab-pane key="3"> |
|||
<template #tab> |
|||
<!-- <span class="icon iconfont icon-chanpinmoxing iconfontsize"></span> --> |
|||
<div class="menu-title"> |
|||
图片<br />模型 |
|||
</div> |
|||
</template> |
|||
<ModelsImages /> |
|||
</a-tab-pane> |
|||
<a-tab-pane key="4"> |
|||
<template #tab> |
|||
<!-- <span class="icon iconfont icon-shengchan iconfontsize"></span> --> |
|||
<div class="menu-title"> |
|||
工具 |
|||
</div> |
|||
</template> |
|||
<Tools /> |
|||
</a-tab-pane> |
|||
<a-tab-pane key="5"> |
|||
<template #tab> |
|||
<!-- <span class="icon iconfont icon-shezhi1 iconfontsize"></span> --> |
|||
<div class="menu-title"> |
|||
系统<br />功能 |
|||
</div> |
|||
</template> |
|||
<SytemComponent /> |
|||
</a-tab-pane> |
|||
</a-tabs> |
|||
</a-row> |
|||
</template> |
|||
<script lang="ts" setup> |
|||
import { ref } from "vue"; |
|||
import Basic from "./leftMenuItem/basic.vue"; |
|||
import ModelsImages from "./leftMenuItem/modelsimages.vue"; |
|||
import Models3D from "./leftMenuItem/models3d.vue"; |
|||
import SytemComponent from "./leftMenuItem/sytemcomponent.vue"; |
|||
import Tools from "./leftMenuItem/tools.vue"; |
|||
let activeKey = ref('1') |
|||
</script> |
|||
<style lang="less" scoped> |
|||
.com-left-menu { |
|||
user-select: none; |
|||
height: 100vh; |
|||
background-color: #001529; |
|||
>div,:deep(.ant-tabs-tab-btn) { |
|||
width: 100%; |
|||
} |
|||
.menu-title{ |
|||
text-align: center; |
|||
} |
|||
} |
|||
|
|||
.com-left-menu-header { |
|||
padding-top: 10px; |
|||
|
|||
user-select: none; |
|||
height: 40px; |
|||
background-color: #001529; |
|||
text-align: center; |
|||
width: 100%; |
|||
border-width: 2px; |
|||
border-bottom: #fff; |
|||
|
|||
span { |
|||
margin-left: 40px; |
|||
width: 100%; |
|||
color: #fff; |
|||
font-size: 18px; |
|||
text-align: left; |
|||
} |
|||
} |
|||
|
|||
|
|||
:deep(.ant-tabs-tab) { |
|||
text-align: center !important; |
|||
padding: 12px 0 !important; |
|||
} |
|||
|
|||
:deep(.ant-tabs-left-bar) { |
|||
width: calc(6 / 24 * 100%); |
|||
} |
|||
|
|||
:deep(.ant-tabs-left-content.ant-tabs-content.ant-tabs-content-animated), |
|||
:deep(.ant-tabs-tabpane) { |
|||
padding-left: 0 !important; |
|||
} |
|||
|
|||
.iconfontsize { |
|||
font-size: 28px; |
|||
} |
|||
</style> |
@ -0,0 +1,77 @@ |
|||
<template> |
|||
<div class="left-com-content"> |
|||
<a-row> |
|||
<a-col class="content-item" title="添加标签" @click="addLabel"> |
|||
<a class="btn btnColor"> |
|||
<i class="fa"> |
|||
<span class="icon iconfont icon-biaoqian"></span> |
|||
</i> |
|||
</a> |
|||
<div class="item-text">添加标签</div> |
|||
</a-col> |
|||
<div style="background: aqua; height: 50%"> |
|||
<a-tree style="background: aqua;" :tree-data="treeData" @select="selectTreeNode" /> |
|||
</div> |
|||
</a-row> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang='ts'> |
|||
import { audio } from "@/utils/audio"; |
|||
import { useStore } from '@/store/index'; |
|||
import {storeToRefs} from 'pinia'; |
|||
import type { TreeProps } from 'ant-design-vue'; |
|||
import * as cameraApi from '@/axios/cameraBase/cameraApi'; |
|||
import { ref,onMounted } from 'vue'; |
|||
let piniaStore = useStore(); |
|||
let cameraMap = new Map<string, any>() |
|||
let treeData = ref<TreeProps['treeData']>([]) |
|||
let curSelectKey=storeToRefs(piniaStore).curSelectKey |
|||
function loadTreeData() { |
|||
cameraApi.GetList().then((res: any) => { |
|||
let list: Array<any> = res.data.data; |
|||
console.log(list); |
|||
|
|||
cameraMap = piniaStore.addCameraMap(list) |
|||
treeData.value=piniaStore.treeData |
|||
console.log(cameraMap); |
|||
}); |
|||
} |
|||
onMounted(()=>{ |
|||
loadTreeData() |
|||
}) |
|||
function selectTreeNode(selectedKeys: any, e: any) { |
|||
if (!e.selected) return; |
|||
let key = selectedKeys[0]; |
|||
if (curSelectKey.value !== key) { |
|||
piniaStore.updateCurSelectKey(key) |
|||
} |
|||
} |
|||
//播放音效 |
|||
function playAudio(): void { |
|||
audio.click(); |
|||
} |
|||
//添加标签 |
|||
function addLabel(e: string) { |
|||
playAudio(); |
|||
} |
|||
|
|||
|
|||
</script> |
|||
|
|||
<style lang='less'> |
|||
.left-com-content { |
|||
.content-item { |
|||
cursor: pointer; |
|||
text-align: center; |
|||
padding: 5px 0; |
|||
margin: 1%; |
|||
width: 48%; |
|||
|
|||
.fa span { |
|||
font-size: 25px; |
|||
|
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,128 @@ |
|||
<template> |
|||
|
|||
<div class="left-com-content"> |
|||
|
|||
<a-collapse v-model:activeKey="activeKey" accordion> |
|||
<a-collapse-panel v-for="item in header" :key="item.id" :header="item.group_name"> |
|||
<div class="img-box"> |
|||
<div class="img" v-for="ele in item.itemArr" @click="addModelFn(ele.url,ele.oldUrl)"> |
|||
<img draggable="false" class="collapse-panel-img" width="36" height="36" :src="ele.dis_img" |
|||
:alt="ele.model_name"> |
|||
</div> |
|||
</div> |
|||
</a-collapse-panel> |
|||
</a-collapse> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
import api from "@/axios/api"; |
|||
import { useStore } from '@/store/index'; |
|||
let piniaStore = useStore(); |
|||
const activeKey = ref([]); |
|||
let header: any = ref([]) |
|||
//图片基础路径 |
|||
let baseUrl = process.env.VUE_APP_API_BASE_URL + "/sys/common/static/";// api base_urll |
|||
let p1 = api.modelsinfo.getModels3DGroup( ).then(res => |
|||
{ |
|||
return res.data; |
|||
}) |
|||
|
|||
let p2 = api.modelsinfo.getModels3D().then(res => |
|||
{ |
|||
|
|||
return res.data; |
|||
}) |
|||
Promise.all([p1, p2]).then(res => |
|||
{ |
|||
let headerArr = res[0] |
|||
let itemArr = res[1] |
|||
//循环折叠标题数组 |
|||
headerArr.forEach((element: any, index: number) => |
|||
{ |
|||
//每个折叠标题添加一个存储子元素的数组 |
|||
element.itemArr = [] |
|||
header.value.push(element) |
|||
itemArr?.forEach((item: any) => |
|||
{ |
|||
|
|||
//判断图片是否为该标题所属 |
|||
if (element.group_name == item.model_group_name) |
|||
{ |
|||
item.oldUrl = item.url |
|||
item.url = baseUrl+item.url |
|||
item.dis_img = baseUrl + item.previewurl |
|||
header.value[index].itemArr.push(item) |
|||
} |
|||
}) |
|||
}); |
|||
|
|||
}) |
|||
let cus: any = null; |
|||
function addModelFn (url: string,img:string): void |
|||
{ |
|||
cus.creating = true |
|||
cus.img = img |
|||
piniaStore.editType = 30; |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang='less'> |
|||
.left-com-content { |
|||
.content-item { |
|||
cursor: pointer; |
|||
|
|||
padding: 5px 0; |
|||
margin: 1%; |
|||
width: 48%; |
|||
|
|||
.fa span { |
|||
font-size: 25px; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
:deep(.ant-collapse-content-box) { |
|||
padding: 16px 10px; |
|||
} |
|||
|
|||
.img-box { |
|||
text-align: left; |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
|
|||
.img { |
|||
flex-basis: 1 / 3 * 100%; |
|||
text-align: center; |
|||
|
|||
.collapse-panel-img { |
|||
padding: 2px; |
|||
|
|||
&:hover { |
|||
border: 1px solid #001529; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
:deep(.ant-collapse) { |
|||
background-color: #001529; |
|||
border: #fff; |
|||
|
|||
|
|||
.ant-collapse-item { |
|||
|
|||
.ant-collapse-header { |
|||
color: #fff !important; |
|||
|
|||
span { |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
</style> |
@ -0,0 +1,118 @@ |
|||
<template> |
|||
|
|||
<div class="left-com-content"> |
|||
|
|||
<a-collapse v-model:activeKey="activeKey" accordion> |
|||
<a-collapse-panel v-for="item in header" :key="item.id" :header="item.group_name"> |
|||
<div class="img-box"> |
|||
<div class="img" v-for="ele in item.itemArr" @click="addImage(ele.dis_img,ele.url)"> |
|||
<img draggable="false" class="collapse-panel-img" width="36" height="36" :src="ele.dis_img" |
|||
:alt="ele.model_name"> |
|||
</div> |
|||
</div> |
|||
</a-collapse-panel> |
|||
</a-collapse> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
|
|||
import { ref } from 'vue'; |
|||
import api from "@/axios/api"; |
|||
import { useStore } from '@/store/index'; |
|||
let piniaStore = useStore(); |
|||
const activeKey = ref([]); |
|||
let header: any = ref([]) |
|||
//图片基础路径 |
|||
let baseUrl = process.env.VUE_APP_API_BASE_URL + "/sys/common/static/"; // api base_urll |
|||
let p1 = api.modelsinfo.getModelsImageGroup().then(res => { |
|||
return res.data; |
|||
}) |
|||
let p2 = api.modelsinfo.getModelsImage().then(res => { |
|||
|
|||
return res.data; |
|||
}) |
|||
Promise.all([p1, p2]).then(res => { |
|||
let headerArr = res[0] |
|||
let itemArr = res[1] |
|||
//循环折叠标题数组 |
|||
headerArr.forEach((element: any, index: number) => { |
|||
//每个折叠标题添加一个存储子元素的数组 |
|||
element.itemArr = [] |
|||
header.value.push(element) |
|||
itemArr?.forEach((item: any) => { |
|||
//判断图片是否为该标题所属 |
|||
if (element.group_name == item.model_group_name) { |
|||
item.dis_img = baseUrl + item.url |
|||
header.value[index].itemArr.push(item) |
|||
} |
|||
}) |
|||
}); |
|||
|
|||
}) |
|||
let cus: any = null; |
|||
function addImage(imgstr: string,img:string): void { |
|||
cus.creating = true |
|||
cus.img = img |
|||
piniaStore.editType = 2; |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang='less'> |
|||
.left-com-content { |
|||
.content-item { |
|||
cursor: pointer; |
|||
|
|||
padding: 5px 0; |
|||
margin: 1%; |
|||
width: 48%; |
|||
|
|||
.fa span { |
|||
font-size: 25px; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
:deep(.ant-collapse-content-box) { |
|||
padding: 16px 10px; |
|||
} |
|||
|
|||
.img-box { |
|||
text-align: left; |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
|
|||
.img { |
|||
flex-basis: 1 / 3 * 100%; |
|||
text-align: center; |
|||
|
|||
.collapse-panel-img { |
|||
padding: 2px; |
|||
|
|||
&:hover { |
|||
border: 1px solid #001529; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
:deep(.ant-collapse) { |
|||
background-color: #001529; |
|||
border: #fff; |
|||
|
|||
|
|||
.ant-collapse-item { |
|||
|
|||
.ant-collapse-header { |
|||
color: #fff !important; |
|||
|
|||
span { |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
</style> |
@ -0,0 +1,89 @@ |
|||
<template> |
|||
<div class="left-com-content"> |
|||
<a-row> |
|||
<a-col class="content-item" @click="haplochromatizationListEvent" title="单体化列表"> |
|||
<a class="btn btnColor"> |
|||
<i class="fa"> |
|||
<span class="icon iconfont icon-moxing-miaobian"></span> |
|||
</i> |
|||
</a> |
|||
<div class="item-text">单体化列表</div> |
|||
</a-col> |
|||
<a-col class="content-item" @click="addhaplochromatization" title="单体化"> |
|||
|
|||
<a class="btn btnColor"> |
|||
<i class="fa"> |
|||
<span class="icon iconfont icon-sanweimoxing"></span> |
|||
</i> |
|||
</a> |
|||
<div class="item-text">单体化</div> |
|||
</a-col> |
|||
<a-col class="content-item" title="防区列表" @click="fenceListFn"> |
|||
<a class="btn btnColor"> |
|||
<i class="fa"> |
|||
<span class="icon iconfont icon-liebiao2"></span> |
|||
</i> |
|||
</a> |
|||
<div class="item-text">防区列表</div> |
|||
</a-col> |
|||
<a-col class="content-item" title="添加防区" @click="addDefenceArea"> |
|||
<a class="btn btnColor"> |
|||
<i class="fa"> |
|||
<span class="icon iconfont icon-24gl-fence"></span> |
|||
</i> |
|||
</a> |
|||
<div class="item-text">添加防区</div> |
|||
</a-col> |
|||
</a-row> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang='ts'> |
|||
import { useStore } from '@/store/index'; |
|||
import { audio } from "@/utils/audio"; |
|||
let piniaStore = useStore(); |
|||
|
|||
|
|||
|
|||
|
|||
function haplochromatizationListEvent(): void { |
|||
playAudio(); |
|||
} |
|||
|
|||
function addhaplochromatization(): void { |
|||
piniaStore.editType = 501 |
|||
} |
|||
function fenceListFn() { |
|||
playAudio(); |
|||
} |
|||
//防区 |
|||
function addDefenceArea(): void { |
|||
playAudio(); |
|||
piniaStore.editType = 502 |
|||
} |
|||
//播放音效 |
|||
function playAudio(): void { |
|||
audio.click(); |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
</script> |
|||
|
|||
<style scoped lang='less'> |
|||
.left-com-content { |
|||
.content-item { |
|||
cursor: pointer; |
|||
|
|||
padding: 5px 0; |
|||
margin: 1%; |
|||
width: 48%; |
|||
|
|||
.fa span { |
|||
font-size: 25px; |
|||
|
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,100 @@ |
|||
<template> |
|||
<div class="left-com-content"> |
|||
<a-row> |
|||
|
|||
<a-col class="content-item" title="位置测量" @click="measureMent('POINT')"> |
|||
<a class="btn btnColor"> |
|||
<i class="fa"> |
|||
<span class="icon iconfont icon-31dingwei"></span> |
|||
</i> |
|||
</a> |
|||
<div class="item-text">位置测量</div> |
|||
</a-col> |
|||
<a-col class="content-item" title="距离测量" @click="measureMent('SPACE_DISTANCE')"> |
|||
<a class="btn btnColor"> |
|||
<i class="fa"> |
|||
<span class="icon iconfont icon-chizi"></span> |
|||
</i> |
|||
</a> |
|||
<div class="item-text">距离测量</div> |
|||
</a-col> |
|||
<a-col class="content-item" title="三角测量" @click="measureMent('TRIANGLE_DISTANCE')"> |
|||
<a class="btn btnColor"> |
|||
<i class="fa"> |
|||
<span class="icon iconfont icon-celiangleixing"></span> |
|||
</i> |
|||
</a> |
|||
<div class="item-text">三角测量</div> |
|||
</a-col> |
|||
<a-col class="content-item" title="面积测量" @click="measureMent('SPACE_AREA')"> |
|||
<a class="btn btnColor"> |
|||
<i class="fa" style="font-size:35px"> |
|||
<span class="icon iconfont icon-mianjiceliang"></span> |
|||
</i> |
|||
</a> |
|||
<div class="item-text">面积测量</div> |
|||
</a-col> |
|||
|
|||
<a-col class="content-item" title="属性设置" @click="setProperties"> |
|||
|
|||
<a class="btn btnColor"> |
|||
<i class="fa"> |
|||
<span class="icon iconfont icon-shezhi"></span> |
|||
</i> |
|||
</a> |
|||
<div class="item-text">属性设置</div> |
|||
</a-col> |
|||
<a-col class="content-item" title="清空测量结果" @click="clearResults"> |
|||
<a class="btn btnColor"> |
|||
<i class="fa"> |
|||
<span class="icon iconfont icon-Eliminate" style="color: #D35400;"></span> |
|||
</i> |
|||
</a> |
|||
<div class="item-text" style="color: #D35400;">清空测量</div> |
|||
</a-col> |
|||
</a-row> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang='ts'> |
|||
import { ref, h, onMounted, getCurrentInstance, ComponentInternalInstance } from "vue"; |
|||
import { audio } from "@/utils/audio"; |
|||
let measureMentType = ref("NONE"); |
|||
const { proxy } = getCurrentInstance() as ComponentInternalInstance; |
|||
onMounted(() => { |
|||
}) |
|||
|
|||
//播放音效 |
|||
function playAudio(): void { |
|||
audio.click(); |
|||
} |
|||
//属性设置 |
|||
function setProperties(): void { |
|||
playAudio(); |
|||
} |
|||
|
|||
//清除测量结果 |
|||
function clearResults(): void { |
|||
measureMentType.value = "NONE"; |
|||
} |
|||
function measureMent(Type: string) { |
|||
measureMentType.value = Type; |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang='less'> |
|||
.left-com-content { |
|||
.content-item { |
|||
cursor: pointer; |
|||
|
|||
padding: 5px 0; |
|||
margin: 1%; |
|||
width: 48%; |
|||
|
|||
.fa span { |
|||
font-size: 25px; |
|||
|
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,40 @@ |
|||
<!-- |
|||
*@描述: 右侧面板 基本信息-数据绑定 |
|||
*@作者: |
|||
*@日期: |
|||
*@版本:1.0 |
|||
*/ |
|||
--> |
|||
<template> |
|||
<div class="rightMenu-class"> |
|||
<a-tabs v-model:activeKey="activeKey"> |
|||
<a-tab-pane key="1" tab="首页"> |
|||
暂无信息 |
|||
</a-tab-pane> |
|||
<a-tab-pane key="2" tab="首页2"> |
|||
暂无信息2 |
|||
</a-tab-pane> |
|||
</a-tabs> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import { ref } from 'vue' |
|||
let activeKey = ref('1') |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.rightMenu-class { |
|||
user-select: none; |
|||
overflow: auto; |
|||
height: 100vh; |
|||
background-color: #001529; |
|||
:deep(.ant-tabs-nav-list) { |
|||
margin: 0 auto; |
|||
padding: 0 20px; |
|||
} |
|||
:deep(.ant-tabs-tabpane){ |
|||
text-align: center; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,16 @@ |
|||
import { createApp } from 'vue' |
|||
import './style.css' |
|||
import App from './App.vue' |
|||
import router from "./router/index" |
|||
import 'ant-design-vue/dist/antd.css'; |
|||
import { createPinia } from 'pinia'; |
|||
import Antd from 'ant-design-vue'; |
|||
const pinia = createPinia() |
|||
|
|||
|
|||
|
|||
const app=createApp(App) |
|||
app.use(router) |
|||
app.use(pinia) |
|||
app.use(Antd) |
|||
app.mount('#app') |
@ -0,0 +1,7 @@ |
|||
import { createRouter, createWebHistory } from "vue-router" |
|||
import routes from "./routes" |
|||
var router=createRouter({ |
|||
history:createWebHistory(), |
|||
routes |
|||
}) |
|||
export default router |
@ -0,0 +1,8 @@ |
|||
const routes = [ |
|||
{ |
|||
name: "", |
|||
path: "/", |
|||
component: () => import("../views/index.vue") |
|||
} |
|||
] |
|||
export default routes; |
@ -0,0 +1,35 @@ |
|||
import { defineStore } from 'pinia'; |
|||
export const useStore = defineStore('Index', { |
|||
state: () => { |
|||
return { |
|||
curSelectKey:"", |
|||
cameraMap:new Map<string, any>(), |
|||
treeData:<any[]>[], |
|||
audioCount: 0, |
|||
audioCurrentCount: 0, |
|||
editType:0 |
|||
} |
|||
}, |
|||
|
|||
// 也可以定义为
|
|||
// state: () => ({ count: 0 })
|
|||
actions: { |
|||
addCameraMap(list:any){ |
|||
this.treeData=[] |
|||
for (let item of list) { |
|||
this.cameraMap.set(item.id, item); |
|||
this.treeData.push({title: item.ip, key: item.id}); |
|||
} |
|||
return this.cameraMap |
|||
}, |
|||
updateCurSelectKey(key:string){ |
|||
this.curSelectKey=key |
|||
}, |
|||
updateaudioCurrentCount(num: number) { |
|||
this.audioCurrentCount = num |
|||
}, |
|||
updateaudioCount(num: number) { |
|||
this.audioCount = num |
|||
}, |
|||
}, |
|||
}) |
@ -0,0 +1,15 @@ |
|||
|
|||
|
|||
body { |
|||
margin: 0; |
|||
min-width: 320px; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
h1 { |
|||
font-size: 3.2em; |
|||
line-height: 1.1; |
|||
} |
|||
hr{ |
|||
margin: 0; |
|||
} |
@ -0,0 +1,2 @@ |
|||
declare module 'mqtt/dist/mqtt.min' |
|||
declare module 'qs' |
@ -0,0 +1,39 @@ |
|||
import { nextTick } from 'vue' |
|||
import { useStore } from '@/store/index'; |
|||
import pop from '@/assets/audio/pop.wav' |
|||
import police from '@/assets/audio/police.wav' |
|||
import excessive from '@/assets/audio/excessive.mp3' |
|||
function audioPlay (url: any, count: number = 1) |
|||
{ |
|||
let store = useStore() |
|||
store.updateaudioCurrentCount(0) |
|||
store.updateaudioCount(count) |
|||
nextTick(() => |
|||
{ |
|||
let audio_: any = document.querySelector(".audio"); |
|||
audio_.src = url |
|||
audio_.play(); |
|||
}) |
|||
} |
|||
|
|||
|
|||
class AudioUnit |
|||
{ |
|||
//播放音效
|
|||
click (): void |
|||
{ |
|||
audioPlay(pop, 1); |
|||
} |
|||
|
|||
warning (): void |
|||
{ |
|||
audioPlay(police, 1); |
|||
} |
|||
fly (): void |
|||
{ |
|||
|
|||
audioPlay(excessive, 1); |
|||
} |
|||
} |
|||
export const audio: AudioUnit = new AudioUnit(); |
|||
|
@ -0,0 +1,10 @@ |
|||
import axios from "axios"; |
|||
|
|||
|
|||
const service = axios.create({ |
|||
timeout: 9000, // 请求超时时间
|
|||
}); |
|||
|
|||
export { |
|||
service as axios, |
|||
} |
@ -0,0 +1,9 @@ |
|||
import { message } from "ant-design-vue"; |
|||
const Msg = message; |
|||
//message全局配置
|
|||
Msg.config({ |
|||
top: '200px', |
|||
duration: 3, |
|||
maxCount: 3, |
|||
}) |
|||
export default Msg; |
@ -0,0 +1,68 @@ |
|||
import * as mqtt from "mqtt/dist/mqtt.min"; |
|||
const host = process.env.VUE_APP_MQTT_HOST || "127.0.0.1"; |
|||
const port = process.env.VUE_APP_MQTT_PORT || 8083; |
|||
const options = {//mqtt配置
|
|||
// port: 8083,//连接端口
|
|||
protocolId: 'MQTT', |
|||
connectTimeout: 4000,//超时时间
|
|||
clientID: "mqtt_" + Math.random().toString(16).substr(2, 8), |
|||
username: 'admin', |
|||
password: 'public', |
|||
}; |
|||
|
|||
// 订阅的频道和事件
|
|||
const topics: string = process.env.VUE_APP_MQTT_TOPICS || "/server/warning/+/zdgl"; |
|||
const topicList = topics.split(',') |
|||
|
|||
export default class MqttUnit |
|||
{ |
|||
|
|||
connectUrl = `ws://${host}:${port}/mqtt` |
|||
client = mqtt.connect(this.connectUrl, options); |
|||
|
|||
mqttInit () |
|||
{ |
|||
// const connectUrl = `mqtt://${host}:${port}/mqtt`
|
|||
|
|||
this.client.on('connect', () => |
|||
{ |
|||
console.log(`MQTT Connection ${this.connectUrl} succeeded!`) |
|||
// 不会订阅多个频道,所以遍历对象,循环监听多个频道
|
|||
for (let key of topicList) |
|||
{ |
|||
this.client.subscribe(key, (err: any) => |
|||
{ |
|||
if (!err) |
|||
{ |
|||
console.log(`订阅'${key}'成功`); |
|||
} |
|||
}); |
|||
} |
|||
}) |
|||
this.client.on('error', (error: any) => |
|||
{ |
|||
console.log('Connection failed', error) |
|||
}) |
|||
/** |
|||
* topic: 监听的频道 |
|||
* message: 返回的数据 |
|||
*/ |
|||
return this.client |
|||
// this.client.on('message', (topic: string, message: Uint8Array) => {
|
|||
// const utf8decoder = new TextDecoder();
|
|||
// const msgString = utf8decoder.decode(message);
|
|||
// if (topic.startsWith("/server/warning")) {
|
|||
// // $mitt.emit("warnInfo", msgString);
|
|||
// console.log(msgString);
|
|||
|
|||
// }
|
|||
// })
|
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
@ -0,0 +1,317 @@ |
|||
export let WebRtcStreamer = function () { |
|||
|
|||
/** |
|||
* Interface with WebRTC-streamer API |
|||
* @constructor |
|||
* @param {string} videoElement - id of the video element tag |
|||
* @param {string} srvurl - url of webrtc-streamer (default is current location) |
|||
*/ |
|||
var WebRtcStreamer = function WebRtcStreamer(videoElement, srvurl) { |
|||
if (typeof videoElement === "string") { |
|||
this.videoElement = document.getElementById(videoElement); |
|||
} else { |
|||
this.videoElement = videoElement; |
|||
} |
|||
this.srvurl = srvurl || location.protocol + "//" + window.location.hostname + ":" + window.location.port; |
|||
this.pc = null; |
|||
|
|||
this.mediaConstraints = {offerToReceiveAudio: true, offerToReceiveVideo: true}; |
|||
|
|||
this.iceServers = null; |
|||
this.earlyCandidates = []; |
|||
} |
|||
|
|||
WebRtcStreamer.prototype._handleHttpErrors = function (response) { |
|||
if (!response.ok) { |
|||
throw Error(response.statusText); |
|||
} |
|||
return response; |
|||
} |
|||
|
|||
/** |
|||
* Connect a WebRTC Stream to videoElement |
|||
* @param {string} videourl - id of WebRTC video stream |
|||
* @param {string} audiourl - id of WebRTC audio stream |
|||
* @param {string} options - options of WebRTC call |
|||
* @param {string} stream - local stream to send |
|||
*/ |
|||
WebRtcStreamer.prototype.connect = function (videourl, audiourl, options, localstream) { |
|||
this.disconnect(); |
|||
|
|||
// getIceServers is not already received
|
|||
if (!this.iceServers) { |
|||
console.log("Get IceServers"); |
|||
|
|||
fetch(this.srvurl + "/api/getIceServers") |
|||
.then(this._handleHttpErrors) |
|||
.then((response) => (response.json())) |
|||
.then((response) => this.onReceiveGetIceServers.call(this, response, videourl, audiourl, options, localstream)) |
|||
.catch((error) => this.onError("getIceServers " + error)) |
|||
|
|||
} else { |
|||
this.onReceiveGetIceServers(this.iceServers, videourl, audiourl, options, localstream); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Disconnect a WebRTC Stream and clear videoElement source |
|||
*/ |
|||
WebRtcStreamer.prototype.disconnect = function () { |
|||
if (this.videoElement?.srcObject) { |
|||
this.videoElement.srcObject.getTracks().forEach(track => { |
|||
track.stop() |
|||
this.videoElement.srcObject.removeTrack(track); |
|||
}); |
|||
} |
|||
if (this.pc) { |
|||
fetch(this.srvurl + "/api/hangup?peerid=" + this.pc.peerid) |
|||
.then(this._handleHttpErrors) |
|||
.catch((error) => this.onError("hangup " + error)) |
|||
|
|||
|
|||
try { |
|||
this.pc.close(); |
|||
} catch (e) { |
|||
console.log("Failure close peer connection:" + e); |
|||
} |
|||
this.pc = null; |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* GetIceServers callback |
|||
*/ |
|||
WebRtcStreamer.prototype.onReceiveGetIceServers = function (iceServers, videourl, audiourl, options, stream) { |
|||
this.iceServers = iceServers; |
|||
this.pcConfig = iceServers || {"iceServers": []}; |
|||
try { |
|||
this.createPeerConnection(); |
|||
|
|||
var callurl = this.srvurl + "/api/call?peerid=" + this.pc.peerid + "&url=" + encodeURIComponent(videourl); |
|||
if (audiourl) { |
|||
callurl += "&audiourl=" + encodeURIComponent(audiourl); |
|||
} |
|||
if (options) { |
|||
callurl += "&options=" + encodeURIComponent(options); |
|||
} |
|||
|
|||
if (stream) { |
|||
this.pc.addStream(stream); |
|||
} |
|||
|
|||
// clear early candidates
|
|||
this.earlyCandidates.length = 0; |
|||
|
|||
// create Offer
|
|||
var bind = this; |
|||
this.pc.createOffer(this.mediaConstraints).then(function (sessionDescription) { |
|||
console.log("Create offer:" + JSON.stringify(sessionDescription)); |
|||
|
|||
bind.pc.setLocalDescription(sessionDescription |
|||
, function () { |
|||
fetch(callurl, {method: "POST", body: JSON.stringify(sessionDescription)}) |
|||
.then(bind._handleHttpErrors) |
|||
.then((response) => (response.json())) |
|||
.catch((error) => bind.onError("call " + error)) |
|||
.then((response) => bind.onReceiveCall.call(bind, response)) |
|||
.catch((error) => bind.onError("call " + error)) |
|||
|
|||
} |
|||
, function (error) { |
|||
console.log("setLocalDescription error:" + JSON.stringify(error)); |
|||
}); |
|||
|
|||
}, function (error) { |
|||
alert("Create offer error:" + JSON.stringify(error)); |
|||
}); |
|||
|
|||
} catch (e) { |
|||
this.disconnect(); |
|||
alert("connect error: " + e); |
|||
} |
|||
} |
|||
|
|||
|
|||
WebRtcStreamer.prototype.getIceCandidate = function () { |
|||
fetch(this.srvurl + "/api/getIceCandidate?peerid=" + this.pc.peerid) |
|||
.then(this._handleHttpErrors) |
|||
.then((response) => (response.json())) |
|||
.then((response) => this.onReceiveCandidate.call(this, response)) |
|||
.catch((error) => bind.onError("getIceCandidate " + error)) |
|||
} |
|||
|
|||
/* |
|||
* create RTCPeerConnection |
|||
*/ |
|||
WebRtcStreamer.prototype.createPeerConnection = function () { |
|||
console.log("createPeerConnection config: " + JSON.stringify(this.pcConfig)); |
|||
this.pc = new RTCPeerConnection(this.pcConfig); |
|||
var pc = this.pc; |
|||
pc.peerid = Math.random(); |
|||
|
|||
var bind = this; |
|||
pc.onicecandidate = function (evt) { |
|||
bind.onIceCandidate.call(bind, evt); |
|||
}; |
|||
pc.onaddstream = function (evt) { |
|||
bind.onAddStream.call(bind, evt); |
|||
}; |
|||
pc.oniceconnectionstatechange = function (evt) { |
|||
console.log("oniceconnectionstatechange state: " + pc.iceConnectionState); |
|||
if (bind.videoElement) { |
|||
if (pc.iceConnectionState === "connected") { |
|||
bind.videoElement.style.opacity = "1.0"; |
|||
} else if (pc.iceConnectionState === "disconnected") { |
|||
bind.videoElement.style.opacity = "0.25"; |
|||
} else if ((pc.iceConnectionState === "failed") || (pc.iceConnectionState === "closed")) { |
|||
bind.videoElement.style.opacity = "0.5"; |
|||
} else if (pc.iceConnectionState === "new") { |
|||
bind.getIceCandidate.call(bind) |
|||
} |
|||
} |
|||
} |
|||
pc.ondatachannel = function (evt) { |
|||
console.log("remote datachannel created:" + JSON.stringify(evt)); |
|||
|
|||
evt.channel.onopen = function () { |
|||
console.log("remote datachannel open"); |
|||
this.send("remote channel openned"); |
|||
} |
|||
evt.channel.onmessage = function (event) { |
|||
console.log("remote datachannel recv:" + JSON.stringify(event.data)); |
|||
} |
|||
} |
|||
pc.onicegatheringstatechange = function () { |
|||
if (pc.iceGatheringState === "complete") { |
|||
const recvs = pc.getReceivers(); |
|||
|
|||
recvs.forEach((recv) => { |
|||
if (recv.track && recv.track.kind === "video") { |
|||
console.log("codecs:" + JSON.stringify(recv.getParameters().codecs)) |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
try { |
|||
var dataChannel = pc.createDataChannel("ClientDataChannel"); |
|||
dataChannel.onopen = function () { |
|||
console.log("local datachannel open"); |
|||
this.send("local channel openned"); |
|||
} |
|||
dataChannel.onmessage = function (evt) { |
|||
console.log("local datachannel recv:" + JSON.stringify(evt.data)); |
|||
} |
|||
} catch (e) { |
|||
console.log("Cannor create datachannel error: " + e); |
|||
} |
|||
|
|||
console.log("Created RTCPeerConnnection with config: " + JSON.stringify(this.pcConfig)); |
|||
return pc; |
|||
} |
|||
|
|||
|
|||
/* |
|||
* RTCPeerConnection IceCandidate callback |
|||
*/ |
|||
WebRtcStreamer.prototype.onIceCandidate = function (event) { |
|||
if (event.candidate) { |
|||
if (this.pc.currentRemoteDescription) { |
|||
this.addIceCandidate(this.pc.peerid, event.candidate); |
|||
} else { |
|||
this.earlyCandidates.push(event.candidate); |
|||
} |
|||
} else { |
|||
console.log("End of candidates."); |
|||
} |
|||
} |
|||
|
|||
|
|||
WebRtcStreamer.prototype.addIceCandidate = function (peerid, candidate) { |
|||
fetch(this.srvurl + "/api/addIceCandidate?peerid=" + peerid, {method: "POST", body: JSON.stringify(candidate)}) |
|||
.then(this._handleHttpErrors) |
|||
.then((response) => (response.json())) |
|||
.then((response) => { |
|||
console.log("addIceCandidate ok:" + response) |
|||
}) |
|||
.catch((error) => this.onError("addIceCandidate " + error)) |
|||
} |
|||
|
|||
/* |
|||
* RTCPeerConnection AddTrack callback |
|||
*/ |
|||
WebRtcStreamer.prototype.onAddStream = function (event) { |
|||
console.log("Remote track added:" + JSON.stringify(event)); |
|||
|
|||
this.videoElement.srcObject = event.stream; |
|||
var promise = this.videoElement.play(); |
|||
if (promise !== undefined) { |
|||
var bind = this; |
|||
promise.catch(function (error) { |
|||
console.warn("error:" + error); |
|||
bind.videoElement.setAttribute("controls", true); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* AJAX /call callback |
|||
*/ |
|||
WebRtcStreamer.prototype.onReceiveCall = function (dataJson) { |
|||
var bind = this; |
|||
console.log("offer: " + JSON.stringify(dataJson)); |
|||
var descr = new RTCSessionDescription(dataJson); |
|||
this.pc.setRemoteDescription(descr |
|||
, function () { |
|||
console.log("setRemoteDescription ok"); |
|||
while (bind.earlyCandidates.length) { |
|||
var candidate = bind.earlyCandidates.shift(); |
|||
bind.addIceCandidate.call(bind, bind.pc.peerid, candidate); |
|||
} |
|||
|
|||
bind.getIceCandidate.call(bind) |
|||
} |
|||
, function (error) { |
|||
console.log("setRemoteDescription error:" + JSON.stringify(error)); |
|||
}); |
|||
} |
|||
|
|||
/* |
|||
* AJAX /getIceCandidate callback |
|||
*/ |
|||
WebRtcStreamer.prototype.onReceiveCandidate = function (dataJson) { |
|||
console.log("candidate: " + JSON.stringify(dataJson)); |
|||
if (dataJson) { |
|||
for (var i = 0; i < dataJson.length; i++) { |
|||
var candidate = new RTCIceCandidate(dataJson[i]); |
|||
|
|||
console.log("Adding ICE candidate :" + JSON.stringify(candidate)); |
|||
this.pc.addIceCandidate(candidate |
|||
, function () { |
|||
console.log("addIceCandidate OK"); |
|||
} |
|||
, function (error) { |
|||
console.log("addIceCandidate error:" + JSON.stringify(error)); |
|||
}); |
|||
} |
|||
this.pc.addIceCandidate(); |
|||
} |
|||
} |
|||
|
|||
|
|||
/* |
|||
* AJAX callback for Error |
|||
*/ |
|||
WebRtcStreamer.prototype.onError = function (status) { |
|||
console.log("onError:" + status); |
|||
} |
|||
|
|||
return WebRtcStreamer; |
|||
}; |
|||
|
|||
if (typeof window !== 'undefined' && typeof window.document !== 'undefined') { |
|||
window.WebRtcStreamer = WebRtcStreamer; |
|||
} |
|||
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { |
|||
module.exports = WebRtcStreamer; |
|||
} |
@ -0,0 +1,34 @@ |
|||
<!-- |
|||
*@描述: 主页 |
|||
*@作者: |
|||
*@日期: |
|||
*@版本:1.0 |
|||
*/ |
|||
--> |
|||
<template> |
|||
<ComSetup> |
|||
<template v-slot:default> |
|||
<!-- 主体内容 --> |
|||
<div class="com-body"> |
|||
<CameraCenter></CameraCenter> |
|||
</div> |
|||
</template> |
|||
</ComSetup> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import ComSetup from "@/components/aside/index.vue"; |
|||
import CameraCenter from "@/views/page/cameraCenter.vue"; |
|||
|
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.com-body { |
|||
width: 100%; |
|||
height: 100%; |
|||
position: relative; |
|||
margin: auto; |
|||
color: black; |
|||
} |
|||
</style> |
|||
|
@ -0,0 +1,297 @@ |
|||
<template> |
|||
|
|||
<div id="videoWrapper"> |
|||
<canvas ref="videoCanvas" id="videoCanvas" :width="canvasWidth" :height="canvasHeight"> |
|||
</canvas> |
|||
<video ref="videoPlayer" id="videoPlayer" autoplay muted |
|||
@loadedmetadata="changeVideoCanvasSize" @mouseover="mouseOverVideo" @mouseout="mouseOutVideo" |
|||
@mousedown="mouseDownVideo"> |
|||
</video> |
|||
<div v-for="item in labelList" class="labels" :key="item.id" :style="`top:${canvasHeight*item.canvasTopRatio}px;left:${canvasWidth*item.canvasLeftRatio}px`"> |
|||
<div class="labels-item" @click="test(item.id)"> |
|||
<!-- <div class="labels-item" v-if="item.inFlag"> --> |
|||
<img src="@/assets/images/dialog.png" width="80" height="50" alt=""> |
|||
<span>{{item.name}}</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang='ts'> |
|||
////@ts-nocheck |
|||
import { ref, h, onMounted, getCurrentInstance, ComponentInternalInstance, onUnmounted,watch } from "vue"; |
|||
import * as markLabelApi from '@/axios/cameraMark/markLabelApi'; |
|||
import * as markSearchApi from '@/axios/core/markSearchApi'; |
|||
import { useStore } from '@/store/index'; |
|||
import { apiUrl } from "@/axios"; |
|||
import {storeToRefs} from 'pinia'; |
|||
import Msg from "@/utils/message"; |
|||
const { proxy } = getCurrentInstance() as ComponentInternalInstance; |
|||
let piniaStore = useStore(); |
|||
let player = <HTMLVideoElement>document.querySelector('#videoPlayer') |
|||
let canvas = <HTMLCanvasElement>document.querySelector('#videoCanvas') |
|||
let canvasWidth = ref(0) |
|||
let canvasHeight = ref(0) |
|||
let curSelectKey = storeToRefs(piniaStore).curSelectKey |
|||
let cameraMap = storeToRefs(piniaStore).cameraMap |
|||
let webRtcServer: any = ref() |
|||
let isActiveChoose = ref(false) |
|||
let labelList=ref<any[]>([]) |
|||
onMounted(() => { |
|||
{ |
|||
player = <HTMLVideoElement>document.querySelector('#videoPlayer'); |
|||
canvas = <HTMLCanvasElement>document.querySelector('#videoCanvas'); |
|||
// loadTreeData(); |
|||
loadVideoPlayer(); |
|||
loadVideoCanvas(); |
|||
|
|||
} |
|||
}) |
|||
watch(curSelectKey,(newVal,oldVal)=>{ |
|||
switchCamera(newVal) |
|||
}) |
|||
function test(id:number){ |
|||
console.log(id,"id++++++++++++"); |
|||
|
|||
markLabelApi.Delete({id}).then((res:any)=>{ |
|||
console.log(res.data); |
|||
if(res.data.code==200){ |
|||
let msg=res.data.data==true?"删除成功":"删除失败" |
|||
if(res.data.data){ |
|||
labelList.value=labelList.value.filter((element:any) => { |
|||
return element.id!=id |
|||
}); |
|||
Msg.success("删除成功") |
|||
return |
|||
} |
|||
} |
|||
Msg.error("删除失败") |
|||
}) |
|||
} |
|||
function loadVideoPlayer() { |
|||
let elmId = 'videoPlayer'; |
|||
let url = apiUrl.WebRtcUrl; |
|||
webRtcServer.value = new WebRtcStreamer(elmId, url); |
|||
} |
|||
function loadVideoCanvas() { |
|||
window.addEventListener('resize', () => { |
|||
changeVideoCanvasSize(); |
|||
}); |
|||
} |
|||
function changeVideoCanvasSize() { |
|||
canvasWidth.value = player.clientWidth; |
|||
canvasHeight.value = player.clientHeight; |
|||
} |
|||
function mouseOverVideo() { |
|||
// console.log("移入"); |
|||
|
|||
if (!isActiveChoose.value) return; |
|||
player.classList.add('activeChoose'); |
|||
} |
|||
function mouseOutVideo() { |
|||
// console.log("移除"); |
|||
|
|||
if (!isActiveChoose) return; |
|||
player.classList.remove('activeChoose'); |
|||
} |
|||
function mouseDownVideo(e: MouseEvent) { |
|||
if (!isActiveChoose.value) return; |
|||
let cameraId = cameraMap.value.get(curSelectKey.value).id; |
|||
let name = 'markLabel-' + Date.now(); |
|||
let videoWidth = player.videoWidth | 1920; |
|||
let videoHeight = player.videoHeight | 1080; |
|||
let canvasWidth = player.clientWidth; |
|||
let canvasHeight = player.clientHeight; |
|||
let canvasLeft = e.offsetX; |
|||
let canvasTop = e.offsetY; |
|||
let canvasLeftRatio = canvasLeft / canvasWidth; |
|||
let canvasTopRatio = canvasTop / canvasHeight; |
|||
let entity = { |
|||
CbCameraId: cameraId, |
|||
Name: name, |
|||
VideoWidth: videoWidth, |
|||
VideoHeight: videoHeight, |
|||
CanvasLeftRatio: canvasLeftRatio, |
|||
CanvasTopRatio: canvasTopRatio |
|||
} |
|||
console.log(entity); |
|||
markLabelApi.AddReturnId({ |
|||
'entity': entity |
|||
}).then((res: any) => { |
|||
let markLabelId: number = res.data.data; |
|||
console.log(markLabelId); |
|||
|
|||
if (markLabelId <= 0) return; |
|||
markSearchApi.ActivateMarkLabel({ |
|||
'cameraId': cameraId, |
|||
'markLabelId': markLabelId |
|||
}).then((res: any) => { |
|||
let ret: boolean = res.data.data; |
|||
}); |
|||
}) |
|||
} |
|||
function activateChoose() { |
|||
if (curSelectKey.value.length == 0) return; |
|||
isActiveChoose.value = true; |
|||
} |
|||
function deactivateChoose() { |
|||
if (curSelectKey.value.length == 0) return; |
|||
isActiveChoose.value = false; |
|||
} |
|||
function switchCamera(cameraId: string) { |
|||
console.log('camera switch.'); |
|||
|
|||
// step1, get camera obj. |
|||
console.log('get camera obj.'); |
|||
let cameraObj = cameraMap.value.get(cameraId); |
|||
if (!cameraObj) { |
|||
console.log('camera obj not found.'); |
|||
return; |
|||
} |
|||
// object to proxy, ? |
|||
console.log(cameraObj, '111111'); |
|||
|
|||
// step2, get camera rtsp url. |
|||
console.log('get camera rtsp url.'); |
|||
let rtspUrl = getRtspUrl(cameraObj); |
|||
|
|||
// step3, connect webrtc-steamer. |
|||
console.log('connect webrtc-steamer.'); |
|||
webRtcServer.value.disconnect(); |
|||
webRtcServer.value.connect(rtspUrl); |
|||
|
|||
// step4, active camera searcher. |
|||
markSearchApi.IsExistsSearcher({ |
|||
'cameraId': cameraObj.id |
|||
}).then((res: any) => { |
|||
let flag: boolean = res.data.data; |
|||
if (!flag) { |
|||
console.log('not exist searcher.'); |
|||
markSearchApi.ActiveSearcher({ |
|||
'cameraId': cameraObj.id |
|||
}).then((res: any) => { |
|||
let flag: boolean = res.data.data; |
|||
console.log('activate searcher : ', flag); |
|||
if (flag) { |
|||
console.log('load camera labels'); |
|||
getLabel(cameraObj.id) |
|||
loadMarkLabelsByLoop(cameraObj); |
|||
|
|||
} |
|||
}); |
|||
} else { |
|||
console.log('load camera labels'); |
|||
getLabel(cameraObj.id) |
|||
loadMarkLabelsByLoop(cameraObj); |
|||
} |
|||
}) |
|||
|
|||
// step5, load camera labels. |
|||
} |
|||
function getRtspUrl(cameraObj: any): string { |
|||
// 模拟 onvif |
|||
return "rtsp://admin:hk123456@192.168.1.65:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1"; |
|||
} |
|||
function getLabel(cbCameraId:string|number){ |
|||
markLabelApi.GetList({cbCameraId}).then((res:any)=>{ |
|||
|
|||
if(res.data.code==200){ |
|||
console.log(res,'res'); |
|||
labelList.value=res.data.data |
|||
} |
|||
}) |
|||
} |
|||
function loadMarkLabelsByLoop(cameraObj: any, ms: number = 1000) { |
|||
if (curSelectKey.value !== cameraObj.id) { |
|||
console.log('load camera labels end.'); |
|||
return; |
|||
} |
|||
loadMarkLabels(cameraObj); |
|||
setTimeout(() => { |
|||
loadMarkLabelsByLoop(cameraObj, ms); |
|||
}, ms); |
|||
} |
|||
function loadMarkLabels(cameraObj: any) { |
|||
markSearchApi.GetMarkLabelCalcResultList({ |
|||
'cameraId': cameraObj.id |
|||
}).then((res: any) => { |
|||
let list: Array<any> = res.data.data; |
|||
list.forEach((element:any) => { |
|||
labelList.value.forEach((item:any,index:number) => { |
|||
if(element.id==item.id){ |
|||
labelList.value[index].canvasLeftRatio=element.canvasLeftRatio |
|||
labelList.value[index].canvasTopRatio=element.canvasTopRatio |
|||
labelList.value[index].inFlag=element.inFlag |
|||
|
|||
} |
|||
|
|||
}); |
|||
}); |
|||
// console.log(labelList); |
|||
|
|||
// drawMarkLabels(list); |
|||
}); |
|||
} |
|||
function drawMarkLabels(markLabels: Array<any>) { |
|||
let ctx = canvas.getContext('2d'); |
|||
if (!ctx) return; |
|||
ctx.clearRect(0, 0, canvasWidth.value, canvasHeight.value); |
|||
for (let markLabel of markLabels) { |
|||
if (!markLabel.inFlag) continue; |
|||
let x = Math.round(canvasWidth.value * markLabel.canvasLeftRatio); |
|||
let y = Math.round(canvasHeight.value * markLabel.canvasTopRatio); |
|||
x = canvasWidth.value * markLabel.canvasLeftRatio; |
|||
y = canvasHeight.value * markLabel.canvasTopRatio; |
|||
// console.log(markLabel); |
|||
ctx.beginPath(); |
|||
ctx.arc(x, y, 5, 0, 2 * Math.PI); |
|||
ctx.fillStyle = "red"; |
|||
ctx.fill(); |
|||
ctx.closePath(); |
|||
} |
|||
} |
|||
|
|||
onUnmounted(() => { |
|||
{ |
|||
console.log('beforeUnmount.'); |
|||
// 释放 webRtcServer |
|||
if (!webRtcServer.value) return; |
|||
console.log(123); |
|||
webRtcServer.value.disconnect(); |
|||
webRtcServer.value = null; |
|||
} |
|||
}) |
|||
</script> |
|||
|
|||
<style scoped lang='less'> |
|||
#videoWrapper { |
|||
height: 100vh; |
|||
position: relative; |
|||
overflow: hidden; |
|||
#videoCanvas { |
|||
position: absolute; |
|||
} |
|||
|
|||
#videoPlayer { |
|||
height: 100%; |
|||
width: 100%; |
|||
object-fit: fill; |
|||
} |
|||
.labels{ |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
transform: translate(-50%,-50%); |
|||
z-index: 2; |
|||
.labels-item{ |
|||
position: relative; |
|||
span{ |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 50%; |
|||
transform: translate(-50%,-50%); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,7 @@ |
|||
/// <reference types="vite/client" />
|
|||
|
|||
declare module '*.vue' { |
|||
import type { DefineComponent } from 'vue' |
|||
const component: DefineComponent<{}, {}, any> |
|||
export default component |
|||
} |
@ -0,0 +1,22 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"target": "ESNext", |
|||
"useDefineForClassFields": true, |
|||
"module": "ESNext", |
|||
"moduleResolution": "Node", |
|||
"strict": true, |
|||
"jsx": "preserve", |
|||
"resolveJsonModule": true, |
|||
"isolatedModules": false, |
|||
"esModuleInterop": true, |
|||
"lib": ["ESNext", "DOM"], |
|||
"skipLibCheck": true, |
|||
"noEmit": true, |
|||
"baseUrl": ".", |
|||
"paths": { |
|||
"@/*": ["src/*"] |
|||
} |
|||
}, |
|||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "src/utils/webrtc-streamer/webrtcstreamer.js"], |
|||
"references": [{ "path": "./tsconfig.node.json" }] |
|||
} |
@ -0,0 +1,9 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"composite": true, |
|||
"module": "ESNext", |
|||
"moduleResolution": "Node", |
|||
"allowSyntheticDefaultImports": true |
|||
}, |
|||
"include": ["vite.config.ts"] |
|||
} |
@ -0,0 +1,57 @@ |
|||
import { defineConfig } from 'vite' |
|||
import vue from '@vitejs/plugin-vue' |
|||
import {resolve} from 'path'; |
|||
import Components from 'unplugin-vue-components/vite' |
|||
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers' |
|||
|
|||
export default defineConfig({ |
|||
plugins: [vue(),Components({ |
|||
resolvers: [ |
|||
AntDesignVueResolver({ |
|||
importStyle: 'less', // 一定要开启这个配置项
|
|||
}), |
|||
], |
|||
}),], |
|||
|
|||
resolve:{ |
|||
//设置路径别名
|
|||
alias: { |
|||
'@': resolve(__dirname, './src'), |
|||
'*': resolve('') |
|||
}, |
|||
}, |
|||
define: { |
|||
'process.env': { |
|||
"VUE_APP_API_BASE_URL":"http://192.168.1.128:8080/military", |
|||
"VUE_APP_MQTT_HOST":"127.0.0.1", |
|||
"VUE_APP_MQTT_PORT":8083, |
|||
"VUE_APP_MQTT_TOPICS": "/server/warning/+/zdgl" |
|||
} |
|||
}, |
|||
server: { |
|||
host: true, // 类型:string | boolean 指定服务器应该监听哪个 IP 地址
|
|||
port: 3100, // 类型: number 指定服务器端口
|
|||
cors: true, // 类型: boolean | CorsOptions 为开发服务器配置 CORS。默认启用并允许任何源
|
|||
hmr : true, |
|||
// proxy: {
|
|||
// '^/api': {
|
|||
// target: 'http://192.168.1.119:800/api',
|
|||
// changeOrigin: true,
|
|||
// rewrite: (path) => path.replace(/^\/api/, '')
|
|||
// }
|
|||
// }
|
|||
}, |
|||
css: { |
|||
preprocessorOptions: { |
|||
less: { |
|||
modifyVars: { // 在这里自定义主题色等样式
|
|||
'primary-color': '#62f7e8a2', |
|||
'link-color': '#00ffe4', |
|||
'border-color-base': '#00ffe4', |
|||
'text-color': '#fff' |
|||
}, |
|||
javascriptEnabled: true, |
|||
}, |
|||
}, |
|||
}, |
|||
}) |
File diff suppressed because it is too large
Loading…
Reference in new issue