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 (
history={
history}>
"/" exact component={IndexPage} />
"/canvas" exact component={Canvas} />
);
}
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 (
);
}
IndexPage.propTypes = {
};
export default IndexPage;
复制代码
/src/components/IndexPage/Header.jsx
import { NavBar } from 'antd-mobile';
import styles from './Header.css';
function Header() {
return (
"light"
className={styles['single-top-nav-bar']}
>
'am-navbar-title']}>日历生成器
)
}
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 (
'文本信息'}>
"内容"
placeholder=""
data-seed="logId"
rows="5"
value={this.state.text}
onChange={(text)=> this.setState({text})}>
this.setState({todo: text})}>宜
this.setState({notTodo: text})}>忌
'选择日期'}>
"date"
value={this.state.date}
onChange={(date)=> this.setState({date: date})}/>
'选择图片'}>
this.setState({picture: files[0], files})}/>
false}
title="检查是否为填写内容或未选择图片!"
footer={[{ text: 'Ok', onPress: () => { this.setState({isShowModal: false}) } }]}
wrapProps={
{ onTouchStart: this.onWrapTouchStart }}
/>
)
}
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 (
)
}
}
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来进行交互,没有网络请求等异步操作,实现较为简单。