654 lines
14 KiB
Vue
654 lines
14 KiB
Vue
<template>
|
||
<view class="castlove-container">
|
||
<!-- 背景图片 -->
|
||
<image class="background-image" src="/static/background/starbook.jpg" mode="aspectFill"></image>
|
||
|
||
<!-- 蒙层 -->
|
||
<!-- <view class="background-overlay"></view> -->
|
||
|
||
<!-- 左上角返回按钮 -->
|
||
<view class="back-button" :style="{ top: backButtonTop }" @click="handleBack">
|
||
<image class="back-icon" src="/static/icon/back.png" mode="aspectFit" />
|
||
</view>
|
||
|
||
<!-- 内容区域 -->
|
||
<scroll-view class="content-wrapper" :class="{ 'fixed-category': isFixed }" scroll-y @scrolltolower="loadMore" @scroll="handleScroll" :show-scrollbar="false" :scroll-top="scrollTop">
|
||
<!-- 区域一:顶部运营轮播图 -->
|
||
<view class="banner-section">
|
||
<swiper
|
||
class="banner-swiper"
|
||
:indicator-dots="true"
|
||
:autoplay="true"
|
||
:interval="3000"
|
||
:duration="500"
|
||
indicator-color="rgba(255, 255, 255, 0.3)"
|
||
indicator-active-color="#FF6B9D"
|
||
>
|
||
<swiper-item v-for="(banner, index) in bannerList" :key="index" @click="handleBannerClick(banner)">
|
||
<image class="banner-image" :src="banner.image_url" mode="aspectFill"></image>
|
||
<view class="banner-overlay">
|
||
<text class="banner-title">{{ banner.title }}</text>
|
||
</view>
|
||
</swiper-item>
|
||
</swiper>
|
||
</view>
|
||
|
||
<!-- 区域二:主Tab标签区(星卡/吧唧/海报) -->
|
||
<view class="main-tab-section">
|
||
<view
|
||
v-for="(tab, index) in mainTabs"
|
||
:key="index"
|
||
class="tab-item"
|
||
@click="handleMainTabClick(tab)"
|
||
>
|
||
<image class="tab-icon" :src="tab.icon" mode="aspectFit"></image>
|
||
<text class="tab-name">{{ tab.name }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 区域三:分类标签区 -->
|
||
<view id="category-section" class="category-section" :class="{ fixed: isFixed }">
|
||
<scroll-view class="category-scroll" scroll-x :show-scrollbar="false">
|
||
<view
|
||
v-for="(category, index) in categories"
|
||
:key="index"
|
||
class="category-item"
|
||
:class="{ active: currentCategory === category.value }"
|
||
@click="handleCategoryChange(category.value)"
|
||
>
|
||
<text class="category-text">{{ category.label }}</text>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- 占位元素,始终存在但透明度为0,避免跳动 -->
|
||
<!-- <view class="category-placeholder" :style="{ opacity: isFixed ? 1 : 0, height: isFixed ? '32rpx' : '0' }"></view> -->
|
||
|
||
<!-- 区域四:创作网格列表 -->
|
||
<view class="creation-grid">
|
||
<view
|
||
v-for="(item, index) in creationList"
|
||
:key="item.id"
|
||
class="creation-card"
|
||
@click="handleCardClick(item)"
|
||
>
|
||
<image class="creation-image" :src="item.cover_image" mode="aspectFill"></image>
|
||
<view class="creation-info">
|
||
<view class="creation-id">
|
||
<text class="id-text">#{{ item.certificate_id }}</text>
|
||
</view>
|
||
<view class="creation-meta">
|
||
<view class="creator-info">
|
||
<image class="creator-avatar" :src="item.creator_avatar" mode="aspectFill"></image>
|
||
<text class="creator-name">{{ item.creator_name }}</text>
|
||
</view>
|
||
<view class="like-info">
|
||
<text class="like-icon">♡</text>
|
||
<text class="like-count">{{ formatCount(item.like_count) }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载更多提示 -->
|
||
<view class="loading-more" v-if="loading">
|
||
<text class="loading-text">加载中...</text>
|
||
</view>
|
||
|
||
<view class="no-more" v-if="noMore && creationList.length > 0">
|
||
<text class="no-more-text">没有更多了</text>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, computed } from 'vue';
|
||
|
||
// 判断是否为Android设备
|
||
const isAndroid = ref(false);
|
||
|
||
// 计算返回按钮的top值
|
||
const backButtonTop = computed(() => {
|
||
if (isAndroid.value) {
|
||
return 'calc(env(safe-area-inset-top) + 80rpx)';
|
||
}
|
||
return 'calc(env(safe-area-inset-top) + 32rpx)';
|
||
});
|
||
|
||
// 轮播图数据
|
||
const bannerList = ref([
|
||
{
|
||
image_url: '/static/sucai/image-01.png',
|
||
title: '这河非遇美学宇宙联名',
|
||
link_type: 'activity',
|
||
link_value: '1'
|
||
},
|
||
{
|
||
image_url: '/static/sucai/image-02.png',
|
||
title: '限时创作活动',
|
||
link_type: 'activity',
|
||
link_value: '2'
|
||
},
|
||
{
|
||
image_url: '/static/sucai/image-03.png',
|
||
title: '精选作品展示',
|
||
link_type: 'topic',
|
||
link_value: '3'
|
||
}
|
||
]);
|
||
|
||
// 主Tab配置
|
||
const mainTabs = ref([
|
||
{
|
||
name: '星卡',
|
||
type: 'star_card',
|
||
icon: '/static/sucai/image-04.png'
|
||
},
|
||
{
|
||
name: '吧唧',
|
||
type: 'badge',
|
||
icon: '/static/sucai/image-05.png'
|
||
},
|
||
{
|
||
name: '海报',
|
||
type: 'poster',
|
||
icon: '/static/sucai/image-06.png'
|
||
}
|
||
]);
|
||
|
||
// 分类配置
|
||
const categories = ref([
|
||
{ label: '热门作品', value: 'hot' },
|
||
{ label: '最新作品', value: 'latest' },
|
||
{ label: '星卡', value: 'star_card' },
|
||
{ label: '吧唧', value: 'badge' },
|
||
{ label: '海报', value: 'poster' }
|
||
]);
|
||
|
||
// 当前选中的分类
|
||
const currentCategory = ref('hot');
|
||
|
||
// 创作列表
|
||
const creationList = ref([]);
|
||
|
||
// 分页参数
|
||
const page = ref(1);
|
||
const pageSize = ref(10);
|
||
const loading = ref(false);
|
||
const noMore = ref(false);
|
||
|
||
// 分类栏吸附状态
|
||
const isFixed = ref(false);
|
||
|
||
// 滚动位置
|
||
const scrollTop = ref(0);
|
||
|
||
// 页面加载
|
||
onMounted(() => {
|
||
// 检测平台
|
||
const systemInfo = uni.getSystemInfoSync();
|
||
console.log('[CastloveContent] 系统信息:', systemInfo);
|
||
isAndroid.value = systemInfo.platform === 'android';
|
||
console.log('[CastloveContent] 是否为Android:', isAndroid.value);
|
||
|
||
loadBanners();
|
||
loadCreations();
|
||
});
|
||
|
||
// 加载轮播图
|
||
const loadBanners = async () => {
|
||
try {
|
||
// TODO: 调用后台接口获取轮播图数据
|
||
// const res = await request('/api/castlove/banners', 'GET');
|
||
// bannerList.value = res.data;
|
||
} catch (e) {
|
||
console.error('加载轮播图失败:', e);
|
||
}
|
||
};
|
||
|
||
// 加载创作列表
|
||
const loadCreations = async () => {
|
||
if (loading.value || noMore.value) return;
|
||
|
||
loading.value = true;
|
||
try {
|
||
// TODO: 调用后台接口获取创作列表
|
||
// const res = await request('/api/castlove/creations', 'GET', {
|
||
// category: currentCategory.value,
|
||
// page: page.value,
|
||
// page_size: pageSize.value
|
||
// });
|
||
|
||
// 模拟数据
|
||
const mockData = generateMockData();
|
||
|
||
if (page.value === 1) {
|
||
creationList.value = mockData;
|
||
} else {
|
||
creationList.value = [...creationList.value, ...mockData];
|
||
}
|
||
|
||
if (mockData.length < pageSize.value) {
|
||
noMore.value = true;
|
||
}
|
||
} catch (e) {
|
||
console.error('加载创作列表失败:', e);
|
||
uni.showToast({
|
||
title: '加载失败',
|
||
icon: 'none'
|
||
});
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
// 素材图片列表
|
||
const sucaiImages = [
|
||
'/static/sucai/image-01.png',
|
||
'/static/sucai/image-02.png',
|
||
'/static/sucai/image-03.png',
|
||
'/static/sucai/image-04.png',
|
||
'/static/sucai/image-05.png',
|
||
'/static/sucai/image-06.png',
|
||
'/static/sucai/image-07.png',
|
||
'/static/sucai/image-08.png',
|
||
'/static/sucai/image-09.png',
|
||
'/static/sucai/image-10.png',
|
||
'/static/sucai/image-11.png',
|
||
'/static/sucai/image-12.png',
|
||
'/static/sucai/image-13.png',
|
||
'/static/sucai/image-14.png',
|
||
'/static/sucai/image-15.png',
|
||
'/static/sucai/image-16.png'
|
||
];
|
||
|
||
// 生成模拟数据
|
||
const generateMockData = () => {
|
||
const data = [];
|
||
for (let i = 0; i < pageSize.value; i++) {
|
||
// 随机选择一张素材图片
|
||
const randomImageIndex = Math.floor(Math.random() * sucaiImages.length);
|
||
|
||
data.push({
|
||
id: `${Date.now()}_${i}`,
|
||
cover_image: sucaiImages[randomImageIndex],
|
||
certificate_id: `ZUA-20260409-${Math.random().toString(36).substr(2, 9).toUpperCase()}`,
|
||
creator_name: `用户${Math.floor(Math.random() * 1000)}`,
|
||
creator_avatar: '/static/avatar/default.png',
|
||
like_count: Math.floor(Math.random() * 1000),
|
||
type: currentCategory.value === 'hot' || currentCategory.value === 'latest'
|
||
? ['star_card', 'badge', 'poster'][Math.floor(Math.random() * 3)]
|
||
: currentCategory.value
|
||
});
|
||
}
|
||
return data;
|
||
};
|
||
|
||
// 轮播图点击
|
||
const handleBannerClick = (banner) => {
|
||
console.log('点击轮播图:', banner);
|
||
// TODO: 根据link_type跳转
|
||
};
|
||
|
||
// 主Tab点击 - 进入铸造页面
|
||
const handleMainTabClick = (tab) => {
|
||
console.log('点击主Tab:', tab);
|
||
// 只有星卡类型才跳转到创建页面
|
||
if (tab.type === 'star_card') {
|
||
uni.navigateTo({
|
||
url: `/pages/castlove/create?type=${tab.type}&name=${encodeURIComponent(tab.name)}`
|
||
});
|
||
} else {
|
||
// 其他类型暂时提示
|
||
uni.showToast({
|
||
title: `${tab.name}功能开发中`,
|
||
icon: 'none',
|
||
duration: 1500
|
||
});
|
||
}
|
||
};
|
||
|
||
// 分类切换
|
||
const handleCategoryChange = (value) => {
|
||
if (currentCategory.value === value) return;
|
||
|
||
currentCategory.value = value;
|
||
page.value = 1;
|
||
noMore.value = false;
|
||
creationList.value = [];
|
||
|
||
// 滚动到创作网格列表的顶部(分类栏位置)
|
||
// 轮播图360rpx + 主Tab约280rpx + 间距 ≈ 320px
|
||
scrollTop.value = 360;
|
||
// 强制触发滚动更新
|
||
setTimeout(() => {
|
||
scrollTop.value = 361;
|
||
}, 100);
|
||
|
||
loadCreations();
|
||
};
|
||
|
||
// 创作卡片点击
|
||
const handleCardClick = (item) => {
|
||
console.log('点击创作卡片:', item);
|
||
uni.navigateTo({
|
||
url: `/pages/castlove/detail?id=${item.id}`
|
||
});
|
||
};
|
||
|
||
// 加载更多
|
||
const loadMore = () => {
|
||
if (!loading.value && !noMore.value) {
|
||
page.value++;
|
||
loadCreations();
|
||
}
|
||
};
|
||
|
||
// 处理滚动事件
|
||
const handleScroll = (e) => {
|
||
const scrollTop = e.detail.scrollTop;
|
||
// 当滚动超过阈值时,固定分类栏(轮播图360rpx + 主Tab约280rpx + 间距)
|
||
isFixed.value = scrollTop > 360; // 约640rpx转px
|
||
};
|
||
|
||
// 格式化数字
|
||
const formatCount = (count) => {
|
||
if (count >= 10000) {
|
||
return (count / 10000).toFixed(1) + 'w';
|
||
}
|
||
return count;
|
||
};
|
||
|
||
// 返回按钮
|
||
const handleBack = () => {
|
||
uni.navigateBack();
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.castlove-container {
|
||
position: relative;
|
||
width: 100vw;
|
||
height: 100vh;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.background-image {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 0;
|
||
}
|
||
|
||
.background-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 0;
|
||
background: rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
|
||
/* 左上角返回按钮 */
|
||
.back-button {
|
||
position: fixed;
|
||
/* top值通过JS动态设置 */
|
||
left: 32rpx;
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
border-radius: 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 101;
|
||
}
|
||
|
||
.back-icon {
|
||
width: 64rpx;
|
||
height: 64rpx;
|
||
}
|
||
|
||
.content-wrapper {
|
||
position: relative;
|
||
z-index: 1;
|
||
width: 100%;
|
||
height: calc(100vh - 64rpx);
|
||
margin-top: 160rpx;
|
||
padding: 0 24rpx;
|
||
box-sizing: border-box;
|
||
transition: margin-top 0.3s ease;
|
||
}
|
||
|
||
.content-wrapper.fixed-category {
|
||
margin-top: 192rpx;
|
||
}
|
||
|
||
/* 区域一:轮播图 */
|
||
.banner-section {
|
||
width: 100%;
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.banner-swiper {
|
||
width: 100%;
|
||
height: 360rpx;
|
||
border-radius: 24rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.banner-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.banner-overlay {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
padding: 24rpx;
|
||
background: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent);
|
||
}
|
||
|
||
.banner-title {
|
||
font-size: 28rpx;
|
||
color: #fff;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* 区域二:主Tab */
|
||
.main-tab-section {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
align-items: center;
|
||
padding: 24rpx 0;
|
||
}
|
||
|
||
.tab-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 200rpx;
|
||
height: 200rpx;
|
||
background: linear-gradient(135deg, rgba(240, 228, 177, 0.3), rgba(240, 131, 153, 0.3));
|
||
border-radius: 24rpx;
|
||
box-shadow: 0 8rpx 24rpx rgba(255, 107, 157, 0.2);
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.tab-item:active {
|
||
transform: scale(0.95);
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.tab-icon {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
margin-bottom: 12rpx;
|
||
border-radius: 50%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.tab-name {
|
||
font-size: 28rpx;
|
||
color: #fff;
|
||
font-weight: 600;
|
||
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
/* 区域三:分类标签 */
|
||
.category-section {
|
||
margin-bottom: 24rpx;
|
||
transition: all 0.3s ease;
|
||
will-change: transform;
|
||
}
|
||
|
||
.category-section.fixed {
|
||
position: fixed;
|
||
top: 96rpx;
|
||
left: 24rpx;
|
||
right: 24rpx;
|
||
z-index: 100;
|
||
padding: 16rpx 0;
|
||
}
|
||
|
||
/* 分类占位符 */
|
||
.category-placeholder {
|
||
margin-bottom: 4rpx;
|
||
transition: all 0.3s ease;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.category-scroll {
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.category-item {
|
||
display: inline-block;
|
||
padding: 16rpx 32rpx;
|
||
margin-right: 16rpx;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
border-radius: 40rpx;
|
||
backdrop-filter: blur(10rpx);
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.category-item.active {
|
||
background: linear-gradient(135deg, #F0E4B1, #F08399);
|
||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 157, 0.4);
|
||
}
|
||
|
||
.category-text {
|
||
font-size: 26rpx;
|
||
color: #fff;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.category-item.active .category-text {
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* 区域四:创作网格 */
|
||
.creation-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: space-between;
|
||
padding-bottom: 40rpx;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.creation-card {
|
||
width: 48%;
|
||
margin-bottom: 24rpx;
|
||
background: rgba(255, 255, 255, 0.15);
|
||
border-radius: 20rpx;
|
||
overflow: hidden;
|
||
backdrop-filter: blur(10rpx);
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.creation-image {
|
||
width: 100%;
|
||
height: 400rpx;
|
||
}
|
||
|
||
.creation-info {
|
||
padding: 16rpx;
|
||
}
|
||
|
||
.creation-id {
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.id-text {
|
||
font-size: 22rpx;
|
||
color: #FFB800;
|
||
font-weight: 600;
|
||
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.creation-meta {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.creator-info {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.creator-avatar {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
border-radius: 50%;
|
||
margin-right: 8rpx;
|
||
}
|
||
|
||
.creator-name {
|
||
font-size: 22rpx;
|
||
color: #fff;
|
||
}
|
||
|
||
.like-info {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.like-icon {
|
||
font-size: 28rpx;
|
||
color: #FF6B9D;
|
||
margin-right: 4rpx;
|
||
}
|
||
|
||
.like-count {
|
||
font-size: 22rpx;
|
||
color: #fff;
|
||
}
|
||
|
||
/* 加载提示 */
|
||
.loading-more,
|
||
.no-more {
|
||
text-align: center;
|
||
padding: 32rpx 0;
|
||
}
|
||
|
||
.loading-text,
|
||
.no-more-text {
|
||
font-size: 24rpx;
|
||
color: rgba(255, 255, 255, 0.6);
|
||
}
|
||
</style>
|