前端用:vue3,后端:net6,的结合代码
vue3手动创建:勾选1、路由配置:router。2、vuex状态管理:store类似全局变量。3、在加上UI库:element-plus模板
大致UI库模板如下:安装ui库命令:npm install element-plus --save,UI库图标命令:npm install @element-plus/icons-vue
App.vue
CategoryView.vue
import { createApp } from 'vue' //框架实例 import App from './App.vue' import router from './router' //路由 import store from './store' //状态管理vuex--类似全局变量 import ElementPlus from 'element-plus'//ui库 import 'element-plus/dist/index.css' //ui库的样式 import * as ElementPlusIconsVue from '@element-plus/icons-vue'//ui库图标 const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component)//注册icon图标,ui库图标 } app.use(store).use(router).use(ElementPlus).mount('#app')main.js入口的挂载
{ path:'/category', name:'category', component:()=>import('../views/CategoryView.vue') }index.js路由的配置
一、后台webAPI的搭建
创建asp.net core web api项目:搭建数据库连接
1.创建【Models】文件夹保存实体类:Category.cs
public class Category { public int Id { get; set; } public string Name { get; set; } }
2.创建【Data】文件夹保存数据库上下文类:ShopDbContext.cs
using Microsoft.EntityFrameworkCore; using ShoopingWeb.Models; namespace ShoopingWeb.Data { public class ShopDbContext:DbContext { public ShopDbContext(DbContextOptions<ShopDbContext> options) : base(options) { //参数DbContext选项值创建的这个新的类型,包装后用base关键字传给父类 } public DbSet<Category> Categories { get; set; }//添加表 } }
3.在【appsettings.json】文件中配置数据库连接字符串,这里用的vs自带数据库
"ConnectionStrings": { "ShopConn": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=OA;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" }
4.把数据库添加到服务全局【Program.cs】
builder.Services.AddDbContext<ShopDbContext>(option => { //添加数据库上下文类 option.UseSqlServer(builder.Configuration.GetConnectionString("ShopConn"));//拿到数据库连接字符串,在appsettings.json配置文件里 });
5.工具-》nuget包管理-》控制台:1.执行数据库迁移:add-migration initDb 2.保存到数据库:update-database 手打
6.在【Controllers】文件夹下创建控制器:CategoryController.cs ,用的是RESTfull开发风格:路由+http请求=方法体定义的api
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using ShoopingWeb.Data; using ShoopingWeb.Models; namespace ShoopingWeb.Controllers { [Route("api/[controller]")]//路由:api/控制器名,这里是Category别写错哟 [ApiController]//控制器是webapi public class CategoryController : ControllerBase { private readonly ShopDbContext db; public CategoryController(ShopDbContext db)//鼠标右键快速生成字段并赋值 { this.db = db;//绑定数据库ShopDbContext上下文类 } [HttpGet]//在webapi中,是通过路由+http请求【特性】来找到方法的,和方法名无关,无调用只有注释的作用。 public async Task<IEnumerable<Category>> GetList()//有async定义的异步编程,不过有没有返回值都必须Task { return await db.Categories.ToListAsync();//有async异步定义的方法,就必须有await来执行任务。 } [HttpGet("{id}")]//路由模式:api/controller/{id} public async Task<ActionResult<Category>> Getid(int id) { var category = await db.Categories.FindAsync(id);//通过id查询 if (category == null) { return NotFound();//返回404的状态码,多种返回类型用:ActionResult } return Ok(category);//返回数据 } [HttpPost]//增 public async Task<ActionResult<int>> Add(Category model) { await db.Categories.AddAsync(model);//添加数据 await db.SaveChangesAsync(); return Ok(model.Id); } [HttpDelete("{id}")]//删 public async Task<IActionResult> Delete(int id) { var category = await db.Categories.FindAsync(id); if (category == null) { return NotFound(); }//返回404,都是返回状态码,用接口即可:IActionResult db.Categories.Remove(category);//删除数据不需要异步 await db.SaveChangesAsync();//保存需要异步 return NoContent();//返回204,成功删除 } [HttpPut]//改 public async Task<IActionResult> Update(Category model) { var category = await db.Categories.FindAsync(model.Id); if (category == null) { return NotFound(); }//返回404 category.Name = model.Name; await db.SaveChangesAsync();//保存修改的数据 return NoContent();//返回204,成功修改 } } }
测试:直接运行后台代码,可以看出后台其实返回的就是josn字符串和状态码而已
二、前后端CORS跨域配置
在Ajax请求时遵循的是同源策略:协议相同,域名相同,端口相同。而在webapi中前后端分离的,端口肯定不一样。所以不能在使用Ajax来发送请求。
CORS(Cross-orgin-resource-sharing)跨域资源共享:打破同源策略的限制。Ajax为了安全考虑做的限制,打破限制的方案有很多,cors最常见。
后台:服务端的配置【Program.cs】,开放运行策略
builder.Services.AddCors(options => //添加跨域服务 { options.AddPolicy("cors", p =>//添加策略,可以添加多种 { //如果是允许指定的域、方法、消息头需要使用WithOrigins、WithMethods、WithHeaders方法。 p.AllowAnyOrigin()//允许可以,参数可以给ip,不给表示允许所有 .AllowAnyMethod() //允许所有方法 .AllowAnyHeader();//请求头 }); });
app.UseCors("cors");//启用策略中间件管道,必须放跳转:app.UseHttpsRedirection();的后面
前台:客户端vue3的框架配置文件【vue.config.js】,每次修改此文件需要重启项目,Ctrl+c项目关闭,重启命令:npm run serve
const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({//会覆盖webpack默认配置 transpileDependencies: true, devServer:{//开发环境的服务器配置 open:true,//是否自动打开浏览器 host:"localhost", port:8080,//修改默认端口 proxy:{//通过代理的方式访问,会重写路由 "/api":{ target:"https://localhost:7135/api/",//服务器请求地址,在后台Properties--》launchSettings.json中 secure:false,//HTTPS需要配置这个参数 changeOrigin:true,//请求头host属性,默认false发本机ip。true会把host设置为target的值。 pathRewrite:{'^/api':''}//路径重写,(正则表达式)识别api路径替换为空字符串。 } } } })
三、获取后台数据【axios请求库】
安装命令:npm install axios ,请求库还有很多,axios只是vue3的一种
vue3前端src目录下创建【api】文件夹放配置文件:api_config.js
import axios from "axios" //请求库 axios.defaults.baseURL="http://localhost:8080/api" //基础路径,做请求前缀。 axios.defaults.headers['X-Requested-With']="XMLHttpRequest" //请求类型:异步请求 axios.defaults.headers.post['Content-Type']='application/json' //post以json格式提交到后台 export default axios;
获取后台所有数据:在【views】试图中【CategoryView.vue】组件下请求数据即可
<template> <el-card class="box-card"> <template #header> <div class="card-header"> <span>商品分类</span> <el-button type="primary" icon="CirclePlus" round>添加分类</el-button> </div> </template> <el-table :data="tableData.list" stripe style="width: 100%"> <el-table-column prop="id" label="Id" width="180" /> <el-table-column prop="name" label="名称" width="180" /> <el-table-column fixed="right" label="操作" width="180"> <template #default> <el-button type="success" size="small">修改</el-button> <el-button type="danger" size="small">删除</el-button> </template> </el-table-column> </el-table> </el-card> </template> <script setup> import {reactive,onMounted} from 'vue' //vue里面要定义变量,需要导入reactive这个方法 import axios from '@/api/api_config';//请求库,@符号表示src文件夹 const tableData = reactive({list:[]})//reactive也可以定义一个对象list onMounted(()=>{ //类似于后台构造方法,初始化时调用 getList() }) const getList=()=>{//获取数据信息 return axios.get('/Category').then((res)=>{//get得的后台控制器的数据,返回结果用then方法获取。 tableData.list=res.data console.log(res.data)//打印到控制台 }) } </script>
增删改:在【components】创建弹窗组件:AddCategory.vue 做添加和修改
<template> <el-dialog v-model="state.dialogVisible" :title="dialogTitle" width="35%" :before-close="handleClose" > <el-form :model="ruleForm" label-width="120px"> <el-form-item label="姓名"> <el-input v-model="ruleForm.name" /> </el-form-item> </el-form> <template #footer> <span class="dialog-footer"> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="primary" @click="add"> 保存 </el-button> </span> </template> </el-dialog> </template> <script setup> import {inject, reactive,toRefs,watch} from "vue" //reactive可以定义变量也可以定义对象。 import {ElMessage} from "element-plus" //弹窗 import axios from '@/api/api_config';//请求库 const state =reactive({ dialogVisible:false,//表示不显示对话框,ui库包对话框当做属性来判断了 ruleForm:{id:"",name:""}//对话框的数据,也是修改和添加用 });//如果没有toRef转换,那么必须通过state.dialogVisible这样一层一层的取值 const {dialogVisible,ruleForm} =toRefs(state)//将reactive转为ref对象,也不需要点value取值 const dialogCategory=()=>{//定义一个打开对话框的方法 state.dialogVisible = true;//调用方法就显示对话框 }; //主动暴露子组件方法,这是编译器的宏命令,不需要引入,其他组件就可以使用 defineExpose({dialogCategory})//需要配置文件.eslintrc.js将宏命令打开:"vue/setup-compiler-macros":true在env选项中添加。 const title = defineProps({//自动暴露这个变量 dialogTitle:{type:String},//标题 tableRow:{type:Object}//id }) watch(//监听器,是vue的一个选项,监听数据的变化而变化 ()=>title.tableRow,//需要监听的数据, ()=>{state.ruleForm=title.tableRow},//如果有变化就改变数据。 {//配置 deep:true,//是否深度检测,数据又多层,可以深度查找 immediate:true //立即执行 } ) const getList=inject("getList")//依赖注入刷新页面,传过来的用provide,接收的用inject const add =()=>{ if(title.dialogTitle==="添加数据"){ let param={name:ruleForm.value.name} //let定义的变量在作用于大括号中,出了大括号无用 axios.post('/Category',param).then(()=>{ ElMessage.success("添加成功") getList()//刷新页面数据 state.dialogVisible = false//关闭窗口 }) }else{ let param={ id:title.tableRow.id, //id name:ruleForm.value.name //姓名 } axios.put('/Category',param).then(()=>{ ElMessage.success("修改成功") getList()//刷新页面数据 state.dialogVisible = false//关闭窗口 }) } } </script> <style scoped> .dialog-footer button:first-child { margin-right: 10px; } </style>
数据页代码如下
<template> <el-card class="box-card"> <template #header> <div class="card-header"> <span>商品分类</span> <el-button type="primary" icon="CirclePlus" round @click="handleDialog()">添加分类</el-button> </div> </template> <el-table :data="tableData.list" stripe style="width: 100%"> <el-table-column prop="id" label="Id" width="180" /> <el-table-column prop="name" label="名称" width="180" /> <el-table-column fixed="right" label="操作" width="180"> <template #default="scop"> <el-button type="success" size="small" @click="handleDialog(scop.row)">修改</el-button> <el-button type="danger" size="small" @click="open(scop.row.id)">删除</el-button> </template> </el-table-column> </el-table> </el-card> <AddCategoryVue ref="AddCategory" :dialogTitle="dialogTitle" :tableRow="tableRow"></AddCategoryVue> </template> <script setup> import {reactive,onMounted,ref, provide} from 'vue' //vue里面要定义变量,需要导入reactive这个方法 import axios from '@/api/api_config';//请求库,@符号表示src文件夹 import AddCategoryVue from '@/components/AddCategory.vue'; //子组件,对话框的标签 import { isNull } from '@/utils/filter';//自定义的做数据筛选处理 import { ElMessage, ElMessageBox } from 'element-plus'//删除的消息弹窗 const tableData = reactive({list:[]})//reactive也可以定义一个对象list onMounted(()=>{ //类似于后台构造方法,初始化时调用,自动运行 getList() }) const getList=()=>{//获取数据信息的方法 return axios.get('/Category').then((res)=>{//返回结果用then方法获取。 tableData.list=res.data console.log(res.data)//打印到控制台,测试数据用 }) } provide("getList",getList);//依赖注入:跨组件,把这个方法提供给子组件执行,传过去的用provide,接收的用inject他们两个是一对依赖注入 const AddCategory = ref(null)//定义在标签里的ref属性,当做一个实例,名字就代表了这个对话框组件,就可以用变量去调用他里面的方法了 const dialogTitle =ref("")//弹窗标题 const tableRow =ref({})//修改和删除的id,绑定到标签,传给子组件 const handleDialog=(row)=>{ //打开弹窗的事件 if(isNull(row)){ dialogTitle.value="添加数据" }else{ dialogTitle.value="修改数据" tableRow.value = row //把id传入子组件的弹窗 } AddCategory.value.dialogCategory()//调用子组件的弹窗方法 } const open =(id)=>{ ElMessageBox.confirm('你确定要删除吗?','温馨提示',{ confirmButtonText:'确定', cancelButtonText:'取消', type:'warning', }).then(()=>{ axios.delete(`/Category/${id}`).then(()=>{//这里的符号是反引号波浪线下面 ElMessage({ type:'success', message:'删除成功!', }); getList() //加载数据的方法,刷新数据 }) }).catch(()=>{//捕捉到错误 ElMessage({ type:'info', message:'取消删除!', }) }) } </script>
这里自定义了一个数据过滤器:src项目创建【utils】文件夹:filter.js怎么配置一些数据过滤
export const isNull=(data)=>{ //定义个数据过滤器 if(!data)return true //普通值 if(JSON.stringify(data)==='{}')return true //对象 if(JSON.stringify(data)==='{}')return true //数组 }
标签:WebAPI,axios,vue,const,C#,api,import,id From: https://www.cnblogs.com/longxinyv/p/16969952.html