一文带你入门 Nuxt 【俺是怎么学习一个框架的be like】

前言

本篇文章是笔者花了两天时间学习新框架的思路展示,同时也是一个 Nuxt 入门教程,希望有兴趣的小伙伴可以多多指点。通过这篇文章,你会得到以下内容:

  • 一个简单的 Nuxt 应用
  • 一个基本的公司门户网站 Demo 应用

最终效果

一、什么是 Nuxt

在开始学习 Nuxt 之前,我们得理解下它主要是用来做什么的。

来看看官网上是怎么说的吧。

这里引入了一个概念:SSR网站。

百度给我们的解释是:是一个在服务器端生成HTML页面并将其发送给客户端的网站

再去 github 上检索下相关内容,看看各位大佬一般用 Nuxt 都开发些什么开源项目。

虽然这里只是个例,但是从侧面反映出:一般大家用 Nuxt 开发企业官网、博客网站等等这些需要首页加载速度快的网站。

一、创建 Nuxt 应用会遇到的问题

去官网上找一下 Nuxt 的创建命令。

lua 复制代码
pnpm create nuxt <项目名称>

这时候我们会遇见一个坑爹的问题:

vbnet 复制代码
Error: Failed to download template from registry: Failed to download https://raw.githubusercontent.com/nuxt/starter/templates/templates/v3.json: TypeError: fetch failed

简单说就是后面这个地址有墙。

解决办法

管理员运行 cmd 进入下面目录:

bash 复制代码
cd C:\Windows\System32\drivers\etc

用记事本打开这个文件:

复制代码
notepad hosts

在最下面添加:

复制代码
185.199.108.133 raw.githubusercontent.com
185.199.109.133 raw.githubusercontent.com
185.199.110.133 raw.githubusercontent.com
185.199.111.133 raw.githubusercontent.com

记得保存。创建项目之前最好 ping 一下 raw.githubusercontent.com 这个地址看能否 ping 通。

二、Nuxt 入门知识

项目创建成功以后,和熟悉的 vue 不同, Nuxt 的目录少了很多东西。
vue 项目一样,我们先看 App.vue 文件。

App.vue 复制代码
<template>
  <div>
    <NuxtRouteAnnouncer />
    <NuxtWelcome />
  </div>
</template>

这两个是官方给的欢迎页,启动项目以后可以发现 App.vue 其实是整个项目的入口。

如果我们想要定义自己的页面,就需要使用 <NuxtPage> 组件。

App.vue 复制代码
<template>
  <div>
    <NuxtPage />
  </div>
</template>

Nuxt 帮我们把 vue-router 封装好了,我们只需要在根目录下新建 pages 文件夹,即可定义自己的页面。

pages/index.vue 复制代码
<template>
  <span>Hello World!</span>
</template>

pages 文件夹下(以及它的子文件夹)里面的 index.vue 就是我们的 redirect 路由。

pages 文件夹下新建文件夹 user 和它的 index.vue

这样一个简单的路由就配置好了。

2.1 路由传参

Nuxt 中,使用 [] 来进行路由传参。

可以看到,[] 不是在代码里写的,而是在 pages 目录里面使用的,通过 $route 或者 useRoute() 的方式去拿到。

2.3 获取各级路由

可以使用 [...slug] 去拿到路径上的各级路由: 如上图,我们就拿到了 pagesuser 路径上所有涉及到的路由名称集合:

2.4 父子路由

也就是我们常说的 嵌套路由 ,可以使用 NuxtPage 来做到这些。

我们先来看 /parent 路径下的页面:

可以看出来,这里其实跟 App.vue 文件里的 NuxtPage 作用其实是一样的。

再来看 /parent/child 路径下的页面:

这里可以看到,params 是正常的传递过来的。

2.5 路由组

这个路由组,我们可以理解为:一种不影响路由路径的文件存放方式。

比如我们现在在某个同级的路由下有需求A和需求B,这样就可以区分出来,方便后续的代码维护。

这里四个 test 文件内容差不多,就不一一截图了。

可以看到完全不影响正常使用,但是在文件层面上分的十分清晰。

2.6 路由元数据

这个元数据有点类似 vue 里面的 provide ,在 Nuxt 里面用 definePageMeta 去使用它:

在子路由我们就可以拿到这个 title 元数据

看看页面效果:

需要注意的一点是,这个 definePageMeta 定义的元数据可以引用绑定值和局部定义 的纯函数,不要把用 ref 声明的对象传进元数据中。官网是这么说的:

除了这些自定义的元数据以外,还有一些特殊的元数据:

【在这里我们挑常用的展示一下】

2.6.1 layout

看名字就知道,这是个布局元数据,通过它我们可以在根目录的 layouts 文件夹里配置自定义布局。

我们要先在 App.vue 里通过 <NuxtLayout> 这个组件去启用它。

然后在根目录的 layouts 文件夹里自定义一个页面布局文件:

<slot /> 插槽就是使用该布局的外部页面。

看看效果:

【注意在 layouts 里的文件名称层级之间不要有重复,避免 Test-base/base 这种结构。如果有那么会被解析为: test-base 这种形式】

假如我们有另外一个布局,那么我们就可以用 setPageLayout 来进行切换。

通常情况下,我们会有在页面上使用布局的需求,那么可以试试 layout: false

2.6.2 alias

这是一个路径别名,比如我们可以把根目录下的 index.vue 换个路径:

使用了 alias 以后,原来的路径也是可以继续使用的。

2.6 导航

我们可以使用 <NuxtLink> 进行导航:

如果需要传递参数,我们可以调用 navigateTo() 函数,这是 Nuxt 提供的函数。

ok,到这里就差不多够用了,接下来让我们去搭建一个公司官网的 Demo

三、搭建一个简单的门户网站

3.1 创建项目

简单做个电子仪器的官网。

lua 复制代码
pnpm create nuxt detection-instrument

3.2 引入 tailwindcss

perl 复制代码
"@nuxtjs/tailwindcss": "^6.13.2",

nuxt.config.ts 文件里引入:

nuxt.config.ts 复制代码
···
modules: ['@nuxtjs/tailwindcss'],
tailwindcss: {
  exposeConfig: true,
  viewer: true,
}
···

在页面上试一下:

pages/index.vue 复制代码
<template>
  <div class="bg-red-600">Hello World</div>
</template>

3.3 引入 fontawesome 图标

perl 复制代码
"@fortawesome/fontawesome-free": "^6.7.2",

同样在 nuxt.config.ts 引入:

nuxt.config.ts 复制代码
···
css: ['@fortawesome/fontawesome-free/css/all.css'],
···

在页面上试下:

pages/index.vue 复制代码
<template>
  <i class="fas fa-search text-red-500"></i>
</template>

ps: 顺便把 devtools 关掉

nuxt.config.ts 复制代码
···
devtools: { enabled: true },
···

最终的 nuxt.config.ts 完整代码如下:

nuxt.config.ts 复制代码
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  compatibilityDate: '2024-11-01',
  devtools: { enabled: true },
  modules: ['@nuxtjs/tailwindcss'],
  css: ['@fortawesome/fontawesome-free/css/all.css'],
  tailwindcss: {
    exposeConfig: true,
    viewer: true,
  }
})

3.4 首页

做一个默认布局,门户网站就靠这个布局撑场面:

3.4.1 布局

laayouts/default.vue 复制代码
<template>
  <div class="layout-container">
    <!-- 导航 -->
    <nav class="fixed top-0 left-0 right-0 bg-white shadow-sm z-50">
      <div class="max-w-[1440px] mx-auto px-8 h-20 flex items-center justify-between">
        <div class="flex items-center gap-16">
          <NuxtLink to="/" class="text-2xl font-bold">杭州爱博仪器有限公司</NuxtLink>
          <div class="flex gap-8">
            <NuxtLink 
              v-for="item in navItems" 
              :key="item.name" 
              :to="item.path" 
              class="text-gray-600 hover:text-blue-600 cursor-pointer"
              :exact-active-class="'!text-blue-600 !font-medium'"
            >
              {{ item.name }}
            </NuxtLink>
          </div>
        </div>
        <div class="flex items-center gap-4">
          <div class="relative">
            <input type="text" placeholder="搜索产品或解决方案" class="w-64 h-10 pl-10 pr-4 rounded-lg bg-gray-100 border-none text-sm outline-none"> 
            <i class="fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
          </div>
          <NuxtLink to="/contact" class="rounded-sm bg-blue-600 text-white px-6 h-10 cursor-pointer whitespace-nowrap flex items-center justify-center">联系我们</NuxtLink>
        </div>
      </div>
    </nav>
    
    <!-- 内容 -->
    <div class="pt-20">
      <slot />
    </div>
    
    <!-- 页脚 -->
    <footer class="bg-gray-900 text-white py-16">
      <div class="max-w-[1440px] mx-auto px-8">
        <div class="grid grid-cols-4 gap-8 mb-12">
          <div>
            <h4 class="text-xl font-bold mb-6">关于我们</h4>
            <p class="text-gray-400">杭州爱博仪器有限公司成立于 1998 年,是中国领先的仪器供应商,专注于为客户提供高品质的产品和专业的技术方案。</p>
          </div>
          <div>
            <h4 class="text-xl font-bold mb-6">联系方式</h4>
            <div class="space-y-4 text-gray-400">
              <p>电话:400-888-8888</p>
              <p>邮箱:[email protected]</p>
              <p>地址:浙江省杭州市余杭区xx高科技园区</p>
            </div>
          </div>
          <div>
            <h4 class="text-xl font-bold mb-6">快速导航</h4>
            <div class="space-y-4">
              <NuxtLink 
                v-for="item in navItems" 
                :key="item.name" 
                :to="item.path" 
                class="block text-gray-400 hover:text-white cursor-pointer"
                :exact-active-class="'!text-white !font-medium'"
              >
                {{ item.name }}
              </NuxtLink>
            </div>
          </div>
          <div>
            <h4 class="text-xl font-bold mb-6">关注我们</h4>
            <div class="flex gap-4">
              <i class="fab fa-weixin text-2xl cursor-pointer"></i>
              <i class="fab fa-weibo text-2xl cursor-pointer"></i>
              <i class="fab fa-qq text-2xl cursor-pointer"></i>
            </div>
          </div>
        </div>
        <div class="text-center text-gray-400 pt-8 border-t border-gray-800">
          <p>© 2025 杭州爱博仪器有限公司. 保留所有权利</p>
        </div>
      </div>
    </footer>
  </div>
</template>

<script lang="ts" setup>
const navItems = [
  { name: '公司介绍', path: '/introduce' },
  { name: '解决方案', path: '/solutions' },
  { name: '产品中心', path: '/products' },
  { name: '服务与支持', path: '/projects' },
  { name: '新闻中心', path: '/news' },
  { name: '关于爱博', path: '/about' },
  { name: '线上商城', path: '/shopping' }
];
</script>

<style scoped>
.layout-container {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
</style>

在页面上使用

pages/index.vue 复制代码
<template>
  <i class="fas fa-search text-red-500"></i>
</template>

<script lang="ts" setup>
definePageMeta({
  layout: 'default'
});
</script>

ps: 爱博是笔者以前小区的名字

3.4.2 首页

安装 swiper 组件

json 复制代码
"swiper": "^11.2.5",

网站首页咱就做简单些,Hero区域、解决方案、产品展示、新闻中心、公司优势5个板块就够用了。

pages/index.vue 复制代码
<template>
  <div class="min-h-[1024px]">
    <!-- Hero区域 -->
    <swiper :pagination="true" :modules="swiperOptions.modules">
      <swiper-slide>
        <SwiperItem :swiperData="swiperData" />
      </swiper-slide>
      <swiper-slide>
        <SwiperItem :swiperData="swiperData" />
      </swiper-slide>
      <swiper-slide>
        <SwiperItem :swiperData="swiperData" />
      </swiper-slide>
      <swiper-slide>
        <SwiperItem :swiperData="swiperData" />
      </swiper-slide>
      <swiper-slide>
        <SwiperItem :swiperData="swiperData" />
      </swiper-slide>
    </swiper>
    <!-- 解决方案 -->
    <div class="bg-gray-50 py-20">
      <div class="max-w-[1440px] mx-auto px-8">
        <h3 class="text-3xl font-bold text-center mb-12">解决方案</h3>
        <div class="flex gap-8">
          <div class="w-64 bg-white rounded-lg p-6">
            <div
              v-for="solution in solutions"
              :key="solution"
              @click="currentSolution = solution"
              class="py-3 px-4 rounded cursor-pointer mb-2"
              :class="
                currentSolution === solution
                  ? 'bg-blue-600 text-white'
                  : 'text-gray-600'
              "
            >
              {{ solution }}
            </div>
          </div>
          <div class="flex-1 bg-white rounded-lg p-8">
            <div class="flex gap-8">
              <div class="flex-1">
                <h4 class="text-2xl font-bold mb-4">
                  {{ solutionData[currentSolution].title }}
                </h4>
                <p class="text-gray-600 mb-6">
                  {{ solutionData[currentSolution].description }}
                </p>
                <ul class="space-y-4">
                  <li
                    v-for="feature in solutionData[currentSolution].features"
                    :key="feature"
                    class="flex items-center gap-3"
                  >
                    <i class="fas fa-check-circle text-blue-600"></i>
                    <span>{{ feature }}</span>
                  </li>
                </ul>
                <div class="mt-8">
                  <NuxtLink
                    :to="`/solutions`"
                    class="!rounded-button bg-blue-600 text-white px-8 h-12 cursor-pointer whitespace-nowrap inline-flex items-center justify-center"
                  >
                    了解更多
                  </NuxtLink>
                </div>
              </div>
              <div class="w-80 rounded-lg overflow-hidden">
                <img
                  :src="solutionData[currentSolution].image"
                  class="w-full h-full object-cover"
                />
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <!-- 产品展示 -->
    <div class="max-w-[1440px] mx-auto px-8 py-20">
      <h3 class="text-3xl font-bold text-center mb-12">产品中心</h3>
      <div class="flex gap-4 mb-12 justify-center">
        <button
          v-for="cat in categories"
          :key="cat"
          @click="currentCategory = cat"
          class="!rounded-button px-6 h-10 cursor-pointer whitespace-nowrap"
          :class="
            currentCategory === cat
              ? 'bg-blue-600 text-white'
              : 'bg-gray-100 text-gray-600'
          "
        >
          {{ cat }}
        </button>
      </div>
      <div class="grid grid-cols-4 gap-8">
        <ProductItem
          v-for="(product, index) in filteredProducts"
          :key="product.name"
          :product="product"
          :detailLink="`/products/${index + 1}`"
          :isReaddy="true"
        />
      </div>
    </div>
    <!-- 新闻中心 -->
    <div class="bg-gray-50 py-20">
      <div class="max-w-[1440px] mx-auto px-8">
        <h3 class="text-3xl font-bold text-center mb-12">新闻中心</h3>
        <div class="grid grid-cols-3 gap-8">
          <NewsItem
            v-for="news in newsItems"
            :key="news.title"
            :news="news"
            :newsLink="`/news/${news.id}`"
            :readMoreLink="`/news/${news.id}`"
            :isReaddy="true"
          />
        </div>
        <div class="text-center mt-10">
          <NuxtLink
            to="/news"
            class="!rounded-button bg-blue-600 text-white px-8 h-12 cursor-pointer whitespace-nowrap inline-flex items-center justify-center"
          >
            查看更多新闻
          </NuxtLink>
        </div>
      </div>
    </div>
    <!-- 公司优势 -->
    <div class="max-w-[1440px] mx-auto px-8 py-20">
      <h3 class="text-3xl font-bold text-center mb-12">为什么选择我们</h3>
      <div class="grid grid-cols-4 gap-8">
        <div
          v-for="advantage in advantages"
          :key="advantage.title"
          class="text-center"
        >
          <i :class="advantage.icon" class="text-4xl text-blue-600 mb-4"></i>
          <h4 class="font-bold mb-2">{{ advantage.title }}</h4>
          <p class="text-gray-600 text-sm">{{ advantage.desc }}</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
definePageMeta({
  layout: "default",
});

import { Swiper, SwiperSlide } from "swiper/vue";

import { Pagination } from "swiper/modules";

import "swiper/css";

import "swiper/css/pagination";

import ProductItem from "~/components/ProductItem.vue";
import NewsItem from "~/components/NewsItem.vue";
import SwiperItem from "~/components/SwiperItem.vue";

const swiperOptions = {
  modules: [Pagination],
};

const swiperData = {
  backgroundImage:
    "https://www.hzaihua.com.cn/public/uploads/admin/img/20230714_104252.jpeg",
  title: "专业电子仪器产品提供商",
  description: "25 年专业经验,服务超过 10000 个工程项目",
  btnLeft: "了解更多",
  btnRight: "工程案例",
};

const heroBackground =
  "https://www.hzaihua.com.cn/public/uploads/admin/img/20230714_104252.jpeg";

const solutions = [
  "功能区声环境质量自动监测",
  "建筑施工场界环境噪声自动监测",
  "工业企业厂界环境噪声自动监测",
  "社会生活环境噪声自动监测解决方案",
] as const;

type SolutionType = (typeof solutions)[number];

interface SolutionData {
  title: string;
  description: string;
  features: string[];
  image: string;
}

const solutionData: Record<SolutionType, SolutionData> = {
  功能区声环境质量自动监测: {
    title: "功能区声环境质量自动监测",
    description:
      "基于自主研发的噪声自动监测仪器,实现噪声自动监测并进行噪声数据统计分析,评价声环境功能区昼间、夜间声环境质量,掌握功能区环境噪声时空分布特征。",
    features: [
      "超强耐磨性能",
      "优异防腐蚀性能",
      "卓越防滑性能",
      "美观持久",
      "施工周期短",
    ],
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20221206_190211.png",
  },
  建筑施工场界环境噪声自动监测: {
    title: "建筑施工场界环境噪声自动监测",
    description:
      "方案按照国家及行业标准规范,基于自主研发的噪声自动监测仪器,掌握建筑施工场界噪声排放变化规律、强度特征及超标声源类型,提升建筑施工噪声监测自动化、标准化、智能化水平,为施工审批、噪声监管等提供有效技术手段和数据支撑",
    features: ["高弹性", "耐候性强", "施工简便", "使用寿命长", "环保无污染"],
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20241128_161118.png",
  },
  工业企业厂界环境噪声自动监测: {
    title: "工业企业厂界环境噪声自动监测",
    description:
      "基于自主研发的噪声自动监测仪器,实现噪声自动监测并进行噪声数据统计分析,掌握噪声变化规律和排放强度,智能识别超标声源类型和方向,为工业企业厂界噪声排放的管理、评价及控制提供数据支撑。",
    features: ["优异保温性能", "防火安全", "抗裂耐久", "施工便捷", "节能环保"],
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20241128_154647.png",
  },
  社会生活环境噪声自动监测解决方案: {
    title: "社会生活环境噪声自动监测解决方案",
    description:
      "基于自主研发的噪声自动监测仪器,实现噪声自动监测并进行噪声数据统计分析,掌握噪声变化规律和排放强度,智能识别超标声源类型和方向,并可拓展显示屏、语音播报等模块实现现场噪声数据实时显示、噪声超标语音提醒,为噪声污染防治管理提供现代化技术手段。",
    features: [
      "加固效果显著",
      "施工周期短",
      "不增加结构自重",
      "不改变建筑外观",
      "经济实用",
    ],
    image:
      "	https://www.hzaihua.com.cn/public/uploads/admin/img/20210706_150836.jpeg",
  },
};

const currentSolution = ref<SolutionType>("功能区声环境质量自动监测");

const categories = [
  "噪声自动监测系统 ",
  "职业卫生噪声测量仪器",
  "多功能声级计",
  "声校准器",
  "智能传声器",
];

const currentCategory = ref("噪声自动监测系统 ");

const products = [
  {
    name: "AWA5000型环境噪声自动监测仪",
    desc: "可对噪声信号进行实时1/1和1/3倍频程分析,并可以监测与分析环境噪声的特征,可通过无线或有线的网络传输,实现远程数据遥测、噪声事件监测、系统自动校准,最终形成报告。",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20240403_104436.gif",
    category: "噪声自动监测系统 ",
  },
  {
    name: "AWA4000型多功能噪声监测系统",
    desc: "AWA4000型多功能噪声监测系统是一款能够进行短期可移动定点监测的环境噪声监测设备,可搭配摄像头进行现场环境的实时监测,广泛应用于噪声监测子站比对测试、投诉及声源治理等短期应急监测、功能区24小时监测、机场噪声监测等场景。",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20221202_081709.jpeg",
    category: "噪声自动监测系统 ",
  },
  {
    name: "AWA3000型环境噪声自动监测仪",
    desc: "AWA3000型环境噪声自动监测仪为固定式噪声自动监测仪器,包括噪声监测子站,支持扩展气象监测单元、视频监控单元、车流量监测单元、LED显示屏、语音播报LED显示屏、声源追踪仪等模块,可固定安装,适用于长期噪声监测。",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20221201_141904.jpeg",
    category: "噪声自动监测系统 ",
  },
  {
    name: "AWA2000型噪声监测系统",
    desc: "AWA2000噪声监测系统是一款简易户外噪声自动监测系统,能实现噪声自动监测并进行实时噪声数据统计分析,支持扩展LED显示屏、语音播报LED显示屏模块等。",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20221202_081909.jpeg",
    category: "噪声自动监测系统 ",
  },
  {
    name: "YSD136本安型声级计",
    desc: "YSD136本安型声级计是一种模块化、多功能声级计。采用了先进的数字检波技术,使得仪器的稳定性、可靠性大大提高。而且动态范围大、操作简单、用途广。外壳采用C4120 NC防静电材料,外形美观,重量轻,便于携带。仪器具有大容量存贮、录音、U盘等功能。",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20240409_085655.gif",
    category: "职业卫生噪声测量仪器",
  },
  {
    name: "AWA5920个体噪声剂量计",
    desc: "AWA5920型个体噪声剂量计采用数字信号处理技术,可同步测量多种评价指标。该仪器采用模块化设计,用户可以根据需求选择相应的模块。它具有多功能、高性能、体积小、耗电低等优点。",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20230704_155731.jpeg",
    category: "职业卫生噪声测量仪器",
  },
  {
    name: "AWA6292 型多功能声级计",
    desc: "AWA6292型多功能声级计是采用数字信号处理技术和网络技术的新一代噪声测量仪器。潘通(PANTONE)高级金属色,视觉效果更加高端大气稳重,外壳采用工业级ABS+PC材料,坚固耐用。",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20210705_104004.jpeg",
    category: "多功能声级计",
  },
  {
    name: "AWA6228+型多功能声级计",
    desc: "AWA6228+型多功能声级计是采用数字信号处理技术的新一代噪声测量仪器。可以同时进行积分测量、统计分析、1/1 OCT分析、1/3 OCT分析、FFT分析、个人声暴露。该仪器可广泛应用在环境保护、职业卫生、安全监督、产品检验、科研教学、厅堂音质等领域,完成工厂噪声、机器设备噪声以及建筑声学等测量。",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20210528_092304.jpeg",
    category: "多功能声级计",
  },
  {
    name: "AWA6021A型声校准器/AWA6022A型声校准器",
    desc: "GB/T 15173---2010和IEC 60942:2017《电声学 声校准器》标准将声校准器的准确度等级分为LS级、1级和2级,LS级一般只在实验室中使用,而1级和2级为现场使用。AWA6021A型声校准器准确度等级为1级可以对1级或2级声级计进行校准;AWA6022A型声校准器准确度等级为2级只可以对2级声级计进行校准;",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20210608_163749.jpeg",
    category: "声校准器",
  },
  {
    name: "iSV1610型USB传声器",
    desc: "由测试传声器、传声器前置放大器、24位A/D和USB接口组成,直接通过USB线与手机和平板电脑等智能设备相连接,将测量的数字信号数据传输到智能设备,配合智能设备上安装的软件,成为一台数字化声级计和实时信号分析仪,可实现积分测量、统计测量、1/1 OCT、1/3 OCT、FFT分析等功能。",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20211208_140528.jpeg",
    category: "智能传声器",
  },
  {
    name: "iSV1102型声级计(智能传声器)",
    desc: "iSV1102 声级计是一种新型的噪声监测仪,可广泛应用于各种噪声监测、监控的场合。可短期监测,也可长期固定点位监测。可单独组网,也可方便集成进各类原有环境监测系统。可以广泛用于工业噪声测量和环境噪声测量。适用于工厂企业、环境保护、教学、科研等领域。",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20210709_160624.jpeg",
    category: "智能传声器",
  },
  {
    name: "AWA3301 智能传声器",
    desc: "AWA3301系列智能传声器是随着物联网技术、云技术的发展而自主研发的智能传声器。产品主要由传感器、数据处理模块、网络传输模块和后端平台组成。AWA3301系列适用于工作场所噪声测量、智慧楼宇噪声监测等室内长期噪声测量和监测。",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20210617_171724.jpeg",
    category: "智能传声器",
  },
  {
    name: "AWA144XX系列传声器",
    desc: "AWA144XX系列测量传声器是精密的声学测量用声-电换能器,用于将声信号转换为电信号。采用镍或钛合金振膜和外壳,并进行特殊的稳定性处理,具有频率范围宽、频率响应好、动态范围宽、动态特性好、温度和长时间稳定性好等优点。",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20210602_134455.jpeg",
    category: "智能传声器",
  },
];

const filteredProducts = computed(() => {
  return products.filter(
    (product) => product.category === currentCategory.value
  );
});

const newsItems = [
  {
    id: "1",
    title: "如何打造"宁静工地"?红茶议事出新招!",
    summary:
      "为破解建筑施工噪声扰民难题,爱华仪器坚持高标准设计、高质量建设,打造"4个1"宁静工地建设解决方案,结合保护目标制定"一工地一方案",探索一条运用基层社会治理手段,解决老百姓"家门口"建筑施工噪声污染问题的科技新路径。",
    date: "2025-04-11",
    views: "6,321",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20241212_145050.png",
  },
  {
    id: "2",
    title: "关于声压、声功率、声强那点事",
    summary:
      "我们每天都被声音包围:鸟鸣、音乐、车辆的轰鸣......但你是否想过,这些声音背后隐藏着怎样的物理规律?为什么有的声音"震耳欲聋",有的却"细若蚊蝇"?今天,我们就用最通俗的语言,揭开声音的三个核心参数------声压、声功率、声强的奥秘。",
    date: "2025-04-14",
    views: "3,326",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20250318_105750.png",
  },
  {
    id: "3",
    title: "信号评估指标------峰度(Kurtosis)",
    summary:
      "等能量假说认为,噪声暴露引起的噪声性听力损失(noise-induced hearing loss,NIHL)是能量强度乘以暴露时间的函数,与噪声的时域结构特征无关,然而研究表明,在同等能量的噪声暴露水平下,脉冲噪声或者含有冲击成分的非稳态噪声会导致更大程度的听力损失。",
    date: "2025-04-17",
    views: "5,142",
    image:
      "https://www.hzaihua.com.cn/public/uploads/admin/img/20250118_090949.png",
  },
];

const advantages = [
  {
    icon: "fas fa-medal",
    title: "25 年专业经验",
    desc: "成立于2000年,是国家高新技术企业,荣获工信部第三批"专精特新"小巨人企业荣誉称号",
  },
  {
    icon: "fas fa-certificate",
    title: "品质认证",
    desc: "ISO9001 质量管理体系认证,建有各类专业实验室,同类产品品种、产量和市场占有率均居国内前列。",
  },
  {
    icon: "fas fa-users",
    title: "专业团队",
    desc: "拥有一支经验丰富的技术服务团队,承担并完成多项国家技术创新基金项目、浙江省重点高新技术新产品研制项目和杭州市科技攻关项目,多次获省、市科技进步奖",
  },
  {
    icon: "fas fa-clock",
    title: "快速响应",
    desc: "营销服务网络覆盖全国各大中城市,用户遍及所有省、自治区、直辖市。",
  },
];
</script>
<style scoped>
.swiper {
  width: 100%;
  height: 100%;
}

.swiper-slide {
  text-align: center;
  font-size: 18px;
  background: #fff;

  /* Center slide text vertically */
  display: flex;
  justify-content: center;
  align-items: center;
}

.swiper-slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
</style>
components/NewsItem.vue 复制代码
<template>
  <div class="bg-white rounded-lg overflow-hidden shadow-sm group cursor-pointer">
    <div class="aspect-video overflow-hidden">
      <NuxtLink
        :to="newsLink"
        :data-readdy="isReaddy"
      >
        <img
          :src="news.image"
          class="w-full h-full object-cover transition duration-300 group-hover:scale-110"
        />
      </NuxtLink>
    </div>
    <div class="p-6">
      <div class="flex items-center gap-4 text-sm text-gray-500 mb-3">
        <span><i class="far fa-calendar-alt mr-2"></i>{{ news.date }}</span>
        <span><i class="far fa-eye mr-2"></i>{{ news.views }}</span>
      </div>
      <h4 class="font-bold text-lg mb-3 group-hover:text-blue-600">
        <NuxtLink :to="newsLink">{{ news.title }}</NuxtLink>
      </h4>
      <p class="text-gray-600 text-sm mb-4 line-clamp-2">
        {{ news.summary }}
      </p>
      <NuxtLink :to="readMoreLink" class="text-blue-600 text-sm flex items-center">
        阅读更多
        <i class="fas fa-arrow-right ml-2"></i>
      </NuxtLink>
    </div>
  </div>
</template>

<script setup>
defineProps({
  news: {
    type: Object,
    required: true
  },
  newsLink: {
    type: String,
    default: "/news/1"
  },
  readMoreLink: {
    type: String,
    default: "/news/1"
  },
  isReaddy: {
    type: Boolean,
    default: true
  }
});
</script> 
components/ProductItem.vue 复制代码
<template>
  <NuxtLink :to="detailLink" :data-readdy="isReaddy" class="block group">
    <div class="cursor-pointer">
      <div class="aspect-square rounded-lg overflow-hidden mb-4">
        <img
          :src="product.image"
          class="w-full h-full object-cover transition duration-300 group-hover:scale-110"
        />
      </div>
      <h4 class="font-bold mb-2">{{ product.name }}</h4>
      <p class="text-gray-600 text-sm mb-4">{{ product.desc }}</p>
      <div class="!rounded-button bg-gray-100 text-gray-600 px-4 h-8 text-sm cursor-pointer whitespace-nowrap group-hover:bg-blue-600 group-hover:text-white inline-flex items-center justify-center">
        查看详情
      </div>
    </div>
  </NuxtLink>
</template>

<script setup>
const props = defineProps({
  product: {
    type: Object,
    required: true
  },
  detailLink: {
    type: String,
    required: true
  },
  isReaddy: {
    type: Boolean,
    default: true
  }
});
</script> 
components/SwiperItem.vue 复制代码
<template>
<div class="relative h-[600px] bg-cover bg-center w-full" :style="{ backgroundImage: `url(${swiperData.backgroundImage})` }">
  <div class="absolute inset-0 bg-gradient-to-r from-gray-900/80 to-transparent">
    <div class="max-w-[1440px] mx-auto px-8 h-full flex items-center">
      <div class="max-w-2xl text-white">
        <h2 class="text-5xl font-bold mb-6">{{ swiperData.title }}</h2>
        <p class="text-xl mb-8">{{ swiperData.description }}</p>
        <div class="flex gap-4">
          <NuxtLink to="/about" class="!rounded-button bg-blue-600 px-8 h-12 cursor-pointer whitespace-nowrap inline-flex items-center justify-center">{{ swiperData.btnLeft }}</NuxtLink>
          <NuxtLink to="/projects" class="!rounded-button border-2 border-white px-8 h-12 cursor-pointer whitespace-nowrap inline-flex items-center justify-center">{{ swiperData.btnRight }}</NuxtLink>
        </div>
      </div>
    </div>
  </div>
</div>
</template>

<script setup>
defineProps({
  swiperData: {
    type: Object
  },
});
</script> 

3.5 结语

介于篇幅,我们只做个首页。里面有些路由可以自行按需更改

最后贴一下用到的参考,可能部分有墙: 图标库 阻止文本换行 exactActiveClass 图标定位平移 边框厚度 grid布局 子元素的间隔 字体大小 边框宽度 Tailwindcss小技巧 gap属性 background-size属性 rem 100vh和100%区别 水平居中 大部分是用到插件的官网示例库之类的。

相关推荐
梦想CAD控件1 分钟前
(AI帮忙网页cad二次开发)MxCAD多行文本扩展
前端·javascript·vue.js
zayyo3 分钟前
企业级:多版本代码管理
前端
阿彪最稳健了5 分钟前
整理一些好用的TS写法
前端·typescript
周星星日记11 分钟前
13.vue3中异步组件defineAsyncComponent实现原理
前端·vue.js·面试
anyup11 分钟前
uni-app APP 高效热更新全攻略
前端·前端框架·uni-app
心走15 分钟前
WebRTC系列 WebGL 绘制YUV 画面
前端·音视频开发
方方洛16 分钟前
组件是怎样写的(1):虚拟列表-VirtualList
前端·vue.js·react.js
VillanelleS25 分钟前
前端工程化之自动化部署
运维·前端·自动化
moyu8434 分钟前
高效开发必备:手把手整合IconFont、 Vant与Element Plus
前端·javascript
trust Tomorrow35 分钟前
JS案例-基于Proxy的响应式数据
前端·javascript·css·html