Files
ns2.0/templates/wiki_detail.html

838 lines
22 KiB
HTML
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% block title %}{{ entry.title }} - Wiki{% endblock %}
{% block head %}
{{ super() }}
<script>
window.entryContent = {{ entry.content|tojson|safe }};
window.entryId = {{ entry.id }};
window.isLoggedIn = {{ 'true' if session.get('huawei_user') else 'false' }};
</script>
<script src="{{ url_for('static', filename='js/wiki_detail.js') }}"></script>
<script src="{{ url_for('static', filename='js/wiki_comments.js') }}"></script>
<link href="{{ url_for('static', filename='libs/quill/quill.snow.css') }}" rel="stylesheet">
<script src="{{ url_for('static', filename='libs/highlight/highlight.min.js') }}"></script>
<script src="{{ url_for('static', filename='libs/quill/quill.min.js') }}"></script>
<link href="{{ url_for('static', filename='libs/lightbox2/css/lightbox.min.css') }}" rel="stylesheet">
<!--<script src="{{ url_for('static', filename='libs/lightbox2/js/lightbox-plus-jquery.min.js') }}"></script>-->
<!--<script src="{{ url_for('static', filename='libs/moment/moment.min.js') }}"></script>-->
<!--<script src="{{ url_for('static', filename='libs/moment/moment-timezone.min.js') }}"></script>-->
<style>
:root {
/* 浅色模式配色 */
--primary-color: #3b82f6;
--secondary-color: #60a5fa;
--text-primary: #0f172a;
--text-secondary: #64748b;
--background-primary: #ffffff;
--background-secondary: #f8fafc;
--border-color: #e2e8f0;
}
[data-theme="dark"] {
/* 深色模式配色 */
--primary-color: #4b5563;
--secondary-color: #374151;
--text-primary: #f9fafb;
--text-secondary: #9ca3af;
--background-primary: #1a1a1a;
--background-secondary: #1f2937;
--border-color: #374151;
}
.wiki-container {
max-width: 1200px;
margin: 0 auto;
}
.wiki-main {
background-color: var(--background-primary);
padding: 2.5rem;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
position: relative;
}
.back-link {
position: absolute;
top: 20px;
left: 20px;
display: flex;
align-items: center;
gap: 0.75rem;
color: #3b82f6;
text-decoration: none;
font-weight: 600;
transition: all 0.2s ease;
font-size: 0.875rem;
z-index: 10;
}
.back-link:hover {
color: #2563eb;
transform: translateX(-4px);
}
.wiki-article {
position: relative;
max-width: 800px;
margin: 0 auto;
}
.article-header {
margin-bottom: 0.5rem;
padding-bottom: 0.2rem;
border-bottom: 1px solid var(--border-color);
}
.article-meta {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
color: var(--text-secondary);
}
.meta-tag {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
background-color: rgba(59, 130, 246, 0.1);
color: var(--primary-color);
}
.article-title {
font-size: 2.5rem;
font-weight: 700;
line-height: 1.2;
color: var(--text-primary);
}
.article-content {
font-size: 1rem;
line-height: 1.8;
color: var(--text-primary);
}
.content-viewer {
margin-bottom: 1.5rem;
}
.content-images {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 0.5rem;
margin-top: 1rem;
width: 100%;
}
.content-images img {
width: 100%;
max-width: 100%;
max-height: 300px;
object-fit: contain;
display: block;
margin: 0 auto;
transition: transform 0.3s ease;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
cursor: pointer;
}
.content-images img:hover {
transform: scale(1.02);
}
/* 确保图片不超过 ql-editor 容器 */
.ql-editor img {
max-width: 100% !important;
height: auto !important;
object-fit: contain !important;
}
.version-list-header {
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
padding: 0.5rem 0;
border-bottom: 1px solid var(--border-color);
}
.version-list {
max-height: 300px;
overflow-y: auto;
transition: all 0.3s ease;
}
.version-list.collapsed {
max-height: 0;
overflow: hidden;
opacity: 0;
}
.version-item {
display: block;
padding: 0.5rem 0;
text-decoration: none;
color: var(--text-secondary);
transition: all 0.3s ease;
}
.version-item:hover {
color: var(--primary-color);
}
.version-item.active {
color: var(--primary-color);
font-weight: bold;
}
/* 暗色模式适配 */
[data-theme="dark"] .wiki-main {
background-color: var(--background-primary);
box-shadow: 0 4px 15px rgba(255,255,255,0.05);
}
[data-theme="dark"] .article-header {
border-bottom-color: var(--border-color);
}
[data-theme="dark"] .article-title {
color: var(--text-primary);
}
[data-theme="dark"] .article-content {
color: var(--text-primary);
}
[data-theme="dark"] .version-list-header {
border-bottom-color: var(--border-color);
}
[data-theme="dark"] .version-item {
color: var(--text-secondary);
}
[data-theme="dark"] .version-item:hover {
color: var(--secondary-color);
}
[data-theme="dark"] .version-item.active {
color: var(--secondary-color);
}
/* 响应式设计 */
@media (max-width: 768px) {
.wiki-main {
padding: 1.5rem;
}
.article-title {
font-size: 2rem;
}
.back-link {
position: static;
margin-bottom: 1rem;
}
.wiki-article {
max-width: 100%;
}
}
/* 评论区样式优化 */
.wiki-comments-section {
background-color: var(--background-primary);
border-radius: 16px;
padding: 0 0 1.5rem 0;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.comments-tabs {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color);
padding-bottom: 0.5rem;
}
.comments-tabs .tab {
background: none;
border: none;
font-weight: 500;
color: var(--text-secondary);
padding: 0.5rem 1rem;
position: relative;
cursor: pointer;
transition: all 0.3s ease;
}
.comments-tabs .tab.active {
color: var(--primary-color);
font-weight: 600;
}
.comments-tabs .tab::before {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 0;
height: 2px;
background-color: var(--primary-color);
transition: width 0.3s ease;
}
.comments-tabs .tab.active::before {
width: 100%;
}
.comment-composer {
margin-bottom: 1.5rem;
}
.comment-input-container {
display: flex;
align-items: flex-start;
gap: 1rem;
background-color: var(--background-secondary);
border-radius: 12px;
padding: 1rem;
transition: all 0.3s ease;
}
.comment-input-container:focus-within {
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
.comment-avatar {
width: 48px;
height: 48px;
border-radius: 50%;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--border-color);
}
.comment-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.comment-avatar i {
color: var(--text-secondary);
font-size: 1.5rem;
}
.comment-input-wrapper {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.comment-input-wrapper textarea {
width: 100%;
min-height: 60px;
max-height: 200px;
resize: none;
border: none;
background: transparent;
color: var(--text-primary);
font-size: 0.95rem;
line-height: 1.6;
overflow: hidden;
transition: all 0.3s ease;
}
.comment-input-wrapper textarea:focus {
outline: none;
}
.comment-input-wrapper #submit-comment {
align-self: flex-end;
margin-top: 0.5rem;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 8px;
padding: 0.5rem 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
transition: all 0.3s ease;
}
.comment-input-wrapper #submit-comment:hover {
background-color: var(--secondary-color);
}
.comment-input-wrapper #submit-comment:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.comments-list {
display: none;
}
.comments-list.active {
display: block;
}
.empty-comments {
text-align: center;
color: var(--text-secondary);
padding: 2rem 0;
background-color: var(--background-primary);
border-radius: 8px;
font-size: 0.875rem;
}
/* 暗色模式适配 */
[data-theme="dark"] .wiki-comments-section {
background-color: var(--background-secondary);
box-shadow: 0 4px 10px rgba(255,255,255,0.05);
}
[data-theme="dark"] .comments-tabs .tab {
color: var(--text-secondary);
}
[data-theme="dark"] .comments-tabs .tab.active {
color: var(--secondary-color);
}
[data-theme="dark"] .comments-tabs .tab.active::after {
background-color: var(--secondary-color);
}
[data-theme="dark"] .comment-input-container {
background-color: var(--background-primary);
}
[data-theme="dark"] .comment-avatar {
background-color: rgba(55, 65, 81, 0.2);
border-color: var(--secondary-color);
}
[data-theme="dark"] .comment-input-wrapper textarea {
background-color: var(--background-primary);
color: var(--text-primary);
border-color: var(--border-color);
}
[data-theme="dark"] .comment-input-wrapper textarea:focus {
border-color: var(--secondary-color);
}
[data-theme="dark"] .comment-input-wrapper #submit-comment {
background: var(--secondary-color);
}
[data-theme="dark"] .comment-input-wrapper #submit-comment:hover {
background: var(--primary-color);
}
[data-theme="dark"] .empty-comments {
background-color: var(--background-primary);
}
/* 响应式设计 */
@media (max-width: 768px) {
.comment-input-container {
flex-direction: column;
align-items: stretch;
}
.comment-avatar {
align-self: center;
margin-bottom: 1rem;
}
.comment-input-wrapper textarea {
min-height: 80px;
}
}
/* 开发提示弹窗样式 */
.developing-tip {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
padding: 10px 20px;
border-radius: 6px;
color: white;
font-size: 0.875rem;
z-index: 1000;
opacity: 0;
transition: opacity 0.3s ease, transform 0.3s ease;
max-width: 90%;
text-align: center;
}
.developing-tip.show {
opacity: 1;
transform: translate(-50%, -10px);
}
.developing-tip.toast-info {
background-color: rgba(59, 130, 246, 0.9);
}
.developing-tip.toast-success {
background-color: rgba(34, 197, 94, 0.9);
}
.developing-tip.toast-error {
background-color: rgba(244, 63, 94, 0.9);
}
.developing-tip.toast-warning {
background-color: rgba(245, 158, 11, 0.9);
}
/* Lightbox 导航按钮垂直居中 */
.lb-container .lb-nav {
top: 50% !important;
transform: translateY(-50%) !important;
width: 10% !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
.lb-container .lb-prev {
left: 0 !important;
margin-left: 20px !important;
}
.lb-container .lb-next {
right: 0 !important;
margin-right: 20px !important;
}
.lb-container .lb-nav a {
opacity: 0.6 !important;
transition: opacity 0.3s ease !important;
background: rgba(0,0,0,0.3) !important;
border-radius: 50% !important;
width: 50px !important;
height: 50px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
.lb-container .lb-nav a:hover {
opacity: 1 !important;
background: rgba(0,0,0,0.5) !important;
}
.lb-container .lb-nav a i {
font-size: 24px !important;
color: white !important;
}
</style>
{% endblock %}
{% block content %}
<div class="wiki-container">
<main class="wiki-main">
<a href="javascript:history.back()" class="back-link">
<i class="fas fa-arrow-left"></i>返回
</a>
<article class="wiki-article">
<!-- 文章头部 -->
<header class="article-header">
<div class="article-meta">
<span class="meta-tag">
<i class="fas fa-code-branch"></i>
{{ entry.version }}
</span>
<span class="meta-tag">
<i class="far fa-calendar-alt"></i>
{{ entry.created_at }}
</span>
</div>
<h1 class="article-title">{{ entry.title }}</h1>
</header>
<!-- 文章内容 -->
<div class="article-content">
<div id="content-viewer" class="content-viewer"></div>
{% if entry.images and entry.images|length > 0 %}
<div class="content-images" id="gallery">
{% for image in entry.images %}
<a href="{{ url_for('static', filename=image) }}"
data-lightbox="wiki-gallery"
data-title="{{ entry.title }}">
<img src="{{ url_for('static', filename=image) }}"
alt="{{ entry.title }}">
</a>
{% endfor %}
</div>
{% endif %}
<!-- 版本列表 -->
<!-- <div class="version-list-section">
<div class="version-list-header">
<h3>版本列表</h3>
<i class="fas fa-chevron-down"></i>
</div>
<div class="version-list">
{% for ver in all_entries %}
<a href="{{ url_for('wiki.wiki_detail', entry_id=ver.id) }}"
class="version-item {% if ver.id == entry.id %}active{% endif %}">
{{ ver.version }}
</a>
{% endfor %}
</div>
</div> -->
<!-- 评论区域 -->
<section class="wiki-comments-section">
<div class="comments-tabs">
<button class="tab active" data-tab="feature-requests" data-type="feature_request">功能建议</button>
<button class="tab" data-tab="bug-reports" data-type="bug_report">已知问题</button>
<button class="tab" data-tab="experience-share" data-type="experience_share">新增功能</button>
</div>
<div class="comment-composer">
<div class="comment-input-container">
<div class="comment-avatar">
{% if session.get('huawei_user') and session.huawei_user.avatar %}
<img src="{{ session.huawei_user.avatar }}" alt="{{ session.huawei_user.name }}">
{% else %}
<i class="fas fa-user"></i>
{% endif %}
</div>
<div class="comment-input-wrapper">
<textarea
id="comment-content"
placeholder="{% if session.get('huawei_user') %}{{ session.huawei_user.name }}{% endif %}分享你的想法..."
rows="1"
></textarea>
<button type="submit" id="submit-comment">
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
</div>
<div class="comments-content">
<div id="feature-requests" class="comments-list active">
<!-- 功能建议列表 -->
<div class="empty-comments">
<p>暂无功能建议,快来提出你的创意吧!</p>
</div>
</div>
<div id="bug-reports" class="comments-list">
<!-- 已知问题列表 -->
<div class="empty-comments">
<p>暂无已知问题,系统运行良好!</p>
</div>
</div>
<div id="experience-share" class="comments-list">
<!-- 新增功能列表 -->
<div class="empty-comments">
<p>还没有人分享新增功能,快来抢沙发!</p>
</div>
</div>
</div>
</section>
</div>
</article>
</main>
</div>
<!-- Developing Tip for Notifications -->
<div id="developingTip" class="developing-tip"></div>
{% endblock %}
{% block footer %}
<script>
function showDevelopingTip(message, type = 'info') {
const tip = document.getElementById('developingTip');
tip.textContent = message;
tip.className = `developing-tip toast-${type} show`;
setTimeout(() => {
tip.classList.remove('show');
}, 3000);
}
document.addEventListener('DOMContentLoaded', function() {
window.showDevelopingTip = showDevelopingTip;
// Lightbox2 配置
lightbox.option({
'resizeDuration': 200,
'wrapAround': true,
'disableScrolling': true,
'fadeDuration': 300,
'maxWidth': window.innerWidth * 0.9,
'maxHeight': window.innerHeight * 0.9
});
// 为 ql-editor 和 content-images 中的图片添加 Lightbox 功能
const imageSections = [
document.querySelector('.ql-editor'),
document.querySelector('.content-images')
];
imageSections.forEach(section => {
if (section) {
section.addEventListener('click', function(event) {
const img = event.target.closest('img');
if (img) {
// 查找最近的 a 标签
const link = img.closest('a') || document.createElement('a');
// 如果没有 a 标签,创建一个
if (!img.closest('a')) {
link.href = img.src;
link.setAttribute('data-lightbox', 'content-gallery');
link.style.display = 'none';
document.body.appendChild(link);
}
// 使用原生方法触发点击
const clickEvent = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
link.dispatchEvent(clickEvent);
// 如果是动态创建的 a 标签,用完即删
if (!img.closest('a')) {
document.body.removeChild(link);
}
}
});
}
});
// 移除自定义的 lightbox.start 方法
delete lightbox.start;
});
document.addEventListener('DOMContentLoaded', function() {
// 创建 Lightbox 控制按钮
function createLightboxControls() {
// 创建按钮容器
const controlsDiv = document.createElement('div');
controlsDiv.className = 'lightbox-custom-controls';
controlsDiv.innerHTML = `
<button class="lightbox-prev" title="上一张">
<i class="fas fa-chevron-left"></i>
</button>
<button class="lightbox-next" title="下一张">
<i class="fas fa-chevron-right"></i>
</button>
<button class="lightbox-close" title="关闭">
<i class="fas fa-times"></i>
</button>
`;
// 添加到文档
document.body.appendChild(controlsDiv);
// 上一张按钮事件
controlsDiv.querySelector('.lightbox-prev').addEventListener('click', function() {
const prevBtn = document.querySelector('.lb-container .lb-prev');
if (prevBtn) prevBtn.click();
});
// 下一张按钮事件
controlsDiv.querySelector('.lightbox-next').addEventListener('click', function() {
const nextBtn = document.querySelector('.lb-container .lb-next');
if (nextBtn) nextBtn.click();
});
// 关闭按钮事件
controlsDiv.querySelector('.lightbox-close').addEventListener('click', function() {
lightbox.end();
});
}
// 监听 Lightbox 打开事件
$(document).on('shown.lightbox', function() {
// 移除已存在的控制按钮
const existingControls = document.querySelector('.lightbox-custom-controls');
if (existingControls) existingControls.remove();
// 创建新的控制按钮
createLightboxControls();
});
// 监听 Lightbox 关闭事件
$(document).on('closed.lightbox', function() {
const controls = document.querySelector('.lightbox-custom-controls');
if (controls) controls.remove();
});
});
</script>
<style>
.lightbox-custom-controls {
position: fixed;
bottom: 50px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 20px;
z-index: 10001;
}
.lightbox-custom-controls button {
background-color: rgba(0,0,0,0.6);
color: white;
border: none;
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
}
.lightbox-custom-controls button:hover {
background-color: rgba(0,0,0,0.8);
transform: scale(1.1);
}
.lightbox-custom-controls button i {
font-size: 24px;
}
.lightbox-custom-controls .lightbox-close {
background-color: rgba(255,0,0,0.6);
}
.lightbox-custom-controls .lightbox-close:hover {
background-color: rgba(255,0,0,0.8);
}
</style>
{% endblock %}