Browse Source

Merge branch 'feature/unlimited-reply' of https://git.fanruan.com/zhaojunzhe/fair-web

* 'feature/unlimited-reply' of https://git.fanruan.com/zhaojunzhe/fair-web:
  bugfix: 修复比赛时后面记录会刷新前面记录的问题,现在只在后面记录得分高于前面记录时刷新。
  update:在回复系统界面里去除authorId
  feat: 完成多级回复功能。
  feat:评论系统改版初步

# Conflicts:
#	models-built/contest_player.js
#	models-built/contest_player.js.map
#	models/contest_player.ts
pull/31/head
zjz1993 5 years ago
parent
commit
50d55a3a80
  1. 2
      models-built/contest_player.js
  2. 1
      models-built/contest_player.js.map
  3. 144
      models-built/new_reply.js
  4. 1
      models-built/new_reply.js.map
  5. 51
      models/new_reply.ts
  6. 131
      modules/discussion.js
  7. 181
      views/article.ejs

2
models-built/contest_player.js

@ -138,7 +138,7 @@ var ContestPlayer = /** @class */ (function (_super) {
if (this.score_details[judge_state.problem_id] && this.score_details[judge_state.problem_id].judge_id > judge_state.id) if (this.score_details[judge_state.problem_id] && this.score_details[judge_state.problem_id].judge_id > judge_state.id)
return [2 /*return*/]; return [2 /*return*/];
this.score_details[judge_state.problem_id] = { this.score_details[judge_state.problem_id] = {
score: this.score_details[judge_state.problem_id] ? Math.max(judge_state.score, this.score_details[judge_state.problem_id].score) : judge_state.score, score: Math.max(judge_state.score, this.score_details[judge_state.problem_id].score),
judge_id: judge_state.id judge_id: judge_state.id
}; };
this.score = 0; this.score = 0;

1
models-built/contest_player.js.map

File diff suppressed because one or more lines are too long

144
models-built/new_reply.js

@ -0,0 +1,144 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
exports.__esModule = true;
var TypeORM = require("typeorm");
var common_1 = require("./common");
var user_1 = require("./user");
var article_1 = require("./article");
var NewReply = /** @class */ (function (_super) {
__extends(NewReply, _super);
function NewReply() {
return _super !== null && _super.apply(this, arguments) || this;
}
NewReply.prototype.loadRelationships = function () {
return __awaiter(this, void 0, void 0, function () {
var _a, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
_a = this;
return [4 /*yield*/, user_1["default"].findById(this.from_user_id)];
case 1:
_a.user = _c.sent();
_b = this;
return [4 /*yield*/, article_1["default"].findById(this.article_id)];
case 2:
_b.article = _c.sent();
return [2 /*return*/];
}
});
});
};
NewReply.prototype.isAllowedEditBy = function (user) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.loadRelationships()];
case 1:
_a.sent();
return [2 /*return*/, user && (user.is_admin || this.from_user_id === user.id || user.id === this.article.user_id)];
}
});
});
};
NewReply.cache = false;
__decorate([
TypeORM.PrimaryGeneratedColumn(),
__metadata("design:type", Number)
], NewReply.prototype, "id");
__decorate([
TypeORM.Column({ nullable: true, type: "mediumtext" }),
__metadata("design:type", String)
], NewReply.prototype, "content");
__decorate([
TypeORM.Column({ nullable: true, type: "integer" }),
__metadata("design:type", Number)
], NewReply.prototype, "article_id");
__decorate([
TypeORM.Column({ nullable: true, type: "integer" }),
__metadata("design:type", Number)
], NewReply.prototype, "from_user_id");
__decorate([
TypeORM.Column({ nullable: true, type: "integer" }),
__metadata("design:type", Number)
], NewReply.prototype, "to_user_id");
__decorate([
TypeORM.Column({ nullable: true, type: "integer" }),
__metadata("design:type", Number)
], NewReply.prototype, "parent_id");
__decorate([
TypeORM.Column({ nullable: true, type: "boolean", "default": true }),
__metadata("design:type", Boolean)
], NewReply.prototype, "is_show");
__decorate([
TypeORM.Column({ nullable: true, type: "integer" }),
__metadata("design:type", Number)
], NewReply.prototype, "public_time");
__decorate([
TypeORM.Column({ nullable: true, type: "integer" }),
__metadata("design:type", Number)
], NewReply.prototype, "update_time");
NewReply = __decorate([
TypeORM.Entity()
], NewReply);
return NewReply;
}(common_1["default"]));
exports["default"] = NewReply;
;
//# sourceMappingURL=new_reply.js.map

1
models-built/new_reply.js.map

@ -0,0 +1 @@
{"version":3,"file":"new_reply.js","sourceRoot":"","sources":["../models/new_reply.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iCAAmC;AACnC,mCAA6B;AAC7B,+BAA0B;AAC1B,qCAAgC;AAIhC;IAAsC,4BAAK;IAA3C;;IA2CA,CAAC;IAVS,oCAAiB,GAAvB;;;;;;wBACI,KAAA,IAAI,CAAA;wBAAQ,qBAAM,iBAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,EAAA;;wBAAlD,GAAK,IAAI,GAAG,SAAsC,CAAC;wBACnD,KAAA,IAAI,CAAA;wBAAW,qBAAM,oBAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,EAAA;;wBAAtD,GAAK,OAAO,GAAG,SAAuC,CAAC;;;;;KAC1D;IAEK,kCAAe,GAArB,UAAsB,IAAI;;;;4BACtB,qBAAM,IAAI,CAAC,iBAAiB,EAAE,EAAA;;wBAA9B,SAA8B,CAAC;wBAC/B,sBAAO,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAC;;;;KACvG;IAxCM,cAAK,GAAG,KAAK,CAAC;IAGrB;QADC,OAAO,CAAC,sBAAsB,EAAE;;gCACtB;IAGX;QADC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;;qCACvC;IAGhB;QADC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;wCACjC;IAGnB;QADC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;0CAC/B;IAGrB;QADC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;wCACjC;IAGnB;QADC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;uCAClC;IAGlB;QADC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,SAAO,EAAE,IAAI,EAAE,CAAC;;qCAClD;IAGjB;QADC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;yCAChC;IAGpB;QADC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;;yCAChC;IA5BH,QAAQ;QAD5B,OAAO,CAAC,MAAM,EAAE;OACI,QAAQ,CA2C5B;IAAD,eAAC;CAAA,AA3CD,CAAsC,mBAAK,GA2C1C;qBA3CoB,QAAQ;AA2C5B,CAAC"}

51
models/new_reply.ts

@ -0,0 +1,51 @@
import * as TypeORM from "typeorm";
import Model from "./common";
import User from "./user";
import Article from "./article";
declare var syzoj: any;
@TypeORM.Entity()
export default class NewReply extends Model {
static cache = false;
@TypeORM.PrimaryGeneratedColumn()
id: number;
@TypeORM.Column({ nullable: true, type: "mediumtext" })
content: string;
@TypeORM.Column({ nullable: true, type: "integer" })
article_id: number;
@TypeORM.Column({ nullable: true, type: "integer" })
from_user_id: number;
@TypeORM.Column({ nullable: true, type: "integer" })
to_user_id: number;
@TypeORM.Column({ nullable: true, type: "integer" })
parent_id: number;
@TypeORM.Column({ nullable: true, type: "boolean", default: true })
is_show: boolean;
@TypeORM.Column({ nullable: true, type: "integer" })
public_time: number;
@TypeORM.Column({ nullable: true, type: "integer" })
update_time: number;
user?: User;
article?: Article;
async loadRelationships() {
this.user = await User.findById(this.from_user_id);
this.article = await Article.findById(this.article_id);
}
async isAllowedEditBy(user) {
await this.loadRelationships();
return user && (user.is_admin || this.from_user_id === user.id || user.id === this.article.user_id);
}
};

131
modules/discussion.js

@ -3,6 +3,7 @@ let Article = syzoj.model('article');
let ArticleComment = syzoj.model('article-comment'); let ArticleComment = syzoj.model('article-comment');
let User = syzoj.model('user'); let User = syzoj.model('user');
let Reply = syzoj.model('reply'); let Reply = syzoj.model('reply');
let NewReply = syzoj.model('new_reply');
app.get('/discussion/:type?', async (req, res) => { app.get('/discussion/:type?', async (req, res) => {
try { try {
@ -21,7 +22,7 @@ app.get('/discussion/:type?', async (req, res) => {
let articles = await Article.queryPage(paginate, where, { let articles = await Article.queryPage(paginate, where, {
sort_time: 'DESC' sort_time: 'DESC'
}); });
let newReplyQuery = await NewReply.createQueryBuilder();
for (let article of articles) { for (let article of articles) {
await article.loadRelationships(); await article.loadRelationships();
if (in_problems) { if (in_problems) {
@ -77,7 +78,6 @@ app.get('/discussion/problem/:pid', async (req, res) => {
app.get('/article/:id', async (req, res) => { app.get('/article/:id', async (req, res) => {
try { try {
let id = parseInt(req.params.id); let id = parseInt(req.params.id);
let replyQuery = await Reply.createQueryBuilder();
let article = await Article.findById(id); let article = await Article.findById(id);
if (!article) throw new ErrorMessage('无此帖子。'); if (!article) throw new ErrorMessage('无此帖子。');
@ -86,30 +86,56 @@ app.get('/article/:id', async (req, res) => {
article.allowedComment = await article.isAllowedCommentBy(res.locals.user); article.allowedComment = await article.isAllowedCommentBy(res.locals.user);
article.content = await syzoj.utils.markdown(article.content); article.content = await syzoj.utils.markdown(article.content);
let newReplyQuery = await NewReply.createQueryBuilder();
let where = { article_id: id }; let where = { article_id: id };
let commentsCount = await ArticleComment.countForPagination(where); let commentsCount = await NewReply.countForPagination(where);
let paginate = syzoj.utils.paginate(commentsCount, req.query.page, syzoj.config.page.article_comment); let paginate = syzoj.utils.paginate(commentsCount, req.query.page, syzoj.config.page.article_comment);
let comments = await ArticleComment.queryPage(paginate, where, { let comments = await newReplyQuery.where('article_id=:id', {id}).getMany();
public_time: 'DESC'
});
for (let comment of comments) { function arrayToTree(data){
const commentId = comment.id; for(let i=0;i<data.length;i++){
const reply = await replyQuery.where("comment_id = :commentId", { commentId }).getMany(); if (data[i].parent_id){
if (reply.length === 0) { var info = data.find(item=>item.id===data[i].parent_id);
comment.reply = []; if (info) {
if (!info.children) {
info.children=[data[i]];
} else { } else {
comment.reply = await reply.mapAsync(async (item) => { info.children.push(data[i]);
const replyUserId = item.user_id; }
item.reply_user = await User.findById(replyUserId); }
return item
});
} }
comment.content = await syzoj.utils.markdown(comment.content);
comment.allowedEdit = await comment.isAllowedEditBy(res.locals.user);
await comment.loadRelationships();
} }
return data.filter(item=>!item.parent_id && item.is_show)
}
const originData = comments.filter(item => item.is_show);
const datas = arrayToTree(comments.filter(item => item.is_show));
const rootIdArray = datas.map(item=>item.id);
const authorId = article.user_id;
let count = 0;
async function operate(data){
for(let i=0;i<data.length;i++){
count++;
data[i].user = await User.findById(data[i].from_user_id);
data[i].avatarUrl = syzoj.utils.gravatar(data[i].user.email, 120);
data[i].date = syzoj.utils.formatDate(data[i].public_time)
data[i].toUser = await User.findById(data[i].to_user_id)
data[i].allowedEdit = await data[i].isAllowedEditBy(res.locals.user);
if (rootIdArray.includes(data[i].parent_id)){
data[i].firstComment = true;
}
if (data[i].children) {
await operate(data[i].children);
}
}
}
await operate(datas);
let articleQuery = await Article.createQueryBuilder();
await articleQuery.update(Article).set({comments_num: count}).where("id = :id", { id }).execute();
datas.sort((a,b) => {
return b.public_time - a.public_time;
})
let problem = null; let problem = null;
if (article.problem_id) { if (article.problem_id) {
@ -121,10 +147,11 @@ app.get('/article/:id', async (req, res) => {
res.render('article', { res.render('article', {
user: res.locals.user, user: res.locals.user,
article: article, article: article,
comments: comments, comments: datas,
paginate: paginate, paginate: paginate,
problem: problem, problem: problem,
commentsCount: commentsCount commentsCount: count,
authorId
}); });
} catch (e) { } catch (e) {
syzoj.log(e); syzoj.log(e);
@ -213,14 +240,9 @@ app.post('/article/:id/delete', async (req, res) => {
} else { } else {
if (!await article.isAllowedEditBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。'); if (!await article.isAllowedEditBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。');
} }
let newReplyQuery = await NewReply.createQueryBuilder();
await Promise.all((await ArticleComment.find({ await newReplyQuery.delete().from(NewReply).where("article_id = :id", { id }).execute();
article_id: article.id
})).map(comment => comment.destroy()));
await Promise.all((await Reply.find({
article_id: article.id
})).map(reply => reply.destroy()));
await article.destroy(); await article.destroy();
@ -239,18 +261,21 @@ app.post('/article/:id/comment', async (req, res) => {
let id = parseInt(req.params.id); let id = parseInt(req.params.id);
let article = await Article.findById(id); let article = await Article.findById(id);
let to_id = article.user_id;
if (!article) { if (!article) {
throw new ErrorMessage('无此帖子。'); throw new ErrorMessage('无此帖子。');
} else { } else {
if (!await article.isAllowedCommentBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。'); if (!await article.isAllowedCommentBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。');
} }
let comment = await ArticleComment.create({ let comment = await NewReply.create({
content: req.body.comment, content: req.body.comment,
article_id: id, article_id: id,
user_id: res.locals.user.id, from_user_id: res.locals.user.id,
public_time: syzoj.utils.getCurrentDate() to_user_id: to_id,
public_time: syzoj.utils.getCurrentDate(),
update_time: syzoj.utils.getCurrentDate(),
is_show: true
}); });
await comment.save(); await comment.save();
@ -274,31 +299,29 @@ app.post('/article/:id/comment/:commentId', async (req, res) => {
let commentId = parseInt(req.params.commentId); let commentId = parseInt(req.params.commentId);
let userId = parseInt(res.locals.user.id); let userId = parseInt(res.locals.user.id);
let article = await Article.findById(id); let article = await Article.findById(id);
let articleComment = await ArticleComment.findById(commentId);
if (!articleComment) {
throw new ErrorMessage('无此回复。');
}
if (!article) { if (!article) {
throw new ErrorMessage('无此帖子。'); throw new ErrorMessage('无此帖子。');
} else { } else {
if (!await article.isAllowedCommentBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。'); if (!await article.isAllowedCommentBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。');
} }
// let articleQuery = await Article.createQueryBuilder();
// await articleQuery.update(Article).set({comments_num: article.comments_num + 1}).where("id = :id", { id }).execute();
const toInfo = await NewReply.findById(commentId);
let newReply = await Reply.create({ let newReply = await NewReply.create({
content: req.body.comment, content: req.body.comment,
article_id: id, article_id: id,
user_id: userId, from_user_id: res.locals.user.id,
comment_id: commentId, to_user_id: toInfo.from_user_id,
parent_id: commentId,
public_time: syzoj.utils.getCurrentDate(), public_time: syzoj.utils.getCurrentDate(),
update_time: syzoj.utils.getCurrentDate() update_time: syzoj.utils.getCurrentDate(),
is_show: true
}); });
const reply = await newReply.save(); await newReply.save();
await article.resetReplyCountAndTime(); await article.resetReplyCountAndTime();
//
res.redirect(syzoj.utils.makeUrl(['article', article.id])); res.redirect(syzoj.utils.makeUrl(['article', article.id]));
} catch (e) { } catch (e) {
syzoj.log(e); syzoj.log(e);
@ -308,27 +331,17 @@ app.post('/article/:id/comment/:commentId', async (req, res) => {
} }
}); });
app.post('/article/:article_id/comment/:id/delete', async (req, res) => { app.post('/article/:articleId/comment/:id/delete', async (req, res) => {
try { try {
if (!res.locals.user) throw new ErrorMessage('请登录后继续。', { '登录': syzoj.utils.makeUrl(['login'], { 'url': req.originalUrl }) }); if (!res.locals.user) throw new ErrorMessage('请登录后继续。', { '登录': syzoj.utils.makeUrl(['login'], { 'url': req.originalUrl }) });
let id = parseInt(req.params.id); let id = parseInt(req.params.id);
let comment = await ArticleComment.findById(id); let articleId = parseInt(req.params.articleId);
let replyQuery = await Reply.createQueryBuilder(); let replyQuery = await NewReply.createQueryBuilder();
if (!comment) { await replyQuery.update(NewReply).set({is_show: 0}).where("id = :id", { id }).execute();
throw new ErrorMessage('无此评论。');
} else {
if (!await comment.isAllowedEditBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。');
}
const article = await Article.findById(comment.article_id);
await comment.destroy();
await replyQuery.delete().from(Reply).where("comment_id = :id", { id }).execute();
await article.resetReplyCountAndTime();
res.redirect(syzoj.utils.makeUrl(['article', comment.article_id])); res.redirect(syzoj.utils.makeUrl(['article', articleId]));
} catch (e) { } catch (e) {
syzoj.log(e); syzoj.log(e);
res.render('error', { res.render('error', {

181
views/article.ejs

@ -16,8 +16,16 @@
.article-comment:hover{ .article-comment:hover{
color: rgba(0,0,0,.8); color: rgba(0,0,0,.8);
} }
.border{
padding-bottom: 10px !important;
border-bottom: 1px solid #DEDEDF !important;
}
.border:last-child{
border-bottom: none !important;
}
</style> </style>
<div class="padding">
<div class="padding" id="comment-zone">
<div class="ui breadcrumb"> <div class="ui breadcrumb">
<div class="section">讨论</div> <div class="section">讨论</div>
<i class="right angle icon divider"></i> <i class="right angle icon divider"></i>
@ -69,102 +77,42 @@
<% if (comments.length) { %> <% if (comments.length) { %>
<div class="ui comments" style="max-width: none;"> <div class="ui comments" style="max-width: none;">
<h3 class="ui dividing header">共 <%= commentsCount %> 条回复</h3> <h3 class="ui dividing header">共 <%= commentsCount %> 条回复</h3>
<% for (let comment of comments) { %> <comment-box v-for="(item, index) in comments" :key="index" :data="item" :index="index" :user="user"></comment-box>
<div class="comment">
<a class="avatar">
<img src="<%= syzoj.utils.gravatar(comment.user.email, 120) %>">
</a>
<div class="content">
<a class="author" href="<%= syzoj.utils.makeUrl(['user', comment.user_id]) %>"><%= comment.user.username %></a><% if (comment.user.nameplate) { %><%- comment.user.nameplate %><% } %>
<div class="metadata">
<span class="date"><%= syzoj.utils.formatDate(comment.public_time) %></span>
</div>
<div class="text font-content" style="min-height: 19.5px; "><%- comment.content %></div>
<% if (comment.allowedEdit) { %>
<div class="actions"><a onclick="$('#modal-delete-<%= comment.id %>').modal('show')">删除</a><a onclick="reply('reply', <%= comment.id %>)">回复</a></div>
<div class="ui basic modal" id="modal-delete-<%= comment.id %>">
<div class="ui icon header">
<i class="trash icon"></i>
<p style="margin-top: 15px; ">删除评论</p>
</div>
<div class="content" style="text-align: center; ">
<p>确认删除这条评论吗?评论下所有回复也会被删除</p>
</div>
<div class="actions">
<div class="ui red basic cancel inverted button">
<i class="remove icon"></i>
</div>
<a class="ui green ok inverted button" href-post="<%= syzoj.utils.makeUrl(['article', article.id, 'comment', comment.id, 'delete']) %>">
<i class="checkmark icon"></i>
</a>
</div>
</div>
<% } else { %>
<div class="actions">
<% if(user){%>
<a onclick="reply('reply', <%= comment.id %>)">回复</a>
<%}%>
</div>
<%}%>
</div> </div>
<% } %>
<% if (article.allowedComment) { %>
<% include monaco-editor %>
<form id="comment-box" class="ui reply form" method="post" action="" style="visibility:hidden">
<div id="comment" class="editor editor-with-border" style="height: 200px; width: 100%; margin-bottom: 1em; ">
<%- this.showLoadingEditor(); %>
</div> </div>
<% for (let reply of comment.reply) { %> <input type="hidden" name="comment">
<div class="comment reply"> <div style="text-align: center; ">
<a class="avatar"> <button id="submit_button" type="submit" class="ui disabled labeled submit icon button">
<img src="<%= syzoj.utils.gravatar(reply.reply_user.email, 120) %>" alt=""> <i class="icon edit"></i> <span id="reply-text">回复</span>
</a> </button>
<div class="content">
<a class="author" href="<%= syzoj.utils.makeUrl(['user', reply.user_id]) %>"><%= reply.reply_user.username %></a><% if (reply.reply_user.nameplate) { %><%- reply.reply_user.nameplate %><% } %>
<div class="metadata">
<span class="date"><%= syzoj.utils.formatDate(reply.public_time) %></span>
</div> </div>
<div class="text font-content" style="min-height: 19.5px; "><%- reply.content %></div> </form>
<% if (comment.allowedEdit) { %> <div class="ui basic modal" id="modal-comment-delete">
<div class="actions"><a onclick="$('#modal-delete-reply-<%= reply.id %>').modal('show')">删除</a></div>
<div class="ui basic modal" id="modal-delete-reply-<%= reply.id %>">
<div class="ui icon header"> <div class="ui icon header">
<i class="trash icon"></i> <i class="trash icon"></i>
<p style="margin-top: 15px; ">删除回复</p> <p style="margin-top: 15px; ">删除评论</p>
</div> </div>
<div class="content" style="text-align: center; "> <div class="content" style="text-align: center; ">
<p>确认删除这条回复吗?</p> <p>确认删除这条评论吗?评论下所有回复也会被删除</p>
</div> </div>
<div class="actions"> <div class="actions">
<div class="ui red basic cancel inverted button"> <div class="ui red basic cancel inverted button">
<i class="remove icon"></i> <i class="remove icon"></i>
</div> </div>
<a class="ui green ok inverted button" href-post="<%= syzoj.utils.makeUrl(['article', article.id, 'reply', reply.id, 'delete']) %>"> <a class="ui green ok inverted button" href-post="" id="modal-comment-delete-href">
<i class="checkmark icon"></i> <i class="checkmark icon"></i>
</a> </a>
</div> </div>
</div> </div>
<% } %>
</div>
</div>
<% } %>
<% } %>
</div>
<div style="margin-bottom: 50px; ">
<% include page %>
</div>
<% } %>
<% if (article.allowedComment) { %>
<% include monaco-editor %>
<form id="comment-box" class="ui reply form" method="post" action="" style="visibility:hidden">
<div id="comment" class="editor editor-with-border" style="height: 200px; width: 100%; margin-bottom: 1em; ">
<%- this.showLoadingEditor(); %>
</div>
<input type="hidden" name="comment">
<div style="text-align: center; ">
<button id="submit_button" type="submit" class="ui disabled labeled submit icon button">
<i class="icon edit"></i> <span id="reply-text">回复</span>
</button>
</div>
</form>
<script> <script>
var editors = { var editors = {
comment: { defaultValue: '' } comment: { defaultValue: '' }
@ -183,6 +131,72 @@
</script> </script>
<% } %> <% } %>
</div> </div>
<script type="text/x-template" id="test-comment">
<div class="comment" :class="{ reply: data.firstComment, border:!data.parent_id }">
<a class="avatar">
<img :src="data.avatarUrl">
</a>
<div class="content">
<a class="author" :href="userUrl(data.user.id)" >{{data.user.username}}</a>
回复给
<a class="author" :href="userUrl(data.toUser.id)" >{{data.toUser.username}}</a>
<div v-if="data.user.nameplate">{{data.user.nameplate}}</div>
<div class="metadata">
<span class="date">{{data.date}}</span>
</div>
<div class="text font-content" style="min-height: 19.5px; ">{{data.content}}</div>
<div class="actions">
<template v-if="data.allowedEdit">
<a @click="deleteComment(data.id)">删除</a>
</template>
<template v-if="user">
<a @click="reply('reply', data.id)">回复</a>
</template>
</div>
</div>
<node v-for="(item, index) in data.children" :key="index" :data="item" :index="index" :user="user"></node>
</div>
</script>
<script>
var comments = <%- serializejs(comments) %>
var user = <%- serializejs(user) %>
var testComponent = Vue.component("comment-box", {
template: "#test-comment",
name: "Node",
props: {
data: {
type: Object,
default() {
return {};
}
},
index:{
type:Number,
default(){
return 0
}
},
user: {
type:Object,
}
},
computed:{
userUrl() {
return function(id){
return `user/${id}`
}
}
}
})
new Vue({
el: '#comment-zone',
data:{
comments:comments,
user
}
})
</script>
<% include footer %> <% include footer %>
<script> <script>
function reply(type, replyId){ function reply(type, replyId){
@ -204,5 +218,14 @@
editorBtn.innerText=btnText; editorBtn.innerText=btnText;
editorDom.style.visibility='initial'; editorDom.style.visibility='initial';
} }
document.body.scrollTo({
top: document.body.scrollHeight,
behavior: "smooth"
});
}
function deleteComment(id){
const articleId = <%= article.id %>;
$('#modal-comment-delete').modal('show')
$('#modal-comment-delete-href').attr('href-post',`/article/${articleId}/comment/${id}/delete`)
} }
</script> </script>

Loading…
Cancel
Save