feat: 页脚优先展示后台配置的页脚 Logo

- CmsSiteInfo 加 websiteFooterLogo 类型
- useSite 新增 siteFooterLogo(websiteFooterLogo 优先,回退
  siteLogo);siteLogo/siteFooterLogo 统一过 ensureFullUrl,
  兼容本地存储相对路径(/api/file 代理)与完整 OSS URL
- template-01~08/10 共 9 个模板 Footer 的 SiteBrand 改用
  siteFooterLogo;template-09 页脚无品牌区保持不变
- 未配置页脚 Logo 时行为与旧版完全一致(回退站点 Logo)
This commit is contained in:
2026-09-25 03:59:45 +08:00
parent 2514bd9ea0
commit 3d3a377221
11 changed files with 38 additions and 20 deletions
+18 -2
View File
@@ -121,9 +121,24 @@ export function useSite() {
return override || siteInfo.value?.websiteName || appName.value || '' return override || siteInfo.value?.websiteName || appName.value || ''
}) })
/** 站点 Logo(优先应用信息,回退 CMS 站点信息) */ /**
* 站点 Logo(优先应用信息,回退 CMS 站点信息)
* 统一 ensureFullUrl 补全:本地存储上传的相对路径(如 /20260925/xxx.png)
* 转为 /api/file/ 代理地址;完整 http(s) URL 原样返回。
*/
const siteLogo = computed(() => { const siteLogo = computed(() => {
return appLogo.value || siteInfo.value?.websiteLogo || '' const raw = appLogo.value || siteInfo.value?.websiteLogo || ''
return raw ? ensureFullUrl(raw) : ''
})
/**
* 页脚 Logo(仅用于 Footer 品牌展示,与顶部导航 Logo 分离)
* 优先级:后台「网站设置 → 站点信息」上传的 websiteFooterLogo > 站点 Logo(siteLogo)。
* 后台未单独上传页脚 Logo 时回退站点 Logo,保持旧行为不变。
*/
const siteFooterLogo = computed(() => {
const raw = siteInfo.value?.websiteFooterLogo || ''
return raw ? ensureFullUrl(raw) : siteLogo.value
}) })
/** /**
@@ -255,6 +270,7 @@ export function useSite() {
showHeaderSearch, showHeaderSearch,
siteName, siteName,
siteLogo, siteLogo,
siteFooterLogo,
siteIcon, siteIcon,
siteKeywords, siteKeywords,
phone, phone,
@@ -6,7 +6,7 @@
<div class="sm:col-span-2 lg:col-span-1"> <div class="sm:col-span-2 lg:col-span-1">
<SiteBrand <SiteBrand
link-class="mb-4" link-class="mb-4"
:logo="siteLogo" :logo="siteFooterLogo"
:icon="siteIcon" :icon="siteIcon"
:name="siteName" :name="siteName"
logo-class="h-8 w-auto max-w-[200px] object-contain" logo-class="h-8 w-auto max-w-[200px] object-contain"
@@ -118,7 +118,7 @@
*/ */
import type { CmsNavigation } from '~/types' import type { CmsNavigation } from '~/types'
const { siteInfo, siteName, siteLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, icpNo, copyright, wxQrcode, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite() const { siteInfo, siteName, siteLogo, siteFooterLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, icpNo, copyright, wxQrcode, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite()
/** 快速链接:取顶级导航中非首页且无子菜单的项 */ /** 快速链接:取顶级导航中非首页且无子菜单的项 */
const quickLinks = computed<CmsNavigation[]>(() => { const quickLinks = computed<CmsNavigation[]>(() => {
@@ -6,7 +6,7 @@
<div class="sm:col-span-2 lg:col-span-1"> <div class="sm:col-span-2 lg:col-span-1">
<SiteBrand <SiteBrand
link-class="mb-4" link-class="mb-4"
:logo="siteLogo" :logo="siteFooterLogo"
:icon="siteIcon" :icon="siteIcon"
:name="siteName" :name="siteName"
logo-class="h-8 w-auto max-w-[200px] object-contain" logo-class="h-8 w-auto max-w-[200px] object-contain"
@@ -118,7 +118,7 @@
*/ */
import type { CmsNavigation } from '~/types' import type { CmsNavigation } from '~/types'
const { siteInfo, siteName, siteLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, icpNo, copyright, wxQrcode, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite() const { siteInfo, siteName, siteLogo, siteFooterLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, icpNo, copyright, wxQrcode, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite()
/** 快速链接:取顶级导航中非首页且无子菜单的项 */ /** 快速链接:取顶级导航中非首页且无子菜单的项 */
const quickLinks = computed<CmsNavigation[]>(() => { const quickLinks = computed<CmsNavigation[]>(() => {
@@ -6,7 +6,7 @@
<div class="max-w-xs"> <div class="max-w-xs">
<SiteBrand <SiteBrand
link-class="mb-2" link-class="mb-2"
:logo="siteLogo" :logo="siteFooterLogo"
:icon="siteIcon" :icon="siteIcon"
:name="siteName" :name="siteName"
logo-class="h-8 w-auto max-w-[180px] object-contain" logo-class="h-8 w-auto max-w-[180px] object-contain"
@@ -80,7 +80,7 @@
*/ */
import type { CmsNavigation } from '~/types' import type { CmsNavigation } from '~/types'
const { siteInfo, siteName, siteLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, copyright, wxQrcode, icpNo, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite() const { siteInfo, siteName, siteLogo, siteFooterLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, copyright, wxQrcode, icpNo, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite()
/** 快速链接:取顶级导航中非首页且无子菜单的项 */ /** 快速链接:取顶级导航中非首页且无子菜单的项 */
const quickLinks = computed<CmsNavigation[]>(() => { const quickLinks = computed<CmsNavigation[]>(() => {
@@ -6,7 +6,7 @@
<div class="max-w-xs"> <div class="max-w-xs">
<SiteBrand <SiteBrand
link-class="mb-2" link-class="mb-2"
:logo="siteLogo" :logo="siteFooterLogo"
:icon="siteIcon" :icon="siteIcon"
:name="siteName" :name="siteName"
logo-class="h-8 w-auto max-w-[180px] object-contain" logo-class="h-8 w-auto max-w-[180px] object-contain"
@@ -80,7 +80,7 @@
*/ */
import type { CmsNavigation } from '~/types' import type { CmsNavigation } from '~/types'
const { siteInfo, siteName, siteLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, copyright, wxQrcode, icpNo, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite() const { siteInfo, siteName, siteLogo, siteFooterLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, copyright, wxQrcode, icpNo, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite()
/** 快速链接:取顶级导航中非首页且无子菜单的项 */ /** 快速链接:取顶级导航中非首页且无子菜单的项 */
const quickLinks = computed<CmsNavigation[]>(() => { const quickLinks = computed<CmsNavigation[]>(() => {
@@ -6,7 +6,7 @@
<div class="sm:col-span-2 lg:col-span-1"> <div class="sm:col-span-2 lg:col-span-1">
<SiteBrand <SiteBrand
link-class="mb-4" link-class="mb-4"
:logo="siteLogo" :logo="siteFooterLogo"
:icon="siteIcon" :icon="siteIcon"
:name="siteName" :name="siteName"
default-name="品牌官网" default-name="品牌官网"
@@ -119,7 +119,7 @@
*/ */
import type { CmsNavigation } from '~/types' import type { CmsNavigation } from '~/types'
const { siteInfo, siteName, siteLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, icpNo, copyright, wxQrcode, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite() const { siteInfo, siteName, siteLogo, siteFooterLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, icpNo, copyright, wxQrcode, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite()
/** 快速链接:取顶级导航中非首页且无子菜单的项 */ /** 快速链接:取顶级导航中非首页且无子菜单的项 */
const quickLinks = computed<CmsNavigation[]>(() => { const quickLinks = computed<CmsNavigation[]>(() => {
@@ -6,7 +6,7 @@
<div class="sm:col-span-2 lg:col-span-1"> <div class="sm:col-span-2 lg:col-span-1">
<SiteBrand <SiteBrand
link-class="mb-4" link-class="mb-4"
:logo="siteLogo" :logo="siteFooterLogo"
:icon="siteIcon" :icon="siteIcon"
:name="siteName" :name="siteName"
logo-class="h-9 w-auto max-w-[200px] object-contain" logo-class="h-9 w-auto max-w-[200px] object-contain"
@@ -74,7 +74,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { CmsNavigation } from '~/types' import type { CmsNavigation } from '~/types'
const { siteInfo, siteName, siteLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, icpNo, copyright, wxQrcode, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite() const { siteInfo, siteName, siteLogo, siteFooterLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, icpNo, copyright, wxQrcode, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite()
const quickLinks = computed<CmsNavigation[]>(() => { const quickLinks = computed<CmsNavigation[]>(() => {
const navs = bottomNavigations.value || [] const navs = bottomNavigations.value || []
return navs.filter((nav) => nav.model !== 'index').slice(0, 6) return navs.filter((nav) => nav.model !== 'index').slice(0, 6)
@@ -6,7 +6,7 @@
<div class="lg:col-span-3"> <div class="lg:col-span-3">
<SiteBrand <SiteBrand
link-class="mb-4" link-class="mb-4"
:logo="siteLogo" :logo="siteFooterLogo"
:icon="siteIcon" :icon="siteIcon"
:name="siteName" :name="siteName"
logo-class="h-8 w-auto max-w-[200px] object-contain" logo-class="h-8 w-auto max-w-[200px] object-contain"
@@ -135,7 +135,7 @@
import type { CmsNavigation } from '~/types' import type { CmsNavigation } from '~/types'
const { const {
siteInfo, siteName, siteLogo, siteIcon, siteConfig, siteInfo, siteName, siteLogo, siteFooterLogo, siteIcon, siteConfig,
bottomNavigations, phone, email, address, icpNo, copyright, bottomNavigations, phone, email, address, icpNo, copyright,
wxQrcode, slogan, officialWebsite, officialWebsiteUrl, socialLinks wxQrcode, slogan, officialWebsite, officialWebsiteUrl, socialLinks
} = useSite() } = useSite()
@@ -6,7 +6,7 @@
<div class="max-w-xs"> <div class="max-w-xs">
<SiteBrand <SiteBrand
link-class="mb-2" link-class="mb-2"
:logo="siteLogo" :logo="siteFooterLogo"
:icon="siteIcon" :icon="siteIcon"
:name="siteName" :name="siteName"
logo-class="h-8 w-auto max-w-[180px] object-contain" logo-class="h-8 w-auto max-w-[180px] object-contain"
@@ -80,7 +80,7 @@
*/ */
import type { CmsNavigation } from '~/types' import type { CmsNavigation } from '~/types'
const { siteInfo, siteName, siteLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, copyright, wxQrcode, icpNo, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite() const { siteInfo, siteName, siteLogo, siteFooterLogo, siteIcon, siteConfig, bottomNavigations, phone, email, address, copyright, wxQrcode, icpNo, slogan, officialWebsite, officialWebsiteUrl, socialLinks } = useSite()
/** 快速链接:取顶级导航中非首页且无子菜单的项 */ /** 快速链接:取顶级导航中非首页且无子菜单的项 */
const quickLinks = computed<CmsNavigation[]>(() => { const quickLinks = computed<CmsNavigation[]>(() => {
@@ -7,7 +7,7 @@
<div class="lg:col-span-2"> <div class="lg:col-span-2">
<SiteBrand <SiteBrand
link-class="inline-flex items-center gap-2 mb-5" link-class="inline-flex items-center gap-2 mb-5"
:logo="siteLogo" :logo="siteFooterLogo"
:icon="siteIcon" :icon="siteIcon"
:name="siteName" :name="siteName"
logo-class="h-[44px] w-auto max-w-[220px] object-contain brightness-0 invert" logo-class="h-[44px] w-auto max-w-[220px] object-contain brightness-0 invert"
@@ -104,7 +104,7 @@
import type { CmsNavigation } from '~/types' import type { CmsNavigation } from '~/types'
const { const {
siteInfo, siteName, siteLogo, siteIcon, siteInfo, siteName, siteLogo, siteFooterLogo, siteIcon,
phone, email, address, copyright, icpNo, phone, email, address, copyright, icpNo,
navigations, allNavigations, fetchSiteInfo navigations, allNavigations, fetchSiteInfo
} = useSite() } = useSite()
+2
View File
@@ -72,6 +72,8 @@ export interface CmsSiteInfo {
websiteIcon?: string websiteIcon?: string
websiteLogo?: string websiteLogo?: string
websiteDarkLogo?: string websiteDarkLogo?: string
/** 页脚 Logo(后台「网站设置 → 站点信息」上传,仅用于页脚品牌展示;空则回退 websiteLogo) */
websiteFooterLogo?: string
websiteType?: string websiteType?: string
/** /**
* 模板主键(cms_website.template_id → app_template.id) * 模板主键(cms_website.template_id → app_template.id)