您当前的位置: 首页 > 

wespten

暂无认证

  • 1浏览

    0关注

    899博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

react-umi_dva项目

wespten 发布时间:2020-05-02 17:51:22 ,浏览量:1

29.9React课程

第11节:umi_Dva路由及项目实现

(第11节:umi-Dva路由及项目实现&)

第11节:umi-Dva路由及项目实现&

项目创建

创建页面

创建目录结构

 router.js

import React from 'react';
import { Router as DefaultRouter, Route, Switch } from 'react-router-dom';
import dynamic from 'umi/dynamic';
import renderRoutes from 'umi/lib/renderRoutes';
import history from '@tmp/history';
import { routerRedux } from 'dva';

const Router = routerRedux.ConnectedRouter;

const routes = [
  {
    path: '/',
    component: require('../../layouts/index.js').default,
    routes: [
      {
        path: '/404',
        exact: true,
        component: require('../404.js').default,
      },
      {
        path: '/about',
        exact: true,
        component: require('../about.js').default,
        title: 'about page',
        Routes: [
          require('../../routes/PrivateRoute.js').default,
          require('../../routes/Test.js').default,
        ],
      },
      {
        path: '/goods',
        exact: true,
        component: require('../goods/index.js').default,
      },
      {
        path: '/goods',
        exact: true,
        component: require('../goods.js').default,
      },
      {
        path: '/',
        exact: true,
        component: require('../index.js').default,
      },
      {
        path: '/login',
        exact: true,
        component: require('../login.js').default,
      },
      {
        path: '/users',
        exact: false,
        component: require('../users/_layout.js').default,
        routes: [
          {
            path: '/users',
            exact: true,
            component: require('../users/index.js').default,
          },
          {
            path: '/users/:id',
            exact: true,
            component: require('../users/$id.js').default,
          },
          {
            component: () =>
              React.createElement(
                require('/Users/kele/.config/yarn/global/node_modules/umi-build-dev/lib/plugins/404/NotFound.js')
                  .default,
                { pagesPath: 'pages', hasRoutesInConfig: false },
              ),
          },
        ],
      },
      {
        path: '/:post/commit',
        exact: true,
        component: require('../$post/commit.js').default,
      },
      {
        path: '/:post',
        exact: true,
        component: require('../$post/index.js').default,
      },
      {
        component: () =>
          React.createElement(
            require('/Users/kele/.config/yarn/global/node_modules/umi-build-dev/lib/plugins/404/NotFound.js')
              .default,
            { pagesPath: 'pages', hasRoutesInConfig: false },
          ),
      },
    ],
  },
  {
    component: () =>
      React.createElement(
        require('/Users/kele/.config/yarn/global/node_modules/umi-build-dev/lib/plugins/404/NotFound.js')
          .default,
        { pagesPath: 'pages', hasRoutesInConfig: false },
      ),
  },
];
window.g_routes = routes;
const plugins = require('umi/_runtimePlugin');
plugins.applyForEach('patchRoutes', { initialValue: routes });

export { routes };

export default class RouterWrapper extends React.Component {
  unListen() {}

  constructor(props) {
    super(props);

    // route change handler
    function routeChangeHandler(location, action) {
      plugins.applyForEach('onRouteChange', {
        initialValue: {
          routes,
          location,
          action,
        },
      });
    }
    this.unListen = history.listen(routeChangeHandler);
    routeChangeHandler(history.location);
  }

  componentWillUnmount() {
    this.unListen();
  }

  render() {
    const props = this.props || {};
    return {renderRoutes(routes, props)};
  }
}

带$前缀的文件或路径为动态路由

创建users目录,页面是动态路由

$id.js

import styles from "./$id.css";

export default function(props) {
  const { match } = props;
  console.log(match);
  return (
    
Page $id:: {match.params.id}
); }

$post

commit.js

2号用户进入留言页

嵌套路由

_layout.js

props.children显示子内容插槽


import styles from './_layout.css';

export default function(props) {
  return (
    
Layout for ./users { props.children }
); }

两个组件都会共享外面的layout

全局layout,约定src/layouts/index.js为全局路由,在pages文件夹外面创建layout

layouts/index.js,共享布局,login有自己的布局不要共享全局的布局

import styles from "./index.css";
import { Layout } from "antd";
import { Menu } from "antd";

const { Header, Footer, Content } = Layout;
import Link from "umi/link";
export default function(props) {
  const { location } = props;
  if (location.pathname === "/login") {
    return {props.children};
  }
  return (
    
      {/* 页头 */}
      
        {/* 新增内容 */}
        
        { lineHeight: "64px", float: "left" }}
        
          
            商品
          
          
            用户
          
          
            关于
          
        
      
      {/* 内容 */}
      
        
{props.children}
{/* 页脚 */} 开课吧 ); // const { location } = props; // if (location.pathname === "/login") { // return {props.children}; // } // return ( //
// Layout for / // {props.children} //
// ); }

全局layouts包含所有的页面

(404页面&)

404页面&

404.js

import { Exception } from "ant-design-pro";
export default function() {
  return ;
}

扩展路由

多了一个title属性

配置式路由与约定式路由是冲突的

config/config.js ,根路径不再是index而是abouts

export default {
  //   routes: [{ path: "/", component: "./about" }]
  plugins: [
    [
      "umi-plugin-react",
      {
        dva: true,
        antd: true
      }
    ]
  ]
};

routes的默认路径都在pages中

路由守卫,增加routes字段格式是一个数组,pages下所有约定式路由全部失效

pages/about.js yml 数组是-

/*
 *
 * title: "about page"
 * Routes:
 *  - ./routes/PrivateRoute.js
 *  - ./routes/Test.js
 *
 *
 *
 */

import styles from "./about.less";

export default function() {
  return (
    
Page about
); }

routes/PrivateRoute.js

export default props => {
  return (
    
PrivateRoute Page {props.children}
); };

routes/Test.js

export default props => {
  return 
test page {props.children}
; };

自动生成配置式路由,路由组件让about.js组件包含PrivateRoute组件,设置路由守卫

最外面是PrivateRoute,里面是testRoute,最里面是

(配置configu路由&)

配置configu路由&

pages/index.js

import styles from "./index.css";
import Link from "umi/link";
export default function() {
  return (
    
Page index 登录
); }

pages/login.js

import React, { Component } from "react";
// import { Button } from "antd";
import styles from "./login.css";
import router from "umi/router";
import { Login } from "ant-design-pro";

import { connect } from "dva";

const { UserName, Password, Submit } = Login; // 通用的用户名、密码和提交组件

export default connect()(function(props) {
  // let from = props.location.state.from || "/"; // 重定向地址
  const onSubmit = (err, values) => {
    console.log("用户输入:", values);
    if (!err) {
      // 校验通过,提交登录
      props.dispatch({ type: "user/login", payload: values });
    }
  };
  return (
    
{/* logo */} {/* 登录表单 */} 登录
); });

(mock数据&)

mock数据&

 

时候mock.js来模拟生成随机数据

import mockjs from "mockjs";

export default {
  "GET /api/users": { users: [1, 2] },
  "GET /api/users/create": (req, res) => {
    setTimeout(() => {
      res.send("Ok");
    }, 1000);
  },
  "GET /api/tags": mockjs.mock({
    "list|100": [{ name: "@city", "value|1-100": 50, "type|0-2": 1 }]
  }),
  //动态的mock
  "/api/random": (req, res) => {
    res.setHeader("Access-Control-Allow-Origin", "*");
    res.send(
      mockjs.mock({
        // 每次请求均产生随机值
        "number|1-100": 100
      })
    );
  }
};

mock/goods.js

let data = [{ title: "web全栈" }, { title: "java架构师" }];

export default {
  "GET /api/goods": function(req, res) {
    setTimeout(() => {
      res.json({ result: data });
    }, 1000);
  }
};

添加跨域请求头,在别的服务请求mock的数据

允许跨域操作

import mockjs from "mockjs";

export default {
  "GET /api/users": { users: [1, 2] },
  "GET /api/users/create": (req, res) => {
    setTimeout(() => {
      res.send("Ok");
    }, 1000);
  },
  "GET /api/tags": mockjs.mock({
    "list|100": [{ name: "@city", "value|1-100": 50, "type|0-2": 1 }]
  }),
  //动态的mock
  "/api/random": (req, res) => {
    res.setHeader("Access-Control-Allow-Origin", "*");
    res.send(
      mockjs.mock({
        // 每次请求均产生随机值
        "number|1-100": 100
      })
    );
  }
};

umi插件集

config/config.js 开启插件,开启dva

export default {
  //   routes: [{ path: "/", component: "./about" }]
  plugins: [
    [
      "umi-plugin-react",
      {
        dva: true,
        antd: true
      }
    ]
  ]
};

(Dva&)

Dva&

 

models/goods.js 通过namespace来区分,不能直接修改state,通过reducers来修改state

export default {
  namespace: "goods",
  state: {
    // 初始状态包括课程和分类
    courses: {}, // 课程
    tags: [] // 分类
  },
  effects: {
    *getList(action, { call, put }) {
      // 解构出courseData并初始化状态
      const {
        data: { data: courseData }
      } = yield call(getGoods);
      yield put({ type: "initGoods", payload: courseData });
    }
  },
  reducers: {
    initGoods(state, { payload }) {
      // 解构出tags和courses并返回
      const { tags, data: courses } = payload;
      return { ...state, tags, courses };
    }
  }
};

models/users.js

localStorage本地存储

import axios from "axios";
import router from "umi/router";

// 初始状态:本地缓存或空值对象
const userinfo = JSON.parse(localStorage.getItem("userinfo")) || {
  token: "",
  role: "",
  username: "",
  balance: 0
};

// 登录请求方法
function login(payload) {
  return axios.post("/api/login", payload);
}

export default {
  namespace: "user", // 可省略
  state: userinfo,
  effects: {
    // action: user/login
    *login({ payload }, { call, put }) {
      const {
        data: { code, data: userinfo }
      } = yield call(login, payload);
      if (code == 0) {
        // 登录成功: 缓存用户信息
        localStorage.setItem("userinfo", JSON.stringify(userinfo));
        yield put({ type: "init", payload: userinfo });
        router.push("/");
      } else {
        // 登录失败:弹出提示信息,可以通过响应拦截器实现
      }
    }
  },
  reducers: {
    init(state, action) {
      // 覆盖旧状态
      return action.payload;
    }
  }
};
import mockjs from "mockjs";

export default {
  "GET /api/users": { users: [1, 2] },
  "GET /api/users/create": (req, res) => {
    setTimeout(() => {
      res.send("Ok");
    }, 1000);
  },
  "GET /api/tags": mockjs.mock({
    "list|100": [{ name: "@city", "value|1-100": 50, "type|0-2": 1 }]
  }),
  //动态的mock
  "/api/random": (req, res) => {
    res.setHeader("Access-Control-Allow-Origin", "*");
    res.send(
      mockjs.mock({
        // 每次请求均产生随机值
        "number|1-100": 100
      })
    );
  }
};

组件里使用model通过connect

pages/goods.js goods页面,把状态映射到组件里

第一个参数映射属性返回一个state对象,通过state.goods(namespace)取到对应的state

function() goodsList已经映射到props中,直接解构{goodLists}

函数式组件在页面渲染前发送请求通过Hooks

connect状态映射到组件,payload参数传递,hooks函数式组件处理副作用,dva-loading

import styles from "./goods.css";

import { connect } from "dva";
import { useEffect } from "react";

export default connect(
  state => ({
    goodList: state.goods,
    usersList: state.users,
    loading: state.loading
  }),
  {
    addGood: title => {
      return {
        type: "goods/addGoods",
        payload: { title }
      };
    },
    getList: () => {
      return {
        type: "goods/getList"
      };
    }
  }
)(function({ goodList, usersList, addGood, getList, loading }) {
  // console.log(usersList[0].name);
  useEffect(() => {
    getList();
  }, []);
  if (loading.models.goods) {
    return 
加载中...
; } return (
Page goods
    {goodList.map((item, index) => { return
  • {item.title}
  • ; })}
{ addGood("商品" + new Date().getTime()); }} > 添加商品
); });

mock/goods.js

let data = [{ title: "web全栈" }, { title: "java架构师" }];

export default {
  "GET /api/goods": function(req, res) {
    setTimeout(() => {
      res.json({ result: data });
    }, 1000);
  }
};

actions返回promise

dva-loading加载状态,直接把loading映射到组件上

(Reat购物车项目实战&)

Reat购物车项目实战&

step01 - 迁出种子项目
git clone -b step01 https://github.com/57code/umi-test.git
umi配置,开启dva和antd

step02 - 创建布局页

antd布局组件

https://ant.design/components/layout-cn/

修改layouts/index.js,所有的子组件通过props放到Content共同拥有一个导航和页尾
import { Layout } from "antd";
import styles from "./index.css";
const { Header, Footer, Content } = Layout;
export default function(props) {
 return (
 
 {/* ⻚头 */}
 导航
 {/* 内容 */}
 
 
{props.children}
{/* ⻚脚 */} 开课吧 ); }
import styles from "./index.css";
import { Layout } from "antd";
import { Menu } from "antd";

const { Header, Footer, Content } = Layout;
import Link from "umi/link";
export default function(props) {
  const { location } = props;
  if (location.pathname === "/login") {
    return {props.children};
  }
  return (
    
      {/* 页头 */}
      
        {/* 新增内容 */}
        
        { lineHeight: "64px", float: "left" }}
        
          
            商品
          
          
            用户
          
          
            关于
          
        
      
      {/* 内容 */}
      
        
{props.children}
{/* 页脚 */} 开课吧 ); // const { location } = props; // if (location.pathname === "/login") { // return {props.children}; // } // return ( //
// Layout for / // {props.children} //
// ); }

样式设置,layouts/index.css

.header {
 color: white; }
.content {
 margin: 16px; }
.box {
锦绣课堂-React课程
 padding: 24px;
 background: #fff;
 min-height: 500px; }
.footer {
 text-align: center; }
.logo {
 float: left;
 width: 160px;
 margin: 16px 16px 0 0; }
配置式路由需要⼿动修改布局,config.js
//登录⻚⾯不需要布局,将登录路由配置移⾄顶层
{ path: "/login", component: "./login" },
{
 path: "/",
 component: "../layouts",
 routes: [
 // 移动之前路由配置到这⾥
 ]
}
export default {
  //   routes: [{ path: "/", component: "./about" }]
  plugins: [
    [
      "umi-plugin-react",
      {
        dva: true,
        antd: true
      }
    ]
  ]
};

导航菜单移动⾄顶部,layouts/index.js

{props.children}

import styles from "./index.css";
import { Layout } from "antd";
import { Menu } from "antd";

const { Header, Footer, Content } = Layout;
import Link from "umi/link";
export default function(props) {
  const { location } = props;
  if (location.pathname === "/login") {
    return {props.children};
  }
  return (
    
      {/* 页头 */}
      
        {/* 新增内容 */}
        
        { lineHeight: "64px", float: "left" }}
        
          
            商品
          
          
            用户
          
          
            关于
          
        
      
      {/* 内容 */}
      
        
{props.children}
{/* 页脚 */} 开课吧 ); // const { location } = props; // if (location.pathname === "/login") { // return {props.children}; // } // return ( //
// Layout for / // {props.children} //
// ); }
step03 - ⽤户登录认证
利⽤ant-design-pro中Login、Exception、图表等业务组件加速开发进
引⼊ant-design-pro,安装: npm install ant-design-pro --save
测试:修改404⻚⾯提示内容,404.js
// umi的配置,已经⾃动⽀持antd-pro的按需加载
import { Exception } from "ant-design-pro";
export default function() {
  return ;
}

登录页构建,pages/login.js
import React, { Component } from "react";
// import { Button } from "antd";
import styles from "./login.css";
import router from "umi/router";
import { Login } from "ant-design-pro";

import { connect } from "dva";

const { UserName, Password, Submit } = Login; // 通用的用户名、密码和提交组件

export default connect()(function(props) {
  // let from = props.location.state.from || "/"; // 重定向地址
  const onSubmit = (err, values) => {
    console.log("用户输入:", values);
    if (!err) {
      // 校验通过,提交登录
      props.dispatch({ type: "user/login", payload: values });
    }
  };
  return (
    
{/* logo */} {/* 登录表单 */} 登录
); });

pages/login.css

.loginForm {
  width: 50%;
  margin: 100px auto;
  text-align: center;
}
.logo {
  margin-bottom: 30px;
}
登录接⼝mock,创建./mock/login.js
登录失败处理
返回json数据,输入错误401被拦截,设置错误码
export default {
  "post /api/login"(req, res, next) {
    const { username, password } = req.body;
    console.log(username, password);
    if (username == "kaikeba" && password == "123") {
      return res.json({
        code: 0,
        data: {
          token: "kaikebaisgood",
          role: "admin",
          balance: 1000,
          username: "kaikeba"
        }
      });
    }
    if (username == "jerry" && password == "123") {
      return res.json({
        code: 0,
        data: {
          token: "kaikebaisgood",
          role: "user",
          balance: 100,
          username: "jerry"
        }
      });
    }
    return res.status(401).json({
      code: -1,
      msg: "密码错误"
    });
  }
};

layouts/index.js 判断是/login,不让布局组件包裹login

import styles from "./index.css";
import { Layout } from "antd";
import { Menu } from "antd";

const { Header, Footer, Content } = Layout;
import Link from "umi/link";
export default function(props) {
  const { location } = props;
  if (location.pathname === "/login") {
    return {props.children};
  }
  return (
    
      {/* 页头 */}
      
        {/* 新增内容 */}
        
        { lineHeight: "64px", float: "left" }}
        
          
            商品
          
          
            用户
          
          
            关于
          
        
      
      {/* 内容 */}
      
        
{props.children}
{/* 页脚 */} 开课吧 ); // const { location } = props; // if (location.pathname === "/login") { // return {props.children}; // } // return ( //
// Layout for / // {props.children} //
// ); }
用户信息保存和登录动作编写,创建./src/models/user.js 
try catch处理异常
import axios from "axios";
import router from "umi/router";

// 初始状态:本地缓存或空值对象
const userinfo = JSON.parse(localStorage.getItem("userinfo")) || {
  token: "",
  role: "",
  username: "",
  balance: 0
};

// 登录请求方法
function login(payload) {
  return axios.post("/api/login", payload);
}

export default {
  namespace: "user", // 可省略
  state: userinfo,
  effects: {
    // action: user/login
    *login({ payload }, { call, put }) {
      const {
        data: { code, data: userinfo }
      } = yield call(login, payload);
      if (code == 0) {
        // 登录成功: 缓存用户信息
        localStorage.setItem("userinfo", JSON.stringify(userinfo));
        yield put({ type: "init", payload: userinfo });
        router.push("/");
      } else {
        // 登录失败:弹出提示信息,可以通过响应拦截器实现
      }
    }
  },
  reducers: {
    init(state, action) {
      // 覆盖旧状态
      return action.payload;
    }
  }
};

pages/login.js

import React, { Component } from "react";
// import { Button } from "antd";
import styles from "./login.css";
import router from "umi/router";
import { Login } from "ant-design-pro";

import { connect } from "dva";

const { UserName, Password, Submit } = Login; // 通用的用户名、密码和提交组件

export default connect()(function(props) {
  // let from = props.location.state.from || "/"; // 重定向地址
  const onSubmit = (err, values) => {
    console.log("用户输入:", values);
    if (!err) {
      // 校验通过,提交登录
      props.dispatch({ type: "user/login", payload: values });
    }
  };
  return (
    
{/* logo */} {/* 登录表单 */} 登录
); });

~ 响应拦截,创建./src/interceptor.js,通过notification通知组件进行弹窗

interceptor拦截不同状态码

import axios from "axios";
import { notification } from "antd";

const codeMessage = {
  202: "一个请求已经进入后台排队(异步任务)。",
  401: "用户没有权限(令牌、用户名、密码错误)。",
  404: "发出的请求针对的是不存在的记录,服务器没有进行操作。",
  500: "服务器发生错误,请检查服务器。"
};

// 仅拦截异常状态响应
axios.interceptors.response.use(null, ({ response }) => {
  if (codeMessage[response.status]) {
    notification.error({
      message: `请求错误 ${response.status}: ${response.config.url}`,
      description: codeMessage[response.status]
    });
  }
  return Promise.reject(err);
});

src/global.js 执行拦截器带需要创建global

global全局引入拦截器

// 全局入口
import interceptor from "./interceptor";

商品列表

mock/goods.js

let data = [{ title: "web全栈" }, { title: "java架构师" }];

export default {
  "GET /api/goods": function(req, res) {
    setTimeout(() => {
      res.json({ result: data });
    }, 1000);
  }
};

models/goods.js

export default {
  namespace: "goods",
  state: {
    // 初始状态包括课程和分类
    courses: {}, // 课程
    tags: [] // 分类
  },
  effects: {
    *getList(action, { call, put }) {
      // 解构出courseData并初始化状态
      const {
        data: { data: courseData }
      } = yield call(getGoods);
      yield put({ type: "initGoods", payload: courseData });
    }
  },
  reducers: {
    initGoods(state, { payload }) {
      // 解构出tags和courses并返回
      const { tags, data: courses } = payload;
      return { ...state, tags, courses };
    }
  }
};

pages/goods/index.js

import { TagSelect } from "ant-design-pro";
import { connect } from "dva";
import React from "react";
// @connect(
//   state => ({
//     courses: state.goods.courses, // 映射课程数据
//     tags: state.goods.tags // 映射标签数据
//   }),
//   {}
// )

export default connect(
  state => ({
    courses: state.goods.courses, // 映射课程数据
    tags: state.goods.tags // 映射标签数据
  }),
  {}
)(
  class GoodsTest extends React.Component {
    // 页签变更
    tagSelectChange = tags => {
      console.log(tags);
    };
    render() {
      if (this.props.loading.models.goods) {
        return 
加载中...
; } return (
{/* 分类标签 */} {this.props.tags.map(tag => { return ( {tag} ); })}
); } } );
显示课程列表,src\pages\goods.js
import styles from "./goods.css";

import { connect } from "dva";
import { useEffect } from "react";

export default connect(
  state => ({
    goodList: state.goods,
    usersList: state.users,
    loading: state.loading
  }),
  {
    addGood: title => {
      return {
        type: "goods/addGoods",
        payload: { title }
      };
    },
    getList: () => {
      return {
        type: "goods/getList"
      };
    }
  }
)(function({ goodList, usersList, addGood, getList, loading }) {
  // console.log(usersList[0].name);
  useEffect(() => {
    getList();
  }, []);
  if (loading.models.goods) {
    return 
加载中...
; } return (
Page goods
    {goodList.map((item, index) => { return
  • {item.title}
  • ; })}
{ addGood("商品" + new Date().getTime()); }} > 添加商品
); });
import { Card, Row, Col, Skeleton, Icon } from "antd";
class Goods extends Component {
 constructor(props) {
 super(props);
 // displayCourses为需要显示的商品数组
 this.state = {
 displayCourses: new Array(8).fill({}) // 填充数组⽤
于⻣架屏展示
 };
 }
 // 数据传⼊时执⾏⼀次tagSelectChange
 componentWillReceiveProps(props){ 
 if(props.tags.length){
 this.tagSelectChange(props.tags, props.courses)
 }
 }
 // 额外传⼊课程列表数据
 tagSelectChange = (tags, courses = this.props.courses)
=> {
 console.log(tags);
 // 过滤出要显示的数据
 let displayCourses = [];
 tags.forEach(tag => {
 displayCourses = [...displayCourses,
...courses[tag]];
 });
 this.setState({ displayCourses });
 console.log(displayCourses); 
 };
 render() {
 // 使⽤⻣架屏做加载反馈,loading属性不再需要
 // if (this.props.loading.models.goods) {
锦绣课堂-React课程
 // return 
加载中...
; // } return (
{/* 分类标签 */} {/* 商品列表 */} {this.state.displayCourses.map((item, index) => { return ( { padding: 10 }} span={6} {item.name ? ( { float: "right" }}
) : ( )} })}
); } } export default Goods;
TagSelect初始状态调整:默认应当全选

 

constructor(props) {
 super(props);
 this.state = {
 //...
 tags: [], // 默认未选中任何标签
 };
}
tagSelectChange = (tags, courses = this.props.courses)
=> {
 // 用户行为修改状态
 this.setState({ displayCourses, tags }); 
};
// 组件受控
step05 - 添加购物车
创建购物车模型,./src/models/cart.js
export default {
 namespace: "cart", // 可省略
 state: JSON.parse(localStorage.getItem("cart")) || [],
// 初始状态:缓存或空数组
 reducers: {
 addCart(cart, action) {
 const good = action.payload;
 const idx = cart.findIndex(v => v.id == good.id);
 if (idx > -1) {
 // 更新数量
 const cartCopy = [...cart];
 const itemCopy = { ...cartCopy[idx] };
 itemCopy.count += 1;
 cartCopy.splice(idx, 1, itemCopy);
 return cartCopy;
 } else {
 // 新增
 return [...cart, { ...good, count: 1 }];
 }
 }
 }
};
请求添加购物车,src\pages\goods\index.js
装饰器组件写法
@connect(
 state => ({ ... }),
 {
 addCart: item => ({ // 加购⽅法
 type: "cart/addCart",
 payload: item
 }),
 }
)
class Goods extends Component {
 addCart = (e, item) => {
 e.stopPropagation();
 this.props.addCart(item);
 };
 render() {
 { fontSize: 18 }} /}>
 }
}

购物车数据同步到localStorage

effects: {
 *addCart({payload}, {put, select}) { 
 yield put({type: 'add', payload})
 const cart = yield select(state => state.cart);
 localStorage.setItem("cart",
JSON.stringify(cart));
 }
 },
 reducers: {
 add(cart, action) {...}
 }
页头添加购物车信息,./src/layouts/index.js
import {Badge, Icon} from 'antd';
export default function(props) {
 render(){
 ...
 {float:'right'}}
 {fontSize:18}}/
 我的购物⻋
 
 
} }
~ 希望徽章小一点,添加全局样式,./src/global.css
.ant-badge-count{
 height: 16px;
 border-radius: 8px;
 min-width: 16px;
 line-height: 16px;
 padding: 0; }

~ 显示购物车数据

import { Dropdown } from "antd";
export default connect(state => ({ // 连接购物⻋状态
 count: state.cart.length,
 cart: state.cart
}))(function(props) {
 render(){
 // 构造购物⻋列表菜单
 const menu = (
 
 {this.props.cart.map((item, index) => (
 
 {item.name}×{item.count} ¥ {item.count * item.price}
 
 ))}
 
 );
 ...
 return (
 {/* 购物⻋信息放在Dropdown以便展示 */}
 
 
{/* 购物⻋项⽬数量 */}
) } })

 

关注
打赏
1665965058
查看更多评论

最近更新

热门博客

立即登录/注册

微信扫码登录

0.0392s