feat: update eslint & prettier rules

This commit is contained in:
HYana
2025-04-30 17:36:46 +08:00
parent d4af2d4326
commit 460e065eed
53 changed files with 2241 additions and 2102 deletions
@@ -1,39 +1,33 @@
import {BotCardVO} from "@/app/home/bots/components/bot-card/BotCardVO";
import styles from "./botCard.module.css";
import { BotCardVO } from '@/app/home/bots/components/bot-card/BotCardVO';
import styles from './botCard.module.css';
export default function BotCard({
botCardVO
}: {
botCardVO: BotCardVO;
}) {
return (
<div className={`${styles.cardContainer}`}>
{/* icon和基本信息 */}
<div className={`${styles.iconBasicInfoContainer}`}>
{/* icon */}
<div className={`${styles.icon}`}>
ICO
</div>
{/* bot基本信息 */}
<div className={`${styles.basicInfoContainer}`}>
<div className={`${styles.basicInfoText} ${styles.bigText}`}>
{botCardVO.name}
</div>
<div className={`${styles.basicInfoText}`}>
{botCardVO.adapter}
</div>
<div className={`${styles.basicInfoText}`}>
线{botCardVO.pipelineName}
</div>
</div>
</div>
{/* 描述和创建时间 */}
<div className={`${styles.urlAndUpdateText}`}>
{botCardVO.description}
</div>
{/* <div className={`${styles.urlAndUpdateText}`}>
export default function BotCard({ botCardVO }: { botCardVO: BotCardVO }) {
return (
<div className={`${styles.cardContainer}`}>
{/* icon和基本信息 */}
<div className={`${styles.iconBasicInfoContainer}`}>
{/* icon */}
<div className={`${styles.icon}`}>ICO</div>
{/* bot基本信息 */}
<div className={`${styles.basicInfoContainer}`}>
<div className={`${styles.basicInfoText} ${styles.bigText}`}>
{botCardVO.name}
</div>
<div className={`${styles.basicInfoText}`}>
{botCardVO.adapter}
</div>
<div className={`${styles.basicInfoText}`}>
线{botCardVO.pipelineName}
</div>
</div>
</div>
{/* 描述和创建时间 */}
<div className={`${styles.urlAndUpdateText}`}>
{botCardVO.description}
</div>
{/* <div className={`${styles.urlAndUpdateText}`}>
更新时间:{botCardVO.updateTime}
</div> */}
</div>
)
}
</div>
);
}
@@ -1,28 +1,26 @@
export interface IBotCardVO {
id: string;
name: string;
adapter: string;
description: string;
updateTime: string;
pipelineName: string;
id: string;
name: string;
adapter: string;
description: string;
updateTime: string;
pipelineName: string;
}
export class BotCardVO implements IBotCardVO {
id: string;
adapter: string;
description: string;
name: string;
updateTime: string;
pipelineName: string;
id: string;
adapter: string;
description: string;
name: string;
updateTime: string;
pipelineName: string;
constructor(props: IBotCardVO) {
this.id = props.id;
this.name = props.name;
this.adapter = props.adapter;
this.description = props.description;
this.updateTime = props.updateTime;
this.pipelineName = props.pipelineName;
}
}
constructor(props: IBotCardVO) {
this.id = props.id;
this.name = props.name;
this.adapter = props.adapter;
this.description = props.description;
this.updateTime = props.updateTime;
this.pipelineName = props.pipelineName;
}
}
@@ -1,24 +1,24 @@
import {
BotFormEntity,
IBotFormEntity
} from "@/app/home/bots/components/bot-form/BotFormEntity";
import { Button, Form, Input, notification, Select, Space } from "antd";
import { useEffect, useState } from "react";
import { IChooseAdapterEntity } from "@/app/home/bots/components/bot-form/ChooseAdapterEntity";
IBotFormEntity,
} from '@/app/home/bots/components/bot-form/BotFormEntity';
import { Button, Form, Input, notification, Select, Space } from 'antd';
import { useEffect, useState } from 'react';
import { IChooseAdapterEntity } from '@/app/home/bots/components/bot-form/ChooseAdapterEntity';
import {
DynamicFormItemConfig,
IDynamicFormItemConfig,
parseDynamicFormItemType
} from "@/app/home/components/dynamic-form/DynamicFormItemConfig";
import { UUID } from "uuidjs";
import DynamicFormComponent from "@/app/home/components/dynamic-form/DynamicFormComponent";
import { httpClient } from "@/app/infra/http/HttpClient";
import { Bot } from "@/app/infra/api/api-types";
parseDynamicFormItemType,
} from '@/app/home/components/dynamic-form/DynamicFormItemConfig';
import { UUID } from 'uuidjs';
import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
import { httpClient } from '@/app/infra/http/HttpClient';
import { Bot } from '@/app/infra/api/api-types';
export default function BotForm({
initBotId,
onFormSubmit,
onFormCancel
onFormCancel,
}: {
initBotId?: string;
onFormSubmit: (value: IBotFormEntity) => void;
@@ -55,9 +55,9 @@ export default function BotForm({
rawAdapterList.adapters.map((item) => {
return {
label: item.label.zh_CN,
value: item.name
value: item.name,
};
})
}),
);
// 初始化适配器表单map
rawAdapterList.adapters.forEach((rawAdapter) => {
@@ -71,9 +71,9 @@ export default function BotForm({
label: item.label,
name: item.name,
required: item.required,
type: parseDynamicFormItemType(item.type)
})
)
type: parseDynamicFormItemType(item.type),
}),
),
);
});
// 拉取初始化表单信息
@@ -99,12 +99,12 @@ export default function BotForm({
adapter: bot.adapter,
description: bot.description,
name: bot.name,
adapter_config: bot.adapter_config
adapter_config: bot.adapter_config,
});
}
function handleAdapterSelect(adapterName: string) {
console.log("Select adapter: ", adapterName);
console.log('Select adapter: ', adapterName);
if (adapterName) {
const dynamicFormConfigList =
adapterNameToDynamicConfigMap.get(adapterName);
@@ -129,33 +129,33 @@ export default function BotForm({
// 只有通过外层固定表单验证才会走到这里,真正的提交逻辑在这里
function onDynamicFormSubmit(value: object) {
setIsLoading(true);
console.log("set loading", true);
console.log('set loading', true);
if (initBotId) {
// 编辑提交
console.log("submit edit", form.getFieldsValue(), value);
console.log('submit edit', form.getFieldsValue(), value);
const updateBot: Bot = {
uuid: initBotId,
name: form.getFieldsValue().name,
description: form.getFieldsValue().description,
adapter: form.getFieldsValue().adapter,
adapter_config: value
adapter_config: value,
};
httpClient
.updateBot(initBotId, updateBot)
.then((res) => {
// TODO success toast
console.log("update bot success", res);
console.log('update bot success', res);
onFormSubmit(form.getFieldsValue());
notification.success({
message: "更新成功",
description: "机器人更新成功"
message: '更新成功',
description: '机器人更新成功',
});
})
.catch(() => {
// TODO error toast
notification.error({
message: "更新失败",
description: "机器人更新失败"
message: '更新失败',
description: '机器人更新失败',
});
})
.finally(() => {
@@ -165,20 +165,20 @@ export default function BotForm({
});
} else {
// 创建提交
console.log("submit create", form.getFieldsValue(), value);
console.log('submit create', form.getFieldsValue(), value);
const newBot: Bot = {
name: form.getFieldsValue().name,
description: form.getFieldsValue().description,
adapter: form.getFieldsValue().adapter,
adapter_config: value
adapter_config: value,
};
httpClient
.createBot(newBot)
.then((res) => {
// TODO success toast
notification.success({
message: "创建成功",
description: "机器人创建成功"
message: '创建成功',
description: '机器人创建成功',
});
console.log(res);
onFormSubmit(form.getFieldsValue());
@@ -186,8 +186,8 @@ export default function BotForm({
.catch(() => {
// TODO error toast
notification.error({
message: "创建失败",
description: "机器人创建失败"
message: '创建失败',
description: '机器人创建失败',
});
})
.finally(() => {
@@ -197,7 +197,7 @@ export default function BotForm({
});
}
setShowDynamicForm(false);
console.log("set loading", false);
console.log('set loading', false);
// TODO 刷新bot列表
// TODO 关闭当前弹窗 Already closed @setShowDynamicForm(false)?
}
@@ -217,9 +217,9 @@ export default function BotForm({
disabled={isLoading}
>
<Form.Item<IBotFormEntity>
label={"机器人名称"}
name={"name"}
rules={[{ required: true, message: "该项为必填项哦~" }]}
label={'机器人名称'}
name={'name'}
rules={[{ required: true, message: '该项为必填项哦~' }]}
>
<Input
placeholder="为机器人取个好听的名字吧~"
@@ -228,17 +228,17 @@ export default function BotForm({
</Form.Item>
<Form.Item<IBotFormEntity>
label={"描述"}
name={"description"}
rules={[{ required: true, message: "该项为必填项哦~" }]}
label={'描述'}
name={'description'}
rules={[{ required: true, message: '该项为必填项哦~' }]}
>
<Input placeholder="简单描述一下这个机器人"></Input>
</Form.Item>
<Form.Item<IBotFormEntity>
label={"平台/适配器选择"}
name={"adapter"}
rules={[{ required: true, message: "该项为必填项哦~" }]}
label={'平台/适配器选择'}
name={'adapter'}
rules={[{ required: true, message: '该项为必填项哦~' }]}
>
<Select
style={{ width: 220 }}
@@ -1,20 +1,20 @@
export interface IBotFormEntity {
name: string,
description: string,
adapter: string,
adapter_config: object;
name: string;
description: string;
adapter: string;
adapter_config: object;
}
export class BotFormEntity implements IBotFormEntity {
adapter: string;
description: string;
name: string;
adapter_config: object;
adapter: string;
description: string;
name: string;
adapter_config: object;
constructor(props: IBotFormEntity) {
this.adapter = props.adapter;
this.description = props.description;
this.name = props.name;
this.adapter_config = props.adapter_config;
}
}
constructor(props: IBotFormEntity) {
this.adapter = props.adapter;
this.description = props.description;
this.name = props.name;
this.adapter_config = props.adapter_config;
}
}
@@ -1,4 +1,4 @@
export interface IChooseAdapterEntity {
label: string
value: string
}
label: string;
value: string;
}
+29 -29
View File
@@ -1,21 +1,21 @@
"use client";
'use client';
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 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 { Bot } from "@/app/infra/api/api-types";
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 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 { Bot } from '@/app/infra/api/api-types';
export default function BotConfigPage() {
const router = useRouter();
const [pageShowRule, setPageShowRule] = useState<BotConfigPageShowRule>(
BotConfigPageShowRule.NO_BOT
BotConfigPageShowRule.NO_BOT,
);
const [modalOpen, setModalOpen] = useState<boolean>(false);
const [botList, setBotList] = useState<BotCardVO[]>([]);
@@ -49,10 +49,10 @@ export default function BotConfigPage() {
return new BotCardVO({
adapter: bot.adapter,
description: bot.description,
id: bot.uuid || "",
id: bot.uuid || '',
name: bot.name,
updateTime: bot.updated_at || "",
pipelineName: bot.use_pipeline_name || ""
updateTime: bot.updated_at || '',
pipelineName: bot.use_pipeline_name || '',
});
});
if (botList.length === 0) {
@@ -63,12 +63,12 @@ export default function BotConfigPage() {
setBotList(botList);
})
.catch((err) => {
console.error("get bot list error", 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: "获取机器人列表失败",
message: '获取机器人列表失败',
description: err.message,
placement: "bottomRight"
placement: 'bottomRight',
});
})
.finally(() => {
@@ -89,7 +89,7 @@ export default function BotConfigPage() {
function selectBot(cardVO: BotCardVO) {
setIsEditForm(true);
setNowSelectedCard(cardVO);
console.log("set now vo", cardVO);
console.log('set now vo', cardVO);
setModalOpen(true);
}
@@ -97,7 +97,7 @@ export default function BotConfigPage() {
<div className={styles.configPageContainer}>
<Spin spinning={isLoading} tip="加载中..." size="large">
<Modal
title={isEditForm ? "编辑机器人" : "创建机器人"}
title={isEditForm ? '编辑机器人' : '创建机器人'}
centered
open={modalOpen}
onOk={() => setModalOpen(false)}
@@ -117,20 +117,20 @@ export default function BotConfigPage() {
</Modal>
{pageShowRule === BotConfigPageShowRule.NO_LLM && (
<EmptyAndCreateComponent
title={"需要先创建大模型才能配置机器人哦~"}
subTitle={"快去创建一个吧!"}
buttonText={"创建大模型 GO"}
title={'需要先创建大模型才能配置机器人哦~'}
subTitle={'快去创建一个吧!'}
buttonText={'创建大模型 GO'}
onButtonClick={() => {
router.push("/home/models");
router.push('/home/models');
}}
/>
)}
{pageShowRule === BotConfigPageShowRule.NO_BOT && (
<EmptyAndCreateComponent
title={"您还未配置机器人哦~"}
subTitle={"快去创建一个吧!"}
buttonText={"创建机器人 +"}
title={'您还未配置机器人哦~'}
subTitle={'快去创建一个吧!'}
buttonText={'创建机器人 +'}
onButtonClick={handleCreateBotClick}
/>
)}
@@ -164,5 +164,5 @@ export default function BotConfigPage() {
enum BotConfigPageShowRule {
NO_LLM,
NO_BOT,
HAVE_BOT
HAVE_BOT,
}
@@ -1,30 +1,21 @@
import {IDynamicFormItemConfig} from "@/app/home/components/dynamic-form/DynamicFormItemConfig";
import {Form, FormInstance} from "antd";
import DynamicFormItemComponent from "@/app/home/components/dynamic-form/DynamicFormItemComponent";
import { IDynamicFormItemConfig } from '@/app/home/components/dynamic-form/DynamicFormItemConfig';
import { Form, FormInstance } from 'antd';
import DynamicFormItemComponent from '@/app/home/components/dynamic-form/DynamicFormItemComponent';
export default function DynamicFormComponent({
form,
itemConfigList,
onSubmit,
form,
itemConfigList,
onSubmit,
}: {
form: FormInstance<object>
itemConfigList: IDynamicFormItemConfig[]
onSubmit?: (val: object) => unknown
form: FormInstance<object>;
itemConfigList: IDynamicFormItemConfig[];
onSubmit?: (val: object) => unknown;
}) {
return (
<Form
form={form}
onFinish={onSubmit}
layout={"vertical"}
>
{
itemConfigList.map(config =>
<DynamicFormItemComponent
key={config.id}
config={config}
/>
)
}
</Form>
)
}
return (
<Form form={form} onFinish={onSubmit} layout={'vertical'}>
{itemConfigList.map((config) => (
<DynamicFormItemComponent key={config.id} config={config} />
))}
</Form>
);
}
@@ -1,38 +1,30 @@
import {Form, Input, InputNumber, Select, Switch} from "antd";
import {DynamicFormItemType, IDynamicFormItemConfig} from "@/app/home/components/dynamic-form/DynamicFormItemConfig";
import { Form, Input, InputNumber, Select, Switch } from 'antd';
import {
DynamicFormItemType,
IDynamicFormItemConfig,
} from '@/app/home/components/dynamic-form/DynamicFormItemConfig';
export default function DynamicFormItemComponent({
config
config,
}: {
config: IDynamicFormItemConfig
config: IDynamicFormItemConfig;
}) {
return (
<Form.Item
label={config.label.zh_CN}
name={config.name}
rules={[{required: config.required, message: "该项为必填项哦~"}]}
initialValue={config.default}
>
{
config.type === DynamicFormItemType.INT &&
<InputNumber/>
}
return (
<Form.Item
label={config.label.zh_CN}
name={config.name}
rules={[{ required: config.required, message: '该项为必填项哦~' }]}
initialValue={config.default}
>
{config.type === DynamicFormItemType.INT && <InputNumber />}
{
config.type === DynamicFormItemType.STRING &&
<Input/>
}
{config.type === DynamicFormItemType.STRING && <Input />}
{
config.type === DynamicFormItemType.BOOLEAN &&
<Switch defaultChecked/>
}
{config.type === DynamicFormItemType.BOOLEAN && <Switch defaultChecked />}
{
config.type === DynamicFormItemType.STRING_ARRAY &&
<Select options={[]}/>
}
</Form.Item>
)
}
{config.type === DynamicFormItemType.STRING_ARRAY && (
<Select options={[]} />
)}
</Form.Item>
);
}
@@ -1,51 +1,54 @@
export interface IDynamicFormItemConfig {
id: string;
default: string | number | boolean | Array<unknown>;
label: IDynamicFormItemLabel;
name: string;
required: boolean;
type: DynamicFormItemType
description?: IDynamicFormItemLabel;
id: string;
default: string | number | boolean | Array<unknown>;
label: IDynamicFormItemLabel;
name: string;
required: boolean;
type: DynamicFormItemType;
description?: IDynamicFormItemLabel;
}
export class DynamicFormItemConfig implements IDynamicFormItemConfig {
id: string;
name: string;
default: string | number | boolean | Array<unknown>;
label: IDynamicFormItemLabel;
required: boolean;
type: DynamicFormItemType;
description?: IDynamicFormItemLabel;
constructor(params: IDynamicFormItemConfig) {
this.id = params.id;
this.name = params.name;
this.default = params.default;
this.label = params.label;
this.required = params.required;
this.type = params.type;
this.description = params.description;
}
id: string;
name: string;
default: string | number | boolean | Array<unknown>;
label: IDynamicFormItemLabel;
required: boolean;
type: DynamicFormItemType;
description?: IDynamicFormItemLabel;
constructor(params: IDynamicFormItemConfig) {
this.id = params.id;
this.name = params.name;
this.default = params.default;
this.label = params.label;
this.required = params.required;
this.type = params.type;
this.description = params.description;
}
}
export interface IDynamicFormItemLabel {
en_US: string,
zh_CN: string,
en_US: string;
zh_CN: string;
}
export enum DynamicFormItemType {
INT = "integer",
STRING = "string",
BOOLEAN = "boolean",
STRING_ARRAY = "array[string]",
UNKNOWN = "unknown",
INT = 'integer',
STRING = 'string',
BOOLEAN = 'boolean',
STRING_ARRAY = 'array[string]',
UNKNOWN = 'unknown',
}
export function isDynamicFormItemType(value: string): value is DynamicFormItemType {
return Object.values(DynamicFormItemType).includes(value as DynamicFormItemType);
export function isDynamicFormItemType(
value: string,
): value is DynamicFormItemType {
return Object.values(DynamicFormItemType).includes(
value as DynamicFormItemType,
);
}
export function parseDynamicFormItemType(value: string): DynamicFormItemType {
return isDynamicFormItemType(value) ? value : DynamicFormItemType.UNKNOWN;
}
return isDynamicFormItemType(value) ? value : DynamicFormItemType.UNKNOWN;
}
@@ -1,41 +1,41 @@
import {
DynamicFormItemConfig,
DynamicFormItemType,
IDynamicFormItemConfig
} from "@/app/home/components/dynamic-form/DynamicFormItemConfig";
DynamicFormItemConfig,
DynamicFormItemType,
IDynamicFormItemConfig,
} from '@/app/home/components/dynamic-form/DynamicFormItemConfig';
export const testDynamicConfigList: IDynamicFormItemConfig[] = [
new DynamicFormItemConfig({
default: "",
id: "111",
label: {
zh_CN: "测试字段string",
en_US: "eng test"
},
name: "string_test",
required: false,
type: DynamicFormItemType.STRING
}),
new DynamicFormItemConfig({
default: "",
id: "222",
label: {
zh_CN: "测试字段int",
en_US: "int eng test"
},
name: "int_test",
required: true,
type: DynamicFormItemType.INT
}),
new DynamicFormItemConfig({
default: "",
id: "333",
label: {
zh_CN: "测试字段boolean",
en_US: "boolean eng test"
},
name: "boolean_test",
required: false,
type: DynamicFormItemType.BOOLEAN
}),
]
new DynamicFormItemConfig({
default: '',
id: '111',
label: {
zh_CN: '测试字段string',
en_US: 'eng test',
},
name: 'string_test',
required: false,
type: DynamicFormItemType.STRING,
}),
new DynamicFormItemConfig({
default: '',
id: '222',
label: {
zh_CN: '测试字段int',
en_US: 'int eng test',
},
name: 'int_test',
required: true,
type: DynamicFormItemType.INT,
}),
new DynamicFormItemConfig({
default: '',
id: '333',
label: {
zh_CN: '测试字段boolean',
en_US: 'boolean eng test',
},
name: 'boolean_test',
required: false,
type: DynamicFormItemType.BOOLEAN,
}),
];
@@ -1,35 +1,27 @@
import styles from "./emptyAndCreate.module.css";
import styles from './emptyAndCreate.module.css';
export default function EmptyAndCreateComponent({
title,
subTitle,
buttonText,
onButtonClick,
export default function EmptyAndCreateComponent({
title,
subTitle,
buttonText,
onButtonClick,
}: {
title: string,
subTitle: string,
buttonText: string,
onButtonClick: () => void,
title: string;
subTitle: string;
buttonText: string;
onButtonClick: () => void;
}) {
return (
<div className={`${styles.emptyPageContainer}`}>
<div className={`${styles.emptyContainer}`}>
<div className={`${styles.emptyInfoContainer}`}>
<div className={`${styles.emptyInfoText}`}>
{title}
</div>
<div className={`${styles.emptyInfoSubText}`}>
{subTitle}
</div>
</div>
<div
className={`${styles.emptyCreateButton}`}
onClick={onButtonClick}
>
{buttonText}
</div>
</div>
return (
<div className={`${styles.emptyPageContainer}`}>
<div className={`${styles.emptyContainer}`}>
<div className={`${styles.emptyInfoContainer}`}>
<div className={`${styles.emptyInfoText}`}>{title}</div>
<div className={`${styles.emptyInfoSubText}`}>{subTitle}</div>
</div>
)
}
<div className={`${styles.emptyCreateButton}`} onClick={onButtonClick}>
{buttonText}
</div>
</div>
</div>
);
}
@@ -1,17 +1,17 @@
"use client";
'use client';
import styles from "./HomeSidebar.module.css";
import { useEffect, useState } from "react";
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 { sidebarConfigList } from "@/app/home/components/home-sidebar/sidbarConfigList";
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({
onSelectedChangeAction
onSelectedChangeAction,
}: {
onSelectedChangeAction: (sidebarChild: SidebarChildVO) => void;
}) {
@@ -24,13 +24,13 @@ export default function HomeSidebar({
}, [pathname]);
const [selectedChild, setSelectedChild] = useState<SidebarChildVO>(
sidebarConfigList[0]
sidebarConfigList[0],
);
useEffect(() => {
console.log("HomeSidebar挂载完成");
console.log('HomeSidebar挂载完成');
initSelect();
return () => console.log("HomeSidebar卸载");
return () => console.log('HomeSidebar卸载');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -52,14 +52,14 @@ export default function HomeSidebar({
function handleRouteChange(pathname: string) {
// TODO 这段逻辑并不好,未来router封装好后改掉
// 判断在home下,并且路由更改的是自己的路由子组件则更新UI
const routeList = pathname.split("/");
const routeList = pathname.split('/');
if (
routeList[1] === "home" &&
routeList[1] === 'home' &&
sidebarConfigList.find((childConfig) => childConfig.route === pathname)
) {
console.log("find success");
console.log('find success');
const routeSelectChild = sidebarConfigList.find(
(childConfig) => childConfig.route === pathname
(childConfig) => childConfig.route === pathname,
);
if (routeSelectChild) {
setSelectedChild(routeSelectChild);
@@ -67,42 +67,34 @@ export default function HomeSidebar({
}
}
return (
<div className={`${styles.sidebarContainer}`}>
{/* LangBot、ICON区域 */}
<div className={`${styles.langbotIconContainer}`}>
{/* icon */}
<div className={`${styles.langbotIcon}`}>
L
</div>
<div className={`${styles.langbotText}`}>
Langbot
</div>
return (
<div className={`${styles.sidebarContainer}`}>
{/* LangBot、ICON区域 */}
<div className={`${styles.langbotIconContainer}`}>
{/* icon */}
<div className={`${styles.langbotIcon}`}>L</div>
<div className={`${styles.langbotText}`}>Langbot</div>
</div>
{/* 菜单列表,后期可升级成配置驱动 */}
<div>
{sidebarConfigList.map((config) => {
return (
<div
key={config.id}
onClick={() => {
console.log('click:', config.id);
handleChildClick(config);
}}
>
<SidebarChild
isSelected={selectedChild.id === config.id}
icon={config.icon}
name={config.name}
/>
</div>
{/* 菜单列表,后期可升级成配置驱动 */}
<div>
{
sidebarConfigList.map(config => {
return (
<div
key={config.id}
onClick={() => {
console.log('click:', config.id)
handleChildClick(config)
}}
>
<SidebarChild
isSelected={selectedChild.id === config.id}
icon={config.icon}
name={config.name}
/>
</div>
)
})
}
</div>
</div>
);
}
);
})}
</div>
</div>
);
}
@@ -1,4 +1,4 @@
import styles from "./HomeSidebar.module.css";
import styles from './HomeSidebar.module.css';
export interface ISidebarChildVO {
id: string;
@@ -24,7 +24,7 @@ export class SidebarChildVO {
export function SidebarChild({
icon,
name,
isSelected
isSelected,
}: {
icon: string;
name: string;
@@ -1,28 +1,28 @@
import {SidebarChildVO} from "@/app/home/components/home-sidebar/HomeSidebarChild";
import { SidebarChildVO } from '@/app/home/components/home-sidebar/HomeSidebarChild';
export const sidebarConfigList = [
new SidebarChildVO({
id: "models",
name: "模型配置",
icon: "",
route: "/home/models",
}),
new SidebarChildVO({
id: "bots",
name: "机器人",
icon: "",
route: "/home/bots",
}),
new SidebarChildVO({
id: "pipelines",
name: "流水线",
icon: "",
route: "/home/pipelines",
}),
new SidebarChildVO({
id: "plugins",
name: "插件管理",
icon: "",
route: "/home/plugins",
}),
]
new SidebarChildVO({
id: 'models',
name: '模型配置',
icon: '',
route: '/home/models',
}),
new SidebarChildVO({
id: 'bots',
name: '机器人',
icon: '',
route: '/home/bots',
}),
new SidebarChildVO({
id: 'pipelines',
name: '流水线',
icon: '',
route: '/home/pipelines',
}),
new SidebarChildVO({
id: 'plugins',
name: '插件管理',
icon: '',
route: '/home/plugins',
}),
];
@@ -1,16 +1,9 @@
import styles from "./HomeTittleBar.module.css"
import styles from './HomeTittleBar.module.css';
export default function HomeTitleBar({
title,
}: {
title: string
}) {
return (
<div className={`${styles.titleBarContainer}`}>
<div
className={`${styles.titleText}`}
>{title}</div>
</div>
);
}
export default function HomeTitleBar({ title }: { title: string }) {
return (
<div className={`${styles.titleBarContainer}`}>
<div className={`${styles.titleText}`}>{title}</div>
</div>
);
}
+12 -16
View File
@@ -1,35 +1,31 @@
"use client"
'use client';
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 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 { Layout } from 'antd';
const { Sider, Content } = Layout;
export default function HomeLayout({
children
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const router = useRouter();
const [title, setTitle] = useState<string>("")
const [title, setTitle] = useState<string>('');
const onSelectedChangeAction = (child: SidebarChildVO) => {
setTitle(child.name)
}
setTitle(child.name);
};
return (
<Layout className={styles.homeLayoutContainer}>
{/* homeLayoutContainer 是整个容器的入口,使用 flex 的左右布局 */}
<Sider className="left">
<HomeSidebar
onSelectedChangeAction={onSelectedChangeAction}
/>
<HomeSidebar onSelectedChangeAction={onSelectedChangeAction} />
{/* HomeSidebar 为侧边栏 */}
</Sider>
@@ -44,5 +40,5 @@ export default function HomeLayout({
</Content>
</Layout>
</Layout>
)
);
}
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -1,8 +1,8 @@
export interface ICreateLLMField {
name: string;
model_provider: string;
url: string;
api_key: string;
abilities: string[];
extra_args: string[];
}
name: string;
model_provider: string;
url: string;
api_key: string;
abilities: string[];
extra_args: string[];
}
@@ -1,33 +1,25 @@
import styles from "../../LLMConfig.module.css"
import {LLMCardVO} from "@/app/home/models/component/llm-card/LLMCardVO";
import styles from '../../LLMConfig.module.css';
import { LLMCardVO } from '@/app/home/models/component/llm-card/LLMCardVO';
export default function LLMCard({
cardVO
}: {
cardVO: LLMCardVO
}) {
return (
<div className={`${styles.cardContainer}`}>
{/* icon和基本信息 */}
<div className={`${styles.iconBasicInfoContainer}`}>
{/* icon */}
<div className={`${styles.icon}`}>
ICO
</div>
{/* bot基本信息 */}
<div className={`${styles.basicInfoContainer}`}>
<div className={`${styles.basicInfoText} ${styles.bigText}`}>
{cardVO.name}
</div>
<div className={`${styles.basicInfoText}`}>
{cardVO.company}
</div>
</div>
</div>
{/* URL和创建时间 */}
<div className={`${styles.urlAndUpdateText}`}>
URL{cardVO.URL}
</div>
export default function LLMCard({ cardVO }: { cardVO: LLMCardVO }) {
return (
<div className={`${styles.cardContainer}`}>
{/* icon和基本信息 */}
<div className={`${styles.iconBasicInfoContainer}`}>
{/* icon */}
<div className={`${styles.icon}`}>ICO</div>
{/* bot基本信息 */}
<div className={`${styles.basicInfoContainer}`}>
<div className={`${styles.basicInfoText} ${styles.bigText}`}>
{cardVO.name}
</div>
<div className={`${styles.basicInfoText}`}>
{cardVO.company}
</div>
</div>
);
}
</div>
{/* URL和创建时间 */}
<div className={`${styles.urlAndUpdateText}`}>URL{cardVO.URL}</div>
</div>
);
}
@@ -1,21 +1,20 @@
export interface ILLMCardVO {
id: string;
name: string;
company: string;
URL: string;
id: string;
name: string;
company: string;
URL: string;
}
export class LLMCardVO implements ILLMCardVO {
id: string;
name: string;
company: string;
URL: string;
id: string;
name: string;
company: string;
URL: string;
constructor(props: ILLMCardVO) {
this.id = props.id;
this.name = props.name;
this.company = props.company;
this.URL = props.URL;
}
}
constructor(props: ILLMCardVO) {
this.id = props.id;
this.name = props.name;
this.company = props.company;
this.URL = props.URL;
}
}
@@ -1,4 +1,4 @@
export interface IChooseRequesterEntity {
label: string
value: string
}
label: string;
value: string;
}
@@ -1,18 +1,18 @@
import styles from "@/app/home/models/LLMConfig.module.css";
import { Button, Form, Input, Select, SelectProps, Space, Modal } from "antd";
import { ICreateLLMField } from "@/app/home/models/ICreateLLMField";
import { useEffect, useState } from "react";
import { IChooseRequesterEntity } from "@/app/home/models/component/llm-form/ChooseAdapterEntity";
import { httpClient } from "@/app/infra/http/HttpClient";
import { LLMModel } from "@/app/infra/api/api-types";
import { UUID } from "uuidjs";
import styles from '@/app/home/models/LLMConfig.module.css';
import { Button, Form, Input, Select, SelectProps, Space, Modal } from 'antd';
import { ICreateLLMField } from '@/app/home/models/ICreateLLMField';
import { useEffect, useState } from 'react';
import { IChooseRequesterEntity } from '@/app/home/models/component/llm-form/ChooseAdapterEntity';
import { httpClient } from '@/app/infra/http/HttpClient';
import { LLMModel } from '@/app/infra/api/api-types';
import { UUID } from 'uuidjs';
export default function LLMForm({
editMode,
initLLMId,
onFormSubmit,
onFormCancel,
onLLMDeleted
onLLMDeleted,
}: {
editMode: boolean;
initLLMId?: string;
@@ -21,18 +21,18 @@ export default function LLMForm({
onLLMDeleted: () => void;
}) {
const [form] = Form.useForm<ICreateLLMField>();
const extraOptions: SelectProps["options"] = [];
const extraOptions: SelectProps['options'] = [];
const [initValue] = useState<ICreateLLMField>();
const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false);
const abilityOptions: SelectProps["options"] = [
const abilityOptions: SelectProps['options'] = [
{
label: "函数调用",
value: "func_call"
label: '函数调用',
value: 'func_call',
},
{
label: "图像识别",
value: "vision"
}
label: '图像识别',
value: 'vision',
},
];
const [requesterNameList, setRequesterNameList] = useState<
IChooseRequesterEntity[]
@@ -56,9 +56,9 @@ export default function LLMForm({
requesterNameList.requesters.map((item) => {
return {
label: item.label.zh_CN,
value: item.name
value: item.name,
};
})
}),
);
}
@@ -76,7 +76,7 @@ export default function LLMForm({
url: llmModel.model.requester_config?.base_url,
api_key: llmModel.model.api_keys[0],
abilities: llmModel.model.abilities,
extra_args: fakeExtraArgs
extra_args: fakeExtraArgs,
};
}
@@ -110,19 +110,19 @@ export default function LLMForm({
// }
function onCreateLLM(value: ICreateLLMField) {
console.log("create llm", value);
console.log('create llm', value);
const requestParam: LLMModel = {
uuid: UUID.generate(),
name: value.name,
description: "",
description: '',
requester: value.model_provider,
requester_config: {
base_url: value.url,
timeout: 120
timeout: 120,
},
extra_args: value.extra_args,
api_keys: [value.api_key],
abilities: value.abilities
abilities: value.abilities,
// created_at: 'Sun Apr 27 2025 21:56:35 GMT+0800',
// updated_at: 'Sun Apr 27 2025 21:56:35 GMT+0800',
};
@@ -145,15 +145,15 @@ export default function LLMForm({
<div className={styles.modalContainer}>
<Modal
open={showDeleteConfirmModal}
title={"删除确认"}
title={'删除确认'}
onCancel={() => setShowDeleteConfirmModal(false)}
footer={
<div
style={{
width: "170px",
display: "flex",
flexDirection: "row",
justifyContent: "space-between"
width: '170px',
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
}}
>
<Button
@@ -183,27 +183,27 @@ export default function LLMForm({
wrapperCol={{ span: 14 }}
layout="horizontal"
initialValues={{
...initValue
...initValue,
}}
onFinish={handleFormSubmit}
clearOnDestroy={true}
disabled={editMode}
>
<Form.Item<ICreateLLMField>
label={"模型名称"}
name={"name"}
rules={[{ required: true, message: "该项为必填项哦~" }]}
label={'模型名称'}
name={'name'}
rules={[{ required: true, message: '该项为必填项哦~' }]}
>
<Input
placeholder={"为自己的大模型取个好听的名字~"}
placeholder={'为自己的大模型取个好听的名字~'}
style={{ width: 260 }}
></Input>
</Form.Item>
<Form.Item<ICreateLLMField>
label={"模型供应商"}
name={"model_provider"}
rules={[{ required: true, message: "该项为必填项哦~" }]}
label={'模型供应商'}
name={'model_provider'}
rules={[{ required: true, message: '该项为必填项哦~' }]}
>
<Select
style={{ width: 120 }}
@@ -213,9 +213,9 @@ export default function LLMForm({
</Form.Item>
<Form.Item<ICreateLLMField>
label={"请求URL"}
name={"url"}
rules={[{ required: true, message: "该项为必填项哦~" }]}
label={'请求URL'}
name={'url'}
rules={[{ required: true, message: '该项为必填项哦~' }]}
>
<Input
placeholder="请求地址,一般是API提供商提供的URL"
@@ -224,14 +224,14 @@ export default function LLMForm({
</Form.Item>
<Form.Item<ICreateLLMField>
label={"API Key"}
name={"api_key"}
rules={[{ required: true, message: "该项为必填项哦~" }]}
label={'API Key'}
name={'api_key'}
rules={[{ required: true, message: '该项为必填项哦~' }]}
>
<Input placeholder="你的API Key" style={{ width: 500 }}></Input>
</Form.Item>
<Form.Item<ICreateLLMField> label={"开启能力"} name={"abilities"}>
<Form.Item<ICreateLLMField> label={'开启能力'} name={'abilities'}>
<Select
mode="tags"
style={{ width: 500 }}
@@ -241,7 +241,7 @@ export default function LLMForm({
/>
</Form.Item>
<Form.Item<ICreateLLMField> label={"其他参数"} name={"extra_args"}>
<Form.Item<ICreateLLMField> label={'其他参数'} name={'extra_args'}>
<Select
mode="tags"
style={{ width: 500 }}
+114 -107
View File
@@ -1,116 +1,123 @@
"use client"
'use client';
import {useState, useEffect} from "react";
import {LLMCardVO} from "@/app/home/models/component/llm-card/LLMCardVO";
import styles from "./LLMConfig.module.css"
import EmptyAndCreateComponent from "@/app/home/components/empty-and-create-component/EmptyAndCreateComponent";
import {Modal} from "antd";
import LLMCard from "@/app/home/models/component/llm-card/LLMCard";
import LLMForm from "@/app/home/models/component/llm-form/LLMForm";
import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent";
import { httpClient } from "@/app/infra/http/HttpClient";
import { LLMModel } from "@/app/infra/api/api-types";
import { useState, useEffect } from 'react';
import { LLMCardVO } from '@/app/home/models/component/llm-card/LLMCardVO';
import styles from './LLMConfig.module.css';
import EmptyAndCreateComponent from '@/app/home/components/empty-and-create-component/EmptyAndCreateComponent';
import { Modal } from 'antd';
import LLMCard from '@/app/home/models/component/llm-card/LLMCard';
import LLMForm from '@/app/home/models/component/llm-form/LLMForm';
import CreateCardComponent from '@/app/infra/basic-component/create-card-component/CreateCardComponent';
import { httpClient } from '@/app/infra/http/HttpClient';
import { LLMModel } from '@/app/infra/api/api-types';
export default function LLMConfigPage() {
const [cardList, setCardList] = useState<LLMCardVO[]>([])
const [modalOpen, setModalOpen] = useState<boolean>(false);
const [isEditForm, setIsEditForm] = useState(false)
const [nowSelectedLLM, setNowSelectedLLM] = useState<LLMCardVO | null>(null)
const [cardList, setCardList] = useState<LLMCardVO[]>([]);
const [modalOpen, setModalOpen] = useState<boolean>(false);
const [isEditForm, setIsEditForm] = useState(false);
const [nowSelectedLLM, setNowSelectedLLM] = useState<LLMCardVO | null>(null);
useEffect(() => {
getLLMModelList()
}, [])
useEffect(() => {
getLLMModelList();
}, []);
function getLLMModelList() {
httpClient.getProviderLLMModels().then((resp) => {
const llmModelList: LLMCardVO[] = resp.models.map((model: LLMModel) => {
console.log("model", model)
return new LLMCardVO({
id: model.uuid,
name: model.name,
company: model.requester,
URL: model.requester_config?.base_url,
})
})
console.log("get llmModelList", llmModelList)
setCardList(llmModelList)
}).catch((err) => {
// TODO error toast
console.error("get LLM model list error", err)
})
}
function getLLMModelList() {
httpClient
.getProviderLLMModels()
.then((resp) => {
const llmModelList: LLMCardVO[] = resp.models.map((model: LLMModel) => {
console.log('model', model);
return new LLMCardVO({
id: model.uuid,
name: model.name,
company: model.requester,
URL: model.requester_config?.base_url,
});
});
console.log('get llmModelList', llmModelList);
setCardList(llmModelList);
})
.catch((err) => {
// TODO error toast
console.error('get LLM model list error', err);
});
}
function selectLLM(cardVO: LLMCardVO) {
setIsEditForm(true)
setNowSelectedLLM(cardVO)
console.log("set now vo", cardVO)
setModalOpen(true)
}
function handleCreateModelClick() {
setIsEditForm(false)
setNowSelectedLLM(null)
setModalOpen(true);
}
function selectLLM(cardVO: LLMCardVO) {
setIsEditForm(true);
setNowSelectedLLM(cardVO);
console.log('set now vo', cardVO);
setModalOpen(true);
}
function handleCreateModelClick() {
setIsEditForm(false);
setNowSelectedLLM(null);
setModalOpen(true);
}
return (
<div className={styles.configPageContainer}>
<Modal
title={isEditForm ? "预览模型" : "创建模型"}
centered
open={modalOpen}
destroyOnClose={true}
onOk={() => setModalOpen(false)}
onCancel={() => setModalOpen(false)}
width={700}
footer={null}
>
<LLMForm
editMode={isEditForm}
initLLMId={nowSelectedLLM?.id}
onFormSubmit={() => {
setModalOpen(false);
getLLMModelList()
}}
onFormCancel={() => {
setModalOpen(false);
}}
onLLMDeleted={() => {
setModalOpen(false)
getLLMModelList()
}}
/>
</Modal>
{
cardList.length > 0 &&
<div className={`${styles.modelListContainer}`}
>
{cardList.map(cardVO => {
return <div key={cardVO.id} onClick={() => {selectLLM(cardVO)}}>
<LLMCard cardVO={cardVO}></LLMCard>
</div>
})}
<CreateCardComponent
width={360}
height={200}
plusSize={90}
onClick={handleCreateModelClick}
/>
</div>
}
{
cardList.length === 0 &&
<div className={`${styles.emptyContainer}`}>
<EmptyAndCreateComponent
title={"模型列表空空如也~"}
subTitle={"快去创建一个吧!"}
buttonText={"创建模型 +"}
onButtonClick={() => {
handleCreateModelClick()
}}
/>
</div>
}
return (
<div className={styles.configPageContainer}>
<Modal
title={isEditForm ? '预览模型' : '创建模型'}
centered
open={modalOpen}
destroyOnClose={true}
onOk={() => setModalOpen(false)}
onCancel={() => setModalOpen(false)}
width={700}
footer={null}
>
<LLMForm
editMode={isEditForm}
initLLMId={nowSelectedLLM?.id}
onFormSubmit={() => {
setModalOpen(false);
getLLMModelList();
}}
onFormCancel={() => {
setModalOpen(false);
}}
onLLMDeleted={() => {
setModalOpen(false);
getLLMModelList();
}}
/>
</Modal>
{cardList.length > 0 && (
<div className={`${styles.modelListContainer}`}>
{cardList.map((cardVO) => {
return (
<div
key={cardVO.id}
onClick={() => {
selectLLM(cardVO);
}}
>
<LLMCard cardVO={cardVO}></LLMCard>
</div>
);
})}
<CreateCardComponent
width={360}
height={200}
plusSize={90}
onClick={handleCreateModelClick}
/>
</div>
)
)}
{cardList.length === 0 && (
<div className={`${styles.emptyContainer}`}>
<EmptyAndCreateComponent
title={'模型列表空空如也~'}
subTitle={'快去创建一个吧!'}
buttonText={'创建模型 +'}
onButtonClick={() => {
handleCreateModelClick();
}}
/>
</div>
)}
</div>
);
}
+1 -4
View File
@@ -1,6 +1,3 @@
export default function Home() {
return (
<div className={``}>
</div>
);
return <div className={``}></div>;
}
@@ -1,33 +1,29 @@
import styles from "./pipelineCard.module.css";
import {PipelineCardVO} from "@/app/home/pipelines/components/pipeline-card/PipelineCardVO";
import styles from './pipelineCard.module.css';
import { PipelineCardVO } from '@/app/home/pipelines/components/pipeline-card/PipelineCardVO';
export default function PipelineCardComponent({
cardVO
cardVO,
}: {
cardVO: PipelineCardVO
cardVO: PipelineCardVO;
}) {
return (
<div className={`${styles.cardContainer}`}>
{/* icon和基本信息 */}
<div className={`${styles.iconBasicInfoContainer}`}>
{/* icon */}
<div className={`${styles.icon}`}>
ICO
</div>
{/* 基本信息 */}
<div className={`${styles.basicInfoContainer}`}>
<div className={`${styles.basicInfoText} ${styles.bigText}`}>
{cardVO.name}
</div>
<div className={`${styles.basicInfoText}`}>
{cardVO.description}
</div>
</div>
</div>
{/* URL和创建时间 */}
<div className={`${styles.urlAndUpdateText}`}>
{cardVO.version}
</div>
return (
<div className={`${styles.cardContainer}`}>
{/* icon和基本信息 */}
<div className={`${styles.iconBasicInfoContainer}`}>
{/* icon */}
<div className={`${styles.icon}`}>ICO</div>
{/* 基本信息 */}
<div className={`${styles.basicInfoContainer}`}>
<div className={`${styles.basicInfoText} ${styles.bigText}`}>
{cardVO.name}
</div>
<div className={`${styles.basicInfoText}`}>
{cardVO.description}
</div>
</div>
);
}
</div>
{/* URL和创建时间 */}
<div className={`${styles.urlAndUpdateText}`}>{cardVO.version}</div>
</div>
);
}
@@ -1,23 +1,23 @@
export interface IPipelineCardVO {
id: string;
name: string;
description: string;
createTime: string;
version: string;
id: string;
name: string;
description: string;
createTime: string;
version: string;
}
export class PipelineCardVO implements IPipelineCardVO {
createTime: string;
description: string;
id: string;
name: string;
version: string;
createTime: string;
description: string;
id: string;
name: string;
version: string;
constructor(props: IPipelineCardVO) {
this.id = props.id;
this.name = props.name;
this.description = props.description;
this.createTime = props.createTime;
this.version = props.version;
}
}
constructor(props: IPipelineCardVO) {
this.id = props.id;
this.name = props.name;
this.description = props.description;
this.createTime = props.createTime;
this.version = props.version;
}
}
@@ -1,4 +1,4 @@
import { DynamicFormItemConfig } from "@/app/home/components/dynamic-form/DynamicFormItemConfig";
import { DynamicFormItemConfig } from '@/app/home/components/dynamic-form/DynamicFormItemConfig';
export interface IPipelineChildFormEntity {
name: string;
@@ -5,15 +5,15 @@ import {
Select,
Input,
InputNumber,
SelectProps
} from "antd";
import { CaretLeftOutlined, CaretRightOutlined } from "@ant-design/icons";
import { useEffect, useState } from "react";
import styles from "./pipelineFormStyle.module.css";
import { httpClient } from "@/app/infra/http/HttpClient";
import { LLMModel, Pipeline } from "@/app/infra/api/api-types";
import { UUID } from "uuidjs";
import {PipelineFormEntity} from "@/app/home/pipelines/components/pipeline-form/PipelineFormEntity";
SelectProps,
} from 'antd';
import { CaretLeftOutlined, CaretRightOutlined } from '@ant-design/icons';
import { useEffect, useState } from 'react';
import styles from './pipelineFormStyle.module.css';
import { httpClient } from '@/app/infra/http/HttpClient';
import { LLMModel, Pipeline } from '@/app/infra/api/api-types';
import { UUID } from 'uuidjs';
import { PipelineFormEntity } from '@/app/home/pipelines/components/pipeline-form/PipelineFormEntity';
export default function PipelineFormComponent({
initValues,
@@ -30,15 +30,15 @@ export default function PipelineFormComponent({
onFinish: () => void;
}) {
const [nowFormIndex, setNowFormIndex] = useState<number>(0);
const [nowAIRunner, setNowAIRunner] = useState("");
const [llmModelList, setLlmModelList] = useState<SelectProps["options"]>([]);
const [nowAIRunner, setNowAIRunner] = useState('');
const [llmModelList, setLlmModelList] = useState<SelectProps['options']>([]);
// 这里不好,可以改成enum等
const formLabelList: FormLabel[] = [
{ label: "基础", name: "basic" },
{ label: "AI能力", name: "ai" },
{ label: "触发条件", name: "trigger" },
{ label: "安全能力", name: "safety" },
{ label: "输出处理", name: "output" }
{ label: '基础', name: 'basic' },
{ label: 'AI能力', name: 'ai' },
{ label: '触发条件', name: 'trigger' },
{ label: '安全能力', name: 'safety' },
{ label: '输出处理', name: 'output' },
];
const [basicForm] = Form.useForm();
const [aiForm] = Form.useForm();
@@ -51,7 +51,7 @@ export default function PipelineFormComponent({
}, []);
useEffect(() => {
console.log("initValues change: ", initValues);
console.log('initValues change: ', initValues);
if (initValues) {
basicForm.setFieldsValue(initValues.basic);
aiForm.setFieldsValue(initValues.ai);
@@ -59,8 +59,7 @@ export default function PipelineFormComponent({
safetyForm.setFieldsValue(initValues.safety);
outputForm.setFieldsValue(initValues.output);
}
}, [initValues]);
}, [aiForm, basicForm, initValues, outputForm, safetyForm, triggerForm]);
function getLLMModelList() {
httpClient
@@ -70,13 +69,13 @@ export default function PipelineFormComponent({
resp.models.map((model: LLMModel) => {
return {
value: model.uuid,
label: model.name
label: model.name,
};
})
}),
);
})
.catch((err) => {
console.error("get LLM model list error", err);
console.error('get LLM model list error', err);
});
}
@@ -114,9 +113,9 @@ export default function PipelineFormComponent({
function handleCommit() {
if (isEditMode) {
handleModify()
handleModify();
} else {
handleCreate()
handleCreate();
}
}
@@ -126,15 +125,15 @@ export default function PipelineFormComponent({
aiForm.validateFields(),
triggerForm.validateFields(),
safetyForm.validateFields(),
outputForm.validateFields()
outputForm.validateFields(),
])
.then(() => {
const pipeline = assembleForm();
httpClient.createPipeline(pipeline).then(() => onFinish());
})
.catch((e) => {
console.error(e);
});
.then(() => {
const pipeline = assembleForm();
httpClient.createPipeline(pipeline).then(() => onFinish());
})
.catch((e) => {
console.error(e);
});
}
function handleModify() {
@@ -143,61 +142,63 @@ export default function PipelineFormComponent({
aiForm.validateFields(),
triggerForm.validateFields(),
safetyForm.validateFields(),
outputForm.validateFields()
outputForm.validateFields(),
])
.then(() => {
const pipeline = assembleForm();
httpClient.updatePipeline(pipelineId || '', pipeline).then(() => onFinish());
})
.catch((e) => {
console.error(e);
});
.then(() => {
const pipeline = assembleForm();
httpClient
.updatePipeline(pipelineId || '', pipeline)
.then(() => onFinish());
})
.catch((e) => {
console.error(e);
});
}
// TODO 类型混乱,需要优化
function assembleForm(): Pipeline {
console.log("basicForm:", basicForm.getFieldsValue());
console.log("aiForm:", aiForm.getFieldsValue());
console.log("triggerForm:", triggerForm.getFieldsValue());
console.log("safetyForm:", safetyForm.getFieldsValue());
console.log("outputForm:", outputForm.getFieldsValue());
console.log('basicForm:', basicForm.getFieldsValue());
console.log('aiForm:', aiForm.getFieldsValue());
console.log('triggerForm:', triggerForm.getFieldsValue());
console.log('safetyForm:', safetyForm.getFieldsValue());
console.log('outputForm:', outputForm.getFieldsValue());
const config: object = {
ai: aiForm.getFieldsValue(),
trigger: triggerForm.getFieldsValue(),
safety: safetyForm.getFieldsValue(),
output: outputForm.getFieldsValue()
output: outputForm.getFieldsValue(),
};
return {
config,
created_at: "",
created_at: '',
description: basicForm.getFieldsValue().description,
for_version: "",
for_version: '',
name: basicForm.getFieldsValue().name,
stages: [],
updated_at: "",
uuid: UUID.generate()
updated_at: '',
uuid: UUID.generate(),
};
}
return (
<div style={{ maxHeight: "70vh", overflowY: "auto" }}>
<div style={{ maxHeight: '70vh', overflowY: 'auto' }}>
<h1>{getNowFormLabel().label}</h1>
<Form
layout={"vertical"}
layout={'vertical'}
style={{
display: getNowFormLabel().name === "basic" ? "block" : "none"
display: getNowFormLabel().name === 'basic' ? 'block' : 'none',
}}
form={basicForm}
disabled={disableForm}
>
<Form.Item
label="流水线名称"
name={"name"}
name={'name'}
rules={[
{
required: true
}
required: true,
},
]}
>
<Input />
@@ -205,11 +206,11 @@ export default function PipelineFormComponent({
<Form.Item
label="流水线描述"
name={"description"}
name={'description'}
rules={[
{
required: true
}
required: true,
},
]}
>
<Input />
@@ -217,8 +218,8 @@ export default function PipelineFormComponent({
</Form>
{/* AI能力表单 ai */}
<Form
layout={"vertical"}
style={{ display: getNowFormLabel().name === "ai" ? "block" : "none" }}
layout={'vertical'}
style={{ display: getNowFormLabel().name === 'ai' ? 'block' : 'none' }}
form={aiForm}
disabled={disableForm}
>
@@ -226,26 +227,26 @@ export default function PipelineFormComponent({
<div className={`${styles.formItemSubtitle}`}></div>
<Form.Item
label="运行器"
name={["runner", "runner"]}
name={['runner', 'runner']}
rules={[{ required: true }]}
>
<Select
options={[
{ label: "内置 Agent", value: "local-agent" },
{ label: "Dify 服务 API", value: "dify-service-api" },
{ label: "阿里云百炼平台 API", value: "dashscope-app-api" }
{ label: '内置 Agent', value: 'local-agent' },
{ label: 'Dify 服务 API', value: 'dify-service-api' },
{ label: '阿里云百炼平台 API', value: 'dashscope-app-api' },
]}
onChange={(value) => setNowAIRunner(value)}
/>
</Form.Item>
{/* 内置 Agent 配置区块 */}
{nowAIRunner === "local-agent" && (
{nowAIRunner === 'local-agent' && (
<>
<div className={`${styles.formItemSubtitle}`}>Agent</div>
<Form.Item
label="模型"
name={["local-agent", "model"]}
name={['local-agent', 'model']}
rules={[{ required: true }]}
tooltip="从模型库中选择"
>
@@ -257,11 +258,11 @@ export default function PipelineFormComponent({
</Form.Item>
<Form.Item
label="最大回合数"
name={["local-agent", "max-round"]}
name={['local-agent', 'max-round']}
rules={[
{
required: true
}
required: true,
},
]}
>
<InputNumber precision={0} />
@@ -269,7 +270,7 @@ export default function PipelineFormComponent({
{/* TODO 这里要做转换处理 */}
<Form.Item
label="提示词"
name={["local-agent", "prompt"]}
name={['local-agent', 'prompt']}
rules={[{ required: true }]}
tooltip="按JSON格式输入"
>
@@ -281,91 +282,91 @@ export default function PipelineFormComponent({
</>
)}
{/* Dify 服务 API 区块 */}
{nowAIRunner === "dify-service-api" && (
{nowAIRunner === 'dify-service-api' && (
<>
<div className={`${styles.formItemSubtitle}`}>Dify服务API</div>
<Form.Item
label="基础 URL"
name={["dify-service-api", "base-url"]}
name={['dify-service-api', 'base-url']}
rules={[
{ required: true },
{ type: "url", message: "请输入有效的URL地址" }
{ type: 'url', message: '请输入有效的URL地址' },
]}
>
<Input />
</Form.Item>
<Form.Item
label="应用类型"
name={["dify-service-api", "app-type"]}
initialValue={"chat"}
name={['dify-service-api', 'app-type']}
initialValue={'chat'}
rules={[{ required: true }]}
>
<Select
options={[
{ label: "聊天(包括Chatflow", value: "chat" },
{ label: "Agent", value: "agent" },
{ label: "工作流", value: "workflow" }
{ label: '聊天(包括Chatflow', value: 'chat' },
{ label: 'Agent', value: 'agent' },
{ label: '工作流', value: 'workflow' },
]}
/>
</Form.Item>
<Form.Item
label="API 密钥"
name={["dify-service-api", "api-key"]}
name={['dify-service-api', 'api-key']}
rules={[{ required: true }]}
>
<Input.Password visibilityToggle={false} />
</Form.Item>
<Form.Item
label="思维链转换"
name={["dify-service-api", "thinking-convert"]}
name={['dify-service-api', 'thinking-convert']}
rules={[{ required: true }]}
>
<Select
options={[
{ label: "转换成 \<think\>...\<\/think\>", value: "plain" },
{ label: "原始", value: "original" },
{ label: "移除", value: "remove" }
{ label: '转换成 \<think\>...\<\/think\>', value: 'plain' },
{ label: '原始', value: 'original' },
{ label: '移除', value: 'remove' },
]}
/>
</Form.Item>
</>
)}
{/* 阿里云百炼区块 */}
{nowAIRunner === "dashscope-app-api" && (
{nowAIRunner === 'dashscope-app-api' && (
<>
<div className={`${styles.formItemSubtitle}`}>
API
</div>
<Form.Item
label="应用类型"
name={["dashscope-app-api", "app-type"]}
name={['dashscope-app-api', 'app-type']}
rules={[{ required: true }]}
>
<Select
options={[
{ label: "Agent", value: "agent" },
{ label: "工作流", value: "workflow" }
{ label: 'Agent', value: 'agent' },
{ label: '工作流', value: 'workflow' },
]}
/>
</Form.Item>
<Form.Item
label="API 密钥"
name={["dashscope-app-api", "api-key"]}
name={['dashscope-app-api', 'api-key']}
rules={[{ required: true }]}
>
<Input.Password visibilityToggle={false} />
</Form.Item>
<Form.Item
label="应用 ID"
name={["dashscope-app-api", "app-id"]}
name={['dashscope-app-api', 'app-id']}
rules={[{ required: true }]}
>
<Input />
</Form.Item>
<Form.Item
label="引用文本"
name={["dashscope-app-api", "references_quote"]}
initialValue={"参考资料来自:"}
name={['dashscope-app-api', 'references_quote']}
initialValue={'参考资料来自:'}
>
<Input.TextArea rows={2} />
</Form.Item>
@@ -375,9 +376,9 @@ export default function PipelineFormComponent({
{/* 触发条件表单 trigger */}
<Form
layout={"vertical"}
layout={'vertical'}
style={{
display: getNowFormLabel().name === "trigger" ? "block" : "none"
display: getNowFormLabel().name === 'trigger' ? 'block' : 'none',
}}
form={triggerForm}
disabled={disableForm}
@@ -385,15 +386,15 @@ export default function PipelineFormComponent({
{/* 群响应规则块 */}
<div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item
label={"是否在消息@机器人时触发"}
name={["group-respond-rules", "at"]}
label={'是否在消息@机器人时触发'}
name={['group-respond-rules', 'at']}
rules={[{ required: true }]}
>
<Switch />
</Form.Item>
<Form.Item
label={"消息前缀"}
name={["group-respond-rules", "prefix"]}
label={'消息前缀'}
name={['group-respond-rules', 'prefix']}
rules={[{ required: true }]}
>
<Select
@@ -401,76 +402,76 @@ export default function PipelineFormComponent({
/>
</Form.Item>
<Form.Item
label={"正则表达式"}
name={["group-respond-rules", "regexp"]}
label={'正则表达式'}
name={['group-respond-rules', 'regexp']}
rules={[{ required: true }]}
>
<Select mode="tags" options={[]} />
</Form.Item>
<Form.Item
label={"随机"}
name={["group-respond-rules", "random"]}
label={'随机'}
name={['group-respond-rules', 'random']}
rules={[{ required: false }]}
>
<InputNumber max={1} min={0} step={0.05} />
</Form.Item>
<div className={`${styles.formItemSubtitle}`}> 访 </div>
<Form.Item
label={"模式"}
name={["access-control", "mode"]}
label={'模式'}
name={['access-control', 'mode']}
rules={[{ required: true }]}
tooltip={"访问控制模式"}
tooltip={'访问控制模式'}
>
<Select
options={[
{ label: "黑名单", value: "blacklist" },
{ label: "白名单", value: "Whitelist" }
{ label: '黑名单', value: 'blacklist' },
{ label: '白名单', value: 'Whitelist' },
]}
/>
</Form.Item>
<Form.Item
label={"黑名单"}
name={["access-control", "blacklist"]}
label={'黑名单'}
name={['access-control', 'blacklist']}
rules={[{ required: true }]}
>
<Select mode={"tags"} options={[]} />
<Select mode={'tags'} options={[]} />
</Form.Item>
<Form.Item
label={"白名单"}
name={["access-control", "whitelist"]}
label={'白名单'}
name={['access-control', 'whitelist']}
rules={[{ required: true }]}
>
<Select mode={"tags"} options={[]} />
<Select mode={'tags'} options={[]} />
</Form.Item>
<div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item
label={"前缀"}
name={["ignore-rules", "whitelist"]}
label={'前缀'}
name={['ignore-rules', 'whitelist']}
rules={[{ required: true }]}
tooltip={"消息前缀"}
tooltip={'消息前缀'}
>
<Select mode={"tags"} options={[]} />
<Select mode={'tags'} options={[]} />
</Form.Item>
<Form.Item
label={"正则表达式"}
name={["ignore-rules", "regexp"]}
label={'正则表达式'}
name={['ignore-rules', 'regexp']}
rules={[{ required: true }]}
tooltip={"消息正则表达式"}
tooltip={'消息正则表达式'}
>
<Select mode={"tags"} options={[]} />
<Select mode={'tags'} options={[]} />
</Form.Item>
</Form>
{/* 安全控制表单 safety */}
<Form
layout={"vertical"}
layout={'vertical'}
style={{
display: getNowFormLabel().name === "safety" ? "block" : "none"
display: getNowFormLabel().name === 'safety' ? 'block' : 'none',
}}
form={safetyForm}
disabled={disableForm}
@@ -478,22 +479,22 @@ export default function PipelineFormComponent({
{/* 内容过滤块 content-filter */}
<div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item
label={"检查范围"}
name={["content-filter", "scope"]}
label={'检查范围'}
name={['content-filter', 'scope']}
rules={[{ required: true }]}
>
<Select
options={[
{ label: "全部", value: "all" },
{ label: "传入消息(用户消息)", value: "income-msg" },
{ label: "传出消息(机器人消息)", value: "output-msg" }
{ label: '全部', value: 'all' },
{ label: '传入消息(用户消息)', value: 'income-msg' },
{ label: '传出消息(机器人消息)', value: 'output-msg' },
]}
/>
</Form.Item>
<Form.Item
label={"检查敏感词"}
name={["content-filter", "check-sensitive-words"]}
label={'检查敏感词'}
name={['content-filter', 'check-sensitive-words']}
rules={[{ required: true }]}
>
<Switch />
@@ -502,31 +503,31 @@ export default function PipelineFormComponent({
{/* 速率限制块 rate-limit */}
<div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item
label={"窗口长度(秒)"}
name={["rate-limit", "window-length"]}
label={'窗口长度(秒)'}
name={['rate-limit', 'window-length']}
rules={[{ required: true }]}
initialValue={60}
>
<InputNumber></InputNumber>
</Form.Item>
<Form.Item
label={"限制次数"}
name={["rate-limit", "limitation"]}
label={'限制次数'}
name={['rate-limit', 'limitation']}
rules={[{ required: true }]}
initialValue={60}
>
<InputNumber />
</Form.Item>
<Form.Item
label={"策略"}
name={["rate-limit", "strategy"]}
label={'策略'}
name={['rate-limit', 'strategy']}
rules={[{ required: true }]}
initialValue={"drop"}
initialValue={'drop'}
>
<Select
options={[
{ label: "丢弃", value: "drop" },
{ label: "等待", value: "wait" }
{ label: '丢弃', value: 'drop' },
{ label: '等待', value: 'wait' },
]}
/>
</Form.Item>
@@ -534,9 +535,9 @@ export default function PipelineFormComponent({
{/* 输出处理控制表单 output */}
<Form
layout={"vertical"}
layout={'vertical'}
style={{
display: getNowFormLabel().name === "output" ? "block" : "none"
display: getNowFormLabel().name === 'output' ? 'block' : 'none',
}}
form={outputForm}
disabled={disableForm}
@@ -545,26 +546,26 @@ export default function PipelineFormComponent({
<div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item
label="阈值"
name={["long-text-processing", "threshold"]}
name={['long-text-processing', 'threshold']}
rules={[{ required: true }]}
>
<InputNumber />
</Form.Item>
<Form.Item
label="策略"
name={["long-text-processing", "strategy"]}
name={['long-text-processing', 'strategy']}
rules={[{ required: true }]}
>
<Select
options={[
{ label: "转发消息组件", value: "forward" },
{ label: "转换为图片", value: "image" }
{ label: '转发消息组件', value: 'forward' },
{ label: '转换为图片', value: 'image' },
]}
/>
</Form.Item>
<Form.Item
label="字体路径"
name={["long-text-processing", "font-path"]}
name={['long-text-processing', 'font-path']}
rules={[{ required: true }]}
>
<Input />
@@ -574,14 +575,14 @@ export default function PipelineFormComponent({
<div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item
label="最小秒数"
name={["force-delay", "min"]}
name={['force-delay', 'min']}
rules={[{ required: true }]}
>
<InputNumber />
</Form.Item>
<Form.Item
label="最大秒数"
name={["force-delay", "max"]}
name={['force-delay', 'max']}
rules={[{ required: true }]}
>
<InputNumber />
@@ -591,7 +592,7 @@ export default function PipelineFormComponent({
<div className={`${styles.formItemSubtitle}`}> </div>
<Form.Item
label="不输出异常信息给用户"
name={["misc", "hide-exception"]}
name={['misc', 'hide-exception']}
rules={[{ required: true }]}
valuePropName="checked"
>
@@ -599,7 +600,7 @@ export default function PipelineFormComponent({
</Form.Item>
<Form.Item
label="在回复中@发送者"
name={["misc", "at-sender"]}
name={['misc', 'at-sender']}
rules={[{ required: true }]}
valuePropName="checked"
>
@@ -607,7 +608,7 @@ export default function PipelineFormComponent({
</Form.Item>
<Form.Item
label="引用原文"
name={["misc", "quote-origin"]}
name={['misc', 'quote-origin']}
rules={[{ required: true }]}
valuePropName="checked"
>
@@ -615,7 +616,7 @@ export default function PipelineFormComponent({
</Form.Item>
<Form.Item
label="跟踪函数调用"
name={["misc", "track-function-calls"]}
name={['misc', 'track-function-calls']}
rules={[{ required: true }]}
valuePropName="checked"
>
@@ -630,16 +631,16 @@ export default function PipelineFormComponent({
onClick={reduceFormLabelIndex}
disabled={!getPreFormLabel()}
>
{getPreFormLabel()?.label || "暂无更多"}
{getPreFormLabel()?.label || '暂无更多'}
</Button>
<Button
type="primary"
icon={<CaretRightOutlined />}
onClick={addFormLabelIndex}
disabled={!getNextFormLabel()}
iconPosition={"end"}
iconPosition={'end'}
>
{getNextFormLabel()?.label || "暂无更多"}
{getNextFormLabel()?.label || '暂无更多'}
</Button>
<Button type="primary" onClick={handleCommit}>
@@ -1,7 +1,7 @@
export interface PipelineFormEntity {
basic: object,
ai: object,
trigger: object,
safety: object,
output: object,
}
basic: object;
ai: object;
trigger: object;
safety: object;
output: object;
}
+34 -33
View File
@@ -1,26 +1,27 @@
"use client";
import { Modal } from "antd";
import { useState, useEffect } from "react";
import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent";
import PipelineFormComponent from "./components/pipeline-form/PipelineFormComponent";
import { httpClient } from "@/app/infra/http/HttpClient";
import { PipelineCardVO } from "@/app/home/pipelines/components/pipeline-card/PipelineCardVO";
import PipelineCardComponent from "@/app/home/pipelines/components/pipeline-card/PipelineCardComponent";
import {PipelineFormEntity} from "@/app/home/pipelines/components/pipeline-form/PipelineFormEntity";
'use client';
import { Modal } from 'antd';
import { useState, useEffect } from 'react';
import CreateCardComponent from '@/app/infra/basic-component/create-card-component/CreateCardComponent';
import PipelineFormComponent from './components/pipeline-form/PipelineFormComponent';
import { httpClient } from '@/app/infra/http/HttpClient';
import { PipelineCardVO } from '@/app/home/pipelines/components/pipeline-card/PipelineCardVO';
import PipelineCardComponent from '@/app/home/pipelines/components/pipeline-card/PipelineCardComponent';
import { PipelineFormEntity } from '@/app/home/pipelines/components/pipeline-form/PipelineFormEntity';
export default function PluginConfigPage() {
const [modalOpen, setModalOpen] = useState<boolean>(false);
const [isEditForm, setIsEditForm] = useState(false);
const [pipelineList, setPipelineList] = useState<PipelineCardVO[]>([]);
const [selectedPipelineId, setSelectedPipelineId] = useState("")
const [selectedPipelineFormValue, setSelectedPipelineFormValue] = useState<PipelineFormEntity>({
const [selectedPipelineId, setSelectedPipelineId] = useState('');
const [selectedPipelineFormValue, setSelectedPipelineFormValue] =
useState<PipelineFormEntity>({
basic: {},
ai: {},
trigger: {},
safety: {},
output: {},
})
const [disableForm, setDisableForm] = useState(false)
});
const [disableForm, setDisableForm] = useState(false);
useEffect(() => {
getPipelines();
@@ -36,7 +37,7 @@ export default function PluginConfigPage() {
description: pipeline.description,
id: pipeline.uuid,
name: pipeline.name,
version: pipeline.for_version
version: pipeline.for_version,
});
});
setPipelineList(pipelineList);
@@ -48,25 +49,25 @@ export default function PluginConfigPage() {
}
function getSelectedPipelineForm(id?: string) {
httpClient.getPipeline(id ?? selectedPipelineId).then((value) => {
setSelectedPipelineFormValue({
ai: value.pipeline.config.ai,
basic: {
description: value.pipeline.description,
name: value.pipeline.name,
},
output: value.pipeline.config.output,
safety: value.pipeline.config.safety,
trigger: value.pipeline.config.trigger,
})
setDisableForm(false)
})
httpClient.getPipeline(id ?? selectedPipelineId).then((value) => {
setSelectedPipelineFormValue({
ai: value.pipeline.config.ai,
basic: {
description: value.pipeline.description,
name: value.pipeline.name,
},
output: value.pipeline.config.output,
safety: value.pipeline.config.safety,
trigger: value.pipeline.config.trigger,
});
setDisableForm(false);
});
}
return (
<div className={``}>
<Modal
title={isEditForm ? "编辑流水线" : "创建流水线"}
title={isEditForm ? '编辑流水线' : '创建流水线'}
centered
open={modalOpen}
destroyOnClose={true}
@@ -94,11 +95,11 @@ export default function PluginConfigPage() {
<div
key={pipeline.id}
onClick={() => {
setDisableForm(true)
setIsEditForm(true);
setModalOpen(true);
setSelectedPipelineId(pipeline.id)
getSelectedPipelineForm(pipeline.id);
setDisableForm(true);
setIsEditForm(true);
setModalOpen(true);
setSelectedPipelineId(pipeline.id);
getSelectedPipelineForm(pipeline.id);
}}
>
<PipelineCardComponent cardVO={pipeline} />
+34 -30
View File
@@ -1,37 +1,41 @@
"use client"
'use client';
import { Radio } from 'antd';
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'
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';
export default function PluginConfigPage() {
enum PageType {
INSTALLED = "installed",
MARKET = 'market'
}
enum PageType {
INSTALLED = 'installed',
MARKET = 'market',
}
const [nowPageType, setNowPageType] = useState(PageType.INSTALLED)
const [nowPageType, setNowPageType] = useState(PageType.INSTALLED);
return (
<div className={styles.pageContainer}>
<Radio.Group
block
options={[
{ label: '已安装', value: PageType.INSTALLED },
{ label: '插件市场', value: PageType.MARKET },
]}
defaultValue={PageType.INSTALLED}
value={nowPageType}
optionType="button"
buttonStyle="solid"
style={{ marginBottom: '20px' }}
onChange={(e) => {
setNowPageType(e.target.value as PageType)
}}
/>
return (
<div className={styles.pageContainer}>
<Radio.Group
block
options={[
{ label: '已安装', value: PageType.INSTALLED },
{ label: '插件市场', value: PageType.MARKET },
]}
defaultValue={PageType.INSTALLED}
value={nowPageType}
optionType="button"
buttonStyle="solid"
style={{ marginBottom: '20px' }}
onChange={(e) => {
setNowPageType(e.target.value as PageType);
}}
/>
{nowPageType === PageType.INSTALLED ? <PluginInstalledComponent /> : <PluginMarketComponent />}
</div>
);
{nowPageType === PageType.INSTALLED ? (
<PluginInstalledComponent />
) : (
<PluginMarketComponent />
)}
</div>
);
}
@@ -1,27 +1,26 @@
export interface IPluginCardVO {
author: string,
version: string,
name: string,
description: string,
handlerCount: number,
isInitialized: boolean,
author: string;
version: string;
name: string;
description: string;
handlerCount: number;
isInitialized: boolean;
}
export class PluginCardVO implements IPluginCardVO {
description: string;
handlerCount: number;
name: string;
author: string;
version: string;
isInitialized: boolean;
constructor(prop: IPluginCardVO) {
this.description = prop.description
this.handlerCount = prop.handlerCount
this.name = prop.name
this.author = prop.author
this.version = prop.version
this.isInitialized = prop.isInitialized
}
description: string;
handlerCount: number;
name: string;
author: string;
version: string;
isInitialized: boolean;
constructor(prop: IPluginCardVO) {
this.description = prop.description;
this.handlerCount = prop.handlerCount;
this.name = prop.name;
this.author = prop.author;
this.version = prop.version;
this.isInitialized = prop.isInitialized;
}
}
@@ -1,18 +1,18 @@
"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 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";
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 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<PluginCardVO[]>([]);
const [modalOpen, setModalOpen] = useState(false);
const [githubURL, setGithubURL] = useState("");
const [githubURL, setGithubURL] = useState('');
useEffect(() => {
initData();
@@ -33,9 +33,9 @@ export default function PluginInstalledComponent() {
handlerCount: 0,
name: plugin.name,
version: plugin.version,
isInitialized: plugin.status === "initialized"
isInitialized: plugin.status === 'initialized',
});
})
}),
);
});
}
@@ -53,7 +53,7 @@ export default function PluginInstalledComponent() {
getPluginList();
})
.catch((err) => {
console.log("error when install plugin:", err);
console.log('error when install plugin:', err);
});
}
return (
@@ -63,8 +63,8 @@ export default function PluginInstalledComponent() {
<div className={`${styles.modalTitle}`}>
<GithubOutlined
style={{
fontSize: "30px",
marginRight: "20px"
fontSize: '30px',
marginRight: '20px',
}}
/>
</div>
@@ -1,12 +1,12 @@
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 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';
export default function PluginCardComponent({
cardVO
cardVO,
}: {
cardVO: PluginCardVO;
}) {
@@ -21,7 +21,7 @@ export default function PluginCardComponent({
setInitialized(!initialized);
})
.catch((err) => {
console.log("error: ", err);
console.log('error: ', err);
})
.finally(() => {
setSwitchEnable(true);
@@ -35,7 +35,7 @@ export default function PluginCardComponent({
<div className={`${styles.fontGray}`}>{cardVO.author}</div>
{/* right icon & version */}
<div className={`${styles.iconVersionContainer}`}>
<GithubOutlined style={{ fontSize: "26px" }} type="setting" />
<GithubOutlined style={{ fontSize: '26px' }} type="setting" />
<Tag color="#108ee9">v{cardVO.version}</Tag>
</div>
</div>
@@ -49,10 +49,10 @@ export default function PluginCardComponent({
<div className={`${styles.footerContainer}`}>
<div className={`${styles.linkAndToolContainer}`}>
<div className={`${styles.link}`}>
<LinkOutlined style={{ fontSize: "22px" }} />
<LinkOutlined style={{ fontSize: '22px' }} />
<span>1</span>
</div>
<ToolOutlined style={{ fontSize: "22px" }} />
<ToolOutlined style={{ fontSize: '22px' }} />
</div>
<div className={`${styles.switchContainer}`}>
<Switch
@@ -1,107 +1,122 @@
"use client";
'use client';
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 { spaceClient } from "@/app/infra/http/HttpClient";
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 { spaceClient } from '@/app/infra/http/HttpClient';
export default function PluginMarketComponent() {
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;
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(() => {
initData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
initData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
function initData() {
getPluginList();
}
function initData() {
getPluginList();
}
function onInputSearchKeyword(keyword: string) {
// 这里记得加防抖,暂时没加
setSearchKeyword(keyword);
setNowPage(1);
getPluginList(1, keyword);
}
function onInputSearchKeyword(keyword: string) {
// 这里记得加防抖,暂时没加
setSearchKeyword(keyword);
setNowPage(1);
getPluginList(1, keyword);
}
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 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) {
setNowPage(page);
getPluginList(page);
}
function handlePageChange(page: number) {
setNowPage(page);
getPluginList(page);
}
return (
<div className={`${styles.marketComponentBody}`}>
<Input
style={{
width: '300px',
marginBottom: '10px',
}}
value={searchKeyword}
placeholder="搜索插件"
onChange={(e) => onInputSearchKeyword(e.target.value)}
/>
<div className={`${styles.pluginListContainer}`}>
{loading ? (
<div style={{ textAlign: 'center', padding: '20px' }}>...</div>
) : marketPluginList.length === 0 ? (
<div style={{ textAlign: 'center', padding: '20px' }}></div>
) : (
marketPluginList.map((vo, index) => (
<div key={`${vo.pluginId}-${index}`}>
<PluginMarketCardComponent cardVO={vo} />
</div>
))
)}
return (
<div className={`${styles.marketComponentBody}`}>
<Input
style={{
width: '300px',
marginBottom: '10px',
}}
value={searchKeyword}
placeholder="搜索插件"
onChange={(e) => onInputSearchKeyword(e.target.value)}
/>
<div className={`${styles.pluginListContainer}`}>
{loading ? (
<div style={{ textAlign: 'center', padding: '20px' }}>...</div>
) : marketPluginList.length === 0 ? (
<div style={{ textAlign: 'center', padding: '20px' }}>
</div>
) : (
marketPluginList.map((vo, index) => (
<div key={`${vo.pluginId}-${index}`}>
<PluginMarketCardComponent cardVO={vo} />
</div>
{totalCount > 0 && (
<div style={{ display: 'flex', justifyContent: 'center', width: '100%', marginTop: '20px' }}>
<Pagination
current={nowPage}
total={totalCount}
pageSize={pageSize}
onChange={handlePageChange}
showSizeChanger={false}
/>
</div>
)}
))
)}
</div>
{totalCount > 0 && (
<div
style={{
display: 'flex',
justifyContent: 'center',
width: '100%',
marginTop: '20px',
}}
>
<Pagination
current={nowPage}
total={totalCount}
pageSize={pageSize}
onChange={handlePageChange}
showSizeChanger={false}
/>
</div>
)
)}
</div>
);
}
@@ -1,15 +1,15 @@
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;
}) {
function handleInstallClick(pluginId: string) {
console.log("Install plugin: ", pluginId);
console.log('Install plugin: ', pluginId);
}
return (
@@ -19,7 +19,7 @@ export default function PluginMarketCardComponent({
{/* left author */}
<div className={`${styles.fontGray}`}>{cardVO.author}</div>
{/* right icon */}
<GithubOutlined style={{ fontSize: "26px" }} type="setting" />
<GithubOutlined style={{ fontSize: '26px' }} type="setting" />
</div>
{/* content */}
<div className={`${styles.cardContent}`}>
@@ -30,13 +30,13 @@ export default function PluginMarketCardComponent({
<div className={`${styles.cardFooter}`}>
<div className={`${styles.linkSettingContainer}`}>
<div className={`${styles.link}`}>
<StarOutlined style={{ fontSize: "22px" }} />
<span style={{ paddingLeft: "5px" }}>{cardVO.starCount}</span>
<StarOutlined style={{ fontSize: '22px' }} />
<span style={{ paddingLeft: '5px' }}>{cardVO.starCount}</span>
</div>
</div>
<Button
type="primary"
size={"small"}
size={'small'}
onClick={() => {
handleInstallClick(cardVO.pluginId);
}}
@@ -1,29 +1,29 @@
export interface IPluginMarketCardVO {
pluginId: string;
author: string,
name: string,
description: string,
starCount: number,
githubURL: string,
version: string,
pluginId: string;
author: string;
name: string;
description: string;
starCount: number;
githubURL: string;
version: string;
}
export class PluginMarketCardVO implements IPluginMarketCardVO {
pluginId: string;
description: string;
name: string;
author: string;
githubURL: string;
starCount: number;
version: string;
pluginId: string;
description: string;
name: string;
author: string;
githubURL: string;
starCount: number;
version: string;
constructor(prop: IPluginMarketCardVO) {
this.description = prop.description
this.name = prop.name
this.author = prop.author
this.githubURL = prop.githubURL
this.starCount = prop.starCount
this.pluginId = prop.pluginId
this.version = prop.version
}
constructor(prop: IPluginMarketCardVO) {
this.description = prop.description;
this.name = prop.name;
this.author = prop.author;
this.githubURL = prop.githubURL;
this.starCount = prop.starCount;
this.pluginId = prop.pluginId;
this.version = prop.version;
}
}