From c143b67275548bd0aebede197df70ae9926856c6 Mon Sep 17 00:00:00 2001 From: duanshuwen Date: Wed, 19 Aug 2026 21:20:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(route-detail):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E8=B7=AF=E7=BA=BF=E8=AF=A6=E6=83=85=E7=8B=AC=E7=AB=8B=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=85=A8=E9=93=BE=E8=B7=AF=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加`DetailRecord`数据库模型、迁移脚本及完整的CRUD管理接口,将路线详情从玩法路线表解耦,实现独立存储。 在Admin UI的路线编辑器中集成详情配置面板,支持编辑详情文案、图片等展示内容。 完善MiniAPP路线详情页,新增导航函数、API调用及数据归一化逻辑,并添加对应单元测试。 更新所有相关文档,明确领域边界、联调流程及接口契约规范。 调整首页和玩法页的点击跳转逻辑,优先跳转路线详情页而非原需求页。 新增数值格式化工具函数优化内容展示效果。 --- .../state/server-info | 1 - .../state/server-stopped | 1 + WonderQ-Admin-UI/src/api.ts | 58 +++++ .../admin/WanfaRouteDetailFields.tsx | 121 ++++++++++ .../src/components/admin/WanfaRouteEditor.tsx | 109 +++++++-- .../src/components/admin/WanfaRouteRail.tsx | 27 ++- .../src/pages/structure/WanfaPage.tsx | 66 +++-- .../alembic/versions/0021_detail_records.py | 97 ++++++++ WonderQ-Admin/app/models.py | 21 ++ WonderQ-Admin/app/routers/admin.py | 113 ++++++++- WonderQ-Admin/app/routers/public.py | 16 +- WonderQ-Admin/app/schemas.py | 77 ++++++ WonderQ-Admin/app/serializers.py | 20 ++ WonderQ-Admin/tests/test_details.py | 225 ++++++++++++++++++ WonderQ-MiniAPP/src/lib/api.ts | 5 + WonderQ-MiniAPP/src/lib/data.ts | 4 + WonderQ-MiniAPP/src/lib/navigation.ts | 4 + WonderQ-MiniAPP/src/lib/types.ts | 14 ++ WonderQ-MiniAPP/src/pages.json | 6 + .../detail/components/DetailOverviewCard.vue | 55 ++--- .../detail/components/detailPresentation.ts | 167 ++++++------- WonderQ-MiniAPP/src/pages/detail/index.vue | 199 ++++++++++------ WonderQ-MiniAPP/src/pages/home/index.vue | 13 +- WonderQ-MiniAPP/src/pages/play/index.vue | 4 +- WonderQ-MiniAPP/tests/detail-data.test.ts | 54 +++++ WonderQ-MiniAPP/tests/navigation.test.ts | 20 ++ docs/README.md | 8 +- docs/detail-api.md | 57 ++++- docs/home-api.md | 2 + docs/integration-workflow.md | 13 + docs/public-api.md | 23 ++ docs/wanfa-api.md | 2 + 32 files changed, 1343 insertions(+), 259 deletions(-) delete mode 100644 .superpowers/brainstorm/team-building-20260819-visual/state/server-info create mode 100644 .superpowers/brainstorm/team-building-20260819-visual/state/server-stopped create mode 100644 WonderQ-Admin-UI/src/components/admin/WanfaRouteDetailFields.tsx create mode 100644 WonderQ-Admin/alembic/versions/0021_detail_records.py create mode 100644 WonderQ-Admin/tests/test_details.py create mode 100644 WonderQ-MiniAPP/tests/detail-data.test.ts create mode 100644 WonderQ-MiniAPP/tests/navigation.test.ts diff --git a/.superpowers/brainstorm/team-building-20260819-visual/state/server-info b/.superpowers/brainstorm/team-building-20260819-visual/state/server-info deleted file mode 100644 index 496ad36..0000000 --- a/.superpowers/brainstorm/team-building-20260819-visual/state/server-info +++ /dev/null @@ -1 +0,0 @@ -{"type":"server-started","port":63862,"host":"0.0.0.0","url_host":"127.0.0.1","url":"http://127.0.0.1:63862","screen_dir":"D:\\www\\znkj\\WonderQ-Project\\.superpowers\\brainstorm\\team-building-20260819-visual\\content","state_dir":"D:\\www\\znkj\\WonderQ-Project\\.superpowers\\brainstorm\\team-building-20260819-visual\\state"} diff --git a/.superpowers/brainstorm/team-building-20260819-visual/state/server-stopped b/.superpowers/brainstorm/team-building-20260819-visual/state/server-stopped new file mode 100644 index 0000000..c0242e3 --- /dev/null +++ b/.superpowers/brainstorm/team-building-20260819-visual/state/server-stopped @@ -0,0 +1 @@ +{"reason":"idle timeout","timestamp":1787142361651} diff --git a/WonderQ-Admin-UI/src/api.ts b/WonderQ-Admin-UI/src/api.ts index ab49cd5..0dbbd7d 100644 --- a/WonderQ-Admin-UI/src/api.ts +++ b/WonderQ-Admin-UI/src/api.ts @@ -120,6 +120,31 @@ export type WanfaRouteCreate = { export type WanfaRoutePatch = Partial; +export type DetailRecord = { + id: string; + key: string; + eyebrow: string; + duration: string; + title: string; + subtitle: string; + intro: string; + highlights: string[]; + included: string[]; + excluded: string[]; + notes: string[]; + gallery: string[]; + isActive: boolean; + sortOrder: number; + createdAt?: string; + updatedAt?: string; +}; + +export type DetailCreate = Omit & { + sortOrder?: number; +}; + +export type DetailPatch = Partial; + export type ConciergeDetail = { icon: string; label: string; @@ -391,6 +416,39 @@ export async function reorderWanfaRoutes(categoryId: string, itemIds: string[]) }); } +export async function getDetails() { + return request<{ details: DetailRecord[] }>("/api/admin/details"); +} + +export async function getDetail(detailId: string) { + return request(`/api/admin/details/${detailId}`); +} + +export async function createDetail(input: DetailCreate) { + return request("/api/admin/details", { + method: "POST", + body: JSON.stringify(input), + }); +} + +export async function updateDetail(detailId: string, input: DetailPatch) { + return request(`/api/admin/details/${detailId}`, { + method: "PATCH", + body: JSON.stringify(input), + }); +} + +export async function deleteDetail(detailId: string) { + return request<{ id: string }>(`/api/admin/details/${detailId}`, { method: "DELETE" }); +} + +export async function reorderDetails(itemIds: string[]) { + return request<{ items: DetailRecord[] }>("/api/admin/details/reorder", { + method: "PATCH", + body: JSON.stringify({ itemIds }), + }); +} + export async function getConciergeAdvisors() { return request<{ advisors: ConciergeAdvisor[] }>('/api/admin/concierge/advisors'); } diff --git a/WonderQ-Admin-UI/src/components/admin/WanfaRouteDetailFields.tsx b/WonderQ-Admin-UI/src/components/admin/WanfaRouteDetailFields.tsx new file mode 100644 index 0000000..56ed514 --- /dev/null +++ b/WonderQ-Admin-UI/src/components/admin/WanfaRouteDetailFields.tsx @@ -0,0 +1,121 @@ +import type { DetailCreate } from "@/api"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; + +const detailArrayFields = [ + ["highlights", "行程亮点", "每行填写一项亮点"], + ["included", "费用包含", "每行填写一项包含内容"], + ["excluded", "费用不含", "每行填写一项不包含内容"], + ["notes", "注意事项", "每行填写一项注意事项"], + ["gallery", "详情图片画廊", "每行填写一个 http(s) 图片地址"], +] as const; + +type DetailArrayField = (typeof detailArrayFields)[number][0]; + +export function WanfaRouteDetailFields({ + detail, + onChange, +}: { + detail: DetailCreate; + onChange: (key: K, value: DetailCreate[K]) => void; +}) { + const updateLines = (key: DetailArrayField, value: string) => { + onChange( + key, + value + .split("\n") + .map((item) => item.trim()) + .filter(Boolean), + ); + }; + + return ( +
+
+

路线详情

+

+ 详情内容由独立详情接口维护,数组字段按每行一项保存。 +

+
+
+ + + +