From 96eb46821e93313616664042c8f0bbd7f2b351f4 Mon Sep 17 00:00:00 2001 From: chashaobao Date: Sat, 14 Jun 2025 23:45:47 +0800 Subject: [PATCH] feat: withdraw --- package.json | 3 +- src/components/partner-invite-list/index.tsx | 2 +- src/components/partner-kanban/index.tsx | 73 +++++++++--- src/components/partner-profit/ProfitList.tsx | 3 +- src/components/partner-profit/index.less | 7 +- src/constants/partner.ts | 6 + src/hooks/user-role-type.tsx | 3 +- src/http/api.ts | 2 + src/pages/partner/index.tsx | 11 +- src/pages/withdraw-record/index.less | 48 ++++++++ src/pages/withdraw-record/index.tsx | 111 ++++++++++++++++++- src/types/partner.ts | 23 ++++ src/utils/partner.ts | 16 ++- 13 files changed, 279 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 9f90bd7..55669c3 100644 --- a/package.json +++ b/package.json @@ -109,5 +109,6 @@ "ignoredBuiltDependencies": [ "@tarojs/binding" ] - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/components/partner-invite-list/index.tsx b/src/components/partner-invite-list/index.tsx index 79c5384..4016e29 100644 --- a/src/components/partner-invite-list/index.tsx +++ b/src/components/partner-invite-list/index.tsx @@ -125,7 +125,7 @@ function PartnerList(props: { hasMore={hasMore} onLoad={handleLoadMore} loading={loadingMore || refreshing} - disabled={loadMoreError} + disabled={loadMoreError || !visible} fixedHeight={typeof listHeight !== 'undefined'} style={listHeight ? { height: `${listHeight}px` } : undefined} > diff --git a/src/components/partner-kanban/index.tsx b/src/components/partner-kanban/index.tsx index 0c08efc..5190b2c 100644 --- a/src/components/partner-kanban/index.tsx +++ b/src/components/partner-kanban/index.tsx @@ -1,14 +1,16 @@ import { Button, Image } from '@tarojs/components'; +import Taro, { useDidShow } from '@tarojs/taro'; import { Dialog } from '@taroify/core'; import { Question } from '@taroify/icons'; -import { useCallback, useState, useEffect } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { PageUrl } from '@/constants/app'; import { PartnerProfitsState } from '@/types/partner'; -import { formatMoney, getPartnerProfitStat } from '@/utils/partner'; +import { formatMoney, getPartnerProfitStat, withdrawMoney } from '@/utils/partner'; import { navigateTo } from '@/utils/route'; import Toast from '@/utils/toast'; + import './index.less'; const PREFIX = 'partner-kanban'; @@ -31,14 +33,45 @@ function TipDialog(props: { open: boolean; onClose: () => void }) { } function WithdrawDialog(props: { open: boolean; onClose: () => void; count: number }) { - const handleWithdraw = useCallback(() => {}, []); + const handleWithdraw = useCallback(async () => { + if (Taro.canIUse('requestMerchantTransfer')) { + try { + const result = await withdrawMoney(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + wx.requestMerchantTransfer({ + mchId: '1642470088', + appId: 'wxf0724a83f8e377d2', + package: result.packageInfo, + success: (res: never) => { + // res.err_msg将在页面展示成功后返回应用时返回ok,并不代表付款成功 + console.log('success:', res); + Toast.success('提现成功'); + props.onClose(); + }, + fail: (res: never) => { + Toast.error('提现失败'); + console.log('fail:', res); + }, + }); + } catch (e) { + Toast.error('提现订单创建失败'); + console.log(e); + } + } else { + await Taro.showModal({ + content: '你的微信版本过低,请更新至最新版本。', + showCancel: false, + }); + } + }, [props]); return (
本次申请提现金额为
- {props.count} + {+props.count}
单笔最大500元
@@ -86,20 +119,24 @@ export default function PartnerKanban({ simple }: PartnerKanbanProps) { const handleTipClose = useCallback(() => { setTipOpen(false); }, []); - const handleViewWithdraw = useCallback(() => { - if (stats.availableBalance < 10 * 1000) { - Toast.info('提现金额需大于等于10元'); - return; - } - setWithdrawOpen(true); - }, []); - const handleWithdrawClose = useCallback(() => { - setWithdrawOpen(false); - }, []); const getProfitStats = useCallback(async () => { const data = await getPartnerProfitStat(); setStats(data); }, []); + const handleViewWithdraw = useCallback(() => { + if (stats.availableBalance < 10 * 1000) { + Toast.error('提现金额需大于等于10元'); + return; + } + setWithdrawOpen(true); + }, [stats.availableBalance]); + const handleWithdrawClose = useCallback(() => { + setWithdrawOpen(false); + getProfitStats(); + }, [getProfitStats]); + useDidShow(() => { + getProfitStats(); + }); useEffect(() => { getProfitStats(); }, []); @@ -154,7 +191,13 @@ export default function PartnerKanban({ simple }: PartnerKanbanProps) { )}
{!simple && } - {!simple && } + {!simple && ( + + )} ); } diff --git a/src/components/partner-profit/ProfitList.tsx b/src/components/partner-profit/ProfitList.tsx index afbe973..e619442 100644 --- a/src/components/partner-profit/ProfitList.tsx +++ b/src/components/partner-profit/ProfitList.tsx @@ -96,7 +96,8 @@ function ProfitList(props: IPartnerProfitListProps) { style={listHeight ? { height: `${listHeight}px` } : undefined} > {dataList.map(item => { - const isChat = type === ProfitType.CHAT_SHARE || item.earnType.toString().toLowerCase().indexOf('chat') > -1; + const isChat = + type === ProfitType.CHAT_SHARE || item.earnType.toString().toLowerCase().indexOf('chat') > -1; return (
diff --git a/src/components/partner-profit/index.less b/src/components/partner-profit/index.less index 5b8da56..f260145 100644 --- a/src/components/partner-profit/index.less +++ b/src/components/partner-profit/index.less @@ -78,10 +78,13 @@ &:last-child { padding-right: 0; } - &.time, - &.project { + &.time { flex: 2; } + &.project { + width: 150px; + flex-shrink: 0; + } &.status { width: 96px; padding: 0 8px; diff --git a/src/constants/partner.ts b/src/constants/partner.ts index 5693df9..549fe0f 100644 --- a/src/constants/partner.ts +++ b/src/constants/partner.ts @@ -52,3 +52,9 @@ export const ProfitStatusDescriptions = { OTHER: '', FINISHED: '已分账', }; + +export const WithdrawStatusDescriptions = { + 0: '提现中', + 1: '已提现', + 2: '失败', +}; diff --git a/src/hooks/user-role-type.tsx b/src/hooks/user-role-type.tsx index ab2785b..9ff3e9d 100644 --- a/src/hooks/user-role-type.tsx +++ b/src/hooks/user-role-type.tsx @@ -1,9 +1,10 @@ import { useSelector } from 'react-redux'; +import { RoleType } from '@/constants/app'; import { selectRoleType } from '@/store/selector'; function useRoleType() { - return useSelector(selectRoleType); + return useSelector(selectRoleType) || RoleType.Anchor; } export default useRoleType; diff --git a/src/http/api.ts b/src/http/api.ts index 676e0ac..d4f3046 100644 --- a/src/http/api.ts +++ b/src/http/api.ts @@ -82,4 +82,6 @@ export enum API { BECOME_PARTNER = '/user/becomePartner', GET_PROFIT_LIST = '/user/profit/list', GET_PROFIT_STAT = '/user/profits', + WITHDRAW = '/user/withdraw', + GET_WITHDRAW_LIST = '/user/withdraw/records', } diff --git a/src/pages/partner/index.tsx b/src/pages/partner/index.tsx index d1e4d7d..4d1677e 100644 --- a/src/pages/partner/index.tsx +++ b/src/pages/partner/index.tsx @@ -1,20 +1,23 @@ import { useShareAppMessage } from '@tarojs/taro'; import { Tabs } from '@taroify/core'; +import { useState } from 'react'; import PartnerIntro from '@/components/partner-intro'; import PartnerInviteList from '@/components/partner-invite-list'; import PartnerProfit from '@/components/partner-profit'; import useInviteCode from '@/hooks/use-invite-code'; import { getCommonShareMessage } from '@/utils/share'; - import './index.less'; const PREFIX = 'partner'; export default function Partner() { const inviteCode = useInviteCode(); - + const [tab, setTab] = useState(0); + const handleChange = v => { + setTab(v); + }; useShareAppMessage(() => { console.log('Partner inviteCode', inviteCode); return getCommonShareMessage(false, inviteCode); @@ -22,12 +25,12 @@ export default function Partner() { return (
- + - + diff --git a/src/pages/withdraw-record/index.less b/src/pages/withdraw-record/index.less index ef8edfc..a067e2f 100644 --- a/src/pages/withdraw-record/index.less +++ b/src/pages/withdraw-record/index.less @@ -2,4 +2,52 @@ @import '@/styles/variables.less'; .withdraw-record { + &__item { + height: 131px; + width: 100%; + background: #fff; + padding: 24px 32px 0 32px; + box-sizing: border-box; + font-size: 28px; + + &-border { + border-bottom: 1px solid #e6e7e8; + } + + &-content { + .flex-row(); + width: 100%; + padding-bottom: 24px; + } + + &-time { + padding-right: 8px; + line-height: 40px; + flex: 1; + .noWrap(); + } + + &-withdraw { + width: 200px; + text-align: right; + padding-left: 8px; + flex-shrink: 0; + } + + &-money { + line-height: 40px; + padding-bottom: 8px; + .noWrap(); + font-weight: 600; + font-size: 30px; + color: #ff5051; + } + + &-status { + font-size: 24px; + line-height: 36px; + color: #999999; + .noWrap(); + } + } } diff --git a/src/pages/withdraw-record/index.tsx b/src/pages/withdraw-record/index.tsx index 1baec79..a3cc0de 100644 --- a/src/pages/withdraw-record/index.tsx +++ b/src/pages/withdraw-record/index.tsx @@ -1,14 +1,119 @@ import { useShareAppMessage } from '@tarojs/taro'; +import { List, PullRefresh } from '@taroify/core'; +import { useCallback, useEffect, useRef, useState } from 'react'; + +import ListPlaceholder from '@/components/list-placeholder'; +import { WithdrawStatusDescriptions } from '@/constants/partner'; +import useInviteCode from '@/hooks/use-invite-code'; +import { WithdrawRecord } from '@/types/partner'; +import { logWithPrefix } from '@/utils/common'; +import { formatMoney, formatTimestamp, getWithdrawList as requestData } from '@/utils/partner'; import { getCommonShareMessage } from '@/utils/share'; + import './index.less'; const PREFIX = 'withdraw-record'; +const log = logWithPrefix(PREFIX); +const FIRST_PAGE = 0; -export default function WithdrawRecord() { +export default function WithdrawRecords() { + const inviteCode = useInviteCode(); + const [hasMore, setHasMore] = useState(true); + const [refreshing, setRefreshing] = useState(false); + const [loadingMore, setLoadingMore] = useState(false); + const [loadMoreError, setLoadMoreError] = useState(false); + const [dataList, setDataList] = useState([]); + const currentPage = useRef(FIRST_PAGE); + + const handleRefresh = useCallback(async () => { + log('start pull refresh'); + try { + setRefreshing(true); + setLoadMoreError(false); + const { content, totalPages } = await requestData({ page: 1 }); + setDataList(content); + currentPage.current = 1; + setHasMore(currentPage.current < totalPages); + log('pull refresh success'); + } catch (e) { + setDataList([]); + setHasMore(false); + setLoadMoreError(true); + currentPage.current = FIRST_PAGE; + log('pull refresh failed'); + } finally { + setRefreshing(false); + } + }, []); + + const handleLoadMore = useCallback(async () => { + log('start load more', hasMore); + if (!hasMore) { + return; + } + setLoadMoreError(false); + setLoadingMore(true); + try { + const { totalPages, content } = await requestData({ page: currentPage.current + 1 }); + setDataList([...dataList, ...content]); + currentPage.current = currentPage.current + 1; + setHasMore(currentPage.current < totalPages); + log('load more success'); + } catch (e) { + setLoadMoreError(true); + log('load more failed'); + } finally { + setLoadingMore(false); + } + }, [dataList, hasMore]); + // 初始化数据&配置变更后刷新数据 + useEffect(() => { + const refresh = async () => { + log('visible changed, start refresh list data'); + try { + setDataList([]); + setLoadingMore(true); + setLoadMoreError(false); + const { totalPages, content } = await requestData({ page: 1 }); + setDataList(content); + currentPage.current = 1; + setHasMore(currentPage.current < totalPages); + } catch (e) { + setDataList([]); + setHasMore(false); + setLoadMoreError(true); + } finally { + log('visible changed, refresh list data end'); + setLoadingMore(false); + } + }; + refresh(); + }, []); useShareAppMessage(() => { - return getCommonShareMessage(false); + return getCommonShareMessage(false, inviteCode); }); - return
; + return ( +
+ + + {dataList.map(item => ( +
+
+
+
{formatTimestamp(item.created)}
+
+
{formatMoney(item.amt)}
+
{WithdrawStatusDescriptions[item.status]}
+
+
+
+
+ ))} + +
+
+
+ ); } diff --git a/src/types/partner.ts b/src/types/partner.ts index a3af293..36ab9bd 100644 --- a/src/types/partner.ts +++ b/src/types/partner.ts @@ -64,3 +64,26 @@ export interface PartnerProfitItem { expectedSettlementDate: string; actualSettlementDate: string; } +export interface WithdrawResponse { + outBillNo: string; + transferBillNo: string; + createTime: string; + state: string; + packageInfo: string; +} +export interface WithdrawRecord { + id: number; + userId: string; + orderNo: string; + outTradeId: string; + amt: number; + status: number; + remark: string; + created: string; + updated: string; + finishedTime: string; +} +export interface PartnerPagination { + content: T[]; + totalPages: number; +} diff --git a/src/utils/partner.ts b/src/utils/partner.ts index d48ee2a..047831f 100644 --- a/src/utils/partner.ts +++ b/src/utils/partner.ts @@ -10,8 +10,11 @@ import { GetProfitRequest, InviteUserInfo, PartnerInviteCode, + PartnerPagination, PartnerProfitItem, PartnerProfitsState, + WithdrawRecord, + WithdrawResponse, } from '@/types/partner'; import { requestUserInfo } from '@/utils/user'; @@ -53,7 +56,7 @@ export const getPartnerProfitStat = async () => { return await http.post(API.GET_PROFIT_STAT); }; export const getPartnerInviteList = async (data: IPaginationRequest) => { - return await http.post<{ content: InviteUserInfo[]; totalPages: number }>(API.GET_INVITE_LIST, { + return await http.post>(API.GET_INVITE_LIST, { data, contentType: 'application/x-www-form-urlencoded', }); @@ -113,3 +116,14 @@ export function formatUserId(input: string): string { // 拼接结果 return beforeMask + maskedPart + afterMask; } + +export async function withdrawMoney() { + return await http.post(API.WITHDRAW); +} + +export async function getWithdrawList(data: IPaginationRequest) { + return await http.post>(API.GET_WITHDRAW_LIST, { + data, + contentType: 'application/x-www-form-urlencoded', + }); +}