238 lines
6.5 KiB
TypeScript
238 lines
6.5 KiB
TypeScript
import Taro from '@tarojs/taro';
|
|
|
|
import { List, PullRefresh } from '@taroify/core';
|
|
import classNames from 'classnames';
|
|
import { isEqual } from 'lodash-es';
|
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
|
import JobCard from '@/components/job-card';
|
|
import ListPlaceholder from '@/components/list-placeholder';
|
|
import { EventName } from '@/constants/app';
|
|
import { JobType, EmployType, SortType } from '@/constants/job';
|
|
import { JobInfo, GetJobsRequest } from '@/types/job';
|
|
import { logWithPrefix } from '@/utils/common';
|
|
import { requestJobList as requestData } from '@/utils/job';
|
|
|
|
import './index.less';
|
|
|
|
interface IRequestProps extends Partial<GetJobsRequest> {}
|
|
|
|
export interface IJobListProps extends IRequestProps {
|
|
visible?: boolean;
|
|
refreshDisabled?: boolean;
|
|
listHeight?: number;
|
|
className?: string;
|
|
onListEmpty?: () => void;
|
|
}
|
|
|
|
const FIRST_PAGE = 0;
|
|
const PAGE_SIZE = 40;
|
|
const PREFIX = 'job-list';
|
|
const log = logWithPrefix(PREFIX);
|
|
|
|
function JobList(props: IJobListProps) {
|
|
const {
|
|
className,
|
|
listHeight,
|
|
refreshDisabled,
|
|
visible = true,
|
|
cityCode = 'ALL',
|
|
category = JobType.All,
|
|
sourceType,
|
|
employType = EmployType.All,
|
|
sortType = SortType.RECOMMEND,
|
|
isFollow = false,
|
|
isOwner = false,
|
|
keyWord,
|
|
latitude,
|
|
longitude,
|
|
minSalary,
|
|
maxSalary,
|
|
blGroupId,
|
|
onListEmpty,
|
|
} = props;
|
|
const [refreshing, setRefreshing] = useState(false);
|
|
const [hasMore, setHasMore] = useState(true);
|
|
const [loadingMore, setLoadingMore] = useState(false);
|
|
const [loadMoreError, setLoadMoreError] = useState(false);
|
|
const [dataList, setDataList] = useState<JobInfo[]>([]);
|
|
const currentPage = useRef<number>(FIRST_PAGE);
|
|
const requestProps = useRef<IRequestProps>({});
|
|
const prevRequestProps = useRef<IRequestProps>({});
|
|
const onListEmptyRef = useRef(onListEmpty);
|
|
|
|
const handleReadJob = useCallback(
|
|
(jobId: string) => {
|
|
const index = dataList.findIndex(d => String(d.id) === jobId);
|
|
if (index < 0) {
|
|
return;
|
|
}
|
|
const job = dataList[index];
|
|
if (!job || job.isRead) {
|
|
return;
|
|
}
|
|
log('auto mark read', jobId);
|
|
dataList.splice(index, 1, { ...job, isRead: true });
|
|
setDataList([...dataList]);
|
|
},
|
|
[dataList]
|
|
);
|
|
|
|
const handleRefresh = useCallback(async () => {
|
|
log('start pull refresh');
|
|
try {
|
|
setRefreshing(true);
|
|
setLoadMoreError(false);
|
|
const { page, hasMore: more, jobResults } = await requestData({ ...requestProps.current, page: 1 });
|
|
setHasMore(more);
|
|
setDataList(jobResults);
|
|
currentPage.current = page;
|
|
!jobResults.length && onListEmptyRef.current?.();
|
|
log('pull refresh success');
|
|
} catch (e) {
|
|
setDataList([]);
|
|
setHasMore(false);
|
|
setLoadMoreError(true);
|
|
currentPage.current = FIRST_PAGE;
|
|
log('pull refresh failed');
|
|
} finally {
|
|
setRefreshing(false);
|
|
}
|
|
}, []);
|
|
|
|
const handleLoadMore = useCallback(async () => {
|
|
log('start load more', hasMore);
|
|
if (!hasMore) {
|
|
return;
|
|
}
|
|
setLoadMoreError(false);
|
|
setLoadingMore(true);
|
|
try {
|
|
const {
|
|
page,
|
|
hasMore: more,
|
|
jobResults,
|
|
} = await requestData({ ...requestProps.current, page: currentPage.current + 1 });
|
|
setDataList([...dataList, ...jobResults]);
|
|
setHasMore(more);
|
|
currentPage.current = page;
|
|
log('load more success');
|
|
} catch (e) {
|
|
setLoadMoreError(true);
|
|
log('load more failed');
|
|
} finally {
|
|
setLoadingMore(false);
|
|
}
|
|
}, [dataList, currentPage, hasMore]);
|
|
|
|
useEffect(() => {
|
|
onListEmptyRef.current = onListEmpty;
|
|
}, [onListEmpty]);
|
|
|
|
useEffect(() => {
|
|
Taro.eventCenter.on(EventName.VIEW_JOB_SUCCESS, handleReadJob);
|
|
return () => {
|
|
Taro.eventCenter.off(EventName.VIEW_JOB_SUCCESS, handleReadJob);
|
|
};
|
|
}, [handleReadJob]);
|
|
|
|
useEffect(() => {
|
|
log('request params changed');
|
|
requestProps.current = {
|
|
category,
|
|
cityCode,
|
|
employType,
|
|
sourceType,
|
|
sortType,
|
|
isFollow,
|
|
isOwner,
|
|
keyWord,
|
|
latitude,
|
|
longitude,
|
|
minSalary,
|
|
maxSalary,
|
|
blGroupId,
|
|
pageSize: PAGE_SIZE,
|
|
};
|
|
}, [
|
|
category,
|
|
cityCode,
|
|
sourceType,
|
|
employType,
|
|
sortType,
|
|
isFollow,
|
|
isOwner,
|
|
keyWord,
|
|
latitude,
|
|
longitude,
|
|
minSalary,
|
|
maxSalary,
|
|
blGroupId,
|
|
]);
|
|
|
|
// 初始化数据&配置变更后刷新数据
|
|
useEffect(() => {
|
|
// 相比前一次可见时没有数据变更时,不再重新请求
|
|
if (isEqual(prevRequestProps.current, requestProps.current)) {
|
|
log('visible/city changed, but request params not change, ignore');
|
|
return;
|
|
}
|
|
// 列表不可见时,先不做处理
|
|
if (!visible) {
|
|
log('visible/city changed, but is not visible, only clear list');
|
|
return;
|
|
}
|
|
|
|
prevRequestProps.current = requestProps.current;
|
|
const refresh = async () => {
|
|
log('visible/city changed, start refresh list data');
|
|
try {
|
|
setDataList([]);
|
|
setLoadingMore(true);
|
|
setLoadMoreError(false);
|
|
const { page, hasMore: more, jobResults } = await requestData({ ...requestProps.current, page: 1 });
|
|
setHasMore(more);
|
|
setDataList(jobResults);
|
|
currentPage.current = page;
|
|
!jobResults.length && onListEmptyRef.current?.();
|
|
} catch (e) {
|
|
setDataList([]);
|
|
setHasMore(false);
|
|
setLoadMoreError(true);
|
|
currentPage.current = FIRST_PAGE;
|
|
} finally {
|
|
log('visible/city changed, refresh list data end');
|
|
setLoadingMore(false);
|
|
}
|
|
};
|
|
refresh();
|
|
}, [visible, cityCode, employType, sourceType, sortType, keyWord, minSalary, maxSalary, blGroupId]);
|
|
|
|
// log('render', `hasMore: ${hasMore}, loadingMore: ${loadingMore}, refreshing: ${refreshing}`);
|
|
|
|
return (
|
|
<PullRefresh
|
|
className={classNames(`${PREFIX}__pull-refresh`, className)}
|
|
loading={refreshing}
|
|
onRefresh={handleRefresh}
|
|
disabled={refreshDisabled}
|
|
>
|
|
<List
|
|
hasMore={hasMore}
|
|
onLoad={handleLoadMore}
|
|
loading={loadingMore || refreshing}
|
|
disabled={loadMoreError}
|
|
fixedHeight={typeof listHeight !== 'undefined'}
|
|
style={listHeight ? { height: `${listHeight}px` } : undefined}
|
|
>
|
|
{dataList.map(item => (
|
|
<JobCard data={item} key={item.id} />
|
|
))}
|
|
<ListPlaceholder hasMore={hasMore} loadingMore={loadingMore} loadMoreError={loadMoreError} />
|
|
</List>
|
|
</PullRefresh>
|
|
);
|
|
}
|
|
|
|
export default JobList;
|