You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

636 lines
30 KiB
HTML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('首页')" />
</head>
<body class="gray-bg">
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-sm-12 search-collapse">
<!-- 左边部分 (占 30% -> 近似使用 col-sm-4 即 33%) -->
<div class="col-sm-3">
<div class="card-box" style="height: 50px; display: flex; align-items: center; justify-content: center;">
<!-- 时间显示区域 -->
<div id="clock" style="font-size: 20px; text-align: center; width: 100%;">
<!-- 内容由 JS 动态填充 -->
</div>
</div>
</div>
<div class="col-sm-9">
<form id="formId">
<div class="select-list">
<li class="select-time">
<label>创建时间: </label>
<input type="text" class="time-input" id="startTime" placeholder="开始时间" name="birthday"/>
<span>-</span>
<input type="text" class="time-input" id="endTime" placeholder="结束时间" name="params[endTime]"/>
</li>
<!-- 2. 分公司下拉框 (新增) -->
<li>
<label>分公司: </label>
<select id="branchCompany" name="branchCompany" class="form-control" onchange="handleCompanyChange()">
<option value="">-- 请选择 --</option>
<option value="one">一分公司</option>
<option value="three">三分公司</option>
</select>
</li>
<!-- 3. 修理厂下拉框 (新增) -->
<li>
<label>修理厂: </label>
<select id="repairShop" name="repairShop" class="form-control" disabled>
<option value="">-- 请先选分公司 --</option>
</select>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
</li>
</ul>
</div>
</form>
</div>
</div>
</div>
<div class="row">
<!-- 左边部分 (占 30% -> 近似使用 col-sm-4 即 33%) -->
<div class="col-sm-6" style="margin-top: 20px">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>实时库存统计</h5>
</div>
<div class="ibox-content">
<!-- ================= 第一部分:实时库存 (环形图 + 卡片) ================= -->
<div class="row" style="margin-bottom: 40px;">
<!-- 左侧:环形图 -->
<div class="col-sm-5 text-center" style="border-right: 1px solid #f0f0f0; position: relative; height: 240px;">
<h5 class="no-margins font-bold">实时库存</h5>
<div id="pieChart" style="height: 200px; width: 100%;"></div>
<!-- 中间文字覆盖 -->
<div id="pie-center-text" style="position: absolute; top: 45%; left: 50%; transform: translate(-50%, -50%); pointer-events: none;">
<!-- <div style="font-size: 14px; color: #666;">总数</div>-->
<!-- <div style="font-size: 24px; font-weight: bold; color: #333;">341</div>-->
</div>
</div>
<!-- 右侧:详细数据卡片 -->
<div class="col-sm-7">
<div class="row">
<!-- 新胎卡片 -->
<div class="col-sm-4">
<div class="panel panel-blue" style="border: 1px solid #e7eaec; box-shadow: none; margin-bottom: 15px;">
<div class="panel-body" style="padding: 10px;">
<div style="display: flex; align-items: center;">
<i class="fa fa-circle" style="color: #1ab394; margin-right: 5px;"></i>
<span style="font-weight: bold; font-size: 16px;">全新胎</span>
</div>
<h2 style="margin: 10px 0;" id="newTop">305</h2>
<small id="new">在库: 305</small>
</div>
</div>
</div>
<!-- 旧轮胎片 -->
<div class="col-sm-4">
<div class="panel panel-blue" style="border: 1px solid #e7eaec; box-shadow: none; margin-bottom: 15px;">
<div class="panel-body" style="padding: 10px;">
<div style="display: flex; align-items: center;">
<i class="fa fa-circle" style="color: #ed5565; margin-right: 5px;"></i>
<span style="font-weight: bold; font-size: 16px;">翻新胎</span>
</div>
<h2 style="margin: 10px 0;" id="renovateTop">35</h2>
<small id="renovate">在库: 34 </small>
</div>
</div>
</div>
<!-- 翻新胎卡片 -->
<div class="col-sm-4">
<div class="panel panel-blue" style="border: 1px solid #e7eaec; box-shadow: none; margin-bottom: 15px;">
<div class="panel-body" style="padding: 10px;">
<div style="display: flex; align-items: center;">
<i class="fa fa-circle" style="color: #f8ac59; margin-right: 5px;"></i>
<span style="font-weight: bold; font-size: 16px;">周转胎</span>
</div>
<h2 style="margin: 10px 0;" id="circulatingTop">1</h2>
<small id="circulating">在库: 1 </small>
</div>
</div>
</div>
<!-- 翻新胎卡片 -->
<div class="col-sm-4">
<div class="panel panel-blue" style="border: 1px solid #e7eaec; box-shadow: none; margin-bottom: 15px;">
<div class="panel-body" style="padding: 10px;">
<div style="display: flex; align-items: center;">
<i class="fa fa-circle" style="color: #3c5dbf; margin-right: 5px;"></i>
<span style="font-weight: bold; font-size: 16px;">实验胎</span>
</div>
<h2 style="margin: 10px 0;"id="experimentalTop">1</h2>
<small id="experimental" >在库: 1 </small>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ================= 第二部分:近一个月出入库统计 (折线图) ================= -->
<div class="row">
<div class="col-sm-12">
<div style="border-top: 1px solid #f0f0f0; padding-top: 20px;">
<h5 class="font-bold"><i class="fa fa-line-chart"></i> 近一个月出入库统计</h5>
<!-- 折线图容器 -->
<div id="lineChart" style="height: 300px; width: 100%; margin-top: 15px;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6" style="margin-top: 20px">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>轮胎入库及装车统计</h5>
</div>
<div class="ibox-content">
<div class="card-box" style="height: 100%;">
<!-- 2. 选项卡切换 -->
<ul class="nav nav-tabs" style="margin-bottom: 20px;">
<li class="active"><a href="###" data-toggle="tab">入库统计</a></li>
</ul>
<!-- 3. 数字统计卡片 (4个) -->
<div class="row" style="margin-bottom: 25px;">
<div class="col-sm-3">
<div class="statistic-box" style="background: #f9f9f9; padding: 15px; border-radius: 5px; text-align: center;">
<h5>全新胎 <i class="fa fa-question-circle text-info"></i></h5>
<h2 class="font-bold text-info" id="right_new">93</h2>
</div>
</div>
<div class="col-sm-3">
<div class="statistic-box" style="background: #f9f9f9; padding: 15px; border-radius: 5px; text-align: center;">
<h5>翻新胎 <i class="fa fa-question-circle text-info"></i></h5>
<h2 class="font-bold text-primary" id="right_renovate">0</h2>
</div>
</div>
<div class="col-sm-3">
<div class="statistic-box" style="background: #f9f9f9; padding: 15px; border-radius: 5px; text-align: center;">
<h5>周转胎 <i class="fa fa-question-circle text-info"></i></h5>
<h2 class="font-bold text-warning" id="right_circulating">0</h2>
</div>
</div>
<div class="col-sm-3">
<div class="statistic-box" style="background: #f9f9f9; padding: 15px; border-radius: 5px; text-align: center;">
<h5>试验胎 <i class="fa fa-question-circle text-info"></i></h5>
<h2 class="font-bold text-danger" id="right_experimental">0</h2>
</div>
</div>
</div>
<!-- 4. 图表区域 -->
<div class="row">
<div class="col-sm-12">
<!-- 规格占比 -->
<div style="margin-bottom: 15px; font-weight: bold; font-size: 16px;">
规格占比 <small style="color: #999; font-weight: normal; margin-left: 10px;"></small>
</div>
<div id="specChart" style="height: 200px; width: 100%; margin-bottom: 30px;"></div>
<!-- 来源占比 -->
<div style="margin-bottom: 15px; font-weight: bold; font-size: 16px;">
新胎装车统计 <small style="color: #999; font-weight: normal; margin-left: 10px;"></small>
</div>
<div id="sourceChart" style="height: 200px; width: 100%;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: datetimepicker-js" />
<th:block th:include="include :: echarts-js" />
<script th:inline="javascript">
var editFlag = [[${@permission.hasPermi('tyre:tyre:edit')}]];
var removeFlag = [[${@permission.hasPermi('tyre:tyre:remove')}]];
var prefix = ctx + "tyre/tyre";
var datas = [[${@dict.getType('tyre_type')}]];
var shopData = {
'one': [], // 一分公司:空数组
'three': [ // 三分公司:包含两个选项
{ value: 'guangming', name: '光明修理厂' },
{ value: 'shiyan', name: '石岩修理厂' }
]
};
$(function() {
$("input[name='birthday']").datetimepicker({
format: "yyyy-mm-dd",
minView: "month",
autoclose: true
});
// 实时时间更新
function updateClock() {
const now = new Date();
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
const timeStr = now.toTimeString().substr(0, 8); // "11:14:10"
const dateStr = now.toLocaleDateString(); // "2026/4/15"
const weekStr = weekDays[now.getDay()];
// 这里的 HTML 结构完全模拟了你图片中的样式
$('#clock').html(`${timeStr} <small style="color:#999; margin: 0 10px;">|</small> ${dateStr} ${weekStr}`);
}
// ================= 1. 环形图逻辑 (保持原有逻辑) =================
var pieChart = echarts.init(document.getElementById('pieChart'));
function renderPieChart(data) {
var option = {
color: ['#1ab394', '#f8ac59', '#5B7CD9', '#ed5565', '#999'], // 配色方案
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
series: [
{
name: '实时库存',
type: 'pie',
radius: ['60%', '75%'], // 环形
avoidLabelOverlap: false,
label: {
show: false
},
labelLine: {
show: false
},
data: [
{ value: data.new, name: '新胎' },
{ value: data.circulating, name: '周转胎' },
{ value: data.renovate, name: '翻新胎' },
{ value: data.experimental, name: '试验胎' }
// 注意如果后端返回0这里也要传0否则图表会少一块
]
}
]
};
pieChart.setOption(option);
$('#new').text(`在库: ${data.new}`);
$('#circulating').text(`在库: ${data.circulating}`);
$('#renovate').text(`在库: ${data.renovate}`);
$('#experimental').text(`在库: ${data.experimental}`);
$('#newTop').text(`${data.new}`);
$('#circulatingTop').text(`${data.circulating}`);
$('#renovateTop').text(`${data.renovate}`);
$('#experimentalTop').text(`${data.experimental}`);
$('#right_new').text(`${data.new}`);
$('#right_circulating').text(`${data.circulating}`);
$('#right_renovate').text(`${data.renovate}`);
$('#right_experimental').text(`${data.experimental}`);
// 3. 动态更新中间的文字覆盖层
// 获取DOM元素并更新文本
const clockDiv = document.getElementById('pie-center-text');
if (clockDiv) {
// 这里复用你原本的逻辑,或者直接用接口返回的 total
clockDiv.innerHTML = `总数<br><span style="font-size:24px;font-weight:bold">${data.total || 0}</span>`;
}
}
// 4. 发起 AJAX 请求获取数据
$.ajax({
url: ctx+'queryIndexData', // 你的接口地址
type: 'POST',
dataType: 'json',
success: function(res) {
if (res.code === 0) { // 假设 code 0 代表成功
// 调用渲染函数
renderPieChart(res.data.tireTypeTotal);
// --- 新增代码:渲染折线图 ---
// 假设 res.data.mapList 是包含日期和数量的数组
if (res.data.mapList && res.data.mapList.length > 0) {
renderLineChart(res.data.mapList);
} else {
// 处理空数据情况,防止报错
renderLineChart([]);
}
// --- 新增代码:渲染规格占比图 ---
// 假设接口返回的数据结构为 res.data.mapListTyreModel
if (res.data.mapListTyreModel && res.data.mapListTyreModel.length > 0) {
renderSpecChart(res.data.mapListTyreModel);
} else {
renderSpecChart([]); // 传空数组显示暂无数据
}
// --- 新增代码:渲染规格占比图 ---
// 假设接口返回的数据结构为 res.data.mapListTyreModel
if (res.data.mapListInstall && res.data.mapListInstall.length > 0) {
renderSourceChart(res.data.mapListInstall);
} else {
renderSourceChart([]); // 传空数组显示暂无数据
}
} else {
// 错误处理
console.error('数据加载失败:', res.msg);
// 即使失败,也渲染一个默认的空数据或者错误提示
renderPieChart({total: 0, new: 0, circulating: 0, renovate: 0, experimental: 0});
renderLineChart([]);
renderSpecChart([]); // 错误时也传空
renderSourceChart([]); // 错误时也传空
}
},
error: function() {
console.error('网络请求出错');
renderPieChart({total: 0, new: 0, circulating: 0, renovate: 0, experimental: 0});
}
});
// ================= 2. 折线图逻辑 (新增) =================
var lineChart = echarts.init(document.getElementById('lineChart'));
function renderLineChart(mapList) {
// 1. 数据预处理:从 mapList 中提取 X轴(date) 和 Y轴数据
var dates = [];
var inData = []; // 入库数量 (type_0_count)
var outData = []; // 出库数量 (type_1_count)
mapList.forEach(function(item) {
dates.push(item.date); // X轴日期
inData.push(item.type_0_count); // Y轴入库
outData.push(item.type_1_count); // Y轴出库
});
var lineOption = {
tooltip: {
trigger: 'axis'
},
legend: {
data: ['入库数量', '出库数量'],
right: '10%'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: dates, // 使用接口返回的日期
axisLine: {
lineStyle: {
color: '#ccc'
}
}
},
yAxis: {
type: 'value',
axisLine: {
show: false
},
splitLine: {
lineStyle: {
type: 'dashed',
color: '#eee'
}
}
},
series: [
{
name: '入库数量',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 6,
itemStyle: {
color: '#1ab394' // 绿色
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(26, 179, 148, 0.5)' },
{ offset: 0.8, color: 'rgba(26, 179, 148, 0.1)' }
])
},
data: inData // 使用接口返回的入库数据
},
{
name: '出库数量',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 6,
itemStyle: {
color: '#ed5565' // 红色
},
data: outData // 使用接口返回的出库数据
}
]
};
lineChart.setOption(lineOption);
}
// 窗口大小改变时重绘图表
window.addEventListener('resize', function() {
pieChart.resize();
lineChart.resize();
});
// ================= 规格占比图表 (修改版:支持异步数据) =================
var specChart = echarts.init(document.getElementById("specChart"));
// 1. 定义一个全局的颜色数组 (放在函数外面,避免 undefined 错误)
var specColors = ['#5B7CD9', '#8CC657', '#F7C763', '#E96966', '#73C0DE', '#45B97C', '#9A60B4', '#EA7E53'];
function renderSpecChart(modelData) {
// 1. 数据预处理
if (!modelData || modelData.length === 0) {
specChart.clear(); // 清空图表
specChart.setOption({
title: { text: '', left: 'center', top: 'center', textStyle: { fontSize: 16 } },
xAxis: { show: false },
yAxis: { show: false },
series: []
});
return;
}
// 2. 动态生成 Series 数据
// 使用 map 遍历后端传来的规格列表
var specSeries = modelData.map(function(item, index) {
// 使用 index % specColors.length 确保颜色循环使用,且不会越界
var color = specColors[index % specColors.length];
return {
name: item.tyreModel,
type: 'bar',
stack: 'total',
barWidth: 20,
label: { show: false },
itemStyle: {
color: color // 使用计算出的颜色
},
data: [item.total]
};
});
// 3. 生成图例数据
var legendData = modelData.map(item => item.tyreModel);
// 4. 设置图表 Option
var specOption = {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' }
},
legend: {
bottom: 0,
data: legendData,
textStyle: { fontSize: 12 }
},
grid: {
left: '50',
right: '20',
bottom: '60',
top: '10',
containLabel: true
},
xAxis: {
type: 'value',
max: function(value) {
return Math.ceil(value.max * 1.2);
},
splitLine: { show: true, lineStyle: { type: 'dashed' } }
},
yAxis: {
type: 'category',
data: ['规格统计'] // 可以改为动态的,或者保持单维度
},
series: specSeries
};
specChart.setOption(specOption);
}
// 初始化时传空数据
renderSpecChart([]);
// ================= 来源占比图表 (修改版:支持异步数据) =================
var sourceChart = echarts.init(document.getElementById("sourceChart"));
function renderSourceChart(modelData) {
// 1. 数据预处理
if (!modelData || modelData.length === 0) {
sourceChart.clear(); // 清空图表
sourceChart.setOption({
title: { text: '', left: 'center', top: 'center', textStyle: { fontSize: 16 } },
xAxis: { show: false },
yAxis: { show: false },
series: []
});
return;
}
// 2. 动态生成 Series 数据
// 使用 map 遍历后端传来的规格列表
var sourceSeries = modelData.map(function(item, index) {
// 使用 index % specColors.length 确保颜色循环使用,且不会越界
var color = specColors[index % specColors.length];
return {
name: item.tyreModel,
type: 'bar',
stack: 'total',
barWidth: 20,
label: { show: false },
itemStyle: {
color: color // 使用计算出的颜色
},
data: [item.total]
};
});
// 3. 生成图例数据
var legendData = modelData.map(item => item.tyreModel);
// 4. 设置图表 Option
var sourceOption = {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' }
},
legend: {
bottom: 0,
data: legendData,
textStyle: { fontSize: 12 }
},
grid: {
left: '50',
right: '20',
bottom: '60',
top: '10',
containLabel: true
},
xAxis: {
type: 'value',
max: function(value) {
return Math.ceil(value.max * 1.2);
},
splitLine: { show: true, lineStyle: { type: 'dashed' } }
},
yAxis: {
type: 'category',
data: ['装车统计'] // 可以改为动态的,或者保持单维度
},
series: sourceSeries
};
sourceChart.setOption(sourceOption);
}
renderSourceChart([])
// 窗口大小改变时自适应
window.addEventListener('resize', function() {
specChart.resize();
sourceChart.resize();
});
updateClock(); // 初始化
setInterval(updateClock, 1000); // 每秒刷新
});
// 分公司下拉框改变时触发的函数
function handleCompanyChange() {
var companyVal = $("#branchCompany").val(); // 获取选中的分公司值
var $shopSelect = $("#repairShop"); // 获取修理厂下拉框对象
// 1. 清空修理厂下拉框现有的选项
$shopSelect.empty();
if (!companyVal) {
// 如果没选分公司
$shopSelect.append('<option value="">-- 请先选分公司 --</option>');
$shopSelect.prop('disabled', true); // 禁用下拉框
} else {
// 获取对应的修理厂数据
var shops = shopData[companyVal];
if (shops && shops.length > 0) {
// 如果有数据,遍历添加
shops.forEach(function(item) {
$shopSelect.append('<option value="' + item.value + '">' + item.name + '</option>');
});
$shopSelect.prop('disabled', false); // 启用下拉框
} else {
// 如果是空数组(如一分公司)
$shopSelect.append('<option value="">-- 暂无数据 --</option>');
$shopSelect.prop('disabled', true); // 禁用下拉框
}
}
}
$.form.reset = function() {
$('form').each(function() {
this.reset();
});
// 手动触发一次 change 事件,确保修理厂下拉框回到初始禁用状态
handleCompanyChange();
};
</script>
</body>
</html>