feat: first commit
This commit is contained in:
3
src/pages/job-publish/index.config.ts
Normal file
3
src/pages/job-publish/index.config.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '发布通告',
|
||||
});
|
60
src/pages/job-publish/index.less
Normal file
60
src/pages/job-publish/index.less
Normal file
@ -0,0 +1,60 @@
|
||||
@import '@/styles/common.less';
|
||||
@import '@/styles/variables.less';
|
||||
|
||||
.page-job-publish {
|
||||
padding: 24px;
|
||||
// 底部高度(paddingTop: 30 + tips: 30 + margin: 40 + button: 80 + margin: 40);
|
||||
padding-bottom: 220px;
|
||||
|
||||
&__reason {
|
||||
width: 100%;;
|
||||
padding: 16px 32px;
|
||||
font-size: 26px;
|
||||
line-height: 36px;
|
||||
font-weight: 400;
|
||||
color: #946724;
|
||||
background: #FFF4F0;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
position: fixed;
|
||||
left: 24px;
|
||||
right: 24px;
|
||||
bottom: 0;
|
||||
background: #F5F6FA;
|
||||
padding-top: 30px;
|
||||
.flex-column();
|
||||
|
||||
&__tips {
|
||||
font-size: 28px;
|
||||
line-height: 30px;
|
||||
font-weight: 400;
|
||||
color: @blColorG1;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
width: 100%;
|
||||
.flex-row();
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
&__button {
|
||||
.button(@width: 100%, @height: 80px, @fontSize: 32px, @fontWeight: 400, @borderRadius: 48px);
|
||||
flex: 1;
|
||||
margin-left: 24px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&.lowLight {
|
||||
color: @blHighlightColor;
|
||||
background: @blHighlightBg;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
312
src/pages/job-publish/index.tsx
Normal file
312
src/pages/job-publish/index.tsx
Normal file
@ -0,0 +1,312 @@
|
||||
import { BaseEventOrig, Button, InputProps } from '@tarojs/components';
|
||||
import Taro, { useLoad } from '@tarojs/taro';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import BlFormCell from '@/components/bl-form-cell';
|
||||
import BlFormInput from '@/components/bl-form-input';
|
||||
import BlFormItem from '@/components/bl-form-item';
|
||||
import { BlFormRadio, BlFormRadioGroup } from '@/components/bl-form-radio';
|
||||
import BlFormSelect from '@/components/bl-form-select';
|
||||
import BlSalaryInput, { BlSalaryValue } from '@/components/bl-salary-input';
|
||||
import { CityPickerPopup } from '@/components/city-picker';
|
||||
import PageLoading from '@/components/page-loading';
|
||||
import SafeBottomPadding from '@/components/safe-bottom-padding';
|
||||
import { EventName, PageUrl } from '@/constants/app';
|
||||
import { CollectEventName } from '@/constants/event';
|
||||
import { EMPLOY_TYPE_TITLE_MAP, EmployType, JOB_TYPE_SELECT_OPTIONS, JobType } from '@/constants/job';
|
||||
import { CreateJobInfo, JobDetails } from '@/types/job';
|
||||
import { logWithPrefix } from '@/utils/common';
|
||||
import { collectEvent } from '@/utils/event';
|
||||
import { postCloseJob, postCreateJob, postUpdateJob, postPublishJob, requestJobDetail, isFullTimePriceRequired, isPartTimePriceRequired } from '@/utils/job';
|
||||
import { getCityValues } from '@/utils/location';
|
||||
import { getPageQuery, navigateBack, navigateTo } from '@/utils/route';
|
||||
import Toast from '@/utils/toast';
|
||||
|
||||
import './index.less';
|
||||
|
||||
const PREFIX = 'page-job-publish';
|
||||
const log = logWithPrefix(PREFIX);
|
||||
|
||||
const isInvalidCreateJobInfo = (data: CreateJobInfo) => {
|
||||
const {
|
||||
title,
|
||||
category,
|
||||
employType,
|
||||
provinceCode,
|
||||
cityCode,
|
||||
countyCode,
|
||||
address,
|
||||
jobDescription,
|
||||
lowPriceForFullTime,
|
||||
highPriceForFullTime,
|
||||
lowPriceForPartyTime,
|
||||
highPriceForPartyTime,
|
||||
} = data;
|
||||
if (!category) {
|
||||
return '请选择通告品类';
|
||||
}
|
||||
if (!title) {
|
||||
return '请选择通告标题';
|
||||
}
|
||||
if (!jobDescription) {
|
||||
return '请输入通告描述';
|
||||
}
|
||||
if (isFullTimePriceRequired(employType)) {
|
||||
if (!lowPriceForFullTime || !highPriceForFullTime) {
|
||||
return '薪资范围不能为空';
|
||||
}
|
||||
if (lowPriceForFullTime > highPriceForFullTime) {
|
||||
return '薪资最高值不能低于最低值';
|
||||
}
|
||||
}
|
||||
if (isPartTimePriceRequired(employType)) {
|
||||
if (!lowPriceForPartyTime || !highPriceForPartyTime) {
|
||||
return '薪资范围不能为空';
|
||||
}
|
||||
if (lowPriceForPartyTime > highPriceForPartyTime) {
|
||||
return '薪资最高值不能低于最低值';
|
||||
}
|
||||
}
|
||||
|
||||
if (!provinceCode || !cityCode || !countyCode) {
|
||||
return '请输入工作地址';
|
||||
}
|
||||
if (!address) {
|
||||
return '请输入详细地址';
|
||||
}
|
||||
};
|
||||
|
||||
export default function JobPublish() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isUpdate, setIsUpdate] = useState(false);
|
||||
const [job, setJob] = useState<JobDetails | null>(null);
|
||||
const [reason, setReason] = useState('');
|
||||
const [employType, setEmployType] = useState(EmployType.Full);
|
||||
const [category, setCategory] = useState<JobType>(JobType.Finery);
|
||||
const [title, setTitle] = useState('');
|
||||
const [describe, setDescribe] = useState('');
|
||||
const [salary, setSalary] = useState<BlSalaryValue>(['', '', '', ''] as unknown as BlSalaryValue);
|
||||
const [city, setCity] = useState<string[] | undefined>();
|
||||
const [showCityPicker, setShowCityPicker] = useState(false);
|
||||
const [address, setAddress] = useState('');
|
||||
|
||||
const handleEmployTypeChange = useCallback((value: EmployType) => {
|
||||
setEmployType(value);
|
||||
}, []);
|
||||
|
||||
const handleSelectCategory = useCallback((value: JobType) => {
|
||||
setCategory(value);
|
||||
}, []);
|
||||
|
||||
const handleInputTitle = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
|
||||
const value = e.detail.value || '';
|
||||
setTitle(value);
|
||||
}, []);
|
||||
|
||||
const handleClickEditDescribe = useCallback(() => navigateTo(PageUrl.JobPublishDescribe, { describe }), [describe]);
|
||||
|
||||
const handleSalaryChange = useCallback((value: BlSalaryValue) => {
|
||||
setSalary(value);
|
||||
}, []);
|
||||
|
||||
const handleClickCity = useCallback(() => setShowCityPicker(true), []);
|
||||
|
||||
const handleConfirmCityPicker = useCallback((areaValues: string[]) => {
|
||||
log('handleConfirmCityPicker', areaValues);
|
||||
setShowCityPicker(false);
|
||||
setCity(areaValues);
|
||||
setAddress('');
|
||||
}, []);
|
||||
|
||||
const handleInputAddress = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
|
||||
const value = e.detail.value || '';
|
||||
setAddress(value);
|
||||
}, []);
|
||||
|
||||
const handleClose = useCallback(async () => {
|
||||
if (!job) {
|
||||
Toast.error('数据出错请重试');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Taro.showLoading();
|
||||
await postCloseJob(job.id);
|
||||
Taro.eventCenter.trigger(EventName.COMPANY_JOB_PUBLISH_CHANGED);
|
||||
navigateBack();
|
||||
} catch (e) {
|
||||
console.error('submit error', e);
|
||||
Toast.error('关闭失败请重试');
|
||||
collectEvent(CollectEventName.CLOSE_JOB_FAILED, e);
|
||||
} finally {
|
||||
Taro.hideLoading();
|
||||
}
|
||||
}, [job]);
|
||||
|
||||
const handleSubmit = useCallback(async () => {
|
||||
const cityCodes = city || [];
|
||||
const data: CreateJobInfo = {
|
||||
title,
|
||||
employType,
|
||||
category: category!,
|
||||
jobDescription: describe,
|
||||
provinceCode: cityCodes[0],
|
||||
cityCode: cityCodes[1],
|
||||
countyCode: cityCodes[2],
|
||||
lowPriceForFullTime: !isFullTimePriceRequired(employType) ? 0 : salary[0] * 1000,
|
||||
highPriceForFullTime: !isFullTimePriceRequired(employType) ? 0 : salary[1] * 1000,
|
||||
lowPriceForPartyTime: !isPartTimePriceRequired(employType) ? 0 : salary[2],
|
||||
highPriceForPartyTime: !isPartTimePriceRequired(employType) ? 0 : salary[3],
|
||||
address: address,
|
||||
};
|
||||
const errMsg = isInvalidCreateJobInfo(data);
|
||||
if (errMsg) {
|
||||
Toast.info(errMsg);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Taro.showLoading();
|
||||
// 将省市区拼到最前面
|
||||
const cityValues = getCityValues(cityCodes);
|
||||
if (!data.address.startsWith(cityValues)) {
|
||||
data.address = `${cityValues}${data.address}`;
|
||||
}
|
||||
const isUpdateJob = isUpdate && job;
|
||||
if (isUpdateJob) {
|
||||
data.jobId = job!.id;
|
||||
await postUpdateJob(data);
|
||||
Taro.eventCenter.trigger(EventName.JOB_UPDATE, job!.id);
|
||||
} else {
|
||||
await postCreateJob(data);
|
||||
}
|
||||
Taro.eventCenter.trigger(EventName.COMPANY_JOB_PUBLISH_CHANGED);
|
||||
await Toast.success(isUpdateJob ? '更新成功' : '创建成功', 1500, true);
|
||||
navigateBack();
|
||||
} catch (e) {
|
||||
console.error('submit error', e);
|
||||
Toast.error('审核失败请重试');
|
||||
collectEvent(CollectEventName.PUBLISH_JOB_FAILED, e);
|
||||
} finally {
|
||||
Taro.hideLoading();
|
||||
}
|
||||
}, [isUpdate, job, title, employType, category, describe, city, salary, address]);
|
||||
|
||||
useEffect(() => {
|
||||
const callback = (d: string) => setDescribe(d);
|
||||
Taro.eventCenter.on(EventName.EDIT_JOB_DESCRIBE, callback);
|
||||
return () => {
|
||||
Taro.eventCenter.off(EventName.EDIT_JOB_DESCRIBE, callback);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLoad(async () => {
|
||||
const query = getPageQuery<{ jobId: string }>();
|
||||
const jobId = query?.jobId;
|
||||
if (!jobId) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setLoading(true);
|
||||
setIsUpdate(true);
|
||||
const details = await requestJobDetail(jobId);
|
||||
setJob(details);
|
||||
details.title && setTitle(details.title);
|
||||
details.employType && setEmployType(details.employType);
|
||||
details.category && setCategory(details.category);
|
||||
details.jobDescription && setDescribe(details.jobDescription);
|
||||
details.jobLocation.provinceCode &&
|
||||
details.jobLocation.cityCode &&
|
||||
details.jobLocation.countyCode &&
|
||||
setCity([details.jobLocation.provinceCode, details.jobLocation.cityCode, details.jobLocation.countyCode]);
|
||||
details.lowPriceForFullTime &&
|
||||
details.highPriceForFullTime &&
|
||||
details.lowPriceForPartyTime &&
|
||||
details.highPriceForPartyTime &&
|
||||
setSalary([
|
||||
details.lowPriceForFullTime / 1000,
|
||||
details.highPriceForFullTime / 1000,
|
||||
details.lowPriceForPartyTime,
|
||||
details.highPriceForPartyTime,
|
||||
]);
|
||||
details.jobLocation.address && setAddress(details.jobLocation.address);
|
||||
details.verifyFailReason && setReason(details.verifyFailReason);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
Toast.error('出错了,请重试');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return <PageLoading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={PREFIX}>
|
||||
{reason && <div className={`${PREFIX}__reason`}>{reason}</div>}
|
||||
<BlFormItem title="通告标题" subTitle="不能填写微信、电话等联系方式">
|
||||
<BlFormInput
|
||||
value={title}
|
||||
maxlength={20}
|
||||
placeholder="如“招聘女装主播”"
|
||||
onInput={handleInputTitle}
|
||||
maxLengthTips
|
||||
/>
|
||||
</BlFormItem>
|
||||
<BlFormItem title="工作类型" subTitle={false}>
|
||||
<BlFormRadioGroup direction="horizontal" value={employType} onChange={handleEmployTypeChange}>
|
||||
<BlFormRadio name={EmployType.Full} text={EMPLOY_TYPE_TITLE_MAP[EmployType.Full]} value={employType} />
|
||||
<BlFormRadio name={EmployType.Part} text={EMPLOY_TYPE_TITLE_MAP[EmployType.Part]} value={employType} />
|
||||
<BlFormRadio name={EmployType.All} text={EMPLOY_TYPE_TITLE_MAP[EmployType.All]} value={employType} />
|
||||
</BlFormRadioGroup>
|
||||
</BlFormItem>
|
||||
<BlFormItem title="品类" subTitle={false}>
|
||||
<BlFormSelect title="品类" value={category} options={JOB_TYPE_SELECT_OPTIONS} onSelect={handleSelectCategory} />
|
||||
</BlFormItem>
|
||||
<BlFormItem title="薪资范围" subTitle={false} dynamicHeight>
|
||||
<BlSalaryInput value={salary} employType={employType} onChange={handleSalaryChange} />
|
||||
</BlFormItem>
|
||||
<BlFormItem title="通告描述" subTitle={false}>
|
||||
<BlFormCell text={describe} placeholder="介绍工作内容、通告要求、加分项" onClick={handleClickEditDescribe} />
|
||||
</BlFormItem>
|
||||
<BlFormItem title="工作城市" subTitle={false}>
|
||||
<BlFormCell text={getCityValues(city, '-')} placeholder="工作所在省-城-区" onClick={handleClickCity} />
|
||||
</BlFormItem>
|
||||
<BlFormItem title="详细地址" subTitle={false}>
|
||||
<BlFormInput value={address} placeholder="请填写详细地址" onInput={handleInputAddress} />
|
||||
</BlFormItem>
|
||||
<SafeBottomPadding />
|
||||
<div className={`${PREFIX}__footer`}>
|
||||
<div className={`${PREFIX}__footer__tips`}>注:通告标题和工作城市发布后不可修改</div>
|
||||
<div className={`${PREFIX}__footer__buttons`}>
|
||||
{!isUpdate && (
|
||||
<Button className={`${PREFIX}__footer__button`} onClick={handleSubmit}>
|
||||
发布
|
||||
</Button>
|
||||
)}
|
||||
{isUpdate && (
|
||||
<>
|
||||
<Button className={classNames(`${PREFIX}__footer__button`, 'lowLight')} onClick={handleClose}>
|
||||
关闭
|
||||
</Button>
|
||||
<Button className={`${PREFIX}__footer__button`} onClick={handleSubmit}>
|
||||
更新
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<SafeBottomPadding />
|
||||
</div>
|
||||
<div>
|
||||
<CityPickerPopup
|
||||
areaValues={city}
|
||||
open={showCityPicker}
|
||||
onConfirm={handleConfirmCityPicker}
|
||||
onCancel={() => setShowCityPicker(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user