@ -30,8 +30,8 @@
< label > 分公司: < / label >
< label > 分公司: < / label >
< select id = "branchCompany" name = "branchCompany" class = "form-control" onchange = "handleCompanyChange()" >
< select id = "branchCompany" name = "branchCompany" class = "form-control" onchange = "handleCompanyChange()" >
< option value = "" > -- 请选择 --< / option >
< option value = "" > -- 请选择 --< / option >
< option value = " one "> 一分公司< / option >
< option value = " 202 "> 一分公司< / option >
< option value = " three "> 三分公司< / option >
< option value = " 227 "> 三分公司< / option >
< / select >
< / select >
< / li >
< / li >
@ -43,7 +43,7 @@
< / select >
< / select >
< / li >
< / li >
< li >
< li >
< a class = "btn btn-primary btn-rounded btn-sm" onclick = "$. table .search()"> < i class = "fa fa-search" > < / i > 搜索< / a >
< a class = "btn btn-primary btn-rounded btn-sm" onclick = "$. form .search()"> < i class = "fa fa-search" > < / i > 搜索< / a >
< a class = "btn btn-warning btn-rounded btn-sm" onclick = "$.form.reset()" > < i class = "fa fa-refresh" > < / i > 重置< / a >
< a class = "btn btn-warning btn-rounded btn-sm" onclick = "$.form.reset()" > < i class = "fa fa-refresh" > < / i > 重置< / a >
< / li >
< / li >
< / ul >
< / ul >
@ -213,426 +213,248 @@
< th:block th:include = "include :: datetimepicker-js" / >
< th:block th:include = "include :: datetimepicker-js" / >
< th:block th:include = "include :: echarts-js" / >
< th:block th:include = "include :: echarts-js" / >
< script th:inline = "javascript" >
< script th:inline = "javascript" >
// ... (保持原有的 editFlag, prefix, shopData 等变量定义不变) ...
var editFlag = [[${@permission.hasPermi('tyre:tyre:edit')}]];
var editFlag = [[${@permission.hasPermi('tyre:tyre:edit')}]];
var removeFlag = [[${@permission.hasPermi('tyre:tyre:remove')}]];
var removeFlag = [[${@permission.hasPermi('tyre:tyre:remove')}]];
var prefix = ctx + "tyre/tyre";
var prefix = ctx + "tyre/tyre";
var datas = [[${@dict.getType('tyre_type')}]];
var datas = [[${@dict.getType('tyre_type')}]];
// 修理厂数据源
var shopData = {
var shopData = {
'one': [
'202': [ { value: '229', name: '一分公司修理厂' }, ],
{ value: 'guangming', name: '一分公司修理厂' },
'227': [
], // 一分公司:空数组
{ value: '200', name: '光明修理厂' },
'three': [ // 三分公司:包含两个选项
{ value: '201', name: '石岩修理厂' }
{ value: 'guangming', name: '光明修理厂' },
{ value: 'shiyan', name: '石岩修理厂' }
]
]
};
};
// 初始化 ECharts 实例
var pieChart = echarts.init(document.getElementById('pieChart'));
var lineChart = echarts.init(document.getElementById('lineChart'));
var specChart = echarts.init(document.getElementById('specChart'));
var sourceChart = echarts.init(document.getElementById('sourceChart'));
$(function() {
$(function() {
// 1. 初始化时间控件
$("input[name='birthday']").datetimepicker({
$("input[name='birthday']").datetimepicker({
format: "yyyy-mm-dd",
format: "yyyy-mm-dd",
minView: "month",
minView: "month",
autoclose: true
autoclose: true
});
});
// 实时时间更新
function updateClock() {
const now = new Date();
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
const timeStr = now.toTimeString().substr(0, 8); // "11:14:10"
// 2. 进入页面时,自动请求数据
const dateStr = now.toLocaleDateString(); // "2026/4/15"
loadData();
const weekStr = weekDays[now.getDay()];
// 这里的 HTML 结构完全模拟了你图片中的样式
// 3. 启动时钟
$('#clock').html(`${timeStr} < small style = "color:#999; margin: 0 10px;" > |< / small > ${dateStr} ${weekStr}`);
updateClock();
}
setInterval(updateClock, 1000);
// ================= 1. 环形图逻辑 (保持原有逻辑) =================
});
var pieChart = echarts.init(document.getElementById('pieChart'));
// ================= 核心修改:统一的数据加载函数 =================
function loadData() {
// 显示加载层(可选,提升体验)
// var index = layer.load(1);
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({
$.ajax({
url: ctx+'queryIndexData', // 你的接口地址
url: ctx + 'queryIndexData',
type: 'POST',
type: 'POST',
// 关键点:使用 serialize() 自动收集表单内的 branchCompany 和 repairShop 值
data: $('#formId').serialize(),
dataType: 'json',
dataType: 'json',
success: function(res) {
success: function(res) {
if (res.code === 0) { // 假设 code 0 代表成功
// layer.close(index);
// 调用渲染函数
if (res.code === 0) {
// 渲染各个图表
renderPieChart(res.data.tireTypeTotal);
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([]); // 传空数组显示暂无数据
}
// --- 新增代码:渲染规格占比图 ---
if (res.data.mapList) renderLineChart(res.data.mapList);
// 假设接口返回的数据结构为 res.data.mapListTyreModel
if (res.data.mapListTyreModel) renderSpecChart(res.data.mapListTyreModel);
if (res.data.mapListInstall & & res.data.mapListInstall.length > 0) {
if (res.data.mapListInstall) renderSourceChart(res.data.mapListInstall);
renderSourceChart(res.data.mapListInstall);
} else {
renderSourceChart([]); // 传空数组显示暂无数据
}
} else {
} else {
// 错误处理
$.modal.msgError(res.msg);
console.error('数据加载失败:', res.msg);
// 即使失败,也渲染一个默认的空数据或者错误提示
renderPieChart({total: 0, new: 0, circulating: 0, renovate: 0, experimental: 0});
renderLineChart([]);
renderSpecChart([]); // 错误时也传空
renderSourceChart([]); // 错误时也传空
}
}
},
},
error: function() {
error: function() {
console.error('网络请求出错' );
// layer.close(index);
renderPieChart({total: 0, new: 0, circulating: 0, renovate: 0, experimental: 0} );
$.modal.msgError("数据请求失败");
}
}
});
});
}
// ================= 2. 折线图逻辑 (新增) =================
// ================= 表单操作逻辑 =================
var lineChart = echarts.init(document.getElementById('lineChart'));
function renderLineChart(mapList) {
// 搜索:直接调用加载函数
// 1. 数据预处理:从 mapList 中提取 X轴(date) 和 Y轴数据
$.form.search = function() {
var dates = [];
loadData();
var inData = []; // 入库数量 (type_0_count)
};
var outData = []; // 出库数量 (type_1_count)
mapList.forEach(function(item) {
// 重置:重置表单 + 恢复下拉框状态 + 重新加载
dates.push(item.date); // X轴: 日期
$.form.reset = function() {
inData.push(item.type_0_count); // Y轴: 入库
$('form')[0].reset(); // 重置表单域
outData.push(item.type_1_count); // Y轴: 出库
handleCompanyChange(); // 恢复修理厂下拉框的禁用状态
});
loadData(); // 重新加载默认数据
};
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() {
function handleCompanyChange() {
var companyVal = $("#branchCompany").val(); // 获取选中的分公司值
var companyVal = $("#branchCompany").val();
var $shopSelect = $("#repairShop"); // 获取修理厂下拉框对象
var $shopSelect = $("#repairShop");
// 1. 清空修理厂下拉框现有的选项
$shopSelect.empty(); // 清空旧选项
$shopSelect.empty();
if (!companyVal) {
if (!companyVal) {
// 如果没选分公司
$shopSelect.append('< option value = "" > -- 请先选分公司 --< / option > ');
$shopSelect.append('< option value = "" > -- 请先选分公司 --< / option > ');
$shopSelect.prop('disabled', true); // 禁用下拉框
$shopSelect.prop('disabled', true);
} else {
} else {
// 获取对应的修理厂数据
var shops = shopData[companyVal];
var shops = shopData[companyVal];
if (shops & & shops.length > 0) {
if (shops & & shops.length > 0) {
// 如果有数据,遍历添加
$shopSelect.append('< option value = "" > -- 请选择修理厂 --< / option > '); // 增加一个空选项方便重置
shops.forEach(function(item) {
shops.forEach(function(item) {
$shopSelect.append('< option value = "' + item.value + '" > ' + item.name + '< / option > ');
$shopSelect.append('< option value = "' + item.value + '" > ' + item.name + '< / option > ');
});
});
$shopSelect.prop('disabled', false); // 启用下拉框
$shopSelect.prop('disabled', false);
} else {
} else {
// 如果是空数组(如一分公司)
$shopSelect.append('< option value = "" > -- 暂无数据 --< / option > ');
$shopSelect.append('< option value = "" > -- 暂无数据 --< / option > ');
$shopSelect.prop('disabled', true); // 禁用下拉框
$shopSelect.prop('disabled', true);
}
}
}
}
}
}
$.form.reset = function() {
$('form').each(function() {
// ================= 图表渲染逻辑 (保持原有逻辑不变) =================
this.reset();
// 1. 时钟逻辑
function updateClock() {
const now = new Date();
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
const timeStr = now.toTimeString().substr(0, 8);
const dateStr = now.toLocaleDateString();
const weekStr = weekDays[now.getDay()];
$('#clock').html(`${timeStr} < small style = "color:#999; margin: 0 10px;" > |< / small > ${dateStr} ${weekStr}`);
}
// 2. 环形图
function renderPieChart(data) {
// ... (保持你原有的 renderPieChart 代码不变) ...
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: '试验胎' }
]
}]
};
pieChart.setOption(option);
// 更新DOM文本...
$('#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}`);
const clockDiv = document.getElementById('pie-center-text');
if (clockDiv) {
clockDiv.innerHTML = `总数< br > < span style = "font-size:24px;font-weight:bold" > ${data.total || 0}< / span > `;
}
}
// 3. 折线图
function renderLineChart(mapList) {
// ... (保持你原有的 renderLineChart 代码不变) ...
var dates = [], inData = [], outData = [];
if(mapList){
mapList.forEach(function(item) {
dates.push(item.date);
inData.push(item.type_0_count);
outData.push(item.type_1_count);
});
}
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);
}
// 4. 规格占比图
var specColors = ['#5B7CD9', '#8CC657', '#F7C763', '#E96966', '#73C0DE', '#45B97C', '#9A60B4', '#EA7E53'];
function renderSpecChart(modelData) {
// ... (保持你原有的 renderSpecChart 代码不变) ...
if (!modelData || modelData.length === 0) { specChart.clear(); return; }
var specSeries = modelData.map(function(item, index) {
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] };
});
});
// 手动触发一次 change 事件,确保修理厂下拉框回到初始禁用状态
var specOption = {
handleCompanyChange();
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
};
legend: { bottom: 0, data: modelData.map(i => i.tyreModel), 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);
}
// 5. 来源占比图
function renderSourceChart(modelData) {
// ... (保持你原有的 renderSourceChart 代码不变) ...
if (!modelData || modelData.length === 0) { sourceChart.clear(); return; }
var sourceSeries = modelData.map(function(item, index) {
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] };
});
var sourceOption = {
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
legend: { bottom: 0, data: modelData.map(i => i.tyreModel), 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);
}
// 窗口大小改变自适应
window.addEventListener('resize', function() {
pieChart.resize();
lineChart.resize();
specChart.resize();
sourceChart.resize();
});
< / script >
< / script >
< / body >
< / body >
< / html >
< / html >