refactor(app): 重构用户中心和订单相关页面

-调整了 app.config.ts 中的页面路径和顺序
- 移除了 article 页面
- 重构了 cart、find、order 和 user 页面的布局和功能
- 优化了导航栏和订单状态的显示逻辑
- 统一了页面样式和图标使用
This commit is contained in:
2025-08-11 21:03:27 +08:00
parent 87b83b4d2c
commit f69decdf4d
23 changed files with 206 additions and 354 deletions

View File

@@ -1,3 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '学习'
})

View File

@@ -1,50 +0,0 @@
import {useEffect, useState} from "react";
import {ArrowRight} from '@nutui/icons-react-taro'
import {pageCmsArticle} from "@/api/cms/cmsArticle";
import {CmsArticle} from "@/api/cms/cmsArticle/model";
import Taro from '@tarojs/taro'
/**
* 文章终极列表
* @constructor
*/
const Article = () => {
// const {params} = useRouter();
// const [categoryId, setCategoryId] = useState<number>(3494)
const [list, setList] = useState<CmsArticle[]>([])
const reload = () => {
// if (params.id) {
// setCategoryId(Number(params.id))
// }
pageCmsArticle({}).then(res => {
if (res?.list) {
setList(res?.list)
}
})
}
useEffect(() => {
reload()
}, [])
return (
<div className={'px-3 mt-4 mb-10'}>
<div className={'flex flex-col justify-between items-center bg-white rounded-lg p-4'}>
<div className={'bg-white w-full'}>
{
list.map((item, index) => {
return (
<div key={index} className={'flex justify-between items-center py-2'} onClick={() => Taro.navigateTo({url: `/cms/help?id=${item.articleId}`}) }>
<div className={'text-sm'}>{item.title}</div>
<ArrowRight color={'#cccccc'} size={18} />
</div>
)
})
}
</div>
</div>
</div>
)
}
export default Article

View File

@@ -7,7 +7,8 @@ import {
InputNumber,
Button,
Empty,
Divider
Divider,
ConfigProvider
} from '@nutui/nutui-react-taro';
import {ArrowLeft, Del, Shopping} from '@nutui/icons-react-taro';
import {View} from '@tarojs/components';
@@ -26,6 +27,16 @@ function Cart() {
clearCart
} = useCart();
// InputNumber 主题配置
const customTheme = {
nutuiInputnumberButtonWidth: '28px',
nutuiInputnumberButtonHeight: '28px',
nutuiInputnumberInputWidth: '40px',
nutuiInputnumberInputHeight: '28px',
nutuiInputnumberInputBorderRadius: '4px',
nutuiInputnumberButtonBorderRadius: '4px',
}
useShareTimeline(() => {
return {
title: '购物车 - 网宿小店'
@@ -138,11 +149,52 @@ function Cart() {
}
}, [selectedItems, cartItems]);
if (cartItems.length === 0) {
return (
<>
<NavBar
fixed={true}
style={{marginTop: `${statusBarHeight}px`}}
left={<ArrowLeft onClick={() => Taro.navigateBack()}/>}
right={
cartItems.length > 0 && (
<Button
size="small"
type="primary"
fill="none"
onClick={() => {
Taro.showModal({
title: '确认清空',
content: '确定要清空购物车吗?',
success: (res) => {
if (res.confirm) {
clearCart();
setSelectedItems([]);
}
}
});
}}
>
</Button>
)
}
>
<span className="text-lg">({cartCount})</span>
</NavBar>
<Empty
description="购物车空空如也"
actions={[{ text: '去逛逛' }]}
style={{ marginTop: `${statusBarHeight + 50}px` }}
onClick={() => Taro.switchTab({ url: '/pages/index/index' })}
/>
</>
)
}
return (
<>
<View style={{backgroundColor: '#f6f6f6', height: `${statusBarHeight}px`}}
className="fixed z-10 top-0 w-full"></View>
className="fixed z-10 top-0 w-full"></View>
<NavBar
fixed={true}
style={{marginTop: `${statusBarHeight}px`}}
@@ -224,16 +276,14 @@ function Cart() {
<span className={'text-xs'}></span>
<span className={'font-bold text-lg'}>{item.price}</span>
</View>
<View className="flex items-center gap-2">
<ConfigProvider theme={customTheme}>
<InputNumber
value={item.quantity}
min={1}
max={99}
onChange={(value) => handleQuantityChange(item.goodsId, Number(value))}
className="w-24"
/>
<Del className={'text-red-500'} size={16} onClick={() => handleRemoveItem(item.goodsId)}/>
</View>
</ConfigProvider>
<Del className={'text-red-500'} size={14} onClick={() => handleRemoveItem(item.goodsId)}/>
</View>
</View>
</View>

View File

@@ -2,7 +2,9 @@ import {useEffect, useState} from "react";
import Taro from '@tarojs/taro';
import {pageCmsArticle} from "@/api/cms/cmsArticle";
import {CmsArticle} from "@/api/cms/cmsArticle/model";
import {NavBar} from '@nutui/nutui-react-taro'
import {NavBar, Cell} from '@nutui/nutui-react-taro';
import {Text} from '@tarojs/components';
import {ArrowRight, ImageRectangle, Coupon, Follow} from '@nutui/icons-react-taro'
import './find.scss'
/**
@@ -17,7 +19,7 @@ const Find = () => {
const reload = async () => {
setLoading(true)
const article = await pageCmsArticle({categoryId: 4289, status: 0})
if(article){
if (article) {
setList(article?.list)
setLoading(false)
}
@@ -38,27 +40,33 @@ const Find = () => {
<>
{loading && (<div></div>)}
<NavBar
fixed={true}
fixed={false}
style={{marginTop: `${statusBarHeight}px`, backgroundColor: 'transparent'}}
onBackClick={() => {
}}
left={
<>
<div className={'flex justify-between items-center w-full'}>
</div>
{/*<SearchBar shape="round" maxLength={5} style={{paddingLeft: '1px'}}/>*/}
{/*<div className={'flex flex-col text-center justify-center items-center'}>*/}
{/* <Filter size={14}/>*/}
{/* <div className={'text-xs text-gray-600 whitespace-nowrap'}>筛选</div>*/}
{/*</div>*/}
</>
}
>
<span></span>
</NavBar>
{list && (
<>
<Cell title={
<div style={{display: 'inline-flex', alignItems: 'center'}}>
<ImageRectangle size={18}/>
<Text className={'pl-3'} style={{ fontSize: '16px'}}></Text>
</div>
} extra={<ArrowRight color="#cccccc" size={18}/>}/>
<Cell title={
<div style={{display: 'inline-flex', alignItems: 'center'}}>
<Coupon size={18}/>
<span className={'pl-3'} style={{ fontSize: '16px'}}></span>
</div>
} extra={<ArrowRight color="#cccccc" size={18}/>}/>
<Cell title={
<div style={{display: 'inline-flex', alignItems: 'center'}}>
<Follow size={18}/>
<span className={'pl-3'} style={{ fontSize: '16px'}}></span>
</div>
} extra={<ArrowRight color="#cccccc" size={18}/>}/>
</>
)}
</>

View File

@@ -1,391 +0,0 @@
import {Avatar, Cell, Space, Tabs, Button, TabPane, Image} from '@nutui/nutui-react-taro'
import {useEffect, useState, CSSProperties} from "react";
import {View} from '@tarojs/components'
import Taro from '@tarojs/taro';
import {InfiniteLoading} from '@nutui/nutui-react-taro'
import dayjs from "dayjs";
import {pageShopOrder, removeShopOrder, updateShopOrder} from "@/api/shop/shopOrder";
import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model";
import {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model";
import {copyText} from "@/utils/common";
const getInfiniteUlStyle = (showSearch: boolean = false): CSSProperties => ({
marginTop: showSearch ? '65px' : '44px', // 如果显示搜索框,增加更多的上边距
height: showSearch ? '75vh' : '82vh', // 相应调整高度
width: '100%',
padding: '0',
overflowY: 'auto',
overflowX: 'hidden',
boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)',
})
// 统一的订单状态标签配置,与后端 statusFilter 保持一致
const tabs = [
{
index: 0,
key: '全部',
title: '全部',
description: '所有订单',
statusFilter: undefined // 不传statusFilter显示所有订单
},
{
index: 1,
key: '待付款',
title: '待付款',
description: '等待付款的订单',
statusFilter: 0 // 对应后端pay_status = false
},
{
index: 2,
key: '待发货',
title: '待发货',
description: '已付款待发货的订单',
statusFilter: 1 // 对应后端pay_status = true AND delivery_status = 10
},
{
index: 3,
key: '待收货',
title: '待收货',
description: '已发货待收货的订单',
statusFilter: 3 // 对应后端pay_status = true AND delivery_status = 20
},
{
index: 4,
key: '已完成',
title: '已完成',
description: '已完成的订单',
statusFilter: 5 // 对应后端order_status = 1
},
{
index: 5,
key: '已取消',
title: '已取消',
description: '已取消/退款的订单',
statusFilter: 6 // 对应后端order_status = 6 (已退款)
}
]
// 扩展订单接口,包含商品信息
interface OrderWithGoods extends ShopOrder {
orderGoods?: ShopOrderGoods[];
}
interface OrderListProps {
data: ShopOrder[];
onReload?: () => void;
searchParams?: ShopOrderParam;
showSearch?: boolean;
}
function OrderList(props: OrderListProps) {
const [list, setList] = useState<OrderWithGoods[]>([])
const [page, setPage] = useState(1)
const [hasMore, setHasMore] = useState(true)
const [tapIndex, setTapIndex] = useState<string | number>(0)
const [loading, setLoading] = useState(false)
// 获取订单状态文本
const getOrderStatusText = (order: ShopOrder) => {
console.log(order,'order')
// 优先检查订单状态
if (order.orderStatus === 2) return '已取消';
if (order.orderStatus === 4) return '退款申请中';
if (order.orderStatus === 5) return '退款被拒绝';
if (order.orderStatus === 6) return '退款成功';
if (order.orderStatus === 7) return '客户端申请退款';
// 检查支付状态 (payStatus为boolean类型false/0表示未付款true/1表示已付款)
if (!order.payStatus) return '等待买家付款';
// 已付款后检查发货状态
if (order.deliveryStatus === 10) return '待发货';
if (order.deliveryStatus === 20) return '待收货';
if (order.deliveryStatus === 30) return '已收货';
// 最后检查订单完成状态
if (order.orderStatus === 1) return '已完成';
if (order.orderStatus === 0) return '未使用';
return '未知状态';
};
// 获取订单状态颜色
const getOrderStatusColor = (order: ShopOrder) => {
// 优先检查订单状态
if (order.orderStatus === 2) return 'text-gray-500'; // 已取消
if (order.orderStatus === 4) return 'text-orange-500'; // 退款申请中
if (order.orderStatus === 5) return 'text-red-500'; // 退款被拒绝
if (order.orderStatus === 6) return 'text-green-500'; // 退款成功
if (order.orderStatus === 7) return 'text-orange-500'; // 客户端申请退款
// 检查支付状态
if (!order.payStatus) return 'text-orange-500'; // 等待买家付款
// 已付款后检查发货状态
if (order.deliveryStatus === 10) return 'text-blue-500'; // 待发货
if (order.deliveryStatus === 20) return 'text-purple-500'; // 待收货
if (order.deliveryStatus === 30) return 'text-green-500'; // 已收货
// 最后检查订单完成状态
if (order.orderStatus === 1) return 'text-green-600'; // 已完成
if (order.orderStatus === 0) return 'text-gray-500'; // 未使用
return 'text-gray-600'; // 默认颜色
};
// 使用后端统一的 statusFilter 进行筛选
const getOrderStatusParams = (index: string | number) => {
let params: ShopOrderParam = {};
// 添加用户ID过滤
params.userId = Taro.getStorageSync('UserId');
// 获取当前tab的statusFilter配置
const currentTab = tabs.find(tab => tab.index === Number(index));
if (currentTab && currentTab.statusFilter !== undefined) {
params.statusFilter = currentTab.statusFilter;
}
console.log(`Tab ${index} (${currentTab?.title}) 筛选参数:`, params);
return params;
};
const reload = async (resetPage = false) => {
setLoading(true);
const currentPage = resetPage ? 1 : page;
const statusParams = getOrderStatusParams(tapIndex);
const searchConditions = {
page: currentPage,
...statusParams,
...props.searchParams
};
console.log('订单筛选条件:', {
tapIndex,
statusParams,
searchConditions
});
try {
const res = await pageShopOrder(searchConditions);
let newList: OrderWithGoods[] = [];
if (res?.list && res?.list.length > 0) {
// 为每个订单获取商品信息
const ordersWithGoods = await Promise.all(
res.list.map(async (order) => {
try {
const orderGoods = await listShopOrderGoods({ orderId: order.orderId });
return {
...order,
orderGoods: orderGoods || []
};
} catch (error) {
console.error('获取订单商品失败:', error);
return {
...order,
orderGoods: []
};
}
})
);
// 合并数据
newList = resetPage ? ordersWithGoods : list?.concat(ordersWithGoods);
setHasMore(true);
} else {
newList = [];
setHasMore(false);
}
setList(newList || []);
setPage(currentPage);
setLoading(false);
} catch (error) {
console.error('加载订单失败:', error);
setLoading(false);
}
};
const reloadMore = async () => {
setPage(page + 1);
reload();
};
// 确认收货
const confirmReceive = async (order: ShopOrder) => {
try {
await updateShopOrder({
...order,
deliveryStatus: 30, // 已收货
orderStatus: 1 // 已完成
});
Taro.showToast({
title: '确认收货成功',
});
reload(true); // 重新加载列表
props.onReload?.(); // 通知父组件刷新
} catch (error) {
Taro.showToast({
title: '确认收货失败',
});
}
};
// 取消订单
const cancelOrder = async (order: ShopOrder) => {
try {
await removeShopOrder(order.orderId);
Taro.showToast({
title: '订单已删除',
});
reload(true); // 重新加载列表
props.onReload?.(); // 通知父组件刷新
} catch (error) {
console.error('取消订单失败:', error);
Taro.showToast({
title: '取消订单失败',
});
}
};
useEffect(() => {
reload(true); // 首次加载或tab切换时重置页码
}, [tapIndex]); // 监听tapIndex变化
useEffect(() => {
reload(true); // 搜索参数变化时重置页码
}, [props.searchParams]); // 监听搜索参数变化
return (
<>
<Tabs
align={'left'}
className={'fixed left-0'}
style={{
top: '44px',
zIndex: 998,
borderBottom: '1px solid #e5e5e5'
}}
tabStyle={{
backgroundColor: '#ffffff',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
}}
value={tapIndex}
onChange={(paneKey) => {
console.log('Tab切换到:', paneKey, '对应状态:', tabs[paneKey]?.title);
setTapIndex(paneKey)
}}
>
{
tabs?.map((item, index) => {
return (
<TabPane
key={index}
title={loading && tapIndex === index ? `${item.title}...` : item.title}
></TabPane>
)
})
}
</Tabs>
<div style={getInfiniteUlStyle(props.showSearch)} id="scroll">
<InfiniteLoading
target="scroll"
hasMore={hasMore}
onLoadMore={reloadMore}
onScroll={() => {
}}
onScrollToUpper={() => {
}}
loadingText={
<>
</>
}
loadMoreText={
<>
</>
}
>
{list?.map((item, index) => {
return (
<Cell key={index} style={{padding: '16px'}} onClick={() => Taro.navigateTo({url: `/shop/orderDetail/index?orderId=${item.orderId}`})}>
<Space direction={'vertical'} className={'w-full flex flex-col'}>
<View className={'order-no flex justify-between'}>
<View className={'text-gray-600 font-bold text-sm'}
onClick={(e) => {e.stopPropagation(); copyText(`${item.orderNo}`)}}>{item.orderNo}</View>
<View className={`${getOrderStatusColor(item)} font-medium`}>{getOrderStatusText(item)}</View>
</View>
<div
className={'create-time text-gray-400 text-xs'}>{dayjs(item.createTime).format('YYYY年MM月DD日 HH:mm:ss')}</div>
{/* 商品信息 */}
<div className={'goods-info'}>
{item.orderGoods && item.orderGoods.length > 0 ? (
item.orderGoods.map((goods, goodsIndex) => (
<div key={goodsIndex} className={'flex items-center mb-2'}>
<Image
src={goods.image || '/default-goods.png'}
width="50"
height="50"
lazyLoad={false}
className={'rounded'}
/>
<div className={'ml-2 flex-1'}>
<div className={'text-sm font-bold'}>{goods.goodsName}</div>
{goods.spec && <div className={'text-gray-500 text-xs'}>{goods.spec}</div>}
<div className={'text-gray-500 text-xs'}>{goods.totalNum}</div>
</div>
<div className={'text-sm'}>{goods.price}</div>
</div>
))
) : (
<div className={'flex items-center'}>
<Avatar
src='/default-goods.png'
size={'50'}
shape={'square'}
/>
<div className={'ml-2'}>
<div className={'text-sm'}>{item.title || '订单商品'}</div>
<div className={'text-gray-400 text-xs'}>{item.totalNum}</div>
</div>
</div>
)}
</div>
<div className={'w-full text-right'}>{item.payPrice}</div>
{/* 操作按钮 */}
<Space className={'btn flex justify-end'}>
{/* 待付款状态:显示取消订单和立即支付 */}
{(!item.payStatus) && item.orderStatus !== 2 && (
<Space>
<Button size={'small'} onClick={(e) => {e.stopPropagation(); cancelOrder(item)}}></Button>
<Button size={'small'} type="primary" onClick={(e) => {e.stopPropagation(); console.log('立即支付')}}></Button>
</Space>
)}
{/* 待收货状态:显示确认收货 */}
{item.deliveryStatus === 20 && (
<Button size={'small'} type="primary" onClick={(e) => {e.stopPropagation(); confirmReceive(item)}}></Button>
)}
{/* 已完成状态:显示申请退款 */}
{item.orderStatus === 1 && (
<Button size={'small'} onClick={(e) => {e.stopPropagation(); console.log('申请退款')}}>退</Button>
)}
{/* 退款相关状态的按钮可以在这里添加 */}
</Space>
</Space>
</Cell>
)
})}
</InfiniteLoading>
</div>
</>
)
}
export default OrderList

View File

@@ -1,47 +0,0 @@
.order-search {
.search-bar {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.filter-popup {
.grid {
display: grid;
}
.grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.gap-2 {
gap: 0.5rem;
}
.flex-wrap {
flex-wrap: wrap;
}
}
}
// 修复Radio按钮样式
.nut-radio {
&.nut-radio--button {
margin: 2px;
.nut-radio__label {
padding: 8px 12px;
border-radius: 4px;
font-size: 12px;
}
}
}
// 修复Input样式
.nut-input {
&.bg-transparent {
background: transparent !important;
}
&.border-none {
border: none !important;
}
}

View File

@@ -1,263 +0,0 @@
import React, { useState } from 'react';
import { View } from '@tarojs/components';
import {
Input,
Button,
Popup,
Cell,
CellGroup,
Radio,
Space
} from '@nutui/nutui-react-taro';
import { Search, Filter, Close } from '@nutui/icons-react-taro';
import { ShopOrderParam } from '@/api/shop/shopOrder/model';
import './OrderSearch.scss';
interface OrderSearchProps {
onSearch: (params: ShopOrderParam) => void;
onReset: () => void;
}
// 订单状态选项
const orderStatusOptions = [
{ text: '全部', value: '' },
{ text: '未使用', value: 0 },
{ text: '已完成', value: 1 },
{ text: '已取消', value: 2 },
{ text: '取消中', value: 3 },
{ text: '退款申请中', value: 4 },
{ text: '退款被拒绝', value: 5 },
{ text: '退款成功', value: 6 },
{ text: '客户端申请退款', value: 7 }
];
// 支付状态选项
const payStatusOptions = [
{ text: '全部', value: '' },
{ text: '未付款', value: 0 },
{ text: '已付款', value: 1 }
];
// 支付方式选项
const payTypeOptions = [
{ text: '全部', value: '' },
{ text: '余额支付', value: 0 },
{ text: '微信支付', value: 1 },
{ text: '会员卡支付', value: 2 },
{ text: '支付宝', value: 3 },
{ text: '现金', value: 4 },
{ text: 'POS机', value: 5 }
];
const OrderSearch: React.FC<OrderSearchProps> = ({ onSearch, onReset }) => {
const [showFilter, setShowFilter] = useState(false);
const [searchParams, setSearchParams] = useState<ShopOrderParam>({
keywords: '',
orderNo: '',
phone: '',
orderStatus: undefined,
payStatus: undefined,
payType: undefined
});
// 搜索关键词
const handleKeywordSearch = () => {
if (!searchParams.keywords?.trim()) {
return;
}
onSearch({ keywords: searchParams.keywords.trim() });
};
// 重置搜索条件
const handleReset = () => {
setSearchParams({
keywords: '',
orderNo: '',
phone: '',
orderStatus: undefined,
payStatus: undefined,
payType: undefined
});
onReset();
};
// 应用筛选条件
const handleFilter = () => {
const filterParams: ShopOrderParam = {};
if (searchParams.orderNo?.trim()) {
filterParams.orderNo = searchParams.orderNo.trim();
}
if (searchParams.phone?.trim()) {
filterParams.phone = searchParams.phone.trim();
}
if (searchParams.orderStatus !== undefined) {
filterParams.orderStatus = searchParams.orderStatus;
}
if (searchParams.payStatus !== undefined) {
filterParams.payStatus = searchParams.payStatus;
}
if (searchParams.payType !== undefined) {
filterParams.payType = searchParams.payType;
}
onSearch(filterParams);
setShowFilter(false);
};
return (
<View className="order-search">
{/* 搜索栏 */}
<View className="search-bar flex items-center p-3 bg-white">
<View className="flex-1 flex items-center bg-gray-100 rounded-full px-3 py-2">
<Search size={16} className="text-gray-400 mr-2" />
<Input
placeholder="搜索订单号、商品名称"
value={searchParams.keywords}
onChange={(value) => setSearchParams(prev => ({ ...prev, keywords: value }))}
onConfirm={handleKeywordSearch}
className="flex-1 bg-transparent border-none"
style={{ padding: 0 }}
/>
</View>
<Button
type="primary"
size={'large'}
className="ml-2"
onClick={handleKeywordSearch}
>
</Button>
<View
className="ml-2 p-2 flex items-center justify-center"
onClick={() => setShowFilter(true)}
>
<Filter size={18} className="text-gray-600" />
</View>
</View>
{/* 筛选弹窗 */}
<Popup
visible={showFilter}
position="right"
onClose={() => setShowFilter(false)}
style={{ width: '80%', height: '100%' }}
>
<View className="filter-popup h-full flex flex-col">
{/* 头部 */}
<View className="flex items-center justify-between p-4 border-b">
<View className="text-lg font-medium"></View>
<Close size={20} onClick={() => setShowFilter(false)} />
</View>
{/* 筛选内容 */}
<View className="flex-1 overflow-y-auto">
<CellGroup>
{/* 订单号 */}
<Cell>
<View className="w-full">
<View className="text-sm text-gray-600 mb-2"></View>
<Input
placeholder="请输入订单号"
value={searchParams.orderNo}
onChange={(value) => setSearchParams(prev => ({ ...prev, orderNo: value }))}
/>
</View>
</Cell>
{/* 手机号 */}
<Cell>
<View className="w-full">
<View className="text-sm text-gray-600 mb-2"></View>
<Input
placeholder="请输入手机号"
value={searchParams.phone}
onChange={(value) => setSearchParams(prev => ({ ...prev, phone: value }))}
/>
</View>
</Cell>
{/* 订单状态 */}
<Cell>
<View className="w-full">
<View className="text-sm text-gray-600 mb-2"></View>
<Radio.Group
value={searchParams.orderStatus}
onChange={(value) => setSearchParams(prev => ({ ...prev, orderStatus: Number(value) }))}
>
<View className="grid grid-cols-2 gap-2">
{orderStatusOptions.map((option) => (
<Radio key={option.value} value={option.value} shape="button">
{option.text}
</Radio>
))}
</View>
</Radio.Group>
</View>
</Cell>
{/* 支付状态 */}
<Cell>
<View className="w-full">
<View className="text-sm text-gray-600 mb-2"></View>
<Radio.Group
value={searchParams.payStatus}
onChange={(value) => setSearchParams(prev => ({ ...prev, payStatus: Number(value) }))}
>
<View className="flex flex-wrap gap-2">
{payStatusOptions.map((option) => (
<Radio key={option.value} value={option.value} shape="button">
{option.text}
</Radio>
))}
</View>
</Radio.Group>
</View>
</Cell>
{/* 支付方式 */}
<Cell>
<View className="w-full">
<View className="text-sm text-gray-600 mb-2"></View>
<Radio.Group
value={searchParams.payType}
onChange={(value) => setSearchParams(prev => ({ ...prev, payType: Number(value) }))}
>
<View className="grid grid-cols-2 gap-2">
{payTypeOptions.map((option) => (
<Radio key={option.value} value={option.value} shape="button">
{option.text}
</Radio>
))}
</View>
</Radio.Group>
</View>
</Cell>
</CellGroup>
</View>
{/* 底部按钮 */}
<View className="p-4 border-t">
<Space className="w-full">
<Button
className="flex-1"
onClick={handleReset}
>
</Button>
<Button
type="primary"
className="flex-1"
onClick={handleFilter}
>
</Button>
</Space>
</View>
</View>
</Popup>
</View>
);
};
export default OrderSearch;

View File

@@ -1,4 +0,0 @@
export default definePageConfig({
navigationBarTitleText: '订单列表',
navigationStyle: 'custom'
})

View File

@@ -1,72 +0,0 @@
page {
background: linear-gradient(to bottom, #f3f3f3, #f9fafb);
background-size: 100%;
}
.search-container {
transition: all 0.3s ease;
.nut-input {
background-color: #f8f9fa !important;
border: 1px solid #e5e5e5 !important;
border-radius: 4px !important;
&:focus {
border-color: #007bff !important;
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25) !important;
}
}
.nut-button {
border-radius: 4px !important;
&--primary {
background: linear-gradient(135deg, #007bff, #0056b3) !important;
border: none !important;
}
&--small {
padding: 6px 12px !important;
font-size: 12px !important;
}
}
}
// Tabs样式优化
.nut-tabs {
.nut-tabs__titles {
background: #ffffff !important;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
.nut-tabs__titles-item {
font-size: 14px !important;
font-weight: 500 !important;
&--active {
color: #007bff !important;
font-weight: 600 !important;
}
}
.nut-tabs__line {
background: #007bff !important;
height: 3px !important;
}
}
}
// 筛选提示样式
.filter-tip {
animation: slideDown 0.3s ease;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

View File

@@ -1,169 +0,0 @@
import {useState} from "react";
import Taro, {useDidShow} from '@tarojs/taro'
import {NavBar, Space, Empty, Button, ConfigProvider, Input} from '@nutui/nutui-react-taro'
import {Search, Filter} from '@nutui/icons-react-taro'
import { View } from '@tarojs/components';
import OrderList from "./components/OrderList";
// import OrderSearch from "./components/OrderSearch";
import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model";
import {pageShopOrder} from "@/api/shop/shopOrder";
import './order.scss'
function Order() {
const [statusBarHeight, setStatusBarHeight] = useState<number>()
const [list, setList] = useState<ShopOrder[]>([])
const [searchParams, setSearchParams] = useState<ShopOrderParam>({})
const [showSearch, setShowSearch] = useState(false)
const [searchKeyword, setSearchKeyword] = useState('')
const reload = async (params?: ShopOrderParam) => {
const searchConditions = {
userId: Taro.getStorageSync('UserId'),
...params
}
const orders = await pageShopOrder(searchConditions)
if (orders) {
setList(orders.list || [])
}
}
// 处理搜索
const handleSearch = (params: ShopOrderParam) => {
setSearchParams(params)
reload(params)
}
// 重置搜索
const handleResetSearch = () => {
setSearchParams({})
reload()
}
useDidShow(() => {
Taro.getSystemInfo({
success: (res) => {
setStatusBarHeight(res.statusBarHeight)
},
})
reload().then()
});
return (
<>
<View style={{ height: `${statusBarHeight}px`, backgroundColor: '#ffffff'}}></View>
<NavBar
fixed={true}
style={{marginTop: `${statusBarHeight}px`, backgroundColor: '#ffffff'}}
left={
<>
<div className={'flex justify-between items-center w-full'}>
<Space>
<Search
size={18}
className={'mx-1'}
onClick={() => setShowSearch(!showSearch)}
/>
<Filter
size={18}
className={'mx-1'}
onClick={() => setShowSearch(!showSearch)}
/>
</Space>
</div>
</>
}
>
<span></span>
</NavBar>
{/* 搜索组件 */}
{showSearch && (
<View
className="search-container bg-white p-3 shadow-sm"
style={{
position: 'fixed',
top: `${(statusBarHeight || 0) + 44}px`, // 导航栏高度通常是44px
left: 0,
right: 0,
zIndex: 999,
borderBottom: '1px solid #e5e5e5'
}}
>
<View className="flex items-center">
<View className="flex-1 mr-2">
<Input
placeholder="搜索订单号、商品名称"
value={searchKeyword}
onChange={setSearchKeyword}
onConfirm={() => {
if (searchKeyword.trim()) {
handleSearch({ keywords: searchKeyword.trim() });
}
}}
style={{
padding: '8px 12px',
border: '1px solid #e5e5e5',
borderRadius: '4px',
backgroundColor: '#f8f9fa'
}}
/>
</View>
<Space>
<Button
type="primary"
onClick={() => {
if (searchKeyword.trim()) {
handleSearch({ keywords: searchKeyword.trim() });
}
}}
>
</Button>
<Button
onClick={() => {
setSearchKeyword('');
handleResetSearch();
}}
>
</Button>
</Space>
</View>
</View>
)}
{/*暂无订单*/}
{list.length == 0 && (
<ConfigProvider>
<div className={'h-full flex flex-col justify-center items-center'} style={{
height: showSearch ? 'calc(100vh - 200px)' : 'calc(100vh - 150px)',
marginTop: showSearch ? '60px' : '0px'
}}>
<Empty
style={{
backgroundColor: 'transparent'
}}
description="您还没有订单哦"
/>
<Space>
<Button type="success" fill="dashed"
onClick={() => Taro.switchTab({url: '/pages/index/index'})}></Button>
</Space>
</div>
</ConfigProvider>
)}
{/*订单列表*/}
{
list.length > 0 && (
<OrderList
data={list}
onReload={() => reload(searchParams)}
searchParams={searchParams}
showSearch={showSearch}
/>
)
}
</>
);
}
export default Order;

View File

@@ -1,211 +0,0 @@
import {useEffect, useState} from 'react'
import {navigateTo} from '@tarojs/taro'
import Taro from '@tarojs/taro'
import {Button} from '@tarojs/components';
import {Image} from '@nutui/nutui-react-taro'
import {getUserInfo, getWxOpenId} from "@/api/layout";
import {TenantId} from "@/config/app";
import {User} from "@/api/system/user/model";
// import News from "./News";
import {myPageBszxBm} from "@/api/bszx/bszxBm";
import {listCmsNavigation} from "@/api/cms/cmsNavigation";
const OrderIcon = () => {
const [loading, setLoading] = useState(true)
const [isLogin, setIsLogin] = useState<boolean>(false)
const [userInfo, setUserInfo] = useState<User>()
const [bmLogs, setBmLogs] = useState<any>()
const [navItems, setNavItems] = useState<any>([])
/* 获取用户手机号 */
const handleGetPhoneNumber = ({detail}) => {
const {code, encryptedData, iv} = detail
Taro.login({
success: function () {
if (code) {
Taro.request({
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
method: 'POST',
data: {
code,
encryptedData,
iv,
notVerifyPhone: true,
refereeId: 0,
sceneType: 'save_referee',
tenantId: TenantId
},
header: {
'content-type': 'application/json',
TenantId
},
success: function (res) {
Taro.setStorageSync('access_token', res.data.data.access_token)
Taro.setStorageSync('UserId', res.data.data.user.userId)
setUserInfo(res.data.data.user)
Taro.setStorageSync('Phone', res.data.data.user.phone)
setIsLogin(true)
Taro.showToast({
title: '登录成功',
icon: 'success'
});
}
})
} else {
console.log('登录失败!')
}
}
})
}
const onLogin = (item: any, index: number) => {
if(!isLogin){
return navigateTo({url: `/pages/category/category?id=${item.navigationId}`})
}else {
// 报名链接
if(index == 0){
console.log(bmLogs,'bmLogs')
if(bmLogs && bmLogs.length > 0){
return navigateTo({url: `/bszx/bm-cert/bm-cert?id=${bmLogs[0].id}`})
}else {
navigateTo({url: `/user/profile/profile`})
}
}
// 善款明细
if(item.navigationId == 4119){
return navigateTo({url: `/bszx/pay-record/pay-record`})
}
return navigateTo({url: `/pages/category/category?id=${item.navigationId}`})
}
}
const reload = () => {
// 读取栏目
listCmsNavigation({parentId: 2828,hide: 0}).then(res => {
console.log(res,'9999')
setNavItems(res);
})
Taro.getUserInfo({
success: (res) => {
const avatar = res.userInfo.avatarUrl;
setUserInfo({
avatar,
nickname: res.userInfo.nickName,
sexName: res.userInfo.gender == 1 ? '男' : '女'
})
getUserInfo().then((data) => {
if (data) {
setUserInfo(data)
setIsLogin(true);
console.log(userInfo, 'userInfo...')
Taro.setStorageSync('UserId', data.userId)
// 获取openId
if (!data.openid) {
Taro.login({
success: (res) => {
getWxOpenId({code: res.code}).then(() => {
})
}
})
}
}
}).catch(() => {
console.log('未登录')
});
}
});
// 报名日志
myPageBszxBm({limit: 1}).then(res => {
if (res.list) {
setBmLogs(res.list);
}
})
setLoading(false);
};
const showAuthModal = () => {
Taro.showModal({
title: '授权提示',
content: '需要获取您的用户信息',
confirmText: '去授权',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
// 用户点击确认,打开授权设置页面
openSetting();
}
}
});
};
const openSetting = () => {
// Taro.openSetting调起客户端小程序设置界面返回用户设置的操作结果。设置界面只会出现小程序已经向用户请求过的权限。
Taro.openSetting({
success: (res) => {
if (res.authSetting['scope.userInfo']) {
// 用户授权成功,可以获取用户信息
reload();
} else {
// 用户拒绝授权,提示授权失败
Taro.showToast({
title: '授权失败',
icon: 'none'
});
}
}
});
};
useEffect(() => {
Taro.getSetting({
success: (res) => {
if (res.authSetting['scope.userInfo']) {
// 用户已经授权过,可以直接获取用户信息
console.log('用户已经授权过,可以直接获取用户信息')
reload();
} else {
// 用户未授权,需要弹出授权窗口
console.log('用户未授权,需要弹出授权窗口')
showAuthModal();
}
}
});
reload();
}, [])
return (
<div className={'my-3'}>
<div className={'pt-4 bg-yellow-50 rounded-2xl'}
style={{background: 'linear-gradient(to bottom, #ffffff, #ffffcc)'}}>
<div className={'flex justify-between pb-2 px-1'}>
{
navItems.map((item, index) => (
<div key={index} className={'text-center'}>
{
isLogin && !loading ?
<div className={'flex flex-col justify-center items-center'} onClick={() => {
onLogin(item, index)
}}>
<Image src={item.icon} height={28} width={28} lazyLoad={false}/>
<div className={'mt-2'} style={{fontSize: '15px'}}>{item?.title}</div>
</div>
:
<Button className={'text-white'} open-type="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber}>
<div className={'flex flex-col justify-center items-center'}>
<Image src={item.icon} height={28} width={28} lazyLoad={false}/>
<div className={'mt-2 text-gray-700'} style={{fontSize: '15px'}}>{item?.title}</div>
</div>
</Button>
}
</div>
))
}
</div>
</div>
{/*<image src={'https://oss.wsdns.cn/20250224/18a2f3b807c94aac8a67af34e95534d6.jpeg'} className={'book'}>倡议书</image>*/}
{/*<News id={categoryId}/>*/}
</div>
)
}
export default OrderIcon

View File

@@ -176,7 +176,7 @@ function UserCard() {
}
return (
<div className={'header-bg pt-36'}>
<div className={'header-bg pt-20'}>
<div className={'p-4'}>
<div
className={'user-card w-full flex flex-col justify-around rounded-xl shadow-sm'}
@@ -184,7 +184,7 @@ function UserCard() {
background: 'linear-gradient(to bottom, #ffffff, #ffffff)', // 这种情况建议使用类名来控制样式(引入外联样式)
// width: '720rpx',
// margin: '10px auto 0px auto',
height: '180px',
height: '170px',
// borderRadius: '22px 22px 0 0',
}}
>
@@ -200,7 +200,7 @@ function UserCard() {
)
}
<div className={'user-info flex flex-col px-2'}>
<div className={'py-1 text-black font-bold'}>{IsLogin ? userInfo?.mobile : '请点击头像登录'}</div>
<div className={'py-1 text-black font-bold'}>{IsLogin ? userInfo?.nickname : '请点击头像登录'}</div>
{IsLogin ? (
<div className={'grade text-xs py-1'}>
<Tag type="success" round>
@@ -215,7 +215,7 @@ function UserCard() {
{'个人资料'}
</div>
</div>
<div className={'flex justify-around mt-5'}>
<div className={'flex justify-around mt-1'}>
<div className={'item flex justify-center flex-col items-center'} onClick={() => navTo('/user/wallet/wallet', true)}>
<span className={'text-sm text-gray-500'}></span>
<span className={'text-xl'}>¥ {userInfo?.balance || '0.00'}</span>

View File

@@ -0,0 +1,69 @@
import {useEffect} from "react";
import navTo from "@/utils/common";
import {View, Text} from '@tarojs/components';
import {ArrowRight, Wallet, Comment, Transit, Refund, Package} from '@nutui/icons-react-taro';
function UserOrder() {
const reload = () => {
};
useEffect(() => {
reload()
}, []);
return (
<>
<View className={'px-4 pb-2'}>
<View
className={'user-card w-full flex flex-col justify-around rounded-xl shadow-sm'}
style={{
background: 'linear-gradient(to bottom, #ffffff, #ffffff)', // 这种情况建议使用类名来控制样式(引入外联样式)
// margin: '10px auto 0px auto',
height: '120px',
// borderRadius: '22px 22px 0 0',
}}
>
<View className={'title-bar flex justify-between pt-2'}>
<Text className={'title font-medium px-4'}></Text>
<View className={'more flex items-center px-2'} onClick={() => navTo('/user/order/order', true)}>
<Text className={'text-xs text-gray-500'}></Text>
<ArrowRight color="#cccccc" size={12}/>
</View>
</View>
<View className={'flex justify-around pb-1'}>
<View className={'item flex justify-center flex-col items-center'}
onClick={() => navTo('/user/order/order?statusFilter=0', true)}>
<Wallet size={26} className={'font-normal text-gray-500'}/>
<Text className={'text-sm text-gray-600 py-1'}></Text>
</View>
<View className={'item flex justify-center flex-col items-center'}
onClick={() => navTo('/user/order/order?statusFilter=1', true)}>
<Package size={26} className={'text-gray-500 font-normal'}/>
<Text className={'text-sm text-gray-600 py-1'}></Text>
</View>
<View className={'item flex justify-center flex-col items-center'}
onClick={() => navTo('/user/order/order?statusFilter=3', true)}>
<Transit size={24} className={'text-gray-500 font-normal'}/>
<Text className={'text-sm text-gray-600 py-1'}></Text>
</View>
<View className={'item flex justify-center flex-col items-center'}
onClick={() => navTo('/user/order/order?statusFilter=5', true)}>
<Comment size={24} className={'text-gray-500 font-normal'}/>
<Text className={'text-sm text-gray-600 py-1'}></Text>
</View>
<View className={'item flex justify-center flex-col items-center'}
onClick={() => navTo('/user/order/order?statusFilter=7', true)}>
<Refund size={26} className={'font-normal text-gray-500'}/>
<Text className={'text-sm text-gray-600 py-1'}>退/</Text>
</View>
</View>
</View>
</View>
</>
)
}
export default UserOrder;

View File

@@ -1,5 +1,6 @@
import {useEffect} from 'react'
import UserCard from "./components/UserCard";
import UserOrder from "./components/UserOrder";
import UserCell from "./components/UserCell";
import './user.scss'
import UserFooter from "./components/UserFooter";
@@ -13,6 +14,7 @@ function User() {
background: 'linear-gradient(to bottom, #e9fff2, #f9fafb)'
}}>
<UserCard />
<UserOrder />
<UserCell />
<UserFooter/>
</div>