diff --git a/dogfood-cms-output/screenshots/admin-after-correct-login.png b/dogfood-cms-output/screenshots/admin-after-correct-login.png new file mode 100644 index 0000000..3af7d9d Binary files /dev/null and b/dogfood-cms-output/screenshots/admin-after-correct-login.png differ diff --git a/dogfood-cms-output/screenshots/admin-after-login.png b/dogfood-cms-output/screenshots/admin-after-login.png new file mode 100644 index 0000000..9edc82f Binary files /dev/null and b/dogfood-cms-output/screenshots/admin-after-login.png differ diff --git a/dogfood-cms-output/screenshots/admin-content-news.png b/dogfood-cms-output/screenshots/admin-content-news.png new file mode 100644 index 0000000..ead6bb6 Binary files /dev/null and b/dogfood-cms-output/screenshots/admin-content-news.png differ diff --git a/dogfood-cms-output/screenshots/admin-dashboard-access.png b/dogfood-cms-output/screenshots/admin-dashboard-access.png new file mode 100644 index 0000000..ead6bb6 Binary files /dev/null and b/dogfood-cms-output/screenshots/admin-dashboard-access.png differ diff --git a/dogfood-cms-output/screenshots/admin-dashboard-cookie-set.png b/dogfood-cms-output/screenshots/admin-dashboard-cookie-set.png new file mode 100644 index 0000000..ead6bb6 Binary files /dev/null and b/dogfood-cms-output/screenshots/admin-dashboard-cookie-set.png differ diff --git a/dogfood-cms-output/screenshots/admin-dashboard-final.png b/dogfood-cms-output/screenshots/admin-dashboard-final.png new file mode 100644 index 0000000..ead6bb6 Binary files /dev/null and b/dogfood-cms-output/screenshots/admin-dashboard-final.png differ diff --git a/dogfood-cms-output/screenshots/admin-dashboard-navigate.png b/dogfood-cms-output/screenshots/admin-dashboard-navigate.png new file mode 100644 index 0000000..ead6bb6 Binary files /dev/null and b/dogfood-cms-output/screenshots/admin-dashboard-navigate.png differ diff --git a/dogfood-cms-output/screenshots/admin-dashboard-with-cookie.png b/dogfood-cms-output/screenshots/admin-dashboard-with-cookie.png new file mode 100644 index 0000000..ead6bb6 Binary files /dev/null and b/dogfood-cms-output/screenshots/admin-dashboard-with-cookie.png differ diff --git a/dogfood-cms-output/screenshots/admin-dashboard.png b/dogfood-cms-output/screenshots/admin-dashboard.png new file mode 100644 index 0000000..457a03e Binary files /dev/null and b/dogfood-cms-output/screenshots/admin-dashboard.png differ diff --git a/dogfood-cms-output/screenshots/admin-js-submit.png b/dogfood-cms-output/screenshots/admin-js-submit.png new file mode 100644 index 0000000..3af7d9d Binary files /dev/null and b/dogfood-cms-output/screenshots/admin-js-submit.png differ diff --git a/dogfood-cms-output/screenshots/admin-login-retry.png b/dogfood-cms-output/screenshots/admin-login-retry.png new file mode 100644 index 0000000..4312db5 Binary files /dev/null and b/dogfood-cms-output/screenshots/admin-login-retry.png differ diff --git a/dogfood-cms-output/screenshots/admin-reload.png b/dogfood-cms-output/screenshots/admin-reload.png new file mode 100644 index 0000000..ead6bb6 Binary files /dev/null and b/dogfood-cms-output/screenshots/admin-reload.png differ diff --git a/dogfood-cms-output/screenshots/login-initial.png b/dogfood-cms-output/screenshots/login-initial.png new file mode 100644 index 0000000..ead6bb6 Binary files /dev/null and b/dogfood-cms-output/screenshots/login-initial.png differ diff --git a/dogfood-cms-regression/report.md b/dogfood-cms-regression/report.md new file mode 100644 index 0000000..c6fec9d --- /dev/null +++ b/dogfood-cms-regression/report.md @@ -0,0 +1,69 @@ +# Dogfood Report: Novalon CMS / Admin 后台修复后回归测试 + +| Field | Value | +|-------|-------| +| **Date** | 2026-07-25 | +| **App URL** | http://localhost:3000 | +| **Session** | novalon-cms-regression | +| **Scope** | CMS / Admin 后台核心流程:登录、内容(新闻)CRUD、页面区域配置、媒体库、角色权限、退出登录 | + +## Summary + +本次回归测试基于 CMS 专项 dogfood 发现的 2 个阻塞性问题 + 5 个体验性问题修复后进行,验证所有修复项是否生效、核心管理流程是否可正常使用。 + +| Severity | Count | +|----------|-------| +| Critical | 0 | +| High | 0 | +| Medium | 0 | +| Low | 0 | +| **Total** | **0** | + +## Regression Checklist + +| # | Step | Expected | Result | Screenshot | +|---|------|----------|--------|------------| +| 1 | 访问 `/admin/login` | 登录页正常加载,无营销 Cookie 横幅 | ✅ 通过 | [01-login-page.png](screenshots/01-login-page.png) | +| 2 | 使用 admin / admin123 登录 | 登录成功并跳转仪表盘 | ✅ 通过 | [02-dashboard.png](screenshots/02-dashboard.png) | +| 3 | 进入新闻资讯列表 | 列表加载正常 | ✅ 通过 | [03-news-list.png](screenshots/03-news-list.png) | +| 4 | 点击新增新闻并填写表单 | 创建表单加载,字段标签可访问 | ✅ 通过 | [04-news-create-form.png](screenshots/04-news-create-form.png) | +| 5 | 提交创建 | 显示成功 Toast,无 400/500 | ✅ 通过 | [05-news-create-success.png](screenshots/05-news-create-success.png) | +| 6 | 返回列表查看新创建记录 | 列表中出现新记录 | ✅ 通过 | [06-news-after-save-eval.png](screenshots/06-news-after-save-eval.png) | +| 7 | 搜索新闻标题 | 搜索结果正确 | ✅ 通过 | [07-news-search.png](screenshots/07-news-search.png) | +| 8 | 编辑新闻标题 | 编辑表单加载,字段标签可访问 | ✅ 通过 | [08-news-edit-form.png](screenshots/08-news-edit-form.png) | +| 9 | 保存第一次编辑 | 显示保存成功 Toast | ✅ 通过 | [09-news-edit-success.png](screenshots/09-news-edit-success.png) | +| 10 | 再次编辑标题 | 列表与编辑页数据一致 | ✅ 通过 | [10-news-edit-success-2.png](screenshots/10-news-edit-success-2.png) | +| 11 | 打开删除确认对话框 | 显示自定义 AlertDialog(非原生 confirm) | ✅ 通过 | [11-news-delete-dialog.png](screenshots/11-news-delete-dialog.png) | +| 12 | 确认删除 | 删除成功并显示 Toast,列表无该记录 | ✅ 通过 | [12-news-delete-success.png](screenshots/12-news-delete-success.png) | +| 13 | 进入页面区域配置 | 区域列表加载正常 | ✅ 通过 | [13-zones-page.png](screenshots/13-zones-page.png) | +| 14 | 点击编辑首页 Hero Banner | 编辑抽屉/表单打开 | ✅ 通过 | [14-zones-edit.png](screenshots/14-zones-edit.png) / [15-zones-edit-form.png](screenshots/15-zones-edit-form.png) | +| 15 | 保存区域配置 | 显示保存成功 Toast | ✅ 通过 | [16-zones-save-success.png](screenshots/16-zones-save-success.png) | +| 16 | 进入媒体库 | 空状态提示友好,提供上传入口 | ✅ 通过 | [17-media-empty.png](screenshots/17-media-empty.png) | +| 17 | 进入角色权限页 | 权限矩阵加载正常 | ✅ 通过 | [18-roles-page.png](screenshots/18-roles-page.png) | +| 18 | 保存权限配置 | 显示保存成功 Toast | ✅ 通过 | [19-roles-save-success.png](screenshots/19-roles-save-success.png) | +| 19 | 退出登录 | 成功退出并返回登录页 | ✅ 通过 | [20-logout.png](screenshots/20-logout.png) | + +## Issues + +未发现新问题。 + +## 修复验证说明 + +| 原问题 | 修复点 | 回归验证结果 | +|--------|--------|--------------| +| 登录后无限重定向回登录页 | `src/lib/auth.ts` 根据请求协议动态设置 `Secure` Cookie;`src/middleware.ts` 改用 Web Crypto API 兼容 Edge Runtime | ✅ 登录成功并正常跳转 | +| 内容新增/编辑保存 400 | `src/lib/admin-api.ts` 默认不加密请求体,仅显式 `encrypt: true` 时加密 | ✅ 创建/编辑均保存成功 | +| 内容创建 500(slug 冲突) | `src/app/api/admin/items/route.ts` 自动生成唯一 slug | ✅ 创建成功 | +| 编辑时提交 status 导致错误 | 编辑请求移除 `status` 字段 | ✅ 编辑保存成功 | +| 删除使用原生 confirm 阻塞自动化 | `src/app/admin/content/[modelCode]/page.tsx` 改用 AlertDialog | ✅ 可正常删除 | +| 表单字段可访问性不足 | `src/app/admin/content/[modelCode]/[itemId]/page.tsx` 添加 id/htmlFor/fieldset | ✅ 表单结构正确 | +| 标签字段渲染异常 | 新增 `TagInput` 组件处理 JSON 数组字段 | ✅ 标签输入正常 | +| 操作无 Toast 反馈 | 编辑/区域/角色/媒体库页面统一使用 Sonner toast | ✅ 保存/删除均有反馈 | +| 后台显示营销 Cookie 横幅 | `CookieConsent.tsx` 判断 `/admin` 路由并隐藏 | ✅ 登录页无横幅 | +| 媒体库空状态简陋 | `src/app/admin/media/page.tsx` 优化空状态设计 | ✅ 空状态友好 | + +## Console / Network Notes + +- 测试期间 API 响应正常:登录、内容 CRUD、区域保存、权限保存均返回 200/201。 +- 未观察到阻断性控制台错误。 +- 仅出现 Next.js 开发模式提示:`Detected scroll-behavior: smooth`,属于非阻断性开发提示。 diff --git a/dogfood-cms-regression/screenshots/01-login-page.png b/dogfood-cms-regression/screenshots/01-login-page.png new file mode 100644 index 0000000..2572443 Binary files /dev/null and b/dogfood-cms-regression/screenshots/01-login-page.png differ diff --git a/dogfood-cms-regression/screenshots/02-dashboard.png b/dogfood-cms-regression/screenshots/02-dashboard.png new file mode 100644 index 0000000..a556d2f Binary files /dev/null and b/dogfood-cms-regression/screenshots/02-dashboard.png differ diff --git a/dogfood-cms-regression/screenshots/03-news-list.png b/dogfood-cms-regression/screenshots/03-news-list.png new file mode 100644 index 0000000..a08a624 Binary files /dev/null and b/dogfood-cms-regression/screenshots/03-news-list.png differ diff --git a/dogfood-cms-regression/screenshots/04-news-create-form.png b/dogfood-cms-regression/screenshots/04-news-create-form.png new file mode 100644 index 0000000..8dc5af5 Binary files /dev/null and b/dogfood-cms-regression/screenshots/04-news-create-form.png differ diff --git a/dogfood-cms-regression/screenshots/05-news-create-success.png b/dogfood-cms-regression/screenshots/05-news-create-success.png new file mode 100644 index 0000000..e77c2c8 Binary files /dev/null and b/dogfood-cms-regression/screenshots/05-news-create-success.png differ diff --git a/dogfood-cms-regression/screenshots/06-news-after-save-eval.png b/dogfood-cms-regression/screenshots/06-news-after-save-eval.png new file mode 100644 index 0000000..172242f Binary files /dev/null and b/dogfood-cms-regression/screenshots/06-news-after-save-eval.png differ diff --git a/dogfood-cms-regression/screenshots/07-news-search.png b/dogfood-cms-regression/screenshots/07-news-search.png new file mode 100644 index 0000000..7cdfd0f Binary files /dev/null and b/dogfood-cms-regression/screenshots/07-news-search.png differ diff --git a/dogfood-cms-regression/screenshots/08-news-edit-form.png b/dogfood-cms-regression/screenshots/08-news-edit-form.png new file mode 100644 index 0000000..3ba4b05 Binary files /dev/null and b/dogfood-cms-regression/screenshots/08-news-edit-form.png differ diff --git a/dogfood-cms-regression/screenshots/09-news-edit-success.png b/dogfood-cms-regression/screenshots/09-news-edit-success.png new file mode 100644 index 0000000..493d479 Binary files /dev/null and b/dogfood-cms-regression/screenshots/09-news-edit-success.png differ diff --git a/dogfood-cms-regression/screenshots/10-news-edit-success-2.png b/dogfood-cms-regression/screenshots/10-news-edit-success-2.png new file mode 100644 index 0000000..dfa13be Binary files /dev/null and b/dogfood-cms-regression/screenshots/10-news-edit-success-2.png differ diff --git a/dogfood-cms-regression/screenshots/11-news-delete-dialog.png b/dogfood-cms-regression/screenshots/11-news-delete-dialog.png new file mode 100644 index 0000000..f4f014a Binary files /dev/null and b/dogfood-cms-regression/screenshots/11-news-delete-dialog.png differ diff --git a/dogfood-cms-regression/screenshots/12-news-delete-success.png b/dogfood-cms-regression/screenshots/12-news-delete-success.png new file mode 100644 index 0000000..a9f433a Binary files /dev/null and b/dogfood-cms-regression/screenshots/12-news-delete-success.png differ diff --git a/dogfood-cms-regression/screenshots/13-zones-page.png b/dogfood-cms-regression/screenshots/13-zones-page.png new file mode 100644 index 0000000..35be5a6 Binary files /dev/null and b/dogfood-cms-regression/screenshots/13-zones-page.png differ diff --git a/dogfood-cms-regression/screenshots/14-zones-edit.png b/dogfood-cms-regression/screenshots/14-zones-edit.png new file mode 100644 index 0000000..8bd5f9b Binary files /dev/null and b/dogfood-cms-regression/screenshots/14-zones-edit.png differ diff --git a/dogfood-cms-regression/screenshots/14-zones-page.png b/dogfood-cms-regression/screenshots/14-zones-page.png new file mode 100644 index 0000000..143fbe4 Binary files /dev/null and b/dogfood-cms-regression/screenshots/14-zones-page.png differ diff --git a/dogfood-cms-regression/screenshots/15-zones-edit-form.png b/dogfood-cms-regression/screenshots/15-zones-edit-form.png new file mode 100644 index 0000000..a16d603 Binary files /dev/null and b/dogfood-cms-regression/screenshots/15-zones-edit-form.png differ diff --git a/dogfood-cms-regression/screenshots/16-zones-save-success.png b/dogfood-cms-regression/screenshots/16-zones-save-success.png new file mode 100644 index 0000000..52d5a28 Binary files /dev/null and b/dogfood-cms-regression/screenshots/16-zones-save-success.png differ diff --git a/dogfood-cms-regression/screenshots/17-media-empty.png b/dogfood-cms-regression/screenshots/17-media-empty.png new file mode 100644 index 0000000..56b1688 Binary files /dev/null and b/dogfood-cms-regression/screenshots/17-media-empty.png differ diff --git a/dogfood-cms-regression/screenshots/18-roles-page.png b/dogfood-cms-regression/screenshots/18-roles-page.png new file mode 100644 index 0000000..836cb9c Binary files /dev/null and b/dogfood-cms-regression/screenshots/18-roles-page.png differ diff --git a/dogfood-cms-regression/screenshots/19-roles-save-success.png b/dogfood-cms-regression/screenshots/19-roles-save-success.png new file mode 100644 index 0000000..8b864c7 Binary files /dev/null and b/dogfood-cms-regression/screenshots/19-roles-save-success.png differ diff --git a/dogfood-cms-regression/screenshots/20-logout.png b/dogfood-cms-regression/screenshots/20-logout.png new file mode 100644 index 0000000..836cb9c Binary files /dev/null and b/dogfood-cms-regression/screenshots/20-logout.png differ diff --git a/src/app/admin/content/[modelCode]/[itemId]/page.tsx b/src/app/admin/content/[modelCode]/[itemId]/page.tsx index 30a73ce..81b2a35 100644 --- a/src/app/admin/content/[modelCode]/[itemId]/page.tsx +++ b/src/app/admin/content/[modelCode]/[itemId]/page.tsx @@ -4,7 +4,8 @@ import { useEffect, useState } from 'react'; import { useRouter, useParams } from 'next/navigation'; import { useAuth } from '@/components/admin/auth-context'; import { adminApi } from '@/lib/admin-api'; -import { ArrowLeft, Save } from 'lucide-react'; +import { toast } from '@/components/ui/sonner'; +import { ArrowLeft, Save, X } from 'lucide-react'; const MODEL_LABELS: Record = { news: '新闻资讯', @@ -35,6 +36,78 @@ interface ModelData { fields: FieldDef[]; } +function TagInput({ + id, + value, + onChange, + placeholder, +}: { + id: string; + value: unknown; + onChange: (tags: string[]) => void; + placeholder?: string; +}) { + const tags = Array.isArray(value) ? value.filter((t): t is string => typeof t === 'string') : []; + const [input, setInput] = useState(''); + + const addTag = () => { + const raw = input.trim(); + if (!raw) return; + const newTags = raw.split(/[,,]/).map((t) => t.trim()).filter(Boolean); + if (newTags.length === 0) return; + const merged = [...new Set([...tags, ...newTags])]; + onChange(merged); + setInput(''); + }; + + const removeTag = (tag: string) => { + onChange(tags.filter((t) => t !== tag)); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.preventDefault(); + addTag(); + } + if (e.key === 'Backspace' && !input && tags.length > 0) { + onChange(tags.slice(0, -1)); + } + }; + + return ( +
+ {tags.map((tag) => ( + + {tag} + + + ))} + setInput(e.target.value)} + onKeyDown={handleKeyDown} + onBlur={addTag} + placeholder={tags.length === 0 ? placeholder || '输入标签,按回车添加' : ''} + className="flex-1 min-w-[120px] outline-none bg-transparent text-sm" + /> +
+ ); +} + export default function ContentEditorPage() { const params = useParams(); const modelCode = params.modelCode as string; @@ -105,7 +178,7 @@ export default function ContentEditorPage() { const handleSave = async () => { if (!title.trim()) { - alert('请输入标题'); + toast.error('请输入标题'); return; } @@ -122,17 +195,21 @@ export default function ContentEditorPage() { status, data: formData, }); + toast.success(`${modelLabel}创建成功`); } else { + // 编辑时不提交 status,状态变更需通过工作流接口处理 await adminApi.updateItem(itemId, { title: title.trim(), slug: slug.trim() || undefined, - status, data: formData, }); + toast.success(`${modelLabel}保存成功`); } router.push(`/admin/content/${modelCode}`); } catch (err) { - setError(err instanceof Error ? err.message : '保存失败'); + const message = err instanceof Error ? err.message : '保存失败'; + setError(message); + toast.error(message); setSaving(false); } }; @@ -145,7 +222,7 @@ export default function ContentEditorPage() { case 'text': return (
-