从 0 开始实现一个 SpringBoot + Vue 项目
从 0 开始实现一个 SpringBoot + Vue 项目
- 从 0 开始实现一个 SpringBoot + Vue 项目
-
- 软件和工具
- 创建 SpringBoot 后端项目
- 创建 MySQL 数据库
- 配置文件
- 实现增删改查接口
-
- Model 层
- mapper 层
- service 层
- controller 层
- 测试
- 实现项目功能接口
-
- 代码
- 测试
- 创建 Vue 前端
-
- 安装 Node.js
- 配置 npm 镜像
- 安装脚手架
- 创建并配置项目
- 项目结构
- Vue 组件结构
- Vue 组件调用与传值
- Vue 组件的生命周期
- 测试 Vue 程序
- 需求分析
- 实现项目页面
-
- 框架搭建
- 项目配置
- 主界面设计
- 设置路由
- 内容组件设计
- 发送组件设计
- 分页面设计
-
- 排行页面 LikesSortedView.vue
- 最新页面 NewestView.vue
- 随机页面 RandomView.vue
- 项目启动
- 源码下载
从 0 开始实现一个 SpringBoot + Vue 项目
参考:梦想屋A
软件和工具
- 后端开发软件:IntelliJ IDEA
- 前端开发软件:Visual Studio Code
- 后端框架:SpringBoot
- 后端语言:Java
- 前端框架:Vue
这是后面要用的妙妙小工具:
- 服务器连接工具:Termius
- 数据库:MySQL
- 数据库管理工具:Navicat Premium
- 数据库连接工具:MyBatis
- API 文档生成工具:Swagger
- API 文档美化工具:Knife4j
- UI 组件库:Element
- 网络请求库:Axios
- 字体处理库:Sfntly
- JSON 处理工具:Fastjson
- Java 工具库:Lombok
可以不必全部用这些来做,有很多类似的产品可以替代。
创建 SpringBoot 后端项目
首先我们打开 IDEA,点击新建项目,选择 Spring Initializr,然后在右侧填写项目名称,类型选择 Maven,JDK 版本选择1.8,如下图所示,然后点击下一步。
在新的页面中选择 SpringBoot 版本 3.0.2,引入一些依赖,点击创建。
后面在 pom.xml 中把 Java 版本改回 1.8,SpringBoot 版本改回 2.7.6。
项目结构:
可以看到 SpringBoot 的基础结构有三个文件:
- src/main/java 下的程序入口:DreamHouseApplication
- src/main/resources 下的配置文件:application.properties
- src/test 下的测试入口:DreamHouseApplicationTests
在运行类 DreamHouseApplication 同级目录下创建 controller、service、mapper、model 四个目录:
controller 层:控制层
- 作用是请求和响应控制。
- 负责前后端交互,接受前端请求,调用 service 层,接收 service 层返回的数据,最后返回具体的页面和数据到客户端。
service 层:业务逻辑层
- 作用是完成功能设计。
- 调用 mapper 层接口,接收 mapper 层返回的数据,完成项目的基本功能设计。
mapper 层:数据持久层,也被称为 dao 层
- 作用是访问数据库,向数据库发送 sql 语句,完成数据的增删改查任务。
model 层:数据库实体层,也被称为 entity 层、pojo 层
- 用于存储数据库中的数据,类属性与数据库表字段对应。
- 通常情况下,Model 层使用 ORM(对象关系映射)技术,如 JPA,MyBatis 等与数据库进行交互。
在项目目录中的 pom.xml 配置文件中引入本次项目中需要用到的相关依赖:
<!--MyBatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.0</version></dependency><!--Swagger3--><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency><!--knife4j--><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-ui</artifactId><version>3.0.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><!--JSON--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.15</version></dependency><!--MySQL--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.31</version><scope>runtime</scope></dependency><!--Lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
将这些依赖放到标签内,就接着 SpringBoot 自带的一些依赖下面就行了。
稍微解释一下这些依赖:
-
Lombok:可以通过注解的方式为 Java 类生成许多常用的方法,例如 getter、setter、toString、equals 等,省去了手写这些方法的代码。
-
Swagger:开源的 API 文档生成工具,可以根据代码中的注释自动生成 API 文档。
-
Knife4j:用来美化 Swagger 生成的 API 文档。
-
MyBatis:持久层框架,主要用于简化数据库操作,提高代码可读性,降低数据访问代码的维护难度,提高效率。
-
MySQL:MySQL 官方提供的 Java 用的 JDBC 驱动,它允许 Java 程序通过 JDBC API 连接到 MySQL 数据库。
-
JSON:用来将 json 数据序列化和反序列化的,主要是觉得 SpringBoot 自带的 json 工具不好用。
创建 MySQL 数据库
安装 MySQL 这里就不讲了,这里用 Navicat Premium 连接数据库。
填写连接名、主机IP、端口、用户名和密码等,然后点击连接。
可以先点测试连接,显示连接成功,在点确定。
连接上数据库之后,新建数据库–>新建表–>添加 id、ip、province、time、str、likes 六个字段。
表名为 dream,具体内容如下所示:
这里有两个细节:
- id 要设置自动递增
- likes 设置为无符号
友情链接:mysql自增navicat_navicat怎么设置主键自增
配置文件
让我们回到 SpringBoot,打开刚刚介绍到的配置文件 application.properties 进行项目配置:
server.port = 8087spring.mvc.pathmatch.matching-strategy = ant_path_matcher# Swagger
swagger.enabled = true# MySQL
spring.datasource.url = jdbc:mysql:(服务器IP地址):3306/(数据库名)
spring.datasource.username = (用户名)
spring.datasource.password = (密码)
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
稍微解释一下:
-
server.port:指定项目使用 8087 端口,默认 8080 端口。
-
spring.mvc.pathmatch.matching-strategy:指定 Spring MVC 框架中 URL 路径匹配策略的实现类的类名,ant_path_matcher 是一种路径匹配策略,使用 Ant 风格的通配符来匹配 URL 路径。
-
swagger.enabled:启用 Swagger 工具。
-
spring.datasource.url:指定 MySQL 数据库的 URL。这里的服务器 IP 地址就是主机名,我的是 localhost(前面要带//),数据库名是 dreamhouse,完整格式:jdbc:mysql://localhost:3306/dreamhouse。
-
spring.datasource.username:指定连接 MySQL 数据库使用的用户名。我的是 root。
-
spring.datasource.password:指定连接 MySQL 数据库使用的密码。
-
spring.datasource.driver-class-name:指定连接 MySQL 数据库使用的 JDBC 驱动程序的类名。
这里可能会有注释中文乱码问题,在设置中可以解决,全部换成 UTF-8:
友情链接:【SpringBoot2】读取配置application.properties文件乱码问题解决
第一次用要安装点驱动啥的,点一下就安装好了,没截到图。
配置好以后,可以测试连接:
也可以在 IDEA 看到数据库:
实现增删改查接口
Model 层
回顾一下 model 层:数据库实体层,也被称为 entity 层、pojo 层
- 用于存储数据库中的数据,类属性与数据库表字段对应。
- 通常情况下,Model 层使用 ORM(对象关系映射)技术,如 JPA,MyBatis 等与数据库进行交互。
在 model 目录下新建 Data 类:
package com.example.dream_house.model;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;/*** @BelongsProject:dream_house* @BelongsPackage:com.example.dream_house.model* @Author:Uestc_Xiye* @CreateTime:2023-12-17 16:29:49*/@lombok.Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("数据库字段")
public class Data {@ApiModelProperty(value = "信息所属ID", required = true, example = "1")private int id;@ApiModelProperty(value = "信息来源IP地址", required = true, example = "127.0.0.1")private String ip;@ApiModelProperty(value = "信息来源所属省份", required = true, example = "广东")private String province;@ApiModelProperty(value = "内容发布时间", required = true, example = "2023-12-17 16:58:00")private String time;@ApiModelProperty(value = "梦想内容", required = true, example = "环游世界!")private String str;@ApiModelProperty(value = "点赞数", required = true, example = "52")private int likes;
}
接下来说一下这段代码中的各个注解的作用:
-
@lombok.Data:这是 Lombok 框架提供的注解,它会自动生成 getter、setter、toString、equals、hashCode 等方法。使用该注解可以简化代码,并提高开发效率。
-
@NoArgsConstructor:这也是 Lombok 提供的注解,它会生成一个无参构造器,可以避免手动编写无参构造器。这个注解常用于一些框架或工具的实例化。
-
@AllArgsConstructor:同样是 Lombok 提供的注解,它会生成一个全参构造器,可以避免手动编写全参构造器。这个注解也常用于一些框架或工具的实例化。
-
@ApiModel:这是 Swagger 框架提供的注解,用于描述一个模型类。这个注解的作用是将模型类描述为一个 API 文档的模型,可以通过该注解指定模型类的名称和描述信息。
-
@ApiModelProperty:也是 Swagger 框架提供的注解,用于描述模型类中的属性信息。该注解可以设置属性的名称、描述、是否必需等信息,以便在 Swagger 生成的 API 文档中显示。
-
value:属性的描述信息,用于在 API 文档中显示该属性的作用。
-
required:属性是否必需。当该值为 true 时,表示该属性必须包含在请求中;当该值为 false 时,表示该属性可以为空或者不包含在请求中。
-
example:属性的示例值。用于在 API 文档中显示该属性的样例值,方便开发者理解该属性的类型和取值范围。
-
mapper 层
回顾一下 mapper 层:数据持久层,也被称为 dao 层
- 作用是访问数据库,向数据库发送 sql 语句,完成数据的增删改查任务。
在 mapper 目录下新建 DataMapper 接口:
package com.example.dream_house.mapper;import com.example.dream_house.model.Data;
import org.apache.ibatis.annotations.*;/*** @BelongsProject:dream_house* @BelongsPackage:com.example.dream_house.mapper* @Author:Uestc_Xiye* @CreateTime:2023-12-17 16:36:57*/@Mapper
public interface DataMapper {/*** 信息来源IP地址* @param ip* 信息来源省份* @param province* 信息发出时间* @param time* 信息内容* @param str* 点赞数* @param likes** @return*/@Insert("insert into dream (ip, province, time, str, likes) values(#{ip}, #{province}, #{time}, #{str}, #{likes})")int insert(@Param("ip") String ip,@Param("province") String province,@Param("time") String time,@Param("str") String str,@Param("likes") int likes);/*** 信息id* @param id** @return** property属性对应Data对象中的成员名,column对应select出的字段名。*/@Results({@Result(property = "id", column = "id"),@Result(property = "ip", column = "ip"),@Result(property = "province", column = "province"),@Result(property = "time", column = "time"),@Result(property = "str", column = "str"),@Result(property = "likes", column = "likes")})@Select("select * from dream where id = #{id}")Data findById(@Param("id") int id);/*** 用Data对象来作为传参,这样语句中的#{id}、#{ip}等数据就分别对应Data对象中的id和ip等属性。** @param data*/@Update("update dream set ip=#{ip}, province=#{province}, time=#{time}, str=#{str}, likes=#{likes} where id=#{id}")void update(Data data);/*** 删除该id对应的信息** @param id*/@Delete("delete from dream where id =#{id}")void delete(int id);}
相关注解的作用:
-
@Mapper:是 MyBatis 框架提供的注解,用于标记一个 Java 接口,该接口用于定义数据访问方法。在使用 @Mapper 注解后,MyBatis 会自动扫描该接口,为其创建一个代理对象。该代理对象可以将接口方法与 MyBatis 的 SQL 映射文件中的 SQL 语句进行绑定,并完成数据访问的操作。
-
@Insert:也是 MyBatis 框架提供的注解,该注解的值为 SQL 语句,用于指定插入操作的具体逻辑。该 SQL 语句使用了预处理语句,从而避免了 SQL 注入的问题。
-
@Param:
-
/** */中的内容是 JavaDoc(Java文档注释),它用于对方法进行说明、描述和文档化。
-
在方法中的 @Param 注解用于指定参数的名称,以便在 SQL 语句中使用相应的占位符。
-
-
@Results:用于定义从查询结果集中将查询结果映射为 Java 对象的过程。
-
@Select:同样是 MyBatis 框架提供的注解,该注解的值为 SQL 语句,用于指定查询操作的具体逻辑。
-
@Update:MyBatis 框架提供的注解,用于指定更新操作的 SQL 语句。
-
@Delete:MyBatis 框架提供的注解,用于指定删除操作的 SQL 语句。
service 层
简单回顾一下 service 层:业务逻辑层
- 作用是完成功能设计。
- 调用 mapper 层接口,接收 mapper 层返回的数据,完成项目的基本功能设计。
在 service 目录下新建 DataService 类:
package com.example.dream_house.service;import com.example.dream_house.mapper.DataMapper;
import com.example.dream_house.model.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** @BelongsProject:dream_house* @BelongsPackage:com.example.dream_house.service* @Author:Uestc_Xiye* @CreateTime:2023-12-17 17:04:45*/@Service
public class DataService {@Autowiredprivate DataMapper dataMapper;/*** 新增信息** @param ip* @param province* @param time* @param str* @param likes* @return*/public String insert(String ip, String province, String time, String str, int likes) {dataMapper.insert(ip, province, time, str, likes);return "succeed";}/*** 查询id对应的信息** @param id* @return*/public Data findById(int id) {return dataMapper.findById(id);}/*** 更新信息** @param data*/public void update(Data data) {dataMapper.update(data);}/*** 删除id对应的信息** @param id*/public void delete(int id) {dataMapper.delete(id);}
}
相关注解的作用:
-
@Service:用于标注一个类为 Spring 框架中的一个服务类,该类中通常包含了业务逻辑的实现。使用该注解可以使 Spring 框架自动扫描并将该类实例化,并将其作为服务类注册到容器中,以供其他组件使用。当我们需要在其他类中使用该服务类时,只需要通过依赖注入的方式获取该类的实例即可。
-
@Autowired:用于实现 Spring 框架中的自动装配功能,将需要使用的 Bean 对象注入到指定的属性中。通过使用该注解,可以避免手动创建 Bean 实例和手动注入对象的麻烦。
controller 层
简单回顾一下 controller 层:控制层
- 作用是请求和响应控制。
- 负责前后端交互,接受前端请求,调用 service 层,接收 service 层返回的数据,最后返回具体的页面和数据到客户端。
在 controller 目录下新建 DataController 类:
package com.example.dream_house.controller;import com.example.dream_house.model.Data;
import com.example.dream_house.service.DataService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;/*** @BelongsProject:dream_house* @BelongsPackage:com.example.dream_house.controller* @Author:Uestc_Xiye* @CreateTime:2023-12-17 17:17:36*/@Api(tags = "API接口")
@RestController
@CrossOrigin(origins = "*", maxAge = 3600)
public class DataController {@Autowiredprivate DataService dataService;@ApiOperation("添加完整信息")@PostMapping("/insert")public String insert(@RequestBody Data data) {// @RequestBody注解用来绑定通过http请求中application/json类型上传的数据return dataService.insert(data.getIp(), data.getProvince(), data.getTime(), data.getStr(), data.getLikes());}@ApiOperation("查询id对应的信息")@GetMapping("/findById/{id}")public Data findById(@PathVariable int id) {return dataService.findById(id);}@ApiOperation("更新信息")@PutMapping("/update")public void update(@RequestBody Data data) {dataService.update(data);}@ApiOperation("删除指定id的信息")@DeleteMapping("/delete/{id}")public void deleteUser(@PathVariable int id) {dataService.delete(id);}}
相关注解的作用:
-
@Api:Swagger 的注解之一,用于对 API 接口进行注释和说明。tags 属性是 Swagger 文档中的一个重要属性,可以用来将 API 接口进行分类,方便管理和查找。
-
@RestController:Spring MVC 中的注解之一,用于标识该类是一个基于 RESTful 风格的 Web 服务类。
-
@CrossOrigin:Spring 中的一个注解,用于支持跨域请求。跨域请求通常指在一个域名下的页面中使用 AJAX 技术向不同的域名或端口号的 Web 服务发送请求。
-
@ApiOperation:Swagger 的注解之一,用于对 API 接口中的具体操作进行注释和说明。
-
@PostMapping:Spring MVC 中的注解之一,表示该方法接收 POST 请求。
-
@RequestBody:Spring MVC 中的注解之一,表示该方法接收的请求参数为请求体中的数据。
-
@GetMapping:Spring MVC 中的注解之一,表示该方法接收 GET 请求。
-
@PathVariable:Spring MVC 中的注解之一,表示该方法接收的请求参数为路径参数。
-
@PutMapping:Spring MVC 中的注解之一,表示该方法接收 PUT 请求。
-
@DeleteMapping:Spring MVC 中的注解之一,表示该方法接收 DELETE 请求。
这里要引入 Spring Web,不然在使用这些注解的时候报错无法解析。
测试
预先设置一些数据库内容:
在把上面的四层架构都处理完之后,我们直接启动项目。
运行 DreamHouseApplication,或者运行它的 main 方法。
踩坑 1:Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstre
切换“跳过测试”模式:
友情链接:解决:Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstre
踩坑点2:Consider defining a bean of type ‘com.example.dream_house.mapper.DataMapper’ in your configuration.
18:10:27.233 [Thread-1] DEBUG org.springframework.boot.devtools.restart.classloader.RestartClassLoader - Created RestartClassLoader org.springframework.boot.devtools.restart.classloader.RestartClassLoader@6d1a5149. ____ _ __ _ _/\ / ___'_ __ _ _(_)_ __ __ _
( ( )___ | '_ | '| | ’ / ` |
/ )| |)| | | | | || (| | ) ) ) )
’ || .__|| ||| |, | / / / /
=========||==============|/=///_/
:: Spring Boot :: (v2.7.6)
2023-12-17 18:10:27.843 INFO 11824 --- [ restartedMain] c.e.dream_house.DreamHouseApplication : Starting DreamHouseApplication using Java 1.8.0_192 on LAPTOP-P25TKBR2 with PID 11824 (C:Users81228DocumentsProgramJava ProjectDreamHousedream_house argetclasses started by 81228 in C:Users81228DocumentsProgramJava ProjectDreamHousedream_house)
2023-12-17 18:10:27.844 INFO 11824 --- [ restartedMain] c.e.dream_house.DreamHouseApplication : No active profile set, falling back to 1 default profile: "default"
2023-12-17 18:10:27.975 INFO 11824 --- [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2023-12-17 18:10:27.975 INFO 11824 --- [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2023-12-17 18:10:29.551 WARN 11824 --- [ restartedMain] o.m.s.mapper.ClassPathMapperScanner : No MyBatis mapper was found in '[com.example.dream_house]' package. Please check your configuration.
2023-12-17 18:10:30.264 INFO 11824 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8087 (http)
2023-12-17 18:10:30.265 INFO 11824 --- [ restartedMain] o.a.catalina.core.AprLifecycleListener : An older version [1.2.24] of the Apache Tomcat Native library is installed, while Tomcat recommends a minimum version of [1.2.30]
2023-12-17 18:10:30.265 INFO 11824 --- [ restartedMain] o.a.catalina.core.AprLifecycleListener : Loaded Apache Tomcat Native library [1.2.24] using APR version [1.7.0].
2023-12-17 18:10:30.265 INFO 11824 --- [ restartedMain] o.a.catalina.core.AprLifecycleListener : APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true], UDS [false].
2023-12-17 18:10:30.265 INFO 11824 --- [ restartedMain] o.a.catalina.core.AprLifecycleListener : APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
2023-12-17 18:10:30.283 INFO 11824 --- [ restartedMain] o.a.catalina.core.AprLifecycleListener : OpenSSL successfully initialized [OpenSSL 1.1.1g 21 Apr 2020]
2023-12-17 18:10:30.295 INFO 11824 --- [ restartedMain] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-12-17 18:10:30.295 INFO 11824 --- [ restartedMain] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.69]
2023-12-17 18:10:30.507 INFO 11824 --- [ restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-12-17 18:10:30.507 INFO 11824 --- [ restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2531 ms
2023-12-17 18:10:30.645 WARN 11824 --- [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataController': Unsatisfied dependency expressed through field 'dataService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataService': Unsatisfied dependency expressed through field 'dataMapper'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.dream_house.mapper.DataMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
2023-12-17 18:10:30.651 INFO 11824 --- [ restartedMain] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2023-12-17 18:10:30.679 INFO 11824 --- [ restartedMain] ConditionEvaluationReportLoggingListener : Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2023-12-17 18:10:30.751 ERROR 11824 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter : ***************************
APPLICATION FAILED TO START
***************************Description:Field dataMapper in com.example.dream_house.service.DataService required a bean of type 'com.example.dream_house.mapper.DataMapper' that could not be found.The injection point has the following annotations:- @org.springframework.beans.factory.annotation.Autowired(required=true)Action:Consider defining a bean of type 'com.example.dream_house.mapper.DataMapper' in your configuration.
解决方法:在 dataService.java 的 private DataMapper dataMapper; 的上面的@Autowired 改成 @Autowired(required = false)。
友情链接:Consider defining a bean of type问题解决
运行成功后,在浏览器中访问 http://127.0.0.1:8087/doc.html 页面,该页面是 Swagger 生成的 API 文档经过 knife4j 美化过后的 API 文档页面。
点击左侧的 “API接口” 可以看到出现了四个熟悉的接口,就是我们刚刚写的 “增删改查” 对应的接口,该 API 文档的好处就是可以在线对接口进行测试。
首先测试添加接口,依次点击并填写数据信息:
然后点击发送,看到响应内容 succeed 说明添加成功了:
我们前往 Navicat Premium 查看数据库内容有没有变化,刷新一下页面,可以看到在最下面的数据出现了我们刚刚添加进去的内容:
其次测试查询接口,依次点击并填写 id 信息,然后点击发送。
可以看到响应内容成功拿到数据:
然后测试更新接口,依次点击并填写信息,然后点击发送:
由于没有设置返回值,所以响应内容为空。
我们直接去看数据库的内容变化,刷新一下数据库,可以看到该条数据已经发生了变化:
最后测试删除接口,点击并填写 id 信息,然后点击发送:
由于没有设置返回值,所以还是直接前往数据库查看,刷新数据库,发现 id 为 4 的这条数据不见了,说明接口没问题:
经过测试,“增删改查”四个接口全部都能够正常使用。
实现项目功能接口
上面详细介绍了编写 “增删改查” 四个接口,下面给出其他项目功能接口。
代码
model 层之前写的一个 Data 的实体类就继续保留,我们再增加一个 getUser 类,用来作为前端从接口获取到的信息的类。
原理与 Data 类相同,这里就不再进行讲解了,直接上代码:
package com.example.dream_house.model;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;/*** @BelongsProject:dream_house* @BelongsPackage:com.example.dream_house.model* @Author:Uestc_Xiye* @CreateTime:2023-12-17 22:25:14*/@lombok.Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("获取信息内容")
public class getUser {@ApiModelProperty(value = "信息所属ID", required = true, example = "1")private int id;@ApiModelProperty(value = "信息来源所属省份", required = true, example = "湖北")private String province;@ApiModelProperty(value = "梦想内容", required = true, example = "环游世界")private String str;@ApiModelProperty(value = "点赞数", required = true, example = "52")private int likes;}
其实就是 Data 类去掉了 ip 和 time。
mapper 层我们需要在 DataMapper 接口中添加新的内容,新增的方法有:
- 查询点赞数前 50 名的信息。
- 查询最新的 50 条信息。
- 查询随机的 50 条信息。
- 更新指定 id 的点赞数 + 1。
- 更新指定 id 的点赞数 - 1。
方法的原理还是与之前相同,只是改变了 SQL 语句。
/*** 查询点赞数前50名的信息*/@Results({@Result(property = "id", column = "id"),@Result(property = "province", column = "province"),@Result(property = "str", column = "str"),@Result(property = "likes", column = "likes")})@Select("SELECT * FROM dream ORDER BY likes DESC LIMIT 50")List<getUser> findByLikes();/*** 查询最新的50条信息*/@Results({@Result(property = "id", column = "id"),@Result(property = "province", column = "province"),@Result(property = "str", column = "str"),@Result(property = "likes", column = "likes")})@Select("SELECT * FROM dream ORDER BY time DESC LIMIT 50")List<getUser> findByTime();/*** 查询随机的50条信息*/@Results({@Result(property = "id", column = "id"),@Result(property = "province", column = "province"),@Result(property = "str", column = "str"),@Result(property = "likes", column = "likes")})@Select("SELECT * FROM dream ORDER BY rand() DESC LIMIT 50")List<getUser> findByRand();/*** 更新指定id的点赞数+1*/@Update("UPDATE dream SET likes = likes + 1 WHERE id = #{id}")void increaseLikesById(int id);/*** 更新指定id的点赞数-1*/@Update("UPDATE dream SET likes = likes - 1 WHERE id = #{id}")void decreaseLikesById(int id);
service 层在这里增加的内容比较多,因为是业务逻辑层,需要完成功能设计。
首先在 service 目录下新建 getIP、getProvince、getTime 三个类,分别用来获取用户 IP 地址、用户所在省份、内容发送时间。
getIP 类代码如下,可以从用户的请求头中筛选出用户真实的IP地址:
package com.example.dream_house.service;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** @BelongsProject:dream_house* @BelongsPackage:com.example.dream_house.service* @Author:Uestc_Xiye* @CreateTime:2023-12-18 09:12:22*/
public class getIP {public String getIp(HttpServletRequest request, HttpServletResponse response) {response.setContentType("text/html;charset=utf-8");// 设置响应头允许ajax跨域访问,星号表示所有的异域请求都可以接受response.setHeader("Access-Control-Allow-Origin", "*");response.setHeader("Access-Control-Allow-Methods", "GET,POST");return getIpAddr(request);}public String getIpAddr(HttpServletRequest request) {// 获取请求头"x-forwarded-for"对应的valueString ip = request.getHeader("x-forwarded-for");// 如果获取的ip值为空if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {// 则获取请求头"Proxy-Client-IP"对应的valueip = request.getHeader("Proxy-Client-IP");}// 如果获取的ip值仍为空if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {//则获取请求头"WL-Proxy-Client-IP"对应的valueip = request.getHeader("WL-Proxy-Client-IP");}// 如果以上方式获取的ip值都为空if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {//则直接获取ip地址ip = request.getRemoteAddr();}// 返回ip地址return ip;}
}
getProvince 类代码如下,可以调用外部 API 来检测出用户 ip 地址所属的省份:
package com.example.dream_house.service;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;/*** @BelongsProject:dream_house* @BelongsPackage:com.example.dream_house.service* @Author:Uestc_Xiye* @CreateTime:2023-12-18 09:23:34*/
public class getProvince {/*** @param ip* @return*/public String get_Province(String ip) {// 设置api的urlString url = "https://ip.useragentinfo.com/json?ip=" + ip;RestTemplate template = new RestTemplate();// 发起一个HTTP GET请求,获取指定URL的响应实体,String.class表示要获取的响应实体的类型是字符串类型ResponseEntity<String> response = template.getForEntity(url, String.class);// 将Spring类型转换为JSON类型JSONObject json = JSON.parseObject(response.getBody());// 取出json中的数据String province = json.getString("province");return province;}
}
getTime 类代码如下,直接调用 Java 自带的包就行:
package com.example.dream_house.service;import java.text.SimpleDateFormat;
import java.util.Date;/*** @BelongsProject:dream_house* @BelongsPackage:com.example.dream_house.service* @Author:Uestc_Xiye* @CreateTime:2023-12-18 09:29:50*/
public class getTime {public String get_Time() {// 设置日期格式SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 获取当前系统时间String date = simpleDateFormat.format(new Date());return date;}
}
接下来修改 DataService 类的内容,需要新增以下方法:
/*** 获取用户数据并调用mapper层上传数据库** @param request* @param response* @param str* @return*/public String Add(HttpServletRequest request, HttpServletResponse response, String str) {getIP getIP = new getIP();getProvince getProvince = new getProvince();getTime getTime = new getTime();// 获取信息的IP地址String ip = getIP.getIp(request, response);// 获取信息所属省份String province = getProvince.get_Province(ip);// 获取当前时间String time = getTime.get_Time();// 设置当前点赞数为0int currentLikes = 0;// 上传数据dataMapper.insert(ip, province, time, str, currentLikes);return "succeed";}/*** 查询点赞数排名前50的信息** @return*/public List<getUser> findByLikes() {return dataMapper.findByLikes();}/*** 查询最新的50条信息** @return*/public List<getUser> findByTime() {return dataMapper.findByTime();}/*** 查询随机的50条信息** @return*/public List<getUser> findByRand() {return dataMapper.findByRand();}/*** 更新指定id对应的点赞数+1** @param id* @return*/public String increaseLikesById(int id) {dataMapper.increaseLikesById(id);return "succeed";}/*** 更新指定id对应的点赞数-1** @param id* @return*/public String decreaseLikesById(int id) {dataMapper.decreaseLikesById(id);return "succeed";}
controller 层需要新增以下接口:
@ApiOperation("上传信息接口")@PostMapping("/Add/{str}")public String Add(HttpServletRequest request, HttpServletResponse response, @PathVariable String str) {return dataService.Add(request, response, str);}@ApiOperation("查询点赞数前50名的信息")@GetMapping("/findByLikes")public List<getUser> findByLikes() {return dataService.findByLikes();}@ApiOperation("查询最新的50条信息")@GetMapping("/findByTime")public List<getUser> findByTime() {return dataService.findByTime();}@ApiOperation("查询随机的50条信息")@GetMapping("/findByRand")public List<getUser> findByRand() {return dataService.findByRand();}@ApiOperation("更新指定id对应的点赞数+1")@PutMapping("/increaseLikesById/{id}")public String increaseLikesById(@PathVariable int id) {return dataService.increaseLikesById(id);}@ApiOperation("更新指定id对应的点赞数-1")@PutMapping("/decreaseLikesById/{id}")public String decreaseLikesById(@PathVariable int id) {return dataService.decreaseLikesById(id);}
测试
启动项目,访问 API 文档页面 http://127.0.0.1:8087/doc.html,可以看到我们编写的接口都在这里:
发现问题:上传信息接口无效。
原因是 service 层的 get_Province 函数中的:
String url = "https://ip.useragentinfo.com/json?ip=" + ip;
我们去 https://ip.useragentinfo.com 这个网址看看:
查询一下,确实能用,但是和代码里的不同,注意这里的网址:
我们把代码改一下:
String url = "https://ip.useragentinfo.com/?ip=" + ip;
还是不行,原因是这个网站已经不支持带参数查询了(悲)。
我们只能按下面的方法修改 get_IP、getIpAddr 和 getProvince 函数。
友情链接:java实现获取IP及归属地
其他测试了都没什么大问题。
有个小问题就是,测试上传信息接口的时候,是能上传,但是 IP 地址是 0:0:0:0:0:0:0:0:1。
友情链接:手把手教你用Java获取IP归属地
这里面说,在本地环境调用获取 IP,要么是 0:0:0:0:0:0:0:1,或者是局域网IP。
局域网IP是以192.168.x.x开头,或者是127.0.0.1的IP。
所以需要部署到外网服务器才能获取到公网地址,部署到外网服务器才能成功获取 IP 地址。
再查了一下,在本地测试时,这个是无解的。
要是在局域网中运行,访问时使用本身的 IP 访问。这时候请求会通过路由器转发,因此服务器获取的就是本机的局域网内 IP,在 Java 中获取的 IP 就是局域网 IP,不是 localhost 或者 127.0.0.1 这种东西。
友情链接:java获取IP为0:0:0:0:0:0:0:1的情况
详细解释:
0:0:0:0:0:0:0:1是属于 ipv6,后来我又进行另一台电脑做测试,发现这种情况只有在服务器和客户端都在同一台电脑上才会出现(例如用 localhost 访问的时候才会出现),这是hosts配置文件的问题。
友情链接:
- request.getRemoteAddr()获取ip地址时得到的值是[0:0:0:0:0:0:0:1]原因和解决方法
- 查询本地ip地址为0:0:0:0:0:0:0:1
总结:
因为电脑优先把 localhost 解析成了 IPv6,用 localhost 测试,返回 IPv6 地址,get_Province 能解析到省份。
用 127.0.0.1 测试,返回本机 IPv4 地址,get_Province 不能解析到省份,是“本机地址”。
创建 Vue 前端
安装 Node.js
Node.js 简介:Node.js 入门 | 青训营笔记
官网下载安装:https://nodejs.org/zh-cn/
调出终端,输入指令 node -v,显示版本号说明 node 安装好了。
输入指令 npm -v,显示版本号,说明 npm 可以正常使用。
配置 npm 镜像
npm 默认的仓库地址在国外,访问速度较慢,我们切换成国内的淘宝镜像。
输入指令安装:npm install -g cnpm --registry=https://registry.npm.taobao.org
输入指令 cnpm -v,显示版本号说明 cnpm 安装好了。
前端代码在 Visual Studio Code 上写,在它上面再测一下:
安装脚手架
终端输入指令 cnpm i -g @vue/cli
或 npm i -g @vue/cli
全局安装。
安装细节很长,这样就算成功了:
创建并配置项目
在任意位置新建一个文件夹用来放置项目。
终端中通过 cd 指令跳转到这个文件夹(我这里已经到项目文件夹了)。
输入指令 vue create dream_house
创建项目。
- 上下键:表示选择
- 回车键:表示确认
选择 Manually select features 手动配置。
选择需要安装的插件,勾选如下插件,按空格键选择:
- Babel:解析 es6 转 es5 的插件
- TypeScript:TypeScript 插件
- Progressive Web App (PWA) Support:渐进式 Web 应用程序(PWA)支持
- Router:vue 路由插件
- Vuex:Vuex 插件
- CSS Pre-processors:css 预处理插件
- Linter/Formatter:格式化程序
- Unit Testing:单元测试
- E2E Testing:端到端(end-to-end)
回车(Enter)确认。
版本选择:选 2.x。
路由模式:选择是否为 history 模式,y 表示是,n 表示使用 hash 模式,这里选择的是 n。
- history:利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法(需要特定浏览器支持)
*hash: 浏览器 url 址栏中的 # 符号(如这个 URL:http://love.byzy.love/#/SuiJi ,hash 的值为“ #/SuiJi ”),hash 不被包括在 HTTP 请求中,所以对后端完全没有影响。因此改变 hash 不会重新加载页面,更容易进行打包上传服务器。
选择 CSS 预处理器:选第一个
选择编码规则:
-
ESLint with error prevention only:只配置使用 ESLint 官网的推荐规则
-
ESLint + Airbnb config:官网推荐的规则 + Airbnb 第三方的配置
-
ESLint + Standard config:使用 ESLint 官网推荐的规则 + Standard 第三方的配置
-
ESLint + Prettier:使用 ESLint 官网推荐的规则 + Prettier 第三方的配置
建议初学者(就是我)选择第一项,表示只有报错时才会验证。
检测条件:
-
Lint on save:保存就检测
-
Lint and fix on commit:fix 和 commit 时候检查
选 Lint on save。
存放配置:
- In dedicated config files:独立文件放置
- In package.json:放 package.json 里
选 In package.json。
最后输入 y:
保存配置并命名:
包管理器就选 npm:
等下载完成,显示出如下界面说明配置完成:
输入指令 cd dream_house 进入项目。
项目结构
Visual Studio Code 打开项目文件夹,观察左侧项目目录。
项目结构说明如下:
- node_modules: 包含所有安装的 npm 包。
- public:包含所有不需要经过 Webpack 处理的静态文件,如:index.html、favicon.ico等。
- favicon.ico
- index.html
- src:包含所有源代码
- assets:存放静态资源文件,如图片、样式表等
- logo.png
- components:存放 Vue 组件
- HelloWorld.vue
- router:存放 Vue 路由
- index.js
- store:存放 Vuex 状态管理相关的代码
- index.js
- views:存放页面级别的 Vue 组件
- AboutView.vue
- HomeView.vue
- App.vue:Vue应用程序的根组件
- main.js:Vue应用程序的入口文件
- assets:存放静态资源文件,如图片、样式表等
- .gitignore:Git 忽略文件列表,告诉 Git 哪些文件和目录应该被忽略,不被纳入版本控制中。
- babel.config.js:Babel 配置文件,用于转换 ES6+ 代码到 ES5,以便它们能够在更旧的浏览器中运行。
- jsconfig.json:用于配置 Vusial Studio Code 的 JavaScript 语言服务的 JSON 配置文件。它可以用来设置JS项目的编译选项、路径别名、自动引入模块等选项。
- package-lock.json:记录当前项目的依赖项及其版本信息,确保不同计算机或者环境下的依赖项保持一致。
- package.json:Vue.js 项目的元数据文件,包含项目的名称、版本、作者、依赖项等信息。其中,dependencies 字段记录的是项目的运行时依赖项,而 devDependencies 字段记录的是项目的开发时依赖项。
- README.md:项目的说明文档,描述项目的目的、功能、使用方法等等。
- vue.config.js:Vue.js 项目的配置文件,包含了一些 Vue CLI 的默认配置,可以用于自定义 Webpack 配置、开发环境的代理设置等等。使用 vue-cli-service 命令时,它将自动被加载并应用于项目配置。
Vue 组件结构
一个 Vue 组件分为三个部分,分别是:template 部分、script 部分、style 部分。
- template:组件的模板部分,用来定义组件的 html 结构。
必须在里面放置一个 html 标签来包裹所有的代码,例如 标签。 - script:组件的 JavaScript 代码部分,用来定义组件的逻辑和属性。
- style:组件的样式部分,它用来定义组件的样式。
Vue 组件调用与传值
我们就从官方的例子中来理解 Vue 组件间的传值与调用。由于 HelloWorld.vue 中的内容过多,所以我进行了删减。
观察下面的两段 Vue 代码。
HomeView.vue:
<template><div class="home"><img alt="Vue logo" src="../assets/logo.png"><HelloWorld msg="Welcome to Your Vue.js App"/></div>
</template><script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'export default {name: 'HomeView',components: {HelloWorld}
}
</script>
HelloWorld.vue:
<template><div class="hello"><h1>{{ msg }}</h1></div>
</template><script>
export default {name: 'HelloWorld',props: {msg: String}
}
</script>
可以看到 HomeView.vue 组件中 template 部分有一个 HelloWorld 标签,这个就是我们自定义的 HelloWorld 组件,在这个标签中,我们将一串字符串赋值给 msg 传递了过去。
在 script 部分中,使用 ES6 的 import 语法引入了 “@/components/HelloWorld.vue” 文件,并将其赋值给 HelloWorld 变量。
-
export default:使用 ES6 的 export 语法导出一个默认的对象,该对象包含了组件的各种属性和方法。
-
name:定义了组件的名称,可以在代码中用来引用这个组件。
-
components:定义了组件所包含的子组件。在这里,我们将 HelloWorld 子组件注册为了 HomeView 组件的一个子组件,以便在 HomeView 组件的模板中使用 HelloWorld 组件。
接下来看 HelloWorld.vue 组件中的 script 部分,我们可以看到 props 属性,这个属性定义了该组件的数据属性,也就是它的输入。在这里,我们定义了一个名为 msg 的属性,它是一个字符串类型。该属性可以从组件外部传递进来,在组件内部使用。
然后在 template 部分,
{{ msg }}
调用了该值。Vue 组件的生命周期
Vue组件的生命周期是指在组件实例化时,从开始到结束,不同阶段会自动执行的一些函数。Vue提供了一些钩子函数,让我们在这些生命周期阶段执行我们的自定义逻辑。Vue组件的生命周期可以分为以下三个阶段:
-
创建阶段:包括组件实例化、数据观测、模板编译和挂载等过程。
具体的生命周期函数有:-
beforeCreate:在实例初始化之后,数据观测和事件配置之前被调用,此时data和methods 等组件属性还未初始化。
-
created:在实例创建完成后被立即调用,此时 data 和 methods 等组件属性已经初始化,但是 DOM 节点还未挂载。
-
beforeMount:在挂载开始之前被调用,此时模板已经编译完成,但是还未渲染成 DOM。
-
mounted:在挂载完成后被调用,此时组件已经挂载到 DOM 上,可以进行 DOM 操作和异步数据请求等操作。
-
-
更新阶段:更新阶段包括数据更新和重新渲染等过程。
具体的生命周期函数有:-
beforeUpdate:在数据更新之前被调用,此时组件还未重新渲染。
-
updated:在数据更新之后被调用,此时组件已经重新渲染。
-
-
销毁阶段:销毁阶段包括组件销毁和清理等过程。
具体的生命周期函数有:-
beforeDestroy:在实例销毁之前被调用,此时组件还未销毁,可以进行一些清理工作。
-
destroyed:在实例销毁之后被调用,此时组件已经完全销毁,不再可用。
-
在组件的生命周期中,我们可以使用这些生命周期函数来执行一些初始化、清理和动态更新等操作。例如,在 created 生命周期函数中可以发起异步请求获取数据,在 beforeDestroy 生命周期函数中可以清理定时器或取消订阅等操作。
测试 Vue 程序
我们先看看默认的 Vue 程序如何运行。
第一步:npm install
第二步:npm run serve
第三步:打开浏览器,进入 http://localhost:8080/
友情链接:如何运行vue项目(超详细图解)
需求分析
整理需求:
- 一个导航栏
- 三个页面
- 页面中显示每个用户发出的内容
- 可以给内容点赞
- 可以发送内容
- 先把整体页面的框架模型搭建起来,然后再进行修饰
在了解了需求之后,就可以开始动手操作了。
实现项目页面
框架搭建
在 views 目录下新建 LikesSortedView.vue、NewestView.vue、RandomView.vue。
在 components 目录下新建 UserList.vue、InputText.vue。
LikesSortedView.vue、NewestView.vue、RandomView.vue 三个组件作为三个页面,内容包括所有的 UserList.vue 组件排列起来。
UserList.vue 组件作为单个用户发送的内容,上面显示省份与点赞等内容。
InputText.vue 组件作为发送内容的组件,包括一个输入框和一个发送按钮。
项目配置
我们需要把项目运行端口更改一下,不然默认是 8080 端口。在 vue.config.js 文件中更改:
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({transpileDependencies: true,devServer: {port: 8086, // 此处修改你想要的端口号},})
然后在项目中我们会用到 Axios 以及 Element ,所以需要下载相关依赖并在 main.js 中引入。
-
安装 Axios 指令:
npm install axios
。 -
安装 Element 指令:
npm install element-ui
。
安装好以后,修改 main.js:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'import ElementUI from 'element-ui'; // 引入element-ui
import 'element-ui/lib/theme-chalk/index.css'; // element-ui的css样式要单独引入import axios from 'axios'Vue.prototype.$axios = axiosVue.use(ElementUI);Vue.config.productionTip = falsenew Vue({router,store,render: h => h(App)
}).$mount('#app')
主界面设计
主页面的设计在根组件 App.vue 中进行,主要负责一些全局内容的显示。
在 template 部分为一个标题,一个导航栏,一个 InputText 组件,一条每页都需要显示的提示文字。
<template><div id="app"><h1 class="MyName">Dream House</h1><nav><router-link to="/">排行</router-link> |<router-link to="/Newest">最新</router-link> |<router-link to="/Random">随机</router-link></nav><router-view /><InputText /><div class="newText">该列表仅显示50条内容!</div></div>
</template>
在 script 部分引入 InputText.vue 组件。
<script>
import InputText from '@/components/InputText.vue'export default {name: 'App',components: {InputText},
}
</script>
在 style 部分设置该组件的样式,以及一些全局效果。
<style lang="scss">
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;
}nav {padding: 20px;font-size: 30px;font-family: pyt;text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.2);a {text-decoration: none;font-weight: bold;color: #2c3e50;&.router-link-exact-active {-webkit-tap-highlight-color: transparent; //清除蓝框text-decoration: none;color: #42b983;}}
}.MyName {font-family: pyt;font-size: 60px;background-clip: text;-webkit-background-clip: text;-webkit-text-fill-color: transparent;background-image: linear-gradient(to right, #41ffa9, rgba(79, 168, 252, 0.6));margin: 20px 10px 0px 10px;text-shadow: 3px 3px 3px rgba(0, 0, 0, 0.2); //文本阴影
}
body {background: rgb(244, 189, 255);background: linear-gradient(90deg,rgb(183, 180, 255) 0%,rgb(255, 255, 255) 50%,rgb(165, 240, 255) 100%);
}// 设置全局滚动条
body::-webkit-scrollbar {width: 5px;background-color: #f5f5f5;
}body::-webkit-scrollbar-thumb {border-radius: 10px;-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);background-color: #c4ffe4;
}.newText {height: 80px;margin: 10px auto;font-size: 13px;
}
</style>
设置路由
在 router 目录下的 index.js 中设置路由:
import Vue from 'vue'
import VueRouter from 'vue-router'Vue.use(VueRouter)const routes = [{path: '/',name: '排行',component: () => import('../views/LikesSortedView.vue')},{path: '/Newest',name: '最新',// route level code-splitting// this generates a separate chunk (Newest.[hash].js) for this route// which is lazy-loaded when the route is visited.component: () => import(/* webpackChunkName: "about" */ '../views/NewestView.vue')},{path: '/Random',name: '随机',component: () => import('../views/RandomView.vue')}
]const router = new VueRouter({routes
})export default router
内容组件设计
UserList.vue 的 template 部分如下,显示一个主要内容,在左下角显示省份,右下角显示点赞数和一个图片按钮。
<template><div class="UserList"><div class="box"><div class="box1"><div class="str">{{str}}</div></div><div class="box2"><div class="province">{{province}}</div><div class="likes">{{likes}}</div><img:src="imagePath"@click="toggleImage"class="img-btn"/></div></div></div>
</template>
style 部分如下,设置对应的标签显示的位置,以及样式等。
<!-- 添加“scoped作用域”属性以将 CSS 限制为此组件 -->
<style scoped lang="scss">
.UserList {display: flex;justify-content: center;align-items: center;
}.box {border: 1px solid #ddd;border-radius: 10px;box-shadow: 2px 2px 4px #ddd;padding: 10px;display: flex;flex-direction: column; //将 flex 子元素沿竖直方向排列align-items: center; //将 flex 子元素在纵轴上居中对齐margin: 5px;width: 500px;transition: all 0.3s ease; //面板放大
}.box:hover {transform: scale(1.04); //面板放大
}.box1 {width: 100%;float: left;margin: 5px;
}.str {flex: 1;text-align: center;font-size: 20px;//font-family:wpyt;width: 100%;height: 25px;display: flex;justify-content: center;align-items: center;text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2);
}.box2 {width: 100%;display: flex;
}.province {text-align: center;font-size: 14px;width: 120px;height: 20px;display: flex;
}.likes {text-align: center;font-size: 14px;width: 100px;height: 20px;margin: auto 5px auto auto;display: flex;justify-content: flex-end;align-items: center;
}.img-btn {width: 16px;height: 16px;transition: all 0.3s ease; // 设置按钮元素的过渡效果
}
</style>
script 部分如下,获取从上级页面传来的数据,在 created() 钩子函数中判断是否给这个内容点过赞,并将图片按钮初始化。
<script>
import axios from 'axios'export default {name: 'UserList',props: {Obj: Object},data() {return {imagePath: require("@/assets/点赞-no.png"),str: this.Obj.str,province: this.Obj.province,likes: this.Obj.likes};},created() {if (localStorage.getItem(`isLiked${this.Obj.id}`)) {this.imagePath = require("@/assets/点赞-yes.png");}},methods: {toggleImage() {// 判断是否点赞过if (this.imagePath === require("@/assets/点赞-no.png")) {// 获取按钮元素const imgBtn = this.$el.querySelector('.img-btn');// 给按钮元素应用缩放和旋转的样式imgBtn.style.transform = 'scale(1.2) rotate(-10deg)';// 设置计时器,在 300 毫秒之后给按钮元素应用新的样式setTimeout(() => {imgBtn.style.transform = 'scale(1) rotate(10deg)';setTimeout(() => {imgBtn.style.transform = 'scale(1) rotate(0deg)';}, 300);}, 300);this.likes++;// 将图片替换为已点赞的图片this.imagePath = require("@/assets/点赞-yes.png");// 将已点赞的状态存入 localStoragelocalStorage.setItem(`isLiked${this.Obj.id}`, true);//向服务器发送发送put请求axios.put(`http://localhost:8087/increaseLikesById/${this.Obj.id}`).then(res => {console.log(res.data);}).catch(error => {console.log("点赞出错!")console.error(error);});} else {this.likes--;// 否则将图片替换为未点赞的图片this.imagePath = require("@/assets/点赞-no.png");// 并且删除已点赞的状态localStorage.removeItem(`isLiked${this.Obj.id}`);// 向服务器发送发送put请求axios.put(`http://localhost:8087/decreaseLikesById/${this.Obj.id}`).then(res => {console.log(res.data);}).catch(error => {console.log("取消点赞出错!")console.error(error);});}}}}
</script>
按钮绑定 toggleImage() 函数,通过点击按钮触发此函数,触发图片按钮的动作,以及更换图片,并且向服务器发出 put 请求,更新数据库中的数据,更新页面中显示的数据。
assets 文件夹下要放图片:
友情链接:阿里巴巴矢量图标库
发送组件设计
InputText.vue 的 template 部分如下,只有一个输入框以及一个图片按钮。
<template><div class="InputText"><inputtype="text"maxlength="15"v-model="inputValue"placeholder="发送你的梦想!"/><imgsrc="../assets/send.png"class="submit-btn"/></div>
</template>
style 部分如下,依然是设置整个发送组件的样式。
<style scoped lang="scss">
.InputText {position: fixed;bottom: 10px;left: 0;right: 0;display: flex;justify-content: space-between;padding: 10px;background-color: transparent;width: 300px;margin: 0 auto;//设置 z-index 值为 999,显示为最顶部z-index: 999;
}input[type="text"] {flex: 1;height: 40px;padding: 0 10px;background-color: rgba(255, 255, 255, 0.7);border: 1px solid transparent;border-radius: 20px;box-shadow: 0 0 10px #42b983;transition: all 0.3s ease-in-out;
}input[type="text"]:hover {transform: scale(1.05);box-shadow: 0 0 15px rgba(24, 144, 255, 0.8);
}.submit-btn {height: 40px;padding: 2px 0px 0px 10px;background-color: transparent;border: none;cursor: pointer;transform: scale(1);transition: all 0.3s ease-in-out;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}.submit-btn:hover {transform: scale(1.2);
}
</style>
script 部分如下,通过点击按钮触发 submit() 函数,播放按钮的动画,以及获取输入框的内容,发送到服务器上。
<script>
import axios from 'axios'export default {name: 'InputText',// 定义数据data() {return {isAnimating: false, // 用于标识动画是否正在播放inputValue: ''};},methods: {// 定义提交方法submit() {// 如果动画正在播放,直接返回if (this.isAnimating) return;//向服务器发送发送post请求axios.post(`http://localhost:8087/Add/${this.inputValue}`).then(res => {console.log(res.data);this.inputValue = '';this.$message({message: '发送成功!',type: 'success',duration: 2000 // 自动关闭延迟时间,单位毫秒});}).catch(error => {this.$message({message: '发送失败!',type: 'error',duration: 2000 // 自动关闭延迟时间,单位毫秒});console.log("发送信息出错!")console.error(error);});this.isAnimating = true; // 标记动画正在播放// 获取提交按钮元素const btn = this.$el.querySelector(".submit-btn");// 记录提交按钮的原始 transform 值const originalTransform = btn.style.transform;// 设置提交按钮的 transform 值,使其向右上方移动btn.style.transform = "translate(100%, -100%)";// 设置动画播放完成后的回调函数setTimeout(() => {// 将提交按钮的 transform 值设置为原始值btn.style.transform = originalTransform;// 标记动画已经播放完毕this.isAnimating = false;}, 500);}},mounted() {// 在组件挂载完成后,给提交按钮元素绑定 click 事件this.$el.querySelector(".submit-btn").addEventListener("click", this.submit);}
};
</script>
assets 文件夹下要放图片:
分页面设计
三个分页面的作用是向数据库发出请求,获取到内容数据,再调用内容组件将各个内容排列显示在页面上。
排行页面 LikesSortedView.vue
template 部分如下,在该部分中只使用了一个自定义组件 UserList ,使用 Vue 的模板语法动态的生成一个组件列表。
<template><div class="PaiHang"><UserListv-for="(item, index) in Obj":key="index":Obj="item"/></div>
</template>
解释:
-
v-for="(item, index) in Obj"指令表示对 Obj 对象进行遍历,生成对应数量的 组件。
-
:key="index"属性用于标识每个组件的唯一性,使 Vue 能够更高效地管理组件的状态。
-
:Obj="item"属性将当前元素的值 item 传递给 组件,作为组件的参数之一。
script 部分如下,在该页面被初始化的的时候会调用 getList() 方法从服务器拿到数据,然后将 json 数据传入对应的 UserList 组件中。
<script>
// @ 是 /src 的别名
import UserList from '@/components/UserList.vue'
import axios from 'axios'export default {name: 'LikesSortedView',components: {UserList},data() {return {Obj: []}},// computed会缓存结果,methods每次都会重新计算methods: {getList() {let list = [];let newObjects = {};axios.get('http://localhost:8087/findByLikes').then(res => {list = res.data;for (let i = 0; i < list.length; i++) {newObjects[i] = list[i];}console.log(newObjects);this.Obj = newObjects;}).catch(error => {this.$message({message: '获取页面内容失败!',type: 'error',duration: 2000});console.log("获取排行出错!")console.error(error);});}},created() {this.getList();}}
</script>
最新页面 NewestView.vue
最新页面基本上与排行页面相同,唯一不同的地方就是请求的参数不同,所以只需要把请求的 url 改一下就行了,改成 API 定义的对应的接口。
url:http://(接口IP地址):8087/findByTime
然后就是本组件的名字需要改一下。
<template><div class="Newest"><UserListv-for="(item, index) in Obj":key="index":Obj="item"/></div></template><script>// @ 是 /src 的别名import UserList from '@/components/UserList.vue'import axios from 'axios'export default {name: 'NewestView',components: {UserList},data() {return {Obj: []}},// computed会缓存结果,methods每次都会重新计算methods: {getList() {let list = [];let newObjects = {};axios.get('http://localhost:8087/findByTime').then(res => {list = res.data;for (let i = 0; i < list.length; i++) {newObjects[i] = list[i];}console.log(newObjects);this.Obj = newObjects;}).catch(error => {this.$message({message: '获取页面内容失败!',type: 'error',duration: 2000});console.log("获取排行出错!")console.error(error);});}},created() {this.getList();}}</script>
随机页面 RandomView.vue
同理,改一下请求的 url 就行。
url:http://(接口IP地址):8087/findByRand
<template><div class="Random"><UserListv-for="(item, index) in Obj":key="index":Obj="item"/></div></template><script>// @ 是 /src 的别名import UserList from '@/components/UserList.vue'import axios from 'axios'export default {name: 'RandomView',components: {UserList},data() {return {Obj: []}},// computed会缓存结果,methods每次都会重新计算methods: {getList() {let list = [];let newObjects = {};axios.get('http://localhost:8087/findByRand').then(res => {list = res.data;for (let i = 0; i < list.length; i++) {newObjects[i] = list[i];}console.log(newObjects);this.Obj = newObjects;}).catch(error => {this.$message({message: '获取页面内容失败!',type: 'error',duration: 2000});console.log("获取排行出错!")console.error(error);});}},created() {this.getList();}}</script>
项目启动
在终端中输入指令:npm run serve
。
可以看到如下界面,说明项目成功运行:
根据提示访问本地地址 http://localhost:8086/。
发现所有功能都正常使用,页面正常排布。
经测试,点赞按钮和发送按钮动画正常播放,页面数据正常更新,梦想内容正常发布,各功能使用正常。
至此,“梦想屋” 小项目成功完成。
源码下载
GitHub:https://github.com//DreamHouse
CSDN:DreamHouse.zip
相关文章:
从 0 开始实现一个 SpringBoot + Vue 项目
从 0 开始实现一个 SpringBoot Vue 项目 从 0 开始实现一个 SpringBoot Vue 项目 软件和工具创建 SpringBoot 后端项目创建 MySQL 数据库配置文件实现增删改查接口 Model 层mapper 层service 层controller 层测试 实现项目功能接口 代码测试 创建 Vue 前端 安装 Node.js配置…...
怎么修复损坏的U盘?而且不用格式化的方式!
当你插入U盘时,若电脑弹出“需要格式化才能使用”提示,且无法打开或读取其中的数据,说明U盘极有可能已经损坏。除此之外,若电脑在连接U盘后显示以下信息,也可能意味着U盘出现问题,需要修复损坏的U盘&#x…...
使用redis-cli命令实现redis crud操作
项目场景: 线上环境上redis中的key影响数据展示,需要删除。但环境特殊没办法通过 redis客户端工具直连。只能使用redis-cli命令来实现。 操作步骤: 1、确定redis安装的服务器; 2、找到redis的安装目录下 ##找到redis安装目…...
Kibana 控制台中提供语义、向量和混合搜索
作者:来自 Elastic Mark_Laney 想要将常规 Elasticsearch 查询与新的 AI 搜索功能结合起来吗?那么,你不需要连接到某个第三方的大型语言模型(LLM)吗?不。你可以使用 Elastic 的 ELSER 模型来改进现有搜索&a…...
设计模式-结构型-装饰器模式
装饰器模式(Decorator Pattern)是结构型设计模式中的一种,它允许你通过将对象封装在一个新的对象中,来动态地添加新的功能,而无需改变原对象的结构。装饰器模式的核心思想是“将功能附加到对象上”,它是一种…...
CAP:Serverless + AI 让应用开发更简单
AI 已被广泛视为推动行业进步的关键力量,其在各行业的落地步伐加快。企业在构建 AI 应用开发过程中经常会面临 AI 技术门槛过高、试错周期过长、GPU 资源昂贵且弹性能力不足、缺乏配套工具、业务与模型的开发运维过于割裂、缺乏定制化能力等挑战,成为企业…...
Redis超详细入门教程(基础篇)
目录 一、什么是Redis 二、安装Redis 1、Windows系统安装 2、Linux系统安装 三、Redis通用命令 四、Redis基本命令 五、五种数据结构类型 5.1、String类型 5.2、List集合类型 5.3、Set集合类型 5.4、Hash集合类型 5.5、Zset有序集合类型 六、总结 一、什么是Redi…...
对话 TDengine 解决方案中心总经理陈肃:构建技术与市场的桥梁
TD 小T导读 他是大数据领域的杰出专家,拥有超过十项一作发明专利,是中国通信行业标准《大数据 消息中间件技术要求与测试方法》的重要编写者,并凭借数据中间件领域的突出成就荣获 2019 年“CJK OSS Award”。他是腾讯云 TVP 专家和 TGO 鲲鹏会…...
Formality:参考设计/实现设计以及顶层设计
相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 Formality存在两个重要的概念:参考设计/实现设计和顶层设计,本文就将对此进行详细阐述。参考设计/实现设计是中两个重要的全局概念&am…...
通过内核模块按fd强制tcp的quickack方法
一、背景 tcp的quickack功能是为了让ack迅速回发,快速响应,减少网络通讯时延,属于一个优化项,但是tcp的quickack是有配额限制的,配置是16个quick,也就是短时间内quickack了16次以后,这个配额为…...
Wi-Fi 7、Wi-Fi 6 与 5G、4G 的全方位对比
随着无线通信技术的飞速发展,Wi-Fi 7、Wi-Fi 6,以及5G、4G 已经成为人们生活和工作中不可或缺的网络技术。无论是家庭网络、高速移动通信,还是工业物联网,这些技术都在发挥各自的作用。那么,它们之间有什么区别&#x…...
【例43.3】 转二进制
目录 描述 输入描述 输出描述 用例输入 1 用例输出 1 来源 类型 知识补充站 代码 C Python3 C 描述 请你把一个整数n转化为二进制并从低位到高位输出。 输入描述 一行,仅含一个正整数 n (1≤n≤109)。 输出描述 从低位到高位输出一个二进制数&a…...
qt vs ios开发应用环境搭建和上架商店的记录
qt 下载链接如下 https://download.qt.io/new_archive/qt/5.14/5.14.2/qt-opensource-mac-x64-5.14.2.dmg 安装选项全勾选就行,这里特别说明下qt5.14.2/qml qt5.14.2对qml支持还算成熟,但很多特性还得qt6才行,这里用qt5.14.2主要是考虑到服…...
安全测评主要标准
大家读完觉得有帮助记得关注和点赞!!! 安全测评的主要标准包括多个国际和国内的标准,这些标准为信息系统和产品的安全评估提供了基础和指导。 一、安全测评的主要标准 1.1、国际标准 可信计算机系统评估准则(TC…...
如何学习数学 | 数学家如何思考
学习数学的关键在哪里? 原创 遇见数学 不少人面对数学都会觉得高深莫测,甚至非常枯燥乏味。 只有当你真正走入它的世界,才会发现里面蕴含着无尽的智慧和美感。要想推开这座数学的大门,需要的不仅仅是背公式,或者做一…...
职场沟通与行为
职场沟通与行为 引言 在职场上,你是否曾遇到过困惑的沟通?是否对同事的行为有过疑虑?这不仅是个别现象,而是我们这个时代工作文化中的普遍问题。许多职场的摩擦,来自沟通不畅或是行为不当。那么,如何才能…...
IIO(Industrial I/O)驱动介绍
文章目录 IIO(Industrial I/O)驱动是Linux内核中用于工业I/O设备的子系统,主要用于处理传感器数据采集和转换。以下是其关键点: 功能 数据采集:从传感器读取数据。数据处理:对原始数据进行滤波、校准等操作…...
关于vite+vue3+ts项目中env.d.ts 文件详解
env.d.ts 文件是 Vite 项目中用于定义全局类型声明的 TypeScript 文件。它帮助开发者向 TypeScript提供全局的类型提示,特别是在使用一些特定于 Vite 的功能时(如 import.meta.env)。以下是详细讲解及代码示例 文章目录 **1. env.d.ts 文件的…...
32单片机综合应用案例——物联网(IoT)环境监测站(四)(内附详细代码讲解!!!)
无论你身处何种困境,都要坚持下去,因为勇气和毅力是成功的基石。不要害怕失败,因为失败并不代表终结,而是为了成长和进步。相信自己的能力,相信自己的潜力,相信自己可以克服一切困难。成功需要付出努力和坚…...
【Flink系列】6. Flink中的时间和窗口
6. Flink中的时间和窗口 在批处理统计中,我们可以等待一批数据都到齐后,统一处理。但是在实时处理统计中,我们是来一条就得处理一条,那么我们怎么统计最近一段时间内的数据呢?引入“窗口”。 所谓的“窗口”ÿ…...
代码随想录算法训练营第三十五天-动态规划-01背包(二维)
动规五部曲 dp数组的含义,注意这是一个二维数组。dp[i][j] 第一维度代表“从0到第i个物品,而且包括选或不选的情况,即这一维度代表物品编号第二维度代表代表背包容量合在一起的意思是当背包容量是j时,从0到i个物品中选择任意物品…...
快速开发:用AI构造AI —— 打造属于个人的Copilot(M-聪明AI)
作品简介: 当今快速发展的AI时代,学会使用AI的同时,也可以融入AI,来打造自己的产品,我给我这个取名M-聪明, 是基于VUE 3 Spring Boot -Redis ChatGML RxJava SSE 的AI 服务平台。然后这款工具旨在为用户…...
Elasticsearch容器启动报错:AccessDeniedException[/usr/share/elasticsearch/data/nodes];
AccessDeniedException 表明 Elasticsearch 容器无法访问或写入数据目录 /usr/share/elasticsearch/data/nodes。这是一个权限问题。 问题原因: 1、宿主机目录权限不足:映射到容器的数据目录 /data/es/data 在宿主机上可能没有足够的权限供容器访问。 …...
用公网服务器实现内网穿透
首先需要一个公网服务器 下载frp 搜索github下载到frp,服务端frps/客户端frpc。。下载的时候要注意自己本地内网机的cpu版本和服务端cpu架构 我的电脑是mac M1PRO版本 下载的是:darwinarm64 比如 服务端一般是Linux(Intel 64位CPU…...
Jmeter如何进行多服务器远程测试
🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 JMeter是Apache软件基金会的开源项目,主要来做功能和性能测试,用Java编写。 我们一般都会用JMeter在本地进行测试,但是受到单…...
前端实习第二个月小结
时间飞快,第一次实习已经过去两个多月,作一些简单的总结和分享。 注:文章整体会比较轻松,提及的经历、经验仅作参考。 一、关于实习/工作内容 1、工作内容 近期做的是管理后台方面的业务,技术栈:前端re…...
C# 并发和并行的区别--16
目录 并发和并行 一.并发 定义 特点 代码示例 代码解释 二.并行 定义 特点 在C#中的体现 代码示例 代码解释 三.并发和并行的区别 四 .如何在C#中选择并发还是并行 1.考虑任务类型 2.代码示例 3.注意事项 五.总结 并发和并行 在编程领域,并发和并行是两个密切…...
Python编程与在线医疗平台数据挖掘与数据应用交互性研究
一、引言 1.1 研究背景与意义 在互联网技术飞速发展的当下,在线医疗平台如雨后春笋般涌现,为人们的就医方式带来了重大变革。这些平台打破了传统医疗服务在时间和空间上的限制,使患者能够更加便捷地获取医疗资源。据相关报告显示,中国基于互联网的医疗保健行业已进入新的…...
HBase实训:纸币冠字号查询任务
一、实验目的 1. 理解分布式数据存储系统HBase的架构和工作原理。 2. 掌握HBase表的设计原则,能够根据实际业务需求设计合理的表结构。 3. 学习使用HBase Java API进行数据的插入、查询和管理。 4. 实践分布式数据存储系统在大数据环境下的应用,…...
Java 读取 Windows 设备的唯一性标识及定位
在 Windows 系统中,获取设备唯一性标识及定位信息对设备管理、安全监控等场景意义重大。本文介绍 Java 中几种实现方法,如 JNA 库、WMI4Java 库及通过 JNI 结合 Windows API。 1. 使用 JNA 库读取 DEVPKEY_Device_ContainerId 在 Windows 系统中&…...
UE控件学习
ListView: item设置:使能在list设置为Entry类 Grid Panel: 常用作背包,每个格子大小可不相同 WidgetSwitcher: 用于切换页签 Wrap_Box: 自动横向排版子节点,超过一定范围则自动换行…...
1.Spring AI 从入门到实践
Spring AI 从入门到实践 1.什么是Spring AI 2.使用Spring Boot&Spring AI快速构建AI应用程序 3.ChatClient&Chat Model简化与AI模型的交互 4.Spring AI Prompt:与大模型进行有效沟通 5.结构化输出大模型响应 6.实战:AI聊天机器人 Ben技术站关注Java技术&#x…...
2025年01月蓝桥杯Scratch1月stema选拔赛真题—美丽的图形
美丽的图形 编程实现美丽的图形具体要求: 1)点击绿旗,角色在舞台中心,如图所示; 2)1秒后,绘制一个边长为 140的红色大正方形,线条粗细为 3,正方形的中心为舞台中心,如图所示; 完整题目可点击下…...
FLASK创建下载
html用a标签 <!-- Button to download the image --> <a href"{{ url_for(download_file, filenameimage.png) }}"><button>Download Image</button> </a> 后端:url_for双大括号即是用来插入变量到模板中的语法。也就是绑…...
LDD3学习7--硬件接口I/O端口(以short为例)
1 理论 1.1 基本概念 目前对外设的操作,都是通过寄存器。寄存器的概念,其实就是接口,访问硬件接口,有I/O端口通信和内存映射I/O (Memory-Mapped I/O),I/O端口通信是比较老的那种,都是老的串口并口设备&am…...
MySQL(高级特性篇) 06 章——索引的数据结构
一、为什么使用索引 索引是存储引擎用于快速找到数据记录的一种数据结构,就好比一本教科书的目录部分,通过目录找到对应文章的页码,便可快速定位到需要的文章。MySQL中也是一样的道理,进行数据查找时,首先查看查询条件…...
【FlutterDart】MVVM(Model-View-ViewModel)架构模式例子-http版本(30 /100)
动图更精彩 MVVM(Model-View-ViewModel) 特点 Model:负责数据管理和业务逻辑。 View:负责显示数据,通常是一个UI组件。 ViewModel:负责处理用户交互,更新Model,并将数据转换为View可…...
光谱相机的光谱分辨率可以达到多少?
多光谱相机 多光谱相机的光谱分辨率相对较低,波段数一般在 10 到 20 个左右,光谱分辨率通常在几十纳米到几百纳米之间,如常见的多光谱相机光谱分辨率为 100nm 左右。 高光谱相机 一般的高光谱相机光谱分辨率可达 2.5nm 到 10nm 左右&#x…...
.Net8 Avalonia跨平台UI框架——<vlc:VideoView>控件播放海康监控、摄像机视频(Windows / Linux)
一、UI效果 二、新建用户控件:VideoViewControl.axaml 需引用:VideoLAN.LibVLC.Windows包 Linux平台需安装:VLC 和 LibVLC (sudo apt-get update、sudo apt-get install vlc libvlccore-dev libvlc-dev) .axaml 代码 注…...
【论文阅读】基于空间相关性与Stacking集成学习的风电功率预测方法
文章目录 摘要0. 引言1. 空间相关性分析2. 风电功率预测模型2.1 Stacking 集成策略2.2 基学习器2.2.1 基于机器学习算法的基学习器2.2.2 基于神经网络的基学习器2.2.3 基于粒子群优化算法的超参数优化 2.3 元学习器2.4 基于空间相关性与Stacking集成学习的风电功率预测方法 3 算…...
什么是Spring Boot 应用开发?
一、引言 在当今的软件开发领域,Java 依然占据着重要的地位,而 Spring Boot 作为 Java 生态系统中极具影响力的框架,极大地简化了企业级应用的开发流程,提升了开发效率和应用的可维护性。它基于 Spring 框架构建,通过…...
选择saas 还是源码主要考虑
公司业务规模:小型企业可能会发现SaaS提供的即用型解决方案更符合其需求,而大型企业可能需要源码以实现更高的定制性和控制权。 公司技术专长:缺乏技术团队的企业可能会倾向于使用SaaS,而那些拥有强大IT部门的企业可能更适合管理…...
【JAVA 基础 第(19)课】Hashtable 类用法和注意细节,是Map接口的实现类
Map接口:存放的是具有映射关系的键值对,键映射到值,键必须是唯一的 Hashtable 类,Map接口的实现类,键和值都不能为nullHashtable 是同步的,是线程安全的 public class MapTest {public static void main(String[] arg…...
AI时代下 | 通义灵码冲刺备战求职季
AI时代下 | 通义灵码冲刺备战求职季 什么是通义灵码使用智能编程助手备战求职靠谱吗体验心得 AI时代下,备战求职季有了不一样的方法,使用通义灵码冲刺备战求职季,会有什么样的体验? 什么是通义灵码 在开始话题之前,首…...
如何将 session 共享存储到 redis 中
文章目录 一. 分布式 session 登录1.1 什么是分布式?1.2 Session 共享1.3 为什么服务器 A 登录后,请求发到服务器 B,不认识该用户?1.4 共享存储 二. Session 共享实现Redis三. 测试session共享四. cookie设置4.1 前端4.2 后端 一.…...
智能科技与共情能力加持,哈曼重新定义驾乘体验
2025年1月6日,拉斯维加斯,2025年国际消费电子展——想象一下,当您步入一辆汽车,它不仅能响应您的指令,更能理解您的需求、适应您的偏好,并为您创造一个独特且专属的交互环境。作为汽车科技领域的知名企业和…...
第4章 Kafka核心API——Kafka客户端操作
Kafka客户端操作 一. 客户端操作1. AdminClient API 一. 客户端操作 1. AdminClient API...
Debian 设定 tomcat 定时重启
目录 背景 过程记录 1、编辑sh文件,完成重启功能 2、设置sh的可执行权限 编辑 3、设置定时任务 背景 在Debian 12系统中,原本部署了两个tomcat,结果总是遇到CPU飙升到影响应用正常使用的程度,找了很久原因还是没有找到。 …...
mysql8.0 重要指标参数介绍
MySQL 8.0 引入了许多新的功能和优化,针对性能、可扩展性、可靠性以及安全性方面做出了显著改进。为了确保 MySQL 的高效运行,了解和配置 MySQL 的一些关键指标参数非常重要。以下是 MySQL 8.0 中的一些重要参数和指标,帮助你优化数据库性能。…...
SpringMVC (2)
目录 1. RequestMapping 注解介绍 2. RequestMapping 使用 3. RequestMapping与请求方式 3.1 RequestMapping 支持Get和Post类型的请求 3.2 RequestMapping 指定接收某种请求 3.3 GetMapping和PostMapping 4. 传参 4.1 通过查询字符串传参 4.2 在 Body 中传参 4.2.1 …...