1
0
mirror of https://github.com/ialley-workshop-open/uni-halo.git synced 2026-09-12 16:40:40 +08:00

feat: 小程序端轮播图重写,接入 Banner 归一化公开接口

- uh-swiper 组件高内聚:内部请求 banners 公开列表,去掉前 5 条
  上限,日期角标显示条目 date 快照,新增作者/日期信息浮层
- home.vue 移除配置 list 与审核模式劫持组装,点击分发改为
  post→文章详情(postId)、custom→banner-detail 详情页
- 新增 banner-detail 详情页:公开详情接口 + mp-html 富文本 +
  外链平台条件编译(非 App 端复制链接 / App 端访问按钮)
- 类型与默认配置同步清理(IBannerConfig 移除 type/list)

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
This commit is contained in:
小莫唐尼
2026-09-02 16:32:39 +08:00
parent 938c09ad4e
commit cf9c02cfc3
6 changed files with 422 additions and 211 deletions
@@ -0,0 +1,189 @@
<script lang="ts" setup>
/**
* 轮播详情页(自定义 Banner 条目点击后进入)
* 通过公开详情接口(getBannerDetail)拉取 content 富文本与外链,类文章详情页
* 外链平台差异(条件编译):非 APP-PLUS(小程序/H5)提供复制+显示链接,APP-PLUS 提供访问按钮(web-view)
*/
import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getBannerDetail } from '@/api/uni-halo'
import { checkAvatarUrl, checkThumbnailUrl } from '@/utils/url'
import { formatTime } from '@/utils/formatTime'
import { copyToClipboard } from '@/utils/restrictRead'
import type { IBannerPublicDetail } from '@/api/types/uni-halo'
definePage({
style: {
navigationBarTitleText: '轮播详情',
},
})
const loading = ref<'loading' | 'success' | 'error'>('loading')
const detail = ref<IBannerPublicDetail | null>(null)
const linkCopied = ref(false)
async function handleGetData(name: string) {
loading.value = 'loading'
try {
const res = await getBannerDetail(name)
detail.value = res.data || null
if (detail.value?.title) {
uni.setNavigationBarTitle({ title: detail.value.title })
}
loading.value = 'success'
}
catch (err) {
console.error('获取轮播详情失败', err)
loading.value = 'error'
}
}
function handleRetry() {
const name = detail.value?.name || ''
if (name) {
handleGetData(name)
}
}
onLoad((options) => {
const name = options?.name
if (name) {
handleGetData(name)
}
else {
loading.value = 'error'
}
})
const coverUrl = computed(() => checkThumbnailUrl(detail.value?.cover, true))
const authorName = computed(() => detail.value?.authorName || '')
const authorAvatar = computed(() =>
detail.value?.authorAvatar ? checkAvatarUrl(detail.value.authorAvatar) : '',
)
const dateText = computed(() => {
if (!detail.value?.date) {
return ''
}
const d = new Date(detail.value.date)
if (Number.isNaN(d.getTime())) {
return ''
}
return formatTime({ d: detail.value.date, f: 'yyyy-MM-dd' })
})
/** 非 APP-PLUS:复制链接 */
function handleCopyLink() {
if (!detail.value?.link) {
return
}
copyToClipboard(detail.value.link, '链接已复制')
linkCopied.value = true
setTimeout(() => {
linkCopied.value = false
}, 1500)
}
/** APP-PLUS:打开内置 web-view 访问外链 */
function handleOpenLink() {
if (!detail.value?.link) {
return
}
uni.navigateTo({
url: `/pages-blog/website/website?data=${JSON.stringify({
title: detail.value.title || '查看链接',
url: encodeURIComponent(detail.value.link),
})}`,
})
}
</script>
<template>
<view class="app-page">
<!-- 加载骨架 -->
<view v-if="loading === 'loading'" class="p-4">
<wd-skeleton :row="3" :animated="true" />
</view>
<!-- 加载失败 -->
<view v-else-if="loading === 'error'" class="flex flex-col items-center gap-4 py-20">
<wd-empty description="详情加载失败" />
<wd-button size="small" @click="handleRetry">重新加载</wd-button>
</view>
<!-- 详情内容 -->
<view v-else-if="detail" class="pb-8">
<!-- 封面图 -->
<image v-if="coverUrl" :src="coverUrl" class="h-[420rpx] w-full" mode="aspectFill" />
<view class="px-4">
<!-- 标题 -->
<view class="mt-6 text-[34rpx] font-bold leading-snug text-[#303133]">
{{ detail.title }}
</view>
<!-- 作者/日期信息 -->
<view v-if="authorName || dateText" class="mt-3 flex items-center gap-2">
<image
v-if="authorAvatar"
:src="authorAvatar"
class="h-[44rpx] w-[44rpx] rounded-full"
mode="aspectFill"
/>
<text class="text-[24rpx] text-[#909399]">{{ authorName }}</text>
<text v-if="dateText" class="text-[22rpx] text-[#c0c4cc]">{{ dateText }}</text>
</view>
<!-- 富文本内容 -->
<view v-if="detail.content" class="mt-6 border-t border-[#f0f0f0] pt-6">
<mp-html :content="detail.content" />
</view>
<!-- 外链(平台差异,条件编译) -->
<view v-if="detail.link" class="link-card mt-8 rounded-xl bg-[#f7f7f9] p-4">
<view class="mb-3 text-[24rpx] text-[#909399]">相关链接</view>
<text class="link-text block break-all text-[26rpx] leading-relaxed text-[#606266]">
{{ detail.link }}
</text>
<!-- #ifndef APP-PLUS -->
<!-- App (小程序/H5):复制 + 提示已复制 -->
<view
class="link-btn mt-3 inline-flex items-center rounded-lg px-6 py-2 text-[24rpx] text-white"
:class="linkCopied ? 'link-btn-copied' : ''"
@click="handleCopyLink"
>
{{ linkCopied ? '已复制' : '复制链接' }}
</view>
<!-- #endif -->
<!-- #ifdef APP-PLUS -->
<!-- App :访问按钮(内置 web-view) -->
<view class="link-btn mt-3 inline-flex items-center rounded-lg px-6 py-2 text-[24rpx] text-white" @click="handleOpenLink">
访问链接
</view>
<!-- #endif -->
</view>
</view>
</view>
</view>
</template>
<style scoped lang="scss">
.app-page {
min-height: 100vh;
background: #fff;
}
.link-text {
word-break: break-all;
}
.link-btn {
background: #2563eb;
&.link-btn-copied {
background: #10b981;
}
}
</style>