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官网点击就能进去
总共由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>
)
}
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'>价 格</label>
<span className='sp2'>抢购价</span>
<span className='sp3'>¥{data.priceStr}</span>
</div>
<div className='graybottom'>
<div className='gragbotop'>
<label className='sp1'>促 销</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 私聊评论噢~