1
This commit is contained in:
@@ -49,7 +49,8 @@
|
||||
|
||||
- (void)configWithTitle:(NSString *)title imageURL:(NSString *)url price:(NSString *)price {
|
||||
self.titleLabel.text = title.length ? title : @"Dopamine";
|
||||
[self.priceBtn setTitle:@"20" forState:UIControlStateNormal];
|
||||
NSString *priceText = price.length ? price : @"20";
|
||||
[self.priceBtn setTitle:priceText forState:UIControlStateNormal];
|
||||
|
||||
// 简化:本地展示占位色,无网络图
|
||||
self.coverView.backgroundColor = [UIColor colorWithWhite:0.92 alpha:1.0];
|
||||
@@ -84,4 +85,3 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
20
keyBoard/Class/Shop/M/KBShopStyleModel.h
Normal file
20
keyBoard/Class/Shop/M/KBShopStyleModel.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// KBShopStyleModel.h
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/11.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface KBShopStyleModel : NSObject
|
||||
/// 风格 id
|
||||
@property (nonatomic, copy, nullable) NSString *styleId;
|
||||
/// 风格名称
|
||||
@property (nonatomic, copy, nullable) NSString *styleName;
|
||||
@end
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
18
keyBoard/Class/Shop/M/KBShopStyleModel.m
Normal file
18
keyBoard/Class/Shop/M/KBShopStyleModel.m
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// KBShopStyleModel.m
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/11.
|
||||
//
|
||||
|
||||
#import "KBShopStyleModel.h"
|
||||
|
||||
@implementation KBShopStyleModel
|
||||
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
|
||||
return @{
|
||||
@"styleId": @"id",
|
||||
@"styleName": @"styleName"
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
34
keyBoard/Class/Shop/M/KBShopThemeModel.h
Normal file
34
keyBoard/Class/Shop/M/KBShopThemeModel.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// KBShopThemeModel.h
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/11.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface KBShopThemeModel : NSObject
|
||||
/// 主题 id
|
||||
@property (nonatomic, copy, nullable) NSString *themeId;
|
||||
/// 主题名称
|
||||
@property (nonatomic, copy, nullable) NSString *themeName;
|
||||
/// 价格(单位:接口返回的货币)
|
||||
@property (nonatomic, assign) CGFloat themePrice;
|
||||
/// 标签或风格名称
|
||||
@property (nonatomic, copy, nullable) NSString *themeTag;
|
||||
/// 下载量描述
|
||||
@property (nonatomic, copy, nullable) NSString *themeDownload;
|
||||
/// 风格类型(接口返回 themeStyle)
|
||||
@property (nonatomic, assign) NSInteger themeStyle;
|
||||
/// 是否上架
|
||||
@property (nonatomic, assign, getter=isThemeStatus) BOOL themeStatus;
|
||||
/// 购买次数
|
||||
@property (nonatomic, assign) NSInteger themePurchasesNumber;
|
||||
/// 预览图
|
||||
@property (nonatomic, copy, nullable) NSString *themePreviewImageUrl;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
16
keyBoard/Class/Shop/M/KBShopThemeModel.m
Normal file
16
keyBoard/Class/Shop/M/KBShopThemeModel.m
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// KBShopThemeModel.m
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/11.
|
||||
//
|
||||
|
||||
#import "KBShopThemeModel.h"
|
||||
|
||||
@implementation KBShopThemeModel
|
||||
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
|
||||
return @{
|
||||
@"themeId": @"id",
|
||||
};
|
||||
}
|
||||
@end
|
||||
@@ -10,17 +10,25 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class KBShopStyleModel;
|
||||
@class KBShopVM;
|
||||
@class KBShopThemeModel;
|
||||
|
||||
@interface KBShopItemVC : UIViewController<JXPagerViewListViewDelegate>
|
||||
/// 列表:使用 UICollectionView 展示两列皮肤卡片
|
||||
@property (nonatomic, strong) UICollectionView *collectionView;
|
||||
/// 数据源:简单字符串作为标题(演示用)
|
||||
@property (nonatomic, strong) NSMutableArray<NSString *> *dataSource;
|
||||
/// 数据源:主题列表
|
||||
@property (nonatomic, strong) NSMutableArray<KBShopThemeModel *> *dataSource;
|
||||
/// 是否需要上拉加载更多
|
||||
@property (nonatomic, assign) BOOL isNeedFooter;
|
||||
/// 是否需要下拉刷新
|
||||
@property (nonatomic, assign) BOOL isNeedHeader;
|
||||
/// 首次是否已刷新过(避免重复触发)
|
||||
@property (nonatomic, assign) BOOL isHeaderRefreshed; // 默认为 YES
|
||||
/// 当前所属风格
|
||||
@property (nonatomic, strong, nullable) KBShopStyleModel *style;
|
||||
/// 可复用的 ShopVM(由外部传入,便于共用缓存/网络配置)
|
||||
@property (nonatomic, strong, nullable) KBShopVM *shopViewModel;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -11,9 +11,12 @@
|
||||
#import "KBSkinCardCell.h"
|
||||
#import "KBSkinInstallBridge.h"
|
||||
#import "KBHUD.h"
|
||||
#import "KBShopVM.h"
|
||||
|
||||
@interface KBShopItemVC ()<UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
||||
@property (nonatomic, copy) void(^scrollCallback)(UIScrollView *scrollView);
|
||||
@property (nonatomic, strong) KBShopVM *internalViewModel;
|
||||
@property (nonatomic, assign, getter=isLoading) BOOL loading;
|
||||
@end
|
||||
|
||||
@implementation KBShopItemVC
|
||||
@@ -21,6 +24,7 @@
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
self.dataSource = [NSMutableArray array];
|
||||
|
||||
// 懒加载 collectionView,并添加到视图
|
||||
[self.view addSubview:self.collectionView];
|
||||
@@ -28,26 +32,19 @@
|
||||
make.edges.equalTo(self.view); // mas 布局:铺满
|
||||
}];
|
||||
|
||||
// 刷新组件(演示:2 秒后结束)
|
||||
KBWeakSelf
|
||||
if (self.isNeedHeader) {
|
||||
self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[weakSelf.collectionView.mj_header endRefreshing];
|
||||
});
|
||||
[weakSelf fetchThemes];
|
||||
}];
|
||||
}
|
||||
if (self.isNeedFooter) {
|
||||
self.collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[weakSelf.dataSource addObject:KBLocalized(@"Loaded more successfully")]; // 模拟新增一条
|
||||
[weakSelf.collectionView reloadData];
|
||||
[weakSelf.collectionView.mj_footer endRefreshing];
|
||||
});
|
||||
[weakSelf fetchThemes];
|
||||
}];
|
||||
}
|
||||
|
||||
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
self.collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
|
||||
[self beginFirstRefresh];
|
||||
}
|
||||
@@ -56,23 +53,58 @@
|
||||
|
||||
// 首次进入自动触发一次刷新(如果需要)
|
||||
- (void)beginFirstRefresh {
|
||||
if (!self.isHeaderRefreshed) {
|
||||
[self beginRefreshImmediately];
|
||||
if (self.isHeaderRefreshed || self.isLoading) {
|
||||
return;
|
||||
}
|
||||
if (!self.style.styleId.length) {
|
||||
return;
|
||||
}
|
||||
[self beginRefreshImmediately];
|
||||
}
|
||||
|
||||
- (void)beginRefreshImmediately {
|
||||
if (self.isNeedHeader) {
|
||||
if (self.isNeedHeader && !self.collectionView.mj_header.isRefreshing) {
|
||||
[self.collectionView.mj_header beginRefreshing];
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
self.isHeaderRefreshed = YES;
|
||||
[self.collectionView reloadData];
|
||||
[self.collectionView.mj_header endRefreshing];
|
||||
});
|
||||
} else {
|
||||
self.isHeaderRefreshed = YES;
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
[self fetchThemes];
|
||||
}
|
||||
|
||||
- (void)fetchThemes {
|
||||
if (self.isLoading) {
|
||||
return;
|
||||
}
|
||||
NSString *styleId = self.style.styleId;
|
||||
if (!styleId.length) {
|
||||
return;
|
||||
}
|
||||
self.loading = YES;
|
||||
KBShopVM *vm = self.shopViewModel;
|
||||
if (!vm) {
|
||||
if (!self.internalViewModel) {
|
||||
self.internalViewModel = [[KBShopVM alloc] init];
|
||||
}
|
||||
vm = self.internalViewModel;
|
||||
}
|
||||
KBWeakSelf
|
||||
[vm fetchThemesForStyleId:styleId completion:^(NSArray<KBShopThemeModel *> * _Nullable themes, NSError * _Nullable error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
weakSelf.loading = NO;
|
||||
weakSelf.isHeaderRefreshed = YES;
|
||||
if (error) {
|
||||
NSString *msg = error.localizedDescription ?: KBLocalized(@"Network error");
|
||||
[KBHUD showInfo:msg];
|
||||
} else {
|
||||
weakSelf.dataSource = themes.count ? themes.mutableCopy : [NSMutableArray array];
|
||||
[weakSelf.collectionView reloadData];
|
||||
}
|
||||
if ([weakSelf.collectionView.mj_header isRefreshing]) {
|
||||
[weakSelf.collectionView.mj_header endRefreshing];
|
||||
}
|
||||
if ([weakSelf.collectionView.mj_footer isRefreshing]) {
|
||||
[weakSelf.collectionView.mj_footer endRefreshing];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionView DataSource
|
||||
@@ -84,8 +116,10 @@
|
||||
|
||||
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
KBSkinCardCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"KBSkinCardCell" forIndexPath:indexPath];
|
||||
NSString *title = (indexPath.item < self.dataSource.count) ? self.dataSource[indexPath.item] : @"Dopamine";
|
||||
[cell configWithTitle:title imageURL:nil price:@"20"]; // 价格写死 20(演示)
|
||||
KBShopThemeModel *theme = (indexPath.item < self.dataSource.count) ? self.dataSource[indexPath.item] : nil;
|
||||
NSString *title = theme.themeName.length ? theme.themeName : KBLocalized(@"Themes");
|
||||
NSString *price = [self kb_priceStringForTheme:theme];
|
||||
[cell configWithTitle:title imageURL:nil price:price];
|
||||
return cell;
|
||||
}
|
||||
|
||||
@@ -117,8 +151,28 @@
|
||||
[self kb_handleShopTapAtIndexPath:indexPath];
|
||||
}
|
||||
|
||||
- (NSString *)kb_priceStringForTheme:(KBShopThemeModel *)theme {
|
||||
if (!theme) {
|
||||
return @"";
|
||||
}
|
||||
if (theme.themePrice <= 0.0f) {
|
||||
return KBLocalized(@"Free");
|
||||
}
|
||||
static NSNumberFormatter *formatter;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
formatter = [[NSNumberFormatter alloc] init];
|
||||
formatter.minimumFractionDigits = 0;
|
||||
formatter.maximumFractionDigits = 2;
|
||||
formatter.minimumIntegerDigits = 1;
|
||||
});
|
||||
NSString *priceString = [formatter stringFromNumber:@(theme.themePrice)];
|
||||
return priceString ?: [NSString stringWithFormat:@"%.2f", theme.themePrice];
|
||||
}
|
||||
|
||||
- (void)kb_handleShopTapAtIndexPath:(NSIndexPath *)indexPath {
|
||||
NSString *title = (indexPath.item < self.dataSource.count) ? self.dataSource[indexPath.item] : KBLocalized(@"专属皮肤002");
|
||||
KBShopThemeModel *theme = (indexPath.item < self.dataSource.count) ? self.dataSource[indexPath.item] : nil;
|
||||
NSString *title = theme.themeName.length ? theme.themeName : KBLocalized(@"专属皮肤002");
|
||||
// 将需求固定到 002.zip,本地写死皮肤 id,便于键盘扩展识别并解压。
|
||||
static NSString * const kKBBundleSkinId002 = @"bundle_skin_fense";
|
||||
[KBSkinInstallBridge publishBundleSkinRequestWithId:kKBBundleSkinId002
|
||||
@@ -128,6 +182,13 @@
|
||||
[KBHUD showInfo:KBLocalized(@"已通知键盘解压,切换到自定义键盘即可生效")];
|
||||
}
|
||||
|
||||
- (void)setStyle:(KBShopStyleModel *)style {
|
||||
_style = style;
|
||||
if (self.isViewLoaded && !self.isHeaderRefreshed) {
|
||||
[self beginFirstRefresh];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UIScrollView Delegate(转发给分页容器)
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
!self.scrollCallback ?: self.scrollCallback(scrollView);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
|
||||
#import "KBWebViewViewController.h"
|
||||
#import "KBShopVM.h"
|
||||
#import "KBHUD.h"
|
||||
|
||||
static const CGFloat JXTableHeaderViewHeight = (323);
|
||||
static const CGFloat JXheightForHeaderInSection = 50;
|
||||
@@ -33,11 +35,13 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
|
||||
@property (nonatomic, strong) JXCategoryTitleView *categoryView;
|
||||
@property (nonatomic, strong) NSArray <NSString *> *titles;
|
||||
@property (nonatomic, copy) NSArray<KBShopStyleModel *> *styles;
|
||||
@property (nonatomic, strong) UIImageView *bgImageView; // 全屏背景图
|
||||
@property (nonatomic, strong) UIButton *searchBtn;
|
||||
@property (nonatomic, strong) UIButton *skinBtn;
|
||||
// 记录当前分类条是否为白底,避免重复设置
|
||||
@property (nonatomic, assign) BOOL categoryIsWhite;
|
||||
@property (nonatomic, strong) KBShopVM *shopVM;
|
||||
|
||||
@end
|
||||
|
||||
@@ -65,6 +69,7 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
make.width.height.mas_equalTo(25);
|
||||
}];
|
||||
|
||||
[self fetchShopStylesWithHUD:YES];
|
||||
}
|
||||
|
||||
// 系统导航栏显隐由 Base 统一管理(全局隐藏),该 VC 不再手动切换,避免闪烁。
|
||||
@@ -76,10 +81,8 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
self.navigationController.navigationBar.translucent = false;
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
_titles = @[KBLocalized(@"能力"), KBLocalized(@"爱好"), KBLocalized(@"队友"),
|
||||
KBLocalized(@"能力2"), KBLocalized(@"爱好2"), KBLocalized(@"队友2"),
|
||||
KBLocalized(@"能力"), KBLocalized(@"爱好"), KBLocalized(@"队友"),
|
||||
KBLocalized(@"能力2"), KBLocalized(@"爱好2"), KBLocalized(@"队友2")];
|
||||
self.styles = @[];
|
||||
_titles = @[];
|
||||
|
||||
_userHeaderView = [[KBShopHeadView alloc] init];
|
||||
_categoryView = (JXCategoryTitleView *)[[KBCategoryTitleView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, JXheightForHeaderInSection)];
|
||||
@@ -133,13 +136,7 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
|
||||
__weak typeof(self)weakSelf = self;
|
||||
self.pagerView.mainTableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
self.categoryView.titles = @[KBLocalized(@"高级能力"), KBLocalized(@"高级爱好"), KBLocalized(@"高级队友")];
|
||||
self.categoryView.defaultSelectedIndex = 0;
|
||||
[self.categoryView reloadData];
|
||||
[self.pagerView reloadData];
|
||||
[weakSelf.pagerView.mainTableView.mj_header endRefreshing];
|
||||
});
|
||||
[weakSelf fetchShopStylesWithHUD:NO];
|
||||
}];
|
||||
|
||||
self.pagerView.pinSectionHeaderVerticalOffset = KB_NAV_TOTAL_HEIGHT;
|
||||
@@ -194,19 +191,9 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
list.title = self.titles[index];
|
||||
list.isNeedHeader = self.isNeedHeader;
|
||||
list.isNeedFooter = self.isNeedFooter;
|
||||
if (index == 0) {
|
||||
list.dataSource = @[KBLocalized(@"橡胶火箭"), KBLocalized(@"橡胶火箭炮"), KBLocalized(@"橡胶机关枪"), KBLocalized(@"橡胶子弹"),
|
||||
KBLocalized(@"橡胶攻城炮"), KBLocalized(@"橡胶象枪"), KBLocalized(@"橡胶象枪乱打"), KBLocalized(@"橡胶灰熊铳"),
|
||||
KBLocalized(@"橡胶雷神象枪"), KBLocalized(@"橡胶猿王枪"), KBLocalized(@"橡胶犀·榴弹炮"), KBLocalized(@"橡胶大蛇炮"),
|
||||
KBLocalized(@"橡胶火箭"), KBLocalized(@"橡胶火箭炮"), KBLocalized(@"橡胶机关枪"), KBLocalized(@"橡胶子弹"),
|
||||
KBLocalized(@"橡胶攻城炮"), KBLocalized(@"橡胶象枪"), KBLocalized(@"橡胶象枪乱打"), KBLocalized(@"橡胶灰熊铳"),
|
||||
KBLocalized(@"橡胶雷神象枪"), KBLocalized(@"橡胶猿王枪"), KBLocalized(@"橡胶犀·榴弹炮"), KBLocalized(@"橡胶大蛇炮")].mutableCopy;
|
||||
}else if (index == 1) {
|
||||
list.dataSource = @[KBLocalized(@"吃烤肉"), KBLocalized(@"吃鸡腿肉"), KBLocalized(@"吃牛肉"), KBLocalized(@"各种肉")].mutableCopy;
|
||||
}else {
|
||||
list.dataSource = @[KBLocalized(@"【剑士】罗罗诺亚·索隆"), KBLocalized(@"【航海士】娜美"), KBLocalized(@"【狙击手】乌索普"),
|
||||
KBLocalized(@"【厨师】香吉士"), KBLocalized(@"【船医】托尼托尼·乔巴"), KBLocalized(@"【船匠】 弗兰奇"),
|
||||
KBLocalized(@"【音乐家】布鲁克"), KBLocalized(@"【考古学家】妮可·罗宾")].mutableCopy;
|
||||
list.shopViewModel = self.shopVM;
|
||||
if (index < self.styles.count) {
|
||||
list.style = self.styles[index];
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -277,4 +264,46 @@ static const CGFloat JXheightForHeaderInSection = 50;
|
||||
}
|
||||
return _skinBtn;
|
||||
}
|
||||
|
||||
- (KBShopVM *)shopVM {
|
||||
if (!_shopVM) {
|
||||
_shopVM = [[KBShopVM alloc] init];
|
||||
}
|
||||
return _shopVM;
|
||||
}
|
||||
|
||||
- (void)fetchShopStylesWithHUD:(BOOL)showHUD {
|
||||
if (showHUD) {
|
||||
[KBHUD show];
|
||||
}
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.shopVM fetchAllStylesWithCompletion:^(NSArray<KBShopStyleModel *> * _Nullable styles, NSError * _Nullable error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (showHUD) {
|
||||
[KBHUD dismiss];
|
||||
}
|
||||
[weakSelf.pagerView.mainTableView.mj_header endRefreshing];
|
||||
if (error) {
|
||||
NSString *msg = error.localizedDescription ?: KBLocalized(@"Network error");
|
||||
[KBHUD showInfo:msg];
|
||||
return;
|
||||
}
|
||||
[weakSelf applyStyles:styles];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)applyStyles:(NSArray<KBShopStyleModel *> *)styles {
|
||||
self.styles = styles ?: @[];
|
||||
NSMutableArray *names = [NSMutableArray array];
|
||||
for (KBShopStyleModel *style in self.styles) {
|
||||
NSString *name = style.styleName.length ? style.styleName : KBLocalized(@"Themes");
|
||||
[names addObject:name];
|
||||
}
|
||||
self.titles = names.copy;
|
||||
self.categoryView.titles = self.titles;
|
||||
self.categoryView.defaultSelectedIndex = 0;
|
||||
[self.categoryView reloadData];
|
||||
[self.pagerView reloadData];
|
||||
}
|
||||
@end
|
||||
|
||||
34
keyBoard/Class/Shop/VM/KBShopVM.h
Normal file
34
keyBoard/Class/Shop/VM/KBShopVM.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// KBShopVM.h
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/9.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#import "KBShopStyleModel.h"
|
||||
#import "KBShopThemeModel.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark - Models
|
||||
|
||||
typedef void(^KBShopStylesCompletion)(NSArray<KBShopStyleModel *> *_Nullable styles,
|
||||
NSError *_Nullable error);
|
||||
typedef void(^KBShopThemesCompletion)(NSArray<KBShopThemeModel *> *_Nullable themes,
|
||||
NSError *_Nullable error);
|
||||
|
||||
@interface KBShopVM : NSObject
|
||||
@property (nonatomic, copy, readonly, nullable) NSArray<KBShopStyleModel *> *styles;
|
||||
|
||||
/// 查询全部主题风格
|
||||
- (void)fetchAllStylesWithCompletion:(KBShopStylesCompletion)completion;
|
||||
|
||||
/// 按风格请求主题列表
|
||||
- (void)fetchThemesForStyleId:(nullable NSString *)styleId
|
||||
completion:(KBShopThemesCompletion)completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
85
keyBoard/Class/Shop/VM/KBShopVM.m
Normal file
85
keyBoard/Class/Shop/VM/KBShopVM.m
Normal file
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// KBShopVM.m
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/9.
|
||||
//
|
||||
|
||||
#import "KBShopVM.h"
|
||||
#import "KBNetworkManager.h"
|
||||
#import "KBAPI.h"
|
||||
#import "KBBizCode.h"
|
||||
#import <MJExtension/MJExtension.h>
|
||||
|
||||
@interface KBShopVM ()
|
||||
@property (nonatomic, copy, readwrite, nullable) NSArray<KBShopStyleModel *> *styles;
|
||||
@end
|
||||
|
||||
|
||||
@implementation KBShopVM
|
||||
|
||||
- (NSError *)kb_invalidResponseError {
|
||||
return [NSError errorWithDomain:KBNetworkErrorDomain
|
||||
code:KBNetworkErrorInvalidResponse
|
||||
userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid response")}];
|
||||
}
|
||||
|
||||
- (NSError *)kb_invalidParameterError {
|
||||
return [NSError errorWithDomain:KBNetworkErrorDomain
|
||||
code:KBNetworkErrorInvalidResponse
|
||||
userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid parameter")}];
|
||||
}
|
||||
|
||||
- (void)fetchAllStylesWithCompletion:(KBShopStylesCompletion)completion {
|
||||
[[KBNetworkManager shared] GET:API_THEME_LIST_ALL_STYLES
|
||||
parameters:nil
|
||||
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:[NSArray class]]) {
|
||||
if (completion) completion(nil, [self kb_invalidResponseError]);
|
||||
return;
|
||||
}
|
||||
NSArray<KBShopStyleModel *> *list = [KBShopStyleModel mj_objectArrayWithKeyValuesArray:(NSArray *)dataObj];
|
||||
self.styles = list;
|
||||
if (completion) completion(list, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)fetchThemesForStyleId:(nullable NSString *)styleId
|
||||
completion:(KBShopThemesCompletion)completion {
|
||||
if (styleId.length == 0) {
|
||||
if (completion) completion(nil, [self kb_invalidParameterError]);
|
||||
return;
|
||||
}
|
||||
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
||||
params[@"themeStyle"] = @([styleId integerValue]);
|
||||
[[KBNetworkManager shared] GET:API_THEME_LIST_BY_STYLE
|
||||
parameters:params
|
||||
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:[NSArray class]]) {
|
||||
if (completion) completion(nil, [self kb_invalidResponseError]);
|
||||
return;
|
||||
}
|
||||
NSArray<KBShopThemeModel *> *list = [KBShopThemeModel mj_objectArrayWithKeyValuesArray:(NSArray *)dataObj];
|
||||
if (completion) completion(list, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user