feat: 优化小程序人生事件详情、表单页及人生剧本页面样式

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 22:52:26 +08:00
parent f62ae880be
commit eb83384623
4 changed files with 127 additions and 324 deletions
@@ -1,26 +1,6 @@
<template> <template>
<view class="detail-page"> <view class="detail-page">
<view class="space-bg"></view> <view class="space-bg"></view>
<view class="status-safe" :style="{ height: safeAreaTop + 'px' }"></view>
<view class="top-bar">
<text class="clock-text">{{ currentTime }}</text>
<view class="route-pill">
<view class="shield-icon"></view>
<text class="route-text">行程中</text>
<text class="score-text">{{ eventScore }}</text>
</view>
<view class="signal-group">
<view class="signal-bars">
<view></view>
<view></view>
<view></view>
<view></view>
</view>
<text class="network-text">5GA</text>
<view class="battery-box"><text>{{ batteryLevel }}</text></view>
</view>
</view>
<view class="floating-nav"> <view class="floating-nav">
<view class="circle-btn" @click="goBack"> <view class="circle-btn" @click="goBack">
@@ -140,10 +120,7 @@ import { useAppStore } from '../../stores/app.js'
import Markdown from '../../components/Markdown.vue' import Markdown from '../../components/Markdown.vue'
const store = useAppStore() const store = useAppStore()
const safeAreaTop = ref(16)
const safeAreaBottom = ref(0) const safeAreaBottom = ref(0)
const currentTime = ref('21:10')
const batteryLevel = ref('41')
const eventId = ref('') const eventId = ref('')
const cachedEvent = ref(null) const cachedEvent = ref(null)
const isFavorite = ref(false) const isFavorite = ref(false)
@@ -151,10 +128,7 @@ const isDescriptionCollapsed = ref(false)
onMounted(async () => { onMounted(async () => {
const info = uni.getWindowInfo() const info = uni.getWindowInfo()
safeAreaTop.value = info.safeAreaInsets?.top || info.statusBarHeight || 16
safeAreaBottom.value = info.safeAreaInsets?.bottom || 0 safeAreaBottom.value = info.safeAreaInsets?.bottom || 0
const now = new Date()
currentTime.value = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
const pages = getCurrentPages() const pages = getCurrentPages()
eventId.value = pages[pages.length - 1]?.options?.id || '' eventId.value = pages[pages.length - 1]?.options?.id || ''
cachedEvent.value = uni.getStorageSync('current_life_event') || null cachedEvent.value = uni.getStorageSync('current_life_event') || null
@@ -236,11 +210,6 @@ const locationText = computed(() => {
return profile.value.city ? `中国 · ${profile.value.city}` : '中国 · 深圳' return profile.value.city ? `中国 · ${profile.value.city}` : '中国 · 深圳'
}) })
const eventScore = computed(() => {
const raw = Number(displayEvent.value.emotionScore || 18)
return Math.max(1, Math.min(99, Math.round(raw)))
})
const eventDescription = computed(() => { const eventDescription = computed(() => {
return displayEvent.value.content || displayEvent.value.description || '经过了长时间的纠结和准备,我终于辞去了稳定的工作,全职投入到自己热爱的AI产品创业中。这是我人生中最大的一次冒险,也是我第一次真正为自己而活。' return displayEvent.value.content || displayEvent.value.description || '经过了长时间的纠结和准备,我终于辞去了稳定的工作,全职投入到自己热爱的AI产品创业中。这是我人生中最大的一次冒险,也是我第一次真正为自己而活。'
}) })
@@ -396,8 +365,6 @@ const goBack = () => {
background-position: 44rpx 44rpx, 10rpx 112rpx; background-position: 44rpx 44rpx, 10rpx 112rpx;
} }
.status-safe,
.top-bar,
.floating-nav, .floating-nav,
.content, .content,
.bottom-actions { .bottom-actions {
@@ -405,127 +372,6 @@ const goBack = () => {
z-index: 1; z-index: 1;
} }
.status-safe {
flex-shrink: 0;
}
.top-bar {
height: 74rpx;
flex-shrink: 0;
display: grid;
grid-template-columns: 140rpx 1fr 178rpx;
align-items: center;
padding: 0 32rpx;
box-sizing: border-box;
}
.clock-text {
color: #fff;
font-size: 30rpx;
font-weight: 700;
letter-spacing: 0;
}
.route-pill {
justify-self: center;
width: 356rpx;
height: 62rpx;
border-radius: 999rpx;
display: grid;
grid-template-columns: 30rpx 1fr auto;
align-items: center;
gap: 12rpx;
padding: 0 24rpx;
box-sizing: border-box;
color: #fff;
background: rgba(0, 0, 0, 0.76);
border: 3rpx solid rgba(255, 255, 255, 0.22);
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.42);
}
.route-text,
.score-text {
font-size: 23rpx;
line-height: 1;
}
.route-text {
font-weight: 800;
}
.score-text {
color: rgba(255, 255, 255, 0.82);
text-align: right;
}
.shield-icon {
position: relative;
width: 25rpx;
height: 27rpx;
border: 4rpx solid #fff;
border-radius: 10rpx 10rpx 13rpx 13rpx;
box-sizing: border-box;
}
.shield-icon::after {
content: '';
position: absolute;
left: 6rpx;
top: 4rpx;
width: 7rpx;
height: 10rpx;
border-right: 3rpx solid #fff;
border-bottom: 3rpx solid #fff;
transform: rotate(42deg);
}
.signal-group {
justify-self: end;
display: flex;
align-items: center;
gap: 10rpx;
}
.signal-bars {
height: 31rpx;
display: flex;
align-items: flex-end;
gap: 5rpx;
}
.signal-bars view {
width: 8rpx;
border-radius: 6rpx;
background: #fff;
}
.signal-bars view:nth-child(1) { height: 12rpx; }
.signal-bars view:nth-child(2) { height: 18rpx; }
.signal-bars view:nth-child(3) { height: 24rpx; }
.signal-bars view:nth-child(4) { height: 31rpx; }
.network-text {
color: #fff;
font-size: 26rpx;
font-weight: 800;
}
.battery-box {
min-width: 52rpx;
height: 35rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
color: #171327;
background: rgba(255, 255, 255, 0.78);
}
.battery-box text {
font-size: 24rpx;
font-weight: 900;
}
.floating-nav { .floating-nav {
height: 108rpx; height: 108rpx;
flex-shrink: 0; flex-shrink: 0;
+7 -143
View File
@@ -1,26 +1,7 @@
<template> <template>
<view class="record-page"> <view class="record-page">
<view class="space-bg"></view> <view class="space-bg"></view>
<view class="status-safe" :style="{ height: safeAreaTop + 'px' }"></view> <view class="safe-top" :style="{ height: safeAreaTop + 10 + 'px' }"></view>
<view class="top-bar">
<text class="clock-text">{{ currentTime }}</text>
<view class="route-pill">
<view class="shield-icon"></view>
<text class="route-text">行程中</text>
<text class="score-text">18</text>
</view>
<view class="signal-group">
<view class="signal-bars">
<view></view>
<view></view>
<view></view>
<view></view>
</view>
<text class="network-text">5GA</text>
<view class="battery-box"><text>41</text></view>
</view>
</view>
<view class="header"> <view class="header">
<view class="back-hit" @click="goBack"><view class="back-icon"></view></view> <view class="back-hit" @click="goBack"><view class="back-icon"></view></view>
@@ -188,8 +169,7 @@ import { computed, onMounted, reactive, ref } from 'vue'
import { useAppStore } from '../../stores/app.js' import { useAppStore } from '../../stores/app.js'
const store = useAppStore() const store = useAppStore()
const safeAreaTop = ref(16) const safeAreaTop = ref(20)
const currentTime = ref('21:10')
const saving = ref(false) const saving = ref(false)
const currentYear = new Date().getFullYear() const currentYear = new Date().getFullYear()
@@ -244,9 +224,7 @@ const seasonPickerValue = computed(() => {
onMounted(() => { onMounted(() => {
const info = uni.getWindowInfo() const info = uni.getWindowInfo()
safeAreaTop.value = info.safeAreaInsets?.top || info.statusBarHeight || 16 safeAreaTop.value = info.safeAreaInsets?.top || info.statusBarHeight || 20
const now = new Date()
currentTime.value = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
const pages = getCurrentPages() const pages = getCurrentPages()
const id = pages[pages.length - 1]?.options?.id || '' const id = pages[pages.length - 1]?.options?.id || ''
if (id) loadEvent(id) if (id) loadEvent(id)
@@ -438,139 +416,25 @@ const goBack = () => {
background-position: 44rpx 44rpx, 10rpx 112rpx; background-position: 44rpx 44rpx, 10rpx 112rpx;
} }
.status-safe, .safe-top,
.top-bar,
.header, .header,
.content { .content {
position: relative; position: relative;
z-index: 1; z-index: 1;
} }
.status-safe, .safe-top,
.top-bar,
.header { .header {
flex-shrink: 0; flex-shrink: 0;
} }
.top-bar {
height: 74rpx;
display: grid;
grid-template-columns: 140rpx 1fr 178rpx;
align-items: center;
padding: 0 32rpx;
box-sizing: border-box;
}
.clock-text {
color: #fff;
font-size: 30rpx;
font-weight: 700;
}
.route-pill {
justify-self: center;
width: 356rpx;
height: 62rpx;
border-radius: 999rpx;
display: grid;
grid-template-columns: 30rpx 1fr auto;
align-items: center;
gap: 12rpx;
padding: 0 24rpx;
box-sizing: border-box;
background: rgba(0, 0, 0, 0.76);
border: 3rpx solid rgba(255, 255, 255, 0.22);
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.42);
}
.route-text,
.score-text {
font-size: 23rpx;
}
.route-text {
font-weight: 800;
}
.score-text {
color: rgba(255, 255, 255, 0.82);
}
.shield-icon {
position: relative;
width: 25rpx;
height: 27rpx;
border: 4rpx solid #fff;
border-radius: 10rpx 10rpx 13rpx 13rpx;
box-sizing: border-box;
}
.shield-icon::after {
content: '';
position: absolute;
left: 6rpx;
top: 4rpx;
width: 7rpx;
height: 10rpx;
border-right: 3rpx solid #fff;
border-bottom: 3rpx solid #fff;
transform: rotate(42deg);
}
.signal-group {
justify-self: end;
display: flex;
align-items: center;
gap: 10rpx;
}
.signal-bars {
height: 31rpx;
display: flex;
align-items: flex-end;
gap: 5rpx;
}
.signal-bars view {
width: 8rpx;
border-radius: 6rpx;
background: #fff;
}
.signal-bars view:nth-child(1) { height: 12rpx; }
.signal-bars view:nth-child(2) { height: 18rpx; }
.signal-bars view:nth-child(3) { height: 24rpx; }
.signal-bars view:nth-child(4) { height: 31rpx; }
.network-text {
color: #fff;
font-size: 26rpx;
font-weight: 800;
}
.battery-box {
min-width: 52rpx;
height: 35rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
color: #171327;
background: rgba(255, 255, 255, 0.78);
}
.battery-box text {
font-size: 24rpx;
font-weight: 900;
}
.header { .header {
min-height: 178rpx; min-height: 154rpx;
display: grid; display: grid;
grid-template-columns: 90rpx 1fr 90rpx; grid-template-columns: 90rpx 1fr 90rpx;
grid-template-rows: auto auto; grid-template-rows: auto auto;
align-items: center; align-items: center;
padding: 20rpx 32rpx 28rpx; padding: 4rpx 32rpx 24rpx;
box-sizing: border-box; box-sizing: border-box;
} }
@@ -353,8 +353,14 @@ onMounted(async () => {
.primary-btn { .primary-btn {
height: 82rpx; height: 82rpx;
border-radius: 999rpx; border-radius: 999rpx;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx; font-size: 26rpx;
font-weight: 800; font-weight: 800;
line-height: 1;
text-align: center;
} }
.secondary-btn { .secondary-btn {
+114 -27
View File
@@ -70,7 +70,8 @@
</view> </view>
<button class="generate-btn kos-primary" :disabled="generating || !prompt.trim()" :loading="generating" @click="generateByPrompt"> <button class="generate-btn kos-primary" :disabled="generating || !prompt.trim()" :loading="generating" @click="generateByPrompt">
<view> <view class="button-stars"></view>
<view class="button-copy">
<text class="generate-title"> 生成我的人生剧本</text> <text class="generate-title"> 生成我的人生剧本</text>
<text class="generate-sub">今日剩余生成次数{{ remainingCount }}</text> <text class="generate-sub">今日剩余生成次数{{ remainingCount }}</text>
</view> </view>
@@ -86,7 +87,7 @@
<view v-else-if="mode === 'custom'" class="custom-panel kos-card"> <view v-else-if="mode === 'custom'" class="custom-panel kos-card">
<view class="panel-title-row"> <view class="panel-title-row">
<text class="spark"></text> <text class="title-spark"></text>
<text class="panel-title">定制你的平行人生</text> <text class="panel-title">定制你的平行人生</text>
</view> </view>
<view class="field"> <view class="field">
@@ -112,7 +113,17 @@
<textarea class="field-textarea" v-model="custom.npc" placeholder="写下一个人、一个阻碍,或一个即将出现的机会" placeholder-class="placeholder" /> <textarea class="field-textarea" v-model="custom.npc" placeholder="写下一个人、一个阻碍,或一个即将出现的机会" placeholder-class="placeholder" />
</view> </view>
<button class="primary-action kos-primary" :disabled="generating || !custom.theme.trim()" :loading="generating" @click="generateCustom"> <button class="primary-action kos-primary" :disabled="generating || !custom.theme.trim()" :loading="generating" @click="generateCustom">
{{ generating ? '正在生成' : '生成平行人生剧本' }} <view class="button-stars"></view>
<view class="button-copy single">
<text>{{ generating ? '正在生成' : '生成平行人生剧本' }}</text>
</view>
<view class="planet-badge">
<view class="planet-face">
<view class="planet-eye left"></view>
<view class="planet-eye right"></view>
<view class="planet-mouth"></view>
</view>
</view>
</button> </button>
</view> </view>
@@ -348,11 +359,19 @@ const handleVoiceInput = () => {
} }
.star, .star,
.spark { .title-spark {
color: #ffd58c; color: #ffd58c;
text-shadow: 0 0 20rpx rgba(255, 202, 125, 0.48); text-shadow: 0 0 20rpx rgba(255, 202, 125, 0.48);
} }
.title-spark {
flex: none;
width: 26rpx;
font-size: 25rpx;
line-height: 1;
text-align: center;
}
.subtitle { .subtitle {
display: block; display: block;
margin-top: 14rpx; margin-top: 14rpx;
@@ -668,20 +687,84 @@ const handleVoiceInput = () => {
font-size: 22rpx; font-size: 22rpx;
} }
.generate-btn { .generate-btn,
.primary-action {
position: relative;
width: 100%; width: 100%;
height: 104rpx; height: 104rpx;
margin-top: 24rpx;
border-radius: 28rpx; border-radius: 28rpx;
padding: 0;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 28rpx; overflow: hidden;
overflow: visible; color: #fff;
background: background:
radial-gradient(circle at 92% 44%, rgba(244, 187, 255, 0.42), transparent 22%), radial-gradient(circle at 92% 52%, rgba(255, 200, 255, 0.72) 0 10%, transparent 23%),
linear-gradient(135deg, #b64cff 0%, #762fff 48%, #4a20ff 100%); radial-gradient(circle at 4% 34%, rgba(255, 255, 255, 0.22), transparent 22%),
box-shadow: 0 0 34rpx rgba(168, 85, 247, 0.58), inset 0 1rpx 0 rgba(255, 255, 255, 0.25); linear-gradient(100deg, #a942ff 0%, #8c3cff 28%, #6a35ff 58%, #351eff 100%);
box-shadow:
0 0 34rpx rgba(164, 69, 255, 0.64),
0 14rpx 34rpx rgba(78, 39, 255, 0.34),
inset 0 2rpx 0 rgba(255, 255, 255, 0.36),
inset 0 -14rpx 24rpx rgba(55, 25, 203, 0.2);
}
.generate-btn {
margin-top: 24rpx;
}
.generate-btn::after,
.primary-action::after {
border: 0;
}
.generate-btn[disabled],
.primary-action[disabled] {
color: #fff;
opacity: 1;
background:
radial-gradient(circle at 92% 52%, rgba(255, 200, 255, 0.72) 0 10%, transparent 23%),
radial-gradient(circle at 4% 34%, rgba(255, 255, 255, 0.22), transparent 22%),
linear-gradient(100deg, #a942ff 0%, #8c3cff 28%, #6a35ff 58%, #351eff 100%);
}
.button-stars {
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0.82;
background-image:
radial-gradient(circle, rgba(255, 230, 160, 0.9) 0 2rpx, transparent 3rpx),
radial-gradient(circle, rgba(255, 255, 255, 0.82) 0 1rpx, transparent 2rpx),
radial-gradient(circle, rgba(255, 225, 151, 0.82) 0 1rpx, transparent 2rpx);
background-size: 126rpx 76rpx, 92rpx 58rpx, 170rpx 82rpx;
background-position: 62rpx 18rpx, 168rpx 42rpx, 264rpx 14rpx;
}
.button-copy {
position: absolute;
left: 28rpx;
right: 126rpx;
top: 50%;
z-index: 2;
transform: translateY(-50%);
text-align: center;
line-height: 1;
}
.button-copy.single {
left: 34rpx;
right: 118rpx;
}
.button-copy.single text {
display: block;
color: #fff;
font-size: 28rpx;
font-weight: 900;
line-height: 1;
text-align: center;
} }
.generate-title, .generate-title,
@@ -691,8 +774,10 @@ const handleVoiceInput = () => {
} }
.generate-title { .generate-title {
color: #fff;
font-size: 28rpx; font-size: 28rpx;
font-weight: 900; font-weight: 900;
line-height: 1.1;
} }
.generate-sub { .generate-sub {
@@ -702,23 +787,30 @@ const handleVoiceInput = () => {
} }
.planet-badge { .planet-badge {
position: relative; position: absolute;
width: 78rpx; right: 10rpx;
height: 78rpx; top: 11rpx;
z-index: 2;
width: 82rpx;
height: 82rpx;
border-radius: 50%; border-radius: 50%;
background: radial-gradient(circle at 36% 28%, #f0c7ff, #a64cff 62%, #5a22d6); background:
box-shadow: 0 0 30rpx rgba(219, 143, 255, 0.82); radial-gradient(circle at 35% 27%, #ffd9ff 0 9%, transparent 10%),
flex-shrink: 0; radial-gradient(circle at 64% 68%, #8d33ff 0 12%, transparent 28%),
radial-gradient(circle at 42% 36%, #f0b5ff 0%, #b557ff 45%, #7431f2 72%, #5120d5 100%);
box-shadow:
0 0 24rpx rgba(237, 177, 255, 0.92),
0 0 44rpx rgba(153, 68, 255, 0.7);
} }
.planet-badge::after { .planet-badge::after {
content: ''; content: '';
position: absolute; position: absolute;
left: -20rpx; left: -18rpx;
top: 34rpx; top: 36rpx;
width: 124rpx; width: 122rpx;
height: 24rpx; height: 25rpx;
border: 4rpx solid rgba(231, 201, 255, 0.72); border: 4rpx solid rgba(223, 197, 255, 0.78);
border-top-color: transparent; border-top-color: transparent;
border-radius: 50%; border-radius: 50%;
transform: rotate(-18deg); transform: rotate(-18deg);
@@ -797,12 +889,7 @@ const handleVoiceInput = () => {
} }
.primary-action { .primary-action {
width: 100%;
height: 86rpx;
margin-top: 28rpx; margin-top: 28rpx;
border-radius: 999rpx;
font-size: 28rpx;
font-weight: 800;
} }
.section-title { .section-title {