diff --git a/Visual_Novel_iOS/Src/API/RoleVoiceActorListApi.swift b/Visual_Novel_iOS/Src/API/RoleVoiceActorListApi.swift new file mode 100644 index 0000000..089c787 --- /dev/null +++ b/Visual_Novel_iOS/Src/API/RoleVoiceActorListApi.swift @@ -0,0 +1,70 @@ +// +// RoleVoiceActorListApi.swift +// Visual_Novel_iOS +// +// Created by mh on 2025/11/11. +// + +import Foundation +import Moya + +let RoleActorProvider = 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 RoleActorApi { + static let useMock: Bool = false + + case roleActor(params: [String: Any]) +} + +extension RoleActorApi: TargetType { + 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 .roleActor: + return "/tts/config/list" + } + } + + var method: Moya.Method { + return .post + } + + var task: Task { + var mParams = [String: Any]() + switch self { + case .roleActor(let params): + // 将传入的参数赋值给 mParams + mParams = params + } + return .requestParameters(parameters: mParams, encoding: JSONEncoding.default) + } + + var headers: [String : String]? { + return APIConfig.apiHeaders() + } + + var sampleData: Data { + switch self { + case .roleActor: + return Data() + } + } +} + + diff --git a/Visual_Novel_iOS/Src/Modules/Chat/Session/SessionController+Event.swift b/Visual_Novel_iOS/Src/Modules/Chat/Session/SessionController+Event.swift index 206a0c2..bce6d41 100755 --- a/Visual_Novel_iOS/Src/Modules/Chat/Session/SessionController+Event.swift +++ b/Visual_Novel_iOS/Src/Modules/Chat/Session/SessionController+Event.swift @@ -398,6 +398,11 @@ extension SessionController { // let vc = ChatSettingListController() // vc.aiId = aiId // navigationController?.pushViewController(vc, animated: true) + + roleActorViewModel.loadRoleActor(language: "en") { model in + print(model) + } + UIView.animate(withDuration: 0.25) { self.swipeBgView.alpha = 1.0 self.swipeView.snp.updateConstraints { make in diff --git a/Visual_Novel_iOS/Src/Modules/Chat/Session/SessionController.swift b/Visual_Novel_iOS/Src/Modules/Chat/Session/SessionController.swift index 2dee45d..7b3e348 100755 --- a/Visual_Novel_iOS/Src/Modules/Chat/Session/SessionController.swift +++ b/Visual_Novel_iOS/Src/Modules/Chat/Session/SessionController.swift @@ -15,6 +15,11 @@ class SessionController: CLBaseViewController { var swipeView: ChatSettingSwipeView! var swipeBgView: UIView! + lazy var roleActorViewModel: RoleActorViewModel = { + let model = RoleActorViewModel() + return model + }() + // MARK: BottomViews var bottomViewsStackV : InputStackView! var toolView: UIView! diff --git a/Visual_Novel_iOS/Src/Modules/Chat/Session/ViewModel/RoleActorViewModel.swift b/Visual_Novel_iOS/Src/Modules/Chat/Session/ViewModel/RoleActorViewModel.swift new file mode 100644 index 0000000..b3eddf5 --- /dev/null +++ b/Visual_Novel_iOS/Src/Modules/Chat/Session/ViewModel/RoleActorViewModel.swift @@ -0,0 +1,58 @@ +// +// RoleActorViewModel.swift +// Visual_Novel_iOS +// +// Created by mh on 2025/11/11. +// + +import Foundation + +struct RoleActorRequest: Codable { + var language: String = "" + // 1:男性 2:女性 不传查所有 + var gender: Int? +} + + +struct RoleActorModel: Codable { + var total: Int = 0 + var rows: [RoleActorItem] = [] +} + +struct RoleActorItem: Codable { + var ttsId: Int? + var language: Int? + var desLanguage: String? + var gender: Int? + // 解锁方式 (付费、vip、公开) + var rules: Int? + var nameLanguage: String? + var headPortrait: String? + var displayAudio: String? +} + +class RoleActorViewModel { + + func loadRoleActor(language: String, gender: Int? = nil, completion: ((_ datas: RoleActorModel?) -> Void)?) { + var req = RoleActorRequest() + req.language = language + req.gender = gender + + let params = req.toNonNilDictionary() + + RoleActorProvider.request(.roleActor(params: params), modelType: RoleActorModel.self) { result in + switch result { + case .success(let model): + // 如果返回的是单个 RoleActorModel,将其包装成数组 + 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 711c83a..92fb2b7 100644 --- a/Visual_Novel_iOS/Src/Modules/Chat/Setting/View/ChatSettingSwipeView.swift +++ b/Visual_Novel_iOS/Src/Modules/Chat/Setting/View/ChatSettingSwipeView.swift @@ -217,7 +217,7 @@ extension ChatSettingSwipeView: UITableViewDelegate, UITableViewDataSource { tableView.performBatchUpdates({ tableView.insertRows(at: [insertIndexPath], with: .fade) - tableView.reloadRows(at: [indexPath], with: .none) +// tableView.reloadRows(at: [indexPath], with: .none) }, completion: { [weak self] _ in // 在 cell 完全布局后,设置筛选器状态 DispatchQueue.main.async { @@ -245,7 +245,7 @@ extension ChatSettingSwipeView: UITableViewDelegate, UITableViewDataSource { tableView.performBatchUpdates({ tableView.deleteRows(at: [deleteIndexPath], with: .fade) - tableView.reloadRows(at: [indexPath], with: .none) +// tableView.reloadRows(at: [indexPath], with: .none) }, completion: nil) } } diff --git a/Visual_Novel_iOS/Src/Modules/Roles/View/CLRoleCollectionCell.swift b/Visual_Novel_iOS/Src/Modules/Roles/View/CLRoleCollectionCell.swift index 6c33f33..9791c72 100644 --- a/Visual_Novel_iOS/Src/Modules/Roles/View/CLRoleCollectionCell.swift +++ b/Visual_Novel_iOS/Src/Modules/Roles/View/CLRoleCollectionCell.swift @@ -138,7 +138,7 @@ class CLRoleCollectionCell: UICollectionViewCell { bookImgView.sd_setImage(with: URL(string: item.sourceCoverImage ?? ""), placeholderImage: nil) tagLab.text = item.tags.compactMap { "#\($0.name)" }.joined(separator: "/") sourceLab.text = item.score?.truncateString(places: 1) - self.bookBgImgView.isHidden = item.sourceType != .novel + self.readImgView.isHidden = item.sourceType != .novel self.playImgView.isHidden = item.sourceType != .video self.remindLab.text = item.sourceName } diff --git a/Visual_Novel_iOS/Src/Modules/Roles/View/RolesRootPageView.swift b/Visual_Novel_iOS/Src/Modules/Roles/View/RolesRootPageView.swift index b42946b..9f35fe5 100644 --- a/Visual_Novel_iOS/Src/Modules/Roles/View/RolesRootPageView.swift +++ b/Visual_Novel_iOS/Src/Modules/Roles/View/RolesRootPageView.swift @@ -22,7 +22,9 @@ class RolesRootPageView: CLContainer { var onRefresh: (() -> Void)? var onLoadMore: (() -> Void)? - var data: [RoleItem] = [] + var data: [RoleItem] = [ +// RoleItem(id: "1", name: "哈哈哈", coverImage: nil, sourceCoverImage: nil, sourceName: "社会", headPortrait: nil, score: 1, description: "sdhfdshfjsdfjdsfh", updateTime: nil, sourceId: nil, sourceType: .novel, commonCount: 1, tags: []) + ] var hasMoreData: Bool = true // 是否还有更多数据 // lazy var topView: CLTopHeaderView = { @@ -177,7 +179,7 @@ extension RolesRootPageView: UICollectionViewDelegate, UICollectionViewDataSourc let maxHeight = lineHeight * CGFloat(maxLines) // 真实文本高度(不会超过 maxHeight) - let textSize = (model.name ?? "").boundingRect( + let textSize = (model.description ?? "").boundingRect( with: CGSize(width: itemWidth - 20, height: maxHeight), // 高度封顶 options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font: font],