效果图
入口
js
import React, { useState } from 'react' ;
import styles from './style.less'
import Header from './component/Header'
import List from './component/List'
import Footer from './component/Footer'
const index = ( ) =>
{
const [ toDos, setToDos] = useState ( [
{
id: '01' , name: '吃饭' , done: true
} ,
{
id: '02' , name: '睡觉' , done: true
} ,
{
id: '03' , name: '打豆豆' , done: false ,
} ,
{
id: '04' , name: '逛街' , done: false
} ,
] )
const addTodo = ( todoObj ) =>
{
const newToDos = [ ... toDos, todoObj]
setToDos ( newToDos)
}
const updateTodo = ( id, done ) =>
{
const newToDos = toDos. map ( todoObj =>
{
if ( todoObj. id === id)
{
return { ... todoObj, done }
} else
{
return todoObj
}
} )
setToDos ( newToDos)
}
const deleteTodo = ( id ) =>
{
const newToDos = toDos. filter ( todoObj =>
{
return todoObj. id !== id
} )
setToDos ( newToDos)
}
const checkAllTodo = ( done ) =>
{
const newToDos = toDos. map ( todoObj =>
{
return { ... todoObj, done }
} )
setToDos ( newToDos)
}
const clearAllTodoDone = ( ) =>
{
const newToDos = toDos. filter ( todoObj =>
{
return ! todoObj. done
} )
setToDos ( newToDos)
}
return (
< div className= { styles. normal} >
< div className= { styles. todo_container} >
< div className= { styles. todo_wrap} >
< Header addTodo= { addTodo} / >
< List toDos= { toDos} updateTodo= { updateTodo} deleteTodo= { deleteTodo} / >
< Footer toDos= { toDos} checkAllTodo= { checkAllTodo} clearAllTodoDone= { clearAllTodoDone} / >
< / div>
< / div>
< / div>
) ;
} ;
export default index
入口CSS
.normal {
position : absolute;
width : 100%;
height : 100%;
display : flex;
align-items : center;
justify-content : center;
}
.todo_container {
width : 600px;
margin : 0 auto;
}
.todo_container .todo_wrap {
padding : 10px;
border : 1px solid #ddd;
border-radius : 5px;
}
Header
js
import React from 'react' ;
import styles from './style.less'
import PropTypes from 'prop-types'
import { nanoid } from 'nanoid'
const index = ( { addTodo } ) =>
{
const handleKeyUp = ( event ) =>
{
console. log ( addTodo) ;
const { keyCode, target } = event
if ( keyCode === 13 )
{
if ( target. value === '' )
{
alert ( '您输入的内容不能为空' )
return
}
const toDoObj = { id: nanoid ( ) , name: target. value, done: false }
addTodo ( toDoObj)
target. value = ''
}
}
return (
< div className= { styles. todo_header} >
< input
onKeyUp= { handleKeyUp}
type= "text"
placeholder= "请输入你的任务名称,按回车键确认"
/ >
< / div>
) ;
} ;
index. propTypes = { addTodo: PropTypes. func. isRequired }
export default index
Header
CSS
.todo_header input {
width : 560px;
height : 28px;
font-size : 14px;
border : 1px solid #ccc;
border-radius : 4px;
padding : 4px 7px;
}
.todo_header input:focus {
outline : none;
border-color : rgba ( 82, 168, 236, 0.8) ;
box-shadow : inset 0 1px 1px rgba ( 0, 0, 0, 0.075) ,
0 0 8px rgba ( 82, 168, 236, 0.6) ;
}
List
js
import React from 'react' ;
import styles from './style.less'
import Item from '../Item'
const index = ( props ) =>
{
const { toDos, updateTodo, deleteTodo } = props
return (
< ul className= { styles. todo_main} >
{ toDos. map ( ( todo ) =>
{
return (
< Item
key= { todo. id}
{ ... todo}
updateTodo= { updateTodo}
deleteTodo= { deleteTodo}
/ >
)
} ) }
< / ul>
) ;
} ;
export default index
List
CSS
.todo_main {
margin : 2px 0 0 1px;
border : 1px solid #ddd;
border-radius : 2px;
padding : 0px;
}
Item
js
import React, { useState } from 'react' ;
import styles from './style.less'
const index = ( props ) =>
{
const { id, name, done } = props
const [ mouse, setMouse] = useState ( false )
const handleMouse = ( flag ) =>
{
return ( ) =>
{
setMouse ( flag)
}
}
const handleCheck = ( id ) =>
{
const { updateTodo } = props
return ( e ) =>
{
console. log ( e, 'e' ) ;
updateTodo ( id, e. target. checked)
}
}
const btnClick = ( id ) =>
{
const { deleteTodo } = props
if ( window. confirm ( '确定删除吗?' ) )
{
console. log ( '通知app删除' , id) ;
deleteTodo ( id)
}
}
return (
< li className= { styles. todo_footer}
style= { { backgroundColor: mouse ? '#ddd' : 'white' } }
onMouseEnter= { handleMouse ( true ) }
onMouseLeave= { handleMouse ( false ) } >
< label>
{ }
< input type= "checkbox" checked= { done}
onChange= { handleCheck ( id) }
/ >
< span> { name} < / span>
< / label>
{ }
< button onClick= { ( ) => btnClick ( id) } className= { ` ${ styles. btn} ${ styles. btn_danger} ` } style= { { display: mouse ? 'block' : 'none' } } > 删除< / button>
< / li>
) ;
} ;
export default index
Item
CSS
li {
list-style : none;
height : 36px;
line-height : 36px;
padding : 0 5px;
border-bottom : 1px solid #ddd;
}
li label {
float : left;
cursor : pointer;
}
li label li input {
vertical-align : middle;
margin-right : 6px;
position : relative;
top : -1px;
}
li button {
float : right;
display : none;
margin-top : 3px;
}
li:before {
content : initial;
}
li:last-child {
border-bottom : none;
}
.btn {
display : inline-block;
padding : 4px 12px;
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_danger {
color : #fff;
background-color : #da4f49;
border : 1px solid #bd362f;
}
.btn_danger:hover {
color : #fff;
background-color : #bd362f;
}
.btn_focus {
outline : none;
}
Footer
js
import React from 'react' ;
import styles from './style.less'
const index = ( props ) =>
{
const { toDos } = props
const doneCount = toDos. reduce ( ( pre, todo ) =>
{
return pre + ( todo. done ? 1 : 0 )
} , 0 )
const total = toDos. length
const handleCheckAll = ( event ) =>
{
props. checkAllTodo ( event. target. checked)
}
const clearAllTodoDone = ( ) =>
{
props. clearAllTodoDone ( )
}
return (
< div className= { styles. todo_footer} >
< label >
< input
type= "checkbox"
onChange= { handleCheckAll}
checked= { doneCount === total && doneCount !== 0 ? true : false }
/ >
< / label>
< span>
< span> 已完成{ doneCount} < / span> / 全部{ total}
< / span>
< button className= { ` ${ styles. btn} ${ styles. btn_danger} ` } onClick= { clearAllTodoDone} >
清除已完成任务
< / button>
< / div>
) ;
} ;
export default index
Footer
CSS
.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;
margin-right : 5px;
}
.todo_footer button {
float : right;
margin-top : 5px;
}
.btn {
float : right;
}