基础的切片和质量控制
This commit is contained in:
135
app/templates/base.html
Normal file
135
app/templates/base.html
Normal file
@@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AIO 3D Slicer</title>
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
|
||||
<!-- Bootstrap Icons -->
|
||||
<link href="{{ url_for('static', filename='css/bootstrap-icons.css') }}" rel="stylesheet">
|
||||
<style>
|
||||
body { padding-top: 56px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background-color: #f8f9fa; }
|
||||
.sidebar { position: fixed; top: 56px; bottom: 0; left: 0; z-index: 100; padding: 0; box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1); background-color: #fff; }
|
||||
.sidebar-sticky { position: relative; top: 0; height: calc(100vh - 56px); padding-top: 1rem; overflow-x: hidden; overflow-y: auto; }
|
||||
|
||||
.nav-pills .nav-link { border-radius: 0.5rem; padding: 0.75rem 1rem; font-weight: 500; transition: all 0.2s ease; margin-bottom: 0.25rem; }
|
||||
.nav-pills .nav-link:hover { background-color: rgba(13,110,253,0.05); color: #0d6efd !important; }
|
||||
.nav-pills .nav-link.active { background-color: #0d6efd; color: white !important; box-shadow: 0 4px 6px rgba(13,110,253,0.3); }
|
||||
|
||||
.navbar-brand { font-size: 1.25rem; letter-spacing: 0.5px; }
|
||||
.card { border: none; border-radius: 0.75rem; overflow: hidden; }
|
||||
.card-header { border-bottom: 1px solid rgba(0,0,0,.05); background-color: transparent; }
|
||||
|
||||
.toast-container { margin-bottom: 20px; margin-right: 20px; }
|
||||
.toast { border-radius: 0.5rem; box-shadow: 0 0.5rem 1rem rgba(0,0,0,.15); opacity: 0.95; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top shadow-sm">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand fw-bold" href="{{ url_for('main.index') }}"><i class="bi bi-printer me-2"></i>AIO 3D Slicer</a>
|
||||
<div class="d-flex text-light align-items-center">
|
||||
<div class="dropdown me-3">
|
||||
<button class="btn btn-sm btn-outline-light dropdown-toggle" type="button" id="langDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-globe me-1"></i>{{ _('Language') }}
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="langDropdown">
|
||||
<li><a class="dropdown-item {% if request.cookies.get('lang') == 'en' %}active{% endif %}" href="{{ url_for('main.set_language', lang='en') }}"><i class="bi bi-translate me-2"></i>English</a></li>
|
||||
<li><a class="dropdown-item {% if request.cookies.get('lang') == 'zh-cn' %}active{% endif %}" href="{{ url_for('main.set_language', lang='zh-cn') }}"><i class="bi bi-translate me-2"></i>中文 (简体)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
{% if current_user.is_guest %}
|
||||
<span class="me-3 text-secondary"><i class="bi bi-incognito me-1"></i>{{ _('Guest') }} ({{ current_user.username }})</span>
|
||||
<a href="{{ url_for('auth.login') }}" class="btn btn-outline-light btn-sm rounded-pill px-3">{{ _('Login') }}</a>
|
||||
{% else %}
|
||||
<span class="me-3 text-success fw-semibold"><i class="bi bi-person-circle me-1"></i>{{ current_user.username }}</span>
|
||||
<a href="{{ url_for('auth.logout') }}" class="btn btn-outline-light btn-sm rounded-pill px-3">{{ _('Logout') }}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-white sidebar collapse border-end">
|
||||
<div class="sidebar-sticky pt-3 px-2">
|
||||
<ul class="nav flex-column nav-pills gap-1">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark {% if request.endpoint == 'main.index' %}active text-white shadow-sm{% endif %}" href="{{ url_for('main.index') }}">
|
||||
<i class="bi bi-house-door me-2"></i>{{ _('Home') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark {% if request.endpoint == 'main.slice_page' %}active text-white shadow-sm{% endif %}" href="{{ url_for('main.slice_page') }}">
|
||||
<i class="bi bi-box me-2"></i>{{ _('New Slice') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark {% if request.endpoint == 'main.files' %}active text-white shadow-sm{% endif %}" href="{{ url_for('main.files') }}">
|
||||
<i class="bi bi-folder2-open me-2"></i>{{ _('My Files') }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{% if current_user.is_authenticated and current_user.is_admin %}
|
||||
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-2 text-muted fw-bold text-uppercase" style="font-size: 0.75rem;">
|
||||
<span><i class="bi bi-shield-lock me-1"></i>{{ _('Admin Options') }}</span>
|
||||
</h6>
|
||||
<ul class="nav flex-column nav-pills gap-1 mb-2">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark {% if request.endpoint == 'admin.settings' %}active text-white shadow-sm{% endif %}" href="{{ url_for('admin.settings') }}">
|
||||
<i class="bi bi-gear me-2"></i>{{ _('System Settings') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark {% if request.endpoint == 'admin.users' %}active text-white shadow-sm{% endif %}" href="{{ url_for('admin.users') }}">
|
||||
<i class="bi bi-people me-2"></i>{{ _('User Management') }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 mt-4 bg-light min-vh-100 pb-5">
|
||||
|
||||
<!-- Toast Notification Container -->
|
||||
<div class="toast-container position-fixed bottom-0 end-0 p-3" style="z-index: 1055;">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
{% set toast_class = 'bg-success text-white' if category == 'success' else 'bg-danger text-white' if category == 'danger' else 'bg-warning text-dark' if category == 'warning' else 'bg-primary text-white' %}
|
||||
<div class="toast align-items-center border-0 {{ toast_class }}" role="alert" aria-live="assertive" aria-atomic="true">
|
||||
<div class="d-flex">
|
||||
<div class="toast-body fw-medium">
|
||||
{{ message }}
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>
|
||||
<script>
|
||||
// Initialize Toasts automatically
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var toastElList = [].slice.call(document.querySelectorAll('.toast'))
|
||||
var toastList = toastElList.map(function (toastEl) {
|
||||
return new bootstrap.Toast(toastEl, { delay: 3000 }).show()
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user