feat: 修改各种小bug
This commit is contained in:
parent
78c70e14df
commit
2450fc1368
@ -27,6 +27,9 @@ type AssetRepository interface {
|
||||
// GetDisplayStatusByAssetID 根据asset_id查询展示状态(从asset_registry表)
|
||||
GetDisplayStatusByAssetID(assetID int64) (int32, error)
|
||||
|
||||
// GetGradeByAssetID 根据asset_id查询藏品等级(从asset_registry表)
|
||||
GetGradeByAssetID(assetID int64) (int32, error)
|
||||
|
||||
// GetByOwner 查询用户的资产列表
|
||||
GetByOwner(ownerUID, starID int64, limit, offset int) ([]*models.Asset, error)
|
||||
|
||||
@ -127,6 +130,27 @@ func (r *assetRepository) GetDisplayStatusByAssetID(assetID int64) (int32, error
|
||||
return registry.DisplayStatus, nil
|
||||
}
|
||||
|
||||
// GetGradeByAssetID 根据asset_id查询藏品等级(从asset_registry表)
|
||||
func (r *assetRepository) GetGradeByAssetID(assetID int64) (int32, error) {
|
||||
if assetID <= 0 {
|
||||
return 0, errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
|
||||
var registry models.AssetRegistry
|
||||
// 直接使用表名查询 public.asset_registry
|
||||
if err := r.db.Table("public.asset_registry").Where("asset_id = ?", assetID).First(®istry).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return 0, nil // 没找到返回0,不报错
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if registry.Grade != nil {
|
||||
return *registry.Grade, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// GetByIDs 批量查询资产
|
||||
func (r *assetRepository) GetByIDs(assetIDs []int64) ([]*models.Asset, error) {
|
||||
if len(assetIDs) == 0 {
|
||||
|
||||
@ -42,6 +42,16 @@ func (r *MaterialRepository) FindByHash(hash string, starID int64) (*models.Mate
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
// FindByHashAndOssKey 根据 hash 和 oss_key 查找(双重去重)
|
||||
func (r *MaterialRepository) FindByHashAndOssKey(hash string, ossKey string, starID int64) (*models.Material, error) {
|
||||
var m models.Material
|
||||
err := r.db.Where("hash = ? AND oss_key = ? AND star_id = ? AND deleted_at IS NULL", hash, ossKey, starID).First(&m).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
// SoftDelete 软删除素材
|
||||
func (r *MaterialRepository) SoftDelete(materialID int64) error {
|
||||
now := time.Now().UnixMilli()
|
||||
|
||||
@ -490,6 +490,16 @@ func (s *assetService) GetAsset(req *pb.GetAssetRequest, userID, starID int64) (
|
||||
exhibitionExpireAt, _ = s.assetRepo.GetExhibitionExpireTime(asset.ID)
|
||||
}
|
||||
|
||||
// 6.5 从 asset_registry 表获取 grade
|
||||
grade, err := s.assetRepo.GetGradeByAssetID(asset.ID)
|
||||
if err != nil {
|
||||
logger.Logger.Warn("Failed to get grade, will return 0",
|
||||
zap.Int64("asset_id", asset.ID),
|
||||
zap.Error(err),
|
||||
)
|
||||
grade = 0
|
||||
}
|
||||
|
||||
// 7. 构建响应
|
||||
response := &pb.GetAssetResponse{
|
||||
Base: &pbCommon.BaseResponse{
|
||||
@ -497,7 +507,7 @@ func (s *assetService) GetAsset(req *pb.GetAssetRequest, userID, starID int64) (
|
||||
Message: "",
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
},
|
||||
Asset: ModelToProtoAssetDetail(asset, ownerNickname, isLiked, displayStatus, earnings, exhibitionExpireAt),
|
||||
Asset: ModelToProtoAssetDetail(asset, ownerNickname, isLiked, displayStatus, earnings, exhibitionExpireAt, grade),
|
||||
}
|
||||
|
||||
logger.Logger.Debug("Get asset successful",
|
||||
@ -646,7 +656,7 @@ func ModelToProtoAsset(asset *models.Asset) *pb.AssetListItem {
|
||||
}
|
||||
|
||||
// ModelToProtoAssetDetail 将数据库模型转换为Proto格式(Asset详情)
|
||||
func ModelToProtoAssetDetail(asset *models.Asset, ownerNickname string, isLiked bool, displayStatus int32, earnings int64, exhibitionExpireAt int64) *pb.Asset {
|
||||
func ModelToProtoAssetDetail(asset *models.Asset, ownerNickname string, isLiked bool, displayStatus int32, earnings int64, exhibitionExpireAt int64, grade int32) *pb.Asset {
|
||||
if asset == nil {
|
||||
return nil
|
||||
}
|
||||
@ -659,7 +669,7 @@ func ModelToProtoAssetDetail(asset *models.Asset, ownerNickname string, isLiked
|
||||
CoverUrl: asset.CoverURL,
|
||||
MaterialUrl: getStringValue(asset.MaterialURL),
|
||||
Description: getStringValue(asset.Description),
|
||||
Grade: getInt32Value(asset.Grade),
|
||||
Grade: grade,
|
||||
Tags: []string(asset.Tags),
|
||||
Visibility: asset.Visibility,
|
||||
Status: asset.Status,
|
||||
|
||||
@ -22,9 +22,10 @@ func NewMaterialService(materialRepo *repository.MaterialRepository, relationRep
|
||||
}
|
||||
}
|
||||
|
||||
// UploadMaterial 上传素材(含 hash 去重)
|
||||
// UploadMaterial 上传素材(含 hash + oss_key 双重去重)
|
||||
func (s *MaterialService) UploadMaterial(m *models.Material) (*models.Material, error) {
|
||||
existing, err := s.materialRepo.FindByHash(m.Hash, m.StarID)
|
||||
// 先按 hash + oss_key 双重判断,避免同一张图被错误复用
|
||||
existing, err := s.materialRepo.FindByHashAndOssKey(m.Hash, m.OssKey, m.StarID)
|
||||
if err == nil && existing != nil {
|
||||
return existing, nil
|
||||
}
|
||||
@ -32,6 +33,22 @@ func (s *MaterialService) UploadMaterial(m *models.Material) (*models.Material,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 如果没找到,再按单纯 hash 查找(兼容旧数据)
|
||||
existing, err = s.materialRepo.FindByHash(m.Hash, m.StarID)
|
||||
if err == nil && existing != nil {
|
||||
// 找到旧记录但 oss_key 不同,说明是不同文件但 hash 巧合相同,需要创建新记录
|
||||
if existing.OssKey != m.OssKey {
|
||||
if err := s.materialRepo.Create(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
return existing, nil
|
||||
}
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.materialRepo.Create(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -464,7 +464,7 @@ func (s *mintService) CreateMintOrder(req *pb.CreateMintOrderRequest, userID, st
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
},
|
||||
Order: ModelToProtoMintOrder(mintOrder),
|
||||
Asset: ModelToProtoAssetDetail(asset, ownerNickname, false, 0, 0, 0), // 新创建的资产,is_liked 为 false,display_status 默认为 0,earnings 和 exhibitionExpireAt 为 0
|
||||
Asset: ModelToProtoAssetDetail(asset, ownerNickname, false, 0, 0, 0, getInt32Value(asset.Grade)), // 新创建的资产,is_liked 为 false,display_status 默认为 0,earnings 和 exhibitionExpireAt 为 0,grade 从 asset.Grade 获取
|
||||
CostCrystal: capturedCostCrystal,
|
||||
BalanceAfter: newBalance,
|
||||
}
|
||||
@ -569,7 +569,7 @@ func (s *mintService) GetMintOrder(orderID string, userID, starID int64) (*pb.Ge
|
||||
// 由于是查询自己的订单,is_liked 设为 false(简化处理)
|
||||
// 获取 display_status
|
||||
displayStatus, _ := s.assetRepo.GetDisplayStatusByAssetID(asset.ID)
|
||||
assetProto = ModelToProtoAssetDetail(asset, ownerNickname, false, displayStatus, 0, 0) // 新创建的资产,earnings 和 exhibitionExpireAt 为 0
|
||||
assetProto = ModelToProtoAssetDetail(asset, ownerNickname, false, displayStatus, 0, 0, getInt32Value(asset.Grade)) // 新创建的资产,earnings 和 exhibitionExpireAt 为 0
|
||||
|
||||
// 如果 cover_url 存在,生成预签名 URL
|
||||
if assetProto.CoverUrl != "" {
|
||||
|
||||
@ -14,8 +14,11 @@ DECLARE
|
||||
asset_rec record;
|
||||
i int := 0;
|
||||
BEGIN
|
||||
-- 收集star_id=87的所有可用slot_id
|
||||
SELECT array_agg(slot_id ORDER BY slot_id) INTO slot_ids FROM booth_slots WHERE star_id = 87;
|
||||
-- 收集star_id=87的所有可用slot_id(排除已被用户1占用的)
|
||||
SELECT array_agg(slot_id ORDER BY slot_id) INTO slot_ids
|
||||
FROM booth_slots
|
||||
WHERE star_id = 87
|
||||
AND slot_id NOT IN (SELECT slot_id FROM exhibitions WHERE occupier_uid = 1 AND deleted_at IS NULL);
|
||||
|
||||
-- 为每个mock资产创建展示记录
|
||||
FOR asset_rec IN SELECT id, owner_uid FROM assets WHERE id >= 10000 ORDER BY id LOOP
|
||||
|
||||
@ -109,7 +109,7 @@ const cardRotateStyle = computed(() => {
|
||||
const x = tiltVisualX.value
|
||||
const rotateY = (x / 120) * 12
|
||||
return {
|
||||
// transform: `perspective(1000px) rotateY(${rotateY}deg) rotateX(0deg)`,
|
||||
transform: `perspective(1000px) rotateY(${rotateY}deg) rotateX(0deg)`,
|
||||
}
|
||||
})
|
||||
|
||||
@ -118,7 +118,7 @@ const shimmerStyle = computed(() => {
|
||||
const angle = 135 + (x / 120) * 60
|
||||
const a = Math.max(0, Math.min(0.35, Number(props.shimmerMidOpacity) || 0.1))
|
||||
return {
|
||||
// background: `linear-gradient(${angle}deg, transparent 30%, rgba(221,183,255,${a}) 50%, transparent 70%)`,
|
||||
background: `linear-gradient(${angle}deg, transparent 30%, rgba(221,183,255,${a}) 50%, transparent 70%)`,
|
||||
}
|
||||
})
|
||||
|
||||
@ -127,11 +127,11 @@ function getLayerStyle(layer) {
|
||||
const baseOpacity = t ? t.opacity : layer.opacity
|
||||
const x = t ? t.x : 0
|
||||
// 不在行内样式写 mix-blend-mode:部分小程序 / WebView 对非 normal 支持差,可能引发渲染异常
|
||||
// return {
|
||||
// opacity: baseOpacity,
|
||||
// transform: `translate3d(${x}px, 0, 0)`,
|
||||
// background: layer.background || 'transparent',
|
||||
// }
|
||||
return {
|
||||
opacity: baseOpacity,
|
||||
transform: `translate3d(${x}px, 0, 0)`,
|
||||
background: layer.background || 'transparent',
|
||||
}
|
||||
}
|
||||
|
||||
function getDotStyle(dot) {
|
||||
|
||||
@ -27,7 +27,20 @@
|
||||
"Push" : {}
|
||||
},
|
||||
"nativePlugins" : {
|
||||
"imengyu-UniAndroidGyro" : {}
|
||||
"imengyu-UniAndroidGyro" : {
|
||||
"__plugin_info__" : {
|
||||
"name" : "imengyu-UniAndroidGyro",
|
||||
"description" : "APP端陀螺仪数据采集",
|
||||
"platforms" : "Android,iOS",
|
||||
"url" : "",
|
||||
"android_package_name" : "",
|
||||
"ios_bundle_id" : "",
|
||||
"isCloud" : false,
|
||||
"bought" : -1,
|
||||
"pid" : "",
|
||||
"parameters" : {}
|
||||
}
|
||||
}
|
||||
},
|
||||
/* 应用发布信息 */
|
||||
"distribute" : {
|
||||
@ -51,7 +64,9 @@
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.INTERNET\"/>"
|
||||
"<uses-permission android:name=\"android.permission.INTERNET\"/>",
|
||||
"<uses-permission android:name=\"android.permission.BODY_SENSORS\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.sensor.gyroscope\"/>"
|
||||
],
|
||||
"abiFilters" : [ "armeabi-v7a", "arm64-v8a" ]
|
||||
},
|
||||
@ -80,33 +95,33 @@
|
||||
},
|
||||
"icons" : {
|
||||
"android" : {
|
||||
"hdpi" : "static/app-icons/72x72.png",
|
||||
"xhdpi" : "static/app-icons/96x96.png",
|
||||
"xxhdpi" : "static/app-icons/144x144.png",
|
||||
"xxxhdpi" : "static/app-icons/192x192.png"
|
||||
"hdpi" : "unpackage/res/icons/72x72.png",
|
||||
"xhdpi" : "unpackage/res/icons/96x96.png",
|
||||
"xxhdpi" : "unpackage/res/icons/144x144.png",
|
||||
"xxxhdpi" : "unpackage/res/icons/192x192.png"
|
||||
},
|
||||
"ios" : {
|
||||
"appstore" : "static/app-icons/1024x1024.png",
|
||||
"appstore" : "unpackage/res/icons/1024x1024.png",
|
||||
"ipad" : {
|
||||
"app" : "static/app-icons/76x76.png",
|
||||
"app@2x" : "static/app-icons/152x152.png",
|
||||
"notification" : "static/app-icons/20x20.png",
|
||||
"notification@2x" : "static/app-icons/40x40.png",
|
||||
"proapp@2x" : "static/app-icons/167x167.png",
|
||||
"settings" : "static/app-icons/29x29.png",
|
||||
"settings@2x" : "static/app-icons/58x58.png",
|
||||
"spotlight" : "static/app-icons/40x40.png",
|
||||
"spotlight@2x" : "static/app-icons/80x80.png"
|
||||
"app" : "unpackage/res/icons/76x76.png",
|
||||
"app@2x" : "unpackage/res/icons/152x152.png",
|
||||
"notification" : "unpackage/res/icons/20x20.png",
|
||||
"notification@2x" : "unpackage/res/icons/40x40.png",
|
||||
"proapp@2x" : "unpackage/res/icons/167x167.png",
|
||||
"settings" : "unpackage/res/icons/29x29.png",
|
||||
"settings@2x" : "unpackage/res/icons/58x58.png",
|
||||
"spotlight" : "unpackage/res/icons/40x40.png",
|
||||
"spotlight@2x" : "unpackage/res/icons/80x80.png"
|
||||
},
|
||||
"iphone" : {
|
||||
"app@2x" : "static/app-icons/120x120.png",
|
||||
"app@3x" : "static/app-icons/180x180.png",
|
||||
"notification@2x" : "static/app-icons/40x40.png",
|
||||
"notification@3x" : "static/app-icons/60x60.png",
|
||||
"settings@2x" : "static/app-icons/58x58.png",
|
||||
"settings@3x" : "static/app-icons/87x87.png",
|
||||
"spotlight@2x" : "static/app-icons/80x80.png",
|
||||
"spotlight@3x" : "static/app-icons/120x120.png"
|
||||
"app@2x" : "unpackage/res/icons/120x120.png",
|
||||
"app@3x" : "unpackage/res/icons/180x180.png",
|
||||
"notification@2x" : "unpackage/res/icons/40x40.png",
|
||||
"notification@3x" : "unpackage/res/icons/60x60.png",
|
||||
"settings@2x" : "unpackage/res/icons/58x58.png",
|
||||
"settings@3x" : "unpackage/res/icons/87x87.png",
|
||||
"spotlight@2x" : "unpackage/res/icons/80x80.png",
|
||||
"spotlight@3x" : "unpackage/res/icons/120x120.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,104 +10,10 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 铸爱:选图后确认铸造(光栅 / 镭射) -->
|
||||
<scroll-view v-if="craftConfirmMode" scroll-y class="content-scroll craft-confirm-scroll">
|
||||
<view class="content-wrapper craft-confirm-body">
|
||||
<view class="card-section craft-card-section">
|
||||
<view class="card-wrapper craft-card-wrapper">
|
||||
<image
|
||||
class="card-frame"
|
||||
src="/static/square/gerenzhongxincangpinkuang.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view v-if="isCraftLenticular" class="craft-lenticular-slot">
|
||||
<LenticularCard
|
||||
class="craft-lenticular-card"
|
||||
:layers="lenticularLayers"
|
||||
:transforms="layerTransforms"
|
||||
:gyro-source="gyroSourceLabel"
|
||||
:skip-built-in-touch="true"
|
||||
tilt-hint-text="晃动查看"
|
||||
:shimmer-mid-opacity="0.16"
|
||||
@simulate="simulate"
|
||||
/>
|
||||
</view>
|
||||
<image
|
||||
v-else
|
||||
class="card-image craft-card-image"
|
||||
:src="craftCoverUrl"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
<view class="card-meta-row">
|
||||
<view class="earnings-area">
|
||||
<image class="crystal-icon" src="/static/icon/crystal.png" mode="aspectFit" />
|
||||
<text class="earnings-text">{{ craftEarningsHint }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="info-row craft-info-row">
|
||||
<view class="info-col">
|
||||
<view class="info-item">
|
||||
<text class="info-label">分类</text>
|
||||
<text class="info-value">{{ craftCategoryLabel }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-col">
|
||||
<view class="info-item">
|
||||
<text class="info-label">创作者</text>
|
||||
<text class="info-value">{{ craftCreatorName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-col">
|
||||
<view class="info-item">
|
||||
<text class="info-label">铸爱时间</text>
|
||||
<text class="info-value">{{ craftMintDate }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="chain-section">
|
||||
<image class="chain-logo" src="/static/logo/APPLOGO.png" mode="aspectFit" />
|
||||
<view class="chain-left">
|
||||
<view class="chain-row">
|
||||
<text class="chain-label">数根名称</text>
|
||||
<view class="chain-value-wrap">
|
||||
<text class="chain-value">{{ craftAssetName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="chain-row">
|
||||
<text class="chain-label">数根发行方</text>
|
||||
<view class="chain-value-wrap">
|
||||
<text class="chain-value">TOPFANS</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="chain-row">
|
||||
<text class="chain-label">区块链编号</text>
|
||||
<view class="chain-value-wrap">
|
||||
<text class="chain-value">{{ craftBlockPlaceholder }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="chain-row">
|
||||
<text class="chain-label">交易哈希</text>
|
||||
<view class="chain-value-wrap">
|
||||
<text class="chain-value chain-hash">{{ craftHashPlaceholder }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="craft-mint-bar">
|
||||
<button class="craft-mint-btn" :disabled="craftMinting" @tap="handleCraftMint">
|
||||
{{ craftMinting ? '提交中…' : '确认铸造' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 加载中 -->
|
||||
<view v-else-if="loading" class="loading-wrapper">
|
||||
<view v-if="loading" class="loading-wrapper">
|
||||
<!-- 旋转光环 -->
|
||||
<view class="loading-ring-outer">
|
||||
<view class="loading-ring"></view>
|
||||
@ -137,44 +43,28 @@
|
||||
<image class="card-frame" src="/static/square/gerenzhongxincangpinkuang.png" mode="aspectFit">
|
||||
</image>
|
||||
<view v-if="isLenticularAsset" class="detail-lenticular-slot">
|
||||
<LenticularCard
|
||||
class="detail-lenticular-card"
|
||||
:layers="lenticularLayers"
|
||||
:transforms="layerTransforms"
|
||||
:gyro-source="gyroSourceLabel"
|
||||
:skip-built-in-touch="true"
|
||||
tilt-hint-text="倾斜手机查看光栅效果"
|
||||
:shimmer-mid-opacity="0.16"
|
||||
@simulate="simulate"
|
||||
/>
|
||||
<LenticularCard class="detail-lenticular-card" :layers="lenticularLayers"
|
||||
:transforms="layerTransforms" :gyro-source="gyroSourceLabel" :skip-built-in-touch="true"
|
||||
tilt-hint-text="倾斜手机查看光栅效果" :shimmer-mid-opacity="0.16" @simulate="simulate" />
|
||||
</view>
|
||||
<image v-else class="card-image" :src="coverUrl" mode="aspectFill"></image>
|
||||
<image class="card-badge" :src="gradeBadgeUrl" mode="aspectFit"></image>
|
||||
<!-- 贴纸叠加层 -->
|
||||
<image
|
||||
v-for="sticker in activeStickers"
|
||||
:key="sticker.id"
|
||||
class="card-sticker"
|
||||
:src="sticker.src"
|
||||
mode="aspectFit"
|
||||
:style="getStickerStyle(sticker)"
|
||||
/>
|
||||
<image v-for="sticker in activeStickers" :key="sticker.id" class="card-sticker"
|
||||
:src="sticker.src" mode="aspectFit" :style="getStickerStyle(sticker)" />
|
||||
</view>
|
||||
|
||||
<!-- 贴纸合成导出隐藏画布 -->
|
||||
<canvas
|
||||
v-if="activeStickers.length"
|
||||
canvas-id="stickerCompositCanvas"
|
||||
class="export-canvas"
|
||||
:style="{ position: 'fixed', left: '-9999px', top: '-9999px', width: '450px', height: '600px' }"
|
||||
/>
|
||||
<canvas v-if="activeStickers.length" canvas-id="stickerCompositCanvas" class="export-canvas"
|
||||
:style="{ position: 'fixed', left: '-9999px', top: '-9999px', width: '450px', height: '600px' }" />
|
||||
|
||||
<!-- 点赞 + 收益 + 倒计时行 -->
|
||||
<view class="card-meta-row">
|
||||
<!-- 点赞 -->
|
||||
<view v-if="assetData.display_status === 1" class="like-area" @tap="handleLike">
|
||||
<view class="like-area" @tap="handleLike">
|
||||
<image :src="isLiked ? '/static/icon/like-after.png' : '/static/icon/like-before.png'"
|
||||
class="like-icon" mode="aspectFit"></image>
|
||||
<text class="like-num">{{ likeCount }}</text>
|
||||
<text class="like-num">{{ likeCount || 0 }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 收益 -->
|
||||
@ -204,7 +94,8 @@
|
||||
<view class="info-item">
|
||||
<text class="info-label">创作者</text>
|
||||
<view class="info-nickname">
|
||||
<image v-if="userAvatarUrl" class="info-avatar" :src="userAvatarUrl" mode="aspectFill"></image>
|
||||
<image v-if="userAvatarUrl" class="info-avatar" :src="userAvatarUrl" mode="aspectFill">
|
||||
</image>
|
||||
<text class="info-value">{{ assetData.owner_nickname || '未知' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
@ -219,8 +110,8 @@
|
||||
|
||||
<!-- 创作者信息模块 -->
|
||||
<!-- <view v-if="likeCount > 0" class="creator-section" @tap="showLikeUsersModal = true"> -->
|
||||
<!-- 点赞用户头像区域 -->
|
||||
<!-- <view class="liked-users-area">
|
||||
<!-- 点赞用户头像区域 -->
|
||||
<!-- <view class="liked-users-area">
|
||||
<view v-for="(user, index) in likedUsers" :key="index" class="liked-user-avatar" :style="{
|
||||
transform: `translate(${user.ellipseX || 0}rpx, ${user.ellipseY || 0}rpx) scale(${user.size || 1})`,
|
||||
zIndex: index < 2 ? index + 1 : (index < 4 ? index + 3 : index + 1)
|
||||
@ -239,17 +130,17 @@
|
||||
<!-- </view> -->
|
||||
|
||||
<!-- 完整链上哈希显示遮罩层 -->
|
||||
<view v-if="showTxHash" class="txhash-mask" @tap="showTxHash = false">
|
||||
<view class="txhash-popup" @tap.stop>
|
||||
<view class="txhash-popup-header">
|
||||
<text class="txhash-popup-title">链上哈希</text>
|
||||
<view class="txhash-popup-close" @tap="showTxHash = false">
|
||||
<image class="close-icon" src="/static/icon/hide.png" mode="aspectFit"></image>
|
||||
<view v-if="showTxHash" class="txhash-mask" @tap="showTxHash = false">
|
||||
<view class="txhash-popup" @tap.stop>
|
||||
<view class="txhash-popup-header">
|
||||
<text class="txhash-popup-title">链上哈希</text>
|
||||
<view class="txhash-popup-close" @tap="showTxHash = false">
|
||||
<image class="close-icon" src="/static/icon/hide.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
</view>
|
||||
<text class="txhash-popup-content" selectable @longpress="copyHash">{{ displayTxHash }}</text>
|
||||
</view>
|
||||
<text class="txhash-popup-content" selectable @longpress="copyHash">{{ displayTxHash }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 链上数据 -->
|
||||
<view class="chain-section">
|
||||
@ -273,7 +164,9 @@
|
||||
<text class="chain-value">{{ showBlockNumber ? (assetData.block_number || '未知') :
|
||||
hiddenBlockNumber }}</text>
|
||||
<view class="toggle-btn" @tap="showBlockNumber = !showBlockNumber">
|
||||
<image class="toggle-icon" :src="showBlockNumber ? '/static/icon/show.png' : '/static/icon/hide.png'" mode="aspectFit"></image>
|
||||
<image class="toggle-icon"
|
||||
:src="showBlockNumber ? '/static/icon/show.png' : '/static/icon/hide.png'"
|
||||
mode="aspectFit"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -283,7 +176,9 @@
|
||||
<text class="chain-value chain-hash" @longpress="copyHash">{{ showTxHash ? displayTxHash
|
||||
: hiddenTxHash }}</text>
|
||||
<view class="toggle-btn" @tap="showTxHash = !showTxHash">
|
||||
<image class="toggle-icon" :src="showTxHash ? '/static/icon/show.png' : '/static/icon/hide.png'" mode="aspectFit"></image>
|
||||
<image class="toggle-icon"
|
||||
:src="showTxHash ? '/static/icon/show.png' : '/static/icon/hide.png'"
|
||||
mode="aspectFit"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -585,6 +480,19 @@ const hiddenBlockNumber = computed(() => {
|
||||
return `${str.substring(0, 6)}******`;
|
||||
});
|
||||
|
||||
// 等级徽章图片
|
||||
const gradeBadgeUrl = computed(() => {
|
||||
const grade = assetData.value.grade;
|
||||
const gradeMap = {
|
||||
1: '/static/starbookcontent/grade/Ndengji.png',
|
||||
2: '/static/starbookcontent/grade/Rdengji.png',
|
||||
3: '/static/starbookcontent/grade/SRdengji.png',
|
||||
4: '/static/starbookcontent/grade/SSRdengji.png',
|
||||
5: '/static/starbookcontent/grade/URengji.png',
|
||||
};
|
||||
return gradeMap[grade] || '/static/starbookcontent/grade/Ndengji.png';
|
||||
});
|
||||
|
||||
// 复制完整哈希
|
||||
const copyHash = () => {
|
||||
const hash = assetData.value.tx_hash;
|
||||
@ -864,14 +772,14 @@ onUnmounted(() => {
|
||||
.header-bar {
|
||||
position: fixed;
|
||||
/* top: 80rpx; */
|
||||
/* #ifdef APP-PLUS */
|
||||
/* App 通用(兜底) */
|
||||
top: 96rpx;
|
||||
/* #endif */
|
||||
/* #ifdef H5 */
|
||||
/* H5 使用普通高度 */
|
||||
top: 16rpx;
|
||||
/* #endif */
|
||||
/* #ifdef APP-PLUS */
|
||||
/* App 通用(兜底) */
|
||||
top: 96rpx;
|
||||
/* #endif */
|
||||
/* #ifdef H5 */
|
||||
/* H5 使用普通高度 */
|
||||
top: 16rpx;
|
||||
/* #endif */
|
||||
left: 32rpx;
|
||||
z-index: 100;
|
||||
}
|
||||
@ -1092,6 +1000,16 @@ onUnmounted(() => {
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
|
||||
.card-badge {
|
||||
position: absolute;
|
||||
top: -8rpx;
|
||||
left: -56rpx;
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
z-index: 4;
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
|
||||
.card-frame {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -1427,7 +1345,7 @@ onUnmounted(() => {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 64rpx;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.txhash-popup {
|
||||
@ -1538,9 +1456,9 @@ onUnmounted(() => {
|
||||
margin-bottom: 8rpx;
|
||||
/* transform: scale(1.2); */
|
||||
}
|
||||
.visit-text{
|
||||
|
||||
.visit-text {
|
||||
color: #FFFFFF;
|
||||
font-size: 16rpx;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@ -70,6 +70,7 @@ import NftCard from '../components/NftCard.vue';
|
||||
import LenticularCard from '@/components/lenticular/LenticularCard.vue';
|
||||
import { useLenticularCraftTiltPreview } from '@/composables/useLenticularCraftTiltPreview.js';
|
||||
import { buildLenticularLayersTwo } from '@/utils/castloveMintForm.js';
|
||||
import { getAssetDetailApi, getAssetMaterialsApi } from '@/utils/api.js';
|
||||
|
||||
// 藏品数据
|
||||
const nftData = ref({
|
||||
@ -103,26 +104,70 @@ const nftCardStyle = {
|
||||
};
|
||||
|
||||
// 页面加载时获取数据
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
// 从全局存储读取数据
|
||||
try {
|
||||
const tempNftData = uni.getStorageSync('temp_nft_data');
|
||||
if (tempNftData) {
|
||||
const data = JSON.parse(tempNftData);
|
||||
nftData.value = {
|
||||
image: data.image || '',
|
||||
name: data.name || '未命名藏品',
|
||||
event: data.event || '',
|
||||
remark: data.remark || '',
|
||||
materialType: data.materialType || '',
|
||||
is_lenticular: data.is_lenticular || false,
|
||||
bg_image: data.bg_image || '',
|
||||
};
|
||||
// 如果是光栅卡,构建图层
|
||||
if (data.is_lenticular && data.image && data.bg_image) {
|
||||
isLenticular.value = true
|
||||
lenticularLayers.value = buildLenticularLayersTwo(data.bg_image, data.image)
|
||||
scheduleTiltStart()
|
||||
|
||||
// 优先使用 API 获取最新数据
|
||||
if (data.asset_id) {
|
||||
try {
|
||||
const res = await getAssetDetailApi(data.asset_id);
|
||||
console.log('[success] asset detail API 返回:', res);
|
||||
if (res.code === 200 && res.data?.asset) {
|
||||
const asset = res.data.asset;
|
||||
console.log('[success] asset:', asset);
|
||||
|
||||
// 获取主图和背景图(通过 materials 接口)
|
||||
let imageUrl = asset.image || data.image || '';
|
||||
let bgImageUrl = asset.bg_image || data.bg_image || '';
|
||||
|
||||
try {
|
||||
const materialsRes = await getAssetMaterialsApi(data.asset_id);
|
||||
console.log('[success] materials API 返回:', materialsRes);
|
||||
if (materialsRes.code === 200 && materialsRes.data) {
|
||||
const materials = Array.isArray(materialsRes.data) ? materialsRes.data : materialsRes.data.materials || [];
|
||||
console.log('[success] materials 数组:', materials);
|
||||
// 找到 type 为主图和背景图的素材
|
||||
const mainMat = materials.find(m => m.material_type === 'main');
|
||||
const bgMat = materials.find(m => m.material_type === 'bg');
|
||||
console.log('[success] mainMat:', mainMat, 'bgMat:', bgMat);
|
||||
if (mainMat?.material_url_signed) imageUrl = mainMat.material_url_signed;
|
||||
if (bgMat?.material_url_signed) bgImageUrl = bgMat.material_url_signed;
|
||||
console.log('[success] imageUrl:', imageUrl, 'bgImageUrl:', bgImageUrl);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取素材详情失败:', e);
|
||||
}
|
||||
|
||||
nftData.value = {
|
||||
image: imageUrl,
|
||||
name: asset.name || data.name || '未命名藏品',
|
||||
event: asset.event || data.event || '',
|
||||
remark: asset.remark || data.remark || '',
|
||||
materialType: asset.material_type || data.materialType || '',
|
||||
is_lenticular: asset.is_lenticular || data.is_lenticular || false,
|
||||
bg_image: bgImageUrl,
|
||||
};
|
||||
console.log('[success] nftData 设置后:', nftData.value);
|
||||
// 如果是光栅卡,构建图层
|
||||
if ((asset.is_lenticular || data.is_lenticular) && bgImageUrl && imageUrl) {
|
||||
isLenticular.value = true
|
||||
lenticularLayers.value = buildLenticularLayersTwo(bgImageUrl, imageUrl)
|
||||
scheduleTiltStart()
|
||||
}
|
||||
} else {
|
||||
// API 返回异常,使用本地数据
|
||||
applyLocalNftData(data);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取藏品详情失败,使用本地数据:', e);
|
||||
applyLocalNftData(data);
|
||||
}
|
||||
} else {
|
||||
applyLocalNftData(data);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@ -134,6 +179,25 @@ onMounted(() => {
|
||||
}
|
||||
});
|
||||
|
||||
// 应用本地存储的数据
|
||||
function applyLocalNftData(data) {
|
||||
nftData.value = {
|
||||
image: data.image || '',
|
||||
name: data.name || '未命名藏品',
|
||||
event: data.event || '',
|
||||
remark: data.remark || '',
|
||||
materialType: data.materialType || '',
|
||||
is_lenticular: data.is_lenticular || false,
|
||||
bg_image: data.bg_image || '',
|
||||
};
|
||||
// 如果是光栅卡,构建图层
|
||||
if (data.is_lenticular && data.image && data.bg_image) {
|
||||
isLenticular.value = true
|
||||
lenticularLayers.value = buildLenticularLayersTwo(data.bg_image, data.image)
|
||||
scheduleTiltStart()
|
||||
}
|
||||
}
|
||||
|
||||
onUnload(() => {
|
||||
stopTiltPreview()
|
||||
try {
|
||||
|
||||
@ -319,7 +319,7 @@ const handleClaimReward = async (item, _index) => {
|
||||
const revenueRecord = records.find(r => r.asset_id === item.id);
|
||||
|
||||
if (!revenueRecord) {
|
||||
uni.showToast({ title: '暂无可领取收益', icon: 'none' });
|
||||
uni.showToast({ title: '一分钟延迟领取', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -330,7 +330,23 @@ const handleClaimReward = async (item, _index) => {
|
||||
|
||||
// 更新全局余额
|
||||
if (claimRes?.data?.total_balance !== undefined) {
|
||||
uni.$emit('balanceUpdated', { crystal_balance: claimRes.data.total_balance });
|
||||
const userStr = uni.getStorageSync('user')
|
||||
if (userStr) {
|
||||
// 确保正确解析用户对象
|
||||
let user
|
||||
if (typeof userStr === 'string') {
|
||||
user = JSON.parse(userStr)
|
||||
} else {
|
||||
// 如果已经是对象,创建一个新副本避免引用问题
|
||||
user = { ...userStr }
|
||||
}
|
||||
// 更新余额,确保是数字类型
|
||||
user.crystal_balance = Number(claimRes.data.total_balance) || 0
|
||||
|
||||
// 保存回存储,确保是字符串格式
|
||||
uni.setStorageSync('user', JSON.stringify(user))
|
||||
uni.$emit('balanceUpdated', { crystal_balance: user.crystal_balance })
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新列表
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<view v-if="visible" class="modal-wrapper" @touchmove.stop.prevent="handlePreventMove" @click.stop>
|
||||
<transition name="fade">
|
||||
<view v-if="visible" class="modal-mask" @touchstart.stop="handleMaskTouchStart" @touchmove.stop.prevent="handlePreventMove" @click.stop>
|
||||
<view v-if="visible" class="modal-mask" @touchstart.stop="handleMaskTouchStart"
|
||||
@touchmove.stop.prevent="handlePreventMove" @click.stop>
|
||||
</view>
|
||||
</transition>
|
||||
|
||||
@ -18,10 +19,11 @@
|
||||
<!-- 顶部区域:返回按钮和Tab -->
|
||||
<view class="top-bar">
|
||||
<!-- 返回按钮 -->
|
||||
<view class="back-button" @touchstart.stop="handleCloseTouchStart" @touchend.stop="handleCloseTouchEnd" @click="handleCloseClick">
|
||||
<view class="back-button" @touchstart.stop="handleCloseTouchStart"
|
||||
@touchend.stop="handleCloseTouchEnd" @click="handleCloseClick">
|
||||
<image class="back-icon" src="/static/icon/back.png" mode="aspectFit" />
|
||||
</view>
|
||||
|
||||
|
||||
<!-- Tab 切换 -->
|
||||
<view class="tab-bar">
|
||||
<view class="tab-item active">
|
||||
@ -56,43 +58,38 @@
|
||||
<view v-for="task in sortedTasks" :key="task.task_key" class="task-item">
|
||||
<view class="task-info">
|
||||
<text class="task-name">{{ task.name }}</text>
|
||||
<text class="task-progress" v-if="task.current_count !== undefined">({{ task.current_count }}/{{ task.target_count }})</text>
|
||||
<text class="task-progress" v-if="task.current_count !== undefined">({{
|
||||
task.current_count }}/{{ task.target_count }})</text>
|
||||
</view>
|
||||
|
||||
<view class="task-right">
|
||||
<!-- 礼盒图标 - 点击展开/收起 -->
|
||||
<view class="reward-gift" @click.stop="toggleReward(task.task_key)">
|
||||
<image
|
||||
class="gift-icon"
|
||||
:class="{ 'gift-open': expandedTaskKey === task.task_key }"
|
||||
:src="expandedTaskKey === task.task_key ? '/static/nft/lihe_kaiqi.png' : '/static/nft/lihe.png'"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
|
||||
<image class="gift-icon" :class="{ 'gift-open': expandedTaskKey === task.task_key }"
|
||||
:src="expandedTaskKey === task.task_key ? '/static/nft/lihe_kaiqi.png' : '/static/nft/lihe.png'"
|
||||
mode="aspectFit"></image>
|
||||
|
||||
<!-- 奖励详情弹出 -->
|
||||
<view v-if="expandedTaskKey === task.task_key" class="reward-popup">
|
||||
<view class="reward-item">
|
||||
<image class="reward-icon-img" src="/static/icon/crystal.png" mode="aspectFit"></image>
|
||||
<image class="reward-icon-img" src="/static/icon/crystal.png"
|
||||
mode="aspectFit"></image>
|
||||
<text class="reward-value">{{ task.crystal_reward }}</text>
|
||||
</view>
|
||||
<view class="reward-item">
|
||||
<!-- <view class="reward-item">
|
||||
<image class="reward-icon-img" src="/static/nft/jingyanzhi.png" mode="aspectFit"></image>
|
||||
<text class="reward-value">{{ task.exp_reward }}</text>
|
||||
</view>
|
||||
<view class="reward-item">
|
||||
<image class="reward-icon-img" src="/static/nft/huoyuezhitubiao.png" mode="aspectFit"></image>
|
||||
<text class="reward-value">{{ task.activity_reward || 10 }}</text>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
<!-- 状态按钮/标签 -->
|
||||
<button
|
||||
class="claim-btn"
|
||||
:class="getStatusClass(task.status)"
|
||||
:loading="claimingTask === task.task_key"
|
||||
:disabled="!task.can_claim"
|
||||
@click="task.can_claim && handleClaim(task)"
|
||||
>
|
||||
<button class="claim-btn" :class="getStatusClass(task.status)"
|
||||
:loading="claimingTask === task.task_key" :disabled="!task.can_claim"
|
||||
@click="task.can_claim && handleClaim(task)">
|
||||
{{ task.can_claim ? '待领取' : getStatusText(task.status) }}
|
||||
</button>
|
||||
</view>
|
||||
@ -104,25 +101,15 @@
|
||||
<!-- 进度条 -->
|
||||
<view class="progress-section">
|
||||
<view class="progress-bar">
|
||||
<view
|
||||
v-for="(milestone, index) in milestones"
|
||||
:key="index"
|
||||
class="progress-node"
|
||||
:class="{ 'active': index < completedCount }"
|
||||
>
|
||||
<view v-for="(milestone, index) in milestones" :key="index" class="progress-node"
|
||||
:class="{ 'active': index < completedCount }">
|
||||
<!-- 奖励图标 -->
|
||||
<image
|
||||
class="milestone-icon"
|
||||
:src="[20, 40, 80].includes(milestone.value) ? '/static/icon/crystal.png' : '/static/nft/lihe.png'"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<image class="milestone-icon"
|
||||
:src="[20, 40, 80].includes(milestone.value) ? '/static/icon/crystal.png' : '/static/nft/lihe.png'"
|
||||
mode="aspectFit"></image>
|
||||
<!-- 进度节点 -->
|
||||
<image
|
||||
class="node-circle"
|
||||
:class="{ 'node-inactive': index >= completedCount }"
|
||||
src="/static/nft/huoyuezhi_jiedian.png"
|
||||
mode="aspectFit"
|
||||
></image>
|
||||
<image class="node-circle" :class="{ 'node-inactive': index >= completedCount }"
|
||||
src="/static/nft/huoyuezhi_jiedian.png" mode="aspectFit"></image>
|
||||
<text class="node-label">{{ milestone.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
@ -130,13 +117,8 @@
|
||||
|
||||
<!-- 一键领取按钮 -->
|
||||
<view class="claim-all-bar">
|
||||
<button
|
||||
class="claim-all-btn"
|
||||
:class="{ 'disabled': !hasClaimableTasks }"
|
||||
:loading="claimingAll"
|
||||
:disabled="!hasClaimableTasks"
|
||||
@click="handleClaimAll"
|
||||
>
|
||||
<button class="claim-all-btn" :class="{ 'disabled': !hasClaimableTasks }"
|
||||
:loading="claimingAll" :disabled="!hasClaimableTasks" @click="handleClaimAll">
|
||||
一键领取 ({{ claimableCount }})
|
||||
</button>
|
||||
</view>
|
||||
@ -223,7 +205,7 @@ function lockBodyScroll() {
|
||||
scrollTop = res.scrollTop || 0
|
||||
}
|
||||
}).exec()
|
||||
|
||||
|
||||
// #ifdef H5
|
||||
// H5 平台:锁定 body 滚动
|
||||
const body = document.body
|
||||
@ -233,7 +215,7 @@ function lockBodyScroll() {
|
||||
body.style.top = `-${scrollTop}px`
|
||||
body.style.width = '100%'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef MP
|
||||
// 小程序平台:禁用页面滚动
|
||||
uni.pageScrollTo({
|
||||
@ -241,7 +223,7 @@ function lockBodyScroll() {
|
||||
duration: 0
|
||||
})
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
// APP 平台:禁用页面滚动
|
||||
const currentWebview = plus.webview.currentWebview()
|
||||
@ -273,7 +255,7 @@ function unlockBodyScroll() {
|
||||
// 恢复滚动位置
|
||||
window.scrollTo(0, scrollTop)
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef MP
|
||||
// 小程序平台:恢复页面滚动
|
||||
uni.pageScrollTo({
|
||||
@ -281,7 +263,7 @@ function unlockBodyScroll() {
|
||||
duration: 0
|
||||
})
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
// APP 平台:恢复页面滚动
|
||||
const currentWebview = plus.webview.currentWebview()
|
||||
@ -359,9 +341,24 @@ async function handleClaim(task) {
|
||||
tasks.value[index].status = 'claimed'
|
||||
tasks.value[index].can_claim = false
|
||||
}
|
||||
const userStr = uni.getStorageSync('user')
|
||||
if (userStr) {
|
||||
// 确保正确解析用户对象
|
||||
let user
|
||||
if (typeof userStr === 'string') {
|
||||
user = JSON.parse(userStr)
|
||||
} else {
|
||||
// 如果已经是对象,创建一个新副本避免引用问题
|
||||
user = { ...userStr }
|
||||
}
|
||||
emit('updated')
|
||||
// 更新余额,确保是数字类型
|
||||
user.crystal_balance = Number(res.data?.crystal_balance) || 0
|
||||
|
||||
emit('updated')
|
||||
uni.$emit('balanceUpdated', { crystal_balance: res.data?.crystal_balance, experience: res.data?.experience })
|
||||
// 保存回存储,确保是字符串格式
|
||||
uni.setStorageSync('user', JSON.stringify(user))
|
||||
uni.$emit('balanceUpdated', { crystal_balance: user.crystal_balance })
|
||||
}
|
||||
uni.showToast({ title: '领取成功', icon: 'success' })
|
||||
} catch (err) {
|
||||
console.error('handleClaim error:', err)
|
||||
@ -386,7 +383,24 @@ async function handleClaimAll() {
|
||||
})
|
||||
|
||||
emit('updated')
|
||||
uni.$emit('balanceUpdated', { crystal_balance: res.data?.crystal_balance, experience: res.data?.experience })
|
||||
const userStr = uni.getStorageSync('user')
|
||||
if (userStr) {
|
||||
// 确保正确解析用户对象
|
||||
let user
|
||||
if (typeof userStr === 'string') {
|
||||
user = JSON.parse(userStr)
|
||||
} else {
|
||||
// 如果已经是对象,创建一个新副本避免引用问题
|
||||
user = { ...userStr }
|
||||
}
|
||||
emit('updated')
|
||||
// 更新余额,确保是数字类型
|
||||
user.crystal_balance = Number(res.data?.crystal_balance) || 0
|
||||
|
||||
// 保存回存储,确保是字符串格式
|
||||
uni.setStorageSync('user', JSON.stringify(user))
|
||||
uni.$emit('balanceUpdated', { crystal_balance: user.crystal_balance })
|
||||
}
|
||||
uni.showToast({ title: '领取成功', icon: 'success' })
|
||||
} catch (err) {
|
||||
console.error('handleClaimAll error:', err)
|
||||
@ -617,7 +631,9 @@ const handleCloseClick = (e) => {
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-text,
|
||||
@ -747,6 +763,7 @@ const handleCloseClick = (e) => {
|
||||
opacity: 0;
|
||||
transform: translateY(-50%) scale(0.8);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%) scale(1);
|
||||
@ -811,21 +828,25 @@ const handleCloseClick = (e) => {
|
||||
height: 50rpx;
|
||||
border-radius: 25rpx;
|
||||
/* 渐变:左浅橙粉 → 右柔粉红 */
|
||||
background: linear-gradient(to bottom right,
|
||||
#F0E4B1 0%, /* 左:浅橙粉 */
|
||||
#F08399 50%,
|
||||
#B94E73 100% /* 右:柔粉红 */
|
||||
);
|
||||
background: linear-gradient(to bottom right,
|
||||
#F0E4B1 0%,
|
||||
/* 左:浅橙粉 */
|
||||
#F08399 50%,
|
||||
#B94E73 100%
|
||||
/* 右:柔粉红 */
|
||||
);
|
||||
|
||||
/* 立体感核心:多层阴影 + 内阴影模拟凸起 */
|
||||
box-shadow:
|
||||
/* 外层投影 - 让按钮浮起 */
|
||||
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
|
||||
0 2rpx 6rpx rgba(255, 143, 158, 0.15),
|
||||
/* 立体感核心:多层阴影 + 内阴影模拟凸起 */
|
||||
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); /* 底部暗部 */
|
||||
/* 内阴影 - 模拟顶部受光 + 底部凹陷 */
|
||||
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4),
|
||||
/* 顶部高光 */
|
||||
inset 0 -2rpx 4rpx rgba(0, 0, 0, 0.05);
|
||||
/* 底部暗部 */
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.5);
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
|
||||
BIN
frontend/static/starbookcontent/grade/Ndengji.png
Normal file
BIN
frontend/static/starbookcontent/grade/Ndengji.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
frontend/static/starbookcontent/grade/Rdengji.png
Normal file
BIN
frontend/static/starbookcontent/grade/Rdengji.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
frontend/static/starbookcontent/grade/SRdengji.png
Normal file
BIN
frontend/static/starbookcontent/grade/SRdengji.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
frontend/static/starbookcontent/grade/SSRdengji.png
Normal file
BIN
frontend/static/starbookcontent/grade/SSRdengji.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
frontend/static/starbookcontent/grade/URengji.png
Normal file
BIN
frontend/static/starbookcontent/grade/URengji.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Loading…
Reference in New Issue
Block a user