1. Введение проекта
1.1 Введение
Этот проект представляет собой генератор календаря, пользователь может создать календарь после ввода информации и выбора изображения.
1.2 Адрес проекта
Исходный код:GitHub.com/wind1I/CA…
ДЕМО: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 Запись файла модели
В Dva Redux используется для управления моделью данных, и для хранения данных существует только одно хранилище.
Магазин: место, где сохраняются данные, а новые состояния могут быть возвращены только через редукторы.
Действие: после того, как компонент будет обернут функцией подключения, будет метод this.props.dispatch, который вызывается для запуска соответствующего действия. Действие вызывает соответствующие редукторы, вызывая функцию put
Редюсеры: редукторы получают старое состояние из Store, формируют новое состояние в соответствии с данными, переданными в действии, и возвращают его.
/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. Резюме
Этот проект взаимодействует, инициируя синхронные действия, без асинхронных операций, таких как сетевые запросы, и реализация относительно проста.