From 40950edd4a01574ff6a0f05368fc5aad94792fb7 Mon Sep 17 00:00:00 2001 From: luoyuehang <2830398107@qq.com> Date: Sat, 27 Dec 2025 18:40:37 +0800 Subject: [PATCH] =?UTF-8?q?app2=E7=9A=84=E5=9C=B0=E5=9B=BE=E6=9F=A5?= =?UTF-8?q?=E7=9C=8B=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/app2/.env | 1 + src/main/resources/app2/.env.development | 5 + src/main/resources/app2/package-lock.json | 7 + src/main/resources/app2/package.json | 1 + .../app2/src/services/deviceService.js | 44 ++ .../resources/app2/src/services/mapService.js | 41 ++ .../resources/app2/src/views/HomePage.vue | 436 ++++++++++++------ .../resources/zzz/src/views/WorkOrderList.vue | 42 +- 8 files changed, 428 insertions(+), 149 deletions(-) create mode 100644 src/main/resources/app2/.env create mode 100644 src/main/resources/app2/.env.development create mode 100644 src/main/resources/app2/src/services/mapService.js diff --git a/src/main/resources/app2/.env b/src/main/resources/app2/.env new file mode 100644 index 0000000..0583bfc --- /dev/null +++ b/src/main/resources/app2/.env @@ -0,0 +1 @@ +VITE_API_BASE_URL=http://localhost:8080 \ No newline at end of file diff --git a/src/main/resources/app2/.env.development b/src/main/resources/app2/.env.development new file mode 100644 index 0000000..067047c --- /dev/null +++ b/src/main/resources/app2/.env.development @@ -0,0 +1,5 @@ +# 开发环境 +NODE_ENV=development +VITE_AMAP_KEY=7e03ef3b43a8cdbb62e3038fc727e035 +VITE_API_BASE_URL=http://localhost:8080 +VITE_DEBUG=true diff --git a/src/main/resources/app2/package-lock.json b/src/main/resources/app2/package-lock.json index 2effd2a..bae1b58 100644 --- a/src/main/resources/app2/package-lock.json +++ b/src/main/resources/app2/package-lock.json @@ -8,6 +8,7 @@ "name": "app2", "version": "0.0.0", "dependencies": { + "@amap/amap-jsapi-loader": "^1.0.1", "pinia": "^3.0.4", "vue": "^3.5.25", "vue-router": "^4.6.3" @@ -31,6 +32,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@amap/amap-jsapi-loader": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz", + "integrity": "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw==", + "license": "MIT" + }, "node_modules/@asamuzakjp/css-color": { "version": "4.1.0", "resolved": "https://registry.npmmirror.com/@asamuzakjp/css-color/-/css-color-4.1.0.tgz", diff --git a/src/main/resources/app2/package.json b/src/main/resources/app2/package.json index d6607da..4d1d0ad 100644 --- a/src/main/resources/app2/package.json +++ b/src/main/resources/app2/package.json @@ -13,6 +13,7 @@ "test:unit": "vitest" }, "dependencies": { + "@amap/amap-jsapi-loader": "^1.0.1", "pinia": "^3.0.4", "vue": "^3.5.25", "vue-router": "^4.6.3" diff --git a/src/main/resources/app2/src/services/deviceService.js b/src/main/resources/app2/src/services/deviceService.js index 6eb0287..b81d310 100644 --- a/src/main/resources/app2/src/services/deviceService.js +++ b/src/main/resources/app2/src/services/deviceService.js @@ -75,5 +75,49 @@ export const deviceService = { console.error(`获取终端 ${terminalId} 实时数据失败:`, error) throw error.response?.data || error.message } + }, + // 在 deviceService.js 中添加 + async getTerminalLocations() { + try { + const response = await api.get('/api/student/terminal/location/all') + return response.data + } catch (error) { + console.error('获取设备位置失败:', error) + // 返回模拟数据作为后备 + return { + code: 200, + message: '使用模拟数据', + data: [ + { + terminalId: 'TERM001', + terminalName: '教学楼饮水机', + longitude: 112.938, + latitude: 28.165, + installLocation: '教学楼1F大厅', + deviceStatus: 'active', + isAvailable: true + }, + { + terminalId: 'TERM002', + terminalName: '学生公寓饮水机', + longitude: 112.940, + latitude: 28.167, + installLocation: '天马学生公寓1F', + deviceStatus: 'active', + isAvailable: true + }, + { + terminalId: 'TERM003', + terminalName: '图书馆饮水机', + longitude: 112.937, + latitude: 28.164, + installLocation: '图书馆2F', + deviceStatus: 'offline', + isAvailable: false + } + ] + } + } } + } \ No newline at end of file diff --git a/src/main/resources/app2/src/services/mapService.js b/src/main/resources/app2/src/services/mapService.js new file mode 100644 index 0000000..01ceafc --- /dev/null +++ b/src/main/resources/app2/src/services/mapService.js @@ -0,0 +1,41 @@ +// src/services/mapService.js +import api from './api' + +export const mapService = { + // 获取所有设备位置 + async getTerminalLocations() { + try { + const response = await api.get('/api/student/terminal/location/all') + return response.data + } catch (error) { + console.error('获取设备位置失败:', error) + throw error.response?.data || error.message + } + }, + + // 获取可用设备位置 + async getAvailableTerminalLocations() { + try { + const response = await api.get('/api/student/terminal/location/available') + return response.data + } catch (error) { + console.error('获取可用设备位置失败:', error) + throw error.response?.data || error.message + } + }, + + // 根据坐标获取附近设备 + async getNearbyTerminals(lng, lat, radius = 1000) { + try { + const response = await api.post('/api/student/terminal/nearby', { + longitude: lng, + latitude: lat, + radius: radius + }) + return response.data + } catch (error) { + console.error('获取附近设备失败:', error) + throw error.response?.data || error.message + } + } +} \ No newline at end of file diff --git a/src/main/resources/app2/src/views/HomePage.vue b/src/main/resources/app2/src/views/HomePage.vue index 4528677..554ea09 100644 --- a/src/main/resources/app2/src/views/HomePage.vue +++ b/src/main/resources/app2/src/views/HomePage.vue @@ -7,127 +7,13 @@
- -
- -
- -
- -
-
- - -
-
🏫
-
教学楼
-
- -
-
🏢
-
学生公寓
-
- -
-
📚
-
图书馆
-
- -
-
🍽️
-
食堂
-
- -
-
-
体育馆
-
- -
-
🔬
-
实验室
-
- - -
-
-
- - -
-
- - -
-
- - - - -
-
-
TERM001
-
- - -
-
- - - -
-
-
TERM002
-
- - -
-
- - - -
-
-
TERM003
-
+ +
- -
-
📍
-
我的位置
-
+ +
+ + ...
@@ -456,10 +342,224 @@ import { ref, reactive, onMounted, computed } from 'vue' import { useRouter } from 'vue-router' import { deviceService } from '@/services/deviceService' import { useUserStore } from '@/stores/user' +import AMapLoader from '@amap/amap-jsapi-loader' const router = useRouter() const userStore = useUserStore() +const mapInstance = ref(null) +const mapLoaded = ref(false) +const isMapLoading = ref(false) + +// 高德地图配置 +// 在 HomePage.vue 中 +const mapConfig = reactive({ + center: [112.9375, 28.1655], + zoom: 16, + key: import.meta.env.VITE_AMAP_KEY || '', // 使用环境变量 + viewMode: '3D' +}) + +// 添加:后端接口获取设备位置 +const fetchDeviceLocations = async () => { + try { + // 调用后端接口获取设备位置数据 + // 这里假设你有对应的后端接口 + const response = await deviceService.getTerminalLocations() + + if (response.code === 200 && response.data) { + return response.data + } + return [] + } catch (error) { + console.error('获取设备位置失败:', error) + return [] + } +} + +// 修改:初始化地图 +const initMap = async () => { + if (isMapLoading.value) return + isMapLoading.value = true + + try { + // 加载高德地图 + const AMap = await AMapLoader.load({ + key: mapConfig.key, + version: '2.0', + plugins: [ + 'AMap.Marker', + 'AMap.Geolocation', + 'AMap.ToolBar', + 'AMap.Scale', + 'AMap.ControlBar' + ] + }) + + // 创建地图实例 + mapInstance.value = new AMap.Map('mapContainer', { + zoom: mapConfig.zoom, + center: mapConfig.center, + viewMode: mapConfig.viewMode + }) + + // 添加控件 + mapInstance.value.addControl(new AMap.ToolBar()) + mapInstance.value.addControl(new AMap.Scale()) + mapInstance.value.addControl(new AMap.ControlBar()) + + // 获取当前位置 + const geolocation = new AMap.Geolocation({ + enableHighAccuracy: true, + timeout: 10000, + buttonOffset: new AMap.Pixel(10, 20), + zoomToAccuracy: true, + buttonPosition: 'RB' + }) + + mapInstance.value.addControl(geolocation) + + // 监听定位成功 + geolocation.getCurrentPosition((status, result) => { + if (status === 'complete') { + console.log('定位成功:', result) + } else { + console.log('定位失败:', result) + } + }) + + // 获取设备数据并在地图上标记 + const devices = await fetchDeviceLocations() + addDeviceMarkers(devices) + + mapLoaded.value = true + console.log('地图初始化成功') + } catch (error) { + console.error('地图初始化失败:', error) + // 如果高德地图初始化失败,使用原有的模拟地图 + useMockMap() + } finally { + isMapLoading.value = false + } +} + +// 添加:在地图上添加设备标记 +const addDeviceMarkers = (devices) => { + if (!mapInstance.value || !devices.length) return + + devices.forEach(device => { + // 创建标记 + const marker = new AMap.Marker({ + position: [device.longitude, device.latitude], + title: device.terminalName, + content: createMarkerContent(device), + offset: new AMap.Pixel(-12, -32) // 调整标记位置 + }) + + // 添加点击事件 + marker.on('click', () => { + showMarkerInfo(device.terminalId) + }) + + marker.setMap(mapInstance.value) + + // 保存标记引用 + if (!markers[device.terminalId]) { + markers[device.terminalId] = {} + } + markers[device.terminalId].marker = marker + }) +} + +// 添加:创建标记内容 +const createMarkerContent = (device) => { + const isOnline = device.deviceStatus === 'active' + const color = isOnline ? '#04d919' : '#aaaaaa' + + return ` +
+ + + +
+ ${device.terminalId} +
+
+ ` +} + +// 添加:模拟地图作为备用 +const useMockMap = () => { + console.log('使用模拟地图') + // 保持原有的模拟地图逻辑不变 +} + +// 修改:onMounted 钩子 +onMounted(() => { + // 初始化设备信息 + Object.keys(markers).forEach(terminalId => { + fetchDeviceInfo(terminalId) + }) + + // 延迟初始化地图 + setTimeout(() => { + initMap() + }, 500) +}) + +// 修改:地图控制方法 +const centerMap = () => { + if (mapInstance.value) { + // 使用高德地图的定位 + const geolocation = new AMap.Geolocation() + geolocation.getCurrentPosition((status, result) => { + if (status === 'complete') { + mapInstance.value.setCenter([result.position.lng, result.position.lat]) + } + }) + } +} + +const zoomIn = () => { + if (mapInstance.value) { + mapInstance.value.setZoom(mapInstance.value.getZoom() + 1) + } +} + +const zoomOut = () => { + if (mapInstance.value) { + mapInstance.value.setZoom(mapInstance.value.getZoom() - 1) + } +} + +// 修改:显示标记信息方法 +const showMarkerInfo = async (markerId) => { + if (!mapInstance.value) { + // 如果没有地图实例,使用原有逻辑 + await originalShowMarkerInfo(markerId) + return + } + + currentMarker.value = markerId + selectedMarker.value = markerId + + // 如果数据还在加载中,重新获取 + if (markers[markerId].status === 'loading') { + await fetchDeviceInfo(markerId) + } + + // 平滑滚动到标记位置 + const device = markers[markerId] + if (device && device.marker) { + mapInstance.value.setCenter(device.marker.getPosition()) + mapInstance.value.setZoom(18) + } + + showDevicePopup.value = true +} // 修改标记点配置 const markerConfigs = { TERM001: { @@ -547,6 +647,7 @@ const currentMarker = ref('') const selectedMarker = ref('') const isLoading = ref(false) +// 从后端获取设备信息 // 从后端获取设备信息 const fetchDeviceInfo = async (terminalId) => { try { @@ -558,9 +659,10 @@ const fetchDeviceInfo = async (terminalId) => { const marker = markers[terminalId] if (marker) { - // 更新标记点信息 - marker.status = data.status === 'active' ? 'online' : 'offline' - marker.statusText = data.status === 'active' ? '在线' : '离线' + // 修改状态判断逻辑,同时兼容 'active' 和 'online' 状态 + const isActive = data.status === 'active' || data.status === 'online' + marker.status = isActive ? 'online' : 'offline' + marker.statusText = isActive ? '在线' : '离线' marker.quality = data.waterQuality || '--' // 更新水质数据 @@ -573,7 +675,7 @@ const fetchDeviceInfo = async (terminalId) => { marker.recordTime = data.updateTime || '--' // 更新标记点颜色 - updateMarkerColor(terminalId, data.status) + updateMarkerColor(terminalId, isActive ? 'active' : 'inactive') } } else { console.error(`获取设备 ${terminalId} 信息失败:`, result.message) @@ -587,6 +689,7 @@ const fetchDeviceInfo = async (terminalId) => { } } + // 获取水质信息 const fetchWaterQualityInfo = async (deviceId) => { try { @@ -609,18 +712,7 @@ const updateMarkerColor = (terminalId, status) => { } } -// 显示标记点信息 -const showMarkerInfo = async (markerId) => { - currentMarker.value = markerId - selectedMarker.value = markerId - // 如果数据还在加载中,重新获取 - if (markers[markerId].status === 'loading') { - await fetchDeviceInfo(markerId) - } - - showDevicePopup.value = true -} // 隐藏弹窗 const hidePopup = () => { @@ -683,18 +775,7 @@ const openMap = (mapType) => { // 例如:window.location.href = `amapuri://route/...` } -// 地图控制 -const centerMap = () => { - alert('定位到当前位置') -} -const zoomIn = () => { - alert('放大地图') -} - -const zoomOut = () => { - alert('缩小地图') -} // 页面跳转 const goToPage = (page) => { @@ -1532,4 +1613,67 @@ onMounted(() => { font-size: 14px; color: #666; } + +/* 地图容器调整 */ +.map-container { + flex: 1; + position: relative; + overflow: hidden; + background: #e8f4fc; +} + +.real-map-container { + width: 100%; + height: 100%; +} + +/* 模拟地图背景(只有在真实地图加载失败时才显示) */ +.map-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(135deg, #a8d8ff 0%, #e3f2fd 100%); + overflow: hidden; + display: none; /* 默认隐藏 */ +} + +/* 当真实地图加载失败时显示模拟地图 */ +.map-container:not(.map-loaded) .map-background { + display: block; +} + +/* 地图控制按钮保持原有样式 */ +.map-controls { + position: absolute; + bottom: 100px; + right: 16px; + display: flex; + flex-direction: column; + gap: 12px; + z-index: 1000; /* 确保在地图上层 */ +} + +/* 自定义标记样式 */ +.custom-marker { + position: relative; +} + +.custom-marker:hover div { + display: block !important; +} + +/* 响应式调整 */ +@media (max-width: 420px) { + .home-page { + width: 100%; + height: 100vh; + } + + .map-controls { + bottom: 70px; + right: 12px; + } +} \ No newline at end of file diff --git a/src/main/resources/zzz/src/views/WorkOrderList.vue b/src/main/resources/zzz/src/views/WorkOrderList.vue index 64513b3..af83dd8 100644 --- a/src/main/resources/zzz/src/views/WorkOrderList.vue +++ b/src/main/resources/zzz/src/views/WorkOrderList.vue @@ -104,9 +104,14 @@ {{ getOrderLocation(order.deviceId) }} {{ formatDate(order.completedTime) }}
+
+ + {{ getStatusText(order.status) }} + +
@@ -138,7 +143,7 @@
- +
@@ -264,6 +269,24 @@ const formatDate = (dateStr) => { return new Date(dateStr).toLocaleDateString('zh-CN') } +// 获取状态文本 +const getStatusText = (status) => { + const statusMap = { + completed: '已完成', + reviewing: '待审核' + } + return statusMap[status] || status +} + +// 获取状态样式类 +const getStatusClass = (status) => { + const classMap = { + completed: 'completed', + reviewing: 'reviewing' + } + return classMap[status] || 'completed' +} + // 加载工单数据 const loadOrders = async () => { loading.value = true @@ -276,8 +299,9 @@ const loadOrders = async () => { processingOrders.value = allOrders.value.filter(order => order.status === 'processing' ) + // 包含已完成和待审核的工单,排除超时工单 completedOrders.value = allOrders.value.filter(order => - order.status === 'completed' + order.status === 'completed' || order.status === 'reviewing' ) // 获取可抢工单 @@ -502,6 +526,18 @@ onMounted(() => { border: 1px solid #b7eb8f; } +.status-badge.completed { + background: #f6ffed; + color: #52c41a; + border: 1px solid #b7eb8f; +} + +.status-badge.reviewing { + background: #fff7e6; + color: #fa8c16; + border: 1px solid #ffd591; +} + /* 工单按钮 */ .order-btn { padding: 6px 16px;