diff --git a/README.md b/README.md
index 8c3e127..c1114b8 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1 @@
# TopFans
-背景有什么想法?边框想要什么样式的?有没有特别想加的装饰元素?整体材质的效果想要什么样的?主色调是应援色吗,还是别的颜色?
\ No newline at end of file
diff --git a/backend/gateway/controller/asset_controller.go b/backend/gateway/controller/asset_controller.go
index 67222b4..647ffdc 100644
--- a/backend/gateway/controller/asset_controller.go
+++ b/backend/gateway/controller/asset_controller.go
@@ -276,7 +276,7 @@ func (ctrl *AssetController) CreateMintOrder(c *gin.Context) {
Rarity: req.Rarity,
Tags: req.Tags,
MaterialType: req.MaterialType,
- Event: req.Event,
+ Info: req.Info,
})
if err != nil {
diff --git a/backend/gateway/dto/asset_converter.go b/backend/gateway/dto/asset_converter.go
index 139d70f..84260a6 100644
--- a/backend/gateway/dto/asset_converter.go
+++ b/backend/gateway/dto/asset_converter.go
@@ -83,6 +83,7 @@ func ConvertAsset(pbAsset *pbAsset.Asset) AssetDTO {
CreatedAt: pbAsset.CreatedAt,
UpdatedAt: pbAsset.UpdatedAt,
IsLiked: pbAsset.IsLiked,
+ Info: pbAsset.Info,
}
// 可选字段
diff --git a/backend/gateway/dto/asset_dto.go b/backend/gateway/dto/asset_dto.go
index 7148e52..2436274 100644
--- a/backend/gateway/dto/asset_dto.go
+++ b/backend/gateway/dto/asset_dto.go
@@ -4,14 +4,14 @@ package dto
// CreateMintOrderRequestDTO 创建铸造订单请求
type CreateMintOrderRequestDTO struct {
- OrderID string `json:"order_id" binding:"required"` // 阶段一生成的订单ID(必填)
- Name string `json:"name" binding:"required"` // 藏品名称(必填)
- MaterialURL string `json:"material_url" binding:"required"` // 用户上传的素材URL(必填,前端已上传到OSS)
- Description string `json:"description"` // 藏品描述(可选)
- Rarity int32 `json:"rarity"` // 稀有度(可选)
- Tags []string `json:"tags"` // 标签列表(可选)
- MaterialType string `json:"material_type"` // 素材类型(可选)
- Event string `json:"event"` // 藏品事件(可选)
+ OrderID string `json:"order_id" binding:"required"` // 阶段一生成的订单ID(必填)
+ Name string `json:"name"` // 藏品名称(可选)
+ MaterialURL string `json:"material_url" binding:"required"` // 用户上传的素材URL(必填,前端已上传到OSS)
+ Description string `json:"description"` // 藏品描述(可选)
+ Rarity int32 `json:"rarity"` // 稀有度(可选)
+ Tags []string `json:"tags"` // 标签列表(可选)
+ MaterialType string `json:"material_type"` // 素材类型(可选)
+ Info string `json:"info" binding:"required"` // 藏品信息(必填)
}
// PreCreateMintOrderRequestDTO 阶段一:预创建铸造订单(返回 order_id)
@@ -79,6 +79,7 @@ type AssetDTO struct {
MintedAt int64 `json:"minted_at,omitempty"` // 上链成功时间(毫秒时间戳,可选)
Owner *OwnerInfoDTO `json:"owner,omitempty"` // 持有者信息(可选,保留用于兼容性)
IsLiked bool `json:"is_liked"` // 当前用户是否已点赞
+ Info string `json:"info"` // 藏品信息
}
// OwnerInfoDTO 持有者信息
diff --git a/backend/pkg/models/asset.go b/backend/pkg/models/asset.go
index 8ab5911..9faa055 100644
--- a/backend/pkg/models/asset.go
+++ b/backend/pkg/models/asset.go
@@ -22,6 +22,7 @@ type Asset struct {
Rarity *int32 `gorm:"column:rarity"` // 稀有度(预留)
Tags StringArray `gorm:"type:jsonb;column:tags"` // 标签(预留,JSON数组)
Visibility string `gorm:"type:varchar(20);default:'private';column:visibility"` // 可见性:private, friends, public(预留)
+ Info string `gorm:"type:text;column:info"` // 藏品信息(必填)
// 状态字段
Status int32 `gorm:"not null;default:0;index:idx_assets_status;column:status"` // 0:Pending, 1:Active
@@ -122,6 +123,7 @@ type MintOrder struct {
Description *string `gorm:"type:text;column:description"` // 藏品描述(阶段一写入,可选)
MaterialType *string `gorm:"type:varchar(50);column:material_type"` // 素材类型(可选)
Event *string `gorm:"type:varchar(100);column:event"` // 藏品事件(可选)
+ Info *string `gorm:"type:text;column:info"` // 藏品信息(必填)
CreatedAt int64 `gorm:"not null;index:idx_mint_orders_created_at,sort:desc;column:created_at"`
UpdatedAt int64 `gorm:"not null;column:updated_at"`
MintedAt *int64 `gorm:"column:minted_at"` // 上链成功时间(用于 Task Service 事件)
diff --git a/backend/pkg/proto/asset/asset.pb.go b/backend/pkg/proto/asset/asset.pb.go
index efea015..cfeaca7 100644
--- a/backend/pkg/proto/asset/asset.pb.go
+++ b/backend/pkg/proto/asset/asset.pb.go
@@ -47,6 +47,7 @@ type Asset struct {
Owner *OwnerInfo `protobuf:"bytes,18,opt,name=owner,proto3" json:"owner,omitempty"` // 持有者信息(保留用于兼容性)
OwnerNickname string `protobuf:"bytes,19,opt,name=owner_nickname,json=ownerNickname,proto3" json:"owner_nickname,omitempty"` // 持有者昵称(在该star下的昵称)
IsLiked bool `protobuf:"varint,20,opt,name=is_liked,json=isLiked,proto3" json:"is_liked,omitempty"` // 当前用户是否已点赞
+ Info string `protobuf:"bytes,21,opt,name=info,proto3" json:"info,omitempty"` // 藏品信息
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -221,6 +222,13 @@ func (x *Asset) GetIsLiked() bool {
return false
}
+func (x *Asset) GetInfo() string {
+ if x != nil {
+ return x.Info
+ }
+ return ""
+}
+
// 持有者信息
type OwnerInfo struct {
state protoimpl.MessageState `protogen:"open.v1"`
@@ -548,13 +556,13 @@ func (x *InitMintOrderResponse) GetOrder() *MintOrder {
type CreateMintOrderRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
OrderId string `protobuf:"bytes,2,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` // 阶段一生成的订单ID(必填)
- Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // 藏品名称(必填,可覆盖阶段一保存值)
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // 藏品名称(可选)
MaterialUrl string `protobuf:"bytes,3,opt,name=material_url,json=materialUrl,proto3" json:"material_url,omitempty"` // 用户上传的素材URL(必填,前端已上传到OSS)
Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` // 藏品描述(可选)
Rarity int32 `protobuf:"varint,5,opt,name=rarity,proto3" json:"rarity,omitempty"` // 稀有度(可选)
Tags []string `protobuf:"bytes,6,rep,name=tags,proto3" json:"tags,omitempty"` // 标签列表(可选)
MaterialType string `protobuf:"bytes,7,opt,name=material_type,json=materialType,proto3" json:"material_type,omitempty"` // 素材类型(可选)
- Event string `protobuf:"bytes,8,opt,name=event,proto3" json:"event,omitempty"` // 藏品事件(可选)
+ Info string `protobuf:"bytes,8,opt,name=info,proto3" json:"info,omitempty"` // 藏品信息(必填)
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -638,9 +646,9 @@ func (x *CreateMintOrderRequest) GetMaterialType() string {
return ""
}
-func (x *CreateMintOrderRequest) GetEvent() string {
+func (x *CreateMintOrderRequest) GetInfo() string {
if x != nil {
- return x.Event
+ return x.Info
}
return ""
}
diff --git a/backend/proto/asset.proto b/backend/proto/asset.proto
index a250285..98aef80 100644
--- a/backend/proto/asset.proto
+++ b/backend/proto/asset.proto
@@ -28,11 +28,12 @@ message Asset {
int64 created_at = 15; // 创建时间(毫秒时间戳)
int64 updated_at = 16; // 更新时间(毫秒时间戳)
int64 minted_at = 17; // 上链成功时间(毫秒时间戳,可选)
-
+
// 扩展字段:持有者信息(用于详情展示)
OwnerInfo owner = 18; // 持有者信息(保留用于兼容性)
string owner_nickname = 19; // 持有者昵称(在该star下的昵称)
bool is_liked = 20; // 当前用户是否已点赞
+ string info = 21; // 藏品信息
}
// 持有者信息
@@ -77,13 +78,13 @@ message InitMintOrderResponse {
// 创建铸造订单请求
message CreateMintOrderRequest {
string order_id = 2; // 阶段一生成的订单ID(必填)
- string name = 1; // 藏品名称(必填,可覆盖阶段一保存值)
+ string name = 1; // 藏品名称(可选)
string material_url = 3; // 用户上传的素材URL(必填,前端已上传到OSS)
string description = 4; // 藏品描述(可选)
int32 rarity = 5; // 稀有度(可选)
repeated string tags = 6; // 标签列表(可选)
string material_type = 7; // 素材类型(可选)
- string event = 8; // 藏品事件(可选)
+ string info = 8; // 藏品信息(必填)
// cover_url 不再需要前端传入,由后端 AI 处理完成后自动生成
}
diff --git a/frontend/pages.json b/frontend/pages.json
index 947e14b..70dc7f5 100644
--- a/frontend/pages.json
+++ b/frontend/pages.json
@@ -8,6 +8,33 @@
}
}
},
+ {
+ "path": "pages/starbook/index",
+ "style": {
+ "navigationStyle": "custom",
+ "app-plus": {
+ "bounce": "none"
+ }
+ }
+ },
+ {
+ "path": "pages/starcity/index",
+ "style": {
+ "navigationStyle": "custom",
+ "app-plus": {
+ "bounce": "none"
+ }
+ }
+ },
+ {
+ "path": "pages/friends/index",
+ "style": {
+ "navigationStyle": "custom",
+ "app-plus": {
+ "bounce": "none"
+ }
+ }
+ },
{
"path": "pages/login/login",
"style": {
@@ -56,6 +83,30 @@
}
}
},
+ {
+ "path": "pages/castlove/index",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/castlove/create",
+ "style": {
+ "navigationStyle": "custom",
+ "app-plus": {
+ "bounce": "none"
+ }
+ }
+ },
+ {
+ "path": "pages/castlove/mall",
+ "style": {
+ "navigationStyle": "custom",
+ "app-plus": {
+ "bounce": "none"
+ }
+ }
+ },
{
"path": "pages/castlove/success",
"style": {
diff --git a/frontend/pages/asset-detail/asset-detail.vue b/frontend/pages/asset-detail/asset-detail.vue
index 01fcbc8..6190061 100644
--- a/frontend/pages/asset-detail/asset-detail.vue
+++ b/frontend/pages/asset-detail/asset-detail.vue
@@ -114,15 +114,15 @@
+
+
diff --git a/frontend/pages/castlove/index.vue b/frontend/pages/castlove/index.vue
new file mode 100644
index 0000000..f4f1971
--- /dev/null
+++ b/frontend/pages/castlove/index.vue
@@ -0,0 +1,946 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 点击上传藏品图片
+
+
+ 支持JPG、PNG格式,大小不超过5MB
+
+
+
+
+
+
+ 素材类型
+
+
+ {{ materialTypes[materialTypeIndex] }}
+ ›
+
+
+
+
+ {{ type }}
+ ✓
+
+
+
+
+
+
+
+ 藏品名称
+
+
+
+
+
+ 藏品事件
+
+
+
+
+
+ 备注
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/pages/castlove/mall.vue b/frontend/pages/castlove/mall.vue
new file mode 100644
index 0000000..9a62d81
--- /dev/null
+++ b/frontend/pages/castlove/mall.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/pages/components/CastloveContent.vue b/frontend/pages/components/CastloveContent.vue
index eec2a68..fcde3c8 100644
--- a/frontend/pages/components/CastloveContent.vue
+++ b/frontend/pages/components/CastloveContent.vue
@@ -1,543 +1,366 @@
-
+
-
-
+
+
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
- 点击上传藏品图片
+
+
+
+
+
+
+
+ {{ banner.title }}
-
- 支持JPG、PNG格式,大小不超过5MB
+
+
+
+
+
+
+
+
+ {{ tab.name }}
-
-
-
-
-
- 素材类型
-
-
- {{ materialTypes[materialTypeIndex] }}
- ›
+
+
+
+
+
+
+ {{ category.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #{{ item.certificate_id }}
+
+
+
+
+ {{ item.creator_name }}
-
-
-
- {{ type }}
- ✓
-
+
+ ♡
+ {{ formatCount(item.like_count) }}
-
-
-
- 藏品名称
-
-
-
-
-
- 藏品事件
-
-
-
-
-
- 备注
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ 加载中...
+
+
+
+ 没有更多了
+
+
-
diff --git a/frontend/pages/friends/index.vue b/frontend/pages/friends/index.vue
new file mode 100644
index 0000000..8fec316
--- /dev/null
+++ b/frontend/pages/friends/index.vue
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/pages/square/square.vue b/frontend/pages/square/square.vue
index da35191..b5d4abb 100644
--- a/frontend/pages/square/square.vue
+++ b/frontend/pages/square/square.vue
@@ -9,7 +9,7 @@
-
+
-
-
+
+
‹
@@ -38,10 +38,10 @@
-
+
-
-
+
+
@@ -54,11 +54,7 @@
-
-
-
-
-
+
@@ -68,7 +64,7 @@
@update:visible="handleRankingModalClose" @visit="handleRankingVisit" />
-
@@ -100,10 +96,6 @@ import {
} from "vuex";
import Header from "../components/Header.vue";
import BottomNav from "../components/BottomNav.vue";
-import CastloveContent from "../components/CastloveContent.vue";
-import StarbookContent from "../components/StarbookContent.vue";
-import StarCityContent from "../components/StarCityContent.vue";
-import FriendsContent from "../components/FriendsContent.vue";
import GuideStartModal from "@/components/GuideStartModal.vue";
import GuideOverlay from "@/components/GuideOverlay.vue";
import {
@@ -840,7 +832,7 @@ onMounted(async () => {
// 从其他路由页面返回时重置
onShow(() => {
- if (activeTab.value === 0) resetSquare();
+ resetSquare();
});
// 页面加载完成后初始化引导
@@ -889,7 +881,6 @@ const getBannerBottom = () => (screenWidth.value / 750) * 496;
let touchInBanner = false;
const onBgTouchStart = (e) => {
- if (activeTab.value !== 0) return;
const touchY = e.touches[0].clientY;
touchInBanner = touchY < getBannerBottom();
if (touchInBanner) return;
@@ -902,11 +893,9 @@ const onBgTouchStart = (e) => {
};
const onBgTouchMove = (e) => {
- // 非 tab 0 时不阻止,让 scroll-view 正常滚动
- if (activeTab.value !== 0) return;
- // tab 0 时如果触摸在 banner 区域也不处理,让 banner 自己处理
+ // 如果触摸在 banner 区域也不处理,让 banner 自己处理
if (touchInBanner) return;
- // tab 0 且不在 banner 区域时,阻止默认行为以实现横向背景滑动
+ // 不在 banner 区域时,阻止默认行为以实现横向背景滑动
e.preventDefault();
const currentX = e.touches[0].clientX;
@@ -920,7 +909,7 @@ const onBgTouchMove = (e) => {
};
const onBgTouchEnd = () => {
- if (activeTab.value !== 0 || touchInBanner) {
+ if (touchInBanner) {
touchInBanner = false;
return;
}
@@ -963,14 +952,25 @@ const handleCabinClick = (cabin) => {
// --- Tab 切换 ---
const handleTabChange = (newTab) => {
- const prevTab = activeTab.value;
- activeTab.value = newTab;
- navExpanded.value = false;
- if (prevTab !== 0 && newTab === 0) resetSquare();
-};
-
-const handleCastloveBack = () => {
- handleTabChange(0);
+ if (newTab === 0) {
+ // 已经在广场页面,不需要跳转
+ navExpanded.value = false;
+ return;
+ }
+
+ const routes = [
+ '/pages/square/square',
+ '/pages/starbook/index',
+ '/pages/castlove/mall',
+ '/pages/starcity/index',
+ '/pages/friends/index'
+ ];
+
+ if (newTab >= 0 && newTab < routes.length) {
+ uni.redirectTo({
+ url: routes[newTab]
+ });
+ }
};
onLoad((options) => {
@@ -988,13 +988,6 @@ onLoad((options) => {
console.log('[Guide] 调试模式已关闭')
}
}
-
- if (options && options.tab) {
- const tabIndex = parseInt(options.tab);
- if (!isNaN(tabIndex) && tabIndex >= 0 && tabIndex <= 4) {
- activeTab.value = tabIndex;
- }
- }
});
diff --git a/frontend/pages/starbook/index.vue b/frontend/pages/starbook/index.vue
new file mode 100644
index 0000000..0bde579
--- /dev/null
+++ b/frontend/pages/starbook/index.vue
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/pages/starcity/index.vue b/frontend/pages/starcity/index.vue
new file mode 100644
index 0000000..914c693
--- /dev/null
+++ b/frontend/pages/starcity/index.vue
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/utils/api.js b/frontend/utils/api.js
index 4dcc4b5..890c7e9 100644
--- a/frontend/utils/api.js
+++ b/frontend/utils/api.js
@@ -1,6 +1,6 @@
// API 基础配置
-const baseURL = 'http://101.132.250.62:8080'
-// const baseURL = 'http://192.168.110.60:8080'
+// const baseURL = 'http://101.132.250.62:8080'
+const baseURL = 'http://192.168.110.60:8080'
// const baseURL = 'http://localhost:8080'
// 是否使用模拟数据(开发调试时设为 true,后端API准备好后改为 false)
diff --git a/frontend/utils/assetImageHelper.js b/frontend/utils/assetImageHelper.js
index 44a9626..3c88c21 100644
--- a/frontend/utils/assetImageHelper.js
+++ b/frontend/utils/assetImageHelper.js
@@ -72,13 +72,13 @@ export function extractOssObjectPath(url) {
*/
export async function getAssetCoverRealUrl(coverUrl) {
// 默认图片路径
- const DEFAULT_IMAGE = '/static/nft/collection.png';
+ const DEFAULT_IMAGE = '';
// 如果是本地静态资源,直接返回
if (!coverUrl || coverUrl.startsWith('/static/')) {
return coverUrl || DEFAULT_IMAGE;
}
-
+ console.log(coverUrl)
try {
// 提取完整OSS对象路径(保留 asset/13/87/filename.jpg 这样的前缀)
const objectPath = extractOssObjectPath(coverUrl);