components文件夹
AddTodo.jsx
import React, { Component} from 'react'
import store from "../store/index"
import { addOneTodoAction } from "../store/actions/todos"
export default class AddTodo extends Component {
constructor ( props) {
super ( props) ;
this . myInputRef = React. createRef ( ) ;
this . state = store. getState ( ) ;
}
componentDidMount ( ) {
store. subscribe ( ( ) => {
this . setState ( store. getState ( ) )
} )
}
_handleKeyDown ( e) {
let { todos } = this . state;
let lastTodoId = todos. length === 0 ? 0 : todos[ todos. length- 1 ] . id
if ( e. keyCode === 13 ) {
let value = this . myInputRef. current. value;
if ( ! value. trim ( ) ) {
alert ( "输入的内容不能为空~" )
return ;
}
let todo = { id: lastTodoId+ 1 , content: value, done: false }
let action = addOneTodoAction ( todo) ;
store. dispatch ( action) ;
this . myInputRef. current. value = "" ;
}
}
render ( ) {
return (
< div className= "todo-header" >
< input
type= "text"
ref= { this . myInputRef}
placeholder= "请输入今天的任务清单,按回车键确认"
onKeyDown= { e=> this . _handleKeyDown ( e) }
/ >
< / div>
)
}
}
Footer.jsx
import React, { Component} from 'react'
import store from "../store/index"
import { delDoneTodo, isCheckedAllTodo } from "../store/actions/todos"
export default class Footer extends Component {
constructor ( props) {
super ( props) ;
this . state = store. getState ( ) ;
}
componentDidMount ( ) {
store. subscribe ( ( ) => {
this . setState ( store. getState ( ) )
} )
}
render ( ) {
let { todos, finishedCount } = this . state;
return (
< div className= "todo-footer" >
< label>
< input type= "checkbox"
checked= { todos. length> 0 && finishedCount === todos. length }
onChange= { ( e) => this . _dealChecked ( e) }
/ >
< / label>
< span> < span> 已完成{ finishedCount } 件< / span> / 总计{ todos. length} 件 < / span>
< button
className= "btn btn-warning"
onClick= { ( ) => this . _dealRemove ( ) }
> 清除已完成任务< / button>
< / div>
)
}
_dealRemove ( ) {
let action = delDoneTodo ( ) ;
store. dispatch ( action)
}
_dealChecked ( e) {
let action = isCheckedAllTodo ( e. target. checked) ;
store. dispatch ( action)
}
}
Todo.jsx
import React, { Component} from 'react'
import PropTypes from "prop-types"
import store from "../store/index"
import { delOneTodoAction, changeOneTodoAction } from "../store/actions/todos"
export default class Todo extends Component {
static propTypes = {
todo: PropTypes. object. isRequired
}
constructor ( props) {
super ( props) ;
this . state = {
isShowDelBtn : false
}
}
_hasShowBtn ( e, flag) {
this . setState ( {
isShowDelBtn: flag
} )
}
render ( ) {
let { todo } = this . props;
let { isShowDelBtn } = this . state;
return (
< li
onMouseOver= { e=> this . _hasShowBtn ( e, true ) }
onMouseOut= { e=> this . _hasShowBtn ( e, false ) }
>
< label>
< input
type= "checkbox"
checked= { todo. done }
onClick= { ( ) => this . _dealChange ( todo. id, ! todo. done) }
readOnly
/ >
< span> { todo. content} < / span>
< / label>
< button
className= "btn btn-warning"
style= { { display: isShowDelBtn ? "block" : "none" } }
onClick= { ( ) => this . _delOneTodo ( todo. id) }
>
删除
< / button>
< / li>
)
}
_dealChange ( todoId, flag) {
let action = changeOneTodoAction ( todoId, flag) ;
store. dispatch ( action)
}
_delOneTodo ( todoId) {
let action = delOneTodoAction ( todoId)
store. dispatch ( action) ;
}
}
TodoList.jsx
import React, { Component} from 'react'
import Todo from "./Todo"
import store from "../store/index"
export default class TodoList extends Component {
constructor ( props) {
super ( props) ;
this . state = store. getState ( ) ;
}
componentDidMount ( ) {
store. subscribe ( ( ) => {
this . setState ( store. getState ( ) ) ;
} )
}
render ( ) {
let { todos } = this . state;
console. log ( todos)
return (
< ul className= "todo-main" >
{
todos. map ( ( todo, index) => (
< Todo
key= { todo. id}
todo= { todo}
> < / Todo>
) )
}
< / ul>
)
}
}
http
index.js
import axios from 'axios'
axios. defaults. timeout = 10000 ;
axios. defaults. headers. post[ 'Content-Type' ] = 'application/x-www-form-urlencoded;charset=UTF-8' ;
axios. interceptors. request. use ( ( config) => {
return config;
} , ( error) => {
return Promise. error ( error) ;
} ) ;
axios. interceptors. response. use ( ( response) => {
if ( response. status === 200 ) {
return Promise. resolve ( response. data) ;
} else {
return Promise. reject ( response. data) ;
}
} , ( error) => {
console. log ( error) ;
} ) ;
export default function ajax ( url = '' , params = { } , type = 'GET' ) {
let promise;
return new Promise ( ( resolve, reject) => {
if ( type. toUpperCase ( ) === 'GET' ) {
promise = axios ( {
url,
params
} )
} else if ( type. toUpperCase ( ) === 'POST' ) {
promise = axios ( {
method: 'post' ,
url,
data: params
} )
}
promise. then ( ( response) => {
resolve ( response) ;
} ) . catch ( ( error) => {
reject ( error) ;
} )
} ) ;
}
store
actions
todos.js
import {
GET_ALL_TODO ,
DEL_ONE_TODO ,
CHANGE_ONE_TODO ,
ADD_ONE_TODO ,
DEL_DONE_TOOD ,
IS_CHECKED_ALL_TODO
}
from "../contains/actionTypes"
export const getAllTodoAction = todos=> ( {
type: GET_ALL_TODO ,
todos
} )
export const delOneTodoAction = todoId=> ( {
type: DEL_ONE_TODO ,
todoId
} )
export const changeOneTodoAction = ( todoId, isDone) => ( {
type: CHANGE_ONE_TODO ,
todoId,
isDone
} )
export const addOneTodoAction = ( todo) => ( {
type: ADD_ONE_TODO ,
todo
} )
export const delDoneTodo = ( ) => ( {
type: DEL_DONE_TOOD ,
} )
export const isCheckedAllTodo = ( flag) => ( {
type: IS_CHECKED_ALL_TODO ,
flag
} )
contains
actionType.js
export const GET_ALL_TODO = "GET_ALL_TODO"
export const DEL_ONE_TODO = "DEL_ONE_TODO"
export const CHANGE_ONE_TODO = "CHANGE_ONE_TODO"
export const ADD_ONE_TODO = "ADD_ONE_TODO"
export const DEL_DONE_TOOD = "DEL_DONE_TOOD"
export const IS_CHECKED_ALL_TODO = "IS_CHECKED_ALL_TODO"
reducers
index.js
import {
GET_ALL_TODO ,
DEL_ONE_TODO ,
CHANGE_ONE_TODO ,
ADD_ONE_TODO ,
DEL_DONE_TOOD ,
IS_CHECKED_ALL_TODO
} from "../contains/actionTypes"
const defaultState = {
todos: [ ] ,
finishedCount : 0
}
export default function reducer ( state= defaultState, action) {
if ( action. type === GET_ALL_TODO ) {
const newState = JSON . parse ( JSON . stringify ( state) ) ;
newState. todos = action. todos;
return newState;
}
if ( action. type === DEL_ONE_TODO ) {
const newState = JSON . parse ( JSON . stringify ( state) ) ;
let tempFinishedCount = 0 ;
newState. todos. forEach ( ( todo, index) => {
if ( action. todoId === todo. id) {
newState. todos. splice ( index, 1 )
}
} )
newState. todos. forEach ( ( todo, index) => {
if ( todo. done) {
tempFinishedCount += 1 ;
}
} )
newState. finishedCount = tempFinishedCount
return newState;
}
if ( action. type === CHANGE_ONE_TODO ) {
const newState = JSON . parse ( JSON . stringify ( state) ) ;
let tempFinishedCount = 0 ;
newState. todos. forEach ( ( todo, index) => {
if ( action. todoId === todo. id) {
todo. done = action. isDone
}
} )
newState. todos. forEach ( ( todo, index) => {
if ( todo. done) {
tempFinishedCount += 1 ;
}
} )
newState. finishedCount = tempFinishedCount
return newState;
}
if ( action. type === ADD_ONE_TODO ) {
const newState = JSON . parse ( JSON . stringify ( state) ) ;
newState. todos. push ( action. todo)
return newState;
}
if ( action. type === DEL_DONE_TOOD ) {
const newState = JSON . parse ( JSON . stringify ( state) ) ;
let tempArr = [ ] ;
newState. todos. forEach ( ( todo, index) => {
if ( ! todo. done) {
tempArr. push ( todo)
}
} )
newState. finishedCount = 0
newState. todos = tempArr;
return newState;
}
if ( action. type === IS_CHECKED_ALL_TODO ) {
const newState = JSON . parse ( JSON . stringify ( state) ) ;
let tempFinishedCount = 0 ;
newState. todos. forEach ( ( todo, index) => {
todo. done = action. flag
} )
newState. todos. forEach ( ( todo, index) => {
if ( todo. done) {
tempFinishedCount += 1 ;
}
} )
newState. finishedCount = tempFinishedCount
return newState;
}
}
index.js
import { createStore } from "redux"
import reducer from "./reducers/index"
const composeEnhancers = window. __REDUX_DEVTOOLS_EXTENSION_COMPOSE__
let store = createStore ( reducer, composeEnhancers ( ) ) ;
export default store;
App.js
import React from 'react' ;
import "./index.css"
import AddTodo from './components/AddTodo'
import TodoList from './components/TodoList'
import Footer from './components/Footer'
import store from "./store/index"
import { getAllTodoAction } from "./store/actions/todos"
class App extends React. Component {
componentWillMount ( ) {
const todos = [
{ id: 1 , content: "学习uniapp" , done: false } ,
{ id: 2 , content: "学习vue" , done: true } ,
{ id: 3 , content: "学习webpack" , done: false } ,
{ id: 4 , content: "学习react" , done: true } ,
{ id: 5 , content: "学习redux" , done: false }
]
let action = getAllTodoAction ( todos) ;
store. dispatch ( action)
}
render ( ) {
return (
< div className= "todo-container" >
< div className= "todo-wrap" >
< AddTodo / >
< TodoList / >
< Footer/ >
< / div>
< / div>
)
}
}
export default App;
index.css
body { background : #fff; }
.btn {
display : inline-block;
padding : 8px 10px;
margin-bottom : 0;
font-size : 14px;
line-height : 20px;
text-align : center;
vertical-align : middle;
cursor : pointer;
box-shadow : inset 0 1px 0 rgba ( 255, 255, 255, 0.2) , 0 1px 2px rgba ( 0, 0, 0, 0.05) ;
border-radius : 4px;
}
.btn-warning {
color : #fff;
background-color : orange;
border : none;
}
.btn-warning:hover {
color : #fff;
background-color : red;
}
.btn:focus {
outline : none;
}
.todo-container {
width : 600px;
margin : 0 auto;
}
.todo-container .todo-wrap {
padding : 10px;
border : 1px solid #ddd;
border-radius : 5px;
}
.todo-header input {
width : 560px;
height : 28px;
font-size : 14px;
border : 1px solid #ccc;
border-radius : 4px;
padding : 4px 7px;
outline : none;
}
.todo-header input:focus {
outline : none;
border-color : rgba ( 255, 0, 0, 0.8) ;
box-shadow : inset 0 1px 1px rgba ( 255, 0, 0, 0.075) , 0 0 8px rgba ( 255, 0, 0, 0.6) ;
}
.todo-main {
margin-left : 0;
border : 1px solid #ddd;
border-radius : 2px;
padding : 0;
}
.todo-empty {
height : 40px;
line-height : 40px;
border : 1px solid #ddd;
border-radius : 2px;
padding-left : 5px;
margin-top : 10px;
}
li {
list-style : none;
height : 36px;
line-height : 36px;
padding : 0 5px;
border-bottom : 1px solid #ddd;
display : flex;
justify-content : space-between;
align-items : center;
}
li label {
cursor : pointer;
}
li button.btn {
display : none;
margin-top : 3px;
padding : 3px 10px;
}
li:before {
content : initial;
}
li:last-child {
border-bottom : none;
}
.todo-footer {
height : 40px;
line-height : 40px;
padding-left : 6px;
margin-top : 5px;
}
.todo-footer label {
display : inline-block;
margin-right : 20px;
cursor : pointer;
}
.todo-footer label input {
position : relative;
top : -1px;
vertical-align : middle;
}
.todo-footer button {
float : right;
margin-top : 5px;
}
index.js
import React from 'react' ;
import ReactDOM from 'react-dom' ;
import App from './App.js' ;
ReactDOM. render ( < App / > , document. getElementById ( 'root' ) ) ;