mirror of
https://github.com/langbot-app/LangBot.git
synced 2025-11-26 03:44:58 +08:00
feat: fix eslint limits to build
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
export interface ICreateBotField {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,281 +1,292 @@
|
|||||||
import {BotFormEntity, IBotFormEntity} from "@/app/home/bots/components/bot-form/BotFormEntity";
|
|
||||||
import {Button, Form, Input, Select, Space} from "antd";
|
|
||||||
import {useEffect, useState} from "react";
|
|
||||||
import {IChooseAdapterEntity} from "@/app/home/bots/components/bot-form/ChooseAdapterEntity";
|
|
||||||
import {
|
import {
|
||||||
DynamicFormItemConfig,
|
BotFormEntity,
|
||||||
IDynamicFormItemConfig,
|
IBotFormEntity
|
||||||
parseDynamicFormItemType
|
} 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";
|
} from "@/app/home/components/dynamic-form/DynamicFormItemConfig";
|
||||||
import {UUID} from 'uuidjs'
|
import { UUID } from "uuidjs";
|
||||||
import DynamicFormComponent from "@/app/home/components/dynamic-form/DynamicFormComponent";
|
import DynamicFormComponent from "@/app/home/components/dynamic-form/DynamicFormComponent";
|
||||||
import {httpClient} from "@/app/infra/http/HttpClient";
|
import { httpClient } from "@/app/infra/http/HttpClient";
|
||||||
import { Bot } from "@/app/infra/api/api-types";
|
import { Bot } from "@/app/infra/api/api-types";
|
||||||
import { notification } from "antd";
|
|
||||||
|
|
||||||
|
|
||||||
export default function BotForm({
|
export default function BotForm({
|
||||||
initBotId,
|
initBotId,
|
||||||
onFormSubmit,
|
onFormSubmit,
|
||||||
onFormCancel,
|
onFormCancel
|
||||||
}: {
|
}: {
|
||||||
initBotId?: string;
|
initBotId?: string;
|
||||||
onFormSubmit: (value: IBotFormEntity) => void;
|
onFormSubmit: (value: IBotFormEntity) => void;
|
||||||
onFormCancel: (value: IBotFormEntity) => void;
|
onFormCancel: (value: IBotFormEntity) => void;
|
||||||
}) {
|
}) {
|
||||||
const [adapterNameToDynamicConfigMap, setAdapterNameToDynamicConfigMap] = useState(new Map<string, IDynamicFormItemConfig[]>())
|
const [adapterNameToDynamicConfigMap, setAdapterNameToDynamicConfigMap] =
|
||||||
const [form] = Form.useForm<IBotFormEntity>();
|
useState(new Map<string, IDynamicFormItemConfig[]>());
|
||||||
const [showDynamicForm, setShowDynamicForm] = useState<boolean>(false)
|
const [form] = Form.useForm<IBotFormEntity>();
|
||||||
const [dynamicForm] = Form.useForm();
|
const [showDynamicForm, setShowDynamicForm] = useState<boolean>(false);
|
||||||
const [adapterNameList, setAdapterNameList] = useState<IChooseAdapterEntity[]>([])
|
const [dynamicForm] = Form.useForm();
|
||||||
const [dynamicFormConfigList, setDynamicFormConfigList] = useState<IDynamicFormItemConfig[]>([])
|
const [adapterNameList, setAdapterNameList] = useState<
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false)
|
IChooseAdapterEntity[]
|
||||||
|
>([]);
|
||||||
|
const [dynamicFormConfigList, setDynamicFormConfigList] = useState<
|
||||||
|
IDynamicFormItemConfig[]
|
||||||
|
>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initBotFormComponent()
|
initBotFormComponent();
|
||||||
if (initBotId) {
|
if (initBotId) {
|
||||||
onEditMode()
|
onEditMode();
|
||||||
} else {
|
} else {
|
||||||
onCreateMode()
|
onCreateMode();
|
||||||
}
|
}
|
||||||
}, [])
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
async function initBotFormComponent() {
|
async function initBotFormComponent() {
|
||||||
// 拉取adapter
|
// 拉取adapter
|
||||||
const rawAdapterList = await httpClient.getAdapters()
|
const rawAdapterList = await httpClient.getAdapters();
|
||||||
// 初始化适配器选择列表
|
// 初始化适配器选择列表
|
||||||
setAdapterNameList(
|
setAdapterNameList(
|
||||||
rawAdapterList.adapters.map(item => {
|
rawAdapterList.adapters.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: item.label.zh_CN,
|
label: item.label.zh_CN,
|
||||||
value: item.name
|
value: item.name
|
||||||
}
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// 初始化适配器表单map
|
||||||
|
rawAdapterList.adapters.forEach((rawAdapter) => {
|
||||||
|
adapterNameToDynamicConfigMap.set(
|
||||||
|
rawAdapter.name,
|
||||||
|
rawAdapter.spec.config.map(
|
||||||
|
(item) =>
|
||||||
|
new DynamicFormItemConfig({
|
||||||
|
default: item.default,
|
||||||
|
id: UUID.generate(),
|
||||||
|
label: item.label,
|
||||||
|
name: item.name,
|
||||||
|
required: item.required,
|
||||||
|
type: parseDynamicFormItemType(item.type)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
// 初始化适配器表单map
|
);
|
||||||
rawAdapterList.adapters.forEach(rawAdapter => {
|
});
|
||||||
adapterNameToDynamicConfigMap.set(
|
// 拉取初始化表单信息
|
||||||
rawAdapter.name,
|
if (initBotId) {
|
||||||
rawAdapter.spec.config.map(item =>
|
getBotFieldById(initBotId).then((val) => {
|
||||||
new DynamicFormItemConfig({
|
form.setFieldsValue(val);
|
||||||
default: item.default,
|
handleAdapterSelect(val.adapter);
|
||||||
id: UUID.generate(),
|
dynamicForm.setFieldsValue(val.adapter_config);
|
||||||
label: item.label,
|
});
|
||||||
name: item.name,
|
} else {
|
||||||
required: item.required,
|
form.resetFields();
|
||||||
type: parseDynamicFormItemType(item.type)
|
}
|
||||||
})
|
setAdapterNameToDynamicConfigMap(adapterNameToDynamicConfigMap);
|
||||||
)
|
}
|
||||||
)
|
|
||||||
|
async function onCreateMode() {}
|
||||||
|
|
||||||
|
function onEditMode() {}
|
||||||
|
|
||||||
|
async function getBotFieldById(botId: string): Promise<IBotFormEntity> {
|
||||||
|
const bot = (await httpClient.getBot(botId)).bot;
|
||||||
|
return new BotFormEntity({
|
||||||
|
adapter: bot.adapter,
|
||||||
|
description: bot.description,
|
||||||
|
name: bot.name,
|
||||||
|
adapter_config: bot.adapter_config
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdapterSelect(adapterName: string) {
|
||||||
|
console.log("Select adapter: ", adapterName);
|
||||||
|
if (adapterName) {
|
||||||
|
const dynamicFormConfigList =
|
||||||
|
adapterNameToDynamicConfigMap.get(adapterName);
|
||||||
|
console.log(dynamicFormConfigList);
|
||||||
|
if (dynamicFormConfigList) {
|
||||||
|
setDynamicFormConfigList(dynamicFormConfigList);
|
||||||
|
}
|
||||||
|
setShowDynamicForm(true);
|
||||||
|
} else {
|
||||||
|
setShowDynamicForm(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmitButton() {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFormFinish() {
|
||||||
|
dynamicForm.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有通过外层固定表单验证才会走到这里,真正的提交逻辑在这里
|
||||||
|
function onDynamicFormSubmit(value: object) {
|
||||||
|
setIsLoading(true);
|
||||||
|
console.log("set loading", true);
|
||||||
|
if (initBotId) {
|
||||||
|
// 编辑提交
|
||||||
|
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
|
||||||
|
};
|
||||||
|
httpClient
|
||||||
|
.updateBot(initBotId, updateBot)
|
||||||
|
.then((res) => {
|
||||||
|
// TODO success toast
|
||||||
|
console.log("update bot success", res);
|
||||||
|
onFormSubmit(form.getFieldsValue());
|
||||||
|
notification.success({
|
||||||
|
message: "更新成功",
|
||||||
|
description: "机器人更新成功"
|
||||||
|
});
|
||||||
})
|
})
|
||||||
// 拉取初始化表单信息
|
.catch(() => {
|
||||||
if (initBotId) {
|
// TODO error toast
|
||||||
getBotFieldById(initBotId).then(val => {
|
notification.error({
|
||||||
form.setFieldsValue(val)
|
message: "更新失败",
|
||||||
handleAdapterSelect(val.adapter)
|
description: "机器人更新失败"
|
||||||
dynamicForm.setFieldsValue(val.adapter_config)
|
});
|
||||||
})
|
|
||||||
} else {
|
|
||||||
form.resetFields()
|
|
||||||
}
|
|
||||||
setAdapterNameToDynamicConfigMap(adapterNameToDynamicConfigMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onCreateMode() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEditMode() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getBotFieldById(botId: string): Promise<IBotFormEntity> {
|
|
||||||
const bot = (await httpClient.getBot(botId)).bot
|
|
||||||
let botFormEntity = new BotFormEntity({
|
|
||||||
adapter: bot.adapter,
|
|
||||||
description: bot.description,
|
|
||||||
name: bot.name,
|
|
||||||
adapter_config: bot.adapter_config
|
|
||||||
})
|
})
|
||||||
return botFormEntity
|
.finally(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
form.resetFields();
|
||||||
|
dynamicForm.resetFields();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 创建提交
|
||||||
|
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
|
||||||
|
};
|
||||||
|
httpClient
|
||||||
|
.createBot(newBot)
|
||||||
|
.then((res) => {
|
||||||
|
// TODO success toast
|
||||||
|
notification.success({
|
||||||
|
message: "创建成功",
|
||||||
|
description: "机器人创建成功"
|
||||||
|
});
|
||||||
|
console.log(res);
|
||||||
|
onFormSubmit(form.getFieldsValue());
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// TODO error toast
|
||||||
|
notification.error({
|
||||||
|
message: "创建失败",
|
||||||
|
description: "机器人创建失败"
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
form.resetFields();
|
||||||
|
dynamicForm.resetFields();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
setShowDynamicForm(false);
|
||||||
|
console.log("set loading", false);
|
||||||
|
// TODO 刷新bot列表
|
||||||
|
// TODO 关闭当前弹窗 Already closed @setShowDynamicForm(false)?
|
||||||
|
}
|
||||||
|
|
||||||
function handleAdapterSelect(adapterName: string) {
|
function handleSaveButton() {
|
||||||
console.log("Select adapter: ", adapterName)
|
form.submit();
|
||||||
if (adapterName) {
|
}
|
||||||
const dynamicFormConfigList = adapterNameToDynamicConfigMap.get(adapterName)
|
|
||||||
console.log(dynamicFormConfigList)
|
|
||||||
if (dynamicFormConfigList) {
|
|
||||||
setDynamicFormConfigList(dynamicFormConfigList)
|
|
||||||
}
|
|
||||||
setShowDynamicForm(true)
|
|
||||||
} else {
|
|
||||||
setShowDynamicForm(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSubmitButton() {
|
return (
|
||||||
form.submit()
|
<div>
|
||||||
}
|
<Form
|
||||||
|
form={form}
|
||||||
|
labelCol={{ span: 5 }}
|
||||||
|
wrapperCol={{ span: 18 }}
|
||||||
|
layout="vertical"
|
||||||
|
onFinish={handleFormFinish}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
<Form.Item<IBotFormEntity>
|
||||||
|
label={"机器人名称"}
|
||||||
|
name={"name"}
|
||||||
|
rules={[{ required: true, message: "该项为必填项哦~" }]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder="为机器人取个好听的名字吧~"
|
||||||
|
style={{ width: 260 }}
|
||||||
|
></Input>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
function handleFormFinish(value: IBotFormEntity) {
|
<Form.Item<IBotFormEntity>
|
||||||
dynamicForm.submit()
|
label={"描述"}
|
||||||
}
|
name={"description"}
|
||||||
|
rules={[{ required: true, message: "该项为必填项哦~" }]}
|
||||||
|
>
|
||||||
|
<Input placeholder="简单描述一下这个机器人"></Input>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
// 只有通过外层固定表单验证才会走到这里,真正的提交逻辑在这里
|
<Form.Item<IBotFormEntity>
|
||||||
function onDynamicFormSubmit(value: object) {
|
label={"平台/适配器选择"}
|
||||||
setIsLoading(true)
|
name={"adapter"}
|
||||||
console.log('setloading', true)
|
rules={[{ required: true, message: "该项为必填项哦~" }]}
|
||||||
if (initBotId) {
|
>
|
||||||
// 编辑提交
|
<Select
|
||||||
console.log('submit edit', form.getFieldsValue() ,value)
|
style={{ width: 220 }}
|
||||||
let updateBot: Bot = {
|
onChange={(value) => {
|
||||||
uuid: initBotId,
|
handleAdapterSelect(value);
|
||||||
name: form.getFieldsValue().name,
|
}}
|
||||||
description: form.getFieldsValue().description,
|
options={adapterNameList}
|
||||||
adapter: form.getFieldsValue().adapter,
|
/>
|
||||||
adapter_config: value
|
</Form.Item>
|
||||||
}
|
</Form>
|
||||||
httpClient.updateBot(initBotId, updateBot).then(res => {
|
{showDynamicForm && (
|
||||||
// TODO success toast
|
<DynamicFormComponent
|
||||||
console.log("update bot success", res)
|
form={dynamicForm}
|
||||||
onFormSubmit(form.getFieldsValue())
|
itemConfigList={dynamicFormConfigList}
|
||||||
notification.success({
|
onSubmit={onDynamicFormSubmit}
|
||||||
message: "更新成功",
|
/>
|
||||||
description: "机器人更新成功"
|
)}
|
||||||
})
|
<Space>
|
||||||
}).catch(err => {
|
{!initBotId && (
|
||||||
// TODO error toast
|
<Button
|
||||||
notification.error({
|
type="primary"
|
||||||
message: "更新失败",
|
htmlType="button"
|
||||||
description: "机器人更新失败"
|
onClick={handleSubmitButton}
|
||||||
})
|
loading={isLoading}
|
||||||
}).finally(() => {
|
>
|
||||||
setIsLoading(false)
|
提交
|
||||||
form.resetFields()
|
</Button>
|
||||||
dynamicForm.resetFields()
|
)}
|
||||||
})
|
{initBotId && (
|
||||||
} else {
|
<Button
|
||||||
// 创建提交
|
type="primary"
|
||||||
console.log('submit create', form.getFieldsValue() ,value)
|
htmlType="submit"
|
||||||
let newBot: Bot = {
|
onClick={handleSaveButton}
|
||||||
name: form.getFieldsValue().name,
|
loading={isLoading}
|
||||||
description: form.getFieldsValue().description,
|
>
|
||||||
adapter: form.getFieldsValue().adapter,
|
保存
|
||||||
adapter_config: value
|
</Button>
|
||||||
}
|
)}
|
||||||
httpClient.createBot(newBot).then(res => {
|
<Button
|
||||||
// TODO success toast
|
htmlType="button"
|
||||||
notification.success({
|
onClick={() => {
|
||||||
message: "创建成功",
|
onFormCancel(form.getFieldsValue());
|
||||||
description: "机器人创建成功"
|
}}
|
||||||
})
|
disabled={isLoading}
|
||||||
console.log(res)
|
>
|
||||||
onFormSubmit(form.getFieldsValue())
|
取消
|
||||||
}).catch(err => {
|
</Button>
|
||||||
// TODO error toast
|
</Space>
|
||||||
notification.error({
|
</div>
|
||||||
message: "创建失败",
|
);
|
||||||
description: "机器人创建失败"
|
}
|
||||||
})
|
|
||||||
}).finally(() => {
|
|
||||||
setIsLoading(false)
|
|
||||||
form.resetFields()
|
|
||||||
dynamicForm.resetFields()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
setShowDynamicForm(false)
|
|
||||||
console.log('setloading', false)
|
|
||||||
// TODO 刷新bot列表
|
|
||||||
// TODO 关闭当前弹窗 Already closed @setShowDynamicForm(false)?
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSaveButton() {
|
|
||||||
form.submit()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Form
|
|
||||||
form={form}
|
|
||||||
labelCol={{span: 5}}
|
|
||||||
wrapperCol={{span: 18}}
|
|
||||||
layout='vertical'
|
|
||||||
onFinish={handleFormFinish}
|
|
||||||
disabled={isLoading}
|
|
||||||
>
|
|
||||||
<Form.Item<IBotFormEntity>
|
|
||||||
label={"机器人名称"}
|
|
||||||
name={"name"}
|
|
||||||
rules={[{required: true, message: "该项为必填项哦~"}]}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
placeholder="为机器人取个好听的名字吧~"
|
|
||||||
style={{width: 260}}
|
|
||||||
></Input>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item<IBotFormEntity>
|
|
||||||
label={"描述"}
|
|
||||||
name={"description"}
|
|
||||||
rules={[{required: true, message: "该项为必填项哦~"}]}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
placeholder="简单描述一下这个机器人"
|
|
||||||
></Input>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item<IBotFormEntity>
|
|
||||||
label={"平台/适配器选择"}
|
|
||||||
name={"adapter"}
|
|
||||||
rules={[{required: true, message: "该项为必填项哦~"}]}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
style={{width: 220}}
|
|
||||||
onChange={(value) => {
|
|
||||||
handleAdapterSelect(value)
|
|
||||||
}}
|
|
||||||
options={adapterNameList}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
{
|
|
||||||
showDynamicForm &&
|
|
||||||
<DynamicFormComponent
|
|
||||||
form={dynamicForm}
|
|
||||||
itemConfigList={dynamicFormConfigList}
|
|
||||||
onSubmit={onDynamicFormSubmit}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
<Space>
|
|
||||||
{
|
|
||||||
!initBotId &&
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
htmlType="button"
|
|
||||||
onClick={handleSubmitButton}
|
|
||||||
loading={isLoading}
|
|
||||||
>
|
|
||||||
提交
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
initBotId &&
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
htmlType="submit"
|
|
||||||
onClick={handleSaveButton}
|
|
||||||
loading={isLoading}
|
|
||||||
>
|
|
||||||
保存
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
<Button htmlType="button" onClick={() => {
|
|
||||||
onFormCancel(form.getFieldsValue())
|
|
||||||
}} disabled={isLoading}>
|
|
||||||
取消
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,107 +1,100 @@
|
|||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import styles from "./HomeSidebar.module.css"
|
import styles from "./HomeSidebar.module.css";
|
||||||
import {useEffect, useState} from "react";
|
import { useEffect, useState } from "react";
|
||||||
import {SidebarChild, SidebarChildVO} from "@/app/home/components/home-sidebar/HomeSidebarChild";
|
import {
|
||||||
import {useRouter, usePathname, useSearchParams} from "next/navigation";
|
SidebarChild,
|
||||||
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 侧边导航栏要加动画
|
// TODO 侧边导航栏要加动画
|
||||||
export default function HomeSidebar({
|
export default function HomeSidebar({
|
||||||
onSelectedChange
|
onSelectedChangeAction
|
||||||
}: {
|
}: {
|
||||||
onSelectedChange: (sidebarChild: SidebarChildVO) => void
|
onSelectedChangeAction: (sidebarChild: SidebarChildVO) => void;
|
||||||
}) {
|
}) {
|
||||||
// 路由相关
|
// 路由相关
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const searchParams = useSearchParams();
|
// 路由被动变化时处理
|
||||||
// 路由被动变化时处理
|
useEffect(() => {
|
||||||
useEffect(() => {
|
handleRouteChange(pathname);
|
||||||
handleRouteChange(pathname)
|
}, [pathname]);
|
||||||
}, [pathname, searchParams]);
|
|
||||||
|
|
||||||
const [selectedChild, setSelectedChild] = useState<SidebarChildVO>(sidebarConfigList[0])
|
const [selectedChild, setSelectedChild] = useState<SidebarChildVO>(
|
||||||
|
sidebarConfigList[0]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('HomeSidebar挂载完成');
|
console.log("HomeSidebar挂载完成");
|
||||||
initSelect()
|
initSelect();
|
||||||
return () => console.log('HomeSidebar卸载');
|
return () => console.log("HomeSidebar卸载");
|
||||||
}, []);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
function handleChildClick(child: SidebarChildVO) {
|
||||||
|
setSelectedChild(child);
|
||||||
|
handleRoute(child);
|
||||||
|
onSelectedChangeAction(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSelect() {
|
||||||
|
handleChildClick(sidebarConfigList[0]);
|
||||||
|
}
|
||||||
|
|
||||||
function handleChildClick(child: SidebarChildVO) {
|
function handleRoute(child: SidebarChildVO) {
|
||||||
setSelectedChild(child)
|
console.log(child);
|
||||||
handleRoute(child)
|
router.push(`${child.route}`);
|
||||||
onSelectedChange(child)
|
}
|
||||||
|
|
||||||
|
function handleRouteChange(pathname: string) {
|
||||||
|
// TODO 这段逻辑并不好,未来router封装好后改掉
|
||||||
|
// 判断在home下,并且路由更改的是自己的路由子组件则更新UI
|
||||||
|
const routeList = pathname.split("/");
|
||||||
|
if (
|
||||||
|
routeList[1] === "home" &&
|
||||||
|
sidebarConfigList.find((childConfig) => childConfig.route === pathname)
|
||||||
|
) {
|
||||||
|
console.log("find success");
|
||||||
|
const routeSelectChild = sidebarConfigList.find(
|
||||||
|
(childConfig) => childConfig.route === pathname
|
||||||
|
);
|
||||||
|
if (routeSelectChild) {
|
||||||
|
setSelectedChild(routeSelectChild);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function initSelect() {
|
return (
|
||||||
handleChildClick(sidebarConfigList[0])
|
<div className={`${styles.sidebarContainer}`}>
|
||||||
}
|
{/* LangBot、ICON区域 */}
|
||||||
|
<div className={`${styles.langbotIconContainer}`}>
|
||||||
function handleRoute(child: SidebarChildVO) {
|
{/* icon */}
|
||||||
console.log(child)
|
<div className={`${styles.langbotIcon}`}>L</div>
|
||||||
router.push(`${child.route}`)
|
<div className={`${styles.langbotText}`}>Langbot</div>
|
||||||
}
|
</div>
|
||||||
|
{/* 菜单列表,后期可升级成配置驱动 */}
|
||||||
function handleRouteChange(pathname: string) {
|
<div>
|
||||||
// TODO 这段逻辑并不好,未来router封装好后改掉
|
{sidebarConfigList.map((config) => {
|
||||||
// 判断在home下,并且路由更改的是自己的路由子组件则更新UI
|
return (
|
||||||
const routeList = pathname.split('/')
|
<div
|
||||||
if (
|
key={config.id}
|
||||||
routeList[1] === "home" &&
|
onClick={() => {
|
||||||
sidebarConfigList.find(childConfig =>
|
console.log("click:", config.id);
|
||||||
childConfig.route === pathname
|
handleChildClick(config);
|
||||||
)
|
}}
|
||||||
) {
|
>
|
||||||
console.log("find success")
|
<SidebarChild
|
||||||
const routeSelectChild = sidebarConfigList.find(childConfig =>
|
isSelected={selectedChild.id === config.id}
|
||||||
childConfig.route === pathname
|
icon={config.icon}
|
||||||
)
|
name={config.name}
|
||||||
if (routeSelectChild) {
|
/>
|
||||||
setSelectedChild(routeSelectChild)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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>
|
||||||
{/* 菜单列表,后期可升级成配置驱动 */}
|
);
|
||||||
<div>
|
})}
|
||||||
{
|
</div>
|
||||||
sidebarConfigList.map(config => {
|
</div>
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,41 +1,44 @@
|
|||||||
import styles from "./HomeSidebar.module.css";
|
import styles from "./HomeSidebar.module.css";
|
||||||
|
|
||||||
export interface ISidebarChildVO {
|
export interface ISidebarChildVO {
|
||||||
id: string;
|
id: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
name: string;
|
name: string;
|
||||||
route: string;
|
route: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SidebarChildVO {
|
export class SidebarChildVO {
|
||||||
id: string;
|
id: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
name: string;
|
name: string;
|
||||||
route: string;
|
route: string;
|
||||||
|
|
||||||
constructor(props: ISidebarChildVO) {
|
constructor(props: ISidebarChildVO) {
|
||||||
this.id = props.id;
|
this.id = props.id;
|
||||||
this.icon = props.icon;
|
this.icon = props.icon;
|
||||||
this.name = props.name;
|
this.name = props.name;
|
||||||
this.route = props.route;
|
this.route = props.route;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function SidebarChild({
|
export function SidebarChild({
|
||||||
icon,
|
icon,
|
||||||
name,
|
name,
|
||||||
isSelected,
|
isSelected
|
||||||
}: {
|
}: {
|
||||||
icon: string;
|
icon: string;
|
||||||
name: string;
|
name: string;
|
||||||
isSelected: boolean;
|
isSelected: boolean;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.sidebarChildContainer} ${isSelected ? styles.sidebarSelected : styles.sidebarUnselected}`}>
|
<div
|
||||||
<div className={`${styles.sidebarChildIcon}`}/>
|
className={`${styles.sidebarChildContainer} ${isSelected ? styles.sidebarSelected : styles.sidebarUnselected}`}
|
||||||
<div>{name}</div>
|
>
|
||||||
</div>
|
<div className={`${styles.sidebarChildIcon}`} />
|
||||||
);
|
<div>
|
||||||
}
|
{icon}
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,36 +1,30 @@
|
|||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import '@ant-design/v5-patch-for-react-19';
|
import "@ant-design/v5-patch-for-react-19";
|
||||||
import styles from "./layout.module.css"
|
import styles from "./layout.module.css";
|
||||||
import HomeSidebar from "@/app/home/components/home-sidebar/HomeSidebar";
|
import HomeSidebar from "@/app/home/components/home-sidebar/HomeSidebar";
|
||||||
import HomeTitleBar from "@/app/home/components/home-titlebar/HomeTitleBar";
|
import HomeTitleBar from "@/app/home/components/home-titlebar/HomeTitleBar";
|
||||||
import React, {useState} from "react";
|
import React, { useState } from "react";
|
||||||
import {SidebarChildVO} from "@/app/home/components/home-sidebar/HomeSidebarChild";
|
import { SidebarChildVO } from "@/app/home/components/home-sidebar/HomeSidebarChild";
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
|
|
||||||
export default function HomeLayout({
|
export default function HomeLayout({
|
||||||
children
|
children
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
const router = useRouter();
|
const [title, setTitle] = useState<string>("");
|
||||||
const [title, setTitle] = useState<string>("")
|
const onSelectedChange = (child: SidebarChildVO) => {
|
||||||
const onSelectedChange = (child: SidebarChildVO) => {
|
setTitle(child.name);
|
||||||
setTitle(child.name)
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.homeLayoutContainer}`}>
|
<div className={`${styles.homeLayoutContainer}`}>
|
||||||
<HomeSidebar
|
<HomeSidebar onSelectedChangeAction={onSelectedChange} />
|
||||||
onSelectedChange={onSelectedChange}
|
<div className={`${styles.main}`}>
|
||||||
/>
|
<HomeTitleBar title={title} />
|
||||||
<div className={`${styles.main}`}>
|
{/* 主页面 */}
|
||||||
<HomeTitleBar title={title}/>
|
<div className={`${styles.mainContent}`}>{children}</div>
|
||||||
{/* 主页面 */}
|
</div>
|
||||||
<div className={`${styles.mainContent}`}>
|
</div>
|
||||||
{children}
|
);
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,296 +1,287 @@
|
|||||||
import styles from "@/app/home/models/LLMConfig.module.css";
|
import styles from "@/app/home/models/LLMConfig.module.css";
|
||||||
import {Button, Form, Input, Select, SelectProps, Space, Modal} from "antd";
|
import { Button, Form, Input, Select, SelectProps, Space, Modal } from "antd";
|
||||||
import {ICreateLLMField} from "@/app/home/models/ICreateLLMField";
|
import { ICreateLLMField } from "@/app/home/models/ICreateLLMField";
|
||||||
import {useEffect, useState} from "react";
|
import { useEffect, useState } from "react";
|
||||||
import {IChooseRequesterEntity} from "@/app/home/models/component/llm-form/ChooseAdapterEntity";
|
import { IChooseRequesterEntity } from "@/app/home/models/component/llm-form/ChooseAdapterEntity";
|
||||||
import { httpClient } from "@/app/infra/http/HttpClient";
|
import { httpClient } from "@/app/infra/http/HttpClient";
|
||||||
import {LLMModel} from "@/app/infra/api/api-types";
|
import { LLMModel } from "@/app/infra/api/api-types";
|
||||||
import {UUID} from "uuidjs";
|
import { UUID } from "uuidjs";
|
||||||
|
|
||||||
export default function LLMForm({
|
export default function LLMForm({
|
||||||
editMode,
|
editMode,
|
||||||
initLLMId,
|
initLLMId,
|
||||||
onFormSubmit,
|
onFormSubmit,
|
||||||
onFormCancel,
|
onFormCancel,
|
||||||
onLLMDeleted,
|
onLLMDeleted
|
||||||
}: {
|
}: {
|
||||||
editMode: boolean;
|
editMode: boolean;
|
||||||
initLLMId?: string;
|
initLLMId?: string;
|
||||||
onFormSubmit: (value: ICreateLLMField) => void;
|
onFormSubmit: (value: ICreateLLMField) => void;
|
||||||
onFormCancel: (value: ICreateLLMField) => void;
|
onFormCancel: (value: ICreateLLMField) => void;
|
||||||
onLLMDeleted: () => void;
|
onLLMDeleted: () => void;
|
||||||
}) {
|
}) {
|
||||||
const [form] = Form.useForm<ICreateLLMField>();
|
const [form] = Form.useForm<ICreateLLMField>();
|
||||||
const extraOptions: SelectProps['options'] = []
|
const extraOptions: SelectProps["options"] = [];
|
||||||
const [initValue, setInitValue] = useState<ICreateLLMField>()
|
const [initValue] = useState<ICreateLLMField>();
|
||||||
const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false)
|
const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false);
|
||||||
const abilityOptions: SelectProps['options'] = [
|
const abilityOptions: SelectProps["options"] = [
|
||||||
{
|
{
|
||||||
label: '函数调用',
|
label: "函数调用",
|
||||||
value: 'func_call',
|
value: "func_call"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '图像识别',
|
label: "图像识别",
|
||||||
value: 'vision',
|
value: "vision"
|
||||||
},
|
|
||||||
];
|
|
||||||
const [requesterNameList, setRequesterNameList] = useState<IChooseRequesterEntity[]>([])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
initLLMModelFormComponent()
|
|
||||||
if (editMode && initLLMId) {
|
|
||||||
getLLMConfig(initLLMId).then(val => {
|
|
||||||
form.setFieldsValue(val)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
form.resetFields()
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
async function initLLMModelFormComponent() {
|
|
||||||
const requesterNameList = await httpClient.getProviderRequesters()
|
|
||||||
setRequesterNameList(requesterNameList.requesters.map(item => {
|
|
||||||
return {
|
|
||||||
label: item.label.zh_CN,
|
|
||||||
value: item.name
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
];
|
||||||
|
const [requesterNameList, setRequesterNameList] = useState<
|
||||||
|
IChooseRequesterEntity[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
async function getLLMConfig(id: string): Promise<ICreateLLMField> {
|
useEffect(() => {
|
||||||
|
initLLMModelFormComponent();
|
||||||
|
if (editMode && initLLMId) {
|
||||||
|
getLLMConfig(initLLMId).then((val) => {
|
||||||
|
form.setFieldsValue(val);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
form.resetFields();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
const llmModel = await httpClient.getProviderLLMModel(id)
|
async function initLLMModelFormComponent() {
|
||||||
|
const requesterNameList = await httpClient.getProviderRequesters();
|
||||||
let fakeExtraArgs = []
|
setRequesterNameList(
|
||||||
const extraArgs = llmModel.model.extra_args as Record<string, string>
|
requesterNameList.requesters.map((item) => {
|
||||||
for (const key in extraArgs) {
|
|
||||||
fakeExtraArgs.push(`${key}:${extraArgs[key]}`)
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
name: llmModel.model.name,
|
label: item.label.zh_CN,
|
||||||
model_provider: llmModel.model.requester,
|
value: item.name
|
||||||
url: llmModel.model.requester_config?.base_url,
|
|
||||||
api_key: llmModel.model.api_keys[0],
|
|
||||||
abilities: llmModel.model.abilities,
|
|
||||||
extra_args: fakeExtraArgs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFormSubmit(value: ICreateLLMField) {
|
|
||||||
if (editMode) {
|
|
||||||
// 暂不支持更改模型
|
|
||||||
// onSaveEdit(value)
|
|
||||||
} else {
|
|
||||||
onCreateLLM(value)
|
|
||||||
}
|
|
||||||
form.resetFields()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSaveEdit(value: ICreateLLMField) {
|
|
||||||
const requestParam: LLMModel = {
|
|
||||||
uuid: UUID.generate(),
|
|
||||||
name: value.name,
|
|
||||||
description: "",
|
|
||||||
requester: value.model_provider,
|
|
||||||
requester_config: {
|
|
||||||
"base_url": value.url,
|
|
||||||
"timeout": 120
|
|
||||||
},
|
|
||||||
extra_args: value.extra_args,
|
|
||||||
api_keys: [value.api_key],
|
|
||||||
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',
|
|
||||||
};
|
};
|
||||||
httpClient.createProviderLLMModel(requestParam).then(r => console.log(r))
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getLLMConfig(id: string): Promise<ICreateLLMField> {
|
||||||
|
const llmModel = await httpClient.getProviderLLMModel(id);
|
||||||
|
|
||||||
|
const fakeExtraArgs = [];
|
||||||
|
const extraArgs = llmModel.model.extra_args as Record<string, string>;
|
||||||
|
for (const key in extraArgs) {
|
||||||
|
fakeExtraArgs.push(`${key}:${extraArgs[key]}`);
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
|
name: llmModel.model.name,
|
||||||
|
model_provider: llmModel.model.requester,
|
||||||
|
url: llmModel.model.requester_config?.base_url,
|
||||||
|
api_key: llmModel.model.api_keys[0],
|
||||||
|
abilities: llmModel.model.abilities,
|
||||||
|
extra_args: fakeExtraArgs
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function onCreateLLM(value: ICreateLLMField) {
|
function handleFormSubmit(value: ICreateLLMField) {
|
||||||
console.log("create llm", value)
|
if (editMode) {
|
||||||
const requestParam: LLMModel = {
|
// 暂不支持更改模型
|
||||||
uuid: UUID.generate(),
|
// onSaveEdit(value)
|
||||||
name: value.name,
|
} else {
|
||||||
description: "",
|
onCreateLLM(value);
|
||||||
requester: value.model_provider,
|
|
||||||
requester_config: {
|
|
||||||
"base_url": value.url,
|
|
||||||
"timeout": 120
|
|
||||||
},
|
|
||||||
extra_args: value.extra_args,
|
|
||||||
api_keys: [value.api_key],
|
|
||||||
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',
|
|
||||||
};
|
|
||||||
httpClient.createProviderLLMModel(requestParam).then(r => {
|
|
||||||
onFormSubmit(value)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
form.resetFields();
|
||||||
|
}
|
||||||
|
|
||||||
function handleAbilitiesChange() {
|
// function onSaveEdit(value: ICreateLLMField) {
|
||||||
|
// const requestParam: LLMModel = {
|
||||||
|
// uuid: UUID.generate(),
|
||||||
|
// name: value.name,
|
||||||
|
// description: "",
|
||||||
|
// requester: value.model_provider,
|
||||||
|
// requester_config: {
|
||||||
|
// "base_url": value.url,
|
||||||
|
// "timeout": 120
|
||||||
|
// },
|
||||||
|
// extra_args: value.extra_args,
|
||||||
|
// api_keys: [value.api_key],
|
||||||
|
// 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',
|
||||||
|
// };
|
||||||
|
// httpClient.createProviderLLMModel(requestParam).then(r => console.log(r))
|
||||||
|
// }
|
||||||
|
|
||||||
|
function onCreateLLM(value: ICreateLLMField) {
|
||||||
|
console.log("create llm", value);
|
||||||
|
const requestParam: LLMModel = {
|
||||||
|
uuid: UUID.generate(),
|
||||||
|
name: value.name,
|
||||||
|
description: "",
|
||||||
|
requester: value.model_provider,
|
||||||
|
requester_config: {
|
||||||
|
base_url: value.url,
|
||||||
|
timeout: 120
|
||||||
|
},
|
||||||
|
extra_args: value.extra_args,
|
||||||
|
api_keys: [value.api_key],
|
||||||
|
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',
|
||||||
|
};
|
||||||
|
httpClient.createProviderLLMModel(requestParam).then(() => {
|
||||||
|
onFormSubmit(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAbilitiesChange() {}
|
||||||
|
|
||||||
|
function deleteModel() {
|
||||||
|
if (initLLMId) {
|
||||||
|
httpClient.deleteProviderLLMModel(initLLMId).then(() => {
|
||||||
|
onLLMDeleted();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function deleteModel() {
|
return (
|
||||||
if (initLLMId) {
|
<div className={styles.modalContainer}>
|
||||||
httpClient.deleteProviderLLMModel(initLLMId).then(res => {
|
<Modal
|
||||||
onLLMDeleted()
|
open={showDeleteConfirmModal}
|
||||||
})
|
title={"删除确认"}
|
||||||
}
|
onCancel={() => setShowDeleteConfirmModal(false)}
|
||||||
}
|
footer={
|
||||||
|
<div
|
||||||
return (
|
style={{
|
||||||
<div className={styles.modalContainer}>
|
width: "170px",
|
||||||
<Modal
|
display: "flex",
|
||||||
open={showDeleteConfirmModal}
|
flexDirection: "row",
|
||||||
title={"删除确认"}
|
justifyContent: "space-between"
|
||||||
onCancel={() => setShowDeleteConfirmModal(false)}
|
}}
|
||||||
footer={
|
>
|
||||||
<div
|
<Button
|
||||||
style={{
|
danger
|
||||||
width: "170px",
|
onClick={() => {
|
||||||
display: "flex",
|
deleteModel();
|
||||||
flexDirection: "row",
|
setShowDeleteConfirmModal(false);
|
||||||
justifyContent: "space-between"
|
}}
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
danger
|
|
||||||
onClick={() => {
|
|
||||||
deleteModel()
|
|
||||||
setShowDeleteConfirmModal(false)
|
|
||||||
}}
|
|
||||||
>确认删除</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setShowDeleteConfirmModal(false)
|
|
||||||
}}
|
|
||||||
>取消</Button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
>
|
>
|
||||||
你确定要删除这个模型吗?
|
确认删除
|
||||||
</Modal>
|
</Button>
|
||||||
<Form
|
<Button
|
||||||
form={form}
|
onClick={() => {
|
||||||
labelCol={{span: 4}}
|
setShowDeleteConfirmModal(false);
|
||||||
wrapperCol={{span: 14}}
|
}}
|
||||||
layout='horizontal'
|
>
|
||||||
initialValues={{
|
取消
|
||||||
...initValue
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
你确定要删除这个模型吗?
|
||||||
|
</Modal>
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
labelCol={{ span: 4 }}
|
||||||
|
wrapperCol={{ span: 14 }}
|
||||||
|
layout="horizontal"
|
||||||
|
initialValues={{
|
||||||
|
...initValue
|
||||||
|
}}
|
||||||
|
onFinish={handleFormSubmit}
|
||||||
|
clearOnDestroy={true}
|
||||||
|
disabled={editMode}
|
||||||
|
>
|
||||||
|
<Form.Item<ICreateLLMField>
|
||||||
|
label={"模型名称"}
|
||||||
|
name={"name"}
|
||||||
|
rules={[{ required: true, message: "该项为必填项哦~" }]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder={"为自己的大模型取个好听的名字~"}
|
||||||
|
style={{ width: 260 }}
|
||||||
|
></Input>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item<ICreateLLMField>
|
||||||
|
label={"模型供应商"}
|
||||||
|
name={"model_provider"}
|
||||||
|
rules={[{ required: true, message: "该项为必填项哦~" }]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
style={{ width: 120 }}
|
||||||
|
onChange={() => {}}
|
||||||
|
options={requesterNameList}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item<ICreateLLMField>
|
||||||
|
label={"请求URL"}
|
||||||
|
name={"url"}
|
||||||
|
rules={[{ required: true, message: "该项为必填项哦~" }]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder="请求地址,一般是API提供商提供的URL"
|
||||||
|
style={{ width: 500 }}
|
||||||
|
></Input>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item<ICreateLLMField>
|
||||||
|
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"}>
|
||||||
|
<Select
|
||||||
|
mode="tags"
|
||||||
|
style={{ width: 500 }}
|
||||||
|
placeholder="选择模型能力,输入回车可自定义能力"
|
||||||
|
onChange={handleAbilitiesChange}
|
||||||
|
options={abilityOptions}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item<ICreateLLMField> label={"其他参数"} name={"extra_args"}>
|
||||||
|
<Select
|
||||||
|
mode="tags"
|
||||||
|
style={{ width: 500 }}
|
||||||
|
placeholder="输入后回车可自定义其他参数,例 key:value"
|
||||||
|
onChange={handleAbilitiesChange}
|
||||||
|
options={extraOptions}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item wrapperCol={{ offset: 4, span: 14 }}>
|
||||||
|
<Space>
|
||||||
|
{!editMode && (
|
||||||
|
<Button type="primary" htmlType="submit">
|
||||||
|
提交
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{editMode && (
|
||||||
|
<Button
|
||||||
|
color="danger"
|
||||||
|
variant="solid"
|
||||||
|
onClick={() => {
|
||||||
|
setShowDeleteConfirmModal(true);
|
||||||
}}
|
}}
|
||||||
onFinish={handleFormSubmit}
|
disabled={false}
|
||||||
clearOnDestroy={true}
|
>
|
||||||
disabled={editMode}
|
删除
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
htmlType="button"
|
||||||
|
onClick={() => {
|
||||||
|
onFormCancel(form.getFieldsValue());
|
||||||
|
}}
|
||||||
|
disabled={false}
|
||||||
>
|
>
|
||||||
<Form.Item<ICreateLLMField>
|
取消
|
||||||
label={"模型名称"}
|
</Button>
|
||||||
name={"name"}
|
</Space>
|
||||||
rules={[{required: true, message: "该项为必填项哦~"}]}
|
</Form.Item>
|
||||||
>
|
</Form>
|
||||||
<Input
|
</div>
|
||||||
placeholder={"为自己的大模型取个好听的名字~"}
|
);
|
||||||
style={{width: 260}}
|
}
|
||||||
></Input>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item<ICreateLLMField>
|
|
||||||
label={"模型供应商"}
|
|
||||||
name={"model_provider"}
|
|
||||||
rules={[{required: true, message: "该项为必填项哦~"}]}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
style={{width: 120}}
|
|
||||||
onChange={() => {
|
|
||||||
}}
|
|
||||||
options={requesterNameList}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item<ICreateLLMField>
|
|
||||||
label={"请求URL"}
|
|
||||||
name={"url"}
|
|
||||||
rules={[{required: true, message: "该项为必填项哦~"}]}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
placeholder="请求地址,一般是API提供商提供的URL"
|
|
||||||
style={{width: 500}}
|
|
||||||
></Input>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item<ICreateLLMField>
|
|
||||||
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"}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
mode="tags"
|
|
||||||
style={{width: 500}}
|
|
||||||
placeholder="选择模型能力,输入回车可自定义能力"
|
|
||||||
onChange={handleAbilitiesChange}
|
|
||||||
options={abilityOptions}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item<ICreateLLMField>
|
|
||||||
label={"其他参数"}
|
|
||||||
name={"extra_args"}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
mode="tags"
|
|
||||||
style={{width: 500}}
|
|
||||||
placeholder="输入后回车可自定义其他参数,例 key:value"
|
|
||||||
onChange={handleAbilitiesChange}
|
|
||||||
options={extraOptions}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
wrapperCol={{offset: 4, span: 14}}
|
|
||||||
>
|
|
||||||
<Space>
|
|
||||||
{
|
|
||||||
!editMode &&
|
|
||||||
<Button type="primary" htmlType="submit">
|
|
||||||
提交
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
editMode &&
|
|
||||||
<Button
|
|
||||||
color="danger"
|
|
||||||
variant="solid"
|
|
||||||
onClick={() => {setShowDeleteConfirmModal(true)}}
|
|
||||||
disabled={false}
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
<Button
|
|
||||||
htmlType="button"
|
|
||||||
onClick={() => {
|
|
||||||
onFormCancel(form.getFieldsValue())
|
|
||||||
}}
|
|
||||||
disabled={false}
|
|
||||||
>
|
|
||||||
取消
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
import {DynamicFormItemConfig} from "@/app/home/components/dynamic-form/DynamicFormItemConfig";
|
import { DynamicFormItemConfig } from "@/app/home/components/dynamic-form/DynamicFormItemConfig";
|
||||||
|
|
||||||
export interface IPipelineChildFormEntity {
|
export interface IPipelineChildFormEntity {
|
||||||
name: string;
|
name: string;
|
||||||
label: string;
|
label: string;
|
||||||
formItems: DynamicFormItemConfig[]
|
formItems: DynamicFormItemConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PipelineChildFormEntity implements IPipelineChildFormEntity {
|
export class PipelineChildFormEntity implements IPipelineChildFormEntity {
|
||||||
formItems: DynamicFormItemConfig[];
|
formItems: DynamicFormItemConfig[];
|
||||||
label: string;
|
label: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
constructor(props: IPipelineChildFormEntity) {
|
constructor(props: IPipelineChildFormEntity) {
|
||||||
this.form = props.form;
|
this.label = props.label;
|
||||||
this.label = props.label;
|
this.name = props.name;
|
||||||
this.name = props.name;
|
this.formItems = props.formItems;
|
||||||
this.formItems = props.formItems;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,68 +1,78 @@
|
|||||||
"use client"
|
"use client";
|
||||||
import {Modal} from "antd";
|
import { Modal } from "antd";
|
||||||
import {useState, useEffect} from "react";
|
import { useState, useEffect } from "react";
|
||||||
import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent";
|
import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent";
|
||||||
import PipelineFormComponent from "./components/pipeline-form/PipelineFormComponent";
|
import PipelineFormComponent from "./components/pipeline-form/PipelineFormComponent";
|
||||||
import {httpClient} from "@/app/infra/http/HttpClient";
|
import { httpClient } from "@/app/infra/http/HttpClient";
|
||||||
import {PipelineCardVO} from "@/app/home/pipelines/components/pipeline-card/PipelineCardVO";
|
import { PipelineCardVO } from "@/app/home/pipelines/components/pipeline-card/PipelineCardVO";
|
||||||
import PipelineCardComponent from "@/app/home/pipelines/components/pipeline-card/PipelineCardComponent";
|
import PipelineCardComponent from "@/app/home/pipelines/components/pipeline-card/PipelineCardComponent";
|
||||||
|
|
||||||
export default function PluginConfigPage() {
|
export default function PluginConfigPage() {
|
||||||
const [modalOpen, setModalOpen] = useState<boolean>(false);
|
const [modalOpen, setModalOpen] = useState<boolean>(false);
|
||||||
const [isEditForm, setIsEditForm] = useState(false)
|
const [isEditForm] = useState(false);
|
||||||
const [pipelineList, setPipelineList] = useState<PipelineCardVO[]>([])
|
const [pipelineList, setPipelineList] = useState<PipelineCardVO[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getPipelines()
|
getPipelines();
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
|
function getPipelines() {
|
||||||
|
httpClient
|
||||||
|
.getPipelines()
|
||||||
|
.then((value) => {
|
||||||
|
const pipelineList = value.pipelines.map((pipeline) => {
|
||||||
|
return new PipelineCardVO({
|
||||||
|
createTime: pipeline.created_at,
|
||||||
|
description: pipeline.description,
|
||||||
|
id: pipeline.uuid,
|
||||||
|
name: pipeline.name,
|
||||||
|
version: pipeline.for_version
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setPipelineList(pipelineList);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
// TODO toast
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getPipelines() {
|
return (
|
||||||
httpClient.getPipelines().then(value => {
|
<div className={``}>
|
||||||
const pipelineList = value.pipelines.map(pipeline => {
|
<Modal
|
||||||
return new PipelineCardVO({
|
title={isEditForm ? "编辑流水线" : "创建流水线"}
|
||||||
createTime: pipeline.created_at,
|
centered
|
||||||
description: pipeline.description,
|
open={modalOpen}
|
||||||
id: pipeline.uuid,
|
onOk={() => setModalOpen(false)}
|
||||||
name: pipeline.name,
|
onCancel={() => setModalOpen(false)}
|
||||||
version: pipeline.for_version
|
width={700}
|
||||||
})
|
footer={null}
|
||||||
})
|
>
|
||||||
setPipelineList(pipelineList)
|
<PipelineFormComponent
|
||||||
}).catch(error => {
|
onFinish={() => {
|
||||||
// TODO toast
|
getPipelines();
|
||||||
console.log(error)
|
setModalOpen(false);
|
||||||
})
|
}}
|
||||||
}
|
/>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
return (
|
{pipelineList.length > 0 && (
|
||||||
<div className={``}>
|
<div className={``}>
|
||||||
<Modal
|
{pipelineList.map((pipeline) => {
|
||||||
title={isEditForm ? "编辑流水线" : "创建流水线"}
|
return (
|
||||||
centered
|
<PipelineCardComponent key={pipeline.id} cardVO={pipeline} />
|
||||||
open={modalOpen}
|
);
|
||||||
onOk={() => setModalOpen(false)}
|
})}
|
||||||
onCancel={() => setModalOpen(false)}
|
|
||||||
width={700}
|
|
||||||
footer={null}
|
|
||||||
>
|
|
||||||
<PipelineFormComponent
|
|
||||||
onFinish={() => {
|
|
||||||
getPipelines()
|
|
||||||
setModalOpen(false)
|
|
||||||
}}
|
|
||||||
onCancel={() => {}}/>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
{
|
|
||||||
pipelineList.length > 0 &&
|
|
||||||
<div className={``}>
|
|
||||||
{pipelineList.map(pipeline => {
|
|
||||||
return <PipelineCardComponent cardVO={pipeline}/>
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<CreateCardComponent width={360} height={200} plusSize={90} onClick={() => {setModalOpen(true)}}/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
)}
|
||||||
|
<CreateCardComponent
|
||||||
|
width={360}
|
||||||
|
height={200}
|
||||||
|
plusSize={90}
|
||||||
|
onClick={() => {
|
||||||
|
setModalOpen(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,104 +1,107 @@
|
|||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent";
|
import CreateCardComponent from "@/app/infra/basic-component/create-card-component/CreateCardComponent";
|
||||||
import {PluginCardVO} from "@/app/home/plugins/plugin-installed/PluginCardVO";
|
import { PluginCardVO } from "@/app/home/plugins/plugin-installed/PluginCardVO";
|
||||||
import {useEffect, useState} from "react";
|
import { useEffect, useState } from "react";
|
||||||
import PluginCardComponent from "@/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent";
|
import PluginCardComponent from "@/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent";
|
||||||
import styles from "@/app/home/plugins/plugins.module.css";
|
import styles from "@/app/home/plugins/plugins.module.css";
|
||||||
import {Modal, Input} from "antd";
|
import { Modal, Input } from "antd";
|
||||||
import {GithubOutlined} from "@ant-design/icons";
|
import { GithubOutlined } from "@ant-design/icons";
|
||||||
import {httpClient} from "@/app/infra/http/HttpClient";
|
import { httpClient } from "@/app/infra/http/HttpClient";
|
||||||
|
|
||||||
export default function PluginInstalledComponent () {
|
export default function PluginInstalledComponent() {
|
||||||
const [pluginList, setPluginList] = useState<PluginCardVO[]>([])
|
const [pluginList, setPluginList] = useState<PluginCardVO[]>([]);
|
||||||
const [modalOpen, setModalOpen] = useState(false)
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
const [githubURL, setGithubURL] = useState("")
|
const [githubURL, setGithubURL] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
initData();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
function initData() {
|
||||||
initData()
|
getPluginList();
|
||||||
}, [])
|
}
|
||||||
|
|
||||||
function initData() {
|
function getPluginList() {
|
||||||
getPluginList()
|
httpClient.getPlugins().then((value) => {
|
||||||
}
|
setPluginList(
|
||||||
|
value.plugins.map((plugin) => {
|
||||||
function getPluginList() {
|
return new PluginCardVO({
|
||||||
httpClient.getPlugins().then((value) => {
|
author: plugin.author,
|
||||||
setPluginList(value.plugins.map(plugin => {
|
description: plugin.description.zh_CN,
|
||||||
return new PluginCardVO({
|
handlerCount: 0,
|
||||||
author: plugin.author,
|
name: plugin.name,
|
||||||
description: plugin.description.zh_CN,
|
version: plugin.version,
|
||||||
handlerCount: 0,
|
isInitialized: plugin.status === "initialized"
|
||||||
name: plugin.name,
|
});
|
||||||
version: plugin.version,
|
|
||||||
isInitialized: plugin.status === "initialized",
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
}
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function handleModalConfirm() {
|
function handleModalConfirm() {
|
||||||
installPlugin(githubURL)
|
installPlugin(githubURL);
|
||||||
setModalOpen(false)
|
setModalOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function installPlugin(url: string) {
|
function installPlugin(url: string) {
|
||||||
httpClient.installPluginFromGithub(url).then(res => {
|
httpClient
|
||||||
// 安装后重新拉取
|
.installPluginFromGithub(url)
|
||||||
getPluginList()
|
.then(() => {
|
||||||
}).catch(err => {
|
// 安装后重新拉取
|
||||||
console.log("error when install plugin:", err)
|
getPluginList();
|
||||||
})
|
})
|
||||||
}
|
.catch((err) => {
|
||||||
return (
|
console.log("error when install plugin:", err);
|
||||||
<div className={`${styles.pluginListContainer}`}>
|
});
|
||||||
<Modal
|
}
|
||||||
title={
|
return (
|
||||||
<div className={`${styles.modalTitle}`}>
|
<div className={`${styles.pluginListContainer}`}>
|
||||||
<GithubOutlined
|
<Modal
|
||||||
style={{
|
title={
|
||||||
fontSize: '30px',
|
<div className={`${styles.modalTitle}`}>
|
||||||
marginRight: '20px'
|
<GithubOutlined
|
||||||
}}
|
style={{
|
||||||
type="setting"
|
fontSize: "30px",
|
||||||
/>
|
marginRight: "20px"
|
||||||
<span>从 GitHub 安装插件</span>
|
}}
|
||||||
</div>
|
type="setting"
|
||||||
}
|
|
||||||
centered
|
|
||||||
open={modalOpen}
|
|
||||||
onOk={() => handleModalConfirm()}
|
|
||||||
onCancel={() => setModalOpen(false)}
|
|
||||||
width={500}
|
|
||||||
destroyOnClose={true}
|
|
||||||
>
|
|
||||||
<div className={`${styles.modalBody}`}>
|
|
||||||
<div>
|
|
||||||
目前仅支持从 GitHub 安装
|
|
||||||
</div>
|
|
||||||
<Input
|
|
||||||
placeholder="请输入插件的Github链接"
|
|
||||||
value={githubURL}
|
|
||||||
onChange={(e) => setGithubURL(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
{
|
|
||||||
pluginList.map((vo, index) => {
|
|
||||||
return <div key={index}>
|
|
||||||
<PluginCardComponent cardVO={vo}/>
|
|
||||||
</div>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
<CreateCardComponent
|
|
||||||
width={360}
|
|
||||||
height={140}
|
|
||||||
plusSize={90}
|
|
||||||
onClick={() => {
|
|
||||||
setModalOpen(true)
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
<span>从 GitHub 安装插件</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
centered
|
||||||
|
open={modalOpen}
|
||||||
|
onOk={() => handleModalConfirm()}
|
||||||
|
onCancel={() => setModalOpen(false)}
|
||||||
|
width={500}
|
||||||
|
destroyOnClose={true}
|
||||||
|
>
|
||||||
|
<div className={`${styles.modalBody}`}>
|
||||||
|
<div>目前仅支持从 GitHub 安装</div>
|
||||||
|
<Input
|
||||||
|
placeholder="请输入插件的Github链接"
|
||||||
|
value={githubURL}
|
||||||
|
onChange={(e) => setGithubURL(e.target.value)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
</Modal>
|
||||||
|
{pluginList.map((vo, index) => {
|
||||||
|
return (
|
||||||
|
<div key={index}>
|
||||||
|
<PluginCardComponent cardVO={vo} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<CreateCardComponent
|
||||||
|
width={360}
|
||||||
|
height={140}
|
||||||
|
plusSize={90}
|
||||||
|
onClick={() => {
|
||||||
|
setModalOpen(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,65 @@
|
|||||||
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 { GithubOutlined, LinkOutlined, ToolOutlined } from "@ant-design/icons";
|
||||||
import {Switch, Tag} from 'antd'
|
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";
|
||||||
|
|
||||||
export default function PluginCardComponent({
|
export default function PluginCardComponent({
|
||||||
cardVO
|
cardVO
|
||||||
}: {
|
}: {
|
||||||
cardVO: PluginCardVO
|
cardVO: PluginCardVO;
|
||||||
}) {
|
}) {
|
||||||
const [initialized, setInitialized] = useState(cardVO.isInitialized)
|
const [initialized, setInitialized] = useState(cardVO.isInitialized);
|
||||||
const [switchEnable, setSwitchEnable] = useState(true)
|
const [switchEnable, setSwitchEnable] = useState(true);
|
||||||
|
|
||||||
function handleEnable() {
|
function handleEnable() {
|
||||||
setSwitchEnable(false)
|
setSwitchEnable(false);
|
||||||
httpClient.togglePlugin(cardVO.author, cardVO.name, !initialized).then(result => {
|
httpClient
|
||||||
setInitialized(!initialized)
|
.togglePlugin(cardVO.author, cardVO.name, !initialized)
|
||||||
}).catch(err => {
|
.then(() => {
|
||||||
console.log("error: ", err)
|
setInitialized(!initialized);
|
||||||
}).finally(() => {
|
})
|
||||||
setSwitchEnable(true)
|
.catch((err) => {
|
||||||
})
|
console.log("error: ", err);
|
||||||
}
|
})
|
||||||
return (
|
.finally(() => {
|
||||||
<div className={`${styles.cardContainer}`}>
|
setSwitchEnable(true);
|
||||||
{/* header */}
|
});
|
||||||
<div className={`${styles.cardHeader}`}>
|
}
|
||||||
{/* left author */}
|
return (
|
||||||
<div className={`${styles.fontGray}`}>{cardVO.author}</div>
|
<div className={`${styles.cardContainer}`}>
|
||||||
{/* right icon & version */}
|
{/* header */}
|
||||||
<div className={`${styles.iconVersionContainer}`}>
|
<div className={`${styles.cardHeader}`}>
|
||||||
<GithubOutlined
|
{/* left author */}
|
||||||
style={{fontSize: '26px'}}
|
<div className={`${styles.fontGray}`}>{cardVO.author}</div>
|
||||||
type="setting"
|
{/* right icon & version */}
|
||||||
/>
|
<div className={`${styles.iconVersionContainer}`}>
|
||||||
<Tag color="#108ee9">v{cardVO.version}</Tag>
|
<GithubOutlined style={{ fontSize: "26px" }} type="setting" />
|
||||||
</div>
|
<Tag color="#108ee9">v{cardVO.version}</Tag>
|
||||||
</div>
|
|
||||||
{/* content */}
|
|
||||||
<div className={`${styles.cardContent}`}>
|
|
||||||
<div className={`${styles.boldFont}`}>{cardVO.name}</div>
|
|
||||||
<div className={`${styles.fontGray}`}>{cardVO.description}</div>
|
|
||||||
</div>
|
|
||||||
{/* footer */}
|
|
||||||
<div className={`${styles.cardFooter}`}>
|
|
||||||
<div className={`${styles.linkSettingContainer}`}>
|
|
||||||
<div className={`${styles.link}`}>
|
|
||||||
<LinkOutlined
|
|
||||||
style={{fontSize: '22px'}}
|
|
||||||
/>
|
|
||||||
<span>1</span>
|
|
||||||
</div>
|
|
||||||
<ToolOutlined
|
|
||||||
style={{fontSize: '22px'}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Switch
|
|
||||||
value={initialized}
|
|
||||||
onClick={handleEnable}
|
|
||||||
disabled={!switchEnable}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
</div>
|
||||||
|
{/* content */}
|
||||||
|
<div className={`${styles.cardContent}`}>
|
||||||
|
<div className={`${styles.boldFont}`}>{cardVO.name}</div>
|
||||||
|
<div className={`${styles.fontGray}`}>{cardVO.description}</div>
|
||||||
|
</div>
|
||||||
|
{/* footer */}
|
||||||
|
<div className={`${styles.cardFooter}`}>
|
||||||
|
<div className={`${styles.linkSettingContainer}`}>
|
||||||
|
<div className={`${styles.link}`}>
|
||||||
|
<LinkOutlined style={{ fontSize: "22px" }} />
|
||||||
|
<span>1</span>
|
||||||
|
</div>
|
||||||
|
<ToolOutlined style={{ fontSize: "22px" }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
value={initialized}
|
||||||
|
onClick={handleEnable}
|
||||||
|
disabled={!switchEnable}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +1,87 @@
|
|||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import {useCallback, useEffect, useState} from "react";
|
import { useEffect, useState } from "react";
|
||||||
import styles from "@/app/home/plugins/plugins.module.css";
|
import styles from "@/app/home/plugins/plugins.module.css";
|
||||||
import {PluginMarketCardVO} from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardVO";
|
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 PluginMarketCardComponent from "@/app/home/plugins/plugin-market/plugin-market-card/PluginMarketCardComponent";
|
||||||
import {Input, Pagination} from "antd";
|
import { Input, Pagination } from "antd";
|
||||||
import {debounce} from "lodash"
|
import { spaceClient } from "@/app/infra/http/HttpClient";
|
||||||
import {httpClient, spaceClient} from "@/app/infra/http/HttpClient";
|
|
||||||
|
|
||||||
export default function PluginMarketComponent () {
|
export default function PluginMarketComponent() {
|
||||||
const [marketPluginList, setMarketPluginList] = useState<PluginMarketCardVO[]>([])
|
const [marketPluginList, setMarketPluginList] = useState<
|
||||||
const [totalCount, setTotalCount] = useState(0)
|
PluginMarketCardVO[]
|
||||||
const [nowPage, setNowPage] = useState(1)
|
>([]);
|
||||||
const [searchKeyword, setSearchKeyword] = useState("")
|
const [totalCount, setTotalCount] = useState(0);
|
||||||
|
const [nowPage, setNowPage] = useState(1);
|
||||||
|
const [searchKeyword, setSearchKeyword] = useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initData()
|
initData();
|
||||||
}, [])
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
function initData() {
|
function initData() {
|
||||||
getPluginList()
|
getPluginList();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onInputSearchKeyword(keyword: string) {
|
function onInputSearchKeyword(keyword: string) {
|
||||||
// 这里记得加防抖,暂时没加
|
// 这里记得加防抖,暂时没加
|
||||||
setSearchKeyword(keyword)
|
setSearchKeyword(keyword);
|
||||||
setNowPage(1)
|
setNowPage(1);
|
||||||
getPluginList(1, keyword)
|
getPluginList(1, keyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPluginList(
|
||||||
|
page: number = nowPage,
|
||||||
|
keyword: string = searchKeyword
|
||||||
|
) {
|
||||||
|
spaceClient.getMarketPlugins(page, 10, keyword).then((res) => {
|
||||||
|
setMarketPluginList(
|
||||||
|
res.plugins.map(
|
||||||
|
(marketPlugin) =>
|
||||||
|
new PluginMarketCardVO({
|
||||||
|
author: marketPlugin.author,
|
||||||
|
description: marketPlugin.description,
|
||||||
|
githubURL: marketPlugin.repository,
|
||||||
|
name: marketPlugin.name,
|
||||||
|
pluginId: String(marketPlugin.ID),
|
||||||
|
starCount: marketPlugin.stars
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
setTotalCount(res.total);
|
||||||
|
console.log("market plugins:", res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getPluginList(page: number = nowPage, keyword: string = searchKeyword) {
|
return (
|
||||||
spaceClient.getMarketPlugins(page, 10, keyword).then(res => {
|
<div className={`${styles.marketComponentBody}`}>
|
||||||
setMarketPluginList(res.plugins.map(marketPlugin => new PluginMarketCardVO({
|
<Input
|
||||||
author: marketPlugin.author,
|
style={{
|
||||||
description: marketPlugin.description,
|
width: "300px",
|
||||||
githubURL: marketPlugin.repository,
|
marginTop: "10px"
|
||||||
name: marketPlugin.name,
|
}}
|
||||||
pluginId: String(marketPlugin.ID),
|
value={searchKeyword}
|
||||||
starCount: marketPlugin.stars,
|
placeholder="搜索插件"
|
||||||
})))
|
onChange={(e) => onInputSearchKeyword(e.target.value)}
|
||||||
setTotalCount(res.total)
|
/>
|
||||||
console.log("market plugins:", res)
|
<div className={`${styles.pluginListContainer}`}>
|
||||||
})
|
{marketPluginList.map((vo, index) => {
|
||||||
}
|
return (
|
||||||
|
<div key={index}>
|
||||||
return (
|
<PluginMarketCardComponent cardVO={vo} />
|
||||||
<div className={`${styles.marketComponentBody}`}>
|
|
||||||
<Input
|
|
||||||
style={{
|
|
||||||
width: '300px',
|
|
||||||
marginTop: '10px',
|
|
||||||
}}
|
|
||||||
value={searchKeyword}
|
|
||||||
placeholder="搜索插件"
|
|
||||||
onChange={(e) => onInputSearchKeyword(e.target.value)}
|
|
||||||
/>
|
|
||||||
<div className={`${styles.pluginListContainer}`}>
|
|
||||||
{
|
|
||||||
marketPluginList.map((vo, index) => {
|
|
||||||
return <div key={index}>
|
|
||||||
<PluginMarketCardComponent cardVO={vo}/>
|
|
||||||
</div>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<Pagination
|
);
|
||||||
defaultCurrent={1}
|
})}
|
||||||
total={totalCount}
|
</div>
|
||||||
onChange={(pageNumber) => {
|
<Pagination
|
||||||
setNowPage(pageNumber)
|
defaultCurrent={1}
|
||||||
getPluginList(pageNumber)
|
total={totalCount}
|
||||||
}}
|
onChange={(pageNumber) => {
|
||||||
/>
|
setNowPage(pageNumber);
|
||||||
</div>
|
getPluginList(pageNumber);
|
||||||
|
}}
|
||||||
)
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,204 +1,217 @@
|
|||||||
export interface ApiResponse<T> {
|
export interface ApiResponse<T> {
|
||||||
code: number;
|
code: number;
|
||||||
data: T;
|
data: T;
|
||||||
msg: string;
|
msg: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface I18nText {
|
export interface I18nText {
|
||||||
en_US: string;
|
en_US: string;
|
||||||
zh_CN: string;
|
zh_CN: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AsyncTaskCreatedResp {
|
export interface AsyncTaskCreatedResp {
|
||||||
task_id: number;
|
task_id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespProviderRequesters {
|
export interface ApiRespProviderRequesters {
|
||||||
requesters: Requester[];
|
requesters: Requester[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespProviderRequester {
|
export interface ApiRespProviderRequester {
|
||||||
requester: Requester;
|
requester: Requester;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Requester {
|
export interface Requester {
|
||||||
name: string;
|
name: string;
|
||||||
label: I18nText;
|
label: I18nText;
|
||||||
description: I18nText;
|
description: I18nText;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
spec: object;
|
spec: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespProviderLLMModels {
|
export interface ApiRespProviderLLMModels {
|
||||||
models: LLMModel[];
|
models: LLMModel[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespProviderLLMModel {
|
export interface ApiRespProviderLLMModel {
|
||||||
model: LLMModel;
|
model: LLMModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LLMModel {
|
export interface LLMModel {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
uuid: string;
|
uuid: string;
|
||||||
requester: string;
|
requester: string;
|
||||||
requester_config: object;
|
requester_config: {
|
||||||
extra_args: object;
|
base_url: string;
|
||||||
api_keys: string[];
|
timeout: number;
|
||||||
abilities: string[];
|
};
|
||||||
// created_at: string;
|
extra_args: object;
|
||||||
// updated_at: string;
|
api_keys: string[];
|
||||||
|
abilities: string[];
|
||||||
|
// created_at: string;
|
||||||
|
// updated_at: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespPipelines {
|
export interface ApiRespPipelines {
|
||||||
pipelines: Pipeline[];
|
pipelines: Pipeline[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespPipeline {
|
export interface ApiRespPipeline {
|
||||||
pipeline: Pipeline;
|
pipeline: Pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Pipeline {
|
export interface Pipeline {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
for_version: string;
|
for_version: string;
|
||||||
config: object;
|
config: object;
|
||||||
stages: string[];
|
stages: string[];
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespPlatformAdapters {
|
export interface ApiRespPlatformAdapters {
|
||||||
adapters: Adapter[];
|
adapters: Adapter[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespPlatformAdapter {
|
export interface ApiRespPlatformAdapter {
|
||||||
adapter: Adapter;
|
adapter: Adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Adapter {
|
export interface Adapter {
|
||||||
name: string;
|
name: string;
|
||||||
label: I18nText;
|
label: I18nText;
|
||||||
description: I18nText;
|
description: I18nText;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
spec: object;
|
spec: {
|
||||||
|
config: AdapterSpecConfig[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdapterSpecConfig {
|
||||||
|
default: string | number | boolean | Array<unknown>;
|
||||||
|
label: I18nText;
|
||||||
|
name: string;
|
||||||
|
required: boolean;
|
||||||
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespPlatformBots {
|
export interface ApiRespPlatformBots {
|
||||||
bots: Bot[];
|
bots: Bot[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespPlatformBot {
|
export interface ApiRespPlatformBot {
|
||||||
bot: Bot;
|
bot: Bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Bot {
|
export interface Bot {
|
||||||
uuid?: string;
|
uuid?: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
enable?: boolean;
|
enable?: boolean;
|
||||||
adapter: string;
|
adapter: string;
|
||||||
adapter_config: object;
|
adapter_config: object;
|
||||||
use_pipeline_name?: string;
|
use_pipeline_name?: string;
|
||||||
use_pipeline_uuid?: string;
|
use_pipeline_uuid?: string;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// plugins
|
// plugins
|
||||||
export interface ApiRespPlugins {
|
export interface ApiRespPlugins {
|
||||||
plugins: Plugin[];
|
plugins: Plugin[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespPlugin {
|
export interface ApiRespPlugin {
|
||||||
plugin: Plugin;
|
plugin: Plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Plugin {
|
export interface Plugin {
|
||||||
author: string;
|
author: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: I18nText;
|
description: I18nText;
|
||||||
label: I18nText;
|
label: I18nText;
|
||||||
version: string;
|
version: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
priority: number;
|
priority: number;
|
||||||
status: string;
|
status: string;
|
||||||
tools: object[];
|
tools: object[];
|
||||||
event_handlers: object;
|
event_handlers: object;
|
||||||
main_file: string;
|
main_file: string;
|
||||||
pkg_path: string;
|
pkg_path: string;
|
||||||
repository: string;
|
repository: string;
|
||||||
config_schema: object;
|
config_schema: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespPluginConfig {
|
export interface ApiRespPluginConfig {
|
||||||
config: object;
|
config: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PluginReorderElement {
|
export interface PluginReorderElement {
|
||||||
author: string;
|
author: string;
|
||||||
name: string;
|
name: string;
|
||||||
priority: number;
|
priority: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// system
|
// system
|
||||||
export interface ApiRespSystemInfo {
|
export interface ApiRespSystemInfo {
|
||||||
debug: boolean;
|
debug: boolean;
|
||||||
version: string;
|
version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespAsyncTasks {
|
export interface ApiRespAsyncTasks {
|
||||||
tasks: AsyncTask[];
|
tasks: AsyncTask[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespAsyncTask {
|
export interface ApiRespAsyncTask {
|
||||||
task: AsyncTask;
|
task: AsyncTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AsyncTaskRuntimeInfo {
|
export interface AsyncTaskRuntimeInfo {
|
||||||
done: boolean;
|
done: boolean;
|
||||||
exception?: string;
|
exception?: string;
|
||||||
result?: object;
|
result?: object;
|
||||||
state: string;
|
state: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AsyncTaskTaskContext {
|
export interface AsyncTaskTaskContext {
|
||||||
current_action: string;
|
current_action: string;
|
||||||
log: string;
|
log: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AsyncTask {
|
export interface AsyncTask {
|
||||||
id: number;
|
id: number;
|
||||||
kind: string;
|
kind: string;
|
||||||
name: string;
|
name: string;
|
||||||
task_type: string; // system or user
|
task_type: string; // system or user
|
||||||
runtime: AsyncTaskRuntimeInfo;
|
runtime: AsyncTaskRuntimeInfo;
|
||||||
task_context: AsyncTaskTaskContext;
|
task_context: AsyncTaskTaskContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiRespUserToken {
|
export interface ApiRespUserToken {
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarketPlugin {
|
export interface MarketPlugin {
|
||||||
ID: number
|
ID: number;
|
||||||
CreatedAt: string // ISO 8601 格式日期
|
CreatedAt: string; // ISO 8601 格式日期
|
||||||
UpdatedAt: string
|
UpdatedAt: string;
|
||||||
DeletedAt: string | null
|
DeletedAt: string | null;
|
||||||
name: string
|
name: string;
|
||||||
author: string
|
author: string;
|
||||||
description: string
|
description: string;
|
||||||
repository: string // GitHub 仓库路径
|
repository: string; // GitHub 仓库路径
|
||||||
artifacts_path: string
|
artifacts_path: string;
|
||||||
stars: number
|
stars: number;
|
||||||
downloads: number
|
downloads: number;
|
||||||
status: "initialized" | "mounted" // 可根据实际状态值扩展联合类型
|
status: "initialized" | "mounted"; // 可根据实际状态值扩展联合类型
|
||||||
synced_at: string
|
synced_at: string;
|
||||||
pushed_at: string // 最后一次代码推送时间
|
pushed_at: string; // 最后一次代码推送时间
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarketPluginResponse {
|
export interface MarketPluginResponse {
|
||||||
plugins: MarketPlugin[]
|
plugins: MarketPlugin[];
|
||||||
total: number
|
total: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,46 @@
|
|||||||
export interface GetMetaDataResponse {
|
export interface GetMetaDataResponse {
|
||||||
configs: Config[]
|
configs: Config[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface Label {
|
interface Label {
|
||||||
en_US: string;
|
en_US: string;
|
||||||
zh_CN: string;
|
zh_CN: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Option {
|
interface Option {
|
||||||
label: Label;
|
label: Label;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ConfigItem {
|
interface ConfigItem {
|
||||||
default?: boolean | Array<unknown> | number | string;
|
default?: boolean | Array<unknown> | number | string;
|
||||||
description?: Label;
|
description?: Label;
|
||||||
items?: {
|
items?: {
|
||||||
type?: string;
|
type?: string;
|
||||||
properties?: {
|
properties?: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
type: string;
|
type: string;
|
||||||
default?: any;
|
default?: object | string;
|
||||||
};
|
};
|
||||||
};
|
|
||||||
};
|
};
|
||||||
label: Label;
|
};
|
||||||
name: string;
|
label: Label;
|
||||||
options?: Option[];
|
name: string;
|
||||||
required: boolean;
|
options?: Option[];
|
||||||
scope?: string;
|
required: boolean;
|
||||||
type: string;
|
scope?: string;
|
||||||
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Stage {
|
interface Stage {
|
||||||
config: ConfigItem[];
|
config: ConfigItem[];
|
||||||
description?: Label;
|
description?: Label;
|
||||||
label: Label;
|
label: Label;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Config {
|
interface Config {
|
||||||
label: Label;
|
label: Label;
|
||||||
name: string;
|
name: string;
|
||||||
stages: Stage[];
|
stages: Stage[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,194 +1,204 @@
|
|||||||
'use client';
|
"use client";
|
||||||
import { Button, Input, Form, Checkbox, Divider } from 'antd';
|
import { Button, Input, Form, Checkbox, Divider } from "antd";
|
||||||
import {GoogleOutlined, AppleOutlined, LockOutlined, UserOutlined, QqCircleFilled, QqOutlined} from '@ant-design/icons';
|
import {
|
||||||
import styles from './login.module.css';
|
GoogleOutlined,
|
||||||
import {useEffect, useState} from 'react';
|
LockOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
QqOutlined
|
||||||
import {httpClient} from "@/app/infra/http/HttpClient";
|
} from "@ant-design/icons";
|
||||||
import '@ant-design/v5-patch-for-react-19';
|
import styles from "./login.module.css";
|
||||||
import {useRouter} from "next/navigation";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { httpClient } from "@/app/infra/http/HttpClient";
|
||||||
|
import "@ant-design/v5-patch-for-react-19";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [form] = Form.useForm<LoginField>();
|
const [form] = Form.useForm<LoginField>();
|
||||||
const [rememberMe, setRememberMe] = useState(false);
|
const [rememberMe, setRememberMe] = useState(false);
|
||||||
const [isRegisterMode, setIsRegisterMode] = useState(false);
|
const [isRegisterMode, setIsRegisterMode] = useState(false);
|
||||||
const [isInitialized, setIsInitialized] = useState(false)
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getIsInitialized()
|
getIsInitialized();
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
|
// 检查是否为首次启动项目,只为首次启动的用户提供注册资格
|
||||||
|
function getIsInitialized() {
|
||||||
|
httpClient
|
||||||
|
.checkIfInited()
|
||||||
|
.then((res) => {
|
||||||
|
setIsInitialized(res.initialized);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("error at getIsInitialized: ", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否为首次启动项目,只为首次启动的用户提供注册资格
|
function handleFormSubmit(formField: LoginField) {
|
||||||
function getIsInitialized() {
|
if (isRegisterMode) {
|
||||||
httpClient.checkIfInited().then(res => {
|
handleRegister(formField.email, formField.password);
|
||||||
setIsInitialized(res.initialized)
|
} else {
|
||||||
}).catch(err => {
|
handleLogin(formField.email, formField.password);
|
||||||
console.log("error at getIsInitialized: ", err)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleFormSubmit(formField: LoginField) {
|
function handleRegister(username: string, password: string) {
|
||||||
if (isRegisterMode) {
|
httpClient
|
||||||
handleRegister(formField.email, formField.password);
|
.initUser(username, password)
|
||||||
} else {
|
.then((res) => {
|
||||||
handleLogin(formField.email, formField.password)
|
console.log("init user success: ", res);
|
||||||
}
|
})
|
||||||
}
|
.catch((err) => {
|
||||||
|
console.log("init user error: ", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function handleRegister(username: string, password: string) {
|
function handleLogin(username: string, password: string) {
|
||||||
httpClient.initUser(username, password).then(res => {
|
httpClient
|
||||||
console.log("init user success: ", res)
|
.authUser(username, password)
|
||||||
}).catch(err => {
|
.then((res) => {
|
||||||
console.log("init user error: ", err)
|
localStorage.setItem("token", res.token);
|
||||||
})
|
console.log("login success: ", res);
|
||||||
}
|
router.push("/home");
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("login error: ", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function handleLogin(username: string, password: string) {
|
return (
|
||||||
httpClient.authUser(username, password).then(res => {
|
// 使用 Ant Design 的组件库,使用 antd 的样式
|
||||||
localStorage.setItem("token", res.token)
|
// 仅前端样式,无交互功能。
|
||||||
console.log("login success: ", res)
|
|
||||||
router.push("/home")
|
|
||||||
}).catch(err => {
|
|
||||||
console.log("login error: ", err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
<div className={styles.container}>
|
||||||
|
{/* login 类是整个 container,使用 flex 左右布局 */}
|
||||||
|
<div className={styles.login}>
|
||||||
|
{/* left 为注册的表单,需要填入的内容有:邮箱,密码 */}
|
||||||
|
<div className={styles.left}>
|
||||||
|
<div className={styles.loginForm}>
|
||||||
|
{isRegisterMode && (
|
||||||
|
<h1 className={styles.title}>注册 LangBot 账号</h1>
|
||||||
|
)}
|
||||||
|
{!isRegisterMode && (
|
||||||
|
<h1 className={styles.title}>欢迎回到 LangBot</h1>
|
||||||
|
)}
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
layout="vertical"
|
||||||
|
onFinish={(values) => {
|
||||||
|
handleFormSubmit(values);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="email"
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: "请输入邮箱!" },
|
||||||
|
{ type: "email", message: "请输入有效的邮箱地址!" }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder="输入邮箱地址"
|
||||||
|
size="large"
|
||||||
|
prefix={<UserOutlined />}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
return (
|
<Form.Item
|
||||||
// 使用 Ant Design 的组件库,使用 antd 的样式
|
name="password"
|
||||||
// 仅前端样式,无交互功能。
|
rules={[{ required: true, message: "请输入密码!" }]}
|
||||||
|
>
|
||||||
|
<Input.Password
|
||||||
|
placeholder="输入密码"
|
||||||
|
size="large"
|
||||||
|
prefix={<LockOutlined />}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<div className={styles.container}>
|
<div className={styles.rememberMe}>
|
||||||
{/* login 类是整个 container,使用 flex 左右布局 */}
|
<Checkbox
|
||||||
<div className={styles.login}>
|
checked={rememberMe}
|
||||||
{/* left 为注册的表单,需要填入的内容有:邮箱,密码 */}
|
onChange={(e) => setRememberMe(e.target.checked)}
|
||||||
<div className={styles.left}>
|
>
|
||||||
<div className={styles.loginForm}>
|
30天内自动登录
|
||||||
{
|
</Checkbox>
|
||||||
isRegisterMode &&
|
<span>
|
||||||
<h1 className={styles.title}>注册 LangBot 账号</h1>
|
<a href="#" className={`${styles.forgetPassword}`}>
|
||||||
}
|
忘记密码?
|
||||||
{
|
</a>
|
||||||
!isRegisterMode &&
|
{!isRegisterMode && (
|
||||||
<h1 className={styles.title}>欢迎回到 LangBot</h1>
|
<a
|
||||||
}
|
href=""
|
||||||
<Form
|
onClick={(event) => {
|
||||||
form={form}
|
setIsRegisterMode(true);
|
||||||
layout="vertical"
|
event.preventDefault();
|
||||||
onFinish={(values) => {
|
}}
|
||||||
handleFormSubmit(values)
|
>
|
||||||
}}
|
去注册?
|
||||||
>
|
</a>
|
||||||
<Form.Item
|
)}
|
||||||
name="email"
|
{isRegisterMode && (
|
||||||
rules={[
|
<a
|
||||||
{ required: true, message: '请输入邮箱!' },
|
href=""
|
||||||
{ type: 'email', message: '请输入有效的邮箱地址!' }
|
onClick={(event) => {
|
||||||
]}
|
setIsRegisterMode(false);
|
||||||
>
|
event.preventDefault();
|
||||||
<Input
|
}}
|
||||||
placeholder="输入邮箱地址"
|
>
|
||||||
size="large"
|
去登录
|
||||||
prefix={<UserOutlined />}
|
</a>
|
||||||
/>
|
)}
|
||||||
</Form.Item>
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Form.Item
|
<Button
|
||||||
name="password"
|
type="primary"
|
||||||
rules={[
|
size="large"
|
||||||
{ required: true, message: '请输入密码!' }
|
className={styles.loginButton}
|
||||||
]}
|
block
|
||||||
>
|
htmlType="submit"
|
||||||
<Input.Password
|
disabled={isRegisterMode && isInitialized}
|
||||||
placeholder="输入密码"
|
>
|
||||||
size="large"
|
{isRegisterMode
|
||||||
prefix={<LockOutlined />}
|
? isInitialized
|
||||||
/>
|
? "暂不提供注册"
|
||||||
</Form.Item>
|
: "注册"
|
||||||
|
: "登录"}
|
||||||
|
</Button>
|
||||||
|
|
||||||
<div className={styles.rememberMe}>
|
<Divider className={styles.divider}>或</Divider>
|
||||||
<Checkbox
|
|
||||||
checked={rememberMe}
|
|
||||||
onChange={(e) => setRememberMe(e.target.checked)}
|
|
||||||
>
|
|
||||||
30天内自动登录
|
|
||||||
</Checkbox>
|
|
||||||
<span>
|
|
||||||
<a href="#" className={`${styles.forgetPassword}`}>忘记密码?</a>
|
|
||||||
{
|
|
||||||
!isRegisterMode &&
|
|
||||||
<a href=""
|
|
||||||
onClick={(event) => {
|
|
||||||
setIsRegisterMode(true)
|
|
||||||
event.preventDefault()
|
|
||||||
}}
|
|
||||||
>去注册?</a>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
isRegisterMode &&
|
|
||||||
<a href=""
|
|
||||||
onClick={(event) => {
|
|
||||||
setIsRegisterMode(false)
|
|
||||||
event.preventDefault()
|
|
||||||
}}
|
|
||||||
>去登录</a>
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div className={styles.socialLogin}>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
className={styles.socialButton}
|
||||||
size="large"
|
icon={<GoogleOutlined />}
|
||||||
className={styles.loginButton}
|
size="large"
|
||||||
block
|
disabled={true}
|
||||||
htmlType="submit"
|
>
|
||||||
disabled={isRegisterMode && isInitialized}
|
使用谷歌账号登录
|
||||||
>
|
</Button>
|
||||||
{
|
</div>
|
||||||
isRegisterMode ? (
|
<div style={{ height: "10px" }}></div>
|
||||||
isInitialized ? "暂不提供注册" : "注册"
|
<div className={styles.socialLogin}>
|
||||||
) : "登录"
|
<Button
|
||||||
}
|
className={styles.socialButton}
|
||||||
</Button>
|
icon={<QqOutlined />}
|
||||||
|
size="large"
|
||||||
|
disabled={true}
|
||||||
<Divider className={styles.divider}>或</Divider>
|
>
|
||||||
|
使用QQ账号登录
|
||||||
<div className={styles.socialLogin}>
|
</Button>
|
||||||
<Button
|
</div>
|
||||||
className={styles.socialButton}
|
</Form>
|
||||||
icon={<GoogleOutlined />}
|
</div>
|
||||||
size="large"
|
|
||||||
disabled={true}
|
|
||||||
>
|
|
||||||
使用谷歌账号登录
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div style={{ height: '10px' }}></div>
|
|
||||||
<div className={styles.socialLogin}>
|
|
||||||
<Button
|
|
||||||
className={styles.socialButton}
|
|
||||||
icon={<QqOutlined />}
|
|
||||||
size="large"
|
|
||||||
disabled={true}
|
|
||||||
>
|
|
||||||
使用QQ账号登录
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoginField {
|
interface LoginField {
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user