diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5b82025 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cSpell.words": ["Cascader"] +} diff --git a/src/App.tsx b/src/App.tsx index 2d5e42f..8c5bf03 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,7 +14,7 @@ import { appWebview, bridgeOb, emitBridgeEvent, registerBridgeFunc } from './uti import { useAppDispatch } from './utils/hooks'; import { addNewPoint, updateTaskState } from './store/features/measureSlice'; import { DeviceStatus, TrackRecordSig } from './services/wsTypes'; -import { updateDevice } from './store/features/contextSlice'; +import { updateBleList, updateDevice, updateSyncProgress } from './store/features/contextSlice'; import { createWebSocket, sharedWsUrl } from './services/socket'; import { updateUploadStatus } from './store/features/historySlice'; @@ -72,27 +72,24 @@ function App() { const dispatch = useAppDispatch(); useEffect(() => { - const subscription = bridgeOb.subscribe(({ func, data }) => { - if (func === 'measureTaskEvent') { - if (Array.isArray(data)) return; - dispatch(updateTaskState(data.event)); - } else if (func === 'measurePointEvent') { - if (Array.isArray(data)) return; - dispatch(addNewPoint(data as TrackRecordSig['data'])); - } else if (func === 'peripheralStatus') { - if (Array.isArray(data)) return; - dispatch(updateDevice(data as DeviceStatus['data'])); - } else if (func === 'uploadRecordsStatus') { - if (Array.isArray(data)) { - dispatch(updateUploadStatus(data)) - } - } + const subscription = bridgeOb.subscribe((datagram) => { + if (datagram.type === 'measure-event') { + dispatch(updateTaskState(datagram.data.event)); + } else if (datagram.type === 'measure-point') { + dispatch(addNewPoint(datagram.data)); + } else if (datagram.type === 'peripheral-status') { + dispatch(updateDevice(datagram.data)); + } else if (datagram.type === 'ble-list') { + dispatch(updateBleList(datagram.data)); + } else if (datagram.type === 'sync-progress') { + dispatch(updateSyncProgress(datagram.data)); + } }); return () => subscription.unsubscribe(); }, [dispatch]); useEffect(() => { - // registerBridgeFunc(); + // registerBridgeFunc(); if (appWebview) { registerBridgeFunc(); } else { diff --git a/src/components/CustomNavBar.tsx b/src/components/CustomNavBar.tsx index 9f1b1ee..7d04842 100644 --- a/src/components/CustomNavBar.tsx +++ b/src/components/CustomNavBar.tsx @@ -15,7 +15,7 @@ export default function CustomNavBar({ title }: { title: string }) { {/** 温度,水平仪 */}
温度: {device.temperature}°C X轴倾斜: {device.inclinatorX} @@ -25,7 +25,7 @@ export default function CustomNavBar({ title }: { title: string }) {

{title}

{/** 蓝牙连接状态 */} - {device.isConnected ? ( + {device.connected ? (
setShowDetail(!showDetail)} @@ -37,7 +37,7 @@ export default function CustomNavBar({ title }: { title: string }) { src={icon_arr_d} alt="arr" style={{ - transform: device.isConnected && showDetail ? 'rotate(180deg)' : 'rotate(0deg)', + transform: device.connected && showDetail ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform 300ms', }} /> diff --git a/src/pages/Measure.tsx b/src/pages/Measure.tsx index 68c10d7..d8143a2 100644 --- a/src/pages/Measure.tsx +++ b/src/pages/Measure.tsx @@ -14,6 +14,8 @@ import { useAppDispatch, useAppSelector } from '../utils/hooks'; import { updateTaskState } from '../store/features/measureSlice'; import Bridge from '../utils/bridge'; import { selectLabeledKtjOrgs } from '../store/features/baseData'; +import { updateOrg } from '../store/features/contextSlice'; +import { textsOfKeys } from '../utils/helper'; // declare global { // interface Window { @@ -38,11 +40,12 @@ export default function Measure() { const dispatch = useAppDispatch(); const labeledKtjOrgs = useAppSelector((state) => selectLabeledKtjOrgs(state.baseData)); const measureState = useAppSelector((state) => state.measure); + const contextState = useAppSelector((state) => state.context); const canvasRef = useRef(null); const [railPickerVisible, setRailPickerVisible] = useState(false); - const [orgPickerVisible, setOrgPickerVisible] = useState(false); + const [railId, setRailId] = useState<(number | string | null)[]>([1]); // 绘制轨型基准线 @@ -66,6 +69,13 @@ export default function Measure() { } }, [measureState.rightPoints]); + const orgTextArr = () => { + return textsOfKeys( + [contextState.currOrgCode, contextState.currGWDCode, contextState.currXMCode], + labeledKtjOrgs + ); + }; + const onSaveClick = () => { navigate('/measure/save'); }; @@ -90,7 +100,10 @@ export default function Measure() { options: labeledKtjOrgs, placeholder: '请选择', }); - Toast.show(value ? `你选择了 ${value.join(' - ')}` : '你没有进行选择'); + // Toast.show(value ? `你选择了 ${value.join(' - ')}` : '你没有进行选择'); + if (value) { + dispatch(updateOrg(value as string[])); + } }; function stepState(step: StepName): StepState { if (!measureState.taskState) { @@ -176,10 +189,10 @@ export default function Measure() { className="h-10 bg-[#e3e8f5] flex justify-between items-center px-4" onClick={onOrgBarClick} > -

北京铁路局 /客运段/京沪线/左侧

- - 修改 - +

+ {contextState.currOrgCode ? orgTextArr().join('/') : '点击此处选择铁路局和工务段'} +

+ 修改
diff --git a/src/pages/Setting.tsx b/src/pages/Setting.tsx index 009fce2..914da8a 100644 --- a/src/pages/Setting.tsx +++ b/src/pages/Setting.tsx @@ -42,37 +42,6 @@ export default function Setting() {
- {/*
-
-

信息设置

-
-
-
- 铁路局名称 - 北京铁路局 - arr -
-
- 线路名称 - 京沪线 - arr -
-
- 路段名称 - 客运段 - arr -
-
- 方向 - -
-
-
*/} -
保存
diff --git a/src/services/mobileWsType.ts b/src/services/mobileWsType.ts new file mode 100644 index 0000000..12346aa --- /dev/null +++ b/src/services/mobileWsType.ts @@ -0,0 +1,66 @@ +export type PeripheralStatus = { + type: 'peripheral-status'; + data: { + connected: boolean; + power: number; + inclinatorX: number; + inclinatorY: number; + temperature: number; + }; +}; + +export type MeasureEvent = { + type: 'measure-event'; + data: { + event: + | 'START_RECORD_LEFT' + | 'FINISH_RECORD_LEFT' + | 'START_RECORD_RIGHT' + | 'FINISH_RECORD_RIGHT' + | 'WRONG_SIDE'; + }; +}; + +export type MeasurePoint = { + type: 'measure-point'; + data: { + x: number; + y: number; + }; +}; + +export type BleList = { + type: 'ble-list'; + data: Array<{ + mac: string; // 蓝牙设备的 MAC 地址(唯一标识) + name: string; // 蓝牙设备的可读名称(如型号/别名) + linked: boolean; //该设备是否已链接 + // ... 后续补充 + }>; +}; + +export type SyncProgress = { + type: 'sync-progress'; // 数据类型:同步进度状态上报 + data: { + remaining: number; // 剩余未同步数量 + fail: number; // 同步失败数量 + total: number; // 总数量 + finish: boolean; // 是否同步完成(true 表示全部完成) + }; +}; + +export type SyncItemFinish = { + type: 'sync-item-finish'; // 数据类型:单项数据同步完成上报 + data: { + id: number; // 数据同步任务的 ID + success: boolean; // 是否同步成功(true 表示成功,false 表示失败) + }; +}; + +export type MobileDatagram = + | PeripheralStatus + | MeasureEvent + | MeasurePoint + | BleList + | SyncProgress + | SyncItemFinish; diff --git a/src/services/socket.ts b/src/services/socket.ts index 28c87da..3c2e869 100644 --- a/src/services/socket.ts +++ b/src/services/socket.ts @@ -1,4 +1,5 @@ import { Subject } from "rxjs"; +import { MobileDatagram } from "./mobileWsType"; export type SocketState = "open" | "close" | "error"; @@ -9,7 +10,7 @@ class WebSocketClient { private maxReconnectAttempts: number = 5; private reconnectInterval: number = 3000; - private dataSub = new Subject<{ func: string; data: Record | any[] }>(); + private dataSub = new Subject(); get dataOb() { return this.dataSub.asObservable(); } @@ -53,7 +54,7 @@ class WebSocketClient { // 接收消息的处理 this.ws.onmessage = (event: MessageEvent) => { try { - const data = JSON.parse(event.data) as { func: string; data: Record | any[] }; + const data = JSON.parse(event.data) as MobileDatagram; // console.log("🚀 ~ WebSocketClient ~ bindEvents ~ data:", data); // if (data.type === "cmd") { // this.dataSub.next({ type: data.type, data: { ...data.data, success: data.data.status === "D0000" } }); diff --git a/src/store/features/contextSlice.ts b/src/store/features/contextSlice.ts index ba342d7..d0f5041 100644 --- a/src/store/features/contextSlice.ts +++ b/src/store/features/contextSlice.ts @@ -1,35 +1,78 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { DeviceStatus } from '../../services/wsTypes'; +import { BleList, PeripheralStatus, SyncItemFinish, SyncProgress } from '../../services/mobileWsType'; interface ContextState { - device: DeviceStatus['data']; - currRailTypeId: number; + device: PeripheralStatus['data']; + currRailTypeId: number; // 当前选择的轨型 + currOrgCode: string; // 铁路局 + currGWDCode: string; // 工务段 + currXMCode: string; // 线名 + + bleList: BleList["data"]; + syncProgress: SyncProgress["data"]; + syncItems: Array +} + +const orgGwdXmStr = localStorage.getItem('org_gwd_xm'); +let orgGwdXm: string[] | undefined; +if (orgGwdXmStr) { + orgGwdXm = orgGwdXmStr.split(','); } const initialState: ContextState = { device: { - isConnected: true, //是否链接 + connected: true, //是否已连接蓝牙 power: 60, //电量 inclinatorX: 0.276, //x轴倾斜 inclinatorY: 3.019, //y轴倾斜 temperature: 32.026, //温度 }, - // 当前选择的轨型 + currRailTypeId: 1, + + currOrgCode: orgGwdXm ? orgGwdXm[0] : '', + currGWDCode: orgGwdXm ? orgGwdXm[1] : '', + currXMCode: orgGwdXm ? orgGwdXm[2] : '', + + bleList: [], + syncProgress: { + remaining: 0, + fail: 0, + total: 0, + finish: true, + }, + syncItems:[] }; export const contextSlice = createSlice({ name: 'context', initialState, reducers: { - updateDevice: (state, action: PayloadAction) => { + updateOrg: (state, action: PayloadAction) => { + state.currOrgCode = action.payload[0]; + state.currGWDCode = action.payload[1]; + state.currXMCode = action.payload[2]; + localStorage.setItem('org_gwd_xm', action.payload.join(',')) + }, + + updateDevice: (state, action: PayloadAction) => { state.device = action.payload; }, + updateRailTypeId: (state, action: PayloadAction) => { state.currRailTypeId = action.payload; }, + + updateBleList:(state, action: PayloadAction) => { + state.bleList = action.payload + }, + + updateSyncProgress: (state, action: PayloadAction) => { + state.syncProgress = action.payload; + }, + }, }); -export const { updateDevice, updateRailTypeId } = contextSlice.actions; +export const { updateOrg, updateDevice, updateRailTypeId, updateBleList, updateSyncProgress } = contextSlice.actions; export default contextSlice.reducer; diff --git a/src/utils/bridge.ts b/src/utils/bridge.ts index f75629b..e2d675d 100644 --- a/src/utils/bridge.ts +++ b/src/utils/bridge.ts @@ -1,6 +1,7 @@ import { Subject } from 'rxjs'; import httpRequest from '../services/httpRequest'; import { Measurement } from '../services/apiTypes'; +import { MobileDatagram } from '../services/mobileWsType'; declare global { interface Window { @@ -81,18 +82,18 @@ type ShowModelParam = Partial<{ // 是否运行在 原生APP 的 WebView 中 export const appWebview = navigator.userAgent.includes('iFlyTop-mobile'); -const bridgeSub = new Subject<{ func: string; data: Record | any[] }>(); +const bridgeSub = new Subject(); export const bridgeOb = bridgeSub.asObservable(); -export function emitBridgeEvent(event: { func: string; data: Record | any[] }) { +export function emitBridgeEvent(event: MobileDatagram) { bridgeSub.next(event); } export function registerBridgeFunc() { - window.bridgeFunc = { - peripheralStatus: (param: string) => { - bridgeSub.next({ func: 'peripheralStatus', data: JSON.parse(param) }); - }, - }; +// window.bridgeFunc = { +// peripheralStatus: (param: string) => { +// bridgeSub.next({ func: 'peripheralStatus', data: JSON.parse(param) }); +// }, +// }; // window.bridgeCall = (func, param) => { // const res = window.bridgeFunc[func].call(null, param); @@ -113,16 +114,6 @@ export default class Bridge { if (appWebview) { window.WebViewJavascriptBridge.registerHandler(name, func); } - - // window.bridgeFunc.peripheralStatus( - // JSON.stringify({ - // isConnected: true, - // power: 99, - // inclinatorx: 0.22, - // inclinatorY: 3.01, - // temperature: 32.02, - // }) - // ); } static showModal(param: ShowModelParam) { @@ -147,7 +138,7 @@ export default class Bridge { resolve(JSON.parse(res)); }); } else { - return httpRequest({ url: '/api/mobile/startMeasure', method: 'POST' }); + return httpRequest({ url: '/api/measure/start', method: 'POST' }); } } diff --git a/src/utils/helper.ts b/src/utils/helper.ts index 11eff67..34af101 100644 --- a/src/utils/helper.ts +++ b/src/utils/helper.ts @@ -1,4 +1,4 @@ -import { KTJOrg } from "../services/apiTypes"; +import { KTJOrg } from '../services/apiTypes'; export type LabelKTJOrg = { label: string; @@ -14,6 +14,7 @@ export type LabelKTJOrg = { }; export function labeledKtjOrgs(ktjOrgs: KTJOrg[]): LabelKTJOrg[] { + console.log('为科天健JSON数组加标签'); return ktjOrgs.map((org) => { const _org: LabelKTJOrg = { label: org.value || org.key, @@ -30,3 +31,13 @@ export function labeledKtjOrgs(ktjOrgs: KTJOrg[]): LabelKTJOrg[] { return _org; }); } + +export function textsOfKeys(keys: string[], orgs: LabelKTJOrg[]): string[] { + const l1 = orgs.find((item) => item.value === keys[0]); + if (!l1) return []; + const l2 = l1.children.find((item) => item.value === keys[1]); + if (!l2) return []; + const l3 = l2.children.find((item) => item.value === keys[2]); + if (!l3) return []; + return [l1.label, l2.label, l3.label]; +}