From 1eb46ad431cab7352fd790d39f03797eed5a77a2 Mon Sep 17 00:00:00 2001 From: zhangjiming Date: Tue, 1 Apr 2025 11:21:39 +0800 Subject: [PATCH] =?UTF-8?q?bridge+mock=20=E4=BF=AE=E6=94=B9=E5=89=8D?= =?UTF-8?q?=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/index.html | 6 ++ src/App.tsx | 8 +- src/components/MeasureItem.tsx | 45 ++++---- src/components/MineSegment.tsx | 24 +++++ src/components/UnuploadList.tsx | 68 ++++++++++++ src/components/UploadedList.tsx | 33 ++++++ src/index.tsx | 4 +- src/pages/MeasureSave.tsx | 29 ++++-- src/pages/Mine.tsx | 205 ++++++++++++++++--------------------- src/services/apiTypes.ts | 13 +++ src/store/features/historySlice.ts | 48 +++++++++ src/store/index.ts | 2 + src/utils/bridge.ts | 130 +++++++++++++++++++++-- src/utils/constant.ts | 59 ++++++++++- 14 files changed, 515 insertions(+), 159 deletions(-) create mode 100644 src/components/MineSegment.tsx create mode 100644 src/components/UnuploadList.tsx create mode 100644 src/components/UploadedList.tsx create mode 100644 src/services/apiTypes.ts create mode 100644 src/store/features/historySlice.ts diff --git a/public/index.html b/public/index.html index 935f13f..b435ed2 100644 --- a/public/index.html +++ b/public/index.html @@ -29,6 +29,12 @@ setupWebViewJavascriptBridge(function(bridge) { window.bridge = bridge; }); + + var SyncBridgeJS = { + call: function(methodName, param) { + return prompt("sync/syncBridge/" + methodName, param); + } + }; diff --git a/src/App.tsx b/src/App.tsx index ac0c923..7177710 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,6 +16,7 @@ import { addNewPoint, updateTaskState } from './store/features/measureSlice'; import { DeviceStatus, TrackRecordSig } from './services/wsTypes'; import { updateDevice } from './store/features/contextSlice'; import { createWebSocket, sharedWsUrl } from './services/socket'; +import { updateUploadStatus } from './store/features/historySlice'; const BottomBar = () => { const navigate = useNavigate(); @@ -81,12 +82,17 @@ function App() { } 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)) + } + } }); return () => subscription.unsubscribe(); }, [dispatch]); useEffect(() => { + // registerBridgeFunc(); if (appWebview) { registerBridgeFunc(); } else { diff --git a/src/components/MeasureItem.tsx b/src/components/MeasureItem.tsx index 7d49ee0..b8e0f89 100644 --- a/src/components/MeasureItem.tsx +++ b/src/components/MeasureItem.tsx @@ -4,6 +4,7 @@ import icon_arr from '../assets/icon_arr_p_r.svg'; import icon_pending from '../assets/icon_upload_0.svg'; import icon_uploading from '../assets/icon_upload_1.svg'; import icon_uploaded from '../assets/icon_upload_2.svg'; +import { Measurement } from '../services/apiTypes'; /* name: '测量名称1', @@ -16,31 +17,29 @@ railType: '60轨', export type UpdateState = 'idle' | 'pending' | 'uploading' | 'uploaded'; -export default function MeasureItem(props: { - name: string; - time: string; - line: string; - segment: string; - direction: string; - railType: string; +export default function MeasureItem({ + item, + onDetail, +}: { + item: Measurement; onDetail?: () => void; }) { return (
-

{props.name}

+

{item.name}

-

{`${props.line}`}

-

{`${props.segment}`}

-

{`${props.direction}方向`}

+

{`${item.line}`}

+

{`${item.section}`}

+

{`${item.direction}方向`}

- {props.time} + {item.createAt}
-
({ ...t, label: t.name, value: t.id }))]} + columns={[railTypes.map((t) => ({ ...t, label: t.name, value: t.id }))]} visible={railPickerVisible} onClose={() => { setRailPickerVisible(false); }} value={railId} - onConfirm={v => { + onConfirm={(v) => { setRailId(v); }} /> diff --git a/src/pages/Mine.tsx b/src/pages/Mine.tsx index dcc7dee..18efd79 100644 --- a/src/pages/Mine.tsx +++ b/src/pages/Mine.tsx @@ -1,139 +1,116 @@ -import { List, NavBar } from 'antd-mobile'; -import { useState } from 'react'; -import MeasureItem, { MeasureItemEx } from '../components/MeasureItem'; +import { NavBar, Toast } from 'antd-mobile'; +import { useEffect, useRef, useState } from 'react'; import './Mine.scss'; -import { useNavigate } from 'react-router-dom'; -const dataList = [ - { - id: 1, - name: '测量名称1', - time: '03-02 10:20', - line: '京沪线', - segment: 'A段', - direction: '上行', - railType: '60轨', - }, - { - id: 2, - name: '测量名称2', - time: '03-02 10:20', - line: '京沪线', - segment: 'B段', - direction: '下行', - railType: '60轨', - }, - { - id: 3, - name: '测量名称3', - time: '03-02 10:20', - line: '京沪线', - segment: 'C段', - direction: '上行', - railType: '60轨', - }, - { - id: 4, - name: '测量名称2', - time: '03-02 10:20', - line: '京沪线', - segment: 'D段', - direction: '下行', - railType: '60轨', - }, - { - id: 5, - name: '测量名称2', - time: '03-02 10:20', - line: '京沪线', - segment: 'E段', - direction: '下行', - railType: '60轨', - }, -]; +import UploadedList from '../components/UploadedList'; +import { Measurement } from '../services/apiTypes'; +import UnuploadList from '../components/UnuploadList'; +import MineSegment from '../components/MineSegment'; +import Bridge from '../utils/bridge'; + +const PAGE_SIZE = 10; export default function Mine() { - const navigate = useNavigate(); const [tabIndex, setTabIndex] = useState(0); + const [uploadedList, setUploadedList] = useState([]); + const [unuploadList, setUnuploadList] = useState([]); + const noMoreUploaded = useRef(false); + const noMoreUnupload = useRef(false); const [selectIds, setSelectIds] = useState([]); const onItemSelected = (id: number) => { if (selectIds.includes(id)) { - setSelectIds(selectIds.filter(i => i !== id)); + setSelectIds(selectIds.filter((i) => i !== id)); } else { setSelectIds([...selectIds, id]); } }; + + useEffect(() => { + Bridge.getUploadedRecords({ + size: PAGE_SIZE, + }).then((res) => { + if (res.success) { + setUploadedList((list) => list.concat(res.data || [])); + noMoreUploaded.current = res.data.length < PAGE_SIZE; + } else { + noMoreUnupload.current = true; + Toast.show(res.message); + } + }); + + Bridge.getUnuploadRecords({ + size: PAGE_SIZE, + }).then((res) => { + if (res.success) { + setUnuploadList((list) => list.concat(res.data || [])); + noMoreUnupload.current = res.data.length < PAGE_SIZE; + } else { + noMoreUnupload.current = true; + Toast.show(res.message); + } + }); + }, []); + + const onUploadClick = () => { + Bridge.uploadRecords({ ids: selectIds }).then((res) => { + if (!res.success) { + Toast.show(res.message); + } + }); + }; + + async function loadMoreUploadedRecords() { + const res = await Bridge.getUploadedRecords({ + lastId: uploadedList[uploadedList.length - 1].id, + size: PAGE_SIZE, + }); + if (res.success) { + noMoreUploaded.current = res.data.length < PAGE_SIZE; + setUploadedList((list) => list.concat(res.data)); + } else { + noMoreUploaded.current = true; + Toast.show(res.message); + } + } + + async function loadMoreUnuploadRecords() { + const res = await Bridge.getUnuploadRecords({ + lastId: unuploadList[unuploadList.length - 1].id, + size: PAGE_SIZE, + }); + if (res.success) { + noMoreUnupload.current = res.data.length < PAGE_SIZE; + setUnuploadList((list) => list.concat(res.data)); + } else { + noMoreUnupload.current = true; + Toast.show(res.message); + } + } + return (
我的
-
-
setTabIndex(0)}> - 测量数据 -
-
setTabIndex(1)}> - 未上传数据 -
-
+
{tabIndex === 0 && ( -
- - {dataList.map(item => ( - - navigate(`/measure/record/${item.id}`)} - /> - - ))} - -
+ )} {tabIndex === 1 && ( - <> -
- -

可在设置页面配置上传地址

-
-
- - {dataList.map(item => ( - - onItemSelected(item.id)} - onDetail={() => navigate(`/measure/record/${item.id}`)} - /> - - ))} - -
- + )}
diff --git a/src/services/apiTypes.ts b/src/services/apiTypes.ts new file mode 100644 index 0000000..2fb3e80 --- /dev/null +++ b/src/services/apiTypes.ts @@ -0,0 +1,13 @@ +export type Measurement = { + id: number; + name: string; + railId: number; + bureau: string; + line: string; + section: string; + direction: string; + createAt: string; // Date; + leftPoints: string; // json: 坐标数组 + rightPoints: string; // json: 坐标数组 + upload: boolean; +}; diff --git a/src/store/features/historySlice.ts b/src/store/features/historySlice.ts new file mode 100644 index 0000000..803375c --- /dev/null +++ b/src/store/features/historySlice.ts @@ -0,0 +1,48 @@ +import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { Measurement } from '../../services/apiTypes'; +import Bridge from '../../utils/bridge'; + +interface HistoryState { + uploadedRecords: Measurement[]; + hasMoreUploadedRecords: boolean; + unuploadRecords: Measurement[]; + hasMoreUnuploadRecords: boolean; + + uploadRecordsStatus: Array<{ id: number; state: 'pending' | 'uploading' | 'uploaded' }>; +} + +const initialState: HistoryState = { + uploadedRecords: [], + hasMoreUploadedRecords: true, + unuploadRecords: [], + hasMoreUnuploadRecords: true, + + uploadRecordsStatus: [], +}; + +export const getUploadedRecords = createAsyncThunk( + 'history/getUploaded', + async ({ lastId, size }: { lastId?: number; size: number }) => { + return Bridge.getUploadedRecords({ lastId, size }); + } +); + +export const historySlice = createSlice({ + name: 'history', + initialState, + reducers: { + updateUploadStatus: (state, action: PayloadAction>) => { + state.uploadRecordsStatus = action.payload + } + }, + extraReducers: (builder) => { + // builder.addCase(getUploadedRecords.fulfilled, (state, action) => { + // if (action.payload.success) { + // state.uploadedRecords = state.uploadedRecords.concat(action.payload.data); + // } + // }); + }, +}); + +export const { updateUploadStatus } = historySlice.actions; +export default historySlice.reducer; diff --git a/src/store/index.ts b/src/store/index.ts index ed69571..ecf8d59 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,12 +1,14 @@ import { configureStore } from '@reduxjs/toolkit'; import measureSlice from './features/measureSlice'; import contextSlice from './features/contextSlice'; +import historySlice from './features/historySlice'; // configureStore创建一个redux数据 const store = configureStore({ // 合并多个Slice reducer: { measure: measureSlice, context: contextSlice, + history: historySlice }, }); diff --git a/src/utils/bridge.ts b/src/utils/bridge.ts index 6d99f00..02325aa 100644 --- a/src/utils/bridge.ts +++ b/src/utils/bridge.ts @@ -1,5 +1,6 @@ import { Subject } from 'rxjs'; import httpRequest from '../services/httpRequest'; +import { Measurement } from '../services/apiTypes'; declare global { interface Window { @@ -15,6 +16,18 @@ declare global { ) => void; }; WVJBCallbacks: Array<(bridge: typeof window.WebViewJavascriptBridge) => void>; + + SyncBridgeJS: { + call: (methodName: string, param: string) => string; + }; + } +} + +declare global { + interface Window { + ReactNativeWebView: { postMessage: (arg: string) => void }; + bridgeFunc: Record; + bridgeCall: (func: string, param: string) => void; } } @@ -75,13 +88,24 @@ export function emitBridgeEvent(event: { func: string; data: Record } export function registerBridgeFunc() { - const jsFuncs = ['funcInJs']; - jsFuncs.forEach((funcName) => { - window.WebViewJavascriptBridge.registerHandler(funcName, (data, callback) => { - bridgeSub.next({ func: funcName, data: JSON.parse(data) }); - callback(JSON.stringify({ success: true })); - }); - }); + 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); +// console.log('res:', res); +// }; + + // const jsFuncs = ['funcInJs']; + // jsFuncs.forEach((funcName) => { + // window.WebViewJavascriptBridge.registerHandler(funcName, (data, callback) => { + // bridgeSub.next({ func: funcName, data: JSON.parse(data) }); + // callback(JSON.stringify({ success: true })); + // }); + // }); } export default class Bridge { @@ -89,6 +113,16 @@ 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) { @@ -106,13 +140,89 @@ export default class Bridge { static startMeasure() { if (appWebview) { return new Promise((resolve) => { - window.WebViewJavascriptBridge.callHandler('startMeasure', {}, (res) => { - resolve(JSON.parse(res)); - }); + // window.WebViewJavascriptBridge.callHandler('startMeasure', {}, (res) => { + // resolve(JSON.parse(res)); + // }); + const res = window.SyncBridgeJS.call('startMeasure', ''); + resolve(JSON.parse(res)); }); } else { return httpRequest({ url: '/api/mobile/startMeasure', method: 'POST' }); } } + static getUploadedRecords(param: { lastId?: number; size: number }) { + if (appWebview) { + return new Promise>((resolve) => { + const res = window.SyncBridgeJS.call('getUploadedRecords', JSON.stringify(param)); + resolve(JSON.parse(res)); + }); + } else { + return httpRequest>({ + url: '/api/mobile/getUploadedRecords', + method: 'GET', + params: param + }); + } + } + + static getUnuploadRecords(param: { lastId?: number; size: number }) { + if (appWebview) { + return new Promise>((resolve) => { + const res = window.SyncBridgeJS.call('getUnuploadRecords', JSON.stringify(param)); + resolve(JSON.parse(res)); + }); + } else { + return httpRequest>({ + url: '/api/mobile/getUnuploadRecords', + method: 'GET', + params: param + }); + } + } + + static searchHistoryRecords(param: { keyword: string }) { + if (appWebview) { + return new Promise>((resolve) => { + const res = window.SyncBridgeJS.call('searchHistoryRecords', JSON.stringify(param)); + resolve(JSON.parse(res)); + }); + } else { + return httpRequest>({ + url: '/api/mobile/searchHistoryRecords', + method: 'GET', + params: param + }); + } + } + + static getMeasurementDetail(param: { id: number }) { + if (appWebview) { + return new Promise>((resolve) => { + const res = window.SyncBridgeJS.call('getMeasurementDetail', JSON.stringify(param)); + resolve(JSON.parse(res)); + }); + } else { + return httpRequest>({ + url: '/api/mobile/getMeasurementDetail', + method: 'GET', + params: param + }); + } + } + + static uploadRecords(param: {ids: number[]}) { + if (appWebview) { + return new Promise((resolve) => { + const res = window.SyncBridgeJS.call('uploadRecords', JSON.stringify(param)); + resolve(JSON.parse(res)); + }); + } else { + return httpRequest({ + url: '/api/mobile/uploadRecords', + method: 'POST', + params: param + }); + } + } } diff --git a/src/utils/constant.ts b/src/utils/constant.ts index cbd7def..613d9fb 100644 --- a/src/utils/constant.ts +++ b/src/utils/constant.ts @@ -29,4 +29,61 @@ export const railTypes = [ code: "4", name: "43轨", }, -]; \ No newline at end of file +]; + +export const bureauList = [ + { + id: 1, + name: "北京铁路局" + }, + { + id: 2, + name: "南京铁路局" + }, + { + id: 3, + name: "上海铁路局" + }, + { + id: 4, + name: "广州铁路局" + } +] + +export const lineList = [ + { + id: 1, + name: "京沪线" + }, + { + id: 2, + name: "京九线" + }, + { + id: 3, + name: "陇海线" + }, + { + id: 4, + name: "京广线" + } +] + +export const sectionList = [ + { + id: 1, + name: "路段一" + }, + { + id: 2, + name: "路段二" + }, + { + id: 3, + name: "路段三" + }, + { + id: 4, + name: "路段四" + } +]