docs: register asset status acceptance — admin branding tab shows current banner/cover/logo
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -15,15 +15,16 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@photo/contracts": "workspace:*",
|
"@photo/contracts": "workspace:*",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
"vue": "~3.4.0",
|
"vue": "~3.4.0",
|
||||||
"vue-i18n": "^9.14.0",
|
"vue-i18n": "^9.14.0",
|
||||||
"vue-router": "~4.4.0",
|
"vue-router": "~4.4.0"
|
||||||
"qrcode": "^1.5.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@photo/db-cn": "workspace:*",
|
"@photo/db-cn": "workspace:*",
|
||||||
"@photo/storage": "workspace:*",
|
"@photo/storage": "workspace:*",
|
||||||
"@types/node": "^22.10.0",
|
"@types/node": "^22.10.0",
|
||||||
|
"@types/qrcode": "^1.5.6",
|
||||||
"@vitejs/plugin-vue": "^5.2.0",
|
"@vitejs/plugin-vue": "^5.2.0",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import PhotoActionBar from './viewer/PhotoActionBar.vue';
|
|||||||
import HeartParticles from './viewer/HeartParticles.vue';
|
import HeartParticles from './viewer/HeartParticles.vue';
|
||||||
import InteractionNotice from './viewer/InteractionNotice.vue';
|
import InteractionNotice from './viewer/InteractionNotice.vue';
|
||||||
import ThumbnailStrip, { type ThumbnailItem } from './viewer/ThumbnailStrip.vue';
|
import ThumbnailStrip, { type ThumbnailItem } from './viewer/ThumbnailStrip.vue';
|
||||||
import { shareOrCopy } from '../support/share';
|
import ShareSheet from './viewer/ShareSheet.vue';
|
||||||
import { reducedMotion, toggleReducedMotion } from '../ui/prefs';
|
import { reducedMotion, toggleReducedMotion } from '../ui/prefs';
|
||||||
import { positionDisplay } from '../viewer/position';
|
import { positionDisplay } from '../viewer/position';
|
||||||
import { MIN_SCALE, panBy, resetZoom, wheelFactor, zoomAt, type ZoomState } from '../viewer/zoom';
|
import { MIN_SCALE, panBy, resetZoom, wheelFactor, zoomAt, type ZoomState } from '../viewer/zoom';
|
||||||
@@ -432,21 +432,12 @@ function onDownloadVariant(variant: DownloadVariant): void {
|
|||||||
// 可选中文本。分享目标为照片深链(含 lang 参数,ActivityPage 组装),
|
// 可选中文本。分享目标为照片深链(含 lang 参数,ActivityPage 组装),
|
||||||
// 不分享 OSS 签名 URL ——
|
// 不分享 OSS 签名 URL ——
|
||||||
|
|
||||||
const shareState = ref<'idle' | 'copied' | 'failed'>('idle');
|
const shareSheetOpen = ref(false);
|
||||||
const shareUrlText = ref('');
|
const shareUrlText = ref('');
|
||||||
let shareResetTimer: ReturnType<typeof setTimeout> | undefined;
|
|
||||||
|
|
||||||
async function shareCurrent(): Promise<void> {
|
function shareCurrent(): void {
|
||||||
shareUrlText.value = props.shareUrl ?? window.location.href;
|
shareUrlText.value = props.shareUrl ?? window.location.href;
|
||||||
const outcome = await shareOrCopy({ title: props.albumTitle ?? '', url: shareUrlText.value });
|
shareSheetOpen.value = true;
|
||||||
shareState.value = outcome === 'copied' ? 'copied' : outcome === 'failed' ? 'failed' : 'idle';
|
|
||||||
if (shareResetTimer !== undefined) clearTimeout(shareResetTimer);
|
|
||||||
// 失败时保留可选中文本(不自动消失),成功反馈数秒后收起。
|
|
||||||
if (outcome === 'copied') {
|
|
||||||
shareResetTimer = setTimeout(() => {
|
|
||||||
shareState.value = 'idle';
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// —— 顶栏「⋯」更多菜单(§5.1:必须有实际功能——减少动画开关) ——
|
// —— 顶栏「⋯」更多菜单(§5.1:必须有实际功能——减少动画开关) ——
|
||||||
@@ -519,7 +510,6 @@ onBeforeUnmount(() => {
|
|||||||
window.removeEventListener('keydown', onKeydown);
|
window.removeEventListener('keydown', onKeydown);
|
||||||
document.removeEventListener('pointerdown', onOuterPointerDown, true);
|
document.removeEventListener('pointerdown', onOuterPointerDown, true);
|
||||||
document.body.style.overflow = savedBodyOverflow;
|
document.body.style.overflow = savedBodyOverflow;
|
||||||
if (shareResetTimer !== undefined) clearTimeout(shareResetTimer);
|
|
||||||
stopLocalLikeListener?.(); // 粒子订阅随大图卸载解除(useLikes 侧不残留)
|
stopLocalLikeListener?.(); // 粒子订阅随大图卸载解除(useLikes 侧不残留)
|
||||||
// 焦点归还触发元素(瀑布流单元格;深链直达时为 body——无可归还则不动)。
|
// 焦点归还触发元素(瀑布流单元格;深链直达时为 body——无可归还则不动)。
|
||||||
if (savedFocus !== null && savedFocus.isConnected) savedFocus.focus();
|
if (savedFocus !== null && savedFocus.isConnected) savedFocus.focus();
|
||||||
@@ -628,14 +618,16 @@ onBeforeUnmount(() => {
|
|||||||
点击定位 = 缩略图条共用入口(序列未含该照片时忽略——不伪造定位) -->
|
点击定位 = 缩略图条共用入口(序列未含该照片时忽略——不伪造定位) -->
|
||||||
<InteractionNotice ref="interactionNotice" @select="selectThumbnail" />
|
<InteractionNotice ref="interactionNotice" @select="selectThumbnail" />
|
||||||
|
|
||||||
|
<!-- 分享弹层(2026-09-23 用户需求:二维码 + 链接 + 复制,页面级提示退役) -->
|
||||||
|
<ShareSheet
|
||||||
|
v-model="shareSheetOpen"
|
||||||
|
:heading="t('viewer.sharePhoto')"
|
||||||
|
:title="albumTitle ?? ''"
|
||||||
|
:url="shareUrlText"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="stage-notices">
|
<div class="stage-notices">
|
||||||
<p v-if="extending" class="loading">{{ t('album.loading') }}</p>
|
<p v-if="extending" class="loading">{{ t('album.loading') }}</p>
|
||||||
<!-- 分享反馈轻提示(回退复制成功/失败;失败保留可选中文本;原生分享
|
|
||||||
成功/取消不显示——面板已收尾,§5.4) -->
|
|
||||||
<p v-if="shareState !== 'idle'" class="share-status" role="status">
|
|
||||||
{{ shareState === 'copied' ? t('viewer.shareCopied') : t('viewer.shareCopyFailed') }}
|
|
||||||
<span v-if="shareState === 'failed'" class="share-url">{{ shareUrlText }}</span>
|
|
||||||
</p>
|
|
||||||
<!-- 下载失败轻提示(端点失败/网络;DOWNLOAD_DISABLED 专属文案):
|
<!-- 下载失败轻提示(端点失败/网络;DOWNLOAD_DISABLED 专属文案):
|
||||||
仅归属到触发下载的那张照片(§5.4 异步错误归属)。 -->
|
仅归属到触发下载的那张照片(§5.4 异步错误归属)。 -->
|
||||||
<p
|
<p
|
||||||
@@ -907,21 +899,6 @@ onBeforeUnmount(() => {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
color: #d1d5db;
|
color: #d1d5db;
|
||||||
}
|
}
|
||||||
.share-status {
|
|
||||||
margin: 0;
|
|
||||||
border-radius: var(--viewer-radius-pill, 20px);
|
|
||||||
background: rgba(22, 28, 36, 0.92);
|
|
||||||
color: #f3f4f6;
|
|
||||||
padding: 6px 16px;
|
|
||||||
font-size: 0.8125rem;
|
|
||||||
pointer-events: auto;
|
|
||||||
max-width: min(92%, 480px);
|
|
||||||
}
|
|
||||||
.share-url {
|
|
||||||
user-select: all;
|
|
||||||
word-break: break-all;
|
|
||||||
color: #9fc3f5;
|
|
||||||
}
|
|
||||||
.download-failed {
|
.download-failed {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border-radius: var(--viewer-radius-pill, 20px);
|
border-radius: var(--viewer-radius-pill, 20px);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onBeforeUnmount, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import type { PublicAlbum, SortMode } from '../../api/client';
|
import type { PublicAlbum, SortMode } from '../../api/client';
|
||||||
@@ -154,9 +154,6 @@ watch(
|
|||||||
{ flush: 'post' },
|
{ flush: 'post' },
|
||||||
);
|
);
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
if (shareResetTimer !== undefined) clearTimeout(shareResetTimer);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -352,17 +349,6 @@ onBeforeUnmount(() => {
|
|||||||
outline: 2px solid var(--viewer-primary, #1a73e8);
|
outline: 2px solid var(--viewer-primary, #1a73e8);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
.share-status {
|
|
||||||
display: block;
|
|
||||||
margin: 2px 0 0;
|
|
||||||
color: var(--viewer-text-secondary, #647084);
|
|
||||||
font-size: 0.8125rem;
|
|
||||||
}
|
|
||||||
.share-url {
|
|
||||||
user-select: text;
|
|
||||||
color: var(--viewer-primary, #1a73e8);
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-expand {
|
.text-expand {
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@@ -58,16 +58,17 @@ async function drawQr(): Promise<void> {
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [open.value, props.url] as const,
|
() => [open.value, props.url] as const,
|
||||||
([isOpen], [wasOpen]) => {
|
([isOpen, url], previous) => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
void nextTick().then(() => {
|
void nextTick().then(() => {
|
||||||
drawQr();
|
drawQr();
|
||||||
sheetEl.value?.focus();
|
sheetEl.value?.focus();
|
||||||
});
|
});
|
||||||
} else if (wasOpen) {
|
} else if (previous !== undefined && previous[0]) {
|
||||||
copyState.value = 'idle';
|
copyState.value = 'idle';
|
||||||
if (copyResetTimer !== undefined) clearTimeout(copyResetTimer);
|
if (copyResetTimer !== undefined) clearTimeout(copyResetTimer);
|
||||||
}
|
}
|
||||||
|
void url;
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 266 KiB |
@@ -151,6 +151,16 @@
|
|||||||
- 部署:cn+ov 双栈 `ui06-8e674a9-20260923`(server+web 均滚动;迁移无待办);模拟器 App 已装新包
|
- 部署:cn+ov 双栈 `ui06-8e674a9-20260923`(server+web 均滚动;迁移无待办);模拟器 App 已装新包
|
||||||
- 登记偏差:已删除相册的展示位绑定不自动解绑(拉取端容错已有;再次绑定校验拒绝 deleted)
|
- 登记偏差:已删除相册的展示位绑定不自动解绑(拉取端容错已有;再次绑定校验拒绝 deleted)
|
||||||
|
|
||||||
|
### 5.z 品牌资产现状展示(2026-09-23,用户报告「宣传图/封面看不到设置了什么」;commit `2618ad3`)
|
||||||
|
|
||||||
|
- 根因:AlbumBannerUpload 只在上传后显示预览,从不加载已有资产;契约亦无资产查询端点(P1b 删列表)。
|
||||||
|
- 新增契约 `listAlbumAssetStatus`(GET /albums/{albumId}/assets):每 (kind, locale) 最新 effective
|
||||||
|
行 + 现签 URL;权限同详情(可见即读);deleted → 404。
|
||||||
|
- Admin 品牌资产页签改造:进入即加载——三种 kind 槽位卡(banner 分全局/zh/en、cover、logo),
|
||||||
|
有图显示预览+设置时间,未设置显示空态;上传成功自动刷新(替换语义)。
|
||||||
|
- 验证:API 56/56、admin 87/87+build;测试服务器真实渲染确认(banner·全局有图 09:40 设置、
|
||||||
|
cover 有图 09-22 设置、zh/en/Logo 空态、未认证 401)。部署 cn+ov `ui06-2618ad3-20260923`。
|
||||||
|
|
||||||
### 6.1 A0 偏差复核(代码级,以 app-preview styles.css 实测为准)
|
### 6.1 A0 偏差复核(代码级,以 app-preview styles.css 实测为准)
|
||||||
|
|
||||||
UI-03 完成态与 app-preview 的核心差异(修前):
|
UI-03 完成态与 app-preview 的核心差异(修前):
|
||||||
|
|||||||
Generated
+3
@@ -208,6 +208,9 @@ importers:
|
|||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^22.10.0
|
specifier: ^22.10.0
|
||||||
version: 22.20.3
|
version: 22.20.3
|
||||||
|
'@types/qrcode':
|
||||||
|
specifier: ^1.5.6
|
||||||
|
version: 1.5.6
|
||||||
'@vitejs/plugin-vue':
|
'@vitejs/plugin-vue':
|
||||||
specifier: ^5.2.0
|
specifier: ^5.2.0
|
||||||
version: 5.2.4(vite@5.4.21(@types/node@22.20.3)(lightningcss@1.32.0)(sass@1.104.1)(terser@5.51.2))(vue@3.4.38(typescript@5.9.3))
|
version: 5.2.4(vite@5.4.21(@types/node@22.20.3)(lightningcss@1.32.0)(sass@1.104.1)(terser@5.51.2))(vue@3.4.38(typescript@5.9.3))
|
||||||
|
|||||||
Reference in New Issue
Block a user