|
@@ -1,25 +1,8 @@
|
|
|
<template>
|
|
<template>
|
|
|
|
|
+ <div class="charts-container">
|
|
|
|
|
|
|
|
- <div class="charts-container" >
|
|
|
|
|
-<!-- <div style="width: 100%;">-->
|
|
|
|
|
-<!-- <span style="margin: 10px;">统计区间:</span>-->
|
|
|
|
|
-<!-- <el-date-picker-->
|
|
|
|
|
-<!-- style="margin: 10px"-->
|
|
|
|
|
-<!-- v-model="value2"-->
|
|
|
|
|
-<!-- type="daterange"-->
|
|
|
|
|
-<!-- align="right"-->
|
|
|
|
|
-<!-- unlink-panels-->
|
|
|
|
|
-<!-- range-separator="至"-->
|
|
|
|
|
-<!-- start-placeholder="开始日期"-->
|
|
|
|
|
-<!-- end-placeholder="结束日期"-->
|
|
|
|
|
-<!-- :picker-options="pickerOptions" >-->
|
|
|
|
|
-<!-- </el-date-picker>-->
|
|
|
|
|
-<!-- </div>-->
|
|
|
|
|
- <span style="position: absolute;justify-content: center;font-size: 18px;font-weight: bolder;margin-top: -10px">物资柜异常统计</span>
|
|
|
|
|
- <!-- 使用 v-for 动态渲染饼图 -->
|
|
|
|
|
- <div class="pie-chart" v-for="(cabinet, index) in cabinetData" :key="index">
|
|
|
|
|
- <div :id="'pieChart' + cabinet.cabinetId" style="width: 300px; height: 150px;"></div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <!-- 单个堆叠柱状图 -->
|
|
|
|
|
+ <div id="stackedBarChart" style="width: 100%; height: 400px;"></div>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
@@ -30,51 +13,55 @@ import * as echarts from 'echarts'
|
|
|
export default {
|
|
export default {
|
|
|
name: 'warehouseEquipmentUsageStatus',
|
|
name: 'warehouseEquipmentUsageStatus',
|
|
|
props: {
|
|
props: {
|
|
|
- startTime:{
|
|
|
|
|
|
|
+ startTime: {
|
|
|
type: String,
|
|
type: String,
|
|
|
default: ''
|
|
default: ''
|
|
|
},
|
|
},
|
|
|
- endTime:{
|
|
|
|
|
|
|
+ endTime: {
|
|
|
type: String,
|
|
type: String,
|
|
|
default: ''
|
|
default: ''
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
data() {
|
|
data() {
|
|
|
return {
|
|
return {
|
|
|
- cabinetData: [], // 存储请求到的柜子数据
|
|
|
|
|
- chartInstances: [], // 存储所有饼图实例
|
|
|
|
|
|
|
+ cabinetData: [], // 存储请求到的柜子数据
|
|
|
|
|
+ chartInstance: null, // 存储柱状图实例
|
|
|
value2: this.getDefaultDateRange(),
|
|
value2: this.getDefaultDateRange(),
|
|
|
- queryParams:{
|
|
|
|
|
|
|
+ queryParams: {
|
|
|
startTime: null,
|
|
startTime: null,
|
|
|
- endTime: null,
|
|
|
|
|
|
|
+ endTime: null
|
|
|
},
|
|
},
|
|
|
pickerOptions: {
|
|
pickerOptions: {
|
|
|
- shortcuts: [{
|
|
|
|
|
- text: '最近一周',
|
|
|
|
|
- onClick(picker) {
|
|
|
|
|
- const end = new Date();
|
|
|
|
|
- const start = new Date();
|
|
|
|
|
- start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
|
|
|
|
|
- picker.$emit('pick', [start, end]);
|
|
|
|
|
|
|
+ shortcuts: [
|
|
|
|
|
+ {
|
|
|
|
|
+ text: '最近一周',
|
|
|
|
|
+ onClick(picker) {
|
|
|
|
|
+ const end = new Date()
|
|
|
|
|
+ const start = new Date()
|
|
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
|
|
|
|
|
+ picker.$emit('pick', [start, end])
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ text: '最近一个月',
|
|
|
|
|
+ onClick(picker) {
|
|
|
|
|
+ const end = new Date()
|
|
|
|
|
+ const start = new Date()
|
|
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
|
|
|
|
|
+ picker.$emit('pick', [start, end])
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ text: '最近三个月',
|
|
|
|
|
+ onClick(picker) {
|
|
|
|
|
+ const end = new Date()
|
|
|
|
|
+ const start = new Date()
|
|
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
|
|
|
|
|
+ picker.$emit('pick', [start, end])
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- }, {
|
|
|
|
|
- text: '最近一个月',
|
|
|
|
|
- onClick(picker) {
|
|
|
|
|
- const end = new Date();
|
|
|
|
|
- const start = new Date();
|
|
|
|
|
- start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
|
|
|
|
|
- picker.$emit('pick', [start, end]);
|
|
|
|
|
- }
|
|
|
|
|
- }, {
|
|
|
|
|
- text: '最近三个月',
|
|
|
|
|
- onClick(picker) {
|
|
|
|
|
- const end = new Date();
|
|
|
|
|
- const start = new Date();
|
|
|
|
|
- start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
|
|
|
|
|
- picker.$emit('pick', [start, end]);
|
|
|
|
|
- }
|
|
|
|
|
- }]
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
computed: {
|
|
computed: {
|
|
@@ -82,20 +69,15 @@ export default {
|
|
|
return {
|
|
return {
|
|
|
startTime: this.startTime,
|
|
startTime: this.startTime,
|
|
|
endTime: this.endTime
|
|
endTime: this.endTime
|
|
|
- };
|
|
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
watch: {
|
|
watch: {
|
|
|
- // value2(val) {
|
|
|
|
|
- // if(val){
|
|
|
|
|
- // this.getList()
|
|
|
|
|
- // }
|
|
|
|
|
- // },
|
|
|
|
|
- dateRange(newVal, oldVal) {
|
|
|
|
|
|
|
+ dateRange(newVal) {
|
|
|
if (newVal.startTime && newVal.endTime) {
|
|
if (newVal.startTime && newVal.endTime) {
|
|
|
- this.queryParams.startTime = newVal.startTime;
|
|
|
|
|
- this.queryParams.endTime = newVal.endTime;
|
|
|
|
|
- this.getList();
|
|
|
|
|
|
|
+ this.queryParams.startTime = newVal.startTime
|
|
|
|
|
+ this.queryParams.endTime = newVal.endTime
|
|
|
|
|
+ this.getList()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
@@ -104,146 +86,123 @@ export default {
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
getDefaultDateRange() {
|
|
getDefaultDateRange() {
|
|
|
- const today = new Date();
|
|
|
|
|
- const oneMonthAgo = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
|
|
|
|
|
- return [oneMonthAgo, today];
|
|
|
|
|
|
|
+ const today = new Date()
|
|
|
|
|
+ const oneMonthAgo = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate())
|
|
|
|
|
+ return [oneMonthAgo, today]
|
|
|
},
|
|
},
|
|
|
formatDate(date) {
|
|
formatDate(date) {
|
|
|
- const year = date.getFullYear().toString().padStart(2, "0");
|
|
|
|
|
- const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
|
|
|
|
- const day = date.getDate().toString().padStart(2, "0");
|
|
|
|
|
- const hours = date.getHours().toString().padStart(2, "0");
|
|
|
|
|
- const minutes = date.getMinutes().toString().padStart(2, "0");
|
|
|
|
|
- const seconds = date.getSeconds().toString().padStart(2, "0");
|
|
|
|
|
|
|
+ const year = date.getFullYear().toString().padStart(2, '0')
|
|
|
|
|
+ const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
|
|
|
|
+ const day = date.getDate().toString().padStart(2, '0')
|
|
|
|
|
+ const hours = date.getHours().toString().padStart(2, '0')
|
|
|
|
|
+ const minutes = date.getMinutes().toString().padStart(2, '0')
|
|
|
|
|
+ const seconds = date.getSeconds().toString().padStart(2, '0')
|
|
|
|
|
|
|
|
- return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
|
|
|
|
|
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
|
|
},
|
|
},
|
|
|
// 请求数据
|
|
// 请求数据
|
|
|
getList() {
|
|
getList() {
|
|
|
- // if (Array.isArray(this.value2) && this.value2.length === 2) {
|
|
|
|
|
- // this.queryParams.startTime = this.formatDate(this.value2[0])
|
|
|
|
|
- // this.queryParams.endTime = this.formatDate(this.value2[1])
|
|
|
|
|
- // }
|
|
|
|
|
getCabinetStatistics(this.queryParams).then(response => {
|
|
getCabinetStatistics(this.queryParams).then(response => {
|
|
|
console.log(response, '物资柜状态统计--物资柜异常数据统计')
|
|
console.log(response, '物资柜状态统计--物资柜异常数据统计')
|
|
|
this.cabinetData = response.data // 获取数据后保存
|
|
this.cabinetData = response.data // 获取数据后保存
|
|
|
|
|
|
|
|
// 在数据加载完成后渲染图表
|
|
// 在数据加载完成后渲染图表
|
|
|
this.$nextTick(() => {
|
|
this.$nextTick(() => {
|
|
|
- this.renderCharts(); // 在 DOM 更新完后再执行渲染
|
|
|
|
|
|
|
+ this.renderChart() // 在 DOM 更新完后再执行渲染
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- // 渲染饼图
|
|
|
|
|
- renderCharts() {
|
|
|
|
|
- this.cabinetData.forEach(cabinet => {
|
|
|
|
|
- // 确保图表的 DOM 元素存在
|
|
|
|
|
- const chartElement = document.getElementById('pieChart' + cabinet.cabinetId)
|
|
|
|
|
- if (chartElement) {
|
|
|
|
|
- // 初始化每个饼图的实例
|
|
|
|
|
- const chart = echarts.init(chartElement)
|
|
|
|
|
|
|
+ // 渲染堆叠柱状图
|
|
|
|
|
+ renderChart() {
|
|
|
|
|
+ const chartElement = document.getElementById('stackedBarChart')
|
|
|
|
|
+ if (chartElement) {
|
|
|
|
|
+ // 初始化柱状图实例
|
|
|
|
|
+ this.chartInstance = echarts.init(chartElement)
|
|
|
|
|
|
|
|
- // 配置饼图的选项
|
|
|
|
|
- const options = {
|
|
|
|
|
- title: {
|
|
|
|
|
- text: cabinet.cabinetName, // 添加饼图标题
|
|
|
|
|
- left: 'center',
|
|
|
|
|
- top: 'center',
|
|
|
|
|
- textStyle: {
|
|
|
|
|
- fontSize: 16,
|
|
|
|
|
- fontWeight: 'bold',
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
- legend: {
|
|
|
|
|
- data:['物资错放','超时未关'],
|
|
|
|
|
- // data: [
|
|
|
|
|
- // { name: '物资错放', icon: 'circle', textStyle: { color: '#5470c6' }, itemStyle: { color: '#5470c6' } }, // 固定图例颜色
|
|
|
|
|
- // { name: '超时未关', icon: 'circle', textStyle: { color: '#91cc75' }, itemStyle: { color: '#91cc75' } }, // 固定图例颜色
|
|
|
|
|
- // ],
|
|
|
|
|
- top: '87%',
|
|
|
|
|
- left: 'center',
|
|
|
|
|
- textStyle: {
|
|
|
|
|
- fontSize: 14, // 设置图例的字体大小
|
|
|
|
|
- color: '#333', // 保持图例文字颜色不变
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
- tooltip: {
|
|
|
|
|
- trigger: 'item',
|
|
|
|
|
- formatter: '{b}: {c} 次 ({d}%)',
|
|
|
|
|
- },
|
|
|
|
|
- series: [
|
|
|
|
|
- {
|
|
|
|
|
- name: '异常情况',
|
|
|
|
|
- type: 'pie',
|
|
|
|
|
- radius: '60%',
|
|
|
|
|
- center: ['50%', '50%'],
|
|
|
|
|
- data: [
|
|
|
|
|
- //
|
|
|
|
|
- { value: cabinet.openTimeoutCount || 0, name: '超时未关' },
|
|
|
|
|
- { value: cabinet.misplaceCount || 0, name: '物资错放' },
|
|
|
|
|
- ],
|
|
|
|
|
- itemStyle: {
|
|
|
|
|
- color: (params) => {
|
|
|
|
|
- if (params.value === 0) {
|
|
|
|
|
- return '#dcdfe6'; // 数据为0时,饼图部分渲染为灰色
|
|
|
|
|
- } else if (params.name === '超时未关') {
|
|
|
|
|
- return '#91cc75'; // 超时未关用绿色
|
|
|
|
|
- } else {
|
|
|
|
|
- return '#5470c6'; // 物资错放用蓝色
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
- labelLine:{
|
|
|
|
|
- normal:{
|
|
|
|
|
- length:5, // 指示线宽度
|
|
|
|
|
- lineStyle: {
|
|
|
|
|
- color: "#595959" // 指示线颜色
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- },
|
|
|
|
|
- label: {
|
|
|
|
|
- normal: {
|
|
|
|
|
- formatter: '{b}', // 显示每个扇区的名称
|
|
|
|
|
- position: 'outside', // 标签位置在外侧
|
|
|
|
|
- align: 'left', // 标签对齐
|
|
|
|
|
- fontSize: 14,
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- ],
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 提取物资柜名称和异常数据
|
|
|
|
|
+ const cabinetNames = this.cabinetData.map(cabinet => cabinet.cabinetName)
|
|
|
|
|
+ const misplaceData = this.cabinetData.map(cabinet => cabinet.misplaceCount || 0)
|
|
|
|
|
+ const timeoutData = this.cabinetData.map(cabinet => cabinet.openTimeoutCount || 0)
|
|
|
|
|
|
|
|
- // 设置图表的配置项
|
|
|
|
|
- chart.setOption(options)
|
|
|
|
|
|
|
+ // 配置柱状图的选项
|
|
|
|
|
+ const options = {
|
|
|
|
|
+ title: {
|
|
|
|
|
+ text: '物资柜异常统计',
|
|
|
|
|
+ left: 'center',
|
|
|
|
|
+ },
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'axis',
|
|
|
|
|
+ axisPointer: {
|
|
|
|
|
+ type: 'shadow'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ legend: {
|
|
|
|
|
+ data: ['物资错放', '超时未关'],
|
|
|
|
|
+ top: '5%',
|
|
|
|
|
+ left: 'center',
|
|
|
|
|
+ textStyle: {
|
|
|
|
|
+ fontSize: 14
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // 将图表实例存储
|
|
|
|
|
- this.chartInstances.push(chart)
|
|
|
|
|
|
|
+ xAxis: {
|
|
|
|
|
+ type: 'category',
|
|
|
|
|
+ data: cabinetNames, // 横坐标为物资柜名称
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ rotate: 0, // 如果名称过长,可以旋转标签
|
|
|
|
|
+ fontSize: 12
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ yAxis: {
|
|
|
|
|
+ type: 'value',
|
|
|
|
|
+ name: '次数',
|
|
|
|
|
+ splitLine: {
|
|
|
|
|
+ show: false // 隐藏纵向的网格线
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '物资错放',
|
|
|
|
|
+ type: 'bar',
|
|
|
|
|
+ stack: '异常情况',
|
|
|
|
|
+ barWidth: '25%',
|
|
|
|
|
+ data: misplaceData, // 物资错放数据
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#5470c6' // 蓝色
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '超时未关',
|
|
|
|
|
+ type: 'bar',
|
|
|
|
|
+ stack: '异常情况',
|
|
|
|
|
+ data: timeoutData, // 超时未关数据
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#91cc75' // 绿色
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
}
|
|
}
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
|
|
+ // 设置图表的配置项
|
|
|
|
|
+ this.chartInstance.setOption(options)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
beforeDestroy() {
|
|
beforeDestroy() {
|
|
|
- // 销毁每个图表实例,避免内存泄漏
|
|
|
|
|
- this.chartInstances.forEach(chart => {
|
|
|
|
|
- chart.dispose()
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ // 销毁图表实例,避免内存泄漏
|
|
|
|
|
+ if (this.chartInstance) {
|
|
|
|
|
+ this.chartInstance.dispose()
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
<style scoped lang="scss">
|
|
|
.charts-container {
|
|
.charts-container {
|
|
|
- display: flex;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
- justify-content: space-around;
|
|
|
|
|
- margin-top: 20px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.pie-chart {
|
|
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
flex-direction: column;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
|
|
+ margin-top: 20px;
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|