product/beenFactory
|
|
@ -24,6 +24,9 @@ export default defineAppConfig({
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
colors: {
|
||||
primary: 'brand',
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,20 @@
|
|||
@import "tailwindcss";
|
||||
@import "@nuxt/ui";
|
||||
|
||||
@theme static {
|
||||
--color-brand-50: #EFFDF5;
|
||||
--color-brand-100: #D9FBE8;
|
||||
--color-brand-200: #B3F5D1;
|
||||
--color-brand-300: #75EDAE;
|
||||
--color-brand-400: #00DC82;
|
||||
--color-brand-500: #00A666;
|
||||
--color-brand-600: #00A666;
|
||||
--color-brand-700: #007F45;
|
||||
--color-brand-800: #016538;
|
||||
--color-brand-900: #0A5331;
|
||||
--color-brand-950: #052E16;
|
||||
}
|
||||
|
||||
|
||||
/*:root {*/
|
||||
/* --ui-container: var(--container-4xl);*/
|
||||
|
|
|
|||
|
|
@ -4,13 +4,9 @@ import type {NavigationMenuItem} from '@nuxt/ui'
|
|||
const items: NavigationMenuItem[] = [
|
||||
{
|
||||
label: '电话:15280659990',
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
label: '邮箱:564737095@qq.com',
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
label: '地址:福建省福鼎市太姥山镇海埕路13号',
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ const items = computed<NavigationMenuItem[]>(() => [{
|
|||
label: '首页',
|
||||
to: '/',
|
||||
icon: 'i-lucide-book-open',
|
||||
active: route.path.startsWith('/')
|
||||
// active: route.path.startsWith('')
|
||||
}, {
|
||||
label: '产品与服务',
|
||||
|
||||
icon: 'i-lucide-box',
|
||||
active: route.path.startsWith('/docs/components'),
|
||||
active: route.path.startsWith('/product'),
|
||||
children: [
|
||||
{label: '智慧蜂场建设', to: '/hive'},
|
||||
{label: '智慧蜂场建设', to: '/product/beenfactory'},
|
||||
{label: '智能蜂箱研制', to: '/hive'},
|
||||
{label: '质量安全溯源', to: '/hive'},
|
||||
{label: '蜂产业大数据', to: '/sensors'},
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
const colorMode = useColorMode()
|
||||
|
||||
const nextTheme = computed(() => (colorMode.value === 'dark' ? 'light' : 'dark'))
|
||||
|
||||
const switchTheme = () => {
|
||||
colorMode.preference = nextTheme.value
|
||||
}
|
||||
|
||||
const startViewTransition = (event: MouseEvent) => {
|
||||
if (!document.startViewTransition) {
|
||||
switchTheme()
|
||||
return
|
||||
}
|
||||
|
||||
const x = event.clientX
|
||||
const y = event.clientY
|
||||
const endRadius = Math.hypot(
|
||||
Math.max(x, window.innerWidth - x),
|
||||
Math.max(y, window.innerHeight - y)
|
||||
)
|
||||
|
||||
const transition = document.startViewTransition(() => {
|
||||
switchTheme()
|
||||
})
|
||||
|
||||
transition.ready.then(() => {
|
||||
const duration = 600
|
||||
document.documentElement.animate(
|
||||
{
|
||||
clipPath: [
|
||||
`circle(0px at ${x}px ${y}px)`,
|
||||
`circle(${endRadius}px at ${x}px ${y}px)`
|
||||
]
|
||||
},
|
||||
{
|
||||
duration: duration,
|
||||
easing: 'cubic-bezier(.76,.32,.29,.99)',
|
||||
pseudoElement: '::view-transition-new(root)'
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<UButton
|
||||
:aria-label="`Switch to ${nextTheme} mode`"
|
||||
:icon="`i-lucide-${nextTheme === 'dark' ? 'sun' : 'moon'}`"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
class="rounded-full"
|
||||
@click="startViewTransition"
|
||||
/>
|
||||
<template #fallback>
|
||||
<div class="size-4" />
|
||||
</template>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
animation: none;
|
||||
mix-blend-mode: normal;
|
||||
}
|
||||
|
||||
::view-transition-new(root) {
|
||||
z-index: 9999;
|
||||
}
|
||||
::view-transition-old(root) {
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
image: {
|
||||
src: string
|
||||
alt: string
|
||||
}
|
||||
index: number
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="bg-white p-2 flex flex-col drop-shadow-2xl transition-transform duration-300 ease-in-out hover:scale-105 hover:rotate-0 hover:z-10"
|
||||
:class="[
|
||||
index % 2 === 0 ? '-rotate-5' : 'rotate-5',
|
||||
index % 2 === 0 ? 'hover:-translate-x-4' : 'hover:translate-x-4'
|
||||
]"
|
||||
>
|
||||
<img
|
||||
:src="image.src"
|
||||
:alt="image.alt"
|
||||
class="size-32 object-cover"
|
||||
>
|
||||
<span class="w-32 text-xs text-black font-serif font-medium text-center mt-2">
|
||||
{{ image.alt }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
28
app/components/TitleDescription.vue
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!-- components/TitleSection.vue -->
|
||||
<template>
|
||||
<view class="flex flex-col items-center">
|
||||
<view class="text-sm mt-4 lg:mt-20 text-primary font-bold">
|
||||
{{ subtitle }}
|
||||
</view>
|
||||
<view class="text-4xl my-6 font-bold">
|
||||
{{ title }}
|
||||
</view>
|
||||
<view class="text-xl lg:mx-32 mx-6 mb-6 text-muted">
|
||||
利用物联网、人工智能等技术,集成蜂箱数据采集硬件设备及数据管理系统,协助养蜂人员远程实时获取、监控蜂箱内部环境,减少频繁开箱操作,同时提供
|
||||
预警功能辅助养殖决策,推动从传统经验养蜂到智能化养蜂转型,提高管理效率。根据不同需求分为基础版、标准版、高级版三种产品。
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '默认标题'
|
||||
},
|
||||
subtitle: {
|
||||
type: String,
|
||||
default: '默认副标题'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
<!-- components/TitleSection.vue -->
|
||||
<template>
|
||||
<view class="flex flex-col items-center">
|
||||
<view class="text-sm mt-4 lg:mt-32 text-primary font-bold">
|
||||
<view class="text-sm mt-4 lg:mt-20 text-primary font-bold">
|
||||
{{ subtitle }}
|
||||
</view>
|
||||
<view class="text-4xl my-6 font-bold">
|
||||
{{ title }}
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import type { IndexCollectionItem } from '@nuxt/content'
|
||||
|
||||
defineProps<{
|
||||
page: IndexCollectionItem
|
||||
}>()
|
||||
|
||||
const { data: posts } = await useAsyncData('index-blogs', () =>
|
||||
queryCollection('blog').order('date', 'DESC').limit(3).all()
|
||||
)
|
||||
if (!posts.value) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'blogs posts not found', fatal: true })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPageSection
|
||||
:description="page.blog.description"
|
||||
:title="page.blog.title"
|
||||
:ui="{
|
||||
container: 'px-0 !pt-0 sm:gap-6 lg:gap-8',
|
||||
title: 'text-left text-xl sm:text-xl lg:text-2xl font-medium',
|
||||
description: 'text-left mt-2 text-sm sm:text-md lg:text-sm text-muted'
|
||||
}"
|
||||
>
|
||||
<UBlogPosts
|
||||
class="gap-4 lg:gap-y-4"
|
||||
orientation="vertical"
|
||||
>
|
||||
<UBlogPost
|
||||
v-for="(post, index) in posts"
|
||||
:key="index"
|
||||
:to="post.path"
|
||||
:ui="{
|
||||
root: 'group relative lg:items-start lg:flex ring-0 hover:ring-0',
|
||||
body: '!px-0',
|
||||
header: 'hidden'
|
||||
}"
|
||||
orientation="horizontal"
|
||||
v-bind="post"
|
||||
variant="naked"
|
||||
>
|
||||
<template #footer>
|
||||
<UButton
|
||||
class="px-0 gap-0"
|
||||
label="Read Article"
|
||||
size="xs"
|
||||
variant="link"
|
||||
>
|
||||
<template #trailing>
|
||||
<UIcon
|
||||
class="size-4 text-primary transition-all opacity-0 group-hover:translate-x-1 group-hover:opacity-100"
|
||||
name="i-lucide-arrow-right"
|
||||
/>
|
||||
</template>
|
||||
</UButton>
|
||||
</template>
|
||||
</UBlogPost>
|
||||
</UBlogPosts>
|
||||
</UPageSection>
|
||||
</template>
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import type { IndexCollectionItem } from '@nuxt/content'
|
||||
|
||||
const props = defineProps<{
|
||||
page: IndexCollectionItem
|
||||
}>()
|
||||
|
||||
const items = computed(() => {
|
||||
return props.page.faq?.categories.map((faq) => {
|
||||
return {
|
||||
label: faq.title,
|
||||
key: faq.title.toLowerCase(),
|
||||
questions: faq.questions
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const ui = {
|
||||
root: 'flex items-center gap-4 w-full',
|
||||
list: 'relative flex bg-transparent dark:bg-transparent gap-2 px-0',
|
||||
indicator: 'absolute top-[4px] duration-200 ease-out focus:outline-none rounded-lg bg-elevated/60',
|
||||
trigger: 'px-3 py-2 rounded-lg hover:bg-muted/50 data-[state=active]:text-highlighted data-[state=inactive]:text-muted',
|
||||
label: 'truncate'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPageSection
|
||||
:title="page.faq.title"
|
||||
:description="page.faq.description"
|
||||
:ui="{
|
||||
container: 'px-0 !pt-0 gap-4 sm:gap-4',
|
||||
title: 'text-left text-xl sm:text-xl lg:text-2xl font-medium',
|
||||
description: 'text-left mt-2 text-sm sm:text-md lg:text-sm text-muted'
|
||||
}"
|
||||
>
|
||||
<UTabs
|
||||
:items
|
||||
orientation="horizontal"
|
||||
:ui
|
||||
>
|
||||
<template #content="{ item }">
|
||||
<UAccordion
|
||||
trailing-icon="lucide:plus"
|
||||
:items="item.questions"
|
||||
:unmount-on-hide="false"
|
||||
:ui="{
|
||||
item: 'border-none',
|
||||
trigger: 'mb-2 border-0 group px-4 transform-gpu rounded-lg bg-elevated/60 will-change-transform hover:bg-muted/50 text-base',
|
||||
trailingIcon: 'group-data-[state=closed]:rotate-0 group-data-[state=open]:rotate-135 text-base text-muted'
|
||||
}"
|
||||
>
|
||||
<template #body="{ item: _item }">
|
||||
<MDC
|
||||
:value="_item.content"
|
||||
unwrap="p"
|
||||
class="px-4"
|
||||
/>
|
||||
</template>
|
||||
</UAccordion>
|
||||
</template>
|
||||
</UTabs>
|
||||
</UPageSection>
|
||||
</template>
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import type { IndexCollectionItem } from '@nuxt/content'
|
||||
|
||||
defineProps<{
|
||||
page: IndexCollectionItem
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPageSection
|
||||
:ui="{
|
||||
container: 'px-0 !pt-0'
|
||||
}"
|
||||
>
|
||||
<UCarousel
|
||||
v-slot="{ item }"
|
||||
:autoplay="{ delay: 4000 }"
|
||||
:items="page.testimonials"
|
||||
:ui="{
|
||||
viewport: '-mx-4 sm:-mx-12 lg:-mx-16 bg-elevated/50 max-w-(--ui-container)'
|
||||
}"
|
||||
dots
|
||||
loop
|
||||
>
|
||||
<UPageCTA
|
||||
:description="item.quote"
|
||||
:ui="{
|
||||
container: 'sm:py-12 lg:py-12 sm:gap-8',
|
||||
description: '!text-base text-balance before:content-[open-quote] before:text-5xl lg:before:text-7xl before:inline-block before:text-dimmed before:absolute before:-ml-6 lg:before:-ml-10 before:-mt-2 lg:before:-mt-4 after:content-[close-quote] after:text-5xl lg:after:text-7xl after:inline-block after:text-dimmed after:absolute after:mt-1 lg:after:mt-0 after:ml-1 lg:after:ml-2'
|
||||
}"
|
||||
class="rounded-none"
|
||||
variant="naked"
|
||||
>
|
||||
<UUser
|
||||
class="justify-center"
|
||||
size="xl"
|
||||
v-bind="item.author"
|
||||
/>
|
||||
</UPageCTA>
|
||||
</UCarousel>
|
||||
</UPageSection>
|
||||
</template>
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import type { IndexCollectionItem } from '@nuxt/content'
|
||||
|
||||
defineProps<{
|
||||
page: IndexCollectionItem
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPageSection
|
||||
:title="page.experience.title"
|
||||
:ui="{
|
||||
container: '!p-0 gap-4 sm:gap-4',
|
||||
title: 'text-left text-xl sm:text-xl lg:text-2xl font-medium',
|
||||
description: 'mt-2'
|
||||
}"
|
||||
>
|
||||
<template #description>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Motion
|
||||
v-for="(experience, index) in page.experience.items"
|
||||
:key="index"
|
||||
:initial="{ opacity: 0, transform: 'translateY(20px)' }"
|
||||
:while-in-view="{ opacity: 1, transform: 'translateY(0)' }"
|
||||
:transition="{ delay: 0.4 + 0.2 * index }"
|
||||
:in-view-options="{ once: true }"
|
||||
class="text-muted flex items-center text-nowrap gap-2"
|
||||
>
|
||||
<p class="text-sm">
|
||||
{{ experience.date }}
|
||||
</p>
|
||||
<USeparator />
|
||||
<ULink
|
||||
class="flex items-center gap-1"
|
||||
:to="experience.company.url"
|
||||
target="_blank"
|
||||
>
|
||||
<span class="text-sm">
|
||||
{{ experience.position }}
|
||||
</span>
|
||||
<div
|
||||
class="inline-flex items-center gap-1"
|
||||
:style="{ color: experience.company.color }"
|
||||
>
|
||||
<span class="font-medium">{{ experience.company.name }}</span>
|
||||
<UIcon :name="experience.company.logo" />
|
||||
</div>
|
||||
</ULink>
|
||||
</Motion>
|
||||
</div>
|
||||
</template>
|
||||
</UPageSection>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
65
app/components/product/beenFactory/Hero.vue
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<script lang="ts" setup>
|
||||
import TitleDescription from "~/components/TitleDescription.vue";
|
||||
|
||||
const cardItems = ref([
|
||||
{
|
||||
title: "蜂场环境气象监测站",
|
||||
description: "为养蜂人员提供的一套蜂场整体环境数据实时获取、监测及软硬件工具,用户不需到现场频繁开箱,可随时远程了解整个蜂场实时环境或历史信息,管理省时又省心。",
|
||||
image: "/img/product/beenFactory/product1.png"
|
||||
},
|
||||
{
|
||||
title: "智能全景视频监控站",
|
||||
description: "利用智能全景视频监控站实现对蜂场物资和周边环境的高清视频远程监控,支持远程视频查看及自动预警。",
|
||||
image: "/img/product/beenFactory/product2.png"
|
||||
},
|
||||
{
|
||||
title: "蜂场环境远程控制系统",
|
||||
description: "智能全景视频监控站和蜂场环境气象监测站的配套软件,帮助用户实现对蜂场环境信息的远程实时自动获取、巡检及预警,辅助精准养殖。",
|
||||
image: "/img/product/beenFactory/product3.png"
|
||||
},
|
||||
{
|
||||
title: "智能蜂箱",
|
||||
description: "为蜂农提供蜜蜂养殖场所的同时,还可实现对蜂箱内部环境的远程实时监控管理,便于用户快速了解蜂箱环境、蜂群活动及产蜜情况。",
|
||||
image: "/img/product/beenFactory/product4.png"
|
||||
},
|
||||
{
|
||||
title: "蜜源地调查",
|
||||
description: "通过对蜂场周边环境进行自动化巡视,准确掌握蜜源地分布情况,确保蜂场周边蜜源充足。",
|
||||
image: "/img/product/beenFactory/product5.png"
|
||||
}
|
||||
])
|
||||
</script>
|
||||
<template>
|
||||
|
||||
<img alt="" class="w-max" src="/img/product/beenFactory/banner.png">
|
||||
|
||||
<TitleDescription
|
||||
subtitle="SERVICE INTRODUCTION"
|
||||
title="服务简介"
|
||||
/>
|
||||
|
||||
|
||||
<UPageCTA
|
||||
v-for="(item, index) in cardItems"
|
||||
:key="index"
|
||||
:description="item.description"
|
||||
:reverse="index % 2 == 0"
|
||||
:title="item.title"
|
||||
:ui="{
|
||||
title: 'font-semibold text-lg text-primary',
|
||||
}"
|
||||
orientation="horizontal"
|
||||
>
|
||||
<img
|
||||
:src="item.image"
|
||||
alt="Illustration"
|
||||
class="w-full rounded-lg"
|
||||
height="364"
|
||||
width="320"
|
||||
>
|
||||
</UPageCTA>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
13
app/pages/product/beenFactory.vue
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Upage>
|
||||
<ProductBeenFactoryHero/>
|
||||
</Upage>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: '2025-07-15',
|
||||
devtools: { enabled: true },
|
||||
modules: ['@nuxt/eslint', '@nuxt/ui', '@nuxt/image'],
|
||||
css: ['~/assets/css/main.css'],
|
||||
fonts: {
|
||||
providers: {
|
||||
google: false, // 禁用 Google Fonts
|
||||
googleicons: false // 禁用 Google Icons
|
||||
}
|
||||
},
|
||||
compatibilityDate: '2025-07-15',
|
||||
devtools: {enabled: true},
|
||||
modules: ['@nuxt/eslint', '@nuxt/ui', '@nuxt/image'],
|
||||
css: ['~/assets/css/main.css'],
|
||||
fonts: {
|
||||
providers: {
|
||||
google: false, // 禁用 Google Fonts
|
||||
googleicons: false // 禁用 Google Icons
|
||||
}
|
||||
},
|
||||
|
||||
})
|
||||
BIN
public/img/product/beenFactory/banner.png
Normal file
|
After Width: | Height: | Size: 948 KiB |
BIN
public/img/product/beenFactory/product1.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
public/img/product/beenFactory/product2.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
public/img/product/beenFactory/product3.png
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
public/img/product/beenFactory/product4.png
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
public/img/product/beenFactory/product5.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
public/img/product/beenFactory/s1.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
public/img/product/beenFactory/s2.350b6dd.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
public/img/product/beenFactory/s3.a743187.png
Normal file
|
After Width: | Height: | Size: 150 KiB |