diff --git a/apps/admin/e2e/albums-governance.spec.ts b/apps/admin/e2e/albums-governance.spec.ts index ccf6c17..977a9fc 100644 --- a/apps/admin/e2e/albums-governance.spec.ts +++ b/apps/admin/e2e/albums-governance.spec.ts @@ -26,6 +26,8 @@ async function createAlbumViaUi(page: Page): Promise { await page.keyboard.press('Enter') await page.getByTestId('album-form-submit').click() await expect(page.getByTestId('album-form-dialog')).toBeHidden() + // UI06:列表默认封面卡片网格;表格断言前先切换 + await page.getByTestId('album-view-table').click() await expect( page.getByTestId('album-list-table').getByText(`E2E治理相册 ${suffix}`) ).toBeVisible() @@ -78,6 +80,8 @@ test('批量隐藏:多选 2 → 汇总 toast + 行原位已隐藏 + 公开列 seedFixture('gov-photos', albumId, adminCredentials().account, '3') await page.reload() + // UI06:照片默认网格视图;表格断言前先切换 + await page.getByTestId('photo-view-table').click() await expect(page.getByTestId('photo-list-table')).toBeVisible() const rows = page.getByTestId('photo-list-table').locator('tbody tr') await expect(rows).toHaveCount(3) diff --git a/apps/admin/e2e/albums.spec.ts b/apps/admin/e2e/albums.spec.ts index 62c5a83..f896b46 100644 --- a/apps/admin/e2e/albums.spec.ts +++ b/apps/admin/e2e/albums.spec.ts @@ -26,6 +26,8 @@ async function createAlbumViaUi(page: Page): Promise { await page.keyboard.press('Enter') await page.getByTestId('album-form-submit').click() await expect(page.getByTestId('album-form-dialog')).toBeHidden() + // UI06:列表默认封面卡片网格;表格断言前先切换 + await page.getByTestId('album-view-table').click() await expect(page.getByTestId('album-list-table').getByText(`E2E相册 ${suffix}`)).toBeVisible() return suffix } @@ -99,6 +101,8 @@ test('详情:邀请成员 → 建分类 → 传 banner → 照片管理(隐 // ── 照片管理:直种两种状态 → 列表可见 → 隐藏已发布照片 ── seedFixture('photos', albumId, adminCredentials().account) await page.reload() + // UI06:照片默认网格视图;表格断言前先切换 + await page.getByTestId('photo-view-table').click() await expect(page.getByTestId('photo-list-table')).toBeVisible() // 用处理状态标签(就绪)锚定行:隐藏后行的可见性文案变化,锚点保持稳定 diff --git a/apps/admin/src/components/business/albums/AlbumCoverCard.vue b/apps/admin/src/components/business/albums/AlbumCoverCard.vue new file mode 100644 index 0000000..c791b50 --- /dev/null +++ b/apps/admin/src/components/business/albums/AlbumCoverCard.vue @@ -0,0 +1,206 @@ + + + + + + diff --git a/apps/admin/src/components/business/albums/AlbumSummaryHeader.vue b/apps/admin/src/components/business/albums/AlbumSummaryHeader.vue new file mode 100644 index 0000000..c1767ff --- /dev/null +++ b/apps/admin/src/components/business/albums/AlbumSummaryHeader.vue @@ -0,0 +1,306 @@ + + + + + + diff --git a/apps/admin/src/components/business/albums/PhotoGrid.vue b/apps/admin/src/components/business/albums/PhotoGrid.vue new file mode 100644 index 0000000..acd25d8 --- /dev/null +++ b/apps/admin/src/components/business/albums/PhotoGrid.vue @@ -0,0 +1,271 @@ + + + + + + diff --git a/apps/admin/src/components/business/albums/PhotoPreviewDialog.vue b/apps/admin/src/components/business/albums/PhotoPreviewDialog.vue new file mode 100644 index 0000000..daab848 --- /dev/null +++ b/apps/admin/src/components/business/albums/PhotoPreviewDialog.vue @@ -0,0 +1,293 @@ + + + + + + diff --git a/apps/admin/src/locales/langs/en.json b/apps/admin/src/locales/langs/en.json index 68f84d5..2045a58 100644 --- a/apps/admin/src/locales/langs/en.json +++ b/apps/admin/src/locales/langs/en.json @@ -304,7 +304,17 @@ "actionEdit": "Edit", "actionMembers": "Members", "actionDetail": "Detail", - "endAtUndecided": "TBD" + "endAtUndecided": "TBD", + "subtitle": "A clear view of every event and the team behind it.", + "searchPlaceholder": "Search loaded albums", + "statusAll": "All access states", + "statusPublicOnly": "Public", + "statusPrivateOnly": "Private", + "viewGrid": "Card view", + "viewTable": "Table view", + "loadedCount": "{n} albums loaded", + "cursorNote": "Sorted by creation time · cursor-based loading", + "emptyFiltered": "No albums match these filters" }, "form": { "createTitle": "New Album", @@ -359,7 +369,9 @@ "fieldUpdatedAt": "Updated", "fieldStatsSyncedAt": "Stats Synced At", "statsNotSynced": "Not synced yet", - "fieldOwnerId": "Owner" + "fieldOwnerId": "Owner", + "subtitle": "Album workspace · Manage the moments that matter", + "share": "Share album" }, "banner": { "kind": "Asset Kind", @@ -457,7 +469,10 @@ "batchResultAllOk": "Batch hide finished: {ok} succeeded", "batchFailuresTitle": "Failures (per item)", "batchColPhoto": "Photo ID", - "batchColReason": "Reason" + "batchColReason": "Reason", + "view": "Details", + "selectedCount": "{n} selected", + "onlyPublicSelectable": "Only published photos can be selected" }, "transfer": { "entry": "Transfer Owner", @@ -514,6 +529,31 @@ "hint": "Guests open the public album page via this link; it stops working when \"Publicly visible\" is off.", "qrTitle": "Scan to open", "publicOffHint": "\"Publicly visible\" is off for this album — the viewer link is currently unreachable" + }, + "card": { + "public": "Public", + "private": "Private", + "active": "Active", + "offline": "Offline", + "uploadsOn": "Uploads on", + "uploadsOff": "Uploads off" + }, + "summary": { + "eventTimezone": "Event time · {tz}", + "venueUnset": "Venue unset", + "venueLabel": "Event venue", + "publicOn": "Public access on", + "publicOff": "Public access off", + "uploadOn": "Staff uploads enabled", + "uploadOff": "Staff uploads disabled", + "advanced": "Advanced info" + }, + "photosGrid": { + "openAria": "View photo", + "selectAria": "Select photo", + "noPreview": "No preview", + "uploader": "Uploader", + "filterAll": "All photos" } }, "usage": { diff --git a/apps/admin/src/locales/langs/zh.json b/apps/admin/src/locales/langs/zh.json index eae1b1e..a0ad549 100644 --- a/apps/admin/src/locales/langs/zh.json +++ b/apps/admin/src/locales/langs/zh.json @@ -304,7 +304,17 @@ "actionEdit": "编辑", "actionMembers": "成员", "actionDetail": "详情", - "endAtUndecided": "未定" + "endAtUndecided": "未定", + "subtitle": "以活动为中心,管理相册、团队和每一张照片。", + "searchPlaceholder": "搜索当前已加载相册", + "statusAll": "全部公开状态", + "statusPublicOnly": "公开", + "statusPrivateOnly": "未公开", + "viewGrid": "卡片视图", + "viewTable": "表格视图", + "loadedCount": "已加载 {n} 个相册", + "cursorNote": "按创建时间排序 · 游标加载", + "emptyFiltered": "没有符合筛选条件的相册" }, "form": { "createTitle": "新建相册", @@ -359,7 +369,9 @@ "fieldUpdatedAt": "更新时间", "fieldStatsSyncedAt": "统计同步时间", "statsNotSynced": "尚无回传", - "fieldOwnerId": "负责人" + "fieldOwnerId": "负责人", + "subtitle": "相册工作区 · 管理活动中的精彩瞬间", + "share": "分享相册" }, "banner": { "kind": "资产类型", @@ -457,7 +469,10 @@ "batchResultAllOk": "批量隐藏完成:成功 {ok} 张", "batchFailuresTitle": "失败明细(逐项原因)", "batchColPhoto": "照片 ID", - "batchColReason": "原因" + "batchColReason": "原因", + "view": "详情", + "selectedCount": "{n} 张已选", + "onlyPublicSelectable": "仅已公开照片可批量选择" }, "transfer": { "entry": "负责人交接", @@ -514,6 +529,31 @@ "hint": "观众经此链接访问相册公开页;关闭「公开可见」开关后链接失效。", "qrTitle": "扫码打开", "publicOffHint": "该相册已关闭「公开可见」,观众端链接当前不可访问" + }, + "card": { + "public": "公开", + "private": "未公开", + "active": "进行中", + "offline": "已下线", + "uploadsOn": "可上传", + "uploadsOff": "上传关闭" + }, + "summary": { + "eventTimezone": "活动时间 · {tz}", + "venueUnset": "未设置地点", + "venueLabel": "活动地点", + "publicOn": "允许公开访问", + "publicOff": "公开访问关闭", + "uploadOn": "工作人员上传已开启", + "uploadOff": "工作人员上传已关闭", + "advanced": "高级信息" + }, + "photosGrid": { + "openAria": "查看照片", + "selectAria": "选择照片", + "noPreview": "无预览", + "uploader": "上传者", + "filterAll": "全部照片" } }, "usage": { diff --git a/apps/admin/src/types/api/album.d.ts b/apps/admin/src/types/api/album.d.ts index 6f69714..74e8a26 100644 --- a/apps/admin/src/types/api/album.d.ts +++ b/apps/admin/src/types/api/album.d.ts @@ -26,6 +26,12 @@ declare namespace Api { address?: string | null } + /** 短期签名 URL 摘要(900s 量级过期;过期经受控重取) */ + interface SignedUrl { + url: string + expiresAt: string + } + /** 相册列表条目(完整开关与多语言标题) */ interface Summary { id: string @@ -46,6 +52,8 @@ declare namespace Api { updatedAt: string /** 统计副本最近同步时间(P2 A27 ov→cn 快照应用时刻;null=尚无回传) */ statsSyncedAt: string | null + /** 封面签名 URL 摘要(UI06;契约已有、服务端已返回;可空=无封面,用中性占位) */ + cover: SignedUrl | null } /** 分类(相册详情内嵌,按 sortOrder 升序) */ @@ -203,6 +211,11 @@ declare namespace Api { account: string displayName: string } + /** 管理预览签名 URL(UI06;契约 AdminPhoto.preview,服务端已返回;隐藏照仅 admin/owner 签发;可空=无预览/处理中) */ + preview: { + thumb: SignedUrl | null + browse: SignedUrl | null + } | null } /** 管理照片列表响应(游标分页) */ diff --git a/apps/admin/src/utils/albums/batchHide.spec.ts b/apps/admin/src/utils/albums/batchHide.spec.ts index 1bb31b5..c39a01b 100644 --- a/apps/admin/src/utils/albums/batchHide.spec.ts +++ b/apps/admin/src/utils/albums/batchHide.spec.ts @@ -17,7 +17,8 @@ function photoOf(photoId: string): Api.Album.AdminPhoto { uploadedAt: '2026-09-01T00:00:00.000Z', publishedAt: '2026-09-01T00:00:00.000Z', likeCount: 0, - uploader: { userId: 'u1', account: 'staff', displayName: 'Staff' } + uploader: { userId: 'u1', account: 'staff', displayName: 'Staff' }, + preview: null } } diff --git a/apps/admin/src/views/albums/detail/AlbumPhotos.vue b/apps/admin/src/views/albums/detail/AlbumPhotos.vue index 85f913e..7607938 100644 --- a/apps/admin/src/views/albums/detail/AlbumPhotos.vue +++ b/apps/admin/src/views/albums/detail/AlbumPhotos.vue @@ -7,20 +7,24 @@ 信息卡展示,照片行无单行同步时间)。 --> - +