SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--后端实现

news2025/1/21 13:05:57

目录

SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--后端实现

技术栈

实现功能04-添加家居信息

需求分析/图解

思路分析

代码实现

创建\service\FurnService.java 和\service\FurnServiceImpl.java, 增加添加方法

修改Furn.java , 当创建Furn 对象imgPath 为null 时, imgPath 给默认值 

创建FurnServiceTest.java ,测试方法

创建Msg.java用来返回json 的数据的通用类

创建FurnController.java , 处理添加请求

使用Postman 来完成Controller 层的测试, 通过Postman 添加Furn 数据

点击添加按钮, 可以出现添加家居的对话框, 修改

完成测试: 看看点击新增按钮,能否正常的弹窗添加家居的对话框(含有表单)

项目前端安装axios, 用于发送Ajax 请求给后台, 一定要注意 

创建工具文件D:\idea_java_projects\ssm_vue\src\utils\request.js , 用于创建axios 

修改HomeView.vue , 在methods 编写save 方法, 并测试会出现跨域问题

创建vue.config.js 解决跨域问题, 因为修改了配置文件

修改Home.vue, 使用跨域请求, 并完成测试, 查看数据库,是否有新数据添加成功

注意事项和细节

实现功能05-显示家居信息

需求分析/图解

 思路分析

代码实现 

 FurnService.java

 FurnServiceImpl

修改FurnServiceTest.java ,测试findAll.

修改FurnController.java , 处理显示请求, 并使用Postman 完成测试

修改HomeView.vue , 编写list 方法

 完成测试,看看是否可以显示家居列表信息.

修改src\utils\request.js 增加response 拦截器, 统一处理响应后结果 

 修改Home.vue , 简化返回处理

 完成测试.

实现功能06-修改家居信息 

需求分析/图解 

思路分析

 代码实现

修改FurnService.java接口 

修改FurnServiceImpl.java

修改FurnServiceTest.java ,测试update

修改FurnController.java , 处理修改请求, 并使用Postman 完成测试

 修改HomeView.vue , 编写handleEdit 方法, 回显数据并测试

可以测试一下, 点击编辑, 回显数据

修改HomeView.vue , 修改save 方法,

完成测试

实现功能07-删除家居信息

需求分析/图解

思路分析

代码实现

修改FurnService.java接口 

修改FurnServiceImpl.java

修改FurnServiceTest.java ,测试del

修改FurnController.java , 处理删除请求, 并使用Postman 完成测试

修改HomeView.vue , 编写handleDel 方法, 完成删除

 完成测试测试

将修改家居信息功能, 回显家居表单数据, 改成从后端-DB 获取 

修改FurnService.java接口 

修改FurnServiceImpl.java

修改FurnServiceTest.java ,处理根据id返回对应的furn对象 

 修改FurnController.java , 处理根据id返回对应的furn对象, 并使用Postman 完成测试

 修改HomeView.vue , 编写handleEdit 第二种方法, 回显数据并测试 

实现功能08-分页显示列表

需求分析/图解

 思路分析

代码实现

1. 修改pom.xml 加入分页插件

2. 修改mybatis-config.xml, 配置分页拦截器

3. 修改FurnController.java 增加分页查询处理

修改FurnServiceTest.java ,测试分页查询是否OK 

使用Postman 进行测试,看看分页查询是否OK 

修改HomeView.vue , 完成分页导航显示、分页请求 

增加element-plus 分页控件 

完成测试

分页显示效果

实现功能09-带条件查询分页显示列表

需求分析/图解​编辑

 思路分析

代码实现

修改FurnService.java接口 

修改FurnServiceImpl.java

修改FurnController.java , 处理带条件分页查询 

修改FurnServiceTest.java ,测试条件分页查询是否OK  

 使用Postman 测试,是否通过

修改HomeView.vue , 完成带条件分页查询

 测试分页条件查询

 实现功能10-添加家居表单前端校验

说明: 参考element-plus 表单验证

思路分析

代码实现

修改HomeView.vue , 增加表单验证处理代码

测试

修改Homeview.vue 当表单验证不通过时,不提交表单 

完成测试

 实现功能11-添加家居表单后端校验

 需求分析/图解

 2. 后端校验-需求分析, 当后端校验没有通过,会出现灰色框提示, 后台不真正入库数据

思路分析

代码实现

1. 修改pom.xml 引入hibernate-validator jar 文件 

 2. 修改Furn.java , 使用hibernate-validator

 3. 修改FurnController.java , 对save 方法进行完善

4. 修改HomeView.vue , 显示服务器校验返回的提示信息

完成测试: 添加家居表单后端校验

测试页面效果 

测试完毕后, 记得恢复valid 的正确写法 


SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--后端实现

技术栈

SSM 项目【前后端分离】

- 说明: 前后端分离开发, 前端框架Vue + 后端框架SSM
1. 前端框架Vue
2. 后台框架-SSM(SpringMVC+Spring+MyBatis)
3. 数据库-MySQL
4. 项目的依赖管理-Maven
5. 分页-pagehelper。
6. 逆向工程-MyBatis Generator。
7. 其它...

-前端框架Vue
1. 使用了ElementPlus 来展示数据。
2. 使用Axios 对象来请求数据/走接口。

-后端框架SSM
1. 使用经典的三层结构。
2. 使用MyBatis Generator 和MyBatis pageHelper。

实现功能04-添加家居信息

需求分析/图解

 

思路分析

1. 完成后台代码从dao -> serivce -> controller , 并对每层代码进行测试, 到controller 这一层,使用Postman 发送http post 请求完成测试
2. 完成前端代码, 使用axios 发送ajax(json 数据)给后台, 实现添加家居信息 

代码实现

创建\service\FurnService.java 和\service\FurnServiceImpl.java, 增加添加方法

 FurnService接口

public interface FurnService {
    //添加
    public void save(Furn furn);
}

FurnServiceImpl 

@Service
public class FurnServiceImpl implements FurnService {


    //注入/装配FurnMapper接口对象(代理对象)
    @Resource
    private FurnMapper furnMapper;

    @Override
    public void save(Furn furn) {
        //解读
        //1. 使用insertSelective
        //2. 因为我们的furn表的id是自增的,就使用insertSelective
        furnMapper.insertSelective(furn);
    }

修改Furn.java , 当创建Furn 对象imgPath 为null 时, imgPath 给默认值 

private String imgPath = "assets/images/product-image/1.jpg";
public Furn(Integer id, String name, String maker, BigDecimal price,Integer sales, Integer stock, String imgPath) {
                this.id = id;
                this.name = name;
                this.maker = maker;
                this.price = price;
                this.sales = sales;
                this.stock = stock;
                if(!(imgPath == null || imgPath.equals("")) ){
                this.imgPath = imgPath;
        }
}

创建FurnServiceTest.java  测试方法

public class FurnServiceTest {

    //属性
    private ApplicationContext ioc;
    //从spring容器中,获取的是FurnService接口对象/代理对象
    private FurnService furnService;

    @Before
    public void init() {
        ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
        //说明
        //1. 通过FurnService.class 类型获取 FurnService接口对象/代理对象
        furnService = ioc.getBean(FurnService.class);
        //com.sun.proxy.$Proxy21
        System.out.println("furnService-" + furnService.getClass());
    }

    @Test
    public void save() {

        Furn furn =
                new Furn(null, "小风扇", "wyx家居", new BigDecimal(180), 10,
                        70, "assets/images/product-image/1.jpg");

        furnService.save(furn);

        System.out.println("添加成功~");
    }
}

创建Msg.java用来返回json 的数据的通用类

package com.wyxdu.furn.bean;

import java.util.HashMap;
import java.util.Map;

/**

 * Msg: 后端程序返回给前端的json数据的Msg对象=》本质就是数据规则
 */
public class Msg {

    //状态码 200-成功 400-失败
    private int code;
    //信息-说明
    private String msg;
    //返回给客户端/浏览器的数据-Map集合
    private Map<String, Object> extend =
            new HashMap<>();

    //编写几个常用的方法-封装好msg
    //返回success对应的msg
    public static Msg success() {
        Msg msg = new Msg();
        msg.setCode(200);
        msg.setMsg("success");
        return msg;
    }

    //返回fail对应的msg
    public static Msg fail() {
        Msg msg = new Msg();
        msg.setCode(400);
        msg.setMsg("fail");
        return msg;
    }

    //给返回的msg设置数据-不难应该可以看懂
    public Msg add(String key, Object value) {
        extend.put(key, value);
        return this;
    }

    public int getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

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

    public Map<String, Object> getExtend() {
        return extend;
    }

    public void setExtend(Map<String, Object> extend) {
        this.extend = extend;
    }
}

创建FurnController.java , 处理添加请求

@Controller
public class FurnController {

    //注入配置FurnService
    @Resource
    private FurnService furnService;

    /**
     * 解读
     * 1、响应客户端的添加请求
     * 2、@RequestBody: 使用 SpringMVC 的 @RequestBody 将客户端提交的 json 数据,封装成 JavaBean 对象
     * 3、@ResponseBody: 服务器返回的数据格式是按照 json 来返回的(底层是按照http协议进行协商)
     */
    @PostMapping("/save")
    @ResponseBody
    public Msg save(@RequestBody Furn furn) {
            furnService.save(furn);
            return Msg.success();

    }

使用Postman 来完成Controller 层的测试, 通过Postman 添加Furn 数据

使用Postman 测试时,因为我们前台是发送的json 数据,被服务器接收到后,转成javabean 数据,因此pom.xml 需要引入jackson,处理json 数据,

否则后台会报错.Content type 'application/json;charset=UTF-8' not supported

<!-- 引入jackson,处理json 数据-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.4</version>
</dependency>

 

点击添加按钮, 可以出现添加家居的对话框, 修改

SSM-Vue 整合项目\HomeView.vue ,

说明el-dialog 从Dialog 对话框获取, 表单代码从Form 表单获取,组合一下并调整一下即可

 

<!-- 添加家居的弹窗
说明:
1. el-dialog :v-model="dialogVisible" 表示对话框, 和dialogVisible 变量双向
绑定,控制是否显示对话框
2. el-form :model="form" 表示表单,数据和form 数据变量双向绑定
3. el-input v-model="form.name" 表示表单的input 空间,名字为name 需要和
后台Javabean 属性一致
-->
<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">
            <el-button @click="dialogVisible = false">取消</el-button>
            <el-button type="primary" @click="save">确定</el-button>
        </span>
    </template>
</el-dialog>
</div>
</template>
//增加数据, 一定要, 否则你会发现,在后面弹出的表单不能输入数据
data() {
    return {
        form: {},
//增加方法
methods: {
    add() {
        this.dialogVisible = true
        this.form = {}
    }
}
//增加点击新增的按钮事件
<div style="margin: 10px 0">
    <el-button type="primary" @click="add">新增</el-button>
    <el-button>其它</el-button>
</div>

完成测试: 看看点击新增按钮,能否正常的弹窗添加家居的对话框(含有表单)

 

项目前端安装axios, 用于发送Ajax 请求给后台, 一定要注意 

 

创建工具文件D:\idea_java_projects\ssm_vue\src\utils\request.js , 用于创建axios 

 

// 引入axios 包
// 重要提示:如果在启动前端项目,提示找不到axios , 把光标放在import axios from 'axios' 的'axios', 会有一个修复提示, 导入axios, 小伙伴点击, 导入即可正常使用
import axios from 'axios'
// 通过axios 创建对象
const request = axios.create({
    timeout: 5000
})
// request 拦截器
// 1. 可以对请求做一些处理
// 2. 比如统一加token,Content-Type 等
request.interceptors.request.use(config => {
    config.headers['Content-Type'] = 'application/json;charset=utf-8';
    return config
}, error => {
    return Promise.reject(error)
});
//导出
export default request

修改HomeView.vue , 在methods 编写save 方法, 并测试会出现跨域问题

<script>
// 引入request 组件
import request from "@/utils/request";
//methods 增加方法.
    save() {
        // =======说明====...
        request.post("http://localhost:10001/save", this.form).then(res => {
            console.log(res)
            this.dialogVisible = false
        })
    }
</script>

 

创建vue.config.js 解决跨域问题, 因为修改了配置文件

npm serve 需要重启, 否则不能识别.

const {defineConfig} = require('@vue/cli-service')
module.exports = defineConfig({
    transpileDependencies: true
})
module.exports = {
    devServer: {
        port: 10000, // 启动端口
        //解读如果我们请的地址 /api/save => 代理到 http://localhost:8080/ssm/save
        proxy: {                    //设置代理,必须填
            '/api': {               //设置拦截器  拦截器格式   斜杠+拦截器名字,名字可以自己定
                target: 'http://localhost:8080/ssm',  //代理的目标地址, 就是/api 代替 http://localhost:10001/
                changeOrigin: true,                 //是否设置同源,输入是的, 浏览器就允许跨域
                pathRewrite: {                      //路径重写
                    '/api': ''                      //选择忽略拦截器里面的单词
                }
            }
        }
    }
}

修改Home.vue, 使用跨域请求, 并完成测试, 查看数据库,是否有新数据添加成功

1. 将form 表单提交给/api/save 的接口

2. /api/save 等价http∶//locaLhost∶10001/save

3. 如果成功,就进入then 方法

4.res 就是返回的信息

5.查看mysql 看看数据是否保存

request.post("/api/save", this.form).then(res => {
        console.log(res)
        this.dialogVisible = false
    })
}

提醒, 这里容易出现的问题

1) 一定要确定request.post("/api/save") 被代理后的url , 是项目后台服务对应提供的API接口url ,  否则报404

2) 当跨域执行时请求,浏览器还是提示http://localhost:5927/api/xxx , 所以不要认为是api没有替换你的配置.

注意事项和细节

1. Postman 测试时, 要指定content-type ,否则会报错415

2. 如果需要将提交的json 数据, 封装到对应的Javabean, 需要配置@RequestBody , 否则会报错500

3. 如果需要返回json 数据, 需要在方法上, 配置@ResponseBody , 否则会报错404 

 


 

实现功能05-显示家居信息

需求分析/图解

 思路分析

1. 完成后台代码从dao -> serivce -> controller , 并对每层代码进行测试
2. 完成前台代码, 使用axios 发送http 请求,返回所有家居数据, 将数据绑定显示

代码实现 

修改FurnService.java 和FurnServiceImpl.java, 增加findAll 方法 

 FurnService.java

    //查询所有的家居信息
    public List<Furn> findAll();

 FurnServiceImpl

  @Override
    public List<Furn> findAll() {
        //查看分析FurnMapper.xml 文件
        //传入是null, 表示返回所有的家居信息
        return furnMapper.selectByExample(null);
    }

修改FurnServiceTest.java ,测试findAll.

 @Test
    public void findAll() {

        List<Furn> furns = furnService.findAll();
        for (Furn furn : furns) {
            System.out.println("furn-" + furn);
        }
    }

修改FurnController.java , 处理显示请求, 并使用Postman 完成测试

 

    @RequestMapping("/furns")
    @ResponseBody
    public Msg listFurns() {

        List<Furn> furnList = furnService.findAll();
        //Msg msg = Msg.success();
        把家居信息,封装到msg对象
        //msg.add("furnList", furnList);
        return Msg.success().add("furnList", furnList);

    }

修改HomeView.vue , 编写list 方法

//修改一下el-table
<el-table : data="tableData" stripe style="width: 100%">
    <el-table-column
        prop="id"
        label="ID"
        sortable
    >
    </el-table-column>
    <el-table-column
        prop="name"
        label="家居名"
    >
    </el-table-column>
    <el-table-column
        prop="maker"
        label="厂家">
    </el-table-column>
    <el-table-column
        prop="price"
        label="价格">
    </el-table-column>
    <el-table-column
        prop="sales"
        label="销量">
    </el-table-column>
    <el-table-column
        prop="stock"
        label="库存">
    </el-table-column>
    <el-table-column fixed="right" label="操作" width="100">
        <template #default="scope">
            <el-button @click="handleEdit(scope.row)" type="text">编辑</el-button>
        <el-button type="text">删除</el-button>
    </template>
</el-table-column>
</el - table >
    //修改一下tableData: []
    data() {
    return {
        form: {},
        dialogVisible: false,
        search: '',
        tableData: []
    }
}
//在created() 调用list() 完成页面数据获取
created() {
    this.list()
}
//编写list() method
list() { //请求显示家居列表-带检索
    request.get("/api/furns").then(res => {
        //绑定tableData, 显示在表格
        this.tableData = res.data.extend.furnsList
    })
}
//在save() 调用后,调用list() 刷新页面
save() {
    // =======说明====...
    request.post("/api/save", this.form).then(res => {
        console.log(res)
        this.dialogVisible = false
        this.list()
    })
}

 完成测试,看看是否可以显示家居列表信息.

 

修改src\utils\request.js 增加response 拦截器, 统一处理响应后结果 

//引入axios
import axios from "axios";
//通过axios创建对象-request对象,用于发送请求到后端
const request = axios.create({
    timeout: 5000
})

//request拦截器的处理
//1. 可以对请求做统一的处理
//2. 比如统一的加入token, Content-Type等
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 不为null, 就进行转换成json对象
            res = res ? JSON.parse(res) : res
        }
        return res;
    },
    error => {
        console.log("err", error)
        return Promise.reject(error);
    }
)

//导出request对象, 在其它文件就可以使用
export default request




 修改Home.vue , 简化返回处理

 

 完成测试.

 


 

实现功能06-修改家居信息 

需求分析/图解 

 

思路分析

1. 完成后台代码从dao -> serivce -> controller , 并对每层代码进行测试
2. 完成前台代码, 回显家居信息,再使用axios 发送http / ajax 请求,更新数据, 将数据绑定显示 

 代码实现

修改FurnService.java接口 和FurnServiceImpl.java, 增加update 方法

修改FurnService.java接口 

    //修改家居
    public void update(Furn furn);

修改FurnServiceImpl.java

    @Override
    public void update(Furn furn) {

        furnMapper.updateByPrimaryKeySelective(furn);
    }

修改FurnServiceTest.java ,测试update

 @Test
    public void update() {

        Furn furn = new Furn();
        furn.setId(1);
        furn.setName("北欧风格小桌子~~");
        furn.setMaker("小猪家居");
        //因为imgPath属性有一个默认值,
        //所以如果我们不希望生成update 语句有对imgPath 字段修改,就显式的设置null

        furn.setImgPath(null);
        furnService.update(furn);

        System.out.println("修改OK");

    }

修改FurnController.java , 处理修改请求, 并使用Postman 完成测试

   @PutMapping("/update")
    @ResponseBody
    public Msg update(@RequestBody Furn furn) {

        furnService.update(furn);
        return Msg.success();

    }

 修改HomeView.vue , 编写handleEdit 方法, 回显数据并测试

handleEdit(row) {
    //说明
    //1. JSON.stringify(row) 将row 转成json 字符串
    //2. JSON.parse(xx) 将字符串转成json 对象
    //3. 为什么这样做? 其实JSON.parse(JSON.stringify(row)) 就是对row 进行了深拷贝
    //4. 这样表格中的行数据和弹出框的数据就是独立的了
    this.form = JSON.parse(JSON.stringify(row))
    this.dialogVisible = true
    }
    //触发handleEdit 方法
    <el-button size="mini" @click="handleEdit(scope.row)" type="primary">编辑</el-button>

可以测试一下, 点击编辑, 回显数据

修改HomeView.vue , 修改save 方法,

处理修改请求, 说明更新成功的消息框, 不需要做额外处理, 直接使用this.$message 即可.

save() {
    //增加处理修改逻辑
    if (this.form.id) {
        request.put("/api/update", this.form).then(res => {
            if (res.code === 200) {//如果code 为200
                this.$message({ //弹出更新成功的消息框
                    type: "success",
                    message: "更新成功"
                })
            } else {
                this.$message({//弹出更新失败信息
                    type: "error",
                    message: res.msg
                })
            }
            this.list() //刷新列表
            this.dialogVisible = false
        })
    } else {//添加
        //=======说明======
        //1. 将form 表单提交给/api/save 的接口
        //2. /api/save 等价http://localhost:10001/save
        //3. 如果成功,就进入then 方法
        //4. res 就是返回的信息
        //5. 查看Mysql 看看数据是否保存
        request.post("/api/save", this.form).then(res => {
            this.dialogVisible = false
            this.list()
        })
    }
}

完成测试

浏览器http://localhost:10000

 


 

实现功能07-删除家居信息

需求分析/图解

思路分析

1. 完成后台代码从dao -> serivce -> controller , 并对每层代码进行测试
2. 完成前台代码,使用axios 发送http Ajax 请求,删除数据, 将数据绑定显示

代码实现

修改FurnService.java 和FurnServiceImpl.java, 增加del 方法

修改FurnService.java接口 

    //删除家居
    public void del(Integer id);


修改FurnServiceImpl.java

    @Override
    public void del(Integer id) {
        furnMapper.deleteByPrimaryKey(id);
    }

修改FurnServiceTest.java ,测试del

    @Test
    public void del() {

        furnService.del(17);
        System.out.println("删除OK");
    }

修改FurnController.java , 处理删除请求, 并使用Postman 完成测试

    @DeleteMapping("/del/{id}")
    @ResponseBody
    public Msg del(@PathVariable Integer id) {
        furnService.del(id);
        return Msg.success();
    }

修改HomeView.vue , 编写handleDel 方法, 完成删除

//处理删除方法
handleDel(id) {
    request.delete("/api/del/" + id).then(res => {
        if (res.code === 200) {
            this.$message({
                type: "success",
                message: "删除成功"
            })
        } else {
            this.$message({
                type: "error",
                message: res.msg
            })
        }
        this.list() // 刷新列表
    })
}
    //响应删除点击
    <template #default="scope">
    <el-button size="mini" @click="handleEdit(scope.row)" type="primary">编辑</el-button>
    <!--增加popcomfirm 控件,确认删除-- >
    <el-popconfirm
        title="确定删除吗?" @confirm="handleDel(scope.row.id)" >
            <template #reference>
                <el-button size="small" type="danger">删除</el-button>
            </template>
    </el - popconfirm >
    </template >

 完成测试测试

 

将修改家居信息功能, 回显家居表单数据, 改成从后端-DB 获取 

修改FurnService.java接口 和FurnServiceImpl.java, 增加根据id返回数据

修改FurnService.java接口 

    //根据id返回Furn
    public Furn findById(Integer id);


修改FurnServiceImpl.java

    @Override
    public Furn findById(Integer id) {
        return furnMapper.selectByPrimaryKey(id);
    }

修改FurnServiceTest.java ,处理根据id返回对应的furn对象 

    @Test
    public  void findById(){
        Furn byId = furnService.findById(2);
        System.out.println(byId);
    }

 修改FurnController.java , 处理根据id返回对应的furn对象, 并使用Postman 完成测试

    //提供接口,根据id返回对应的furn对象-封装Msg
    @GetMapping("/find/{id}")
    @ResponseBody
    public Msg findById(@PathVariable Integer id) {
        Furn furn = furnService.findById(id);
        return Msg.success().add("furn", furn);
    }

 修改HomeView.vue , 编写handleEdit 第二种方法, 回显数据并测试 

    handleEdit(row) {
      //console.log("row--", row)

      //将当前的家居信息绑定到弹出对话框的form
      //1. 方式1: 可以通过row.id 到后端-DB去获取对应的家居信息, 返回后将其绑定 this.form[这个是小伙伴可以解决-思考]
      //2. 方式2: 把获取的row的数据通过处理,绑定到this.form 进行显示
      //3. JSON.stringify(row): 将row 转成json字符串
      //4. JSON.parse(JSON.stringify(row)) 将json字符串转成json对象
      //this.form = JSON.parse(JSON.stringify(row))

      //可以通过row.id 到后端-DB去获取对应的家居信息
      request.get("/api/find/" + row.id).then(res => {
        // console.log("家居信息-", res.extend.furn)
        this.form = res.extend.furn
      })

      this.dialogVisible = true
    }

 


 

实现功能08-分页显示列表

需求分析/图解

 思路分析

1. 后台使用MyBatis PageHelper 插件完成分页查询, 前端我们使用分页组件
2. 修改FurnController , 增加处理分页显示代码API/接口
3. 完成前台代码,加入分页导航,并将分页请求和后台接口结合
4.  说明:有了现在的MyBatis PageHelper和前端的分页组件,完成分页就非常的方便,但是底层的分页模型和前面我们的java web原生项目一样

代码实现

1. 修改pom.xml 加入分页插件

<!-- 引入mybatis pageHelper 分页插件-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.1</version>
</dependency>

2. 修改mybatis-config.xml, 配置分页拦截器

<!-- plugins 标签要放在typeAliases 标签后面-->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 分页合理化,如果pageNum > pages,就让他查询最后一页。
        如果pageNum < 0,就查询第一页-->
        <property name="reasonable" value="true"/>
    </plugin>
</plugins>

3. 修改FurnController.java 增加分页查询处理

 /**
     * 分页请求接口
     *
     * @param pageNum:  要显示第几页 : 默认为1
     * @param pageSize: 每页要显示几条记录:默认为5
     * @return
     */
    @ResponseBody
    @RequestMapping("/furnsByPage")
    public Msg listFurnsByPage(@RequestParam(defaultValue = "1") Integer pageNum,
                               @RequestParam(defaultValue = "5") Integer pageSize) {

        //设置分页参数
        //解读
        //1.调用findAll是完成查询,底层会进行物理分页,而不是逻辑分页
        //2.会根据分页参数来计算 limit ?, ?, 在发出SQL语句时,会带limit
        PageHelper.startPage(pageNum, pageSize);

        List<Furn> furnList = furnService.findAll();


        //将分页查询的结果,封装到PageInfo
        //PageInfo 对象包含了分页的各个信息,比如当前页面pageNum , 共有多少记录
        //...

        PageInfo pageInfo = new PageInfo(furnList, pageSize);
        //将pageInfo封装到Msg对象,返回
        return Msg.success().add("pageInfo", pageInfo);
    }

修改FurnServiceTest.java ,测试分页查询是否OK 

    @Test
    public  void findById(){
        Furn byId = furnService.findById(2);
        System.out.println(byId);
    }

 

使用Postman 进行测试,看看分页查询是否OK 

修改HomeView.vue , 完成分页导航显示、分页请求 

 

 

增加element-plus 分页控件 

//增加element-plus 分页控件
<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 >
</div >
</template >
    //增加分页初始化数据
    data() {
    return {
        currentPage: 1,
        pageSize: 5,
        total: 10,
        //修改list(), 换成分页请求数据
        list() { //请求显示家居列表-不带检索
            request.get("/api/furnsByPage", {
                params: {
                    pageNum: this.currentPage,
                    pageSize: this.pageSize

                }
            }).then(res => {
                //绑定tableData, 显示在表格
                this.tableData = res.extend.pageInfo.list
                this.total = res.extend.pageInfo.total
            })
        }
//增加方法, 处理记录的变化, 这两个方法是和分页控件绑定的.
//处理每页显示多少条记录变化
handlePageSizeChange(pageSize) {
            this.pageSize = pageSize
            this.list()
        }
        ,
        //处理当前页变化, 比如点击分页连接,或者go to 第几页
        handleCurrentChange(pageNum) {
            this.currentPage = pageNum
            this.list()
        }

完成测试

启动项目后台服务furns_ssm
启动项目前台ssm_vue
浏览器: http://localhost:10000/

分页显示效果

● 测试分页显示效果, 浏览器: http://localhost:10000/

 


 

实现功能09-带条件查询分页显示列表

需求分析/图解

 思路分析

1. 完成后台代码从dao -> serivce -> controller , 并对每层代码进行测试
2. 完成前台代码,使用axios 发送http 请求,完成带条件查询分页显示

代码实现

修改FurnService.java 和FurnServiceImpl.java , 增加条件查询

修改FurnService.java接口 

    //根据家居名称进行查询
    public List<Furn> findByCondition(String name);

修改FurnServiceImpl.java

 @Override
    public List<Furn> findByCondition(String name) {

        FurnExample furnExample = new FurnExample();
        //通过Criteria 对象可以设置查询条件
        FurnExample.Criteria criteria = furnExample.createCriteria();

        //判断name是有具体的内容
        if (StringUtils.hasText(name)) {
            criteria.andNameLike("%" + name + "%");
        }
        //说明:如果name没有传值null ,"", "   ", 依然是查询所有的记录
        return furnMapper.selectByExample(furnExample);
    }

修改FurnController.java , 处理带条件分页查询 

 /**
     * 根据家居名进行分页查询-条件
     *
     * @param pageNum
     * @param pageSize
     * @return
     */
    @ResponseBody
    @RequestMapping("/furnsByConditionPage")
    public Msg listFurnsByConditionPage(@RequestParam(defaultValue = "1") Integer pageNum,
                                        @RequestParam(defaultValue = "5") Integer pageSize,
                                        @RequestParam(defaultValue = "") String search) {

        PageHelper.startPage(pageNum, pageSize);
        List<Furn> furnList = furnService.findByCondition(search);

        PageInfo pageInfo = new PageInfo(furnList, pageSize);

        //将pageInfo封装到Msg对象,返回
        return Msg.success().add("pageInfo", pageInfo);
    }

 

    @Test
    public void findByCondition() {

        List<Furn> furns = furnService.findByCondition("风格");

        for (Furn furn : furns) {
            System.out.println("furn--" + furn);
        }

    }

修改FurnServiceTest.java ,测试条件分页查询是否OK  

    @Test
    public void findByCondition() {

        List<Furn> furns = furnService.findByCondition("风格");

        for (Furn furn : furns) {
            System.out.println("furn--" + furn);
        }
    }

 使用Postman 测试,是否通过

 

修改HomeView.vue , 完成带条件分页查询

========增加搜索区========== 

< !--功能区域-->
    <div style="margin: 10px 0">
        <i class="el-icon-add-location"></i>
        <el-button type="primary" @click="add">新增</el-button>
</div>

< !--搜索区域-->
    <div style="margin: 10px 0">
        <el-input v-model="search" placeholder=" 请输入关键字" style="width: 20%"
            clearable></el-input>
        <el-button type="primary" style="margin-left: 5px" @click="list">检索</el-button>
</div>

 ===========在数据池,增加search 变量=====

 

 ==========修改list 方法,请求带条件分页的API 接口===

 测试分页条件查询

 


 

 实现功能10-添加家居表单前端校验

说明: 参考element-plus 表单验证

思路分析

1. 完成前台代码,使用ElementPlus 的表单rules 验证即可
2. 参考ElementPlus 的表单验证文档 

代码实现

修改HomeView.vue , 增加表单验证处理代码

==========增加对表单各个字段的校验规则=========

tableData: [],
    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' }
                    ]
}

==========指定将创建的规则应用到form 表单, 注意名称要对应=========
 添加家居的弹窗
说明:
1. el-dialog :v-model="dialogVisible" 表示对话框, 和dialogVisible 变量双向绑定,控制是否显示对话框

2. el-form :model="form" 表示表单,数据和form 数据变量双向绑定

3. el-input v-model="form.name" 表示表单的input 空间, 名字为name 需要和后台Javabean 属性一致

测试

就可以看到验证规则生效了【是光标离开输出框时,出现校验效果,因为是trigger:'blur' 事件】, 但是用户提交还是能成

 

修改Homeview.vue 当表单验证不通过时,不提交表单 

========修改save()===========

save() {
    //增加处理修改逻辑
    if (this.form.id) {
        request.put("/api/update", this.form).then(res => {
            if (res.code === 200) {//如果code 为200
                this.$message({ //弹出更新成功的消息框
                    type: "success",
                    message: "更新成功"
                })
            } else {
                this.$message({//弹出更新失败信息
                    type: "error",
                    message: res.msg
                })
            }
            this.list() //刷新列表
            this.dialogVisible = false
        })
    } else {//添加
        //表单数据校验是否
        this.$refs['form'].validate((valid) => {
            if (valid) {
                //=======说明======
                //1. 将form 表单提交给/api/save 的接口
                //2. /api/save 等价http://localhost:10001/save
                //3. 如果成功,就进入then 方法
                //4. res 就是返回的信息
                //5. 查看Mysql 看看数据是否保存
                request.post("/api/save", this.form).then(res => {
                    this.dialogVisible = false
                    this.list()
                })
            } else {
                this.$message({//弹出更新失败信息
                    type: "error",
                    message: "验证失败,不提交"
                })
                return false
            }
        })
    }
}

===========修改add()============

add() {
    this.dialogVisible = true
    this.form = {}
    this.$refs['form'].resetFields()//将添加验证提示消息,清空
}

完成测试

 


 

 实现功能11-添加家居表单后端校验

 需求分析/图解

1. 为什么前端校验了,后端还需要校验?-看老韩使用Postman 添加数据, 破前端校验机制. 

 

 

 

 

 2. 后端校验-需求分析, 当后端校验没有通过,会出现灰色框提示, 后台不真正入库数据

 

思路分析

1. 后台使用JSR303 数据校验,引入hibernate-validator.jar ,在SpringMVC的博客有详细的 链接 
2. 前台使用ElementPlus 进行数据绑定,并显示错误信息 

代码实现

1. 修改pom.xml 引入hibernate-validator jar 文件 

<!-- JSR303 数据校验支持 引入hibernate-validator-->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.1.0.Final</version>
</dependency>

 2. 修改Furn.java , 使用hibernate-validator

package com.wyxdu.furn.bean;

import org.hibernate.validator.constraints.Range;
import org.springframework.util.StringUtils;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;

public class Furn {
    private Integer id;

    @NotEmpty(message = "请输入家居名")
    private String name;

    @NotEmpty(message = "请输入制造厂商")
    private String maker;

    @NotNull(message = "请输入数字")
    @Range(min = 0, message = "价格不能小于0")
    private BigDecimal price;

    @NotNull(message = "请输入数字")
    @Range(min = 0, message = "销量不能小于0")
    private Integer sales;

    @NotNull(message = "请输入数字")
    @Range(min = 0, message = "库存不能小于0")
    private Integer stock;

    //说明: 修改Furn.java ,
    // 当创建Furn对象 imgPath 为null时, imgPath 给默认值(默认图片路径)
    private String imgPath = "assets/images/product-image/1.jpg";


    //增加一个全参构造器

    public Furn(Integer id, String name, String maker, BigDecimal price, Integer sales, Integer stock, String imgPath) {
        this.id = id;
        this.name = name;
        this.maker = maker;
        this.price = price;
        this.sales = sales;
        this.stock = stock;
        //解读
        //如果imgPath 不为null ,而且是有数据,就设置给 this.imgPath,否则就使用默认值
        //imgPath != null && !imgPath.equals("") =>使用一个工具类方法完成
        /**
         * public static boolean hasText(@Nullable String str) {
         *         return str != null && !str.isEmpty() && containsText(str);
         *     }
         */
        //StringUtils.hasText(imgPath) 就是要求imgPath 不是 null ,而且不是"", 而且不是"     "
        //该方法后面小伙伴会常常使用
        if (StringUtils.hasText(imgPath)) {
            this.imgPath = imgPath;
        }
    }

    //声明无参构造器
    public Furn() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public String getMaker() {
        return maker;
    }

    public void setMaker(String maker) {
        this.maker = maker == null ? null : maker.trim();
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getSales() {
        return sales;
    }

    public void setSales(Integer sales) {
        this.sales = sales;
    }

    public Integer getStock() {
        return stock;
    }

    public void setStock(Integer stock) {
        this.stock = stock;
    }

    public String getImgPath() {
        return imgPath;
    }

    public void setImgPath(String imgPath) {
        this.imgPath = imgPath == null ? null : imgPath.trim();
    }

    @Override
    public String toString() {
        return "Furn{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", maker='" + maker + '\'' +
                ", price=" + price +
                ", sales=" + sales +
                ", stock=" + stock +
                ", imgPath='" + imgPath + '\'' +
                '}';
    }
}

 3. 修改FurnController.java , 对save 方法进行完善
 

   @PostMapping("/save")
    @ResponseBody
    public Msg save(@Validated @RequestBody Furn furn, Errors errors) {

        Map<String, Object> map = new HashMap<>();

        List<FieldError> fieldErrors = errors.getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            map.put(fieldError.getField(), fieldError.getDefaultMessage());
        }

        if (map.isEmpty()) {//说明后端校验通过,因为没有发现校验错误
            furnService.save(furn);
            //返回成功msg
            return Msg.success();
        } else {
            //校验失败,把校验错误信息封装到Msg对象,并返回
            return Msg.fail().add("errorMsg", map);
        }

    }

4. 修改HomeView.vue , 显示服务器校验返回的提示信息

===========在数据池,增加显示错误信息变量========

data() {
    return {
    //存放错误信息
    serverValidErrors: {},

================修改save()方法,显示错误提示============

save() {
    //增加处理修改逻辑
    if (this.form.id) {
        request.put("/api/update", this.form).then(res => {
            if (res.code === 200) {//如果code 为200
                this.$message({ //弹出更新成功的消息框
                    type: "success",
                    message: "更新成功"
                })
            } else {
                this.$message({//弹出更新失败信息
                    type: "error",
                    message: res.msg
                })
            }
            this.list() //刷新列表
            this.dialogVisible = false
        })
    } else {//添加
        //表单数据校验是否
        this.$refs['form'].validate((valid) => {
            if (valid) {
                //=======说明======
                //1. 将form 表单提交给/api/save 的接口
                //2. /api/save 等价http://localhost:10001/save
                //3. 如果成功,就进入then 方法
                //4. res 就是返回的信息
                //5. 查看Mysql 看看数据是否保存
                request.post("/api/save", this.form).then(res => {
                    if (res.code === 200) {
                        this.dialogVisible = false
                        this.list()
                    } else if (res.code === 400) {
                        this.serverValidErrors.name = res.extend.errorMsg.name;
                        this.serverValidErrors.sales = res.extend.errorMsg.sales;
                        this.serverValidErrors.price = res.extend.errorMsg.price;
                        this.serverValidErrors.maker = res.extend.errorMsg.maker;
                        this.serverValidErrors.stock = res.extend.errorMsg.stock;
                    }
                })
            } else {
                this.$message({//弹出更新失败信息
                    type: "error",
                    message: "验证失败,不提交"
                })
                return false
            }
        })
    }
}

==========修改add()方法,清空错误信息=========

add() {
    this.dialogVisible = true
    this.form = {}
    this.$refs['form'].resetFields()//将上传验证消息,清空
    this.serverValidErrors = {}
    },

========修改对话框,显示后台返回的校验错误信息========

<!--添加家居的弹窗
说明:
1. el-dialog :v-model="dialogVisible" 表示对话框, 和dialogVisible 变量双向绑定,控制是否
显示对话框
2. el-form :model="form" 表示表单,数据和form 数据变量双向绑定
3. el-input v-model="form.name" 表示表单的input 空间,名字为name 需要和后台Javabean
属性一致
-->
<el-dialog title="提示" v-model="dialogVisible" width="30%">
    <el-form :model="form" :rules="rules" ref="form" label-width="120px">
        <el-form-item label="家居名" prop="name">
            <el-input v-model="form.name" style="width: 60%"></el-input>
            {{ serverValidErrors.name }}
        </el-form-item>
        <el-form-item label="厂商" prop="maker">
            <el-input v-model="form.maker" style="width: 60%"></el-input>
            {{ serverValidErrors.maker }}
        </el-form-item>
        <el-form-item label="价格" prop="price">
            <el-input v-model="form.price" style="width: 60%"></el-input>
            {{ serverValidErrors.price }}
        </el-form-item>
        <el-form-item label="销量" prop="sales">
            <el-input v-model="form.sales" style="width: 60%"></el-input>
            {{ serverValidErrors.sales }}
        </el-form-item>
        <el-form-item label="库存" prop="stock">
            <el-input v-model="form.stock" style="width: 60%"></el-input>
            {{ serverValidErrors.stock }}
        </el-form-item>
    </el-form>
    <template #footer>
        <span class="dialog-footer">
            <el-button @click="dialogVisible = false">取消</el-button>
            <el-button type="primary" @click="save">确定</el-button>
        </span>
    </template>
</el-dialog>

完成测试: 添加家居表单后端校验

2、这时, 后台返回添加失败的提示信息 

启动项目前台ssm_vue 

浏览器: http://localhost:10000/

测试页面效果 

 

测试完毕后, 记得恢复valid 的正确写法 

本项目就已经全部完成

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

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

相关文章

Java14-常用类:字符串,日期类,比较器

一&#xff1a;字符串&#xff1a;String 1.概述&#xff1a; String&#xff1a;字符串&#xff0c;使用一对""引起来表示。 1.String 声明 为final的&#xff0c;不可被继承 2.String 实现了Serializable接口&#xff1a;表示字符串是支持序列化的。 实现了Co…

多线程相关高频面试题

一、线程的基础知识 1、线程和进程的区别&#xff1f; 进程是正在运行程序的实例&#xff0c;进程中包含了线程&#xff0c;每个线程执行不同的任务。不同的进程使用不同的内存空间&#xff0c;在当前进程下的所有线程可以共享内存空间。线程更轻量&#xff0c;线程上下文切换…

GPIO口输出与输入模式的理解

问题&#xff1f;看GPIO的结构图&#xff0c;发现&#xff1a; 上拉输入电流是从引脚流入外部 下来输入电流是从引脚流进芯片内部 推挽输出推模式电流是从引脚流入外部 推挽输出挽模式电流是从外部流入内部 输入输出模式都有电流流入流出&#xff0c;为什么还要分为输入输出模…

K8s排错之浏览器打不开K8s Dashboard

一、问题 10.0.0.10 通常会使用加密技术来保护您的信息。Chrome 此次尝试连接到 10.0.0.10 时&#xff0c;该网站发回了异常的错误凭据。这可能是因为有攻击者在试图冒充 10.0.0.10&#xff0c;或者 Wi-Fi 登录屏幕中断了此次连接。请放心&#xff0c;您的信息仍然是安全的&am…

【饿了么UI】elementUI密码框图标实现睁眼和闭眼效果(阿里巴巴iconfront图标库vue项目本地引用)

elementUI中输入框的密码框属性&#xff0c; 默认是一个始终睁眼的图标&#xff0c;测试今天提bug要有闭眼效果&#xff08;无大语&#xff09;… 因为elementUI中的icon没有闭眼的&#xff0c;所以还要去iconfront下载引入 效果图&#xff1a; 点击后 一、下载图标 http…

【LeetCode】138. 复制带随机指针的链表

题目链接&#xff1a;https://leetcode.cn/problems/copy-list-with-random-pointer/description/ &#x1f4d5;题目要求&#xff1a; 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。…

基于springboot+mybatis-puls+mysql+html实现大学生创新创业管理系统

基于springbootmybatis-pulsmysqlhtml实现大学生创新创业管理系统 一、系统介绍1、系统主要功能&#xff1a;2.涉及技术框架&#xff1a;3.本项目所用环境&#xff1a; 二、功能展示三、其它系统四、获取源码 一、系统介绍 1、系统主要功能&#xff1a; 学生&#xff1a;申报…

自更新参数web接口预热工具

痛点 日常上线流程中经常需要对接口进行预热&#xff0c;因为服务器每次启动后都有一定次数访问失败&#xff0c;如果不处理将此请求直接抛出&#xff0c;会降低用户体验。当服务器数量较少时&#xff0c;我们可以在发布机器后&#xff0c;待机器启动使用本地hosts更改IP&…

20230510MTCNN3

MTCNN数据制作 - 1 多任务 分类任务 回归任务 模型增加任务&#xff0c;其实就是增加输出 级联即减少了 数据量&#xff0c;又增加了 模型的精度 级联可以让网络变得越快 越好 单独来看这三个网络&#xff0c;它们的效果不会好&#xff0c;因为网络太浅了 但&#xff0c;当…

【嵌入式烧录刷写文件】-1.3-删除/修改Motorola S-record(S19/SREC/mot/SX)文件中指定地址范围内的数据

案例背景&#xff08;共6页精讲&#xff09;&#xff1a; 有如下一段S19文件&#xff0c;如何“自动”地完成地址范围0x9110-0x9113数据的删除或修改。 S0110000486578766965772056312E30352EA6 S123910058595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717273747576775B…

t检验前世今生

1、背景 t检验是科研中非常常用的一种方法和手段&#xff0c;但是理解到位的人并不多&#xff0c;虽然这也不影响其使用。本文主要目的在于将与t检验有关的前前后后都讲明白。 2、补充知识 理解t检验&#xff0c;我们需要补充一些统计学有关的先验知识。 2.1 正态分布 概率…

【文本三剑客】SED

SED 一、sed编辑器1.2sed简介1.3sed工作流程1.4sed命令格式1.5常用选项1.6常用操作 二、sed实验2.1打印内容2.2删除行2.3替换2.4插入 一、sed编辑器 1.2sed简介 sed是一种流编辑器&#xff0c;流编辑器会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。sed编辑器可…

链表详解 - C语言描述

目录 认识链表 链表的分类 链表的实现 单链表的增删查改 增操作 删操作 查操作 改操作 带头双向循环链表 认识链表 链表是一种物理存储上非连续&#xff0c;数据元素的逻辑顺序通过链表中的指针链接次序&#xff0c;实现的一种线性存储结构。链表由一系列节点(结点)组…

最火爆ChatGPT知识星球分享,开启与GPT的神奇之旅

今天给大家介绍的是一个讲解ChatGPt的知识星球&#xff0c;这个星球是专门为ChatGPT爱好者和AI绘画感兴趣的朋友们打造的。这个知识星球主题是关于ChatGPT的&#xff0c;旨在提供一个交流、学习和探索GPT的平台。 这里有一个强大的阵容&#xff0c;汇集了许多对人工智能和自然…

.Net平台下OpenGL绘制图形(2)(VS2019,Winform,C#)

本节主要讲诉图形绘制的原理&#xff0c;使用介绍和代码演示。 原理介绍 我们先来讲讲OpenGL的图形绘制。其实&#xff0c;所有的图形都是由许多个小图形连接而成的。你可以理解为是图片的像素&#xff0c;一张彩图是由很多个色彩不一的像素点组合而成。要实现色彩绚丽的图形设…

安装旧版本chrome 浏览器方法

1、下载 国内推荐下载网址&#xff1a;https://www.slimjet.com/chrome/google-chrome-old-version.php 我自己目前再用的103.0.5060.53版本&#xff0c;也可以到我网盘下载[若失效可联系我更新]。 2、安装之前&#xff0c;请先卸载干净当前版本 打开控制面板&#xff0c;找打…

挣值管理专题

挣值管理 基本思路 该方法的基本思想是应用统计学的原理&#xff0c;通过引进一个中间变量即“挣值”来帮助项目管理者分析项目成本的变动情况&#xff0c;并给出项目成本与工期相关变化的信息及对项目成本发展趋势作出预测与决策。 挣值的定义 挣值是一个表示项目“已完成作业…

Linux多路复用机制原理分析--select/poll

前言 Linux访问设备的IO模型主要有五种&#xff0c;分别是非阻塞IO模型、阻塞IO模型、IO多路复用模型、信号驱动模型以及异步IO模型。本文主要分析IO多路复用模型&#xff0c;Linux下的IO多路复用模型主要有select/poll/epoll等机制实现。 IO多路复用模型可以实现以非阻塞的方…

Dijkstra算法图解,C++实现Dijkstra算法

目录 Dijkstra算法简介数据结构抽象初始化开始计算第一轮计算第二轮计算第三轮计算第四轮计算算法总结 C实现Dijkstra算法 Dijkstra算法简介 Dijkstra算法计算是从一个顶点到其余各顶点的最短路径算法&#xff0c;解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起…

1929-2022年全球气象站点的逐日平均能见度

气象数据是我们在各项研究中都非常常用的数据&#xff0c;之前我们分享过全球范围的1929-2022年的具体到气象站点的逐日气象数据&#xff0c;包括平均气温、最高气温、最低气温、平均风速&#xff08;可查看之前的文章获取&#xff09;。 本次我们带来的是全球范围的1929-2022…