2 Commits
v1.2.1 ... main

Author SHA1 Message Date
0b67bbe885 feat: 上传文件功能改进
- 文件上传后显示标记(文件名标签),不填充输入框
- 用户正常输入问题,发送时文件内容自动附加
- 文件内容作为系统消息提交给AI
- 界面显示'已上传文件'标记,可点击移除
- 发送后自动清除文件标记
2026-04-29 22:59:39 +08:00
b98ab79ae2 feat: 上传文件功能优化
- 只支持文本类型文件(txt、md、json、csv、代码文件等)
- 最多读取 10000 字符,超出自动截取
- 文件内容附加到输入框,用户可编辑后发送
- 与用户消息一起提交给大模型处理
- 移除 PDF/DOC 等非文本文件支持
2026-04-29 22:51:06 +08:00
2 changed files with 157 additions and 30 deletions

View File

@@ -25,6 +25,9 @@ let systemAgents = [];
// 用户智能体界面显示的智能体
let agents = [];
// 上传的文件(临时存储)
let uploadedFile = null; // { name: string, content: string }
// 用户添加的智能体(按类别分组)
let myAgents = {
basic: [],
@@ -3249,7 +3252,7 @@ function showAgentChatPage() {
</div>
</div>
<input type="file" id="imageInput" accept="image/*" style="display:none">
<input type="file" id="fileInput" accept=".txt,.md,.pdf,.doc,.docx,.json,.csv" style="display:none">
<input type="file" id="fileInput" accept=".txt,.md,.json,.csv,.xml,.yaml,.yml,.log,.sql,.html,.css,.js,.py,.java,.c,.cpp,.go,.rs,.sh,.bash,.ini,.conf,.cfg" style="display:none">
<!-- 搜索栏 -->
<div class="search-bar" id="searchBar">
@@ -3780,7 +3783,7 @@ function openConversation(id) {
</div>
</div>
<input type="file" id="imageInput" accept="image/*" style="display:none">
<input type="file" id="fileInput" accept=".txt,.md,.pdf,.doc,.docx,.json,.csv" style="display:none">
<input type="file" id="fileInput" accept=".txt,.md,.json,.csv,.xml,.yaml,.yml,.log,.sql,.html,.css,.js,.py,.java,.c,.cpp,.go,.rs,.sh,.bash,.ini,.conf,.cfg" style="display:none">
</div>
`;
@@ -3997,8 +4000,26 @@ async function sendMessage() {
// 隐藏欢迎界面
welcome.style.display = 'none';
// 添加用户消息
currentConversation.messages.push({ role: 'user', content: text });
// 构建用户消息内容
let userContent = text;
let fileContent = null;
// 如果有上传的文件,附加文件内容
if (uploadedFile) {
fileContent = uploadedFile;
userContent = text; // 用户输入的问题
// 发送后清除上传文件标记
uploadedFile = null;
const fileTagArea = document.getElementById('fileTagArea');
if (fileTagArea) fileTagArea.innerHTML = '';
}
// 添加用户消息(界面显示)
const displayContent = fileContent
? `${text}\n\n📎 已上传文件: ${fileContent.name}`
: text;
currentConversation.messages.push({ role: 'user', content: displayContent });
// 更新对话标题(第一条用户消息)
if (currentConversation.title === '新对话') {
@@ -4033,11 +4054,11 @@ async function sendMessage() {
autoResize(userInput);
// 调用流式生成
await streamGenerate(currentConversation.messages.length - 1);
await streamGenerate(currentConversation.messages.length - 1, fileContent);
}
// 流式生成 AI 回复
async function streamGenerate(userMsgIndex) {
async function streamGenerate(userMsgIndex, fileContent = null) {
isLoading = true;
sendBtn.disabled = true;
@@ -4082,6 +4103,14 @@ async function streamGenerate(userMsgIndex) {
content: m.content
}));
// 如果有上传的文件,添加文件内容作为系统消息
if (fileContent) {
messagesToSend.push({
role: 'system',
content: `用户上传了文件 "${fileContent.name}",以下是文件内容:\n\n${fileContent.content}`
});
}
// 如果有搜索结果,将搜索内容添加到消息中
if (searchResults) {
const searchContext = formatSearchResultsForLLM(searchResults);
@@ -5299,39 +5328,93 @@ async function handleFileUpload(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = async (event) => {
const content = event.target.result;
const fileName = file.name;
// 添加用户消息
currentConversation.messages.push({
role: 'user',
content: `[文件: ${fileName}]\\n\\n${content.slice(0, 500)}${content.length > 500 ? '...' : ''}`
});
currentConversation.updatedAt = Date.now();
saveConversations();
renderMessages();
if (welcome) welcome.style.display = 'none';
// 调用AI生成
await streamGenerateWithFile(content, fileName);
};
// 检查文件类型(只支持文本文件)
const allowedExtensions = ['.txt', '.md', '.json', '.csv', '.xml', '.yaml', '.yml', '.log', '.sql', '.html', '.css', '.js', '.py', '.java', '.c', '.cpp', '.go', '.rs', '.sh', '.bash', '.ini', '.conf', '.cfg'];
const fileName = file.name.toLowerCase();
const isAllowed = allowedExtensions.some(ext => fileName.endsWith(ext));
// 根据文件类型读取
if (file.name.endsWith('.pdf') || file.name.endsWith('.doc') || file.name.endsWith('.docx')) {
// PDF/Word文件暂时只显示文件名
showToast('PDF/Word文件暂不支持解析请上传文本文件');
if (!isAllowed) {
showToast('只支持文本类型的文件txt、md、json、代码文件等');
e.target.value = '';
return;
}
// 检查文件大小(限制约 10KB大约 10000 字符)
const maxSizeKB = 15; // 留一点余量
if (file.size > maxSizeKB * 1024) {
showToast(`文件太大,请上传小于 ${maxSizeKB}KB 的文本文件`);
e.target.value = '';
return;
}
showToast('正在读取文件...');
const reader = new FileReader();
reader.onload = async (event) => {
let content = event.target.result;
// 限制最多 10000 字符
if (content.length > 10000) {
content = content.slice(0, 10000);
}
// 存储上传的文件
uploadedFile = {
name: file.name,
content: content
};
// 显示文件标记
showUploadedFileTag();
showToast(`已上传: ${file.name}`);
userInput.focus();
};
reader.onerror = () => {
showToast('文件读取失败');
};
reader.readAsText(file);
e.target.value = '';
}
// 显示上传文件标记
function showUploadedFileTag() {
if (!uploadedFile) return;
// 查找或创建文件标记区域
let fileTagArea = document.getElementById('fileTagArea');
if (!fileTagArea) {
fileTagArea = document.createElement('div');
fileTagArea.id = 'fileTagArea';
fileTagArea.className = 'file-tag-area';
// 插入到输入区域前面
const inputArea = document.querySelector('.input-area');
if (inputArea) {
inputArea.insertBefore(fileTagArea, inputArea.firstChild);
}
}
fileTagArea.innerHTML = `
<div class="file-tag">
<span class="file-tag-icon">📄</span>
<span class="file-tag-name">${uploadedFile.name}</span>
<button class="file-tag-remove" onclick="removeUploadedFile()">✕</button>
</div>
`;
}
// 移除上传的文件
function removeUploadedFile() {
uploadedFile = null;
const fileTagArea = document.getElementById('fileTagArea');
if (fileTagArea) {
fileTagArea.innerHTML = '';
}
showToast('已移除文件');
}
// 带图片的流式生成
async function streamGenerateWithImage(base64, imageName) {
isLoading = true;

View File

@@ -3198,6 +3198,50 @@ body {
color: var(--text-color);
}
/* 文件标记区域 */
.file-tag-area {
padding: 8px 12px;
background: rgba(102, 126, 234, 0.05);
border-radius: 8px;
margin-bottom: 8px;
}
.file-tag {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background: var(--primary);
color: white;
border-radius: 20px;
font-size: 13px;
}
.file-tag-icon {
font-size: 16px;
}
.file-tag-name {
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-tag-remove {
background: none;
border: none;
color: white;
font-size: 14px;
cursor: pointer;
padding: 0 4px;
opacity: 0.8;
}
.file-tag-remove:hover {
opacity: 1;
}
#userInput {
flex: 1;
padding: 12px 16px;