From b53929cb225e84fbfb6c3fcdca1532f36afdadc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Wed, 6 Aug 2025 17:22:01 +0800 Subject: [PATCH] =?UTF-8?q?FEAT=20=3D>=20=E6=9C=AC=E6=AC=A1=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E9=A1=B9=E7=9B=AE=E4=B8=BA=EF=BC=9A=20=E5=85=85?= =?UTF-8?q?=E5=80=BC=E8=AE=B0=E5=BD=95=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recharge/{ => index}/index.module.scss | 0 .../mine/recharge/{ => index}/index.tsx | 4 +- .../pages/mobile/mine/recharge/order/api.ts | 197 ++++++++++ .../pages/mobile/mine/recharge/order/data.ts | 40 ++ .../mine/recharge/order/index.module.scss | 242 ++++++++++++ .../mobile/mine/recharge/order/index.tsx | 344 ++++++++++++++++++ .../src/pages/mobile/mine/setting/Privacy.tsx | 18 +- nkebao/src/router/module/mine.tsx | 6 + 8 files changed, 835 insertions(+), 16 deletions(-) rename nkebao/src/pages/mobile/mine/recharge/{ => index}/index.module.scss (100%) rename nkebao/src/pages/mobile/mine/recharge/{ => index}/index.tsx (98%) create mode 100644 nkebao/src/pages/mobile/mine/recharge/order/api.ts create mode 100644 nkebao/src/pages/mobile/mine/recharge/order/data.ts create mode 100644 nkebao/src/pages/mobile/mine/recharge/order/index.module.scss create mode 100644 nkebao/src/pages/mobile/mine/recharge/order/index.tsx diff --git a/nkebao/src/pages/mobile/mine/recharge/index.module.scss b/nkebao/src/pages/mobile/mine/recharge/index/index.module.scss similarity index 100% rename from nkebao/src/pages/mobile/mine/recharge/index.module.scss rename to nkebao/src/pages/mobile/mine/recharge/index/index.module.scss diff --git a/nkebao/src/pages/mobile/mine/recharge/index.tsx b/nkebao/src/pages/mobile/mine/recharge/index/index.tsx similarity index 98% rename from nkebao/src/pages/mobile/mine/recharge/index.tsx rename to nkebao/src/pages/mobile/mine/recharge/index/index.tsx index e322aca2..9507b73c 100644 --- a/nkebao/src/pages/mobile/mine/recharge/index.tsx +++ b/nkebao/src/pages/mobile/mine/recharge/index/index.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { useNavigate } from "react-router-dom"; -import { Card, Button, Toast, NavBar, Tabs } from "antd-mobile"; +import { Card, Button, Toast, Tabs } from "antd-mobile"; import { useUserStore } from "@/store/module/user"; import style from "./index.module.scss"; import { @@ -338,7 +338,7 @@ const Recharge: React.FC = () => { right={
navigate("/mine/consumption-records")} + onClick={() => navigate("/recharge/order")} >  记录 diff --git a/nkebao/src/pages/mobile/mine/recharge/order/api.ts b/nkebao/src/pages/mobile/mine/recharge/order/api.ts new file mode 100644 index 00000000..11d4573e --- /dev/null +++ b/nkebao/src/pages/mobile/mine/recharge/order/api.ts @@ -0,0 +1,197 @@ +import { + RechargeOrdersResponse, + RechargeOrderDetail, + RechargeOrderParams, +} from "./data"; + +// 模拟数据 +const mockOrders = [ + { + id: "1", + orderNo: "RC20241201001", + amount: 100.0, + paymentMethod: "wechat", + status: "success" as const, + createTime: "2024-12-01T10:30:00Z", + payTime: "2024-12-01T10:32:15Z", + description: "账户充值", + balance: 150.0, + }, + { + id: "2", + orderNo: "RC20241201002", + amount: 200.0, + paymentMethod: "alipay", + status: "pending" as const, + createTime: "2024-12-01T14:20:00Z", + description: "账户充值", + balance: 350.0, + }, + { + id: "3", + orderNo: "RC20241130001", + amount: 50.0, + paymentMethod: "bank", + status: "success" as const, + createTime: "2024-11-30T09:15:00Z", + payTime: "2024-11-30T09:18:30Z", + description: "账户充值", + balance: 50.0, + }, + { + id: "4", + orderNo: "RC20241129001", + amount: 300.0, + paymentMethod: "wechat", + status: "failed" as const, + createTime: "2024-11-29T16:45:00Z", + description: "账户充值", + }, + { + id: "5", + orderNo: "RC20241128001", + amount: 150.0, + paymentMethod: "alipay", + status: "cancelled" as const, + createTime: "2024-11-28T11:20:00Z", + description: "账户充值", + }, + { + id: "6", + orderNo: "RC20241127001", + amount: 80.0, + paymentMethod: "wechat", + status: "success" as const, + createTime: "2024-11-27T13:10:00Z", + payTime: "2024-11-27T13:12:45Z", + description: "账户充值", + balance: 80.0, + }, + { + id: "7", + orderNo: "RC20241126001", + amount: 120.0, + paymentMethod: "bank", + status: "success" as const, + createTime: "2024-11-26T08:30:00Z", + payTime: "2024-11-26T08:33:20Z", + description: "账户充值", + balance: 120.0, + }, + { + id: "8", + orderNo: "RC20241125001", + amount: 250.0, + paymentMethod: "alipay", + status: "pending" as const, + createTime: "2024-11-25T15:45:00Z", + description: "账户充值", + balance: 370.0, + }, +]; + +// 模拟延迟 +const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + +// 获取充值记录列表 +export async function getRechargeOrders( + params: RechargeOrderParams, +): Promise { + await delay(800); // 模拟网络延迟 + + let filteredOrders = [...mockOrders]; + + // 状态筛选 + if (params.status && params.status !== "all") { + filteredOrders = filteredOrders.filter( + order => order.status === params.status, + ); + } + + // 时间筛选 + if (params.startTime) { + filteredOrders = filteredOrders.filter( + order => new Date(order.createTime) >= new Date(params.startTime!), + ); + } + if (params.endTime) { + filteredOrders = filteredOrders.filter( + order => new Date(order.createTime) <= new Date(params.endTime!), + ); + } + + // 分页 + const startIndex = (params.page - 1) * params.limit; + const endIndex = startIndex + params.limit; + const paginatedOrders = filteredOrders.slice(startIndex, endIndex); + + return { + list: paginatedOrders, + total: filteredOrders.length, + page: params.page, + limit: params.limit, + }; +} + +// 获取充值记录详情 +export async function getRechargeOrderDetail( + id: string, +): Promise { + await delay(500); + + const order = mockOrders.find(o => o.id === id); + if (!order) { + throw new Error("订单不存在"); + } + + return { + ...order, + paymentChannel: + order.paymentMethod === "wechat" + ? "微信支付" + : order.paymentMethod === "alipay" + ? "支付宝" + : "银行转账", + transactionId: `TX${order.orderNo}`, + }; +} + +// 取消充值订单 +export async function cancelRechargeOrder(id: string): Promise { + await delay(1000); + + const orderIndex = mockOrders.findIndex(o => o.id === id); + if (orderIndex === -1) { + throw new Error("订单不存在"); + } + + if (mockOrders[orderIndex].status !== "pending") { + throw new Error("只能取消处理中的订单"); + } + + // 模拟更新订单状态 + (mockOrders[orderIndex] as any).status = "cancelled"; +} + +// 申请退款 +export async function refundRechargeOrder( + id: string, + reason: string, +): Promise { + await delay(1200); + + const orderIndex = mockOrders.findIndex(o => o.id === id); + if (orderIndex === -1) { + throw new Error("订单不存在"); + } + + if (mockOrders[orderIndex].status !== "success") { + throw new Error("只能对成功的订单申请退款"); + } + + // 模拟添加退款信息 + const order = mockOrders[orderIndex]; + (order as any).refundAmount = order.amount; + (order as any).refundTime = new Date().toISOString(); + (order as any).refundReason = reason; +} diff --git a/nkebao/src/pages/mobile/mine/recharge/order/data.ts b/nkebao/src/pages/mobile/mine/recharge/order/data.ts new file mode 100644 index 00000000..95b9a0e9 --- /dev/null +++ b/nkebao/src/pages/mobile/mine/recharge/order/data.ts @@ -0,0 +1,40 @@ +// 充值记录类型定义 +export interface RechargeOrder { + id: string; + orderNo: string; + amount: number; + paymentMethod: string; + status: "success" | "pending" | "failed" | "cancelled"; + createTime: string; + payTime?: string; + description?: string; + remark?: string; + operator?: string; + balance?: number; +} + +// API响应类型 +export interface RechargeOrdersResponse { + list: RechargeOrder[]; + total: number; + page: number; + limit: number; +} + +// 充值记录详情 +export interface RechargeOrderDetail extends RechargeOrder { + paymentChannel?: string; + transactionId?: string; + refundAmount?: number; + refundTime?: string; + refundReason?: string; +} + +// 查询参数 +export interface RechargeOrderParams { + page: number; + limit: number; + status?: string; + startTime?: string; + endTime?: string; +} diff --git a/nkebao/src/pages/mobile/mine/recharge/order/index.module.scss b/nkebao/src/pages/mobile/mine/recharge/order/index.module.scss new file mode 100644 index 00000000..64031e92 --- /dev/null +++ b/nkebao/src/pages/mobile/mine/recharge/order/index.module.scss @@ -0,0 +1,242 @@ +.recharge-orders-page { + padding: 16px; + background: #f5f5f5; + min-height: 100vh; + + .orders-list { + .order-card { + margin-bottom: 12px; + border-radius: 12px; + background: #fff; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + overflow: hidden; + + .order-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px; + border-bottom: 1px solid #f0f0f0; + + .order-info { + flex: 1; + + .order-no { + font-size: 14px; + color: #333; + font-weight: 500; + margin-bottom: 4px; + } + + .order-time { + font-size: 12px; + color: #999; + display: flex; + align-items: center; + gap: 4px; + } + } + + .order-amount { + text-align: right; + + .amount-text { + font-size: 18px; + font-weight: 600; + color: #52c41a; + margin-bottom: 4px; + } + + .status-tag { + font-size: 12px; + padding: 2px 8px; + border-radius: 10px; + } + } + } + + .order-details { + padding: 12px 16px; + + .detail-row { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + font-size: 14px; + + &:last-child { + margin-bottom: 0; + } + + .label { + color: #666; + } + + .value { + color: #333; + font-weight: 500; + } + } + + .payment-method { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 8px; + + .method-icon { + width: 20px; + height: 20px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; + color: #fff; + } + + .method-text { + font-size: 14px; + color: #333; + } + } + + .balance-info { + background: #f8f9fa; + padding: 8px 12px; + border-radius: 6px; + font-size: 12px; + color: #666; + margin-top: 8px; + } + } + + .order-actions { + padding: 12px 16px; + border-top: 1px solid #f0f0f0; + display: flex; + gap: 8px; + + .action-btn { + flex: 1; + height: 32px; + border-radius: 6px; + font-size: 12px; + border: none; + cursor: pointer; + transition: all 0.2s; + + &.primary { + background: #1677ff; + color: #fff; + + &:hover { + background: #0958d9; + } + } + + &.secondary { + background: #f5f5f5; + color: #666; + border: 1px solid #d9d9d9; + + &:hover { + background: #e6e6e6; + } + } + + &.danger { + background: #ff4d4f; + color: #fff; + + &:hover { + background: #cf1322; + } + } + } + } + } + } + + .empty-state { + padding: 60px 20px; + text-align: center; + + .empty-icon { + font-size: 48px; + color: #d9d9d9; + margin-bottom: 16px; + } + + .empty-text { + font-size: 14px; + color: #999; + } + } + + .loading-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 40px 20px; + + .loading-text { + margin-top: 12px; + font-size: 14px; + color: #666; + } + } + + .load-more { + text-align: center; + padding: 16px; + color: #1677ff; + font-size: 14px; + cursor: pointer; + background: #fff; + border-radius: 8px; + margin-top: 12px; + + &:hover { + background: #f0f8ff; + } + } + + .filter-bar { + background: #fff; + border-radius: 12px; + padding: 16px; + margin-bottom: 16px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + + .filter-tabs { + display: flex; + gap: 8px; + + .filter-tab { + flex: 1; + height: 32px; + border-radius: 16px; + font-size: 12px; + border: 1px solid #d9d9d9; + background: #fff; + color: #666; + cursor: pointer; + transition: all 0.2s; + + &.active { + background: #1677ff; + color: #fff; + border-color: #1677ff; + } + + &:hover:not(.active) { + border-color: #1677ff; + color: #1677ff; + } + } + } + } +} diff --git a/nkebao/src/pages/mobile/mine/recharge/order/index.tsx b/nkebao/src/pages/mobile/mine/recharge/order/index.tsx new file mode 100644 index 00000000..7e5260e6 --- /dev/null +++ b/nkebao/src/pages/mobile/mine/recharge/order/index.tsx @@ -0,0 +1,344 @@ +import React, { useState, useEffect, useCallback } from "react"; +import { useNavigate } from "react-router-dom"; +import { Card, SpinLoading, Empty, Toast, Dialog } from "antd-mobile"; +import { + WalletOutlined, + ClockCircleOutlined, + WechatOutlined, + AlipayCircleOutlined, + BankOutlined, +} from "@ant-design/icons"; +import NavCommon from "@/components/NavCommon"; +import Layout from "@/components/Layout/Layout"; +import { + getRechargeOrders, + cancelRechargeOrder, + refundRechargeOrder, +} from "./api"; +import { RechargeOrder } from "./data"; +import style from "./index.module.scss"; + +const RechargeOrders: React.FC = () => { + const navigate = useNavigate(); + const [orders, setOrders] = useState([]); + const [loading, setLoading] = useState(true); + const [hasMore, setHasMore] = useState(true); + const [page, setPage] = useState(1); + const [statusFilter, setStatusFilter] = useState("all"); + + const loadOrders = async (reset = false) => { + setLoading(true); + try { + const currentPage = reset ? 1 : page; + const params = { + page: currentPage, + limit: 20, + ...(statusFilter !== "all" && { status: statusFilter }), + }; + + const response = await getRechargeOrders(params); + const newOrders = response.list || []; + setOrders(prev => (reset ? newOrders : [...prev, ...newOrders])); + setHasMore(newOrders.length === 20); + if (reset) setPage(1); + else setPage(currentPage + 1); + } catch (error) { + console.error("加载充值记录失败:", error); + Toast.show({ content: "加载失败,请重试", position: "top" }); + } finally { + setLoading(false); + } + }; + + // 初始化加载 + useEffect(() => { + loadOrders(true); + }, []); + + // 筛选条件变化时重新加载 + const handleFilterChange = (newStatus: string) => { + setStatusFilter(newStatus); + setPage(1); + setOrders([]); + loadOrders(true); + }; + + const getStatusText = (status: string) => { + switch (status) { + case "success": + return "充值成功"; + case "pending": + return "处理中"; + case "failed": + return "充值失败"; + case "cancelled": + return "已取消"; + default: + return "未知状态"; + } + }; + + const getStatusColor = (status: string) => { + switch (status) { + case "success": + return "#52c41a"; + case "pending": + return "#faad14"; + case "failed": + return "#ff4d4f"; + case "cancelled": + return "#999"; + default: + return "#666"; + } + }; + + const getPaymentMethodIcon = (method: string) => { + switch (method.toLowerCase()) { + case "wechat": + return ; + case "alipay": + return ; + case "bank": + return ; + default: + return ; + } + }; + + const getPaymentMethodColor = (method: string) => { + switch (method.toLowerCase()) { + case "wechat": + return "#07c160"; + case "alipay": + return "#1677ff"; + case "bank": + return "#722ed1"; + default: + return "#666"; + } + }; + + const formatTime = (timeStr: string) => { + const date = new Date(timeStr); + const now = new Date(); + const diff = now.getTime() - date.getTime(); + const days = Math.floor(diff / (1000 * 60 * 60 * 24)); + + if (days === 0) { + return date.toLocaleTimeString("zh-CN", { + hour: "2-digit", + minute: "2-digit", + }); + } else if (days === 1) { + return ( + "昨天 " + + date.toLocaleTimeString("zh-CN", { + hour: "2-digit", + minute: "2-digit", + }) + ); + } else if (days < 7) { + return `${days}天前`; + } else { + return date.toLocaleDateString("zh-CN"); + } + }; + + const handleCancelOrder = async (orderId: string) => { + const result = await Dialog.confirm({ + content: "确定要取消这个充值订单吗?", + confirmText: "确定取消", + cancelText: "再想想", + }); + + if (result) { + try { + await cancelRechargeOrder(orderId); + Toast.show({ content: "订单已取消", position: "top" }); + loadOrders(true); + } catch (error) { + console.error("取消订单失败:", error); + Toast.show({ content: "取消失败,请重试", position: "top" }); + } + } + }; + + const handleRefundOrder = async (orderId: string) => { + const result = await Dialog.confirm({ + content: "确定要申请退款吗?退款将在1-3个工作日内处理。", + confirmText: "申请退款", + cancelText: "取消", + }); + + if (result) { + try { + await refundRechargeOrder(orderId, "用户主动申请退款"); + Toast.show({ content: "退款申请已提交", position: "top" }); + loadOrders(true); + } catch (error) { + console.error("申请退款失败:", error); + Toast.show({ content: "申请失败,请重试", position: "top" }); + } + } + }; + + const renderOrderItem = (order: RechargeOrder) => ( + +
+
+
订单号:{order.orderNo}
+
+ + {formatTime(order.createTime)} +
+
+
+
+ ¥{order.amount.toFixed(2)} +
+
+ {getStatusText(order.status)} +
+
+
+ +
+
+
+ {getPaymentMethodIcon(order.paymentMethod)} +
+
{order.paymentMethod}
+
+ + {order.description && ( +
+ 备注 + {order.description} +
+ )} + + {order.payTime && ( +
+ 支付时间 + {formatTime(order.payTime)} +
+ )} + + {order.balance !== undefined && ( +
+ 充值后余额: ¥{order.balance.toFixed(2)} +
+ )} +
+ + {order.status === "pending" && ( +
+ +
+ )} + + {order.status === "success" && ( +
+ + +
+ )} + + {order.status === "failed" && ( +
+ +
+ )} +
+ ); + + const filterTabs = [ + { key: "all", label: "全部" }, + { key: "success", label: "成功" }, + { key: "pending", label: "处理中" }, + { key: "failed", label: "失败" }, + { key: "cancelled", label: "已取消" }, + ]; + + return ( + } + loading={loading && page === 1} + > +
+
+
+ {filterTabs.map(tab => ( + + ))} +
+
+ + {orders.length === 0 && !loading ? ( + } + /> + ) : ( +
+ {orders.map(renderOrderItem)} + {loading && page > 1 && ( +
+ +
加载中...
+
+ )} + {!loading && hasMore && ( +
loadOrders()}> + 加载更多 +
+ )} +
+ )} +
+
+ ); +}; + +export default RechargeOrders; diff --git a/nkebao/src/pages/mobile/mine/setting/Privacy.tsx b/nkebao/src/pages/mobile/mine/setting/Privacy.tsx index ab3f0737..4a3ea570 100644 --- a/nkebao/src/pages/mobile/mine/setting/Privacy.tsx +++ b/nkebao/src/pages/mobile/mine/setting/Privacy.tsx @@ -1,27 +1,17 @@ import React from "react"; -import { useNavigate } from "react-router-dom"; -import { NavBar, Card } from "antd-mobile"; +import { Card } from "antd-mobile"; import Layout from "@/components/Layout/Layout"; import style from "./index.module.scss"; +import NavCommon from "@/components/NavCommon"; const Privacy: React.FC = () => { - const navigate = useNavigate(); - return ( - navigate(-1)} style={{ background: "#fff" }}> - - 用户隐私协议 - - - } - > + }>

用户隐私协议

-

更新时间:2024年12月1日

+

更新时间:2025年8月1日

1. 信息收集

diff --git a/nkebao/src/router/module/mine.tsx b/nkebao/src/router/module/mine.tsx index 431c3da2..9bdb1ac3 100644 --- a/nkebao/src/router/module/mine.tsx +++ b/nkebao/src/router/module/mine.tsx @@ -6,6 +6,7 @@ import TrafficPoolDetail from "@/pages/mobile/mine/traffic-pool/detail/index"; import WechatAccounts from "@/pages/mobile/mine/wechat-accounts/list/index"; import WechatAccountDetail from "@/pages/mobile/mine/wechat-accounts/detail/index"; import Recharge from "@/pages/mobile/mine/recharge/index"; +import RechargeOrder from "@/pages/mobile/mine/recharge/order/index"; import Setting from "@/pages/mobile/mine/setting/index"; import SecuritySetting from "@/pages/mobile/mine/setting/SecuritySetting"; import About from "@/pages/mobile/mine/setting/About"; @@ -54,6 +55,11 @@ const routes = [ element: , auth: true, }, + { + path: "/recharge/order", + element: , + auth: true, + }, { path: "/settings", element: ,