|
|
|
|
<template>
|
|
|
|
|
<view class="universe-page">
|
|
|
|
|
<view class="top-safe" :style="{ height: menuButtonInfo.top + 'px' }"></view>
|
|
|
|
|
<view class="float-bar">
|
|
|
|
|
<view class="home-back" @tap="backHome">
|
|
|
|
|
<text class="home-back-icon">‹</text>
|
|
|
|
|
<text>返回首页</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="profile">
|
|
|
|
|
<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>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="stats">
|
|
|
|
|
<view><text>{{ profile.dailyQuota || 3 }}</text><text>今日匹配总次数</text></view>
|
|
|
|
|
<view class="space-stat" @tap="goMySpace"><text>我的空间</text><text>查看动态 ›</text></view>
|
|
|
|
|
</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>
|
|
|
|
|
</view>
|
|
|
|
|
<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>
|
|
|
|
|
<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>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<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>
|
|
|
|
|
<ie-bottom-tab active="universe" :unread-count="unreadCount"></ie-bottom-tab>
|
|
|
|
|
|
|
|
|
|
<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>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import { getIeProfile, saveIeProfile, getIeUnreadCount } from '@/common/ieApi.js'
|
|
|
|
|
import IeBottomTab from '@/components/ie-bottom-tab/ie-bottom-tab.vue'
|
|
|
|
|
import tui from '@/common/httpRequest.js'
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
components: { IeBottomTab },
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
menuButtonInfo: { top: 44 },
|
|
|
|
|
unreadCount: 0,
|
|
|
|
|
unreadTimer: null,
|
|
|
|
|
profile: {},
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
onLoad(options = {}) {
|
|
|
|
|
if (uni.getMenuButtonBoundingClientRect) this.menuButtonInfo = uni.getMenuButtonBoundingClientRect()
|
|
|
|
|
this.unreadCount = Number(options.unreadCount) || 0
|
|
|
|
|
this.loadProfile()
|
|
|
|
|
},
|
|
|
|
|
onShow() {
|
|
|
|
|
this.startUnreadTimer()
|
|
|
|
|
},
|
|
|
|
|
onHide() {
|
|
|
|
|
this.stopUnreadTimer()
|
|
|
|
|
},
|
|
|
|
|
onUnload() {
|
|
|
|
|
this.stopUnreadTimer()
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
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
|
|
|
|
|
},
|
|
|
|
|
async loadProfile() {
|
|
|
|
|
const profile = await getIeProfile()
|
|
|
|
|
if (profile) this.profile = profile
|
|
|
|
|
},
|
|
|
|
|
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] })
|
|
|
|
|
},
|
|
|
|
|
previewPersona(index) {
|
|
|
|
|
uni.previewImage({ urls: this.profile.personaImages || [], current: this.profile.personaImages[index] })
|
|
|
|
|
},
|
|
|
|
|
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('上传失败'))
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
page { background: #f7f9ff; }
|
|
|
|
|
.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); }
|
|
|
|
|
.float-bar { position: relative; z-index: 5; display: flex; align-items: center; justify-content: space-between;}
|
|
|
|
|
.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); }
|
|
|
|
|
.home-back-icon { margin-right: 6rpx;padding-bottom: 6rpx; font-size: 40rpx; line-height: 50rpx; font-weight: 400; }
|
|
|
|
|
.profile { padding-top: 18rpx; text-align: center; }
|
|
|
|
|
.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; }
|
|
|
|
|
.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; }
|
|
|
|
|
.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); }
|
|
|
|
|
.stats view { flex: 1; text-align: center; }
|
|
|
|
|
.stats text { display: block; }
|
|
|
|
|
.stats text:first-child { font-size: 34rpx; font-weight: 800; color: #151a2d; }
|
|
|
|
|
.stats text:last-child { margin-top: 8rpx; color: rgba(22,27,46,.42); font-size: 21rpx; }
|
|
|
|
|
.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; }
|
|
|
|
|
.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; }
|
|
|
|
|
.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); }
|
|
|
|
|
.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; }
|
|
|
|
|
.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; }
|
|
|
|
|
.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>
|