daShangDao_erpWeb/public/1.html
2026-06-15 14:30:37 +08:00

683 lines
22 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>
<!-- 使用 ECharts 5 -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Microsoft YaHei', sans-serif; }
body {
background: #0a1020;
overflow: hidden;
color: #fff;
}
.dashboard {
width: 100vw;
height: 100vh;
padding: 20px;
display: flex;
flex-direction: column;
background: #0a1020;
gap: 20px;
}
/* 顶部KPI行 */
.kpi-row {
margin-top: 30px;
margin-bottom: 65px;
display: flex;
gap: 18px;
height: 130px;
}
.kpi-card {
flex: 1;
background: rgba(20, 32, 55, 0.8);
backdrop-filter: blur(5px);
border: 1px solid #2a4a99;
border-radius: 24px;
padding: 20px;
border-bottom: 4px solid #2a4a99;
box-shadow: 0 10px 30px -10px #000;
}
.kpi-card.today {
border-bottom-color: #ffb347;
background: rgba(32, 58, 106, 0.8);
}
.kpi-label {
font-size: 15px;
color: #aac0ff;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 6px;
}
.dot {
width: 10px;
height: 10px;
border-radius: 10px;
background: #ffb347;
box-shadow: 0 0 10px #ffb347;
}
.kpi-value {
font-size: 46px;
font-weight: 700;
line-height: 1;
color: #fff;
text-shadow: 0 0 15px #4d7eff;
}
.kpi-sub {
font-size: 13px;
color: #99b0e0;
margin-top: 8px;
padding-left: 8px;
border-left: 3px solid #ffb347;
}
/* 图表区域 - 网格布局 */
.charts-grid {
flex: 1;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto 1fr;
gap: 20px;
min-height: 0;
}
.chart-card {
background: rgba(15, 25, 45, 0.8);
backdrop-filter: blur(5px);
border: 1px solid #2a4a99;
border-radius: 24px;
padding: 16px;
display: flex;
flex-direction: column;
}
.chart-title {
font-size: 16px;
color: #c0d0ff;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
}
.chart-title .dot {
width: 8px;
height: 8px;
background: #4fe0b0;
box-shadow: 0 0 10px #4fe0b0;
}
.chart-container {
margin-top: 40px;
flex: 1;
width: 100%;
min-height: 300px;
}
/* 右侧面板特殊处理 */
.span-2 {
grid-column: span 2;
}
/* 城市列表 */
.city-list {
list-style: none;
height: 100%;
overflow-y: auto;
padding-right: 5px;
}
.city-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #2a4a99;
font-size: 14px;
color: #d0e0ff;
}
.city-item span:last-child {
background: #1e3a7a;
padding: 2px 12px;
border-radius: 20px;
font-size: 13px;
}
/* 滚动条 */
.city-list::-webkit-scrollbar {
width: 4px;
}
.city-list::-webkit-scrollbar-track {
background: #1a2a4a;
}
.city-list::-webkit-scrollbar-thumb {
background: #3a6ab0;
border-radius: 2px;
}
/* 底部标记 */
.footer-note {
grid-column: span 3;
text-align: center;
color: #7a98d0;
font-size: 13px;
border-top: 1px dashed #2a4a99;
padding-top: 20px;
margin-top: 30px;
}
.glow { color: #ffb347; font-weight: 600; }
</style>
</head>
<body>
<div class="dashboard">
<!-- 顶部KPI: 今日订单唯一真实 -->
<div class="kpi-row">
<div class="kpi-card today">
<div class="kpi-label">📅 今日订单 </div>
<div class="kpi-value" id="todayOrder">0</div>
<!-- <div class="kpi-sub">📦 唯一真实数据,固定不变</div> -->
</div>
<div class="kpi-card">
<div class="kpi-label">💰 今日总额</div>
<div class="kpi-value" id="todayAmount">0</div>
<!-- <div class="kpi-sub">⬆️ 客单价 ¥698</div> -->
</div>
<div class="kpi-card">
<div class="kpi-label">📅 本月订单</div>
<div class="kpi-value" id="totalOrder">0</div>
<!-- <div class="kpi-sub">📊 环比 +8.1%</div> -->
</div>
<div class="kpi-card">
<div class="kpi-label">💰 本月总额</div>
<div class="kpi-value" id="totalAmount">0</div>
<!-- <div class="kpi-sub">🚚 履约率 93.8%</div> -->
</div>
</div>
<!-- 图表网格区域 -->
<div class="charts-grid">
<!-- 图表1: 订单展示 -->
<div class="chart-card">
<div class="chart-title"><span class="dot"></span> 订单展示</div>
<ul class="city-list" id="cityRankList"></ul>
</div>
<!-- 图表2: 小时分布 -->
<div class="chart-card">
<div class="chart-title"><span class="dot"></span> 小时订单分布</div>
<div class="chart-container" id="hourChart"></div>
</div>
<!-- 图表3: 客单价区间 -->
<div class="chart-card">
<div class="chart-title"><span class="dot"></span> 客单价区间</div>
<div class="chart-container" id="priceChart"></div>
</div>
<!-- 底部说明 -->
<div class="footer-note">
<span class="glow">⚡ 今日订单: <span id="todayOnlySpan">0</span> </span> · 数据每小时自动更新
</div>
</div>
</div>
<script>
(function() {
// ================= 真实今日订单 =================
let TODAY_REAL = 15728;
let userId = '';
// 全局变量用于存储用户ID
window.userId = '';
console.log('1.html脚本开始执行');
// 接收来自父窗口的用户ID
function handleMessage(event) {
console.log('接收到消息:', event.data);
if (event.data && event.data.userId) {
window.userId = event.data.userId;
console.log('接收到用户ID:', window.userId);
// 收到用户ID后立即获取所有数据
fetchOrderCount();
fetchTodaySale(); // 启用
fetchMonthCount(); // 启用
fetchMonthSale(); // 启用
fetchOrderDisplay(); // 启用
fetchHourOrder(); // 启用小时订单分布数据
fetchOrderAmount(); // 启用客单价区间数据
} else if (event.data) {
console.log('消息数据中没有userId字段:', event.data);
}
}
// 绑定消息事件监听器
console.log('绑定message事件监听器');
window.addEventListener('message', handleMessage);
// 测试:手动触发一次,验证监听器是否工作
setTimeout(() => {
console.log('测试检查message事件监听器是否绑定');
console.log('当前window.userId:', window.userId);
}, 1500);
// 暴露全局方法,让父窗口直接调用
window.setUserId = function(id) {
console.log('通过全局方法设置用户ID:', id);
window.userId = id;
fetchOrderCount();
fetchTodaySale(); // 启用
fetchMonthCount(); // 启用
fetchMonthSale(); // 启用
fetchOrderDisplay(); // 启用
fetchHourOrder(); // 启用小时订单分布数据
fetchOrderAmount(); // 启用客单价区间数据
};
// 从API获取订单总数
async function fetchOrderCount() {
try {
console.log('开始获取订单总数...');
console.log('当前用户ID:', window.userId);
// 构建请求URL包含用户ID参数
const url = `https://api.buzhiyushu.cn/zhishu/shopOrder/todayCount/${window.userId}`;
const response = await fetch(url);
console.log('请求状态:', response.status, response.statusText);
if (response.ok) {
TODAY_REAL = await response.json();
console.log('获取订单总数成功:', TODAY_REAL);
document.getElementById('todayOrder').innerText = TODAY_REAL.toLocaleString();
document.getElementById('todayOnlySpan').innerText = TODAY_REAL.toLocaleString();
} else {
console.error('获取订单总数失败,响应状态:', response.status);
}
} catch (error) {
console.error('获取订单总数异常:', error);
}
}
// 从API获取今日交易总额
async function fetchTodaySale() {
try {
console.log('开始获取今日交易总额...');
console.log('当前用户ID:', window.userId);
// 构建请求URL包含用户ID参数
const url = `https://api.buzhiyushu.cn/zhishu/shopOrder/todaySale/${window.userId}`;
const response = await fetch(url);
console.log('请求状态:', response.status, response.statusText);
if (response.ok) {
const totalSale = await response.json();
console.log('获取今日交易总额成功:', totalSale);
// 后台返回单位是分,转换为元
const totalSaleInYuan = parseFloat(totalSale) / 100;
// 格式化显示,单位为元
const formattedAmount = totalSaleInYuan.toLocaleString('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
document.getElementById('todayAmount').innerText = `¥ ${formattedAmount}`;
} else {
console.error('获取今日交易总额失败,响应状态:', response.status);
}
} catch (error) {
console.error('获取今日交易总额异常:', error);
}
}
// 从API获取本月订单总数
async function fetchMonthCount() {
try {
console.log('开始获取本月订单总数...');
console.log('当前用户ID:', window.userId);
// 构建请求URL包含用户ID参数
const url = `https://api.buzhiyushu.cn/zhishu/shopOrder/totalCount/${window.userId}`;
const response = await fetch(url);
console.log('请求状态:', response.status, response.statusText);
if (response.ok) {
const monthCount = await response.json();
console.log('获取本月订单总数成功:', monthCount);
document.getElementById('totalOrder').innerText = monthCount.toLocaleString();
} else {
console.error('获取本月订单总数失败,响应状态:', response.status);
}
} catch (error) {
console.error('获取本月订单总数异常:', error);
}
}
// 从API获取本月交易总额
async function fetchMonthSale() {
try {
console.log('开始获取本月交易总额...');
console.log('当前用户ID:', window.userId);
// 构建请求URL包含用户ID参数
const url = `https://api.buzhiyushu.cn/zhishu/shopOrder/totalSale/${window.userId}`;
const response = await fetch(url);
console.log('请求状态:', response.status, response.statusText);
if (response.ok) {
const totalSale = await response.json();
console.log('获取本月交易总额成功:', totalSale);
// 后台返回单位是分,转换为元
const totalSaleInYuan = parseFloat(totalSale) / 100;
// 格式化显示,单位为元
const formattedAmount = totalSaleInYuan.toLocaleString('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
document.getElementById('totalAmount').innerText = `¥ ${formattedAmount}`;
} else {
console.error('获取本月交易总额失败,响应状态:', response.status);
}
} catch (error) {
console.error('获取本月交易总额异常:', error);
}
}
// 初始化时不立即调用等待接收用户ID后再调用
// fetchOrderCount();
// 随机函数
function random(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// ================= 刷新卡片假数据 =================
function refreshCards() {
document.getElementById('totalOrder').innerText = random(330000, 480000).toLocaleString();
document.getElementById('totalAmount').innerText = random(18000, 53000).toLocaleString();
let total = random(21000000, 35000000).toLocaleString('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
document.getElementById('todayAmount').innerText = '¥ ' + total;
}
// refreshCards(); // 暂时注释掉,等待真实数据
// ================= 城市排行数据 =================
async function fetchOrderDisplay() {
try {
console.log('开始获取订单展示数据...');
console.log('当前用户ID:', window.userId);
// 构建请求URL根据后台API签名需要传递路径参数和查询参数
// 路径参数id为用户ID查询参数id为1触发外部接口调用shopIdList为空列表
const url = `https://api.buzhiyushu.cn/zhishu/shopOrder/orderDisplay/${window.userId}`;
const response = await fetch(url);
console.log('请求状态:', response.status, response.statusText);
if (response.ok) {
const data = await response.json();
console.log('获取订单展示数据成功:', data);
updateCityRank(data);
} else {
console.error('获取订单展示数据失败,响应状态:', response.status);
}
} catch (error) {
console.error('获取订单展示数据异常:', error);
}
}
// ================= 小时订单分布数据 =================
async function fetchHourOrder() {
try {
console.log('开始获取小时订单分布数据...');
console.log('当前用户ID:', window.userId);
// 检查用户ID是否存在
if (!window.userId) {
console.error('用户ID不存在无法获取小时订单分布数据');
return;
}
// 构建请求URL
const url = `https://api.buzhiyushu.cn/zhishu/shopOrder/hourOrder/${window.userId}`;
console.log('请求URL:', url);
const response = await fetch(url);
console.log('请求状态:', response.status, response.statusText);
if (response.ok) {
const data = await response.json();
console.log('获取小时订单分布数据成功:', data);
updateHourChart(data);
} else {
console.error('获取小时订单分布数据失败,响应状态:', response.status);
}
} catch (error) {
console.error('获取小时订单分布数据异常:', error);
}
}
// ================= 客单价区间数据 =================
async function fetchOrderAmount() {
try {
console.log('开始获取客单价区间数据...');
console.log('当前用户ID:', window.userId);
// 检查用户ID是否存在
if (!window.userId) {
console.error('用户ID不存在无法获取客单价区间数据');
return;
}
// 构建请求URL
const url = `https://api.buzhiyushu.cn/zhishu/shopOrder/orderAmount/${window.userId}`;
console.log('请求URL:', url);
const response = await fetch(url);
console.log('请求状态:', response.status, response.statusText);
if (response.ok) {
const data = await response.json();
console.log('获取客单价区间数据成功:', data);
updatePriceChart(data);
} else {
console.error('获取客单价区间数据失败,响应状态:', response.status);
}
} catch (error) {
console.error('获取客单价区间数据异常:', error);
}
}
function updateCityRank(data) {
const listEl = document.getElementById('cityRankList');
// 转换为数组并排序
const cityData = Object.entries(data)
.map(([name, value]) => ({ name, value }))
.sort((a, b) => b.value - a.value);
let html = '';
cityData.slice(0, 10).forEach(item => {
html += `<li class="city-item"><span>${item.name}</span><span>${item.value.toLocaleString()} 单</span></li>`;
});
listEl.innerHTML = html;
}
// 初始化时不立即调用等待接收用户ID后再调用
// fetchOrderDisplay();
// ================= 初始化所有图表 =================
// 小时分布
let hourChart;
// 客单价区间
let priceChart;
// 初始化图表实例
function initChartInstances() {
if (document.getElementById('hourChart')) {
hourChart = echarts.init(document.getElementById('hourChart'));
console.log('小时订单分布图表实例初始化成功');
} else {
console.error('小时订单分布图表容器不存在');
}
if (document.getElementById('priceChart')) {
priceChart = echarts.init(document.getElementById('priceChart'));
console.log('客单价区间图表实例初始化成功');
} else {
console.error('客单价区间图表容器不存在');
}
}
// 页面加载完成后初始化图表实例
window.addEventListener('load', initChartInstances);
// 通用图表主题配置
const chartTheme = {
backgroundColor: 'transparent',
textStyle: { color: '#c0d0ff' },
grid: { left: '5%', right: '5%', top: 30, bottom: 15, containLabel: true },
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
xAxis: {
axisLine: { lineStyle: { color: '#2a4a99' } },
axisLabel: { color: '#99b0e0' }
},
yAxis: {
axisLine: { lineStyle: { color: '#2a4a99' } },
axisLabel: { color: '#99b0e0' },
splitLine: { lineStyle: { color: '#1a2a4a', type: 'dashed' } }
},
legend: { textStyle: { color: '#c0d0ff' } }
};
// 更新小时分布
function updateHourChart(data = null) {
const hours = Array.from({ length: 24 }, (_, i) => i + '时');
let orderData = Array(24).fill(0); // 初始化24小时数据为0
if (data) {
// 使用API返回的真实数据
for (const [key, value] of Object.entries(data)) {
// 解析键格式:"2026-02-26 00:00"
const hourMatch = key.match(/\s(\d{2}):\d{2}$/);
if (hourMatch) {
const hour = parseInt(hourMatch[1]);
if (hour >= 0 && hour < 24) {
orderData[hour] = value;
}
}
}
} else {
// 模拟数据当API调用失败时使用
for (let i = 0; i < 24; i++) {
// 模拟白天订单多
if (i >= 9 && i <= 21) {
orderData[i] = random(3000, 6000);
} else {
orderData[i] = random(500, 2000);
}
}
}
hourChart.setOption({
...chartTheme,
xAxis: { ...chartTheme.xAxis, data: hours },
yAxis: { ...chartTheme.yAxis, name: '订单量' },
series: [{
type: 'line',
data: orderData,
smooth: true,
lineStyle: { color: '#6b8cff', width: 2 },
areaStyle: { color: 'rgba(107, 140, 255, 0.2)' },
symbol: 'none'
}]
});
}
// 更新客单价区间
function updatePriceChart(data = null) {
const ranges = ['0-10元', '10-30元', '30-50元', '50-100元', '100元以上'];
let chartData = [];
if (data) {
// 使用API返回的真实数据
// 确保数据按顺序对应各个区间
chartData = ranges.map(range => data[range] || 0);
} else {
// 模拟数据当API调用失败时使用
chartData = ranges.map(() => random(10, 40));
}
priceChart.setOption({
...chartTheme,
xAxis: { ...chartTheme.xAxis, data: ranges },
yAxis: { ...chartTheme.yAxis, name: '订单数' },
series: [{
type: 'bar',
data: chartData,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#ffb347' },
{ offset: 1, color: '#ff8a4a' }
]),
borderRadius: [4, 4, 0, 0]
},
barWidth: 20
}]
});
}
// 初始化所有图表
function initAllCharts() {
// 确保图表实例已经初始化
if (!hourChart || !priceChart) {
console.error('图表实例未初始化,无法更新图表');
return;
}
if (window.userId) {
fetchHourOrder(); // 调用API获取小时订单分布数据
fetchOrderAmount(); // 调用API获取客单价区间数据
} else {
// 如果用户ID不存在使用模拟数据初始化图表
updateHourChart();
updatePriceChart();
}
}
// 页面加载完成后初始化所有图表
window.addEventListener('load', function() {
// 等待图表实例初始化完成
setTimeout(initAllCharts, 100);
});
// 窗口自适应
window.addEventListener('resize', () => {
if (hourChart) {
hourChart.resize();
}
if (priceChart) {
priceChart.resize();
}
});
// ================= 定时刷新所有数据 =================
setInterval(() => {
refreshCards();
// 更新所有数据
fetchOrderCount();
fetchTodaySale(); // 启用
fetchMonthCount(); // 启用
fetchMonthSale(); // 启用
fetchOrderDisplay(); // 启用
fetchHourOrder(); // 启用小时订单分布数据
fetchOrderAmount(); // 启用客单价区间数据
// 不再需要单独调用updatePriceChart因为fetchOrderAmount会调用它
}, 3600000); // 每小时刷新一次3600000毫秒
})();
</script>
</body>
</html>