14 changed files with 515 additions and 159 deletions
-
6public/index.html
-
8src/App.tsx
-
45src/components/MeasureItem.tsx
-
24src/components/MineSegment.tsx
-
68src/components/UnuploadList.tsx
-
33src/components/UploadedList.tsx
-
4src/index.tsx
-
29src/pages/MeasureSave.tsx
-
205src/pages/Mine.tsx
-
13src/services/apiTypes.ts
-
48src/store/features/historySlice.ts
-
2src/store/index.ts
-
130src/utils/bridge.ts
-
57src/utils/constant.ts
@ -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> |
||||
|
); |
||||
|
} |
@ -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> |
||||
|
</> |
||||
|
); |
||||
|
} |
@ -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> |
||||
|
); |
||||
|
} |
@ -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; |
||||
|
}; |
@ -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; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue