1.專案簡介
1.1 介紹
本專案為生成日曆器,使用者輸入資訊及選擇圖片後即可生成日曆。
1.2 專案地址
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來進行互動,沒有網路請求等非同步操作,實現較為簡單。