Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: init bot create #10

Merged
merged 1 commit into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions src/app/chat/bot/create/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
'use client';

import React, { useState, } from 'react';

import ReturnBtn from '@/components/ReturnBtn';
import { useStyles } from './styles';
import { Button, Flex, Form, Upload, Input, Select, Tag, Dropdown } from 'antd';
import type { UploadFile, MenuProps } from 'antd';
import { LoadingOutlined, PlusOutlined, FolderFilled, LockOutlined, GlobalOutlined, LinkOutlined } from '@ant-design/icons';

interface AgentClassification {
value: string;
text: string;
}
const AGENT_CLASSIFICATION_MAP:AgentClassification[] = [
{ text: '通用对话', value: '通用对话'},
{ text: '工作学习', value: '工作学习'},
{ text: '内容创作', value: '内容创作'},
{ text: 'AI 绘画', value: 'AI 绘画'},
{ text: '影音生成', value: '影音生成'},
{ text: '角色扮演', value: '角色扮演'},
{ text: '生活趣味', value: '生活趣味'},
{ text: '其他', value: '其他'},
]
const normFile = (e: any) => {
if (Array.isArray(e)) {
return e;
}
if (!e?.fileList?.length) {
return [];
}
return [e?.fileList[e?.fileList?.length - 1]];
};

const getBase64 = (img: any, callback: (url: string) => void) => {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result as string));
reader.readAsDataURL(img);
};


const BotCreate = React.memo<BotCreateProps>(() => {
const { styles } = useStyles();
const [form] = Form.useForm()
const [loading, setLoading] = useState(false);
const [imageUrl, setImageUrl] = useState<string>();
const onClick: MenuProps['onClick'] = ({ key }) => {
form.validateFields().then(values => {
const params = {
values,
key
}
console.warn(params)
})
};

const items: MenuProps['items'] = [
{
label: <span>
<LockOutlined className={styles.menuIcon}/>
私密 · 仅自己可对话
</span>,
key: 'private',
},
{
label: <span>
<LinkOutlined className={styles.menuIcon}/>
通过链接访问 · 获得链接的用户可对话
</span>,
key: 'linked',
},
{
label: <span>
<GlobalOutlined className={styles.menuIcon}/>
公开 · 所有人可对话
</span>,
key: 'public',
},
];
return (
<div className={styles.BotCreate}>
<ReturnBtn
extra={
<Dropdown menu={{ items, onClick }} trigger={["click"]}>
<Button onClick={() => {
}} type="primary">保存</Button>
</Dropdown>
}
isLeftTitle
title="创建 AI 智能体"
/>
<Flex className={styles.content}>
<div className={styles.leftContent}>
<Form form={form} layout="vertical" requiredMark={false}>
<Form.Item getValueFromEvent={normFile} name="avatar" valuePropName="fileList">
<Upload
accept={'image/png, image/jpeg, image/jpg'}
beforeUpload={(file: any) => {
// const isLt10M = file.size / 1024 / 1024 <= 5;
// if (!isLt10M) {
// notification.warn({
// message: '不能大于 5MB!',
// });
// return Promise.reject();
// }
return Promise.resolve(file);
}}
className={styles.uploadAvatar}
customRequest={(options: any) => {
const { onSuccess, file } = options;
file.status = 'done';
onSuccess(file.uid);
getBase64(file as UploadFile, url => {
setLoading(false);
setImageUrl(url);
});
}}
listType="picture-circle"
onChange={({ file }) => {
file.status = 'done';
}}
showUploadList={false}
>
{imageUrl ? (
<img alt="avatar" src={imageUrl} style={{ width: '100%' }} />
) : (
<div className={styles.uploadText}>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div>上传头像</div>
</div>
)}
</Upload>
</Form.Item>
<Form.Item
label="智能体名称"
name="nickname"
rules={[{ required: true, message: '请输入 AI 智能体名称' }]}
>
<Input placeholder="请输入 AI 智能体名称" size="large" />
</Form.Item>
<Form.Item
label="设定"
name="设定"
rules={[{ required: true, message: '请输入 AI 智能体角色设定' }]}
>
<Input.TextArea placeholder={`请输入 AI 智能体角色设定,如这个智能体有什么作用?他能做什么事情?它应该有哪些注意事项?
示例:
你是一个 AI 产品专家,你熟悉 AI 大模型的全生命周期,对于大模型的开发、训练、评估、调优、部署以及使用等你都非常了解,你知晓所有 AI 相关的概念。你可以帮助用户提出设计理念、完善设计思路、指出设计问题等,但是你是一个严谨的专家,你不会编造回答。`}
rows={8}
size="large"
/>
</Form.Item>
<Form.Item
label="智能体分类"
name="智能体分类"
rules={[{ required: true, message: '请选择 AI 智能体分类' }]}
>
<Select placeholder="请选择 AI 智能体分类" size="large" >
{
AGENT_CLASSIFICATION_MAP.map((item: AgentClassification) => <Select.Option key={item.value}>
{item.text}
</Select.Option>)
}
</Select>
</Form.Item>
<Form.Item
label="智能体描述"
name="智能体描述"
rules={[{ required: true, message: '请输入 AI 智能体描述' }]}
>
<Input placeholder="请输入 AI 智能体描述" size="large" />
</Form.Item>
<Form.Item
label="知识库"
name="知识库"
rules={[{ required: true, message: '请上传知识库文件' }]}
>
<Upload
beforeUpload={() => {
return false;
}}
showUploadList={true}
>
<div className={styles.uploadFile}>
<FolderFilled className={styles.uploadIcon}/>上传文件
</div>
</Upload>
</Form.Item>
</Form>
</div>
<div className={styles.rightContent}>
<Tag bordered={false} className={styles.tag}>预览</Tag>
</div>
</Flex>
</div>
);
});

export default BotCreate;
57 changes: 57 additions & 0 deletions src/app/chat/bot/create/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { createStyles } from 'antd-style';

export const useStyles = createStyles(({ token }) => {
return ({
BotCreate: {
'width': '100%',
'background': token.colorBgLayout,
'position': 'relative',
},
content: {
paddingTop: '64px',
paddingBottom: '24px',
'.ant-input, .ant-select-selector': {
borderColor: 'transparent !important',
},
'.ant-input:focus, .ant-select-focused >.ant-select-selector': {
borderColor: `${token.colorPrimaryActive} !important`,
},
},
uploadText: {
'border': 0,
'background': 'none',
'& > div': {
marginTop: 4,
},
},
leftContent: {
width: '40%',
padding: "24px 40px 0 60px"
},
rightContent: {
width: '60%',
background: token.colorWhite,
},
tag:{
position: "relative",
top: "20px",
left: "40px",
},
uploadAvatar: {
textAlign: 'center'
},
uploadFile: {
background: token.colorWhite,
color: token.colorTextPlaceholder,
padding: "10px 20px 10px 20px",
borderRadius: token.borderRadius,
},
uploadIcon: {
marginRight: '4px',
},
menuIcon: {
marginRight: '4px',
color: token.colorPrimary
},
})
});
19 changes: 17 additions & 2 deletions src/components/ReturnBtn/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,37 @@ export const useStyles = createStyles(() => ({
flexDirection: 'column',
marginLeft: '-40px',
},
leftTitle: {
fontWeight: 590,
fontSize: 16,
},
layout: {
width: '100%'
}
}));

interface ReturnBtnProps {
to?: string;
title?: string;
isLeftTitle?: boolean;
extra?: React.ReactNode;
}

const ReturnBtn = React.memo<ReturnBtnProps>(props => {
const { to } = props;
const { to, extra, isLeftTitle } = props;
const { styles } = useStyles();
return (
<Flex align={'center'} className={styles.returnBtn}>
<Link href={to || '/chat'}>
<ActionIcon className={styles.btn} icon={ChevronLeft} />
</Link>
<div className={styles.title}>{props.title}</div>
{isLeftTitle
? <Flex align="center" className={styles.layout} justify='space-between'>
<div className={styles.leftTitle}>{props.title}</div>
{extra}
</Flex>
: <div className={styles.title}>{props.title}</div>
}
</Flex>
);
});
Expand Down
2 changes: 1 addition & 1 deletion src/layout/AppLayout/SideBar/SideBarHeader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const SidebarHeader = () => {
<Link href="/chat">AgileGPT</Link>
</div>
<Flex className={styles.btns} vertical>
<Link className={styles.linkItem} href="/">
<Link className={styles.linkItem} href="/chat/bot/create">
<CodepenCircleOutlined />
<span className={styles.btnName}>创建智能体</span>
<ChevronRight color={'rgb(204, 204, 204)'} />
Expand Down
Loading