优化: 所有剩余UI/UX待办一批完成(持久化+AuditLog+解耦+total+原12大改+P2)

持久化(P1-c):新建 usePersistedRef composable,Tasks/AuditLog/ProjectDetail 等接入 localStorage

AuditLog(P1-d):后端 list_tool_executions 加 WHERE 筛选+返 {items,total,has_more},前端对接+长列折叠+筛选持久化

数据源解耦(P1-g):ProjectDetail projectTasks 按 project_id 独立加载 + ChatInput @项目联想独立加载(不读 store.tasks 当前页)

GitChanges(12a):后端 get_module_commits 加 git rev-list --count 返 total,前端显真实总数

原12大改:Dashboard 统计卡压底行(1)/Projects 列表卡片视图(2)/project_event 埋点排序(3)/TaskDetail 重设计(4)/IdeaDetail 重设计(5)/KnowledgeDetail 重设计(6)/界面持久化+侧栏Ctrl+B+审批数字键(7)/ProjectDetail 三栏改两栏(10)

P2打磨:文件浏览器(FileTree去重/FilePreview行号.md Diff/selectedFilePath归位)/settings反馈(假保存/端口校验)/AI会话(try-catch/scrollIntoView)/后端计数(move_queue事件/timeline total/workflow分页/import_batch分块)/杂项(TopBar/ConfirmDialog键盘/CIStatus i18n/ToolResultBody/ModuleNode/ApprovalDialog全选)
This commit is contained in:
lxy
2026-08-02 13:11:06 +08:00
parent caaabf0c15
commit f736f435bc
70 changed files with 2645 additions and 576 deletions
+65 -7
View File
@@ -3,6 +3,21 @@
<header class="page-header">
<h1>{{ $t('projects.title') }}</h1>
<div class="header-actions">
<!-- 视图模式切换(默认 list 紧凑;card 宽松):切换持久化 localStorage -->
<div class="view-toggle" :title="$t('projects.viewModeTitle')">
<button
class="view-toggle-btn"
:class="{ active: viewMode === 'list' }"
:title="$t('projects.viewList')"
@click="viewMode = 'list'"
>☰</button>
<button
class="view-toggle-btn"
:class="{ active: viewMode === 'card' }"
:title="$t('projects.viewCard')"
@click="viewMode = 'card'"
>▦</button>
</div>
<button class="btn btn-ghost" @click="openImportModal">{{ $t('projects.importHistory') }}</button>
<button class="btn btn-ghost" @click="openTrash">{{ $t('projects.trash') }}</button>
<button class="btn btn-primary" @click="showCreateModal = true">{{ $t('projects.create') }}</button>
@@ -139,8 +154,15 @@
</div>
<!-- 项目卡片网格(单卡片渲染抽至 components/project/ProjectCard.vue,删除走 emit 回父处理) -->
<div class="project-grid">
<ProjectCard v-for="project in pagedProjects" :key="project.id" :project="project" @delete="handleDelete" />
<!-- viewMode='list' → 单列紧凑;viewMode='card' → 双列宽松(默认改造前形态) -->
<div class="project-grid" :class="'grid-' + viewMode">
<ProjectCard
v-for="project in pagedProjects"
:key="project.id"
:project="project"
:view="viewMode"
@delete="handleDelete"
/>
</div>
<!-- 分页器(F-260621-02 P3):pageSize=0 不分页(全量,等价旧行为);用户选 N 条触发客户端切片 -->
@@ -175,15 +197,20 @@ import ProjectCard from '@/components/project/ProjectCard.vue'
import Paginator from '@/components/Paginator.vue'
import { useConfirm } from '@/composables/useConfirm'
import { useToast } from '@/composables/useToast'
import { usePersistedRef } from '@/composables/usePersistedRef'
import type { ProjectRecord } from '@/api/types'
import type { ScannedProjectItem } from '@/api/project'
const store = useProjectStore()
const { t } = useI18n()
// 分页(F-260621-02 P3):pageSize=0 → 全量(向后兼容,等价改造前);用户选 N → 客户端切片
const page = ref(1)
const pageSize = ref(0)
// 分页(F-260621-02 P3):pageSize=0 → 全量(向后兼容,等价改造前);用户选 N → 客户端切片。
// page/pageSize 持久化 localStorage(刷新/重开记住用户选择)。
const page = usePersistedRef('projects.page', 1)
const pageSize = usePersistedRef('projects.pageSize', 0)
// 视图模式:'list'(紧凑单列,默认)/'card'(宽松双列)。持久化 localStorage。
const viewMode = usePersistedRef<'list' | 'card'>('projects.viewMode', 'list')
// 分页视图:pageSize=0 → 全量;否则取当前页窗口
const pagedProjects = computed(() => {
@@ -426,11 +453,41 @@ async function runImport() {
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
/* ===== 卡片网格 ===== */
/* 默认改造前为双列;viewMode 驱动 list(单列紧凑)/card(双列宽松) */
.project-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--df-gap-grid);
}
.project-grid.grid-card {
grid-template-columns: repeat(2, 1fr);
}
.project-grid.grid-list {
grid-template-columns: 1fr;
}
/* ===== 视图模式切换 ===== */
.view-toggle {
display: inline-flex;
border: 0.5px solid var(--df-border);
border-radius: var(--df-radius-sm);
overflow: hidden;
}
.view-toggle-btn {
min-width: 30px;
padding: 6px 10px;
background: transparent;
border: none;
color: var(--df-text-dim);
font-size: 14px;
line-height: 1;
cursor: pointer;
transition: all 0.15s;
}
.view-toggle-btn:hover { background: var(--df-bg-card); color: var(--df-text); }
.view-toggle-btn.active {
background: rgba(108, 99, 255, 0.12);
color: var(--df-accent);
}
/* ===== 进度条 ===== */
.card-progress { margin-bottom: 16px; }
@@ -498,7 +555,8 @@ async function runImport() {
/* ===== 响应式 ===== */
@media (max-width: 768px) {
.projects { padding: 16px; }
.project-grid {
/* 移动端:card 视图也降为单列;list 本就单列 */
.project-grid.grid-card {
grid-template-columns: 1fr;
}
}