修复任务提交与弹窗交互问题
This commit is contained in:
@ -61,6 +61,14 @@ enum MaterialMediaType: Int, CaseIterable, Sendable, Equatable {
|
||||
case .video: 1
|
||||
}
|
||||
}
|
||||
|
||||
/// 云盘文件类型,视频为 1、图片为 2。
|
||||
var cloudFileType: Int {
|
||||
switch self {
|
||||
case .image: 2
|
||||
case .video: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 素材列表项,对齐 Android `MaterialItemEntity`。
|
||||
|
||||
@ -354,6 +354,48 @@ 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
|
||||
@ -376,6 +418,16 @@ 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 {
|
||||
|
||||
@ -61,20 +61,6 @@ final class TaskAPI {
|
||||
)
|
||||
}
|
||||
|
||||
/// 登记 OSS 上传后的文件 URL。
|
||||
func registerUploadedFileURL(scenicId: Int, fileURL: String, folderId: Int = 0) async throws {
|
||||
let _: EmptyResponse = try await client.send(
|
||||
APIRequest(
|
||||
method: .post,
|
||||
path: "/api/yf-handset-app/photog/album/file-upload-url",
|
||||
queryItems: [
|
||||
URLQueryItem(name: "scenic_id", value: String(scenicId)),
|
||||
URLQueryItem(name: "file_url", value: fileURL),
|
||||
URLQueryItem(name: "folder_id", value: String(folderId)),
|
||||
]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// 空响应占位,用于无 data 字段的成功接口。
|
||||
|
||||
@ -177,4 +177,14 @@ struct TaskFileItem: Sendable, Equatable, Hashable, Identifiable {
|
||||
return uploadFileName ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
/// 素材来源角标文案。
|
||||
var sourceBadgeText: String {
|
||||
switch source {
|
||||
case .cloud:
|
||||
return "云盘"
|
||||
case .upload:
|
||||
return "本地"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,9 +25,13 @@ final class CloudStoragePickForTaskViewModel {
|
||||
private var totalCount = 0
|
||||
private let pageSize = 20
|
||||
private let importedFileIDs: Set<Int>
|
||||
private let allowedFileType: Int?
|
||||
|
||||
init(importedFileIDs: Set<Int> = []) {
|
||||
/// 初始化云盘多选 ViewModel,可按云盘文件类型限制选择范围。
|
||||
init(importedFileIDs: Set<Int> = [], allowedFileType: Int? = nil) {
|
||||
self.importedFileIDs = importedFileIDs
|
||||
self.allowedFileType = allowedFileType
|
||||
self.filterType = allowedFileType ?? 0
|
||||
}
|
||||
|
||||
/// 当前文件夹 ID。
|
||||
@ -94,6 +98,10 @@ final class CloudStoragePickForTaskViewModel {
|
||||
return
|
||||
}
|
||||
guard file.isSelectableMedia else { return }
|
||||
if let allowedFileType, file.type != allowedFileType {
|
||||
onShowMessage?(allowedFileType == 1 ? "请选择视频素材" : "请选择图片素材")
|
||||
return
|
||||
}
|
||||
if isImported(file.id) {
|
||||
onShowMessage?("已选择")
|
||||
return
|
||||
|
||||
@ -139,16 +139,6 @@ final class WildPhotographerReportSubmitViewModel {
|
||||
return "当前定位:\(reportLocation)"
|
||||
}
|
||||
|
||||
var locationSubtitleText: String {
|
||||
if isUpdatingLocation {
|
||||
return "正在更新定位,请稍候"
|
||||
}
|
||||
if locationConfirmed {
|
||||
return "已获取最新定位,如不准确请点击更新定位"
|
||||
}
|
||||
return "如位置不准确,请点击更新定位"
|
||||
}
|
||||
|
||||
init(
|
||||
homeViewModel: WildPhotographerReportHomeViewModel,
|
||||
locationProvider: any LocationProviding = LocationProvider.shared
|
||||
@ -358,12 +348,13 @@ final class WildPhotographerReportSubmitViewModel {
|
||||
let address = snapshot.address.isEmpty
|
||||
? String(format: "%.5f, %.5f", snapshot.latitude, snapshot.longitude)
|
||||
: snapshot.address
|
||||
let reportAddress = Self.simplifiedReportLocation(address)
|
||||
locationSnapshot = HomeLocationSnapshot(
|
||||
latitude: snapshot.latitude,
|
||||
longitude: snapshot.longitude,
|
||||
address: address
|
||||
address: reportAddress
|
||||
)
|
||||
reportLocation = address
|
||||
reportLocation = reportAddress
|
||||
locationConfirmed = true
|
||||
isUpdatingLocation = false
|
||||
notifyStateChange()
|
||||
@ -393,6 +384,40 @@ final class WildPhotographerReportSubmitViewModel {
|
||||
return types.filter { seen.insert($0.value).inserted }
|
||||
}
|
||||
|
||||
private static func simplifiedReportLocation(_ address: String) -> String {
|
||||
let trimmed = address.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty else { return trimmed }
|
||||
let detail = WildReportLocationParser.split(trimmed).detail
|
||||
let simplified = removeAdministrativePrefix(from: detail)
|
||||
return simplified.isEmpty ? trimmed : simplified
|
||||
}
|
||||
|
||||
private static func removeAdministrativePrefix(from address: String) -> String {
|
||||
var text = address
|
||||
removeLeadingRegion(from: &text, matches: [
|
||||
"北京市", "天津市", "上海市", "重庆市",
|
||||
"内蒙古自治区", "广西壮族自治区", "西藏自治区", "宁夏回族自治区", "新疆维吾尔自治区"
|
||||
])
|
||||
removeLeadingRegion(from: &text, suffixes: ["省"], maxLength: 8)
|
||||
removeLeadingRegion(from: &text, suffixes: ["自治州", "地区", "盟", "市"], maxLength: 12)
|
||||
removeLeadingRegion(from: &text, suffixes: ["自治县", "新区", "区", "县", "旗", "市"], maxLength: 12)
|
||||
return text.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
}
|
||||
|
||||
private static func removeLeadingRegion(from text: inout String, matches: [String]) {
|
||||
guard let match = matches.first(where: { text.hasPrefix($0) }) else { return }
|
||||
text.removeFirst(match.count)
|
||||
}
|
||||
|
||||
private static func removeLeadingRegion(from text: inout String, suffixes: [String], maxLength: Int) {
|
||||
guard let match = suffixes.compactMap({ suffix -> String? in
|
||||
guard let range = text.range(of: suffix) else { return nil }
|
||||
let prefix = String(text[..<range.upperBound])
|
||||
return prefix.count <= maxLength ? prefix : nil
|
||||
}).min(by: { $0.count < $1.count }) else { return }
|
||||
text.removeFirst(match.count)
|
||||
}
|
||||
|
||||
private var evidenceCount: Int {
|
||||
images.count + videos.count
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user