Files
2026-01-11 18:14:42 +08:00

414 lines
15 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>商家后台 - 奶茶店管理系统</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
<style>
.stat-card {
transition: transform 0.3s, box-shadow 0.3s;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
.stat-icon {
font-size: 3rem;
opacity: 0.3;
}
/* 侧边栏样式 */
.sidebar {
position: fixed;
top: 0;
left: 0;
width: 260px;
height: 100vh;
background: linear-gradient(180deg, #2c3e50 0%, #34495e 100%);
color: #fff;
z-index: 1050;
display: flex;
flex-direction: column;
transition: transform 0.3s ease;
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
}
.sidebar-header {
padding: 1.5rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
.sidebar-brand {
font-size: 1.25rem;
font-weight: 600;
}
.sidebar-nav {
flex: 1;
overflow-y: auto;
padding: 1rem 0;
}
.sidebar-nav .nav-link {
color: rgba(255, 255, 255, 0.8);
padding: 0.75rem 1.5rem;
transition: all 0.3s;
border-left: 3px solid transparent;
display: flex;
align-items: center;
}
.sidebar-nav .nav-link:hover {
background-color: rgba(255, 255, 255, 0.1);
color: #fff;
border-left-color: #0d6efd;
}
.sidebar-nav .nav-link.active {
background-color: rgba(13, 110, 253, 0.2);
color: #fff;
border-left-color: #0d6efd;
font-weight: 600;
}
.sidebar-nav .nav-link i {
width: 24px;
font-size: 1.1rem;
margin-right: 0.75rem;
}
.sidebar-nav .nav-text {
flex: 1;
}
.sidebar-footer {
padding: 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
/* 移动端样式 */
@media (max-width: 767.98px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.show {
transform: translateX(0);
}
.sidebar-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1049;
display: none;
transition: opacity 0.3s;
}
.sidebar-overlay.show {
display: block;
}
.top-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 56px;
background: linear-gradient(180deg, #2c3e50 0%, #34495e 100%);
z-index: 1048;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
body {
padding-top: 56px;
}
}
/* 桌面端主内容区域 */
@media (min-width: 768px) {
.main-content {
margin-left: 260px;
min-height: 100vh;
position: relative;
z-index: 1;
width: calc(100% - 260px);
}
}
/* 滚动条样式 */
.sidebar-nav::-webkit-scrollbar {
width: 6px;
}
.sidebar-nav::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
.sidebar-nav::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
}
.sidebar-nav::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.3);
}
</style>
</head>
<body>
<!-- 侧边栏导航 -->
<div class="sidebar" id="sidebar">
<div class="sidebar-header">
<div class="d-flex align-items-center">
<i class="bi bi-shop fs-4 me-2"></i>
<span class="sidebar-brand">商家后台</span>
</div>
<button class="btn btn-link text-white p-0 d-md-none" id="sidebarCloseBtn">
<i class="bi bi-x-lg"></i>
</button>
</div>
<nav class="sidebar-nav">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="/merchant/dashboard.html" data-page="dashboard">
<i class="bi bi-speedometer2"></i>
<span class="nav-text">首页</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/merchant/products.html" data-page="products">
<i class="bi bi-box-seam"></i>
<span class="nav-text">商品管理</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/merchant/orders.html" data-page="orders">
<i class="bi bi-receipt-cutoff"></i>
<span class="nav-text">订单管理</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/merchant/inventory.html" data-page="inventory">
<i class="bi bi-archive"></i>
<span class="nav-text">库存管理</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/merchant/statistics.html" data-page="statistics">
<i class="bi bi-bar-chart"></i>
<span class="nav-text">数据统计</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/merchant/announcements.html" data-page="announcements">
<i class="bi bi-megaphone"></i>
<span class="nav-text">公告管理</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/merchant/messages.html" data-page="messages">
<i class="bi bi-chat-dots"></i>
<span class="nav-text">留言管理</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/merchant/reviews.html" data-page="reviews">
<i class="bi bi-star"></i>
<span class="nav-text">评价管理</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/merchant/users.html" data-page="users">
<i class="bi bi-people"></i>
<span class="nav-text">用户管理</span>
</a>
</li>
</ul>
</nav>
<div class="sidebar-footer">
<a href="/merchant/logout" class="btn btn-outline-light w-100">
<i class="bi bi-box-arrow-right"></i>
<span class="ms-2">退出登录</span>
</a>
</div>
</div>
<!-- 侧边栏遮罩层(移动端) -->
<div class="sidebar-overlay" id="sidebarOverlay"></div>
<!-- 顶部导航栏(移动端) -->
<nav class="top-navbar d-md-none">
<div class="container-fluid d-flex align-items-center">
<button class="btn btn-link text-white p-0" id="sidebarToggleBtn">
<i class="bi bi-list fs-4"></i>
</button>
<span class="ms-3 text-white fw-bold">商家后台</span>
</div>
</nav>
<div class="main-content">
<div class="container-fluid mt-4 mb-5">
<h2 class="mb-4"><i class="bi bi-speedometer2"></i> 数据概览</h2>
<div class="row">
<div class="col-md-4 mb-4">
<div class="card stat-card shadow-sm border-primary">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-2">今日订单</h6>
<h3 class="mb-0" id="todayOrders">-</h3>
</div>
<i class="bi bi-receipt-cutoff stat-icon text-primary"></i>
</div>
</div>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="card stat-card shadow-sm border-success">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-2">今日营收</h6>
<h3 class="mb-0 text-success" id="todayRevenue">-</h3>
</div>
<i class="bi bi-currency-yen stat-icon text-success"></i>
</div>
</div>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="card stat-card shadow-sm border-warning">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-2">待处理订单</h6>
<h3 class="mb-0 text-warning" id="pendingOrders">-</h3>
</div>
<i class="bi bi-clock-history stat-icon text-warning"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="/static/js/sidebar-replacer.js"></script>
<script src="/static/js/ui-enhancements.js"></script>
<script>
// 使用Session浏览器自动发送Cookie
// 检查登录状态
async function checkLogin() {
try {
const response = await fetch('/merchant/profile', {
credentials: 'include'
});
const data = await response.json();
if (data.code !== 200 || !data.data) {
window.location.href = '/merchant/login.html';
return false;
}
return true;
} catch (error) {
console.error('Error:', error);
window.location.href = '/merchant/login.html';
return false;
}
}
async function loadStatistics() {
try {
const { data } = await UIEnhancements.enhancedFetch('/merchant/statistics/sales?period=day', {
credentials: 'include'
});
if (data.code === 200 && data.data) {
document.getElementById('todayOrders').textContent = data.data.totalOrders || 0;
document.getElementById('todayRevenue').textContent = '¥' + UIEnhancements.formatNumber(data.data.totalAmount || 0);
} else {
document.getElementById('todayOrders').textContent = '-';
document.getElementById('todayRevenue').textContent = '-';
}
// 加载待处理订单数
try {
const { data: ordersData } = await UIEnhancements.enhancedFetch('/merchant/orders?status=MAKING&pageNum=1&pageSize=1', {
credentials: 'include'
});
if (ordersData.code === 200 && ordersData.data) {
document.getElementById('pendingOrders').textContent = ordersData.data.total || 0;
} else {
document.getElementById('pendingOrders').textContent = '-';
}
} catch (e) {
document.getElementById('pendingOrders').textContent = '-';
}
} catch (error) {
console.error('Error:', error);
document.getElementById('todayOrders').textContent = '-';
document.getElementById('todayRevenue').textContent = '-';
document.getElementById('pendingOrders').textContent = '-';
}
}
// 侧边栏交互逻辑
(function() {
const sidebar = document.getElementById('sidebar');
const sidebarOverlay = document.getElementById('sidebarOverlay');
const sidebarToggleBtn = document.getElementById('sidebarToggleBtn');
const sidebarCloseBtn = document.getElementById('sidebarCloseBtn');
// 移动端切换侧边栏
if (sidebarToggleBtn) {
sidebarToggleBtn.addEventListener('click', function() {
sidebar.classList.add('show');
sidebarOverlay.classList.add('show');
document.body.style.overflow = 'hidden';
});
}
// 关闭侧边栏
function closeSidebar() {
sidebar.classList.remove('show');
sidebarOverlay.classList.remove('show');
document.body.style.overflow = '';
}
if (sidebarCloseBtn) {
sidebarCloseBtn.addEventListener('click', closeSidebar);
}
if (sidebarOverlay) {
sidebarOverlay.addEventListener('click', closeSidebar);
}
// 点击导航项时,移动端自动关闭侧边栏
const navLinks = document.querySelectorAll('.sidebar-nav .nav-link');
navLinks.forEach(link => {
link.addEventListener('click', function() {
if (window.innerWidth < 768) {
closeSidebar();
}
});
});
})();
// 页面加载时检查登录
checkLogin().then(isLoggedIn => {
if (isLoggedIn) {
loadStatistics();
}
});
</script>
</body>
</html>