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.

374 lines
16 KiB

3 weeks ago
<template>
<view class="setup-page">
<view class="top-safe" :style="{ height: menuButtonInfo.top + 'px' }"></view>
<view class="nav">
<view class="back" @tap="back"></view>
<view class="title">{{ isEdit ? '编辑 i/e 资料' : '先认识一下你' }}</view>
1 week ago
<view class="step">{{ step }}/5</view>
3 weeks ago
</view>
1 week ago
<view class="progress"><view :style="{ width: step * 20 + '%' }"></view></view>
3 weeks ago
<view class="card" v-if="step === 1">
<view class="eyebrow">Your Energy</view>
<view class="headline">今天更像 i 还是 e</view>
<view class="mode-card i" :class="{ active: form.currentMode === 'i' }" @tap="form.currentMode = 'i'">
<view class="mark">i</view>
<view><view class="mode-title">安静陪伴</view><view class="mode-desc">慢回复允许沉默低压力靠近</view></view>
</view>
<view class="mode-card e" :class="{ active: form.currentMode === 'e' }" @tap="form.currentMode = 'e'">
<view class="mark">e</view>
<view><view class="mode-title">轻轻热闹</view><view class="mode-desc">有人开场聊点废话把情绪拉亮</view></view>
</view>
</view>
<view class="card" v-if="step === 2">
<view class="eyebrow">Light Profile</view>
<view class="headline">设置一个轻社交资料</view>
<view class="avatar-wrap" @tap="chooseAvatar">
<image class="avatar-img" v-if="form.avatarUrl" :src="form.avatarUrl" mode="aspectFill"></image>
<view class="avatar-text" v-else>{{ avatarPreview }}</view>
<view class="avatar-tip">点一下换头像</view>
</view>
<view class="random-name" @tap="randomName">随机一个昵称</view>
<input class="input" v-model="form.anonymousName" maxlength="16" placeholder="昵称,例如:南门慢跑员" />
<view class="mini-title">性别</view>
<view class="gender-row">
<view class="gender-chip" v-for="item in genderOptions" :key="item.key" :class="{ active: form.gender === item.key }" @tap="form.gender = item.key">
{{ item.label }}
</view>
</view>
<textarea class="textarea" v-model="form.intro" maxlength="80" placeholder="一句轻介绍:我通常慢热,但会认真听。" />
</view>
<view class="card" v-if="step === 3">
<view class="eyebrow">Tags</view>
<view class="headline">选择几个此刻标签</view>
<view class="tag-grid">
<view class="tag" v-for="item in tagOptions" :key="item" :class="{ active: form.interestTags.includes(item) }" @tap="toggleTag(item)">
{{ item }}
</view>
</view>
</view>
<view class="card" v-if="step === 4">
1 week ago
<view class="eyebrow">Persona Cards</view>
<view class="headline">上传你的人格卡片</view>
<view class="card-desc">最多 5 像个人主页的形象展示别人点击头像时可以看到</view>
<view class="persona-grid">
<view class="persona-item" v-for="(img, index) in form.personaImages" :key="img">
<image :src="img" mode="aspectFill" @tap="previewPersona(index)"></image>
<view class="persona-delete" @tap.stop="removePersonaImage(index)">×</view>
</view>
<view class="persona-add" v-if="form.personaImages.length < 5" @tap="choosePersonaImages">
<view class="plus"></view>
<view>添加图片</view>
</view>
</view>
</view>
<view class="card" v-if="step === 5">
3 weeks ago
<view class="eyebrow">Match Target</view>
<view class="headline">你现在想遇见哪类人</view>
<view class="target-card" v-for="item in targetOptions" :key="item.key" :class="{ active: form.targetModePreference === item.key }" @tap="form.targetModePreference = item.key">
<view class="target-title">{{ item.title }}</view>
<view class="target-desc">{{ item.desc }}</view>
</view>
<view class="mini-title target-gender-title">想匹配的性别</view>
<view class="gender-row">
<view class="gender-chip" v-for="item in targetGenderOptions" :key="item.key" :class="{ active: form.targetGenderPreference === item.key }" @tap="form.targetGenderPreference = item.key">
{{ item.label }}
</view>
</view>
</view>
<view class="actions">
<view class="ghost" @tap="prev" v-if="step > 1">上一步</view>
1 week ago
<view class="solid" @tap="next">{{ step === 5 ? '进入 i/e 此刻' : '继续' }}</view>
3 weeks ago
</view>
</view>
</template>
<script>
import { getIeProfile, saveIeProfile } from '@/common/ieApi.js'
import tui from '@/common/httpRequest.js'
export default {
data() {
return {
menuButtonInfo: { top: 44 },
step: 1,
isEdit: false,
submitting: false,
profileCompleted: 0,
form: {
currentMode: 'i',
targetModePreference: 'any',
targetGenderPreference: 'any',
anonymousName: '',
avatarText: '我',
avatarUrl: '',
gender: 'unknown',
intro: '',
1 week ago
interestTags: [],
personaImages: []
3 weeks ago
},
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: {
avatarPreview() {
return (this.form.anonymousName || this.form.avatarText || '我').slice(0, 1)
}
},
onLoad(options) {
if (uni.getMenuButtonBoundingClientRect) this.menuButtonInfo = uni.getMenuButtonBoundingClientRect()
this.isEdit = options && options.edit === '1'
this.loadProfile()
},
2 weeks ago
onShow(){
uni.authorize({
scope: 'scope.writePhotosAlbum',
success() {
},
fail() {
this.tui.toast("您未授权,图片上传功能可能会出现错误")
}
})
},
3 weeks ago
methods: {
async loadProfile() {
const profile = await getIeProfile()
if (!profile) return
this.profileCompleted = profile.profileCompleted || 0
this.form.currentMode = profile.currentMode || 'i'
this.form.targetModePreference = profile.targetModePreference || 'any'
this.form.targetGenderPreference = profile.targetGenderPreference || 'any'
this.form.anonymousName = this.isEdit || profile.profileCompleted === 1 ? (profile.anonymousName || '') : ''
this.form.avatarText = profile.avatarText || '我'
this.form.avatarUrl = profile.avatarUrl || ''
this.form.gender = profile.gender || 'unknown'
this.form.intro = profile.intro || ''
this.form.interestTags = profile.interestTags || []
1 week ago
this.form.personaImages = profile.personaImages || []
3 weeks ago
},
toggleTag(tag) {
const index = this.form.interestTags.indexOf(tag)
if (index > -1) {
this.form.interestTags.splice(index, 1)
return
}
if (this.form.interestTags.length >= 6) {
uni.showToast({ title: '最多选择 6 个标签', icon: 'none' })
return
}
this.form.interestTags.push(tag)
},
randomName() {
this.form.anonymousName = this.nameSeeds[Math.floor(Math.random() * this.nameSeeds.length)]
},
chooseAvatar() {
2 weeks ago
let that = this;
uni.chooseMedia({
3 weeks ago
count: 1,
2 weeks ago
mediaType: ['image'],
sourceType: ['camera', 'album'],
camera: 'back',
success(res) {
that.upLoadFile(res.tempFiles[0].tempFilePath)
},
fail() {
uni.hideLoading();
3 weeks ago
}
})
},
2 weeks ago
async upLoadFile(path) {
let that = this;
uni.showLoading({
title: '加载中...',
mask: true
2 weeks ago
})
2 weeks ago
let hiver_token = uni.getStorageSync("hiver_token")
await uni.uploadFile({
url: that.tui.interfaceUrl() + '/upload/file',
filePath: path,
name: 'file',
header: {
"content-type": "multipart/form-data",
'accessToken': hiver_token
},
formData: {},
success: (uploadFileRes) => {
let path = JSON.parse(uploadFileRes.data)
this.form.avatarUrl = path.result
},
fail: (err) => {
uni.hideLoading();
uni.showToast({
title: JSON.stringify(err),
icon: 'none'
})
}
});
await setTimeout(res => {
that.parentId = ''
// uni.hideLoading();
}, 1000)
2 weeks ago
},
1 week ago
choosePersonaImages() {
const remain = 5 - this.form.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 || []
for (const file of files) {
if (this.form.personaImages.length >= 5) break
const url = await this.uploadPersonaFile(file.tempFilePath)
if (url) this.form.personaImages.push(url)
}
}
})
},
uploadPersonaFile(path) {
if (!path) return Promise.resolve('')
const hiverToken = uni.getStorageSync('hiver_token')
uni.showLoading({ title: '上传中...', mask: true })
return new Promise((resolve) => {
uni.uploadFile({
url: this.tui.interfaceUrl() + '/upload/file',
filePath: path,
name: 'file',
header: {
'content-type': 'multipart/form-data',
'accessToken': hiverToken
},
success: (uploadFileRes) => {
try {
const data = JSON.parse(uploadFileRes.data)
resolve(data.result || '')
} catch (e) {
resolve('')
}
},
fail: () => resolve(''),
complete: () => uni.hideLoading()
})
})
},
removePersonaImage(index) {
this.form.personaImages.splice(index, 1)
},
previewPersona(index) {
uni.previewImage({ urls: this.form.personaImages, current: this.form.personaImages[index] })
},
3 weeks ago
validateStep() {
if (this.step === 2 && !this.form.anonymousName.trim()) {
uni.showToast({ title: '先起一个昵称吧', icon: 'none' })
return false
}
if (this.step === 3 && !this.form.interestTags.length) {
uni.showToast({ title: '至少选一个标签', icon: 'none' })
return false
}
return true
},
prev() { this.step = Math.max(1, this.step - 1) },
async next() {
if (this.submitting) return
if (!this.validateStep()) return
1 week ago
if (this.step < 5) {
3 weeks ago
this.step += 1
return
}
this.submitting = true
uni.showLoading({ title: '正在进入 i/e', mask: true })
try {
const profile = await saveIeProfile({
...this.form,
avatarText: this.avatarPreview
})
if (!profile) return
uni.redirectTo({ url: this.isEdit ? '/package1/ieBrowser/universe' : '/package1/ieBrowser/index' })
} finally {
this.submitting = false
uni.hideLoading()
}
},
back() {
if (this.isEdit) {
uni.redirectTo({ url: '/package1/ieBrowser/universe' })
return
}
uni.redirectTo({ url: '/pages/index/index' })
}
}
}
</script>
<style lang="scss" scoped>
page { background: #f7f9ff; }
.setup-page { min-height: 100vh; padding: 0 32rpx 168rpx; box-sizing: border-box; color: #151a2d; background: radial-gradient(circle at 82% 10%, rgba(169,255,231,.42), transparent 320rpx), radial-gradient(circle at 16% 70%, rgba(255,184,209,.24), transparent 340rpx), linear-gradient(180deg, #fbfdff, #eef4ff 64%, #fff4e8); }
.nav { height: 92rpx; display: flex; align-items: center; }
.back { width: 70rpx; color: rgba(21,26,45,.56); font-size: 56rpx; }
.title { flex: 1; font-size: 32rpx; font-weight: 800; }
.step { color: #6c69d8; font-size: 24rpx; font-weight: 800; }
.progress { height: 10rpx; border-radius: 999rpx; background: rgba(21,26,45,.06); overflow: hidden; }
.progress view { height: 100%; border-radius: 999rpx; background: #a9ffe7; transition: width .25s ease; }
.card { margin-top: 36rpx; padding: 38rpx 32rpx; border-radius: 46rpx; border: 1rpx solid rgba(255,255,255,.82); background: rgba(255,255,255,.64); box-shadow: 0 26rpx 80rpx rgba(96,112,160,.14); backdrop-filter: blur(26rpx); }
.eyebrow { color: rgba(21,26,45,.42); font-size: 22rpx; letter-spacing: 5rpx; text-transform: uppercase; }
.headline { margin-top: 16rpx; margin-bottom: 28rpx; font-size: 46rpx; line-height: 60rpx; font-weight: 800; }
1 week ago
.card-desc { margin: -10rpx 0 24rpx; color: rgba(21,26,45,.52); font-size: 24rpx; line-height: 38rpx; }
3 weeks ago
.mode-card, .target-card { display: flex; align-items: center; margin-top: 22rpx; padding: 28rpx; border-radius: 34rpx; background: rgba(238,244,255,.68); border: 2rpx solid transparent; }
.mode-card.active, .target-card.active { border-color: #a9ffe7; background: rgba(223,254,244,.7); transform: scale(1.01); }
.mark { width: 82rpx; height: 82rpx; margin-right: 22rpx; border-radius: 28rpx; text-align: center; line-height: 82rpx; font-size: 46rpx; font-weight: 800; }
.i .mark { color: #fff; background: #7771d8; }
.e .mark { color: #11162a; background: #a9ffe7; }
.mode-title, .target-title { font-size: 30rpx; font-weight: 800; }
.mode-desc, .target-desc { margin-top: 8rpx; color: rgba(21,26,45,.54); font-size: 23rpx; line-height: 36rpx; }
.mini-title { margin: 24rpx 0 16rpx; color: rgba(21,26,45,.5); font-size: 23rpx; font-weight: 800; }
.target-gender-title { margin-top: 30rpx; }
.gender-row { display: flex; flex-wrap: wrap; }
.gender-chip { 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; }
.gender-chip.active { color: #11162a; background: #a9ffe7; font-weight: 800; }
.avatar-wrap { width: 184rpx; margin: 0 auto 30rpx; text-align: center; }
.avatar-img, .avatar-text { width: 154rpx; height: 154rpx; margin: 0 auto; border-radius: 50%; }
.avatar-img { display: block; }
.avatar-text { text-align: center; line-height: 154rpx; color: #11162a; background: linear-gradient(145deg, #effffb, #a9ffe7); font-size: 58rpx; font-weight: 800; }
.avatar-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; }
.input, .textarea { width: 100%; box-sizing: border-box; border-radius: 28rpx; background: rgba(238,244,255,.78); color: #151a2d; font-size: 27rpx; }
.input { height: 88rpx; padding: 0 26rpx; }
.textarea { height: 180rpx; margin-top: 22rpx; padding: 24rpx 26rpx; line-height: 40rpx; }
.tag-grid { display: flex; flex-wrap: wrap; }
.tag { margin: 0 14rpx 18rpx 0; padding: 16rpx 24rpx; border-radius: 999rpx; color: rgba(21,26,45,.62); background: rgba(238,244,255,.82); font-size: 24rpx; }
.tag.active { color: #11162a; background: #a9ffe7; font-weight: 800; }
1 week ago
.persona-grid { display: flex; flex-wrap: wrap; gap: 18rpx; }
.persona-item, .persona-add { position: relative; width: 184rpx; height: 224rpx; border-radius: 30rpx; overflow: hidden; background: rgba(238,244,255,.82); }
.persona-item image { width: 100%; height: 100%; display: block; }
.persona-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-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-add .plus { margin-bottom: 10rpx; color: #6c69d8; font-size: 44rpx; font-weight: 800; }
3 weeks ago
.actions { position: fixed; left: 32rpx; right: 32rpx; bottom: 42rpx; display: flex; gap: 18rpx; }
.ghost, .solid { flex: 1; height: 96rpx; border-radius: 999rpx; text-align: center; line-height: 96rpx; font-size: 28rpx; font-weight: 800; }
.ghost { color: rgba(21,26,45,.58); background: rgba(255,255,255,.68); }
.solid { color: #11162a; background: linear-gradient(135deg, #effffb, #a9ffe7); box-shadow: 0 22rpx 58rpx rgba(169,255,231,.28); }
</style>