vue3+node.js:一个基础入门的全栈CURD模块

✨该模块搭建承接上一个笔录:Vue3+Node.js

前端:vue3+element-plus+axios+vite

../utils/http.js

javascript 复制代码
// axios 二次封装
// 1、导入核心库
import axios from "axios";
// 2、配置路径
axios.defaults.baseURL = 'http://localhost:3000'
// axios.defaults.withCredentials = true
// 3、请求拦截器
axios.interceptors.request.use(config =>config);
// 4、响应拦截器
axios.interceptors.response.use(
    res =>{
        if(res.status == 200){
            return res.data
        }

        return res;
    },
    error=>Promise.reject(error)
);
// 5、导出
export default axios;

home.vue

html 复制代码
<script setup>
import {  onMounted,reactive,ref} from 'vue'
import {useRouter} from 'vue-router'
import  axios from "../utils/http.js"
import HelloWorld from '../components/HelloWorld.vue'

const router = useRouter()
const ruleFormRef = ref()
const ruleForm = reactive({
  product_name: '',
  product_price: '',
})
const tableData2 = ref([]);
const edit_id = ref('')

function goPage(){
    router.push('/about?name=Jen');
}
onMounted(() => {
  // 首次获取数据,渲染列表
  onGetData()
})
/**
 * 获取数据,渲染列表
 */
const onGetData = async() => {
  try {
    const {code,list} = await axios.get('/api/getInfo');
    if(code == 200){
      // 0 从数组的第 0 个元素开始操作。
      // tableData.length 清空数组中所有的旧数据。
      // ...list 把新数据填进去。
      //  tableData.splice(0, tableData.length, ...list)
      //  替换方案 建议用tableData.value = list 形式,能直接赋值
       tableData2.value = list
    }
  } catch (error) {
    console.error('获取数据失败:', error);
  }
}
/**
 * 数值规则
 */
const checkNum = (rule, value, callback) => {
  if (value === '') return callback(new Error('Please input the price'))
  if (value <= 0)  callback(new Error('Price must be greater than 0'))
  setTimeout(() => {
    if (isNaN(value)) {
      callback(new Error('Please input digits'))
    } else {
      callback()
    }
  }, 500)
}
/**
 * 表单规则
 */
const rules = reactive({
  product_name: [
    { required: true, message: 'Please input product name', trigger: 'blur' },
    { min: 1, max: 10, message: 'Length should be 1 to 10', trigger: 'blur' },
  ],
  product_price: [{ required: true,validator: checkNum, trigger: 'blur' }],
})

/**
 * 提交表单
 * 添加/删除操作
 */
const submitForm = (formEl) => {
  if (!formEl) return
  formEl.validate(async (valid) => {
    if (valid) {
      // 保留两位小数 浮点数四舍五入 位数不够 不补0
      ruleForm.product_price = Math.round(ruleForm.product_price * Math.pow(10, 2))/Math.pow(10, 2);  
      try {
        let res_code = 0;
        if(edit_id.value){
            const {code} = await axios.post('/api/editInfo',{id:edit_id.value,...ruleForm});
            res_code = code
        }else{
            const {code} = await axios.post('/api/addInfo',ruleForm);
            res_code = code
        }
        if(res_code == 200){
            resetForm(ruleFormRef.value)
            onGetData()
        }

      } catch (error) {
        console.error('提交数据失败:', error);
      }
    } else {
      console.log('error submit!')
    }
  })
}

/**
 * 重置表单
 */
const resetForm = (formEl) => {
  if (!formEl) return
  formEl.resetFields()
  edit_id.value = ""
}

/**
 * 修改功能
 * 编辑表单某条数据
 */
function editFun(row){
  edit_id.value = row.id
  Object.assign(ruleForm, {product_name:row.product_name,product_price:row.product_price})

}
/**
 * 删除功能
 * 删除表单某条数据
 */
async function delFun(id){
  const {code} = await axios.post('/api/delInfo',{id});
  if(code == 200) onGetData()
}
</script>

<template>
    <div>
        <el-form
          ref="ruleFormRef"
          style="max-width: 600px"
          :model="ruleForm"
          status-icon
          :rules="rules"
          label-width="auto"
          class="demo-ruleForm"
        >
            <el-form-item label="商品名称" prop="product_name">
              <el-input v-model="ruleForm.product_name" />
            </el-form-item>
            <el-form-item label="商品价格" prop="product_price">
              <el-input v-model.number="ruleForm.product_price" />
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="submitForm(ruleFormRef)">
                {{edit_id?'修改':'添加'}}
              </el-button>
              <el-button @click="resetForm(ruleFormRef)">重置</el-button>
            </el-form-item>
        </el-form>

        <div>
            <el-table :data="tableData2" border style="width: 100%">
              <el-table-column prop="id" label="id" />
              <el-table-column prop="product_name" label="商品名称" />
              <el-table-column prop="product_price" label="商品价格" />
              <el-table-column fixed="right" label="操作" min-width="120">
                <template #default="scope">
                  <el-button type="primary" @click.prevent="editFun(scope.row)">修改</el-button>
                  <el-button type="danger" @click.prevent="delFun(scope.row.id)">删除</el-button>
                </template>
              </el-table-column>
            </el-table>
        </div>
    </div>
</template>

后端:koa+@koa/router+mysql2

package.json(CommonJS)

javascript 复制代码
{
  "name": "nodeserve",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "dev": "nodemon server.js",
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "commonjs",
  "dependencies": {
    "@koa/router": "^15.4.0",
    "koa": "^3.2.0",
    "koa-bodyparser": "^4.4.1",
    "koa-static": "^5.0.0",
    "koa2-cors": "^2.0.6",
    "mysql2": "^3.22.2"
  }
}

server.js (CommonJS)

javascript 复制代码
// koa ------ 基于Node.js平台的下一代web开发框架
// 运用node.js中的koa框架创建一个http服务器

// "type": "commonjs",
//引入koa模块
const Koa = require('koa'); 
// 引入路由库
const Router = require('@koa/router');
// 引入处理跨域组件
const cors = require('koa2-cors');
// 引入处理请求体解析
const BodyParser = require('koa-bodyparser'); 
// 使用promise版本
const mysql = require('mysql2/promise');

// 实例化
const app = new Koa();  //创建应用程序实例
const router = new Router({prefix:'/api'});  //实例化一个Router对象,并设置一个路由访问前缀
const bodyparser = new BodyParser();


// koa请求跨域配置(注意:一定要在路由使用前设置)
app.use(
    cors({
        origin:function(ctx){  //指定允许跨域访问的源。
            // return ctx.request.headers.origin || "";  //动态获取地址  //当 credentials: true 时,不能设置为 *,必须是具体的域名。
        
        // 白名单
        const whiteList = ['http://localhost:5173', 'https://www.yourdomain.com'];
        const requestOrigin = ctx.request.header.origin;
        
        // 如果请求源在白名单内,则返回该请求源;否则不允许跨域
        if (whiteList.includes(requestOrigin)) {
            return requestOrigin;
        }
        return false; 

        // 如果想允许所有域名并且携带cookie,可以直接返回请求源(不安全,仅用于开发环境)
        // return requestOrigin || '*';
        
        },
        credentials:true,
        // exposeHeaders:["WWW-Authenticate","Server-Authorization"],
        allowMethods:["GET","POST","PUT","DELETE","OPTIONS"],
        allowHeaders:["Content-Type","Authorization","Accept"]
    })
)
app.use(bodyparser);

// 加载路由
app.use(router.routes()).use(router.allowedMethods());

// 创建数据库连接池
const pool = mysql.createPool({
    host:'localhost',
    user:'item01DB',
    password:'item01DB',
    database:'item01DB'  //要连接的数据库名
})
// 测试连接
pool.getConnection()
    .then(conn => {
        console.log('数据库连接成功');
        conn.release();  //释放连接
    })
    .catch(err => {
        console.log('数据库连接失败',err);
        
    })
// 查询接口
router.get('/getInfo',async (ctx)=> {
    // 数据库查询数据
    const [rows] = await pool.execute('select * from product');
    ctx.body = {
        code:200,
        message:'success',
        list:rows
    }
})
// 添加接口
router.post('/addInfo',async (ctx) => {
    // 接受前端传的参数
    let {product_name,product_price} = ctx.request.body;

    // 操作数据库
    await pool.execute(
        'insert into product (product_name,product_price) values (?,?)',
        [product_name,product_price]
    )
    ctx.body = {
        code:200,
        message:'success'
    }
})
// 修改接口
router.post('/editInfo',async (ctx) => {
    // 接受前端传的参数
    let {product_name,product_price,id} = ctx.request.body;

    // 操作数据库
    await pool.execute(
        'update product set product_name = ?,product_price = ? where id = ?',
        [product_name,product_price,id]
    )
    ctx.body = {
        code:200,
        message:'success'
    }
})
// 删除接口
router.post('/delInfo',async (ctx) => {
    // 接受前端传的参数
    let {id} = ctx.request.body;

    // 操作数据库
    await pool.execute(
        'delete from product where id = ?',
        [id]
    )
    ctx.body = {
        code:200,
        message:'success'
    }
})



//启动成功,访问端口号3000
app.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

package.json(ES Module)

javascript 复制代码
{
  "name": "nodeserve",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "dev": "nodemon server.js",
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "module",
  "dependencies": {
    "@koa/router": "^15.4.0",
    "koa": "^3.2.0",
    "koa-bodyparser": "^4.4.1",
    "koa-static": "^5.0.0",
    "koa2-cors": "^2.0.6",
    "mysql2": "^3.22.2"
  }
}

server.js (ES Module)

javascript 复制代码
// // koa ------ 基于Node.js平台的下一代web开发框架
// // 运用node.js中的koa框架创建一个http服务器


// // "type": "module",
// //引入koa模块
// import Koa from 'koa';
// const app = new Koa();  //创建应用程序实例

// // 引入路由库
// import Router from "@koa/router";
// const router = new Router();  //实例化一个Router对象

// // 设置默认端口号和主机名
// const hostname = '127.0.0.1';
// const port = 1823;

// // ------GET 请求
// router.get('/',async ctx => {
//     ctx.body = "测试get请求";
// })

// // ------GET 请求 http://localhost:1823/test?id=001&web=testapi
// router.get('/test',async ctx => {
//     let id = ctx.query.id;
//     let web = ctx.query.web;
//     ctx.body = id + ":" + web;
// })

// app.listen(port,hostname,()=>{
//     console.log(`服务器已启动:http://${hostname}:${port}`);
    
// })

// // 添加中间件
// app.use(async ctx => {
//     ctx.body = "Hello World3";
// });

// //启动成功,访问端口号3000
// // app.listen(3000, () => {
// //   console.log('Server is running on http://localhost:3000');
// // });
相关推荐
zhensherlock2 小时前
Protocol Launcher 系列:Working Copy 提交与同步全攻略
javascript·git·typescript·node.js·自动化·github·js
无巧不成书021818 小时前
2026最新Next-AI-Draw-io全攻略:AI驱动专业图表生成,Docker/Node.js本地部署零踩坑指南
人工智能·docker·node.js·next-ai-draw-io
悟空瞎说1 天前
我踩过的4个Node.js微服务经典Bug,用一个库彻底解决(2000字详解+可直接复用代码)
后端·node.js
捉鸭子1 天前
某红书X-s X-s-common VMP逆向(算法还原)
python·web安全·网络安全·node.js·网络爬虫
freewlt1 天前
Node.js 性能分析实战指南:从入门到精通
node.js
火乐暖阳851051 天前
Vue3+Node.js
vue.js·node.js·pnpm·koa2·myslq2
被巨款砸中1 天前
从零到一:构建高效 Node.js 后端 API (Koa + Prisma 实战)
前端·javascript·vue.js·node.js·web
网络点点滴1 天前
Node.js-填充模板
node.js
zhensherlock2 天前
Protocol Launcher 系列:Trello 看板管理的协议自动化
前端·javascript·typescript·node.js·自动化·github·js