style: 样式修改

This commit is contained in:
zerosaturation 2026-05-11 17:56:16 +08:00
parent e05769e79e
commit 8652f1af2e
7 changed files with 304 additions and 221 deletions

View File

@ -71,7 +71,8 @@
"secretkey" : "1i5Aj8FwL3i11LYPeXMRwRWycictWq2X"
}
},
"push" : {}
"push" : {},
"statics" : {}
},
"icons" : {
"android" : {
@ -105,6 +106,9 @@
}
}
}
},
"uniStatistics" : {
"enable" : true
}
},
/* */
@ -115,19 +119,82 @@
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"usingComponents" : true,
"uniStatistics" : {
"enable" : false
}
},
"vueVersion" : "3"
"mp-alipay" : {
"usingComponents" : true,
"uniStatistics" : {
"enable" : false
}
},
"mp-baidu" : {
"usingComponents" : true,
"uniStatistics" : {
"enable" : false
}
},
"mp-toutiao" : {
"usingComponents" : true,
"uniStatistics" : {
"enable" : false
}
},
"uniStatistics" : {
"enable" : false,
"version" : "2"
},
"vueVersion" : "3",
"app-harmony" : {
"uniStatistics" : {
"enable" : false
}
},
"h5" : {
"uniStatistics" : {
"enable" : true
}
},
"mp-harmony" : {
"uniStatistics" : {
"enable" : false
}
},
"mp-jd" : {
"uniStatistics" : {
"enable" : false
}
},
"mp-kuaishou" : {
"uniStatistics" : {
"enable" : false
}
},
"mp-lark" : {
"uniStatistics" : {
"enable" : false
}
},
"mp-qq" : {
"uniStatistics" : {
"enable" : false
}
},
"mp-xhs" : {
"uniStatistics" : {
"enable" : false
}
},
"quickapp-webview-huawei" : {
"uniStatistics" : {
"enable" : false
}
},
"quickapp-webview-union" : {
"uniStatistics" : {
"enable" : false
}
}
}

View File

@ -2,14 +2,51 @@
<!-- 只渲染背景图卡片由父组件 BannerCarousel 渲染在 swiper 外层 -->
<view class="banner-top3-bg" >
<image class="banner-bg" src="/static/square/paihangbang.png" mode="aspectFill" />
<!-- 卡片层 -->
<view class="cards-overlay" @click.stop="$emit('top3Click')">
<view
v-for="(item, index) in top3Items"
:key="item.asset_id || index"
class="card-wrapper"
:class="`card-pos-${index}`"
>
<view class="card-frame">
<view class="card-image-wrap">
<image
class="card-image"
:src="item.cover_url || '/static/avatar/1.jpeg'"
mode="aspectFill"
/>
<!-- 点赞数叠在图片底部 -->
<view class="card-footer">
<image class="card-heart" src="/static/icon/heart-icon.png" mode="aspectFit" />
<view class="card-likes-wrap">
<text class="card-likes">{{ formatLikes(item.like_count) }}</text>
</view>
</view>
</view>
<!-- 边框叠在整张卡片上 -->
<image class="card-frame-border" src="/static/square/gerenzhongxincangpinkuang.png" mode="aspectFill" />
</view>
</view>
<!-- 骨架屏 -->
<view v-if="loading" class="skeleton-wrap">
<view v-for="i in 3" :key="i" class="skeleton-card" :class="`skeleton-pos-${i - 1}`" />
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { ref, onMounted, onUnmounted } from 'vue';
import { getHotRankingApi } from '@/utils/api.js';
const emit = defineEmits(['dataLoaded']);
const emit = defineEmits(['dataLoaded', 'top3Click']);
const top3Items = ref([]);
const loading = ref(true);
const resolveOssUrl = async (fileName, type) => {
if (!fileName) return '';
@ -17,6 +54,13 @@ const resolveOssUrl = async (fileName, type) => {
return fileName;
};
const formatLikes = (n) => {
if (!n || isNaN(n)) return '0';
if (n >= 10000) return (n / 10000).toFixed(1) + 'w';
if (n >= 1000) return (n / 1000).toFixed(1) + 'k';
return String(n);
};
const loadTop3 = async () => {
try {
const res = await getHotRankingApi('total', null, 1, 3);
@ -25,10 +69,13 @@ const loadTop3 = async () => {
const resolved = items.map(item => {
return { ...item, cover_url: item.cover_url || '', avatar_url: item.avatar_url || '' };
});
top3Items.value = resolved;
loading.value = false;
emit('dataLoaded', resolved);
}
} catch (e) {
console.error('[BannerTop3] 加载失败', e?.message ?? e);
loading.value = false;
}
};
@ -37,6 +84,16 @@ const loadTop3 = async () => {
// };
onMounted(loadTop3);
//
uni.$on('userInfoUpdated', () => {
loadTop3();
});
onUnmounted(() => {
uni.$off('userInfoUpdated');
});
defineExpose({ reload: loadTop3 });
</script>
@ -45,7 +102,7 @@ defineExpose({ reload: loadTop3 });
width: 100%;
height: 360rpx;
position: relative;
overflow: hidden;
/* overflow: hidden; */
border-radius: 24rpx;
}
@ -55,4 +112,143 @@ defineExpose({ reload: loadTop3 });
width: 100%;
height: 100%;
}
/* 卡片层 */
.cards-overlay {
position: absolute;
top: 0;
right: 8rpx;
width: 420rpx;
height: 360rpx;
pointer-events: auto;
z-index: 10;
}
.card-wrapper {
position: absolute;
width: 148rpx;
height: 220rpx;
}
.card-pos-0 {
left: 50rpx;
top: 40rpx;
transform: rotate(-6deg);
z-index: 3;
}
.card-pos-1 {
left: 140rpx;
top: -8rpx;
transform: rotate(6deg);
z-index: 4;
}
.card-pos-2 {
left: 240rpx;
top: 106rpx;
transform: rotate(16deg);
z-index: 5;
}
.card-frame {
width: 100%;
height: 100%;
border-radius: 16rpx;
position: relative;
overflow: visible;
}
.card-image-wrap {
width: 90%;
height: 92%;
position: relative;
border-radius: 16rpx;
overflow: hidden;
z-index: 5;
padding: 8rpx;
}
.card-image {
width: 100%;
height: 100%;
border-radius: 16rpx;
}
.card-frame-border {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 2;
pointer-events: none;
}
.card-footer {
position: absolute;
bottom: 8rpx;
left: 0;
right: 64rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 4rpx;
z-index: 3;
}
.card-heart {
width: 22rpx;
height: 22rpx;
flex-shrink: 0;
}
.card-likes-wrap {
background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%
);
border-radius: 8rpx;
padding: 2rpx 8rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
}
.card-likes {
font-size: 12rpx;
color: #ffffff;
font-weight: 700;
}
/* 骨架屏 */
.skeleton-wrap {
position: absolute;
inset: 0;
z-index: 5;
}
.skeleton-card {
position: absolute;
width: 148rpx;
height: 220rpx;
border-radius: 16rpx;
background: linear-gradient(90deg,
rgba(255,255,255,0.06) 25%,
rgba(255,255,255,0.14) 50%,
rgba(255,255,255,0.06) 75%);
background-size: 200% 100%;
animation: shimmer 1.4s infinite;
}
.skeleton-pos-0 { left: 50rpx; top: 40rpx; transform: rotate(-6deg); }
.skeleton-pos-1 { left: 140rpx; top: -40rpx; transform: rotate(6deg); }
.skeleton-pos-2 { left: 240rpx; top: 10rpx; transform: rotate(16deg); }
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
</style>

View File

@ -67,7 +67,7 @@
<!-- 收益文字 -->
<view class="crystal-bg-layer">
<text class="balance-income">收益 27.1/H</text>
<text class="balance-income">收益 0/H</text>
</view>
</view>

View File

@ -150,7 +150,7 @@
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { ref, onMounted, onUnmounted } from 'vue';
import { getMyExhibitedAssetsApi, getMyLikedAssetsApi, getMyTodayLikedAssetsApi, getMyWeekLikedAssetsApi, getMyGalleriesApi, placeAssetToGalleryApi } from '@/utils/api.js';
import AssetSelector from '../components/AssetSelector.vue';
import { onShow } from '@dcloudio/uni-app';
@ -379,6 +379,16 @@ const loadLikedAssets = async () => {
onMounted(() => {
loadExhibitedAssets();
loadLikedAssets();
//
uni.$on('userInfoUpdated', () => {
loadExhibitedAssets();
loadLikedAssets();
});
});
onUnmounted(() => {
uni.$off('userInfoUpdated');
});
onShow(() => {

View File

@ -9,8 +9,9 @@
:indicator-dots="false"
@change="onSwiperChange"
>
<swiper-item @click.stop="$emit('top3Click')">
<BannerTop3 @dataLoaded="onTop3DataLoaded" />
<swiper-item @click.stop="$emit('top3Click')" style="display: flex;
align-items: center;">
<BannerTop3 @dataLoaded="onTop3DataLoaded" @top3Click="$emit('top3Click')" />
</swiper-item>
<swiper-item
v-for="item in bannerActivities"
@ -24,44 +25,6 @@
/>
</swiper-item>
</swiper>
<!-- 卡片层跟随第一个 swiper-item切走时隐藏 -->
<view
class="cards-overlay"
:style="{ opacity: currentIndex === 0 ? 1 : 0, pointerEvents: currentIndex === 0 ? 'auto' : 'none' }"
@click.stop="$emit('top3Click')"
>
<view
v-for="(item, index) in top3Items"
:key="item.asset_id || index"
class="card-wrapper"
:class="`card-pos-${index}`"
>
<view class="card-frame">
<view class="card-image-wrap">
<image
class="card-image"
:src="item.cover_url || '/static/avatar/1.jpeg'"
mode="aspectFill"
/>
<!-- 点赞数叠在图片底部 -->
<view class="card-footer">
<image class="card-heart" src="/static/icon/heart-icon.png" mode="aspectFit" />
<view class="card-likes-wrap">
<text class="card-likes">{{ formatLikes(item.like_count) }}</text>
</view>
</view>
</view>
<!-- 边框叠在整张卡片上 -->
<image class="card-frame-border" src="/static/square/gerenzhongxincangpinkuang.png" mode="aspectFill" />
</view>
</view>
<!-- 骨架屏 -->
<view v-if="loading" class="skeleton-wrap">
<view v-for="i in 3" :key="i" class="skeleton-card" :class="`skeleton-pos-${i - 1}`" />
</view>
</view>
</view>
</template>
@ -74,8 +37,6 @@ defineProps({
})
defineEmits(['activityClick', 'top3Click'])
const top3Items = ref([])
const loading = ref(true)
const currentIndex = ref(0)
const onSwiperChange = (e) => {
@ -83,17 +44,8 @@ const onSwiperChange = (e) => {
}
const onTop3DataLoaded = (items) => {
const list = items.slice(0, 3)
while (list.length < 3) list.push({ asset_id: `placeholder-${list.length}` })
top3Items.value = list
loading.value = false
}
const formatLikes = (n) => {
if (!n || isNaN(n)) return '0'
if (n >= 10000) return (n / 10000).toFixed(1) + 'w'
if (n >= 1000) return (n / 1000).toFixed(1) + 'k'
return String(n)
// BannerTop3
console.log('[BannerCarousel] top3 data loaded', items)
}
</script>
@ -108,161 +60,18 @@ const formatLikes = (n) => {
.banner-swiper {
width: 100%;
height: 360rpx;
height: 392rpx;
border-radius: 24rpx;
position: relative;
bottom: 24rpx;
}
.banner-activity-img {
width: 100%;
height: 100%;
height: 328rpx;
display: block;
}
/* 卡片层:绝对定位叠在 swiper 上,可自由溢出 */
.cards-overlay {
position: absolute;
top: 0;
right: 8rpx;
width: 420rpx;
height: 360rpx;
pointer-events: auto;
z-index: 10;
}
.card-wrapper {
position: absolute;
width: 148rpx;
height: 220rpx;
}
.card-pos-0 {
left: 50rpx;
top: 40rpx;
transform: rotate(-6deg);
z-index: 3;
}
.card-pos-1 {
left: 140rpx;
top: -8rpx;
transform: rotate(6deg);
z-index: 4;
}
.card-pos-2 {
left: 240rpx;
top: 106rpx;
transform: rotate(16deg);
z-index: 5;
}
.card-frame {
width: 100%;
height: 100%;
border-radius: 16rpx;
border-radius:24rpx;
position: relative;
overflow: visible;
}
.card-image-wrap {
width: 90%;
height: 92%;
position: relative;
border-radius: 16rpx;
overflow: hidden;
z-index: 5;
padding: 8rpx;
}
.card-image {
width: 100%;
height: 100%;
border-radius: 16rpx;
}
.card-frame-border {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 2;
pointer-events: none;
}
.card-footer {
position: absolute;
bottom: 8rpx;
left: 0;
right: 64rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 4rpx;
z-index: 3;
}
.card-bg-frame {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 0;
pointer-events: none;
}
.card-heart {
width: 22rpx;
height: 22rpx;
flex-shrink: 0;
}
.card-likes-wrap {
background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%
);
border-radius: 8rpx;
padding: 2rpx 8rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
}
.card-likes {
font-size: 12rpx;
color: #ffffff;
font-weight: 700;
}
/* 骨架屏 */
.skeleton-wrap {
position: absolute;
inset: 0;
z-index: 5;
}
.skeleton-card {
position: absolute;
width: 148rpx;
height: 220rpx;
border-radius: 16rpx;
background: linear-gradient(90deg,
rgba(255,255,255,0.06) 25%,
rgba(255,255,255,0.14) 50%,
rgba(255,255,255,0.06) 75%);
background-size: 200% 100%;
animation: shimmer 1.4s infinite;
}
.skeleton-pos-0 { left: 50rpx; top: 40rpx; transform: rotate(-6deg); }
.skeleton-pos-1 { left: 140rpx; top: -40rpx; transform: rotate(6deg); }
.skeleton-pos-2 { left: 240rpx; top: 10rpx; transform: rotate(16deg); }
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
top: 32rpx;
}
</style>

View File

@ -52,6 +52,7 @@ const tabs = [
top: unset;
left: unset;
right: unset;
bottom: 16rpx;
z-index: 100;
display: flex;
flex-direction: row;

View File

@ -1,7 +1,7 @@
// API 基础配置
// const baseURL = 'http://101.132.250.62:8080'
const baseURL = 'http://101.132.250.62:8080'
// const baseURL = 'http://192.168.110.60:8080'
const baseURL = 'http://localhost:8080'
// const baseURL = 'http://localhost:8080'
// 是否使用模拟数据(开发调试时设为 true后端API准备好后改为 false
const USE_MOCK_API = false