1
0
mirror of https://github.com/ialley-workshop-open/uni-halo.git synced 2026-07-27 04:20:43 +08:00
Files
uni-halo/src/pages/index/index.vue
T
小莫唐尼 f105dedc23 refactor: 重构项目并新增多项功能
1. 新增全局配置管理store,拉取uni-halo插件配置
2. 新增post-card文章卡片组件,简化首页文章列表代码
3. 优化tabbar样式与切换逻辑,调整图标库类型
4. 新增随机图片工具函数,重构首页横幅与分类模块
5. 调整openapi生成配置,新增工具类与样式类
6. 移除废弃的tabbar关于页面配置项
2026-06-12 14:32:21 +08:00

315 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts" setup>
import useRequest from '@/hooks/useRequest'
import { queryPostByName, queryPosts } from '@/api/halo-base/post'
import { completeUrl } from '@/utils/url'
import { timeFrom } from '@/utils/base/timeFrom'
import { useListData } from '@/hooks/useListData'
import { getRandomUrl, randomImageUrl } from '@/utils/randomResources'
import type { ListedPostVo, ListedPostVoList, PostV1alpha1PublicApiQueryPostByNameRequest, PostV1alpha1PublicApiQueryPostsRequest, PostVo } from '@halo-dev/api-client'
import PostCard from '@/components/post-card/post-card.vue'
defineOptions({
name: 'Home',
})
definePage({
type: 'home',
style: {
navigationStyle: 'custom',
navigationBarTitleText: '首页',
enablePullDownRefresh: true,
},
})
const exampleImageUrls = reactive({
banner: 'https://fastly.picsum.photos/id/353/800/800.jpg?hmac=RaDuQ92sSXj4q5vOYna9G00J7KrBUC3eBS0slCWYZXA',
avatar: 'https://www.xiaoxiaomo.cn/logo.jpg',
postCover1: 'https://lh3.googleusercontent.com/aida-public/AB6AXuDg-QKAXOU1_Lv726MyeTiY2R1MAJWKzjiUy__RYoUw78YL5PctVg2sfMUCRMBybAzw_E0V0fEhir4IW2RHF7YRcxm89JFDKVMlfU_yQTijnr4o-praEXX_buxFWWxsSayLdA64X9pOmjYT5FDp9kfpW4tqZBWLfiyqhiYZnyHTCFeKZruH2auHZt4OlsOdzamOOkoib4CMGmUwTj112emdzlshRDLxGOSbThTWUOw61YirCl-RUrx1WFg2kZimP9Byq4pV7tv9YhtS',
postCover: 'https://www.yijunzhao.cn/upload/HermesWebUIHermesStudio%E8%83%8C%E5%90%8E%E7%9A%84%E5%93%81%E7%89%8C%E5%8D%87%E7%BA%A7%E4%B8%8E%E4%BA%A7%E5%93%81%E8%BF%9B%E5%8C%96-02.png',
})
const banner = reactive({
current: 0,
items: [
exampleImageUrls.banner,
exampleImageUrls.postCover1,
],
list: [
{
value: exampleImageUrls.banner,
title: 'UNI HALO v3.0',
desc: '新全新探索简约的艺术风格',
time: timeFrom(new Date()),
},
{
value: randomImageUrl.pc,
title: 'UNI HALO v3.0 使用手册',
desc: 'uni-halo 使用手册,包含安装、配置、使用等信息666666666aaaa啊啊啊啊啊啊啊啊啊啊啊',
time: timeFrom(new Date()),
},
],
})
const categories = reactive([
{
name: '全栈开发',
count: 120,
cover: getRandomUrl(),
},
{
name: 'Docker',
count: 0,
cover: getRandomUrl(),
},
{
name: 'Vue技术',
count: 99,
cover: getRandomUrl(),
},
])
const quickList = reactive([
{
pagePath: 'pages/moments/moments',
text: '动态',
iconType: 'uiLib',
icon: 'gift',
},
{
pagePath: 'pages-blog/about/about',
text: '关于',
iconType: 'uiLib',
icon: 'image',
},
{
pagePath: 'pages/me/me',
text: '我的',
iconType: 'uiLib',
icon: 'user',
},
{
pagePath: 'pages/category/category',
text: '分类',
iconType: 'uiLib',
icon: 'image',
},
{
pagePath: 'pages/category/category',
text: '其他',
iconType: 'uiLib',
icon: 'image',
},
])
const { loading, error, data, run } = useRequest<ListedPostVoList, PostV1alpha1PublicApiQueryPostsRequest>(queryPosts, { loadingToast: true, loadingToastContent: '加载中...', loadingToastMask: true })
const { loading: loadingSingle, error: errorSingle, data: dataSingle, run: runSingle } = useRequest<PostVo, PostV1alpha1PublicApiQueryPostByNameRequest>(queryPostByName)
const { list: postList, refresh } = useListData<ListedPostVo>({
params: {
page: 1,
size: 10,
},
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 ?? [],
})
},
})
onLoad(() => {
console.log('home onLoad')
refresh()
runSingle({ name: '019eb1ca-e37c-7729-97af-e0b658aeef0f' })
})
onPageScroll(({ scrollTop }) => {
})
</script>
<template>
<view class="min-h-screen bg-page pb-safe">
<!-- 顶部banner -->
<view v-if="false" class="relative h-72 w-full">
<image class="h-72 w-full object-cover" :src="exampleImageUrls.banner" />
<view class="absolute bottom-4 left-4 flex flex-col gap-y-2">
<text class="text-xs text-white">welcome to new world</text>
<text class="text-2xl text-white font-bold">UNI HALO V3.0 </text>
<text class="text-xs text-white">新全新探索简约的艺术风格</text>
</view>
</view>
<view>
<wd-swiper
v-model:current="banner.current" :list="banner.items" indicator-position="right" autoplay
:height="340"
>
<template #default="{ index }">
<view class="relative h-full w-full">
<image class="h-full w-full" mode="aspectFill" :src="banner.list[index].value" />
<!-- 渐变遮罩 from-black/0 via-black/50 to-black/90 bg-gradient-to-b -->
<view class="absolute bottom-0 left-0 right-0 p-4 pb-16">
<view class="relative z-20 w-5/6 flex flex-col gap-y-2">
<text class="text-xs text-white/90">{{ banner.list[index].time }}</text>
<text class="line-clamp-1 text-xl text-white font-bold">{{ banner.list[index].title }}</text>
<text class="line-clamp-1 text-xs text-white/95">{{ banner.list[index].desc }}</text>
</view>
<view
class="absolute bottom-0 left-0 right-0 z-10 h-3/5 from-black/0 via-page/50 to-page bg-gradient-to-b"
/>
</view>
</view>
</template>
<template #indicator="{ current, total }">
<view class="absolute bottom-16 right-4 text-xs text-white">
{{ current + 1 }}/{{ total }}
</view>
</template>
</wd-swiper>
</view>
<!-- 作者卡片 -->
<view class="relative z-10 box-border px-4 -mt-12">
<view class="uh-shadow-xs box-border flex flex-col items-center gap-y-2 rounded-2xl bg-white p-4">
<view class="w-full flex items-center justify-between gap-x-2">
<image
class="uh-shadow-xs h-11 w-11 shrink-0 border-2 border-white rounded-full border-solid"
src="https://www.xiaoxiaomo.cn/logo.jpg" mode="scaleToFill"
/>
<view class="h-full flex flex-1 flex-col justify-between gap-y-1">
<view class="text-sm text-gray-900 font-bold">
小莫唐尼
</view>
<view class="line-clamp-1 text-xs text-gray-500">
一个爱凑热闹喜欢捣鼓前端的码农
</view>
</view>
<view class="h-full shrink-0 rounded-3xl bg-[#1A1A1A] px-4 py-2 text-xs text-white">
联系我
</view>
</view>
<view class="my-1 h-1px w-full bg-gray-50" />
<view class="grid grid-cols-5 w-full gap-x-4">
<view class="flex flex-col items-center justify-center gap-y-0.5">
<text class="text-sm text-gray-900 font-bold">128</text>
<text class="text-xs text-gray-600">内容</text>
</view>
<view class="flex flex-col items-center justify-center gap-y-0.5">
<text class="text-sm text-gray-900 font-bold">999</text>
<text class="text-xs text-gray-600">访客</text>
</view>
<view class="flex flex-col items-center justify-center gap-y-0.5">
<text class="text-sm text-gray-900 font-bold">1.2k</text>
<text class="text-xs text-gray-600">分类</text>
</view>
<view class="flex flex-col items-center justify-center gap-y-0.5">
<text class="text-sm text-gray-900 font-bold">128k</text>
<text class="text-xs text-gray-600">评论</text>
</view>
<view class="flex flex-col items-center justify-center gap-y-0.5">
<text class="text-sm text-gray-900 font-bold">10.5k</text>
<text class="text-xs text-gray-600">点赞</text>
</view>
</view>
</view>
</view>
<!-- 金刚区 -->
<view class="mt-6 px-4">
<view class="flex items-center justify-around gap-x-4">
<view
v-for="item in quickList" :key="item.pagePath"
class="w-1/5 flex flex-col items-center justify-center gap-y-1"
>
<view class="uh-shadow-xs flex items-center justify-center rounded-2xl bg-white p-3.5">
<wd-icon :name="item.icon" :size="24" />
</view>
<text class="text-xs text-gray-800">{{ item.text }}</text>
</view>
</view>
</view>
<!-- 分类推荐磁贴 -->
<view class="mt-6 box-border px-4">
<view class="mb-4 box-border flex items-center justify-between">
<text class="text-sm text-gray-900 font-bold">热门分类</text>
</view>
<view class="box-border h-36 flex items-center gap-2 overflow-hidden">
<view class="relative box-border h-full flex-1 overflow-hidden border-2 border-white rounded-xl border-solid bg-white">
<image :src="completeUrl(categories[0].cover)" mode="heightFix" class="h-full w-full rounded-xl" />
<view class="absolute bottom-0 left-0 right-0 z-10 box-border box-border w-full flex flex-col gap-y-1 from-black/0 via-black/50 to-black/90 bg-gradient-to-b p-2">
<text class="line-clamp-1 text-xs text-white font-bold">{{ categories[0].name }}</text>
</view>
<view class="absolute right-2 top-2 z-10 box-border rounded-md bg-black/20 px-1.5 py-0.5">
<text class="line-clamp-1 text-[12px] text-white">{{ categories[0].count }}</text>
</view>
</view>
<view class="h-full flex flex-1 flex-col gap-2">
<view v-for="cate in categories.slice(1)" :key="cate.name" class="relative box-border flex-1 overflow-hidden border-2 border-white rounded-xl border-solid bg-white">
<image :src="completeUrl(cate.cover)" mode="aspectFill" class="w-full rounded-xl" />
<view class="absolute bottom-0 left-0 right-0 z-10 box-border box-border w-full flex flex-col gap-y-1 from-black/0 via-black/50 to-black/90 bg-gradient-to-b p-2">
<text class="line-clamp-1 text-xs text-white font-bold">{{ cate.name }}</text>
</view>
<view class="absolute right-2 top-2 z-10 box-border rounded-md bg-black/20 px-1.5 py-0.5">
<text class="line-clamp-1 text-[12px] text-white">{{ cate.count }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 瞬间推荐 -->
<!-- 置顶文章 -->
<view v-if="dataSingle && false" class="mt-4 box-border px-4">
<view class="mb-2 box-border flex items-center justify-between">
<text class="text-lg text-gray-900 font-bold">置顶文章</text>
</view>
<view class="box-border rounded-xl bg-white p-4">
<image :src="completeUrl(dataSingle.spec.cover)" mode="aspectFill" class="h-36 w-full rounded-xl" />
<view class="box-border w-full flex flex-col gap-y-1 pt-2">
<text class="line-clamp-1 text-sm text-gray-900 font-bold">{{ dataSingle.spec.title }}</text>
<text class="line-clamp-2 text-xs text-gray-500">{{ dataSingle.status.excerpt }}</text>
</view>
</view>
</view>
<!-- 最新文章 -->
<view class="mt-4 mt-4 box-border px-4">
<view class="mb-4 box-border flex items-center justify-between">
<text class="text-sm text-gray-900 font-bold">最新文章</text>
</view>
<view class="box-border w-full flex flex-col items-center justify-center gap-y-4 overflow-hidden">
<view v-if="error && postList.length > 0" class="un-shadow-xs w-full rounded-2xl bg-white py-12">
<wd-empty :icon-size="60" icon="no-result" tip="暂无数据" />
</view>
<template v-else>
<post-card v-for="post in postList" :key="post.metadata.name" :post="post" type="topCover" />
</template>
</view>
</view>
<!-- 底部导航占位 -->
<view class="h-60px w-full" />
</view>
</template>
<style scoped>
.uh-shadow-xs {
box-shadow: 0 0 24rpx rgba(0, 0, 0, 0.035);
}
</style>