collabdocchat 2.4.3 → 2.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "collabdocchat",
3
- "version": "2.4.3",
3
+ "version": "2.4.4",
4
4
  "description": "开源的实时协作文档聊天平台 - 集成任务管理、多人文档编辑、智能点名功能",
5
5
  "main": "./server/index.js",
6
6
  "type": "module",
@@ -0,0 +1,56 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const filePath = path.join(__dirname, '../src/pages/admin-dashboard.js');
9
+
10
+ console.log('添加按钮悬停效果...');
11
+ let content = fs.readFileSync(filePath, 'utf8');
12
+
13
+ // 查找设置按钮的点击事件位置
14
+ const settingsBtnPattern = /document\.getElementById\('settingsBtn'\)\.addEventListener\('click', \(\) => \{/;
15
+
16
+ if (settingsBtnPattern.test(content)) {
17
+ // 在设置按钮点击事件之前添加悬停效果
18
+ const hoverEffects = `
19
+ // 设置和帮助按钮悬停效果
20
+ const settingsBtn = document.getElementById('settingsBtn');
21
+ const helpBtn = document.getElementById('helpBtn');
22
+
23
+ settingsBtn.addEventListener('mouseenter', () => {
24
+ settingsBtn.style.transform = 'translateY(-2px)';
25
+ settingsBtn.style.boxShadow = '0 4px 12px rgba(102, 126, 234, 0.5)';
26
+ });
27
+ settingsBtn.addEventListener('mouseleave', () => {
28
+ settingsBtn.style.transform = 'translateY(0)';
29
+ settingsBtn.style.boxShadow = '0 2px 8px rgba(102, 126, 234, 0.3)';
30
+ });
31
+
32
+ helpBtn.addEventListener('mouseenter', () => {
33
+ helpBtn.style.transform = 'translateY(-2px)';
34
+ helpBtn.style.boxShadow = '0 4px 12px rgba(240, 147, 251, 0.5)';
35
+ });
36
+ helpBtn.addEventListener('mouseleave', () => {
37
+ helpBtn.style.transform = 'translateY(0)';
38
+ helpBtn.style.boxShadow = '0 2px 8px rgba(240, 147, 251, 0.3)';
39
+ });
40
+
41
+ `;
42
+
43
+ content = content.replace(
44
+ settingsBtnPattern,
45
+ hoverEffects + "document.getElementById('settingsBtn').addEventListener('click', () => {"
46
+ );
47
+
48
+ console.log('✅ 悬停效果已添加');
49
+ } else {
50
+ console.log('⚠️ 未找到设置按钮点击事件');
51
+ }
52
+
53
+ fs.writeFileSync(filePath, content, 'utf8');
54
+
55
+ console.log('\n✅ 完成!按钮现在有悬停动画效果');
56
+
@@ -0,0 +1,45 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const filePath = path.join(__dirname, '../src/pages/admin-dashboard.js');
9
+
10
+ console.log('美化设置和帮助按钮...');
11
+ let content = fs.readFileSync(filePath, 'utf8');
12
+
13
+ // 替换设置和帮助按钮
14
+ const oldButtonsSection = ` <div class="sidebar-footer">
15
+ <button class="sidebar-footer-btn" id="settingsBtn">
16
+ <span class="icon">⚙️</span> 设置
17
+ </button>
18
+ <button class="sidebar-footer-btn" id="helpBtn">
19
+ <span class="icon">❓</span> 帮助
20
+ </button>
21
+ </div>`;
22
+
23
+ const newButtonsSection = ` <div class="sidebar-footer" style="display: flex; gap: 8px; padding: 0 10px;">
24
+ <button class="sidebar-footer-btn" id="settingsBtn" style="flex: 1; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 12px; border-radius: 10px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); display: flex; align-items: center; justify-content: center; gap: 6px;">
25
+ <span class="icon" style="font-size: 18px;">⚙️</span>
26
+ <span>设置</span>
27
+ </button>
28
+ <button class="sidebar-footer-btn" id="helpBtn" style="flex: 1; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; border: none; padding: 12px; border-radius: 10px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(240, 147, 251, 0.3); display: flex; align-items: center; justify-content: center; gap: 6px;">
29
+ <span class="icon" style="font-size: 18px;">❓</span>
30
+ <span>帮助</span>
31
+ </button>
32
+ </div>`;
33
+
34
+ content = content.replace(oldButtonsSection, newButtonsSection);
35
+ console.log('✅ 设置和帮助按钮已美化');
36
+
37
+ fs.writeFileSync(filePath, content, 'utf8');
38
+
39
+ console.log('\n✅ 美化完成!');
40
+ console.log('\n变更内容:');
41
+ console.log('1. 设置按钮:紫色渐变背景 + 白色文字 + 阴影');
42
+ console.log('2. 帮助按钮:粉色渐变背景 + 白色文字 + 阴影');
43
+ console.log('3. 按钮并排显示,各占50%宽度');
44
+ console.log('4. 图标和文字居中对齐');
45
+
@@ -0,0 +1,267 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const filePath = path.join(__dirname, '../src/pages/admin-dashboard.js');
9
+
10
+ console.log('美化设置和帮助按钮,优化AI助手界面...');
11
+ let content = fs.readFileSync(filePath, 'utf8');
12
+
13
+ // 1. 查找并替换设置和帮助按钮的样式
14
+ const oldButtons = ` <button class="nav-btn" id="settingsBtn">⚙️ 设置</button>
15
+ <button class="nav-btn" id="helpBtn">❓ 帮助</button>`;
16
+
17
+ const newButtons = ` <button class="nav-btn" id="settingsBtn" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 10px 20px; border-radius: 8px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);">⚙️ 设置</button>
18
+ <button class="nav-btn" id="helpBtn" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; border: none; padding: 10px 20px; border-radius: 8px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(240, 147, 251, 0.3); margin-left: 10px;">❓ 帮助</button>`;
19
+
20
+ if (content.includes(oldButtons)) {
21
+ content = content.replace(oldButtons, newButtons);
22
+ console.log('✅ 设置和帮助按钮样式已更新');
23
+ } else {
24
+ console.log('⚠️ 未找到设置和帮助按钮,尝试其他匹配方式...');
25
+ }
26
+
27
+ // 2. 删除AI助手模态框底部的输入框和提示
28
+ const oldAIInput = ` <!-- 输入区域 -->
29
+ <div class="ai-input-container" style="padding: 20px; border-top: 1px solid var(--border); background: var(--bg-secondary);">
30
+ <div style="display: flex; gap: 12px; align-items: end;">
31
+ <textarea id="aiInputText" placeholder="输入你的问题..." rows="1" style="flex: 1; padding: 12px 16px; border: 2px solid var(--border); border-radius: 12px; resize: none; font-size: 14px; transition: border-color 0.2s; max-height: 120px;"></textarea>
32
+ <button class="btn-primary" id="aiSendBtnModal" style="padding: 12px 24px; border-radius: 12px; font-size: 15px; font-weight: 600; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none; cursor: pointer; transition: transform 0.2s;">
33
+ <span style="display: flex; align-items: center; gap: 6px;">
34
+ <span>发送</span>
35
+ <span>🚀</span>
36
+ </span>
37
+ </button>
38
+ </div>
39
+ <div style="margin-top: 10px; font-size: 11px; color: var(--text-tertiary); text-align: center;">
40
+ 💡 提示:按 Enter 发送,Shift + Enter 换行
41
+ </div>
42
+ </div>`;
43
+
44
+ const newAIInput = ` <!-- 输入区域已移除,使用快捷问题按钮 -->`;
45
+
46
+ content = content.replace(oldAIInput, newAIInput);
47
+ console.log('✅ AI助手输入框已删除');
48
+
49
+ // 3. 优化AI助手的快捷问题按钮样式
50
+ const oldQuickButtons = ` <div style="display: flex; gap: 8px; flex-wrap: wrap;">
51
+ <button class="quick-question-btn" data-question="如何创建一个新文档?" style="padding: 6px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 16px; font-size: 12px; cursor: pointer; transition: all 0.2s;">📄 如何创建文档?</button>
52
+ <button class="quick-question-btn" data-question="如何邀请成员加入群组?" style="padding: 6px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 16px; font-size: 12px; cursor: pointer; transition: all 0.2s;">👥 如何邀请成员?</button>
53
+ <button class="quick-question-btn" data-question="如何使用工作流功能?" style="padding: 6px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 16px; font-size: 12px; cursor: pointer; transition: all 0.2s;">⚙️ 工作流使用?</button>
54
+ <button class="quick-question-btn" data-question="如何备份数据?" style="padding: 6px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 16px; font-size: 12px; cursor: pointer; transition: all 0.2s;">💾 如何备份?</button>
55
+ </div>`;
56
+
57
+ const newQuickButtons = ` <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
58
+ <button class="quick-question-btn" data-question="如何创建一个新文档?" style="padding: 12px 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 12px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); display: flex; align-items: center; gap: 8px;">
59
+ <span style="font-size: 20px;">📄</span>
60
+ <span>如何创建文档?</span>
61
+ </button>
62
+ <button class="quick-question-btn" data-question="如何邀请成员加入群组?" style="padding: 12px 16px; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; border: none; border-radius: 12px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(240, 147, 251, 0.3); display: flex; align-items: center; gap: 8px;">
63
+ <span style="font-size: 20px;">👥</span>
64
+ <span>如何邀请成员?</span>
65
+ </button>
66
+ <button class="quick-question-btn" data-question="如何使用工作流功能?" style="padding: 12px 16px; background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); color: white; border: none; border-radius: 12px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(79, 172, 254, 0.3); display: flex; align-items: center; gap: 8px;">
67
+ <span style="font-size: 20px;">⚙️</span>
68
+ <span>工作流使用?</span>
69
+ </button>
70
+ <button class="quick-question-btn" data-question="如何备份数据?" style="padding: 12px 16px; background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); color: white; border: none; border-radius: 12px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(250, 112, 154, 0.3); display: flex; align-items: center; gap: 8px;">
71
+ <span style="font-size: 20px;">💾</span>
72
+ <span>如何备份?</span>
73
+ </button>
74
+ </div>`;
75
+
76
+ content = content.replace(oldQuickButtons, newQuickButtons);
77
+ console.log('✅ 快捷问题按钮已美化');
78
+
79
+ // 4. 更新快捷问题按钮的事件处理,移除输入框相关代码
80
+ const oldQuickBtnHandler = ` // 快捷问题按钮
81
+ document.querySelectorAll('.quick-question-btn').forEach(btn => {
82
+ btn.addEventListener('click', () => {
83
+ document.getElementById('aiInputText').value = btn.dataset.question;
84
+ document.getElementById('aiSendBtnModal').click();
85
+ });
86
+ btn.addEventListener('mouseenter', (e) => {
87
+ e.target.style.background = 'var(--primary)';
88
+ e.target.style.color = 'white';
89
+ e.target.style.transform = 'translateY(-2px)';
90
+ });
91
+ btn.addEventListener('mouseleave', (e) => {
92
+ e.target.style.background = 'var(--bg)';
93
+ e.target.style.color = 'inherit';
94
+ e.target.style.transform = 'translateY(0)';
95
+ });
96
+ });`;
97
+
98
+ const newQuickBtnHandler = ` // 快捷问题按钮 - 直接发送问题
99
+ document.querySelectorAll('.quick-question-btn').forEach(btn => {
100
+ btn.addEventListener('click', async () => {
101
+ const question = btn.dataset.question;
102
+ const chatMessages = document.getElementById('aiChatMessages');
103
+
104
+ // 显示用户问题
105
+ const userMsg = document.createElement('div');
106
+ userMsg.className = 'ai-message user';
107
+ userMsg.textContent = question;
108
+ userMsg.style.cssText = 'background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 18px; border-radius: 18px 18px 4px 18px; margin: 10px 0; max-width: 75%; margin-left: auto; text-align: right; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); animation: slideInRight 0.3s ease;';
109
+ chatMessages.appendChild(userMsg);
110
+ chatMessages.scrollTop = chatMessages.scrollHeight;
111
+
112
+ // 发送到AI
113
+ try {
114
+ const token = localStorage.getItem('token');
115
+ const response = await fetch('http://localhost:8765/api/ai/ask', {
116
+ method: 'POST',
117
+ headers: {
118
+ 'Content-Type': 'application/json',
119
+ 'Authorization': \`Bearer \${token}\`
120
+ },
121
+ body: JSON.stringify({ question })
122
+ });
123
+
124
+ const data = await response.json();
125
+
126
+ // 显示AI回复
127
+ const aiMsg = document.createElement('div');
128
+ aiMsg.className = 'ai-message ai';
129
+ aiMsg.textContent = data.answer || '抱歉,我现在无法回答这个问题。';
130
+ aiMsg.style.cssText = 'background: var(--bg-secondary); padding: 15px 18px; border-radius: 18px 18px 18px 4px; margin: 10px 0; max-width: 75%; border: 1px solid var(--border); box-shadow: 0 2px 4px rgba(0,0,0,0.05); animation: slideInLeft 0.3s ease; line-height: 1.6;';
131
+ chatMessages.appendChild(aiMsg);
132
+ chatMessages.scrollTop = chatMessages.scrollHeight;
133
+ } catch (error) {
134
+ const errorMsg = document.createElement('div');
135
+ errorMsg.className = 'ai-message ai';
136
+ errorMsg.textContent = '抱歉,发生了错误:' + error.message;
137
+ errorMsg.style.cssText = 'background: var(--bg-secondary); padding: 15px 18px; border-radius: 18px 18px 18px 4px; margin: 10px 0; max-width: 75%; border: 1px solid var(--border); box-shadow: 0 2px 4px rgba(0,0,0,0.05); animation: slideInLeft 0.3s ease; line-height: 1.6;';
138
+ chatMessages.appendChild(errorMsg);
139
+ chatMessages.scrollTop = chatMessages.scrollHeight;
140
+ }
141
+ });
142
+
143
+ // 悬停效果
144
+ btn.addEventListener('mouseenter', (e) => {
145
+ e.target.style.transform = 'translateY(-3px) scale(1.02)';
146
+ e.target.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)';
147
+ });
148
+ btn.addEventListener('mouseleave', (e) => {
149
+ e.target.style.transform = 'translateY(0) scale(1)';
150
+ const gradient = e.target.style.background;
151
+ if (gradient.includes('667eea')) {
152
+ e.target.style.boxShadow = '0 2px 8px rgba(102, 126, 234, 0.3)';
153
+ } else if (gradient.includes('f093fb')) {
154
+ e.target.style.boxShadow = '0 2px 8px rgba(240, 147, 251, 0.3)';
155
+ } else if (gradient.includes('4facfe')) {
156
+ e.target.style.boxShadow = '0 2px 8px rgba(79, 172, 254, 0.3)';
157
+ } else {
158
+ e.target.style.boxShadow = '0 2px 8px rgba(250, 112, 154, 0.3)';
159
+ }
160
+ });
161
+ });`;
162
+
163
+ content = content.replace(oldQuickBtnHandler, newQuickBtnHandler);
164
+ console.log('✅ 快捷问题按钮事件已更新');
165
+
166
+ // 5. 删除不再需要的输入框相关代码
167
+ const inputRelatedCode = [
168
+ ` // AI输入框自动调整高度
169
+ const aiInput = document.getElementById('aiInputText');
170
+ aiInput.addEventListener('input', () => {
171
+ aiInput.style.height = 'auto';
172
+ aiInput.style.height = aiInput.scrollHeight + 'px';
173
+ });
174
+
175
+ // 支持 Enter 发送,Shift+Enter 换行
176
+ aiInput.addEventListener('keydown', (e) => {
177
+ if (e.key === 'Enter' && !e.shiftKey) {
178
+ e.preventDefault();
179
+ document.getElementById('aiSendBtnModal').click();
180
+ }
181
+ });
182
+
183
+ // 输入框聚焦效果
184
+ aiInput.addEventListener('focus', () => {
185
+ aiInput.style.borderColor = 'var(--primary)';
186
+ });
187
+ aiInput.addEventListener('blur', () => {
188
+ aiInput.style.borderColor = 'var(--border)';
189
+ });
190
+
191
+ // 发送按钮悬停效果
192
+ const aiSendBtn = document.getElementById('aiSendBtnModal');
193
+ aiSendBtn.addEventListener('mouseenter', () => {
194
+ aiSendBtn.style.transform = 'scale(1.05)';
195
+ });
196
+ aiSendBtn.addEventListener('mouseleave', () => {
197
+ aiSendBtn.style.transform = 'scale(1)';
198
+ });
199
+
200
+ document.getElementById('aiSendBtnModal').addEventListener('click', async () => {`,
201
+ ` document.getElementById('aiSendBtnModal').addEventListener('click', async () => {
202
+ const question = document.getElementById('aiInputText').value.trim();
203
+ if (!question) return;
204
+
205
+ const chatMessages = document.getElementById('aiChatMessages');
206
+
207
+ // 显示用户消息
208
+ const userMsg = document.createElement('div');
209
+ userMsg.className = 'ai-message user';
210
+ userMsg.textContent = question;
211
+ userMsg.style.cssText = 'background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 18px; border-radius: 18px 18px 4px 18px; margin: 10px 0; max-width: 75%; margin-left: auto; text-align: right; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); animation: slideInRight 0.3s ease;';
212
+ chatMessages.appendChild(userMsg);
213
+
214
+ // 清空输入框
215
+ document.getElementById('aiInputText').value = '';
216
+ document.getElementById('aiInputText').style.height = 'auto';
217
+
218
+ chatMessages.scrollTop = chatMessages.scrollHeight;
219
+
220
+ try {
221
+ const token = localStorage.getItem('token');
222
+ const response = await fetch('http://localhost:8765/api/ai/ask', {
223
+ method: 'POST',
224
+ headers: {
225
+ 'Content-Type': 'application/json',
226
+ 'Authorization': \`Bearer \${token}\`
227
+ },
228
+ body: JSON.stringify({ question })
229
+ });
230
+
231
+ const data = await response.json();
232
+
233
+ // 显示AI回复
234
+ const aiMsg = document.createElement('div');
235
+ aiMsg.className = 'ai-message ai';
236
+ aiMsg.textContent = data.answer || '抱歉,我现在无法回答这个问题。';
237
+ aiMsg.style.cssText = 'background: var(--bg-secondary); padding: 15px 18px; border-radius: 18px 18px 18px 4px; margin: 10px 0; max-width: 75%; border: 1px solid var(--border); box-shadow: 0 2px 4px rgba(0,0,0,0.05); animation: slideInLeft 0.3s ease; line-height: 1.6;';
238
+ chatMessages.appendChild(aiMsg);
239
+ chatMessages.scrollTop = chatMessages.scrollHeight;
240
+ } catch (error) {
241
+ const errorMsg = document.createElement('div');
242
+ errorMsg.className = 'ai-message ai';
243
+ errorMsg.textContent = '抱歉,发生了错误:' + error.message;
244
+ errorMsg.style.cssText = 'background: var(--bg-secondary); padding: 15px 18px; border-radius: 18px 18px 18px 4px; margin: 10px 0; max-width: 75%; border: 1px solid var(--border); box-shadow: 0 2px 4px rgba(0,0,0,0.05); animation: slideInLeft 0.3s ease; line-height: 1.6;';
245
+ chatMessages.appendChild(errorMsg);
246
+ chatMessages.scrollTop = chatMessages.scrollHeight;
247
+ }
248
+ });`
249
+ ];
250
+
251
+ inputRelatedCode.forEach(code => {
252
+ if (content.includes(code)) {
253
+ content = content.replace(code, '');
254
+ console.log('✅ 已删除输入框相关代码');
255
+ }
256
+ });
257
+
258
+ fs.writeFileSync(filePath, content, 'utf8');
259
+
260
+ console.log('\n✅ 所有美化完成!');
261
+ console.log('\n变更内容:');
262
+ console.log('1. 设置按钮:紫色渐变 + 阴影');
263
+ console.log('2. 帮助按钮:粉色渐变 + 阴影');
264
+ console.log('3. AI助手:删除底部输入框');
265
+ console.log('4. 快捷问题:2x2网格布局,渐变色按钮');
266
+ console.log('5. 点击快捷问题直接发送到AI');
267
+
@@ -0,0 +1,20 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const filePath = path.join(__dirname, '../src/pages/admin-dashboard.js');
9
+ const content = fs.readFileSync(filePath, 'utf8');
10
+ const lines = content.split('\n');
11
+
12
+ console.log('搜索设置和帮助按钮...\n');
13
+
14
+ lines.forEach((line, i) => {
15
+ if ((line.includes('设置') || line.includes('帮助')) &&
16
+ (line.includes('btn') || line.includes('button') || line.includes('nav-btn'))) {
17
+ console.log(`第 ${i + 1} 行: ${line.trim()}`);
18
+ }
19
+ });
20
+
@@ -0,0 +1,21 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const filePath = path.join(__dirname, '../src/pages/admin-dashboard.js');
9
+ const content = fs.readFileSync(filePath, 'utf8');
10
+ const lines = content.split('\n');
11
+
12
+ console.log('搜索侧边栏底部按钮...\n');
13
+
14
+ lines.forEach((line, i) => {
15
+ if (line.includes('退出登录') ||
16
+ (line.includes('⚙️') && line.includes('设置')) ||
17
+ (line.includes('❓') && line.includes('帮助'))) {
18
+ console.log(`第 ${i + 1} 行: ${line.trim()}`);
19
+ }
20
+ });
21
+
@@ -66,12 +66,14 @@ export function renderAdminDashboard(user, wsService) {
66
66
  </button>
67
67
  </nav>
68
68
 
69
- <div class="sidebar-footer">
70
- <button class="sidebar-footer-btn" id="settingsBtn">
71
- <span class="icon">⚙️</span> 设置
69
+ <div class="sidebar-footer" style="display: flex; gap: 8px; padding: 0 10px;">
70
+ <button class="sidebar-footer-btn" id="settingsBtn" style="flex: 1; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 12px; border-radius: 10px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); display: flex; align-items: center; justify-content: center; gap: 6px;">
71
+ <span class="icon" style="font-size: 18px;">⚙️</span>
72
+ <span>设置</span>
72
73
  </button>
73
- <button class="sidebar-footer-btn" id="helpBtn">
74
- <span class="icon">❓</span> 帮助
74
+ <button class="sidebar-footer-btn" id="helpBtn" style="flex: 1; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; border: none; padding: 12px; border-radius: 10px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(240, 147, 251, 0.3); display: flex; align-items: center; justify-content: center; gap: 6px;">
75
+ <span class="icon" style="font-size: 18px;">❓</span>
76
+ <span>帮助</span>
75
77
  </button>
76
78
  </div>
77
79
  <button class="btn-logout" id="logoutBtn">退出登录</button>
@@ -99,6 +101,29 @@ export function renderAdminDashboard(user, wsService) {
99
101
  });
100
102
 
101
103
  // 设置按钮
104
+
105
+ // 设置和帮助按钮悬停效果
106
+ const settingsBtn = document.getElementById('settingsBtn');
107
+ const helpBtn = document.getElementById('helpBtn');
108
+
109
+ settingsBtn.addEventListener('mouseenter', () => {
110
+ settingsBtn.style.transform = 'translateY(-2px)';
111
+ settingsBtn.style.boxShadow = '0 4px 12px rgba(102, 126, 234, 0.5)';
112
+ });
113
+ settingsBtn.addEventListener('mouseleave', () => {
114
+ settingsBtn.style.transform = 'translateY(0)';
115
+ settingsBtn.style.boxShadow = '0 2px 8px rgba(102, 126, 234, 0.3)';
116
+ });
117
+
118
+ helpBtn.addEventListener('mouseenter', () => {
119
+ helpBtn.style.transform = 'translateY(-2px)';
120
+ helpBtn.style.boxShadow = '0 4px 12px rgba(240, 147, 251, 0.5)';
121
+ });
122
+ helpBtn.addEventListener('mouseleave', () => {
123
+ helpBtn.style.transform = 'translateY(0)';
124
+ helpBtn.style.boxShadow = '0 2px 8px rgba(240, 147, 251, 0.3)';
125
+ });
126
+
102
127
  document.getElementById('settingsBtn').addEventListener('click', () => {
103
128
  renderView('settings');
104
129
  });
@@ -983,11 +1008,23 @@ export function renderAdminDashboard(user, wsService) {
983
1008
  <!-- 快捷问题 -->
984
1009
  <div style="padding: 15px; background: var(--bg-secondary); border-bottom: 1px solid var(--border);">
985
1010
  <div style="font-size: 12px; color: var(--text-tertiary); margin-bottom: 10px;">💡 快捷问题</div>
986
- <div style="display: flex; gap: 8px; flex-wrap: wrap;">
987
- <button class="quick-question-btn" data-question="如何创建一个新文档?" style="padding: 6px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 16px; font-size: 12px; cursor: pointer; transition: all 0.2s;">📄 如何创建文档?</button>
988
- <button class="quick-question-btn" data-question="如何邀请成员加入群组?" style="padding: 6px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 16px; font-size: 12px; cursor: pointer; transition: all 0.2s;">👥 如何邀请成员?</button>
989
- <button class="quick-question-btn" data-question="如何使用工作流功能?" style="padding: 6px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 16px; font-size: 12px; cursor: pointer; transition: all 0.2s;">⚙️ 工作流使用?</button>
990
- <button class="quick-question-btn" data-question="如何备份数据?" style="padding: 6px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 16px; font-size: 12px; cursor: pointer; transition: all 0.2s;">💾 如何备份?</button>
1011
+ <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
1012
+ <button class="quick-question-btn" data-question="如何创建一个新文档?" style="padding: 12px 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 12px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); display: flex; align-items: center; gap: 8px;">
1013
+ <span style="font-size: 20px;">📄</span>
1014
+ <span>如何创建文档?</span>
1015
+ </button>
1016
+ <button class="quick-question-btn" data-question="如何邀请成员加入群组?" style="padding: 12px 16px; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; border: none; border-radius: 12px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(240, 147, 251, 0.3); display: flex; align-items: center; gap: 8px;">
1017
+ <span style="font-size: 20px;">👥</span>
1018
+ <span>如何邀请成员?</span>
1019
+ </button>
1020
+ <button class="quick-question-btn" data-question="如何使用工作流功能?" style="padding: 12px 16px; background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); color: white; border: none; border-radius: 12px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(79, 172, 254, 0.3); display: flex; align-items: center; gap: 8px;">
1021
+ <span style="font-size: 20px;">⚙️</span>
1022
+ <span>工作流使用?</span>
1023
+ </button>
1024
+ <button class="quick-question-btn" data-question="如何备份数据?" style="padding: 12px 16px; background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); color: white; border: none; border-radius: 12px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 2px 8px rgba(250, 112, 154, 0.3); display: flex; align-items: center; gap: 8px;">
1025
+ <span style="font-size: 20px;">💾</span>
1026
+ <span>如何备份?</span>
1027
+ </button>
991
1028
  </div>
992
1029
  </div>
993
1030
 
@@ -1009,21 +1046,7 @@ export function renderAdminDashboard(user, wsService) {
1009
1046
  </div>
1010
1047
  </div>
1011
1048
 
1012
- <!-- 输入区域 -->
1013
- <div class="ai-input-container" style="padding: 20px; border-top: 1px solid var(--border); background: var(--bg-secondary);">
1014
- <div style="display: flex; gap: 12px; align-items: end;">
1015
- <textarea id="aiInputText" placeholder="输入你的问题..." rows="1" style="flex: 1; padding: 12px 16px; border: 2px solid var(--border); border-radius: 12px; resize: none; font-size: 14px; transition: border-color 0.2s; max-height: 120px;"></textarea>
1016
- <button class="btn-primary" id="aiSendBtnModal" style="padding: 12px 24px; border-radius: 12px; font-size: 15px; font-weight: 600; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none; cursor: pointer; transition: transform 0.2s;">
1017
- <span style="display: flex; align-items: center; gap: 6px;">
1018
- <span>发送</span>
1019
- <span>🚀</span>
1020
- </span>
1021
- </button>
1022
- </div>
1023
- <div style="margin-top: 10px; font-size: 11px; color: var(--text-tertiary); text-align: center;">
1024
- 💡 提示:按 Enter 发送,Shift + Enter 换行
1025
- </div>
1026
- </div>
1049
+ <!-- 输入区域已移除,使用快捷问题按钮 -->
1027
1050
  </div>
1028
1051
  </div>
1029
1052
  </div>
@@ -1248,57 +1271,72 @@ export function renderAdminDashboard(user, wsService) {
1248
1271
  document.getElementById('aiModal').classList.add('hidden');
1249
1272
  });
1250
1273
 
1251
- // 快捷问题按钮
1274
+ // 快捷问题按钮 - 直接发送问题
1252
1275
  document.querySelectorAll('.quick-question-btn').forEach(btn => {
1253
- btn.addEventListener('click', () => {
1254
- document.getElementById('aiInputText').value = btn.dataset.question;
1255
- document.getElementById('aiSendBtnModal').click();
1276
+ btn.addEventListener('click', async () => {
1277
+ const question = btn.dataset.question;
1278
+ const chatMessages = document.getElementById('aiChatMessages');
1279
+
1280
+ // 显示用户问题
1281
+ const userMsg = document.createElement('div');
1282
+ userMsg.className = 'ai-message user';
1283
+ userMsg.textContent = question;
1284
+ userMsg.style.cssText = 'background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 18px; border-radius: 18px 18px 4px 18px; margin: 10px 0; max-width: 75%; margin-left: auto; text-align: right; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); animation: slideInRight 0.3s ease;';
1285
+ chatMessages.appendChild(userMsg);
1286
+ chatMessages.scrollTop = chatMessages.scrollHeight;
1287
+
1288
+ // 发送到AI
1289
+ try {
1290
+ const token = localStorage.getItem('token');
1291
+ const response = await fetch('http://localhost:8765/api/ai/ask', {
1292
+ method: 'POST',
1293
+ headers: {
1294
+ 'Content-Type': 'application/json',
1295
+ 'Authorization': `Bearer ${token}`
1296
+ },
1297
+ body: JSON.stringify({ question })
1298
+ });
1299
+
1300
+ const data = await response.json();
1301
+
1302
+ // 显示AI回复
1303
+ const aiMsg = document.createElement('div');
1304
+ aiMsg.className = 'ai-message ai';
1305
+ aiMsg.textContent = data.answer || '抱歉,我现在无法回答这个问题。';
1306
+ aiMsg.style.cssText = 'background: var(--bg-secondary); padding: 15px 18px; border-radius: 18px 18px 18px 4px; margin: 10px 0; max-width: 75%; border: 1px solid var(--border); box-shadow: 0 2px 4px rgba(0,0,0,0.05); animation: slideInLeft 0.3s ease; line-height: 1.6;';
1307
+ chatMessages.appendChild(aiMsg);
1308
+ chatMessages.scrollTop = chatMessages.scrollHeight;
1309
+ } catch (error) {
1310
+ const errorMsg = document.createElement('div');
1311
+ errorMsg.className = 'ai-message ai';
1312
+ errorMsg.textContent = '抱歉,发生了错误:' + error.message;
1313
+ errorMsg.style.cssText = 'background: var(--bg-secondary); padding: 15px 18px; border-radius: 18px 18px 18px 4px; margin: 10px 0; max-width: 75%; border: 1px solid var(--border); box-shadow: 0 2px 4px rgba(0,0,0,0.05); animation: slideInLeft 0.3s ease; line-height: 1.6;';
1314
+ chatMessages.appendChild(errorMsg);
1315
+ chatMessages.scrollTop = chatMessages.scrollHeight;
1316
+ }
1256
1317
  });
1318
+
1319
+ // 悬停效果
1257
1320
  btn.addEventListener('mouseenter', (e) => {
1258
- e.target.style.background = 'var(--primary)';
1259
- e.target.style.color = 'white';
1260
- e.target.style.transform = 'translateY(-2px)';
1321
+ e.target.style.transform = 'translateY(-3px) scale(1.02)';
1322
+ e.target.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)';
1261
1323
  });
1262
1324
  btn.addEventListener('mouseleave', (e) => {
1263
- e.target.style.background = 'var(--bg)';
1264
- e.target.style.color = 'inherit';
1265
- e.target.style.transform = 'translateY(0)';
1325
+ e.target.style.transform = 'translateY(0) scale(1)';
1326
+ const gradient = e.target.style.background;
1327
+ if (gradient.includes('667eea')) {
1328
+ e.target.style.boxShadow = '0 2px 8px rgba(102, 126, 234, 0.3)';
1329
+ } else if (gradient.includes('f093fb')) {
1330
+ e.target.style.boxShadow = '0 2px 8px rgba(240, 147, 251, 0.3)';
1331
+ } else if (gradient.includes('4facfe')) {
1332
+ e.target.style.boxShadow = '0 2px 8px rgba(79, 172, 254, 0.3)';
1333
+ } else {
1334
+ e.target.style.boxShadow = '0 2px 8px rgba(250, 112, 154, 0.3)';
1335
+ }
1266
1336
  });
1267
1337
  });
1268
1338
 
1269
- // AI输入框自动调整高度
1270
- const aiInput = document.getElementById('aiInputText');
1271
- aiInput.addEventListener('input', () => {
1272
- aiInput.style.height = 'auto';
1273
- aiInput.style.height = aiInput.scrollHeight + 'px';
1274
- });
1275
-
1276
- // 支持 Enter 发送,Shift+Enter 换行
1277
- aiInput.addEventListener('keydown', (e) => {
1278
- if (e.key === 'Enter' && !e.shiftKey) {
1279
- e.preventDefault();
1280
- document.getElementById('aiSendBtnModal').click();
1281
- }
1282
- });
1283
-
1284
- // 输入框聚焦效果
1285
- aiInput.addEventListener('focus', () => {
1286
- aiInput.style.borderColor = 'var(--primary)';
1287
- });
1288
- aiInput.addEventListener('blur', () => {
1289
- aiInput.style.borderColor = 'var(--border)';
1290
- });
1291
-
1292
- // 发送按钮悬停效果
1293
- const aiSendBtn = document.getElementById('aiSendBtnModal');
1294
- aiSendBtn.addEventListener('mouseenter', () => {
1295
- aiSendBtn.style.transform = 'scale(1.05)';
1296
- });
1297
- aiSendBtn.addEventListener('mouseleave', () => {
1298
- aiSendBtn.style.transform = 'scale(1)';
1299
- });
1300
1339
 
1301
- document.getElementById('aiSendBtnModal').addEventListener('click', async () => {
1302
1340
  const input = document.getElementById('aiInputText');
1303
1341
  const question = input.value.trim();
1304
1342
  if (!question) return;