568 lines
11 KiB
Vue
568 lines
11 KiB
Vue
<template>
|
||
<view class="select-role-container">
|
||
<!-- 背景图片 -->
|
||
<image class="background-image" src="/static/background/starbook.jpg" mode="aspectFill"></image>
|
||
<view class="background-overlay"></view>
|
||
|
||
<!-- 内容区域 -->
|
||
<view class="content-wrapper">
|
||
<!-- 导航栏 -->
|
||
<view class="nav-bar">
|
||
<view v-if="!isWelcome" class="nav-back" @click="goBack">
|
||
<text class="back-icon">←</text>
|
||
</view>
|
||
<text class="nav-title">选择身份</text>
|
||
</view>
|
||
|
||
<!-- 明星轮播区域 -->
|
||
<view class="carousel-container">
|
||
<view class="carousel-wrapper" @touchstart="onTouchStart" @touchmove="onTouchMove"
|
||
@touchend="onTouchEnd">
|
||
<view class="star-card" v-for="(star, index) in stars" :key="star.id"
|
||
:class="{ 'active': currentIndex === index }" :style="getCardStyle(index)"
|
||
@click="selectStar(index)">
|
||
<image class="star-image" :src="star.image" mode="aspectFill"></image>
|
||
<view class="star-info">
|
||
<text class="star-name-cn">{{ star.nameCn }}</text>
|
||
<text class="star-name-en">{{ star.nameEn }}</text>
|
||
</view>
|
||
<view v-if="currentIndex === index" class="selected-badge">
|
||
<text class="check-icon">✓</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部文字说明 -->
|
||
<view class="promo-section">
|
||
<view class="promo-text-wrapper">
|
||
<text class="superstar-text">Superstar</text>
|
||
<view class="promo-title">
|
||
<text class="promo-title-line">选择您</text>
|
||
<text class="promo-title-line">最喜爱的明星</text>
|
||
</view>
|
||
</view>
|
||
<text class="promo-desc">{{ getPromoText() }}</text>
|
||
|
||
<!-- 下一步按钮 -->
|
||
<view class="next-button-wrapper">
|
||
<button class="btn-next" @click="handleNext">
|
||
<text class="arrow-icon">→</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue';
|
||
import { useStore } from 'vuex';
|
||
|
||
const props = defineProps({
|
||
isWelcome: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
});
|
||
|
||
const emit = defineEmits(['confirm']);
|
||
|
||
const store = useStore();
|
||
|
||
// 响应式数据
|
||
const currentIndex = ref(0);
|
||
const startX = ref(0);
|
||
const startY = ref(0);
|
||
const isDragging = ref(false);
|
||
const stars = ref([
|
||
{
|
||
id: 1,
|
||
star_id: 87,
|
||
nameCn: '肖战',
|
||
nameEn: 'Xiao Zhan',
|
||
image: '/static/idols/xiaozhan.png'
|
||
},
|
||
{
|
||
id: 2,
|
||
star_id: 88,
|
||
nameCn: '王一博',
|
||
nameEn: 'Wang Yibo',
|
||
image: '/static/idols/wangyibo.png'
|
||
},
|
||
{
|
||
id: 3,
|
||
star_id: 89,
|
||
nameCn: '杨洋',
|
||
nameEn: 'Yang Yang',
|
||
image: '/static/idols/yangyang.png'
|
||
}
|
||
]);
|
||
|
||
// 返回上一页
|
||
const goBack = () => {
|
||
uni.navigateBack();
|
||
};
|
||
|
||
// 获取卡片样式
|
||
const getCardStyle = (index) => {
|
||
const total = stars.value.length;
|
||
const diff = index - currentIndex.value;
|
||
let adjustedDiff = diff;
|
||
|
||
// 处理循环
|
||
if (adjustedDiff > total / 2) {
|
||
adjustedDiff -= total;
|
||
} else if (adjustedDiff < -total / 2) {
|
||
adjustedDiff += total;
|
||
}
|
||
|
||
const absDiff = Math.abs(adjustedDiff);
|
||
|
||
// 如果是当前选中的卡片
|
||
if (absDiff === 0) {
|
||
return {
|
||
transform: `translateX(0rpx) scale(1) rotateZ(0deg)`,
|
||
opacity: 1,
|
||
zIndex: 10
|
||
};
|
||
}
|
||
|
||
// 左右两侧的卡片
|
||
const scale = 0.75;
|
||
const translateX = adjustedDiff * 320;
|
||
const rotateZ = adjustedDiff * 8;
|
||
const opacity = 1;
|
||
const zIndex = total - absDiff;
|
||
|
||
return {
|
||
transform: `translateX(${translateX}rpx) scale(${scale}) rotateZ(${rotateZ}deg)`,
|
||
opacity: opacity,
|
||
zIndex: zIndex
|
||
};
|
||
};
|
||
|
||
// 触摸开始
|
||
const onTouchStart = (e) => {
|
||
startX.value = e.touches[0].clientX;
|
||
startY.value = e.touches[0].clientY;
|
||
isDragging.value = false;
|
||
};
|
||
|
||
// 触摸移动
|
||
const onTouchMove = (e) => {
|
||
const deltaX = e.touches[0].clientX - startX.value;
|
||
const deltaY = e.touches[0].clientY - startY.value;
|
||
|
||
// 判断是否为横向拖动
|
||
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 10) {
|
||
isDragging.value = true;
|
||
}
|
||
};
|
||
|
||
// 触摸结束
|
||
const onTouchEnd = (e) => {
|
||
if (!isDragging.value) return;
|
||
|
||
const deltaX = e.changedTouches[0].clientX - startX.value;
|
||
const threshold = 50;
|
||
|
||
if (Math.abs(deltaX) > threshold) {
|
||
if (deltaX > 0) {
|
||
prevStar();
|
||
} else {
|
||
nextStar();
|
||
}
|
||
}
|
||
|
||
isDragging.value = false;
|
||
};
|
||
|
||
// 选择明星
|
||
const selectStar = (index) => {
|
||
currentIndex.value = index;
|
||
};
|
||
|
||
// 上一个明星
|
||
const prevStar = () => {
|
||
currentIndex.value = (currentIndex.value - 1 + stars.value.length) % stars.value.length;
|
||
};
|
||
|
||
// 下一个明星
|
||
const nextStar = () => {
|
||
currentIndex.value = (currentIndex.value + 1) % stars.value.length;
|
||
};
|
||
|
||
// 获取宣传文字
|
||
const getPromoText = () => {
|
||
const currentStar = stars.value[currentIndex.value];
|
||
return `成为${currentStar.nameCn}的粉丝,铸造关于他的藏品,及关注他的最新相关讯息,一起成为头号粉丝吧!`;
|
||
};
|
||
|
||
// 下一步
|
||
const handleNext = async () => {
|
||
// 欢迎动画模式下,触发确认事件并传递选中的明星
|
||
if (props.isWelcome) {
|
||
const currentStar = stars.value[currentIndex.value];
|
||
emit('confirm', currentStar);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 获取临时存储的注册信息
|
||
const mobile = uni.getStorageSync('temp_register_mobile');
|
||
const password = uni.getStorageSync('temp_register_password');
|
||
const nickname = uni.getStorageSync('temp_register_nickname');
|
||
const currentStar = stars.value[currentIndex.value];
|
||
const star_id = currentStar.star_id;
|
||
|
||
// 验证数据完整性
|
||
if (!mobile || !password || !nickname || !star_id) {
|
||
uni.showToast({
|
||
title: '注册信息不完整,请重新注册',
|
||
icon: 'none'
|
||
});
|
||
// 清除临时数据
|
||
uni.removeStorageSync('temp_register_mobile');
|
||
uni.removeStorageSync('temp_register_password');
|
||
uni.removeStorageSync('temp_register_nickname');
|
||
// 跳转回注册页
|
||
setTimeout(() => {
|
||
uni.reLaunch({
|
||
url: '/pages/register/register'
|
||
});
|
||
}, 1500);
|
||
return;
|
||
}
|
||
|
||
// 显示加载提示
|
||
uni.showLoading({
|
||
title: '注册中...',
|
||
mask: true
|
||
});
|
||
|
||
// 调用注册 API
|
||
await store.dispatch('user/register', {
|
||
mobile,
|
||
password,
|
||
star_id,
|
||
nickname
|
||
});
|
||
|
||
uni.hideLoading();
|
||
|
||
// 清除临时数据
|
||
uni.removeStorageSync('temp_register_mobile');
|
||
uni.removeStorageSync('temp_register_password');
|
||
uni.removeStorageSync('temp_register_nickname');
|
||
|
||
// 添加这行:设置新用户标记
|
||
uni.setStorageSync('is_new_user', true);
|
||
|
||
// 跳转到主页
|
||
uni.reLaunch({
|
||
url: '/pages/square/square'
|
||
});
|
||
|
||
} catch (error) {
|
||
uni.hideLoading();
|
||
|
||
// 处理昵称已存在的情况(409错误)
|
||
if (error.code === 409 || error.message.includes('昵称') || error.message.includes('已存在')) {
|
||
uni.showModal({
|
||
title: '昵称已存在',
|
||
content: '该昵称已被使用,请返回修改昵称',
|
||
showCancel: true,
|
||
confirmText: '返回修改',
|
||
cancelText: '取消',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.navigateBack();
|
||
}
|
||
}
|
||
});
|
||
} else {
|
||
uni.showToast({
|
||
title: error.message || '注册失败,请重试',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.select-role-container {
|
||
position: relative;
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
height: 100vh;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.background-image {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 0;
|
||
}
|
||
|
||
.background-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.2);
|
||
z-index: 1;
|
||
}
|
||
|
||
.content-wrapper {
|
||
position: relative;
|
||
z-index: 10;
|
||
width: 100%;
|
||
flex: 1;
|
||
min-height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding-top: 80rpx;
|
||
box-sizing: border-box;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 导航栏 */
|
||
.nav-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 40rpx 40rpx 40rpx;
|
||
position: relative;
|
||
}
|
||
|
||
.nav-back {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.back-icon {
|
||
font-size: 48rpx;
|
||
color: #000000;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.nav-title {
|
||
flex: 1;
|
||
text-align: center;
|
||
font-size: 36rpx;
|
||
font-weight: 500;
|
||
color: #000000;
|
||
margin-right: 60rpx;
|
||
}
|
||
|
||
/* 轮播容器 */
|
||
.carousel-container {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
overflow: visible;
|
||
padding: 0 100rpx;
|
||
min-height: 0;
|
||
}
|
||
|
||
.carousel-wrapper {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 800rpx;
|
||
max-height: 60vh;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.star-card {
|
||
position: absolute;
|
||
width: 480rpx;
|
||
height: 680rpx;
|
||
border-radius: 30rpx;
|
||
overflow: hidden;
|
||
background: #e6e6e6;
|
||
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3);
|
||
transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||
transform-origin: center center;
|
||
}
|
||
|
||
.star-card.active {
|
||
box-shadow: 0 30rpx 80rpx rgba(138, 43, 226, 0.5);
|
||
}
|
||
|
||
.star-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
}
|
||
|
||
.star-info {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);
|
||
padding: 40rpx 30rpx 30rpx 30rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.star-name-cn {
|
||
font-size: 40rpx;
|
||
font-weight: bold;
|
||
color: #e6e6e6;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.star-name-en {
|
||
font-size: 28rpx;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
}
|
||
|
||
.selected-badge {
|
||
position: absolute;
|
||
top: 20rpx;
|
||
right: 20rpx;
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
background: #000000;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.check-icon {
|
||
font-size: 36rpx;
|
||
color: #e6e6e6;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* 底部宣传区域 */
|
||
.promo-section {
|
||
padding: 30rpx 40rpx 40rpx 40rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
flex-shrink: 0;
|
||
min-height: 0;
|
||
margin-top: auto;
|
||
}
|
||
|
||
.promo-text-wrapper {
|
||
position: relative;
|
||
width: 100%;
|
||
display: flex;
|
||
justify-content: flex-start;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
min-height: 160rpx;
|
||
max-height: 200rpx;
|
||
padding-left: 40rpx;
|
||
}
|
||
|
||
.superstar-text {
|
||
font-size: 120rpx;
|
||
font-family: 'ZaoZiGongFangJianHei-1', sans-serif;
|
||
color: rgba(255, 182, 193, 0.3);
|
||
letter-spacing: 8rpx;
|
||
text-align: left;
|
||
line-height: 1;
|
||
position: absolute;
|
||
top: -65rpx;
|
||
left: 0;
|
||
z-index: 1;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
@media screen and (max-height: 667px) {
|
||
.superstar-text {
|
||
font-size: 100rpx;
|
||
}
|
||
}
|
||
|
||
.promo-title {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
position: relative;
|
||
z-index: 2;
|
||
margin-top: -50rpx;
|
||
}
|
||
|
||
.promo-title-line {
|
||
font-size: 48rpx;
|
||
font-family: 'ZaoZiGongFangJianHei-1', sans-serif;
|
||
color: #000000;
|
||
line-height: 1.4;
|
||
letter-spacing: 6rpx;
|
||
}
|
||
|
||
.promo-desc {
|
||
font-size: 28rpx;
|
||
color: #333333;
|
||
line-height: 1.8;
|
||
text-align: center;
|
||
padding: 0 40rpx;
|
||
margin-bottom: 30rpx;
|
||
flex-shrink: 0;
|
||
max-height: 120rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.next-button-wrapper {
|
||
width: 100%;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
flex-shrink: 0;
|
||
margin-top: auto;
|
||
}
|
||
|
||
.btn-next {
|
||
width: 100rpx;
|
||
height: 100rpx;
|
||
border-radius: 50%;
|
||
background: #000000;
|
||
border: none;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.3);
|
||
padding: 0;
|
||
transition: transform 0.2s ease;
|
||
}
|
||
|
||
.btn-next:active {
|
||
transform: scale(1.15);
|
||
}
|
||
|
||
.btn-next::after {
|
||
border: none;
|
||
}
|
||
|
||
.arrow-icon {
|
||
font-size: 48rpx;
|
||
font-weight: 900;
|
||
color: #e6e6e6;
|
||
}
|
||
</style>
|