feat: 星册添加点赞数,修改样式
@ -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
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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%;
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/* 排名图标 - 排名越靠前越大 */
|
||||
|
||||
@ -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;
|
||||
|
||||
BIN
frontend/static/rank/paihangbang.png
Normal file
|
After Width: | Height: | Size: 399 KiB |
BIN
frontend/static/square/dianjibaifang.png
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
BIN
frontend/static/square/diyi.png
Normal file
|
After Width: | Height: | Size: 191 KiB |
BIN
frontend/static/square/shuijingtubiao.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
frontend/static/starbookcontent/V1dengji.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
frontend/static/starbookcontent/V2dengji.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
frontend/static/starbookcontent/V3dengji.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 298 KiB After Width: | Height: | Size: 298 KiB |
BIN
frontend/static/starbookcontent/dengji.png
Normal file
|
After Width: | Height: | Size: 295 KiB |
BIN
frontend/static/starbookcontent/dengji2.png
Normal file
|
After Width: | Height: | Size: 302 KiB |
BIN
frontend/static/starbookcontent/dengji3.png
Normal file
|
After Width: | Height: | Size: 303 KiB |
BIN
frontend/static/starbookcontent/tuichu.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
@ -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
|
||||
|
||||