Compare commits
13 Commits
d0369bab36
...
feat/share
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c5fd1a86b | |||
| a4e03ae1bd | |||
| 8e42fef4f7 | |||
| 6f7e78896a | |||
| b9cd0a3e6d | |||
| c0d27a5ab2 | |||
| 6d76c82e96 | |||
| f2e7fd9d85 | |||
| 087e05a9a0 | |||
| aea8323d95 | |||
| 61686950bd | |||
| eea89263f2 | |||
| 285db3f445 |
@ -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;
|
||||||
|
|||||||
@ -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`} />
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -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>
|
||||||
|
|||||||
@ -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} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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`}>
|
||||||
|
|||||||
@ -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[] = [
|
||||||
|
|||||||
@ -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',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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`}>
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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>
|
||||||
|
|||||||
@ -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',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -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`}>群发次数:每日1次,连发3天</div>
|
<div className={`${PREFIX}__delegate-body`}>群发次数:杭州、广州发3次,其他城市1次</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
11
src/types/coupon.ts
Normal 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
31
src/utils/coupon.ts
Normal 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;
|
||||||
|
};
|
||||||
@ -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({
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user