feat: 抽离 inputbar
This commit is contained in:
parent
2e12e2d93b
commit
40766106d8
|
|
@ -0,0 +1,17 @@
|
|||
.InputBar {
|
||||
.CenterCard {
|
||||
width: 50vw;
|
||||
padding: 18px 18px 0 18px;
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
padding-bottom: 24px,
|
||||
}
|
||||
|
||||
.Flex {
|
||||
flex: 1;
|
||||
|
||||
.ant-form-item {
|
||||
--ant-form-item-margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
import { ProForm, ProFormTextArea } from '@ant-design/pro-components';
|
||||
import { useModel, history } from '@umijs/max';
|
||||
import { Button, Divider, Dropdown, Flex, message, Tag, theme } from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import './index.less'
|
||||
import { FileSearchOutlined, GlobalOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { TweenOneGroup } from 'rc-tween-one';
|
||||
|
||||
export type InputBarEvent = {
|
||||
query: string;
|
||||
tools: Object[];
|
||||
}
|
||||
|
||||
export type InputBarSubmitHandler = (event: InputBarEvent) => void;
|
||||
|
||||
export type InputBarProps = {
|
||||
onSubmit?: InputBarSubmitHandler;
|
||||
}
|
||||
|
||||
export const InputBarToolItems = [{
|
||||
key: 'searchWeb',
|
||||
label: '搜索网站',
|
||||
color: '#55acee',
|
||||
icon: <GlobalOutlined />,
|
||||
}, {
|
||||
key: 'searchKB',
|
||||
label: '知识库',
|
||||
color: '#55acee',
|
||||
icon: <FileSearchOutlined />,
|
||||
children: [
|
||||
{ key: 'searchKB_kb1', label: '知识库1' },
|
||||
{ key: 'searchKB_kb2', label: '知识库2' },
|
||||
{ type: 'divider' },
|
||||
{ key: 'searchKB_add', label: '新增知识库' },
|
||||
],
|
||||
}]
|
||||
|
||||
const tagGroupStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap',
|
||||
gap: 8,
|
||||
marginBottom: 8,
|
||||
marginTop: 16,
|
||||
};
|
||||
|
||||
const InputBar = (props: InputBarProps) => {
|
||||
const textAreaRef = useRef<any>(null);
|
||||
const [tools, setTools] = useState<any>([]);
|
||||
const textAreaFieldProps: any = {
|
||||
ref: textAreaRef,
|
||||
autoSize: {
|
||||
minRows: 1,
|
||||
maxRows: 5,
|
||||
},
|
||||
variant: 'borderless',
|
||||
onPressEnter: (e: any) => {
|
||||
const { nativeEvent: { shiftKey, key } } = e;
|
||||
if (shiftKey && key === "Enter") {
|
||||
const { current: { resizableTextArea: { textArea } } } = textAreaRef;
|
||||
e.preventDefault();
|
||||
const text = textArea?.value;
|
||||
console.log({
|
||||
query: text,
|
||||
tools: tools,
|
||||
});
|
||||
props?.onSubmit && props.onSubmit({
|
||||
query: text,
|
||||
tools: tools?.map((e:any) => e.key),
|
||||
})
|
||||
textArea.value = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const itemOnClick = ({ key }: any) => {
|
||||
if (key === 'searchKB_add') {
|
||||
message.info("创建知识库")
|
||||
return;
|
||||
}
|
||||
const toolsKeys: string[] = tools?.map((e: any) => e.key);
|
||||
if (toolsKeys?.includes(key)) {
|
||||
setTools(tools.filter((e: any) => e.key !== key));
|
||||
return;
|
||||
}
|
||||
if (key.includes('kb')) {
|
||||
const kbObj = InputBarToolItems?.find(i => i.key === 'searchKB')
|
||||
const obj = kbObj?.children?.find(i => i.key === key);
|
||||
setTools([{ ...obj, icon: kbObj?.icon }, ...tools]);
|
||||
} else {
|
||||
const obj = InputBarToolItems?.find(i => i.key === key);
|
||||
setTools([obj, ...tools]);
|
||||
}
|
||||
}
|
||||
|
||||
const Tags = () => {
|
||||
if (tools?.length) {
|
||||
return (<>
|
||||
<Divider style={{ height: 1, margin: '8px 0' }} />
|
||||
<TweenOneGroup
|
||||
appear={false}
|
||||
style={tagGroupStyle}
|
||||
enter={{ scale: 0.8, opacity: 0, type: 'from', duration: 100 }}
|
||||
leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
|
||||
onEnd={(e) => {
|
||||
if (e.type === 'appear' || e.type === 'enter') {
|
||||
(e.target as any).style = 'display: inline-block';
|
||||
}
|
||||
}}
|
||||
>
|
||||
{tools?.map((e: any) => <Tag closable key={e.key} icon={e.icon} onClose={() => itemOnClick({ key: e.key })}>
|
||||
{e.label}
|
||||
</Tag>)}
|
||||
|
||||
</TweenOneGroup>
|
||||
</>)
|
||||
}
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='InputBar'>
|
||||
<div className='CenterCard'>
|
||||
<ProForm submitter={false}>
|
||||
<Flex align='flex-end'>
|
||||
<Dropdown menu={{ items: InputBarToolItems, onClick: itemOnClick }} placement='topCenter' arrow>
|
||||
<Button icon={<PlusOutlined />} type='text'></Button>
|
||||
</Dropdown>
|
||||
<div className='Flex'>
|
||||
<ProFormTextArea placeholder="按 Shift+Enter 进行提问" fieldProps={textAreaFieldProps} />
|
||||
</div>
|
||||
</Flex>
|
||||
</ProForm>
|
||||
<Tags />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InputBar;
|
||||
|
|
@ -1,129 +1,24 @@
|
|||
import { PageContainer, ProForm, ProFormTextArea } from '@ant-design/pro-components';
|
||||
import { useModel, history } from '@umijs/max';
|
||||
import { Button, Divider, Dropdown, Flex, message, Tag, theme } from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { useModel } from '@umijs/max';
|
||||
import { Flex, theme } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import './Welcome.less'
|
||||
import { FileSearchOutlined, GlobalOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { TweenOneGroup } from 'rc-tween-one';
|
||||
import HeaderModelSelect from '@/components/Custom/HeaderModelSelect';
|
||||
import type { ModelItem } from '@/components/Custom/HeaderModelSelect';
|
||||
|
||||
const tagGroupStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap',
|
||||
gap: 8,
|
||||
marginBottom: 8,
|
||||
marginTop: 16,
|
||||
};
|
||||
|
||||
import InputBar from '@/components/Custom/InputBar';
|
||||
|
||||
const Welcome: React.FC = () => {
|
||||
const { token } = theme.useToken();
|
||||
const { initialState } = useModel('@@initialState');
|
||||
const textAreaRef = useRef<any>(null);
|
||||
const [tools, setTools] = useState<any>([]);
|
||||
const [model, setModel] = useState<ModelItem | undefined>(undefined);
|
||||
const textAreaFieldProps: any = {
|
||||
ref: textAreaRef,
|
||||
autoSize: {
|
||||
minRows: 1,
|
||||
maxRows: 5,
|
||||
},
|
||||
variant: 'borderless',
|
||||
onPressEnter: (e: any) => {
|
||||
const { nativeEvent: { shiftKey, key } } = e;
|
||||
if (shiftKey && key === "Enter") {
|
||||
const { current: { resizableTextArea: { textArea } } } = textAreaRef;
|
||||
e.preventDefault();
|
||||
const text = textArea?.value;
|
||||
console.log(text);
|
||||
textArea.value = "";
|
||||
history.push('/chat');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const items = [{
|
||||
key: 'searchWeb',
|
||||
label: '搜索网站',
|
||||
color: '#55acee',
|
||||
icon: <GlobalOutlined />,
|
||||
}, {
|
||||
key: 'searchKB',
|
||||
label: '知识库',
|
||||
color: '#55acee',
|
||||
icon: <FileSearchOutlined />,
|
||||
children: [
|
||||
{ key: 'searchKB_kb1', label: '知识库1' },
|
||||
{ key: 'searchKB_kb2', label: '知识库2' },
|
||||
{ type: 'divider' },
|
||||
{ key: 'searchKB_add', label: '新增知识库' },
|
||||
],
|
||||
}]
|
||||
|
||||
const itemOnClick = ({ key }: any) => {
|
||||
if (key === 'searchKB_add') {
|
||||
message.info("创建知识库")
|
||||
return;
|
||||
}
|
||||
const toolsKeys: string[] = tools?.map((e: any) => e.key);
|
||||
if (toolsKeys?.includes(key)) {
|
||||
setTools(tools.filter((e: any) => e.key !== key));
|
||||
return;
|
||||
}
|
||||
if (key.includes('kb')) {
|
||||
const kbObj = items?.find(i => i.key === 'searchKB')
|
||||
const obj = kbObj?.children?.find(i => i.key === key);
|
||||
setTools([{ ...obj, icon: kbObj?.icon }, ...tools]);
|
||||
} else {
|
||||
const obj = items?.find(i => i.key === key);
|
||||
setTools([obj, ...tools]);
|
||||
}
|
||||
}
|
||||
|
||||
const Tags = () => {
|
||||
if (tools?.length) {
|
||||
return (<>
|
||||
<Divider style={{ height: 1, margin: '8px 0' }} />
|
||||
<TweenOneGroup
|
||||
appear={false}
|
||||
style={tagGroupStyle}
|
||||
enter={{ scale: 0.8, opacity: 0, type: 'from', duration: 100 }}
|
||||
leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
|
||||
onEnd={(e) => {
|
||||
if (e.type === 'appear' || e.type === 'enter') {
|
||||
(e.target as any).style = 'display: inline-block';
|
||||
}
|
||||
}}
|
||||
>
|
||||
{tools?.map((e: any) => <Tag closable key={e.key} icon={e.icon} onClose={() => itemOnClick({ key: e.key })}>
|
||||
{e.label}
|
||||
</Tag>)}
|
||||
|
||||
</TweenOneGroup>
|
||||
</>)
|
||||
}
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<PageContainer title={<HeaderModelSelect value={model} onChange={e => setModel(e)}/>} className='WelcomePage'>
|
||||
<Flex align='center' justify='center' vertical>
|
||||
<div className='PageHeader'></div>
|
||||
<div className='CenterCard'>
|
||||
<ProForm submitter={false}>
|
||||
<Flex align='flex-end'>
|
||||
<Dropdown menu={{ items, onClick: itemOnClick }} placement='topCenter' arrow>
|
||||
<Button icon={<PlusOutlined />} type='text'></Button>
|
||||
</Dropdown>
|
||||
<div className='Flex'>
|
||||
<ProFormTextArea placeholder="按 Shift+Enter 进行提问" fieldProps={textAreaFieldProps} />
|
||||
</div>
|
||||
</Flex>
|
||||
</ProForm>
|
||||
<Tags />
|
||||
</div>
|
||||
<InputBar onSubmit={e => {
|
||||
console.log(e);
|
||||
}}/>
|
||||
</Flex>
|
||||
</PageContainer>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,4 +2,12 @@
|
|||
.Flex {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.CenterCard {
|
||||
width: 50vw;
|
||||
padding: 18px 18px 0 18px;
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
padding-bottom: 24px,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,22 @@
|
|||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { PageContainer, ProForm, ProFormTextArea } from '@ant-design/pro-components';
|
||||
import type { ModelItem } from '@/components/Custom/HeaderModelSelect';
|
||||
import './index.less';
|
||||
import { useState } from 'react';
|
||||
import HeaderModelSelect from '@/components/Custom/HeaderModelSelect';
|
||||
import { Flex } from 'antd';
|
||||
import { Button, Dropdown, Flex } from 'antd';
|
||||
|
||||
export default () => {
|
||||
const [model, setModel] = useState<ModelItem | undefined>(undefined);
|
||||
|
||||
|
||||
return <PageContainer
|
||||
className='ChatPage'
|
||||
title={<HeaderModelSelect value={model} onChange={val => setModel(val)} />}
|
||||
>
|
||||
<Flex vertical>
|
||||
<div className='Flex'></div>
|
||||
|
||||
<div className='CenterCard'>
|
||||
</div>
|
||||
</Flex>
|
||||
</PageContainer>
|
||||
}
|
||||
Loading…
Reference in New Issue