迁移设备监控模块
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled

- 新增计划与生产设备监控 V2 页面

- 新增设备监控接口、路由和中英文文案

- 更新迁移任务列表中的设备监控状态
This commit is contained in:
sheng
2026-06-22 15:50:19 +08:00
parent b942d24f2c
commit eb31da72e0
6 changed files with 283 additions and 3 deletions

View File

@@ -0,0 +1,221 @@
<template>
<d2-container>
<template #header>
<div class="status-board">
<el-card
:class="['status-card', 'status-card--all', { 'is-active': activeStatus === '' }]"
shadow="hover"
@click.native="selectStatus('all')"
>
<span class="status-card__label">{{ $t(key('device_total')) }}</span>
<strong class="status-card__value">{{ totalDeviceCount }}</strong>
</el-card>
<el-card
v-for="item in statusCards"
:key="item.status"
:class="['status-card', 'status-card--' + item.className, { 'is-active': activeStatus === item.status }]"
shadow="hover"
@click.native="selectStatus(item.status)"
>
<span class="status-card__label">{{ $t(key(item.label)) }}</span>
<strong class="status-card__value">{{ item.count }}</strong>
</el-card>
</div>
</template>
<page-table
:columns="columns"
:data="tableData"
:loading="loading"
:toolbar-buttons="[]"
:row-buttons="[]"
:pagination="null"
:table-attrs="{ size: 'mini', rowKey: 'id', highlightCurrentRow: true }"
auto-height
>
<template #col-status="{ row }">
<el-tag :color="statusColor(row.status)" class="status-tag">
{{ statusName(row.status) }}
</el-tag>
</template>
<template #col-msg="{ row }">
<span>{{ row.status === 'TROUBLE' ? row.msg : '' }}</span>
</template>
<template #empty>
<el-empty :description="$t('暂无数据')" :image-size="80" />
</template>
</page-table>
</d2-container>
</template>
<script>
import { useTableColumns } from '@/composables/useTableColumns'
import { i18nMixin } from '@/composables/useI18n'
import PageTable from '@/components/page-table'
import { getDeviceAll } from '@/api/planning-production/equipment-monitoring'
const STATUS_META = {
FINISH: { label: 'finished', color: '#409EFF', className: 'finish' },
RUN: { label: 'run', color: '#67C23A', className: 'run' },
OFFLINE: { label: 'offline', color: '#A1A1A1', className: 'offline' },
TROUBLE: { label: 'exception', color: '#F56C6C', className: 'trouble' },
IDLE: { label: 'idle', color: '#E6A23C', className: 'idle' },
PAUSE: { label: 'manual', color: '#EF42EF', className: 'pause' }
}
export default {
name: 'planning-production-equipment-monitoring',
components: { PageTable },
mixins: [i18nMixin('page.planning_production.production_monitoring.equipment_monitoring')],
data () {
return {
loading: false,
activeStatus: '',
tableData: [],
statusNum: [],
refreshTimer: null
}
},
computed: {
columns () {
return useTableColumns([
{ prop: 'code', label: this.key('device_code'), minWidth: 140, showOverflowTooltip: true },
{ prop: 'name', label: this.key('device_name'), minWidth: 160, showOverflowTooltip: true },
{ prop: 'area_name', label: this.key('area'), minWidth: 120, showOverflowTooltip: true },
{ prop: 'line_name', label: this.key('production_line'), minWidth: 140, showOverflowTooltip: true },
{ prop: 'workstation_name', label: this.key('workstation'), minWidth: 140, showOverflowTooltip: true },
{ prop: 'heartbeat', label: this.key('last_response'), minWidth: 170, showOverflowTooltip: true },
{ prop: 'status', label: this.key('work_status'), minWidth: 110, slot: 'status' },
{ prop: 'msg', label: this.key('msg'), minWidth: 180, slot: 'msg', showOverflowTooltip: true }
], {
selectionWidth: 0,
indexWidth: 55
})
},
statusCountMap () {
return this.statusNum.reduce((result, item) => {
result[item.status] = Number(item.num || 0)
return result
}, {})
},
totalDeviceCount () {
return Object.keys(this.statusCountMap).reduce((total, status) => {
return total + this.statusCountMap[status]
}, 0)
},
statusCards () {
return Object.keys(STATUS_META).map(status => ({
status,
count: this.statusCountMap[status] || 0,
...STATUS_META[status]
}))
}
},
mounted () {
this.fetchData()
this.refreshTimer = setInterval(() => {
this.fetchData(false)
}, 5000)
},
beforeDestroy () {
if (this.refreshTimer) {
clearInterval(this.refreshTimer)
this.refreshTimer = null
}
},
methods: {
selectStatus (status) {
this.activeStatus = status === 'all' ? '' : status
this.fetchData()
},
async fetchData (showLoading = true) {
if (showLoading) this.loading = true
try {
const res = await getDeviceAll({ status: this.activeStatus })
const payload = res && res.data ? res.data : (res || {})
this.statusNum = Array.isArray(payload.status_num) ? payload.status_num : []
this.tableData = Array.isArray(payload.data) ? payload.data : []
} finally {
if (showLoading) this.loading = false
}
},
statusName (status) {
const meta = STATUS_META[status]
return meta ? this.$t(this.key(meta.label)) : status
},
statusColor (status) {
const meta = STATUS_META[status]
return meta ? meta.color : '#909399'
}
}
}
</script>
<style lang="scss" scoped>
.status-board {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
gap: 10px;
}
.status-card {
cursor: pointer;
border-left: 4px solid #dcdfe6;
::v-deep .el-card__body {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 14px;
}
&.is-active {
border-color: #303133;
box-shadow: 0 2px 10px rgba(48, 49, 51, 0.12);
}
}
.status-card--all {
border-left-color: #303133;
}
.status-card--finish {
border-left-color: #409EFF;
}
.status-card--run {
border-left-color: #67C23A;
}
.status-card--offline {
border-left-color: #A1A1A1;
}
.status-card--trouble {
border-left-color: #F56C6C;
}
.status-card--idle {
border-left-color: #E6A23C;
}
.status-card--pause {
border-left-color: #EF42EF;
}
.status-card__label {
color: #606266;
font-size: 13px;
}
.status-card__value {
color: #303133;
font-size: 22px;
line-height: 1;
}
.status-tag {
color: #fff;
border: 0;
}
</style>