初始化鸿蒙应用展示平台项目 - 前后端分离架构
This commit is contained in:
577
templates/admin_wiki_comments.html
Executable file
577
templates/admin_wiki_comments.html
Executable file
@@ -0,0 +1,577 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
{% include 'admin_nav.html' %}
|
||||
<div class="admin-container wiki-comments-review">
|
||||
<div class="admin-header">
|
||||
<h1>Wiki 评论审核</h1>
|
||||
<div class="header-actions">
|
||||
<div class="comments-filter">
|
||||
<select id="status-filter" class="form-select">
|
||||
<option value="all">全部状态</option>
|
||||
<option value="pending">待审核</option>
|
||||
<option value="approved">已通过</option>
|
||||
<option value="rejected">已拒绝</option>
|
||||
</select>
|
||||
<select id="type-filter" class="form-select">
|
||||
<option value="all">全部类型</option>
|
||||
<option value="feature_request">功能建议</option>
|
||||
<option value="bug_report">问题报告</option>
|
||||
<option value="experience_share">新增功能</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="debug-info">
|
||||
<p>总评论数: {{ comments|length }}</p>
|
||||
{% if comments %}
|
||||
<p>第一条评论详情:</p>
|
||||
<pre>{{ comments[0]|tojson(indent=2) }}</pre>
|
||||
{% endif %}
|
||||
</div> -->
|
||||
|
||||
<div class="comments-table-container">
|
||||
<table class="admin-table comments-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">ID</th>
|
||||
<th>Wiki 标题</th>
|
||||
<th>用户</th>
|
||||
<th>评论类型</th>
|
||||
<th class="content-column">内容</th>
|
||||
<th>状态</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="comments-list">
|
||||
{% for comment in comments %}
|
||||
<tr data-comment-id="{{ comment.id }}"
|
||||
data-status="{{ comment.status }}"
|
||||
data-type="{{ comment.comment_type }}"
|
||||
class="comment-row {% if comment.status == 'pending' %}pending-review{% endif %}">
|
||||
<td class="text-center">{{ comment.id }}</td>
|
||||
<td>
|
||||
<div class="wiki-title-cell">
|
||||
<span>{{ comment.wiki_title }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="user-cell">
|
||||
{% if comment.user_avatar %}
|
||||
<div class="user-avatar">
|
||||
<img src="{{ comment.user_avatar }}" alt="{{ comment.user_name }}">
|
||||
</div>
|
||||
{% endif %}
|
||||
<span class="user-name">{{ comment.user_name }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="comment-type-badge
|
||||
{% if comment.comment_type == 'feature_request' %}badge-feature
|
||||
{% elif comment.comment_type == 'bug_report' %}badge-bug
|
||||
{% else %}badge-experience{% endif %}">
|
||||
{% if comment.comment_type == 'feature_request' %}功能建议
|
||||
{% elif comment.comment_type == 'bug_report' %}问题报告
|
||||
{% else %}新增功能{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
<td class="content-column">
|
||||
<div class="content-wrapper">
|
||||
<div class="comment-content-preview" title="{{ comment.content }}">
|
||||
{{ comment.content }}
|
||||
</div>
|
||||
<span class="comment-expand-btn" style="display: none;">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="status-cell">
|
||||
{% if comment.status == 'pending' %}
|
||||
<span class="badge badge-warning">待审核</span>
|
||||
{% elif comment.status == 'approved' %}
|
||||
<span class="badge badge-success">已通过</span>
|
||||
{% else %}
|
||||
<span class="badge badge-danger">已拒绝</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ comment.created_at }}</td>
|
||||
<td>
|
||||
<div class="action-buttons">
|
||||
{% if comment.status == 'pending' %}
|
||||
<button class="btn btn-success approve-btn" title="通过">
|
||||
<i class="fas fa-check"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger reject-btn" title="拒绝">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
<button class="btn btn-secondary delete-btn" title="删除">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.comment-content-preview {
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
line-height: 1.4;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.comment-expand-btn {
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
color: var(--text-secondary);
|
||||
transition: transform 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.comment-expand-btn:hover {
|
||||
color: var(--primary-color);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.comment-content-full {
|
||||
display: none;
|
||||
width: 100%;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.comment-content-full.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 展开模式 */
|
||||
.comment-content-preview.expanded {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1000;
|
||||
background-color: var(--background-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
||||
max-width: 80vw;
|
||||
max-height: 70vh;
|
||||
width: 600px;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* 遮罩层 */
|
||||
.comment-preview-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
z-index: 999;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.comment-preview-overlay.show {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.expanded {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1000;
|
||||
background-color: var(--background-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
||||
max-width: 80vw;
|
||||
max-height: 70vh;
|
||||
width: 600px;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.expanded.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.expanded-comment-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.expanded-comment-close {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
color: var(--text-secondary);
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.expanded-comment-close:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.expanded-comment-content {
|
||||
line-height: 1.6;
|
||||
font-size: 16px;
|
||||
color: var(--text-primary);
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* 深色模式适配 */
|
||||
[data-theme="dark"] .comment-content-preview:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .comment-content-preview.expanded {
|
||||
background-color: var(--background-secondary);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.user-avatar img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.user-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.wiki-comments-review {
|
||||
background-color: var(--background-primary);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.admin-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
background-color: var(--background-secondary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.comments-table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0 0.5rem;
|
||||
}
|
||||
|
||||
.comments-table thead th {
|
||||
background-color: var(--background-secondary);
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.comment-row {
|
||||
background-color: var(--background-secondary);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.comment-row:hover {
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.pending-review {
|
||||
border-left: 4px solid #ffc107;
|
||||
}
|
||||
|
||||
.content-column {
|
||||
max-width: 200px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.comment-content-preview {
|
||||
flex-grow: 1;
|
||||
max-width: calc(100% - 30px); /* 为箭头预留空间 */
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.comment-expand-btn {
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
color: var(--text-secondary);
|
||||
transition: transform 0.3s ease, color 0.3s ease;
|
||||
display: none; /* 默认隐藏 */
|
||||
}
|
||||
|
||||
.comment-expand-btn:hover {
|
||||
color: var(--primary-color);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.comment-content-full {
|
||||
display: none;
|
||||
width: 100%;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.comment-content-full.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.comment-type-badge {
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.badge-feature {
|
||||
background-color: rgba(59, 130, 246, 0.1);
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.badge-bug {
|
||||
background-color: rgba(239, 68, 68, 0.1);
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.action-buttons .btn {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.action-buttons .btn i {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .wiki-comments-review {
|
||||
background-color: var(--background-secondary);
|
||||
box-shadow: 0 4px 6px rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.badge-experience {
|
||||
background-color: rgba(52, 211, 153, 0.1);
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.comments-filter {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* 删除按钮样式 */
|
||||
.action-buttons .delete-btn {
|
||||
background-color: rgba(220, 53, 69, 0.1);
|
||||
color: #dc3545;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.action-buttons .delete-btn:hover {
|
||||
background-color: rgba(220, 53, 69, 0.2);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .action-buttons .delete-btn {
|
||||
background-color: rgba(220, 53, 69, 0.2);
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .action-buttons .delete-btn:hover {
|
||||
background-color: rgba(220, 53, 69, 0.3);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const commentsList = document.getElementById('comments-list');
|
||||
|
||||
// 审核和删除操作
|
||||
commentsList.addEventListener('click', function(event) {
|
||||
const approveBtn = event.target.closest('.approve-btn');
|
||||
const rejectBtn = event.target.closest('.reject-btn');
|
||||
const deleteBtn = event.target.closest('.delete-btn');
|
||||
|
||||
if (approveBtn || rejectBtn || deleteBtn) {
|
||||
// 找到最近的 tr 行
|
||||
const row = event.target.closest('tr');
|
||||
|
||||
// 获取评论ID
|
||||
const commentId = row.getAttribute('data-comment-id');
|
||||
|
||||
let url, method;
|
||||
if (approveBtn) {
|
||||
url = `/admin/wiki/comment/${commentId}/approve`;
|
||||
method = '通过';
|
||||
} else if (rejectBtn) {
|
||||
url = `/admin/wiki/comment/${commentId}/reject`;
|
||||
method = '拒绝';
|
||||
} else if (deleteBtn) {
|
||||
url = `/admin/wiki/comment/${commentId}/delete`;
|
||||
method = '删除';
|
||||
}
|
||||
|
||||
// 确认删除操作
|
||||
if (deleteBtn && !confirm(`确定要${method}这条评论吗?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
if (approveBtn) {
|
||||
const statusCell = row.querySelector('.status-cell');
|
||||
statusCell.innerHTML = '<span class="badge badge-success">已通过</span>';
|
||||
} else if (rejectBtn) {
|
||||
const statusCell = row.querySelector('.status-cell');
|
||||
statusCell.innerHTML = '<span class="badge badge-danger">已拒绝</span>';
|
||||
} else if (deleteBtn) {
|
||||
// 删除整行
|
||||
row.remove();
|
||||
}
|
||||
|
||||
// 移除操作按钮
|
||||
const actionButtons = row.querySelector('.action-buttons');
|
||||
if (actionButtons && (approveBtn || rejectBtn)) {
|
||||
actionButtons.remove();
|
||||
}
|
||||
} else {
|
||||
alert(result.error || `${method}操作失败`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert(`${method}操作失败,请重试`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 原有的内容预览和筛选代码保持不变
|
||||
const contentPreviews = document.querySelectorAll('.content-wrapper');
|
||||
|
||||
contentPreviews.forEach(wrapper => {
|
||||
const preview = wrapper.querySelector('.comment-content-preview');
|
||||
const expandBtn = wrapper.querySelector('.comment-expand-btn');
|
||||
|
||||
const fullContent = preview.textContent.trim();
|
||||
|
||||
// 如果文本超过单元格宽度,显示展开箭头
|
||||
if (preview.scrollWidth > preview.clientWidth) {
|
||||
expandBtn.style.display = 'inline-block';
|
||||
}
|
||||
|
||||
const previewText = fullContent.length > 50
|
||||
? fullContent.substring(0, 50) + '...'
|
||||
: fullContent;
|
||||
|
||||
// 创建完整内容容器
|
||||
const fullContentDiv = document.createElement('div');
|
||||
fullContentDiv.classList.add('comment-content-full');
|
||||
fullContentDiv.textContent = fullContent;
|
||||
wrapper.parentNode.appendChild(fullContentDiv);
|
||||
|
||||
// 添加展开/收起事件
|
||||
expandBtn.addEventListener('click', function(e) {
|
||||
e.stopPropagation();
|
||||
fullContentDiv.classList.toggle('show');
|
||||
|
||||
// 旋转箭头
|
||||
expandBtn.querySelector('i').style.transform =
|
||||
fullContentDiv.classList.contains('show')
|
||||
? 'rotate(90deg)'
|
||||
: 'rotate(0deg)';
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user