diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index 360d6f4..a5f77ee 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import { Outlet, useNavigate, useLocation } from 'react-router-dom'; import { Layout, Menu, Button, Space, Select, Avatar, Card, Tag @@ -15,7 +15,7 @@ import logo from '../assets/logo.png'; import { useSelector } from 'react-redux'; import { RootState, useAppDispatch } from '../store'; import { logout } from '../store/userSlice'; -import { setCurrentStation, clearStation } from '../store/stationSlice'; +import { setCurrentStation, clearStation, setStationList as setStoreStationList } from '../store/stationSlice'; import { getMenuList } from '../api/mune'; import { getStationByUserId } from '../api/stationManage'; import LanguageSwitcher from './LanguageSwitcher'; @@ -36,13 +36,19 @@ export default function AppLayout() { const dispatch = useAppDispatch(); const { userInfo } = useSelector((state: RootState) => state.user); - const { currentStation, stationId } = useSelector((state: RootState) => state.station); + // 从 store 中同时取出 stationList(优先级最高,保证最新) + const { currentStation, stationId, stationList: storeStationList } = useSelector((state: RootState) => state.station); + + // 真正用于展示和选择的列表:优先用 store 中的(StationManage 编辑后会同步),否则用本地接口的 + const mergedStationList = storeStationList?.length ? storeStationList : stationList; const getStationData = () => { getStationByUserId().then(res => { if (res.code === 200) { const list = res.data || []; setStationList(list); + // 同步到全局 store,确保各组件共享同一份最新列表 + dispatch(setStoreStationList(list)); if (!currentStation && list.length > 0) { dispatch(setCurrentStation(list[0])); } @@ -63,7 +69,9 @@ export default function AppLayout() { }; const handleStationChange = (id: number) => { - const station = stationList.find(s => s.id === id); + // 优先从合并后的列表中取(包含 store 中最新编辑的数据),其次从本地 stationList + const station = mergedStationList.find(s => Number(s.id) === Number(id)) + || stationList.find(s => Number(s.id) === Number(id)); if (station) { dispatch(setCurrentStation(station)); } @@ -206,7 +214,7 @@ export default function AppLayout() { value={stationId} placeholder={t('layout.selectStation')} onChange={handleStationChange} - options={stationList.map(s => ({ label: s.siteName, value: s.id }))} + options={mergedStationList.map(s => ({ label: s.siteName, value: s.id }))} style={{ width: 'clamp(160px, 15vw, 220px)', backgroundColor: '#0837a8', diff --git a/src/components/SystemSetting/StationManage.tsx b/src/components/SystemSetting/StationManage.tsx index 02d69b5..60fc7a4 100644 --- a/src/components/SystemSetting/StationManage.tsx +++ b/src/components/SystemSetting/StationManage.tsx @@ -15,7 +15,12 @@ import { getOrgList } from '../../api/organization'; import { fetchPublishedLayers } from '../../lib/geoServer'; import { useSelector } from 'react-redux'; import { useAppDispatch, RootState } from '../../store'; -import { setCurrentStation as setStoreStation } from '../../store/stationSlice'; +import { + setCurrentStation as setStoreStation, + setStationList, + updateStationInList, + removeStationFromList, +} from '../../store/stationSlice'; import { useTranslation } from 'react-i18next'; const { Option } = Select; @@ -96,12 +101,15 @@ export default function StationManage() { }; getStationList(params).then(res => { if (res.code === 200) { - setStationList(res.rows || []); + const list = res.rows || []; + setStationList(list); setPagination({ ...pagination, total: res.total || res.rows?.length || 0 }); + + dispatch(setStationList(list)); } }); }; @@ -193,11 +201,18 @@ export default function StationManage() { saveSite(params).then(res => { if (res.code === 200) { messageApi.success(editRecord ? t('common.editSuccess') : t('common.addSuccess')); - getStationData(); - // 编辑的场站若正是当前选中场站,则同步更新到 store(尤其 siteMapName) - if (editRecord && storeStation && editRecord.id === storeStation.id) { - dispatch(setStoreStation({ ...storeStation, ...params })); + + // ========== 关键修复:同步更新全局 store ========== + // 优先使用接口返回的最新对象(res.data),否则回退到提交参数 + const latestStation = { ...(editRecord || {}), ...params }; + + if (editRecord) { + // 编辑:更新 stationList 中对应项(slice 内部会自动同步 currentStation) + dispatch(updateStationInList(latestStation)); } + // =================================================== + + getStationData(); setModalVisible(false); } else { messageApi.error(res.msg || t('common.saveFail')); @@ -211,6 +226,8 @@ export default function StationManage() { deleteSite([id]).then(res => { if (res.code === 200) { messageApi.success(t('common.deleteSuccess')); + // 同步从全局 store 中移除 + dispatch(removeStationFromList(id)); getStationData(); } else { messageApi.error(res.msg || t('common.deleteFail')); diff --git a/src/components/devices/DeviceOverviewPage.tsx b/src/components/devices/DeviceOverviewPage.tsx index 287b91a..b22b964 100644 --- a/src/components/devices/DeviceOverviewPage.tsx +++ b/src/components/devices/DeviceOverviewPage.tsx @@ -647,8 +647,8 @@ export default function DeviceOverviewPage() { {t('devices.fixed2EquipmentListFixed2')}} variant="borderless" style={{ borderRadius: 8, marginBottom: 8 }}> record.id || record.serialNumber || record.device_sn} size="small" pagination={false} columns={[ - { title: t('devices.fixed2EquipmentFixed2NameFixed2'), dataIndex: 'name', width: 140 }, - { title: t('common.type'), dataIndex: 'type', width: 90 }, + { title: t('devices.fixed2EquipmentFixed2NameFixed2'), dataIndex: 'name', width: 180 }, + { title: t('common.type'), dataIndex: 'type', }, { title: t('workOrder.currentStatus'), dataIndex: 'onlineStatus', @@ -659,15 +659,16 @@ export default function DeviceOverviewPage() { return {text}; } }, { - title: t('devices.abMode'), dataIndex: 'mode', width: 70, render: (text) => { - let color = 'default'; - if (text === '作业中') color = 'processing'; - if (text === t('devices.idle')) color = 'success'; - if (text === '空闲中') color = 'success'; - if (text === '现场调试') color = 'warning'; - if (text === '远程调试') color = 'warning'; - if (text === '固件升级中') color = 'error'; - return {text}; + title: t('workOrder.currentStatus'), dataIndex: 'feStatus', width: 70, render: (text) => { + let color; + if (text == '1') color = 'processing'; + if (text == '2') color = 'success'; + if (text == '3') color = 'default'; // default 就是灰色标签 + + return + {text == '1' ? "工作中" : text == '2' ? "空闲中" : "离线"} + ; + } }, { diff --git a/src/locales/zh/index.ts b/src/locales/zh/index.ts index 3bc4fbc..9c0b311 100644 --- a/src/locales/zh/index.ts +++ b/src/locales/zh/index.ts @@ -464,6 +464,7 @@ const zh = { execTime: '执行时间', saveTask: '保存任务', abMode: '自定义模式', + saveFail: '保存失败', activatedTime: '已激活时间', aiAction1: 'AI', diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index e415838..6d416ac 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -89,18 +89,28 @@ export default function StationDashboard() { report_time: "2026-03-27 12:35:08" }); const weatherType = getWeatherType(weatherdata.weather); - // 请求天气 + // 请求天气:按当前场站地址/经纬度动态定位 useEffect(() => { + console.log('当前电站信息:', currentStation); const fetchWeather = async () => { try { - const res = await getWeatherData(); - setWeatherData(res); + const options = {} as any; + + if (currentStation?.address) { + options.city = currentStation.address; + } + const res = await getWeatherData(options); + setWeatherData({ + ...res, + city: res?.city || currentStation?.siteName || '', + district: res?.district || '', + }); } catch (err) { console.error("天气接口请求失败", err); } }; fetchWeather(); - }, []); + }, [currentStation]); // 模拟数据 const stationOverview = { todayEnergy: 4752.0, diff --git a/src/store/stationSlice.ts b/src/store/stationSlice.ts index 2c931ef..3c6f7e6 100644 --- a/src/store/stationSlice.ts +++ b/src/store/stationSlice.ts @@ -10,9 +10,20 @@ const loadStationFromStorage = () => { } }; +// 从 localStorage 加载场站列表(可选,加速首屏) +const loadStationListFromStorage = () => { + try { + const list = localStorage.getItem('stationList'); + return list ? JSON.parse(list) : []; + } catch { + return []; + } +}; + const initialState = { currentStation: loadStationFromStorage(), // 当前选中场站(完整对象) stationId: loadStationFromStorage()?.id || null, // 方便直接用 id + stationList: loadStationListFromStorage(), // 全局场站列表(用于跨组件共享最新数据) }; const stationSlice = createSlice({ @@ -24,19 +35,107 @@ const stationSlice = createSlice({ state.currentStation = action.payload; state.stationId = action.payload?.id || null; localStorage.setItem('currentStation', JSON.stringify(action.payload)); + + // 同时更新 stationList 中对应项(保证列表也是最新) + if (action.payload?.id && state.stationList?.length) { + const idx = state.stationList.findIndex(s => Number(s.id) === Number(action.payload.id)); + if (idx >= 0) { + state.stationList[idx] = { ...state.stationList[idx], ...action.payload }; + localStorage.setItem('stationList', JSON.stringify(state.stationList)); + } + } }, + + // 设置全局场站列表 + setStationList: (state, action) => { + state.stationList = action.payload || []; + localStorage.setItem('stationList', JSON.stringify(state.stationList)); + + // 如果当前 currentStation 在列表中,同步更新为列表中的最新版本 + if (state.stationId && state.stationList?.length) { + const latest = state.stationList.find(s => Number(s.id) === Number(state.stationId)); + if (latest) { + state.currentStation = latest; + localStorage.setItem('currentStation', JSON.stringify(latest)); + } + } + }, + + // 部分更新当前选中场站(不覆盖全部字段) + updateCurrentStation: (state, action) => { + if (!state.currentStation) return; + state.currentStation = { ...state.currentStation, ...action.payload }; + state.stationId = state.currentStation?.id || state.stationId; + localStorage.setItem('currentStation', JSON.stringify(state.currentStation)); + + // 同步更新 stationList 中对应项 + if (state.currentStation?.id && state.stationList?.length) { + const idx = state.stationList.findIndex(s => Number(s.id) === Number(state.currentStation.id)); + if (idx >= 0) { + state.stationList[idx] = { ...state.stationList[idx], ...action.payload }; + localStorage.setItem('stationList', JSON.stringify(state.stationList)); + } + } + }, + + // 更新列表中的单个场站(编辑场站后调用) + updateStationInList: (state, action) => { + const updatedStation = action.payload; + if (!updatedStation?.id) return; + + if (!state.stationList) state.stationList = []; + + const idx = state.stationList.findIndex(s => Number(s.id) === Number(updatedStation.id)); + if (idx >= 0) { + state.stationList[idx] = { ...state.stationList[idx], ...updatedStation }; + } else { + state.stationList.push(updatedStation); + } + localStorage.setItem('stationList', JSON.stringify(state.stationList)); + + // 如果更新的场站正好是当前选中的,同步 currentStation + if (state.stationId && Number(state.stationId) === Number(updatedStation.id)) { + state.currentStation = { ...state.currentStation, ...updatedStation }; + localStorage.setItem('currentStation', JSON.stringify(state.currentStation)); + } + }, + + // 从列表中删除场站 + removeStationFromList: (state, action) => { + const deletedId = action.payload; + if (!state.stationList) return; + + state.stationList = state.stationList.filter(s => Number(s.id) !== Number(deletedId)); + localStorage.setItem('stationList', JSON.stringify(state.stationList)); + + // 如果删除的是当前选中场站,清空 currentStation + if (state.stationId && Number(state.stationId) === Number(deletedId)) { + state.currentStation = null; + state.stationId = null; + localStorage.removeItem('currentStation'); + } + }, + // 清空场站(退出登录时用) clearStation: (state) => { state.currentStation = null; state.stationId = null; + state.stationList = []; localStorage.removeItem('currentStation'); + localStorage.removeItem('stationList'); }, }, }); -export const { setCurrentStation, clearStation } = stationSlice.actions; +export const { + setCurrentStation, + setStationList, + updateCurrentStation, + updateStationInList, + removeStationFromList, + clearStation, +} = stationSlice.actions; export default stationSlice.reducer; -//使用const { currentStation, stationId } = useSelector((state: RootState) => state.station); - +//使用const { currentStation, stationId, stationList } = useSelector((state: RootState) => state.station);