feat: 星册添加点赞数,修改样式

This commit is contained in:
zerosaturation 2026-05-08 20:20:52 +08:00
parent 86f32cd4df
commit b1c5bf13c8
22 changed files with 312 additions and 72 deletions

View File

@ -32,6 +32,11 @@ type GalleryRepository interface {
// 资产注册表相关
UpdateAssetRegistryDisplayStatus(assetID int64, displayStatus int32) error
// 事务性操作:创建展品并更新展示状态(原子操作)
PlaceExhibitionTx(exhibition *models.Exhibition, displayStatus int32) error
// 事务性操作:删除展品并更新展示状态(原子操作)
RemoveExhibitionTx(exhibitionID int64, assetID int64) error
// ========== 我的作品相关 ==========
// GetMyExhibitedAssets 获取我展出的作品列表(只返回展出中且未过期的,含收益)
@ -282,6 +287,59 @@ func (r *galleryRepository) UpdateAssetRegistryDisplayStatus(assetID int64, disp
Update("display_status", displayStatus).Error
}
// PlaceExhibitionTx 事务性创建展品并更新展示状态(原子操作)
func (r *galleryRepository) PlaceExhibitionTx(exhibition *models.Exhibition, displayStatus int32) error {
now := time.Now().UnixMilli()
exhibition.CreatedAt = now
exhibition.UpdatedAt = now
exhibition.DeletedAt = nil
return r.db.Transaction(func(tx *gorm.DB) error {
// 1. 物理删除已软删除的旧记录
if err := tx.Exec(`
DELETE FROM exhibitions
WHERE asset_id = ? AND deleted_at IS NOT NULL
`, exhibition.AssetID).Error; err != nil {
return err
}
// 2. 插入新记录
if err := tx.Create(exhibition).Error; err != nil {
return err
}
// 3. 更新展示状态(与展出操作在同一事务中)
if err := tx.Model(&models.AssetRegistry{}).
Where("asset_id = ?", exhibition.AssetID).
Update("display_status", displayStatus).Error; err != nil {
return err
}
return nil
})
}
// RemoveExhibitionTx 事务性删除展品并更新展示状态(原子操作)
func (r *galleryRepository) RemoveExhibitionTx(exhibitionID int64, assetID int64) error {
now := time.Now().UnixMilli()
return r.db.Transaction(func(tx *gorm.DB) error {
// 1. 软删除展品记录
if err := tx.Model(&models.Exhibition{}).
Where("id = ?", exhibitionID).
Updates(map[string]interface{}{
"deleted_at": now,
"updated_at": now,
}).Error; err != nil {
return err
}
// 2. 更新展示状态为未展示(与删除操作在同一事务中)
if err := tx.Model(&models.AssetRegistry{}).
Where("asset_id = ?", assetID).
Update("display_status", int32(0)).Error; err != nil {
return err
}
return nil
})
}
// ========== 我的作品相关实现 ==========
// GetMyExhibitedAssets 获取我展出的作品列表(只返回展出中且未过期的,含收益)
@ -304,15 +362,16 @@ func (r *galleryRepository) GetMyExhibitedAssets(userID, starID int64, page, pag
err = r.db.Model(&models.Exhibition{}).
Raw(`
SELECT exhibitions.asset_id, a.name, a.cover_url, a.like_count,
exhibitions.start_time as exhibited_at, exhibitions.expire_at,
exhibitions.start_time as exhibited_at, exhibitions.expire_at, bs.slot_index,
COALESCE(CAST(SUM(err.crystal_amount) / 10 AS bigint), 0) as earnings
FROM exhibitions
JOIN assets a ON a.id = exhibitions.asset_id
JOIN booth_slots bs ON bs.slot_id = exhibitions.slot_id
LEFT JOIN exhibition_revenue_records err ON err.asset_id = a.id AND err.status = 'claimable'
WHERE exhibitions.occupier_uid = ? AND exhibitions.occupier_star_id = ?
AND exhibitions.deleted_at IS NULL AND exhibitions.expire_at > ?
GROUP BY exhibitions.asset_id, a.name, a.cover_url, a.like_count, exhibitions.start_time, exhibitions.expire_at
ORDER BY exhibitions.start_time DESC
GROUP BY exhibitions.asset_id, a.name, a.cover_url, a.like_count, exhibitions.start_time, exhibitions.expire_at, bs.slot_index
ORDER BY bs.slot_index ASC
LIMIT ? OFFSET ?
`, userID, starID, now, pageSize, offset).Scan(&items).Error
@ -343,15 +402,16 @@ func (r *galleryRepository) GetUserExhibitedAssets(userID, starID int64, page, p
err = r.db.Model(&models.Exhibition{}).
Raw(`
SELECT exhibitions.asset_id, a.name, a.cover_url, a.like_count,
exhibitions.start_time as exhibited_at, exhibitions.expire_at,
exhibitions.start_time as exhibited_at, exhibitions.expire_at, bs.slot_index,
COALESCE(CAST(SUM(err.crystal_amount) / 10 AS bigint), 0) as earnings
FROM exhibitions
JOIN assets a ON a.id = exhibitions.asset_id
JOIN booth_slots bs ON bs.slot_id = exhibitions.slot_id
LEFT JOIN exhibition_revenue_records err ON err.asset_id = a.id AND err.status = 'claimable'
WHERE exhibitions.occupier_uid = ? AND exhibitions.occupier_star_id = ?
AND exhibitions.deleted_at IS NULL AND exhibitions.expire_at > ?
GROUP BY exhibitions.asset_id, a.name, a.cover_url, a.like_count, exhibitions.start_time, exhibitions.expire_at
ORDER BY exhibitions.start_time DESC
GROUP BY exhibitions.asset_id, a.name, a.cover_url, a.like_count, exhibitions.start_time, exhibitions.expire_at, bs.slot_index
ORDER BY bs.slot_index ASC
LIMIT ? OFFSET ?
`, userID, starID, now, pageSize, offset).Scan(&items).Error

View File

@ -276,6 +276,7 @@ func (s *starbookService) buildAssetItemsFromRegistries(registries []*models.Ass
var assetCoverMap map[int64]string // assetID -> coverURL
var assetNameMap map[int64]string // assetID -> name
var categoryMap map[int64]string // assetID -> category
var assetLikeCountMap map[int64]int32 // assetID -> likeCount
switch assetType {
case models.AssetTypeRegular:
@ -283,9 +284,11 @@ func (s *starbookService) buildAssetItemsFromRegistries(registries []*models.Ass
if err == nil && len(assets) > 0 {
assetCoverMap = make(map[int64]string)
assetNameMap = make(map[int64]string)
assetLikeCountMap = make(map[int64]int32)
for _, asset := range assets {
assetCoverMap[asset.ID] = asset.CoverURL
assetNameMap[asset.ID] = asset.Name
assetLikeCountMap[asset.ID] = asset.LikeCount
}
}
case models.AssetTypeCollection:
@ -345,6 +348,13 @@ func (s *starbookService) buildAssetItemsFromRegistries(registries []*models.Ass
item.Category = cat
}
// 从 assets 表获取点赞数regular 类型)
if assetType == models.AssetTypeRegular {
if likeCount, ok := assetLikeCountMap[reg.AssetID]; ok {
item.LikeCount = likeCount
}
}
items = append(items, item)
}

View File

@ -55,11 +55,11 @@
>
<image
class="visit-icon"
src="/static/icon/visit-house.png"
src="/static/square/dianjibaifang.png"
mode="aspectFit"
lazy-load
></image>
<text class="visit-text">拜访小屋</text>
<text class="visit-text">点击拜访</text>
</view>
</view>
@ -71,8 +71,8 @@
class="menu-item"
@tap="handleMenuVisit"
>
<image class="menu-icon" src="/static/icon/visit-house.png" mode="aspectFit" lazy-load></image>
<text class="menu-text">拜访小屋</text>
<image class="menu-icon" src="/static/square/dianjibaifang.png" mode="aspectFit" lazy-load></image>
<text class="menu-text">点击拜访</text>
</view>
<view class="menu-item" @tap="handleMenuViewProfile">
<image class="menu-icon" src="/static/icon/character.png" mode="aspectFit" lazy-load></image>
@ -207,14 +207,14 @@ const handleArtworkError = (e) => {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 32rpx;
margin-bottom: 24rpx;
gap: 20rpx;
position:relative;
}
.rank-decoration {
position: absolute;
bottom: -24rpx;
bottom: -8rpx;
left: 32rpx;
width: 92%;
height: 8rpx;
@ -225,14 +225,14 @@ const handleArtworkError = (e) => {
/* 排名编号 */
.rank-number {
min-width: 48rpx;
min-width: 64rpx;
display: flex;
align-items: center;
padding: 8rpx;
}
.rank-text {
font-size: 48rpx;
font-size: 32rpx;
font-weight: bold;
color: #FFFFFF;
text-shadow:
@ -269,8 +269,8 @@ const handleArtworkError = (e) => {
/* 作品图片 */
.artwork-image {
width: 96rpx;
height: 120rpx;
width: 80rpx;
height: 104rpx;
border-radius: 16rpx;
border: 3rpx solid rgba(255, 255, 255, 0.6);
/* 优化2层阴影简化为1层 */
@ -359,6 +359,7 @@ const handleArtworkError = (e) => {
}
.popularity-container {
position: relative;
display: flex;
align-items: center;
gap: 20rpx;
@ -373,12 +374,16 @@ const handleArtworkError = (e) => {
}
.fire-icon {
width: 32rpx;
height: 40rpx;
width: 64rpx;
height: 72rpx;
position: absolute;
left: -32rpx;
top: -32rpx;
}
.popularity-score {
font-size: 24rpx;
padding: 0 16rpx 0 32rpx;
color: #FFFFFF;
text-shadow:
0 2rpx 4rpx rgba(0, 0, 0, 0.4),
@ -401,7 +406,8 @@ const handleArtworkError = (e) => {
.visit-icon {
width: 80rpx;
height: 80rpx;
transform: scale(1.2);
margin-bottom: 8rpx;
/* transform: scale(1.2); */
}
.visit-text{
color: #FFFFFF;

View File

@ -1295,15 +1295,12 @@ const handleTouchEnd = (e) => {
.modal-container {
position: absolute;
width: 100%;
width: calc(100% - 16rpx);
height: calc(100% - 224rpx);
bottom: 32rpx;
border-radius: 48rpx;
overflow: hidden;
z-index: 10;
/* 优化3层阴影简化为1层提升渲染性能 */
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.35);
border: 2rpx solid rgba(255, 255, 255, 0.2);
/* 添加过渡动画,使下拉关闭更流畅 */
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
@ -1319,9 +1316,10 @@ const handleTouchEnd = (e) => {
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background-image: url('/static/rank/rank-bg.png');
/* width: 100%;
height: 100%; */
background-image: url('/static/rank/paihangbang.png');
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center;
z-index: 0;
@ -1356,6 +1354,7 @@ const handleTouchEnd = (e) => {
margin-bottom: 40rpx;
padding: 0 15rpx;
position: relative;
top: 16rpx;
}
.nav-arrow {
@ -1405,7 +1404,7 @@ const handleTouchEnd = (e) => {
.ranking-title-image.loaded {
opacity: 1;
transform: scale(2.5);
transform: scale(2.3);
}
@ -1625,7 +1624,7 @@ const handleTouchEnd = (e) => {
justify-content: space-between;
gap: 60rpx;
margin-bottom: 84rpx;
padding: 0 15rpx;
padding: 0 48rpx;
}
/* 空数据提示 */
@ -1652,7 +1651,7 @@ const handleTouchEnd = (e) => {
/* 排名列表区域 */
.ranking-list-section {
margin-top: 20rpx;
padding: 0 15rpx;
padding: 0 48rpx;
}
/* 加载更多容器 */
@ -1702,9 +1701,9 @@ const handleTouchEnd = (e) => {
/* 当前用户栏 - 固定在底部 */
.current-user-bar {
position: absolute;
bottom: 148rpx;
left: 20rpx;
right: 20rpx;
bottom: 152rpx;
left: 84rpx;
right: 84rpx;
padding: 24rpx;
/* 优化5色渐变简化为2色提升性能 */
background: linear-gradient(135deg, rgba(255, 107, 157, 0.9) 0%, rgba(255, 177, 153, 0.9) 100%);
@ -1733,11 +1732,12 @@ const handleTouchEnd = (e) => {
/* gap: 34rpx; */
position: relative;
z-index: 1;
height: 64rpx;
}
.current-user-avatar {
width: 88rpx;
height: 88rpx;
height: 64rpx;
border-radius: 50%;
border: 4rpx solid rgba(255, 255, 255, 0.9);
box-shadow:
@ -1746,7 +1746,7 @@ const handleTouchEnd = (e) => {
}
.current-user-avatar.no-artwork {
width: 64rpx;
width: 56rpx;
border-radius: 0;
}
@ -2103,7 +2103,7 @@ const handleTouchEnd = (e) => {
.nav-arrow {
min-width: 88rpx;
transform: scale(2)
transform: scale(1.7)
}
.tab-item {

View File

@ -1,7 +1,7 @@
<template>
<view class="starbook-content">
<!-- 背景图片 -->
<image class="background-image" src="/static/background/starbook.jpg" mode="aspectFill"></image>
<image class="background-image" src="/static/starbookcontent/beijing.png" mode="aspectFill"></image>
<!-- 内容区域 -->
<view class="content-wrapper">
@ -53,6 +53,11 @@
<!-- category 下的所有 grades -->
<view v-for="gradeItem in group.grades" :key="gradeItem.grade" class="grade-section">
<view class="group-header">
<image
class="group-header-bg"
:src="getGradeBackground(gradeItem.grade)"
mode="aspectFill"
></image>
<text class="group-title">{{ formatGrade(gradeItem.grade) }}</text>
</view>
<scroll-view class="nft-row" scroll-x :show-scrollbar="false" :enable-flex="true">
@ -73,8 +78,9 @@
<view v-if="item.display_status === 1" class="status-overlay">
<text class="status-text-center">已展示</text>
</view>
<view class="nft-info">
<text class="nft-name">{{ item.name }}</text>
<view class="card-rate-badge-overlay">
<image class="heart-icon" src="/static/icon/heart-icon.png" mode="aspectFit"></image>
<text class="card-rate-text">{{ item.like_count || 0 }}</text>
</view>
</view>
<!-- 更多按钮 -->
@ -213,10 +219,20 @@ const cardSize = computed(() => {
return Math.floor(availableWidth / 2.5);
});
// grade
const gradeMap = { 1: '一', 2: '二', 3: '三', 4: '四', 5: '五' };
// grade
function formatGrade(grade) {
return `等级${gradeMap[grade] || grade}`;
return `V${grade}`;
}
//
function getGradeBackground(grade) {
if (grade <= 2) {
return '/static/starbookcontent/V1dengji.png';
} else if (grade <= 4) {
return '/static/starbookcontent/V2dengji.png';
} else {
return '/static/starbookcontent/V3dengji.png';
}
}
//
@ -495,14 +511,31 @@ watch(() => props.isActive, (newVal) => {
/* 分组标题 */
.group-header {
position: relative;
margin-bottom: 16rpx;
padding-bottom: 10rpx;
border-bottom: 1rpx solid rgba(255, 255, 255, 0.1);
padding: 16rpx 24rpx;
display: inline-flex;
align-items: center;
justify-content: center;
width: 160rpx;
height: 32rpx;
}
.group-header-bg {
position: absolute;
top: 0;
left: 0;
width: 160rpx;
height: 64rpx;
z-index: 0;
}
.group-title {
font-size: 26rpx;
position: relative;
z-index: 1;
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
font-weight: 600;
}
/* 藏品行 - 水平滚动 */
@ -510,13 +543,15 @@ watch(() => props.isActive, (newVal) => {
width: 100%;
height: 288rpx;
white-space: nowrap;
background: #f0839960;
padding: 0.5rem;
}
/* 藏品行内容容器 */
.nft-row-content {
display: inline-block;
white-space: nowrap;
padding-left: 24rpx;
/* padding-left: 24rpx; */
height: 100%;
}
@ -541,7 +576,7 @@ watch(() => props.isActive, (newVal) => {
/* NFT 图片 */
.nft-image {
width: 192rpx;
height: 224rpx;
height: 100%;
border-radius: 16rpx;
/* background: rgba(255, 255, 255, 0.05); */
display: block;
@ -549,7 +584,7 @@ watch(() => props.isActive, (newVal) => {
/* 已展示的图片 - 灰色滤镜 */
.nft-image-displayed {
filter: grayscale(23%);
filter: grayscale(25%);
}
/* 更多占位符 */
@ -573,7 +608,7 @@ watch(() => props.isActive, (newVal) => {
top: 0;
left: 0;
width: 192rpx;
height: 224rpx;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
@ -608,6 +643,41 @@ watch(() => props.isActive, (newVal) => {
padding: 16rpx;
}
/* 点赞数徽章 - 图片上覆盖层 */
.card-rate-badge-overlay {
position: absolute;
bottom: 12rpx;
left: 12rpx;
display: flex;
align-items: center;
background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%);
border-radius: 999rpx;
padding: 8rpx 20rpx 8rpx 40rpx;
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
/* background: rgba(0, 0, 0, 0.5); */
border-radius: 20rpx;
padding: 6rpx 12rpx;
z-index: 2;
}
.card-rate-badge-overlay .heart-icon {
width: 24rpx;
height: 24rpx;
margin-right: 6rpx;
}
.card-rate-badge-overlay .card-rate-text {
font-size: 22rpx;
color: #fff;
font-weight: bold;
}
.nft-name {
display: block;
font-size: 22rpx;
@ -629,7 +699,7 @@ watch(() => props.isActive, (newVal) => {
left: 50%;
transform: translate(-50%, -50%); */
width: 192rpx;
height: 224rpx;
height: 100%;
display: flex;
align-items: center;
justify-content: center;

View File

@ -97,8 +97,8 @@
class="menu-item"
@tap="handleMenuVisit"
>
<image class="menu-icon" src="/static/icon/visit-house.png" mode="aspectFit" lazy-load></image>
<text class="menu-text">拜访小屋</text>
<image class="menu-icon" src="/static/square/dianjibaifang.png" mode="aspectFit" lazy-load></image>
<text class="menu-text">点击拜访</text>
</view>
<view class="menu-item" @tap="handleMenuViewProfile">
<image class="menu-icon" src="/static/icon/character.png" mode="aspectFit" lazy-load></image>
@ -426,8 +426,8 @@
}
.user-nickname {
font-size: 14rpx;
margin-left: 12rpx;
font-size: 12rpx;
margin-left: 10rpx;
color: #FFFFFF;
text-align: center;
max-width: 100%;

View File

@ -103,7 +103,7 @@
<!-- 右侧奖励 -->
<view class="liked-reward">
<image class="reward-token-icon" src="/static/icon/crystal.png" mode="aspectFit">
<image class="reward-token-icon" :src="item.earnings > 10 ? '/static/square/shuijingtubiao.png' : '/static/icon/crystal.png'" mode="aspectFit">
</image>
<text class="reward-amount">+{{ item.reward }}</text>
</view>

View File

@ -104,9 +104,10 @@
<image v-if="index < 3" :src="rankIcons[index]" :class="'rank-icon rank-icon-' + (index + 1)" mode="aspectFit"></image>
<!-- 卡片主体 -->
<view class="liked-item" :class="index === 0 ? 'liked-item-first' : ''">
<!-- 作品封面 -->
<view class="liked-cover-wrap" :class="index === 0 ? 'liked-cover-wrap-first' : ''">
<view class="liked-cover-wrap" :class="index === 0 ? 'liked-cover-wrap-first' : ''" >
<image class="liked-cover" :src="item.cover_url || '/static/nft/placeholder.png'"
mode="aspectFill"></image>
<image class="liked-cover-frame" src="/static/square/cangpinkuang1.png"
@ -125,7 +126,7 @@
<!-- 右侧奖励 -->
<view class="liked-reward">
<image class="reward-token-icon" src="/static/icon/crystal.png" mode="aspectFit">
<image class="reward-token-icon" :src="item.reward > 10 ? '/static/square/shuijingtubiao.png' : '/static/icon/crystal.png'" mode="aspectFit">
</image>
<text class="reward-amount">+{{ item.reward }}</text>
</view>
@ -323,15 +324,19 @@ const loadExhibitedAssets = async () => {
try {
const res = await getMyExhibitedAssetsApi(1, 20);
if (res.data && res.data.items) {
exhibitionWorks.value = res.data.items.map(item => ({
id: item.asset_id,
cover_url: item.cover_url,
like_count: item.like_count,
earnings: item.earnings,
exhibited_at: item.exhibited_at,
expire_at: item.expire_at,
name: item.name,
}));
exhibitionWorks.value = res.data.items
.map(item => ({
id: item.asset_id,
cover_url: item.cover_url,
like_count: item.like_count,
earnings: item.earnings,
exhibited_at: item.exhibited_at,
expire_at: item.expire_at,
name: item.name,
slot_index: item.slot_index ?? 0,
}))
.sort((a, b) => (a.slot_index ?? 0) - (b.slot_index ?? 0));
console.log('展出作品:', exhibitionWorks.value);
}
} catch (err) {
console.error('加载展出作品失败:', err);
@ -545,10 +550,14 @@ onShow(() => {
.card-tilt-left {
transform: rotate(-4deg) translateY(10rpx);
margin-right: 32rpx;
border-radius: 32rpx;
box-shadow: -16rpx 16rpx 16rpx rgba(229, 76, 93, 0.9);
}
.card-tilt-right {
transform: rotate(4deg) translateY(10rpx);
border-radius: 32rpx;
box-shadow: 16rpx 16rpx 16rpx rgba(229, 76, 93, 0.9);
}
.card-income-row.income-tilt-right {
@ -611,7 +620,7 @@ onShow(() => {
/* 图片下方收益 */
.card-income-row {
position: absolute;
bottom: -52rpx;
bottom: -88rpx;
left: 50%;
transform: translateX(-50%);
display: flex;
@ -758,7 +767,7 @@ onShow(() => {
display: flex;
align-items: center;
background: #ffffff50;
border-radius: 32rpx;
border-radius: 48rpx;
padding: 16rpx 20rpx;
gap: 16rpx;
overflow: hidden;
@ -771,6 +780,10 @@ onShow(() => {
padding: 28rpx 20rpx;
width: 90%;
padding-left: 20%;
background-image: url(/static/square/diyi.png);
background-size: 102%;
background-position: center;
background-repeat: no-repeat;
}
/* 排名图标 - 排名越靠前越大 */

View File

@ -23,12 +23,20 @@
>
<image
class="nft-cover"
:class="{ 'nft-image-displayed': item.display_status === 1 }"
:src="item.coverUrl || item.cover_url_signed"
mode="aspectFill"
/>
<view class="nft-info">
<text class="nft-name">{{ item.name }}</text>
<view v-if="item.display_status === 1" class="status-overlay">
<text class="status-text-center">已展示</text>
</view>
<view class="card-rate-badge-overlay">
<image class="heart-icon" src="/static/icon/heart-icon.png" mode="aspectFit"></image>
<text class="card-rate-text">{{ item.like_count || 0 }}</text>
</view>
<!-- <view class="nft-info">
<text class="nft-name">{{ item.name }}</text>
</view> -->
</view>
</view>
@ -232,6 +240,7 @@ onMounted(() => {
}
.nft-grid-item {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
@ -241,7 +250,7 @@ onMounted(() => {
.nft-cover {
width: 192rpx;
height: 224rpx;
height: 256rpx;
}
.nft-grid-item:active {
@ -264,6 +273,78 @@ onMounted(() => {
white-space: nowrap;
}
/* 已展示的图片 - 灰色滤镜 */
.nft-image-displayed {
filter: grayscale(25%);
}
/* 展示状态覆盖层 - 居中显示 */
.status-overlay {
position: absolute;
top: 0;
left: 0;
width: 192rpx;
height: 224rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 16rpx;
z-index: 1;
}
.status-text-center {
font-size: 24rpx;
color: #fff;
font-weight: bold;
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.8);
background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%
);
border-radius: 24rpx;
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
0 2rpx 6rpx rgba(255, 143, 158, 0.15),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4),
inset 0 -2rpx 4rpx rgba(0, 0, 0, 0.05);
padding: 16rpx;
}
/* 点赞数徽章 - 图片上覆盖层 */
.card-rate-badge-overlay {
position: absolute;
bottom: 12rpx;
left: 12rpx;
display: flex;
align-items: center;
background: linear-gradient(to bottom right,
#F0E4B1 0%,
#F08399 50%,
#B94E73 100%);
border-radius: 999rpx;
padding: 8rpx 20rpx 8rpx 40rpx;
box-shadow:
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
border-radius: 20rpx;
padding: 6rpx 12rpx;
z-index: 2;
}
.card-rate-badge-overlay .heart-icon {
width: 24rpx;
height: 24rpx;
margin-right: 6rpx;
}
.card-rate-badge-overlay .card-rate-text {
font-size: 22rpx;
color: #fff;
font-weight: bold;
}
/* 加载更多 */
.load-more {
display: flex;

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 298 KiB

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

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