模块化 AppStore 并完善素材管理与个人空间设置。
将会话、权限、位置、收款与排队存储拆分为独立模块,同步更新各 ViewModel 与单元测试,并补充素材管理 UI 与个人空间设置交互。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -141,7 +141,12 @@ final class MaterialListViewModel {
|
||||
)
|
||||
totalCount = response.total
|
||||
orderInfo = response.order ?? MaterialOrderInfo()
|
||||
items = reset ? response.list : items + response.list
|
||||
if reset {
|
||||
items = response.list
|
||||
} else {
|
||||
let existingIDs = Set(items.map(\.id))
|
||||
items.append(contentsOf: response.list.filter { !existingIDs.contains($0.id) })
|
||||
}
|
||||
canLoadMore = items.count < totalCount
|
||||
} catch is CancellationError {
|
||||
return
|
||||
@ -244,6 +249,7 @@ final class MaterialFormViewModel {
|
||||
private(set) var coverImage: MaterialUploadedMediaItem?
|
||||
private(set) var tagList: [MaterialTagItem] = []
|
||||
private(set) var tagInput = ""
|
||||
private(set) var pendingDeleteTag: MaterialTagItem?
|
||||
private(set) var uploadDialogState: MaterialUploadDialogState?
|
||||
private(set) var isSubmitting = false
|
||||
private(set) var isLoadingDetail = false
|
||||
@ -259,7 +265,7 @@ final class MaterialFormViewModel {
|
||||
/// 初始化素材表单 ViewModel。
|
||||
init(
|
||||
mode: Mode = .create,
|
||||
currentScenicIdProvider: @escaping () -> Int = { AppStore.shared.currentScenicId }
|
||||
currentScenicIdProvider: @escaping () -> Int = { AppStore.shared.session.currentScenicId }
|
||||
) {
|
||||
self.mode = mode
|
||||
self.currentScenicIdProvider = currentScenicIdProvider
|
||||
@ -295,7 +301,7 @@ final class MaterialFormViewModel {
|
||||
|
||||
/// 更新标签输入。
|
||||
func updateTagInput(_ text: String) {
|
||||
tagInput = text
|
||||
tagInput = String(text.prefix(15))
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
@ -327,9 +333,23 @@ final class MaterialFormViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
/// 删除本地标签。
|
||||
func deleteTag(_ tag: MaterialTagItem) {
|
||||
/// 请求删除标签,等待用户确认。
|
||||
func requestDeleteTag(_ tag: MaterialTagItem) {
|
||||
pendingDeleteTag = tag
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
/// 取消删除标签。
|
||||
func cancelDeleteTag() {
|
||||
pendingDeleteTag = nil
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
/// 确认删除当前待删除标签。
|
||||
func confirmDeleteTag() {
|
||||
guard let tag = pendingDeleteTag else { return }
|
||||
tagList.removeAll { $0.id == tag.id }
|
||||
pendingDeleteTag = nil
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
@ -354,48 +374,6 @@ final class MaterialFormViewModel {
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
/// 增加云盘素材文件,文件已在云盘中时直接使用其 URL。
|
||||
func addCloudMediaFiles(_ files: [CloudFile]) {
|
||||
guard !files.isEmpty else { return }
|
||||
let remaining = mediaType.maxCount - uploadedMediaList.count
|
||||
guard remaining > 0 else {
|
||||
onShowMessage?(mediaType == .video ? "视频只能上传1个" : "图片最多上传9张")
|
||||
return
|
||||
}
|
||||
|
||||
var seenCloudIDs = importedCloudFileIDs
|
||||
var accepted: [MaterialUploadedMediaItem] = []
|
||||
for file in files where accepted.count < remaining {
|
||||
let fileURL = file.fileUrl.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard file.type == mediaType.cloudFileType, !fileURL.isEmpty, !seenCloudIDs.contains(file.id) else {
|
||||
continue
|
||||
}
|
||||
seenCloudIDs.insert(file.id)
|
||||
accepted.append(
|
||||
MaterialUploadedMediaItem(
|
||||
id: "cloud_\(file.id)",
|
||||
data: Data(),
|
||||
thumbnailData: nil,
|
||||
previewURL: file.coverUrl.isEmpty ? fileURL : file.coverUrl,
|
||||
fileName: file.name.isEmpty ? "cloud_file_\(file.id)" : file.name,
|
||||
size: file.fileSize,
|
||||
width: 0,
|
||||
height: 0,
|
||||
ossUrl: fileURL,
|
||||
isUploading: false,
|
||||
uploadProgress: 100
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
guard !accepted.isEmpty else {
|
||||
onShowMessage?(mediaType == .video ? "请选择视频素材" : "请选择图片素材")
|
||||
return
|
||||
}
|
||||
uploadedMediaList.append(contentsOf: accepted)
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
/// 设置封面并立即上传。
|
||||
func setCoverImage(_ item: MaterialUploadedMediaItem, uploader: any MaterialOSSUploading) async {
|
||||
coverImage = item
|
||||
@ -418,16 +396,6 @@ final class MaterialFormViewModel {
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
/// 已从云盘导入的文件 ID。
|
||||
var importedCloudFileIDs: Set<Int> {
|
||||
Set(
|
||||
uploadedMediaList.compactMap { item in
|
||||
guard item.id.hasPrefix("cloud_") else { return nil }
|
||||
return Int(item.id.dropFirst("cloud_".count))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// 上传或编辑素材。
|
||||
func submit(api: any MaterialManagementServing) async {
|
||||
guard !materialName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
||||
|
||||
Reference in New Issue
Block a user