feat: first commit

This commit is contained in:
eleanor.mao
2025-03-31 22:34:22 +08:00
commit d25187c9c8
390 changed files with 57031 additions and 0 deletions

View File

@ -0,0 +1,163 @@
import { Image } from '@tarojs/components';
import { NodesRef, useDidHide } from '@tarojs/taro';
import { Tabs } from '@taroify/core';
import { ArrowUp, ArrowDown } from '@taroify/icons';
import classNames from 'classnames';
import { useCallback, useEffect, useState } from 'react';
import JobList, { IJobListProps } from '@/components/job-list';
import Overlay from '@/components/overlay';
import SalarySelect from '@/components/salary-select';
import SearchInput from '@/components/search';
import { APP_TAB_BAR_ID, PageUrl } from '@/constants/app';
import { CITY_CODE_TO_NAME_MAP } from '@/constants/city';
import { JOB_TABS, JobType, EmployType, ALL_SORT_TYPES, SORT_TYPE_TITLE_MAP, SortType } from '@/constants/job';
import useListHeight, { IUseListHeightProps } from '@/hooks/use-list-height';
import { SalaryRange } from '@/types/job';
import { Coordinate } from '@/types/location';
import { logWithPrefix } from '@/utils/common';
import { navigateTo } from '@/utils/route';
import './index.less';
interface IProps {
cityCode: string;
sortType: SortType;
employType: EmployType;
coordinate: Coordinate;
onClickCity: () => void;
onClickSort: (type: SortType) => void;
}
const PREFIX = 'all-job-fragment';
const LIST_CONTAINER_CLASS = 'all-job-fragment-list-container';
const CALC_LIST_PROPS: IUseListHeightProps = {
selectors: [`.${LIST_CONTAINER_CLASS}`, `#${APP_TAB_BAR_ID}`],
calc: (rects: [NodesRef.BoundingClientRectCallbackResult, NodesRef.BoundingClientRectCallbackResult]) => {
const [rect, diffRect] = rects;
return diffRect.top - rect.top;
},
};
const log = logWithPrefix(PREFIX);
const NoGroupTips = (props: { className?: string; height?: number }) => {
const { className, height } = props;
return (
<div className={classNames(`${PREFIX}__tips-container`, className)} style={height ? { height } : undefined}>
<Image className={`${PREFIX}__empty-box`} src={require('@/statics/svg/empty-box.svg')} mode="aspectFit" />
<div className={`${PREFIX}__tips-title`}></div>
</div>
);
};
function ListWrapper(props: IJobListProps) {
const { className, listHeight, visible, cityCode, employType, minSalary, maxSalary } = props;
const [isEmpty, setIsEmpty] = useState(false);
const handleListEmpty = useCallback(() => {
setIsEmpty(true);
}, []);
useEffect(() => {
if (visible) {
setIsEmpty(false);
}
}, [visible, cityCode, employType, minSalary, maxSalary]);
if (isEmpty) {
return <NoGroupTips className={className} height={listHeight} />;
}
return <JobList {...props} onListEmpty={handleListEmpty} />;
}
function JobFragment(props: IProps) {
const { cityCode, employType, sortType = SortType.RECOMMEND, coordinate, onClickCity, onClickSort } = props;
const listHeight = useListHeight(CALC_LIST_PROPS);
const [tabType, setTabType] = useState<JobType>(JobType.All);
const [salaryRange, setSalaryRange] = useState<SalaryRange | undefined>();
const [showSalarySelect, setShowSalarySelect] = useState<boolean>(false);
const { latitude, longitude } = coordinate;
const handleClickSearch = useCallback(() => navigateTo(PageUrl.JobSearch, { city: cityCode }), [cityCode]);
const handleClickSalarySelect = useCallback(() => {
setShowSalarySelect(!showSalarySelect);
}, [showSalarySelect]);
const handleHideSalarySelect = useCallback(() => setShowSalarySelect(false), []);
const handleSelectSalary = useCallback((value?: SalaryRange) => {
log('handleSelectSalary', value);
setSalaryRange(value);
setShowSalarySelect(false);
}, []);
const onTypeChange = useCallback(
value => {
log('onTypeChange', value);
setTabType(value);
},
[setTabType]
);
useDidHide(() => setShowSalarySelect(false));
return (
<div className={`${PREFIX}__container`}>
<div className={`${PREFIX}__top-search-bar`}>
<SearchInput disabled className={`${PREFIX}__search`} placeholder="试试 女装" onClick={handleClickSearch} />
<div className={classNames(`${PREFIX}__search-city`)} onClick={onClickCity}>
<div className="title">{CITY_CODE_TO_NAME_MAP.get(cityCode)}</div>
<ArrowDown />
</div>
<div className={classNames(`${PREFIX}__salary-select`)} onClick={handleClickSalarySelect}>
<div className="title"></div>
{showSalarySelect ? <ArrowUp /> : <ArrowDown />}
</div>
<div className={classNames(`${PREFIX}__sort-type`)}>
{ALL_SORT_TYPES.map(type => (
<div
key={type}
className={classNames(`${PREFIX}__sort-item`, { selected: sortType === type })}
onClick={() => onClickSort(type)}
>
{SORT_TYPE_TITLE_MAP[type]}
</div>
))}
</div>
</div>
<Tabs className={`${PREFIX}__type-tabs`} value={tabType} onChange={onTypeChange}>
{JOB_TABS.map(tab => (
<Tabs.TabPane title={tab.title} key={tab.type} value={tab.type}>
<ListWrapper
category={tab.type}
cityCode={cityCode}
sortType={sortType}
latitude={latitude}
longitude={longitude}
employType={employType}
minSalary={salaryRange?.minSalary}
maxSalary={salaryRange?.maxSalary}
listHeight={listHeight}
className={LIST_CONTAINER_CLASS}
visible={tabType === tab.type}
/>
</Tabs.TabPane>
))}
</Tabs>
<Overlay
visible={showSalarySelect}
onClickOuter={handleHideSalarySelect}
outerClassName={`${PREFIX}__overlay-outer`}
innerClassName={`${PREFIX}__overlay-inner`}
>
<SalarySelect type={employType} value={salaryRange} onSelect={handleSelectSalary} />
</Overlay>
</div>
);
}
export default JobFragment;