mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-07-27 12:30:41 +08:00
refactor: 项目结构与代码优化调整
1. 重构tabbar路由与多语言key,调整首页默认跳转 2. 拆分博客页面到subpkg目录,调整vite分包配置 3. 新增markdown、图片组件、滚动钩子等工具模块 4. 优化api类型定义与工具函数,修复图片缓存逻辑 5. 调整国际化文案与个人页面路径
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { getPicSumImages } from '@/utils/randomResources'
|
||||
import { queryLinks } from '@/api/halo-plugin/link'
|
||||
|
||||
defineOptions({
|
||||
name: 'Gallery',
|
||||
})
|
||||
@@ -6,12 +9,340 @@ defineOptions({
|
||||
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 items-center justify-center gap-y-6 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 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>
|
||||
|
||||
@@ -14,8 +14,8 @@ definePage({
|
||||
onLoad(() => {
|
||||
console.log('index onLoad')
|
||||
// 默认跳转首页
|
||||
uni.switchTab({ url: '/pages/home/home' })
|
||||
// uni.switchTab({ url: '/pages/moments/moments' })
|
||||
// uni.switchTab({ url: '/pages/home/home' })
|
||||
uni.switchTab({ url: '/pages/gallery/gallery' })
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { LOGIN_PAGE } from '@/router/config'
|
||||
import { useUserStore } from '@/store'
|
||||
import { useTokenStore } from '@/store/token'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '我的',
|
||||
},
|
||||
})
|
||||
|
||||
const userStore = useUserStore()
|
||||
const tokenStore = useTokenStore()
|
||||
// 使用storeToRefs解构userInfo
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
|
||||
// 微信小程序下登录
|
||||
async function handleLogin() {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信登录
|
||||
await tokenStore.wxLogin()
|
||||
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.navigateTo({
|
||||
url: `${LOGIN_PAGE}`,
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
function handleLogout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要退出登录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 清空用户信息
|
||||
useTokenStore().logout()
|
||||
// 执行退出登录逻辑
|
||||
uni.showToast({
|
||||
title: '退出登录成功',
|
||||
icon: 'success',
|
||||
})
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序,去首页
|
||||
// uni.reLaunch({ url: '/pages/index/index' })
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
// 非微信小程序,去登录页
|
||||
// uni.navigateTo({ url: LOGIN_PAGE })
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="min-h-screen w-full flex flex-col items-center justify-center gap-y-6 bg-page">
|
||||
<view class="mt-3 break-all px-3 text-center text-green-500">
|
||||
{{ userInfo.username ? '已登录' : '未登录' }}
|
||||
</view>
|
||||
<view class="mt-3 break-all px-3">
|
||||
{{ JSON.stringify(userInfo, null, 2) }}
|
||||
</view>
|
||||
|
||||
<view class="mt-[60vh] px-3">
|
||||
<view class="m-auto w-160px text-center">
|
||||
<button v-if="tokenStore.hasLogin" type="warn" class="w-full" @click="handleLogout">
|
||||
退出登录
|
||||
</button>
|
||||
<button v-else type="primary" class="w-full" @click="handleLogin">
|
||||
登录
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -0,0 +1,141 @@
|
||||
<script lang="ts" setup>
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { LOGIN_PAGE } from '@/router/config'
|
||||
import { useUserStore } from '@/store'
|
||||
import { useTokenStore } from '@/store/token'
|
||||
import i18n, { t } from '@/locale/index'
|
||||
import { setTabbarItem } from '@/tabbar/i18n'
|
||||
|
||||
defineOptions({
|
||||
name: 'Mine',
|
||||
})
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '我的',
|
||||
navigationBarBackgroundColor: '#ffffff',
|
||||
enablePullDownRefresh: true,
|
||||
},
|
||||
})
|
||||
|
||||
const userStore = useUserStore()
|
||||
const tokenStore = useTokenStore()
|
||||
// 使用storeToRefs解构userInfo
|
||||
const { userInfo } = storeToRefs(userStore)
|
||||
|
||||
// 微信小程序下登录
|
||||
async function handleLogin() {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信登录
|
||||
await tokenStore.wxLogin()
|
||||
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.navigateTo({
|
||||
url: `${LOGIN_PAGE}`,
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
function handleLogout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要退出登录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 清空用户信息
|
||||
useTokenStore().logout()
|
||||
// 执行退出登录逻辑
|
||||
uni.showToast({
|
||||
title: '退出登录成功',
|
||||
icon: 'success',
|
||||
})
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序,去首页
|
||||
// uni.reLaunch({ url: '/pages/index/index' })
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
// 非微信小程序,去登录页
|
||||
// uni.navigateTo({ url: LOGIN_PAGE })
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const current = ref(uni.getLocale())
|
||||
const languages = [
|
||||
{
|
||||
value: 'zh-Hans',
|
||||
name: '中文',
|
||||
checked: 'true',
|
||||
},
|
||||
{
|
||||
value: 'en',
|
||||
name: '英文',
|
||||
},
|
||||
]
|
||||
|
||||
function radioChange(evt) {
|
||||
// console.log(evt)
|
||||
current.value = evt.detail.value
|
||||
// 下面2句缺一不可!!!
|
||||
uni.setLocale(evt.detail.value)
|
||||
i18n.global.locale = evt.detail.value
|
||||
|
||||
// 底部tabbar需要重新设置一下
|
||||
setTabbarItem()
|
||||
// 本页的标题也需要重新设置一下
|
||||
uni.setNavigationBarTitle({
|
||||
title: t('i18n.title'),
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="box-border min-h-screen w-full flex flex-col items-center justify-center gap-y-6 bg-page2 px-4">
|
||||
<view class="mt-3 break-all px-3 text-center text-green-500">
|
||||
{{ userInfo.username ? '已登录' : '未登录' }}
|
||||
</view>
|
||||
<view class="mt-3 break-all px-3">
|
||||
{{ JSON.stringify(userInfo, null, 2) }}
|
||||
</view>
|
||||
|
||||
<!-- 切换语言 -->
|
||||
<view class="mt-6 w-full flex flex-col items-center justify-center">
|
||||
<view class="mb-2 text-gray-900 font-bold">
|
||||
切换语言
|
||||
</view>
|
||||
<view class="w-full flex items-center justify-center gap-4">
|
||||
<radio-group class="flex flex-col items-center justify-center gap-2" @change="radioChange">
|
||||
<label
|
||||
v-for="item in languages"
|
||||
:key="item.value"
|
||||
class="flex items-center gap-x-2"
|
||||
>
|
||||
<view>
|
||||
<radio :value="item.value" :checked="item.value === current" />
|
||||
</view>
|
||||
<view>{{ item.name }}</view>
|
||||
</label>
|
||||
</radio-group>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="mt-6">
|
||||
<view class="m-auto w-160px text-center">
|
||||
<button v-if="tokenStore.hasLogin" type="warn" class="w-full rounded-xl text-xs" @click="handleLogout">
|
||||
退出登录
|
||||
</button>
|
||||
<button v-else class="w-full rounded-xl bg-gray-900 py-2.5 text-xs text-white" @click="handleLogin">
|
||||
立即登录
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部导航占位 -->
|
||||
<view class="w-full shrink-0">
|
||||
<uh-tabbar-page-placeholder />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
Reference in New Issue
Block a user