diff --git a/web_ui/package-lock.json b/web_ui/package-lock.json
index 269c3839..8279ebd0 100644
--- a/web_ui/package-lock.json
+++ b/web_ui/package-lock.json
@@ -11,6 +11,7 @@
"@ant-design/v5-patch-for-react-19": "^1.0.3",
"antd": "^5.24.6",
"axios": "^1.8.4",
+ "lodash": "^4.17.21",
"next": "15.2.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
@@ -18,6 +19,7 @@
},
"devDependencies": {
"@eslint/eslintrc": "^3",
+ "@types/lodash": "^4.17.16",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
@@ -1094,6 +1096,12 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
+ "node_modules/@types/lodash": {
+ "version": "4.17.16",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz",
+ "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==",
+ "dev": true
+ },
"node_modules/@types/node": {
"version": "20.17.27",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.27.tgz",
@@ -3837,6 +3845,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
diff --git a/web_ui/package.json b/web_ui/package.json
index 9e7c20f7..e01e5464 100644
--- a/web_ui/package.json
+++ b/web_ui/package.json
@@ -12,6 +12,7 @@
"@ant-design/v5-patch-for-react-19": "^1.0.3",
"antd": "^5.24.6",
"axios": "^1.8.4",
+ "lodash": "^4.17.21",
"next": "15.2.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
@@ -19,6 +20,7 @@
},
"devDependencies": {
"@eslint/eslintrc": "^3",
+ "@types/lodash": "^4.17.16",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
diff --git a/web_ui/src/app/home/plugins/plugin-installed/PluginCardVO.ts b/web_ui/src/app/home/plugins/plugin-installed/PluginCardVO.ts
index e83bd75c..768624d7 100644
--- a/web_ui/src/app/home/plugins/plugin-installed/PluginCardVO.ts
+++ b/web_ui/src/app/home/plugins/plugin-installed/PluginCardVO.ts
@@ -6,7 +6,7 @@ export interface IPluginCardVO {
handlerCount: number,
}
-export class PluginCardVO implements IPluginCardVO{
+export class PluginCardVO implements IPluginCardVO {
description: string;
handlerCount: number;
name: string;
diff --git a/web_ui/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx b/web_ui/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx
index be137ded..1c4511c7 100644
--- a/web_ui/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx
+++ b/web_ui/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx
@@ -17,7 +17,7 @@ export default function PluginCardComponent({
{/* right icon & version */}
v{cardVO.version}
diff --git a/web_ui/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx b/web_ui/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx
index c6b7b540..90487cd5 100644
--- a/web_ui/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx
+++ b/web_ui/src/app/home/plugins/plugin-market/PluginMarketComponent.tsx
@@ -1,9 +1,84 @@
"use client"
-export default function PluginMarketComponent () {
+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} from "antd";
+import {debounce} from "lodash"
+
+export default function PluginInstalledComponent () {
+ const [marketPluginList, setMarketPluginList] = useState
([])
+ const [searchKeyword, setSearchKeyword] = useState("")
+
+ useEffect(() => {
+ initData()
+ }, [])
+
+ function initData() {
+ getPluginList().then((value) => {
+ setMarketPluginList(value)
+ })
+ }
+
+ function onInputSearchKeyword(keyword: string) {
+ setSearchKeyword(keyword)
+ debounceSearch(keyword)
+ }
+
+ const debounceSearch = useCallback(
+ debounce((keyword: string) => {
+ console.log("debounce search", keyword)
+ searchPlugin(keyword).then(marketPluginList => {
+ setMarketPluginList(marketPluginList)
+ })
+ }, 500), []
+ )
+
+ async function searchPlugin(keyword: string): Promise {
+ // TODO 实现搜索
+ const demoResult: PluginMarketCardVO[] = []
+ for (let i = 0; i < keyword.length; i ++) {
+ demoResult.push(new PluginMarketCardVO({
+ author: "/hanahana",
+ description: "一个搜索测试的描述",
+ githubURL: "?",
+ name: "搜索插件" + i,
+ pluginId: `${i}`,
+ starCount: 19 + i,
+ version: `0.${i}`,
+ }))
+ }
+ return demoResult
+ }
+
+ async function getPluginList(): Promise {
+ return [
+ new PluginMarketCardVO({
+ pluginId: "aaa",
+ description: "一般的描述",
+ name: "插件AAA",
+ author: "/hana",
+ version: "0.1",
+ githubURL: "",
+ starCount: 23
+ }),
+ ]
+ }
+
return (
-
- plugin-market
+
+
onInputSearchKeyword(e.target.value)}
+ />
+ {
+ marketPluginList.map((vo, index) => {
+ return
+ })
+ }
)
}
diff --git a/web_ui/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx b/web_ui/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx
index b2a94b74..5d96c57f 100644
--- a/web_ui/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx
+++ b/web_ui/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent.tsx
@@ -1,7 +1,56 @@
-export function PluginMarketCardComponent() {
+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: PluginMarketCardVO
+}) {
+
+
+ function handleInstallClick (pluginId: string) {
+ console.log("Install plugin: ", pluginId)
+ }
+
return (
-
- plugin market card
+
+ {/* header */}
+
+ {/* left author */}
+
{cardVO.author}
+ {/* right icon */}
+
+
+ {/* content */}
+
+
{cardVO.name}
+
{cardVO.description}
+
+ {/* footer */}
+
+
+
+
+ {cardVO.starCount}
+
+
+
+
- )
+ );
}
diff --git a/web_ui/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO.ts b/web_ui/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO.ts
new file mode 100644
index 00000000..4a806ec4
--- /dev/null
+++ b/web_ui/src/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO.ts
@@ -0,0 +1,31 @@
+export interface IPluginMarketCardVO {
+ pluginId: string;
+ author: string,
+ version: string,
+ name: string,
+ description: string,
+ starCount: number,
+ githubURL: string,
+}
+
+export class PluginMarketCardVO implements IPluginMarketCardVO {
+ pluginId: string;
+ description: string;
+ name: string;
+ author: string;
+ version: string;
+ githubURL: string;
+ starCount: number;
+
+ constructor(prop: IPluginMarketCardVO) {
+ this.description = prop.description
+ this.name = prop.name
+ this.author = prop.author
+ this.version = prop.version
+ this.githubURL = prop.githubURL
+ this.starCount = prop.starCount
+ this.pluginId = prop.pluginId
+ }
+
+
+}
diff --git a/web_ui/src/app/home/plugins/plugin-market/plugin-market-card/pluginMarketCard.module.css b/web_ui/src/app/home/plugins/plugin-market/plugin-market-card/pluginMarketCard.module.css
new file mode 100644
index 00000000..f611f335
--- /dev/null
+++ b/web_ui/src/app/home/plugins/plugin-market/plugin-market-card/pluginMarketCard.module.css
@@ -0,0 +1,77 @@
+.cardContainer {
+ width: 360px;
+ height: 140px;
+ box-sizing: border-box;
+ background-color: #FFF;
+ border-radius: 9px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ box-shadow: rgba(0, 0, 0, 0.4) 0 1px 1px -1px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-evenly;
+}
+
+.cardHeader {
+ width: 90%;
+ height: 30px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+
+ .iconVersionContainer {
+ width: 90px;
+ height: 100%;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ }
+}
+
+.cardContent {
+ width: 90%;
+
+ height: 70px;
+}
+
+.cardFooter {
+ width: 90%;
+ height: 30px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+}
+
+
+.fontGray {
+ color: #6C6C6C;
+}
+
+.boldFont {
+ font-size: 22px;
+ font-weight: bold;
+}
+
+.linkSettingContainer {
+ width: 80px;
+ height: 100%;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+
+ .link {
+ width: 32px;
+ cursor: pointer;
+ text-align: center;
+ display: flex;
+ flex-direction: row;
+ color: #6062E7;
+ align-self: center;
+ justify-content: space-between;
+ }
+}