图:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>现代销售管理系统</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
:root {
--primary: #4361ee;
--secondary: #3f37c9;
--accent: #4895ef;
--dark: #1a1a2e;
--darker: #0d0d1a;
--light: #f8f9fa;
--success: #4cc9f0;
--danger: #f72585;
--warning: #f8961e;
--card-bg: rgba(255, 255, 255, 0.05);
--border: rgba(255, 255, 255, 0.1);
--text: #e0e0e0;
--text-muted: #a0a0a0;
--transition: all 0.3s ease;
--shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
--glass: rgba(255, 255, 255, 0.05);
--glass-border: rgba(255, 255, 255, 0.1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, var(--darker) 0%, var(--dark) 100%);
color: var(--text);
min-height: 100vh;
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* 登录界面样式 */
.login-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
background: linear-gradient(135deg, #0f0c29, #302b63, #24243e);
}
.login-card {
background: var(--glass);
backdrop-filter: blur(10px);
border: 1px solid var(--glass-border);
border-radius: 16px;
padding: 30px;
width: 100%;
max-width: 420px;
box-shadow: var(--shadow);
transform: translateY(0);
transition: var(--transition);
}
.login-card:hover {
transform: translateY(-5px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.4);
}
.login-header {
text-align: center;
margin-bottom: 25px;
}
.login-header h1 {
font-size: 24px;
margin-bottom: 8px;
background: linear-gradient(90deg, var(--accent), var(--success));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.login-header p {
color: var(--text-muted);
font-size: 13px;
}
.form-group {
margin-bottom: 18px;
}
.form-group label {
display: block;
margin-bottom: 6px;
font-size: 13px;
color: var(--text);
font-weight: 500;
}
.form-control {
width: 100%;
padding: 12px 16px;
background: rgba(255, 255, 255, 0.07);
border: 1px solid var(--border);
border-radius: 8px;
color: var(--text);
font-size: 15px;
transition: var(--transition);
-webkit-appearance: none;
}
.form-control:focus {
outline: none;
border-color: var(--accent);
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 0 0 3px rgba(72, 149, 239, 0.2);
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
-webkit-tap-highlight-color: transparent;
}
.btn:active {
transform: scale(0.98);
}
.btn-primary {
background: linear-gradient(90deg, var(--primary), var(--secondary));
color: white;
width: 100%;
padding: 14px;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(67, 97, 238, 0.4);
}
.btn-success {
background: linear-gradient(90deg, #10b981, #059669);
color: white;
}
.btn-danger {
background: linear-gradient(90deg, var(--danger), #d9046d);
color: white;
}
.btn-warning {
background: linear-gradient(90deg, var(--warning), #e67e22);
color: white;
}
/* 主界面样式 */
.app-container {
display: none;
min-height: 100vh;
}
/* 顶部导航栏 - 修复移动端显示 */
.topbar {
background: var(--glass);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--glass-border);
padding: 12px 16px;
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
z-index: 100;
height: 60px;
}
.logo {
font-size: 18px;
font-weight: 700;
display: flex;
align-items: center;
gap: 10px;
background: linear-gradient(90deg, var(--accent), var(--success));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
white-space: nowrap;
}
.menu-toggle {
display: none;
background: transparent;
border: none;
color: var(--text);
font-size: 20px;
cursor: pointer;
padding: 8px;
width: 40px;
height: 40px;
border-radius: 8px;
transition: var(--transition);
-webkit-tap-highlight-color: transparent;
}
.menu-toggle:active {
background: rgba(255, 255, 255, 0.1);
transform: scale(0.95);
}
.user-info {
display: flex;
align-items: center;
gap: 12px;
font-size: 14px;
}
.logout-btn {
background: transparent;
border: 1px solid var(--border);
color: var(--text);
padding: 6px 12px;
border-radius: 6px;
cursor: pointer;
transition: var(--transition);
font-size: 13px;
-webkit-tap-highlight-color: transparent;
}
.logout-btn:hover {
background: rgba(255, 255, 255, 0.1);
border-color: var(--danger);
color: var(--danger);
}
/* 侧边栏导航 - 移动端优化 */
.sidebar {
position: fixed;
top: 60px;
left: 0;
width: 240px;
height: calc(100vh - 60px);
background: var(--glass);
backdrop-filter: blur(10px);
border-right: 1px solid var(--glass-border);
padding: 12px 0;
transition: var(--transition);
z-index: 90;
overflow-y: auto;
transform: translateX(0);
}
.nav-item {
padding: 12px 20px;
display: flex;
align-items: center;
gap: 12px;
color: var(--text-muted);
cursor: pointer;
transition: var(--transition);
border-left: 3px solid transparent;
margin: 4px 8px;
border-radius: 8px;
font-size: 14px;
-webkit-tap-highlight-color: transparent;
}
.nav-item:hover, .nav-item.active {
background: rgba(255, 255, 255, 0.08);
color: var(--text);
border-left-color: var(--accent);
}
.nav-item:active {
background: rgba(255, 255, 255, 0.12);
transform: scale(0.98);
}
.nav-item i {
width: 20px;
text-align: center;
font-size: 16px;
}
/* 主内容区 */
.main-content {
margin-left: 240px;
padding: 16px;
transition: var(--transition);
}
.page {
display: none;
animation: fadeIn 0.4s ease;
}
.page.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.page-header {
margin-bottom: 16px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
.page-title {
font-size: 20px;
font-weight: 600;
background: linear-gradient(90deg, var(--text), var(--text-muted));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
/* 卡片和仪表盘 */
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 12px;
margin-bottom: 16px;
}
.card {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px;
transition: var(--transition);
box-shadow: var(--shadow);
backdrop-filter: blur(5px);
}
.card:hover {
transform: translateY(-3px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.35);
border-color: var(--accent);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.card-title {
font-size: 13px;
color: var(--text-muted);
font-weight: 500;
}
.card-value {
font-size: 22px;
font-weight: 700;
margin-bottom: 6px;
word-break: break-all;
}
.card-footer {
font-size: 11px;
color: var(--text-muted);
display: flex;
align-items: center;
gap: 4px;
}
.trend-up {
color: #10b981;
}
.trend-down {
color: var(--danger);
}
/* 图表容器 */
.chart-container {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px;
margin-bottom: 16px;
height: 280px;
box-shadow: var(--shadow);
backdrop-filter: blur(5px);
}
.chart-title {
margin-bottom: 12px;
font-size: 16px;
font-weight: 600;
color: var(--text);
}
/* 表格样式 */
.table-container {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 10px;
padding: 12px;
overflow-x: auto;
box-shadow: var(--shadow);
backdrop-filter: blur(5px);
margin-bottom: 16px;
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
padding: 0 4px;
}
.table-header h3 {
font-size: 16px;
font-weight: 600;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
th, td {
padding: 10px 8px;
text-align: left;
border-bottom: 1px solid var(--border);
white-space: nowrap;
}
th {
color: var(--text-muted);
font-weight: 500;
font-size: 12px;
background: rgba(255, 255, 255, 0.03);
}
tr:hover {
background: rgba(255, 255, 255, 0.05);
}
.status {
padding: 4px 8px;
border-radius: 12px;
font-size: 11px;
font-weight: 600;
display: inline-block;
}
.status-completed {
background: rgba(16, 185, 129, 0.15);
color: #10b981;
}
.status-pending {
background: rgba(248, 150, 30, 0.15);
color: #f8961e;
}
.status-cancelled {
background: rgba(247, 37, 133, 0.15);
color: var(--danger);
}
/* 表单样式 */
.form-grid {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
margin-bottom: 16px;
}
.form-row {
display: flex;
gap: 10px;
margin-top: 16px;
flex-wrap: wrap;
}
.form-row .btn {
flex: 1;
min-width: 120px;
}
/* 模态框 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(5px);
z-index: 1000;
align-items: center;
justify-content: center;
padding: 16px;
}
.modal.active {
display: flex;
animation: fadeIn 0.3s ease;
}
.modal-content {
background: var(--dark);
border: 1px solid var(--border);
border-radius: 12px;
width: 100%;
max-width: 500px;
padding: 20px;
max-height: 90vh;
overflow-y: auto;
box-shadow: var(--shadow);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid var(--border);
}
.modal-title {
font-size: 18px;
font-weight: 600;
}
.close-modal {
background: transparent;
border: none;
color: var(--text);
font-size: 20px;
cursor: pointer;
width: 32px;
height: 32px;
border-radius: 6px;
transition: var(--transition);
-webkit-tap-highlight-color: transparent;
}
.close-modal:hover {
background: rgba(255, 255, 255, 0.1);
color: var(--danger);
transform: rotate(90deg);
}
.close-modal:active {
transform: scale(0.9);
}
/* 响应式调整 - 修复移动端 */
@media (max-width: 992px) {
.sidebar {
transform: translateX(-105%);
width: 260px;
background: var(--darker);
border-right: 1px solid var(--glass-border);
}
.sidebar.open {
transform: translateX(0);
box-shadow: 8px 0 24px rgba(0, 0, 0, 0.5);
}
.main-content {
margin-left: 0;
padding: 12px;
}
.menu-toggle {
display: block;
}
.logo span {
font-size: 16px;
}
.user-info span {
display: none;
}
.logout-btn {
padding: 6px 10px;
font-size: 12px;
}
.topbar {
padding: 10px 12px;
}
.dashboard-grid {
grid-template-columns: repeat(2, 1fr);
}
.card-value {
font-size: 18px;
}
.chart-container {
height: 240px;
}
.table-container {
padding: 8px;
}
table {
font-size: 12px;
}
th, td {
padding: 8px 6px;
}
.page-title {
font-size: 18px;
}
.btn {
padding: 10px 16px;
font-size: 14px;
}
.action-btn {
padding: 4px 8px;
font-size: 11px;
margin-right: 2px;
}
.modal-content {
padding: 16px;
max-width: 95%;
}
.modal-title {
font-size: 16px;
}
}
@media (max-width: 576px) {
.dashboard-grid {
grid-template-columns: 1fr;
}
.form-grid {
grid-template-columns: 1fr;
}
.form-row {
flex-direction: column;
}
.form-row .btn {
width: 100%;
}
.table-container {
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
}
table {
min-width: 600px;
}
.user-info {
gap: 8px;
}
.logout-btn {
padding: 6px 8px;
}
.login-card {
padding: 20px;
}
.login-header h1 {
font-size: 20px;
}
}
/* 遮罩层 - 点击关闭侧边栏 */
.sidebar-overlay {
display: none;
position: fixed;
top: 60px;
left: 0;
width: 100%;
height: calc(100vh - 60px);
background: rgba(0, 0, 0, 0.5);
z-index: 85;
backdrop-filter: blur(2px);
}
.sidebar-overlay.active {
display: block;
animation: fadeIn 0.3s ease;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 40px 20px;
color: var(--text-muted);
}
.empty-state i {
font-size: 48px;
margin-bottom: 15px;
opacity: 0.5;
}
/* 通知提示 */
.notification {
position: fixed;
top: 70px;
right: 16px;
background: var(--glass);
backdrop-filter: blur(10px);
border: 1px solid var(--glass-border);
border-left: 4px solid var(--success);
border-radius: 8px;
padding: 12px 16px;
box-shadow: var(--shadow);
transform: translateX(150%);
transition: var(--transition);
z-index: 1001;
min-width: 280px;
max-width: 90vw;
}
.notification.show {
transform: translateX(0);
}
.notification.error {
border-left-color: var(--danger);
}
.notification.warning {
border-left-color: var(--warning);
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.3);
}
/* 移动端触摸优化 */
@media (hover: none) {
.nav-item:active {
background: rgba(255, 255, 255, 0.15);
}
.btn:active {
transform: scale(0.96);
}
}
/* 防止 iOS 缩放 */
@media screen and (-webkit-min-device-pixel-ratio: 0) {
select, textarea, input {
font-size: 16px !important;
}
}
</style>
</head>
<body>
<!-- 登录界面 -->
<div class="login-container" id="loginScreen">
<div class="login-card">
<div class="login-header">
<h1><i class="fas fa-chart-line"></i> 销售管理系统</h1>
<p>现代化的销售数据管理平台</p>
</div>
<form id="loginForm">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" class="form-control" placeholder="输入用户名" required autocomplete="username">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" class="form-control" placeholder="输入密码" required autocomplete="current-password">
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-sign-in-alt"></i> 登录系统
</button>
</form>
<div style="margin-top: 16px; text-align: center; font-size: 12px; color: var(--text-muted);">
提示:用户名: admin, 密码: 123456
</div>
</div>
</div>
<!-- 主应用界面 -->
<div class="app-container" id="appContainer">
<!-- 顶部导航 -->
<div class="topbar">
<div class="logo">
<button class="menu-toggle" id="menuToggle" aria-label="切换菜单">
<i class="fas fa-bars"></i>
</button>
<span>销售管理系统</span>
</div>
<div class="user-info">
<span id="welcomeUser">欢迎, 管理员</span>
<button class="logout-btn" id="logoutBtn">
<i class="fas fa-sign-out-alt"></i> 退出
</button>
</div>
</div>
<!-- 侧边栏导航 -->
<div class="sidebar" id="sidebar">
<div class="nav-item active" data-page="dashboard">
<i class="fas fa-home"></i> 仪表盘
</div>
<div class="nav-item" data-page="products">
<i class="fas fa-box"></i> 产品管理
</div>
<div class="nav-item" data-page="orders">
<i class="fas fa-shopping-cart"></i> 订单管理
</div>
<div class="nav-item" data-page="customers">
<i class="fas fa-users"></i> 客户管理
</div>
</div>
<!-- 遮罩层 -->
<div class="sidebar-overlay" id="sidebarOverlay"></div>
<!-- 主内容区 -->
<div class="main-content">
<!-- 仪表盘页面 -->
<div class="page active" id="dashboardPage">
<div class="page-header">
<h2 class="page-title">仪表盘概览</h2>
</div>
<div class="dashboard-grid">
<div class="card">
<div class="card-header">
<div class="card-title">总销售额</div>
<i class="fas fa-dollar-sign" style="color: var(--success);"></i>
</div>
<div class="card-value" id="totalSales">¥0</div>
<div class="card-footer">
<i class="fas fa-arrow-up trend-up"></i> <span class="trend-up">12.5%</span> 较上月
</div>
</div>
<div class="card">
<div class="card-header">
<div class="card-title">订单数量</div>
<i class="fas fa-shopping-cart" style="color: var(--accent);"></i>
</div>
<div class="card-value" id="totalOrders">0</div>
<div class="card-footer">
<i class="fas fa-arrow-up trend-up"></i> <span class="trend-up">8.2%</span> 较上月
</div>
</div>
<div class="card">
<div class="card-header">
<div class="card-title">客户总数</div>
<i class="fas fa-users" style="color: var(--warning);"></i>
</div>
<div class="card-value" id="totalCustomers">0</div>
<div class="card-footer">
<i class="fas fa-arrow-up trend-up"></i> <span class="trend-up">5.7%</span> 较上月
</div>
</div>
<div class="card">
<div class="card-header">
<div class="card-title">产品库存</div>
<i class="fas fa-box-open" style="color: var(--danger);"></i>
</div>
<div class="card-value" id="totalProducts">0</div>
<div class="card-footer">
<i class="fas fa-arrow-down trend-down"></i> <span class="trend-down">3.1%</span> 较上月
</div>
</div>
</div>
<div class="chart-container">
<div class="chart-title">销售趋势 (最近7天)</div>
<canvas id="salesChart"></canvas>
</div>
</div>
<!-- 产品管理页面 -->
<div class="page" id="productsPage">
<div class="page-header">
<h2 class="page-title">产品管理</h2>
<button class="btn btn-primary" id="addProductBtn"><i class="fas fa-plus"></i> 添加产品</button>
</div>
<div class="table-container">
<div class="table-header">
<h3>产品列表</h3>
</div>
<table id="productsTable">
<thead>
<tr>
<th>ID</th>
<th>产品名称</th>
<th>类别</th>
<th>价格</th>
<th>库存</th>
<th>操作</th>
</tr>
</thead>
<tbody id="productsTableBody">
<!-- 动态生成 -->
</tbody>
</table>
<div id="emptyProducts" class="empty-state" style="display: none;">
<i class="fas fa-box-open"></i>
<p>暂无产品数据,请添加</p>
</div>
</div>
</div>
<!-- 订单管理页面 -->
<div class="page" id="ordersPage">
<div class="page-header">
<h2 class="page-title">订单管理</h2>
<button class="btn btn-primary" id="addOrderBtn"><i class="fas fa-plus"></i> 新增订单</button>
</div>
<div class="table-container">
<div class="table-header">
<h3>订单列表</h3>
</div>
<table id="ordersTable">
<thead>
<tr>
<th>订单ID</th>
<th>客户名称</th>
<th>产品</th>
<th>数量</th>
<th>金额</th>
<th>日期</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody id="ordersTableBody">
<!-- 动态生成 -->
</tbody>
</table>
<div id="emptyOrders" class="empty-state" style="display: none;">
<i class="fas fa-shopping-cart"></i>
<p>暂无订单数据,请添加</p>
</div>
</div>
</div>
<!-- 客户管理页面 -->
<div class="page" id="customersPage">
<div class="page-header">
<h2 class="page-title">客户管理</h2>
<button class="btn btn-primary" id="addCustomerBtn"><i class="fas fa-plus"></i> 添加客户</button>
</div>
<div class="table-container">
<div class="table-header">
<h3>客户列表</h3>
</div>
<table id="customersTable">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>电话</th>
<th>邮箱</th>
<th>消费金额</th>
<th>操作</th>
</tr>
</thead>
<tbody id="customersTableBody">
<!-- 动态生成 -->
</tbody>
</table>
<div id="emptyCustomers" class="empty-state" style="display: none;">
<i class="fas fa-users"></i>
<p>暂无客户数据,请添加</p>
</div>
</div>
</div>
</div>
</div>
<!-- 模态框 - 产品 -->
<div class="modal" id="productModal">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title" id="productModalTitle">添加产品</h3>
<button class="close-modal" data-modal="productModal">×</button>
</div>
<form id="productForm">
<input type="hidden" id="productId">
<div class="form-grid">
<div class="form-group">
<label for="productName">产品名称</label>
<input type="text" id="productName" class="form-control" required>
</div>
<div class="form-group">
<label for="productCategory">类别</label>
<input type="text" id="productCategory" class="form-control" required>
</div>
<div class="form-group">
<label for="productPrice">价格 (¥)</label>
<input type="number" id="productPrice" class="form-control" min="0" step="0.01" required>
</div>
<div class="form-group">
<label for="productStock">库存</label>
<input type="number" id="productStock" class="form-control" min="0" required>
</div>
</div>
<div class="form-row">
<button type="submit" class="btn btn-primary">保存产品</button>
<button type="button" class="btn btn-warning close-modal-btn" data-modal="productModal">取消</button>
</div>
</form>
</div>
</div>
<!-- 模态框 - 订单 -->
<div class="modal" id="orderModal">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">新增订单</h3>
<button class="close-modal" data-modal="orderModal">×</button>
</div>
<form id="orderForm">
<div class="form-grid">
<div class="form-group">
<label for="orderCustomer">客户</label>
<select id="orderCustomer" class="form-control" required>
<option value="">选择客户</option>
</select>
</div>
<div class="form-group">
<label for="orderProduct">产品</label>
<select id="orderProduct" class="form-control" required>
<option value="">选择产品</option>
</select>
</div>
<div class="form-group">
<label for="orderQuantity">数量</label>
<input type="number" id="orderQuantity" class="form-control" min="1" required>
</div>
<div class="form-group">
<label for="orderDate">日期</label>
<input type="date" id="orderDate" class="form-control" required>
</div>
</div>
<div class="form-row">
<button type="submit" class="btn btn-primary">创建订单</button>
<button type="button" class="btn btn-warning close-modal-btn" data-modal="orderModal">取消</button>
</div>
</form>
</div>
</div>
<!-- 模态框 - 客户 -->
<div class="modal" id="customerModal">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title" id="customerModalTitle">添加客户</h3>
<button class="close-modal" data-modal="customerModal">×</button>
</div>
<form id="customerForm">
<input type="hidden" id="customerId">
<div class="form-grid">
<div class="form-group">
<label for="customerName">姓名</label>
<input type="text" id="customerName" class="form-control" required>
</div>
<div class="form-group">
<label for="customerPhone">电话</label>
<input type="tel" id="customerPhone" class="form-control" required>
</div>
<div class="form-group">
<label for="customerEmail">邮箱</label>
<input type="email" id="customerEmail" class="form-control" required>
</div>
<div class="form-group">
<label for="customerSpent">消费金额 (¥)</label>
<input type="number" id="customerSpent" class="form-control" min="0" step="0.01" value="0">
</div>
</div>
<div class="form-row">
<button type="submit" class="btn btn-primary">保存客户</button>
<button type="button" class="btn btn-warning close-modal-btn" data-modal="customerModal">取消</button>
</div>
</form>
</div>
</div>
<!-- 通知提示 -->
<div class="notification" id="notification">
<div id="notificationMessage"></div>
</div>
<script>
// 全局状态
const state = {
currentUser: null,
products: [],
orders: [],
customers: [],
currentEditId: null
};
// 模拟数据初始化
function initializeData() {
const storedProducts = localStorage.getItem('sales_products');
const storedOrders = localStorage.getItem('sales_orders');
const storedCustomers = localStorage.getItem('sales_customers');
if (!storedProducts) {
state.products = [
{ id: 1, name: '笔记本电脑', category: '电子产品', price: 5999, stock: 25 },
{ id: 2, name: '智能手机', category: '电子产品', price: 2999, stock: 40 },
{ id: 3, name: '办公椅', category: '家具', price: 899, stock: 15 },
{ id: 4, name: '显示器', category: '电子产品', price: 1299, stock: 30 }
];
localStorage.setItem('sales_products', JSON.stringify(state.products));
} else {
state.products = JSON.parse(storedProducts);
}
if (!storedOrders) {
const today = new Date().toISOString().split('T')[0];
state.orders = [
{ id: 1, customerId: 1, productId: 1, quantity: 1, amount: 5999, date: today, status: 'completed' },
{ id: 2, customerId: 2, productId: 2, quantity: 2, amount: 5998, date: today, status: 'pending' },
{ id: 3, customerId: 3, productId: 3, quantity: 1, amount: 899, date: today, status: 'completed' }
];
localStorage.setItem('sales_orders', JSON.stringify(state.orders));
} else {
state.orders = JSON.parse(storedOrders);
}
if (!storedCustomers) {
state.customers = [
{ id: 1, name: '张三', phone: '13800138000', email: 'zhangsan@example.com', spent: 5999 },
{ id: 2, name: '李四', phone: '13900139000', email: 'lisi@example.com', spent: 5998 },
{ id: 3, name: '王五', phone: '13700137000', email: 'wangwu@example.com', spent: 899 }
];
localStorage.setItem('sales_customers', JSON.stringify(state.customers));
} else {
state.customers = JSON.parse(storedCustomers);
}
}
// 登录功能
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
if (username === 'admin' && password === '123456') {
state.currentUser = username;
document.getElementById('loginScreen').style.display = 'none';
document.getElementById('appContainer').style.display = 'block';
document.getElementById('welcomeUser').textContent = `欢迎, ${username}`;
initializeData();
updateDashboard();
renderProducts();
renderOrders();
renderCustomers();
showNotification('登录成功!', 'success');
} else {
showNotification('用户名或密码错误!', 'error');
}
});
// 退出登录
document.getElementById('logoutBtn').addEventListener('click', function() {
state.currentUser = null;
document.getElementById('appContainer').style.display = 'none';
document.getElementById('loginScreen').style.display = 'flex';
document.getElementById('loginForm').reset();
showNotification('已安全退出', 'success');
});
// 页面导航
document.querySelectorAll('.nav-item').forEach(item => {
item.addEventListener('click', function() {
// 更新导航状态
document.querySelectorAll('.nav-item').forEach(nav => nav.classList.remove('active'));
this.classList.add('active');
// 显示对应页面
const pageId = this.getAttribute('data-page') + 'Page';
document.querySelectorAll('.page').forEach(page => page.classList.remove('active'));
document.getElementById(pageId).classList.add('active');
// 移动端:点击后关闭侧边栏
if (window.innerWidth <= 992) {
closeSidebar();
}
});
});
// 移动端菜单切换 - 修复版本
function openSidebar() {
document.getElementById('sidebar').classList.add('open');
document.getElementById('sidebarOverlay').classList.add('active');
document.body.style.overflow = 'hidden'; // 防止背景滚动
}
function closeSidebar() {
document.getElementById('sidebar').classList.remove('open');
document.getElementById('sidebarOverlay').classList.remove('active');
document.body.style.overflow = ''; // 恢复滚动
}
document.getElementById('menuToggle').addEventListener('click', function(e) {
e.stopPropagation();
const sidebar = document.getElementById('sidebar');
if (sidebar.classList.contains('open')) {
closeSidebar();
} else {
openSidebar();
}
});
// 点击遮罩层关闭侧边栏
document.getElementById('sidebarOverlay').addEventListener('click', function() {
closeSidebar();
});
// 点击侧边栏外部关闭(移动端)
document.addEventListener('click', function(e) {
const sidebar = document.getElementById('sidebar');
const menuToggle = document.getElementById('menuToggle');
if (window.innerWidth <= 992 && sidebar.classList.contains('open')) {
if (!sidebar.contains(e.target) && !menuToggle.contains(e.target)) {
closeSidebar();
}
}
});
// 模态框控制
function openModal(modalId) {
document.getElementById(modalId).classList.add('active');
document.body.style.overflow = 'hidden'; // 防止背景滚动
}
function closeModal(modalId) {
document.getElementById(modalId).classList.remove('active');
document.body.style.overflow = ''; // 恢复滚动
}
// 关闭模态框事件
document.querySelectorAll('.close-modal, .close-modal-btn').forEach(btn => {
btn.addEventListener('click', function() {
const modalId = this.getAttribute('data-modal');
closeModal(modalId);
});
});
// 点击模态框外部关闭
document.querySelectorAll('.modal').forEach(modal => {
modal.addEventListener('click', function(e) {
if (e.target === this) {
this.classList.remove('active');
document.body.style.overflow = '';
}
});
});
// 产品管理
document.getElementById('addProductBtn').addEventListener('click', function() {
state.currentEditId = null;
document.getElementById('productModalTitle').textContent = '添加产品';
document.getElementById('productForm').reset();
openModal('productModal');
});
document.getElementById('productForm').addEventListener('submit', function(e) {
e.preventDefault();
const product = {
id: state.currentEditId || (state.products.length > 0 ? Math.max(...state.products.map(p => p.id)) + 1 : 1),
name: document.getElementById('productName').value,
category: document.getElementById('productCategory').value,
price: parseFloat(document.getElementById('productPrice').value),
stock: parseInt(document.getElementById('productStock').value)
};
if (state.currentEditId) {
const index = state.products.findIndex(p => p.id === state.currentEditId);
if (index !== -1) {
state.products[index] = product;
showNotification('产品更新成功', 'success');
}
} else {
state.products.push(product);
showNotification('产品添加成功', 'success');
}
localStorage.setItem('sales_products', JSON.stringify(state.products));
renderProducts();
updateDashboard();
closeModal('productModal');
});
function renderProducts() {
const tbody = document.getElementById('productsTableBody');
const emptyState = document.getElementById('emptyProducts');
tbody.innerHTML = '';
if (state.products.length === 0) {
emptyState.style.display = 'block';
return;
}
emptyState.style.display = 'none';
state.products.forEach(product => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${product.id}</td>
<td>${product.name}</td>
<td>${product.category}</td>
<td>¥${product.price.toFixed(2)}</td>
<td>${product.stock}</td>
<td>
<button class="action-btn action-edit" onclick="editProduct(${product.id})">编辑</button>
<button class="action-btn action-delete" onclick="deleteProduct(${product.id})">删除</button>
</td>
`;
tbody.appendChild(row);
});
}
function editProduct(id) {
const product = state.products.find(p => p.id === id);
if (product) {
state.currentEditId = id;
document.getElementById('productModalTitle').textContent = '编辑产品';
document.getElementById('productId').value = product.id;
document.getElementById('productName').value = product.name;
document.getElementById('productCategory').value = product.category;
document.getElementById('productPrice').value = product.price;
document.getElementById('productStock').value = product.stock;
openModal('productModal');
}
}
function deleteProduct(id) {
if (confirm('确定要删除这个产品吗?')) {
state.products = state.products.filter(p => p.id !== id);
localStorage.setItem('sales_products', JSON.stringify(state.products));
renderProducts();
updateDashboard();
showNotification('产品已删除', 'success');
}
}
// 订单管理
document.getElementById('addOrderBtn').addEventListener('click', function() {
const customerSelect = document.getElementById('orderCustomer');
const productSelect = document.getElementById('orderProduct');
customerSelect.innerHTML = '<option value="">选择客户</option>';
productSelect.innerHTML = '<option value="">选择产品</option>';
state.customers.forEach(customer => {
const option = document.createElement('option');
option.value = customer.id;
option.textContent = customer.name;
customerSelect.appendChild(option);
});
state.products.forEach(product => {
const option = document.createElement('option');
option.value = product.id;
option.textContent = `${product.name} (库存: ${product.stock})`;
productSelect.appendChild(option);
});
document.getElementById('orderDate').valueAsDate = new Date();
openModal('orderModal');
});
document.getElementById('orderForm').addEventListener('submit', function(e) {
e.preventDefault();
const customerId = parseInt(document.getElementById('orderCustomer').value);
const productId = parseInt(document.getElementById('orderProduct').value);
const quantity = parseInt(document.getElementById('orderQuantity').value);
const date = document.getElementById('orderDate').value;
const product = state.products.find(p => p.id === productId);
if (product.stock < quantity) {
showNotification(`库存不足!可用库存: ${product.stock}`, 'error');
return;
}
const amount = product.price * quantity;
const order = {
id: state.orders.length > 0 ? Math.max(...state.orders.map(o => o.id)) + 1 : 1,
customerId,
productId,
quantity,
amount,
date,
status: 'pending'
};
state.orders.push(order);
product.stock -= quantity;
const productIndex = state.products.findIndex(p => p.id === productId);
state.products[productIndex] = product;
const customer = state.customers.find(c => c.id === customerId);
customer.spent += amount;
const customerIndex = state.customers.findIndex(c => c.id === customerId);
state.customers[customerIndex] = customer;
localStorage.setItem('sales_orders', JSON.stringify(state.orders));
localStorage.setItem('sales_products', JSON.stringify(state.products));
localStorage.setItem('sales_customers', JSON.stringify(state.customers));
renderOrders();
renderProducts();
renderCustomers();
updateDashboard();
closeModal('orderModal');
showNotification('订单创建成功', 'success');
});
function renderOrders() {
const tbody = document.getElementById('ordersTableBody');
const emptyState = document.getElementById('emptyOrders');
tbody.innerHTML = '';
if (state.orders.length === 0) {
emptyState.style.display = 'block';
return;
}
emptyState.style.display = 'none';
state.orders.forEach(order => {
const customer = state.customers.find(c => c.id === order.customerId);
const product = state.products.find(p => p.id === order.productId);
const row = document.createElement('tr');
row.innerHTML = `
<td>#${order.id}</td>
<td>${customer ? customer.name : '未知'}</td>
<td>${product ? product.name : '未知'}</td>
<td>${order.quantity}</td>
<td>¥${order.amount.toFixed(2)}</td>
<td>${order.date}</td>
<td><span class="status status-${order.status}">${order.status === 'completed' ? '已完成' : '处理中'}</span></td>
<td>
<button class="action-btn action-view" onclick="completeOrder(${order.id})">完成</button>
<button class="action-btn action-delete" onclick="deleteOrder(${order.id})">删除</button>
</td>
`;
tbody.appendChild(row);
});
}
function completeOrder(id) {
const orderIndex = state.orders.findIndex(o => o.id === id);
if (orderIndex !== -1) {
state.orders[orderIndex].status = 'completed';
localStorage.setItem('sales_orders', JSON.stringify(state.orders));
renderOrders();
showNotification('订单已完成', 'success');
}
}
function deleteOrder(id) {
if (confirm('确定要删除这个订单吗?')) {
state.orders = state.orders.filter(o => o.id !== id);
localStorage.setItem('sales_orders', JSON.stringify(state.orders));
renderOrders();
updateDashboard();
showNotification('订单已删除', 'success');
}
}
// 客户管理
document.getElementById('addCustomerBtn').addEventListener('click', function() {
state.currentEditId = null;
document.getElementById('customerModalTitle').textContent = '添加客户';
document.getElementById('customerForm').reset();
openModal('customerModal');
});
document.getElementById('customerForm').addEventListener('submit', function(e) {
e.preventDefault();
const customer = {
id: state.currentEditId || (state.customers.length > 0 ? Math.max(...state.customers.map(c => c.id)) + 1 : 1),
name: document.getElementById('customerName').value,
phone: document.getElementById('customerPhone').value,
email: document.getElementById('customerEmail').value,
spent: parseFloat(document.getElementById('customerSpent').value)
};
if (state.currentEditId) {
const index = state.customers.findIndex(c => c.id === state.currentEditId);
if (index !== -1) {
state.customers[index] = customer;
showNotification('客户信息更新成功', 'success');
}
} else {
state.customers.push(customer);
showNotification('客户添加成功', 'success');
}
localStorage.setItem('sales_customers', JSON.stringify(state.customers));
renderCustomers();
updateDashboard();
closeModal('customerModal');
});
function renderCustomers() {
const tbody = document.getElementById('customersTableBody');
const emptyState = document.getElementById('emptyCustomers');
tbody.innerHTML = '';
if (state.customers.length === 0) {
emptyState.style.display = 'block';
return;
}
emptyState.style.display = 'none';
state.customers.forEach(customer => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${customer.id}</td>
<td>${customer.name}</td>
<td>${customer.phone}</td>
<td>${customer.email}</td>
<td>¥${customer.spent.toFixed(2)}</td>
<td>
<button class="action-btn action-edit" onclick="editCustomer(${customer.id})">编辑</button>
<button class="action-btn action-delete" onclick="deleteCustomer(${customer.id})">删除</button>
</td>
`;
tbody.appendChild(row);
});
}
function editCustomer(id) {
const customer = state.customers.find(c => c.id === id);
if (customer) {
state.currentEditId = id;
document.getElementById('customerModalTitle').textContent = '编辑客户';
document.getElementById('customerId').value = customer.id;
document.getElementById('customerName').value = customer.name;
document.getElementById('customerPhone').value = customer.phone;
document.getElementById('customerEmail').value = customer.email;
document.getElementById('customerSpent').value = customer.spent;
openModal('customerModal');
}
}
function deleteCustomer(id) {
if (confirm('确定要删除这个客户吗?')) {
state.customers = state.customers.filter(c => c.id !== id);
localStorage.setItem('sales_customers', JSON.stringify(state.customers));
renderCustomers();
updateDashboard();
showNotification('客户已删除', 'success');
}
}
// 仪表盘更新
function updateDashboard() {
const totalSales = state.orders.reduce((sum, order) => sum + order.amount, 0);
document.getElementById('totalSales').textContent = `¥${totalSales.toFixed(2)}`;
document.getElementById('totalOrders').textContent = state.orders.length;
document.getElementById('totalCustomers').textContent = state.customers.length;
document.getElementById('totalProducts').textContent = state.products.reduce((sum, p) => sum + p.stock, 0);
updateSalesChart();
}
// 图表初始化
let salesChart = null;
function updateSalesChart() {
const ctx = document.getElementById('salesChart').getContext('2d');
const labels = [];
const data = [];
const today = new Date();
for (let i = 6; i >= 0; i--) {
const date = new Date(today);
date.setDate(date.getDate() - i);
const dateStr = date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' });
labels.push(dateStr);
const dailySales = Math.floor(Math.random() * 10000) + 2000;
data.push(dailySales);
}
if (salesChart) {
salesChart.destroy();
}
salesChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: '销售额 (¥)',
data: data,
borderColor: '#4895ef',
backgroundColor: 'rgba(72, 149, 239, 0.1)',
borderWidth: 3,
pointBackgroundColor: '#4895ef',
pointRadius: 4,
pointHoverRadius: 6,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
tooltip: {
backgroundColor: 'rgba(0, 0, 0, 0.7)',
titleColor: '#fff',
bodyColor: '#fff',
borderColor: '#4895ef',
borderWidth: 1,
padding: 12,
displayColors: false,
callbacks: {
label: function(context) {
return '¥' + context.parsed.y.toLocaleString();
}
}
}
},
scales: {
y: {
beginAtZero: true,
grid: {
color: 'rgba(255, 255, 255, 0.1)'
},
ticks: {
color: 'rgba(255, 255, 255, 0.6)',
callback: function(value) {
return '¥' + value.toLocaleString();
}
}
},
x: {
grid: {
display: false
},
ticks: {
color: 'rgba(255, 255, 255, 0.6)'
}
}
}
}
});
}
// 通知系统
function showNotification(message, type = 'success') {
const notification = document.getElementById('notification');
const messageEl = document.getElementById('notificationMessage');
messageEl.textContent = message;
notification.className = 'notification show';
if (type === 'error') {
notification.classList.add('error');
} else if (type === 'warning') {
notification.classList.add('warning');
}
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
}
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
const today = new Date().toISOString().split('T')[0];
if (document.getElementById('orderDate')) {
document.getElementById('orderDate').value = today;
}
// 防止iOS双击缩放
document.addEventListener('touchstart', function() {}, {passive: true});
});
// 暴露全局函数供HTML事件调用
window.editProduct = editProduct;
window.deleteProduct = deleteProduct;
window.deleteOrder = deleteOrder;
window.completeOrder = completeOrder;
window.editCustomer = editCustomer;
window.deleteCustomer = deleteCustomer;
</script>
</body>
</html>
系统功能说明
这个销售管理系统包含以下核心功能:
-
登录系统:
- 用户名: admin
- 密码: 123456
- 简单认证(实际应用中应使用后端验证)
-
仪表盘:
- 显示关键指标:总销售额、订单数量、客户总数、产品库存
- 销售趋势图表(使用Chart.js)
- 数据可视化展示
-
产品管理:
- 添加新产品
- 编辑现有产品
- 删除产品
- 产品列表展示(名称、类别、价格、库存)
-
订单管理:
- 创建新订单(选择客户和产品)
- 完成订单(更新状态)
- 删除订单
- 订单列表(包含客户、产品、数量、金额、日期、状态)
-
客户管理:
- 添加新客户
- 编辑客户信息
- 删除客户
- 客户列表(包含姓名、电话、邮箱、消费金额)
-
数据持久化:
- 使用浏览器localStorage存储所有数据
- 刷新页面数据不会丢失
-
响应式设计:
- 适配桌面和移动设备
- 移动端侧边栏菜单
设计亮点
-
现代化UI:
- 深色主题,减少视觉疲劳
- 玻璃态设计(glassmorphism)
- 平滑过渡动画
- 卡片式布局
-
用户体验:
- 直观的导航
- 即时反馈(通知系统)
- 表单验证
- 操作确认(删除操作)
-
数据可视化:
- 关键指标卡片
- 销售趋势图表
- 状态标签(已完成、处理中)
-
代码结构:
- 模块化JavaScript
- 状态管理
- 清晰的HTML结构
- 响应式CSS
这个系统可以直接在浏览器中运行,无需任何后端支持。所有数据都存储在浏览器的localStorage中,刷新页面后数据仍然保留