Commit b59a755e authored by littleforest's avatar littleforest
parents 9ee639c1 b7f9e938
import { message } from 'antd';
import moment from 'moment';
/**
* @see https://umijs.org/zh-CN/plugins/plugin-access
* */
export default function access(initialState: {
userInfo: { token: ''; roles: []; name: ''; userName: '' };
expires: 0;
}) {
console.log('initialState', initialState);
checkIsTokenExpires();
return initialState.userInfo ?? {};
}
export function checkIsTokenExpires() {
const expires = parseInt(localStorage.getItem('expires') || '0');
const now = moment().unix();
const user = localStorage.getItem('user');
if (expires - now <= 0) {
localStorage.removeItem('user');
if (user) {
message.error('会话已过期,请重新登录');
window.location.href = '/chat';
return true;
}
}
return false;
}
......@@ -16,7 +16,6 @@ export const initialStateConfig = {
export async function getInitialState() {
const userInfo = JSON.parse(localStorage.getItem('user') || '{}');
if (isEmpty(userInfo)) {
history.push(loginPath);
return {};
......
.edit_type {
display: flex;
align-items: center;
span {
width: 70px;
margin-right: 10px;
}
}
.edit_prompt {
display: flex;
align-items: flex-start;
span {
width: 70px;
margin-right: 10px;
}
}
\ No newline at end of file
import { Form, Modal, Select } from "antd";
import TextArea from "antd/lib/input/TextArea";
import { useEffect, useState } from "react";
import styles from './index.less';
import { IFormValue } from "@/pages/PromptWord";
interface IEditPromptModalProps {
open: boolean;
setOpen: (value: boolean) => void;
types: string[];
modalType: 'create' | 'edit' | '';
handleEditPrompt: (value: IFormValue) => void;
oldRow: IFormValue;
loading?: boolean;
handleCreatePrompt: (value: IFormValue) => void;
}
const EditPromptModal = (props: IEditPromptModalProps) => {
const { open, setOpen, types, modalType, handleEditPrompt, loading, oldRow, handleCreatePrompt } = props;
const [currentSelectValue, setCurrentSelectValue] = useState<string | null>("请选择");
const [textAreaValue, setTextAreaValue] = useState('');
useEffect(() => {
if(modalType === 'create') {
setTextAreaValue('');
setCurrentSelectValue('');
} else {
setTextAreaValue(oldRow.content);
setCurrentSelectValue(oldRow.type || '请选择');
}
}, [open])
const handleSavePrompt = async () => {
if(modalType === 'create') {
await handleCreatePrompt({
type: currentSelectValue,
content: textAreaValue,
});
}
if(modalType === 'edit') {
await handleEditPrompt({
type: currentSelectValue,
content: textAreaValue,
})
}
setOpen(false);
};
const layout = {
labelCol: { span: 3 },
};
return (
<Modal
open={open}
centered
confirmLoading={loading}
wrapClassName={styles.modal}
title={modalType === 'create' ? '新增提示用语' : '编辑提示用语'}
okText='保存'
onOk={handleSavePrompt}
onCancel={() => {
setCurrentSelectValue('请选择');
setOpen(false);
}}
width={574}
bodyStyle={{
padding: '20px'
}}
>
<Form
labelAlign="right"
{...layout}
>
<Form.Item
label="类型:"
>
<Select
onChange={(value: string) => {
setCurrentSelectValue(value);
}}
value={currentSelectValue}
>
{types.map(item => <Select.Option value={item} key={item}>{item}</Select.Option>)}
</Select>
</Form.Item>
<Form.Item
label="提示词语"
>
<TextArea
style={{
height: '200px'
}}
value={textAreaValue}
onChange={(e) => {
setTextAreaValue(e.target.value);
}}
/>
</Form.Item>
</Form>
</Modal>
)
}
export default EditPromptModal;
\ No newline at end of file
.deleteTitle {
display: flex;
height: 22px;
margin-bottom: 10px;
color: rgba(0, 0, 0, 0.85);
font-weight: 600;
font-size: 16px;
font-family: PingFangSC-Semibold, PingFang SC;
line-height: 22px;
& svg {
width: 24px;
height: 24px;
margin-right: 10px;
}
}
.deleteContent {
width: 322px;
height: 44px;
margin-left: 34px;
color: rgba(0, 0, 0, 0.65);
font-weight: 400;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
line-height: 22px;
}
\ No newline at end of file
import { Modal } from "antd";
import styles from './index.less';
import { Exclamtion } from "../Icons/Icons";
interface IDeletePromptModalProps {
title: string;
open: boolean;
setOpen: (value: boolean) => void;
tip: string;
content: string;
handleDeletePrompt: () => void;
loading?: boolean;
}
const DeletePromptModal = (props: IDeletePromptModalProps) => {
const {
title,
open,
setOpen,
tip,
content,
loading,
handleDeletePrompt,
} = props;
const handleSavePrompt = async () => {
await handleDeletePrompt();
setOpen(false);
}
return (
<Modal
title={title}
okText='确认'
open={open}
onOk={handleSavePrompt}
onCancel={() => {
setOpen(false);
}}
confirmLoading={loading}
>
<div className={styles.deleteTitle}>
<Exclamtion /> {tip}
</div>
<div className={styles.deleteContent}>
{content}
</div>
</Modal>
)
}
export default DeletePromptModal;
\ No newline at end of file
......@@ -5,6 +5,7 @@ import logoSvg from '../../assets/logo.svg';
import { useEffect, useRef, useState } from 'react';
import { loginChatGLM } from '@/services/chatGLM/api';
import { useModel } from 'umi';
import moment from 'moment';
interface ILoginModalProps {
open: boolean;
setOpen: (value: boolean) => void;
......@@ -46,6 +47,7 @@ const LoginModal = (props: ILoginModalProps) => {
if (checked) {
localStorage.setItem('account', JSON.stringify({ username, password, checked }));
}
localStorage.setItem('expires', moment().add(11, 'hour').unix().toString());
setInitialState({ ...initialState, userInfo: result });
localStorage.setItem('user', JSON.stringify(result));
setLoading(false);
......
......@@ -96,9 +96,22 @@
box-shadow: 0 2px 8px #f0f1f2;
.chatWrapper {
border-radius: 4px;
:global {
.ant-input:focus,
.ant-input:hover,
.ant-input-focused {
border-color: #009b95;
}
.ant-input:focus,
.ant-input-focused {
box-shadow: 0 0 0 2px #009b95;
}
}
}
.outputButtonWrapper {
:global(.ant-btn-primary) {
background: #009b95;
border: solid 1px #009b95;
border-radius: 6px !important;
}
}
......
......@@ -12,12 +12,13 @@ import {
} from '@ant-design/icons';
import classNames from 'classnames';
import { history } from 'umi';
import LoginModal from '@/components/loginModal';
import NewSession from '@/components/newSession';
import LoginModal from '@/components/LoginModal';
import NewSession from '@/components/NewSession';
import { useWebSocket, useRequest } from 'ahooks';
import { GetChatGLMToken, GetPromptList } from '@/services/chatGLM/api';
import { useAccess } from 'umi';
import { PageLoading } from '@ant-design/pro-layout';
import { checkIsTokenExpires } from '@/access';
const { useBreakpoint } = Grid;
const { Title } = Typography;
......@@ -76,20 +77,22 @@ const Chat: React.FC = () => {
return messageHistory.current;
}, [answer]);
async function handleSendMessage() {
messageHistory.current.push({ prompt: chat, answer: '' });
setLoading(true);
sendMessage &&
glmToken &&
sendMessage(
JSON.stringify({
prompt: chat,
max_length: 2048,
top_p: 0.7,
temperature: 0.95,
history: [messageHistory.current.map((h) => h.prompt)],
token: glmToken,
}),
);
if (!checkIsTokenExpires()) {
messageHistory.current.push({ prompt: chat, answer: '' });
setLoading(true);
sendMessage &&
glmToken &&
sendMessage(
JSON.stringify({
prompt: chat,
max_length: 2048,
top_p: 0.7,
temperature: 0.95,
history: [messageHistory.current.map((h) => h.prompt)],
token: glmToken,
}),
);
}
}
useEffect(() => {
......
.table_action {
display: flex;
.table_action_edit {
color: #1890ff;
span {
margin-left: 3px;
}
}
.table_action_delete {
color: #F5222D;
span {
margin-left: 3px;
}
}
}
\ No newline at end of file
import { useState } from "react";
import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
import { ActionType, PageContainer } from '@ant-design/pro-components';
import { ProTable } from '@ant-design/pro-components';
import { Button, message } from 'antd';
import { isEmpty } from 'lodash';
import { useEffect, useRef, useState } from 'react';
import styles from './index.less';
import EditPromptModal from '@/components/EditAndAddPromptModal';
import DeletePromptModal from '@/components/DeletePromptModal';
import { pagePromptWords, createPromptWords, updatePromptWords, deletePromptWords, typePromptWords } from '@/services/chatGLM/api';
interface IPromptList {
id: string;
title?: string;
content: string;
type: string;
creationTime: string;
sequenceNumber: number;
}
export interface IFormValue {
type: string | null;
content: string;
}
interface ILoading {
editModalLoading: boolean;
createModalLoading: boolean;
deleteModalLoading: boolean;
listLoading: boolean;
}
const PromptWord = () => {
const actionRef = useRef<ActionType>();
const [allLoading, setAllLoading] = useState<ILoading>({
editModalLoading: false,
createModalLoading: false,
deleteModalLoading: false,
listLoading: false
});
const [editAndAddModalOpen, setEditAndDeleteModalOpen] = useState(false);
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
const [promptList, setPromptList] = useState<IPromptList[]>([]);
const [modalType, setModalType] = useState<'create' | 'edit' | ''>('');
const [types, setTypes] = useState<string[]>([]);
const [currentRow, setCurrentRaw] = useState({} as IPromptList);
const columns: any[] = [
{
title: '类型',
width: '20%',
key: 'type',
dataIndex: 'type',
valueType: 'select',
order: 1,
fieldProps: {
options: types.map((type) => {
return {
label: type,
value: type
}
})
}
},
{
title: '提示词语',
width: '40%',
key: 'promptWord',
dataIndex: 'content',
ellipsis: true,
search: true,
order: 2
},
{
title: '创建时间',
width: '25%',
key: 'creationTime',
dataIndex: 'creationTime',
search: false,
},
{
title: '操作',
valueType: 'option',
width: '15%',
key: 'option',
render: (_: unknown, record: IPromptList) => (
<div className={styles.table_action}>
<div
className={styles.table_action_edit}
onClick={() => {
setCurrentRaw(record);
setEditAndDeleteModalOpen(true);
setModalType('edit');
}}
>
<EditOutlined />
<span>编辑</span>
</div>
<span style={{ margin: '0 9px' }}>|</span>
<div
className={styles.table_action_delete}
onClick={() => {
setDeleteModalOpen(true);
setCurrentRaw(record);
}}
>
<DeleteOutlined />
<span>删除</span>
</div>
</div>
),
},
];
const fetchTypes = async () => {
const data = await typePromptWords();
setTypes(data);
};
useEffect(() => {
fetchTypes();
}, []);
const handleEditPrompt = async (editValue: IFormValue) => {
setAllLoading({ ...allLoading, editModalLoading: true });
try {
await updatePromptWords({
id: currentRow.id,
content: editValue.content,
type: editValue.type
});
setAllLoading({ ...allLoading, editModalLoading: false });
actionRef.current?.reload();
} catch(error) {
setAllLoading({ ...allLoading, editModalLoading: false });
message.error('编辑提示用语失败,请重试!')
}
};
const handleCreatePrompt = async (createValue: IFormValue) => {
setAllLoading({ ...allLoading, createModalLoading: true });
try {
await createPromptWords({
content: createValue.content,
type: createValue.type
})
setAllLoading({ ...allLoading, createModalLoading: false });
actionRef.current?.reload();
} catch(error) {
setAllLoading({ ...allLoading, createModalLoading: false });
message.error('新增提示用语失败,请重试!')
}
};
const handleDeletePrompt = async () => {
setAllLoading({ ...allLoading, deleteModalLoading: true });
try {
await deletePromptWords({
id: currentRow.id
});
setAllLoading({ ...allLoading, deleteModalLoading: false });
actionRef.current?.reload();
} catch(error) {
setAllLoading({ ...allLoading, deleteModalLoading: false });
message.error('删除提示用语失败,请重试!')
}
};
return (
<>
PromptWord
</>
)
<PageContainer>
<DeletePromptModal
open={deleteModalOpen}
setOpen={setDeleteModalOpen}
title='删除提示用语'
tip='确定要删除这段提示词语吗?'
content='一旦删除后,用户无法看到这一条提示词,建议谨慎操作。'
handleDeletePrompt={handleDeletePrompt}
loading={allLoading.deleteModalLoading}
/>
<EditPromptModal
open={editAndAddModalOpen}
setOpen={setEditAndDeleteModalOpen}
types={types}
modalType={modalType}
handleEditPrompt={handleEditPrompt}
oldRow={currentRow}
handleCreatePrompt={handleCreatePrompt}
loading={modalType === 'create' ? allLoading.createModalLoading : allLoading.editModalLoading}
/>
<ProTable
request={async (params = { pageSize: 10, current: 0 }) => {
setAllLoading({ ...allLoading, listLoading: true });
const promptListParams = {
filter: params?.promptWord || '',
type: params?.type || '',
pageIndex: params?.current as number - 1,
pageSize: params?.pageSize,
}
try {
const response = await pagePromptWords(promptListParams);
setAllLoading({ ...allLoading, listLoading: false });
return {
data: response.items,
total: response.totalCount,
success: !isEmpty(response),
}
} catch(error) {
setAllLoading({ ...allLoading, listLoading: false });
message.error('请求列表失败,请重试!');
return {
data: [],
total: 0,
success: false,
}
}
}}
actionRef={actionRef}
columns={columns}
cardBordered
rowKey="id"
pagination={{
pageSize: 10,
}}
options={false}
dateFormatter="string"
headerTitle="账号列表"
toolBarRender={() => [
<Button
key="button"
icon={<PlusOutlined />}
onClick={() => {
setEditAndDeleteModalOpen(true);
setModalType('create');
}}
type="primary"
>
添加提示语
</Button>,
]}
loading={allLoading.listLoading}
/>
</PageContainer>
);
};
export default PromptWord;
......@@ -92,10 +92,44 @@ export const getUserMessage = (params?: any): Promise<string> => {
});
};
export const createPromptWords = (params: any): Promise<any> => {
return request('https://glm-mangement-api.baibaomen.com/PromptWords/create', {
method: 'POST',
data: params
})
}
export const updatePromptWords = (params: any): Promise<any> => {
return request('https://glm-mangement-api.baibaomen.com/PromptWords/update', {
method: 'POST',
data: params
})
}
export const deletePromptWords = (params: any): Promise<any> => {
return request('https://glm-mangement-api.baibaomen.com/PromptWords/delete', {
method: 'POST',
data: params
})
}
export const pagePromptWords = (params: any): Promise<any> => {
return request('https://glm-mangement-api.baibaomen.com/PromptWords/page', {
method: 'POST',
data: params
})
}
export const typePromptWords = (): Promise<any> => {
return request('https://glm-mangement-api.baibaomen.com/PromptWords/type', {
method: 'POST',
})
}
export const postDeleteUser = (params?: any): Promise<string> => {
return request('https://glm-mangement-api.baibaomen.com/User/delete', {
method: 'POST',
body: JSON.stringify(params),
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