172 lines
9.7 KiB
HTML
172 lines
9.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<title>{% block title %}供水管道健康评估系统{% endblock %}</title>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<link href="{{ url_for('static', filename='css/app.css') }}" rel="stylesheet" />
|
|
{% block head_extra %}{% endblock %}
|
|
</head>
|
|
<body class="flex min-h-screen flex-col bg-page text-textMain antialiased">
|
|
<header class="sticky top-0 z-30 border-b border-line bg-white/95 backdrop-blur">
|
|
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
|
<div class="h-16 flex items-center justify-between gap-4">
|
|
<a href="{{ url_for('main.home') }}" class="flex min-w-0 items-center gap-2 text-[16px] font-extrabold tracking-tight">
|
|
<span class="material-symbols-outlined text-primary">water_drop</span>
|
|
<span class="truncate">供水管道健康评估系统</span>
|
|
</a>
|
|
<nav class="hidden md:flex h-full items-center gap-1 text-sm font-semibold">
|
|
<a href="{{ url_for('main.home') }}" class="flex h-full items-center px-3 {{ 'text-primary border-b-2 border-primary' if active_page == 'home' else 'text-slate-500 border-b-2 border-transparent hover:text-primary' }}">主页</a>
|
|
<a href="{{ url_for('main.result_page') }}" class="flex h-full items-center px-3 {{ 'text-primary border-b-2 border-primary' if active_page == 'result' else 'text-slate-500 border-b-2 border-transparent hover:text-primary' }}">结果</a>
|
|
<a href="{{ url_for('main.history_page') }}" class="flex h-full items-center px-3 {{ 'text-primary border-b-2 border-primary' if active_page == 'history' else 'text-slate-500 border-b-2 border-transparent hover:text-primary' }}">历史</a>
|
|
<a href="{{ url_for('main.reference_page') }}" class="flex h-full items-center px-3 {{ 'text-primary border-b-2 border-primary' if active_page == 'reference' else 'text-slate-500 border-b-2 border-transparent hover:text-primary' }}">文档</a>
|
|
<a href="{{ url_for('main.account_security') }}" class="flex h-full items-center px-3 {{ 'text-primary border-b-2 border-primary' if active_page == 'security' else 'text-slate-500 border-b-2 border-transparent hover:text-primary' }}">安全</a>
|
|
{% if current_user.is_admin %}
|
|
<a href="{{ url_for('main.admin_dashboard') }}" class="flex h-full items-center px-3 {{ 'text-primary border-b-2 border-primary' if active_page == 'admin' else 'text-slate-500 border-b-2 border-transparent hover:text-primary' }}">管理</a>
|
|
{% endif %}
|
|
</nav>
|
|
{% if current_user.is_authenticated %}<div class="flex items-center gap-3">
|
|
<div class="hidden sm:flex items-center gap-2 text-sm font-semibold text-slate-600">
|
|
<div class="h-8 w-8 rounded-full bg-primary text-white flex items-center justify-center text-xs">{{ current_user.username[:1]|upper }}</div>
|
|
<span class="max-w-[120px] truncate">{{ current_user.username }}</span>
|
|
</div>
|
|
<form method="post" action="{{ url_for('main.logout') }}" data-clear-home-state>
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<button class="ui-btn ui-btn-sm ui-btn-secondary font-semibold text-slate-600" type="submit">退出</button>
|
|
</form>
|
|
</div>{% endif %}
|
|
</div>
|
|
</div>
|
|
<nav class="md:hidden border-t border-line bg-white">
|
|
<div class="mx-auto max-w-7xl px-2 flex overflow-x-auto text-sm font-semibold">
|
|
<a href="{{ url_for('main.home') }}" class="whitespace-nowrap px-3 py-3 {{ 'text-primary' if active_page == 'home' else 'text-slate-500' }}">主页</a>
|
|
<a href="{{ url_for('main.result_page') }}" class="whitespace-nowrap px-3 py-3 {{ 'text-primary' if active_page == 'result' else 'text-slate-500' }}">结果</a>
|
|
<a href="{{ url_for('main.history_page') }}" class="whitespace-nowrap px-3 py-3 {{ 'text-primary' if active_page == 'history' else 'text-slate-500' }}">历史</a>
|
|
<a href="{{ url_for('main.reference_page') }}" class="whitespace-nowrap px-3 py-3 {{ 'text-primary' if active_page == 'reference' else 'text-slate-500' }}">文档</a>
|
|
<a href="{{ url_for('main.account_security') }}" class="whitespace-nowrap px-3 py-3 {{ 'text-primary' if active_page == 'security' else 'text-slate-500' }}">安全</a>
|
|
{% if current_user.is_admin %}
|
|
<a href="{{ url_for('main.admin_dashboard') }}" class="whitespace-nowrap px-3 py-3 {{ 'text-primary' if active_page == 'admin' else 'text-slate-500' }}">管理</a>
|
|
{% endif %}
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
|
|
{% with flashed_messages = get_flashed_messages(with_categories=true) %}
|
|
<div id="alertBox" class="pointer-events-none fixed left-1/2 top-20 z-50 hidden w-[calc(100vw-2rem)] max-w-[430px] -translate-x-1/2 translate-y-3 rounded-lg border bg-white p-4 opacity-0 shadow-[0_18px_45px_rgba(15,23,42,.16)] transition-all duration-200 ease-out sm:top-24" role="status" aria-live="polite">
|
|
<div class="flex items-start gap-3">
|
|
<span id="alertIconWrap" class="flex h-9 w-9 shrink-0 items-center justify-center rounded-md">
|
|
<span id="alertIcon" class="material-symbols-outlined text-xl">priority_high</span>
|
|
</span>
|
|
<div class="min-w-0 flex-1 pt-0.5">
|
|
<div id="alertTitle" class="text-sm font-extrabold text-textMain"></div>
|
|
<div id="alertMessage" class="mt-1 text-sm leading-5 text-textSub"></div>
|
|
</div>
|
|
<button id="alertClose" class="ui-icon-btn -mr-1 -mt-1 text-slate-400 hover:bg-slate-100 hover:text-slate-700" type="button" aria-label="关闭通知">
|
|
<span class="material-symbols-outlined text-base">close</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
window.__flashMessages = {{ flashed_messages|tojson }};
|
|
</script>
|
|
<div class="sr-only" aria-hidden="true">
|
|
{% for category, message in flashed_messages %}{{ message }}{% endfor %}
|
|
</div>
|
|
{% endwith %}
|
|
|
|
<main class="mx-auto w-full max-w-7xl flex-1 px-4 py-6 sm:px-6 lg:px-8 lg:py-8">
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
|
|
<footer class="border-t border-line bg-white">
|
|
<div class="mx-auto max-w-7xl px-4 py-4 sm:px-6 lg:px-8 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between text-xs text-slate-500">
|
|
<span>预测结果仅供参考</span>
|
|
<span>© {{ now_year() }} 供水管道健康评估系统</span>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
(() => {
|
|
const alertBox = document.getElementById('alertBox');
|
|
const alertIconWrap = document.getElementById('alertIconWrap');
|
|
const alertIcon = document.getElementById('alertIcon');
|
|
const alertTitle = document.getElementById('alertTitle');
|
|
const alertMessage = document.getElementById('alertMessage');
|
|
const alertClose = document.getElementById('alertClose');
|
|
let alertTimer = null;
|
|
let alertHideTimer = null;
|
|
|
|
if (!alertBox || !alertIconWrap || !alertIcon || !alertTitle || !alertMessage || !alertClose) return;
|
|
|
|
function hideAlert() {
|
|
clearTimeout(alertTimer);
|
|
clearTimeout(alertHideTimer);
|
|
alertBox.classList.remove('opacity-100', 'translate-y-0');
|
|
alertBox.classList.add('pointer-events-none', 'opacity-0', 'translate-y-3');
|
|
alertHideTimer = setTimeout(() => {
|
|
alertBox.classList.add('hidden');
|
|
}, 200);
|
|
}
|
|
|
|
function showAppNotification(message, type = 'info', title) {
|
|
clearTimeout(alertTimer);
|
|
clearTimeout(alertHideTimer);
|
|
alertBox.classList.remove('hidden', 'border-red-200', 'border-blue-200');
|
|
alertIconWrap.classList.remove('bg-dangerSoft', 'text-dangerText', 'bg-blueSoft', 'text-primary');
|
|
|
|
if (type === 'error') {
|
|
alertBox.classList.add('border-red-200');
|
|
alertIconWrap.classList.add('bg-dangerSoft', 'text-dangerText');
|
|
alertIcon.textContent = 'priority_high';
|
|
alertTitle.textContent = title || '操作未完成';
|
|
} else {
|
|
alertBox.classList.add('border-blue-200');
|
|
alertIconWrap.classList.add('bg-blueSoft', 'text-primary');
|
|
alertIcon.textContent = 'check_circle';
|
|
alertTitle.textContent = title || '操作成功';
|
|
}
|
|
|
|
alertMessage.textContent = message;
|
|
requestAnimationFrame(() => {
|
|
alertBox.classList.remove('pointer-events-none', 'opacity-0', 'translate-y-3');
|
|
alertBox.classList.add('opacity-100', 'translate-y-0');
|
|
});
|
|
alertTimer = setTimeout(hideAlert, 10000);
|
|
}
|
|
|
|
alertClose.addEventListener('click', hideAlert);
|
|
window.showAppNotification = showAppNotification;
|
|
window.hideAppNotification = hideAlert;
|
|
|
|
const flashedMessages = window.__flashMessages || [];
|
|
if (flashedMessages.length) {
|
|
const [category, message] = flashedMessages[flashedMessages.length - 1];
|
|
showAppNotification(message, category === 'error' ? 'error' : 'info');
|
|
}
|
|
|
|
document.querySelectorAll('[data-download-action]').forEach((button) => {
|
|
const restoreDelay = 3000;
|
|
const originalHtml = button.innerHTML;
|
|
const pendingLabel = button.dataset.downloadPending || '下载中...';
|
|
|
|
button.addEventListener('click', () => {
|
|
if (button.getAttribute('aria-disabled') === 'true') return;
|
|
|
|
button.setAttribute('aria-disabled', 'true');
|
|
button.setAttribute('aria-busy', 'true');
|
|
button.innerHTML = `<span class="spinner" aria-hidden="true"></span><span>${pendingLabel}</span>`;
|
|
|
|
window.setTimeout(() => {
|
|
button.innerHTML = originalHtml;
|
|
button.removeAttribute('aria-disabled');
|
|
button.removeAttribute('aria-busy');
|
|
}, restoreDelay);
|
|
});
|
|
});
|
|
|
|
})();
|
|
</script>
|
|
{% block scripts %}{% endblock %}
|
|
</body>
|
|
</html>
|