Initial commit: DVS Cloud Drive v1.0.0
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
|||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
|
||||||
|
# Database & uploads
|
||||||
|
*.db
|
||||||
|
*.sqlite3
|
||||||
|
uploads/
|
||||||
|
user_data/
|
||||||
|
data/
|
||||||
|
|
||||||
|
# System files
|
||||||
|
Thumbs.db
|
||||||
|
.DS_Store
|
||||||
|
desktop.ini
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 DVS (dvs-dvsxt)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# ☁️ DVS Cloud Drive — Web File Management Platform
|
||||||
|
|
||||||
|
> A full-featured **web-based file management (cloud drive)** platform built with Flask. Upload, download, preview, share, search, and manage your files — with robust security.
|
||||||
|
|
||||||
|
**DVS Cloud Drive** is a self-hosted web file manager. It provides user authentication, file upload/download, folder management, batch operations, in-browser preview (PDF / audio / video), file search, and shareable links — all protected by security controls that block access to system-critical directories.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
| Feature | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| 👤 **User System** | Register / login / logout / change password (hashed with `werkzeug`) |
|
||||||
|
| 📤 **File Upload** | Upload files with secure filename handling |
|
||||||
|
| 📁 **Folder Management** | Create / rename / delete folders & files |
|
||||||
|
| 📥 **Download** | Single & batch download (ZIP archive) |
|
||||||
|
| 👁️ **In-Browser Preview** | PDF / video / audio preview |
|
||||||
|
| 🔍 **Search** | Search files across the drive |
|
||||||
|
| 🔗 **File Sharing** | Generate shareable links (`/s/<share_id>`) |
|
||||||
|
| 🗑️ **Batch Delete** | Delete multiple items at once |
|
||||||
|
| 📊 **Storage Usage** | View storage usage via API |
|
||||||
|
| 🛡️ **Security** | Forbidden system directories (Linux & Windows) blocked |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔌 Routes
|
||||||
|
|
||||||
|
| Route | Method | Description |
|
||||||
|
|-------|--------|-------------|
|
||||||
|
| `/` | GET | Dashboard / file list |
|
||||||
|
| `/login` | GET/POST | User login |
|
||||||
|
| `/register` | GET/POST | User registration |
|
||||||
|
| `/change_password` | GET/POST | Change password |
|
||||||
|
| `/logout` | GET | Log out |
|
||||||
|
| `/upload` | POST | Upload files |
|
||||||
|
| `/create_folder` | POST | Create a folder |
|
||||||
|
| `/rename` | POST | Rename item |
|
||||||
|
| `/delete` | POST | Delete item |
|
||||||
|
| `/download/<path>` | GET | Download file/folder |
|
||||||
|
| `/preview/<path>` | GET | Preview file (PDF/video/audio) |
|
||||||
|
| `/search` | GET | Search files |
|
||||||
|
| `/batch_download` | POST | Batch download as ZIP |
|
||||||
|
| `/batch_delete` | POST | Batch delete |
|
||||||
|
| `/share` | POST | Create share link |
|
||||||
|
| `/unshare` | POST | Remove share |
|
||||||
|
| `/shared_files` | GET | List shared files |
|
||||||
|
| `/s/<share_id>` | GET | Access a shared file |
|
||||||
|
| `/api/storage_usage` | GET | Storage usage (JSON) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Python 3.8+
|
||||||
|
- `flask`, `flask-cors`, `werkzeug`
|
||||||
|
|
||||||
|
### Install & Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
pip install flask flask-cors
|
||||||
|
|
||||||
|
# Run the platform
|
||||||
|
python app.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open `http://localhost:5000` in your browser.
|
||||||
|
|
||||||
|
### Default Flow
|
||||||
|
1. **Register** an account
|
||||||
|
2. **Login**
|
||||||
|
3. Upload / create folders / share / preview files from the dashboard
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ Security
|
||||||
|
|
||||||
|
- Passwords hashed with `werkzeug.security` (PBKDF2)
|
||||||
|
- Secure filename normalization via `werkzeug.utils.secure_filename`
|
||||||
|
- **Forbidden directories** blocked (path traversal protection):
|
||||||
|
- Linux: `/root`, `/etc`, `/usr`, `/var`, `/proc`, `/sys`, etc.
|
||||||
|
- Windows: `C:\Windows`, `C:\Program Files`, System32, etc.
|
||||||
|
- Session-based authentication & CSRF protection
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
DVS-Cloud-Drive/
|
||||||
|
├── app.py # Main Flask application
|
||||||
|
├── templates/ # HTML templates (Jinja2)
|
||||||
|
│ ├── base.html # Layout
|
||||||
|
│ ├── index.html # Dashboard / file list
|
||||||
|
│ ├── login.html / register.html / change_password.html
|
||||||
|
│ ├── preview_pdf.html / preview_video.html / preview_audio.html
|
||||||
|
│ ├── search_results.html / shared_files.html
|
||||||
|
│ ├── 404.html / 500.html
|
||||||
|
├── favicon.ico # Site icon
|
||||||
|
└── README.md # This document
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
This project is licensed under the **MIT License**. See [LICENSE](LICENSE) for details.
|
||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 834 B |
@@ -0,0 +1,16 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="max-w-3xl mx-auto mt-12 text-center">
|
||||||
|
<div class="mb-6 text-gray-400">
|
||||||
|
<i class="fa fa-exclamation-triangle text-9xl"></i>
|
||||||
|
</div>
|
||||||
|
<h1 class="text-4xl font-bold text-gray-900 mb-2">404 - 页面未找到</h1>
|
||||||
|
<p class="text-lg text-gray-600 mb-6">您要访问的页面不存在或已被移动。</p>
|
||||||
|
<div class="mt-8">
|
||||||
|
<a href="{{ url_for('index') }}" class="inline-flex items-center px-5 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
|
||||||
|
<i class="fa fa-home mr-2"></i> 返回主页
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="max-w-3xl mx-auto mt-12 text-center">
|
||||||
|
<div class="mb-6 text-gray-400">
|
||||||
|
<i class="fa fa-server text-9xl"></i>
|
||||||
|
</div>
|
||||||
|
<h1 class="text-4xl font-bold text-gray-900 mb-2">500 - 服务器内部错误</h1>
|
||||||
|
<p class="text-lg text-gray-600 mb-6">发生了一些意外情况,请稍后再试。</p>
|
||||||
|
<div class="mt-8">
|
||||||
|
<a href="{{ url_for('index') }}" class="inline-flex items-center px-5 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
|
||||||
|
<i class="fa fa-home mr-2"></i> 返回主页
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,742 @@
|
|||||||
|
<!DOCTYPE html>␍
|
||||||
|
<html lang="zh">␍
|
||||||
|
<head>␍
|
||||||
|
<meta charset="UTF-8">␍
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">␍
|
||||||
|
<title>{% block title %}DVS云盘{% endblock %}</title>␍
|
||||||
|
␍
|
||||||
|
<!-- 核心样式:基于 css.css -->␍
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css.css') }}">␍
|
||||||
|
␍
|
||||||
|
<!-- Tailwind CSS (辅助) -->␍
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>␍
|
||||||
|
<script>␍
|
||||||
|
tailwind.config = {␍
|
||||||
|
corePlugins: {␍
|
||||||
|
preflight: false, // 避免与css.css冲突␍
|
||||||
|
}␍
|
||||||
|
}␍
|
||||||
|
</script>␍
|
||||||
|
␍
|
||||||
|
<!-- Font Awesome -->␍
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">␍
|
||||||
|
␍
|
||||||
|
<!-- Bootstrap Icons (补充) -->␍
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">␍
|
||||||
|
␍
|
||||||
|
<!-- 额外样式覆盖 -->␍
|
||||||
|
<style>␍
|
||||||
|
/* 确保css.css变量生效 */␍
|
||||||
|
:root {␍
|
||||||
|
--repo-color-primary: #3b82f6;␍
|
||||||
|
--repo-color-primary-hover: #2563eb;␍
|
||||||
|
--repo-color-primary-active: #1d4ed8;␍
|
||||||
|
--repo-color-danger: #ef4444;␍
|
||||||
|
--repo-color-success: #10b981;␍
|
||||||
|
--repo-color-warning: #f59e0b;␍
|
||||||
|
--repo-color-info: #3b82f6;␍
|
||||||
|
--repo-color-text: #1f2937;␍
|
||||||
|
--repo-color-text-secondary: #4b5563;␍
|
||||||
|
--repo-color-text-tertiary: #9ca3af;␍
|
||||||
|
--repo-color-bg: #ffffff;␍
|
||||||
|
--repo-color-bg-secondary: #f9fafb;␍
|
||||||
|
--repo-color-bg-tertiary: #f3f4f6;␍
|
||||||
|
--repo-color-border: #e5e7eb;␍
|
||||||
|
--repo-color-border-hover: #d1d5db;␍
|
||||||
|
--repo-header-height: 60px;␍
|
||||||
|
--repo-sidebar-width: 260px;␍
|
||||||
|
--repo-spacing-1: 4px;␍
|
||||||
|
--repo-spacing-2: 8px;␍
|
||||||
|
--repo-spacing-3: 12px;␍
|
||||||
|
--repo-spacing-4: 16px;␍
|
||||||
|
--repo-spacing-5: 20px;␍
|
||||||
|
--repo-spacing-6: 24px;␍
|
||||||
|
--repo-spacing-8: 32px;␍
|
||||||
|
--repo-radius-sm: 4px;␍
|
||||||
|
--repo-radius-md: 6px;␍
|
||||||
|
--repo-radius-lg: 8px;␍
|
||||||
|
--repo-radius-xl: 12px;␍
|
||||||
|
--repo-radius-full: 9999px;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 全局重置 */␍
|
||||||
|
* {␍
|
||||||
|
margin: 0;␍
|
||||||
|
padding: 0;␍
|
||||||
|
box-sizing: border-box;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
body {␍
|
||||||
|
font-family: var(--repo-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif);␍
|
||||||
|
font-size: var(--repo-font-size-base, 14px);␍
|
||||||
|
line-height: var(--repo-line-height-base, 1.5);␍
|
||||||
|
color: var(--repo-color-text);␍
|
||||||
|
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);␍
|
||||||
|
min-height: 100vh;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* Mac风格导航栏 - 基于css.css变量 */␍
|
||||||
|
.mac-nav {␍
|
||||||
|
background: rgba(255, 255, 255, 0.92);␍
|
||||||
|
backdrop-filter: blur(12px);␍
|
||||||
|
-webkit-backdrop-filter: blur(12px);␍
|
||||||
|
border-bottom: 1px solid var(--repo-color-border);␍
|
||||||
|
box-shadow: var(--repo-shadow-sm, 0 1px 3px rgba(0,0,0,0.05));␍
|
||||||
|
padding: 0;␍
|
||||||
|
position: sticky;␍
|
||||||
|
top: 0;␍
|
||||||
|
z-index: var(--repo-z-sticky, 100);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-nav .container {␍
|
||||||
|
max-width: 1400px;␍
|
||||||
|
margin: 0 auto;␍
|
||||||
|
padding: 0 var(--repo-spacing-6);␍
|
||||||
|
height: var(--repo-header-height);␍
|
||||||
|
display: flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
justify-content: space-between;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-nav-brand {␍
|
||||||
|
font-weight: 700;␍
|
||||||
|
font-size: 20px;␍
|
||||||
|
color: var(--repo-color-text);␍
|
||||||
|
display: flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
gap: 10px;␍
|
||||||
|
text-decoration: none;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-nav-brand i {␍
|
||||||
|
font-size: 24px;␍
|
||||||
|
color: var(--repo-color-primary);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-nav-link {␍
|
||||||
|
color: var(--repo-color-text-secondary);␍
|
||||||
|
font-weight: 500;␍
|
||||||
|
padding: 8px 16px;␍
|
||||||
|
border-radius: var(--repo-radius-md);␍
|
||||||
|
transition: all 0.2s ease;␍
|
||||||
|
margin: 0 2px;␍
|
||||||
|
display: inline-flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
gap: 8px;␍
|
||||||
|
text-decoration: none;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-nav-link:hover {␍
|
||||||
|
background: rgba(59, 130, 246, 0.08);␍
|
||||||
|
color: var(--repo-color-primary);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-nav-link.active {␍
|
||||||
|
background: var(--repo-color-primary);␍
|
||||||
|
color: white;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-nav-link.active i {␍
|
||||||
|
color: white;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 按钮样式 - 基于css.css */␍
|
||||||
|
.mac-btn {␍
|
||||||
|
display: inline-flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
justify-content: center;␍
|
||||||
|
gap: 8px;␍
|
||||||
|
background: var(--repo-color-primary);␍
|
||||||
|
color: white;␍
|
||||||
|
border: none;␍
|
||||||
|
border-radius: var(--repo-radius-full);␍
|
||||||
|
padding: 10px 24px;␍
|
||||||
|
font-weight: 500;␍
|
||||||
|
font-size: 14px;␍
|
||||||
|
cursor: pointer;␍
|
||||||
|
transition: all 0.2s ease;␍
|
||||||
|
box-shadow: var(--repo-shadow-xs);␍
|
||||||
|
text-decoration: none;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-btn:hover {␍
|
||||||
|
background: var(--repo-color-primary-hover);␍
|
||||||
|
transform: translateY(-1px);␍
|
||||||
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-btn-sm {␍
|
||||||
|
padding: 6px 16px;␍
|
||||||
|
font-size: 13px;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-btn-outline {␍
|
||||||
|
background: transparent;␍
|
||||||
|
color: var(--repo-color-primary);␍
|
||||||
|
border: 1px solid var(--repo-color-border);␍
|
||||||
|
box-shadow: none;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-btn-outline:hover {␍
|
||||||
|
background: rgba(59, 130, 246, 0.08);␍
|
||||||
|
border-color: var(--repo-color-primary);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-btn-secondary {␍
|
||||||
|
background: var(--repo-color-text-secondary);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-btn-secondary:hover {␍
|
||||||
|
background: #475569;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-btn-success {␍
|
||||||
|
background: var(--repo-color-success);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-btn-danger {␍
|
||||||
|
background: var(--repo-color-danger);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 卡片样式 */␍
|
||||||
|
.mac-card {␍
|
||||||
|
background: var(--repo-color-bg);␍
|
||||||
|
border-radius: var(--repo-radius-xl);␍
|
||||||
|
border: 1px solid var(--repo-color-border);␍
|
||||||
|
box-shadow: var(--repo-shadow-sm);␍
|
||||||
|
overflow: hidden;␍
|
||||||
|
transition: all 0.2s ease;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-card:hover {␍
|
||||||
|
box-shadow: var(--repo-shadow-lg);␍
|
||||||
|
transform: translateY(-2px);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-card-header {␍
|
||||||
|
background: var(--repo-color-bg-secondary);␍
|
||||||
|
padding: 20px;␍
|
||||||
|
border-bottom: 1px solid var(--repo-color-border);␍
|
||||||
|
font-weight: 600;␍
|
||||||
|
color: var(--repo-color-text);␍
|
||||||
|
display: flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
justify-content: space-between;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 文件列表项 */␍
|
||||||
|
.mac-file-item {␍
|
||||||
|
background: var(--repo-color-bg);␍
|
||||||
|
border: 1px solid var(--repo-color-border);␍
|
||||||
|
border-radius: var(--repo-radius-lg);␍
|
||||||
|
padding: 16px;␍
|
||||||
|
margin-bottom: 8px;␍
|
||||||
|
display: flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
gap: 16px;␍
|
||||||
|
transition: all 0.2s ease;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-file-item:hover {␍
|
||||||
|
background: var(--repo-color-bg-hover, #f8fafc);␍
|
||||||
|
border-color: var(--repo-color-border-hover);␍
|
||||||
|
transform: translateX(4px);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-file-icon {␍
|
||||||
|
width: 48px;␍
|
||||||
|
height: 48px;␍
|
||||||
|
border-radius: var(--repo-radius-lg);␍
|
||||||
|
display: flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
justify-content: center;␍
|
||||||
|
color: white;␍
|
||||||
|
font-size: 20px;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-file-icon.folder {␍
|
||||||
|
background: linear-gradient(135deg, #60a5fa, #3b82f6);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-file-icon.image {␍
|
||||||
|
background: linear-gradient(135deg, #38bdf8, #0ea5e9);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-file-icon.video {␍
|
||||||
|
background: linear-gradient(135deg, #8b5cf6, #7c3aed);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-file-icon.audio {␍
|
||||||
|
background: linear-gradient(135deg, #10b981, #059669);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-file-icon.pdf {␍
|
||||||
|
background: linear-gradient(135deg, #f87171, #ef4444);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-file-icon.document {␍
|
||||||
|
background: linear-gradient(135deg, #94a3b8, #64748b);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 面包屑 */␍
|
||||||
|
.mac-breadcrumb {␍
|
||||||
|
display: flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
gap: 8px;␍
|
||||||
|
background: var(--repo-color-bg);␍
|
||||||
|
border: 1px solid var(--repo-color-border);␍
|
||||||
|
border-radius: var(--repo-radius-lg);␍
|
||||||
|
padding: 12px 20px;␍
|
||||||
|
margin-bottom: 24px;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-breadcrumb-item {␍
|
||||||
|
color: var(--repo-color-text-secondary);␍
|
||||||
|
font-size: 14px;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-breadcrumb-item a {␍
|
||||||
|
color: var(--repo-color-primary);␍
|
||||||
|
text-decoration: none;␍
|
||||||
|
font-weight: 500;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 输入框 */␍
|
||||||
|
.mac-input {␍
|
||||||
|
background: var(--repo-color-bg);␍
|
||||||
|
border: 1px solid var(--repo-color-border);␍
|
||||||
|
border-radius: var(--repo-radius-lg);␍
|
||||||
|
padding: 12px 16px;␍
|
||||||
|
font-size: 14px;␍
|
||||||
|
color: var(--repo-color-text);␍
|
||||||
|
transition: all 0.2s ease;␍
|
||||||
|
outline: none;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-input:focus {␍
|
||||||
|
border-color: var(--repo-color-primary);␍
|
||||||
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 徽章 */␍
|
||||||
|
.mac-badge {␍
|
||||||
|
display: inline-flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
gap: 4px;␍
|
||||||
|
background: var(--repo-color-bg-tertiary);␍
|
||||||
|
color: var(--repo-color-text-secondary);␍
|
||||||
|
padding: 4px 12px;␍
|
||||||
|
border-radius: var(--repo-radius-full);␍
|
||||||
|
font-size: 12px;␍
|
||||||
|
font-weight: 500;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-badge-primary {␍
|
||||||
|
background: rgba(59, 130, 246, 0.1);␍
|
||||||
|
color: var(--repo-color-primary);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-badge-success {␍
|
||||||
|
background: rgba(16, 185, 129, 0.1);␍
|
||||||
|
color: var(--repo-color-success);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-badge-warning {␍
|
||||||
|
background: rgba(245, 158, 11, 0.1);␍
|
||||||
|
color: var(--repo-color-warning);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-badge-danger {␍
|
||||||
|
background: rgba(239, 68, 68, 0.1);␍
|
||||||
|
color: var(--repo-color-danger);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 警告框 */␍
|
||||||
|
.mac-alert {␍
|
||||||
|
background: var(--repo-color-bg);␍
|
||||||
|
border: 1px solid;␍
|
||||||
|
border-radius: var(--repo-radius-lg);␍
|
||||||
|
padding: 16px;␍
|
||||||
|
margin: 16px 0;␍
|
||||||
|
display: flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
gap: 12px;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-alert-success {␍
|
||||||
|
border-color: var(--repo-color-success);␍
|
||||||
|
background: rgba(16, 185, 129, 0.05);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-alert-warning {␍
|
||||||
|
border-color: var(--repo-color-warning);␍
|
||||||
|
background: rgba(245, 158, 11, 0.05);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-alert-danger {␍
|
||||||
|
border-color: var(--repo-color-danger);␍
|
||||||
|
background: rgba(239, 68, 68, 0.05);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-alert-info {␍
|
||||||
|
border-color: var(--repo-color-primary);␍
|
||||||
|
background: rgba(59, 130, 246, 0.05);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 动作按钮组 */␍
|
||||||
|
.mac-action-buttons {␍
|
||||||
|
display: flex;␍
|
||||||
|
gap: 8px;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-action-btn {␍
|
||||||
|
width: 36px;␍
|
||||||
|
height: 36px;␍
|
||||||
|
display: flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
justify-content: center;␍
|
||||||
|
background: var(--repo-color-bg);␍
|
||||||
|
border: 1px solid var(--repo-color-border);␍
|
||||||
|
border-radius: var(--repo-radius-md);␍
|
||||||
|
color: var(--repo-color-text-secondary);␍
|
||||||
|
cursor: pointer;␍
|
||||||
|
transition: all 0.2s ease;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-action-btn:hover {␍
|
||||||
|
background: var(--repo-color-bg-hover);␍
|
||||||
|
border-color: var(--repo-color-border-hover);␍
|
||||||
|
color: var(--repo-color-primary);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 下拉菜单 */␍
|
||||||
|
.mac-dropdown {␍
|
||||||
|
background: var(--repo-color-bg);␍
|
||||||
|
border: 1px solid var(--repo-color-border);␍
|
||||||
|
border-radius: var(--repo-radius-lg);␍
|
||||||
|
box-shadow: var(--repo-shadow-lg);␍
|
||||||
|
padding: 8px;␍
|
||||||
|
min-width: 200px;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-dropdown-item {␍
|
||||||
|
padding: 10px 16px;␍
|
||||||
|
border-radius: var(--repo-radius-md);␍
|
||||||
|
color: var(--repo-color-text);␍
|
||||||
|
cursor: pointer;␍
|
||||||
|
transition: all 0.2s ease;␍
|
||||||
|
display: flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
gap: 12px;␍
|
||||||
|
text-decoration: none;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-dropdown-item:hover {␍
|
||||||
|
background: var(--repo-color-bg-hover);␍
|
||||||
|
color: var(--repo-color-primary);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 表格 */␍
|
||||||
|
.mac-table {␍
|
||||||
|
background: var(--repo-color-bg);␍
|
||||||
|
border: 1px solid var(--repo-color-border);␍
|
||||||
|
border-radius: var(--repo-radius-lg);␍
|
||||||
|
overflow: hidden;␍
|
||||||
|
width: 100%;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-table th {␍
|
||||||
|
background: var(--repo-color-bg-secondary);␍
|
||||||
|
padding: 16px;␍
|
||||||
|
text-align: left;␍
|
||||||
|
font-weight: 600;␍
|
||||||
|
color: var(--repo-color-text);␍
|
||||||
|
border-bottom: 1px solid var(--repo-color-border);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-table td {␍
|
||||||
|
padding: 16px;␍
|
||||||
|
border-bottom: 1px solid var(--repo-color-border-light, #f1f5f9);␍
|
||||||
|
color: var(--repo-color-text-secondary);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-table tr:hover td {␍
|
||||||
|
background: var(--repo-color-bg-hover);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 浮动按钮 */␍
|
||||||
|
.mac-float-btn {␍
|
||||||
|
position: fixed;␍
|
||||||
|
bottom: 24px;␍
|
||||||
|
right: 24px;␍
|
||||||
|
width: 56px;␍
|
||||||
|
height: 56px;␍
|
||||||
|
border-radius: 50%;␍
|
||||||
|
background: var(--repo-color-primary);␍
|
||||||
|
color: white;␍
|
||||||
|
display: flex;␍
|
||||||
|
align-items: center;␍
|
||||||
|
justify-content: center;␍
|
||||||
|
box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3);␍
|
||||||
|
cursor: pointer;␍
|
||||||
|
transition: all 0.2s ease;␍
|
||||||
|
z-index: 100;␍
|
||||||
|
border: none;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-float-btn:hover {␍
|
||||||
|
background: var(--repo-color-primary-hover);␍
|
||||||
|
transform: translateY(-2px) scale(1.05);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 复选框 */␍
|
||||||
|
.mac-checkbox {␍
|
||||||
|
width: 18px;␍
|
||||||
|
height: 18px;␍
|
||||||
|
border-radius: var(--repo-radius-sm);␍
|
||||||
|
border: 2px solid var(--repo-color-border);␍
|
||||||
|
cursor: pointer;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 模态框/弹窗 - 保持原有逻辑不变 */␍
|
||||||
|
.mac-dialog-overlay {␍
|
||||||
|
position: fixed;␍
|
||||||
|
top: 0;␍
|
||||||
|
left: 0;␍
|
||||||
|
right: 0;␍
|
||||||
|
bottom: 0;␍
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);␍
|
||||||
|
z-index: 1040;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.mac-dialog {␍
|
||||||
|
position: fixed;␍
|
||||||
|
top: 50%;␍
|
||||||
|
left: 50%;␍
|
||||||
|
transform: translate(-50%, -50%);␍
|
||||||
|
background: var(--repo-color-bg);␍
|
||||||
|
border-radius: var(--repo-radius-xl);␍
|
||||||
|
box-shadow: var(--repo-shadow-xl);␍
|
||||||
|
z-index: 1050;␍
|
||||||
|
min-width: 400px;␍
|
||||||
|
max-width: 500px;␍
|
||||||
|
width: 90%;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 弹窗样式 - 保持原有 */␍
|
||||||
|
#uploadPopup, #createFolderPopup, #sharePopup, #renamePopup, #deletePopup {␍
|
||||||
|
display: none !important;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
#uploadPopup:not(.hidden), #createFolderPopup:not(.hidden), ␍
|
||||||
|
#sharePopup:not(.hidden), #renamePopup:not(.hidden), ␍
|
||||||
|
#deletePopup:not(.hidden) {␍
|
||||||
|
display: flex !important;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
.hidden {␍
|
||||||
|
display: none !important;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 滚动条 */␍
|
||||||
|
::-webkit-scrollbar {␍
|
||||||
|
width: 8px;␍
|
||||||
|
height: 8px;␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
::-webkit-scrollbar-track {␍
|
||||||
|
background: var(--repo-color-bg-tertiary);␍
|
||||||
|
border-radius: var(--repo-radius-full);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
::-webkit-scrollbar-thumb {␍
|
||||||
|
background: var(--repo-color-border);␍
|
||||||
|
border-radius: var(--repo-radius-full);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
::-webkit-scrollbar-thumb:hover {␍
|
||||||
|
background: var(--repo-color-border-hover);␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
/* 多彩图标增强 */␍
|
||||||
|
.text-primary { color: var(--repo-color-primary); }␍
|
||||||
|
.text-success { color: var(--repo-color-success); }␍
|
||||||
|
.text-danger { color: var(--repo-color-danger); }␍
|
||||||
|
.text-warning { color: var(--repo-color-warning); }␍
|
||||||
|
.text-info { color: var(--repo-color-info); }␍
|
||||||
|
␍
|
||||||
|
/* 响应式 */␍
|
||||||
|
@media (max-width: 768px) {␍
|
||||||
|
.mac-nav .container {␍
|
||||||
|
padding: 0 var(--repo-spacing-4);␍
|
||||||
|
}␍
|
||||||
|
.mac-nav-link span:not(.hidden-on-mobile) {␍
|
||||||
|
display: none;␍
|
||||||
|
}␍
|
||||||
|
.mac-nav-link {␍
|
||||||
|
padding: 8px 12px;␍
|
||||||
|
}␍
|
||||||
|
.mac-dialog {␍
|
||||||
|
min-width: 90%;␍
|
||||||
|
width: 90%;␍
|
||||||
|
}␍
|
||||||
|
}␍
|
||||||
|
</style>␍
|
||||||
|
␍
|
||||||
|
{% block extra_css %}{% endblock %}␍
|
||||||
|
</head>␍
|
||||||
|
<body>␍
|
||||||
|
<!-- 导航栏 -->␍
|
||||||
|
<nav class="mac-nav">␍
|
||||||
|
<div class="container">␍
|
||||||
|
<div class="flex items-center space-x-8">␍
|
||||||
|
<a href="{{ url_for('index') }}" class="mac-nav-brand">␍
|
||||||
|
<i class="fas fa-cloud text-2xl"></i>␍
|
||||||
|
<span>DVS云盘</span>␍
|
||||||
|
</a>␍
|
||||||
|
␍
|
||||||
|
<div class="flex items-center space-x-1">␍
|
||||||
|
<a href="{{ url_for('index') }}" ␍
|
||||||
|
class="mac-nav-link {{ 'active' if request.endpoint == 'index' }}">␍
|
||||||
|
<i class="fas fa-home"></i>␍
|
||||||
|
<span>主页</span>␍
|
||||||
|
</a>␍
|
||||||
|
␍
|
||||||
|
<a href="{{ url_for('shared_files') }}" ␍
|
||||||
|
class="mac-nav-link {{ 'active' if request.endpoint == 'shared_files' }}">␍
|
||||||
|
<i class="fas fa-link"></i>␍
|
||||||
|
<span>已分享</span>␍
|
||||||
|
</a>␍
|
||||||
|
</div>␍
|
||||||
|
</div>␍
|
||||||
|
␍
|
||||||
|
<div class="flex items-center">␍
|
||||||
|
{% if session.get('username') %}␍
|
||||||
|
<div class="relative ml-4">␍
|
||||||
|
<button class="mac-nav-link flex items-center" id="userMenuButton">␍
|
||||||
|
<i class="fas fa-user-circle text-xl mr-2"></i>␍
|
||||||
|
<span>{{ session.username }}</span>␍
|
||||||
|
<i class="fas fa-chevron-down ml-2 text-xs"></i>␍
|
||||||
|
</button>␍
|
||||||
|
␍
|
||||||
|
<div class="mac-dropdown absolute right-0 mt-2 hidden z-50" id="userMenuDropdown">␍
|
||||||
|
<a href="{{ url_for('change_password') }}" class="mac-dropdown-item">␍
|
||||||
|
<i class="fas fa-key"></i>␍
|
||||||
|
<span>修改密码</span>␍
|
||||||
|
</a>␍
|
||||||
|
<div class="border-t border-gray-100 my-1"></div>␍
|
||||||
|
<a href="{{ url_for('logout') }}" class="mac-dropdown-item">␍
|
||||||
|
<i class="fas fa-sign-out-alt"></i>␍
|
||||||
|
<span>退出登录</span>␍
|
||||||
|
</a>␍
|
||||||
|
</div>␍
|
||||||
|
</div>␍
|
||||||
|
{% else %}␍
|
||||||
|
<a href="{{ url_for('login') }}" class="mac-btn mac-btn-sm ml-4">␍
|
||||||
|
<i class="fas fa-sign-in-alt"></i>␍
|
||||||
|
<span>登录</span>␍
|
||||||
|
</a>␍
|
||||||
|
{% endif %}␍
|
||||||
|
</div>␍
|
||||||
|
</div>␍
|
||||||
|
</nav>␍
|
||||||
|
␍
|
||||||
|
<!-- 主内容区域 -->␍
|
||||||
|
<main class="container mx-auto px-4 py-8">␍
|
||||||
|
{% block content %}{% endblock %}␍
|
||||||
|
</main>␍
|
||||||
|
␍
|
||||||
|
<!-- 浮动操作按钮 -->␍
|
||||||
|
<button class="mac-float-btn mac-tooltip" data-tooltip="快速操作">␍
|
||||||
|
<i class="fas fa-plus"></i>␍
|
||||||
|
</button>␍
|
||||||
|
␍
|
||||||
|
<!-- JavaScript -->␍
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>␍
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>␍
|
||||||
|
␍
|
||||||
|
<script>␍
|
||||||
|
// 下拉菜单切换␍
|
||||||
|
const userMenuBtn = document.getElementById('userMenuButton');␍
|
||||||
|
const userMenuDropdown = document.getElementById('userMenuDropdown');␍
|
||||||
|
if (userMenuBtn && userMenuDropdown) {␍
|
||||||
|
userMenuBtn.addEventListener('click', function(e) {␍
|
||||||
|
e.stopPropagation();␍
|
||||||
|
userMenuDropdown.classList.toggle('hidden');␍
|
||||||
|
});␍
|
||||||
|
}␍
|
||||||
|
␍
|
||||||
|
// 点击外部关闭下拉菜单␍
|
||||||
|
document.addEventListener('click', function() {␍
|
||||||
|
if (userMenuDropdown) userMenuDropdown.classList.add('hidden');␍
|
||||||
|
});␍
|
||||||
|
␍
|
||||||
|
// 全局函数声明␍
|
||||||
|
window.showUploadPopup = function() {␍
|
||||||
|
const popup = document.getElementById('uploadPopup');␍
|
||||||
|
if (popup) {␍
|
||||||
|
popup.classList.remove('hidden');␍
|
||||||
|
popup.style.cssText = 'display:flex!important;position:fixed!important;inset:0!important;background-color:rgba(0,0,0,0.5)!important;align-items:center!important;justify-content:center!important;z-index:9999!important;';␍
|
||||||
|
}␍
|
||||||
|
};␍
|
||||||
|
␍
|
||||||
|
window.showCreateFolderPopup = function() {␍
|
||||||
|
const popup = document.getElementById('createFolderPopup');␍
|
||||||
|
if (popup) {␍
|
||||||
|
popup.classList.remove('hidden');␍
|
||||||
|
popup.style.cssText = 'display:flex!important;position:fixed!important;inset:0!important;background-color:rgba(0,0,0,0.5)!important;align-items:center!important;justify-content:center!important;z-index:9999!important;';␍
|
||||||
|
}␍
|
||||||
|
};␍
|
||||||
|
␍
|
||||||
|
window.closePopup = function(id) {␍
|
||||||
|
const el = document.getElementById(id);␍
|
||||||
|
if (el) {␍
|
||||||
|
el.classList.add('hidden');␍
|
||||||
|
el.style.cssText = '';␍
|
||||||
|
}␍
|
||||||
|
};␍
|
||||||
|
␍
|
||||||
|
// 工具提示␍
|
||||||
|
document.querySelectorAll('.mac-tooltip').forEach(element => {␍
|
||||||
|
element.addEventListener('mouseenter', function() {␍
|
||||||
|
const tooltip = this.getAttribute('data-tooltip');␍
|
||||||
|
if (tooltip) {␍
|
||||||
|
const tooltipEl = document.createElement('div');␍
|
||||||
|
tooltipEl.className = 'fixed bg-gray-900 text-white text-xs px-2 py-1 rounded shadow-lg z-[9999] pointer-events-none';␍
|
||||||
|
tooltipEl.textContent = tooltip;␍
|
||||||
|
const rect = this.getBoundingClientRect();␍
|
||||||
|
tooltipEl.style.left = rect.left + (rect.width / 2) + 'px';␍
|
||||||
|
tooltipEl.style.top = rect.top - 30 + 'px';␍
|
||||||
|
tooltipEl.style.transform = 'translateX(-50%)';␍
|
||||||
|
document.body.appendChild(tooltipEl);␍
|
||||||
|
this._tooltipEl = tooltipEl;␍
|
||||||
|
}␍
|
||||||
|
});␍
|
||||||
|
␍
|
||||||
|
element.addEventListener('mouseleave', function() {␍
|
||||||
|
if (this._tooltipEl) {␍
|
||||||
|
this._tooltipEl.remove();␍
|
||||||
|
this._tooltipEl = null;␍
|
||||||
|
}␍
|
||||||
|
});␍
|
||||||
|
});␍
|
||||||
|
</script>␍
|
||||||
|
␍
|
||||||
|
{% block extra_js %}{% endblock %}␍
|
||||||
|
␍
|
||||||
|
<!-- 页脚 -->␍
|
||||||
|
<footer class="mt-12 py-8 text-center border-t border-gray-200">␍
|
||||||
|
<div class="container mx-auto px-4">␍
|
||||||
|
<p class="text-gray-600 mb-2 text-lg">🚀 DVS云盘 · 某个开发者的作品</p>␍
|
||||||
|
<p class="text-gray-500 text-sm">␍
|
||||||
|
安全的代码是优美的代码 · ␍
|
||||||
|
用户友好的设计是简单的设计 ·␍
|
||||||
|
每一次302重定向都是明了的防护␍
|
||||||
|
</p>␍
|
||||||
|
<p class="text-gray-400 text-xs mt-4">␍
|
||||||
|
© 2026 DVS云盘 保留所有权利␍
|
||||||
|
</p>␍
|
||||||
|
</div>␍
|
||||||
|
</footer>␍
|
||||||
|
</body>␍
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}修改密码 - DVS云盘{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<div class="max-w-lg mx-auto">
|
||||||
|
<div class="mac-card">
|
||||||
|
<div class="mac-card-header">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fas fa-key mr-3"></i>
|
||||||
|
<h5 class="mb-0">修改密码</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% if messages %}
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<div class="mac-alert mac-alert-{{ 'success' if category == 'success' else 'info' if category == 'info' else 'warning' if category == 'warning' else 'danger' }} mb-6">
|
||||||
|
<i class="fas fa-{{ 'check-circle' if category == 'success' else 'info-circle' if category == 'info' else 'exclamation-triangle' if category == 'warning' else 'times-circle' }}"></i>
|
||||||
|
<span>{{ message|safe }}</span>
|
||||||
|
<button type="button" class="mac-action-btn ml-auto" onclick="this.parentElement.remove()">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<form method="POST" action="{{ url_for('change_password') }}">
|
||||||
|
<div class="mb-6">
|
||||||
|
<label for="current_password" class="block text-gray-700 mb-2 font-medium">
|
||||||
|
<i class="fas fa-lock mr-2"></i>当前密码
|
||||||
|
</label>
|
||||||
|
<input type="password" class="mac-input w-full" id="current_password" name="current_password" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<label for="new_password" class="block text-gray-700 mb-2 font-medium">
|
||||||
|
<i class="fas fa-key mr-2"></i>新密码
|
||||||
|
</label>
|
||||||
|
<input type="password" class="mac-input w-full" id="new_password" name="new_password" required>
|
||||||
|
<div class="text-gray-500 text-sm mt-2">密码长度至少6个字符</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-8">
|
||||||
|
<label for="confirm_password" class="block text-gray-700 mb-2 font-medium">
|
||||||
|
<i class="fas fa-lock mr-2"></i>确认新密码
|
||||||
|
</label>
|
||||||
|
<input type="password" class="mac-input w-full" id="confirm_password" name="confirm_password" required>
|
||||||
|
<div class="text-gray-500 text-sm mt-2">请再次输入新密码以确保一致性</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-between gap-3">
|
||||||
|
<a href="{{ url_for('index') }}" class="mac-btn mac-btn-secondary">
|
||||||
|
<i class="fas fa-arrow-left mr-2"></i>返回
|
||||||
|
</a>
|
||||||
|
<button type="submit" class="mac-btn mac-btn-primary">
|
||||||
|
<i class="fas fa-save mr-2"></i>保存更改
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// 密码确认验证
|
||||||
|
const newPassword = document.getElementById('new_password');
|
||||||
|
const confirmPassword = document.getElementById('confirm_password');
|
||||||
|
|
||||||
|
function validatePassword() {
|
||||||
|
if (newPassword.value !== confirmPassword.value) {
|
||||||
|
confirmPassword.setCustomValidity('两次输入的密码不一致');
|
||||||
|
} else {
|
||||||
|
confirmPassword.setCustomValidity('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newPassword.addEventListener('change', validatePassword);
|
||||||
|
confirmPassword.addEventListener('keyup', validatePassword);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 834 B |
@@ -0,0 +1,973 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}DVS云盘 - 首页{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<!-- 面包屑 -->
|
||||||
|
<div class="mac-breadcrumb">
|
||||||
|
<div class="flex items-center flex-wrap gap-2">
|
||||||
|
<div class="mac-breadcrumb-item">
|
||||||
|
<a href="{{ url_for('index') }}">根目录</a>
|
||||||
|
</div>
|
||||||
|
{% if breadcrumbs %}
|
||||||
|
{% for crumb in breadcrumbs[1:] %}
|
||||||
|
<div class="mac-breadcrumb-item">/</div>
|
||||||
|
<div class="mac-breadcrumb-item">
|
||||||
|
<a href="{{ url_for('index', path=crumb.path) }}">{{ crumb.name }}</a>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Flash 消息 -->
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% if messages %}
|
||||||
|
<div id="flashMessages" class="fixed top-20 right-4 left-4 md:left-auto z-50 space-y-2 max-w-md">
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<div class="mac-alert mac-alert-{{ category }} animate-slideInRight" data-duration="5000">
|
||||||
|
<div class="flex items-start gap-3">
|
||||||
|
{% if category == 'success' %}
|
||||||
|
<i class="fas fa-check-circle text-success text-xl"></i>
|
||||||
|
{% elif category == 'danger' %}
|
||||||
|
<i class="fas fa-exclamation-circle text-danger text-xl"></i>
|
||||||
|
{% elif category == 'warning' %}
|
||||||
|
<i class="fas fa-exclamation-triangle text-warning text-xl"></i>
|
||||||
|
{% else %}
|
||||||
|
<i class="fas fa-info-circle text-primary text-xl"></i>
|
||||||
|
{% endif %}
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="font-medium">
|
||||||
|
{% if category == 'success' %}成功{% elif category == 'danger' %}错误{% elif category == 'warning' %}警告{% else %}提示{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="text-sm mt-1">{{ message|safe }}</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="text-gray-400 hover:text-gray-600" onclick="this.parentElement.parentElement.remove()">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.querySelectorAll('#flashMessages .mac-alert').forEach(msg => {
|
||||||
|
const duration = parseInt(msg.getAttribute('data-duration')) || 5000;
|
||||||
|
setTimeout(() => { msg.style.opacity = '0'; setTimeout(() => msg.remove(), 300); }, duration);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<!-- 顶部操作按钮 - 移动端适配 -->
|
||||||
|
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
|
||||||
|
<div>
|
||||||
|
<h1 class="text-2xl font-bold text-dark">我的文件</h1>
|
||||||
|
<p class="text-muted mt-1 text-sm">管理你的文件与文件夹</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-3 flex-wrap">
|
||||||
|
<button onclick="showUploadPopup()" class="mac-btn mac-btn-sm">
|
||||||
|
<i class="fas fa-upload"></i><span>上传文件</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="showCreateFolderPopup()" class="mac-btn mac-btn-sm mac-btn-outline">
|
||||||
|
<i class="fas fa-folder-plus"></i><span>新建文件夹</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 文件列表 - 自适应空白 -->
|
||||||
|
<div class="mac-card">
|
||||||
|
<div class="mac-card-header">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<i class="fas fa-folder-open text-primary"></i><span>文件列表</span>
|
||||||
|
</div>
|
||||||
|
<span class="mac-badge mac-badge-primary">共 {{ items|length if items else 0 }} 项</span>
|
||||||
|
</div>
|
||||||
|
<div class="file-list-container">
|
||||||
|
{% if items and items|length > 0 %}
|
||||||
|
{% for item in items %}
|
||||||
|
<div class="mac-file-item">
|
||||||
|
<!-- 图标区域 -->
|
||||||
|
<div class="mac-file-icon
|
||||||
|
{% if item.is_dir %}folder
|
||||||
|
{% elif item.ext in ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'] %}image
|
||||||
|
{% elif item.ext in ['mp4', 'avi', 'mov', 'mkv', 'flv', 'wmv', 'webm', 'm4v'] %}video
|
||||||
|
{% elif item.ext in ['mp3', 'wav', 'flac', 'aac', 'ogg', 'm4a', 'wma'] %}audio
|
||||||
|
{% elif item.ext == 'pdf' %}pdf
|
||||||
|
{% elif item.ext in ['doc', 'docx'] %}word
|
||||||
|
{% elif item.ext in ['xls', 'xlsx', 'csv'] %}excel
|
||||||
|
{% elif item.ext in ['ppt', 'pptx'] %}powerpoint
|
||||||
|
{% elif item.ext in ['zip', 'rar', '7z', 'tar', 'gz', 'bz2'] %}archive
|
||||||
|
{% elif item.ext in ['py', 'js', 'java', 'c', 'cpp', 'h', 'html', 'css', 'php', 'rb', 'go', 'rs', 'swift', 'kt'] %}code
|
||||||
|
{% elif item.ext in ['txt', 'md', 'log', 'ini', 'cfg', 'conf'] %}text
|
||||||
|
{% elif item.ext in ['exe', 'msi', 'dmg', 'pkg', 'deb', 'rpm', 'appimage'] %}executable
|
||||||
|
{% elif item.ext in ['iso', 'img', 'vhd', 'vmdk'] %}disk
|
||||||
|
{% elif item.ext in ['psd', 'ai', 'eps'] %}design
|
||||||
|
{% elif item.ext in ['bin', 'dat', 'db'] %}binary
|
||||||
|
{% else %}document
|
||||||
|
{% endif %}">
|
||||||
|
|
||||||
|
<!-- 图标内容 -->
|
||||||
|
{% if item.is_dir %}
|
||||||
|
<i class="fas fa-folder"></i>
|
||||||
|
{% elif item.ext in ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'] %}
|
||||||
|
<i class="fas fa-image"></i>
|
||||||
|
{% elif item.ext in ['mp4', 'avi', 'mov', 'mkv', 'flv', 'wmv', 'webm', 'm4v'] %}
|
||||||
|
<i class="fas fa-video"></i>
|
||||||
|
{% elif item.ext in ['mp3', 'wav', 'flac', 'aac', 'ogg', 'm4a', 'wma'] %}
|
||||||
|
<i class="fas fa-music"></i>
|
||||||
|
{% elif item.ext == 'pdf' %}
|
||||||
|
<i class="fas fa-file-pdf"></i>
|
||||||
|
{% elif item.ext in ['doc', 'docx'] %}
|
||||||
|
<i class="fas fa-file-word"></i>
|
||||||
|
{% elif item.ext in ['xls', 'xlsx', 'csv'] %}
|
||||||
|
<i class="fas fa-file-excel"></i>
|
||||||
|
{% elif item.ext in ['ppt', 'pptx'] %}
|
||||||
|
<i class="fas fa-file-powerpoint"></i>
|
||||||
|
{% elif item.ext in ['zip', 'rar', '7z', 'tar', 'gz', 'bz2'] %}
|
||||||
|
<i class="fas fa-file-archive"></i>
|
||||||
|
{% elif item.ext in ['py', 'js', 'java', 'c', 'cpp', 'h', 'html', 'css', 'php', 'rb', 'go', 'rs', 'swift', 'kt'] %}
|
||||||
|
<i class="fas fa-code"></i>
|
||||||
|
{% elif item.ext in ['txt', 'md', 'log', 'ini', 'cfg', 'conf'] %}
|
||||||
|
<i class="fas fa-file-alt"></i>
|
||||||
|
{% elif item.ext in ['exe', 'msi', 'dmg', 'pkg', 'deb', 'rpm', 'appimage'] %}
|
||||||
|
<i class="fas fa-cog"></i>
|
||||||
|
{% elif item.ext in ['iso', 'img', 'vhd', 'vmdk'] %}
|
||||||
|
<i class="fas fa-compact-disc"></i>
|
||||||
|
{% elif item.ext in ['psd', 'ai', 'eps'] %}
|
||||||
|
<i class="fas fa-palette"></i>
|
||||||
|
{% elif item.ext in ['bin', 'dat', 'db'] %}
|
||||||
|
<i class="fas fa-microchip"></i>
|
||||||
|
{% else %}
|
||||||
|
<i class="fas fa-file"></i>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 文件信息区域 - 支持自动换行 -->
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<!-- 文件名和标签行 - 自动换行 -->
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
{% if item.is_dir %}
|
||||||
|
<a class="font-semibold text-dark hover:text-primary text-base break-all" href="{{ url_for('index', path=item.path) }}">{{ item.name }}</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="font-semibold text-dark text-base break-all">{{ item.name }}</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- 文件类型标签 -->
|
||||||
|
{% if not item.is_dir %}
|
||||||
|
<span class="mac-badge mac-badge-sm file-type-badge flex-shrink-0">
|
||||||
|
<i class="fas fa-tag text-xs"></i>
|
||||||
|
{% if item.ext %}
|
||||||
|
{{ item.ext|upper }} -
|
||||||
|
{% if item.ext in ['mp3', 'wav', 'flac', 'aac', 'ogg', 'm4a', 'wma'] %}音频文件
|
||||||
|
{% elif item.ext in ['mp4', 'avi', 'mov', 'mkv', 'flv', 'wmv', 'webm', 'm4v'] %}视频文件
|
||||||
|
{% elif item.ext in ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'] %}图片文件
|
||||||
|
{% elif item.ext == 'pdf' %}PDF文档
|
||||||
|
{% elif item.ext in ['doc', 'docx'] %}Word文档
|
||||||
|
{% elif item.ext in ['xls', 'xlsx', 'csv'] %}Excel表格
|
||||||
|
{% elif item.ext in ['ppt', 'pptx'] %}演示文稿
|
||||||
|
{% elif item.ext in ['zip', 'rar', '7z', 'tar', 'gz', 'bz2'] %}压缩文件
|
||||||
|
{% elif item.ext in ['py', 'js', 'java', 'c', 'cpp', 'h', 'html', 'css', 'php', 'rb', 'go', 'rs', 'swift', 'kt'] %}代码文件
|
||||||
|
{% elif item.ext in ['txt', 'md', 'log', 'ini', 'cfg', 'conf'] %}文本文件
|
||||||
|
{% elif item.ext in ['exe', 'msi', 'dmg', 'pkg', 'deb', 'rpm', 'appimage'] %}可执行文件
|
||||||
|
{% elif item.ext in ['iso', 'img', 'vhd', 'vmdk'] %}磁盘镜像
|
||||||
|
{% elif item.ext in ['psd', 'ai', 'eps'] %}设计文件
|
||||||
|
{% elif item.ext in ['bin', 'dat', 'db'] %}二进制文件
|
||||||
|
{% else %}其他文件
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
未知类型
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="mac-badge mac-badge-sm folder-badge flex-shrink-0">
|
||||||
|
<i class="fas fa-folder-open text-xs"></i>
|
||||||
|
文件夹目录
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if item.previewable %}
|
||||||
|
<span class="mac-badge mac-badge-info flex-shrink-0"><i class="fas fa-eye"></i>可预览</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 文件详情 - 自动换行 -->
|
||||||
|
<div class="text-sm text-muted flex flex-wrap items-center gap-x-3 gap-y-1">
|
||||||
|
{% if item.is_dir %}
|
||||||
|
<span><i class="far fa-folder-open mr-1"></i>文件夹</span>
|
||||||
|
{% else %}
|
||||||
|
<span><i class="far fa-file mr-1"></i>{{ item.size or "未知大小" }}</span>
|
||||||
|
{% endif %}
|
||||||
|
<span><i class="far fa-clock mr-1"></i>{{ item.modified or "未知" }}</span>
|
||||||
|
{% if not item.is_dir %}
|
||||||
|
<span><i class="fas fa-hashtag mr-1"></i>扩展名: {{ item.ext|upper if item.ext else '无' }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 操作按钮区域 - 彩色但柔和 -->
|
||||||
|
<div class="mac-action-buttons">
|
||||||
|
<!-- 文件夹专用"打开"按钮 -->
|
||||||
|
{% if item.is_dir %}
|
||||||
|
<a class="mac-action-btn mac-action-btn-folder" data-tooltip="打开文件夹" href="{{ url_for('index', path=item.path) }}">
|
||||||
|
<i class="fas fa-folder-open"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if not item.is_dir %}
|
||||||
|
<a class="mac-action-btn mac-action-btn-download" data-tooltip="下载" href="{{ url_for('download', file_path=item.path) }}">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
</a>
|
||||||
|
{% if item.previewable %}
|
||||||
|
<a class="mac-action-btn mac-action-btn-preview" data-tooltip="预览" href="{{ url_for('preview', file_path=item.path) }}" target="_blank">
|
||||||
|
<i class="fas fa-eye"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<button class="mac-action-btn mac-action-btn-rename" data-tooltip="重命名" onclick="openRenamePopup('{{ item.path }}', '{{ item.name }}')">
|
||||||
|
<i class="fas fa-pen"></i>
|
||||||
|
</button>
|
||||||
|
<button class="mac-action-btn mac-action-btn-share" data-tooltip="分享" onclick="openSharePopup('{{ item.path }}')">
|
||||||
|
<i class="fas fa-link"></i>
|
||||||
|
</button>
|
||||||
|
<button class="mac-action-btn mac-action-btn-delete" data-tooltip="删除" onclick="openDeletePopup('{{ item.path }}', '{{ item.name }}')">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<div class="mac-alert mac-alert-info">
|
||||||
|
<i class="fas fa-info-circle text-primary text-xl"></i>
|
||||||
|
<div>
|
||||||
|
<div class="font-semibold text-dark">这里还没有文件</div>
|
||||||
|
<div class="text-muted text-sm mt-1">点击右上角上传文件或创建文件夹。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 上传弹窗 - MAC风格圆角 -->
|
||||||
|
<div id="uploadPopup" class="hidden fixed inset-0 items-center justify-center" style="display:none;">
|
||||||
|
<div class="absolute inset-0 bg-black/50" onclick="closePopup('uploadPopup')"></div>
|
||||||
|
<div class="mac-dialog relative w-[90%] max-w-lg mx-4 rounded-2xl overflow-hidden">
|
||||||
|
<div class="mac-card-header bg-gray-50 px-5 py-4">
|
||||||
|
<div class="flex items-center gap-2"><i class="fas fa-upload text-primary"></i><span class="font-semibold">上传文件</span></div>
|
||||||
|
<button class="mac-action-btn" onclick="closePopup('uploadPopup')"><i class="fas fa-times"></i></button>
|
||||||
|
</div>
|
||||||
|
<form method="POST" action="{{ url_for('upload') }}" enctype="multipart/form-data" class="p-5 space-y-4">
|
||||||
|
<input type="hidden" name="path" value="{{ path or '' }}">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-dark mb-2">选择文件(支持多选)</label>
|
||||||
|
<input class="mac-input w-full rounded-xl" type="file" name="file" multiple required>
|
||||||
|
<p class="text-muted text-xs mt-2"><i class="fas fa-info-circle"></i> 可同时选择多个文件上传</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-3 pt-2 flex-wrap">
|
||||||
|
<button type="button" class="mac-btn mac-btn-outline rounded-full" onclick="closePopup('uploadPopup')">取消</button>
|
||||||
|
<button type="submit" class="mac-btn mac-btn-success rounded-full"><i class="fas fa-cloud-upload-alt"></i>开始上传</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新建文件夹弹窗 -->
|
||||||
|
<div id="createFolderPopup" class="hidden fixed inset-0 items-center justify-center" style="display:none;">
|
||||||
|
<div class="absolute inset-0 bg-black/50" onclick="closePopup('createFolderPopup')"></div>
|
||||||
|
<div class="mac-dialog relative w-[90%] max-w-lg mx-4 rounded-2xl overflow-hidden">
|
||||||
|
<div class="mac-card-header bg-gray-50 px-5 py-4">
|
||||||
|
<div class="flex items-center gap-2"><i class="fas fa-folder-plus text-primary"></i><span class="font-semibold">新建文件夹</span></div>
|
||||||
|
<button class="mac-action-btn" onclick="closePopup('createFolderPopup')"><i class="fas fa-times"></i></button>
|
||||||
|
</div>
|
||||||
|
<form method="POST" action="{{ url_for('create_folder') }}" class="p-5 space-y-4">
|
||||||
|
<input type="hidden" name="path" value="{{ path or '' }}">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-dark mb-2">文件夹名称</label>
|
||||||
|
<input class="mac-input w-full rounded-xl" type="text" name="folder_name" placeholder="例如:学习资料" required>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-3 pt-2 flex-wrap">
|
||||||
|
<button type="button" class="mac-btn mac-btn-outline rounded-full" onclick="closePopup('createFolderPopup')">取消</button>
|
||||||
|
<button type="submit" class="mac-btn rounded-full"><i class="fas fa-check"></i>创建</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分享弹窗 -->
|
||||||
|
<div id="sharePopup" class="hidden fixed inset-0 items-center justify-center" style="display:none;">
|
||||||
|
<div class="absolute inset-0 bg-black/50" onclick="closePopup('sharePopup')"></div>
|
||||||
|
<div class="mac-dialog relative w-[90%] max-w-lg mx-4 rounded-2xl overflow-hidden">
|
||||||
|
<div class="mac-card-header bg-gray-50 px-5 py-4">
|
||||||
|
<div class="flex items-center gap-2"><i class="fas fa-link text-primary"></i><span class="font-semibold">分享</span></div>
|
||||||
|
<button class="mac-action-btn" onclick="closePopup('sharePopup')"><i class="fas fa-times"></i></button>
|
||||||
|
</div>
|
||||||
|
<form method="POST" action="{{ url_for('share') }}" class="p-5 space-y-4">
|
||||||
|
<input type="hidden" name="item_path" id="shareItemPathInput">
|
||||||
|
<div class="mac-alert mac-alert-info rounded-xl">
|
||||||
|
<i class="fas fa-info-circle text-primary text-xl"></i>
|
||||||
|
<div><div class="font-semibold text-dark">创建分享链接</div><div class="text-muted text-sm mt-1">提交后,后端会显示分享链接。</div></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-3 pt-2 flex-wrap">
|
||||||
|
<button type="button" class="mac-btn mac-btn-outline rounded-full" onclick="closePopup('sharePopup')">取消</button>
|
||||||
|
<button type="submit" class="mac-btn rounded-full"><i class="fas fa-link"></i>生成链接</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 重命名弹窗 -->
|
||||||
|
<div id="renamePopup" class="hidden fixed inset-0 items-center justify-center" style="display:none;">
|
||||||
|
<div class="absolute inset-0 bg-black/50" onclick="closePopup('renamePopup')"></div>
|
||||||
|
<div class="mac-dialog relative w-[90%] max-w-lg mx-4 rounded-2xl overflow-hidden">
|
||||||
|
<div class="mac-card-header bg-gray-50 px-5 py-4">
|
||||||
|
<div class="flex items-center gap-2"><i class="fas fa-pen text-primary"></i><span class="font-semibold">重命名</span></div>
|
||||||
|
<button class="mac-action-btn" onclick="closePopup('renamePopup')"><i class="fas fa-times"></i></button>
|
||||||
|
</div>
|
||||||
|
<form method="POST" action="{{ url_for('rename') }}" class="p-5 space-y-4">
|
||||||
|
<input type="hidden" name="path" value="{{ path or '' }}">
|
||||||
|
<input type="hidden" name="item_path" id="renameItemPathInput">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-dark mb-2">新名称</label>
|
||||||
|
<input class="mac-input w-full rounded-xl" type="text" name="new_name" id="renameNewNameInput" required>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-3 pt-2 flex-wrap">
|
||||||
|
<button type="button" class="mac-btn mac-btn-outline rounded-full" onclick="closePopup('renamePopup')">取消</button>
|
||||||
|
<button type="submit" class="mac-btn mac-btn-warning rounded-full"><i class="fas fa-check"></i>确认修改</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 删除弹窗 -->
|
||||||
|
<div id="deletePopup" class="hidden fixed inset-0 items-center justify-center" style="display:none;">
|
||||||
|
<div class="absolute inset-0 bg-black/50" onclick="closePopup('deletePopup')"></div>
|
||||||
|
<div class="mac-dialog relative w-[90%] max-w-lg mx-4 rounded-2xl overflow-hidden">
|
||||||
|
<div class="mac-card-header bg-gray-50 px-5 py-4">
|
||||||
|
<div class="flex items-center gap-2 text-danger"><i class="fas fa-trash"></i><span class="font-semibold">确认删除</span></div>
|
||||||
|
<button class="mac-action-btn" onclick="closePopup('deletePopup')"><i class="fas fa-times"></i></button>
|
||||||
|
</div>
|
||||||
|
<form method="POST" action="{{ url_for('delete') }}" class="p-5 space-y-4">
|
||||||
|
<input type="hidden" name="path" value="{{ path or '' }}">
|
||||||
|
<input type="hidden" name="item_path" id="deleteItemPathInput">
|
||||||
|
<div class="mac-alert mac-alert-danger rounded-xl">
|
||||||
|
<i class="fas fa-exclamation-triangle text-danger text-xl"></i>
|
||||||
|
<div><div class="font-semibold text-dark">你确定要删除 <span id="deleteNameSpan"></span> 吗?</div><div class="text-muted text-sm mt-1">删除后不可恢复。</div></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-3 pt-2 flex-wrap">
|
||||||
|
<button type="button" class="mac-btn mac-btn-outline rounded-full" onclick="closePopup('deletePopup')">取消</button>
|
||||||
|
<button type="submit" class="mac-btn mac-btn-danger rounded-full"><i class="fas fa-trash"></i>确认删除</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 浮动快速操作菜单 -->
|
||||||
|
<div id="floatMenu" class="hidden fixed bottom-24 right-6 left-6 sm:left-auto mac-dropdown z-50 rounded-xl shadow-lg">
|
||||||
|
<div class="mac-dropdown-item rounded-lg" onclick="showUploadPopup(); hideFloatMenu();"><i class="fas fa-upload text-primary"></i><span>上传文件</span></div>
|
||||||
|
<div class="mac-dropdown-item rounded-lg" onclick="showCreateFolderPopup(); hideFloatMenu();"><i class="fas fa-folder-plus text-success"></i><span>新建文件夹</span></div>
|
||||||
|
<div class="mac-dropdown-item rounded-lg" onclick="window.location.href='{{ url_for('shared_files') }}'; hideFloatMenu();"><i class="fas fa-share-alt text-info"></i><span>我的分享</span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* ========== 自适应空白系统 ========== */
|
||||||
|
/* 基础容器 - 响应式内边距 */
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主内容区域 - 自适应边距 */
|
||||||
|
main {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文件列表容器 - 动态边距 */
|
||||||
|
.file-list-container {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 >= 1200px (电脑) - 保持原样 */
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 24px;
|
||||||
|
padding-right: 24px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 992px - 1199px (小电脑/大平板横屏) - 较小空白 */
|
||||||
|
@media (min-width: 992px) and (max-width: 1199px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 768px - 991px (平板横屏) - 中等空白 */
|
||||||
|
@media (min-width: 768px) and (max-width: 991px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 28px;
|
||||||
|
padding-right: 28px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 18px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 640px - 767px (平板竖屏/大手机) - 较小空白 */
|
||||||
|
@media (min-width: 640px) and (max-width: 767px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 480px - 639px (手机) - 很小空白 */
|
||||||
|
@media (min-width: 480px) and (max-width: 639px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 360px - 479px (小手机) - 最小空白 */
|
||||||
|
@media (min-width: 360px) and (max-width: 479px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 < 360px (极小手机) - 边缘贴边 */
|
||||||
|
@media (max-width: 359px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文件类型图标颜色增强 */
|
||||||
|
.mac-file-icon.folder { background: linear-gradient(135deg, #60a5fa, #3b82f6); }
|
||||||
|
.mac-file-icon.image { background: linear-gradient(135deg, #38bdf8, #0ea5e9); }
|
||||||
|
.mac-file-icon.video { background: linear-gradient(135deg, #8b5cf6, #7c3aed); }
|
||||||
|
.mac-file-icon.audio { background: linear-gradient(135deg, #10b981, #059669); }
|
||||||
|
.mac-file-icon.pdf { background: linear-gradient(135deg, #f87171, #ef4444); }
|
||||||
|
.mac-file-icon.word { background: linear-gradient(135deg, #3b82f6, #2563eb); }
|
||||||
|
.mac-file-icon.excel { background: linear-gradient(135deg, #10b981, #059669); }
|
||||||
|
.mac-file-icon.powerpoint { background: linear-gradient(135deg, #f97316, #ea580c); }
|
||||||
|
.mac-file-icon.archive { background: linear-gradient(135deg, #f59e0b, #d97706); }
|
||||||
|
.mac-file-icon.code { background: linear-gradient(135deg, #6366f1, #4f46e5); }
|
||||||
|
.mac-file-icon.text { background: linear-gradient(135deg, #94a3b8, #64748b); }
|
||||||
|
.mac-file-icon.executable { background: linear-gradient(135deg, #ef4444, #dc2626); }
|
||||||
|
.mac-file-icon.disk { background: linear-gradient(135deg, #06b6d4, #0891b2); }
|
||||||
|
.mac-file-icon.design { background: linear-gradient(135deg, #ec4899, #db2777); }
|
||||||
|
.mac-file-icon.binary { background: linear-gradient(135deg, #78716c, #57534e); }
|
||||||
|
.mac-file-icon.document { background: linear-gradient(135deg, #a855f7, #9333ea); }
|
||||||
|
|
||||||
|
/* 文件列表项 */
|
||||||
|
.mac-file-item {
|
||||||
|
background: var(--repo-color-bg);
|
||||||
|
border: 1px solid var(--repo-color-border);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 14px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-file-item:hover {
|
||||||
|
background: var(--repo-color-bg-hover, #f8fafc);
|
||||||
|
border-color: var(--repo-color-border-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端布局调整 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.mac-file-item {
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 14px;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.mac-file-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
font-size: 20px;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
.mac-action-buttons {
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 桌面端保持行内布局 */
|
||||||
|
@media (min-width: 641px) {
|
||||||
|
.mac-file-item {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.mac-action-buttons {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文件名自动换行 */
|
||||||
|
.break-all {
|
||||||
|
word-break: break-all;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文件类型标签 */
|
||||||
|
.file-type-badge {
|
||||||
|
background: rgba(59, 130, 246, 0.1);
|
||||||
|
color: var(--repo-color-primary);
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.folder-badge {
|
||||||
|
background: rgba(16, 185, 129, 0.1);
|
||||||
|
color: var(--repo-color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-badge-sm {
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: 11px;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-badge-info {
|
||||||
|
background: rgba(59, 130, 246, 0.1);
|
||||||
|
color: var(--repo-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮 - 柔和彩色 */
|
||||||
|
.mac-action-btn {
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 12px;
|
||||||
|
color: #6b7280;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-action-btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文件夹按钮 */
|
||||||
|
.mac-action-btn-folder {
|
||||||
|
background: rgba(59, 130, 246, 0.08);
|
||||||
|
border-color: rgba(59, 130, 246, 0.2);
|
||||||
|
color: #3b82f6;
|
||||||
|
}
|
||||||
|
.mac-action-btn-folder:hover {
|
||||||
|
background: rgba(59, 130, 246, 0.15);
|
||||||
|
border-color: #3b82f6;
|
||||||
|
color: #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 下载按钮 */
|
||||||
|
.mac-action-btn-download {
|
||||||
|
background: rgba(16, 185, 129, 0.08);
|
||||||
|
border-color: rgba(16, 185, 129, 0.2);
|
||||||
|
color: #10b981;
|
||||||
|
}
|
||||||
|
.mac-action-btn-download:hover {
|
||||||
|
background: rgba(16, 185, 129, 0.15);
|
||||||
|
border-color: #10b981;
|
||||||
|
color: #059669;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 预览按钮 */
|
||||||
|
.mac-action-btn-preview {
|
||||||
|
background: rgba(139, 92, 246, 0.08);
|
||||||
|
border-color: rgba(139, 92, 246, 0.2);
|
||||||
|
color: #8b5cf6;
|
||||||
|
}
|
||||||
|
.mac-action-btn-preview:hover {
|
||||||
|
background: rgba(139, 92, 246, 0.15);
|
||||||
|
border-color: #8b5cf6;
|
||||||
|
color: #7c3aed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 重命名按钮 */
|
||||||
|
.mac-action-btn-rename {
|
||||||
|
background: rgba(245, 158, 11, 0.08);
|
||||||
|
border-color: rgba(245, 158, 11, 0.2);
|
||||||
|
color: #f59e0b;
|
||||||
|
}
|
||||||
|
.mac-action-btn-rename:hover {
|
||||||
|
background: rgba(245, 158, 11, 0.15);
|
||||||
|
border-color: #f59e0b;
|
||||||
|
color: #d97706;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分享按钮 */
|
||||||
|
.mac-action-btn-share {
|
||||||
|
background: rgba(6, 182, 212, 0.08);
|
||||||
|
border-color: rgba(6, 182, 212, 0.2);
|
||||||
|
color: #06b6d4;
|
||||||
|
}
|
||||||
|
.mac-action-btn-share:hover {
|
||||||
|
background: rgba(6, 182, 212, 0.15);
|
||||||
|
border-color: #06b6d4;
|
||||||
|
color: #0891b2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 删除按钮 */
|
||||||
|
.mac-action-btn-delete {
|
||||||
|
background: rgba(239, 68, 68, 0.08);
|
||||||
|
border-color: rgba(239, 68, 68, 0.2);
|
||||||
|
color: #ef4444;
|
||||||
|
}
|
||||||
|
.mac-action-btn-delete:hover {
|
||||||
|
background: rgba(239, 68, 68, 0.15);
|
||||||
|
border-color: #ef4444;
|
||||||
|
color: #dc2626;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端按钮触摸优化 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.mac-action-btn {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
.mac-action-buttons {
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 动画 */
|
||||||
|
@keyframes slideInRight {
|
||||||
|
from { transform: translateX(100%); opacity: 0; }
|
||||||
|
to { transform: translateX(0); opacity: 1; }
|
||||||
|
}
|
||||||
|
.animate-slideInRight { animation: slideInRight 0.3s ease-out; }
|
||||||
|
|
||||||
|
/* 面包屑移动端适配 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.mac-breadcrumb {
|
||||||
|
padding: 10px 14px;
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
.mac-breadcrumb .flex {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
.mac-breadcrumb-item {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flash消息移动端适配 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
#flashMessages {
|
||||||
|
top: 70px;
|
||||||
|
right: 12px;
|
||||||
|
left: 12px;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片移动端边距优化 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.mac-card {
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
.mac-card-header {
|
||||||
|
padding: 14px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 模态框MAC风格圆角 */
|
||||||
|
.mac-dialog {
|
||||||
|
border-radius: 24px !important;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-card-header {
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 面包屑样式 */
|
||||||
|
.mac-breadcrumb {
|
||||||
|
background: var(--repo-color-bg);
|
||||||
|
border: 1px solid var(--repo-color-border);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-breadcrumb-item {
|
||||||
|
color: var(--repo-color-text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-breadcrumb-item a {
|
||||||
|
color: var(--repo-color-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-breadcrumb-item a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片样式 */
|
||||||
|
.mac-card {
|
||||||
|
background: var(--repo-color-bg);
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px solid var(--repo-color-border);
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-card:hover {
|
||||||
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-card-header {
|
||||||
|
background: var(--repo-color-bg-secondary);
|
||||||
|
padding: 18px 24px;
|
||||||
|
border-bottom: 1px solid var(--repo-color-border);
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--repo-color-text);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮样式 */
|
||||||
|
.mac-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: var(--repo-color-primary);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 9999px;
|
||||||
|
padding: 10px 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn:hover {
|
||||||
|
background: var(--repo-color-primary-hover);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn-sm {
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn-outline {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--repo-color-primary);
|
||||||
|
border: 1px solid var(--repo-color-border);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn-outline:hover {
|
||||||
|
background: rgba(59, 130, 246, 0.08);
|
||||||
|
border-color: var(--repo-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn-success {
|
||||||
|
background: var(--repo-color-success);
|
||||||
|
}
|
||||||
|
.mac-btn-success:hover {
|
||||||
|
background: #059669;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn-warning {
|
||||||
|
background: var(--repo-color-warning);
|
||||||
|
}
|
||||||
|
.mac-btn-warning:hover {
|
||||||
|
background: #d97706;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn-danger {
|
||||||
|
background: var(--repo-color-danger);
|
||||||
|
}
|
||||||
|
.mac-btn-danger:hover {
|
||||||
|
background: #dc2626;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 警告框 */
|
||||||
|
.mac-alert {
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 14px 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-alert-success {
|
||||||
|
background: rgba(16, 185, 129, 0.08);
|
||||||
|
border: 1px solid rgba(16, 185, 129, 0.2);
|
||||||
|
}
|
||||||
|
.mac-alert-danger {
|
||||||
|
background: rgba(239, 68, 68, 0.08);
|
||||||
|
border: 1px solid rgba(239, 68, 68, 0.2);
|
||||||
|
}
|
||||||
|
.mac-alert-warning {
|
||||||
|
background: rgba(245, 158, 11, 0.08);
|
||||||
|
border: 1px solid rgba(245, 158, 11, 0.2);
|
||||||
|
}
|
||||||
|
.mac-alert-info {
|
||||||
|
background: rgba(59, 130, 246, 0.08);
|
||||||
|
border: 1px solid rgba(59, 130, 246, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 浮动按钮 */
|
||||||
|
.mac-float-btn {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--repo-color-primary);
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
z-index: 100;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-float-btn:hover {
|
||||||
|
background: var(--repo-color-primary-hover);
|
||||||
|
transform: translateY(-2px) scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 下拉菜单 */
|
||||||
|
.mac-dropdown {
|
||||||
|
background: var(--repo-color-bg);
|
||||||
|
border: 1px solid var(--repo-color-border);
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 8px;
|
||||||
|
min-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-dropdown-item {
|
||||||
|
padding: 10px 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
color: var(--repo-color-text);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-dropdown-item:hover {
|
||||||
|
background: var(--repo-color-bg-hover);
|
||||||
|
color: var(--repo-color-primary);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function hideFloatMenu() { const m = document.getElementById('floatMenu'); if (m) m.classList.add('hidden'); }
|
||||||
|
const floatBtn = document.querySelector('.mac-float-btn');
|
||||||
|
const floatMenu = document.getElementById('floatMenu');
|
||||||
|
if (floatBtn && floatMenu) {
|
||||||
|
floatBtn.addEventListener('click', function(e) { e.stopPropagation(); floatMenu.classList.toggle('hidden'); });
|
||||||
|
document.addEventListener('click', hideFloatMenu);
|
||||||
|
}
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
['uploadPopup','createFolderPopup','sharePopup','renamePopup','deletePopup'].forEach(id => { const el = document.getElementById(id); if(el) el.classList.add('hidden'); });
|
||||||
|
hideFloatMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function openSharePopup(itemPath) {
|
||||||
|
document.getElementById('shareItemPathInput').value = itemPath;
|
||||||
|
const p = document.getElementById('sharePopup');
|
||||||
|
p.classList.remove('hidden');
|
||||||
|
p.style.cssText = 'display:flex!important;position:fixed!important;inset:0!important;background-color:rgba(0,0,0,0.5)!important;align-items:center!important;justify-content:center!important;z-index:9999!important;';
|
||||||
|
}
|
||||||
|
function openRenamePopup(itemPath, name) {
|
||||||
|
document.getElementById('renameItemPathInput').value = itemPath;
|
||||||
|
document.getElementById('renameNewNameInput').value = name;
|
||||||
|
const p = document.getElementById('renamePopup');
|
||||||
|
p.classList.remove('hidden');
|
||||||
|
p.style.cssText = 'display:flex!important;position:fixed!important;inset:0!important;background-color:rgba(0,0,0,0.5)!important;align-items:center!important;justify-content:center!important;z-index:9999!important;';
|
||||||
|
}
|
||||||
|
function openDeletePopup(itemPath, name) {
|
||||||
|
document.getElementById('deleteItemPathInput').value = itemPath;
|
||||||
|
document.getElementById('deleteNameSpan').textContent = name;
|
||||||
|
const p = document.getElementById('deletePopup');
|
||||||
|
p.classList.remove('hidden');
|
||||||
|
p.style.cssText = 'display:flex!important;position:fixed!important;inset:0!important;background-color:rgba(0,0,0,0.5)!important;align-items:center!important;justify-content:center!important;z-index:9999!important;';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
{% extends "base.html" %}␍
|
||||||
|
{% block title %}登录 - DVS云盘{% endblock %}␍
|
||||||
|
␍
|
||||||
|
{% block content %}␍
|
||||||
|
<div class="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">␍
|
||||||
|
<div class="max-w-md w-full space-y-8">␍
|
||||||
|
<!-- Flash 消息 -->␍
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}␍
|
||||||
|
{% if messages %}␍
|
||||||
|
{% for category, message in messages %}␍
|
||||||
|
<div class="mac-alert mac-alert-{{ category }}">␍
|
||||||
|
<i class="fas fa-{{ 'check-circle' if category == 'success' else 'exclamation-circle' if category == 'danger' else 'info-circle' }}"></i>␍
|
||||||
|
<span>{{ message|safe }}</span>␍
|
||||||
|
<button type="button" class="ml-auto text-gray-400 hover:text-gray-600" onclick="this.parentElement.remove()"><i class="fas fa-times"></i></button>␍
|
||||||
|
</div>␍
|
||||||
|
{% endfor %}␍
|
||||||
|
{% endif %}␍
|
||||||
|
{% endwith %}␍
|
||||||
|
␍
|
||||||
|
<div class="mac-card">␍
|
||||||
|
<div class="mac-card-header text-center justify-center">␍
|
||||||
|
<div>␍
|
||||||
|
<i class="fas fa-cloud text-4xl text-primary"></i>␍
|
||||||
|
<h2 class="mt-3 text-2xl font-bold text-dark">DVS云盘</h2>␍
|
||||||
|
<p class="text-muted text-sm mt-1">安全、高效的云端存储解决方案</p>␍
|
||||||
|
</div>␍
|
||||||
|
</div>␍
|
||||||
|
<div class="p-6">␍
|
||||||
|
<form method="POST" action="/login">␍
|
||||||
|
<div class="mb-4">␍
|
||||||
|
<label class="block text-dark font-medium mb-2"><i class="fas fa-user mr-2"></i>用户名</label>␍
|
||||||
|
<input type="text" class="mac-input w-full" name="username" required placeholder="请输入用户名">␍
|
||||||
|
</div>␍
|
||||||
|
<div class="mb-6">␍
|
||||||
|
<label class="block text-dark font-medium mb-2"><i class="fas fa-lock mr-2"></i>密码</label>␍
|
||||||
|
<input type="password" class="mac-input w-full" name="password" required placeholder="请输入密码" id="password">␍
|
||||||
|
</div>␍
|
||||||
|
<button type="submit" class="mac-btn w-full"><i class="fas fa-sign-in-alt mr-2"></i>登录</button>␍
|
||||||
|
</form>␍
|
||||||
|
<div class="border-t border-gray-200 my-6"></div>␍
|
||||||
|
<div class="text-center">␍
|
||||||
|
<p class="text-muted">还没有账户? <a href="/register" class="text-primary font-medium">立即注册</a></p>␍
|
||||||
|
</div>␍
|
||||||
|
<div class="flex justify-center gap-6 mt-6">␍
|
||||||
|
<div class="flex items-center gap-2 text-muted text-sm"><i class="fas fa-shield-alt text-primary"></i><span>数据加密</span></div>␍
|
||||||
|
<div class="flex items-center gap-2 text-muted text-sm"><i class="fas fa-bolt text-warning"></i><span>高速传输</span></div>␍
|
||||||
|
<div class="flex items-center gap-2 text-muted text-sm"><i class="fas fa-sync text-success"></i><span>多端同步</span></div>␍
|
||||||
|
</div>␍
|
||||||
|
</div>␍
|
||||||
|
</div>␍
|
||||||
|
␍
|
||||||
|
<div class="text-center">␍
|
||||||
|
<p class="text-muted text-xs"><i class="fas fa-shield-alt mr-1"></i>安全提示:请妥善保管您的登录凭证</p>␍
|
||||||
|
</div>␍
|
||||||
|
␍
|
||||||
|
<!-- DVS 宣言 -->␍
|
||||||
|
<div class="mac-card mt-8">␍
|
||||||
|
<div class="p-6 text-center">␍
|
||||||
|
<div class="text-primary font-bold text-lg mb-4">🚀 DVS云盘</div>␍
|
||||||
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">␍
|
||||||
|
<div class="text-sm text-muted">安全的代码是优美的代码</div>␍
|
||||||
|
<div class="text-sm text-muted">用户友好的设计是简单的设计</div>␍
|
||||||
|
<div class="text-sm text-muted">每一次302重定向都是明了的防护</div>␍
|
||||||
|
<div class="text-sm text-muted">每一行日志都是错误的明确记录</div>␍
|
||||||
|
</div>␍
|
||||||
|
␍
|
||||||
|
</div>␍
|
||||||
|
</div>␍
|
||||||
|
</div>␍
|
||||||
|
</div>␍
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}音频预览 - DVS云盘{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<div class="mac-card">
|
||||||
|
<div class="mac-card-header">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fas fa-music mr-3"></i>
|
||||||
|
<h5 class="mb-0">音频预览</h5>
|
||||||
|
</div>
|
||||||
|
<a href="{{ url_for('index') }}" class="mac-btn mac-btn-sm">
|
||||||
|
<i class="fas fa-arrow-left mr-2"></i>返回
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="text-center">
|
||||||
|
<audio controls class="w-full max-w-2xl mx-auto rounded-lg shadow-mac">
|
||||||
|
<source src="{{ file_path }}" type="audio/mpeg">
|
||||||
|
您的浏览器不支持音频播放。
|
||||||
|
</audio>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}PDF预览 - DVS云盘{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<div class="mac-card">
|
||||||
|
<div class="mac-card-header">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fas fa-file-pdf mr-3"></i>
|
||||||
|
<h5 class="mb-0">PDF预览</h5>
|
||||||
|
</div>
|
||||||
|
<a href="{{ url_for('index') }}" class="mac-btn mac-btn-sm">
|
||||||
|
<i class="fas fa-arrow-left mr-2"></i>返回
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="rounded-xl overflow-hidden shadow-mac">
|
||||||
|
<iframe src="{{ file_path }}" class="w-full" style="height: 80vh;" frameborder="0"></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}视频预览 - DVS云盘{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<div class="mac-card">
|
||||||
|
<div class="mac-card-header">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fas fa-video mr-3"></i>
|
||||||
|
<h5 class="mb-0">视频预览</h5>
|
||||||
|
</div>
|
||||||
|
<a href="{{ url_for('index') }}" class="mac-btn mac-btn-sm">
|
||||||
|
<i class="fas fa-arrow-left mr-2"></i>返回
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="text-center">
|
||||||
|
<video controls class="w-full rounded-xl shadow-mac" style="max-height: 70vh;">
|
||||||
|
<source src="{{ file_path }}" type="video/mp4">
|
||||||
|
您的浏览器不支持视频播放。
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}注册 - DVS云盘{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mx-auto px-4 py-12">
|
||||||
|
<div class="max-w-md mx-auto">
|
||||||
|
<div class="mac-card mb-8">
|
||||||
|
<div class="mac-card-header">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fas fa-user-plus mr-3"></i>
|
||||||
|
<h5 class="mb-0">注册账户</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% if messages %}
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<div class="mac-alert mac-alert-{{ 'success' if category == 'success' else 'info' if category == 'info' else 'warning' if category == 'warning' else 'danger' }} mb-6">
|
||||||
|
<i class="fas fa-{{ 'check-circle' if category == 'success' else 'info-circle' if category == 'info' else 'exclamation-triangle' if category == 'warning' else 'times-circle' }}"></i>
|
||||||
|
<span>{{ message|safe }}</span>
|
||||||
|
<button type="button" class="mac-action-btn ml-auto" onclick="this.parentElement.remove()">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<form method="POST" action="{{ url_for('register') }}">
|
||||||
|
<div class="mb-6">
|
||||||
|
<label for="username" class="block text-gray-700 mb-2 font-medium">
|
||||||
|
<i class="fas fa-user mr-2"></i>用户名
|
||||||
|
</label>
|
||||||
|
<input type="text" class="mac-input w-full" id="username" name="username" required
|
||||||
|
placeholder="请输入用户名" minlength="3" maxlength="20">
|
||||||
|
<div class="text-gray-500 text-sm mt-2">用户名长度3-20个字符,支持字母、数字和下划线</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<label for="password" class="block text-gray-700 mb-2 font-medium">
|
||||||
|
<i class="fas fa-lock mr-2"></i>密码
|
||||||
|
</label>
|
||||||
|
<input type="password" class="mac-input w-full" id="password" name="password" required
|
||||||
|
placeholder="请输入密码" minlength="6">
|
||||||
|
<div class="text-gray-500 text-sm mt-2">密码长度至少6个字符</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<label for="confirm-password" class="block text-gray-700 mb-2 font-medium">
|
||||||
|
<i class="fas fa-lock mr-2"></i>确认密码
|
||||||
|
</label>
|
||||||
|
<input type="password" class="mac-input w-full" id="confirm-password" name="confirm-password" required
|
||||||
|
placeholder="请再次输入密码">
|
||||||
|
<div class="text-gray-500 text-sm mt-2">请再次输入密码以确保一致性</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-8">
|
||||||
|
<label for="invitation_code" class="block text-gray-700 mb-2 font-medium">
|
||||||
|
<i class="fas fa-key mr-2"></i>邀请码
|
||||||
|
</label>
|
||||||
|
<input type="text" class="mac-input w-full" id="invitation_code" name="invitation_code" required
|
||||||
|
placeholder="请输入邀请码">
|
||||||
|
<div class="text-gray-500 text-sm mt-2">请联系管理员获取邀请码</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<input class="mac-checkbox mr-3" type="checkbox" id="agreeTerms" required>
|
||||||
|
<label class="text-gray-700 text-sm" for="agreeTerms">
|
||||||
|
我已阅读并同意
|
||||||
|
<a href="#" class="text-blue-500 hover:text-blue-600">服务条款</a>
|
||||||
|
和
|
||||||
|
<a href="#" class="text-blue-500 hover:text-blue-600">隐私政策</a>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<button type="submit" class="mac-btn mac-btn-lg w-full">
|
||||||
|
<i class="fas fa-user-plus mr-2"></i>注册账户
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="border-t border-gray-200 pt-6 text-center">
|
||||||
|
<p class="text-gray-600 mb-0">
|
||||||
|
已有账户?
|
||||||
|
<a href="{{ url_for('login') }}" class="text-blue-500 font-medium hover:text-blue-600">
|
||||||
|
<i class="fas fa-sign-in-alt mr-1"></i>立即登录
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mac-card">
|
||||||
|
<div class="mac-card-header">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fas fa-crown text-yellow-500 mr-3"></i>
|
||||||
|
<h5 class="mb-0">VIP专属特权</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
<div class="grid grid-cols-3 gap-6 text-center">
|
||||||
|
<div class="feature-item">
|
||||||
|
<div class="w-12 h-12 bg-blue-100 rounded-2xl flex items-center justify-center mx-auto mb-3">
|
||||||
|
<i class="fas fa-cloud-upload-alt text-blue-500 text-xl"></i>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm font-medium text-gray-700">超大存储</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<div class="w-12 h-12 bg-green-100 rounded-2xl flex items-center justify-center mx-auto mb-3">
|
||||||
|
<i class="fas fa-bolt text-green-500 text-xl"></i>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm font-medium text-gray-700">极速传输</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<div class="w-12 h-12 bg-red-100 rounded-2xl flex items-center justify-center mx-auto mb-3">
|
||||||
|
<i class="fas fa-shield-alt text-red-500 text-xl"></i>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm font-medium text-gray-700">高级安全</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.feature-item {
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
.feature-item:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// 密码确认验证
|
||||||
|
const password = document.getElementById('password');
|
||||||
|
const confirmPassword = document.getElementById('confirm-password');
|
||||||
|
|
||||||
|
function validatePassword() {
|
||||||
|
if (password.value !== confirmPassword.value) {
|
||||||
|
confirmPassword.setCustomValidity('两次输入的密码不一致');
|
||||||
|
} else {
|
||||||
|
confirmPassword.setCustomValidity('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
password.addEventListener('change', validatePassword);
|
||||||
|
confirmPassword.addEventListener('keyup', validatePassword);
|
||||||
|
|
||||||
|
// 用户名实时验证
|
||||||
|
const username = document.getElementById('username');
|
||||||
|
username.addEventListener('input', function() {
|
||||||
|
const usernameRegex = /^[a-zA-Z0-9_]{3,20}$/;
|
||||||
|
if (!usernameRegex.test(username.value)) {
|
||||||
|
username.setCustomValidity('用户名只能包含字母、数字和下划线,长度3-20位');
|
||||||
|
} else {
|
||||||
|
username.setCustomValidity('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 表单提交前的最终验证
|
||||||
|
document.querySelector('form').addEventListener('submit', function(e) {
|
||||||
|
validatePassword();
|
||||||
|
|
||||||
|
if (!document.getElementById('agreeTerms').checked) {
|
||||||
|
e.preventDefault();
|
||||||
|
alert('请同意服务条款和隐私政策');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}搜索结果 - DVS云盘{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<!-- 面包屑导航 -->
|
||||||
|
<div class="mac-breadcrumb mb-8">
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
{% for crumb in breadcrumbs %}
|
||||||
|
{% if loop.last %}
|
||||||
|
<span class="mac-breadcrumb-item text-dark font-medium">{{ crumb.name }}</span>
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ url_for('index', path=crumb.path) }}" class="mac-breadcrumb-item">
|
||||||
|
{{ crumb.name }}
|
||||||
|
</a>
|
||||||
|
<i class="fas fa-chevron-right text-gray-400"></i>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 搜索结果统计 -->
|
||||||
|
<div class="mac-card mb-8">
|
||||||
|
<div class="mac-card-header">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fas fa-search mr-3"></i>
|
||||||
|
<h5 class="mb-0">搜索结果</h5>
|
||||||
|
<span class="mac-badge mac-badge-primary ml-3">{{ results|length }} 个匹配项</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
<p class="text-gray-700">
|
||||||
|
搜索关键词: <strong>"{{ query }}"</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 搜索结果列表 -->
|
||||||
|
<div class="mac-card">
|
||||||
|
<div class="mac-card-header">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fas fa-list mr-3"></i>
|
||||||
|
<h5 class="mb-0">搜索结果列表</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
{% if results %}
|
||||||
|
<div class="space-y-3">
|
||||||
|
{% for item in results %}
|
||||||
|
<div class="mac-file-item">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="mr-4">
|
||||||
|
{% if item.is_dir %}
|
||||||
|
<div class="mac-file-icon folder">
|
||||||
|
<i class="fas fa-folder"></i>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
{% if item.ext in ['png', 'jpg', 'jpeg', 'gif'] %}
|
||||||
|
<div class="mac-file-icon image">
|
||||||
|
<i class="fas fa-image"></i>
|
||||||
|
</div>
|
||||||
|
{% elif item.ext in ['mp4', 'avi', 'mov'] %}
|
||||||
|
<div class="mac-file-icon video">
|
||||||
|
<i class="fas fa-video"></i>
|
||||||
|
</div>
|
||||||
|
{% elif item.ext in ['mp3', 'wav'] %}
|
||||||
|
<div class="mac-file-icon audio">
|
||||||
|
<i class="fas fa-music"></i>
|
||||||
|
</div>
|
||||||
|
{% elif item.ext == 'pdf' %}
|
||||||
|
<div class="mac-file-icon pdf">
|
||||||
|
<i class="fas fa-file-pdf"></i>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="mac-file-icon document">
|
||||||
|
<i class="fas fa-file"></i>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="mb-1">
|
||||||
|
<strong class="text-dark">{{ item.name }}</strong>
|
||||||
|
</div>
|
||||||
|
<div class="text-sm text-gray-600">
|
||||||
|
{% if item.is_dir %}
|
||||||
|
文件夹 • {{ item.path }}
|
||||||
|
{% else %}
|
||||||
|
文件 • {{ item.size }} • {{ item.path }}
|
||||||
|
{% endif %}
|
||||||
|
• 修改时间: {{ item.modified }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mac-action-buttons">
|
||||||
|
{% if item.is_dir %}
|
||||||
|
<a href="{{ url_for('index', path=item.path) }}" class="mac-action-btn" title="打开文件夹">
|
||||||
|
<i class="fas fa-folder-open"></i>
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
{% if item.previewable %}
|
||||||
|
<a href="{{ url_for('preview', file_path=item.path) }}" class="mac-action-btn" title="预览" target="_blank">
|
||||||
|
<i class="fas fa-eye"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('download', file_path=item.path) }}" class="mac-action-btn" title="下载">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="text-center py-12">
|
||||||
|
<i class="fas fa-search text-gray-400 text-5xl mb-4"></i>
|
||||||
|
<h5 class="text-gray-600 text-lg font-medium mb-2">没有找到匹配的结果</h5>
|
||||||
|
<p class="text-gray-500 mb-6">请尝试使用其他关键词搜索</p>
|
||||||
|
<a href="{{ url_for('index', path=path) }}" class="mac-btn">
|
||||||
|
<i class="fas fa-arrow-left mr-2"></i>返回文件列表
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,618 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}已分享文件 - DVS云盘{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<!-- Flash 消息 -->
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% if messages %}
|
||||||
|
<div id="flashMessages" class="fixed top-20 right-4 left-4 md:left-auto z-50 space-y-2 max-w-md">
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<div class="mac-alert mac-alert-{{ category }} animate-slideInRight" data-duration="5000">
|
||||||
|
<div class="flex items-start gap-3">
|
||||||
|
{% if category == 'success' %}
|
||||||
|
<i class="fas fa-check-circle text-success text-xl"></i>
|
||||||
|
{% elif category == 'danger' %}
|
||||||
|
<i class="fas fa-exclamation-circle text-danger text-xl"></i>
|
||||||
|
{% elif category == 'warning' %}
|
||||||
|
<i class="fas fa-exclamation-triangle text-warning text-xl"></i>
|
||||||
|
{% else %}
|
||||||
|
<i class="fas fa-info-circle text-primary text-xl"></i>
|
||||||
|
{% endif %}
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="font-medium">
|
||||||
|
{% if category == 'success' %}成功{% elif category == 'danger' %}错误{% elif category == 'warning' %}警告{% else %}提示{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="text-sm mt-1">{{ message|safe }}</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="text-gray-400 hover:text-gray-600" onclick="this.parentElement.parentElement.remove()">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.querySelectorAll('#flashMessages .mac-alert').forEach(msg => {
|
||||||
|
const duration = parseInt(msg.getAttribute('data-duration')) || 5000;
|
||||||
|
setTimeout(() => { msg.style.opacity = '0'; setTimeout(() => msg.remove(), 300); }, duration);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
|
||||||
|
<div>
|
||||||
|
<h1 class="text-2xl font-bold text-dark">已分享文件</h1>
|
||||||
|
<p class="text-muted mt-1 text-sm">管理你分享的文件和文件夹</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-3 flex-wrap">
|
||||||
|
<a href="{{ url_for('index') }}" class="mac-btn mac-btn-sm mac-btn-outline">
|
||||||
|
<i class="fas fa-arrow-left"></i><span>返回文件列表</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mac-card">
|
||||||
|
<div class="mac-card-header">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<i class="fas fa-share-alt text-primary"></i><span>分享列表</span>
|
||||||
|
</div>
|
||||||
|
<span class="mac-badge mac-badge-primary">共 {{ shared_items|length }} 个分享</span>
|
||||||
|
</div>
|
||||||
|
<div class="file-list-container">
|
||||||
|
{% if shared_items %}
|
||||||
|
{% for item in shared_items %}
|
||||||
|
<div class="mac-file-item">
|
||||||
|
<!-- 图标区域 -->
|
||||||
|
<div class="mac-file-icon {% if item.is_dir %}folder{% else %}document{% endif %}">
|
||||||
|
{% if item.is_dir %}
|
||||||
|
<i class="fas fa-folder"></i>
|
||||||
|
{% else %}
|
||||||
|
<i class="fas fa-file-share"></i>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 文件信息区域 - 支持自动换行 -->
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<!-- 文件名和标签行 - 自动换行 -->
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<span class="font-semibold text-dark text-base break-all">{{ item.name }}</span>
|
||||||
|
|
||||||
|
<!-- 文件类型标签 -->
|
||||||
|
{% if not item.is_dir %}
|
||||||
|
<span class="mac-badge mac-badge-sm file-type-badge flex-shrink-0">
|
||||||
|
<i class="fas fa-tag text-xs"></i>
|
||||||
|
分享文件
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="mac-badge mac-badge-sm folder-badge flex-shrink-0">
|
||||||
|
<i class="fas fa-folder-open text-xs"></i>
|
||||||
|
分享文件夹
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 文件详情 - 自动换行 -->
|
||||||
|
<div class="text-sm text-muted flex flex-wrap items-center gap-x-3 gap-y-1">
|
||||||
|
<span><i class="fas fa-link mr-1"></i>分享ID: {{ item.share_id }}</span>
|
||||||
|
<span><i class="far fa-calendar-alt mr-1"></i>创建时间: {{ item.created_at }}</span>
|
||||||
|
<span><i class="fas fa-download mr-1"></i>下载次数: {{ item.downloads }}</span>
|
||||||
|
<span><i class="far fa-folder mr-1"></i>路径: {{ item.path }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 操作按钮区域 - 彩色但柔和 -->
|
||||||
|
<div class="mac-action-buttons">
|
||||||
|
<a class="mac-action-btn mac-action-btn-share" data-tooltip="访问分享链接" href="{{ url_for('download_shared', share_id=item.share_id) }}" target="_blank">
|
||||||
|
<i class="fas fa-external-link-alt"></i>
|
||||||
|
</a>
|
||||||
|
<button class="mac-action-btn mac-action-btn-delete unshare-btn" data-tooltip="取消分享" data-share-id="{{ item.share_id }}" data-item-name="{{ item.name }}">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<div class="mac-alert mac-alert-info">
|
||||||
|
<i class="fas fa-info-circle text-primary text-xl"></i>
|
||||||
|
<div>
|
||||||
|
<div class="font-semibold text-dark">还没有分享任何文件</div>
|
||||||
|
<div class="text-muted text-sm mt-1">从文件列表中选择文件或文件夹并点击"分享"按钮来创建分享链接。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center py-6">
|
||||||
|
<a href="{{ url_for('index') }}" class="mac-btn">
|
||||||
|
<i class="fas fa-folder mr-2"></i>浏览文件
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 取消分享弹窗 -->
|
||||||
|
<div id="unshareDialog" class="hidden fixed inset-0 items-center justify-center" style="display:none;">
|
||||||
|
<div class="absolute inset-0 bg-black/50" onclick="closeDialog()"></div>
|
||||||
|
<div class="mac-dialog relative w-[90%] max-w-lg mx-4 rounded-2xl overflow-hidden">
|
||||||
|
<div class="mac-card-header bg-gray-50 px-5 py-4">
|
||||||
|
<div class="flex items-center gap-2 text-danger"><i class="fas fa-trash"></i><span class="font-semibold">取消分享</span></div>
|
||||||
|
<button class="mac-action-btn mac-dialog-close" onclick="closeDialog()"><i class="fas fa-times"></i></button>
|
||||||
|
</div>
|
||||||
|
<form id="unshareForm" method="POST" action="{{ url_for('unshare') }}" class="p-5 space-y-4">
|
||||||
|
<input type="hidden" id="unshare_share_id" name="share_id">
|
||||||
|
<div class="mac-alert mac-alert-warning rounded-xl">
|
||||||
|
<i class="fas fa-exclamation-triangle text-warning text-xl"></i>
|
||||||
|
<div>
|
||||||
|
<div class="font-semibold text-dark">确定要取消分享 "<span id="unshare_item_name" class="font-medium"></span>" 吗?</div>
|
||||||
|
<div class="text-muted text-sm mt-1">取消分享后,已分享的链接将立即失效。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-3 pt-2 flex-wrap">
|
||||||
|
<button type="button" class="mac-btn mac-btn-outline rounded-full mac-dialog-close" onclick="closeDialog()">取消</button>
|
||||||
|
<button type="submit" class="mac-btn mac-btn-danger rounded-full">确认取消</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* ========== 自适应空白系统 ========== */
|
||||||
|
/* 主内容区域 - 自适应边距 */
|
||||||
|
main {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文件列表容器 - 动态边距 */
|
||||||
|
.file-list-container {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 >= 1200px (电脑) - 保持原样 */
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 24px;
|
||||||
|
padding-right: 24px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 992px - 1199px (小电脑/大平板横屏) - 较小空白 */
|
||||||
|
@media (min-width: 992px) and (max-width: 1199px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 768px - 991px (平板横屏) - 中等空白 */
|
||||||
|
@media (min-width: 768px) and (max-width: 991px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 28px;
|
||||||
|
padding-right: 28px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 18px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 640px - 767px (平板竖屏/大手机) - 较小空白 */
|
||||||
|
@media (min-width: 640px) and (max-width: 767px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 480px - 639px (手机) - 很小空白 */
|
||||||
|
@media (min-width: 480px) and (max-width: 639px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 360px - 479px (小手机) - 最小空白 */
|
||||||
|
@media (min-width: 360px) and (max-width: 479px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 屏幕宽度 < 360px (极小手机) - 边缘贴边 */
|
||||||
|
@media (max-width: 359px) {
|
||||||
|
main .container {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
.file-list-container {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
.mac-card {
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文件类型图标颜色 */
|
||||||
|
.mac-file-icon.folder { background: linear-gradient(135deg, #60a5fa, #3b82f6); }
|
||||||
|
.mac-file-icon.document { background: linear-gradient(135deg, #a855f7, #9333ea); }
|
||||||
|
|
||||||
|
/* 文件列表项 */
|
||||||
|
.mac-file-item {
|
||||||
|
background: var(--repo-color-bg);
|
||||||
|
border: 1px solid var(--repo-color-border);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 14px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-file-item:hover {
|
||||||
|
background: var(--repo-color-bg-hover, #f8fafc);
|
||||||
|
border-color: var(--repo-color-border-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端布局调整 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.mac-file-item {
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 14px;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.mac-file-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
font-size: 20px;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
.mac-action-buttons {
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 桌面端保持行内布局 */
|
||||||
|
@media (min-width: 641px) {
|
||||||
|
.mac-file-item {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.mac-action-buttons {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文件名自动换行 */
|
||||||
|
.break-all {
|
||||||
|
word-break: break-all;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文件类型标签 */
|
||||||
|
.file-type-badge {
|
||||||
|
background: rgba(59, 130, 246, 0.1);
|
||||||
|
color: var(--repo-color-primary);
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.folder-badge {
|
||||||
|
background: rgba(16, 185, 129, 0.1);
|
||||||
|
color: var(--repo-color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-badge-sm {
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: 11px;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮 - 柔和彩色 */
|
||||||
|
.mac-action-btn {
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 12px;
|
||||||
|
color: #6b7280;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-action-btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分享按钮 */
|
||||||
|
.mac-action-btn-share {
|
||||||
|
background: rgba(6, 182, 212, 0.08);
|
||||||
|
border-color: rgba(6, 182, 212, 0.2);
|
||||||
|
color: #06b6d4;
|
||||||
|
}
|
||||||
|
.mac-action-btn-share:hover {
|
||||||
|
background: rgba(6, 182, 212, 0.15);
|
||||||
|
border-color: #06b6d4;
|
||||||
|
color: #0891b2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 删除按钮 */
|
||||||
|
.mac-action-btn-delete {
|
||||||
|
background: rgba(239, 68, 68, 0.08);
|
||||||
|
border-color: rgba(239, 68, 68, 0.2);
|
||||||
|
color: #ef4444;
|
||||||
|
}
|
||||||
|
.mac-action-btn-delete:hover {
|
||||||
|
background: rgba(239, 68, 68, 0.15);
|
||||||
|
border-color: #ef4444;
|
||||||
|
color: #dc2626;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端按钮触摸优化 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.mac-action-btn {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
.mac-action-buttons {
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 动画 */
|
||||||
|
@keyframes slideInRight {
|
||||||
|
from { transform: translateX(100%); opacity: 0; }
|
||||||
|
to { transform: translateX(0); opacity: 1; }
|
||||||
|
}
|
||||||
|
.animate-slideInRight { animation: slideInRight 0.3s ease-out; }
|
||||||
|
|
||||||
|
/* 卡片样式 */
|
||||||
|
.mac-card {
|
||||||
|
background: var(--repo-color-bg);
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px solid var(--repo-color-border);
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-card:hover {
|
||||||
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-card-header {
|
||||||
|
background: var(--repo-color-bg-secondary);
|
||||||
|
padding: 18px 24px;
|
||||||
|
border-bottom: 1px solid var(--repo-color-border);
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--repo-color-text);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端卡片头部调整 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.mac-card-header {
|
||||||
|
padding: 14px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮样式 */
|
||||||
|
.mac-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: var(--repo-color-primary);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 9999px;
|
||||||
|
padding: 10px 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn:hover {
|
||||||
|
background: var(--repo-color-primary-hover);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn-sm {
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn-outline {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--repo-color-primary);
|
||||||
|
border: 1px solid var(--repo-color-border);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn-outline:hover {
|
||||||
|
background: rgba(59, 130, 246, 0.08);
|
||||||
|
border-color: var(--repo-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn-danger {
|
||||||
|
background: var(--repo-color-danger);
|
||||||
|
}
|
||||||
|
.mac-btn-danger:hover {
|
||||||
|
background: #dc2626;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 警告框 */
|
||||||
|
.mac-alert {
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 14px 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-alert-success {
|
||||||
|
background: rgba(16, 185, 129, 0.08);
|
||||||
|
border: 1px solid rgba(16, 185, 129, 0.2);
|
||||||
|
}
|
||||||
|
.mac-alert-danger {
|
||||||
|
background: rgba(239, 68, 68, 0.08);
|
||||||
|
border: 1px solid rgba(239, 68, 68, 0.2);
|
||||||
|
}
|
||||||
|
.mac-alert-warning {
|
||||||
|
background: rgba(245, 158, 11, 0.08);
|
||||||
|
border: 1px solid rgba(245, 158, 11, 0.2);
|
||||||
|
}
|
||||||
|
.mac-alert-info {
|
||||||
|
background: rgba(59, 130, 246, 0.08);
|
||||||
|
border: 1px solid rgba(59, 130, 246, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 徽章 */
|
||||||
|
.mac-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
background: var(--repo-color-bg-tertiary);
|
||||||
|
color: var(--repo-color-text-secondary);
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 9999px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-badge-primary {
|
||||||
|
background: rgba(59, 130, 246, 0.1);
|
||||||
|
color: var(--repo-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 模态框MAC风格圆角 */
|
||||||
|
.mac-dialog {
|
||||||
|
border-radius: 24px !important;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flash消息移动端适配 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
#flashMessages {
|
||||||
|
top: 70px;
|
||||||
|
right: 12px;
|
||||||
|
left: 12px;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文本颜色 */
|
||||||
|
.text-dark { color: #1e293b; }
|
||||||
|
.text-muted { color: #64748b; }
|
||||||
|
.text-primary { color: #3b82f6; }
|
||||||
|
.text-success { color: #10b981; }
|
||||||
|
.text-danger { color: #ef4444; }
|
||||||
|
.text-warning { color: #f59e0b; }
|
||||||
|
|
||||||
|
/* 背景色 */
|
||||||
|
.bg-gray-50 { background-color: #f9fafb; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
// 取消分享功能 - 显示弹窗
|
||||||
|
$('.unshare-btn').click(function() {
|
||||||
|
var shareId = $(this).data('share-id');
|
||||||
|
var itemName = $(this).data('item-name');
|
||||||
|
|
||||||
|
$('#unshare_share_id').val(shareId);
|
||||||
|
$('#unshare_item_name').text(itemName);
|
||||||
|
|
||||||
|
// 显示弹窗
|
||||||
|
$('#unshareDialog').removeClass('hidden');
|
||||||
|
$('#unshareDialog').css({
|
||||||
|
'display': 'flex',
|
||||||
|
'position': 'fixed',
|
||||||
|
'inset': '0',
|
||||||
|
'background-color': 'rgba(0, 0, 0, 0.5)',
|
||||||
|
'align-items': 'center',
|
||||||
|
'justify-content': 'center',
|
||||||
|
'z-index': '9999'
|
||||||
|
});
|
||||||
|
$('body').css('overflow', 'hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
// ESC键关闭弹窗
|
||||||
|
$(document).keyup(function(e) {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
closeDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 关闭弹窗函数
|
||||||
|
function closeDialog() {
|
||||||
|
$('#unshareDialog').addClass('hidden');
|
||||||
|
$('#unshareDialog').css('display', 'none');
|
||||||
|
$('body').css('overflow', 'auto');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击遮罩层关闭弹窗
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
const dialog = document.getElementById('unshareDialog');
|
||||||
|
if (dialog && !dialog.classList.contains('hidden')) {
|
||||||
|
if (e.target === dialog || e.target.classList.contains('mac-dialog-close') || e.target.closest('.mac-dialog-close')) {
|
||||||
|
closeDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user