feat: 重构项目心跳数据结构并实现项目列表API

- 新增统一项目列表Redis键和迁移工具
- 实现GET /api/projects端点获取项目列表
- 实现POST /api/projects/migrate端点支持数据迁移
- 更新前端ProjectSelector组件使用真实项目数据
- 扩展projectStore状态管理
- 更新相关文档和OpenSpec规范
- 添加测试用例验证新功能
This commit is contained in:
2026-01-13 19:45:05 +08:00
parent 19e65d78dc
commit 282f7268ed
66 changed files with 4378 additions and 456 deletions

View File

@@ -147,7 +147,9 @@
class="metadata-item"
>
<span class="metadata-key">{{ key }}:</span>
<span class="metadata-value">{{ JSON.stringify(value) }}</span>
<span class="metadata-value">
{{ JSON.stringify(value) }}
</span>
</div>
</div>
</div>
@@ -156,7 +158,7 @@
<!-- 展开/折叠按钮 -->
<div
class="expand-btn"
:class="{ 'expanded': expandedItems.includes(item.id) }"
:class="{ expanded: expandedItems.includes(item.id) }"
@click="toggleExpand(item.id)"
>
<span class="expand-icon">
@@ -252,8 +254,11 @@ const initDebugData = () => {
// 生成50条模拟数据
for (let i = 0; i < 50; i++) {
const type = types[Math.floor(Math.random() * types.length)];
const projectId = projects.value[Math.floor(Math.random() * projects.value.length)].id;
const timestamp = new Date(Date.now() - Math.random() * 3600000 * 24).toISOString();
const projectId =
projects.value[Math.floor(Math.random() * projects.value.length)].id;
const timestamp = new Date(
Date.now() - Math.random() * 3600000 * 24,
).toISOString();
// 根据类型生成不同的内容
let content = '';
@@ -318,8 +323,8 @@ const initDebugData = () => {
}
// 按时间降序排序
debugInfo.value = newDebugInfo.sort((a, b) =>
new Date(b.timestamp) - new Date(a.timestamp),
debugInfo.value = newDebugInfo.sort(
(a, b) => new Date(b.timestamp) - new Date(a.timestamp),
);
};
@@ -329,23 +334,25 @@ const filteredDebugInfo = computed(() => {
// 按项目筛选
if (selectedProjectId.value !== 'all') {
result = result.filter(item => item.projectId === selectedProjectId.value);
result = result.filter(
(item) => item.projectId === selectedProjectId.value,
);
}
// 按调试类型筛选
if (selectedDebugType.value !== 'all') {
result = result.filter(item => item.type === selectedDebugType.value);
result = result.filter((item) => item.type === selectedDebugType.value);
}
// 按时间范围筛选
if (startTime.value) {
const startDate = new Date(startTime.value);
result = result.filter(item => new Date(item.timestamp) >= startDate);
result = result.filter((item) => new Date(item.timestamp) >= startDate);
}
if (endTime.value) {
const endDate = new Date(endTime.value);
result = result.filter(item => new Date(item.timestamp) <= endDate);
result = result.filter((item) => new Date(item.timestamp) <= endDate);
}
// 分页处理
@@ -361,21 +368,23 @@ const totalPages = computed(() => {
// 应用相同的过滤条件来计算总条数
if (selectedProjectId.value !== 'all') {
result = result.filter(item => item.projectId === selectedProjectId.value);
result = result.filter(
(item) => item.projectId === selectedProjectId.value,
);
}
if (selectedDebugType.value !== 'all') {
result = result.filter(item => item.type === selectedDebugType.value);
result = result.filter((item) => item.type === selectedDebugType.value);
}
if (startTime.value) {
const startDate = new Date(startTime.value);
result = result.filter(item => new Date(item.timestamp) >= startDate);
result = result.filter((item) => new Date(item.timestamp) >= startDate);
}
if (endTime.value) {
const endDate = new Date(endTime.value);
result = result.filter(item => new Date(item.timestamp) <= endDate);
result = result.filter((item) => new Date(item.timestamp) <= endDate);
}
return Math.ceil(result.length / pageSize.value);
@@ -420,7 +429,9 @@ const exportDebugInfo = () => {
};
// 创建下载链接
const blob = new Blob([ JSON.stringify(dataToExport, null, 2) ], { type: 'application/json' });
const blob = new Blob([ JSON.stringify(dataToExport, null, 2) ], {
type: 'application/json',
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
@@ -443,7 +454,7 @@ const toggleExpand = (id) => {
// 获取项目名称
const getProjectName = (projectId) => {
const project = projects.value.find(p => p.id === projectId);
const project = projects.value.find((p) => p.id === projectId);
return project ? project.name : '未知项目';
};
@@ -937,4 +948,4 @@ onMounted(() => {
font-size: 0.8rem;
}
}
</style>
</style>