| {% extends 'base.html' %} |
|
|
| {% block title %}Мои файлы{% endblock %} |
|
|
| {% block content %} |
| <div class="row mb-4"> |
| <div class="col-md-8"> |
| <nav aria-label="breadcrumb" class="breadcrumb-container"> |
| <ol class="breadcrumb"> |
| {% for path, name in breadcrumbs %} |
| <li class="breadcrumb-item {% if loop.last %}active{% endif %}"> |
| {% if not loop.last %} |
| <a href="{{ url_for('dashboard', folder=path) }}">{{ name }}</a> |
| {% else %} |
| {{ name }} |
| {% endif %} |
| </li> |
| {% endfor %} |
| </ol> |
| </nav> |
| </div> |
| <div class="col-md-4"> |
| <form action="{{ url_for('search') }}" method="get" class="search-form"> |
| <div class="search-container"> |
| <input type="text" name="query" class="form-control search-input" placeholder="Поиск файлов..."> |
| <button type="submit" class="search-button"><i class="fas fa-search"></i></button> |
| </div> |
| </form> |
| </div> |
| </div> |
|
|
| <div class="row mb-4"> |
| <div class="col-md-12"> |
| <div class="card"> |
| <div class="card-header bg-primary text-white d-flex justify-content-between align-items-center"> |
| <h5 class="mb-0">Управление файлами</h5> |
| <div> |
| <button type="button" class="btn btn-light btn-sm" data-bs-toggle="modal" data-bs-target="#uploadModal"> |
| <i class="fas fa-upload"></i> Загрузить файл |
| </button> |
| <button type="button" class="btn btn-light btn-sm ms-2" data-bs-toggle="modal" data-bs-target="#createFolderModal"> |
| <i class="fas fa-folder-plus"></i> Создать папку |
| </button> |
| </div> |
| </div> |
| <div class="card-body"> |
| {% if files %} |
| <div class="table-responsive"> |
| <table class="table table-hover"> |
| <thead> |
| <tr> |
| <th>Имя</th> |
| <th>Тип</th> |
| <th>Размер</th> |
| <th>Дата создания</th> |
| <th>Действия</th> |
| </tr> |
| </thead> |
| <tbody> |
| {% for file in files %} |
| <tr> |
| <td> |
| {% if file.is_folder %} |
| <a href="{{ url_for('dashboard', folder=file.filename) }}" class="text-decoration-none"> |
| <i class="fas fa-folder text-warning"></i> {{ file.original_filename }} |
| </a> |
| {% else %} |
| {% if file.file_type in ['image', 'video', 'audio'] %} |
| <a href="#" class="text-decoration-none" data-bs-toggle="modal" data-bs-target="#mediaModal" |
| data-file-type="{{ file.file_type }}" |
| data-file-path="{{ url_for('download_file', file_id=file.id) }}" |
| data-file-name="{{ file.original_filename }}"> |
| <i class="fas fa-file text-primary"></i> {{ file.original_filename }} |
| </a> |
| {% else %} |
| <i class="fas fa-file text-primary"></i> {{ file.original_filename }} |
| {% endif %} |
| {% endif %} |
| </td> |
| <td> |
| {% if file.is_folder %} |
| Папка |
| {% else %} |
| {% if file.file_type == 'image' %} |
| <i class="fas fa-image text-info"></i> Изображение |
| {% elif file.file_type == 'video' %} |
| <i class="fas fa-video text-danger"></i> Видео |
| {% elif file.file_type == 'audio' %} |
| <i class="fas fa-music text-success"></i> Аудио |
| {% elif file.file_type == 'text' %} |
| <i class="fas fa-file-alt text-secondary"></i> Текст |
| {% else %} |
| <i class="fas fa-file text-secondary"></i> Файл |
| {% endif %} |
| {% endif %} |
| </td> |
| <td>{{ file.size|filesizeformat if not file.is_folder else '-' }}</td> |
| <td>{{ file.created_at }}</td> |
| <td> |
| <div class="d-flex gap-2"> |
| {% if not file.is_folder %} |
| <a href="{{ url_for('download_file', file_id=file.id) }}" class="btn btn-sm btn-primary rounded-circle" title="Скачать"> |
| <i class="fas fa-download"></i> |
| </a> |
| {% if file.file_type in ['text', 'code', 'document'] or file.original_filename.endswith(('.txt', '.html', '.css', '.js', '.py', '.md', '.json', '.xml', '.csv')) %} |
| <button class="btn btn-sm btn-info rounded-circle" title="Редактировать" onclick="openTextEditor('{{ file.id }}', '{{ file.original_filename }}')"> |
| <i class="fas fa-edit"></i> |
| </button> |
| {% endif %} |
| <a href="{{ url_for('share_file', file_id=file.id) }}" class="btn btn-sm btn-success rounded-circle" title="Поделиться"> |
| <i class="fas fa-share-alt"></i> |
| </a> |
| {% endif %} |
| <form action="{{ url_for('delete_file', file_id=file.id) }}" method="post" class="d-inline" onsubmit="return confirm('Вы уверены, что хотите удалить этот элемент?');"> |
| <button type="submit" class="btn btn-sm btn-danger rounded-circle" title="Удалить"> |
| <i class="fas fa-trash"></i> |
| </button> |
| </form> |
| </div> |
| </td> |
| </tr> |
| {% endfor %} |
| </tbody> |
| </table> |
| </div> |
| {% else %} |
| <div class="text-center py-5"> |
| <i class="fas fa-folder-open fa-4x text-muted mb-3"></i> |
| <h5>Эта папка пуста</h5> |
| <p>Загрузите файлы или создайте новую папку</p> |
| </div> |
| {% endif %} |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="modal fade" id="uploadModal" tabindex="-1" aria-hidden="true"> |
| <div class="modal-dialog"> |
| <div class="modal-content"> |
| <div class="modal-header"> |
| <h5 class="modal-title">Загрузить файл</h5> |
| <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
| </div> |
| <form action="{{ url_for('upload_file') }}" method="post" enctype="multipart/form-data"> |
| <div class="modal-body"> |
| <input type="hidden" name="current_folder" value="{{ current_folder }}"> |
| <div class="mb-3"> |
| <label for="file" class="form-label">Выберите файл</label> |
| <input type="file" class="form-control" id="file" name="files[]" multiple required> |
| </div> |
| </div> |
| <div class="modal-footer"> |
| <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button> |
| <button type="submit" class="btn btn-primary">Загрузить</button> |
| </div> |
| </form> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="modal fade" id="createFolderModal" tabindex="-1" aria-hidden="true"> |
| <div class="modal-dialog"> |
| <div class="modal-content"> |
| <div class="modal-header"> |
| <h5 class="modal-title">Создать папку</h5> |
| <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
| </div> |
| <form action="{{ url_for('create_folder') }}" method="post"> |
| <div class="modal-body"> |
| <input type="hidden" name="current_folder" value="{{ current_folder }}"> |
| <div class="mb-3"> |
| <label for="folder_name" class="form-label">Имя папки</label> |
| <input type="text" class="form-control" id="folder_name" name="folder_name" required> |
| </div> |
| </div> |
| <div class="modal-footer"> |
| <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button> |
| <button type="submit" class="btn btn-primary">Создать</button> |
| </div> |
| </form> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="modal fade" id="editModal" tabindex="-1"> |
| <div class="modal-dialog modal-xl"> |
| <div class="modal-content"> |
| <div class="modal-header"> |
| <h5 class="modal-title">Редактор: <span id="editFileName"></span></h5> |
| <button type="button" class="btn-close" data-bs-dismiss="modal"></button> |
| </div> |
| <div class="modal-body"> |
| <textarea id="editorContent" class="form-control font-monospace" rows="20"></textarea> |
| </div> |
| <div class="modal-footer"> |
| <button type="button" class="btn btn-primary" onclick="saveChanges()">Сохранить</button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="modal fade" id="mediaModal" tabindex="-1" aria-hidden="true"> |
| <div class="modal-dialog modal-lg"> |
| <div class="modal-content"> |
| <div class="modal-header"> |
| <h5 class="modal-title">Просмотр файла: <span id="mediaFileName"></span></h5> |
| <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
| </div> |
| <div class="modal-body text-center"> |
| |
| <img id="imageViewer" class="img-fluid d-none" alt="Просмотр изображения"> |
| |
| |
| <video id="videoPlayer" class="d-none w-100" controls> |
| Ваш браузер не поддерживает воспроизведение видео. |
| </video> |
| |
| |
| <audio id="audioPlayer" class="d-none w-100" controls> |
| Ваш браузер не поддерживает воспроизведение аудио. |
| </audio> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| const mediaModal = document.getElementById('mediaModal'); |
| mediaModal.addEventListener('show.bs.modal', function(event) { |
| const button = event.relatedTarget; |
| const fileType = button.getAttribute('data-file-type'); |
| const filePath = button.getAttribute('data-file-path'); |
| const fileName = button.getAttribute('data-file-name'); |
| |
| document.getElementById('mediaFileName').textContent = fileName; |
| |
| |
| document.getElementById('imageViewer').classList.add('d-none'); |
| document.getElementById('videoPlayer').classList.add('d-none'); |
| document.getElementById('audioPlayer').classList.add('d-none'); |
| |
| |
| if (fileType === 'image') { |
| const imageViewer = document.getElementById('imageViewer'); |
| imageViewer.src = filePath; |
| imageViewer.classList.remove('d-none'); |
| } else if (fileType === 'video') { |
| const videoPlayer = document.getElementById('videoPlayer'); |
| videoPlayer.src = filePath; |
| videoPlayer.classList.remove('d-none'); |
| } else if (fileType === 'audio') { |
| const audioPlayer = document.getElementById('audioPlayer'); |
| audioPlayer.src = filePath; |
| audioPlayer.classList.remove('d-none'); |
| } |
| }); |
| |
| |
| mediaModal.addEventListener('hidden.bs.modal', function() { |
| document.getElementById('imageViewer').src = ''; |
| document.getElementById('videoPlayer').src = ''; |
| document.getElementById('audioPlayer').src = ''; |
| }); |
| }); |
| |
| |
| let currentFileId = null; |
| |
| function openTextEditor(fileId, fileName) { |
| currentFileId = fileId; |
| document.getElementById('editFileName').textContent = fileName; |
| |
| |
| fetch(`/get_file_content/${fileId}`) |
| .then(response => response.text()) |
| .then(content => { |
| document.getElementById('editorContent').value = content; |
| const editModal = new bootstrap.Modal(document.getElementById('editModal')); |
| editModal.show(); |
| }) |
| .catch(error => { |
| alert('Ошибка при загрузке файла: ' + error); |
| }); |
| } |
| |
| function saveChanges() { |
| if (!currentFileId) return; |
| |
| const content = document.getElementById('editorContent').value; |
| |
| fetch(`/save_file_content/${currentFileId}`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify({ content: content }) |
| }) |
| .then(response => response.json()) |
| .then(data => { |
| if (data.success) { |
| alert('Файл успешно сохранен!'); |
| const editModal = bootstrap.Modal.getInstance(document.getElementById('editModal')); |
| editModal.hide(); |
| } else { |
| alert('Ошибка при сохранении файла: ' + data.error); |
| } |
| }) |
| .catch(error => { |
| alert('Ошибка при сохранении файла: ' + error); |
| }); |
| } |
| </script> |
| {% endblock %} |