Commit 06cce791 authored by 文旺-丰林-DEV's avatar 文旺-丰林-DEV

update ui

parents 6a27fa97 0bc59ceb
...@@ -38,3 +38,4 @@ screenshot ...@@ -38,3 +38,4 @@ screenshot
.eslintcache .eslintcache
build build
node_modules
/** /**
* @see https://umijs.org/zh-CN/plugins/plugin-access * @see https://umijs.org/zh-CN/plugins/plugin-access
* */ * */
export default function access(initialState: { currentUser?: API.CurrentUser } | undefined) { export default function access(initialState: { userInfo: { token: '' }; }) {
console.log(initialState) console.log('initialState', initialState)
const { currentUser } = initialState ?? {}; const { token } = initialState.userInfo ?? {};
return { return {
canAdmin: currentUser && currentUser.access === 'admin', token
}; };
} }
...@@ -13,15 +13,17 @@ export const initialStateConfig = { ...@@ -13,15 +13,17 @@ export const initialStateConfig = {
/** /**
* @see https://umijs.org/zh-CN/plugins/plugin-initial-state * @see https://umijs.org/zh-CN/plugins/plugin-initial-state
* */ * */
export async function getInitialState() { export async function getInitialState() {
const userMessage = JSON.parse(localStorage.getItem('user')); const userInfo = JSON.parse(localStorage.getItem('user') || '{}');
if (isEmpty(userMessage)) { if (isEmpty(userInfo)) {
history.push(loginPath); history.push(loginPath);
return {}; return {};
} }
return { userMessage }; return { userInfo };
} }
// ProLayout 支持的api https://procomponents.ant.design/components/layout // ProLayout 支持的api https://procomponents.ant.design/components/layout
......
import styles from './index.less'; import styles from './index.less';
import { Button, Checkbox, Form, Input, Modal, message } from 'antd'; import { Button, Checkbox, Form, Input, Modal } from 'antd';
import { LockOutlined, UserOutlined } from '@ant-design/icons'; import { LockOutlined, UserOutlined } from '@ant-design/icons';
import logoSvg from '../../assets/logo.svg'; import logoSvg from '../../assets/logo.svg';
import { useState } from 'react'; import { useState } from 'react';
import { loginChatGLM } from '@/services/chatGLM/api'; import { loginChatGLM } from '@/services/chatGLM/api';
import { useIntl } from 'react-intl';
interface ILoginModalProps { interface ILoginModalProps {
open: boolean; open: boolean;
setOpen: (value: boolean) => void; setOpen: (value: boolean) => void;
...@@ -14,7 +12,6 @@ interface ILoginModalProps { ...@@ -14,7 +12,6 @@ interface ILoginModalProps {
const LoginModal = (props: ILoginModalProps) => { const LoginModal = (props: ILoginModalProps) => {
const { open, setOpen } = props; const { open, setOpen } = props;
const intl = useIntl();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [form] = Form.useForm(); const [form] = Form.useForm();
...@@ -31,17 +28,11 @@ const LoginModal = (props: ILoginModalProps) => { ...@@ -31,17 +28,11 @@ const LoginModal = (props: ILoginModalProps) => {
name: username, name: username,
password, password,
}); });
localStorage.setItem('user', JSON.stringify(result));
localStorage.setItem('user', JSON.stringify(result || {}));
console.log('result', result);
setLoading(false); setLoading(false);
reset(); reset();
} catch(error) { } catch(error) {
const defaultLoginFailureMessage = intl.formatMessage({ setLoading(false);
id: 'pages.login.failure',
defaultMessage: '登录失败,请重试!',
});
message.error(defaultLoginFailureMessage);
} }
}; };
......
...@@ -17,47 +17,51 @@ const Answer = (props: IAnswerProps) => { ...@@ -17,47 +17,51 @@ const Answer = (props: IAnswerProps) => {
const screens = useBreakpoint(); const screens = useBreakpoint();
const isMobile = screens.xs; const isMobile = screens.xs;
console.log('isMobile', isMobile);
return ( return (
<div> <Col span={24}>
<p className={styles.question} style={{ marginTop: isMobile ? '20px' : '40px' }}> <Row>
<img src={logoSvg} alt="" className={styles.question_img} /> <Col span={24}>
<span <p className={styles.question} style={{ marginTop: isMobile ? '20px' : '40px' }}>
className={classNames(isMobile && styles.mobile_question_span, styles.question_span)} <img src={logoSvg} alt="" className={styles.question_img} />
style={{ marginLeft: isMobile ? '8px' : '16px' }} <span
> className={classNames(isMobile && styles.mobile_question_span, styles.question_span)}
回答如下: style={{ marginLeft: isMobile ? '8px' : '16px' }}
</span> >
</p> 回答如下:
<div className={styles.chat}> </span>
<div className={classNames(styles.answer, isMobile && styles.mobile_answer)}> </p>
{!isMobile && ( </Col>
<p className={styles.copyAnswer} onClick={() => {}}> <Col span={24} className={styles.chat}>
<img src={copySvg} alt="" /> <div className={classNames(styles.answer, isMobile && styles.mobile_answer)}>
<span>复制回答</span> <div className={styles.answer}>
</p> {!answerValue ? (
)} <p style={{ color: '#666666' }}>请耐心等待3-5秒~</p>
<div className={styles.answer}> ) : (
{!answerValue ? ( <p dangerouslySetInnerHTML={{ __html: answerValue }}></p>
<p>请耐心等待3-5秒~</p> )}
) : ( </div>
<p dangerouslySetInnerHTML={{ __html: answerValue }}></p> {!isMobile && answerValue && (
<p className={styles.copyAnswer} onClick={() => {}}>
<img src={copySvg} alt="" />
<span>复制回答</span>
</p>
)} )}
</div> </div>
</div> {isShowRetry && !isMobile && <Button className={styles.chat_retry}>重试</Button>}
{isShowRetry && !isMobile && <Button className={styles.chat_retry}>重试</Button>} </Col>
</div> <Col>
{isMobile && ( {isMobile && answerValue && (
<div className={styles.mobile_button}> <Row justify={'center'} className={styles.mobile_button}>
<Button className={styles.mobile_copyAnswer}> <Button className={styles.mobile_copyAnswer}>
<img src={copySvg} alt="" /> <img src={copySvg} alt="" />
<span>复制回答</span> <span>复制回答</span>
</Button> </Button>
<Button className={styles.mobile_chat_retry}>重试</Button> <Button className={styles.mobile_chat_retry}>重试</Button>
</div> </Row>
)} )}
</div> </Col>
</Row>
</Col>
); );
}; };
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
.mobile_newSession { .mobile_newSession {
width: 100% !important; width: 100% !important;
margin-top: 24px !important;
padding: 48px 16px 16px; padding: 48px 16px 16px;
} }
...@@ -10,14 +11,13 @@ ...@@ -10,14 +11,13 @@
} }
.mobile_answer { .mobile_answer {
width: 316px !important; // width: 316px !important;
font-size: 16px !important; font-size: 16px !important;
line-height: 28px !important; line-height: 28px !important;
} }
.newSession { .newSession {
width: 1000px; margin-top: 0px;
margin-top: 40px;
.question { .question {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
...@@ -35,25 +35,28 @@ ...@@ -35,25 +35,28 @@
.chat { .chat {
display: flex; display: flex;
width: 100vw;
padding-left: 28px;
// justify-content: center;
.answer { .answer {
position: relative; width: 100%;
width: 900px; padding: 4px 8px;
min-height: 268px;
margin-left: 28px;
color: rgba(0, 0, 0, 0.85); color: rgba(0, 0, 0, 0.85);
font-size: 18px; font-size: 16px;
line-height: 32px; line-height: 32px;
white-space: pre-wrap;
word-wrap: break-word;
background: #f0f3f6; background: #f0f3f6;
border-radius: 4px; border-radius: 4px;
.copyAnswer { .copyAnswer {
position: absolute;
right: 16px;
bottom: 0;
z-index: 2; z-index: 2;
padding-right: 16px;
text-align: right;
cursor: pointer; cursor: pointer;
img { img {
width: 24px; width: 24px;
height: 24px; height: 24px;
padding-left: 2px;
} }
span { span {
height: 24px; height: 24px;
...@@ -70,7 +73,7 @@ ...@@ -70,7 +73,7 @@
.chat_retry { .chat_retry {
width: 90px; width: 90px;
height: 48px; height: 48px;
margin-left: 10px; // margin-left: 10px;
background: #ffffff; background: #ffffff;
border: 1px solid #009b95; border: 1px solid #009b95;
border-radius: 6px; border-radius: 6px;
...@@ -89,7 +92,7 @@ ...@@ -89,7 +92,7 @@
display: flex; display: flex;
justify-content: space-around; justify-content: space-around;
margin-top: 16px; margin-top: 16px;
margin-left: 24px; margin-left: 28px;
.mobile_copyAnswer { .mobile_copyAnswer {
width: 151px; width: 151px;
height: 40px; height: 40px;
...@@ -109,6 +112,7 @@ ...@@ -109,6 +112,7 @@
.mobile_chat_retry { .mobile_chat_retry {
width: 151px; width: 151px;
height: 40px; height: 40px;
margin-left: 12px;
color: #009b95; color: #009b95;
background: #ffffff; background: #ffffff;
border: 1px solid #009b95; border: 1px solid #009b95;
......
import styles from './index.less'; import styles from './index.less';
import Issue from './issue.component'; import Issue from './issue.component';
import Answer from './answer.component'; import Answer from './answer.component';
import { Grid } from 'antd'; import { Grid, Row } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
interface INewSessionProps { interface INewSessionProps {
...@@ -18,10 +18,16 @@ const NewSession = (props: INewSessionProps) => { ...@@ -18,10 +18,16 @@ const NewSession = (props: INewSessionProps) => {
const isMobile = screens.xs; const isMobile = screens.xs;
return ( return (
<div className={classNames(isMobile && styles.mobile_newSession, styles.newSession)}> <Row
style={{
padding: isMobile ? '' : '0 0 0 72px !important',
width: isMobile ? '100vw' : 'calc(100vw - 240px - 144px)',
}}
className={classNames(isMobile && styles.mobile_newSession, styles.newSession)}
>
<Issue questionValue={questionValue} /> <Issue questionValue={questionValue} />
<Answer answerValue={answerValue} /> <Answer answerValue={answerValue} />
</div> </Row>
); );
}; };
......
import classNames from 'classnames'; import classNames from 'classnames';
import userSvg from '../../assets/user.svg'; import userSvg from '../../assets/user.svg';
import styles from './index.less'; import styles from './index.less';
import { Grid } from 'antd'; import { Col, Grid } from 'antd';
interface IIssueProps { interface IIssueProps {
questionValue: string; questionValue: string;
...@@ -10,7 +10,6 @@ interface IIssueProps { ...@@ -10,7 +10,6 @@ interface IIssueProps {
const { useBreakpoint } = Grid; const { useBreakpoint } = Grid;
const Issue = (props: IIssueProps) => { const Issue = (props: IIssueProps) => {
const { questionValue } = props; const { questionValue } = props;
const screens = useBreakpoint(); const screens = useBreakpoint();
...@@ -19,11 +18,16 @@ const Issue = (props: IIssueProps) => { ...@@ -19,11 +18,16 @@ const Issue = (props: IIssueProps) => {
console.log('isMobile', isMobile); console.log('isMobile', isMobile);
return ( return (
<div className={styles.question} style={{ marginTop: isMobile ? '20px' : '40px' }}> <Col span={24} className={styles.question} style={{ marginTop: isMobile ? '0px' : '40px' }}>
<img src={userSvg} alt="" className={styles.question_img } /> <img src={userSvg} alt="" className={styles.question_img} />
<span className={classNames(isMobile && styles.mobile_question_span, styles.question_span)} style={{ marginLeft: isMobile ? '8px' : '16px' }}>{questionValue}</span> <span
</div> className={classNames(isMobile && styles.mobile_question_span, styles.question_span)}
style={{ marginLeft: isMobile ? '8px' : '16px' }}
>
{questionValue}
</span>
</Col>
); );
} };
export default Issue; export default Issue;
\ No newline at end of file
...@@ -12,6 +12,11 @@ body, ...@@ -12,6 +12,11 @@ body,
height: 100%; height: 100%;
} }
body {
margin: 0;
padding: 0;
}
.colorWeak { .colorWeak {
filter: invert(80%); filter: invert(80%);
} }
......
...@@ -2,8 +2,5 @@ ...@@ -2,8 +2,5 @@
padding: 8px; padding: 8px;
} }
.CardContent { .CardContent {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-bottom: 150px; margin-bottom: 150px;
} }
...@@ -13,17 +13,25 @@ interface ChatFirstContentProps { ...@@ -13,17 +13,25 @@ interface ChatFirstContentProps {
const ChatFirstContent: FC<ChatFirstContentProps> = (props) => { const ChatFirstContent: FC<ChatFirstContentProps> = (props) => {
const { onChange, list } = props; const { onChange, list } = props;
const screen = useBreakpoint();
const isMobile = screen.xs;
return ( return (
<div className={styles.CardContent}> <div className={styles.CardContent}>
<Row gutter={1}> <Row>
{list.map((item, index) => { {list.map((item, index) => {
return ( return (
<Col lg={8} md={12} span={8} xs={24} sm={16} key={`${item.title}-card-${index}`}> <Col
<Row justify={isMobile && 'center'} className={styles.cardPadding}> lg={12}
md={12}
span={8}
xs={24}
sm={12}
xl={8}
xxl={8}
key={`${item.title}-card-${index}`}
>
<Row justify={'start'} style={{ marginBottom: 16, minWidth: 306 }}>
<ProductionCard <ProductionCard
index={index}
title={item.title} title={item.title}
content={item.content} content={item.content}
onChange={() => { onChange={() => {
......
.CardWrapper { .CardWrapper {
width: 306px; width: 100%;
cursor: pointer; padding: 0 16px;
&:hover .CardContent { cursor: pointer;
background-color: #0a7a73; &:hover .CardContent {
&:hover .content { background-color: #0a7a73;
color: white; &:hover .content {
} color: white;
&:hover .button { }
color: #0a7a73; &:hover .button {
background-color: white; color: #0a7a73;
border: solid 1px white; background-color: white;
} border: solid 1px white;
} }
.title { }
padding: 5px; .title {
color: #333333; padding-bottom: 10px;
font-weight: 600; color: #333333;
font-size: 22px; font-weight: 600;
line-height: 26px; font-size: 20px;
text-align: center; text-align: center;
} }
.CardContent { .CardContent {
padding: 16px 24px; padding: 16px 16px;
background-color: #f8f9fb; background-color: #f8f9fb;
border-radius: 5px; border-radius: 5px;
transition: all 0.25s; transition: all 0.25s;
.content { .content {
height: 204px; display: -webkit-box;
padding: 12px 8px; height: 202px;
color: #666666; overflow: hidden;
font-weight: 400; color: #666666;
font-size: 14px; font-weight: 400;
line-height: 28px; font-size: 18px;
} line-height: 28px;
.button { white-space: normal;
width: 180px; text-overflow: ellipsis;
margin: 0 auto; -webkit-box-orient: vertical;
padding: 6px 24px; -webkit-line-clamp: 7;
color: #0a7a73; }
font-size: 16px; .button {
text-align: center; width: 180px;
border: solid 1px #0a7a73; margin: 0 auto;
border-radius: 5px; padding: 6px 24px;
} color: #0a7a73;
} font-weight: 500;
} font-size: 16px;
text-align: center;
border: solid 1px #0a7a73;
border-radius: 5px;
}
}
}
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import styles from './productionCard.less'; import styles from './productionCard.less';
import classnames from 'classnames'; import classnames from 'classnames';
interface ProductionCardProps { interface ProductionCardProps {
title?: string; index: number;
content: ReactNode; title?: string;
onChange: (content: string) => void; content: ReactNode;
} onChange: (content: string) => void;
}
const ProductionCard = (props: ProductionCardProps) => {
const { title, content, onChange } = props; const ProductionCard = (props: ProductionCardProps) => {
const { index, title, content, onChange } = props;
const handleClick = () => {
onChange && onChange(content); const handleClick = () => {
}; onChange && onChange(content);
};
return (
<div className={classnames(styles.CardWrapper)}> return (
<div className={styles.title}>{title}</div> <div className={classnames(styles.CardWrapper)}>
<div className={styles.CardContent}> <div className={styles.title}>{title}</div>
<div className={styles.content}>{content}</div> <div className={styles.CardContent}>
<div className={styles.button} onClick={handleClick}> <div className={styles.content}>{content}</div>
试一下看看 <div className={styles.button} onClick={handleClick}>
</div> 试一下看看
</div> </div>
</div> </div>
); </div>
}; );
};
export default ProductionCard;
export default ProductionCard;
...@@ -3,16 +3,15 @@ ...@@ -3,16 +3,15 @@
.chatContent { .chatContent {
background: white !important; background: white !important;
} }
.siderWrapper { .siderWrapper {
background: white; background: white;
.sider { .sider {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
z-index: 100;
width: 240px; width: 240px;
min-height: 100vh; min-height: 100vh;
padding: 30px 20px;
background-color: #001529; background-color: #001529;
.siderChatContent { .siderChatContent {
height: 70%; height: 70%;
...@@ -26,12 +25,13 @@ ...@@ -26,12 +25,13 @@
} }
} }
.header { .header {
margin-top: 68px; margin-top: 80px;
margin-bottom: 40px;
color: #333; color: #333;
font-size: 32px; font-size: 32px;
line-height: 45px;
text-align: center; text-align: center;
.subTitle { .subTitle {
margin-bottom: 0;
color: #999999; color: #999999;
font-weight: 400; font-weight: 400;
} }
...@@ -68,11 +68,28 @@ ...@@ -68,11 +68,28 @@
} }
} }
.chatFooter {
position: fixed;
bottom: 0;
left: 0;
width: 240px;
width: 100%;
margin-bottom: 52px;
}
.chatFooterItem {
margin-left: 64px;
padding: 19px 0px;
color: #ffffff;
font-size: 18px;
line-height: 25px;
}
.footer { .footer {
position: fixed; position: fixed;
bottom: 0; bottom: 0;
left: 0; left: 0;
z-index: 10; z-index: 12;
width: 100%; width: 100%;
height: 150px; height: 150px;
padding: 16px; padding: 16px;
...@@ -80,7 +97,7 @@ ...@@ -80,7 +97,7 @@
background-color: white; background-color: white;
box-shadow: 0 2px 8px #f0f1f2; box-shadow: 0 2px 8px #f0f1f2;
.chatWrapper { .chatWrapper {
padding: 0 16px 0 50px; border-radius: 4px;
:global { :global {
.ant-input:focus, .ant-input:focus,
.ant-input:hover, .ant-input:hover,
...@@ -94,10 +111,10 @@ ...@@ -94,10 +111,10 @@
} }
} }
.outputButtonWrapper { .outputButtonWrapper {
width: 120px;
:global(.ant-btn-primary) { :global(.ant-btn-primary) {
background: #009b95; background: #009b95;
border: solid 1px #009b95; border: solid 1px #009b95;
border-radius: 6px !important;
} }
} }
} }
......
/* eslint-disable @typescript-eslint/no-unused-expressions */ /* eslint-disable @typescript-eslint/no-unused-expressions */
import { Form, Button, Col, Row, Input, Typography, Grid, message } from 'antd'; import { Form, Button, Col, Row, Input, Typography, Grid, message, Space } from 'antd';
import React, { useState, useEffect, useRef, useMemo } from 'react'; import React, { useState, useEffect, useRef, useMemo } from 'react';
import styles from './index.less'; import styles from './index.less';
import ChatFirstContent from './components/content'; import ChatFirstContent from './components/content';
import { import {
FieldTimeOutlined, FieldTimeOutlined,
LoadingOutlined,
MenuOutlined, MenuOutlined,
PlusOutlined, PlusOutlined,
SettingOutlined, SettingOutlined,
...@@ -17,6 +18,7 @@ import LoginModal from '@/components/loginModal'; ...@@ -17,6 +18,7 @@ import LoginModal from '@/components/loginModal';
import NewSession from '@/components/newSession'; import NewSession from '@/components/newSession';
import { useWebSocket, useRequest } from 'ahooks'; import { useWebSocket, useRequest } from 'ahooks';
import { GetChatGLMToken, GetPromptList } from '@/services/chatGLM/api'; import { GetChatGLMToken, GetPromptList } from '@/services/chatGLM/api';
import { useAccess } from 'umi';
const { useBreakpoint } = Grid; const { useBreakpoint } = Grid;
const { Title } = Typography; const { Title } = Typography;
...@@ -40,6 +42,11 @@ const Chat: React.FC = () => { ...@@ -40,6 +42,11 @@ const Chat: React.FC = () => {
const [answer, setAnswer] = useState(''); const [answer, setAnswer] = useState('');
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [open, setOpen] = useState(false);
const tabShow = showSider && !isMobile;
const access = useAccess();
const messageHistory = useRef<IHistory[]>([]); const messageHistory = useRef<IHistory[]>([]);
const { sendMessage } = useWebSocket('wss://glm-api.baibaomen.com/ws', { const { sendMessage } = useWebSocket('wss://glm-api.baibaomen.com/ws', {
onMessage: (event) => { onMessage: (event) => {
...@@ -50,6 +57,7 @@ const Chat: React.FC = () => { ...@@ -50,6 +57,7 @@ const Chat: React.FC = () => {
} else { } else {
setAnswer(text); setAnswer(text);
window.scrollTo(0, document.body.scrollHeight); window.scrollTo(0, document.body.scrollHeight);
console.log(text);
} }
}, },
onOpen: () => { onOpen: () => {
...@@ -68,6 +76,7 @@ const Chat: React.FC = () => { ...@@ -68,6 +76,7 @@ const Chat: React.FC = () => {
return messageHistory.current; return messageHistory.current;
}, [answer]); }, [answer]);
async function handleSendMessage() { async function handleSendMessage() {
messageHistory.current.push({ prompt: chat, answer: '' });
setLoading(true); setLoading(true);
sendMessage && sendMessage &&
sendMessage( sendMessage(
...@@ -80,29 +89,17 @@ const Chat: React.FC = () => { ...@@ -80,29 +89,17 @@ const Chat: React.FC = () => {
token: glmToken, token: glmToken,
}), }),
); );
messageHistory.current.push({ prompt: chat, answer: '' });
} }
const [open, setOpen] = useState(false); console.log('access', access);
const tabShow = showSider;
useEffect(() => { useEffect(() => {
if (!isMobile) { if (access?.token) {
setShowSider(true);
}
}, [isMobile]);
let token = '';
useEffect(() => {
if (localStorage.getItem('user')) {
token = JSON.parse(localStorage.user)?.token;
}
if (token) {
setOpen(false); setOpen(false);
} else { } else {
setOpen(true); setOpen(true);
} }
}, []); }, [access]);
const onSend = () => { const onSend = () => {
form.setFieldValue('chatInput', ''); form.setFieldValue('chatInput', '');
...@@ -120,45 +117,19 @@ const Chat: React.FC = () => { ...@@ -120,45 +117,19 @@ const Chat: React.FC = () => {
value?.firstInput && setInput(value); value?.firstInput && setInput(value);
}; };
const siderTabsList = [
{
icons: <SettingOutlined />,
text: '后台',
checked: 'gotoAdmin',
},
{
icons: <WechatOutlined />,
text: '活动交流群',
checked: 'wechat',
},
{
icons: <UserOutlined />,
text: '游客登录',
checked: 'noMentionLogin',
},
];
const selectSliderTabsHandleClick = (value: string) => {
setSelectSiderTabs(value);
if (value === 'gotoAdmin') {
history.push('/admin/user');
}
};
const renderMobileHeader = () => { const renderMobileHeader = () => {
return ( return (
<Row align={'middle'} justify="space-between" gutter={1} className={styles.mobileHeader}> <Row align={'middle'} justify="space-between" gutter={1} className={styles.mobileHeader}>
<Col push={1}> <Col push={1}>
<MenuOutlined <MenuOutlined
onClick={() => { onClick={() => {
console.log(showSider);
setShowSider(!showSider); setShowSider(!showSider);
}} }}
/> />
</Col> </Col>
<Col>AI改变世界</Col> <Col>AI改变世界</Col>
<Col pull={1}> <Col pull={1}>{/* <FieldTimeOutlined /> */}</Col>
<FieldTimeOutlined />
</Col>
</Row> </Row>
); );
}; };
...@@ -168,121 +139,149 @@ const Chat: React.FC = () => { ...@@ -168,121 +139,149 @@ const Chat: React.FC = () => {
<LoginModal open={open} setOpen={setOpen} /> <LoginModal open={open} setOpen={setOpen} />
{isMobile && renderMobileHeader()} {isMobile && renderMobileHeader()}
<Row> <Row>
{tabShow && ( {(showSider || !isMobile) && (
<Col span={4} sm={10} md={7} xs={12} lg={5} className={styles.siderWrapper}> <Col className={`${styles.siderWrapper}`}>
<div style={{ top: isMobile && '44px' }} className={styles.sider}> <div
style={{ top: isMobile && '44px', zIndex: isMobile ? 13 : 10 }}
className={`${styles.sider}`}
>
<div className={styles.siderChatContent}> <div className={styles.siderChatContent}>
<Button size="large" block type="primary" icon={<PlusOutlined />}> <div style={{ padding: '30px 20px 0 20px' }}>
新建对话 <Button size="large" block type="primary" icon={<PlusOutlined />}>
</Button> 新建对话
</Button>
</div>
</div>
<div className={styles.chatFooter}>
<Row style={{ width: '240px' }} justify={'center'} align={'middle'}>
<Col span={24} className={styles.chatFooterItem}>
<Space
wrap
onClick={() => {
history.push('/admin/user');
}}
>
<SettingOutlined />
后台管理
</Space>
</Col>
<Col span={24} className={styles.chatFooterItem} style={{ color: '#009B95' }}>
<Space wrap>
<UserOutlined />
游客登录
</Space>
</Col>
</Row>
</div> </div>
<Row>
{siderTabsList.map((item) => {
return (
<Col key={item.text}>
<div
className={styles.chatSiderChatTabsCard}
onClick={() => {
selectSliderTabsHandleClick(item.checked);
}}
>
<div
className={classNames(
styles.chatSiderChatTabsCardIcons,
selectSiderTabs === item.checked &&
styles.checkedChatSiderChatTabsCardIcons,
)}
>
{item.icons}
</div>
<div
className={classNames(
styles.chatSiderChatTabsCardText,
selectSiderTabs === item.checked &&
styles.checkedChatSiderChatTabsCardText,
)}
>
{item.text}
</div>
</div>
</Col>
);
})}
</Row>
</div> </div>
</Col> </Col>
)} )}
<Col <Form
className={styles.chatContent} form={form}
span={20} initialValues={{ chatInput: chat }}
lg={19} name="chat"
sm={14} onValuesChange={onChange}
md={17} onFinish={onSend}
xs={isMobile ? 24 : 12}
> >
{!isMobile && ( <Col
<div className={styles.header} hidden={firstChat}> className={styles.chatContent}
<Row> style={{
<Col span={24}> marginLeft: isMobile ? 0 : 240,
<Title level={1}>产品名称</Title> width: isMobile ? 'calc(100vw)' : 'calc(100vw - 240px)',
</Col> }}
<Col span={24}>
<Title level={5} className={styles.subTitle}>
AI改变世界
</Title>
</Col>
</Row>
</div>
)}
<Form
form={form}
initialValues={{ chatInput: chat }}
name="chat"
onValuesChange={onChange}
onFinish={onSend}
> >
{firstChat ? ( <Row justify={'center'}>
<div style={{ overflowY: 'scroll', marginBottom: 150 }}> <Col span={'24'}>
{messageHistory.current.map((item, index) => ( {!isMobile && (
<NewSession <div className={styles.header} hidden={firstChat}>
key={'new' + index} <Row>
questionValue={item.prompt} <Col span={24}>
answerValue={item.answer} <Title level={2} style={{ marginBottom: 8, fontSize: 32 }}>
/> 产品名称
))} </Title>
</div> </Col>
) : ( <Col span={24}>
<div className={classNames(isMobile && styles.mobileContent)}> <Title level={5} className={styles.subTitle}>
<Item name="firstInput"> AI改变世界
<ChatFirstContent list={promptList?.items || []} /> </Title>
</Item> </Col>
</div> </Row>
)} </div>
<div className={styles.footer}> )}
<Row> </Col>
<Col span={isMobile ? 20 : 16} push={isMobile ? 0 : 4}>
<div style={{ paddingLeft: isMobile && 0 }} className={styles.chatWrapper}> <Col span={'24'} style={{ padding: isMobile ? '' : '0 100px' }}>
<Item name="chatInput"> {firstChat ? (
<Input <div style={{ marginBottom: 150 }}>
placeholder="在这里输入你想知道的问题,给我几句提示,我来帮你回答" {messageHistory.current.map((item, index) => (
size="large" <NewSession
key={'new' + index}
questionValue={item.prompt}
answerValue={item.answer}
/> />
</Item> ))}
</div> </div>
</Col> ) : (
<Col span={4} push={isMobile ? 0 : 4}> <div className={classNames(isMobile && styles.mobileContent)}>
<div className={styles.outputButtonWrapper}> <Item name="firstInput">
<Item name="outputButton"> <ChatFirstContent list={promptList?.items || []} />
<Button htmlType="submit" type="primary" size="large" loading={loading}>
发送
</Button>
</Item> </Item>
</div> </div>
</Col> )}
</Row> </Col>
</div>
</Form> <div
</Col> className={styles.footer}
style={{
marginLeft: isMobile ? 0 : '240px',
height: isMobile ? '100px' : '120px',
width: isMobile ? '100vw' : 'calc(100vw - 240px)',
}}
>
<Row justify={'center'} align={'middle'} style={{ textAlign: 'center' }}>
<Col span={24} style={{ textAlign: 'center' }}>
<Space style={{ textAlign: 'center' }}>
<div
className={styles.chatWrapper}
style={{
marginLeft: firstChat && !isMobile ? '82px' : 0,
width: isMobile
? 'calc(100vw - 108px)'
: firstChat
? 'calc(100vw - 482px)'
: 'calc(100vw - 550px)',
}}
>
<Item name="chatInput">
<Input
required
placeholder="在这里输入你想知道的问题,给我几句提示,我来帮你回答"
size="large"
/>
</Item>
</div>
<div className={styles.outputButtonWrapper}>
<Item name="outputButton">
<Button htmlType="submit" type="primary" size="large" disabled={loading}>
{loading ? (
<>
&nbsp;
<LoadingOutlined />
&nbsp; &nbsp;
</>
) : (
'发送'
)}
</Button>
</Item>
</div>
</Space>
</Col>
</Row>
</div>
</Row>
</Col>
</Form>
</Row> </Row>
</> </>
); );
......
.isActive {
display: inline-flex;
align-items: center;
justify-content: center;
}
.dot {
display: inline-block;
width: 6px;
height: 6px;
margin-right: 5px;
border-radius: 50%;
}
.green {
background-color: #52c41a;
}
.red {
background-color: #f5222d;
}
import {
CreatedAccount,
getRole,
getUserMessage,
postDelete,
postDeleteUser,
} from '@/services/chatGLM/api';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import type { ActionType } from '@ant-design/pro-components'; import type { ActionType } from '@ant-design/pro-components';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { ProTable } from '@ant-design/pro-components'; import { ProTable } from '@ant-design/pro-components';
import { Button } from 'antd'; import { Button, Col, Divider, Form, Input, Modal, Radio, Row, Switch, message } from 'antd';
import { useRef } from 'react'; import classNames from 'classnames';
import request from 'umi-request'; import { isEmpty } from 'lodash';
import { useRef, useState } from 'react';
import styles from './index.less';
const columns: any[] = [ const { Item } = Form;
{
title: '用户',
dataIndex: 'name',
ellipsis: true,
},
{
title: '用户账号',
dataIndex: 'username',
ellipsis: true,
},
{
title: '角色',
dataIndex: 'role',
hideInSearch: true,
ellipsis: true,
},
{
title: '账号状态',
dataIndex: 'iaActive',
hideInSearch: true,
},
{
title: '创建时间',
dataIndex: 'creationTime',
hideInSearch: true,
valueType: 'date',
},
{
title: '备注',
hideInSearch: true,
dataIndex: 'creationTime',
valueType: 'date',
},
{
title: '操作',
valueType: 'option',
key: 'option',
render: (text, record, _, action) => [
<a
key="editable"
onClick={() => {
action?.startEditable?.(record.id);
}}
>
编辑
</a>,
<a href={record.url} target="_blank" rel="noopener noreferrer" key="view">
重置
</a>,
<a href={record.url} target="_blank" rel="noopener noreferrer" key="view">
删除
</a>,
],
},
];
const UserManage = () => { const UserManage = () => {
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
const [isModalAccountOpen, setIsModalAccountOpen] = useState(false);
const [roles, setRoles] = useState([]);
const [loading, setLoading] = useState(false);
const [accountModalName, setAccountModalName] = useState('新建账号');
const [accountForm] = Form.useForm();
const layout = {
labelCol: { span: 4 },
wrapperCol: { span: 18 },
};
const showModal = () => {
getRole()
.then((res) => {
if (!isEmpty(res)) {
setRoles(res);
setIsModalAccountOpen(true);
}
})
.catch((err) => {
message.error('获取用户权限失败,请重试');
});
};
const columns: any[] = [
{
title: '用户',
dataIndex: 'name',
ellipsis: true,
},
{
title: '用户账号',
dataIndex: 'userName',
ellipsis: true,
},
{
title: '角色',
dataIndex: 'roles',
hideInSearch: true,
ellipsis: true,
},
{
title: '账号状态',
dataIndex: 'iaActive',
hideInSearch: true,
render(text, record) {
return (
<div className={styles.isActive}>
<span className={classNames(styles.dot, record.isActive ? styles.green : styles.red)} />
<div>{record.isActive ? '启动' : '禁用'}</div>
</div>
);
},
},
{
title: '创建时间',
dataIndex: 'creationTime',
hideInSearch: true,
valueType: 'date',
},
{
title: '备注',
hideInSearch: true,
dataIndex: 'creationTime',
valueType: 'date',
},
{
title: '操作',
valueType: 'option',
key: 'option',
render: (text, record, _, action) => [
<a
key="editable"
onClick={() => {
setAccountModalName('编辑账号');
showModal();
const data = {
id: record.id,
Name: record.userName,
Phone: record.phoneNumber,
Role: record.roles,
isActive: record.isActive,
};
accountForm.setFieldsValue(data);
console.log(data);
}}
>
编辑
</a>,
<a
onClick={() => {
// postDeleteUser({
// id: record.id,
// Password:
// });
}}
key="reset"
>
重置
</a>,
<a
key="remove"
onClick={() => {
postDeleteUser({
id: record.id,
});
}}
>
删除
</a>,
],
},
];
const handleOk = () => {
setIsModalAccountOpen(false);
};
const handleModalCancel = () => {
accountForm.resetFields();
setIsModalAccountOpen(false);
};
const createdAccount = (values: any) => {
setLoading(true);
const data = {
role: values.roles,
Phone: values.phoneNumber,
Password: values,
};
CreatedAccount(values)
.then((res) => {
setLoading(false);
})
.catch((error) => {
setLoading(false);
});
};
const editAccountFinish = (values: any) => {
setLoading(true);
};
const onHandleClickAccountFinish = (values: any) => {
if (accountModalName === '添加账号') {
createdAccount(values);
}
if (accountModalName === '编辑账号') {
editAccountFinish(values);
}
};
const onHandleClickAccountChange = (values: any) => {
console.log(values);
};
const validatePhoneNumber = (_, value: any) => {
const phoneNumberRegex = /^1[3456789]\d{9}$/;
if (!value || phoneNumberRegex.test(value)) {
return Promise.resolve();
}
return Promise.reject(new Error('请输入有效的手机号码!'));
};
const validatePassword = (_, value: any) => {
const letterNumberRegex = /^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[^0-9a-zA-Z])([a-zA-Z0-9\S]+)$/;
const lowercaseRegex = /[a-z]/;
const uppercaseRegex = /[A-Z]/;
const specialCharacterRegex = /[^0-9a-zA-Z]/;
if (
letterNumberRegex.test(value) &&
lowercaseRegex.test(value) &&
uppercaseRegex.test(value) &&
specialCharacterRegex.test(value)
) {
return Promise.resolve();
}
return Promise.reject(new Error('密码格式不正确!'));
};
return ( return (
<PageContainer> <PageContainer>
<Modal
title={`${accountModalName}`}
open={isModalAccountOpen}
onOk={handleOk}
onCancel={handleModalCancel}
footer={null}
maskClosable={false}
>
<Form
{...layout}
form={accountForm}
onFinish={onHandleClickAccountFinish}
onValuesChange={onHandleClickAccountChange}
labelAlign="right"
name="accountSetting"
>
<Item
name={'Phone'}
label="用户账号"
rules={[
{ required: true, message: '请输入手机号!' },
{ validator: validatePhoneNumber },
]}
>
<Input />
</Item>
{accountModalName === '添加账号' && (
<Item
name={'Password'}
label="密码"
rules={[{ required: true, message: '请输入密码!' }, { validator: validatePassword }]}
>
<Input />
</Item>
)}
<Item name={'Role'} label="角色" rules={[{ required: true, message: '请设置你的角色' }]}>
<Radio.Group>
{roles.map((item) => {
return (
<Radio key={item} value={`${item}`}>
{item}
</Radio>
);
})}
</Radio.Group>
</Item>
<Item name={'Name'} label="姓名" rules={[{ required: true, message: '请输入你的姓名' }]}>
<Input placeholder="请输入(必填)" />
</Item>
<Item name={'isActive'} label="是否启用">
<Switch />
</Item>
<Divider />
<Row justify={'end'} gutter={8}>
<Col>
<Item key={'cancelButton'} noStyle={true}>
<Button key="back" onClick={handleModalCancel}>
取消
</Button>
</Item>
</Col>
<Col>
<Item key={'saveButton'} noStyle={true}>
<Button key="submit" type="primary" loading={loading} htmlType="submit">
保存
</Button>
</Item>
</Col>
</Row>
</Form>
</Modal>
<ProTable <ProTable
columns={columns} columns={columns}
actionRef={actionRef} actionRef={actionRef}
cardBordered cardBordered
request={async (params = {}, sort, filter) => { request={async (params = {}, sort, filter) => {
const defaultParams = { const defaultParams = {
name: '', Name: null,
Enabled: false, Enabled: null,
PageSize: 9, PageSize: 10,
PageIndex: 1, PageIndex: 0,
};
const response = await getUserMessage(defaultParams);
return {
data: response.items,
total: response.totalCount,
success: true,
}; };
return request<{
data: any[];
}>('https://glm-mangement-api.baibaomen.com/User/page', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
__tenant: '3a0a90fe-9a0d-70f4-200d-a80a41fb6195',
},
data: defaultParams,
});
}} }}
rowKey="id" rowKey="id"
pagination={{ pagination={{
...@@ -102,6 +303,8 @@ const UserManage = () => { ...@@ -102,6 +303,8 @@ const UserManage = () => {
key="button" key="button"
icon={<PlusOutlined />} icon={<PlusOutlined />}
onClick={() => { onClick={() => {
setAccountModalName('添加账号');
showModal();
actionRef.current?.reload(); actionRef.current?.reload();
}} }}
type="primary" type="primary"
......
import request from 'umi-request'; import { extend } from 'umi-request';
import { history } from 'umi';
import { message } from 'antd';
interface ILoginParams { interface ILoginParams {
name: string; name: string;
password: string; password: string;
} }
const request = extend({});
// request.interceptors.request.use((url, options) => { request.interceptors.request.use((url, options) => {
// let { token } = JSON.parse(localStorage.getItem('user') || ''); const token = JSON.parse(localStorage.getItem('user') || '{}')?.token;
// if (null === token) { const authHeader = {
// token = ''; Authorization: `Bearer ${token || ''}`,
// } 'Content-Type': 'application/json',
// const authHeader = { __tenant: '3a0a90fe-9a0d-70f4-200d-a80a41fb6195',
// Authorization: `Bearer ${token}`, };
// 'Content-Type': 'application/json', return {
// '__tenant': '3a0a90fe-9a0d-70f4-200d-a80a41fb6195' // 用户id url: url,
// }; options: { ...options, interceptors: true, headers: authHeader },
// return { };
// url: url, });
// options: { ...options, interceptors: true, headers: authHeader },
// }; request.interceptors.response.use(async (response): Promise<any> => {
// }); console.log('response', response);
if (response.status === 401) {
localStorage.removeItem('user');
history.push('/chat');
} else if (response.status === 200) {
return response;
} else {
message.error('请求失败,请重试! ');
return Promise.reject(response.statusText);
}
});
export function loginChatGLM(options: ILoginParams) { export function loginChatGLM(options: ILoginParams) {
return request('https://glm-mangement-api.baibaomen.com/api/app/account/login', { return request('https://glm-mangement-api.baibaomen.com/api/app/account/login', {
method: 'POST', method: 'POST',
headers: {
'Content-Type': 'application/json',
__tenant: '3a0a90fe-9a0d-70f4-200d-a80a41fb6195',
},
data: options, data: options,
}); });
} }
...@@ -39,7 +48,6 @@ export interface IPromptWord { ...@@ -39,7 +48,6 @@ export interface IPromptWord {
export function GetChatGLMToken(): Promise<string> { export function GetChatGLMToken(): Promise<string> {
return request('https://glm-mangement-api.baibaomen.com/user/token', { return request('https://glm-mangement-api.baibaomen.com/user/token', {
method: 'POST', method: 'POST',
headers: { Authorization: 'Bearer ' + JSON.parse(localStorage.getItem('user') || '')?.token },
}); });
} }
...@@ -63,15 +71,37 @@ interface ITalkParams { ...@@ -63,15 +71,37 @@ interface ITalkParams {
temperature?: number; temperature?: number;
} }
// export function talkChatGLM({ prompt, history, max_length = 2000, top_p = 0.9, temperature = 0.8 }: ITalkParams) { export const CreatedAccount = (params: any): Promise<string> => {
// return request('https://glm-mangement-api.baibaomen.com/', { return request('https://glm-mangement-api.baibaomen.com/User/create', {
// method: 'POST', method: 'POST',
// data: { params,
// prompt, });
// history, };
// max_length,
// top_p, export const getRole = (params?: any): Promise<string> => {
// temperature return request('https://glm-mangement-api.baibaomen.com/User/roles', {
// } method: 'POST',
// }) params,
// } });
};
export const getUserMessage = (params?: any): Promise<string> => {
return request('https://glm-mangement-api.baibaomen.com/User/page', {
method: 'POST',
data: params,
});
};
export const postDeleteUser = (params?: any): Promise<string> => {
return request('https://glm-mangement-api.baibaomen.com/User/delete', {
method: 'POST',
data: params,
});
};
export const postResetUser = (params?: any): Promise<string> => {
return request('https://glm-mangement-api.baibaomen.com/User/delete', {
method: 'POST',
data: params,
});
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment