提交 54c8475e authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 e0539045
差异被折叠。
......@@ -31,7 +31,9 @@
"js-cookie": "^3.0.5",
"js-md5": "^0.8.3",
"lodash-es": "^4.17.21",
"lucide-react": "^0.474.0",
"markdown-it": "^14.1.0",
"nanoid": "^3.3.8",
"qs": "^6.11.2",
"rc-slider-captcha": "^1.3.0",
"react": "^18.3.1",
......
......@@ -122,22 +122,35 @@
}
.chapter-right {
flex: 1;
display: flex;
align-items: center;
.chapter-right-content {
flex: 1;
padding: 10px;
border-radius: 5px;
&:hover {
background-color: #e5e7eb;
}
h3 {
h3,
.chapter-title {
font-size: 14px;
font-weight: 600;
}
p {
p,
.chapter-desc {
font-size: 13px;
color: #999;
}
}
&:hover {
.chapter-right-tools {
opacity: 1;
}
}
.chapter-right-tools {
opacity: 0;
}
}
.message-file {
......
......@@ -7,33 +7,47 @@ import {
FileTextOutlined,
FileWordOutlined,
DownloadOutlined,
PlusCircleOutlined,
DeleteOutlined,
} from '@ant-design/icons'
import { ConfigProvider, Modal, Input, Button } from 'antd'
import { ConfigProvider, Modal, Input, Button, Dropdown } from 'antd'
const { TextArea } = Input
import './AISearchModal.less'
import { usePaper } from '@/hooks/useWenku'
import { useCopyToClipboard } from 'react-use'
import { CircleEllipsis } from 'lucide-react'
export default function AIModal() {
const [isModalOpen, setIsModalOpen] = useState(true)
const [content, setContent] = useState('商业数据分析')
const [content, setContent] = useState('')
const [textIndent, setTextIndent] = useState(0)
const prePromptRef = useRef(null)
const messageScrollRef = useRef(null)
const { messages, setMessages, isLoading, generateOutline, generatePaper } = usePaper()
const {
messages,
setMessages,
isLoading,
generateOutline,
generatePaper,
chapterTagRender,
getChaptersMarkdown,
addChapter,
updateChapter,
removeChapter,
} = usePaper()
useEffect(() => {
if (prePromptRef.current) {
const width = prePromptRef.current.offsetWidth + 10
setTextIndent(width)
}
}, [isModalOpen])
const [isEditMessage, setIsEditMessage] = useState(false)
useEffect(() => {
if (messageScrollRef.current) {
if (messageScrollRef.current && !isEditMessage) {
const scrollContainer = messageScrollRef.current
scrollContainer.scrollTop = scrollContainer.scrollHeight
}
}, [messages])
}, [messages, isEditMessage])
const prePrompt = '帮我生成一篇课题报告,主题是:'
const handleEnterSearch = (e) => {
......@@ -45,13 +59,14 @@ export default function AIModal() {
const handleSearch = () => {
generateOutline(prePrompt + content)
setContent('')
setIsEditMessage(false)
}
const [, copyToClipboard] = useCopyToClipboard()
// 复制内容
const handleCopy = (content) => {
copyToClipboard(content)
const handleCopy = (msg) => {
copyToClipboard(getChaptersMarkdown(msg.chapters))
}
// 生成论文
......@@ -59,13 +74,26 @@ export default function AIModal() {
setMessages((prevMessages) => {
return [...prevMessages, { content: '正在生成长文...', role: 'ai', tips: '预计10分钟', queryID: msg.queryID }]
})
const paper = await generatePaper({ userQuery: msg.userQuery, queryID: msg.queryID, outline: msg.content })
const paper = await generatePaper({
userQuery: msg.userQuery,
queryID: msg.queryID,
outline: getChaptersMarkdown(msg.chapters),
})
setMessages((prevMessages) => {
prevMessages.pop()
return [...prevMessages, { content: '已为您生成初稿,请点击下载', role: 'ai', queryID: msg.queryID, paper }]
})
}
let chapterMenuItems = [
{ key: 'add', label: '新增', icon: <PlusCircleOutlined style={{ fontSize: 14 }} /> },
{ key: 'remove', label: '删除', icon: <DeleteOutlined style={{ fontSize: 14 }} /> },
]
const handleMenuClick = async (e, chapter, message) => {
setIsEditMessage(true)
if (e.key === 'add') addChapter(chapter, message)
if (e.key === 'remove') removeChapter(chapter, message)
}
const MessageRender = ({ msg }) => {
if (msg.role === 'ai') {
if (msg.chapters && msg.chapters.length) {
......@@ -73,11 +101,11 @@ export default function AIModal() {
<div className={`message-item ${msg.role}`}>
<div className="message-box">
<div className="message-content">
{msg.chapters.map((item, index) => {
{msg.chapters.map((item) => {
return (
<div className="chapter-item" key={index}>
<div className="chapter-item" key={item.id}>
<div className="chapter-left">
<div className="chapter-left-title">{item.tag}</div>
<div className="chapter-left-title">{chapterTagRender(item, msg.chapters)}</div>
<div className="line-dot">
<div className="dot"></div>
<div className="line"></div>
......@@ -85,8 +113,33 @@ export default function AIModal() {
</div>
<div className="chapter-right">
<div className="chapter-right-content">
<h3>{item.title}</h3>
<p>{item.desc}</p>
<Input
className="chapter-title"
placeholder="请输入标题"
defaultValue={item.title}
variant="borderless"
onBlur={(e) => updateChapter(item, msg, { ...item, title: e.target.value })}
/>
{item.level !== 1 && (
<Input
className="chapter-desc"
placeholder="请输入你的备注,如这个章节必须包含正反观点等。"
defaultValue={item.desc}
variant="borderless"
onBlur={(e) => updateChapter(item, msg, { ...item, desc: e.target.value })}
/>
)}
{/* <h3>{item.title}</h3> */}
{/* <p>{item.desc}</p> */}
</div>
<div className="chapter-right-tools">
{item.level !== 1 && (
<Dropdown
menu={{ items: chapterMenuItems, onClick: (e) => handleMenuClick(e, item, msg) }}
getPopupContainer={(triggerNode) => triggerNode.parentNode}>
<Button type="text" icon={<CircleEllipsis color="#222" size={20} />}></Button>
</Dropdown>
)}
</div>
</div>
</div>
......@@ -110,7 +163,7 @@ export default function AIModal() {
onClick={() => handleGeneratePaper(msg)}>
生成长文
</Button>
<Button type="text" size="small" icon={<CopyOutlined />} onClick={() => handleCopy(msg.content)}>
<Button type="text" size="small" icon={<CopyOutlined />} onClick={() => handleCopy(msg)}>
复制内容
</Button>
</div>
......@@ -163,8 +216,8 @@ export default function AIModal() {
onCancel={() => setIsModalOpen(false)}
width={1000}>
<div className="message-scroll" ref={messageScrollRef}>
{messages.map((msg, index) => {
return <MessageRender msg={msg} key={index}></MessageRender>
{messages.map((msg) => {
return <MessageRender msg={msg} key={msg.id}></MessageRender>
})}
</div>
<div className="input-container">
......
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论