mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-09-12 16:40:40 +08:00
chore: 批量新增项目依赖、工具函数、页面与组件资源
1. 新增mp-html、qs等生产依赖,补全项目基础库 2. 新增平台判断、缓存、工具函数等通用工具集 3. 新增标签页、网站浏览页、关于页等业务页面 4. 新增分类卡片、通知弹窗等业务组件 5. 新增uts-progressNotification、liu-poster、uhalo-upgrade等uni模块 6. 补充audio/video组件样式补件,修复uni-components路径缺失问题 7. 新增环境变量Halo个人令牌配置项 8. 重构store导出结构,新增appConfig/halo/setting三个状态模块 9. 新增tsconfig编译目标配置,适配更高版本ES语法
This commit is contained in:
@@ -1,13 +1,277 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 图库页(源自旧项目 pages/tabbar/gallery/gallery.vue,新建复刻)
|
||||
* 功能:相册分组切换 + 图片列表(瀑布流/网格) + 图片预览
|
||||
*/
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
|
||||
import { getPhotoGroupList, getPhotoListByGroupName } from '@/api/halo'
|
||||
import { useAppConfigStore } from '@/store/appConfig'
|
||||
import { checkImageUrl } from '@/utils/url'
|
||||
import { t } from '@/locale'
|
||||
import { usePluginAvailable } from '@/utils/plugin'
|
||||
import type { IPhoto } from '@/api/types/halo'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '图库',
|
||||
enablePullDownRefresh: true,
|
||||
},
|
||||
})
|
||||
|
||||
const appConfigStore = useAppConfigStore()
|
||||
const haloConfigs = computed(() => appConfigStore.configs)
|
||||
const mockJson = computed(() => appConfigStore.mockJson)
|
||||
const calcAuditModeEnabled = computed(() => !!haloConfigs.value.auditConfig?.auditModeEnabled)
|
||||
|
||||
const galleryConfig = computed(() => haloConfigs.value.pageConfig?.galleryConfig)
|
||||
|
||||
/** 依赖插件(plugin-photos) */
|
||||
const uniHaloPluginId = 'plugin-photos'
|
||||
const uniHaloPluginAvailable = ref(true)
|
||||
|
||||
/* ---------------- 状态 ---------------- */
|
||||
const loading = ref<'loading' | 'success' | 'error'>('loading')
|
||||
const category = ref<{ activeIndex: number, list: { name?: string, displayName: string, priority: number }[] }>({
|
||||
activeIndex: 0,
|
||||
list: [],
|
||||
})
|
||||
const queryParams = ref({ size: 10, page: 1, group: '' })
|
||||
const isLoadMore = ref(false)
|
||||
const loadMoreText = ref('')
|
||||
const hasNext = ref(false)
|
||||
const dataList = ref<IPhoto[]>([])
|
||||
const lock = ref(false)
|
||||
|
||||
/* ---------------- 数据加载 ---------------- */
|
||||
async function handleGetCategory() {
|
||||
if (calcAuditModeEnabled.value) {
|
||||
handleGetData(true)
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await getPhotoGroupList({ page: 1, size: 0 })
|
||||
category.value.list = (res.data.items || [])
|
||||
.map(item => ({
|
||||
name: item.metadata.name,
|
||||
displayName: item.spec.displayName,
|
||||
priority: item.spec.priority ?? 0,
|
||||
}))
|
||||
.sort((a, b) => a.priority - b.priority)
|
||||
category.value.list.unshift({ name: undefined, displayName: '全部', priority: 0 })
|
||||
if (category.value.list.length !== 0) {
|
||||
queryParams.value.group = category.value.list[0].name || ''
|
||||
handleGetData(true)
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e)
|
||||
loading.value = 'error'
|
||||
category.value = { activeIndex: 0, list: [] }
|
||||
}
|
||||
}
|
||||
|
||||
async function handleGetData(isClearList = false) {
|
||||
if (calcAuditModeEnabled.value) {
|
||||
const galleryMock = mockJson.value.gallery as { list?: string[] } | undefined
|
||||
dataList.value = (galleryMock?.list || []).map(item => ({
|
||||
metadata: { name: String(Date.now() * Math.random()) },
|
||||
spec: {
|
||||
displayName: '',
|
||||
url: checkImageUrl(item),
|
||||
},
|
||||
}))
|
||||
loading.value = 'success'
|
||||
loadMoreText.value = t('common.noMore')
|
||||
uni.hideLoading()
|
||||
uni.stopPullDownRefresh()
|
||||
lock.value = false
|
||||
return
|
||||
}
|
||||
|
||||
if (!isLoadMore.value) {
|
||||
loading.value = 'loading'
|
||||
}
|
||||
loadMoreText.value = ''
|
||||
|
||||
try {
|
||||
const res = await getPhotoListByGroupName({ ...queryParams.value })
|
||||
hasNext.value = res.data.hasNext
|
||||
loading.value = 'success'
|
||||
if (res.data.items.length !== 0) {
|
||||
const list = res.data.items.map(item => ({
|
||||
...item,
|
||||
spec: { ...item.spec, url: checkImageUrl(item.spec.url || item.spec.cover) },
|
||||
}))
|
||||
dataList.value = isLoadMore.value
|
||||
? dataList.value.concat(list)
|
||||
: list
|
||||
}
|
||||
loadMoreText.value = res.data.hasNext ? t('common.loadMore') : t('common.noMore')
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err)
|
||||
loading.value = 'error'
|
||||
loadMoreText.value = t('common.loadFailed')
|
||||
}
|
||||
finally {
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.stopPullDownRefresh()
|
||||
lock.value = false
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
function handleGetDataByCategory(index: number) {
|
||||
const item = category.value.list[index]
|
||||
if (!item)
|
||||
return
|
||||
queryParams.value.group = item.name || ''
|
||||
queryParams.value.page = 1
|
||||
uni.pageScrollTo({ scrollTop: 0, duration: 500 })
|
||||
dataList.value = []
|
||||
handleGetData(true)
|
||||
}
|
||||
|
||||
function handleOnCategoryChange(e: { detail: { current: number } }) {
|
||||
if (lock.value)
|
||||
return
|
||||
handleGetDataByCategory(e.detail.current)
|
||||
}
|
||||
|
||||
/* ---------------- 图片预览 ---------------- */
|
||||
function handlePreview(data: IPhoto) {
|
||||
const current = dataList.value.findIndex(x => x.metadata.name === data.metadata.name)
|
||||
uni.previewImage({
|
||||
current,
|
||||
urls: dataList.value.map(x => x.spec.url),
|
||||
indicator: 'number',
|
||||
loop: true,
|
||||
})
|
||||
}
|
||||
|
||||
/* ---------------- 生命周期 ---------------- */
|
||||
onLoad(async () => {
|
||||
// 检查插件可用性
|
||||
uniHaloPluginAvailable.value = await usePluginAvailable(uniHaloPluginId)
|
||||
if (!uniHaloPluginAvailable.value) {
|
||||
uni.stopPullDownRefresh()
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
watch(galleryConfig, (newVal) => {
|
||||
if (!newVal)
|
||||
return
|
||||
uni.setNavigationBarTitle({ title: newVal.pageTitle || t('page.gallery.title') })
|
||||
handleGetCategory()
|
||||
}, { deep: true, immediate: true })
|
||||
|
||||
onPullDownRefresh(() => {
|
||||
if (!uniHaloPluginAvailable.value) {
|
||||
uni.stopPullDownRefresh()
|
||||
return
|
||||
}
|
||||
dataList.value = []
|
||||
isLoadMore.value = false
|
||||
queryParams.value.page = 1
|
||||
handleGetData(true)
|
||||
})
|
||||
|
||||
onReachBottom(() => {
|
||||
if (!uniHaloPluginAvailable.value)
|
||||
return
|
||||
if (calcAuditModeEnabled.value) {
|
||||
uni.showToast({ icon: 'none', title: t('common.noMoreData') })
|
||||
return
|
||||
}
|
||||
if (hasNext.value) {
|
||||
queryParams.value.page += 1
|
||||
isLoadMore.value = true
|
||||
handleGetData(false)
|
||||
}
|
||||
else {
|
||||
uni.showToast({ icon: 'none', title: t('common.noMoreData') })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="mt-10 text-center text-green-500">
|
||||
相册图库
|
||||
<view class="app-page min-h-screen w-screen flex flex-col pb-6" style="background-color: #fafafa;">
|
||||
<uh-plugin-unavailable
|
||||
v-if="!uniHaloPluginAvailable"
|
||||
:plugin-id="uniHaloPluginId"
|
||||
error-text="检测到当前插件没有安装或者启用,无法使用图库功能哦,请联系管理员"
|
||||
@on-refresh="handleGetCategory"
|
||||
/>
|
||||
<template v-else>
|
||||
<!-- 顶部切换 -->
|
||||
<view v-if="category.list.length > 0" class="category-tabs fixed inset-x-0 top-0 z-6 bg-white">
|
||||
<wd-tabs
|
||||
v-model="category.activeIndex"
|
||||
:tabs="category.list.map(item => ({ title: item.displayName }))"
|
||||
align="left"
|
||||
@change="handleOnCategoryChange"
|
||||
/>
|
||||
</view>
|
||||
<!-- 占位区域 -->
|
||||
<view v-if="category.list.length > 0" class="h-[90rpx] w-screen" />
|
||||
|
||||
<!-- 骨架屏 -->
|
||||
<view v-if="loading === 'loading'" class="loading-wrap box-border p-3">
|
||||
<wd-skeleton :row="4" :animated="true" />
|
||||
</view>
|
||||
|
||||
<!-- 错误态 -->
|
||||
<view v-else-if="loading === 'error'" class="error-wrap h-[60vh] w-full flex flex-col items-center justify-center gap-6">
|
||||
<wd-empty description="阿偶,获取数据失败了~" />
|
||||
<wd-button size="small" plain type="primary" @click="handleGetCategory()">
|
||||
刷新试试
|
||||
</wd-button>
|
||||
</view>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<view v-else class="content box-border w-full p-3">
|
||||
<view v-if="dataList.length === 0" class="h-[70vh] w-full flex items-center justify-center content-empty">
|
||||
<wd-empty description="博主还没有分享图片~" />
|
||||
</view>
|
||||
<block v-else>
|
||||
<!-- 瀑布流(双列) -->
|
||||
<view class="waterfall flex flex-wrap gap-1.5">
|
||||
<view
|
||||
v-for="(item, index) in dataList"
|
||||
:key="index"
|
||||
class="waterfall-item h-[250rpx] w-[calc(50%-6rpx)] overflow-hidden rounded-xl"
|
||||
:class="{ 'is-even mt-3': index % 2 === 1 }"
|
||||
>
|
||||
<image
|
||||
class="waterfall-img h-full w-full"
|
||||
:src="item.spec.url"
|
||||
mode="aspectFill"
|
||||
lazy-load
|
||||
@click="handlePreview(item)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="load-text w-full py-5 text-center text-[24rpx] text-[#999]">
|
||||
{{ loadMoreText }}
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.app-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.error-wrap {
|
||||
.error-wrap-inner {
|
||||
/* 无额外样式 */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user