You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

177 lines
5.1 KiB

import { ActionSheet, Dialog, InfiniteScroll, List, NavBar, Toast } from 'antd-mobile';
import { useNavigate } from 'react-router-dom';
import { MoreOutline } from 'antd-mobile-icons';
import { useCallback, useEffect, useState } from 'react';
import MeasureItem from '../components/MeasureItem';
import { Measurement } from '../services/apiTypes';
import Bridge, { bridgeOb } from '../utils/bridge';
import { useAppDispatch, useAppSelector } from '../utils/hooks';
import { refreshSyncProgress } from '../store/features/contextSlice';
const PAGE_SIZE = 10;
export default function UploadList() {
const navigate = useNavigate();
const dispatch = useAppDispatch();
const context = useAppSelector((state) => state.context);
const [showMenu, setShowMenu] = useState(false);
const [list, setList] = useState<Measurement[]>([]);
const [noMore, setNoMore] = useState(false);
const actions = [
{
text: '重试上传',
key: 'retry',
onClick: async () => {
setShowMenu(false);
const res = await Bridge.retryFailureSync();
if (!res.success) {
Toast.show(res.message);
}
},
},
{
text: '清空列表',
key: 'clear',
onClick: () => {
setShowMenu(false);
if (context.syncProgress.status === 'uploading') {
Dialog.confirm({
content: '存在未完成的任务,确定清空?',
onConfirm: () => {
clearSyncList();
},
});
} else {
clearSyncList();
}
},
},
{
text: '清空已完成任务',
key: 'clearCompleted',
onClick: async () => {
setShowMenu(false);
const res = await Bridge.clearFinishedSync();
if (res.success) {
loadData();
} else {
Toast.show(res.message);
}
},
},
];
function clearSyncList() {
Bridge.clearSyncList().then((res) => {
if (res.success) {
loadData();
} else {
Toast.show(res.message);
}
});
}
const loadData = useCallback(() => {
dispatch(refreshSyncProgress());
Bridge.getSyncTaskList({ pageNum: 1, size: PAGE_SIZE }).then((res) => {
if (res.success) {
const nList = res.data.list.map((r) => ({ ...r, extraDescObj: JSON.parse(r.extraDesc) }));
setList(nList);
setNoMore(res.data.list.length < PAGE_SIZE);
} else {
Toast.show(res.message);
}
});
}, [dispatch]);
async function loadMoreData() {
const pageNum = Math.floor(list.length / PAGE_SIZE);
const res = await Bridge.getSyncTaskList({ pageNum: pageNum + 1, size: PAGE_SIZE });
if (res.success) {
const nList = res.data.list.map((r) => ({ ...r, extraDescObj: JSON.parse(r.extraDesc) }));
setList(list.concat(nList));
setNoMore(res.data.list.length < PAGE_SIZE);
} else {
Toast.show(res.message);
}
}
useEffect(() => {
loadData();
}, [loadData]);
useEffect(() => {
const subscription = bridgeOb.subscribe((datagram) => {
if (datagram.type === 'sync-item-finish') {
const item = list.find((item) => item.id === datagram.data.id);
if (item) {
item.syncStatus = datagram.data.success ? 'finish' : 'fail';
}
}
});
return () => subscription.unsubscribe();
}, [list]);
const back = () => navigate(-1);
const right = (
<div
onClick={() => setShowMenu(!showMenu)}
className="flex justify-end gap-x-2"
style={{ fontSize: 24 }}
>
<MoreOutline />
</div>
);
const statusText = () => {
if (context.syncProgress.status === 'finished') {
return '完成';
} else if (context.syncProgress.status === 'paused') {
return '暂停';
} else if (context.syncProgress.status === 'uploading') {
return '上传中';
}
};
return (
<div>
<NavBar className="bg-white" onBack={back} right={right}>
</NavBar>
<div className="main-page-content">
<header className="h-8 bg-[#EEE] flex items-center px-4">
<span>{context.syncProgress.remaining}</span>
<i className="border-l border-[#999] h-3 mx-2" />
<span>{context.syncProgress.fail}</span>
<p className="ml-auto text-primary">{statusText()}</p>
</header>
<main
className="relative overflow-x-hidden overflow-y-auto"
style={{ height: 'calc(100% - 32px)' }}
>
{list.length > 0 ? (
<List>
{list.map((item) => (
<List.Item key={item.id}>
<MeasureItem
item={item}
onDetail={() => navigate(`/measure/record/${item.id}`)}
/>
</List.Item>
))}
<InfiniteScroll loadMore={loadMoreData} hasMore={!noMore} />
</List>
) : (
<p className="absolute text-title left-1/2 top-1/3 -translate-x-1/2"></p>
)}
</main>
</div>
<ActionSheet visible={showMenu} actions={actions} onClose={() => setShowMenu(false)} />
</div>
);
}