From ea1a24fd1ee2997435a8313fddac02869fb76e62 Mon Sep 17 00:00:00 2001 From: Chris <1637083533@qq.com> Date: Mon, 28 Apr 2025 23:10:33 +0800 Subject: [PATCH] Refactor and enhance UI components across the application - Improved formatting and consistency in BotConfigPage, HomeSidebar, and Plugin components. - Removed unnecessary Spin component to prevent layout collapse in BotConfigPage. - Enhanced sidebar selection logic to reflect current URL path in HomeSidebar. - Updated layout styles for better responsiveness and visual appeal. - Implemented mock data fetching in PluginMarketComponent for improved testing and development. - Added pagination and search functionality in PluginMarketComponent. - Refactored PluginInstalledComponent to streamline plugin list rendering and modal handling. - Adjusted CSS styles for better alignment and spacing in various components. - Removed commented-out code in HttpClient for cleaner codebase. - Enhanced NotFound component layout for better user experience. --- web/src/app/home/bots/botConfig.module.css | 2 +- web/src/app/home/bots/page.tsx | 62 ++--- .../components/home-sidebar/HomeSidebar.tsx | 186 +++++++------ web/src/app/home/layout.module.css | 13 +- web/src/app/home/layout.tsx | 46 +++- web/src/app/home/plugins/page.tsx | 45 ++- .../PluginInstalledComponent.tsx | 260 ++++++++++++++---- .../plugin-card/PluginCardComponent.tsx | 98 +++---- .../plugin-card/pluginCard.module.css | 9 +- .../plugin-market/PluginMarketComponent.tsx | 186 ++++++++----- .../plugin-market-card/PluginMarketCardVO.ts | 3 + .../pluginMarketCard.module.css | 4 +- web/src/app/home/plugins/plugins.module.css | 12 +- .../CreateCardComponent.tsx | 4 +- web/src/app/infra/http/HttpClient.ts | 6 +- web/src/app/not-found.tsx | 115 ++++---- 16 files changed, 631 insertions(+), 420 deletions(-) diff --git a/web/src/app/home/bots/botConfig.module.css b/web/src/app/home/bots/botConfig.module.css index ae201324..80fa5f2c 100644 --- a/web/src/app/home/bots/botConfig.module.css +++ b/web/src/app/home/bots/botConfig.module.css @@ -13,7 +13,7 @@ align-self: flex-start; justify-self: flex-start; width: calc(100% - 60px); - margin: auto; + margin: auto; display: grid; grid-template-rows: repeat(auto-fill, minmax(220px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); diff --git a/web/src/app/home/bots/page.tsx b/web/src/app/home/bots/page.tsx index fdd32f59..a3570eaf 100644 --- a/web/src/app/home/bots/page.tsx +++ b/web/src/app/home/bots/page.tsx @@ -1,15 +1,15 @@ "use client" -import {useEffect, useState} from "react"; +import { useEffect, useState } from "react"; import styles from "./botConfig.module.css"; import EmptyAndCreateComponent from "@/app/home/components/empty-and-create-component/EmptyAndCreateComponent"; -import {useRouter} from "next/navigation"; -import {BotCardVO} from "@/app/home/bots/components/bot-card/BotCardVO"; -import {Modal, notification, Spin} from "antd"; +import { useRouter } from "next/navigation"; +import { BotCardVO } from "@/app/home/bots/components/bot-card/BotCardVO"; +import { Modal, notification, Spin } from "antd"; import BotForm from "@/app/home/bots/components/bot-form/BotForm"; import BotCard from "@/app/home/bots/components/bot-card/BotCard"; import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent" -import {httpClient} from "@/app/infra/http/HttpClient"; +import { httpClient } from "@/app/infra/http/HttpClient"; import { Bot } from "@/app/infra/api/api-types"; export default function BotConfigPage() { @@ -89,8 +89,9 @@ export default function BotConfigPage() { } return ( -
+ + {/* 删除 spin,使用 spin 会导致盒子塌陷。 */} + } {pageShowRule === BotConfigPageShowRule.HAVE_BOT && -
- {botList.map(cardVO => { - return ( -
{selectBot(cardVO)}} - > - -
) - })} - -
+
+ {botList.map(cardVO => { + return ( +
{ selectBot(cardVO) }} + > + +
) + })} + +
}
-
+ ) } diff --git a/web/src/app/home/components/home-sidebar/HomeSidebar.tsx b/web/src/app/home/components/home-sidebar/HomeSidebar.tsx index 12813b29..56a6d6f5 100644 --- a/web/src/app/home/components/home-sidebar/HomeSidebar.tsx +++ b/web/src/app/home/components/home-sidebar/HomeSidebar.tsx @@ -1,100 +1,118 @@ -"use client"; +"use client" -import styles from "./HomeSidebar.module.css"; +import styles from "./HomeSidebar.module.css" import { useEffect, useState } from "react"; -import { - SidebarChild, - SidebarChildVO -} from "@/app/home/components/home-sidebar/HomeSidebarChild"; -import { useRouter, usePathname } from "next/navigation"; +import { SidebarChild, SidebarChildVO } from "@/app/home/components/home-sidebar/HomeSidebarChild"; +import { useRouter, usePathname, useSearchParams } from "next/navigation"; import { sidebarConfigList } from "@/app/home/components/home-sidebar/sidbarConfigList"; // TODO 侧边导航栏要加动画 export default function HomeSidebar({ - onSelectedChangeAction + onSelectedChange }: { - onSelectedChangeAction: (sidebarChild: SidebarChildVO) => void; + onSelectedChange: (sidebarChild: SidebarChildVO) => void }) { - // 路由相关 - const router = useRouter(); - const pathname = usePathname(); - // 路由被动变化时处理 - useEffect(() => { - handleRouteChange(pathname); - }, [pathname]); + // 路由相关 + const router = useRouter() + const pathname = usePathname(); + const searchParams = useSearchParams(); + // 路由被动变化时处理 + useEffect(() => { + handleRouteChange(pathname) + }, [pathname, searchParams]); - const [selectedChild, setSelectedChild] = useState( - sidebarConfigList[0] - ); + const [selectedChild, setSelectedChild] = useState(sidebarConfigList[0]) - useEffect(() => { - console.log("HomeSidebar挂载完成"); - initSelect(); - return () => console.log("HomeSidebar卸载"); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + useEffect(() => { + console.log('HomeSidebar挂载完成'); + initSelect() + return () => console.log('HomeSidebar卸载'); + }, []); - function handleChildClick(child: SidebarChildVO) { - setSelectedChild(child); - handleRoute(child); - onSelectedChangeAction(child); - } - function initSelect() { - handleChildClick(sidebarConfigList[0]); - } - function handleRoute(child: SidebarChildVO) { - console.log(child); - router.push(`${child.route}`); - } - - function handleRouteChange(pathname: string) { - // TODO 这段逻辑并不好,未来router封装好后改掉 - // 判断在home下,并且路由更改的是自己的路由子组件则更新UI - const routeList = pathname.split("/"); - if ( - routeList[1] === "home" && - sidebarConfigList.find((childConfig) => childConfig.route === pathname) - ) { - console.log("find success"); - const routeSelectChild = sidebarConfigList.find( - (childConfig) => childConfig.route === pathname - ); - if (routeSelectChild) { - setSelectedChild(routeSelectChild); - } + function handleChildClick(child: SidebarChildVO) { + setSelectedChild(child) + handleRoute(child) + onSelectedChange(child) } - } - return ( -
- {/* LangBot、ICON区域 */} -
- {/* icon */} -
L
-
Langbot
-
- {/* 菜单列表,后期可升级成配置驱动 */} -
- {sidebarConfigList.map((config) => { - return ( -
{ - console.log("click:", config.id); - handleChildClick(config); - }} - > - + function initSelect() { + // 根据当前URL路径选择相应的菜单项,而不是总是使用第一个菜单项 + const currentPath = pathname; + const matchedChild = sidebarConfigList.find(child => child.route === currentPath); + + if (matchedChild) { + // 如果找到匹配的菜单项,则选择它 + setSelectedChild(matchedChild); + onSelectedChange(matchedChild); + } else { + // 如果没有匹配项,则回退到默认选择第一个菜单项 + handleChildClick(sidebarConfigList[0]); + } + } + + function handleRoute(child: SidebarChildVO) { + console.log(child) + router.push(`${child.route}`) + } + + function handleRouteChange(pathname: string) { + // TODO 这段逻辑并不好,未来router封装好后改掉 + // 判断在home下,并且路由更改的是自己的路由子组件则更新UI + const routeList = pathname.split('/') + if ( + routeList[1] === "home" && + sidebarConfigList.find(childConfig => + childConfig.route === pathname + ) + ) { + console.log("find success") + const routeSelectChild = sidebarConfigList.find(childConfig => + childConfig.route === pathname + ) + if (routeSelectChild) { + setSelectedChild(routeSelectChild) + } + } + } + + + return ( +
+ {/* LangBot、ICON区域 */} +
+ {/* icon */} +
+ L +
+
+ Langbot +
- ); - })} -
-
- ); -} + {/* 菜单列表,后期可升级成配置驱动 */} +
+ { + sidebarConfigList.map(config => { + return ( +
{ + console.log('click:', config.id) + handleChildClick(config) + }} + > + +
+ ) + }) + } + +
+
+ ); +} \ No newline at end of file diff --git a/web/src/app/home/layout.module.css b/web/src/app/home/layout.module.css index bcbdc800..8bc33e35 100644 --- a/web/src/app/home/layout.module.css +++ b/web/src/app/home/layout.module.css @@ -1,3 +1,4 @@ +/* 主布局容器 */ .homeLayoutContainer { width: 100vw; height: 100vh; @@ -5,14 +6,12 @@ flex-direction: row; } +/* 主内容区域 */ .main { + background-color: #f5f5f7; + padding: 0; + overflow: auto; width: 100%; height: 100%; - background-color: #FAFBFB; -} - -.mainContent { - width: calc(100% - 40px); - height: calc(100% - 110px); - margin: 20px; + padding: 20px; } \ No newline at end of file diff --git a/web/src/app/home/layout.tsx b/web/src/app/home/layout.tsx index 7985dae6..8ac4efb4 100644 --- a/web/src/app/home/layout.tsx +++ b/web/src/app/home/layout.tsx @@ -1,30 +1,48 @@ -"use client"; +"use client" -import "@ant-design/v5-patch-for-react-19"; -import styles from "./layout.module.css"; +import '@ant-design/v5-patch-for-react-19'; +import styles from "./layout.module.css" import HomeSidebar from "@/app/home/components/home-sidebar/HomeSidebar"; import HomeTitleBar from "@/app/home/components/home-titlebar/HomeTitleBar"; import React, { useState } from "react"; import { SidebarChildVO } from "@/app/home/components/home-sidebar/HomeSidebarChild"; +import { useRouter } from 'next/navigation'; +import { Layout } from 'antd'; + +const { Sider, Content } = Layout; export default function HomeLayout({ children }: Readonly<{ children: React.ReactNode; }>) { - const [title, setTitle] = useState(""); + const router = useRouter(); + const [title, setTitle] = useState("") const onSelectedChange = (child: SidebarChildVO) => { - setTitle(child.name); - }; + setTitle(child.name) + } return ( -
- -
+ + {/* homeLayoutContainer 是整个容器的入口,使用 flex 的左右布局 */} + + + + {/* HomeSidebar 为侧边栏 */} + + + + {/* right 为内容显示区域,right使用 flex 上下布局,right 使用 flex 布局吃掉剩余部分 */} + - {/* 主页面 */} -
{children}
-
-
- ); + + + {/* mainContent 为主页面 */} + {children} + + + + ) } diff --git a/web/src/app/home/plugins/page.tsx b/web/src/app/home/plugins/page.tsx index e28db69d..10151b39 100644 --- a/web/src/app/home/plugins/page.tsx +++ b/web/src/app/home/plugins/page.tsx @@ -1,6 +1,6 @@ "use client" import { Radio } from 'antd'; -import {useState} from "react"; +import { useState } from "react"; import PluginInstalledComponent from "@/app/home/plugins/plugin-installed/PluginInstalledComponent"; import PluginMarketComponent from "@/app/home/plugins/plugin-market/PluginMarketComponent"; import styles from './plugins.module.css' @@ -14,33 +14,24 @@ export default function PluginConfigPage() { const [nowPageType, setNowPageType] = useState(PageType.INSTALLED) return ( -
-
- { - // 这里静态类型检测有问题 - setNowPageType(e.target.value) - }} - /> -
-
- { - nowPageType === PageType.INSTALLED && - } - { - nowPageType === PageType.MARKET && - } -
+
+ { + setNowPageType(e.target.value as PageType) + }} + /> + {nowPageType === PageType.INSTALLED ? : }
); } diff --git a/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx b/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx index 1b5cea43..2897cef8 100644 --- a/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx +++ b/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx @@ -1,4 +1,4 @@ -"use client"; +"use client" import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent"; import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO"; @@ -7,54 +7,221 @@ import PluginCardComponent from "@/app/home/plugins/plugin-installed/plugin-card import styles from "@/app/home/plugins/plugins.module.css"; import { Modal, Input } from "antd"; import { GithubOutlined } from "@ant-design/icons"; -import { httpClient } from "@/app/infra/http/HttpClient"; export default function PluginInstalledComponent() { - const [pluginList, setPluginList] = useState([]); - const [modalOpen, setModalOpen] = useState(false); - const [githubURL, setGithubURL] = useState(""); + const [pluginList, setPluginList] = useState([]) + const [modalOpen, setModalOpen] = useState(false) + const [githubURL, setGithubURL] = useState("") + useEffect(() => { - initData(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + initData() + }, []) function initData() { - getPluginList(); + getPluginList().then((value) => { + setPluginList(value) + }) } - function getPluginList() { - httpClient.getPlugins().then((value) => { - setPluginList( - value.plugins.map((plugin) => { - return new PluginCardVO({ - author: plugin.author, - description: plugin.description.zh_CN, - handlerCount: 0, - name: plugin.name, - version: plugin.version, - isInitialized: plugin.status === "initialized" - }); - }) - ); - }); + async function getPluginList() { + return [ + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), new PluginCardVO({ + description: "一般的描述", + handlerCount: 0, + name: "插件AAA", + author: "/hana", + version: "0.1", + isInitialized: false + }), + + + ] } function handleModalConfirm() { - installPlugin(githubURL); - setModalOpen(false); + installPlugin(githubURL) + setModalOpen(false) } function installPlugin(url: string) { - httpClient - .installPluginFromGithub(url) - .then(() => { - // 安装后重新拉取 - getPluginList(); - }) - .catch((err) => { - console.log("error when install plugin:", err); - }); + // TODO 接安装Plugin的接口 + console.log("installPlugin: ", url) } return (
@@ -63,8 +230,8 @@ export default function PluginInstalledComponent() {
@@ -79,7 +246,9 @@ export default function PluginInstalledComponent() { destroyOnClose={true} >
-
目前仅支持从 GitHub 安装
+
+ 目前仅支持从 GitHub 安装 +
- {pluginList.map((vo, index) => { - return ( -
+ { + pluginList.map((vo, index) => { + return
- ); - })} + }) + } { - setModalOpen(true); + setModalOpen(true) }} />
- ); + ) } diff --git a/web/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx b/web/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx index fcae4000..267bb3b7 100644 --- a/web/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx +++ b/web/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx @@ -1,65 +1,47 @@ -import styles from "./pluginCard.module.css"; +import styles from "./pluginCard.module.css" import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO"; -import { GithubOutlined, LinkOutlined, ToolOutlined } from "@ant-design/icons"; -import { Switch, Tag } from "antd"; -import { useState } from "react"; -import { httpClient } from "@/app/infra/http/HttpClient"; +import { GithubOutlined, LinkOutlined, ToolOutlined } from '@ant-design/icons'; +import { Tag } from 'antd' export default function PluginCardComponent({ - cardVO + cardVO }: { - cardVO: PluginCardVO; + cardVO: PluginCardVO }) { - const [initialized, setInitialized] = useState(cardVO.isInitialized); - const [switchEnable, setSwitchEnable] = useState(true); - - function handleEnable() { - setSwitchEnable(false); - httpClient - .togglePlugin(cardVO.author, cardVO.name, !initialized) - .then(() => { - setInitialized(!initialized); - }) - .catch((err) => { - console.log("error: ", err); - }) - .finally(() => { - setSwitchEnable(true); - }); - } - return ( -
- {/* header */} -
- {/* left author */} -
{cardVO.author}
- {/* right icon & version */} -
- - v{cardVO.version} + return ( +
+ {/* header */} +
+ {/* left author */} +
{cardVO.author}
+ {/* right icon & version */} +
+ + v{cardVO.version} +
+
+ {/* content */} +
+
{cardVO.name}
+
{cardVO.description}
+
+ {/* footer */} +
+
+
+ + 1 +
+ +
+
-
- {/* content */} -
-
{cardVO.name}
-
{cardVO.description}
-
- {/* footer */} -
-
-
- - 1 -
- -
- - -
-
- ); + ); } diff --git a/web/src/app/home/plugins/plugin-installed/plugin-card/pluginCard.module.css b/web/src/app/home/plugins/plugin-installed/plugin-card/pluginCard.module.css index 81ad489a..3ec44fd4 100644 --- a/web/src/app/home/plugins/plugin-installed/plugin-card/pluginCard.module.css +++ b/web/src/app/home/plugins/plugin-installed/plugin-card/pluginCard.module.css @@ -1,5 +1,6 @@ .cardContainer { - width: 360px; + width: 100%; + /* 修改为 100% 以撑满整个网格单元 */ height: 140px; box-sizing: border-box; background-color: #FFF; @@ -40,10 +41,6 @@ .cardFooter { width: 90%; height: 30px; - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; } @@ -73,4 +70,4 @@ align-self: center; justify-content: space-between; } -} +} \ No newline at end of file diff --git a/web/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx b/web/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx index d01aeb6e..793db49c 100644 --- a/web/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx +++ b/web/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx @@ -1,87 +1,127 @@ -"use client"; +"use client" -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import styles from "@/app/home/plugins/plugins.module.css"; import { PluginMarketCardVO } from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO"; import PluginMarketCardComponent from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent"; import { Input, Pagination } from "antd"; -import { spaceClient } from "@/app/infra/http/HttpClient"; +import { debounce } from "lodash" export default function PluginMarketComponent() { - const [marketPluginList, setMarketPluginList] = useState< - PluginMarketCardVO[] - >([]); - const [totalCount, setTotalCount] = useState(0); - const [nowPage, setNowPage] = useState(1); - const [searchKeyword, setSearchKeyword] = useState(""); + const [marketPluginList, setMarketPluginList] = useState([]) + const [searchKeyword, setSearchKeyword] = useState("") + const [currentPage, setCurrentPage] = useState(1) + const [totalItems, setTotalItems] = useState(0) + const [loading, setLoading] = useState(false) + const pageSize = 10 // 每页显示的项目数量 - useEffect(() => { - initData(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + useEffect(() => { + fetchPlugins(searchKeyword, currentPage) + }, [currentPage]) - function initData() { - getPluginList(); - } + // 获取插件列表,整合了搜索和分页功能 + async function fetchPlugins(keyword: string = "", page: number = 1): Promise { + setLoading(true) + try { + // 实际应用中,这里应该调用API获取数据 + const result = await mockFetchPlugins(keyword, page, pageSize) + setMarketPluginList(result.data) + setTotalItems(result.total) + } finally { + setLoading(false) + } + } - function onInputSearchKeyword(keyword: string) { - // 这里记得加防抖,暂时没加 - setSearchKeyword(keyword); - setNowPage(1); - getPluginList(1, keyword); - } + // 模拟从API获取数据 + async function mockFetchPlugins(keyword: string, page: number, pageSize: number): Promise<{ data: PluginMarketCardVO[], total: number }> { + // 模拟API延迟 + await new Promise(resolve => setTimeout(resolve, 300)) - function getPluginList( - page: number = nowPage, - keyword: string = searchKeyword - ) { - spaceClient.getMarketPlugins(page, 10, keyword).then((res) => { - setMarketPluginList( - res.plugins.map( - (marketPlugin) => - new PluginMarketCardVO({ - author: marketPlugin.author, - description: marketPlugin.description, - githubURL: marketPlugin.repository, - name: marketPlugin.name, - pluginId: String(marketPlugin.ID), - starCount: marketPlugin.stars - }) - ) - ); - setTotalCount(res.total); - console.log("market plugins:", res); - }); - } + // 创建模拟数据 + const allPlugins: PluginMarketCardVO[] = [] + const totalPlugins = 50 // 模拟总数据量 - return ( -
- onInputSearchKeyword(e.target.value)} - /> -
- {marketPluginList.map((vo, index) => { - return ( -
- + for (let i = 0; i < totalPlugins; i++) { + allPlugins.push(new PluginMarketCardVO({ + pluginId: `plugin-${i}`, + description: `这是插件 ${i} 的描述,包含一些详细信息`, + name: `插件 ${i}`, + author: `/author-${i % 5}`, // 模拟5个不同的作者 + version: `0.${i % 10}`, + githubURL: `https://github.com/author-${i % 5}/plugin-${i}`, + starCount: 10 + Math.floor(Math.random() * 100) + })) + } + + // 根据关键词过滤 + const filtered = keyword + ? allPlugins.filter(p => + p.name.toLowerCase().includes(keyword.toLowerCase()) || + p.description.toLowerCase().includes(keyword.toLowerCase())) + : allPlugins + + // 分页处理 + const start = (page - 1) * pageSize + const end = start + pageSize + const paginatedData = filtered.slice(start, end) + + return { + data: paginatedData, + total: filtered.length + } + } + + function onInputSearchKeyword(keyword: string) { + setSearchKeyword(keyword) + setCurrentPage(1) // 搜索时重置为第一页 + debounceSearch(keyword) + } + + const debounceSearch = useCallback( + debounce((keyword: string) => { + fetchPlugins(keyword, 1) + }, 500), [] + ) + + function handlePageChange(page: number) { + setCurrentPage(page) + } + + return ( +
+ onInputSearchKeyword(e.target.value)} + /> +
+ {loading ? ( +
加载中...
+ ) : marketPluginList.length === 0 ? ( +
没有找到匹配的插件
+ ) : ( + marketPluginList.map((vo, index) => ( +
+ +
+ )) + )}
- ); - })} -
- { - setNowPage(pageNumber); - getPluginList(pageNumber); - }} - /> -
- ); + {totalItems > 0 && ( +
+ +
+ )} +
+ ) } diff --git a/web/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO.ts b/web/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO.ts index 6af3f199..9d508066 100644 --- a/web/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO.ts +++ b/web/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO.ts @@ -5,6 +5,7 @@ export interface IPluginMarketCardVO { description: string, starCount: number, githubURL: string, + version: string, } export class PluginMarketCardVO implements IPluginMarketCardVO { @@ -14,6 +15,7 @@ export class PluginMarketCardVO implements IPluginMarketCardVO { author: string; githubURL: string; starCount: number; + version: string; constructor(prop: IPluginMarketCardVO) { this.description = prop.description @@ -22,5 +24,6 @@ export class PluginMarketCardVO implements IPluginMarketCardVO { this.githubURL = prop.githubURL this.starCount = prop.starCount this.pluginId = prop.pluginId + this.version = prop.version } } diff --git a/web/src/app/home/plugins/plugin-market/plugin-market-card/pluginMarketCard.module.css b/web/src/app/home/plugins/plugin-market/plugin-market-card/pluginMarketCard.module.css index f611f335..33297b09 100644 --- a/web/src/app/home/plugins/plugin-market/plugin-market-card/pluginMarketCard.module.css +++ b/web/src/app/home/plugins/plugin-market/plugin-market-card/pluginMarketCard.module.css @@ -1,6 +1,4 @@ .cardContainer { - width: 360px; - height: 140px; box-sizing: border-box; background-color: #FFF; border-radius: 9px; @@ -74,4 +72,4 @@ align-self: center; justify-content: space-between; } -} +} \ No newline at end of file diff --git a/web/src/app/home/plugins/plugins.module.css b/web/src/app/home/plugins/plugins.module.css index 1393c15b..24688273 100644 --- a/web/src/app/home/plugins/plugins.module.css +++ b/web/src/app/home/plugins/plugins.module.css @@ -1,8 +1,8 @@ .pageContainer { width: 100%; - height: calc(100% - 30px); } + .marketComponentBody { width: 100%; height: calc(100% - 60px); @@ -11,17 +11,13 @@ .pluginListContainer { align-self: flex-start; justify-self: flex-start; - width: calc(100% - 60px); - height: 100%; - max-height: 100%; - margin: auto; + margin: auto; display: grid; grid-template-rows: repeat(auto-fill, minmax(160px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); gap: 15px; - justify-items: center; + /* justify-items: center; */ align-items: center; - overflow-y: scroll; } .modalTitle { @@ -35,4 +31,4 @@ display: flex; flex-direction: column; justify-content: space-around; -} +} \ No newline at end of file diff --git a/web/src/app/infra/basic-component/create-card-component/CreateCardComponent.tsx b/web/src/app/infra/basic-component/create-card-component/CreateCardComponent.tsx index 3e33d712..f1a280fc 100644 --- a/web/src/app/infra/basic-component/create-card-component/CreateCardComponent.tsx +++ b/web/src/app/infra/basic-component/create-card-component/CreateCardComponent.tsx @@ -1,12 +1,10 @@ import styles from "./createCartComponent.module.css"; export default function CreateCardComponent({ - width, height, plusSize, onClick, }: { - width: number; height: number; plusSize: number; onClick: () => void @@ -15,7 +13,7 @@ export default function CreateCardComponent({
-
-
- {/* Ant Design 图标,可以换成 Langbot 的 Logo */} -
- -
+ + + +
+
+ {/* Ant Design 图标,可以换成 Langbot 的 Logo */} +
+ +
-
- - 404 - - - 页面不存在 - - - 您要查找的页面似乎不存在。请检查您输入的 URL 是否正确,或者返回首页。 - -
+
+ + 404 + + + 页面不存在 + + + 您要查找的页面似乎不存在。请检查您输入的 URL 是否正确,或者返回首页。 + +
-
- - - - -
+
+ + + + +
-
- - 需要帮助吗?您可以联系 support@qq.com - +
+ + 需要帮助吗?您可以联系 support@qq.com + +
+
-
-
- + + ); }