From a65cb05d5d3222417f69d17c900c5f752b8073ff Mon Sep 17 00:00:00 2001 From: flowerstonezl <125468358+flowerstonezl@users.noreply.github.com> Date: Tue, 19 May 2026 21:05:31 +0800 Subject: [PATCH] mwy --- frontend/src/api/client.ts | 9 +- frontend/src/components/FirstCallPage.vue | 161 +++++++ .../src/components/FirstMeetCampusPage.vue | 187 ++++++++ frontend/src/components/FirstMeetDmPage.vue | 342 ++++++++++++++ .../src/components/FirstMeetTrainPage.vue | 442 ++++++++++++++++++ frontend/src/components/FissurePage.vue | 400 +++++++++++++--- .../src/components/HeartMangaBackdrop.vue | 61 ++- frontend/src/components/HeartWindowPage.vue | 316 ++++++++----- frontend/src/components/HomeRentalPage.vue | 149 ++++++ frontend/src/components/IkeaDatePage.vue | 145 ++++++ frontend/src/components/LuckinCoffeePage.vue | 182 ++++++++ .../src/components/NepalPageContainer.vue | 37 +- frontend/src/components/PolaroidCard.vue | 31 +- frontend/src/components/ReconcilePage.vue | 171 +++++++ frontend/src/utils/storyPageTransition.ts | 12 + 15 files changed, 2443 insertions(+), 202 deletions(-) create mode 100644 frontend/src/components/FirstCallPage.vue create mode 100644 frontend/src/components/FirstMeetCampusPage.vue create mode 100644 frontend/src/components/FirstMeetDmPage.vue create mode 100644 frontend/src/components/FirstMeetTrainPage.vue create mode 100644 frontend/src/components/HomeRentalPage.vue create mode 100644 frontend/src/components/IkeaDatePage.vue create mode 100644 frontend/src/components/LuckinCoffeePage.vue create mode 100644 frontend/src/components/ReconcilePage.vue diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 546f84a..850da68 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -7,7 +7,14 @@ export const api = axios.create({ headers: { 'Content-Type': 'application/json' }, }) -export type MemoryFeatureType = 'heart_window' | 'fissure_page' | 'standard_nepal' +export type MemoryFeatureType = + | 'heart_window' + | 'fissure_page' + | 'standard_nepal' + | 'first_meet' + | 'first_meet_ju' + | 'first_meet_rui' + | 'first_meet_train' export type NepalArtStyle = | 'none' diff --git a/frontend/src/components/FirstCallPage.vue b/frontend/src/components/FirstCallPage.vue new file mode 100644 index 0000000..79cc75a --- /dev/null +++ b/frontend/src/components/FirstCallPage.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/frontend/src/components/FirstMeetCampusPage.vue b/frontend/src/components/FirstMeetCampusPage.vue new file mode 100644 index 0000000..3ff3f5d --- /dev/null +++ b/frontend/src/components/FirstMeetCampusPage.vue @@ -0,0 +1,187 @@ + + + + + diff --git a/frontend/src/components/FirstMeetDmPage.vue b/frontend/src/components/FirstMeetDmPage.vue new file mode 100644 index 0000000..65f1614 --- /dev/null +++ b/frontend/src/components/FirstMeetDmPage.vue @@ -0,0 +1,342 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/FirstMeetTrainPage.vue b/frontend/src/components/FirstMeetTrainPage.vue new file mode 100644 index 0000000..b690221 --- /dev/null +++ b/frontend/src/components/FirstMeetTrainPage.vue @@ -0,0 +1,442 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/FissurePage.vue b/frontend/src/components/FissurePage.vue index 3fb35bf..0c1049c 100644 --- a/frontend/src/components/FissurePage.vue +++ b/frontend/src/components/FissurePage.vue @@ -2,14 +2,31 @@ import { usePointerSwipe, usePreferredReducedMotion } from '@vueuse/core' import { computed, ref } from 'vue' import type { Memory } from '../api/client' +import PolaroidCard from './PolaroidCard.vue' +import { renderMarkdown } from '../utils/renderMarkdown' -defineProps<{ +const props = defineProps<{ memory: Memory }>() const stageRef = ref(null) const split = ref(0) const reduceMotion = usePreferredReducedMotion() +const bodyHtml = computed(() => renderMarkdown(props.memory.main_text)) + +const hurtFacts = [ + '我把你和前任之间的事情告诉了别人', + '本该被保护的隐私,被我轻率地说了出去', + '于是别人开始对你评头论足', +] + +const judgmentClouds = [ + '那些不属于他们的评价', + '被误读、被讨论、被围观', + '我知道这件事最先伤到的是你', +] + +const apologyTags = ['越界了', '在认错', '也在解释'] usePointerSwipe(stageRef, { disableTextSelect: true, @@ -23,90 +40,349 @@ usePointerSwipe(stageRef, { }, }) -const rippleShift = computed(() => `${split.value * 14}px`) +const isOpen = computed(() => split.value > 0.35) function toggleSplit() { - split.value = split.value > 0.35 ? 0 : 0.85 + split.value = isOpen.value ? 0 : 0.85 } + +const splitHint = computed(() => + isOpen.value + ? '裂缝拉开后,露出那封迟来的长信。' + : '左右轻扫,把我没说完的道歉从裂缝里拉出来。', +) + +const fracturePulseClass = computed(() => { + if (reduceMotion.value === 'reduce' || isOpen.value) return '' + return 'fissure-fracture-pulse' +}) + + diff --git a/frontend/src/components/HeartMangaBackdrop.vue b/frontend/src/components/HeartMangaBackdrop.vue index 62abdd4..abdb867 100644 --- a/frontend/src/components/HeartMangaBackdrop.vue +++ b/frontend/src/components/HeartMangaBackdrop.vue @@ -1,25 +1,68 @@ diff --git a/frontend/src/components/HeartWindowPage.vue b/frontend/src/components/HeartWindowPage.vue index 9ec4f14..f60fe08 100644 --- a/frontend/src/components/HeartWindowPage.vue +++ b/frontend/src/components/HeartWindowPage.vue @@ -3,6 +3,7 @@ import { usePointerSwipe, usePreferredReducedMotion } from '@vueuse/core' import { computed, ref } from 'vue' import type { Memory } from '../api/client' import HeartMangaBackdrop from './HeartMangaBackdrop.vue' +import PolaroidCard from './PolaroidCard.vue' import { renderMarkdown } from '../utils/renderMarkdown' const props = defineProps<{ @@ -26,152 +27,229 @@ usePointerSwipe(stageRef, { }) const openDeg = computed(() => openAmount.value * 78) - const bodyHtml = computed(() => renderMarkdown(props.memory.main_text)) +const isOpen = computed(() => openAmount.value > 0.4) +const statusText = computed(() => + isOpen.value ? '心窗已经被推开,光会落进来。' : '如果不开心,就把这一页轻轻打开。', +) +const finaleTags = ['不开心时', '想说话时', '只想被陪着时'] function toggleDoors() { - openAmount.value = openAmount.value > 0.4 ? 0 : 0.92 + openAmount.value = isOpen.value ? 0 : 0.92 } diff --git a/frontend/src/components/HomeRentalPage.vue b/frontend/src/components/HomeRentalPage.vue new file mode 100644 index 0000000..84ad1f6 --- /dev/null +++ b/frontend/src/components/HomeRentalPage.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/frontend/src/components/IkeaDatePage.vue b/frontend/src/components/IkeaDatePage.vue new file mode 100644 index 0000000..26b61d6 --- /dev/null +++ b/frontend/src/components/IkeaDatePage.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/frontend/src/components/LuckinCoffeePage.vue b/frontend/src/components/LuckinCoffeePage.vue new file mode 100644 index 0000000..4be0364 --- /dev/null +++ b/frontend/src/components/LuckinCoffeePage.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/frontend/src/components/NepalPageContainer.vue b/frontend/src/components/NepalPageContainer.vue index fb2c764..5758b8f 100644 --- a/frontend/src/components/NepalPageContainer.vue +++ b/frontend/src/components/NepalPageContainer.vue @@ -1,14 +1,22 @@ + + + + diff --git a/frontend/src/utils/storyPageTransition.ts b/frontend/src/utils/storyPageTransition.ts index 35e01d3..29c62f8 100644 --- a/frontend/src/utils/storyPageTransition.ts +++ b/frontend/src/utils/storyPageTransition.ts @@ -42,6 +42,8 @@ export function getStoryTransitionName(m: Memory | null): StoryTransitionName { const t = m.unique_feature_type as MemoryFeatureType if (t === 'heart_window') return 'story-heart' if (t === 'fissure_page') return 'story-fissure' + if (t === 'first_meet' || t === 'first_meet_ju' || t === 'first_meet_rui') return 'story-night' + if (t === 'first_meet_train') return 'story-dawn' return transitionForStandardStyle(m.nepal_art_style) } @@ -57,6 +59,12 @@ export function getStoryShellClasses(m: Memory | null): string { if (t === 'fissure_page') { return 'border-fuchsia-300/30 shadow-[0_22px_65px_-24px_rgba(120,60,160,0.28)] ring-fuchsia-200/20' } + if (t === 'first_meet' || t === 'first_meet_ju' || t === 'first_meet_rui') { + return 'border-slate-400/35 shadow-[0_24px_70px_-22px_rgba(30,58,138,0.28)] ring-cyan-900/15' + } + if (t === 'first_meet_train') { + return 'border-amber-200/50 shadow-[0_24px_72px_-22px_rgba(234,88,12,0.24)] ring-amber-100/35' + } const shells: Partial> = { dawn_meet: 'border-amber-200/45 shadow-[0_22px_68px_-22px_rgba(217,119,6,0.22)] ring-amber-100/30', @@ -87,6 +95,10 @@ export function getStoryNavAccentClass(m: Memory | null): string { const t = m.unique_feature_type if (t === 'heart_window') return 'hover:border-rose-300/50 hover:bg-rose-50/80' if (t === 'fissure_page') return 'hover:border-fuchsia-300/45 hover:bg-fuchsia-50/70' + if (t === 'first_meet' || t === 'first_meet_ju' || t === 'first_meet_rui') { + return 'hover:border-slate-400/45 hover:bg-slate-100/85' + } + if (t === 'first_meet_train') return 'hover:border-amber-300/50 hover:bg-amber-50/80' const accents: Partial> = { dawn_meet: 'hover:border-amber-300/50 hover:bg-amber-50/80', blush_argue: 'hover:border-rose-300/45 hover:bg-rose-50/75',