index.html 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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. }
  42. setFilters(text) {
  43. this.setState({
  44. filters: text,
  45. });
  46. }
  47. addTodo(title) {
  48. // 注意:this 必须是 组件实例
  49. this.setState((prevState) => ({
  50. todos: [
  51. ...prevState.todos,
  52. { id: Date.now(), title, completed: false },
  53. ],
  54. }));
  55. }
  56. toggleAllTodoStatus(completed) {
  57. // 注意:this 必须是 组件实例
  58. this.setState((prevState) => ({
  59. todos: prevState.todos.map((todo) => {
  60. todo.completed = completed;
  61. return todo;
  62. }),
  63. }));
  64. }
  65. calcUndoneTodoCount() {
  66. return this.state.todos.filter((todo) => !todo.completed).length;
  67. }
  68. // 根据filters,todos得到过滤后的代办数组
  69. renderTodos() {
  70. let { todos, filters } = this.state;
  71. if (filters === 'all') return todos;
  72. // return todos.filter((todo) =>
  73. // filters === 'active'
  74. // ? todo.completed === false
  75. // : todo.completed === true
  76. // );
  77. return todos.filter((todo) =>
  78. filters === 'active' ? !todo.completed : todo.completed
  79. );
  80. }
  81. render() {
  82. // console.log(`output->render`);
  83. return (
  84. <section className="todoapp">
  85. <TodoHeader addTodo={this.addTodo.bind(this)} />
  86. <TodoMain
  87. todos={this.renderTodos()}
  88. toggle={this.toggleAllTodoStatus.bind(this)}
  89. />
  90. <TodoFooter
  91. undoneCount={this.calcUndoneTodoCount()}
  92. setFilters={this.setFilters.bind(this)}
  93. filter={this.state.filters}
  94. />
  95. </section>
  96. );
  97. }
  98. }
  99. function TodoHeader({ addTodo }) {
  100. return (
  101. <header className="header">
  102. <h1>todos</h1>
  103. <input
  104. autoFocus="autofocus"
  105. autoComplete="off"
  106. placeholder="输入您要完成的任务?"
  107. className="new-todo"
  108. onKeyUp={(e) => {
  109. // 判断按下enter
  110. // console.log(`output->e`, e);
  111. if (e.which === 13) {
  112. let title = e.target.value.trim();
  113. if (!title) return alert('您输入的代办名称不合法!');
  114. addTodo(title);
  115. e.target.value = '';
  116. }
  117. }}
  118. />
  119. </header>
  120. );
  121. }
  122. function TodoMain({ todos, toggle }) {
  123. return (
  124. <section className="main">
  125. <input
  126. id="toggle-all"
  127. type="checkbox"
  128. className="toggle-all"
  129. onChange={(e) => {
  130. toggle(e.target.checked);
  131. }}
  132. />
  133. <label htmlFor="toggle-all"></label>
  134. <ul className="todo-list">
  135. {todos &&
  136. todos.map((todo) => <TodoItem key={todo.id} todo={todo} />)}
  137. </ul>
  138. </section>
  139. );
  140. }
  141. function TodoFooter({ undoneCount, setFilters, filter }) {
  142. return (
  143. <footer className="footer">
  144. <span className="todo-count">
  145. <strong>{undoneCount}</strong> items left
  146. </span>
  147. <ul className="filters">
  148. <li>
  149. <a
  150. href="#/all"
  151. className={filter === 'all' ? 'selected' : ''}
  152. onClick={() => {
  153. setFilters('all');
  154. }}
  155. >
  156. All
  157. </a>
  158. </li>
  159. <li>
  160. <a
  161. href="#/active"
  162. className={filter === 'active' ? 'selected' : ''}
  163. onClick={() => {
  164. setFilters('active');
  165. }}
  166. >
  167. Active
  168. </a>
  169. </li>
  170. <li>
  171. <a
  172. href="#/completed"
  173. className={filter === 'completed' ? 'selected' : ''}
  174. onClick={() => {
  175. setFilters('completed');
  176. }}
  177. >
  178. Completed
  179. </a>
  180. </li>
  181. </ul>
  182. <button className="clear-completed">Clear completed</button>
  183. </footer>
  184. );
  185. }
  186. function TodoItem({ todo }) {
  187. return (
  188. <li className="todo">
  189. <div className="view">
  190. <input
  191. type="checkbox"
  192. className="toggle"
  193. checked={todo.completed}
  194. />
  195. <label>{todo.title}</label>
  196. <button className="destroy"></button>
  197. </div>
  198. </li>
  199. );
  200. }
  201. const root = ReactDOM.createRoot(document.getElementById('root'));
  202. root.render(<App />);
  203. </script>
  204. </body>
  205. </html>