MsgUser.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import { useCallback, useEffect, useState } from "react";
  2. import type { Message } from "./AiChat"
  3. import Marked from "../general/Marked";
  4. import TextArea from "antd/lib/input/TextArea";
  5. import { Button, message, Space, Tooltip } from "antd";
  6. import {
  7. CheckOutlined,
  8. CloseOutlined,
  9. CopyOutlined,
  10. EditOutlined,
  11. } from "@ant-design/icons";
  12. interface IWidget {
  13. msg?: Message;
  14. onChange?: (value: string) => void;
  15. }
  16. const MsgUser = ({ msg, onChange }: IWidget) => {
  17. const [editing, setEditing] = useState(false);
  18. const [current, _setCurrent] = useState(0);
  19. const [content, setContent] = useState<string>("");
  20. useEffect(() => {
  21. if (msg?.versions && msg?.versions.length > 0) {
  22. setContent(msg.versions[current].content);
  23. }
  24. }, [current, msg]);
  25. const confirmEdit = useCallback((): void => {
  26. onChange && onChange(content);
  27. }, [content, onChange]);
  28. const cancelEdit = useCallback((): void => {
  29. setEditing(false);
  30. }, []);
  31. const handleEditKeyPress = useCallback(
  32. (e: React.KeyboardEvent<HTMLTextAreaElement>): void => {
  33. if (e.key === "Enter" && e.ctrlKey) {
  34. e.preventDefault();
  35. confirmEdit();
  36. } else if (e.key === "Escape") {
  37. cancelEdit();
  38. }
  39. },
  40. [cancelEdit, confirmEdit]
  41. );
  42. return (
  43. <div
  44. style={{
  45. display: "flex",
  46. justifyContent: "flex-end",
  47. }}
  48. >
  49. <div
  50. style={{
  51. maxWidth: "70%",
  52. minWidth: 400,
  53. backgroundColor: "rgba(255, 255, 255, 0.8)",
  54. color: "black",
  55. borderRadius: "8px",
  56. padding: "16px",
  57. border: "none",
  58. boxShadow: "0 1px 2px rgba(0, 0, 0, 0.03)",
  59. textAlign: "left",
  60. }}
  61. >
  62. {editing ? (
  63. <div style={{ width: "100%" }}>
  64. <TextArea
  65. value={content}
  66. onChange={(e) => setContent(e.target.value)}
  67. onKeyPress={handleEditKeyPress}
  68. autoSize={{ minRows: 2, maxRows: 8 }}
  69. style={{ marginBottom: "8px", width: "100%" }}
  70. />
  71. <Space size="small">
  72. <Button
  73. size="small"
  74. type="primary"
  75. icon={<CheckOutlined />}
  76. onClick={() => confirmEdit()}
  77. >
  78. 确认
  79. </Button>
  80. <Button
  81. size="small"
  82. icon={<CloseOutlined />}
  83. onClick={cancelEdit}
  84. >
  85. 取消
  86. </Button>
  87. </Space>
  88. </div>
  89. ) : (
  90. <div>
  91. <div>
  92. <Marked text={msg?.versions[current].content} />
  93. </div>
  94. <div
  95. style={{
  96. fontSize: "12px",
  97. opacity: 0.6,
  98. marginTop: "8px",
  99. }}
  100. >
  101. {msg?.versions[current].timestamp}
  102. </div>
  103. <div>
  104. <Space size="small">
  105. <Tooltip title="复制">
  106. <Button
  107. size="small"
  108. type="text"
  109. icon={<CopyOutlined />}
  110. onClick={() => {
  111. msg &&
  112. navigator.clipboard
  113. .writeText(msg.versions[current].content)
  114. .then((_value) => message.success("已复制到剪贴板"))
  115. .catch((reason: any) => {
  116. console.error("复制失败:", reason);
  117. message.error("复制失败");
  118. });
  119. }}
  120. />
  121. </Tooltip>
  122. <Tooltip title="复制">
  123. <Button
  124. size="small"
  125. type="text"
  126. icon={<EditOutlined />}
  127. onClick={() => {
  128. msg && setEditing(true);
  129. }}
  130. />
  131. </Tooltip>
  132. </Space>
  133. </div>
  134. </div>
  135. )}
  136. </div>
  137. </div>
  138. );
  139. };
  140. export default MsgUser;