统计视图
This commit is contained in:
parent
b123bb2131
commit
995839d70e
@ -4,8 +4,7 @@ NODE_ENV=development
|
||||
VITE_DEV=true
|
||||
|
||||
# 请求路径
|
||||
# VITE_BASE_URL='http://192.168.0.74:48080'
|
||||
# VITE_BASE_URL='http://192.168.0.153:48080'
|
||||
# VITE_BASE_URL='http://192.168.77.50:48080'
|
||||
VITE_BASE_URL='http://10.10.100.17:48080'
|
||||
|
||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
|
||||
|
21
src/api/map/statistics.ts
Normal file
21
src/api/map/statistics.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
//统计车辆状态分类
|
||||
export const robotStatusClassification = async (params) => {
|
||||
return await request.get({ url: `/system/statistics/robotStatusClassification`, params })
|
||||
}
|
||||
|
||||
//统计任务人工完成-自动完成
|
||||
export const robotTaskAutomaticArtificial = async (params) => {
|
||||
return await request.get({ url: `/system/statistics/robotTaskAutomaticArtificial`, params })
|
||||
}
|
||||
|
||||
//统计故障根因分析
|
||||
export const robotWarnMsgClassification = async (params) => {
|
||||
return await request.get({ url: `/system/statistics/robotWarnMsgClassification`, params })
|
||||
}
|
||||
|
||||
//车辆工作时长统计
|
||||
export const robotWorkHourStatistics = async (params) => {
|
||||
return await request.get({ url: `/system/statistics/robotWorkHourStatistics`, params })
|
||||
}
|
19
src/styles/FormCreate/index.css
Normal file
19
src/styles/FormCreate/index.css
Normal file
@ -0,0 +1,19 @@
|
||||
@font-face {
|
||||
font-family: "fc-icon";
|
||||
src: url("@/styles/FormCreate/fonts/fontello.woff") format("woff");
|
||||
}
|
||||
.icon-doc-text:before {
|
||||
content: "\f0f6";
|
||||
}
|
||||
|
||||
.icon-server:before {
|
||||
content: "\f233";
|
||||
}
|
||||
|
||||
.icon-address-card-o:before {
|
||||
content: "\f2bc";
|
||||
}
|
||||
|
||||
.icon-user-o:before {
|
||||
content: "\f2c0";
|
||||
}/*# sourceMappingURL=index.css.map */
|
1
src/styles/FormCreate/index.css.map
Normal file
1
src/styles/FormCreate/index.css.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["index.scss","index.css"],"names":[],"mappings":"AAEA;EACE,sBAAA;EACA,kEAAA;ACDF;ADIA;EACE,gBAAA;ACFF;;ADKA;EACE,gBAAA;ACFF;;ADKA;EACE,gBAAA;ACFF;;ADKA;EACE,gBAAA;ACFF","file":"index.css"}
|
@ -66,6 +66,15 @@
|
||||
class="current-item"
|
||||
:class="currentItem && currentItem.id == floor.id ? 'tool-active' : ''"
|
||||
@click="chooseLocationPoint(floor)"
|
||||
:style="{
|
||||
background:
|
||||
floor.locationEnable === 0 ||
|
||||
floor.locationLock === 0 ||
|
||||
(floor.locationUseStatus === 1 && locationTypeStr === 'release') ||
|
||||
(floor.locationUseStatus === 0 && locationTypeStr === 'take')
|
||||
? '#FFE2E2'
|
||||
: '#F6FFEF'
|
||||
}"
|
||||
>
|
||||
<div>层数: 第{{ floor.locationStorey }}层</div>
|
||||
<div class="mt-4px">库位号: {{ floor.locationNo }}</div>
|
||||
|
424
src/views/statisticalView/components/ChartCard.vue
Normal file
424
src/views/statisticalView/components/ChartCard.vue
Normal file
@ -0,0 +1,424 @@
|
||||
<template>
|
||||
<div class="chart-card">
|
||||
<div class="chart-header">
|
||||
<div class="chart-header-title">
|
||||
<h3>{{ title }}</h3>
|
||||
<div v-if="chartType === 'bar'" class="ml-2">
|
||||
<el-cascader
|
||||
v-model="selectedOptions"
|
||||
:options="cascaderOptions"
|
||||
:props="cascaderProps"
|
||||
@change="handleChange"
|
||||
placeholder="请选择区域"
|
||||
class="!w-140px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dimension-switcher" v-if="chartType !== 'pie1'">
|
||||
<button
|
||||
v-for="item in dimensionOptions"
|
||||
:key="item.value"
|
||||
:class="{ active: dimension === item.value }"
|
||||
@click="handleDimensionChange(item.value)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="chartRef" class="chart-container"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
|
||||
import { ElLoading } from 'element-plus'
|
||||
import * as echarts from 'echarts'
|
||||
import * as MapApi from '@/api/map/map'
|
||||
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
dimension: Number,
|
||||
fetchData: Function,
|
||||
chartType: {
|
||||
type: String,
|
||||
default: 'pie',
|
||||
validator: (value) => ['pie1', 'bar', 'stackedBar', 'pie2'].includes(value)
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['dimension-change'])
|
||||
|
||||
const chartRef = ref(null)
|
||||
let chartInstance = null
|
||||
|
||||
const dimensionOptions = [
|
||||
{ label: '周', value: 1 },
|
||||
{ label: '月', value: 2 },
|
||||
{ label: '季度', value: 3 }
|
||||
]
|
||||
|
||||
// 处理维度切换
|
||||
const handleDimensionChange = (dimension) => {
|
||||
emit('dimension-change', dimension)
|
||||
}
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose()
|
||||
}
|
||||
chartInstance = echarts.init(chartRef.value)
|
||||
}
|
||||
|
||||
// 更新图表
|
||||
const updateChart = async () => {
|
||||
if (!chartInstance) return
|
||||
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: 'Loading',
|
||||
background: 'rgba(0, 0, 0, 0.6)'
|
||||
})
|
||||
|
||||
const data = await props.fetchData(props.dimension)
|
||||
let option = {}
|
||||
if (props.chartType === 'pie1') {
|
||||
option = getPieOption1(data)
|
||||
} else if (props.chartType === 'bar') {
|
||||
option = getBarOption(data)
|
||||
} else if (props.chartType === 'stackedBar') {
|
||||
option = getStackedBarOption(data)
|
||||
} else if (props.chartType === 'pie2') {
|
||||
option = getPieOption2(data)
|
||||
}
|
||||
chartInstance.setOption(option)
|
||||
|
||||
loading.close()
|
||||
}
|
||||
|
||||
// 饼图1配置
|
||||
const getPieOption1 = (data) => {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b} : {c} ({d}%)'
|
||||
},
|
||||
toolbox: {
|
||||
show: true
|
||||
},
|
||||
color: ['#5C7BD9', '#EE6666', '#91CC75', '#FAC858'],
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 70,
|
||||
bottom: 20
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
clockwise: false, //饼图的扇区是否是顺时针排布
|
||||
minAngle: 2, //最小的扇区角度(0 ~ 360)
|
||||
radius: ['50%', '65%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
//图形样式
|
||||
normal: {
|
||||
borderColor: '#ffffff',
|
||||
borderWidth: 6
|
||||
}
|
||||
},
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
position: 'center',
|
||||
formatter: '{a|任务总数}:{c|23412}',
|
||||
rich: {
|
||||
c: {
|
||||
align: 'center',
|
||||
color: '#0D162A',
|
||||
fontSize: 15,
|
||||
fontWeight: 'bold',
|
||||
height: 30
|
||||
},
|
||||
a: {
|
||||
align: 'center',
|
||||
color: '#0D162A',
|
||||
fontSize: 14
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
length: 15,
|
||||
length2: 0,
|
||||
maxSurfaceAngle: 80
|
||||
},
|
||||
data: data
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 普通条形图配置
|
||||
const getBarOption = (data) => {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b} : {c}'
|
||||
},
|
||||
color: ['#2268FF', '#34bf49', '#ffdd00'],
|
||||
toolbox: {},
|
||||
legend: {
|
||||
top: 14
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: data.categories
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, 0.01]
|
||||
},
|
||||
series: data.series
|
||||
}
|
||||
}
|
||||
|
||||
// 堆叠条形图配置
|
||||
const getStackedBarOption = (data) => {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} : {c}'
|
||||
},
|
||||
color: ['#FF7D7D', '#8a8acb', '#FAC858', '#91CC75'],
|
||||
toolbox: {
|
||||
show: true
|
||||
},
|
||||
legend: {
|
||||
top: 14,
|
||||
data: data.series.map((item) => item.name)
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: data.categories
|
||||
// axisLabel: {
|
||||
// interval: 0,
|
||||
// rotate: 30
|
||||
// }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '告警数量',
|
||||
nameTextStyle: {
|
||||
fontSize: 14, // 文字大小
|
||||
padding: [0, 0, 10, 0] // 文字内边距
|
||||
}
|
||||
},
|
||||
series: data.series.map((item) => ({
|
||||
...item,
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
label: {
|
||||
// show: true
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// 饼图2配置
|
||||
const getPieOption2 = (data) => {
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b} : {c} ({d}%)'
|
||||
},
|
||||
color: ['#fc636b', '#6a67ce'],
|
||||
toolbox: {
|
||||
show: true
|
||||
},
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 70,
|
||||
bottom: 20
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Access From',
|
||||
type: 'pie',
|
||||
radius: ['50%', '65%'],
|
||||
center: ['50%', '60%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
//图形样式
|
||||
normal: {
|
||||
borderColor: '#ffffff',
|
||||
borderWidth: 6
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: data
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式调整
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize()
|
||||
}
|
||||
}
|
||||
|
||||
// 先获取所有的地图
|
||||
const selectedOptions = ref([])
|
||||
const selectedId = ref(null)
|
||||
const cascaderOptions = ref([])
|
||||
|
||||
// 配置级联选择器属性
|
||||
const cascaderProps = {
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
children: 'children'
|
||||
}
|
||||
|
||||
const getMapList = async () => {
|
||||
let res = await MapApi.getPositionMapGetMap()
|
||||
cascaderOptions.value = Object.entries(res).map(([floor, areas]) => {
|
||||
return {
|
||||
id: `floor_${floor}`,
|
||||
name: `${floor}层`,
|
||||
children: areas.map((area) => ({
|
||||
id: area.id,
|
||||
name: `${area.area}`,
|
||||
areaInfo: area
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
// 设置默认选中第一个选项
|
||||
if (cascaderOptions.value.length > 0 && cascaderOptions.value[0].children.length > 0) {
|
||||
selectedOptions.value = [cascaderOptions.value[0].id, cascaderOptions.value[0].children[0].id]
|
||||
selectedId.value = cascaderOptions.value[0].children[0].id
|
||||
emit('mapId-change', selectedId.value)
|
||||
}
|
||||
}
|
||||
|
||||
// 选择变化时的处理
|
||||
const handleChange = (value) => {
|
||||
// 最后一个值就是选中的区域ID
|
||||
selectedId.value = value[value.length - 1]
|
||||
emit('mapId-change', selectedId.value)
|
||||
}
|
||||
|
||||
// 监听维度变化
|
||||
watch(
|
||||
() => props.dimension,
|
||||
() => {
|
||||
updateChart()
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => selectedId.value,
|
||||
() => {
|
||||
updateChart()
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
initChart()
|
||||
await getMapList()
|
||||
await updateChart()
|
||||
window.addEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose()
|
||||
chartInstance = null
|
||||
}
|
||||
window.removeEventListener('resize', handleResize)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.chart-card {
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
|
||||
.chart-header-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-header h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.dimension-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dimension-switcher button {
|
||||
padding: 4px 14px;
|
||||
border: 1px solid #dcdfe6;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.dimension-switcher button.active {
|
||||
background: #00329f;
|
||||
color: white;
|
||||
border-color: #00329f;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
min-height: 400px;
|
||||
}
|
||||
</style>
|
@ -1,462 +1,182 @@
|
||||
el-col
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<div class="top-box">
|
||||
<div class="top-box-left"> 统计视图 </div>
|
||||
<div class="top-box-right">
|
||||
<!-- <div class="top-box-right">
|
||||
<div class="top-box-right-title"> 统计方式 </div>
|
||||
<el-select v-model="type" placeholder="请选择" style="width: 100px; margin-left: 10px">
|
||||
<el-option :label="'天'" :value="1" />
|
||||
<el-option :label="'周'" :value="2" />
|
||||
<el-option :label="'月'" :value="3" />
|
||||
</el-select>
|
||||
<el-button style="margin-left: 16px" @click="openForm()">看板管理</el-button>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="dashboard-container">
|
||||
<el-row :gutter="16">
|
||||
<!-- 状态分类饼图 -->
|
||||
<el-col :span="12">
|
||||
<el-card style="width: 100%" shadow="never">
|
||||
<div class="charts-title"> 任务总览 </div>
|
||||
<div ref="chartDom" style="width: 100%; height: 400px"></div>
|
||||
</el-card>
|
||||
<chart-card
|
||||
title="状态分类"
|
||||
:dimension="dimension1"
|
||||
:fetch-data="fetchStatusData"
|
||||
chart-type="pie1"
|
||||
@dimension-change="(val) => (dimension1 = val)"
|
||||
/>
|
||||
</el-col>
|
||||
<!-- 任务总览条形图 -->
|
||||
<el-col :span="12">
|
||||
<el-card style="width: 100%" shadow="never">
|
||||
<div class="charts-title"> 任务完成率 </div>
|
||||
<div ref="chartDomFinish" style="width: 100%; height: 400px"></div>
|
||||
</el-card>
|
||||
<chart-card
|
||||
title="任务总览"
|
||||
:dimension="dimension2"
|
||||
:fetch-data="fetchTaskOverviewData"
|
||||
chart-type="bar"
|
||||
@dimension-change="(val) => (dimension2 = val)"
|
||||
@mapId-change="mapIdChange"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16" style="margin-top: 12px">
|
||||
<el-row :gutter="16" class="mt-4">
|
||||
<!-- 故障类目分析堆叠条形图 -->
|
||||
<el-col :span="12">
|
||||
<el-card style="width: 100%" shadow="never">
|
||||
<div class="charts-title"> AGV工作利用率统计 </div>
|
||||
<div ref="chartDomAgv" style="width: 100%; height: 400px"></div>
|
||||
</el-card>
|
||||
<chart-card
|
||||
title="故障类目分析"
|
||||
:dimension="dimension3"
|
||||
:fetch-data="fetchFaultAnalysisData"
|
||||
chart-type="stackedBar"
|
||||
@dimension-change="(val) => (dimension3 = val)"
|
||||
/>
|
||||
</el-col>
|
||||
<!-- 人工干预任务饼图 -->
|
||||
<el-col :span="12">
|
||||
<el-card style="width: 100%" shadow="never">
|
||||
<div class="charts-title"> 任务异常数 </div>
|
||||
<div ref="chartDomError" style="width: 100%; height: 400px"></div>
|
||||
</el-card>
|
||||
<chart-card
|
||||
title="人工干预任务"
|
||||
:dimension="dimension4"
|
||||
:fetch-data="fetchManualInterventionData"
|
||||
chart-type="pie2"
|
||||
@dimension-change="(val) => (dimension4 = val)"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<BoarViewDialog ref="boarViewDialogRef" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, nextTick, reactive } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { ref } from 'vue'
|
||||
import ChartCard from './components/ChartCard.vue'
|
||||
import BoarViewDialog from './boarViewDialog.vue'
|
||||
import * as StatisticsAPi from '@/api/map/statistics'
|
||||
|
||||
// 创建一个响应式引用来保存DOM元素
|
||||
const chartDom = ref(null)
|
||||
const chartDomFinish = ref(null)
|
||||
const chartDomError = ref(null)
|
||||
const chartDomAgv = ref(null)
|
||||
const boarViewDialogRef = ref(null)
|
||||
// 初始化ECharts实例并设置配置项(这里以折线图为例,但可灵活替换)
|
||||
onMounted(async () => {
|
||||
await nextTick() // 确保DOM已经渲染完成
|
||||
initEcharts()
|
||||
// 每个图表的独立维度状态
|
||||
const dimension1 = ref(1) // 状态分类
|
||||
const dimension2 = ref(1) // 任务总览
|
||||
const dimension3 = ref(1) // 故障类目分析
|
||||
const dimension4 = ref(1) // 人工干预任务
|
||||
const selectedMapId = ref()
|
||||
|
||||
// 1. 状态分类饼图数据获取
|
||||
const fetchStatusData = async () => {
|
||||
const res = await StatisticsAPi.robotStatusClassification()
|
||||
const chartData = [
|
||||
{ value: res.chargeNum, name: '充电车辆' },
|
||||
{ value: res.faultNum, name: '故障车辆' },
|
||||
{ value: res.doingTaskNum, name: '执行任务车辆' },
|
||||
{ value: res.idleNum, name: '空闲车辆' }
|
||||
]
|
||||
return chartData
|
||||
}
|
||||
|
||||
// 2. 任务总览条形图数据获取
|
||||
const fetchTaskOverviewData = async (dimension) => {
|
||||
const res = await StatisticsAPi.robotWorkHourStatistics({
|
||||
type: dimension,
|
||||
positionMapId: selectedMapId.value
|
||||
})
|
||||
|
||||
const initEcharts = () => {
|
||||
initOne()
|
||||
initTwo()
|
||||
initFour()
|
||||
if (!res) {
|
||||
return {
|
||||
categories: [],
|
||||
series: []
|
||||
}
|
||||
} else {
|
||||
const xAxisData = res.map((item) => {
|
||||
return item.robotNo
|
||||
})
|
||||
const freeTimeNum = res.map((item) => {
|
||||
return item.freeTimeNum
|
||||
})
|
||||
const taskTimeNum = res.map((item) => {
|
||||
return item.taskTimeNum
|
||||
})
|
||||
const chargeTimeNum = res.map((item) => {
|
||||
return item.chargeTimeNum
|
||||
})
|
||||
|
||||
const chartData = {
|
||||
categories: xAxisData,
|
||||
series: [
|
||||
{ name: '空闲时长', type: 'bar', data: freeTimeNum, barWidth: '20%', barGap: '25%' },
|
||||
{ name: '任务时长', type: 'bar', data: taskTimeNum, barWidth: '20%', barGap: '25%' },
|
||||
{ name: '充电时长', type: 'bar', data: chargeTimeNum, barWidth: '20%', barGap: '25%' }
|
||||
]
|
||||
}
|
||||
return chartData
|
||||
}
|
||||
}
|
||||
|
||||
const chartInstance = ref(null)
|
||||
const initOne = () => {
|
||||
chartInstance.value = echarts.init(chartDom.value)
|
||||
let ydata = [
|
||||
{
|
||||
name: '执行中',
|
||||
value: 18
|
||||
},
|
||||
{
|
||||
name: '已完成',
|
||||
value: 16
|
||||
},
|
||||
{
|
||||
name: '已取消',
|
||||
value: 15
|
||||
},
|
||||
{
|
||||
name: '未开始',
|
||||
value: 14
|
||||
},
|
||||
{
|
||||
name: '异常',
|
||||
value: 10
|
||||
}
|
||||
]
|
||||
let color = ['#0147EB', '#01BCEB', '#C800FF', '#F1CD0B', '#EB0000']
|
||||
let xdata = ['执行中', '已完成', '已取消', '未开始', '异常']
|
||||
const option = {
|
||||
backgroundColor: 'rgba(255,255,255,1)',
|
||||
color: color,
|
||||
// tooltip: {
|
||||
// trigger: 'item',
|
||||
// // formatter: '{a} <br/>{b} : {c} ({d}%)'
|
||||
// },
|
||||
legend: {
|
||||
orient: 'vartical',
|
||||
x: 'left',
|
||||
top: 'center',
|
||||
left: '60%',
|
||||
bottom: '0%',
|
||||
data: xdata,
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 16,
|
||||
formatter: function (name) {
|
||||
let str = ''
|
||||
ydata.forEach((item) => {
|
||||
if (item.name == name) {
|
||||
str = `{c|${item.name}} {a|${item.value}}`
|
||||
// 3. 故障类目分析堆叠条形图数据获取
|
||||
const fetchFaultAnalysisData = async (dimension) => {
|
||||
let res = await StatisticsAPi.robotWarnMsgClassification({
|
||||
type: dimension
|
||||
})
|
||||
|
||||
const dates = Object.keys(res).sort((a, b) => new Date(a) - new Date(b))
|
||||
const warnLevels = ['1', '2', '3', '4']
|
||||
const series = warnLevels.map((level) => ({
|
||||
name: `级别${level}`,
|
||||
data: new Array(dates.length).fill(0)
|
||||
}))
|
||||
|
||||
dates.forEach((date, dateIndex) => {
|
||||
const warnings = res[date]
|
||||
warnings.forEach((warning) => {
|
||||
const levelIndex = warnLevels.indexOf(warning.warnLevel)
|
||||
if (levelIndex !== -1) {
|
||||
series[levelIndex].data[dateIndex] += warning.num
|
||||
}
|
||||
})
|
||||
return str
|
||||
},
|
||||
textStyle: {
|
||||
rich: {
|
||||
a: {
|
||||
color: '#0D162A',
|
||||
fontSize: 18
|
||||
},
|
||||
c: {
|
||||
color: '#536387',
|
||||
fontSize: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
clockwise: false, //饼图的扇区是否是顺时针排布
|
||||
minAngle: 2, //最小的扇区角度(0 ~ 360)
|
||||
radius: ['50%', '65%'],
|
||||
center: ['30%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
//图形样式
|
||||
normal: {
|
||||
borderColor: '#ffffff',
|
||||
borderWidth: 6
|
||||
}
|
||||
},
|
||||
label: {
|
||||
// '{text|{b}}\n{c} ({d}%)'
|
||||
normal: {
|
||||
show: true,
|
||||
position: 'center',
|
||||
formatter: '{c|234,12} \n {a|任务总数}',
|
||||
rich: {
|
||||
c: {
|
||||
color: '#0D162A',
|
||||
fontSize: 15,
|
||||
fontWeight: 'bold',
|
||||
height: 30
|
||||
},
|
||||
|
||||
a: {
|
||||
align: 'center',
|
||||
color: '#727272',
|
||||
fontSize: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
length: 15,
|
||||
length2: 0,
|
||||
maxSurfaceAngle: 80
|
||||
},
|
||||
data: ydata
|
||||
}
|
||||
]
|
||||
}
|
||||
chartInstance.value.setOption(option)
|
||||
}
|
||||
|
||||
const chartInstanceTwo = ref(null)
|
||||
const initTwo = () => {
|
||||
chartInstanceTwo.value = echarts.init(chartDomFinish.value)
|
||||
let option = {
|
||||
backgroundColor: '#fff',
|
||||
grid: {
|
||||
top: '9%',
|
||||
bottom: '19%',
|
||||
left: '6%',
|
||||
right: '4%'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
label: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
boundaryGap: true, //默认,坐标轴留白策略
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
alignWithLabel: true
|
||||
},
|
||||
data: [
|
||||
'武汉',
|
||||
'襄阳',
|
||||
'黄冈',
|
||||
'荆门',
|
||||
'十堰',
|
||||
'随州',
|
||||
'鄂州',
|
||||
'恩施',
|
||||
'宜昌',
|
||||
'孝感',
|
||||
'咸宁',
|
||||
'仙桃',
|
||||
'潜江',
|
||||
'天门',
|
||||
'黄石',
|
||||
'荆州',
|
||||
'神农架'
|
||||
]
|
||||
},
|
||||
yAxis: {
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: 'solid',
|
||||
color: '#E2E7F5'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitArea: {
|
||||
show: true,
|
||||
areaStyle: {
|
||||
color: 'rgb(245,250,254)'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
symbol: 'circle',
|
||||
symbolSize: 1,
|
||||
lineStyle: {
|
||||
color: '#0147EB'
|
||||
},
|
||||
|
||||
label: {
|
||||
show: false,
|
||||
distance: 1,
|
||||
emphasis: {
|
||||
show: true,
|
||||
offset: [25, -2],
|
||||
backgroundColor: 'rgba(0,0,0,0.7)',
|
||||
color: '#FFF',
|
||||
padding: [8, 20, 8, 6],
|
||||
//width:60,
|
||||
height: 36,
|
||||
formatter: function (params) {
|
||||
var name = params.name
|
||||
var value = params.data
|
||||
var str = name + '\n数据量:' + value
|
||||
return str
|
||||
},
|
||||
rich: {
|
||||
bg: {
|
||||
width: 78,
|
||||
//height:42,
|
||||
color: '#FFF',
|
||||
padding: [20, 0, 20, 10]
|
||||
},
|
||||
br: {
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
2000, 1800, 2800, 900, 1600, 2000, 3000, 2030, 1356, 1900, 4000, 3000, 2000, 3000, 4200,
|
||||
3200, 3800
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
chartInstanceTwo.value.setOption(option)
|
||||
}
|
||||
|
||||
const chartInstanceFour = ref(null)
|
||||
const initFour = () => {
|
||||
chartInstanceFour.value = echarts.init(chartDomError.value)
|
||||
let option = {
|
||||
backgroundColor: '#fff',
|
||||
grid: {
|
||||
top: '9%',
|
||||
bottom: '19%',
|
||||
left: '6%',
|
||||
right: '4%'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
label: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
boundaryGap: true, //默认,坐标轴留白策略
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
alignWithLabel: true
|
||||
},
|
||||
data: [
|
||||
'武汉',
|
||||
'襄阳',
|
||||
'黄冈',
|
||||
'荆门',
|
||||
'十堰',
|
||||
'随州',
|
||||
'鄂州',
|
||||
'恩施',
|
||||
'宜昌',
|
||||
'孝感',
|
||||
'咸宁',
|
||||
'仙桃',
|
||||
'潜江',
|
||||
'天门',
|
||||
'黄石',
|
||||
'荆州',
|
||||
'神农架'
|
||||
]
|
||||
},
|
||||
yAxis: {
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: 'solid',
|
||||
color: '#E2E7F5'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitArea: {
|
||||
show: true,
|
||||
areaStyle: {
|
||||
color: 'rgb(245,250,254)'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
symbol: 'circle',
|
||||
symbolSize: 1,
|
||||
lineStyle: {
|
||||
color: '#0147EB'
|
||||
},
|
||||
|
||||
label: {
|
||||
show: false,
|
||||
distance: 1,
|
||||
emphasis: {
|
||||
show: true,
|
||||
offset: [25, -2],
|
||||
backgroundColor: 'rgba(0,0,0,0.7)',
|
||||
color: '#FFF',
|
||||
padding: [8, 20, 8, 6],
|
||||
//width:60,
|
||||
height: 36,
|
||||
formatter: function (params) {
|
||||
var name = params.name
|
||||
var value = params.data
|
||||
var str = name + '\n数据量:' + value
|
||||
return str
|
||||
},
|
||||
rich: {
|
||||
bg: {
|
||||
width: 78,
|
||||
//height:42,
|
||||
color: '#FFF',
|
||||
padding: [20, 0, 20, 10]
|
||||
},
|
||||
br: {
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
2000, 1800, 2800, 900, 1600, 2000, 3000, 2030, 1356, 1900, 4000, 3000, 2000, 3000, 4200,
|
||||
3200, 3800
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
chartInstanceFour.value.setOption(option)
|
||||
}
|
||||
// 销毁ECharts实例
|
||||
onUnmounted(() => {
|
||||
if (chartInstance.value != null && chartInstance.value.dispose) {
|
||||
chartInstance.value.dispose()
|
||||
}
|
||||
})
|
||||
|
||||
return { categories: dates, series }
|
||||
}
|
||||
|
||||
// 4. 人工干预任务饼图数据获取
|
||||
const fetchManualInterventionData = async (dimension) => {
|
||||
const res = await StatisticsAPi.robotTaskAutomaticArtificial({
|
||||
type: dimension
|
||||
})
|
||||
const chartData = [
|
||||
{ value: res.artificialDoneNum, name: '人工' },
|
||||
{ value: res.automaticDoneNum, name: '自动' }
|
||||
]
|
||||
return chartData
|
||||
}
|
||||
|
||||
const mapIdChange = (e) => {
|
||||
selectedMapId.value = e
|
||||
}
|
||||
|
||||
const openForm = () => {
|
||||
boarViewDialogRef.value.open()
|
||||
}
|
||||
const type = ref(1)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.top-box {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
<style scoped>
|
||||
.dashboard-container {
|
||||
margin-top: 14px;
|
||||
}
|
||||
.top-box-left {
|
||||
color: #0d162a;
|
||||
font-size: 18px;
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.dashboard-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.top-box-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.top-box-right-title {
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
color: #0d162a;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user