feat: 修改suqre里面组件的样式和修改bug

This commit is contained in:
zheng020 2026-05-29 16:17:08 +08:00
parent 20dfbcac6b
commit 3a292e84a2
6 changed files with 202 additions and 101 deletions

View File

@ -11,10 +11,10 @@
{ {
"path": "pages/square/hot-category-more", "path": "pages/square/hot-category-more",
"style": { "style": {
"navigationBarTitleText": "热门推荐", "navigationStyle": "custom",
"navigationBarBackgroundColor": "#1a1a2e", "app-plus": {
"navigationBarTextStyle": "white", "bounce": "none"
"backgroundColor": "#1a1a2e" }
} }
}, },
{ {

View File

@ -69,10 +69,10 @@ const onTop3DataLoaded = (items) => {
.banner-activity-img { .banner-activity-img {
width: 100%; width: 100%;
height: 296rpx; height: 312rpx;
display: block; display: block;
border-radius:24rpx; border-radius:24rpx;
position: relative; position: relative;
top: 32rpx; top: 24rpx;
} }
</style> </style>

View File

@ -1,48 +1,39 @@
<template> <template>
<view class="content-tabs" @click.stop> <view class="content-tabs">
<view <view v-for="(tab, index) in tabs" :key="tab.key" class="tab-item" :class="{ active: modelValue === tab.key }"
v-for="(tab, index) in tabs" :data-key="tab.key" @click="handleTabClick">
:key="tab.key"
class="tab-item"
:class="{ active: modelValue === tab.key }"
@click="$emit('update:modelValue', tab.key)"
>
<!-- 背景图片 --> <!-- 背景图片 -->
<image <!-- <image class="tab-bg" :class="{ 'tab-bg-inactive': modelValue !== tab.key }"
class="tab-bg" src="/static/nft/dingbutubiao_liang.png" mode="scaleToFill" /> -->
: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 }"> <view class="tab-left" :class="{ 'tab-left-first': index === 2 }">
<text v-if="tab.emoji" class="tab-emoji">{{ tab.emoji }}</text> <text v-if="tab.emoji" class="tab-emoji">{{ tab.emoji }}</text>
<image <image v-else class="tab-icon" :src="tab.icon" mode="aspectFill"
v-else :style="{ width: tab.iconWidth + 'rpx', height: tab.iconHeight + 'rpx' }" />
class="tab-icon" <!-- 右侧文字 -->
:src="tab.icon" <text class="tab-label">{{ tab.label }}</text>
mode="aspectFill"
:style="{ width: tab.iconWidth + 'rpx', height: tab.iconHeight + 'rpx' }"
/>
</view> </view>
<!-- 右侧文字 -->
<text class="tab-label">{{ tab.label }}</text>
</view> </view>
</view> </view>
</template> </template>
<script setup> <script setup>
defineProps({ const props = defineProps({
modelValue: { type: String, default: 'hot' } modelValue: { type: String, default: 'hot' }
}) })
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 tabs = [ const tabs = [
{ key: 'hot', label: '人气王者', emoji: null, icon: '/static/square/3.png',iconWidth: 80, iconHeight: 80 }, { key: 'displaying', label: '日榜', emoji: null, icon: '/static/square/1.png', iconWidth: 32, iconHeight: 40 },
{ key: 'new', label: '新鲜上架', emoji: null, icon: '/static/square/2.png',iconWidth: 80, iconHeight: 80 }, { key: 'week', label: '周榜', emoji: null, icon: '/static/square/1.png', iconWidth: 32, iconHeight: 40 },
{ key: 'potential', label: '潜力之星', emoji: null, icon: '/static/square/1.png',iconWidth: 96, iconHeight: 96 }, { key: 'month', label: '月榜', emoji: null, icon: '/static/square/1.png', iconWidth: 32, iconHeight: 40 },
{ key: 'myworks', label: '我的', emoji: null, icon: '/static/square/4.png',iconWidth: 80, iconHeight: 80 },
] ]
</script> </script>
@ -66,16 +57,18 @@ const tabs = [
} }
.tab-item { .tab-item {
flex: 1; max-width: 134rpx;
max-width: 160rpx; height: 42rpx;
border-radius: 21rpx;
opacity: 0.78;
position: relative; position: relative;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 56rpx;
margin: 0 4rpx;
overflow: visible; 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 */ /* 背景图片铺满整个 tab */
@ -96,25 +89,13 @@ const tabs = [
/* 左侧图标,绝对浮动覆盖背景左侧色块,不影响文字布局 */ /* 左侧图标,绝对浮动覆盖背景左侧色块,不影响文字布局 */
.tab-left { .tab-left {
position: absolute;
z-index: 1;
left: -16rpx;
top: 35%;
transform: translateY(-50%) rotateZ(8deg);
width: 80rpx;
height: 80rpx;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 4rpx 30rpx 4rpx 20rpx ;
transition: transform 0.25s ease; transition: transform 0.25s ease;
} }
/* 第一个 tab 图标单独样式 */
.tab-left-first {
top: 23%;
}
/* 选中时图标上移 */ /* 选中时图标上移 */
/* .tab-item.active .tab-left { /* .tab-item.active .tab-left {
transform: translateY(-70%); transform: translateY(-70%);
@ -133,13 +114,13 @@ const tabs = [
.tab-label { .tab-label {
position: relative; position: relative;
z-index: 1; z-index: 1;
font-size: 18rpx; font-size: 24rpx;
font-weight: 600; font-weight: 600;
color: rgba(255, 255, 255, 0.95); color: rgba(255, 255, 255, 0.95);
/* color: #ffffff; */ /* color: #ffffff; */
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.8); text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.8);
white-space: nowrap; white-space: nowrap;
margin-left: 48rpx; margin-left: 14rpx;
} }
.tab-item.active .tab-label { .tab-item.active .tab-label {

View File

@ -1,9 +1,9 @@
<template> <template>
<view class="hot-category-block"> <view class="hot-category-block">
<!-- 标题 --> <!-- 标题 -->
<view class="block-title"> <!-- <view class="block-title">
<text class="recommend-text"> {{ title }}</text> <text class="recommend-text"> {{ title }}</text>
</view> </view> -->
<!-- 骨架屏 --> <!-- 骨架屏 -->
<view v-if="loading" class="grid-skeleton"> <view v-if="loading" class="grid-skeleton">
@ -54,13 +54,17 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, onUnmounted } from 'vue' import { ref, watch, onMounted, onUnmounted } from 'vue'
import { getHotRankingApi } from '@/utils/api.js' import { getHotRankingApi } from '@/utils/api.js'
const props = defineProps({ const props = defineProps({
title: { title: {
type: String, type: String,
default: '在线榜单' default: '在线榜单'
},
dimension: {
type: String,
default: 'displaying'
} }
}) })
@ -70,6 +74,11 @@ const items = ref([])
const loading = ref(false) const loading = ref(false)
const likingMap = ref({}) const likingMap = ref({})
// dimension
watch(() => props.dimension, () => {
loadData()
})
// //
const formatCount = (count) => { const formatCount = (count) => {
if (!count) return '0' if (!count) return '0'
@ -105,7 +114,7 @@ const onAssetLiked = ({ asset_id, data }) => {
const loadData = async () => { const loadData = async () => {
loading.value = true loading.value = true
try { try {
const res = await getHotRankingApi('displaying', null, 1, 12) const res = await getHotRankingApi(props.dimension, null, 1, 12)
if (res.code === 200 && res.data?.items) { if (res.code === 200 && res.data?.items) {
items.value = res.data.items.map(item => ({ items.value = res.data.items.map(item => ({
...item, ...item,
@ -131,12 +140,8 @@ onUnmounted(() => {
<style scoped> <style scoped>
.hot-category-block { .hot-category-block {
padding: 0 24rpx; padding: 14rpx 14rpx 0;
padding-top: 52rpx; border-radius: 24rpx;
border-top-left-radius: 72rpx;
border-top-right-radius: 24rpx;
border-bottom-right-radius: 24rpx;
border-bottom-left-radius: 24rpx;
opacity: 0.8; opacity: 0.8;
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%); 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); backdrop-filter: blur(9.300000190734863px);
@ -235,7 +240,6 @@ onUnmounted(() => {
.grid-card { .grid-card {
width: calc(25% - 12rpx); width: calc(25% - 12rpx);
margin-bottom: 16rpx;
border-radius: 16rpx; border-radius: 16rpx;
overflow: hidden; overflow: hidden;
position: relative; position: relative;

View File

@ -40,6 +40,17 @@
@tap="handleCardClick(item)" @tap="handleCardClick(item)"
> >
<image class="creation-image" :src="item.cover_image" mode="aspectFill"></image> <image class="creation-image" :src="item.cover_image" mode="aspectFill"></image>
<!-- 点赞角标 -->
<view class="like-badge">
<view class="like-icon-wrapper">
<image
class="like-icon"
:src="item.is_liked ? '/static/icon/heart-icon.png' : '/static/icon/heart-icon-false.png'"
mode="aspectFit"
></image>
<text class="like-count">{{ formatCount(item.like_count) }}</text>
</view>
</view>
<!-- 光波动画层 - 外层 --> <!-- 光波动画层 - 外层 -->
<view <view
class="wf-like-wave wf-like-wave-outer" class="wf-like-wave wf-like-wave-outer"
@ -59,10 +70,6 @@
<image class="creator-avatar" :src="item.creator_avatar" mode="aspectFill"></image> <image class="creator-avatar" :src="item.creator_avatar" mode="aspectFill"></image>
<text class="creator-name">{{ item.creator_name }}</text> <text class="creator-name">{{ item.creator_name }}</text>
</view> </view>
<view class="like-info">
<image class="like-icon" src="/static/icon/heart-icon.png" mode="aspectFit"></image>
<text class="like-count">{{ formatCount(item.like_count) }}</text>
</view>
</view> </view>
</view> </view>
</view> </view>
@ -92,18 +99,20 @@
<script setup> <script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue' import { ref, computed, onMounted, onUnmounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app' import { onLoad } from '@dcloudio/uni-app'
import { getHotRankingApi } from '@/utils/api.js'
import { doubleTapLike } from '@/utils/likeHelper.js'
// ========== ========== // ========== ==========
const type = ref('') const dimension = ref('displaying')
const pageTitle = ref('') const pageTitle = ref('')
// ========== ========== // ========== ==========
const items = ref([]) const items = ref([])
const cursor = ref('')
const loading = ref(false) const loading = ref(false)
const noMore = ref(false) const noMore = ref(false)
const activeSubTab = ref('all') const activeSubTab = ref('all')
const likingMap = ref({}) const likingMap = ref({})
const cardTapTimers = {}
// ========== ========== // ========== ==========
const starCardSubTabs = [ const starCardSubTabs = [
@ -114,8 +123,11 @@ const starCardSubTabs = [
{ label: '拍立得', value: 'polaroid' } { label: '拍立得', value: 'polaroid' }
] ]
// ========== ==========
const emit = defineEmits(['cardClick', 'scroll'])
// ========== ========== // ========== ==========
const isStarCard = computed(() => type.value === 'hot_star_card') const isStarCard = computed(() => false)
// ========== ========== // ========== ==========
const formatCount = (count) => { const formatCount = (count) => {
@ -127,8 +139,8 @@ const formatCount = (count) => {
// ========== ========== // ========== ==========
onLoad((options) => { onLoad((options) => {
if (options.type) { if (options.dimension) {
type.value = options.type dimension.value = options.dimension
} }
if (options.title) { if (options.title) {
pageTitle.value = decodeURIComponent(options.title) pageTitle.value = decodeURIComponent(options.title)
@ -139,7 +151,6 @@ onLoad((options) => {
const loadData = async (reset = false) => { const loadData = async (reset = false) => {
if (loading.value) return if (loading.value) return
if (reset) { if (reset) {
cursor.value = ''
noMore.value = false noMore.value = false
items.value = [] items.value = []
} }
@ -147,24 +158,25 @@ const loadData = async (reset = false) => {
loading.value = true loading.value = true
try { try {
const res = await Promise.resolve({ code: 200, data: { items: [] } }) const res = await getHotRankingApi(dimension.value, null, 1, 20)
if (res.code === 200 && res.data?.items && res.data.items.length > 0) { if (res.code === 200 && res.data?.items) {
const newItems = res.data.items.map((item) => { const newItems = res.data.items.map((item) => {
return { return {
...item,
id: item.asset_id, id: item.asset_id,
certificate_id: item.asset_id, certificate_id: item.asset_id,
cover_image: item.cover_url || '', cover_image: item.cover_url || '',
creator_avatar: item.owner_avatar || '', creator_avatar: item.owner_avatar || '',
creator_name: item.owner_nickname || item.name || '', creator_name: item.owner_nickname || item.name || '',
like_count: item.likes || item.like_count || 0, like_count: item.like_count || 0,
is_liked: item.is_liked || false,
} }
}) })
cursor.value = res.data.cursor || ''
items.value = reset ? newItems : [...items.value, ...newItems] items.value = reset ? newItems : [...items.value, ...newItems]
if (!cursor.value) { if (res.data.items.length < 20) {
noMore.value = true noMore.value = true
} }
} else { } else {
@ -190,10 +202,32 @@ const handleLoadMore = () => {
} }
} }
const handleCardClick = (item) => { const handleCardClick = (card) => {
uni.navigateTo({ if (cardTapTimers[card.id]) {
url: `/pages/asset-detail/asset-detail?asset_id=${item.id}` //
}) clearTimeout(cardTapTimers[card.id]);
delete cardTapTimers[card.id];
//
likingMap.value = { ...likingMap.value, [card.id]: true };
setTimeout(() => {
likingMap.value = { ...likingMap.value, [card.id]: false };
}, 600);
doubleTapLike(card.id, card.exhibition_id || 0, (success) => {
if (success) {
uni.showToast({ title: '点赞成功', icon: 'success' })
}
});
} else {
//
if (card.id) {
cardTapTimers[card.id] = setTimeout(() => {
delete cardTapTimers[card.id];
uni.navigateTo({ url: `/pages/asset-detail/asset-detail?asset_id=${card.id}` });
}, 300);
}
}
} }
const goBack = () => { const goBack = () => {
@ -218,9 +252,14 @@ onMounted(() => {
setTimeout(() => { setTimeout(() => {
likingMap.value = { ...likingMap.value, [asset_id]: false } likingMap.value = { ...likingMap.value, [asset_id]: false }
}, 600) }, 600)
const idx = items.value.findIndex(c => c.id === asset_id) const idx = items.value.findIndex(c => (c.asset_id || c.id) === asset_id)
if (idx !== -1 && data && typeof data.new_like_count === 'number') { if (idx !== -1) {
items.value[idx].like_count = data.new_like_count if (data && typeof data.new_like_count === 'number') {
items.value[idx].like_count = data.new_like_count
}
if (data && typeof data.is_liked === 'boolean') {
items.value[idx].is_liked = data.is_liked
}
items.value = [...items.value] items.value = [...items.value]
} }
}) })
@ -347,6 +386,46 @@ onUnmounted(() => {
height: 400rpx; height: 400rpx;
} }
/* 点赞角标 */
.like-badge {
position: absolute;
top: 0;
left: 0;
width: 122rpx;
height: 140rpx;
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-icon-wrapper {
display: flex;
align-items: center;
padding: 8rpx;
}
.like-badge .like-icon {
width: 38rpx;
height: 38rpx;
margin-right: 6rpx;
}
.like-badge .like-count {
font-size: 32rpx;
font-weight: 400;
line-height: 100%;
letter-spacing: 0%;
color: #fffabd;
text-shadow:
-1px 1px 4px #ce0909d6,
0px 0px 10px #fffabd;
}
/* 光波动画 */ /* 光波动画 */
.wf-like-wave { .wf-like-wave {
position: absolute; position: absolute;

View File

@ -29,9 +29,14 @@
@top3Click="showRankingModal = true" /> @top3Click="showRankingModal = true" />
</view> </view>
<ContentTabs class="tabs" :modelValue="activeContentTab" @update:modelValue="activeContentTab = $event" />
<!-- 在线榜单区块 --> <!-- 在线榜单区块 -->
<view class="hot-category-wrapper"> <view class="hot-category-wrapper">
<HotCategoryBlock :title="'日榜'" @cardClick="handleCardClick" /> <HotCategoryBlock :dimension="activeContentTab" @cardClick="handleCardClick" />
<view class="hot-more-btn" @click="goToHotCategoryMore">
<text class="hot-more-text">查看更多</text>
</view>
</view> </view>
<!-- 区域二主Tab标签区星卡/吧唧/海报 --> <!-- 区域二主Tab标签区星卡/吧唧/海报 -->
@ -48,7 +53,7 @@
<view id="category-section" class="category-section" :class="{ fixed: isFixed }"> <view id="category-section" class="category-section" :class="{ fixed: isFixed }">
<scroll-view class="category-scroll" scroll-x :show-scrollbar="false"> <scroll-view class="category-scroll" scroll-x :show-scrollbar="false">
<view v-for="(category, index) in categories" :key="index" class="category-item" <view v-for="(category, index) in categories" :key="index" class="category-item"
:class="{ active: activeContentTab === category.value }" @click="handleCategoryChange(category.value)"> :class="{ active: activeCategoryTab === category.value }" @click="handleCategoryChange(category.value)">
<text class="category-text">{{ category.label }}</text> <text class="category-text">{{ category.label }}</text>
</view> </view>
</scroll-view> </scroll-view>
@ -56,7 +61,7 @@
<!-- 区域四创作网格列表 --> <!-- 区域四创作网格列表 -->
<CreationGrid :screenWidth="screenWidth" :screenHeight="screenHeight" :bannerBottom="bannerBottomPx" <CreationGrid :screenWidth="screenWidth" :screenHeight="screenHeight" :bannerBottom="bannerBottomPx"
:category="activeContentTab" :isActive="isActive" @cardClick="handleCardClick" ref="creationGridRef" /> :category="activeCategoryTab" :isActive="isActive" @cardClick="handleCardClick" ref="creationGridRef" />
</scroll-view> </scroll-view>
</view> </view>
</template> </template>
@ -70,6 +75,7 @@ import BottomNav from '../components/BottomNav.vue'
import GuideOverlay from '@/components/GuideOverlay.vue' import GuideOverlay from '@/components/GuideOverlay.vue'
import RankingModal from '../components/RankingModal.vue' import RankingModal from '../components/RankingModal.vue'
import BannerCarousel from './components/BannerCarousel.vue' import BannerCarousel from './components/BannerCarousel.vue'
import ContentTabs from './components/ContentTabs.vue'
import HotCategoryBlock from './components/HotCategoryBlock.vue' import HotCategoryBlock from './components/HotCategoryBlock.vue'
import CreationGrid from './components/CreationGrid.vue' import CreationGrid from './components/CreationGrid.vue'
// import { clearSubStepProgress, shouldShowGuideStartModal } from '@/utils/guideConfig.js' // import { clearSubStepProgress, shouldShowGuideStartModal } from '@/utils/guideConfig.js'
@ -82,7 +88,8 @@ const store = useStore()
const currentStarId = ref(uni.getStorageSync('star_id') || null) const currentStarId = ref(uni.getStorageSync('star_id') || null)
// ========== UI State ========== // ========== UI State ==========
const activeContentTab = ref('hot') const activeContentTab = ref('displaying')
const activeCategoryTab = ref('hot')
const navExpanded = ref(false) const navExpanded = ref(false)
const showRankingModal = ref(false) const showRankingModal = ref(false)
const isActive = ref(true) const isActive = ref(true)
@ -129,12 +136,12 @@ const categories = ref([
]) ])
// ========== Watch activeContentTab ========== // ========== Watch activeContentTab ==========
watch(activeContentTab, (newTab) => { // watch(activeContentTab, (newTab) => {
if (newTab === 'myworks') { // if (newTab === 'myworks') {
uni.navigateTo({ url: '/pages/profile/myWorks' }) // uni.navigateTo({ url: '/pages/profile/myWorks' })
return // return
} // }
}) // })
// ========== Screen Info ========== // ========== Screen Info ==========
const screenWidth = ref(375) const screenWidth = ref(375)
@ -206,6 +213,13 @@ 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) => { const handleTabChange = (newTab) => {
if (newTab === 4) { if (newTab === 4) {
navExpanded.value = false navExpanded.value = false
@ -228,8 +242,8 @@ const handleTabChange = (newTab) => {
} }
const handleCategoryChange = (value) => { const handleCategoryChange = (value) => {
if (activeContentTab.value === value) return if (activeCategoryTab.value === value) return
activeContentTab.value = value activeCategoryTab.value = value
} }
// ========== Tile Change Callback ========== // ========== Tile Change Callback ==========
@ -250,7 +264,8 @@ onMounted(() => {
onShow(() => { onShow(() => {
isActive.value = true isActive.value = true
activeContentTab.value = 'hot' activeContentTab.value = 'displaying'
activeCategoryTab.value = 'hot'
}) })
onHide(() => { onHide(() => {
@ -327,6 +342,7 @@ onUnmounted(() => {
/* 区域一:轮播图 */ /* 区域一:轮播图 */
.banner-section { .banner-section {
width: 100%; width: 100%;
height: 360rpx;
/* margin-bottom: 32rpx; */ /* margin-bottom: 32rpx; */
} }
@ -416,7 +432,28 @@ onUnmounted(() => {
/* 热门分类区块 */ /* 热门分类区块 */
.hot-category-wrapper { .hot-category-wrapper {
margin-bottom: 16rpx; position: relative;
padding-bottom: 64rpx;
}
.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;
}
.hot-more-text {
font-size: 22rpx;
color: #fff;
font-weight: 600;
} }
/* 蒙层 */ /* 蒙层 */