
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>销售数据可视化仪表盘</title><!-- 引入Tailwind CSS --><script src="https://cdn.tailwindcss.com"></script><!-- 引入Font Awesome --><link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet"><!-- 引入Chart.js --><script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script><!-- 配置Tailwind自定义颜色和字体 --><script>tailwind.config = {theme: {extend: {colors: {primary: '#165DFF',secondary: '#36CFC9',accent: '#722ED1',success: '#52C41A',warning: '#FAAD14',danger: '#F5222D',info: '#1890FF',dark: '#1D2129','gray-light': '#F2F3F5','gray-medium': '#C9CDD4'},fontFamily: {inter: ['Inter', 'system-ui', 'sans-serif'],},}}}</script><!-- 自定义工具类 --><style type="text/tailwindcss">@layer utilities {.content-auto {content-visibility: auto;}.card-shadow {box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);}.card-hover {transition: all 0.3s ease;}.card-hover:hover {transform: translateY(-5px);box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);}.animate-fade-in {animation: fadeIn 0.5s ease-in-out;}.animate-slide-up {animation: slideUp 0.5s ease-out;}}@keyframes fadeIn {from { opacity: 0; }to { opacity: 1; }}@keyframes slideUp {from { transform: translateY(20px); opacity: 0; }to { transform: translateY(0); opacity: 1; }}</style>
</head>
<body class="bg-gray-50 font-inter text-dark"><!-- 顶部导航栏 --><header class="bg-white shadow-sm fixed top-0 left-0 right-0 z-50 transition-all duration-300" id="navbar"><div class="container mx-auto px-4 py-3 flex items-center justify-between"><div class="flex items-center space-x-2"><i class="fa fa-line-chart text-primary text-2xl"></i><h1 class="text-xl font-bold text-primary">销售数据分析平台</h1></div><div class="hidden md:flex items-center space-x-6"><a href="#" class="text-primary font-medium">仪表盘</a><a href="#" class="text-gray-600 hover:text-primary transition-colors">报表</a><a href="#" class="text-gray-600 hover:text-primary transition-colors">分析</a><a href="#" class="text-gray-600 hover:text-primary transition-colors">设置</a></div><div class="flex items-center space-x-4"><button id="refreshBtn" class="p-2 rounded-full hover:bg-gray-light transition-colors text-gray-600"><i class="fa fa-refresh"></i></button><div class="relative"><button class="flex items-center space-x-2 focus:outline-none"><img src="https://picsum.photos/id/1005/40/40" alt="用户头像" class="w-8 h-8 rounded-full object-cover border-2 border-primary"><span class="hidden md:inline text-sm font-medium">管理员</span><i class="fa fa-angle-down text-gray-500"></i></button></div></div></div></header><!-- 主内容区 --><main class="container mx-auto px-4 pt-24 pb-16"><!-- 页面标题和过滤器 --><div class="mb-8 animate-fade-in"><div class="flex flex-col md:flex-row md:items-center md:justify-between mb-6"><div><h2 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold mb-2">销售数据概览</h2><p class="text-gray-500">实时监控销售趋势和关键指标</p></div><div class="flex flex-wrap gap-3 mt-4 md:mt-0"><div class="relative"><select id="timeFilter" class="appearance-none bg-white border border-gray-300 rounded-lg py-2 pl-4 pr-10 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary"><option value="month">近30天</option><option value="quarter">近90天</option><option value="year">近一年</option></select><div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-500"><i class="fa fa-chevron-down text-xs"></i></div></div><div class="relative"><select id="regionFilter" class="appearance-none bg-white border border-gray-300 rounded-lg py-2 pl-4 pr-10 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary"><option value="all">全部区域</option><option value="north">北区</option><option value="south">南区</option><option value="east">东区</option><option value="west">西区</option></select><div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-500"><i class="fa fa-chevron-down text-xs"></i></div></div><button class="bg-primary hover:bg-primary/90 text-white rounded-lg py-2 px-4 text-sm transition-colors flex items-center"><i class="fa fa-download mr-2"></i>导出报表</button></div></div><!-- 日期显示 --><div class="bg-white rounded-lg p-3 shadow-sm flex items-center justify-between"><div class="text-sm text-gray-500"><i class="fa fa-calendar-o mr-2"></i><span id="dateRange">2023年5月15日 - 2023年6月14日</span></div><div class="text-sm text-gray-500 flex items-center"><span class="mr-4"><i class="fa fa-refresh mr-1"></i> 上次更新: <span id="lastUpdate">今天 09:45</span></span><span class="text-success"><i class="fa fa-check-circle mr-1"></i> 数据已同步</span></div></div></div><!-- 关键指标卡片 --><div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8"><!-- 总销售额 --><div class="bg-white rounded-xl p-6 card-shadow card-hover animate-slide-up" style="animation-delay: 0.1s"><div class="flex justify-between items-start mb-4"><div><p class="text-gray-500 text-sm">总销售额</p><h3 class="text-2xl font-bold mt-1" id="totalSales">¥0</h3></div><div class="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center text-primary"><i class="fa fa-rmb"></i></div></div><div class="flex items-center"><span class="text-success text-sm flex items-center"><i class="fa fa-arrow-up mr-1"></i><span id="salesGrowth">0%</span></span><span class="text-gray-400 text-xs ml-2">相比上期</span></div></div><!-- 订单数量 --><div class="bg-white rounded-xl p-6 card-shadow card-hover animate-slide-up" style="animation-delay: 0.2s"><div class="flex justify-between items-start mb-4"><div><p class="text-gray-500 text-sm">订单数量</p><h3 class="text-2xl font-bold mt-1" id="orderCount">0</h3></div><div class="w-10 h-10 rounded-full bg-secondary/10 flex items-center justify-center text-secondary"><i class="fa fa-shopping-cart"></i></div></div><div class="flex items-center"><span class="text-success text-sm flex items-center"><i class="fa fa-arrow-up mr-1"></i><span id="orderGrowth">0%</span></span><span class="text-gray-400 text-xs ml-2">相比上期</span></div></div><!-- 平均客单价 --><div class="bg-white rounded-xl p-6 card-shadow card-hover animate-slide-up" style="animation-delay: 0.3s"><div class="flex justify-between items-start mb-4"><div><p class="text-gray-500 text-sm">平均客单价</p><h3 class="text-2xl font-bold mt-1" id="avgOrderValue">¥0</h3></div><div class="w-10 h-10 rounded-full bg-accent/10 flex items-center justify-center text-accent"><i class="fa fa-usd"></i></div></div><div class="flex items-center"><span class="text-danger text-sm flex items-center"><i class="fa fa-arrow-down mr-1"></i><span id="avgValueGrowth">0%</span></span><span class="text-gray-400 text-xs ml-2">相比上期</span></div></div><!-- 转化率 --><div class="bg-white rounded-xl p-6 card-shadow card-hover animate-slide-up" style="animation-delay: 0.4s"><div class="flex justify-between items-start mb-4"><div><p class="text-gray-500 text-sm">转化率</p><h3 class="text-2xl font-bold mt-1" id="conversionRate">0%</h3></div><div class="w-10 h-10 rounded-full bg-success/10 flex items-center justify-center text-success"><i class="fa fa-exchange"></i></div></div><div class="flex items-center"><span class="text-success text-sm flex items-center"><i class="fa fa-arrow-up mr-1"></i><span id="conversionGrowth">0%</span></span><span class="text-gray-400 text-xs ml-2">相比上期</span></div></div></div><!-- 图表区域 --><div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8"><!-- 销售趋势图 --><div class="bg-white rounded-xl p-6 card-shadow lg:col-span-2 animate-slide-up" style="animation-delay: 0.5s"><div class="flex justify-between items-center mb-6"><h3 class="font-bold text-lg">销售趋势分析</h3><div class="flex space-x-2"><button class="chart-filter-btn active px-3 py-1 text-xs rounded-full bg-primary text-white" data-period="day">按日</button><button class="chart-filter-btn px-3 py-1 text-xs rounded-full bg-gray-light text-gray-600 hover:bg-gray-200" data-period="week">按周</button><button class="chart-filter-btn px-3 py-1 text-xs rounded-full bg-gray-light text-gray-600 hover:bg-gray-200" data-period="month">按月</button></div></div><div class="h-[300px]"><canvas id="salesTrendChart"></canvas></div></div><!-- 销售分布 --><div class="bg-white rounded-xl p-6 card-shadow animate-slide-up" style="animation-delay: 0.6s"><div class="flex justify-between items-center mb-6"><h3 class="font-bold text-lg">销售区域分布</h3><button class="text-gray-400 hover:text-gray-600"><i class="fa fa-ellipsis-v"></i></button></div><div class="h-[300px] flex items-center justify-center"><canvas id="salesDistributionChart"></canvas></div></div></div><!-- 产品销售和区域表现 --><div class="grid grid-cols-1 lg:grid-cols-5 gap-6"><!-- 产品销售排行 --><div class="bg-white rounded-xl p-6 card-shadow lg:col-span-3 animate-slide-up" style="animation-delay: 0.7s"><div class="flex justify-between items-center mb-6"><h3 class="font-bold text-lg">产品销售排行</h3><button class="text-primary text-sm hover:underline flex items-center">查看全部 <i class="fa fa-angle-right ml-1"></i></button></div><div class="overflow-x-auto"><table class="min-w-full"><thead><tr class="border-b border-gray-100"><th class="py-3 px-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">产品名称</th><th class="py-3 px-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">类别</th><th class="py-3 px-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">销售额</th><th class="py-3 px-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">销量</th><th class="py-3 px-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">同比增长</th></tr></thead><tbody id="productTableBody" class="divide-y divide-gray-100"><!-- 产品数据将通过JavaScript动态生成 --></tbody></table></div></div><!-- 区域销售表现 --><div class="bg-white rounded-xl p-6 card-shadow lg:col-span-2 animate-slide-up" style="animation-delay: 0.8s"><div class="flex justify-between items-center mb-6"><h3 class="font-bold text-lg">区域销售表现</h3><div class="relative"><select id="regionMetricFilter" class="appearance-none bg-gray-light border-none rounded-lg py-1.5 pl-3 pr-8 text-xs focus:outline-none focus:ring-1 focus:ring-primary"><option value="sales">按销售额</option><option value="orders">按订单数</option><option value="growth">按增长率</option></select><div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-500"><i class="fa fa-chevron-down text-xs"></i></div></div></div><div class="space-y-5" id="regionPerformance"><!-- 区域数据将通过JavaScript动态生成 --></div></div></div></main><!-- 页脚 --><footer class="bg-white border-t border-gray-200 py-6"><div class="container mx-auto px-4"><div class="flex flex-col md:flex-row justify-between items-center"><div class="text-gray-500 text-sm mb-4 md:mb-0">© 2023 销售数据分析平台. 保留所有权利.</div><div class="flex space-x-6"><a href="#" class="text-gray-400 hover:text-primary transition-colors"><i class="fa fa-question-circle"></i><span class="ml-1 text-sm">帮助中心</span></a><a href="#" class="text-gray-400 hover:text-primary transition-colors"><i class="fa fa-file-text-o"></i><span class="ml-1 text-sm">使用文档</span></a><a href="#" class="text-gray-400 hover:text-primary transition-colors"><i class="fa fa-envelope-o"></i><span class="ml-1 text-sm">联系我们</span></a></div></div></div></footer><script>// 页面加载完成后初始化document.addEventListener('DOMContentLoaded', function() {// 生成模拟数据(1万至100万区间)const salesData = generateSalesData();// 渲染数据到页面renderDashboard(salesData);// 初始化图表initCharts(salesData);// 添加事件监听器addEventListeners();// 导航栏滚动效果window.addEventListener('scroll', function() {const navbar = document.getElementById('navbar');if (window.scrollY > 10) {navbar.classList.add('py-2', 'shadow');navbar.classList.remove('py-3', 'shadow-sm');} else {navbar.classList.add('py-3', 'shadow-sm');navbar.classList.remove('py-2', 'shadow');}});});// 生成模拟数据(1万至100万区间)function generateSalesData() {// 生成30天的销售数据const days = 30;const dailySales = [];const dailyOrders = [];for (let i = 0; i < days; i++) {// 销售额:1万至100万之间的随机数const sales = Math.floor(Math.random() * 990000) + 10000;// 订单数:根据销售额估算,平均客单价约2万const orders = Math.max(1, Math.floor(sales / 20000));dailySales.push(sales);dailyOrders.push(orders);// 添加一些趋势,使数据更真实if (i > 0) {const trend = Math.random() > 0.6 ? 1 : -1;const variation = Math.floor(dailySales[i] * (0.05 + Math.random() * 0.15) * trend);dailySales[i] = Math.max(10000, dailySales[i] + variation);}}// 计算总计const totalSales = dailySales.reduce((sum, val) => sum + val, 0);const totalOrders = dailyOrders.reduce((sum, val) => sum + val, 0);const avgOrderValue = Math.round(totalSales / totalOrders);// 模拟上期数据,用于计算增长率const lastPeriodSales = totalSales * (0.9 + Math.random() * 0.15);const lastPeriodOrders = totalOrders * (0.9 + Math.random() * 0.15);const lastPeriodAvgValue = Math.round(lastPeriodSales / lastPeriodOrders);// 计算增长率const salesGrowth = ((totalSales - lastPeriodSales) / lastPeriodSales * 100).toFixed(1);const orderGrowth = ((totalOrders - lastPeriodOrders) / lastPeriodOrders * 100).toFixed(1);const avgValueGrowth = ((avgOrderValue - lastPeriodAvgValue) / lastPeriodAvgValue * 100).toFixed(1);// 转化率(随机5%-15%)const conversionRate = (5 + Math.random() * 10).toFixed(1);const lastPeriodConversion = (5 + Math.random() * 10).toFixed(1);const conversionGrowth = ((conversionRate - lastPeriodConversion) / lastPeriodConversion * 100).toFixed(1);// 区域销售分布const regions = [{ name: '北区', sales: Math.floor(totalSales * (0.2 + Math.random() * 0.15)), color: '#165DFF' },{ name: '南区', sales: Math.floor(totalSales * (0.2 + Math.random() * 0.15)), color: '#36CFC9' },{ name: '东区', sales: Math.floor(totalSales * (0.2 + Math.random() * 0.15)), color: '#722ED1' },{ name: '西区', sales: Math.floor(totalSales * (0.2 + Math.random() * 0.15)), color: '#FAAD14' }];// 确保区域销售总和接近总销售额const regionSalesTotal = regions.reduce((sum, r) => sum + r.sales, 0);const ratio = totalSales / regionSalesTotal;regions.forEach(r => r.sales = Math.floor(r.sales * ratio));// 为每个区域计算增长率regions.forEach(region => {region.growth = (5 - Math.random() * 10 + Math.random() * 10).toFixed(1);region.orders = Math.max(1, Math.floor(region.sales / (15000 + Math.random() * 15000)));});// 产品数据const productCategories = ['电子产品', '服装鞋帽', '家居用品', '食品饮料', '图书音像'];const products = [];for (let i = 0; i < 8; i++) {const category = productCategories[Math.floor(Math.random() * productCategories.length)];const sales = Math.floor(Math.random() * 990000) + 10000; // 1万至100万const volume = Math.max(1, Math.floor(sales / (5000 + Math.random() * 15000)));const growth = (5 - Math.random() * 10 + Math.random() * 10).toFixed(1);products.push({id: i + 1,name: `产品${String.fromCharCode(65 + i)}`,category,sales,volume,growth});}// 按销售额排序products.sort((a, b) => b.sales - a.sales);// 生成日期标签const labels = [];const today = new Date();for (let i = days - 1; i >= 0; i--) {const date = new Date();date.setDate(today.getDate() - i);labels.push(`${date.getMonth() + 1}/${date.getDate()}`);}return {dailySales,dailyOrders,labels,totalSales,totalOrders,avgOrderValue,conversionRate,salesGrowth,orderGrowth,avgValueGrowth,conversionGrowth,regions,products};}// 渲染仪表盘数据function renderDashboard(data) {// 格式化数字为带逗号的形式const formatNumber = (num) => {return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");};// 渲染关键指标document.getElementById('totalSales').textContent = `¥${formatNumber(data.totalSales)}`;document.getElementById('orderCount').textContent = formatNumber(data.totalOrders);document.getElementById('avgOrderValue').textContent = `¥${formatNumber(data.avgOrderValue)}`;document.getElementById('conversionRate').textContent = `${data.conversionRate}%`;// 渲染增长率并设置颜色renderGrowthRate('salesGrowth', data.salesGrowth);renderGrowthRate('orderGrowth', data.orderGrowth);renderGrowthRate('avgValueGrowth', data.avgValueGrowth);renderGrowthRate('conversionGrowth', data.conversionGrowth);// 更新最后更新时间const now = new Date();document.getElementById('lastUpdate').textContent = `今天 ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;// 渲染产品表格const productTableBody = document.getElementById('productTableBody');productTableBody.innerHTML = '';data.products.forEach(product => {const row = document.createElement('tr');row.className = 'hover:bg-gray-50 transition-colors';row.innerHTML = `<td class="py-4 px-4 whitespace-nowrap"><div class="flex items-center"><div class="w-8 h-8 rounded-md bg-gray-100 flex items-center justify-center mr-3"><i class="fa fa-cube text-gray-500"></i></div><div><div class="font-medium">${product.name}</div></div></div></td><td class="py-4 px-4 whitespace-nowrap"><span class="px-2 py-1 text-xs rounded-full bg-gray-100 text-gray-600">${product.category}</span></td><td class="py-4 px-4 whitespace-nowrap font-medium">¥${formatNumber(product.sales)}</td><td class="py-4 px-4 whitespace-nowrap">${formatNumber(product.volume)}</td><td class="py-4 px-4 whitespace-nowrap"><span class="${product.growth >= 0 ? 'text-success' : 'text-danger'} text-sm flex items-center"><i class="fa fa-arrow-${product.growth >= 0 ? 'up' : 'down'} mr-1"></i>${Math.abs(product.growth)}%</span></td>`;productTableBody.appendChild(row);});// 渲染区域表现renderRegionPerformance(data.regions, 'sales');}// 渲染增长率function renderGrowthRate(elementId, value) {const element = document.getElementById(elementId);element.textContent = `${Math.abs(value)}%`;if (parseFloat(value) >= 0) {element.parentElement.className = 'text-success text-sm flex items-center';element.parentElement.innerHTML = `<i class="fa fa-arrow-up mr-1"></i><span id="${elementId}">${Math.abs(value)}%</span>`;} else {element.parentElement.className = 'text-danger text-sm flex items-center';element.parentElement.innerHTML = `<i class="fa fa-arrow-down mr-1"></i><span id="${elementId}">${Math.abs(value)}%</span>`;}}// 渲染区域表现function renderRegionPerformance(regions, metric) {const container = document.getElementById('regionPerformance');container.innerHTML = '';// 根据所选指标排序let sortedRegions = [...regions];if (metric === 'sales') {sortedRegions.sort((a, b) => b.sales - a.sales);} else if (metric === 'orders') {sortedRegions.sort((a, b) => b.orders - a.orders);} else if (metric === 'growth') {sortedRegions.sort((a, b) => b.growth - a.growth);}sortedRegions.forEach(region => {let value, label;if (metric === 'sales') {value = region.sales;label = `¥${formatNumber(value)}`;} else if (metric === 'orders') {value = region.orders;label = formatNumber(value);} else {value = Math.abs(region.growth);label = `${region.growth}%`;}// 计算最大值用于进度条const maxValue = metric === 'sales'? Math.max(...regions.map(r => r.sales)): metric === 'orders'? Math.max(...regions.map(r => r.orders)): 20; // 增长率最大20%const percentage = Math.min(100, Math.round((value / maxValue) * 100));const regionElement = document.createElement('div');regionElement.className = 'region-item';regionElement.innerHTML = `<div class="flex justify-between items-center mb-1"><div class="flex items-center"><div class="w-3 h-3 rounded-full mr-2" style="background-color: ${region.color}"></div><span class="text-sm font-medium">${region.name}</span></div><span class="text-sm font-semibold ${metric === 'growth' && region.growth < 0 ? 'text-danger' : metric === 'growth' ? 'text-success' : ''}">${label}</span></div><div class="w-full bg-gray-100 rounded-full h-2"><div class="h-2 rounded-full" style="width: ${percentage}%; background-color: ${region.color}"></div></div>`;container.appendChild(regionElement);});}// 初始化图表function initCharts(data) {// 销售趋势图const trendCtx = document.getElementById('salesTrendChart').getContext('2d');// 图表样式配置const gridLineColor = 'rgba(0, 0, 0, 0.05)';const tooltipBackgroundColor = 'rgba(255, 255, 255, 0.95)';const tooltipBorderColor = 'rgba(0, 0, 0, 0.1)';window.salesTrendChart = new Chart(trendCtx, {type: 'line',data: {labels: data.labels,datasets: [{label: '销售额',data: data.dailySales,borderColor: '#165DFF',backgroundColor: 'rgba(22, 93, 255, 0.1)',borderWidth: 2,pointBackgroundColor: '#FFFFFF',pointBorderColor: '#165DFF',pointBorderWidth: 2,pointRadius: 4,pointHoverRadius: 6,tension: 0.3,fill: true},{label: '订单数',data: data.dailyOrders,borderColor: '#722ED1',backgroundColor: 'transparent',borderWidth: 2,pointBackgroundColor: '#FFFFFF',pointBorderColor: '#722ED1',pointBorderWidth: 2,pointRadius: 4,pointHoverRadius: 6,tension: 0.3,fill: false,yAxisID: 'y1'}]},options: {responsive: true,maintainAspectRatio: false,interaction: {mode: 'index',intersect: false,},plugins: {legend: {position: 'top',align: 'end',labels: {usePointStyle: true,boxWidth: 6,font: {size: 12},padding: 20}},tooltip: {backgroundColor: tooltipBackgroundColor,titleColor: '#1D2129',bodyColor: '#4E5969',borderColor: tooltipBorderColor,borderWidth: 1,padding: 12,boxPadding: 6,usePointStyle: true,callbacks: {label: function(context) {let label = context.dataset.label || '';if (label) {label += ': ';}if (context.datasetIndex === 0) {label += '¥' + formatNumber(context.parsed.y);} else {label += formatNumber(context.parsed.y) + ' 单';}return label;}}}},scales: {x: {grid: {display: false},ticks: {maxRotation: 0,autoSkip: true,maxTicksLimit: 8}},y: {beginAtZero: true,grid: {color: gridLineColor},ticks: {callback: function(value) {if (value >= 1000000) {return '¥' + (value / 1000000).toFixed(1) + 'M';} else if (value >= 1000) {return '¥' + (value / 1000).toFixed(0) + 'K';}return '¥' + value;}