mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-07-27 04:20:43 +08:00
feat(gallery): 实现相册功能对接后端API,替换静态模拟数据
1. 重构相册页面,使用真实的Halo图片插件API获取分组与图片数据 2. 新增图片预览工具函数,支持大图预览 3. 更新滚动加载hook,补充响应式原始数据与加载状态控制 4. 调整相册面板组件参数与样式,适配真实数据展示 5. 修复开发环境接口地址配置
This commit is contained in:
Vendored
+2
-1
@@ -7,4 +7,5 @@ VITE_SHOW_SOURCEMAP=false
|
|||||||
|
|
||||||
# development mode 后台请求地址
|
# development mode 后台请求地址
|
||||||
# VITE_UNI_HALO_BASEURL='https://www.yijunzhao.cn'
|
# VITE_UNI_HALO_BASEURL='https://www.yijunzhao.cn'
|
||||||
VITE_UNI_HALO_BASEURL='https://www.xhhao.com'
|
# VITE_UNI_HALO_BASEURL='https://www.xhhao.com'
|
||||||
|
VITE_UNI_HALO_BASEURL='https://ryanc.cc'
|
||||||
|
|||||||
@@ -18,6 +18,27 @@ export interface IQueryPhotosRequest {
|
|||||||
size?: number
|
size?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IPhotoGroup {
|
||||||
|
metadata: {
|
||||||
|
creationTimestamp: string
|
||||||
|
generateName: string
|
||||||
|
name: string
|
||||||
|
version: string
|
||||||
|
}
|
||||||
|
spec: {
|
||||||
|
displayName: string
|
||||||
|
priority: number
|
||||||
|
}
|
||||||
|
status: {
|
||||||
|
photoCount: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPhotoTag {
|
||||||
|
name: string
|
||||||
|
photoCount: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface IPhoto {
|
export interface IPhoto {
|
||||||
metadata: {
|
metadata: {
|
||||||
creationTimestamp: string
|
creationTimestamp: string
|
||||||
@@ -36,12 +57,10 @@ export interface IPhoto {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取图库分组列表
|
* 获取图库分组列表
|
||||||
* @param params 获取图库分组列表请求参数
|
|
||||||
* @returns 获取图库分组列表响应参数
|
* @returns 获取图库分组列表响应参数
|
||||||
*/
|
*/
|
||||||
export function queryPhotoGroups(params: IQueryPhotoGroupsRequest) {
|
export function queryPhotoGroups() {
|
||||||
return http.Get<any>(`/apis/api.photo.halo.run/v1alpha1/photogroups`, {
|
return http.Get<Array<IPhotoGroup>>(`/apis/api.photo.halo.run/v1alpha1/photogroups`, {
|
||||||
params,
|
|
||||||
meta: {
|
meta: {
|
||||||
isHalo: true,
|
isHalo: true,
|
||||||
},
|
},
|
||||||
@@ -51,10 +70,11 @@ export function queryPhotoGroups(params: IQueryPhotoGroupsRequest) {
|
|||||||
/**
|
/**
|
||||||
* 获取图库标签列表 列出所有不重复的标签名称及对应图片数量,支持可选的 name 参数进行大小写不敏感模糊过滤
|
* 获取图库标签列表 列出所有不重复的标签名称及对应图片数量,支持可选的 name 参数进行大小写不敏感模糊过滤
|
||||||
* @param params 获取图库标签列表请求参数
|
* @param params 获取图库标签列表请求参数
|
||||||
|
* @param params.name 标签名称
|
||||||
* @returns 获取图库标签列表响应参数
|
* @returns 获取图库标签列表响应参数
|
||||||
*/
|
*/
|
||||||
export function queryPhotoTags(params: any) {
|
export function queryPhotoTags(params: { name?: string }) {
|
||||||
return http.Get<any>(`/apis/api.photo.halo.run/v1alpha1/tags`, {
|
return http.Get<IHaloListResponseBase<IPhotoTag>>(`/apis/api.photo.halo.run/v1alpha1/tags`, {
|
||||||
params,
|
params,
|
||||||
meta: {
|
meta: {
|
||||||
isHalo: true,
|
isHalo: true,
|
||||||
@@ -70,6 +90,7 @@ export function queryPhotoTags(params: any) {
|
|||||||
export function queryPhotos(params: IQueryPhotosRequest) {
|
export function queryPhotos(params: IQueryPhotosRequest) {
|
||||||
return http.Get<IHaloListResponseBase<IPhoto>>(`/apis/api.photo.halo.run/v1alpha1/photos`, {
|
return http.Get<IHaloListResponseBase<IPhoto>>(`/apis/api.photo.halo.run/v1alpha1/photos`, {
|
||||||
params,
|
params,
|
||||||
|
cacheFor: null,
|
||||||
meta: {
|
meta: {
|
||||||
isHalo: true,
|
isHalo: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ import { queryPhotos } from '@/api/halo-plugin/photo'
|
|||||||
import { completeUrl } from '@/utils/url'
|
import { completeUrl } from '@/utils/url'
|
||||||
import { useHaloScroll } from '@/hooks/useHaloScroll'
|
import { useHaloScroll } from '@/hooks/useHaloScroll'
|
||||||
import { random } from '@/utils/base/random'
|
import { random } from '@/utils/base/random'
|
||||||
|
import { preview } from '@/utils/imageHelper'
|
||||||
import type { IPhoto, IQueryPhotosRequest } from '@/api/halo-plugin/photo'
|
import type { IPhoto, IQueryPhotosRequest } from '@/api/halo-plugin/photo'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
groupName: string
|
groupMetadataName: string
|
||||||
|
groupDisplayName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<IProps>()
|
const props = defineProps<IProps>()
|
||||||
@@ -16,6 +18,7 @@ const emits = defineEmits<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
response, // 响应式的原始数据响应
|
||||||
list, // 响应式的数据列表
|
list, // 响应式的数据列表
|
||||||
loading, // 是否加载中
|
loading, // 是否加载中
|
||||||
finished, // 是否已全部加载
|
finished, // 是否已全部加载
|
||||||
@@ -27,6 +30,7 @@ const {
|
|||||||
params: {
|
params: {
|
||||||
page: 1,
|
page: 1,
|
||||||
size: 12,
|
size: 12,
|
||||||
|
group: props.groupMetadataName.trim() ?? undefined,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -36,61 +40,62 @@ function getImageItemClass() {
|
|||||||
const randomIndex = random(0, imageItemClass.length - 1)
|
const randomIndex = random(0, imageItemClass.length - 1)
|
||||||
return imageItemClass[randomIndex]
|
return imageItemClass[randomIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handlePreview(item: IPhoto) {
|
||||||
|
preview(completeUrl(item.spec.url), list.value.map(item => completeUrl(item.spec.url)))
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- 相册列表:根据分组显示 -->
|
<!-- 相册列表:根据分组显示 -->
|
||||||
<view class="uh-backdrop-blur-xs fixed inset-0 z-110 flex items-center justify-center bg-black/20">
|
<view
|
||||||
|
class="uh-backdrop-blur-xs fixed inset-0 z-110 flex items-center justify-center bg-black/20"
|
||||||
|
@click.stop="emits('close')"
|
||||||
|
>
|
||||||
<wd-transition :show="true" :lazy-render="true" :duration="300" name="slide-left">
|
<wd-transition :show="true" :lazy-render="true" :duration="300" name="slide-left">
|
||||||
<view class="box-border w-[88vw] rounded-2xl bg-page2 p-4">
|
<view class="box-border w-[88vw] rounded-2xl bg-white p-4" @click.stop>
|
||||||
<view class="box-border w-full flex shrink-0 items-center justify-between gap-x-4">
|
<view class="box-border w-full flex shrink-0 items-center justify-between gap-x-4">
|
||||||
<view class="text-gray-900 font-bold">
|
<view class="text-gray-900 font-bold">
|
||||||
{{ props.groupName }}
|
{{ props.groupDisplayName }}
|
||||||
</view>
|
</view>
|
||||||
<view class="shrink-0">
|
<view class="shrink-0">
|
||||||
<wd-icon name="close-circle" size="24px" color="#1A1A1A" @click="emits('close')" />
|
<wd-icon name="close-circle" size="24px" color="#1A1A1A" @click="emits('close')" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="mt-4 w-full">
|
<view class="mt-2 w-full">
|
||||||
<scroll-view
|
<scroll-view
|
||||||
v-if="list" class="h-[56vh] w-full" scroll-y :refresher-enabled="true"
|
v-if="list" class="h-[60vh] w-full" scroll-y :refresher-enabled="true"
|
||||||
:refresher-triggered="loading" @scrolltolower="loadMore" @refresherrefresh="refresh"
|
:refresher-triggered="loading" @scrolltolower="loadMore" @refresherrefresh="refresh"
|
||||||
>
|
>
|
||||||
<view class="w-full flex flex-wrap gap-y-4">
|
<view class="grid grid-cols-2 box-border w-full gap-x-3 gap-y-2 overflow-hidden px-1">
|
||||||
<view
|
<view v-for="(item) in list" :key="item.metadata.name" class="box-border w-full">
|
||||||
v-for="(item) in list" :key="item.metadata.name"
|
|
||||||
class="box-border w-1/2 overflow-hidden px-2"
|
|
||||||
:class="[getImageItemClass()]"
|
|
||||||
>
|
|
||||||
<image
|
<image
|
||||||
class="uh-shadow-xs w-full border-2 border-white rounded-xl border-solid bg-white/80"
|
class="uh-shadow-xs box-border h-32 w-full border-2 border-white rounded-xl border-solid bg-white/80"
|
||||||
:src="completeUrl(item.spec.url)"
|
:src="completeUrl(item.spec.url)" mode="aspectFill" @click="handlePreview(item)"
|
||||||
mode="widthFix"
|
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
<view v-else class="h-[56vh] flex items-center justify-center">
|
<view v-else class="h-[60vh] flex items-center justify-center">
|
||||||
<wd-empty tip="暂无数据">
|
<wd-empty tip="暂无数据">
|
||||||
<template #image>
|
<template #image>
|
||||||
<wd-icon name="empty" color="#1A1A1A" :size="52" />
|
<wd-icon name="empty" color="#1A1A1A" :size="52" />
|
||||||
</template>
|
</template>
|
||||||
</wd-empty>
|
</wd-empty>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view class="mt-4 w-full flex items-center justify-center gap-x-2 text-xs text-gray-900">
|
||||||
v-if="loading || error || finished"
|
<view class="text-xs text-gray-900">
|
||||||
class="mt-4 w-full flex items-center justify-center text-xs text-gray-900"
|
共 {{ response?.total || 0 }} 张 已加载 {{ list.length }} 张
|
||||||
>
|
|
||||||
<wd-loading v-if="loading" text="加载中..." direction="horizontal" color="#1A1A1A" :size="16" />
|
|
||||||
<view
|
|
||||||
v-else-if="error"
|
|
||||||
class="w-full rounded-lg bg-gray-900 py-1.5 text-center text-xs text-white"
|
|
||||||
>
|
|
||||||
<wd-icon name="refresh" :size="14" /> 加载失败,点击刷新试试
|
|
||||||
</view>
|
</view>
|
||||||
<view v-else-if="finished" class="text-black/60">
|
<view v-if="loading || error || finished">
|
||||||
数据已全部加载,没有更多了
|
<wd-loading v-if="loading" text="加载中..." direction="horizontal" color="#1A1A1A" :size="16" />
|
||||||
|
<view v-else-if="error" class="w-full rounded-lg bg-gray-900 py-1.5 text-center text-xs text-white">
|
||||||
|
<wd-icon name="refresh" :size="14" /> 加载失败,点击刷新试试
|
||||||
|
</view>
|
||||||
|
<view v-else-if="finished" class="text-xs text-gray-900">
|
||||||
|
数据已全部加载
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ interface UseScrollOptions<T, P extends IHaloListRequestBase = { page: number, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface UseScrollReturn<T> {
|
interface UseScrollReturn<T> {
|
||||||
|
response: Ref<IHaloListResponseBase<T>>
|
||||||
list: Ref<T[]>
|
list: Ref<T[]>
|
||||||
loading: Ref<boolean>
|
loading: Ref<boolean>
|
||||||
finished: Ref<boolean>
|
finished: Ref<boolean>
|
||||||
@@ -25,6 +26,7 @@ export function useHaloScroll<T, P extends IHaloListRequestBase = { page: number
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const finished = ref(false)
|
const finished = ref(false)
|
||||||
const error = ref<any>(null)
|
const error = ref<any>(null)
|
||||||
|
const isLoadMore = ref(false)
|
||||||
|
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
if (loading.value || finished.value) {
|
if (loading.value || finished.value) {
|
||||||
@@ -38,17 +40,23 @@ export function useHaloScroll<T, P extends IHaloListRequestBase = { page: number
|
|||||||
response.value = await fetchData(params)
|
response.value = await fetchData(params)
|
||||||
finished.value = !response.value?.hasNext
|
finished.value = !response.value?.hasNext
|
||||||
list.value.push(...response.value?.items || [])
|
list.value.push(...response.value?.items || [])
|
||||||
params.page++
|
if (!finished.value) {
|
||||||
|
params.page += 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
error.value = err
|
error.value = err
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
isLoadMore.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
|
if (isLoadMore.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
params.page = 1
|
params.page = 1
|
||||||
finished.value = false
|
finished.value = false
|
||||||
list.value = []
|
list.value = []
|
||||||
@@ -56,6 +64,7 @@ export function useHaloScroll<T, P extends IHaloListRequestBase = { page: number
|
|||||||
}
|
}
|
||||||
|
|
||||||
const loadMore = async () => {
|
const loadMore = async () => {
|
||||||
|
isLoadMore.value = true
|
||||||
await loadData()
|
await loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +73,7 @@ export function useHaloScroll<T, P extends IHaloListRequestBase = { page: number
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
response,
|
||||||
list,
|
list,
|
||||||
loading,
|
loading,
|
||||||
finished,
|
finished,
|
||||||
|
|||||||
@@ -0,0 +1,348 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { getPicSumImages } from '@/utils/randomResources'
|
||||||
|
import { queryLinks } from '@/api/halo-plugin/link'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'Gallery',
|
||||||
|
})
|
||||||
|
|
||||||
|
definePage({
|
||||||
|
style: {
|
||||||
|
navigationBarTitleText: '相册',
|
||||||
|
navigationBarBackgroundColor: '#ffffff',
|
||||||
|
enablePullDownRefresh: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
interface GalleryItem {
|
||||||
|
metadataName: string
|
||||||
|
displayName: string
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GalleryGroup {
|
||||||
|
metadataName: string
|
||||||
|
displayName: string
|
||||||
|
images: GalleryItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupBanner = reactive({
|
||||||
|
current: 0,
|
||||||
|
})
|
||||||
|
const galleryStyle = ref('list')
|
||||||
|
const showListPanel = reactive({
|
||||||
|
show: false,
|
||||||
|
metadataName: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 相册分组预览
|
||||||
|
const galleryGroups = ref<GalleryGroup[]>([
|
||||||
|
{
|
||||||
|
metadataName: 'photo-group-HGaGs',
|
||||||
|
displayName: '我们的故事',
|
||||||
|
images: [{
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称1',
|
||||||
|
url: getPicSumImages(800, 600),
|
||||||
|
}, {
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称2',
|
||||||
|
url: getPicSumImages(800, 600),
|
||||||
|
}, {
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称3',
|
||||||
|
url: getPicSumImages(800, 600),
|
||||||
|
}, {
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称4',
|
||||||
|
url: getPicSumImages(800, 600),
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metadataName: '履行记录',
|
||||||
|
displayName: '履行记录',
|
||||||
|
images: [{
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称1',
|
||||||
|
url: getPicSumImages(800, 600),
|
||||||
|
}, {
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称2',
|
||||||
|
url: getPicSumImages(800, 600),
|
||||||
|
}, {
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称3',
|
||||||
|
url: getPicSumImages(800, 600),
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metadataName: '家庭时光',
|
||||||
|
displayName: '家庭时光',
|
||||||
|
images: [{
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称1',
|
||||||
|
url: getPicSumImages(800, 600),
|
||||||
|
}, {
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称2',
|
||||||
|
url: getPicSumImages(800, 600),
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metadataName: '其他',
|
||||||
|
displayName: '其他',
|
||||||
|
images: [{
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称1',
|
||||||
|
url: getPicSumImages(800, 600),
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// 相册图片
|
||||||
|
const galleryItems = ref<GalleryItem[]>([
|
||||||
|
{
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称1',
|
||||||
|
url: getPicSumImages(400, 400),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称2',
|
||||||
|
url: getPicSumImages(800, 600),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称3',
|
||||||
|
url: getPicSumImages(400, 800),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称4',
|
||||||
|
url: getPicSumImages(1920, 1080),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '图片名称5',
|
||||||
|
url: getPicSumImages(800, 1920),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const { loading, error, data, run } = useRequest<any, IPaginationParams>(queryLinks, { loadingToast: false, loadingToastContent: '加载中...', loadingToastMask: true })
|
||||||
|
|
||||||
|
const { list: links, refresh } = useListData<any>({
|
||||||
|
params: {
|
||||||
|
page: 1,
|
||||||
|
size: 0,
|
||||||
|
},
|
||||||
|
loading: toRef(loading),
|
||||||
|
onPullDownRefresh: async (params, updateData) => {
|
||||||
|
await run({ ...params })
|
||||||
|
updateData({
|
||||||
|
error: error.value,
|
||||||
|
hasNext: data.value?.hasNext,
|
||||||
|
items: data.value?.items ?? [],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onReachBottom: async (params, updateData) => {
|
||||||
|
await run({ ...params })
|
||||||
|
updateData({
|
||||||
|
error: error.value,
|
||||||
|
hasNext: data.value?.hasNext,
|
||||||
|
items: data.value?.items ?? [],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function onGroupBannerChange(e: any) {
|
||||||
|
groupBanner.current = e.detail.current
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGroupImageRotateClass(index: number) {
|
||||||
|
if (index === 0) {
|
||||||
|
return 'z-30 border-2 border-solid border-white uh-shadow-card group-hover:scale-95'
|
||||||
|
}
|
||||||
|
if (index === 1) {
|
||||||
|
return 'z-20 -rotate-6 group-hover:-rotate-22 border-2 border-solid border-white uh-shadow-card'
|
||||||
|
}
|
||||||
|
if (index === 2) {
|
||||||
|
return 'z-10 rotate-6 group-hover:rotate-22 border-2 border-solid border-white uh-shadow-card'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- 图片列表模式
|
||||||
|
// 图片列表容器样式
|
||||||
|
function getGalleryListContainerClass(total: number) {
|
||||||
|
if (total === 1) {
|
||||||
|
return 'h-36'
|
||||||
|
}
|
||||||
|
if (total === 2) {
|
||||||
|
return 'grid-cols-2 h-32'
|
||||||
|
}
|
||||||
|
if (total === 3) {
|
||||||
|
return 'grid-cols-2 grid-rows-2 h-36'
|
||||||
|
}
|
||||||
|
if (total === 4) {
|
||||||
|
return 'grid-cols-2 grid-rows-2 h-42'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图片项样式
|
||||||
|
function getGalleryListItemClass(index: number, total: number) {
|
||||||
|
if (total === 1) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
if (total === 2) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
if (total === 3) {
|
||||||
|
if (index === 0) {
|
||||||
|
return 'row-span-2'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
if (total === 4) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleShowListPanel(metadataName: string) {
|
||||||
|
showListPanel.metadataName = metadataName
|
||||||
|
showListPanel.show = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
refresh()
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
})
|
||||||
|
|
||||||
|
onPageScroll(() => { })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="min-h-screen w-full flex flex-col bg-page">
|
||||||
|
<!-- 分组列表:常态显示 -->
|
||||||
|
<view class="box-border w-full px-4 py-4">
|
||||||
|
<!-- 顶部区域固定区域 -->
|
||||||
|
<view class="relative w-full">
|
||||||
|
<swiper
|
||||||
|
autoplay circular class="h-46 w-full overflow-hidden rounded-xl" :current="groupBanner.current"
|
||||||
|
@change="onGroupBannerChange"
|
||||||
|
>
|
||||||
|
<swiper-item v-for="img in galleryGroups[0].images" :key="img.metadataName">
|
||||||
|
<image :src="img.url" class="h-full w-full object-cover" />
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
<view
|
||||||
|
class="absolute left-4 top-4 flex flex-col items-center justify-center rounded-2xl bg-orange-500 px-2 py-1.5 text-xs text-white font-bold"
|
||||||
|
>
|
||||||
|
最新精选
|
||||||
|
</view>
|
||||||
|
<view class="absolute bottom-4 left-4 flex flex-col gap-y-1">
|
||||||
|
<view class="text-sm text-white font-bold">
|
||||||
|
显示图片的名称
|
||||||
|
</view>
|
||||||
|
<view class="text-xs text-white/50">
|
||||||
|
显示描述/日期
|
||||||
|
</view>
|
||||||
|
<view class="w-full flex items-center justify-center gap-x-2">
|
||||||
|
<image
|
||||||
|
v-for="(item, index) in galleryGroups[0].images" :key="item.metadataName" :src="item.url"
|
||||||
|
mode="scaleToFill"
|
||||||
|
class="box-border h-8 w-8 border-2 border-white rounded-lg border-solid transition-all duration-300"
|
||||||
|
:class="[
|
||||||
|
{
|
||||||
|
'scale-110': groupBanner.current === index,
|
||||||
|
},
|
||||||
|
index % 2 === 0 ? '-rotate-4' : 'rotate-4',
|
||||||
|
]" @click="groupBanner.current = index"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 分组列表区域 -->
|
||||||
|
<view class="mt-6 w-full flex items-center justify-between">
|
||||||
|
<text class="text-sm text-gray-900 font-bold">我的相册</text>
|
||||||
|
<text class="text-[10px] text-gray-600">共 30 组记录</text>
|
||||||
|
</view>
|
||||||
|
<!-- 混合模式 -->
|
||||||
|
<view v-if="galleryStyle === 'mix'" class="grid grid-cols-2 mt-4 w-full gap-4">
|
||||||
|
<view
|
||||||
|
v-for="group in galleryGroups" :key="group.metadataName"
|
||||||
|
class="group relative box-border h-36 w-full flex items-center justify-center p-1"
|
||||||
|
>
|
||||||
|
<!-- 预览图区域 -->
|
||||||
|
<view class="relative h-full w-full">
|
||||||
|
<view
|
||||||
|
v-for="(img, index) in group.images.slice(0, 3)" :key="img.metadataName"
|
||||||
|
class="absolute left-0 top-0 box-border h-full w-full origin-center overflow-hidden rounded-2xl transition-all duration-300"
|
||||||
|
:class="[getGroupImageRotateClass(index)]"
|
||||||
|
>
|
||||||
|
<image :src="img.url" mode="scaleToFill" class="h-full w-full" />
|
||||||
|
<!-- 文字区域 -->
|
||||||
|
<view
|
||||||
|
v-if="index === 0"
|
||||||
|
class="absolute bottom-0 left-0 right-0 z-40 w-full flex flex-col gap-y-0.5 from-transparent to-black/90 bg-gradient-to-b px-2 py-1.5"
|
||||||
|
>
|
||||||
|
<text class="text-xs text-white font-bold">{{ group.displayName }}</text>
|
||||||
|
<text class="text-[8px] text-white/80">{{ group.images.length }} 张图片</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 列表卡片模式 -->
|
||||||
|
<view v-else-if="galleryStyle === 'list'" class="mt-2 w-full flex flex-col gap-y-4">
|
||||||
|
<view
|
||||||
|
v-for="group in galleryGroups" :key="group.metadataName"
|
||||||
|
class="box-border flex flex-col gap-y-2 overflow-hidden rounded-xl bg-white p-4 pt-3" @click="handleShowListPanel(group.metadataName)"
|
||||||
|
>
|
||||||
|
<!-- 顶部区域 -->
|
||||||
|
<view class="flex items-center justify-between">
|
||||||
|
<view class="flex items-center gap-x-2 text-sm font-bold">
|
||||||
|
<view class="hidden h-2 w-2 rounded-full bg-gray-900" /> {{ group.displayName }}
|
||||||
|
</view>
|
||||||
|
<view class="shrink-0">
|
||||||
|
<text class="text-[10px] text-gray-400">{{ group.images.length }} 张</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 图片列表 -->
|
||||||
|
<view
|
||||||
|
class="grid gap-1 overflow-hidden rounded-lg"
|
||||||
|
:class="[getGalleryListContainerClass(group.images.slice(0, 4).length)]"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
v-for="(img, index) in group.images.slice(0, 4)" :key="img.metadataName"
|
||||||
|
class="overflow-hidden"
|
||||||
|
:class="getGalleryListItemClass(index, group.images.slice(0, 4).length)"
|
||||||
|
>
|
||||||
|
<image
|
||||||
|
:src="img.url" mode="scaleToFill"
|
||||||
|
class="h-full w-full object-cover transition-transform duration-500 hover:scale-105"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 分组列表:无数据显示 -->
|
||||||
|
<view class="hidden">
|
||||||
|
无数据
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 相册列表:根据分组显示 -->
|
||||||
|
<uh-gallery-panel v-if="showListPanel.show" :group-name="showListPanel.metadataName" @close="showListPanel.show = false" />
|
||||||
|
|
||||||
|
<!-- 底部导航占位 -->
|
||||||
|
<view class="w-full shrink-0">
|
||||||
|
<uh-tabbar-page-placeholder />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
+176
-218
@@ -1,6 +1,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getPicSumImages } from '@/utils/randomResources'
|
import useRequest from '@/hooks/useRequest'
|
||||||
import { queryLinks } from '@/api/halo-plugin/link'
|
import { queryPhotoGroups, queryPhotos } from '@/api/halo-plugin/photo'
|
||||||
|
import type { IPhoto, IPhotoGroup } from '@/api/halo-plugin/photo'
|
||||||
|
import { completeUrl } from '@/utils/url'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'Gallery',
|
name: 'Gallery',
|
||||||
@@ -14,16 +16,13 @@ definePage({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
interface GalleryItem {
|
interface IGalleryGroup {
|
||||||
metadataName: string
|
metadataName: string
|
||||||
displayName: string
|
displayName: string
|
||||||
url: string
|
creationTimestamp: string
|
||||||
}
|
photoCount: number
|
||||||
|
priority: number
|
||||||
interface GalleryGroup {
|
photos: IPhoto[]
|
||||||
metadataName: string
|
|
||||||
displayName: string
|
|
||||||
images: GalleryItem[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupBanner = reactive({
|
const groupBanner = reactive({
|
||||||
@@ -33,126 +32,13 @@ const galleryStyle = ref('list')
|
|||||||
const showListPanel = reactive({
|
const showListPanel = reactive({
|
||||||
show: false,
|
show: false,
|
||||||
metadataName: '',
|
metadataName: '',
|
||||||
|
displayName: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
// 相册分组预览
|
// 相册分组预览
|
||||||
const galleryGroups = ref<GalleryGroup[]>([
|
const galleryGroups = ref<IGalleryGroup[]>([])
|
||||||
{
|
|
||||||
metadataName: 'photo-group-HGaGs',
|
|
||||||
displayName: '我们的故事',
|
|
||||||
images: [{
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称1',
|
|
||||||
url: getPicSumImages(800, 600),
|
|
||||||
}, {
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称2',
|
|
||||||
url: getPicSumImages(800, 600),
|
|
||||||
}, {
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称3',
|
|
||||||
url: getPicSumImages(800, 600),
|
|
||||||
}, {
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称4',
|
|
||||||
url: getPicSumImages(800, 600),
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadataName: '履行记录',
|
|
||||||
displayName: '履行记录',
|
|
||||||
images: [{
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称1',
|
|
||||||
url: getPicSumImages(800, 600),
|
|
||||||
}, {
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称2',
|
|
||||||
url: getPicSumImages(800, 600),
|
|
||||||
}, {
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称3',
|
|
||||||
url: getPicSumImages(800, 600),
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadataName: '家庭时光',
|
|
||||||
displayName: '家庭时光',
|
|
||||||
images: [{
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称1',
|
|
||||||
url: getPicSumImages(800, 600),
|
|
||||||
}, {
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称2',
|
|
||||||
url: getPicSumImages(800, 600),
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadataName: '其他',
|
|
||||||
displayName: '其他',
|
|
||||||
images: [{
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称1',
|
|
||||||
url: getPicSumImages(800, 600),
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// 相册图片
|
const { loading, error, data, run } = useRequest<Array<IPhotoGroup>>(queryPhotoGroups, { loadingToast: false, loadingToastContent: '加载中...', loadingToastMask: true })
|
||||||
const galleryItems = ref<GalleryItem[]>([
|
|
||||||
{
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称1',
|
|
||||||
url: getPicSumImages(400, 400),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称2',
|
|
||||||
url: getPicSumImages(800, 600),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称3',
|
|
||||||
url: getPicSumImages(400, 800),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称4',
|
|
||||||
url: getPicSumImages(1920, 1080),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
metadataName: '',
|
|
||||||
displayName: '图片名称5',
|
|
||||||
url: getPicSumImages(800, 1920),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
const { loading, error, data, run } = useRequest<any, IPaginationParams>(queryLinks, { loadingToast: false, loadingToastContent: '加载中...', loadingToastMask: true })
|
|
||||||
|
|
||||||
const { list: links, refresh } = useListData<any>({
|
|
||||||
params: {
|
|
||||||
page: 1,
|
|
||||||
size: 0,
|
|
||||||
},
|
|
||||||
loading: toRef(loading),
|
|
||||||
onPullDownRefresh: async (params, updateData) => {
|
|
||||||
await run({ ...params })
|
|
||||||
updateData({
|
|
||||||
error: error.value,
|
|
||||||
hasNext: data.value?.hasNext,
|
|
||||||
items: data.value?.items ?? [],
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onReachBottom: async (params, updateData) => {
|
|
||||||
await run({ ...params })
|
|
||||||
updateData({
|
|
||||||
error: error.value,
|
|
||||||
hasNext: data.value?.hasNext,
|
|
||||||
items: data.value?.items ?? [],
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
function onGroupBannerChange(e: any) {
|
function onGroupBannerChange(e: any) {
|
||||||
groupBanner.current = e.detail.current
|
groupBanner.current = e.detail.current
|
||||||
@@ -208,16 +94,84 @@ function getGalleryListItemClass(index: number, total: number) {
|
|||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleShowListPanel(metadataName: string) {
|
function handleShowListPanel(group: IGalleryGroup) {
|
||||||
showListPanel.metadataName = metadataName
|
showListPanel.metadataName = group.metadataName
|
||||||
|
showListPanel.displayName = group.displayName
|
||||||
showListPanel.show = true
|
showListPanel.show = true
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoad(() => {
|
// 查询分组的数据
|
||||||
refresh()
|
function getGalleryPhotosByGroups() {
|
||||||
|
if (galleryGroups.value.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 执行对每个分组查询图片
|
||||||
|
galleryGroups.value.forEach(async (group) => {
|
||||||
|
const queryParams = {
|
||||||
|
group: undefined,
|
||||||
|
fieldSelector: undefined,
|
||||||
|
page: 1,
|
||||||
|
size: 4,
|
||||||
|
}
|
||||||
|
if (group.metadataName.trim()) {
|
||||||
|
queryParams.group = group.metadataName
|
||||||
|
}
|
||||||
|
if (!group.metadataName.trim()) {
|
||||||
|
queryParams.fieldSelector = 'spec.groupName!=""'
|
||||||
|
}
|
||||||
|
const res = await queryPhotos(queryParams)
|
||||||
|
if (res?.items) {
|
||||||
|
group.photos = res.items
|
||||||
|
}
|
||||||
|
if (!group.metadataName.trim()) {
|
||||||
|
group.photoCount = res.total
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => loading.value, () => {
|
||||||
|
if (loading.value || error.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
galleryGroups.value = [
|
||||||
|
{
|
||||||
|
metadataName: '',
|
||||||
|
displayName: '未分组',
|
||||||
|
creationTimestamp: '',
|
||||||
|
photoCount: 0,
|
||||||
|
priority: 0,
|
||||||
|
photos: [],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
data.value.forEach((item) => {
|
||||||
|
galleryGroups.value.push({
|
||||||
|
metadataName: item.metadata.name,
|
||||||
|
displayName: item.spec.displayName,
|
||||||
|
creationTimestamp: item.metadata.creationTimestamp,
|
||||||
|
photoCount: item.status.photoCount,
|
||||||
|
priority: item.spec.priority,
|
||||||
|
photos: [],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
getGalleryPhotosByGroups()
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
async function init() {
|
||||||
|
await run()
|
||||||
|
uni.stopPullDownRefresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
onPullDownRefresh(async () => {
|
||||||
|
await init()
|
||||||
|
})
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await init()
|
||||||
})
|
})
|
||||||
|
|
||||||
onPageScroll(() => { })
|
onPageScroll(() => { })
|
||||||
@@ -226,119 +180,123 @@ onPageScroll(() => { })
|
|||||||
<template>
|
<template>
|
||||||
<view class="min-h-screen w-full flex flex-col bg-page">
|
<view class="min-h-screen w-full flex flex-col bg-page">
|
||||||
<!-- 分组列表:常态显示 -->
|
<!-- 分组列表:常态显示 -->
|
||||||
<view class="box-border w-full px-4 py-4">
|
<view v-if="galleryGroups.length !== 0" class="box-border w-full">
|
||||||
<!-- 顶部区域固定区域 -->
|
<!-- 顶部区域固定区域 -->
|
||||||
<view class="relative w-full">
|
<view v-if="galleryGroups.length > 1 && galleryGroups[0].photos.length >= 4" class="relative w-full">
|
||||||
<swiper
|
<swiper
|
||||||
autoplay circular class="h-46 w-full overflow-hidden rounded-xl" :current="groupBanner.current"
|
autoplay circular class="h-56 w-full overflow-hidden" :current="groupBanner.current"
|
||||||
@change="onGroupBannerChange"
|
@change="onGroupBannerChange"
|
||||||
>
|
>
|
||||||
<swiper-item v-for="img in galleryGroups[0].images" :key="img.metadataName">
|
<swiper-item v-for="photo in galleryGroups[0].photos.slice(0, 5)" :key="photo.metadata.name">
|
||||||
<image :src="img.url" class="h-full w-full object-cover" />
|
<image :src="completeUrl(photo.spec.url)" class="h-full w-full object-cover" />
|
||||||
</swiper-item>
|
</swiper-item>
|
||||||
</swiper>
|
</swiper>
|
||||||
|
<!-- 渐变底部遮罩 -->
|
||||||
<view
|
<view
|
||||||
class="absolute left-4 top-4 flex flex-col items-center justify-center rounded-2xl bg-orange-500 px-2 py-1.5 text-xs text-white font-bold"
|
class="absolute bottom-0 left-0 right-0 z-10 h-2/5 from-transparent via-page/30 to-page bg-gradient-to-b"
|
||||||
>
|
/>
|
||||||
最新精选
|
</view>
|
||||||
</view>
|
<view v-if="galleryGroups.length > 1 && galleryGroups[0].photos.length >= 4" class="relative z-10 box-border w-full flex items-center justify-center gap-x-2 overflow-hidden p-4 -mt-12">
|
||||||
<view class="absolute bottom-4 left-4 flex flex-col gap-y-1">
|
<view class="uh-shadow-card box-border w-full flex items-center justify-center gap-x-4 rounded-xl bg-white p-4">
|
||||||
<view class="text-sm text-white font-bold">
|
<image
|
||||||
显示图片的名称
|
v-for="(photo, index) in galleryGroups[0].photos.slice(0, 5)" :key="photo.metadata.name"
|
||||||
</view>
|
:src="completeUrl(photo.spec.url)" mode="aspectFill"
|
||||||
<view class="text-xs text-white/50">
|
class="box-border h-12 w-12 border-2 border-white rounded-lg border-solid transition-all duration-300"
|
||||||
显示描述/日期
|
:class="[
|
||||||
</view>
|
{
|
||||||
<view class="w-full flex items-center justify-center gap-x-2">
|
'shadow-md rotate-4': groupBanner.current === index,
|
||||||
<image
|
},
|
||||||
v-for="(item, index) in galleryGroups[0].images" :key="item.metadataName" :src="item.url"
|
]" @click="groupBanner.current = index"
|
||||||
mode="scaleToFill"
|
/>
|
||||||
class="box-border h-8 w-8 border-2 border-white rounded-lg border-solid transition-all duration-300"
|
|
||||||
:class="[
|
|
||||||
{
|
|
||||||
'scale-110': groupBanner.current === index,
|
|
||||||
},
|
|
||||||
index % 2 === 0 ? '-rotate-4' : 'rotate-4',
|
|
||||||
]" @click="groupBanner.current = index"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="mt-4 box-border px-4">
|
||||||
<!-- 分组列表区域 -->
|
<!-- 分组列表区域 -->
|
||||||
<view class="mt-6 w-full flex items-center justify-between">
|
<view v-if="false" class="w-full flex items-center justify-between">
|
||||||
<text class="text-sm text-gray-900 font-bold">我的相册</text>
|
<text class="text-sm text-gray-900 font-bold">我的相册</text>
|
||||||
<text class="text-[10px] text-gray-600">共 30 组记录</text>
|
<text class="text-[10px] text-gray-600">共 30 组记录</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 混合模式 -->
|
<!-- 混合模式 -->
|
||||||
<view v-if="galleryStyle === 'mix'" class="grid grid-cols-2 mt-4 w-full gap-4">
|
<view v-if="galleryStyle === 'mix'" class="grid grid-cols-2 mt-4 w-full gap-4">
|
||||||
<view
|
<view
|
||||||
v-for="group in galleryGroups" :key="group.metadataName"
|
v-for="group in galleryGroups" :key="group.metadataName"
|
||||||
class="group relative box-border h-36 w-full flex items-center justify-center p-1"
|
class="group relative box-border h-36 w-full flex items-center justify-center p-1"
|
||||||
>
|
>
|
||||||
<!-- 预览图区域 -->
|
<!-- 预览图区域 -->
|
||||||
<view class="relative h-full w-full">
|
<view class="relative h-full w-full">
|
||||||
<view
|
|
||||||
v-for="(img, index) in group.images.slice(0, 3)" :key="img.metadataName"
|
|
||||||
class="absolute left-0 top-0 box-border h-full w-full origin-center overflow-hidden rounded-2xl transition-all duration-300"
|
|
||||||
:class="[getGroupImageRotateClass(index)]"
|
|
||||||
>
|
|
||||||
<image :src="img.url" mode="scaleToFill" class="h-full w-full" />
|
|
||||||
<!-- 文字区域 -->
|
|
||||||
<view
|
<view
|
||||||
v-if="index === 0"
|
v-for="(photo, index) in group.photos.slice(0, 3)" :key="photo.metadata.name"
|
||||||
class="absolute bottom-0 left-0 right-0 z-40 w-full flex flex-col gap-y-0.5 from-transparent to-black/90 bg-gradient-to-b px-2 py-1.5"
|
class="absolute left-0 top-0 box-border h-full w-full origin-center overflow-hidden rounded-2xl transition-all duration-300"
|
||||||
|
:class="[getGroupImageRotateClass(index)]"
|
||||||
>
|
>
|
||||||
<text class="text-xs text-white font-bold">{{ group.displayName }}</text>
|
<image :src="photo.spec.url" mode="scaleToFill" class="h-full w-full" />
|
||||||
<text class="text-[8px] text-white/80">{{ group.images.length }} 张图片</text>
|
<!-- 文字区域 -->
|
||||||
|
<view
|
||||||
|
v-if="index === 0"
|
||||||
|
class="absolute bottom-0 left-0 right-0 z-40 w-full flex flex-col gap-y-0.5 from-transparent to-black/90 bg-gradient-to-b px-2 py-1.5"
|
||||||
|
>
|
||||||
|
<text class="text-xs text-white font-bold">{{ group.displayName }}</text>
|
||||||
|
<text class="text-[8px] text-white/80">{{ group.photoCount }} 张图片</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 列表卡片模式 -->
|
<!-- 列表卡片模式 -->
|
||||||
<view v-else-if="galleryStyle === 'list'" class="mt-2 w-full flex flex-col gap-y-4">
|
<view v-else-if="galleryStyle === 'list'" class="mt-2 w-full flex flex-col gap-y-4">
|
||||||
<view
|
<template v-for="group in galleryGroups" :key="group.metadataName">
|
||||||
v-for="group in galleryGroups" :key="group.metadataName"
|
|
||||||
class="box-border flex flex-col gap-y-2 overflow-hidden rounded-xl bg-white p-4 pt-3" @click="handleShowListPanel(group.metadataName)"
|
|
||||||
>
|
|
||||||
<!-- 顶部区域 -->
|
|
||||||
<view class="flex items-center justify-between">
|
|
||||||
<view class="flex items-center gap-x-2 text-sm font-bold">
|
|
||||||
<view class="hidden h-2 w-2 rounded-full bg-gray-900" /> {{ group.displayName }}
|
|
||||||
</view>
|
|
||||||
<view class="shrink-0">
|
|
||||||
<text class="text-[10px] text-gray-400">{{ group.images.length }} 张</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 图片列表 -->
|
|
||||||
<view
|
|
||||||
class="grid gap-1 overflow-hidden rounded-lg"
|
|
||||||
:class="[getGalleryListContainerClass(group.images.slice(0, 4).length)]"
|
|
||||||
>
|
|
||||||
<view
|
<view
|
||||||
v-for="(img, index) in group.images.slice(0, 4)" :key="img.metadataName"
|
v-if="group.photoCount > 0"
|
||||||
class="overflow-hidden"
|
class="box-border flex flex-col gap-y-2 overflow-hidden rounded-xl bg-white p-4 pt-3"
|
||||||
:class="getGalleryListItemClass(index, group.images.slice(0, 4).length)"
|
@click="handleShowListPanel(group)"
|
||||||
>
|
>
|
||||||
<image
|
<!-- 顶部区域 -->
|
||||||
:src="img.url" mode="scaleToFill"
|
<view class="flex items-center justify-between">
|
||||||
class="h-full w-full object-cover transition-transform duration-500 hover:scale-105"
|
<view class="flex items-center gap-x-2 text-sm font-bold">
|
||||||
/>
|
<view class="hidden h-2 w-2 rounded-full bg-gray-900" /> {{ group.displayName }}
|
||||||
|
</view>
|
||||||
|
<view class="shrink-0">
|
||||||
|
<text class="text-[10px] text-gray-400">{{ group.photoCount }} 张</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 图片列表 -->
|
||||||
|
<view
|
||||||
|
class="grid gap-1 overflow-hidden rounded-lg"
|
||||||
|
:class="[getGalleryListContainerClass(group.photos.slice(0, 4).length)]"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
v-for="(photo, index) in group.photos.slice(0, 4)" :key="photo.metadata.name"
|
||||||
|
class="overflow-hidden" :class="getGalleryListItemClass(index, group.photos.slice(0, 4).length)"
|
||||||
|
>
|
||||||
|
<image
|
||||||
|
:src="completeUrl(photo.spec.url)" mode="aspectFill"
|
||||||
|
class="h-full w-full object-cover transition-transform duration-500 hover:scale-105"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 分组列表:无数据显示 -->
|
<!-- 分组列表:无数据显示 -->
|
||||||
<view class="hidden">
|
<view v-else class="w-full flex flex-1 items-center justify-center">
|
||||||
无数据
|
<wd-empty icon="no-content" tip="暂无数据">
|
||||||
|
<template #image>
|
||||||
|
<wd-icon name="empty" color="#1A1A1A" :size="52" />
|
||||||
|
</template>
|
||||||
|
</wd-empty>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 相册列表:根据分组显示 -->
|
<!-- 相册列表:根据分组显示 -->
|
||||||
<uh-gallery-panel v-if="showListPanel.show" :group-name="showListPanel.metadataName" @close="showListPanel.show = false" />
|
<uh-gallery-panel
|
||||||
|
v-if="showListPanel.show"
|
||||||
|
:group-metadata-name="showListPanel.metadataName"
|
||||||
|
:group-display-name="showListPanel.displayName"
|
||||||
|
@close="showListPanel.show = false"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- 底部导航占位 -->
|
<!-- 底部导航占位 -->
|
||||||
<view class="w-full shrink-0">
|
<view class="w-full shrink-0">
|
||||||
|
|||||||
@@ -12,3 +12,21 @@ export function cover(cover: string) {
|
|||||||
}
|
}
|
||||||
return getRandomUrl()
|
return getRandomUrl()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片预览
|
||||||
|
* @param url 图片URL
|
||||||
|
* @param urls 图片URL列表
|
||||||
|
*/
|
||||||
|
export function preview(url: string, urls: string[]) {
|
||||||
|
if (urls.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uni.previewImage({
|
||||||
|
current: url,
|
||||||
|
urls,
|
||||||
|
showmenu: true,
|
||||||
|
indicator: 'number',
|
||||||
|
loop: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user