965 lines
26 KiB
HTML
Executable File
965 lines
26 KiB
HTML
Executable File
{% extends "base.html" %}
|
|
|
|
{% block title %}Wiki 管理{% endblock %}
|
|
|
|
{% block head %}
|
|
<!-- 引入相关CSS和JS -->
|
|
<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/highlight/languages/javascript.min.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='libs/highlight/languages/python.min.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='libs/highlight/languages/bash.min.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='libs/highlight/languages/xml.min.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='libs/quill/quill.min.js') }}"></script>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
{% include 'admin_nav.html' %}
|
|
<div class="admin-wiki-container">
|
|
<h1 class="page-title">添加 Wiki 条目</h1>
|
|
|
|
<div class="add-entry-section">
|
|
<h2>添加新条目</h2>
|
|
<form id="addEntryForm" class="entry-form" enctype="multipart/form-data">
|
|
<div class="wiki-editor-container">
|
|
<div class="wiki-editor-sidebar">
|
|
<div class="form-group">
|
|
<label for="title">标题</label>
|
|
<input type="text" id="title" name="title" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="version">版本</label>
|
|
<input type="text" id="version" name="version" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>条目类型</label>
|
|
<select id="wiki-type" class="form-control" required>
|
|
<option value="">选择条目类型</option>
|
|
<!-- 动态加载的选项将在这里生成 -->
|
|
</select>
|
|
<div id="selected-category-display" class="mt-2 text-muted"></div>
|
|
</div>
|
|
</div>
|
|
<div class="wiki-editor-main">
|
|
<div class="form-group">
|
|
<label for="content">内容</label>
|
|
<div id="content-editor" class="editor"></div>
|
|
<input type="hidden" id="content" name="content">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit" class="btn-primary">添加条目</button>
|
|
<!-- 隐藏的分类字段 -->
|
|
<input type="hidden" id="first-level" name="first_level">
|
|
<input type="hidden" id="second-level" name="second_level">
|
|
<input type="hidden" id="third-level" name="third_level">
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
/* 新增样式 */
|
|
.wiki-editor-container {
|
|
display: flex;
|
|
gap: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.wiki-editor-sidebar {
|
|
width: 250px;
|
|
background: #f9fafb;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
border: 1px solid #e5e7eb;
|
|
}
|
|
|
|
.wiki-editor-main {
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.wiki-editor-sidebar .form-group {
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.wiki-editor-sidebar .form-group label {
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.wiki-editor-sidebar .form-group input,
|
|
.wiki-editor-sidebar .form-group select {
|
|
width: 100%;
|
|
padding: 8px;
|
|
border: 1px solid #d1d5db;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
/* 暗色模式适配 */
|
|
[data-theme="dark"] .wiki-editor-sidebar {
|
|
background: #1f2937;
|
|
border-color: #374151;
|
|
}
|
|
|
|
[data-theme="dark"] .wiki-editor-sidebar .form-group input,
|
|
[data-theme="dark"] .wiki-editor-sidebar .form-group select {
|
|
background: #111827;
|
|
border-color: #374151;
|
|
color: #f9fafb;
|
|
}
|
|
|
|
/* 移动端适配 */
|
|
@media (max-width: 768px) {
|
|
.wiki-editor-container {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.wiki-editor-sidebar {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
/* 容器布局 */
|
|
.admin-wiki-container {
|
|
margin-left: 250px;
|
|
padding: 20px;
|
|
min-height: 100vh;
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.admin-wiki-container {
|
|
margin-left: 0;
|
|
padding: 15px;
|
|
}
|
|
}
|
|
|
|
/* 二级导航栏样式 */
|
|
.wiki-header {
|
|
position: sticky;
|
|
top: 0;
|
|
background: rgba(255, 255, 255, 0.9);
|
|
backdrop-filter: blur(10px);
|
|
-webkit-backdrop-filter: blur(10px);
|
|
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
margin: -20px -20px 20px -20px;
|
|
padding: 15px 20px;
|
|
z-index: 100;
|
|
}
|
|
|
|
.header-content {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
}
|
|
|
|
.admin-title {
|
|
margin: 0;
|
|
font-size: 24px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
background: linear-gradient(120deg, #3b82f6, #2563eb);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
}
|
|
|
|
.wiki-nav {
|
|
display: flex;
|
|
gap: 20px;
|
|
border-bottom: 1px solid #eee;
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
.nav-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 8px 16px;
|
|
color: #666;
|
|
text-decoration: none;
|
|
font-weight: 500;
|
|
border-radius: 20px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.nav-item:hover {
|
|
color: #3b82f6;
|
|
background: rgba(59, 130, 246, 0.1);
|
|
}
|
|
|
|
.nav-item.active {
|
|
color: #3b82f6;
|
|
background: rgba(59, 130, 246, 0.1);
|
|
}
|
|
|
|
.nav-item i {
|
|
font-size: 16px;
|
|
}
|
|
|
|
/* 编辑器样式 */
|
|
.editor {
|
|
height: 400px;
|
|
margin-bottom: 20px;
|
|
background: #fff;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.ql-toolbar.ql-snow {
|
|
border-top-left-radius: 4px;
|
|
border-top-right-radius: 4px;
|
|
}
|
|
|
|
.ql-container.ql-snow {
|
|
border-bottom-left-radius: 4px;
|
|
border-bottom-right-radius: 4px;
|
|
}
|
|
|
|
/* 表单样式 */
|
|
.add-entry-section {
|
|
background: #fff;
|
|
padding: 30px;
|
|
border-radius: 10px;
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.add-entry-section h2 {
|
|
margin: 0 0 20px 0;
|
|
font-size: 20px;
|
|
color: #333;
|
|
}
|
|
|
|
.entry-form {
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.form-group label {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.form-group input[type="text"],
|
|
.form-group input[type="file"] {
|
|
width: 100%;
|
|
padding: 8px 12px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.admin-title {
|
|
margin: 0;
|
|
font-size: 24px;
|
|
color: #333;
|
|
}
|
|
|
|
.wiki-actions {
|
|
display: flex;
|
|
gap: 15px;
|
|
}
|
|
|
|
.btn-primary,
|
|
.btn-secondary {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 10px 20px;
|
|
border: none;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
text-decoration: none;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: #007AFF;
|
|
color: #fff;
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: #f8f9fa;
|
|
color: #333;
|
|
border: 1px solid #ddd;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
background: #0056b3;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: #e9ecef;
|
|
}
|
|
|
|
/* 暗色模式适配 */
|
|
[data-theme="dark"] .wiki-header {
|
|
background: rgba(26, 26, 26, 0.9);
|
|
border-bottom-color: rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
[data-theme="dark"] .wiki-nav {
|
|
border-bottom-color: #333;
|
|
}
|
|
|
|
[data-theme="dark"] .nav-item {
|
|
color: #999;
|
|
}
|
|
|
|
[data-theme="dark"] .nav-item:hover {
|
|
color: #3b82f6;
|
|
background: rgba(59, 130, 246, 0.15);
|
|
}
|
|
|
|
[data-theme="dark"] .nav-item.active {
|
|
color: #3b82f6;
|
|
background: rgba(59, 130, 246, 0.15);
|
|
}
|
|
|
|
/* 移动端适配 */
|
|
@media (max-width: 768px) {
|
|
.wiki-header {
|
|
position: static;
|
|
margin: -15px -15px 15px -15px;
|
|
padding: 15px;
|
|
}
|
|
|
|
.header-content {
|
|
gap: 10px;
|
|
}
|
|
|
|
.admin-title {
|
|
font-size: 20px;
|
|
}
|
|
|
|
.wiki-nav {
|
|
gap: 10px;
|
|
}
|
|
|
|
.nav-item {
|
|
padding: 6px 12px;
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
|
|
.page-title {
|
|
margin: 0 0 30px 0;
|
|
font-size: 24px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
}
|
|
|
|
.entry-count {
|
|
font-size: 14px;
|
|
color: #666;
|
|
font-weight: normal;
|
|
}
|
|
|
|
[data-theme="dark"] .page-title {
|
|
color: #fff;
|
|
}
|
|
|
|
[data-theme="dark"] .entry-count {
|
|
color: #999;
|
|
}
|
|
|
|
.image-upload-container {
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.image-upload-area {
|
|
border: 2px dashed #e5e7eb;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
position: relative;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.image-upload-area:hover {
|
|
border-color: #3b82f6;
|
|
background: #f8fafc;
|
|
}
|
|
|
|
.image-input {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
opacity: 0;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.upload-placeholder {
|
|
color: #6b7280;
|
|
}
|
|
|
|
.upload-placeholder i {
|
|
font-size: 32px;
|
|
margin-bottom: 10px;
|
|
color: #9ca3af;
|
|
}
|
|
|
|
.upload-placeholder p {
|
|
margin: 5px 0;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.upload-hint {
|
|
font-size: 14px;
|
|
color: #9ca3af;
|
|
}
|
|
|
|
.image-preview-container {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|
gap: 16px;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.preview-item {
|
|
position: relative;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
aspect-ratio: 1;
|
|
background: #f8fafc;
|
|
border: 1px solid #e5e7eb;
|
|
}
|
|
|
|
.preview-item img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
display: block;
|
|
}
|
|
|
|
.preview-remove {
|
|
position: absolute;
|
|
top: 8px;
|
|
right: 8px;
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 12px;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
color: white;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
z-index: 10;
|
|
}
|
|
|
|
.preview-remove:hover {
|
|
background: rgba(0, 0, 0, 0.7);
|
|
transform: scale(1.1);
|
|
}
|
|
|
|
/* 暗色模式适配 */
|
|
[data-theme="dark"] .image-upload-area {
|
|
border-color: #374151;
|
|
}
|
|
|
|
[data-theme="dark"] .image-upload-area:hover {
|
|
border-color: #3b82f6;
|
|
background: #1f2937;
|
|
}
|
|
|
|
[data-theme="dark"] .preview-item {
|
|
background: #1f2937;
|
|
border-color: #374151;
|
|
}
|
|
|
|
[data-theme="dark"] .upload-placeholder {
|
|
color: #9ca3af;
|
|
}
|
|
|
|
[data-theme="dark"] .upload-placeholder i {
|
|
color: #6b7280;
|
|
}
|
|
|
|
.custom-modal {
|
|
position: fixed;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
background: white;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
z-index: 1000;
|
|
max-width: 400px;
|
|
width: 90%;
|
|
text-align: center;
|
|
}
|
|
|
|
.custom-modal-overlay {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0,0,0,0.5);
|
|
z-index: 999;
|
|
display: none;
|
|
}
|
|
|
|
.custom-modal-content {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.custom-modal-actions {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
[data-theme="dark"] .custom-modal {
|
|
background: #2c2c2c;
|
|
color: white;
|
|
}
|
|
</style>
|
|
|
|
<div id="customModalOverlay" class="custom-modal-overlay">
|
|
<div class="custom-modal">
|
|
<div id="customModalContent" class="custom-modal-content"></div>
|
|
<div class="custom-modal-actions">
|
|
<button id="customModalConfirm" class="btn-primary">确定</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
console.log('=== 页面加载完成,开始初始化分类选择 ===');
|
|
|
|
// 获取关键元素
|
|
const wikiTypeSelect = document.getElementById('wiki-type');
|
|
const selectedCategoryDisplay = document.getElementById('selected-category-display');
|
|
const firstLevelInput = document.getElementById('first-level');
|
|
const secondLevelInput = document.getElementById('second-level');
|
|
const thirdLevelInput = document.getElementById('third-level');
|
|
|
|
// 详细的元素存在性检查
|
|
console.log('元素检查:', {
|
|
wikiTypeSelect: !!wikiTypeSelect,
|
|
selectedCategoryDisplay: !!selectedCategoryDisplay,
|
|
firstLevelInput: !!firstLevelInput,
|
|
secondLevelInput: !!secondLevelInput,
|
|
thirdLevelInput: !!thirdLevelInput
|
|
});
|
|
|
|
// 如果任何关键元素缺失,立即退出
|
|
if (!wikiTypeSelect || !selectedCategoryDisplay ||
|
|
!firstLevelInput || !secondLevelInput || !thirdLevelInput) {
|
|
console.error('未找到必要的页面元素,分类加载终止');
|
|
return;
|
|
}
|
|
|
|
// 分类加载函数
|
|
async function loadWikiCategories() {
|
|
try {
|
|
const response = await fetch('/admin/wiki/categories', {
|
|
headers: {
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.error('获取分类失败:', response.statusText);
|
|
return;
|
|
}
|
|
|
|
const categories = await response.json();
|
|
console.log('获取到的分类数据:', categories);
|
|
|
|
// 按一级分类分组
|
|
const groupedCategories = {};
|
|
categories.forEach(category => {
|
|
const { first_level, second_level, third_level } = category;
|
|
|
|
if (!groupedCategories[first_level]) {
|
|
groupedCategories[first_level] = {};
|
|
}
|
|
|
|
if (!groupedCategories[first_level][second_level]) {
|
|
groupedCategories[first_level][second_level] = [];
|
|
}
|
|
|
|
groupedCategories[first_level][second_level].push(third_level);
|
|
});
|
|
|
|
const wikiTypeSelect = document.getElementById('wiki-type');
|
|
wikiTypeSelect.innerHTML = ''; // 清空现有选项
|
|
|
|
// 动态生成分类选项
|
|
Object.entries(groupedCategories).forEach(([firstLevel, secondLevels]) => {
|
|
const firstLevelOptgroup = document.createElement('optgroup');
|
|
firstLevelOptgroup.label = firstLevel;
|
|
|
|
Object.entries(secondLevels).forEach(([secondLevel, thirdLevels]) => {
|
|
thirdLevels.forEach(thirdLevel => {
|
|
const option = document.createElement('option');
|
|
option.value = `${firstLevel} - ${secondLevel} - ${thirdLevel}`;
|
|
option.textContent = `${firstLevel} - ${secondLevel} - ${thirdLevel}`;
|
|
firstLevelOptgroup.appendChild(option);
|
|
});
|
|
});
|
|
|
|
wikiTypeSelect.appendChild(firstLevelOptgroup);
|
|
});
|
|
|
|
console.log('分类数据处理完成');
|
|
} catch (error) {
|
|
console.error('加载分类时发生错误:', error);
|
|
}
|
|
}
|
|
|
|
// 处理分类选择的函数
|
|
function handleCategorySelection() {
|
|
console.log('handleCategorySelection 被调用');
|
|
|
|
// 确保选择了有效选项
|
|
if (wikiTypeSelect.selectedIndex < 0) {
|
|
console.warn('未选择有效选项');
|
|
|
|
selectedCategoryDisplay.textContent = '请选择分类';
|
|
|
|
// 清空隐藏字段
|
|
firstLevelInput.value = '';
|
|
secondLevelInput.value = '';
|
|
thirdLevelInput.value = '';
|
|
|
|
selectedCategoryDisplay.style.color = '#6b7280';
|
|
return;
|
|
}
|
|
|
|
const selectedOption = wikiTypeSelect.options[wikiTypeSelect.selectedIndex];
|
|
console.log('选中的选项:', selectedOption);
|
|
|
|
// 安全地解析选择的分类
|
|
try {
|
|
// 直接使用 value 属性,不再假设使用下划线分隔
|
|
const categoryParts = selectedOption.value.split(' - ');
|
|
|
|
// 检查分类是否完整
|
|
if (categoryParts.length !== 3) {
|
|
throw new Error('分类格式不正确');
|
|
}
|
|
|
|
const [firstLevel, secondLevel, thirdLevel] = categoryParts;
|
|
console.log('解析后的分类:', { firstLevel, secondLevel, thirdLevel });
|
|
|
|
// 验证分类不为空
|
|
if (!firstLevel || !secondLevel || !thirdLevel) {
|
|
throw new Error('分类不能为空');
|
|
}
|
|
|
|
// 更新隐藏字段
|
|
firstLevelInput.value = firstLevel;
|
|
secondLevelInput.value = secondLevel;
|
|
thirdLevelInput.value = thirdLevel;
|
|
|
|
// 更新显示的分类信息
|
|
const displayText = `${firstLevel} - ${secondLevel} - ${thirdLevel}`;
|
|
selectedCategoryDisplay.textContent = displayText;
|
|
|
|
// 样式增强
|
|
selectedCategoryDisplay.style.color = '#3b82f6';
|
|
selectedCategoryDisplay.style.fontWeight = '600';
|
|
selectedCategoryDisplay.style.fontSize = '0.875rem';
|
|
|
|
} catch (error) {
|
|
console.error('分类选择错误:', error);
|
|
|
|
// 重置显示
|
|
selectedCategoryDisplay.textContent = '分类选择错误';
|
|
selectedCategoryDisplay.style.color = '#dc3545';
|
|
|
|
// 清空隐藏字段
|
|
firstLevelInput.value = '';
|
|
secondLevelInput.value = '';
|
|
thirdLevelInput.value = '';
|
|
}
|
|
}
|
|
|
|
// 添加 change 事件监听器
|
|
wikiTypeSelect.addEventListener('change', handleCategorySelection);
|
|
|
|
// 调用分类加载函数
|
|
loadWikiCategories();
|
|
|
|
// 额外的调试函数
|
|
function debugCategoryLoading() {
|
|
console.log('=== 分类加载调试 ===');
|
|
fetch('/admin/wiki/categories', {
|
|
method: 'GET',
|
|
headers: {
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
'Accept': 'application/json'
|
|
}
|
|
})
|
|
.then(response => {
|
|
console.log('完整响应对象:', response);
|
|
console.log('响应状态:', response.status);
|
|
console.log('响应状态文本:', response.statusText);
|
|
|
|
// 记录所有响应头
|
|
console.log('响应头:');
|
|
for (let [key, value] of response.headers.entries()) {
|
|
console.log(`${key}: ${value}`);
|
|
}
|
|
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
console.log('=== 分类数据详细信息 ===');
|
|
console.log('是否成功:', data.success);
|
|
console.log('分类总数:', data.total_count);
|
|
console.log('分类详情:', data.categories);
|
|
})
|
|
.catch(error => {
|
|
console.error('分类加载调试失败:', error);
|
|
});
|
|
}
|
|
|
|
// 立即执行调试
|
|
debugCategoryLoading();
|
|
});
|
|
|
|
// 在页面加载时,默认选择第一个选项
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const wikiTypeSelect = document.getElementById('wiki-type');
|
|
if (wikiTypeSelect && wikiTypeSelect.options.length > 0) {
|
|
// 选择第一个 option
|
|
wikiTypeSelect.selectedIndex = 0;
|
|
// 触发 change 事件以更新分类显示
|
|
wikiTypeSelect.dispatchEvent(new Event('change'));
|
|
}
|
|
});
|
|
|
|
// 自定义弹窗函数
|
|
function showCustomModal(options) {
|
|
const overlay = document.getElementById('customModalOverlay');
|
|
const content = document.getElementById('customModalContent');
|
|
const confirmBtn = document.getElementById('customModalConfirm');
|
|
|
|
// 设置内容
|
|
content.innerHTML = `
|
|
<h3>${options.title || '提示'}</h3>
|
|
<p>${options.text || ''}</p>
|
|
`;
|
|
|
|
// 设置样式
|
|
content.parentElement.style.backgroundColor = options.type === 'success' ? '#e6f3e6' : '#f8d7da';
|
|
confirmBtn.style.backgroundColor = options.type === 'success' ? '#28a745' : '#dc3545';
|
|
|
|
// 显示弹窗
|
|
overlay.style.display = 'block';
|
|
|
|
// 确认按钮事件
|
|
const handleConfirm = () => {
|
|
overlay.style.display = 'none';
|
|
confirmBtn.removeEventListener('click', handleConfirm);
|
|
|
|
// 执行关闭回调
|
|
if (options.didClose) {
|
|
options.didClose();
|
|
}
|
|
};
|
|
|
|
confirmBtn.addEventListener('click', handleConfirm);
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Quill 工具栏配置
|
|
const toolbarOptions = [
|
|
['bold', 'italic', 'underline', 'strike'],
|
|
['blockquote', 'code-block'],
|
|
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
|
|
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
|
|
[{ 'script': 'sub'}, { 'script': 'super' }],
|
|
[{ 'indent': '-1'}, { 'indent': '+1' }],
|
|
[{ 'direction': 'rtl' }],
|
|
[{ 'color': [] }, { 'background': [] }],
|
|
[{ 'font': [] }],
|
|
[{ 'align': [] }],
|
|
['clean'],
|
|
['link', 'image', 'video']
|
|
];
|
|
|
|
// 确保编辑器容器存在
|
|
const editorContainer = document.getElementById('content-editor');
|
|
const hiddenContentInput = document.getElementById('content');
|
|
|
|
if (!editorContainer || !hiddenContentInput) {
|
|
console.error('富文本编辑器的必要元素未找到');
|
|
return;
|
|
}
|
|
|
|
// 初始化 Quill 编辑器
|
|
const editor = new Quill('#content-editor', {
|
|
modules: {
|
|
toolbar: {
|
|
container: toolbarOptions,
|
|
handlers: {
|
|
// 自定义链接处理
|
|
link: function(value) {
|
|
if (value) {
|
|
const range = this.quill.getSelection();
|
|
if (range) {
|
|
let url = prompt('请输入链接URL:');
|
|
if (url) {
|
|
if (!/^https?:\/\//i.test(url)) {
|
|
url = 'http://' + url;
|
|
}
|
|
this.quill.format('link', url);
|
|
}
|
|
}
|
|
} else {
|
|
this.quill.format('link', false);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
syntax: {
|
|
highlight: (text) => hljs.highlightAuto(text).value
|
|
}
|
|
},
|
|
theme: 'snow'
|
|
});
|
|
|
|
// 同步编辑器内容到隐藏输入框
|
|
editor.on('text-change', function() {
|
|
// 将编辑器内容转换为 JSON 格式
|
|
const content = editor.getContents();
|
|
hiddenContentInput.value = JSON.stringify(content);
|
|
});
|
|
|
|
// 调试:检查编辑器是否正确初始化
|
|
console.log('Quill 编辑器初始化:', {
|
|
editor: !!editor,
|
|
container: editorContainer,
|
|
hiddenInput: hiddenContentInput
|
|
});
|
|
|
|
// 可选:如果需要预填充内容
|
|
try {
|
|
// 尝试从隐藏输入框读取初始内容
|
|
const initialContent = hiddenContentInput.value;
|
|
if (initialContent) {
|
|
const parsedContent = JSON.parse(initialContent);
|
|
editor.setContents(parsedContent);
|
|
}
|
|
} catch (error) {
|
|
console.warn('初始内容解析失败:', error);
|
|
}
|
|
});
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// 表单提交处理
|
|
const addEntryForm = document.getElementById('addEntryForm');
|
|
|
|
if (!addEntryForm) {
|
|
console.error('未找到表单元素 #addEntryForm');
|
|
return;
|
|
}
|
|
|
|
addEntryForm.addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
|
|
// 获取表单元素
|
|
const titleInput = document.getElementById('title');
|
|
const versionInput = document.getElementById('version');
|
|
const contentInput = document.getElementById('content');
|
|
const firstLevelInput = document.getElementById('first-level');
|
|
const secondLevelInput = document.getElementById('second-level');
|
|
const thirdLevelInput = document.getElementById('third-level');
|
|
|
|
// 验证表单数据
|
|
if (!titleInput || !versionInput || !contentInput ||
|
|
!firstLevelInput || !secondLevelInput || !thirdLevelInput) {
|
|
console.error('表单元素缺失');
|
|
showCustomModal({
|
|
title: '错误',
|
|
text: '表单元素未正确加载,请刷新页面',
|
|
type: 'error'
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 获取表单数据
|
|
const formData = {
|
|
title: titleInput.value.trim(),
|
|
version: versionInput.value.trim(),
|
|
content: contentInput.value,
|
|
first_level: firstLevelInput.value,
|
|
second_level: secondLevelInput.value,
|
|
third_level: thirdLevelInput.value
|
|
};
|
|
|
|
// 数据验证
|
|
const validationErrors = [];
|
|
if (!formData.title) validationErrors.push('标题不能为空');
|
|
if (!formData.version) validationErrors.push('版本不能为空');
|
|
if (!formData.content) validationErrors.push('内容不能为空');
|
|
if (!formData.first_level || !formData.second_level || !formData.third_level) {
|
|
validationErrors.push('请选择完整的分类');
|
|
}
|
|
|
|
if (validationErrors.length > 0) {
|
|
showCustomModal({
|
|
title: '表单验证错误',
|
|
text: validationErrors.join('<br>'),
|
|
type: 'error'
|
|
});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('/admin/wiki/add', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(formData)
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
showCustomModal({
|
|
title: '成功',
|
|
text: 'Wiki 条目添加成功',
|
|
type: 'success',
|
|
didClose: () => {
|
|
// 可选:重置表单或跳转
|
|
addEntryForm.reset();
|
|
window.location.href = '/admin/wiki';
|
|
}
|
|
});
|
|
} else {
|
|
showCustomModal({
|
|
title: '错误',
|
|
text: result.error || '添加 Wiki 条目失败',
|
|
type: 'error'
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('提交表单时发生错误:', error);
|
|
showCustomModal({
|
|
title: '网络错误',
|
|
text: '无法提交表单,请检查网络连接',
|
|
type: 'error'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %} |