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

chore: 废弃第三方友链提交功能,重构友链相关配置与分类逻辑

1.  移除linksSubmitPlugin相关配置与提交接口代码
2.  重构友链配置数据结构,拆分linkInfo为miniInfo/siteInfo/authorInfo
3.  优化分类页审核模式渲染逻辑,补充postCount默认值处理
4.  更新所有关联页面与组件的数据源读取逻辑
This commit is contained in:
小莫唐尼
2026-09-10 02:32:06 +08:00
parent eb19276a2d
commit be55d9ac2c
9 changed files with 114 additions and 143 deletions
+44 -5
View File
@@ -14,14 +14,41 @@ export interface IImagesConfig {
[key: string]: unknown
}
/** 插件配置(toolsPlugin/linksSubmitPlugin 等带 Authorization) */
/** 插件配置(toolsPlugin/linksPlugin 等带 Authorization) */
export interface IPluginConfig {
votePlugin?: Record<string, unknown>
toolsPlugin?: { Authorization?: string } & Record<string, unknown>
linksPlugin?: Record<string, unknown>
linksSubmitPlugin?: { Authorization?: string } & Record<string, unknown>
/** 链接配置(插件端 spec.linkInfo 直接下发到本键,字段名无映射:displayName/miniProgramCode/link/description/applyRemark/authorName/avatar/website */
linkInfo?: Record<string, unknown>
/**
* 链接配置(插件端 spec.linkInfo 直接下发到本键,结构 = {miniInfo, siteInfo, authorInfo},字段名无映射;
* linksSubmitPlugin 旧键已弃用不再下发)
*/
linkInfo?: {
/** 小程序信息(「申请信息」弹窗展示) */
miniInfo?: {
displayName?: string
miniProgramCode?: string
link?: string
description?: string
applyRemark?: string
}
/** 站点信息(对齐 Halo 官方友链提交 APIdisplayName/url/logo/description/email/backlink/feedUrls */
siteInfo?: {
displayName?: string
url?: string
logo?: string
description?: string
email?: string
backlink?: string
feedUrls?: string[]
}
/** 作者信息(小程序端作者区) */
authorInfo?: {
authorName?: string
avatar?: string
website?: string
}
}
doubanPlugin?: { position?: string } & Record<string, unknown>
[key: string]: unknown
}
@@ -78,7 +105,7 @@ export interface IPageConfig {
visible?: boolean
}>
/** 首页精选分类引用(插件端「通用配置 → 页面设置 → 首页」配置,固定最多 3 个,
* 快照含名称/封面/排序权重,数组顺序 = 展示顺序;配置模式下直接映射渲染不发请求,
* 快照含名称/封面/排序权重/文章数,数组顺序 = 展示顺序;配置模式下直接映射渲染不发请求,
* 未配置/为空时回退默认取数) */
categories?: Array<{
name: string
@@ -86,6 +113,8 @@ export interface IPageConfig {
cover?: string
/** 分类排序权重(Halo Category.spec.priority,越大越靠前) */
priority?: number
/** 分类文章数(Halo Category.status.postCount 冗余快照,缺失默认 0 */
postCount?: number
}>
}
categoryConfig?: { type?: string }
@@ -123,6 +152,16 @@ export interface IAuditDataResult {
description?: string
[key: string]: unknown
}
/** 分类完整快照(插件端公开接口附带:剔除失效、按配置顺序;app 端审核模式分类页免请求映射 ICategory) */
categoryDetails?: Array<{
name: string
title?: string
cover?: string
/** 排序权重(Halo Category.spec.priority) */
priority?: number
/** 文章数(Halo Category.status.postCount,缺失默认 0) */
postCount?: number
}>
}
/** 应用基础配置(对应旧 DefaultAppConfigs) */
-20
View File
@@ -377,19 +377,6 @@ export function createVerificationCode() {
/* ==================== 友链提交(linkssubmit.muyin.site) ==================== */
/**
* 提交友链
*/
export function submitLink(form: ISubmitLinkForm) {
return http.Post<IResponse<{ msg?: string, message?: string }>>('/apis/linkssubmit.muyin.site/v1alpha1/submit', form, {
headers: {
'Authorization': getLinksSubmitAuthorization(),
'Wechat-Session-Id': getOpenid(),
},
meta: { requestFrom: RequestFrom.Halo },
})
}
/* ==================== 投票(api.vote.kunkunyu.com) ==================== */
/**
@@ -510,13 +497,6 @@ function getToolsAuthorization(): string {
return getAppConfigFromStore().pluginConfig?.toolsPlugin?.Authorization || ''
}
/**
* 友链提交插件授权头(源自应用配置 pluginConfig.linksSubmitPlugin.Authorization)
*/
function getLinksSubmitAuthorization(): string {
return getAppConfigFromStore().pluginConfig?.linksSubmitPlugin?.Authorization || ''
}
/**
* 读取应用配置(store 未就绪时兜底为空)
* 注:待 src/store/appConfig.ts 建立后改为 useAppConfigStore 读取
@@ -34,7 +34,8 @@
slug: '',
cover: checkThumbnailUrl(c.cover),
priority: c.priority,
}
},
postCount: c.postCount ?? 0,
} as ICategory))
}
else {
@@ -24,20 +24,21 @@ const emit = defineEmits<{
const isShow = ref(false)
const appConfigStore = useAppConfigStore()
/** 小程序申请信息(字段与 uh-links-mini-apply 表单一致,从插件端 linkInfo 配置直接读取,字段名无映射) */
/** 小程序申请信息(字段与 uh-links-mini-apply 表单一致;小程序信息读 linkInfo.miniInfo、作者信息读 linkInfo.authorInfo字段名无映射) */
const miniInfo = computed(() => {
const cfg = (appConfigStore.configs.pluginConfig?.linkInfo || {}) as Record<string, unknown>
const str = (key: string, fallback = '') => String(cfg[key] || fallback || '')
const miniCfg = (appConfigStore.configs.pluginConfig?.linkInfo?.miniInfo || {}) as Record<string, unknown>
const authorCfg = (appConfigStore.configs.pluginConfig?.linkInfo?.authorInfo || {}) as Record<string, unknown>
const str = (cfg: Record<string, unknown>, key: string, fallback = '') => String(cfg[key] || fallback || '')
return {
displayName: str('displayName'),
miniProgramCode: str('miniProgramCode'),
link: str('link'),
authorName: str('authorName'),
avatar: str('avatar'),
website: str('website'),
description: str('description'),
applyRemark: str('applyRemark'),
email: str('email'),
displayName: str(miniCfg, 'displayName'),
miniProgramCode: str(miniCfg, 'miniProgramCode'),
link: str(miniCfg, 'link'),
authorName: str(authorCfg, 'authorName'),
avatar: str(authorCfg, 'avatar'),
website: str(authorCfg, 'website'),
description: str(miniCfg, 'description'),
applyRemark: str(miniCfg, 'applyRemark'),
email: str(miniCfg, 'email'),
}
})
@@ -5,8 +5,6 @@
* 提交后等待站长审核,通过后展示在「站点」列表中
*/
import { ref, watch } from 'vue'
import { submitLink } from '@/api/uni-halo'
import type { ISubmitLinkForm } from '@/api/types/uni-halo'
const props = withDefaults(defineProps<{
show?: boolean
@@ -88,34 +86,9 @@ async function handleSubmit() {
if (!validateForm())
return
submitting.value = true
uni.showLoading({ title: '正在提交...' })
try {
const payload: ISubmitLinkForm = {
name: form.value.name.trim(),
url: form.value.url.trim(),
logo: form.value.logo.trim() || undefined,
linkPageUrl: form.value.linkPageUrl.trim() || undefined,
email: form.value.email.trim() || undefined,
rssUrl: form.value.rssUrl.trim() || undefined,
description: form.value.description.trim() || undefined,
}
const res = await submitLink(payload)
const msg = res.data?.msg || res.data?.message || res.message || '提交成功'
uni.showToast({ icon: 'none', title: msg })
if (res.code === 200 || res.code === undefined) {
handleClose(true)
handleResetForm()
}
}
catch (err) {
console.error('友链申请提交失败', err)
uni.showToast({ icon: 'none', title: '提交失败,请稍后重试!' })
}
finally {
submitting.value = false
uni.hideLoading()
}
// linksSubmitPlugin 已弃用(2026-09-08),第三方友链自助提交暂未开放;
// 后续可对接 Halo 官方 plugin-links link-applications 接口
uni.showToast({ icon: 'none', title: '友链申请功能暂未开放,请联系站长' })
}
function handleOnChange(isOpen: boolean) {
@@ -2,7 +2,7 @@
/**
* 站点友链信息弹窗(源自旧页面 pages-blog/submit-link 的博客详情弹窗,重设计为底部玻璃弹窗)
* 展示本站友链交换信息(博客名片 + 复制交换信息 + 站点缩略图)
* 数据源为 linksSubmitPlugin 配置(即本站申请提交的信息)
* 数据源为插件端 linkInfo.siteInfo 配置(字段对齐 Halo 官方友链提交 API:displayName/url/logo/description/email/backlink/feedUrls)
*/
import { computed, ref, watch } from 'vue'
import { useAppConfigStore } from '@/store/appConfig'
@@ -21,19 +21,22 @@ const emit = defineEmits<{
const isShow = ref(false)
const appConfigStore = useAppConfigStore()
const blogDetail = computed(() => (appConfigStore.configs.pluginConfig?.linksSubmitPlugin as {
blogName?: string
blogUrl?: string
blogLogo?: string
blogDesc?: string
const blogDetail = computed(() => (appConfigStore.configs.pluginConfig?.linkInfo?.siteInfo as {
displayName?: string
url?: string
logo?: string
description?: string
email?: string
backlink?: string
feedUrls?: string[]
} | undefined) || {})
/** 友链交换信息文案(复制用) */
const calcBlogContent = computed(() => `
博客名称:${blogDetail.value.blogName || ''}
博客地址:${blogDetail.value.blogUrl || ''}
博客logo${checkAvatarUrl(blogDetail.value.blogLogo)}
博客简介:${blogDetail.value.blogDesc || ''}
博客名称:${blogDetail.value.displayName || ''}
博客地址:${blogDetail.value.url || ''}
博客logo${checkAvatarUrl(blogDetail.value.logo)}
博客简介:${blogDetail.value.description || ''}
`)
function calcSiteThumbnail(val?: string): string {
@@ -88,14 +91,14 @@ watch(() => props.show, (val) => {
<view class="flex items-center">
<image
class="uh-global-card-glass h-20 w-20 shrink-0 rounded-2xl"
:src="checkAvatarUrl(blogDetail.blogLogo)" mode="aspectFill"
:src="checkAvatarUrl(blogDetail.logo)" mode="aspectFill"
/>
<view class="ml-4 flex flex-1 flex-col justify-center gap-y-1">
<text class="text-md text-gray-900 font-bold">
{{ blogDetail.blogName || '未命名博客' }}
{{ blogDetail.displayName || '未命名博客' }}
</text>
<text class="text-xs text-gray-500">
{{ blogDetail.blogDesc || '这个博主很懒,没写简介~' }}
{{ blogDetail.description || '这个博主很懒,没写简介~' }}
</text>
</view>
</view>
@@ -107,8 +110,8 @@ watch(() => props.show, (val) => {
<!-- 站点缩略图 -->
<image
v-if="blogDetail.blogUrl" class="mt-4 h-[320rpx] w-full rounded-xl"
:src="calcSiteThumbnail(blogDetail.blogUrl)" mode="aspectFill"
v-if="blogDetail.url" class="mt-4 h-[320rpx] w-full rounded-xl"
:src="calcSiteThumbnail(blogDetail.url)" mode="aspectFill"
/>
<view class="my-6">
-2
View File
@@ -10,8 +10,6 @@ export const DefaultAppConfigs: IAppConfig = {
votePlugin: {},
toolsPlugin: {},
linksPlugin: {},
// 保留:友链提交授权头(pluginConfig.linksSubmitPlugin.Authorization)与站点信息展示仍读取
linksSubmitPlugin: {},
doubanPlugin: {
position: 'bottom',
},
+22 -45
View File
@@ -5,7 +5,6 @@
*/
import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { submitLink } from '@/api/uni-halo'
import { useAppConfigStore } from '@/store/appConfig'
import { checkAvatarUrl } from '@/utils/url'
@@ -18,11 +17,14 @@ definePage({
const appConfigStore = useAppConfigStore()
const haloPluginConfigs = computed(() => appConfigStore.configs.pluginConfig)
const blogDetail = computed(() => (haloPluginConfigs.value?.linksSubmitPlugin as {
blogName?: string
blogUrl?: string
blogLogo?: string
blogDesc?: string
const blogDetail = computed(() => (haloPluginConfigs.value?.linkInfo?.siteInfo as {
displayName?: string
url?: string
logo?: string
description?: string
email?: string
backlink?: string
feedUrls?: string[]
} | undefined) || {})
const blogDetailPoupShow = ref(false)
@@ -38,10 +40,10 @@ const form = ref({
})
const calcBlogContent = computed(() => `
博客名称:${blogDetail.value.blogName}
博客地址:${blogDetail.value.blogUrl}
博客logo${checkAvatarUrl(blogDetail.value.blogLogo)}
博客简介:${blogDetail.value.blogDesc}
博客名称:${blogDetail.value.displayName}
博客地址:${blogDetail.value.url}
博客logo${checkAvatarUrl(blogDetail.value.logo)}
博客简介:${blogDetail.value.description}
`)
function calcSiteThumbnail(val?: string): string {
@@ -77,34 +79,9 @@ async function handleHandle() {
return
}
uni.showLoading({ title: '正在提交...' })
try {
const res = await submitLink({
name: form.value.name,
url: form.value.url,
logo: form.value.logo,
description: form.value.description,
email: form.value.email,
linkPageUrl: form.value.linkPageUrl,
rssUrl: form.value.rssUrl,
})
uni.hideLoading()
const code = res.code
const msg = res.data?.msg || res.data?.message || res.message || '提交成功'
uni.showToast({ icon: 'none', title: msg })
if (code === 200 || code === undefined) {
setTimeout(() => {
uni.navigateTo({
url: '/pages-blog/friend-links/friend-links',
})
}, 1000)
}
}
catch (err) {
console.error(err)
uni.hideLoading()
uni.showToast({ icon: 'none', title: '提交失败,请重试!' })
}
// linksSubmitPlugin 已弃用(2026-09-08),第三方友链自助提交暂未开放;
// 后续可对接 Halo 官方 plugin-links link-applications 接口
uni.showToast({ icon: 'none', title: '友链申请功能暂未开放,请联系站长' })
}
function handleCopyLink() {
@@ -129,13 +106,13 @@ onLoad(() => {
<view class="app-page box-border min-h-screen w-screen bg-[#fafafd] p-8">
<!-- 博客详情卡片 -->
<view class="blog-coupon mb-6 flex items-center rounded-xl p-6" style="background: linear-gradient(135deg, #2196f3, #64b5f6);" @click="blogDetailPoupShow = true">
<image class="coupon-img h-[80rpx] w-[80rpx] shrink-0 rounded-xl" :src="checkAvatarUrl(blogDetail.blogLogo)" mode="aspectFill" />
<image class="coupon-img h-[80rpx] w-[80rpx] shrink-0 rounded-xl" :src="checkAvatarUrl(blogDetail.logo)" mode="aspectFill" />
<view class="coupon-info ml-5 flex-1">
<view class="coupon-title text-[30rpx] text-white font-bold">
{{ blogDetail.blogName }}
{{ blogDetail.displayName }}
</view>
<view class="coupon-desc mt-1 text-[24rpx] text-white/80">
{{ blogDetail.blogDesc }}
{{ blogDetail.description }}
</view>
</view>
<view class="coupon-btn border-2 border-white/60 rounded-3xl px-5 py-1 text-[24rpx] text-white">
@@ -198,20 +175,20 @@ onLoad(() => {
<wd-popup v-model="blogDetailPoupShow" position="center" custom-style="width:640rpx;border-radius:12rpx;">
<view class="poup p-9">
<view class="info flex">
<image class="poup-logo h-[140rpx] w-[140rpx] rounded-3xl" :src="checkAvatarUrl(blogDetail.blogLogo)" mode="aspectFill" />
<image class="poup-logo h-[140rpx] w-[140rpx] rounded-3xl" :src="checkAvatarUrl(blogDetail.logo)" mode="aspectFill" />
<view class="info-detail ml-6 flex flex-1 flex-col justify-center">
<view class="poup-name text-[34rpx] font-bold">
{{ blogDetail.blogName }}
{{ blogDetail.displayName }}
</view>
<view class="poup-tag mt-2.5 text-[24rpx] text-[#999]">
{{ blogDetail.blogDesc }}
{{ blogDetail.description }}
</view>
</view>
</view>
<view class="poup-desc mt-6 whitespace-pre-wrap text-[28rpx] text-[#555] leading-[1.8]">
<text>{{ calcBlogContent }}</text>
</view>
<image v-if="blogDetail.blogUrl" class="poup-img mt-6 h-[320rpx] w-[568rpx] rounded-xl" :src="calcSiteThumbnail(blogDetail.blogUrl)" mode="aspectFill" />
<image v-if="blogDetail.url" class="poup-img mt-6 h-[320rpx] w-[568rpx] rounded-xl" :src="calcSiteThumbnail(blogDetail.url)" mode="aspectFill" />
<view class="poup-link my-6 flex justify-center gap-6">
<wd-button size="small" plain type="primary" @click="handleCopyLink">
复制友链交换信息
+12 -13
View File
@@ -54,21 +54,20 @@
updateLoadingStatus(DataLoadingStatusEnum.Loading)
// 增加延迟,提升用户体验
await sleep(800)
// 审核模式
// 审核模式:直接使用审核配置分类快照(categoryDetails)映射 ICategory,免请求
if (calcAuditModeEnabled.value) {
const auditCategoryNames = appConfigStore.auditData.spec?.categories || []
const auditCategoryDetails = appConfigStore.auditData.categoryDetails || []
try {
const res = await getCategoryList({ page: 1, size: 99999 })
const filtered = res.data.items
.filter(item => auditCategoryNames.includes(item.metadata.name))
.map(item => ({
...item,
postCount: item.postCount ?? 0,
spec: { ...item.spec, cover: checkThumbnailUrl(item.spec.cover, true) },
}))
const orderMap = new Map(auditCategoryNames.map((name, index) => [name, index]))
filtered.sort((a, b) => (orderMap.get(a.metadata.name) ?? 999) - (orderMap.get(b.metadata.name) ?? 999))
dataList.value = filtered
dataList.value = auditCategoryDetails.map(item => ({
metadata: { name: item.name },
spec: {
displayName: item.title || item.name,
slug: '',
cover: checkThumbnailUrl(item.cover, true),
priority: item.priority,
},
postCount: item.postCount ?? 0,
} as ICategory))
updateLoadingStatus(dataList.value.length === 0 ? DataLoadingStatusEnum.Empty : DataLoadingStatusEnum.Success)
loadMoreText.value = t('common.noMore')
uni.hideLoading()