Compare commits
10 Commits
546d90cc28
...
81c63e0e65
| Author | SHA1 | Date | |
|---|---|---|---|
| 81c63e0e65 | |||
| 86f7506422 | |||
| fae144549e | |||
| 718eddff63 | |||
| a4a0a922fc | |||
| ca2436a2e8 | |||
| 83ba49d860 | |||
| 7375a3b1ce | |||
| 756b548bf9 | |||
| 76e76c62ef |
@@ -134,11 +134,11 @@ const DealerIndex: React.FC = () => {
|
|||||||
<View className="grid grid-cols-3 gap-3">
|
<View className="grid grid-cols-3 gap-3">
|
||||||
<View className="text-center p-3 rounded-lg flex flex-col" style={{
|
<View className="text-center p-3 rounded-lg flex flex-col" style={{
|
||||||
background: businessGradients.money.available
|
background: businessGradients.money.available
|
||||||
}}>
|
}} onClick={() => navigateToPage('/dealer/withdraw/index')}>
|
||||||
<Text className="text-lg font-bold mb-1 text-white">
|
<Text className="text-lg font-bold mb-1 text-white">
|
||||||
{formatMoney(dealerUser.money)}
|
{formatMoney(dealerUser.money)}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.9)' }} onClick={() => navigateToPage('/dealer/withdraw/index')}>可提现</Text>
|
<Text className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.9)' }}>可提现</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="text-center p-3 rounded-lg flex flex-col" style={{
|
<View className="text-center p-3 rounded-lg flex flex-col" style={{
|
||||||
background: businessGradients.money.frozen
|
background: businessGradients.money.frozen
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ const normalizeMoneyString = (money: unknown) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DealerWithdraw: React.FC = () => {
|
const DealerWithdraw: React.FC = () => {
|
||||||
const [activeTab, setActiveTab] = useState<string | number>('0')
|
const [activeTab, setActiveTab] = useState<string>('0')
|
||||||
const [loading, setLoading] = useState<boolean>(false)
|
const [loading, setLoading] = useState<boolean>(false)
|
||||||
const [refreshing, setRefreshing] = useState<boolean>(false)
|
const [refreshing, setRefreshing] = useState<boolean>(false)
|
||||||
const [submitting, setSubmitting] = useState<boolean>(false)
|
const [submitting, setSubmitting] = useState<boolean>(false)
|
||||||
@@ -114,10 +114,11 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
// Tab 切换处理函数
|
// Tab 切换处理函数
|
||||||
const handleTabChange = (value: string | number) => {
|
const handleTabChange = (value: string | number) => {
|
||||||
console.log('Tab切换到:', value)
|
console.log('Tab切换到:', value)
|
||||||
setActiveTab(value)
|
const next = String(value)
|
||||||
|
setActiveTab(next)
|
||||||
|
|
||||||
// 如果切换到提现记录页面,刷新数据
|
// 如果切换到提现记录页面,刷新数据
|
||||||
if (String(value) === '1') {
|
if (next === '1') {
|
||||||
fetchWithdrawRecords()
|
fetchWithdrawRecords()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,7 +311,7 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
if (amount > available) {
|
if (amount > available) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '提现金额超过可用余额',
|
title: '提现金额超过可用余额',
|
||||||
icon: 'error'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -628,13 +629,12 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
<View className="bg-gray-50 min-h-screen">
|
<View className="bg-gray-50 min-h-screen">
|
||||||
<Tabs value={activeTab} onChange={handleTabChange}>
|
<Tabs value={activeTab} onChange={handleTabChange}>
|
||||||
<Tabs.TabPane title="申请提现" value="0">
|
<Tabs.TabPane title="申请提现" value="0">
|
||||||
{renderWithdrawForm()}
|
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
|
|
||||||
<Tabs.TabPane title="提现记录" value="1">
|
<Tabs.TabPane title="提现记录" value="1">
|
||||||
{renderWithdrawRecords()}
|
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
{activeTab === '0' ? renderWithdrawForm() : renderWithdrawRecords()}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Banner from './Banner'
|
|||||||
import Taro, { useDidShow, useShareAppMessage } from '@tarojs/taro'
|
import Taro, { useDidShow, useShareAppMessage } from '@tarojs/taro'
|
||||||
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
||||||
import { useEffect, useMemo, useState, type ReactNode } from 'react'
|
import { useEffect, useMemo, useState, type ReactNode } from 'react'
|
||||||
import { Cart, Gift, Ticket, Agenda } from '@nutui/icons-react-taro'
|
import { Cart, Gift, Ticket, Agenda, ArrowRight } from '@nutui/icons-react-taro'
|
||||||
import { getShopInfo } from '@/api/layout'
|
import { getShopInfo } from '@/api/layout'
|
||||||
import { checkAndHandleInviteRelation, hasPendingInvite } from '@/utils/invite'
|
import { checkAndHandleInviteRelation, hasPendingInvite } from '@/utils/invite'
|
||||||
import { pageShopGoods } from '@/api/shop/shopGoods'
|
import { pageShopGoods } from '@/api/shop/shopGoods'
|
||||||
@@ -11,6 +11,7 @@ import type { ShopGoods, ShopGoodsParam } from '@/api/shop/shopGoods/model'
|
|||||||
import { getMyGltUserTicketTotal } from '@/api/glt/gltUserTicket'
|
import { getMyGltUserTicketTotal } from '@/api/glt/gltUserTicket'
|
||||||
import { ensureLoggedIn } from '@/utils/auth'
|
import { ensureLoggedIn } from '@/utils/auth'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
// import navTo from "@/utils/common";
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
const [activeTabKey, setActiveTabKey] = useState('recommend')
|
const [activeTabKey, setActiveTabKey] = useState('recommend')
|
||||||
@@ -178,8 +179,8 @@ function Home() {
|
|||||||
>(
|
>(
|
||||||
() => [
|
() => [
|
||||||
{ key: 'recommend', title: '推荐', params: { recommend: 1 } },
|
{ key: 'recommend', title: '推荐', params: { recommend: 1 } },
|
||||||
{ key: '4476', title: '政企采购专区', params: { categoryId: 4476 } },
|
{ key: '4476', title: '桶装水', params: { categoryId: 4476 } },
|
||||||
{ key: '4556', title: '桂乐淘·福利惊爆区', params: { categoryId: 4556 } },
|
{ key: '4556', title: '水票套餐', params: { categoryId: 4556 } },
|
||||||
// { key: '4557', title: '购机套餐', params: { categoryId: 4557 } },
|
// { key: '4557', title: '购机套餐', params: { categoryId: 4557 } },
|
||||||
// { key: '4477', title: '饮水设备', params: { categoryId: 4477 } },
|
// { key: '4477', title: '饮水设备', params: { categoryId: 4477 } },
|
||||||
],
|
],
|
||||||
@@ -289,7 +290,21 @@ function Home() {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 分类Tabs */}
|
<View className="ticket-card" onClick={() => Taro.navigateTo({ url: `/shop/category/index?id=4560` })}>
|
||||||
|
<View className="ticket-card__head">
|
||||||
|
<Text className="ticket-card__title">政企采购专区</Text>
|
||||||
|
<ArrowRight className={'text-gray-50'} size={16} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="ticket-card" onClick={() => Taro.navigateTo({ url: `/shop/category/index?id=4556` })}>
|
||||||
|
<View className="ticket-card__head">
|
||||||
|
<Text className="ticket-card__title">桂乐淘·福利惊爆区</Text>
|
||||||
|
<ArrowRight className={'text-gray-50'} size={16} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/*分类Tabs*/}
|
||||||
<ScrollView className="home-tabs" scrollX enableFlex>
|
<ScrollView className="home-tabs" scrollX enableFlex>
|
||||||
<View className="home-tabs__inner">
|
<View className="home-tabs__inner">
|
||||||
{tabs.map((tab) => {
|
{tabs.map((tab) => {
|
||||||
@@ -306,7 +321,6 @@ function Home() {
|
|||||||
})}
|
})}
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
{/* 商品列表 */}
|
{/* 商品列表 */}
|
||||||
<View className="goods-grid">
|
<View className="goods-grid">
|
||||||
{visibleGoods.map((item) => (
|
{visibleGoods.map((item) => (
|
||||||
@@ -356,6 +370,7 @@ function Home() {
|
|||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const UserFooter = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={'text-center py-4 w-full text-gray-300'} onClick={onLoginByPhone}>
|
<div className={'text-center py-4 w-full text-gray-300'} onClick={onLoginByPhone}>
|
||||||
<div className={'text-xs text-gray-400 py-1'}>当前版本:{Version}</div>
|
{/*<div className={'text-xs text-gray-400 py-1'}>当前版本:{Version}</div>*/}
|
||||||
{/*<div className={'text-xs text-gray-400 py-1'}>Copyright © { new Date().getFullYear() } {Copyright}</div>*/}
|
{/*<div className={'text-xs text-gray-400 py-1'}>Copyright © { new Date().getFullYear() } {Copyright}</div>*/}
|
||||||
<div className={'text-xs text-gray-400 py-1'}>{Copyright}</div>
|
<div className={'text-xs text-gray-400 py-1'}>{Copyright}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ const DealerIndex: React.FC = () => {
|
|||||||
{dealerUser && (
|
{dealerUser && (
|
||||||
<View className="mx-4 -mt-6 rounded-xl p-4 relative z-10" style={cardGradients.elevated}>
|
<View className="mx-4 -mt-6 rounded-xl p-4 relative z-10" style={cardGradients.elevated}>
|
||||||
<View className="mb-4">
|
<View className="mb-4">
|
||||||
<Text className="font-semibold text-gray-800">工资统计</Text>
|
<Text className="font-semibold text-gray-800">配送提成</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="grid grid-cols-3 gap-3">
|
<View className="grid grid-cols-3 gap-3">
|
||||||
<View className="text-center p-3 rounded-lg flex flex-col" style={{
|
<View className="text-center p-3 rounded-lg flex flex-col" style={{
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
|
||||||
|
.goods-grid {
|
||||||
|
margin-top: 18rpx;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 18rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card {
|
||||||
|
border-radius: 22rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 18rpx 36rpx rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__imgWrap {
|
||||||
|
padding: 18rpx 18rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__img {
|
||||||
|
width: 100%;
|
||||||
|
height: 280rpx;
|
||||||
|
border-radius: 18rpx;
|
||||||
|
background: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__body {
|
||||||
|
padding: 18rpx 18rpx 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__title {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1c1c1c;
|
||||||
|
min-height: 72rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__meta {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__sold {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #9a9a9a;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__price {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 4rpx;
|
||||||
|
color: #27c86b;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__priceUnit {
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__priceValue {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__actions {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
display: flex;
|
||||||
|
gap: 14rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 64rpx;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__btn--ghost {
|
||||||
|
border: 2rpx solid rgba(32, 194, 106, 0.7);
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__btn--primary {
|
||||||
|
background: linear-gradient(90deg, #24d34c 0%, #6df09a 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__btnText {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #18b85a;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-card__btnText--primary {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buy-btn{
|
||||||
|
height: 70px;
|
||||||
|
background: linear-gradient(to bottom, #1cd98a, #24ca94);
|
||||||
|
border-radius: 100px;
|
||||||
|
color: #ffffff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
.cart-icon{
|
||||||
|
background: linear-gradient(to bottom, #bbe094, #4ee265);
|
||||||
|
border-radius: 100px 0 0 100px;
|
||||||
|
height: 70px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,51 +1,57 @@
|
|||||||
import {Image} from '@nutui/nutui-react-taro'
|
import {Image} from '@nutui/nutui-react-taro'
|
||||||
import {Share} from '@nutui/icons-react-taro'
|
|
||||||
import {View, Text} from '@tarojs/components'
|
import {View, Text} from '@tarojs/components'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import './GoodsList.scss'
|
import './GoodsList.scss'
|
||||||
|
import {ShopGoods} from "@/api/shop/shopGoods/model";
|
||||||
|
|
||||||
|
|
||||||
const GoodsList = (props: any) => {
|
const GoodsList = (props: any) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<View className={'py-3'}>
|
<View className={'p-3'}>
|
||||||
<View className={'flex flex-col justify-between items-center rounded-lg px-2'}>
|
|
||||||
{props.data?.map((item: any, index: number) => {
|
<View className="goods-grid">
|
||||||
return (
|
{props.data?.map((item: ShopGoods) => (
|
||||||
<View key={index} className={'flex flex-col rounded-lg bg-white shadow-sm w-full mb-5'}>
|
<View key={item.goodsId} className="goods-card">
|
||||||
<Image src={item.image} mode={'aspectFit'} lazyLoad={false}
|
<View className="goods-card__imgWrap">
|
||||||
radius="10px 10px 0 0" height="180"
|
<Image
|
||||||
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}/>
|
className="goods-card__img"
|
||||||
<View className={'flex flex-col p-2 rounded-lg'}>
|
src={item.image || ''}
|
||||||
<View>
|
mode="aspectFill"
|
||||||
<View className={'car-no text-sm'}>{item.name}</View>
|
width="100%"
|
||||||
<View className={'flex justify-between text-xs py-1'}>
|
height="280rpx"
|
||||||
<Text className={'text-orange-500'}>{item.comments}</Text>
|
radius="18rpx"
|
||||||
<Text className={'text-gray-400'}>已售 {item.sales}</Text>
|
lazyLoad={false}
|
||||||
|
onClick={() =>
|
||||||
|
Taro.navigateTo({ url: `/shop/goodsDetail/index?id=${item.goodsId}` })
|
||||||
|
}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View className={'flex justify-between items-center py-2'}>
|
|
||||||
<View className={'flex text-red-500 text-xl items-baseline'}>
|
<View className="goods-card__body">
|
||||||
<Text className={'text-xs'}>¥</Text>
|
<Text className="goods-card__title">{item.name}</Text>
|
||||||
<Text className={'font-bold text-2xl'}>{item.price}</Text>
|
<View className="goods-card__meta">
|
||||||
<Text className={'text-xs px-1'}>会员价</Text>
|
<Text className="goods-card__sold">已购:{item.sales || 0}人</Text>
|
||||||
<Text className={'text-xs text-gray-400 line-through'}>¥{item.salePrice}</Text>
|
<View className="goods-card__price">
|
||||||
|
<Text className="goods-card__priceUnit">¥</Text>
|
||||||
|
<Text className="goods-card__priceValue">{item.buyingPrice}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className={'buy-btn'}>
|
|
||||||
<View className={'cart-icon'}>
|
|
||||||
<Share size={20} className={'mx-4 mt-2'}
|
|
||||||
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}/>
|
|
||||||
</View>
|
</View>
|
||||||
<View className={'text-white pl-4 pr-5'}
|
|
||||||
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}>购买
|
<View className="goods-card__actions">
|
||||||
|
<View
|
||||||
|
className="goods-card__btn goods-card__btn--primary"
|
||||||
|
onClick={() =>
|
||||||
|
Taro.navigateTo({ url: `/shop/goodsDetail/index?id=${item.goodsId}` })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text className="goods-card__btnText goods-card__btnText--primary">立即购买</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
))}
|
||||||
</View>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import GoodsList from './components/GoodsList'
|
import GoodsList from './components/GoodsList'
|
||||||
import {useShareAppMessage} from "@tarojs/taro"
|
import {useShareAppMessage} from "@tarojs/taro"
|
||||||
import {Loading} from '@nutui/nutui-react-taro'
|
import {Loading,Empty} from '@nutui/nutui-react-taro'
|
||||||
import {useEffect, useState} from "react"
|
import {useEffect, useState} from "react"
|
||||||
import {useRouter} from '@tarojs/taro'
|
import {useRouter} from '@tarojs/taro'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
@@ -21,7 +21,7 @@ function Category() {
|
|||||||
// 1.加载远程数据
|
// 1.加载远程数据
|
||||||
const id = Number(params.id)
|
const id = Number(params.id)
|
||||||
const nav = await getCmsNavigation(id)
|
const nav = await getCmsNavigation(id)
|
||||||
const shopGoods = await pageShopGoods({categoryId: id})
|
const shopGoods = await pageShopGoods({categoryId: id, status: 0})
|
||||||
|
|
||||||
// 2.处理业务逻辑
|
// 2.处理业务逻辑
|
||||||
setCategoryId(id)
|
setCategoryId(id)
|
||||||
@@ -59,6 +59,12 @@ function Category() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(list.length == 0){
|
||||||
|
return (
|
||||||
|
<Empty description="暂无数据"/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={'flex flex-col'}>
|
<div className={'flex flex-col'}>
|
||||||
|
|||||||
@@ -430,6 +430,7 @@ const OrderConfirm = () => {
|
|||||||
* 统一支付入口
|
* 统一支付入口
|
||||||
*/
|
*/
|
||||||
const onPay = async (goods: ShopGoods) => {
|
const onPay = async (goods: ShopGoods) => {
|
||||||
|
let skipFinallyResetPayLoading = false
|
||||||
try {
|
try {
|
||||||
setPayLoading(true)
|
setPayLoading(true)
|
||||||
|
|
||||||
@@ -603,6 +604,29 @@ const OrderConfirm = () => {
|
|||||||
// })
|
// })
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const message = String(error?.message || '')
|
const message = String(error?.message || '')
|
||||||
|
const isUserCancelPay =
|
||||||
|
message.includes('用户取消支付') ||
|
||||||
|
message.includes('取消支付') ||
|
||||||
|
message.toLowerCase().includes('requestpayment:fail cancel') ||
|
||||||
|
message.toLowerCase().includes('cancel')
|
||||||
|
|
||||||
|
// 用户取消支付:跳转到待付款列表,方便继续支付
|
||||||
|
if (isUserCancelPay) {
|
||||||
|
skipFinallyResetPayLoading = true
|
||||||
|
setPayLoading(false)
|
||||||
|
const url = '/user/order/order?statusFilter=0'
|
||||||
|
try {
|
||||||
|
await Taro.redirectTo({ url })
|
||||||
|
} catch (_e) {
|
||||||
|
try {
|
||||||
|
await Taro.navigateTo({ url })
|
||||||
|
} catch (_e2) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const isOutOfDeliveryRange =
|
const isOutOfDeliveryRange =
|
||||||
message.includes('不在配送范围') ||
|
message.includes('不在配送范围') ||
|
||||||
message.includes('配送范围') ||
|
message.includes('配送范围') ||
|
||||||
@@ -632,8 +656,10 @@ const OrderConfirm = () => {
|
|||||||
Taro.showToast({ title: message || '支付失败,请重试', icon: 'none' })
|
Taro.showToast({ title: message || '支付失败,请重试', icon: 'none' })
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
if (!skipFinallyResetPayLoading) {
|
||||||
setPayLoading(false)
|
setPayLoading(false)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 统一的数据加载函数
|
// 统一的数据加载函数
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {Cell, CellGroup, Image, Space, Button, Dialog} from '@nutui/nutui-react-
|
|||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import {View} from '@tarojs/components'
|
import {View} from '@tarojs/components'
|
||||||
import {ShopOrder} from "@/api/shop/shopOrder/model";
|
import {ShopOrder} from "@/api/shop/shopOrder/model";
|
||||||
import {getShopOrder, updateShopOrder, refundShopOrder} from "@/api/shop/shopOrder";
|
import {getShopOrder, updateShopOrder} from "@/api/shop/shopOrder";
|
||||||
import {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
|
import {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
|
||||||
import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model";
|
import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
@@ -69,7 +69,7 @@ const OrderDetail = () => {
|
|||||||
Taro.showLoading({ title: '提交中...' })
|
Taro.showLoading({ title: '提交中...' })
|
||||||
|
|
||||||
// 退款相关操作使用退款接口:PUT /api/shop/shop-order/refund
|
// 退款相关操作使用退款接口:PUT /api/shop/shop-order/refund
|
||||||
await refundShopOrder({
|
await updateShopOrder({
|
||||||
orderId: order.orderId,
|
orderId: order.orderId,
|
||||||
refundMoney: order.payPrice || order.totalPrice,
|
refundMoney: order.payPrice || order.totalPrice,
|
||||||
orderStatus: 7
|
orderStatus: 7
|
||||||
|
|||||||
@@ -104,6 +104,10 @@ interface OrderListProps {
|
|||||||
baseParams?: ShopOrderParam;
|
baseParams?: ShopOrderParam;
|
||||||
// 只读模式:隐藏“支付/取消/确认收货/退款”等用户操作按钮
|
// 只读模式:隐藏“支付/取消/确认收货/退款”等用户操作按钮
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
|
// 是否自动取消“支付已过期”的待支付订单(仅 user 模式生效)
|
||||||
|
autoCancelExpired?: boolean;
|
||||||
|
// 支付超时时间(小时),默认 24 小时
|
||||||
|
paymentTimeoutHours?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function OrderList(props: OrderListProps) {
|
function OrderList(props: OrderListProps) {
|
||||||
@@ -111,6 +115,8 @@ function OrderList(props: OrderListProps) {
|
|||||||
const pageRef = useRef(1)
|
const pageRef = useRef(1)
|
||||||
const [hasMore, setHasMore] = useState(true)
|
const [hasMore, setHasMore] = useState(true)
|
||||||
const [payingOrderId, setPayingOrderId] = useState<number | null>(null)
|
const [payingOrderId, setPayingOrderId] = useState<number | null>(null)
|
||||||
|
const autoCanceledOrderIdsRef = useRef<Set<number>>(new Set())
|
||||||
|
const autoCancelRunningRef = useRef(false)
|
||||||
// 根据传入的statusFilter设置初始tab索引
|
// 根据传入的statusFilter设置初始tab索引
|
||||||
const getInitialTabIndex = () => {
|
const getInitialTabIndex = () => {
|
||||||
if (props.searchParams?.statusFilter !== undefined) {
|
if (props.searchParams?.statusFilter !== undefined) {
|
||||||
@@ -138,6 +144,26 @@ function OrderList(props: OrderListProps) {
|
|||||||
return Number.isFinite(n) ? n : undefined;
|
return Number.isFinite(n) ? n : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const parseTime = (raw: any): dayjs.Dayjs | null => {
|
||||||
|
const text = String(raw ?? '').trim();
|
||||||
|
if (!text) return null;
|
||||||
|
const t = /^\d+$/.test(text)
|
||||||
|
? dayjs(Number(text) < 1e12 ? Number(text) * 1000 : Number(text))
|
||||||
|
: dayjs(text);
|
||||||
|
return t.isValid() ? t : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isOrderPaymentExpiredSafe = (order: ShopOrder, timeoutHours: number) => {
|
||||||
|
if (order.payStatus) return false;
|
||||||
|
if (toNum(order.orderStatus) === 2) return false;
|
||||||
|
|
||||||
|
const expiration = parseTime(order.expirationTime);
|
||||||
|
if (expiration) return dayjs().isAfter(expiration);
|
||||||
|
|
||||||
|
if (order.createTime) return isPaymentExpired(order.createTime, timeoutHours);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
// “已完成”应以订单状态为准;不要用商品ID等字段推断完成态,否则会造成 Tab(待发货/待收货) 与状态文案不同步
|
// “已完成”应以订单状态为准;不要用商品ID等字段推断完成态,否则会造成 Tab(待发货/待收货) 与状态文案不同步
|
||||||
const isOrderCompleted = (order: ShopOrder) => toNum(order.orderStatus) === 1;
|
const isOrderCompleted = (order: ShopOrder) => toNum(order.orderStatus) === 1;
|
||||||
|
|
||||||
@@ -249,23 +275,81 @@ function OrderList(props: OrderListProps) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await pageShopOrder(searchConditions);
|
const timeoutHours = typeof props.paymentTimeoutHours === 'number' ? props.paymentTimeoutHours : 24;
|
||||||
|
const canAutoCancelExpired =
|
||||||
|
!!props.autoCancelExpired &&
|
||||||
|
(!props.mode || props.mode === 'user') &&
|
||||||
|
!props.readOnly;
|
||||||
|
const isPendingPayList = statusParams.statusFilter === 0;
|
||||||
|
|
||||||
if (res?.list && res?.list.length > 0) {
|
const fetchOrders = async () => pageShopOrder(searchConditions);
|
||||||
|
|
||||||
|
let res = await fetchOrders();
|
||||||
|
let incoming = (res?.list || []) as ShopOrder[];
|
||||||
|
let rawIncomingLength = incoming.length;
|
||||||
|
|
||||||
|
// 自动取消“支付已过期”的待支付订单(避免用户看到一堆不可支付的过期单)
|
||||||
|
if (canAutoCancelExpired && incoming.length && !autoCancelRunningRef.current) {
|
||||||
|
const expiredToCancel = incoming
|
||||||
|
.filter(o => !!o?.orderId)
|
||||||
|
.filter(o => !autoCanceledOrderIdsRef.current.has(o.orderId as number))
|
||||||
|
.filter(o => isOrderPaymentExpiredSafe(o, timeoutHours));
|
||||||
|
|
||||||
|
if (expiredToCancel.length) {
|
||||||
|
autoCancelRunningRef.current = true;
|
||||||
|
const justCanceled = new Set<number>();
|
||||||
|
try {
|
||||||
|
// 单次最多处理 20 笔,避免接口风暴
|
||||||
|
for (const order of expiredToCancel.slice(0, 20)) {
|
||||||
|
try {
|
||||||
|
await updateShopOrder({ orderId: order.orderId, orderStatus: 2 });
|
||||||
|
autoCanceledOrderIdsRef.current.add(order.orderId as number);
|
||||||
|
justCanceled.add(order.orderId as number);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('自动取消过期订单失败:', order?.orderId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
autoCancelRunningRef.current = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (justCanceled.size > 0) {
|
||||||
|
if (resetPage) {
|
||||||
|
// resetPage 时重新拉取一次,确保列表状态与服务端一致
|
||||||
|
res = await fetchOrders();
|
||||||
|
incoming = (res?.list || []) as ShopOrder[];
|
||||||
|
rawIncomingLength = incoming.length;
|
||||||
|
Taro.showToast({ title: '已自动取消过期订单', icon: 'none' });
|
||||||
|
} else {
|
||||||
|
// loadMore 时不重新拉取,避免破坏滚动;仅在本地列表中做最小同步
|
||||||
|
if (isPendingPayList) {
|
||||||
|
incoming = incoming.filter(o => !justCanceled.has(o.orderId as number));
|
||||||
|
} else {
|
||||||
|
incoming = incoming.map(o => (
|
||||||
|
justCanceled.has(o.orderId as number) ? { ...o, orderStatus: 2 } : o
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawIncomingLength > 0) {
|
||||||
// 订单分页接口已返回 orderGoods:列表直接使用该字段
|
// 订单分页接口已返回 orderGoods:列表直接使用该字段
|
||||||
const incoming = res.list as ShopOrder[];
|
|
||||||
|
|
||||||
// 使用函数式更新避免依赖 list
|
// 使用函数式更新避免依赖 list
|
||||||
setList(prevList => {
|
if (incoming.length > 0) {
|
||||||
const newList = resetPage ? incoming : (prevList || []).concat(incoming);
|
setList(prevList => (resetPage ? incoming : (prevList || []).concat(incoming)));
|
||||||
return newList;
|
} else {
|
||||||
});
|
// 本页数据全部被自动取消过滤掉:不清空历史列表,仅保持现状
|
||||||
|
setList(prevList => (resetPage ? [] : prevList));
|
||||||
|
}
|
||||||
|
|
||||||
// 正确判断是否还有更多数据
|
// 正确判断是否还有更多数据(以服务端返回条数为准)
|
||||||
const hasMoreData = incoming.length >= 10; // 假设每页10条数据
|
const hasMoreData = rawIncomingLength >= 10; // 假设每页10条数据
|
||||||
setHasMore(hasMoreData);
|
setHasMore(hasMoreData);
|
||||||
} else {
|
} else {
|
||||||
setList(prevList => resetPage ? [] : prevList);
|
// 服务端已无更多数据
|
||||||
|
setList(prevList => (resetPage ? [] : prevList));
|
||||||
setHasMore(false);
|
setHasMore(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +365,7 @@ function OrderList(props: OrderListProps) {
|
|||||||
icon: 'none'
|
icon: 'none'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [tapIndex, props.searchParams]); // 移除 list/page 依赖,避免useEffect触发循环
|
}, [tapIndex, props.searchParams, props.baseParams, props.mode, props.readOnly, props.autoCancelExpired, props.paymentTimeoutHours]); // 移除 list/page 依赖,避免useEffect触发循环
|
||||||
|
|
||||||
const reloadMore = useCallback(async () => {
|
const reloadMore = useCallback(async () => {
|
||||||
if (loading || !hasMore) return; // 防止重复加载
|
if (loading || !hasMore) return; // 防止重复加载
|
||||||
@@ -820,12 +904,12 @@ function OrderList(props: OrderListProps) {
|
|||||||
<Button size={'small'} onClick={(e) => {
|
<Button size={'small'} onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
void cancelOrder(item);
|
void cancelOrder(item);
|
||||||
}}>取消订单</Button>
|
}}>取消</Button>
|
||||||
{(!item.createTime || !isPaymentExpired(item.createTime, 24)) && (
|
{(!item.createTime || !isPaymentExpired(item.createTime, 24)) && (
|
||||||
<Button size={'small'} type="primary" onClick={(e) => {
|
<Button size={'small'} type="primary" onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
void payOrder(item);
|
void payOrder(item);
|
||||||
}}>立即支付</Button>
|
}}>继续支付</Button>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ function Order() {
|
|||||||
onReload={() => reload(searchParams)}
|
onReload={() => reload(searchParams)}
|
||||||
searchParams={searchParams}
|
searchParams={searchParams}
|
||||||
showSearch={showSearch}
|
showSearch={showSearch}
|
||||||
|
autoCancelExpired
|
||||||
onSearchParamsChange={(newParams) => {
|
onSearchParamsChange={(newParams) => {
|
||||||
console.log('父组件接收到searchParams变化:', newParams);
|
console.log('父组件接收到searchParams变化:', newParams);
|
||||||
setSearchParams(newParams);
|
setSearchParams(newParams);
|
||||||
|
|||||||
Reference in New Issue
Block a user