使用 React Hooks + mock + antd构建一

2019-12-01  本文已影响0人  梁坤同学

实现效果:

todoList.gif

搭建项目

使用 create-react-app 快速创建一个项目,删除不必要的文件,保留文件如下:


文件目录

目录说明

在项目中引入 antd,具体方法见 在 create-react-app 中使用 antd

各部分代码展示

mock.js
// 引入 mock 模拟数据
import Mock from 'mockjs'

const Random = Mock.Random

Random.extend({
  planInfo: function(date) {
    const Info = ["吃饭", "睡觉", "打豆豆"]
    return this.pick(Info)
  }
})

const data = Mock.mock('/fakeData', 'get', {
  success: true,
  message: 'success',
  todoList: {
    "filter": "SHOW_ALL",
    "list|1-10": [
      {
        "id|+1": 1,
        "content": '@planInfo',
        "isDone": Random.boolean(),
        "dataTime": Random.date('yyyy-MM-dd')
      },
    ]
  }
})

export default data
TodoList.js
import React, { useState, useEffect } from "react";
import axios from "axios";
import "./mock";

import { Input, List, Button, Icon } from "antd";

const { Search } = Input;

export default function TodoList() {
  //  data - 完整的 todo 数据
  const [data, setData] = useState([]);

  // 在此处请求接口数据并初始化 todoList,
  //  useEffect 传入第二个参数为空数组,使得该 effect 仅执行一次
  useEffect(() => {
    (async () => {
      const res = await axios.get("/fakeData");
      const { list } = res.data.todoList;
      setData(list);
    })();
  }, []);

  // 展示的数据,之所以出现需要出现这个数据,
  // 是因为我们的所有操作都是在前端完成的,不存在请求接口调用数据。  
  // 那么我们在进行展示 "已完成" "未完成" 的不同任务时,就需要全部的数据不会被覆盖,showArr 只负责数据
  const [showArr, setShowArr] = useState([].concat(data));

  // 当 data 发生改变时,执行该 effect,替换 showArr
  useEffect(() => {
    setShowArr([].concat(data));
  }, [data]);

  // 实现任务输入框的双向绑定
  const [inputValue, setInputValue] = useState("");

  // 控制当前展示任务按钮样式 active
  const [active, setActive] = useState("SHOW_ALL");

  function addTodo(content) {
    const newData = [].concat(data);
    newData.push({
      id: Date.now(),
      content: content,
      isDone: false,
      dataTime: "2019-10-28"
    });
    setData(newData);
    setInputValue("");
  }

  function showStatusList(showStatus) {
    if (showStatus === "SHOW_COMPLETED") {
      const newData = data.filter(item => item.isDone);
      setShowArr([].concat(newData));
      setActive("SHOW_COMPLETED");
    } else if (showStatus === "SHOW_ACTIVE") {
      const newData = data.filter(item => !item.isDone);
      setShowArr([].concat(newData));
      setActive("SHOW_ACTIVE");
    } else {
      setShowArr([].concat(data));
      setActive("SHOW_ALL");
    }
  }

  function finishTodo(id) {
    const newData = [].concat(data);
    const index = newData.findIndex(item => item.id === id);
    newData[index].isDone = true;
    setData(newData);
  }

  function toggleTodo(id) {
    const newData = [].concat(data);
    const index = newData.findIndex(item => item.id === id);
    newData[index].isDone = !newData[index].isDone;
    setData(newData);
  }

  function deleteTodo(id) {
    const newData = [].concat(data);
    const index = newData.findIndex(item => item.id === id);
    newData.splice(index, 1);
    setData(newData);
  }

  function todoHeader() {
    return (
      <div className={"mainHeader"}>
        任务列表
        <div className="mainHandle">
          <Button
            size="small"
            type="default"
            className={[
              "classifyBtn",
              active === "SHOW_ALL" ? "active" : null
            ].join(" ")}
            onClick={() => showStatusList("SHOW_ALL")}
          >
            全部
          </Button>
          <Button
            size="small"
            type="default"
            className={[
              "classifyBtn",
              active === "SHOW_COMPLETED" ? "active" : null
            ].join(" ")}
            onClick={() => showStatusList("SHOW_COMPLETED")}
          >
            已完成
          </Button>
          <Button
            size="small"
            type="default"
            className={[
              "classifyBtn",
              active === "SHOW_ACTIVE" ? "active" : null
            ].join(" ")}
            onClick={() => showStatusList("SHOW_ACTIVE")}
          >
            未完成
          </Button>
        </div>
      </div>
    );
  }

  return (
    <div className="todoList">
      <div className="todoHeader">
        <h1 className="card-title">React Hooks Todo</h1>
        <span className="card-subtitle">添加任务,管理每日计划</span>
      </div>
      <div className="todoSearch">
        <Search
          placeholder="今天完成什么计划?"
          enterButton="添加任务"
          size="default"
          value={inputValue}
          onChange={e => setInputValue(e.target.value)}
          onSearch={(content, event) => addTodo(content, event)}
        />
      </div>
      <div className="todoMain">
        <List
          header={todoHeader()}
          footer={
            <div className={"mainFooter"}>共 {showArr.length} 项任务</div>
          }
          bordered
          dataSource={showArr}
          renderItem={item => (
            <List.Item>
              <Button
                size="small"
                type={item.isDone ? "primary" : "danger"}
                className="status"
              >
                {item.isDone ? "完成" : "未完成"}
              </Button>
              <p className="content">{item.content}</p>
              <div className="operate">
                {/* <span>{item.dataTime}</span> */}
                {item.isDone ? (
                  ""
                ) : (
                  <Icon
                    type="check-square"
                    style={{ color: "green" }}
                    onClick={() => finishTodo(item.id)}
                  />
                )}
                <Icon
                  type="retweet"
                  style={{ color: "#E7AF62" }}
                  onClick={() => toggleTodo(item.id)}
                />
                <Icon
                  type="close"
                  style={{ color: "red" }}
                  onClick={() => deleteTodo(item.id)}
                />
              </div>
            </List.Item>
          )}
        />
      </div>
    </div>
  );
}

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.less';
import TodoList from './TodoList';

ReactDOM.render(<TodoList />, document.getElementById('root'));
index.less
.todoHeader {
  width: 100%;
  height: 200px;
  background: #eee;
  position: relative;
  text-align: center;
  .card-title {
    padding: 50px 0 30px 0; 
    margin: 0;
    font-size: 44px;
  }
  .card-subtitle {
    color: #666;
    font-size: 14px;
  }
}
.todoSearch {
  width: 100%;
  padding: 20px 25%;
}
.todoMain {
  width: 100%;
  padding: 0 15%;
  border-color: #1890ff;
  .mainHeader {
    .mainHandle {
      float: right;
      button {
        margin: 0 5px;
      }
      .active {
        background: #96D196;
        border-color: #96D196;
        color: #fff;
      }
    }
  }
  .ant-list {
    border-color: #1890ff;
    .ant-list-header {
      color: #fff;
      background-color: #1890ff;
      font-size: 16px;
    }
    .ant-list-item {
      display: flex;
      .status {
        flex: 1;
      }
      .content {
        flex: 9;
        padding-left: 5px;
        margin: 0;
      }
      .operate {
        flex: 4;
        text-align: right;
        i {
          padding-left: 5px;
          cursor: pointer;
        }
      }
    }
    .ant-list-footer {
      background: #eee;
      .mainFooter {
        text-align: right;
      }
    }
  }
}

上一篇下一篇

猜你喜欢

热点阅读