vue3+node.js+mysql写接口(二)

目录

一、产品模块(products表)

1.1、添加产品(/adminapi/product/add)

1.2、产品列表(/adminapi/product/list)

1.3、编辑产品(/adminapi/product/update)

1.4、首页产品联动

二、前台模块

2.1、路由配置

2.2、NavBar组件

2.3、新闻搜索

2.4、新闻选项卡

2.5、新闻详情

2.6、产品与服务

三、总结

前言:

前端项目git地址:Enterprise Product Management System: 企业产品管理系统:web端展示、后台进行添加信息,前端用的vue3+vuex。

后端项目git地址:Enterprise Product Management System Backend: 企业产品管理系统:后端项目,用的node +mysql

查看git记录:"第一版"是只有后台管理+admin/web分离的接口,"第二版"是将前台页面和后台管理系统放在一起了,根据自己需要进行下载与切换。目前是"第二版"。

一、产品模块(products表)

1.1、添加产品(/adminapi/product/add)

后端先创建一个products表

-- 创建products表
CREATE TABLE IF NOT EXISTS products (
  id INT AUTO_INCREMENT PRIMARY KEY,-- 1、传统自增int主键
  title VARCHAR(50) NOT NULL UNIQUE,
  introduction TEXT,
  detail TEXT,
  cover VARCHAR(255),
  userId INT DEFAULT 1,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  edit_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 2、UUID作为主键
-- id CHAR(36) PRIMARY KEY DEFAULT (UUID())
-- 缺点:不连续,索引性能低于自增int
-- 3、时间戳作为主键
-- 4、带前缀的自增编号(如 NEWS0001、PROD0001)
-- 每个表直接会重复?

-- 插入测试产品数据
INSERT INTO products (title, introduction, detail, cover,userId) VALUES
('标题1','简要描述1','详细描述1','/avatar/admin.jpg', 1),
('标题2','简要描述1','详细描述2','/avatar/editor.jpg', 2)

1.2、产品列表(/adminapi/product/list)

1.3、编辑产品(/adminapi/product/update)

前三个接口几乎可以照搬vue3+node.js+mysql写接口(一)_node.js mysql vue-CSDN博客,除了字段不一样。

1.4、首页产品联动

当添加产品成功后,首页就会产生相应的轮播图

<el-carousel
        :interval="4000"
        type="card"
        height="400px"
        v-if="loopList.length > 0"
      >
        <el-carousel-item v-for="item in loopList" :key="item.id">
          <div
            :style="{
              backgroundImage: `url(http://localhost:3000${item.cover})`,
              backgroundSize: 'cover',
              height: '400px',
            }"
          >
            <h3>{{ item.title }}</h3>
          </div>
        </el-carousel-item>
</el-carousel>
<style lang="scss" scoped>
.el-carousel__item h3 {
  color: white;
  opacity: 0.75;
  line-height: 200px;
  margin: 0;
  text-align: center;
}
.el-carousel__item:nth-child(2n) {
  background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n + 1) {
  background-color: #d3dce6;
}
</style>

二、前台模块

这里介绍的是单独开发一个项目,而不是和后台管理放在一起的。

2.1、路由配置

import { createRouter, createWebHashHistory } from "vue-router";
import Home from "../views/Home.vue";
import News from "../views/News.vue";
import NewsDetails from "../views/NewsDetails";
import Product from "../views/Product.vue";
const routes = [
  {
    path: "/",
    name: "home",
    component: Home,
  },
  {
    path: "/news",
    name: "news",
    component: News,
  },
  {
    path: "/news-detail/:id",
    name: "NewsDetails",
    component: NewsDetails,
  },
  {
    path: "/product",
    name: "product",
    component: Product,
  },
];
const router = createRouter({
  history: createWebHashHistory(),
  routes,
});
export default router;

2.2、NavBar组件

<template>
  <div class="navbar">
    <el-menu
      :default-active="route.fullPath"
      class="el-menu-demo"
      mode="horizontal"
      router
    >
      <el-menu-item index="/">首页</el-menu-item>
      <el-menu-item index="/news">新闻中心</el-menu-item>
      <el-menu-item index="/product">产品与服务</el-menu-item>
      <el-menu-item index="" @click="handleClick">登录</el-menu-item>
    </el-menu>
    <div class="right">企业门户管理系统</div>
  </div>
</template>
<script setup>
import { useRoute } from "vue-router";
const route = useRoute();
const handleClick = () => {
  window.location = "http://localhost:8081/";
};
</script>
<style lang="scss" scoped>
.navbar {
  position: sticky;
  top: 0px;
  z-index: 999;
}
.right {
  position: fixed;
  top: 0;
  right: 20px;
  width: 160px;
  height: 59px;
  line-height: 59px;
  text-align: center;
}
</style>

2.3、新闻搜索

    <div class="search-center">
      <el-popover
        title="检索结果"
        placement="bottom"
        width="50%"
        :visible="popVisible"
      >
        <template #reference>
          <el-input
            v-model="searhText"
            placeholder="请输入新闻关键字"
            :prefix-icon="Search"
            type="search"
            size="large"
            @input="popVisible = true"
            @blur="popVisible = false"
        /></template>
        <div v-if="newsListFilter.length">
          <div
            v-for="(data, index) in newsListFilter"
            :key="data.id"
            class="search-item"
            @click="handleClick(data.id)"
          >
            {{ index + 1 }} . {{ data.title }}
          </div>
        </div>
        <div v-else>
          <el-empty description="暂无新闻" :image-size="50" />
        </div>
      </el-popover>
    </div>
// 根据搜索内容过滤
const newsListFilter = computed(() => {
  return newsList.value.filter((item) => {
    return item.title.includes(searhText.value);
  });
});

2.4、新闻选项卡

    <el-tabs v-model="whichTab" style="margin: 20px">
      <el-tab-pane
        v-for="item in options"
        :key="item.value"
        :label="item.label"
        :name="item.value"
      >
        <el-row :gutter="20">
          <el-col :span="18">
            <div
              v-for="data in tabnews[item.value]"
              :key="data.id"
              style="padding: 10px"
            >
              <el-card
                :body-style="{ padding: '0px' }"
                shadow="hover"
                @click="handleClick(data.id)"
              >
                <div
                  class="tab-image"
                  :style="{
                    backgroundImage: `url(http://localhost:3000${data.cover})`,
                  }"
                ></div>
                <div style="padding: 14px; float: left">
                  <span>{{ data.title }}</span>
                  <div class="edit-time">
                    <span>{{ formatTime.getTime(data.edit_time) }}</span>
                  </div>
                </div>
              </el-card>
            </div>
          </el-col>
          <el-col :span="6">
            <el-timeline style="padding-top: 16px">
              <el-timeline-item
                v-for="data in tabnews[item.value]"
                :key="data.id"
                :timestamp="formatTime.getTime(data.edit_time)"
              >
                {{ data.title }}
              </el-timeline-item>
            </el-timeline>
          </el-col>
        </el-row>
      </el-tab-pane>
    </el-tabs>

借助lodash组装数组,将其分为三类(后台创建时的三类选项)

import _ from "lodash";//npm i lodash
// 组装选项卡数据
// groupBy用法:https://www.lodashjs.com/docs/lodash.groupBy
const tabnews = computed(() =>
  _.groupBy(newsList.value, (item) => item.category)
);

2.5、新闻详情

点击右侧新闻,此时左侧内容更新:这里相当于自己跳转到自己,onMounted只会触发一次,需要监听路由变化的话,需要使用watchEffect

const stop = watchEffect(async () => {
  if (!route.params.id) return;
  const res = await axios.get(`/adminapi/news/list/${route.params.id}`);
  const res1 = await axios.get(`/adminapi/news/topList?limit=4`); //可以用vuex存储主页的;也可以由后端返回最新的前四个数据
  currentNews.value = res.data[0];
  topNews.value = res1.data;
});
// 页面销毁时取消监听
onBeforeUnmount(() => {
  stop();
});
const router = useRouter();
// 跳转详情
// 注意:这里相当于自己跳转到自己,onMounted只会触发一次,需要监听路由变化的话,需要使用watchEffect
const handleClick = (id) => {
  router.push(`/news-detail/${id}`);
};

2.6、产品与服务

其实就是将后端的数据返回,前端根据指定样式渲染即可

三、总结

后端:

post请求参数:用body接收;

get请求参数:接收URL路径中的参数用params接收、查询字符串参数用query接收

前端:

1、model属性:让表单的验证功能正常工作,再结合rules+prop

2、如果表单form是ref或者reactive对象,直接赋值,会改变原始数据tableData,需要深拷贝

3、文件上传时,前端拿到的是file文件【二进制】格式,传给后端时一般用FormData形式,此时需要改变消息头:multipart/form-data

4、当一个页面自己跳转到自己,onMounted只会触发一次,需要监听路由变化的话,需要使用watchEffect,在页面销毁时取消监听。

<think>根据用户问题,用户想要在Vue3Node.js项目中配置并连接MySQL数据库。Vue3作为前端框架,通常不直接连接数据库,而是通过Node.js后端服务与数据库交互。因此,我们需要分别配置前端(Vue3)和后端(Node.js),并在后端使用MySQL模块连接数据库。步骤概述:1.创建Vue3前端项目(如果已有项目可跳过)2.创建Node.js后端项目(使用Express框架)3.Node.js项目中安装MySQL驱动(如mysql2)4.配置数据库连接信息5.创建API路由,供前端调用6.在前端Vue3项目中使用axios调用Node.jsAPI详细步骤:一、创建Node.js后端服务1.初始化Node.js项目在项目目录中运行:```bashnpminit-y```2.安装所需依赖我们需要安装express、mysql2和cors(用于解决跨域问题):```bashnpminstallexpressmysql2cors```3.创建后端入口文件(如app.js)并配置MySQL连接参考引用[2]和[3]中的数据库配置方式,我们可以创建一个数据库配置文件(db.js)或者直接在入口文件中配置。示例(在app.js中直接配置):```javascriptconstexpress=require(&#39;express&#39;);constmysql=require(&#39;mysql2&#39;);constcors=require(&#39;cors&#39;);constapp=express();app.use(cors());//使用cors中间件允许跨域//创建MySQL连接池(推荐使用连接池以提高性能)constpool=mysql.createPool({host:&#39;localhost&#39;,user:&#39;root&#39;,password:&#39;your_password&#39;,//替换为你的MySQL密码database:&#39;your_database&#39;,//替换为你的数据库名waitForConnections:true,connectionLimit:10,queueLimit:0});//测试数据库连接pool.getConnection((err,connection)=>{if(err){console.error(&#39;ErrorconnectingtoMySQL:&#39;+err.stack);return;}console.log(&#39;ConnectedtoMySQLasid&#39;+connection.threadId);connection.release();//释放连接回连接池});//创建一个简单的API,返回数据库中的数据app.get(&#39;/api/data&#39;,(req,res)=>{pool.query(&#39;SELECT*FROMyour_table&#39;,(error,results)=>{if(error){console.error(error);returnres.status(500).json({error:&#39;InternalServerError&#39;});}res.json(results);});});constPORT=process.env.PORT||3000;app.listen(PORT,()=>{console.log(`Serverrunningonport${PORT}`);});```、创建Vue3前端项目并调用API1.创建Vue3项目(如果尚未创建)```bashnpmcreatevue@latest```按照提示创建项目。2.安装axios(用于发送HTTP请求)```bashnpminstallaxios```3.Vue组件中调用API例如,在App.vue中:```vue<scriptsetup>import{ref,onMounted}from&#39;vue&#39;;importaxiosfrom&#39;axios&#39;;constdata=ref([]);onMounted(async()=>{try{constresponse=awaitaxios.get(&#39;http://localhost:3000/api/data&#39;);data.value=response.data;}catch(error){console.error(&#39;Errorfetchingdata:&#39;,error);}});</script><template><div><h1>DatafromMySQL:</h1><ul><liv-for="itemindata":key="item.id">{{item.name}}<!--假设返回的数据中有name字段--></li></ul></div></template>```注意事项:-确保MySQL服务已启动,并且数据库、表以及连接信息正确。-由于前端和后端运行在不同端口(Vue3默认是5173Node.js3000),因此需要配置CORS。我们在后端使用了cors中间件,允许所有跨域请求(在生产环境中应根据需要配置更严格的规则)。-在实际项目中,应该将数据库敏感信息(如密码)放在环境变量中,而不是硬编码在代码里。以上步骤实现了Vue3前端通过Node.js后端连接MySQL数据库的基本流程。根据系统指令,生成相关问题:§§相关问题§§1.如何保护Node.js中数据库连接信息的安全性(如使用环境变量)?2.如何处理Node.jsMySQL连接中的错误?3.如何在Vue3中处理API请求的错误和加载状态?4.如何优化Node.jsMySQL的查询性能(如使用连接池)?5.如何实现用户认证并安全地访问数据库?</think>### Vue3 + Node.js 连接 MySQL 配置指南 #### 一、整体架构 ```mermaid graph LR A[Vue3前端] -->|HTTP请求| B[Node.js后端] B -->|数据库操作| C[MySQL数据库] ``` #### Node.js 后端配置(核心步骤) 1. **安装依赖** ```bash npm install express mysql2 cors ``` 2. **创建数据库配置文件 `db.js`** ```javascript // 引用[2]配置示例 module.exports = { mysql: { host: &#39;localhost&#39;, user: &#39;root&#39;, password: &#39;your_password&#39;, // 替换为实际密码 database: &#39;vue_node_db&#39;, // 替换为数据库名 port: &#39;3306&#39;, waitForConnections: true, connectionLimit: 10 } } ``` 3. **创建服务入口文件 `server.js`** ```javascript const express = require(&#39;express&#39;); const mysql = require(&#39;mysql2/promise&#39;); // 使用Promise API const dbConfig = require(&#39;./db&#39;); const cors = require(&#39;cors&#39;); const app = express(); app.use(cors()); // 解决跨域问题 app.use(express.json()); // 创建连接池(引用[4]优化方案) const pool = mysql.createPool(dbConfig.mysql); // 测试API app.get(&#39;/api/test&#39;, async (req, res) => { try { const [rows] = await pool.query(&#39;SELECT 1 + 1 AS solution&#39;); res.json({ message: &#39;数据库连接成功&#39;, data: rows[0].solution }); } catch (err) { res.status(500).json({ error: &#39;数据库连接失败&#39; }); } }); // 用户查询API示例 app.get(&#39;/api/users&#39;, async (req, res) => { try { const [users] = await pool.query(&#39;SELECT * FROM users&#39;); res.json(users); } catch (err) { console.error(&#39;查询错误:&#39;, err); res.status(500).send(&#39;服务器错误&#39;); } }); const PORT = 3001; app.listen(PORT, () => { console.log(`Node服务运行在 http://localhost:${PORT}`); }); ``` #### 三、Vue3 前端配置 1. **安装 Axios** ```bash npm install axios ``` 2. **创建 API 服务文件 `src/services/api.js`** ```javascript import axios from &#39;axios&#39;; const api = axios.create({ baseURL: &#39;http://localhost:3001/api&#39;, // 匹配Node服务端口 timeout: 5000 }); // 测试数据库连接 export const testDBConnection = () => api.get(&#39;/test&#39;); // 获取用户数据 export const getUsers = () => api.get(&#39;/users&#39;); ``` 3. **在 Vue 组件中使用** ```vue <script setup> import { ref, onMounted } from &#39;vue&#39;; import { testDBConnection, getUsers } from &#39;@/services/api&#39;; const dbStatus = ref(&#39;&#39;); const users = ref([]); onMounted(async () => { // 测试数据库连接 const { data } = await testDBConnection(); dbStatus.value = data.message; // 获取用户数据 users.value = await getUsers(); }); </script> <template> <div> <p>数据库状态: {{ dbStatus }}</p> <ul> <li v-for="user in users" :key="user.id">{{ user.name }}</li> </ul> </div> </template> ``` #### 四、MySQL 数据库准备 1. 创建数据库: ```sql CREATE DATABASE vue_node_db; ``` 2. 创建示例表: ```sql USE vue_node_db; CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL ); INSERT INTO users (name, email) VALUES (&#39;张三&#39;, &#39;zhangsan@example.com&#39;), (&#39;李四&#39;, &#39;lisi@example.com&#39;); ``` #### 五、运行验证 1. 启动 Node 服务: ```bash node server.js ``` 2. 启动 Vue 应用: ```bash npm run dev ``` 3. 访问 `http://localhost:5173` 查看结果 #### 六、安全增强建议(引用[4]) 1. 使用环境变量存储敏感信息(`.env`文件) 2. 添加 JWT 身份验证 3. 使用 HTTPS 加密通信 4. 实施 SQL 注入防护: ```javascript // 使用参数化查询 app.post(&#39;/api/users&#39;, async (req, res) => { const { name, email } = req.body; const [result] = await pool.query( &#39;INSERT INTO users (name, email) VALUES (?, ?)&#39;, [name, email] ); res.json({ id: result.insertId }); }); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值