new
This commit is contained in:
@@ -1,54 +1,72 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Form, Input, Button, Card, List, Tag, Typography, message, Space, Statistic, Divider, Modal, Descriptions } from 'antd';
|
||||
import { MobileOutlined, LockOutlined, SearchOutlined, CarOutlined, InboxOutlined, SafetyCertificateOutlined, CheckCircleOutlined, ClockCircleOutlined, CloseCircleOutlined, UserOutlined, EnvironmentOutlined, PhoneOutlined } from '@ant-design/icons';
|
||||
import { sendSms, queryMyOrders } from '../api';
|
||||
import { queryMyOrders } from '../api';
|
||||
import { motion } from 'framer-motion';
|
||||
import LoginModal from '../components/LoginModal';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
|
||||
const { Title, Text, Paragraph } = Typography;
|
||||
|
||||
const MyOrders = () => {
|
||||
const [step, setStep] = useState(0); // 0: Input Phone, 1: Verify Code, 2: Show Orders
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [phone, setPhone] = useState('');
|
||||
const [orders, setOrders] = useState([]);
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [currentOrder, setCurrentOrder] = useState(null);
|
||||
const [form] = Form.useForm();
|
||||
const [loginVisible, setLoginVisible] = useState(false);
|
||||
|
||||
const { user, login } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
// 如果已登录,自动查询订单
|
||||
if (user.phone_number) {
|
||||
handleQueryOrders(user.phone_number);
|
||||
}
|
||||
} else {
|
||||
// Don't auto-show login modal on mount if not logged in, just show the "Please login" UI
|
||||
// setLoginVisible(true);
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
const showDetail = (order) => {
|
||||
setCurrentOrder(order);
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
const handleSendSms = async (values) => {
|
||||
const handleQueryOrders = async (phone) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { phone_number } = values;
|
||||
await sendSms({ phone_number });
|
||||
message.success('验证码已发送');
|
||||
setPhone(phone_number);
|
||||
setStep(1);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
message.error('发送验证码失败,请重试');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleQueryOrders = async (values) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { code } = values;
|
||||
const response = await queryMyOrders({ phone_number: phone, code });
|
||||
// 使用 queryMyOrders 接口,这里我们需要调整该接口以支持仅传手机号(如果已登录)
|
||||
// 或者,既然已登录,后端应该能通过 Token 知道是谁,直接查这个人的订单
|
||||
// 但目前的 queryMyOrders 是 POST {phone_number, code},这主要用于免登录查询
|
||||
// 我们应该使用 OrderViewSet 的 list 方法,它已经支持 filter(wechat_user=user)
|
||||
// 但前端 api.js 中 getOrder 是查单个,我们需要一个 getMyOrders 接口
|
||||
|
||||
// 修改策略:如果已登录,直接调用 queryMyOrders,但不需要 code?
|
||||
// 后端 my_orders 接口目前强制需要 code。
|
||||
// 应该使用 OrderViewSet 的标准 list 接口,它会根据 Token 返回自己的订单。
|
||||
// api.js 中没有导出 getOrders list 接口,我们可以临时用 queryMyOrders 但绕过 code 检查?
|
||||
// 不,最好的方式是使用标准的 GET /orders/,后端 OrderViewSet.get_queryset 已经处理了 get_current_wechat_user
|
||||
|
||||
// 让我们先用 GET /orders/ 试试,需要在 api.js 确认是否有 export
|
||||
// 检查 api.js 发现没有 getOrderList, 只有 getOrder(id)
|
||||
// 我们需要修改 api.js 或在此处直接调用
|
||||
|
||||
// 为了不修改 api.js 太多,我们引入 axios 实例自己发请求,或者假设 api.js 有一个 getMyOrderList
|
||||
// 实际上,查看 api.js, queryMyOrders 是 POST /orders/my_orders/,这是免登录版本
|
||||
// 我们应该用 GET /orders/,因为 get_queryset 已经过滤了。
|
||||
|
||||
// 临时引入 api 实例
|
||||
const { default: api } = await import('../api');
|
||||
const response = await api.get('/orders/');
|
||||
setOrders(response.data);
|
||||
setStep(2);
|
||||
if (response.data.length === 0) {
|
||||
message.info('未查询到相关订单');
|
||||
message.info('您暂时没有订单');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
message.error('验证失败或查询出错');
|
||||
message.error('查询出错');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -75,119 +93,32 @@ const MyOrders = () => {
|
||||
<div style={{ width: '100%', maxWidth: 1200 }}>
|
||||
<div style={{ textAlign: 'center', marginBottom: 40 }}>
|
||||
<SafetyCertificateOutlined style={{ fontSize: 48, color: '#00b96b', marginBottom: 20 }} />
|
||||
<Title level={2} style={{ color: '#fff', margin: 0, fontFamily: "'Orbitron', sans-serif" }}>我的订单查询</Title>
|
||||
<Title level={2} style={{ color: '#fff', margin: 0, fontFamily: "'Orbitron', sans-serif" }}>我的订单</Title>
|
||||
<Text style={{ color: '#666' }}>Secure Order Verification System</Text>
|
||||
</div>
|
||||
|
||||
{step < 2 ? (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<Card
|
||||
style={{
|
||||
background: 'rgba(0,0,0,0.6)',
|
||||
border: '1px solid rgba(0, 185, 107, 0.3)',
|
||||
backdropFilter: 'blur(20px)',
|
||||
borderRadius: '16px',
|
||||
boxShadow: '0 8px 32px 0 rgba(0, 185, 107, 0.1)',
|
||||
maxWidth: 600,
|
||||
margin: '0 auto'
|
||||
}}
|
||||
bodyStyle={{ padding: '40px' }}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onFinish={step === 0 ? handleSendSms : handleQueryOrders}
|
||||
size="large"
|
||||
>
|
||||
{step === 0 && (
|
||||
<Form.Item
|
||||
name="phone_number"
|
||||
rules={[{ required: true, message: '请输入手机号' }, { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号' }]}
|
||||
>
|
||||
<Input
|
||||
prefix={<MobileOutlined style={{ color: '#00b96b', fontSize: 20 }} />}
|
||||
placeholder="请输入下单时的手机号"
|
||||
style={{
|
||||
background: 'rgba(255,255,255,0.05)',
|
||||
border: '1px solid rgba(255,255,255,0.1)',
|
||||
color: '#fff',
|
||||
height: 50,
|
||||
borderRadius: 8
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{step === 1 && (
|
||||
<>
|
||||
<div style={{ textAlign: 'center', marginBottom: 30, color: '#aaa' }}>
|
||||
已发送验证码至 <span style={{ color: '#00b96b', fontWeight: 'bold' }}>{phone}</span>
|
||||
<Button type="link" onClick={() => setStep(0)} style={{ color: '#1890ff', marginLeft: 10 }}>修改</Button>
|
||||
</div>
|
||||
<Form.Item
|
||||
name="code"
|
||||
rules={[{ required: true, message: '请输入验证码' }]}
|
||||
>
|
||||
<Input
|
||||
prefix={<LockOutlined style={{ color: '#00b96b', fontSize: 20 }} />}
|
||||
placeholder="请输入6位验证码"
|
||||
maxLength={6}
|
||||
style={{
|
||||
background: 'rgba(255,255,255,0.05)',
|
||||
border: '1px solid rgba(255,255,255,0.1)',
|
||||
color: '#fff',
|
||||
height: 50,
|
||||
borderRadius: 8,
|
||||
textAlign: 'center',
|
||||
letterSpacing: '8px',
|
||||
fontSize: '20px'
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Form.Item style={{ marginBottom: 0, marginTop: 20 }}>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
block
|
||||
loading={loading}
|
||||
icon={<SearchOutlined />}
|
||||
style={{
|
||||
height: 50,
|
||||
fontSize: 18,
|
||||
background: 'linear-gradient(90deg, #00b96b 0%, #009456 100%)',
|
||||
border: 'none',
|
||||
borderRadius: 8,
|
||||
boxShadow: '0 4px 15px rgba(0, 185, 107, 0.3)'
|
||||
}}
|
||||
>
|
||||
{step === 0 ? '获取验证码' : '查询订单'}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Card>
|
||||
</motion.div>
|
||||
{!user ? (
|
||||
<div style={{ textAlign: 'center', padding: 40, background: 'rgba(0,0,0,0.5)', borderRadius: 16 }}>
|
||||
<Text style={{ color: '#fff', fontSize: 18, display: 'block', marginBottom: 20 }}>请先登录以查看您的订单</Text>
|
||||
<Button type="primary" size="large" onClick={() => setLoginVisible(true)}>立即登录</Button>
|
||||
</div>
|
||||
) : (
|
||||
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
|
||||
<div style={{ marginBottom: 20, textAlign: 'right' }}>
|
||||
<div style={{ marginBottom: 20, textAlign: 'right', color: '#fff' }}>
|
||||
当前登录用户: <span style={{ color: '#00b96b', fontWeight: 'bold', marginRight: 10 }}>{user.nickname}</span>
|
||||
<Button
|
||||
onClick={() => { setStep(0); setOrders([]); form.resetFields(); }}
|
||||
ghost
|
||||
style={{ borderColor: '#666', color: '#888' }}
|
||||
onClick={() => handleQueryOrders(user.phone_number)}
|
||||
loading={loading}
|
||||
icon={<SearchOutlined />}
|
||||
>
|
||||
查询其他号码
|
||||
刷新订单
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<List
|
||||
grid={{ gutter: 24, xs: 1, sm: 1, md: 2, lg: 2, xl: 3, xxl: 3 }}
|
||||
dataSource={orders}
|
||||
loading={loading}
|
||||
renderItem={order => (
|
||||
<List.Item>
|
||||
<Card
|
||||
@@ -334,6 +265,17 @@ const MyOrders = () => {
|
||||
</Descriptions>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
<LoginModal
|
||||
visible={loginVisible}
|
||||
onClose={() => setLoginVisible(false)}
|
||||
onLoginSuccess={(userData) => {
|
||||
login(userData);
|
||||
if (userData.phone_number) {
|
||||
handleQueryOrders(userData.phone_number);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Button, Row, Col, Tag, Statistic, Modal, Form, Input, InputNumber, mess
|
||||
import { ShoppingCartOutlined, SafetyCertificateOutlined, ThunderboltOutlined, EyeOutlined, StarOutlined } from '@ant-design/icons';
|
||||
import { getConfigs, createOrder, nativePay } from '../api';
|
||||
import ModelViewer from '../components/ModelViewer';
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
import './ProductDetail.css';
|
||||
|
||||
const ProductDetail = () => {
|
||||
@@ -16,9 +17,22 @@ const ProductDetail = () => {
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const { user } = useAuth();
|
||||
|
||||
// 优先从 URL 获取,如果没有则从 localStorage 获取,不再默认绑定 flw666
|
||||
const refCode = searchParams.get('ref') || localStorage.getItem('ref_code');
|
||||
|
||||
useEffect(() => {
|
||||
// 自动填充用户信息
|
||||
if (user) {
|
||||
form.setFieldsValue({
|
||||
phone_number: user.phone_number,
|
||||
// 如果后端返回了地址信息,这里也可以填充
|
||||
// shipping_address: user.shipping_address
|
||||
});
|
||||
}
|
||||
}, [isModalOpen, user]); // 当弹窗打开或用户状态变化时填充
|
||||
|
||||
useEffect(() => {
|
||||
console.log('[ProductDetail] Current ref_code:', refCode);
|
||||
}, [refCode]);
|
||||
|
||||
Reference in New Issue
Block a user