完善首页权限申请流程与队列播报体验。
对齐 Android 无权限强制弹窗、角色下拉与申请页交互,补充 UIButton configuration 适配,并增强景区排队 TTS 与相关单元测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -18,10 +18,16 @@ final class PermissionApplyViewModel {
|
||||
private(set) var isSubmitting = false
|
||||
private(set) var showRolePicker = false
|
||||
private(set) var showScenicPicker = false
|
||||
private(set) var rolePermissionList: [RolePermissionResponse] = []
|
||||
private(set) var scenicSearchQuery = ""
|
||||
private(set) var pendingDeletion: PermissionDeletionTarget?
|
||||
private(set) var showScenicListDialog = false
|
||||
private(set) var dialogRoleName = ""
|
||||
private(set) var dialogScenicList: [ScenicInfo] = []
|
||||
|
||||
var onStateChange: (() -> Void)?
|
||||
var onShowMessage: ((String) -> Void)?
|
||||
var onSubmitSuccess: ((String) -> Void)?
|
||||
var onSubmitSuccess: (() -> Void)?
|
||||
|
||||
private let appStore: AppStore
|
||||
|
||||
@ -30,6 +36,18 @@ final class PermissionApplyViewModel {
|
||||
loadRolesFromLocal()
|
||||
}
|
||||
|
||||
/// 当前表单是否满足 Android 的提交条件。
|
||||
var canSubmit: Bool {
|
||||
selectedRoleId != nil && !selectedScenicIds.isEmpty && !isSubmitting
|
||||
}
|
||||
|
||||
/// 按当前搜索词过滤景区,保持接口原始顺序。
|
||||
var filteredScenics: [ScenicOption] {
|
||||
let query = scenicSearchQuery.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !query.isEmpty else { return availableScenics }
|
||||
return availableScenics.filter { $0.name.localizedCaseInsensitiveContains(query) }
|
||||
}
|
||||
|
||||
/// 从待审核数据预填表单(status=3 去编辑)。
|
||||
func initWithPendingData(_ pending: RoleApplyPendingResponse) {
|
||||
selectedRoleId = pending.roleId
|
||||
@ -45,7 +63,7 @@ final class PermissionApplyViewModel {
|
||||
}
|
||||
|
||||
func loadRolesFromLocal() {
|
||||
let rolePermissionList = appStore.permissions.rolePermissionList()
|
||||
rolePermissionList = appStore.permissions.rolePermissionList()
|
||||
var seen = Set<Int>()
|
||||
availableRoles = rolePermissionList.compactMap { item in
|
||||
guard !seen.contains(item.role.id) else { return nil }
|
||||
@ -102,6 +120,7 @@ final class PermissionApplyViewModel {
|
||||
func loadAvailableScenics(api: HomeAPI?) async {
|
||||
guard let roleId = selectedRoleId else { return }
|
||||
guard let api else { return }
|
||||
guard !isLoadingScenics else { return }
|
||||
isLoadingScenics = true
|
||||
notifyStateChange()
|
||||
do {
|
||||
@ -143,7 +162,37 @@ final class PermissionApplyViewModel {
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
/// 更新景区下拉菜单的搜索词。
|
||||
func updateScenicSearchQuery(_ query: String) {
|
||||
scenicSearchQuery = query
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
func removeSelectedScenic(id: Int, name: String) {
|
||||
pendingDeletion = .scenic(id: id, name: name)
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
/// 确认删除当前待删除的角色或景区。
|
||||
func confirmDeletion() {
|
||||
guard let pendingDeletion else { return }
|
||||
switch pendingDeletion {
|
||||
case .role:
|
||||
clearRoleSelectionImmediately()
|
||||
case .scenic(let id, let name):
|
||||
removeSelectedScenicImmediately(id: id, name: name)
|
||||
}
|
||||
self.pendingDeletion = nil
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
/// 取消删除确认。
|
||||
func cancelDeletion() {
|
||||
pendingDeletion = nil
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
private func removeSelectedScenicImmediately(id: Int, name: String) {
|
||||
selectedScenicIds.removeAll { $0 == id }
|
||||
selectedScenicNames.removeAll { $0 == name }
|
||||
availableScenics = availableScenics.map { item in
|
||||
@ -153,7 +202,6 @@ final class PermissionApplyViewModel {
|
||||
}
|
||||
return copy
|
||||
}
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
/// 已有角色下已开通的景区列表。
|
||||
@ -164,6 +212,12 @@ final class PermissionApplyViewModel {
|
||||
}
|
||||
|
||||
func clearRoleSelection() {
|
||||
guard !selectedRoleName.isEmpty else { return }
|
||||
pendingDeletion = .role(name: selectedRoleName)
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
private func clearRoleSelectionImmediately() {
|
||||
selectedRoleId = nil
|
||||
selectedRoleName = ""
|
||||
selectedScenicIds = []
|
||||
@ -174,33 +228,42 @@ final class PermissionApplyViewModel {
|
||||
copy.isSelected = false
|
||||
return copy
|
||||
}
|
||||
}
|
||||
|
||||
/// 展示已有角色下的全部景区。
|
||||
func showScenicList(roleName: String, scenics: [ScenicInfo]) {
|
||||
dialogRoleName = roleName
|
||||
dialogScenicList = scenics
|
||||
showScenicListDialog = true
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
/// 关闭已有景区列表弹窗。
|
||||
func hideScenicList() {
|
||||
dialogRoleName = ""
|
||||
dialogScenicList = []
|
||||
showScenicListDialog = false
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
func submit(api: HomeAPI) async {
|
||||
guard let roleId = selectedRoleId else {
|
||||
onShowMessage?("请选择角色")
|
||||
return
|
||||
}
|
||||
let newScenicIds = selectedScenicIds.filter { id in
|
||||
!existingScenicIds(for: roleId).contains(id)
|
||||
}
|
||||
guard !newScenicIds.isEmpty else {
|
||||
onShowMessage?("请至少选择一个景区")
|
||||
guard canSubmit, let roleId = selectedRoleId else {
|
||||
onShowMessage?("请选择景区和角色")
|
||||
return
|
||||
}
|
||||
|
||||
isSubmitting = true
|
||||
notifyStateChange()
|
||||
do {
|
||||
let result = try await api.submitRoleApply(scenicIds: newScenicIds, roleId: roleId)
|
||||
onShowMessage?("提交成功")
|
||||
onSubmitSuccess?(result.code)
|
||||
try await api.submitRoleApply(scenicIds: selectedScenicIds, roleId: roleId)
|
||||
isSubmitting = false
|
||||
notifyStateChange()
|
||||
onSubmitSuccess?()
|
||||
} catch {
|
||||
onShowMessage?("提交失败,请稍后重试")
|
||||
isSubmitting = false
|
||||
notifyStateChange()
|
||||
}
|
||||
isSubmitting = false
|
||||
notifyStateChange()
|
||||
}
|
||||
|
||||
private func existingScenicIds(for roleId: Int) -> Set<Int> {
|
||||
|
||||
Reference in New Issue
Block a user