This commit is contained in:
2025-12-22 19:19:28 +08:00
parent 5cfc76e6c5
commit 4e6fd90668
20 changed files with 679 additions and 3 deletions

View File

@@ -57,6 +57,9 @@
#define API_THEME_RECOMMENDED @"/themes/recommended" // 推荐主题列表 #define API_THEME_RECOMMENDED @"/themes/recommended" // 推荐主题列表
#define API_THEME_SEARCH @"/themes/search" // 搜索主题themeName #define API_THEME_SEARCH @"/themes/search" // 搜索主题themeName
#define API_USER_THEMES_BATCH_DELETE @"/user-themes/batch-delete" // 批量删除用户主题 #define API_USER_THEMES_BATCH_DELETE @"/user-themes/batch-delete" // 批量删除用户主题
#define API_THEME_PURCHASE_LIST @"/themes/purchase/list" // 查询主题购买记录
#define API_THEME_RESTORE @"/themes/restore" // 恢复已删除的主题
#define API_WALLET_TRANSACTIONS @"/wallet/transactions" // 分页查询钱包交易记录
/// pay /// pay
#define API_VALIDATE_RECEIPT @"/apple/validate-receipt" // 排行榜标签列表 #define API_VALIDATE_RECEIPT @"/apple/validate-receipt" // 排行榜标签列表

View File

@@ -115,6 +115,9 @@
04890A052EC0BBBB00FABA60 /* KBCategoryTitleImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04890A032EC0BBBB00FABA60 /* KBCategoryTitleImageView.m */; }; 04890A052EC0BBBB00FABA60 /* KBCategoryTitleImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04890A032EC0BBBB00FABA60 /* KBCategoryTitleImageView.m */; };
04890B122EC2F00000FABA60 /* KBMyHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04890B112EC2F00000FABA60 /* KBMyHeaderView.m */; }; 04890B122EC2F00000FABA60 /* KBMyHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04890B112EC2F00000FABA60 /* KBMyHeaderView.m */; };
0498BD622EDFFC12006CC1D5 /* KBMyVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD612EDFFC12006CC1D5 /* KBMyVM.m */; }; 0498BD622EDFFC12006CC1D5 /* KBMyVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD612EDFFC12006CC1D5 /* KBMyVM.m */; };
A1F0C1B12F1234567890ABCD /* KBConsumptionRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = A1F0C1A12F1234567890ABCD /* KBConsumptionRecord.m */; };
A1F0C1B22F1234567890ABCD /* KBConsumptionRecordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A1F0C1A32F1234567890ABCD /* KBConsumptionRecordCell.m */; };
A1F0C1B32F1234567890ABCD /* KBConsumptionRecordVC.m in Sources */ = {isa = PBXBuildFile; fileRef = A1F0C1A52F1234567890ABCD /* KBConsumptionRecordVC.m */; };
0498BD652EE0116D006CC1D5 /* KBEmailLoginVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD642EE0116D006CC1D5 /* KBEmailLoginVC.m */; }; 0498BD652EE0116D006CC1D5 /* KBEmailLoginVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD642EE0116D006CC1D5 /* KBEmailLoginVC.m */; };
0498BD682EE01180006CC1D5 /* KBEmailRegistVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD672EE01180006CC1D5 /* KBEmailRegistVC.m */; }; 0498BD682EE01180006CC1D5 /* KBEmailRegistVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD672EE01180006CC1D5 /* KBEmailRegistVC.m */; };
0498BD6B2EE025FC006CC1D5 /* KBForgetPwdVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD6A2EE025FC006CC1D5 /* KBForgetPwdVC.m */; }; 0498BD6B2EE025FC006CC1D5 /* KBForgetPwdVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0498BD6A2EE025FC006CC1D5 /* KBForgetPwdVC.m */; };
@@ -401,9 +404,15 @@
05A1B2C72F5B1A2B3C4D5E60 /* KBSearchThemeModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSearchThemeModel.m; sourceTree = "<group>"; }; 05A1B2C72F5B1A2B3C4D5E60 /* KBSearchThemeModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSearchThemeModel.m; sourceTree = "<group>"; };
048908DE2EBF73DC00FABA60 /* MySkinVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MySkinVC.h; sourceTree = "<group>"; }; 048908DE2EBF73DC00FABA60 /* MySkinVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MySkinVC.h; sourceTree = "<group>"; };
048908DF2EBF73DC00FABA60 /* MySkinVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MySkinVC.m; sourceTree = "<group>"; }; 048908DF2EBF73DC00FABA60 /* MySkinVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MySkinVC.m; sourceTree = "<group>"; };
048908E12EBF760000FABA60 /* MySkinCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MySkinCell.h; sourceTree = "<group>"; }; 048908E12EBF760000FABA60 /* MySkinCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MySkinCell.h; sourceTree = "<group>"; };
048908E12EBF821700FABA60 /* KBSkinDetailVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinDetailVC.h; sourceTree = "<group>"; }; 048908E12EBF821700FABA60 /* KBSkinDetailVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinDetailVC.h; sourceTree = "<group>"; };
048908E22EBF760000FABA60 /* MySkinCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MySkinCell.m; sourceTree = "<group>"; }; 048908E22EBF760000FABA60 /* MySkinCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MySkinCell.m; sourceTree = "<group>"; };
A1F0C1A02F1234567890ABCD /* KBConsumptionRecord.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBConsumptionRecord.h; sourceTree = "<group>"; };
A1F0C1A12F1234567890ABCD /* KBConsumptionRecord.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBConsumptionRecord.m; sourceTree = "<group>"; };
A1F0C1A22F1234567890ABCD /* KBConsumptionRecordCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBConsumptionRecordCell.h; sourceTree = "<group>"; };
A1F0C1A32F1234567890ABCD /* KBConsumptionRecordCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBConsumptionRecordCell.m; sourceTree = "<group>"; };
A1F0C1A42F1234567890ABCD /* KBConsumptionRecordVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBConsumptionRecordVC.h; sourceTree = "<group>"; };
A1F0C1A52F1234567890ABCD /* KBConsumptionRecordVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBConsumptionRecordVC.m; sourceTree = "<group>"; };
048908E22EBF821700FABA60 /* KBSkinDetailVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinDetailVC.m; sourceTree = "<group>"; }; 048908E22EBF821700FABA60 /* KBSkinDetailVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinDetailVC.m; sourceTree = "<group>"; };
048908E42EBF841B00FABA60 /* KBSkinDetailTagCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinDetailTagCell.h; sourceTree = "<group>"; }; 048908E42EBF841B00FABA60 /* KBSkinDetailTagCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinDetailTagCell.h; sourceTree = "<group>"; };
048908E52EBF841B00FABA60 /* KBSkinDetailTagCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinDetailTagCell.m; sourceTree = "<group>"; }; 048908E52EBF841B00FABA60 /* KBSkinDetailTagCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinDetailTagCell.m; sourceTree = "<group>"; };
@@ -1335,6 +1344,8 @@
0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */, 0498BD8E2EE6A3BD006CC1D5 /* KBMyMainModel.m */,
7ECBD0E320F971D0FBEDD7BC /* KBMyTheme.h */, 7ECBD0E320F971D0FBEDD7BC /* KBMyTheme.h */,
180D662EC4DB3A7FFF83FF18 /* KBMyTheme.m */, 180D662EC4DB3A7FFF83FF18 /* KBMyTheme.m */,
A1F0C1A02F1234567890ABCD /* KBConsumptionRecord.h */,
A1F0C1A12F1234567890ABCD /* KBConsumptionRecord.m */,
); );
path = M; path = M;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1346,6 +1357,8 @@
049FB31C2EC21BCD00FAB05D /* KBMyKeyboardCell.m */, 049FB31C2EC21BCD00FAB05D /* KBMyKeyboardCell.m */,
048908E12EBF760000FABA60 /* MySkinCell.h */, 048908E12EBF760000FABA60 /* MySkinCell.h */,
048908E22EBF760000FABA60 /* MySkinCell.m */, 048908E22EBF760000FABA60 /* MySkinCell.m */,
A1F0C1A22F1234567890ABCD /* KBConsumptionRecordCell.h */,
A1F0C1A32F1234567890ABCD /* KBConsumptionRecordCell.m */,
048908E42EBF841B00FABA60 /* KBSkinDetailTagCell.h */, 048908E42EBF841B00FABA60 /* KBSkinDetailTagCell.h */,
048908E52EBF841B00FABA60 /* KBSkinDetailTagCell.m */, 048908E52EBF841B00FABA60 /* KBSkinDetailTagCell.m */,
048908E72EBF843000FABA60 /* KBSkinDetailHeaderCell.h */, 048908E72EBF843000FABA60 /* KBSkinDetailHeaderCell.h */,
@@ -1379,6 +1392,8 @@
049FB2192EC20A9E00FAB05D /* KBMyKeyBoardVC.m */, 049FB2192EC20A9E00FAB05D /* KBMyKeyBoardVC.m */,
048908DE2EBF73DC00FABA60 /* MySkinVC.h */, 048908DE2EBF73DC00FABA60 /* MySkinVC.h */,
048908DF2EBF73DC00FABA60 /* MySkinVC.m */, 048908DF2EBF73DC00FABA60 /* MySkinVC.m */,
A1F0C1A42F1234567890ABCD /* KBConsumptionRecordVC.h */,
A1F0C1A52F1234567890ABCD /* KBConsumptionRecordVC.m */,
049FB2212EC311F900FAB05D /* KBPersonInfoVC.h */, 049FB2212EC311F900FAB05D /* KBPersonInfoVC.h */,
049FB2222EC311F900FAB05D /* KBPersonInfoVC.m */, 049FB2222EC311F900FAB05D /* KBPersonInfoVC.m */,
04791F902ED48010004E8522 /* KBNoticeVC.h */, 04791F902ED48010004E8522 /* KBNoticeVC.h */,
@@ -1990,6 +2005,9 @@
048908BC2EBE1FCB00FABA60 /* BaseViewController.m in Sources */, 048908BC2EBE1FCB00FABA60 /* BaseViewController.m in Sources */,
0498BD902EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */, 0498BD902EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */,
471CAD3574798685B72ADD55 /* KBMyTheme.m in Sources */, 471CAD3574798685B72ADD55 /* KBMyTheme.m in Sources */,
A1F0C1B12F1234567890ABCD /* KBConsumptionRecord.m in Sources */,
A1F0C1B22F1234567890ABCD /* KBConsumptionRecordCell.m in Sources */,
A1F0C1B32F1234567890ABCD /* KBConsumptionRecordVC.m in Sources */,
04FC95D72EB1EA16007BD342 /* BaseTableView.m in Sources */, 04FC95D72EB1EA16007BD342 /* BaseTableView.m in Sources */,
0498BD712EE02A41006CC1D5 /* KBForgetPwdNewPwdVC.m in Sources */, 0498BD712EE02A41006CC1D5 /* KBForgetPwdNewPwdVC.m in Sources */,
048908EF2EBF861800FABA60 /* KBSkinSectionTitleCell.m in Sources */, 048908EF2EBF861800FABA60 /* KBSkinSectionTitleCell.m in Sources */,

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "my_chongzhi_bg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "my_chongzhi_bg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "my_record_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "my_record_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -0,0 +1,25 @@
//
// KBConsumptionRecord.h
// keyBoard
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// 消费/购买记录模型 1 消费 2充值
typedef NS_ENUM(NSInteger, KBConsumptionRecordType) {
KBConsumptionRecordTypeConsumption = 1,
KBConsumptionRecordTypeRecharge = 2,
};
@interface KBConsumptionRecord : NSObject
@property (nonatomic, strong, nullable) NSNumber *amount;
@property (nonatomic, assign) KBConsumptionRecordType type;
@property (nonatomic, strong, nullable) NSNumber *beforeBalance;
@property (nonatomic, strong, nullable) NSNumber *afterBalance;
@property (nonatomic, copy, nullable) NSString *kbdescription;
@property (nonatomic, copy, nullable) NSString *createdAt;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,16 @@
//
// KBConsumptionRecord.m
// keyBoard
//
#import "KBConsumptionRecord.h"
@implementation KBConsumptionRecord
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
// JSON: { "id": 0, "tagName": "xxx" }
// Model: tagId / tagName
return @{
@"kbdescription" : @"description",
};
}
@end

View File

@@ -0,0 +1,16 @@
//
// KBConsumptionRecordCell.h
// keyBoard
//
#import "BaseCell.h"
@class KBConsumptionRecord;
NS_ASSUME_NONNULL_BEGIN
@interface KBConsumptionRecordCell : BaseCell
+ (NSString *)reuseId;
- (void)configWithRecord:(KBConsumptionRecord *)record;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,123 @@
//
// KBConsumptionRecordCell.m
// keyBoard
//
#import "KBConsumptionRecordCell.h"
#import "KBConsumptionRecord.h"
#import <Masonry/Masonry.h>
#import "UIColor+Extension.h"
@interface KBConsumptionRecordCell ()
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *timeLabel;
@property (nonatomic, strong) UILabel *amountLabel;
@property (nonatomic, strong) UIView *lineView;
@end
@implementation KBConsumptionRecordCell
- (void)setupUI {
self.contentView.backgroundColor = [UIColor whiteColor];
[self.contentView addSubview:self.titleLabel];
[self.contentView addSubview:self.timeLabel];
[self.contentView addSubview:self.amountLabel];
[self.contentView addSubview:self.lineView];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.contentView).offset(16);
make.top.equalTo(self.contentView).offset(14);
make.right.lessThanOrEqualTo(self.amountLabel.mas_left).offset(-12);
}];
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.titleLabel);
make.top.equalTo(self.titleLabel.mas_bottom).offset(6);
make.bottom.equalTo(self.contentView).offset(-14);
make.right.lessThanOrEqualTo(self.amountLabel.mas_left).offset(-12);
}];
[self.amountLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.contentView).offset(-16);
make.centerY.equalTo(self.titleLabel);
}];
[self.lineView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.contentView).offset(16);
make.right.equalTo(self.contentView).offset(-16);
make.bottom.equalTo(self.contentView);
make.height.mas_equalTo(KB_ONE_PIXEL);
}];
}
- (void)configWithRecord:(KBConsumptionRecord *)record {
NSString *title = record.kbdescription.length ? record.kbdescription : KBLocalized(@"Consumption");
self.titleLabel.text = title;
self.timeLabel.text = record.createdAt.length ? record.createdAt : @"--";
NSString *displayAmount = [self kb_formatAmountText:record.amount];
self.amountLabel.text = displayAmount;
UIColor *incomeColor = [UIColor colorWithHex:0x66CD7C];
UIColor *expenseColor = [UIColor colorWithHex:0xCD2853];
self.amountLabel.textColor = (record.type == KBConsumptionRecordTypeRecharge) ? incomeColor : expenseColor;
}
- (NSString *)kb_formatAmountText:(NSNumber *)amount {
if (![amount isKindOfClass:NSNumber.class]) { return @"--"; }
static NSNumberFormatter *formatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
formatter.minimumFractionDigits = 2;
formatter.maximumFractionDigits = 2;
formatter.minimumIntegerDigits = 1;
formatter.positivePrefix = @"+";
});
return [formatter stringFromNumber:amount] ?: @"--";
}
#pragma mark - Lazy
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [UILabel new];
_titleLabel.font = [KBFont medium:15];
_titleLabel.textColor = [UIColor colorWithHex:KBBlackValue];
_titleLabel.text = KBLocalized(@"Consumption");
}
return _titleLabel;
}
- (UILabel *)timeLabel {
if (!_timeLabel) {
_timeLabel = [UILabel new];
_timeLabel.font = [KBFont regular:12];
_timeLabel.textColor = [UIColor colorWithHex:0x9FA5B5];
_timeLabel.text = @"--";
}
return _timeLabel;
}
- (UILabel *)amountLabel {
if (!_amountLabel) {
_amountLabel = [UILabel new];
_amountLabel.font = [KBFont medium:16];
_amountLabel.textColor = [UIColor colorWithHex:0xE36464];
_amountLabel.textAlignment = NSTextAlignmentRight;
_amountLabel.text = @"--";
}
return _amountLabel;
}
- (UIView *)lineView {
if (!_lineView) {
_lineView = [UIView new];
_lineView.backgroundColor = [UIColor colorWithHex:0xEFEFF1];
}
return _lineView;
}
@end

View File

@@ -0,0 +1,14 @@
//
// KBConsumptionRecordVC.h
// keyBoard
//
#import "BaseViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface KBConsumptionRecordVC : BaseViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,337 @@
//
// KBConsumptionRecordVC.m
// keyBoard
//
#import "KBConsumptionRecordVC.h"
#import "KBConsumptionRecord.h"
#import "KBConsumptionRecordCell.h"
#import "KBMyVM.h"
#import "KBShopVM.h"
#import "KBJfPay.h"
#import <Masonry/Masonry.h>
#import "UIColor+Extension.h"
#import "KBHUD.h"
#import <MJRefresh/MJRefresh.h>
@interface KBConsumptionRecordVC () <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) BaseTableView *tableView;
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UIView *cardView;
@property (nonatomic, strong) UILabel *pointsTitleLabel;
@property (nonatomic, strong) UILabel *pointsLabel;
@property (nonatomic, strong) UIImageView *pointsIconView;
@property (nonatomic, strong) UIButton *rechargeButton;
@property (nonatomic, strong) UIImageView *sectionIconView;
@property (nonatomic, strong) UILabel *sectionTitleLabel;
@property (nonatomic, strong) NSMutableArray<KBConsumptionRecord *> *records;
@property (nonatomic, strong) KBMyVM *viewModel;
@property (nonatomic, strong) KBShopVM *shopVM;
@property (nonatomic, strong) UIImageView *bgImageView; //
@property (nonatomic, assign) NSInteger pageNumber;
@property (nonatomic, assign) NSInteger pageSize;
@property (nonatomic, assign) BOOL isLoading;
@end
@implementation KBConsumptionRecordVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.kb_titleLabel.text = KBLocalized(@"Consumption Record");
self.kb_navView.backgroundColor = [UIColor clearColor];
self.bgImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"my_bg_icon"]];
self.bgImageView.contentMode = UIViewContentModeScaleAspectFill;
[self.view insertSubview:self.bgImageView belowSubview:self.kb_navView];
[self.bgImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
self.records = [NSMutableArray array];
[self.view addSubview:self.tableView];
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.bottom.equalTo(self.view);
make.top.equalTo(self.view).offset(KB_NAV_TOTAL_HEIGHT);
}];
self.tableView.tableHeaderView = self.headerView;
self.pageNumber = 1;
self.pageSize = 10;
[self setupRefresh];
[self fetchWalletBalance];
[self.tableView.mj_header beginRefreshing];
}
#pragma mark - Data
- (void)fetchWalletBalance {
__weak typeof(self) weakSelf = self;
[self.shopVM fetchWalletBalanceWithCompletion:^(NSString * _Nullable balance, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (!error && balance.length > 0) {
weakSelf.pointsLabel.text = balance;
}
});
}];
}
- (void)setupRefresh {
__weak typeof(self) weakSelf = self;
self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
[weakSelf refreshRecords];
}];
self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
[weakSelf loadMoreRecords];
}];
self.tableView.mj_footer.hidden = YES;
}
- (void)refreshRecords {
if (self.isLoading) {
[self.tableView.mj_header endRefreshing];
return;
}
self.pageNumber = 1;
[self.tableView.mj_footer resetNoMoreData];
[self fetchPurchaseRecordsIsRefresh:YES];
}
- (void)loadMoreRecords {
if (self.isLoading || self.tableView.mj_footer.state == MJRefreshStateNoMoreData) {
[self.tableView.mj_footer endRefreshing];
return;
}
self.pageNumber += 1;
[self fetchPurchaseRecordsIsRefresh:NO];
}
- (void)fetchPurchaseRecordsIsRefresh:(BOOL)isRefresh {
self.isLoading = YES;
BOOL showHUD = !self.tableView.mj_header.isRefreshing && !self.tableView.mj_footer.isRefreshing;
if (showHUD) {
[KBHUD show];
}
__weak typeof(self) weakSelf = self;
[self.viewModel fetchWalletTransactionsWithPage:self.pageNumber
pageSize:self.pageSize
completion:^(NSArray<KBConsumptionRecord *> * _Nullable records, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.isLoading = NO;
if (showHUD) {
[KBHUD dismiss];
}
if (isRefresh) {
[weakSelf.tableView.mj_header endRefreshing];
} else {
[weakSelf.tableView.mj_footer endRefreshing];
}
if (error) {
if (!isRefresh) {
weakSelf.pageNumber = MAX(1, weakSelf.pageNumber - 1);
}
NSString *msg = error.localizedDescription ?: KBLocalized(@"Network error");
[KBHUD showInfo:msg];
return;
}
if (isRefresh) {
[weakSelf.records removeAllObjects];
}
if (records.count > 0) {
[weakSelf.records addObjectsFromArray:records];
}
[weakSelf.tableView reloadData];
weakSelf.tableView.mj_footer.hidden = (weakSelf.records.count == 0);
if (records.count < weakSelf.pageSize) {
[weakSelf.tableView.mj_footer endRefreshingWithNoMoreData];
}
});
}];
}
#pragma mark - UITableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.records.count;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return KBFit(78);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
KBConsumptionRecordCell *cell = [tableView dequeueReusableCellWithIdentifier:[KBConsumptionRecordCell reuseId]
forIndexPath:indexPath];
if (indexPath.row < self.records.count) {
[cell configWithRecord:self.records[indexPath.row]];
}
return cell;
}
#pragma mark - Actions
- (void)onRecharge {
KBJfPay *vc = [[KBJfPay alloc] init];
[self.navigationController pushViewController:vc animated:YES];
}
#pragma mark - Lazy
- (BaseTableView *)tableView {
if (!_tableView) {
_tableView = [[BaseTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
_tableView.backgroundColor = [UIColor clearColor];
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableView.dataSource = self;
_tableView.delegate = self;
[_tableView registerClass:KBConsumptionRecordCell.class forCellReuseIdentifier:[KBConsumptionRecordCell reuseId]];
}
return _tableView;
}
- (UIView *)headerView {
if (!_headerView) {
CGFloat headerHeight = KBFit(210);
_headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KB_SCREEN_WIDTH, headerHeight)];
_headerView.backgroundColor = [UIColor clearColor];
[_headerView addSubview:self.cardView];
[_headerView addSubview:self.sectionIconView];
[_headerView addSubview:self.sectionTitleLabel];
[self.cardView addSubview:self.pointsTitleLabel];
[self.cardView addSubview:self.pointsIconView];
[self.cardView addSubview:self.pointsLabel];
[self.cardView addSubview:self.rechargeButton];
[self.cardView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(_headerView).offset(16);
make.right.equalTo(_headerView).offset(-16);
make.top.equalTo(_headerView).offset(24);
make.height.mas_equalTo(KBFit(126));
}];
[self.pointsTitleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.cardView).offset(16);
make.top.equalTo(self.cardView).offset(18);
}];
[self.pointsIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.cardView).offset(16);
make.centerY.equalTo(self.cardView);
make.width.height.mas_equalTo(38);
}];
[self.pointsLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.pointsIconView.mas_right).offset(8);
make.centerY.equalTo(self.pointsIconView).offset(5);
}];
[self.rechargeButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.cardView).offset(-16);
make.centerY.equalTo(self.pointsIconView);
make.width.mas_equalTo(114);
make.height.mas_equalTo(42);
}];
[self.sectionIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(_headerView).offset(16);
make.top.equalTo(self.cardView.mas_bottom).offset(18);
make.width.height.mas_equalTo(24);
}];
[self.sectionTitleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.sectionIconView);
make.left.equalTo(self.sectionIconView.mas_right).offset(8);
}];
}
return _headerView;
}
- (UIView *)cardView {
if (!_cardView) {
_cardView = [UIView new];
_cardView.backgroundColor = [UIColor colorWithHex:0xC5FFF6];
_cardView.layer.cornerRadius = 20;
_cardView.layer.masksToBounds = YES;
}
return _cardView;
}
- (UILabel *)pointsTitleLabel {
if (!_pointsTitleLabel) {
_pointsTitleLabel = [UILabel new];
_pointsTitleLabel.text = KBLocalized(@"My Points");
_pointsTitleLabel.font = [KBFont medium:14];
_pointsTitleLabel.textColor = [UIColor colorWithHex:0x6B7A7A];
}
return _pointsTitleLabel;
}
- (UILabel *)pointsLabel {
if (!_pointsLabel) {
_pointsLabel = [UILabel new];
_pointsLabel.text = @"0";
_pointsLabel.font = [KBFont bold:40];
_pointsLabel.textColor = [UIColor colorWithHex:0x02BEAC];
}
return _pointsLabel;
}
- (UIImageView *)pointsIconView {
if (!_pointsIconView) {
_pointsIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"shop_jb_icon"]];
_pointsIconView.contentMode = UIViewContentModeScaleAspectFit;
}
return _pointsIconView;
}
- (UIButton *)rechargeButton {
if (!_rechargeButton) {
_rechargeButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_rechargeButton setTitle:KBLocalized(@"Recharge") forState:UIControlStateNormal];
_rechargeButton.titleLabel.font = [KBFont medium:13];
[_rechargeButton setTitleColor:[UIColor colorWithHex:0x1B1F1A] forState:UIControlStateNormal];
// _rechargeButton.backgroundColor = [UIColor colorWithHex:0xCFF7EA];
[_rechargeButton setBackgroundImage:[UIImage imageNamed:@"my_chongzhi_bg"] forState:UIControlStateNormal];
_rechargeButton.layer.cornerRadius = 21;
_rechargeButton.layer.masksToBounds = YES;
[_rechargeButton addTarget:self action:@selector(onRecharge) forControlEvents:UIControlEventTouchUpInside];
}
return _rechargeButton;
}
- (UIImageView *)sectionIconView {
if (!_sectionIconView) {
_sectionIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"shop_jb_icon"]];
_sectionIconView.contentMode = UIViewContentModeScaleAspectFit;
}
return _sectionIconView;
}
- (UILabel *)sectionTitleLabel {
if (!_sectionTitleLabel) {
_sectionTitleLabel = [UILabel new];
_sectionTitleLabel.text = KBLocalized(@"Consumption Details");
_sectionTitleLabel.font = [KBFont medium:14];
_sectionTitleLabel.textColor = [UIColor colorWithHex:KBBlackValue];
}
return _sectionTitleLabel;
}
- (KBMyVM *)viewModel {
if (!_viewModel) {
_viewModel = [[KBMyVM alloc] init];
}
return _viewModel;
}
- (KBShopVM *)shopVM {
if (!_shopVM) {
_shopVM = [[KBShopVM alloc] init];
}
return _shopVM;
}
@end

View File

@@ -14,6 +14,8 @@
#import "KBNoticeVC.h" #import "KBNoticeVC.h"
#import "KBFeedBackVC.h" #import "KBFeedBackVC.h"
#import "KBMyVM.h" #import "KBMyVM.h"
#import "KBConsumptionRecordVC.h"
@interface MyVC () <UITableViewDelegate, UITableViewDataSource> @interface MyVC () <UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, strong) BaseTableView *tableView; // @property (nonatomic, strong) BaseTableView *tableView; //
@@ -37,8 +39,9 @@
make.edges.equalTo(self.view); make.edges.equalTo(self.view);
}]; }];
// title + SF Symbols + // title + SF Symbols + my_record_icon
self.data = @[ self.data = @[
@[@{ @"title": KBLocalized(@"Consumption record"), @"icon": @"my_record_icon", @"color": @(0x60A3FF),@"id":@"8" }],
@[@{ @"title": KBLocalized(@"Notice"), @"icon": @"my_notice_icon", @"color": @(0x60A3FF),@"id":@"1" }], @[@{ @"title": KBLocalized(@"Notice"), @"icon": @"my_notice_icon", @"color": @(0x60A3FF),@"id":@"1" }],
@[@{ @"title": KBLocalized(@"Share App"), @"icon": @"my_share_icon", @"color": @(0xF5A623),@"id":@"2" }], @[@{ @"title": KBLocalized(@"Share App"), @"icon": @"my_share_icon", @"color": @(0xF5A623),@"id":@"2" }],
@[@{ @"title": KBLocalized(@"Feedback"), @"icon": @"my_feedback_icon", @"color": @(0xB06AFD),@"id":@"3" }, @[@{ @"title": KBLocalized(@"Feedback"), @"icon": @"my_feedback_icon", @"color": @(0xB06AFD),@"id":@"3" },
@@ -130,6 +133,9 @@
}else if ([itemID isEqualToString:@"6"]){ }else if ([itemID isEqualToString:@"6"]){
}else if ([itemID isEqualToString:@"8"]){
KBConsumptionRecordVC *vc = [[KBConsumptionRecordVC alloc] init];
[self.navigationController pushViewController:vc animated:true];
}else if ([itemID isEqualToString:@"7"]){ }else if ([itemID isEqualToString:@"7"]){
KBTestVC *vc = [[KBTestVC alloc] init]; KBTestVC *vc = [[KBTestVC alloc] init];
[self.navigationController pushViewController:vc animated:true]; [self.navigationController pushViewController:vc animated:true];

View File

@@ -8,6 +8,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "KBCharacter.h" #import "KBCharacter.h"
#import "KBMyTheme.h" #import "KBMyTheme.h"
#import "KBConsumptionRecord.h"
@class KBUser; @class KBUser;
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@@ -26,6 +27,7 @@ typedef void(^KBDeleteUserCharacterCompletion)(BOOL success, NSError * _Nullable
typedef void(^KBMyPurchasedThemesCompletion)(NSArray<KBMyTheme *> *_Nullable themes, NSError *_Nullable error); typedef void(^KBMyPurchasedThemesCompletion)(NSArray<KBMyTheme *> *_Nullable themes, NSError *_Nullable error);
typedef void(^KBDeleteThemesCompletion)(BOOL success, NSError *_Nullable error); typedef void(^KBDeleteThemesCompletion)(BOOL success, NSError *_Nullable error);
typedef void(^KBSubmitFeedbackCompletion)(BOOL success, NSError *_Nullable error); typedef void(^KBSubmitFeedbackCompletion)(BOOL success, NSError *_Nullable error);
typedef void(^KBMyPurchaseRecordCompletion)(NSArray<KBConsumptionRecord *> *_Nullable records, NSError *_Nullable error);
@interface KBMyVM : NSObject @interface KBMyVM : NSObject
@@ -39,6 +41,10 @@ typedef void(^KBSubmitFeedbackCompletion)(BOOL success, NSError *_Nullable error
/// 批量删除用户主题(/user-themes/batch-delete /// 批量删除用户主题(/user-themes/batch-delete
- (void)deletePurchasedThemesWithIds:(NSArray<NSString *> *)themeIds - (void)deletePurchasedThemesWithIds:(NSArray<NSString *> *)themeIds
completion:(KBDeleteThemesCompletion)completion; completion:(KBDeleteThemesCompletion)completion;
/// 分页查询钱包交易记录(/wallet/transactions
- (void)fetchWalletTransactionsWithPage:(NSInteger)pageNum
pageSize:(NSInteger)pageSize
completion:(KBMyPurchaseRecordCompletion)completion;
/// 本地已下载主题列表 /// 本地已下载主题列表
- (void)fetchDownloadedThemesWithCompletion:(KBMyPurchasedThemesCompletion)completion; - (void)fetchDownloadedThemesWithCompletion:(KBMyPurchasedThemesCompletion)completion;
/// 删除本地主题资源 /// 删除本地主题资源

View File

@@ -158,6 +158,47 @@ NSString * const KBUserCharacterDeletedNotification = @"KBUserCharacterDeletedNo
}]; }];
} }
- (void)fetchWalletTransactionsWithPage:(NSInteger)pageNum
pageSize:(NSInteger)pageSize
completion:(KBMyPurchaseRecordCompletion)completion {
NSInteger safePageNum = pageNum > 0 ? pageNum : 1;
NSInteger safePageSize = pageSize > 0 ? pageSize : 10;
NSDictionary *body = @{
@"pageNum": @(safePageNum),
@"pageSize": @(safePageSize)
};
[[KBNetworkManager shared] POST:API_WALLET_TRANSACTIONS
jsonBody:body
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary * _Nullable json,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
if (error) {
if (completion) completion(nil, error);
return;
}
id dataObj = json[KBData] ?: json[@"data"];
if (![dataObj isKindOfClass:[NSDictionary class]]) {
NSError *e = [NSError errorWithDomain:KBNetworkErrorDomain
code:KBNetworkErrorInvalidResponse
userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid response")}];
if (completion) completion(nil, e);
return;
}
id recordsObj = [(NSDictionary *)dataObj objectForKey:@"records"];
if (![recordsObj isKindOfClass:[NSArray class]]) {
NSError *e = [NSError errorWithDomain:KBNetworkErrorDomain
code:KBNetworkErrorInvalidResponse
userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid response")}];
if (completion) completion(nil, e);
return;
}
NSArray<KBConsumptionRecord *> *records = [KBConsumptionRecord mj_objectArrayWithKeyValuesArray:(NSArray *)recordsObj];
if (completion) completion(records, nil);
}];
}
- (void)fetchDownloadedThemesWithCompletion:(KBMyPurchasedThemesCompletion)completion { - (void)fetchDownloadedThemesWithCompletion:(KBMyPurchasedThemesCompletion)completion {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
NSArray<KBSkinDownloadRecord *> *records = [KBSkinInstallBridge installedSkinRecords]; NSArray<KBSkinDownloadRecord *> *records = [KBSkinInstallBridge installedSkinRecords];

View File

@@ -293,7 +293,10 @@ typedef NS_ENUM(NSInteger, KBSkinDetailSection) {
mode:KBSkinSourceModeRemoteZip mode:KBSkinSourceModeRemoteZip
completion:^(BOOL success) { completion:^(BOOL success) {
if (success) { if (success) {
// [KBHUD showSuccess:KBLocalized(@"已开始下载")]; NSString *themeId = self.detailModel.themeId;
if (themeId.length > 0) {
[self.shopVM restoreThemeWithId:themeId completion:nil];
}
} else { } else {
[KBHUD showInfo:KBLocalized(@"下载失败")]; [KBHUD showInfo:KBLocalized(@"下载失败")];
} }

View File

@@ -27,6 +27,8 @@ typedef void(^KBShopPurchaseCompletion)(BOOL success,
NSError *_Nullable error); NSError *_Nullable error);
typedef void(^KBShopDownloadInfoCompletion)(NSDictionary *_Nullable info, typedef void(^KBShopDownloadInfoCompletion)(NSDictionary *_Nullable info,
NSError *_Nullable error); NSError *_Nullable error);
typedef void(^KBShopRestoreCompletion)(BOOL success,
NSError *_Nullable error);
@interface KBShopVM : NSObject @interface KBShopVM : NSObject
@property (nonatomic, copy, readonly, nullable) NSArray<KBShopStyleModel *> *styles; @property (nonatomic, copy, readonly, nullable) NSArray<KBShopStyleModel *> *styles;
@@ -56,6 +58,10 @@ typedef void(^KBShopDownloadInfoCompletion)(NSDictionary *_Nullable info,
/// 推荐主题列表(用于皮肤详情页底部网格) /// 推荐主题列表(用于皮肤详情页底部网格)
- (void)fetchRecommendedThemesWithCompletion:(KBShopThemesCompletion)completion; - (void)fetchRecommendedThemesWithCompletion:(KBShopThemesCompletion)completion;
/// 恢复已删除的主题(/themes/restore
- (void)restoreThemeWithId:(nullable NSString *)themeId
completion:(nullable KBShopRestoreCompletion)completion;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -201,6 +201,24 @@
}]; }];
} }
- (void)restoreThemeWithId:(nullable NSString *)themeId
completion:(nullable KBShopRestoreCompletion)completion {
if (themeId.length == 0) {
if (completion) completion(NO, [self kb_invalidParameterError]);
return;
}
NSDictionary *body = @{@"themeId": [self kb_themeIdParamFromString:themeId]};
[[KBNetworkManager shared] POST:API_THEME_RESTORE
jsonBody:body
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary * _Nullable json,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
if (completion) completion(error == nil, error);
}];
}
- (id)kb_themeIdParamFromString:(NSString *)themeId { - (id)kb_themeIdParamFromString:(NSString *)themeId {
if (themeId.length == 0) { return @""; } if (themeId.length == 0) { return @""; }
NSNumberFormatter *formatter = [NSNumberFormatter new]; NSNumberFormatter *formatter = [NSNumberFormatter new];