Compare commits
4 Commits
7ebba1bbf6
...
5c0adefcc7
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c0adefcc7 | |||
| 3797701d43 | |||
| c1792670cb | |||
| 02e29b25d1 |
@@ -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',
|
||||
|
||||
@@ -609,9 +609,9 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine,
|
||||
`;
|
||||
} else {
|
||||
tooltip.innerHTML = `
|
||||
<div style="font-weight:600;margin-bottom:4px;">📍 ${t('systemSetting.longitude')}:${lonStr}</div>
|
||||
<div style="font-weight:600;">${t('systemSetting.latitude')}:${latStr}</div>
|
||||
`;
|
||||
<div style="font-weight:600;margin-bottom:4px;">📍 ${t('systemSetting.longitude')}:${lonStr}</div>
|
||||
<div style="font-weight:600;">${t('systemSetting.latitude')}:${latStr}</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1665,16 +1665,17 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine,
|
||||
handler.setInputAction((movement) => {
|
||||
const picked = viewer.scene.pick(movement.endPosition);
|
||||
const pickedRobot = (window.Cesium.defined(picked) && picked.id?.properties?.robot) ? picked.id.properties.robot.getValue() : null;
|
||||
const { lon, lat } = getCursorLngLat(viewer, movement.endPosition);
|
||||
if (window.Cesium.defined(lon) && Number.isFinite(lon)) {
|
||||
|
||||
// ✅ 只有拾取到设备实体,才显示tooltip,空白区域直接隐藏
|
||||
if (pickedRobot) {
|
||||
const { lon, lat } = getCursorLngLat(viewer, movement.endPosition);
|
||||
showCoordinateTooltip(tooltipRef.current, movement.endPosition, t, lon, lat, pickedRobot);
|
||||
} else if (pickedRobot) {
|
||||
showCoordinateTooltip(tooltipRef.current, movement.endPosition, t, NaN, NaN, pickedRobot);
|
||||
} else {
|
||||
tooltipRef.current.style.display = "none";
|
||||
}
|
||||
}, window.Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
||||
|
||||
|
||||
handler.setInputAction((movement) => {
|
||||
const picked = viewer.scene.pick(movement.position);
|
||||
if (window.Cesium.defined(picked) && picked.id?.properties?.robot) {
|
||||
|
||||
@@ -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'));
|
||||
|
||||
@@ -647,8 +647,8 @@ export default function DeviceOverviewPage() {
|
||||
<Card title={<><SettingOutlined />{t('devices.fixed2EquipmentListFixed2')}</>} variant="borderless" style={{ borderRadius: 8, marginBottom: 8 }}>
|
||||
<Table rowKey={(record) => 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 <Tag color={color} style={{ margin: 0 }}>{text}</Tag>;
|
||||
}
|
||||
}, {
|
||||
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 <Tag color={color} style={{ margin: 0 }}>{text}</Tag>;
|
||||
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 <Tag color={color} style={{ margin: 0 }}>
|
||||
{text == '1' ? "工作中" : text == '2' ? "空闲中" : "离线"}
|
||||
</Tag>;
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -606,7 +606,6 @@ export default function DeviceStatusPage() {
|
||||
longitude: lng,
|
||||
latitude: lat,
|
||||
},
|
||||
// 让地图取最新经纬度
|
||||
longitude: lng,
|
||||
latitude: lat,
|
||||
name: device?.deviceAlias || device?.callsign || device?.deviceName || t('devices.device'),
|
||||
|
||||
@@ -271,7 +271,46 @@ export default function RobotTaskPage() {
|
||||
controller.onLocationInfoUpdate = setRobotRealtimePosition;
|
||||
controller.onIsFinished = handleFinish;
|
||||
controller.handleArrivedPoint = handleFinishEdPoint;
|
||||
controller.onRoutePost = setPoints;
|
||||
// 处理路线点回调:taskId必须和当前选中任务一致
|
||||
controller.onRoutePost = (msg) => {
|
||||
console.log('收到 route_post 消息:', msg);
|
||||
const { taskId, data } = msg;
|
||||
|
||||
// 1. 先在任务池中查找该taskId对应的任务
|
||||
const matchedTask = taskPoolList.find(task => String(task.id) === String(taskId));
|
||||
|
||||
// 2. 两种情况处理:
|
||||
// a) 当前没有选中任务,且匹配到了任务池中的任务 → 自动选中该任务
|
||||
// b) 当前已选中任务,且选中任务的id === 传来的taskId → 更新数据
|
||||
const isSameAsSelected = runningPoolTask && String(runningPoolTask.id) === String(taskId);
|
||||
const shouldSelectNewTask = !runningPoolTask && matchedTask;
|
||||
|
||||
if (isSameAsSelected || shouldSelectNewTask) {
|
||||
console.log('taskId匹配成功,处理路线数据', {
|
||||
taskId,
|
||||
runningPoolTaskId: runningPoolTask?.id,
|
||||
matchedTask
|
||||
});
|
||||
|
||||
// 如果是匹配到新任务就设置选中状态
|
||||
if (shouldSelectNewTask) {
|
||||
setRunningPoolTask(matchedTask);
|
||||
}
|
||||
|
||||
// 先直接用推送的data设置点(实时性)
|
||||
if (data) {
|
||||
setPoints(data);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
console.log('taskId不匹配,忽略本次推送', {
|
||||
receivedTaskId: taskId,
|
||||
currentSelectedTaskId: runningPoolTask?.id,
|
||||
matchedTaskFound: !!matchedTask
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -67,6 +67,16 @@ export default class DeviceController {
|
||||
wsManager.close();
|
||||
|
||||
}
|
||||
disconnect() {
|
||||
console.log('DeviceController disconnect: 断开 WebSocket');
|
||||
wsManager.close();
|
||||
this.connectedDeviceSn = null;
|
||||
this.currentDevice = null;
|
||||
this.wsDeviceInfo = {};
|
||||
this.locationInfo = {};
|
||||
this.isControlled = false;
|
||||
this.onControlStatusChange?.(false);
|
||||
}
|
||||
|
||||
handleVisibilityChange() {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
@@ -362,7 +372,7 @@ export default class DeviceController {
|
||||
case 'route_post':
|
||||
console.log('route post:', msg);
|
||||
if (msg.deviceId) {
|
||||
this.onRoutePost?.(msg.data);
|
||||
this.onRoutePost?.(msg);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -93,6 +93,10 @@ const en = {
|
||||
requestError: 'Request Error',
|
||||
settings: 'Settings',
|
||||
storeValue: 'Store Value',
|
||||
action: 'Action',
|
||||
locate: 'Locate',
|
||||
minutes: 'min',
|
||||
totalCount: 'Total {{count}}',
|
||||
submitFail: 'Submit Fail',
|
||||
timeRange: 'Time Range',
|
||||
working: 'Working',
|
||||
@@ -170,6 +174,9 @@ const en = {
|
||||
ambientTemperature: 'Ambient Temperature',
|
||||
current: 'Current',
|
||||
currentIrradiance: 'Current Irradiance',
|
||||
charge: 'Battery',
|
||||
model: 'Model',
|
||||
ratPower: 'Rated Power',
|
||||
frequency: 'Frequency',
|
||||
gridConnected: 'Grid Connected',
|
||||
gridConnection: 'Grid Connection',
|
||||
@@ -371,6 +378,36 @@ const en = {
|
||||
networkLatency: 'Network Latency',
|
||||
waterRiskWarning: 'Warning: Water accumulation risk in Area B, avoid it',
|
||||
presetRoutes: 'Preset Routes',
|
||||
all: 'All',
|
||||
stop: 'Stop',
|
||||
currentTask: 'Current Task',
|
||||
realtimePosition: 'Real-time Position',
|
||||
taskCompleted: 'Task completed',
|
||||
deviceSerialNumberMissing: 'Device serial number missing',
|
||||
confirmExecuteRoute: 'Confirm to execute this route?',
|
||||
targetDevice: 'Target Device',
|
||||
routePointCount: 'Route points',
|
||||
taskExecuteFailed: 'Task execution failed',
|
||||
taskPauseAbnormal: 'Task pause abnormal',
|
||||
areYouSureCancelTask: 'Are you sure to cancel the task?',
|
||||
taskCancelled: 'Task cancelled',
|
||||
taskCancelAbnormal: 'Failed to cancel the task',
|
||||
taskStatusNew: 'Pending',
|
||||
taskStatusExecuting: 'Executing',
|
||||
taskStatusPause: 'Paused',
|
||||
taskStatusFinish: 'Finished',
|
||||
taskStatusCancel: 'Cancelled',
|
||||
unnamedTask: 'Unnamed Task',
|
||||
planId: 'Plan ID',
|
||||
expectedComplete: 'Expected completion',
|
||||
noCurrentTask: 'No current task',
|
||||
searchRoute: 'Search route',
|
||||
noRouteTip: 'No preset routes',
|
||||
pointCount: 'Point count',
|
||||
deviceInfo: 'Device Info',
|
||||
deviceCode: 'Device Code',
|
||||
siteBelonging: 'Site',
|
||||
deviceLocation: 'Device location',
|
||||
searchPlaceholder: 'Search...',
|
||||
unnamedRoute: 'Unnamed Route',
|
||||
deleteRoute: 'Delete Route',
|
||||
@@ -3604,6 +3641,13 @@ const en = {
|
||||
fixed2Offline3: ' Offline 3',
|
||||
|
||||
powerCurveFixed2: 'Real-time power curve',
|
||||
viewDeviceDetailAndTask: 'View device detail and task',
|
||||
showAllDevices: 'Show All Devices',
|
||||
showOnlySelected: 'Show Selected Only',
|
||||
clickDeviceOnMapTip: 'Click a device on the map to locate and view details',
|
||||
focusingDevice: 'Locating device: {{name}}',
|
||||
foundDevices: 'Found {{count}} devices',
|
||||
gridNorm: 'Grid Normal',
|
||||
|
||||
s11: 'today',
|
||||
|
||||
|
||||
@@ -97,6 +97,10 @@ const zh = {
|
||||
requestError: '请求错误',
|
||||
settings: '设置',
|
||||
storeValue: '存储值',
|
||||
action: '操作',
|
||||
locate: '定位',
|
||||
minutes: '分钟',
|
||||
totalCount: '共 {{count}} 台',
|
||||
submitFail: '提交失败',
|
||||
timeRange: '时间范围',
|
||||
working: '作业中',
|
||||
@@ -174,6 +178,9 @@ const zh = {
|
||||
ambientTemperature: '环境温度',
|
||||
current: '当前',
|
||||
currentIrradiance: '当前辐照度',
|
||||
charge: '电量',
|
||||
model: '型号',
|
||||
ratPower: '额定功率',
|
||||
frequency: '频率',
|
||||
gridConnected: '电网已连接',
|
||||
gridConnection: '电网连接',
|
||||
@@ -386,6 +393,36 @@ const zh = {
|
||||
networkLatency: '网络延迟',
|
||||
waterRiskWarning: '注意:区域 B 存在积水风险,建议避开',
|
||||
presetRoutes: '预设路线',
|
||||
all: '全部',
|
||||
stop: '停止',
|
||||
currentTask: '当前任务',
|
||||
realtimePosition: '实时位置',
|
||||
taskCompleted: '任务已完成',
|
||||
deviceSerialNumberMissing: '设备序列号缺失',
|
||||
confirmExecuteRoute: '确认执行该路线?',
|
||||
targetDevice: '目标设备',
|
||||
routePointCount: '路径点数',
|
||||
taskExecuteFailed: '任务执行失败',
|
||||
taskPauseAbnormal: '任务暂停异常',
|
||||
areYouSureCancelTask: '确定要取消任务?',
|
||||
taskCancelled: '任务已取消',
|
||||
taskCancelAbnormal: '任务取消失败',
|
||||
taskStatusNew: '待执行',
|
||||
taskStatusExecuting: '执行中',
|
||||
taskStatusPause: '已暂停',
|
||||
taskStatusFinish: '已完成',
|
||||
taskStatusCancel: '已取消',
|
||||
unnamedTask: '未命名任务',
|
||||
planId: '任务ID',
|
||||
expectedComplete: '预计完成',
|
||||
noCurrentTask: '暂无当前任务',
|
||||
searchRoute: '搜索路径',
|
||||
noRouteTip: '暂无预设路径',
|
||||
pointCount: '路径点数',
|
||||
deviceInfo: '设备信息',
|
||||
deviceCode: '设备编码',
|
||||
siteBelonging: '所属场站',
|
||||
deviceLocation: '设备位置',
|
||||
searchPlaceholder: '搜索...',
|
||||
unnamedRoute: '未命名路线',
|
||||
deleteRoute: '删除航线',
|
||||
@@ -427,6 +464,7 @@ const zh = {
|
||||
execTime: '执行时间',
|
||||
saveTask: '保存任务',
|
||||
abMode: '自定义模式',
|
||||
|
||||
saveFail: '保存失败',
|
||||
activatedTime: '已激活时间',
|
||||
aiAction1: 'AI',
|
||||
@@ -555,7 +593,7 @@ const zh = {
|
||||
equipmentSituationMap: '设备态势地图',
|
||||
execTimePoint: '执行时间点',
|
||||
executeDeviceColon: '执行设备',
|
||||
executeNow: '执行现在',
|
||||
executeNow: '执行',
|
||||
executeWayline: '执行航线',
|
||||
executeWaylineTask: '执行航线任务',
|
||||
executingTasks: '执行中任务',
|
||||
@@ -3653,6 +3691,13 @@ const zh = {
|
||||
fixed2Offline3: '离线 3',
|
||||
|
||||
powerCurveFixed2: '实时功率曲线',
|
||||
viewDeviceDetailAndTask: '查看设备详情与任务',
|
||||
showAllDevices: '显示全部设备',
|
||||
showOnlySelected: '仅显示选中设备',
|
||||
clickDeviceOnMapTip: '点击地图上的设备可定位并查看详情',
|
||||
focusingDevice: '正在定位设备:{{name}}',
|
||||
foundDevices: '已找到 {{count}} 台设备',
|
||||
gridNorm: '并网正常',
|
||||
|
||||
s11: '今天',
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user