Spring Boot项目(Vue3+ElementPlus+Axios+MyBatisPlus+Spring Boot前后端分离)

news2025/5/12 13:08:04

下载地址:

前端:https://download.csdn.net/download/2401_83418369/90811402

后端:https://download.csdn.net/download/2401_83418369/90811405

一、前端vue部分的搭建

这里直接看另一期刊的搭建Vue前端工程部分

前端vue+后端ssm项目_vue框架和ssm框架配置-CSDN博客


二、修改项目基础页面

修改App.vue页面

修改HomeView.vue 页面

删除HelloWorld.vue组件,新增一个组件Header

<script >
export default {
  name:"Header"
}
</script>

<template>
  <div style="height: 50px; line-height: 50px; border-bottom: 1px solid #ccc; display:
flex">
    <div style="width: 200px; padding-left: 30px; font-weight: bold; color: dodgerblue">后
      台管理</div>
    <div style="flex: 1"></div>
    <div style="width: 100px">下拉框</div>
  </div>
</template>

<style scoped>

</style>

 创建全局样式global.css

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
不要忘记在main.js文件中导入(导入才能生效) 

导入 ElementPlus并运用

 快速开始 | Element Plus (element-plus.org)

测试一下:添加一个按钮 

增加下拉列表的组件 

<script >
export default {
  name:"Header"
}
</script>

<template>
  <div style="height: 50px;line-height: 50px;border-bottom: 1px solid #ccc;display: flex">
    <div style="width: 200px;padding-left: 30px;font-weight: bold;color:dodgerblue">后台管理</div>
    <div style="flex: 1"></div>
    <div style="width: 100px">
      <el-dropdown>
    <span class="el-dropdown-link">
      tom
      <el-icon class="el-icon--right">
        <arrow-down />
      </el-icon>
    </span>
        <template #dropdown>
          <el-dropdown-menu>
            <el-dropdown-item>个人信息</el-dropdown-item>
            <el-dropdown-item>退出登录</el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown>
    </div>
  </div>
</template>

<style scoped>

</style>

导入到App.vue页面 

<template>
  <div>
<!--    引用组件-->
    <Header/>
    <div style="display: flex">
      <!--    侧边栏:将侧边栏放入盒子里面然后设置盒子的展示是弹性的-->
      <Aside/>
<!--      通过路由展示-->
      <router-view style="flex: 1"/>
    </div>
  </div>
</template>
<style>
</style>
<script>
import Header from "@/components/Header.vue";
import Aside from "@/components/Aside.vue";
export default {
  name:"Layout",
  components:{
    Header,Aside
  }
}
</script>

在HomeView.vue页面添加一个按钮 

添加 增加、查询的组件和一个表格

<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!--  添加按钮和查询框-->
  <div style="margin: 10px 5px;display:inline-block">
    <el-button type="primary">新增</el-button>
    <el-button>其他</el-button>
  </div>
  <div style="display: inline-block">
    <el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
    <el-button type="success">提交</el-button>
  </div>
<!--  表格-->
  <el-table :data="tableData" stripe style="width: 100%">
    <el-table-column sortable prop="date" label="日期" />
    <el-table-column prop="name" label="姓名"  />
    <el-table-column prop="address" label="地址" />
    <el-table-column fixed="right" label="操作" min-width="120">
      <template #default>
        <el-button link type="primary"  @click="handleClick">
          删除
        </el-button>
        <el-button link type="primary">编辑</el-button>
      </template>
    </el-table-column>
  </el-table>
</div>
</template>
 
<script>
// @ is an alias to /src
// import HelloWorld from '@/components/HelloWorld.vue'
 
export default {
  name: 'HomeView',
  components: {
  },
  //数据池的方法
  data(){
    return{
      tableData:[
          {
            date: '2016-05-03',
            name: 'Tom',
            address: 'No. 189, Grove St, Los Angeles',
          },
    {
      date: '2016-05-02',
          name: 'Tom',
        address: 'No. 189, Grove St, Los Angeles',
    },
    {
      date: '2016-05-04',
          name: 'Tom',
        address: 'No. 189, Grove St, Los Angeles',
    },
    {
      date: '2016-05-01',
          name: 'Tom',
        address: 'No. 189, Grove St, Los Angeles',
    },
  ]
    }
  }
}
</script>


三、后端springboot环境搭建

主要是配置pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.study</groupId>
    <artifactId>springboot_furn</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.5.3</version>
    </parent>

    <dependencies>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>




        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.17</version>
        </dependency>

        <!--        引入Mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>


    </dependencies>

</project>

在application.yaml文件 配置数据源和端口

创建启动类 


四、添加家居

完成后台代码从 mapper -> service -> controller , 并对每层代码进行测试 , 到 controller 这一层,使用 Postman 发送 http 请求完成测试

 创建数据库和表

CREATE DATABASE springboot_vue;

USE springboot_vue;


CREATE TABLE furn(
`id` INT(11) PRIMARY KEY AUTO_INCREMENT, ## id
`name` VARCHAR(64) NOT NULL, ## 家居名
`maker` VARCHAR(64) NOT NULL, ## 厂商
`price` DECIMAL(11,2) NOT NULL, ## 价格
`sales` INT(11) NOT NULL, ## 销量
`stock` INT(11) NOT NULL ## 库存
);


INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock`)
VALUES(NULL , '北欧风格小桌子' , '熊猫家居' , 180 , 666 , 7);
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock`)
VALUES(NULL , '简约风格小椅子' , '熊猫家居' , 180 , 666 , 7 );
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` )
VALUES(NULL , '典雅风格小台灯' , '蚂蚁家居' , 180 , 666 , 7 );
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` )
VALUES(NULL , '温馨风格盆景架' , '蚂蚁家居' , 180 , 666 , 7 );


SELECT * FROM furn;

创建 Result.java 该工具类用于返回结果(json 格式),这个工具类,在网上也可找到,直接拿来使用 , SSM 项目也用过类似的工具类

   

package com.study.furn.utils;

public class Result<T> {
    private String code;
    private String msg;
    private T data;

    public Result() {
    }

    public Result(String code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }


    public Result(T data){
        this.data=data;
    }

    //返回成功的result不携带data数据
    public static Result success(){
        Result<Object> objectResult = new Result<>();
        objectResult.setCode("200");
        objectResult.setMsg("success");
        return objectResult;
    }
    //返回成功的result携带data数据,将静态方法定义为泛型方法,这个泛型方法和泛型类的泛型参数是独立的
    //静态方法的泛型类型参数与泛型类的类型参数名称相同但是编译器会进行区分
    public static<K> Result success(K data){
        Result<K> tResult = new Result<>(data);
        tResult.setCode("200");
        tResult.setMsg("success");
        return tResult;
    }


    //返回错误的result不携带data
    public static Result error(String code,String msg){
        Result<Object> objectResult = new Result<>();
        objectResult.setCode(code);
        objectResult.setMsg(msg);
        return objectResult;
    }

    //返回失败的result携带data数据
    public static<K> Result error(K data,String code,String msg){
        Result<K> tResult = new Result<>(data);
        tResult.setCode(code);
        tResult.setMsg(msg);
        return tResult;
    }

}

编写dao层 

这里直接继承父接口BaseMapeer 

package com.study.furn.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.study.furn.bean.Furn;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface FurnMapper extends BaseMapper<Furn> {
}

在测试类中进行测试 :

编写Service层 

package com.study.furn.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.study.furn.bean.Furn;

public interface FurnService extends IService<Furn> {
}
package com.study.furn.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.study.furn.bean.Furn;
import com.study.furn.dao.FurnMapper;
import com.study.furn.service.FurnService;
import org.springframework.stereotype.Service;

@Service
public class FurnServiceImpl extends ServiceImpl<FurnMapper, Furn> implements FurnService {
}

 测试类中进行测试:

编写controller层 

package com.study.furn.controller;


import com.study.furn.bean.Furn;
import com.study.furn.service.FurnService;
import com.study.furn.utils.Result;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

/**
 * 这里使用复合注解确保所有的方法返回给前端的数据都是json格式的数据
 */
@RestController
public class FurnController {

    @Resource
    private FurnService furnService;

    /**
     * 这是一个保存Furn对象的方法
     * @param furn
     * @return
     */
    @PostMapping("/save")
    public Result save(@RequestBody Furn furn){
        furnService.save(furn);
        return Result.success();
    }
}

使用Postman进行测试:

注意细节:

1、@RequestBody注解是将json格式的数据封装到方法的参数对象中,如果前端发送的表单数据/请求参数,不使用@RequestBody注解也会进行自动封装(还要注意的是请求头的Content-Type要和请求的数据类型匹配:表单的数据类型是application/x-www-form-urlencoded,json数据类型是application/json)

2、使用@RequestBody注解接收 JSON 时,若 ID 字段显式设为 null,可能导致 ORM(如 Hibernate)抛出异常,解决的方法是在id字段上添加@TableId(type = IdType.AUTO)注解

在前端项目中添加表单组件

<template>
  <!--这个主要是用来路由到的页面,默认访问的页面-->
  <div>
    <!--  添加按钮和查询框-->
    <div style="margin: 10px 5px;display:inline-block">
      <el-button type="primary" @click="add">新增</el-button>
      <el-button>其他</el-button>
    </div>
    <div style="display: inline-block">
      <el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
      <el-button type="success">提交</el-button>
    </div>
    <!--  表格-->
    <el-table :data="tableData" stripe style="width: 100%">
      <el-table-column sortable prop="date" label="日期" />
      <el-table-column prop="name" label="姓名"  />
      <el-table-column prop="address" label="地址" />
      <el-table-column fixed="right" label="操作" min-width="120">
        <template #default>
          <el-button link type="primary"  @click="handleClick">
            删除
          </el-button>
          <el-button link type="primary">编辑</el-button>
        </template>
      </el-table-column>
    </el-table>


    <!--  下面是对话框和表单
    <el-input v-model="form.name" style="width: 80%"></el-input>
    这里的form.name 表示form对象的属性name
    必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
    这里的属性名可以动态生成,不需要在数据池里面编写

    -->
    <el-dialog
        title="提示"
        v-model="dialogVisible"
        width="30%">

      <el-form :model="form" label-width="120px">
        <!-- 家居名 -->
        <el-form-item label="家居名">
          <el-input v-model="form.name" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 厂商 -->
        <el-form-item label="厂商">
          <el-input v-model="form.maker" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 价格 -->
        <el-form-item label="价格">
          <el-input v-model="form.price" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 销量 -->
        <el-form-item label="销量">
          <el-input v-model="form.sales" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 库存 -->
        <el-form-item label="库存">
          <el-input v-model="form.stock" style="width: 80%"></el-input>
        </el-form-item>
      </el-form>

      <template #footer>
    <span class="dialog-footer">
<!--      直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上-->
      <el-button @click="dialogVisible = false">取消</el-button>
      <el-button type="primary" @click="save">确定</el-button>
    </span>
      </template>
    </el-dialog>
  </div>
</template>

<script>
// @ is an alias to /src
// import HelloWorld from '@/components/HelloWorld.vue'
export default {
  name: 'HomeView',
  components: {
  },
  //数据池的方法
  data(){
    return{
      //这里默认表单是不显示的
      dialogVisible:false,
      form:{},
      tableData:[
        {
          date: '2016-05-03',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
        {
          date: '2016-05-02',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
        {
          date: '2016-05-04',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
        {
          date: '2016-05-01',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
      ]
    }
  },
  methods:{
    add(){
      this.dialogVisible=true;
      //但调用该方法后,将form对象的信息进行清除,
      // 因为表单里的数据和数据池的数据是双向绑定的如果填写了表单的数据那么数据池的数据就有了
      // 防止下次点击后出现上一次填写的数据,必须清空数据池的数据
      this.form={}
    }
  }
}
</script>

在vue项目的终端中安装Axios

npm i axios -S

编写request请求对象

//导入Axios包
import axios from "axios";
//通过Axios创建对象Request 发送请求到后端
const request = axios.create({
    timeout:5000
})
//Request拦截器加上统一的处理
//比如Content-Type,请求头中添加Content-Type表示请求体中的数据类型是json数据,
// 后端@RequestBody注解在内容协商中能匹配成功
request.interceptors.request.use(config=>{
    config.headers['Content-Type'] = 'application/json;charset=utf-8'
    return config
},error=>{
    return Promise.reject(error)
})


//response拦截器拦截响应对象,统一处理返回的结果
request.interceptors.response.use(response=>{
    let res = response.data
    //如果返回的是文件,就返回
    if (response.config.responseType==='blob'){
        return res
    }
    //如果是String,就转成json对象
    if (typeof res==='string'){
        res = res ? JSON.parse(res):res
    }
    return res
},error=>{
    return Promise.reject(error)
})

//导出Request对象,在其他组件就可以使用
export default request

在HomeView.vue页面中编写save方法发送json数据给后端

 

<template>
  <!--这个主要是用来路由到的页面,默认访问的页面-->
  <div>
    <!--  添加按钮和查询框-->
    <div style="margin: 10px 5px;display:inline-block">
      <el-button type="primary" @click="add">新增</el-button>
      <el-button>其他</el-button>
    </div>
    <div style="display: inline-block">
      <el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
      <el-button type="success">提交</el-button>
    </div>
    <!--  表格-->
    <el-table :data="tableData" stripe style="width: 100%">
      <el-table-column sortable prop="date" label="日期" />
      <el-table-column prop="name" label="姓名"  />
      <el-table-column prop="address" label="地址" />
      <el-table-column fixed="right" label="操作" min-width="120">
        <template #default>
          <el-button link type="primary"  @click="handleClick">
            删除
          </el-button>
          <el-button link type="primary">编辑</el-button>
        </template>
      </el-table-column>
    </el-table>


    <!--  下面是对话框和表单
    <el-input v-model="form.name" style="width: 80%"></el-input>
    这里的form.name 表示form对象的属性name
    必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
    这里的属性名可以动态生成,不需要在数据池里面编写

    -->
    <el-dialog
        title="提示"
        v-model="dialogVisible"
        width="30%">

      <el-form :model="form" label-width="120px">
        <!-- 家居名 -->
        <el-form-item label="家居名">
          <el-input v-model="form.name" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 厂商 -->
        <el-form-item label="厂商">
          <el-input v-model="form.maker" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 价格 -->
        <el-form-item label="价格">
          <el-input v-model="form.price" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 销量 -->
        <el-form-item label="销量">
          <el-input v-model="form.sales" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 库存 -->
        <el-form-item label="库存">
          <el-input v-model="form.stock" style="width: 80%"></el-input>
        </el-form-item>
      </el-form>

      <template #footer>
    <span class="dialog-footer">
<!--      直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上-->
      <el-button @click="dialogVisible = false">取消</el-button>
      <el-button type="primary" @click="save">确定</el-button>
    </span>
      </template>
    </el-dialog>
  </div>
</template>

<script>
// @ is an alias to /src
import request from "@/utils/request"
export default {
  name: 'HomeView',
  components: {
  },
  //数据池的方法
  data(){
    return{
      //这里默认表单是不显示的
      dialogVisible:false,
      form:{},
      tableData:[
        {
          date: '2016-05-03',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
        {
          date: '2016-05-02',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
        {
          date: '2016-05-04',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
        {
          date: '2016-05-01',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
      ]
    }
  },
  methods:{
    add(){
      this.dialogVisible=true;
      //但调用该方法后,将form对象的信息进行清除,
      // 因为表单里的数据和数据池的数据是双向绑定的如果填写了表单的数据那么数据池的数据就有了
      // 防止下次点击后出现上一次填写的数据,必须清空数据池的数据
      this.form={}
    },
    save(){
      request.post("http://localhost:9090/save",this.form).then(
          response=>{
            console.log("response",response)
          }
      )
    }
  }
}
</script>

 这里遇到一个跨域请求的问题:

跨域请求问题是指由于浏览器的**同源策略(Same-Origin Policy)**限制,导致网页无法直接访问不同源(协议、域名、端口任一不同)的资源,从而引发的访问限制问题。

1. 跨域问题的本质

跨域问题的根源是浏览器的同源策略,该策略规定:

  • 同源条件:协议、域名、端口必须完全相同。例如,https://example.comhttp://example.com(协议不同)、example.comapi.example.com(子域名不同)均属于跨域
  • 目的:防止恶意网站通过脚本窃取敏感数据(如Cookie、用户信息等),确保用户数据安全。

2. 跨域请求的触发场景

以下情况会触发跨域限制:

  1. Ajax/Fetch请求:前端通过JavaScript发起的HTTP请求目标不同源的服务端接口。
  2. 资源加载:跨域加载图片、CSS、JavaScript文件等静态资源。
  3. Web API调用:如调用第三方API(如支付接口、地图服务)时,若未配置CORS则会被拦截。

3. 跨域问题的表现

浏览器拦截:即使请求成功发送到服务端并返回数据,浏览器仍会拦截响应,导致前端无法获取结果。

错误提示:常见控制台报错如 No 'Access-Control-Allow-Origin' header is present

解决方法:代理服务器

  • 原理:前端请求同源代理服务器,由代理转发请求至目标服务器,规避浏览器限制。常用工具如Nginx或后端框架(如Spring Boot)的代理配置

在vue.config.js文件中添加代理 

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true
})

module.exports={
  devServer:{
    port:10000 ,//启动端口
    // 设置代理
    proxy:{
      '/api':{ //设置拦截器,拦截器的格式 斜杆+名字   拦截规则:匹配所有以/api开头的请求路径
        target:'http://localhost:9090', //目标服务器地址:将匹配的请求转发到http://localhost:9090
        changeOrigin:true, //设置同源
        pathRewrite:{ //路径重写
          '/api':'' //api被替换为空,前端统一使用代理前缀(如 /api),但后端接口路径不含该前缀
          //前端请求:/api/user/list
          //实际转发:http://backend.com/user/list
          //    /api 这部分被 target的路径代替
        }
      }
    }
  }
}

在HomeView.vue页面中进行修改 

<template>
  <!--这个主要是用来路由到的页面,默认访问的页面-->
  <div>
    <!--  添加按钮和查询框-->
    <div style="margin: 10px 5px;display:inline-block">
      <el-button type="primary" @click="add">新增</el-button>
      <el-button>其他</el-button>
    </div>
    <div style="display: inline-block">
      <el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
      <el-button type="success">提交</el-button>
    </div>
    <!--  表格-->
    <el-table :data="tableData" stripe style="width: 100%">
      <el-table-column sortable prop="date" label="日期" />
      <el-table-column prop="name" label="姓名"  />
      <el-table-column prop="address" label="地址" />
      <el-table-column fixed="right" label="操作" min-width="120">
        <template #default>
          <el-button link type="primary"  @click="handleClick">
            删除
          </el-button>
          <el-button link type="primary">编辑</el-button>
        </template>
      </el-table-column>
    </el-table>


    <!--  下面是对话框和表单
    <el-input v-model="form.name" style="width: 80%"></el-input>
    这里的form.name 表示form对象的属性name
    必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
    这里的属性名可以动态生成,不需要在数据池里面编写

    -->
    <el-dialog
        title="提示"
        v-model="dialogVisible"
        width="30%">

      <el-form :model="form" label-width="120px">
        <!-- 家居名 -->
        <el-form-item label="家居名">
          <el-input v-model="form.name" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 厂商 -->
        <el-form-item label="厂商">
          <el-input v-model="form.maker" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 价格 -->
        <el-form-item label="价格">
          <el-input v-model="form.price" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 销量 -->
        <el-form-item label="销量">
          <el-input v-model="form.sales" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 库存 -->
        <el-form-item label="库存">
          <el-input v-model="form.stock" style="width: 80%"></el-input>
        </el-form-item>
      </el-form>

      <template #footer>
    <span class="dialog-footer">
<!--      直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上-->
      <el-button @click="dialogVisible = false">取消</el-button>
      <el-button type="primary" @click="save">确定</el-button>
    </span>
      </template>
    </el-dialog>
  </div>
</template>

<script>
// @ is an alias to /src
import request from "@/utils/request"
export default {
  name: 'HomeView',
  components: {
  },
  //数据池的方法
  data(){
    return{
      //这里默认表单是不显示的
      dialogVisible:false,
      form:{},
      tableData:[
        {
          date: '2016-05-03',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
        {
          date: '2016-05-02',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
        {
          date: '2016-05-04',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
        {
          date: '2016-05-01',
          name: 'Tom',
          address: 'No. 189, Grove St, Los Angeles',
        },
      ]
    }
  },
  methods:{
    add(){
      this.dialogVisible=true;
      //但调用该方法后,将form对象的信息进行清除,
      // 因为表单里的数据和数据池的数据是双向绑定的如果填写了表单的数据那么数据池的数据就有了
      // 防止下次点击后出现上一次填写的数据,必须清空数据池的数据
      this.form={}
    },
    save(){
      request.post("/api/save",this.form).then(
          response=>{
            console.log("response",response)
            this.dialogVisible=false
          }
      )
    }
  }
}
</script>

1. 代理机制的核心逻辑

通过将浏览器发起的跨域请求 转发到同源的后端代理服务,再由代理服务转发到实际目标服务器,从而绕过浏览器的同源策略限制。具体流程如下:

  1. 前端请求路径:前端代码向同源地址(如 http://localhost:10000/api/data)发送请求。
  2. 代理拦截:webpack-dev-server 根据 /api 规则拦截请求,替换目标地址为 http://localhost:9090/data(通过 pathRewrite 移除 /api 前缀)。
  3. 服务端转发:代理服务将请求转发到目标服务器,并返回响应给前端。
  4. 浏览器无感知:浏览器始终认为请求来自同源(localhost:10000),因此不会触发跨域拦截

过程:浏览器发送请求到vue,vue中进行拦截请求替换目标地址再请求到后端,后端响应数据到前端 

2. 关键配置项的作用

(1)target: 'http://localhost:9090'
  • 功能:指定目标服务器的真实地址,告知代理将请求转发至此地址。
  • 本质:将前端路径中的 /api 替换为 http://localhost:9090,实现请求重定向
2)changeOrigin: true
  • 作用:修改请求头中的 Host 和 Origin 为目标服务器地址(localhost:9090),使目标服务器认为请求来自合法源。
  • 必要性:部分服务器会验证请求来源,若未设置此参数,目标服务器可能因 Host 不匹配而拒绝请求
3)pathRewrite: { '/api': '' }
  • 功能:重写请求路径,移除前端添加的 /api 前缀。例如,/api/user 将被改写为 /user,确保目标接口路径正确。
  • 灵活性:允许前端统一使用代理前缀,后端接口无需修改即可适配

3. 代理为何能绕过跨域限制?

(1)浏览器同源策略的规避
  • 浏览器视角:所有请求均指向本地开发服务器(如 localhost:10000),未触发跨域规则。
  • 实际路径:代理服务器作为中间层,将请求转发至外部目标服务器(localhost:9090),而 服务器间通信不受浏览器同源策略限制

4. 与其他方案的对比

方案优势局限性
代理服务器无需后端配合修改,适合本地开发调试;配置简单灵活仅适用于开发环境,生产环境需通过Nginx等实现代理
CORS标准化解决方案,支持所有HTTP方法需后端配置响应头,对第三方API无法控制
JSONP兼容老旧浏览器仅支持GET请求,存在安全风险

5. 注意事项

  • 仅限开发环境:代理配置通常在 webpack.config.js 或 vue.config.js 中设置,不适用于生产环境。生产环境需通过Nginx反向代理或后端服务处理跨域
  • 复杂路径匹配:可通过正则表达式定义更灵活的代理规则(如 /api/** 匹配多级路径),满足复杂接口需求

 进行测试:


五、显示家居信息

完成后台代码从 mapper -> service -> controller , 并对每层代码进行测试 , 到 controller 这一层,使用 Postman 发送 http 请求完成测试,由于mybatis-plus已经提供了父接口的方法,所以不需要再编写查询方法

在controller层添加查询方法 

使用Postman进行测试

在前端vue项目中修改,先清空数据池中tableData中的数据

表格部分的字段进行修改 

下面是Template标签 的组件
<template>
  <!--这个主要是用来路由到的页面,默认访问的页面-->
  <div>
    <!--  添加按钮和查询框-->
    <div style="margin: 10px 5px;display:inline-block">
      <el-button type="primary" @click="add">新增</el-button>
      <el-button>其他</el-button>
    </div>
    <div style="display: inline-block">
      <el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
      <el-button type="success">提交</el-button>
    </div>
    <!--  表格
    :data="tableData" 单向渲染数据,从数据池的data数据池的tableData字段里获取数据,
    <=> v-bind:data     -->
    <el-table :data="tableData" stripe style="width: 100%">
      <el-table-column sortable prop="id" label="ID" />
      <el-table-column prop="name" label="家居名"  />
      <el-table-column prop="maker" label="厂家" />
      <el-table-column prop="price" label="价格" />
      <el-table-column prop="sales" label="销量" />
      <el-table-column prop="stock" label="库存" />
      <el-table-column fixed="right" label="操作" min-width="120">
        <template #default>
          <el-button link type="primary"  @click="handleClick">
            删除
          </el-button>
          <el-button link type="primary">编辑</el-button>
        </template>
      </el-table-column>
    </el-table>


    <!--  下面是对话框和表单
    <el-input v-model="form.name" style="width: 80%"></el-input>
    这里的form.name 表示form对象的属性name
    必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
    这里的属性名可以动态生成,不需要在数据池里面编写

    -->
    <el-dialog
        title="提示"
        v-model="dialogVisible"
        width="30%">

      <el-form :model="form" label-width="120px">
        <!-- 家居名 -->
        <el-form-item label="家居名">
          <el-input v-model="form.name" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 厂商 -->
        <el-form-item label="厂商">
          <el-input v-model="form.maker" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 价格 -->
        <el-form-item label="价格">
          <el-input v-model="form.price" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 销量 -->
        <el-form-item label="销量">
          <el-input v-model="form.sales" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 库存 -->
        <el-form-item label="库存">
          <el-input v-model="form.stock" style="width: 80%"></el-input>
        </el-form-item>
      </el-form>

      <template #footer>
    <span class="dialog-footer">
<!--      直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上-->
      <el-button @click="dialogVisible = false">取消</el-button>
      <el-button type="primary" @click="save">确定</el-button>
    </span>
      </template>
    </el-dialog>
  </div>
</template>

添加list方法

将data字段的数据填充到数据池中

注意response拦截器进行了拦截处理:将字符串转成json格式的对象,并且将变量进行赋值

<template>
  <!--这个主要是用来路由到的页面,默认访问的页面-->
  <div>
    <!--  添加按钮和查询框-->
    <div style="margin: 10px 5px;display:inline-block">
      <el-button type="primary" @click="add">新增</el-button>
      <el-button>其他</el-button>
    </div>
    <div style="display: inline-block">
      <el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
      <el-button type="success">提交</el-button>
    </div>
    <!--  表格
    :data="tableData" 单向渲染数据,从数据池的data数据池的tableData字段里获取数据,
    <=> v-bind:data     -->
    <el-table :data="tableData" stripe style="width: 100%">
      <el-table-column sortable prop="id" label="ID" />
      <el-table-column prop="name" label="家居名"  />
      <el-table-column prop="maker" label="厂家" />
      <el-table-column prop="price" label="价格" />
      <el-table-column prop="sales" label="销量" />
      <el-table-column prop="stock" label="库存" />
      <el-table-column fixed="right" label="操作" min-width="120">
        <template #default>
          <el-button link type="primary"  @click="handleClick">
            删除
          </el-button>
          <el-button link type="primary">编辑</el-button>
        </template>
      </el-table-column>
    </el-table>


    <!--  下面是对话框和表单
    <el-input v-model="form.name" style="width: 80%"></el-input>
    这里的form.name 表示form对象的属性name
    必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
    这里的属性名可以动态生成,不需要在数据池里面编写

    -->
    <el-dialog
        title="提示"
        v-model="dialogVisible"
        width="30%">

      <el-form :model="form" label-width="120px">
        <!-- 家居名 -->
        <el-form-item label="家居名">
          <el-input v-model="form.name" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 厂商 -->
        <el-form-item label="厂商">
          <el-input v-model="form.maker" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 价格 -->
        <el-form-item label="价格">
          <el-input v-model="form.price" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 销量 -->
        <el-form-item label="销量">
          <el-input v-model="form.sales" style="width: 80%"></el-input>
        </el-form-item>

        <!-- 库存 -->
        <el-form-item label="库存">
          <el-input v-model="form.stock" style="width: 80%"></el-input>
        </el-form-item>
      </el-form>

      <template #footer>
    <span class="dialog-footer">
<!--      直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上-->
      <el-button @click="dialogVisible = false">取消</el-button>
      <el-button type="primary" @click="save">确定</el-button>
    </span>
      </template>
    </el-dialog>
  </div>
</template>

<script>
//导入组件
import request from "@/utils/request";
export default {
  name: 'HomeView',
  components: {
  },
  //数据池的方法
  data(){
    return{
      //这里默认表单是不显示的
      dialogVisible:false,
      form:{},
      tableData:[]
    }
  },
  //钩子函数, created()函数调用后
  //数据池和方法池的数据都进行了初始化
  created() {
    //调用list方法展示数据
    this.list()
  },
  methods:{
    list(){
      request.get("/api/list").then(response=>{
        this.tableData=response.data
      })
    },
    add(){
      this.dialogVisible=true;
      //但调用该方法后,将form对象的信息进行清除,
      // 防止下次点击后出现上一次填写的数据
      this.form={}
    },
    save(){
      request.post("/api/save",this.form).then(response=>{
        console.log("response",response)
        this.dialogVisible=false
        //增加家居后调用查询
        this.list()
      })
    },

  }
}
</script>


六、修改家居信息

完成后台代码从 mapper -> service -> controller , 并对每层代码进行测试 , 到 controller 这一层,使用 Postman 发送 http 请求完成测试,由于mybatis-plus已经提供了父接口的方法,所以不需要再编写修改方法

在controller层添加修改方法 

使用Postman进行测试 

在前端vue项目中添加方法得到回显的数据

直接通过前端scope.row获取当前行的代理对象

  

发现这是个代理对象,需要转换 Proxy 为原始对象

将代理对象转成原始对象,再将原始json对象绑定到 form表单中并进行展示

save方法如下:根据form表单中是否有数据来区分要进行增加数据还是修改数据,同时根据响应的状态码来回显状态信息 

   save(){//添加、修改
      if (this.form.id){//如果表单中有数据id就是修改操作
        request.put("/api/update",this.form).then(
            response=>{
              if (response.code==="200"){//响应的状态码
                ElMessage({
                  type:"success",
                  message:"修改成功"
                })
              }else {
                ElMessage({
                  type:"error",
                  message:"修改失败"
                })
              }
              //刷新数据
              this.list()
              //关闭对话框
              this.dialogVisible=false
            }
        )


      }else {//添加
        request.post("/api/save",this.form).then(response=>{
          console.log("response",response)
          //增加家居后调用查询,刷新数据
          this.list()
          this.dialogVisible=false
        })
      }
    }

补充回显数据的方式2:

将代理对象的id取出,然后将id发送给后端,后端根据id查询对应的Furn对象,然后再将对象进行回显

 在HomeView.vue页面中添加一个方法

 

 同样能回显数据,这种方式回显才能确保数据库的真实数据


七、删除家居信息 

完成后台代码从 mapper -> service -> controller , 并对每层代码进行测试 , 到 controller 这一层,使用 Postman 发送 http 请求完成测试,由于mybatis-plus已经提供了父接口的方法,所以不需要再编写删除方法

在controller层添加删除方法 

使用Postman进行测试  

在前端vue实现删除的方法

将删除按钮进行修改

实现删除的方法 

删除按钮: 

 <!--        删除按钮 -->
          <el-popconfirm title="确认删除吗?" @confirm="handleDel(scope.row.id)">
            <template #reference>
              <el-button size="small" type="danger">删除</el-button>
            </template>
          </el-popconfirm>

删除方法: 

    handleDel(id){
     request.delete("/api/delete?id="+id).then(
         response=> {
           if (response.code === "200") {//响应的状态码
             ElMessage({
               type: "success",
               message: "删除成功"
             })
           } else {
             ElMessage({
               type: "error",
               message:"删除失败"
             })
           }
           //刷新数据
           this.list()
         }
     )
    }


八、分页显示列表

在配置类中配置分页拦截器

package com.study.furn.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 这是一个mybatis-plus配置类,在这里配置分页插件
 */
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //配置分页拦截器
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }
}

 在controller层添加分页方法 

使用Postman进行测试  

在表格下面添加导航栏组件 

<!--    添加分页导航-->
    <div style="margin:10px 0">
      <el-pagination
          @size-change="handlePageSizeChange"
          @current-change="handleCurrentChange"
          :current-page="currentPage"
          :page-sizes="[5, 10]"
          :page-size="pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="total"
      >
      </el-pagination>
    </div>

数据池中绑定初始化数据 

完善导航栏的两个方法,双向数据绑定 

修改list方法为分页方法

修改请求的路径并传两个参数(当前页,每页的数量)给后端,后端返回的total绑定到数据池 

 

list方法如下 :

list(){
      // request.get("/api/list").then(response=>{
      //   this.tableData=response.data
      // })
      request.get("/api/page",{
       params:{
         pageNum:this.currentPage, //传给后端,当前页是第几页
         pageSize:this.pageSize//传给后端,当前页的数量
       }
      }).then(response=>{
        this.tableData=response.data.records
        this.total=response.data.total
      })
    },


九、切换数据源为druid数据源

新创建一个配置类 

package com.study.furn.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/**
 * 数据源配置类
 */
@Configuration
public class DruidDataSourceConfig {


    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        return druidDataSource;
    }
}

在application.yaml文件中配置也是可以的 


十、带条件查询分页显示列表

完成后台代码从 mapper -> service -> controller , 并对每层代码进行测试 , 到 controller 这一层,使用 Postman 发送 http 请求完成测试,由于mybatis-plus已经提供了父接口的方法,所以不需要再编写方法

在controller层添加方法 

使用Postman进行测试   

在查询输入框中绑定数据池,查询按钮中绑定点击方法

这里还是调用list方法

修改list方法 


十一、添加家居表单完善前端校验

在数据池中添加校验规则

      //校验规则
      rules:{
        name:[{
          required:true,message:"请输入家居名",trigger:"blur"
        }],
        maker:[{
          required:true,message:"请输入厂家名",trigger:"blur"
        }],
        price:[{
          required:true,message:"请输入价格",trigger:"blur"
        },{
          pattern:/^([1-9]\d*|0)(\.\d+)?$/ ,message: "请输入数字",trigger:"blur"
        }
        ],
        sales:[{
          required:true,message:"请输入销量",trigger:"blur"
        },{
          pattern:/^([1-9]\d*|0)$/ ,message: "请输入数字",trigger:"blur"
        }],
        stock:[{
          required:true,message:"请输入库存",trigger:"blur"
        },{
          pattern:/^([1-9]\d*|0)$/ ,message: "请输入数字",trigger:"blur"
        }]
      }

表单中绑定校验规则 

在点击确定按钮时进行校验,如果校验通过才发送请求到后端 

清空上一次的校验 

 

//清空上一次的校验
      this.$nextTick(() => {
        this.$refs['form'].resetFields();
      });


十二、添加家居表单完善后端校验 

后端校验主要是防止别人绕过前端校验直接发送save请求到后端

引入jsr303数据校验支持

记得刷新Maven

使用注解对furn的字段进行校验 

 使用Postman进行测试   

在数据池中添加一个后端校验对象 

 在save方法中添加代码根据后端的状态码来处理

在表单中展示后端的校验信息 

清空上一次的后端校验信息 


总结: 

该项目使用了前后端分离,前端的主体框架Vue3+后端的基础框架Spring-Boot

1.前端技术栈: vue3 + Axios + ElementsPlus

2.后端技术栈:SpringBoot + MyBatis Plus

3.数据库-Mysql

4.项目依赖管理-Maven

5.分页-MyBatis Plus 分页插件

6.切换数据源DruidDataSources

7.项目前端我们使用到 request 和 response拦截器,并且我们解决了跨域问题

目前该项目还有很多bug,后续再完善...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2373946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spyglass:在batch/shell模式下运行目标的顶层是什么?

相关阅读 Spyglasshttps://blog.csdn.net/weixin_45791458/category_12828934.html?spm1001.2014.3001.5482 除了可以在图形用户界面(GUI)中运行目标外&#xff0c;使用Batch模式或Shell模式也可以运行目标&#xff0c;如下面的命令所示。 % spyglass -project test.prj -ba…

微服务架构中如何保证服务间通讯的安全

在微服务架构中,保证服务间通信的安全至关重要。服务间的通信通常是通过HTTP、gRPC、消息队列等方式实现的,而这些通信链路可能面临多种安全风险。为了应对这些风险,可以采取多种措施来保证通信安全。 常见的服务间通信风险 1.数据泄露:在服务间通信过程中,敏感数据可能会…

工具篇-Cherry Studio之MCP使用

一、添加MCP 打开Cherry Studio,如果没有可以到官网下载:Cherry Studio 官方网站 - 全能的AI助手 按上面步骤打开同步服务器 1、先去注册ModelScope,申请令牌 2、再打开MCP广场,找到高德MCP 选择工具测试,这里有个高德的api key需要申请 打开如下地址高德开放平…

Python 运维脚本

1、备份文件 import os import shutil# 定义配置文件目录和备份目录的路径 config_dir "/root/python/to/config/files/" backup_dir "/root/python/to/backup/"# 遍历配置文件目录中的所有文件 for filename in os.listdir(config_dir):# 如果文件名以…

大模型项目:普通蓝牙音响接入DeepSeek,解锁语音交互新玩法

本文附带视频讲解 【代码宇宙019】技术方案&#xff1a;蓝牙音响接入DeepSeek&#xff0c;解锁语音交互新玩法_哔哩哔哩_bilibili 目录 效果演示 核心逻辑 技术实现 大模型对话&#xff08;技术&#xff1a; LangChain4j 接入 DeepSeek&#xff09; 语音识别&#xff08;…

单链表设计与实现

01. 单链表简介 在数据结构中&#xff0c;单链表的实现可以分为 带头结点 和 不带头结点 两种方式&#xff0c;这里我们讨论第二种方式。 头结点&#xff1a;链表第一个节点不存实际数据&#xff0c;仅作为辅助节点指向首元节点&#xff08;第一个数据节点&#xff09;。头指…

springboot生成二维码到海报模板上

springboot生成二维码到海报模板上 QRCodeController package com.ruoyi.web.controller.app;import com.google.zxing.WriterException; import com.ruoyi.app.domain.Opportunity; import com.ruoyi.app.tool.QRCodeGenerator; import com.ruoyi.common.core.page.TableDat…

SEO长尾关键词布局优化法则

内容概要 在SEO优化体系中&#xff0c;长尾关键词的精准布局是突破流量瓶颈的关键路径。相较于竞争激烈的核心词&#xff0c;长尾词凭借其高转化率和低竞争特性&#xff0c;成为内容矩阵流量裂变的核心驱动力。本节将系统梳理长尾关键词布局的核心逻辑框架&#xff0c;涵盖从需…

python:trimesh 用于 STL 文件解析和 3D 操作

python&#xff1a;trimesh 是一个用于处理三维模型的库&#xff0c;支持多种格式的导入导出&#xff0c;比如STL、OBJ等&#xff0c;还包含网格操作、几何计算等功能。 Python Trimesh 库使用指南 安装依赖库 pip install trimesh Downloading trimesh-4.6.8-py3-none-any.w…

应急响应基础模拟靶机-security2

PS:杰克创建的流量包(result.pcap)在root目录下&#xff0c;请根据已有信息进行分析 1、首个攻击者扫描端口使用的工具是&#xff1f; 2、后个攻击者使用的漏洞扫描工具是&#xff1f; 3、攻击者上传webshell的绝对路径及User-agent是什么&#xff1f; 4、攻击者反弹shell的…

OpenCV定位地板上的书

任务目标是将下面的图片中的书本找出来&#xff1a; 使用到的技术包括&#xff1a;转灰度图、提取颜色分量、二值化、形态学、轮廓提取等。 我们尝试先把图片转为灰度图&#xff0c;然后二值化&#xff0c;看看效果&#xff1a; 可以看到&#xff0c;二值化后&#xff0c;书的…

NHANES稀有指标推荐:MedHi

文章题目&#xff1a;Association of dietary live microbe intake with frailty in US adults: evidence from NHANES DOI&#xff1a;10.1016/j.jnha.2024.100171 中文标题&#xff1a;美国成人膳食活微生物摄入量与虚弱的相关性&#xff1a;来自 NHANES 的证据 发表杂志&…

关于我在实现用户头像更换时遇到的图片上传和保存的问题

目录 前言 前端更换头像 后端处理 文件系统存储图片 数据库存储图片 处理图片文件 生成图片名 保存图片 将图片路径存储到数据库 完整代码 总结 前言 最近在实现一个用户头像更换的功能&#xff0c;但是因为之前并没有处理过图片的上传和保存&#xff0c;所以就开始…

10.二叉搜索树中第k小的元素(medium)

1.题目链接&#xff1a; 230. 二叉搜索树中第 K 小的元素 - 力扣&#xff08;LeetCode&#xff09;230. 二叉搜索树中第 K 小的元素 - 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 小的元素&#xff08;从 1 开始计数…

AlimaLinux设置静态IP

通过nmcli命令来操作 步骤 1&#xff1a;确认当前活动的网络接口名称 首先&#xff0c;需要确认当前系统中可用的网络接口名称。可以使用以下命令查看&#xff1a; nmcli device步骤 2&#xff1a;修改配置以匹配正确的接口名称 sudo nmcli connection modify ens160 ipv4.…

滑动窗口——将x减到0的最小操作数

题目&#xff1a; 这个题如果我们直接去思考方法是很困难的&#xff0c;因为我们不知道下一步是在数组的左还是右操作才能使其最小。正难则反&#xff0c;思考一下&#xff0c;无论是怎么样的&#xff0c;最终这个数组都会分成三个部分左中右&#xff0c;而左右的组合就是我们…

基于SpringBoot的抽奖系统测试报告

一、编写目的 本报告为抽奖系统测试报告&#xff0c;本项目可用于团体抽奖活动&#xff0c;包括了用户注册&#xff0c;用户登录&#xff0c;修改奖项以及抽奖等功能。 二、项目背景 抽奖系统采用前后端分离的方法来实现&#xff0c;同时使用了数据库来存储相关的数据&…

服务器mysql连接我碰到的错误

搞了2个下午&#xff0c;总算成功了 我在服务器上使用docker部署了java项目与mysql&#xff0c;但mysql连接一直出现问题 1.首先&#xff0c;我使用的是localhost连接&#xff0c;心想反正都在服务器上吧。 jdbc:mysql://localhost:3306/fly-bird?useSSLfalse&serverTime…

【Part 2安卓原生360°VR播放器开发实战】第四节|安卓VR播放器性能优化与设备适配

《VR 360全景视频开发》专栏 将带你深入探索从全景视频制作到Unity眼镜端应用开发的全流程技术。专栏内容涵盖安卓原生VR播放器开发、Unity VR视频渲染与手势交互、360全景视频制作与优化&#xff0c;以及高分辨率视频性能优化等实战技巧。 &#x1f4dd; 希望通过这个专栏&am…

从知识图谱到精准决策:基于MCP的招投标货物比对溯源系统实践

前言 从最初对人工智能的懵懂认知&#xff0c;到逐渐踏入Prompt工程的世界&#xff0c;我们一路探索&#xff0c;从私有化部署的实际场景&#xff0c;到对DeepSeek技术的全面解读&#xff0c;再逐步深入到NL2SQL、知识图谱构建、RAG知识库设计&#xff0c;以及ChatBI这些高阶应…