diff --git a/src/components/prejob-popup/index.tsx b/src/components/prejob-popup/index.tsx index fbbb6e2..6744e99 100644 --- a/src/components/prejob-popup/index.tsx +++ b/src/components/prejob-popup/index.tsx @@ -30,7 +30,7 @@ const GET_CONTACT_TYPE_OPTIONS = [ type: GET_CONTACT_TYPE.VIP, icon: 'https://publiccdn.neighbourhood.com.cn/img/diamond.svg', title: '播络会员', - desc: '开通会员每天可查看10个', + desc: '开通会员每天免费查看', btnText: '开通', }, ]; @@ -81,7 +81,7 @@ export function PrejobPopup({ onCancel, onConfirm }: IProps) { - + diff --git a/src/components/product-dialog/contact/index.tsx b/src/components/product-dialog/contact/index.tsx new file mode 100644 index 0000000..f4a2453 --- /dev/null +++ b/src/components/product-dialog/contact/index.tsx @@ -0,0 +1,102 @@ +import Taro from '@tarojs/taro'; + +import { Dialog } from '@taroify/core'; +import { useCallback, useEffect, useRef, useState } from 'react'; + +import { DialogStatus, PREFIX } from '@/components/product-dialog/const'; +import ContactCustomerService from '@/components/product-dialog/steps-ui/job-contact-customer'; +import ContactDirect from '@/components/product-dialog/steps-ui/job-contact-direct'; +import UnableUnlockContent from '@/components/product-dialog/steps-ui/job-unable'; +import { DeclarationType, ProductType } from '@/constants/product'; +import { JobDetails } from '@/types/job'; +import { ProductInfo } from '@/types/product'; +import { logWithPrefix } from '@/utils/common'; +import { requestProductUseRecord, requestUseProduct } from '@/utils/product'; +import Toast from '@/utils/toast'; + +import '../index.less'; +import { EventName } from '@/constants/app'; + +interface IProps { + data: JobDetails; + productInfo?: ProductInfo; + onClose: () => void; +} + +const PRODUCT_CODE = ProductType.VIP; +const log = logWithPrefix('product-contact-dialog'); + +function ProductContactDialog(props: Omit) { + const { data, productInfo: productInfoProps, onClose } = props; + const [status, setStatus] = useState(DialogStatus.LOADING); + const [publisherAcctNo, setPublisherAcctNo] = useState(''); + const initRef = useRef(() => {}); + + const handleCloseDialog = useCallback(() => { + onClose(); + }, [onClose]); + + const handleReport = useCallback(() => { + log('report', data.id); + }, [data]); + + useEffect(() => { + initRef.current = async () => { + const handleContact = (declarationTypeResult?: ProductInfo['declarationTypeResult']) => { + if (declarationTypeResult?.type === DeclarationType.Direct && declarationTypeResult.publisherAcctNo) { + setStatus(DialogStatus.JOB_CONTACT_DIRECT); + setPublisherAcctNo(declarationTypeResult.publisherAcctNo); + } else { + setStatus(DialogStatus.JOB_CONTACT_CS); + } + }; + try { + Taro.showLoading(); + // if (1 < 2) { + // setStatus(DialogStatus.JOB_CONTACT_CS); + // return; + // } + const result = await requestProductUseRecord(PRODUCT_CODE, { jobId: data.id }); + log('requestProductUseRecord result', result); + if (result) { + handleContact(result.declarationTypeResult); + return; + } + if (!productInfoProps?.balance) { + setStatus(DialogStatus.JOB_UNABLE_UNLOCK); + return; + } + const productInfo = await requestUseProduct(PRODUCT_CODE, { jobId: data.id }); + Taro.eventCenter.trigger(EventName.READ_CONTACT); + handleContact(productInfo.declarationTypeResult); + } catch (e) { + Toast.error('出错了,请重试'); + handleCloseDialog(); + } finally { + Taro.hideLoading(); + } + }; + }, [data, handleCloseDialog]); + + useEffect(() => { + initRef.current(); + }, []); + + if (status === DialogStatus.LOADING) { + return null; + } + + return ( + + + {status === DialogStatus.JOB_CONTACT_CS && } + {status === DialogStatus.JOB_CONTACT_DIRECT && ( + + )} + {status === DialogStatus.JOB_UNABLE_UNLOCK && } + + + ); +} + +export default ProductContactDialog; diff --git a/src/components/product-dialog/steps-ui/company-publish-job-buy.tsx b/src/components/product-dialog/steps-ui/company-publish-job-buy.tsx index daddf71..2d197b4 100644 --- a/src/components/product-dialog/steps-ui/company-publish-job-buy.tsx +++ b/src/components/product-dialog/steps-ui/company-publish-job-buy.tsx @@ -2,7 +2,7 @@ import Taro from '@tarojs/taro'; import { Button } from '@taroify/core'; import classNames from 'classnames'; -import { useCallback, useEffect, useState } from 'react'; +import { Fragment, useCallback, useEffect, useState } from 'react'; import Badge from '@/components/badge'; import { PREFIX } from '@/components/product-dialog/const'; @@ -11,9 +11,17 @@ import { OrderStatus, OrderType, ProductSpecId, ProductType } from '@/constants/ import { SubscribeTempId } from '@/constants/subscribe'; import { logWithPrefix } from '@/utils/common'; import { collectEvent, reportEvent } from '@/utils/event'; -import { getOrderPrice, isCancelPay, requestCreatePayInfo, requestOrderInfo, requestPayment } from '@/utils/product'; +import { + getOrderPrice, + isCancelPay, + requestCreatePayInfo, + requestOrderInfo, + requestPayment, + requestProductTypeList, +} from '@/utils/product'; import { postSubscribe, subscribeMessage } from '@/utils/subscribe'; import Toast from '@/utils/toast'; +import { ProductSpecResult } from '@/types/product'; interface IProps { onNext: () => void; @@ -82,22 +90,35 @@ const subscribe = async () => { postSubscribe(TEMP_IDS, successIds); }; +const getJsonContent = (jsonString: string) => { + try { + return JSON.parse(jsonString); + } catch (e) { + return []; + } +}; + export default function CompanyPublishJobBuy(props: IProps) { const { onNext } = props; - const [selectItem, setSelectItem] = useState(LIST[0]); + const [productList, setProductList] = useState([]); + const [selectItem, setSelectItem] = useState(); - const handleClickItem = useCallback((newSelectItem: Item) => setSelectItem(newSelectItem), []); + const handleClickItem = useCallback((newSelectItem: ProductSpecResult) => setSelectItem(newSelectItem), []); const handleBuy = useCallback(async () => { log('handleBuy'); reportEvent(ReportEventId.CLICK_PAY_PUBLISH_JOB); + if (!selectItem) { + Toast.error('请选择购买的产品'); + return; + } try { Taro.showLoading(); const { payOrderNo, createPayInfo } = await requestCreatePayInfo({ type: OrderType.CompanyVip, - amt: getOrderPrice(selectItem.amt), + amt: selectItem.payPrice, productCode: ProductType.BOSS_VIP_NEW, - productSpecId: selectItem.id, + productSpecId: selectItem.productSpecId, }); log('handleBuy payInfo', payOrderNo, createPayInfo); await requestPayment({ @@ -121,8 +142,14 @@ export default function CompanyPublishJobBuy(props: IProps) { log('handleBuy error', e); } }, [selectItem, onNext]); + const handleGetProductInfo = useCallback(async () => { + const result = await requestProductTypeList(ProductType.BOSS_VIP_NEW); + setProductList(result); + setSelectItem(result[0]); + }, []); useEffect(() => { + handleGetProductInfo(); collectEvent(CollectEventName.CREATE_ORDER_VIEW, { orderType: OrderType.BossVip, source: 'user-page' }); }, []); @@ -130,19 +157,21 @@ export default function CompanyPublishJobBuy(props: IProps) {
发认证通告限时折扣
- {LIST.map(item => ( + {productList.map(item => (
handleClickItem(item)} + onClick={item.payPrice === 0 ? undefined : () => handleClickItem(item)} > -
+
{item.title}
-
{item.price}
+
{item.priceText}
{item.badge && }
))} @@ -152,20 +181,24 @@ export default function CompanyPublishJobBuy(props: IProps) {
包含
-
- {selectItem.contents.map(i => ( -
- {i.content} - {i.inlineHighlight &&
{i.inlineHighlight}
} + {selectItem && ( + +
+ {getJsonContent(selectItem.contentsJson).map(i => ( +
+ {i.content} + {i.inlineHighlight &&
{i.inlineHighlight}
} +
+ ))}
- ))} -
- + + + )}
); } diff --git a/src/components/product-dialog/steps-ui/job-buy.tsx b/src/components/product-dialog/steps-ui/job-buy.tsx index 301103c..bb0bcdb 100644 --- a/src/components/product-dialog/steps-ui/job-buy.tsx +++ b/src/components/product-dialog/steps-ui/job-buy.tsx @@ -6,51 +6,59 @@ import { useCallback, useEffect, useState } from 'react'; import Badge from '@/components/badge'; import { PREFIX } from '@/components/product-dialog/const'; +import { PageUrl } from '@/constants/app'; import { CollectEventName } from '@/constants/event'; -import { OrderStatus, OrderType, ProductSpecId, ProductType } from '@/constants/product'; +import { OrderStatus, OrderType, ProductType } from '@/constants/product'; import { SubscribeTempId } from '@/constants/subscribe'; +import { ProductSpecResult } from '@/types/product'; import { logWithPrefix } from '@/utils/common'; import { collectEvent } from '@/utils/event'; -import { getOrderPrice, isCancelPay, requestCreatePayInfo, requestOrderInfo, requestPayment } from '@/utils/product'; +import { + isCancelPay, + requestCreatePayInfo, + requestOrderInfo, + requestPayment, + requestProductTypeList, +} from '@/utils/product'; +import { navigateTo } from '@/utils/route'; import { postSubscribe, subscribeMessage } from '@/utils/subscribe'; import Toast from '@/utils/toast'; interface IProps { - buyOnly?: boolean; onConfirm: () => void; } -interface Item { - id: ProductSpecId; - title: string; - content: string; - buyOnlyContent?: string; - price: string; - amt: number; - badge?: string; -} +// interface Item { +// id: ProductSpecId; +// title: string; +// content: string; +// buyOnlyContent?: string; +// price: string; +// amt: number; +// badge?: string; +// } -const LIST: Item[] = [ - { id: ProductSpecId.WeeklyVIP, title: '非会员', content: '每日2次', price: '免费', amt: 0 }, - { - id: ProductSpecId.DailyVIP, - title: '日会员', - content: '每日+10次', - buyOnlyContent: '每日12次', - price: '60播豆', - amt: 6, - badge: '限时体验', - }, - { - id: ProductSpecId.WeeklyVIP, - title: '周会员', - content: '每日+10次', - buyOnlyContent: '每日12次', - price: '180播豆', - amt: 18, - badge: ' 超值', - }, -]; +// const LIST: Item[] = [ +// { id: ProductSpecId.WeeklyVIP, title: '非会员', content: '每日2次', price: '免费', amt: 0 }, +// { +// id: ProductSpecId.DailyVIP, +// title: '日会员', +// content: '每日+10次', +// buyOnlyContent: '每日12次', +// price: '60播豆', +// amt: 6, +// badge: '限时体验', +// }, +// { +// id: ProductSpecId.WeeklyVIP, +// title: '周会员', +// content: '每日+10次', +// buyOnlyContent: '每日12次', +// price: '180播豆', +// amt: 18, +// badge: ' 超值', +// }, +// ]; const log = logWithPrefix('job-buy'); const SUBSCRIBE_ID = SubscribeTempId.SUBSCRIBE_VIP; @@ -66,20 +74,26 @@ const subscribe = async () => { }; export default function JobBuy(props: IProps) { - const { onConfirm, buyOnly } = props; - const [selectItem, setSelectItem] = useState(LIST[1]); + const { onConfirm } = props; + const [productList, setProductList] = useState([]); + const [selectItem, setSelectItem] = useState(); - const handleClickItem = useCallback((newSelectItem: Item) => setSelectItem(newSelectItem), []); + const handleClickItem = useCallback((newSelectItem: ProductSpecResult) => setSelectItem(newSelectItem), []); const handleBuy = useCallback(async () => { log('handleBuy', selectItem); + if (!selectItem) { + Toast.error('请选择购买的产品'); + return; + } try { Taro.showLoading(); + const { payOrderNo, createPayInfo } = await requestCreatePayInfo({ type: OrderType.VIP, - amt: getOrderPrice(selectItem.amt), + amt: selectItem.payPrice, productCode: ProductType.VIP, - productSpecId: selectItem.id, + productSpecId: selectItem.productSpecId, }); log('handleBuy payInfo', payOrderNo, createPayInfo); await requestPayment({ @@ -104,59 +118,65 @@ export default function JobBuy(props: IProps) { } }, [selectItem, onConfirm]); + const handleGetProductInfo = useCallback(async () => { + const result = await requestProductTypeList(ProductType.VIP); + setProductList(result); + setSelectItem(result[0]); + }, []); + + const handleResume = useCallback(() => { + navigateTo(PageUrl.MaterialUploadVideo); + }, []); + useEffect(() => { + handleGetProductInfo(); collectEvent(CollectEventName.CREATE_ORDER_VIEW, { orderType: OrderType.VIP }); }, []); return (
- {buyOnly ? ( -
开通播络会员即可直接查看联系方式
- ) : ( -
-
今日通告对接次数
-
已用完
+
+
免费查看次数已用完
+
请升级会员
+
+
+
完善模卡
+
赠10次查看
+
+ 去完善
- )} - {buyOnly ? ( -
每天可获取12个联系方式
- ) : ( -
-
-
明日
-
再来 或
-
升级会员
-
- )} +
- {LIST.map(item => { - if (buyOnly && !item.amt) { - return null; - } + {productList.map(item => { return (
handleClickItem(item)} + onClick={item.payPrice === 0 ? undefined : () => handleClickItem(item)} > -
+
{item.title}
-
{buyOnly ? item.buyOnlyContent : item.content}
-
{item.price}
+
{item.contentSingle}
+
{item.showPrice}元
{item.badge && }
); })}
-
- 注:日会员有效期为
支付后24小时
-
- {/*
{`已选:${selectItem.title},含进本地群服务`}
*/}
diff --git a/src/components/product-dialog/steps-ui/job-contact-direct.tsx b/src/components/product-dialog/steps-ui/job-contact-direct.tsx index 57b3231..e997753 100644 --- a/src/components/product-dialog/steps-ui/job-contact-direct.tsx +++ b/src/components/product-dialog/steps-ui/job-contact-direct.tsx @@ -10,7 +10,7 @@ interface IContactDirectProps { } export default function ContactDirect(props: IContactDirectProps) { - const { publisherAcctNo, onAfterConfirm, onReport } = props; + const { publisherAcctNo, onAfterConfirm } = props; const handleCopyAndContact = async () => { await copy(publisherAcctNo); diff --git a/src/constants/app.ts b/src/constants/app.ts index 0b40374..2022998 100644 --- a/src/constants/app.ts +++ b/src/constants/app.ts @@ -17,6 +17,7 @@ export enum EventName { ADD_GROUP = 'add_group', SELECT_CITY = 'select_city', CREATE_PROFILE = 'create_profile', + READ_CONTACT = 'read_contact', UPDATE_PROFILE = 'update_profile', EDIT_JOB_DESCRIBE = 'edit_job_describe', JOB_UPDATE = 'job_update', diff --git a/src/pages/job-detail/index.less b/src/pages/job-detail/index.less index 871e0f8..1a16fec 100644 --- a/src/pages/job-detail/index.less +++ b/src/pages/job-detail/index.less @@ -269,6 +269,21 @@ flex: 2 2; .button(@height: 88px; @fontSize: 32px; @fontWeight: 500; @borderRadius: 44px;); margin-left: 32px; + position: relative; + overflow: visible; + + &-tag { + position: absolute; + right: 8px; + top: -12px; + font-weight: 400; + font-size: 24px; + line-height: 32px; + color: #FFFFFF; + padding: 2px 8px; + background: #FF5051; + border-radius: 20px 24px 24px 0px; + } } -} \ No newline at end of file +} diff --git a/src/pages/job-detail/index.tsx b/src/pages/job-detail/index.tsx index 8114e77..0f22a4f 100644 --- a/src/pages/job-detail/index.tsx +++ b/src/pages/job-detail/index.tsx @@ -12,8 +12,9 @@ import { JoinGroupHint } from '@/components/join-group-hint'; import LoginButton from '@/components/login-button'; import PageLoading from '@/components/page-loading'; import { PrejobPopup } from '@/components/prejob-popup'; -import ProductJobDialog from '@/components/product-dialog/job'; +import ProductContactDialog from '@/components/product-dialog/contact'; import CompanyPublishJobBuy from '@/components/product-dialog/steps-ui/company-publish-job-buy'; +import JobBuy from '@/components/product-dialog/steps-ui/job-buy'; import { EventName, PageUrl, RoleType } from '@/constants/app'; import { CertificationStatusType } from '@/constants/company'; import { ReportEventId } from '@/constants/event'; @@ -27,6 +28,7 @@ import { RESPONSE_ERROR_CODE } from '@/http/constant'; import { HttpError } from '@/http/error'; import { JobDetails } from '@/types/job'; import { IMaterialMessage } from '@/types/message'; +import { ProductInfo } from '@/types/product'; import { switchRoleType } from '@/utils/app'; import { copy, logWithPrefix } from '@/utils/common'; import { reportEvent } from '@/utils/event'; @@ -35,13 +37,11 @@ import { calcDistance, isValidLocation } from '@/utils/location'; import { requestProfileDetail } from '@/utils/material'; import { isChatWithSelf, postCreateChat } from '@/utils/message'; import { getInviteCodeFromQueryAndUpdate } from '@/utils/partner'; -import { requestProductBalance, requestProductUseRecord } from '@/utils/product'; +import { requestProductBalance } from '@/utils/product'; import { getJumpUrl, getPageQuery, navigateTo } from '@/utils/route'; import { getCommonShareMessage } from '@/utils/share'; import { formatDate } from '@/utils/time'; import Toast from '@/utils/toast'; -import { isNeedCreateMaterial } from '@/utils/user'; - import './index.less'; const PREFIX = 'job-detail'; @@ -70,8 +70,15 @@ const getMapCallout = (data: JobDetails): MapProps.callout | undefined => { const AnchorFooter = (props: { data: JobDetails }) => { const { data } = props; const [errorTips, setErrorTips] = useState(''); - const [dialogVisible, setDialogVisible] = useState(false); + const [contactDialogVisible, setContactDialogVisible] = useState(false); const [showMaterialGuide, setShowMaterialGuide] = useState(false); + const [showBuyDialog, setShowBuyDialog] = useState(false); + const [productInfo, setProductInfo] = useState(); + + const getProductBalance = useCallback(async () => { + const [, resp] = await requestProductBalance(ProductType.VIP); + setProductInfo(resp); + }, [data.id]); const handleClickContact = useCallback(async () => { log('handleClickContact'); @@ -79,21 +86,8 @@ const AnchorFooter = (props: { data: JobDetails }) => { return; } reportEvent(ReportEventId.CLICK_JOB_CONTACT); - try { - const needCreateMaterial = await isNeedCreateMaterial(); - if (data.sourcePlat !== 'bl') { - if (needCreateMaterial) { - const result = await requestProductUseRecord(ProductType.VIP, { jobId: data.id }); - if (!result) { - const [time, isPaidVip] = await requestProductBalance(ProductType.VIP); - if (time <= 0 || !isPaidVip) { - setShowMaterialGuide(true); - return; - } - } - } - } + try { if (data.isAuthed) { const toUserId = data.userId; if (isChatWithSelf(toUserId)) { @@ -102,7 +96,7 @@ const AnchorFooter = (props: { data: JobDetails }) => { } const chat = await postCreateChat(toUserId); let materialMessage: null | IMaterialMessage = null; - if (!needCreateMaterial) { + if (!!productInfo?.isCreateResume) { const profile = await requestProfileDetail(); if (profile) { materialMessage = { @@ -124,7 +118,17 @@ const AnchorFooter = (props: { data: JobDetails }) => { jobId: data.id, }); } else { - setDialogVisible(true); + if (!productInfo?.isCreateResume && !productInfo?.isPaidVip && !productInfo?.freeBalance) { + setShowMaterialGuide(true); + return; + } + + if (!productInfo.isPaidVip && !productInfo.freeBalance) { + setShowBuyDialog(true); + return; + } + + setContactDialogVisible(true); } } catch (error) { const e = error as HttpError; @@ -135,17 +139,57 @@ const AnchorFooter = (props: { data: JobDetails }) => { Toast.error('请求失败请重试'); } } - }, [data]); + }, [data, productInfo?.freeBalance, productInfo?.isCreateResume, productInfo?.isPaidVip]); const handleDialogHidden = useCallback(() => { - setDialogVisible(false); + setContactDialogVisible(false); }, []); - const handleConfirmPrejob = useCallback((type: GET_CONTACT_TYPE) => { - setShowMaterialGuide(false); - if (GET_CONTACT_TYPE.VIP === type) { - setDialogVisible(true); - } + const handleConfirmPrejob = useCallback( + (type: GET_CONTACT_TYPE) => { + setShowMaterialGuide(false); + if (GET_CONTACT_TYPE.VIP === type) { + getProductBalance(); + setContactDialogVisible(true); + } + }, + [getProductBalance] + ); + + const handleAfterBuy = useCallback(() => { + setShowBuyDialog(false); + getProductBalance(); + setContactDialogVisible(true); + }, [getProductBalance]); + + const handleCancel = useCallback(() => { + setShowBuyDialog(false); }, []); + + // const unAuthedButtonText = useMemo(() => { + // if (haveSeen) { + // return '查看联系方式'; + // } + // + // if (productInfo?.isPaidVip) { + // return '您是会员,可直接查看'; + // } + // + // if (productInfo?.freeBalance) { + // return `还剩${productInfo.freeBalance}次查看次数`; + // } + // + // return productInfo?.isCreateResume? '升级会员即可查看': '创建模卡,免费查看'; + // }, [productInfo, haveSeen]); + + useEffect(() => { + Taro.eventCenter.on(EventName.CREATE_PROFILE, getProductBalance); + Taro.eventCenter.on(EventName.READ_CONTACT, getProductBalance); + }, []); + + useEffect(() => { + getProductBalance(); + }, [getProductBalance]); + return ( <>
@@ -153,14 +197,28 @@ const AnchorFooter = (props: { data: JobDetails }) => { 分享 - {data.isAuthed ? '急招岗位可免费查看' : '查看联系方式'} + {data.isAuthed ? '在线沟通' : '查看联系方式'} + {data.isAuthed || productInfo?.content ? ( +
+ {data.isAuthed ? '急招岗位可免费查看' : productInfo?.content} +
+ ) : null}
- {dialogVisible && } + {contactDialogVisible && ( + + )} {showMaterialGuide && ( setShowMaterialGuide(false)} onConfirm={handleConfirmPrejob} /> )} + {showBuyDialog && ( + + + + + + )} (buildUrl(API.LIST_PRODUCT_TYPE, { productType })); + const list = await http.get(buildUrl(API.LIST_PRODUCT_TYPE, { productType })); + return list.sort((a, b) => a.sort - b.sort); } // 使用某一个产品 @@ -59,13 +61,14 @@ export async function requestUseProduct( } // 获取某个产品的剩余解锁次数 -export async function requestProductBalance(productCode: ProductType): Promise<[number, boolean | undefined]> { +export async function requestProductBalance(productCode: ProductType): Promise<[number, ProductInfo]> { const data: GetProductDetailRequest = { productCode, userId: getUserId() }; - const { balance, isPaidVip } = await http.post(API.GET_PRODUCT_DETAIL, { + const result = await http.post(API.GET_PRODUCT_DETAIL, { data, contentType: 'application/x-www-form-urlencoded', }); - return [balance, isPaidVip]; + + return [result.balance, result]; } // 是否可以购买某一个产品