Dva+Antd-mobile项目实践

1.项目简介

1.1 介绍

本项目为生成日历器,用户输入信息及选择图片后即可生成日历。

1.2 项目地址

源码:github.com/Wind1ike/Ca…

DEMO:wind1ike.github.io/Calendar/

1.3 截图



2.安装与配置

2.1 安装

(安装请使用科学上网,不然安装速度很慢)

npm i dva-cli -g
dva new calendar
cd calendar
npm i babel-import-plugin antd-mobile prop-types -D
复制代码

2.2 配置

.webpackrc

{
    "extraBabelPlugins": [
         ["import", { "libraryName": "antd-mobile", "style": true }]  //按需加载antd-mobile样式文件
    ]
}
复制代码

3.开发

3.1 编写入口文件

/scr/index.js

import dva from 'dva';
import createHistory from 'history/createBrowserHistory';
import './index.css';

// 1. Initialize
const app = dva({
    history: createHistory()
});

// 2. Plugins
// app.use({});    //插件

// 3. Model
app.model(require('./models/calendar').default);    //加载数据model文件

// 4. Router
app.router(require('./router').default);    //加载路由文件

// 5. Start
app.start('#root');     //启动程序

复制代码

3.2 编写路由文件

该项目可分为两个路由页面,一个为用户填写信息的页面,另一个为生成的日历页面。

/src/router.js

import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';
import Canvas from './routes/Canvas';

function RouterConfig({ history, text }) {
  return (
    <Router history={history}>
      <Switch>
        <Route path="/" exact component={IndexPage} />
        <Route path="/canvas" exact component={Canvas} />
      </Switch>
    </Router>
  );
}

export default RouterConfig;

复制代码

3.3 编写组件布局

/src/routes/IndexPage.jsx

import React from 'react';
import Header from '../components/IndexPage/Header';
import InputBox from '../components/IndexPage/InputBox';

function IndexPage() {
  return (
    <div>
      <Header></Header>
      <InputBox></InputBox>
    </div>
  );
}

IndexPage.propTypes = {
};

export default IndexPage;

复制代码

/src/components/IndexPage/Header.jsx

import { NavBar } from 'antd-mobile';
import styles from './Header.css';

function Header() {
    return (
        <NavBar
        mode="light"
        className={styles['single-top-nav-bar']}
        >
            <p className={styles['am-navbar-title']}>日历生成器</p>
        </NavBar>
    )
}

export default Header;

复制代码

/src/components/IndexPage/InputBox.jsx

import { List, TextareaItem, InputItem, DatePickerView, ImagePicker, Button, Modal } from 'antd-mobile';
import { connect } from 'dva';
import { routerRedux } from 'dva/router';
import React from 'react';

class InputBox extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            text: '',
            todo: '',
            notTodo: '',
            date: 0,
            files: [],
            picture: null,
            isShowModal: false
        }
    }
    
    render() {
        return (
            <React.Fragment>
                <List renderHeader={()=> '文本信息'}>
                    <TextareaItem
                    title="内容"
                    placeholder=""
                    data-seed="logId"
                    rows="5"
                    value={this.state.text}
                    onChange={(text)=> this.setState({text})}></TextareaItem>
                    <InputItem
                    value={this.state.todo}
                    onChange={(text)=> this.setState({todo: text})}>宜</InputItem>
                    <InputItem
                    value={this.state.notTodo}
                    onChange={(text)=> this.setState({notTodo: text})}>忌</InputItem>
                </List>
                <List renderHeader={()=> '选择日期'}>
                    <DatePickerView
                    mode="date" 
                    value={this.state.date}
                    onChange={(date)=> this.setState({date: date})}/>
                </List>
                <List renderHeader={()=> '选择图片'}>
                    <ImagePicker 
                    files={this.state.files}
                    selectable={this.state.files.length < 1}
                    onChange={(files)=> this.setState({picture: files[0], files})}/>
                </List>
                <Modal
                visible={this.state.isShowModal}
                transparent
                maskClosable={false}
                title="检查是否为填写内容或未选择图片!"
                footer={[{ text: 'Ok', onPress: () => { this.setState({isShowModal: false}) } }]}
                wrapProps={{ onTouchStart: this.onWrapTouchStart }}
                />
                <Button
                type="primary"
                onClick={()=> this.onSubmit()}>生成图片</Button>
            </React.Fragment>
        )
    }

    onSubmit() {
        if(!(this.state.text && this.state.picture)) {
            this.setState({isShowModal: true});
            return ;
        }

        this.props.dispatch(routerRedux.push('/canvas'));   //切换路由
    }
}


export default InputBox;

复制代码

/scr/routes/Canvas.jsx

import React from 'react';

class Canvas extends React.Component {

    componentDidMount() {
        this.canvas.height = document.body.scrollHeight;
        this.canvas.width = document.body.scrollWidth;

        this.cxt = this.canvas.getContext('2d');
    }

    render() {
        return (
            <canvas 
            ref={(el)=> this.canvas = el}></canvas>
        )
    }

    
}

export default Canvas;
复制代码

3.4 编写model文件

Dva中采用Redux来管理数据模型,只有一个Store来保存数据。

Store:保存数据的地方,只能通过Reducers返回新的状态

Action:组件通过connect函数包装后会有this.props.dispatch方法,调用该方法来触发相应的Action。Action中通过调用put函数调用相应的Reducers

Reducers:Reducers从Store中获取旧状态,并根据Action中传过来的数据组成新的状态并返回。

/src/models/calendar.js

export default {

  namespace: 'calendar',

  state: {  // 状态
    text: '',
    todo: '',
    notTodo: '',
    date: 0,
    picture: null
  },

  subscriptions: {
    setup({ dispatch, history }) {  // 监听路由触发动作
    },
  },

  effects: {    // actions
    *submit({ payload }, { call, put }) {  
      yield put({
        type: 'save' ,  //调用reducers中save函数
        payload,
      });
    },
  },

  reducers: {
    save(state, action) {
      return { ...state, ...action.payload };   //返回新的State,不修改旧的State
    },
  },

};
复制代码

修改 /src/components/IndexPage/InputBox.jsx

...
import { connect } from 'dva';
...
class InputBox extends React.Component {
   constructor(props) {
        super(props);
        let date;
        
        if(this.props.date) {
            date = new Date(this.props.date);
        } else {
            date = new Date();
        }
        console.log(this.props)
        this.state = {
            text: this.props.text,
            todo: this.props.todo,
            notTodo: this.props.notTodo,
            date: date,
            files: [],
            picture: null,
            isShowModal: false
        }
    }

    ...

    onSubmit() {
        if(!(this.state.text && this.state.picture)) {
            this.setState({isShowModal: true});
            return ;
        }

        const { text, todo, notTodo, date, picture } = this.state;

        this.props.dispatch({
            type: 'calendar/submit',
            payload: { text, todo, notTodo, picture, date: date.getTime() }
        });     //触发命名空间中submit的Actions

        this.props.dispatch(routerRedux.push('/canvas'));   //切换路由
    }
}

function mapStateToProps(state) {   //该函数用来把Store中的state转换成组件的props
    return state.calendar;
}

export default connect(mapStateToProps)(InputBox);  //通过connnect函数把state注入组件的props中

复制代码

修改 /src/routes/Canvas.jsx

...
import { connect } from 'dva';
...
class Canvas extends React.Component {
    componentDidMount() {
        this.canvas.height = document.body.scrollHeight;
        this.canvas.width = document.body.scrollWidth;

        this.cxt = this.canvas.getContext('2d');
        this.drawImage();
        this.drawDate();
        this.drawText();
        this.drawEvent();
    }
    ...
    (大部分代码为操作canvas代码,不贴出来,有需要可以去看源码)
}
function mapStateToProps(state) {   //该函数用来把Store中的state转换成组件的props
    return state.calendar;
}

export default connect(mapStateToProps)(Canvas);    //通过connnect函数把state注入组件的props中
复制代码

4.总结

本项目通过触发同步Actions来进行交互,没有网络请求等异步操作,实现较为简单。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值