mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 03:55:55 +00:00
feat: perf plugin card
This commit is contained in:
@@ -1,26 +1,38 @@
|
|||||||
export interface IPluginCardVO {
|
export interface IPluginCardVO {
|
||||||
author: string;
|
author: string;
|
||||||
version: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
handlerCount: number;
|
version: string;
|
||||||
isInitialized: boolean;
|
enabled: boolean;
|
||||||
|
priority: number;
|
||||||
|
status: string;
|
||||||
|
tools: object[];
|
||||||
|
event_handlers: object;
|
||||||
|
repository: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PluginCardVO implements IPluginCardVO {
|
export class PluginCardVO implements IPluginCardVO {
|
||||||
description: string;
|
|
||||||
handlerCount: number;
|
|
||||||
name: string;
|
|
||||||
author: string;
|
author: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
version: string;
|
version: string;
|
||||||
isInitialized: boolean;
|
enabled: boolean;
|
||||||
|
priority: number;
|
||||||
|
status: string;
|
||||||
|
tools: object[];
|
||||||
|
event_handlers: object;
|
||||||
|
repository: string;
|
||||||
|
|
||||||
constructor(prop: IPluginCardVO) {
|
constructor(prop: IPluginCardVO) {
|
||||||
this.description = prop.description;
|
|
||||||
this.handlerCount = prop.handlerCount;
|
|
||||||
this.name = prop.name;
|
|
||||||
this.author = prop.author;
|
this.author = prop.author;
|
||||||
|
this.description = prop.description;
|
||||||
|
this.enabled = prop.enabled;
|
||||||
|
this.event_handlers = prop.event_handlers;
|
||||||
|
this.name = prop.name;
|
||||||
|
this.priority = prop.priority;
|
||||||
|
this.repository = prop.repository;
|
||||||
|
this.status = prop.status;
|
||||||
|
this.tools = prop.tools;
|
||||||
this.version = prop.version;
|
this.version = prop.version;
|
||||||
this.isInitialized = prop.isInitialized;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,10 +40,14 @@ const PluginInstalledComponent = forwardRef<PluginInstalledComponentRef>((props,
|
|||||||
return new PluginCardVO({
|
return new PluginCardVO({
|
||||||
author: plugin.author,
|
author: plugin.author,
|
||||||
description: plugin.description.zh_CN,
|
description: plugin.description.zh_CN,
|
||||||
handlerCount: 0,
|
enabled: plugin.enabled,
|
||||||
name: plugin.name,
|
name: plugin.name,
|
||||||
version: plugin.version,
|
version: plugin.version,
|
||||||
isInitialized: plugin.status === 'initialized',
|
status: plugin.status,
|
||||||
|
tools: plugin.tools,
|
||||||
|
event_handlers: plugin.event_handlers,
|
||||||
|
repository: plugin.repository,
|
||||||
|
priority: plugin.priority,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
import styles from './pluginCard.module.css';
|
import styles from './pluginCard.module.css';
|
||||||
import { PluginCardVO } from '@/app/home/plugins/plugin-installed/PluginCardVO';
|
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 { useState } from 'react';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import { Switch } from "@/components/ui/switch"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
|
||||||
export default function PluginCardComponent({
|
export default function PluginCardComponent({
|
||||||
cardVO,
|
cardVO,
|
||||||
}: {
|
}: {
|
||||||
cardVO: PluginCardVO;
|
cardVO: PluginCardVO;
|
||||||
}) {
|
}) {
|
||||||
const [initialized, setInitialized] = useState(cardVO.isInitialized);
|
const [enabled, setEnabled] = useState(cardVO.enabled);
|
||||||
const [switchEnable, setSwitchEnable] = useState(true);
|
const [switchEnable, setSwitchEnable] = useState(true);
|
||||||
|
|
||||||
function handleEnable() {
|
function handleEnable() {
|
||||||
setSwitchEnable(false);
|
setSwitchEnable(false);
|
||||||
httpClient
|
httpClient
|
||||||
.togglePlugin(cardVO.author, cardVO.name, !initialized)
|
.togglePlugin(cardVO.author, cardVO.name, !enabled)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setInitialized(!initialized);
|
setEnabled(!enabled);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log('error: ', err);
|
console.log('error: ', err);
|
||||||
@@ -29,38 +30,53 @@ export default function PluginCardComponent({
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.cardContainer}`}>
|
<div className={`${styles.cardContainer}`}>
|
||||||
{/* header */}
|
<div className={styles.contentContainer}>
|
||||||
<div className={`${styles.cardHeader}`}>
|
<svg className={styles.pluginIcon} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M8 4C8 2.34315 9.34315 1 11 1C12.6569 1 14 2.34315 14 4C14 4.35064 13.9398 4.68722 13.8293 5H18C18.5523 5 19 5.44772 19 6V10.1707C19.3128 10.0602 19.6494 10 20 10C21.6569 10 23 11.3431 23 13C23 14.6569 21.6569 16 20 16C19.6494 16 19.3128 15.9398 19 15.8293V20C19 20.5523 18.5523 21 18 21H4C3.44772 21 3 20.5523 3 20V6C3 5.44772 3.44772 5 4 5H8.17071C8.06015 4.68722 8 4.35064 8 4Z"></path></svg>
|
||||||
{/* left author */}
|
|
||||||
<div className={`${styles.fontGray}`}>{cardVO.author}</div>
|
<div className={styles.infoContainer}>
|
||||||
{/* right icon & version */}
|
|
||||||
<div className={`${styles.iconVersionContainer}`}>
|
<div className={styles.basicInfoContainer}>
|
||||||
<GithubOutlined style={{ fontSize: '26px' }} type="setting" />
|
|
||||||
<Tag color="#108ee9">v{cardVO.version}</Tag>
|
<div className={styles.nameContainer}>
|
||||||
</div>
|
<div className={styles.author}>{cardVO.author} / </div>
|
||||||
</div>
|
<div className={styles.nameAndVersionContainer}>
|
||||||
{/* content */}
|
<div className={styles.name}>{cardVO.name}</div>
|
||||||
<div className={`${styles.cardContent}`}>
|
<Badge variant="outline" className={styles.version}>v{cardVO.version}</Badge>
|
||||||
<div className={`${styles.boldFont}`}>{cardVO.name}</div>
|
</div>
|
||||||
<div className={`${styles.fontGray}`}>{cardVO.description}</div>
|
|
||||||
</div>
|
|
||||||
{/* footer */}
|
|
||||||
<div className={`${styles.cardFooter}`}>
|
|
||||||
<div className={`${styles.footerContainer}`}>
|
|
||||||
<div className={`${styles.linkAndToolContainer}`}>
|
|
||||||
<div className={`${styles.link}`}>
|
|
||||||
<LinkOutlined style={{ fontSize: '22px' }} />
|
|
||||||
<span>1</span>
|
|
||||||
</div>
|
</div>
|
||||||
<ToolOutlined style={{ fontSize: '22px' }} />
|
|
||||||
|
<div className={styles.description}>{cardVO.description}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`${styles.switchContainer}`}>
|
|
||||||
|
<div className={styles.componentContainer}>
|
||||||
|
<div className={styles.componentEntryContainer}>
|
||||||
|
<svg className={styles.componentIcon} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M24 12L18.3431 17.6569L16.9289 16.2426L21.1716 12L16.9289 7.75736L18.3431 6.34315L24 12ZM2.82843 12L7.07107 16.2426L5.65685 17.6569L0 12L5.65685 6.34315L7.07107 7.75736L2.82843 12ZM9.78845 21H7.66009L14.2116 3H16.3399L9.78845 21Z"></path></svg>
|
||||||
|
<div className={styles.componentText}>事件 11</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.componentEntryContainer}>
|
||||||
|
<svg className={styles.componentIcon} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5.32943 3.27158C6.56252 2.8332 7.9923 3.10749 8.97927 4.09446C10.1002 5.21537 10.3019 6.90741 9.5843 8.23385L20.293 18.9437L18.8788 20.3579L8.16982 9.64875C6.84325 10.3669 5.15069 10.1654 4.02952 9.04421C3.04227 8.05696 2.7681 6.62665 3.20701 5.39332L5.44373 7.63C6.02952 8.21578 6.97927 8.21578 7.56505 7.63C8.15084 7.04421 8.15084 6.09446 7.56505 5.50868L5.32943 3.27158ZM15.6968 5.15512L18.8788 3.38736L20.293 4.80157L18.5252 7.98355L16.7574 8.3371L14.6361 10.4584L13.2219 9.04421L15.3432 6.92289L15.6968 5.15512ZM8.97927 13.2868L10.3935 14.7011L5.09018 20.0044C4.69966 20.3949 4.06649 20.3949 3.67597 20.0044C3.31334 19.6417 3.28744 19.0699 3.59826 18.6774L3.67597 18.5902L8.97927 13.2868Z"></path></svg>
|
||||||
|
<div className={styles.componentText}>工具 45</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.operationContainer}>
|
||||||
|
<div className={styles.operationTop}>
|
||||||
<Switch
|
<Switch
|
||||||
value={initialized}
|
checked={enabled}
|
||||||
onClick={handleEnable}
|
onCheckedChange={handleEnable}
|
||||||
disabled={!switchEnable}
|
disabled={!switchEnable}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.operationBottom}>
|
||||||
|
{/* <Button variant="ghost">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6.17071 18C6.58254 16.8348 7.69378 16 9 16C10.3062 16 11.4175 16.8348 11.8293 18H22V20H11.8293C11.4175 21.1652 10.3062 22 9 22C7.69378 22 6.58254 21.1652 6.17071 20H2V18H6.17071ZM12.1707 11C12.5825 9.83481 13.6938 9 15 9C16.3062 9 17.4175 9.83481 17.8293 11H22V13H17.8293C17.4175 14.1652 16.3062 15 15 15C13.6938 15 12.5825 14.1652 12.1707 13H2V11H12.1707ZM6.17071 4C6.58254 2.83481 7.69378 2 9 2C10.3062 2 11.4175 2.83481 11.8293 4H22V6H11.8293C11.4175 7.16519 10.3062 8 9 8C7.69378 8 6.58254 7.16519 6.17071 6H2V4H6.17071ZM9 6C9.55228 6 10 5.55228 10 5C10 4.44772 9.55228 4 9 4C8.44772 4 8 4.44772 8 5C8 5.55228 8.44772 6 9 6ZM15 13C15.5523 13 16 12.5523 16 12C16 11.4477 15.5523 11 15 11C14.4477 11 14 11.4477 14 12C14 12.5523 14.4477 13 15 13ZM9 20C9.55228 20 10 19.5523 10 19C10 18.4477 9.55228 18 9 18C8.44772 18 8 18.4477 8 19C8 19.5523 8.44772 20 9 20Z"></path></svg>
|
||||||
|
</Button> */}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,94 +1,140 @@
|
|||||||
.cardContainer {
|
.cardContainer {
|
||||||
|
width: 26rem;
|
||||||
|
height: 10rem;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0px 2px 2px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
padding: 1.2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentContainer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
/* 修改为 100% 以撑满整个网格单元 */
|
height: 100%;
|
||||||
height: 140px;
|
display: flex;
|
||||||
box-sizing: border-box;
|
flex-direction: row;
|
||||||
background-color: #FFF;
|
align-items: flex-start;
|
||||||
border-radius: 9px;
|
justify-content: flex-start;
|
||||||
padding-top: 10px;
|
gap: 1.2rem;
|
||||||
padding-bottom: 10px;
|
}
|
||||||
box-shadow: rgba(0, 0, 0, 0.4) 0 1px 1px -1px;
|
|
||||||
|
.pluginIcon {
|
||||||
|
width: 4rem;
|
||||||
|
height: 4rem;
|
||||||
|
color: #2288ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoContainer {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
/* background-color: aqua; */
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
justify-content: space-evenly;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cardHeader {
|
.basicInfoContainer {
|
||||||
width: 90%;
|
display: flex;
|
||||||
height: 30px;
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.nameContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nameAndVersionContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: flex-start;
|
||||||
|
gap: 0.2rem;
|
||||||
.iconVersionContainer {
|
|
||||||
width: 90px;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cardContent {
|
.name {
|
||||||
width: 90%;
|
font-size: 1.2rem;
|
||||||
|
color: #000;
|
||||||
height: 70px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cardFooter {
|
.description {
|
||||||
width: 90%;
|
font-size: 1rem;
|
||||||
height: 30px;
|
color: #666;
|
||||||
position: relative;
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footerContainer {
|
.componentContainer {
|
||||||
width: 100%;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.componentEntryContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.componentIcon {
|
||||||
|
width: 1.2rem;
|
||||||
|
height: 1.2rem;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.componentText {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #555;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operationContainer {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: flex-end;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.linkAndToolContainer {
|
.operationTop {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.switchContainer {
|
.operationBottom {
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fontGray {
|
|
||||||
color: #6C6C6C;
|
|
||||||
}
|
|
||||||
|
|
||||||
.boldFont {
|
|
||||||
font-size: 22px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.linkSettingContainer {
|
|
||||||
width: 80px;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: center;
|
||||||
|
|
||||||
.link {
|
|
||||||
width: 32px;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-self: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
46
web/src/components/ui/badge.tsx
Normal file
46
web/src/components/ui/badge.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const badgeVariants = cva(
|
||||||
|
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
||||||
|
secondary:
|
||||||
|
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
||||||
|
destructive:
|
||||||
|
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||||
|
outline:
|
||||||
|
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function Badge({
|
||||||
|
className,
|
||||||
|
variant,
|
||||||
|
asChild = false,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"span"> &
|
||||||
|
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
||||||
|
const Comp = asChild ? Slot : "span"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
data-slot="badge"
|
||||||
|
className={cn(badgeVariants({ variant }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Badge, badgeVariants }
|
||||||
@@ -10,7 +10,7 @@ const buttonVariants = cva(
|
|||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default:
|
default:
|
||||||
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
"bg-[#2288ee] text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||||
destructive:
|
destructive:
|
||||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||||
outline:
|
outline:
|
||||||
|
|||||||
Reference in New Issue
Block a user