feat(搜索页面): 添加AI回答功能及优化搜索界面布局
- 新增AI回答功能,包含折叠展示和全屏模态框 - 调整搜索框布局和样式 - 使用Empty组件替换自定义空状态 - 移除未使用的Title组件
This commit is contained in:
1 parent
2ce687ba0e
commit
eb2db40b62
1 file changed
+48
-25
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Input, List, Card, Modal, Empty, Typography, Tag, Button, Switch } from 'antd'
|
||||
import { SendOutlined } from '@ant-design/icons'
|
||||
import { SendOutlined, ExpandOutlined } from '@ant-design/icons'
|
||||
import { Document, Page, pdfjs } from 'react-pdf'
|
||||
import DocViewer, { DocViewerRenderers } from 'react-doc-viewer'
|
||||
import 'react-pdf/dist/Page/AnnotationLayer.css'
|
||||
@@ -16,7 +16,6 @@ pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
||||
).toString()
|
||||
|
||||
const { TextArea } = Input
|
||||
const { Title } = Typography
|
||||
|
||||
const SearchPage: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
@@ -26,6 +25,8 @@ const SearchPage: React.FC = () => {
|
||||
const [currentDoc, setCurrentDoc] = useState<SearchResult | null>(null)
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
const [aiAnswerEnabled, setAiAnswerEnabled] = useState(false)
|
||||
const [aiAnswer, setAiAnswer] = useState<string>('')
|
||||
const [aiAnswerModalVisible, setAiAnswerModalVisible] = useState(false)
|
||||
|
||||
const handleSearch = async (value: string) => {
|
||||
if (!value.trim()) return
|
||||
@@ -34,6 +35,10 @@ const SearchPage: React.FC = () => {
|
||||
const response = await window.api.search(value)
|
||||
setResults(response.results)
|
||||
setTokens(response.tokens)
|
||||
if (aiAnswerEnabled) {
|
||||
const snippet = response.results?.[0]?.text || ''
|
||||
setAiAnswer(snippet || 'AI 正在准备回答...')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Search error:', error)
|
||||
} finally {
|
||||
@@ -41,7 +46,7 @@ const SearchPage: React.FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const openPreview = (item: SearchResult) => {
|
||||
setCurrentDoc(item)
|
||||
@@ -89,14 +94,14 @@ const SearchPage: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#fafafa] text-black p-4 font-sans">
|
||||
<div className="max-w-3xl mx-auto pt-8">
|
||||
<div className=" mx-auto pt-8 px-5">
|
||||
{/* Search Box */}
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-6 shadow-sm mb-6 focus-within:ring-2 focus-within:ring-black/5 transition-all">
|
||||
<TextArea
|
||||
placeholder="基于您的资源搜索..."
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-6 shadow-sm mb-6 focus-within:ring-2 focus-within:ring-black/5 transition-all relative">
|
||||
<TextArea
|
||||
placeholder="基于您的资源搜索..."
|
||||
autoSize={{ minRows: 2, maxRows: 6 }}
|
||||
bordered={false}
|
||||
className="text-lg bg-transparent mb-4 placeholder:text-gray-400 !px-0 text-center"
|
||||
className="text-lg bg-transparent mb-4 placeholder:text-gray-400 !px-0 text-left"
|
||||
style={{ resize: 'none' }}
|
||||
value={searchValue}
|
||||
onChange={e => setSearchValue(e.target.value)}
|
||||
@@ -107,17 +112,15 @@ const SearchPage: React.FC = () => {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div className="flex justify-center items-center">
|
||||
<Button
|
||||
type="primary"
|
||||
shape="circle"
|
||||
icon={<SendOutlined />}
|
||||
size="large"
|
||||
className="bg-black hover:bg-gray-800 border-none shadow-none flex items-center justify-center"
|
||||
onClick={() => handleSearch(searchValue)}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
shape="circle"
|
||||
icon={<SendOutlined />}
|
||||
size="large"
|
||||
className="absolute bottom-6 right-6 bg-black hover:bg-gray-800 border-none shadow-none flex items-center justify-center"
|
||||
onClick={() => handleSearch(searchValue)}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* AI Answer Toggle */}
|
||||
@@ -126,9 +129,20 @@ const SearchPage: React.FC = () => {
|
||||
<Switch checked={aiAnswerEnabled} onChange={setAiAnswerEnabled} />
|
||||
</div>
|
||||
|
||||
{aiAnswerEnabled && (
|
||||
<Card className="border border-gray-200 shadow-sm mb-8">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="font-bold text-base">AI 回答</span>
|
||||
<Button type="text" icon={<ExpandOutlined />} onClick={() => setAiAnswerModalVisible(true)} />
|
||||
</div>
|
||||
<Typography.Paragraph className="text-gray-600" ellipsis={{ rows: 6 }}>
|
||||
{aiAnswer || 'AI 正在准备回答...'}
|
||||
</Typography.Paragraph>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Results */}
|
||||
<div>
|
||||
<Title level={5} className="mb-3 !text-base !font-semibold text-[#404040]">搜索结果</Title>
|
||||
<div className="min-h-[200px]">
|
||||
{results.length > 0 ? (
|
||||
<List
|
||||
@@ -160,11 +174,8 @@ const SearchPage: React.FC = () => {
|
||||
/>
|
||||
) : (
|
||||
!loading && (
|
||||
<div className="flex flex-col items-center justify-center py-20 opacity-50">
|
||||
<div className="w-24 h-24 bg-gray-100 rounded-lg mb-4 flex items-center justify-center">
|
||||
<img src={brandIcon} alt="icon" className="w-12 h-12 opacity-50" />
|
||||
</div>
|
||||
<span className="text-gray-400">暂无数据</span>
|
||||
<div className="flex items-center justify-center py-20">
|
||||
<Empty description="暂无数据" />
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
@@ -183,6 +194,18 @@ const SearchPage: React.FC = () => {
|
||||
>
|
||||
{renderPreview()}
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="AI 回答"
|
||||
open={aiAnswerModalVisible}
|
||||
onCancel={() => setAiAnswerModalVisible(false)}
|
||||
width="60%"
|
||||
footer={null}
|
||||
>
|
||||
<Typography.Paragraph style={{ marginBottom: 0 }}>
|
||||
{aiAnswer || 'AI 正在准备回答...'}
|
||||
</Typography.Paragraph>
|
||||
</Modal>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user