以todolist为例
目录如下
app.js
import React, { PureComponent } from 'react'
import Input from './components/Input'
import List from './components/List'
import Total from './components/Total'
import Mask from './components/Mask'
import { bus as $bus } from './components/bus'
import './App.css'
export default class App extends PureComponent {
constructor() {
super()
this.state = {
flag: false,
list: [
{
id: 1,
content: '哈哈哈',
checked: false
},
{
id: 7,
content: '哈哈哈',
checked: false
},
{
id: 5,
content: '哈哈哈',
checked: false
},
],
checkAll: false,
selectLength: 0,
item: {}
}
}
// 全选全不选
checkAllHandler(checked) {
console.log("checked",checked);
const { list } = this.state
const newList = list.map(item =>{
return {...item,checked}
})
this.setState({list:newList,checkAll: checked})
}
// 单选单不选
checkHandler =(id,checked)=> {
const { list } = this.state
const newList = list.map(item => {
return item.id === id ? {...item,checked} : item
})
let checkAll = newList.length && newList.every(item => item.checked)
this.setState(() => ({list: newList,checkAll}))
}
// 添加
addHandler = (obj)=>{
let { list } = this.state;
let newList = [...list,obj]
console.log('newList===='+newList)
this.setState({
list: newList,
})
}
// 搜索
searchHandler=(content)=>{
console.log("content",content);
let { list } = this.state;
let newList = list.filter(item => item.content.includes(content))
this.setState({
list: newList
})
}
// 删除
delHandler = (id)=> {
console.log("id",id);
const { list } = this.state
const newList = list.filter(item => item.id !=id)
let checkAll = newList.length && newList.every(item => item.checked)
this.setState(() => ({list: newList,checkAll}))
}
// 编辑
editHandler = (items)=>{
this.setState({
item: items
})
}
// 更新
update = (content)=>{
const { list,item } = this.state
let obj = Object.assign(item,{content})
const newList = list.map(v =>{
if(v.id === obj.id) {
v = {...obj}
}
return v
})
this.setState({
list: newList,
item: obj
})
}
// 已完成
// doneLenth=()=> {
// const { list } = this.state
// const newList = list.filter(item => item.checked)
// let selectLength = newList.length
// setTimeout(()=>{
// this.setState({
// selectLength
// })
// })
// }
// 挂载
componentDidMount() {
this.unSubscribe = $bus.addListener("getFlag",(flag)=>{
this.setState({flag})
})
this.unSubscribe1 = $bus.addListener("sendValue",(obj)=>{
this.addHandler(obj)
})
this.unSubscribe2 = $bus.addListener("searchValue",(value)=>{
this.searchHandler(value)
})
this.unSubscribe3 = $bus.addListener("getItem",(item)=>{
this.editHandler(item)
})
this.unSubscribe4 = $bus.addListener("update",(content)=>{
this.update(content)
})
}
// 卸载
componentWillUnmount() {
$bus.removeListener(this.unSubscribe)
$bus.removeListener(this.unSubscribe1)
$bus.removeListener(this.unSubscribe2)
$bus.removeListener(this.unSubscribe3)
$bus.removeListener(this.unSubscribe4)
}
render() {
let { flag, list,checkAll } = this.state
return (
<div className='container'>
{/* 输入框 */}
<Input></Input>
{/* 列表 */}
<List list={list} checkHandler={this.checkHandler} delHandler={this.delHandler}></List>
{/* 统计 */}
<Total checkAllHandler={this.checkAllHandler.bind(this)} checkAll={checkAll} list={list}></Total>
{/* 编辑弹框 */}
{ flag ? <Mask ></Mask> : ''}
</div>
)
}
}
Input.js
import React, { Component } from 'react'
import { bus as $bus } from './bus'
export default class Input extends Component {
constructor() {
super()
this.state = {
value:""
}
}
changeHandler = (e)=>{
this.setState({
value: e.target.value
})
console.log("this.state.value",this.state.value);
}
// 添加
addHandler = ()=>{
let { value } = this.state;
let obj = {
id: Date.now(),
content: value,
done: false
}
if(value) {
$bus.emit("sendValue",obj)
} else {
console.log("请输入")
}
}
// 搜索
searchHandler = ()=>{
console.log("搜索");
let { value } = this.state;
if(!value) return console.log("请输入");
$bus.emit("searchValue",value)
}
render() {
let { value } = this.state
return (
<>
<div className="input">
<input type="text" value={value} placeholder='请输入你的任务名称,按回车键确认' onInput={this.changeHandler}/>
<button className="btn btn-success" onClick={this.addHandler}>添加</button>
<button className="btn btn-primary" onClick={this.searchHandler}>搜索</button>
</div>
</>
)
}
}
List.js
import React, { Component } from 'react'
import Item from './Item'
import PropTypes from 'prop-types'
export default class List extends Component {
static propTypes = {
list:PropTypes.array.isRequired,
}
render() {
let { list,checkHandler,checkAllHandler,delHandler } = this.props;
console.log("list",list);
return (
<ul className="task-list">
{
list.map(item => (<Item item={item} key={item.id} checkHandler={checkHandler} checkAllHandler={checkAllHandler} delHandler={delHandler}></Item>))
}
</ul>
)
}
}
Item.js
import React, { Component } from 'react'
import { bus as $bus } from './bus'
export default class Item extends Component {
constructor(props) {
super(props)
this.state = {}
}
changeHandler = (id)=>{
let { checkHandler } = this.props;
return (e)=>{
checkHandler(id,e.target.checked)
}
}
removeHandler(){
let { delHandler } = this.props;
delHandler(arguments[0])
}
editHadnler = (item)=>{
$bus.emit("getFlag",true)
localStorage.setItem("obj",JSON.stringify(item))
$bus.emit("getItem",item)
}
render() {
let { item } = this.props;
return (
<li className="task-item">
<input type="checkbox" checked={item.checked} onChange={this.changeHandler(item.id)}/>
<div className="content">
{item.content}
</div>
<button className={`btn btn-success ${!item.checked ? "d-none" : "d-block"}`} onClick={()=> this.editHadnler(item)}>编辑</button>
<button className={`btn btn-danger ${!item.checked ? "d-none" : "d-block"}`} onClick={this.removeHandler.bind(this,item.id)}>删除</button>
</li>
)
}
}
Total.js
import React, { Component } from 'react'
export default class Total extends Component {
constructor() {
super()
this.changeAllHandler = this.changeAllHandler.bind(this)
}
changeAllHandler(e) {
let { checkAllHandler } = this.props
checkAllHandler(e.target.checked)
}
render() {
let { checkAll,list } = this.props;
let length = list.filter(item => item.checked).length; // 选中的length长度
return (
<div className="task-done">
<input type="checkbox" onChange={this.changeAllHandler} checked={checkAll}/>
<p>已完成<span className="single-number">{length}</span> 全部<span className="all-number">{list.length}</span></p>
<p></p>
</div>
)
}
}
Mask.js(弹窗)
import React, { Component } from 'react'
import { bus as $bus } from './bus'
export default class mask extends Component {
constructor() {
super()
this.state = {
value: ''
}
}
closeMask = ()=>{ // 关闭弹窗
$bus.emit("getFlag",false)
}
updateHandler = ()=>{
$bus.emit("getFlag",false)
$bus.emit("update",this.state.value)
}
onChange = (e) =>{
this.setState({
value: e.target.value
})
}
componentDidMount() {
let obj = JSON.parse(localStorage.getItem("obj"))
this.setState({
value: obj.content
})
}
render() {
let { value } = this.state
return (
<div>
<div className="mm-mask" >
<div className="mm-modal">
<div className="mm-title">
<span className="mm-edit">编辑</span>
<span className="mm-close" onClick={this.closeMask}>x</span>
</div>
<div className="mm-content">
<input type="text" value={value} placeholder="任务名称" onInput={this.onChange}/>
</div>
<div className="mm-box-btn">
<div className="mm-update" onClick={this.updateHandler}>更新</div>
<div className="mm-cancel" onClick={this.closeMask}>取消</div>
</div>
</div>
</div>
</div>
)
}
}
bus.js
yarn add -D events
import { EventEmitter } from 'events'
export const bus = new EventEmitter() // 导出bus实例
App.css
* {
margin: 0;
padding: 0;
}
input,button {
outline: none;
border: 0;
}
ul>li {
list-style: none;
}
.container {
width: 400px;
height: 500px;
margin: 10px auto auto;
padding: 20px;
box-sizing: border-box;
color: #3333;
border: 1px solid;
overflow: hidden;
}
.input {
width: 100%;
height: 30px;
display: flex;
}
input {
width: 100%;
height: 100%;
border: 1px solid #e1e1e1;
box-sizing: border-box;
border-radius: 4px;
padding: 0 10px;
}
input::placeholder {
color: #e1e1e1;
}
input:focus {
border: 1px solid #0096e6;
}
.task-list {
width: 100%;
display: flex;
flex-flow: column wrap;
margin-top: 10px;
}
.task-list li {
display: flex;
height: 40px;
justify-content: center;
align-items: center;
padding: 0 10px;
background-color: #eef0f4;
margin-bottom: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.task-list li input[type^="checkbox"] {
width: 15px;
height: 15px;
border: 1px solid #e1e1e1;
cursor: pointer;
flex-shrink: 0;
}
.task-list li .content {
flex: 1;
margin-left: 10px;
}
.btn {
flex-shrink: 0;
display: flex;
align-items: center;
height: 30px;
justify-content: center;
padding: 5px 10px;
text-align: center;
cursor: pointer;
border-radius: 4px;
color: #fff;
letter-spacing: 2px;
margin: 0 5px;
box-sizing: border-box;
font-size: 16px;
}
.btn-success {
background-color: #0f0;
}
.btn-danger {
background-color: #f00;
}
.btn-primary {
background-color: #0096e6;
}
.task-done {
width: 100%;
height: 40px;
line-height: 40px;
display: flex;
align-items: center;
background-color: #eef0f4;
padding-left: 10px;
box-sizing: border-box;
margin-top: 30px;
}
.task-done input {
width: 15px;
height: 15px;
border: 1px solid #e1e1e1;
cursor: pointer;
flex-shrink: 0;
margin-right: 10px;
}
.single-number {
color: #333;
margin-left: 5px;
}
.all-number {
color: red;
margin-left: 5px;
}
.mm-mask{
position:fixed;
top:0;
left:0;
right:0;
bottom:0;
background:rgba(0,0,0,0.5);
}
.mm-modal{
width:350px;
position:absolute;
top:50%;
left:50%;
transform:translate(-50%,-50%);
z-index:1000;
background:#ffffff;
border-radius:4px;
color:#333333;
}
.mm-title {
height:50px;
line-height:50px;
display:flex;
justify-content:space-between;
border-bottom:1px solid #e1e1e1;
box-sizing:border-box;
font-size:20px;
}
.mm-edit{
text-indent:20px;
}
.mm-close{
margin-right:20px;
font-family:consals;
cursor:pointer;
}
.mm-content{
padding:0 20px;
margin-bottom:20px;
}
.mm-content input{
width:100%;
height:30px;
line-height:30px;
text-indent:20px;
border-radius:4px;
margin-top:20px;
border:1px solid #666;
box-sizing:border-box;
}
.mm-content input:hover{
border:1px solid #0096e6;
}
.mm-content input:last-child{
text-indent:5px;
}
.mm-box-btn{
display:flex;
}
.mm-update,.mm-cancel{
width:80px;
height:30px;
line-height:30px;
text-align: center;
cursor:pointer;
background:#0096e6;
color:#ffffff;
user-select:none;
border-radius:4px;
margin:0 20px 50px;
}
.mm-update{
margin-right:10px;
}
.d-none {
display: none;
}
.d-block {
display: block;
}
|