diff --git a/www/app.js b/www/app.js
index 0a0fdfa..143aae4 100644
--- a/www/app.js
+++ b/www/app.js
@@ -83,18 +83,38 @@ function showConversationList() {
${conversations.length === 0
? '
暂无对话记录
'
- : conversations.map(conv => `
-
+ : sortConversations().map(conv => `
+
+ ${conv.is_pinned ? '
📌' : ''}
${escapeHtml(conv.title)}
${conv.messages.length} 条消息 · ${formatTime(conv.updatedAt)}
-
`).join('')
}
+
+
+
`;
@@ -107,21 +127,201 @@ function showConversationList() {
}
const conversationList = document.getElementById('conversationList');
+ const actionMenu = document.getElementById('actionMenu');
+ let longPressTimer = null;
+ let currentActionConvId = null;
+
if (conversationList) {
+ // 点击事件
conversationList.addEventListener('click', (e) => {
const item = e.target.closest('.conversation-item');
- const deleteBtn = e.target.closest('.conv-delete-btn');
- if (deleteBtn) {
- e.stopPropagation();
- const id = deleteBtn.getAttribute('data-id');
- deleteConversation(id);
- } else if (item) {
+ if (item) {
const id = item.getAttribute('data-id');
openConversation(id);
}
});
+
+ // 长按事件
+ conversationList.addEventListener('touchstart', (e) => {
+ const item = e.target.closest('.conversation-item');
+ if (item) {
+ longPressTimer = setTimeout(() => {
+ currentActionConvId = item.getAttribute('data-id');
+ showActionMenu(currentActionConvId);
+ }, 500); // 500ms长按
+ }
+ });
+
+ conversationList.addEventListener('touchend', () => {
+ if (longPressTimer) {
+ clearTimeout(longPressTimer);
+ longPressTimer = null;
+ }
+ });
+
+ conversationList.addEventListener('touchmove', () => {
+ if (longPressTimer) {
+ clearTimeout(longPressTimer);
+ longPressTimer = null;
+ }
+ });
+
+ // 鼠标长按(PC端)
+ conversationList.addEventListener('mousedown', (e) => {
+ const item = e.target.closest('.conversation-item');
+ if (item) {
+ longPressTimer = setTimeout(() => {
+ currentActionConvId = item.getAttribute('data-id');
+ showActionMenu(currentActionConvId);
+ }, 500);
+ }
+ });
+
+ conversationList.addEventListener('mouseup', () => {
+ if (longPressTimer) {
+ clearTimeout(longPressTimer);
+ longPressTimer = null;
+ }
+ });
+
+ conversationList.addEventListener('mouseleave', () => {
+ if (longPressTimer) {
+ clearTimeout(longPressTimer);
+ longPressTimer = null;
+ }
+ });
}
+
+ // 操作菜单事件
+ if (actionMenu) {
+ actionMenu.addEventListener('click', (e) => {
+ const item = e.target.closest('.action-menu-item');
+ if (item && currentActionConvId) {
+ const action = item.getAttribute('data-action');
+ handleActionMenuAction(action, currentActionConvId);
+ hideActionMenu();
+ }
+ });
+
+ // 点击其他地方关闭菜单
+ document.addEventListener('click', (e) => {
+ if (actionMenu.classList.contains('show') && !actionMenu.contains(e.target)) {
+ hideActionMenu();
+ }
+ });
+ }
+}
+
+// 排序对话(置顶在前)
+function sortConversations() {
+ return [...conversations].sort((a, b) => {
+ // 置顶优先
+ if (a.is_pinned && !b.is_pinned) return -1;
+ if (!a.is_pinned && b.is_pinned) return 1;
+ // 然后按更新时间
+ return b.updatedAt - a.updatedAt;
+ });
+}
+
+// 显示操作菜单
+function showActionMenu(convId) {
+ const actionMenu = document.getElementById('actionMenu');
+ const conv = conversations.find(c => c.id === convId);
+ if (!actionMenu || !conv) return;
+
+ // 更新置顶按钮文字
+ const pinText = document.getElementById('pinText');
+ if (pinText) {
+ pinText.textContent = conv.is_pinned ? '取消置顶' : '置顶';
+ }
+
+ // 显示菜单
+ actionMenu.classList.add('show');
+}
+
+// 隐藏操作菜单
+function hideActionMenu() {
+ const actionMenu = document.getElementById('actionMenu');
+ if (actionMenu) {
+ actionMenu.classList.remove('show');
+ }
+}
+
+// 处理操作菜单动作
+function handleActionMenuAction(action, convId) {
+ const conv = conversations.find(c => c.id === convId);
+ if (!conv) return;
+
+ switch (action) {
+ case 'rename':
+ renameConversation(convId);
+ break;
+ case 'share':
+ shareConversation(convId);
+ break;
+ case 'pin':
+ togglePinConversation(convId);
+ break;
+ case 'delete':
+ deleteConversation(convId);
+ break;
+ }
+}
+
+// 重命名对话
+function renameConversation(convId) {
+ const conv = conversations.find(c => c.id === convId);
+ if (!conv) return;
+
+ const newTitle = prompt('请输入新的对话标题:', conv.title);
+ if (newTitle && newTitle.trim() && newTitle !== conv.title) {
+ conv.title = newTitle.trim();
+ conv.updatedAt = Date.now();
+ saveConversations();
+ showConversationList();
+ showToast('已重命名');
+ }
+}
+
+// 分享对话
+function shareConversation(convId) {
+ const conv = conversations.find(c => c.id === convId);
+ if (!conv) return;
+
+ // 构建分享内容
+ const shareContent = `【${conv.title}】\n\n${conv.messages.map(m =>
+ `${m.role === 'user' ? '👤 用户' : '🤖 AI'}: ${m.content}`
+ ).join('\n\n')}`;
+
+ // 复制到剪贴板
+ try {
+ const textarea = document.createElement('textarea');
+ textarea.value = shareContent;
+ textarea.style.position = 'fixed';
+ textarea.style.top = '0';
+ textarea.style.left = '0';
+ textarea.style.opacity = '0';
+ document.body.appendChild(textarea);
+ textarea.select();
+ document.execCommand('copy');
+ document.body.removeChild(textarea);
+ showToast('对话已复制到剪贴板');
+ } catch (err) {
+ showToast('分享失败');
+ }
+}
+
+// 置顶/取消置顶对话
+function togglePinConversation(convId) {
+ const conv = conversations.find(c => c.id === convId);
+ if (!conv) return;
+
+ conv.is_pinned = !conv.is_pinned;
+ conv.updatedAt = Date.now();
+ saveConversations();
+ showConversationList();
+ showToast(conv.is_pinned ? '已置顶' : '已取消置顶');
}
// 创建新对话
diff --git a/www/index.html b/www/index.html
index f511bc8..a8a62d7 100644
--- a/www/index.html
+++ b/www/index.html
@@ -8,12 +8,12 @@
AI助手
-
+
-
-
+
+