dm-design/商家端web/数据分析/客户分析.html

778 lines
24 KiB
HTML
Raw 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 lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据分析 - 商家端</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", , Arial, sans-serif;
background-color: #f5f5f5;
color: #333;
overflow-x: hidden;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 30px;
}
/* 筛选区域 */
.filter-section {
background: #fff;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.filter-row {
display: flex;
align-items: center;
gap: 20px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
align-items: center;
gap: 10px;
}
.filter-item label {
font-weight: 500;
color: #333;
min-width: 70px;
}
.filter-item select,
.filter-item input {
padding: 8px 12px;
border: 1px solid #d9d9d9;
border-radius: 4px;
min-width: 120px;
font-size: 14px;
transition: all 0.3s ease;
}
.filter-item select:focus,
.filter-item input:focus {
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
outline: none;
}
.btn {
padding: 10px 20px;
border-radius: 5px;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 5px;
border: none;
cursor: pointer;
}
.btn-primary {
background-color: #1890ff;
color: #fff;
}
.btn-primary:hover {
background-color: #40a9ff;
transform: translateY(-2px);
}
.btn-success {
background-color: #52c41a;
color: #fff;
}
.btn-success:hover {
background-color: #73d13d;
transform: translateY(-2px);
}
/* 数据卡片区域 */
.stats-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stats-card {
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
position: relative;
overflow: hidden;
}
.stats-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background: linear-gradient(45deg, #1890ff, #40a9ff);
}
.stats-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 15px;
}
.stats-item:last-child {
margin-bottom: 0;
}
.stats-label {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #666;
}
.stats-icon {
width: 20px;
height: 20px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: #fff;
}
.icon-view { background-color: #1890ff; }
.icon-visitor { background-color: #52c41a; }
.icon-new { background-color: #faad14; }
.icon-order { background-color: #f5222d; }
.icon-rate { background-color: #722ed1; }
.icon-purchase { background-color: #13c2c2; }
.stats-value {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.stats-number {
font-size: 24px;
font-weight: bold;
color: #333;
margin-bottom: 2px;
}
.stats-change {
font-size: 12px;
display: flex;
align-items: center;
gap: 2px;
}
.stats-change.positive {
color: #52c41a;
}
.stats-change.negative {
color: #f5222d;
}
.stats-change.neutral {
color: #666;
}
/* 图表区域 */
.charts-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
gap: 20px;
}
.chart-section {
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #e8e8e8;
}
.chart-title {
font-size: 16px;
font-weight: 600;
color: #333;
}
.chart-legend {
display: flex;
align-items: center;
gap: 20px;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #666;
}
.legend-color {
width: 12px;
height: 12px;
border-radius: 50%;
}
.chart-container {
position: relative;
height: 300px;
width: 100%;
}
/* 响应式设计 */
@media (max-width: 768px) {
.container {
padding: 15px;
}
.filter-row {
flex-direction: column;
align-items: stretch;
}
.filter-item {
flex-direction: column;
align-items: stretch;
gap: 5px;
}
.filter-item label {
min-width: auto;
}
.stats-section {
grid-template-columns: 1fr;
}
.charts-section {
grid-template-columns: 1fr;
}
.chart-header {
flex-direction: column;
align-items: stretch;
gap: 15px;
}
.chart-legend {
justify-content: center;
flex-wrap: wrap;
}
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.stats-card,
.filter-section,
.chart-section {
animation: fadeIn 0.6s ease-out;
}
.stats-card:nth-child(2) { animation-delay: 0.1s; }
.stats-card:nth-child(3) { animation-delay: 0.2s; }
</style>
</head>
<body>
<div class="container">
<!-- 筛选区域 -->
<div class="filter-section">
<div class="filter-row">
<div class="filter-item">
<label>时间范围</label>
<input type="date" id="startDate">
<span style="margin: 0 5px;"></span>
<input type="date" id="endDate">
</div>
<button class="btn btn-primary" onclick="queryData()">查询</button>
<button class="btn btn-success" onclick="exportData()">导出</button>
</div>
</div>
<!-- 数据卡片区域 -->
<div class="stats-section">
<div class="stats-card">
<div class="stats-item">
<div class="stats-label">
<div class="stats-icon icon-view">👁</div>
展现量(次)
</div>
<div class="stats-value">
<div class="stats-number">0</div>
<div class="stats-change neutral">↑ 0.00%</div>
</div>
</div>
<div class="stats-item">
<div class="stats-label">
<div class="stats-icon icon-order">👤</div>
支付订单人数(人)
</div>
<div class="stats-value">
<div class="stats-number">0</div>
<div class="stats-change neutral"></div>
</div>
</div>
</div>
<div class="stats-card">
<div class="stats-item">
<div class="stats-label">
<div class="stats-icon icon-visitor">👥</div>
浏览人数(人)
</div>
<div class="stats-value">
<div class="stats-number">0</div>
<div class="stats-change neutral">↑ 0.00%</div>
</div>
</div>
<div class="stats-item">
<div class="stats-label">
<div class="stats-icon icon-rate">📊</div>
支付订单转化率(%
</div>
<div class="stats-value">
<div class="stats-number">0.00%</div>
<div class="stats-change neutral"></div>
</div>
</div>
</div>
<div class="stats-card">
<div class="stats-item">
<div class="stats-label">
<div class="stats-icon icon-new"></div>
新客数量(人)
</div>
<div class="stats-value">
<div class="stats-number">0</div>
<div class="stats-change neutral">↑ 0.00%</div>
</div>
</div>
<div class="stats-item">
<div class="stats-label">
<div class="stats-icon icon-purchase">💰</div>
客户复购率(%
</div>
<div class="stats-value">
<div class="stats-number">0.00%</div>
<div class="stats-change neutral"></div>
</div>
</div>
</div>
</div>
<!-- 图表区域 -->
<div class="charts-section">
<div class="chart-section">
<div class="chart-header">
<div class="chart-title">浏览数量趋势</div>
<div class="chart-legend">
<div class="legend-item">
<div class="legend-color" style="background-color: #1890ff;"></div>
浏览数量
</div>
</div>
</div>
<div class="chart-container">
<canvas id="viewChart"></canvas>
</div>
</div>
<div class="chart-section">
<div class="chart-header">
<div class="chart-title">浏览人数趋势</div>
<div class="chart-legend">
<div class="legend-item">
<div class="legend-color" style="background-color: #52c41a;"></div>
浏览人数
</div>
</div>
</div>
<div class="chart-container">
<canvas id="visitorChart"></canvas>
</div>
</div>
<div class="chart-section">
<div class="chart-header">
<div class="chart-title">新客数量趋势</div>
<div class="chart-legend">
<div class="legend-item">
<div class="legend-color" style="background-color: #faad14;"></div>
新客数量
</div>
</div>
</div>
<div class="chart-container">
<canvas id="newCustomerChart"></canvas>
</div>
</div>
<div class="chart-section">
<div class="chart-header">
<div class="chart-title">支付订单人数趋势</div>
<div class="chart-legend">
<div class="legend-item">
<div class="legend-color" style="background-color: #f5222d;"></div>
支付订单人数
</div>
</div>
</div>
<div class="chart-container">
<canvas id="orderChart"></canvas>
</div>
</div>
</div>
</div>
<script>
// 初始化图表数据
const chartLabels = [];
const chartData = [];
// 生成最近30天的日期标签
for (let i = 29; i >= 0; i--) {
const date = new Date();
date.setDate(date.getDate() - i);
chartLabels.push(String(date.getMonth() + 1).padStart(2, '0') + '-' + String(date.getDate()).padStart(2, '0'));
chartData.push(0);
}
// 设置默认日期范围最近30天
const today = new Date();
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(today.getDate() - 30);
document.getElementById('startDate').valueAsDate = thirtyDaysAgo;
document.getElementById('endDate').valueAsDate = today;
// 图表配置
const chartConfig = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
grid: {
color: '#f0f0f0'
},
ticks: {
color: '#666',
font: {
size: 12
}
}
},
x: {
grid: {
color: '#f0f0f0'
},
ticks: {
color: '#666',
font: {
size: 12
},
maxRotation: 45
}
}
},
interaction: {
intersect: false,
mode: 'index'
},
elements: {
point: {
hoverRadius: 6
}
}
};
// 初始化浏览数量图表
const viewChart = new Chart(document.getElementById('viewChart').getContext('2d'), {
type: 'line',
data: {
labels: chartLabels,
datasets: [{
label: '浏览数量',
data: chartData,
borderColor: '#1890ff',
backgroundColor: 'rgba(24, 144, 255, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4,
pointRadius: 4,
pointBackgroundColor: '#1890ff',
pointBorderColor: '#fff',
pointBorderWidth: 2
}]
},
options: chartConfig
});
// 初始化浏览人数图表
const visitorChart = new Chart(document.getElementById('visitorChart').getContext('2d'), {
type: 'line',
data: {
labels: chartLabels,
datasets: [{
label: '浏览人数',
data: chartData,
borderColor: '#52c41a',
backgroundColor: 'rgba(82, 196, 26, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4,
pointRadius: 4,
pointBackgroundColor: '#52c41a',
pointBorderColor: '#fff',
pointBorderWidth: 2
}]
},
options: chartConfig
});
// 初始化新客数量图表
const newCustomerChart = new Chart(document.getElementById('newCustomerChart').getContext('2d'), {
type: 'line',
data: {
labels: chartLabels,
datasets: [{
label: '新客数量',
data: chartData,
borderColor: '#faad14',
backgroundColor: 'rgba(250, 173, 20, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4,
pointRadius: 4,
pointBackgroundColor: '#faad14',
pointBorderColor: '#fff',
pointBorderWidth: 2
}]
},
options: chartConfig
});
// 初始化支付订单人数图表
const orderChart = new Chart(document.getElementById('orderChart').getContext('2d'), {
type: 'line',
data: {
labels: chartLabels,
datasets: [{
label: '支付订单人数',
data: chartData,
borderColor: '#f5222d',
backgroundColor: 'rgba(245, 34, 45, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4,
pointRadius: 4,
pointBackgroundColor: '#f5222d',
pointBorderColor: '#fff',
pointBorderWidth: 2
}]
},
options: chartConfig
});
// 查询数据函数
function queryData() {
const startDate = document.getElementById('startDate').value;
const endDate = document.getElementById('endDate').value;
if (!startDate || !endDate) {
showNotification('请选择时间范围', 'error');
return;
}
if (new Date(startDate) > new Date(endDate)) {
showNotification('开始时间不能大于结束时间', 'error');
return;
}
console.log('查询参数:', { startDate, endDate });
updateStatsCards();
updateCharts();
showNotification('数据查询成功', 'success');
}
// 导出数据函数
function exportData() {
const startDate = document.getElementById('startDate').value;
const endDate = document.getElementById('endDate').value;
if (!startDate || !endDate) {
showNotification('请选择时间范围', 'error');
return;
}
console.log('导出数据');
showNotification('数据导出中...', 'info');
setTimeout(() => {
showNotification('数据导出完成', 'success');
}, 2000);
}
// 更新统计卡片数据
function updateStatsCards() {
const statsNumbers = document.querySelectorAll('.stats-number');
statsNumbers.forEach((element, index) => {
if (element.textContent.includes('%')) {
element.textContent = (Math.random() * 10).toFixed(2) + '%';
} else {
element.textContent = Math.floor(Math.random() * 1000);
}
});
const statsChanges = document.querySelectorAll('.stats-change');
statsChanges.forEach(element => {
const change = (Math.random() * 20 - 10).toFixed(2);
if (change > 0) {
element.className = 'stats-change positive';
element.textContent = `${change}%`;
} else if (change < 0) {
element.className = 'stats-change negative';
element.textContent = `${Math.abs(change)}%`;
} else {
element.className = 'stats-change neutral';
element.textContent = '0.00%';
}
});
}
// 更新所有图表数据
function updateCharts() {
const newData1 = chartData.map(() => Math.floor(Math.random() * 100));
const newData2 = chartData.map(() => Math.floor(Math.random() * 80));
const newData3 = chartData.map(() => Math.floor(Math.random() * 50));
const newData4 = chartData.map(() => Math.floor(Math.random() * 60));
viewChart.data.datasets[0].data = newData1;
visitorChart.data.datasets[0].data = newData2;
newCustomerChart.data.datasets[0].data = newData3;
orderChart.data.datasets[0].data = newData4;
viewChart.update('active');
visitorChart.update('active');
newCustomerChart.update('active');
orderChart.update('active');
}
// 显示通知函数
function showNotification(message, type) {
const notification = document.createElement('div');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
border-radius: 4px;
color: white;
font-weight: 500;
z-index: 9999;
animation: slideIn 0.3s ease-out;
background-color: ${type === 'success' ? '#52c41a' : type === 'error' ? '#f5222d' : '#1890ff'};
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.animation = 'slideOut 0.3s ease-in';
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
}, 3000);
}
// 添加动画样式
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
`;
document.head.appendChild(style);
// 页面加载完成后的初始化
document.addEventListener('DOMContentLoaded', function() {
console.log('数据分析页面加载完成');
setTimeout(() => {
updateStatsCards();
updateCharts();
}, 1000);
});
</script>
</body>
</html>