• 前后端分离


    接:Servlet的日常开发(基于场景)_林纾໌້ᮨ的博客-CSDN博客

    目录

    一、模板技术

    二、前后端分离

    总结—图析:

    三、Java中为什么需要这么多类

    1.承载数据的对象

    2.承载流程的对象

    对上面代码重构,分一下包:

    四、浏览器以JSON格式把数据给服务器

    1)通过JSON方式发送一个请求体

    2)从请求体中把数据读出

    五、eg:在浏览器中看到一个页面的简单架构


          观察上个博客中的代码可知,拼接HTML是比较麻烦的(都是字符串操作),需要手写写出所有字符串,可以有两种技术来改善:

    1.模板技术(jsp、thymeleaf...)

    2.前后端分离(后端只负责提供数据,以JSON格式,前端JS修改DOM树)

    一、模板技术

    提前把HTML的内容放在一个文件中

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>模板</title>
    </head>
    <body>
        <h1>你好 {{ target }}</h1>
    </body>
    </html>

    <!--这里写在项目的目录下,不写在webapp目录下-->

    1. //Template:模板
    2. //模板模式:提前写好一个文件(template.html)作为模板
    3. public class WithTemplate {
    4. public static void main(String[] args) throws IOException {
    5. try(FileInputStream is=new FileInputStream("template.html")){
    6. try(Reader reader=new InputStreamReader(is,"UTF-8")){
    7. char[] buf=new char[1024];
    8. int n=reader.read(buf);
    9. String template=new String(buf,0,n);
    10. String s=template.replace("{{ target }}","世界");
    11. System.out.println(s);
    12. }
    13. }
    14. }
    15. }

    二、前后端分离

    前端通过ajax技术,从后端(通过HTTP协议)读取数据(数据的格式是JSON为主)

    eg:对录入成绩前后端分离

    静态资源:

    1. <!DOCTYPE html>
    2. <html lang="zh-hans">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>成绩列表</title>
    6. </head>
    7. <body>
    8. <table>
    9. <tbody>
    10. </tbody>
    11. </table>
    12. <!--前端要去后端去取一定需要一个js-->
    13. <script>
    14. function render(students) {
    15. var tbody = document.querySelector('tbody');
    16. for (var i in students) {
    17. var s = students[i];
    18. var tr = `<tr><td>${s.姓名}</td><td>${s.成绩}</td></tr>`;
    19. tbody.innerHTML = tbody.innerHTML + tr;
    20. }
    21. }
    22. var xhr = new XMLHttpRequest();//构建一个ajax出来
    23. xhr.open("GET", "/data/grade-list.json");//把这个目录下的数据给我//注意下面动态时的路径写的是/grade-list.json
    24. //前端只用读取json数据,读取出来后通过DOM树的修改操作把它加到tbody中
    25. xhr.onload = function() {
    26. // this.responseText 是 JSON 格式的字符串,是我们得到的响应体
    27. // 进行 JSON 的反序列(进行JSON解码)
    28. var students = JSON.parse(this.responseText);
    29. // 进行渲染操作 (渲染:render)(进行DOM树的修改,即渲染操作)
    30. render(students);
    31. }
    32. xhr.send();//发送
    33. </script>
    34. </body>
    35. </html>
    1. /**
    2. * .json下的静态资源改为动态资源
    3. **/
    4. @WebServlet("/grade-list.json")
    5. public class GradeListJsonServlet extends HttpServlet {
    6. private final HashMap<String,Integer> gradeMap=new HashMap<>();
    7. //初始化
    8. @Override
    9. public void init() throws ServletException {
    10. gradeMap.put("小A",100);
    11. gradeMap.put("小B",90);
    12. gradeMap.put("小C",80);
    13. }
    14. @Override
    15. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    16. //输出:json格式
    17. resp.setCharacterEncoding("utf-8");
    18. resp.setContentType("application/json");//json格式的输出
    19. PrintWriter writer = resp.getWriter();//把小A...这组数据类似拼接成开心超人那些属性那样的格式
    20. //调用getJSON方法进行拼接操作
    21. String json=getJSON();
    22. writer.println(json);
    23. }
    24. //手动写拼接方法
    25. private String getJSON() {
    26. StringBuilder sb=new StringBuilder();
    27. sb.append("[");
    28. for (Map.Entry<String,Integer> entry: gradeMap.entrySet()) {
    29. String name= entry.getKey();
    30. int value=entry.getValue();
    31. sb.append("{")
    32. .append("\"姓名\": ")
    33. .append("\"")
    34. .append(name)
    35. .append("\",")
    36. .append("\"成绩\": ")
    37. .append(value)
    38. .append("},");
    39. }
    40. sb.delete(sb.length()-1,sb.length());
    41. sb.append("]");
    42. return sb.toString();
    43. }
    44. }

    上面改动态资源是用拼接json格式自己拼接的,比较麻烦,可以引用别人提供好的方法去做:引入一个第三方包:

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.12.4</version>
    </dependency>
    
    1. /**
    2. * .json下的静态资源改为动态资源
    3. **/
    4. @WebServlet("/grade-list.json")
    5. public class GradeListJsonServlet extends HttpServlet {
    6. // private final HashMap<String,Integer> gradeMap=new HashMap<>();
    7. List<Map<String,Object>> gradeList=new ArrayList<>();//list里面放map
    8. //初始化
    9. @Override
    10. public void init() throws ServletException {
    11. // gradeMap.put("小A",100);
    12. // gradeMap.put("小B",90);
    13. // gradeMap.put("小C",80);
    14. {
    15. Map<String,Object> s=new HashMap<>();
    16. s.put("姓名","小王");
    17. s.put("成绩",100);
    18. gradeList.add(s);
    19. }
    20. {
    21. Map<String,Object> s=new HashMap<>();
    22. s.put("姓名","小李");
    23. s.put("成绩",90);
    24. gradeList.add(s);
    25. }
    26. {
    27. Map<String,Object> s=new HashMap<>();
    28. s.put("姓名","小张");
    29. s.put("成绩",80);
    30. gradeList.add(s);
    31. }
    32. }
    33. @Override
    34. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    35. //输出:json格式
    36. resp.setCharacterEncoding("utf-8");
    37. resp.setContentType("application/json");//json格式的输出
    38. PrintWriter writer = resp.getWriter();//把小A...这组数据类似拼接成开心超人那些属性那样的格式
    39. //调用getJSON方法进行拼接操作
    40. String json=getJSON();
    41. writer.println(json);
    42. }
    43. //2.调用别人写好的东西
    44. //数组或线性表方式存储(要按协议来,map方式存储是不遵守协议的,打印不一样)//期望打印达到的效果是上面静态.json文件中哪个效果
    45. private String getJSON() {
    46. ObjectMapper om=new ObjectMapper();
    47. try{
    48. // return om.writeValueAsString(gradeList);
    49. return om.writerWithDefaultPrettyPrinter().writeValueAsString(gradeList);//与上面一样,只是网页显示上带一些换行和缩进,对数据格式没影响
    50. }catch (JsonProcessingException e){
    51. throw new RuntimeException(e);
    52. }
    53. }
    54. }

          小结:index.html通过构建ajax GET(用户直接GET)请求动态资源/grade-list.json,动态资源根据数据修改DOM树(将index.html内容按所想要的方式呈现)

    继续改造:

    对象和Map其实是一回事,JSON直接支持定义好一个类往里面放东西,所以可以:

    class  Grade{

            姓名;

            成绩;

    }

    List<Grade>

    1. public class Grade {
    2. public String 姓名;
    3. public int 成绩;
    4. }
    1. /**
    2. * .json下的静态资源改为动态资源(动态获取json的一个工程)
    3. **/
    4. @WebServlet("/grade-list.json")
    5. public class GradeListJsonServlet extends HttpServlet {
    6. // private final HashMap<String,Integer> gradeMap=new HashMap<>();
    7. List<Map<String,Object>> gradeList=new ArrayList<>();//list里面放map
    8. List<Grade> gradeList2=new ArrayList<>();
    9. //初始化
    10. @Override
    11. public void init() throws ServletException {
    12. {
    13. Grade g=new Grade();
    14. g.姓名="胡图图";
    15. g.成绩=60;
    16. gradeList2.add(g);
    17. }
    18. {
    19. Grade g=new Grade();
    20. g.姓名="张小丽";
    21. g.成绩=98;
    22. gradeList2.add(g);
    23. }
    24. // gradeMap.put("小A",100);
    25. // gradeMap.put("小B",90);
    26. // gradeMap.put("小C",80);
    27. {
    28. Map<String,Object> s=new HashMap<>();
    29. s.put("姓名","小王");
    30. s.put("成绩",100);
    31. gradeList.add(s);
    32. }
    33. {
    34. Map<String,Object> s=new HashMap<>();
    35. s.put("姓名","小李");
    36. s.put("成绩",90);
    37. gradeList.add(s);
    38. }
    39. {
    40. Map<String,Object> s=new HashMap<>();
    41. s.put("姓名","小张");
    42. s.put("成绩",80);
    43. gradeList.add(s);
    44. }
    45. }
    46. @Override
    47. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    48. //输出:json格式
    49. resp.setCharacterEncoding("utf-8");
    50. resp.setContentType("application/json");//json格式的输出
    51. PrintWriter writer = resp.getWriter();//把小A...这组数据类似拼接成开心超人那些属性那样的格式
    52. //调用getJSON方法进行拼接操作
    53. String json=getJSON();
    54. writer.println(json);
    55. }
    56. //2.调用别人写好的东西
    57. //数组或线性表方式存储(要按协议来,map方式存储是不遵守协议的,打印不一样)//期望打印达到的效果是上面静态.json文件中哪个效果
    58. private String getJSON() {
    59. ObjectMapper om=new ObjectMapper();
    60. try{
    61. //将gradeList2序列化
    62. return om.writerWithDefaultPrettyPrinter().writeValueAsString(gradeList2);
    63. return om.writeValueAsString(gradeList);
    64. // return om.writerWithDefaultPrettyPrinter().writeValueAsString(gradeList);//与上面一样,只是网页显示上带一些换行和缩进,对数据格式没影响
    65. }catch (JsonProcessingException e){
    66. throw new RuntimeException(e);
    67. }
    68. }
    69. }

          问题:一般java中不直接用中文起名,如Grade类中的姓名成绩,一般用英文起名,但是修改代码会出问题,即你在Grade修改成英文时,访问grade-list.json显示修改成功,但是调用index显示是错误显示的(因为html中写的是姓名成绩两个中文)。

    解决:通过注解的方式

          此时数据全部处理OK,可以将数据改到数据库中(利用之前数据库grades表,直接在数据库中查询),此时代码:

    1. /**
    2. * GetGradeList:从数据源(目前是MySQL)中,获取对应的数据
    3. * 只要调用这个方法就可以从数据库把数据查出来
    4. */
    5. public class GetGradeList {
    6. List<Grade> list=new ArrayList<>();
    7. public List<Grade> getList(){
    8. try (Connection c= DBUtil.connection()){
    9. String sql="select name,grade from grades order by id";
    10. try(PreparedStatement ps=c.prepareStatement(sql)){
    11. try (ResultSet rs= ps.executeQuery()){
    12. while (rs.next()){
    13. Grade grade=new Grade();
    14. grade.name=rs.getString("name");
    15. grade.score=rs.getInt("grade");
    16. list.add(grade);
    17. }
    18. }
    19. }
    20. }catch (SQLException e){
    21. throw new RuntimeException(e);
    22. }
    23. return list;
    24. }
    25. }
    1. @WebServlet("/grade-list.json")
    2. public class GradeListJsonServlet extends HttpServlet {
    3. //初始化GetGradeList类:(该类负责从数据库中查出数据并放在了该类的list中返回)
    4. private final GetGradeList getGradeList=new GetGradeList();
    5. @Override
    6. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    7. //输出:json格式
    8. resp.setCharacterEncoding("utf-8");
    9. resp.setContentType("application/json");//json格式的输出
    10. PrintWriter writer = resp.getWriter();//把小A...这组数据类似拼接成开心超人那些属性那样的格式
    11. //调用getJSON方法进行拼接操作
    12. String json=getJSON();
    13. writer.println(json);
    14. }
    15. //调用别人写好的东西
    16. //将数据来源改为从数据库拿(与其他没有差别,只是这里数据源出现区别,变成了数据库中读到的数据)
    17. private String getJSON() {
    18. ObjectMapper om=new ObjectMapper();
    19. try{
    20. List<Grade> list=getGradeList.getList();//获取GetGradeList类的对象getGradeList调用到的数据(name,score)给list
    21. return om.writerWithDefaultPrettyPrinter().writeValueAsString(list);//将数据序列化(调用别人写好的方法将数据转化为了对应的json格式)
    22. }catch (JsonProcessingException e){
    23. throw new RuntimeException(e);
    24. }
    25. }
    26. }

    总结—图析:

    三、Java中为什么需要这么多类

    java中一切皆对象,可分成两种:

    1.承载数据的对象

          eg:宫保鸡丁、鱼香肉丝、麻婆豆腐:是加工出来的产品

          承载数据重要的是属性,如上面Grade类,类中只有属性name  score,完全无方法,这就是承载数据的对象,以数据位主。

    数据承担的是各种各样的序列化如上面代码json格式的序列化
    数据为什么序列化序列化的目标是存储和搬运(酒厂中的酒才需要搬运,厂房不要)
    一般承载数据的对象中的方法equals、comparable、hashCode(产品间才有比较的价值)

    2.承载流程的对象

          eg:前台、服务员、厨师:流程,是职责中的一部分

          承载流程重要的是方法,如上面代码GetGradeList和GradeListJsonServlet就是流程,GetGradeList就是从数据库中把对象查出来这个过程,就是个流程/方法。

          总而言之,实际开发中,有偏向数据的对象,有偏向属性的对象,一般也没有纯数据或纯方法,讲求一个偏向性来理解。

    一般使用流程时主要是单例的

    1)MVC:Model(模型-厨师)+View(展示)+Controller(控制器)

    2)Controller(前台)+Service(服务员)+DataAccessObject(数据访问对象DAO(厨师))

          Controller负责承接HTTP,即对接过来的客户点什么菜,DAO,即dao对象,库管,从数据库(仓库)里面把东西拿出来,Service上面代码无,我们流程简单,只是拿出来就给前台了,若业务复杂,就需要服务员参与。

    对上面代码重构,分一下包:

          GetGradeList放在dao层/包下,改名GradeDao,可以给看的人快速知道这是从数据库取东西的一个类,数据放在model层下,model包下放承载数据职责的类,util包下放DBUtil工具。

    Model类型对象也被称为DataObject(DO),注意不要和DAO混起来。

    四、浏览器以JSON格式把数据给服务器

          之前代码是服务器以json格式把数据给浏览器,浏览器以表单(form)形式将数据交给服务器,但还可以以json方式发送过去,如下:

    1)通过JSON方式发送一个请求体

    1. <!DOCTYPE html>
    2. <html lang="zh-hans">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>只做json数据的发送,即浏览器以json形式把数据发给服务器</title>
    6. </head>
    7. <body>
    8. <button>发送 JSON 到服务器</button>
    9. <script>
    10. // 只能使用 ajax 做到
    11. var btn = document.querySelector('button');
    12. //绑定一个事件
    13. btn.onclick = function() {
    14. var students = [
    15. { 姓名: "小赵", 成绩: 83 },
    16. { 姓名: "小钱", 成绩: 84 },
    17. { 姓名: "小李", 成绩: 85 }
    18. ];
    19. //写js代码,key可以不加引号,所以姓名那里没加引号
    20. // 1. 先 JS 的数据变成 String 类型 —— JSON 序列化【准备数据】
    21. // 反序列化: JSON.parse(...);
    22. // 序列化: JSON.stringify
    23. //对students序列化为json格式的内容
    24. var s = JSON.stringify(students);
    25. console.log(s);//通过打印观察,s就是一串字符串(JSON格式的)
    26. console.log(students);//通过打印观察,students就是上面所写的那一组数据
    27. // 2. 发送,因为要在请求体中携带 JSON 数据,所以只能使用 POST 方法
    28. var xhr = new XMLHttpRequest();
    29. xhr.open("POST", "/get-json-from-request-body");
    30. xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");//设置请求头
    31. xhr.send(s); // 发送。send 的参数,就是请求体(传入s就代表作为请求体发送)
    32. }
    33. </script>
    34. </body>
    35. </html>

    分析:students和json之间是相互转化的,String(JSON下的/JSON格式)到数据(json格式解析后的内容)是在JS中调的是JSON.parse(...),叫做解序列化过程,数据到String,在JS中是JSON.stringify(...),叫做序列化过程。(数据->String在java中用ObjectMapper.writeValueAsString(...),String->数据在java中用ObjectMapper.readValue(...)

    2)从请求体中把数据读出

    1. package com.wy4.servlet;
    2. import com.fasterxml.jackson.databind.ObjectMapper;
    3. import com.wy4.model.Grade;
    4. import javax.servlet.ServletException;
    5. import javax.servlet.annotation.WebServlet;
    6. import javax.servlet.http.HttpServlet;
    7. import javax.servlet.http.HttpServletRequest;
    8. import javax.servlet.http.HttpServletResponse;
    9. import java.io.IOException;
    10. import java.io.InputStream;
    11. import java.util.List;
    12. /**
    13. * @author 美女
    14. * @date 2022/06/28 21:58
    15. **/
    16. @WebServlet("/get-json-from-request-body")
    17. public class GetJsonFromRequestBodyServlet extends HttpServlet {
    18. //支持的方法是POST方法,因为是POST请求提交的
    19. @Override
    20. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    21. //1.请求体放在req.getInputStream()的输入流中
    22. InputStream inputStream = req.getInputStream();
    23. //2.利用Jackson进行解析(这个需要导依赖)
    24. ObjectMapper om=new ObjectMapper();
    25. //3.3
    26. List<Grade> list = om.readValue(inputStream, om.getTypeFactory().constructParametricType(List.class, Grade.class));
    27. for (Grade grade:list) {
    28. System.out.println(grade);
    29. }
    30. //结果:Grade{name='小赵', score=83}
    31. //Grade{name='小钱', score=84}
    32. //Grade{name='小李', score=85}
    33. // //3.2. 读一行
    34. // List list1 = om.readValue(inputStream, List.class);//数据里主要是List.class。至于外面的list会自己帮我解析,最终就会解析出来
    35. //写的话操作是write,在GradeListJsonServlet类里有
    36. // //结果:[{姓名=小赵, 成绩=83}, {姓名=小钱, 成绩=84}, {姓名=小李, 成绩=85}]
    37. // System.out.println(list1);
    38. // //3.1.多个读
    39. // Object o = om.readValue(inputStream, om.getTypeFactory().constructParametricType(List.class, Grade.class));
    40. // System.out.println(o);
    41. //结果:[Grade{name='小赵', score=83}, Grade{name='小钱', score=84}, Grade{name='小李', score=85}]
    42. }
    43. }
    public class Grade {
        //注解意思是写到json里面是用姓名or成绩作为它的key
        @JsonProperty("姓名")
        public String name;
        @JsonProperty("成绩")
        public int score;
    
        @Override
        public String toString() {
            return "Grade{" +
                    "name='" + name + '\'' +
                    ", score=" + score +
                    '}';
        }
    }

    五、eg:在浏览器中看到一个页面的简单架构

    在前后端分离的场景下:

          xxx.html是用户看到的主页面,是入口。页面由多个资源组成,一般就是css做样式资源,js做逻辑资源,除此之外,在前后端分离场景下,数据往往是后端以xxx.json形式提供好的。

    eg:其他代码小练习:

    1. <!DOCTYPE html>
    2. <html lang="zh-hans">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>成绩列表</title>
    6. </head>
    7. <body>
    8. <div>
    9. <input type="text" id="name" name="name" placeholder="姓名">
    10. <input type="text" id="score" name="score" placeholder="成绩">
    11. <!--button的type是button时,代表不是提交form表单的功能,我们通过js添加点击事件处理自行控制流程
    12. (即框架中浏览器->服务器的操作方法2类似,通过js手动把数据序列化,通过POST传出去)-->
    13. <button type="button">保存</button><!--由于button本身是提交表单,现在不需要,默认type处是submit,
    14. 现在改成button,默认就不提交了(失去了提交表单功能),type可写可不写【此时就需要js添加点击事件处理来处理它】-->
    15. </div>
    16. <table>
    17. <tbody>
    18. </tbody>
    19. </table>
    20. <!--前端要去后端去取一定需要一个js-->
    21. <script>
    22. <!-- function render(students) {-->
    23. <!-- var tbody = document.querySelector('tbody');-->
    24. <!-- for (var i in students) {-->
    25. <!-- var s = students[i];-->
    26. <!-- var tr = `<tr><td>${s.姓名}</td><td>${s.成绩}</td></tr>`;-->
    27. <!-- tbody.innerHTML = tbody.innerHTML + tr;-->
    28. <!-- }-->
    29. <!-- }-->
    30. <!-- var xhr = new XMLHttpRequest();//构建一个ajax出来-->
    31. <!-- xhr.open("GET", "/grade-list.json");//把这个目录下的数据给我-->
    32. <!-- //前端只用读取json数据,读取出来后通过DOM树的修改操作把它加到tbody中-->
    33. <!-- xhr.onload = function() {-->
    34. <!-- // this.responseText 是 JSON 格式的字符串,是我们得到的响应体-->
    35. <!-- // 进行 JSON 的反序列(进行JSON解码)-->
    36. <!-- var students = JSON.parse(this.responseText);-->
    37. <!-- // 进行渲染操作 (渲染:render)(进行DOM树的修改,即渲染操作)-->
    38. <!-- render(students);-->
    39. <!-- }-->
    40. <!-- xhr.send();//发送-->
    41. //通过添加事件处理进行的处理:
    42. var btn=document.querySelector('button');//找到button
    43. btn.onclick=function(){
    44. var name=document.querySelector('#name').value;
    45. var score=document.querySelector('#score').value;
    46. var data={
    47. 姓名:name,
    48. 成绩:score
    49. };
    50. var s=JSON.stringify(data);
    51. var xhr2=new XMLHttpRequest();
    52. xhr2.open('post','/save.json');
    53. xhr2.onload=function(){
    54. //不考虑错的情况了,所以我们手动重定向【一旦返回代表保存成功,保存成功用一个重定向(这是前端重定向,直接把location一改就会自动重定向)】
    55. location='/';
    56. }
    57. xhr2.send(s);
    58. }
    59. </script>
    60. </body>
    61. </html>
    1. @WebServlet("/save.json")
    2. public class SaveServlet extends HttpServlet {
    3. @Override
    4. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    5. ObjectMapper om=new ObjectMapper();
    6. Grade grade=om.readValue(req.getInputStream(),Grade.class);
    7. System.out.println(grade);
    8. //TODO:保存到数据库中
    9. try(Connection c= DBUtil.connection()){
    10. String sql="insert into grades(name,grade) values(?,?)";
    11. try(PreparedStatement ps=c.prepareStatement(sql)){
    12. ps.setString(1,grade.name);
    13. ps.setInt(2,grade.score);
    14. ps.executeUpdate();
    15. }
    16. }catch (SQLException e){
    17. throw new RuntimeException(e);
    18. }
    19. resp.setCharacterEncoding("utf-8");
    20. resp.setContentType("application/json");
    21. PrintWriter writer = resp.getWriter();
    22. HashMap<String,String> result=new HashMap<>();
    23. result.put("结果","正确");
    24. String s=om.writeValueAsString(result);
    25. writer.println(s);
    26. }
    27. }

  • 相关阅读:
    maya blendshape
    【五、接口自动化测试】5分钟掌握python + requests接口测试
    数据库基本结论
    分布式数据库HBase(林子雨慕课课程)
    戏说领域驱动设计(十三)——核心架构
    Python: 使用pytest进行代码测试
    什么是 Nacos
    抵押品管理服务市场现状及未来发展趋势分析
    java毕业生设计众筹平台网站计算机源码+系统+mysql+调试部署+lw
    Element UI 多选表格【翻页多选】简易版(不支持翻页多选数据反显)
  • 原文地址:https://blog.csdn.net/m0_58006481/article/details/125481858