diff --git a/Visual_Novel_iOS.xcodeproj/project.pbxproj b/Visual_Novel_iOS.xcodeproj/project.pbxproj index 252dc5c..352997a 100644 --- a/Visual_Novel_iOS.xcodeproj/project.pbxproj +++ b/Visual_Novel_iOS.xcodeproj/project.pbxproj @@ -437,6 +437,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 121; DEVELOPMENT_TEAM = 6GS5RC7C89; @@ -460,8 +461,9 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = com.personal.ChinaTravel; + PRODUCT_BUNDLE_IDENTIFIER = com.person.ChinaTravel; PRODUCT_NAME = Visual_Novel_iOSLevel; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_ACTIVE_COMPILATION_CONDITIONS = CLPRODUCT; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Visual_Novel_iOS/CL-Bridging-Header.h"; @@ -550,6 +552,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 121; DEVELOPMENT_TEAM = 6GS5RC7C89; @@ -573,8 +576,9 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = com.personal.ChinaTravel; + PRODUCT_BUNDLE_IDENTIFIER = com.person.ChinaTravel; PRODUCT_NAME = Visual_Novel_iOSLevel; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_ACTIVE_COMPILATION_CONDITIONS = CLAPPSTORE; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Visual_Novel_iOS/CL-Bridging-Header.h"; @@ -607,6 +611,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 121; DEVELOPMENT_TEAM = 6GS5RC7C89; @@ -630,8 +635,9 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = com.personal.ChinaTravel; + PRODUCT_BUNDLE_IDENTIFIER = com.person.ChinaTravel; PRODUCT_NAME = Visual_Novel_iOSLevel; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Visual_Novel_iOS/CL-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -645,6 +651,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 121; DEVELOPMENT_TEAM = 6GS5RC7C89; @@ -668,8 +675,9 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0.0; - PRODUCT_BUNDLE_IDENTIFIER = com.personal.ChinaTravel; + PRODUCT_BUNDLE_IDENTIFIER = com.person.ChinaTravel; PRODUCT_NAME = Visual_Novel_iOSLevel; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Visual_Novel_iOS/CL-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/Visual_Novel_iOS/Info.plist b/Visual_Novel_iOS/Info.plist index 7ee5ec2..9b14c1d 100644 --- a/Visual_Novel_iOS/Info.plist +++ b/Visual_Novel_iOS/Info.plist @@ -22,7 +22,15 @@ NSAllowsArbitraryLoadsInWebContent NSExceptionDomains - + + 54.223.196.180 + + NSExceptionAllowsInsecureHTTPLoads + + NSIncludesSubdomains + + + UIAppFonts diff --git a/Visual_Novel_iOS/Src/API/Network/APIConfig.swift b/Visual_Novel_iOS/Src/API/Network/APIConfig.swift index 752ca08..7d46302 100644 --- a/Visual_Novel_iOS/Src/API/Network/APIConfig.swift +++ b/Visual_Novel_iOS/Src/API/Network/APIConfig.swift @@ -43,18 +43,18 @@ struct APIConfig { static func apiHeaders() -> [String: String]? { var updatHeaders = headers +// +// let platform = "IOS" // _\(Bundle.appVersion) +// updatHeaders?.updateValue(platform, forKey: "platform") - let platform = "IOS" // _\(Bundle.appVersion) - updatHeaders?.updateValue(platform, forKey: "platform") +// let tokenNow = UserCore.shared.token +// if tokenNow.count > 0 { +// updatHeaders?.updateValue(tokenNow, forKey: "AUTH_TK") +// } - let tokenNow = UserCore.shared.token - if tokenNow.count > 0 { - updatHeaders?.updateValue(tokenNow, forKey: "AUTH_TK") - } +// updatHeaders?.updateValue("\(versionNum)", forKey: "versionNum") - updatHeaders?.updateValue("\(versionNum)", forKey: "versionNum") - - updatHeaders?.updateValue(UIDevice.UUID, forKey: "AUTH_DID") +// updatHeaders?.updateValue(UIDevice.UUID, forKey: "AUTH_DID") /* if let lan = Languages.preferedLans.first { updatHeaders?.updateValue(lan.rawValue, forKey: "accept-language") @@ -106,4 +106,9 @@ struct APIConfig { static var pigeon: String{ return "https://test-pigeon.crushlevel.ai" } + + static var role: String { + // 确保 URL 格式正确,包含协议、域名和端口 + return "http://54.223.196.180:8091" + } } diff --git a/Visual_Novel_iOS/Src/API/Network/APIProvider.swift b/Visual_Novel_iOS/Src/API/Network/APIProvider.swift index 69f1a94..e5fca56 100644 --- a/Visual_Novel_iOS/Src/API/Network/APIProvider.swift +++ b/Visual_Novel_iOS/Src/API/Network/APIProvider.swift @@ -128,11 +128,14 @@ public enum ServiceErrorEnum: String, Codable { // 假设 ResponseData 是一个泛型结构体,用于包装 API 响应 struct ResponseData: Codable { /// OK or ERROR - let status: String? - let errorCode: ServiceErrorEnum? - let errorMsg: String? - let content: T? - let traceId : String? +// let status: String? +// let errorCode: ServiceErrorEnum? +// let errorMsg: String? +// let content: T? +// let traceId : String? + let code: Int? + let message: String? + let data: T? } class ResponseContentPageData: Codable{ @@ -213,58 +216,58 @@ extension MoyaProvider { dlog("👉⭐️\(target.path)⭐️ response:\n\(jsonString)") } - let status = data.status - let code = data.errorCode - let msg = data.errorMsg +// let status = data.status +// let code = data.errorCode +// let msg = data.errorMsg - if (status ?? "") == "OK" { - let model = data.content + if (data.code ?? 0) == 200 { + let model = data.data completion(.success(model)) } else { - var toastMsg = autoShowErrMsg - - if code == .tokenExpired || code == .tokenIllegal || code == .accountIsFrozen || code == .sign_usernotexist || code == .sign_usernotLoggedIn || code == .sign_userLoginclientNotExist || code == .sign_userNotAuthorizedClient { - // ⚠️ 弹出登陆 - UserCore.shared.logout() - - if let Vc = UIWindow.getTopViewController(), (Vc is CLLoginMainController || Vc is PersonalInformationFillController) { - // do nothing. - } else { -// AppRouter.goBackRootController(jumpIndex: .home) { -// NotificationCenter.post(name: .presentSignInVc, object: nil, userInfo: nil) +// var toastMsg = autoShowErrMsg +// +// if code == .tokenExpired || code == .tokenIllegal || code == .accountIsFrozen || code == .sign_usernotexist || code == .sign_usernotLoggedIn || code == .sign_userLoginclientNotExist || code == .sign_userNotAuthorizedClient { +// // ⚠️ 弹出登陆 +// UserCore.shared.logout() +// +// if let Vc = UIWindow.getTopViewController(), (Vc is CLLoginMainController || Vc is PersonalInformationFillController) { +// // do nothing. +// } else { +//// AppRouter.goBackRootController(jumpIndex: .home) { +//// NotificationCenter.post(name: .presentSignInVc, object: nil, userInfo: nil) +//// } +// NotificationCenter.post(name: .presentSignInVc, object: nil, userInfo: nil) +// } +// } else if code == .newAccount { +// // ⚠️ 不进行Toast的Msg类型 +// toastMsg = false +// } else if code == .insufficentCoin { +// // ⚠️钱包余额不足 +// // refresh wallet +// toastMsg = false +// +// var handled = false +// if let cow = target as? AICowAPI{ +// switch cow{ +// case .voiceCallOperate, .voiceAsr2, .voiceTts: +// IMAIViewModel.shared.showChatModelInsufficentCoinSheet() +// handled = true +// default: +// break // } - NotificationCenter.post(name: .presentSignInVc, object: nil, userInfo: nil) - } - } else if code == .newAccount { - // ⚠️ 不进行Toast的Msg类型 - toastMsg = false - } else if code == .insufficentCoin { - // ⚠️钱包余额不足 - // refresh wallet - toastMsg = false - - var handled = false - if let cow = target as? AICowAPI{ - switch cow{ - case .voiceCallOperate, .voiceAsr2, .voiceTts: - IMAIViewModel.shared.showChatModelInsufficentCoinSheet() - handled = true - default: - break - } - } - if handled == false{ - // 弹出充值Sheet - CLPurchase.shared.showIAPBuyCoinSheet() - } - } - - if toastMsg, let msgUnpack = msg, msgUnpack.count > 0 { - UIWindow.getTopDisplayWindow()?.makeToast(msgUnpack) - } - - dlog("⛔️error: code = \(code ?? .common), msg = \(data.errorMsg ?? "业务状态失败")") - completion(.failure(.serviceError(code: code, msg: msg))) +// } +// if handled == false{ +// // 弹出充值Sheet +// CLPurchase.shared.showIAPBuyCoinSheet() +// } +// } +// +// if toastMsg, let msgUnpack = msg, msgUnpack.count > 0 { +// UIWindow.getTopDisplayWindow()?.makeToast(msgUnpack) +// } +// +// dlog("⛔️error: code = \(code ?? .common), msg = \(data.errorMsg ?? "业务状态失败")") +// completion(.failure(.serviceError(code: code, msg: msg))) } } catch { diff --git a/Visual_Novel_iOS/Src/API/RoleApi.swift b/Visual_Novel_iOS/Src/API/RoleApi.swift new file mode 100644 index 0000000..2ced0d6 --- /dev/null +++ b/Visual_Novel_iOS/Src/API/RoleApi.swift @@ -0,0 +1,69 @@ +// +// RoleApi.swift +// Visual_Novel_iOS +// +// Created by mh on 2025/11/6. +// + +import Moya + +let RoleProvider = APIConfig.useMock && UserAPI.useMock +? MoyaProvider(endpointClosure: myEndpointClosure, stubClosure: { target in + let data = target.sampleData + if(data.count > 0){ + return .delayed(seconds: 0.5) + }else{ + return .never + } +}) +: MoyaProvider(requestClosure: myRequestClosure) + +enum RoleAPI { + static let useMock: Bool = false + + // 查询角色列表 + case characterList(params: [String: Any]) + +} + +extension RoleAPI: TargetType { + var method: Moya.Method { + return .post + } + + var task: Task { + var mParams = [String: Any]() + switch self { + case .characterList(let params): + // 将传入的参数赋值给 mParams + mParams = params + } + return .requestParameters(parameters: mParams, encoding: JSONEncoding.default) + } + + var headers: [String : String]? { + return APIConfig.apiHeaders() + } + + var baseURL: URL { + // 确保 URL 格式正确,避免强制解包导致的崩溃 + guard let url = URL(string: APIConfig.role) else { + fatalError("Invalid baseURL: \(APIConfig.role)") + } + return url + } + + var path: String { + switch self { + case .characterList: + return "/character/list" + } + } + + var sampleData: Data { + switch self { + case .characterList: + return Data() + } + } +} diff --git a/Visual_Novel_iOS/Src/Modules/Chat/Setting/Model/ChatRoleViewModel.swift b/Visual_Novel_iOS/Src/Modules/Chat/Setting/Model/ChatRoleViewModel.swift new file mode 100644 index 0000000..92fe652 --- /dev/null +++ b/Visual_Novel_iOS/Src/Modules/Chat/Setting/Model/ChatRoleViewModel.swift @@ -0,0 +1,64 @@ +// +// ChatRoleViewModel.swift +// Visual_Novel_iOS +// +// Created by mh on 2025/11/6. +// + +import Foundation +import CodableWrappers + +// request +struct RoleListRequest: Codable { + var index = 1 + var limit = 20 + var name: String = "" + var sourceId = 0 + var tagId: Int? +} + +// model +struct RoleListModel: Codable { + var total: Int = 0 + var index: Int = -1 + var rows: [RoleItem] = [] +} + +struct RoleItem: Codable { + var id: Int = 0 + var name: String = "" + var coverImage: String = "" + var updateTime: String = "" + var sourceId: Int = 0 +} + +class ChatRoleViewModel { + + func loadRoles(index: Int, limit: Int = 20, name: String = "", sourceId: Int = -1, tagId: Int? = nil, completion: ((_ datas: [RoleListModel]?) -> Void)?) { + var req = RoleListRequest() + req.index = index + req.limit = limit + req.name = name + req.sourceId = sourceId + req.tagId = tagId + + let params = req.toNonNilDictionary() + + // 根据 API 响应格式,返回的是单个 RoleListModel 对象,而不是数组 + // 但为了保持接口一致性,我们将单个对象包装成数组 + RoleProvider.request(.characterList(params: params), modelType: RoleListModel.self) { result in + switch result { + case .success(let model): + // 如果返回的是单个 RoleListModel,将其包装成数组 + if let model = model { + completion?([model]) + } else { + completion?(nil) + } + case .failure(let failure): + dlog("⛔️ 加载角色列表失败: \(failure)") + completion?(nil) + } + } + } +} diff --git a/Visual_Novel_iOS/Src/Modules/Chat/Setting/View/ChatSettingSwipeView.swift b/Visual_Novel_iOS/Src/Modules/Chat/Setting/View/ChatSettingSwipeView.swift index 3ce98ea..711c83a 100644 --- a/Visual_Novel_iOS/Src/Modules/Chat/Setting/View/ChatSettingSwipeView.swift +++ b/Visual_Novel_iOS/Src/Modules/Chat/Setting/View/ChatSettingSwipeView.swift @@ -189,11 +189,6 @@ extension ChatSettingSwipeView { extension ChatSettingSwipeView: UITableViewDelegate, UITableViewDataSource { - func scrollViewDidScroll(_ scrollView: UIScrollView) { - // 确保tableView内容可以滑动到最顶部 - // 不需要额外的contentInset调整 - } - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let imageRow = rows[indexPath.section][indexPath.row] as? ImageRow, imageRow.showArrow else { return } diff --git a/Visual_Novel_iOS/Src/Modules/Roles/RolesRootPageController.swift b/Visual_Novel_iOS/Src/Modules/Roles/RolesRootPageController.swift index 951fa79..544b194 100644 --- a/Visual_Novel_iOS/Src/Modules/Roles/RolesRootPageController.swift +++ b/Visual_Novel_iOS/Src/Modules/Roles/RolesRootPageController.swift @@ -12,6 +12,8 @@ import Combine class RolesRootPageController: CLTabRootController { private var cancellables = Set() + var page: Int = 1 + var viewModel: ChatRoleViewModel = ChatRoleViewModel() override func viewDidLoad() { super.viewDidLoad() @@ -29,6 +31,11 @@ class RolesRootPageController: CLTabRootController { setupEvent() } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + loadData() + } + private func setupViews() { navigationView.bgView.alpha = 0 navigationView.setupBgViewToStatusBarHeight() @@ -44,6 +51,18 @@ class RolesRootPageController: CLTabRootController { .store(in: &cancellables) } + func loadData() { +// if page == 1{ +//// loadedAiIds.removeAll() +// } + + viewModel.loadRoles(index: page) { datas in + print(datas) + print("11111") + + } + } + // 点击顶部 实现跳转 private func handleJump(_ target: JumpTarget) { switch target {