feat: layout the knowledge list page and modify the page switching button in the header (#48)

* feat: remove unnecessary 'loading' fields from other files

* feat: layout the knowledge list page

* feat: modify the page switching button in the header
This commit is contained in:
balibabu 2024-01-31 19:29:57 +08:00 committed by GitHub
parent 362ec6c364
commit af3ef26977
29 changed files with 940 additions and 786 deletions

View File

@ -16,6 +16,11 @@ export default defineConfig({
},
plugins: ['@react-dev-inspector/umi4-plugin', '@umijs/plugins/dist/dva'],
dva: {},
lessLoader: {
modifyVars: {
hack: `true; @import "~@/less/variable.less";`,
},
},
// proxy: {
// '/v1': {
// 'target': 'http://54.80.112.79:9380/',

View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 10H15M2.5 5H17.5M7.5 15H12.5" stroke="#344054" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 234 B

View File

@ -0,0 +1,12 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1164_5493)">
<path
d="M3.08282 12.894V10.0795M3.08282 4.45031V1.63574M1.67554 3.04303H4.49011M1.67554 11.4867H4.49011M7.86759 2.19866L6.8914 4.73676C6.73265 5.1495 6.65328 5.35588 6.52984 5.52947C6.42045 5.68332 6.28603 5.81774 6.13218 5.92713C5.95858 6.05057 5.75221 6.12994 5.33947 6.28869L2.80137 7.26488L5.33947 8.24107C5.75221 8.39982 5.95859 8.4792 6.13218 8.60263C6.28603 8.71203 6.42045 8.84645 6.52984 9.0003C6.65328 9.17389 6.73265 9.38026 6.8914 9.79301L7.86759 12.3311L8.84378 9.79301C9.00253 9.38026 9.08191 9.17389 9.20534 9.0003C9.31474 8.84645 9.44916 8.71203 9.60301 8.60263C9.7766 8.4792 9.98297 8.39982 10.3957 8.24107L12.9338 7.26488L10.3957 6.28869C9.98297 6.12994 9.7766 6.05057 9.60301 5.92713C9.44916 5.81774 9.31474 5.68332 9.20534 5.52947C9.08191 5.35588 9.00253 5.1495 8.84378 4.73676L7.86759 2.19866Z"
stroke="black" stroke-width="1.68874" stroke-linecap="round" stroke-linejoin="round" />
</g>
<defs>
<clipPath id="clip0_1164_5493">
<rect width="13.5099" height="13.5099" fill="white" transform="translate(0.549805 0.509888)" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M18.1777 8C23.2737 8 23.2737 16 18.1777 16C13.0827 16 11.0447 8 5.43875 8C0.85375 8 0.85375 16 5.43875 16C11.0447 16 13.0828 8 18.1788 8H18.1777Z"
stroke="#7F56D9" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 362 B

View File

@ -0,0 +1,11 @@
<svg width="24" height="6" viewBox="0 0 24 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M12 4.25C12.6904 4.25 13.25 3.69036 13.25 3C13.25 2.30964 12.6904 1.75 12 1.75C11.3096 1.75 10.75 2.30964 10.75 3C10.75 3.69036 11.3096 4.25 12 4.25Z"
stroke="#98A2B3" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
<path
d="M20.75 4.25C21.4404 4.25 22 3.69036 22 3C22 2.30964 21.4404 1.75 20.75 1.75C20.0596 1.75 19.5 2.30964 19.5 3C19.5 3.69036 20.0596 4.25 20.75 4.25Z"
stroke="#98A2B3" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
<path
d="M3.25 4.25C3.94036 4.25 4.5 3.69036 4.5 3C4.5 2.30964 3.94036 1.75 3.25 1.75C2.55964 1.75 2 2.30964 2 3C2 3.69036 2.55964 4.25 3.25 4.25Z"
stroke="#98A2B3" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 883 B

View File

@ -0,0 +1,46 @@
.tag {
height: 40px;
padding: 0 30px;
margin: 0 5px;
border: 1px solid #000;
border-radius: 10px;
cursor: pointer;
}
.checked {
color: #1677ff;
border-color: #1677ff;
}
.appIcon {
vertical-align: middle;
}
.appName {
vertical-align: middle;
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px;
}
.radioGroup {
background: rgba(249, 249, 249, 1) !important;
& > label {
border: 0 !important;
&::before {
display: none !important;
}
}
:global(.ant-radio-button-wrapper-checked) {
border-radius: 6px !important;
}
}
.ant-radio-button-wrapper-checked {
border-radius: 6px !important;
}
.radioButtonIcon {
vertical-align: middle;
}

View File

@ -0,0 +1,74 @@
import { ReactComponent as StarIon } from '@/assets/svg/chat-star.svg';
import { ReactComponent as Logo } from '@/assets/svg/logo.svg';
import { Layout, Radio, Space, theme } from 'antd';
import styles from './index.less';
import { useMemo } from 'react';
import { useLocation, useNavigate } from 'umi';
import User from '../user';
const { Header } = Layout;
const RagHeader = () => {
const {
token: { colorBgContainer },
} = theme.useToken();
const navigate = useNavigate();
const { pathname } = useLocation();
const tagsData = [
{ path: '/knowledge', name: 'knowledge' },
{ path: '/chat', name: 'chat' },
{ path: '/file', name: 'file' },
];
const currentPath = useMemo(() => {
return tagsData.find((x) => x.path === pathname)?.name || 'knowledge';
}, [pathname]);
const handleChange = (path: string) => {
navigate(path);
};
return (
<Header
style={{
padding: '0 16px',
background: colorBgContainer,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
height: '72px',
}}
>
<Space size={12}>
<Logo className={styles.appIcon}></Logo>
<label className={styles.appName}>Infinity flow</label>
</Space>
<Space size={[0, 8]} wrap>
<Radio.Group
defaultValue="a"
buttonStyle="solid"
className={styles.radioGroup}
value={currentPath}
>
{tagsData.map((item) => (
<Radio.Button
value={item.name}
onClick={() => handleChange(item.path)}
>
<Space>
<StarIon className={styles.radioButtonIcon}></StarIon>
{item.name}
</Space>
</Radio.Button>
))}
</Radio.Group>
</Space>
<User></User>
</Header>
);
};
export default RagHeader;

View File

@ -18,16 +18,6 @@ body {
margin: 0;
}
.tag {
height: 40px;
padding: 0 30px;
margin: 0 5px;
border: 1px solid #000;
border-radius: 10px;
cursor: pointer;
}
.checked {
color: #1677ff;
border-color: #1677ff;
.divider {
margin: 0;
}

View File

@ -1,74 +1,26 @@
import logo from '@/assets/logo.png';
import { Layout, Space, theme } from 'antd';
import classnames from 'classnames';
import React, { useEffect, useState } from 'react';
import { Divider, Layout, theme } from 'antd';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Outlet, useLocation, useNavigate } from 'umi';
import { Outlet } from 'umi';
import '../locales/config';
import User from './components/user';
import Header from './components/header';
import styles from './index.less';
const { Header, Content } = Layout;
const { Content } = Layout;
const App: React.FC = (props) => {
const App: React.FC = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
const [current, setCurrent] = useState('knowledge');
const location = useLocation();
useEffect(() => {
if (location.pathname !== '/') {
const path = location.pathname.split('/');
// setCurrent(path[1]);
}
console.log(location.pathname.split('/'));
}, [location.pathname]);
const handleChange = (path: string) => {
// setCurrent(path)
navigate(path);
};
const tagsData = [
{ path: '/knowledge', name: 'knowledge' },
{ path: '/chat', name: 'chat' },
{ path: '/file', name: 'file' },
];
return (
<Layout className={styles.layout}>
<Layout>
<Header
style={{
padding: '0 8px',
background: colorBgContainer,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<img src={logo} alt="" style={{ height: 30, width: 30 }} />
<Space size={[0, 8]} wrap>
{tagsData.map((item) => (
<span
key={item.name}
className={classnames(styles['tag'], {
[styles['checked']]: current === item.name,
})}
onClick={() => handleChange(item.path)}
>
{item.name}
</span>
))}
</Space>
<User></User>
</Header>
<Header></Header>
<Divider orientationMargin={0} className={styles.divider} />
<Content
style={{
margin: '24px 16px',
minHeight: 280,
background: colorBgContainer,
borderRadius: borderRadiusLG,

View File

@ -0,0 +1 @@
@fontWeight600: 600;

View File

@ -1,5 +1,4 @@
import { Button, Result } from 'antd';
import React from 'react';
import { history } from 'umi';
const NoFoundPage = () => {

View File

@ -2,7 +2,6 @@ import { DvaModel } from 'umi';
export interface kAModelState {
isShowPSwModal: boolean;
isShowTntModal: boolean;
loading: boolean;
tenantIfo: any;
activeKey: string;
id: string;
@ -14,7 +13,6 @@ const model: DvaModel<kAModelState> = {
state: {
isShowPSwModal: false,
isShowTntModal: false,
loading: false,
tenantIfo: {},
activeKey: 'setting',
id: '',

View File

@ -1,40 +1,43 @@
// @import '~@/less/variable.less';
.knowledge {
padding: 24px;
padding: 48px 60px;
}
.container {
height: 100px;
display: flex;
flex-direction: column;
justify-content: space-between;
.content {
.topWrapper {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding-bottom: 72px;
.context {
flex: 1;
.title {
font-family: Inter;
font-size: 30px;
font-style: normal;
font-weight: @fontWeight600;
line-height: 38px;
color: rgba(16, 24, 40, 1);
}
.description {
font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 24px;
color: rgba(71, 84, 103, 1);
}
.footer {
height: 20px;
.text {
margin-left: 10px;
}
}
.topButton {
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: @fontWeight600;
line-height: 20px;
}
.card {
:global {
.ant-card-body {
padding: 10px;
margin: 0;
.filterButton {
display: flex;
align-items: center;
.topButton();
}
margin-bottom: 10px;
}
cursor: pointer;
}

View File

@ -1,13 +1,10 @@
import { formatDate } from '@/utils/date';
import {
DeleteOutlined,
MinusSquareOutlined,
PlusOutlined,
} from '@ant-design/icons';
import { Card, Col, FloatButton, Popconfirm, Row } from 'antd';
import { ReactComponent as FilterIcon } from '@/assets/filter.svg';
import { PlusOutlined } from '@ant-design/icons';
import { Button, Col, Row, Space } from 'antd';
import { useCallback, useEffect } from 'react';
import { useDispatch, useNavigate, useSelector } from 'umi';
import styles from './index.less';
import KnowledgeCard from './knowledge-card';
const Knowledge = () => {
const dispatch = useDispatch();
@ -22,32 +19,37 @@ const Knowledge = () => {
});
}, []);
const confirm = (id: string) => {
dispatch({
type: 'knowledgeModel/rmKb',
payload: {
kb_id: id,
},
});
};
const handleAddKnowledge = () => {
navigate(`add/setting?activeKey=setting`);
};
const handleEditKnowledge = (id: string) => {
navigate(`add/setting?activeKey=file&id=${id}`);
};
useEffect(() => {
fetchList();
}, [fetchList]);
return (
<>
<div className={styles.knowledge}>
<FloatButton
onClick={handleAddKnowledge}
icon={<PlusOutlined />}
<div className={styles.topWrapper}>
<div>
<span className={styles.title}>Welcome back, Zing</span>
<p className={styles.description}>
Which database are we going to use today?
</p>
</div>
<Space size={'large'}>
<Button icon={<FilterIcon />} className={styles.filterButton}>
Filters
</Button>
<Button
type="primary"
style={{ right: 24, top: 100 }}
/>
icon={<PlusOutlined />}
onClick={handleAddKnowledge}
className={styles.topButton}
>
Create knowledge base
</Button>
</Space>
</div>
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 32 }}>
{data.map((item: any) => {
return (
@ -59,61 +61,12 @@ const Knowledge = () => {
md={8}
lg={6}
>
<Card
className={styles.card}
onClick={() => {
handleEditKnowledge(item.id);
}}
>
<div className={styles.container}>
<div className={styles.content}>
<span className={styles.context}>{item.name}</span>
<span className={styles.delete}>
<Popconfirm
title="Delete the task"
description="Are you sure to delete this task?"
onConfirm={(e: any) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
confirm(item.id);
}}
okText="Yes"
cancelText="No"
>
<DeleteOutlined
onClick={(e) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}}
/>
</Popconfirm>
</span>
</div>
<div className={styles.footer}>
<span className={styles.text}>
<MinusSquareOutlined />
{item.doc_num}
</span>
<span className={styles.text}>
<MinusSquareOutlined />
{item.chunk_num}
</span>
<span className={styles.text}>
<MinusSquareOutlined />
{item.token_num}
</span>
<span style={{ float: 'right' }}>
{formatDate(item.update_date)}
</span>
</div>
</div>
</Card>
<KnowledgeCard item={item}></KnowledgeCard>
</Col>
);
})}
</Row>
</div>
</>
);
};

View File

@ -0,0 +1,73 @@
.container {
height: 251px;
display: flex;
flex-direction: column;
justify-content: space-between;
.content {
display: flex;
justify-content: space-between;
.context {
flex: 1;
}
}
.footer {
// text-align: left;
}
.footerTop {
padding-bottom: 2px;
}
}
.card {
border-radius: 12px;
border: 1px solid rgba(234, 236, 240, 1);
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
padding: 24px;
min-width: 300px;
cursor: pointer;
.titleWrapper {
// flex: 1;
.title {
font-size: 24px;
line-height: 32px;
font-weight: 600;
color: rgba(0, 0, 0, 0.88);
}
.description {
font-size: 12px;
font-weight: 600;
line-height: 20px;
color: rgba(0, 0, 0, 0.45);
}
}
:global {
.ant-card-body {
padding: 0;
margin: 0;
}
}
.bottom {
display: flex;
align-items: center;
justify-content: space-between;
}
.bottomLeft {
vertical-align: middle;
}
.leftIcon {
margin-right: 10px;
font-size: 18px;
vertical-align: middle;
}
.rightText {
font-size: 12px;
font-weight: 600;
color: rgba(0, 0, 0, 0.65);
vertical-align: middle;
}
}

View File

@ -0,0 +1,123 @@
import { ReactComponent as MoreIcon } from '@/assets/svg/more.svg';
import { formatDate } from '@/utils/date';
import {
AntDesignOutlined,
CalendarOutlined,
DeleteOutlined,
FileTextOutlined,
UserOutlined,
} from '@ant-design/icons';
import { Avatar, Card, Dropdown, MenuProps, Space, Tooltip } from 'antd';
import { MouseEvent } from 'react';
import { useDispatch, useNavigate } from 'umi';
import styles from './index.less';
interface IProps {
item: any;
}
const KnowledgeCard = ({ item }: IProps) => {
const navigate = useNavigate();
const dispatch = useDispatch();
const handleDelete = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
};
const items: MenuProps['items'] = [
{
key: '1',
label: (
<Space>
<DeleteOutlined onClick={handleDelete} />
</Space>
),
},
];
const confirm = (id: string) => {
dispatch({
type: 'knowledgeModel/rmKb',
payload: {
kb_id: id,
},
});
};
const handleCardClick = () => {
navigate(`add/setting?activeKey=file&id=${item.id}`);
};
const onConfirmDelete = (e?: MouseEvent<HTMLElement>) => {
e?.stopPropagation();
e?.nativeEvent.stopImmediatePropagation();
confirm(item.id);
};
return (
<Card className={styles.card} onClick={handleCardClick}>
<div className={styles.container}>
<div className={styles.content}>
<Avatar size={34} icon={<UserOutlined />} />
<span className={styles.delete}>
{/* <Popconfirm
title="Delete the task"
description="Are you sure to delete this task?"
onConfirm={onConfirmDelete}
okText="Yes"
cancelText="No"
>
<DeleteOutlined onClick={handleDelete} />
</Popconfirm> */}
<Dropdown menu={{ items }}>
<MoreIcon />
</Dropdown>
</span>
</div>
<div className={styles.titleWrapper}>
<span className={styles.title}>{item.name}</span>
<p>A comprehensive knowledge base for crafting effective resumes.</p>
</div>
<div className={styles.footer}>
<div className={styles.footerTop}>
<div className={styles.bottomLeft}>
<FileTextOutlined className={styles.leftIcon} />
<span className={styles.rightText}>
<Space>{item.doc_num}</Space>
</span>
</div>
</div>
<div className={styles.bottom}>
<div className={styles.bottomLeft}>
<CalendarOutlined className={styles.leftIcon} />
<span className={styles.rightText}>
{formatDate(item.update_date)}
</span>
</div>
<Avatar.Group size={25}>
<Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=1" />
<a href="https://ant.design">
<Avatar style={{ backgroundColor: '#f56a00' }}>K</Avatar>
</a>
<Tooltip title="Ant User" placement="top">
<Avatar
style={{ backgroundColor: '#87d068' }}
icon={<UserOutlined />}
/>
</Tooltip>
<Avatar
style={{ backgroundColor: '#1677ff' }}
icon={<AntDesignOutlined />}
/>
</Avatar.Group>
</div>
</div>
</div>
</Card>
);
};
export default KnowledgeCard;

View File

@ -19,7 +19,7 @@ const model: DvaModel<KnowledgeModelState> = {
},
},
effects: {
*rmKb({ payload = {}, callback }, { call, put }) {
*rmKb({ payload = {} }, { call, put }) {
const { data } = yield call(kbService.rmKb, payload);
const { retcode } = data;
if (retcode === 0) {

View File

@ -12,6 +12,8 @@ const Login = () => {
(state) => state.loading.effects,
);
// TODO: When the server address request is not accessible, the value of dva-loading always remains true.
const signLoading =
effectsLoading['loginModel/login'] || effectsLoading['loginModel/register'];

View File

@ -32,10 +32,8 @@ const model: DvaModel<LoginModelState> = {
},
effects: {
*login({ payload = {} }, { call, put }) {
console.log(111, payload);
const { data, response } = yield call(userService.login, payload);
const { retcode, data: res, retmsg } = data;
console.log();
const { retcode, data: res } = data;
const authorization = response.headers.get(Authorization);
if (retcode === 0) {
message.success('登录成功!');

View File

@ -1,66 +1,53 @@
import { connect, Dispatch } from 'umi';
import i18n from 'i18next';
import { useTranslation, Trans } from 'react-i18next'
import { Input, Modal, Form } from 'antd'
import { rsaPsw } from '@/utils'
import styles from './index.less';
import { FC } from 'react';
import { rsaPsw } from '@/utils';
import { Form, Input, Modal } from 'antd';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'umi';
type FieldType = {
newPassword?: string;
password?: string;
};
interface CPwModalProps {
dispatch: Dispatch;
settingModel: any
}
const Index: FC<CPwModalProps> = ({ settingModel, dispatch }) => {
const { isShowPSwModal } = settingModel
const { t } = useTranslation()
const CpwModal = () => {
const dispatch = useDispatch();
const settingModel = useSelector((state: any) => state.settingModel);
const { isShowPSwModal } = settingModel;
const { t } = useTranslation();
const [form] = Form.useForm();
const handleCancel = () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowPSwModal: false
}
isShowPSwModal: false,
},
});
};
const [form] = Form.useForm()
const handleOk = async () => {
try {
const values = await form.validateFields();
var password = rsaPsw(values.password)
var new_password = rsaPsw(values.newPassword)
var password = rsaPsw(values.password);
var new_password = rsaPsw(values.newPassword);
dispatch({
type: 'settingModel/setting',
payload: {
password,
new_password
new_password,
},
callback: () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowPSwModal: false
}
});
dispatch({
type: 'settingModel/getUserInfo',
payload: {
}
});
}
});
} catch (errorInfo) {
console.log('Failed:', errorInfo);
}
};
return (
<Modal title="Basic Modal" open={isShowPSwModal} onOk={handleOk} onCancel={handleCancel}>
<Modal
title="Basic Modal"
open={isShowPSwModal}
onOk={handleOk}
onCancel={handleCancel}
>
<Form
form={form}
labelCol={{ span: 8 }}
@ -78,15 +65,14 @@ const Index: FC<CPwModalProps> = ({ settingModel, dispatch }) => {
<Form.Item<FieldType>
label="新密码"
name="newPassword"
rules={[{ required: true, message: 'Please input your newPassword!' }]}
rules={[
{ required: true, message: 'Please input your newPassword!' },
]}
>
<Input.Password />
</Form.Item>
</Form>
</Modal>
);
}
export default connect(({ settingModel, loading }) => ({ settingModel, loading }))(Index);
};
export default CpwModal;

View File

@ -1,15 +1,12 @@
import { connect, Dispatch } from 'umi';
import i18n from 'i18next';
import { useTranslation, Trans } from 'react-i18next'
import { useTranslation } from 'react-i18next';
import { useEffect, useState } from 'react';
import styles from './index.less';
import type { ColumnsType } from 'antd/es/table';
import { useEffect, useState, FC } from 'react';
import { RadarChartOutlined } from '@ant-design/icons';
import { ProCard } from '@ant-design/pro-components';
import { Button, Tag, Row, Col, Card } from 'antd';
import { Button, Card, Col, Row, Tag } from 'antd';
import { useDispatch, useSelector } from 'umi';
interface DataType {
key: React.Key;
@ -18,74 +15,29 @@ interface DataType {
address: string;
description: string;
}
interface ListProps {
dispatch: Dispatch;
settingModel: any
}
const Index: FC<ListProps> = ({ settingModel, dispatch }) => {
const { llmInfo = {}, factoriesList, myLlm = [] } = settingModel
const { OpenAI = [], tongyi = [] } = llmInfo
console.log(OpenAI)
const SettingList = () => {
const dispatch = useDispatch();
const settingModel = useSelector((state: any) => state.settingModel);
const { llmInfo = {}, factoriesList, myLlm = [] } = settingModel;
const { OpenAI = [], tongyi = [] } = llmInfo;
const [collapsed, setCollapsed] = useState(true);
const { t } = useTranslation()
const columns: ColumnsType<DataType> = [
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Age', dataIndex: 'age', key: 'age' },
{
title: 'Action',
dataIndex: '',
key: 'x',
render: () => <a>Delete</a>,
},
];
const { t } = useTranslation();
useEffect(() => {
dispatch({
type: 'settingModel/factories_list',
payload: {
},
payload: {},
});
dispatch({
type: 'settingModel/llm_list',
payload: {
},
payload: {},
});
dispatch({
type: 'settingModel/my_llm',
payload: {
},
payload: {},
});
}, [])
const data: DataType[] = [
{
key: 1,
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.',
},
{
key: 2,
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.',
},
{
key: 3,
name: 'Not Expandable',
age: 29,
address: 'Jiangsu No. 1 Lake Park',
description: 'This not expandable',
},
{
key: 4,
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
description: 'My name is Joe Black, I am 32 years old, living in Sydney No. 1 Lake Park.',
},
];
}, []);
return (
<div
@ -97,9 +49,9 @@ const Index: FC<ListProps> = ({ settingModel, dispatch }) => {
gap: 12,
}}
>
{
myLlm.map((item: any) => {
return (<ProCard
{myLlm.map((item: any) => {
return (
<ProCard
key={item.llm_factory}
// title={<div>可折叠-图标自定义</div>}
collapsibleIconRender={({
@ -107,28 +59,37 @@ const Index: FC<ListProps> = ({ settingModel, dispatch }) => {
}: {
collapsed: boolean;
}) => {
return (<div>
<h3><RadarChartOutlined />{item.llm_factory}</h3>
<div>{item.tags.split(',').map((d: string) => {
return <Tag key={d}>{d}</Tag>
})}</div>
{
buildInCollapsed ? <span>{OpenAI.length}</span> : <span>{OpenAI.length} </span>
}
</div>)
return (
<div>
<h3>
<RadarChartOutlined />
{item.llm_factory}
</h3>
<div>
{item.tags.split(',').map((d: string) => {
return <Tag key={d}>{d}</Tag>;
})}
</div>
{buildInCollapsed ? (
<span>{OpenAI.length}</span>
) : (
<span>{OpenAI.length} </span>
)}
</div>
);
}}
extra={
<Button
size="small"
type='link'
type="link"
onClick={(e) => {
e.stopPropagation();
dispatch({
type: 'settingModel/updateState',
payload: {
llm_factory: item.llm_factory,
isShowSAKModal: true
}
isShowSAKModal: true,
},
});
}}
>
@ -139,58 +100,47 @@ const Index: FC<ListProps> = ({ settingModel, dispatch }) => {
headerBordered
collapsible
defaultCollapsed
>
{/* <ul>
{OpenAI.map(item => {
return <li key={item.llm_name}>
<span>{item.llm_name}</span>
<span className={styles[item.available ? 'statusAvailable' : 'statusDisaabled']}>
</span>
</li>
></ProCard>
);
})}
</ul> */}
</ProCard>)
})
}
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 32 }}>
{
factoriesList.map((item: any) => {
return (<Col key={item.name} xs={24} sm={12} md={8} lg={6}>
<Card title={item.name} bordered={false} extra={
{factoriesList.map((item: any) => {
return (
<Col key={item.name} xs={24} sm={12} md={8} lg={6}>
<Card
title={item.name}
bordered={false}
extra={
<Button
size="small"
type='link'
type="link"
onClick={(e) => {
e.stopPropagation();
dispatch({
type: 'settingModel/updateState',
payload: {
llm_factory: item.name,
isShowSAKModal: true
}
isShowSAKModal: true,
},
});
}}
>
</Button>
}>
<div>
{
item.tags.split(',').map((d: string) => {
return <Tag key={d}>{d}</Tag>
})
}
>
<div>
{item.tags.split(',').map((d: string) => {
return <Tag key={d}>{d}</Tag>;
})}
</div>
</Card>
</Col>)
})
}
</Col>
);
})}
</Row>
</div>
);
}
export default connect(({ settingModel, loading }) => ({ settingModel, loading }))(Index);
};
export default SettingList;

View File

@ -1,30 +1,26 @@
import { connect, Dispatch } from 'umi';
import i18n from 'i18next';
import { FC } from 'react'
import { useTranslation, Trans } from 'react-i18next'
import { Input, Modal, Form } from 'antd'
import styles from './index.less';
import { Form, Input, Modal } from 'antd';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'umi';
type FieldType = {
api_key?: string;
};
interface SAKModalProps {
dispatch: Dispatch;
settingModel: any
}
const Index: FC<SAKModalProps> = ({ settingModel, dispatch }) => {
const { isShowSAKModal, llm_factory } = settingModel
console.log(llm_factory)
const { t } = useTranslation()
const SakModal = () => {
const dispatch = useDispatch();
const settingModel = useSelector((state: any) => state.settingModel);
const { isShowSAKModal, llm_factory } = settingModel;
const { t } = useTranslation();
const [form] = Form.useForm();
const handleCancel = () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowSAKModal: false
}
isShowSAKModal: false,
},
});
};
const [form] = Form.useForm()
const handleOk = async () => {
try {
const values = await form.validateFields();
@ -33,31 +29,21 @@ const Index: FC<SAKModalProps> = ({ settingModel, dispatch }) => {
type: 'settingModel/set_api_key',
payload: {
api_key: values.api_key,
llm_factory: llm_factory
llm_factory: llm_factory,
},
callback: () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowSAKModal: false
}
});
// dispatch({
// type: 'settingModel/getUserInfo',
// payload: {
// }
// });
}
});
} catch (errorInfo) {
console.log('Failed:', errorInfo);
}
};
return (
<Modal title="Basic Modal" open={isShowSAKModal} onOk={handleOk} onCancel={handleCancel}>
<Modal
title="Basic Modal"
open={isShowSAKModal}
onOk={handleOk}
onCancel={handleCancel}
>
<Form
form={form}
name="validateOnly"
@ -73,11 +59,8 @@ const Index: FC<SAKModalProps> = ({ settingModel, dispatch }) => {
>
<Input />
</Form.Item>
</Form>
</Modal>
);
}
export default connect(({ settingModel, loading }) => ({ settingModel, loading }))(Index);
};
export default SakModal;

View File

@ -1,69 +1,61 @@
import { connect, Dispatch } from 'umi';
import { FC } from 'react'
import i18n from 'i18next';
import { useTranslation, Trans } from 'react-i18next'
import { Input, Modal, Form, Select } from 'antd'
import styles from './index.less';
import { Form, Modal, Select } from 'antd';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'umi';
type FieldType = {
embd_id?: string;
img2txt_id?: string;
llm_id?: string;
asr_id?: string
asr_id?: string;
};
interface SSModalProps {
dispatch: Dispatch;
settingModel: any
}
const Index: FC<SSModalProps> = ({ settingModel, dispatch }) => {
const { isShowSSModal, llmInfo = {}, tenantIfo } = settingModel
const { t } = useTranslation()
const SsModal = () => {
const dispatch = useDispatch();
const settingModel = useSelector((state: any) => state.settingModel);
const { isShowSSModal, llmInfo = {}, tenantIfo } = settingModel;
const [form] = Form.useForm();
const { t } = useTranslation();
const handleCancel = () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowSSModal: false
}
isShowSSModal: false,
},
});
};
const [form] = Form.useForm()
const handleOk = async () => {
try {
const values = await form.validateFields();
console.log(values)
dispatch({
const retcode = await dispatch<any>({
type: 'settingModel/set_tenant_info',
payload: {
...values,
tenant_id: tenantIfo.tenant_id,
},
callback: () => {
});
retcode === 0 &&
dispatch({
type: 'settingModel/updateState',
payload: {
isShowSSModal: false
}
isShowSSModal: false,
},
});
// dispatch({
// type: 'settingModel/getUserInfo',
// payload: {
// }
// });
}
});
} catch (errorInfo) {
console.log('Failed:', errorInfo);
}
};
const handleChange = () => {
}
const handleChange = () => {};
return (
<Modal title="Basic Modal" open={isShowSSModal} onOk={handleOk} onCancel={handleCancel}>
<Modal
title="Basic Modal"
open={isShowSSModal}
onOk={handleOk}
onCancel={handleCancel}
>
<Form
form={form}
name="validateOnly"
@ -78,15 +70,16 @@ const Index: FC<SSModalProps> = ({ settingModel, dispatch }) => {
name="embd_id"
rules={[{ required: true, message: 'Please input value' }]}
initialValue={tenantIfo.embd_id}
>
<Select
// style={{ width: 200 }}
onChange={handleChange}
// fieldNames={label:}
options={Object.keys(llmInfo).map(t => {
const options = llmInfo[t].filter((d: any) => d.model_type === 'embedding').map((d: any) => ({ label: d.llm_name, value: d.llm_name, }))
return { label: t, options }
options={Object.keys(llmInfo).map((t) => {
const options = llmInfo[t]
.filter((d: any) => d.model_type === 'embedding')
.map((d: any) => ({ label: d.llm_name, value: d.llm_name }));
return { label: t, options };
})}
/>
</Form.Item>
@ -95,15 +88,16 @@ const Index: FC<SSModalProps> = ({ settingModel, dispatch }) => {
name="llm_id"
rules={[{ required: true, message: 'Please input value' }]}
initialValue={tenantIfo.llm_id}
>
<Select
// style={{ width: 200 }}
onChange={handleChange}
// fieldNames={label:}
options={Object.keys(llmInfo).map(t => {
const options = llmInfo[t].filter((d: any) => d.model_type === 'chat').map((d: any) => ({ label: d.llm_name, value: d.llm_name, }))
return { label: t, options }
options={Object.keys(llmInfo).map((t) => {
const options = llmInfo[t]
.filter((d: any) => d.model_type === 'chat')
.map((d: any) => ({ label: d.llm_name, value: d.llm_name }));
return { label: t, options };
})}
/>
</Form.Item>
@ -112,15 +106,16 @@ const Index: FC<SSModalProps> = ({ settingModel, dispatch }) => {
name="img2txt_id"
rules={[{ required: true, message: 'Please input value' }]}
initialValue={tenantIfo.img2txt_id}
>
<Select
// style={{ width: 200 }}
onChange={handleChange}
// fieldNames={label:}
options={Object.keys(llmInfo).map(t => {
const options = llmInfo[t].filter((d: any) => d.model_type === 'image2text').map((d: any) => ({ label: d.llm_name, value: d.llm_name, }))
return { label: t, options }
options={Object.keys(llmInfo).map((t) => {
const options = llmInfo[t]
.filter((d: any) => d.model_type === 'image2text')
.map((d: any) => ({ label: d.llm_name, value: d.llm_name }));
return { label: t, options };
})}
/>
</Form.Item>
@ -129,24 +124,21 @@ const Index: FC<SSModalProps> = ({ settingModel, dispatch }) => {
name="asr_id"
rules={[{ required: true, message: 'Please input value' }]}
initialValue={tenantIfo.asr_id}
>
<Select
// style={{ width: 200 }}
onChange={handleChange}
// fieldNames={label:}
options={Object.keys(llmInfo).map(t => {
const options = llmInfo[t].filter((d: any) => d.model_type === 'speech2text').map((d: any) => ({ label: d.llm_name, value: d.llm_name, }))
return { label: t, options }
options={Object.keys(llmInfo).map((t) => {
const options = llmInfo[t]
.filter((d: any) => d.model_type === 'speech2text')
.map((d: any) => ({ label: d.llm_name, value: d.llm_name }));
return { label: t, options };
})}
/>
</Form.Item>
</Form>
</Modal>
);
}
export default connect(({ settingModel, loading }) => ({ settingModel, loading }))(Index);
};
export default SsModal;

View File

@ -1,11 +1,9 @@
import { connect, Dispatch } from 'umi';
import { FC } from 'react'
import i18n from 'i18next';
import { useTranslation, Trans } from 'react-i18next'
import { Modal, Table } from 'antd'
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
import { Modal, Table } from 'antd';
import { ColumnsType } from 'antd/es/table';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'umi';
import styles from './index.less';
import type { ColumnsType } from 'antd/es/table';
interface DataType {
key: React.Key;
@ -14,45 +12,54 @@ interface DataType {
time: string;
}
interface TntodalProps {
dispatch: Dispatch;
settingModel: any
}
const TntModal = () => {
const dispatch = useDispatch();
const settingModel = useSelector((state: any) => state.settingModel);
const { isShowTntModal, tenantIfo, factoriesList } = settingModel;
const { t } = useTranslation();
const loading = useOneNamespaceEffectsLoading('settingModel', [
'getTenantInfo',
]);
const Index: FC<TntodalProps> = ({ settingModel, dispatch }) => {
const { isShowTntModal, tenantIfo, loading, factoriesList } = settingModel
const { t } = useTranslation()
const handleCancel = () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowTntModal: false
}
});
};
console.log(tenantIfo)
const handleOk = async () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowTntModal: false
}
});
};
const columns: ColumnsType<DataType> = [
{ title: '姓名', dataIndex: 'name', key: 'name' },
{ title: '活动时间', dataIndex: 'update_date', key: 'update_date' },
{ title: '角色', dataIndex: 'role', key: 'age' },
];
const handleCancel = () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowTntModal: false,
},
});
};
const handleOk = async () => {
dispatch({
type: 'settingModel/updateState',
payload: {
isShowTntModal: false,
},
});
};
return (
<Modal title="用户" open={isShowTntModal} onOk={handleOk} onCancel={handleCancel}>
<div className={styles.tenantIfo}>
{tenantIfo.name}
</div>
<Table rowKey='name' loading={loading} columns={columns} dataSource={factoriesList} />
<Modal
title="用户"
open={isShowTntModal}
onOk={handleOk}
onCancel={handleCancel}
>
<div className={styles.tenantIfo}>{tenantIfo.name}</div>
<Table
rowKey="name"
loading={loading}
columns={columns}
dataSource={factoriesList}
/>
</Modal>
);
}
export default connect(({ settingModel, loading }) => ({ settingModel, loading }))(Index);
};
export default TntModal;

View File

@ -1,34 +1,35 @@
import { Button, FloatButton } from 'antd';
import i18n from 'i18next';
import { useTranslation } from 'react-i18next';
import { Dispatch, connect } from 'umi';
import authorizationUtil from '@/utils/authorizationUtil';
import { FC, useEffect } from 'react';
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'umi';
import CPwModal from './CPwModal';
import List from './List';
import SAKModal from './SAKModal';
import SSModal from './SSModal';
import TntModal from './TntModal';
import styles from './index.less';
interface CPwModalProps {
dispatch: Dispatch;
settingModel: any;
}
const Index: FC<CPwModalProps> = ({ settingModel, dispatch }) => {
// const [llm_factory, set_llm_factory] = useState('')
const Setting = () => {
const dispatch = useDispatch();
const settingModel = useSelector((state: any) => state.settingModel);
const { t } = useTranslation();
const userInfo = authorizationUtil.getUserInfoObject();
const changeLang = (val: string) => {
// 改变状态里的 语言 进行切换
i18n.changeLanguage(val);
};
useEffect(() => {
dispatch({
type: 'settingModel/getTenantInfo',
payload: {},
});
}, []);
const showCPwModal = () => {
dispatch({
type: 'settingModel/updateState',
@ -52,11 +53,6 @@ const Index: FC<CPwModalProps> = ({ settingModel, dispatch }) => {
isShowSSModal: true,
},
});
// dispatch({
// type: 'settingModel/getTenantInfo',
// payload: {
// }
// });
};
return (
<div className={styles.settingPage}>
@ -99,7 +95,4 @@ const Index: FC<CPwModalProps> = ({ settingModel, dispatch }) => {
</div>
);
};
export default connect(({ settingModel, loading }) => ({
settingModel,
loading,
}))(Index);
export default Setting;

View File

@ -9,7 +9,6 @@ export interface SettingModelState {
isShowSAKModal: boolean;
isShowSSModal: boolean;
llm_factory: string;
loading: boolean;
tenantIfo: any;
llmInfo: any;
myLlm: any[];
@ -24,7 +23,6 @@ const model: DvaModel<SettingModelState> = {
isShowSAKModal: false,
isShowSSModal: false,
llm_factory: '',
loading: false,
tenantIfo: {},
llmInfo: {},
myLlm: [],
@ -44,12 +42,21 @@ const model: DvaModel<SettingModelState> = {
},
},
effects: {
*setting({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(userService.setting, payload);
const { retcode, data: res, retmsg } = data;
*setting({ payload = {} }, { call, put }) {
const { data } = yield call(userService.setting, payload);
const { retcode } = data;
if (retcode === 0) {
message.success('密码修改成功!');
callback && callback();
yield put({
type: 'updateState',
payload: {
isShowPSwModal: false,
},
});
yield put({
type: 'getUserInfo',
payload: {},
});
}
},
*getUserInfo({ payload = {} }, { call, put }) {
@ -72,11 +79,8 @@ const model: DvaModel<SettingModelState> = {
loading: true,
},
});
const { data, response } = yield call(
userService.get_tenant_info,
payload,
);
const { retcode, data: res, retmsg } = data;
const { data } = yield call(userService.get_tenant_info, payload);
const { retcode, data: res } = data;
// llm_id 对应chat_id
// asr_id 对应speech2txt
@ -98,11 +102,8 @@ const model: DvaModel<SettingModelState> = {
}
},
*set_tenant_info({ payload = {} }, { call, put }) {
const { data, response } = yield call(
userService.set_tenant_info,
payload,
);
const { retcode, data: res, retmsg } = data;
const { data } = yield call(userService.set_tenant_info, payload);
const { retcode } = data;
// llm_id 对应chat_id
// asr_id 对应speech2txt
if (retcode === 0) {
@ -116,6 +117,7 @@ const model: DvaModel<SettingModelState> = {
type: 'getTenantInfo',
});
}
return retcode;
},
*factories_list({ payload = {} }, { call, put }) {
@ -157,12 +159,17 @@ const model: DvaModel<SettingModelState> = {
});
}
},
*set_api_key({ payload = {}, callback }, { call, put }) {
const { data, response } = yield call(userService.set_api_key, payload);
const { retcode, data: res, retmsg } = data;
*set_api_key({ payload = {} }, { call, put }) {
const { data } = yield call(userService.set_api_key, payload);
const { retcode } = data;
if (retcode === 0) {
message.success('设置API KEY成功');
callback && callback();
yield put({
type: 'updateState',
payload: {
isShowSAKModal: false,
},
});
}
},
},

View File

@ -1,14 +1,8 @@
let api_host = `http://54.80.112.79:9380/v1`;
let api_host = `http://223.111.148.200:9380/v1`;
export { api_host };
export default {
// 用户
login: `${api_host}/user/login`,
register: `${api_host}/user/register`,
@ -23,8 +17,6 @@ export default {
my_llm: `${api_host}/llm/my_llms`,
set_api_key: `${api_host}/llm/set_api_key`,
//知识库管理
kb_list: `${api_host}/kb/list`,
create_kb: `${api_host}/kb/create`,
@ -41,9 +33,6 @@ export default {
rm_chunk: `${api_host}/chunk/rm`,
retrieval_test: `${api_host}/chunk/retrieval_test`,
// 上传
upload: `${api_host}/document/upload`,
get_document_list: `${api_host}/document/list`,
@ -51,5 +40,4 @@ export default {
document_rm: `${api_host}/document/rm`,
document_create: `${api_host}/document/create`,
document_change_parser: `${api_host}/document/change_parser`,
};

View File

@ -12,9 +12,9 @@ export function lastWeek() {
return formatDate(moment().subtract(1, 'weeks'));
}
export function formatDate(date) {
export function formatDate(date: any) {
if (!date) {
return '';
}
return moment(date).format('YYYY-MM-DD');
return moment(date).format('DD/MM/YYYY');
}

View File

@ -83,7 +83,7 @@ const errorHandler = (error: {
*/
const request: RequestMethod = extend({
errorHandler, // 默认错误处理
timeout: 3000000,
timeout: 300000,
getResponse: true,
});