From a07b015d8ee58ec6c0f0b740daa0cd61478aeae5 Mon Sep 17 00:00:00 2001 From: chashaobao Date: Fri, 28 Nov 2025 00:16:25 +0800 Subject: [PATCH 01/18] feat: --- src/http/api.ts | 2 ++ src/pages/job-detail/index.tsx | 2 +- src/types/product.ts | 17 +++++++++++++++++ src/utils/product.ts | 7 ++++++- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/http/api.ts b/src/http/api.ts index d733e38..7d8f2e1 100644 --- a/src/http/api.ts +++ b/src/http/api.ts @@ -41,6 +41,8 @@ export enum API { PRODUCT_USE_RECORD = '/product/getProductUseRecord', // 查询某个产品是否已经解锁 USE_PRODUCT = '/product/use', // 使用某个产品扣费/次数 ALLOW_BUY_PRODUCT = '/product/allowBuyProduct', // 是否可以购买某个产品 + LIST_PRODUCT_TYPE = '/product/listByType/{productType}', + CS_QR_CODE = '/customerService/get', // 客服微信二维码 CREATE_PAY_ORDER = '/payOrder/create', // 创建支付订单 GET_PAY_ORDER = '/payOrder/get', // 订单查询 diff --git a/src/pages/job-detail/index.tsx b/src/pages/job-detail/index.tsx index 35d61a0..8114e77 100644 --- a/src/pages/job-detail/index.tsx +++ b/src/pages/job-detail/index.tsx @@ -153,7 +153,7 @@ const AnchorFooter = (props: { data: JobDetails }) => { 分享 - {data.isAuthed ? '在线沟通' : '立即联系'} + {data.isAuthed ? '急招岗位可免费查看' : '查看联系方式'}
diff --git a/src/types/product.ts b/src/types/product.ts index 239a452..90ae46d 100644 --- a/src/types/product.ts +++ b/src/types/product.ts @@ -107,3 +107,20 @@ export interface GetOrderInfoRequest { payOrderNo: string; userId: string; } + +export interface ProductSpecResult { + productId: string; + productSpecId: string; + productType: ProductType; + productName: string; + title: string; + priceText: string; + payPrice: number; // 分 + showPrice: number; + originalPrice: number; + badge: string; + contentSingle: string; + contentsJson: string; + sort: number; + createTime: string; +} diff --git a/src/utils/product.ts b/src/utils/product.ts index c473758..6c715a9 100644 --- a/src/utils/product.ts +++ b/src/utils/product.ts @@ -14,8 +14,9 @@ import { CreatePayInfoResponse, CreatePayOrderParams, GetOrderInfoRequest, - OrderInfo, + OrderInfo, ProductSpecResult, } from '@/types/product'; +import { buildUrl } from '@/utils/common'; import { getUserId } from '@/utils/user'; export const isCancelPay = err => err?.errMsg === 'requestPayment:fail cancel'; @@ -44,6 +45,10 @@ export async function requestProductUseRecord( }); } +export async function requestProductTypeList(productType: ProductType) { + return await http.get(buildUrl(API.LIST_PRODUCT_TYPE, { productType })); +} + // 使用某一个产品 export async function requestUseProduct( productCode: ProductType, From 288521ebd92ea10561e052f31370c99bbccd4043 Mon Sep 17 00:00:00 2001 From: chashaobao Date: Tue, 2 Dec 2025 21:18:50 +0800 Subject: [PATCH 02/18] feat: --- src/components/prejob-popup/index.tsx | 4 +- .../product-dialog/contact/index.tsx | 102 +++++++++++ .../steps-ui/company-publish-job-buy.tsx | 85 ++++++--- .../product-dialog/steps-ui/job-buy.tsx | 162 ++++++++++-------- .../steps-ui/job-contact-direct.tsx | 2 +- src/constants/app.ts | 1 + src/pages/job-detail/index.less | 17 +- src/pages/job-detail/index.tsx | 118 +++++++++---- src/types/product.ts | 20 ++- src/utils/product.ts | 13 +- 10 files changed, 381 insertions(+), 143 deletions(-) create mode 100644 src/components/product-dialog/contact/index.tsx 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]; } // 是否可以购买某一个产品 From 6b084b2df262628d3542173ed92a390e6ceac992 Mon Sep 17 00:00:00 2001 From: chashaobao Date: Tue, 2 Dec 2025 21:44:07 +0800 Subject: [PATCH 03/18] feat --- .../product-dialog/contact/index.tsx | 16 ++++----- src/pages/job-detail/index.tsx | 36 ++++++++++++++----- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/components/product-dialog/contact/index.tsx b/src/components/product-dialog/contact/index.tsx index f4a2453..f2a7d36 100644 --- a/src/components/product-dialog/contact/index.tsx +++ b/src/components/product-dialog/contact/index.tsx @@ -7,18 +7,19 @@ 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 { EventName } from '@/constants/app'; import { DeclarationType, ProductType } from '@/constants/product'; import { JobDetails } from '@/types/job'; -import { ProductInfo } from '@/types/product'; +import { GetProductIsUnlockResponse, 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; + productRecord?: GetProductIsUnlockResponse; productInfo?: ProductInfo; onClose: () => void; } @@ -27,7 +28,7 @@ const PRODUCT_CODE = ProductType.VIP; const log = logWithPrefix('product-contact-dialog'); function ProductContactDialog(props: Omit) { - const { data, productInfo: productInfoProps, onClose } = props; + const { data, productInfo: productInfoProps, productRecord, onClose } = props; const [status, setStatus] = useState(DialogStatus.LOADING); const [publisherAcctNo, setPublisherAcctNo] = useState(''); const initRef = useRef(() => {}); @@ -56,10 +57,9 @@ function ProductContactDialog(props: Omit) { // setStatus(DialogStatus.JOB_CONTACT_CS); // return; // } - const result = await requestProductUseRecord(PRODUCT_CODE, { jobId: data.id }); - log('requestProductUseRecord result', result); - if (result) { - handleContact(result.declarationTypeResult); + log('requestProductUseRecord result', productRecord); + if (productRecord) { + handleContact(productRecord.declarationTypeResult); return; } if (!productInfoProps?.balance) { @@ -76,7 +76,7 @@ function ProductContactDialog(props: Omit) { Taro.hideLoading(); } }; - }, [data, handleCloseDialog]); + }, [data, handleCloseDialog, productRecord, productInfoProps?.balance]); useEffect(() => { initRef.current(); diff --git a/src/pages/job-detail/index.tsx b/src/pages/job-detail/index.tsx index 0f22a4f..d26fe33 100644 --- a/src/pages/job-detail/index.tsx +++ b/src/pages/job-detail/index.tsx @@ -28,7 +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 { GetProductIsUnlockResponse, ProductInfo } from '@/types/product'; import { switchRoleType } from '@/utils/app'; import { copy, logWithPrefix } from '@/utils/common'; import { reportEvent } from '@/utils/event'; @@ -37,7 +37,7 @@ import { calcDistance, isValidLocation } from '@/utils/location'; import { requestProfileDetail } from '@/utils/material'; import { isChatWithSelf, postCreateChat } from '@/utils/message'; import { getInviteCodeFromQueryAndUpdate } from '@/utils/partner'; -import { requestProductBalance } from '@/utils/product'; +import { requestProductBalance, requestProductUseRecord } from '@/utils/product'; import { getJumpUrl, getPageQuery, navigateTo } from '@/utils/route'; import { getCommonShareMessage } from '@/utils/share'; import { formatDate } from '@/utils/time'; @@ -74,11 +74,17 @@ const AnchorFooter = (props: { data: JobDetails }) => { const [showMaterialGuide, setShowMaterialGuide] = useState(false); const [showBuyDialog, setShowBuyDialog] = useState(false); const [productInfo, setProductInfo] = useState(); + const [productRecord, setProductRecord] = useState(); + + const getProductRecord = useCallback(async () => { + const result = await requestProductUseRecord(ProductType.VIP, { jobId: data.id }); + setProductRecord(result); + }, [data.id]); const getProductBalance = useCallback(async () => { const [, resp] = await requestProductBalance(ProductType.VIP); setProductInfo(resp); - }, [data.id]); + }, []); const handleClickContact = useCallback(async () => { log('handleClickContact'); @@ -155,9 +161,9 @@ const AnchorFooter = (props: { data: JobDetails }) => { [getProductBalance] ); - const handleAfterBuy = useCallback(() => { + const handleAfterBuy = useCallback(async () => { setShowBuyDialog(false); - getProductBalance(); + await getProductBalance(); setContactDialogVisible(true); }, [getProductBalance]); @@ -184,12 +190,21 @@ const AnchorFooter = (props: { data: JobDetails }) => { useEffect(() => { Taro.eventCenter.on(EventName.CREATE_PROFILE, getProductBalance); Taro.eventCenter.on(EventName.READ_CONTACT, getProductBalance); - }, []); + + return () => { + Taro.eventCenter.off(EventName.CREATE_PROFILE); + Taro.eventCenter.off(EventName.READ_CONTACT); + }; + }, [getProductBalance]); useEffect(() => { getProductBalance(); }, [getProductBalance]); + useEffect(() => { + getProductRecord(); + }, [getProductRecord]); + return ( <>
@@ -198,7 +213,7 @@ const AnchorFooter = (props: { data: JobDetails }) => { {data.isAuthed ? '在线沟通' : '查看联系方式'} - {data.isAuthed || productInfo?.content ? ( + {!productRecord && (data.isAuthed || productInfo?.content) ? (
{data.isAuthed ? '急招岗位可免费查看' : productInfo?.content}
@@ -207,7 +222,12 @@ const AnchorFooter = (props: { data: JobDetails }) => {
{contactDialogVisible && ( - + )} {showMaterialGuide && ( setShowMaterialGuide(false)} onConfirm={handleConfirmPrejob} /> From 4ed1d4587314752bfae86d51c4f76d81a7d54d80 Mon Sep 17 00:00:00 2001 From: chashaobao Date: Sat, 6 Dec 2025 21:19:25 +0800 Subject: [PATCH 04/18] feat: --- src/components/prejob-popup/index.tsx | 24 ++++++++++++++++--- .../product-dialog/contact/index.tsx | 3 ++- src/constants/cache-key.ts | 1 + src/constants/job.ts | 1 + src/pages/job-detail/index.tsx | 9 ++++++- 5 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/components/prejob-popup/index.tsx b/src/components/prejob-popup/index.tsx index 6744e99..6872ad5 100644 --- a/src/components/prejob-popup/index.tsx +++ b/src/components/prejob-popup/index.tsx @@ -1,14 +1,15 @@ import { Button, Image } from '@tarojs/components'; +import Taro from '@tarojs/taro'; -import { Popup, Dialog } from '@taroify/core'; +import { Dialog, Popup } from '@taroify/core'; import { Fragment, useCallback, useState } from 'react'; import JobBuy from '@/components/product-dialog/steps-ui/job-buy'; import SafeBottomPadding from '@/components/safe-bottom-padding'; import { PageUrl } from '@/constants/app'; +import { CacheKey } from '@/constants/cache-key'; import { GET_CONTACT_TYPE } from '@/constants/job'; -import { navigateTo } from '@/utils/route'; - +import { switchTab, navigateTo } from '@/utils/route'; import './index.less'; interface IProps { @@ -33,11 +34,19 @@ const GET_CONTACT_TYPE_OPTIONS = [ desc: '开通会员每天免费查看', btnText: '开通', }, + { + type: GET_CONTACT_TYPE.GROUP, + icon: 'https://publiccdn.neighbourhood.com.cn/img/group-avatar.png', + title: '进群领会员(免费报单)', + desc: '群内定期发放会员,免费报单', + btnText: '进群', + }, ]; export function PrejobPopup({ onCancel, onConfirm }: IProps) { const [openPopup, setOpenPopup] = useState(true); const [openDialog, setOpenDialog] = useState(false); + const [clicked, setClicked] = useState(!!Taro.getStorageSync(CacheKey.JOIN_GROUP_POPUP_CLICKED)); const handleClick = (type: GET_CONTACT_TYPE) => () => { if (type === GET_CONTACT_TYPE.MATERIAL) { navigateTo(PageUrl.MaterialUploadVideo); @@ -47,6 +56,12 @@ export function PrejobPopup({ onCancel, onConfirm }: IProps) { setOpenPopup(false); setOpenDialog(true); } + if (type === GET_CONTACT_TYPE.GROUP) { + Taro.setStorageSync(CacheKey.JOIN_GROUP_POPUP_CLICKED, true); + setClicked(true); + switchTab(PageUrl.GroupV2); + onConfirm(type); + } }; const handleAfterBuy = useCallback(async () => { onConfirm(GET_CONTACT_TYPE.VIP); @@ -58,6 +73,9 @@ export function PrejobPopup({ onCancel, onConfirm }: IProps) {
以下方式任选其一均可获取联系方式
{GET_CONTACT_TYPE_OPTIONS.map(option => { + if (clicked && option.type === GET_CONTACT_TYPE.GROUP) { + return; + } return (
diff --git a/src/components/product-dialog/contact/index.tsx b/src/components/product-dialog/contact/index.tsx index f2a7d36..13ee976 100644 --- a/src/components/product-dialog/contact/index.tsx +++ b/src/components/product-dialog/contact/index.tsx @@ -12,7 +12,7 @@ import { DeclarationType, ProductType } from '@/constants/product'; import { JobDetails } from '@/types/job'; import { GetProductIsUnlockResponse, ProductInfo } from '@/types/product'; import { logWithPrefix } from '@/utils/common'; -import { requestProductUseRecord, requestUseProduct } from '@/utils/product'; +import { requestUseProduct } from '@/utils/product'; import Toast from '@/utils/toast'; import '../index.less'; @@ -67,6 +67,7 @@ function ProductContactDialog(props: Omit) { return; } const productInfo = await requestUseProduct(PRODUCT_CODE, { jobId: data.id }); + console.log('开始报单'); Taro.eventCenter.trigger(EventName.READ_CONTACT); handleContact(productInfo.declarationTypeResult); } catch (e) { diff --git a/src/constants/cache-key.ts b/src/constants/cache-key.ts index f45331f..d1c0495 100644 --- a/src/constants/cache-key.ts +++ b/src/constants/cache-key.ts @@ -13,4 +13,5 @@ export enum CacheKey { AGREEMENT_SIGNED = '__agreement_signed__', CITY_CODES = '__city_codes__', JOIN_GROUP_CARD_CLICKED = '__join_group_card_clicked__', + JOIN_GROUP_POPUP_CLICKED = '__join_group_popup_clicked__', } diff --git a/src/constants/job.ts b/src/constants/job.ts index 5d2e6c5..efb7f01 100644 --- a/src/constants/job.ts +++ b/src/constants/job.ts @@ -213,4 +213,5 @@ export const FULL_PRICE_OPTIONS = FULL_EMPLOY_SALARY_OPTIONS.filter(o => !!o.val export enum GET_CONTACT_TYPE { VIP = 'vip', MATERIAL = 'material', + GROUP = 'group', } diff --git a/src/pages/job-detail/index.tsx b/src/pages/job-detail/index.tsx index d26fe33..3444c51 100644 --- a/src/pages/job-detail/index.tsx +++ b/src/pages/job-detail/index.tsx @@ -42,6 +42,7 @@ import { getJumpUrl, getPageQuery, navigateTo } from '@/utils/route'; import { getCommonShareMessage } from '@/utils/share'; import { formatDate } from '@/utils/time'; import Toast from '@/utils/toast'; +import { isNeedPhone } from '@/utils/user'; import './index.less'; const PREFIX = 'job-detail'; @@ -75,7 +76,8 @@ const AnchorFooter = (props: { data: JobDetails }) => { const [showBuyDialog, setShowBuyDialog] = useState(false); const [productInfo, setProductInfo] = useState(); const [productRecord, setProductRecord] = useState(); - + const userInfo = useUserInfo(); + const needPhone = isNeedPhone(userInfo); const getProductRecord = useCallback(async () => { const result = await requestProductUseRecord(ProductType.VIP, { jobId: data.id }); setProductRecord(result); @@ -163,8 +165,11 @@ const AnchorFooter = (props: { data: JobDetails }) => { const handleAfterBuy = useCallback(async () => { setShowBuyDialog(false); + Taro.showLoading({ mask: true, title: '加载中...' }); await getProductBalance(); + console.log('购买后重新获取次数'); setContactDialogVisible(true); + Taro.hideLoading(); }, [getProductBalance]); const handleCancel = useCallback(() => { @@ -217,6 +222,8 @@ const AnchorFooter = (props: { data: JobDetails }) => {
{data.isAuthed ? '急招岗位可免费查看' : productInfo?.content}
+ ) : needPhone ? ( +
登录后可免费报单
) : null}
From 587436058acdcac9d33d277bf72a3cd8651ad2c5 Mon Sep 17 00:00:00 2001 From: chashaobao Date: Sun, 7 Dec 2025 20:43:06 +0800 Subject: [PATCH 05/18] feat --- src/components/product-dialog/contact/index.tsx | 5 +++-- src/pages/job-detail/index.tsx | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/product-dialog/contact/index.tsx b/src/components/product-dialog/contact/index.tsx index 13ee976..edfc3fe 100644 --- a/src/components/product-dialog/contact/index.tsx +++ b/src/components/product-dialog/contact/index.tsx @@ -67,11 +67,12 @@ function ProductContactDialog(props: Omit) { return; } const productInfo = await requestUseProduct(PRODUCT_CODE, { jobId: data.id }); - console.log('开始报单'); + console.log('开始报单', productInfo); Taro.eventCenter.trigger(EventName.READ_CONTACT); - handleContact(productInfo.declarationTypeResult); + handleContact(productInfo ? productInfo.declarationTypeResult : undefined); } catch (e) { Toast.error('出错了,请重试'); + console.log(e); handleCloseDialog(); } finally { Taro.hideLoading(); diff --git a/src/pages/job-detail/index.tsx b/src/pages/job-detail/index.tsx index 3444c51..b2a641e 100644 --- a/src/pages/job-detail/index.tsx +++ b/src/pages/job-detail/index.tsx @@ -131,7 +131,7 @@ const AnchorFooter = (props: { data: JobDetails }) => { return; } - if (!productInfo.isPaidVip && !productInfo.freeBalance) { + if (!productRecord && !productInfo.isPaidVip && !productInfo.freeBalance) { setShowBuyDialog(true); return; } From d4fb6828522cd1209d4a180f44124a352787f413 Mon Sep 17 00:00:00 2001 From: chashaobao Date: Sun, 7 Dec 2025 21:28:15 +0800 Subject: [PATCH 06/18] feat --- src/components/prejob-popup/index.tsx | 5 +++-- src/components/product-dialog/contact/index.tsx | 4 +++- .../product-dialog/steps-ui/job-buy.tsx | 17 ++++++++++------- src/pages/job-detail/index.tsx | 8 ++++++-- src/utils/common.ts | 10 ++++------ 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/components/prejob-popup/index.tsx b/src/components/prejob-popup/index.tsx index 6872ad5..befe23e 100644 --- a/src/components/prejob-popup/index.tsx +++ b/src/components/prejob-popup/index.tsx @@ -14,6 +14,7 @@ import './index.less'; interface IProps { onCancel: () => void; + isCreateResume?: boolean; onConfirm: (type: GET_CONTACT_TYPE) => void; } @@ -43,7 +44,7 @@ const GET_CONTACT_TYPE_OPTIONS = [ }, ]; -export function PrejobPopup({ onCancel, onConfirm }: IProps) { +export function PrejobPopup({ onCancel, isCreateResume, onConfirm }: IProps) { const [openPopup, setOpenPopup] = useState(true); const [openDialog, setOpenDialog] = useState(false); const [clicked, setClicked] = useState(!!Taro.getStorageSync(CacheKey.JOIN_GROUP_POPUP_CLICKED)); @@ -99,7 +100,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 index edfc3fe..2519ff2 100644 --- a/src/components/product-dialog/contact/index.tsx +++ b/src/components/product-dialog/contact/index.tsx @@ -45,9 +45,11 @@ function ProductContactDialog(props: Omit) { initRef.current = async () => { const handleContact = (declarationTypeResult?: ProductInfo['declarationTypeResult']) => { if (declarationTypeResult?.type === DeclarationType.Direct && declarationTypeResult.publisherAcctNo) { + console.log('set JOB_CONTACT_DIRECT', declarationTypeResult.publisherAcctNo) setStatus(DialogStatus.JOB_CONTACT_DIRECT); setPublisherAcctNo(declarationTypeResult.publisherAcctNo); } else { + console.log('set JOB_CONTACT_CS') setStatus(DialogStatus.JOB_CONTACT_CS); } }; @@ -67,8 +69,8 @@ function ProductContactDialog(props: Omit) { return; } const productInfo = await requestUseProduct(PRODUCT_CODE, { jobId: data.id }); - console.log('开始报单', productInfo); Taro.eventCenter.trigger(EventName.READ_CONTACT); + console.log('开始报单', productInfo); handleContact(productInfo ? productInfo.declarationTypeResult : undefined); } catch (e) { Toast.error('出错了,请重试'); diff --git a/src/components/product-dialog/steps-ui/job-buy.tsx b/src/components/product-dialog/steps-ui/job-buy.tsx index bb0bcdb..47fe36d 100644 --- a/src/components/product-dialog/steps-ui/job-buy.tsx +++ b/src/components/product-dialog/steps-ui/job-buy.tsx @@ -26,6 +26,7 @@ import Toast from '@/utils/toast'; interface IProps { onConfirm: () => void; + isCreateResume?: boolean; } // interface Item { @@ -74,7 +75,7 @@ const subscribe = async () => { }; export default function JobBuy(props: IProps) { - const { onConfirm } = props; + const { onConfirm, isCreateResume } = props; const [productList, setProductList] = useState([]); const [selectItem, setSelectItem] = useState(); @@ -139,13 +140,15 @@ export default function JobBuy(props: IProps) {
免费查看次数已用完
请升级会员
-
-
完善模卡
-
赠10次查看
-
- 去完善 + {!isCreateResume && ( +
+
完善模卡
+
赠5次查看
+
+ 去完善 +
-
+ )}
{productList.map(item => { return ( diff --git a/src/pages/job-detail/index.tsx b/src/pages/job-detail/index.tsx index b2a641e..d019f94 100644 --- a/src/pages/job-detail/index.tsx +++ b/src/pages/job-detail/index.tsx @@ -237,12 +237,16 @@ const AnchorFooter = (props: { data: JobDetails }) => { /> )} {showMaterialGuide && ( - setShowMaterialGuide(false)} onConfirm={handleConfirmPrejob} /> + setShowMaterialGuide(false)} + onConfirm={handleConfirmPrejob} + /> )} {showBuyDialog && ( - + )} diff --git a/src/utils/common.ts b/src/utils/common.ts index f6e881c..cffa537 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -15,12 +15,10 @@ export const isDesktop = (() => { return info.platform === 'windows' || info.platform === 'mac'; })(); -export const logWithPrefix = isDev() - ? (prefix: string) => - (...args: BL.Anything[]) => - console.log(`[${prefix}]`, ...args) - : (_prefix: string) => - (..._args: BL.Anything[]) => {}; +export const logWithPrefix = + (prefix: string) => + (...args: BL.Anything[]) => + console.log(`[${prefix}]`, ...args); export const safeJsonParse = (str: string, defaultValue: BL.Anything = {}): T => { try { From 6c1e1cfd2d566de2320add163446d123f7685b4a Mon Sep 17 00:00:00 2001 From: chashaobao Date: Mon, 8 Dec 2025 22:08:53 +0800 Subject: [PATCH 07/18] featL --- .../product-dialog/contact/index.tsx | 106 ----------- .../product-dialog/job-contact/index.tsx | 175 ++++++++++++++++++ src/pages/job-detail/index.tsx | 66 +++---- 3 files changed, 197 insertions(+), 150 deletions(-) delete mode 100644 src/components/product-dialog/contact/index.tsx create mode 100644 src/components/product-dialog/job-contact/index.tsx diff --git a/src/components/product-dialog/contact/index.tsx b/src/components/product-dialog/contact/index.tsx deleted file mode 100644 index 2519ff2..0000000 --- a/src/components/product-dialog/contact/index.tsx +++ /dev/null @@ -1,106 +0,0 @@ -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 { EventName } from '@/constants/app'; -import { DeclarationType, ProductType } from '@/constants/product'; -import { JobDetails } from '@/types/job'; -import { GetProductIsUnlockResponse, ProductInfo } from '@/types/product'; -import { logWithPrefix } from '@/utils/common'; -import { requestUseProduct } from '@/utils/product'; -import Toast from '@/utils/toast'; - -import '../index.less'; - -interface IProps { - data: JobDetails; - productRecord?: GetProductIsUnlockResponse; - productInfo?: ProductInfo; - onClose: () => void; -} - -const PRODUCT_CODE = ProductType.VIP; -const log = logWithPrefix('product-contact-dialog'); - -function ProductContactDialog(props: Omit) { - const { data, productInfo: productInfoProps, productRecord, 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) { - console.log('set JOB_CONTACT_DIRECT', declarationTypeResult.publisherAcctNo) - setStatus(DialogStatus.JOB_CONTACT_DIRECT); - setPublisherAcctNo(declarationTypeResult.publisherAcctNo); - } else { - console.log('set JOB_CONTACT_CS') - setStatus(DialogStatus.JOB_CONTACT_CS); - } - }; - try { - Taro.showLoading(); - // if (1 < 2) { - // setStatus(DialogStatus.JOB_CONTACT_CS); - // return; - // } - log('requestProductUseRecord result', productRecord); - if (productRecord) { - handleContact(productRecord.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); - console.log('开始报单', productInfo); - handleContact(productInfo ? productInfo.declarationTypeResult : undefined); - } catch (e) { - Toast.error('出错了,请重试'); - console.log(e); - handleCloseDialog(); - } finally { - Taro.hideLoading(); - } - }; - }, [data, handleCloseDialog, productRecord, productInfoProps?.balance]); - - 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/job-contact/index.tsx b/src/components/product-dialog/job-contact/index.tsx new file mode 100644 index 0000000..b7b9bff --- /dev/null +++ b/src/components/product-dialog/job-contact/index.tsx @@ -0,0 +1,175 @@ +import Taro from '@tarojs/taro'; + +import { Dialog } from '@taroify/core'; +import { useCallback, useEffect, useState } from 'react'; + +import { DialogStatus, PREFIX } from '@/components/product-dialog/const'; +import JobBuy from '@/components/product-dialog/steps-ui/job-buy'; +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 { GetProductIsUnlockResponse, ProductInfo } from '@/types/product'; +import { logWithPrefix } from '@/utils/common'; +import { requestUseProduct } from '@/utils/product'; +import Toast from '@/utils/toast'; + +import '../index.less'; + +interface IProps { + data: JobDetails; + /** Product use record from parent - if exists, user has already unlocked this job */ + productRecord?: GetProductIsUnlockResponse | null; + /** Product balance info from parent */ + productInfo?: ProductInfo; + /** Callback to refresh product balance in parent after purchase */ + onRefreshBalance?: () => Promise; + onClose: () => void; +} + +const PRODUCT_CODE = ProductType.VIP; +const log = logWithPrefix('product-job-contact-dialog'); + +/** + * Integrated dialog component for job contact flow + * Handles: balance check -> buy if needed -> use product -> show contact info + * + * @param productRecord - Pass from parent to avoid duplicate API calls + * @param productInfo - Pass from parent to avoid duplicate API calls + * @param onRefreshBalance - Callback to refresh balance in parent after purchase + */ +function ProductJobContactDialog(props: IProps) { + const { data, productRecord, productInfo, onRefreshBalance, onClose } = props; + const [status, setStatus] = useState(DialogStatus.LOADING); + const [publisherAcctNo, setPublisherAcctNo] = useState(''); + + /** + * Handle contact display based on declaration type + */ + const showContactResult = useCallback((declarationTypeResult?: ProductInfo['declarationTypeResult']) => { + if (declarationTypeResult?.type === DeclarationType.Direct && declarationTypeResult.publisherAcctNo) { + log('show JOB_CONTACT_DIRECT', declarationTypeResult.publisherAcctNo); + setPublisherAcctNo(declarationTypeResult.publisherAcctNo); + setStatus(DialogStatus.JOB_CONTACT_DIRECT); + } else { + log('show JOB_CONTACT_CS'); + setStatus(DialogStatus.JOB_CONTACT_CS); + } + }, []); + + /** + * Use product and show contact info + */ + const consumeProductAndShowContact = useCallback(async () => { + const productResult = await requestUseProduct(PRODUCT_CODE, { jobId: data.id }); + log('consumeProductAndShowContact result', productResult); + // Refresh balance in parent after consuming product + await onRefreshBalance?.(); + showContactResult(productResult?.declarationTypeResult); + }, [data.id, showContactResult, onRefreshBalance]); + + /** + * Callback after successful purchase + * Refresh balance via parent callback and use product to show contact info + */ + const handleAfterBuy = useCallback(async () => { + log('handleAfterBuy - start'); + try { + Taro.showLoading({ mask: true, title: '加载中...' }); + + // Refresh balance via parent callback + await onRefreshBalance?.(); + + // Use product and show contact info after purchase + await consumeProductAndShowContact(); + } catch (e) { + log('handleAfterBuy error', e); + Toast.error('出错了,请重试'); + onClose(); + } finally { + Taro.hideLoading(); + } + }, [consumeProductAndShowContact, onRefreshBalance, onClose]); + + const handleReport = useCallback(() => { + log('report', data.id); + }, [data.id]); + + /** + * Initialize dialog on mount + */ + useEffect(() => { + let isMounted = true; + + const init = async () => { + try { + Taro.showLoading({ mask: true, title: '加载中...' }); + + log('init with productRecord', productRecord); + log('init with productInfo', productInfo); + + // Step 1: Already unlocked - show contact directly + if (productRecord) { + log('show JOB_CONTACT_DIRECT from productRecord', productRecord.declarationTypeResult); + showContactResult(productRecord.declarationTypeResult); + return; + } + + // Step 2: No productInfo - error state + if (!productInfo) { + log('no productInfo provided, closing'); + Toast.error('出错了,请重试'); + onClose(); + return; + } + + // Step 3: Determine status based on balance + if (!productInfo.isPaidVip && !productInfo.freeBalance) { + log('show JOB_BUY'); + if (isMounted) setStatus(DialogStatus.JOB_BUY); + } else if (!productInfo.balance) { + log('show JOB_UNABLE_UNLOCK'); + if (isMounted) setStatus(DialogStatus.JOB_UNABLE_UNLOCK); + } else { + await consumeProductAndShowContact(); + } + } catch (e) { + log('init error', e); + Toast.error('出错了,请重试'); + onClose(); + } finally { + Taro.hideLoading(); + } + }; + + init(); + + return () => { + isMounted = false; + }; + // Only run on mount - props are captured at dialog open time + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + if (status === DialogStatus.LOADING) { + return null; + } + + return ( + + + {status === DialogStatus.JOB_CONTACT_CS && } + {status === DialogStatus.JOB_CONTACT_DIRECT && ( + + )} + {status === DialogStatus.JOB_BUY && ( + + )} + {status === DialogStatus.JOB_UNABLE_UNLOCK && } + + + ); +} + +export default ProductJobContactDialog; diff --git a/src/pages/job-detail/index.tsx b/src/pages/job-detail/index.tsx index d019f94..75cda83 100644 --- a/src/pages/job-detail/index.tsx +++ b/src/pages/job-detail/index.tsx @@ -12,9 +12,8 @@ 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 ProductContactDialog from '@/components/product-dialog/contact'; +import ProductJobContactDialog from '@/components/product-dialog/job-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'; @@ -71,13 +70,13 @@ const getMapCallout = (data: JobDetails): MapProps.callout | undefined => { const AnchorFooter = (props: { data: JobDetails }) => { const { data } = props; const [errorTips, setErrorTips] = useState(''); - const [contactDialogVisible, setContactDialogVisible] = useState(false); + const [showJobContactDialog, setShowJobContactDialog] = useState(false); const [showMaterialGuide, setShowMaterialGuide] = useState(false); - const [showBuyDialog, setShowBuyDialog] = useState(false); const [productInfo, setProductInfo] = useState(); const [productRecord, setProductRecord] = useState(); const userInfo = useUserInfo(); const needPhone = isNeedPhone(userInfo); + const getProductRecord = useCallback(async () => { const result = await requestProductUseRecord(ProductType.VIP, { jobId: data.id }); setProductRecord(result); @@ -86,6 +85,7 @@ const AnchorFooter = (props: { data: JobDetails }) => { const getProductBalance = useCallback(async () => { const [, resp] = await requestProductBalance(ProductType.VIP); setProductInfo(resp); + return resp; }, []); const handleClickContact = useCallback(async () => { @@ -126,17 +126,13 @@ const AnchorFooter = (props: { data: JobDetails }) => { jobId: data.id, }); } else { + // Show material guide if no resume and no VIP and no free balance if (!productInfo?.isCreateResume && !productInfo?.isPaidVip && !productInfo?.freeBalance) { setShowMaterialGuide(true); return; } - - if (!productRecord && !productInfo.isPaidVip && !productInfo.freeBalance) { - setShowBuyDialog(true); - return; - } - - setContactDialogVisible(true); + // Open integrated dialog - it handles buy + contact internally + setShowJobContactDialog(true); } } catch (error) { const e = error as HttpError; @@ -149,33 +145,24 @@ const AnchorFooter = (props: { data: JobDetails }) => { } }, [data, productInfo?.freeBalance, productInfo?.isCreateResume, productInfo?.isPaidVip]); - const handleDialogHidden = useCallback(() => { - setContactDialogVisible(false); - }, []); + const handleDialogClose = useCallback(() => { + setShowJobContactDialog(false); + // Refresh data after dialog closes + getProductRecord(); + }, [getProductRecord]); + const handleConfirmPrejob = useCallback( (type: GET_CONTACT_TYPE) => { setShowMaterialGuide(false); if (GET_CONTACT_TYPE.VIP === type) { - getProductBalance(); - setContactDialogVisible(true); + getProductBalance().then(() => { + setShowJobContactDialog(true); + }); } }, [getProductBalance] ); - const handleAfterBuy = useCallback(async () => { - setShowBuyDialog(false); - Taro.showLoading({ mask: true, title: '加载中...' }); - await getProductBalance(); - console.log('购买后重新获取次数'); - setContactDialogVisible(true); - Taro.hideLoading(); - }, [getProductBalance]); - - const handleCancel = useCallback(() => { - setShowBuyDialog(false); - }, []); - // const unAuthedButtonText = useMemo(() => { // if (haveSeen) { // return '查看联系方式'; @@ -194,11 +181,8 @@ const AnchorFooter = (props: { data: JobDetails }) => { useEffect(() => { Taro.eventCenter.on(EventName.CREATE_PROFILE, getProductBalance); - Taro.eventCenter.on(EventName.READ_CONTACT, getProductBalance); - return () => { Taro.eventCenter.off(EventName.CREATE_PROFILE); - Taro.eventCenter.off(EventName.READ_CONTACT); }; }, [getProductBalance]); @@ -228,12 +212,13 @@ const AnchorFooter = (props: { data: JobDetails }) => {
- {contactDialogVisible && ( - )} {showMaterialGuide && ( @@ -243,13 +228,6 @@ const AnchorFooter = (props: { data: JobDetails }) => { onConfirm={handleConfirmPrejob} /> )} - {showBuyDialog && ( - - - - - - )} Date: Tue, 9 Dec 2025 07:55:14 +0800 Subject: [PATCH 08/18] feat --- src/components/login-button/index.tsx | 10 +++++++--- src/pages/job-detail/index.tsx | 8 ++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/login-button/index.tsx b/src/components/login-button/index.tsx index 9ad38de..05c7d5f 100644 --- a/src/components/login-button/index.tsx +++ b/src/components/login-button/index.tsx @@ -6,7 +6,7 @@ import { useCallback, useState } from 'react'; import { AgreementPopup } from '@/components/agreement-popup'; import LoginDialog from '@/components/login-dialog'; import useUserInfo from '@/hooks/use-user-info'; -import { getAgreementSigned, isNeedLogin, setAgreementSigned } from '@/utils/user'; +import { getAgreementSigned, isNeedLogin, requestUserInfo, setAgreementSigned } from '@/utils/user'; import './index.less'; @@ -18,13 +18,14 @@ export enum BindPhoneStatus { export interface ILoginButtonProps extends ButtonProps { needPhone?: boolean; + needRefresh?: boolean; needAssignment?: boolean; } const PREFIX = 'login-button'; function LoginButton(props: ILoginButtonProps) { - const { className, children, needPhone, onClick, ...otherProps } = props; + const { className, children, needPhone, onClick, needRefresh, ...otherProps } = props; const userInfo = useUserInfo(); const [loginVisible, setLoginVisible] = useState(false); const [assignVisible, setAssignVisible] = useState(false); @@ -70,9 +71,12 @@ function LoginButton(props: ILoginButtonProps) { const handleLoginSuccess = useCallback( e => { setLoginVisible(false); + if (needRefresh) { + requestUserInfo(); + } onClick?.(e); }, - [onClick] + [needRefresh, onClick] ); return ( diff --git a/src/pages/job-detail/index.tsx b/src/pages/job-detail/index.tsx index 75cda83..10d2009 100644 --- a/src/pages/job-detail/index.tsx +++ b/src/pages/job-detail/index.tsx @@ -200,14 +200,14 @@ const AnchorFooter = (props: { data: JobDetails }) => { - + {data.isAuthed ? '在线沟通' : '查看联系方式'} - {!productRecord && (data.isAuthed || productInfo?.content) ? ( + {needPhone ? ( +
登录后可免费报单
+ ) : !productRecord && (data.isAuthed || productInfo?.content) ? (
{data.isAuthed ? '急招岗位可免费查看' : productInfo?.content}
- ) : needPhone ? ( -
登录后可免费报单
) : null}
From c696e93bc5e324392f894c4f3126ef845859d19e Mon Sep 17 00:00:00 2001 From: chashaobao Date: Wed, 17 Dec 2025 00:10:53 +0800 Subject: [PATCH 09/18] feat: --- src/components/login-button/index.tsx | 6 ++++-- src/pages/job-detail/index.tsx | 20 ++++++++++++++++++-- src/utils/group.ts | 5 ++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/components/login-button/index.tsx b/src/components/login-button/index.tsx index 05c7d5f..b5c1697 100644 --- a/src/components/login-button/index.tsx +++ b/src/components/login-button/index.tsx @@ -19,13 +19,14 @@ export enum BindPhoneStatus { export interface ILoginButtonProps extends ButtonProps { needPhone?: boolean; needRefresh?: boolean; + onRefresh?: (() => void) | (() => Promise); needAssignment?: boolean; } const PREFIX = 'login-button'; function LoginButton(props: ILoginButtonProps) { - const { className, children, needPhone, onClick, needRefresh, ...otherProps } = props; + const { className, children, needPhone, onRefresh, onClick, needRefresh, ...otherProps } = props; const userInfo = useUserInfo(); const [loginVisible, setLoginVisible] = useState(false); const [assignVisible, setAssignVisible] = useState(false); @@ -69,10 +70,11 @@ function LoginButton(props: ILoginButtonProps) { }, []); const handleLoginSuccess = useCallback( - e => { + async e => { setLoginVisible(false); if (needRefresh) { requestUserInfo(); + onRefresh && (await onRefresh()); } onClick?.(e); }, diff --git a/src/pages/job-detail/index.tsx b/src/pages/job-detail/index.tsx index 10d2009..d0a3186 100644 --- a/src/pages/job-detail/index.tsx +++ b/src/pages/job-detail/index.tsx @@ -82,9 +82,16 @@ const AnchorFooter = (props: { data: JobDetails }) => { setProductRecord(result); }, [data.id]); - const getProductBalance = useCallback(async () => { + const getProductBalance = useCallback(async (loading?: boolean) => { + if (loading) { + Taro.showLoading(); + } const [, resp] = await requestProductBalance(ProductType.VIP); setProductInfo(resp); + console.log(resp); + if (loading) { + Taro.hideLoading(); + } return resp; }, []); @@ -179,6 +186,10 @@ const AnchorFooter = (props: { data: JobDetails }) => { // return productInfo?.isCreateResume? '升级会员即可查看': '创建模卡,免费查看'; // }, [productInfo, haveSeen]); + const handleRefresh = useCallback(async () => { + await getProductBalance(true); + }, [getProductBalance]); + useEffect(() => { Taro.eventCenter.on(EventName.CREATE_PROFILE, getProductBalance); return () => { @@ -200,7 +211,12 @@ const AnchorFooter = (props: { data: JobDetails }) => { - + {data.isAuthed ? '在线沟通' : '查看联系方式'} {needPhone ? (
登录后可免费报单
diff --git a/src/utils/group.ts b/src/utils/group.ts index b9a7db7..c3b5371 100644 --- a/src/utils/group.ts +++ b/src/utils/group.ts @@ -51,5 +51,8 @@ export const requestBatchPublishGroups = async () => { }; export const requestSimpleGroupList = async (cityCode?: string) => { - return http.post(API.SIMPLE_GROUP_LIST, { data: { cityCode } }); + return http.post(API.SIMPLE_GROUP_LIST, { + data: { cityCode }, + contentType: 'application/x-www-form-urlencoded', + }); }; From ec96058d5d0758269d9bda84be36973320186e15 Mon Sep 17 00:00:00 2001 From: chashaobao Date: Mon, 22 Dec 2025 00:00:23 +0800 Subject: [PATCH 10/18] feat: --- src/components/prejob-popup/index.tsx | 14 ++-- .../product-dialog/steps-ui/job-buy.tsx | 38 ++++++---- src/pages/user-batch-publish/index.tsx | 72 +++++++++---------- 3 files changed, 69 insertions(+), 55 deletions(-) diff --git a/src/components/prejob-popup/index.tsx b/src/components/prejob-popup/index.tsx index befe23e..7de4e6a 100644 --- a/src/components/prejob-popup/index.tsx +++ b/src/components/prejob-popup/index.tsx @@ -35,13 +35,13 @@ const GET_CONTACT_TYPE_OPTIONS = [ desc: '开通会员每天免费查看', btnText: '开通', }, - { - type: GET_CONTACT_TYPE.GROUP, - icon: 'https://publiccdn.neighbourhood.com.cn/img/group-avatar.png', - title: '进群领会员(免费报单)', - desc: '群内定期发放会员,免费报单', - btnText: '进群', - }, + // { + // type: GET_CONTACT_TYPE.GROUP, + // icon: 'https://publiccdn.neighbourhood.com.cn/img/group-avatar.png', + // title: '进群领会员(免费报单)', + // desc: '群内定期发放会员,免费报单', + // btnText: '进群', + // }, ]; export function PrejobPopup({ onCancel, isCreateResume, onConfirm }: IProps) { diff --git a/src/components/product-dialog/steps-ui/job-buy.tsx b/src/components/product-dialog/steps-ui/job-buy.tsx index 47fe36d..498a48a 100644 --- a/src/components/product-dialog/steps-ui/job-buy.tsx +++ b/src/components/product-dialog/steps-ui/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'; @@ -136,18 +136,32 @@ export default function JobBuy(props: IProps) { return (
-
-
免费查看次数已用完
-
请升级会员
-
- {!isCreateResume && ( -
-
完善模卡
-
赠5次查看
-
- 去完善 + {isCreateResume ? ( + +
+
今日免费查看次数
+
已用完
-
+
+
+
明日
+
再来
+
+
升级会员
+
+ + ) : ( + +
+
开通会员即可查看联系方式
+
+
+
完善模卡,每日可免费查看
+
+ 去完善 +
+
+
)}
{productList.map(item => { diff --git a/src/pages/user-batch-publish/index.tsx b/src/pages/user-batch-publish/index.tsx index 202fb12..0f17540 100644 --- a/src/pages/user-batch-publish/index.tsx +++ b/src/pages/user-batch-publish/index.tsx @@ -24,7 +24,7 @@ const COMMENT_IMAGE = 'https://publiccdn.neighbourhood.com.cn/img/delegate-comme export default function BizService() { const inviteCode = useInviteCode(); const cityOperators = useCityOperators(); - const [value, setValue] = useState('2'); + const [value, setValue] = useState('0'); const handleClickDelegate = useCallback(() => { navigateTo(PageUrl.GroupDelegatePublish); @@ -80,7 +80,40 @@ export default function BizService() {
- + +
+
+
服务城市
+
江、沪、皖-上海、南京、合肥
+
粤、闽-广州、深圳、佛山、厦门、福州、泉州
+
京、鲁-北京、青岛
+
鄂、豫、湘、陕-长沙、武汉、郑州、西安
+
川、渝、云-成都、重庆、昆明
+
服务方式及收费标准
+
服务方式:提供录屏和基本资料供挑选,挑中安排面试
+
收费标准:安排一场面试200元
+
收费方式:预付费,按安排面试场数扣费
+
服务能力
+
+ 我们在每个城市均有数量众多的主播群,少则几十个,多则上千个,有各种类型和层次的带货主播资源 +
+
+ +
+
+
+
+ + 群代发 + + + } + >
- - 主播群 - - - } - > + - -
-
-
服务城市
-
江、沪、皖-上海、南京、合肥
-
粤、闽-广州、深圳、佛山、厦门、福州、泉州
-
京、鲁-北京、青岛
-
鄂、豫、湘、陕-长沙、武汉、郑州、西安
-
川、渝、云-成都、重庆、昆明
-
服务方式及收费标准
-
服务方式:提供录屏和基本资料供挑选,挑中安排面试
-
收费标准:安排一场面试200元
-
收费方式:预付费,按安排面试场数扣费
-
服务能力
-
- 我们在每个城市均有数量众多的主播群,少则几十个,多则上千个,有各种类型和层次的带货主播资源 -
-
- -
-
-
-
From e38114d32b107cf50debaa1e0c31e95b105999bd Mon Sep 17 00:00:00 2001 From: chashaobao Date: Wed, 24 Dec 2025 00:41:36 +0800 Subject: [PATCH 11/18] =?UTF-8?q?feat:=20=E9=82=80=E8=AF=B7=E8=BF=90?= =?UTF-8?q?=E8=90=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/partner-intro/index.tsx | 38 ++--- src/components/prejob-popup/index.tsx | 13 +- src/constants/app.ts | 2 + src/constants/job.ts | 1 + src/hooks/use-config.tsx | 1 + src/pages/invite-operations/index.config.ts | 3 + src/pages/invite-operations/index.less | 156 ++++++++++++++++++++ src/pages/invite-operations/index.tsx | 117 +++++++++++++++ 8 files changed, 311 insertions(+), 20 deletions(-) create mode 100644 src/pages/invite-operations/index.config.ts create mode 100644 src/pages/invite-operations/index.less create mode 100644 src/pages/invite-operations/index.tsx diff --git a/src/components/partner-intro/index.tsx b/src/components/partner-intro/index.tsx index 7aa4a55..3af3ceb 100644 --- a/src/components/partner-intro/index.tsx +++ b/src/components/partner-intro/index.tsx @@ -146,25 +146,6 @@ export default function PartnerIntro() {
-
-
- 群主认证 -
- - 强烈推荐 -
-
-
-
-
- 完成群主认证后,群成员通过该群访问任何人分享的播络小程序进行注册,你都能获得分成 -
-
- -
-
3重收益
@@ -194,6 +175,25 @@ export default function PartnerIntro() {
+
+
+ 群主认证 +
+ + 强烈推荐 +
+
+
+
+
+ 完成群主认证后,群成员通过该群访问任何人分享的播络小程序进行注册,你都能获得分成 +
+
+ +
+
交流群
diff --git a/src/components/prejob-popup/index.tsx b/src/components/prejob-popup/index.tsx index 7de4e6a..212588b 100644 --- a/src/components/prejob-popup/index.tsx +++ b/src/components/prejob-popup/index.tsx @@ -9,7 +9,7 @@ import SafeBottomPadding from '@/components/safe-bottom-padding'; import { PageUrl } from '@/constants/app'; import { CacheKey } from '@/constants/cache-key'; import { GET_CONTACT_TYPE } from '@/constants/job'; -import { switchTab, navigateTo } from '@/utils/route'; +import { navigateTo, switchTab } from '@/utils/route'; import './index.less'; interface IProps { @@ -42,6 +42,13 @@ const GET_CONTACT_TYPE_OPTIONS = [ // desc: '群内定期发放会员,免费报单', // btnText: '进群', // }, + { + type: GET_CONTACT_TYPE.INVITE, + icon: '', + title: '邀请运营进群(进会员)', + desc: '每邀请进一个主播群送一个日会员', + btnText: '邀请', + }, ]; export function PrejobPopup({ onCancel, isCreateResume, onConfirm }: IProps) { @@ -53,6 +60,10 @@ export function PrejobPopup({ onCancel, isCreateResume, onConfirm }: IProps) { navigateTo(PageUrl.MaterialUploadVideo); onConfirm(type); } + if (type === GET_CONTACT_TYPE.INVITE) { + navigateTo(PageUrl.InviteOperations); + onConfirm(type); + } if (type === GET_CONTACT_TYPE.VIP) { setOpenPopup(false); setOpenDialog(true); diff --git a/src/constants/app.ts b/src/constants/app.ts index 80e295b..d9601e4 100644 --- a/src/constants/app.ts +++ b/src/constants/app.ts @@ -37,6 +37,7 @@ export enum OpenSource { AnchorPage = 'anchor_page', MaterialViewPage = 'material_view_page', GroupOwnerCertificate = 'group_owner_certificate', + InviteOperations = 'invite_operations', } export enum PageUrl { @@ -82,6 +83,7 @@ export enum PageUrl { GiveVip = 'pages/give-vip/index', GroupOwnerCertificate = 'pages/group-owner-certification/index', PartnerShareVip = 'pages/partner-share-vip/index', + InviteOperations = 'pages/invite-operations/index', } export enum PluginUrl { diff --git a/src/constants/job.ts b/src/constants/job.ts index efb7f01..ef07746 100644 --- a/src/constants/job.ts +++ b/src/constants/job.ts @@ -214,4 +214,5 @@ export enum GET_CONTACT_TYPE { VIP = 'vip', MATERIAL = 'material', GROUP = 'group', + INVITE = 'invite', } diff --git a/src/hooks/use-config.tsx b/src/hooks/use-config.tsx index a190aaf..129c13f 100644 --- a/src/hooks/use-config.tsx +++ b/src/hooks/use-config.tsx @@ -101,6 +101,7 @@ export const APP_CONFIG: AppConfigType = { PageUrl.GiveVip, PageUrl.GroupOwnerCertificate, PageUrl.PartnerShareVip, + PageUrl.InviteOperations, PageUrl.AccelerateDelegatePublish, // PageUrl.DevDebug, ], diff --git a/src/pages/invite-operations/index.config.ts b/src/pages/invite-operations/index.config.ts new file mode 100644 index 0000000..660d09e --- /dev/null +++ b/src/pages/invite-operations/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: '邀请运营', +}); diff --git a/src/pages/invite-operations/index.less b/src/pages/invite-operations/index.less new file mode 100644 index 0000000..829adc9 --- /dev/null +++ b/src/pages/invite-operations/index.less @@ -0,0 +1,156 @@ +@import '@/styles/variables.less'; +@import '@/styles/common.less'; + +.invite-operations { + &__main { + padding-left: 24px; + padding-right: 24px; + position: relative; + } + + &__block { + margin-bottom: 12px; + position: relative; + } + + &__card { + background: #fff; + border-radius: 24px; + padding: 32px; + position: relative; + } + + &__edging { + position: absolute; + font-weight: 400; + font-size: 26px; + line-height: 28px; + color: #946724; + padding: 18px 24px; + background: #FFF4F0; + border-radius:0 0 24px 24px; + bottom: 0; + left: 0; + right: 0; + } + + &__lined-wrapper { + text-align: center; + margin-top: 40px; + } + &__lined-title { + text-align: center; + font-weight: 400; + font-size: 28px; + line-height: 40px; + color: #333333; + position: relative; + display: inline-block; + + &:before { + content: ''; + position: absolute; + left: -68px; + width: 56px; + height: 1px; + background: #ccc; + top: 50%; + } + + &:after { + content: ''; + position: absolute; + right: -68px; + width: 56px; + height: 1px; + background: #ccc; + top: 50%; + } + } + + + &__h1 { + font-weight: 500; + font-size: 32px; + line-height: 40px; + color: #1d2129; + margin-bottom: 16px; + margin-top: 32px; + position: relative; + z-index: 0; + + &:first-child { + margin-top: 0; + } + + &.center { + text-align: center; + } + + .highlight { + color: @blHighlightColor; + display: inline-block; + } + } + + &__body { + font-weight: 400; + font-size: 28px; + line-height: 40px; + color: @blColor; + } + + &__title { + margin-bottom: 24px; + font-weight: 500; + font-size: 32px; + line-height: 32px; + color: #1d2129; + + &:before { + left: -138px; + width: 120px; + } + + &:after { + right: -138px; + width: 120px; + } + } + + &__copy { + .button(@height: 72px; @width: 384px; @fontSize: 28px; @fontWeight: 400; @borderRadius: 44px; @highlight: 0); + margin-top: 32px; + } + + &__city-select { + background: #F7F7F7; + border-radius: 16px; + padding: 34px 32px; + font-weight: 400; + font-size: 32px; + margin-top: 24px; + line-height: 32px; + color: #333333; + display: flex; + justify-content: space-between; + } + &__qrcode { + width: 280px; + height: 280px; + background: #6F7686; + display: block; + margin: auto auto 24px; + } + + &__action-block { + background: #F7F7F7; + border-radius: 16px; + padding: 32px 0; + .flex-column(); + text-align: center; + color: @blHighlightColor; + font-size: 28px; + font-weight: 400; + } +} diff --git a/src/pages/invite-operations/index.tsx b/src/pages/invite-operations/index.tsx new file mode 100644 index 0000000..cfa09a3 --- /dev/null +++ b/src/pages/invite-operations/index.tsx @@ -0,0 +1,117 @@ +import { Button, Image } from '@tarojs/components'; +import Taro from '@tarojs/taro'; + +import { Arrow } from '@taroify/icons'; +import { Fragment, useCallback, useEffect, useState } from 'react'; + +import { EventName, OpenSource, PageUrl } from '@/constants/app'; +import { CITY_CODE_TO_NAME_MAP } from '@/constants/city'; +import useLocation from '@/hooks/use-location'; +import useUserInfo from '@/hooks/use-user-info'; +import { StaffInfo } from '@/types/partner'; +import { copy } from '@/utils/common'; +import { getStaffInfo } from '@/utils/partner'; +import { navigateTo } from '@/utils/route'; +import './index.less'; + +const PREFIX = 'invite-operations'; + +export default function InviteOperations() { + const location = useLocation(); + const userInfo = useUserInfo(); + const [cityCode, setCityCode] = useState(location.cityCode); + const [staffInfo, setStaffInfo] = useState(null); + + const handleClickCityMenu = useCallback(() => { + navigateTo(PageUrl.CitySearch, { city: cityCode || location.cityCode, source: OpenSource.InviteOperations }); + }, [cityCode, location.cityCode]); + + const handleCityChange = useCallback(data => { + console.log('handleCityChange', data); + const { openSource, cityCode: cCode } = data; + if (openSource !== OpenSource.InviteOperations) { + return; + } + setCityCode(cCode); + }, []); + + const handleCopy = useCallback(() => { + copy(`我的ID是:${userInfo.userId},邀你进主播群`); + }, [userInfo.userId]); + + useEffect(() => { + Taro.eventCenter.on(EventName.SELECT_CITY, handleCityChange); + return () => { + Taro.eventCenter.off(EventName.SELECT_CITY, handleCityChange); + }; + }, [handleCityChange]); + + useEffect(() => { + if (!cityCode) return; + + getStaffInfo(cityCode) + .then(data => { + setStaffInfo(data); + }) + .catch(() => { + setStaffInfo(null); + }); + }, [cityCode]); + + return ( +
+
+
+
+
活动说明
+
+
+
邀请播络运营进带货主播群
+
+ 每邀请进一个群送一个日会员 +
+
注:只能邀请带货主播群,请勿邀请其他群
+
+
+
+
+
邀请方法
+
+
+
+
+ 加运营为好友并将以下信息
粘贴发送给运营
+
+
+
我的ID是:{userInfo.userId},邀你进主播群
+ +
+
+
选择城市,添加运营
+
+
+ {cityCode ? CITY_CODE_TO_NAME_MAP.get(cityCode) : '请选择城市'} + +
+ {staffInfo && ( + +
+
长按并识别二维码添加运营
+
+ +
+ )} +
+
+
+
+
+ ); +} From 3f782b6be3f054cc1163a96604a0941f968281da Mon Sep 17 00:00:00 2001 From: chashaobao Date: Wed, 24 Dec 2025 00:45:43 +0800 Subject: [PATCH 12/18] =?UTF-8?q?feat:=20user=20page=20=E9=82=80=E8=AF=B7?= =?UTF-8?q?=E8=BF=90=E8=90=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/fragments/user/anchor/index.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/fragments/user/anchor/index.tsx b/src/fragments/user/anchor/index.tsx index 9ee7cf1..0b9e112 100644 --- a/src/fragments/user/anchor/index.tsx +++ b/src/fragments/user/anchor/index.tsx @@ -18,6 +18,8 @@ export default function AnchorFragment() { const handleClickSwitch = useCallback(() => switchRoleType(RoleType.Company), []); + const handleClickInviteOperations = useCallback(() => navigateTo(PageUrl.InviteOperations), []); + return (
@@ -29,6 +31,13 @@ export default function AnchorFragment() { className={`${PREFIX}__cell`} onClick={handleClickMyDeclaration} /> + Date: Wed, 24 Dec 2025 23:30:30 +0800 Subject: [PATCH 13/18] feat: --- .../invite-operations-banner/index.less | 22 ++++++++++ .../invite-operations-banner/index.tsx | 40 +++++++++++++++++++ src/components/prejob-popup/index.tsx | 2 +- src/fragments/job/base/index.tsx | 6 ++- 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 src/components/invite-operations-banner/index.less create mode 100644 src/components/invite-operations-banner/index.tsx diff --git a/src/components/invite-operations-banner/index.less b/src/components/invite-operations-banner/index.less new file mode 100644 index 0000000..b98e1b1 --- /dev/null +++ b/src/components/invite-operations-banner/index.less @@ -0,0 +1,22 @@ +@import '@/styles/variables.less'; +@import '@/styles/common.less'; + +.invite-operations-fragment-banner { + width: 100%; + height: 90px; + position: relative; + margin-bottom: 24px; + + &__image { + width: 100%; + height: 100%; + } + + &__close { + width: 64px; + height: 38px; + position: absolute; + right: 0; + top: 0; + } +} diff --git a/src/components/invite-operations-banner/index.tsx b/src/components/invite-operations-banner/index.tsx new file mode 100644 index 0000000..cbcee24 --- /dev/null +++ b/src/components/invite-operations-banner/index.tsx @@ -0,0 +1,40 @@ +import { Image } from '@tarojs/components'; + +import { useCallback, useState } from 'react'; + +import { PageUrl } from '@/constants/app'; +import { getPartnerBannerClose, setPartnerBannerClose } from '@/utils/partner'; +import { navigateTo } from '@/utils/route'; +import './index.less'; + +const PREFIX = 'invite-operations-fragment-banner'; + +export default function InviteOperationsBanner() { + const [bannerClose, setBannerClose] = useState(getPartnerBannerClose()); + + const handlePartnerBannerClose = useCallback(e => { + e.preventDefault(); + e.stopPropagation(); + setBannerClose(true); + setPartnerBannerClose(); + }, []); + + const handleClick = useCallback(async () => { + navigateTo(PageUrl.InviteOperations); + }, []); + + if (bannerClose) { + return null; + } + + return ( +
+ +
+
+ ); +} diff --git a/src/components/prejob-popup/index.tsx b/src/components/prejob-popup/index.tsx index 212588b..3e75fe5 100644 --- a/src/components/prejob-popup/index.tsx +++ b/src/components/prejob-popup/index.tsx @@ -44,7 +44,7 @@ const GET_CONTACT_TYPE_OPTIONS = [ // }, { type: GET_CONTACT_TYPE.INVITE, - icon: '', + icon: 'https://publiccdn.neighbourhood.com.cn/img/invite-operations.png', title: '邀请运营进群(进会员)', desc: '每邀请进一个主播群送一个日会员', btnText: '邀请', diff --git a/src/fragments/job/base/index.tsx b/src/fragments/job/base/index.tsx index 294c4fc..9fdaee4 100644 --- a/src/fragments/job/base/index.tsx +++ b/src/fragments/job/base/index.tsx @@ -9,7 +9,7 @@ import { useCallback, useEffect, useState } from 'react'; import EmployTypeSelect from '@/components/employ-type-select'; import JobList, { IJobListProps } from '@/components/job-list'; import Overlay from '@/components/overlay'; -import PartnerBanner from '@/components/partner-banner'; +// import PartnerBanner from '@/components/partner-banner'; import SalarySelect from '@/components/salary-select'; import SearchInput from '@/components/search'; import { APP_TAB_BAR_ID, PageUrl } from '@/constants/app'; @@ -30,6 +30,7 @@ import { Coordinate } from '@/types/location'; import { logWithPrefix } from '@/utils/common'; import { navigateTo } from '@/utils/route'; import './index.less'; +import InviteOperationsBanner from '@/components/invite-operations-banner'; interface IProps { cityCode: string; @@ -173,7 +174,8 @@ function JobFragment(props: IProps) { {JOB_TABS.map(tab => ( - + {/**/} + Date: Sat, 27 Dec 2025 16:00:25 +0800 Subject: [PATCH 14/18] feat: --- src/pages/job-detail/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/job-detail/index.tsx b/src/pages/job-detail/index.tsx index d0a3186..9f2888c 100644 --- a/src/pages/job-detail/index.tsx +++ b/src/pages/job-detail/index.tsx @@ -134,7 +134,7 @@ const AnchorFooter = (props: { data: JobDetails }) => { }); } else { // Show material guide if no resume and no VIP and no free balance - if (!productInfo?.isCreateResume && !productInfo?.isPaidVip && !productInfo?.freeBalance) { + if (!productRecord && !productInfo?.isCreateResume && !productInfo?.isPaidVip && !productInfo?.freeBalance) { setShowMaterialGuide(true); return; } From 14853bcda2707be19f84e82dbb5426d3006fdab7 Mon Sep 17 00:00:00 2001 From: chashaobao Date: Sat, 27 Dec 2025 16:21:33 +0800 Subject: [PATCH 15/18] feat: --- src/components/prejob-popup/index.tsx | 2 +- .../steps-ui/company-publish-job-buy.tsx | 2 +- .../product-dialog/steps-ui/job-buy.tsx | 4 ++-- src/fragments/user/anchor/index.less | 21 +++++++++++++++++++ src/fragments/user/anchor/index.tsx | 11 +++++++++- src/http/constant.ts | 1 + src/pages/invite-operations/index.tsx | 2 +- src/types/product.ts | 1 + 8 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/components/prejob-popup/index.tsx b/src/components/prejob-popup/index.tsx index 3e75fe5..587a810 100644 --- a/src/components/prejob-popup/index.tsx +++ b/src/components/prejob-popup/index.tsx @@ -45,7 +45,7 @@ const GET_CONTACT_TYPE_OPTIONS = [ { type: GET_CONTACT_TYPE.INVITE, icon: 'https://publiccdn.neighbourhood.com.cn/img/invite-operations.png', - title: '邀请运营进群(进会员)', + title: '邀请运营进群(送会员)', desc: '每邀请进一个主播群送一个日会员', btnText: '邀请', }, 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 2d197b4..8ebc141 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 @@ -161,7 +161,7 @@ export default function CompanyPublishJobBuy(props: IProps) {
handleClickItem(item)} diff --git a/src/components/product-dialog/steps-ui/job-buy.tsx b/src/components/product-dialog/steps-ui/job-buy.tsx index 498a48a..77dfd2c 100644 --- a/src/components/product-dialog/steps-ui/job-buy.tsx +++ b/src/components/product-dialog/steps-ui/job-buy.tsx @@ -167,9 +167,9 @@ export default function JobBuy(props: IProps) { {productList.map(item => { return (
handleClickItem(item)} diff --git a/src/fragments/user/anchor/index.less b/src/fragments/user/anchor/index.less index d575507..dc72799 100644 --- a/src/fragments/user/anchor/index.less +++ b/src/fragments/user/anchor/index.less @@ -17,4 +17,25 @@ border-radius: 16px; margin-top: 24px; } + + &__recommend { + display: inline-flex; + line-height: 36px; + padding: 0 8px; + height: 36px; + background: rgb(255, 80, 81); + border-radius: 4px; + font-size: 24px; + color: #fff; + align-items: center; + gap: 6px; + position: absolute; + left: 208px; + top: 8px; + width: 128px; + + &-cell { + position: relative; + } + } } diff --git a/src/fragments/user/anchor/index.tsx b/src/fragments/user/anchor/index.tsx index 0b9e112..db52ceb 100644 --- a/src/fragments/user/anchor/index.tsx +++ b/src/fragments/user/anchor/index.tsx @@ -1,6 +1,7 @@ import { Image } from '@tarojs/components'; import { Cell } from '@taroify/core'; +import { GoodJob } from '@taroify/icons'; import { useCallback } from 'react'; import MaterialCard from '@/components/material-card'; @@ -34,7 +35,15 @@ export default function AnchorFragment() { + 免费领主播会员 +
+ + 强烈推荐 +
+
+ } className={`${PREFIX}__cell`} onClick={handleClickInviteOperations} /> diff --git a/src/http/constant.ts b/src/http/constant.ts index 4d73434..28428f8 100644 --- a/src/http/constant.ts +++ b/src/http/constant.ts @@ -27,6 +27,7 @@ export enum RESPONSE_ERROR_CODE { INSUFFICIENT_FREE_BALANCE = 'INSUFFICIENT_FREE_BALANCE', // 免费查看次数(未购买会员)超限 BOSS_VIP_EXPIRED = 'BOSS_VIP_EXPIRED', // 会员过期 CHAT_MSG_SEND_NOT_ALLOW = 'CHAT_MSG_SEND_NOT_ALLOW', + JOB_EXIST_ONE_PUBLISHED = 'JOB_EXIST_ONE_PUBLISHED', } export const RESPONSE_ERROR_INFO: { [key in RESPONSE_ERROR_CODE]?: string } = { diff --git a/src/pages/invite-operations/index.tsx b/src/pages/invite-operations/index.tsx index cfa09a3..1a1fafd 100644 --- a/src/pages/invite-operations/index.tsx +++ b/src/pages/invite-operations/index.tsx @@ -19,7 +19,7 @@ const PREFIX = 'invite-operations'; export default function InviteOperations() { const location = useLocation(); const userInfo = useUserInfo(); - const [cityCode, setCityCode] = useState(location.cityCode); + const [cityCode, setCityCode] = useState(); const [staffInfo, setStaffInfo] = useState(null); const handleClickCityMenu = useCallback(() => { diff --git a/src/types/product.ts b/src/types/product.ts index fbd6f4f..dfc892f 100644 --- a/src/types/product.ts +++ b/src/types/product.ts @@ -114,6 +114,7 @@ export interface GetOrderInfoRequest { } export interface ProductSpecResult { + id: string; productId: string; productSpecId: ProductSpecId; productType: ProductType; From 23879fe88de3be46d3b07c4181439361e3ab6df7 Mon Sep 17 00:00:00 2001 From: chashaobao Date: Sat, 27 Dec 2025 16:26:53 +0800 Subject: [PATCH 16/18] feat: --- src/hooks/use-publish-job.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/hooks/use-publish-job.ts b/src/hooks/use-publish-job.ts index 9927aba..03536ef 100644 --- a/src/hooks/use-publish-job.ts +++ b/src/hooks/use-publish-job.ts @@ -39,9 +39,11 @@ export const usePublishJob = (jobId?: string): [boolean, Dispatch Date: Mon, 29 Dec 2025 23:49:51 +0800 Subject: [PATCH 17/18] feat: --- src/hooks/use-publish-job.ts | 2 -- src/pages/certification/index.tsx | 13 +++++++++---- src/pages/invite-operations/index.tsx | 11 ++++++++++- src/pages/job-publish/index.tsx | 19 +++++++++++++++---- src/pages/material-view/index.tsx | 8 +++++++- 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/hooks/use-publish-job.ts b/src/hooks/use-publish-job.ts index 03536ef..6c8b08f 100644 --- a/src/hooks/use-publish-job.ts +++ b/src/hooks/use-publish-job.ts @@ -39,8 +39,6 @@ export const usePublishJob = (jobId?: string): [boolean, Dispatch { + if (!userInfo.userId) return; + if (useCopyRef.current) return; + handleCopy(); + useCopyRef.current = true; + }, [handleCopy, userInfo.userId]); + useEffect(() => { if (!cityCode) return; diff --git a/src/pages/job-publish/index.tsx b/src/pages/job-publish/index.tsx index ed5d823..5f2213a 100644 --- a/src/pages/job-publish/index.tsx +++ b/src/pages/job-publish/index.tsx @@ -41,6 +41,7 @@ import { getPageQuery, navigateBack, navigateTo } from '@/utils/route'; import Toast from '@/utils/toast'; import './index.less'; +import { RESPONSE_ERROR_CODE } from '@/http/constant'; const PREFIX = 'page-job-publish'; const log = logWithPrefix(PREFIX); @@ -237,8 +238,13 @@ export default function JobPublish() { navigateBack(); } catch (e) { createdJobIdRef.current = ''; - console.error('submit error', e); - Toast.error('审核失败请重试'); + console.error('submit error', e.errorCode, e.errorMsg); + console.dir(e); + if (e.errorCode === RESPONSE_ERROR_CODE.JOB_EXIST_ONE_PUBLISHED) { + Toast.info(e.errorMsg || e.errorCode, 3000); + } else { + Toast.error('审核失败请重试'); + } collectEvent(CollectEventName.PUBLISH_JOB_FAILED, e); } finally { Taro.hideLoading(); @@ -253,8 +259,13 @@ export default function JobPublish() { await Toast.success('发布成功', 1500, true); navigateBack(); } catch (e) { - console.error('submit error', e); - Toast.error('审核失败请重试'); + console.error('submit error', e.errorCode, e.errorMsg); + console.dir(e); + if (e.errorCode === RESPONSE_ERROR_CODE.JOB_EXIST_ONE_PUBLISHED) { + Toast.info(e.errorMsg || e.errorCode, 3000); + } else { + Toast.error('审核失败请重试'); + } collectEvent(CollectEventName.PUBLISH_JOB_FAILED, e); } finally { Taro.hideLoading(); diff --git a/src/pages/material-view/index.tsx b/src/pages/material-view/index.tsx index afc5635..ae7b82d 100644 --- a/src/pages/material-view/index.tsx +++ b/src/pages/material-view/index.tsx @@ -141,7 +141,13 @@ export default function MaterialViewPage() { await postPublishJob(jobId!); await handleClickContact(); } catch (e) { - Toast.error('通告发布失败'); + console.error('submit error', e.errorCode, e.errorMsg); + console.dir(e); + if (e.errorCode === RESPONSE_ERROR_CODE.JOB_EXIST_ONE_PUBLISHED) { + Toast.info(e.errorMsg || e.errorCode, 3000); + } else { + Toast.error('通告发布失败'); + } collectEvent(CollectEventName.PUBLISH_JOB_FAILED, e); } }, [handleClickContact, jobId]); From 34e8a775e4fbc74ae663fd10645b65206dab207e Mon Sep 17 00:00:00 2001 From: chashaobao Date: Mon, 5 Jan 2026 00:22:42 +0800 Subject: [PATCH 18/18] feat --- src/app.tsx | 24 +++++++++++++ src/components/product-dialog/index.less | 35 ++++++++----------- .../steps-ui/company-publish-job-buy.tsx | 3 +- src/pages/invite-operations/index.tsx | 4 +-- src/pages/material-upload-video/index.less | 23 ++++++++++-- src/pages/material-upload-video/index.tsx | 1 + 6 files changed, 63 insertions(+), 27 deletions(-) diff --git a/src/app.tsx b/src/app.tsx index da271d6..ad02ef3 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -15,6 +15,29 @@ import { requestUserInfo, updateLastLoginTime } from '@/utils/user'; import './app.less'; function App({ children }: PropsWithChildren) { + const checkBasicUpdate = () => { + if (process.env.TARO_ENV !== 'weapp' || !Taro.canIUse('getUpdateManager')) { + return; + } + const manager = Taro.getUpdateManager(); + manager.onCheckForUpdate(res => { + console.log('onCheckForUpdate', res); + }); + + // 新版本下载完成 + manager.onUpdateReady(() => { + Taro.showModal({ + title: '更新提示', + content: '新版本已经准备好,是否重启应用?', + success: function (res) { + if (res.confirm) { + // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 + manager.applyUpdate(); + } + }, + }); + }); + }; useLaunch(async ({ query }) => { console.log('App launched.'); await http.init(getInviteCodeFromQuery(query)); @@ -30,6 +53,7 @@ function App({ children }: PropsWithChildren) { }); useDidShow(options => { + checkBasicUpdate(); requestCityConfigs(); console.log(options); diff --git a/src/components/product-dialog/index.less b/src/components/product-dialog/index.less index 73e41cd..9f068c2 100644 --- a/src/components/product-dialog/index.less +++ b/src/components/product-dialog/index.less @@ -2,7 +2,6 @@ @import '@/styles/variables.less'; .product-dialog { - .layout() { display: flex; flex-direction: column; @@ -31,7 +30,7 @@ font-weight: 500; line-height: 72px; border-radius: 44px; - color: #FFFFFF; + color: #ffffff; background: @blHighlightColor; margin-top: 40px; } @@ -42,7 +41,7 @@ font-size: 24px; line-height: 48px; color: @blHighlightColor; - background: #6D3DF514; + background: #6d3df514; border-radius: 8px; padding: 32px 72px; margin-top: 40px; @@ -73,7 +72,7 @@ &__describe { .describe-font(); - margin-top: 24px + margin-top: 24px; } &__content { @@ -173,8 +172,8 @@ } &.disabled { - border-color: #E0E0E0; - background: #F7F7F7; + border-color: #e0e0e0; + background: #f7f7f7; } &.selected { @@ -237,9 +236,7 @@ color: @blHighlightColor; display: inline; } - } - } // ============================================= 群 ================================================= // @@ -323,7 +320,7 @@ line-height: 36px; text-align: center; border-radius: 50%; - border: 2px solid #E0E0E0; + border: 2px solid #e0e0e0; } &__qr-code__step-title { @@ -338,7 +335,7 @@ position: relative; width: 2px; height: 40px; - background: #E0E0E0; + background: #e0e0e0; margin: 4px 0; margin-left: 18px; } @@ -365,7 +362,7 @@ .divider { width: 540px; height: 1px; - background: #E0E0E0; + background: #e0e0e0; margin: 40px 0; } @@ -417,7 +414,7 @@ margin-top: 40px; .highlight { - color: @blHighlightColor + color: @blHighlightColor; } } @@ -523,7 +520,6 @@ .button(); } - // ============================================= 发布通告的企业会员 ================================================= // &__publish-job-buy__header { .header-font(); @@ -576,21 +572,21 @@ &__item { position: relative; - width: 230px; + width: 182px; height: 156px; .flex-column(); justify-content: center; border: 2px solid @blHighlightColor; border-radius: 8px; - margin-right: 24px; + margin-right: 15px; &:last-child { margin-right: 0; } &.disabled { - border-color: #E0E0E0; - background: #F7F7F7; + border-color: #e0e0e0; + background: #f7f7f7; } &.selected { @@ -635,11 +631,11 @@ } &__left-line { - background: linear-gradient(270deg, #E0E0E0 0%, #FFFFFF 100%); + background: linear-gradient(270deg, #e0e0e0 0%, #ffffff 100%); } &__right-line { - background: linear-gradient(90deg, #E0E0E0 0%, #FFFFFF 100%); + background: linear-gradient(90deg, #e0e0e0 0%, #ffffff 100%); } &__title { @@ -680,5 +676,4 @@ margin-top: 32px; } } - } 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 8ebc141..7cc20d5 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 @@ -9,10 +9,10 @@ import { PREFIX } from '@/components/product-dialog/const'; import { CollectEventName, ReportEventId } from '@/constants/event'; import { OrderStatus, OrderType, ProductSpecId, ProductType } from '@/constants/product'; import { SubscribeTempId } from '@/constants/subscribe'; +import { ProductSpecResult } from '@/types/product'; import { logWithPrefix } from '@/utils/common'; import { collectEvent, reportEvent } from '@/utils/event'; import { - getOrderPrice, isCancelPay, requestCreatePayInfo, requestOrderInfo, @@ -21,7 +21,6 @@ import { } from '@/utils/product'; import { postSubscribe, subscribeMessage } from '@/utils/subscribe'; import Toast from '@/utils/toast'; -import { ProductSpecResult } from '@/types/product'; interface IProps { onNext: () => void; diff --git a/src/pages/invite-operations/index.tsx b/src/pages/invite-operations/index.tsx index 727f67b..97420aa 100644 --- a/src/pages/invite-operations/index.tsx +++ b/src/pages/invite-operations/index.tsx @@ -36,7 +36,7 @@ export default function InviteOperations() { }, []); const handleCopy = useCallback(() => { - copy(`我的ID是:${userInfo.userId},邀你进主播群`); + copy(`我的播络ID是:${userInfo.userId},邀你进群`); }, [userInfo.userId]); useEffect(() => { @@ -92,7 +92,7 @@ export default function InviteOperations() { 加运营为好友并将以下信息
粘贴发送给运营
-
我的ID是:{userInfo.userId},邀你进主播群
+
我的播络ID是:{userInfo.userId},邀你进群
diff --git a/src/pages/material-upload-video/index.less b/src/pages/material-upload-video/index.less index 1fdaa9d..48581a3 100644 --- a/src/pages/material-upload-video/index.less +++ b/src/pages/material-upload-video/index.less @@ -21,7 +21,24 @@ } &__video-list { - margin-top: 16px; + margin-top: 25px; + } + + &__hint { + line-height: 64px; + height: 64px; + background: #fff4f0; + border-radius: 24px 24px 0px 0px; + padding: 0 24px; + font-weight: 400; + font-size: 26px; + color: #946724; + + + .material-video-card { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; + } } &__footer { @@ -29,11 +46,11 @@ left: 24px; right: 24px; bottom: 0; - background: #F5F6FA; + background: #f5f6fa; } &__submit-btn { .button(@width: 100%, @height: 80px, @fontSize: 32px, @fontWeight: 400, @borderRadius: 48px); bottom: 40px; } -} \ No newline at end of file +} diff --git a/src/pages/material-upload-video/index.tsx b/src/pages/material-upload-video/index.tsx index 5ec7947..9b43ef3 100644 --- a/src/pages/material-upload-video/index.tsx +++ b/src/pages/material-upload-video/index.tsx @@ -181,6 +181,7 @@ export default function MaterialUploadVideo() {
录屏是企业最关注的资料,建议提供多个风格和品类
+
注:请勿乱传,也不要上传简历,会被封号
{videoList.map(video => (