feat: 完善相册 AI 修图模板选择流程

This commit is contained in:
2026-08-11 15:22:08 +08:00
parent 43c75c8e36
commit e64dcec094
11 changed files with 261 additions and 202 deletions
@@ -250,7 +250,8 @@ final class TravelAlbumDetailViewControllerTests: XCTestCase {
api.aiRetouchTemplatesResponse = TravelAlbumAIRetouchTemplatesResponse(
refinedTemplates: [TravelAlbumAIRetouchTemplate(id: 1, name: "清透", previewURL: "")],
atmosphereTemplates: [TravelAlbumAIRetouchTemplate(id: 2, name: "暖阳", previewURL: "")],
coverTemplates: [TravelAlbumAIRetouchTemplate(id: 3, name: "杂志", previewURL: "")]
coverTemplates: [TravelAlbumAIRetouchTemplate(id: 3, name: "杂志", previewURL: "")],
remainingQuota: 8
)
let viewModel = TravelAlbumAIRetouchTemplateViewModel(
albumId: 9,
@@ -308,7 +309,8 @@ final class TravelAlbumDetailViewControllerTests: XCTestCase {
api.aiRetouchTemplatesResponse = TravelAlbumAIRetouchTemplatesResponse(
refinedTemplates: [TravelAlbumAIRetouchTemplate(id: 1, name: "清透", previewURL: "")],
atmosphereTemplates: [TravelAlbumAIRetouchTemplate(id: 2, name: "暖阳", previewURL: "")],
coverTemplates: [TravelAlbumAIRetouchTemplate(id: 3, name: "杂志", previewURL: "")]
coverTemplates: [TravelAlbumAIRetouchTemplate(id: 3, name: "杂志", previewURL: "")],
remainingQuota: 8
)
let viewModel = TravelAlbumAIRetouchTemplateViewModel(
albumId: 9,
@@ -330,9 +332,13 @@ final class TravelAlbumDetailViewControllerTests: XCTestCase {
XCTAssertTrue(accessibleLabels.contains("氛围感修图,选填"))
XCTAssertTrue(accessibleLabels.contains("封面风格模板"))
let modeCell = TravelAlbumAIRetouchModeCell(frame: .zero)
modeCell.apply()
XCTAssertEqual(modeCell.accessibilityLabel, "AI精修,按张收费,剩余张数暂不可用")
let quotaLabel = try XCTUnwrap(
controller.view.findSubview {
$0.accessibilityIdentifier == "travelAlbum.aiRetouchRemainingQuotaLabel"
} as? UILabel
)
XCTAssertEqual(quotaLabel.text, "剩余 8 次")
XCTAssertEqual(quotaLabel.accessibilityLabel, "剩余8次修图额度")
}
func testAIRetouchTemplateCellExposesSelectedState() {
@@ -358,7 +364,8 @@ final class TravelAlbumDetailViewControllerTests: XCTestCase {
let api = TravelAlbumMockAPI()
api.aiRetouchTemplatesResponse = TravelAlbumAIRetouchTemplatesResponse(
refinedTemplates: [TravelAlbumAIRetouchTemplate(id: 11, name: "清透", previewURL: "")],
atmosphereTemplates: [TravelAlbumAIRetouchTemplate(id: 21, name: "暖阳", previewURL: "")]
atmosphereTemplates: [TravelAlbumAIRetouchTemplate(id: 21, name: "暖阳", previewURL: "")],
remainingQuota: 8
)
let project = TravelAlbumPreviewProject(
originalMaterialId: 7,
@@ -451,10 +458,11 @@ final class TravelAlbumDetailViewControllerTests: XCTestCase {
projects: [TravelAlbumPreviewProject(originalMaterialId: 7, assets: [original, retouched])],
totalCount: 1,
startProjectIndex: 0,
reload: {
reload: { materialId in
XCTAssertEqual(materialId, 7)
reloadCount += 1
let assets = reloadCount == 1 ? [original, retouched] : [original]
return ([TravelAlbumPreviewProject(originalMaterialId: 7, assets: assets)], 1)
return TravelAlbumPreviewProject(originalMaterialId: 7, assets: assets)
}
)
controller.loadViewIfNeeded()
@@ -487,6 +495,43 @@ final class TravelAlbumDetailViewControllerTests: XCTestCase {
XCTAssertTrue(controller.view.allLabels().contains { $0.text == "原图.jpg" })
}
func testPreviewRefreshUsesGlobalLoadingWithoutToast() async throws {
GlobalLoadingManager.shared.hideAll()
defer { GlobalLoadingManager.shared.hideAll() }
let original = TravelAlbumPreviewAsset(
id: "original-7",
kind: .original,
fileURL: "",
coverURL: "",
fileName: "原图.jpg",
fileSize: 0
)
let project = TravelAlbumPreviewProject(originalMaterialId: 7, assets: [original])
let controller = TravelAlbumPhotoPreviewViewController(
projects: [project],
totalCount: 1,
startProjectIndex: 0,
reload: { _ in
try await Task.sleep(nanoseconds: 50_000_000)
return project
}
)
controller.loadViewIfNeeded()
let refreshButton = try XCTUnwrap(
controller.view.findSubview {
$0.accessibilityIdentifier == "travelAlbum.previewRefreshButton"
} as? UIButton
)
refreshButton.sendActions(for: .touchUpInside)
XCTAssertTrue(GlobalLoadingManager.shared.isShowing)
await waitUntil { !GlobalLoadingManager.shared.isShowing && refreshButton.isEnabled }
XCTAssertFalse(GlobalLoadingManager.shared.isShowing)
XCTAssertFalse(controller.view.allLabels().contains { $0.text == "刷新成功" })
XCTAssertFalse(controller.view.allLabels().contains { $0.text == "刷新失败" })
}
func testMaterialCellShowsSemanticStatusBadgeWithoutOverlappingSelectionCheck() throws {
let cases: [(TravelAlbumMaterial, String, UInt)] = [
(TravelAlbumMaterial(isPurchased: true), "已购", 0x475569),