react:todolist

效果图

在这里插入图片描述

入口 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
        },
    ])
    // 用于添加一个 todo  接受的参数是 一个 todo对象
    const addTodo = (todoObj) =>
    {
        // 合并数组 在末尾追加
        const newToDos = [...toDos, todoObj]
        // 更新状态 
        setToDos(newToDos)

    }
    // 用于更新一个 todo
    const updateTodo = (id, done) =>
    {
        const newToDos = toDos.map(todoObj =>
        {
            if (todoObj.id === id)
            {
                return { ...todoObj, done }   // es6语法 可以更改对象的值 done:done 简写 忘记可以看 es6

            } else
            {
                return todoObj
            }

        })
        setToDos(newToDos)
    }
    // 用于删除一个 todo
    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 }  // es6 语法 返回数组更改值
        })
        // 更新状态
        setToDos(newToDos)
    }
    const clearAllTodoDone = () =>
    {
        const newToDos = toDos.filter(todoObj =>
        {
            return !todoObj.done  // filter 保留结果为 true 然而要删除的就是选中的
        })
        // 更新状态
        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>
    );
};
// 对 props进行限制
index.propTypes = { addTodo: PropTypes.func.isRequired }
export default index

Header CSS

/*header*/
.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

/*main*/
.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>
                {/* defaultChecked 第一次是否勾选 */}
                <input type="checkbox" checked={done}
                    onChange={handleCheck(id)}
                />
                <span>{name}</span>
            </label>
            {/* onClick={() => this.btnClick(id)}  这个方法忘记的话 可以看看之前那个不用柯里化 */}
            <button onClick={() => btnClick(id)} className={`${styles.btn} ${styles.btn_danger}`} style={{ display: mouse ? 'block' : 'none' }}>删除</button>
        </li>
    );
};
export default index

Item CSS

/*item*/
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}
                    // 相等并且不等于0
                    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

/*footer*/
.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;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

臧小川

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值