feat(iod): 重构数联网搜索功能

- 新增数联网设置页面
- 优化数联网搜索结果展示
- 添加数据集、科创场景和科技企业等不同类型的搜索结果
- 重构搜索结果卡片组件,支持加载状态和不同展示模式
- 更新数联网搜索相关的国际化文案
This commit is contained in:
zhaoweijie
2025-08-22 17:15:19 +08:00
parent efbf2a3eff
commit 17020e8755
33 changed files with 1321 additions and 773 deletions

View File

@@ -6,7 +6,7 @@ import { PlaygroundForm } from "./PlaygroundForm"
import { PlaygroundChat } from "./PlaygroundChat"
import { useMessageOption } from "@/hooks/useMessageOption"
import { webUIResumeLastChat } from "@/services/app"
import { PlaygroundData } from '@/components/Common/Playground/Data.tsx'
import { PlaygroundData } from "@/components/Common/Playground/Data.tsx"
import { PlaygroundScene } from "@/components/Common/Playground/Scene.tsx"
import {
@@ -23,7 +23,6 @@ import { PlaygroundTeam } from "@/components/Common/Playground/Team.tsx"
import { PlaygroundHistory } from "@/components/Common/Playground/History.tsx"
import { PlaygroundIodRelevant } from "@/components/Common/Playground/IodRelevant.tsx"
export const Playground = () => {
const drop = React.useRef<HTMLDivElement>(null)
const [dropedFile, setDropedFile] = React.useState<File | undefined>()
@@ -146,13 +145,14 @@ export const Playground = () => {
dropState === "dragging" ? "bg-gray-100 dark:bg-gray-800" : ""
} bg-white dark:bg-[#171717]`}>
<PlaygroundHistory />
<div className="relative h-full flex-1 prose-lg flex flex-col items-center [&>*]:max-w-[848px] pt-[60px]">
<div className="h-full flex-1 overflow-x-hidden prose-lg flex flex-col items-center [&>*]:max-w-[848px] pt-[60px]">
<div
ref={containerRef}
className="custom-scrollbar flex h-auto w-full flex-col items-center overflow-x-hidden overflow-y-auto px-5">
className="custom-scrollbar flex h-auto w-full flex-col items-center px-5">
<PlaygroundChat />
</div>
<div className="relative bottom-0 w-full">
<div
className={`${messages.length ? "absolute" : "relative"} bottom-0 w-full`}>
{!isAtBottom && (
<div className="absolute bottom-36 z-20 left-0 right-0 flex justify-center">
<button
@@ -166,22 +166,20 @@ export const Playground = () => {
</div>
</div>
{/*auto_530px_165px*/}
{messages.length && (
<div
className="w-4/12 h-full grid grid-rows-10 gap-3 pt-16 pr-5 pb-0"
style={{ paddingTop: "4rem" }}>
<div className="w-full row-span-4">
<PlaygroundIodRelevant />
</div>
<div className="w-full row-span-4 grid grid-cols-2 gap-3 custom-scrollbar">
<PlaygroundData />
<PlaygroundScene />
</div>
<div className="w-full row-span-2 pb-3">
<PlaygroundTeam />
</div>
<div
className="w-4/12 h-full grid grid-rows-12 gap-3 pt-16 pr-5 pb-0"
style={{ paddingTop: "4rem" }}>
<div className="w-full row-span-5">
<PlaygroundIodRelevant />
</div>
)}
<div className="w-full row-span-4 grid grid-cols-2 gap-3 custom-scrollbar">
<PlaygroundData />
<PlaygroundScene />
</div>
<div className="w-full row-span-3 pb-3">
<PlaygroundTeam />
</div>
</div>
</div>
)
}

View File

@@ -52,7 +52,7 @@ export const PlaygroundChat = () => {
/>
))}
</div>
{/*<div className="w-full pb-[0px]"></div>*/}
{messages.length !== 0 && <div className="w-full pb-[157px]"></div>}
<MessageSourcePopup
open={isSourceOpen}
setOpen={setIsSourceOpen}

View File

@@ -19,11 +19,11 @@ export const PlaygroundEmpty = () => {
})
function handleQuestion(message: string) {
void sendMessage({ message, image: "" })
void sendMessage({ message, image: "", isRegenerate: true })
}
return (
<div className="w-full p-4">
<div className="w-full pb-4 pt-[20%]">
{/* 标题区域 */}
<div className="mb-4">
<h2

View File

@@ -4,8 +4,7 @@ import React from "react"
import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize"
import { toBase64 } from "~/libs/to-base64"
import { useMessageOption } from "~/hooks/useMessageOption"
import { Checkbox, Dropdown, Switch, Tooltip } from "antd"
import { Image } from "antd"
import { Checkbox, Dropdown, Image, Switch, Tooltip } from "antd"
import { useWebUI } from "~/store/webui"
import { defaultEmbeddingModelForRag } from "~/services/ollama"
import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react"
@@ -234,8 +233,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
/>
</div>
<div>
<div
className={`flex bg-transparent `}>
<div className={`flex bg-transparent `}>
<form
onSubmit={form.onSubmit(async (value) => {
stopListening()
@@ -304,33 +302,38 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
<div className="flex">
{!selectedKnowledge && (
<div>
<Tooltip title={t("tooltip.searchInternet")}>
<div className="inline-flex items-center gap-2">
<PiGlobe
className={`h-5 w-5 dark:text-gray-300 `}
/>
<Switch
value={webSearch}
onChange={(e) => setWebSearch(e)}
checkedChildren={t("form.webSearch.on")}
unCheckedChildren={t("form.webSearch.off")}
/>
</div>
</Tooltip>
<Tooltip title={t("tooltip.searchIod")} className="ml-3">
<div className="inline-flex items-center gap-2">
<PiNetwork
className={`h-5 w-5 dark:text-gray-300 `}
/>
<Switch
value={iodSearch}
onChange={(e) => setIodSearch(e)}
checkedChildren={t("form.webSearch.on")}
unCheckedChildren={t("form.webSearch.off")}
/>
</div>
</Tooltip>
</div>
{/* 展示隐藏深度搜索*/}
<Tooltip
title={t("tooltip.searchInternet")}
className="hidden">
<div className="inline-flex items-center gap-2">
<PiGlobe
className={`h-5 w-5 dark:text-gray-300 `}
/>
<Switch
value={webSearch}
onChange={(e) => setWebSearch(e)}
checkedChildren={t("form.webSearch.on")}
unCheckedChildren={t("form.webSearch.off")}
/>
</div>
</Tooltip>
<Tooltip
title={t("tooltip.searchIod")}
className="ml-3">
<div className="inline-flex items-center gap-2">
<PiNetwork
className={`h-5 w-5 dark:text-gray-300 `}
/>
<Switch
value={iodSearch}
onChange={(e) => setIodSearch(e)}
checkedChildren={t("form.webSearch.on")}
unCheckedChildren={t("form.webSearch.off")}
/>
</div>
</Tooltip>
</div>
)}
</div>
<div className="flex !justify-end gap-3">
@@ -357,7 +360,10 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
if (isListening) {
stopSpeechRecognition()
} else {
console.log("开始语音识别,语言:", speechToTextLanguage);
console.log(
"开始语音识别,语言:",
speechToTextLanguage
)
resetTranscript()
startListening({
continuous: true,