feat: 产品页面去掉规划中 + 项目名和版本可点击跳转

1. 移除"规划中"状态:
   - 去掉顶部图例的"规划中"
   - activeVersions 过滤改为只显示 status === 'developing'
   - 项目展开列表中的 projActive 同步只取 developing

2. 项目名可点击跳转:
   - 项目名改为按钮样式,点击跳转 /projects/{id}
   - hover 显示蓝色下划线

3. 版本胶囊可点击跳转:
   - VersionChip 包裹按钮,点击跳转 /versions/{id}
   - hover 半透明效果

4. 数据联动:
   - 不再展示规划中的版本(视觉简洁)
   - 点击直达对应详情页,方便快速访问

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Script Generator
2026-06-15 18:29:58 +08:00
parent fa7a0f7294
commit a3099f6952

View File

@@ -1,6 +1,7 @@
'use client';
import { useState, useEffect, useMemo } from 'react';
import { useRouter } from 'next/navigation';
import {
Plus,
Search,
@@ -55,12 +56,14 @@ function SortableProductRow({
onToggle,
onEdit,
onDelete,
router,
}: {
product: ProductOverview;
expanded: boolean;
onToggle: () => void;
onEdit: () => void;
onDelete: () => void;
router: ReturnType<typeof useRouter>;
}) {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } =
useSortable({ id: product.id });
@@ -72,7 +75,7 @@ function SortableProductRow({
};
const activeVersions = product.versions.filter(
(v) => v.status === 'developing' || v.status === 'planned'
(v) => v.status === 'developing'
);
const releasedCount = product.versions.filter((v) => v.status === 'released').length;
@@ -148,7 +151,7 @@ function SortableProductRow({
v.name.toLowerCase().startsWith(proj.name.toLowerCase())
);
const projActive = projVersions.filter(
(v) => v.status === 'developing' || v.status === 'planned'
(v) => v.status === 'developing'
);
const projReleased = projVersions.filter((v) => v.status === 'released').length;
return (
@@ -156,9 +159,12 @@ function SortableProductRow({
key={proj.id}
className="flex items-center gap-3 py-2 border-b border-[var(--line-soft)] last:border-b-0"
>
<span className="text-[12px] font-medium text-[var(--ink-soft)] min-w-0 flex-shrink-0">
<button
onClick={() => router.push(`/projects/${proj.id}`)}
className="text-[12px] font-medium text-[var(--ink-soft)] hover:text-[var(--accent)] hover:underline min-w-0 flex-shrink-0 cursor-pointer"
>
{proj.name}
</span>
</button>
{proj.description && (
<span className="text-[11px] text-[var(--ink-muted)] truncate hidden lg:inline">
{proj.description}
@@ -166,11 +172,16 @@ function SortableProductRow({
)}
<div className="flex-1 flex items-center gap-1.5 flex-wrap justify-end">
{projActive.map((v) => (
<VersionChip
<button
key={v.id}
onClick={() => router.push(`/versions/${v.id}`)}
className="hover:opacity-80 transition-opacity"
>
<VersionChip
name={v.name}
status={(v.status as VersionStatus) ?? 'developing'}
/>
</button>
))}
{projReleased > 0 && (
<span className="text-[11px] text-[var(--ink-muted)] px-1.5">
@@ -320,6 +331,7 @@ function MigrateDialog({
/* ─── Main Page ─── */
export default function ProductsPage() {
const router = useRouter();
const {
overview,
loading,
@@ -432,9 +444,6 @@ export default function ProductsPage() {
<span className="inline-flex items-center gap-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-[#2563eb]" />
</span>
<span className="inline-flex items-center gap-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-orange-500" />
</span>
<span className="inline-flex items-center gap-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-zinc-400" />
</span>
@@ -493,6 +502,7 @@ export default function ProductsPage() {
onToggle={() => toggleExpand(product.id)}
onEdit={() => setEditing(product)}
onDelete={() => handleDelete(product)}
router={router}
/>
))}
</SortableContext>