|
|
|
|
@ -87,8 +87,9 @@
|
|
|
|
|
<el-dialog
|
|
|
|
|
v-model="wmsDialogVisible"
|
|
|
|
|
title="库存详情"
|
|
|
|
|
width="80%"
|
|
|
|
|
width="90%"
|
|
|
|
|
destroy-on-close
|
|
|
|
|
@close="handleDialogClose"
|
|
|
|
|
>
|
|
|
|
|
<WmsTable
|
|
|
|
|
v-if="wmsDialogVisible"
|
|
|
|
|
@ -97,14 +98,46 @@
|
|
|
|
|
:specification="currentWmsSpecification"
|
|
|
|
|
:loading-height="200"
|
|
|
|
|
:show-action="true"
|
|
|
|
|
@data-loaded="onWmsDataLoaded"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<!-- 库存历史图表 -->
|
|
|
|
|
<div v-if="wmsDialogVisible" class="inventory-chart-section">
|
|
|
|
|
<div class="chart-header">
|
|
|
|
|
<h4>库存历史趋势 - {{ currentWmsModel }} - {{ currentWmsSpecification }}</h4>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="chart-controls">
|
|
|
|
|
<span class="filter-label">时间范围:</span>
|
|
|
|
|
<el-select v-model="selectedTimeRange" @change="onChartTimeRangeChange" style="width: 120px">
|
|
|
|
|
<el-option label="近1天" value="近1天" />
|
|
|
|
|
<el-option label="近3天" value="近3天" />
|
|
|
|
|
<el-option label="近1周" value="近1周" />
|
|
|
|
|
<el-option label="近1月" value="近1月" />
|
|
|
|
|
</el-select>
|
|
|
|
|
|
|
|
|
|
<span class="filter-label" style="margin-left: 20px;">显示单位:</span>
|
|
|
|
|
<el-radio-group v-model="chartUnit" @change="onChartUnitChange" size="small">
|
|
|
|
|
<el-radio-button value="box">箱数</el-radio-button>
|
|
|
|
|
<el-radio-button value="weight">重量</el-radio-button>
|
|
|
|
|
</el-radio-group>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-if="historyChartLoading" v-loading="true" class="chart-loading" style="height: 400px;"></div>
|
|
|
|
|
<div v-else-if="!hasHistoryData" class="no-data-message" style="height: 400px;">
|
|
|
|
|
暂无历史数据
|
|
|
|
|
</div>
|
|
|
|
|
<div v-show="!historyChartLoading && hasHistoryData" ref="inventoryChartRef" class="chart" style="height: 450px;"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { ref, onMounted, computed } from 'vue'
|
|
|
|
|
import { ref, onMounted, computed, nextTick } from 'vue'
|
|
|
|
|
import { API_CONFIG } from "@/config/api"
|
|
|
|
|
import { getDateRangeByTimeRange } from '@/utils/dateFormat';
|
|
|
|
|
import * as echarts from 'echarts';
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
import WmsTable from '@/components/WmsTable/index.vue'
|
|
|
|
|
|
|
|
|
|
@ -119,6 +152,15 @@ const currentWmsModel = ref('')
|
|
|
|
|
const currentWmsSpecification = ref('')
|
|
|
|
|
const wmsTableRef = ref(null)
|
|
|
|
|
|
|
|
|
|
// 图表相关状态
|
|
|
|
|
const inventoryChartRef = ref(null)
|
|
|
|
|
const historyChartLoading = ref(false)
|
|
|
|
|
const historyData = ref([])
|
|
|
|
|
const hasHistoryData = ref(false)
|
|
|
|
|
const selectedTimeRange = ref('近1月')
|
|
|
|
|
const chartUnit = ref('box')
|
|
|
|
|
let inventoryChart = null
|
|
|
|
|
|
|
|
|
|
// 显示控制
|
|
|
|
|
const showDifference = ref(true)
|
|
|
|
|
|
|
|
|
|
@ -337,12 +379,411 @@ const handleCellDblClick = (row, column, cell, event) => {
|
|
|
|
|
wire_disc = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('双击事件参数:', {
|
|
|
|
|
columnKey,
|
|
|
|
|
model,
|
|
|
|
|
wire_disc,
|
|
|
|
|
specification: row.specification
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置当前WMS数据的参数
|
|
|
|
|
currentWmsModel.value = model;
|
|
|
|
|
currentWmsSpecification.value = row.specification;
|
|
|
|
|
|
|
|
|
|
// 清空历史数据
|
|
|
|
|
historyData.value = [];
|
|
|
|
|
hasHistoryData.value = false;
|
|
|
|
|
|
|
|
|
|
// 显示WMS详情对话框
|
|
|
|
|
wmsDialogVisible.value = true;
|
|
|
|
|
|
|
|
|
|
// 获取库存历史数据
|
|
|
|
|
fetchInventoryHistoryData(model, row.specification, wire_disc);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 获取库存历史数据
|
|
|
|
|
const fetchInventoryHistoryData = (model, specification, wireDisc) => {
|
|
|
|
|
historyChartLoading.value = true;
|
|
|
|
|
|
|
|
|
|
const dateRange = getDateRangeByTimeRange(selectedTimeRange.value);
|
|
|
|
|
|
|
|
|
|
// 构建API URL,确保参数正确编码
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
model: model || '',
|
|
|
|
|
specification: specification || '',
|
|
|
|
|
wire_disc: wireDisc || '',
|
|
|
|
|
start_date: dateRange.start_date,
|
|
|
|
|
end_date: dateRange.end_date
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let apiUrl = `${API_CONFIG.BASE_URL}/api/plan/wms/history?${params.toString()}`;
|
|
|
|
|
|
|
|
|
|
console.log('API调用参数:', {
|
|
|
|
|
model,
|
|
|
|
|
specification,
|
|
|
|
|
wireDisc,
|
|
|
|
|
dateRange,
|
|
|
|
|
apiUrl
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
fetch(apiUrl)
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
.then(data => {
|
|
|
|
|
console.log('API原始返回数据:', data);
|
|
|
|
|
if (data.code === 200 && data.data && data.data.records) {
|
|
|
|
|
let records = data.data.records;
|
|
|
|
|
console.log('原始records数据:', records);
|
|
|
|
|
console.log('records数量:', records.length);
|
|
|
|
|
|
|
|
|
|
// 检查第一条记录的结构
|
|
|
|
|
if (records.length > 0) {
|
|
|
|
|
console.log('第一条记录结构:', records[0]);
|
|
|
|
|
console.log('第一条记录的所有键:', Object.keys(records[0]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果时间范围超过一周,使用降采样
|
|
|
|
|
if (isMoreThanOneWeek(dateRange.start_date, dateRange.end_date)) {
|
|
|
|
|
records = downsampleHistoryToKeyTimes(records);
|
|
|
|
|
console.log('降采样后records数量:', records.length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
historyData.value = records;
|
|
|
|
|
hasHistoryData.value = historyData.value.length > 0;
|
|
|
|
|
|
|
|
|
|
console.log('设置后的historyData:', historyData.value);
|
|
|
|
|
console.log('hasHistoryData:', hasHistoryData.value);
|
|
|
|
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
console.log('开始初始化图表');
|
|
|
|
|
initInventoryChart();
|
|
|
|
|
}, 100);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
console.error('API返回数据格式错误:', data);
|
|
|
|
|
console.error('data.code:', data.code);
|
|
|
|
|
console.error('data.data:', data.data);
|
|
|
|
|
historyData.value = [];
|
|
|
|
|
hasHistoryData.value = false;
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('库存历史数据API调用失败:', error);
|
|
|
|
|
historyData.value = [];
|
|
|
|
|
hasHistoryData.value = false;
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
historyChartLoading.value = false;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 历史数据降采样函数
|
|
|
|
|
const downsampleHistoryToKeyTimes = (records, targetHours = [8,9,10,11,12,13,14,15,16,17, 18]) => {
|
|
|
|
|
if (!records?.length) return [];
|
|
|
|
|
|
|
|
|
|
const groups = new Map();
|
|
|
|
|
records.forEach(item => {
|
|
|
|
|
const dt = new Date(item.create_time);
|
|
|
|
|
const dateKey = dt.toISOString().split('T')[0];
|
|
|
|
|
if (!groups.has(dateKey)) groups.set(dateKey, []);
|
|
|
|
|
groups.get(dateKey).push({ dt, item });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const result = [];
|
|
|
|
|
|
|
|
|
|
groups.forEach((items, dateKey) => {
|
|
|
|
|
items.sort((a, b) => a.dt - b.dt);
|
|
|
|
|
|
|
|
|
|
const selected = new Map();
|
|
|
|
|
|
|
|
|
|
targetHours.forEach(targetH => {
|
|
|
|
|
let closest = null;
|
|
|
|
|
let minDiff = Infinity;
|
|
|
|
|
|
|
|
|
|
items.forEach(({ dt, item }) => {
|
|
|
|
|
const diff = Math.abs((dt.getHours() - targetH) * 60 + dt.getMinutes());
|
|
|
|
|
if (diff < minDiff) {
|
|
|
|
|
minDiff = diff;
|
|
|
|
|
closest = item;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (closest && minDiff <= 90) {
|
|
|
|
|
selected.set(targetH, closest);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
targetHours.forEach(h => {
|
|
|
|
|
if (selected.has(h)) {
|
|
|
|
|
result.push(selected.get(h));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
result.sort((a, b) => new Date(a.create_time) - new Date(b.create_time));
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 判断时间范围是否超过一周
|
|
|
|
|
const isMoreThanOneWeek = (startDate, endDate) => {
|
|
|
|
|
if (!startDate || !endDate) return false;
|
|
|
|
|
const start = new Date(startDate);
|
|
|
|
|
const end = new Date(endDate);
|
|
|
|
|
const diffDays = (end - start) / (1000 * 60 * 60 * 24);
|
|
|
|
|
return diffDays > 7;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 初始化库存图表
|
|
|
|
|
const initInventoryChart = () => {
|
|
|
|
|
console.log('initInventoryChart 被调用');
|
|
|
|
|
console.log('inventoryChartRef.value:', inventoryChartRef.value);
|
|
|
|
|
console.log('hasHistoryData:', hasHistoryData.value);
|
|
|
|
|
console.log('historyData.value:', historyData.value);
|
|
|
|
|
|
|
|
|
|
if (!inventoryChartRef.value) {
|
|
|
|
|
console.warn('图表容器不存在,跳过初始化');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!hasHistoryData.value || !historyData.value.length) {
|
|
|
|
|
console.warn('没有历史数据,跳过图表初始化');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 销毁现有图表
|
|
|
|
|
if (inventoryChart) {
|
|
|
|
|
console.log('销毁现有图表');
|
|
|
|
|
inventoryChart.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建新图表
|
|
|
|
|
console.log('创建新的ECharts实例');
|
|
|
|
|
inventoryChart = echarts.init(inventoryChartRef.value);
|
|
|
|
|
|
|
|
|
|
// 准备数据
|
|
|
|
|
const chartData = prepareInventoryChartData();
|
|
|
|
|
console.log('准备的图表数据:', chartData);
|
|
|
|
|
|
|
|
|
|
if (!chartData.length) {
|
|
|
|
|
console.warn('图表数据为空,不渲染图表');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 图表配置
|
|
|
|
|
const option = {
|
|
|
|
|
title: {
|
|
|
|
|
text: '库存历史趋势',
|
|
|
|
|
left: 'center',
|
|
|
|
|
textStyle: {
|
|
|
|
|
fontSize: 16,
|
|
|
|
|
fontWeight: 'bold'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
axisPointer: {
|
|
|
|
|
type: 'cross',
|
|
|
|
|
label: {
|
|
|
|
|
backgroundColor: '#6a7985'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
formatter: function(params) {
|
|
|
|
|
let result = `<div style="margin-bottom: 5px;"><strong>${echarts.format.formatTime('yyyy-MM-dd hh:mm', params[0].value[0])}</strong></div>`;
|
|
|
|
|
|
|
|
|
|
params.forEach(param => {
|
|
|
|
|
const seriesName = param.seriesName;
|
|
|
|
|
const value = param.value[1];
|
|
|
|
|
const unit = chartUnit.value === 'box' ? '箱' : 'kg';
|
|
|
|
|
|
|
|
|
|
result += `<div style="margin: 3px 0;">
|
|
|
|
|
<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${param.color};"></span>
|
|
|
|
|
${seriesName}: ${value}${unit}
|
|
|
|
|
</div>`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
legend: {
|
|
|
|
|
data: ['库存'],
|
|
|
|
|
top: 30
|
|
|
|
|
},
|
|
|
|
|
grid: {
|
|
|
|
|
left: '3%',
|
|
|
|
|
right: '4%',
|
|
|
|
|
bottom: '3%',
|
|
|
|
|
containLabel: true
|
|
|
|
|
},
|
|
|
|
|
xAxis: [
|
|
|
|
|
{
|
|
|
|
|
type: 'time',
|
|
|
|
|
splitLine: {
|
|
|
|
|
show: false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
yAxis: [
|
|
|
|
|
{
|
|
|
|
|
type: 'value',
|
|
|
|
|
name: chartUnit.value === 'box' ? '箱数' : '重量(kg)',
|
|
|
|
|
axisLabel: {
|
|
|
|
|
formatter: '{value}'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
series: chartData,
|
|
|
|
|
dataZoom: [
|
|
|
|
|
{
|
|
|
|
|
type: 'inside',
|
|
|
|
|
start: 0,
|
|
|
|
|
end: 100
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
start: 0,
|
|
|
|
|
end: 100
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
console.log('图表配置选项:', option);
|
|
|
|
|
|
|
|
|
|
// 设置图表配置并渲染
|
|
|
|
|
inventoryChart.setOption(option);
|
|
|
|
|
console.log('图表渲染完成');
|
|
|
|
|
|
|
|
|
|
// 监听窗口大小变化,自动调整图表大小
|
|
|
|
|
window.addEventListener('resize', resizeInventoryChart);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 准备图表数据
|
|
|
|
|
const prepareInventoryChartData = () => {
|
|
|
|
|
console.log('prepareInventoryChartData 被调用');
|
|
|
|
|
console.log('hasHistoryData.value:', hasHistoryData.value);
|
|
|
|
|
console.log('historyData.value.length:', historyData.value ? historyData.value.length : 0);
|
|
|
|
|
|
|
|
|
|
const series = [];
|
|
|
|
|
|
|
|
|
|
if (hasHistoryData.value && historyData.value.length > 0) {
|
|
|
|
|
console.log('开始处理历史数据');
|
|
|
|
|
console.log('第一条历史数据示例:', historyData.value[0]);
|
|
|
|
|
|
|
|
|
|
const historySeriesData = historyData.value.map((item, index) => {
|
|
|
|
|
console.log(`处理第${index}条数据:`, item);
|
|
|
|
|
|
|
|
|
|
// 兼容多种时间字段名
|
|
|
|
|
const timeField = item.create_time || item.createTime || item.time || item.timestamp;
|
|
|
|
|
const boxCountField = item.box_count || item.boxCount || item.box || item.count || item.total_number;
|
|
|
|
|
const weightField = item.total_weight || item.totalWeight || item.weight || item.total_net_weight || item.totalGrossWeight;
|
|
|
|
|
|
|
|
|
|
// console.log('字段映射检查:', {
|
|
|
|
|
// timeField,
|
|
|
|
|
// boxCountField,
|
|
|
|
|
// weightField
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
if (!timeField) {
|
|
|
|
|
// console.warn(`第${index}条数据缺少时间字段`);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const time = new Date(timeField).getTime();
|
|
|
|
|
let value;
|
|
|
|
|
|
|
|
|
|
if (chartUnit.value === 'box') {
|
|
|
|
|
value = boxCountField || 0;
|
|
|
|
|
} else {
|
|
|
|
|
value = weightField || 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(`时间戳: ${time}, 值: ${value}`);
|
|
|
|
|
|
|
|
|
|
if (isNaN(time) || time <= 0) {
|
|
|
|
|
console.warn(`第${index}条数据时间格式无效:`, timeField);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [time, value];
|
|
|
|
|
}).filter(item => item !== null); // 过滤掉无效数据
|
|
|
|
|
|
|
|
|
|
// console.log('转换后的图表数据:', historySeriesData);
|
|
|
|
|
// console.log('有效数据点数量:', historySeriesData.length);
|
|
|
|
|
|
|
|
|
|
if (historySeriesData.length > 0) {
|
|
|
|
|
series.push({
|
|
|
|
|
name: '库存',
|
|
|
|
|
type: 'line',
|
|
|
|
|
data: historySeriesData,
|
|
|
|
|
smooth: true,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: '#E6A23C',
|
|
|
|
|
width: 2
|
|
|
|
|
},
|
|
|
|
|
itemStyle: {
|
|
|
|
|
color: '#E6A23C'
|
|
|
|
|
},
|
|
|
|
|
areaStyle: {
|
|
|
|
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
|
|
{ offset: 0, color: 'rgba(230, 162, 60, 0.3)' },
|
|
|
|
|
{ offset: 1, color: 'rgba(230, 162, 60, 0.1)' }
|
|
|
|
|
])
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
console.log('成功创建图表序列');
|
|
|
|
|
} else {
|
|
|
|
|
console.warn('没有有效的图表数据');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
console.warn('没有历史数据或历史数据为空');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('最终返回的series:', series);
|
|
|
|
|
return series;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 图表窗口大小调整
|
|
|
|
|
const resizeInventoryChart = () => {
|
|
|
|
|
if (inventoryChart) {
|
|
|
|
|
inventoryChart.resize();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// WmsTable组件数据加载完成回调
|
|
|
|
|
const onWmsDataLoaded = (loadedData) => {
|
|
|
|
|
console.log('WmsTable数据加载完成:', loadedData);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 方法:图表时间范围变化
|
|
|
|
|
const onChartTimeRangeChange = () => {
|
|
|
|
|
if (currentWmsModel.value && currentWmsSpecification.value) {
|
|
|
|
|
fetchInventoryHistoryData(currentWmsModel.value, currentWmsSpecification.value, null);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 方法:图表单位切换
|
|
|
|
|
const onChartUnitChange = () => {
|
|
|
|
|
if (hasHistoryData.value) {
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
initInventoryChart();
|
|
|
|
|
}, 100);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 处理对话框关闭
|
|
|
|
|
const handleDialogClose = () => {
|
|
|
|
|
// 销毁图表实例
|
|
|
|
|
if (inventoryChart) {
|
|
|
|
|
inventoryChart.dispose();
|
|
|
|
|
inventoryChart = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 清空历史数据
|
|
|
|
|
historyData.value = [];
|
|
|
|
|
hasHistoryData.value = false;
|
|
|
|
|
|
|
|
|
|
// 移除窗口大小变化监听器
|
|
|
|
|
window.removeEventListener('resize', resizeInventoryChart);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 组件挂载时获取数据
|
|
|
|
|
@ -450,4 +891,58 @@ onMounted(() => {
|
|
|
|
|
.table-wrapper::-webkit-scrollbar-thumb:hover {
|
|
|
|
|
background: #a8a8a8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 库存历史图表样式 */
|
|
|
|
|
.inventory-chart-section {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-header h4 {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
color: #303133;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-controls {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-label {
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-loading {
|
|
|
|
|
height: 300px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.no-data-message {
|
|
|
|
|
height: 300px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
color: #909399;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart {
|
|
|
|
|
width: 100%;
|
|
|
|
|
min-height: 350px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|