Compare commits

..

2 Commits

Author SHA1 Message Date
sp mac bookpro 2605 c23496a56c fix: 积分商城兑换先扣积分后填地址 2026-06-03 15:33:32 +08:00
sp mac bookpro 2605 29fa2fc034 fix: 添加缺失的PointsMallCategory类型导入 2026-06-03 02:38:53 +08:00
2 changed files with 59 additions and 19 deletions

View File

@ -492,6 +492,15 @@ export interface PointsExchangeRequest {
zipCode?: string; zipCode?: string;
} }
export interface PointsExchangePrepareResponse {
orderId: string;
pointsDeducted: number;
remainingPoints: number;
expiresAt?: string;
}
export type PointsExchangeShippingRequest = Omit<PointsExchangeRequest, 'productId'>;
export interface PointsExchangeResponse { export interface PointsExchangeResponse {
orderId: string; orderId: string;
pointsDeducted: number; pointsDeducted: number;
@ -574,11 +583,17 @@ export const PointsMallAPI = {
} }
}) })
.then((r) => r.data), .then((r) => r.data),
exchangePrepare: (productId: string) =>
api.post<PointsExchangePrepareResponse>('/points-mall/exchange/prepare', { productId }).then((r) => r.data),
exchangeSubmitShipping: (orderId: string, shippingInfo: PointsExchangeShippingRequest) =>
api.post<PointsExchangeResponse>(`/points-mall/exchange/${orderId}/shipping`, shippingInfo).then((r) => r.data),
exchange: (productId: string, shippingInfo: Omit<PointsExchangeRequest, 'productId'>) => exchange: (productId: string, shippingInfo: Omit<PointsExchangeRequest, 'productId'>) =>
api.post<PointsExchangeResponse>('/points-mall/exchange', { api
productId, .post<PointsExchangeResponse>('/points-mall/exchange', {
...shippingInfo productId,
}).then((r) => r.data) ...shippingInfo
})
.then((r) => r.data)
}; };
// ============== 调用统计 (v0.8 P1) ============== // ============== 调用统计 (v0.8 P1) ==============

View File

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Button, Card, Empty, Input, Select, Space, Spin, Tag, Modal, Form, message } from 'antd'; import { Button, Card, Empty, Input, Select, Space, Spin, Tag, Modal, Form, message } from 'antd';
import { SearchOutlined } from '@ant-design/icons'; import { SearchOutlined } from '@ant-design/icons';
import { PointsMallAPI, PointsMallOverview, PointsMallProduct, PointsMallProductsResponse } from '../api'; import { PointsMallAPI, PointsMallCategory, PointsMallOverview, PointsMallProduct, PointsMallProductsResponse } from '../api';
type SortKey = 'popular' | 'price_asc' | 'price_desc' | 'newest'; type SortKey = 'popular' | 'price_asc' | 'price_desc' | 'newest';
@ -61,6 +61,7 @@ export default function PointsMallPage() {
const [exchangeModalVisible, setExchangeModalVisible] = useState(false); const [exchangeModalVisible, setExchangeModalVisible] = useState(false);
const [selectedProduct, setSelectedProduct] = useState<PointsMallProduct | null>(null); const [selectedProduct, setSelectedProduct] = useState<PointsMallProduct | null>(null);
const [pendingOrderId, setPendingOrderId] = useState<string | null>(null);
const [exchangeLoading, setExchangeLoading] = useState(false); const [exchangeLoading, setExchangeLoading] = useState(false);
const [form] = Form.useForm<ExchangeFormValues>(); const [form] = Form.useForm<ExchangeFormValues>();
@ -74,7 +75,11 @@ export default function PointsMallPage() {
setCategoryId(data.categories[0].id); setCategoryId(data.categories[0].id);
} }
} catch { } catch {
setOverview(MOCK_OVERVIEW); message.error('获取积分信息失败,请稍后重试');
setOverview({
...MOCK_OVERVIEW,
me: { points: 0, level: 'Lv.0' }
});
setCategories(MOCK_OVERVIEW.categories); setCategories(MOCK_OVERVIEW.categories);
} finally { } finally {
setOverviewLoading(false); setOverviewLoading(false);
@ -122,29 +127,46 @@ export default function PointsMallPage() {
const products = productsRes?.items || []; const products = productsRes?.items || [];
const total = productsRes?.total || 0; const total = productsRes?.total || 0;
const handleExchangeClick = (product: PointsMallProduct) => { const handleExchangeClick = async (product: PointsMallProduct) => {
setSelectedProduct(product); if (userPoints < product.pointsPrice) return;
setExchangeModalVisible(true);
form.resetFields(); setExchangeLoading(true);
try {
const res = await PointsMallAPI.exchangePrepare(product.id);
setSelectedProduct(product);
setPendingOrderId(res.orderId);
setExchangeModalVisible(true);
form.resetFields();
setOverview((prev) => {
if (!prev) return prev;
return { ...prev, me: { ...prev.me, points: res.remainingPoints } };
});
message.success('积分扣减成功,请填写收件信息完成兑换');
} catch (e: any) {
message.error(e?.message || '兑换失败,请稍后重试');
} finally {
setExchangeLoading(false);
}
}; };
const handleExchangeSubmit = async () => { const handleExchangeSubmit = async () => {
if (!selectedProduct) return; if (!selectedProduct || !pendingOrderId) return;
try { try {
await form.validateFields(); await form.validateFields();
setExchangeLoading(true); setExchangeLoading(true);
// TODO: 调用后端兑换接口 await PointsMallAPI.exchangeSubmitShipping(pendingOrderId, form.getFieldsValue());
// await PointsMallAPI.exchange(selectedProduct.id, form.getFieldsValue());
message.success('兑换成功!我们将尽快为您安排发货'); message.success('兑换成功!我们将尽快为您安排发货');
setExchangeModalVisible(false); setExchangeModalVisible(false);
setPendingOrderId(null);
// 刷新积分余额 // 刷新积分余额
loadOverview(); loadOverview();
} catch (error) { } catch (error: any) {
// 表单验证失败 if (error?.errorFields) return;
message.error(error?.message || '提交失败,请稍后重试');
} finally { } finally {
setExchangeLoading(false); setExchangeLoading(false);
} }
@ -350,7 +372,10 @@ export default function PointsMallPage() {
<Modal <Modal
title="兑换商品" title="兑换商品"
open={exchangeModalVisible} open={exchangeModalVisible}
onCancel={() => setExchangeModalVisible(false)} onCancel={() => {
setExchangeModalVisible(false);
setPendingOrderId(null);
}}
footer={null} footer={null}
width={500} width={500}
className="points-exchange-modal" className="points-exchange-modal"