feat: 对接接口数据

master
donghao 2 weeks ago
parent eaa15cddc5
commit 66da196362

1019
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -13,7 +13,6 @@
"preview:prod": "vite preview --port 6060 --mode production"
},
"dependencies": {
"@types/three": "^0.175.0",
"axios": "^1.8.3",
"echarts": "^5.6.0",
"exceljs": "^4.4.0",
@ -22,7 +21,6 @@
"postcss-scss": "^4.0.9",
"sass": "^1.85.1",
"swiper": "^11.2.5",
"three": "^0.175.0",
"unplugin-auto-import": "^19.1.1",
"video.js": "^8.22.0",
"vue": "^3.5.13"

@ -1,3 +1,11 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-06-10 17:36:23
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-06-11 11:00:17
* @FilePath: \Web-Traffic-Police\src\api\dashboard.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-07 15:09:18

@ -0,0 +1,5 @@
import request from "@/utils/request/instance";
import { config } from "@/config";
export const getAllDataApi = (params: any) => {
return request.get(`/api`, params);
};

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-06 15:00:26
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-07 14:16:20
* @LastEditTime: 2025-06-11 09:51:40
* @FilePath: \vite-ai\data-dashboard\src\views\dashboard\components\footer.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
@ -23,9 +23,9 @@
<script lang="ts" setup>
import { useRoute } from 'vue-router';
import { dashboardRoutes } from '@/router/dashboard';
import { dataViewRoutes } from '@/router/dataView';
//
const navList = computed(() => dashboardRoutes?.children)
const navList = computed(() => dataViewRoutes?.children)
const route = useRoute();
//

@ -1,6 +1,8 @@
const env = import.meta.env
export const config = {
apiHost: 'http://192.168.10.103:8888', // 接口地址
apiMapTimeout: 600_000, // 接口轮询时间
env: env.VITE_APP_ENV,
baseURL: env.VITE_APP_BASE_API,
timeout: 10000,

@ -1,51 +0,0 @@
// 路由配置示例router/index.ts
// import PoleMonitor from "../views/PoleMonitor.vue";
// import DeviceStatus from "../views/DeviceStatus.vue";
export const dashboardRoutes = {
path: "/dashboard",
fullPath: "/dashboard",
redirect: "/dashboard/dataOverview",
component: () => import("@/layouts/MainLayout.vue"),
meta: { requiresAuth: true , isDashboard: true, keepAlive: false,},
children: [
{
path: "dataOverview",
fullPath: "/dashboard/dataOverview",
name: "DataOverview",
component: () => import("@/views/dashboard/DataOverview.vue"),
meta: { title: "数据总览" },
},
{
path: "monitor",
name: "AppearanceMonitor",
fullPath: "/dashboard/monitor",
component: () => import("@/views/dashboard/AppearanceMonitor.vue"),
meta: { title: "外观监测" },
},
{
path: "pole",
name: "PoleMonitor",
fullPath: "/dashboard/pole",
component: () => import("@/views/dashboard/PoleMonitor.vue"),
meta: { title: "撑杆监测" },
},
{
path: "device",
name: "DeviceStatus",
fullPath: "/dashboard/device",
component: () => import("@/views/dashboard/DeviceStatus.vue"),
meta: { title: "设备状态" },
},
{
path: "vehicle",
name: "VehiclManagement",
fullPath: "/dashboard/vehicle",
component: () => import("@/views/dashboard/VehiclManagement.vue"),
meta: { title: "车辆管理" },
}
// { path: "pole", name: "PoleMonitor", component: PoleMonitor },
// { path: "device", name: "DeviceStatus", component: DeviceStatus },
],
};

@ -0,0 +1,57 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-06-11 09:13:47
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-06-11 11:02:17
* @FilePath: \Web-Traffic-Police\src\router\home.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
// 路由配置示例router/index.ts
// import PoleMonitor from "../views/PoleMonitor.vue";
// import DeviceStatus from "../views/DeviceStatus.vue";
export const dataViewRoutes = {
path: "/dataView",
fullPath: "/dataView",
redirect: "/dataView/dataOverview",
component: () => import("@/layouts/MainLayout.vue"),
meta: { requiresAuth: true , isDashboard: true, keepAlive: false,},
children: [
{
path: "dataOverview",
fullPath: "/dataView/dataOverview",
name: "DataOverviews",
component: () => import("@/views/dataView/DataOverview.vue"),
meta: { title: "数据总览" },
},
{
path: "monitor",
name: "AppearanceMonitor",
fullPath: "/dataView/monitor",
component: () => import("@/views/dataView/AppearanceMonitor.vue"),
meta: { title: "外观监测" },
},
{
path: "pole",
name: "PoleMonitor",
fullPath: "/dataView/pole",
component: () => import("@/views/dataView/PoleMonitor.vue"),
meta: { title: "撑杆监测" },
},
{
path: "device",
name: "DeviceStatus",
fullPath: "/dataView/device",
component: () => import("@/views/dataView/DeviceStatus.vue"),
meta: { title: "设备状态" },
},
{
path: "vehicle",
name: "VehiclManagement",
fullPath: "/dataView/vehicle",
component: () => import("@/views/dataView/VehiclManagement.vue"),
meta: { title: "车辆管理" },
}
],
};

@ -2,13 +2,13 @@
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-06 13:53:22
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-03-18 10:59:08
* @LastEditTime: 2025-06-11 09:51:55
* @FilePath: \vite-ai\data-dashboard\src\router\index.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { createRouter, createWebHashHistory } from "vue-router";
import { useUserStore } from "@/stores/user";
import { dashboardRoutes } from "./dashboard";
import { dataViewRoutes } from "./dataView";
import { buildHierarchyTree } from "@/utils/tree";
const router = createRouter({
@ -26,9 +26,9 @@ const router = createRouter({
},
{
path: "/",
redirect: "/dashboard",
redirect: "/dataView",
},
{ ...dashboardRoutes },
{ ...dataViewRoutes },
],
});

@ -1,149 +0,0 @@
.data-overview-wrap {
padding-top: 30px;
// background: #002a5c;
color: white;
font-family: "Arial", sans-serif;
.grid-container {
display: flex;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-bottom: 20px;
width: 100%;
.grid-item {
// background: rgba(74, 126, 191, 0.1);
background-image: url("@/assets/common/gridItemBg.png");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
// border-radius: 8px;
width: 50%;
&>li{
width: 50%;
}
&>li:last-child{
margin-left: 12px;
}
.module-header {
color: #4a7ebf;
margin-bottom: 15px;
.month-btn,
.week-btn {
width: 70px;
height: 28px;
font-size: 14px;
color: #FFF;
background: #0F2839;
border: none;
}
.week-btn {
margin-right: 20px;
}
.active-btn {
background: #0B345E;
border: 1px solid #3FDDEB;
}
}
.chart-container {
// margin-top: 0px;
width: 100%;
height: 300px;
}
.chart-container-bar {
// margin-top: 0px;
width: 100%;
height: 340px;
}
.chart-pie-bg{
margin-top: 19px;
background-image: url("@/assets/common/gridItemPieBg.png");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
}
.grid-item-pie {
background-image: none;
}
}
.device-info {
.total-device {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 15px;
.device-icon {
width: 80px;
height: 90px;
background: url('@/assets/common/deviceTotal.png')
no-repeat center;
background-size: 100%;
}
.device-count {
margin-left: 15px;
.count-number {
font-size: 24px;
font-weight: bold;
}
}
}
.device-list {
box-sizing: border-box;
display: grid;
grid-template-columns: repeat(3, 1fr);
padding: 0 16px 20px;
gap: 10px;
.device-card {
box-sizing: border-box;
// background: rgba(74, 126, 191, 0.1);
background-image: url("@/assets/common/deviceCardBg.png");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
padding: 16px;
// border-radius: 4px;
.status-bar {
margin-top: 5px;
}
}
}
}
.monitor-images {
display: flex;
box-sizing: border-box;
gap: 16px;
padding: 20px 16px 20px 16px;
.monitor-images-left,
.monitor-images-right {
flex: 1;
position: relative;
img {
width: 100%;
height: 256px;
}
.fault-info {
position: absolute;
padding: 0 10px;
// miomwidth: 80px;
height: 28px;
background: rgba(0,0,0,0.6);
border-radius: 16px 16px 16px 16px;
top: 16px;
left: 16px;
font-size: 14px;
color: #FFF;
text-align: center;
line-height: 28px;
}
}
}
}

@ -1,309 +0,0 @@
<template>
<div class="data-overview-wrap">
<!-- 检测总量汇总 -->
<div class="grid-container">
<div class="grid-item">
<div class="module-header">
<ContentHeader bgLayout="918">
<template #title>
<div class="w-[200px] bg_title bg_title_0"></div>
</template>
<template #extra>
<div>
<el-button type="primary" class="month-btn" @click="getList('month')"
:class="{ 'active-btn': activeBtn === 'month' }">
</el-button>
<el-button class="week-btn" @click="getList('week')" :class="{ 'active-btn': activeBtn === 'week' }">
</el-button>
</div>
</template>
</ContentHeader>
</div>
<div class="chart-container chart-container-bar">
<BarChart :xData="xData" :legendArr="legendArr" :datas="datas" :colorArr="colorArr" />
</div>
</div>
<!-- 设备信息 -->
<div class="grid-item">
<div class="module-header">
<ContentHeader bgLayout="918">
<template #title>
<div class="w-[200px] bg_title bg_title_1"></div>
</template>
<template #extra>
<div></div>
</template>
</ContentHeader>
</div>
<div class="device-info">
<div class="total-device">
<div class="device-icon"></div>
<div class="device-count">
<div>设备总数</div>
<div class="count-number">{{ deviceTotal }}</div>
</div>
</div>
<div class="device-list">
<div class="device-card">
<div class="mb-3">车体检测设备: {{ carDevice?.total || 0 }}</div>
<DeviceStatus :deviceStatus="carDevice" />
</div>
<div class="device-card">
<div class="mb-3">撑杆检测设备: {{ poleDevice?.total || 0 }}</div>
<DeviceStatus :deviceStatus="poleDevice" />
</div>
<div class="device-card">
<div class="mb-3">钩机检测设备: {{ excavatorDevice?.total || 0 }}</div>
<DeviceStatus :deviceStatus="excavatorDevice" />
</div>
</div>
</div>
</div>
</div>
<!-- 中部检测模块 -->
<div class="grid-container">
<ul class="flex grid-item grid-item-pie">
<li>
<div class="module-header">
<ContentHeader bgLayout="450">
<template #title>
<div class="w-[200px] bg_title bg_title_2"></div>
</template>
<template #extra>
<el-select v-model="searchForm.car" placeholder="时间" class="custom-select" @change="getCarFault()">
<el-option label="1月" value="1"></el-option>
<el-option label="2月" value="2"></el-option>
<el-option label="3月" value="3"></el-option>
<el-option label="4月" value="4"></el-option>
<el-option label="5月" value="5"></el-option>
<el-option label="6月" value="6"></el-option>
<el-option label="7月" value="7"></el-option>
<el-option label="8月" value="8"></el-option>
<el-option label="9月" value="9"></el-option>
<el-option label="10月" value="10"></el-option>
<el-option label="11月" value="11"></el-option>
<el-option label="12月" value="12"></el-option>
</el-select>
</template>
</ContentHeader>
</div>
<div class="chart-container chart-pie-bg">
<PieChart :data="carFaultTotal" :colors="[
'#FF7C09',
'#0032FF',
'#04FFF2',
'#D19EFF',
'#FF0103',
'#9EFFF3',
]" />
</div>
</li>
<li>
<div class="module-header">
<ContentHeader bgLayout="450">
<template #title>
<div class="w-[200px] bg_title bg_title_3"></div>
</template>
<template #extra>
<el-select v-model="searchForm.pole" placeholder="时间" class="custom-select" @change="getPoleFault()">
<el-option label="1月" value="1"></el-option>
<el-option label="2月" value="2"></el-option>
<el-option label="3月" value="3"></el-option>
<el-option label="4月" value="4"></el-option>
<el-option label="5月" value="5"></el-option>
<el-option label="6月" value="6"></el-option>
<el-option label="7月" value="7"></el-option>
<el-option label="8月" value="8"></el-option>
<el-option label="9月" value="9"></el-option>
<el-option label="10月" value="10"></el-option>
<el-option label="11月" value="11"></el-option>
<el-option label="12月" value="12"></el-option>
</el-select>
</template>
</ContentHeader>
</div>
<div class="chart-container chart-pie-bg">
<PieChartSmall :data="poleFaultTotal" :colors="['#9DFFF3', '#FFA179']" />
</div>
</li>
</ul>
<div class="grid-item">
<div class="module-header">
<ContentHeader bgLayout="918">
<template #title>
<div class="w-[200px] bg_title bg_title_4"></div>
</template>
<template #extra>
<div></div>
</template>
</ContentHeader>
</div>
<div class="monitor-images">
<div class="monitor-images-left">
<img :src="imageFault[0]?.url" />
<div class="fault-info">{{ imageFault[0]?.fault_type }}</div>
</div>
<div class="monitor-images-right">
<img :src="imageFault[1]?.url"/>
<div class="fault-info">{{ imageFault[1]?.fault_type }}</div>
</div>
</div>
</div>
</div>
<AlarmModal v-model:value="isAlarmOpen" :info="currentRow" :image="currFileList" @close="isAlarmOpen = false" />
</div>
</template>
<script lang="ts" setup>
import * as echarts from "echarts";
import ContentHeader from "@/components/ContentHeader.vue";
import BarChart from "./components/BarChart.vue";
import PieChart from "./components/PieChart.vue";
import PieChartSmall from "./components/PieChartSmall.vue";
import DeviceStatus from "./components/DeviceStatus.vue";
import { getDataOverviewApi, getDeviceInfowApi, getRecordFaultApi, getRealTimeApi } from '@/api/dashboard';
import { isSuccessApi } from "@/utils/forApi";
import AlarmModal from './components/AlarmModal.vue'
import { useWebSocketStore } from '@/stores/websocketStore';
import { onBeforeRouteLeave } from 'vue-router';
defineOptions({
name: "DataOverviewWrap"
});
const xData = ref(["1月", "2月", "3月", "4月", "5月"]);
const legendArr = ["车体检测", "撑杆检测"];
const datas = ref([
[1528, 1266.02, 2468.39, 2982.67, 3165.91],
[2844.44, 6505.07, 8016.12, 6350.87, 1474.61],
]);
const colorArr = [
["#3B9FFE", "#5070F2"],
["#FFDA8D", "#FFAC06"],
];
const deviceStatus = ref({
onlineCount: 50,
errorCount: 10,
outlineCount: 10
});
const searchForm = reactive({
car: "1",
pole: "1",
});
const deviceTotal = ref(0);
const carDevice = ref({});
const poleDevice = ref({});
const excavatorDevice = ref({});
const carFaultTotal = ref([]);
const poleFaultTotal = ref([]);
const imageFault = ref([]);
const activeBtn = ref("month");
const isAlarmOpen = ref<Boolean>(false); //
const currentRow = ref<Record<string, any>>({}); //
const currFileList = ref<Record<string, any>[]>([]); //
const websocketStore = useWebSocketStore();
// messages
watch(() => websocketStore.messages, (newMessages: string[], oldMessages: string[]) => {
if(newMessages?.length > 0 && !isAlarmOpen.value) {
currentRow.value = newMessages[newMessages?.length - 1];
currFileList.value = newMessages[newMessages?.length - 1]?.images;
isAlarmOpen.value = true;
}
}, { deep: true, immediate: true });
const getList = async (dateType: string = "month") => {
activeBtn.value = dateType
const res = await getDataOverviewApi({ dateType })
if (isSuccessApi(res)) {
const { data } = res
datas.value[0] = data.appearance
datas.value[1] = data.pole
if (dateType === "month") {
xData.value = data.dateArr.map((item: any) => {
if (dateType === 'month') {
return item + '月'
}
})
} else {
xData.value = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
}
// console.log(data, 'getList_data')
}
};
const getDeviceInfo = async () => {
try {
const res = await getDeviceInfowApi();
if (isSuccessApi(res)) {
const { data } = res;
deviceTotal.value = data.deviceTotal;
carDevice.value = data.appearance;
poleDevice.value = data.pole;
excavatorDevice.value = data.excavator;
// deviceStatus.value = data
}
} catch (error) {
console.error('获取设备信息出错:', error);
}
}
const getPoleFault = async () => {
try {
const res = await getRecordFaultApi({ dateType: 'month', value: searchForm.pole, type: "pole" });
if (isSuccessApi(res)) {
const { data } = res;
poleFaultTotal.value = data;
// deviceStatus.value = data
console.log(data);
}
} catch (error) {
console.error('获取设备信息出错:', error);
}
}
const getCarFault = async () => {
try {
const res = await getRecordFaultApi({ dateType: 'month', value: searchForm.car, type: "appearance" });
if (isSuccessApi(res)) {
const { data } = res;
carFaultTotal.value = data;
// deviceStatus.value = data
console.log(data);
}
} catch (error) {
console.error('获取设备信息出错:', error);
}
}
const getRealTime = async () => {
try {
const res = await getRealTimeApi({ deviceType: '' });
if (isSuccessApi(res)) {
const { data } = res;
imageFault.value = data;
// deviceStatus.value = data
console.log(data);
}
} catch (error) {
console.error('获取设备信息出错:', error);
}
}
onBeforeRouteLeave(() => {
isAlarmOpen.value = false;
currentRow.value = {};
currFileList.value = [];
});
onMounted(() => {
getList()
getDeviceInfo()
getCarFault()
getPoleFault()
getRealTime()
});
</script>
<style scoped lang="scss">
@import url("./DataOverview.scss");
</style>

@ -1,369 +0,0 @@
<template>
<el-dialog class="pointModal-wrap" v-model="show" @close="handleClose" @opened="onDialogOpened">
<div class="pcd-container">
<div ref="pcdContainer" style="width: 100%; height: 100%" />
<span class="button-first" @click="zoomIn"></span>
<span class="button-second" @click="zoomOut"></span>
<span class="button-thrid" :disabled="!pcdLoaded" @click="returnToCenter"></span>
<!-- 新增进度条 -->
<div v-if="loading" class="progress-bar">
<div class="progress" :style="{ width: progress + '%' }"></div>
</div>
</div>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, computed, defineProps, defineEmits, withDefaults } from 'vue';
import * as THREE from "three";
import { PCDLoader } from "three/examples/jsm/loaders/PCDLoader.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
interface Props {
/** 弹窗显隐 */
value: boolean;
info: Record<string, any>;
}
interface Emits {
(e: "update:value", val: boolean): void;
}
const props = withDefaults(defineProps<Props>(), {
value: false,
info: {}
});
const emit = defineEmits<Emits>();
//
let scene;
let renderer;
//
const loading = ref(false);
const progress = ref(0);
//
const handleClose = () => {
if (pcdContainer.value) {
// DOM
while (pcdContainer.value.firstChild) {
pcdContainer.value.removeChild(pcdContainer.value.firstChild);
}
//
if (scene) {
scene.traverse((object) => {
if (object instanceof THREE.Mesh) {
object.geometry.dispose();
if (object.material instanceof THREE.Material) {
object.material.dispose();
} else {
object.material.forEach((material) => material.dispose());
}
}
});
}
//
if (controls) {
controls.dispose();
}
//
// window.removeEventListener('resize', onWindowResize);
//
scene = null;
camera = null;
renderer = null;
controls = null;
pcdLoaded.value = false;
}
emit("update:value", false);
};
const show = computed({
get() {
return props.value;
},
set(val: boolean) {
emit("update:value", val);
}
});
const pcdContainer = ref(null);
let initialCameraPosition;
let initialCameraRotation;
let initialControlsTarget;
let initialCameraZoom;
let controls;
let camera;
const pcdLoaded = ref(false);
const onDialogOpened = () => {
if (pcdContainer.value) {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(
75,
pcdContainer.value.clientWidth / pcdContainer.value.clientHeight,
0.1,
1000
);
renderer = new THREE.WebGLRenderer();
renderer.setSize(pcdContainer.value.clientWidth, pcdContainer.value.clientHeight);
pcdContainer.value.appendChild(renderer.domElement);
// OrbitControls
controls = new OrbitControls(camera, renderer.domElement);
const loader = new PCDLoader();
// true
loading.value = true;
loader.load(
"three/mod3.pcd",
function (pointCloud) {
// false
loading.value = false;
//
pointCloud.position.set(0, 100, 0);
//
const geometry = pointCloud.geometry;
//
const colors = [];
const positions = geometry.attributes.position.array;
const numPoints = positions.length / 3;
//
const startColor = new THREE.Color(0x0000ff); //
const midColor1 = new THREE.Color(0x00ff00); // 绿
const midColor2 = new THREE.Color(0xffff00); //
const endColor = new THREE.Color(0xff0000); //
// z
let minZ = Infinity;
let maxZ = -Infinity;
for (let i = 0; i < numPoints; i++) {
const z = positions[i * 3 + 2];
if (z < minZ) minZ = z;
if (z > maxZ) maxZ = z;
}
for (let i = 0; i < numPoints; i++) {
const z = positions[i * 3 + 2];
//
const factor = (z - minZ) / (maxZ - minZ);
let color;
if (factor < 0.33) {
// 绿
const subFactor = factor / 0.33;
color = startColor.clone().lerp(midColor1, subFactor);
} else if (factor < 0.66) {
// 绿
const subFactor = (factor - 0.33) / 0.33;
color = midColor1.clone().lerp(midColor2, subFactor);
} else {
//
const subFactor = (factor - 0.66) / 0.34;
color = midColor2.clone().lerp(endColor, subFactor);
}
//
colors.push(color.r, color.g, color.b);
}
//
const colorAttribute = new THREE.Float32BufferAttribute(colors, 3);
geometry.setAttribute('color', colorAttribute);
//
const material = new THREE.PointsMaterial({ vertexColors: true });
pointCloud.material = material;
//
pointCloud.scale.set(1, 1, 1);
//
const box = new THREE.Box3().setFromObject(pointCloud);
const size = box.getSize(new THREE.Vector3());
const maxSize = Math.max(size.x, size.y, size.z);
const fov = camera.fov * (Math.PI / -180);
const distance = maxSize / 2 / Math.tan(fov / 3);
//
camera.position.set(0, distance, 0);
camera.rotation.set(0, 0, 0);
camera.lookAt(0, 0, 0);
// y 45 Math.PI / 4
pointCloud.rotation.set(0, 0, 0);
//
initialCameraPosition = camera.position.clone();
initialCameraRotation = camera.rotation.clone();
initialCameraZoom = camera.zoom;
initialControlsTarget = controls.target.clone();
scene.add(pointCloud);
pcdLoaded.value = true;
// //
// const onWindowResize = () => {
// camera.aspect = pcdContainer.value.clientWidth / pcdContainer.value.clientHeight;
// camera.updateProjectionMatrix();
// renderer.setSize(pcdContainer.value.clientWidth, pcdContainer.value.clientHeight);
// };
// window.addEventListener('resize', onWindowResize);
},
function (xhr) {
//
progress.value = (xhr.loaded / xhr.total) * 100;
},
function (error) {
// false
loading.value = false;
console.error('点云文件加载失败:', error);
}
);
function animate() {
requestAnimationFrame(animate);
//
if (controls) {
controls.update();
}
if (renderer && scene && camera) {
renderer.render(scene, camera);
}
}
animate();
} else {
console.error('pcdContainer is null');
}
};
const zoomIn = () => {
if (camera) {
camera.position.multiplyScalar(0.9);
camera.updateProjectionMatrix();
if (controls) {
controls.update();
}
}
};
const zoomOut = () => {
if (camera) {
camera.position.multiplyScalar(1.1);
camera.updateProjectionMatrix();
if (controls) {
controls.update();
}
}
};
const returnToCenter = () => {
if (initialCameraPosition && initialCameraRotation && initialControlsTarget && camera && controls) {
//
controls.reset();
//
camera.position.copy(initialCameraPosition);
//
camera.rotation.copy(initialCameraRotation);
//
camera.zoom = initialCameraZoom;
//
for (let i = 0; i < 3; i++) {
camera.updateProjectionMatrix();
if (controls) {
controls.update();
}
}
//
controls.target.copy(initialControlsTarget);
//
controls.target0.copy(initialControlsTarget);
controls.position0.copy(initialCameraPosition);
controls.zoom0 = initialCameraZoom;
//
setTimeout(() => {
if (controls) {
controls.update();
}
}, 100);
} else {
console.error("Initial state is not defined");
}
};
</script>
<style lang="scss">
.pointModal-wrap.el-dialog {
box-sizing: border-box;
width: 1600px;
height: 810px;
background-image: url("@/assets/common/pointModalBg.png");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
background-color: #000;
.pcd-container {
position: relative;
width: 1560px;
height: 760px;
.button-first,
.button-second,
.button-thrid {
position: absolute;
width: 24px;
height: 24px;
cursor: pointer;
background-image: url("@/assets/common/amplify_btn.png");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
bottom: 112px;
right: 32px;
/* 添加过渡效果 */
transition: all 0.3s ease;
}
.button-second {
bottom: 72px;
right: 32px;
background-image: url("@/assets/common/reduce_btn.png");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
/* 添加过渡效果 */
transition: all 0.3s ease;
}
.button-thrid {
bottom: 33px;
right: 32px;
background-image: url("@/assets/common/reset_btn.png");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
/* 添加过渡效果 */
transition: all 0.3s ease;
}
.progress-bar {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
width: 80%;
height: 20px;
background-color: #ccc;
border-radius: 10px;
overflow: hidden;
}
.progress {
height: 100%;
background-color: #007bff;
transition: width 0.3s ease;
}
}
}
</style>

@ -85,7 +85,6 @@
</div>
</div>
<PointModal v-model:value="isPointOpen" :info="currentRow" @close="isPointOpen = false" />
<AppearanceAlarmModal v-model:value="isAlarmOpen" :info="currentRow" :image="currFileList" :beforeImage="currBeforeFileList" @close="isAlarmOpen = false" />
<DeleteModal v-model:value="isDeleteOpen" @delete-success="getList()" :info="currentRow" @close="isDeleteOpen = false" />
</div>
@ -99,7 +98,6 @@ import { Swiper, SwiperSlide } from "swiper/vue";
import { Navigation, Scrollbar } from "swiper/modules";
import { getAppearanceMonitorApi, getAppearanceMonitorDetailApi, getBeforeMonitorDetailApi } from '@/api/dashboard';
import { isSuccessApi } from "@/utils/forApi";
import PointModal from './components/PointModal.vue'
import AppearanceAlarmModal from './components/AppearanceAlarmModal.vue'
import DeleteModal from './components/DeleteModal.vue'
import { useWebSocketStore } from '@/stores/websocketStore';

@ -0,0 +1,91 @@
<!-- src/components/PollingComponent.vue -->
<script setup lang="ts">
import { config } from '@/config';
//
interface ListItem {
id: number;
name: string;
status: string;
}
//
const dataList = ref<ListItem[]>([]);
const isLoading = ref(false);
let pollingTimer: number | null = null;
let abortController: AbortController | null = null;
//
const fetchList = async (): Promise<ListItem[]> => {
abortController?.abort(); //
abortController = new AbortController();
try {
isLoading.value = true;
// API
const response = await fetch(config.apiHost);
if (!response.ok) throw new Error('请求失败');
const result = await response.json();
// TypeScript
if (!Array.isArray(result)) {
throw new Error('返回数据格式错误');
}
return result as ListItem[];
} catch (err) {
return []; //
} finally {
isLoading.value = false;
}
};
//
const startPolling = () => {
//
fetchList().then((result) => {
dataList.value = result;
});
//
pollingTimer = window.setInterval(async () => {
const result = await fetchList();
dataList.value = result;
}, config.apiMapTimeout);
};
//
const stopPolling = () => {
if (pollingTimer) clearInterval(pollingTimer);
abortController?.abort();
};
//
onMounted(() => {
startPolling()
});
//
onUnmounted(() => {
stopPolling()
});
</script>
<template>
<div>
<h2>轮询数据列表</h2>
<p v-if="isLoading">...</p>
<ul>
<li v-for="item in dataList" :key="item.id">
{{ item.titles[0] }} 状态:
{{ item.titles[0] + "时间"}}
<span v-for="(timeItem, index) in item[item.titles[0] + '时间']" :key="index">
{{ timeItem }}
</span>
</li>
</ul>
</div>
</template>
<style lang="scss">
@import url('./DataOverview.scss');
</style>
Loading…
Cancel
Save