Skip to content

Commit 5a66ff8

Browse files
committed
feat(AgentPopover): 增强待办事项图标样式并添加文件下载功能
- 将简单的文本图标替换为SVG图标,提升视觉体验 - 为文件列表项添加下载按钮功能 - 优化文件区域的样式和布局 - 移除已完成TODO注释和相关代码
1 parent 10ad84a commit 5a66ff8

File tree

5 files changed

+110
-13
lines changed

5 files changed

+110
-13
lines changed

docs/latest/changelog/roadmap.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
- 集成 LangFuse (观望) 添加用户日志与用户反馈模块,可以在 AgentView 中查看信息
1313
- 集成 neo4j mcp (或者自己构建工具)
1414
- 文档解析部分的 markdown 中的图片替换为内部可访问的链接 (2/4)
15+
- 同名文件处理逻辑:遇到同名文件则在上传区域提示,是否删除旧文件
16+
- conversation 待修改为异步的版本
17+
- DBManager 需要将数据库修改为异步的aiosqlite或者异步mysql,缓存使用Redis存储
18+
- agent 状态中的文件区域,新增可以下载
1519

1620
### Bugs
1721
- 部分异常状态下,智能体的模型名称出现重叠[#279](https://github.com/xerrors/Yuxi-Know/issues/279)

server/routers/knowledge_router.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,8 +1065,6 @@ async def upload_file(
10651065
upload_dir = os.path.join(config.save_dir, "database", "uploads")
10661066

10671067
basename, ext = os.path.splitext(file.filename)
1068-
# TODO:
1069-
# 后续修改为遇到同名文件则在上传区域提示,是否删除旧文件,同时 filename name 也就不用添加 hash 了
10701068
filename = f"{basename}_{hashstr(basename, 4, with_salt=True, salt='fixed_salt')}{ext}".lower()
10711069

10721070
file_path = os.path.join(upload_dir, filename)

server/utils/lifespan.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
from server.services import tasker
66

7-
# TODO:[已完成]使用lifespan进行统一生命周期管理
8-
97

108
@asynccontextmanager
119
async def lifespan(app: FastAPI):

web/src/components/AgentPopover.vue

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,15 @@
4040
class="todo-item"
4141
>
4242
<span class="todo-status" :class="todo.status">
43-
{{ todo.status === 'completed' ? '✓' : todo.status === 'in_progress' ? '⟳' : '○' }}
43+
<svg v-if="todo.status === 'completed'" viewBox="0 0 24 24" fill="currentColor" class="icon">
44+
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
45+
</svg>
46+
<svg v-else-if="todo.status === 'in_progress'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="icon spinning">
47+
<circle cx="12" cy="12" r="10" stroke-dasharray="31.416" stroke-dashoffset="31.416" stroke-linecap="round"/>
48+
</svg>
49+
<svg v-else viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="icon">
50+
<circle cx="12" cy="12" r="10"/>
51+
</svg>
4452
</span>
4553
<span class="todo-text">{{ todo.content }}</span>
4654
</div>
@@ -60,10 +68,19 @@
6068
@click="showFileContent(fileItem.path, fileItem)"
6169
>
6270
<div class="file-info">
63-
<div class="file-name">{{ getFileName(fileItem) }}</div>
64-
<div class="file-time" v-if="fileItem.modified_at">
65-
{{ formatDate(fileItem.modified_at) }}
71+
<div class="file-content-wrapper">
72+
<div class="file-name">{{ getFileName(fileItem) }}</div>
73+
<div class="file-time" v-if="fileItem.modified_at">
74+
{{ formatDate(fileItem.modified_at) }}
75+
</div>
6676
</div>
77+
<button
78+
class="download-btn"
79+
@click.stop="downloadFile(fileItem)"
80+
title="下载文件"
81+
>
82+
<Download :size="18" />
83+
</button>
6784
</div>
6885
</div>
6986
</div>
@@ -92,6 +109,7 @@
92109

93110
<script setup>
94111
import { computed, ref, watch } from 'vue';
112+
import { Download } from 'lucide-vue-next';
95113
96114
const props = defineProps({
97115
visible: {
@@ -211,6 +229,24 @@ const closeModal = () => {
211229
currentFilePath.value = '';
212230
};
213231
232+
const downloadFile = (fileItem) => {
233+
try {
234+
const content = formatContent(fileItem.content);
235+
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
236+
const url = URL.createObjectURL(blob);
237+
const link = document.createElement('a');
238+
239+
link.href = url;
240+
link.download = getFileName(fileItem);
241+
document.body.appendChild(link);
242+
link.click();
243+
document.body.removeChild(link);
244+
URL.revokeObjectURL(url);
245+
} catch (error) {
246+
console.error('下载文件失败:', error);
247+
}
248+
};
249+
214250
const emitRefresh = () => {
215251
emit('refresh');
216252
};
@@ -330,19 +366,35 @@ const emitRefresh = () => {
330366
331367
.todo-status {
332368
flex-shrink: 0;
333-
width: 16px;
334-
height: 16px;
369+
width: 20px;
370+
height: 20px;
335371
display: flex;
336372
align-items: center;
337373
justify-content: center;
338-
font-size: 10px;
339374
margin-top: 2px;
340375
border-radius: 50%;
341376
transition: all 0.15s ease;
342377
378+
.icon {
379+
width: 14px;
380+
height: 14px;
381+
transition: all 0.15s ease;
382+
}
383+
384+
.spinning {
385+
animation: spin 1.5s linear infinite;
386+
stroke-dasharray: 31.416;
387+
stroke-dashoffset: 0;
388+
animation: spin 1.5s linear infinite;
389+
}
390+
343391
&.completed {
344392
background: var(--color-success-50);
345393
color: var(--color-success-700);
394+
395+
.icon {
396+
transform: scale(1.1);
397+
}
346398
}
347399
348400
&.in_progress {
@@ -356,6 +408,20 @@ const emitRefresh = () => {
356408
}
357409
}
358410
411+
@keyframes spin {
412+
0% {
413+
transform: rotate(0deg);
414+
stroke-dashoffset: 0;
415+
}
416+
50% {
417+
stroke-dashoffset: 15.708;
418+
}
419+
100% {
420+
transform: rotate(360deg);
421+
stroke-dashoffset: 0;
422+
}
423+
}
424+
359425
.todo-text {
360426
flex: 1;
361427
font-size: 14px;
@@ -377,7 +443,7 @@ const emitRefresh = () => {
377443
378444
.file-item {
379445
padding: 12px 14px;
380-
background: var(--main-5);
446+
background: var(--gray-0);
381447
border: 1px solid var(--gray-150);
382448
border-radius: 6px;
383449
cursor: pointer;
@@ -395,6 +461,15 @@ const emitRefresh = () => {
395461
justify-content: space-between;
396462
align-items: center;
397463
gap: 10px;
464+
width: 100%;
465+
}
466+
467+
.file-content-wrapper {
468+
flex: 1;
469+
display: flex;
470+
flex-direction: column;
471+
gap: 4px;
472+
min-width: 0;
398473
}
399474
400475
.file-name {
@@ -415,6 +490,29 @@ const emitRefresh = () => {
415490
white-space: nowrap;
416491
}
417492
493+
.download-btn {
494+
display: flex;
495+
align-items: center;
496+
justify-content: center;
497+
width: 28px;
498+
height: 28px;
499+
background: transparent;
500+
border: none;
501+
color: var(--gray-600);
502+
cursor: pointer;
503+
transition: all 0.15s ease;
504+
padding: 0;
505+
flex-shrink: 0;
506+
507+
&:hover {
508+
color: var(--main-600);
509+
}
510+
511+
&:active {
512+
color: var(--main-400);
513+
}
514+
}
515+
418516
.file-content {
419517
max-height: 60vh;
420518
overflow-y: auto;

web/src/views/SettingView.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@
162162
<ModelProvidersComponent />
163163
</div>
164164

165-
<!-- TODO 用户管理优化,添加姓名(默认使用用户名配置项) -->
166165
<div class="setting" v-if="(state.windowWidth <= 520 || state.section === 'user') && userStore.isAdmin">
167166
<UserManagementComponent />
168167
</div>

0 commit comments

Comments
 (0)