feat: first commit
This commit is contained in:
147
src/fragments/job/base/index.less
Normal file
147
src/fragments/job/base/index.less
Normal file
@ -0,0 +1,147 @@
|
||||
@import '@/styles/variables.less';
|
||||
|
||||
.all-job-fragment {
|
||||
&__container {
|
||||
position: relative;
|
||||
|
||||
.all-job-fragment__type-tabs {
|
||||
padding: 0 20px;
|
||||
margin-top: 20px;
|
||||
|
||||
.taroify-tabs__wrap {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.taroify-tabs__wrap__scroll {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.taroify-tabs__tab {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 0 0 auto !important;
|
||||
font-size: 28px;
|
||||
--tab-color: @blColorG1;
|
||||
--tabs-active-color: @blColor;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.taroify-tabs__line {
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.taroify-tabs__content {
|
||||
padding: 20px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__top-search-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
&__search {
|
||||
flex: 1 0;
|
||||
}
|
||||
|
||||
&__salary-select,
|
||||
&__search-city {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
font-size: 28px;
|
||||
line-height: 32px;
|
||||
color: @blColor;
|
||||
|
||||
margin-left: 20px;
|
||||
|
||||
.title {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&__sort-type {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-left: solid 2px #E0E0E0;
|
||||
|
||||
font-size: 28px;
|
||||
line-height: 32px;
|
||||
color: @blColor;
|
||||
|
||||
margin-left: 20px;
|
||||
|
||||
.selected {
|
||||
color: @blHighlightColor;
|
||||
}
|
||||
}
|
||||
|
||||
&__sort-item {
|
||||
margin-left: 32px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__overlay-outer {
|
||||
top: 82px;
|
||||
}
|
||||
|
||||
&__overlay-inner {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__tips-container {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding-top: 218px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__empty-box {
|
||||
width: 386px;
|
||||
height: 278px;
|
||||
}
|
||||
|
||||
&__tips-title {
|
||||
font-size: 28px;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
color: @blColor;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
&__tips-desc {
|
||||
font-size: 24px;
|
||||
line-height: 40px;
|
||||
color: @blColorG1;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&__add-group-button {
|
||||
font-size: 28px;
|
||||
line-height: 40px;
|
||||
color: #FFFFFF;
|
||||
background-color: @blHighlightColor;
|
||||
border-radius: 48px;
|
||||
padding: 20px 30px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
163
src/fragments/job/base/index.tsx
Normal file
163
src/fragments/job/base/index.tsx
Normal 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;
|
||||
29
src/fragments/job/follow/index.less
Normal file
29
src/fragments/job/follow/index.less
Normal file
@ -0,0 +1,29 @@
|
||||
@import '@/styles/variables.less';
|
||||
|
||||
.my-follow-job-fragment {
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
|
||||
&__empty-container {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding-top: 338px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__empty-box {
|
||||
width: 386px;
|
||||
height: 278px;
|
||||
}
|
||||
|
||||
&__empty-text {
|
||||
font-size: 28px;
|
||||
line-height: 40px;
|
||||
font-weight: 400;
|
||||
color: @blColor;
|
||||
margin-top: 34px;
|
||||
}
|
||||
}
|
||||
72
src/fragments/job/follow/index.tsx
Normal file
72
src/fragments/job/follow/index.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import { Image } from '@tarojs/components';
|
||||
import { NodesRef } from '@tarojs/taro';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import JobList from '@/components/job-list';
|
||||
import { APP_TAB_BAR_ID } from '@/constants/app';
|
||||
import useListHeight, { IUseListHeightProps } from '@/hooks/use-list-height';
|
||||
import { logWithPrefix } from '@/utils/common';
|
||||
|
||||
import './index.less';
|
||||
|
||||
interface IProps {
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
const PREFIX = 'my-follow-job-fragment';
|
||||
const LIST_CONTAINER_CLASS = 'my-follow-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 NoFollowTips = (props: { className?: string; height?: number }) => {
|
||||
const { className, height } = props;
|
||||
return (
|
||||
<div className={classNames(`${PREFIX}__empty-container`, className)} style={height ? { height } : undefined}>
|
||||
<Image className={`${PREFIX}__empty-box`} src={require('@/statics/svg/empty-box.svg')} mode="aspectFit" />
|
||||
<div className={`${PREFIX}__empty-text`}>暂无关注的群</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function MyFollowFragment(props: IProps) {
|
||||
const { visible } = props;
|
||||
const [isEmpty, setIsEmpty] = useState(false);
|
||||
const listHeight = useListHeight(CALC_LIST_PROPS);
|
||||
log('list height', listHeight);
|
||||
|
||||
const handleListEmpty = useCallback(() => {
|
||||
setIsEmpty(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setIsEmpty(false);
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
if (isEmpty) {
|
||||
return <NoFollowTips className={LIST_CONTAINER_CLASS} height={listHeight} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={PREFIX}>
|
||||
<JobList
|
||||
visible
|
||||
isFollow
|
||||
className={LIST_CONTAINER_CLASS}
|
||||
listHeight={listHeight}
|
||||
onListEmpty={handleListEmpty}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyFollowFragment;
|
||||
87
src/fragments/job/my-group/index.less
Normal file
87
src/fragments/job/my-group/index.less
Normal file
@ -0,0 +1,87 @@
|
||||
@import '@/styles/variables.less';
|
||||
|
||||
.my-group-job-fragment {
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
|
||||
&__tips-container{
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding-top: 268px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__tips-content {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
padding: 48px;
|
||||
font-weight: 400;
|
||||
font-size: 24px;
|
||||
line-height: 40px;
|
||||
color: @blColorG1;
|
||||
background: #FFFFFF;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
&__tips-content-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #6D3DF517;
|
||||
border-radius: 16px;
|
||||
transform: rotate(-6deg);
|
||||
transform-origin: 50% bottom;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&__tips-first-panel {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
&__tips-title {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
font-size: 28px;
|
||||
color: @blColor;
|
||||
margin-bottom: 16px;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
height: 10px;
|
||||
background: #6D3DF599;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&__add-group-button {
|
||||
font-size: 28px;
|
||||
line-height: 40px;
|
||||
color: #FFFFFF;
|
||||
background-color: @blHighlightColor;
|
||||
border-radius: 48px;
|
||||
padding: 20px 30px;
|
||||
margin-top: 48px;
|
||||
}
|
||||
|
||||
&__float-add-group-button {
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
bottom: 56px;
|
||||
width: 112px;
|
||||
height: 112px;
|
||||
font-size: 28px;
|
||||
line-height: 112px;
|
||||
white-space: nowrap;
|
||||
border-radius: 50%;
|
||||
color: #FFFFFF;
|
||||
background-color: @blHighlightColor;
|
||||
}
|
||||
}
|
||||
107
src/fragments/job/my-group/index.tsx
Normal file
107
src/fragments/job/my-group/index.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
import { Button } from '@tarojs/components';
|
||||
import { NodesRef } from '@tarojs/taro';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import JobList from '@/components/job-list';
|
||||
import ProductGroupDialog from '@/components/product-group-dialog';
|
||||
import { APP_TAB_BAR_ID } from '@/constants/app';
|
||||
import useListHeight, { IUseListHeightProps } from '@/hooks/use-list-height';
|
||||
import { logWithPrefix } from '@/utils/common';
|
||||
|
||||
import './index.less';
|
||||
|
||||
interface IProps {
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
const PREFIX = 'my-group-job-fragment';
|
||||
const LIST_CONTAINER_CLASS = 'my-group-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: { onClick: () => void; className?: string; height?: number }) => {
|
||||
const { className, height, onClick } = props;
|
||||
return (
|
||||
<div className={classNames(`${PREFIX}__tips-container`, className)} style={height ? { height } : undefined}>
|
||||
<div className={`${PREFIX}__tips-content`}>
|
||||
<div className={`${PREFIX}__tips-content-bg`} />
|
||||
<div className={`${PREFIX}__tips-first-panel`}>
|
||||
<div className={`${PREFIX}__tips-title`}>找不到您所在群的通告?</div>
|
||||
<div>点击下方添加群,联系客服</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={`${PREFIX}__tips-title`}>添加群后,您可以</div>
|
||||
<div>·在播络小程序即可查看群里去重后的所有通告</div>
|
||||
<div>·通过类目、地址等关键信息查找群内通告</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button className={`${PREFIX}__add-group-button`} onClick={onClick}>
|
||||
免费添加群
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function MyGroup(props: IProps) {
|
||||
const { visible } = props;
|
||||
const listHeight = useListHeight(CALC_LIST_PROPS);
|
||||
const [isEmpty, setIsEmpty] = useState(false);
|
||||
const [dialogVisible, setDialogVisible] = useState(false);
|
||||
log('list height', listHeight);
|
||||
|
||||
const handleListEmpty = useCallback(() => {
|
||||
setIsEmpty(true);
|
||||
}, []);
|
||||
|
||||
const handleAddGroup = useCallback(() => {
|
||||
setDialogVisible(true);
|
||||
// Taro.eventCenter.trigger(EventName.ADD_GROUP);
|
||||
}, []);
|
||||
|
||||
const handleDialogHidden = useCallback(() => {
|
||||
setDialogVisible(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setIsEmpty(false);
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
const dialog = (
|
||||
<ProductGroupDialog
|
||||
visible={dialogVisible}
|
||||
onClose={handleDialogHidden}
|
||||
style={{ position: 'absolute', top: isEmpty ? '30%' : '40%' }}
|
||||
/>
|
||||
);
|
||||
|
||||
if (isEmpty) {
|
||||
return (
|
||||
<>
|
||||
<NoGroupTips className={LIST_CONTAINER_CLASS} height={listHeight} onClick={handleAddGroup} />
|
||||
{dialog}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={PREFIX}>
|
||||
<JobList visible isOwner className={LIST_CONTAINER_CLASS} listHeight={listHeight} onListEmpty={handleListEmpty} />
|
||||
<Button className={`${PREFIX}__float-add-group-button`} onClick={handleAddGroup}>
|
||||
添加群
|
||||
</Button>
|
||||
{dialog}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyGroup;
|
||||
Reference in New Issue
Block a user