当前位置: 首页 > news >正文

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

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

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

../utils/http.js

// 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

<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)

{ "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)

// 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)

{ "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)

// // 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'); // // });
http://www.jsqmd.com/news/697384/

相关文章:

  • 2026年查重率过高别慌!高效降重实用方法收藏 - 降AI实验室
  • 淡斑防晒淡化新生色斑防晒推荐,怕晒出色斑?Leeyo 防晒来守护 - 全网最美
  • 在树莓派4B(ARM64)上搞定PyQt5:从源码编译到解决Qt::ItemDataRole报错的全过程
  • 天虹提货券离得太远不方便用?可以这样处理 - 抖抖收
  • 3步彻底清理显卡驱动:Display Driver Uninstaller完全指南
  • 2026年全国大型一比一仿真模型定制指南:工业机械、航空航天、展览展示完整选购手册 - 企业名录优选推荐
  • 别再说零基础学不了网安!电脑小白专属 4 阶段入门路线
  • 手把手教你用Matlab R2022a和CCS 12.0给C2000 F28035点灯(附常见报错解决)
  • 科研效率翻倍:我是如何用Python脚本把Tafel数据处理时间从2小时压缩到5分钟的
  • 别再乱用push_back了!C++11后,emplace_back才是vector插入的正确姿势(附性能对比)
  • VCS/irun仿真效率提升:如何用UCLI和TCL脚本灵活控制fsdb波形记录?
  • 永辉超市卡附近没有门店怎么办?教你如何处理 - 抖抖收
  • 告别MAC冲突!手把手教你用RKDevInfoWriteTool V1.1.4正确设置RK3566以太网地址
  • 贵阳南明区2026年招聘潮:销售、客服、运营岗位为何持续火爆? - 年度推荐企业名录
  • real-anime-z部署实战:Xinference+Gradio一键生成真实系动漫图
  • 别再傻傻分不清了!一文讲透OPC UA和OPC DA到底差在哪(附选型建议)
  • 国内主流 AI模型及衍生品
  • 超越Arduino_GFX:在ESP-IDF中用面向对象思想重构ST7701S SPI驱动
  • UWB定位进阶:如何利用DW1000的CIR数据做NLOS信号识别?
  • 聊一聊!2026国内靠谱锡条锡膏锡渣回收公司 - 大风02
  • WSL 下使用 Claude Code Router 将 VS Code Claude Code 指向 AWS Bedrock GLM-5 模型
  • 如何用大气层Atmosphere解锁Switch隐藏潜能:从新手到高手的完整路线图
  • 基于TinyEMU的RISC-V指令集验证实战(一)
  • 从游戏加载到数据库响应:为什么你的SSD需要关注99.9%延迟?一个真实场景的性能解读
  • 速度即护城河:AMD GPU 上的推理性能
  • ESP8266 I2C通信避坑指南:从SHT30读取失败到BH1750数据不准的常见问题排查
  • 明景裕达祥贴隐形车衣靠谱吗,客户案例来证明 - 工业品网
  • 白世贸花岗岩源头厂家怎么选?靠谱供应商筛选攻略来了 - 匠言榜单
  • 信创即时通讯怎么选?三个标准帮你判断
  • 修好三个老旧电源适配器后,我总结的12V开关电源常见故障排查指南(附实物图对照)