Files
FT577-WCS/ruoyi-ui/src/views/index.vue
猫猫guowen fc6c0ae4dd 首页修改
2025-10-16 16:41:09 +08:00

700 lines
20 KiB
Vue

<template>
<div class="app-container">
<!-- 统计卡片 -->
<el-row :gutter="20" class="stats-row">
<el-col :span="4" >
<el-card class="stats-card">
<div class="stats-content">
<div class="stats-icon agv-icon">
<i class="el-icon-truck"></i>
</div>
<div class="stats-info">
<div class="stats-number">{{ deviceStats.agvCount }}</div>
<div class="stats-label">AGV设备总数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card class="stats-card">
<div class="stats-content">
<div class="stats-icon ddj-icon">
<i class="el-icon-cpu"></i>
</div>
<div class="stats-info">
<div class="stats-number">{{ deviceStats.ddjCount }}</div>
<div class="stats-label">堆垛机设备总数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card class="stats-card">
<div class="stats-content">
<div class="stats-icon ctu-icon">
<i class="el-icon-s-grid"></i>
</div>
<div class="stats-info">
<div class="stats-number">{{ deviceStats.ssjCount }}</div>
<div class="stats-label">输送机设备总数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card class="stats-card">
<div class="stats-content">
<div class="stats-icon queue-icon">
<i class="el-icon-time"></i>
</div>
<div class="stats-info">
<div class="stats-number">{{ taskStats.pendingCount }}</div>
<div class="stats-label">待执行任务</div> <!-- 待执行任务 -->
</div>
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card class="stats-card">
<div class="stats-content">
<div class="stats-icon running-icon">
<i class="el-icon-loading"></i>
</div>
<div class="stats-info">
<div class="stats-number">{{ taskStats.runningCount }}</div>
<div class="stats-label">执行中任务</div> <!-- 执行中任务 -->
</div>
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card class="stats-card">
<div class="stats-content">
<div class="stats-icon finish-icon">
<i class="el-icon-check"></i>
</div>
<div class="stats-info">
<div class="stats-number">{{ taskStats.finishedCount }}</div>
<div class="stats-label">已完成任务</div> <!-- 已完成任务 -->
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 图表区域 -->
<el-row :gutter="20" class="chart-row">
<el-col :span="12">
<el-card class="chart-card">
<div slot="header" class="card-header">
<span>任务类型分布</span> <!-- 任务类型分布 -->
</div>
<div id="taskTypeChart" style="height: 300px;"></div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="chart-card">
<div slot="header" class="card-header">
<span>设备状态分布</span> <!-- 设备状态分布 -->
</div>
<div id="deviceStatusChart" style="height: 300px;"></div>
</el-card>
</el-col>
</el-row>
<!-- 实时任务展示 -->
<el-row :gutter="20" class="task-row">
<el-col :span="12">
<el-card class="task-card">
<div slot="header" class="card-header">
<span>正执行作业列表</span> <!-- 当前任务队列 -->
<el-button type="text" @click="refreshTasks">刷新</el-button> <!-- 刷新 -->
</div>
<el-table
:data="currentTasks"
style="width: 100%"
height="300"
v-loading="loading.tasks">
<el-table-column prop="inOrOut" label="作业类型" width="100">
<template slot-scope="scope">
<dict-tag :options="dict.type.in_or_out" :value="scope.row.inOrOut"/>
</template>
</el-table-column>
<el-table-column prop="jobId" label="作业ID" width="80"/>
<el-table-column prop="taskNo" label="任务号" width="80"/>
<el-table-column prop="executionPhase" label="执行阶段" width="80">
<template slot-scope="scope">
<dict-tag :options="dict.type.job_ep" :value="scope.row.executionPhase"/>
</template>
</el-table-column>
<el-table-column prop="status" :label="$t('index.status')" width="100">
<template slot-scope="scope">
<el-tag :type="getStatusType(scope.row.status)">
<dict-tag :options="dict.type.task_status" :value="scope.row.status"/>
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip/>
</el-table>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="task-card">
<div slot="header" class="card-header">
<span>{{ $t('index.runningDeviceList') }}</span> <!-- 正在运行设备列表 -->
<el-button type="text" @click="refreshDevices">{{ $t('index.refresh') }}</el-button> <!-- 刷新 -->
</div>
<el-table
:data="deviceList"
style="width: 100%"
height="300"
v-loading="loading.devices">
<el-table-column prop="deviceCode" :label="$t('index.deviceCode')" width="200"/> <!-- 设备编码 -->
<el-table-column prop="deviceName" :label="$t('index.deviceName')" width="200"/> <!-- 设备名称 -->
<el-table-column prop="deviceType" :label="$t('index.type')" width="150"> <!-- 类型 -->
<template slot-scope="scope">
<el-tag>{{scope.row.deviceType}}</el-tag>
</template>
</el-table-column>
<el-table-column prop="taskNo" :label="$t('index.currentTask')" show-overflow-tooltip/> <!-- 当前任务 -->
</el-table>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import * as echarts from 'echarts'
import { getDeviceStats, getTaskStats, getCurrentTasks, getDeviceList, getTaskTypeChart, getDeviceStatusChart } from '@/api/index'
export default {
name: 'Index',
dicts: ['task_status', 'wms_task_type','job_ep','in_or_out'],
data() {
return {
// 统计数据
deviceStats: {
agvCount: 0,
ctuCount: 0,
ssjCount: 0
},
taskStats: {
pendingCount: 0,
runningCount: 0,
finishedCount: 0
},
// 当前任务列表
currentTasks: [],
// 设备列表
deviceList: [],
// 加载状态
loading: {
tasks: false,
devices: false
},
// 图表实例
taskTypeChart: null,
deviceStatusChart: null,
// 定时器
refreshTimer: null
}
},
created() {
this.initData()
// 添加页面可见性监听
this.handleVisibilityChange = this.handleVisibilityChange.bind(this)
document.addEventListener('visibilitychange', this.handleVisibilityChange)
},
mounted() {
this.$nextTick(() => {
this.initCharts()
})
// 页面挂载后启动定时器
this.startTimer()
},
activated() {
// keep-alive组件激活时启动定时器
this.startTimer()
},
deactivated() {
// keep-alive组件失活时停止定时器
this.stopTimer()
},
beforeDestroy() {
// 清除定时器
this.stopTimer()
// 移除页面可见性监听
document.removeEventListener('visibilitychange', this.handleVisibilityChange)
// 销毁图表
if (this.taskTypeChart) {
this.taskTypeChart.dispose()
}
if (this.deviceStatusChart) {
this.deviceStatusChart.dispose()
}
},
watch: {
// 监听路由变化
'$route'(to, from) {
// 当路由变化时,检查是否为首页
if (to.path === '/index' || to.name === 'Index' || to.path === '/') {
this.startTimer() // 进入首页时启动定时器
} else {
this.stopTimer() // 离开首页时停止定时器
}
}
},
methods: {
// 启动定时器
startTimer() {
// 如果定时器已存在,先清除
this.stopTimer()
// 只有在页面可见且为首页时才启动定时器
if (this.isPageVisible() && this.isIndexPage()) {
this.refreshTimer = setInterval(() => {
this.refreshData()
}, 8000) // 5秒刷新一次
console.log('定时器已启动') // 定时器已启动
}
},
// 停止定时器
stopTimer() {
if (this.refreshTimer) {
clearInterval(this.refreshTimer)
this.refreshTimer = null
console.log('定时器已停止') // 定时器已停止
}
},
// 检查页面是否可见
isPageVisible() {
return document.visibilityState === 'visible'
},
// 检查是否为首页
isIndexPage() {
return this.$route.path === '/index' || this.$route.name === 'Index' || this.$route.path === '/'
},
// 处理页面可见性变化
handleVisibilityChange() {
if (this.isPageVisible() && this.isIndexPage()) {
this.startTimer() // 页面变为可见且为首页时启动定时器
} else {
this.stopTimer() // 页面不可见时停止定时器
}
},
// 初始化数据
async initData() {
await Promise.all([
this.loadDeviceStats(),
this.loadTaskStats(),
this.loadCurrentTasks(),
this.loadDeviceList()
])
},
// 刷新数据
async refreshData() {
await this.initData()
this.updateCharts()
},
// 加载设备统计
async loadDeviceStats() {
try {
const response = await getDeviceStats()
if (response.code === 200) {
this.deviceStats = response.data
}
} catch (error) {
console.error('加载设备统计失败:', error) // 加载设备统计失败
this.$message.error(this.$t('index.loadDeviceStatsError')) // 加载设备统计失败
}
},
// 加载任务统计
async loadTaskStats() {
try {
const response = await getTaskStats()
if (response.code === 200) {
this.taskStats = response.data
}
} catch (error) {
console.error('加载任务统计失败:', error) // 加载任务统计失败
this.$message.error(this.$t('index.loadTaskStatsError')) // 加载任务统计失败
}
},
// 加载当前任务
async loadCurrentTasks() {
this.loading.tasks = true
try {
const response = await getCurrentTasks()
if (response.code === 200) {
this.currentTasks = response.data
}
} catch (error) {
console.error('加载任务列表失败:', error) // 加载任务列表失败
this.$message.error(this.$t('index.loadTaskListError')) // 加载任务列表失败
} finally {
this.loading.tasks = false
}
},
// 加载设备列表
async loadDeviceList() {
this.loading.devices = true
try {
const response = await getDeviceList()
if (response.code === 200) {
this.deviceList = response.data
}
} catch (error) {
console.error('加载设备列表失败:', error) // 加载设备列表失败
this.$message.error(this.$t('index.loadDeviceListError')) // 加载设备列表失败
} finally {
this.loading.devices = false
}
},
// 初始化图表
initCharts() {
this.initTaskTypeChart()
this.initDeviceStatusChart()
},
// 初始化任务类型图表
async initTaskTypeChart() {
const chartDom = document.getElementById('taskTypeChart')
this.taskTypeChart = echarts.init(chartDom)
try {
const response = await getTaskTypeChart()
let chartData = []
if (response.code === 200 && response.data.length > 0) {
chartData = response.data.map(item => ({
...item,
name: this.selectDictLabel(this.dict.type.wms_task_type, item.name)
}))
} else {
// 默认数据
chartData = [
{ value: 45, name: this.$t('index.inboundTask') }, // 入库任务
{ value: 35, name: this.$t('index.outboundTask') }, // 出库任务
{ value: 20, name: this.$t('index.transferTask') } // 移库任务
]
}
const option = {
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: this.$t('index.taskTypeName'), // 任务类型
type: 'pie',
radius: '50%',
data: chartData,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
this.taskTypeChart.setOption(option)
} catch (error) {
console.error('加载任务类型图表数据失败:', error) // 加载任务类型图表数据失败
}
},
// 初始化设备状态图表
async initDeviceStatusChart() {
const chartDom = document.getElementById('deviceStatusChart')
this.deviceStatusChart = echarts.init(chartDom)
try {
const response = await getDeviceStatusChart()
let runningData = [0, 0, 0]
let idleData = [0, 0, 0]
if (response.code === 200) {
const statusData = response.data
// AGV数据
if (statusData.AGV) {
runningData[0] = statusData.AGV['运行'] || 0 // 运行
idleData[0] = statusData.AGV['其他'] || 0 // 其他
}
// CTU数据
if (statusData.DDJ) {
runningData[1] = statusData.DDJ['运行'] || 0 // 运行
idleData[1] = statusData.DDJ['其他'] || 0 // 其他
}
// SSJ数据
if (statusData.SSJ) {
runningData[2] = statusData.SSJ['运行'] || 0 // 运行
idleData[2] = statusData.SSJ['其他'] || 0 // 其他
}
} else {
// 默认数据
runningData = [8, 4, 2]
idleData = [7, 4, 3]
}
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: [this.$t('index.running'), this.$t('index.other')] // 运行, 其他
},
xAxis: {
type: 'category',
data: ['AGV', 'DDJ', 'SSJ']
},
yAxis: {
type: 'value'
},
series: [
{
name: this.$t('index.running'), // 运行
type: 'bar',
data: runningData,
itemStyle: { color: '#67C23A' }
},
{
name: this.$t('index.other'), // 其他
type: 'bar',
data: idleData,
itemStyle: { color: '#909399' }
}
]
}
this.deviceStatusChart.setOption(option)
} catch (error) {
console.error('加载设备状态图表数据失败:', error) // 加载设备状态图表数据失败
// 使用默认数据
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: [this.$t('index.running'), this.$t('index.other')] // 运行, 其他
},
xAxis: {
type: 'category',
data: ['AGV', 'DDJ', 'SSJ']
},
yAxis: {
type: 'value'
},
series: [
{
name: this.$t('index.running'), // 运行
type: 'bar',
data: [8, 4, 2],
itemStyle: { color: '#67C23A' }
},
{
name: this.$t('index.other'), // 其他
type: 'bar',
data: [7, 4, 3],
itemStyle: { color: '#909399' }
}
]
}
this.deviceStatusChart.setOption(option)
}
},
// 更新图表
async updateCharts() {
if (this.taskTypeChart) {
await this.initTaskTypeChart()
}
if (this.deviceStatusChart) {
await this.initDeviceStatusChart()
}
},
// 刷新任务
refreshTasks() {
this.loadCurrentTasks()
},
// 刷新设备
refreshDevices() {
this.loadDeviceList()
},
// 获取状态类型
getStatusType(status) {
const statusMap = {
'0': 'info',
'1': 'warning',
'2': 'primary',
'3': 'success',
'4': 'warning',
}
return statusMap[status] || 'info'
},
// 获取设备类型文本
getDeviceTypeText(type) {
const typeMap = {
'1': 'AGV',
'2': 'CTU',
'3': 'SSJ'
}
return typeMap[type] || this.$t('index.unknown') // 未知
},
// 获取设备状态类型
getDeviceStatusType(status) {
const statusMap = {
'运行中': 'success',
'空闲': 'info',
'故障': 'danger',
'维护': 'warning'
}
return statusMap[status] || 'info'
}
}
}
</script>
<style scoped>
.app-container {
padding: 20px;
background-color: #f5f5f5;
}
.stats-row {
margin-bottom: 20px;
}
.chart-row {
margin-bottom: 20px;
}
.task-row {
margin-bottom: 20px;
}
.stats-card {
height: 120px;
cursor: pointer;
transition: all 0.3s;
}
.stats-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.stats-content {
display: flex;
align-items: center;
height: 100%;
padding: 20px;
}
.stats-icon {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20px;
font-size: 24px;
color: white;
}
.agv-icon {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.ddj-icon {
background: linear-gradient(135deg, #dcb536 0%, #e5845b 100%);
}
.ctu-icon {
background: linear-gradient(135deg, #36d1dc 0%, #5b86e5 100%);
}
.queue-icon {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.running-icon {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.finish-icon {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
.stats-info {
flex: 1;
}
.stats-number {
font-size: 32px;
font-weight: bold;
color: #303133;
line-height: 1;
margin-bottom: 8px;
}
.stats-label {
font-size: 14px;
color: #909399;
}
.chart-card,
.task-card {
height: 400px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
font-weight: bold;
color: #303133;
}
.el-table {
font-size: 12px;
}
.el-table th {
background-color: #fafafa;
}
.el-tag {
font-size: 12px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.stats-row .el-col {
margin-bottom: 10px;
}
.chart-row .el-col,
.task-row .el-col {
margin-bottom: 20px;
}
.stats-content {
padding: 15px;
}
.stats-number {
font-size: 24px;
}
}
</style>