426 lines
21 KiB
Plaintext
426 lines
21 KiB
Plaintext
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>主题详情页</title>
|
|
<!-- <link rel="stylesheet" type="text/css" href="static/semanticui/semantic.css"> -->
|
|
<!-- <script src="static/jquery/jquery-3.4.1.min.js"></script> -->
|
|
<!-- <script src="static/semanticui/semantic.min.js"></script> -->
|
|
<!-- default header name is X-CSRF-TOKEN -->
|
|
<meta name="_csrf" content="${_csrf.token}"/>
|
|
<meta name="_csrf_header" content="${_csrf.headerName}"/>
|
|
|
|
<#include "comm/commjs.ftl"/>
|
|
</head>
|
|
<body style=" background: #f7f8fa;">
|
|
<#include "comm/header.ftl"/>
|
|
|
|
<div class="ui container" id="container">
|
|
<div class="ui info message">
|
|
<i class="close icon"></i>
|
|
<div class="header">欢迎来到这里</div>
|
|
<ul class="list">
|
|
<li>JAVA + DISCUSS = JISCUSS 。</li>
|
|
<li>简单的,易用的JAVA论坛。</li>
|
|
<li>请享受发帖回帖带来的乐趣吧!</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<h3 class="ui center aligned header"></h3>
|
|
<div class="ui four column grid">
|
|
<div class="ui widescreen large screen computer tablet only twelve wide column">
|
|
<div id="context2" style="padding-left: 14px; padding-right: 14px;background: #FFFFFF; padding: 1em 1em; border-radius: 0.28571429rem; border: 1px solid rgba(34, 36, 38, 0.15);">
|
|
<h2 class="ui header">
|
|
<i class="edit outline icon"></i>
|
|
<div class="content">
|
|
${discussions.title}
|
|
<#list tags as tag>
|
|
<div class="sub header">${tag.name}</div>
|
|
</#list>
|
|
</div>
|
|
</h2>
|
|
|
|
|
|
<div class="ui main text container">
|
|
${discussions.content}
|
|
</div>
|
|
|
|
<div class="ui labeled button" tabindex="0" style=" margin-top: 30px;">
|
|
<div class="ui red button" id="likeBtn" onclick="voteDiscussion('like')">
|
|
<i class="heart icon"></i> 赞这个主题
|
|
</div>
|
|
<a class="ui basic red left pointing label" id="likeCount">
|
|
${discussions.likeCount!0}
|
|
</a>
|
|
</div>
|
|
<div class="ui labeled button" tabindex="0" style="margin-left:8px; margin-top: 30px;">
|
|
<div class="ui grey button" id="dislikeBtn" onclick="voteDiscussion('dislike')">
|
|
<i class="thumbs down icon"></i> 踩
|
|
</div>
|
|
<a class="ui basic grey left pointing label" id="dislikeCount">
|
|
${discussions.dislikeCount!0}
|
|
</a>
|
|
</div>
|
|
<span style="margin-left:12px; color:#888; font-size:0.9em;">
|
|
<i class="eye icon"></i> ${discussions.viewCount!0} 浏览
|
|
<i class="comment icon"></i> ${discussions.commentsCount!0} 回复
|
|
</span>
|
|
|
|
|
|
<div class="ui feed">
|
|
<div class="event">
|
|
<div class="label">
|
|
<img src="/static/assets/images/logo.png">
|
|
</div>
|
|
<div class="content">
|
|
<div class="summary">
|
|
用户 <a>${discussions.realname}</a> 发布于
|
|
<div class="date">
|
|
${discussions.startTime}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="ui feed">
|
|
<div class="event">
|
|
<div class="label">
|
|
<img src="/static/assets/images/logo.png">
|
|
</div>
|
|
<div class="content">
|
|
<div class="summary">
|
|
用户 <a>${discussions.realnameLast}</a> 最后回复于
|
|
<div class="date">
|
|
${discussions.lastTime}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ui threaded comments">
|
|
<h3 class="ui dividing header">
|
|
评论区
|
|
<div class="ui mini buttons" style="float:right; font-size:0.75em;">
|
|
<a class="ui button <#if sort == 'oldest'>active</#if>" href="?id=${discussions.id}&sort=oldest">最早</a>
|
|
<a class="ui button <#if sort == 'newest'>active</#if>" href="?id=${discussions.id}&sort=newest">最新</a>
|
|
<a class="ui button <#if sort == 'hot'>active</#if>" href="?id=${discussions.id}&sort=hot">最热</a>
|
|
</div>
|
|
</h3>
|
|
<input type="hidden" name="postId" id="postId" value=""/>
|
|
|
|
<!-- 定义遍历方法 -->
|
|
<#macro bpTree posts>
|
|
<#if posts?? && posts?size gt 0> <!-- 判断是否为空,并且长度是否大于0 -->
|
|
<!-- 不为空 开始遍历 -->
|
|
<#list posts as post>
|
|
<#if post.nextNodes?? && post.nextNodes?size gt 0>
|
|
<!-- 如果是拥有子节点 -->
|
|
<div class="comment">
|
|
<a class="avatar">
|
|
<img src="/static/assets/images/logo.png">
|
|
</a>
|
|
<div class="content">
|
|
<a class="author user-card-trigger" data-user-id="${post.createId!''}">${post.username}</a>
|
|
<span style="color:#aaa; font-size:0.8em; margin-left:6px;">#${post.number!''}</span>
|
|
<div class="metadata">
|
|
<span class="date">${post.createTime}</span>
|
|
<#if post.ipRegion?? && post.ipRegion != ""><span style="margin-left:6px;"><i class="map marker alternate icon"></i>${post.ipRegion}</span></#if>
|
|
<#if post.browser?? && post.browser != ""><span style="margin-left:6px;"><i class="chrome icon"></i>${post.browser}</span></#if>
|
|
</div>
|
|
<div class="text">
|
|
${post.content}
|
|
</div>
|
|
<div class="actions">
|
|
<a class="reply" onclick="replyThis('${post.username}', '${post.id}')">回复</a>
|
|
<a onclick="votePost(${post.id}, 'like')" style="cursor:pointer;"><i class="thumbs up outline icon"></i>${(post.likeCount!0)?c}</a>
|
|
<a onclick="votePost(${post.id}, 'dislike')" style="cursor:pointer;"><i class="thumbs down outline icon"></i>${(post.dislikeCount!0)?c}</a>
|
|
</div>
|
|
</div>
|
|
<div class="comments">
|
|
<@bpTree posts=post.nextNodes />
|
|
</div>
|
|
</div>
|
|
<#else>
|
|
<!-- 没有子节点! -->
|
|
<div class="comment">
|
|
<a class="avatar">
|
|
<img src="/static/assets/images/logo.png">
|
|
</a>
|
|
<div class="content">
|
|
<a class="author user-card-trigger" data-user-id="${post.createId!''}">${post.username}</a>
|
|
<span style="color:#aaa; font-size:0.8em; margin-left:6px;">#${post.number!''}</span>
|
|
<div class="metadata">
|
|
<span class="date">${post.createTime}</span>
|
|
<#if post.ipRegion?? && post.ipRegion != ""><span style="margin-left:6px;"><i class="map marker alternate icon"></i>${post.ipRegion}</span></#if>
|
|
<#if post.browser?? && post.browser != ""><span style="margin-left:6px;"><i class="chrome icon"></i>${post.browser}</span></#if>
|
|
</div>
|
|
<div class="text">
|
|
${post.content}
|
|
</div>
|
|
<div class="actions">
|
|
<a class="reply" onclick="replyThis('${post.username}', '${post.id}')">回复</a>
|
|
<a onclick="votePost(${post.id}, 'like')" style="cursor:pointer;"><i class="thumbs up outline icon"></i>${(post.likeCount!0)?c}</a>
|
|
<a onclick="votePost(${post.id}, 'dislike')" style="cursor:pointer;"><i class="thumbs down outline icon"></i>${(post.dislikeCount!0)?c}</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</#if>
|
|
</#list>
|
|
</#if>
|
|
</#macro>
|
|
<@bpTree posts=posts /> <!-- 调用方法 -->
|
|
|
|
<div class="ui">
|
|
|
|
</div>
|
|
<form class="ui reply form">
|
|
<div class="field">
|
|
<textarea id="postContent"></textarea>
|
|
</div>
|
|
<div class="ui blue labeled submit icon button" id="addPost">
|
|
<i class="icon edit"></i> 添加评论
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="widescreen large screen computer tablet only four wide column">
|
|
<form action="/search" method="get" class="ui fluid action input">
|
|
<input type="text" name="q" placeholder="搜索主题..." id="sidebarSearch">
|
|
<button type="submit" class="ui button">搜索</button>
|
|
</form>
|
|
|
|
<div class="ui section divider"></div>
|
|
|
|
<div class="ui card">
|
|
<div class="content">
|
|
<div class="header">作者</div>
|
|
</div>
|
|
<div class="content">
|
|
<h4 class="ui sub header">
|
|
<a class="user-card-trigger" data-user-id="${discussions.startUserId!''}">${discussions.realname!''}</a>
|
|
</h4>
|
|
<div class="ui small statistics" style="margin-top:8px;">
|
|
<div class="statistic">
|
|
<div class="value">${authorDiscCount!0}</div>
|
|
<div class="label">发帖</div>
|
|
</div>
|
|
<div class="statistic">
|
|
<div class="value">${authorReplyCount!0}</div>
|
|
<div class="label">回复</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<#if authorTopDiscs?? && authorTopDiscs?size gt 0>
|
|
<div class="ui card">
|
|
<div class="content">
|
|
<div class="header">作者其他主题</div>
|
|
</div>
|
|
<div class="content" style="padding:0;">
|
|
<div class="ui middle aligned divided list">
|
|
<#list authorTopDiscs as d>
|
|
<#if d.id != discussions.id>
|
|
<div class="item" style="padding:8px 12px;">
|
|
<div class="content">
|
|
<a class="header" href="/getdiscussionsbyid?id=${d.id}" style="font-size:.9em;font-weight:normal;">
|
|
${(d.title?length > 30)?then(d.title?substring(0,30) + '…', d.title)}
|
|
</a>
|
|
<div class="description" style="font-size:.8em;color:#999;">
|
|
<i class="heart icon"></i>${d.likeCount!0}
|
|
<i class="comment icon"></i>${d.commentsCount!0}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</#if>
|
|
</#list>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</#if>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mobile only sixteen wide column">
|
|
<div id="context2" style="padding: 1em; background: #fff; border-radius: 0.28571429rem; border: 1px solid rgba(34, 36, 38, 0.15);">
|
|
<h3 class="ui header"><i class="edit outline icon"></i>${discussions.title}</h3>
|
|
<div class="ui main text container">${discussions.content}</div>
|
|
<div style="margin-top:1em; color:#888; font-size:0.85em;">
|
|
<i class="eye icon"></i> ${discussions.viewCount!0} 浏览
|
|
<i class="comment icon"></i> ${discussions.commentsCount!0} 回复
|
|
</div>
|
|
<div class="ui threaded comments" style="margin-top:1em;">
|
|
<h4 class="ui dividing header">评论区</h4>
|
|
<@bpTree posts=posts />
|
|
<form class="ui reply form" style="margin-top:1em;">
|
|
<div class="field"><textarea id="postContentMobile"></textarea></div>
|
|
<div class="ui blue labeled submit icon button" onclick="addPostMobile()">
|
|
<i class="icon edit"></i> 添加评论
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
<#include "comm/footer.ftl"/>
|
|
|
|
<script type="text/javascript">
|
|
|
|
$(document).ready(function () {
|
|
<#if username??>
|
|
setusername('${username}');
|
|
console.log('已经登陆:' + username);
|
|
</#if>
|
|
setDiscussionsId('${discussions.id}');
|
|
|
|
// User card — custom fixed-position tooltip (more reliable than Semantic UI popup)
|
|
var cardCache = {};
|
|
var $card = $('<div id="jUserCard" style="position:fixed;background:#fff;border:1px solid #ddd;border-radius:6px;padding:12px 16px;box-shadow:0 4px 16px rgba(0,0,0,.15);z-index:9999;min-width:180px;display:none;"></div>').appendTo('body');
|
|
|
|
function showCard(u, x, y) {
|
|
$card.html(
|
|
'<div style="font-weight:700;font-size:1em;margin-bottom:4px;">' + (u.username || '') + '</div>' +
|
|
'<div style="color:#888;font-size:.88em;">发帖: ' + (u.discussionsCount || 0) +
|
|
' 回复: ' + (u.commentsCount || 0) + '</div>' +
|
|
'<div style="margin-top:8px;display:flex;gap:12px;">' +
|
|
'<a href="/profile?uid=' + u.id + '" style="font-size:.85em;color:#2185d0;"><i class="user icon"></i>主页</a>' +
|
|
'<a class="open-msg-modal" data-uid="' + u.id + '" data-uname="' + (u.username||'') + '" style="font-size:.85em;color:#2185d0;cursor:pointer;"><i class="envelope icon"></i>发私信</a>' +
|
|
'</div>'
|
|
).css({ left: x + 14, top: y + 14 }).show();
|
|
}
|
|
|
|
$(document).on('mouseenter', '.user-card-trigger', function(e) {
|
|
var userId = $(this).data('user-id');
|
|
if (!userId) return;
|
|
if (cardCache[userId]) { showCard(cardCache[userId], e.clientX, e.clientY); return; }
|
|
$.getJSON('/msg_api/user-card/' + userId, function(res) {
|
|
if (res && res.code === 10000 && res.data) {
|
|
cardCache[userId] = res.data;
|
|
showCard(res.data, e.clientX, e.clientY);
|
|
}
|
|
});
|
|
}).on('mousemove', '.user-card-trigger', function(e) {
|
|
if ($card.is(':visible')) $card.css({ left: e.clientX + 14, top: e.clientY + 14 });
|
|
}).on('mouseleave', '.user-card-trigger', function() {
|
|
// Delay hide so user can click links inside card
|
|
setTimeout(function() {
|
|
if (!$card.is(':hover')) $card.hide();
|
|
}, 300);
|
|
});
|
|
$card.on('mouseleave', function() { $card.hide(); });
|
|
});
|
|
|
|
function voteDiscussion(action) {
|
|
var token = $("meta[name='_csrf']").attr("content");
|
|
var header = $("meta[name='_csrf_header']").attr("content");
|
|
$.ajax({
|
|
url: '/api/discussions/${discussions.id}/vote?action=' + action,
|
|
type: 'POST',
|
|
beforeSend: function(xhr) { xhr.setRequestHeader(header, token); },
|
|
success: function(res) {
|
|
if (res.code === 10000) {
|
|
location.reload();
|
|
} else {
|
|
alert(res.msg || '操作失败,请先登录');
|
|
}
|
|
},
|
|
error: function() { alert('请先登录'); }
|
|
});
|
|
}
|
|
|
|
function votePost(postId, action) {
|
|
var token = $("meta[name='_csrf']").attr("content");
|
|
var header = $("meta[name='_csrf_header']").attr("content");
|
|
$.ajax({
|
|
url: '/api/posts/' + postId + '/vote?action=' + action,
|
|
type: 'POST',
|
|
beforeSend: function(xhr) { xhr.setRequestHeader(header, token); },
|
|
success: function(res) {
|
|
if (res.code === 10000) {
|
|
location.reload();
|
|
} else {
|
|
alert(res.msg || '操作失败,请先登录');
|
|
}
|
|
},
|
|
error: function() { alert('请先登录'); }
|
|
});
|
|
}
|
|
|
|
</script>
|
|
<script type="text/javascript" charset="UTF-8" src="/static/js/user/discussions.js"></script>
|
|
|
|
<#-- 私信弹窗 Modal -->
|
|
<div class="ui small modal" id="sendMsgModal">
|
|
<div class="header"><i class="envelope icon"></i> 发送私信给 <span id="msgModalUsername"></span></div>
|
|
<div class="content">
|
|
<div class="ui form">
|
|
<div class="field">
|
|
<textarea id="msgModalContent" rows="4" placeholder="输入消息内容..." style="resize:none;"></textarea>
|
|
</div>
|
|
<div id="msgModalError" class="ui red message" style="display:none;"></div>
|
|
</div>
|
|
</div>
|
|
<div class="actions">
|
|
<div class="ui cancel button">取消</div>
|
|
<div class="ui primary button" id="msgModalSendBtn">
|
|
<i class="send icon"></i>发送
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
var _msgReceiverId = null;
|
|
var _msgCsrfToken = $('meta[name="_csrf"]').attr('content');
|
|
var _msgCsrfHeader = $('meta[name="_csrf_header"]').attr('content');
|
|
|
|
// Open modal when user clicks 发私信 in user card
|
|
$(document).on('click', '.open-msg-modal', function() {
|
|
_msgReceiverId = $(this).data('uid');
|
|
var uname = $(this).data('uname');
|
|
$('#msgModalUsername').text(uname);
|
|
$('#msgModalContent').val('');
|
|
$('#msgModalError').hide();
|
|
$('#jUserCard').hide();
|
|
$('#sendMsgModal').modal('show');
|
|
});
|
|
|
|
$('#msgModalSendBtn').on('click', function() {
|
|
var content = $('#msgModalContent').val().trim();
|
|
if (!content) { $('#msgModalError').text('消息内容不能为空').show(); return; }
|
|
var $btn = $(this).addClass('loading disabled');
|
|
var headers = {};
|
|
headers[_msgCsrfHeader] = _msgCsrfToken;
|
|
$.ajax({
|
|
url: '/msg_api/messages/send',
|
|
type: 'POST',
|
|
headers: headers,
|
|
data: { receiverId: _msgReceiverId, content: content },
|
|
success: function(resp) {
|
|
$btn.removeClass('loading disabled');
|
|
if (resp && resp.code === 10000) {
|
|
$('#sendMsgModal').modal('hide');
|
|
massage('私信发送成功!', 'success', '');
|
|
} else {
|
|
$('#msgModalError').text((resp && resp.msg) || '发送失败,请重试').show();
|
|
}
|
|
},
|
|
error: function() {
|
|
$btn.removeClass('loading disabled');
|
|
$('#msgModalError').text('请求失败,请确认已登录').show();
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|