Files
jczxw-pc/app/components/PortalFooter.vue
T
2026-09-11 12:25:38 +08:00

319 lines
6.8 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.
<template>
<div>
<div class="footer-nav">
<div class="container footer-nav-inner">
<span class="footer-nav-title">友情链接</span>
<div v-if="friendLinkGroups.length" class="friend-link-groups">
<div
v-for="group in friendLinkGroups"
:key="group.categoryKey"
class="friend-link-group"
tabindex="0"
>
<button type="button" class="friend-link-trigger">
<span>{{ group.categoryName }}</span>
<span class="friend-link-arrow"></span>
</button>
<div class="friend-link-menu">
<a
v-for="item in group.links"
:key="item.linkId || item.id"
:href="item.linkUrl"
:target="Number(item.target) === 1 ? '_blank' : undefined"
:rel="Number(item.target) === 1 ? 'noopener noreferrer' : undefined"
>
{{ item.title }}
</a>
</div>
</div>
</div>
<span v-else class="footer-nav-empty">暂无友情链接</span>
</div>
</div>
<div class="container footer-main">
<div class="footer-left">
<p>扫一扫公众号</p>
<p>了解我们的动态</p>
<img :src="wechatQrcode || '/images/qrcode-mp-official.jpg'" alt="公众号二维码" />
</div>
<div class="footer-center">
<h3>广西决策咨询网</h3>
<p>承办单位{{ operatorName }}</p>
<p>地址{{ address }}</p>
</div>
</div>
<div class="footer-record">
<a
href="https://beian.miit.gov.cn/"
target="_blank"
rel="nofollow noopener noreferrer"
>桂ICP备2026004011号-1</a>
</div>
</div>
</template>
<script setup lang="ts">
import { listSiteDictionaryItems, listSiteFriendLinks, resolveSiteAssetUrl, type SiteFriendLink } from '@/api/site'
const operatorName = ref('广西决策咨询网有限公司')
const address = ref('广西壮族自治区南宁市良庆区五象大道401号五象航洋城3号楼')
const wechatQrcode = ref('')
const friendLinks = ref<SiteFriendLink[]>([])
const friendLinkGroups = computed(() => {
const groupMap = new Map<string, { categoryKey: string; categoryName: string; categorySort: number; links: SiteFriendLink[] }>()
friendLinks.value.forEach((item) => {
const categoryName = (item.categoryName || '其他链接').trim() || '其他链接'
const categoryKey = item.categoryId ? `${item.categoryId}` : categoryName
const current = groupMap.get(categoryKey)
if (current) {
current.links.push(item)
return
}
groupMap.set(categoryKey, {
categoryKey,
categoryName,
categorySort: Number(item.categorySort || 0),
links: [item],
})
})
return Array.from(groupMap.values()).sort((left, right) => {
const sortCompare = right.categorySort - left.categorySort
return sortCompare || left.categoryName.localeCompare(right.categoryName, 'zh-Hans-CN')
})
})
async function loadDictionary() {
try {
const items = await listSiteDictionaryItems({ dictCode: 'site_contact' })
const itemMap = Object.fromEntries((items || []).map((item) => [item.itemKey, item.itemValue]))
operatorName.value = itemMap.operator_name || operatorName.value
address.value = itemMap.address || address.value
wechatQrcode.value = resolveSiteAssetUrl(itemMap.wechat_qrcode || '')
} catch (error) {
console.error('加载站点联系字典失败', error)
}
}
async function loadFriendLinks() {
try {
friendLinks.value = await listSiteFriendLinks()
} catch (error) {
console.error('加载友情链接失败', error)
friendLinks.value = []
}
}
onMounted(() => {
loadDictionary()
loadFriendLinks()
})
</script>
<style scoped>
.container {
width: min(1200px, calc(100% - 32px));
margin: 0 auto;
}
.footer-nav {
background: #1f4fa3;
}
.footer-nav-inner {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 24px;
min-height: 58px;
flex-wrap: wrap;
padding: 10px 0;
}
.footer-nav-title {
color: #fff;
font-size: 14px;
font-weight: 600;
}
.friend-link-groups {
display: flex;
align-items: center;
gap: 14px;
flex-wrap: wrap;
}
.friend-link-group {
position: relative;
z-index: 10;
}
.friend-link-trigger {
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 34px;
padding: 0 14px;
border: 1px solid rgba(255, 255, 255, 0.28);
border-radius: 4px;
background: rgba(255, 255, 255, 0.08);
color: rgba(255, 255, 255, 0.94);
font-size: 14px;
line-height: 1;
cursor: pointer;
}
.friend-link-arrow {
font-size: 12px;
transition: transform 0.16s ease;
}
.friend-link-menu {
position: absolute;
left: 0;
top: calc(100% + 8px);
display: none;
min-width: 168px;
max-width: min(280px, calc(100vw - 32px));
padding: 8px;
border-radius: 6px;
background: #fff;
box-shadow: 0 12px 32px rgba(14, 37, 72, 0.18);
}
.friend-link-menu::after {
position: absolute;
left: 24px;
top: -6px;
width: 12px;
height: 12px;
background: #fff;
transform: rotate(45deg);
content: '';
}
.friend-link-menu a {
position: relative;
z-index: 1;
display: block;
padding: 9px 10px;
border-radius: 4px;
color: #31445a;
font-size: 14px;
line-height: 1.35;
white-space: nowrap;
}
.friend-link-menu a:hover {
background: #eef5ff;
color: #1f4fa3;
}
.friend-link-group:hover .friend-link-menu,
.friend-link-group:focus-within .friend-link-menu {
display: block;
}
.friend-link-group:hover .friend-link-arrow,
.friend-link-group:focus-within .friend-link-arrow {
transform: rotate(180deg);
}
.footer-nav-empty {
color: rgba(255, 255, 255, 0.9);
font-size: 14px;
}
.footer-main {
display: flex;
justify-content: space-between;
gap: 60px;
padding: 28px 0 52px;
}
.footer-left {
color: #4f5c67;
font-size: 13px;
}
.footer-left p {
margin: 0 0 8px;
}
.footer-left img {
width: 100px;
height: 100px;
margin-top: 10px;
object-fit: contain;
}
.footer-center {
padding-top: 10px;
color: #697680;
text-align: left;
}
.footer-center h3 {
margin: 0 0 8px;
color: #27313a;
font-size: 30px;
font-weight: 700;
}
.footer-center p {
margin: 0 0 8px;
font-size: 13px;
}
.footer-record {
padding: 16px;
border-top: 1px solid #e7ebef;
color: #697680;
font-size: 13px;
text-align: center;
}
.footer-record a {
color: inherit;
}
.footer-record a:hover {
color: #1f4fa3;
}
@media (max-width: 1100px) {
.footer-main {
flex-direction: column;
gap: 24px;
}
}
@media (max-width: 768px) {
.container {
width: min(100%, calc(100% - 24px));
}
.footer-nav-inner {
gap: 18px;
justify-content: flex-start;
}
.friend-link-groups {
width: 100%;
gap: 10px;
}
.friend-link-menu {
left: 0;
top: calc(100% + 8px);
}
}
</style>