You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

574 lines
28 KiB

4 weeks ago
<template>
<view class="universe-page">
<view class="top-safe" :style="{ height: menuButtonInfo.top + 'px' }"></view>
3 days ago
<view class="float-bar">
<view class="home-back" @tap="backHome">
<text class="home-back-icon"></text>
<text>返回首页</text>
</view>
4 weeks ago
</view>
<view class="profile">
4 weeks ago
<image class="orb-img" v-if="profile.avatarUrl" :src="profile.avatarUrl" mode="aspectFill"></image>
<view class="orb" v-else>{{ profile.avatarText || '' }}</view>
<view class="name">{{ profile.anonymousName || '半匿名漂流者' }}</view>
<view class="desc">{{ profile.intro || '不经营人设,不展示粉丝。这里只记录你更喜欢怎样被陪伴。' }}</view>
4 weeks ago
</view>
<view class="stats">
2 days ago
<view><text>{{ profile.dailyQuota || 3 }}</text><text>今日匹配总次数</text></view>
<view class="space-stat" @tap="goMySpace"><text>我的空间</text><text>查看动态 </text></view>
4 weeks ago
</view>
<view class="tag-panel" v-if="profile.interestTags && profile.interestTags.length">
<view class="tag" v-for="item in profile.interestTags" :key="item">{{ item }}</view>
4 weeks ago
</view>
3 weeks ago
<view class="persona-panel" v-if="profile.personaImages && profile.personaImages.length">
<view class="panel-title">人格卡片</view>
<scroll-view scroll-x class="persona-scroll">
<image class="persona-image" v-for="(img, index) in profile.personaImages" :key="img" :src="img" mode="aspectFill" @tap="previewPersona(index)"></image>
</scroll-view>
</view>
2 days ago
<view class="profile-panel">
<view class="panel-head">
<view>
<view class="panel-title inline-title">我的 i/e 资料</view>
</view>
</view>
<view class="profile-edit-item" @tap="openProfileEditor('mode')">
<view class="profile-edit-icon">i/e</view>
<view>
<view class="profile-edit-title">当前身份</view>
<view class="profile-edit-desc">{{ modeText(profile.currentMode) }}</view>
</view>
<view class="edit-mini-btn">更改</view>
</view>
<view class="profile-edit-item" @tap="openProfileEditor('basic')">
<view class="profile-edit-icon"></view>
<view>
<view class="profile-edit-title">基础资料</view>
<view class="profile-edit-desc">{{ profile.anonymousName || '未设置昵称' }} · {{ genderText(profile.gender) }}</view>
</view>
<view class="edit-mini-btn">更改</view>
</view>
<view class="profile-edit-item" @tap="openProfileEditor('tags')">
<view class="profile-edit-icon">#</view>
<view>
<view class="profile-edit-title">兴趣标签</view>
<view class="profile-edit-desc">{{ profileTagsText }}</view>
</view>
<view class="edit-mini-btn">更改</view>
</view>
<view class="profile-edit-item" @tap="openProfileEditor('persona')">
<view class="profile-edit-icon"></view>
<view>
<view class="profile-edit-title">人格卡片</view>
<view class="profile-edit-desc">{{ profilePersonaText }}</view>
</view>
<view class="edit-mini-btn">更改</view>
</view>
<view class="profile-edit-item" @tap="openProfileEditor('target')">
<view class="profile-edit-icon"></view>
<view>
<view class="profile-edit-title">匹配偏好</view>
<view class="profile-edit-desc">{{ targetPreferenceText }}</view>
</view>
<view class="edit-mini-btn">更改</view>
</view>
<view class="profile-edit-item region">
<view class="profile-edit-icon"></view>
<view>
<view class="profile-edit-title">当前校区</view>
<view class="profile-edit-desc">{{ profile.regionName || '暂未选择校区' }}</view>
</view>
4 weeks ago
</view>
</view>
2 days ago
4 weeks ago
<view class="panel">
<view class="setting" @tap="goSettings">
<view>
<view class="setting-title">隐私与安全</view>
<view class="setting-desc">黑名单举报记录半匿名规则</view>
</view>
<view class="arrow"></view>
</view>
</view>
3 days ago
<ie-bottom-tab active="universe" :unread-count="unreadCount"></ie-bottom-tab>
2 days ago
<view class="edit-mask" v-if="editSection" @tap="closeProfileEditor">
<view class="edit-sheet" @tap.stop>
<view class="edit-head">
<view>
<view class="edit-title">{{ editTitle }}</view>
<view class="edit-sub">只修改这一项其它资料会保留</view>
</view>
<view class="edit-close" @tap="closeProfileEditor">×</view>
</view>
<view v-if="editSection === 'mode'">
<view class="mode-card i" :class="{ active: editForm.currentMode === 'i' }" @tap="editForm.currentMode = 'i'">
<view class="mode-mark">i</view>
<view>
<view class="mode-title">安静陪伴</view>
<view class="mode-desc">慢回复允许沉默低压力靠近</view>
</view>
</view>
<view class="mode-card e" :class="{ active: editForm.currentMode === 'e' }" @tap="editForm.currentMode = 'e'">
<view class="mode-mark">e</view>
<view>
<view class="mode-title">轻轻热闹</view>
<view class="mode-desc">有人开场聊点废话把情绪拉亮</view>
</view>
</view>
</view>
<view v-if="editSection === 'basic'">
<view class="avatar-edit" @tap="chooseProfileAvatar">
<image class="avatar-edit-img" v-if="editForm.avatarUrl" :src="editForm.avatarUrl" mode="aspectFill"></image>
<view class="avatar-edit-text" v-else>{{ editAvatarPreview }}</view>
<view class="avatar-edit-tip">点一下换头像</view>
</view>
<view class="random-name" @tap="randomEditName">随机一个昵称</view>
<input class="edit-input" v-model="editForm.anonymousName" maxlength="16" placeholder="昵称,例如:南门慢跑员" />
<view class="edit-label">性别</view>
<view class="chip-row">
<view class="edit-chip" v-for="item in genderOptions" :key="item.key" :class="{ active: editForm.gender === item.key }" @tap="editForm.gender = item.key">
{{ item.label }}
</view>
</view>
<textarea class="edit-textarea" v-model="editForm.intro" maxlength="80" placeholder="一句轻介绍:我通常慢热,但会认真听。" />
</view>
<view v-if="editSection === 'tags'">
<view class="tag-edit-grid">
<view class="edit-tag" v-for="item in tagOptions" :key="item" :class="{ active: editForm.interestTags.includes(item) }" @tap="toggleEditTag(item)">
{{ item }}
</view>
</view>
<view class="edit-help">最多选择 6 个标签</view>
</view>
<view v-if="editSection === 'persona'">
<view class="persona-edit-grid">
<view class="persona-edit-item" v-for="(img, index) in editForm.personaImages" :key="img">
<image :src="img" mode="aspectFill" @tap="previewEditPersona(index)"></image>
<view class="persona-edit-delete" @tap.stop="removeEditPersonaImage(index)">×</view>
</view>
<view class="persona-edit-add" v-if="editForm.personaImages.length < 5" @tap="chooseProfilePersonaImages">
<view class="plus"></view>
<view>添加图片</view>
</view>
</view>
<view class="edit-help">最多 5 会展示在你的个人空间和聊天资料里</view>
</view>
<view v-if="editSection === 'target'">
<view class="edit-label">想遇见哪类人</view>
<view class="target-card" v-for="item in targetOptions" :key="item.key" :class="{ active: editForm.targetModePreference === item.key }" @tap="editForm.targetModePreference = item.key">
<view class="target-title">{{ item.title }}</view>
<view class="target-desc">{{ item.desc }}</view>
</view>
<view class="edit-label">想匹配的性别</view>
<view class="chip-row">
<view class="edit-chip" v-for="item in targetGenderOptions" :key="item.key" :class="{ active: editForm.targetGenderPreference === item.key }" @tap="editForm.targetGenderPreference = item.key">
{{ item.label }}
</view>
</view>
</view>
<view class="edit-save" :class="{ disabled: savingProfile }" @tap="saveProfileSection">{{ savingProfile ? '保存中...' : '保存修改' }}</view>
</view>
</view>
4 weeks ago
</view>
</template>
<script>
2 days ago
import { getIeProfile, saveIeProfile, getIeUnreadCount } from '@/common/ieApi.js'
3 days ago
import IeBottomTab from '@/components/ie-bottom-tab/ie-bottom-tab.vue'
2 days ago
import tui from '@/common/httpRequest.js'
4 weeks ago
4 weeks ago
export default {
3 days ago
components: { IeBottomTab },
4 weeks ago
data() {
return {
menuButtonInfo: { top: 44 },
3 days ago
unreadCount: 0,
unreadTimer: null,
4 weeks ago
profile: {},
2 days ago
editSection: '',
editForm: this.emptyProfileForm(),
savingProfile: false,
tagOptions: ['慢热', '爱听歌', '夜跑', '自习', '想聊天', '想安静', '情绪低电量', '散步', '电影', '游戏', '摄影', '干饭'],
targetOptions: [
{ key: 'i', title: '想遇见 i 人', desc: '安静、慢一点、允许沉默。' },
{ key: 'e', title: '想遇见 e 人', desc: '轻松开场,聊一点不重要的小事。' },
{ key: 'any', title: '都可以', desc: '交给此刻的随机同频。' }
],
genderOptions: [{ key: 'male', label: '男生' }, { key: 'female', label: '女生' }, { key: 'unknown', label: '不想说' }],
targetGenderOptions: [{ key: 'male', label: '男生' }, { key: 'female', label: '女生' }, { key: 'any', label: '不限' }],
nameSeeds: ['南门慢跑员', '图书馆三楼', '便利店灯光', '耳机里的云', '操场晚风', '自习逃跑员', '月台旁的影子']
}
},
computed: {
profileTagsText() {
const tags = this.profile.interestTags || []
return tags.length ? tags.slice(0, 4).join(' · ') : '还没有选择标签'
},
profilePersonaText() {
const count = (this.profile.personaImages || []).length
return count ? `${count} 张人格卡片` : '还没有上传人格卡片'
},
targetPreferenceText() {
return `${this.targetModeText(this.profile.targetModePreference)} · ${this.targetGenderText(this.profile.targetGenderPreference)}`
},
editTitle() {
const map = {
mode: '更改当前身份',
basic: '编辑基础资料',
tags: '编辑兴趣标签',
persona: '编辑人格卡片',
target: '编辑匹配偏好'
}
return map[this.editSection] || '编辑资料'
},
editAvatarPreview() {
return (this.editForm.anonymousName || this.editForm.avatarText || '我').slice(0, 1)
4 weeks ago
}
},
3 days ago
onLoad(options = {}) {
4 weeks ago
if (uni.getMenuButtonBoundingClientRect) this.menuButtonInfo = uni.getMenuButtonBoundingClientRect()
3 days ago
this.unreadCount = Number(options.unreadCount) || 0
4 weeks ago
this.loadProfile()
4 weeks ago
},
3 days ago
onShow() {
this.startUnreadTimer()
},
onHide() {
this.stopUnreadTimer()
},
onUnload() {
this.stopUnreadTimer()
},
4 weeks ago
methods: {
3 days ago
startUnreadTimer() {
if (this.unreadTimer) return
this.unreadTimer = setInterval(() => {
this.refreshUnreadCount()
}, 15000)
},
stopUnreadTimer() {
if (!this.unreadTimer) return
clearInterval(this.unreadTimer)
this.unreadTimer = null
},
async refreshUnreadCount() {
const count = await getIeUnreadCount()
if (count === null || count === undefined) return
this.unreadCount = Number(count) || 0
},
4 weeks ago
async loadProfile() {
const profile = await getIeProfile()
if (profile) this.profile = profile
},
2 days ago
backHome() { uni.switchTab({ url: '/pages/index/index' }) },
emptyProfileForm() {
return {
currentMode: 'i',
targetModePreference: 'any',
targetGenderPreference: 'any',
anonymousName: '',
avatarText: '我',
avatarUrl: '',
gender: 'unknown',
intro: '',
interestTags: [],
personaImages: [],
regionId: '',
regionName: ''
}
},
cloneProfile(profile = {}) {
return {
...this.emptyProfileForm(),
currentMode: profile.currentMode || 'i',
targetModePreference: profile.targetModePreference || 'any',
targetGenderPreference: profile.targetGenderPreference || 'any',
anonymousName: profile.anonymousName || '',
avatarText: profile.avatarText || '我',
avatarUrl: profile.avatarUrl || '',
gender: profile.gender || 'unknown',
intro: profile.intro || '',
interestTags: (profile.interestTags || []).slice(),
personaImages: (profile.personaImages || []).slice(),
regionId: profile.regionId || '',
regionName: profile.regionName || ''
}
},
openProfileEditor(section) {
this.editSection = section
this.editForm = this.cloneProfile(this.profile)
},
closeProfileEditor() {
if (this.savingProfile) return
this.editSection = ''
this.editForm = this.emptyProfileForm()
},
modeShortText(mode) {
return mode === 'e' ? 'e' : 'i'
},
modeText(mode) {
return mode === 'e' ? 'e 人 · 轻轻热闹' : 'i 人 · 安静陪伴'
},
genderText(gender) {
if (gender === 'male') return '男生'
if (gender === 'female') return '女生'
return '不想说'
},
targetModeText(mode) {
if (mode === 'i') return '想遇见 i 人'
if (mode === 'e') return '想遇见 e 人'
return '都可以'
},
targetGenderText(gender) {
if (gender === 'male') return '男生'
if (gender === 'female') return '女生'
return '不限性别'
},
toggleEditTag(tag) {
const index = this.editForm.interestTags.indexOf(tag)
if (index > -1) {
this.editForm.interestTags.splice(index, 1)
return
}
if (this.editForm.interestTags.length >= 6) {
uni.showToast({ title: '最多选择 6 个标签', icon: 'none' })
return
}
this.editForm.interestTags.push(tag)
},
randomEditName() {
this.editForm.anonymousName = this.nameSeeds[Math.floor(Math.random() * this.nameSeeds.length)]
},
mergedProfilePayload() {
const base = this.cloneProfile(this.profile)
const form = this.cloneProfile(this.editForm)
let merged = { ...base }
if (this.editSection === 'mode') {
merged.currentMode = form.currentMode
} else if (this.editSection === 'basic') {
merged.anonymousName = form.anonymousName
merged.avatarText = this.editAvatarPreview
merged.avatarUrl = form.avatarUrl
merged.gender = form.gender
merged.intro = form.intro
} else if (this.editSection === 'tags') {
merged.interestTags = form.interestTags
} else if (this.editSection === 'persona') {
merged.personaImages = form.personaImages
} else if (this.editSection === 'target') {
merged.targetModePreference = form.targetModePreference
merged.targetGenderPreference = form.targetGenderPreference
}
return merged
},
validateProfileSection() {
if (this.editSection === 'basic' && !this.editForm.anonymousName.trim()) {
uni.showToast({ title: '先起一个昵称吧', icon: 'none' })
return false
}
if (this.editSection === 'tags' && !this.editForm.interestTags.length) {
uni.showToast({ title: '至少选一个标签', icon: 'none' })
return false
}
return true
},
async saveProfileSection() {
if (this.savingProfile) return
if (!this.validateProfileSection()) return
this.savingProfile = true
uni.showLoading({ title: '保存中...', mask: true })
try {
const payload = this.mergedProfilePayload()
const saved = await saveIeProfile(payload)
if (!saved) return
this.profile = saved
this.editSection = ''
this.editForm = this.emptyProfileForm()
uni.showToast({ title: '已保存', icon: 'none' })
} finally {
this.savingProfile = false
uni.hideLoading()
}
},
chooseProfileAvatar() {
uni.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['camera', 'album'],
camera: 'back',
success: async (res) => {
const file = res.tempFiles && res.tempFiles[0]
if (!file || !file.tempFilePath) return
uni.showLoading({ title: '上传中...', mask: true })
try {
this.editForm.avatarUrl = await this.silentUpload(file.tempFilePath)
} catch (e) {
uni.showToast({ title: '头像上传失败,请重试', icon: 'none' })
} finally {
uni.hideLoading()
}
}
})
},
chooseProfilePersonaImages() {
const remain = 5 - this.editForm.personaImages.length
if (remain <= 0) {
uni.showToast({ title: '最多上传 5 张', icon: 'none' })
return
}
uni.chooseMedia({
count: remain,
mediaType: ['image'],
sourceType: ['camera', 'album'],
camera: 'back',
success: async (res) => {
const files = res.tempFiles || []
if (!files.length) return
uni.showLoading({ title: '上传中...', mask: true })
try {
for (const file of files) {
if (this.editForm.personaImages.length >= 5) break
if (!file || !file.tempFilePath) continue
const url = await this.silentUpload(file.tempFilePath)
if (url) this.editForm.personaImages.push(url)
}
} catch (e) {
uni.showToast({ title: '图片上传失败,请重试', icon: 'none' })
} finally {
uni.hideLoading()
}
}
})
},
removeEditPersonaImage(index) {
this.editForm.personaImages.splice(index, 1)
},
previewEditPersona(index) {
uni.previewImage({ urls: this.editForm.personaImages || [], current: this.editForm.personaImages[index] })
},
3 weeks ago
previewPersona(index) {
uni.previewImage({ urls: this.profile.personaImages || [], current: this.profile.personaImages[index] })
},
2 days ago
goMySpace() {
uni.navigateTo({ url: '/package1/ieBrowser/mySpace' })
},
goSettings() { uni.navigateTo({ url: '/package1/ieBrowser/settings' }) },
// 不用 tui.uploadFile:它会弹全局 loading,且业务码异常时 Promise 永远不结束
silentUpload(filePath) {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: tui.interfaceUrl() + '/upload/file',
filePath,
name: 'file',
timeout: 300000,
header: { accessToken: uni.getStorageSync('hiver_token') },
success: (res) => {
try {
const d = JSON.parse(String(res.data || '{}').replace(/\ufeff/g, ''))
const fileObj = d.result !== undefined ? d.result : d.data
const url = typeof fileObj === 'string' ? fileObj : (fileObj && (fileObj.url || fileObj.fileUrl || fileObj.path || fileObj.fullPath))
if (d.code % 100 === 0 && url) resolve(url)
else reject(new Error(d.message || '上传失败'))
} catch (e) {
reject(e)
}
},
fail: () => reject(new Error('上传失败'))
})
})
}
4 weeks ago
}
}
</script>
<style lang="scss" scoped>
page { background: #f7f9ff; }
3 days ago
.universe-page { min-height: 100vh; padding: 0 32rpx 230rpx; box-sizing: border-box; color: #161b2e; background: radial-gradient(circle at 50% 12%, rgba(169,255,231,.42), transparent 330rpx), radial-gradient(circle at 86% 28%, rgba(255,184,209,.24), transparent 320rpx), linear-gradient(180deg, #fbfdff, #eef4ff 62%, #fff4e8); }
2 days ago
.float-bar { position: relative; z-index: 5; display: flex; align-items: center; justify-content: space-between;}
3 days ago
.home-back { display: flex; align-items: center; height: 58rpx; padding: 0 22rpx 0 12rpx; border: 1rpx solid rgba(255,255,255,.88); border-radius: 999rpx; color: rgba(22,27,46,.66); background: rgba(255,255,255,.66); backdrop-filter: blur(20rpx); box-shadow: 0 14rpx 36rpx rgba(96,112,160,.12), inset 0 1rpx 0 rgba(255,255,255,.95); font-size: 23rpx; font-weight: 800; }
.home-back:active { transform: scale(.95); background: rgba(169,255,231,.7); }
2 days ago
.home-back-icon { margin-right: 6rpx;padding-bottom: 6rpx; font-size: 40rpx; line-height: 50rpx; font-weight: 400; }
4 weeks ago
.profile { padding-top: 18rpx; text-align: center; }
4 weeks ago
.orb,
.orb-img { width: 148rpx; height: 148rpx; margin: 0 auto; border-radius: 50%; box-shadow: 0 0 80rpx rgba(169,255,231,.18); }
.orb { text-align: center; line-height: 148rpx; color: #11162a; background: linear-gradient(145deg, #effffb, #a9ffe7); font-size: 48rpx; font-weight: 800; }
4 weeks ago
.name { margin-top: 26rpx; font-size: 42rpx; font-weight: 800; }
.desc { width: 560rpx; margin: 14rpx auto 0; color: rgba(22,27,46,.52); font-size: 24rpx; line-height: 38rpx; }
2 days ago
.stats { display: flex; gap: 18rpx; margin-top: 36rpx; padding: 18rpx; border-radius: 36rpx; background: rgba(255,255,255,.62); border: 1rpx solid rgba(255,255,255,.78); box-shadow: 0 20rpx 64rpx rgba(96,112,160,.12); }
4 weeks ago
.stats view { flex: 1; text-align: center; }
.stats text { display: block; }
2 days ago
.stats text:first-child { font-size: 34rpx; font-weight: 800; color: #151a2d; }
4 weeks ago
.stats text:last-child { margin-top: 8rpx; color: rgba(22,27,46,.42); font-size: 21rpx; }
2 days ago
.stats view { padding: 16rpx 8rpx; border-radius: 26rpx; }
.space-stat { background: rgba(238,244,255,.62); border: 1rpx solid rgba(255,255,255,.78); }
.space-stat:active { transform: scale(.97); }
.space-stat text:first-child { font-size: 34rpx; }
.space-stat text:last-child { color: rgba(108,105,216,.72); font-weight: 800; }
.profile-panel { position: relative; margin-top: 26rpx; padding: 32rpx 28rpx 28rpx; border-radius: 40rpx; background: rgba(255,255,255,.64); border: 1rpx solid rgba(255,255,255,.86); backdrop-filter: blur(24rpx); box-shadow: 0 24rpx 76rpx rgba(96,112,160,.12); overflow: hidden; }
.profile-panel::before { content: ''; position: absolute; right: -120rpx; top: -120rpx; width: 280rpx; height: 280rpx; border-radius: 50%; background: radial-gradient(circle, rgba(169,255,231,.26), transparent 70%); pointer-events: none; }
.panel-head { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 18rpx; }
.inline-title { margin-bottom: 8rpx; }
.panel-sub { color: rgba(22,27,46,.44); font-size: 22rpx; line-height: 34rpx; }
.profile-edit-item { position: relative; z-index: 1; display: flex; align-items: center; margin-top: 16rpx; padding: 22rpx 24rpx; border-radius: 28rpx; background: rgba(255,255,255,.78); border: 1rpx solid rgba(255,255,255,.9); box-shadow: 0 12rpx 32rpx rgba(96,112,160,.07); }
.profile-edit-item:active { transform: scale(.98); background: rgba(223,254,244,.7); }
.profile-edit-item.region:active { transform: none; background: rgba(255,255,255,.72); }
.profile-edit-item > view:nth-child(2) { flex: 1; min-width: 0; }
.profile-edit-icon { flex-shrink: 0; width: 58rpx; height: 58rpx; margin-right: 18rpx; border-radius: 20rpx; text-align: center; line-height: 58rpx; color: #5a55c8; background: linear-gradient(145deg, rgba(238,244,255,.96), rgba(223,254,244,.92)); font-size: 22rpx; font-weight: 900; box-shadow: inset 0 1rpx 0 rgba(255,255,255,.95); }
.profile-edit-title { font-size: 26rpx; font-weight: 800; }
.profile-edit-desc { margin-top: 8rpx; color: rgba(22,27,46,.5); font-size: 22rpx; line-height: 34rpx; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.edit-mini-btn { flex-shrink: 0; margin-left: 18rpx; padding: 8rpx 18rpx; border-radius: 999rpx; color: #6c69d8; background: rgba(139,124,255,.1); font-size: 21rpx; font-weight: 800; }
4 weeks ago
.tag-panel { display: flex; flex-wrap: wrap; margin-top: 24rpx; padding: 24rpx; border-radius: 32rpx; background: rgba(255,255,255,.58); border: 1rpx solid rgba(255,255,255,.78); }
.tag { margin: 0 12rpx 12rpx 0; padding: 10rpx 18rpx; border-radius: 999rpx; color: #6c69d8; background: rgba(139,124,255,.1); font-size: 22rpx; }
3 weeks ago
.persona-panel { margin-top: 26rpx; padding: 30rpx; border-radius: 36rpx; background: rgba(255,255,255,.62); border: 1rpx solid rgba(255,255,255,.78); backdrop-filter: blur(24rpx); box-shadow: 0 20rpx 64rpx rgba(96,112,160,.12); }
.persona-scroll { white-space: nowrap; }
.persona-image { display: inline-block; width: 214rpx; height: 260rpx; margin-right: 18rpx; border-radius: 28rpx; background: rgba(22,27,46,.06); box-shadow: 0 16rpx 42rpx rgba(96,112,160,.12); }
4 weeks ago
.panel { margin-top: 26rpx; padding: 30rpx; border-radius: 36rpx; background: rgba(255,255,255,.62); border: 1rpx solid rgba(255,255,255,.78); backdrop-filter: blur(24rpx); box-shadow: 0 20rpx 64rpx rgba(96,112,160,.12); }
.panel-title { margin-bottom: 24rpx; font-size: 30rpx; font-weight: 800; }
2 days ago
.edit-mask { position: fixed; inset: 0; z-index: 130; display: flex; align-items: flex-end; padding: 0 28rpx 42rpx; box-sizing: border-box; background: rgba(13,17,32,.36); backdrop-filter: blur(12rpx); }
.edit-sheet { width: 100%; max-height: 82vh; overflow-y: auto; padding: 34rpx 30rpx 30rpx; border-radius: 42rpx; box-sizing: border-box; background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(247,249,255,.96)); border: 1rpx solid rgba(255,255,255,.9); box-shadow: 0 34rpx 90rpx rgba(42,50,86,.22); }
.edit-head { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 24rpx; }
.edit-title { font-size: 34rpx; font-weight: 900; color: #151a2d; }
.edit-sub { margin-top: 8rpx; color: rgba(21,26,45,.48); font-size: 23rpx; }
.edit-close { width: 58rpx; height: 58rpx; border-radius: 50%; text-align: center; line-height: 54rpx; color: rgba(21,26,45,.5); background: rgba(238,244,255,.8); font-size: 36rpx; font-weight: 700; }
.mode-card, .target-card { display: flex; align-items: center; margin-top: 18rpx; padding: 24rpx; border-radius: 28rpx; background: rgba(238,244,255,.78); border: 2rpx solid transparent; }
.mode-card.active, .target-card.active { border-color: #a9ffe7; background: rgba(223,254,244,.78); }
.mode-mark { flex-shrink: 0; width: 72rpx; height: 72rpx; margin-right: 20rpx; border-radius: 24rpx; text-align: center; line-height: 72rpx; font-size: 42rpx; font-weight: 900; }
.i .mode-mark { color: #fff; background: #7771d8; }
.e .mode-mark { color: #11162a; background: #a9ffe7; }
.mode-title, .target-title { font-size: 28rpx; font-weight: 800; }
.mode-desc, .target-desc { margin-top: 8rpx; color: rgba(21,26,45,.54); font-size: 23rpx; line-height: 36rpx; }
.avatar-edit { width: 184rpx; margin: 0 auto 26rpx; text-align: center; }
.avatar-edit-img, .avatar-edit-text { width: 154rpx; height: 154rpx; margin: 0 auto; border-radius: 50%; }
.avatar-edit-img { display: block; }
.avatar-edit-text { text-align: center; line-height: 154rpx; color: #11162a; background: linear-gradient(145deg, #effffb, #a9ffe7); font-size: 58rpx; font-weight: 800; }
.avatar-edit-tip { margin-top: 14rpx; color: rgba(21,26,45,.42); font-size: 22rpx; }
.random-name { width: 220rpx; height: 58rpx; margin: 0 auto 22rpx; border-radius: 999rpx; text-align: center; line-height: 58rpx; color: #6c69d8; background: rgba(139,124,255,.1); font-size: 23rpx; font-weight: 800; }
.edit-input, .edit-textarea { width: 100%; box-sizing: border-box; border-radius: 26rpx; background: rgba(238,244,255,.82); color: #151a2d; font-size: 26rpx; }
.edit-input { height: 82rpx; padding: 0 24rpx; }
.edit-textarea { height: 170rpx; margin-top: 20rpx; padding: 22rpx 24rpx; line-height: 40rpx; }
.edit-label { margin: 24rpx 0 14rpx; color: rgba(21,26,45,.52); font-size: 23rpx; font-weight: 800; }
.chip-row, .tag-edit-grid, .persona-edit-grid { display: flex; flex-wrap: wrap; }
.edit-chip, .edit-tag { height: 58rpx; line-height: 58rpx; margin: 0 14rpx 14rpx 0; padding: 0 24rpx; border-radius: 999rpx; color: rgba(21,26,45,.58); background: rgba(238,244,255,.82); font-size: 23rpx; }
.edit-chip.active, .edit-tag.active { color: #11162a; background: #a9ffe7; font-weight: 800; }
.edit-help { margin-top: 10rpx; color: rgba(21,26,45,.4); font-size: 22rpx; line-height: 34rpx; }
.persona-edit-grid { gap: 18rpx; }
.persona-edit-item, .persona-edit-add { position: relative; width: 184rpx; height: 224rpx; border-radius: 30rpx; overflow: hidden; background: rgba(238,244,255,.82); }
.persona-edit-item image { width: 100%; height: 100%; display: block; }
.persona-edit-delete { position: absolute; right: 12rpx; top: 12rpx; width: 42rpx; height: 42rpx; border-radius: 50%; text-align: center; line-height: 38rpx; color: #fff; background: rgba(21,26,45,.56); font-size: 34rpx; }
.persona-edit-add { display: flex; flex-direction: column; align-items: center; justify-content: center; color: rgba(21,26,45,.46); border: 2rpx dashed rgba(108,105,216,.22); box-sizing: border-box; font-size: 23rpx; }
.persona-edit-add .plus { margin-bottom: 10rpx; color: #6c69d8; font-size: 44rpx; font-weight: 800; }
.edit-save { height: 88rpx; line-height: 88rpx; margin-top: 28rpx; border-radius: 999rpx; text-align: center; color: #11162a; background: linear-gradient(135deg, #effffb, #a9ffe7); box-shadow: 0 16rpx 34rpx rgba(169,255,231,.28); font-size: 28rpx; font-weight: 900; }
.edit-save.disabled { opacity: .56; }
4 weeks ago
.setting { display: flex; align-items: center; }
.setting-title { font-size: 28rpx; font-weight: 800; }
.setting-desc { margin-top: 8rpx; color: rgba(22,27,46,.48); font-size: 22rpx; }
.arrow { flex: 1; text-align: right; color: rgba(22,27,46,.4); font-size: 46rpx; }
</style>