经历了新冠疫情始末,之后,多出门走走,看看山,看看海,吃好吃的
sql链接: https://pan.baidu.com/s/17LSC_ldZ3C75-bZ_Uezs-A?pwd=abcd
ap_article 文章基本信息表 和 ap_article_config 文章配置表 是一对一的关系
ap_article 文章基本信息表 和 ap_article_content 文章内容表 是一对一的关系
表名称 | 说明 |
---|---|
ap_article | 文章信息表,存储已发布的文章 |
ap_article_config | APP已发布文章配置表 |
ap_article_content | APP已发布文章内容表 |
ap_author | APP文章作者信息表 |
ap_collection | APP收藏信息表 |
CREATE TABLE `ap_article` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '标题',
`author_id` int(11) unsigned DEFAULT NULL COMMENT '文章作者的ID',
`author_name` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '作者昵称',
`channel_id` int(10) unsigned DEFAULT NULL COMMENT '文章所属频道ID',
`channel_name` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '频道名称',
`layout` tinyint(1) unsigned DEFAULT NULL COMMENT '文章布局\r\n 0 无图文章\r\n 1 单图文章\r\n 2 多图文章',
`flag` tinyint(3) unsigned DEFAULT NULL COMMENT '文章标记\r\n 0 普通文章\r\n 1 热点文章\r\n 2 置顶文章\r\n 3 精品文章\r\n 4 大V 文章',
`images` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文章图片\r\n 多张逗号分隔',
`labels` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文章标签最多3个 逗号分隔',
`likes` int(5) unsigned DEFAULT NULL COMMENT '点赞数量',
`collection` int(5) unsigned DEFAULT NULL COMMENT '收藏数量',
`comment` int(5) unsigned DEFAULT NULL COMMENT '评论数量',
`views` int(5) unsigned DEFAULT NULL COMMENT '阅读数量',
`province_id` int(11) unsigned DEFAULT NULL COMMENT '省市',
`city_id` int(11) unsigned DEFAULT NULL COMMENT '市区',
`county_id` int(11) unsigned DEFAULT NULL COMMENT '区县',
`created_time` datetime DEFAULT NULL COMMENT '创建时间',
`publish_time` datetime DEFAULT NULL COMMENT '发布时间',
`sync_status` tinyint(1) DEFAULT '0' COMMENT '同步状态',
`origin` tinyint(1) unsigned DEFAULT '0' COMMENT '来源',
`static_url` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1383828014629179394 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='文章信息表,存储已发布的文章';
CREATE TABLE `ap_article_config` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`article_id` bigint(20) unsigned DEFAULT NULL COMMENT '文章ID',
`is_comment` tinyint(1) unsigned DEFAULT NULL COMMENT '是否可评论',
`is_forward` tinyint(1) unsigned DEFAULT NULL COMMENT '是否转发',
`is_down` tinyint(1) unsigned DEFAULT NULL COMMENT '是否下架',
`is_delete` tinyint(1) unsigned DEFAULT NULL COMMENT '是否已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_article_id` (`article_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1383828014645956610 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='APP已发布文章配置表';
CREATE TABLE `ap_article_content` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`article_id` bigint(20) unsigned DEFAULT NULL COMMENT '文章ID',
`content` longtext COLLATE utf8mb4_unicode_ci COMMENT '文章内容',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_article_id` (`article_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1383828014650150915 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='APP已发布文章内容表';
将一个表的字段分散到多个表中,每个表存储其中一部分字段
优势:
拆分规则:
新建 heima-leadnews-model/src/main/java/com/heima/model/article/pojos/ApArticle.java
实体类:
/**
*
* 文章信息表,存储已发布的文章
*
*
* @author itheima
*/
@Data
@TableName("ap_article")
public class ApArticle implements Serializable {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
/**
* 标题
*/
private String title;
/**
* 作者id
*/
@TableField("author_id")
private Long authorId;
/**
* 作者名称
*/
@TableField("author_name")
private String authorName;
/**
* 频道id
*/
@TableField("channel_id")
private Integer channelId;
/**
* 频道名称
*/
@TableField("channel_name")
private String channelName;
/**
* 文章布局 0 无图文章 1 单图文章 2 多图文章
*/
private Short layout;
/**
* 文章标记 0 普通文章 1 热点文章 2 置顶文章 3 精品文章 4 大V 文章
*/
private Byte flag;
/**
* 文章封面图片 多张逗号分隔
*/
private String images;
/**
* 标签
*/
private String labels;
/**
* 点赞数量
*/
private Integer likes;
/**
* 收藏数量
*/
private Integer collection;
/**
* 评论数量
*/
private Integer comment;
/**
* 阅读数量
*/
private Integer views;
/**
* 省市
*/
@TableField("province_id")
private Integer provinceId;
/**
* 市区
*/
@TableField("city_id")
private Integer cityId;
/**
* 区县
*/
@TableField("county_id")
private Integer countyId;
/**
* 创建时间
*/
@TableField("created_time")
private Date createdTime;
/**
* 发布时间
*/
@TableField("publish_time")
private Date publishTime;
/**
* 同步状态
*/
@TableField("sync_status")
private Boolean syncStatus;
/**
* 来源
*/
private Boolean origin;
/**
* 静态页面地址
*/
@TableField("static_url")
private String staticUrl;
}
新建 heima-leadnews-model/src/main/java/com/heima/model/article/pojos/ApArticleConfig.java
实体类:
/**
*
* APP已发布文章配置表
*
*
* @author itheima
*/
@Data
@TableName("ap_article_config")
public class ApArticleConfig implements Serializable {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
/**
* 文章id
*/
@TableField("article_id")
private Long articleId;
/**
* 是否可评论
* true: 可以评论 1
* false: 不可评论 0
*/
@TableField("is_comment")
private Boolean isComment;
/**
* 是否转发
* true: 可以转发 1
* false: 不可转发 0
*/
@TableField("is_forward")
private Boolean isForward;
/**
* 是否下架
* true: 下架 1
* false: 没有下架 0
*/
@TableField("is_down")
private Boolean isDown;
/**
* 是否已删除
* true: 删除 1
* false: 没有删除 0
*/
@TableField("is_delete")
private Boolean isDelete;
}
新建 heima-leadnews-model/src/main/java/com/heima/model/article/pojos/ApArticleContent.java
实体类:
@Data
@TableName("ap_article_content")
public class ApArticleContent implements Serializable {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
/**
* 文章id
*/
@TableField("article_id")
private Long articleId;
/**
* 文章内容
*/
private String content;
}
加载首页 | 加载更多 | 加载最新 | |
---|---|---|---|
接口路径 | /api/v1/article/load | /api/v1/article/loadmore | /api/v1/article/loadnew |
请求方式 | POST | POST | POST |
参数 | ArticleHomeDto | ArticleHomeDto | ArticleHomeDto |
响应结果 | ResponseResult | ResponseResult | ResponseResult |
ArticleHomeDto:
# 最大时间
Date maxBehotTime;
# 最小时间
Date minBehotTime;
# 分页size
Integer size;
# 频道ID
String tag;
资料链接: https://pan.baidu.com/s/1S3T-ssECQILH0IsCHk-HgQ?pwd=abcd
解压至 heima-leadnews-service
目录下
编辑 heima-leadnews-service/pom.xml
文件:
<modules>
<module>heima-leadnews-usermodule>
<module>heima-leadnews-articlemodule>
modules>
刷新 Maven,再在maven中 + 添加 heima-leadnews-article
的pom文件
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/leadnews_article?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
username: root
password: 123456
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml
# 设置别名包扫描路径,通过该属性可以给包中的类注册别名
type-aliases-package: com.heima.model.article.pojos
新建 heima-leadnews-model/src/main/java/com/heima/model/article/dtos/ArticleHomeDto.java
文件:
@Data
public class ArticleHomeDto {
// 最大时间
Date maxBehotTime;
// 最小时间
Date minBehotTime;
// 分页size
Integer size;
// 频道ID
String tag;
}
新建 heima-leadnews-service/heima-leadnews-article/src/main/java/com/heima/article/controller/v1/articleHomeController.java
文件:
@RestController
@RequestMapping("/api/v1/article")
public class articleHomeController {
/**
* 加载首页
* @param dto
* @return
*/
@PostMapping("/load")
public ResponseResult load(@RequestBody ArticleHomeDto dto) {
return null;
}
/**
* 加载更多
* @param dto
* @return
*/
@PostMapping("/loadmore")
public ResponseResult loadmore(@RequestBody ArticleHomeDto dto) {
return null;
}
/**
* 加载最新
* @param dto
* @return
*/
@PostMapping("/loadnew")
public ResponseResult loadnew(@RequestBody ArticleHomeDto dto) {
return null;
}
}
新建 heima-leadnews-service/heima-leadnews-article/src/main/java/com/heima/article/mapper/ApArticleMapper.java
文件:
public interface ApArticleMapper extends BaseMapper<ApArticle> {
/**
* 加载文章列表
* @param dto
* @param type 1 加载更多 2 加载最新
* @return
*/
public List<ApArticle> loadArticleList(ArticleHomeDto dto, short type);
}
新建 heima-leadnews-service/heima-leadnews-article/src/main/resources/mapper/ApArticleMapper.xml
文件:
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heima.article.mapper.ApArticleMapper">
<resultMap id="resultMap" type="com.heima.model.article.pojos.ApArticle">
<id column="id" property="id"/>
<result column="title" property="title"/>
<result column="author_id" property="authorId"/>
<result column="author_name" property="authorName"/>
<result column="channel_id" property="channelId"/>
<result column="channel_name" property="channelName"/>
<result column="layout" property="layout"/>
<result column="flag" property="flag"/>
<result column="images" property="images"/>
<result column="labels" property="labels"/>
<result column="likes" property="likes"/>
<result column="collection" property="collection"/>
<result column="comment" property="comment"/>
<result column="views" property="views"/>
<result column="province_id" property="provinceId"/>
<result column="city_id" property="cityId"/>
<result column="county_id" property="countyId"/>
<result column="created_time" property="createdTime"/>
<result column="publish_time" property="publishTime"/>
<result column="sync_status" property="syncStatus"/>
<result column="static_url" property="staticUrl"/>
resultMap>
<select id="loadArticleList" resultMap="resultMap">
SELECT
aa.*
FROM
`ap_article` aa
LEFT JOIN ap_article_config aac ON aa.id = aac.article_id
<where>
and aac.is_delete != 1 -- 不能删除
and aac.is_down != 1 -- 不能下载
<if test="type != null and type == 1"> -- 加载更多
and aa.publish_time #{dto.minBehotTime}
if>
<if test="type != null and type == 2"> -- 加载最新
and aa.publish_time ]]> #{dto.maxBehotTime}
if>
<if test="dto.tag != '__all__'"> -- 推荐栏(无频道)
and aa.channel_id = #{dto.tag}
if>
where>
order by aa.publish_time desc -- 发布时间倒序
limit #{dto.size} -- 限制条数
select>
mapper>
新建 heima-leadnews-common/src/main/java/com/heima/common/constants/ArticleConstants.java
文件:
public class ArticleConstants {
public static final Short LOADTYPE_LOAD_MORE = 1;
public static final Short LOADTYPE_LOAD_NEW = 2;
public static final String DEFAULT_TAG = "__all__";
}
新建 heima-leadnews-service/heima-leadnews-article/src/main/java/com/heima/article/service/ApArticleService.java
文件:
public interface ApArticleService extends IService<ApArticle> {
/**
* 加载文章列表
* @param dto
* @param type 1 加载更多 2 加载最新
* @return
*/
public ResponseResult load(ArticleHomeDto dto, Short type);
}
新建 heima-leadnews-service/heima-leadnews-article/src/main/java/com/heima/article/service/impl/ApArticleServiceImpl.java
文件:
@Service
@Transactional
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
@Autowired
private ApArticleMapper apArticleMapper;
private final static short Max_PAGE_SIZE = 50;
/**
* 加载文章列表
* @param dto
* @param type 1 加载更多 2 加载最新
* @return
*/
@Override
public ResponseResult load(ArticleHomeDto dto, Short type) {
// 1.参数校验
// 1.1 分页参数校验
Integer size = dto.getSize();
if( size == null || size == 0) {
size = 10;
}
// 分页的值不超过50
size = Math.min(size, Max_PAGE_SIZE);
// 1.2 类型参数校验(更多/最新)
if(!type.equals(ArticleConstants.LOADTYPE_LOAD_MORE) && !type.equals(ArticleConstants.LOADTYPE_LOAD_NEW)) {
type = ArticleConstants.LOADTYPE_LOAD_MORE;
}
// 1.3 频道参数校验
if(StringUtils.isBlank(dto.getTag())) {
dto.setTag(ArticleConstants.DEFAULT_TAG);
}
// 1.4 时间参数校验
if(dto.getMaxBehotTime() == null) dto.setMaxBehotTime(new Date());
if(dto.getMinBehotTime() == null) dto.setMinBehotTime(new Date());
// 2. 查询
List<ApArticle> articleList = apArticleMapper.loadArticleList(dto, type);
// 3. 结果返回
return ResponseResult.okResult(articleList);
}
}
编辑 heima-leadnews-service/heima-leadnews-article/src/main/java/com/heima/article/controller/v1/articleHomeController.java
文件:
@RestController
@RequestMapping("/api/v1/article")
public class articleHomeController {
@Autowired
private ApArticleService apArticleService;
/**
* 加载首页
* @param dto
* @return
*/
@PostMapping("/load")
public ResponseResult load(@RequestBody ArticleHomeDto dto) {
return apArticleService.load(dto, ArticleConstants.LOADTYPE_LOAD_MORE);
}
/**
* 加载更多
* @param dto
* @return
*/
@PostMapping("/loadmore")
public ResponseResult loadmore(@RequestBody ArticleHomeDto dto) {
return apArticleService.load(dto, ArticleConstants.LOADTYPE_LOAD_MORE);
}
/**
* 加载最新
* @param dto
* @return
*/
@PostMapping("/loadnew")
public ResponseResult loadnew(@RequestBody ArticleHomeDto dto) {
return apArticleService.load(dto, ArticleConstants.LOADTYPE_LOAD_NEW);
}
}
在app网关的微服务的nacos的配置中心添加文章微服务的路由
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]': # 匹配所有请求
allowedOrigins: "*" #跨域处理 允许所有的域
allowedMethods: # 支持的方法
- GET
- POST
- PUT
- DELETE
routes:
# 用户微服务
- id: user
uri: lb://leadnews-user
predicates:
- Path=/user/**
filters:
- StripPrefix= 1
# 文章微服务
- id: article
uri: lb://leadnews-article
predicates:
- Path=/article/**
filters:
- StripPrefix= 1
页面地址: http://localhost:8801/#/login
FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。
freemarker 作为springmvc一种视图格式,默认情况下SpringMVC支持freemarker视图格式。
编辑 heima-leadnews-test/freemarker-demo/pom.xml
文件:
...
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-ioartifactId>
<version>1.3.2version>
dependency>
dependencies>
新建 heima-leadnews-test/freemarker-demo/src/main/resources/application.yml
文件:
server:
port: 8881 #服务端口
spring:
application:
name: freemarker-demo #指定服务名
freemarker:
cache: false #关闭模板缓存,方便测试
settings:
template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
suffix: .ftl #指定Freemarker模板文件的后缀名
新建 heima-leadnews-test/freemarker-demo/src/main/resources/templates/01-basic.ftl
文件:
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!title>
head>
<body>
<b>普通文本 String 展示:b><br><br>
Hello ${name} <br>
<hr>
<b>对象Student中的数据展示:b><br/>
姓名:${stu.name}<br/>
年龄:${stu.age}
<hr>
body>
html>
新建 heima-leadnews-test/freemarker-demo/src/main/java/com/heima/freemarker/FreemarkerDemoApplication.java
文件:
@SpringBootApplication
public class FreemarkerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(FreemarkerDemoApplication.class, args);
}
}
新建 heima-leadnews-test/freemarker-demo/src/main/java/com/heima/freemarker/entity/Student.java
文件:
@Data
public class Student {
private String name;//姓名
private int age;//年龄
private Date birthday;//生日
private Float money;//钱包
}
新建 heima-leadnews-test/freemarker-demo/src/main/java/com/heima/freemarker/controller/HelloController.java
文件:
@Controller
public class HelloController {
@GetMapping("/basic")
public String hello(Model model) {
// name
model.addAttribute("name", "freemarker");
// stu
Student student = new Student();
student.setName("lily");
student.setAge(18);
model.addAttribute("stu", student);
return "01-basic";
}
}
模板地址: http://localhost:8881/basic
1、注释,即<#-- -->,介于其之间的内容会被freemarker忽略
<#--我是一个freemarker注释-->
2、插值(Interpolation): 即 ${..}
部分,freemarker会用真实的值代替**${..}
**
Hello ${name}
3、FTL指令: 和HTML标记类似,名字前加#予以区分,Freemarker会解析标签中的表达式或逻辑。
<# >FTL指令#>
4、文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容。
<#--freemarker中的普通文本-->
我是一个普通的文本
新建 heima-leadnews-test/freemarker-demo/src/main/resources/templates/02-list.ftl
文件:
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!title>
head>
<body>
<#-- list 数据的展示 -->
<b>展示list中的stu数据:b>
<br>
<br>
<table>
<tr>
<td>序号td>
<td>姓名td>
<td>年龄td>
<td>钱包td>
tr>
table>
<hr>
<#-- Map 数据的展示 -->
<b>map数据的展示:b>
<br/><br/>
<a href="###">方式一:通过map['keyname'].propertya><br/>
输出stu1的学生信息:<br/>
姓名:<br/>
年龄:<br/>
<br/>
<a href="###">方式二:通过map.keyname.propertya><br/>
输出stu2的学生信息:<br/>
姓名:<br/>
年龄:<br/>
<br/>
<a href="###">遍历map中两个学生信息:a><br/>
<table>
<tr>
<td>序号td>
<td>姓名td>
<td>年龄td>
<td>钱包td>
tr>
table>
<hr>
body>
html>
编辑 heima-leadnews-test/freemarker-demo/src/main/java/com/heima/freemarker/controller/HelloController.java
文件:
@GetMapping("/list")
public String list(Model model) {
return "02-list";
}
地址: http://localhost:8881/list
编辑 heima-leadnews-test/freemarker-demo/src/main/java/com/heima/freemarker/controller/HelloController.java
文件:
@GetMapping("/list")
public String list(Model model) {
//------------------------------------
Student stu1 = new Student();
stu1.setName("小强");
stu1.setAge(18);
stu1.setMoney(1000.86f);
stu1.setBirthday(new Date());
//小红对象模型数据
Student stu2 = new Student();
stu2.setName("小红");
stu2.setMoney(200.1f);
stu2.setAge(19);
//将两个对象模型数据存放到List集合中
List<Student> stus = new ArrayList<>();
stus.add(stu1);
stus.add(stu2);
//向model中存放List集合数据
model.addAttribute("stus",stus);
return "02-list";
}
编辑 heima-leadnews-test/freemarker-demo/src/main/resources/templates/02-list.ftl
文件:
<#-- list 数据的展示 -->
<b>展示list中的stu数据:b>
<br>
<br>
<table>
<tr>
<td>序号td>
<td>姓名td>
<td>年龄td>
<td>钱包td>
tr>
<#list stus as stu>
<tr>
<td>${stu_index + 1}td>
<td>${stu.name}td>
<td>${stu.age}td>
<td>${stu.money}td>
tr>
#list>
table>
<hr>
地址: http://localhost:8881/list
# 获取map中的值
map['keyname'].property
http://map.keyname.property/
# 遍历map
<#list userMap?keys as key>
key:${key}--value:${userMap["${key}"]}
</#list>
编辑 heima-leadnews-test/freemarker-demo/src/main/java/com/heima/freemarker/controller/HelloController.java
文件:
//------------------------------------
//创建Map数据
Map<String, Student> stuMap = new HashMap<>();
stuMap.put("stu1", stu1);
stuMap.put("stu2", stu2);
model.addAttribute("stuMap", stuMap);
编辑 heima-leadnews-test/freemarker-demo/src/main/resources/templates/02-list.ftl
文件:
<a href="###">遍历map中两个学生信息:a><br/>
<table>
<tr>
<td>序号td>
<td>姓名td>
<td>年龄td>
<td>钱包td>
tr>
<#list stuMap?keys as key>
<tr>
<td>${key_index + 1}td>
<td>${stuMap[key].name}td>
<td>${stuMap[key].age}td>
<td>${stuMap[key].money}td>
tr>
#list>
table>
地址: http://localhost:8881/list
# 语法
<#if expression>
<#else>
</#if>
编辑 heima-leadnews-test/freemarker-demo/src/main/resources/templates/02-list.ftl
文件:
<table>
<tr>
<td>序号td>
<td>姓名td>
<td>年龄td>
<td>钱包td>
tr>
<#list stus as stu>
<#--<tr>
<td>${stu_index + 1}td>
<td>${stu.name}td>
<td>${stu.age}td>
<td>${stu.money}td>
tr>-->
<#--if指令-->
<#--判断名称为小红的数据字体显示为红色-->
<#if stu.name = "小红">
<tr style="color:red">
<td>${stu_index + 1}td>
<td>${stu.name}td>
<td>${stu.age}td>
<td>${stu.money}td>
tr>
<#else >
<tr>
<td>${stu_index + 1}td>
<td>${stu.name}td>
<td>${stu.age}td>
<td>${stu.money}td>
tr>
#if>
#list>
table>
地址: http://localhost:8881/list
100+5 运算: ${100 + 5 }<br/>
100 - 5 * 5运算:${100 - 5 * 5}<br/>
5 / 2运算:${5 / 2}<br/>
12 % 10运算:${12 % 10}<br/>
Column 1 | Column 2 |
---|---|
=或者== | 判断两个值是否相等 |
!= | 判断两个值是否不等 |
>或者gt | 判断左边值是否大于右边值 |
>=或者gte | 判断左边值是否大于等于右边值 |
<或者lt | 判断左边值是否小于右边值 |
<=或者lte | 判断左边值是否小于等于右边值 |
<#if (10 lt 12 )&&( 10 gt 5 ) >
(10 lt 12 )&&( 10 gt 5 ) 显示为 true
#if>
<br/>
<br/>
<#if !false>
false 取反为true
#if>
编辑 heima-leadnews-test/freemarker-demo/src/main/java/com/heima/freemarker/controller/HelloController.java
文件:
// model.addAttribute("stus",stus); // 测试数据为空时报错
地址: http://localhost:8881/list
编辑 heima-leadnews-test/freemarker-demo/src/main/resources/templates/02-list.ftl
文件:
<#--空值处理-->
<#if stus??>
<#list stus as stu>
<#--<tr>
<td>${stu_index + 1}td>
<td>${stu.name}td>
<td>${stu.age}td>
<td>${stu.money}td>
tr>-->
<#--if指令-->
<#--判断名称为小红的数据字体显示为红色-->
<#if stu.name = "小红">
<#--= 和 == 是一样的-->
<tr style="color:red">
<td>${stu_index + 1}td>
<td>${stu.name}td>
<td>${stu.age}td>
<td>${stu.money}td>
tr>
<#else >
<tr>
<td>${stu_index + 1}td>
<td>${stu.name}td>
<td>${stu.age}td>
<td>${stu.money}td>
tr>
#if>
#list>
#if>
地址: http://localhost:8881/list
编辑 heima-leadnews-test/freemarker-demo/src/main/java/com/heima/freemarker/controller/HelloController.java
文件:
// model.addAttribute("stus",stus); // 测试数据为空时报错
编辑 heima-leadnews-test/freemarker-demo/src/main/resources/templates/02-list.ftl
文件:
<#--测试变量缺失-->
<#--<td>${stu.name}td>-->
<td>${stu.name2}td>
地址: http://localhost:8881/list
编辑 heima-leadnews-test/freemarker-demo/src/main/resources/templates/02-list.ftl
文件:
<#--测试变量缺失-->
<#--<td>${stu.name}td>-->
<#--<td>${stu.name2}td>-->
<td>${stu.name2!'-'}td>
地址: http://localhost:8881/list
如果是嵌套对象则建议使用()括起来
例:${(stu.name)!‘’}表示,如果stu或name为空默认显示空字符串。
# 语法: 变量 + ? + 函数名称
${stus?size}
# 语法:
${集合名?size}
编辑 heima-leadnews-test/freemarker-demo/src/main/resources/templates/02-list.ftl
文件:
...
<#-- 内建函数 -->
<b>内建函数:b>
<br/><br/>
<#--集合大小-->
stus集合大小: ${stus?size}
<br/><br/>
编辑 heima-leadnews-test/freemarker-demo/src/main/java/com/heima/freemarker/controller/HelloController.java
文件:
model.addAttribute("stuMap", stuMap);
// 内建函数 - 日期
model.addAttribute("today", new Date());
编辑 heima-leadnews-test/freemarker-demo/src/main/resources/templates/02-list.ftl
文件:
<#--日期格式化-->
显示年月日: ${today?date}
<br>
显示时分秒:${today?time}
<br>
显示日期+时间:${today?datetime}
<br>
自定义格式化:${today?string("yyyy年MM月")}
地址: http://localhost:8881/list
编辑 heima-leadnews-test/freemarker-demo/src/main/java/com/heima/freemarker/controller/HelloController.java
文件:
// 内建函数 - c
model.addAttribute("point", 234523452345L);
编辑 heima-leadnews-test/freemarker-demo/src/main/resources/templates/02-list.ftl
文件:
<#--日期格式化-->
初始数值: ${point}
<br>
初始数值: ${point?c}
地址: http://localhost:8881/list
将json字符串转成对象
<#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
<#assign data=text?eval />
开户行:${data.bank} 账号:${data.account}
新建 heima-leadnews-test/freemarker-demo/src/test/java/com/heima/freemarker/test/FreemarkerTest.java
测试类:
@SpringBootTest(classes = FreemarkerDemoApplication.class)
@RunWith(SpringRunner.class)
public class FreemarkerTest {
@Autowired
private Configuration configuration;
@Test
public void test() throws IOException, TemplateException {
Template template = configuration.getTemplate("02-list.ftl");
/**
* 合成方法
* 两个数据: 模型数据 / 输出流
*/
template.process(getData(), new FileWriter("d:/list.html"));
}
public Map getData() {
Map<String, Object> map = new HashMap<>();
//小强对象模型数据
Student stu1 = new Student();
stu1.setName("小强");
stu1.setAge(18);
stu1.setMoney(1000.86f);
stu1.setBirthday(new Date());
//小红对象模型数据
Student stu2 = new Student();
stu2.setName("小红");
stu2.setMoney(200.1f);
stu2.setAge(19);
//将两个对象模型数据存放到List集合中
List<Student> stus = new ArrayList<>();
stus.add(stu1);
stus.add(stu2);
//向map中存放List集合数据
map.put("stus",stus);
//------------------------------------
//创建Map数据
Map<String, Student> stuMap = new HashMap<>();
stuMap.put("stu1", stu1);
stuMap.put("stu2", stu2);
map.put("stuMap", stuMap);
map.put("today", new Date());
map.put("point", 234523452345L);
return map;
}
}
MinIO基于Apache License v2.0开源协议的对象存储服务,可以做为云存储的解决方案用来保存海量的图片,视频,文档。由于采用Golang实现,服务端可以工作在Windows,Linux, OS X和FreeBSD上。配置简单,基本是复制可执行程序,单行命令可以运行起来。
MinIO兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
MinIO官网文档:http://docs.minio.org.cn/docs/
MinIO特点:
# 搜索minio(第一项)
docker search minio
# 列出本地镜像(本地已下载)
docker images
# 安装 minio(已安装)
docker run -p 9000:9000 --name minio -d --restart=always -e "MINIO_ACCESS_KEY=minio" -e "MINIO_SECRET_KEY=minio123" -v /home/data:/data -v /home/config:/root/.minio minio/minio server /data
# 列出容器
docker ps
# 查看日志(启动)
docker logs -f 容器ID
访问minio系统: http://192.168.200.130:9000/
编辑 heima-leadnews-test/minio-demo/pom.xml
文件:
<dependencies>
<dependency>
<groupId>io.miniogroupId>
<artifactId>minioartifactId>
<version>7.1.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
dependencies>
新建 heima-leadnews-test/minio-demo/src/main/java/com/heima/minio/MinIOApplication.java
文件:
@SpringBootApplication
public class MinIOApplication {
public static void main(String[] args) {
SpringApplication.run(MinIOApplication.class,args);
}
}
新建 heima-leadnews-test/minio-demo/src/test/java/com/heima/minio/test/MinIOTest.java
文件:
public class MinIOTest {
/**
* 把 list.html 文件上传到minio中, 并且可以在浏览器中访问
* @param args
*/
public static void main(String[] args) {
try {
FileInputStream fileInputStream = new FileInputStream("d:/list.html");
// 1. 获取minio的链接信息 创建一个minio客户端
MinioClient minioClient = MinioClient.builder().credentials("minio", "minio123").endpoint("http://192.168.200.130:9000").build();
// 2. 上传
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.object("list.html") // 文件名
.contentType("text/html") // 文件类型
.bucket("leadnews") // 桶名称, 与minio管理界面创建的桶一致
.stream(fileInputStream, fileInputStream.available(), -1).build();
minioClient.putObject(putObjectArgs);
// 3. 访问路径
System.out.println("http://192.168.200.130:9000/leadnews/list.html");
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行测试类,点击测试链接: http://192.168.200.130:9000/leadnews/list.html
minio地址: http://192.168.200.130:9000/minio/leadnews/
模块资源链接: https://pan.baidu.com/s/1wpx-XxADF7qJIymKhWzD4Q?pwd=abcd
解压至 heima-leadnews
目录下,编辑 pom.xml
配置文件:
<modules>
<module>heima-leadnews-commonmodule>
<module>heima-leadnews-utilsmodule>
<module>heima-leadnews-modelmodule>
<module>heima-leadnews-feign-apimodule>
<module>heima-leadnews-servicemodule>
<module>heima-leadnews-gatewaymodule>
<module>heima-leadnews-testmodule>
<module>heima-leadnews-basicmodule>
modules>
重新加载Maven,查看到 heima-leadnews-basic
图标改变即可
编辑 heima-leadnews-test/minio-demo/pom.xml
文件:
<dependencies>
<dependency>
<groupId>io.miniogroupId>
<artifactId>minioartifactId>
<version>7.1.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>com.heimagroupId>
<artifactId>heima-file-starterartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
dependencies>
新建 heima-leadnews-test/minio-demo/src/main/resources/application.yml
文件:
minio:
accessKey: minio
secretKey: minio123
bucket: leadnews
endpoint: http://192.168.200.130:9000
readPath: http://192.168.200.130:9000
编辑 heima-leadnews-test/minio-demo/src/test/java/com/heima/minio/test/MinIOTest.java
文件:
@SpringBootTest(classes = MinIOApplication.class)
@RunWith(SpringRunner.class)
public class MinIOTest {
/* *//**
* 把 list.html 文件上传到minio中, 并且可以在浏览器中访问
* @param args
*//*
public static void main(String[] args) {
try {
FileInputStream fileInputStream = new FileInputStream("d:/list.html");
// 1. 获取minio的链接信息 创建一个minio客户端
MinioClient minioClient = MinioClient.builder().credentials("minio", "minio123").endpoint("http://192.168.200.130:9000").build();
// 2. 上传
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.object("list.html") // 文件名
.contentType("text/html") // 文件类型
.bucket("leadnews") // 桶名称, 与minio管理界面创建的桶一致
.stream(fileInputStream, fileInputStream.available(), -1).build();
minioClient.putObject(putObjectArgs);
// 3. 访问路径
System.out.println("http://192.168.200.130:9000/leadnews/list.html");
} catch (Exception e) {
e.printStackTrace();
}
}*/
@Autowired
private FileStorageService fileStorageService;
// 把 list.html 文件上传到minio中, 并且可以在浏览器中访问
@Test
public void test() throws FileNotFoundException {
FileInputStream fileInputStream = new FileInputStream("d:/list.html");
String path = fileStorageService.uploadHtmlFile("", "list.html", fileInputStream);
System.out.println(path);
}
}
minio地址: http://192.168.200.130:9000/minio/leadnews/
编辑 heima-leadnews-service/heima-leadnews-article/pom.xml
文件:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>com.heimagroupId>
<artifactId>heima-file-starterartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
dependencies>
编辑 leadnews-article
配置:
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/leadnews_article?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
username: root
password: 123456
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml
# 设置别名包扫描路径,通过该属性可以给包中的类注册别名
type-aliases-package: com.heima.model.article.pojos
minio:
accessKey: minio
secretKey: minio123
bucket: leadnews
endpoint: http://192.168.200.130:9000
readPath: http://192.168.200.130:9000
新建 heima-leadnews-service/heima-leadnews-article/src/main/resources/templates/article.ftl
文件:
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
<title>黑马头条title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/lib/index.css">
<link rel="stylesheet" href="../../../plugins/css/index.css">
head>
<body>
<div id="app">
<div class="article">
<van-row>
<van-col span="24" class="article-title" v-html="title">van-col>
van-row>
<van-row type="flex" align="center" class="article-header">
<van-col span="3">
<van-image round class="article-avatar" src="https://p3.pstatp.com/thumb/1480/7186611868">van-image>
van-col>
<van-col span="16">
<div v-html="authorName">div>
<div>{{ publishTime | timestampToDateTime }}div>
van-col>
<van-col span="5">
<van-button round :icon="relation.isfollow ? '' : 'plus'" type="info" class="article-focus"
:text="relation.isfollow ? '取消关注' : '关注'" :loading="followLoading" @click="handleClickArticleFollow">
van-button>
van-col>
van-row>
<van-row class="article-content">
<#if content??>
<#list content as item>
<#if item.type='text'>
<van-col span="24" class="article-text">${item.value}van-col>
<#else>
<van-col span="24" class="article-image">
<van-image width="100%" src="${item.value}">van-image>
van-col>
#if>
#list>
#if>
van-row>
<van-row type="flex" justify="center" class="article-action">
<van-col>
<van-button round :icon="relation.islike ? 'good-job' : 'good-job-o'" class="article-like"
:loading="likeLoading" :text="relation.islike ? '取消赞' : '点赞'" @click="handleClickArticleLike">van-button>
<van-button round :icon="relation.isunlike ? 'delete' : 'delete-o'" class="article-unlike"
:loading="unlikeLoading" @click="handleClickArticleUnlike">不喜欢van-button>
van-col>
van-row>
<van-list v-model="commentsLoading" :finished="commentsFinished" finished-text="没有更多了"
@load="onLoadArticleComments">
<van-row id="#comment-view" type="flex" class="article-comment" v-for="(item, index) in comments" :key="index">
<van-col span="3">
<van-image round src="https://p3.pstatp.com/thumb/1480/7186611868" class="article-avatar">van-image>
van-col>
<van-col span="21">
<van-row type="flex" align="center" justify="space-between">
<van-col class="comment-author" v-html="item.authorName">van-col>
<van-col>
<van-button round :icon="item.operation === 0 ? 'good-job' : 'good-job-o'" size="normal"
@click="handleClickCommentLike(item)">{{ item.likes || '' }}
van-button>
van-col>
van-row>
<van-row>
<van-col class="comment-content" v-html="item.content">van-col>
van-row>
<van-row type="flex" align="center">
<van-col span="10" class="comment-time">
{{ item.createdTime | timestampToDateTime }}
van-col>
<van-col span="3">
<van-button round size="normal" v-html="item.reply" @click="showCommentRepliesPopup(item.id)">回复 {{
item.reply || '' }}
van-button>
van-col>
van-row>
van-col>
van-row>
van-list>
div>
<van-row type="flex" justify="space-around" align="center" class="article-bottom-bar">
<van-col span="13">
<van-field v-model="commentValue" placeholder="写评论">
<template #button>
<van-button icon="back-top" @click="handleSaveComment">van-button>
template>
van-field>
van-col>
<van-col span="3">
<van-button icon="comment-o" @click="handleScrollIntoCommentView">van-button>
van-col>
<van-col span="3">
<van-button :icon="relation.iscollection ? 'star' : 'star-o'" :loading="collectionLoading"
@click="handleClickArticleCollection">van-button>
van-col>
<van-col span="3">
<van-button icon="share-o">van-button>
van-col>
van-row>
<van-popup v-model="showPopup" closeable position="bottom"
:style="{ width: '750px', height: '60%', left: '50%', 'margin-left': '-375px' }">
<van-list v-model="commentRepliesLoading" :finished="commentRepliesFinished" finished-text="没有更多了"
@load="onLoadCommentReplies">
<van-row id="#comment-reply-view" type="flex" class="article-comment-reply"
v-for="(item, index) in commentReplies" :key="index">
<van-col span="3">
<van-image round src="https://p3.pstatp.com/thumb/1480/7186611868" class="article-avatar">van-image>
van-col>
<van-col span="21">
<van-row type="flex" align="center" justify="space-between">
<van-col class="comment-author" v-html="item.authorName">van-col>
<van-col>
<van-button round :icon="item.operation === 0 ? 'good-job' : 'good-job-o'" size="normal"
@click="handleClickCommentReplyLike(item)">{{ item.likes || '' }}
van-button>
van-col>
van-row>
<van-row>
<van-col class="comment-content" v-html="item.content">van-col>
van-row>
<van-row type="flex" align="center">
<van-col span="10" class="comment-time">
{{ item.createdTime | timestampToDateTime }}
van-col>
van-row>
van-col>
van-row>
van-list>
<van-row type="flex" justify="space-around" align="center" class="comment-reply-bottom-bar">
<van-col span="13">
<van-field v-model="commentReplyValue" placeholder="写评论">
<template #button>
<van-button icon="back-top" @click="handleSaveCommentReply">van-button>
template>
van-field>
van-col>
<van-col span="3">
<van-button icon="comment-o">van-button>
van-col>
<van-col span="3">
<van-button icon="star-o">van-button>
van-col>
<van-col span="3">
<van-button icon="share-o">van-button>
van-col>
van-row>
van-popup>
div>
<script src=" https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js">
script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/vant.min.js">script>
<#--<script src="https://unpkg.com/axios/dist/axios.min.js">script>-->
<script src="../../../plugins/js/axios.min.js">script>
<script src="../../../plugins/js/index.js">script>
body>
html>
新建 heima-leadnews-service/heima-leadnews-article/src/main/resources/a.json
文件:
[
{
"type": "text",
"value": "Java虚拟机——JVM\n\n- JVM(Java Virtual Machine ):Java虚拟机,简称JVM,是运行所有Java程序的假想计算机,是Java程序的运行环境,是Java 最具吸引力的特性之一。我们编写的Java代码,都运行在JVM 之上。\n- 跨平台:任何软件的运行,都必须要运行在操作系统之上,而我们用Java编写的软件可以运行在任何的操作系统上,这个特性称为Java语言的跨平台特性。该特性是由JVM实现的,我们编写的程序运行在JVM上,而JVM运行在操作系统上。\n"
},
{
"type": "image",
"value": "http://192.168.200.130/group1/M00/00/00/wKjIgl9V2n6AArZsAAGMmaPdt7w502.png"
},
{
"type": "text",
"value": "如图所示,Java的虚拟机本身不具备跨平台功能的,每个操作系统下都有不同版本的虚拟机。\n\n问题1: Java 是如何实现跨平台的呢?\n\n- 答:因为在不同操作系统中都安装了对应版本的 JVM 虚拟机\n- 注意: Java程序想要运行, 必须依赖于JVM虚拟机.\n\n问题2: JVM 本身是否允许跨平台呢?\n\n- 答:不允许,允许跨平台的是 Java 程序,而不是虚拟机。\n"
}
]
新增 D:\tem\css\index.css
文件:
html {
overflow-x: hidden;
}
#app {
position: relative;
width: 750px;
margin: 0 auto;
color: #333;
background-color: #f8f8f8;
}
.article {
padding: 0 40px 120px;
}
.article-title {
margin-top: 48px;
font-size: 40px;
font-weight: bold;
color: #3A3A3A;
line-height: 65px;
}
.article-header {
margin-top: 57px;
}
.article-content {
margin-top: 39px;
}
.article-avatar {
width: 70px;
height: 70px;
}
.article-author {
font-size: 28px;
font-weight: 400;
color: #3A3A3A;
}
.article-publish-time {
font-size: 24px;
font-weight: 400;
color: #B4B4B4;
}
.article-focus {
width: 170px;
height: 58px;
font-size: 28px;
font-weight: 400;
color: #FFFFFF;
}
.article-text {
font-size: 32px;
font-weight: 400;
color: #3A3A3A;
line-height: 56px;
text-align: justify;
}
.article-action {
margin-top: 59px;
}
.article-like {
width: 156px;
height: 58px;
font-size: 25px;
font-weight: 400;
color: #777777;
}
.article-unlike {
width: 156px;
height: 58px;
margin-left: 42px;
font-size: 25px;
font-weight: 400;
color: #E22829;
}
.article-comment {
margin-top: 69px;
}
.comment-author {
font-size: 24px;
font-weight: 400;
color: #777777;
line-height: 49px;
}
.comment-content {
font-size: 32px;
font-weight: 400;
color: #3A3A3A;
line-height: 49px;
}
.comment-time {
font-size: 24px;
font-weight: 400;
color: #B4B4B4;
line-height: 49px;
}
.article-comment-reply {
padding: 40px;
}
.article-bottom-bar, .comment-reply-bottom-bar {
position: fixed;
bottom: 0;
width: 750px;
height: 99px;
background: #F4F5F6;
}
.article-bottom-bar .van-field, .comment-reply-bottom-bar .van-field {
width: 399px;
height: 64px;
background: #FFFFFF;
border: 2px solid #EEEEEE;
border-radius: 32px;
font-size: 25px;
font-weight: 400;
color: #777777;
}
.article-bottom-bar .van-button, .comment-reply-bottom-bar .van-button {
background-color: transparent;
border-color: transparent;
font-size: 25px;
font-weight: 400;
color: #777777;
}
新增 D:\tem\js\index.js
文件:
// 初始化 Vue 实例
new Vue({
el: '#app',
data() {
return {
// Minio模板应该写真实接口地址
baseUrl: 'http://192.168.200.150:51601', //'http://172.16.17.191:5001',
token: '',
equipmentId: '',
articleId: '',
title: '',
authorId: 0,
authorName: '',
publishTime: '',
relation: {
islike: false,
isunlike: false,
iscollection: false,
isfollow: false,
isforward: false
},
followLoading: false,
likeLoading: false,
unlikeLoading: false,
collectionLoading: false,
// 评论
comments: [],
commentsLoading: false,
commentsFinished: false,
commentValue: '',
currentCommentId: '',
// 评论回复
commentReplies: [],
commentRepliesLoading: false,
commentRepliesFinished: false,
commentReplyValue: '',
showPopup: false
}
},
filters: {
// TODO: js计算时间差
timestampToDateTime: function (value) {
if (!value) return ''
const date = new Date(value)
const Y = date.getFullYear() + '-'
const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
const D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' '
const h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':'
const m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':'
const s = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds())
return Y + M + D + h + m + s
}
},
created() {
this.token = this.getQueryVariable('token')
this.equipmentId = this.getQueryVariable('equipmentId')
this.articleId = this.getQueryVariable('articleId')
this.title = this.getQueryVariable('title')
const authorId = this.getQueryVariable('authorId')
if (authorId) {
this.authorId = parseInt(authorId, 10)
}
this.authorName = this.getQueryVariable('authorName')
const publishTime = this.getQueryVariable('publishTime')
if (publishTime) {
this.publishTime = parseInt(publishTime, 10)
}
this.loadArticleBehavior()
this.readArticleBehavior()
},
methods: {
// 加载文章评论
async loadArticleComments(index = 1, minDate = 20000000000000) {
const url = `${this.baseUrl}/comment/api/v1/comment/load`
const data = { articleId: this.articleId, index: index, minDate: minDate }
const config = { headers: { 'token': this.token } }
try {
const { status, data: { code, errorMessage, data: comments } } = await axios.post(url, data, config)
if (status !== 200) {
vant.Toast.fail('当前系统正在维护,请稍后重试')
return
}
if (code !== 200) {
vant.Toast.fail(errorMessage)
return
}
if (comments.length) {
this.comments = this.comments.concat(comments)
}
// 加载状态结束
this.commentsLoading = false;
// 数据全部加载完成
if (!comments.length) {
this.commentsFinished = true
}
} catch (err) {
this.commentsLoading = false
this.commentsFinished = true
console.log('err: ' + err)
}
},
// 滚动加载文章评论
onLoadArticleComments() {
let index = undefined
let minDate = undefined
if (this.comments.length) {
index = 2
minDate = this.comments[this.comments.length - 1].createdTime
}
this.loadArticleComments(index, minDate)
},
// 加载文章行为
async loadArticleBehavior() {
const url = `${this.baseUrl}/article/api/v1/article/load_article_behavior/`
const data = { equipmentId: this.equipmentId, articleId: this.articleId, authorId: this.authorId }
const config = { headers: { 'token': this.token } }
try {
const { status, data: { code, errorMessage, data: relation } } = await axios.post(url, data, config)
if (status !== 200) {
vant.Toast.fail('当前系统正在维护,请稍后重试')
return
}
if (code !== 200) {
vant.Toast.fail(errorMessage)
return
}
this.relation = relation
} catch (err) {
console.log('err: ' + err)
}
},
//阅读文章行为
async readArticleBehavior(){
const url = `${this.baseUrl}/behavior/api/v1/read_behavior`
const data = {equipmentId:this.equipmentId,articleId:this.articleId,count:1,readDuration:0,percentage:0,loadDuration:0}
const config = {headers:{'token':this.token}}
try{
const {status,data:{code,errorMessage}} = await axios.post(url,data,config)
if(status !== 200){
vant.Toast.fail("当前系统正在维护,请稍后重试")
return
}
if(code !== 0){
vant.Toast.fail(errorMessage)
return
}
}catch (err){
console.log('err: '+ err)
}
},
// 关注/取消关注
async handleClickArticleFollow() {
const url = `${this.baseUrl}/user/api/v1/user/user_follow/`
const data = { authorId: this.authorId, operation: this.relation.isfollow ? 1 : 0, articleId: this.articleId }
const config = { headers: { 'token': this.token } }
this.followLoading = true
try {
const { status, data: { code, errorMessage } } = await axios.post(url, data, config)
if (status !== 200) {
vant.Toast.fail('当前系统正在维护,请稍后重试')
return
}
if (code !== 200) {
vant.Toast.fail(errorMessage)
return
}
this.relation.isfollow = !this.relation.isfollow
vant.Toast.success(this.relation.isfollow ? '成功关注' : '成功取消关注')
} catch (err) {
console.log('err: ' + err)
}
this.followLoading = false
},
// 点赞/取消赞
async handleClickArticleLike() {
const url = `${this.baseUrl}/behavior/api/v1/likes_behavior/`
const data = { equipmentId: this.equipmentId, articleId: this.articleId, type: 0, operation: this.relation.islike ? 1 : 0 }
const config = { headers: { 'token': this.token } }
this.likeLoading = true
try {
const { status, data: { code, errorMessage } } = await axios.post(url, data, config)
if (status !== 200) {
vant.Toast.fail('当前系统正在维护,请稍后重试')
return
}
if (code !== 200) {
vant.Toast.fail(errorMessage)
return
}
this.relation.islike = !this.relation.islike
vant.Toast.success(this.relation.islike ? '点赞操作成功' : '取消点赞操作成功')
} catch (err) {
console.log('err: ' + err)
}
this.likeLoading = false
},
// 不喜欢/取消不喜欢
async handleClickArticleUnlike() {
const url = `${this.baseUrl}/behavior/api/v1/un_likes_behavior/`
const data = { equipmentId: this.equipmentId, articleId: this.articleId, type: this.relation.isunlike ? 1 : 0 }
const config = { headers: { 'token': this.token } }
this.unlikeLoading = true
try {
const { status, data: { code, errorMessage } } = await axios.post(url, data, config)
if (status !== 200) {
vant.Toast.fail('当前系统正在维护,请稍后重试')
return
}
if (code !== 200) {
vant.Toast.fail(errorMessage)
return
}
this.relation.isunlike = !this.relation.isunlike
vant.Toast.success(this.relation.isunlike ? '不喜欢操作成功' : '取消不喜欢操作成功')
} catch (err) {
console.log('err: ' + err)
}
this.unlikeLoading = false
},
// 提交评论
async handleSaveComment() {
if (!this.commentValue) {
vant.Toast.fail('评论内容不能为空')
return
}
if (this.commentValue.length > 140) {
vant.Toast.fail('评论字数不能超过140字')
return
}
const url = `${this.baseUrl}/comment/api/v1/comment/save`
const data = { articleId: this.articleId, content: this.commentValue }
const config = { headers: { 'token': this.token } }
try {
const { status, data: { code, errorMessage } } = await axios.post(url, data, config)
if (status !== 200) {
vant.Toast.fail('当前系统正在维护,请稍后重试')
return
}
if (code !== 200) {
vant.Toast.fail(errorMessage)
return
}
vant.Toast.success('评论成功')
this.commentValue = ''
this.comments = []
this.loadArticleComments()
this.commentsFinished = false;
} catch (err) {
console.log('err: ' + err)
}
},
// 页面滚动到评论区
handleScrollIntoCommentView() {
document.getElementById('#comment-view').scrollIntoView({ behavior: 'smooth' })
},
// 收藏/取消收藏
async handleClickArticleCollection() {
const url = `${this.baseUrl}/article/api/v1/collection_behavior/`
const data = { equipmentId: this.equipmentId, entryId: this.articleId, publishedTime: this.publishTime, type: 0, operation: this.relation.iscollection ? 1 :0 }
const config = { headers: { 'token': this.token } }
this.collectionLoading = true
try {
const { status, data: { code, errorMessage } } = await axios.post(url, data, config)
if (status !== 200) {
vant.Toast.fail('当前系统正在维护,请稍后重试')
return
}
if (code !== 200) {
vant.Toast.fail(errorMessage)
return
}
this.relation.iscollection = !this.relation.iscollection
vant.Toast.success(this.relation.iscollection ? '收藏操作成功' : '取消收藏操作成功')
} catch (err) {
console.log('err: ' + err)
}
this.collectionLoading = false
},
// 评论点赞
async handleClickCommentLike(comment) {
const commentId = comment.id
const operation = comment.operation === 0 ? 1 : 0
const url = `${this.baseUrl}/comment/api/v1/comment/like`
const data = { commentId: comment.id, operation: operation }
const config = { headers: { 'token': this.token } }
try {
const { status, data: { code, errorMessage, data: { likes } } } = await axios.post(url, data, config)
if (status !== 200) {
vant.Toast.fail('当前系统正在维护,请稍后重试')
return
}
if (code !== 200) {
vant.Toast.fail(errorMessage)
return
}
const item = this.comments.find((item) => {
return item.id === commentId
})
item.operation = operation
item.likes = likes
vant.Toast.success((operation === 0 ? '点赞' : '取消点赞') + '操作成功!')
} catch (err) {
console.log('err: ' + err)
}
},
// 弹出评论回复Popup
showCommentRepliesPopup(commentId) {
this.showPopup = true;
this.currentCommentId = commentId
this.commentReplies = []
this.commentRepliesFinished = false
},
// 加载评论回复
async loadCommentReplies(minDate = 20000000000000) {
const url = `${this.baseUrl}/comment/api/v1/comment_repay/load`
const data = { commentId: this.currentCommentId, 'minDate': minDate}
const config = { headers: { 'token': this.token } }
try {
const { status, data: { code, errorMessage, data: commentReplies } } = await axios.post(url, data, config)
if (status !== 200) {
vant.Toast.fail('当前系统正在维护,请稍后重试')
return
}
if (code !== 200) {
vant.Toast.fail(errorMessage)
return
}
if (commentReplies.length) {
this.commentReplies = this.commentReplies.concat(commentReplies)
}
// 加载状态结束
this.commentRepliesLoading = false;
// 数据全部加载完成
if (!commentReplies.length) {
this.commentRepliesFinished = true
}
} catch (err) {
this.commentRepliesLoading = false
this.commentRepliesFinished = true
console.log('err: ' + err)
}
},
// 滚动加载评论回复
onLoadCommentReplies() {
let minDate = undefined
if (this.commentReplies.length) {
minDate = this.commentReplies[this.commentReplies.length - 1].createdTime
}
this.loadCommentReplies(minDate)
},
// 提交评论回复
async handleSaveCommentReply() {
if (!this.commentReplyValue) {
vant.Toast.fail('评论内容不能为空')
return
}
if (this.commentReplyValue.length > 140) {
vant.Toast.fail('评论字数不能超过140字')
return
}
const url = `${this.baseUrl}/comment/api/v1/comment_repay/save`
const data = { commentId: this.currentCommentId, content: this.commentReplyValue }
const config = { headers: { 'token': this.token } }
try {
const { status, data: { code, errorMessage } } = await axios.post(url, data, config)
if (status !== 200) {
vant.Toast.fail('当前系统正在维护,请稍后重试')
return
}
if (code !== 200) {
vant.Toast.fail(errorMessage)
return
}
vant.Toast.success('评论成功')
this.commentReplyValue = ''
this.commentReplies = []
this.comments = []
// 刷新评论回复列表
this.loadCommentReplies()
// 刷新文章评论列表
this.loadArticleComments()
} catch (err) {
console.log('err: ' + err)
}
},
// 评论回复点赞
async handleClickCommentReplyLike(commentReply) {
const commentReplyId = commentReply.id
const operation = commentReply.operation === 0 ? 1 : 0
const url = `${this.baseUrl}/comment/api/v1/comment_repay/like`
const data = { commentRepayId: commentReplyId, 'operation': operation }
const config = { headers: { 'token': this.token } }
try {
const { status, data: { code, errorMessage, data: { likes } } } = await axios.post(url, data, config)
if (status !== 200) {
vant.Toast.fail('当前系统正在维护,请稍后重试')
return
}
if (code !== 200) {
vant.Toast.fail(errorMessage)
return
}
const item = this.commentReplies.find((item) => {
return item.id === commentReplyId
})
item.operation = operation
item.likes = likes
vant.Toast.success((operation === 0 ? '点赞' : '取消点赞') + '操作成功!')
} catch (err) {
console.log('err: ' + err)
}
},
getQueryVariable(aVariable) {
const query = decodeURI(window.location.search).substring(1)
const array = query.split('&')
for (let i = 0; i < array.length; i++) {
const pair = array[i].split('=')
if (pair[0] == aVariable) {
return pair[1]
}
}
return undefined
},
// onSelect(option) {
// vant.Toast(option.name);
// this.showShare = false;
// }
}
})
新增 D:\tem\js\axios.min.js
文件:
/* axios v0.21.1 | (c) 2020 by Matt Zabriskie */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(4),a=n(22),u=n(10),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(9),c.all=function(e){return Promise.all(e)},c.spread=n(25),c.isAxiosError=n(26),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===R.call(e)}function o(e){return"undefined"==typeof e}function s(e){return null!==e&&!o(e)&&null!==e.constructor&&!o(e.constructor)&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}function i(e){return"[object ArrayBuffer]"===R.call(e)}function a(e){return"undefined"!=typeof FormData&&e instanceof FormData}function u(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function c(e){return"string"==typeof e}function f(e){return"number"==typeof e}function p(e){return null!==e&&"object"==typeof e}function d(e){if("[object Object]"!==R.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function l(e){return"[object Date]"===R.call(e)}function h(e){return"[object File]"===R.call(e)}function m(e){return"[object Blob]"===R.call(e)}function y(e){return"[object Function]"===R.call(e)}function g(e){return p(e)&&y(e.pipe)}function v(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function x(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function w(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function b(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.call(null,e[s],s,e)}function E(){function e(e,n){d(t[n])&&d(e)?t[n]=E(t[n],e):d(e)?t[n]=E({},e):r(e)?t[n]=e.slice():t[n]=e}for(var t={},n=0,o=arguments.length;n<o;n++)b(arguments[n],e);return t}function j(e,t,n){return b(t,function(t,r){n&&"function"==typeof t?e[r]=S(t,n):e[r]=t}),e}function C(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e}var S=n(3),R=Object.prototype.toString;e.exports={isArray:r,isArrayBuffer:i,isBuffer:s,isFormData:a,isArrayBufferView:u,isString:c,isNumber:f,isObject:p,isPlainObject:d,isUndefined:o,isDate:l,isFile:h,isBlob:m,isFunction:y,isStream:g,isURLSearchParams:v,isStandardBrowserEnv:w,forEach:b,merge:E,extend:j,trim:x,stripBOM:C}},function(e,t){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return e.apply(t,n)}}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(5),i=n(6),a=n(7),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(u(n||{},{method:e,url:t,data:(n||{}).data}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(u(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}if(s){var a=e.indexOf("#");a!==-1&&(e=e.slice(0,a)),e+=(e.indexOf("?")===-1?"?":"&")+s}return e}},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(2);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t,n){"use strict";function r(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var o=n(2),s=n(8),i=n(9),a=n(10);e.exports=function(e){r(e),e.headers=e.headers||{},e.data=s(e.data,e.headers,e.transformRequest),e.headers=o.merge(e.headers.common||{},e.headers[e.method]||{},e.headers),o.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]});var t=e.adapter||a.adapter;return t(e).then(function(t){return r(e),t.data=s(t.data,t.headers,e.transformResponse),t},function(t){return i(t)||(r(e),t&&t.response&&(t.response.data=s(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,n){"use strict";function r(e,t){!s.isUndefined(e)&&s.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}function o(){var e;return"undefined"!=typeof XMLHttpRequest?e=n(12):"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process)&&(e=n(12)),e}var s=n(2),i=n(11),a={"Content-Type":"application/x-www-form-urlencoded"},u={adapter:o(),transformRequest:[function(e,t){return i(t,"Accept"),i(t,"Content-Type"),s.isFormData(e)||s.isArrayBuffer(e)||s.isBuffer(e)||s.isStream(e)||s.isFile(e)||s.isBlob(e)?e:s.isArrayBufferView(e)?e.buffer:s.isURLSearchParams(e)?(r(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):s.isObject(e)?(r(t,"application/json;charset=utf-8"),JSON.stringify(e)):e}],transformResponse:[function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(e){}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,validateStatus:function(e){return e>=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},s.forEach(["delete","get","head"],function(e){u.headers[e]={}}),s.forEach(["post","put","patch"],function(e){u.headers[e]=s.merge(a)}),e.exports=u},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(13),s=n(16),i=n(5),a=n(17),u=n(20),c=n(21),f=n(14);e.exports=function(e){return new Promise(function(t,n){var p=e.data,d=e.headers;r.isFormData(p)&&delete d["Content-Type"];var l=new XMLHttpRequest;if(e.auth){var h=e.auth.username||"",m=e.auth.password?unescape(encodeURIComponent(e.auth.password)):"";d.Authorization="Basic "+btoa(h+":"+m)}var y=a(e.baseURL,e.url);if(l.open(e.method.toUpperCase(),i(y,e.params,e.paramsSerializer),!0),l.timeout=e.timeout,l.onreadystatechange=function(){if(l&&4===l.readyState&&(0!==l.status||l.responseURL&&0===l.responseURL.indexOf("file:"))){var r="getAllResponseHeaders"in l?u(l.getAllResponseHeaders()):null,s=e.responseType&&"text"!==e.responseType?l.response:l.responseText,i={data:s,status:l.status,statusText:l.statusText,headers:r,config:e,request:l};o(t,n,i),l=null}},l.onabort=function(){l&&(n(f("Request aborted",e,"ECONNABORTED",l)),l=null)},l.onerror=function(){n(f("Network Error",e,null,l)),l=null},l.ontimeout=function(){var t="timeout of "+e.timeout+"ms exceeded";e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),n(f(t,e,"ECONNABORTED",l)),l=null},r.isStandardBrowserEnv()){var g=(e.withCredentials||c(y))&&e.xsrfCookieName?s.read(e.xsrfCookieName):void 0;g&&(d[e.xsrfHeaderName]=g)}if("setRequestHeader"in l&&r.forEach(d,function(e,t){"undefined"==typeof p&&"content-type"===t.toLowerCase()?delete d[t]:l.setRequestHeader(t,e)}),r.isUndefined(e.withCredentials)||(l.withCredentials=!!e.withCredentials),e.responseType)try{l.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&l.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&l.upload&&l.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){l&&(l.abort(),n(e),l=null)}),p||(p=null),l.send(p)})}},function(e,t,n){"use strict";var r=n(14);e.exports=function(e,t,n){var o=n.config.validateStatus;n.status&&o&&!o(n.status)?t(r("Request failed with status code "+n.status,n.config,null,n.request,n)):e(n)}},function(e,t,n){"use strict";var r=n(15);e.exports=function(e,t,n,o,s){var i=new Error(e);return r(i,t,n,o,s)}},function(e,t){"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,s,i){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(s)&&a.push("domain="+s),i===!0&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t,n){"use strict";var r=n(18),o=n(19);e.exports=function(e,t){return e&&!r(t)?o(e,t):t}},function(e,t){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(2),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,s,i={};return e?(r.forEach(e.split("\n"),function(e){if(s=e.indexOf(":"),t=r.trim(e.substr(0,s)).toLowerCase(),n=r.trim(e.substr(s+1)),t){if(i[t]&&o.indexOf(t)>=0)return;"set-cookie"===t?i[t]=(i[t]?i[t]:[]).concat([n]):i[t]=i[t]?i[t]+", "+n:n}}),i):i}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){function n(e,t){return r.isPlainObject(e)&&r.isPlainObject(t)?r.merge(e,t):r.isPlainObject(t)?r.merge({},t):r.isArray(t)?t.slice():t}function o(o){r.isUndefined(t[o])?r.isUndefined(e[o])||(s[o]=n(void 0,e[o])):s[o]=n(e[o],t[o])}t=t||{};var s={},i=["url","method","data"],a=["headers","auth","proxy","params"],u=["baseURL","transformRequest","transformResponse","paramsSerializer","timeout","timeoutMessage","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","decompress","maxContentLength","maxBodyLength","maxRedirects","transport","httpAgent","httpsAgent","cancelToken","socketPath","responseEncoding"],c=["validateStatus"];r.forEach(i,function(e){r.isUndefined(t[e])||(s[e]=n(void 0,t[e]))}),r.forEach(a,o),r.forEach(u,function(o){r.isUndefined(t[o])?r.isUndefined(e[o])||(s[o]=n(void 0,e[o])):s[o]=n(void 0,t[o])}),r.forEach(c,function(r){r in t?s[r]=n(e[r],t[r]):r in e&&(s[r]=n(void 0,e[r]))});var f=i.concat(a).concat(u).concat(c),p=Object.keys(e).concat(Object.keys(t)).filter(function(e){return f.indexOf(e)===-1});return r.forEach(p,o),s}},function(e,t){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,n){"use strict";function r(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new o(e),t(n.reason))})}var o=n(23);r.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},r.source=function(){var e,t=new r(function(t){e=t});return{token:t,cancel:e}},e.exports=r},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},function(e,t){"use strict";e.exports=function(e){return"object"==typeof e&&e.isAxiosError===!0}}])});
//# sourceMappingURL=axios.min.map
编辑 heima-leadnews-test/minio-demo/src/test/java/com/heima/minio/test/MinIOTest.java
测试类上传CSS:
// 上传css
public static void main(String[] args) {
try {
FileInputStream fileInputStream = new FileInputStream("d:/tem/css/index.css");
// 1. 获取minio的链接信息 创建一个minio客户端
MinioClient minioClient = MinioClient.builder().credentials("minio", "minio123").endpoint("http://192.168.200.130:9000").build();
// 2. 上传
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.object("plugins/css/index.css") // 文件名
.contentType("text/html") // 文件类型
.bucket("leadnews") // 桶名称, 与minio管理界面创建的桶一致
.stream(fileInputStream, fileInputStream.available(), -1).build();
minioClient.putObject(putObjectArgs);
} catch (Exception e) {
e.printStackTrace();
}
}
编辑 heima-leadnews-test/minio-demo/src/test/java/com/heima/minio/test/MinIOTest.java
测试类上传JS:
// 上传js
public static void main(String[] args) {
try {
FileInputStream fileInputStream = new FileInputStream("d:/tem/js/index.js");
// 1. 获取minio的链接信息 创建一个minio客户端
MinioClient minioClient = MinioClient.builder().credentials("minio", "minio123").endpoint("http://192.168.200.130:9000").build();
// 2. 上传
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.object("plugins/js/index.js") // 文件名
.contentType("text/js") // 文件类型
.bucket("leadnews") // 桶名称, 与minio管理界面创建的桶一致
.stream(fileInputStream, fileInputStream.available(), -1).build();
minioClient.putObject(putObjectArgs);
} catch (Exception e) {
e.printStackTrace();
}
}
编辑 heima-leadnews-test/minio-demo/src/test/java/com/heima/minio/test/MinIOTest.java
测试类上传JS:
// 上传axios.min
public static void main(String[] args) {
try {
FileInputStream fileInputStream = new FileInputStream("d:/tem/js/axios.min.js");
// 1. 获取minio的链接信息 创建一个minio客户端
MinioClient minioClient = MinioClient.builder().credentials("minio", "minio123").endpoint("http://192.168.200.130:9000").build();
// 2. 上传
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.object("plugins/js/axios.min.js") // 文件名
.contentType("text/js") // 文件类型
.bucket("leadnews") // 桶名称, 与minio管理界面创建的桶一致
.stream(fileInputStream, fileInputStream.available(), -1).build();
minioClient.putObject(putObjectArgs);
} catch (Exception e) {
e.printStackTrace();
}
}
新建 heima-leadnews-service/heima-leadnews-article/src/main/java/com/heima/article/mapper/ApArticleContentMapper.java
文件:
@Mapper
public interface ApArticleContentMapper extends BaseMapper<ApArticleContent> {
}
新建 heima-leadnews-service/heima-leadnews-article/src/test/java/com/heima/article/test/ArticleFreemarkerTest.java
文件:
@SpringBootTest(classes = ArticleApplication.class)
@RunWith(SpringRunner.class)
public class ArticleFreemarkerTest {
@Autowired
private ApArticleContentMapper apArticleContentMapper;
@Autowired
private Configuration configuration;
@Autowired
private FileStorageService fileStorageService;
@Autowired
private ApArticleService apArticleService;
@Autowired
private ApArticleMapper apArticleMapper;
@Test
public void createStaticUrlTest() throws Exception {
//1.获取文章内容
ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, 1303156149041758210L));
// 2.判断文章内容不为空
if(apArticleContent != null && StringUtils.isNotBlank(apArticleContent.getContent())){
//3.文章内容通过freemarker生成html文件
Template template = configuration.getTemplate("article.ftl");
// 数据模型
Map<String, Object> context = new HashMap<>();
context.put("content", JSONArray.parseArray(apArticleContent.getContent()));
StringWriter out = new StringWriter();
// 合成
template.process(context, out);
//4.把html文件上传到minio中
InputStream in = new ByteArrayInputStream(out.toString().getBytes());
String path = fileStorageService.uploadHtmlFile("", apArticleContent.getArticleId() + ".html", in);
//5.修改ap_article表,保存static_url字段
apArticleService.update(Wrappers.<ApArticle>lambdaUpdate().eq(ApArticle::getId, apArticleContent.getArticleId())
.set(ApArticle::getStaticUrl, path));
// ApArticle article = new ApArticle();
// article.setId(apArticleContent.getArticleId());
// article.setStaticUrl(path);
// apArticleMapper.updateById(article);
}
}
}
minio地址: http://192.168.200.130:9000/minio/leadnews/
启动 ArticleApplication(文章)
、UserApplication(用户)
、AppGatewayApplication(网关)
服务,访问App端: http://localhost:8801/#/login