react商城项目(手把手教)


有些代码很丑陋,望评论区的大佬多指点一下

创建项目

1. npm install -g create-react-app 
2. create-react-app my-app   //新建并对react项目进行命名(注:项目名称不能有大写)
3. cd my-app
4. npm run start //后面可以自行更改

技术栈

框架用到了Antd,跨域是用到了插件http-proxy-middleware,react版本为17.0.2,react-router-dom为v6
如果需要启动 可以修改start ,音乐插件用的是网易云官方的node,数据为自己的node,需要开启两个服务端,需要node和源码的私聊

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "antd": "^4.17.4",
    "http-proxy-middleware": "^2.0.1",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-router-dom": "^6.0.2",
    "react-scripts": "4.0.3",
    "redux": "^4.1.2",
    "web-vitals": "^1.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "axios": "^0.24.0"
  }
}

跨域

这里我们用的是http-proxy-middleware,是需要用到require的 http-proxy-middleware可以配置多个代理,一开始是在packjson中配置跨域的,后来加了音乐的所以需要配置多个改用了http-proxy-middleware

安装

npm install http-proxy-middleware
const { createProxyMiddleware } = require('http-proxy-middleware')  // 需要{}包裹住 不然会报错

module.exports = function (app) {
    app.use(createProxyMiddleware('/api', {     // api
        target: 'http://127.0.0.1:1024/api',  // 服务端的url
        pathRewrite: {
            '^/api': '',
        },
        changeOrigin: true,          
        secure: false
    }));
    app.use(createProxyMiddleware('/music', { // music api
        target: 'http://127.0.0.2:3000/',   // 服务的的url 
        pathRewrite: {
            '^/music': '',
        },
        changeOrigin: true,         
        secure: false
    }));
}

扩展跨域

直接在package.json 中设置字段"proxy":“自己的url”
缺点是只能配置一个代理
在这里插入图片描述

路由

接触 router v6 我也又很多不明白的地方,因为是随便写的项目所以起名字随意了点,基本配置就是这样的

import React from "react";
import { BrowserRouter, Route, Routes, useParams } from "react-router-dom";
import Home from "../test/home";
import Zhuce from "../test/zhuce";
import Login from '../test/login'
import Detail from '../test/detail'
import Create from "../test/crete";
import Fenlei from "../test/fenlei";
import Shopping from "../test/shop";
import Search from "../test/search";
import NotFound from "../test/notfound";


export default function Router() {

    let use = useParams()

    console.log(use)

    return (
        <BrowserRouter>
            {/* 使用 Routes 替换 Switch  */}
            <Routes>
                <Route path='/' element={<Home />} />   
                <Route path='/zhuce' element={<Zhuce />} />
                <Route path='/login' element={<Login />} />
                <Route path='/detail/:id' element={<Detail />} />
                <Route path='/create/:value' element={<Create />} />
                <Route path='/fenlei/:value' element={<Fenlei />} />
                <Route path='/shopcart' element={<Shopping />} />
                <Route path='/search/:value' element={<Search />} />
                <Route path="*" element={<NotFound />} />         //404页面
            </Routes>
        </BrowserRouter>
    );
}

Antd

Antd官网点击就能进去

页面

总共由9个页面组成,所以项目是比较简单的,一个简易的移动端商城,接下来是准备贴图片与代码,与大家一起学习,优化

首页

首先头部和音乐播放器是所有页面都需要的,所以头部需要做两个个全局组件,其次是分类,分类是通过接口获取的数据,轮播图也是通过接口获取的数据,内容也是通过接口获取的数据
在这里插入图片描述
头部组件

/* eslint-disable no-unused-vars */
/* eslint-disable jsx-a11y/alt-text */
import React, { useState, useEffect } from "react";
import '../header/header.css'
import { Input, Space, Badge, message } from 'antd';
import { useNavigate, useLocation } from "react-router-dom";
import axios from "axios";
import Audio from '../header/audio';


const { Search } = Input;


export default function Header(props) {



    let user = JSON.parse(localStorage.getItem('user')) ? JSON.parse(localStorage.getItem('user')) : '' //这里判断是存在user

    let loc = useLocation() 

    const onSearch = (value) => {
     console.log(value);
     navigate(`/search/${value}`)   // 搜索的跳转
     window.location.reload()       // 丑陋的代码来了,因为找不到router找不到location.key所以用刷新代替了
    }
    const navigate = useNavigate();
    let [num, setnum] = useState(0)

    const gozhuce = () => {
        navigate('/zhuce')   //去注册界面
    }
    const godenglu = () => {
        navigate('/login')  //去登录界面
    }
    const goshouye = () => {
        navigate('/')     //去首页
    }
    const goshop = () => {
        navigate('/shopcart')  // 去购物车
    }
    const tuichu = () => {
        message.success('退出成功')
        localStorage.removeItem('user')  // 退出登录
        window.location.reload()
        navigate('/')
    }
     //获取购物车数量的值
    useEffect(() => {                         
        axios.get('/api/shopList', {
            params: {
                token: user.token
            }
        }).then(res => {
            if (res.data.length > 0) {
                let a = res.data.map(item => item.count).reduce((pre, cur) => {
                    return pre + cur
                })
                setnum(a)                           
            }
        })
        if (props.rea === true) {
            setnum(props.num)
        }
        console.log(loc)
    }, [loc, props.num, props.rea, user.token])




    let username = ''
    let isshow = 'block'
    let isshows = 'none'

    // 判断user的值是否存在
    if (JSON.parse(localStorage.getItem('user'))) {
        username = JSON.parse(localStorage.getItem('user')).username + `欢迎你`
        isshow = 'none'
        isshows = 'block'
    } else {
        username = '未登录,请先登录'
        isshow = 'block'
        isshows = 'none'
    }

    return (
        <div className="divs" key={loc.key}>
            <Audio />
            <ul>
                <li><img src="https://res0.vmallres.com/shopdc//pic/20211021/0b8c9044-df36-48d4-a25c-7bd23f5c7898.png"></img></li>
                <li><span onClick={goshouye}>首页</span></li>
                <li><span onClick={godenglu} >{username}</span></li>
                <li><Badge count={num} className="bad"><span onClick={goshop}>购物车</span></Badge></li>
                <li><span onClick={gozhuce}>注册</span></li>
                <li style={{ display: isshow }}><span onClick={godenglu} >登录</span></li>
                <li style={{ display: isshows }}><span onClick={tuichu} >退出</span></li>
            </ul>
            <div className="inputs">
                <Space direction="vertical">
                    <Search placeholder="输入内容" onSearch={onSearch} enterButton />
                </Space>
            </div>
        </div>
    )

}

音乐组件

音乐组件做的有点丑,输入想听的歌之后回车就出来列表,点击哪个就能听那个
在这里插入图片描述

/* eslint-disable jsx-a11y/alt-text */
import axios from 'axios'
import React, { useState, useRef, useEffect } from 'react'
import '../header/audio.css'
import { Input, message } from 'antd';


const wid = {
    height: '500px'
}
const wids = {
    height: '0px'
}


export default function Audio() {



    let [data, setdata] = useState([])
    let doms = useRef({});  //doms.current 可以找到audio
    let [datas, setdatas] = useState(localStorage.getItem('music'))
    let [cont, setcont] = useState(false)



    const searchs = (value) => {
        axios.get('/music/search?', {
            params: {
                keywords: value.target.value    //atnd中 键盘enter的事件
            }
        }).then(res => {
            console.log(res)
            if (res.data.code === 200) {
                setcont(true)
                setTimeout(() => {
                    setdata(res.data.result.songs.slice(0, 10))  //因为动画要做到同步所以需要定时器
                }, 200);
            }
        })
    }


    const getsrc = (ids) => {
        console.log(ids)
        axios.get('/music/song/url?', {
            params: {
                id: ids
            }
        }).then(res => {
            if (res.data.code === 200) {
                setdatas(res.data.data[0].url)
                localStorage.setItem('music', res.data.data[0].url)  
                localStorage.setItem('time', 0)
                setTimeout(() => {
                    setcont(false)
                    setdata([])
                }, 5000);
            } else {
                message.error('加载失败')
            }
        })
    }


 // 刷新页面需要记住当前播放的时间
    useEffect(() => {
        doms.current.currentTime = localStorage.getItem('time') ? localStorage.getItem('time') : 0

    }, [])
    //刷新页面需要记住当前播放的时间
    doms.current.ontimeupdate = function () {
        if (localStorage.getItem('time')) {
            localStorage.setItem('time', doms.current.currentTime)
        }
    }



    return (
        <div className='audiobox'  >
            <Input placeholder='你想听什么' onPressEnter={searchs}></Input>
            <audio controls src={datas} ref={doms}> // audio 设置ref 需要找到这个dom元素
            </audio>
            <div className='audiobot' style={cont === false ? wids : wid}>
                {
                    data.map((item, index) => {
                        return <div className='bot' onClick={() => getsrc(item.id)} key={index}>
                            <span>{item.name}</span>
                            <span>{item.artists[0].name}</span>
                        </div>
                    })
                }
            </div>
        </div>
    )
}

首页内容

在这里插入图片描述

/* eslint-disable no-undef */
/* eslint-disable no-unused-vars */
/* eslint-disable jsx-a11y/alt-text */
/* eslint-disable react-hooks/rules-of-hooks */
import React from 'react';
// import Request from '../axios/index'
import axios from 'axios'
import { Carousel } from 'antd';
import '../test/home.css'
import Header from '../header/header';
import { Spin } from 'antd'
import { BackTop } from 'antd';
import { Link } from 'react-router-dom';

const imgs = {
    width: '100%',
    height: '550px'
}
export default class Home extends React.Component {
    constructor() {
        super()
        this.state = {
            gogo: [],
            title: 'http://127.0.0.1:1024/api',
            fenlei: [],
            tods: [],
            len: 24,
            loadings: true,
            load: true
        }
    }


// 数据的懒加载
    handleScroll = () => {
        let clientHeight = document.body.clientHeight
        let scrollTop = document.documentElement.scrollTop; //滚动条滚动高度
        let scrollHeight = document.documentElement.scrollHeight;
        let height = scrollHeight - scrollTop - clientHeight
        if (height === 0) {   //当height为0时就是滚动条到最底部的时候
            this.setState({
                loadings: true,
                load: true
            })
            console.log('1');
            let timer = null;
            clearTimeout(timer);
            if (!timer) {
                timer = setTimeout(() => {
                    axios.get('/api/hotList').then(ress => {
                        this.setState({
                            loadings: false,
                            load: false
                        })
                        // 这里实现懒加载
                        const _len = this.state.len + 24
                        this.setState({
                            len: _len,
                            tods: ress.data.slice(0, _len)
                        })
                    })
                }, 1500);
            }
        }
    }


    componentDidMount() {
        axios.get('/api/banner').then(res => {
            this.setState({
                gogo: res.data
            })
        })
        axios.get('/api/getTypeOne').then(ress => {
            this.setState({
                fenlei: ress.data
            })
        })
        axios.get('/api/hotList').then(ress => {
            this.setState({
                tods: ress.data.slice(0, 24)
            })
        })
        window.addEventListener('scroll', this.handleScroll);
        setTimeout(() => {
            this.setState({
                loadings: false,
                load: false
            })
        }, 2000);
    }
    render() {
        return (
            <div>
                <BackTop />
                <Header />
                <div className='ulsbox'>
                    <ul className='uls'>
                        {
                            this.state.fenlei.map((item, index) => {
                                return <Link to={{ pathname: `/fenlei/${item}` }} key={index}><li  >{item}</li></Link>
                            })
                        }
                    </ul>
                </div>
                <Carousel autoplay >
                    {
                        this.state.gogo.map((item, index) => {
                            // eslint-disable-next-line jsx-a11y/alt-text
                            return <div key={index}><img src={this.state.title + item} style={imgs} ></img></div>
                        })
                    }
                </Carousel>
                <div className='bigbox'>
                    <Spin spinning={this.state.loadings}>
                        <div className='remen'>
                            <div className='pad10'><span className='remenspan'>热门商品</span></div>
                            <div className='remenbox'>
                                {
                                    this.state.tods.map((item, index) => {

                                        return <Link to={{ pathname: `/detail/${item.Id}` }} key={item.Id}>
                                            <div className='smallbox'>
                                                <Spin spinning={this.state.load}><img src={item.imageUrl}></img></Spin>
                                                <h4>{item.title}</h4>
                                                <p>{item.salePoint}</p>
                                                <span>{item.priceStr}</span>
                                            </div>
                                        </Link>
                                    })
                                }
                            </div>
                        </div>
                    </Spin>
                </div>
            </div >
        )
    }
}

注册

注册和登录没什么好说的就点击注册时候的传值

在这里插入图片描述

import React, { useState } from "react";
import Header from "../header/header";
import '../test/zhuce.css'
import axios from 'axios'
import { Form, Input, Button, message } from 'antd'
import { useNavigate } from "react-router-dom";



export default function Zhuce() {

    const navigate = useNavigate();

    let [count, setCount] = useState(3);
    let [dis, setdis] = useState('block')
    let [diss, setdiss] = useState('none')

    const dianji = (value) => {
        axios.get('/api/register', {
            params: {
                userName: value.username,
                password: value.password
            }
        }).then(res => {
            if (res.data.code === 1) {
                let timer = null
                timer = setInterval(() => {
                    let nun = count--
                    setCount(nun)
                    if (count === 0) {
                        navigate('/login')
                        clearInterval(timer)
                    }
                    console.log(count)
                }, 1000);
                setTimeout(() => {
                    setdis('none')
                    setdiss('block')
                }, 500);
                message.success(res.data.data);
            } else {
                // eslint-disable-next-line no-unused-expressions
                message.error(res.data.data)
            }
        })
    }

    return (
        <div className='body'>
            <Header />
            <div className="bodybox">
                <div className="box" style={{ display: dis }}>
                    <Form
                        name="basic"
                        labelCol={{
                            span: 8,
                        }}
                        wrapperCol={{
                            span: 16,
                        }}
                        initialValues={{
                            remember: true,
                        }}
                        autoComplete="off"
                        onFinish={dianji}
                    >
                        <Form.Item
                            label="用户名"
                            name="username"
                            rules={[
                                {
                                    required: true,
                                    message: '请输入用户名',
                                },
                            ]}
                        >
                            <Input placeholder="请输入用户名" />
                        </Form.Item>

                        <Form.Item
                            label="密码"
                            name="password"
                            rules={[
                                {
                                    required: true,
                                    message: '请输入密码',
                                },
                            ]}
                        >
                            <Input.Password placeholder="请输入密码" />
                        </Form.Item>

                        <Form.Item
                            name="confirm"
                            label="确认密码"
                            dependencies={['password']}
                            hasFeedback
                            rules={[
                                {
                                    required: true,
                                    message: 'Please confirm your password!',
                                },
                                ({ getFieldValue }) => ({
                                    validator(_, value) {
                                        if (!value || getFieldValue('password') === value) {
                                            return Promise.resolve();
                                        }
                                        return Promise.reject(new Error('The two passwords that you entered do not match!'));
                                    },
                                }),
                            ]}
                        >
                            <Input.Password placeholder="请输入密码" />
                        </Form.Item>

                        <Form.Item
                            wrapperCol={{
                                offset: 8,
                                span: 16,
                            }}
                        >
                            <Button type="primary" htmlType="submit" >
                                点击注册
                            </Button>
                        </Form.Item>
                    </Form>
                </div>
                <div><span className="spans" style={{ display: diss }}>...跳转至登录界面</span></div>
            </div>
        </div>
    )
}

登录

和注册一样传input的值
在这里插入图片描述

import React from "react";
import Header from "../header/header";
import '../test/login.css'
import { Form, Input, Button, message } from 'antd'
import axios from "axios";
import { useNavigate } from "react-router-dom";

export default function Login() {
   
    const navigate = useNavigate()

    const dianji = (value) => {
        axios.get('/api/login', {
            params: {
                userName: value.username,
                password: value.password
            }
        }).then(res => {
            console.log(res)
            if (res.data.code === 1) {
                let user = {
                    username: res.data.userName,
                    token: res.data.token
                   }
                let users  = JSON.stringify(user)
                localStorage.setItem('user', users)
                message.success('登录成功')
                navigate('/')
            } else {
                message.error('账号或密码错误')
            }
        })
    }


    return (
        <div className='body'>
            <Header />
            <div className="bodybox">
                <div className="box" >
                    <Form
                        name="basic"
                        labelCol={{
                            span: 8,
                        }}
                        wrapperCol={{
                            span: 16,
                        }}
                        initialValues={{
                            remember: true,
                        }}
                        autoComplete="off"
                        onFinish={dianji}
                    >
                        <Form.Item
                            label="用户名"
                            name="username"
                            rules={[
                                {
                                    required: true,
                                    message: '请输入用户名',
                                },
                            ]}
                        >
                            <Input placeholder="请输入用户名" />
                        </Form.Item>

                        <Form.Item
                            label="密码"
                            name="password"
                            rules={[
                                {
                                    required: true,
                                    message: '请输入密码',
                                },
                            ]}
                        >
                            <Input.Password placeholder="请输入密码" />
                        </Form.Item>

                        <Form.Item
                            wrapperCol={{
                                offset: 8,
                                span: 16,
                            }}
                        >
                            <Button type="primary" htmlType="submit" >
                                点击登录
                            </Button>
                        </Form.Item>
                    </Form>
                </div>

            </div>
        </div>
    )
}

一级分类和二级分类

点击一级分类后出现二级分类,再次点击二级分类出现各分类的商品
在这里插入图片描述

/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable no-unused-vars */
/* eslint-disable jsx-a11y/alt-text */
import React, { useState, useEffect } from "react";
import '../test/fenlei.css'
import Header from "../header/header";
import { useParams, useNavigate } from "react-router-dom";
import axios from "axios";
import { BackTop, Spin } from 'antd';

export default function Fenlei() {
    let par = useParams();
    let nav = useNavigate()
    let [data, setdata] = useState([])
    let [datas, setdatas] = useState([])
    let [shop, setshop] = useState('')
    let [type, settype] = useState([])
    let [is, setis] = useState(0)
    let [loads, setloads] = useState(true)
    let [load, setload] = useState(true)

    useEffect(() => {
        axios.get('/api/getTypeOne').then(res => {
            setdata(res.data)
        })
    }, [])

    useEffect(() => {
        axios.get('/api/getTypeTwo', {
            params: {
                type_one: par.value
            }
        }).then(res => {
            setdatas(res.data)
            axios.get('/api/getTypeTwoList', {
                params: {
                    type_one: par.value,
                    type_two: res.data[0]
                }
            }).then(ress => {
                settype(ress.data)
                setshop(res.data[0])
                setloads(false)
                setload(false)
            })
        })
    }, [])

    useEffect(() => {
        axios.get('/api/goodList', {
            params: {
                type_one: par.value
            }
        }).then(res => {
            console.log(res)
        })
    }, [])


    const myindex = (value, indexs) => {
        setload(true)
        console.log(value, indexs)
        axios.get('/api/getTypeTwoList', {
            params: {
                type_one: par.value,
                type_two: value
            }
        }).then(res => {
            settype(res.data)
            setshop(value)
            setis(indexs)
            setTimeout(() => {
                setload(false)
            }, 500);
        })
    }
    const myfenlei = (value) => {
        nav(`/fenlei/${value}`)
        window.location.reload()
    }
    const godetail = (value) => {
        nav(`/detail/${value}`)
    }




    return (
        <div>
            <Header />
            <BackTop />
            <Spin spinning={loads}>     <div className='ulsbox'>
                <ul className='uls'>
                    {
                        data.map((item, index) => {
                            return <li key={index} onClick={() => myfenlei(item)}>{item}</li>
                        })
                    }
                </ul>
                <div className="flexbox" >
                    {
                        datas.map((item, index) => {
                            return <div key={index} className="flexback" onClick={() => myindex(item, index)}><span className={is === index ? 'hover' : 'hover2'} >{item}</span></div>
                        })
                    }
                </div>
                <Spin spinning={load} tip='正在加载...'>
                    <div className='remen'>
                        <div className='pad10'><span className='remenspan'>{shop}</span></div>
                        <div className='remenbox' >
                            {
                                type.map((item, index) => {
                                    return <div className='smallbox' key={index} onClick={() => godetail(item.Id)}>
                                        <img src={item.imageUrl}></img>
                                        <h4>{item.title}</h4>
                                        <p>{item.salePoint}</p>
                                        <span>{item.priceStr}</span>
                                    </div>
                                })
                            }
                        </div>
                    </div>
                </Spin>

            </div></Spin>

        </div>
    )
}

详情页

详情页包含了放大镜 加入购物车等功能,来详细看看
在这里插入图片描述
放大镜思路
设置放大后图片的大小,page为初始图片的大小

    let [left, setleft] = useState(0)
    let [top, settop] = useState(0)
    let [n,setn] = useState(0)

// 基本配置
    const page = {
        sclc: 2,
        width: 530,
        height: 400,
    }

 //放大后图片的样式
    const bigmore = {
        width: `${page.width * page.sclc}px`,
        height: `${page.height * page.sclc}px`,
        position: 'absolute',
        left: `-${left}px`,
        top: `-${top}px`,
    }

    const one = {
        display: 'block'
    }
    const two = {
        display: 'none'
    }
    <div className='fangdabox'>
                        <div className='fangdajing'>
                            <img src={imgs[num]} className='bigimg' onMouseEnter={boxshow} onMouseLeave={boxnone} onMouseMove={boxs} ref={doms}></img>
                            <ul className='fangdajingul'>
                                {
                                    imgs.map((item, index) => {
                                        return <li key={index}><img src={item} className='smallimg' onClick={() => changeimg(index)}
                                        ></img></li>
                                    })
                                }
                            </ul>

                            <div className='fangdabox' style={shows === true ? one : two}><img src={imgs[num]} style={bigmore}></img></div>
                        </div>
    const boxshow = (event) => { 
        setshows(true)
    }
    const boxnone = () => {
        setshows(false)
    }

    const boxs = (event) => {   //鼠标移动
        let e = event 
        let scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
        let scrollY = document.documentElement.scrollTop || document.body.scrollTop;
        var x = e.pageX || e.clientX + scrollX;
        var y = e.pageY || e.clientY + scrollY;
        setleft(x - doms.current.getBoundingClientRect().x)
        settop(y - doms.current.getBoundingClientRect().y)
    }

整体代码

import React, { useEffect, useState, useRef } from 'react';
import { useParams, useNavigate,useLocation  } from "react-router-dom";
import Header from '../header/header';
import axios from 'axios';
import '../test/detail.css'
import { InputNumber, Tabs, BackTop, message, Spin } from 'antd';

const { TabPane } = Tabs;




export default function Detail() {
    const par = useParams()
    const loc = useLocation()
    let nav = useNavigate()
    let nums = {}
    let [data, setdata] = useState('')
    let [imgs, setimg] = useState([])
    let [num, setnum] = useState(0);
    let [st, setst] = useState(false)
    let [stss, setstss] = useState(false)
    let [datas, setdatas] = useState([])
    let [imgtwo, setimgtwo] = useState([])
    let [guige, setguige] = useState([])
    let [pingjia, setpingjia] = useState([])
    let [values, setvalue] = useState(1);
    let [loadings, setloadings] = useState(true)
    let [shows, setshows] = useState(false)
    let doms = useRef(null);
    let [left, setleft] = useState(0)
    let [top, settop] = useState(0)
    let [n,setn] = useState(0)

    const page = {
        sclc: 2,
        width: 530,
        height: 400,
    }

    const bigmore = {
        width: `${page.width * page.sclc}px`,
        height: `${page.height * page.sclc}px`,
        position: 'absolute',
        left: `-${left}px`,
        top: `-${top}px`,
    }

    const one = {
        display: 'block'
    }
    const two = {
        display: 'none'
    }




    useEffect(async () => {
        await axios.get('/api/detail', {
            params: {
                goodId: par.id
            }
        }).then(res => {
            if (res.status === 200) {
                setloadings(false)
            }
            nums = res.data[0]
            setdata(nums)
            let num = nums.imgs.replace('[', '')
            let numb = num.replace(']', '')
            let ser = numb.replace(/\"/g, "");
            let numbs = ser.split(',')
            setimg(numbs.slice(1))

            let imgs = nums.descriptionImage.replace('[', '')
            let imgss = imgs.replace(']', '')
            let imgser = imgss.replace(/\"/g, "");
            let imgsers = imgser.split(',')
            setimgtwo(imgsers)

            const arr = JSON.parse(nums.description.replace(/\\n/g, '')).map(item => {
                item.title = item.title.trim()
                item.text = item.text.trim()
                return item

            });
            let arrs = JSON.parse(nums.comment)
            setguige(arr)
            setpingjia(arrs)
        })
    }, [])




    useEffect(() => {
        setTimeout(() => {
            axios.get('/api/sameList', {
                params: {
                    supplier: data.supplier
                }
            }).then(res => {
                setdatas(res.data)
            })
        }, 1);
    }, [data])
    // 发送两次请求


    const changeimg = (indexs) => {
        setnum(indexs)
    }
    const sts = () => {
        setst(!st)
    }
    const sea = () => {
        setstss(!stss)
    }
    const onChange = (value) => {
        setvalue(value);
    }
    const godetails = (ids) => {
        nav(`/detail/${ids}`)
        window.location.reload()
        // 使用了强制刷新
    }
    const goshouye = () => {
        nav('/');
    }
    const callback = (value) => {
        console.log(value)
    }
    const gocreate = (value) => {
        console.log(value)
        nav(`/create/${value}`)
    }
    const goshopping = () => {
        let user = JSON.parse(localStorage.getItem('user'))
        console.log(user)
        axios.get('/api/add', {
            params: {
                goodId: data.Id,
                token: user.token,
                count: values
            }
        }).then(res => {
            console.log(res)
            if (res.data.code === 1) {
                message.success('加入购物车成功')
            } else {
                message.error('加入购物车失败')
            }
            axios.get('/api/shopList', {
                params: {
                    token: user.token
                }
            }).then(res => {
                if(res.data.length>0){
                    let a  = res.data.map(item=>item.count).reduce((pre, cur) => {
                      return  pre+cur
                    })
                    setn(a)
                   }
            })
        })
    }
    const boxshow = (event) => {
        setshows(true)
    }
    const boxnone = () => {
        setshows(false)
    }

    const boxs = (event) => {
        let e = event
        let scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
        let scrollY = document.documentElement.scrollTop || document.body.scrollTop;
        var x = e.pageX || e.clientX + scrollX;
        var y = e.pageY || e.clientY + scrollY;
        setleft(x - doms.current.getBoundingClientRect().x)
        settop(y - doms.current.getBoundingClientRect().y)
    }




    return (
        <div className='body' >
            <Header num={n} rea={true}/>
            <BackTop />
            <Spin spinning={loadings}>
                <div className='bigboxs'>
                    <div>
                        <ul>
                            <li><span onClick={goshouye}>首页</span></li>
                            <li>{'>'}</li>
                            <li><span onClick={() => gocreate(data.supplier)}>{data.supplier}</span></li>
                            <li>{'>'}</li>
                            <span className='left'>{data.title}</span>
                        </ul>
                    </div>
                    <div className='fangdabox'>
                        <div className='fangdajing'>
                            <img src={imgs[num]} className='bigimg' onMouseEnter={boxshow} onMouseLeave={boxnone} onMouseMove={boxs} ref={doms}></img>
                            <ul className='fangdajingul'>
                                {
                                    imgs.map((item, index) => {
                                        return <li key={index}><img src={item} className='smallimg' onClick={() => changeimg(index)}
                                        ></img></li>
                                    })
                                }
                            </ul>

                            <div className='fangdabox' style={shows === true ? one : two}><img src={imgs[num]} style={bigmore}></img></div>
                        </div>
                        <div className='bigboxsright'>
                            <span className='rigthspan'>{data.salePoint}</span>
                            <a href='https://blog.csdn.net/qq_54566021?spm=1001.2101.3001.5343'>12.25特惠预订】①预订立省100元 ②享3期免息丨点击立即前往预订</a>
                            <div className='graybox'>
                                <div className='graytop'>
                                    <label className='sp1'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
                                    <span className='sp2'>抢购价</span>
                                    <span className='sp3'>{data.priceStr}</span>
                                </div>
                                <div className='graybottom'>
                                    <div className='gragbotop'>
                                        <label className='sp1'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
                                        <div className='tag'>限时特价</div>
                                        <span className='tagspan'>限时直降50</span>
                                    </div>
                                    <div className='gragbotoptwo'>
                                        <div className='tag'>以旧换新</div>
                                        <span className='tagspan'>
                                            以旧换新最高补贴1212</span>
                                    </div>
                                    <div className='gragbotoptwo'>
                                        <div className='tag'>分期免息</div>
                                        <span className='tagspan'>
                                            银联、花呗、掌上生活、工行分期支付可享免息(免息活动适用于单款免息商品订单,含多款商品订单仅在免息活动一致时可享用) </span>
                                    </div>
                                    <div className='gragbotoptwo'>
                                        <div className='tag'>赠送积分</div>
                                        <span className='tagspan'>
                                            购买即赠商城积分,积分可抵现~ </span>
                                    </div>
                                </div>
                            </div>
                            <div className='bianma'>
                                <span>商品编码</span>
                                <span className='sd'>2901010033402</span>
                            </div>
                            <div className='bianma'>
                                <span>保障服务</span>
                                <div className={[st === false ? "bao2" : "bao1"]} onClick={sts}>每年100</div>
                                <div className={[st === false ? "bao1" : "bao2"]} onClick={sts}>每年200</div>
                            </div>
                            <div className='bianma'>
                                <span>分期服务</span>
                                <div className={[stss === false ? "bao2" : "bao1"]} onClick={sea}>6</div>
                                <div className={[stss === false ? "bao1" : "bao2"]} onClick={sea}>12</div>
                            </div>
                            <div className='jisuanqi'>
                                <InputNumber min={1} max={999} defaultValue={1} onChange={onChange} size="large" />
                                <button onClick={goshopping}>加入购物车</button>
                                <div className='look' onClick={() => gocreate(data.supplier)}>进店看看</div>
                            </div>
                        </div>
                    </div>
                    <div className='samebox'>
                        <div className='sameboxtop'><span>同类商品</span></div>
                        <div className='sameboxbottom'>
                            {
                                datas.map((item, index) => {
                                    return <div className='smallbox' onClick={() => godetails(item.Id)} key={item.Id}>
                                        <img src={item.imageUrl}></img>
                                        <h4>{item.title}</h4>
                                        <p>{item.salePoint}</p>
                                        <span>{item.priceStr}</span>
                                    </div>
                                })
                            }
                        </div>
                    </div>
                    <Tabs onChange={callback} type="card">
                        <TabPane tab="商品详情" key="1">
                            {
                                imgtwo.map((item, index) => {
                                    return <div className='detailshops' key={index}>
                                        <img src={item}></img>
                                    </div>
                                })
                            }
                        </TabPane>
                        <TabPane tab="商品规格" key="2">
                            <div className='guige'>
                                <span className='span1'>主要参数</span>
                                {
                                    guige.map((item, index) => {
                                        return <div key={index} >
                                            <span className='span2'>{item.title}</span>
                                            <span className='span3'>{item.text}</span>
                                        </div>
                                    })
                                }
                            </div>
                        </TabPane>
                        <TabPane tab="用户评价" key="3">
                            <div className='pingjia'>
                                {
                                    pingjia.map((item, index) => {
                                        return <div key={index} className='pingjiabox'>
                                            <div className='top'>
                                                <img src={item.userPic}></img>
                                                <div className='yuan'>{item.vip}</div>
                                                <span>用户名:{item.userName}</span>
                                            </div>
                                            <div className='bot'>
                                                <div className='botbox'>
                                                    <span className='one'>{item.text}</span>
                                                    <span className='two'>{item.time}</span>
                                                </div>
                                                <span className='nams'>{item.product}</span>
                                            </div>
                                        </div>
                                    })
                                }
                            </div>
                        </TabPane>
                    </Tabs>
                </div>
            </Spin>
        </div>
    )
}

制造商

通过点击的value 可以获得制造商的商品

/* eslint-disable jsx-a11y/alt-text */
import React, { useEffect, useState } from "react";
import Header from "../header/header";
import { useParams, useNavigate } from "react-router-dom";
import '../test/create.css'
import axios from "axios";


export default function Create(){
   
    let nav = useNavigate()
     let par  = useParams()
     
     let [datas,setdatas] = useState([])
     console.log(par)

    const goshouye = ()=>{
        nav(-1)
    }
    const godetails = (value)=>{
        nav(`/detail/${value}`)
    }

    useEffect(()=>{
        axios.get('/sameList',{params:{
            supplier:par.value
        }}).then(res=>{
            setdatas(res.data)
        })
    },[])

    return(
        <div>
            <Header />
            <div className="createbox">
                <ul>
                <li><span onClick={goshouye}>返回</span></li>
                        <li>{'>'}</li> 
                        <li>{par.value}</li> 
                </ul>
                <div className='samebox'>
                    <div className='sameboxbottom'>
                        {
                            datas.map((item, index) => {
                                return <div className='smallbox' onClick={() => godetails(item.Id)} key={item.Id}>
                                    <img src={item.imageUrl}></img>
                                    <h4>{item.title}</h4>
                                    <p>{item.salePoint}</p>
                                    <span>{item.priceStr}</span>
                                </div>
                            })
                        }
                    </div>
                </div>
            </div>
        </div>
    )
}

购物车代码就不贴啦 需要源码 node 私聊评论噢~

  • 7
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 32
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oomsday

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值