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 1/8] 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 + +
+
-
-
- + + ); } From 4b5ac6ad03604f523f58ace02cf16d12fdc2f1be Mon Sep 17 00:00:00 2001 From: Chris <1637083533@qq.com> Date: Mon, 28 Apr 2025 23:14:35 +0800 Subject: [PATCH 2/8] http --- web/src/app/infra/http/HttpClient.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/app/infra/http/HttpClient.ts b/web/src/app/infra/http/HttpClient.ts index 192566a0..a8bb53cc 100644 --- a/web/src/app/infra/http/HttpClient.ts +++ b/web/src/app/infra/http/HttpClient.ts @@ -129,9 +129,9 @@ class HttpClient { const errMessage = data?.message || error.message; switch (status) { - // case 401: - // window.location.href = "/login"; - // break; + case 401: + window.location.href = "/login"; + break; case 403: console.error("Permission denied:", errMessage); break; From 9d724dbb8d28bd8eb29e203544ea3d79b28f5455 Mon Sep 17 00:00:00 2001 From: chris <1637083533@qq.com> Date: Tue, 29 Apr 2025 14:58:17 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BB=93=E5=BA=93?= =?UTF-8?q?=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/home-sidebar/HomeSidebar.tsx | 118 ++++++++-------- web/src/app/home/layout.tsx | 4 +- .../PluginInstalledComponent.tsx | 1 + .../plugin-card/PluginCardComponent.tsx | 22 ++- .../plugin-market/PluginMarketComponent.tsx | 126 ++++++++---------- 5 files changed, 131 insertions(+), 140 deletions(-) diff --git a/web/src/app/home/components/home-sidebar/HomeSidebar.tsx b/web/src/app/home/components/home-sidebar/HomeSidebar.tsx index 56a6d6f5..7a950e91 100644 --- a/web/src/app/home/components/home-sidebar/HomeSidebar.tsx +++ b/web/src/app/home/components/home-sidebar/HomeSidebar.tsx @@ -1,81 +1,71 @@ -"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, useSearchParams } from "next/navigation"; +import { + SidebarChild, + SidebarChildVO +} from "@/app/home/components/home-sidebar/HomeSidebarChild"; +import { useRouter, usePathname } from "next/navigation"; import { sidebarConfigList } from "@/app/home/components/home-sidebar/sidbarConfigList"; // TODO 侧边导航栏要加动画 export default function HomeSidebar({ - onSelectedChange + onSelectedChangeAction }: { - onSelectedChange: (sidebarChild: SidebarChildVO) => void + onSelectedChangeAction: (sidebarChild: SidebarChildVO) => void; }) { - // 路由相关 - const router = useRouter() - const pathname = usePathname(); - const searchParams = useSearchParams(); - // 路由被动变化时处理 - useEffect(() => { - handleRouteChange(pathname) - }, [pathname, searchParams]); + // 路由相关 + const router = useRouter(); + const pathname = usePathname(); + // 路由被动变化时处理 + useEffect(() => { + handleRouteChange(pathname); + }, [pathname]); - const [selectedChild, setSelectedChild] = useState(sidebarConfigList[0]) + const [selectedChild, setSelectedChild] = useState( + sidebarConfigList[0] + ); - useEffect(() => { - console.log('HomeSidebar挂载完成'); - initSelect() - return () => console.log('HomeSidebar卸载'); - }, []); + useEffect(() => { + console.log("HomeSidebar挂载完成"); + initSelect(); + return () => console.log("HomeSidebar卸载"); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + function handleChildClick(child: SidebarChildVO) { + setSelectedChild(child); + handleRoute(child); + onSelectedChangeAction(child); + } + function initSelect() { + handleChildClick(sidebarConfigList[0]); + } - function handleChildClick(child: SidebarChildVO) { - setSelectedChild(child) - handleRoute(child) - onSelectedChange(child) - } - - 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) - } - } + 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 ( diff --git a/web/src/app/home/layout.tsx b/web/src/app/home/layout.tsx index 8ac4efb4..c765aef0 100644 --- a/web/src/app/home/layout.tsx +++ b/web/src/app/home/layout.tsx @@ -18,7 +18,7 @@ export default function HomeLayout({ }>) { const router = useRouter(); const [title, setTitle] = useState("") - const onSelectedChange = (child: SidebarChildVO) => { + const onSelectedChangeAction = (child: SidebarChildVO) => { setTitle(child.name) } @@ -28,7 +28,7 @@ export default function HomeLayout({ {/* HomeSidebar 为侧边栏 */} diff --git a/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx b/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx index 2897cef8..be770733 100644 --- a/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx +++ b/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx @@ -16,6 +16,7 @@ export default function PluginInstalledComponent() { useEffect(() => { initData() + // eslint-disable-next-line react-hooks/exhaustive-deps }, []) function initData() { 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 267bb3b7..9efbbe9e 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,13 +1,33 @@ import styles from "./pluginCard.module.css" import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO"; import { GithubOutlined, LinkOutlined, ToolOutlined } from '@ant-design/icons'; -import { Tag } from 'antd' +import { Switch, Tag } from 'antd' +import { useState } from "react"; +import { httpClient } from "@/app/infra/http/HttpClient"; export default function PluginCardComponent({ cardVO }: { 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 */} diff --git a/web/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx b/web/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx index 793db49c..9e84c0ff 100644 --- a/web/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx +++ b/web/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx @@ -1,90 +1,70 @@ -"use client" +"use client"; -import { useCallback, useEffect, useState } from "react"; +import { 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 { debounce } from "lodash" +import { spaceClient } from "@/app/infra/http/HttpClient"; export default function PluginMarketComponent() { - 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 // 每页显示的项目数量 + const [marketPluginList, setMarketPluginList] = useState< + PluginMarketCardVO[] + >([]); + const [totalCount, setTotalCount] = useState(0); + const [nowPage, setNowPage] = useState(1); + const [searchKeyword, setSearchKeyword] = useState(""); + const [loading, setLoading] = useState(false); + const pageSize = 10; useEffect(() => { - fetchPlugins(searchKeyword, currentPage) - }, [currentPage]) + initData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - // 获取插件列表,整合了搜索和分页功能 - 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) - } - } - - // 模拟从API获取数据 - async function mockFetchPlugins(keyword: string, page: number, pageSize: number): Promise<{ data: PluginMarketCardVO[], total: number }> { - // 模拟API延迟 - await new Promise(resolve => setTimeout(resolve, 300)) - - // 创建模拟数据 - const allPlugins: PluginMarketCardVO[] = [] - const totalPlugins = 50 // 模拟总数据量 - - 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 initData() { + getPluginList(); } function onInputSearchKeyword(keyword: string) { - setSearchKeyword(keyword) - setCurrentPage(1) // 搜索时重置为第一页 - debounceSearch(keyword) + // 这里记得加防抖,暂时没加 + setSearchKeyword(keyword); + setNowPage(1); + getPluginList(1, keyword); } - const debounceSearch = useCallback( - debounce((keyword: string) => { - fetchPlugins(keyword, 1) - }, 500), [] - ) + function getPluginList( + page: number = nowPage, + keyword: string = searchKeyword + ) { + setLoading(true); + spaceClient.getMarketPlugins(page, pageSize, 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, + version: "version" in marketPlugin ? String(marketPlugin.version) : "1.0.0", // Default version if not provided + }) + ) + ); + setTotalCount(res.total); + setLoading(false); + console.log("market plugins:", res); + }).catch(error => { + console.error("获取插件列表失败:", error); + setLoading(false); + }); + } function handlePageChange(page: number) { - setCurrentPage(page) + setNowPage(page); + getPluginList(page); } return ( @@ -111,11 +91,11 @@ export default function PluginMarketComponent() { )) )}
- {totalItems > 0 && ( + {totalCount > 0 && (
Date: Tue, 29 Apr 2025 15:05:15 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BB=93=E5=BA=93?= =?UTF-8?q?=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/plugins/plugin-installed/PluginInstalledComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx b/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx index be770733..39c2be86 100644 --- a/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx +++ b/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx @@ -15,7 +15,7 @@ export default function PluginInstalledComponent() { useEffect(() => { - initData() + initData(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []) From 44b005ffdd53acd8c52dd8b0c63c9b07ab2649d9 Mon Sep 17 00:00:00 2001 From: chris <1637083533@qq.com> Date: Tue, 29 Apr 2025 15:32:06 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/app/home/bots/page.tsx | 280 +++++++++--------- .../PluginInstalledComponent.tsx | 264 +++-------------- web/src/app/infra/http/HttpClient.ts | 26 +- 3 files changed, 200 insertions(+), 370 deletions(-) diff --git a/web/src/app/home/bots/page.tsx b/web/src/app/home/bots/page.tsx index a3570eaf..c2760b01 100644 --- a/web/src/app/home/bots/page.tsx +++ b/web/src/app/home/bots/page.tsx @@ -1,4 +1,4 @@ -"use client" +"use client"; import { useEffect, useState } from "react"; import styles from "./botConfig.module.css"; @@ -8,155 +8,161 @@ 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 CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent"; import { httpClient } from "@/app/infra/http/HttpClient"; import { Bot } from "@/app/infra/api/api-types"; export default function BotConfigPage() { - const router = useRouter(); - const [pageShowRule, setPageShowRule] = useState(BotConfigPageShowRule.NO_BOT) - const [modalOpen, setModalOpen] = useState(false); - const [botList, setBotList] = useState([]) - const [isEditForm, setIsEditForm] = useState(false) - const [nowSelectedBotCard, setNowSelectedBotCard] = useState() - const [isLoading, setIsLoading] = useState(false) + const router = useRouter(); + const [pageShowRule, setPageShowRule] = useState( + BotConfigPageShowRule.NO_BOT + ); + const [modalOpen, setModalOpen] = useState(false); + const [botList, setBotList] = useState([]); + const [isEditForm, setIsEditForm] = useState(false); + const [nowSelectedBotCard, setNowSelectedBotCard] = useState(); + const [isLoading, setIsLoading] = useState(false); + useEffect(() => { + // TODO:补齐加载转圈逻辑 + setIsLoading(true); + checkHasLLM().then((hasLLM) => { + if (hasLLM) { + getBotList(); + } else { + setPageShowRule(BotConfigPageShowRule.NO_LLM); + setIsLoading(false); + } + }); + }, []); - useEffect(() => { - // TODO:补齐加载转圈逻辑 - setIsLoading(true) - checkHasLLM().then((hasLLM) => { - if (hasLLM) { - getBotList() - } else { - setPageShowRule(BotConfigPageShowRule.NO_LLM) - setIsLoading(false) - } - }) - }, []) + async function checkHasLLM(): Promise { + // NOT IMPL + return true; + } - async function checkHasLLM(): Promise { - // NOT IMPL - return true - } + function getBotList() { + httpClient + .getBots() + .then((resp) => { + const botList: BotCardVO[] = resp.bots.map((bot: Bot) => { + return new BotCardVO({ + adapter: bot.adapter, + description: bot.description, + id: bot.uuid || "", + name: bot.name, + updateTime: bot.updated_at || "", + pipelineName: bot.use_pipeline_name || "" + }); + }); + if (botList.length === 0) { + setPageShowRule(BotConfigPageShowRule.NO_BOT); + } else { + setPageShowRule(BotConfigPageShowRule.HAVE_BOT); + } + setBotList(botList); + }) + .catch((err) => { + console.error("get bot list error", err); + // TODO HACK: need refactor to hook mode Notification, but it's not working under render + notification.error({ + message: "获取机器人列表失败", + description: err.message, + placement: "bottomRight" + }); + }) + .finally(() => { + setIsLoading(false); + }); + } - function getBotList() { - httpClient.getBots().then((resp) => { - const botList: BotCardVO[] = resp.bots.map((bot: Bot) => { - return new BotCardVO({ - adapter: bot.adapter, - description: bot.description, - id: bot.uuid || "", - name: bot.name, - updateTime: bot.updated_at || "", - pipelineName: bot.use_pipeline_name || "", - }) - }) - if (botList.length === 0) { - setPageShowRule(BotConfigPageShowRule.NO_BOT) - } else { - setPageShowRule(BotConfigPageShowRule.HAVE_BOT) - } - setBotList(botList) - }).catch((err) => { - console.error("get bot list error", err) - // TODO HACK: need refactor to hook mode Notification, but it's not working under render - notification.error({ - message: "获取机器人列表失败", - description: err.message, - placement: "bottomRight", - }) - }).finally(() => { - setIsLoading(false) - }) - } + function handleCreateBotClick() { + setIsEditForm(false); + setNowSelectedCard(undefined); + setModalOpen(true); + } - function handleCreateBotClick() { - setIsEditForm(false) - setNowSelectedCard(undefined) - setModalOpen(true); - } + function setNowSelectedCard(cardVO: BotCardVO | undefined) { + setNowSelectedBotCard(cardVO); + } - function setNowSelectedCard(cardVO: BotCardVO | undefined) { - setNowSelectedBotCard(cardVO) - } + function selectBot(cardVO: BotCardVO) { + setIsEditForm(true); + setNowSelectedCard(cardVO); + console.log("set now vo", cardVO); + setModalOpen(true); + } - function selectBot(cardVO: BotCardVO) { - setIsEditForm(true) - setNowSelectedCard(cardVO) - console.log("set now vo", cardVO) - setModalOpen(true) - } + return ( +
+ + setModalOpen(false)} + onCancel={() => setModalOpen(false)} + width={700} + footer={null} + destroyOnClose={true} + > + { + getBotList(); + setModalOpen(false); + }} + onFormCancel={() => setModalOpen(false)} + /> + + {pageShowRule === BotConfigPageShowRule.NO_LLM && ( + { + router.push("/home/models"); + }} + /> + )} - return ( -
- - {/* 删除 spin,使用 spin 会导致盒子塌陷。 */} - setModalOpen(false)} - onCancel={() => setModalOpen(false)} - width={700} - footer={null} - destroyOnClose={true} - > - { - getBotList() - setModalOpen(false) - }} - onFormCancel={() => setModalOpen(false)} - /> - - {pageShowRule === BotConfigPageShowRule.NO_LLM && - { - router.push("/home/models"); - }} - /> - } - - {pageShowRule === BotConfigPageShowRule.NO_BOT && - - } - - {pageShowRule === BotConfigPageShowRule.HAVE_BOT && -
- {botList.map(cardVO => { - return ( -
{ selectBot(cardVO) }} - > - -
) - })} - -
- } + {pageShowRule === BotConfigPageShowRule.NO_BOT && ( + + )} + + {/* 注意:其余的返回内容需要保持在Spin组件外部 */} + {pageShowRule === BotConfigPageShowRule.HAVE_BOT && ( +
+ {botList.map((cardVO) => { + return ( +
{ + selectBot(cardVO); + }} + > + +
+ ); + })} +
- - ) + )} +
+ ); } enum BotConfigPageShowRule { - NO_LLM, - NO_BOT, - HAVE_BOT, -} \ No newline at end of file + NO_LLM, + NO_BOT, + HAVE_BOT +} diff --git a/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx b/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx index 39c2be86..b6e149ae 100644 --- a/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx +++ b/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx @@ -1,228 +1,60 @@ -"use client" +"use client"; +import { useState, useEffect } from "react"; import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent"; import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO"; -import { useEffect, useState } from "react"; import PluginCardComponent from "@/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent"; 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 - }, []) + }, []); function initData() { - getPluginList().then((value) => { - setPluginList(value) - }) + getPluginList(); } - 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 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" + }); + }) + ); + }); } function handleModalConfirm() { - installPlugin(githubURL) - setModalOpen(false) + installPlugin(githubURL); + setModalOpen(false); } function installPlugin(url: string) { - // TODO 接安装Plugin的接口 - console.log("installPlugin: ", url) + httpClient + .installPluginFromGithub(url) + .then(() => { + // 安装后重新拉取 + getPluginList(); + }) + .catch((err) => { + console.log("error when install plugin:", err); + }); } return (
@@ -231,25 +63,19 @@ export default function PluginInstalledComponent() {
- 从 GitHub 安装插件
} - centered open={modalOpen} - onOk={() => handleModalConfirm()} + onOk={handleModalConfirm} onCancel={() => setModalOpen(false)} - width={500} destroyOnClose={true} >
-
- 目前仅支持从 GitHub 安装 -
+
目前仅支持从 GitHub 安装
- { - pluginList.map((vo, index) => { - return
+ {pluginList.map((vo, index) => { + return ( +
- }) - } + ); + })} { - setModalOpen(true) + setModalOpen(true); }} />
- ) + ); } diff --git a/web/src/app/infra/http/HttpClient.ts b/web/src/app/infra/http/HttpClient.ts index a8bb53cc..58462059 100644 --- a/web/src/app/infra/http/HttpClient.ts +++ b/web/src/app/infra/http/HttpClient.ts @@ -26,7 +26,8 @@ import { ApiRespSystemInfo, ApiRespAsyncTasks, ApiRespAsyncTask, - ApiRespUserToken, MarketPluginResponse + ApiRespUserToken, + MarketPluginResponse } from "../api/api-types"; import { notification } from "antd"; @@ -50,22 +51,19 @@ export interface RequestConfig extends AxiosRequestConfig { class HttpClient { private instance: AxiosInstance; - private disableToken: boolean = false + private disableToken: boolean = false; // 暂不需要SSR // private ssrInstance: AxiosInstance | null = null - constructor( - baseURL?: string, - disableToken?: boolean - ) { + constructor(baseURL?: string, disableToken?: boolean) { this.instance = axios.create({ baseURL: baseURL || this.getBaseUrl(), timeout: 15000, headers: { - "Content-Type": "application/json", + "Content-Type": "application/json" } }); - this.disableToken = disableToken || false + this.disableToken = disableToken || false; this.initInterceptors(); } @@ -129,9 +127,9 @@ class HttpClient { const errMessage = data?.message || error.message; switch (status) { - case 401: - window.location.href = "/login"; - break; + // case 401: + // window.location.href = "/login"; + // break; case 403: console.error("Permission denied:", errMessage); break; @@ -358,7 +356,7 @@ class HttpClient { public getMarketPlugins( page: number, page_size: number, - query: string, + query: string ): Promise { return this.post(`/api/v1/market/plugins`, { page, @@ -366,7 +364,7 @@ class HttpClient { query, sort_by: "stars", sort_order: "DESC" - }) + }); } public installPluginFromGithub( source: string @@ -415,4 +413,4 @@ class HttpClient { export const httpClient = new HttpClient("https://version-4.langbot.dev"); // 临时写法,未来两种Client都继承自HttpClient父类,不允许共享方法 -export const spaceClient = new HttpClient("https://space.langbot.app") +export const spaceClient = new HttpClient("https://space.langbot.app"); From db547fb3788bf1f4ae703196e3324f97c2a48e89 Mon Sep 17 00:00:00 2001 From: chris <1637083533@qq.com> Date: Tue, 29 Apr 2025 15:36:03 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin-card/PluginCardComponent.tsx | 114 +++++++++--------- 1 file changed, 56 insertions(+), 58 deletions(-) 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 9efbbe9e..53b3ecf9 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,67 +1,65 @@ -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 { GithubOutlined, LinkOutlined, ToolOutlined } from "@ant-design/icons"; +import { Switch, Tag } from "antd"; import { useState } from "react"; import { httpClient } from "@/app/infra/http/HttpClient"; export default function PluginCardComponent({ - cardVO + cardVO }: { - cardVO: PluginCardVO + cardVO: PluginCardVO; }) { - const [initialized, setInitialized] = useState(cardVO.isInitialized); - const [switchEnable, setSwitchEnable] = useState(true); + 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} -
-
- {/* content */} -
-
{cardVO.name}
-
{cardVO.description}
-
- {/* footer */} -
-
-
- - 1 -
- -
-
+ 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}
- ); -} +
+ {/* content */} +
+
{cardVO.name}
+
{cardVO.description}
+
+ {/* footer */} +
+
+
+ + 1 +
+ +
+ + +
+
+ ); +} \ No newline at end of file From 5c162009eee90d2594cbc879be7bb7d970ebe675 Mon Sep 17 00:00:00 2001 From: chris <1637083533@qq.com> Date: Tue, 29 Apr 2025 15:41:17 +0800 Subject: [PATCH 7/8] =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/app/infra/http/HttpClient.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/app/infra/http/HttpClient.ts b/web/src/app/infra/http/HttpClient.ts index 58462059..7183ba59 100644 --- a/web/src/app/infra/http/HttpClient.ts +++ b/web/src/app/infra/http/HttpClient.ts @@ -127,9 +127,9 @@ class HttpClient { const errMessage = data?.message || error.message; switch (status) { - // case 401: - // window.location.href = "/login"; - // break; + case 401: + window.location.href = "/login"; + break; case 403: console.error("Permission denied:", errMessage); break; From f1beb108930f3f93aaf64bd0e680efd9e816a209 Mon Sep 17 00:00:00 2001 From: chris <1637083533@qq.com> Date: Tue, 29 Apr 2025 16:25:58 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8D=A1=E7=89=87=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin-card/PluginCardComponent.tsx | 27 +++--- .../plugin-card/pluginCard.module.css | 21 +++++ .../PluginMarketCardComponent.tsx | 91 +++++++++---------- .../pluginMarketCard.module.css | 1 + 4 files changed, 79 insertions(+), 61 deletions(-) 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 53b3ecf9..8999534f 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 @@ -46,20 +46,23 @@ export default function PluginCardComponent({
{/* footer */}
-
-
- - 1 +
+
+
+ + 1 +
+ +
+
+
-
- -
); -} \ No newline at end of file +} 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 3ec44fd4..1d09d462 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 @@ -41,8 +41,29 @@ .cardFooter { width: 90%; height: 30px; + position: relative; } +.footerContainer { + width: 100%; + height: 100%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +} + +.linkAndToolContainer { + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; +} + +.switchContainer { + display: flex; + justify-content: flex-end; +} .fontGray { color: #6C6C6C; diff --git a/web/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx b/web/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx index 5d96c57f..dcc1dfd0 100644 --- a/web/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx +++ b/web/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx @@ -1,56 +1,49 @@ -import styles from "./pluginMarketCard.module.css" -import {GithubOutlined, StarOutlined} from '@ant-design/icons'; -import {PluginMarketCardVO} from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO"; -import {Button} from "antd"; +import styles from "./pluginMarketCard.module.css"; +import { GithubOutlined, StarOutlined } from "@ant-design/icons"; +import { PluginMarketCardVO } from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO"; +import { Button } from "antd"; export default function PluginMarketCardComponent({ - cardVO + cardVO }: { - cardVO: PluginMarketCardVO + cardVO: PluginMarketCardVO; }) { + function handleInstallClick(pluginId: string) { + console.log("Install plugin: ", pluginId); + } - - function handleInstallClick (pluginId: string) { - console.log("Install plugin: ", pluginId) - } - - return ( -
- {/* header */} -
- {/* left author */} -
{cardVO.author}
- {/* right icon */} - -
- {/* content */} -
-
{cardVO.name}
-
{cardVO.description}
-
- {/* footer */} -
-
-
- - {cardVO.starCount} -
-
- -
+ return ( +
+ {/* header */} +
+ {/* left author */} +
{cardVO.author}
+ {/* right icon */} + +
+ {/* content */} +
+
{cardVO.name}
+
{cardVO.description}
+
+ {/* footer */} +
+
+
+ + {cardVO.starCount} +
- ); + +
+
+ ); } 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 33297b09..97a5e224 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 @@ -71,5 +71,6 @@ color: #6062E7; align-self: center; justify-content: space-between; + align-items: center; } } \ No newline at end of file