赞
踩
// src\helpers\cart.ts import { Product } from '../store/models/product' export interface CartItem extends Product { count: number } // 将产品添加到购物车 export const addItem = (item: Product, next: () => void) => { let cart: CartItem[] = [] if (typeof window !== 'undefined') { if (localStorage.getItem('cart')) { // `!` 非空断言 cart = JSON.parse(localStorage.getItem('cart')!) } } const cartItem = cart.find(product => item._id === product._id) if (cartItem) { cartItem.count++ } else { cart.push({ ...item, count: 1 }) } localStorage.setItem('cart', JSON.stringify(cart)) // 执行回调 next() }
// src\components\core\ProductItem.tsx import { Button, Card, Col, Image, Row, Typography } from 'antd' import { push } from 'connected-react-router' import moment from 'moment' import { useDispatch } from 'react-redux' import { Link } from 'react-router-dom' import { FC } from 'react' import { API } from '../../config' import { addItem } from '../../helpers/cart' import { Product } from '../../store/models/product' const { Title, Paragraph } = Typography interface Props { product: Product showViewBtn?: boolean showCartBtn?: boolean } const ProductItem: FC<Props> = ({ product, showViewBtn = true, showCartBtn = true }) => { const dispatch = useDispatch() // 加入购物车 const addToCart = () => { addItem(product, () => { // 添加完成后跳转到购物车页面 // connected-react-router 通过向 store 派发动作的方式实现路由跳转 // 可以在 store 中存储路由历史 dispatch(push('/cart')) }) } const showButtons = () => { const buttonArray = [] if (showViewBtn) { buttonArray.push( <Button type="link"> <Link to={`/product/${product._id}`}>查看详情</Link> </Button> ) } if (showCartBtn) { buttonArray.push( <Button type="link" onClick={addToCart}> 加入购物车 </Button> ) } return buttonArray } return ( <Card cover={<Image src={`${API}/product/photo/${product._id}`} alt={product.name} preview={false} />} actions={showButtons()} > <Title level={5}>{product.name}</Title> <Paragraph ellipsis={{ rows: 2 }}>{product.description}</Paragraph> <Row> <Col span="12">销量:{product.sold}</Col> <Col span="12" style={{ textAlign: 'right' }}> 价格:¥{product.price} </Col> </Row> <Row> <Col span="12">上架时间:{moment(product.createdAt).format('YYYY-MM-DD')}</Col> <Col span="12" style={{ textAlign: 'right' }}> 所属分类:{product.category.name} </Col> </Row> </Card> ) } export default ProductItem
// src\helpers\cart.ts import { Product } from '../store/models/product' export interface CartItem extends Product { count: number } // 将产品添加到购物车 export const addItem = (item: Product, next: () => void) => { let cart: CartItem[] = [] if (typeof window !== 'undefined') { if (localStorage.getItem('cart')) { // `!` 非空断言 cart = JSON.parse(localStorage.getItem('cart')!) } } const cartItem = cart.find(product => item._id === product._id) if (cartItem) { cartItem.count++ } else { cart.push({ ...item, count: 1 }) } localStorage.setItem('cart', JSON.stringify(cart)) // 执行回调 next() } // 获取本地购物车数据 export const getCart = () => { if (typeof window !== 'undefined') { if (localStorage.getItem('cart')) { return JSON.parse(localStorage.getItem('cart')!) } } return [] }
// src\components\core\Cart.tsx import { Row, Col } from 'antd' import { useEffect, useState } from 'react' import { CartItem as CartItemModel, getCart } from '../../helpers/cart' import CartItem from './CartItem' import Layout from './Layout' const Cart = () => { const [cart, setCart] = useState<CartItemModel[]>([]) useEffect(() => { setCart(getCart()) }, []) const showCart = () => ( // 为了拆分组件,使用 Antd 的 Table 组件的结构,但不使用这个组件 <table style={{ width: '100%' }}> <thead className="ant-table-thead"> <tr> <th className="ant-table-cell">产品封面</th> <th className="ant-table-cell">产品名称</th> <th className="ant-table-cell">产品价格</th> <th className="ant-table-cell">产品分类</th> <th className="ant-table-cell">产品数量</th> <th className="ant-table-cell">操作</th> </tr> </thead> <tbody className="ant-table-tbody"> {cart.map(item => ( <CartItem product={item} /> ))} </tbody> </table> ) return ( <Layout title="购物车" subTitle=""> <Row gutter={16}> <Col span="16">{showCart()}</Col> <Col span="8"></Col> </Row> </Layout> ) } export default Cart
// src\components\core\CartItem.tsx import { Button, Image, Input } from 'antd' import { FC } from 'react' import { API } from '../../config' import { CartItem as CartItemModel } from '../../helpers/cart' interface Props { product: CartItemModel } const CartItem: FC<Props> = ({ product }) => { return ( <tr className="ant-table-row"> <td className="ant-table-cell"> <Image src={`${API}/product/photo/${product._id}`} width={120} alt={product.name} preview={false} /> </td> <td className="ant-table-cell">{product.name}</td> <td className="ant-table-cell">¥{product.price}</td> <td className="ant-table-cell">{product.category.name}</td> <td className="ant-table-cell"> <Input type="number" value={product.count} /> </td> <td className="ant-table-cell"> <Button danger type="primary"> 删除 </Button> </td> </tr> ) } export default CartItem
// src\helpers\cart.ts import { Product } from '../store/models/product' export interface CartItem extends Product { count: number } ... // 更改购物车中产品数量 export const updateItem = (productId: string, count: number) => { const cart: CartItem[] = getCart() const cartItem = cart.find(product => productId === product._id) if (cartItem) { cartItem.count = count localStorage.setItem('cart', JSON.stringify(cart)) } return cart }
// src\components\core\CartItem.tsx import { Button, Image, Input } from 'antd' import { API } from '../../config' import { CartItem as CartItemModel, updateItem } from '../../helpers/cart' import { ChangeEvent, FC } from 'react' interface Props { product: CartItemModel setCart: (arg: CartItemModel[]) => void } const CartItem: FC<Props> = ({ product, setCart }) => { const handleChange = (event: ChangeEvent<HTMLInputElement>) => { const count = Math.max(parseInt(event.target.value), 1) setCart(updateItem(product._id, count)) } return ( <tr className="ant-table-row"> <td className="ant-table-cell"> <Image src={`${API}/product/photo/${product._id}`} width={120} alt={product.name} preview={false} /> </td> <td className="ant-table-cell">{product.name}</td> <td className="ant-table-cell">¥{product.price}</td> <td className="ant-table-cell">{product.category.name}</td> <td className="ant-table-cell"> <Input type="number" value={product.count} onChange={handleChange} /> </td> <td className="ant-table-cell"> <Button danger type="primary"> 删除 </Button> </td> </tr> ) } export default CartItem
// src\components\core\Cart.tsx import { Row, Col } from 'antd' import { useEffect, useState } from 'react' import { CartItem as CartItemModel, getCart } from '../../helpers/cart' import CartItem from './CartItem' import Layout from './Layout' const Cart = () => { const [cart, setCart] = useState<CartItemModel[]>([]) useEffect(() => { setCart(getCart()) }, []) const showCart = () => ( // 为了拆分组件,使用 Antd 的 Table 组件的结构,但不使用这个组件 <table style={{ width: '100%' }}> <thead className="ant-table-thead"> <tr> <th className="ant-table-cell">产品封面</th> <th className="ant-table-cell">产品名称</th> <th className="ant-table-cell">产品价格</th> <th className="ant-table-cell">产品分类</th> <th className="ant-table-cell">产品数量</th> <th className="ant-table-cell">操作</th> </tr> </thead> <tbody className="ant-table-tbody"> {cart.map(item => ( <CartItem key={item._id} setCart={setCart} product={item} /> ))} </tbody> </table> ) return ( <Layout title="购物车" subTitle=""> <Row gutter={16}> <Col span="16">{showCart()}</Col> <Col span="8"></Col> </Row> </Layout> ) } export default Cart
// src\helpers\cart.ts import { Product } from '../store/models/product' export interface CartItem extends Product { count: number } ... // 删除购物车中的产品 export const deleteItem = (productId: string) => { const cart: CartItem[] = getCart() const cartIndex = cart.findIndex(product => productId === product._id) if (cartIndex !== -1) { cart.splice(cartIndex, 1) localStorage.setItem('cart', JSON.stringify(cart)) } return cart }
// src\components\core\CartItem.tsx import { Button, Image, Input } from 'antd' import { API } from '../../config' import { CartItem as CartItemModel, deleteItem, updateItem } from '../../helpers/cart' import { ChangeEvent, FC } from 'react' interface Props { product: CartItemModel setCart: (arg: CartItemModel[]) => void } const CartItem: FC<Props> = ({ product, setCart }) => { const handleChange = (event: ChangeEvent<HTMLInputElement>) => { const count = Math.max(parseInt(event.target.value), 1) setCart(updateItem(product._id, count)) } return ( <tr className="ant-table-row"> <td className="ant-table-cell"> <Image src={`${API}/product/photo/${product._id}`} width={120} alt={product.name} preview={false} /> </td> <td className="ant-table-cell">{product.name}</td> <td className="ant-table-cell">¥{product.price}</td> <td className="ant-table-cell">{product.category.name}</td> <td className="ant-table-cell"> <Input type="number" value={product.count} onChange={handleChange} /> </td> <td className="ant-table-cell"> <Button danger type="primary" onClick={() => { setCart(deleteItem(product._id)) }} > 删除 </Button> </td> </tr> ) } export default CartItem
// src\components\core\TotalPrice.tsx import { Typography } from 'antd' import { FC, useEffect } from 'react' import { CartItem } from '../../helpers/cart' interface Props { cart: CartItem[] setTotalPrice: (price: number) => void } const { Title } = Typography const TotalPrice: FC<Props> = ({ cart, setTotalPrice }) => { const getTotalPrice = () => { return cart .reduce((total, cartItem) => { return total + cartItem.price * cartItem.count }, 0) .toFixed(2) } useEffect(() => { setTotalPrice(parseFloat(getTotalPrice())) }, [cart]) return <Title level={5}>商品总价:¥{getTotalPrice()}</Title> } export default TotalPrice
// src\components\core\Cart.tsx import { Row, Col, Input, Divider } from 'antd' import { ChangeEvent, useEffect, useState } from 'react' import { CartItem as CartItemModel, getCart } from '../../helpers/cart' import CartItem from './CartItem' import Layout from './Layout' import TotalPrice from './TotalPrice' const Cart = () => { const [cart, setCart] = useState<CartItemModel[]>([]) const [address, setAddress] = useState<string>('') const [totalPrice, setTotalPrice] = useState<number>(0) useEffect(() => { setCart(getCart()) }, []) const showCart = () => ( // 为了拆分组件,使用 Antd 的 Table 组件的结构,但不使用这个组件 <table style={{ width: '100%' }}> <thead className="ant-table-thead"> <tr> <th className="ant-table-cell">产品封面</th> <th className="ant-table-cell">产品名称</th> <th className="ant-table-cell">产品价格</th> <th className="ant-table-cell">产品分类</th> <th className="ant-table-cell">产品数量</th> <th className="ant-table-cell">操作</th> </tr> </thead> <tbody className="ant-table-tbody"> {cart.map(item => ( <CartItem key={item._id} setCart={setCart} product={item} /> ))} </tbody> </table> ) return ( <Layout title="购物车" subTitle=""> <Row gutter={16}> <Col span="16">{showCart()}</Col> <Col span="8"> <Row> <Input placeholder="请输入收货地址" value={address} onChange={(event: ChangeEvent<HTMLInputElement>) => setAddress(event.target.value)} /> </Row> <Divider /> <Row> <TotalPrice cart={cart} setTotalPrice={setTotalPrice} /> </Row> </Col> </Row> </Layout> ) } export default Cart
// src\components\core\Pay.tsx import { Button } from 'antd' import { Link } from 'react-router-dom' import { isAuth } from '../../helpers/auth' const Pay = () => { const showButton = () => { return isAuth() ? ( <Button>提交订单</Button> ) : ( <Button> <Link to="/signin">登录</Link> </Button> ) } return <>{showButton()}</> } export default Pay
// src\components\core\Cart.tsx
<Row>
<TotalPrice cart={cart} setTotalPrice={setTotalPrice} />
</Row>
<Row>
<Pay />
</Row>
// src\components\core\Success.tsx import { Button } from 'antd' import { Link } from 'react-router-dom' import Layout from './Layout' const Success = () => { return ( <Layout title="支付完成" subTitle=""> <Button> <Link to="/">继续购物</Link> </Button> </Layout> ) } export default Success
// src\Routes.tsx import { HashRouter, Route, Switch } from 'react-router-dom' import AddCategory from './components/admin/AddCategory' import AddProduct from './components/admin/AddProduct' import AdminDashboard from './components/admin/AdminDashboard' import AdminRoute from './components/admin/AdminRoute' import Dashboard from './components/admin/Dashboard' import PrivateRoute from './components/admin/PrivateRoute' import Cart from './components/core/Cart' import Home from './components/core/Home' import Product from './components/core/Product' import Shop from './components/core/Shop' import Signin from './components/core/Signin' import Signup from './components/core/Signup' import Success from './components/core/Success' const Routes = () => { return ( <HashRouter> <Switch> <Route path="/" component={Home} exact /> <Route path="/shop" component={Shop} /> <Route path="/signin" component={Signin} /> <Route path="/signup" component={Signup} /> <PrivateRoute path="/user/dashboard" component={Dashboard} /> <AdminRoute path="/admin/dashboard" component={AdminDashboard} /> <AdminRoute path="/create/category" component={AddCategory} /> <AdminRoute path="/create/product" component={AddProduct} /> <Route path="/product/:productId" component={Product} /> <Route path="/cart" component={Cart} /> <Route path="/paysuccess" component={Success} /> </Switch> </HashRouter> ) } export default Routes
localhost
),应该提供公网可以访问的地址,如本地服务的 IP,或部署服务端 API 的服务器的地址// src\components\core\Pay.tsx import { Button } from 'antd' import axios from 'axios' import { FC } from 'react' import { Link } from 'react-router-dom' import { API } from '../../config' import { isAuth } from '../../helpers/auth' import { CartItem } from '../../helpers/cart' import { Jwt } from '../../store/models/auth' interface Props { totalPrice: number address: string cart: CartItem[] } const Pay: FC<Props> = ({ totalPrice, address, cart }) => { const getPayUrl = () => { axios .post(`${API}/alipay`, { totalAmount: totalPrice, subject: '订单标题', body: '订单描述', // 测试时也要用 127.0.0.1 returnUrl: 'http://127.0.0.1:3000/#/paysuccess', notifyUrl: '<服务端地址>/api/alipayNotifyUrl', address: address, products: cart.map(item => ({ product: item._id, count: item.count })), userId: (isAuth() as Jwt).user._id }) .then(response => { window.location.href = response.data.url }) .catch(error => { console.error(error) }) } const showButton = () => { return isAuth() ? ( <Button onClick={getPayUrl}>提交订单</Button> ) : ( <Button> <Link to="/signin">登录</Link> </Button> ) } return <>{showButton()}</> } export default Pay
传递参数:
// src\components\core\Cart.tsx
<Pay totalPrice={totalPrice} address={address} cart={cart} />
// src\helpers\cart.ts
import { Product } from '../store/models/product'
...
// 获取购物车产品数量
export const itemCount = () => {
const cart: CartItem[] = getCart()
return cart.length
}
// src\components\core\Navigation.tsx import { Badge, Menu } from 'antd' import { Link } from 'react-router-dom' import { useSelector } from 'react-redux' import { AppState } from '../../store/reducers' import { RouterState } from 'connected-react-router' import { isAuth } from '../../helpers/auth' import { Jwt } from '../../store/models/auth' // 判断选中类名的钩子函数 function useActive(currentPath: string, path: string): string { return currentPath === path ? 'ant-menu-item-selected' : '' } const Navigation = () => { const router = useSelector<AppState, RouterState>(state => state.router) const pathname = router.location.pathname const isHome = useActive(pathname, '/') const isShop = useActive(pathname, '/shop') const isSignin = useActive(pathname, '/signin') const isSignup = useActive(pathname, '/signup') const isCart = useActive(pathname, '/cart') const isDashboard = useActive(pathname, getDashboardUrl()) function getDashboardUrl() { let url = '/user/dashboard' if (isAuth()) { const { user: { role } } = isAuth() as Jwt if (role === 1) { url = '/admin/dashboard' } } return url } return ( <Menu mode="horizontal" selectable={false}> <Menu.Item className={isHome}> <Link to="/">首页</Link> </Menu.Item> <Menu.Item className={isShop}> <Link to="/shop">商城</Link> </Menu.Item> <Menu.Item className={isCart}> <Link to="/cart"> 购物车 <Badge count={10} offset={[5, -10]} /> </Link> </Menu.Item> {!isAuth() && ( <> <Menu.Item className={isSignin}> <Link to="/signin">登录</Link> </Menu.Item> <Menu.Item className={isSignup}> <Link to="/signup">注册</Link> </Menu.Item> </> )} {isAuth() && ( <Menu.Item className={isDashboard}> <Link to={getDashboardUrl()}>dashboard</Link> </Menu.Item> )} </Menu> ) } export default Navigation
使用 createContext 创建共享状态的组件:
// src\anotherStore.tsx import React, { Dispatch, FC, SetStateAction, useState } from 'react' import { itemCount } from './helpers/cart' export const TotalContext = React.createContext<[number, Dispatch<SetStateAction<number>>]>([0, () => null]) interface Props { children: React.ReactNode[] | React.ReactNode } const AnotherStore: FC<Props> = ({ children }) => { const [count, setCount] = useState(itemCount()) return <TotalContext.Provider value={[count, setCount]}>{children}</TotalContext.Provider> } export default AnotherStore
包裹全部组件:
// src\index.tsx import React from 'react' import ReactDOM from 'react-dom' import 'antd/dist/antd.css' import './style.css' import Routes from './Routes' import { Provider } from 'react-redux' import store, { history } from './store' import { ConnectedRouter } from 'connected-react-router' import AnotherStore from './anotherStore' ReactDOM.render( <React.StrictMode> <Provider store={store}> <ConnectedRouter history={history}> <AnotherStore> <Routes /> </AnotherStore> </ConnectedRouter> </Provider> </React.StrictMode>, document.getElementById('root') )
// src\components\core\Navigation.tsx import { Badge, Menu } from 'antd' import { Link } from 'react-router-dom' import { useSelector } from 'react-redux' import { AppState } from '../../store/reducers' import { RouterState } from 'connected-react-router' import { isAuth } from '../../helpers/auth' import { Jwt } from '../../store/models/auth' import { useContext, useEffect } from 'react' import { TotalContext } from '../../anotherStore' import { itemCount } from '../../helpers/cart' // 判断选中类名的钩子函数 function useActive(currentPath: string, path: string): string { return currentPath === path ? 'ant-menu-item-selected' : '' } const Navigation = () => { const router = useSelector<AppState, RouterState>(state => state.router) const pathname = router.location.pathname const isHome = useActive(pathname, '/') const isShop = useActive(pathname, '/shop') const isSignin = useActive(pathname, '/signin') const isSignup = useActive(pathname, '/signup') const isCart = useActive(pathname, '/cart') const isDashboard = useActive(pathname, getDashboardUrl()) // 获取 anotherStore 中的状态 const [count, setCount] = useContext(TotalContext) // 每轮渲染后都获取购物车数量 useEffect(() => { setCount(itemCount()) }) function getDashboardUrl() { let url = '/user/dashboard' if (isAuth()) { const { user: { role } } = isAuth() as Jwt if (role === 1) { url = '/admin/dashboard' } } return url } return ( <Menu mode="horizontal" selectable={false}> <Menu.Item className={isHome}> <Link to="/">首页</Link> </Menu.Item> <Menu.Item className={isShop}> <Link to="/shop">商城</Link> </Menu.Item> <Menu.Item className={isCart}> <Link to="/cart"> 购物车 <Badge count={count} offset={[5, -10]} /> </Link> </Menu.Item> {!isAuth() && ( <> <Menu.Item className={isSignin}> <Link to="/signin">登录</Link> </Menu.Item> <Menu.Item className={isSignup}> <Link to="/signup">注册</Link> </Menu.Item> </> )} {isAuth() && ( <Menu.Item className={isDashboard}> <Link to={getDashboardUrl()}>dashboard</Link> </Menu.Item> )} </Menu> ) } export default Navigation
// src\helpers\cart.ts
...
// 清空购物车
export const clearCart = () => {
if (typeof window !== 'undefined') {
localStorage.removeItem('cart')
}
}
// src\components\core\Success.tsx import { Button } from 'antd' import { useContext, useEffect } from 'react' import { Link } from 'react-router-dom' import { TotalContext } from '../../anotherStore' import { clearCart } from '../../helpers/cart' import Layout from './Layout' const Success = () => { const [count, setCount] = useContext(TotalContext) useEffect(() => { clearCart() setCount(0) }) return ( <Layout title="支付完成" subTitle=""> <Button> <Link to="/">继续购物</Link> </Button> </Layout> ) } export default Success
// src\components\admin\Orders.tsx import Layout from '../core/Layout' const Orders = () => { return ( <Layout title="订单" subTitle="当前订单的数量是 10"> <Title level={4}>订单号:{order.out_trade_no}</Title> <table style={{ width: '100%' }}> <thead className="ant-table-thead"> <tr> <th className="ant-table-cell">订单状态</th> <th className="ant-table-cell">订单号</th> <th className="ant-table-cell">总价</th> <th className="ant-table-cell">创建时间</th> <th className="ant-table-cell">邮寄地址</th> <th className="ant-table-cell">客户姓名</th> </tr> </thead> <tbody className="ant-table-tbody"> <tr className="abt-table-row"> <td className="ant-table-cell"></td> <td className="ant-table-cell"></td> <td className="ant-table-cell"></td> <td className="ant-table-cell"></td> <td className="ant-table-cell"></td> <td className="ant-table-cell"></td> </tr> </tbody> </table> <table style={{ width: '100%' }}> <thead className="ant-table-thead"> <tr> <th className="ant-table-cell">产品名称</th> <th className="ant-table-cell">产品价格</th> <th className="ant-table-cell">产品数量</th> <th className="ant-table-cell">产品 ID</th> </tr> </thead> <tbody className="ant-table-tbody"> <tr className="abt-table-row"> <td className="ant-table-cell"></td> <td className="ant-table-cell"></td> <td className="ant-table-cell"></td> <td className="ant-table-cell"></td> </tr> </tbody> </table> </Layout> ) } export default Orders
// src\Routes.tsx import { HashRouter, Route, Switch } from 'react-router-dom' import AddCategory from './components/admin/AddCategory' import AddProduct from './components/admin/AddProduct' import AdminDashboard from './components/admin/AdminDashboard' import AdminRoute from './components/admin/AdminRoute' import Dashboard from './components/admin/Dashboard' import Orders from './components/admin/Orders' import PrivateRoute from './components/admin/PrivateRoute' import Cart from './components/core/Cart' import Home from './components/core/Home' import Product from './components/core/Product' import Shop from './components/core/Shop' import Signin from './components/core/Signin' import Signup from './components/core/Signup' import Success from './components/core/Success' const Routes = () => { return ( <HashRouter> <Switch> <Route path="/" component={Home} exact /> <Route path="/shop" component={Shop} /> <Route path="/signin" component={Signin} /> <Route path="/signup" component={Signup} /> <Route path="/product/:productId" component={Product} /> <Route path="/cart" component={Cart} /> <Route path="/paysuccess" component={Success} /> <PrivateRoute path="/user/dashboard" component={Dashboard} /> <AdminRoute path="/admin/dashboard" component={AdminDashboard} /> <AdminRoute path="/create/category" component={AddCategory} /> <AdminRoute path="/create/product" component={AddProduct} /> <AdminRoute path="/admin/orders" component={Orders} /> </Switch> </HashRouter> ) } export default Routes
// src\components\admin\AdminDashboard.tsx
<Link to="/admin/orders">订单列表</Link>
// src\store\models\order.ts import { User } from './auth' import { Product } from './product' export interface OrderProduct { _id: string count: number product: Product snapshot: Product } export interface Order { status: string _id: string out_trade_no: string trade_no: string amount: number address: string products: OrderProduct[] user: User createdAt: string }
// src\helpers\status.ts
// 订单状态
export const status: { [param: string]: string } = {
Unpaid: '未付款',
Paid: '已付款',
Shipped: '运输中',
Complete: '已完成',
Cancelle: '已取消'
}
// src\components\admin\Orders.tsx import { Divider, message, Select, Typography } from 'antd' import axios from 'axios' import moment from 'moment' import React, { useEffect, useState } from 'react' import { API } from '../../config' import { isAuth } from '../../helpers/auth' import { status } from '../../helpers/status' import { Jwt } from '../../store/models/auth' import { Order } from '../../store/models/order' import Layout from '../core/Layout' const { Title } = Typography const Orders = () => { const { token, user: { _id: userId } } = isAuth() as Jwt const [orders, setOrders] = useState([]) useEffect(() => { async function getOrders() { try { const response = await axios.get(`${API}/orders/${userId}`, { headers: { Authorization: `Bearer ${token}` } }) setOrders(response.data) } catch (error) { console.dir(error) message.error(error.response.data.errors[0]) } } getOrders() }, []) // 页面副标题 const getOrderCount = () => { if (orders.length > 0) { return `当前订单的数量是 ${orders.length}` } else { return `还没有订单` } } return ( <Layout title="订单" subTitle={getOrderCount()}> {orders.map((order: Order) => ( <React.Fragment key={order._id}> <Title level={4}>订单号:{order.out_trade_no}</Title> <table style={{ width: '100%' }}> <thead className="ant-table-thead"> <tr> <th className="ant-table-cell">订单状态</th> <th className="ant-table-cell">订单号</th> <th className="ant-table-cell">总价</th> <th className="ant-table-cell">创建时间</th> <th className="ant-table-cell">邮寄地址</th> <th className="ant-table-cell">客户姓名</th> </tr> </thead> <tbody className="ant-table-tbody"> <tr className="abt-table-row"> <td className="ant-table-cell">{status[order.status]}</td> <td className="ant-table-cell">{order.out_trade_no}</td> <td className="ant-table-cell">{order.amount}</td> <td className="ant-table-cell">{moment(order.createdAt).format('YYYY-MM-DD HH:mm:ss')}</td> <td className="ant-table-cell">{order.address}</td> <td className="ant-table-cell">{order.user.name}</td> </tr> </tbody> </table> <table style={{ width: '100%' }}> <thead className="ant-table-thead"> <tr> <th className="ant-table-cell">产品名称</th> <th className="ant-table-cell">产品价格</th> <th className="ant-table-cell">产品数量</th> <th className="ant-table-cell">产品 ID</th> </tr> </thead> <tbody className="ant-table-tbody"> {order.products.map(item => ( <tr key={item._id} className="abt-table-row"> <td className="ant-table-cell">{item.product.name}</td> <td className="ant-table-cell">{item.product.price}</td> <td className="ant-table-cell">{item.count}</td> <td className="ant-table-cell">{item.product._id}</td> </tr> ))} </tbody> </table> <Divider /> </React.Fragment> ))} </Layout> ) } export default Orders
// src\components\admin\Orders.tsx import { Divider, message, Select, Typography } from 'antd' import axios from 'axios' import moment from 'moment' import React, { useEffect, useState } from 'react' import { API } from '../../config' import { isAuth } from '../../helpers/auth' import { status } from '../../helpers/status' import { Jwt } from '../../store/models/auth' import { Order } from '../../store/models/order' import Layout from '../core/Layout' const { Title } = Typography const Orders = () => { const { token, user: { _id: userId } } = isAuth() as Jwt const [orders, setOrders] = useState([]) async function getOrders() { try { const response = await axios.get(`${API}/orders/${userId}`, { headers: { Authorization: `Bearer ${token}` } }) setOrders(response.data) } catch (error) { console.dir(error) message.error(error.response.data.errors[0]) } } useEffect(() => { getOrders() }, []) // 页面副标题 const getOrderCount = () => { if (orders.length > 0) { return `当前订单的数量是 ${orders.length}` } else { return `还没有订单` } } // 变更订单状态 const handleChange = (orderId: string) => (status: string) => { axios .put( `${API}/order/updateStatus/${userId}`, { orderId, status }, { headers: { Authorization: `Bearer ${token}` } } ) .then(() => { getOrders() }) } return ( <Layout title="订单" subTitle={getOrderCount()}> {orders.map((order: Order) => ( <React.Fragment key={order._id}> <Title level={4}>订单号:{order.out_trade_no}</Title> <table style={{ width: '100%' }}> <thead className="ant-table-thead"> <tr> <th className="ant-table-cell">订单状态</th> <th className="ant-table-cell">订单号</th> <th className="ant-table-cell">总价</th> <th className="ant-table-cell">创建时间</th> <th className="ant-table-cell">邮寄地址</th> <th className="ant-table-cell">客户姓名</th> </tr> </thead> <tbody className="ant-table-tbody"> <tr className="abt-table-row"> <td className="ant-table-cell"> <Select defaultValue={order.status} onChange={handleChange(order._id)}> {Object.entries(status).map(([value, label]) => ( <Select.Option key={value} value={value}> {label} </Select.Option> ))} </Select> </td> <td className="ant-table-cell">{order.out_trade_no}</td> <td className="ant-table-cell">{order.amount}</td> <td className="ant-table-cell">{moment(order.createdAt).format('YYYY-MM-DD HH:mm:ss')}</td> <td className="ant-table-cell">{order.address}</td> <td className="ant-table-cell">{order.user.name}</td> </tr> </tbody> </table> <table style={{ width: '100%' }}> <thead className="ant-table-thead"> <tr> <th className="ant-table-cell">产品名称</th> <th className="ant-table-cell">产品价格</th> <th className="ant-table-cell">产品数量</th> <th className="ant-table-cell">产品 ID</th> </tr> </thead> <tbody className="ant-table-tbody"> {order.products.map(item => ( <tr key={item._id} className="abt-table-row"> <td className="ant-table-cell">{item.product.name}</td> <td className="ant-table-cell">{item.product.price}</td> <td className="ant-table-cell">{item.count}</td> <td className="ant-table-cell">{item.product._id}</td> </tr> ))} </tbody> </table> <Divider /> </React.Fragment> ))} </Layout> ) } export default Orders
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。