feat: 上传文件功能改进
- 文件上传后显示标记(文件名标签),不填充输入框 - 用户正常输入问题,发送时文件内容自动附加 - 文件内容作为系统消息提交给AI - 界面显示'已上传文件'标记,可点击移除 - 发送后自动清除文件标记
This commit is contained in:
94
www/app.js
94
www/app.js
@@ -25,6 +25,9 @@ let systemAgents = [];
|
|||||||
// 用户智能体界面显示的智能体
|
// 用户智能体界面显示的智能体
|
||||||
let agents = [];
|
let agents = [];
|
||||||
|
|
||||||
|
// 上传的文件(临时存储)
|
||||||
|
let uploadedFile = null; // { name: string, content: string }
|
||||||
|
|
||||||
// 用户添加的智能体(按类别分组)
|
// 用户添加的智能体(按类别分组)
|
||||||
let myAgents = {
|
let myAgents = {
|
||||||
basic: [],
|
basic: [],
|
||||||
@@ -3997,8 +4000,26 @@ async function sendMessage() {
|
|||||||
// 隐藏欢迎界面
|
// 隐藏欢迎界面
|
||||||
welcome.style.display = 'none';
|
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 === '新对话') {
|
if (currentConversation.title === '新对话') {
|
||||||
@@ -4033,11 +4054,11 @@ async function sendMessage() {
|
|||||||
autoResize(userInput);
|
autoResize(userInput);
|
||||||
|
|
||||||
// 调用流式生成
|
// 调用流式生成
|
||||||
await streamGenerate(currentConversation.messages.length - 1);
|
await streamGenerate(currentConversation.messages.length - 1, fileContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 流式生成 AI 回复
|
// 流式生成 AI 回复
|
||||||
async function streamGenerate(userMsgIndex) {
|
async function streamGenerate(userMsgIndex, fileContent = null) {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
sendBtn.disabled = true;
|
sendBtn.disabled = true;
|
||||||
|
|
||||||
@@ -4082,6 +4103,14 @@ async function streamGenerate(userMsgIndex) {
|
|||||||
content: m.content
|
content: m.content
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// 如果有上传的文件,添加文件内容作为系统消息
|
||||||
|
if (fileContent) {
|
||||||
|
messagesToSend.push({
|
||||||
|
role: 'system',
|
||||||
|
content: `用户上传了文件 "${fileContent.name}",以下是文件内容:\n\n${fileContent.content}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 如果有搜索结果,将搜索内容添加到消息中
|
// 如果有搜索结果,将搜索内容添加到消息中
|
||||||
if (searchResults) {
|
if (searchResults) {
|
||||||
const searchContext = formatSearchResultsForLLM(searchResults);
|
const searchContext = formatSearchResultsForLLM(searchResults);
|
||||||
@@ -5327,24 +5356,19 @@ async function handleFileUpload(e) {
|
|||||||
// 限制最多 10000 字符
|
// 限制最多 10000 字符
|
||||||
if (content.length > 10000) {
|
if (content.length > 10000) {
|
||||||
content = content.slice(0, 10000);
|
content = content.slice(0, 10000);
|
||||||
showToast(`文件内容已截取前 10000 字符`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 把文件内容附加到输入框,让用户可以添加额外说明
|
// 存储上传的文件
|
||||||
const currentText = userInput.value.trim();
|
uploadedFile = {
|
||||||
const filePrefix = `【文件:${file.name}】\n内容如下:\n`;
|
name: file.name,
|
||||||
|
content: content
|
||||||
|
};
|
||||||
|
|
||||||
if (currentText) {
|
// 显示文件标记
|
||||||
userInput.value = `${currentText}\n\n${filePrefix}${content}`;
|
showUploadedFileTag();
|
||||||
} else {
|
|
||||||
userInput.value = `${filePrefix}${content}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 自动调整输入框高度
|
showToast(`已上传: ${file.name}`);
|
||||||
autoResize(userInput);
|
|
||||||
userInput.focus();
|
userInput.focus();
|
||||||
|
|
||||||
showToast('文件内容已添加到输入框');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
reader.onerror = () => {
|
reader.onerror = () => {
|
||||||
@@ -5355,6 +5379,42 @@ async function handleFileUpload(e) {
|
|||||||
e.target.value = '';
|
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) {
|
async function streamGenerateWithImage(base64, imageName) {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
|||||||
@@ -3198,6 +3198,50 @@ body {
|
|||||||
color: var(--text-color);
|
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 {
|
#userInput {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
|
|||||||
Reference in New Issue
Block a user