Files
pc-10584/app/pages/index.vue
赵忠林 aaef9e0dba feat(home): 替换首页轮播图实现为广告系统驱动
- 移除原有的硬编码轮播图组件和相关样式
- 新增 getAdByCode 方法用于获取广告数据
- 实现解析广告数据的工具函数 parseSlides 和 parsePx
- 集成 useAsyncData 获取 flash 广告数据
- 添加备用图片以确保加载失败时的显示
- 更新页面样式适配新的轮播组件结构
2026-01-29 14:05:01 +08:00

453 lines
10 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>
<main class="home">
<Carousel arrows class="mx-auto" :items="flashSlides" :height="flashHeight">
<template #prevArrow>
<div class="custom-slick-arrow" style="left: 10px; z-index: 1">
<left-circle-outlined />
</div>
</template>
<template #nextArrow>
<div class="custom-slick-arrow" style="right: 10px">
<right-circle-outlined />
</div>
</template>
</Carousel>
<!-- <section class="banner">-->
<!-- <div class="mx-auto max-w-screen-xl px-4 py-6">-->
<!-- <div class="banner-title">以合规经营与品质服务为核心</div>-->
<!-- </div>-->
<!-- </section>-->
<section class="mx-auto max-w-screen-xl px-4 py-6">
<div class="section-title">
<div class="section-title-main">资讯与公告</div>
<div class="section-title-sub">NEWS & UPDATES</div>
</div>
<div class="mt-6 grid grid-cols-12 gap-6">
<div v-for="c in columns" :key="c.title" class="col-span-12 lg:col-span-4">
<div class="panel">
<div class="column-head">
<div class="column-title">{{ c.title }}</div>
<a href="#" class="column-more" @click.prevent>更多 +</a>
</div>
<div class="column-list">
<a
v-for="it in c.items"
:key="it"
class="column-item"
href="#"
@click.prevent
>
{{ it }}
</a>
</div>
</div>
</div>
</div>
<div class="mt-10">
<div class="section-title">
<div class="section-title-main">资质与合作</div>
<div class="section-title-sub">COMPLIANCE</div>
</div>
<div class="mt-6 grid grid-cols-12 gap-6">
<a-card
v-for="c in compliance"
:key="c.title"
hoverable
class="case-card col-span-12 sm:col-span-6 lg:col-span-3"
@click="navigateTo(c.to)"
>
<a-typography-title :level="5" class="!mb-2 case-title">{{ c.title }}</a-typography-title>
<div class="case-desc">{{ c.desc }}</div>
</a-card>
</div>
</div>
</section>
</main>
</template>
<script setup lang="ts">
import {
AppstoreOutlined,
FileTextOutlined,
LeftCircleOutlined,
RightCircleOutlined,
SafetyCertificateOutlined,
ShopOutlined
} from '@ant-design/icons-vue'
import { usePageSeo } from '@/composables/usePageSeo'
import { getAdByCode } from '@/api/cms/cmsAd'
import type { CmsAd } from '@/api/cms/cmsAd/model'
import { COMPANY } from '@/config/company'
usePageSeo({
title: '首页',
description:
'桂乐淘:生物基材料技术研发、技术服务与食品/农产品流通服务。提供经营范围与资质信息查询及合作咨询入口。',
path: '/'
})
function parsePx(value?: string) {
if (!value) return undefined
const m = String(value).match(/(\d+)/)
if (!m) return undefined
const n = Number(m[1])
return Number.isFinite(n) ? n : undefined
}
type FlashImage = {
url?: string
path?: string
title?: string
}
const { data: flashAd } = await useAsyncData<CmsAd | null>('cms-ad-flash', () =>
getAdByCode('flash').catch(() => null)
)
const flashSlides = computed(() => {
const list = (flashAd.value?.imageList || []) as FlashImage[]
const slides = list
.map((it) => {
if (!it?.url) return null
return {
src: it.url,
href: it.path || undefined,
alt: it.title || undefined
}
})
.filter(Boolean) as Array<{ src: string; href?: string; alt?: string }>
return slides.length
? slides
: [{ src: 'https://file-cloud.yst.com.cn/photo/website/2024/11/28/5a5cc07336224e54a84561c80899bcac.jpg' }]
})
const flashHeight = computed(() => parsePx(flashAd.value?.height) ?? 360)
function splitScope(text: string) {
return text
.split(/[;]+/g)
.map((s) => s.trim())
.filter(Boolean)
.map((s) => s.replace(/[。.]$/, '').trim())
}
const generalScopeItems = computed(() => splitScope(COMPANY.scope.general))
const licensedScopeItems = computed(() => splitScope(COMPANY.scope.licensed))
const services = [
{
title: '生物基材料技术研发',
desc: '面向应用场景开展研发与技术支持',
icon: SafetyCertificateOutlined
},
{
title: '技术服务与技术推广',
desc: '技术开发/咨询/交流/转让/推广',
icon: AppstoreOutlined
},
{
title: '食品销售(预包装)',
desc: '含保健食品(预包装)销售等',
icon: ShopOutlined
},
{
title: '农产品与生鲜流通',
desc: '鲜肉/水产/蔬果/蛋类等零售与批发',
icon: FileTextOutlined
}
]
const columns = [
{
title: '公司动态',
items: [
'桂乐淘官方网站上线公告',
'业务范围与资质信息已更新',
'合作咨询:欢迎留言,我们将尽快联系',
'合规提示:涉及许可项目以许可文件为准',
'更多动态敬请期待...'
]
},
{
title: '行业资讯',
items: [
'生物基材料与绿色低碳趋势速览',
'食品流通与预包装食品合规要点',
'冷链与生鲜品质管理建议',
'更多资讯敬请期待...',
'更多资讯敬请期待...'
]
},
{
title: '合规与公告',
items: [
'一般项目:凭营业执照依法自主开展经营活动',
'许可项目:依法须经批准的项目,经批准后方可开展',
'具体经营项目以相关部门批准文件或许可证件为准',
'更多公告敬请期待...',
'更多公告敬请期待...'
]
}
]
const compliance = [
{
title: '经营范围',
desc: '一般项目/许可项目明细与说明',
to: '/products'
},
{
title: '注册地址',
desc: '南宁市江南区国凯大道东13号神冠胶原智库项目加工厂房',
to: '/products'
},
{
title: '合作咨询',
desc: '提交需求与合作意向,我们尽快联系',
to: '/contact'
},
{
title: '更多内容',
desc: '后续可接入资讯、产品与资质展示',
to: '/contact'
}
]
function scrollToCompany() {
if (!import.meta.client) return
const el = document.querySelector('#company')
if (!el) return
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
</script>
<style scoped>
.home {
background: #f4f6f8;
}
.panel {
background: #fff;
height: 360px;
border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 10px;
overflow: hidden;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.04);
}
.panel-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 14px;
background: linear-gradient(90deg, #16a34a, #22c55e);
color: #fff;
}
.panel-title {
display: inline-flex;
align-items: center;
gap: 8px;
font-weight: 800;
}
.panel-more {
color: rgba(255, 255, 255, 0.9);
font-size: 12px;
text-decoration: none;
}
.scope-item {
padding: 6px 0;
color: rgba(0, 0, 0, 0.75);
line-height: 1.6;
}
.section-pill {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 14px;
border-radius: 9999px;
background: linear-gradient(90deg, #16a34a, #22c55e);
color: #fff;
font-weight: 800;
}
.pill-left {
display: inline-flex;
align-items: center;
gap: 8px;
}
.pill-right {
font-weight: 700;
font-size: 12px;
opacity: 0.9;
}
.guide-card {
border-radius: 10px;
}
.guide-icon {
background: rgba(22, 163, 74, 0.12);
color: #16a34a;
}
.service-icon {
background: rgba(22, 163, 74, 0.12);
color: #16a34a;
}
.guide-title {
font-size: 14px;
font-weight: 800;
color: rgba(0, 0, 0, 0.88);
}
.guide-desc {
margin-top: 3px;
font-size: 12px;
color: rgba(0, 0, 0, 0.55);
}
.login-hero {
padding: 18px 16px;
background:
radial-gradient(circle at 20% 20%, rgba(255, 255, 255, 0.22), transparent 55%),
linear-gradient(90deg, #16a34a, #22c55e);
color: #fff;
}
.login-hero-title {
font-size: 18px;
font-weight: 900;
}
.login-hero-sub {
margin-top: 6px;
font-size: 12px;
letter-spacing: 0.12em;
opacity: 0.9;
}
.banner {
background:
radial-gradient(circle at 20% 30%, rgba(22, 163, 74, 0.18), transparent 55%),
linear-gradient(180deg, #ffffff, #f8fafc);
border-top: 1px solid rgba(0, 0, 0, 0.06);
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.banner-title {
text-align: center;
font-size: 28px;
font-weight: 900;
color: #15803d;
}
.section-title {
text-align: center;
}
.section-title-main {
font-size: 18px;
font-weight: 900;
color: rgba(0, 0, 0, 0.88);
}
.section-title-sub {
margin-top: 4px;
font-size: 12px;
letter-spacing: 0.12em;
color: rgba(0, 0, 0, 0.45);
}
.column-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 14px 10px;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.column-title {
font-weight: 900;
color: rgba(0, 0, 0, 0.88);
}
.column-more {
font-size: 12px;
color: rgba(21, 128, 61, 0.95);
text-decoration: none;
}
.column-list {
padding: 10px 14px 14px;
}
.column-item {
display: block;
padding: 8px 0;
font-size: 13px;
color: rgba(0, 0, 0, 0.78);
text-decoration: none;
border-bottom: 1px dashed rgba(0, 0, 0, 0.08);
}
.column-item:last-child {
border-bottom: 0;
}
.case-card {
border-radius: 10px;
overflow: hidden;
}
.case-title {
color: rgba(21, 128, 61, 0.95);
}
.case-desc {
font-size: 12px;
color: rgba(0, 0, 0, 0.55);
line-height: 1.6;
}
:deep(.slick-slide) {
text-align: center;
height: 160px;
line-height: 160px;
background: #364d79;
overflow: hidden;
}
:deep(.slick-arrow.custom-slick-arrow) {
width: 25px;
height: 25px;
font-size: 25px;
color: #fff;
background-color: rgba(31, 45, 61, 0.11);
transition: ease all 0.3s;
opacity: 0.3;
z-index: 1;
}
:deep(.slick-arrow.custom-slick-arrow:before) {
display: none;
}
:deep(.slick-arrow.custom-slick-arrow:hover) {
color: #fff;
opacity: 0.5;
}
:deep(.slick-slide h3) {
color: #fff;
}
</style>