This commit is contained in:
2025-11-25 17:25:40 +08:00
parent f093791c14
commit 833d125f64
56 changed files with 9428 additions and 4334 deletions
Regular → Executable
+28 -7
View File
@@ -3,6 +3,23 @@
include 'views/layouts/header.php';
?>
<h1>Создание новой книги</h1>
<?php if (isset($_SESSION['error'])): ?>
<div class="alert alert-error">
<?= e($_SESSION['error']) ?>
<?php unset($_SESSION['error']); ?>
</div>
<?php endif; ?>
<?php if (isset($error) && $error): ?>
<div class="alert alert-error">
<?= e($error) ?>
</div>
<?php endif; ?>
<?php if (isset($cover_error) && $cover_error): ?>
<div class="alert alert-error">
Ошибка загрузки обложки: <?= e($cover_error) ?>
</div>
<?php endif; ?>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<div style="max-width: 100%; margin-bottom: 0.5rem;">
@@ -21,13 +38,7 @@ include 'views/layouts/header.php';
value="<?= e($_POST['genre'] ?? '') ?>"
placeholder="Например: Фантастика, Роман, Детектив..."
style="width: 100%; margin-bottom: 1.5rem;">
<label for="editor_type" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Режим редактора
</label>
<select id="editor_type" name="editor_type" style="width: 100%; margin-bottom: 1.5rem;">
<option value="markdown" <?= ($_POST['editor_type'] ?? 'markdown') == 'markdown' ? 'selected' : '' ?>>Markdown редактор</option>
<option value="html" <?= ($_POST['editor_type'] ?? '') == 'html' ? 'selected' : '' ?>>HTML редактор (TinyMCE)</option>
</select>
<label for="series_id" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Серия
</label>
@@ -46,6 +57,16 @@ include 'views/layouts/header.php';
placeholder="Краткое описание сюжета или аннотация..."
rows="6"
style="width: 100;"><?= e($_POST['description'] ?? '') ?></textarea>
<div style="margin-bottom: 1rem;">
<label for="cover_image" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Обложка книги
</label>
<input type="file" id="cover_image" name="cover_image"
accept="image/jpeg,image/png,image/gif,image/webp">
<small style="color: var(--muted-color);">
Разрешены форматы: JPG, PNG, GIF, WebP. Максимальный размер: 5MB.
</small>
</div>
<div style="margin-top: 1rem;">
<label for="published">
<input type="checkbox" id="published" name="published" value="1"
Regular → Executable
+7 -36
View File
@@ -2,6 +2,12 @@
// views/books/edit.php
include 'views/layouts/header.php';
?>
<?php if (isset($_SESSION['cover_error'])): ?>
<div class="alert alert-error">
Ошибка загрузки обложки: <?= e($_SESSION['cover_error']) ?>
<?php unset($_SESSION['cover_error']); ?>
</div>
<?php endif; ?>
<h1>Редактирование книги</h1>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
@@ -21,19 +27,6 @@ include 'views/layouts/header.php';
value="<?= e($book['genre'] ?? '') ?>"
placeholder="Например: Фантастика, Роман, Детектив..."
style="width: 100%; margin-bottom: 1.5rem;">
<label for="editor_type" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Режим редактора
</label>
<select id="editor_type" name="editor_type" style="width: 100%; margin-bottom: 1.5rem;" onchange="showEditorWarning(this)">
<?php foreach ($editor_types as $type => $label): ?>
<option value="<?= e($type) ?>" <?= ($book['editor_type'] ?? 'markdown') == $type ? 'selected' : '' ?>>
<?= e($label) ?>
</option>
<?php endforeach; ?>
</select>
<div id="editor_warning" style="display: none; background: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px; margin-bottom: 1rem;">
<strong>Внимание:</strong> При смене редактора содержимое всех глав будет автоматически сконвертировано в новый формат.
</div>
<label for="series_id" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Серия
</label>
@@ -111,15 +104,6 @@ include 'views/layouts/header.php';
</form>
<?php if ($book): ?>
<div style="margin-top: 2rem;">
<form method="post" action="<?= SITE_URL ?>/books/<?= $book['id'] ?>/normalize" onsubmit="return confirm('Нормализовать контент всех глав книги? Это действие нельзя отменить.')">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<button type="submit" class="button secondary">🔄 Нормализовать контент глав</button>
<p style="margin-top: 0.5rem; font-size: 0.8em; color: var(--muted-color);">
Если контент глав отображается неправильно после смены редактора, можно нормализовать его структуру.
</p>
</form>
</div>
<div style="margin-top: 2rem; padding: 1rem; background: var(--card-background-color); border-radius: 5px;">
<h3>Публичная ссылка для чтения</h3>
<div style="display: flex; gap: 5px; align-items: center; flex-wrap: wrap;">
@@ -224,22 +208,9 @@ include 'views/layouts/header.php';
<?php endif; ?>
<script>
function showEditorWarning(select) {
const warning = document.getElementById('editor_warning');
const currentEditor = '<?= $book['editor_type'] ?? 'markdown' ?>';
if (select.value !== currentEditor) {
warning.style.display = 'block';
} else {
warning.style.display = 'none';
}
}
document.addEventListener('DOMContentLoaded', function() {
const currentEditor = '<?= $book['editor_type'] ?? 'markdown' ?>';
const selectedEditor = document.getElementById('editor_type').value;
if (currentEditor !== selectedEditor) {
document.getElementById('editor_warning').style.display = 'block';
}
// Копирование ссылки для чтения
window.copyShareLink = function() {
Regular → Executable
+97 -41
View File
@@ -3,11 +3,14 @@
include 'views/layouts/header.php';
?>
<h1>Мои книги</h1>
<h1>Мои книги <small style="color: #ccc; font-size:1rem;">(Всего книг: <?= count($books) ?>)</small></h1>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; flex-wrap: wrap; gap: 1rem;">
<h2 style="margin: 0;">Всего книг: <?= count($books) ?></h2>
<div style="display: flex; justify-content: right; align-items: center; margin-bottom: 1rem; flex-wrap: wrap; gap: 1rem;">
<a href="<?= SITE_URL ?>/books/create" class="action-button primary"> Новая книга</a>
<?php if (!empty($books)): ?>
<a href="#" onclick="showDeleteAllConfirmation()" class="action-button delete">🗑️ Удалить все книги</a>
<?php endif; ?>
</div>
<?php if (empty($books)): ?>
@@ -17,50 +20,103 @@ include 'views/layouts/header.php';
<a href="<?= SITE_URL ?>/books/create" role="button">📖 Создать первую книгу</a>
</article>
<?php else: ?>
<div class="grid">
<div class="books-grid">
<?php foreach ($books as $book): ?>
<article>
<header>
<h3 style="margin-bottom: 0.5rem;">
<?= e($book['title']) ?>
<div style="float: right; display: flex; gap: 3px;">
<a href="<?= SITE_URL ?>/books/<?= $book['id'] ?>/edit" class="compact-button secondary" title="Редактировать книгу">
✏️
<article class="book-card">
<!-- Обложка книги -->
<div class="book-cover-container">
<?php if (!empty($book['cover_image'])): ?>
<img src="<?= COVERS_URL . e($book['cover_image']) ?>"
alt="<?= e($book['title']) ?>"
class="book-cover"
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
<div class="cover-placeholder" style="display: none;">
📚
</div>
<?php else: ?>
<div class="cover-placeholder">
📚
</div>
<?php endif; ?>
<!-- Статус книги -->
<div class="book-status <?= $book['published'] ? 'published' : 'draft' ?>">
<?= $book['published'] ? '✅' : '📝' ?>
</div>
</div>
<!-- Информация о книге -->
<div class="book-info">
<h3 class="book-title">
<a href="<?= SITE_URL ?>/books/<?= $book['id'] ?>/edit">
<?= e($book['title']) ?>
</a>
<a href="<?= SITE_URL ?>/book/<?= $book['share_token'] ?>" class="compact-button secondary" title="Просмотреть книгу" target="_blank">
👁️
</h3>
<?php if (!empty($book['genre'])): ?>
<p class="book-genre"><?= e($book['genre']) ?></p>
<?php endif; ?>
<?php if (!empty($book['description'])): ?>
<p class="book-description">
<?= e(mb_strimwidth($book['description'], 0, 120, '...')) ?>
</p>
<?php endif; ?>
<!-- Статистика -->
<div class="book-stats">
<span class="stat-item">
<strong><?= $book['chapter_count'] ?? 0 ?></strong> глав
</span>
<span class="stat-item">
<strong><?= number_format($book['total_words'] ?? 0) ?></strong> слов
</span>
</div>
<!-- Действия -->
<div class="book-actions">
<a href="<?= SITE_URL ?>/books/<?= $book['id'] ?>/edit" class="compact-button primary-btn">
✏️ Редактировать
</a>
<a href="<?= SITE_URL ?>/books/<?= $book['id'] ?>/chapters" class="compact-button secondary-btn">
📑 Главы
</a>
<a href="<?= SITE_URL ?>/book/<?= $book['share_token'] ?>" class="compact-button secondary-btn" target="_blank">
👁️ Просмотр
</a>
</div>
</h3>
<?php if ($book['genre']): ?>
<p style="margin: 0; color: var(--muted-color);"><em><?= e($book['genre']) ?></em></p>
<?php endif; ?>
</header>
<?php if ($book['description']): ?>
<p><?= e(mb_strimwidth($book['description'], 0, 200, '...')) ?></p>
<?php endif; ?>
<footer>
<div>
<small>
Глав: <?= $book['chapter_count'] ?> |
Слов: <?= $book['total_words'] ?> |
Статус: <?= $book['published'] ? '✅ Опубликована' : '📝 Черновик' ?>
</small>
</div>
<div style="margin-top: 0.5rem; display: flex; gap: 5px; flex-wrap: wrap;">
<a href="<?= SITE_URL ?>/books/<?= $book['id'] ?>/chapters" class="adaptive-button secondary">
📑 Главы
</a>
<a href="<?= SITE_URL ?>/export/<?= $book['id'] ?>" class="adaptive-button secondary" target="_blank">
📄 Экспорт
</a>
</div>
</footer>
</article>
</article>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- Статистика внизу -->
<div class="books-stats-footer">
<strong>Общая статистика:</strong>
Книг: <?= count($books) ?> |
Глав: <?= array_sum(array_column($books, 'chapter_count')) ?> |
Слов: <?= number_format(array_sum(array_column($books, 'total_words'))) ?> |
Опубликовано: <?= count(array_filter($books, function($book) { return $book['published']; })) ?>
</div>
<?php endif; ?>
<?php if (!empty($books)): ?>
<script>
function showDeleteAllConfirmation() {
if (confirm('Вы уверены, что хотите удалить ВСЕ книги? Это действие также удалит все главы и обложки книг. Действие нельзя отменить!')) {
const form = document.createElement('form');
form.method = 'POST';
form.action = '<?= SITE_URL ?>/books/delete-all';
const csrfInput = document.createElement('input');
csrfInput.type = 'hidden';
csrfInput.name = 'csrf_token';
csrfInput.value = '<?= generate_csrf_token() ?>';
form.appendChild(csrfInput);
document.body.appendChild(form);
form.submit();
}
}
</script>
<?php endif; ?>
<?php include 'views/layouts/footer.php'; ?>
Regular → Executable
View File