162 lines
5.6 KiB
TypeScript
162 lines
5.6 KiB
TypeScript
import { Button } from '@tarojs/components';
|
|
import Taro from '@tarojs/taro';
|
|
|
|
import { Fragment, useCallback, useEffect, useRef, useState } from 'react';
|
|
|
|
import SafeBottomPadding from '@/components/safe-bottom-padding';
|
|
import { EventName } from '@/constants/app';
|
|
import { CollectEventName } from '@/constants/event';
|
|
import { ProfileGroupType, ProfileTitleMap } from '@/constants/material';
|
|
import ProfileAdvantagesFragment from '@/fragments/profile/advantages';
|
|
import ProfileBasicFragment from '@/fragments/profile/basic';
|
|
import ProfileExperienceFragment from '@/fragments/profile/experience';
|
|
import ProfileIntentionFragment from '@/fragments/profile/intention';
|
|
import useLocation from '@/hooks/use-location';
|
|
import { MaterialProfile } from '@/types/material';
|
|
import { logWithPrefix } from '@/utils/common';
|
|
import { collectEvent } from '@/utils/event';
|
|
import { isFullTimePriceRequired, isPartTimePriceRequired } from '@/utils/job';
|
|
import { updateProfile, subscribeMaterialMessage } from '@/utils/material';
|
|
import { navigateBack } from '@/utils/route';
|
|
import Toast from '@/utils/toast';
|
|
|
|
import './index.less';
|
|
import ProfileOthersFragment from '@/fragments/profile/others';
|
|
|
|
const PREFIX = 'page-material-create-profile';
|
|
const log = logWithPrefix(PREFIX);
|
|
|
|
const REQUIRE_KEYS = {
|
|
[ProfileGroupType.Basic]: ['name', 'gender', 'age'],
|
|
[ProfileGroupType.Intention]: [
|
|
'cityCodes',
|
|
'employType',
|
|
'fullTimeMinPrice',
|
|
'fullTimeMaxPrice',
|
|
'partyTimeMinPrice',
|
|
'partyTimeMaxPrice',
|
|
],
|
|
[ProfileGroupType.Experience]: [],
|
|
[ProfileGroupType.Advantages]: [],
|
|
[ProfileGroupType.Others]: [],
|
|
};
|
|
|
|
const COULD_SKIP = [ProfileGroupType.Experience, ProfileGroupType.Others];
|
|
|
|
const CONDITIONAL_REQUIRED_KEYS = {
|
|
[ProfileGroupType.Intention]: [
|
|
['fullTimeMinPrice', data => isFullTimePriceRequired(data.employType)],
|
|
['fullTimeMaxPrice', data => isFullTimePriceRequired(data.employType)],
|
|
['partyTimeMinPrice', data => isPartTimePriceRequired(data.employType)],
|
|
['partyTimeMaxPrice', data => isPartTimePriceRequired(data.employType)],
|
|
],
|
|
};
|
|
|
|
const getNextStepGroupType = (curType: ProfileGroupType) => {
|
|
switch (curType) {
|
|
case ProfileGroupType.Basic:
|
|
return ProfileGroupType.Experience;
|
|
case ProfileGroupType.Intention:
|
|
return ProfileGroupType.Basic;
|
|
case ProfileGroupType.Experience:
|
|
return ProfileGroupType.Others;
|
|
case ProfileGroupType.Others:
|
|
return ProfileGroupType.Advantages;
|
|
default:
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const isValidFormData = (type: ProfileGroupType, data: Partial<MaterialProfile>) => {
|
|
const requireKeys = REQUIRE_KEYS[type] || [];
|
|
const conditionalKeys = CONDITIONAL_REQUIRED_KEYS[type] || [];
|
|
|
|
const requiredValidator = (key: any) => typeof data[key] !== 'undefined' && data[key] !== '';
|
|
return (
|
|
requireKeys.every(requiredValidator) &&
|
|
conditionalKeys.every(([key, validator]) => {
|
|
return !validator(data) || requiredValidator(key);
|
|
})
|
|
);
|
|
};
|
|
|
|
export default function MaterialCreateProfile() {
|
|
const location = useLocation();
|
|
const [groupType, setGroupType] = useState<ProfileGroupType>(ProfileGroupType.Intention);
|
|
const ref = useRef<{ getData: () => Partial<MaterialProfile> } | null>(null);
|
|
const ProfileFragment =
|
|
groupType === ProfileGroupType.Basic
|
|
? ProfileBasicFragment
|
|
: groupType === ProfileGroupType.Intention
|
|
? ProfileIntentionFragment
|
|
: groupType === ProfileGroupType.Experience
|
|
? ProfileExperienceFragment
|
|
: groupType === ProfileGroupType.Advantages
|
|
? ProfileAdvantagesFragment
|
|
: groupType === ProfileGroupType.Others
|
|
? ProfileOthersFragment
|
|
: Fragment;
|
|
|
|
const handleSkip = useCallback(() => {
|
|
const nextType = getNextStepGroupType(groupType);
|
|
if (nextType) {
|
|
setGroupType(nextType);
|
|
}
|
|
}, [groupType]);
|
|
|
|
const handleSubmit = useCallback(async () => {
|
|
try {
|
|
const data = ref.current?.getData();
|
|
log('handleSubmit data:', data);
|
|
if (!data) {
|
|
throw new Error('数据异常');
|
|
}
|
|
if (!isValidFormData(groupType, data)) {
|
|
Toast.error('重要选项必填!');
|
|
return;
|
|
}
|
|
|
|
const nextType = getNextStepGroupType(groupType);
|
|
log('handleSubmit nextType:', nextType);
|
|
if (nextType) {
|
|
await updateProfile(data);
|
|
} else {
|
|
// 发起订阅不能在异步任务中,保证是第一个
|
|
await Promise.all([subscribeMaterialMessage(), updateProfile(data)]);
|
|
}
|
|
Taro.eventCenter.trigger(EventName.CREATE_PROFILE);
|
|
nextType ? setGroupType(nextType) : navigateBack(2);
|
|
} catch (e) {
|
|
Toast.error('保存失败请重试');
|
|
collectEvent(CollectEventName.CREATE_MATERIAL_FAILED, e);
|
|
}
|
|
}, [ref, groupType]);
|
|
|
|
useEffect(() => {
|
|
const title = ProfileTitleMap[groupType];
|
|
Taro.setNavigationBarTitle({ title });
|
|
}, [groupType]);
|
|
|
|
const couldSkip = COULD_SKIP.includes(groupType);
|
|
|
|
return (
|
|
<div className={PREFIX}>
|
|
<ProfileFragment ref={ref} profile={{ cityCodes: location.cityCode || '' }} />
|
|
<SafeBottomPadding />
|
|
<div className={`${PREFIX}__footer`}>
|
|
<Button className={`${PREFIX}__submit-btn ${couldSkip ? 'up' : ''}`} onClick={handleSubmit}>
|
|
{groupType === ProfileGroupType.Advantages ? '完成' : '下一步'}
|
|
</Button>
|
|
{couldSkip && (
|
|
<div className={`${PREFIX}__skip-btn-wrapper`}>
|
|
<div className={`${PREFIX}__skip-btn`} onClick={handleSkip}>
|
|
跳过
|
|
</div>
|
|
</div>
|
|
)}
|
|
<SafeBottomPadding />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|