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(DbContextOptionsoptions) : base(options) { //参数DbContext选项值创建的这个新的类型,包装后用base关键字传给父类 } public DbSet 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(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> GetList()//有async定义的异步编程,不过有没有返回值都必须Task { return await db.Categories.ToListAsync();//有async异步定义的方法,就必须有await来执行任务。 } [HttpGet("{id}")]//路由模式:api/controller/{id} public async Task > 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 > Add(Category model) { await db.Categories.AddAsync(model);//添加数据 await db.SaveChangesAsync(); return Ok(model.Id); } [HttpDelete("{id}")]//删 public async Task 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 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】组件下请求数据即可
<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 做添加和修改
<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>
数据页代码如下
商品分类添加分类 修改 删除 <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 //数组}