• react--电商商品列表使用


    目录

    整体页面效果

    项目技术点

    拦截器的配置

    主页面

    添加商品

    分页,搜索

    修改商品

    删除商品

    完整代码


    整体页面效果

    项目技术点

    • antd组件库,@ant-design/icons antd的图标库
    • axios 接口请求,拦截器配置
    • node-sass  sass-loader css样式的一个嵌套
    • react-router-dom  react路由使用
    • react-redux    redux
    • hooks:大多数我们用的是函数组件,函数组件没有state属性,所以我们使用hooks来初始化数据,并且函数组件没有生命周期

    拦截器的配置

    由于我们登录成功之后,需要我们获取到token令牌之后,我们才能获取到数据,如果每个页面都需要获取一次token,代码比较啰嗦,所以我们配置了一个拦截器

    需要授权的 API ,必须在请求头中使用 Authorization 字段提供 token 令牌

    1. //配置拦截器
    2. import axios from "axios";
    3. //2. 创建对象
    4. const Server = axios.create({
    5. baseURL:"http://api.xiaohuihui0728.cn:8888/api/private/v1/",//基地址
    6. timeout:5000,
    7. })
    8. //3. 请求拦截器
    9. Server.interceptors.request.use((config)=>{
    10. //前置拦截器请求发送出去之前触发
    11. console.log(config);
    12. //增加一个token值
    13. let token = sessionStorage.getItem("token");
    14. config.headers["Authorization"] = token;
    15. return config;
    16. },(err)=>Promise.reject(err));
    17. //4. 响应拦截器
    18. Server.interceptors.response.use((response)=>{
    19. //请求成功,服务端返回数据到客户端之前触发
    20. return response.data;
    21. },(err)=>Promise.reject(err))
    22. //5.抛出Serve对象的内容
    23. export default Server;

    主页面

    我们的数据写在columns里面,在里面的dataIndex绑定我们获取到的数据

    1. const columns = [
    2. {
    3. title: '商品名称',
    4. dataIndex: 'goods_name',
    5. key: 'username',
    6. },
    7. {
    8. title: '商品价格',
    9. dataIndex: 'goods_price',
    10. key: 'email',
    11. },
    12. {
    13. title: '商品重量',
    14. dataIndex: 'goods_weight',
    15. key: 'mobile',
    16. },
    17. {
    18. title: '创建时间',
    19. dataIndex: 'goods_state',
    20. key: 'role_name',
    21. },
    22. {
    23. title: '操作',
    24. key: 'action',
    25. render: (_, record) => {
    26. return (
    27. <>
    28. <Button type="primary" size="small" onClick={() => { showedit(record.goods_id) }}> <EditOutlined /> </Button> &nbsp;
    29. <Button type="primary" size="small" danger onClick={() => { delGoods(record.goods_id) }} > <DeleteOutlined /> </Button> &nbsp;
    30. </>
    31. )
    32. }
    33. }
    34. ];
    35. const [userData, setUserData] = useState([])
    36. // 初始化数据
    37. useEffect(() => {
    38. getUserData()
    39. setPage(1)
    40. }, [search, page, pageSize])
    41. // 初始化请求数据 用户
    42. const getUserData = async () => {
    43. console.log(pageSize);
    44. console.log(search);
    45. console.log(page);
    46. let { data } = await axios.get(`goods?pagenum=${page}&query=${search}&pagesize=${pageSize}`)
    47. console.log(data.goods);
    48. if (data) {
    49. setTotal(data.total);
    50. setUserData(data.goods);
    51. }
    52. }
    53. <Table pagination={false} bordered dataSource={userData} columns={columns} rowKey={(record) => record.goods_id} />

    添加商品

    添加弹出对话框,添加里面有一个上传图片,我们上传的图片有一个单独的添加接口,所以我们使用action属性绑定我们要上传的路径,headres获取token,使用onChange获取图片上传的路径,然后在我们点击提交form表单数据时把图片临时地址添加成功 

    1. // 添加弹窗状态
    2. const [addIsModalVisible, setAddIsModalVisible] = useState(false);
    3. // 存图片
    4. let [img,setimg] = useState("");
    5. // 添加取消
    6. const addHandleCancel = () => {
    7. setAddIsModalVisible(false);
    8. };
    9. //获取token
    10. let token = sessionStorage.getItem("token");
    11. // 添加商品
    12. const onAdd = async (value)=>{
    13. console.log(value);
    14. let pics = [{pic:img}];
    15. let data= await axios.post("goods",{...value,pics})
    16. setAddIsModalVisible(false);
    17. getUserData()
    18. }
    19. //图片上传
    20. const success = (info)=>{
    21. if(info.file.status==="done"){
    22. console.log(info.file.response.data.tmp_path);
    23. setimg(info.file.response.data.tmp_path)
    24. }
    25. }
    26. <Modal title="商品添加" visible={addIsModalVisible} footer={null} ref={addFormRef}>
    27. <Form
    28. name="basic"
    29. labelCol={{ span: 4 }}
    30. wrapperCol={{ span: 16 }}
    31. initialValues={{ remember: true }}
    32. onFinish={onAdd}
    33. onFinishFailed={onFinishFailed}
    34. autoComplete="off"
    35. >
    36. <Form.Item
    37. label="商品名称"
    38. name="goods_name"
    39. rules={[{ required: true, message: '请输入商品名称' }]}
    40. >
    41. <Input />
    42. </Form.Item>
    43. <Form.Item
    44. label="商品价格"
    45. name="goods_price"
    46. rules={[{ required: true, message: '请输入商品价格' }]}
    47. >
    48. <Input />
    49. </Form.Item>
    50. <Form.Item
    51. label="商品数量"
    52. name="goods_number"
    53. rules={[{ required: true, message: '请输入商品数量' }]}
    54. >
    55. <Input />
    56. </Form.Item>
    57. <Form.Item
    58. label="商品重量"
    59. name="goods_weight"
    60. rules={[{ required: true, message: '请输入商品重量' }]}
    61. >
    62. <Input />
    63. </Form.Item>
    64. <Form.Item
    65. label="商品图片"
    66. name="pics"
    67. >
    68. <Upload {...props} action='http://api.xiaohuihui0728.cn:8888/api/private/v1/upload'
    69. headers={{"Authorization":token}} onChange={success} listType='picture'>
    70. <Button type='primary'>上传图片</Button>
    71. </Upload>
    72. </Form.Item>
    73. <Form.Item wrapperCol={{ ...layout.wrapperCol, offset: 16 }}>
    74. <Button type="primary" htmlType="submit" >
    75. Submit
    76. </Button>
    77. &nbsp;
    78. <Button type="primary" onClick={() => { (addHandleCancel()) }} danger>
    79. 取消
    80. </Button>
    81. </Form.Item>
    82. </Form>
    83. </Modal>
    84. <Button type="primary" onClick={showModal}> <EditOutlined /> 添加商品 </Button>

    分页,搜索

    我们在获取数据的时候,就获取我们的分页条数和总数以及搜索的关键字,然后在分页中进行数据属性的配置即可

    1. // 搜索
    2. let [search, setSearch] = useState("")
    3. // 总条数
    4. let [total, setTotal] = useState(0)
    5. // 当前页
    6. let [page, setPage] = useState(1)
    7. // 每页条数
    8. let [pageSize, setPageSize] = useState(2)
    9. // 搜索
    10. const onSearch = (val) => {
    11. setSearch(val)
    12. }
    13. // 分页
    14. const onChange = (page, pageSize) => {
    15. setPage(page)
    16. setPageSize(pageSize)
    17. }
    18. // 初始化请求数据 用户
    19. const getUserData = async () => {
    20. console.log(pageSize);
    21. console.log(search);
    22. console.log(page);
    23. let { data } = await axios.get(`goods?pagenum=${page}&query=${search}&pagesize=${pageSize}`)
    24. console.log(data.goods);
    25. if (data) {
    26. setTotal(data.total);
    27. setUserData(data.goods);
    28. }
    29. }
    30. <Search allowClear placeholder="input search text" onSearch={onSearch} enterButton style={{ width: 300, height: 60 }} />
    31. <Pagination
    32. className='pagination'
    33. total={total}
    34. showSizeChanger
    35. showQuickJumper
    36. pageSizeOptions={[10, 20, 30]}
    37. defaultPageSize={2}
    38. showTotal={(total) => `Total ${total} items`}
    39. onChange={onChange}
    40. />

    修改商品

    我们在点击修改弹出对话框的同时,需要把数据绑定到输入框内,我们利用 formRef.current.setFieldsValue进行数据回填,然后请求修改接口即可

    1. // 修改弹出框
    2. const [isModalVisible, setIsModalVisible] = useState(false);
    3. // 修改弹出框,数据回填
    4. const showedit = async (e) => {
    5. setIsModalVisible(true);
    6. setgoodid(e)
    7. let { data } = await axios.get(`goods/${e}`)
    8. console.log(data);
    9. formRef.current.setFieldsValue({
    10. goods_name: data.goods_name,
    11. goods_price: data.goods_price,
    12. goods_number: data.goods_number,
    13. goods_weight: data.goods_weight
    14. })
    15. };
    16. // 修改取消按钮
    17. const editHandleCancel = () => {
    18. setIsModalVisible(false)
    19. }
    20. // 修改商品
    21. const onFinish = async (values) => {
    22. let { meta } = await axios.put(`goods/${goodid}`, {
    23. goods_name: values.goods_name,
    24. goods_number: values.goods_number,
    25. goods_price: values.goods_price,
    26. goods_weight: values.goods_weight
    27. })
    28. // console.log(data);
    29. if (meta.status === 200) {
    30. message.success(meta.msg)
    31. setIsModalVisible(false);
    32. getUserData()
    33. } else {
    34. message.info(meta.msg)
    35. setIsModalVisible(false);
    36. }
    37. };
    38. <Button type="primary" size="small" onClick={() => { showedit(record.goods_id) }}> <EditOutlined /> </Button>
    39. {/* 修改弹出框 */}
    40. <Modal title="修改商品" visible={isModalVisible} footer={null}>
    41. <Form
    42. name="basic"
    43. labelCol={{ span: 4 }}
    44. wrapperCol={{ span: 16 }}
    45. initialValues={{ remember: true }}
    46. onFinish={onFinish}
    47. onFinishFailed={onFinishFailed}
    48. autoComplete="off"
    49. {...layout}
    50. ref={formRef}
    51. >
    52. <Form.Item
    53. label="商品名称"
    54. name="goods_name"
    55. rules={[{ required: true, message: '请输入商品名称' }]}
    56. >
    57. <Input />
    58. </Form.Item>
    59. <Form.Item
    60. label="商品价格"
    61. name="goods_price"
    62. rules={[{ required: true, message: '请输入商品价格' }]}
    63. >
    64. <Input />
    65. </Form.Item>
    66. <Form.Item
    67. label="商品数量"
    68. name="goods_number"
    69. rules={[{ required: true, message: '请输入商品数量' }]}
    70. >
    71. <Input />
    72. </Form.Item>
    73. <Form.Item
    74. label="商品重量"
    75. name="goods_weight"
    76. rules={[{ required: true, message: '请输入商品重量' }]}
    77. >
    78. <Input />
    79. </Form.Item>
    80. <Form.Item wrapperCol={{ ...layout.wrapperCol, offset: 16 }}>
    81. <Button type="primary" htmlType="submit">
    82. Submit
    83. </Button>
    84. &nbsp;
    85. <Button type="primary" onClick={() => { (editHandleCancel()) }} danger>
    86. 取消
    87. </Button>
    88. </Form.Item>
    89. </Form>
    90. </Modal>

    删除商品

    点击删除按钮传递一个id,然后请求删除接口即可

    1. // 删除
    2. const delGoods = async (e) => {
    3. console.log(e);
    4. let { meta } = await axios.delete(`goods/${e}`)
    5. switch (meta.status) {
    6. case 200:
    7. message.success(meta.msg)
    8. break;
    9. default:
    10. message.warning("删除失败")
    11. break;
    12. }
    13. getUserData()
    14. }
    15. <Button type="primary" size="small" danger onClick={() => { delGoods(record.goods_id) }} > <DeleteOutlined /> </Button>

    完整代码

    1. import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
    2. import { Button, Card, Form, Input, message, Modal, Pagination, Table, Upload } from 'antd';
    3. import React, { useEffect, useRef, useState } from 'react';
    4. import axios from '../utils/request';
    5. import './goods.scss';
    6. const { Search } = Input;
    7. const props = {
    8. name: 'file',
    9. action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
    10. headers: {
    11. authorization: 'authorization-text',
    12. }
    13. }
    14. export default function Goods() {
    15. let formRef = useRef()
    16. let addFormRef = useRef(null)
    17. const [userData, setUserData] = useState([])
    18. // 添加弹窗状态
    19. const [addIsModalVisible, setAddIsModalVisible] = useState(false);
    20. // 存图片
    21. let [img,setimg] = useState("");
    22. // 搜索
    23. let [search, setSearch] = useState("")
    24. // 总条数
    25. let [total, setTotal] = useState(0)
    26. // 当前页
    27. let [page, setPage] = useState(1)
    28. // 修改弹出框
    29. const [isModalVisible, setIsModalVisible] = useState(false);
    30. // 每页条数
    31. let [pageSize, setPageSize] = useState(2)
    32. const [goodid, setgoodid] = useState(0)
    33. const showModal = () => {
    34. setAddIsModalVisible(true);
    35. };
    36. // 修改商品
    37. const onFinish = async (values) => {
    38. let { meta } = await axios.put(`goods/${goodid}`, {
    39. goods_name: values.goods_name,
    40. goods_number: values.goods_number,
    41. goods_price: values.goods_price,
    42. goods_weight: values.goods_weight
    43. })
    44. // console.log(data);
    45. if (meta.status === 200) {
    46. message.success(meta.msg)
    47. setIsModalVisible(false);
    48. getUserData()
    49. } else {
    50. message.info(meta.msg)
    51. setIsModalVisible(false);
    52. }
    53. };
    54. const onFinishFailed = (errorInfo) => {
    55. console.log('Failed:', errorInfo);
    56. };
    57. // 修改弹出框,数据回填
    58. const showedit = async (e) => {
    59. setIsModalVisible(true);
    60. setgoodid(e)
    61. let { data } = await axios.get(`goods/${e}`)
    62. console.log(data);
    63. formRef.current.setFieldsValue({
    64. goods_name: data.goods_name,
    65. goods_price: data.goods_price,
    66. goods_number: data.goods_number,
    67. goods_weight: data.goods_weight
    68. })
    69. };
    70. // 初始化数据
    71. useEffect(() => {
    72. getUserData()
    73. setPage(1)
    74. }, [search, page, pageSize])
    75. // 添加
    76. const layout = { labelCol: { span: 4 }, wrapperCol: { span: 20 } };
    77. // 添加取消
    78. const addHandleCancel = () => {
    79. setAddIsModalVisible(false);
    80. };
    81. // 修改取消按钮
    82. const editHandleCancel = () => {
    83. setIsModalVisible(false)
    84. }
    85. // 删除
    86. const delGoods = async (e) => {
    87. console.log(e);
    88. let { meta } = await axios.delete(`goods/${e}`)
    89. switch (meta.status) {
    90. case 200:
    91. message.success(meta.msg)
    92. break;
    93. default:
    94. message.warning("删除失败")
    95. break;
    96. }
    97. getUserData()
    98. }
    99. // 初始化请求数据 用户
    100. const getUserData = async () => {
    101. console.log(pageSize);
    102. console.log(search);
    103. console.log(page);
    104. let { data } = await axios.get(`goods?pagenum=${page}&query=${search}&pagesize=${pageSize}`)
    105. console.log(data.goods);
    106. if (data) {
    107. setTotal(data.total);
    108. setUserData(data.goods);
    109. }
    110. }
    111. const columns = [
    112. {
    113. title: '商品名称',
    114. dataIndex: 'goods_name',
    115. key: 'username',
    116. },
    117. {
    118. title: '商品价格',
    119. dataIndex: 'goods_price',
    120. key: 'email',
    121. },
    122. {
    123. title: '商品重量',
    124. dataIndex: 'goods_weight',
    125. key: 'mobile',
    126. },
    127. {
    128. title: '创建时间',
    129. dataIndex: 'goods_state',
    130. key: 'role_name',
    131. },
    132. {
    133. title: '操作',
    134. key: 'action',
    135. render: (_, record) => {
    136. return (
    137. <>
    138. <Button type="primary" size="small" onClick={() => { showedit(record.goods_id) }}> <EditOutlined /> </Button> &nbsp;
    139. <Button type="primary" size="small" danger onClick={() => { delGoods(record.goods_id) }} > <DeleteOutlined /> </Button> &nbsp;
    140. </>
    141. )
    142. }
    143. }
    144. ];
    145. // 搜索
    146. const onSearch = (val) => {
    147. setSearch(val)
    148. }
    149. // 分页
    150. const onChange = (page, pageSize) => {
    151. setPage(page)
    152. setPageSize(pageSize)
    153. }
    154. let token = sessionStorage.getItem("token");
    155. // 添加商品
    156. const onAdd = async (value)=>{
    157. console.log(value);
    158. let pics = [{pic:img}];
    159. let data= await axios.post("goods",{...value,pics})
    160. setAddIsModalVisible(false);
    161. getUserData()
    162. }
    163. const success = (info)=>{
    164. if(info.file.status==="done"){
    165. console.log(info.file.response.data.tmp_path);
    166. setimg(info.file.response.data.tmp_path)
    167. }
    168. }
    169. return (
    170. <div className='goods'>
    171. {/* 主体表格区域 */}
    172. <Card >
    173. <Search allowClear placeholder="input search text" onSearch={onSearch} enterButton style={{ width: 300, height: 60 }} />
    174. &nbsp;
    175. <Button type="primary" onClick={showModal}> <EditOutlined /> 添加商品 </Button>
    176. <Table pagination={false} bordered dataSource={userData} columns={columns} rowKey={(record) => record.goods_id} />
    177. <Pagination
    178. className='pagination'
    179. total={total}
    180. showSizeChanger
    181. showQuickJumper
    182. pageSizeOptions={[10, 20, 30]}
    183. defaultPageSize={2}
    184. showTotal={(total) => `Total ${total} items`}
    185. onChange={onChange}
    186. />
    187. </Card>
    188. <Modal title="商品添加" visible={addIsModalVisible} footer={null} ref={addFormRef}>
    189. <Form
    190. name="basic"
    191. labelCol={{ span: 4 }}
    192. wrapperCol={{ span: 16 }}
    193. initialValues={{ remember: true }}
    194. onFinish={onAdd}
    195. onFinishFailed={onFinishFailed}
    196. autoComplete="off"
    197. >
    198. <Form.Item
    199. label="商品名称"
    200. name="goods_name"
    201. rules={[{ required: true, message: '请输入商品名称' }]}
    202. >
    203. <Input />
    204. </Form.Item>
    205. <Form.Item
    206. label="商品价格"
    207. name="goods_price"
    208. rules={[{ required: true, message: '请输入商品价格' }]}
    209. >
    210. <Input />
    211. </Form.Item>
    212. <Form.Item
    213. label="商品数量"
    214. name="goods_number"
    215. rules={[{ required: true, message: '请输入商品数量' }]}
    216. >
    217. <Input />
    218. </Form.Item>
    219. <Form.Item
    220. label="商品重量"
    221. name="goods_weight"
    222. rules={[{ required: true, message: '请输入商品重量' }]}
    223. >
    224. <Input />
    225. </Form.Item>
    226. <Form.Item
    227. label="商品图片"
    228. name="pics"
    229. >
    230. <Upload {...props} action='http://api.xiaohuihui0728.cn:8888/api/private/v1/upload'
    231. headers={{"Authorization":token}} onChange={success} listType='picture'>
    232. <Button type='primary'>上传图片</Button>
    233. </Upload>
    234. </Form.Item>
    235. <Form.Item wrapperCol={{ ...layout.wrapperCol, offset: 16 }}>
    236. <Button type="primary" htmlType="submit" >
    237. Submit
    238. </Button>
    239. &nbsp;
    240. <Button type="primary" onClick={() => { (addHandleCancel()) }} danger>
    241. 取消
    242. </Button>
    243. </Form.Item>
    244. </Form>
    245. </Modal>
    246. {/* 修改弹出框 */}
    247. <Modal title="修改商品" visible={isModalVisible} footer={null}>
    248. <Form
    249. name="basic"
    250. labelCol={{ span: 4 }}
    251. wrapperCol={{ span: 16 }}
    252. initialValues={{ remember: true }}
    253. onFinish={onFinish}
    254. onFinishFailed={onFinishFailed}
    255. autoComplete="off"
    256. {...layout}
    257. ref={formRef}
    258. >
    259. <Form.Item
    260. label="商品名称"
    261. name="goods_name"
    262. rules={[{ required: true, message: '请输入商品名称' }]}
    263. >
    264. <Input />
    265. </Form.Item>
    266. <Form.Item
    267. label="商品价格"
    268. name="goods_price"
    269. rules={[{ required: true, message: '请输入商品价格' }]}
    270. >
    271. <Input />
    272. </Form.Item>
    273. <Form.Item
    274. label="商品数量"
    275. name="goods_number"
    276. rules={[{ required: true, message: '请输入商品数量' }]}
    277. >
    278. <Input />
    279. </Form.Item>
    280. <Form.Item
    281. label="商品重量"
    282. name="goods_weight"
    283. rules={[{ required: true, message: '请输入商品重量' }]}
    284. >
    285. <Input />
    286. </Form.Item>
    287. <Form.Item wrapperCol={{ ...layout.wrapperCol, offset: 16 }}>
    288. <Button type="primary" htmlType="submit">
    289. Submit
    290. </Button>
    291. &nbsp;
    292. <Button type="primary" onClick={() => { (editHandleCancel()) }} danger>
    293. 取消
    294. </Button>
    295. </Form.Item>
    296. </Form>
    297. </Modal>
    298. </div>
    299. )
    300. }

  • 相关阅读:
    VUE+Spring Boot前后端分离开发实战(三):基于vue+spirngboot实现后台通用管理系统框架
    多人音视频实时通讯架构
    MEMS制造的基本工艺——晶圆键合工艺
    数据结构知识点补充
    H5判断当前环境是否为微信小程序
    Fast semi-supervised learning with anchor graph for large
    VRChat 2024年裁员原因与背景深度分析
    git-仓库迁移并保留commit log
    这些国外客户真直接
    day09-1存储引擎
  • 原文地址:https://blog.csdn.net/qq_60976312/article/details/126399717