6 changed files with 294 additions and 18 deletions
-
28src/components/GroupItem.tsx
-
63src/components/MeasureGroups.tsx
-
45src/components/MeasurementItem.tsx
-
3src/index.tsx
-
34src/pages/Measure.tsx
-
139src/pages/Mine2.tsx
@ -0,0 +1,28 @@ |
|||
import icon_check_s from '../assets/icon_check_s_s.svg'; |
|||
import icon_check_u from '../assets/icon_check_s_u.svg'; |
|||
|
|||
export default function GroupItem({ |
|||
title, |
|||
editMode = false, |
|||
selected = false, |
|||
onGroupSelect, |
|||
}: { |
|||
title: string; |
|||
editMode?: boolean; |
|||
selected?: boolean; |
|||
onGroupSelect?: () => void; |
|||
}) { |
|||
const select = () => { |
|||
if (editMode) { |
|||
return <img className="w-[14px]" src={selected ? icon_check_s : icon_check_u} alt="icon" />; |
|||
} |
|||
return null; |
|||
}; |
|||
|
|||
return ( |
|||
<div className="px-5 py-2 text-sm flex gap-x-3" onClick={onGroupSelect}> |
|||
{select()} |
|||
<span>{title}</span> |
|||
</div> |
|||
); |
|||
} |
@ -0,0 +1,63 @@ |
|||
import { InfiniteScroll, List } from 'antd-mobile'; |
|||
import { Measurement } from '../services/apiTypes'; |
|||
import { useNavigate } from 'react-router'; |
|||
import GroupItem from './GroupItem'; |
|||
import MeasurementItem from './MeasurementItem'; |
|||
import * as R from 'ramda'; |
|||
|
|||
export default function MeasureGroups({ |
|||
dataList, |
|||
editMode, |
|||
hasMore, |
|||
loadMore, |
|||
onItemSelect, |
|||
onGroupSelect, |
|||
selectedIds, |
|||
}: { |
|||
dataList: Array<{ groupName: string; list: Measurement[] }>; |
|||
editMode: boolean; |
|||
hasMore: boolean; |
|||
loadMore: () => Promise<void>; |
|||
onItemSelect: (groupIndex: number, id: number) => void; |
|||
onGroupSelect: (groupIndex: number) => void; |
|||
selectedIds: Array<number>; |
|||
}) { |
|||
const navigate = useNavigate(); |
|||
|
|||
const isSubset = (ids: number[]) => R.intersection(selectedIds, ids).length === ids.length; |
|||
|
|||
return ( |
|||
<div> |
|||
{dataList.length > 0 ? ( |
|||
<> |
|||
{dataList.map((group, idx) => ( |
|||
<div key={group.groupName}> |
|||
<GroupItem |
|||
title={group.groupName} |
|||
editMode={editMode} |
|||
selected={isSubset(group.list.map((item) => item.id))} |
|||
onGroupSelect={() => onGroupSelect(idx)} |
|||
/> |
|||
<List> |
|||
{group.list.map((item) => ( |
|||
<List.Item key={item.id}> |
|||
<MeasurementItem |
|||
item={item} |
|||
editMode={editMode} |
|||
selected={selectedIds.includes(item.id)} |
|||
onSelected={() => onItemSelect(idx, item.id)} |
|||
onDetail={() => navigate(`/measure/record/${item.id}`)} |
|||
/> |
|||
</List.Item> |
|||
))} |
|||
</List> |
|||
</div> |
|||
))} |
|||
<InfiniteScroll loadMore={loadMore} hasMore={hasMore} /> |
|||
</> |
|||
) : ( |
|||
<p className="absolute text-title left-1/2 top-1/3 -translate-x-1/2">暂无数据</p> |
|||
)} |
|||
</div> |
|||
); |
|||
} |
@ -0,0 +1,45 @@ |
|||
import icon_check_s from '../assets/icon_check_s_s.svg'; |
|||
import icon_check_u from '../assets/icon_check_s_u.svg'; |
|||
import icon_arr from '../assets/icon_arr_p_r.svg'; |
|||
import { Measurement } from '../services/apiTypes'; |
|||
|
|||
export default function MeasurementItem({ |
|||
item, |
|||
editMode, |
|||
selected, |
|||
onDetail, |
|||
onSelected, |
|||
}: { |
|||
item: Measurement; |
|||
editMode: boolean; |
|||
selected: boolean; |
|||
onDetail?: () => void; |
|||
onSelected?: () => void; |
|||
}) { |
|||
return ( |
|||
<div className="flex mx-2 gap-3" onClick={editMode ? onSelected : undefined}> |
|||
{editMode && ( |
|||
<div className="flex justify-center items-center"> |
|||
<img className="w-[14px]" src={selected ? icon_check_s : icon_check_u} alt="icon" /> |
|||
</div> |
|||
)} |
|||
<main className="flex-1"> |
|||
<header className="flex items-center gap-2"> |
|||
<h1 className="text-[15px] font-medium ">{item.name}</h1> |
|||
{/* <span className="text-sm text-[#b7b7b7]">{item.createAt.replace('T',' ').split(' ')[1]}</span> */} |
|||
</header> |
|||
<main className="flex mt-2"> |
|||
<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> |
|||
</main> |
|||
{!editMode && ( |
|||
<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> |
|||
)} |
|||
</div> |
|||
); |
|||
} |
@ -0,0 +1,139 @@ |
|||
import { NavBar, Toast } from 'antd-mobile'; |
|||
import { useEffect, useRef, useState } from 'react'; |
|||
import { Measurement } from '../services/apiTypes'; |
|||
import Bridge from '../utils/bridge'; |
|||
import MeasureGroups from '../components/MeasureGroups'; |
|||
import { MoreOutline } from 'antd-mobile-icons'; |
|||
import * as R from 'ramda'; |
|||
|
|||
const PAGE_SIZE = 10; |
|||
|
|||
const dataList: Array<{ groupName: string; list: Measurement[] }> = [ |
|||
{ |
|||
groupName: '2025-03-02', |
|||
list: [ |
|||
{ |
|||
id: 1, |
|||
name: '测量名称1', |
|||
createAt: '2025-03-02 10:20', |
|||
line: '京沪线', |
|||
section: 'A段', |
|||
direction: '上行', |
|||
railId: 2, |
|||
leftPoints: '[]', |
|||
rightPoints: '[]', |
|||
bureau: '北京铁路局', |
|||
upload: false, |
|||
}, |
|||
{ |
|||
id: 2, |
|||
name: '测量名称2', |
|||
createAt: '2025-03-02 12:22', |
|||
line: '京沪线', |
|||
section: 'B段', |
|||
direction: '下行', |
|||
railId: 2, |
|||
leftPoints: '[]', |
|||
rightPoints: '[]', |
|||
bureau: '北京铁路局', |
|||
upload: false, |
|||
}, |
|||
{ |
|||
id: 3, |
|||
name: '测量名称3', |
|||
createAt: '2025-03-02 12:20', |
|||
line: '京沪线', |
|||
section: 'C段', |
|||
direction: '上行', |
|||
railId: 2, |
|||
leftPoints: '[]', |
|||
rightPoints: '[]', |
|||
bureau: '北京铁路局', |
|||
upload: false, |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
groupName: '2025-02-01', |
|||
list: [ |
|||
{ |
|||
id: 4, |
|||
name: '测量名称2', |
|||
createAt: '2025-03-02 10:20', |
|||
line: '京沪线', |
|||
section: 'D段', |
|||
direction: '下行', |
|||
railId: 2, |
|||
leftPoints: '[]', |
|||
rightPoints: '[]', |
|||
bureau: '北京铁路局', |
|||
upload: false, |
|||
}, |
|||
{ |
|||
id: 5, |
|||
name: '测量名称2', |
|||
createAt: '2025-03-02 10:20', |
|||
line: '京沪线', |
|||
section: 'E段', |
|||
direction: '下行', |
|||
railId: 2, |
|||
leftPoints: '[]', |
|||
rightPoints: '[]', |
|||
bureau: '北京铁路局', |
|||
upload: false, |
|||
}, |
|||
], |
|||
}, |
|||
]; |
|||
|
|||
export default function Mine2() { |
|||
const [editMode, setEditMode] = useState(false); |
|||
const [selectedIds, setSelectedIds] = useState<number[]>([]); |
|||
async function loadMoreRecords() {} |
|||
|
|||
const onItemSelect = (groupIdx: number, id: number) => { |
|||
if (selectedIds.includes(id)) { |
|||
setSelectedIds(R.reject((item) => item === id, selectedIds)); |
|||
} else { |
|||
setSelectedIds([...selectedIds, id]); |
|||
} |
|||
}; |
|||
|
|||
const onGroupSelect = (groupIdx: number) => { |
|||
const ids = dataList[groupIdx].list.map((item) => item.id); |
|||
if (R.intersection(selectedIds, ids).length === ids.length) { |
|||
setSelectedIds(R.difference(selectedIds, ids)); |
|||
} else { |
|||
setSelectedIds(R.union(selectedIds, ids)); |
|||
} |
|||
}; |
|||
|
|||
const right = ( |
|||
<div |
|||
onClick={() => setEditMode(!editMode)} |
|||
className="flex justify-end" |
|||
style={{ fontSize: 24 }} |
|||
> |
|||
<MoreOutline /> |
|||
</div> |
|||
); |
|||
|
|||
return ( |
|||
<div> |
|||
<NavBar className="bg-white" back={null} right={right}> |
|||
我的 |
|||
</NavBar> |
|||
<div className="home-page-content overflow-x-hidden overflow-y-auto"> |
|||
<MeasureGroups |
|||
dataList={dataList} |
|||
editMode={editMode} |
|||
hasMore={false} |
|||
loadMore={loadMoreRecords} |
|||
onItemSelect={onItemSelect} |
|||
onGroupSelect={onGroupSelect} |
|||
selectedIds={selectedIds} |
|||
/> |
|||
</div> |
|||
</div> |
|||
); |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue