Files
crearte-monorepo/docs/plans/2026-09-30-creator-center.md

399 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 创作者中心(/creator)Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 在 crearte 前端新增首页样式的「创作者中心」页(`/creator`):hero + 「作品数据」占位卡(按登录态三态分流)+「创作教程」占位卡(链现有文档),页头主导航加第三个 tab。
**Architecture:** 纯前端改动——一个新路由、一个新视图(复刻 `LandingView.vue` 的墨纸 class 体系,零新 token)、`AppHeader` 加 tab;登录态用既有 `authEnabled` + `session`(`@/auth`);页面无数据请求、无加载/失败态。测试走 Playwright e2e(默认配置 + noauth 配置),TDD:先写失败 e2e 再实现。
**Tech Stack:** Vue 3 `<script setup>` + TypeScript + vue-router + Tailwind class 体系;Playwright(`@playwright/test`);crearte 仓(monorepo 内层前端仓)。
**Spec:** `crearte-monorepo/docs/specs/2026-09-30-creator-center-design.md`
## Global Constraints
- 工作目录:`crearte-monorepo/crearte`(内层前端仓)。除标注外,所有命令在 `crearte-monorepo/crearte/src` 下执行。
- 分支:从 `master`(当前顶端 `eb5fbed`,changelog 0.16.0)切 `feat/creator-center`。crearte AGENTS.md:dev 分支命名 `{feat|fix|docs|chore}/{name}`;**不在 master 上直接提交**。
- 每个 Task 完成即提交,提交信息风格对齐仓库现状(`feat(scope): 简述`)。
- 文案逐字采用 spec §3.2,不得改写:hero 徽章「创作者」、标语 `SHARE YOUR CREATIONS`、说明「把你的作品分享给所有人」、按钮「提交作品」「投稿指南」;卡标题「作品数据」「创作教程」;数据卡三态文案与教程卡占位文案见各 Task。
- 样式只复用 `LandingView.vue` 既有 class;不新增 design token、不改 `main.css`。
- CHANGELOG:`docs/CHANGELOG.md`(crearte 仓)顶部追加 `## [0.17.0] - 2026-09-30` 条目;同一条目英文行、中文行连续书写(中间无空行),条目之间空行分隔。
- 完成定义:`npx vitest run`、`npx vue-tsc --noEmit`、`npx playwright test`、`npx playwright test --config playwright.noauth.config.ts` 四条全绿;最后 `--no-ff` 合并回 `master`、删分支、推送。
- 默认 e2e 构建注入了 `VITE_API_BASE_URL=http://localhost:4173`(`authEnabled === true`);noauth 构建注入空值(`authEnabled === false`)。两条路径都要覆盖。
---
### Task 1: 分支准备 + 路由 + 主导航 tab + hero
**Files:**
- Create: `src/app/views/CreatorCenterView.vue`(先只含 hero)
- Modify: `src/app/router/index.ts`(在 `home` 路由后插入 `creator` 路由)
- Modify: `src/app/components/AppHeader.vue`(`onCreator` computed + 主导航第三个 tab)
- Test: `src/e2e/creator.spec.ts`(新建,先写第 1 条用例)
**Interfaces:**
- Consumes: 无(首个任务)
- Produces: 路由 `{ path: '/creator', name: 'creator' }`;视图默认导出 `CreatorCenterView.vue`;header tab 可访问名「创作者中心」(激活时 `aria-current="page"`)
- [ ] **Step 1: 从 master 切功能分支**
```bash
cd crearte-monorepo/crearte
git checkout master
git checkout -b feat/creator-center
```
- [ ] **Step 2: 写失败的 e2e(第 1 条用例)**
创建 `src/e2e/creator.spec.ts`:
```ts
import { expect, test } from '@playwright/test'
test('主导航有创作者中心 tab,/creator 渲染 hero', async ({ page }) => {
await page.goto('http://localhost:4173/creator')
const tab = page.locator('header').getByRole('link', { name: '创作者中心' })
await expect(tab).toBeVisible()
await expect(tab).toHaveAttribute('aria-current', 'page')
await expect(page.getByRole('heading', { level: 1, name: '创作者中心' })).toBeVisible()
await expect(page.getByText('SHARE YOUR CREATIONS')).toBeVisible()
await expect(page.getByText('把你的作品分享给所有人')).toBeVisible()
await expect(page.getByRole('link', { name: '提交作品' })).toHaveAttribute('href', '/submit/new')
await expect(page.getByRole('link', { name: '投稿指南' })).toHaveAttribute('href', '/docs/contribute')
})
```
- [ ] **Step 3: 运行确认失败**
Run: `cd src && npx playwright test e2e/creator.spec.ts`
Expected: FAIL——`/creator` 落入 NotFound 路由,heading「创作者中心」不存在(Playwright 自动拉起 webServer:`build:e2e` + `serve-runtime`,首次约 1–3 分钟)。
- [ ] **Step 4: 新建视图(仅 hero)**
创建 `src/app/views/CreatorCenterView.vue`:
```vue
<script setup lang="ts">
import { RouterLink } from 'vue-router'
const SLOGAN = 'SHARE YOUR CREATIONS'
const TAGLINE = '把你的作品分享给所有人'
</script>
<template>
<section class="border-[3px] border-ink bg-surface px-6 py-10 text-center shadow-hard sm:px-10 sm:py-14">
<span class="inline-block bg-ink px-2 py-1 text-[0.6875rem] font-bold tracking-[0.3em] text-paper">创作者</span>
<h1 class="mt-3 font-display text-4xl leading-none font-black tracking-tight sm:text-5xl md:text-6xl">创作者中心</h1>
<p class="mt-3 font-mono text-[0.6875rem] tracking-[0.3em] text-ink-soft">{{ SLOGAN }}</p>
<div class="mx-auto my-5 h-[3px] w-16 bg-ink"></div>
<p class="text-sm text-ink-soft">{{ TAGLINE }}</p>
<div class="mt-6 flex flex-wrap justify-center gap-3">
<RouterLink to="/submit/new" class="btn-ink lift hover:shadow-hard active:shadow-none">提交作品</RouterLink>
<RouterLink to="/docs/contribute" class="btn-surface lift hover:shadow-hard active:shadow-none">投稿指南</RouterLink>
</div>
</section>
</template>
```
- [ ] **Step 5: 注册路由**
`src/app/router/index.ts`,在 `home` 路由行之后插入:
```ts
{ path: '/creator', name: 'creator', component: () => import('@/views/CreatorCenterView.vue') },
```
- [ ] **Step 6: AppHeader 加 tab**
`src/app/components/AppHeader.vue` script 段,在 `onDocs` 行之后加:
```ts
const onCreator = computed(() => route.name === 'creator')
```
template 主导航 `<nav>` 内,「文档」RouterLink 之后、`</nav>` 之前插入:
```html
<RouterLink
to="/creator"
class="border-b-[3px] pb-0.5 text-sm font-bold"
:class="onCreator ? 'border-b-accent-ink text-accent-ink' : 'border-b-transparent text-ink-soft'"
:aria-current="onCreator ? 'page' : undefined"
>创作者中心</RouterLink>
```
- [ ] **Step 7: 运行确认通过**
Run: `npx playwright test e2e/creator.spec.ts`
Expected: PASS(1 passed)
- [ ] **Step 8: 提交**
```bash
cd crearte-monorepo/crearte
git add src/app/views/CreatorCenterView.vue src/app/router/index.ts src/app/components/AppHeader.vue src/e2e/creator.spec.ts
git commit -m "feat(creator): /creator route, header tab and hero — landing-style skeleton"
```
---
### Task 2: 作品数据卡(登录态三态分流)
**Files:**
- Modify: `src/app/views/CreatorCenterView.vue`(script 加 `user` computed;template 在 hero 后加卡片行 + 「作品数据」卡)
- Modify: `src/e2e/creator.spec.ts`(追加第 2、3 条用例)
- Modify: `src/e2e/noauth.spec.ts`(在既有用例末尾追加 `/creator` 断言)
**Interfaces:**
- Consumes: Task 1 的 `/creator` 路由与视图;`authEnabled` / `session`(`@/auth`,`AppHeader.vue` 同款用法)
- Produces: 卡片标题「作品数据」(h3);未登录时卡内「登录」链接 `href="/login?next=%2Fcreator"`
- [ ] **Step 1: 写失败的 e2e(第 2、3 条用例)**
`src/e2e/creator.spec.ts` 头部 import 改为:
```ts
import { expect, test } from '@playwright/test'
import { seedSession } from './helpers'
```
文件末尾追加:
```ts
test('数据卡:未登录给登录引导且 next 回跳 /creator', async ({ page }) => {
await page.goto('http://localhost:4173/creator')
const card = page.getByRole('heading', { name: '作品数据' }).locator('..')
await expect(card.getByText('登录后即可查看您作品的数据。')).toBeVisible()
await expect(card.getByRole('link', { name: '登录' })).toHaveAttribute('href', '/login?next=%2Fcreator')
})
test('数据卡:已登录显示建设中文案且无登录链接', async ({ page }) => {
await seedSession(page)
await page.goto('http://localhost:4173/creator')
const card = page.getByRole('heading', { name: '作品数据' }).locator('..')
await expect(card.getByText('数据面板正在建设中。上线后将展示您作品的浏览、下载与评分。')).toBeVisible()
await expect(card.getByRole('link', { name: '登录' })).toHaveCount(0)
})
```
`src/e2e/noauth.spec.ts` 既有用例末尾(`expect(authRequests).toEqual([])` 之前)追加:
```ts
await page.goto('/creator')
await expect(page.getByRole('heading', { level: 1, name: '创作者中心' })).toBeVisible()
await expect(page.getByText('数据面板正在建设中。', { exact: true })).toBeVisible()
await expect(page.getByRole('link', { name: '登录' })).toHaveCount(0)
```
- [ ] **Step 2: 运行确认失败**
Run: `npx playwright test e2e/creator.spec.ts`
Expected: FAIL——「作品数据」heading 不存在。
- [ ] **Step 3: 实现数据卡**
`src/app/views/CreatorCenterView.vue` script 段改为:
```vue
<script setup lang="ts">
import { computed } from 'vue'
import { RouterLink } from 'vue-router'
import { authEnabled, session } from '@/auth'
const SLOGAN = 'SHARE YOUR CREATIONS'
const TAGLINE = '把你的作品分享给所有人'
const user = computed(() => session.state.user)
</script>
```
template 在 hero `</section>` 之后追加(本 Task 先只放数据卡,教程卡在 Task 3 加):
```html
<section class="mt-8 flex flex-col gap-4 sm:flex-row">
<h2 class="sr-only">创作者中心</h2>
<section class="flex-1 border-2 border-ink bg-surface p-4 shadow-hard-sm">
<h3 class="text-sm font-black">作品数据</h3>
<p class="mt-2 text-xs leading-relaxed text-ink-soft">
<template v-if="authEnabled && !user">登录后即可查看您作品的数据。<RouterLink :to="{ path: '/login', query: { next: '/creator' } }" class="underline">登录</RouterLink></template>
<template v-else-if="authEnabled">数据面板正在建设中。上线后将展示您作品的浏览、下载与评分。</template>
<template v-else>数据面板正在建设中。</template>
</p>
</section>
</section>
```
- [ ] **Step 4: 运行确认通过(默认配置)**
Run: `npx playwright test e2e/creator.spec.ts`
Expected: PASS(3 passed)
- [ ] **Step 5: 运行 noauth 配置确认通过**
Run: `npx playwright test --config playwright.noauth.config.ts`
Expected: PASS(1 passed,含新增的 `/creator` 断言)
- [ ] **Step 6: 提交**
```bash
cd crearte-monorepo/crearte
git add src/app/views/CreatorCenterView.vue src/e2e/creator.spec.ts src/e2e/noauth.spec.ts
git commit -m "feat(creator): work-data placeholder card with three auth states (anon prompt / signed-in / noauth)"
```
---
### Task 3: 创作教程卡(占位 + 链现有文档)
**Files:**
- Modify: `src/app/views/CreatorCenterView.vue`(卡片行内追加「创作教程」卡)
- Modify: `src/e2e/creator.spec.ts`(追加第 4 条用例)
**Interfaces:**
- Consumes: Task 2 的卡片行结构(`section.mt-8.flex ... sm:flex-row`)
- Produces: 卡片标题「创作教程」(h3);卡内链接「《提交作品指南》」`href="/docs/contribute"`
- [ ] **Step 1: 写失败的 e2e(第 4 条用例)**
`src/e2e/creator.spec.ts` 末尾追加:
```ts
test('教程卡占位并链到提交作品指南', async ({ page }) => {
await page.goto('http://localhost:4173/creator')
const card = page.getByRole('heading', { name: '创作教程' }).locator('..')
await expect(card.getByText('教程整理中,敬请期待。')).toBeVisible()
await expect(card.getByRole('link', { name: '《提交作品指南》' })).toHaveAttribute('href', '/docs/contribute')
})
```
- [ ] **Step 2: 运行确认失败**
Run: `npx playwright test e2e/creator.spec.ts`
Expected: FAIL——「创作教程」heading 不存在。
- [ ] **Step 3: 实现教程卡**
`src/app/views/CreatorCenterView.vue`,在「作品数据」卡的 `</section>` 之后、卡片行 `</section>` 之前插入:
```html
<section class="flex-1 border-2 border-ink bg-surface p-4 shadow-hard-sm">
<h3 class="text-sm font-black">创作教程</h3>
<p class="mt-2 text-xs leading-relaxed text-ink-soft">教程整理中,敬请期待。投稿流程、打包规范与过审要点正在重新整理,完成后在本页发布;可先阅读<RouterLink to="/docs/contribute" class="underline">《提交作品指南》</RouterLink>。</p>
</section>
```
- [ ] **Step 4: 运行确认通过**
Run: `npx playwright test e2e/creator.spec.ts`
Expected: PASS(4 passed)
- [ ] **Step 5: 提交**
```bash
cd crearte-monorepo/crearte
git add src/app/views/CreatorCenterView.vue src/e2e/creator.spec.ts
git commit -m "feat(creator): tutorial placeholder card linking to the submission guide"
```
---
### Task 4: CHANGELOG + 全量验证 + 合并回 master + 推送
**Files:**
- Modify: `docs/CHANGELOG.md`(crearte 仓,追加 0.17.0)
**Interfaces:**
- Consumes: Task 1–3 的全部提交(分支 `feat/creator-center`)
- Produces: `master` 上的合并提交与 0.17.0 条目;远端更新
- [ ] **Step 1: 追加 CHANGELOG 条目**
`docs/CHANGELOG.md`,在文件说明段之后、`## [0.16.0] - 2026-09-30` 之前插入:
```md
## [0.17.0] - 2026-09-30
### Added / 新增
- Added the Creator Center (`/creator`): a homepage-style page with a hero (提交作品 / 投稿指南 CTAs) and two cards — 「作品数据」 (placeholder adapting to auth state: anonymous visitors get a login prompt with `next` return, signed-in users see build-in-progress copy, noauth deploys show no login link) and 「创作教程」 (placeholder linking to the existing submission guide until the tutorial content lands). The header main nav gains a third tab with the same active styling as 作品/文档. No backend or deploy changes.
- 新增创作者中心(`/creator`):首页样式页面,hero(提交作品 / 投稿指南两个按钮)+ 两张卡——「作品数据」占位卡按登录态分流(未登录给登录引导并带 `next` 回跳、已登录显示建设中文案、noauth 部署不出现登录链接)与「创作教程」占位卡(教程内容定稿前链向现有《提交作品指南》)。页头主导航新增第三个 tab,激活样式与「作品 / 文档」一致。零后端、零部署改动。
```
- [ ] **Step 2: 全量验证(四条命令依次跑,必须全绿)**
```bash
cd crearte-monorepo/crearte/src
npx vitest run
npx vue-tsc --noEmit
npx playwright test
npx playwright test --config playwright.noauth.config.ts
```
Expected: vitest 全 passed(存量 333+,无新增单测);`vue-tsc` 无输出;默认 e2e 全 passed(含 creator.spec.ts 4 条,存量不回归);noauth e2e 1 passed。
- [ ] **Step 3: 提交 CHANGELOG**
```bash
cd crearte-monorepo/crearte
git add docs/CHANGELOG.md
git commit -m "docs: changelog 0.17.0 — creator center"
```
- [ ] **Step 4: 推送功能分支(触发 CI)**
```bash
git push origin feat/creator-center
```
Expected: 推送成功;`validate.yml` 工作流对分支运行(若失败,修复后追加提交再推送,勿改历史)。
- [ ] **Step 5: 合并回 master(--no-ff)并删分支**
```bash
git checkout master
git merge --no-ff feat/creator-center -m "merge: creator center (/creator) — placeholder data & tutorial cards, header tab; crearte 0.17.0"
git branch -d feat/creator-center
```
- [ ] **Step 6: 推送 master**
```bash
git push origin master
```
Expected: `master` 更新,包含 4 个功能提交 + 1 个合并提交。
- [ ] **Step 7: 收尾核对**
```bash
git log --oneline -6
git status --short --branch
```
Expected: 合并提交在顶端;工作区干净、与 `origin/master` 同步。
---
## Self-Review 记录(计划编写时自查)
**1. Spec 覆盖核对:**
| Spec 验收标准 | 对应步骤 |
|---|---|
| 1. 主导航 tab + 激活态一致 | Task 1 Step 2/6/7 |
| 2. hero + 两卡、视觉语言复用 | Task 1 Step 4、Task 2 Step 3、Task 3 Step 3 |
| 3. 三态分流(未登录 next / 已登录 / noauth) | Task 2 Step 1/3/4/5 |
| 4. 教程卡占位 + /docs/contribute 链接 | Task 3 |
| 5. 新增 e2e 通过 + 存量全量绿 | Task 2–4 各 Step 4/5、Task 4 Step 2 |
| 6. server / deploy 零 diff | 全程只碰 `src/` 与 crearte 仓 `docs/CHANGELOG.md` |
**2. 占位符扫描:** 无 TBD/TODO;每步含确切代码或命令;失败预期逐条写明。
**3. 一致性:** 路由名 `creator`、视图 `CreatorCenterView.vue`、tab 名「创作者中心」、登录链接 `href="/login?next=%2Fcreator"`、教程链接 `href="/docs/contribute"` 在各 Task 间一致;CHANGELOG 版本 0.17.0 与 master 顶端 0.16.0 衔接。