完成70%
This commit is contained in:
@@ -1,342 +1,50 @@
|
||||
<!-- 页面编号:F-08;用途:相册详情与照片墙。 -->
|
||||
<!-- 页面编号:F-08;用途:相册照片墙。响应未声明照片展示 DTO 时保持关闭。 -->
|
||||
<template>
|
||||
<view class="album-detail-page" :class="`album-state--${albumState}`">
|
||||
<view class="album-detail-page">
|
||||
<ModulePageBackground module="family" />
|
||||
<view class="album-detail-header"><PageHeader title="相册详情" custom-back @back="requestBack" /></view>
|
||||
|
||||
<view class="album-detail-header"><PageHeader title="相册详情" custom-back @back="returnToAlbums" /></view>
|
||||
<view class="album-detail-content">
|
||||
<view v-if="albumState === 'expired'" class="album-expired-state">
|
||||
<view class="album-state-card__body">
|
||||
<text class="album-state-card__eyebrow">相册状态</text>
|
||||
<text class="album-state-card__title">相册已失效或入口无效</text>
|
||||
<text class="album-state-card__copy">当前家谱中没有找到这本相册,页面不会回退到其他相册。</text>
|
||||
<AppButton block :label="genealogyId ? '返回相册列表' : '返回上一页'" @click="returnToAlbums" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<template v-else>
|
||||
<view class="album-heading">
|
||||
<text class="album-heading__eyebrow">家族影像</text>
|
||||
<text class="album-heading__title">{{ album.name }}</text>
|
||||
<text class="album-heading__copy">{{ album.description }}</text>
|
||||
<text class="album-heading__count"
|
||||
>{{ albumState === "empty" ? "0 张照片" : `${photos.length} 张照片` }}</text
|
||||
>
|
||||
</view>
|
||||
|
||||
<view v-if="albumState === 'empty'" class="album-empty-state">
|
||||
<view class="album-state-card__body">
|
||||
<text class="album-state-card__eyebrow">照片墙</text>
|
||||
<text class="album-state-card__title">相册里还没有照片</text>
|
||||
<text class="album-state-card__copy"
|
||||
>添加第一张团圆影像,把值得珍藏的时刻留在这里。</text
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="album-wall">
|
||||
<view
|
||||
class="album-photo-tile album-photo-tile--hero"
|
||||
@click="openPreview(0)"
|
||||
>
|
||||
<image
|
||||
class="album-hero-photo"
|
||||
:src="photos[0].src"
|
||||
:alt="photos[0].alt"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<text class="album-photo-tile__caption">{{ photos[0].caption }}</text>
|
||||
</view>
|
||||
|
||||
<view class="album-photo-grid">
|
||||
<view
|
||||
v-for="(photo, index) in photos.slice(1)"
|
||||
:key="photo.src"
|
||||
class="album-photo-tile album-photo-tile--grid"
|
||||
@click="openPreview(index + 1)"
|
||||
>
|
||||
<image
|
||||
class="album-photo-tile__image"
|
||||
:src="photo.src"
|
||||
:alt="photo.alt"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<text class="album-photo-tile__caption">{{ photo.caption }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="album-upload-action" @click="toUpload">
|
||||
<AppButton block label="添加照片" />
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<view v-if="previewVisible" class="album-preview">
|
||||
<view class="album-preview__toolbar">
|
||||
<text class="album-preview__title">照片预览</text>
|
||||
<view class="album-preview__close" @click="closePreview"><text>关闭</text></view>
|
||||
</view>
|
||||
<image
|
||||
class="album-preview__image"
|
||||
:src="photos[previewIndex].src"
|
||||
:alt="photos[previewIndex].alt"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view class="album-preview__footer">
|
||||
<text class="album-preview__position"
|
||||
>{{ previewIndex + 1 }} / {{ photos.length }}</text
|
||||
>
|
||||
<text class="album-preview__caption">{{ photos[previewIndex].caption }}</text>
|
||||
<view class="album-state-card">
|
||||
<text>{{ stateCopy.title }}</text>
|
||||
<text>{{ stateCopy.copy }}</text>
|
||||
<AppButton block :label="stateCopy.action" @click="returnToAlbums" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { onBackPress, onLoad } from "@dcloudio/uni-app";
|
||||
import { computed, ref } from "vue";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import AppButton from "@/components/AppButton.vue";
|
||||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import { findFamilyAlbumFixture } from "@/data/mock.js";
|
||||
import {
|
||||
goBack,
|
||||
handleBackPress,
|
||||
openPage,
|
||||
returnTo,
|
||||
runBackGuard,
|
||||
} from "@/utils/navigation.js";
|
||||
import { goBack, returnTo } from "@/utils/navigation.js";
|
||||
|
||||
const albumState = ref("normal");
|
||||
const genealogyId = ref("");
|
||||
const albumId = ref("");
|
||||
const album = ref(null);
|
||||
const photos = ref([]);
|
||||
const previewVisible = ref(false);
|
||||
const previewIndex = ref(0);
|
||||
|
||||
const openPreview = (index) => {
|
||||
previewIndex.value = index;
|
||||
previewVisible.value = true;
|
||||
albumState.value = "preview";
|
||||
};
|
||||
|
||||
const closePreview = () => {
|
||||
previewVisible.value = false;
|
||||
albumState.value = "normal";
|
||||
};
|
||||
|
||||
const toUpload = () =>
|
||||
openPage(
|
||||
"F09",
|
||||
{ genealogyId: genealogyId.value, albumId: albumId.value },
|
||||
"F08",
|
||||
);
|
||||
|
||||
const returnToAlbums = () =>
|
||||
genealogyId.value
|
||||
? returnTo("F07", { genealogyId: genealogyId.value })
|
||||
: goBack();
|
||||
|
||||
const hasValidContext = computed(() => /^[1-9]\d*$/.test(genealogyId.value) && /^[1-9]\d*$/.test(albumId.value));
|
||||
const stateCopy = computed(() => hasValidContext.value
|
||||
? { title: "照片墙待后端字段合同", copy: "照片列表没有声明照片 ID、OSS 访问地址、标题、说明或相册展示字段;页面已停止展示本地图片和本地预览。", action: "返回相册列表" }
|
||||
: { title: "相册入口无效", copy: "没有取得有效家谱或相册标识,页面不会回退到其他相册。", action: "返回上一页" },
|
||||
);
|
||||
onLoad((query) => {
|
||||
genealogyId.value = String(query.genealogyId || "");
|
||||
albumId.value = String(query.albumId || "");
|
||||
album.value = findFamilyAlbumFixture(genealogyId.value, albumId.value);
|
||||
photos.value = album.value?.photos || [];
|
||||
if (!album.value) {
|
||||
albumState.value = "expired";
|
||||
previewVisible.value = false;
|
||||
return;
|
||||
}
|
||||
albumState.value =
|
||||
query.state === "empty"
|
||||
? "empty"
|
||||
: query.state === "preview"
|
||||
? "preview"
|
||||
: query.state === "expired"
|
||||
? "expired"
|
||||
: "normal";
|
||||
previewVisible.value = albumState.value === "preview";
|
||||
genealogyId.value = String(query?.genealogyId || "");
|
||||
albumId.value = String(query?.albumId || "");
|
||||
});
|
||||
|
||||
const requestBack = () =>
|
||||
runBackGuard({
|
||||
transientOpen: previewVisible.value,
|
||||
"close-transient": closePreview,
|
||||
});
|
||||
|
||||
onBackPress((event) => handleBackPress(event, requestBack));
|
||||
|
||||
const returnToAlbums = () => /^[1-9]\d*$/.test(genealogyId.value)
|
||||
? returnTo("F07", { genealogyId: genealogyId.value })
|
||||
: goBack();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "../../styles/adaptive-frame-profiles.scss" as adaptive;
|
||||
|
||||
.album-detail-page {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
flex-direction: column;
|
||||
background: $paper;
|
||||
}
|
||||
.album-detail-header,
|
||||
.album-detail-content {
|
||||
z-index: 1;
|
||||
}
|
||||
.album-detail-content {
|
||||
padding: 22rpx 24rpx calc(48rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
.album-heading {
|
||||
padding: 18rpx 12rpx 20rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.album-heading text,
|
||||
.album-state-card__body text,
|
||||
.album-preview__footer text {
|
||||
display: block;
|
||||
}
|
||||
.album-heading__eyebrow,
|
||||
.album-state-card__eyebrow {
|
||||
color: $brand-red;
|
||||
font-size: 22rpx;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
.album-heading__title,
|
||||
.album-state-card__title {
|
||||
margin-top: 8rpx;
|
||||
color: $ink;
|
||||
font-family: STKaiti, KaiTi, serif;
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.album-heading__copy,
|
||||
.album-state-card__copy {
|
||||
margin-top: 10rpx;
|
||||
color: $ink-muted;
|
||||
font-size: 23rpx;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.album-heading__count {
|
||||
margin-top: 12rpx;
|
||||
color: #806a51;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
.album-wall {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
.album-photo-tile {
|
||||
position: relative;
|
||||
min-height: 44px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
border: 2rpx solid rgba(179, 133, 63, 0.62);
|
||||
background: rgba(245, 238, 226, 0.86);
|
||||
box-shadow: 0 8rpx 18rpx rgba(91, 60, 32, 0.12);
|
||||
}
|
||||
.album-photo-tile--hero {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
.album-photo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14rpx;
|
||||
margin-top: 14rpx;
|
||||
}
|
||||
.album-photo-tile--grid {
|
||||
aspect-ratio: 4 / 3;
|
||||
}
|
||||
.album-hero-photo,
|
||||
.album-photo-tile__image {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.album-photo-tile__caption {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 10rpx 12rpx;
|
||||
background: rgba(48, 35, 25, 0.72);
|
||||
color: #fffaf0;
|
||||
font-size: 21rpx;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.album-upload-action {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
.album-empty-state,
|
||||
.album-expired-state {
|
||||
@include adaptive.adaptive-family-panel;
|
||||
width: 100%;
|
||||
min-height: 430rpx;
|
||||
}
|
||||
.album-state-card__body {
|
||||
padding: 104rpx 40rpx 62rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.album-state-card__body > .app-button {
|
||||
margin-top: 34rpx;
|
||||
}
|
||||
.album-preview {
|
||||
position: fixed;
|
||||
z-index: 80;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: rgba(24, 17, 13, 0.96);
|
||||
}
|
||||
.album-preview__toolbar {
|
||||
display: flex;
|
||||
min-height: 96rpx;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: calc(env(safe-area-inset-top) + 12rpx) 24rpx 12rpx;
|
||||
}
|
||||
.album-preview__title {
|
||||
color: #fffaf0;
|
||||
font-size: 29rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.album-preview__close {
|
||||
display: flex;
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fffaf0;
|
||||
font-size: 25rpx;
|
||||
}
|
||||
.album-preview__image {
|
||||
width: 100%;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.album-preview__footer {
|
||||
padding: 20rpx 28rpx calc(30rpx + env(safe-area-inset-bottom));
|
||||
color: #fffaf0;
|
||||
text-align: center;
|
||||
}
|
||||
.album-preview__position {
|
||||
font-size: 23rpx;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
.album-preview__caption {
|
||||
margin-top: 8rpx;
|
||||
color: rgba(255, 250, 240, 0.78);
|
||||
font-size: 22rpx;
|
||||
}
|
||||
@media (max-width: 340px) {
|
||||
.album-detail-content {
|
||||
padding-right: 16rpx;
|
||||
padding-left: 16rpx;
|
||||
}
|
||||
.album-photo-grid {
|
||||
gap: 10rpx;
|
||||
}
|
||||
}
|
||||
.album-detail-page { display: flex; min-height: 100vh; flex-direction: column; background: $paper; }
|
||||
.album-detail-header, .album-detail-content { z-index: 1; }
|
||||
.album-detail-content { padding: 22rpx 24rpx calc(48rpx + env(safe-area-inset-bottom)); }
|
||||
.album-state-card { @include adaptive.adaptive-family-panel; width: 100%; min-height: 430rpx; padding: 104rpx 40rpx 62rpx; box-sizing: border-box; text-align: center; }
|
||||
.album-state-card text { display: block; }
|
||||
.album-state-card text:first-child { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 36rpx; font-weight: 700; }
|
||||
.album-state-card text:nth-child(2) { margin-top: 10rpx; color: $ink-muted; font-size: 23rpx; line-height: 1.6; }
|
||||
.album-state-card .app-button { margin-top: 34rpx; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user