feat: first commit
This commit is contained in:
217
src/pages/job-search/index.tsx
Normal file
217
src/pages/job-search/index.tsx
Normal file
@ -0,0 +1,217 @@
|
||||
import { Button } from '@tarojs/components';
|
||||
import Taro, { NodesRef, useLoad } from '@tarojs/taro';
|
||||
|
||||
import { Tabs } from '@taroify/core';
|
||||
import { ArrowDown, ArrowUp } from '@taroify/icons';
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import JobList from '@/components/job-list';
|
||||
import JobPicker from '@/components/job-picker';
|
||||
import Overlay from '@/components/overlay';
|
||||
import SafeBottomPadding from '@/components/safe-bottom-padding';
|
||||
import SearchInput from '@/components/search';
|
||||
import { EventName, OpenSource, PageUrl } from '@/constants/app';
|
||||
import { CacheKey } from '@/constants/cache-key';
|
||||
import { CITY_CODE_TO_NAME_MAP } from '@/constants/city';
|
||||
import { JOB_TABS, JobType, EmployType } from '@/constants/job';
|
||||
import useListHeight, { IUseListHeightProps } from '@/hooks/use-list-height';
|
||||
import { logWithPrefix } from '@/utils/common';
|
||||
import { getPageQuery, navigateTo } from '@/utils/route';
|
||||
|
||||
import './index.less';
|
||||
|
||||
const PREFIX = 'page-search-job';
|
||||
const LIST_CLASS = 'search-job-list-container';
|
||||
const SAFE_PADDING_BOTTOM_CLASS = `${PREFIX}__safe-padding-bottom`;
|
||||
const CALC_LIST_PROPS: IUseListHeightProps = {
|
||||
selectors: [`.${LIST_CLASS}`, `.${PREFIX}`, `.${SAFE_PADDING_BOTTOM_CLASS}`],
|
||||
calc: (
|
||||
rects: [
|
||||
NodesRef.BoundingClientRectCallbackResult,
|
||||
NodesRef.BoundingClientRectCallbackResult,
|
||||
NodesRef.BoundingClientRectCallbackResult,
|
||||
]
|
||||
) => {
|
||||
const [listRect, pageRect, safePaddingRect] = rects;
|
||||
return pageRect.bottom - listRect.top - safePaddingRect.height;
|
||||
},
|
||||
};
|
||||
const log = logWithPrefix(PREFIX);
|
||||
|
||||
export default function JobSearch() {
|
||||
const listHeight = useListHeight(CALC_LIST_PROPS);
|
||||
const [focus, setFocus] = useState(true);
|
||||
const [employType, setEmployType] = useState(EmployType.All);
|
||||
const [tabType, setTabType] = useState<JobType>(JobType.All);
|
||||
const [value, setValue] = useState<string>('');
|
||||
const [keyWord, setKeyWord] = useState<string>('');
|
||||
const [cityCode, setCityCode] = useState<string>('');
|
||||
const [showMenu, setShowMenu] = useState<boolean>(false);
|
||||
const historyRef = useRef<string[]>([]);
|
||||
const showContent = keyWord.length > 0;
|
||||
|
||||
const handleUpdateHistory = useCallback((historyKeyword: string) => {
|
||||
const newHistory = [...new Set([historyKeyword, ...historyRef.current])].slice(0, 20);
|
||||
historyRef.current = newHistory;
|
||||
Taro.setStorageSync(CacheKey.JOB_SEARCH_HISTORY, newHistory);
|
||||
log('handleUpdateHistory', newHistory);
|
||||
}, []);
|
||||
|
||||
const handleClickHistoryItem = useCallback(
|
||||
(historyKeyword: string) => {
|
||||
setValue(historyKeyword);
|
||||
setKeyWord(historyKeyword);
|
||||
handleUpdateHistory(historyKeyword);
|
||||
},
|
||||
[handleUpdateHistory]
|
||||
);
|
||||
|
||||
const handleClickSearch = useCallback(() => {
|
||||
if (value === keyWord) {
|
||||
return;
|
||||
}
|
||||
setFocus(false);
|
||||
setKeyWord(value);
|
||||
handleUpdateHistory(value);
|
||||
}, [value, keyWord, handleUpdateHistory]);
|
||||
|
||||
const handleSearchClear = useCallback(() => {
|
||||
setValue('');
|
||||
setKeyWord('');
|
||||
}, []);
|
||||
|
||||
const handleSearchBlur = useCallback(() => setFocus(false), []);
|
||||
|
||||
const handleSearchFocus = useCallback(() => setFocus(true), []);
|
||||
|
||||
const handleSearchChange = useCallback(e => setValue(e.detail.value), []);
|
||||
|
||||
const handleClickCityMenu = useCallback(() => {
|
||||
navigateTo(PageUrl.CitySearch, { city: cityCode, source: OpenSource.JobSearch });
|
||||
}, [cityCode]);
|
||||
|
||||
const handleClickTypeMenu = useCallback(() => {
|
||||
setShowMenu(!showMenu);
|
||||
}, [showMenu]);
|
||||
|
||||
const handleHideMenu = useCallback(() => setShowMenu(false), []);
|
||||
|
||||
const handleCityChange = useCallback(data => {
|
||||
log('handleCityChange', data);
|
||||
const { openSource, cityCode: code } = data;
|
||||
if (openSource !== OpenSource.JobSearch) {
|
||||
return;
|
||||
}
|
||||
setCityCode(code);
|
||||
}, []);
|
||||
|
||||
const handleTypePickerConfirm = useCallback((newEmployType: EmployType) => {
|
||||
log('picker confirm', newEmployType);
|
||||
setEmployType(newEmployType);
|
||||
setShowMenu(false);
|
||||
}, []);
|
||||
|
||||
const onTypeChange = useCallback(type => setTabType(type), [setTabType]);
|
||||
|
||||
useLoad(() => {
|
||||
const query = getPageQuery<{ city: string }>();
|
||||
const currentCity = query.city;
|
||||
if (!currentCity) {
|
||||
return;
|
||||
}
|
||||
setCityCode(currentCity);
|
||||
});
|
||||
|
||||
useLoad(() => {
|
||||
const cacheHistory = Taro.getStorageSync(CacheKey.JOB_SEARCH_HISTORY);
|
||||
log('useLoad', cacheHistory);
|
||||
if (!cacheHistory) {
|
||||
return;
|
||||
}
|
||||
historyRef.current = cacheHistory;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
Taro.eventCenter.on(EventName.SELECT_CITY, handleCityChange);
|
||||
return () => {
|
||||
Taro.eventCenter.off(EventName.SELECT_CITY, handleCityChange);
|
||||
};
|
||||
}, [handleCityChange]);
|
||||
|
||||
const searchAction = (
|
||||
<Button className={`${PREFIX}__search-button`} hoverClass="none" onClick={handleClickSearch}>
|
||||
搜索
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={PREFIX}>
|
||||
<SearchInput
|
||||
focus={focus}
|
||||
value={value}
|
||||
placeholder="你想搜什么"
|
||||
searchAction={searchAction}
|
||||
className={`${PREFIX}__search`}
|
||||
onClear={handleSearchClear}
|
||||
onBlur={handleSearchBlur}
|
||||
onFocus={handleSearchFocus}
|
||||
onSearch={handleClickSearch}
|
||||
onChange={handleSearchChange}
|
||||
/>
|
||||
<div className={`${PREFIX}__page-content`} style={{ visibility: showContent ? 'visible' : 'hidden' }}>
|
||||
<div className={`${PREFIX}__search-filter`}>
|
||||
<div className={classNames(`${PREFIX}__search-filter-menu`)} onClick={handleClickCityMenu}>
|
||||
<div className="title">{CITY_CODE_TO_NAME_MAP.get(cityCode)}</div>
|
||||
<ArrowDown />
|
||||
</div>
|
||||
<div
|
||||
className={classNames(`${PREFIX}__search-filter-menu`, { show: showMenu })}
|
||||
onClick={handleClickTypeMenu}
|
||||
>
|
||||
<div className="title">筛选</div>
|
||||
{showMenu ? <ArrowUp /> : <ArrowDown />}
|
||||
</div>
|
||||
</div>
|
||||
<Overlay
|
||||
visible={showMenu}
|
||||
onClickOuter={handleHideMenu}
|
||||
outerClassName={`${PREFIX}__overlay-outer`}
|
||||
innerClassName={`${PREFIX}__overlay-inner`}
|
||||
>
|
||||
<JobPicker onConfirm={handleTypePickerConfirm} />
|
||||
</Overlay>
|
||||
<Tabs className={`${PREFIX}__type-tabs`} value={tabType} onChange={onTypeChange}>
|
||||
{JOB_TABS.map(tab => (
|
||||
<Tabs.TabPane title={tab.title} key={tab.type} value={tab.type}>
|
||||
<JobList
|
||||
cityCode={cityCode}
|
||||
category={tab.type}
|
||||
employType={employType}
|
||||
keyWord={keyWord}
|
||||
visible={tabType === tab.type}
|
||||
className={LIST_CLASS}
|
||||
listHeight={listHeight}
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
))}
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className={`${PREFIX}__search-history`} style={{ visibility: showContent ? 'hidden' : 'visible' }}>
|
||||
<div className={`${PREFIX}__search-history-title`}>历史搜索</div>
|
||||
<div className={`${PREFIX}__search-history-items`}>
|
||||
{historyRef.current.map(historyKeyword => (
|
||||
<div
|
||||
key={historyKeyword}
|
||||
className={`${PREFIX}__search-history-item`}
|
||||
onClick={() => handleClickHistoryItem(historyKeyword)}
|
||||
>
|
||||
{historyKeyword}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<SafeBottomPadding className={SAFE_PADDING_BOTTOM_CLASS} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user