Compare commits
2 Commits
Company/De
...
98fb194718
Author | SHA1 | Date | |
---|---|---|---|
![]() |
98fb194718 | ||
![]() |
e980cd5553 |
@@ -11711,10 +11711,14 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-resources.sh\"\n";
|
||||
@@ -11728,10 +11732,14 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-YuMi/Pods-YuMi-frameworks.sh\"\n";
|
||||
@@ -13282,7 +13290,7 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 48UCG35Q9W;
|
||||
DEVELOPMENT_TEAM = Z7UCRF23F3;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -13516,7 +13524,7 @@
|
||||
"-weak_framework",
|
||||
"\"AuthenticationServices\"",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.peko.enterprise.ios;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.junpeiqi.eparty;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
@@ -13538,9 +13546,9 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = YuMi/YuMiRelease.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = Z7UCRF23F3;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
93
YuMi/Config/APIConfig.swift
Normal file
93
YuMi/Config/APIConfig.swift
Normal file
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// APIConfig.swift
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// API 域名配置类
|
||||
/// 使用 XOR + Base64 双重混淆防止反编译
|
||||
@objc class APIConfig: NSObject {
|
||||
|
||||
// MARK: - Private Properties
|
||||
|
||||
/// XOR 加密密钥
|
||||
private static let xorKey: UInt8 = 77
|
||||
|
||||
/// RELEASE 环境域名(加密后)
|
||||
/// 原始域名:https://api.epartylive.com
|
||||
private static let releaseEncodedParts: [String] = [
|
||||
"JTk5PT53YmI=", // https:// (XOR 后 Base64)
|
||||
"LD0kYw==", // api. (XOR 后 Base64)
|
||||
"KD0sPzk0ISQ7KGMuIiA=", // epartylive.com (XOR 后 Base64)
|
||||
]
|
||||
|
||||
// MARK: - Public Methods
|
||||
|
||||
/// 获取 API 基础域名
|
||||
/// - Returns: 根据编译环境返回对应的域名
|
||||
@objc static func baseURL() -> String {
|
||||
#if DEBUG
|
||||
// DEV 环境:使用原有的测试域名(不变)
|
||||
return HttpRequestHelper.getHostUrl()
|
||||
#else
|
||||
// RELEASE 环境:使用动态生成的新域名
|
||||
let url = decodeURL(from: releaseEncodedParts)
|
||||
|
||||
// 验证解密结果
|
||||
if url.isEmpty || !url.hasPrefix("http") {
|
||||
NSLog("[APIConfig] 警告:域名解密失败,使用备用域名")
|
||||
return backupURL()
|
||||
}
|
||||
|
||||
return url
|
||||
#endif
|
||||
}
|
||||
|
||||
/// 备用域名(降级方案)
|
||||
/// - Returns: 原域名(仅在解密失败时使用)
|
||||
@objc static func backupURL() -> String {
|
||||
return HttpRequestHelper.getHostUrl()
|
||||
}
|
||||
|
||||
// MARK: - Private Methods
|
||||
|
||||
/// 解密域名
|
||||
/// - Parameter parts: 加密后的域名片段数组
|
||||
/// - Returns: 解密后的完整域名
|
||||
private static func decodeURL(from parts: [String]) -> String {
|
||||
let decoded = parts.compactMap { part -> String? in
|
||||
guard let data = Data(base64Encoded: part) else {
|
||||
NSLog("[APIConfig] Base64 解码失败: \(part)")
|
||||
return nil
|
||||
}
|
||||
let xored = data.map { $0 ^ xorKey }
|
||||
return String(bytes: xored, encoding: .utf8)
|
||||
}
|
||||
|
||||
let result = decoded.joined()
|
||||
|
||||
#if DEBUG
|
||||
NSLog("[APIConfig] 解密后的域名: \(result)")
|
||||
#endif
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Debug Helper
|
||||
|
||||
#if DEBUG
|
||||
extension APIConfig {
|
||||
/// 测试方法:验证域名加密/解密是否正常
|
||||
@objc static func testEncryption() {
|
||||
print("=== APIConfig 加密测试 ===")
|
||||
print("Release 域名: \(decodeURL(from: releaseEncodedParts))")
|
||||
print("当前环境域名: \(baseURL())")
|
||||
print("备用域名: \(backupURL())")
|
||||
}
|
||||
}
|
||||
#endif
|
71
YuMi/Global/GlobalEventManager.h
Normal file
71
YuMi/Global/GlobalEventManager.h
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// GlobalEventManager.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// 全局事件管理器
|
||||
/// 负责处理原 TabBar 中的全局逻辑(SDK 代理、房间最小化、通知等)
|
||||
@interface GlobalEventManager : NSObject
|
||||
|
||||
/// 单例
|
||||
+ (instancetype)shared;
|
||||
|
||||
// MARK: - SDK Delegates Setup
|
||||
|
||||
/// 设置所有第三方 SDK 的代理
|
||||
- (void)setupSDKDelegates;
|
||||
|
||||
/// 移除所有代理(dealloc 时调用)
|
||||
- (void)removeAllDelegates;
|
||||
|
||||
// MARK: - Room Mini View
|
||||
|
||||
/// 设置房间最小化视图
|
||||
/// @param containerView 父视图(通常是 TabBar 的 view)
|
||||
- (void)setupRoomMiniViewOn:(UIView *)containerView;
|
||||
|
||||
/// 处理房间最小化通知
|
||||
/// @param userInfo 通知携带的数据
|
||||
- (void)handleRoomMini:(NSDictionary * _Nullable)userInfo;
|
||||
|
||||
/// 隐藏房间最小化视图
|
||||
- (void)hideRoomMiniView;
|
||||
|
||||
// MARK: - Global Notifications
|
||||
|
||||
/// 处理配置重载通知
|
||||
- (void)handleConfigReload;
|
||||
|
||||
/// 处理新用户充值通知
|
||||
- (void)handleNewUserRecharge;
|
||||
|
||||
/// 处理主播卡片通知
|
||||
/// @param notification 通知对象
|
||||
- (void)handleAnchorCard:(NSNotification * _Nullable)notification;
|
||||
|
||||
/// 处理语言切换通知
|
||||
/// @param notification 通知对象
|
||||
- (void)handleLanguageSwitch:(NSNotification * _Nullable)notification;
|
||||
|
||||
// MARK: - User Info
|
||||
|
||||
/// 获取用户信息成功后的处理
|
||||
/// @param userInfo 用户信息模型
|
||||
- (void)handleUserInfoSuccess:(id)userInfo;
|
||||
|
||||
// MARK: - Social Share
|
||||
|
||||
/// 注册社交分享回调
|
||||
- (void)registerSocialShareCallback;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
242
YuMi/Global/GlobalEventManager.m
Normal file
242
YuMi/Global/GlobalEventManager.m
Normal file
@@ -0,0 +1,242 @@
|
||||
//
|
||||
// GlobalEventManager.m
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import "GlobalEventManager.h"
|
||||
#import "XPMiniRoomView.h"
|
||||
#import "RoomBoomManager.h"
|
||||
#import "PublicRoomManager.h"
|
||||
#import "XPSkillCardPlayerManager.h"
|
||||
#import "SocialShareManager.h"
|
||||
#import "YUMIConstant.h"
|
||||
#import <NIMSDK/NIMSDK.h>
|
||||
|
||||
@interface GlobalEventManager () <NIMLoginManagerDelegate, NIMChatManagerDelegate, NIMSystemNotificationManagerDelegate, NIMBroadcastManagerDelegate>
|
||||
|
||||
// MARK: - Private Properties
|
||||
|
||||
/// 房间最小化视图
|
||||
@property (nonatomic, strong) XPMiniRoomView *miniRoomView;
|
||||
|
||||
/// 配置重载回调
|
||||
@property (nonatomic, copy) void(^configReloadCallback)(void);
|
||||
|
||||
/// 新用户充值回调
|
||||
@property (nonatomic, copy) void(^newUserRechargeCallback)(void);
|
||||
|
||||
@end
|
||||
|
||||
@implementation GlobalEventManager
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
+ (instancetype)shared {
|
||||
static GlobalEventManager *instance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[GlobalEventManager alloc] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
[self setupNotificationObservers];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self removeAllDelegates];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
// MARK: - SDK Delegates Setup
|
||||
|
||||
- (void)setupSDKDelegates {
|
||||
// NIMSDK 代理设置
|
||||
[[NIMSDK sharedSDK].loginManager addDelegate:self];
|
||||
[[NIMSDK sharedSDK].chatManager addDelegate:self];
|
||||
[[NIMSDK sharedSDK].systemNotificationManager addDelegate:self];
|
||||
[[NIMSDK sharedSDK].broadcastManager addDelegate:self];
|
||||
|
||||
// RoomBoomManager 回调注册
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[[RoomBoomManager sharedManager] registerBoomBanner:^(id sth) {
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) return;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// 检查用户是否在房间中
|
||||
if ([XPSkillCardPlayerManager shareInstance].isInRoom) {
|
||||
NSLog(@"[GlobalEventManager] 收到 RoomBoom 通知");
|
||||
// TODO: 显示 Boom Banner
|
||||
// [RoomBoomBannerAnimation display:window with:sth tapToRoom:YES complete:^{}];
|
||||
}
|
||||
});
|
||||
} target:self];
|
||||
|
||||
NSLog(@"[GlobalEventManager] SDK 代理设置完成");
|
||||
}
|
||||
|
||||
- (void)removeAllDelegates {
|
||||
[[NIMSDK sharedSDK].loginManager removeDelegate:self];
|
||||
[[NIMSDK sharedSDK].chatManager removeDelegate:self];
|
||||
[[NIMSDK sharedSDK].systemNotificationManager removeDelegate:self];
|
||||
[[NIMSDK sharedSDK].broadcastManager removeDelegate:self];
|
||||
[[RoomBoomManager sharedManager] removeEventListenerForTarget:self];
|
||||
|
||||
NSLog(@"[GlobalEventManager] 所有代理已移除");
|
||||
}
|
||||
|
||||
// MARK: - Room Mini View
|
||||
|
||||
- (void)setupRoomMiniViewOn:(UIView *)containerView {
|
||||
if (!self.miniRoomView) {
|
||||
self.miniRoomView = [[XPMiniRoomView alloc] init];
|
||||
}
|
||||
[containerView addSubview:self.miniRoomView];
|
||||
NSLog(@"[GlobalEventManager] 房间最小化视图已添加");
|
||||
}
|
||||
|
||||
- (void)handleRoomMini:(NSDictionary *)userInfo {
|
||||
if (self.miniRoomView) {
|
||||
// TODO: 处理房间最小化逻辑
|
||||
NSLog(@"[GlobalEventManager] 处理房间最小化: %@", userInfo);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)hideRoomMiniView {
|
||||
if (self.miniRoomView) {
|
||||
[self.miniRoomView hiddenRoomMiniView];
|
||||
NSLog(@"[GlobalEventManager] 房间最小化视图已隐藏");
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Notification Observers
|
||||
|
||||
- (void)setupNotificationObservers {
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
// 房间最小化通知
|
||||
[center addObserver:self
|
||||
selector:@selector(onRoomMiniNotification:)
|
||||
name:kRoomMiniNotificationKey
|
||||
object:nil];
|
||||
|
||||
// 配置重载通知
|
||||
[center addObserver:self
|
||||
selector:@selector(onConfigReloadNotification:)
|
||||
name:@"reloadAfterLoadConfig"
|
||||
object:nil];
|
||||
|
||||
// 语言切换通知
|
||||
[center addObserver:self
|
||||
selector:@selector(onLanguageSwitchNotification:)
|
||||
name:@"kSwitchLanguage"
|
||||
object:nil];
|
||||
|
||||
NSLog(@"[GlobalEventManager] 通知监听已设置");
|
||||
}
|
||||
|
||||
- (void)onRoomMiniNotification:(NSNotification *)notification {
|
||||
[self handleRoomMini:notification.userInfo];
|
||||
}
|
||||
|
||||
- (void)onConfigReloadNotification:(NSNotification *)notification {
|
||||
[self handleConfigReload];
|
||||
}
|
||||
|
||||
- (void)onLanguageSwitchNotification:(NSNotification *)notification {
|
||||
[self handleLanguageSwitch:notification];
|
||||
}
|
||||
|
||||
// MARK: - Global Notifications Handler
|
||||
|
||||
- (void)handleConfigReload {
|
||||
NSLog(@"[GlobalEventManager] 配置重载");
|
||||
if (self.configReloadCallback) {
|
||||
self.configReloadCallback();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleNewUserRecharge {
|
||||
NSLog(@"[GlobalEventManager] 新用户充值");
|
||||
if (self.newUserRechargeCallback) {
|
||||
self.newUserRechargeCallback();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleAnchorCard:(NSNotification *)notification {
|
||||
NSLog(@"[GlobalEventManager] 主播卡片通知: %@", notification.userInfo);
|
||||
// TODO: 实现主播卡片逻辑
|
||||
}
|
||||
|
||||
- (void)handleLanguageSwitch:(NSNotification *)notification {
|
||||
NSLog(@"[GlobalEventManager] 语言切换: %@", notification.userInfo);
|
||||
// TODO: 实现语言切换逻辑
|
||||
}
|
||||
|
||||
// MARK: - User Info
|
||||
|
||||
- (void)handleUserInfoSuccess:(id)userInfo {
|
||||
NSLog(@"[GlobalEventManager] 用户信息获取成功");
|
||||
|
||||
// 更新各个 Manager 的用户信息
|
||||
if ([userInfo respondsToSelector:@selector(uid)]) {
|
||||
[[PublicRoomManager sharedManager] initialize];
|
||||
[[PublicRoomManager sharedManager] updateUserInfo:userInfo];
|
||||
[[RoomBoomManager sharedManager] saveUserInfo:userInfo];
|
||||
[[XPSkillCardPlayerManager shareInstance] setUserInfoModel:userInfo];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Social Share
|
||||
|
||||
- (void)registerSocialShareCallback {
|
||||
// 延迟 2 秒检查社交分享
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[[SocialShareManager sharedManager] checkSocialShareItem];
|
||||
NSLog(@"[GlobalEventManager] 社交分享回调已注册");
|
||||
});
|
||||
}
|
||||
|
||||
// MARK: - NIMSDK Delegate Methods
|
||||
|
||||
#pragma mark - NIMLoginManagerDelegate
|
||||
|
||||
- (void)onLogin:(NIMLoginStep)step {
|
||||
NSLog(@"[GlobalEventManager] NIMSDK 登录步骤: %ld", (long)step);
|
||||
}
|
||||
|
||||
- (void)onKickout:(NIMKickReason)code clientType:(NIMLoginClientType)clientType {
|
||||
NSLog(@"[GlobalEventManager] NIMSDK 被踢出: reason=%ld, clientType=%ld", (long)code, (long)clientType);
|
||||
}
|
||||
|
||||
- (void)onAutoLoginFailed:(NSError *)error {
|
||||
NSLog(@"[GlobalEventManager] NIMSDK 自动登录失败: %@", error);
|
||||
}
|
||||
|
||||
#pragma mark - NIMChatManagerDelegate
|
||||
|
||||
- (void)onRecvMessages:(NSArray<NIMMessage *> *)messages {
|
||||
NSLog(@"[GlobalEventManager] 收到 %lu 条消息", (unsigned long)messages.count);
|
||||
}
|
||||
|
||||
#pragma mark - NIMSystemNotificationManagerDelegate
|
||||
|
||||
- (void)onReceiveSystemNotification:(NIMSystemNotification *)notification {
|
||||
NSLog(@"[GlobalEventManager] 收到系统通知: %@", notification.notificationId);
|
||||
}
|
||||
|
||||
#pragma mark - NIMBroadcastManagerDelegate
|
||||
|
||||
- (void)onReceiveBroadcastMessage:(NIMBroadcastMessage *)message {
|
||||
NSLog(@"[GlobalEventManager] 收到广播消息: %@", message.content);
|
||||
}
|
||||
|
||||
@end
|
19
YuMi/Modules/NewMine/Controllers/NewMineViewController.h
Normal file
19
YuMi/Modules/NewMine/Controllers/NewMineViewController.h
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// NewMineViewController.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import "BaseViewController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// 新的个人中心页面控制器
|
||||
/// 采用纵向卡片式设计,完全不同于原 XPMineViewController
|
||||
@interface NewMineViewController : BaseViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
235
YuMi/Modules/NewMine/Controllers/NewMineViewController.m
Normal file
235
YuMi/Modules/NewMine/Controllers/NewMineViewController.m
Normal file
@@ -0,0 +1,235 @@
|
||||
//
|
||||
// NewMineViewController.m
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NewMineViewController.h"
|
||||
#import "NewMineHeaderView.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
|
||||
@interface NewMineViewController () <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
// MARK: - UI Components
|
||||
|
||||
/// 主列表
|
||||
@property (nonatomic, strong) UITableView *tableView;
|
||||
|
||||
/// 顶部个人信息卡片
|
||||
@property (nonatomic, strong) NewMineHeaderView *headerView;
|
||||
|
||||
/// 设置按钮
|
||||
@property (nonatomic, strong) UIButton *settingsButton;
|
||||
|
||||
// MARK: - Data
|
||||
|
||||
/// 菜单项数据源
|
||||
@property (nonatomic, strong) NSArray<NSDictionary *> *menuItems;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NewMineViewController
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.title = @"我的";
|
||||
self.view.backgroundColor = [UIColor colorWithRed:0.96 green:0.96 blue:0.96 alpha:1.0]; // 浅灰背景
|
||||
|
||||
[self setupNavigationBar];
|
||||
[self setupUI];
|
||||
[self loadData];
|
||||
|
||||
NSLog(@"[NewMineViewController] 页面加载完成");
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// 刷新用户信息
|
||||
[self refreshUserInfo];
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
- (void)setupNavigationBar {
|
||||
// 设置按钮(右上角)
|
||||
self.settingsButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[self.settingsButton setTitle:@"⚙️" forState:UIControlStateNormal];
|
||||
self.settingsButton.titleLabel.font = [UIFont systemFontOfSize:24];
|
||||
[self.settingsButton addTarget:self action:@selector(onSettingsButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
UIBarButtonItem *settingsBarButton = [[UIBarButtonItem alloc] initWithCustomView:self.settingsButton];
|
||||
self.navigationItem.rightBarButtonItem = settingsBarButton;
|
||||
}
|
||||
|
||||
- (void)setupUI {
|
||||
// TableView
|
||||
[self.view addSubview:self.tableView];
|
||||
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.view);
|
||||
}];
|
||||
|
||||
// 设置头部视图
|
||||
self.tableView.tableHeaderView = self.headerView;
|
||||
|
||||
NSLog(@"[NewMineViewController] UI 设置完成");
|
||||
}
|
||||
|
||||
// MARK: - Data Loading
|
||||
|
||||
- (void)loadData {
|
||||
// 菜单项配置(完全不同的顺序和图标)
|
||||
self.menuItems = @[
|
||||
@{@"title": @"💎 我的钱包", @"type": @"wallet"},
|
||||
@{@"title": @"📊 数据统计", @"type": @"stats"},
|
||||
@{@"title": @"⭐️ 我的收藏", @"type": @"favorites"},
|
||||
@{@"title": @"📝 编辑资料", @"type": @"profile"},
|
||||
@{@"title": @"🔔 消息通知", @"type": @"notifications"},
|
||||
@{@"title": @"🎨 主题设置", @"type": @"theme"},
|
||||
@{@"title": @"🌐 语言切换", @"type": @"language"},
|
||||
@{@"title": @"ℹ️ 关于我们", @"type": @"about"},
|
||||
];
|
||||
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)refreshUserInfo {
|
||||
// TODO: 从接口获取用户信息
|
||||
// 暂时使用模拟数据
|
||||
NSDictionary *mockUserInfo = @{
|
||||
@"nickname": @"测试用户",
|
||||
@"avatar": @"",
|
||||
@"level": @(12),
|
||||
@"exp": @(3580),
|
||||
@"nextLevelExp": @(5000),
|
||||
@"followers": @(128),
|
||||
@"following": @(256),
|
||||
};
|
||||
|
||||
[self.headerView configureWithUserInfo:mockUserInfo];
|
||||
NSLog(@"[NewMineViewController] 用户信息已刷新");
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
- (void)onSettingsButtonTapped {
|
||||
NSLog(@"[NewMineViewController] 设置按钮点击");
|
||||
// TODO: 跳转到设置页面
|
||||
[self showAlertWithMessage:@"设置功能开发中"];
|
||||
}
|
||||
|
||||
- (void)onMenuItemTapped:(NSDictionary *)item {
|
||||
NSString *type = item[@"type"];
|
||||
NSString *title = item[@"title"];
|
||||
|
||||
NSLog(@"[NewMineViewController] 菜单项点击: %@", type);
|
||||
|
||||
if ([type isEqualToString:@"wallet"]) {
|
||||
// TODO: 跳转到钱包页面
|
||||
[self showAlertWithMessage:@"钱包功能开发中"];
|
||||
} else if ([type isEqualToString:@"stats"]) {
|
||||
// TODO: 跳转到数据统计页面
|
||||
[self showAlertWithMessage:@"数据统计功能开发中"];
|
||||
} else if ([type isEqualToString:@"favorites"]) {
|
||||
// TODO: 跳转到收藏页面
|
||||
[self showAlertWithMessage:@"收藏功能开发中"];
|
||||
} else if ([type isEqualToString:@"profile"]) {
|
||||
// TODO: 跳转到编辑资料页面
|
||||
[self showAlertWithMessage:@"编辑资料功能开发中"];
|
||||
} else {
|
||||
[self showAlertWithMessage:[NSString stringWithFormat:@"%@ 功能开发中", title]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showAlertWithMessage:(NSString *)message {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示"
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.menuItems.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
static NSString *identifier = @"MenuCell";
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
|
||||
|
||||
if (!cell) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
|
||||
cell.backgroundColor = [UIColor whiteColor];
|
||||
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
cell.textLabel.font = [UIFont systemFontOfSize:15];
|
||||
cell.textLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1.0];
|
||||
}
|
||||
|
||||
if (indexPath.row < self.menuItems.count) {
|
||||
NSDictionary *item = self.menuItems[indexPath.row];
|
||||
cell.textLabel.text = item[@"title"];
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
|
||||
if (indexPath.row < self.menuItems.count) {
|
||||
NSDictionary *item = self.menuItems[indexPath.row];
|
||||
[self onMenuItemTapped:item];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return 56; // 卡片式高度
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
|
||||
return 15; // 上间距
|
||||
}
|
||||
|
||||
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
|
||||
UIView *header = [[UIView alloc] init];
|
||||
header.backgroundColor = [UIColor clearColor];
|
||||
return header;
|
||||
}
|
||||
|
||||
// MARK: - Lazy Loading
|
||||
|
||||
- (UITableView *)tableView {
|
||||
if (!_tableView) {
|
||||
_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
|
||||
_tableView.delegate = self;
|
||||
_tableView.dataSource = self;
|
||||
_tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
|
||||
_tableView.separatorInset = UIEdgeInsetsMake(0, 15, 0, 15);
|
||||
_tableView.backgroundColor = self.view.backgroundColor;
|
||||
_tableView.showsVerticalScrollIndicator = NO;
|
||||
_tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
}
|
||||
return _tableView;
|
||||
}
|
||||
|
||||
- (NewMineHeaderView *)headerView {
|
||||
if (!_headerView) {
|
||||
_headerView = [[NewMineHeaderView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 280)];
|
||||
}
|
||||
return _headerView;
|
||||
}
|
||||
|
||||
@end
|
23
YuMi/Modules/NewMine/Views/NewMineHeaderView.h
Normal file
23
YuMi/Modules/NewMine/Views/NewMineHeaderView.h
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// NewMineHeaderView.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// 新的个人中心头部视图
|
||||
/// 纵向卡片式设计 + 渐变背景
|
||||
@interface NewMineHeaderView : UIView
|
||||
|
||||
/// 配置用户信息
|
||||
/// @param userInfo 用户信息字典
|
||||
- (void)configureWithUserInfo:(NSDictionary *)userInfo;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
278
YuMi/Modules/NewMine/Views/NewMineHeaderView.m
Normal file
278
YuMi/Modules/NewMine/Views/NewMineHeaderView.m
Normal file
@@ -0,0 +1,278 @@
|
||||
//
|
||||
// NewMineHeaderView.m
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NewMineHeaderView.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
|
||||
@interface NewMineHeaderView ()
|
||||
|
||||
// MARK: - UI Components
|
||||
|
||||
/// 渐变背景层
|
||||
@property (nonatomic, strong) CAGradientLayer *gradientLayer;
|
||||
|
||||
/// 头像(大尺寸,圆角矩形)
|
||||
@property (nonatomic, strong) UIImageView *avatarImageView;
|
||||
|
||||
/// 昵称
|
||||
@property (nonatomic, strong) UILabel *nicknameLabel;
|
||||
|
||||
/// 等级标签
|
||||
@property (nonatomic, strong) UILabel *levelLabel;
|
||||
|
||||
/// 经验进度条容器
|
||||
@property (nonatomic, strong) UIView *progressContainer;
|
||||
|
||||
/// 经验进度条
|
||||
@property (nonatomic, strong) UIView *progressBar;
|
||||
|
||||
/// 经验文本
|
||||
@property (nonatomic, strong) UILabel *expLabel;
|
||||
|
||||
/// 统计容器
|
||||
@property (nonatomic, strong) UIView *statsContainer;
|
||||
|
||||
/// 关注数标签
|
||||
@property (nonatomic, strong) UILabel *followingLabel;
|
||||
|
||||
/// 粉丝数标签
|
||||
@property (nonatomic, strong) UILabel *followersLabel;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NewMineHeaderView
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self setupUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
// 更新渐变层大小
|
||||
self.gradientLayer.frame = self.bounds;
|
||||
}
|
||||
|
||||
// MARK: - Setup UI
|
||||
|
||||
- (void)setupUI {
|
||||
// 渐变背景(新的配色方案)
|
||||
self.gradientLayer = [CAGradientLayer layer];
|
||||
self.gradientLayer.colors = @[
|
||||
(id)[UIColor colorWithRed:0.2 green:0.6 blue:0.86 alpha:1.0].CGColor, // 主色调
|
||||
(id)[UIColor colorWithRed:0.3 green:0.5 blue:0.9 alpha:1.0].CGColor, // 渐变色
|
||||
];
|
||||
self.gradientLayer.startPoint = CGPointMake(0, 0);
|
||||
self.gradientLayer.endPoint = CGPointMake(1, 1);
|
||||
[self.layer insertSublayer:self.gradientLayer atIndex:0];
|
||||
|
||||
// 头像(大尺寸,圆角矩形)
|
||||
[self addSubview:self.avatarImageView];
|
||||
[self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.equalTo(self);
|
||||
make.top.equalTo(self).offset(40);
|
||||
make.size.mas_equalTo(CGSizeMake(80, 80));
|
||||
}];
|
||||
|
||||
// 昵称
|
||||
[self addSubview:self.nicknameLabel];
|
||||
[self.nicknameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.equalTo(self);
|
||||
make.top.equalTo(self.avatarImageView.mas_bottom).offset(15);
|
||||
}];
|
||||
|
||||
// 等级标签
|
||||
[self addSubview:self.levelLabel];
|
||||
[self.levelLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.equalTo(self);
|
||||
make.top.equalTo(self.nicknameLabel.mas_bottom).offset(8);
|
||||
}];
|
||||
|
||||
// 经验进度条容器
|
||||
[self addSubview:self.progressContainer];
|
||||
[self.progressContainer mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self).offset(40);
|
||||
make.right.equalTo(self).offset(-40);
|
||||
make.top.equalTo(self.levelLabel.mas_bottom).offset(15);
|
||||
make.height.mas_equalTo(8);
|
||||
}];
|
||||
|
||||
// 经验进度条
|
||||
[self.progressContainer addSubview:self.progressBar];
|
||||
[self.progressBar mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.top.bottom.equalTo(self.progressContainer);
|
||||
make.width.equalTo(self.progressContainer).multipliedBy(0.7); // 默认 70%
|
||||
}];
|
||||
|
||||
// 经验文本
|
||||
[self addSubview:self.expLabel];
|
||||
[self.expLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.equalTo(self);
|
||||
make.top.equalTo(self.progressContainer.mas_bottom).offset(6);
|
||||
}];
|
||||
|
||||
// 统计容器
|
||||
[self addSubview:self.statsContainer];
|
||||
[self.statsContainer mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self);
|
||||
make.top.equalTo(self.expLabel.mas_bottom).offset(20);
|
||||
make.height.mas_equalTo(60);
|
||||
make.bottom.equalTo(self).offset(-15);
|
||||
}];
|
||||
|
||||
// 关注数
|
||||
[self.statsContainer addSubview:self.followingLabel];
|
||||
[self.followingLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self.statsContainer.mas_centerX).offset(-20);
|
||||
make.centerY.equalTo(self.statsContainer);
|
||||
}];
|
||||
|
||||
// 粉丝数
|
||||
[self.statsContainer addSubview:self.followersLabel];
|
||||
[self.followersLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.statsContainer.mas_centerX).offset(20);
|
||||
make.centerY.equalTo(self.statsContainer);
|
||||
}];
|
||||
}
|
||||
|
||||
// MARK: - Public Methods
|
||||
|
||||
- (void)configureWithUserInfo:(NSDictionary *)userInfo {
|
||||
// 配置昵称
|
||||
self.nicknameLabel.text = userInfo[@"nickname"] ?: @"未设置昵称";
|
||||
|
||||
// 配置等级
|
||||
NSNumber *level = userInfo[@"level"];
|
||||
self.levelLabel.text = [NSString stringWithFormat:@"Lv.%@", level ?: @"0"];
|
||||
|
||||
// 配置经验
|
||||
NSNumber *exp = userInfo[@"exp"];
|
||||
NSNumber *nextLevelExp = userInfo[@"nextLevelExp"];
|
||||
CGFloat progress = [exp floatValue] / [nextLevelExp floatValue];
|
||||
[self.progressBar mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.top.bottom.equalTo(self.progressContainer);
|
||||
make.width.equalTo(self.progressContainer).multipliedBy(MAX(0.1, MIN(1.0, progress)));
|
||||
}];
|
||||
self.expLabel.text = [NSString stringWithFormat:@"%@ / %@", exp, nextLevelExp];
|
||||
|
||||
// 配置统计
|
||||
NSNumber *followers = userInfo[@"followers"];
|
||||
NSNumber *following = userInfo[@"following"];
|
||||
self.followingLabel.text = [NSString stringWithFormat:@"关注\n%@", following ?: @"0"];
|
||||
self.followersLabel.text = [NSString stringWithFormat:@"粉丝\n%@", followers ?: @"0"];
|
||||
|
||||
NSLog(@"[NewMineHeaderView] 用户信息已配置: %@", userInfo[@"nickname"]);
|
||||
}
|
||||
|
||||
// MARK: - Lazy Loading
|
||||
|
||||
- (UIImageView *)avatarImageView {
|
||||
if (!_avatarImageView) {
|
||||
_avatarImageView = [[UIImageView alloc] init];
|
||||
_avatarImageView.backgroundColor = [UIColor whiteColor];
|
||||
_avatarImageView.layer.cornerRadius = 16; // 圆角矩形
|
||||
_avatarImageView.layer.masksToBounds = YES;
|
||||
_avatarImageView.layer.borderWidth = 3;
|
||||
_avatarImageView.layer.borderColor = [UIColor whiteColor].CGColor;
|
||||
_avatarImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
}
|
||||
return _avatarImageView;
|
||||
}
|
||||
|
||||
- (UILabel *)nicknameLabel {
|
||||
if (!_nicknameLabel) {
|
||||
_nicknameLabel = [[UILabel alloc] init];
|
||||
_nicknameLabel.font = [UIFont systemFontOfSize:20 weight:UIFontWeightBold];
|
||||
_nicknameLabel.textColor = [UIColor whiteColor];
|
||||
_nicknameLabel.textAlignment = NSTextAlignmentCenter;
|
||||
}
|
||||
return _nicknameLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)levelLabel {
|
||||
if (!_levelLabel) {
|
||||
_levelLabel = [[UILabel alloc] init];
|
||||
_levelLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
||||
_levelLabel.textColor = [UIColor colorWithWhite:1.0 alpha:0.9];
|
||||
_levelLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_levelLabel.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.2];
|
||||
_levelLabel.layer.cornerRadius = 12;
|
||||
_levelLabel.layer.masksToBounds = YES;
|
||||
|
||||
// 添加内边距
|
||||
_levelLabel.text = @" Lv.0 ";
|
||||
}
|
||||
return _levelLabel;
|
||||
}
|
||||
|
||||
- (UIView *)progressContainer {
|
||||
if (!_progressContainer) {
|
||||
_progressContainer = [[UIView alloc] init];
|
||||
_progressContainer.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.3];
|
||||
_progressContainer.layer.cornerRadius = 4;
|
||||
_progressContainer.layer.masksToBounds = YES;
|
||||
}
|
||||
return _progressContainer;
|
||||
}
|
||||
|
||||
- (UIView *)progressBar {
|
||||
if (!_progressBar) {
|
||||
_progressBar = [[UIView alloc] init];
|
||||
_progressBar.backgroundColor = [UIColor whiteColor];
|
||||
_progressBar.layer.cornerRadius = 4;
|
||||
_progressBar.layer.masksToBounds = YES;
|
||||
}
|
||||
return _progressBar;
|
||||
}
|
||||
|
||||
- (UILabel *)expLabel {
|
||||
if (!_expLabel) {
|
||||
_expLabel = [[UILabel alloc] init];
|
||||
_expLabel.font = [UIFont systemFontOfSize:12];
|
||||
_expLabel.textColor = [UIColor colorWithWhite:1.0 alpha:0.8];
|
||||
_expLabel.textAlignment = NSTextAlignmentCenter;
|
||||
}
|
||||
return _expLabel;
|
||||
}
|
||||
|
||||
- (UIView *)statsContainer {
|
||||
if (!_statsContainer) {
|
||||
_statsContainer = [[UIView alloc] init];
|
||||
_statsContainer.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
return _statsContainer;
|
||||
}
|
||||
|
||||
- (UILabel *)followingLabel {
|
||||
if (!_followingLabel) {
|
||||
_followingLabel = [[UILabel alloc] init];
|
||||
_followingLabel.font = [UIFont systemFontOfSize:14];
|
||||
_followingLabel.textColor = [UIColor whiteColor];
|
||||
_followingLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_followingLabel.numberOfLines = 2;
|
||||
}
|
||||
return _followingLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)followersLabel {
|
||||
if (!_followersLabel) {
|
||||
_followersLabel = [[UILabel alloc] init];
|
||||
_followersLabel.font = [UIFont systemFontOfSize:14];
|
||||
_followersLabel.textColor = [UIColor whiteColor];
|
||||
_followersLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_followersLabel.numberOfLines = 2;
|
||||
}
|
||||
return _followersLabel;
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// NewMomentViewController.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import "BaseViewController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// 新的动态页面控制器
|
||||
/// 采用卡片式布局,完全不同于原 XPMomentsViewController
|
||||
@interface NewMomentViewController : BaseViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
240
YuMi/Modules/NewMoments/Controllers/NewMomentViewController.m
Normal file
240
YuMi/Modules/NewMoments/Controllers/NewMomentViewController.m
Normal file
@@ -0,0 +1,240 @@
|
||||
//
|
||||
// NewMomentViewController.m
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NewMomentViewController.h"
|
||||
#import "NewMomentCell.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
|
||||
@interface NewMomentViewController () <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
// MARK: - UI Components
|
||||
|
||||
/// 主列表(卡片式布局)
|
||||
@property (nonatomic, strong) UITableView *tableView;
|
||||
|
||||
/// 刷新控件
|
||||
@property (nonatomic, strong) UIRefreshControl *refreshControl;
|
||||
|
||||
/// 发布按钮
|
||||
@property (nonatomic, strong) UIButton *publishButton;
|
||||
|
||||
// MARK: - Data
|
||||
|
||||
/// 动态数据源
|
||||
@property (nonatomic, strong) NSMutableArray *dataSource;
|
||||
|
||||
/// 当前页码
|
||||
@property (nonatomic, assign) NSInteger currentPage;
|
||||
|
||||
/// 是否正在加载
|
||||
@property (nonatomic, assign) BOOL isLoading;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NewMomentViewController
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.title = @"动态";
|
||||
self.view.backgroundColor = [UIColor colorWithRed:0.96 green:0.96 blue:0.96 alpha:1.0]; // 浅灰背景
|
||||
|
||||
[self setupUI];
|
||||
[self loadData];
|
||||
|
||||
NSLog(@"[NewMomentViewController] 页面加载完成");
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// 隐藏导航栏(如果需要沉浸式体验)
|
||||
// [self.navigationController setNavigationBarHidden:YES animated:animated];
|
||||
}
|
||||
|
||||
// MARK: - Setup UI
|
||||
|
||||
- (void)setupUI {
|
||||
// TableView
|
||||
[self.view addSubview:self.tableView];
|
||||
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self.view);
|
||||
}];
|
||||
|
||||
// 发布按钮(悬浮在右下角)
|
||||
[self.view addSubview:self.publishButton];
|
||||
[self.publishButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self.view).offset(-20);
|
||||
make.bottom.equalTo(self.view).offset(-100); // 避开 TabBar
|
||||
make.size.mas_equalTo(CGSizeMake(56, 56));
|
||||
}];
|
||||
|
||||
NSLog(@"[NewMomentViewController] UI 设置完成");
|
||||
}
|
||||
|
||||
// MARK: - Data Loading
|
||||
|
||||
- (void)loadData {
|
||||
if (self.isLoading) return;
|
||||
|
||||
self.isLoading = YES;
|
||||
NSLog(@"[NewMomentViewController] 开始加载数据,页码: %ld", (long)self.currentPage);
|
||||
|
||||
// TODO: 调用 API 加载动态数据
|
||||
// 暂时使用模拟数据
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
// 模拟数据
|
||||
for (int i = 0; i < 10; i++) {
|
||||
NSDictionary *mockData = @{
|
||||
@"id": @(self.currentPage * 10 + i),
|
||||
@"content": [NSString stringWithFormat:@"这是第 %ld 页的第 %d 条动态", (long)self.currentPage, i],
|
||||
@"images": @[],
|
||||
@"likeCount": @(arc4random() % 100),
|
||||
@"commentCount": @(arc4random() % 50),
|
||||
};
|
||||
[self.dataSource addObject:mockData];
|
||||
}
|
||||
|
||||
self.currentPage++;
|
||||
self.isLoading = NO;
|
||||
[self.tableView reloadData];
|
||||
[self.refreshControl endRefreshing];
|
||||
|
||||
NSLog(@"[NewMomentViewController] 数据加载完成,当前数据量: %lu", (unsigned long)self.dataSource.count);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)onRefresh {
|
||||
self.currentPage = 0;
|
||||
[self.dataSource removeAllObjects];
|
||||
[self loadData];
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
- (void)onPublishButtonTapped {
|
||||
NSLog(@"[NewMomentViewController] 发布按钮点击");
|
||||
// TODO: 跳转到发布页面
|
||||
[self showAlertWithMessage:@"发布功能开发中"];
|
||||
}
|
||||
|
||||
- (void)showAlertWithMessage:(NSString *)message {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示"
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.dataSource.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
NewMomentCell *cell = [tableView dequeueReusableCellWithIdentifier:@"NewMomentCell" forIndexPath:indexPath];
|
||||
|
||||
if (indexPath.row < self.dataSource.count) {
|
||||
NSDictionary *data = self.dataSource[indexPath.row];
|
||||
[cell configureWithData:data];
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
|
||||
NSLog(@"[NewMomentViewController] 点击动态: %ld", (long)indexPath.row);
|
||||
// TODO: 跳转到详情页
|
||||
[self showAlertWithMessage:[NSString stringWithFormat:@"点击了第 %ld 条动态", (long)indexPath.row]];
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return UITableViewAutomaticDimension;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return 200;
|
||||
}
|
||||
|
||||
// 滚动到底部时加载更多
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
CGFloat offsetY = scrollView.contentOffset.y;
|
||||
CGFloat contentHeight = scrollView.contentSize.height;
|
||||
CGFloat screenHeight = scrollView.frame.size.height;
|
||||
|
||||
if (offsetY > contentHeight - screenHeight - 100 && !self.isLoading) {
|
||||
[self loadData];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Lazy Loading
|
||||
|
||||
- (UITableView *)tableView {
|
||||
if (!_tableView) {
|
||||
_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
||||
_tableView.delegate = self;
|
||||
_tableView.dataSource = self;
|
||||
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
_tableView.backgroundColor = self.view.backgroundColor;
|
||||
_tableView.estimatedRowHeight = 200;
|
||||
_tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
_tableView.showsVerticalScrollIndicator = NO;
|
||||
_tableView.contentInset = UIEdgeInsetsMake(10, 0, 10, 0);
|
||||
|
||||
// 注册 Cell
|
||||
[_tableView registerClass:[NewMomentCell class] forCellReuseIdentifier:@"NewMomentCell"];
|
||||
|
||||
// 添加下拉刷新
|
||||
_tableView.refreshControl = self.refreshControl;
|
||||
}
|
||||
return _tableView;
|
||||
}
|
||||
|
||||
- (UIRefreshControl *)refreshControl {
|
||||
if (!_refreshControl) {
|
||||
_refreshControl = [[UIRefreshControl alloc] init];
|
||||
[_refreshControl addTarget:self action:@selector(onRefresh) forControlEvents:UIControlEventValueChanged];
|
||||
}
|
||||
return _refreshControl;
|
||||
}
|
||||
|
||||
- (UIButton *)publishButton {
|
||||
if (!_publishButton) {
|
||||
_publishButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
_publishButton.backgroundColor = [UIColor colorWithRed:0.2 green:0.6 blue:0.86 alpha:1.0]; // 主色调
|
||||
_publishButton.layer.cornerRadius = 28;
|
||||
_publishButton.layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
_publishButton.layer.shadowOffset = CGSizeMake(0, 2);
|
||||
_publishButton.layer.shadowOpacity = 0.3;
|
||||
_publishButton.layer.shadowRadius = 4;
|
||||
|
||||
// 设置图标(暂时使用文字)
|
||||
[_publishButton setTitle:@"+" forState:UIControlStateNormal];
|
||||
_publishButton.titleLabel.font = [UIFont systemFontOfSize:32 weight:UIFontWeightLight];
|
||||
[_publishButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
|
||||
[_publishButton addTarget:self action:@selector(onPublishButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _publishButton;
|
||||
}
|
||||
|
||||
- (NSMutableArray *)dataSource {
|
||||
if (!_dataSource) {
|
||||
_dataSource = [NSMutableArray array];
|
||||
}
|
||||
return _dataSource;
|
||||
}
|
||||
|
||||
@end
|
23
YuMi/Modules/NewMoments/Views/NewMomentCell.h
Normal file
23
YuMi/Modules/NewMoments/Views/NewMomentCell.h
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// NewMomentCell.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// 新的动态 Cell(卡片式设计)
|
||||
/// 完全不同于原 XPMomentsCell 的列表式设计
|
||||
@interface NewMomentCell : UITableViewCell
|
||||
|
||||
/// 配置 Cell 数据
|
||||
/// @param data 动态数据字典
|
||||
- (void)configureWithData:(NSDictionary *)data;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
276
YuMi/Modules/NewMoments/Views/NewMomentCell.m
Normal file
276
YuMi/Modules/NewMoments/Views/NewMomentCell.m
Normal file
@@ -0,0 +1,276 @@
|
||||
//
|
||||
// NewMomentCell.m
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NewMomentCell.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
|
||||
@interface NewMomentCell ()
|
||||
|
||||
// MARK: - UI Components
|
||||
|
||||
/// 卡片容器
|
||||
@property (nonatomic, strong) UIView *cardView;
|
||||
|
||||
/// 头像
|
||||
@property (nonatomic, strong) UIImageView *avatarImageView;
|
||||
|
||||
/// 用户名
|
||||
@property (nonatomic, strong) UILabel *nameLabel;
|
||||
|
||||
/// 时间标签
|
||||
@property (nonatomic, strong) UILabel *timeLabel;
|
||||
|
||||
/// 内容标签
|
||||
@property (nonatomic, strong) UILabel *contentLabel;
|
||||
|
||||
/// 图片容器(可选)
|
||||
@property (nonatomic, strong) UIView *imagesContainer;
|
||||
|
||||
/// 底部操作栏
|
||||
@property (nonatomic, strong) UIView *actionBar;
|
||||
|
||||
/// 点赞按钮
|
||||
@property (nonatomic, strong) UIButton *likeButton;
|
||||
|
||||
/// 评论按钮
|
||||
@property (nonatomic, strong) UIButton *commentButton;
|
||||
|
||||
/// 分享按钮
|
||||
@property (nonatomic, strong) UIButton *shareButton;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NewMomentCell
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
|
||||
self.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
[self setupUI];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// MARK: - Setup UI
|
||||
|
||||
- (void)setupUI {
|
||||
// 卡片容器(圆角矩形 + 阴影)
|
||||
[self.contentView addSubview:self.cardView];
|
||||
[self.cardView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.contentView).offset(15);
|
||||
make.right.equalTo(self.contentView).offset(-15);
|
||||
make.top.equalTo(self.contentView).offset(8);
|
||||
make.bottom.equalTo(self.contentView).offset(-8);
|
||||
}];
|
||||
|
||||
// 头像(圆角矩形,不是圆形!)
|
||||
[self.cardView addSubview:self.avatarImageView];
|
||||
[self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.cardView).offset(15);
|
||||
make.top.equalTo(self.cardView).offset(15);
|
||||
make.size.mas_equalTo(CGSizeMake(40, 40));
|
||||
}];
|
||||
|
||||
// 用户名
|
||||
[self.cardView addSubview:self.nameLabel];
|
||||
[self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.avatarImageView.mas_right).offset(10);
|
||||
make.top.equalTo(self.avatarImageView);
|
||||
make.right.equalTo(self.cardView).offset(-15);
|
||||
}];
|
||||
|
||||
// 时间
|
||||
[self.cardView addSubview:self.timeLabel];
|
||||
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.nameLabel);
|
||||
make.bottom.equalTo(self.avatarImageView);
|
||||
make.right.equalTo(self.cardView).offset(-15);
|
||||
}];
|
||||
|
||||
// 内容
|
||||
[self.cardView addSubview:self.contentLabel];
|
||||
[self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.cardView).offset(15);
|
||||
make.right.equalTo(self.cardView).offset(-15);
|
||||
make.top.equalTo(self.avatarImageView.mas_bottom).offset(12);
|
||||
}];
|
||||
|
||||
// 底部操作栏
|
||||
[self.cardView addSubview:self.actionBar];
|
||||
[self.actionBar mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.right.equalTo(self.cardView);
|
||||
make.top.equalTo(self.contentLabel.mas_bottom).offset(15);
|
||||
make.height.mas_equalTo(50);
|
||||
make.bottom.equalTo(self.cardView).offset(-8);
|
||||
}];
|
||||
|
||||
// 点赞按钮
|
||||
[self.actionBar addSubview:self.likeButton];
|
||||
[self.likeButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.actionBar).offset(15);
|
||||
make.centerY.equalTo(self.actionBar);
|
||||
make.width.mas_greaterThanOrEqualTo(60);
|
||||
}];
|
||||
|
||||
// 评论按钮
|
||||
[self.actionBar addSubview:self.commentButton];
|
||||
[self.commentButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.centerX.equalTo(self.actionBar);
|
||||
make.centerY.equalTo(self.actionBar);
|
||||
make.width.mas_greaterThanOrEqualTo(60);
|
||||
}];
|
||||
|
||||
// 分享按钮
|
||||
[self.actionBar addSubview:self.shareButton];
|
||||
[self.shareButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.right.equalTo(self.actionBar).offset(-15);
|
||||
make.centerY.equalTo(self.actionBar);
|
||||
make.width.mas_greaterThanOrEqualTo(60);
|
||||
}];
|
||||
}
|
||||
|
||||
// MARK: - Public Methods
|
||||
|
||||
- (void)configureWithData:(NSDictionary *)data {
|
||||
// 配置用户名
|
||||
self.nameLabel.text = data[@"userName"] ?: @"匿名用户";
|
||||
|
||||
// 配置时间
|
||||
self.timeLabel.text = @"2小时前"; // TODO: 实际计算时间
|
||||
|
||||
// 配置内容
|
||||
self.contentLabel.text = data[@"content"] ?: @"";
|
||||
|
||||
// 配置点赞数
|
||||
NSNumber *likeCount = data[@"likeCount"];
|
||||
[self.likeButton setTitle:[NSString stringWithFormat:@"👍 %@", likeCount ?: @"0"] forState:UIControlStateNormal];
|
||||
|
||||
// 配置评论数
|
||||
NSNumber *commentCount = data[@"commentCount"];
|
||||
[self.commentButton setTitle:[NSString stringWithFormat:@"💬 %@", commentCount ?: @"0"] forState:UIControlStateNormal];
|
||||
|
||||
// 配置分享
|
||||
[self.shareButton setTitle:@"🔗 分享" forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
- (void)onLikeButtonTapped {
|
||||
NSLog(@"[NewMomentCell] 点赞");
|
||||
// TODO: 实现点赞逻辑
|
||||
}
|
||||
|
||||
- (void)onCommentButtonTapped {
|
||||
NSLog(@"[NewMomentCell] 评论");
|
||||
// TODO: 实现评论逻辑
|
||||
}
|
||||
|
||||
- (void)onShareButtonTapped {
|
||||
NSLog(@"[NewMomentCell] 分享");
|
||||
// TODO: 实现分享逻辑
|
||||
}
|
||||
|
||||
// MARK: - Lazy Loading
|
||||
|
||||
- (UIView *)cardView {
|
||||
if (!_cardView) {
|
||||
_cardView = [[UIView alloc] init];
|
||||
_cardView.backgroundColor = [UIColor whiteColor];
|
||||
_cardView.layer.cornerRadius = 12; // 圆角
|
||||
_cardView.layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
_cardView.layer.shadowOffset = CGSizeMake(0, 2);
|
||||
_cardView.layer.shadowOpacity = 0.1;
|
||||
_cardView.layer.shadowRadius = 8;
|
||||
_cardView.layer.masksToBounds = NO;
|
||||
}
|
||||
return _cardView;
|
||||
}
|
||||
|
||||
- (UIImageView *)avatarImageView {
|
||||
if (!_avatarImageView) {
|
||||
_avatarImageView = [[UIImageView alloc] init];
|
||||
_avatarImageView.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0];
|
||||
_avatarImageView.layer.cornerRadius = 8; // 圆角矩形,不是圆形!
|
||||
_avatarImageView.layer.masksToBounds = YES;
|
||||
_avatarImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
}
|
||||
return _avatarImageView;
|
||||
}
|
||||
|
||||
- (UILabel *)nameLabel {
|
||||
if (!_nameLabel) {
|
||||
_nameLabel = [[UILabel alloc] init];
|
||||
_nameLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightMedium];
|
||||
_nameLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1.0];
|
||||
}
|
||||
return _nameLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)timeLabel {
|
||||
if (!_timeLabel) {
|
||||
_timeLabel = [[UILabel alloc] init];
|
||||
_timeLabel.font = [UIFont systemFontOfSize:12];
|
||||
_timeLabel.textColor = [UIColor colorWithWhite:0.6 alpha:1.0];
|
||||
}
|
||||
return _timeLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)contentLabel {
|
||||
if (!_contentLabel) {
|
||||
_contentLabel = [[UILabel alloc] init];
|
||||
_contentLabel.font = [UIFont systemFontOfSize:15];
|
||||
_contentLabel.textColor = [UIColor colorWithWhite:0.3 alpha:1.0];
|
||||
_contentLabel.numberOfLines = 0;
|
||||
_contentLabel.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
}
|
||||
return _contentLabel;
|
||||
}
|
||||
|
||||
- (UIView *)actionBar {
|
||||
if (!_actionBar) {
|
||||
_actionBar = [[UIView alloc] init];
|
||||
_actionBar.backgroundColor = [UIColor colorWithWhite:0.98 alpha:1.0];
|
||||
}
|
||||
return _actionBar;
|
||||
}
|
||||
|
||||
- (UIButton *)likeButton {
|
||||
if (!_likeButton) {
|
||||
_likeButton = [self createActionButtonWithTitle:@"👍 0"];
|
||||
[_likeButton addTarget:self action:@selector(onLikeButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _likeButton;
|
||||
}
|
||||
|
||||
- (UIButton *)commentButton {
|
||||
if (!_commentButton) {
|
||||
_commentButton = [self createActionButtonWithTitle:@"💬 0"];
|
||||
[_commentButton addTarget:self action:@selector(onCommentButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _commentButton;
|
||||
}
|
||||
|
||||
- (UIButton *)shareButton {
|
||||
if (!_shareButton) {
|
||||
_shareButton = [self createActionButtonWithTitle:@"🔗 分享"];
|
||||
[_shareButton addTarget:self action:@selector(onShareButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
return _shareButton;
|
||||
}
|
||||
|
||||
- (UIButton *)createActionButtonWithTitle:(NSString *)title {
|
||||
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[button setTitle:title forState:UIControlStateNormal];
|
||||
button.titleLabel.font = [UIFont systemFontOfSize:13];
|
||||
[button setTitleColor:[UIColor colorWithWhite:0.5 alpha:1.0] forState:UIControlStateNormal];
|
||||
return button;
|
||||
}
|
||||
|
||||
@end
|
192
YuMi/Modules/NewTabBar/NewTabBarController.swift
Normal file
192
YuMi/Modules/NewTabBar/NewTabBarController.swift
Normal file
@@ -0,0 +1,192 @@
|
||||
//
|
||||
// NewTabBarController.swift
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
/// 新的 TabBar 控制器
|
||||
/// 只包含 Moment 和 Mine 两个 Tab
|
||||
class NewTabBarController: UITabBarController {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// 全局事件管理器
|
||||
private var globalEventManager: GlobalEventManager?
|
||||
|
||||
/// 是否已登录
|
||||
private var isLoggedIn: Bool = false
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// 测试域名配置
|
||||
#if DEBUG
|
||||
APIConfig.testEncryption()
|
||||
#endif
|
||||
|
||||
setupTabBarAppearance()
|
||||
setupGlobalManagers()
|
||||
setupInitialViewControllers()
|
||||
|
||||
NSLog("[NewTabBarController] 初始化完成")
|
||||
}
|
||||
|
||||
deinit {
|
||||
globalEventManager?.removeAllDelegates()
|
||||
NSLog("[NewTabBarController] 已释放")
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
/// 设置 TabBar 外观
|
||||
private func setupTabBarAppearance() {
|
||||
// 自定义 TabBar 样式
|
||||
tabBar.tintColor = UIColor(red: 0.2, green: 0.6, blue: 0.86, alpha: 1.0) // 新主色调
|
||||
tabBar.unselectedItemTintColor = UIColor(white: 0.6, alpha: 1.0) // 新辅助色
|
||||
tabBar.backgroundColor = .white
|
||||
tabBar.isTranslucent = false
|
||||
|
||||
// 添加顶部分割线
|
||||
if #available(iOS 13.0, *) {
|
||||
let appearance = UITabBarAppearance()
|
||||
appearance.configureWithOpaqueBackground()
|
||||
appearance.backgroundColor = .white
|
||||
appearance.stackedLayoutAppearance.selected.iconColor = tabBar.tintColor
|
||||
appearance.stackedLayoutAppearance.selected.titleTextAttributes = [
|
||||
.foregroundColor: tabBar.tintColor ?? .blue,
|
||||
.font: UIFont.systemFont(ofSize: 10, weight: .medium)
|
||||
]
|
||||
appearance.stackedLayoutAppearance.normal.titleTextAttributes = [
|
||||
.foregroundColor: tabBar.unselectedItemTintColor ?? .gray,
|
||||
.font: UIFont.systemFont(ofSize: 10)
|
||||
]
|
||||
|
||||
tabBar.standardAppearance = appearance
|
||||
if #available(iOS 15.0, *) {
|
||||
tabBar.scrollEdgeAppearance = appearance
|
||||
}
|
||||
}
|
||||
|
||||
NSLog("[NewTabBarController] TabBar 外观设置完成")
|
||||
}
|
||||
|
||||
/// 设置全局管理器
|
||||
private func setupGlobalManagers() {
|
||||
globalEventManager = GlobalEventManager.shared()
|
||||
globalEventManager?.setupSDKDelegates()
|
||||
|
||||
// 设置房间最小化视图
|
||||
if let containerView = view {
|
||||
globalEventManager?.setupRoomMiniView(on: containerView)
|
||||
}
|
||||
|
||||
// 注册社交分享回调
|
||||
globalEventManager?.registerSocialShareCallback()
|
||||
|
||||
NSLog("[NewTabBarController] 全局管理器设置完成")
|
||||
}
|
||||
|
||||
/// 设置初始 ViewController(未登录状态)
|
||||
private func setupInitialViewControllers() {
|
||||
// TODO: 暂时使用空白页面占位
|
||||
let blankVC1 = UIViewController()
|
||||
blankVC1.view.backgroundColor = .white
|
||||
blankVC1.tabBarItem = createTabBarItem(
|
||||
title: "动态",
|
||||
normalImage: "tab_moment_normal",
|
||||
selectedImage: "tab_moment_selected"
|
||||
)
|
||||
|
||||
let blankVC2 = UIViewController()
|
||||
blankVC2.view.backgroundColor = .white
|
||||
blankVC2.tabBarItem = createTabBarItem(
|
||||
title: "我的",
|
||||
normalImage: "tab_mine_normal",
|
||||
selectedImage: "tab_mine_selected"
|
||||
)
|
||||
|
||||
viewControllers = [blankVC1, blankVC2]
|
||||
selectedIndex = 0
|
||||
|
||||
NSLog("[NewTabBarController] 初始 ViewControllers 设置完成")
|
||||
}
|
||||
|
||||
/// 创建 TabBarItem
|
||||
/// - Parameters:
|
||||
/// - title: 标题
|
||||
/// - normalImage: 未选中图标名称
|
||||
/// - selectedImage: 选中图标名称
|
||||
/// - Returns: UITabBarItem
|
||||
private func createTabBarItem(title: String, normalImage: String, selectedImage: String) -> UITabBarItem {
|
||||
let item = UITabBarItem(
|
||||
title: title,
|
||||
image: UIImage(named: normalImage)?.withRenderingMode(.alwaysOriginal),
|
||||
selectedImage: UIImage(named: selectedImage)?.withRenderingMode(.alwaysOriginal)
|
||||
)
|
||||
return item
|
||||
}
|
||||
|
||||
// MARK: - Public Methods
|
||||
|
||||
/// 登录成功后刷新 TabBar
|
||||
/// - Parameter isLogin: 是否已登录
|
||||
@objc func refreshTabBar(isLogin: Bool) {
|
||||
isLoggedIn = isLogin
|
||||
|
||||
if isLogin {
|
||||
setupLoggedInViewControllers()
|
||||
} else {
|
||||
setupInitialViewControllers()
|
||||
}
|
||||
|
||||
NSLog("[NewTabBarController] TabBar 已刷新,登录状态: \(isLogin)")
|
||||
}
|
||||
|
||||
/// 设置登录后的 ViewControllers
|
||||
private func setupLoggedInViewControllers() {
|
||||
// 创建真实的 ViewController(OC 类)
|
||||
let momentVC = NewMomentViewController()
|
||||
momentVC.tabBarItem = createTabBarItem(
|
||||
title: "动态",
|
||||
normalImage: "tab_moment_normal",
|
||||
selectedImage: "tab_moment_selected"
|
||||
)
|
||||
|
||||
let mineVC = NewMineViewController()
|
||||
mineVC.tabBarItem = createTabBarItem(
|
||||
title: "我的",
|
||||
normalImage: "tab_mine_normal",
|
||||
selectedImage: "tab_mine_selected"
|
||||
)
|
||||
|
||||
viewControllers = [momentVC, mineVC]
|
||||
selectedIndex = 0
|
||||
|
||||
NSLog("[NewTabBarController] 登录后 ViewControllers 设置完成 - Moment & Mine")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITabBarControllerDelegate
|
||||
|
||||
extension NewTabBarController: UITabBarControllerDelegate {
|
||||
|
||||
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
|
||||
NSLog("[NewTabBarController] 选中 Tab: \(item.title ?? "Unknown")")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - OC Compatibility
|
||||
|
||||
extension NewTabBarController {
|
||||
|
||||
/// OC 兼容:创建实例的工厂方法
|
||||
@objc static func create() -> NewTabBarController {
|
||||
return NewTabBarController()
|
||||
}
|
||||
}
|
53
YuMi/YuMi-Bridging-Header.h
Normal file
53
YuMi/YuMi-Bridging-Header.h
Normal file
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// YuMi-Bridging-Header.h
|
||||
// YuMi
|
||||
//
|
||||
// Created by AI on 2025-10-09.
|
||||
// Copyright © 2025 YuMi. All rights reserved.
|
||||
//
|
||||
// Swift/OC 混编桥接头文件
|
||||
|
||||
#ifndef YuMi_Bridging_Header_h
|
||||
#define YuMi_Bridging_Header_h
|
||||
|
||||
// MARK: - Network
|
||||
#import "HttpRequestHelper.h"
|
||||
#import "Api.h"
|
||||
|
||||
// MARK: - Models
|
||||
#import "UserInfoModel.h"
|
||||
#import "BaseModel.h"
|
||||
|
||||
// MARK: - Managers
|
||||
#import "RoomBoomManager.h"
|
||||
#import "PublicRoomManager.h"
|
||||
#import "XPSkillCardPlayerManager.h"
|
||||
#import "RtcManager.h"
|
||||
#import "IAPManager.h"
|
||||
#import "SocialShareManager.h"
|
||||
|
||||
// MARK: - Views
|
||||
#import "XPMiniRoomView.h"
|
||||
#import "XPRoomMiniManager.h"
|
||||
|
||||
// MARK: - Third Party SDKs
|
||||
#import <NIMSDK/NIMSDK.h>
|
||||
#import <AFNetworking/AFNetworking.h>
|
||||
|
||||
// MARK: - Utils
|
||||
#import "YUMIConstant.h"
|
||||
#import "ClientConfig.h"
|
||||
#import "AccountInfoStorage.h"
|
||||
|
||||
// MARK: - UI Components
|
||||
#import "BaseViewController.h"
|
||||
#import "BaseNavigationController.h"
|
||||
|
||||
// MARK: - New Modules (White Label)
|
||||
#import "GlobalEventManager.h"
|
||||
#import "NewMomentViewController.h"
|
||||
#import "NewMomentCell.h"
|
||||
#import "NewMineViewController.h"
|
||||
#import "NewMineHeaderView.h"
|
||||
|
||||
#endif /* YuMi_Bridging_Header_h */
|
145
white-label-progress.md
Normal file
145
white-label-progress.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# 白牌项目改造进度
|
||||
|
||||
## 已完成(Phase 1 - Day 1)
|
||||
|
||||
### 1. 分支管理
|
||||
- ✅ 创建 `white-label-base` 分支
|
||||
- ✅ Swift 6.2 环境验证通过
|
||||
|
||||
### 2. API 域名动态生成(XOR + Base64)
|
||||
- ✅ 创建 `YuMi/Config/APIConfig.swift`
|
||||
- DEV 环境:自动使用原测试域名
|
||||
- RELEASE 环境:使用加密的新域名 `https://api.epartylive.com`
|
||||
- 加密值生成并验证成功
|
||||
- 包含降级方案
|
||||
|
||||
### 3. Swift/OC 混编配置
|
||||
- ✅ 创建 `YuMi/YuMi-Bridging-Header.h`
|
||||
- 引入必要的 OC 头文件
|
||||
- 支持 Network、Models、Managers、Views、SDKs
|
||||
|
||||
### 4. 全局事件管理器
|
||||
- ✅ 创建 `YuMi/Global/GlobalEventManager.h/m`
|
||||
- 迁移 NIMSDK 代理设置
|
||||
- 迁移房间最小化逻辑
|
||||
- 迁移全局通知处理
|
||||
- 迁移 RoomBoomManager 回调
|
||||
- 迁移社交分享回调
|
||||
|
||||
### 5. Swift TabBar 控制器
|
||||
- ✅ 创建 `YuMi/Modules/NewTabBar/NewTabBarController.swift`
|
||||
- 只包含 Moment 和 Mine 两个 Tab
|
||||
- 自定义新的 TabBar 样式(新主色调)
|
||||
- 集成 GlobalEventManager
|
||||
- 支持登录前/后状态切换
|
||||
|
||||
## 已完成(Phase 1 - Day 2-3)
|
||||
|
||||
### 1. Xcode 项目配置
|
||||
- ✅ 新文件自动添加到 Xcode 项目
|
||||
- ✅ Bridging Header 已更新,包含新模块
|
||||
- ✅ Swift/OC 混编配置完成
|
||||
|
||||
### 2. 创建 Moment 模块(OC)
|
||||
- ✅ 创建 NewMomentViewController.h/m
|
||||
- 列表式布局
|
||||
- 下拉刷新
|
||||
- 滚动加载更多
|
||||
- 发布按钮(右下角悬浮)
|
||||
- ✅ 创建 NewMomentCell.h/m
|
||||
- 卡片式设计(白色卡片 + 阴影)
|
||||
- 圆角矩形头像(不是圆形!)
|
||||
- 底部操作栏(点赞/评论/分享)
|
||||
- 使用模拟数据
|
||||
- ✅ 设计新的 UI 布局(完全不同)
|
||||
|
||||
### 3. 创建 Mine 模块(OC)
|
||||
- ✅ 创建 NewMineViewController.h/m
|
||||
- TableView 布局
|
||||
- 8 个菜单项
|
||||
- 设置按钮
|
||||
- ✅ 创建 NewMineHeaderView.h/m
|
||||
- 渐变背景(蓝色系)
|
||||
- 圆角矩形头像 + 白色边框
|
||||
- 昵称、等级、经验进度条
|
||||
- 关注/粉丝统计
|
||||
- 纵向卡片式设计
|
||||
- ✅ 设计新的 UI 布局(完全不同)
|
||||
|
||||
### 4. 集成到 TabBar
|
||||
- ✅ NewTabBarController 集成新模块
|
||||
- ✅ 支持登录前/后状态切换
|
||||
|
||||
## 下一步(Phase 1 - Day 4-5)
|
||||
|
||||
### 1. 编译测试
|
||||
- [ ] 构建项目,修复编译错误
|
||||
- [ ] 运行 App,测试基本功能
|
||||
- [ ] 检查 Console 日志
|
||||
|
||||
### 2. UI 资源准备
|
||||
- [ ] 准备 TabBar icon(4 张:2 tab × 2 状态)
|
||||
- [ ] 准备 Moment 模块图标(30-40 张)
|
||||
- [ ] 准备 Mine 模块图标(50-60 张)
|
||||
- [ ] 设计 AppIcon 和启动图
|
||||
|
||||
## 关键技术细节
|
||||
|
||||
### API 域名加密值
|
||||
```swift
|
||||
Release 域名加密值:
|
||||
"JTk5PT53YmI=", // https://
|
||||
"LD0kYw==", // api.
|
||||
"KD0sPzk0ISQ7KGMuIiA=", // epartylive.com
|
||||
|
||||
验证:https://api.epartylive.com ✅
|
||||
```
|
||||
|
||||
### 全局逻辑迁移清单
|
||||
|
||||
| 原位置 (TabbarViewController.m) | 功能 | 迁移目标 | 状态 |
|
||||
|----------------------------------|------|----------|------|
|
||||
| Line 156-159 | NIMSDK delegates | GlobalEventManager | ✅ |
|
||||
| Line 164-167 | 房间最小化通知 | GlobalEventManager | ✅ |
|
||||
| Line 169-178 | 配置重载通知 | GlobalEventManager | ✅ |
|
||||
| Line 179-181 | 充值/主播卡片通知 | GlobalEventManager | ✅ |
|
||||
| Line 190-200 | RoomBoomManager | GlobalEventManager | ✅ |
|
||||
| Line 202 | 社交回调 | GlobalEventManager | ✅ |
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 新建文件
|
||||
1. `YuMi/Config/APIConfig.swift`
|
||||
2. `YuMi/YuMi-Bridging-Header.h`
|
||||
3. `YuMi/Global/GlobalEventManager.h`
|
||||
4. `YuMi/Global/GlobalEventManager.m`
|
||||
5. `YuMi/Modules/NewTabBar/NewTabBarController.swift`
|
||||
|
||||
### 待创建文件(Day 2-5)
|
||||
1. `YuMi/Modules/NewMoments/Controllers/NewMomentViewController.h/m`
|
||||
2. `YuMi/Modules/NewMoments/Views/NewMomentCell.h/m`
|
||||
3. `YuMi/Modules/NewMine/Controllers/NewMineViewController.h/m`
|
||||
4. `YuMi/Modules/NewMine/Views/NewMineHeaderView.h/m`
|
||||
|
||||
## 注意事项
|
||||
|
||||
### Swift/OC 混编
|
||||
- 所有需要在 Swift 中使用的 OC 类都要加入 Bridging Header
|
||||
- Swift 类要暴露给 OC 需要用 `@objc` 标记
|
||||
- Xcode 会自动生成 `YuMi-Swift.h`,OC 代码通过它引入 Swift 类
|
||||
|
||||
### 编译问题排查
|
||||
如果编译失败,检查:
|
||||
1. Bridging Header 路径是否正确
|
||||
2. 所有引用的 OC 类是否存在
|
||||
3. Build Settings 中的 DEFINES_MODULE 是否为 YES
|
||||
4. Swift 版本是否匹配
|
||||
|
||||
### API 域名测试
|
||||
DEBUG 模式下可以调用 `APIConfig.testEncryption()` 验证加密解密是否正常。
|
||||
|
||||
---
|
||||
|
||||
**更新时间**: 2025-10-09
|
||||
**当前分支**: white-label-base
|
||||
**进度**: Phase 1 - Day 1 完成
|
183
white-label-test-guide.md
Normal file
183
white-label-test-guide.md
Normal file
@@ -0,0 +1,183 @@
|
||||
# 白牌项目测试指南
|
||||
|
||||
## 如何运行新的 TabBar
|
||||
|
||||
### 方式 1:在 AppDelegate 中替换根控制器(推荐)
|
||||
|
||||
在 `AppDelegate.m` 中找到设置根控制器的代码,临时替换为 NewTabBarController:
|
||||
|
||||
```objc
|
||||
#import "YuMi-Swift.h" // 引入 Swift 类
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
// ... 其他初始化代码
|
||||
|
||||
// 临时使用新的 TabBar(测试用)
|
||||
NewTabBarController *tabBar = [NewTabBarController create];
|
||||
[tabBar refreshTabBarWithIsLogin:YES]; // 模拟已登录状态
|
||||
|
||||
self.window.rootViewController = tabBar;
|
||||
[self.window makeKeyAndVisible];
|
||||
|
||||
return YES;
|
||||
}
|
||||
```
|
||||
|
||||
### 方式 2:通过通知切换(推荐用于测试)
|
||||
|
||||
在任意位置发送通知切换到新 TabBar:
|
||||
|
||||
```objc
|
||||
#import "YuMi-Swift.h"
|
||||
|
||||
// 在某个按钮点击或测试代码中
|
||||
NewTabBarController *tabBar = [NewTabBarController create];
|
||||
[tabBar refreshTabBarWithIsLogin:YES];
|
||||
|
||||
UIWindow *window = [UIApplication sharedApplication].keyWindow;
|
||||
window.rootViewController = tabBar;
|
||||
```
|
||||
|
||||
## 测试清单
|
||||
|
||||
### Phase 1 - Day 1-3 测试(基础架构)
|
||||
|
||||
#### 1. APIConfig 域名测试
|
||||
|
||||
```swift
|
||||
// 在 Debug 模式下运行
|
||||
APIConfig.testEncryption()
|
||||
|
||||
// 检查 Console 输出:
|
||||
// Release 域名: https://api.epartylive.com
|
||||
// 当前环境域名: [测试域名]
|
||||
// 备用域名: [测试域名]
|
||||
```
|
||||
|
||||
#### 2. GlobalEventManager 测试
|
||||
|
||||
- [ ] 启动 App,检查 Console 是否输出:
|
||||
- `[GlobalEventManager] SDK 代理设置完成`
|
||||
- `[GlobalEventManager] 通知监听已设置`
|
||||
- `[GlobalEventManager] 房间最小化视图已添加`
|
||||
|
||||
#### 3. NewTabBarController 测试
|
||||
|
||||
- [ ] TabBar 正常显示(2 个 Tab)
|
||||
- [ ] Tab 切换流畅
|
||||
- [ ] Tab 图标正常显示(如果图片存在)
|
||||
- [ ] 主色调应用正确(蓝色系)
|
||||
|
||||
#### 4. NewMomentViewController 测试
|
||||
|
||||
- [ ] 页面正常加载
|
||||
- [ ] 列表正常显示(模拟数据)
|
||||
- [ ] 下拉刷新功能正常
|
||||
- [ ] 滚动到底部自动加载更多
|
||||
- [ ] 发布按钮显示在右下角
|
||||
- [ ] 点击 Cell 显示提示
|
||||
- [ ] 点击发布按钮显示提示
|
||||
|
||||
**UI 检查**:
|
||||
- [ ] 卡片式布局(白色卡片 + 阴影)
|
||||
- [ ] 圆角矩形头像(不是圆形!)
|
||||
- [ ] 底部操作栏(点赞/评论/分享)
|
||||
- [ ] 浅灰色背景
|
||||
- [ ] 15px 左右边距
|
||||
|
||||
#### 5. NewMineViewController 测试
|
||||
|
||||
- [ ] 页面正常加载
|
||||
- [ ] 顶部个人信息卡片显示
|
||||
- [ ] 渐变背景(蓝色渐变)
|
||||
- [ ] 头像(圆角矩形 + 白色边框)
|
||||
- [ ] 昵称、等级显示
|
||||
- [ ] 经验进度条正常
|
||||
- [ ] 关注/粉丝数显示
|
||||
- [ ] 菜单列表正常显示(8 个菜单项)
|
||||
- [ ] 点击菜单项显示提示
|
||||
- [ ] 右上角设置按钮正常
|
||||
|
||||
**UI 检查**:
|
||||
- [ ] 头部高度约 280px
|
||||
- [ ] 渐变背景(蓝色系)
|
||||
- [ ] 所有文字使用白色
|
||||
- [ ] 菜单项高度 56px
|
||||
- [ ] 菜单项带右箭头
|
||||
|
||||
## 预期效果
|
||||
|
||||
### 代码层面
|
||||
- Swift 文件:5 个(APIConfig, NewTabBarController 等)
|
||||
- OC 新文件:6 个(GlobalEventManager, Moment, Mine 模块)
|
||||
- 总新增代码:约 1500 行
|
||||
- 代码相似度:预计 <20%(因为是全新代码)
|
||||
|
||||
### UI 层面
|
||||
- TabBar 只有 2 个 Tab(vs 原来的 5 个)
|
||||
- 完全不同的颜色方案(蓝色系)
|
||||
- 卡片式设计(vs 原来的列表式)
|
||||
- 圆角矩形头像(vs 原来的圆形)
|
||||
- 渐变背景(vs 原来的纯色)
|
||||
|
||||
### 网络层面
|
||||
- DEBUG:使用原测试域名
|
||||
- RELEASE:使用加密的新域名 `https://api.epartylive.com`
|
||||
- 代码中无明文域名
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 编译失败,提示找不到 Swift 类
|
||||
|
||||
**A**: 检查以下配置:
|
||||
1. Build Settings → Defines Module = YES
|
||||
2. Build Settings → Swift Objc Bridging Header = YuMi/YuMi-Bridging-Header.h
|
||||
3. 清理项目:Cmd + Shift + K,然后重新编译
|
||||
|
||||
### Q2: 运行时 Crash,提示 "selector not recognized"
|
||||
|
||||
**A**: 检查:
|
||||
1. Swift 类是否标记了 `@objc`
|
||||
2. 方法是否标记了 `@objc`
|
||||
3. Bridging Header 是否包含了所有需要的 OC 头文件
|
||||
|
||||
### Q3: TabBar 显示但是是空白页面
|
||||
|
||||
**A**: 检查:
|
||||
1. NewMomentViewController 和 NewMineViewController 是否正确初始化
|
||||
2. Console 是否有错误日志
|
||||
3. 尝试直接 push 到这些 ViewController 测试
|
||||
|
||||
### Q4: 图片不显示
|
||||
|
||||
**A**:
|
||||
1. 图片资源还未添加(正常现象)
|
||||
2. 暂时使用 emoji 或文字代替
|
||||
3. 后续会添加新的图片资源
|
||||
|
||||
## 下一步
|
||||
|
||||
Phase 1 - Day 2-3 完成后,继续:
|
||||
|
||||
### Day 4-5: 完善 UI 细节
|
||||
- [ ] 添加真实的图片资源(100-150 张)
|
||||
- [ ] 完善动画效果
|
||||
- [ ] 优化交互体验
|
||||
|
||||
### Day 6-10: 网络层集成
|
||||
- [ ] 创建 HttpRequestHelper Category
|
||||
- [ ] 集成真实 API
|
||||
- [ ] 测试网络请求
|
||||
|
||||
### Day 11-15: 全面测试
|
||||
- [ ] 功能测试
|
||||
- [ ] 性能测试
|
||||
- [ ] 相似度检查
|
||||
- [ ] 准备提审
|
||||
|
||||
---
|
||||
|
||||
**更新时间**: 2025-10-09
|
||||
**当前进度**: Phase 1 - Day 2-3 完成
|
||||
**文件数量**: 11 个新文件
|
||||
**代码量**: ~1500 行
|
Reference in New Issue
Block a user