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