重构: 拆Projects.vue项目卡片(strategy前端)
- 新建 components/project/ProjectCard.vue(133行): 卡片模板+样式+parseStack/stripMd/statusLabel/stageClass+useRouter导航 - Projects.vue 692→593(-99): v-for改<ProjectCard> + 删卡片样式/导入 - props project + emit delete(props/emit最小耦合, 无store) 主代兜底(新工作模式·vue-tsc only不grep降上下文): vue-tsc 0(子代理自验印证) strategy: 前端views, 子组件抽卡片; Projects余593(导入模态留下批); tech-tag父子各存(scoped隔离) git add指定(Projects.vue+ProjectCard.vue)
This commit is contained in:
@@ -138,38 +138,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 项目卡片网格 -->
|
||||
<!-- 项目卡片网格(单卡片渲染抽至 components/project/ProjectCard.vue,删除走 emit 回父处理) -->
|
||||
<div class="project-grid">
|
||||
<div class="project-card" v-for="project in store.projects" :key="project.id" @click="router.push('/projects/' + project.id)">
|
||||
<div class="card-top">
|
||||
<div class="card-title-row">
|
||||
<h2 class="card-name">{{ project.name }}</h2>
|
||||
</div>
|
||||
<div class="card-top-actions">
|
||||
<span class="stage-badge" :class="stageClass(project.status)">{{ $t(statusLabel(project.status)) }}</span>
|
||||
<button class="card-del-btn" :title="$t('projects.deleteTitle')" @click.stop="handleDelete(project)">🗑</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="card-desc">{{ stripMd(project.description) }}</p>
|
||||
|
||||
<!-- 底部信息 -->
|
||||
<div class="card-footer">
|
||||
<div class="footer-stat">
|
||||
<span class="stat-icon">📅</span>
|
||||
<span>{{ formatDate(project.created_at) }}</span>
|
||||
</div>
|
||||
<div class="footer-stat">
|
||||
<span class="stat-icon">🕐</span>
|
||||
<span>{{ formatDate(project.updated_at) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 技术栈标签 -->
|
||||
<div class="card-tags" v-if="parseStack(project.stack).length">
|
||||
<span class="tech-tag" v-for="t in parseStack(project.stack)" :key="t">{{ t }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ProjectCard v-for="project in store.projects" :key="project.id" :project="project" @delete="handleDelete" />
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
@@ -187,21 +158,17 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { useProjectStore } from '@/stores/project'
|
||||
import { projectApi } from '@/api'
|
||||
import { formatDate } from '@/utils/time'
|
||||
import { parseStack } from '@/utils/project'
|
||||
import { stripMd } from '@/utils/markdown'
|
||||
import { projectStatusLabel as statusLabel, projectBadgeClass as stageClass } from '../constants/project'
|
||||
import ConfirmDialog from '@/components/ConfirmDialog.vue'
|
||||
import ProjectCard from '@/components/project/ProjectCard.vue'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
import type { ProjectRecord } from '@/api/types'
|
||||
import type { ScannedProjectItem } from '@/api/project'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useProjectStore()
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -477,52 +444,9 @@ async function runImport() {
|
||||
gap: var(--df-gap-grid);
|
||||
}
|
||||
|
||||
.project-card {
|
||||
background: var(--df-bg-card);
|
||||
border: 0.5px solid var(--df-border);
|
||||
border-radius: var(--df-radius-lg);
|
||||
padding: var(--df-pad-panel);
|
||||
transition: all 0.15s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.project-card:hover {
|
||||
border-color: var(--df-accent);
|
||||
|
||||
}
|
||||
|
||||
.card-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.card-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.card-icon { font-size: 22px; }
|
||||
.card-name { font-size: 17px; font-weight: 500; color: var(--df-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
|
||||
.stage-badge {
|
||||
font-size: 12px; font-weight: 500;
|
||||
padding: 3px 10px;
|
||||
border-radius: var(--df-radius-sm);
|
||||
}
|
||||
.stage-design { background: rgba(100,181,246,0.15); color: var(--df-info); }
|
||||
.stage-coding { background: rgba(108,99,255,0.15); color: var(--df-accent); }
|
||||
.stage-testing { background: rgba(255,217,61,0.15); color: var(--df-warning); }
|
||||
.stage-release { background: rgba(100,255,218,0.15); color: var(--df-success); }
|
||||
|
||||
.card-desc {
|
||||
font-size: 13px;
|
||||
color: var(--df-text-secondary);
|
||||
line-height: 1.5;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* ===== 进度条 ===== */
|
||||
.card-progress { margin-bottom: 16px; }
|
||||
.card-progress { margin-bottom: 16px; }
|
||||
.progress-label-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -543,22 +467,7 @@ async function runImport() {
|
||||
transition: width 0.3s;
|
||||
}
|
||||
|
||||
/* ===== 底部信息 ===== */
|
||||
.card-footer {
|
||||
display: flex;
|
||||
gap: var(--df-gap-grid);
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.footer-stat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
color: var(--df-text-dim);
|
||||
}
|
||||
.stat-icon { font-size: 13px; }
|
||||
|
||||
/* ===== 技术栈标签 ===== */
|
||||
/* ===== 技术栈标签(创建/导入模态复用,保留;卡片自身样式随 ProjectCard.vue 迁出) ===== */
|
||||
.card-tags {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
@@ -631,15 +540,7 @@ async function runImport() {
|
||||
}
|
||||
.error-dismiss:hover { opacity: 0.7; }
|
||||
|
||||
/* ===== 卡片删除按钮 + 回收站 ===== */
|
||||
.card-top-actions { display: flex; align-items: center; gap: 8px; }
|
||||
.card-del-btn {
|
||||
background: transparent; border: none; cursor: pointer;
|
||||
font-size: 14px; padding: 2px 4px; opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
.project-card:hover .card-del-btn { opacity: 0.55; }
|
||||
.card-del-btn:hover { opacity: 1 !important; }
|
||||
/* ===== 回收站(卡片删除按钮样式随 ProjectCard.vue 迁出) ===== */
|
||||
.trash-list { display: flex; flex-direction: column; max-height: 400px; overflow-y: auto; }
|
||||
.trash-item {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user