feat: 优化人生轨迹详情页布局与顶部安全区
- 合并事件标题卡与描述卡为统一的 event-content-card - 移除顶部悬浮导航,避免与小程序原生胶囊按钮重叠 - 添加顶部安全区占位(参考 profile/index.vue 等页面) - 调整按钮布局:收藏、分析、分享、编辑、收起放入内容区域 - 底部只保留居中的'聊聊'主按钮 - 使用 Markdown 组件渲染事件描述 - 添加 scrollToAnalysis 滚动定位功能 - 更新 AGENTS.md 添加操作系统命令执行规则 - 更新 CLAUDE.md 添加操作系统命令执行规则
This commit is contained in:
@@ -2,61 +2,91 @@
|
||||
<view class="detail-page">
|
||||
<view class="space-bg"></view>
|
||||
|
||||
<view class="floating-nav" :style="floatingTopStyle">
|
||||
<view class="circle-btn" @click="goBack">
|
||||
<view class="back-icon"></view>
|
||||
</view>
|
||||
<view class="circle-btn" @click="openMore">
|
||||
<view class="more-dot"></view>
|
||||
<view class="more-dot"></view>
|
||||
<view class="more-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view
|
||||
class="content"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
:scroll-top="scrollTop"
|
||||
@scroll="onContentScroll"
|
||||
scroll-with-animation
|
||||
>
|
||||
<view class="top-safe" :style="{ height: capsuleTopReservePx + 'px' }"></view>
|
||||
<view class="event-content-card glass-card">
|
||||
<view class="event-hero-section">
|
||||
<view class="hero-timeline">
|
||||
<view class="timeline-glow"></view>
|
||||
<view class="timeline-node"></view>
|
||||
<view class="timeline-line"></view>
|
||||
</view>
|
||||
|
||||
<scroll-view class="content" scroll-y :show-scrollbar="false">
|
||||
<view class="hero-card glass-card">
|
||||
<view class="hero-timeline">
|
||||
<view class="timeline-glow"></view>
|
||||
<view class="timeline-node"></view>
|
||||
<view class="timeline-line"></view>
|
||||
<view class="hero-copy">
|
||||
<text class="year">{{ eventYear }}</text>
|
||||
<view class="date-row">
|
||||
<text class="date-range">{{ dateRange }}</text>
|
||||
<text class="type-chip">{{ primaryTag }}</text>
|
||||
</view>
|
||||
<text class="event-title">{{ displayEvent.title || '决定全职做自己热爱的AI产品' }}</text>
|
||||
<view class="location-row">
|
||||
<view class="pin-icon"></view>
|
||||
<text>{{ locationText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="memory-cover">
|
||||
<view class="cover-sky"></view>
|
||||
<view class="cover-window"></view>
|
||||
<view class="cover-desk"></view>
|
||||
<view class="cover-screen main"></view>
|
||||
<view class="cover-screen small left"></view>
|
||||
<view class="cover-screen small right"></view>
|
||||
</view>
|
||||
<view class="hero-star">★</view>
|
||||
</view>
|
||||
|
||||
<view class="hero-copy">
|
||||
<text class="year">{{ eventYear }}</text>
|
||||
<view class="date-row">
|
||||
<text class="date-range">{{ dateRange }}</text>
|
||||
<text class="type-chip">{{ primaryTag }}</text>
|
||||
<view class="event-body-section">
|
||||
<view class="event-body-toolbar">
|
||||
<view class="content-action favorite-action" @click="toggleFavorite">
|
||||
<view class="bookmark-icon" :class="{ active: isFavorite }"></view>
|
||||
<text>{{ isFavorite ? '已收藏' : '收藏' }}</text>
|
||||
</view>
|
||||
|
||||
<view class="toolbar-right">
|
||||
<view class="analysis-pill" @click="scrollToAnalysis">
|
||||
<text>分析</text>
|
||||
</view>
|
||||
<view class="content-action share-action" @click="shareCurrent">
|
||||
<view class="share-icon">
|
||||
<view></view>
|
||||
<view></view>
|
||||
<view></view>
|
||||
</view>
|
||||
<text>分享</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="event-title">{{ displayEvent.title || '决定全职做自己热爱的AI产品' }}</text>
|
||||
<view class="location-row">
|
||||
<view class="pin-icon"></view>
|
||||
<text>{{ locationText }}</text>
|
||||
|
||||
<view class="section-title-row description-title-row">
|
||||
<view class="orbit-icon"></view>
|
||||
<text>事件描述</text>
|
||||
</view>
|
||||
|
||||
<view class="description-markdown" :class="{ collapsed: isDescriptionCollapsed }">
|
||||
<Markdown :content="eventDescription" />
|
||||
</view>
|
||||
|
||||
<view class="event-body-footer">
|
||||
<view class="content-action edit-action" @click="editEvent">
|
||||
<view class="edit-icon"></view>
|
||||
<text>编辑</text>
|
||||
</view>
|
||||
<text class="collapse-action" @click="isDescriptionCollapsed = !isDescriptionCollapsed">
|
||||
{{ isDescriptionCollapsed ? '展开' : '收起' }} ^
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="memory-cover">
|
||||
<view class="cover-sky"></view>
|
||||
<view class="cover-window"></view>
|
||||
<view class="cover-desk"></view>
|
||||
<view class="cover-screen main"></view>
|
||||
<view class="cover-screen small left"></view>
|
||||
<view class="cover-screen small right"></view>
|
||||
</view>
|
||||
<view class="hero-star">★</view>
|
||||
</view>
|
||||
|
||||
<view class="description-card glass-card">
|
||||
<view class="section-title-row">
|
||||
<view class="orbit-icon"></view>
|
||||
<text>事件描述</text>
|
||||
</view>
|
||||
<text class="description-text" :class="{ collapsed: isDescriptionCollapsed }">{{ eventDescription }}</text>
|
||||
<text class="collapse-action" @click="isDescriptionCollapsed = !isDescriptionCollapsed">
|
||||
{{ isDescriptionCollapsed ? '展开' : '收起' }} ^
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="analysis-panel">
|
||||
<view class="analysis-panel" id="analysisPanel">
|
||||
<view class="section-title-row analysis-head">
|
||||
<text class="sparkle">✦</text>
|
||||
<text>AI 分析</text>
|
||||
@@ -90,25 +120,9 @@
|
||||
</scroll-view>
|
||||
|
||||
<view class="bottom-actions" :style="{ paddingBottom: bottomInset + 'px' }">
|
||||
<view class="bottom-action" @click="editEvent">
|
||||
<view class="edit-icon"></view>
|
||||
<text>编辑</text>
|
||||
</view>
|
||||
<view class="bottom-action" @click="toggleFavorite">
|
||||
<view class="bookmark-icon" :class="{ active: isFavorite }"></view>
|
||||
<text>{{ isFavorite ? '已收藏' : '收藏' }}</text>
|
||||
</view>
|
||||
<view class="chat-action" @click="chatEvent">
|
||||
<view class="chat-badge">AI</view>
|
||||
<text>和开开聊聊这段经历</text>
|
||||
</view>
|
||||
<view class="bottom-action" @click="shareCurrent">
|
||||
<view class="share-icon">
|
||||
<view></view>
|
||||
<view></view>
|
||||
<view></view>
|
||||
</view>
|
||||
<text>分享</text>
|
||||
<text>聊聊</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -118,17 +132,19 @@
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { useAppStore } from '../../stores/app.js'
|
||||
import Markdown from '../../components/Markdown.vue'
|
||||
import { useMenuButtonSafeArea } from '../../composables/useMenuButtonSafeArea.js'
|
||||
import analytics from '../../services/analytics.js'
|
||||
import { useMenuButtonSafeArea } from '../../composables/useMenuButtonSafeArea.js'
|
||||
|
||||
const store = useAppStore()
|
||||
const pagePath = '/pages/life-event/detail'
|
||||
const { floatingTopStyle } = useMenuButtonSafeArea()
|
||||
const { capsuleTopReservePx } = useMenuButtonSafeArea({ extraTopPx: 10 })
|
||||
const safeAreaBottom = ref(0)
|
||||
const eventId = ref('')
|
||||
const cachedEvent = ref(null)
|
||||
const isFavorite = ref(false)
|
||||
const isDescriptionCollapsed = ref(false)
|
||||
const scrollTop = ref(0)
|
||||
const currentScrollTop = ref(0)
|
||||
|
||||
onMounted(async () => {
|
||||
const info = uni.getWindowInfo()
|
||||
@@ -280,6 +296,20 @@ const analysisBlocks = computed(() => {
|
||||
]
|
||||
})
|
||||
|
||||
const onContentScroll = (event) => {
|
||||
currentScrollTop.value = event.detail?.scrollTop || 0
|
||||
}
|
||||
|
||||
const scrollToAnalysis = () => {
|
||||
uni.createSelectorQuery()
|
||||
.select('#analysisPanel')
|
||||
.boundingClientRect((rect) => {
|
||||
if (!rect) return
|
||||
scrollTop.value = Math.max(0, currentScrollTop.value + rect.top - 24)
|
||||
})
|
||||
.exec()
|
||||
}
|
||||
|
||||
const editEvent = () => {
|
||||
if (!eventId.value) return
|
||||
uni.navigateTo({ url: `/pages/life-event/form?id=${eventId.value}` })
|
||||
@@ -385,59 +415,18 @@ const goBack = () => {
|
||||
background-position: 44rpx 44rpx, 10rpx 112rpx;
|
||||
}
|
||||
|
||||
.floating-nav,
|
||||
.content,
|
||||
.bottom-actions {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.floating-nav {
|
||||
height: 108rpx;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 32rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.circle-btn {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5rpx;
|
||||
color: #fff;
|
||||
border: 1rpx solid rgba(118, 82, 225, 0.35);
|
||||
background: rgba(22, 18, 63, 0.72);
|
||||
box-shadow: inset 0 0 20rpx rgba(137, 91, 255, 0.1);
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 23rpx;
|
||||
height: 23rpx;
|
||||
border-left: 6rpx solid #fff;
|
||||
border-bottom: 6rpx solid #fff;
|
||||
transform: rotate(45deg);
|
||||
margin-left: 7rpx;
|
||||
}
|
||||
|
||||
.more-dot {
|
||||
width: 7rpx;
|
||||
height: 7rpx;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
min-height: 0;
|
||||
box-sizing: border-box;
|
||||
padding: 0 32rpx 134rpx;
|
||||
padding: 12rpx 32rpx 154rpx;
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
@@ -450,15 +439,24 @@ const goBack = () => {
|
||||
-webkit-backdrop-filter: blur(24rpx);
|
||||
}
|
||||
|
||||
.hero-card {
|
||||
.event-content-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 30rpx;
|
||||
}
|
||||
|
||||
.event-hero-section {
|
||||
position: relative;
|
||||
min-height: 264rpx;
|
||||
border-radius: 28rpx;
|
||||
display: grid;
|
||||
grid-template-columns: 70rpx 1fr 148rpx;
|
||||
gap: 20rpx;
|
||||
padding: 38rpx 34rpx 32rpx;
|
||||
padding: 40rpx 34rpx 34rpx;
|
||||
box-sizing: border-box;
|
||||
background:
|
||||
radial-gradient(circle at 86% 12%, rgba(129, 76, 255, 0.22), transparent 36%),
|
||||
linear-gradient(145deg, rgba(6, 8, 35, 0.98) 0%, rgba(15, 13, 58, 0.96) 58%, rgba(23, 16, 72, 0.92) 100%);
|
||||
box-shadow: inset 0 -1rpx 0 rgba(164, 116, 255, 0.22), inset 0 0 42rpx rgba(63, 45, 148, 0.16);
|
||||
}
|
||||
|
||||
.hero-timeline {
|
||||
@@ -633,7 +631,15 @@ const goBack = () => {
|
||||
text-shadow: 0 0 22rpx rgba(255, 198, 85, 0.9);
|
||||
}
|
||||
|
||||
.description-card,
|
||||
.event-body-section {
|
||||
position: relative;
|
||||
padding: 26rpx 30rpx 24rpx;
|
||||
box-sizing: border-box;
|
||||
background:
|
||||
radial-gradient(circle at 92% 0%, rgba(159, 98, 255, 0.12), transparent 36%),
|
||||
rgba(13, 16, 52, 0.62);
|
||||
}
|
||||
|
||||
.tags-card {
|
||||
border-radius: 28rpx;
|
||||
margin-top: 18rpx;
|
||||
@@ -641,6 +647,62 @@ const goBack = () => {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.event-body-toolbar,
|
||||
.event-body-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 18rpx;
|
||||
}
|
||||
|
||||
.event-body-toolbar {
|
||||
min-height: 64rpx;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
.content-action {
|
||||
min-height: 64rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10rpx;
|
||||
color: rgba(238, 231, 255, 0.88);
|
||||
font-size: 23rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.favorite-action,
|
||||
.share-action,
|
||||
.edit-action {
|
||||
padding: 0 4rpx;
|
||||
}
|
||||
|
||||
.analysis-pill {
|
||||
min-width: 92rpx;
|
||||
height: 54rpx;
|
||||
padding: 0 24rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 0 18rpx 18rpx 18rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgba(231, 217, 255, 0.86);
|
||||
font-size: 23rpx;
|
||||
font-weight: 900;
|
||||
background: rgba(157, 104, 255, 0.18);
|
||||
border: 1rpx solid rgba(193, 154, 255, 0.24);
|
||||
box-shadow: inset 0 0 18rpx rgba(185, 124, 255, 0.08);
|
||||
}
|
||||
|
||||
.description-title-row {
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.section-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -671,25 +733,24 @@ const goBack = () => {
|
||||
border-color: rgba(176, 112, 255, 0.62);
|
||||
}
|
||||
|
||||
.description-text {
|
||||
display: block;
|
||||
margin-top: 24rpx;
|
||||
color: rgba(227, 218, 246, 0.73);
|
||||
font-size: 25rpx;
|
||||
line-height: 1.75;
|
||||
.description-markdown {
|
||||
margin-top: 22rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.description-text.collapsed {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
.description-markdown.collapsed {
|
||||
max-height: 168rpx;
|
||||
}
|
||||
|
||||
.event-body-footer {
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.collapse-action {
|
||||
display: block;
|
||||
margin-top: 14rpx;
|
||||
text-align: right;
|
||||
min-height: 64rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
color: #a970ff;
|
||||
font-size: 24rpx;
|
||||
font-weight: 800;
|
||||
@@ -830,10 +891,9 @@ const goBack = () => {
|
||||
bottom: 0;
|
||||
min-height: 108rpx;
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
grid-template-columns: 0.76fr 0.76fr 2fr 0.76fr;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
justify-content: center;
|
||||
padding: 14rpx 32rpx 0;
|
||||
border-radius: 36rpx 36rpx 0 0;
|
||||
border-top: 1rpx solid rgba(126, 87, 255, 0.32);
|
||||
@@ -843,25 +903,17 @@ const goBack = () => {
|
||||
-webkit-backdrop-filter: blur(28rpx);
|
||||
}
|
||||
|
||||
.bottom-action,
|
||||
.chat-action {
|
||||
width: min(420rpx, 100%);
|
||||
height: 72rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10rpx;
|
||||
color: rgba(238, 231, 255, 0.9);
|
||||
font-size: 24rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.bottom-action {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.chat-action {
|
||||
gap: 12rpx;
|
||||
border-radius: 999rpx;
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
font-weight: 900;
|
||||
background: linear-gradient(135deg, #b246ff, #742fff 58%, #5126ff);
|
||||
box-shadow: 0 0 28rpx rgba(168, 85, 247, 0.58), inset 0 1rpx 0 rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user