添加公共讨论区,题目讨论区,比赛评论
This commit is contained in:
parent
60b249f901
commit
ee71b85ab5
46
README.md
46
README.md
|
@ -2,7 +2,7 @@
|
|||
|
||||
基于前后端分离,分布式架构的在线测评平台(hoj)
|
||||
|
||||
在线Demo:[http://www.hcode.top](http://www.hcode.top)
|
||||
在线Demo:[https://www.hcode.top](https://www.hcode.top)
|
||||
|
||||
> 上线日记
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
|||
| 2021-04-19 | 加入rsync实现评测数据同步,修复一些已知的BUG | Himit_ZH |
|
||||
| 2021-04-24 | 加入题目模板,修改页面页脚 | Himit_ZH |
|
||||
| 2021-05-02 | 修复比赛后管理员重判题目导致排行榜失效的问题 | Himit_ZH |
|
||||
| 2021-05-09 | 添加公共讨论区,题目讨论区,比赛评论 | Himit_ZH |
|
||||
|
||||
# 二、部署
|
||||
|
||||
|
@ -35,12 +36,15 @@
|
|||
- [x] 题目支持特别判题
|
||||
- [x] 题目支持可选择性去除提交代码的末尾空白符(会影响CE判定)
|
||||
- [x] 题目支持可选择性允许用户查看各个测试点结果(状态,运行时间,运行空间,OI题目的测试点得分),暂不支持测试点数据公开。
|
||||
- [x] 题目讨论
|
||||
- [x] 管理后台支持题目数据以ZIP上传或手动输入上传
|
||||
- [x] 管理后台支持监控服务系统的状态及各判题服务的状态
|
||||
- [x] 管理后台支持动态修改网站配置,例如邮件系统配置,数据库配置等
|
||||
- [x] 比赛支持封榜,支持ACM与OI模式
|
||||
- [x] 比赛支持私有赛(需要密码才可查看与提交),保护赛(每个用户都可查看,提交需要密码),公开赛(每个用户都可查看与提交)三种模式
|
||||
- [x] 用户提交失败时可重新提交,管理员支持提交重判与比赛题目所有提交重判
|
||||
- [x] 公共讨论区
|
||||
- [x] 比赛讨论
|
||||
- [ ] ......
|
||||
- 后端:
|
||||
- [x] Web框架技术以Springboot为主
|
||||
|
@ -111,33 +115,33 @@
|
|||
|
||||
> 首页页面
|
||||
|
||||
![hoj1](https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/hoj1.png)
|
||||
![首页](https://img-blog.csdnimg.cn/20210509232352226.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70)
|
||||
|
||||
|
||||
|
||||
> 题目列表页
|
||||
|
||||
![hoj2](https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/hoj2.png)
|
||||
![题目列表](https://img-blog.csdnimg.cn/20210509232501952.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70)
|
||||
|
||||
|
||||
|
||||
> 题目详情页
|
||||
|
||||
![hoj7](https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/hoj7.png)
|
||||
|
||||
|
||||
![题目详情页](https://img-blog.csdnimg.cn/20210509232609398.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70)
|
||||
|
||||
> 比赛列表页
|
||||
|
||||
![hoj3](https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/hoj3.png)
|
||||
![比赛列表](https://img-blog.csdnimg.cn/20210509232701288.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70)
|
||||
|
||||
|
||||
|
||||
> 比赛详情首页
|
||||
|
||||
![hoj4](https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/hoj4.png)
|
||||
![比赛详情](https://img-blog.csdnimg.cn/20210509232843932.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70#pic_center)
|
||||
|
||||
> 提交列表页
|
||||
|
||||
![提交列表](https://img-blog.csdnimg.cn/20210509232933478.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70)
|
||||
|
||||
> 排行榜
|
||||
|
||||
|
@ -145,15 +149,29 @@
|
|||
|
||||
|
||||
|
||||
> 公共讨论区
|
||||
|
||||
![公共讨论区](https://img-blog.csdnimg.cn/2021050923351998.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70)
|
||||
|
||||
|
||||
|
||||
> 评论组件
|
||||
|
||||
![评论组件](C:\Users\HZH990730\AppData\Roaming\Typora\typora-user-images\image-20210509233700989.png)
|
||||
|
||||
|
||||
|
||||
> 个人信息页
|
||||
|
||||
![hoj6](https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/hoj6.png)
|
||||
![个人信息](https://img-blog.csdnimg.cn/20210509233300701.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70)
|
||||
|
||||
|
||||
|
||||
> 个人设置页
|
||||
|
||||
![hoj8](https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/hoj8.png)
|
||||
![个人设置](https://img-blog.csdnimg.cn/20210509233439791.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -165,11 +183,15 @@
|
|||
|
||||
> 部分手机端显示
|
||||
|
||||
![hojmb1](https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/hojmb1.png)
|
||||
![](https://img-blog.csdnimg.cn/20210509233756882.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70)
|
||||
|
||||
|
||||
|
||||
![评论区](https://img-blog.csdnimg.cn/20210509233845230.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70)
|
||||
|
||||
|
||||
|
||||
|
||||
![hojmb2](https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/hojmb2.png)
|
||||
|
||||
# 四、特判程序例子
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
-e JVM_XMN=64m \
|
||||
-e MODE=standalone \
|
||||
-e SPRING_DATASOURCE_PLATFORM=mysql \
|
||||
-e MYSQL_SERVICE_HOST="数据库所在服务器ip" \
|
||||
-e MYSQL_SERVICE_HOST="数据库所在服务器ip或使用容器ip(172.18.0.1)" \
|
||||
-e MYSQL_SERVICE_PORT=3306 \
|
||||
-e MYSQL_SERVICE_USER=root \
|
||||
-e MYSQL_SERVICE_PASSWORD="数据库密码" \
|
||||
|
@ -82,7 +82,7 @@
|
|||
nacos/nacos-server
|
||||
```
|
||||
|
||||
3. 查看自定义网络中各容器ip,一般该hoj-network的ip应该是**172.18.0.2或172.19.0.2**
|
||||
3. 查看自定义网络中各容器ip
|
||||
|
||||
```shell
|
||||
//查看网络
|
||||
|
@ -91,7 +91,7 @@
|
|||
docker network inspect hoj-network
|
||||
```
|
||||
|
||||
6. 连上nacos,将后端服务需要的配置添加进去
|
||||
4. 连上nacos,将后端服务需要的配置添加进去
|
||||
|
||||
```she
|
||||
http://ip:8848/nacos/index.html
|
||||
|
@ -106,23 +106,23 @@
|
|||
|
||||
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210416154647434.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70)
|
||||
|
||||
7. hoj-data-backup-prod.yml的配置如下,请自行修改
|
||||
5. hoj-data-backup-prod.yml的配置如下,请自行修改
|
||||
|
||||
```yaml
|
||||
hoj:
|
||||
jwt:
|
||||
# 加密秘钥
|
||||
secret: zsc-acm-hoj
|
||||
# token有效时长,24小时,单位秒
|
||||
expire: 86400
|
||||
# 6小时内还有请求,可进行刷新
|
||||
checkRefreshExpire: 21600
|
||||
# token有效时长,3*3600*24,单位秒
|
||||
expire: 259200
|
||||
# 2*3600*24s内还有请求,可进行刷新
|
||||
checkRefreshExpire: 172800
|
||||
header: token
|
||||
judge:
|
||||
# 调用判题服务器的token
|
||||
token: zsc-acm-hoj-judge-server
|
||||
db: # mysql数据库服务配置
|
||||
host: your_mysql_host
|
||||
host: your_mysql_host #如果是公用容器网络 请使用网络ip 例如1.1的172.18.0.1
|
||||
port: your_mysql_port
|
||||
name: your_mysql_database_name # 默认hoj
|
||||
username: your_mysql_username
|
||||
|
@ -135,7 +135,7 @@
|
|||
port: your_email_port
|
||||
background-img: https://cdn.jsdelivr.net/gh/HimitZH/CDN/images/HCODE.png # 邮箱系统发送邮件模板的背景图片地址
|
||||
redis: # redis服务配置
|
||||
host: your_redis_host
|
||||
host: your_redis_host #如果是公用容器网络 请使用网络ip 例如1.3的172.18.0.2
|
||||
port: 6371
|
||||
password: your_redis_password
|
||||
web-config:
|
||||
|
@ -160,7 +160,7 @@
|
|||
password: cf账号1密码,cf账号2密码,...
|
||||
```
|
||||
|
||||
8. 添加好后点击发布,再次添加hoj-judge-server-prod.yml,流程一样
|
||||
6. 添加好后点击发布,再次添加hoj-judge-server-prod.yml,流程一样
|
||||
|
||||
```yaml
|
||||
hoj:
|
||||
|
@ -207,7 +207,7 @@ docker run -d --name redis -p 6379:6379 -v /hoj/redis/data:/data -v /hoj/redis/c
|
|||
```yaml
|
||||
hoj-backstage:
|
||||
port: 6688
|
||||
nacos-url: 172.18.0.2:8848 # nacos地址,如果使用了docker network 可用使用network的ip 否则请使用服务器ip
|
||||
nacos-url: 172.18.0.3:8848 # nacos地址,如果使用了docker network 可用使用network的ip 否则请使用服务器ip
|
||||
```
|
||||
|
||||
2. 使用cmd打开当前JudgeServer文件夹路径,然后使用mvn命令进行打包成jar包
|
||||
|
@ -371,11 +371,7 @@ docker run -d --name redis -p 6379:6379 -v /hoj/redis/data:/data -v /hoj/redis/c
|
|||
|
||||
2. 前提是本地有vue-cli4,请自行百度下载
|
||||
|
||||
3. 进入/hoj-vue文件夹,修改生产环境下的后端服务地址
|
||||
|
||||
![在这里插入图片描述](https://img-blog.csdnimg.cn/2021041616214988.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzg1MzA5Nw==,size_16,color_FFFFFF,t_70)
|
||||
|
||||
4. 然后在当前路径运行打包命令
|
||||
4. 然后在当前src路径运行打包命令
|
||||
|
||||
```powershell
|
||||
npm run build
|
||||
|
|
|
@ -130,11 +130,11 @@
|
|||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--热部署工具-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<!-- <!–热部署工具–>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-devtools</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<!--模板引擎,主要用于发送邮件模板-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -164,6 +164,12 @@
|
|||
<artifactId>jsoup</artifactId>
|
||||
<version>1.13.1</version>
|
||||
</dependency>
|
||||
<!--emoji表情格式转换-->
|
||||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>java-emoji-converter</artifactId>
|
||||
<version>1.0.1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -1,15 +1,224 @@
|
|||
package top.hcode.hoj.controller.oj;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.binarywang.java.emoji.EmojiConverter;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import top.hcode.hoj.common.result.CommonResult;
|
||||
import top.hcode.hoj.pojo.entity.Comment;
|
||||
import top.hcode.hoj.pojo.entity.CommentLike;
|
||||
import top.hcode.hoj.pojo.entity.Reply;
|
||||
import top.hcode.hoj.pojo.vo.CommentsVo;
|
||||
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
||||
import top.hcode.hoj.service.impl.CommentLikeServiceImpl;
|
||||
import top.hcode.hoj.service.impl.CommentServiceImpl;
|
||||
import top.hcode.hoj.service.impl.ReplyServiceImpl;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2020/10/29 10:14
|
||||
* @Date: 2021/5/5 15:41
|
||||
* @Description:
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class CommentController {
|
||||
|
||||
}
|
||||
@Autowired
|
||||
private CommentServiceImpl commentService;
|
||||
|
||||
@Autowired
|
||||
private CommentLikeServiceImpl commentLikeService;
|
||||
|
||||
@Autowired
|
||||
private ReplyServiceImpl replyService;
|
||||
|
||||
private static EmojiConverter emojiConverter = EmojiConverter.getInstance();
|
||||
|
||||
@GetMapping("/comments")
|
||||
public CommonResult getComments(@RequestParam(value = "cid", required = false) Long cid,
|
||||
@RequestParam(value = "did", required = false) Integer did,
|
||||
@RequestParam(value = "limit", required = false, defaultValue = "20") Integer limit,
|
||||
@RequestParam(value = "currentPage", required = false, defaultValue = "1") Integer currentPage,
|
||||
HttpServletRequest request) {
|
||||
|
||||
IPage<CommentsVo> commentList = commentService.getCommentList(limit, currentPage, cid, did);
|
||||
|
||||
// 获取当前登录的用户
|
||||
HttpSession session = request.getSession();
|
||||
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||
|
||||
HashMap<Integer, Boolean> commentLikeMap = new HashMap<>();
|
||||
|
||||
if (userRolesVo != null) {
|
||||
// 如果是有登录 需要检查是否对评论有点赞
|
||||
List<Integer> commentIdList = new LinkedList<>();
|
||||
|
||||
for (CommentsVo commentsVo : commentList.getRecords()) {
|
||||
commentIdList.add(commentsVo.getId());
|
||||
}
|
||||
|
||||
if (commentIdList.size() > 0) {
|
||||
|
||||
QueryWrapper<CommentLike> commentLikeQueryWrapper = new QueryWrapper<>();
|
||||
commentLikeQueryWrapper.in("cid", commentIdList);
|
||||
|
||||
List<CommentLike> commentLikeList = commentLikeService.list(commentLikeQueryWrapper);
|
||||
|
||||
// 如果存在记录需要修正Map为true
|
||||
for (CommentLike tmp : commentLikeList) {
|
||||
commentLikeMap.put(tmp.getCid(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CommonResult.successResponse(MapUtil.builder()
|
||||
.put("commentList", commentList)
|
||||
.put("commentLikeMap", commentLikeMap).map(), "获取成功");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/comment")
|
||||
@RequiresAuthentication
|
||||
public CommonResult addComment(@RequestBody Comment comment, HttpServletRequest request) {
|
||||
// 获取当前登录的用户
|
||||
HttpSession session = request.getSession();
|
||||
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||
|
||||
comment.setFromAvatar(userRolesVo.getAvatar())
|
||||
.setFromName(userRolesVo.getUsername())
|
||||
.setFromUid(userRolesVo.getUid());
|
||||
|
||||
if (SecurityUtils.getSubject().hasRole("root")) {
|
||||
comment.setFromRole("root");
|
||||
} else if (SecurityUtils.getSubject().hasRole("admin")) {
|
||||
comment.setFromRole("admin");
|
||||
} else {
|
||||
comment.setFromRole("user");
|
||||
}
|
||||
|
||||
// 带有表情的字符串转换为编码
|
||||
comment.setContent(emojiConverter.toHtml(comment.getContent()));
|
||||
|
||||
boolean isOk = commentService.saveOrUpdate(comment);
|
||||
|
||||
if (isOk) {
|
||||
CommentsVo commentsVo = new CommentsVo();
|
||||
commentsVo.setContent(comment.getContent());
|
||||
commentsVo.setId(comment.getId());
|
||||
commentsVo.setFromAvatar(comment.getFromAvatar());
|
||||
commentsVo.setFromName(comment.getFromName());
|
||||
commentsVo.setFromUid(comment.getFromUid());
|
||||
commentsVo.setLikeNum(0);
|
||||
commentsVo.setGmtCreate(comment.getGmtCreate());
|
||||
commentsVo.setReplyList(new LinkedList<>());
|
||||
return CommonResult.successResponse(commentsVo, "评论成功");
|
||||
} else {
|
||||
return CommonResult.errorResponse("评论失败,请重新尝试!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/comment-like")
|
||||
@RequiresAuthentication
|
||||
@Transactional
|
||||
public CommonResult addDiscussionLike(@RequestParam("cid") Integer cid,
|
||||
@RequestParam("toLike") Boolean toLike,
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 获取当前登录的用户
|
||||
HttpSession session = request.getSession();
|
||||
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||
|
||||
QueryWrapper<CommentLike> commentLikeQueryWrapper = new QueryWrapper<>();
|
||||
commentLikeQueryWrapper.eq("cid", cid).eq("uid", userRolesVo.getUid());
|
||||
|
||||
CommentLike commentLike = commentLikeService.getOne(commentLikeQueryWrapper, false);
|
||||
|
||||
if (toLike) { // 添加点赞
|
||||
if (commentLike == null) { // 如果不存在就添加
|
||||
boolean isSave = commentLikeService.saveOrUpdate(new CommentLike()
|
||||
.setUid(userRolesVo.getUid())
|
||||
.setCid(cid));
|
||||
if (!isSave) {
|
||||
return CommonResult.errorResponse("点赞失败,请重试尝试!");
|
||||
}
|
||||
}
|
||||
// 点赞+1
|
||||
UpdateWrapper<Comment> commentUpdateWrapper = new UpdateWrapper<>();
|
||||
commentUpdateWrapper.setSql("like_num=like_num+1").eq("id", cid);
|
||||
commentService.update(commentUpdateWrapper);
|
||||
return CommonResult.successResponse(null, "点赞成功");
|
||||
} else { // 取消点赞
|
||||
if (commentLike != null) { // 如果存在就删除
|
||||
boolean isDelete = commentLikeService.removeById(commentLike.getId());
|
||||
if (!isDelete) {
|
||||
return CommonResult.errorResponse("取消点赞失败,请重试尝试!");
|
||||
}
|
||||
}
|
||||
// 点赞-1
|
||||
UpdateWrapper<Comment> commentUpdateWrapper = new UpdateWrapper<>();
|
||||
commentUpdateWrapper.setSql("like_num=like_num-1").eq("id", cid);
|
||||
commentService.update(commentUpdateWrapper);
|
||||
return CommonResult.successResponse(null, "取消成功");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/reply")
|
||||
public CommonResult getAllReply(@RequestParam("commentId") Integer commentId) {
|
||||
|
||||
QueryWrapper<Reply> replyQueryWrapper = new QueryWrapper<>();
|
||||
replyQueryWrapper.eq("comment_id", commentId);
|
||||
replyQueryWrapper.orderByDesc("gmt_create");
|
||||
|
||||
List<Reply> replyList = replyService.list(replyQueryWrapper);
|
||||
|
||||
return CommonResult.successResponse(replyList, "获取全部回复列表成功");
|
||||
}
|
||||
|
||||
@PostMapping("/reply")
|
||||
@RequiresAuthentication
|
||||
public CommonResult addReply(@RequestBody Reply reply, HttpServletRequest request) {
|
||||
// 获取当前登录的用户
|
||||
HttpSession session = request.getSession();
|
||||
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||
|
||||
reply.setFromAvatar(userRolesVo.getAvatar())
|
||||
.setFromName(userRolesVo.getUsername())
|
||||
.setFromUid(userRolesVo.getUid());
|
||||
|
||||
if (SecurityUtils.getSubject().hasRole("root")) {
|
||||
reply.setFromRole("root");
|
||||
} else if (SecurityUtils.getSubject().hasRole("admin")) {
|
||||
reply.setFromRole("admin");
|
||||
} else {
|
||||
reply.setFromRole("user");
|
||||
}
|
||||
// 带有表情的字符串转换为编码
|
||||
reply.setContent(emojiConverter.toHtml(reply.getContent()));
|
||||
|
||||
boolean isOk = replyService.saveOrUpdate(reply);
|
||||
|
||||
if (isOk) {
|
||||
return CommonResult.successResponse(reply, "评论成功");
|
||||
} else {
|
||||
return CommonResult.errorResponse("评论失败,请重新尝试!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
package top.hcode.hoj.controller.oj;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import top.hcode.hoj.common.result.CommonResult;
|
||||
import top.hcode.hoj.pojo.entity.Category;
|
||||
import top.hcode.hoj.pojo.entity.Discussion;
|
||||
import top.hcode.hoj.pojo.entity.DiscussionLike;
|
||||
import top.hcode.hoj.pojo.vo.DiscussionVo;
|
||||
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
||||
import top.hcode.hoj.service.impl.CategoryServiceImpl;
|
||||
import top.hcode.hoj.service.impl.DiscussionLikeServiceImpl;
|
||||
import top.hcode.hoj.service.impl.DiscussionServiceImpl;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/05/04 10:14
|
||||
* @Description: 负责讨论与评论模块的数据接口
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class DiscussionController {
|
||||
|
||||
@Autowired
|
||||
private DiscussionServiceImpl discussionService;
|
||||
|
||||
@Autowired
|
||||
private DiscussionLikeServiceImpl discussionLikeService;
|
||||
|
||||
@Autowired
|
||||
private CategoryServiceImpl categoryService;
|
||||
|
||||
@GetMapping("/discussions")
|
||||
public CommonResult getDiscussionList(@RequestParam(value = "limit", required = false, defaultValue = "15") Integer limit,
|
||||
@RequestParam(value = "currentPage", required = false, defaultValue = "1") Integer currentPage,
|
||||
@RequestParam(value = "cid", required = false) Integer categoryId,
|
||||
@RequestParam(value = "pid", required = false) String pid,
|
||||
@RequestParam(value = "keyword", required = false) String keyword) {
|
||||
|
||||
QueryWrapper<Discussion> discussionQueryWrapper = new QueryWrapper<>();
|
||||
|
||||
IPage<Discussion> iPage = new Page<>(currentPage, limit);
|
||||
|
||||
if (categoryId != null) {
|
||||
discussionQueryWrapper.eq("category_id", categoryId);
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(keyword)) {
|
||||
|
||||
final String key = keyword.trim();
|
||||
|
||||
discussionQueryWrapper.and(wrapper -> wrapper.like("title", key).or()
|
||||
.like("author", key).or()
|
||||
.like("description", key));
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(pid)) {
|
||||
discussionQueryWrapper.eq("pid", pid);
|
||||
}
|
||||
|
||||
discussionQueryWrapper.eq("status", false)
|
||||
.orderByDesc("top_priority")
|
||||
.orderByDesc("gmt_modified")
|
||||
.orderByDesc("like_num")
|
||||
.orderByDesc("view_num");
|
||||
|
||||
IPage<Discussion> discussionList = discussionService.page(iPage, discussionQueryWrapper);
|
||||
|
||||
if (discussionList.getTotal() == 0) { // 未查询到一条数据
|
||||
return CommonResult.successResponse(discussionList, "暂无数据");
|
||||
} else {
|
||||
return CommonResult.successResponse(discussionList, "获取成功");
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/discussion")
|
||||
public CommonResult getDiscussion(@RequestParam(value = "did", required = true) Integer did,
|
||||
HttpServletRequest request) {
|
||||
|
||||
// 获取当前登录的用户
|
||||
HttpSession session = request.getSession();
|
||||
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||
|
||||
String uid = null;
|
||||
|
||||
if (userRolesVo != null) {
|
||||
uid = userRolesVo.getUid();
|
||||
}
|
||||
|
||||
DiscussionVo discussion = discussionService.getDiscussion(did, uid);
|
||||
|
||||
if (discussion.getStatus()) {
|
||||
return CommonResult.errorResponse("对不起,该讨论已被封禁!", CommonResult.STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
// 浏览量+1
|
||||
UpdateWrapper<Discussion> discussionUpdateWrapper = new UpdateWrapper<>();
|
||||
discussionUpdateWrapper.setSql("view_num=view_num+1").eq("id", discussion.getId());
|
||||
discussionService.update(discussionUpdateWrapper);
|
||||
discussion.setViewNum(discussion.getViewNum()+1);
|
||||
|
||||
return CommonResult.successResponse(discussion, "获取成功");
|
||||
}
|
||||
|
||||
@PostMapping("/discussion")
|
||||
@RequiresAuthentication
|
||||
public CommonResult addDiscussion(@RequestBody Discussion discussion, HttpServletRequest request) {
|
||||
// 获取当前登录的用户
|
||||
HttpSession session = request.getSession();
|
||||
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||
|
||||
discussion.setAuthor(userRolesVo.getUsername())
|
||||
.setAvatar(userRolesVo.getAvatar())
|
||||
.setUid(userRolesVo.getUid());
|
||||
|
||||
if (SecurityUtils.getSubject().hasRole("root")) {
|
||||
discussion.setRole("root");
|
||||
} else if (SecurityUtils.getSubject().hasRole("admin")) {
|
||||
discussion.setRole("admin");
|
||||
} else {
|
||||
// 如果不是管理员角色,一律重置为不置顶
|
||||
discussion.setTopPriority(false);
|
||||
}
|
||||
|
||||
boolean isOk = discussionService.saveOrUpdate(discussion);
|
||||
if (isOk) {
|
||||
return CommonResult.successResponse(null, "发布成功!");
|
||||
} else {
|
||||
return CommonResult.errorResponse("发布失败,请重新尝试!");
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/discussion")
|
||||
@RequiresAuthentication
|
||||
public CommonResult updateDiscussion() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@DeleteMapping("/discussion")
|
||||
@RequiresAuthentication
|
||||
public CommonResult removeDiscussion() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@GetMapping("/discussion-like")
|
||||
@Transactional
|
||||
@RequiresAuthentication
|
||||
public CommonResult addDiscussionLike(@RequestParam("did") Integer did,
|
||||
@RequestParam("toLike") Boolean toLike,
|
||||
HttpServletRequest request) {
|
||||
// 获取当前登录的用户
|
||||
HttpSession session = request.getSession();
|
||||
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||
|
||||
QueryWrapper<DiscussionLike> discussionLikeQueryWrapper = new QueryWrapper<>();
|
||||
discussionLikeQueryWrapper.eq("did", did).eq("uid", userRolesVo.getUid());
|
||||
|
||||
DiscussionLike discussionLike = discussionLikeService.getOne(discussionLikeQueryWrapper, false);
|
||||
|
||||
if (toLike) { // 添加点赞
|
||||
if (discussionLike == null) { // 如果不存在就添加
|
||||
boolean isSave = discussionLikeService.saveOrUpdate(new DiscussionLike().setUid(userRolesVo.getUid()).setDid(did));
|
||||
if (!isSave) {
|
||||
return CommonResult.errorResponse("点赞失败,请重试尝试!");
|
||||
}
|
||||
}
|
||||
// 点赞+1
|
||||
UpdateWrapper<Discussion> discussionUpdateWrapper = new UpdateWrapper<>();
|
||||
discussionUpdateWrapper.setSql("like_num=like_num+1").eq("id", did);
|
||||
discussionService.update(discussionUpdateWrapper);
|
||||
return CommonResult.successResponse(null, "点赞成功");
|
||||
} else { // 取消点赞
|
||||
if (discussionLike != null) { // 如果存在就删除
|
||||
boolean isDelete = discussionLikeService.removeById(discussionLike.getId());
|
||||
if (!isDelete) {
|
||||
return CommonResult.errorResponse("取消点赞失败,请重试尝试!");
|
||||
}
|
||||
}
|
||||
// 点赞-1
|
||||
UpdateWrapper<Discussion> discussionUpdateWrapper = new UpdateWrapper<>();
|
||||
discussionUpdateWrapper.setSql("like_num=like_num-1").eq("id", did);
|
||||
discussionService.update(discussionUpdateWrapper);
|
||||
return CommonResult.successResponse(null, "取消成功");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/discussion-category")
|
||||
public CommonResult getDiscussionCategory() {
|
||||
|
||||
List<Category> categoryList = categoryService.list();
|
||||
return CommonResult.successResponse(categoryList, "获取成功");
|
||||
}
|
||||
|
||||
}
|
|
@ -85,16 +85,6 @@ public class JudgeController {
|
|||
@Autowired
|
||||
private RemoteJudgeDispatcher remoteJudgeDispatcher;
|
||||
|
||||
// @Autowired
|
||||
// private RestTemplate restTemplate;
|
||||
|
||||
// @Value("${service-url.hoj-judge-server}") // restTemplate风格调用不使用
|
||||
// private String REST_URL_PREFIX;
|
||||
|
||||
// @GetMapping("/submit-problem-judge")
|
||||
// public String list(){
|
||||
// return restTemplate.getForObject(REST_URL_PREFIX+"/hoj-judge-server/submit-problem-judge", String.class);
|
||||
// }
|
||||
|
||||
/**
|
||||
* @MethodName submitProblemJudge
|
||||
|
|
|
@ -135,7 +135,6 @@ public class ProblemController {
|
|||
// 需要获取一下该token对应用户的数据
|
||||
HttpSession session = request.getSession();
|
||||
UserRolesVo userRolesVo = (UserRolesVo) session.getAttribute("userInfo");
|
||||
|
||||
HashMap<Long, Object> result = new HashMap<>();
|
||||
// 先查询判断该用户对于这些题是否已经通过,若已通过,则无论后续再提交结果如何,该题都标记为通过
|
||||
QueryWrapper<Judge> queryWrapper = new QueryWrapper<>();
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package top.hcode.hoj.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import top.hcode.hoj.pojo.entity.Category;
|
||||
|
||||
@Mapper
|
||||
@Repository
|
||||
public interface CategoryMapper extends BaseMapper<Category> {
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package top.hcode.hoj.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import top.hcode.hoj.pojo.entity.CommentLike;
|
||||
|
||||
|
||||
@Mapper
|
||||
@Repository
|
||||
public interface CommentLikeMapper extends BaseMapper<CommentLike> {
|
||||
}
|
|
@ -1,16 +1,26 @@
|
|||
package top.hcode.hoj.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import top.hcode.hoj.pojo.entity.Comment;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import top.hcode.hoj.pojo.vo.CommentsVo;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author Himit_ZH
|
||||
* @since 2020-10-23
|
||||
*/
|
||||
@Mapper
|
||||
@Repository
|
||||
public interface CommentMapper extends BaseMapper<Comment> {
|
||||
|
||||
IPage<CommentsVo> getCommentList(Page<CommentsVo> page, @Param("cid") Long cid, @Param("did") Integer did);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package top.hcode.hoj.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import top.hcode.hoj.pojo.entity.DiscussionLike;
|
||||
|
||||
|
||||
@Mapper
|
||||
@Repository
|
||||
public interface DiscussionLikeMapper extends BaseMapper<DiscussionLike> {
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package top.hcode.hoj.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import top.hcode.hoj.pojo.entity.Discussion;
|
||||
import top.hcode.hoj.pojo.vo.DiscussionVo;
|
||||
|
||||
|
||||
@Mapper
|
||||
@Repository
|
||||
public interface DiscussionMapper extends BaseMapper<Discussion> {
|
||||
DiscussionVo getDiscussion(@Param("did") Integer did, @Param("uid") String uid);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package top.hcode.hoj.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import top.hcode.hoj.pojo.entity.Reply;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/5 22:07
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
@Mapper
|
||||
@Repository
|
||||
public interface ReplyMapper extends BaseMapper<Reply> {
|
||||
}
|
|
@ -2,4 +2,37 @@
|
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="top.hcode.hoj.dao.CommentMapper">
|
||||
|
||||
<resultMap id="map_CommentList" type="top.hcode.hoj.pojo.vo.CommentsVo">
|
||||
<id column="id" property="id"></id>
|
||||
<result column="content" property="content"></result>
|
||||
<result column="from_uid" property="fromUid"></result>
|
||||
<result column="from_name" property="fromName"></result>
|
||||
<result column="from_avatar" property="fromAvatar"></result>
|
||||
<result column="from_role" property="fromRole"></result>
|
||||
<result column="like_num" property="likeNum"></result>
|
||||
<result column="total_reply_num" property="totalReplyNum"></result>
|
||||
<result column="gmt_create" property="gmtCreate"></result>
|
||||
<collection property="replyList" ofType="top.hcode.hoj.pojo.entity.Reply" select="getCommentListReply" column="id">
|
||||
</collection>
|
||||
</resultMap>
|
||||
|
||||
|
||||
<!-- 主查询 -->
|
||||
<select id="getCommentList" resultMap="map_CommentList" resultType="list">
|
||||
SELECT c.*,(SELECT COUNT(1) FROM reply WHERE comment_id=c.id) as total_reply_num FROM comment c
|
||||
<where>
|
||||
<if test="cid!=null">
|
||||
c.cid=#{cid}
|
||||
</if>
|
||||
<if test="did!=null">
|
||||
AND c.did=#{did}
|
||||
</if>
|
||||
</where>
|
||||
order by c.like_num desc,c.gmt_create desc
|
||||
</select>
|
||||
|
||||
<!-- 子查询 -->
|
||||
<select id="getCommentListReply" resultType="top.hcode.hoj.pojo.entity.Reply">
|
||||
select r.* from reply r where r.comment_id=#{id} order by r.gmt_create desc LIMIT 3
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="top.hcode.hoj.dao.DiscussionMapper">
|
||||
|
||||
<resultMap id="map_DiscussionVo" type="top.hcode.hoj.pojo.vo.DiscussionVo">
|
||||
<id column="id" property="id"></id>
|
||||
<result column="content" property="content"></result>
|
||||
<result column="description" property="description"></result>
|
||||
<result column="title" property="title"></result>
|
||||
<result column="category_id" property="categoryId"></result>
|
||||
<result column="category_name" property="categoryName"></result>
|
||||
<result column="pid" property="pid"></result>
|
||||
<result column="uid" property="uid"></result>
|
||||
<result column="author" property="author"></result>
|
||||
<result column="avatar" property="avatar"></result>
|
||||
<result column="role" property="role"></result>
|
||||
<result column="view_num" property="viewNum"></result>
|
||||
<result column="like_num" property="likeNum"></result>
|
||||
<result column="has_like" property="hasLike"></result>
|
||||
<result column="top_priority" property="topPriority"></result>
|
||||
<result column="status" property="status"></result>
|
||||
<result column="gmt_modified" property="gmtModified"></result>
|
||||
<result column="gmt_create" property="gmtCreate"></result>
|
||||
</resultMap>
|
||||
|
||||
|
||||
<!-- 主查询 -->
|
||||
<select id="getDiscussion" resultMap="map_DiscussionVo" resultType="top.hcode.hoj.pojo.vo.DiscussionVo">
|
||||
SELECT d.*,c.id as category_id,c.name as category_name,
|
||||
(SELECT 1 FROM discussion_like dl WHERE dl.uid = #{uid} AND dl.did = #{did} LIMIT 1) as has_like
|
||||
FROM discussion d,category c
|
||||
<where>
|
||||
c.id = d.category_id
|
||||
<if test="did!=null">
|
||||
and d.id=#{did}
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -88,4 +88,4 @@
|
|||
select r.* from role r,user_role ur where ur.uid=#{uuid} and ur.role_id = r.id
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
</mapper>
|
|
@ -0,0 +1,54 @@
|
|||
package top.hcode.hoj.pojo.vo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import top.hcode.hoj.pojo.entity.Reply;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/5 22:30
|
||||
* @Description:
|
||||
*/
|
||||
@ApiModel(value = "评论数据列表VO", description = "")
|
||||
@Data
|
||||
public class CommentsVo {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty(value = "评论id")
|
||||
private Integer id;
|
||||
|
||||
@ApiModelProperty(value = "评论内容")
|
||||
private String content;
|
||||
|
||||
@ApiModelProperty(value = "评论者id")
|
||||
private String fromUid;
|
||||
|
||||
@ApiModelProperty(value = "评论者用户名")
|
||||
private String fromName;
|
||||
|
||||
@ApiModelProperty(value = "评论组头像地址")
|
||||
private String fromAvatar;
|
||||
|
||||
@ApiModelProperty(value = "评论者角色")
|
||||
private String fromRole;
|
||||
|
||||
@ApiModelProperty(value = "点赞数量")
|
||||
private Integer likeNum;
|
||||
|
||||
@ApiModelProperty(value = "该评论的总回复数")
|
||||
private Integer totalReplyNum;
|
||||
|
||||
private Date gmtCreate;
|
||||
|
||||
@ApiModelProperty(value = "该评论回复列表")
|
||||
private List<Reply> replyList;
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package top.hcode.hoj.pojo.vo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/7 10:06
|
||||
* @Description:
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "讨论数据VO", description = "")
|
||||
public class DiscussionVo {
|
||||
|
||||
private Integer id;
|
||||
|
||||
@ApiModelProperty(value = "分类id")
|
||||
private Integer categoryId;
|
||||
|
||||
@ApiModelProperty(value = "分类名字")
|
||||
private String categoryName;
|
||||
|
||||
@ApiModelProperty(value = "讨论标题")
|
||||
private String title;
|
||||
|
||||
@ApiModelProperty(value = "讨论简介")
|
||||
private String description;
|
||||
|
||||
@ApiModelProperty(value = "讨论内容")
|
||||
private String content;
|
||||
|
||||
@ApiModelProperty(value = "题目关联 默认为null则不关联题目")
|
||||
private String pid;
|
||||
|
||||
@ApiModelProperty(value = "发表者id")
|
||||
private String uid;
|
||||
|
||||
@ApiModelProperty(value = "发表者用户名")
|
||||
private String author;
|
||||
|
||||
@ApiModelProperty(value = "发表者头像地址")
|
||||
private String avatar;
|
||||
|
||||
@ApiModelProperty(value = "发表者角色")
|
||||
private String role;
|
||||
|
||||
@ApiModelProperty(value = "浏览数量")
|
||||
private Integer viewNum;
|
||||
|
||||
@ApiModelProperty(value = "点赞数量")
|
||||
private Integer likeNum;
|
||||
|
||||
@ApiModelProperty(value = "如果有登录的话,是否点赞了")
|
||||
private Boolean hasLike;
|
||||
|
||||
@ApiModelProperty(value = "优先级,是否置顶")
|
||||
private Boolean topPriority;
|
||||
|
||||
@ApiModelProperty(value = "是否封禁")
|
||||
private Boolean status;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date gmtCreate;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private Date gmtModified;
|
||||
}
|
|
@ -18,6 +18,8 @@ import java.util.List;
|
|||
@Data
|
||||
public class UserRolesVo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 10000L;
|
||||
|
||||
@ApiModelProperty(value = "用户id")
|
||||
private String uid;
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package top.hcode.hoj.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import top.hcode.hoj.pojo.entity.Category;
|
||||
|
||||
|
||||
public interface CategoryService extends IService<Category> {
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package top.hcode.hoj.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import top.hcode.hoj.pojo.entity.CommentLike;
|
||||
|
||||
|
||||
public interface CommentLikeService extends IService<CommentLike> {
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
package top.hcode.hoj.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import top.hcode.hoj.pojo.entity.Comment;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import top.hcode.hoj.pojo.vo.CommentsVo;
|
||||
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
|
@ -12,5 +15,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
|||
* @since 2020-10-23
|
||||
*/
|
||||
public interface CommentService extends IService<Comment> {
|
||||
|
||||
IPage<CommentsVo> getCommentList(int limit, int currentPage, Long cid,Integer did);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package top.hcode.hoj.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import top.hcode.hoj.pojo.entity.DiscussionLike;
|
||||
|
||||
public interface DiscussionLikeService extends IService<DiscussionLike> {
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package top.hcode.hoj.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import top.hcode.hoj.pojo.entity.Discussion;
|
||||
import top.hcode.hoj.pojo.vo.DiscussionVo;
|
||||
|
||||
public interface DiscussionService extends IService<Discussion> {
|
||||
DiscussionVo getDiscussion(Integer did,String uid);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package top.hcode.hoj.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import top.hcode.hoj.pojo.entity.Reply;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/5 22:08
|
||||
* @Description:
|
||||
*/
|
||||
public interface ReplyService extends IService<Reply> {
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package top.hcode.hoj.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import top.hcode.hoj.pojo.entity.UserRole;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package top.hcode.hoj.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.hcode.hoj.dao.CategoryMapper;
|
||||
import top.hcode.hoj.pojo.entity.Category;
|
||||
import top.hcode.hoj.service.CategoryService;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/4 22:30
|
||||
* @Description:
|
||||
*/
|
||||
@Service
|
||||
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package top.hcode.hoj.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.hcode.hoj.dao.CommentLikeMapper;
|
||||
import top.hcode.hoj.pojo.entity.CommentLike;
|
||||
import top.hcode.hoj.service.CommentLikeService;
|
||||
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/4 22:31
|
||||
* @Description:
|
||||
*/
|
||||
@Service
|
||||
public class CommentLikeServiceImpl extends ServiceImpl<CommentLikeMapper, CommentLike> implements CommentLikeService {
|
||||
}
|
|
@ -1,14 +1,18 @@
|
|||
package top.hcode.hoj.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import top.hcode.hoj.pojo.entity.Comment;
|
||||
import top.hcode.hoj.dao.CommentMapper;
|
||||
import top.hcode.hoj.pojo.vo.CommentsVo;
|
||||
import top.hcode.hoj.service.CommentService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务实现类
|
||||
* 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author Himit_ZH
|
||||
|
@ -17,4 +21,13 @@ import org.springframework.stereotype.Service;
|
|||
@Service
|
||||
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService {
|
||||
|
||||
@Autowired
|
||||
private CommentMapper commentMapper;
|
||||
|
||||
@Override
|
||||
public IPage<CommentsVo> getCommentList(int limit, int currentPage, Long cid, Integer did) {
|
||||
//新建分页
|
||||
Page<CommentsVo> page = new Page<>(currentPage, limit);
|
||||
return commentMapper.getCommentList(page, cid, did);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package top.hcode.hoj.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.hcode.hoj.dao.DiscussionLikeMapper;
|
||||
import top.hcode.hoj.pojo.entity.DiscussionLike;
|
||||
import top.hcode.hoj.service.DiscussionLikeService;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/4 22:31
|
||||
* @Description:
|
||||
*/
|
||||
@Service
|
||||
public class DiscussionLikeServiceImpl extends ServiceImpl<DiscussionLikeMapper, DiscussionLike> implements DiscussionLikeService {
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package top.hcode.hoj.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.hcode.hoj.dao.DiscussionMapper;
|
||||
import top.hcode.hoj.pojo.entity.Discussion;
|
||||
import top.hcode.hoj.pojo.vo.DiscussionVo;
|
||||
import top.hcode.hoj.service.DiscussionService;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/4 22:31
|
||||
* @Description:
|
||||
*/
|
||||
@Service
|
||||
public class DiscussionServiceImpl extends ServiceImpl<DiscussionMapper, Discussion> implements DiscussionService {
|
||||
|
||||
@Autowired
|
||||
private DiscussionMapper discussionMapper;
|
||||
|
||||
@Override
|
||||
public DiscussionVo getDiscussion(Integer did, String uid) {
|
||||
return discussionMapper.getDiscussion(did, uid);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package top.hcode.hoj.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.hcode.hoj.dao.ReplyMapper;
|
||||
import top.hcode.hoj.pojo.entity.Reply;
|
||||
import top.hcode.hoj.service.ReplyService;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/5 22:09
|
||||
* @Description:
|
||||
*/
|
||||
@Service
|
||||
public class ReplyServiceImpl extends ServiceImpl<ReplyMapper, Reply> implements ReplyService {
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
hoj-backstage:
|
||||
port: 6688 # 本服务器启动的端口号
|
||||
nacos-url: 172.18.0.3:8848 # nacos的地址
|
||||
nacos-url: 127.0.0.1:8848 # nacos的地址
|
||||
|
||||
server:
|
||||
port: ${hoj-backstage.port}
|
||||
|
|
|
@ -28,16 +28,10 @@ import org.springframework.web.client.RestTemplate;
|
|||
import top.hcode.hoj.common.result.CommonResult;
|
||||
import top.hcode.hoj.dao.*;
|
||||
import top.hcode.hoj.pojo.entity.*;
|
||||
import top.hcode.hoj.pojo.vo.AnnouncementVo;
|
||||
import top.hcode.hoj.pojo.vo.ContestVo;
|
||||
import top.hcode.hoj.pojo.vo.RoleAuthsVo;
|
||||
import top.hcode.hoj.pojo.vo.UserRolesVo;
|
||||
import top.hcode.hoj.pojo.vo.*;
|
||||
import top.hcode.hoj.service.UserInfoService;
|
||||
import top.hcode.hoj.service.UserRoleService;
|
||||
import top.hcode.hoj.service.impl.AnnouncementServiceImpl;
|
||||
import top.hcode.hoj.service.impl.LanguageServiceImpl;
|
||||
import top.hcode.hoj.service.impl.UserInfoServiceImpl;
|
||||
import top.hcode.hoj.service.impl.UserRoleServiceImpl;
|
||||
import top.hcode.hoj.service.impl.*;
|
||||
import top.hcode.hoj.utils.Constants;
|
||||
import top.hcode.hoj.utils.IpUtils;
|
||||
import top.hcode.hoj.utils.JsoupUtils;
|
||||
|
@ -79,37 +73,13 @@ public class DataBackupApplicationTests {
|
|||
@Autowired
|
||||
private AnnouncementServiceImpl announcementService;
|
||||
|
||||
@Autowired
|
||||
private DiscussionServiceImpl discussionService;
|
||||
|
||||
@Test
|
||||
public void Test1() {
|
||||
// UserRolesVo roles = userRoleMapper.getUserRoles("c5ddbe4b38d641bea7d87ae0e102260d",null);
|
||||
// System.out.println(roles);
|
||||
// IPage<UserRolesVo> admin = userRoleService.getUserList(10, 1, "admin");
|
||||
// System.out.println(admin.getPages());
|
||||
// System.out.println(admin.getSize());
|
||||
// System.out.println(admin.getRecords());
|
||||
// List<String> list = new LinkedList<>();
|
||||
// list.add("1");
|
||||
// list.add("2");
|
||||
// boolean b = userInfoService.removeByIds(list);
|
||||
// System.out.println(b);
|
||||
// UserInfo userInfo = new UserInfo().
|
||||
// setUsername("111")
|
||||
// .setPassword("1111")
|
||||
// .setEmail("11111");
|
||||
// boolean b = userInfoService.saveOrUpdate(userInfo);
|
||||
// if (b){
|
||||
// System.out.println(userInfo);
|
||||
// }
|
||||
|
||||
// List<AnnouncementVo> contestAnnouncement = announcementService.getContestAnnouncement(1L);
|
||||
// System.out.println(contestAnnouncement.size());
|
||||
String test = "{1}....{2}";
|
||||
String command = "/usr/bin/java -cp {1} -XX:MaxRAM={2}k -Djava.security.manager -Dfile.encoding=UTF-8 -Djava.security.policy==/etc/java_policy -Djava.awt.headless=true Main";
|
||||
String exePath = "/judge/run/32/1.exe";
|
||||
String exeDir = "/judge/run/32";
|
||||
int maxMemory = 1024 * 10;
|
||||
List<String> commandList = Arrays.asList(MessageFormat.format(command, exePath, exeDir, (maxMemory / 1024)).split(" "));
|
||||
System.out.println(commandList);
|
||||
DiscussionVo discussion = discussionService.getDiscussion(5, "c5ddbe4b38d641bea7d87ae0e102260d");
|
||||
System.out.println(discussion);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package top.hcode.hoj.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/4 22:09
|
||||
* @Description:
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@ApiModel(value="Category对象", description="")
|
||||
public class Category {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "分类名字")
|
||||
private String name;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date gmtCreate;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private Date gmtModified;
|
||||
}
|
|
@ -30,24 +30,31 @@ public class Comment implements Serializable {
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Integer id;
|
||||
|
||||
private String uid;
|
||||
@ApiModelProperty(value = "NULL表示无引用比赛")
|
||||
private Long cid;
|
||||
|
||||
@ApiModelProperty(value = "讨论标题")
|
||||
private String title;
|
||||
@ApiModelProperty(value = "NULL表示无引用讨论")
|
||||
private Integer did;
|
||||
|
||||
@ApiModelProperty(value = "讨论详情")
|
||||
@ApiModelProperty(value = "评论内容")
|
||||
private String content;
|
||||
|
||||
@ApiModelProperty(value = "标签id,0表示无")
|
||||
private Long tid;
|
||||
@ApiModelProperty(value = "评论者id")
|
||||
private String fromUid;
|
||||
|
||||
@ApiModelProperty(value = "0表示无引用题目")
|
||||
private Long pid;
|
||||
@ApiModelProperty(value = "评论者用户名")
|
||||
private String fromName;
|
||||
|
||||
@ApiModelProperty(value = "0表示无引用比赛")
|
||||
private Long cid;
|
||||
@ApiModelProperty(value = "评论组头像地址")
|
||||
private String fromAvatar;
|
||||
|
||||
@ApiModelProperty(value = "评论者角色")
|
||||
private String fromRole;
|
||||
|
||||
@ApiModelProperty(value = "点赞数量")
|
||||
private Integer likeNum;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date gmtCreate;
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package top.hcode.hoj.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/5 11:36
|
||||
* @Description:
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@ApiModel(value="CommentLike对象", description="")
|
||||
public class CommentLike {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "评论id")
|
||||
private Integer cid;
|
||||
|
||||
@ApiModelProperty(value = "用户id")
|
||||
private String uid;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date gmtCreate;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private Date gmtModified;
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package top.hcode.hoj.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/4 22:11
|
||||
* @Description:
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@ApiModel(value="Discussion对象", description="")
|
||||
public class Discussion {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
@ApiModelProperty(value = "分类id")
|
||||
private Integer categoryId;
|
||||
|
||||
@ApiModelProperty(value = "讨论标题")
|
||||
private String title;
|
||||
|
||||
@ApiModelProperty(value = "讨论简介")
|
||||
private String description;
|
||||
|
||||
@ApiModelProperty(value = "讨论内容")
|
||||
private String content;
|
||||
|
||||
@ApiModelProperty(value = "题目关联 默认为null则不关联题目")
|
||||
private String pid;
|
||||
|
||||
@ApiModelProperty(value = "发表者id")
|
||||
private String uid;
|
||||
|
||||
@ApiModelProperty(value = "发表者用户名")
|
||||
private String author;
|
||||
|
||||
@ApiModelProperty(value = "发表者头像地址")
|
||||
private String avatar;
|
||||
|
||||
@ApiModelProperty(value = "发表者角色")
|
||||
private String role;
|
||||
|
||||
@ApiModelProperty(value = "浏览数量")
|
||||
private Integer viewNum;
|
||||
|
||||
@ApiModelProperty(value = "点赞数量")
|
||||
private String likeNum;
|
||||
|
||||
@ApiModelProperty(value = "优先级,是否置顶")
|
||||
private Boolean topPriority;
|
||||
|
||||
@ApiModelProperty(value = "是否封禁")
|
||||
private Boolean status;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date gmtCreate;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private Date gmtModified;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package top.hcode.hoj.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/5 11:36
|
||||
* @Description:
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@ApiModel(value="DiscussionLike对象", description="")
|
||||
public class DiscussionLike {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "讨论id")
|
||||
private Integer did;
|
||||
|
||||
@ApiModelProperty(value = "用户id")
|
||||
private String uid;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date gmtCreate;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private Date gmtModified;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package top.hcode.hoj.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @Author: Himit_ZH
|
||||
* @Date: 2021/5/5 19:03
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@ApiModel(value="Reply对象", description="")
|
||||
public class Reply {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
@ApiModelProperty(value = "评论id")
|
||||
private Integer commentId;
|
||||
|
||||
@ApiModelProperty(value = "回复评论者id")
|
||||
private String fromUid;
|
||||
|
||||
@ApiModelProperty(value = "回复评论者用户名")
|
||||
private String fromName;
|
||||
|
||||
@ApiModelProperty(value = "回复评论组头像地址")
|
||||
private String fromAvatar;
|
||||
|
||||
@ApiModelProperty(value = "回复评论者角色")
|
||||
private String fromRole;
|
||||
|
||||
@ApiModelProperty(value = "被回复的用户id")
|
||||
private String toUid;
|
||||
|
||||
@ApiModelProperty(value = "被回复的用户名")
|
||||
private String toName;
|
||||
|
||||
@ApiModelProperty(value = "被回复的用户头像地址")
|
||||
private String toAvatar;
|
||||
|
||||
@ApiModelProperty(value = "回复的内容")
|
||||
private String content;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date gmtCreate;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private Date gmtModified;
|
||||
}
|
|
@ -140,7 +140,7 @@ export default {
|
|||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background-color: #eee !important;
|
||||
background-color: #eff3f5 !important;
|
||||
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
|
||||
Microsoft YaHei, Arial, sans-serif !important;
|
||||
-webkit-font-smoothing: antialiased !important;
|
||||
|
|
|
@ -11,14 +11,6 @@ import utils from '@/common/utils'
|
|||
// NProgress.configure({ ease: 'ease', speed: 1000,showSpinner: false})
|
||||
Vue.prototype.$http = axios
|
||||
|
||||
// 环境的切换
|
||||
if (process.env.NODE_ENV == 'development') {
|
||||
axios.defaults.baseURL = "http://localhost:6688";
|
||||
} else if (process.env.NODE_ENV == 'debug') {
|
||||
axios.defaults.baseURL = "http://localhost:6688";
|
||||
} else if (process.env.NODE_ENV == 'production') {
|
||||
axios.defaults.baseURL = '你的域名或者ip:端口号';
|
||||
}
|
||||
|
||||
// 请求超时时间
|
||||
axios.defaults.timeout = 40000;
|
||||
|
@ -439,7 +431,85 @@ const ojApi = {
|
|||
return ajax("/api/change-userInfo",'post',{
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 讨论页相关请求
|
||||
getCategoryList(){
|
||||
return ajax("/api/discussion-category",'get')
|
||||
},
|
||||
|
||||
getDiscussionList(limit,searchParams){
|
||||
let params = {
|
||||
limit
|
||||
}
|
||||
Object.keys(searchParams).forEach((element) => {
|
||||
if (searchParams[element]!==''&&searchParams[element]!==null&&searchParams[element]!==undefined) {
|
||||
params[element] = searchParams[element]
|
||||
}
|
||||
})
|
||||
return ajax("/api/discussions",'get',{
|
||||
params
|
||||
})
|
||||
},
|
||||
|
||||
getDiscussion(did){
|
||||
return ajax("/api/discussion",'get',{
|
||||
params:{
|
||||
did
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
addDiscussion(data){
|
||||
return ajax("/api/discussion",'post',{
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
toLikeDiscussion(did,toLike){
|
||||
return ajax("/api/discussion-like",'get',{
|
||||
params:{
|
||||
did,
|
||||
toLike
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getCommentList(params){
|
||||
return ajax("/api/comments",'get',{
|
||||
params
|
||||
})
|
||||
},
|
||||
|
||||
addComment(data){
|
||||
return ajax("/api/comment",'post',{
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
toLikeComment(cid,toLike){
|
||||
return ajax("/api/comment-like",'get',{
|
||||
params:{
|
||||
cid,
|
||||
toLike
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
addReply(data){
|
||||
return ajax("/api/reply",'post',{
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
getAllReply(commentId){
|
||||
return ajax("/api/reply",'get',{
|
||||
params:{
|
||||
commentId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 处理admin后台管理的请求
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
import $ from 'jquery'
|
||||
import myMessage from '@/common/message';
|
||||
export const addCodeBtn = _ => {
|
||||
//markdown代码存放在pre code 标签对中
|
||||
$('pre code').each(function () {
|
||||
let lines = $(this).text().split('\n').length - 1
|
||||
//添加有序列表
|
||||
let $numbering = $('<ol/>').addClass('pre-numbering')
|
||||
//添加复制按钮,此处使用的是element-ui icon 图标
|
||||
let $copy = $('<i title="copy"> COPY</i>').addClass('el-icon-document-copy code-copy')
|
||||
$(this)
|
||||
.parent()
|
||||
.append($numbering)
|
||||
.append($copy)
|
||||
for (let i = 0; i <= lines; i++) {
|
||||
$numbering.append($('<li/>'))
|
||||
}
|
||||
})
|
||||
//监听复制按钮点击事件
|
||||
$('pre i.code-copy').click(e => {
|
||||
let text = $(e.target).siblings('code').text()
|
||||
let element = $('<textarea>' + text + '</textarea>')
|
||||
$('body').append(element)
|
||||
element[0].select()
|
||||
document.execCommand('Copy')
|
||||
element.remove()
|
||||
//这里是自定义的消息通知组件
|
||||
myMessage.success('success')
|
||||
})
|
||||
}
|
||||
|
|
@ -4,13 +4,14 @@
|
|||
ref="md"
|
||||
@imgAdd="$imgAdd"
|
||||
@imgDel="$imgDel"
|
||||
:ishljs="true"
|
||||
v-model="currentValue"
|
||||
codeStyle="arduino-light"
|
||||
></mavon-editor>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import myMessage from '@/common/message';
|
||||
import { addCodeBtn } from '@/common/codeblock';
|
||||
export default {
|
||||
name: 'Editor',
|
||||
props: {
|
||||
|
@ -32,7 +33,7 @@ export default {
|
|||
formdata.append('image', $file);
|
||||
//将下面上传接口替换为你自己的服务器接口
|
||||
this.$http({
|
||||
url: '/file/upload-md-img',
|
||||
url: '/api/file/upload-md-img',
|
||||
method: 'post',
|
||||
data: formdata,
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
|
@ -44,7 +45,7 @@ export default {
|
|||
$imgDel(pos) {
|
||||
// 删除文件
|
||||
this.$http({
|
||||
url: '/file/delete-md-img',
|
||||
url: '/api/file/delete-md-img',
|
||||
method: 'get',
|
||||
params: {
|
||||
filePath: this.img_file[pos[0]],
|
||||
|
@ -61,8 +62,131 @@ export default {
|
|||
currentValue(newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
this.$emit('update:value', newVal);
|
||||
this.$nextTick((_) => {
|
||||
addCodeBtn();
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.markdown-body pre {
|
||||
display: block;
|
||||
border-radius: 3px !important;
|
||||
border: 1px solid #c3ccd0;
|
||||
padding: 0 16px 0 55px !important;
|
||||
position: relative !important;
|
||||
overflow-y: hidden !important;
|
||||
font-size: 1rem !important;
|
||||
background: #fff !important;
|
||||
white-space: pre !important;
|
||||
}
|
||||
.markdown-body pre code {
|
||||
line-height: 26px !important;
|
||||
}
|
||||
.markdown-body pre ol.pre-numbering {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
line-height: 26px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
counter-reset: sectioncounter;
|
||||
background: #f1f1f1;
|
||||
color: #777;
|
||||
font-size: 12px;
|
||||
}
|
||||
.markdown-body pre ol.pre-numbering li {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
.markdown-body pre ol.pre-numbering li:before {
|
||||
content: counter(sectioncounter) '';
|
||||
counter-increment: sectioncounter;
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
.markdown-body pre i.code-copy {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background-color: #2196f3;
|
||||
display: none;
|
||||
padding: 7px;
|
||||
margin: 5px 5px 0 0;
|
||||
font-size: 11px;
|
||||
border-radius: inherit;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.markdown-body pre:hover i.code-copy {
|
||||
display: block;
|
||||
}
|
||||
.markdown-body pre i.code-copy:hover i.code-copy {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.markdown-body blockquote {
|
||||
color: #666;
|
||||
border-left: 4px solid #8bc34a;
|
||||
padding: 10px;
|
||||
margin-left: 0;
|
||||
font-size: 14px;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
.markdown-body h1,
|
||||
.markdown-body h2,
|
||||
.markdown-body h3,
|
||||
.markdown-body h4,
|
||||
.markdown-body h5,
|
||||
.markdown-body h6 {
|
||||
position: relative;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 16px;
|
||||
font-weight: bold;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.markdown-body h1 {
|
||||
padding-bottom: 0.3em;
|
||||
font-size: 2.25em;
|
||||
line-height: 1.2;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.markdown-body h2 {
|
||||
font-size: 1.45em;
|
||||
line-height: 1.425;
|
||||
border-bottom: 1px solid #eee;
|
||||
background: #cce5ff;
|
||||
padding: 8px 10px;
|
||||
color: #545857;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.markdown-body h3 {
|
||||
font-size: 1.3em;
|
||||
line-height: 1.43;
|
||||
}
|
||||
.markdown-body h3:before {
|
||||
content: '';
|
||||
border-left: 4px solid #03a9f4;
|
||||
padding-left: 6px;
|
||||
}
|
||||
.markdown-body h4 {
|
||||
font-size: 1.12em;
|
||||
}
|
||||
.markdown-body h4:before {
|
||||
content: '';
|
||||
border-left: 4px solid #bbb;
|
||||
padding-left: 6px;
|
||||
}
|
||||
.markdown-body img {
|
||||
border: 0;
|
||||
background: #ffffff;
|
||||
padding: 15px;
|
||||
margin: 5px 0;
|
||||
box-shadow: inset 0 0 12px rgb(219 219 219);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,979 @@
|
|||
<!--评论模块-->
|
||||
<template>
|
||||
<div>
|
||||
<div class="container">
|
||||
<div class="own-input">
|
||||
<el-input
|
||||
v-model="ownInputComment"
|
||||
type="textarea"
|
||||
:rows="8"
|
||||
id="own-textarea"
|
||||
placeholder="快来写下你的评论吧~😘"
|
||||
>
|
||||
</el-input>
|
||||
<div class="input-bottom">
|
||||
<span title="emoji表情">
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
width="300"
|
||||
trigger="click"
|
||||
v-model="facelistVisiable"
|
||||
>
|
||||
<div class="emotionList">
|
||||
<a
|
||||
href="javascript:void(0);"
|
||||
@click="getEmo(index, true)"
|
||||
v-for="(item, index) in faceList"
|
||||
:key="index"
|
||||
class="emotionItem"
|
||||
>{{ item }}</a
|
||||
>
|
||||
</div>
|
||||
<i class="fa fa-smile-o emotionSelect" slot="reference"></i>
|
||||
</el-popover>
|
||||
</span>
|
||||
<span
|
||||
class="markdown-key"
|
||||
title="行内代码"
|
||||
@click="addContentTips(0, false)"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
class="css-1rhb60f-Svg ea8ky5j0"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5.5 5h1.866a.5.5 0 01.486.617l-.96 4a.5.5 0 01-.486.383H5.5a.5.5 0 01-.5-.5v-4a.5.5 0 01.5-.5zm8 0h1.866a.5.5 0 01.486.617l-.96 4a.5.5 0 01-.486.383H13.5a.5.5 0 01-.5-.5v-4a.5.5 0 01.5-.5z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="markdown-key"
|
||||
title="代码块"
|
||||
@click="addContentTips(1, false)"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
class="css-1rhb60f-Svg ea8ky5j0"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M3.64 10.98c.147 0 .312-.022.495-.066.183-.044.348-.12.495-.23.147-.11.271-.269.374-.474.103-.205.154-.462.154-.77V5.084c0-.499.077-.928.231-1.287.154-.36.363-.66.627-.902s.565-.422.902-.539c.337-.117.69-.176 1.056-.176H9.58v1.848H8.37c-.425 0-.693.14-.803.418-.11.279-.165.58-.165.902v4.136c0 .425-.08.788-.242 1.09-.161.3-.352.545-.572.736-.22.19-.455.337-.704.44-.25.103-.462.161-.638.176v.044c.176.015.389.062.638.143.25.08.484.216.704.407.22.19.41.447.572.77.161.323.242.74.242 1.254v4.004c0 .323.055.623.165.902.11.279.378.418.803.418h1.21v1.848H7.974c-.367 0-.719-.059-1.056-.176a2.573 2.573 0 01-.902-.539 2.586 2.586 0 01-.627-.902c-.154-.36-.231-.788-.231-1.287V14.61c0-.352-.051-.638-.154-.858a1.494 1.494 0 00-.374-.517 1.18 1.18 0 00-.495-.253 2.143 2.143 0 00-.495-.066V10.98zm16.72 2.04c-.161 0-.33.022-.506.066a1.184 1.184 0 00-.484.253 1.494 1.494 0 00-.374.517c-.103.22-.154.506-.154.858v4.202c0 .499-.077.928-.231 1.287-.154.36-.363.66-.627.902a2.573 2.573 0 01-.902.54c-.337.116-.69.175-1.056.175H14.42v-1.848h1.21c.425 0 .693-.14.803-.418.11-.279.165-.58.165-.902v-4.004c0-.513.08-.931.242-1.254.161-.323.352-.58.572-.77.22-.19.455-.326.704-.407.25-.08.462-.128.638-.143v-.044a2.225 2.225 0 01-.638-.176 2.567 2.567 0 01-.704-.44c-.22-.19-.41-.436-.572-.737-.161-.3-.242-.664-.242-1.089V5.452c0-.323-.055-.623-.165-.902-.11-.279-.378-.418-.803-.418h-1.21V2.284h1.606c.367 0 .719.059 1.056.176.337.117.638.297.902.539.264.242.473.543.627.902.154.36.231.788.231 1.287v4.356c0 .308.051.565.154.77.103.205.227.363.374.473.147.11.308.187.484.231.176.044.345.066.506.066v1.936z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="markdown-key"
|
||||
title="链接"
|
||||
@click="addContentTips(2, false)"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
class="css-1rhb60f-Svg ea8ky5j0"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M13 7a1 1 0 011-1h2a6 6 0 010 12h-2a1 1 0 110-2h2a4 4 0 000-8h-2a1 1 0 01-1-1zm-3 10a1 1 0 01-1 1H8A6 6 0 018 6h1a1 1 0 010 2H8a4 4 0 100 8h1a1 1 0 011 1zm-1-6h6a1 1 0 110 2H9a1 1 0 110-2z"
|
||||
clip-rule="evenodd"
|
||||
></path></svg
|
||||
></span>
|
||||
<span
|
||||
class="markdown-key"
|
||||
title="无序列表"
|
||||
@click="addContentTips(3, false)"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
class="css-1rhb60f-Svg ea8ky5j0"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M8 7a1 1 0 110-2h13a1 1 0 110 2H8zm0 6a1 1 0 110-2h13a1 1 0 110 2H8zm0 6a1 1 0 110-2h13a1 1 0 110 2H8zM2.952 6.85a1.201 1.201 0 111.698-1.7 1.201 1.201 0 01-1.698 1.7zm0 6a1.201 1.201 0 111.698-1.7 1.201 1.201 0 01-1.698 1.7zm0 6a1.201 1.201 0 111.698-1.7 1.201 1.201 0 01-1.698 1.7z"
|
||||
clip-rule="evenodd"
|
||||
></path></svg
|
||||
></span>
|
||||
<span
|
||||
class="markdown-key"
|
||||
title="有序列表"
|
||||
@click="addContentTips(4, false)"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
class="css-1rhb60f-Svg ea8ky5j0"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M7 6a1 1 0 011-1h13a1 1 0 110 2H8a1 1 0 01-1-1zm1 13a1 1 0 110-2h13a1 1 0 010 2H8zm0-6a1 1 0 110-2h13a1 1 0 010 2H8zM2.756 4.98l.236-1.18h2.22l-.92 4.6h-1.38l.684-3.42h-.84zm2.823 8.3l-.235 1.192H1.756l.174-.87.073-.117 1.911-1.498c.219-.178.368-.324.445-.434a.49.49 0 00.099-.287.208.208 0 00-.083-.18c-.067-.052-.176-.082-.335-.082a.897.897 0 00-.398.094 1.02 1.02 0 00-.342.272l-.12.144-.968-.702.118-.162c.193-.264.456-.473.786-.625.327-.15.684-.225 1.068-.225.318 0 .602.053.85.16.255.11.456.267.6.47.146.206.22.44.22.698 0 .298-.083.58-.247.839-.158.25-.424.518-.797.807l-.653.506h1.422zm-3.063 3.7l.245-1.18h3.345l-.166.867-.06.11-.93.869a1.205 1.205 0 01.737 1.144c-.001.327-.098.622-.29.881a1.856 1.856 0 01-.774.593c-.322.139-.685.208-1.087.208-.328 0-.636-.045-.924-.135a2.06 2.06 0 01-.743-.4l-.132-.114.688-1.05.173.147c.12.103.267.185.442.245.177.06.364.091.562.091.251 0 .435-.044.554-.124a.31.31 0 00.146-.282c0-.086-.025-.135-.08-.171-.076-.05-.213-.079-.41-.079h-.687l.173-.88.06-.108.674-.632H2.516z"
|
||||
clip-rule="evenodd"
|
||||
></path></svg
|
||||
></span>
|
||||
<span class="own-btn-comment">
|
||||
<el-button class="btn" type="primary" round @click="commitComment"
|
||||
><i class="el-icon-edit"> 提交评论</i></el-button
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="comment-total">
|
||||
<div class="text">
|
||||
<span>全部评论</span><span class="number">{{ totalComment }}</span>
|
||||
</div>
|
||||
</h3>
|
||||
<div
|
||||
class="comment"
|
||||
id="commentbox"
|
||||
v-for="(item, index) in comments"
|
||||
:key="index"
|
||||
>
|
||||
<div
|
||||
class="info"
|
||||
@click="getInfoByUsername(item.fromUid, reply.fromName)"
|
||||
:title="item.fromName"
|
||||
>
|
||||
<avatar
|
||||
:username="item.fromName"
|
||||
:inline="true"
|
||||
:size="38"
|
||||
color="#FFF"
|
||||
:src="item.fromAvatar"
|
||||
></avatar>
|
||||
<div class="right">
|
||||
<div class="name">{{ item.fromName }}</div>
|
||||
<div class="date">{{ item.gmtCreate | localtime }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-bottom">
|
||||
<div
|
||||
class="content markdown-content markdown-body"
|
||||
v-html="mdToHtml(item.content)"
|
||||
v-katex
|
||||
v-highlight
|
||||
></div>
|
||||
<div class="control">
|
||||
<span
|
||||
class="like"
|
||||
:class="{ active: commentLikeMap[item.id] == true }"
|
||||
@click="likeClick(item)"
|
||||
>
|
||||
<i class="iconfont fa fa-thumbs-o-up"></i>
|
||||
<span class="like-num">{{
|
||||
item.likeNum > 0 ? item.likeNum + '人赞' : '赞'
|
||||
}}</span>
|
||||
</span>
|
||||
<span class="comment-reply" @click="showCommentInput(item)">
|
||||
<i class="iconfont el-icon-chat-square"></i>
|
||||
<span>回复</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="reply">
|
||||
<div
|
||||
class="item"
|
||||
v-for="(reply, index) in item.replyList"
|
||||
:key="index"
|
||||
>
|
||||
<div class="reply-content">
|
||||
<span
|
||||
class="from-name"
|
||||
:title="reply.fromName"
|
||||
@click="getInfoByUsername(reply.fromUid, reply.fromName)"
|
||||
>
|
||||
<avatar
|
||||
:username="reply.fromName"
|
||||
:inline="true"
|
||||
:size="25"
|
||||
class="user-avatar"
|
||||
color="#FFF"
|
||||
:src="reply.fromAvatar"
|
||||
></avatar>
|
||||
{{ reply.fromName }} </span
|
||||
><span class="reply-text">回复</span>
|
||||
<span
|
||||
class="to-name"
|
||||
:title="reply.toName"
|
||||
@click="getInfoByUsername(reply.toUid, reply.toName)"
|
||||
>@{{ reply.toName }}</span
|
||||
>
|
||||
</div>
|
||||
<div style="padding: 8px 0;margin-left: 34px;">
|
||||
<span
|
||||
v-html="mdToHtml(reply.content)"
|
||||
class="markdown-content markdown-body"
|
||||
v-katex
|
||||
v-highlight
|
||||
></span>
|
||||
</div>
|
||||
<div class="reply-bottom">
|
||||
<span>{{ reply.gmtCreate | localtime }}</span>
|
||||
<span class="reply-text" @click="showCommentInput(item, reply)">
|
||||
<i class="iconfont el-icon-chat-square"></i>
|
||||
<span>回复</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-more item" v-if="item.totalReplyNum > 3">
|
||||
共<b>{{ item.totalReplyNum }}</b
|
||||
>条回复,
|
||||
<a
|
||||
class="btn-more"
|
||||
@click="showAllReply(item)"
|
||||
v-if="!item.hadOpen"
|
||||
>点击查看全部</a
|
||||
>
|
||||
<a class="btn-more" @click="pickWayReply(item)" v-else>收起</a>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<div class="input-wrapper" v-if="showItemId === item.id">
|
||||
<el-input
|
||||
v-model="replyInputComment"
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
autofocus
|
||||
id="reply-textarea"
|
||||
:placeholder="replyPlaceholder"
|
||||
>
|
||||
</el-input>
|
||||
<div class="input-bottom">
|
||||
<span title="emoji表情">
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
width="300"
|
||||
trigger="click"
|
||||
v-model="replyFacelistVisiable"
|
||||
>
|
||||
<div class="emotionList">
|
||||
<a
|
||||
href="javascript:void(0);"
|
||||
@click="getEmo(index, false)"
|
||||
v-for="(item, index) in faceList"
|
||||
:key="index"
|
||||
class="emotionItem"
|
||||
>{{ item }}</a
|
||||
>
|
||||
</div>
|
||||
<i
|
||||
class="fa fa-smile-o emotionSelect"
|
||||
slot="reference"
|
||||
></i>
|
||||
</el-popover>
|
||||
</span>
|
||||
<span
|
||||
class="markdown-key"
|
||||
title="行内代码"
|
||||
@click="addContentTips(0, true)"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
class="css-1rhb60f-Svg ea8ky5j0"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5.5 5h1.866a.5.5 0 01.486.617l-.96 4a.5.5 0 01-.486.383H5.5a.5.5 0 01-.5-.5v-4a.5.5 0 01.5-.5zm8 0h1.866a.5.5 0 01.486.617l-.96 4a.5.5 0 01-.486.383H13.5a.5.5 0 01-.5-.5v-4a.5.5 0 01.5-.5z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="markdown-key"
|
||||
title="代码块"
|
||||
@click="addContentTips(1, true)"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
class="css-1rhb60f-Svg ea8ky5j0"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M3.64 10.98c.147 0 .312-.022.495-.066.183-.044.348-.12.495-.23.147-.11.271-.269.374-.474.103-.205.154-.462.154-.77V5.084c0-.499.077-.928.231-1.287.154-.36.363-.66.627-.902s.565-.422.902-.539c.337-.117.69-.176 1.056-.176H9.58v1.848H8.37c-.425 0-.693.14-.803.418-.11.279-.165.58-.165.902v4.136c0 .425-.08.788-.242 1.09-.161.3-.352.545-.572.736-.22.19-.455.337-.704.44-.25.103-.462.161-.638.176v.044c.176.015.389.062.638.143.25.08.484.216.704.407.22.19.41.447.572.77.161.323.242.74.242 1.254v4.004c0 .323.055.623.165.902.11.279.378.418.803.418h1.21v1.848H7.974c-.367 0-.719-.059-1.056-.176a2.573 2.573 0 01-.902-.539 2.586 2.586 0 01-.627-.902c-.154-.36-.231-.788-.231-1.287V14.61c0-.352-.051-.638-.154-.858a1.494 1.494 0 00-.374-.517 1.18 1.18 0 00-.495-.253 2.143 2.143 0 00-.495-.066V10.98zm16.72 2.04c-.161 0-.33.022-.506.066a1.184 1.184 0 00-.484.253 1.494 1.494 0 00-.374.517c-.103.22-.154.506-.154.858v4.202c0 .499-.077.928-.231 1.287-.154.36-.363.66-.627.902a2.573 2.573 0 01-.902.54c-.337.116-.69.175-1.056.175H14.42v-1.848h1.21c.425 0 .693-.14.803-.418.11-.279.165-.58.165-.902v-4.004c0-.513.08-.931.242-1.254.161-.323.352-.58.572-.77.22-.19.455-.326.704-.407.25-.08.462-.128.638-.143v-.044a2.225 2.225 0 01-.638-.176 2.567 2.567 0 01-.704-.44c-.22-.19-.41-.436-.572-.737-.161-.3-.242-.664-.242-1.089V5.452c0-.323-.055-.623-.165-.902-.11-.279-.378-.418-.803-.418h-1.21V2.284h1.606c.367 0 .719.059 1.056.176.337.117.638.297.902.539.264.242.473.543.627.902.154.36.231.788.231 1.287v4.356c0 .308.051.565.154.77.103.205.227.363.374.473.147.11.308.187.484.231.176.044.345.066.506.066v1.936z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="markdown-key"
|
||||
title="链接"
|
||||
@click="addContentTips(2, true)"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
class="css-1rhb60f-Svg ea8ky5j0"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M13 7a1 1 0 011-1h2a6 6 0 010 12h-2a1 1 0 110-2h2a4 4 0 000-8h-2a1 1 0 01-1-1zm-3 10a1 1 0 01-1 1H8A6 6 0 018 6h1a1 1 0 010 2H8a4 4 0 100 8h1a1 1 0 011 1zm-1-6h6a1 1 0 110 2H9a1 1 0 110-2z"
|
||||
clip-rule="evenodd"
|
||||
></path></svg
|
||||
></span>
|
||||
<span
|
||||
class="markdown-key"
|
||||
title="无序列表"
|
||||
@click="addContentTips(3, true)"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
class="css-1rhb60f-Svg ea8ky5j0"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M8 7a1 1 0 110-2h13a1 1 0 110 2H8zm0 6a1 1 0 110-2h13a1 1 0 110 2H8zm0 6a1 1 0 110-2h13a1 1 0 110 2H8zM2.952 6.85a1.201 1.201 0 111.698-1.7 1.201 1.201 0 01-1.698 1.7zm0 6a1.201 1.201 0 111.698-1.7 1.201 1.201 0 01-1.698 1.7zm0 6a1.201 1.201 0 111.698-1.7 1.201 1.201 0 01-1.698 1.7z"
|
||||
clip-rule="evenodd"
|
||||
></path></svg
|
||||
></span>
|
||||
<span
|
||||
class="markdown-key"
|
||||
title="有序列表"
|
||||
@click="addContentTips(4, true)"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
class="css-1rhb60f-Svg ea8ky5j0"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M7 6a1 1 0 011-1h13a1 1 0 110 2H8a1 1 0 01-1-1zm1 13a1 1 0 110-2h13a1 1 0 010 2H8zm0-6a1 1 0 110-2h13a1 1 0 010 2H8zM2.756 4.98l.236-1.18h2.22l-.92 4.6h-1.38l.684-3.42h-.84zm2.823 8.3l-.235 1.192H1.756l.174-.87.073-.117 1.911-1.498c.219-.178.368-.324.445-.434a.49.49 0 00.099-.287.208.208 0 00-.083-.18c-.067-.052-.176-.082-.335-.082a.897.897 0 00-.398.094 1.02 1.02 0 00-.342.272l-.12.144-.968-.702.118-.162c.193-.264.456-.473.786-.625.327-.15.684-.225 1.068-.225.318 0 .602.053.85.16.255.11.456.267.6.47.146.206.22.44.22.698 0 .298-.083.58-.247.839-.158.25-.424.518-.797.807l-.653.506h1.422zm-3.063 3.7l.245-1.18h3.345l-.166.867-.06.11-.93.869a1.205 1.205 0 01.737 1.144c-.001.327-.098.622-.29.881a1.856 1.856 0 01-.774.593c-.322.139-.685.208-1.087.208-.328 0-.636-.045-.924-.135a2.06 2.06 0 01-.743-.4l-.132-.114.688-1.05.173.147c.12.103.267.185.442.245.177.06.364.091.562.091.251 0 .435-.044.554-.124a.31.31 0 00.146-.282c0-.086-.025-.135-.08-.171-.076-.05-.213-.079-.41-.079h-.687l.173-.88.06-.108.674-.632H2.516z"
|
||||
clip-rule="evenodd"
|
||||
></path></svg
|
||||
></span>
|
||||
<span class="btn-control">
|
||||
<el-button
|
||||
class="btn"
|
||||
type="danger"
|
||||
round
|
||||
@click="cancel"
|
||||
size="small"
|
||||
>取消</el-button
|
||||
>
|
||||
<el-button
|
||||
class="btn"
|
||||
type="primary"
|
||||
round
|
||||
@click="commitReply(item.id)"
|
||||
size="small"
|
||||
>发送</el-button
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container loading-text" v-if="showloading">
|
||||
<a style="background: #fff;padding:10px;" @click="loadMoreComment"
|
||||
><span>加载更多...</span></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Avatar from 'vue-avatar';
|
||||
import { mapGetters } from 'vuex';
|
||||
import myMessage from '@/common/message';
|
||||
import api from '@/common/api';
|
||||
export default {
|
||||
props: {
|
||||
did: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
cid: {
|
||||
type: Number,
|
||||
require: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
components: { Avatar },
|
||||
data() {
|
||||
return {
|
||||
replyInputComment: '',
|
||||
ownInputComment: '',
|
||||
showItemId: '',
|
||||
faceList: [],
|
||||
comments: [],
|
||||
commentLikeMap: {},
|
||||
facelistVisiable: false,
|
||||
replyFacelistVisiable: false,
|
||||
replyPlaceholder: '写下你的评论吧~',
|
||||
query: {
|
||||
limit: 10,
|
||||
currentPage: 1,
|
||||
cid: null,
|
||||
did: null,
|
||||
},
|
||||
total: 0,
|
||||
totalComment: 0,
|
||||
replyObj: {
|
||||
commentId: 0,
|
||||
content: '',
|
||||
toUid: '',
|
||||
toName: '',
|
||||
toAvatar: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
const appData = require('./emoji.json');
|
||||
for (let i in appData) {
|
||||
this.faceList.push(appData[i]);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query.did = this.did;
|
||||
this.query.cid = this.cid;
|
||||
this.init();
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'isAuthenticated', 'isAdminRole']),
|
||||
avatar() {
|
||||
return this.$store.getters.userInfo.avatar;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
let queryParams = Object.assign({}, this.query);
|
||||
api.getCommentList(queryParams).then((res) => {
|
||||
let moreCommentList = res.data.data.commentList.records;
|
||||
for (let i = 0; i < moreCommentList.length; i++) {
|
||||
this.totalComment += 1 + moreCommentList[i].totalReplyNum;
|
||||
}
|
||||
this.comments = this.comments.concat(moreCommentList);
|
||||
this.total = res.data.data.commentList.total;
|
||||
this.commentLikeMap = res.data.data.commentLikeMap;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 点赞
|
||||
*/
|
||||
likeClick(item) {
|
||||
// 没有赞过则表明想去进行点赞
|
||||
let toLike = this.commentLikeMap[item.id] != true;
|
||||
api.toLikeComment(item.id, toLike).then((res) => {
|
||||
if (toLike) {
|
||||
this.commentLikeMap[item.id] = true;
|
||||
item.likeNum++;
|
||||
} else {
|
||||
item.likeNum--;
|
||||
this.commentLikeMap[item.id] = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 点击取消按钮
|
||||
*/
|
||||
cancel() {
|
||||
this.showItemId = '';
|
||||
},
|
||||
|
||||
/**
|
||||
* 提交评论
|
||||
*/
|
||||
commitComment() {
|
||||
if (!this.isAuthenticated) {
|
||||
myMessage.warning('请先登录');
|
||||
this.$store.dispatch('changeModalStatus', { visible: true });
|
||||
return;
|
||||
}
|
||||
let comment = {
|
||||
content: this.ownInputComment,
|
||||
cid: this.cid,
|
||||
did: this.did,
|
||||
};
|
||||
api.addComment(comment).then((res) => {
|
||||
this.comments = [res.data.data].concat(this.comments);
|
||||
this.totalComment++;
|
||||
myMessage.success(res.data.msg);
|
||||
this.ownInputComment = '';
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 提交回复
|
||||
*/
|
||||
|
||||
commitReply() {
|
||||
if (!this.isAuthenticated) {
|
||||
myMessage.warning('请先登录');
|
||||
this.$store.dispatch('changeModalStatus', { visible: true });
|
||||
return;
|
||||
}
|
||||
this.replyObj.content = this.replyInputComment;
|
||||
let replyData = Object.assign({}, this.replyObj);
|
||||
api.addReply(replyData).then((res) => {
|
||||
for (let i = 0; i < this.comments.length; i++) {
|
||||
if (this.comments[i].id == this.replyObj.commentId) {
|
||||
this.comments[i].replyList = [res.data.data].concat(
|
||||
this.comments[i].replyList
|
||||
);
|
||||
if (this.comments[i].backupReplyList) {
|
||||
this.comments[i].backupReplyList = [res.data.data].concat(
|
||||
this.comments[i].backupReplyList
|
||||
);
|
||||
}
|
||||
this.comments[i].totalReplyNum++;
|
||||
if (this.comments[i].totalReplyNum > 3) {
|
||||
this.comments[i].hadOpen = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.totalComment++;
|
||||
myMessage.success(res.data.msg);
|
||||
this.replyInputComment = '';
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 点击评论按钮显示输入框
|
||||
* item: 当前大评论
|
||||
* reply: 当前回复的评论
|
||||
*/
|
||||
showCommentInput(item, reply) {
|
||||
this.replyInputComment = '';
|
||||
if (reply) {
|
||||
this.replyPlaceholder = '@' + reply.fromName;
|
||||
// 对当前需要回复评论对象进行赋值
|
||||
this.replyObj.commentId = item.id;
|
||||
this.replyObj.toUid = reply.fromUid;
|
||||
this.replyObj.toName = reply.fromName;
|
||||
this.replyObj.toAvatar = reply.fromAvatar;
|
||||
} else {
|
||||
this.replyPlaceholder = '快来写下你的评论吧~';
|
||||
this.replyObj.commentId = item.id;
|
||||
this.replyObj.toUid = item.fromUid;
|
||||
this.replyObj.toName = item.fromName;
|
||||
this.replyObj.toAvatar = item.fromAvatar;
|
||||
}
|
||||
this.showItemId = item.id;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取emoji表情写入到文本域中
|
||||
*
|
||||
*/
|
||||
getEmo(index, isOwn) {
|
||||
var textArea;
|
||||
if (isOwn) {
|
||||
textArea = document.getElementById('own-textarea');
|
||||
} else {
|
||||
textArea = document.getElementById('reply-textarea');
|
||||
}
|
||||
function changeSelectedText(obj, str) {
|
||||
if (window.getSelection) {
|
||||
// 非IE浏览器
|
||||
textArea.setRangeText(str);
|
||||
// 在未选中文本的情况下,重新设置光标位置
|
||||
textArea.selectionStart += str.length;
|
||||
textArea.focus();
|
||||
} else if (document.selection) {
|
||||
// IE浏览器
|
||||
obj.focus();
|
||||
var sel = document.selection.createRange();
|
||||
sel.text = str;
|
||||
}
|
||||
}
|
||||
changeSelectedText(textArea, this.faceList[index]);
|
||||
if (isOwn) {
|
||||
this.ownInputComment = textArea.value; // 要同步data中的数据
|
||||
this.facelistVisiable = false; // 同时关闭表情列表
|
||||
} else {
|
||||
this.replyInputComment = textArea.value; // 要同步data中的数据
|
||||
this.replyFacelistVisiable = false; // 同时关闭表情列表
|
||||
}
|
||||
return;
|
||||
},
|
||||
|
||||
addContentTips(num, isReply) {
|
||||
let tips = '';
|
||||
switch (num) {
|
||||
case 0:
|
||||
tips = '`your inline code...`';
|
||||
break;
|
||||
case 1:
|
||||
tips = '\n```\ncode block\n```\n';
|
||||
break;
|
||||
case 2:
|
||||
tips = '[HOJ](https://hcode.top)';
|
||||
break;
|
||||
case 3:
|
||||
tips = '\n- 无序列表';
|
||||
break;
|
||||
case 4:
|
||||
tips = '\n1. 有序列表';
|
||||
break;
|
||||
}
|
||||
if (isReply) {
|
||||
this.replyInputComment += tips;
|
||||
} else {
|
||||
this.ownInputComment += tips;
|
||||
}
|
||||
},
|
||||
|
||||
mdToHtml(content) {
|
||||
return this.$markDown.render(content);
|
||||
},
|
||||
|
||||
getInfoByUsername(uid, username) {
|
||||
this.$router.push({
|
||||
path: '/user-home',
|
||||
query: { uid, username },
|
||||
});
|
||||
},
|
||||
|
||||
showAllReply(item) {
|
||||
if (!item.backupReplyList) {
|
||||
api.getAllReply(item.id).then((res) => {
|
||||
item.replyList = res.data.data;
|
||||
item.backupReplyList = res.data.data;
|
||||
item.hadOpen = true;
|
||||
this.totalComment += res.data.data.length - item.totalReplyNum;
|
||||
item.totalReplyNum = res.data.data.length;
|
||||
});
|
||||
} else {
|
||||
item.replyList = item.backupReplyList;
|
||||
item.hadOpen = true;
|
||||
}
|
||||
},
|
||||
pickWayReply(item) {
|
||||
let newArr = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
newArr.push(item.replyList[i]);
|
||||
}
|
||||
item.replyList = newArr;
|
||||
item.hadOpen = false;
|
||||
return;
|
||||
},
|
||||
loadMoreComment() {
|
||||
this.query.currentPage += 1;
|
||||
this.init();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isAuthenticated']),
|
||||
showloading() {
|
||||
if (this.query.currentPage * this.query.limit >= this.total) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isAuthenticated(newVal, oldVal) {
|
||||
if (newVal != oldVal) {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.el-popover {
|
||||
height: 200px !important;
|
||||
width: 300px !important;
|
||||
overflow: scroll !important;
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
.comment .markdown-content p {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
padding: 10px 20px;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
border: 1px solid #ebeef5;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.container .own-input {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.container .input-bottom {
|
||||
margin-top: 10px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.container .input-bottom .markdown-key {
|
||||
font-size: 20px;
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.container .own-input .own-btn-comment {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.container .emotionSelect {
|
||||
font-size: 25px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.emotionList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 5px;
|
||||
}
|
||||
.emotionItem {
|
||||
width: 10%;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
/*包含以下四种的链接*/
|
||||
.emotionItem {
|
||||
text-decoration: none;
|
||||
}
|
||||
/*正常的未被访问过的链接*/
|
||||
.emotionItem:link {
|
||||
text-decoration: none;
|
||||
}
|
||||
/*已经访问过的链接*/
|
||||
.emotionItem:visited {
|
||||
text-decoration: none;
|
||||
}
|
||||
/*鼠标划过(停留)的链接*/
|
||||
.emotionItem:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
/* 正在点击的链接*/
|
||||
.emotionItem:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.comment-total {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
padding-left: 12px;
|
||||
border-left: 4px solid #03a9f4;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.comment-total .text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.comment-total .number {
|
||||
margin-left: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.user-avatar {
|
||||
margin-right: 5px !important;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.container .comment {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.container .comment .info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.container .comment .info .right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.container .comment .info .right .name {
|
||||
font-size: 16px;
|
||||
color: #409eff;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.container .comment .info .right .date {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
.container .comment .info-bottom {
|
||||
margin-left: 47px;
|
||||
}
|
||||
.container .comment .content {
|
||||
font-size: 16px;
|
||||
color: #303133;
|
||||
line-height: 20px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.container .comment .control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
}
|
||||
.container .comment .control .like {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.container .comment .control .like.active,
|
||||
.container .comment .control .like:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
.container .comment .control .like .iconfont {
|
||||
font-size: 14px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.container .comment .control .comment-reply {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.container .comment .control .comment-reply:hover {
|
||||
color: #333;
|
||||
}
|
||||
.container .comment .control .comment-reply .iconfont {
|
||||
font-size: 16px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.container .comment .reply {
|
||||
margin: 10px 0;
|
||||
border-left: 2px solid #dcdfe6;
|
||||
}
|
||||
.container .comment .reply .item {
|
||||
margin: 0 10px;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px dashed #ebeef5;
|
||||
}
|
||||
.container .comment .reply .item .reply-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
}
|
||||
.container .comment .reply .item .reply-content .from-name {
|
||||
color: #409eff;
|
||||
cursor: pointer;
|
||||
}
|
||||
.container .comment .reply .item .reply-content .reply-text {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.container .comment .reply .item .reply-content .to-name {
|
||||
color: #409eff;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.container .comment .reply .item .reply-bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-left: 34px;
|
||||
}
|
||||
.container .comment .reply .item .reply-bottom .reply-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.container .comment .reply .item .reply-bottom .reply-text:hover {
|
||||
color: #333;
|
||||
}
|
||||
.container .comment .reply .item .reply-bottom .reply-text .icon-comment {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.container .comment .reply .view-more {
|
||||
font-size: 12px;
|
||||
color: #6d757a;
|
||||
}
|
||||
.container .comment .reply .view-more .btn-more {
|
||||
padding: 2px 3px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.container .comment .reply .view-more a {
|
||||
outline: none;
|
||||
color: #00a1d6;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.container .comment .reply .view-more a:hover {
|
||||
background: #e5e9ef;
|
||||
color: #00a1d6;
|
||||
}
|
||||
|
||||
.container .comment .reply .fade-enter-active,
|
||||
.container .comment .reply fade-leave-active {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
.container .comment .reply .fade-enter,
|
||||
.container .comment .reply .fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.container .comment .reply .input-wrapper {
|
||||
padding: 10px;
|
||||
}
|
||||
.container .btn-control {
|
||||
float: right;
|
||||
align-items: center;
|
||||
}
|
||||
.container .comment .reply .input-wrapper .btn-control .cancel {
|
||||
font-size: 16px;
|
||||
color: #606266;
|
||||
margin-right: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.container .comment .reply .input-wrapper .btn-control .cancel:hover {
|
||||
color: #333;
|
||||
}
|
||||
.container .comment .reply .input-wrapper .btn-control .confirm {
|
||||
font-size: 16px;
|
||||
}
|
||||
.loading-text {
|
||||
text-align: center;
|
||||
}
|
||||
.loading-text a {
|
||||
color: #999;
|
||||
}
|
||||
.loading-text a:hover {
|
||||
text-decoration: none;
|
||||
color: #03a9f4;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1 @@
|
|||
["😀","😄","😁","😆","😅","🤣","😂","🙂","🙃","😉","😊","😇","🥰","😍","🤩","😘","😗","😚","😙","😋","😛","😜","🤪","😝","🤑","🤗","🤭","🤫","🤔","🤐","🤨","😐","😶","😶🌫️","😏","😒","🙄","😬","😮💨","🤥","😌","😔","😪","🤤","😴","😷","🤒","🤕","🤢","🤮","🤧","🥵","🥶","🥴","😵","😵💫","🤯","🤠","🥳","😎","🤓","🧐","😕","😟","🙁","☹️","😮","😯","😲","😳","🥺","😦","😧","😨","😰","😥","😢","😭","😱","😖","😣","😞","😓","😩","😫","🥱","😤","😡","😠","🤬","😈","👿","💀","☠️","💩","🤡","👹","👺","👻","👽","👾","🤖","😺","😸","😹","😻","😼","😽","🙀","😿","😾","🙈","🙉","🙊","💋","💌","💘","💝","💖","💗","💓","💞","💕","💟","❣️","❣","💔","❤️🔥","❤🩹","❤️","❤","🧡","💛","💚","💙","💜","🤎","🖤","🤍","💯","💢","💥","💫","💦","💨","🕳️","🕳","💣","💬","👁️🗨️","🗨️","🗨","🗯️","🗯","💭","💤","🙋","🙇","🙌","🙏","🚶","🏃","👯","💃","👫","👬","👭","💏","💑","👪","💪","👈","👉","☝","👆","👇","✌","✋","👌","👍","👎","✊","👊","👋","👏","👐","✍","🐁","🐂","🐅","🐇","🐉","🐍","🐎","🐐","🐒","🐓","🐕","🐖","💐","🌸","💮","🌹","🌺","🌻","🌼","🌷","🌱","🌿","🍀"]
|
|
@ -79,6 +79,7 @@
|
|||
|
||||
<script>
|
||||
import api from '@/common/api';
|
||||
import { addCodeBtn } from '@/common/codeblock';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
export default {
|
||||
name: 'Announcement',
|
||||
|
@ -142,6 +143,9 @@ export default {
|
|||
this.announcement = announcement;
|
||||
this.announcement.content = this.$markDown.render(announcement.content);
|
||||
this.listVisible = false;
|
||||
this.$nextTick((_) => {
|
||||
addCodeBtn();
|
||||
});
|
||||
},
|
||||
goBack() {
|
||||
this.listVisible = true;
|
||||
|
|
|
@ -205,7 +205,6 @@ export default {
|
|||
});
|
||||
}
|
||||
});
|
||||
this.editor.focus();
|
||||
},
|
||||
methods: {
|
||||
onEditorCodeChange(newCode) {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
<template>
|
||||
<pre
|
||||
v-highlight="code"
|
||||
:style="styleObject"
|
||||
><code :class="language"></code></pre>
|
||||
<div class="markdown-body">
|
||||
<pre
|
||||
v-highlight="code"
|
||||
:style="styleObject"
|
||||
><code :class="language"></code></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -38,13 +40,8 @@ export default {
|
|||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
pre {
|
||||
padding: 0;
|
||||
display: block;
|
||||
}
|
||||
code {
|
||||
padding: 20px;
|
||||
font-size: 1.1em;
|
||||
<style>
|
||||
.hljs {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -33,6 +33,10 @@
|
|||
<el-menu-item index="/oi-rank">OI Rank</el-menu-item>
|
||||
</el-submenu>
|
||||
|
||||
<el-menu-item index="/discussion"
|
||||
><i class="el-icon-s-comment"></i>Discussion</el-menu-item
|
||||
>
|
||||
|
||||
<el-submenu index="about">
|
||||
<template slot="title"><i class="el-icon-info"></i>About</template>
|
||||
<el-menu-item index="/introduction">Introduction</el-menu-item>
|
||||
|
@ -250,6 +254,18 @@
|
|||
</mu-list-item>
|
||||
</mu-list-item>
|
||||
|
||||
<mu-list-item
|
||||
button
|
||||
to="/discussion"
|
||||
@click="opendrawer = !opendrawer"
|
||||
active-class="mobile-menu-active"
|
||||
>
|
||||
<mu-list-item-action>
|
||||
<mu-icon value=":fa fa-comments" size="24"></mu-icon>
|
||||
</mu-list-item-action>
|
||||
<mu-list-item-title>Discussion</mu-list-item-title>
|
||||
</mu-list-item>
|
||||
|
||||
<mu-list-item
|
||||
button
|
||||
:ripple="false"
|
||||
|
@ -380,6 +396,8 @@ export default {
|
|||
activeMenuName() {
|
||||
if (this.$route.path.split('/')[1] == 'submission-detail') {
|
||||
return '/status';
|
||||
} else if (this.$route.path.split('/')[1] == 'discussion-detail') {
|
||||
return '/discussion';
|
||||
}
|
||||
return '/' + this.$route.path.split('/')[1];
|
||||
},
|
||||
|
|
|
@ -46,7 +46,7 @@ import SlideVerify from 'vue-monoplasty-slide-verify'
|
|||
|
||||
// markdown编辑器
|
||||
import mavonEditor from 'mavon-editor' //引入markdown编辑器
|
||||
import 'mavon-editor/dist/css/index.css'
|
||||
import 'mavon-editor/dist/css/index.css';
|
||||
Vue.use(mavonEditor)
|
||||
|
||||
import {Drawer,List,Menu,Icon,AppBar,Button,Divider} from 'muse-ui';
|
||||
|
|
|
@ -15,7 +15,10 @@ import ContestProblemList from "@/views/oj/contest/children/ContestProblemList.v
|
|||
import ContestRank from "@/views/oj/contest/children/ContestRank.vue"
|
||||
import ACMInfoAdmin from "@/views/oj/contest/children/ACMInfoAdmin.vue"
|
||||
import Announcements from "@/components/oj/common/Announcements.vue"
|
||||
import ContestComment from "@/views/oj/contest/children/ContestComment.vue"
|
||||
import ContestRejudgeAdmin from "@/views/oj/contest/children/ContestRejudgeAdmin.vue"
|
||||
import DiscussionList from "@/views/oj/discussion/discussionList.vue"
|
||||
import Discussion from "@/views/oj/discussion/discussion.vue"
|
||||
import Introduction from "@/views/oj/about/Introduction.vue"
|
||||
import Developer from "@/views/oj/about/Developer.vue"
|
||||
import NotFound from "@/views/404.vue"
|
||||
|
@ -151,20 +154,42 @@ const ojRoutes = [
|
|||
path:'rejudge',
|
||||
component:ContestRejudgeAdmin,
|
||||
meta: { title: 'Contest Rejudge',requireSuperAdmin:true }
|
||||
},
|
||||
{
|
||||
name: 'ContestComment',
|
||||
path:'comment',
|
||||
component: ContestComment,
|
||||
meta: { title: 'Contest Comment'}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/discussion',
|
||||
name: 'AllDiscussion',
|
||||
meta: {title: 'Discussion'},
|
||||
component:DiscussionList
|
||||
},
|
||||
{
|
||||
path: '/discussion/:problemID',
|
||||
name: 'ProblemDiscussion',
|
||||
meta: {title: 'Discussion'},
|
||||
component:DiscussionList
|
||||
},
|
||||
{
|
||||
path: '/discussion-detail/:discussionID',
|
||||
name:'DiscussionDetail',
|
||||
meta: {title: 'Discussion Detail'},
|
||||
component: Discussion
|
||||
},
|
||||
{
|
||||
path: '/introduction',
|
||||
meta: {title: 'Introduction'},
|
||||
component:Introduction,
|
||||
meta: { title: 'Introduction' }
|
||||
},
|
||||
{
|
||||
path: '/developer',
|
||||
meta: {title: 'Developer'},
|
||||
component:Developer,
|
||||
meta: { title: 'Developer' }
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
|
|
|
@ -178,7 +178,6 @@ export default {
|
|||
title: '',
|
||||
content: '',
|
||||
status: 0,
|
||||
content: '',
|
||||
uid: '',
|
||||
},
|
||||
// 对话框标题
|
||||
|
|
|
@ -199,6 +199,7 @@ import api from '@/common/api';
|
|||
import Announcements from '@/components/oj/common/Announcements.vue';
|
||||
import { CONTEST_STATUS_REVERSE } from '@/common/constants';
|
||||
import { mapState } from 'vuex';
|
||||
import { addCodeBtn } from '@/common/codeblock';
|
||||
import Avatar from 'vue-avatar';
|
||||
export default {
|
||||
name: 'home',
|
||||
|
@ -246,6 +247,11 @@ export default {
|
|||
this.contests[i].description
|
||||
);
|
||||
}
|
||||
if (this.contests.length > 0) {
|
||||
this.$nextTick((_) => {
|
||||
addCodeBtn();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
getRecentOtherContests() {
|
||||
|
|
|
@ -158,15 +158,15 @@
|
|||
</transition>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- <el-tab-pane name="ContestComment" lazy :disabled="contestMenuDisabled">
|
||||
<el-tab-pane name="ContestComment" lazy :disabled="contestMenuDisabled">
|
||||
<span slot="label"
|
||||
><i class="fa fa-commenting" aria-hidden="true"></i
|
||||
> Comments</span
|
||||
> Comment</span
|
||||
>
|
||||
<transition name="el-zoom-in-bottom">
|
||||
<router-view v-if="route_name === 'ContestComment'"></router-view>
|
||||
</transition>
|
||||
</el-tab-pane> -->
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane
|
||||
name="ContestACInfo"
|
||||
|
@ -208,6 +208,7 @@ import time from '@/common/time';
|
|||
import moment from 'moment';
|
||||
import api from '@/common/api';
|
||||
import { mapState, mapGetters, mapActions } from 'vuex';
|
||||
import { addCodeBtn } from '@/common/codeblock';
|
||||
import {
|
||||
CONTEST_STATUS_REVERSE,
|
||||
CONTEST_STATUS,
|
||||
|
@ -253,6 +254,9 @@ export default {
|
|||
this.$store.commit('nowAdd1s');
|
||||
}, 1000);
|
||||
}
|
||||
this.$nextTick((_) => {
|
||||
addCodeBtn();
|
||||
});
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<comment :cid="$route.params.contestID"></comment>
|
||||
</template>
|
||||
<script>
|
||||
import comment from '@/components/oj/comment/comment';
|
||||
export default {
|
||||
name: 'ContestComment',
|
||||
components: {
|
||||
comment,
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,264 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="container">
|
||||
<div class="title-article" style="text-align: left">
|
||||
<h1 class="title" id="sharetitle">
|
||||
<span>{{ discussion.title }}</span>
|
||||
</h1>
|
||||
<div class="title-msg">
|
||||
<span>
|
||||
<a
|
||||
class="c999"
|
||||
@click="getInfoByUsername(discussion.uid, discussion.author)"
|
||||
:title="discussion.author"
|
||||
>
|
||||
<avatar
|
||||
:username="discussion.author"
|
||||
:inline="true"
|
||||
:size="26"
|
||||
color="#FFF"
|
||||
class="user-avatar"
|
||||
:src="discussion.avatar"
|
||||
></avatar>
|
||||
<span class="user-name">{{ discussion.author }}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span
|
||||
class="role-root role"
|
||||
title="Super Administrator"
|
||||
v-if="discussion.role == 'root'"
|
||||
>SPA</span
|
||||
>
|
||||
<span
|
||||
class="role-admin role"
|
||||
title="Administrator"
|
||||
v-if="discussion.role == 'admin'"
|
||||
>ADM</span
|
||||
>
|
||||
<span class="c999" style="padding:0 6px;"
|
||||
><i class="el-icon-folder-opened"></i
|
||||
><a
|
||||
class="c999"
|
||||
@click="toAllDiscussionByCid(discussion.categoryId)"
|
||||
>
|
||||
分类:{{ discussion.categoryName }}</a
|
||||
></span
|
||||
>
|
||||
<span class="c999"
|
||||
><i class="fa fa-thumbs-o-up"></i
|
||||
><span> 点赞:{{ discussion.likeNum }}</span></span
|
||||
>
|
||||
<span class="c999"
|
||||
><i class="fa fa-eye"></i
|
||||
><span> 浏览:{{ discussion.viewNum }}</span></span
|
||||
>
|
||||
|
||||
<a href="javascript:void(0);" class="report" title="举报"
|
||||
><i class="fa fa-envira"></i><span>举报</span></a
|
||||
>
|
||||
<a
|
||||
@click="toLikeDiscussion(discussion.id, true)"
|
||||
class="like"
|
||||
title="点赞"
|
||||
v-if="!discussion.hasLike"
|
||||
>
|
||||
<i class="fa fa-thumbs-o-up"></i> <span>点赞</span></a
|
||||
>
|
||||
<a
|
||||
@click="toLikeDiscussion(discussion.id, false)"
|
||||
class="like"
|
||||
title="已点赞"
|
||||
v-else
|
||||
>
|
||||
<i class="fa fa-thumbs-up"></i> <span>已点赞</span></a
|
||||
>
|
||||
|
||||
<span>
|
||||
<i class="fa fa-clock-o"> 最后修改于:</i>
|
||||
<span>{{ discussion.gmtModified | localtime }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body-article">
|
||||
<div
|
||||
class="markdown-body"
|
||||
v-html="contentHtml"
|
||||
v-katex
|
||||
v-highlight
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<comment :did="$route.params.discussionID"></comment>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import comment from '@/components/oj/comment/comment';
|
||||
import api from '@/common/api';
|
||||
import myMessage from '@/common/message';
|
||||
import { addCodeBtn } from '@/common/codeblock';
|
||||
import Avatar from 'vue-avatar';
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
comment,
|
||||
Avatar,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
discussion: {
|
||||
author: 'HOJ',
|
||||
avatar: '',
|
||||
},
|
||||
query: {
|
||||
currentPage: 1,
|
||||
did: null,
|
||||
},
|
||||
discussionID: 0,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['changeDomTitle']),
|
||||
init() {
|
||||
this.routeName = this.$route.name;
|
||||
this.discussionID = this.$route.params.discussionID || '';
|
||||
|
||||
api.getDiscussion(this.discussionID).then((res) => {
|
||||
this.discussion = res.data.data;
|
||||
this.changeDomTitle({ title: this.discussion.title });
|
||||
this.$nextTick((_) => {
|
||||
addCodeBtn();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getInfoByUsername(uid, username) {
|
||||
this.$router.push({
|
||||
path: '/user-home',
|
||||
query: { uid, username },
|
||||
});
|
||||
},
|
||||
|
||||
toAllDiscussionByCid(cid) {
|
||||
this.$router.push({
|
||||
path: '/discussion',
|
||||
query: { cid },
|
||||
});
|
||||
},
|
||||
|
||||
toLikeDiscussion(did, toLike) {
|
||||
api.toLikeDiscussion(did, toLike).then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
if (toLike) {
|
||||
this.discussion.likeNum++;
|
||||
this.discussion.hasLike = true;
|
||||
} else {
|
||||
this.discussion.likeNum--;
|
||||
this.discussion.hasLike = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isAuthenticated', 'isAdminRole']),
|
||||
contentHtml() {
|
||||
if (this.discussion.content) {
|
||||
return this.$markDown.render(this.discussion.content);
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isAuthenticated(newVal, oldVal) {
|
||||
if (newVal != oldVal) {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.container {
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
border: 1px solid #ebeef5;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.title-article {
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
padding: 10px 20px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
.title-article h1.title {
|
||||
font-size: 25px;
|
||||
font-weight: 600;
|
||||
color: #34495e;
|
||||
padding: 0 0 10px;
|
||||
width: 80%;
|
||||
line-height: 32px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.title-article .title-msg {
|
||||
margin-bottom: 0px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
.title-article .title-msg span {
|
||||
margin-right: 3px;
|
||||
}
|
||||
.title-article .title-msg span a.c999 {
|
||||
color: #999 !important;
|
||||
}
|
||||
.title-article .title-msg span a.c999:hover {
|
||||
color: #007bff !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
.user-avatar {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.user-name {
|
||||
margin: 0 0.25rem !important;
|
||||
}
|
||||
.title-article .title-msg a.report {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 5px;
|
||||
color: #4caf50 !important;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
.title-article .title-msg a.like {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 60px;
|
||||
color: #ff6700 !important;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
.title-article .title-msg a.report {
|
||||
top: 50px !important;
|
||||
right: 12px !important;
|
||||
}
|
||||
.title-article .title-msg a.like {
|
||||
top: 24px !important;
|
||||
right: 12px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.body-article {
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
padding: 20px 20px;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,581 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :md="18" :xs="24">
|
||||
<div class="discussion-header">
|
||||
<span style="padding: 16px;float:left;">
|
||||
<el-breadcrumb separator-class="el-icon-arrow-right">
|
||||
<template v-if="currentCategory">
|
||||
<el-breadcrumb-item :to="{ name: routeName, query: null }"
|
||||
>全部</el-breadcrumb-item
|
||||
>
|
||||
<el-breadcrumb-item
|
||||
>{{ currentCategory }} ( {{ total }} )</el-breadcrumb-item
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-breadcrumb-item :to="{ name: routeName }"
|
||||
>全部 ( {{ total }} )</el-breadcrumb-item
|
||||
>
|
||||
</template>
|
||||
</el-breadcrumb>
|
||||
</span>
|
||||
<span class="search"
|
||||
><vxe-input
|
||||
v-model="query.keyword"
|
||||
placeholder="Enter the keyword"
|
||||
type="search"
|
||||
@keyup.enter.native="handleQueryChange"
|
||||
@search-click="handleQueryChange"
|
||||
></vxe-input
|
||||
></span>
|
||||
</div>
|
||||
<div
|
||||
class="title-article"
|
||||
v-for="(discussion, index) in discussionList"
|
||||
:key="index"
|
||||
>
|
||||
<el-card shadow="hover" class="list-card">
|
||||
<span class="svg-top" v-if="discussion.topPriority">
|
||||
<svg
|
||||
t="1620283436433"
|
||||
class="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="10095"
|
||||
width="48"
|
||||
height="48"
|
||||
>
|
||||
<path
|
||||
d="M989.9222626666667 444.3410103333334L580.1490096666668 34.909091333333336H119.41107066666666l870.511192 870.596525V444.3410103333334z"
|
||||
fill="#F44336"
|
||||
p-id="10096"
|
||||
></path>
|
||||
<path
|
||||
d="M621.3675956666667 219.39846433333332l-43.832889-43.770828-126.663111 126.841535-32.826182-32.780929 126.663112-126.841535-43.734627-43.673859 26.739071-26.775273 120.396283 120.224324-26.741657 26.776565zM582.6055756666667 284.67587833333334c24.030384-24.065293 50.614303-36.636444 79.751758-37.71604 29.134869-1.07701 55.240404 9.903838 78.31402 32.945131 21.950061 21.91903 32.323232 46.86998 31.120808 74.851556s-13.257697 53.441939-36.167111 76.383677c-23.901091 23.934707-50.254869 36.406303-79.057455 37.41608-28.806465 1.012364-54.481455-9.739636-77.024969-32.252121-22.016-21.98497-32.689131-47.067798-32.014223-75.244606 0.672323-28.179394 12.365576-53.638465 35.077172-76.383677z m36.196849 32.57794c-14.921697 14.943677-23.517091 30.756202-25.783596 47.438869-2.269091 16.68396 2.880646 31.297939 15.441454 43.841939 12.825859 12.807758 27.34804 18.234182 43.566546 16.271515 16.217212-1.960081 31.985778-10.608485 47.303111-25.947798 15.976727-15.998707 25.133253-32.109899 27.46699-48.332283 2.333737-16.221091-2.813414-30.637253-15.441455-43.247192-12.827152-12.809051-27.67903-18.133333-44.558222-15.972848-16.879192 2.157899-32.877899 10.808889-47.994828 25.947798zM780.1276766666667 524.3048083333333l-53.476848 53.553131-32.726627-32.681374 153.400889-153.616808 52.858829 52.783839c38.213818 38.159515 41.146182 73.44097 8.79709 105.83402-15.71297 15.737535-34.076444 22.586182-55.086545 20.552404-21.012687-2.032485-39.97996-11.897535-56.905697-29.591273l-16.861091-16.833939z m74.572283-74.67701l-49.516606 49.586424 14.182141 14.161454c19.240081 19.211636 37.209212 20.455434 53.913859 3.728809 16.305131-16.329697 14.941091-34.002747-4.101172-53.016566L854.6999596666667 449.6277983333334z"
|
||||
fill="#FFFFFF"
|
||||
p-id="10097"
|
||||
></path>
|
||||
</svg>
|
||||
</span>
|
||||
<a @click="toDiscussionDetail(discussion.id)" class="article-hlink">
|
||||
<h1>
|
||||
{{ discussion.title }}
|
||||
</h1>
|
||||
</a>
|
||||
<a
|
||||
@click="toDiscussionDetail(discussion.id)"
|
||||
class="article-hlink2"
|
||||
>
|
||||
<p>{{ discussion.description }}</p>
|
||||
</a>
|
||||
<div class="title-msg">
|
||||
<span>
|
||||
<a
|
||||
@click="getInfoByUsername(discussion.uid, discussion.author)"
|
||||
:title="discussion.author"
|
||||
>
|
||||
<avatar
|
||||
:username="discussion.author"
|
||||
:inline="true"
|
||||
:size="24"
|
||||
color="#FFF"
|
||||
class="user-avatar"
|
||||
:src="discussion.avatar"
|
||||
></avatar>
|
||||
<span class="pl">{{ discussion.author }}</span></a
|
||||
>
|
||||
<span
|
||||
class="role-root role"
|
||||
title="Super Administrator"
|
||||
v-if="discussion.role == 'root'"
|
||||
>SPA</span
|
||||
>
|
||||
<span
|
||||
class="role-admin role"
|
||||
title="Administrator"
|
||||
v-if="discussion.role == 'admin'"
|
||||
>ADM</span
|
||||
>
|
||||
</span>
|
||||
<span class="pr pl hidden-xs-only"
|
||||
><label class="fw"><i class="fa fa-clock-o"></i></label
|
||||
><span>
|
||||
时间:{{ discussion.gmtCreate | localtime }}</span
|
||||
></span
|
||||
>
|
||||
<span class="pr"
|
||||
><label class="fw"><i class="fa fa-thumbs-o-up"></i></label
|
||||
><span> 点赞:{{ discussion.likeNum }}</span></span
|
||||
>
|
||||
<span class="pr"
|
||||
><label class="fw"><i class="fa fa-eye"></i></label
|
||||
><span> 浏览:{{ discussion.viewNum }}</span></span
|
||||
>
|
||||
<span
|
||||
><label class="fw"><i class="el-icon-folder-opened"></i></label>
|
||||
<a
|
||||
@click="
|
||||
pushRouter(
|
||||
{ cid: discussion.categoryId },
|
||||
{ problemID: query.pid },
|
||||
routeName
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ cidMapName[discussion.categoryId] }}</a
|
||||
>
|
||||
</span>
|
||||
|
||||
<span class="pr hidden-sm-and-up" style="float:right "
|
||||
><label class="fw"><i class="fa fa-clock-o"></i></label
|
||||
><span> {{ discussion.gmtCreate | localtime }}</span></span
|
||||
>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<Pagination
|
||||
:total="total"
|
||||
:page-size="limit"
|
||||
@on-change="changeRoute"
|
||||
:current.sync="currentPage"
|
||||
></Pagination>
|
||||
</el-col>
|
||||
<el-col :md="6" :xs="24">
|
||||
<el-button
|
||||
class="btn"
|
||||
type="primary"
|
||||
@click="toEditDiscussion"
|
||||
style="width: 100%;"
|
||||
><i class="el-icon-edit">
|
||||
{{ this.query.pid == '' ? '发布讨论' : '发布题目讨论' }}</i
|
||||
>
|
||||
</el-button>
|
||||
<template v-if="this.query.pid">
|
||||
<el-button
|
||||
class="btn"
|
||||
type="success"
|
||||
@click="toAllDiscussion"
|
||||
style="width: 100%;margin-left:0;margin-top:10px"
|
||||
><i class="el-icon-s-home"> 综合讨论区</i>
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
class="btn"
|
||||
type="warning"
|
||||
@click="
|
||||
pushRouter(null, { problemID: query.pid }, 'ProblemDetails')
|
||||
"
|
||||
style="width: 100%;margin-left:0;margin-top:10px"
|
||||
><i class="el-icon-back"> 返回 ({{ query.pid }}) 题目</i>
|
||||
</el-button>
|
||||
</template>
|
||||
<div class="category-body">
|
||||
<h3 class="title-sidebar">
|
||||
<a @click="pushRouter(null, { problemID: query.pid }, routeName)"
|
||||
><i class="el-icon-folder-opened"></i> 讨论分类</a
|
||||
>
|
||||
</h3>
|
||||
<el-row>
|
||||
<el-col
|
||||
:span="24"
|
||||
class="category-item"
|
||||
:title="category.name"
|
||||
v-for="(category, index) in categoryList"
|
||||
:key="index"
|
||||
>
|
||||
<a
|
||||
@click="
|
||||
pushRouter(
|
||||
{ cid: category.id },
|
||||
{ problemID: query.pid },
|
||||
routeName
|
||||
)
|
||||
"
|
||||
style="display: block"
|
||||
>
|
||||
<i class="fa fa-flag"> {{ category.name }}</i>
|
||||
<span
|
||||
class="el-icon-arrow-right"
|
||||
style="float:right;font-weight: 600!important;"
|
||||
></span>
|
||||
</a>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--编辑讨论对话框-->
|
||||
<el-dialog
|
||||
:title="discussionDialogTitle"
|
||||
:visible.sync="showEditDiscussionDialog"
|
||||
:fullscreen="true"
|
||||
@open="onOpenEditDialog"
|
||||
>
|
||||
<el-form label-position="top" :model="discussion">
|
||||
<el-form-item label="讨论标题" required>
|
||||
<el-input
|
||||
v-model="discussion.title"
|
||||
placeholder="请输入讨论标题"
|
||||
class="title-input"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="讨论简介" required>
|
||||
<el-input
|
||||
v-model="discussion.description"
|
||||
placeholder="请输入讨论简介"
|
||||
class="title-input"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="讨论分类" required>
|
||||
<el-select v-model="discussion.categoryId" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="category in categoryList"
|
||||
:key="category.id"
|
||||
:label="category.name"
|
||||
:value="category.id"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否置顶" required v-if="isAdminRole">
|
||||
<el-switch v-model="discussion.topPriority"> </el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="讨论内容" required>
|
||||
<Editor :value.sync="discussion.content"></Editor>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button
|
||||
type="danger"
|
||||
@click.native="showEditDiscussionDialog = false"
|
||||
>取消</el-button
|
||||
>
|
||||
<el-button type="primary" @click.native="submitDiscussion"
|
||||
>发布</el-button
|
||||
>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Avatar from 'vue-avatar';
|
||||
import Pagination from '@/components/oj/common/Pagination';
|
||||
import Editor from '@/components/admin/Editor.vue';
|
||||
import api from '@/common/api';
|
||||
import myMessage from '@/common/message';
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
export default {
|
||||
components: {
|
||||
Avatar,
|
||||
Editor,
|
||||
Pagination,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
total: 0,
|
||||
limit: 15,
|
||||
currentPage: 1,
|
||||
showEditDiscussionDialog: false,
|
||||
discussion: {
|
||||
id: null,
|
||||
pid: null, // 题目id 为null表示不关联
|
||||
title: '',
|
||||
content: '',
|
||||
description: '',
|
||||
categoryId: '',
|
||||
topPriority: false,
|
||||
uid: '',
|
||||
author: '',
|
||||
avatar: '',
|
||||
},
|
||||
// 对话框标题
|
||||
discussionDialogTitle: 'Edit Discussion',
|
||||
discussionList: [],
|
||||
categoryList: [],
|
||||
cidMapName: {},
|
||||
currentCategory: '',
|
||||
query: {
|
||||
keyword: '',
|
||||
cid: '',
|
||||
currentPage: 1,
|
||||
limit: 15,
|
||||
pid: '',
|
||||
},
|
||||
routeName: '',
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
api.getCategoryList().then((res) => {
|
||||
this.categoryList = res.data.data;
|
||||
for (let i = 0; i < this.categoryList.length; i++) {
|
||||
this.cidMapName[this.categoryList[i].id] = this.categoryList[i].name;
|
||||
}
|
||||
this.init();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['changeDomTitle']),
|
||||
init() {
|
||||
this.routeName = this.$route.name;
|
||||
let query = this.$route.query;
|
||||
this.query.currentPage = parseInt(query.currentPage) || 1;
|
||||
if (this.query.currentPage < 1) {
|
||||
this.query.currentPage = 1;
|
||||
}
|
||||
this.query.keyword = query.keyword || '';
|
||||
this.query.cid = query.cid || '';
|
||||
this.query.pid = this.$route.params.problemID || '';
|
||||
|
||||
if (this.query.cid) {
|
||||
this.currentCategory = this.cidMapName[this.query.cid];
|
||||
} else {
|
||||
this.currentCategory = '';
|
||||
}
|
||||
|
||||
if (this.query.pid) {
|
||||
this.discussion.pid = this.query.pid;
|
||||
this.changeDomTitle({ title: this.query.pid + ' Discussion' });
|
||||
} else {
|
||||
this.discussion.pid = null;
|
||||
}
|
||||
|
||||
this.getDiscussionList();
|
||||
},
|
||||
|
||||
getDiscussionList() {
|
||||
let queryParams = Object.assign({}, this.query);
|
||||
api.getDiscussionList(this.limit, queryParams).then((res) => {
|
||||
this.total = res.data.data.total;
|
||||
this.discussionList = res.data.data.records;
|
||||
});
|
||||
},
|
||||
|
||||
getInfoByUsername(uid, username) {
|
||||
this.$router.push({
|
||||
path: '/user-home',
|
||||
query: { uid, username },
|
||||
});
|
||||
},
|
||||
|
||||
toEditDiscussion() {
|
||||
if (!this.isAuthenticated) {
|
||||
myMessage.warning('请先登录');
|
||||
this.$store.dispatch('changeModalStatus', { visible: true });
|
||||
} else {
|
||||
this.showEditDiscussionDialog = true;
|
||||
}
|
||||
},
|
||||
|
||||
handleQueryChange() {
|
||||
this.pushRouter(
|
||||
this.query,
|
||||
{ problemID: this.query.pid },
|
||||
this.routeName
|
||||
);
|
||||
},
|
||||
toDiscussionDetail(discussionID) {
|
||||
this.$router.push({
|
||||
name: 'DiscussionDetail',
|
||||
params: { discussionID: discussionID },
|
||||
});
|
||||
},
|
||||
|
||||
toAllDiscussion() {
|
||||
this.$router.push({
|
||||
path: '/discussion',
|
||||
});
|
||||
},
|
||||
|
||||
pushRouter(query, params, name) {
|
||||
this.$router.push({
|
||||
name: name,
|
||||
query: query,
|
||||
params: params,
|
||||
});
|
||||
},
|
||||
|
||||
// 打开编辑对话框的回调
|
||||
onOpenEditDialog() {
|
||||
// todo 优化
|
||||
// 暂时解决 文本编辑器显示异常bug
|
||||
setTimeout(() => {
|
||||
if (document.createEvent) {
|
||||
let event = document.createEvent('HTMLEvents');
|
||||
event.initEvent('resize', true, true);
|
||||
window.dispatchEvent(event);
|
||||
} else if (document.createEventObject) {
|
||||
window.fireEvent('onresize');
|
||||
}
|
||||
}, 0);
|
||||
},
|
||||
|
||||
submitDiscussion() {
|
||||
// 默认为题目的讨论添加题号格式
|
||||
let discussion = Object.assign({}, this.discussion);
|
||||
if (discussion.pid) {
|
||||
discussion.title = '[' + discussion.pid + '] ' + discussion.title;
|
||||
}
|
||||
api.addDiscussion(discussion).then((res) => {
|
||||
myMessage.success(res.data.msg);
|
||||
this.showEditDiscussionDialog = false;
|
||||
this.init();
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route(newVal, oldVal) {
|
||||
if (newVal != oldVal) {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['isAuthenticated', 'userInfo', 'isAdminRole']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.role-root {
|
||||
background-color: #f9d681 !important;
|
||||
color: #ff503f !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.role-admin {
|
||||
background-color: #409eff !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.role {
|
||||
display: inline-block;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.1875rem 0.25rem;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
<style scoped>
|
||||
/deep/ .el-card__body {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.discussion-header {
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
height: 41px;
|
||||
}
|
||||
.discussion-header .search {
|
||||
margin-top: 3px;
|
||||
margin-right: 6px;
|
||||
float: right;
|
||||
}
|
||||
.list-card {
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
}
|
||||
.list-card p {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
.list-card .article-hlink {
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
.svg-top {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
.article-hlink h1 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #34495e;
|
||||
margin-top: 5px;
|
||||
}
|
||||
a {
|
||||
color: #34495e;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.article-hlink2 p {
|
||||
margin-bottom: 10px;
|
||||
color: #888;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.title-article .title-msg {
|
||||
margin-top: 15px;
|
||||
font-size: 12px;
|
||||
color: #999 !important;
|
||||
}
|
||||
.title-article .title-msg a {
|
||||
color: #999;
|
||||
text-decoration: none;
|
||||
}
|
||||
.user-avatar {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.title-article .title-msg span {
|
||||
margin-right: 3px;
|
||||
}
|
||||
.title-article .title-msg .pl {
|
||||
padding-left: 0.3rem !important;
|
||||
}
|
||||
.title-article .title-msg .pr {
|
||||
padding-right: 0.3rem !important;
|
||||
}
|
||||
|
||||
.category-body {
|
||||
background: #fff;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.category-body .title-sidebar {
|
||||
border-bottom: 1px solid #eee;
|
||||
width: 100%;
|
||||
color: #34495e;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.category-body .title-sideba a {
|
||||
color: #34495e;
|
||||
}
|
||||
.category-body h3 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.category-item {
|
||||
height: 30px;
|
||||
font-size: 14px;
|
||||
padding: 3px 10px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.category-item a {
|
||||
color: #34495e;
|
||||
}
|
||||
.category-item:hover {
|
||||
background-color: #eff3f5 !important;
|
||||
font-weight: bold;
|
||||
color: #222;
|
||||
}
|
||||
</style>
|
|
@ -25,6 +25,15 @@
|
|||
><el-tag effect="plain" size="small">暂无标签</el-tag></span
|
||||
>
|
||||
<div class="problem-menu">
|
||||
<span v-if="!contestID">
|
||||
<el-link
|
||||
type="primary"
|
||||
:underline="false"
|
||||
@click="goProblemDiscussion"
|
||||
><i class="fa fa-comments" aria-hidden="true"></i>
|
||||
Discussion</el-link
|
||||
></span
|
||||
>
|
||||
<span>
|
||||
<el-link
|
||||
type="primary"
|
||||
|
@ -40,7 +49,7 @@
|
|||
:underline="false"
|
||||
@click="goProblemSubmission"
|
||||
><i class="fa fa-bars" aria-hidden="true"></i>
|
||||
Submissions</el-link
|
||||
Submission</el-link
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
|
@ -538,6 +547,12 @@ export default {
|
|||
});
|
||||
}
|
||||
},
|
||||
goProblemDiscussion() {
|
||||
this.$router.push({
|
||||
name: 'ProblemDiscussion',
|
||||
params: { problemID: this.problemID },
|
||||
});
|
||||
},
|
||||
|
||||
handleRoute(route) {
|
||||
this.$router.push(route);
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
</vxe-table>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24">
|
||||
<el-col :span="24" style="margin-top: 13px;">
|
||||
<Highlight
|
||||
:code="submission.code"
|
||||
:language="submission.language"
|
||||
|
@ -184,7 +184,7 @@ import { JUDGE_STATUS, JUDGE_STATUS_RESERVE } from '@/common/constants';
|
|||
import utils from '@/common/utils';
|
||||
import Highlight from '@/components/oj/common/Highlight';
|
||||
import myMessage from '@/common/message';
|
||||
|
||||
import { addCodeBtn } from '@/common/codeblock';
|
||||
export default {
|
||||
name: 'submissionDetails',
|
||||
components: {
|
||||
|
@ -309,6 +309,10 @@ export default {
|
|||
} else {
|
||||
this.codeShare = data.codeShare;
|
||||
}
|
||||
|
||||
this.$nextTick((_) => {
|
||||
addCodeBtn();
|
||||
});
|
||||
},
|
||||
() => {
|
||||
this.loadingTable = false;
|
||||
|
@ -400,10 +404,7 @@ export default {
|
|||
.el-row--flex {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
pre {
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.test-detail-item {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
|
|
|
@ -56,8 +56,13 @@ module.exports={
|
|||
devServer: {
|
||||
open: true, // npm run serve后自动打开页面
|
||||
host: '0.0.0.0', // 匹配本机IP地址(默认是0.0.0.0)
|
||||
port: 8080, // 开发服务器运行端口号
|
||||
proxy: null,
|
||||
port: 8088, // 开发服务器运行端口号
|
||||
proxy: {
|
||||
'/api': { // 以'/api'开头的请求会被代理进行转发
|
||||
target: 'http://localhost:6688', // 要发向的后台服务器地址 如果后台服务跑在后台开发人员的机器上,就写成 `http://ip:port` 如 `http:192.168.12.213:8081` ip为后台服务器的ip
|
||||
changeOrigin: true
|
||||
}
|
||||
},
|
||||
disableHostCheck: true,
|
||||
},
|
||||
//去除生产环境的productionSourceMap
|
||||
|
|
|
@ -2,10 +2,10 @@ hoj:
|
|||
jwt:
|
||||
# 加密秘钥
|
||||
secret: zsc-acm-hoj
|
||||
# token有效时长,24小时,单位秒
|
||||
expire: 86400
|
||||
# 6小时内还有请求,可进行刷新
|
||||
checkRefreshExpire: 21600
|
||||
# token有效时长,3*3600*24,单位秒
|
||||
expire: 259200
|
||||
# 2*3600*24s内还有请求,可进行刷新
|
||||
checkRefreshExpire: 172800
|
||||
header: token
|
||||
judge:
|
||||
# 调用判题服务器的token
|
||||
|
|
|
@ -47,6 +47,18 @@ CREATE TABLE `auth` (
|
|||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `category` */
|
||||
|
||||
DROP TABLE IF EXISTS `category`;
|
||||
|
||||
CREATE TABLE `category` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) DEFAULT NULL,
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `code_template` */
|
||||
|
||||
DROP TABLE IF EXISTS `code_template`;
|
||||
|
@ -71,25 +83,43 @@ CREATE TABLE `code_template` (
|
|||
DROP TABLE IF EXISTS `comment`;
|
||||
|
||||
CREATE TABLE `comment` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`uid` varchar(32) NOT NULL,
|
||||
`title` varchar(255) NOT NULL COMMENT '讨论标题',
|
||||
`content` longtext COMMENT '讨论详情',
|
||||
`tid` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '标签id,0表示无',
|
||||
`pid` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '0表示无引用题目',
|
||||
`cid` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '0表示无引用比赛',
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`cid` bigint(20) unsigned DEFAULT NULL COMMENT 'null表示无引用比赛',
|
||||
`did` int(11) DEFAULT NULL COMMENT 'null表示无引用讨论',
|
||||
`content` longtext COMMENT '评论内容',
|
||||
`from_uid` varchar(32) NOT NULL COMMENT '评论者id',
|
||||
`from_name` varchar(255) DEFAULT NULL COMMENT '评论者用户名',
|
||||
`from_avatar` varchar(255) DEFAULT NULL COMMENT '评论组头像地址',
|
||||
`from_role` varchar(20) DEFAULT NULL COMMENT '评论者角色',
|
||||
`like_num` int(11) DEFAULT '0' COMMENT '点赞数量',
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `uid` (`from_uid`),
|
||||
KEY `from_avatar` (`from_avatar`),
|
||||
KEY `comment_ibfk_7` (`did`),
|
||||
KEY `cid` (`cid`),
|
||||
CONSTRAINT `comment_ibfk_6` FOREIGN KEY (`from_avatar`) REFERENCES `user_info` (`avatar`) ON DELETE NO ACTION ON UPDATE CASCADE,
|
||||
CONSTRAINT `comment_ibfk_7` FOREIGN KEY (`did`) REFERENCES `discussion` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `comment_ibfk_8` FOREIGN KEY (`cid`) REFERENCES `contest` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `comment_like` */
|
||||
|
||||
DROP TABLE IF EXISTS `comment_like`;
|
||||
|
||||
CREATE TABLE `comment_like` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`uid` varchar(255) NOT NULL,
|
||||
`cid` int(11) NOT NULL,
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `uid` (`uid`),
|
||||
KEY `tid` (`tid`),
|
||||
KEY `pid` (`pid`),
|
||||
KEY `cid` (`cid`),
|
||||
CONSTRAINT `comment_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uuid`) ON DELETE NO ACTION ON UPDATE CASCADE,
|
||||
CONSTRAINT `comment_ibfk_2` FOREIGN KEY (`tid`) REFERENCES `tag` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE,
|
||||
CONSTRAINT `comment_ibfk_3` FOREIGN KEY (`pid`) REFERENCES `problem` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE,
|
||||
CONSTRAINT `comment_ibfk_4` FOREIGN KEY (`cid`) REFERENCES `contest` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CONSTRAINT `comment_like_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uuid`) ON DELETE NO ACTION ON UPDATE CASCADE,
|
||||
CONSTRAINT `comment_like_ibfk_2` FOREIGN KEY (`cid`) REFERENCES `comment` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `contest` */
|
||||
|
||||
|
@ -244,6 +274,55 @@ CREATE TABLE `contest_score` (
|
|||
CONSTRAINT `contest_score_ibfk_1` FOREIGN KEY (`cid`) REFERENCES `contest` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `discussion` */
|
||||
|
||||
DROP TABLE IF EXISTS `discussion`;
|
||||
|
||||
CREATE TABLE `discussion` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`category_id` int(11) NOT NULL COMMENT '分类id',
|
||||
`title` varchar(255) DEFAULT NULL COMMENT '讨论标题',
|
||||
`description` varchar(255) DEFAULT NULL COMMENT '讨论简介',
|
||||
`content` longtext COMMENT '讨论内容',
|
||||
`pid` varchar(255) DEFAULT NULL COMMENT '关联题目id',
|
||||
`uid` varchar(32) NOT NULL COMMENT '发表者id',
|
||||
`author` varchar(255) NOT NULL COMMENT '发表者用户名',
|
||||
`avatar` varchar(255) DEFAULT NULL COMMENT '发表讨论者头像',
|
||||
`role` varchar(25) DEFAULT 'user' COMMENT '发表者角色',
|
||||
`view_num` int(11) DEFAULT '0' COMMENT '浏览数量',
|
||||
`like_num` int(11) DEFAULT '0' COMMENT '点赞数量',
|
||||
`top_priority` tinyint(1) DEFAULT '0' COMMENT '优先级,是否置顶',
|
||||
`status` tinyint(1) DEFAULT '0' COMMENT '是否封禁',
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `category_id` (`category_id`),
|
||||
KEY `discussion_ibfk_4` (`avatar`),
|
||||
KEY `discussion_ibfk_1` (`uid`),
|
||||
KEY `pid` (`pid`),
|
||||
CONSTRAINT `discussion_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uuid`) ON DELETE NO ACTION ON UPDATE CASCADE,
|
||||
CONSTRAINT `discussion_ibfk_2` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE,
|
||||
CONSTRAINT `discussion_ibfk_4` FOREIGN KEY (`avatar`) REFERENCES `user_info` (`avatar`) ON DELETE NO ACTION ON UPDATE CASCADE,
|
||||
CONSTRAINT `discussion_ibfk_6` FOREIGN KEY (`pid`) REFERENCES `problem` (`problem_id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `discussion_like` */
|
||||
|
||||
DROP TABLE IF EXISTS `discussion_like`;
|
||||
|
||||
CREATE TABLE `discussion_like` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`uid` varchar(255) NOT NULL,
|
||||
`did` int(11) NOT NULL,
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `did` (`did`),
|
||||
KEY `uid` (`uid`),
|
||||
CONSTRAINT `discussion_like_ibfk_1` FOREIGN KEY (`did`) REFERENCES `discussion` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `discussion_like_ibfk_2` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uuid`) ON DELETE NO ACTION ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `file` */
|
||||
|
||||
DROP TABLE IF EXISTS `file`;
|
||||
|
@ -298,7 +377,7 @@ CREATE TABLE `judge` (
|
|||
CONSTRAINT `judge_ibfk_1` FOREIGN KEY (`pid`) REFERENCES `problem` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE,
|
||||
CONSTRAINT `judge_ibfk_2` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uuid`) ON DELETE NO ACTION ON UPDATE CASCADE,
|
||||
CONSTRAINT `judge_ibfk_3` FOREIGN KEY (`username`) REFERENCES `user_info` (`username`) ON DELETE NO ACTION ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=132 DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=145 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `judge_case` */
|
||||
|
||||
|
@ -327,7 +406,7 @@ CREATE TABLE `judge_case` (
|
|||
CONSTRAINT `judge_case_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `judge_case_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `problem` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `judge_case_ibfk_3` FOREIGN KEY (`submit_id`) REFERENCES `judge` (`submit_id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1525 DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1733 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `judge_server` */
|
||||
|
||||
|
@ -348,7 +427,7 @@ CREATE TABLE `judge_server` (
|
|||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=124 DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=132 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `language` */
|
||||
|
||||
|
@ -399,8 +478,9 @@ CREATE TABLE `problem` (
|
|||
`case_version` varchar(40) DEFAULT '0' COMMENT '题目测试数据的版本号',
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`,`problem_id`),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `author` (`author`),
|
||||
KEY `problem_id` (`problem_id`),
|
||||
CONSTRAINT `problem_ibfk_1` FOREIGN KEY (`author`) REFERENCES `user_info` (`username`) ON DELETE NO ACTION ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1065 DEFAULT CHARSET=utf8;
|
||||
|
||||
|
@ -481,6 +561,32 @@ CREATE TABLE `problem_tag` (
|
|||
CONSTRAINT `problem_tag_ibfk_2` FOREIGN KEY (`tid`) REFERENCES `tag` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=71 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `reply` */
|
||||
|
||||
DROP TABLE IF EXISTS `reply`;
|
||||
|
||||
CREATE TABLE `reply` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`comment_id` int(11) NOT NULL COMMENT '被回复的评论id',
|
||||
`from_uid` varchar(255) NOT NULL COMMENT '发起回复的用户id',
|
||||
`from_name` varchar(255) NOT NULL COMMENT '发起回复的用户名',
|
||||
`from_avatar` varchar(255) DEFAULT NULL COMMENT '发起回复的用户头像地址',
|
||||
`from_role` varchar(255) DEFAULT NULL COMMENT '发起回复的用户角色',
|
||||
`to_uid` varchar(255) NOT NULL COMMENT '被回复的用户id',
|
||||
`to_name` varchar(255) NOT NULL COMMENT '被回复的用户名',
|
||||
`to_avatar` varchar(255) DEFAULT NULL COMMENT '被回复的用户头像地址',
|
||||
`content` longtext COMMENT '回复的内容',
|
||||
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `comment_id` (`comment_id`),
|
||||
KEY `from_avatar` (`from_avatar`),
|
||||
KEY `to_avatar` (`to_avatar`),
|
||||
CONSTRAINT `reply_ibfk_1` FOREIGN KEY (`comment_id`) REFERENCES `comment` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `reply_ibfk_2` FOREIGN KEY (`from_avatar`) REFERENCES `user_info` (`avatar`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `reply_ibfk_3` FOREIGN KEY (`to_avatar`) REFERENCES `user_info` (`avatar`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `role` */
|
||||
|
||||
DROP TABLE IF EXISTS `role`;
|
||||
|
@ -526,7 +632,7 @@ CREATE TABLE `session` (
|
|||
PRIMARY KEY (`id`),
|
||||
KEY `uid` (`uid`),
|
||||
CONSTRAINT `session_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=93 DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=118 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `tag` */
|
||||
|
||||
|
@ -560,7 +666,7 @@ CREATE TABLE `user_acproblem` (
|
|||
CONSTRAINT `user_acproblem_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `user_acproblem_ibfk_2` FOREIGN KEY (`pid`) REFERENCES `problem` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `user_acproblem_ibfk_3` FOREIGN KEY (`submit_id`) REFERENCES `judge` (`submit_id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=279 DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=306 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `user_info` */
|
||||
|
||||
|
@ -586,7 +692,8 @@ CREATE TABLE `user_info` (
|
|||
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
PRIMARY KEY (`uuid`),
|
||||
UNIQUE KEY `USERNAME_UNIQUE` (`username`),
|
||||
UNIQUE KEY `EMAIL_UNIQUE` (`email`)
|
||||
UNIQUE KEY `EMAIL_UNIQUE` (`email`),
|
||||
UNIQUE KEY `avatar` (`avatar`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `user_record` */
|
||||
|
@ -605,7 +712,7 @@ CREATE TABLE `user_record` (
|
|||
PRIMARY KEY (`id`,`uid`),
|
||||
KEY `uid` (`uid`),
|
||||
CONSTRAINT `user_record_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=304 DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=305 DEFAULT CHARSET=utf8;
|
||||
|
||||
/*Table structure for table `user_role` */
|
||||
|
||||
|
@ -622,7 +729,7 @@ CREATE TABLE `user_role` (
|
|||
KEY `role_id` (`role_id`) USING BTREE,
|
||||
CONSTRAINT `user_role_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uuid`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `user_role_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=306 DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=307 DEFAULT CHARSET=utf8;
|
||||
|
||||
/* Trigger structure for table `contest` */
|
||||
|
||||
|
|
Loading…
Reference in New Issue