feat: 模卡更新

This commit is contained in:
chashaobao
2025-11-03 22:18:39 +08:00
parent fde2027588
commit 5e3e6903cb
31 changed files with 479 additions and 193 deletions

View File

@ -6,7 +6,7 @@ import { Provider } from 'react-redux';
import { REFRESH_UNREAD_COUNT_TIME } from '@/constants/message'; import { REFRESH_UNREAD_COUNT_TIME } from '@/constants/message';
import http from '@/http'; import http from '@/http';
import store from '@/store'; import store from '@/store';
import { requestCityOperators } from '@/utils/location'; import { requestCityConfigs } from '@/utils/location';
import { requestUnreadMessageCount } from '@/utils/message'; import { requestUnreadMessageCount } from '@/utils/message';
import { decryptOpenGid, getInviteCode, getInviteCodeFromQuery } from '@/utils/partner'; import { decryptOpenGid, getInviteCode, getInviteCodeFromQuery } from '@/utils/partner';
import qiniuUpload from '@/utils/qiniu-upload'; import qiniuUpload from '@/utils/qiniu-upload';
@ -30,7 +30,7 @@ function App({ children }: PropsWithChildren<BL.Anything>) {
}); });
useDidShow(options => { useDidShow(options => {
requestCityOperators(); requestCityConfigs();
console.log(options); console.log(options);
const inviteCode = getInviteCodeFromQuery(options?.query || {}); const inviteCode = getInviteCodeFromQuery(options?.query || {});
@ -48,7 +48,7 @@ function App({ children }: PropsWithChildren<BL.Anything>) {
// @ts-expect-error // @ts-expect-error
encryptedData: info.encryptedData, encryptedData: info.encryptedData,
}); });
console.log('哈哈哈', info); console.log('getGroupEnterInfo', info);
}) })
.catch(() => { .catch(() => {
console.log('没有解析到群', options?.scene); console.log('没有解析到群', options?.scene);

View File

@ -2,20 +2,36 @@
@import '@/styles/variables.less'; @import '@/styles/variables.less';
.profile-checkbox { .profile-checkbox {
&__group { &__group {
width: 100%; width: 100%;
height: 100%; height: 100%;
.flex-row(); .flex-row();
flex-wrap: wrap;
gap: 24px;
} }
&__item { &__item {
flex: 1; flex: 0 1 auto;
height: 100%; padding: 34px 36px;
} line-height: 32px;
border-radius: 16px;
border: 2px solid #fff;
background: #fff;
margin-right: 0;
margin-bottom: 0;
box-sizing: border-box;
&.active {
background: rgba(109, 61, 245, 0.12);
border: 2px solid #6d3df5;
&__icon { .taroify-checkbox__label {
width: 36px; color: #6d3df5;
height: 36px; }
}
.taroify-checkbox__label {
line-height: 32px;
margin-left: 0;
}
} }
} }

View File

@ -1,42 +1,44 @@
import { Image } from '@tarojs/components';
import { Checkbox } from '@taroify/core'; import { Checkbox } from '@taroify/core';
import { CheckboxProps, CheckboxGroupProps } from '@taroify/core/checkbox'; import { CheckboxProps, CheckboxGroupProps } from '@taroify/core/checkbox';
import CheckboxGroupContext from '@taroify/core/checkbox/checkbox-group.context';
import { useContext } from 'react';
import './index.less'; import './index.less';
interface IProps extends CheckboxProps { interface IProps extends CheckboxProps {
text: string; text: string;
}
interface IGroupProps extends CheckboxGroupProps {
value: BL.Anything[]; value: BL.Anything[];
} }
interface IGroupProps extends CheckboxGroupProps {}
const PREFIX = 'profile-checkbox'; const PREFIX = 'profile-checkbox';
export function BlCheckboxGroup(props: IGroupProps) { export function BlCheckboxGroup({ value, onChange, children }: IGroupProps) {
return <Checkbox.Group className={`${PREFIX}__group`} direction="horizontal" {...props} />; return (
<Checkbox.Group className={`${PREFIX}__group`} value={value} onChange={onChange}>
{children}
</Checkbox.Group>
);
} }
export function BlCheckbox(props: IProps) { export function BlCheckbox(props: IProps) {
const { name, text, value } = props; const { name, text } = props;
const { value: names = [], onChange: onNamesChange } = useContext(CheckboxGroupContext);
const checked = names.includes(name);
const handleClick = () => {
if (!name) return;
if (checked) {
onNamesChange?.(names.filter(aName => aName !== name));
} else {
onNamesChange?.([...names, name]);
}
};
return ( return (
<Checkbox <Checkbox className={`${PREFIX}__item ${checked ? 'active' : ''}`} name={name} icon={null} onClick={handleClick}>
className={`${PREFIX}__item`}
name={name}
icon={
<Image
className={`${PREFIX}__icon`}
mode="aspectFit"
src={
value.includes(name)
? require('@/statics/svg/radio-checked.svg')
: require('@/statics/svg/radio-uncheck.svg')
}
/>
}
>
{text} {text}
</Checkbox> </Checkbox>
); );

View File

@ -36,7 +36,7 @@
padding-right: 9px; padding-right: 9px;
font-size: 64px; font-size: 64px;
line-height: 62px; line-height: 62px;
background: linear-gradient(87.53deg, #683DE3 0.4%, #39227D 84.55%); background: linear-gradient(87.53deg, #683de3 0.4%, #39227d 84.55%);
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
background-clip: text; background-clip: text;
@ -87,31 +87,48 @@
box-sizing: border-box; box-sizing: border-box;
height: 130px; height: 130px;
padding: 24px 32px; padding: 24px 32px;
&-time { &-top-line {
.flex-row();
font-style: normal; font-style: normal;
font-size: 28px; font-size: 28px;
line-height: 40px; line-height: 40px;
color: #333333; color: #333333;
font-weight: 400; font-weight: 400;
position: relative; position: relative;
.noWrap();
}
&-id {
.id {
color: #999999;
display: inline-block;
padding-left: 10px;
}
}
&-time {
padding-right: 55px;
} }
&-details { &-details {
margin-top: 5px; margin-top: 5px;
white-space: nowrap; .flex-row();
justify-content: space-between;
} }
&-id { &-tag {
height: 36px;
margin-right: 16px;
padding: 0 12px;
border: 1px solid #6d3df5;
border-radius: 4px;
font-size: 24px; font-size: 24px;
line-height: 36px; line-height: 36px;
color: #999999; color: #6d3df5;
padding-right: 22px;
display: inline-block; display: inline-block;
} }
&-info { &-info {
font-size: 28px; font-size: 28px;
line-height: 40px; line-height: 40px;
color: #333333; color: #333333;
margin-right: 16px;
display: inline-block; display: inline-block;
margin-right: 16px;
&:last-child { &:last-child {
margin-right: 0; margin-right: 0;
} }
@ -257,7 +274,7 @@
&__share-button { &__share-button {
//flex: 2 2; //flex: 2 2;
flex: 0 0 406px; flex: 0 0 406px;
width: 406px; width: 406px;
.button(@height: 88px; @fontSize: 32px; @fontWeight: 500; @borderRadius: 44px;); .button(@height: 88px; @fontSize: 32px; @fontWeight: 500; @borderRadius: 44px;);
//margin-left: 32px; //margin-left: 32px;

View File

@ -4,28 +4,56 @@ import { Swiper } from '@taroify/core';
import { GoodJob } from '@taroify/icons'; import { GoodJob } from '@taroify/icons';
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import LoginDialog from '@/components/login-dialog';
import { PageUrl } from '@/constants/app'; import { PageUrl } from '@/constants/app';
import useUserInfo from '@/hooks/use-user-info';
import { EarnType, UserProfitListItem } from '@/types/partner'; import { EarnType, UserProfitListItem } from '@/types/partner';
import { openCustomerServiceChat } from '@/utils/common'; import { openCustomerServiceChat } from '@/utils/common';
import { formatMoney, formatTimestamp, formatUserId, getLastProfitList } from '@/utils/partner'; import { becomePartner, formatMoney, formatTimestamp, formatUserId, getLastProfitList } from '@/utils/partner';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
import { isNeedPhone, requestUserInfo } from '@/utils/user';
import './index.less'; import './index.less';
const PREFIX = 'partner-intro'; const PREFIX = 'partner-intro';
export default function PartnerIntro() { export default function PartnerIntro() {
const handleConfirm = useCallback(() => { const userInfo = useUserInfo();
navigateTo(PageUrl.GroupOwnerCertificate); const needPhone = isNeedPhone(userInfo);
}, []); const [loginVisible, setLoginVisible] = useState(false);
const handleOpenService = useCallback(() => { const handleOpenService = useCallback(() => {
openCustomerServiceChat('https://work.weixin.qq.com/kfid/kfc4fcf6b109b3771d7'); openCustomerServiceChat('https://work.weixin.qq.com/kfid/kfc4fcf6b109b3771d7');
}, []); }, []);
const handleJump = useCallback(() => { const handleBindSuccess = useCallback(async () => {
navigateTo(PageUrl.PartnerShareVip); await becomePartner();
await requestUserInfo();
setLoginVisible(false);
}, []); }, []);
const handleBecomePartner = useCallback(() => {
if (needPhone) {
setLoginVisible(true);
return false;
} else {
handleBindSuccess();
return true;
}
}, [handleBindSuccess, needPhone]);
const handleConfirm = useCallback(() => {
if (!userInfo.isPartner && !handleBecomePartner()) {
return;
}
navigateTo(PageUrl.GroupOwnerCertificate);
}, [handleBecomePartner, userInfo.isPartner]);
const handleJump = useCallback(() => {
if (!userInfo.isPartner && !handleBecomePartner()) {
return;
}
navigateTo(PageUrl.PartnerShareVip);
}, [handleBecomePartner, userInfo.isPartner]);
const timerRef = useRef<NodeJS.Timeout | null>(null); const timerRef = useRef<NodeJS.Timeout | null>(null);
const [bannerList, setBannerList] = useState<UserProfitListItem[]>([]); const [bannerList, setBannerList] = useState<UserProfitListItem[]>([]);
@ -57,6 +85,19 @@ export default function PartnerIntro() {
} }
}; };
}, [getBannerList]); }, [getBannerList]);
const showedRef = useRef(false);
useEffect(() => {
if (showedRef.current || !userInfo.userId) {
return;
}
showedRef.current = true;
if (!userInfo.isPartner) {
handleBecomePartner();
}
}, [userInfo]);
return ( return (
<div className={PREFIX}> <div className={PREFIX}>
<Image <Image
@ -80,17 +121,24 @@ export default function PartnerIntro() {
<Swiper className={`${PREFIX}__swiper`} autoplay={3000} touchable={false}> <Swiper className={`${PREFIX}__swiper`} autoplay={3000} touchable={false}>
{bannerList.map((item, index) => ( {bannerList.map((item, index) => (
<Swiper.Item className={`${PREFIX}__swiper-item`} key={`${item.userId}-${index}`}> <Swiper.Item className={`${PREFIX}__swiper-item`} key={`${item.userId}-${index}`}>
<div className={`${PREFIX}__swiper-item-time`}>{formatTimestamp(item.updatedAt)}</div> <div className={`${PREFIX}__swiper-item-top-line`}>
<div className={`${PREFIX}__swiper-item-time`}>{formatTimestamp(item.updatedAt)}</div>
<div className={`${PREFIX}__swiper-item-id`}>
<div className="id">{formatUserId(item.userId)}</div>
</div>
</div>
<div className={`${PREFIX}__swiper-item-details`}> <div className={`${PREFIX}__swiper-item-details`}>
<div className={`${PREFIX}__swiper-item-id`}>{formatUserId(item.userId)}</div> <div>
<div className={`${PREFIX}__swiper-item-info`}> <div className={`${PREFIX}__swiper-item-tag`}></div>
{[EarnType.CHAT_ACTIVITY_SHARE_L1, EarnType.CHAT_ACTIVITY_SHARE_L2].includes(item.earnType) <div className={`${PREFIX}__swiper-item-info`}>
? '主播被开聊' {[EarnType.CHAT_ACTIVITY_SHARE_L1, EarnType.CHAT_ACTIVITY_SHARE_L2].includes(item.earnType)
: '会员支付'} ? '主播被开聊'
<div className="money">+{formatMoney(item.amount, 1)}</div> : '会员支付'}
<div className="money">+{formatMoney(item.amount)}</div>
</div>
</div> </div>
<div className={`${PREFIX}__swiper-item-info`}> <div className={`${PREFIX}__swiper-item-info`}>
<div className="money">{formatMoney(item.total, 1)}</div> <div className="money">{formatMoney(item.total)}</div>
</div> </div>
</div> </div>
</Swiper.Item> </Swiper.Item>
@ -163,6 +211,10 @@ export default function PartnerIntro() {
</Button> </Button>
</div> </div>
{loginVisible && (
<LoginDialog onCancel={() => setLoginVisible(false)} onSuccess={handleBindSuccess} needPhone={needPhone} />
)}
</div> </div>
); );
} }

View File

@ -3,6 +3,7 @@ export enum ProfileGroupType {
Intention = 'intention', Intention = 'intention',
Experience = 'experience', Experience = 'experience',
Advantages = 'advantages', Advantages = 'advantages',
Others = 'others',
} }
export const ProfileTitleMap = { export const ProfileTitleMap = {
@ -10,6 +11,7 @@ export const ProfileTitleMap = {
[ProfileGroupType.Intention]: '求职意向', [ProfileGroupType.Intention]: '求职意向',
[ProfileGroupType.Experience]: '直播经验', [ProfileGroupType.Experience]: '直播经验',
[ProfileGroupType.Advantages]: '自身优势', [ProfileGroupType.Advantages]: '自身优势',
[ProfileGroupType.Others]: '身型、学历',
}; };
export enum WorkedYears { export enum WorkedYears {
@ -19,6 +21,13 @@ export enum WorkedYears {
MoreThreeYear = 3, MoreThreeYear = 3,
} }
export enum EducationType {
MiddleSchool = 1,
HighSchool = 2,
AssociateDegree = 3,
BachelorOrAbove = 4,
}
export enum GenderType { export enum GenderType {
All = -1, All = -1,
MEN = 0, MEN = 0,
@ -110,3 +119,15 @@ export const ANCHOR_READ_TITLE_MAP = {
[AnchorReadType.Read]: '已读', [AnchorReadType.Read]: '已读',
[AnchorReadType.Unread]: '未读', [AnchorReadType.Unread]: '未读',
}; };
export const EDUCATION_TYPE_LABELS = {
[EducationType.MiddleSchool]: '初中',
[EducationType.HighSchool]: '职高/高中',
[EducationType.AssociateDegree]: '大专',
[EducationType.BachelorOrAbove]: '本科及以上',
};
export const EDUCATION_TYPE_OPTIONS = [
{ label: EDUCATION_TYPE_LABELS[EducationType.MiddleSchool], value: EducationType.MiddleSchool },
{ label: EDUCATION_TYPE_LABELS[EducationType.HighSchool], value: EducationType.HighSchool },
{ label: EDUCATION_TYPE_LABELS[EducationType.AssociateDegree], value: EducationType.AssociateDegree },
{ label: EDUCATION_TYPE_LABELS[EducationType.BachelorOrAbove], value: EducationType.BachelorOrAbove },
];

View File

@ -124,7 +124,6 @@ function JobFragment(props: IProps) {
const handleSelectEmployType = useCallback( const handleSelectEmployType = useCallback(
(value?: EmployType) => { (value?: EmployType) => {
console.log('哈哈哈', value);
setShowEmployTypeSelect(false); setShowEmployTypeSelect(false);
onClickEmployType(value!); onClickEmployType(value!);
}, },

View File

@ -23,9 +23,7 @@ function ProfileBasicFragment(props: IProps, ref) {
const [name, setName] = useState(profile.name || ''); const [name, setName] = useState(profile.name || '');
const [gender, setGender] = useState<GenderType>(profile.gender || GenderType.WOMEN); const [gender, setGender] = useState<GenderType>(profile.gender || GenderType.WOMEN);
const [age, setAge] = useState(profile.age || ''); const [age, setAge] = useState(profile.age || '');
const [height, setHeight] = useState(profile.height || ''); const [workedSecCategoryStr, setWorkedSecCategoryStr] = useState(profile.workedSecCategoryStr || '');
const [weight, setWeight] = useState(profile.weight || '');
const [shoeSize, setShoeSize] = useState(profile.shoeSize || '');
const handleInputName = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => { const handleInputName = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || ''; const value = e.detail.value || '';
@ -40,41 +38,21 @@ function ProfileBasicFragment(props: IProps, ref) {
setAge(string2Number(value)); setAge(string2Number(value));
}, []); }, []);
const handleInputHeight = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
if (Number.isNaN(Number(value))) {
return;
}
setHeight(string2Number(value));
}, []);
const handleInputWeight = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
if (Number.isNaN(Number(value))) {
return;
}
setWeight(string2Number(value));
}, []);
const handleInputShoeSize = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
if (Number.isNaN(Number(value))) {
return;
}
setShoeSize(string2Number(value));
}, []);
const handleGenderChange = useCallback((value: GenderType) => { const handleGenderChange = useCallback((value: GenderType) => {
log('handleGenderChange', value); log('handleGenderChange', value);
setGender(value); setGender(value);
}, []); }, []);
const handleInputWorkedSecCategoryStr = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
setWorkedSecCategoryStr(value);
}, []);
useImperativeHandle( useImperativeHandle(
ref, ref,
() => ({ () => ({
getData: () => ({ name, gender, age, height, weight, shoeSize }), getData: () => ({ name, gender, age, workedSecCategoryStr }),
}), }),
[name, gender, age, height, weight, shoeSize] [name, gender, age, workedSecCategoryStr]
); );
return ( return (
@ -82,29 +60,17 @@ function ProfileBasicFragment(props: IProps, ref) {
<BlFormItem title="称呼"> <BlFormItem title="称呼">
<BlFormInput value={name} onInput={handleInputName} /> <BlFormInput value={name} onInput={handleInputName} />
</BlFormItem> </BlFormItem>
<BlFormItem title="年龄">
<BlFormInput value={String(age)} type="number" maxlength={3} onInput={handleInputAge} rightText="岁" />
</BlFormItem>
<BlFormItem title="性别"> <BlFormItem title="性别">
<BlFormRadioGroup direction="horizontal" value={gender} onChange={handleGenderChange}> <BlFormRadioGroup direction="horizontal" value={gender} onChange={handleGenderChange}>
<BlFormRadio name={GenderType.WOMEN} text="女" value={gender} /> <BlFormRadio name={GenderType.WOMEN} text="女" value={gender} />
<BlFormRadio name={GenderType.MEN} text="男" value={gender} /> <BlFormRadio name={GenderType.MEN} text="男" value={gender} />
</BlFormRadioGroup> </BlFormRadioGroup>
</BlFormItem> </BlFormItem>
<BlFormItem title="年龄"> <BlFormItem title="直播过的品类" optional>
<BlFormInput value={String(age)} type="number" maxlength={3} onInput={handleInputAge} rightText="岁" /> <BlFormInput value={workedSecCategoryStr} maxlength={140} onInput={handleInputWorkedSecCategoryStr} />
</BlFormItem>
<BlFormItem title="身高">
<BlFormInput value={String(height)} type="number" maxlength={3} onInput={handleInputHeight} rightText="CM" />
</BlFormItem>
<BlFormItem title="体重">
<BlFormInput value={String(weight)} type="number" maxlength={3} onInput={handleInputWeight} rightText="KG" />
</BlFormItem>
<BlFormItem title="鞋码" optional>
<BlFormInput
value={String(shoeSize)}
type="number"
maxlength={2}
onInput={handleInputShoeSize}
rightText="码"
/>
</BlFormItem> </BlFormItem>
</div> </div>
); );

View File

@ -48,10 +48,9 @@ function ProfileExperienceFragment(props: IProps, ref) {
const [workedYear, setWorkedYear] = useState(profile.workedYear); const [workedYear, setWorkedYear] = useState(profile.workedYear);
const [workedAccounts, setWorkedAccounts] = useState(profile.workedAccounts || ''); const [workedAccounts, setWorkedAccounts] = useState(profile.workedAccounts || '');
const [newAccountExperience, setNewAccountExperience] = useState(profile.newAccountExperience); const [newAccountExperience, setNewAccountExperience] = useState(profile.newAccountExperience);
const [workedSecCategoryStr, setWorkedSecCategoryStr] = useState(profile.workedSecCategoryStr || '');
// const [style, setStyle] = useState(calcInitStyle(profile.style)); // const [style, setStyle] = useState(calcInitStyle(profile.style));
const [maxGmv, setMaxGmv] = useState(calcInitMaxGmv(profile?.maxGmv || 0)); const [maxGmv, setMaxGmv] = useState(calcInitMaxGmv(profile?.maxGmv || 0));
const [maxOnline, setMaxOnline] = useState(profile.maxOnline || ''); // const [maxOnline, setMaxOnline] = useState(profile.maxOnline || '');
const handleSelectWorkYear = useCallback((newYear: WorkedYears) => { const handleSelectWorkYear = useCallback((newYear: WorkedYears) => {
setWorkedYear(newYear); setWorkedYear(newYear);
@ -67,11 +66,6 @@ function ProfileExperienceFragment(props: IProps, ref) {
setNewAccountExperience(value); setNewAccountExperience(value);
}, []); }, []);
const handleInputWorkedSecCategoryStr = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
setWorkedSecCategoryStr(value);
}, []);
// const handleStyleChange = useCallback((value: StyleType[]) => { // const handleStyleChange = useCallback((value: StyleType[]) => {
// log('handleStyleChange', value); // log('handleStyleChange', value);
// setStyle(value); // setStyle(value);
@ -84,14 +78,14 @@ function ProfileExperienceFragment(props: IProps, ref) {
} }
setMaxGmv(string2Number(value)); setMaxGmv(string2Number(value));
}, []); }, []);
//
const handleInputMaxOnline = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => { // const handleInputMaxOnline = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || ''; // const value = e.detail.value || '';
if (Number.isNaN(Number(value))) { // if (Number.isNaN(Number(value))) {
return; // return;
} // }
setMaxOnline(string2Number(value)); // setMaxOnline(string2Number(value));
}, []); // }, []);
useImperativeHandle( useImperativeHandle(
ref, ref,
@ -100,22 +94,21 @@ function ProfileExperienceFragment(props: IProps, ref) {
workedYear, workedYear,
workedAccounts, workedAccounts,
newAccountExperience, newAccountExperience,
workedSecCategoryStr,
// style: getStyleString(style.sort()), // style: getStyleString(style.sort()),
maxOnline: maxOnline === '' ? undefined : maxOnline, // maxOnline: maxOnline === '' ? undefined : maxOnline,
maxGmv: maxGmv === '' ? undefined : getMaxGmv(Number(maxGmv)), maxGmv: maxGmv === '' ? undefined : getMaxGmv(Number(maxGmv)),
}), }),
}), }),
[workedYear, workedAccounts, newAccountExperience, workedSecCategoryStr, maxGmv, maxOnline] [workedYear, workedAccounts, newAccountExperience, maxGmv]
); );
return ( return (
<div className={PREFIX}> <div className={PREFIX}>
<BlFormItem title="直播经验"> <BlFormItem title="直播经验" optional>
<BlFormSelect title="直播经验" value={workedYear} options={WORK_YEAR_OPTIONS} onSelect={handleSelectWorkYear} /> <BlFormSelect title="直播经验" value={workedYear} options={WORK_YEAR_OPTIONS} onSelect={handleSelectWorkYear} />
</BlFormItem> </BlFormItem>
<BlFormItem title="直播过的品类" optional> <BlFormItem title="直播过的账号" optional>
<BlFormInput value={workedSecCategoryStr} maxlength={140} onInput={handleInputWorkedSecCategoryStr} /> <BlFormInput value={workedAccounts} maxlength={140} onInput={handleInputWorkedAccounts} />
</BlFormItem> </BlFormItem>
<BlFormItem title="自然流起号经验" optional> <BlFormItem title="自然流起号经验" optional>
<BlFormRadioGroup <BlFormRadioGroup
@ -127,9 +120,6 @@ function ProfileExperienceFragment(props: IProps, ref) {
<BlFormRadio name text="有" value={newAccountExperience} /> <BlFormRadio name text="有" value={newAccountExperience} />
</BlFormRadioGroup> </BlFormRadioGroup>
</BlFormItem> </BlFormItem>
<BlFormItem title="直播过的账号" optional>
<BlFormInput value={workedAccounts} maxlength={140} onInput={handleInputWorkedAccounts} />
</BlFormItem>
{/* <ProfileEditItem title="直播风格" optional> {/* <ProfileEditItem title="直播风格" optional>
<ProfileCheckboxGroup value={style} onChange={handleStyleChange}> <ProfileCheckboxGroup value={style} onChange={handleStyleChange}>
<ProfileCheckbox value={style} name={StyleType.Broadcasting} text="平播" /> <ProfileCheckbox value={style} name={StyleType.Broadcasting} text="平播" />
@ -140,9 +130,9 @@ function ProfileExperienceFragment(props: IProps, ref) {
<BlFormItem title="最高GMV" optional> <BlFormItem title="最高GMV" optional>
<BlFormInput value={String(maxGmv)} type="number" maxlength={10} onInput={handleInputMaxGmv} rightText="W" /> <BlFormInput value={String(maxGmv)} type="number" maxlength={10} onInput={handleInputMaxGmv} rightText="W" />
</BlFormItem> </BlFormItem>
<BlFormItem title="最高在线人数" optional> {/*<BlFormItem title="最高在线人数" optional>*/}
<BlFormInput value={String(maxOnline)} type="number" onInput={handleInputMaxOnline} rightText="人" /> {/* <BlFormInput value={String(maxOnline)} type="number" onInput={handleInputMaxOnline} rightText="人" />*/}
</BlFormItem> {/*</BlFormItem>*/}
</div> </div>
); );
} }

View File

@ -45,4 +45,8 @@
} }
} }
} .transparent {
background: transparent;
padding: 0;
}
}

View File

@ -3,12 +3,21 @@ import Taro from '@tarojs/taro';
import { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react'; import { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react';
import { BlCheckbox, BlCheckboxGroup } from '@/components/bl-checkbox';
import BlFormItem from '@/components/bl-form-item'; import BlFormItem from '@/components/bl-form-item';
import { BlFormRadio, BlFormRadioGroup } from '@/components/bl-form-radio'; import { BlFormRadio, BlFormRadioGroup } from '@/components/bl-form-radio';
import BlFormSelect from '@/components/bl-form-select'; import BlFormSelect from '@/components/bl-form-select';
import { EventName, OpenSource, PageUrl } from '@/constants/app'; import { EventName, OpenSource, PageUrl } from '@/constants/app';
import { CITY_CODE_TO_NAME_MAP } from '@/constants/city'; import { CITY_CODE_TO_NAME_MAP } from '@/constants/city';
import { EmployType, EMPLOY_TYPE_TITLE_MAP, FULL_PRICE_OPTIONS, PART_PRICE_OPTIONS } from '@/constants/job'; import {
EmployType,
EMPLOY_TYPE_TITLE_MAP,
FULL_PRICE_OPTIONS,
PART_PRICE_OPTIONS,
ALL_JOB_TYPES,
JobType,
JOB_TYPE_TITLE_MAP,
} from '@/constants/job';
import { SalaryRange } from '@/types/job'; import { SalaryRange } from '@/types/job';
import { MaterialProfile } from '@/types/material'; import { MaterialProfile } from '@/types/material';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
@ -60,6 +69,12 @@ const getSalaryPrice = (fullSalary?: SalaryRange, partSalary?: SalaryRange) => {
return price; return price;
}; };
const getWorkedCategoryString = (workedCategory: JobType[]) => workedCategory.join(',');
const getWorkedCategory = (workedCategory?: string): JobType[] => {
return workedCategory ? (workedCategory.split(',') as JobType[]).filter(v => ALL_JOB_TYPES.includes(v)) : [];
};
function ProfileIntentionFragment(props: IProps, ref) { function ProfileIntentionFragment(props: IProps, ref) {
const { profile } = props; const { profile } = props;
const [cityCodes, setCityCodes] = useState(calcInitCityCodes(profile.cityCodes || '')); const [cityCodes, setCityCodes] = useState(calcInitCityCodes(profile.cityCodes || ''));
@ -67,6 +82,7 @@ function ProfileIntentionFragment(props: IProps, ref) {
const [fullSalary, setFullSalary] = useState(calcSalarySelect(profile, true)); const [fullSalary, setFullSalary] = useState(calcSalarySelect(profile, true));
const [partSalary, setPartSalary] = useState(calcSalarySelect(profile, false)); const [partSalary, setPartSalary] = useState(calcSalarySelect(profile, false));
const [acceptWorkForSit, setAcceptWorkForSit] = useState(profile.acceptWorkForSit); const [acceptWorkForSit, setAcceptWorkForSit] = useState(profile.acceptWorkForSit);
const [workedCategory, setWorkedCategory] = useState<JobType[]>(getWorkedCategory(profile.workedCategory));
const handleDeleteCity = useCallback( const handleDeleteCity = useCallback(
(deleteCode: string) => { (deleteCode: string) => {
@ -85,7 +101,7 @@ function ProfileIntentionFragment(props: IProps, ref) {
data => { data => {
log('handleSelectCity', data); log('handleSelectCity', data);
realtimeLogger.info('handleSelectCity', data); realtimeLogger.info('handleSelectCity', data);
const { openSource, cityCode: code } = data; const { cityCode: code } = data;
// if (openSource !== OpenSource.AddIndentCity) { // if (openSource !== OpenSource.AddIndentCity) {
// return; // return;
// } // }
@ -118,6 +134,11 @@ function ProfileIntentionFragment(props: IProps, ref) {
setAcceptWorkForSit(value); setAcceptWorkForSit(value);
}, []); }, []);
const handleWorkedCategoryChange = useCallback((value: JobType[]) => {
log('handleWorkedCategoryChange', value);
setWorkedCategory(value);
}, []);
useEffect(() => { useEffect(() => {
Taro.eventCenter.on(EventName.SELECT_CITY, handleSelectCity); Taro.eventCenter.on(EventName.SELECT_CITY, handleSelectCity);
return () => { return () => {
@ -132,10 +153,11 @@ function ProfileIntentionFragment(props: IProps, ref) {
cityCodes: getCityCodeString(cityCodes), cityCodes: getCityCodeString(cityCodes),
employType, employType,
acceptWorkForSit, acceptWorkForSit,
workedCategory: getWorkedCategoryString(workedCategory),
...getSalaryPrice(fullSalary, partSalary), ...getSalaryPrice(fullSalary, partSalary),
}), }),
}), }),
[cityCodes, employType, fullSalary, partSalary, acceptWorkForSit] [cityCodes, employType, fullSalary, partSalary, acceptWorkForSit, workedCategory]
); );
return ( return (
@ -174,7 +196,7 @@ function ProfileIntentionFragment(props: IProps, ref) {
</BlFormRadioGroup> </BlFormRadioGroup>
</BlFormItem> </BlFormItem>
{isFullTimePriceRequired(employType) && ( {isFullTimePriceRequired(employType) && (
<BlFormItem title="期望全职薪资"> <BlFormItem title="期望全职薪资" subTitle="重要">
<BlFormSelect <BlFormSelect
title="期望全职薪资" title="期望全职薪资"
value={fullSalary} value={fullSalary}
@ -184,7 +206,7 @@ function ProfileIntentionFragment(props: IProps, ref) {
</BlFormItem> </BlFormItem>
)} )}
{isPartTimePriceRequired(employType) && ( {isPartTimePriceRequired(employType) && (
<BlFormItem title="期望兼职薪资"> <BlFormItem title="期望兼职薪资" subTitle="重要">
<BlFormSelect <BlFormSelect
title="期望兼职薪资" title="期望兼职薪资"
value={partSalary} value={partSalary}
@ -193,12 +215,19 @@ function ProfileIntentionFragment(props: IProps, ref) {
/> />
</BlFormItem> </BlFormItem>
)} )}
<BlFormItem title="是否接受坐班"> <BlFormItem title="是否接受坐班" optional>
<BlFormRadioGroup direction="horizontal" value={acceptWorkForSit} onChange={handleAcceptSittingChange}> <BlFormRadioGroup direction="horizontal" value={acceptWorkForSit} onChange={handleAcceptSittingChange}>
<BlFormRadio name text="接受" value={acceptWorkForSit} /> <BlFormRadio name text="接受" value={acceptWorkForSit} />
<BlFormRadio name={false} text="不接受" value={acceptWorkForSit} /> <BlFormRadio name={false} text="不接受" value={acceptWorkForSit} />
</BlFormRadioGroup> </BlFormRadioGroup>
</BlFormItem> </BlFormItem>
<BlFormItem title="意向类目" subTitle="多选" dynamicHeight contentClassName="transparent">
<BlCheckboxGroup value={workedCategory} onChange={handleWorkedCategoryChange}>
{ALL_JOB_TYPES.filter(type => type !== JobType.All).map(type => {
return <BlCheckbox text={JOB_TYPE_TITLE_MAP[type]} key={type} name={type} />;
})}
</BlCheckboxGroup>
</BlFormItem>
</div> </div>
); );
} }

View File

@ -0,0 +1,15 @@
@import '@/styles/common.less';
@import '@/styles/variables.less';
.fragment-profile-others {
width: 100%;
&__title {
font-weight: 400;
font-size: 32px;
line-height: 40px;
margin-bottom: 24px;
}
}

View File

@ -0,0 +1,94 @@
import { BaseEventOrig, InputProps } from '@tarojs/components';
import { forwardRef, useCallback, useImperativeHandle, useState } from 'react';
import BlFormInput from '@/components/bl-form-input';
import BlFormItem from '@/components/bl-form-item';
import BlFormSelect from '@/components/bl-form-select';
import { EDUCATION_TYPE_OPTIONS, EducationType } from '@/constants/material';
import { MaterialProfile } from '@/types/material';
import { logWithPrefix, string2Number } from '@/utils/common';
import './index.less';
interface IProps {
profile: Partial<MaterialProfile>;
}
const PREFIX = 'fragment-profile-others';
const log = logWithPrefix(PREFIX);
function ProfileOthersFragment(props: IProps, ref) {
const { profile } = props;
const [educationType, setEducationType] = useState(profile.educationType || '');
const [height, setHeight] = useState(profile.height || '');
const [weight, setWeight] = useState(profile.weight || '');
const [shoeSize, setShoeSize] = useState(profile.shoeSize || '');
const handleInputHeight = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
if (Number.isNaN(Number(value))) {
return;
}
setHeight(string2Number(value));
}, []);
const handleInputWeight = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
if (Number.isNaN(Number(value))) {
return;
}
setWeight(string2Number(value));
}, []);
const handleInputShoeSize = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
if (Number.isNaN(Number(value))) {
return;
}
setShoeSize(string2Number(value));
}, []);
const handleSelectEducationType = useCallback((type: EducationType) => {
setEducationType(type);
}, []);
useImperativeHandle(
ref,
() => ({
getData: () => ({ height, weight, shoeSize, educationType }),
}),
[height, weight, shoeSize, educationType]
);
return (
<div className={PREFIX}>
<div className={`${PREFIX}__title`}>穿</div>
<BlFormItem title="身高" optional>
<BlFormInput value={String(height)} type="number" maxlength={3} onInput={handleInputHeight} rightText="CM" />
</BlFormItem>
<BlFormItem title="体重" optional>
<BlFormInput value={String(weight)} type="number" maxlength={3} onInput={handleInputWeight} rightText="KG" />
</BlFormItem>
<BlFormItem title="鞋码" optional>
<BlFormInput
value={String(shoeSize)}
type="number"
maxlength={2}
onInput={handleInputShoeSize}
rightText="码"
/>
</BlFormItem>
<BlFormItem title="学历" optional>
<BlFormSelect
title="学历"
value={educationType}
options={EDUCATION_TYPE_OPTIONS}
onSelect={handleSelectEducationType}
/>
</BlFormItem>
</div>
);
}
export default forwardRef(ProfileOthersFragment);

View File

@ -8,16 +8,22 @@ import DevDiv from '@/components/dev-div';
import SafeBottomPadding from '@/components/safe-bottom-padding'; import SafeBottomPadding from '@/components/safe-bottom-padding';
import { OpenSource, PageUrl } from '@/constants/app'; import { OpenSource, PageUrl } from '@/constants/app';
import { CITY_CODE_TO_NAME_MAP } from '@/constants/city'; import { CITY_CODE_TO_NAME_MAP } from '@/constants/city';
import { FULL_PRICE_OPTIONS, PART_PRICE_OPTIONS } from '@/constants/job'; import { FULL_PRICE_OPTIONS, JOB_TYPE_TITLE_MAP, JobType, PART_PRICE_OPTIONS } from '@/constants/job';
import { ProfileGroupType, ProfileTitleMap, WORK_YEAR_OPTIONS } from '@/constants/material'; import {
EDUCATION_TYPE_LABELS,
GenderType,
ProfileGroupType,
ProfileTitleMap,
WORK_YEAR_OPTIONS,
} from '@/constants/material';
import { MaterialProfile } from '@/types/material'; import { MaterialProfile } from '@/types/material';
import { logWithPrefix, isDesktop } from '@/utils/common'; import { isDesktop, logWithPrefix } from '@/utils/common';
import { getBasicInfo, sortVideos } from '@/utils/material'; import { sortVideos } from '@/utils/material';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
import './index.less'; import './index.less';
import onChangeEventDetail = SwiperProps.onChangeEventDetail;
import { JoinGroupHint } from '@/components/join-group-hint'; import { JoinGroupHint } from '@/components/join-group-hint';
import onChangeEventDetail = SwiperProps.onChangeEventDetail;
interface IProps { interface IProps {
editable: boolean; editable: boolean;
@ -30,7 +36,7 @@ const log = logWithPrefix(PREFIX);
const DEFAULT_TEXT = '未填写'; const DEFAULT_TEXT = '未填写';
const getIndentCity = (codeString: string = '') => { const getIndentCity = (codeString: string = '') => {
const codes = (codeString||'').split('、'); const codes = (codeString || '').split('、');
const cityNames: string[] = []; const cityNames: string[] = [];
codes.forEach(code => { codes.forEach(code => {
const cityName = CITY_CODE_TO_NAME_MAP.get(code); const cityName = CITY_CODE_TO_NAME_MAP.get(code);
@ -63,7 +69,14 @@ const getWorkYear = (year: number) => {
const y = Math.min(year, 3); const y = Math.min(year, 3);
return WORK_YEAR_OPTIONS.find(option => option.value === y)?.label || `${y}`; return WORK_YEAR_OPTIONS.find(option => option.value === y)?.label || `${y}`;
}; };
const getWorkedCategory = (workedCategory?: string): string => {
return workedCategory
? (workedCategory.split(',') as JobType[])
.map(v => (v !== JobType.All ? JOB_TYPE_TITLE_MAP[v] : null))
.filter(Boolean)
.join('、')
: '';
};
const getDataGroup = (profile: MaterialProfile | null) => { const getDataGroup = (profile: MaterialProfile | null) => {
if (!profile) { if (!profile) {
return []; return [];
@ -77,6 +90,7 @@ const getDataGroup = (profile: MaterialProfile | null) => {
{ title: '兼职薪资', value: getIndentPrice(profile.partyTimeMinPrice, profile.partyTimeMaxPrice, false) || '' }, { title: '兼职薪资', value: getIndentPrice(profile.partyTimeMinPrice, profile.partyTimeMaxPrice, false) || '' },
{ title: '意向城市', value: getIndentCity(profile.cityCodes) || DEFAULT_TEXT }, { title: '意向城市', value: getIndentCity(profile.cityCodes) || DEFAULT_TEXT },
{ title: '是否接受坐班', value: profile.acceptWorkForSit ? '是' : '否' }, { title: '是否接受坐班', value: profile.acceptWorkForSit ? '是' : '否' },
{ title: '意向品类', value: getWorkedCategory(profile.workedCategory) || DEFAULT_TEXT, fullLine: true },
], ],
}, },
{ {
@ -86,9 +100,18 @@ const getDataGroup = (profile: MaterialProfile | null) => {
{ title: '直播年限', value: getWorkYear(profile.workedYear) || DEFAULT_TEXT }, { title: '直播年限', value: getWorkYear(profile.workedYear) || DEFAULT_TEXT },
{ title: '直播过的账号', value: profile.workedAccounts || DEFAULT_TEXT }, { title: '直播过的账号', value: profile.workedAccounts || DEFAULT_TEXT },
{ title: '自然流起号经验', value: Boolean(profile.newAccountExperience) ? '有' : '无' }, { title: '自然流起号经验', value: Boolean(profile.newAccountExperience) ? '有' : '无' },
{ title: '直播过的品类', value: profile.workedSecCategoryStr || DEFAULT_TEXT }, // { title: '直播过的品类', value: profile.workedSecCategoryStr || DEFAULT_TEXT },
{ title: '最高GMV', value: profile.maxGmv ? `${profile.maxGmv / 10000}` : DEFAULT_TEXT }, { title: '最高GMV', value: profile.maxGmv ? `${profile.maxGmv / 10000}` : DEFAULT_TEXT },
{ title: '最高在线人数', value: profile.maxOnline || DEFAULT_TEXT }, ],
},
{
title: ProfileTitleMap[ProfileGroupType.Others],
type: ProfileGroupType.Others,
items: [
{ title: '身高', value: profile.height ? `${profile.height}cm` : DEFAULT_TEXT },
{ title: '体重', value: profile.weight ? `${profile.weight}kg` : DEFAULT_TEXT },
{ title: '鞋码', value: profile.shoeSize ? `${profile.shoeSize}` : DEFAULT_TEXT },
{ title: '学历', value: EDUCATION_TYPE_LABELS[profile.educationType] || DEFAULT_TEXT },
], ],
}, },
{ {
@ -99,6 +122,16 @@ const getDataGroup = (profile: MaterialProfile | null) => {
]; ];
}; };
export const getBasicInfo = (profile: Pick<MaterialProfile, 'age' | 'gender'>) => {
const result: string[] = [];
if (typeof profile.age !== 'undefined' && profile.age !== null) {
result.push(`${profile.age}`);
}
result.push(profile.gender === GenderType.MEN ? '男' : '女');
return result.join('·');
};
export default function ProfileViewFragment(props: IProps) { export default function ProfileViewFragment(props: IProps) {
const { profile, editable, onDev } = props; const { profile, editable, onDev } = props;
const [coverIndex, setCoverIndex] = useState(0); const [coverIndex, setCoverIndex] = useState(0);
@ -221,7 +254,9 @@ export default function ProfileViewFragment(props: IProps) {
</div> </div>
)} )}
</div> </div>
<div className={`${PREFIX}__basic-info__content`}>{getBasicInfo(profile)}</div> <div className={`${PREFIX}__basic-info__content`}>
<div>{getBasicInfo(profile)}</div>
</div>
<div className={`${PREFIX}__divider`} /> <div className={`${PREFIX}__divider`} />
</div> </div>
{dataGroup.map((data, index: number) => ( {dataGroup.map((data, index: number) => (

View File

@ -1,9 +1,9 @@
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { selectCityOperators } from '@/store/selector'; import { selectCityConfigs } from '@/store/selector';
function useCityOperators() { function useCityOperators() {
const data = useSelector(selectCityOperators); const data = useSelector(selectCityConfigs);
return data || []; return data || [];
} }

View File

@ -3,8 +3,8 @@ import { useSelector } from 'react-redux';
import { selectUserInfo } from '@/store/selector/user'; import { selectUserInfo } from '@/store/selector/user';
function useUserInfo() { function useUserInfo() {
const useInfo = useSelector(selectUserInfo); const userInfo = useSelector(selectUserInfo);
return useInfo; return userInfo;
} }
export default useUserInfo; export default useUserInfo;

View File

@ -95,5 +95,5 @@ export enum API {
GET_AUTHED_GROUP_LIST = '/partner/getAuthedGroupList', GET_AUTHED_GROUP_LIST = '/partner/getAuthedGroupList',
GET_STAFF_CODE = '/partner/staff/{cityCode}', GET_STAFF_CODE = '/partner/staff/{cityCode}',
// 所有城市运营 // 所有城市运营
GET_ALL_CITY_OPERATOR = '/group/getAllGroup', GET_ALL_CITY_CONFIGS = '/group/getAllGroup',
} }

View File

@ -115,7 +115,6 @@ export default function AnchorPage() {
const handleChangeSelectJob = useCallback((select?: JobManageInfo) => { const handleChangeSelectJob = useCallback((select?: JobManageInfo) => {
log('select job change', select); log('select job change', select);
setSelectJob(select); setSelectJob(select);
console.log('哈哈哈', !select);
setOpenPopup(!select); setOpenPopup(!select);
setLastSelectMyJobId(select?.id || ''); setLastSelectMyJobId(select?.id || '');
}, []); }, []);

View File

@ -10,11 +10,27 @@
left: 24px; left: 24px;
right: 24px; right: 24px;
bottom: 0; bottom: 0;
background: #F5F6FA; background: #f5f6fa;
} }
&__submit-btn { &__submit-btn {
.button(@width: 100%, @height: 80px, @fontSize: 32px, @fontWeight: 400, @borderRadius: 48px); .button(@width: 100%, @height: 80px, @fontSize: 32px, @fontWeight: 400, @borderRadius: 48px);
bottom: 40px; bottom: 40px;
} }
}
&__skip-btn-wrapper {
text-align: center;
bottom: 60px;
}
&__skip-btn {
color: @blHighlightColor;
width: 161px;
height: 40px;
font-weight: 400;
font-size: 32px;
line-height: 40px;
text-align: center;
display: inline-block;
}
}

View File

@ -21,25 +21,28 @@ import { navigateBack } from '@/utils/route';
import Toast from '@/utils/toast'; import Toast from '@/utils/toast';
import './index.less'; import './index.less';
import ProfileOthersFragment from '@/fragments/profile/others';
const PREFIX = 'page-material-create-profile'; const PREFIX = 'page-material-create-profile';
const log = logWithPrefix(PREFIX); const log = logWithPrefix(PREFIX);
const REQUIRE_KEYS = { const REQUIRE_KEYS = {
[ProfileGroupType.Basic]: ['name', 'gender', 'age', 'height', 'weight'], [ProfileGroupType.Basic]: ['name', 'gender', 'age'],
[ProfileGroupType.Intention]: [ [ProfileGroupType.Intention]: [
'cityCodes', 'cityCodes',
'employType', 'employType',
'acceptWorkForSit',
'fullTimeMinPrice', 'fullTimeMinPrice',
'fullTimeMaxPrice', 'fullTimeMaxPrice',
'partyTimeMinPrice', 'partyTimeMinPrice',
'partyTimeMaxPrice', 'partyTimeMaxPrice',
], ],
[ProfileGroupType.Experience]: ['workedYear'], [ProfileGroupType.Experience]: [],
[ProfileGroupType.Advantages]: [], [ProfileGroupType.Advantages]: [],
[ProfileGroupType.Others]: [],
}; };
const COULD_SKIP = [ProfileGroupType.Experience, ProfileGroupType.Others];
const CONDITIONAL_REQUIRED_KEYS = { const CONDITIONAL_REQUIRED_KEYS = {
[ProfileGroupType.Intention]: [ [ProfileGroupType.Intention]: [
['fullTimeMinPrice', data => isFullTimePriceRequired(data.employType)], ['fullTimeMinPrice', data => isFullTimePriceRequired(data.employType)],
@ -52,11 +55,13 @@ const CONDITIONAL_REQUIRED_KEYS = {
const getNextStepGroupType = (curType: ProfileGroupType) => { const getNextStepGroupType = (curType: ProfileGroupType) => {
switch (curType) { switch (curType) {
case ProfileGroupType.Basic: case ProfileGroupType.Basic:
return ProfileGroupType.Advantages;
case ProfileGroupType.Intention:
return ProfileGroupType.Experience; return ProfileGroupType.Experience;
case ProfileGroupType.Experience: case ProfileGroupType.Intention:
return ProfileGroupType.Basic; return ProfileGroupType.Basic;
case ProfileGroupType.Experience:
return ProfileGroupType.Others;
case ProfileGroupType.Others:
return ProfileGroupType.Advantages;
default: default:
return null; return null;
} }
@ -88,7 +93,16 @@ export default function MaterialCreateProfile() {
? ProfileExperienceFragment ? ProfileExperienceFragment
: groupType === ProfileGroupType.Advantages : groupType === ProfileGroupType.Advantages
? ProfileAdvantagesFragment ? ProfileAdvantagesFragment
: Fragment; : groupType === ProfileGroupType.Others
? ProfileOthersFragment
: Fragment;
const handleSkip = useCallback(() => {
const nextType = getNextStepGroupType(groupType);
if (nextType) {
setGroupType(nextType);
}
}, [groupType]);
const handleSubmit = useCallback(async () => { const handleSubmit = useCallback(async () => {
try { try {
@ -123,14 +137,23 @@ export default function MaterialCreateProfile() {
Taro.setNavigationBarTitle({ title }); Taro.setNavigationBarTitle({ title });
}, [groupType]); }, [groupType]);
const couldSkip = COULD_SKIP.includes(groupType);
return ( return (
<div className={PREFIX}> <div className={PREFIX}>
<ProfileFragment ref={ref} profile={{ cityCodes: location.cityCode || '' }} /> <ProfileFragment ref={ref} profile={{ cityCodes: location.cityCode || '' }} />
<SafeBottomPadding /> <SafeBottomPadding />
<div className={`${PREFIX}__footer`}> <div className={`${PREFIX}__footer`}>
<Button className={`${PREFIX}__submit-btn`} onClick={handleSubmit}> <Button className={`${PREFIX}__submit-btn ${couldSkip ? 'up' : ''}`} onClick={handleSubmit}>
{groupType === ProfileGroupType.Advantages ? '完成' : '下一步'} {groupType === ProfileGroupType.Advantages ? '完成' : '下一步'}
</Button> </Button>
{couldSkip && (
<div className={`${PREFIX}__skip-btn-wrapper`}>
<div className={`${PREFIX}__skip-btn`} onClick={handleSkip}>
</div>
</div>
)}
<SafeBottomPadding /> <SafeBottomPadding />
</div> </div>
</div> </div>

View File

@ -12,6 +12,7 @@ import ProfileAdvantagesFragment from '@/fragments/profile/advantages';
import ProfileBasicFragment from '@/fragments/profile/basic'; import ProfileBasicFragment from '@/fragments/profile/basic';
import ProfileExperienceFragment from '@/fragments/profile/experience'; import ProfileExperienceFragment from '@/fragments/profile/experience';
import ProfileIntentionFragment from '@/fragments/profile/intention'; import ProfileIntentionFragment from '@/fragments/profile/intention';
import ProfileOthersFragment from '@/fragments/profile/others';
import { MaterialProfile } from '@/types/material'; import { MaterialProfile } from '@/types/material';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
import { collectEvent } from '@/utils/event'; import { collectEvent } from '@/utils/event';
@ -37,7 +38,9 @@ export default function MaterialEdit() {
? ProfileExperienceFragment ? ProfileExperienceFragment
: groupType === ProfileGroupType.Advantages : groupType === ProfileGroupType.Advantages
? ProfileAdvantagesFragment ? ProfileAdvantagesFragment
: Fragment; : groupType === ProfileGroupType.Others
? ProfileOthersFragment
: Fragment;
log('MaterialEdit', groupType, ref); log('MaterialEdit', groupType, ref);
const handleSubmit = useCallback(async () => { const handleSubmit = useCallback(async () => {

View File

@ -2,7 +2,7 @@ import { RoleType, PageType } from '@/constants/app';
import { LocationInfo } from '@/types/location'; import { LocationInfo } from '@/types/location';
import { AppState } from '@/types/store'; import { AppState } from '@/types/store';
import { CHANGE_ROLE_TYPE, CHANGE_HOME_PAGE, SET_LOCATION_INFO, SET_SERVICE_URLS } from '../constants'; import { CHANGE_ROLE_TYPE, CHANGE_HOME_PAGE, SET_LOCATION_INFO, SET_CITY_CONFIGS } from '../constants';
export const changeRoleType = (value: RoleType) => ({ type: CHANGE_ROLE_TYPE, value }); export const changeRoleType = (value: RoleType) => ({ type: CHANGE_ROLE_TYPE, value });
@ -10,4 +10,4 @@ export const changeHomePage = (value: PageType) => ({ type: CHANGE_HOME_PAGE, va
export const setLocationInfo = (value: LocationInfo) => ({ type: SET_LOCATION_INFO, value }); export const setLocationInfo = (value: LocationInfo) => ({ type: SET_LOCATION_INFO, value });
export const setCityOperators = (value: AppState['cityOperators']) => ({ type: SET_SERVICE_URLS, value }); export const setCityConfigs = (value: AppState['cityConfigs']) => ({ type: SET_CITY_CONFIGS, value });

View File

@ -6,4 +6,4 @@ export const SET_BIND_PHONE = 'SET_BIND_PHONE';
export const SET_USER_MESSAGE = 'SET_USER_MESSAGE'; export const SET_USER_MESSAGE = 'SET_USER_MESSAGE';
export const SET_INVITE_CODE = 'SET_INVITE_CODE'; export const SET_INVITE_CODE = 'SET_INVITE_CODE';
export const SET_JOB_ID = 'SET_JOB_ID'; export const SET_JOB_ID = 'SET_JOB_ID';
export const SET_SERVICE_URLS = 'SET_SERVICE_URLS'; export const SET_CITY_CONFIGS = 'SET_CITY_CONFIGS';

View File

@ -7,7 +7,7 @@ import { CacheKey } from '@/constants/cache-key';
import { LocationInfo } from '@/types/location'; import { LocationInfo } from '@/types/location';
import { AppState } from '@/types/store'; import { AppState } from '@/types/store';
import { CHANGE_ROLE_TYPE, CHANGE_HOME_PAGE, SET_LOCATION_INFO, SET_SERVICE_URLS } from '../constants'; import { CHANGE_ROLE_TYPE, CHANGE_HOME_PAGE, SET_LOCATION_INFO, SET_CITY_CONFIGS } from '../constants';
const DEFAULT_LOCATION: LocationInfo = { const DEFAULT_LOCATION: LocationInfo = {
provinceCode: '440000', provinceCode: '440000',
@ -23,7 +23,7 @@ const INIT_STATE: AppState = {
roleType: defaultAppMode, roleType: defaultAppMode,
homePageType: defaultAppMode === RoleType.Company ? PageType.Anchor : PageType.JOB, homePageType: defaultAppMode === RoleType.Company ? PageType.Anchor : PageType.JOB,
location: Taro.getStorageSync<LocationInfo>(CacheKey.CACHE_LOCATION_INFO) || DEFAULT_LOCATION, location: Taro.getStorageSync<LocationInfo>(CacheKey.CACHE_LOCATION_INFO) || DEFAULT_LOCATION,
cityOperators: [], cityConfigs: [],
}; };
const appState = (state: AppState = INIT_STATE, action: Action): AppState => { const appState = (state: AppState = INIT_STATE, action: Action): AppState => {
@ -34,8 +34,8 @@ const appState = (state: AppState = INIT_STATE, action: Action): AppState => {
return { ...state, roleType: value }; return { ...state, roleType: value };
case CHANGE_HOME_PAGE: case CHANGE_HOME_PAGE:
return { ...state, homePageType: value }; return { ...state, homePageType: value };
case SET_SERVICE_URLS: case SET_CITY_CONFIGS:
return { ...state, cityOperators: value }; return { ...state, cityConfigs: value };
case SET_LOCATION_INFO: case SET_LOCATION_INFO:
Taro.setStorageSync(CacheKey.CACHE_LOCATION_INFO, value); Taro.setStorageSync(CacheKey.CACHE_LOCATION_INFO, value);
return { ...state, location: value }; return { ...state, location: value };

View File

@ -6,4 +6,4 @@ export const selectHomePageType = (state: IState) => state.appState.homePageType
export const selectLocation = (state: IState) => state.appState.location; export const selectLocation = (state: IState) => state.appState.location;
export const selectCityOperators = (state: IState) => state.appState.cityOperators || {}; export const selectCityConfigs = (state: IState) => state.appState.cityConfigs || [];

View File

@ -21,7 +21,7 @@ export interface GetCityCodeRequest {
longitude: number; // 经度,范围为-180~180负数表示西经 longitude: number; // 经度,范围为-180~180负数表示西经
} }
export interface CityOperatorListItem { export interface CityConfigListItem {
id: number; id: number;
staffId: number; staffId: number;
staffName: string; staffName: string;
@ -30,4 +30,6 @@ export interface CityOperatorListItem {
groupLink: string; groupLink: string;
created: string; created: string;
updated: string; updated: string;
price: number | null;
sendCount: number | null;
} }

View File

@ -1,5 +1,12 @@
import { EmployType } from '@/constants/job'; import { EmployType } from '@/constants/job';
import { AnchorReadType, AnchorSortType, GenderType, ProfileCreateSource, WorkedYears } from '@/constants/material'; import {
AnchorReadType,
AnchorSortType,
EducationType,
GenderType,
ProfileCreateSource,
WorkedYears,
} from '@/constants/material';
import { IPaginationRequest, IPaginationResponse } from '@/types/common'; import { IPaginationRequest, IPaginationResponse } from '@/types/common';
export interface MaterialVideoInfo { export interface MaterialVideoInfo {
@ -51,6 +58,7 @@ export interface MaterialProfile {
filledItems: number; // 已填资料项数 filledItems: number; // 已填资料项数
created: number; // 时间戳 created: number; // 时间戳
updated: number; // 时间戳 updated: number; // 时间戳
educationType: EducationType;
} }
export interface AnchorInfo { export interface AnchorInfo {

View File

@ -2,7 +2,7 @@ import { RoleType, PageType } from '@/constants/app';
import { UserMessage } from '@/types/message'; import { UserMessage } from '@/types/message';
import { PartnerInfo } from '@/types/partner'; import { PartnerInfo } from '@/types/partner';
import { LocationInfo } from './location'; import { CityConfigListItem, LocationInfo } from './location';
import { UserInfo } from './user'; import { UserInfo } from './user';
export interface IState { export interface IState {
@ -17,8 +17,5 @@ export interface AppState {
roleType: RoleType; roleType: RoleType;
homePageType: PageType; homePageType: PageType;
location: LocationInfo; location: LocationInfo;
cityOperators: Array<{ cityConfigs: CityConfigListItem[];
cityCode: string;
groupLink: string;
}>;
} }

View File

@ -6,9 +6,9 @@ import { CITY_CODE_TO_NAME_MAP, COUNTY_CODE_TO_NAME_MAP, PROVINCE_CODE_TO_NAME_M
import http from '@/http'; import http from '@/http';
import { API } from '@/http/api'; import { API } from '@/http/api';
import store from '@/store'; import store from '@/store';
import { setLocationInfo, setCityOperators } from '@/store/actions'; import { setLocationInfo, setCityConfigs } from '@/store/actions';
import { selectLocation } from '@/store/selector'; import { selectLocation } from '@/store/selector';
import { CityOperatorListItem, GetCityCodeRequest, LocationInfo } from '@/types/location'; import { CityConfigListItem, GetCityCodeRequest, LocationInfo } from '@/types/location';
import { authorize, getWxSetting } from './wx'; import { authorize, getWxSetting } from './wx';
@ -135,14 +135,7 @@ export async function requestLocation(force: boolean = false) {
return location; return location;
} }
export async function requestCityOperators() { export async function requestCityConfigs() {
const list = await http.get<CityOperatorListItem[]>(API.GET_ALL_CITY_OPERATOR); const list = await http.get<CityConfigListItem[]>(API.GET_ALL_CITY_CONFIGS);
store.dispatch( store.dispatch(setCityConfigs(list));
setCityOperators(
(list || []).map(it => ({
cityCode: it.cityCode,
groupLink: it.groupLink,
}))
)
);
} }

View File

@ -2,7 +2,7 @@ import Taro from '@tarojs/taro';
import { PageUrl } from '@/constants/app'; import { PageUrl } from '@/constants/app';
import { CollectEventName } from '@/constants/event'; import { CollectEventName } from '@/constants/event';
import { GenderType, MaterialViewSource } from '@/constants/material'; import { EDUCATION_TYPE_LABELS, GenderType, MaterialViewSource } from '@/constants/material';
import { MessageSubscribeIds } from '@/constants/subscribe'; import { MessageSubscribeIds } from '@/constants/subscribe';
import http from '@/http'; import http from '@/http';
import { API } from '@/http/api'; import { API } from '@/http/api';
@ -44,7 +44,9 @@ const updateUserIfNeed = async () => {
await requestUserInfo(); await requestUserInfo();
}; };
export const getBasicInfo = (profile: Pick<MaterialProfile, 'age' | 'height' | 'weight' | 'shoeSize' | 'gender'>) => { export const getBasicInfo = (
profile: Pick<MaterialProfile, 'age' | 'height' | 'weight' | 'shoeSize' | 'gender' | 'educationType'>
) => {
const result: string[] = []; const result: string[] = [];
if (typeof profile.age !== 'undefined' && profile.age !== null) { if (typeof profile.age !== 'undefined' && profile.age !== null) {
result.push(`${profile.age}`); result.push(`${profile.age}`);
@ -59,6 +61,9 @@ export const getBasicInfo = (profile: Pick<MaterialProfile, 'age' | 'height' | '
result.push(`${profile.shoeSize}`); result.push(`${profile.shoeSize}`);
} }
result.push(profile.gender === GenderType.MEN ? '男' : '女'); result.push(profile.gender === GenderType.MEN ? '男' : '女');
if (typeof profile.educationType !== 'undefined' && profile.educationType !== null) {
result.push(EDUCATION_TYPE_LABELS[profile.educationType]);
}
return result.join('·'); return result.join('·');
}; };

View File

@ -31,7 +31,7 @@ export const getUserId = () => getUserInfo().userId;
export const isValidUserInfo = (info: UserInfo) => !!info.userId; export const isValidUserInfo = (info: UserInfo) => !!info.userId;
export const isNeedLogin = (info: UserInfo) => !info.isBindPhone; export const isNeedLogin = (info: UserInfo) => !info.isBindPhone;
// export const isNeedLogin = (info: UserInfo) => !info.isBindPhone || info.userId === '218396277145075712'; // export const isNeedLogin = (info: UserInfo) => !info.isBindPhone || info.userId === '171633767918600192';
export const isNeedPhone = (info: UserInfo) => isNeedLogin(info) || !info.phone; export const isNeedPhone = (info: UserInfo) => isNeedLogin(info) || !info.phone;