refactor(components): 重构 playground组件

- 移除 Data、Scene 和 IodRelevant 组件中的 Drawer
- 优化 Data、Scene 和 IodRelevant 组件的结构
- 添加 Header 组件用于展示标题和关闭按钮
- 使用 Main 组件替代原来的 ShowCard 组件
- 调整样式和布局,提高组件的可复用性和可维护性
This commit is contained in:
zhaoweijie
2025-08-23 17:03:14 +08:00
parent 6fb71b01f0
commit e640b1254d
17 changed files with 664 additions and 410 deletions

View File

@@ -1,5 +1,3 @@
import { Card, Col, Row } from "antd"
import { useMessageOption } from "@/hooks/useMessageOption.tsx"
import { useMutation, useQueryClient } from "@tanstack/react-query"
import { qaPrompt } from "@/libs/playground.tsx"
@@ -23,36 +21,19 @@ export const PlaygroundEmpty = () => {
}
return (
<div className="w-full pb-4 pt-[20%]">
{/* 标题区域 */}
{/*<div className="mb-4">*/}
{/* <h2*/}
{/* className="text-xl font-bold text-gray-800"*/}
{/* style={{ lineHeight: "0" }}>*/}
{/* 数联网科创智能体*/}
{/* </h2>*/}
{/* <p className="text-sm text-gray-500">您好!请问有什么可以帮您?</p>*/}
{/*</div>*/}
{/* 卡片网格布局 */}
<Row gutter={[16, 16]} className="w-full">
{qaPrompt.map((item, index) => (
<Col key={index} xs={24} sm={12} md={8}>
<Card
hoverable
style={{ backgroundColor: "#f3f4f6" }}
className="border border-gray-200 rounded-lg shadow-sm hover:shadow-md transition-shadow duration-200 cursor-pointer"
onClick={() => handleQuestion(item.title)}>
<div className="flex items-center">
<div className="text-blue-500 mr-2 w-10">{item.icon}</div>
<div className="font-medium text-sm text-gray-800">
{item.title}
</div>
</div>
</Card>
</Col>
))}
</Row>
<div className="w-full pb-4 pt-[20%] grid grid-cols-3 gap-3">
{qaPrompt.map((item, index) => (
<div
className="p-6 bg-gradient-to-br from-blue-50/90 via-indigo-50/90 to-purple-50/90 backdrop-blur-xl border border-white/60 shadow-xl rounded-2xl cursor-pointer hover:shadow-blue-200/40 hover:from-blue-100/90 hover:to-indigo-100/90 transition-all duration-500 hover:-translate-y-1"
onClick={() => handleQuestion(item.title)}>
<div className="flex items-center">
<div className="text-blue-500 mr-2 w-10">{item.icon}</div>
<div className="font-medium text-sm text-gray-800">
{item.title}
</div>
</div>
</div>
))}
</div>
)
}

View File

@@ -249,13 +249,11 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
}, [iodSearch])
return (
<div className="flex w-full flex-col items-center p-2 px-5 pt-1 pb-4">
<div className="flex w-full flex-col items-center pt-1 px-5 pb-4">
<div className="relative z-10 flex w-full flex-col items-center justify-center gap-2 text-base">
<div className="relative flex w-full flex-row justify-center gap-2 lg:w-5/5">
<div
className={` bg-neutral-50 dark:bg-[#262626] relative w-full max-w-[65rem] p-1 backdrop-blur-lg duration-100 border border-gray-300 rounded-xl dark:border-gray-600
${temporaryChat ? "!bg-gray-200 dark:!bg-black " : ""}
`}>
className={`shadow-xl relative w-full max-w-[65rem] p-1 rounded-xl bg-gradient-to-br from-white/90 via-blue-50/90 to-cyan-50/90 backdrop-blur-lg border border-blue-100/70 cursor-pointer hover:shadow-blue-100/60 transition-all duration-500`}>
<div
className={`border-b border-gray-200 dark:border-gray-600 relative ${
form.values.image.length === 0 ? "hidden" : "block"
@@ -361,38 +359,23 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
/>
</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>*/}
<Dropdown
menu={{ items: iodSearchItems }}
placement="bottom"
trigger={["click"]}
arrow>
<Button
color="purple"
color="default"
variant="filled"
size="large"
className="w-full mt-4 hover:!bg-[#0057ff1a]"
style={{
style={iodSearch ? {
color: "#0057ff",
background: "#0057ff0f",
border: "1px solid #0066ff26"
}}>
} : {}}>
<PiNetwork className="h-5 w-5" />
{iodSearch ? "开" : ""}
{iodSearch ? "开" : ""}
</Button>
</Dropdown>
</div>

View File

@@ -1,25 +1,121 @@
import React from "react"
import React, { createContext, useContext, useState } from "react"
import { AnimatePresence, motion } from "framer-motion"
import { PlaygroundIodRelevant } from "@/components/Common/Playground/IodRelevant.tsx"
import { PlaygroundData } from "@/components/Common/Playground/Data.tsx"
import { PlaygroundScene } from "@/components/Common/Playground/Scene.tsx"
import { PlaygroundTeam } from "@/components/Common/Playground/Team.tsx"
import { Card } from "antd"
import { CloseOutlined } from "@ant-design/icons"
// 定义 Context 类型
interface IodPlaygroundContextType {
showPlayground: boolean
setShowPlayground: React.Dispatch<React.SetStateAction<boolean>>
detailHeader: React.ReactNode
setDetailHeader: React.Dispatch<React.SetStateAction<React.ReactNode>>
detailMain: React.ReactNode
setDetailMain: React.Dispatch<React.SetStateAction<React.ReactNode>>
}
// 创建 Context
const PlaygroundContext = createContext<IodPlaygroundContextType | undefined>(
undefined
)
// 创建自定义 hook 以便子组件使用
export const useIodPlaygroundContext = () => {
const context = useContext(PlaygroundContext)
if (context === undefined) {
throw new Error(
"usePlaygroundContext must be used within a PlaygroundProvider"
)
}
return context
}
export const PlaygroundIod = () => {
const [showPlayground, setShowPlayground] = useState<boolean>(true)
const [detailHeader, setDetailHeader] = useState(<></>)
const [detailMain, setDetailMain] = useState(<></>)
return (
<div
className="w-[36%] 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 />
<PlaygroundContext.Provider
value={{
showPlayground,
setShowPlayground,
detailMain,
setDetailMain,
detailHeader,
setDetailHeader
}}>
<div className="w-[36%] h-full pt-16 pr-5 pb-0">
<PlaygroundContent />
</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>
</PlaygroundContext.Provider>
)
}
// 子组件使用修改card的默认样式
const classNames =
"h-full [&_.ant-card-body]:h-full [&_.ant-card-body]:!p-[20px] overflow-y-hidden !bg-[rgba(240,245,255,0.3)] backdrop-blur-sm border border-white/30 shadow-xl rounded-2xl"
// 将原来的返回内容移到这个组件中
const PlaygroundContent = () => {
const { showPlayground, detailMain, detailHeader, setShowPlayground } =
useIodPlaygroundContext()
return (
<AnimatePresence mode="popLayout">
{showPlayground ? (
<motion.div
key="playground"
initial={{ x: "100%" }}
animate={{ x: 0 }}
exit={{ x: "100%" }}
transition={{
duration: 0.6,
ease: "easeInOut"
}}
className="h-full grid grid-rows-12 gap-3">
<div className="w-full row-span-5">
<PlaygroundIodRelevant className={classNames.replace('!bg-[rgba(240,245,255,0.3)]', '')} />
</div>
<div className="w-full row-span-4 grid grid-cols-2 gap-3 custom-scrollbar">
<PlaygroundData className={classNames} />
<PlaygroundScene className={classNames} />
</div>
<div className="w-full row-span-3 pb-3">
<PlaygroundTeam className={classNames} />
</div>
</motion.div>
) : (
<motion.div
key="alternative"
initial={{ x: "100%" }}
animate={{ x: 0 }}
exit={{ x: "100%" }}
transition={{
duration: 0.6,
ease: "easeInOut"
}}
className="h-full pb-5">
<Card className="h-full shadow-xl shadow-gray-500/20 [&_.ant-card-body]:h-full">
<div className="flex flex-col h-full">
<div className="pb-6 flex items-center justify-between">
<div>{detailHeader}</div>
<CloseOutlined
size={30}
className="hover:text-red-500 cursor-pointer transition-colors duration-200 text-xl"
onClick={() => setShowPlayground(true)}
/>
</div>
{detailMain}
</div>
</Card>
</motion.div>
)}
</AnimatePresence>
)
}