feat: 优化管理后台页面UI、修复TS编译错误、新增人生事件模块
- 优化 AI 配置列表页面:重构统计卡片、搜索表单、表格列展示 - 修复 3 处 TypeScript TS6133 编译错误,恢复构建 - 新增管理员修改密码和重置密码功能 - 优化小程序多个页面样式和交互 - 人生事件模块完善 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
# 开发环境配置(本地开发调试)
|
||||
VITE_APP_ENV=dev
|
||||
# 本地环境
|
||||
VITE_API_BASE_URL=http://localhost:19089/api
|
||||
VITE_WS_URL=ws://localhost:19089/ws
|
||||
# VITE_API_BASE_URL=http://localhost:19089/api
|
||||
# VITE_WS_URL=ws://localhost:19089/ws
|
||||
# 直连后端服务(不经过 Nginx)
|
||||
VITE_API_BASE_URL=http://101.200.208.45:19089/api
|
||||
VITE_WS_URL=ws://101.200.208.45:19089/ws
|
||||
# 测试环境
|
||||
# VITE_API_BASE_URL=https://lifescript.happylifeos.com/api
|
||||
# VITE_WS_URL=wss://lifescript.happylifeos.com/ws
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# 测试环境配置(小程序体验版)
|
||||
VITE_APP_ENV=test
|
||||
VITE_API_BASE_URL=https://lifescript.happylifeos.com/api
|
||||
VITE_WS_URL=wss://lifescript.happylifeos.com/ws
|
||||
# 直连后端服务(不经过 Nginx)
|
||||
VITE_API_BASE_URL=http://101.200.208.45:19089/api
|
||||
VITE_WS_URL=ws://101.200.208.45:19089/ws
|
||||
VITE_DEBUG=true
|
||||
|
||||
@@ -4,15 +4,23 @@
|
||||
<!-- 分割线 -->
|
||||
<view v-if="block.type === 'hr'" class="markdown-hr"></view>
|
||||
|
||||
<text v-else-if="block.type === 'h1'" class="markdown-h1">{{ block.content }}</text>
|
||||
|
||||
<text v-else-if="block.type === 'h2'" class="markdown-h2">{{ block.content }}</text>
|
||||
|
||||
<!-- 三级标题 -->
|
||||
<text v-else-if="block.type === 'h3'" class="markdown-h3">{{ block.content }}</text>
|
||||
|
||||
<!-- 四级标题 -->
|
||||
<text v-else-if="block.type === 'h4'" class="markdown-h4">{{ block.content }}</text>
|
||||
|
||||
<view v-else-if="block.type === 'blockquote'" class="markdown-quote">
|
||||
<text>{{ block.content }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 列表项 -->
|
||||
<view v-else-if="block.type === 'li'" class="markdown-li">
|
||||
<text class="li-bullet">• </text>
|
||||
<text class="li-bullet">{{ block.marker }}</text>
|
||||
<text class="li-content">{{ block.content }}</text>
|
||||
</view>
|
||||
|
||||
@@ -53,6 +61,18 @@ const parsedBlocks = computed(() => {
|
||||
continue
|
||||
}
|
||||
|
||||
const h1Match = trimmed.match(/^#\s+(.+)/)
|
||||
if (h1Match) {
|
||||
blocks.push({ type: 'h1', content: h1Match[1] })
|
||||
continue
|
||||
}
|
||||
|
||||
const h2Match = trimmed.match(/^##\s+(.+)/)
|
||||
if (h2Match) {
|
||||
blocks.push({ type: 'h2', content: h2Match[1] })
|
||||
continue
|
||||
}
|
||||
|
||||
// 三级标题 ###
|
||||
const h3Match = trimmed.match(/^###\s+(.+)/)
|
||||
if (h3Match) {
|
||||
@@ -67,10 +87,16 @@ const parsedBlocks = computed(() => {
|
||||
continue
|
||||
}
|
||||
|
||||
// 列表项 * 或 -
|
||||
const liMatch = trimmed.match(/^[*\-]\s+(.+)/)
|
||||
const quoteMatch = trimmed.match(/^>\s+(.+)/)
|
||||
if (quoteMatch) {
|
||||
blocks.push({ type: 'blockquote', content: quoteMatch[1] })
|
||||
continue
|
||||
}
|
||||
|
||||
// 列表项 *、-、+ 或 1.
|
||||
const liMatch = trimmed.match(/^([*\-+]|\d+\.)\s+(.+)/)
|
||||
if (liMatch) {
|
||||
blocks.push({ type: 'li', content: liMatch[1] })
|
||||
blocks.push({ type: 'li', marker: /^\d+\./.test(liMatch[1]) ? liMatch[1] : '•', content: liMatch[2] })
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -117,7 +143,7 @@ const parseBoldText = (text) => {
|
||||
.markdown-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
@@ -133,24 +159,52 @@ const parseBoldText = (text) => {
|
||||
}
|
||||
|
||||
/* 标题 */
|
||||
.markdown-h1,
|
||||
.markdown-h2,
|
||||
.markdown-h3 {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: rgba(243, 232, 255, 0.95);
|
||||
margin: 20rpx 0 12rpx 0;
|
||||
color: rgba(248, 244, 255, 0.96);
|
||||
line-height: 1.4;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.markdown-h1 {
|
||||
font-size: 32rpx;
|
||||
margin: 4rpx 0 10rpx 0;
|
||||
}
|
||||
|
||||
.markdown-h2 {
|
||||
font-size: 30rpx;
|
||||
margin: 14rpx 0 8rpx 0;
|
||||
}
|
||||
|
||||
.markdown-h3 {
|
||||
font-size: 28rpx;
|
||||
margin: 12rpx 0 6rpx 0;
|
||||
}
|
||||
|
||||
.markdown-h4 {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
font-size: 26rpx;
|
||||
font-weight: 800;
|
||||
color: rgba(243, 232, 255, 0.95);
|
||||
margin: 16rpx 0 8rpx 0;
|
||||
margin: 10rpx 0 4rpx 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.markdown-quote {
|
||||
padding: 16rpx 18rpx;
|
||||
border-left: 5rpx solid rgba(180, 108, 255, 0.8);
|
||||
border-radius: 14rpx;
|
||||
color: rgba(218, 202, 246, 0.82);
|
||||
background: rgba(168, 85, 247, 0.1);
|
||||
}
|
||||
|
||||
.markdown-quote text {
|
||||
font-size: 24rpx;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
/* 列表项 */
|
||||
.markdown-li {
|
||||
display: flex;
|
||||
@@ -164,6 +218,7 @@ const parseBoldText = (text) => {
|
||||
color: #C084FC;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.5;
|
||||
min-width: 28rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
const isPlaying = ref(false)
|
||||
const bottomPosition = ref('180rpx')
|
||||
const audioContext = null
|
||||
let audioInstance = null
|
||||
|
||||
// 背景音乐 URL - 使用原型中的音乐
|
||||
@@ -29,9 +28,18 @@ const initAudio = () => {
|
||||
audioInstance.loop = true
|
||||
audioInstance.autoplay = false
|
||||
|
||||
audioInstance.onPlay(() => {
|
||||
isPlaying.value = true
|
||||
})
|
||||
|
||||
audioInstance.onPause(() => {
|
||||
isPlaying.value = false
|
||||
})
|
||||
|
||||
audioInstance.onError((err) => {
|
||||
console.error('音乐播放失败:', err)
|
||||
isPlaying.value = false
|
||||
uni.showToast({ title: '音乐资源暂不可用', icon: 'none' })
|
||||
})
|
||||
|
||||
audioInstance.onEnded(() => {
|
||||
@@ -47,8 +55,7 @@ const toggleMusic = async () => {
|
||||
audioInstance.pause()
|
||||
} else {
|
||||
try {
|
||||
await audioInstance.play()
|
||||
isPlaying.value = true
|
||||
audioInstance.play()
|
||||
} catch (err) {
|
||||
console.error('音乐播放失败:', err)
|
||||
isPlaying.value = false
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,248 +1,821 @@
|
||||
<template>
|
||||
<view class="mine-view">
|
||||
<view class="profile-card kos-card">
|
||||
<view class="avatar">{{ avatarText }}</view>
|
||||
<view class="profile-main">
|
||||
<view class="name-row">
|
||||
<text class="name">{{ profile.nickname || 'Zoey' }}</text>
|
||||
<text class="star">✦</text>
|
||||
<view class="script-library">
|
||||
<view class="page-head">
|
||||
<view class="title-row">
|
||||
<text class="spark">✦</text>
|
||||
<text class="page-title">我的剧本</text>
|
||||
</view>
|
||||
<view class="head-actions">
|
||||
<view class="circle-btn" @click="openSearch">
|
||||
<view class="search-icon"></view>
|
||||
</view>
|
||||
<text class="signature">{{ signature }}</text>
|
||||
<view class="chips">
|
||||
<text v-for="chip in chips" :key="chip" class="chip kos-pill">{{ chip }}</text>
|
||||
<view class="circle-btn" @click="openMoreMenu">
|
||||
<view class="more-icon">
|
||||
<view></view>
|
||||
<view></view>
|
||||
<view></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="edit-btn kos-pill" @click="editProfile">编辑资料</view>
|
||||
</view>
|
||||
|
||||
<view class="stats-grid">
|
||||
<view class="stat-card kos-card">
|
||||
<text class="stat-value">{{ eventsCount }}</text>
|
||||
<text class="stat-label">人生记录</text>
|
||||
</view>
|
||||
<view class="stat-card kos-card">
|
||||
<text class="stat-value">{{ scriptsCount }}</text>
|
||||
<text class="stat-label">生成剧本</text>
|
||||
<view class="type-tabs">
|
||||
<text
|
||||
v-for="tab in typeTabs"
|
||||
:key="tab.value"
|
||||
class="type-tab"
|
||||
:class="{ active: activeType === tab.value }"
|
||||
@click="activeType = tab.value"
|
||||
>{{ tab.label }}</text>
|
||||
<view class="new-script" @click="createScript">
|
||||
<text class="plus">+</text>
|
||||
<text>新建剧本</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-card kos-card">
|
||||
<view class="section-title">兴趣爱好</view>
|
||||
<view class="tag-cloud">
|
||||
<text v-for="tag in hobbyTags" :key="tag" class="tag kos-pill">{{ tag }}</text>
|
||||
<view class="filter-bar">
|
||||
<scroll-view class="status-scroll" scroll-x :show-scrollbar="false">
|
||||
<view class="status-row">
|
||||
<text
|
||||
v-for="filter in statusFilters"
|
||||
:key="filter.value"
|
||||
class="status-chip"
|
||||
:class="{ active: activeStatus === filter.value }"
|
||||
@click="activeStatus = filter.value"
|
||||
>{{ filter.label }}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="sort-tools">
|
||||
<text class="sort-text" @click="toggleSort">{{ sortLabel }}</text>
|
||||
<view class="grid-icon" :class="{ active: viewMode === 'grid' }" @click="toggleViewMode">
|
||||
<view v-for="i in 4" :key="i"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-card kos-card">
|
||||
<view class="section-title">生命摘要</view>
|
||||
<view class="memory-line">
|
||||
<text class="memory-label">童年</text>
|
||||
<text class="memory-text">{{ profile.childhood?.text || '还没有写下最早的光。' }}</text>
|
||||
</view>
|
||||
<view class="memory-line">
|
||||
<text class="memory-label">高光</text>
|
||||
<text class="memory-text">{{ profile.joy?.text || '等待记录一次会发光的瞬间。' }}</text>
|
||||
</view>
|
||||
<view class="memory-line">
|
||||
<text class="memory-label">未来</text>
|
||||
<text class="memory-text">{{ profile.future?.vision || '未来档案还在生成中。' }}</text>
|
||||
<view v-if="visibleScripts.length" class="script-list" :class="{ grid: viewMode === 'grid' }">
|
||||
<view
|
||||
v-for="(script, index) in visibleScripts"
|
||||
:key="script.id || index"
|
||||
class="script-card"
|
||||
@click="viewScript(script)"
|
||||
>
|
||||
<view class="cover" :class="'cover-' + (index % 6)">
|
||||
<text>{{ getInitial(script) }}</text>
|
||||
</view>
|
||||
<view class="card-main">
|
||||
<view class="card-top">
|
||||
<view class="title-wrap">
|
||||
<text class="script-title">{{ script.title }}</text>
|
||||
<text class="length-badge">{{ getLengthLabel(script.length) }}</text>
|
||||
</view>
|
||||
<view class="right-state">
|
||||
<text class="state-pill" :class="'state-' + getStatus(script)">{{ getStatusLabel(script) }}</text>
|
||||
<text class="ellipsis" @click.stop="openScriptMenu(script)">•••</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="tag-row">
|
||||
<text v-for="tag in getTags(script)" :key="tag" class="tag">{{ tag }}</text>
|
||||
</view>
|
||||
|
||||
<text class="summary">{{ script.summary || script.content || '一段正在生成中的平行人生剧本。' }}</text>
|
||||
|
||||
<view class="meta-row">
|
||||
<text>{{ getChapterCount(script) }}章</text>
|
||||
<text>|</text>
|
||||
<text>{{ getWordCount(script) }}</text>
|
||||
<text>|</text>
|
||||
<text>{{ getDateText(script) }}</text>
|
||||
</view>
|
||||
|
||||
<view v-if="getStatus(script) === 'progress'" class="progress-row">
|
||||
<view class="progress-track">
|
||||
<view class="progress-fill" :style="{ width: getProgress(script) + '%' }"></view>
|
||||
</view>
|
||||
<text>{{ getProgress(script) }}%</text>
|
||||
</view>
|
||||
<view v-else-if="isFavorite(script)" class="favorite-row">
|
||||
<text class="favorite-star">★</text>
|
||||
<text>已收藏</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button class="logout kos-pill" @click="handleLogout">退出登录</button>
|
||||
<view v-else class="empty-card">
|
||||
<view class="empty-book">
|
||||
<view></view>
|
||||
<view></view>
|
||||
</view>
|
||||
<text class="empty-title">还没有人生剧本</text>
|
||||
<text class="empty-text">去爽文生成页写下一句灵感,生成你的第一段平行人生。</text>
|
||||
<view class="empty-action" @click="createScript">新建剧本</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useAppStore } from '../../stores/app.js'
|
||||
|
||||
const store = useAppStore()
|
||||
const profile = computed(() => store.userProfile || store.registrationData || {})
|
||||
const eventsCount = computed(() => store.events?.length || 0)
|
||||
const scriptsCount = computed(() => store.scripts?.length || 0)
|
||||
const activeType = ref('long')
|
||||
const activeStatus = ref('all')
|
||||
const keyword = ref('')
|
||||
const sortMode = ref('updated')
|
||||
const viewMode = ref('list')
|
||||
const localFavorites = ref(uni.getStorageSync('script_favorites') || {})
|
||||
|
||||
const avatarText = computed(() => (profile.value.nickname || 'Z').slice(0, 1))
|
||||
const chips = computed(() => [profile.value.zodiac, profile.value.mbti, profile.value.profession].filter(Boolean))
|
||||
const hobbyTags = computed(() => {
|
||||
const hobbies = profile.value.hobbies
|
||||
if (Array.isArray(hobbies) && hobbies.length) return hobbies
|
||||
return ['阅读', '旅行', '音乐', '创作']
|
||||
const typeTabs = [
|
||||
{ label: '长篇', value: 'long' },
|
||||
{ label: '短篇', value: 'short' },
|
||||
{ label: '风格', value: 'style' }
|
||||
]
|
||||
|
||||
const statusFilters = [
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '进行中', value: 'progress' },
|
||||
{ label: '已完成', value: 'done' },
|
||||
{ label: '草稿箱', value: 'draft' },
|
||||
{ label: '收藏夹', value: 'favorite' }
|
||||
]
|
||||
|
||||
const fallbackScripts = [
|
||||
{
|
||||
id: 'demo-1',
|
||||
title: '逆袭人生:从低谷到巅峰',
|
||||
length: 'long',
|
||||
status: 'progress',
|
||||
tags: ['逆袭成长', '都市', '事业', '热血'],
|
||||
summary: '从被分手、被否定的低谷开始,凭借天赋、努力与智慧,一步步逆袭成为行业巅峰,收获事业、财富...',
|
||||
chapterCount: 28,
|
||||
wordCount: 128000,
|
||||
updatedAt: '今天 21:30',
|
||||
progress: 28
|
||||
},
|
||||
{
|
||||
id: 'demo-2',
|
||||
title: '如果那年我没有放弃',
|
||||
length: 'long',
|
||||
status: 'done',
|
||||
tags: ['成长治愈', '校园', '爱情', '温暖'],
|
||||
summary: '重回十八岁,弥补遗憾,勇敢追梦,守护那些曾经错过的人和事。',
|
||||
chapterCount: 36,
|
||||
wordCount: 156000,
|
||||
completedAt: '2025.05.10',
|
||||
isFavorite: true
|
||||
},
|
||||
{
|
||||
id: 'demo-3',
|
||||
title: '重生之我在未来等你',
|
||||
length: 'long',
|
||||
status: 'progress',
|
||||
tags: ['重生', '科幻', '爱情', '未来'],
|
||||
summary: '一觉醒来,回到十年前的那一天。这一次,我不仅要改变自己的人生,还要找到你。',
|
||||
chapterCount: 18,
|
||||
wordCount: 83000,
|
||||
updatedAt: '昨天 18:47',
|
||||
progress: 46
|
||||
},
|
||||
{
|
||||
id: 'demo-4',
|
||||
title: '天才作曲家的璀璨之路',
|
||||
length: 'long',
|
||||
status: 'draft',
|
||||
tags: ['音乐', '励志', '天赋', '梦想'],
|
||||
summary: '从默默无闻到享誉全球,用音符征服世界,写下属于自己的传奇乐章。',
|
||||
chapterCount: 9,
|
||||
wordCount: 31000,
|
||||
createdAt: '2025.05.08'
|
||||
},
|
||||
{
|
||||
id: 'demo-5',
|
||||
title: '咖啡馆里的奇遇',
|
||||
length: 'short',
|
||||
status: 'done',
|
||||
tags: ['生活', '治愈', '奇幻', '温暖'],
|
||||
summary: '一杯咖啡,一次奇遇,改变了我平凡的生活,也让我遇见了最特别的你。',
|
||||
chapterCount: 1,
|
||||
wordCount: 23000,
|
||||
completedAt: '2025.05.01',
|
||||
isFavorite: true
|
||||
},
|
||||
{
|
||||
id: 'demo-6',
|
||||
title: '赛博时代的追光者',
|
||||
length: 'long',
|
||||
status: 'draft',
|
||||
tags: ['科幻', '未来', '冒险', '热血'],
|
||||
summary: '在数据与代码构建的世界里,我追寻光明,也在黑暗中寻找真正的自由。',
|
||||
chapterCount: 3,
|
||||
wordCount: 12000,
|
||||
createdAt: '2025.05.12'
|
||||
}
|
||||
]
|
||||
|
||||
const scripts = computed(() => {
|
||||
const list = store.scripts || []
|
||||
return list.length ? list : fallbackScripts
|
||||
})
|
||||
const signature = computed(() => profile.value.future?.ideal || '正在把人生整理成一份会发光的档案。')
|
||||
|
||||
const editProfile = () => {
|
||||
uni.navigateTo({ url: '/pages/onboarding/index?edit=1' })
|
||||
const visibleScripts = computed(() => {
|
||||
const filtered = scripts.value.filter(script => {
|
||||
const status = getStatus(script)
|
||||
if (keyword.value) {
|
||||
const haystack = [script.title, script.summary, script.content, script.style, ...(script.tags || [])].join(' ')
|
||||
if (!haystack.includes(keyword.value)) return false
|
||||
}
|
||||
if (activeStatus.value === 'favorite') return isFavorite(script)
|
||||
if (activeStatus.value !== 'all' && status !== activeStatus.value) return false
|
||||
if (activeType.value === 'short') return script.length === 'short'
|
||||
if (activeType.value === 'long') return script.length !== 'short'
|
||||
return true
|
||||
})
|
||||
return [...filtered].sort((a, b) => {
|
||||
if (sortMode.value === 'words') return Number(b.wordCount || 0) - Number(a.wordCount || 0)
|
||||
if (sortMode.value === 'progress') return getProgress(b) - getProgress(a)
|
||||
return String(b.updateTime || b.updatedAt || b.createTime || b.date || '').localeCompare(String(a.updateTime || a.updatedAt || a.createTime || a.date || ''))
|
||||
})
|
||||
})
|
||||
|
||||
const sortLabel = computed(() => {
|
||||
const map = { updated: '最近更新⌄', words: '字数最多⌄', progress: '进度最高⌄' }
|
||||
return map[sortMode.value] || '最近更新⌄'
|
||||
})
|
||||
|
||||
const getStatus = (script) => {
|
||||
if (script.status) return script.status
|
||||
if (script.isDraft) return 'draft'
|
||||
if (script.isCompleted || script.completedAt) return 'done'
|
||||
return script.progress ? 'progress' : 'done'
|
||||
}
|
||||
|
||||
const handleLogout = () => {
|
||||
const getStatusLabel = (script) => {
|
||||
const map = { progress: '进行中', done: '已完成', draft: '草稿' }
|
||||
return map[getStatus(script)] || '已完成'
|
||||
}
|
||||
|
||||
const getLengthLabel = (length) => {
|
||||
return length === 'short' ? '短篇' : '长篇'
|
||||
}
|
||||
|
||||
const getTags = (script) => {
|
||||
if (Array.isArray(script.tags) && script.tags.length) return script.tags.slice(0, 4)
|
||||
return [script.style || '逆袭成长', '都市', '事业', '热血']
|
||||
}
|
||||
|
||||
const getChapterCount = (script) => script.chapterCount || script.chapters || Math.max(1, Math.round((script.wordCount || 30000) / 4500))
|
||||
|
||||
const getWordCount = (script) => {
|
||||
const count = Number(script.wordCount || 0)
|
||||
if (!count) return '3.1万字'
|
||||
if (count >= 10000) return `${(count / 10000).toFixed(1)}万字`
|
||||
return `${count}字`
|
||||
}
|
||||
|
||||
const getDateText = (script) => {
|
||||
if (getStatus(script) === 'done') return `完成于:${script.completedAt || script.date || '2025.05.10'}`
|
||||
if (getStatus(script) === 'draft') return `创建于:${script.createdAt || script.date || '2025.05.08'}`
|
||||
return `最近更新:${script.updatedAt || script.date || '今天 21:30'}`
|
||||
}
|
||||
|
||||
const getProgress = (script) => Math.max(1, Math.min(99, Number(script.progress || 28)))
|
||||
|
||||
const getInitial = (script) => (script.title || '剧').slice(0, 1)
|
||||
|
||||
const isFavorite = (script) => {
|
||||
return Boolean(script.isFavorite || script.favorite || localFavorites.value[String(script.id)])
|
||||
}
|
||||
|
||||
const viewScript = (script) => {
|
||||
if (!script?.id || String(script.id).startsWith('demo-')) return
|
||||
uni.navigateTo({ url: `/pages/main/ScriptDetailView?id=${script.id}` })
|
||||
}
|
||||
|
||||
const createScript = () => {
|
||||
uni.$emit('switchTab', 'script')
|
||||
}
|
||||
|
||||
const openSearch = () => {
|
||||
uni.showModal({
|
||||
title: '退出登录',
|
||||
content: '确定要离开当前数字生命档案吗?',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
await store.logout()
|
||||
uni.reLaunch({ url: '/pages/login/index' })
|
||||
title: '搜索剧本',
|
||||
editable: true,
|
||||
placeholderText: '输入标题、标签或关键词',
|
||||
success: (res) => {
|
||||
if (res.confirm) keyword.value = String(res.content || '').trim()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const openMoreMenu = () => {
|
||||
uni.showActionSheet({
|
||||
itemList: ['清空搜索', '只看收藏', '查看全部'],
|
||||
success: ({ tapIndex }) => {
|
||||
if (tapIndex === 0) keyword.value = ''
|
||||
if (tapIndex === 1) activeStatus.value = 'favorite'
|
||||
if (tapIndex === 2) {
|
||||
keyword.value = ''
|
||||
activeStatus.value = 'all'
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const toggleSort = () => {
|
||||
const order = ['updated', 'words', 'progress']
|
||||
sortMode.value = order[(order.indexOf(sortMode.value) + 1) % order.length]
|
||||
}
|
||||
|
||||
const toggleViewMode = () => {
|
||||
viewMode.value = viewMode.value === 'list' ? 'grid' : 'list'
|
||||
}
|
||||
|
||||
const openScriptMenu = (script) => {
|
||||
const favorite = isFavorite(script)
|
||||
uni.showActionSheet({
|
||||
itemList: [favorite ? '取消收藏' : '收藏剧本', '查看详情', '映射路径'],
|
||||
success: ({ tapIndex }) => {
|
||||
if (tapIndex === 0) {
|
||||
const next = { ...localFavorites.value }
|
||||
if (favorite) delete next[String(script.id)]
|
||||
else next[String(script.id)] = true
|
||||
localFavorites.value = next
|
||||
uni.setStorageSync('script_favorites', next)
|
||||
uni.showToast({ title: favorite ? '已取消收藏' : '已收藏', icon: 'success' })
|
||||
}
|
||||
if (tapIndex === 1) viewScript(script)
|
||||
if (tapIndex === 2) mapScript(script)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const mapScript = async (script) => {
|
||||
if (!script?.id || String(script.id).startsWith('demo-')) {
|
||||
uni.showToast({ title: '示例剧本暂不可映射', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const res = await store.selectScript(script.id)
|
||||
if (!res.success) {
|
||||
uni.showToast({ title: res.error || '映射失败', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.navigateTo({ url: '/pages/main/PathView' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mine-view {
|
||||
.script-library {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
padding-bottom: 32rpx;
|
||||
padding-bottom: 26rpx;
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
border-radius: 34rpx;
|
||||
padding: 32rpx;
|
||||
.page-head,
|
||||
.title-row,
|
||||
.head-actions,
|
||||
.type-tabs,
|
||||
.filter-bar,
|
||||
.sort-tools,
|
||||
.card-top,
|
||||
.title-wrap,
|
||||
.right-state,
|
||||
.meta-row,
|
||||
.progress-row,
|
||||
.favorite-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 22rpx;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 112rpx;
|
||||
height: 112rpx;
|
||||
flex-shrink: 0;
|
||||
.page-head {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.title-row {
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
.spark {
|
||||
color: #ffd58c;
|
||||
font-size: 34rpx;
|
||||
text-shadow: 0 0 20rpx rgba(255, 202, 125, 0.5);
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: #fff;
|
||||
font-size: 42rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.head-actions {
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.circle-btn {
|
||||
width: 58rpx;
|
||||
height: 58rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 50rpx;
|
||||
border: 1rpx solid rgba(142, 105, 255, 0.36);
|
||||
background: rgba(10, 11, 38, 0.72);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
width: 25rpx;
|
||||
height: 25rpx;
|
||||
border: 4rpx solid #fff;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: -9rpx;
|
||||
bottom: -8rpx;
|
||||
width: 14rpx;
|
||||
height: 4rpx;
|
||||
border-radius: 999rpx;
|
||||
background: #fff;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.more-icon {
|
||||
display: flex;
|
||||
gap: 5rpx;
|
||||
}
|
||||
|
||||
.more-icon view {
|
||||
width: 6rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.type-tabs {
|
||||
justify-content: space-between;
|
||||
border-bottom: 1rpx solid rgba(126, 87, 255, 0.18);
|
||||
padding-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.type-tab {
|
||||
position: relative;
|
||||
color: rgba(224, 214, 243, 0.7);
|
||||
font-size: 31rpx;
|
||||
font-weight: 900;
|
||||
background: linear-gradient(135deg, #b245ff, #2a7dff);
|
||||
box-shadow: 0 0 36rpx rgba(168, 85, 255, 0.55);
|
||||
padding: 0 20rpx 14rpx;
|
||||
}
|
||||
|
||||
.profile-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
.type-tab.active {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.name-row {
|
||||
.type-tab.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
right: 20rpx;
|
||||
bottom: -17rpx;
|
||||
height: 5rpx;
|
||||
border-radius: 999rpx;
|
||||
background: #b246ff;
|
||||
box-shadow: 0 0 18rpx rgba(178, 70, 255, 0.8);
|
||||
}
|
||||
|
||||
.new-script {
|
||||
margin-left: auto;
|
||||
height: 64rpx;
|
||||
padding: 0 24rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.name {
|
||||
gap: 8rpx;
|
||||
color: #fff;
|
||||
font-size: 38rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.star {
|
||||
color: #ffd184;
|
||||
}
|
||||
|
||||
.signature {
|
||||
display: block;
|
||||
margin-top: 10rpx;
|
||||
color: rgba(224, 211, 246, 0.66);
|
||||
font-size: 24rpx;
|
||||
line-height: 1.5;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, #b346ff, #7330ff);
|
||||
box-shadow: 0 0 26rpx rgba(168, 85, 247, 0.54);
|
||||
}
|
||||
|
||||
.chips,
|
||||
.tag-cloud {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
margin-top: 18rpx;
|
||||
.plus {
|
||||
font-size: 32rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.chip,
|
||||
.tag {
|
||||
height: 44rpx;
|
||||
padding: 0 16rpx;
|
||||
.filter-bar {
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
.status-scroll {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.status-row {
|
||||
display: inline-flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.status-chip {
|
||||
height: 52rpx;
|
||||
min-width: 88rpx;
|
||||
padding: 0 24rpx;
|
||||
border-radius: 999rpx;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: rgba(244, 235, 255, 0.86);
|
||||
font-size: 21rpx;
|
||||
justify-content: center;
|
||||
color: rgba(224, 214, 243, 0.78);
|
||||
font-size: 23rpx;
|
||||
border: 1rpx solid rgba(151, 111, 255, 0.42);
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
align-self: flex-start;
|
||||
height: 54rpx;
|
||||
padding: 0 18rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #caa0ff;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 18rpx;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
border-radius: 28rpx;
|
||||
padding: 28rpx;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
display: block;
|
||||
.status-chip.active {
|
||||
color: #fff;
|
||||
font-size: 48rpx;
|
||||
font-weight: 900;
|
||||
border-color: rgba(206, 82, 255, 0.92);
|
||||
background: rgba(130, 48, 220, 0.42);
|
||||
box-shadow: 0 0 18rpx rgba(168, 67, 255, 0.46);
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
display: block;
|
||||
margin-top: 6rpx;
|
||||
color: rgba(224, 211, 246, 0.62);
|
||||
.sort-tools {
|
||||
gap: 14rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sort-text {
|
||||
color: #c99fff;
|
||||
font-size: 23rpx;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
border-radius: 30rpx;
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: #fff;
|
||||
font-size: 30rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.memory-line {
|
||||
.grid-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 18rpx;
|
||||
display: grid;
|
||||
grid-template-columns: 86rpx 1fr;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 6rpx;
|
||||
padding: 11rpx;
|
||||
box-sizing: border-box;
|
||||
border: 1rpx solid rgba(151, 111, 255, 0.32);
|
||||
}
|
||||
|
||||
.grid-icon view {
|
||||
border: 2rpx solid rgba(230, 222, 250, 0.78);
|
||||
border-radius: 3rpx;
|
||||
}
|
||||
|
||||
.script-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18rpx;
|
||||
padding: 22rpx 0;
|
||||
border-bottom: 1rpx solid rgba(180, 139, 255, 0.14);
|
||||
}
|
||||
|
||||
.memory-line:last-child {
|
||||
border-bottom: 0;
|
||||
.script-list.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.memory-label {
|
||||
color: #b56cff;
|
||||
.script-list.grid .script-card {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.script-list.grid .cover {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.grid-icon.active {
|
||||
border-color: rgba(206, 82, 255, 0.9);
|
||||
background: rgba(130, 48, 220, 0.28);
|
||||
}
|
||||
|
||||
.script-card {
|
||||
display: grid;
|
||||
grid-template-columns: 150rpx 1fr;
|
||||
gap: 22rpx;
|
||||
min-height: 196rpx;
|
||||
padding: 20rpx;
|
||||
border-radius: 24rpx;
|
||||
border: 1rpx solid rgba(105, 79, 210, 0.34);
|
||||
background:
|
||||
radial-gradient(circle at 100% 0%, rgba(112, 72, 255, 0.14), transparent 38%),
|
||||
rgba(9, 12, 42, 0.72);
|
||||
box-shadow: inset 0 0 28rpx rgba(92, 57, 197, 0.08);
|
||||
}
|
||||
|
||||
.cover {
|
||||
width: 150rpx;
|
||||
height: 150rpx;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 54rpx;
|
||||
font-weight: 900;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(135deg, #3b1a90, #d65cff);
|
||||
}
|
||||
|
||||
.cover-0 { background: linear-gradient(135deg, #29135f, #9037ff 48%, #191b5e); }
|
||||
.cover-1 { background: linear-gradient(135deg, #3c1c4a, #f2b3cc 48%, #16143b); }
|
||||
.cover-2 { background: linear-gradient(135deg, #1a225f, #7d4cff 48%, #0a0f2c); }
|
||||
.cover-3 { background: linear-gradient(135deg, #2f240b, #f7b44a 48%, #0d0a16); }
|
||||
.cover-4 { background: linear-gradient(135deg, #3f2417, #d8b58a 48%, #17101d); }
|
||||
.cover-5 { background: linear-gradient(135deg, #141451, #cc46ff 48%, #0c0b28); }
|
||||
|
||||
.card-main {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.card-top {
|
||||
justify-content: space-between;
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
.title-wrap {
|
||||
min-width: 0;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.script-title {
|
||||
color: #fff;
|
||||
font-size: 27rpx;
|
||||
line-height: 1.25;
|
||||
font-weight: 900;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.length-badge {
|
||||
flex-shrink: 0;
|
||||
padding: 4rpx 9rpx;
|
||||
border-radius: 8rpx;
|
||||
color: #c985ff;
|
||||
font-size: 18rpx;
|
||||
border: 1rpx solid rgba(182, 92, 255, 0.5);
|
||||
background: rgba(128, 55, 204, 0.22);
|
||||
}
|
||||
|
||||
.right-state {
|
||||
flex-shrink: 0;
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
.state-pill {
|
||||
height: 34rpx;
|
||||
padding: 0 14rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 19rpx;
|
||||
}
|
||||
|
||||
.state-progress {
|
||||
color: #ffbf4c;
|
||||
background: rgba(170, 103, 20, 0.22);
|
||||
}
|
||||
|
||||
.state-done {
|
||||
color: #79e6a9;
|
||||
background: rgba(44, 146, 88, 0.2);
|
||||
}
|
||||
|
||||
.state-draft {
|
||||
color: rgba(224, 214, 243, 0.76);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
color: rgba(224, 214, 243, 0.66);
|
||||
font-size: 24rpx;
|
||||
letter-spacing: 3rpx;
|
||||
}
|
||||
|
||||
.tag-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
height: 34rpx;
|
||||
padding: 0 14rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #d49cff;
|
||||
font-size: 19rpx;
|
||||
background: rgba(149, 55, 255, 0.2);
|
||||
}
|
||||
|
||||
.summary {
|
||||
display: -webkit-box;
|
||||
margin-top: 14rpx;
|
||||
color: rgba(226, 215, 246, 0.72);
|
||||
font-size: 22rpx;
|
||||
line-height: 1.55;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.meta-row {
|
||||
gap: 14rpx;
|
||||
margin-top: 14rpx;
|
||||
color: rgba(218, 204, 243, 0.66);
|
||||
font-size: 21rpx;
|
||||
}
|
||||
|
||||
.progress-row {
|
||||
justify-content: flex-end;
|
||||
gap: 14rpx;
|
||||
margin-top: 14rpx;
|
||||
color: #bd72ff;
|
||||
font-size: 22rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.memory-text {
|
||||
color: rgba(224, 211, 246, 0.72);
|
||||
font-size: 24rpx;
|
||||
line-height: 1.55;
|
||||
.progress-track {
|
||||
width: 118rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 999rpx;
|
||||
background: rgba(173, 160, 210, 0.18);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.logout {
|
||||
height: 72rpx;
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(90deg, #b246ff, #d878ff);
|
||||
}
|
||||
|
||||
.favorite-row {
|
||||
justify-content: flex-end;
|
||||
gap: 8rpx;
|
||||
margin-top: 14rpx;
|
||||
color: #b768ff;
|
||||
font-size: 23rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.favorite-star {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.empty-card {
|
||||
margin-top: 30rpx;
|
||||
border-radius: 26rpx;
|
||||
padding: 44rpx 30rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
border: 1rpx solid rgba(105, 79, 210, 0.34);
|
||||
background: rgba(9, 12, 42, 0.72);
|
||||
}
|
||||
|
||||
.empty-book {
|
||||
display: flex;
|
||||
gap: 6rpx;
|
||||
margin-bottom: 18rpx;
|
||||
}
|
||||
|
||||
.empty-book view {
|
||||
width: 32rpx;
|
||||
height: 46rpx;
|
||||
border: 4rpx solid #b768ff;
|
||||
border-radius: 8rpx 4rpx 4rpx 8rpx;
|
||||
}
|
||||
|
||||
.empty-title {
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
margin-top: 12rpx;
|
||||
color: rgba(226, 215, 246, 0.68);
|
||||
font-size: 22rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.empty-action {
|
||||
margin-top: 22rpx;
|
||||
height: 56rpx;
|
||||
padding: 0 30rpx;
|
||||
border-radius: 999rpx;
|
||||
color: rgba(224, 211, 246, 0.74);
|
||||
font-size: 25rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
font-size: 23rpx;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, #b346ff, #7330ff);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -63,10 +63,42 @@ const loadPath = async (scriptId) => {
|
||||
if (!scriptId) return
|
||||
try {
|
||||
const res = await lifePathService.getPathByScriptId(scriptId)
|
||||
pathData.value = res.data || null
|
||||
pathData.value = res.data || await createPlaceholderPath(scriptId)
|
||||
store.setCurrentPath(pathData.value)
|
||||
} catch (error) {
|
||||
pathData.value = null
|
||||
pathData.value = await createPlaceholderPath(scriptId)
|
||||
store.setCurrentPath(pathData.value)
|
||||
}
|
||||
}
|
||||
|
||||
const createPlaceholderPath = async (scriptId) => {
|
||||
const script = selectedScript.value || {}
|
||||
const title = script.title ? `${script.title} · 实现路径` : '我的实现路径'
|
||||
const steps = [
|
||||
{ phase: '阶段1', task: '整理目标', desc: '把剧本中的关键目标拆成可以执行的小目标。', content: '把剧本中的关键目标拆成可以执行的小目标。', done: true },
|
||||
{ phase: '阶段2', task: '建立习惯', desc: '选择一个最小行动,每天稳定推进。', content: '选择一个最小行动,每天稳定推进。', done: false },
|
||||
{ phase: '阶段3', task: '复盘迭代', desc: '每周回看进展,根据现实反馈调整路径。', content: '每周回看进展,根据现实反馈调整路径。', done: false }
|
||||
]
|
||||
try {
|
||||
const res = await lifePathService.createPath({
|
||||
scriptId,
|
||||
title,
|
||||
description: script.summary || '根据选中的人生剧本生成的占位实现路径,后续可接入AI生成更细的行动计划。',
|
||||
steps,
|
||||
progress: 8,
|
||||
status: 'active'
|
||||
})
|
||||
return lifePathService.transformToFrontendFormat(res.data)
|
||||
} catch (error) {
|
||||
return {
|
||||
id: `local-${scriptId}`,
|
||||
scriptId,
|
||||
title,
|
||||
description: script.summary || '占位实现路径',
|
||||
steps,
|
||||
progress: 8,
|
||||
status: 'active'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
</view>
|
||||
<text class="section-subtitle">你的成长之路,正在展开</text>
|
||||
</view>
|
||||
<view class="map-btn kos-pill">
|
||||
<view class="map-btn kos-pill" @click="openMap">
|
||||
<view class="map-icon"></view>
|
||||
<text>轨迹地图</text>
|
||||
</view>
|
||||
@@ -81,7 +81,7 @@
|
||||
:class="{ active: activeFilter === filter.value }"
|
||||
@click="activeFilter = filter.value"
|
||||
>{{ filter.label }}</text>
|
||||
<text class="add-filter">+</text>
|
||||
<text class="add-filter" @click="addFilter">+</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
@@ -130,14 +130,16 @@ import { useAppStore } from '../../stores/app.js'
|
||||
|
||||
const store = useAppStore()
|
||||
const activeFilter = ref('all')
|
||||
const customFilters = ref([])
|
||||
|
||||
const filters = [
|
||||
const baseFilters = [
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '童年', value: 'childhood' },
|
||||
{ label: '高光', value: 'highlight' },
|
||||
{ label: '低谷', value: 'valley' },
|
||||
{ label: '美好的瞬间', value: 'daily_log' }
|
||||
]
|
||||
const filters = computed(() => [...baseFilters, ...customFilters.value])
|
||||
|
||||
const sampleEvents = [
|
||||
{
|
||||
@@ -192,6 +194,10 @@ const avatar = computed(() => {
|
||||
|
||||
const displayEvents = computed(() => {
|
||||
if (activeFilter.value === 'all') return events.value
|
||||
if (activeFilter.value.startsWith('custom_')) {
|
||||
const label = activeFilter.value.replace('custom_', '')
|
||||
return events.value.filter(event => Array.isArray(event.tags) && event.tags.includes(label))
|
||||
}
|
||||
return events.value.filter(event => event.eventType === activeFilter.value || event.emotionType === activeFilter.value)
|
||||
})
|
||||
|
||||
@@ -222,28 +228,50 @@ const createEvent = () => {
|
||||
}
|
||||
|
||||
const openDetail = (event) => {
|
||||
if (String(event.id || '').startsWith('sample-')) return
|
||||
uni.setStorageSync('current_life_event', JSON.parse(JSON.stringify(event || {})))
|
||||
if (!event?.id) return
|
||||
uni.navigateTo({ url: `/pages/life-event/detail?id=${event.id}` })
|
||||
}
|
||||
|
||||
const editProfile = () => {
|
||||
uni.navigateTo({ url: '/pages/onboarding/index?edit=1' })
|
||||
}
|
||||
|
||||
const openMap = () => {
|
||||
uni.navigateTo({ url: '/pages/main/PathView' })
|
||||
}
|
||||
|
||||
const addFilter = () => {
|
||||
uni.showModal({
|
||||
title: '新增筛选',
|
||||
editable: true,
|
||||
placeholderText: '输入标签名',
|
||||
success: (res) => {
|
||||
const value = String(res.content || '').trim()
|
||||
if (!res.confirm || !value) return
|
||||
const filter = { label: value, value: `custom_${value}` }
|
||||
if (!customFilters.value.some(item => item.label === value)) {
|
||||
customFilters.value.push(filter)
|
||||
}
|
||||
activeFilter.value = filter.value
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.record-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 28rpx;
|
||||
padding-bottom: 34rpx;
|
||||
gap: 24rpx;
|
||||
padding-bottom: 22rpx;
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 34rpx;
|
||||
padding: 40rpx 34rpx 32rpx;
|
||||
padding: 34rpx 30rpx 28rpx;
|
||||
}
|
||||
|
||||
.profile-card::after {
|
||||
@@ -251,8 +279,8 @@ const editProfile = () => {
|
||||
position: absolute;
|
||||
right: -28rpx;
|
||||
bottom: -20rpx;
|
||||
width: 250rpx;
|
||||
height: 180rpx;
|
||||
width: 220rpx;
|
||||
height: 156rpx;
|
||||
background: radial-gradient(circle, rgba(122, 58, 255, 0.35), transparent 62%);
|
||||
border: 1rpx solid rgba(158, 88, 255, 0.26);
|
||||
border-radius: 50%;
|
||||
@@ -269,18 +297,18 @@ const editProfile = () => {
|
||||
.profile-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
gap: 22rpx;
|
||||
}
|
||||
|
||||
.avatar-wrap {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 134rpx;
|
||||
height: 134rpx;
|
||||
padding: 6rpx;
|
||||
width: 112rpx;
|
||||
height: 112rpx;
|
||||
padding: 5rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #fff, #9b54ff 38%, #4a67ff);
|
||||
box-shadow: 0 0 42rpx rgba(149, 89, 255, 0.56);
|
||||
box-shadow: 0 0 34rpx rgba(149, 89, 255, 0.52);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
@@ -292,10 +320,10 @@ const editProfile = () => {
|
||||
|
||||
.avatar-edit {
|
||||
position: absolute;
|
||||
right: -6rpx;
|
||||
bottom: -6rpx;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
right: -5rpx;
|
||||
bottom: -5rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #8b4dff, #4a2cff);
|
||||
box-shadow: 0 0 20rpx rgba(158, 91, 255, 0.6);
|
||||
@@ -308,7 +336,7 @@ const editProfile = () => {
|
||||
border-radius: 6rpx;
|
||||
background: #fff;
|
||||
transform: rotate(-45deg);
|
||||
margin: 21rpx auto;
|
||||
margin: 17rpx auto;
|
||||
}
|
||||
|
||||
.profile-info {
|
||||
@@ -324,33 +352,33 @@ const editProfile = () => {
|
||||
|
||||
.profile-name {
|
||||
color: #fff;
|
||||
font-size: 44rpx;
|
||||
font-size: 36rpx;
|
||||
line-height: 1.1;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.star {
|
||||
color: #ffd589;
|
||||
font-size: 30rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.profile-subtitle {
|
||||
display: block;
|
||||
margin-top: 14rpx;
|
||||
margin-top: 10rpx;
|
||||
color: rgba(239, 232, 255, 0.78);
|
||||
font-size: 27rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.edit-profile {
|
||||
flex-shrink: 0;
|
||||
height: 64rpx;
|
||||
padding: 0 22rpx;
|
||||
height: 56rpx;
|
||||
padding: 0 18rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
gap: 8rpx;
|
||||
color: #dccbff;
|
||||
font-size: 24rpx;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.tiny-pen {
|
||||
@@ -363,12 +391,12 @@ const editProfile = () => {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 1rpx;
|
||||
margin: 32rpx 0 24rpx;
|
||||
margin: 28rpx 0 20rpx;
|
||||
background: rgba(180, 139, 255, 0.22);
|
||||
}
|
||||
|
||||
.profile-divider.small {
|
||||
margin: 24rpx 0;
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
|
||||
.meta-grid {
|
||||
@@ -380,23 +408,28 @@ const editProfile = () => {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14rpx;
|
||||
padding-right: 18rpx;
|
||||
gap: 10rpx;
|
||||
padding-right: 12rpx;
|
||||
border-right: 1rpx solid rgba(180, 139, 255, 0.22);
|
||||
}
|
||||
|
||||
.meta-item + .meta-item {
|
||||
padding-left: 22rpx;
|
||||
padding-left: 16rpx;
|
||||
}
|
||||
|
||||
.meta-item.no-border {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.meta-item > view,
|
||||
.hobby-row > view {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.meta-icon,
|
||||
.heart {
|
||||
color: #a855ff;
|
||||
font-size: 42rpx;
|
||||
font-size: 34rpx;
|
||||
line-height: 1;
|
||||
text-shadow: 0 0 24rpx rgba(168, 85, 255, 0.7);
|
||||
}
|
||||
@@ -404,35 +437,35 @@ const editProfile = () => {
|
||||
.person-icon,
|
||||
.job-icon {
|
||||
position: relative;
|
||||
width: 38rpx;
|
||||
height: 38rpx;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.person-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 10rpx;
|
||||
left: 8rpx;
|
||||
top: 0;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border: 4rpx solid #a855ff;
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
border: 3rpx solid #a855ff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.person-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 2rpx;
|
||||
left: 1rpx;
|
||||
bottom: 0;
|
||||
width: 30rpx;
|
||||
height: 18rpx;
|
||||
border: 4rpx solid #a855ff;
|
||||
width: 28rpx;
|
||||
height: 16rpx;
|
||||
border: 3rpx solid #a855ff;
|
||||
border-radius: 18rpx 18rpx 4rpx 4rpx;
|
||||
}
|
||||
|
||||
.job-icon {
|
||||
border: 5rpx solid #a855ff;
|
||||
border: 4rpx solid #a855ff;
|
||||
border-radius: 8rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -440,11 +473,11 @@ const editProfile = () => {
|
||||
.job-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 10rpx;
|
||||
top: -10rpx;
|
||||
left: 8rpx;
|
||||
top: -8rpx;
|
||||
width: 12rpx;
|
||||
height: 8rpx;
|
||||
border: 4rpx solid #a855ff;
|
||||
height: 7rpx;
|
||||
border: 3rpx solid #a855ff;
|
||||
border-bottom: 0;
|
||||
border-radius: 8rpx 8rpx 0 0;
|
||||
}
|
||||
@@ -457,14 +490,14 @@ const editProfile = () => {
|
||||
|
||||
.meta-label {
|
||||
color: rgba(219, 204, 247, 0.54);
|
||||
font-size: 22rpx;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.meta-value,
|
||||
.hobby-text {
|
||||
margin-top: 4rpx;
|
||||
margin-top: 3rpx;
|
||||
color: #fff;
|
||||
font-size: 27rpx;
|
||||
font-size: 23rpx;
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -474,7 +507,7 @@ const editProfile = () => {
|
||||
.hobby-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 28rpx;
|
||||
gap: 22rpx;
|
||||
}
|
||||
|
||||
.section-head {
|
||||
@@ -486,12 +519,12 @@ const editProfile = () => {
|
||||
.title-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14rpx;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: #fff;
|
||||
font-size: 44rpx;
|
||||
font-size: 40rpx;
|
||||
line-height: 1.1;
|
||||
font-weight: 800;
|
||||
}
|
||||
@@ -502,26 +535,26 @@ const editProfile = () => {
|
||||
|
||||
.section-subtitle {
|
||||
display: block;
|
||||
margin-top: 14rpx;
|
||||
margin-top: 10rpx;
|
||||
color: rgba(210, 194, 242, 0.68);
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.map-btn {
|
||||
height: 64rpx;
|
||||
padding: 0 24rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
color: #caa9ff;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.map-btn {
|
||||
height: 56rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
color: #caa9ff;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.map-icon {
|
||||
width: 28rpx;
|
||||
height: 22rpx;
|
||||
border: 4rpx solid currentColor;
|
||||
width: 24rpx;
|
||||
height: 20rpx;
|
||||
border: 3rpx solid currentColor;
|
||||
border-radius: 4rpx;
|
||||
transform: skewY(-12deg);
|
||||
}
|
||||
@@ -533,21 +566,21 @@ const editProfile = () => {
|
||||
|
||||
.filter-row {
|
||||
display: inline-flex;
|
||||
gap: 18rpx;
|
||||
gap: 16rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-chip,
|
||||
.add-filter {
|
||||
height: 62rpx;
|
||||
min-width: 104rpx;
|
||||
padding: 0 30rpx;
|
||||
height: 54rpx;
|
||||
min-width: 92rpx;
|
||||
padding: 0 24rpx;
|
||||
border-radius: 999rpx;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgba(224, 214, 243, 0.72);
|
||||
font-size: 25rpx;
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -559,11 +592,11 @@ const editProfile = () => {
|
||||
}
|
||||
|
||||
.add-filter {
|
||||
min-width: 62rpx;
|
||||
min-width: 56rpx;
|
||||
padding: 0;
|
||||
border: 2rpx dashed rgba(155, 112, 255, 0.45);
|
||||
color: #c49cff;
|
||||
font-size: 36rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
@@ -573,8 +606,8 @@ const editProfile = () => {
|
||||
|
||||
.timeline-item {
|
||||
display: grid;
|
||||
grid-template-columns: 88rpx 1fr;
|
||||
min-height: 188rpx;
|
||||
grid-template-columns: 76rpx 1fr;
|
||||
min-height: 170rpx;
|
||||
}
|
||||
|
||||
.rail {
|
||||
@@ -586,19 +619,19 @@ const editProfile = () => {
|
||||
.node {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
width: 46rpx;
|
||||
height: 46rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 4rpx solid currentColor;
|
||||
box-shadow: 0 0 28rpx currentColor;
|
||||
box-shadow: 0 0 24rpx currentColor;
|
||||
}
|
||||
|
||||
.node view {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
}
|
||||
@@ -610,7 +643,7 @@ const editProfile = () => {
|
||||
|
||||
.line {
|
||||
position: absolute;
|
||||
top: 56rpx;
|
||||
top: 50rpx;
|
||||
bottom: -6rpx;
|
||||
width: 4rpx;
|
||||
background: linear-gradient(180deg, currentColor, rgba(255,255,255,0.14));
|
||||
@@ -618,14 +651,14 @@ const editProfile = () => {
|
||||
}
|
||||
|
||||
.event-card {
|
||||
min-height: 160rpx;
|
||||
margin-bottom: 22rpx;
|
||||
min-height: 146rpx;
|
||||
margin-bottom: 18rpx;
|
||||
border-radius: 28rpx;
|
||||
display: grid;
|
||||
grid-template-columns: 116rpx 1rpx 1fr 108rpx 24rpx;
|
||||
grid-template-columns: 96rpx 1rpx 1fr 88rpx 20rpx;
|
||||
align-items: center;
|
||||
gap: 22rpx;
|
||||
padding: 22rpx;
|
||||
gap: 18rpx;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.date-box {
|
||||
@@ -635,16 +668,16 @@ const editProfile = () => {
|
||||
.date-month,
|
||||
.date-age {
|
||||
display: block;
|
||||
font-size: 25rpx;
|
||||
font-size: 23rpx;
|
||||
}
|
||||
|
||||
.date-age {
|
||||
margin-top: 8rpx;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.event-divider {
|
||||
width: 1rpx;
|
||||
height: 92rpx;
|
||||
height: 82rpx;
|
||||
background: rgba(180, 139, 255, 0.18);
|
||||
}
|
||||
|
||||
@@ -655,17 +688,17 @@ const editProfile = () => {
|
||||
.event-title {
|
||||
display: block;
|
||||
color: #fff;
|
||||
font-size: 27rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.25;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.event-tag {
|
||||
display: inline-flex;
|
||||
margin-top: 14rpx;
|
||||
padding: 6rpx 14rpx;
|
||||
margin-top: 10rpx;
|
||||
padding: 5rpx 12rpx;
|
||||
border-radius: 999rpx;
|
||||
font-size: 20rpx;
|
||||
font-size: 19rpx;
|
||||
}
|
||||
|
||||
.tag-0 { color: #8effc7; background: rgba(50, 196, 128, 0.18); }
|
||||
@@ -675,9 +708,9 @@ const editProfile = () => {
|
||||
|
||||
.event-summary {
|
||||
display: -webkit-box;
|
||||
margin-top: 12rpx;
|
||||
margin-top: 9rpx;
|
||||
color: rgba(226, 215, 246, 0.66);
|
||||
font-size: 24rpx;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.45;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
@@ -685,14 +718,14 @@ const editProfile = () => {
|
||||
}
|
||||
|
||||
.thumb {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 22rpx;
|
||||
width: 78rpx;
|
||||
height: 78rpx;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 44rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: 900;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -704,7 +737,7 @@ const editProfile = () => {
|
||||
|
||||
.chevron {
|
||||
color: rgba(223, 213, 245, 0.68);
|
||||
font-size: 54rpx;
|
||||
font-size: 46rpx;
|
||||
}
|
||||
|
||||
.empty-card {
|
||||
@@ -733,20 +766,20 @@ const editProfile = () => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
gap: 8rpx;
|
||||
color: #caa6ff;
|
||||
font-size: 24rpx;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.plus-core {
|
||||
width: 92rpx;
|
||||
height: 92rpx;
|
||||
width: 78rpx;
|
||||
height: 78rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 56rpx;
|
||||
font-size: 46rpx;
|
||||
background: linear-gradient(135deg, #b348ff, #582cff);
|
||||
box-shadow: 0 0 38rpx rgba(171, 72, 255, 0.62);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
<view class="mode-tabs kos-card">
|
||||
<view class="mode-tab" :class="{ active: mode === 'inspiration' }" @click="mode = 'inspiration'">
|
||||
<text class="tab-icon">✦</text>
|
||||
<view class="tab-icon sparkles-mini">
|
||||
<view class="spark main"></view>
|
||||
<view class="spark dot"></view>
|
||||
</view>
|
||||
<view>
|
||||
<text class="tab-title">灵感模式</text>
|
||||
<text class="tab-sub">一句话生成爽文</text>
|
||||
@@ -34,37 +37,37 @@
|
||||
<view v-if="mode === 'inspiration'" class="prompt-panel kos-card">
|
||||
<view class="panel-head">
|
||||
<view class="panel-title-row">
|
||||
<text class="spark">✦</text>
|
||||
<view class="panel-sparkles">
|
||||
<view class="panel-spark large"></view>
|
||||
<view class="panel-spark small"></view>
|
||||
</view>
|
||||
<text class="panel-title">写下你想要的故事设定或人生想法</text>
|
||||
</view>
|
||||
<view class="help-chip" @click="shuffleInspirations">不知道写什么?</view>
|
||||
<view class="help-chip" @click="shuffleInspirations">
|
||||
<view class="bulb-icon"></view>
|
||||
<text>不知道写什么?</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<textarea
|
||||
class="prompt-box"
|
||||
maxlength="500"
|
||||
v-model="prompt"
|
||||
placeholder="例如: “如果我没有分手,现在会怎样?” “我成为顶级作曲家的人生” “重生回18岁改变一切” “从小镇做题家到世界首富”"
|
||||
:placeholder="promptPlaceholder"
|
||||
placeholder-class="placeholder"
|
||||
/>
|
||||
|
||||
<view class="prompt-tools">
|
||||
<view class="tool-pill kos-pill">语音输入</view>
|
||||
<text class="counter">{{ prompt.length }}/500</text>
|
||||
<view class="tool-pill kos-pill" @click="shuffleInspirations">随机灵感</view>
|
||||
</view>
|
||||
|
||||
<scroll-view class="style-scroll" scroll-x :show-scrollbar="false">
|
||||
<view class="style-row">
|
||||
<text
|
||||
v-for="item in styleOptions"
|
||||
:key="item.value"
|
||||
class="style-chip kos-pill"
|
||||
:class="{ active: style === item.value }"
|
||||
@click="style = item.value"
|
||||
>{{ item.label }}</text>
|
||||
<view class="tool-pill kos-pill" @click="handleVoiceInput">
|
||||
<view class="mic-icon"></view>
|
||||
<text>语音输入</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<text class="counter">{{ prompt.length }}/500</text>
|
||||
<view class="tool-pill kos-pill" @click="shuffleInspirations">
|
||||
<view class="refresh-icon"></view>
|
||||
<text>随机灵感</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button class="generate-btn kos-primary" :disabled="generating || !prompt.trim()" :loading="generating" @click="generateByPrompt">
|
||||
<view>
|
||||
@@ -72,7 +75,11 @@
|
||||
<text class="generate-sub">今日剩余生成次数:{{ remainingCount }}次</text>
|
||||
</view>
|
||||
<view class="planet-badge">
|
||||
<view></view>
|
||||
<view class="planet-face">
|
||||
<view class="planet-eye left"></view>
|
||||
<view class="planet-eye right"></view>
|
||||
<view class="planet-mouth"></view>
|
||||
</view>
|
||||
</view>
|
||||
</button>
|
||||
</view>
|
||||
@@ -171,6 +178,13 @@ const generating = ref(false)
|
||||
const remainingCount = ref(3)
|
||||
const style = ref('career')
|
||||
const randomRecommendations = ref([])
|
||||
const promptPlaceholder = [
|
||||
'例如:',
|
||||
'“如果我没有分手,现在会怎样?”',
|
||||
'“我成为顶级作曲家的人生”',
|
||||
'“重生回18岁改变一切”',
|
||||
'“从小镇做题家到世界首富”'
|
||||
].join('\n')
|
||||
const custom = reactive({
|
||||
theme: '',
|
||||
style: 'career',
|
||||
@@ -249,6 +263,7 @@ const generateByPrompt = async () => {
|
||||
}
|
||||
|
||||
prompt.value = ''
|
||||
if (typeof res.data?.remainingCount === 'number') remainingCount.value = res.data.remainingCount
|
||||
mode.value = 'list'
|
||||
uni.showToast({ title: '剧本已生成', icon: 'success' })
|
||||
}
|
||||
@@ -286,14 +301,28 @@ const selectScript = async (id) => {
|
||||
}
|
||||
uni.navigateTo({ url: '/pages/main/PathView' })
|
||||
}
|
||||
|
||||
const handleVoiceInput = () => {
|
||||
uni.showModal({
|
||||
title: '语音输入',
|
||||
content: '语音识别入口已保留。后续接入微信录音和AI语音转文字后,会把识别结果自动填入灵感输入框。',
|
||||
confirmText: '填入示例',
|
||||
cancelText: '知道了',
|
||||
success: (res) => {
|
||||
if (res.confirm && !prompt.value.trim()) {
|
||||
prompt.value = '如果我能重新选择一次,我想把人生过成更勇敢的版本'
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.script-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 28rpx;
|
||||
padding-bottom: 32rpx;
|
||||
gap: 24rpx;
|
||||
padding-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.page-head,
|
||||
@@ -313,7 +342,7 @@ const selectScript = async (id) => {
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 46rpx;
|
||||
font-size: 40rpx;
|
||||
line-height: 1.08;
|
||||
font-weight: 900;
|
||||
}
|
||||
@@ -326,23 +355,24 @@ const selectScript = async (id) => {
|
||||
|
||||
.subtitle {
|
||||
display: block;
|
||||
margin-top: 16rpx;
|
||||
margin-top: 14rpx;
|
||||
color: rgba(219, 203, 247, 0.74);
|
||||
font-size: 27rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.script-list-btn,
|
||||
.help-chip,
|
||||
.tool-pill,
|
||||
.rewrite-btn {
|
||||
height: 60rpx;
|
||||
padding: 0 22rpx;
|
||||
height: 56rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #caa0ff;
|
||||
font-size: 24rpx;
|
||||
font-size: 22rpx;
|
||||
gap: 9rpx;
|
||||
}
|
||||
|
||||
.list-icon {
|
||||
@@ -354,7 +384,7 @@ const selectScript = async (id) => {
|
||||
}
|
||||
|
||||
.mode-tabs {
|
||||
height: 94rpx;
|
||||
height: 88rpx;
|
||||
border-radius: 28rpx;
|
||||
padding: 6rpx;
|
||||
display: grid;
|
||||
@@ -366,27 +396,209 @@ const selectScript = async (id) => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
gap: 14rpx;
|
||||
color: rgba(216, 207, 238, 0.62);
|
||||
}
|
||||
|
||||
.mode-tab.active {
|
||||
color: #fff;
|
||||
border: 1rpx solid rgba(191, 91, 255, 0.85);
|
||||
background: rgba(115, 45, 255, 0.2);
|
||||
box-shadow: 0 0 30rpx rgba(167, 60, 255, 0.5);
|
||||
background:
|
||||
radial-gradient(circle at 18% 12%, rgba(208, 118, 255, 0.22), transparent 42%),
|
||||
rgba(115, 45, 255, 0.22);
|
||||
box-shadow: 0 0 30rpx rgba(167, 60, 255, 0.48), inset 0 0 22rpx rgba(190, 92, 255, 0.18);
|
||||
}
|
||||
|
||||
.tab-icon {
|
||||
position: relative;
|
||||
width: 34rpx;
|
||||
height: 34rpx;
|
||||
color: #d875ff;
|
||||
font-size: 34rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.gear-icon {
|
||||
position: relative;
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
border: 3rpx solid currentColor;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
.gear-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 8rpx;
|
||||
top: 8rpx;
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.spark,
|
||||
.panel-spark,
|
||||
.book-sparkle {
|
||||
position: absolute;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.spark::before,
|
||||
.spark::after,
|
||||
.panel-spark::before,
|
||||
.panel-spark::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
border-radius: 999rpx;
|
||||
background: currentColor;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.spark::before,
|
||||
.panel-spark::before {
|
||||
width: 4rpx;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.spark::after,
|
||||
.panel-spark::after {
|
||||
width: 100%;
|
||||
height: 4rpx;
|
||||
}
|
||||
|
||||
.spark.main {
|
||||
left: 8rpx;
|
||||
top: 7rpx;
|
||||
width: 19rpx;
|
||||
height: 19rpx;
|
||||
}
|
||||
|
||||
.spark.dot {
|
||||
right: 3rpx;
|
||||
top: 2rpx;
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.panel-sparkles {
|
||||
position: relative;
|
||||
width: 38rpx;
|
||||
height: 38rpx;
|
||||
flex-shrink: 0;
|
||||
color: #cf78ff;
|
||||
}
|
||||
|
||||
.panel-spark.large {
|
||||
left: 9rpx;
|
||||
top: 8rpx;
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
|
||||
.panel-spark.small {
|
||||
left: 1rpx;
|
||||
bottom: 3rpx;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
opacity: 0.78;
|
||||
}
|
||||
|
||||
.bulb-icon {
|
||||
position: relative;
|
||||
width: 26rpx;
|
||||
height: 30rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.bulb-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 5rpx;
|
||||
top: 2rpx;
|
||||
width: 16rpx;
|
||||
height: 18rpx;
|
||||
border: 3rpx solid currentColor;
|
||||
border-radius: 50% 50% 45% 45%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bulb-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 9rpx;
|
||||
bottom: 2rpx;
|
||||
width: 10rpx;
|
||||
height: 8rpx;
|
||||
border-top: 3rpx solid currentColor;
|
||||
border-bottom: 3rpx solid currentColor;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.mic-icon {
|
||||
position: relative;
|
||||
width: 24rpx;
|
||||
height: 30rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mic-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 7rpx;
|
||||
top: 1rpx;
|
||||
width: 10rpx;
|
||||
height: 19rpx;
|
||||
border: 3rpx solid currentColor;
|
||||
border-radius: 999rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.mic-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 4rpx;
|
||||
bottom: 1rpx;
|
||||
width: 16rpx;
|
||||
height: 11rpx;
|
||||
border: 3rpx solid currentColor;
|
||||
border-top: 0;
|
||||
border-radius: 0 0 999rpx 999rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.refresh-icon {
|
||||
position: relative;
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
border: 4rpx solid currentColor;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.refresh-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 5rpx;
|
||||
border: 3rpx solid currentColor;
|
||||
border-left-color: transparent;
|
||||
border-radius: 50%;
|
||||
transform: rotate(-28deg);
|
||||
}
|
||||
|
||||
.refresh-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 3rpx;
|
||||
top: 3rpx;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8rpx solid currentColor;
|
||||
border-top: 5rpx solid transparent;
|
||||
border-bottom: 5rpx solid transparent;
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
|
||||
.tab-title,
|
||||
@@ -395,45 +607,51 @@ const selectScript = async (id) => {
|
||||
}
|
||||
|
||||
.tab-title {
|
||||
font-size: 27rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.tab-sub {
|
||||
margin-top: 4rpx;
|
||||
font-size: 20rpx;
|
||||
font-size: 19rpx;
|
||||
color: rgba(224, 214, 243, 0.52);
|
||||
}
|
||||
|
||||
.prompt-panel,
|
||||
.custom-panel {
|
||||
border-radius: 32rpx;
|
||||
padding: 30rpx;
|
||||
padding: 28rpx;
|
||||
border-color: rgba(173, 84, 255, 0.36);
|
||||
background:
|
||||
radial-gradient(circle at 16% 0%, rgba(143, 64, 255, 0.18), transparent 42%),
|
||||
rgba(12, 12, 42, 0.62);
|
||||
box-shadow: inset 0 0 34rpx rgba(132, 56, 255, 0.14), 0 0 26rpx rgba(140, 55, 255, 0.12);
|
||||
}
|
||||
|
||||
.panel-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14rpx;
|
||||
gap: 12rpx;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
color: #c783ff;
|
||||
font-size: 29rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.prompt-box {
|
||||
width: 100%;
|
||||
height: 216rpx;
|
||||
height: 214rpx;
|
||||
box-sizing: border-box;
|
||||
margin-top: 28rpx;
|
||||
padding: 30rpx;
|
||||
margin-top: 26rpx;
|
||||
padding: 26rpx 28rpx;
|
||||
border-radius: 24rpx;
|
||||
border: 1rpx solid rgba(151, 111, 255, 0.26);
|
||||
background: rgba(13, 15, 43, 0.72);
|
||||
background: rgba(12, 14, 42, 0.86);
|
||||
color: #f7f1ff;
|
||||
font-size: 27rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
@@ -442,50 +660,28 @@ const selectScript = async (id) => {
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
gap: 16rpx;
|
||||
align-items: center;
|
||||
margin-top: 18rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.counter {
|
||||
color: rgba(222, 211, 240, 0.62);
|
||||
font-size: 23rpx;
|
||||
}
|
||||
|
||||
.style-scroll {
|
||||
width: 100%;
|
||||
margin-top: 20rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.style-row {
|
||||
display: inline-flex;
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
.style-chip {
|
||||
display: inline-flex;
|
||||
height: 52rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 999rpx;
|
||||
align-items: center;
|
||||
color: rgba(224, 214, 243, 0.66);
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.style-chip.active {
|
||||
color: #fff;
|
||||
background: rgba(152, 62, 255, 0.34);
|
||||
border-color: rgba(191, 91, 255, 0.82);
|
||||
}
|
||||
|
||||
.generate-btn {
|
||||
width: 100%;
|
||||
height: 104rpx;
|
||||
margin-top: 28rpx;
|
||||
margin-top: 24rpx;
|
||||
border-radius: 28rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 28rpx;
|
||||
overflow: visible;
|
||||
background:
|
||||
radial-gradient(circle at 92% 44%, rgba(244, 187, 255, 0.42), transparent 22%),
|
||||
linear-gradient(135deg, #b64cff 0%, #762fff 48%, #4a20ff 100%);
|
||||
box-shadow: 0 0 34rpx rgba(168, 85, 247, 0.58), inset 0 1rpx 0 rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.generate-title,
|
||||
@@ -495,38 +691,72 @@ const selectScript = async (id) => {
|
||||
}
|
||||
|
||||
.generate-title {
|
||||
font-size: 30rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.generate-sub {
|
||||
margin-top: 8rpx;
|
||||
font-size: 22rpx;
|
||||
font-size: 21rpx;
|
||||
color: rgba(255, 255, 255, 0.74);
|
||||
}
|
||||
|
||||
.planet-badge {
|
||||
position: relative;
|
||||
width: 68rpx;
|
||||
height: 68rpx;
|
||||
width: 78rpx;
|
||||
height: 78rpx;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, #e8b7ff, #8b37ff 62%, #4c1d95);
|
||||
box-shadow: 0 0 24rpx rgba(219, 143, 255, 0.7);
|
||||
background: radial-gradient(circle at 36% 28%, #f0c7ff, #a64cff 62%, #5a22d6);
|
||||
box-shadow: 0 0 30rpx rgba(219, 143, 255, 0.82);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.planet-badge::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -12rpx;
|
||||
top: 27rpx;
|
||||
width: 90rpx;
|
||||
height: 20rpx;
|
||||
left: -20rpx;
|
||||
top: 34rpx;
|
||||
width: 124rpx;
|
||||
height: 24rpx;
|
||||
border: 4rpx solid rgba(231, 201, 255, 0.72);
|
||||
border-top-color: transparent;
|
||||
border-radius: 50%;
|
||||
transform: rotate(-18deg);
|
||||
}
|
||||
|
||||
.planet-face {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.planet-eye {
|
||||
position: absolute;
|
||||
top: 31rpx;
|
||||
width: 7rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 999rpx;
|
||||
background: #16062f;
|
||||
}
|
||||
|
||||
.planet-eye.left {
|
||||
left: 27rpx;
|
||||
}
|
||||
|
||||
.planet-eye.right {
|
||||
right: 27rpx;
|
||||
}
|
||||
|
||||
.planet-mouth {
|
||||
position: absolute;
|
||||
left: 34rpx;
|
||||
bottom: 23rpx;
|
||||
width: 14rpx;
|
||||
height: 9rpx;
|
||||
border-bottom: 3rpx solid #16062f;
|
||||
border-radius: 0 0 999rpx 999rpx;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
@@ -577,43 +807,44 @@ const selectScript = async (id) => {
|
||||
|
||||
.section-title {
|
||||
color: #c684ff;
|
||||
font-size: 31rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.refresh {
|
||||
color: #c99fff;
|
||||
font-size: 24rpx;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.recommend-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 18rpx;
|
||||
gap: 16rpx;
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.recommend-card {
|
||||
min-height: 118rpx;
|
||||
min-height: 110rpx;
|
||||
border-radius: 22rpx;
|
||||
padding: 24rpx;
|
||||
padding: 22rpx;
|
||||
background: rgba(12, 12, 42, 0.54);
|
||||
}
|
||||
|
||||
.recommend-text {
|
||||
display: block;
|
||||
color: rgba(248, 244, 255, 0.9);
|
||||
font-size: 25rpx;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.recommend-tag {
|
||||
display: inline-flex;
|
||||
margin-top: 18rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
margin-top: 16rpx;
|
||||
padding: 7rpx 14rpx;
|
||||
border-radius: 999rpx;
|
||||
color: #d985ff;
|
||||
background: rgba(151, 66, 255, 0.18);
|
||||
font-size: 21rpx;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.recent-section,
|
||||
@@ -625,8 +856,9 @@ const selectScript = async (id) => {
|
||||
|
||||
.script-card {
|
||||
border-radius: 24rpx;
|
||||
padding: 22rpx;
|
||||
gap: 20rpx;
|
||||
padding: 20rpx;
|
||||
gap: 18rpx;
|
||||
background: rgba(12, 12, 42, 0.6);
|
||||
}
|
||||
|
||||
.script-card.selected {
|
||||
@@ -634,15 +866,15 @@ const selectScript = async (id) => {
|
||||
}
|
||||
|
||||
.script-cover {
|
||||
width: 92rpx;
|
||||
height: 92rpx;
|
||||
width: 86rpx;
|
||||
height: 86rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 18rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 42rpx;
|
||||
font-size: 38rpx;
|
||||
font-weight: 900;
|
||||
background: linear-gradient(135deg, #7d35ff, #1bb7ff);
|
||||
}
|
||||
@@ -660,20 +892,20 @@ const selectScript = async (id) => {
|
||||
|
||||
.script-title {
|
||||
color: #fff;
|
||||
font-size: 27rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.script-date {
|
||||
margin-top: 8rpx;
|
||||
margin-top: 7rpx;
|
||||
color: rgba(218, 204, 243, 0.62);
|
||||
font-size: 22rpx;
|
||||
font-size: 21rpx;
|
||||
}
|
||||
|
||||
.script-summary {
|
||||
margin-top: 8rpx;
|
||||
margin-top: 7rpx;
|
||||
color: rgba(218, 204, 243, 0.68);
|
||||
font-size: 23rpx;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.45;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -682,7 +914,7 @@ const selectScript = async (id) => {
|
||||
|
||||
.empty-panel {
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx;
|
||||
padding: 26rpx;
|
||||
color: rgba(230, 218, 250, 0.66);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -17,24 +17,27 @@
|
||||
<MusicPlayer ref="musicPlayer" />
|
||||
|
||||
<view class="bottom-nav">
|
||||
<view class="nav-inner" :style="{ paddingBottom: safeAreaBottom + 'px' }">
|
||||
<view class="nav-inner">
|
||||
<view class="nav-item" :class="{ active: activeTab === 'record' }" @click="switchTab('record')">
|
||||
<view class="planet-icon">
|
||||
<view class="tab-icon planet-ring-icon">
|
||||
<view class="planet-core"></view>
|
||||
<view class="planet-ring"></view>
|
||||
</view>
|
||||
<text>人生轨迹</text>
|
||||
</view>
|
||||
<view class="nav-item" :class="{ active: activeTab === 'script' }" @click="switchTab('script')">
|
||||
<view class="book-icon">
|
||||
<view></view>
|
||||
<view></view>
|
||||
<view class="tab-icon book-star-icon">
|
||||
<view class="book-page left"></view>
|
||||
<view class="book-page right"></view>
|
||||
<view class="book-sparkle"></view>
|
||||
</view>
|
||||
<text>爽文生成</text>
|
||||
</view>
|
||||
<view class="nav-item" :class="{ active: activeTab === 'mine' }" @click="switchTab('mine')">
|
||||
<view class="smile-icon">
|
||||
<view class="eye left"></view>
|
||||
<view class="eye right"></view>
|
||||
<view class="tab-icon smile-face-icon">
|
||||
<view class="smile-eye left"></view>
|
||||
<view class="smile-eye right"></view>
|
||||
<view class="smile-mouth"></view>
|
||||
</view>
|
||||
<text>我的</text>
|
||||
</view>
|
||||
@@ -156,7 +159,7 @@ onUnmounted(() => {
|
||||
height: 0;
|
||||
min-height: 0;
|
||||
box-sizing: border-box;
|
||||
padding: 0 28rpx 156rpx;
|
||||
padding: 0 28rpx 132rpx;
|
||||
}
|
||||
|
||||
.bottom-nav {
|
||||
@@ -171,16 +174,21 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.nav-inner {
|
||||
height: 108rpx;
|
||||
height: 104rpx;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
align-items: center;
|
||||
border-radius: 34rpx;
|
||||
border: 1rpx solid rgba(153, 112, 255, 0.32);
|
||||
background: rgba(11, 9, 35, 0.84);
|
||||
box-shadow: inset 0 0 38rpx rgba(129, 65, 255, 0.12), 0 18rpx 70rpx rgba(0, 0, 0, 0.36);
|
||||
backdrop-filter: blur(26rpx);
|
||||
-webkit-backdrop-filter: blur(26rpx);
|
||||
border: 1rpx solid rgba(150, 95, 255, 0.26);
|
||||
background:
|
||||
radial-gradient(circle at 18% 14%, rgba(137, 78, 255, 0.18), transparent 36%),
|
||||
linear-gradient(180deg, rgba(20, 13, 52, 0.9), rgba(12, 7, 34, 0.94));
|
||||
box-shadow:
|
||||
inset 0 1rpx 0 rgba(255, 255, 255, 0.08),
|
||||
inset 0 0 34rpx rgba(130, 72, 255, 0.12),
|
||||
0 14rpx 42rpx rgba(0, 0, 0, 0.36);
|
||||
backdrop-filter: blur(28rpx);
|
||||
-webkit-backdrop-filter: blur(28rpx);
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
@@ -189,93 +197,161 @@ onUnmounted(() => {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
color: rgba(216, 208, 235, 0.58);
|
||||
font-size: 23rpx;
|
||||
font-weight: 600;
|
||||
transition: transform 0.22s ease, color 0.22s ease;
|
||||
gap: 5rpx;
|
||||
color: rgba(174, 165, 199, 0.72);
|
||||
font-size: 22rpx;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
transition: transform 0.32s cubic-bezier(0.23, 1, 0.32, 1), color 0.32s ease, text-shadow 0.32s ease;
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
color: #b86cff;
|
||||
transform: translateY(-4rpx);
|
||||
text-shadow: 0 0 22rpx rgba(178, 91, 255, 0.8);
|
||||
color: #a855f7;
|
||||
transform: translateY(-6rpx);
|
||||
text-shadow: 0 0 24rpx rgba(168, 85, 247, 0.86);
|
||||
}
|
||||
|
||||
.planet-icon,
|
||||
.book-icon,
|
||||
.smile-icon {
|
||||
.tab-icon {
|
||||
position: relative;
|
||||
width: 46rpx;
|
||||
height: 42rpx;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
.planet-core {
|
||||
position: absolute;
|
||||
left: 10rpx;
|
||||
top: 8rpx;
|
||||
width: 26rpx;
|
||||
height: 26rpx;
|
||||
top: 11rpx;
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
box-shadow: 0 0 24rpx currentColor;
|
||||
box-shadow: 0 0 26rpx rgba(168, 85, 247, 0.74);
|
||||
}
|
||||
|
||||
.planet-icon::before {
|
||||
.planet-core::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 2rpx;
|
||||
top: 14rpx;
|
||||
width: 42rpx;
|
||||
height: 14rpx;
|
||||
left: 9rpx;
|
||||
top: 7rpx;
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.58);
|
||||
}
|
||||
|
||||
.planet-ring {
|
||||
position: absolute;
|
||||
left: 1rpx;
|
||||
top: 16rpx;
|
||||
width: 48rpx;
|
||||
height: 16rpx;
|
||||
border: 4rpx solid currentColor;
|
||||
border-top-color: transparent;
|
||||
border-radius: 50%;
|
||||
transform: rotate(-22deg);
|
||||
}
|
||||
|
||||
.book-icon {
|
||||
display: flex;
|
||||
gap: 4rpx;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.book-icon view {
|
||||
width: 17rpx;
|
||||
height: 34rpx;
|
||||
border-radius: 6rpx 3rpx 3rpx 6rpx;
|
||||
border: 4rpx solid currentColor;
|
||||
box-shadow: 0 0 18rpx currentColor;
|
||||
}
|
||||
|
||||
.smile-icon {
|
||||
border-radius: 50%;
|
||||
border: 5rpx solid currentColor;
|
||||
box-sizing: border-box;
|
||||
transform: rotate(-22deg);
|
||||
opacity: 0.96;
|
||||
}
|
||||
|
||||
.eye {
|
||||
.book-page {
|
||||
position: absolute;
|
||||
top: 12rpx;
|
||||
width: 6rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
top: 13rpx;
|
||||
width: 20rpx;
|
||||
height: 26rpx;
|
||||
border: 4rpx solid currentColor;
|
||||
box-sizing: border-box;
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.eye.left { left: 12rpx; }
|
||||
.eye.right { right: 12rpx; }
|
||||
.book-page.left {
|
||||
left: 5rpx;
|
||||
border-radius: 8rpx 3rpx 3rpx 8rpx;
|
||||
transform: skewY(5deg);
|
||||
}
|
||||
|
||||
.smile-icon::after {
|
||||
.book-page.right {
|
||||
right: 5rpx;
|
||||
border-radius: 3rpx 8rpx 8rpx 3rpx;
|
||||
transform: skewY(-5deg);
|
||||
}
|
||||
|
||||
.book-star-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 12rpx;
|
||||
right: 12rpx;
|
||||
bottom: 10rpx;
|
||||
left: 23rpx;
|
||||
top: 15rpx;
|
||||
width: 3rpx;
|
||||
height: 23rpx;
|
||||
border-radius: 999rpx;
|
||||
background: currentColor;
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.book-sparkle {
|
||||
position: absolute;
|
||||
right: 4rpx;
|
||||
top: 4rpx;
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.book-sparkle::before,
|
||||
.book-sparkle::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
border-radius: 999rpx;
|
||||
background: currentColor;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.book-sparkle::before {
|
||||
width: 4rpx;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.book-sparkle::after {
|
||||
width: 100%;
|
||||
height: 4rpx;
|
||||
}
|
||||
|
||||
.smile-face-icon {
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
box-shadow: 0 0 18rpx rgba(174, 165, 199, 0.22);
|
||||
}
|
||||
|
||||
.nav-item.active .smile-face-icon {
|
||||
box-shadow: 0 0 24rpx rgba(168, 85, 247, 0.62);
|
||||
}
|
||||
|
||||
.smile-eye {
|
||||
position: absolute;
|
||||
top: 16rpx;
|
||||
width: 7rpx;
|
||||
height: 7rpx;
|
||||
border-radius: 50%;
|
||||
background: #100719;
|
||||
}
|
||||
|
||||
.smile-eye.left {
|
||||
left: 13rpx;
|
||||
}
|
||||
|
||||
.smile-eye.right {
|
||||
right: 13rpx;
|
||||
}
|
||||
|
||||
.smile-mouth {
|
||||
position: absolute;
|
||||
left: 16rpx;
|
||||
right: 16rpx;
|
||||
bottom: 14rpx;
|
||||
height: 8rpx;
|
||||
border-bottom: 4rpx solid currentColor;
|
||||
border-radius: 0 0 20rpx 20rpx;
|
||||
border-bottom: 4rpx solid #100719;
|
||||
border-radius: 0 0 18rpx 18rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,92 +5,133 @@
|
||||
|
||||
<view class="topbar">
|
||||
<text class="back" @click="goBack">‹</text>
|
||||
<text class="title">{{ isEdit ? '编辑资料' : '初始化档案' }} ✦</text>
|
||||
<text class="title">编辑资料 <text class="gold">✦</text></text>
|
||||
<text class="save" @click="saveProfile">保存</text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="scroll" scroll-y :show-scrollbar="false">
|
||||
<view class="hero-card kos-card">
|
||||
<view class="avatar">{{ avatarText }}</view>
|
||||
<view class="hero-card glass-card">
|
||||
<view class="avatar-wrap">
|
||||
<image class="avatar-img" :src="avatarUrl" mode="aspectFill" />
|
||||
<view class="avatar-edit" @click="chooseAvatar">
|
||||
<view class="pen-icon"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="hero-info">
|
||||
<text class="hero-name">{{ form.nickname || 'Zoey' }} ✦</text>
|
||||
<view class="hero-name-row">
|
||||
<text class="hero-name">{{ form.nickname || 'Zoey' }}</text>
|
||||
<text class="gold hero-star">✦</text>
|
||||
</view>
|
||||
<text class="hero-sub">正在成为更清晰的自己</text>
|
||||
<text class="quote">生活是自己的,选择也是。</text>
|
||||
<view class="hero-line"></view>
|
||||
<text class="hero-quote">生活是自己的,选择也是。”</text>
|
||||
</view>
|
||||
<view class="hero-planet">
|
||||
<view class="planet-core"></view>
|
||||
<view class="planet-ring"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel kos-card">
|
||||
<view class="panel-title">基础信息</view>
|
||||
<view class="line-field">
|
||||
<text class="field-label">昵称</text>
|
||||
<input class="line-input" v-model="form.nickname" placeholder="输入你的昵称" placeholder-class="placeholder" />
|
||||
<view class="panel glass-card">
|
||||
<view class="section-head">
|
||||
<view class="section-icon basic-icon"></view>
|
||||
<text class="section-title">基础信息</text>
|
||||
</view>
|
||||
<view class="line-field">
|
||||
<text class="field-label">性别</text>
|
||||
<view class="profile-row">
|
||||
<text class="row-label">昵称</text>
|
||||
<input class="row-input" v-model="form.nickname" placeholder="输入昵称" placeholder-class="placeholder" />
|
||||
<text class="chevron">›</text>
|
||||
</view>
|
||||
<view class="profile-row">
|
||||
<text class="row-label">性别</text>
|
||||
<view class="gender-row">
|
||||
<text v-for="item in genderOptions" :key="item" class="choice" :class="{ active: form.gender === item }" @click="form.gender = item">{{ item }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="line-field">
|
||||
<text class="field-label">生日</text>
|
||||
<picker mode="date" :value="birthday" @change="onBirthday">
|
||||
<view class="line-value">{{ birthday || '选择生日' }} ›</view>
|
||||
<view class="profile-row">
|
||||
<text class="row-label">生日</text>
|
||||
<picker class="row-picker" mode="date" :value="birthday" @change="onBirthday">
|
||||
<view class="row-value">{{ birthdayDisplay }}</view>
|
||||
</picker>
|
||||
<text class="chevron">›</text>
|
||||
</view>
|
||||
<view class="line-field">
|
||||
<text class="field-label">所在城市</text>
|
||||
<input class="line-input" v-model="form.city" placeholder="填写城市" placeholder-class="placeholder" />
|
||||
<view class="profile-row">
|
||||
<text class="row-label">所在城市</text>
|
||||
<input class="row-input" v-model="form.city" placeholder="填写城市" placeholder-class="placeholder" />
|
||||
<text class="chevron">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="dual-panel kos-card">
|
||||
<view class="select-block">
|
||||
<view class="block-title">星座</view>
|
||||
<view class="zodiac-grid">
|
||||
<view
|
||||
v-for="item in zodiacOptions"
|
||||
:key="item.name"
|
||||
class="zodiac-item"
|
||||
:class="{ active: form.zodiac === item.name }"
|
||||
@click="form.zodiac = item.name"
|
||||
>
|
||||
<text class="zodiac-symbol">{{ item.symbol }}</text>
|
||||
<text>{{ item.name }}</text>
|
||||
<view class="astro-panel">
|
||||
<view class="astro-col">
|
||||
<view class="astro-title-row">
|
||||
<view class="zodiac-head-icon">♋</view>
|
||||
<text class="astro-title">星座</text>
|
||||
<text class="astro-current">{{ form.zodiac || '巨蟹座' }}</text>
|
||||
<text class="chevron">›</text>
|
||||
</view>
|
||||
<text class="select-title">选择星座</text>
|
||||
<view class="zodiac-grid">
|
||||
<view
|
||||
v-for="item in zodiacOptions"
|
||||
:key="item.name"
|
||||
class="zodiac-item"
|
||||
:class="{ active: form.zodiac === item.name }"
|
||||
@click="form.zodiac = item.name"
|
||||
>
|
||||
<view class="zodiac-bubble">{{ item.symbol }}</view>
|
||||
<text>{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="astro-col mbti-col">
|
||||
<view class="astro-title-row">
|
||||
<view class="mbti-head-icon"></view>
|
||||
<text class="astro-title">MBTI</text>
|
||||
<text class="astro-current">{{ form.mbti || 'ENTJ' }}</text>
|
||||
<text class="chevron">›</text>
|
||||
</view>
|
||||
<text class="select-title">选择MBTI</text>
|
||||
<view class="mbti-grid">
|
||||
<text
|
||||
v-for="item in mbtiOptions"
|
||||
:key="item"
|
||||
class="mbti-chip"
|
||||
:class="{ active: form.mbti === item }"
|
||||
@click="form.mbti = item"
|
||||
>{{ item }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="select-block mbti-block">
|
||||
<view class="block-title">MBTI</view>
|
||||
<view class="mbti-grid">
|
||||
<text
|
||||
v-for="item in mbtiOptions"
|
||||
:key="item"
|
||||
class="mbti-chip"
|
||||
:class="{ active: form.mbti === item }"
|
||||
@click="form.mbti = item"
|
||||
>{{ item }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel glass-card">
|
||||
<view class="section-head">
|
||||
<view class="section-icon job-title-icon"></view>
|
||||
<text class="section-title">职业信息</text>
|
||||
</view>
|
||||
<view class="profile-row">
|
||||
<text class="row-label">职业</text>
|
||||
<input class="row-input" v-model="form.profession" placeholder="产品经理" placeholder-class="placeholder" />
|
||||
<text class="chevron">›</text>
|
||||
</view>
|
||||
<view class="profile-row">
|
||||
<text class="row-label">行业</text>
|
||||
<input class="row-input" v-model="form.industry" placeholder="互联网" placeholder-class="placeholder" />
|
||||
<text class="chevron">›</text>
|
||||
</view>
|
||||
<view class="profile-row">
|
||||
<text class="row-label">公司(可选)</text>
|
||||
<input class="row-input muted" v-model="form.company" placeholder="填写公司名称" placeholder-class="placeholder" />
|
||||
<text class="chevron">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel kos-card">
|
||||
<view class="panel-title">职业信息</view>
|
||||
<view class="line-field">
|
||||
<text class="field-label">职业</text>
|
||||
<input class="line-input" v-model="form.profession" placeholder="产品经理" placeholder-class="placeholder" />
|
||||
<view class="panel glass-card">
|
||||
<view class="section-head">
|
||||
<view class="section-icon smile-title-icon"></view>
|
||||
<text class="section-title">性格标签</text>
|
||||
<text class="section-hint">(最多选择5个)</text>
|
||||
</view>
|
||||
<view class="line-field">
|
||||
<text class="field-label">行业</text>
|
||||
<input class="line-input" v-model="form.industry" placeholder="互联网" placeholder-class="placeholder" />
|
||||
</view>
|
||||
<view class="line-field">
|
||||
<text class="field-label">公司(可选)</text>
|
||||
<input class="line-input" v-model="form.company" placeholder="填写公司名称" placeholder-class="placeholder" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel kos-card">
|
||||
<view class="panel-title">性格标签(最多选择5个)</view>
|
||||
<view class="tag-grid">
|
||||
<text
|
||||
v-for="tag in personalityTags"
|
||||
@@ -103,10 +144,14 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel kos-card">
|
||||
<view class="panel-head">
|
||||
<text class="panel-title">兴趣爱好(最多选择5个)</text>
|
||||
<text class="custom">+ 自定义兴趣</text>
|
||||
<view class="panel glass-card">
|
||||
<view class="section-head with-action">
|
||||
<view class="section-title-wrap">
|
||||
<view class="section-icon heart-title-icon"></view>
|
||||
<text class="section-title">兴趣爱好</text>
|
||||
<text class="section-hint">(最多选择5个)</text>
|
||||
</view>
|
||||
<text class="custom" @click="addCustomHobby">+ 自定义兴趣</text>
|
||||
</view>
|
||||
<view class="tag-grid">
|
||||
<text
|
||||
@@ -119,18 +164,21 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel kos-card">
|
||||
<view class="panel-title">个人简介</view>
|
||||
<textarea class="bio" v-model="form.future.ideal" maxlength="200" placeholder="热爱阅读和旅行,喜欢用文字和镜头记录生活。相信真诚和努力能让世界变得更美好。" placeholder-class="placeholder" />
|
||||
<view class="panel glass-card bio-panel">
|
||||
<view class="section-head">
|
||||
<view class="section-icon bio-title-icon"></view>
|
||||
<text class="section-title">个人简介</text>
|
||||
</view>
|
||||
<textarea
|
||||
class="bio"
|
||||
v-model="form.future.ideal"
|
||||
maxlength="200"
|
||||
:placeholder="bioPlaceholder"
|
||||
placeholder-class="placeholder"
|
||||
/>
|
||||
<text class="bio-count">{{ (form.future.ideal || '').length }}/200</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="bottom-bar">
|
||||
<button class="submit kos-primary" :loading="saving" :disabled="saving || !form.nickname.trim()" @click="saveProfile">
|
||||
{{ saving ? '正在保存' : '保存生命档案' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -143,12 +191,23 @@ const statusBarHeight = ref(20)
|
||||
const isEdit = ref(false)
|
||||
const saving = ref(false)
|
||||
const birthday = ref('')
|
||||
const avatarLocal = ref('')
|
||||
const bioPlaceholder = '热爱阅读和旅行,喜欢用文字和镜头记录生活。\n相信真诚和努力能让世界变得更美好。'
|
||||
|
||||
const genderOptions = ['女', '男', '不透露']
|
||||
const zodiacOptions = [
|
||||
{ name: '白羊座', symbol: '♈' }, { name: '金牛座', symbol: '♉' }, { name: '双子座', symbol: '♊' }, { name: '巨蟹座', symbol: '♋' },
|
||||
{ name: '狮子座', symbol: '♌' }, { name: '处女座', symbol: '♍' }, { name: '天秤座', symbol: '♎' }, { name: '天蝎座', symbol: '♏' },
|
||||
{ name: '射手座', symbol: '♐' }, { name: '摩羯座', symbol: '♑' }, { name: '水瓶座', symbol: '♒' }, { name: '双鱼座', symbol: '♓' }
|
||||
{ name: '白羊座', symbol: '♈' },
|
||||
{ name: '金牛座', symbol: '♉' },
|
||||
{ name: '双子座', symbol: '♊' },
|
||||
{ name: '巨蟹座', symbol: '♋' },
|
||||
{ name: '狮子座', symbol: '♌' },
|
||||
{ name: '处女座', symbol: '♍' },
|
||||
{ name: '天秤座', symbol: '♎' },
|
||||
{ name: '天蝎座', symbol: '♏' },
|
||||
{ name: '射手座', symbol: '♐' },
|
||||
{ name: '摩羯座', symbol: '♑' },
|
||||
{ name: '水瓶座', symbol: '♒' },
|
||||
{ name: '双鱼座', symbol: '♓' }
|
||||
]
|
||||
const mbtiOptions = ['INTJ', 'INTP', 'ENTJ', 'ENTP', 'INFJ', 'INFP', 'ENFJ', 'ENFP', 'ISTJ', 'ISFJ', 'ESTJ', 'ESFJ', 'ISTP', 'ISFP', 'ESTP', 'ESFP']
|
||||
const personalityTags = ['理性', '感性', '乐观', '独立', '有创造力', '坚韧', '细腻', '好奇', '内敛', '冒险', '自由']
|
||||
@@ -171,27 +230,37 @@ const form = reactive({
|
||||
future: { vision: '', ideal: '' }
|
||||
})
|
||||
|
||||
const avatarText = computed(() => (form.nickname || 'Z').slice(0, 1))
|
||||
const avatarUrl = computed(() => {
|
||||
if (avatarLocal.value) return avatarLocal.value
|
||||
const nickname = form.nickname || 'Zoey'
|
||||
return `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(nickname)}&backgroundColor=b982ff`
|
||||
})
|
||||
|
||||
const birthdayDisplay = computed(() => {
|
||||
if (!birthday.value) return '1998年06月18日'
|
||||
const [year, month, day] = birthday.value.split('-')
|
||||
return `${year}年${month}月${day}日`
|
||||
})
|
||||
|
||||
const syncFromStore = () => {
|
||||
const source = store.userProfile || store.registrationData || {}
|
||||
Object.assign(form, {
|
||||
nickname: source.nickname || '',
|
||||
gender: source.gender || '',
|
||||
zodiac: source.zodiac || '',
|
||||
mbti: source.mbti || '',
|
||||
profession: source.profession || '',
|
||||
city: source.city || '',
|
||||
industry: source.industry || '',
|
||||
gender: source.gender || '女',
|
||||
zodiac: source.zodiac || '巨蟹座',
|
||||
mbti: source.mbti || 'ENTJ',
|
||||
profession: source.profession || '产品经理',
|
||||
city: source.city || '上海市',
|
||||
industry: source.industry || '互联网',
|
||||
company: source.company || '',
|
||||
personalityTags: Array.isArray(source.personalityTags) ? [...source.personalityTags] : [],
|
||||
hobbies: Array.isArray(source.hobbies) ? [...source.hobbies] : [],
|
||||
personalityTags: Array.isArray(source.personalityTags) && source.personalityTags.length ? [...source.personalityTags] : ['理性', '乐观', '独立', '有创造力', '坚韧'],
|
||||
hobbies: Array.isArray(source.hobbies) && source.hobbies.length ? [...source.hobbies] : ['阅读', '旅行', '音乐'],
|
||||
childhood: { date: source.childhood?.date || '', text: source.childhood?.text || '' },
|
||||
joy: { date: source.joy?.date || '', text: source.joy?.text || '' },
|
||||
low: { date: source.low?.date || '', text: source.low?.text || '' },
|
||||
future: { vision: source.future?.vision || '', ideal: source.future?.ideal || '' }
|
||||
future: { vision: source.future?.vision || '', ideal: source.future?.ideal || '热爱阅读和旅行,喜欢用文字和镜头记录生活。\n相信真诚和努力能让世界变得更美好。' }
|
||||
})
|
||||
birthday.value = source.birthday || ''
|
||||
birthday.value = source.birthday || '1998-06-18'
|
||||
}
|
||||
|
||||
const onBirthday = (event) => {
|
||||
@@ -211,6 +280,32 @@ const toggleList = (list, tag, max) => {
|
||||
list.push(tag)
|
||||
}
|
||||
|
||||
const chooseAvatar = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
avatarLocal.value = res.tempFilePaths?.[0] || ''
|
||||
uni.showToast({ title: '头像已更新到本地预览', icon: 'none' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const addCustomHobby = () => {
|
||||
uni.showModal({
|
||||
title: '自定义兴趣',
|
||||
editable: true,
|
||||
placeholderText: '输入兴趣名称',
|
||||
success: (res) => {
|
||||
const value = String(res.content || '').trim()
|
||||
if (!res.confirm || !value) return
|
||||
if (!hobbyOptions.includes(value)) hobbyOptions.push(value)
|
||||
toggleList(form.hobbies, value, 5)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const saveProfile = async () => {
|
||||
if (!form.nickname.trim() || saving.value) return
|
||||
saving.value = true
|
||||
@@ -259,48 +354,52 @@ onMounted(() => {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(circle at 86% 10%, rgba(124, 58, 237, 0.3), transparent 30%),
|
||||
radial-gradient(circle at 10% 32%, rgba(48, 112, 255, 0.16), transparent 28%),
|
||||
linear-gradient(180deg, #07091d 0%, #07031a 52%, #04020e 100%);
|
||||
radial-gradient(circle at 84% 3%, rgba(87, 122, 255, 0.12), transparent 28%),
|
||||
radial-gradient(circle at 14% 26%, rgba(130, 71, 255, 0.16), transparent 30%),
|
||||
linear-gradient(180deg, #05081b 0%, #07031a 52%, #03020d 100%);
|
||||
}
|
||||
|
||||
.status-space,
|
||||
.topbar,
|
||||
.scroll,
|
||||
.bottom-bar {
|
||||
.scroll {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.status-space,
|
||||
.topbar,
|
||||
.bottom-bar {
|
||||
.topbar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
height: 92rpx;
|
||||
display: grid;
|
||||
grid-template-columns: 80rpx 1fr 80rpx;
|
||||
grid-template-columns: 90rpx 1fr 90rpx;
|
||||
align-items: center;
|
||||
padding: 0 28rpx;
|
||||
padding: 0 32rpx;
|
||||
}
|
||||
|
||||
.back {
|
||||
font-size: 66rpx;
|
||||
color: #fff;
|
||||
font-size: 68rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 36rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.save,
|
||||
.custom {
|
||||
color: #c06dff;
|
||||
font-size: 27rpx;
|
||||
.gold {
|
||||
color: #ffd58c;
|
||||
text-shadow: 0 0 20rpx rgba(255, 202, 125, 0.45);
|
||||
}
|
||||
|
||||
.save {
|
||||
color: #b94cff;
|
||||
font-size: 28rpx;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@@ -312,177 +411,443 @@ onMounted(() => {
|
||||
padding: 0 28rpx 28rpx;
|
||||
}
|
||||
|
||||
.hero-card,
|
||||
.panel,
|
||||
.dual-panel {
|
||||
border-radius: 28rpx;
|
||||
margin-bottom: 22rpx;
|
||||
padding: 28rpx;
|
||||
.glass-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border: 1rpx solid rgba(124, 75, 255, 0.34);
|
||||
background:
|
||||
radial-gradient(circle at 92% 12%, rgba(104, 66, 255, 0.14), transparent 34%),
|
||||
rgba(10, 13, 43, 0.74);
|
||||
box-shadow: inset 0 0 34rpx rgba(123, 60, 255, 0.08), 0 14rpx 48rpx rgba(0, 0, 0, 0.22);
|
||||
backdrop-filter: blur(24rpx);
|
||||
-webkit-backdrop-filter: blur(24rpx);
|
||||
}
|
||||
|
||||
.hero-card {
|
||||
min-height: 190rpx;
|
||||
border-radius: 24rpx;
|
||||
margin-bottom: 22rpx;
|
||||
padding: 22rpx 28rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 28rpx;
|
||||
min-height: 150rpx;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 132rpx;
|
||||
height: 132rpx;
|
||||
.avatar-wrap {
|
||||
position: relative;
|
||||
width: 136rpx;
|
||||
height: 136rpx;
|
||||
flex-shrink: 0;
|
||||
padding: 5rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 58rpx;
|
||||
font-weight: 900;
|
||||
background: linear-gradient(135deg, #b245ff, #2a7dff);
|
||||
box-shadow: 0 0 38rpx rgba(168, 85, 255, 0.52);
|
||||
background: linear-gradient(135deg, #fff, #9b54ff 46%, #4a67ff);
|
||||
box-shadow: 0 0 34rpx rgba(162, 91, 255, 0.52);
|
||||
}
|
||||
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background: #27124a;
|
||||
}
|
||||
|
||||
.avatar-edit {
|
||||
position: absolute;
|
||||
right: -4rpx;
|
||||
bottom: -2rpx;
|
||||
width: 42rpx;
|
||||
height: 42rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #8f4dff, #582cff);
|
||||
box-shadow: 0 0 18rpx rgba(158, 91, 255, 0.62);
|
||||
}
|
||||
|
||||
.pen-icon {
|
||||
width: 17rpx;
|
||||
height: 6rpx;
|
||||
margin: 18rpx auto;
|
||||
border-radius: 999rpx;
|
||||
background: #fff;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.hero-info {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.hero-name,
|
||||
.hero-sub,
|
||||
.quote,
|
||||
.panel-title,
|
||||
.block-title {
|
||||
display: block;
|
||||
.hero-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.hero-name {
|
||||
color: #fff;
|
||||
font-size: 42rpx;
|
||||
font-weight: 900;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.hero-star {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.hero-sub {
|
||||
margin-top: 8rpx;
|
||||
color: rgba(239, 232, 255, 0.78);
|
||||
display: block;
|
||||
margin-top: 12rpx;
|
||||
color: rgba(239, 232, 255, 0.84);
|
||||
font-size: 25rpx;
|
||||
}
|
||||
|
||||
.quote {
|
||||
.hero-line {
|
||||
width: 280rpx;
|
||||
height: 1rpx;
|
||||
margin-top: 14rpx;
|
||||
color: #c06dff;
|
||||
font-size: 24rpx;
|
||||
background: rgba(180, 139, 255, 0.22);
|
||||
}
|
||||
|
||||
.panel-title,
|
||||
.block-title {
|
||||
margin-bottom: 20rpx;
|
||||
color: #eadcff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 900;
|
||||
.hero-quote {
|
||||
display: block;
|
||||
margin-top: 14rpx;
|
||||
color: #b94cff;
|
||||
font-size: 25rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
.hero-planet {
|
||||
position: absolute;
|
||||
right: 34rpx;
|
||||
top: 28rpx;
|
||||
width: 210rpx;
|
||||
height: 150rpx;
|
||||
opacity: 0.42;
|
||||
}
|
||||
|
||||
.planet-core {
|
||||
position: absolute;
|
||||
right: 30rpx;
|
||||
top: 24rpx;
|
||||
width: 86rpx;
|
||||
height: 86rpx;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 34% 26%, #7d63ff, #4b1da8 62%, #19083b);
|
||||
box-shadow: 0 0 48rpx rgba(141, 78, 255, 0.58);
|
||||
}
|
||||
|
||||
.planet-ring {
|
||||
position: absolute;
|
||||
right: 4rpx;
|
||||
top: 58rpx;
|
||||
width: 158rpx;
|
||||
height: 34rpx;
|
||||
border: 5rpx solid rgba(161, 92, 255, 0.55);
|
||||
border-top-color: transparent;
|
||||
border-radius: 50%;
|
||||
transform: rotate(-18deg);
|
||||
}
|
||||
|
||||
.panel {
|
||||
border-radius: 24rpx;
|
||||
margin-bottom: 18rpx;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.section-head,
|
||||
.section-title-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section-head {
|
||||
min-height: 44rpx;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.section-head.with-action {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.line-field {
|
||||
min-height: 76rpx;
|
||||
display: grid;
|
||||
grid-template-columns: 150rpx 1fr;
|
||||
align-items: center;
|
||||
border-top: 1rpx solid rgba(180, 139, 255, 0.14);
|
||||
}
|
||||
|
||||
.field-label {
|
||||
color: rgba(221, 207, 246, 0.72);
|
||||
.section-title {
|
||||
color: rgba(239, 232, 255, 0.9);
|
||||
font-size: 25rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.line-input,
|
||||
.line-value {
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
.section-hint {
|
||||
color: rgba(222, 211, 240, 0.54);
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
position: relative;
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
color: #a855ff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.basic-icon::before {
|
||||
content: '♜';
|
||||
color: currentColor;
|
||||
font-size: 28rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.job-title-icon {
|
||||
border: 4rpx solid currentColor;
|
||||
border-radius: 6rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.job-title-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 7rpx;
|
||||
top: -8rpx;
|
||||
width: 10rpx;
|
||||
height: 7rpx;
|
||||
border: 3rpx solid currentColor;
|
||||
border-bottom: 0;
|
||||
border-radius: 6rpx 6rpx 0 0;
|
||||
}
|
||||
|
||||
.smile-title-icon {
|
||||
border-radius: 50%;
|
||||
border: 3rpx solid currentColor;
|
||||
}
|
||||
|
||||
.smile-title-icon::before,
|
||||
.smile-title-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
width: 4rpx;
|
||||
height: 4rpx;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.smile-title-icon::before { left: 7rpx; }
|
||||
.smile-title-icon::after { right: 7rpx; }
|
||||
|
||||
.heart-title-icon::before {
|
||||
content: '♡';
|
||||
font-size: 30rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.bio-title-icon {
|
||||
border: 3rpx solid currentColor;
|
||||
border-radius: 5rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bio-title-icon::before,
|
||||
.bio-title-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 6rpx;
|
||||
right: 6rpx;
|
||||
height: 3rpx;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.bio-title-icon::before { top: 8rpx; }
|
||||
.bio-title-icon::after { top: 15rpx; }
|
||||
|
||||
.profile-row {
|
||||
min-height: 64rpx;
|
||||
display: grid;
|
||||
grid-template-columns: 154rpx 1fr 24rpx;
|
||||
align-items: center;
|
||||
border-top: 1rpx solid rgba(180, 139, 255, 0.16);
|
||||
}
|
||||
|
||||
.section-head + .profile-row {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.row-label {
|
||||
color: rgba(205, 191, 238, 0.82);
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.row-input,
|
||||
.row-value {
|
||||
min-width: 0;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 24rpx;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.row-picker {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: rgba(217, 205, 238, 0.42);
|
||||
}
|
||||
|
||||
.chevron {
|
||||
color: rgba(218, 204, 243, 0.7);
|
||||
font-size: 44rpx;
|
||||
line-height: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.gender-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12rpx;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.choice,
|
||||
.mbti-chip,
|
||||
.tag-choice {
|
||||
height: 52rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgba(224, 214, 243, 0.72);
|
||||
border: 1rpx solid rgba(151, 111, 255, 0.28);
|
||||
background: rgba(255, 255, 255, 0.035);
|
||||
font-size: 23rpx;
|
||||
border: 1rpx solid rgba(151, 111, 255, 0.42);
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
font-size: 22rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.choice.active,
|
||||
.mbti-chip.active,
|
||||
.tag-choice.active {
|
||||
color: #fff;
|
||||
border-color: rgba(202, 97, 255, 0.9);
|
||||
background: rgba(149, 55, 255, 0.38);
|
||||
box-shadow: 0 0 24rpx rgba(168, 67, 255, 0.38);
|
||||
border-color: rgba(206, 82, 255, 0.95);
|
||||
background: linear-gradient(180deg, rgba(169, 61, 255, 0.62), rgba(107, 41, 206, 0.5));
|
||||
box-shadow: 0 0 18rpx rgba(168, 67, 255, 0.52), inset 0 1rpx 0 rgba(255, 255, 255, 0.22);
|
||||
}
|
||||
|
||||
.dual-panel {
|
||||
.astro-panel {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 24rpx;
|
||||
margin-top: 8rpx;
|
||||
border-top: 1rpx solid rgba(180, 139, 255, 0.16);
|
||||
}
|
||||
|
||||
.mbti-block {
|
||||
border-left: 1rpx solid rgba(180, 139, 255, 0.14);
|
||||
padding-left: 24rpx;
|
||||
.astro-col {
|
||||
min-width: 0;
|
||||
padding: 18rpx 18rpx 0 0;
|
||||
}
|
||||
|
||||
.mbti-col {
|
||||
border-left: 1rpx solid rgba(180, 139, 255, 0.18);
|
||||
padding-left: 18rpx;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.astro-title-row {
|
||||
display: grid;
|
||||
grid-template-columns: 34rpx 74rpx 1fr 18rpx;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.zodiac-head-icon,
|
||||
.mbti-head-icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
color: #a855ff;
|
||||
}
|
||||
|
||||
.zodiac-head-icon {
|
||||
font-size: 28rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.mbti-head-icon {
|
||||
border: 4rpx solid currentColor;
|
||||
border-radius: 4rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.astro-title {
|
||||
color: rgba(222, 211, 240, 0.76);
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.astro-current {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 23rpx;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.select-title {
|
||||
display: block;
|
||||
margin-top: 22rpx;
|
||||
color: rgba(222, 211, 240, 0.72);
|
||||
font-size: 21rpx;
|
||||
}
|
||||
|
||||
.zodiac-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12rpx;
|
||||
gap: 14rpx 8rpx;
|
||||
margin-top: 14rpx;
|
||||
}
|
||||
|
||||
.zodiac-item {
|
||||
min-height: 88rpx;
|
||||
border-radius: 18rpx;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
color: rgba(226, 217, 246, 0.84);
|
||||
font-size: 19rpx;
|
||||
}
|
||||
|
||||
.zodiac-bubble {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgba(224, 214, 243, 0.76);
|
||||
}
|
||||
|
||||
.zodiac-item.active {
|
||||
color: #fff;
|
||||
background: rgba(149, 55, 255, 0.26);
|
||||
box-shadow: inset 0 0 18rpx rgba(168, 67, 255, 0.28);
|
||||
}
|
||||
|
||||
.zodiac-symbol {
|
||||
color: #a855ff;
|
||||
font-size: 32rpx;
|
||||
font-size: 28rpx;
|
||||
background: rgba(124, 58, 237, 0.28);
|
||||
border: 1rpx solid rgba(173, 84, 255, 0.36);
|
||||
}
|
||||
|
||||
.zodiac-item.active .zodiac-bubble {
|
||||
color: #fff;
|
||||
border-color: rgba(215, 128, 255, 0.95);
|
||||
background: linear-gradient(135deg, rgba(168, 85, 247, 0.8), rgba(98, 47, 190, 0.66));
|
||||
box-shadow: 0 0 22rpx rgba(190, 92, 255, 0.72);
|
||||
}
|
||||
|
||||
.mbti-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 12rpx;
|
||||
margin-top: 14rpx;
|
||||
}
|
||||
|
||||
.mbti-chip {
|
||||
height: 42rpx;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.tag-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 14rpx;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 14rpx 24rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.tag-choice {
|
||||
height: 40rpx;
|
||||
font-size: 21rpx;
|
||||
}
|
||||
|
||||
.tag-choice.dashed {
|
||||
@@ -490,41 +855,38 @@ onMounted(() => {
|
||||
color: #c06dff;
|
||||
}
|
||||
|
||||
.custom {
|
||||
color: #c06dff;
|
||||
font-size: 23rpx;
|
||||
}
|
||||
|
||||
.bio-panel {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bio {
|
||||
width: 100%;
|
||||
height: 140rpx;
|
||||
height: 116rpx;
|
||||
box-sizing: border-box;
|
||||
padding: 22rpx;
|
||||
border-radius: 22rpx;
|
||||
border: 1rpx solid rgba(151, 111, 255, 0.24);
|
||||
background: rgba(12, 15, 46, 0.72);
|
||||
margin-top: 16rpx;
|
||||
padding: 18rpx 20rpx;
|
||||
border-radius: 18rpx;
|
||||
border: 1rpx solid rgba(151, 111, 255, 0.22);
|
||||
background: rgba(10, 12, 40, 0.66);
|
||||
color: #fff;
|
||||
font-size: 25rpx;
|
||||
line-height: 1.55;
|
||||
font-size: 23rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.bio-count {
|
||||
display: block;
|
||||
margin-top: 10rpx;
|
||||
margin-top: 8rpx;
|
||||
text-align: right;
|
||||
color: rgba(224, 214, 243, 0.56);
|
||||
font-size: 22rpx;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
height: 116rpx;
|
||||
box-sizing: border-box;
|
||||
padding: 14rpx 28rpx 22rpx;
|
||||
background: rgba(5, 6, 21, 0.72);
|
||||
backdrop-filter: blur(24rpx);
|
||||
}
|
||||
|
||||
.submit {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
border-radius: 999rpx;
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 900;
|
||||
.placeholder {
|
||||
color: rgba(214, 204, 235, 0.42);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -37,11 +37,30 @@ export const deleteEvent = async (id) => {
|
||||
return response
|
||||
}
|
||||
|
||||
export const assistEventWriting = async (eventData = {}) => {
|
||||
return post('/lifeEvent/ai-assist', eventData)
|
||||
}
|
||||
|
||||
export const chatAboutEvent = async (eventData = {}) => {
|
||||
return post('/lifeEvent/chat-placeholder', eventData)
|
||||
}
|
||||
|
||||
export const shareEvent = async (eventData = {}) => {
|
||||
return post('/lifeEvent/share-placeholder', eventData)
|
||||
}
|
||||
|
||||
export const favoriteEvent = async ({ id, favorite }) => {
|
||||
return post('/lifeEvent/favorite-placeholder', { id, favorite })
|
||||
}
|
||||
|
||||
const transformToBackendFormat = (frontendData) => {
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
time,
|
||||
timeMode = 'date',
|
||||
eventDateText,
|
||||
endTime,
|
||||
content,
|
||||
aiFeedback,
|
||||
eventType = 'daily_log',
|
||||
@@ -54,6 +73,9 @@ const transformToBackendFormat = (frontendData) => {
|
||||
id,
|
||||
title,
|
||||
eventDate: time,
|
||||
timeMode,
|
||||
eventDateText: eventDateText || time,
|
||||
eventEndDate: timeMode === 'range' ? endTime : null,
|
||||
content,
|
||||
aiReply: aiFeedback,
|
||||
eventType,
|
||||
@@ -71,6 +93,9 @@ export const transformToFrontendFormat = (backendData) => {
|
||||
userId,
|
||||
title,
|
||||
eventDate,
|
||||
timeMode,
|
||||
eventDateText,
|
||||
eventEndDate,
|
||||
content,
|
||||
aiReply,
|
||||
eventType,
|
||||
@@ -85,6 +110,9 @@ export const transformToFrontendFormat = (backendData) => {
|
||||
userId,
|
||||
title: title || '',
|
||||
time: eventDate ? eventDate.split('T')[0] : '',
|
||||
timeMode: timeMode || 'date',
|
||||
eventDateText: eventDateText || (eventDate ? eventDate.split('T')[0] : ''),
|
||||
endTime: eventEndDate ? eventEndDate.split('T')[0] : '',
|
||||
content: content || '',
|
||||
aiFeedback: aiReply || '',
|
||||
eventType: eventType || 'daily_log',
|
||||
@@ -107,6 +135,10 @@ export default {
|
||||
createEvent,
|
||||
updateEvent,
|
||||
deleteEvent,
|
||||
assistEventWriting,
|
||||
chatAboutEvent,
|
||||
shareEvent,
|
||||
favoriteEvent,
|
||||
transformToFrontendFormat,
|
||||
transformListToFrontend
|
||||
}
|
||||
|
||||
@@ -49,11 +49,12 @@ const transformToBackendFormat = (frontendData) => {
|
||||
title,
|
||||
description,
|
||||
content,
|
||||
steps: inputSteps,
|
||||
status = 'active',
|
||||
progress = 0
|
||||
} = frontendData
|
||||
|
||||
let steps = []
|
||||
let steps = Array.isArray(inputSteps) ? inputSteps : []
|
||||
if (content) {
|
||||
const stepMatches = content.match(/(\d+)\.\s*([^::]+)[::]\s*([^\n]+)/g)
|
||||
if (stepMatches) {
|
||||
@@ -123,7 +124,13 @@ export const transformToFrontendFormat = (backendData) => {
|
||||
title: title || '实现路径',
|
||||
description: description || '',
|
||||
content,
|
||||
steps: steps || [],
|
||||
steps: Array.isArray(steps)
|
||||
? steps.map((step, index) => ({
|
||||
...step,
|
||||
task: step.task || step.phase || `阶段${index + 1}`,
|
||||
desc: step.desc || step.content || step.action || ''
|
||||
}))
|
||||
: [],
|
||||
status: status || 'active',
|
||||
progress: progress || 0,
|
||||
createTime
|
||||
|
||||
@@ -151,6 +151,62 @@ const createEvent = async (eventData) => {
|
||||
}
|
||||
}
|
||||
|
||||
const updateEvent = async (eventData) => {
|
||||
try {
|
||||
await lifeEventService.updateEvent(eventData)
|
||||
await fetchEvents()
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
const deleteEvent = async (id) => {
|
||||
try {
|
||||
await lifeEventService.deleteEvent(id)
|
||||
await fetchEvents()
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
const assistEventWriting = async (eventData) => {
|
||||
try {
|
||||
const res = await lifeEventService.assistEventWriting(eventData)
|
||||
return { success: true, data: res.data }
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
const chatAboutEvent = async (eventData) => {
|
||||
try {
|
||||
const res = await lifeEventService.chatAboutEvent(eventData)
|
||||
return { success: true, data: res.data }
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
const shareEvent = async (eventData) => {
|
||||
try {
|
||||
const res = await lifeEventService.shareEvent(eventData)
|
||||
return { success: true, data: res.data }
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
const favoriteEvent = async ({ id, favorite }) => {
|
||||
try {
|
||||
const res = await lifeEventService.favoriteEvent({ id, favorite })
|
||||
return { success: true, data: res.data }
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
const getEventById = (id) => {
|
||||
return state.events.find(event => String(event.id) === String(id)) || null
|
||||
}
|
||||
@@ -350,6 +406,12 @@ export const useAppStore = () => {
|
||||
setCurrentStep,
|
||||
fetchEvents,
|
||||
createEvent,
|
||||
updateEvent,
|
||||
deleteEvent,
|
||||
assistEventWriting,
|
||||
chatAboutEvent,
|
||||
shareEvent,
|
||||
favoriteEvent,
|
||||
getEventById,
|
||||
fetchScripts,
|
||||
createScript,
|
||||
|
||||
Reference in New Issue
Block a user