feat: 展览选择藏品进行分类功能
This commit is contained in:
parent
5931040057
commit
e5a09194ad
@ -148,24 +148,89 @@ func ConvertGetMyAssetsResponse(pbResp *pbAsset.GetMyAssetsResponse) *GetMyAsset
|
||||
return nil
|
||||
}
|
||||
|
||||
dto := &GetMyAssetsResponseDTO{
|
||||
Total: pbResp.Total,
|
||||
Page: pbResp.Page,
|
||||
PageSize: pbResp.PageSize,
|
||||
HasMore: pbResp.HasMore,
|
||||
Items: make([]AssetListItemDTO, 0, len(pbResp.Items)),
|
||||
dto := &GetMyAssetsResponseDTO{}
|
||||
|
||||
if pbResp.Data != nil {
|
||||
dto.Data = &AssetListDataDTO{
|
||||
Total: pbResp.Data.Total,
|
||||
Page: pbResp.Data.Page,
|
||||
PageSize: pbResp.Data.PageSize,
|
||||
HasMore: pbResp.Data.HasMore,
|
||||
Groups: make([]AssetGroupDTO, 0, len(pbResp.Data.Groups)),
|
||||
}
|
||||
|
||||
// 转换列表项
|
||||
for _, item := range pbResp.Items {
|
||||
if item != nil {
|
||||
dto.Items = append(dto.Items, ConvertAssetListItem(item))
|
||||
// 转换分组
|
||||
for _, group := range pbResp.Data.Groups {
|
||||
if group != nil {
|
||||
dto.Data.Groups = append(dto.Data.Groups, ConvertAssetGroup(group))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dto
|
||||
}
|
||||
|
||||
// ConvertAssetGroup 转换资产分组
|
||||
func ConvertAssetGroup(pbGroup *pbAsset.AssetGroup) AssetGroupDTO {
|
||||
dto := AssetGroupDTO{
|
||||
Type: pbGroup.Type,
|
||||
Category: pbGroup.Category,
|
||||
CategoryName: pbGroup.CategoryName,
|
||||
TotalCount: pbGroup.TotalCount,
|
||||
HasMore: pbGroup.HasMore,
|
||||
Grades: make([]GradeSectionDTO, 0, len(pbGroup.Grades)),
|
||||
Items: make([]AssetItemDTO, 0, len(pbGroup.Items)),
|
||||
}
|
||||
|
||||
// 转换 grades
|
||||
for _, grade := range pbGroup.Grades {
|
||||
if grade != nil {
|
||||
dto.Grades = append(dto.Grades, ConvertGradeSection(grade))
|
||||
}
|
||||
}
|
||||
|
||||
// 转换 items
|
||||
for _, item := range pbGroup.Items {
|
||||
if item != nil {
|
||||
dto.Items = append(dto.Items, ConvertAssetItem(item))
|
||||
}
|
||||
}
|
||||
|
||||
return dto
|
||||
}
|
||||
|
||||
// ConvertGradeSection 转换等级分组
|
||||
func ConvertGradeSection(pbGrade *pbAsset.GradeSection) GradeSectionDTO {
|
||||
dto := GradeSectionDTO{
|
||||
Grade: pbGrade.Grade,
|
||||
TotalCount: pbGrade.TotalCount,
|
||||
HasMore: pbGrade.HasMore,
|
||||
Items: make([]AssetItemDTO, 0, len(pbGrade.Items)),
|
||||
}
|
||||
|
||||
for _, item := range pbGrade.Items {
|
||||
if item != nil {
|
||||
dto.Items = append(dto.Items, ConvertAssetItem(item))
|
||||
}
|
||||
}
|
||||
|
||||
return dto
|
||||
}
|
||||
|
||||
// ConvertAssetItem 转换资产项
|
||||
func ConvertAssetItem(pbItem *pbAsset.AssetItem) AssetItemDTO {
|
||||
return AssetItemDTO{
|
||||
AssetID: pbItem.AssetId,
|
||||
Name: pbItem.Name,
|
||||
CoverURLSigned: pbItem.CoverUrlSigned,
|
||||
LikeCount: pbItem.LikeCount,
|
||||
CreatedAt: pbItem.CreatedAt,
|
||||
Category: pbItem.Category,
|
||||
Grade: pbItem.Grade,
|
||||
DisplayStatus: pbItem.DisplayStatus,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertAssetListItem 转换资产列表项
|
||||
func ConvertAssetListItem(pbItem *pbAsset.AssetListItem) AssetListItemDTO {
|
||||
dto := AssetListItemDTO{
|
||||
|
||||
@ -91,13 +91,49 @@ type OwnerInfoDTO struct {
|
||||
|
||||
// GetMyAssetsResponseDTO 获取我的藏品列表响应
|
||||
type GetMyAssetsResponseDTO struct {
|
||||
Items []AssetListItemDTO `json:"items"` // 资产列表
|
||||
Data *AssetListDataDTO `json:"data"` // 分组后的藏品数据
|
||||
}
|
||||
|
||||
// AssetListDataDTO 藏品列表数据(与星册home一致的结构)
|
||||
type AssetListDataDTO struct {
|
||||
Groups []AssetGroupDTO `json:"groups"` // 分组列表
|
||||
Total int64 `json:"total"` // 总数
|
||||
Page int32 `json:"page"` // 当前页码
|
||||
PageSize int32 `json:"page_size"` // 每页数量
|
||||
HasMore bool `json:"has_more"` // 是否有更多
|
||||
}
|
||||
|
||||
// AssetGroupDTO 资产分组(与星册home一致)
|
||||
type AssetGroupDTO struct {
|
||||
Type string `json:"type"` // 'regular' / 'collection' / 'activity'
|
||||
Category string `json:"category"` // 'castlove'(regular) / collection_category / activity_type
|
||||
CategoryName string `json:"category_name"` // 分组名称
|
||||
Grades []GradeSectionDTO `json:"grades"` // 仅 regular 时有效
|
||||
Items []AssetItemDTO `json:"items"` // collection / activity 时有效
|
||||
TotalCount int32 `json:"total_count"` // 总数
|
||||
HasMore bool `json:"has_more"` // 是否有更多
|
||||
}
|
||||
|
||||
// GradeSectionDTO 等级分组(仅 regular 类型使用)
|
||||
type GradeSectionDTO struct {
|
||||
Grade int32 `json:"grade"` // 等级:1/2/3/4/5...
|
||||
Items []AssetItemDTO `json:"items"` // 藏品列表
|
||||
TotalCount int32 `json:"total_count"` // 该等级总数
|
||||
HasMore bool `json:"has_more"` // 是否有更多
|
||||
}
|
||||
|
||||
// AssetItemDTO 资产项(与星册home一致)
|
||||
type AssetItemDTO struct {
|
||||
AssetID int64 `json:"asset_id"` // 资产ID
|
||||
Name string `json:"name"` // 藏品名称
|
||||
CoverURLSigned string `json:"cover_url_signed"` // 预签名封面URL
|
||||
LikeCount int32 `json:"like_count"` // 点赞数
|
||||
CreatedAt int64 `json:"created_at"` // 创建时间
|
||||
Category string `json:"category"` // 分类
|
||||
Grade int32 `json:"grade"` // 等级(仅 regular 时有效)
|
||||
DisplayStatus int32 `json:"display_status"` // 展示状态:0=待展示, 1=已展示
|
||||
}
|
||||
|
||||
// AssetListItemDTO 资产列表项(简化版,用于列表展示)
|
||||
type AssetListItemDTO struct {
|
||||
AssetID int64 `json:"asset_id"` // 资产ID
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -125,14 +125,50 @@ message GetMyAssetsRequest {
|
||||
// 获取我的藏品列表响应
|
||||
message GetMyAssetsResponse {
|
||||
topfans.common.BaseResponse base = 1;
|
||||
repeated AssetListItem items = 2; // 资产列表
|
||||
int64 total = 3; // 总数
|
||||
int32 page = 4; // 当前页码
|
||||
int32 page_size = 5; // 每页数量
|
||||
bool has_more = 6; // 是否有更多
|
||||
AssetListData data = 2; // 分组后的藏品数据
|
||||
}
|
||||
|
||||
// 资产列表项(简化版,用于列表展示)
|
||||
// 藏品列表数据(与星册home一致的结构)
|
||||
message AssetListData {
|
||||
repeated AssetGroup groups = 1; // 分组列表
|
||||
int64 total = 2; // 总数
|
||||
int32 page = 3; // 当前页码
|
||||
int32 page_size = 4; // 每页数量
|
||||
bool has_more = 5; // 是否有更多
|
||||
}
|
||||
|
||||
// 资产分组(与星册home一致)
|
||||
message AssetGroup {
|
||||
string type = 1; // 'regular' / 'collection' / 'activity'
|
||||
string category = 2; // 'castlove'(regular) / collection_category / activity_type
|
||||
string category_name = 3;
|
||||
repeated GradeSection grades = 4; // 仅 regular 时有效
|
||||
repeated AssetItem items = 5; // collection / activity 时有效
|
||||
int32 total_count = 6;
|
||||
bool has_more = 7;
|
||||
}
|
||||
|
||||
// 等级分组(仅 regular 类型使用)
|
||||
message GradeSection {
|
||||
int32 grade = 1; // 等级:1/2/3/4/5...
|
||||
repeated AssetItem items = 2;
|
||||
int32 total_count = 3;
|
||||
bool has_more = 4;
|
||||
}
|
||||
|
||||
// 资产项(与星册home一致)
|
||||
message AssetItem {
|
||||
int64 asset_id = 1;
|
||||
string name = 2;
|
||||
string cover_url_signed = 3; // 预签名封面URL
|
||||
int32 like_count = 4;
|
||||
int64 created_at = 5;
|
||||
string category = 6; // regular: 'castlove' / collection: category / activity: activity_type
|
||||
int32 grade = 7; // 仅 regular 时有效(1/2/3...),其他类型为 0
|
||||
int32 display_status = 8; // 展示状态:0=待展示, 1=已展示
|
||||
}
|
||||
|
||||
// 资产列表项(简化版,用于列表展示 - 保留兼容)
|
||||
message AssetListItem {
|
||||
int64 asset_id = 1; // 资产ID
|
||||
string name = 2; // 藏品名称
|
||||
|
||||
@ -129,8 +129,8 @@ func main() {
|
||||
logger.Logger.Info("User Service RPC client initialized")
|
||||
|
||||
// 创建 Service 层实例
|
||||
assetService := service.NewAssetService(assetRepo, mintOrderRepo, assetLikeRepo, userClient, database.GetDB())
|
||||
registryRepo := starbookRepo.NewAssetRegistryRepository(database.GetDB())
|
||||
assetService := service.NewAssetService(assetRepo, mintOrderRepo, assetLikeRepo, userClient, database.GetDB(), registryRepo)
|
||||
mintService := service.NewMintService(assetRepo, mintOrderRepo, userClient, database.GetDB(), config.GlobalAssetConfig, registryRepo)
|
||||
assetLikeService := service.NewAssetLikeService(assetRepo, assetLikeRepo, database.GetDB())
|
||||
rankingService := service.NewRankingService(rankingRepo, userClient)
|
||||
|
||||
@ -212,7 +212,7 @@ func (p *AssetProvider) GetMyAssets(ctx context.Context, req *pb.GetMyAssetsRequ
|
||||
logger.Logger.Debug("GetMyAssets successful",
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Int64("star_id", starID),
|
||||
zap.Int("count", len(resp.Items)),
|
||||
zap.Int("count", len(resp.Data.Groups)),
|
||||
)
|
||||
|
||||
return resp, nil
|
||||
|
||||
@ -160,23 +160,133 @@
|
||||
<text class="modal-title">选择要展出的藏品</text>
|
||||
</view>
|
||||
|
||||
<!-- 藏品网格 -->
|
||||
<view class="modal-nft-grid-container">
|
||||
<!-- 类型Tab -->
|
||||
<view class="modal-type-tabs">
|
||||
<view
|
||||
v-for="(item, index) in myAssetsList"
|
||||
:key="item.asset_id || index"
|
||||
class="modal-nft-grid-item"
|
||||
class="modal-tab-item"
|
||||
:class="{ active: modalCurrentType === 'regular' }"
|
||||
@click="switchModalType('regular')"
|
||||
>
|
||||
<NftCard
|
||||
:cover-image="item.image || '/static/nft/collection.png'"
|
||||
:width="cardSize"
|
||||
:height="cardSize"
|
||||
:custom-style="cardCustomStyle"
|
||||
@click="handleAssetSelect(item)"
|
||||
<text>原创</text>
|
||||
</view>
|
||||
<view
|
||||
class="modal-tab-item"
|
||||
:class="{ active: modalCurrentType === 'collection' }"
|
||||
@click="switchModalType('collection')"
|
||||
>
|
||||
<text>典藏</text>
|
||||
</view>
|
||||
<view
|
||||
class="modal-tab-item"
|
||||
:class="{ active: modalCurrentType === 'activity' }"
|
||||
@click="switchModalType('activity')"
|
||||
>
|
||||
<text>活动</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载中 -->
|
||||
<view v-if="modalLoading" class="modal-loading-container">
|
||||
<text class="modal-loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-else-if="!modalHasData" class="modal-empty-container">
|
||||
<text class="modal-empty-text">暂无藏品</text>
|
||||
</view>
|
||||
|
||||
<!-- 藏品列表 - 与星册一致的水平滚动布局 -->
|
||||
<scroll-view v-else class="modal-nft-scroll-view" scroll-y :show-scrollbar="false">
|
||||
<view class="modal-nft-list-container">
|
||||
<!-- 原创藏品:按 grade 分组 -->
|
||||
<template v-if="modalCurrentType === 'regular'">
|
||||
<view v-for="gradeItem in modalRegularGrades" :key="gradeItem.grade" class="modal-grade-section">
|
||||
<view class="modal-group-header">
|
||||
<text class="modal-group-title">{{ formatModalGrade(gradeItem.grade) }}</text>
|
||||
</view>
|
||||
<scroll-view class="modal-nft-row" scroll-x :show-scrollbar="false" :enable-flex="true">
|
||||
<view class="modal-nft-row-content">
|
||||
<view
|
||||
v-for="item in gradeItem.items"
|
||||
:key="item.asset_id"
|
||||
class="modal-nft-grid-item"
|
||||
@click.stop="handleAssetSelect(item)"
|
||||
>
|
||||
<image
|
||||
class="modal-nft-image"
|
||||
:src="item.coverUrl || '/static/nft/collection.png'"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="modal-status-badge" :class="item.display_status === 1 ? 'modal-status-badge-active' : 'modal-status-badge-pending'">
|
||||
<text class="modal-status-text">{{ item.display_status === 1 ? '已展示' : '待展示' }}</text>
|
||||
</view>
|
||||
<view class="modal-nft-info">
|
||||
<text class="modal-nft-name">{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 典藏藏品:直接列表 -->
|
||||
<template v-else-if="modalCurrentType === 'collection'">
|
||||
<view class="modal-grade-section">
|
||||
<scroll-view class="modal-nft-row" scroll-x :show-scrollbar="false" :enable-flex="true">
|
||||
<view class="modal-nft-row-content">
|
||||
<view
|
||||
v-for="item in modalCollectionItems"
|
||||
:key="item.asset_id"
|
||||
class="modal-nft-grid-item"
|
||||
@click.stop="handleAssetSelect(item)"
|
||||
>
|
||||
<image
|
||||
class="modal-nft-image"
|
||||
:src="item.coverUrl || '/static/nft/collection.png'"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="modal-status-badge" :class="item.display_status === 1 ? 'modal-status-badge-active' : 'modal-status-badge-pending'">
|
||||
<text class="modal-status-text">{{ item.display_status === 1 ? '已展示' : '待展示' }}</text>
|
||||
</view>
|
||||
<view class="modal-nft-info">
|
||||
<text class="modal-nft-name">{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 活动藏品:直接列表 -->
|
||||
<template v-else-if="modalCurrentType === 'activity'">
|
||||
<view class="modal-grade-section">
|
||||
<scroll-view class="modal-nft-row" scroll-x :show-scrollbar="false" :enable-flex="true">
|
||||
<view class="modal-nft-row-content">
|
||||
<view
|
||||
v-for="item in modalActivityItems"
|
||||
:key="item.asset_id"
|
||||
class="modal-nft-grid-item"
|
||||
@click.stop="handleAssetSelect(item)"
|
||||
>
|
||||
<image
|
||||
class="modal-nft-image"
|
||||
:src="item.coverUrl || '/static/nft/collection.png'"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="modal-status-badge" :class="item.display_status === 1 ? 'modal-status-badge-active' : 'modal-status-badge-pending'">
|
||||
<text class="modal-status-text">{{ item.display_status === 1 ? '已展示' : '待展示' }}</text>
|
||||
</view>
|
||||
<view class="modal-nft-info">
|
||||
<text class="modal-nft-name">{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -332,7 +442,54 @@ export default {
|
||||
const selectedSlotIndex = ref(null);
|
||||
const showPlaceConfirmModal = ref(false);
|
||||
const selectedAssetForPlace = ref(null);
|
||||
const myAssetsList = ref([]);
|
||||
const myAssetsGroups = ref([]); // 分组后的藏品数据
|
||||
const modalLoading = ref(false); // 弹窗内藏品加载状态
|
||||
const modalCurrentType = ref('regular'); // 当前选中的类型
|
||||
|
||||
// 计算当前类型是否有数据
|
||||
const modalHasData = computed(() => {
|
||||
const groups = myAssetsGroups.value;
|
||||
if (modalCurrentType.value === 'regular') {
|
||||
const group = groups.find(g => g.type === 'regular');
|
||||
return group && group.grades && group.grades.some(g => g.items && g.items.length > 0);
|
||||
} else if (modalCurrentType.value === 'collection') {
|
||||
const group = groups.find(g => g.type === 'collection');
|
||||
return group && group.items && group.items.length > 0;
|
||||
} else if (modalCurrentType.value === 'activity') {
|
||||
const group = groups.find(g => g.type === 'activity');
|
||||
return group && group.items && group.items.length > 0;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// 获取原创藏品的等级分组
|
||||
const modalRegularGrades = computed(() => {
|
||||
const group = myAssetsGroups.value.find(g => g.type === 'regular');
|
||||
return group && group.grades ? group.grades : [];
|
||||
});
|
||||
|
||||
// 获取典藏藏品列表
|
||||
const modalCollectionItems = computed(() => {
|
||||
const group = myAssetsGroups.value.find(g => g.type === 'collection');
|
||||
return group && group.items ? group.items : [];
|
||||
});
|
||||
|
||||
// 获取活动藏品列表
|
||||
const modalActivityItems = computed(() => {
|
||||
const group = myAssetsGroups.value.find(g => g.type === 'activity');
|
||||
return group && group.items ? group.items : [];
|
||||
});
|
||||
|
||||
// grade 中文转换
|
||||
const gradeMap = { 1: '一', 2: '二', 3: '三', 4: '四', 5: '五' };
|
||||
const formatModalGrade = (grade) => {
|
||||
return `等级${gradeMap[grade] || grade}`;
|
||||
};
|
||||
|
||||
// 切换弹窗内类型
|
||||
const switchModalType = (type) => {
|
||||
modalCurrentType.value = type;
|
||||
};
|
||||
|
||||
// 下架藏品相关状态
|
||||
const showRemoveConfirmModal = ref(false);
|
||||
@ -822,26 +979,82 @@ export default {
|
||||
|
||||
|
||||
// 获取用户藏品列表
|
||||
modalLoading.value = true;
|
||||
try {
|
||||
const response = await getMyAssetsApi(1, 20);
|
||||
if (response.code === 200 && response.data && response.data.items) {
|
||||
const assetsPromises = response.data.items.map(async item => {
|
||||
const realCoverUrl = await getAssetCoverRealUrl(item.cover_url);
|
||||
return {
|
||||
asset_id: item.asset_id,
|
||||
name: item.name,
|
||||
image: realCoverUrl,
|
||||
cover_url: item.cover_url || '/static/nft/collection.png'
|
||||
};
|
||||
});
|
||||
myAssetsList.value = await Promise.all(assetsPromises);
|
||||
|
||||
// 显示藏品选择弹窗
|
||||
if (response.code === 200 && response.data && response.data.data.groups) {
|
||||
// 先展示弹窗,再异步处理图片
|
||||
showAssetSelectModal.value = true;
|
||||
// 延迟触发动画
|
||||
setTimeout(() => {
|
||||
assetSelectModalAnimated.value = true;
|
||||
}, 50);
|
||||
|
||||
// 收集所有需要处理的藏品项
|
||||
const allItems = [];
|
||||
const itemRefs = [];
|
||||
|
||||
// 处理分组数据,收集 items 稍后并行处理
|
||||
const processedGroups = [];
|
||||
for (const group of response.data.data.groups) {
|
||||
const processedGroup = {
|
||||
type: group.type,
|
||||
category: group.category,
|
||||
category_name: group.category_name,
|
||||
total_count: group.total_count,
|
||||
has_more: group.has_more,
|
||||
grades: [],
|
||||
items: []
|
||||
};
|
||||
|
||||
// 处理 grades
|
||||
if (group.grades) {
|
||||
for (const grade of group.grades) {
|
||||
const processedGrade = {
|
||||
grade: grade.grade,
|
||||
total_count: grade.total_count,
|
||||
has_more: grade.has_more,
|
||||
items: []
|
||||
};
|
||||
for (const item of grade.items || []) {
|
||||
allItems.push(item);
|
||||
itemRefs.push({ type: 'grade', parent: processedGrade, grade: item.grade, category: null });
|
||||
}
|
||||
processedGroup.grades.push(processedGrade);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 items
|
||||
if (group.items) {
|
||||
for (const item of group.items) {
|
||||
allItems.push(item);
|
||||
itemRefs.push({ type: 'item', parent: processedGroup, grade: null, category: item.category });
|
||||
}
|
||||
}
|
||||
|
||||
processedGroups.push(processedGroup);
|
||||
}
|
||||
|
||||
// 并行处理所有封面URL
|
||||
const coverUrlPromises = allItems.map(item => getAssetCoverRealUrl(item.cover_url_signed));
|
||||
const coverUrls = await Promise.all(coverUrlPromises);
|
||||
|
||||
// 将处理好的封面URL填回数据结构
|
||||
for (let i = 0; i < allItems.length; i++) {
|
||||
const item = allItems[i];
|
||||
const ref = itemRefs[i];
|
||||
const processedItem = {
|
||||
asset_id: item.asset_id,
|
||||
name: item.name,
|
||||
coverUrl: coverUrls[i],
|
||||
display_status: item.display_status || 0,
|
||||
like_count: item.like_count,
|
||||
grade: ref.grade,
|
||||
category: ref.category
|
||||
};
|
||||
ref.parent.items.push(processedItem);
|
||||
}
|
||||
|
||||
myAssetsGroups.value = processedGroups;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取藏品列表失败:', error);
|
||||
@ -850,6 +1063,8 @@ export default {
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
} finally {
|
||||
modalLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
@ -860,7 +1075,7 @@ export default {
|
||||
// 等待动画结束后再隐藏弹窗
|
||||
setTimeout(() => {
|
||||
showAssetSelectModal.value = false;
|
||||
myAssetsList.value = [];
|
||||
myAssetsGroups.value = [];
|
||||
// 重置替换模式状态
|
||||
if (isReplaceMode.value) {
|
||||
isReplaceMode.value = false;
|
||||
@ -964,7 +1179,7 @@ export default {
|
||||
assetSelectModalAnimated.value = false;
|
||||
setTimeout(() => {
|
||||
showAssetSelectModal.value = false;
|
||||
myAssetsList.value = [];
|
||||
myAssetsGroups.value = [];
|
||||
}, 300);
|
||||
|
||||
// 清空选中状态
|
||||
@ -1100,26 +1315,82 @@ export default {
|
||||
selectedSlotId.value = asset.slot_id;
|
||||
|
||||
// 获取用户藏品列表
|
||||
modalLoading.value = true;
|
||||
try {
|
||||
const response = await getMyAssetsApi(1, 20);
|
||||
if (response.code === 200 && response.data && response.data.items) {
|
||||
const assetsPromises = response.data.items.map(async item => {
|
||||
const realCoverUrl = await getAssetCoverRealUrl(item.cover_url);
|
||||
return {
|
||||
asset_id: item.asset_id,
|
||||
name: item.name,
|
||||
image: realCoverUrl,
|
||||
cover_url: item.cover_url || '/static/nft/collection.png'
|
||||
};
|
||||
});
|
||||
myAssetsList.value = await Promise.all(assetsPromises);
|
||||
|
||||
// 显示藏品选择弹窗
|
||||
if (response.code === 200 && response.data && response.data.data.groups) {
|
||||
// 先展示弹窗,再异步处理图片
|
||||
showAssetSelectModal.value = true;
|
||||
// 延迟触发动画
|
||||
setTimeout(() => {
|
||||
assetSelectModalAnimated.value = true;
|
||||
}, 50);
|
||||
|
||||
// 收集所有需要处理的藏品项
|
||||
const allItems = [];
|
||||
const itemRefs = [];
|
||||
|
||||
// 处理分组数据,收集 items 稍后并行处理
|
||||
const processedGroups = [];
|
||||
for (const group of response.data.data.groups) {
|
||||
const processedGroup = {
|
||||
type: group.type,
|
||||
category: group.category,
|
||||
category_name: group.category_name,
|
||||
total_count: group.total_count,
|
||||
has_more: group.has_more,
|
||||
grades: [],
|
||||
items: []
|
||||
};
|
||||
|
||||
// 处理 grades
|
||||
if (group.grades) {
|
||||
for (const grade of group.grades) {
|
||||
const processedGrade = {
|
||||
grade: grade.grade,
|
||||
total_count: grade.total_count,
|
||||
has_more: grade.has_more,
|
||||
items: []
|
||||
};
|
||||
for (const item of grade.items || []) {
|
||||
allItems.push(item);
|
||||
itemRefs.push({ type: 'grade', parent: processedGrade, grade: item.grade, category: null });
|
||||
}
|
||||
processedGroup.grades.push(processedGrade);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 items
|
||||
if (group.items) {
|
||||
for (const item of group.items) {
|
||||
allItems.push(item);
|
||||
itemRefs.push({ type: 'item', parent: processedGroup, grade: null, category: item.category });
|
||||
}
|
||||
}
|
||||
|
||||
processedGroups.push(processedGroup);
|
||||
}
|
||||
|
||||
// 并行处理所有封面URL
|
||||
const coverUrlPromises = allItems.map(item => getAssetCoverRealUrl(item.cover_url_signed));
|
||||
const coverUrls = await Promise.all(coverUrlPromises);
|
||||
|
||||
// 将处理好的封面URL填回数据结构
|
||||
for (let i = 0; i < allItems.length; i++) {
|
||||
const item = allItems[i];
|
||||
const ref = itemRefs[i];
|
||||
const processedItem = {
|
||||
asset_id: item.asset_id,
|
||||
name: item.name,
|
||||
coverUrl: coverUrls[i],
|
||||
display_status: item.display_status || 0,
|
||||
like_count: item.like_count,
|
||||
grade: ref.grade,
|
||||
category: ref.category
|
||||
};
|
||||
ref.parent.items.push(processedItem);
|
||||
}
|
||||
|
||||
myAssetsGroups.value = processedGroups;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取藏品列表失败:', error);
|
||||
@ -1131,6 +1402,8 @@ export default {
|
||||
// 重置状态
|
||||
isReplaceMode.value = false;
|
||||
selectedAssetForRemove.value = null;
|
||||
} finally {
|
||||
modalLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1539,28 +1812,198 @@ export default {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 藏品网格容器 */
|
||||
.modal-nft-grid-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
column-gap: 15rpx;
|
||||
row-gap: 10rpx;
|
||||
align-items: start;
|
||||
align-content: start;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
/* 弹窗内类型Tab */
|
||||
.modal-type-tabs {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 40rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.modal-tab-item {
|
||||
padding: 12rpx 30rpx;
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
border-bottom: 4rpx solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.modal-tab-item.active {
|
||||
color: #ffffff;
|
||||
border-bottom-color: #ffffff;
|
||||
}
|
||||
|
||||
/* 弹窗内加载中 */
|
||||
.modal-loading-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-top: 200rpx;
|
||||
}
|
||||
|
||||
.modal-loading-text {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* 弹窗内空状态 */
|
||||
.modal-empty-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-top: 200rpx;
|
||||
}
|
||||
|
||||
.modal-empty-text {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* 弹窗内可滚动区域 */
|
||||
.modal-nft-scroll-view {
|
||||
height: calc(80vh - 200rpx);
|
||||
}
|
||||
|
||||
/* 隐藏滚动条 */
|
||||
.modal-nft-scroll-view::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.modal-nft-scroll-view {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
/* 弹窗内藏品列表容器 */
|
||||
.modal-nft-list-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 弹窗内藏品分组 */
|
||||
.modal-nft-group {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
/* 弹窗内等级区块 */
|
||||
.modal-grade-section {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 弹窗内分组标题 */
|
||||
.modal-group-header {
|
||||
margin-bottom: 16rpx;
|
||||
padding-bottom: 10rpx;
|
||||
border-bottom: 1rpx solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.modal-group-title {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
/* 弹窗内藏品行 - 水平滚动 */
|
||||
.modal-nft-row {
|
||||
width: 100%;
|
||||
height: 288rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 弹窗内藏品行内容容器 */
|
||||
.modal-nft-row-content {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
padding-left: 24rpx;
|
||||
height: 100%;
|
||||
width: 192rpx;
|
||||
}
|
||||
|
||||
/* 隐藏滚动条 */
|
||||
.modal-nft-row::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.modal-nft-row {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
/* 弹窗内藏品网格项 */
|
||||
.modal-nft-grid-item {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 133.33%;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-right: 32rpx;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 弹窗内NFT图片 */
|
||||
.modal-nft-image {
|
||||
width: 192rpx;
|
||||
height: 224rpx;
|
||||
border-radius: 16rpx;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 弹窗内更多项 */
|
||||
.modal-nft-grid-item.modal-more-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 弹窗内藏品信息 */
|
||||
.modal-nft-info {
|
||||
padding: 12rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 弹窗内展示状态标签 */
|
||||
.modal-status-badge {
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
right: 8rpx;
|
||||
border-radius: 8rpx;
|
||||
padding: 4rpx 8rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
.modal-status-badge-active {
|
||||
background: linear-gradient(135deg, #FFD700, #FFA500);
|
||||
box-shadow: 0 0 12rpx rgba(255, 215, 0, 0.6);
|
||||
}
|
||||
.modal-status-badge-pending {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.modal-status-text {
|
||||
font-size: 18rpx;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.modal-nft-name {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 弹窗内更多覆盖层 */
|
||||
.modal-more-overlay {
|
||||
width: 192rpx;
|
||||
height: 224rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.modal-more-text {
|
||||
font-size: 26rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 上架确认弹窗遮罩 */
|
||||
.place-confirm-modal-mask {
|
||||
position: fixed;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user