feat(admin): batch ops, transfer, watermark, usage and qr share pages

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-20 21:10:30 +08:00
parent 618d1d796c
commit f08d68bebf
31 changed files with 2717 additions and 34 deletions
+47
View File
@@ -0,0 +1,47 @@
/**
* 批量隐藏结果汇总(P4a A16 逐项部分成功语义,纯函数供 vitest 覆盖)
*
* - 汇总:成功/失败计数 + 失败明细(photoId + 错误码)——toast 文案与
* 失败详情弹窗共用同一份归集结果
* - 错误码本地化:apiMsg 有词条用词条(PHOTO_NOT_PUBLIC/PHOTO_NOT_FOUND),
* 未知码原样展示(契约 code 为开放枚举,服务端后续扩充不破前端)
* - 行内回显归集:结果 photoId → 列表行索引(shallowRef 列表整行替换用)
*/
import type { useI18n } from 'vue-i18n'
/** 批量隐藏结果归集 */
export interface BatchHideSummary {
/** 成功项数 */
okCount: number
/** 失败项数 */
failCount: number
/** 失败明细(与结果同序) */
failures: Array<{ photoId: string; code: string }>
}
/** 逐项结果 → 汇总(ok 计成功;ok=false 收 code,缺 code 兜底 UNKNOWN) */
export function summarizeBatchHideResults(results: Api.Album.BatchHideItem[]): BatchHideSummary {
const failures: BatchHideSummary['failures'] = []
let okCount = 0
for (const item of results) {
if (item.ok) {
okCount += 1
} else {
failures.push({ photoId: item.photoId, code: item.code ?? 'UNKNOWN' })
}
}
return { okCount, failCount: failures.length, failures }
}
/** 失败码 → 本地化文案(apiMsg 词条优先,未知码原样;t 为 i18n 翻译函数) */
export function localizeBatchHideCode(t: ReturnType<typeof useI18n>['t'], code: string): string {
const key = `apiMsg.${code}`
const text = t(key)
// vue-i18n 缺词条时回退键名本身(apiMsg.${code})——非词条原样展示 code
return text === key ? code : text
}
/** 结果 photoId → 列表行索引(不在当前页的结果忽略——换页/过滤竞态兜底) */
export function indexOfPhotoId(items: Api.Album.AdminPhoto[], photoId: string): number {
return items.findIndex((item) => item.photoId === photoId)
}