提交 350da781 authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 068ee8d5
import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
export default function MarkdownRender(props: { children?: string }) {
return <Markdown remarkPlugins={[remarkGfm]}>{props.children}</Markdown>
}
......@@ -2,9 +2,8 @@ import { useState, KeyboardEvent, useEffect, useRef } from 'react'
import { Button, Card, FloatButton, Input, Select } from 'antd'
import { CircleArrowLeft, CircleArrowRight } from 'lucide-react'
import { OpenAIOutlined, ArrowUpOutlined } from '@ant-design/icons'
import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import { useAIStore, AIMessage } from '@/stores/ai'
import MarkdownRender from '../MarkdownRender'
import './AIChat.scss'
export const MessageItem = ({ message }: { message: AIMessage }) => {
......@@ -12,7 +11,7 @@ export const MessageItem = ({ message }: { message: AIMessage }) => {
<div className={`message-item ${message.role}`}>
<div className="message-box">
<div className="message-content">
<Markdown remarkPlugins={[remarkGfm]}>{message.content}</Markdown>
<MarkdownRender>{message.content}</MarkdownRender>
</div>
</div>
</div>
......
import { useState, useEffect, useCallback } from 'react'
import aiService, { AIMessage, AIData, AI_OPTIONS } from '@/utils/ai'
import aiService, { AIMessage, AIData, AI_OPTIONS, InitOptions } from '@/utils/ai'
export function useAI() {
export function useAI(globalOptions?: InitOptions) {
const [ai, setAI] = useState<string>(localStorage.getItem('ai') || 'qwen')
const [messages, setMessages] = useState<AIMessage[]>([])
const [message, setMessage] = useState<AIMessage | null>(null) // 存储最新的消息
const [isLoading, setIsLoading] = useState<boolean>(false)
useEffect(() => {
localStorage.setItem('ai', ai)
}, [ai])
const post = useCallback(
async (data: AIData) => {
async (data: AIData, options?: InitOptions) => {
setIsLoading(true)
setMessages((prevMessages) => [...prevMessages, ...data.messages.filter((item) => item.role !== 'system')])
try {
await aiService.post(ai, data, {
onUpdate: (response) => {
setMessages((prevMessages) => {
const messageIndex = prevMessages.findIndex((msg) => msg.id === response.id)
if (messageIndex === -1) {
return [...prevMessages, { id: response.id, role: 'assistant', content: response.content }]
} else {
return prevMessages.map((msg) =>
msg.id === response.id ? { ...msg, content: msg.content + response.content } : msg
)
}
})
},
onError: (err) => {
console.error('AI 请求失败:', err)
setIsLoading(false)
},
onComplete: () => {
setIsLoading(false)
},
})
} catch (err) {
console.error('AI 请求失败:', err)
setIsLoading(false)
}
// 先添加用户消息(去掉 system 角色的消息)
const userMessages = data.messages.filter((item) => item.role !== 'system')
setMessages((prevMessages) => [...prevMessages, ...userMessages])
await aiService.post(ai, data, {
onMessage: (response) => {
setMessages((prevMessages) => {
const messageIndex = prevMessages.findIndex((msg) => msg.id === response.id)
if (messageIndex === -1) {
const newMessage: AIMessage = { id: response.id, role: 'assistant', content: response.content }
setMessage(newMessage) // 更新最新消息
return [...prevMessages, newMessage]
} else {
return prevMessages.map((msg) => {
if (msg.id === response.id) {
const updatedMessage = { ...msg, content: msg.content + response.content }
setMessage(updatedMessage) // 更新最新消息
return updatedMessage
}
return msg
})
}
})
},
onerror: () => {
setIsLoading(false)
},
onclose: () => {
setIsLoading(false)
},
...globalOptions,
...options,
})
},
[ai]
)
return { ai, setAI, options: AI_OPTIONS, post, messages, isLoading }
return { ai, setAI, options: AI_OPTIONS, post, messages, message, isLoading }
}
import { Button, Flex, Modal, Spin } from 'antd'
import { useEffect, useState } from 'react'
import { useAI } from '@/hooks/useAI'
import MarkdownRender from '@/components/MarkdownRender'
export default function DataReport() {
const [open, setOpen] = useState(false)
const { post, isLoading, message } = useAI()
useEffect(() => {
if (open) {
post({
messages: [
{
role: 'user',
content:
'作为数据分析师,请基于提供的数据集,生成一份结构化的数据质量分析报告。需包含字段解释、关系梳理、质量评估及改进建议。',
},
],
})
}
}, [open])
return (
<>
<Button type="primary" onClick={() => setOpen(true)}>
数据分析报告
</Button>
<Modal title="数据分析报告" open={open} footer={null} width={1000} onCancel={() => setOpen(false)} destroyOnClose>
<MarkdownRender>{message?.content}</MarkdownRender>
<Flex justify="center">
<Spin size="large" spinning={isLoading}></Spin>
</Flex>
</Modal>
</>
)
}
import { Link } from 'react-router'
import { Button, Empty, Flex, Space } from 'antd'
import DataWrap from '@/components/data/DataWrap'
import DataReport from '../components/DataReport'
// 无数据渲染
const EmptyRender = () => {
......@@ -37,7 +38,7 @@ export default function DataWriteMy() {
headerRender={(data) => (
<Flex justify="space-between" align="middle" style={{ marginBottom: '20px' }}>
<h4>数据集名称:{data.info.name}</h4>
<Button type="primary">数据质量分析报告</Button>
<DataReport />
</Flex>
)}
empty={<EmptyRender />}></DataWrap>
......
import { create } from 'zustand'
import aiService, { AIOption, AIMessage, AIData, AI_OPTIONS } from '@/utils/ai'
import aiService, { AIOption, AIMessage, AIData, AI_OPTIONS, InitOptions } from '@/utils/ai'
interface AIState {
ai: string
options: AIOption[]
message: AIMessage | null
messages: AIMessage[]
isLoading: boolean
collapsed: boolean
setAI: (ai: string) => void
toggleCollapsed: () => void
post: (data: AIData) => Promise<void>
post: (data: AIData, options?: InitOptions) => Promise<void>
}
export const useAIStore = create<AIState>((set, get) => ({
ai: localStorage.getItem('ai') || 'qwen',
options: AI_OPTIONS,
message: null,
messages: [],
isLoading: false,
collapsed: false,
......@@ -25,40 +27,52 @@ export const useAIStore = create<AIState>((set, get) => ({
toggleCollapsed: () => {
set((state) => ({ collapsed: !state.collapsed }))
},
post: async (data) => {
post: async (data, options) => {
const { ai, messages } = get()
// 处理用户消息(去掉 system 角色的消息)
const userMessages = data.messages.filter((item) => item.role !== 'system')
set({
collapsed: true,
isLoading: true,
messages: [...messages, ...data.messages.filter((item) => item.role !== 'system')],
messages: [...messages, ...userMessages],
})
try {
await aiService.post(ai, data, {
onUpdate: (response) => {
onMessage: (response) => {
set((state) => {
const messageIndex = state.messages.findIndex((msg) => msg.id === response.id)
if (messageIndex === -1) {
// 新的 AI 回复
const newMessage: AIMessage = { id: response.id, role: 'assistant', content: response.content }
return {
messages: [...state.messages, { id: response.id, role: 'assistant', content: response.content }],
message: newMessage, // 存储最新的 AI 消息
messages: [...state.messages, newMessage], // 追加到历史消息
}
} else {
// 追加内容到已有的消息
const updatedMessages = state.messages.map((msg) =>
msg.id === response.id ? { ...msg, content: msg.content + response.content } : msg
)
return {
messages: state.messages.map((msg) =>
msg.id === response.id ? { ...msg, content: msg.content + response.content } : msg
),
message: updatedMessages[messageIndex], // 更新最新的 AI 消息
messages: updatedMessages,
}
}
})
},
onError: (err) => {
onerror: (err) => {
console.error('AI 请求失败:', err)
set({ isLoading: false })
},
onComplete: () => {
onclose: () => {
set({ isLoading: false })
},
...options,
})
} catch (err) {
console.error('AI 请求失败:', err)
......
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论