TodoDrawer.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import React, { useImperativeHandle, forwardRef, useState, useEffect } from "react";
  2. import { DownOutlined, PlusOutlined, DeleteOutlined } from "@ant-design/icons";
  3. import type { MenuProps } from "antd";
  4. import {
  5. Dropdown,
  6. Space,
  7. Drawer,
  8. Button,
  9. Checkbox,
  10. Input,
  11. Popover,
  12. Radio,
  13. Tooltip,
  14. } from "antd";
  15. import { useModel } from "umi";
  16. import { TodoItem } from "@/type";
  17. import { uuid } from "@repo/utils";
  18. import noData from "@/assets/no-data.png";
  19. const TodoDrawer = forwardRef((props: {}, ref) => {
  20. const [open, setOpen] = React.useState(false);
  21. const { project, setProject } = useModel("erModel");
  22. const [activeKey, setActiveKey] = React.useState("");
  23. const [todoList, setTodoList] = useState<TodoItem[]>(project.todos);
  24. useEffect(() => {
  25. setTodoList(project.todos);
  26. }, [project.todos]);
  27. useImperativeHandle(ref, () => ({
  28. open: () => setOpen(true),
  29. close: () => setOpen(false),
  30. }));
  31. const items: MenuProps["items"] = [
  32. {
  33. key: "1",
  34. label: "按优先级排序",
  35. },
  36. {
  37. key: "2",
  38. label: "按完成时间排序",
  39. },
  40. {
  41. key: "3",
  42. label: "按创建时间排序",
  43. },
  44. ];
  45. const levelMap = {
  46. 0: {
  47. label: "无",
  48. color: "#1890ff",
  49. },
  50. 1: {
  51. label: "低",
  52. color: "#52c41a",
  53. },
  54. 2: {
  55. label: "中",
  56. color: "#d89614",
  57. },
  58. 3: {
  59. label: "高",
  60. color: "#ff4d4f",
  61. },
  62. };
  63. const handleChange = (index: number, key: string, value: any) => {
  64. const newTodos = [...todoList];
  65. newTodos[index] = {
  66. ...newTodos[index],
  67. [key]: value,
  68. };
  69. setTodoList(newTodos);
  70. setProject(
  71. {
  72. ...project,
  73. todos: newTodos,
  74. },
  75. false,
  76. false,
  77. true
  78. );
  79. };
  80. const addToto = () => {
  81. setProject(
  82. {
  83. ...project,
  84. todos: [
  85. ...todoList,
  86. {
  87. id: uuid(),
  88. dataModelId: project.id,
  89. name: "",
  90. text: "",
  91. isDone: false,
  92. level: 1,
  93. },
  94. ],
  95. },
  96. false,
  97. false,
  98. true
  99. );
  100. };
  101. const handleDelete = (index: number) => {
  102. const newTodos = [...todoList];
  103. newTodos.splice(index, 1);
  104. setProject(
  105. {
  106. ...project,
  107. todos: newTodos,
  108. },
  109. false,
  110. false,
  111. true
  112. );
  113. };
  114. return (
  115. <Drawer
  116. open={open}
  117. placement="right"
  118. width={400}
  119. title={<>待办事项</>}
  120. onClose={() => setOpen(false)}
  121. >
  122. <div className="flex justify-between m-b-12px">
  123. <Dropdown menu={{ items }}>
  124. <Space>
  125. 排序方式
  126. <DownOutlined />
  127. </Space>
  128. </Dropdown>
  129. <Button type="primary" icon={<PlusOutlined />} onClick={addToto}>
  130. 新建待办
  131. </Button>
  132. </div>
  133. {todoList.map((todo: TodoItem, index: number) => (
  134. <div
  135. className="todo-item flex border-b-1 border-b-solid border-#eee p-b-12px m-b-12px"
  136. key={todo.id}
  137. onClick={() => setActiveKey(todo.id)}
  138. >
  139. <div className="left m-r-12px">
  140. <Tooltip title="完成"><Checkbox
  141. checked={todo.isDone}
  142. onChange={(e) => {
  143. handleChange(index, "isDone", e.target.checked);
  144. }}
  145. /></Tooltip>
  146. </div>
  147. <div className="right flex-1">
  148. <div className="flex m-b-12px">
  149. <Input
  150. placeholder="标题..."
  151. value={todo.name}
  152. onChange={(e) => handleChange(index, "name", e.target.value)}
  153. />
  154. <Popover
  155. trigger={["click"]}
  156. placement="bottom"
  157. content={
  158. <div className="w-200px">
  159. <div className="font-bold">优先级:</div>
  160. <div className="p-l-12px">
  161. <Radio.Group value={todo.level} onChange={(e) => handleChange(index, "level", e.target.value)}>
  162. <Space direction="vertical">
  163. <Radio value={0}>无</Radio>
  164. <Radio value={1}>低</Radio>
  165. <Radio value={2}>中</Radio>
  166. <Radio value={3}>高</Radio>
  167. </Space>
  168. </Radio.Group>
  169. </div>
  170. <div>
  171. <Button danger icon={<DeleteOutlined />} className="m-t-12px w-full" onClick={() => {handleDelete(index)}}>删除</Button>
  172. </div>
  173. </div>
  174. }
  175. >
  176. <div className="rounded-4px cus-btn w-32px h-32px bg-#eee flex-none text-center leading-32px cursor-pointer hover:bg-#ddd m-l-12px">
  177. <i className="iconfont icon-gengduo" />
  178. </div>
  179. </Popover>
  180. </div>
  181. <div
  182. className="m-b-12px grid"
  183. style={{
  184. transition: "all 0.3s",
  185. gridTemplateRows: activeKey === todo.id ? "1fr" : "0fr",
  186. }}
  187. >
  188. <div className="overflow-hidden">
  189. <Input.TextArea
  190. placeholder="描述..."
  191. value={todo.text}
  192. onChange={(e) => handleChange(index, "text", e.target.value)}
  193. />
  194. </div>
  195. </div>
  196. <div>
  197. 优先级:{" "}
  198. <span
  199. className="inline-block w-20px h-20px bg-red-500 color-#fff rounded-4px text-center leading-20px"
  200. style={{
  201. background: levelMap[todo.level].color,
  202. fontSize: "12px",
  203. }}
  204. >
  205. {levelMap[todo.level].label}
  206. </span>
  207. </div>
  208. </div>
  209. </div>
  210. ))}
  211. {project.todos.length === 0 && (
  212. <div className="flex flex-col items-center justify-center h-[300px]">
  213. <img src={noData} alt="暂无数据" className="w-[200px] h-[200px]" />
  214. <div className="text-gray-400">暂无待办项!</div>
  215. </div>
  216. )}
  217. </Drawer>
  218. );
  219. });
  220. export default TodoDrawer;