index.html 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <title>代办事项</title>
  8. <script src="./babel.min.js"></script>
  9. <script src="./react.development.js"></script>
  10. <script src="./react-dom.development.js"></script>
  11. <link rel="stylesheet" href="./index.css" />
  12. </head>
  13. <body>
  14. <div id="root"></div>
  15. <script type="text/babel">
  16. function App() {
  17. return (
  18. <>
  19. <TodoApp />
  20. </>
  21. );
  22. }
  23. class TodoApp extends React.Component {
  24. constructor() {
  25. super();
  26. this.state = {
  27. todos: [
  28. {
  29. id: 1,
  30. title: '吃饭',
  31. completed: false,
  32. },
  33. {
  34. id: 2,
  35. title: '睡觉',
  36. completed: true,
  37. },
  38. ], // 所有代办, 默认值 为 []
  39. filters: 'all', // 过滤条件 值为 "all | active | completed", 默认值 all
  40. };
  41. this.clearCompletedTodos = this.clearCompletedTodos.bind(this);
  42. this.removeTodoById = this.removeTodoById.bind(this);
  43. this.toggleTodoStatus = this.toggleTodoStatus.bind(this);
  44. }
  45. toggleTodoStatus(id) {
  46. this.setState(({ todos }) => {
  47. for (var i = 0, l = todos.length; i < l; i++) {
  48. if (todos[i].id === id) {
  49. todos[i].completed = !todos[i].completed;
  50. break;
  51. }
  52. }
  53. return {
  54. todos,
  55. };
  56. });
  57. }
  58. removeTodoById(id) {
  59. if (!confirm('确定删除当前代办吗?')) return;
  60. this.setState((prevState) => ({
  61. todos: prevState.todos.filter((todo) => todo.id !== id),
  62. }));
  63. }
  64. clearCompletedTodos() {
  65. if (!confirm('确定清楚所有已完成的代办吗?')) return;
  66. this.setState((prevState) => ({
  67. todos: prevState.todos.filter((todo) => !todo.completed),
  68. }));
  69. }
  70. setFilters(text) {
  71. this.setState({
  72. filters: text,
  73. });
  74. }
  75. addTodo(title) {
  76. // 注意:this 必须是 组件实例
  77. this.setState((prevState) => ({
  78. todos: [
  79. ...prevState.todos,
  80. { id: Date.now(), title, completed: false },
  81. ],
  82. }));
  83. }
  84. toggleAllTodoStatus(completed) {
  85. // 注意:this 必须是 组件实例
  86. this.setState((prevState) => ({
  87. todos: prevState.todos.map((todo) => {
  88. todo.completed = completed;
  89. return todo;
  90. }),
  91. }));
  92. }
  93. calcUndoneTodoCount() {
  94. return this.state.todos.filter((todo) => !todo.completed).length;
  95. }
  96. // 根据filters,todos得到过滤后的代办数组
  97. renderTodos() {
  98. let { todos, filters } = this.state;
  99. if (filters === 'all') return todos;
  100. // return todos.filter((todo) =>
  101. // filters === 'active'
  102. // ? todo.completed === false
  103. // : todo.completed === true
  104. // );
  105. return todos.filter((todo) =>
  106. filters === 'active' ? !todo.completed : todo.completed
  107. );
  108. }
  109. render() {
  110. // console.log(`output->render`);
  111. return (
  112. <section className="todoapp">
  113. <TodoHeader addTodo={this.addTodo.bind(this)} />
  114. <TodoMain
  115. todos={this.renderTodos()}
  116. toggleAll={this.toggleAllTodoStatus.bind(this)}
  117. remove={this.removeTodoById}
  118. toggle={this.toggleTodoStatus}
  119. />
  120. <TodoFooter
  121. undoneCount={this.calcUndoneTodoCount()}
  122. setFilters={this.setFilters.bind(this)}
  123. filter={this.state.filters}
  124. clear={this.clearCompletedTodos}
  125. />
  126. </section>
  127. );
  128. }
  129. }
  130. function TodoHeader({ addTodo }) {
  131. return (
  132. <header className="header">
  133. <h1>todos</h1>
  134. <input
  135. autoFocus="autofocus"
  136. autoComplete="off"
  137. placeholder="输入您要完成的任务?"
  138. className="new-todo"
  139. onKeyUp={(e) => {
  140. // 判断按下enter
  141. // console.log(`output->e`, e);
  142. if (e.which === 13) {
  143. let title = e.target.value.trim();
  144. if (!title) return alert('您输入的代办名称不合法!');
  145. addTodo(title);
  146. e.target.value = '';
  147. }
  148. }}
  149. />
  150. </header>
  151. );
  152. }
  153. function TodoMain({ todos, toggle, remove, toggleAll }) {
  154. return (
  155. <section className="main">
  156. <input
  157. id="toggle-all"
  158. type="checkbox"
  159. className="toggle-all"
  160. onChange={(e) => {
  161. toggleAll(e.target.checked);
  162. }}
  163. />
  164. <label htmlFor="toggle-all"></label>
  165. <ul className="todo-list">
  166. {todos &&
  167. todos.map((todo) => (
  168. <TodoItem
  169. key={todo.id}
  170. todo={todo}
  171. onRemove={remove}
  172. onToggle={toggle}
  173. />
  174. ))}
  175. </ul>
  176. </section>
  177. );
  178. }
  179. function TodoFooter({ undoneCount, setFilters, filter, clear }) {
  180. return (
  181. <footer className="footer">
  182. <span className="todo-count">
  183. <strong>{undoneCount}</strong> items left
  184. </span>
  185. <ul className="filters">
  186. <li>
  187. <a
  188. href="#/all"
  189. className={filter === 'all' ? 'selected' : ''}
  190. onClick={() => {
  191. setFilters('all');
  192. }}
  193. >
  194. All
  195. </a>
  196. </li>
  197. <li>
  198. <a
  199. href="#/active"
  200. className={filter === 'active' ? 'selected' : ''}
  201. onClick={() => {
  202. setFilters('active');
  203. }}
  204. >
  205. Active
  206. </a>
  207. </li>
  208. <li>
  209. <a
  210. href="#/completed"
  211. className={filter === 'completed' ? 'selected' : ''}
  212. onClick={() => {
  213. setFilters('completed');
  214. }}
  215. >
  216. Completed
  217. </a>
  218. </li>
  219. </ul>
  220. <button className="clear-completed" onClick={clear}>
  221. Clear completed
  222. </button>
  223. </footer>
  224. );
  225. }
  226. function TodoItem({ todo, onRemove, onToggle }) {
  227. return (
  228. <li className="todo">
  229. <div className="view">
  230. <input
  231. type="checkbox"
  232. className="toggle"
  233. checked={todo.completed}
  234. onChange={() => onToggle(todo.id)}
  235. />
  236. <label>{todo.title}</label>
  237. <button
  238. className="destroy"
  239. onClick={() => onRemove(todo.id)}
  240. ></button>
  241. </div>
  242. </li>
  243. );
  244. }
  245. const root = ReactDOM.createRoot(document.getElementById('root'));
  246. root.render(<App />);
  247. </script>
  248. </body>
  249. </html>