Browse Source

bridge+mock 修改前保存

master
zhangjiming 4 months ago
parent
commit
1eb46ad431
  1. 6
      public/index.html
  2. 8
      src/App.tsx
  3. 45
      src/components/MeasureItem.tsx
  4. 24
      src/components/MineSegment.tsx
  5. 68
      src/components/UnuploadList.tsx
  6. 33
      src/components/UploadedList.tsx
  7. 4
      src/index.tsx
  8. 29
      src/pages/MeasureSave.tsx
  9. 205
      src/pages/Mine.tsx
  10. 13
      src/services/apiTypes.ts
  11. 48
      src/store/features/historySlice.ts
  12. 2
      src/store/index.ts
  13. 130
      src/utils/bridge.ts
  14. 59
      src/utils/constant.ts

6
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);
}
};
</script>
</body>
</html>

8
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 {

45
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 (
<div className="flex mx-2 gap-3">
<main className="flex-1">
<header className="flex items-center gap-2">
<h1 className="text-[15px] font-medium ">{props.name}</h1>
<h1 className="text-[15px] font-medium ">{item.name}</h1>
</header>
<main className="flex my-2">
<p className="flex-1 text-sm ">{`${props.line}`}</p>
<p className="flex-1 text-sm ">{`${props.segment}`}</p>
<p className="flex-1 text-sm ">{`${props.direction}方向`}</p>
<p className="flex-1 text-sm ">{`${item.line}`}</p>
<p className="flex-1 text-sm ">{`${item.section}`}</p>
<p className="flex-1 text-sm ">{`${item.direction}方向`}</p>
</main>
<footer>
<span className="text-sm text-[#b7b7b7]">{props.time}</span>
<span className="text-sm text-[#b7b7b7]">{item.createAt}</span>
</footer>
</main>
<aside className="flex items-center" onClick={props.onDetail}>
<aside className="flex items-center" onClick={onDetail}>
<span className="mr-2 text-sm text-primary font-medium"></span>
<img src={icon_arr} alt="arr" className="h-[10px]" />
</aside>
@ -49,12 +48,7 @@ export default function MeasureItem(props: {
}
export function MeasureItemEx(props: {
name: string;
time: string;
line: string;
segment: string;
direction: string;
railType: string;
item: Measurement;
selected: boolean;
uploadState: UpdateState;
onSelected?: () => void;
@ -89,19 +83,20 @@ export function MeasureItemEx(props: {
return (
<div
className="relative flex mx-2 gap-3"
onClick={props.uploadState === 'idle' ? props.onSelected : undefined}>
onClick={props.uploadState === 'idle' ? props.onSelected : undefined}
>
<main className="flex-1">
<header className="flex items-center gap-2">
{stateImg()}
<h1 className="text-[15px] font-medium ">{props.name}</h1>
<h1 className="text-[15px] font-medium ">{props.item.name}</h1>
</header>
<main className="flex my-2">
<p className="flex-1 text-sm ">{`${props.line}`}</p>
<p className="flex-1 text-sm ">{`${props.segment}`}</p>
<p className="flex-1 text-sm ">{`${props.direction}方向`}</p>
<p className="flex-1 text-sm ">{`${props.item.line}`}</p>
<p className="flex-1 text-sm ">{`${props.item.section}`}</p>
<p className="flex-1 text-sm ">{`${props.item.direction}方向`}</p>
</main>
<footer>
<span className="text-sm text-[#b7b7b7]">{props.time}</span>
<span className="text-sm text-[#b7b7b7]">{props.item.createAt}</span>
</footer>
</main>
<aside className="flex items-center" onClick={props.onDetail}>

24
src/components/MineSegment.tsx

@ -0,0 +1,24 @@
export default function MineSegment({
tabIndex,
setTabIndex,
}: {
tabIndex: number;
setTabIndex: (idx: number) => void;
}) {
return (
<section className="px-4 py-5 flex">
<div
className={`flex-1 h-10 rounded-l-md ${tabIndex === 0 ? 'btn-contained' : 'btn-elevated'}`}
onClick={() => setTabIndex(0)}
>
</div>
<div
className={`flex-1 h-10 rounded-r-md ${tabIndex === 1 ? 'btn-contained' : 'btn-elevated'}`}
onClick={() => setTabIndex(1)}
>
</div>
</section>
);
}

68
src/components/UnuploadList.tsx

@ -0,0 +1,68 @@
import { InfiniteScroll, List } from 'antd-mobile';
import { Measurement } from '../services/apiTypes';
import { MeasureItemEx, UpdateState } from './MeasureItem';
import { useNavigate } from 'react-router';
import { useAppSelector } from '../utils/hooks';
export default function UnuploadList({
dataList,
selectIds,
onItemSelected,
onUpload,
hasMore,
loadMore,
}: {
dataList: Measurement[];
selectIds: number[];
onItemSelected: (id: number) => void;
onUpload: () => void;
hasMore: boolean;
loadMore: () => Promise<void>;
}) {
const navigate = useNavigate();
const historyState = useAppSelector((state) => state.history);
function uploadState(id: number): UpdateState {
const item = historyState.uploadRecordsStatus.find((item) => item.id === id);
if (item) {
return item.state;
} else {
return 'idle';
}
}
return (
<>
<div className="flex items-center gap-3 px-4 mb-4">
<button
className="btn-contained rounded-md h-9 w-[100px]"
disabled={selectIds.length === 0}
onClick={selectIds.length === 0 ? undefined : onUpload}
>
</button>
<p className="text-sm text-[#AFAFAF]"></p>
</div>
<div className="unUpload-list overflow-x-hidden overflow-y-auto">
{dataList.length > 0 ? (
<List>
{dataList.map((item) => (
<List.Item key={item.id}>
<MeasureItemEx
item={item}
uploadState={uploadState(item.id)}
selected={selectIds.includes(item.id)}
onSelected={() => onItemSelected(item.id)}
onDetail={() => navigate(`/measure/record/${item.id}`)}
/>
</List.Item>
))}
<InfiniteScroll loadMore={loadMore} hasMore={hasMore} />
</List>
) : (
<p className="absolute text-title left-1/2 top-1/3 -translate-x-1/2"></p>
)}
</div>
</>
);
}

33
src/components/UploadedList.tsx

@ -0,0 +1,33 @@
import { InfiniteScroll, List } from 'antd-mobile';
import { Measurement } from '../services/apiTypes';
import MeasureItem from './MeasureItem';
import { useNavigate } from 'react-router';
export default function UploadedList({
dataList,
hasMore,
loadMore,
}: {
dataList: Measurement[];
hasMore: boolean;
loadMore: () => Promise<void>;
}) {
const navigate = useNavigate();
return (
<div className="all-list relative overflow-x-hidden overflow-y-auto">
{dataList.length > 0 ? (
<List>
{dataList.map((item) => (
<List.Item key={item.id}>
<MeasureItem item={item} onDetail={() => navigate(`/measure/record/${item.id}`)} />
</List.Item>
))}
<InfiniteScroll loadMore={loadMore} hasMore={hasMore} />
</List>
) : (
<p className="absolute text-title left-1/2 top-1/3 -translate-x-1/2"></p>
)}
</div>
);
}

4
src/index.tsx

@ -40,11 +40,11 @@ const router = createHashRouter(
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<React.StrictMode>
// <React.StrictMode>
<Provider store={store}>
<RouterProvider router={router} />
</Provider>
</React.StrictMode>
// </React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function

29
src/pages/MeasureSave.tsx

@ -2,15 +2,23 @@ import { NavBar, Picker } from 'antd-mobile';
import { useNavigate } from 'react-router';
import icon_arr_r from '../assets/icon_arr_s_r.svg';
import { railTypes } from '../utils/constant';
import { useState } from 'react';
import { ChangeEvent, useState } from 'react';
export default function MeasureSave() {
const navigate = useNavigate();
const back = () => navigate(-1);
const [name, setName] = useState("")
const [railPickerVisible, setRailPickerVisible] = useState(false);
const [railId, setRailId] = useState<(number | string | null)[]>([1]);
const onInputChange = (evt: ChangeEvent<HTMLInputElement>) => {
setName(evt.target.value)
}
const onSaveBtnClick = () => {
console.log(name, railId)
};
return (
<>
<div>
@ -23,27 +31,36 @@ export default function MeasureSave() {
<span></span>
<input
type="text"
value={name}
placeholder="请填写"
className="border-0 outline-none self-stretch text-right flex-1 ml-4"
onChange={onInputChange}
/>
</div>
<div className="h-12 flex items-center " onClick={()=>setRailPickerVisible(true)}>
<div className="h-12 flex items-center " onClick={() => setRailPickerVisible(true)}>
<span></span>
<span className="ml-auto mr-4">{railTypes.find(r => r.id === railId[0])?.name || ""}</span>
<span className="ml-auto mr-4">
{railTypes.find((r) => r.id === railId[0])?.name || ''}
</span>
<img src={icon_arr_r} alt="arr" />
</div>
</div>
<div className="btn-contained rounded-md h-12 mx-9 my-8 text-base font-medium"></div>
<div
className="btn-contained rounded-md h-12 mx-9 my-8 text-base font-medium"
onClick={onSaveBtnClick}
>
</div>
</div>
</div>
<Picker
columns={[railTypes.map(t => ({ ...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);
}}
/>

205
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<Measurement[]>([]);
const [unuploadList, setUnuploadList] = useState<Measurement[]>([]);
const noMoreUploaded = useRef(false);
const noMoreUnupload = useRef(false);
const [selectIds, setSelectIds] = useState<number[]>([]);
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 (
<div>
<NavBar className="bg-white" back={null}>
</NavBar>
<div className="home-page-content">
<section className="px-4 py-5 flex">
<div
className={`flex-1 h-10 rounded-l-md ${
tabIndex === 0 ? 'btn-contained' : 'btn-elevated'
}`}
onClick={() => setTabIndex(0)}>
</div>
<div
className={`flex-1 h-10 rounded-r-md ${
tabIndex === 1 ? 'btn-contained' : 'btn-elevated'
}`}
onClick={() => setTabIndex(1)}>
</div>
</section>
<MineSegment tabIndex={tabIndex} setTabIndex={setTabIndex} />
<main>
{tabIndex === 0 && (
<div className="all-list overflow-x-hidden overflow-y-auto">
<List>
{dataList.map(item => (
<List.Item key={item.id}>
<MeasureItem
{...item}
onDetail={() => navigate(`/measure/record/${item.id}`)}
/>
</List.Item>
))}
</List>
</div>
<UploadedList
dataList={uploadedList}
hasMore={!noMoreUploaded.current}
loadMore={loadMoreUploadedRecords}
/>
)}
{tabIndex === 1 && (
<>
<div className="flex items-center gap-3 px-4 mb-4">
<button
className="btn-contained rounded-md h-9 w-[100px]"
disabled={selectIds.length === 0}>
</button>
<p className="text-sm text-[#AFAFAF]"></p>
</div>
<div className="unUpload-list overflow-x-hidden overflow-y-auto">
<List>
{dataList.map(item => (
<List.Item key={item.id}>
<MeasureItemEx
{...item}
uploadState={
item.id === 1
? 'uploaded'
: item.id === 2
? 'uploading'
: item.id === 3
? 'pending'
: 'idle'
}
selected={selectIds.includes(item.id)}
onSelected={() => onItemSelected(item.id)}
onDetail={() => navigate(`/measure/record/${item.id}`)}
/>
</List.Item>
))}
</List>
</div>
</>
<UnuploadList
dataList={unuploadList}
selectIds={selectIds}
onItemSelected={onItemSelected}
onUpload={onUploadClick}
hasMore={!noMoreUnupload.current}
loadMore={loadMoreUnuploadRecords}
/>
)}
</main>
</div>

13
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;
};

48
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<Array<{ id: number; state: 'pending' | 'uploading' | 'uploaded' }>>) => {
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;

2
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
},
});

130
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<string, any>;
bridgeCall: (func: string, param: string) => void;
}
}
@ -75,13 +88,24 @@ export function emitBridgeEvent(event: { func: string; data: Record<string, any>
}
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<BridgeBaseResult>((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<BridgeBaseResult>({ url: '/api/mobile/startMeasure', method: 'POST' });
}
}
static getUploadedRecords(param: { lastId?: number; size: number }) {
if (appWebview) {
return new Promise<BridgeBaseResult<Measurement[]>>((resolve) => {
const res = window.SyncBridgeJS.call('getUploadedRecords', JSON.stringify(param));
resolve(JSON.parse(res));
});
} else {
return httpRequest<BridgeBaseResult<Measurement[]>>({
url: '/api/mobile/getUploadedRecords',
method: 'GET',
params: param
});
}
}
static getUnuploadRecords(param: { lastId?: number; size: number }) {
if (appWebview) {
return new Promise<BridgeBaseResult<Measurement[]>>((resolve) => {
const res = window.SyncBridgeJS.call('getUnuploadRecords', JSON.stringify(param));
resolve(JSON.parse(res));
});
} else {
return httpRequest<BridgeBaseResult<Measurement[]>>({
url: '/api/mobile/getUnuploadRecords',
method: 'GET',
params: param
});
}
}
static searchHistoryRecords(param: { keyword: string }) {
if (appWebview) {
return new Promise<BridgeBaseResult<Measurement[]>>((resolve) => {
const res = window.SyncBridgeJS.call('searchHistoryRecords', JSON.stringify(param));
resolve(JSON.parse(res));
});
} else {
return httpRequest<BridgeBaseResult<Measurement[]>>({
url: '/api/mobile/searchHistoryRecords',
method: 'GET',
params: param
});
}
}
static getMeasurementDetail(param: { id: number }) {
if (appWebview) {
return new Promise<BridgeBaseResult<Measurement>>((resolve) => {
const res = window.SyncBridgeJS.call('getMeasurementDetail', JSON.stringify(param));
resolve(JSON.parse(res));
});
} else {
return httpRequest<BridgeBaseResult<Measurement>>({
url: '/api/mobile/getMeasurementDetail',
method: 'GET',
params: param
});
}
}
static uploadRecords(param: {ids: number[]}) {
if (appWebview) {
return new Promise<BridgeBaseResult>((resolve) => {
const res = window.SyncBridgeJS.call('uploadRecords', JSON.stringify(param));
resolve(JSON.parse(res));
});
} else {
return httpRequest<BridgeBaseResult>({
url: '/api/mobile/uploadRecords',
method: 'POST',
params: param
});
}
}
}

59
src/utils/constant.ts

@ -29,4 +29,61 @@ export const railTypes = [
code: "4",
name: "43轨",
},
];
];
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: "路段四"
}
]
Loading…
Cancel
Save