Files
pc-10584/app/pages/index.vue
赵忠林 682e264a6f feat(router): 更新路由结构并优化页面组件
- 移除经营范围按钮,精简导航栏
- 实现文章标题链接功能,提升用户体验
- 添加商品详情页面包屑导航,支持分类跳转
- 引入配送管理相关页面(区域、接单台、配送员、派单)
- 替换控制台布局为站点头部和底部组件
- 重构商品分类页面,集成CMS导航功能
- 新增文章详情页面,支持多种访问方式
- 删除已迁移的创建应用和空应用页面
- 优化样式和组件导入,提升代码质量
2026-01-29 16:21:22 +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: '/goods/4476'
},
{
title: '注册地址',
desc: '南宁市江南区国凯大道东13号神冠胶原智库项目加工厂房',
to: '/contact'
},
{
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>