update
This commit is contained in:
parent
c6c6f3f47f
commit
4a052a89f2
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 30 KiB |
|
@ -1,506 +0,0 @@
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { Card, Input, Button, Space, Flex, Layout, List, Tabs, message } from 'antd';
|
|
||||||
import { useState, useMemo, useEffect } from 'react';
|
|
||||||
import HightLightMarkdown from '@/components/highlight-markdown';
|
|
||||||
import { Document, Packer, Paragraph, TextRun } from 'docx';
|
|
||||||
import { saveAs } from 'file-saver';
|
|
||||||
import { marked } from 'marked';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { aiAssistantConfig } from '@/pages/write/ai-assistant-config';
|
|
||||||
|
|
||||||
const { Sider, Content } = Layout;
|
|
||||||
const { TabPane } = Tabs;
|
|
||||||
|
|
||||||
interface TemplateItem {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Write = () => {
|
|
||||||
const { t } = useTranslate('write');
|
|
||||||
const [content, setContent] = useState('');
|
|
||||||
const [aiQuestion, setAiQuestion] = useState('');
|
|
||||||
const [isAiLoading, setIsAiLoading] = useState(false);
|
|
||||||
const [dialogId, setDialogId] = useState('');
|
|
||||||
// 定义模板内容
|
|
||||||
const [templates] = useState<TemplateItem[]>([
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
name: t('defaultTemplate'),
|
|
||||||
content: `# ${t('defaultTemplateTitle')}
|
|
||||||
|
|
||||||
## ${t('introduction')}
|
|
||||||
|
|
||||||
## ${t('mainContent')}
|
|
||||||
|
|
||||||
## ${t('conclusion')}
|
|
||||||
`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
name: t('technicalDoc'),
|
|
||||||
content: `# ${t('technicalDocTitle')}
|
|
||||||
|
|
||||||
## ${t('overview')}
|
|
||||||
|
|
||||||
## ${t('requirements')}
|
|
||||||
|
|
||||||
## ${t('architecture')}
|
|
||||||
|
|
||||||
## ${t('implementation')}
|
|
||||||
|
|
||||||
## ${t('testing')}
|
|
||||||
|
|
||||||
## ${t('deployment')}
|
|
||||||
|
|
||||||
## ${t('maintenance')}
|
|
||||||
`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
name: t('meetingMinutes'),
|
|
||||||
content: `# ${t('meetingMinutesTitle')}
|
|
||||||
|
|
||||||
## ${t('date')}: ${new Date().toLocaleDateString()}
|
|
||||||
|
|
||||||
## ${t('participants')}
|
|
||||||
|
|
||||||
## ${t('agenda')}
|
|
||||||
|
|
||||||
## ${t('discussions')}
|
|
||||||
|
|
||||||
## ${t('decisions')}
|
|
||||||
|
|
||||||
## ${t('actionItems')}
|
|
||||||
|
|
||||||
## ${t('nextMeeting')}
|
|
||||||
`
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const [selectedTemplate, setSelectedTemplate] = useState<string>('1');
|
|
||||||
const [viewMode, setViewMode] = useState<'edit' | 'preview' | 'split'>('split');
|
|
||||||
|
|
||||||
// 在组件加载时获取对话列表
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchDialogList = async () => {
|
|
||||||
try {
|
|
||||||
const authorization = localStorage.getItem('Authorization');
|
|
||||||
if (!authorization) return;
|
|
||||||
|
|
||||||
const response = await axios.get('/v1/dialog/list', {
|
|
||||||
headers: {
|
|
||||||
'authorization': authorization
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data?.data?.length > 0) {
|
|
||||||
// 使用第一个对话的ID
|
|
||||||
setDialogId(response.data.data[0].id);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取对话列表失败:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchDialogList();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 处理模板选择
|
|
||||||
const handleTemplateSelect = (templateId: string) => {
|
|
||||||
setSelectedTemplate(templateId);
|
|
||||||
// 查找选中的模板
|
|
||||||
const selectedTemplateItem = templates.find(item => item.id === templateId);
|
|
||||||
if (selectedTemplateItem) {
|
|
||||||
// 填充模板内容
|
|
||||||
setContent(selectedTemplateItem.content);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 实现保存为Word功能
|
|
||||||
const handleSave = () => {
|
|
||||||
// 获取当前选中的模板名称
|
|
||||||
const selectedTemplateItem = templates.find(item => item.id === selectedTemplate);
|
|
||||||
if (!selectedTemplateItem) return;
|
|
||||||
|
|
||||||
// 生成文件名:模板名+当前日期,例如:学术论文20250319.docx
|
|
||||||
const today = new Date();
|
|
||||||
const dateStr = `${today.getFullYear()}${String(today.getMonth() + 1).padStart(2, '0')}${String(today.getDate()).padStart(2, '0')}`;
|
|
||||||
const fileName = `${selectedTemplateItem.name}${dateStr}.docx`;
|
|
||||||
|
|
||||||
// 使用 marked 解析 Markdown 内容
|
|
||||||
const tokens = marked.lexer(content);
|
|
||||||
|
|
||||||
// 创建段落数组
|
|
||||||
const paragraphs: Paragraph[] = [];
|
|
||||||
|
|
||||||
tokens.forEach(token => {
|
|
||||||
if (token.type === 'heading') {
|
|
||||||
// 处理标题
|
|
||||||
const headingToken = token as any; // 使用 any 类型绕过 marked.Tokens 问题
|
|
||||||
let headingType: 'Heading1' | 'Heading2' | 'Heading3' | 'Heading4' | 'Heading5' | 'Heading6' | undefined;
|
|
||||||
|
|
||||||
// 根据标题级别设置正确的标题类型
|
|
||||||
switch (headingToken.depth) {
|
|
||||||
case 1:
|
|
||||||
headingType = 'Heading1';
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
headingType = 'Heading2';
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
headingType = 'Heading3';
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
headingType = 'Heading4';
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
headingType = 'Heading5';
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
headingType = 'Heading6';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
headingType = 'Heading1';
|
|
||||||
}
|
|
||||||
|
|
||||||
paragraphs.push(
|
|
||||||
new Paragraph({
|
|
||||||
children: [
|
|
||||||
new TextRun({
|
|
||||||
text: headingToken.text,
|
|
||||||
size: 28 - (headingToken.depth * 2)
|
|
||||||
})
|
|
||||||
],
|
|
||||||
heading: headingType,
|
|
||||||
spacing: { after: 200 - (headingToken.depth * 40) }
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (token.type === 'paragraph') {
|
|
||||||
// 处理段落
|
|
||||||
const paraToken = token as any; // 使用 any 类型绕过 marked.Tokens 问题
|
|
||||||
paragraphs.push(
|
|
||||||
new Paragraph({
|
|
||||||
children: [
|
|
||||||
new TextRun({
|
|
||||||
text: paraToken.text
|
|
||||||
})
|
|
||||||
],
|
|
||||||
spacing: { after: 80 }
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (token.type === 'space') {
|
|
||||||
// 处理空行
|
|
||||||
paragraphs.push(new Paragraph({}));
|
|
||||||
}
|
|
||||||
// 可以根据需要添加对其他类型的处理,如列表、表格等
|
|
||||||
});
|
|
||||||
|
|
||||||
// 将所有段落添加到文档中
|
|
||||||
const doc = new Document({
|
|
||||||
sections: [{
|
|
||||||
children: paragraphs
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
// 生成并下载 Word 文档
|
|
||||||
Packer.toBlob(doc).then(blob => {
|
|
||||||
saveAs(blob, fileName);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// 渲染编辑器部分
|
|
||||||
const renderEditor = () => (
|
|
||||||
<Input.TextArea
|
|
||||||
style={{
|
|
||||||
height: '100%',
|
|
||||||
border: 'none',
|
|
||||||
padding: 24,
|
|
||||||
fontSize: 16,
|
|
||||||
resize: 'none',
|
|
||||||
}}
|
|
||||||
value={content}
|
|
||||||
onChange={(e) => setContent(e.target.value)}
|
|
||||||
placeholder={t('writePlaceholder')}
|
|
||||||
autoSize={false}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
// 渲染预览部分
|
|
||||||
const renderPreview = () => (
|
|
||||||
<div style={{
|
|
||||||
height: '100%',
|
|
||||||
padding: 24,
|
|
||||||
overflow: 'auto',
|
|
||||||
fontSize: 16,
|
|
||||||
}}>
|
|
||||||
<HightLightMarkdown>
|
|
||||||
{content || t('previewPlaceholder')}
|
|
||||||
</HightLightMarkdown>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
// 根据视图模式渲染内容
|
|
||||||
const renderContent = () => {
|
|
||||||
switch (viewMode) {
|
|
||||||
case 'edit':
|
|
||||||
return renderEditor();
|
|
||||||
case 'preview':
|
|
||||||
return renderPreview();
|
|
||||||
case 'split':
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<Flex style={{ height: '100%' }}>
|
|
||||||
<div style={{ width: '50%', borderRight: '1px solid #f0f0f0', height: '100%' }}>
|
|
||||||
{renderEditor()}
|
|
||||||
</div>
|
|
||||||
<div style={{ width: '50%', height: '100%' }}>
|
|
||||||
{renderPreview()}
|
|
||||||
</div>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理 AI 助手问题提交
|
|
||||||
const handleAiQuestionSubmit = async (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
||||||
if (e.key === 'Enter' && !e.shiftKey) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (!aiQuestion.trim()) {
|
|
||||||
message.warning('请输入问题');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dialogId) {
|
|
||||||
message.error('未找到可用的对话');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsAiLoading(true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const authorization = localStorage.getItem('Authorization');
|
|
||||||
if (!authorization) {
|
|
||||||
message.error('未登录,请先登录');
|
|
||||||
setIsAiLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 生成一个随机的会话ID
|
|
||||||
const conversationId = Math.random().toString(36).substring(2) + Date.now().toString(36);
|
|
||||||
|
|
||||||
// 创建新会话
|
|
||||||
const createSessionResponse = await axios.post(
|
|
||||||
'v1/conversation/set',
|
|
||||||
{
|
|
||||||
dialog_id: dialogId,
|
|
||||||
name: "文档撰写对话",
|
|
||||||
is_new: true,
|
|
||||||
conversation_id: conversationId,
|
|
||||||
message: [
|
|
||||||
{
|
|
||||||
role: "assistant",
|
|
||||||
content: "新对话"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'authorization': authorization
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!createSessionResponse.data?.data?.id) {
|
|
||||||
message.error('创建会话失败');
|
|
||||||
setIsAiLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 组合当前问题和编辑器内容
|
|
||||||
const combinedQuestion = `${aiQuestion}\n\n当前文档内容:\n${content}`;
|
|
||||||
|
|
||||||
console.log('发送问题到 AI 助手:', aiQuestion);
|
|
||||||
|
|
||||||
const response = await axios.post(
|
|
||||||
'/v1/conversation/completion',
|
|
||||||
{
|
|
||||||
conversation_id: conversationId,
|
|
||||||
messages: [
|
|
||||||
{
|
|
||||||
role: "user",
|
|
||||||
content: combinedQuestion
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
timeout: aiAssistantConfig.api.timeout,
|
|
||||||
headers: {
|
|
||||||
'authorization': authorization
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// 修改响应处理逻辑
|
|
||||||
if (response.data) {
|
|
||||||
try {
|
|
||||||
const lines = response.data.split('\n').filter((line: string) => line.trim());
|
|
||||||
let accumulatedContent = ''; // 累积的内容
|
|
||||||
|
|
||||||
// 初始化编辑器内容
|
|
||||||
setContent('');
|
|
||||||
|
|
||||||
// 处理每一行数据
|
|
||||||
lines.forEach((line: string, index:number) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
try {
|
|
||||||
const jsonStr = line.replace('data:', '').trim();
|
|
||||||
const jsonData = JSON.parse(jsonStr);
|
|
||||||
|
|
||||||
if (jsonData.code === 0 && jsonData.data?.answer) {
|
|
||||||
const answer = jsonData.data.answer;
|
|
||||||
|
|
||||||
// 过滤掉 think 标签内容
|
|
||||||
const cleanedAnswer = answer.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
|
|
||||||
|
|
||||||
if (cleanedAnswer) {
|
|
||||||
// 更新累积内容
|
|
||||||
accumulatedContent = cleanedAnswer;
|
|
||||||
// 更新编辑器
|
|
||||||
setContent(accumulatedContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (parseErr) {
|
|
||||||
console.error('解析单行数据失败:', parseErr);
|
|
||||||
}
|
|
||||||
}, index * 100); // 每100毫秒处理一行
|
|
||||||
});
|
|
||||||
// 在处理完所有响应后,删除临时会话
|
|
||||||
setTimeout(async () => {
|
|
||||||
try {
|
|
||||||
await axios.post('/v1/conversation/rm', {
|
|
||||||
conversation_ids: [conversationId],
|
|
||||||
dialog_id: dialogId
|
|
||||||
}, {
|
|
||||||
headers: {
|
|
||||||
'authorization': authorization
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.log('临时会话已删除:', conversationId);
|
|
||||||
} catch (rmErr) {
|
|
||||||
console.error('删除临时会话失败:', rmErr);
|
|
||||||
}
|
|
||||||
}, lines.length * 100 + 500);
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
console.error('解析响应数据失败:', err);
|
|
||||||
message.error('获取 AI 回答失败');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
finally {
|
|
||||||
setIsAiLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<Layout style={{ height: 'auto', padding: 24, overflow: 'hidden'}}>
|
|
||||||
<Sider
|
|
||||||
width={280}
|
|
||||||
theme="light"
|
|
||||||
style={{
|
|
||||||
borderRight: '1px solid #f0f0f0',
|
|
||||||
padding: '0 16px',
|
|
||||||
overflow: 'auto',
|
|
||||||
height: '100%'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<List
|
|
||||||
header={<div className="templateHeader" style={{ textAlign: 'center'}}>{t('templateList')}</div>}
|
|
||||||
dataSource={templates}
|
|
||||||
renderItem={(item) => (
|
|
||||||
<List.Item
|
|
||||||
actions={[<a key="select" onClick={() => handleTemplateSelect(item.id)}></a>]}
|
|
||||||
style={{
|
|
||||||
cursor: 'pointer',
|
|
||||||
background: selectedTemplate === item.id ? '#f0f7ff' : 'none',
|
|
||||||
borderRadius: 8,
|
|
||||||
paddingLeft: 12
|
|
||||||
}}
|
|
||||||
onClick={() => handleTemplateSelect(item.id)}
|
|
||||||
>
|
|
||||||
<span className="templateItemText">{item.name}</span>
|
|
||||||
</List.Item>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Sider>
|
|
||||||
<Content style={{ paddingLeft: 24 }}>
|
|
||||||
<Flex vertical style={{ height: '100%', gap: 16 }}>
|
|
||||||
<Flex justify="space-between" align="center" style={{ marginBottom: 16 }}>
|
|
||||||
<h2 className="pageTitle">{t('writeDocument')}</h2>
|
|
||||||
<Space>
|
|
||||||
<Button.Group>
|
|
||||||
<Button
|
|
||||||
type={viewMode === 'edit' ? 'primary' : 'default'}
|
|
||||||
onClick={() => setViewMode('edit')}
|
|
||||||
>
|
|
||||||
{t('edit')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type={viewMode === 'split' ? 'primary' : 'default'}
|
|
||||||
onClick={() => setViewMode('split')}
|
|
||||||
>
|
|
||||||
{t('split')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type={viewMode === 'preview' ? 'primary' : 'default'}
|
|
||||||
onClick={() => setViewMode('preview')}
|
|
||||||
>
|
|
||||||
{t('preview')}
|
|
||||||
</Button>
|
|
||||||
</Button.Group>
|
|
||||||
<Button type="primary" onClick={handleSave}>
|
|
||||||
{t('save')}
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</Flex>
|
|
||||||
<Card bodyStyle={{
|
|
||||||
padding: 0,
|
|
||||||
height: 'calc(70vh - 100px)',
|
|
||||||
overflow: 'hidden',
|
|
||||||
position: 'relative'
|
|
||||||
}}>
|
|
||||||
{renderContent()}
|
|
||||||
</Card>
|
|
||||||
<Card
|
|
||||||
title={t('aiAssistant')}
|
|
||||||
bodyStyle={{
|
|
||||||
padding: 10,
|
|
||||||
height: 'auto',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
gap: 10
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="dialogContainer">
|
|
||||||
{isAiLoading && (
|
|
||||||
<div style={{ textAlign: 'center'}}>
|
|
||||||
<div>AI 助手正在思考中...</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Input.TextArea
|
|
||||||
className="inputArea"
|
|
||||||
placeholder={t('askAI')}
|
|
||||||
autoSize={{ minRows: 3, maxRows: 6 }}
|
|
||||||
value={aiQuestion}
|
|
||||||
onChange={(e) => setAiQuestion(e.target.value)}
|
|
||||||
onKeyDown={handleAiQuestionSubmit}
|
|
||||||
disabled={isAiLoading}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</Flex>
|
|
||||||
</Content>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Write;
|
|
|
@ -1,98 +0,0 @@
|
||||||
yarn install
|
|
||||||
yarn start
|
|
||||||
|
|
||||||
1. 跳过登录验证
|
|
||||||
src\utils\request.ts
|
|
||||||
|
|
||||||
return new Response(JSON.stringify({
|
|
||||||
code: 0,
|
|
||||||
message: 'Success',
|
|
||||||
data: {}
|
|
||||||
}));
|
|
||||||
|
|
||||||
2. 移除Agent
|
|
||||||
src\layouts\components\header\index.tsx
|
|
||||||
|
|
||||||
|
|
||||||
注释掉:{ path: '/flow', name: t('flow'), icon: GraphIcon },
|
|
||||||
|
|
||||||
3. 移除登陆页注册功能
|
|
||||||
src\pages\login\index.tsx
|
|
||||||
|
|
||||||
注释掉:
|
|
||||||
{ <div>
|
|
||||||
{title === 'login' && (
|
|
||||||
<div>
|
|
||||||
{t('signInTip')}
|
|
||||||
<Button type="link" onClick={changeTitle}>
|
|
||||||
{t('signUp')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{title === 'register' && (
|
|
||||||
<div>
|
|
||||||
{t('signUpTip')}
|
|
||||||
<Button type="link" onClick={changeTitle}>
|
|
||||||
{t('login')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>}
|
|
||||||
|
|
||||||
4. 修改登录页相关描述
|
|
||||||
src\pages\login\right-panel.tsx
|
|
||||||
|
|
||||||
注释
|
|
||||||
{/* <Text className={classNames(styles.pink, styles.loginDescription)}>
|
|
||||||
{t('description')}
|
|
||||||
</Text>
|
|
||||||
<Flex align="center" gap={16}>
|
|
||||||
<Avatars></Avatars>
|
|
||||||
<Flex vertical>
|
|
||||||
<Space>
|
|
||||||
<Rate disabled defaultValue={5} />
|
|
||||||
<span
|
|
||||||
className={classNames(styles.white, styles.loginRateNumber)}
|
|
||||||
>
|
|
||||||
5.0
|
|
||||||
</span>
|
|
||||||
</Space>
|
|
||||||
<span className={classNames(styles.pink, styles.loginRateReviews)}>
|
|
||||||
{t('review')}
|
|
||||||
</span>
|
|
||||||
</Flex>
|
|
||||||
</Flex> */}
|
|
||||||
|
|
||||||
src\locales\zh.ts
|
|
||||||
开始构建您的智能助手
|
|
||||||
修改为:西电711大模型平台
|
|
||||||
styles.white 改成 styles.pink
|
|
||||||
很高兴再次见到您!改成 很高兴见到您!
|
|
||||||
|
|
||||||
5. 删除主页github按钮
|
|
||||||
src\layouts\components\right-toolbar\index.tsx
|
|
||||||
|
|
||||||
注释:
|
|
||||||
{/* <Circle>
|
|
||||||
<GithubOutlined onClick={handleGithubCLick} />
|
|
||||||
</Circle> */}
|
|
||||||
|
|
||||||
6. 修改默认头像
|
|
||||||
src\layouts\components\user\index.tsx
|
|
||||||
|
|
||||||
https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png
|
|
||||||
修改为 https://picx.zhimg.com/v2-aaf12b68b54b8812e6b449e7368d30cf_l.jpg
|
|
||||||
|
|
||||||
7. 修改软件左侧表头
|
|
||||||
ragflow/web/public/logo.svg 替换
|
|
||||||
src\conf.json "appName": "西电711大模型平台"
|
|
||||||
|
|
||||||
取消链接:删除 href={window.location.origin}
|
|
||||||
|
|
||||||
|
|
||||||
8. 批量账户插入
|
|
||||||
密码验证逻辑:前端先用RSA加密传到后端,通过check_password_hash和数据库里的hash值进行对比
|
|
||||||
后端用RSA解密,
|
|
||||||
|
|
||||||
看我两个python文件。
|
|
||||||
|
|
Loading…
Reference in New Issue