13 Commits

Author SHA1 Message Date
8c5fd1a86b feat: update 2025-09-19 22:55:21 +08:00
a4e03ae1bd Merge branch 'trunk' into feat/share-coupon 2025-09-14 21:38:42 +08:00
8e42fef4f7 feat: ps 2025-09-11 21:28:01 +08:00
6f7e78896a feat: distance 2025-09-10 21:37:20 +08:00
b9cd0a3e6d feat: update material card 2025-09-08 23:28:17 +08:00
c0d27a5ab2 feat: update img 2025-09-05 01:44:42 +08:00
6d76c82e96 feat: update 2025-09-03 23:50:03 +08:00
f2e7fd9d85 feat: 2025-09-01 18:44:26 +08:00
087e05a9a0 feat: update 2025-09-01 18:43:49 +08:00
aea8323d95 feat: 2025-08-31 22:43:00 +08:00
61686950bd feat: update 2025-08-28 14:47:02 +08:00
eea89263f2 Merge branch 'trunk' into feat/share-coupon 2025-08-28 14:45:50 +08:00
285db3f445 feat: wenan update 2025-08-28 14:40:48 +08:00
17 changed files with 275 additions and 138 deletions

View File

@ -9,7 +9,7 @@ import { PageUrl } from '@/constants/app';
import { MaterialViewSource, WORK_YEAR_LABELS } from '@/constants/material'; import { MaterialViewSource, WORK_YEAR_LABELS } from '@/constants/material';
import { AnchorInfo } from '@/types/material'; import { AnchorInfo } from '@/types/material';
import { calcDistance } from '@/utils/location'; import { calcDistance } from '@/utils/location';
import { getBasicInfo } from '@/utils/material'; import { getBasicInfo, getSalary } from '@/utils/material';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
import { activeDate } from '@/utils/time'; import { activeDate } from '@/utils/time';
@ -22,17 +22,6 @@ interface IProps {
} }
const PREFIX = 'anchor-card'; const PREFIX = 'anchor-card';
const getSalary = (data: AnchorInfo) => {
const { fullTimeMinPrice, fullTimeMaxPrice, partyTimeMinPrice, partyTimeMaxPrice } = data;
const prices: string[] = [];
if (fullTimeMinPrice && fullTimeMaxPrice) {
prices.push(`${fullTimeMinPrice / 1000}-${fullTimeMaxPrice / 1000}K/月`);
}
if (partyTimeMinPrice && partyTimeMaxPrice) {
prices.push(`${partyTimeMinPrice}-${partyTimeMaxPrice}/小时`);
}
return prices.filter(Boolean).join(' ');
};
function AnchorCard(props: IProps) { function AnchorCard(props: IProps) {
const { data, jobId, validator } = props; const { data, jobId, validator } = props;

View File

@ -11,7 +11,7 @@ import { EMPLOY_TYPE_TITLE_MAP, EmployType } from '@/constants/job';
import { JobInfo } from '@/types/job'; import { JobInfo } from '@/types/job';
// import { LocationInfo } from '@/types/location'; // import { LocationInfo } from '@/types/location';
import { getJobSalary, getJobTitle } from '@/utils/job'; import { getJobSalary, getJobTitle } from '@/utils/job';
// import { calcDistance } from '@/utils/location'; import { calcDistance } from '@/utils/location';
import { navigateTo, redirectTo } from '@/utils/route'; import { navigateTo, redirectTo } from '@/utils/route';
import './index.less'; import './index.less';
@ -45,7 +45,7 @@ function JobCard(props: IProps) {
publisher, publisher,
publisherAvatar, publisherAvatar,
jobLocation, jobLocation,
// distance, distance,
isAuthed = false, isAuthed = false,
} = data; } = data;
@ -84,12 +84,12 @@ function JobCard(props: IProps) {
<div className={`${PREFIX}__summary`}>{jobDescription || sourceText}</div> <div className={`${PREFIX}__summary`}>{jobDescription || sourceText}</div>
<div className={`${PREFIX}__distance-wrapper`}> <div className={`${PREFIX}__distance-wrapper`}>
<div className={`${PREFIX}__detailed-address`}>{jobLocation?.address}</div> <div className={`${PREFIX}__detailed-address`}>{jobLocation?.address}</div>
{/*{distance && (*/} {distance && (
{/* <>*/} <>
{/* <Image className={`${PREFIX}__distance-icon`} src={require('@/statics/svg/location.svg')} />*/} <Image className={`${PREFIX}__distance-icon`} src={require('@/statics/svg/location.svg')} />
{/* <div className={`${PREFIX}__distance`}>{calcDistance(distance)}</div>*/} <div className={`${PREFIX}__distance`}>{calcDistance(distance)}</div>
{/* </>*/} </>
{/*)}*/} )}
</div> </div>
</div> </div>
<div className={`${PREFIX}__divider`} /> <div className={`${PREFIX}__divider`} />

View File

@ -4,7 +4,7 @@
.material-card { .material-card {
padding: 32px 24px; padding: 32px 24px;
border-radius: 16px; border-radius: 16px;
background: #FFFFFF; background: #ffffff;
box-sizing: border-box; box-sizing: border-box;
&__header { &__header {
@ -16,6 +16,10 @@
.flex-row(); .flex-row();
} }
&__right {
color: @blHighlightColor;
}
&__title { &__title {
font-size: 32px; font-size: 32px;
line-height: 40px; line-height: 40px;
@ -50,7 +54,7 @@
&__body { &__body {
width: 100%; width: 100%;
height: 156px; height: 160px;
margin-top: 24px; margin-top: 24px;
.flex-column(); .flex-column();
justify-content: center; justify-content: center;
@ -80,38 +84,44 @@
} }
} }
&__scroll-view { &__info {
position: relative; .flex-row();
gap: 24px;
width: 100%;
&-left {
width: 125px;
height: 160px;
}
&-cover {
width: 100%; width: 100%;
height: 100%; height: 100%;
.flex-row(); border-radius: 12px;
&::-webkit-scrollbar {
display: none;
} }
&-right {
.name {
font-style: normal;
font-weight: 500;
font-size: 28px;
line-height: 32px;
color: @blColor;
margin-bottom: 12px;
}
.info,
.worked {
font-weight: 400;
font-size: 24px;
line-height: 36px;
color: @blColorG2;
margin-bottom: 8px;
}
.salary {
font-weight: 500;
font-size: 24px;
line-height: 36px;
color: @blColor;
&::after {
content: '';
position: absolute;
right: 0;
width: 102px;
height: 100%;
background: linear-gradient(91.41deg, rgba(255, 255, 255, 0) 1.86%, #FFFFFF 99.47%);
} }
} }
&__cover-list {
height: 100%;
.flex-row();
} }
&__cover-image {
width: 120px;
height: 100%;
margin-right: 24px;
// 不知道为啥高度不对,可能 scroll-view 默认底部是滚动条高度?
margin-top: 38px;
border-radius: 8px;
}
} }

View File

@ -1,4 +1,4 @@
import { Image, ScrollView } from '@tarojs/components'; import { Image } from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import { Loading } from '@taroify/core'; import { Loading } from '@taroify/core';
@ -9,11 +9,12 @@ import { useCallback, useEffect, useRef, useState } from 'react';
import LoginButton from '@/components/login-button'; import LoginButton from '@/components/login-button';
import { EventName, PageUrl } from '@/constants/app'; import { EventName, PageUrl } from '@/constants/app';
import { CollectEventName, ReportEventId } from '@/constants/event'; import { CollectEventName, ReportEventId } from '@/constants/event';
import { WORK_YEAR_LABELS } from '@/constants/material';
import useUserInfo from '@/hooks/use-user-info'; import useUserInfo from '@/hooks/use-user-info';
import { MaterialProfile } from '@/types/material'; import { MaterialProfile } from '@/types/material';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
import { collectEvent, reportEvent } from '@/utils/event'; import { collectEvent, reportEvent } from '@/utils/event';
import { requestProfileDetail, sortVideos } from '@/utils/material'; import { getBasicInfo, getSalary, requestProfileDetail, sortVideos } from '@/utils/material';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
import Toast from '@/utils/toast'; import Toast from '@/utils/toast';
import { isValidUserInfo } from '@/utils/user'; import { isValidUserInfo } from '@/utils/user';
@ -34,8 +35,9 @@ function MaterialCard(props: IProps) {
const userInfo = useUserInfo(); const userInfo = useUserInfo();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [profile, setProfile] = useState<MaterialProfile | null>(null); const [profile, setProfile] = useState<MaterialProfile | null>(null);
const refreshRef = useRef((_f?: boolean) => { }); const refreshRef = useRef((_f?: boolean) => {});
const hasMaterial = !!profile; const hasMaterial = !!profile;
const firstCover = sortVideos(profile?.materialVideoInfoList || [])[0];
const handleGoCreateProfile = useCallback(() => { const handleGoCreateProfile = useCallback(() => {
reportEvent(ReportEventId.CLICK_GO_TO_CREATE_MATERIAL); reportEvent(ReportEventId.CLICK_GO_TO_CREATE_MATERIAL);
@ -44,7 +46,7 @@ function MaterialCard(props: IProps) {
const handleGoProfile = useCallback(() => { const handleGoProfile = useCallback(() => {
if (!hasMaterial) { if (!hasMaterial) {
realtimeLogger.info('handleGoProfile noMaterial') realtimeLogger.info('handleGoProfile noMaterial');
return; return;
} }
navigateTo(PageUrl.MaterialProfile).catch(err => { navigateTo(PageUrl.MaterialProfile).catch(err => {
@ -67,7 +69,7 @@ function MaterialCard(props: IProps) {
try { try {
const profileDetail = await requestProfileDetail(); const profileDetail = await requestProfileDetail();
if (!profileDetail) { if (!profileDetail) {
realtimeLogger.info('getProfileDetail no profileDetail') realtimeLogger.info('getProfileDetail no profileDetail');
} }
setProfile(profileDetail); setProfile(profileDetail);
} catch (e) { } catch (e) {
@ -90,8 +92,10 @@ function MaterialCard(props: IProps) {
refreshRef.current?.(true); refreshRef.current?.(true);
}; };
Taro.eventCenter.on(EventName.CREATE_PROFILE, callback); Taro.eventCenter.on(EventName.CREATE_PROFILE, callback);
Taro.eventCenter.on(EventName.UPDATE_PROFILE, callback);
return () => { return () => {
Taro.eventCenter.off(EventName.CREATE_PROFILE, callback); Taro.eventCenter.off(EventName.CREATE_PROFILE, callback);
Taro.eventCenter.on(EventName.UPDATE_PROFILE, callback);
}; };
}, [userInfo]); }, [userInfo]);
@ -100,16 +104,11 @@ function MaterialCard(props: IProps) {
<div className={`${PREFIX}__header`}> <div className={`${PREFIX}__header`}>
<div className={`${PREFIX}__header__left`}> <div className={`${PREFIX}__header__left`}>
<div className={`${PREFIX}__header__title`}></div> <div className={`${PREFIX}__header__title`}></div>
{/* {profile && (
<div
className={`${PREFIX}__header__progress`}
>{`完成度${Math.min((profile.progressBar || 0) * 100, 100)}%`}</div>
)} */}
</div> </div>
{profile && ( {profile && (
<div className={`${PREFIX}__header__right`}> <div className={`${PREFIX}__header__right`}>
{/* <div className={`${PREFIX}__header__status`}>{profile?.isOpen ? '开放中' : '关闭'}</div> */} <div></div>
<ArrowRight className={`${PREFIX}__header__icon`} /> <ArrowRight color="#6D3DF5" className={`${PREFIX}__header__icon`} />
</div> </div>
)} )}
</div> </div>
@ -123,18 +122,21 @@ function MaterialCard(props: IProps) {
</div> </div>
)} )}
{!loading && hasMaterial && ( {!loading && hasMaterial && (
<ScrollView className={`${PREFIX}__scroll-view`} showScrollbar={false} enableFlex enhanced scrollX> <div className={`${PREFIX}__info`}>
<div className={`${PREFIX}__cover-list`}> {firstCover && (
{sortVideos(profile?.materialVideoInfoList || []).map(video => ( <div className={`${PREFIX}__info-left`}>
<Image <Image className={`${PREFIX}__info-cover`} mode="aspectFill" src={firstCover.coverUrl} />
className={`${PREFIX}__cover-image`} </div>
mode="aspectFit" )}
key={video.coverUrl} <div className={`${PREFIX}__info-right`}>
src={video.coverUrl} <div className="name">{profile?.name}</div>
/> <div className="info">
))} {WORK_YEAR_LABELS[profile?.workedYear] || ''}·{getBasicInfo({ ...profile, shoeSize: null })}
</div>
<div className="worked"> {profile?.workedSecCategoryStr}</div>
<div className="salary">{getSalary(profile)}</div>
</div>
</div> </div>
</ScrollView>
)} )}
{loading && <Loading />} {loading && <Loading />}
</div> </div>

View File

@ -61,7 +61,7 @@ export default function PartnerBanner() {
<div className={`${PREFIX}__close`} onClick={handlePartnerBannerClose} /> <div className={`${PREFIX}__close`} onClick={handlePartnerBannerClose} />
</div> </div>
{visible && ( {visible && (
<LoginDialog disableCheck onCancel={() => setVisible(false)} onSuccess={handleBind} needPhone={needPhone} /> <LoginDialog onCancel={() => setVisible(false)} onSuccess={handleBind} needPhone={needPhone} />
)} )}
</> </>
); );

View File

@ -1,19 +1,19 @@
import { Button, Canvas } from '@tarojs/components'; import { Button, Canvas } from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import { useCallback, useState } from 'react'; import { useCallback } from 'react';
import { openCustomerServiceChat } from '@/utils/common'; import { openCustomerServiceChat } from '@/utils/common';
import { getPartnerQrcode } from '@/utils/partner'; import { getCouponQrCode, generateMembershipCoupon } from '@/utils/coupon';
import './index.less'; import './index.less';
const PREFIX = 'partner-intro'; const PREFIX = 'partner-intro';
export default function PartnerIntro() { export default function PartnerIntro() {
const [posterPath, setPosterPath] = useState('');
const getQrcode = async () => { const getQrcode = async () => {
try { try {
const data = await getPartnerQrcode(); // 假设 getPartnerQrcode 返回 ArrayBuffer const { code } = await generateMembershipCoupon();
const data = await getCouponQrCode(code);
const base64 = Taro.arrayBufferToBase64(data); const base64 = Taro.arrayBufferToBase64(data);
return `data:image/png;base64,${base64}`; return `data:image/png;base64,${base64}`;
} catch (error) { } catch (error) {
@ -36,11 +36,9 @@ export default function PartnerIntro() {
height: 2668, // 实际绘制高度 height: 2668, // 实际绘制高度
destWidth: 750, // 目标显示宽度 destWidth: 750, // 目标显示宽度
destHeight: 1334, // 目标显示高度 destHeight: 1334, // 目标显示高度
fileType: 'jpg', fileType: 'png',
}); });
setPosterPath(tempFilePath.tempFilePath);
resolve(tempFilePath.tempFilePath); resolve(tempFilePath.tempFilePath);
}); });
} catch (error) { } catch (error) {
@ -57,8 +55,8 @@ export default function PartnerIntro() {
const canvas = res[0].node; const canvas = res[0].node;
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
canvas.width = 550*2; canvas.width = 550 * 2;
canvas.height = 918*2; canvas.height = 918 * 2;
ctx.scale(2, 2); ctx.scale(2, 2);
// 绘制背景图片 // 绘制背景图片
@ -84,13 +82,10 @@ export default function PartnerIntro() {
}); });
}; };
const savePoster = async () => { const savePoster = async () => {
let filePath = posterPath;
if (!filePath) {
Taro.showLoading({ title: '正在生成海报' }); Taro.showLoading({ title: '正在生成海报' });
const qrCode = await getQrcode(); const qrCode = await getQrcode();
filePath = await drawCanvas(qrCode); const filePath = await drawCanvas(qrCode);
Taro.hideLoading(); Taro.hideLoading();
}
const res = await Taro.getSetting(); const res = await Taro.getSetting();
const hasPermission = res.authSetting['scope.writePhotosAlbum']; const hasPermission = res.authSetting['scope.writePhotosAlbum'];
@ -144,7 +139,9 @@ export default function PartnerIntro() {
<div className={`${PREFIX}__block`}> <div className={`${PREFIX}__block`}>
<div className={`${PREFIX}__title`}></div> <div className={`${PREFIX}__title`}></div>
<div className={`${PREFIX}__card`}> <div className={`${PREFIX}__card`}>
<div className={`${PREFIX}__body`}></div> <div className={`${PREFIX}__body`}>
</div>
</div> </div>
</div> </div>
<div className={`${PREFIX}__block`}> <div className={`${PREFIX}__block`}>

View File

@ -36,7 +36,7 @@ interface CityOption extends ISelectOption<CityValue> {
const PREFIX = 'user-batch-publish'; const PREFIX = 'user-batch-publish';
const log = logWithPrefix(PREFIX); const log = logWithPrefix(PREFIX);
const SERVICE_ILLUSTRATE = `服务方式:帮您把招聘需求发到众多同城合作主播群 const SERVICE_ILLUSTRATE = `服务方式:帮您把招聘需求发到众多同城合作主播群
群发次数:每日1次连发3天 群发次数:杭州、广州发3次其他城市1次
内容要求:仅限带货主播招聘需求,其他不发 内容要求:仅限带货主播招聘需求,其他不发
主播联系:内容中留招聘方联系方式,主播直接联系`; 主播联系:内容中留招聘方联系方式,主播直接联系`;
const cityValues: CityValue[] = [ const cityValues: CityValue[] = [

View File

@ -1,4 +1,4 @@
// export const DOMAIN = 'http://192.168.60.191:8082'; // export const DOMAIN = 'http://192.168.60.116:8082';
export const DOMAIN = process.env.BRAND === 'dev' ? 'https://dev.neighbourhood.cn' : 'https://neighbourhood.cn'; export const DOMAIN = process.env.BRAND === 'dev' ? 'https://dev.neighbourhood.cn' : 'https://neighbourhood.cn';
// export const DOMAIN = 'https://dev.neighbourhood.cn'; // export const DOMAIN = 'https://dev.neighbourhood.cn';
export const BASE_URL = `${DOMAIN}/api`; export const BASE_URL = `${DOMAIN}/api`;
@ -85,4 +85,7 @@ export enum API {
GET_PROFIT_STAT = '/user/profits', GET_PROFIT_STAT = '/user/profits',
WITHDRAW = '/user/withdraw', WITHDRAW = '/user/withdraw',
GET_WITHDRAW_LIST = '/user/withdraw/records', GET_WITHDRAW_LIST = '/user/withdraw/records',
GENERATE_MEMBERSHIP_COUPON = '/coupon/membership/generate',
CLAIM_MEMBERSHIP_COUPON = '/coupon/membership/claim',
GET_VIP_QRCODE = '/user/getVipQrCode',
} }

View File

@ -1,27 +1,54 @@
import { Image, Button } from '@tarojs/components'; import { Image, Button } from '@tarojs/components';
import { useLoad } from '@tarojs/taro';
import { Fragment, useCallback } from 'react'; import { Fragment, useCallback, useState } from 'react';
import SafeBottomPadding from '@/components/safe-bottom-padding'; import SafeBottomPadding from '@/components/safe-bottom-padding';
import { PageUrl } from '@/constants/app'; import { PageUrl } from '@/constants/app';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
import { switchTab } from '@/utils/route'; import { claimMembershipCoupon, getCouponCodeFromQuery } from '@/utils/coupon';
import { getPageQuery, switchTab } from '@/utils/route';
import { formatTime } from '@/utils/time';
import './index.less'; import './index.less';
const PREFIX = 'give-vip'; const PREFIX = 'give-vip';
const log = logWithPrefix(PREFIX); const log = logWithPrefix(PREFIX);
enum State {
PENDING,
SUCCESS,
FAILED,
}
export default function GiveVip() { export default function GiveVip() {
const [state, setState] = useState<State>(State.PENDING);
const [expireAt, setExpireAt] = useState('');
const [usedBefore, setUsedBefore] = useState(false);
const handleNavigate = useCallback(() => { const handleNavigate = useCallback(() => {
switchTab(PageUrl.Job); switchTab(PageUrl.Job);
}, []); }, []);
useLoad(() => {
const query = getPageQuery<{ d: string }>();
const code = getCouponCodeFromQuery(query);
claimMembershipCoupon(code!)
.then(res => {
setUsedBefore(res.usedBefore);
setExpireAt(res.usageExpireAt);
setState(Date.now() > new Date(res.usageExpireAt).getTime() ? State.FAILED : State.SUCCESS);
})
.catch(err => {
console.error(err);
setState(State.FAILED);
});
});
return ( return (
<div className={PREFIX}> <div className={PREFIX}>
{state === State.SUCCESS && (
<Fragment> <Fragment>
<div className={`${PREFIX}__title`}> <div className={`${PREFIX}__title`}>
<Image src="https://publiccdn.neighbourhood.com.cn/img/partner-yes.svg" className={`${PREFIX}__icon`} /> <Image src="https://publiccdn.neighbourhood.com.cn/img/partner-yes.svg" className={`${PREFIX}__icon`} />
<div></div> <div>{usedBefore ? '宝子,你已经领过了,下周再来' : '宝子,播络会员领取成功'}</div>
</div> </div>
<div className={`${PREFIX}__coupon-info`}> <div className={`${PREFIX}__coupon-info`}>
<Image src="https://publiccdn.neighbourhood.com.cn/img/parnet-share-bg.png" className={`${PREFIX}__bg`} /> <Image src="https://publiccdn.neighbourhood.com.cn/img/parnet-share-bg.png" className={`${PREFIX}__bg`} />
@ -29,9 +56,13 @@ export default function GiveVip() {
<div className={`${PREFIX}__coupon-intro`}> <div className={`${PREFIX}__coupon-intro`}>
<div className="highlight">10</div> <div className="highlight">10</div>
</div> </div>
<div className={`${PREFIX}__coupon-valid`}>2025/8/26 22:23:56</div> <div className={`${PREFIX}__coupon-valid`}>
{formatTime(expireAt, 'YYYY/MM/DD HH:mm:ss', false)}
</div>
</div> </div>
</Fragment> </Fragment>
)}
{state === State.FAILED && (
<Fragment> <Fragment>
<div className={`${PREFIX}__hint`}> <div className={`${PREFIX}__hint`}>
<Image <Image
@ -45,6 +76,7 @@ export default function GiveVip() {
<div className="highlight"></div> <div className="highlight"></div>
</div> </div>
</Fragment> </Fragment>
)}
<div className={`${PREFIX}__info-block`}> <div className={`${PREFIX}__info-block`}>
<div className={`${PREFIX}__info-title`}></div> <div className={`${PREFIX}__info-title`}></div>
<div className={`${PREFIX}__info-card`}> <div className={`${PREFIX}__info-card`}>

View File

@ -56,7 +56,27 @@
&__icon { &__icon {
width: 48px; width: 48px;
height: 48px; height: 48px;
}
&__block {
margin-top: 24px; margin-top: 24px;
.flex-row();
gap: 16px;
}
&__status {
font-size: 28px;
line-height: 32px;
font-weight: 400;
color: @blColorG1;
&.open {
color: @blHighlightColor;
}
&.error {
color: #FF5051;
}
} }
} }
} }

View File

@ -6,6 +6,7 @@ import classNames from 'classnames';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { EventName, OpenSource } from '@/constants/app'; import { EventName, OpenSource } from '@/constants/app';
import { JOB_MANAGE_STATUS_TITLE_MAP, JobManageStatus } from '@/constants/job';
import { JobManageInfo } from '@/types/job'; import { JobManageInfo } from '@/types/job';
import { getJobLocation, requestJobManageList } from '@/utils/job'; import { getJobLocation, requestJobManageList } from '@/utils/job';
import { getPageQuery, navigateBack } from '@/utils/route'; import { getPageQuery, navigateBack } from '@/utils/route';
@ -15,7 +16,14 @@ import Toast from '@/utils/toast';
import './index.less'; import './index.less';
const PREFIX = 'page-job-select-my-publish'; const PREFIX = 'page-job-select-my-publish';
const STATUS_CLASS_MAP = {
[JobManageStatus.WaitVerify]: 'pending',
[JobManageStatus.Open]: 'open',
[JobManageStatus.Pending]: 'pending',
[JobManageStatus.Error]: 'error',
[JobManageStatus.Close]: 'close',
[JobManageStatus.Expire]: 'close',
};
export default function JobSelectMyPublish() { export default function JobSelectMyPublish() {
const [list, setList] = useState<JobManageInfo[]>([]); const [list, setList] = useState<JobManageInfo[]>([]);
const [jobId, setJobId] = useState<string | null>('1'); const [jobId, setJobId] = useState<string | null>('1');
@ -57,6 +65,10 @@ export default function JobSelectMyPublish() {
</div> </div>
<div className={`${PREFIX}__right`}> <div className={`${PREFIX}__right`}>
<div className={`${PREFIX}__right__time`}>{formatTime(item.updated)}</div> <div className={`${PREFIX}__right__time`}>{formatTime(item.updated)}</div>
<div className={`${PREFIX}__right__block`}>
<div className={classNames(`${PREFIX}__right__status`, { [STATUS_CLASS_MAP[item.status]]: true })}>
{JOB_MANAGE_STATUS_TITLE_MAP[item.status]}
</div>
{item.id === jobId && ( {item.id === jobId && (
<Image <Image
mode="aspectFit" mode="aspectFit"
@ -66,6 +78,7 @@ export default function JobSelectMyPublish() {
)} )}
</div> </div>
</div> </div>
</div>
))} ))}
</List> </List>
</div> </div>

View File

@ -6,7 +6,9 @@ import { useState } from 'react';
import PartnerIntro from '@/components/partner-intro'; import PartnerIntro from '@/components/partner-intro';
import PartnerInviteList from '@/components/partner-invite-list'; import PartnerInviteList from '@/components/partner-invite-list';
import PartnerProfit from '@/components/partner-profit'; import PartnerProfit from '@/components/partner-profit';
import { PageUrl } from '@/constants/app';
import useInviteCode from '@/hooks/use-invite-code'; import useInviteCode from '@/hooks/use-invite-code';
import { generateMembershipCoupon } from '@/utils/coupon';
import { getCommonShareMessage } from '@/utils/share'; import { getCommonShareMessage } from '@/utils/share';
import './index.less'; import './index.less';
@ -18,13 +20,16 @@ export default function Partner() {
const handleChange = v => { const handleChange = v => {
setTab(v); setTab(v);
}; };
useShareAppMessage(() => { useShareAppMessage(async () => {
console.log('Partner inviteCode', inviteCode); console.log('Partner inviteCode', inviteCode);
const { code } = await generateMembershipCoupon();
return getCommonShareMessage({ return getCommonShareMessage({
useCapture: false, useCapture: false,
inviteCode, inviteCode,
title: '宝子,送你个播络会员,找工作更方便', title: '宝子,送你个播络会员,免费找主播工作',
imageUrl: 'https://publiccdn.neighbourhood.com.cn/img/share-coupon.png', path: PageUrl.GiveVip,
params: { d: code },
imageUrl: 'https://publiccdn.neighbourhood.com.cn/img/share-coupon1.png',
}); });
}); });

View File

@ -82,7 +82,7 @@ export default function BizService() {
<div className={`${PREFIX}__delegate-h5`}></div> <div className={`${PREFIX}__delegate-h5`}></div>
<div className={`${PREFIX}__delegate-card`}> <div className={`${PREFIX}__delegate-card`}>
<div className={`${PREFIX}__delegate-body`}></div> <div className={`${PREFIX}__delegate-body`}></div>
<div className={`${PREFIX}__delegate-body`}>13</div> <div className={`${PREFIX}__delegate-body`}>广31</div>
<div className={`${PREFIX}__delegate-body`}></div> <div className={`${PREFIX}__delegate-body`}></div>
<div className={`${PREFIX}__delegate-body`}></div> <div className={`${PREFIX}__delegate-body`}></div>
</div> </div>

11
src/types/coupon.ts Normal file
View File

@ -0,0 +1,11 @@
export type ClaimMembershipCouponResponse = {
code: string;
usedBefore: boolean;
usageExpireAt: string;
};
export type GenerateMembershipCouponResponse = {
code: string;
linkExpireAt: string;
shareUrl: string;
};

31
src/utils/coupon.ts Normal file
View File

@ -0,0 +1,31 @@
import http from '@/http';
import { API } from '@/http/api';
import { ClaimMembershipCouponResponse, GenerateMembershipCouponResponse } from '@/types/coupon';
export const generateMembershipCoupon = async () => {
return await http.post<GenerateMembershipCouponResponse>(API.GENERATE_MEMBERSHIP_COUPON);
};
export const claimMembershipCoupon = async (code: string) => {
return await http.post<ClaimMembershipCouponResponse>(API.CLAIM_MEMBERSHIP_COUPON, {
contentType: 'application/x-www-form-urlencoded',
data: { code },
});
};
export const getCouponQrCode = async (code: string) => {
return await http.post(API.GET_VIP_QRCODE, {
contentType: 'application/x-www-form-urlencoded',
responseType: 'arraybuffer',
data: { code },
});
};
export const getCouponCodeFromQuery = (query: Record<string, string>): string | undefined => {
if (query) {
if (query.scene) {
return query.scene.replace('d%3D', '');
}
if (query.d) {
return query.d;
}
}
return undefined;
};

View File

@ -62,6 +62,28 @@ export const getBasicInfo = (profile: Pick<MaterialProfile, 'age' | 'height' | '
return result.join('·'); return result.join('·');
}; };
export const getSalary = (
data: Pick<MaterialProfile, 'fullTimeMaxPrice' | 'fullTimeMinPrice' | 'partyTimeMaxPrice' | 'partyTimeMinPrice'>
) => {
const { fullTimeMinPrice, fullTimeMaxPrice, partyTimeMinPrice, partyTimeMaxPrice } = data;
const prices: string[] = [];
if (fullTimeMinPrice && fullTimeMaxPrice) {
if (fullTimeMinPrice >= 50000) {
prices.push(`${fullTimeMinPrice / 1000}K以上/月`);
} else {
prices.push(`${fullTimeMinPrice / 1000}-${fullTimeMaxPrice / 1000}K/月`);
}
}
if (partyTimeMinPrice && partyTimeMaxPrice) {
if (partyTimeMinPrice >= 500) {
prices.push(`500以上/小时`);
} else {
prices.push(`${partyTimeMinPrice}-${partyTimeMaxPrice}/小时`);
}
}
return prices.filter(Boolean).join(' ');
};
export const chooseMedia = async (option: Taro.chooseMedia.Option = {}) => { export const chooseMedia = async (option: Taro.chooseMedia.Option = {}) => {
try { try {
const result = await Taro.chooseMedia({ const result = await Taro.chooseMedia({

View File

@ -1,10 +1,12 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
export function formatTime(time: number | string, template = 'YYYY-MM-DD'): string { export function formatTime(time: number | string, template = 'YYYY-MM-DD', toNum = true): string {
if (!time) { if (!time) {
return ''; return '';
} }
if (toNum) {
time = Number(time); time = Number(time);
}
return dayjs(time).format(template); return dayjs(time).format(template);
} }