feat: 修改square组件
This commit is contained in:
parent
b14cc119b4
commit
b00f7dabf0
@ -1,15 +1,21 @@
|
||||
<template>
|
||||
<view class="content-tabs">
|
||||
<view v-for="(tab, index) in tabs" :key="tab.key" class="tab-item" :class="{ active: modelValue === tab.key }"
|
||||
:data-key="tab.key" @click="handleTabClick">
|
||||
<view
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="tab.key"
|
||||
class="tab-item"
|
||||
:class="{ active: modelValue === tab.key }"
|
||||
:data-key="tab.key"
|
||||
@click="handleTabClick"
|
||||
>
|
||||
<!-- 背景图片 -->
|
||||
<!-- <image class="tab-bg" :class="{ 'tab-bg-inactive': modelValue !== tab.key }"
|
||||
src="/static/nft/dingbutubiao_liang.png" mode="scaleToFill" /> -->
|
||||
<!-- 左侧图标,绝对浮动覆盖背景左侧色块,不影响文字布局 -->
|
||||
<view class="tab-left" :class="{ 'tab-left-first': index === 2 }">
|
||||
<text v-if="tab.emoji" class="tab-emoji">{{ tab.emoji }}</text>
|
||||
<view class="tab-left">
|
||||
<!-- <text v-if="tab.emoji" class="tab-emoji">{{ tab.emoji }}</text>
|
||||
<image v-else class="tab-icon" :src="tab.icon" mode="aspectFill"
|
||||
:style="{ width: tab.iconWidth + 'rpx', height: tab.iconHeight + 'rpx' }" />
|
||||
:style="{ width: tab.iconWidth + 'rpx', height: tab.iconHeight + 'rpx' }" /> -->
|
||||
<!-- 右侧文字 -->
|
||||
<text class="tab-label">{{ tab.label }}</text>
|
||||
</view>
|
||||
@ -19,56 +25,75 @@
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
modelValue: { type: String, default: 'hot' }
|
||||
})
|
||||
modelValue: { type: String, default: "hot" },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const handleTabClick = (e) => {
|
||||
const key = e.currentTarget.dataset.key
|
||||
console.log(key)
|
||||
emit('update:modelValue', key)
|
||||
}
|
||||
const key = e.currentTarget.dataset.key;
|
||||
console.log(key);
|
||||
emit("update:modelValue", key);
|
||||
};
|
||||
|
||||
const tabs = [
|
||||
{ key: 'displaying', label: '日榜', emoji: null, icon: '/static/square/1.png', iconWidth: 32, iconHeight: 40 },
|
||||
// { key: 'week', label: '周榜', emoji: null, icon: '/static/square/1.png', iconWidth: 32, iconHeight: 40 },
|
||||
// { key: 'month', label: '月榜', emoji: null, icon: '/static/square/1.png', iconWidth: 32, iconHeight: 40 },
|
||||
]
|
||||
{
|
||||
key: "xinghe",
|
||||
label: "星河",
|
||||
emoji: null,
|
||||
icon: "/static/square/1.png",
|
||||
iconWidth: 32,
|
||||
iconHeight: 40,
|
||||
},
|
||||
{
|
||||
key: "xingbang",
|
||||
label: "星榜",
|
||||
emoji: null,
|
||||
icon: "/static/square/1.png",
|
||||
iconWidth: 32,
|
||||
iconHeight: 40,
|
||||
},
|
||||
{
|
||||
key: "guangchang",
|
||||
label: "广场",
|
||||
emoji: null,
|
||||
icon: "/static/square/1.png",
|
||||
iconWidth: 32,
|
||||
iconHeight: 40,
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content-tabs {
|
||||
position: relative;
|
||||
top: unset;
|
||||
left: unset;
|
||||
right: unset;
|
||||
bottom: 16rpx;
|
||||
left: 50%;
|
||||
bottom: 0.25rem;
|
||||
transform: translateX(-50%);
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 8rpx 16rpx 0;
|
||||
padding: 0 4rpx;
|
||||
height: 56rpx;
|
||||
background: transparent;
|
||||
margin: 8rpx 16rpx;
|
||||
padding: 0 8rpx;
|
||||
width: 336rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 16px;
|
||||
overflow: visible;
|
||||
background: #d9d9d91c;
|
||||
box-shadow: 0px 4px 4px 0px #b3323240;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
max-width: 134rpx;
|
||||
width: 112rpx;
|
||||
height: 42rpx;
|
||||
border-radius: 21rpx;
|
||||
opacity: 0.78;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: visible;
|
||||
background: linear-gradient(90deg, rgba(255, 222, 8, 0.2989) -17.54%, rgba(252, 100, 102, 0.61) 64.4%, rgba(244, 88, 104, 0.61) 116.67%);
|
||||
box-shadow: 2px 2px 4px 0px #F2151578;
|
||||
}
|
||||
|
||||
/* 背景图片铺满整个 tab */
|
||||
@ -92,7 +117,6 @@ const tabs = [
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4rpx 30rpx 4rpx 20rpx ;
|
||||
transition: transform 0.25s ease;
|
||||
}
|
||||
|
||||
@ -123,7 +147,13 @@ const tabs = [
|
||||
margin-left: 14rpx;
|
||||
}
|
||||
|
||||
.tab-item.active .tab-label {
|
||||
color: #ffffff;
|
||||
.tab-item.active{
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 222, 8, 0.2989) -17.54%,
|
||||
rgba(252, 100, 102, 0.61) 64.4%,
|
||||
rgba(244, 88, 104, 0.61) 116.67%
|
||||
);
|
||||
box-shadow: 2px 2px 4px 0px #f2151578;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
v-for="(category, index) in categories"
|
||||
:key="index"
|
||||
class="category-item"
|
||||
:class="{ active: category === category.value }"
|
||||
:class="{ active: category.value === activeCategory }"
|
||||
@click="handleCategoryChange(category.value)"
|
||||
>
|
||||
<text class="category-text">{{ category.label }}</text>
|
||||
@ -110,26 +110,21 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { onShow, onHide } from "@dcloudio/uni-app";
|
||||
import { getInspirationFlowApi } from '@/utils/api.js'
|
||||
import { getAssetCoverRealUrl } from '@/utils/assetImageHelper.js'
|
||||
|
||||
const props = defineProps({
|
||||
screenWidth: { type: Number, default: 375 },
|
||||
screenHeight: { type: Number, default: 812 },
|
||||
bannerBottom: { type: Number, default: 200 },
|
||||
useMockData: { type: Boolean, default: false },
|
||||
category: { type: String, default: '' },
|
||||
isActive: { type: Boolean, default: true },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['cardClick', 'scroll', 'loaded', 'mainTabClick', 'categoryChange'])
|
||||
// 组件不接收任何 prop,所有数据/状态内部管理
|
||||
const emit = defineEmits(['cardClick', 'loaded'])
|
||||
|
||||
// ========== 内部状态 ==========
|
||||
const creationList = ref([])
|
||||
const cursor = ref('')
|
||||
const isLoading = ref(false)
|
||||
const noMore = ref(false)
|
||||
let isComponentMounted = false
|
||||
const likingMap = ref({});
|
||||
const likingMap = ref({})
|
||||
const activeCategory = ref('hot') // 当前选中的分类
|
||||
const isComponentActive = ref(true) // 组件是否处于激活态
|
||||
|
||||
// ========== 区域 A:主Tab 配置(内化) ==========
|
||||
const mainTabs = [
|
||||
@ -168,7 +163,7 @@ const categories = [
|
||||
{ label: '海报', value: 'poster' },
|
||||
]
|
||||
|
||||
// ========== 模板 ref(expose 给父组件 spotlight 系统 + 占位元素) ==========
|
||||
// ========== 模板 ref(暴露给父组件 spotlight 系统 + 占位元素) ==========
|
||||
const mainTabsRef = ref(null)
|
||||
const categoryRef = ref(null)
|
||||
|
||||
@ -188,14 +183,19 @@ watch(isFixed, (val) => {
|
||||
}, 450)
|
||||
})
|
||||
|
||||
// ========== 内部事件 ==========
|
||||
// ========== 内部事件处理(不再冒泡给父组件) ==========
|
||||
|
||||
// 主Tab点击 - 内部直接跳转铸造页
|
||||
const handleMainTabClick = (tab) => {
|
||||
emit('mainTabClick', tab)
|
||||
uni.navigateTo({
|
||||
url: `/pages/castlove/mall?type=${encodeURIComponent(tab.type)}`,
|
||||
})
|
||||
}
|
||||
|
||||
// 分类切换 - 内部更新 activeCategory
|
||||
const handleCategoryChange = (value) => {
|
||||
if (props.category === value) return
|
||||
emit('categoryChange', value)
|
||||
if (activeCategory.value === value) return
|
||||
activeCategory.value = value
|
||||
}
|
||||
|
||||
// ========== 父级调用的方法(滚动 → fixed 切换) ==========
|
||||
@ -224,7 +224,7 @@ function remeasure() {
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 原有:创作网格逻辑(保留) ==========
|
||||
// ========== 创作网格逻辑(保留) ==========
|
||||
const formatCount = (count) => {
|
||||
if (!count) return '0'
|
||||
if (count >= 10000) return (count / 10000).toFixed(1) + 'w'
|
||||
@ -246,28 +246,40 @@ const handleCardClick = (item) => {
|
||||
emit('cardClick', item)
|
||||
}
|
||||
|
||||
// 把后端返回的 cover_url 转成真实可访问的 URL(OSS 预签名前端实现)
|
||||
async function resolveItemImage(item) {
|
||||
if (!item) return item
|
||||
const cover = item.cover_url || item.cover_image || ''
|
||||
if (cover) {
|
||||
item.cover_image = await getAssetCoverRealUrl(cover)
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
const loadUsers = async () => {
|
||||
if (!isComponentMounted) return Promise.resolve()
|
||||
cursor.value = ''
|
||||
isLoading.value = true
|
||||
noMore.value = false
|
||||
try {
|
||||
const res = await getInspirationFlowApi({ limit: 20, type: props.category, cursor: '' })
|
||||
if (!isComponentMounted) return
|
||||
const res = await getInspirationFlowApi({ limit: 20, type: activeCategory.value, cursor: '' })
|
||||
if (res.code === 200 && res.data?.items && res.data.items.length > 0) {
|
||||
const items = res.data.items
|
||||
cursor.value = res.data.cursor || ''
|
||||
creationList.value = items.map((item) => {
|
||||
return {
|
||||
id: item.asset_id,
|
||||
certificate_id: item.asset_id,
|
||||
cover_image: item.cover_url || '',
|
||||
creator_avatar: item.owner_avatar || '',
|
||||
creator_name: item.owner_nickname || item.name || '',
|
||||
like_count: item.likes || item.like_count || 0,
|
||||
is_liked: item.is_liked || false,
|
||||
}
|
||||
})
|
||||
// 逐个把 cover_url 转成真实可访问的 URL
|
||||
creationList.value = await Promise.all(
|
||||
items.map(async (item) => {
|
||||
const resolved = await resolveItemImage({ ...item })
|
||||
return {
|
||||
id: item.asset_id,
|
||||
certificate_id: item.asset_id,
|
||||
cover_image: resolved.cover_image,
|
||||
creator_avatar: item.owner_avatar || '',
|
||||
creator_name: item.owner_nickname || item.name || '',
|
||||
like_count: item.likes || item.like_count || 0,
|
||||
is_liked: item.is_liked || false,
|
||||
}
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
noMore.value = true
|
||||
}
|
||||
@ -280,25 +292,27 @@ const loadUsers = async () => {
|
||||
}
|
||||
|
||||
const loadMore = async () => {
|
||||
if (!isComponentMounted || isLoading.value || noMore.value) return
|
||||
if (isLoading.value || noMore.value) return
|
||||
isLoading.value = true
|
||||
try {
|
||||
const res = await getInspirationFlowApi({ limit: 20, type: props.category, cursor: cursor.value })
|
||||
if (!isComponentMounted) return
|
||||
const res = await getInspirationFlowApi({ limit: 20, type: activeCategory.value, cursor: cursor.value })
|
||||
if (res.code === 200 && res.data?.items && res.data.items.length > 0) {
|
||||
const items = res.data.items
|
||||
cursor.value = res.data.cursor || ''
|
||||
const newItems = items.map((item) => {
|
||||
return {
|
||||
id: item.asset_id,
|
||||
certificate_id: item.asset_id,
|
||||
cover_image: item.cover_url || '',
|
||||
creator_avatar: item.owner_avatar || '',
|
||||
creator_name: item.owner_nickname || item.name || '',
|
||||
like_count: item.likes || item.like_count || 0,
|
||||
is_liked: item.is_liked || false,
|
||||
}
|
||||
})
|
||||
const newItems = await Promise.all(
|
||||
items.map(async (item) => {
|
||||
const resolved = await resolveItemImage({ ...item })
|
||||
return {
|
||||
id: item.asset_id,
|
||||
certificate_id: item.asset_id,
|
||||
cover_image: resolved.cover_image,
|
||||
creator_avatar: item.owner_avatar || '',
|
||||
creator_name: item.owner_nickname || item.name || '',
|
||||
like_count: item.likes || item.like_count || 0,
|
||||
is_liked: item.is_liked || false,
|
||||
}
|
||||
}),
|
||||
)
|
||||
creationList.value = [...creationList.value, ...newItems]
|
||||
} else {
|
||||
noMore.value = true
|
||||
@ -311,25 +325,21 @@ const loadMore = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.category,
|
||||
() => {
|
||||
loadUsers()
|
||||
},
|
||||
)
|
||||
// 分类变化 → 重新加载
|
||||
watch(activeCategory, () => {
|
||||
loadUsers()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.isActive,
|
||||
(active) => {
|
||||
if (active && creationList.value.length === 0) {
|
||||
loadUsers()
|
||||
}
|
||||
},
|
||||
)
|
||||
// 激活态变化 → 首次激活时拉数据
|
||||
watch(isComponentActive, (active) => {
|
||||
if (active && creationList.value.length === 0) {
|
||||
loadUsers()
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
loadUsers()
|
||||
isComponentMounted = true
|
||||
|
||||
uni.$on('assetLiked', ({ asset_id, data }) => {
|
||||
likingMap.value = { ...likingMap.value, [asset_id]: true }
|
||||
setTimeout(() => {
|
||||
@ -360,20 +370,21 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
// loadUsers()
|
||||
isComponentActive.value = true
|
||||
})
|
||||
|
||||
onHide(() => {
|
||||
isComponentActive.value = false
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
isComponentMounted = false
|
||||
uni.$off('assetLiked')
|
||||
})
|
||||
|
||||
// ========== 对外暴露 ==========
|
||||
// ========== 对外暴露(仅 spotlight / 滚动需要的方法) ==========
|
||||
defineExpose({
|
||||
// 原有的
|
||||
loadMore,
|
||||
getCardRefs,
|
||||
// 新增:spotlight 系统 + 滚动 fixed + 占位
|
||||
mainTabsRef,
|
||||
categoryRef,
|
||||
categoryHeight,
|
||||
|
||||
@ -1,9 +1,28 @@
|
||||
<template>
|
||||
<view class="hot-category-block">
|
||||
<!-- 标题 -->
|
||||
<!-- <view class="block-title">
|
||||
<text class="recommend-text"> {{ title }}</text>
|
||||
</view> -->
|
||||
<!-- Tab 栏 -->
|
||||
<view class="ranking-tabs">
|
||||
<view
|
||||
v-for="tab in tabs"
|
||||
:key="tab.key"
|
||||
class="ranking-tab-item"
|
||||
:class="{ active: activeTabKey === tab.key }"
|
||||
:data-key="tab.key"
|
||||
@click="handleTabClick"
|
||||
>
|
||||
<image
|
||||
v-if="tab.icon"
|
||||
class="ranking-tab-icon"
|
||||
:src="tab.icon"
|
||||
mode="aspectFit"
|
||||
:style="{
|
||||
width: (tab.iconWidth || 32) + 'rpx',
|
||||
height: (tab.iconHeight || 40) + 'rpx',
|
||||
}"
|
||||
/>
|
||||
<!-- <text class="ranking-tab-label">{{ tab.label }}</text> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 骨架屏 -->
|
||||
<view v-if="loading" class="grid-skeleton">
|
||||
@ -22,6 +41,10 @@
|
||||
v-for="(item, index) in items"
|
||||
:key="item.id || index"
|
||||
class="grid-card"
|
||||
:class="{
|
||||
'grid-card-top': index < 3,
|
||||
[`grid-card-top-${index + 1}`]: index < 3,
|
||||
}"
|
||||
@click="handleCardClick(item)"
|
||||
>
|
||||
<!-- 点赞动效波纹 -->
|
||||
@ -33,15 +56,35 @@
|
||||
class="wf-like-wave wf-like-wave-inner"
|
||||
:class="{ 'wf-like-wave-active': likingMap[item.id] }"
|
||||
/>
|
||||
<!-- 底部信息模块:独立于图片 -->
|
||||
<view class="card-bottom" :class="`card-bottom-${index + 1}`">
|
||||
<image
|
||||
class="card-image"
|
||||
:src="item.cover_url || item.cover_image || ''"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="like-badge" :class="`like-badge-${index + 1}`">
|
||||
<view class="like-icon-wrapper">
|
||||
|
||||
<!-- 单行布局:藏品图片 + 头像 + 点赞数 + TOP 标签 -->
|
||||
<view class="card-row">
|
||||
<view class="card-image-wrap">
|
||||
<image
|
||||
class="card-image"
|
||||
:src="item.cover_url || item.cover_image || ''"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<!-- 前 3 名专属:包裹整个卡片的边框图 -->
|
||||
<image
|
||||
v-if="index < 3"
|
||||
class="frame-image"
|
||||
:src="TOP_FRAME_MAP[index]"
|
||||
mode="scaleToFill"
|
||||
/>
|
||||
<!-- 前 3 名专属:左上角奖牌装饰 -->
|
||||
<image
|
||||
v-if="index < 3"
|
||||
class="card-medal"
|
||||
:src="MEDAL_MAP[index]"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<view
|
||||
class="like-info"
|
||||
:class="{ [`like-info-top-${index + 1}`]: index < 3 }"
|
||||
>
|
||||
<view class="like-row">
|
||||
<image
|
||||
class="like-icon"
|
||||
:src="
|
||||
@ -50,47 +93,24 @@
|
||||
: '/static/icon/heart-icon-false.png'
|
||||
"
|
||||
mode="aspectFit"
|
||||
>
|
||||
</image>
|
||||
/>
|
||||
<text class="like-count">{{ formatCount(item.like_count) }}</text>
|
||||
</view>
|
||||
<text class="user-name">{{
|
||||
item.owner_nickname || item.creator_name || item.name || ""
|
||||
}}</text>
|
||||
<text class="user-number">No.{{ item.owner_uid || "" }}</text>
|
||||
</view>
|
||||
<!-- 用户信息 -->
|
||||
<view class="card-info" :class="`card-info-${index + 1}`">
|
||||
<view class="user-info">
|
||||
<view v-if="index >= 3" class="top-badge">
|
||||
<view class="badge-rank">
|
||||
<image
|
||||
class="user-avatar"
|
||||
:src="item.owner_avatar || item.creator_avatar || ''"
|
||||
mode="aspectFill"
|
||||
>
|
||||
</image>
|
||||
<text class="user-name">{{
|
||||
item.owner_nickname || item.creator_name || item.name || ""
|
||||
}}</text>
|
||||
class="badge-rank-icon"
|
||||
src="/static/square/top/top.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="badge-rank-number">{{ index + 1 }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 前三名专属:包裹 card-bottom 的边框 -->
|
||||
<view v-if="index < 3" class="card-frame">
|
||||
<image
|
||||
class="frame-image"
|
||||
:src="TOP_FRAME_MAP[index]"
|
||||
mode="scaleToFill"
|
||||
></image>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 前三名专属:右上角装饰图(位于 grid-card 层级,避免被 card-bottom 的 overflow 裁切) -->
|
||||
<view v-if="index < 3" class="corner-decoration">
|
||||
<image :src="TOP_ICON_MAP[index]" mode="aspectFit"></image>
|
||||
</view>
|
||||
<!-- Top 排名标签 -->
|
||||
<view class="top-badge" :class="`top-badge-${index + 1}`">
|
||||
<view
|
||||
v-if="index < 3"
|
||||
class="corner-decoration top-corner-decoration"
|
||||
>
|
||||
<image :src="TOP_ICON_MAP[index]" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="badge-rank">TOP {{ index + 1 }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -98,7 +118,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted, onUnmounted } from "vue";
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { getHotRankingApi } from "@/utils/api.js";
|
||||
import { getAssetCoverRealUrl } from "@/utils/assetImageHelper.js";
|
||||
@ -106,51 +126,79 @@ import { getAssetCoverRealUrl } from "@/utils/assetImageHelper.js";
|
||||
// 把后端返回的 cover_url / cover_image 转成真实可访问的 URL
|
||||
// 处理 3 种形态:/static/... (本地)、相对路径 (需 presign)、完整 URL (可能过期)
|
||||
async function resolveItemUrls(item) {
|
||||
if (!item) return item
|
||||
const cover = item.cover_url || item.cover_image || ""
|
||||
if (!item) return item;
|
||||
const cover = item.cover_url || item.cover_image || "";
|
||||
if (cover) {
|
||||
item.cover_url = await getAssetCoverRealUrl(cover)
|
||||
item.cover_url = await getAssetCoverRealUrl(cover);
|
||||
}
|
||||
return item
|
||||
return item;
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: "在线榜单",
|
||||
},
|
||||
dimension: {
|
||||
type: String,
|
||||
default: "displaying",
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["cardClick"]);
|
||||
|
||||
const items = ref([]);
|
||||
const loading = ref(false);
|
||||
const likingMap = ref({});
|
||||
const activeTabKey = ref("");
|
||||
|
||||
// 前三名对应的边框图与右上角装饰图
|
||||
const TOP_FRAME_MAP = {
|
||||
0: "/static/square/top/TOP1biankuang1.png",
|
||||
1: "/static/square/top/TOP2biankuang2.png",
|
||||
2: "/static/square/top/TOP3biankuangpng3.png",
|
||||
};
|
||||
const TOP_ICON_MAP = {
|
||||
// Tab 配置(直接写死在组件内)
|
||||
// 新增 tab 在这里 push 一项即可:{ key, label, icon, iconWidth, iconHeight, fetch }
|
||||
const tabs = [
|
||||
{
|
||||
key: "hot",
|
||||
label: "热度榜",
|
||||
icon: "/static/square/rementubiao.png",
|
||||
iconWidth: 32,
|
||||
iconHeight: 40,
|
||||
fetch: () => getHotRankingApi("displaying", null, 1, 11),
|
||||
},
|
||||
];
|
||||
|
||||
// 前 3 名奖牌图标
|
||||
const MEDAL_MAP = {
|
||||
0: "/static/square/top/TOP1icon.png",
|
||||
1: "/static/square/top/TOP2icon.png",
|
||||
2: "/static/square/top/TOP3icon.png",
|
||||
};
|
||||
|
||||
// 监听 dimension 变化,重新加载数据
|
||||
watch(
|
||||
() => props.dimension,
|
||||
() => {
|
||||
loadData();
|
||||
},
|
||||
// 前 3 名对应的边框图
|
||||
const TOP_FRAME_MAP = {
|
||||
0: "/static/square/top/TOP1biankuang1.png",
|
||||
1: "/static/square/top/TOP2biankuang2.png",
|
||||
2: "/static/square/top/TOP3biankuangpng3.png",
|
||||
};
|
||||
|
||||
const activeTab = computed(
|
||||
() => tabs.find((t) => t.key === activeTabKey.value) || tabs[0],
|
||||
);
|
||||
|
||||
// 初始化 activeTabKey:默认选第一个 tab
|
||||
watch(
|
||||
() => tabs,
|
||||
(newTabs) => {
|
||||
if (
|
||||
newTabs.length > 0 &&
|
||||
!newTabs.some((t) => t.key === activeTabKey.value)
|
||||
) {
|
||||
activeTabKey.value = newTabs[0].key;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// 切换 tab
|
||||
const handleTabClick = (e) => {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
if (key && key !== activeTabKey.value) {
|
||||
activeTabKey.value = key;
|
||||
}
|
||||
};
|
||||
|
||||
// activeTab 变化时重新加载数据
|
||||
watch(activeTab, () => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
// 格式化数量
|
||||
const formatCount = (count) => {
|
||||
if (!count) return "0";
|
||||
@ -187,22 +235,31 @@ const onAssetLiked = ({ asset_id, data }) => {
|
||||
|
||||
// 加载数据
|
||||
const loadData = async () => {
|
||||
const tab = activeTab.value;
|
||||
if (!tab || typeof tab.fetch !== "function") {
|
||||
console.warn("[HotCategoryBlock] 当前 tab 未配置 fetch:", tab);
|
||||
items.value = [];
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getHotRankingApi(props.dimension, null, 1, 11);
|
||||
if (res.code === 200 && res.data?.items) {
|
||||
const res = await tab.fetch();
|
||||
if (res && res.code === 200 && res.data?.items) {
|
||||
// 逐个把 cover_url 转换成真实可访问 URL(OSS 预签名前端实现)
|
||||
items.value = await Promise.all(
|
||||
res.data.items.map(async (item) => {
|
||||
return await resolveItemUrls({
|
||||
...item,
|
||||
id: item.id || item.asset_id,
|
||||
})
|
||||
})
|
||||
});
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
items.value = [];
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("[HotCategoryBlock] 加载数据失败", e?.message ?? e);
|
||||
items.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@ -224,9 +281,81 @@ onUnmounted(() => {
|
||||
|
||||
<style scoped>
|
||||
.hot-category-block {
|
||||
padding: 19rpx 9.5rpx;
|
||||
padding: 0 9.5rpx;
|
||||
border-radius: 24rpx;
|
||||
/* opacity: 0.8; */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Tab 栏 */
|
||||
.ranking-tabs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 16rpx;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
opacity: 0.8;
|
||||
border-top-left-radius: 14px;
|
||||
border-top-right-radius: 13px;
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: 7px;
|
||||
z-index: 1; /* 在内容网格之下 */
|
||||
width: 480rpx;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(183.58deg, #ff5a5d -36.55%, #c2ebff 121.2%);
|
||||
backdrop-filter: blur(11.699999809265137px);
|
||||
}
|
||||
|
||||
.ranking-tab-item {
|
||||
height: 80rpx;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
transition: all 0.25s ease;
|
||||
position: absolute;
|
||||
top: 24rpx;
|
||||
}
|
||||
|
||||
.ranking-tab-item.active {
|
||||
background: linear-gradient(
|
||||
185.9deg,
|
||||
rgba(255, 90, 140, 0.98) 5.54%,
|
||||
rgba(194, 235, 255, 0.98) 184.09%
|
||||
);
|
||||
backdrop-filter: blur(1.7999999523162842px);
|
||||
height: 144rpx;
|
||||
width: 88rpx;
|
||||
}
|
||||
|
||||
.ranking-tab-label {
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.4);
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ranking-tab-icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -8rpx;
|
||||
}
|
||||
|
||||
.ranking-tab-item.active .ranking-tab-label {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 骨架屏 */
|
||||
.grid-skeleton {
|
||||
display: flex;
|
||||
flex-direction: column; /* 与 items-grid 保持一致:纵向 */
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
border-radius: 12px;
|
||||
|
||||
background: linear-gradient(
|
||||
161.28deg,
|
||||
rgba(255, 90, 93, 0.2) 16.63%,
|
||||
@ -234,98 +363,39 @@ onUnmounted(() => {
|
||||
rgba(255, 122, 124, 0.2) 83.71%
|
||||
);
|
||||
backdrop-filter: blur(9.300000190734863px);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.block-title {
|
||||
position: absolute;
|
||||
top: -24rpx;
|
||||
left: 4rpx;
|
||||
width: 188rpx;
|
||||
height: 56rpx;
|
||||
border-top-left-radius: 44rpx;
|
||||
border-top-right-radius: 8rpx;
|
||||
border-bottom-right-radius: 44rpx;
|
||||
border-bottom-left-radius: 4rpx;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 0, 4, 0.73) -3.96%,
|
||||
rgba(254, 141, 103, 0.73) 57.95%,
|
||||
rgba(252, 228, 75, 0.73) 97%
|
||||
);
|
||||
box-shadow: 2px 2px 4px 0px #d9262640;
|
||||
backdrop-filter: blur(7.599999904632568px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.recommend-text {
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
text-shadow: 0px 2px 8px #00000074;
|
||||
font-weight: 600;
|
||||
line-height: 100%;
|
||||
letter-spacing: 0%;
|
||||
}
|
||||
|
||||
/* 骨架屏 */
|
||||
.grid-skeleton {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.skeleton-card {
|
||||
width: calc(25% - 12rpx);
|
||||
margin-bottom: 16rpx;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 骨架屏:第一排3个大图 */
|
||||
.skeleton-card:nth-child(-n + 3) {
|
||||
width: calc(33.333% - 12rpx);
|
||||
}
|
||||
|
||||
.skeleton-image {
|
||||
.skeleton-card {
|
||||
width: 100%;
|
||||
height: 192rpx;
|
||||
background: linear-gradient(90deg, #3a3a4a 25%, #4a4a5a 50%, #3a3a4a 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: shimmer 1.5s infinite;
|
||||
}
|
||||
|
||||
/* 骨架屏:第一排图片更高 */
|
||||
.skeleton-card:nth-child(-n + 3) .skeleton-image {
|
||||
height: 236rpx;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 24px;
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: 21px;
|
||||
}
|
||||
|
||||
.skeleton-info {
|
||||
height: 120rpx; /* 与新卡片 row 高度一致 */
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16rpx;
|
||||
padding: 12rpx 16rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.skeleton-avatar {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
border-radius: 50%;
|
||||
background: #3a3a4a;
|
||||
margin-right: 8rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.skeleton-name {
|
||||
width: 100rpx;
|
||||
height: 24rpx;
|
||||
flex: 1;
|
||||
height: 28rpx;
|
||||
background: #3a3a4a;
|
||||
border-radius: 8rpx;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.skeleton-image,
|
||||
.skeleton-info {
|
||||
display: none; /* 新布局下不再使用 */
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
@ -341,222 +411,182 @@ onUnmounted(() => {
|
||||
/* 内容网格 */
|
||||
.items-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
flex-direction: column; /* 改为纵向排列,每张卡片独占一行 */
|
||||
position: relative;
|
||||
z-index: 2; /* 在 tab 栏之上 */
|
||||
/* 背景与父容器一致,看上去与 tab 栏融成一体 */
|
||||
background: linear-gradient(
|
||||
161.28deg,
|
||||
rgba(255, 90, 93, 0.2) 16.63%,
|
||||
rgba(76, 237, 255, 0.2) 48.19%,
|
||||
rgba(255, 122, 124, 0.2) 83.71%
|
||||
);
|
||||
backdrop-filter: blur(9.300000190734863px);
|
||||
border-radius: 12px;
|
||||
padding: 40rpx 20rpx 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.grid-card {
|
||||
width: calc(25% - 12rpx);
|
||||
width: 100%;
|
||||
border-radius: 16rpx;
|
||||
/* overflow: hidden; */
|
||||
position: relative;
|
||||
/* background: rgba(255, 255, 255, 0.15); */
|
||||
/* box-shadow: 2px 2px 4.5px 0px #f04b4b40; */
|
||||
box-shadow: 2px 4px 4px 0px #c92f2f5c;
|
||||
margin-bottom: 48rpx;
|
||||
}
|
||||
|
||||
/* 第一排:3个大图突出显示 */
|
||||
.grid-card:nth-child(-n + 3) {
|
||||
width: calc(33% - 20rpx);
|
||||
}
|
||||
|
||||
.grid-card:nth-child(-n + 3) .card-image {
|
||||
height: 264rpx;
|
||||
box-shadow: 3px 3px 4.5px 2px rgba(0, 0, 0, 0.15);
|
||||
backdrop-filter: blur(0px);
|
||||
/* padding: 8rpx; */
|
||||
/* 单行布局:藏品图片 + 头像 + 点赞信息 + TOP 标签 */
|
||||
.card-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 16rpx;
|
||||
width: 100%;
|
||||
height: 104rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.card-image-wrap {
|
||||
position: relative;
|
||||
width: 90rpx;
|
||||
height: 120rpx;
|
||||
|
||||
}
|
||||
.card-image {
|
||||
width: 100%;
|
||||
height: 224rpx;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -16rpx;
|
||||
left: 32rpx;
|
||||
}
|
||||
|
||||
/* 前三名专属:包裹 card-bottom 的边框 */
|
||||
.card-frame {
|
||||
/* 前 3 名专属:左上角奖牌装饰 */
|
||||
.card-medal {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
top: -32rpx;
|
||||
left: 96rpx;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
z-index: 5;
|
||||
pointer-events: none;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
/* 前 3 名专属:卡片整体突出 */
|
||||
.grid-card-top {
|
||||
}
|
||||
|
||||
.grid-card-top-1 {
|
||||
}
|
||||
|
||||
.grid-card-top-2 {
|
||||
}
|
||||
|
||||
.grid-card-top-3 {
|
||||
}
|
||||
|
||||
/* 前 3 名专属:包裹藏品图的边框图(叠加在 card-image 之上) */
|
||||
.frame-image {
|
||||
position: absolute;
|
||||
top: -16rpx;
|
||||
left: 32rpx;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 4;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.frame-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.corner-decoration {
|
||||
position: absolute;
|
||||
top: -16rpx;
|
||||
right: -16rpx;
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
z-index: 6;
|
||||
pointer-events: none;
|
||||
transform: rotate(60deg);
|
||||
}
|
||||
|
||||
.corner-decoration.top-corner-decoration {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
left: -16rpx;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.corner-decoration image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 底部信息模块 - 独立模块,有背景色和圆角 */
|
||||
.card-bottom {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
box-shadow: 2px 2px 4.5px 0px #F04B4B40;
|
||||
}
|
||||
|
||||
.card-bottom-1,
|
||||
.card-bottom-2,
|
||||
.card-bottom-3 {
|
||||
border-radius: 28rpx;
|
||||
}
|
||||
|
||||
/* Top 排名标签 */
|
||||
.top-badge {
|
||||
width: 80rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 16rpx;
|
||||
margin: 16rpx auto;
|
||||
background: linear-gradient(
|
||||
93.1deg,
|
||||
rgba(224, 180, 247, 0.71) -12.06%,
|
||||
rgba(178, 246, 204, 0.71) 52.09%,
|
||||
rgba(98, 178, 244, 0.71) 163.5%
|
||||
);
|
||||
backdrop-filter: blur(11.699999809265137px);
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
|
||||
.top-badge-1,
|
||||
.top-badge-2,
|
||||
.top-badge-3 {
|
||||
padding-left: 24rpx;
|
||||
}
|
||||
|
||||
.badge-rank {
|
||||
width: 80rpx;
|
||||
height: 32rpx;
|
||||
color: #fffabd;
|
||||
font-size: 18rpx;
|
||||
font-weight: 600;
|
||||
border-radius: 16rpx;
|
||||
.like-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column; /* 从上往下:用户名 → 编号 → 点赞 */
|
||||
justify-content: center;
|
||||
text-shadow: -1px 1px 4px #ce0909d6;
|
||||
}
|
||||
|
||||
/* 用户信息 */
|
||||
.card-info {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8rpx;
|
||||
border-top-left-radius: 7px;
|
||||
border-top-right-radius: 7px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background: linear-gradient(
|
||||
177.83deg,
|
||||
rgba(235, 228, 219, 0) 11.38%,
|
||||
rgba(138, 135, 131, 0.4) 23.67%,
|
||||
rgba(255, 231, 231, 0.6) 43.04%,
|
||||
rgba(255, 255, 255, 0.9) 67.52%,
|
||||
#ffffff 98.2%
|
||||
);
|
||||
backdrop-filter: blur(0px);
|
||||
}
|
||||
|
||||
.card-info-1,
|
||||
.card-info-2,
|
||||
.card-info-3 {
|
||||
padding-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 8rpx;
|
||||
margin-left: 64rpx; /* 距头像 10rpx */
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 18rpx;
|
||||
font-weight: 400;
|
||||
color: #554545;
|
||||
max-width: 120rpx;
|
||||
overflow: hidden;
|
||||
font-size: 20rpx;
|
||||
font-weight: 800;
|
||||
color: #fffabd;
|
||||
text-shadow: -1px 1px 4px #ce0909d6;
|
||||
white-space: nowrap;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.user-number {
|
||||
font-size: 20rpx;
|
||||
font-weight: 800;
|
||||
line-height: 1.3;
|
||||
color: #fffabd;
|
||||
text-shadow: -1px 1px 4px #ce0909d6;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.like-badge {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 80rpx;
|
||||
height: 64rpx;
|
||||
opacity: 1;
|
||||
border-top-left-radius: 7px;
|
||||
border-bottom-right-radius: 21.5px;
|
||||
background: linear-gradient(
|
||||
177.83deg,
|
||||
rgba(83, 244, 211, 0.2) 2.52%,
|
||||
rgba(15, 9, 0, 0) 69.07%
|
||||
);
|
||||
|
||||
backdrop-filter: blur(0px);
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.like-badge-1,
|
||||
.like-badge-2,
|
||||
.like-badge-3 {
|
||||
padding: 10rpx 0 0 10rpx;
|
||||
}
|
||||
|
||||
.like-icon-wrapper {
|
||||
.like-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8rpx;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.like-badge .like-icon {
|
||||
.like-info .like-icon {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
margin-right: 6rpx;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.like-badge .like-count {
|
||||
font-size: 20rpx;
|
||||
.like-info .like-count {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
line-height: 100%;
|
||||
letter-spacing: 0%;
|
||||
color: #fffabd;
|
||||
text-shadow:
|
||||
-1px 1px 4px #ce0909d6,
|
||||
0px 0px 10px #fffabd;
|
||||
text-shadow: -1px 1px 4px #ce0909d6;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Top 排名标签(顶到右边) */
|
||||
.top-badge {
|
||||
margin-left: auto; /* 推到右侧 */
|
||||
min-width: 100rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 18rpx;
|
||||
padding: 0 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.badge-rank {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.badge-rank-icon {
|
||||
width: 90.24rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.badge-rank-number {
|
||||
font-size: 84rpx;
|
||||
font-weight: 600;
|
||||
/* 渐变填充到文字 */
|
||||
background: linear-gradient(
|
||||
181.98deg,
|
||||
#fcfcf8 23.78%,
|
||||
#ffb3eb 66.88%,
|
||||
#ffeded 94.93%
|
||||
);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
color: transparent;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 点赞动效波纹 */
|
||||
|
||||
@ -38,27 +38,29 @@
|
||||
<ContentTabs ref="contentTabsRef" class="tabs" :modelValue="activeContentTab"
|
||||
@update:modelValue="activeContentTab = $event" />
|
||||
|
||||
<!-- 在线榜单区块 -->
|
||||
<view ref="hotCategoryRef" class="hot-category-wrapper">
|
||||
<HotCategoryBlock :dimension="activeContentTab" @cardClick="handleCardClick" />
|
||||
<view class="hot-more-btn" @click="goToHotCategoryMore">
|
||||
<text class="hot-more-text">查看更多</text>
|
||||
</view>
|
||||
<!-- 在线榜单区块 - 仅在 星榜 时显示 -->
|
||||
<view
|
||||
v-if="activeContentTab === 'xingbang'"
|
||||
ref="hotCategoryRef"
|
||||
class="hot-category-wrapper"
|
||||
>
|
||||
<HotCategoryBlock @cardClick="handleCardClick" />
|
||||
</view>
|
||||
|
||||
<!-- 区域二/三/四已合并到 CreationGrid 组件(主Tab + 分类标签 + 网格列表) -->
|
||||
<CreationGrid :screenWidth="screenWidth" :screenHeight="screenHeight" :bannerBottom="bannerBottomPx"
|
||||
:category="activeCategoryTab" :isActive="isActive"
|
||||
@cardClick="handleCardClick" @loaded="onGridLoaded"
|
||||
@mainTabClick="handleMainTabClick" @categoryChange="handleCategoryChange"
|
||||
ref="creationGridRef" />
|
||||
<!-- CreationGrid 组件(主Tab + 分类标签 + 网格列表) - 仅在 广场 时显示 -->
|
||||
<CreationGrid
|
||||
v-if="activeContentTab === 'guangchang'"
|
||||
@cardClick="handleCardClick"
|
||||
@loaded="onGridLoaded"
|
||||
ref="creationGridRef"
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from "vue";
|
||||
import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
|
||||
import { ref, watch, onMounted, onUnmounted, nextTick } from "vue";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import { useStore } from "vuex";
|
||||
import Header from "../components/Header.vue";
|
||||
import BottomNav from "../components/BottomNav.vue";
|
||||
@ -79,11 +81,9 @@ const store = useStore();
|
||||
const currentStarId = ref(uni.getStorageSync("star_id") || null);
|
||||
|
||||
// ========== UI State ==========
|
||||
const activeContentTab = ref("displaying");
|
||||
const activeCategoryTab = ref("hot");
|
||||
const activeContentTab = ref("xinghe");
|
||||
const navExpanded = ref(false);
|
||||
const showRankingModal = ref(false);
|
||||
const isActive = ref(true);
|
||||
const creationGridRef = ref(null);
|
||||
const cardTapTimers = {};
|
||||
const likingMap = ref({});
|
||||
@ -99,7 +99,7 @@ const allSpotlightRefs = () => {
|
||||
const sections = [
|
||||
bannerSectionRef.value,
|
||||
contentTabsRef.value?.$el || contentTabsRef.value,
|
||||
hotCategoryRef.value,
|
||||
// hotCategoryRef.value,
|
||||
creationGridRef.value?.mainTabsRef?.value,
|
||||
creationGridRef.value?.categoryRef?.value,
|
||||
].filter(Boolean)
|
||||
@ -129,13 +129,10 @@ const onTouchMove = () => {
|
||||
update()
|
||||
}
|
||||
|
||||
// tab / 分类变化 → 新内容可能进入视口,重做一次 spotlight 计算
|
||||
// tab 变化 → 新内容可能进入视口,重做一次 spotlight 计算
|
||||
watch(activeContentTab, () => {
|
||||
nextTick(() => setTimeout(update, 50));
|
||||
});
|
||||
watch(activeCategoryTab, () => {
|
||||
nextTick(() => setTimeout(update, 50));
|
||||
});
|
||||
|
||||
// 卡片加载到位 → 重新算一次 spotlight(卡片异步加载,不在初始 200ms update 范围内)
|
||||
const onGridLoaded = (count) => {
|
||||
@ -144,18 +141,9 @@ const onGridLoaded = (count) => {
|
||||
}
|
||||
};
|
||||
|
||||
// ========== Screen Info ==========
|
||||
const screenWidth = ref(375);
|
||||
const screenHeight = ref(812);
|
||||
|
||||
// ========== Composables ==========
|
||||
const { bannerActivities, banners, loadBannerActivities, loadBanners } = useBanner();
|
||||
|
||||
// banner(216+360rpx) + tab栏(16+80rpx) + 间距(8rpx) ≈ 680rpx
|
||||
const bannerBottomPx = computed(() =>
|
||||
Math.round((screenWidth.value / 750) * 715),
|
||||
);
|
||||
|
||||
// ========== Handlers ==========
|
||||
|
||||
const handleCardClick = (card) => {
|
||||
@ -233,18 +221,6 @@ const handleRankingModalClose = (visible) => {
|
||||
}
|
||||
};
|
||||
|
||||
const goToHotCategoryMore = () => {
|
||||
const title =
|
||||
activeContentTab.value === "displaying"
|
||||
? "日榜"
|
||||
: activeContentTab.value === "week"
|
||||
? "周榜"
|
||||
: "月榜";
|
||||
uni.navigateTo({
|
||||
url: `/pages/square/hot-category-more?dimension=${activeContentTab.value}&title=${encodeURIComponent(title)}`,
|
||||
});
|
||||
};
|
||||
|
||||
const handleTabChange = (newTab) => {
|
||||
// if (newTab === 4) {
|
||||
// navExpanded.value = false;
|
||||
@ -266,18 +242,7 @@ const handleTabChange = (newTab) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 主Tab点击 - 进入铸造页面
|
||||
const handleMainTabClick = (tab) => {
|
||||
// 跳转到 mall 页(mall 内嵌 craft-select 组件,带菜单),并带上 type
|
||||
uni.navigateTo({
|
||||
url: `/pages/castlove/mall?type=${encodeURIComponent(tab.type)}`,
|
||||
});
|
||||
};
|
||||
|
||||
const handleCategoryChange = (value) => {
|
||||
if (activeCategoryTab.value === value) return;
|
||||
activeCategoryTab.value = value;
|
||||
};
|
||||
// 主Tab点击 / 分类切换 已在 CreationGrid 内部处理,不再冒泡
|
||||
|
||||
// ========== Tile Change Callback ==========
|
||||
const handleTileChange = () => { };
|
||||
@ -287,10 +252,6 @@ const resetSquare = async () => { };
|
||||
|
||||
// ========== Lifecycle ==========
|
||||
onMounted(() => {
|
||||
const info = uni.getSystemInfoSync();
|
||||
screenWidth.value = info.windowWidth;
|
||||
screenHeight.value = info.windowHeight;
|
||||
|
||||
resetSquare();
|
||||
loadBannerActivities();
|
||||
loadBanners();
|
||||
@ -303,18 +264,12 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
isActive.value = true;
|
||||
activeContentTab.value = "displaying";
|
||||
activeCategoryTab.value = "hot";
|
||||
activeContentTab.value = "xinghe";
|
||||
nextTick(() => {
|
||||
setTimeout(update, 80);
|
||||
});
|
||||
});
|
||||
|
||||
onHide(() => {
|
||||
isActive.value = false;
|
||||
});
|
||||
|
||||
// onLoad((options) => {
|
||||
// if (options && 'guide_debug' in options) {
|
||||
// const debugValue = options.guide_debug
|
||||
@ -414,31 +369,6 @@ onUnmounted(() => {
|
||||
padding-bottom: 80rpx;
|
||||
}
|
||||
|
||||
.hot-more-btn {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 16rpx;
|
||||
z-index: 10;
|
||||
width: 104rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 20rpx;
|
||||
opacity: 0.66;
|
||||
padding: 8rpx 20rpx;
|
||||
background: linear-gradient(90deg,
|
||||
rgba(255, 222, 8, 0.61) -17.54%,
|
||||
rgba(255, 0, 25, 0.61) 116.67%);
|
||||
box-shadow: 2px 2px 4px 0px #f2151578;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hot-more-text {
|
||||
font-size: 22rpx;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 蒙层 */
|
||||
.nav-mask {
|
||||
position: fixed;
|
||||
|
||||
BIN
frontend/static/square/top/top.png
Normal file
BIN
frontend/static/square/top/top.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 441 KiB |
Loading…
Reference in New Issue
Block a user