style:dashBoard1数据大屏适配中英文

main
黄伟杰 4 days ago
parent 13d157fa72
commit 39bfd8a2e9

@ -99,165 +99,6 @@ export default {
messageRouteMissing: 'Preview route is not configured',
messageDevicePointRequired: 'Please configure at least one device and point group',
messageMissingId: 'Missing record ID, unable to edit'
},
Dashboard1: {
header: {
title: 'Production Line Dashboard',
weather: {
cloudyToClear: 'Cloudy to clear'
},
weekDays: {
Sunday: 'Sunday',
Monday: 'Monday',
Tuesday: 'Tuesday',
Wednesday: 'Wednesday',
Thursday: 'Thursday',
Friday: 'Friday',
Saturday: 'Saturday'
}
},
deviceIdPrefix: 'ID:',
defaultDeviceName: 'Default Device',
statusOffline: 'Offline',
statusRunning: 'Running',
statusStandby: 'Standby',
statusFault: 'Fault',
deviceOverview: {
title: 'Device Overview',
device: 'Devices',
running: 'Running',
idle: 'Standby',
alarm: 'Alarms',
utilization: 'Utilization',
faultRate: 'Fault Rate'
},
alarms: {
title: 'Real-time Alarms',
tag: 'Scrolling',
levelSevere: 'Severe',
levelWarn: 'Warning',
levelInfo: 'Info'
},
productionTrend: {
title: 'Output Trend',
tagToday: 'Today',
seriesOutput: 'Output'
},
eventReminder: {
title: 'Event Reminder',
modeDevice: 'Device',
modeMold: 'Mold',
check: 'Inspection',
maintain: 'Maintenance',
repair: 'Repair'
},
taskList: {
title: 'Task List',
columns: {
code: 'Code',
name: 'Name',
type: 'Type',
finishStatus: 'Completion',
result: 'Result'
},
finishPending: 'Pending',
finishDone: 'Completed',
finishCanceled: 'Canceled',
resultPass: 'Pass',
resultFail: 'Fail'
},
energyMonitor: {
title: 'Energy Monitor',
selectPlaceholder: 'Please select',
seriesEnergy: 'Energy'
}
}
},
Dashboard8: {
Header: {
title: 'Smart Manufacturing Line Task Overview',
subTitle: 'INTELLIGENT MANUFACTURING REAL-TIME DASHBOARD'
},
TaskBoard: {
title: 'Line Task Board',
tag: 'Auto refresh · Scrolling',
statusCompleted: 'Completed',
statusLowProgress: 'Behind',
columns: {
lineName: 'Line',
planNo: 'Plan No.',
productName: 'Product',
planQty: 'Planned',
doneQty: 'Completed',
passRate: 'Pass Rate'
}
},
DayCapacity: {
title: 'Daily Capacity',
tag: 'Today',
metrics: {
orders: 'Orders',
scheduled: 'Scheduled',
produced: 'Produced',
rate: 'Quality Rate'
},
chart: {
scheduled: 'Scheduled',
produced: 'Produced'
}
},
MonthCapacity: {
title: 'Monthly Capacity',
tag: 'This month',
metrics: {
orders: 'Orders',
scheduled: 'Scheduled',
produced: 'Produced',
rate: 'Quality Rate'
},
chart: {
scheduled: 'Scheduled',
produced: 'Produced'
}
},
WeekTrend: {
title: 'Weekly Output Trend',
legendOutput: 'Output',
legendPlan: 'Plan',
seriesPlanOutput: 'Planned Output',
seriesActualOutput: 'Actual Output'
},
OpsTrend: {
title: 'Completed Output',
defaultLine: 'All lines',
yAxisName: 'Completed Output',
seriesName: 'Completed Output',
summary: '{line} · Avg {value}'
},
TodayOps: {
title: 'Today Power/Utilization',
empty: 'No data',
powerOnRate: 'Power-on Rate',
utilizationRate: 'Utilization'
},
QualityTrend: {
title: 'Product Quality Trend',
tag: 'Daily (All lines)',
seriesName: 'Pass Rate',
markLineAverage: 'Avg {value}%',
markLineAverageName: 'Average'
},
RealAlarm: {
title: 'Real-time Alarms',
tag: 'Scrolling',
levelSevere: 'Severe',
levelWarn: 'Warning',
levelInfo: 'Info'
},
EnergyTrend: {
title: 'Weekly Energy Trend',
selectPlaceholder: 'Please select',
seriesActualEnergy: 'Actual Energy (kWh)'
}
},
ErpStock: {
@ -3964,6 +3805,8 @@ export default {
dialogCollectionTimeLabel: 'Collection Time',
dialogCollectionTimeStartPlaceholder: 'Start Time',
dialogCollectionTimeEndPlaceholder: 'End Time',
dialogPointFilterLabel: 'Point Filter',
dialogPointFilterPlaceholder: 'Please select points',
dialogSearchButtonText: 'Search',
dialogResetButtonText: 'Reset',
dialogRecordCollectionTimePrefix: 'Collection Time: ',

@ -99,165 +99,6 @@ export default {
messageRouteMissing: '未配置预览路由',
messageDevicePointRequired: '请至少配置一组设备和点位',
messageMissingId: '缺少数据编号,无法编辑'
},
Dashboard1: {
header: {
title: '产线运行看板',
weather: {
cloudyToClear: '多云转晴'
},
weekDays: {
Sunday: '星期日',
Monday: '星期一',
Tuesday: '星期二',
Wednesday: '星期三',
Thursday: '星期四',
Friday: '星期五',
Saturday: '星期六'
}
},
deviceIdPrefix: 'ID:',
defaultDeviceName: '默认设备',
statusOffline: '离线',
statusRunning: '运行',
statusStandby: '待机',
statusFault: '故障',
deviceOverview: {
title: '设备概况',
device: '设备数量',
running: '运行数量',
idle: '待机数量',
alarm: '报警数量',
utilization: '稼动率',
faultRate: '故障率'
},
alarms: {
title: '实时报警信息',
tag: '滚动展示',
levelSevere: '严重',
levelWarn: '警告',
levelInfo: '提示'
},
productionTrend: {
title: '产量趋势',
tagToday: '今日',
seriesOutput: '产量'
},
eventReminder: {
title: '事件提醒',
modeDevice: '设备',
modeMold: '模具',
check: '点检',
maintain: '保养',
repair: '维修'
},
taskList: {
title: '任务列表',
columns: {
code: '编号',
name: '名称',
type: '类型',
finishStatus: '完成状态',
result: '结果'
},
finishPending: '待完成',
finishDone: '已完成',
finishCanceled: '已取消',
resultPass: '通过',
resultFail: '不通过'
},
energyMonitor: {
title: '能耗监测',
selectPlaceholder: '请选择',
seriesEnergy: '能耗'
}
}
},
Dashboard8: {
Header: {
title: '智能制造产线任务总览',
subTitle: '智能制造实时看板'
},
TaskBoard: {
title: '产线任务看板',
tag: '实时刷新 · 滚动展示',
statusCompleted: '已完成',
statusLowProgress: '进度偏低',
columns: {
lineName: '产线名称',
planNo: '计划单',
productName: '产品名称',
planQty: '计划数量',
doneQty: '完工数量',
passRate: '合格率'
}
},
DayCapacity: {
title: '日产能达成情况',
tag: '当日维度',
metrics: {
orders: '排产单数量',
scheduled: '已排产数量',
produced: '已生产数量',
rate: '产能合格率'
},
chart: {
scheduled: '已排产',
produced: '已生产'
}
},
MonthCapacity: {
title: '月产能达成情况',
tag: '当月累计',
metrics: {
orders: '排产单数量',
scheduled: '已排产数量',
produced: '已生产数量',
rate: '产能合格率'
},
chart: {
scheduled: '已排产',
produced: '已生产'
}
},
WeekTrend: {
title: '周生产趋势',
legendOutput: '产量',
legendPlan: '计划',
seriesPlanOutput: '计划产量',
seriesActualOutput: '实际产量'
},
OpsTrend: {
title: '产能完成数',
defaultLine: '全部产线',
yAxisName: '产能完成数',
seriesName: '产能完成数',
summary: '{line} · 日均完成 {value}'
},
TodayOps: {
title: '今日开机率/稼动率',
empty: '暂无数据',
powerOnRate: '开机率',
utilizationRate: '稼动率'
},
QualityTrend: {
title: '成品检合格率趋势图',
tag: '按天统计(全产线)',
seriesName: '合格率',
markLineAverage: '平均 {value}%',
markLineAverageName: '平均值'
},
RealAlarm: {
title: '实时报警信息',
tag: '滚动展示',
levelSevere: '严重',
levelWarn: '警告',
levelInfo: '提示'
},
EnergyTrend: {
title: '能耗周趋势',
selectPlaceholder: '请选择',
seriesActualEnergy: '实际能耗(kWh)'
}
},
ErpStock: {
@ -3808,6 +3649,8 @@ export default {
dialogCollectionTimeLabel: '采集时间',
dialogCollectionTimeStartPlaceholder: '开始时间',
dialogCollectionTimeEndPlaceholder: '结束时间',
dialogPointFilterLabel: '点位筛选',
dialogPointFilterPlaceholder: '请选择点位',
dialogSearchButtonText: '查询',
dialogResetButtonText: '重置',
dialogRecordCollectionTimePrefix: '采集时间:',

@ -43,7 +43,7 @@
filter="url(#frameGlow)"
/>
</svg>
<div class="title">{{ t('Dashboard1.header.title') }}</div>
<div class="title">产线运行看板</div>
</div>
</div>
<div class="header-right">
@ -64,17 +64,13 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
const router = useRouter()
const { t } = useI18n('ReportDashboard')
const localeStore = useLocaleStore()
const timeStr = ref('')
const dateStr = ref('')
const weather = ref({ temp: '27°C', desc: t('Dashboard1.header.weather.cloudyToClear') })
const weather = ref({ temp: '27°C', desc: 'Cloudy to clear' })
let timer: number | undefined
const goBack = () => {
@ -91,9 +87,8 @@ const updateTime = () => {
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
const weekKeys = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
const weekKey = weekKeys[d.getDay()] || 'Sunday'
dateStr.value = `${t(`Dashboard1.header.weekDays.${weekKey}`)}, ${y}-${m}-${day}`
const weekMap = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
dateStr.value = `${weekMap[d.getDay()]}, ${y}-${m}-${day}`
}
onMounted(() => {
@ -101,14 +96,6 @@ onMounted(() => {
timer = window.setInterval(updateTime, 1000)
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
weather.value.desc = t('Dashboard1.header.weather.cloudyToClear')
updateTime()
}
)
onUnmounted(() => {
if (timer) clearInterval(timer)
})

@ -2,7 +2,7 @@
<div class="card">
<div class="panel-title">
<span class="title-dot"></span>
<span>{{ t('Dashboard1.deviceOverview.title') }}</span>
<span>设备概况</span>
</div>
<div class="panel-body overview-body">
<div v-for="item in overviewItems" :key="item.key" class="gauge-item">
@ -18,12 +18,10 @@
</template>
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { colors } from '../utils'
import { DashboardApi } from '@/api/dashboard'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
type OverviewItemKey = 'device' | 'running' | 'idle' | 'alarm' | 'utilization' | 'faultRate'
@ -37,25 +35,14 @@ interface OverviewItem {
const route = useRoute()
const orgId = route.query.orgId
const { t } = useI18n('ReportDashboard')
const localeStore = useLocaleStore()
const getLabel = (key: OverviewItemKey) => {
if (key === 'device') return t('Dashboard1.deviceOverview.device')
if (key === 'running') return t('Dashboard1.deviceOverview.running')
if (key === 'idle') return t('Dashboard1.deviceOverview.idle')
if (key === 'alarm') return t('Dashboard1.deviceOverview.alarm')
if (key === 'utilization') return t('Dashboard1.deviceOverview.utilization')
return t('Dashboard1.deviceOverview.faultRate')
}
const overviewItems = ref<OverviewItem[]>([
{ key: 'device', label: getLabel('device'), value: '0', percent: 0, color: colors.cyan },
{ key: 'running', label: getLabel('running'), value: '0', percent: 0, color: colors.blue },
{ key: 'idle', label: getLabel('idle'), value: '0', percent: 0, color: colors.warn },
{ key: 'alarm', label: getLabel('alarm'), value: '0', percent: 0, color: colors.danger },
{ key: 'utilization', label: getLabel('utilization'), value: '0%', percent: 0, color: colors.green },
{ key: 'faultRate', label: getLabel('faultRate'), value: '0%', percent: 0, color: colors.purple }
{ key: 'device', label: '设备数量', value: '0', percent: 0, color: colors.cyan },
{ key: 'running', label: '运行数量', value: '0', percent: 0, color: colors.blue },
{ key: 'idle', label: '待机数量', value: '0', percent: 0, color: colors.warn },
{ key: 'alarm', label: '报警数量', value: '0', percent: 0, color: colors.danger },
{ key: 'utilization', label: '稼动率', value: '0%', percent: 0, color: colors.green },
{ key: 'faultRate', label: '故障率', value: '0%', percent: 0, color: colors.purple }
])
const getGaugeStyle = (percent: number, color: string) => {
@ -68,8 +55,7 @@ const getGaugeStyle = (percent: number, color: string) => {
const formatNumber = (value: number | string | undefined | null) => {
const n = Number(value ?? 0)
if (!Number.isFinite(n)) return '0'
const lang = localeStore.getCurrentLocale.lang
return n.toLocaleString(lang === 'zh-CN' ? 'zh-CN' : 'en-US')
return n.toLocaleString('zh-CN')
}
const calcPercent = (part: number, total: number) => {
@ -151,14 +137,6 @@ const loadOverview = async () => {
onMounted(() => {
loadOverview()
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
overviewItems.value = overviewItems.value.map((item) => ({ ...item, label: getLabel(item.key) }))
loadOverview()
}
)
</script>
<style scoped>

@ -3,11 +3,11 @@
<div class="panel-title panel-title-between">
<div class="panel-title-left">
<span class="title-dot"></span>
<span>{{ t('Dashboard1.energyMonitor.title') }}</span>
<span>能耗监测</span>
</div>
<div class="tabs">
<el-select
v-model="selectedEnergyTypeId" :placeholder="t('Dashboard1.energyMonitor.selectPlaceholder')" style="width: 120px" size="small"
v-model="selectedEnergyTypeId" placeholder="请选择" style="width: 120px" size="small"
@change="getChartData">
<el-option v-for="item in energyTypes" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
@ -20,19 +20,15 @@ v-model="selectedEnergyTypeId" :placeholder="t('Dashboard1.energyMonitor.selectP
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { onMounted, onUnmounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import * as echarts from 'echarts'
import { colors, style } from '../utils'
import { EnergyTypeApi, EnergyTypeVO } from '@/api/mes/energytype'
import { EnergyDeviceApi } from '@/api/mes/energydevice'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
const route = useRoute()
const orgId = route.query.orgId
const { t } = useI18n('ReportDashboard')
const localeStore = useLocaleStore()
const energyTypes = ref<EnergyTypeVO[]>([])
const selectedEnergyTypeId = ref<number | undefined>(undefined)
@ -66,31 +62,6 @@ const getChartData = async () => {
}
}
const buildOption = (x: string[], y: number[]) => {
return {
backgroundColor: 'transparent',
tooltip: { trigger: 'axis' },
grid: { top: '18%', left: '6%', right: '4%', bottom: '12%', containLabel: true },
xAxis: { type: 'category', data: x, axisLine: style.axisLine, axisLabel: { ...style.axisLabel, fontSize: 10, rotate: 40 } },
yAxis: { type: 'value', axisLine: { show: false }, axisLabel: { ...style.axisLabel, fontSize: 10 }, splitLine: style.splitLine },
series: [
{
name: t('Dashboard1.energyMonitor.seriesEnergy'),
type: 'bar',
barWidth: 12,
itemStyle: {
borderRadius: [6, 6, 0, 0],
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: colors.blue },
{ offset: 1, color: 'rgba(30,144,255,0.10)' }
])
},
data: y
}
]
}
}
const render = (data: any = []) => {
if (!chart) return
@ -118,7 +89,27 @@ const render = (data: any = []) => {
y = []
}
chart.setOption(buildOption(x, y))
chart.setOption({
backgroundColor: 'transparent',
tooltip: { trigger: 'axis' },
grid: { top: '18%', left: '6%', right: '4%', bottom: '12%', containLabel: true },
xAxis: { type: 'category', data: x, axisLine: style.axisLine, axisLabel: { ...style.axisLabel, fontSize: 10, rotate: 40 } },
yAxis: { type: 'value', axisLine: { show: false }, axisLabel: { ...style.axisLabel, fontSize: 10 }, splitLine: style.splitLine },
series: [
{
type: 'bar',
barWidth: 12,
itemStyle: {
borderRadius: [6, 6, 0, 0],
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: colors.blue },
{ offset: 1, color: 'rgba(30,144,255,0.10)' }
])
},
data: y
}
]
})
}
const resize = () => {
@ -128,19 +119,13 @@ const resize = () => {
onMounted(() => {
if (!chartRef.value) return
chart = echarts.init(chartRef.value, 'dark', { renderer: 'canvas' })
chart.setOption(buildOption([], []))
//
render([])
//
getEnergyTypes()
window.addEventListener('resize', resize)
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
getChartData()
}
)
onUnmounted(() => {
window.removeEventListener('resize', resize)
chart?.dispose()

@ -3,12 +3,12 @@
<div class="panel-title">
<div class="panel-title-left">
<span class="title-dot"></span>
<span>{{ t('Dashboard1.eventReminder.title') }}</span>
<span>事件提醒</span>
</div>
<div class="panel-title-right">
<el-radio-group v-model="mode" size="small">
<el-radio-button label="device">{{ t('Dashboard1.eventReminder.modeDevice') }}</el-radio-button>
<el-radio-button label="mold">{{ t('Dashboard1.eventReminder.modeMold') }}</el-radio-button>
<el-radio-button label="device">设备</el-radio-button>
<el-radio-button label="mold">模具</el-radio-button>
</el-radio-group>
</div>
</div>
@ -37,8 +37,6 @@ import { useRoute } from 'vue-router'
import * as echarts from 'echarts'
import { DashboardApi, TaskStatisticsData } from '@/api/dashboard'
import { colors } from '../utils'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
interface EventItem {
key: string
@ -50,8 +48,6 @@ interface EventItem {
const route = useRoute()
const orgId = route.query.orgId
const { t } = useI18n('ReportDashboard')
const localeStore = useLocaleStore()
const chartRef = ref<HTMLElement | null>(null)
let chart: echarts.ECharts | null = null
@ -112,21 +108,21 @@ const applyTaskStatistics = (data: TaskStatisticsData) => {
eventItems.value = [
{
key: 'check',
name: t('Dashboard1.eventReminder.check'),
name: '点检',
count: inspection,
percent: toPercent(inspection),
color: colors.cyan
},
{
key: 'maintain',
name: t('Dashboard1.eventReminder.maintain'),
name: '保养',
count: maintenance,
percent: toPercent(maintenance),
color: colors.warn
},
{
key: 'repair',
name: t('Dashboard1.eventReminder.repair'),
name: '维修',
count: repair,
percent: toPercent(repair),
color: colors.danger
@ -153,14 +149,6 @@ watch(mode, () => {
applyTaskStatistics(rawStats.value)
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
if (!rawStats.value) return
applyTaskStatistics(rawStats.value)
}
)
const resize = () => {
chart?.resize()
}

@ -5,9 +5,9 @@
<span class="card-title-icon">
<Icon icon="fa-solid:bell" />
</span>
<span>{{ t('Dashboard1.alarms.title') }}</span>
<span>实时报警信息</span>
</div>
<span class="tag">{{ t('Dashboard1.alarms.tag') }}</span>
<span class="tag">滚动展示</span>
</div>
<div class="card-body">
<ul ref="listRef" class="alarm-list">
@ -28,35 +28,17 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import { DeviceWarningRecordApi, DeviceWarningRecordVO } from '@/api/iot/deviceWarningRecord'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
const route = useRoute()
const orgId = route.query.orgId
const { t } = useI18n('ReportDashboard')
const localeStore = useLocaleStore()
const alarms = ref<any[]>([])
const isAnimating = ref(false)
let timer: ReturnType<typeof setInterval> | null = null
const mapAlarmLevel = (alarmLevel: unknown) => {
const raw = String(alarmLevel ?? '').trim()
if (raw === '2' || raw === '严重' || raw.toLowerCase() === 'severe') {
return { type: 'danger', levelText: t('Dashboard1.alarms.levelSevere') }
}
if (raw === '1' || raw === '警告' || raw.toLowerCase() === 'warning' || raw.toLowerCase() === 'warn') {
return { type: 'warn', levelText: t('Dashboard1.alarms.levelWarn') }
}
if (raw === '0' || raw === '提示' || raw.toLowerCase() === 'info' || raw.toLowerCase() === 'notice') {
return { type: 'safe', levelText: t('Dashboard1.alarms.levelInfo') }
}
return { type: 'safe', levelText: raw || t('Dashboard1.alarms.levelInfo') }
}
const getAlarms = async () => {
try {
const data = (await DeviceWarningRecordApi.getList({ orgId })) || []
@ -67,12 +49,28 @@ const getAlarms = async () => {
timeStr = d.toTimeString().slice(0, 8)
}
const mapped = mapAlarmLevel(item.alarmLevel)
let type = 'safe'
let levelText = item.alarmLevel
if (item.alarmLevel === '2') {
type = 'danger'
levelText = '严重'
} else if (item.alarmLevel === '1') {
type = 'warn'
levelText = '警告'
} else if (item.alarmLevel === '0') {
type = 'safe'
levelText = '提示'
} else {
if (item.alarmLevel === '严重') type = 'danger'
else if (item.alarmLevel === '警告') type = 'warn'
else if (item.alarmLevel === '提示') type = 'safe'
}
return {
time: timeStr,
level: mapped.levelText,
type: mapped.type,
level: levelText,
type,
msg: `${item.deviceName}-${item.ruleName}`
}
})
@ -103,13 +101,6 @@ onMounted(() => {
getAlarms()
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
getAlarms()
}
)
onUnmounted(() => {
if (timer) clearInterval(timer)
})

@ -2,8 +2,8 @@
<div class="card">
<div class="panel-title">
<span class="title-dot"></span>
<span>{{ t('Dashboard1.productionTrend.title') }}</span>
<span class="tag">{{ t('Dashboard1.productionTrend.tagToday') }}</span>
<span>产量趋势</span>
<span class="tag">今日</span>
</div>
<div class="panel-body">
<div ref="chartRef" class="chart"></div>
@ -12,19 +12,15 @@
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { onMounted, onUnmounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import * as echarts from 'echarts'
import { colors, style } from '../utils'
import { DeviceWarningRecordApi } from '@/api/iot/deviceWarningRecord'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
const route = useRoute()
const chartRef = ref<HTMLElement | null>(null)
let chart: echarts.ECharts | null = null
const { t } = useI18n('ReportDashboard')
const localeStore = useLocaleStore()
const render = async () => {
if (!chart) return
@ -73,7 +69,7 @@ const render = async () => {
},
series: [
{
name: t('Dashboard1.productionTrend.seriesOutput'),
name: '产量',
type: 'bar',
barWidth: 12,
itemStyle: {
@ -100,13 +96,6 @@ onMounted(() => {
window.addEventListener('resize', resize)
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
render()
}
)
onUnmounted(() => {
window.removeEventListener('resize', resize)
chart?.dispose()

@ -2,7 +2,7 @@
<div class="card">
<div class="panel-title">
<span class="title-dot"></span>
<span>{{ t('Dashboard1.taskList.title') }}</span>
<span>任务列表</span>
</div>
<div class="panel-body table-body">
<el-table
@ -14,10 +14,10 @@
:border="false"
:highlight-current-row="false"
>
<el-table-column prop="code" :label="t('Dashboard1.taskList.columns.code')" width="100" show-overflow-tooltip />
<el-table-column prop="name" :label="t('Dashboard1.taskList.columns.name')" />
<el-table-column prop="type" :label="t('Dashboard1.taskList.columns.type')" />
<el-table-column :label="t('Dashboard1.taskList.columns.finishStatus')">
<el-table-column prop="code" label="编号" width="100" show-overflow-tooltip />
<el-table-column prop="name" label="名称" />
<el-table-column prop="type" label="类型" />
<el-table-column label="完成状态">
<template #default="scope">
<el-tag
v-if="scope.row.finishStatusLabel"
@ -32,7 +32,7 @@
</el-tag>
</template>
</el-table-column>
<el-table-column :label="t('Dashboard1.taskList.columns.result')">
<el-table-column label="结果">
<template #default="scope">
<div class="status-cell">
<el-tag
@ -56,12 +56,10 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import type { ElTable } from 'element-plus'
import { DashboardApi, DashboardTaskItem } from '@/api/dashboard'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
interface TaskRow {
id: string
@ -76,32 +74,29 @@ interface TaskRow {
const route = useRoute()
const orgId = route.query.orgId
const { t } = useI18n('ReportDashboard')
const localeStore = useLocaleStore()
const taskRows = ref<TaskRow[]>([])
const rawItems = ref<DashboardTaskItem[]>([])
const tableRef = ref<InstanceType<typeof ElTable> | null>(null)
let scrollTimer: number | undefined
const mapItemToRow = (item: DashboardTaskItem, index: number): TaskRow => {
const resultStatus = Number(item.resultStatus) || 0
const finishCode = String(item.finishStatus ?? '').trim()
let finishStatusLabel = String(item.finishStatus ?? '')
let finishStatusLabel = item.finishStatus
let finishStatusType: TaskRow['finishStatusType'] = ''
if (finishCode === '0') finishStatusLabel = t('Dashboard1.taskList.finishPending')
else if (finishCode === '1') finishStatusLabel = t('Dashboard1.taskList.finishDone')
else if (finishCode === '2') finishStatusLabel = t('Dashboard1.taskList.finishCanceled')
if (finishCode === '0') finishStatusLabel = '待完成'
else if (finishCode === '1') finishStatusLabel = '已完成'
else if (finishCode === '2') finishStatusLabel = '已取消'
if (finishCode === '0') finishStatusType = 'warning'
else if (finishCode === '1') finishStatusType = 'success'
else if (finishCode === '2') finishStatusType = 'info'
let resultStatusLabel = '-'
let resultStatusType: TaskRow['resultStatusType'] = ''
if (resultStatus === 1) {
resultStatusLabel = t('Dashboard1.taskList.resultPass')
resultStatusLabel = '通过'
resultStatusType = 'success'
} else if (resultStatus === 2) {
resultStatusLabel = t('Dashboard1.taskList.resultFail')
resultStatusLabel = '不通过'
resultStatusType = 'danger'
}
return {
@ -123,15 +118,12 @@ const loadTaskList = async () => {
| DashboardTaskItem[]
| null
if (!payload || !Array.isArray(payload)) {
rawItems.value = []
taskRows.value = []
return
}
rawItems.value = payload
taskRows.value = payload.map(mapItemToRow)
} catch (error) {
console.error(error)
rawItems.value = []
taskRows.value = []
}
}
@ -169,13 +161,6 @@ onMounted(async () => {
startAutoScroll()
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
taskRows.value = rawItems.value.map(mapItemToRow)
}
)
onUnmounted(() => {
if (scrollTimer) {
clearInterval(scrollTimer)

@ -48,7 +48,7 @@
</el-tag>
</div>
<div class="header-right">
<span class="device-id">{{ t('Dashboard1.deviceIdPrefix') }}{{ item.deviceId }}</span>
<span class="device-id">{{ t('ReportDashboard.Dashboard1.deviceIdPrefix') }}{{ item.deviceId }}</span>
</div>
</div>
<div class="device-body">
@ -80,7 +80,7 @@
</el-tag>
</div>
<div class="header-right">
<span class="device-id">{{ t('Dashboard1.deviceIdPrefix') }}{{ item.deviceId }}</span>
<span class="device-id">{{ t('ReportDashboard.Dashboard1.deviceIdPrefix') }}{{ item.deviceId }}</span>
</div>
</div>
<div class="device-body">
@ -128,7 +128,7 @@ import ProductionTrend from './components/ProductionTrend.vue'
const route = useRoute()
const goviewId = route.query.goviewId as string
const orgId = route.query.orgId
const { t } = useI18n('ReportDashboard')
const { t } = useI18n()
interface DeviceAttribute {
attributeName: string
@ -165,7 +165,7 @@ const loadDeviceAttributes = async () => {
const cards: DeviceCardData[] = list.map((d: any) => {
return {
deviceName: d.deviceName || t('Dashboard1.defaultDeviceName'),
deviceName: d.deviceName || t('ReportDashboard.Dashboard1.defaultDeviceName'),
deviceId: d.deviceId,
operatingStatus: d.operatingStatus,
attributes: (d.attributes || []).map((attr: any) => ({
@ -194,14 +194,8 @@ const getDeviceStatusType = (status?: string) => {
}
const getDeviceStatusText = (status?: string) => {
if (!status) return t('Dashboard1.statusOffline')
const raw = String(status).trim()
const s = raw.toLowerCase()
if (s.includes('故障') || s === 'fault') return t('Dashboard1.statusFault')
if (s.includes('待机') || s === 'standby') return t('Dashboard1.statusStandby')
if (s.includes('运行') || s === 'run' || s === 'running') return t('Dashboard1.statusRunning')
if (s.includes('离线') || s === 'offline') return t('Dashboard1.statusOffline')
return raw
if (!status) return t('ReportDashboard.Dashboard1.statusOffline')
return status
}
onMounted(() => {

@ -6,8 +6,8 @@
<Icon icon="fa-solid:microchip" class="title-mark-icon" />
</div>
<div>
<div class="title">{{ t('Header.title') }}</div>
<div class="sub-title">{{ t('Header.subTitle') }}</div>
<div class="title">智能制造产线任务总览</div>
<div class="sub-title">INTELLIGENT MANUFACTURING REAL-TIME DASHBOARD</div>
</div>
</div>
<div class="header-right">
@ -31,10 +31,8 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from '@/hooks/web/useI18n'
const router = useRouter()
const { t } = useI18n('Dashboard8')
const timeStr = ref('')
let timer: number | undefined

@ -5,9 +5,9 @@
<span class="card-title-icon">
<Icon icon="fa-solid:sun" />
</span>
<span>{{ t('DayCapacity.title') }}</span>
<span>日产能达成情况</span>
</div>
<span class="tag">{{ t('DayCapacity.tag') }}</span>
<span class="tag">当日维度</span>
</div>
<div class="card-body">
<div class="card-body-row">
@ -17,19 +17,19 @@
<div class="card-body-col">
<div class="metrics-grid">
<div class="metric-card">
<div class="metric-label">{{ t('DayCapacity.metrics.orders') }}</div>
<div class="metric-label">排产单数量</div>
<div class="metric-value" ref="dayOrderEl"></div>
</div>
<div class="metric-card">
<div class="metric-label">{{ t('DayCapacity.metrics.scheduled') }}</div>
<div class="metric-label">已排产数量</div>
<div class="metric-value" ref="dayPlanEl"></div>
</div>
<div class="metric-card">
<div class="metric-label">{{ t('DayCapacity.metrics.produced') }}</div>
<div class="metric-label">已生产数量</div>
<div class="metric-value ok" ref="dayPendingEl"></div>
</div>
<div class="metric-card">
<div class="metric-label">{{ t('DayCapacity.metrics.rate') }}</div>
<div class="metric-label">产能合格率</div>
<div class="metric-value accent" ref="dayRateEl"></div>
</div>
</div>
@ -40,18 +40,14 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import * as echarts from 'echarts'
import { colors, animateCount, style } from '../utils'
import { PlanApi } from '@/api/mes/plan'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
const route = useRoute()
const orgId = route.query.orgId
const { t } = useI18n('Dashboard8')
const localeStore = useLocaleStore()
type CapacityData = {
orders: number
@ -75,27 +71,6 @@ const dayRateEl = ref<HTMLElement | null>(null)
let chart: echarts.ECharts | null = null
const buildOption = () => {
return {
backgroundColor: 'transparent',
tooltip: { trigger: 'item' },
legend: { bottom: '0%', left: 'center', textStyle: style.legendText },
series: [
{
type: 'pie',
radius: ['60%', '82%'],
center: ['50%', '55%'],
label: { show: false },
emphasis: { scale: false },
data: [
{ value: day.scheduled, name: t('DayCapacity.chart.scheduled'), itemStyle: { color: colors.cyan } },
{ value: day.produced, name: t('DayCapacity.chart.produced'), itemStyle: { color: colors.green } }
]
}
]
}
}
const mapCapacity = (raw: any): CapacityData => {
const orderCount = Number(raw?.orders ?? raw?.order ?? 0)
const scheduled = Number(raw?.plan ?? 0)
@ -119,7 +94,24 @@ const loadDayCapacity = async () => {
const initChart = () => {
if (!chartRef.value) return
chart = echarts.init(chartRef.value, 'dark', { renderer: 'canvas' })
chart.setOption(buildOption())
chart.setOption({
backgroundColor: 'transparent',
tooltip: { trigger: 'item' },
legend: { bottom: '0%', left: 'center', textStyle: style.legendText },
series: [
{
type: 'pie',
radius: ['60%', '82%'],
center: ['50%', '55%'],
label: { show: false },
emphasis: { scale: false },
data: [
{ value: day.scheduled, name: '已排产', itemStyle: { color: colors.cyan } },
{ value: day.produced, name: '已生产', itemStyle: { color: colors.green } }
]
}
]
})
}
const resizeHandler = () => {
@ -129,7 +121,6 @@ const resizeHandler = () => {
onMounted(async () => {
await loadDayCapacity()
initChart()
chart?.setOption(buildOption(), true)
animateCount(dayOrderEl.value, day.orders, '', 900)
animateCount(dayPlanEl.value, day.scheduled, '', 900)
animateCount(dayPendingEl.value, day.produced, '', 900)
@ -137,13 +128,6 @@ onMounted(async () => {
window.addEventListener('resize', resizeHandler)
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
chart?.setOption(buildOption(), true)
}
)
onUnmounted(() => {
window.removeEventListener('resize', resizeHandler)
chart?.dispose()

@ -5,11 +5,11 @@
<span class="card-title-icon">
<Icon icon="fa-solid:bolt" />
</span>
<span>{{ t('EnergyTrend.title') }}</span>
<span>能耗周趋势</span>
</div>
<div class="card-toolbar">
<el-select
v-model="selectedEnergyTypeId" :placeholder="t('EnergyTrend.selectPlaceholder')" class="energy-type-select" size="small"
v-model="selectedEnergyTypeId" placeholder="请选择" class="energy-type-select" size="small"
@change="handleEnergyTypeChange">
<el-option v-for="item in energyTypes" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
@ -22,19 +22,15 @@ v-model="selectedEnergyTypeId" :placeholder="t('EnergyTrend.selectPlaceholder')"
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { onMounted, onUnmounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import * as echarts from 'echarts'
import { colors, style } from '../utils'
import { EnergyTypeApi, EnergyTypeVO } from '@/api/mes/energytype'
import { EnergyDeviceApi } from '@/api/mes/energydevice'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
const route = useRoute()
const orgId = route.query.orgId
const { t } = useI18n('Dashboard8')
const localeStore = useLocaleStore()
const energyTypes = ref<EnergyTypeVO[]>([])
const selectedEnergyTypeId = ref<number | undefined>(undefined)
@ -72,8 +68,24 @@ const handleEnergyTypeChange = () => {
getChartData()
}
const buildOption = (x: string[], actual: number[]) => {
return {
const render = (data: any = []) => {
if (!chart) return
const list = (data as any).data || (Array.isArray(data) ? data : [])
const x: string[] = []
const actual: number[] = []
const baseline: number[] = []
if (list && list.length > 0) {
list.forEach((item: any) => {
const label = item.hour || item.day || ''
x.push(label)
const v = Number(item.value ?? item.energy ?? item.consumption ?? 0)
actual.push(Number.isNaN(v) ? 0 : v)
})
}
chart.setOption({
backgroundColor: 'transparent',
tooltip: { trigger: 'axis' },
legend: { top: 0, right: 0, textStyle: style.legendText },
@ -87,7 +99,7 @@ const buildOption = (x: string[], actual: number[]) => {
yAxis: { type: 'value', axisLine: { show: false }, axisLabel: style.axisLabel, splitLine: style.splitLine },
series: [
{
name: t('EnergyTrend.seriesActualEnergy'),
name: '实际能耗(kWh)',
type: 'bar',
barWidth: 16,
itemStyle: {
@ -100,28 +112,9 @@ const buildOption = (x: string[], actual: number[]) => {
data: actual
}
]
}
}
const render = (data: any = []) => {
if (!chart) return
const list = (data as any).data || (Array.isArray(data) ? data : [])
const x: string[] = []
const actual: number[] = []
if (list && list.length > 0) {
list.forEach((item: any) => {
const label = item.hour || item.day || ''
x.push(label)
const v = Number(item.value ?? item.energy ?? item.consumption ?? 0)
actual.push(Number.isNaN(v) ? 0 : v)
})
}
chart.setOption(buildOption(x, actual))
}
const resize = () => {
chart?.resize()
}
@ -129,18 +122,11 @@ const resize = () => {
onMounted(async () => {
if (!chartRef.value) return
chart = echarts.init(chartRef.value, 'dark', { renderer: 'canvas' })
chart.setOption(buildOption([], []))
render([])
await getEnergyTypes()
window.addEventListener('resize', resize)
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
getChartData()
}
)
onUnmounted(() => {
window.removeEventListener('resize', resize)
chart?.dispose()

@ -5,9 +5,9 @@
<span class="card-title-icon">
<Icon icon="fa-solid:moon" />
</span>
<span>{{ t('MonthCapacity.title') }}</span>
<span>月产能达成情况</span>
</div>
<span class="tag">{{ t('MonthCapacity.tag') }}</span>
<span class="tag">当月累计</span>
</div>
<div class="card-body">
<div class="card-body-row">
@ -17,19 +17,19 @@
<div class="card-body-col">
<div class="metrics-grid">
<div class="metric-card">
<div class="metric-label">{{ t('MonthCapacity.metrics.orders') }}</div>
<div class="metric-label">排产单数量</div>
<div class="metric-value" ref="monthOrderEl"></div>
</div>
<div class="metric-card">
<div class="metric-label">{{ t('MonthCapacity.metrics.scheduled') }}</div>
<div class="metric-label">已排产数量</div>
<div class="metric-value" ref="monthPlanEl"></div>
</div>
<div class="metric-card">
<div class="metric-label">{{ t('MonthCapacity.metrics.produced') }}</div>
<div class="metric-label">已生产数量</div>
<div class="metric-value ok" ref="monthPendingEl"></div>
</div>
<div class="metric-card">
<div class="metric-label">{{ t('MonthCapacity.metrics.rate') }}</div>
<div class="metric-label">产能合格率</div>
<div class="metric-value accent" ref="monthRateEl"></div>
</div>
</div>
@ -40,18 +40,14 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import * as echarts from 'echarts'
import { colors, animateCount, style } from '../utils'
import { PlanApi } from '@/api/mes/plan'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
const route = useRoute()
const orgId = route.query.orgId
const { t } = useI18n('Dashboard8')
const localeStore = useLocaleStore()
type CapacityData = {
orders: number
@ -75,27 +71,6 @@ const monthRateEl = ref<HTMLElement | null>(null)
let chart: echarts.ECharts | null = null
const buildOption = () => {
return {
backgroundColor: 'transparent',
tooltip: { trigger: 'item' },
legend: { bottom: '0%', left: 'center', textStyle: style.legendText },
series: [
{
type: 'pie',
radius: ['60%', '82%'],
center: ['50%', '55%'],
label: { show: false },
emphasis: { scale: false },
data: [
{ value: month.scheduled, name: t('MonthCapacity.chart.scheduled'), itemStyle: { color: colors.cyan } },
{ value: month.produced, name: t('MonthCapacity.chart.produced'), itemStyle: { color: colors.green } }
]
}
]
}
}
const mapCapacity = (raw: any): CapacityData => {
const orderCount = Number(raw?.orders ?? raw?.order ?? 0)
const scheduled = Number(raw?.plan ?? 0)
@ -119,7 +94,24 @@ const loadMonthCapacity = async () => {
const initChart = () => {
if (!chartRef.value) return
chart = echarts.init(chartRef.value, 'dark', { renderer: 'canvas' })
chart.setOption(buildOption())
chart.setOption({
backgroundColor: 'transparent',
tooltip: { trigger: 'item' },
legend: { bottom: '0%', left: 'center', textStyle: style.legendText },
series: [
{
type: 'pie',
radius: ['60%', '82%'],
center: ['50%', '55%'],
label: { show: false },
emphasis: { scale: false },
data: [
{ value: month.scheduled, name: '已排产', itemStyle: { color: colors.cyan } },
{ value: month.produced, name: '已生产', itemStyle: { color: colors.green } }
]
}
]
})
}
const resizeHandler = () => {
@ -129,7 +121,6 @@ const resizeHandler = () => {
onMounted(async () => {
await loadMonthCapacity()
initChart()
chart?.setOption(buildOption(), true)
animateCount(monthOrderEl.value, month.orders, '', 900)
animateCount(monthPlanEl.value, month.scheduled, '', 900)
animateCount(monthPendingEl.value, month.produced, '', 900)
@ -137,13 +128,6 @@ onMounted(async () => {
window.addEventListener('resize', resizeHandler)
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
chart?.setOption(buildOption(), true)
}
)
onUnmounted(() => {
window.removeEventListener('resize', resizeHandler)
chart?.dispose()

@ -5,7 +5,7 @@
<span class="card-title-icon">
<Icon icon="fa-solid:wave-square" />
</span>
<span>{{ t('OpsTrend.title') }}</span>
<span>产能完成数</span>
</div>
<div class="ops-header-right">
<span class="tag">{{ summary }}</span>
@ -18,21 +18,17 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import * as echarts from 'echarts'
import { colors, style } from '../utils'
import { PlanApi } from '@/api/mes/plan'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
const route = useRoute()
const { t } = useI18n('Dashboard8')
const localeStore = useLocaleStore()
const opsDays: string[] = []
const opsLines: { name: string; value: number[] }[] = [
{ name: t('OpsTrend.defaultLine'), value: [] }
{ name: '全部产线', value: [] }
]
const currentLine = ref(opsLines[0].name)
@ -47,7 +43,7 @@ const avg = (arr: number[]) => {
const updateOps = (lineName: string) => {
currentLine.value = lineName
const item = opsLines.find((x) => x.name === lineName) || opsLines[0]
summary.value = t('OpsTrend.summary', { line: item.name, value: avg(item.value) })
summary.value = `${item.name} · 日均完成 ${avg(item.value)}`
if (!chart) return
chart.setOption({
@ -78,7 +74,7 @@ const loadOpsData = async () => {
opsDays.length = 0
opsDays.push(...days)
opsLines.length = 0
opsLines.push({ name: t('OpsTrend.defaultLine'), value: values })
opsLines.push({ name: '全部产线', value: values })
if (opsLines[0]) {
currentLine.value = opsLines[0].name
updateOps(currentLine.value)
@ -99,14 +95,14 @@ const initChart = async () => {
xAxis: { type: 'category', data: opsDays, axisLine: style.axisLine, axisLabel: style.axisLabel },
yAxis: {
type: 'value',
name: t('OpsTrend.yAxisName'),
name: '产能完成数',
nameTextStyle: { color: '#a8b7d8', fontSize: 12 },
axisLabel: style.axisLabel,
splitLine: style.splitLine
},
series: [
{
name: t('OpsTrend.seriesName'),
name: '产能完成数',
type: 'line',
smooth: true,
showSymbol: false,
@ -128,20 +124,6 @@ onMounted(() => {
window.addEventListener('resize', resizeHandler)
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
chart?.setOption(
{
yAxis: { name: t('OpsTrend.yAxisName') },
series: [{ name: t('OpsTrend.seriesName') }]
},
false
)
updateOps(currentLine.value)
}
)
onUnmounted(() => {
window.removeEventListener('resize', resizeHandler)
chart?.dispose()

@ -5,9 +5,9 @@
<span class="card-title-icon">
<Icon icon="fa-solid:check-double" />
</span>
<span>{{ t('QualityTrend.title') }}</span>
<span>成品检合格率趋势图</span>
</div>
<span class="tag">{{ t('QualityTrend.tag') }}</span>
<span class="tag">按天统计全产线</span>
</div>
<div class="card-body">
<div id="chart-quality" class="chart"></div>
@ -16,20 +16,16 @@
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue'
import { onMounted } from 'vue'
import { useRoute } from 'vue-router'
import * as echarts from 'echarts'
import { useChart, colors, style } from '../utils'
import { PlanApi } from '@/api/mes/plan'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
const route = useRoute()
const orgId = route.query.orgId
const { t } = useI18n('Dashboard8')
const localeStore = useLocaleStore()
const { init, instance } = useChart('chart-quality')
const { init } = useChart('chart-quality')
const last7Days: string[] = []
for (let i = 6; i >= 0; i--) {
@ -39,8 +35,6 @@ for (let i = 6; i >= 0; i--) {
}
const passRate = [98.5, 99.2, 97.8, 98.9, 99.0, 98.2, 99.5]
const xAxisDataRef = ref<string[]>(last7Days.slice())
const seriesDataRef = ref<number[]>(passRate.slice())
type QualityPoint = {
label: string
@ -77,12 +71,15 @@ const normalizeQualityData = (raw: any): QualityPoint[] => {
}
onMounted(async () => {
let xAxisData = last7Days.slice()
let seriesData = passRate.slice()
try {
const data = await PlanApi.getLastDaysRate({ orgId })
const list = normalizeQualityData(data)
if (list.length > 0) {
xAxisDataRef.value = list.map((item) => item.label)
seriesDataRef.value = list.map((item) => item.rate)
xAxisData = list.map((item) => item.label)
seriesData = list.map((item) => item.rate)
}
} catch {
}
@ -94,10 +91,10 @@ onMounted(async () => {
backgroundColor: 'transparent',
tooltip: { trigger: 'axis' },
grid: { top: '18%', left: '6%', right: '6%', bottom: '10%', containLabel: true },
xAxis: { type: 'category', data: xAxisDataRef.value, axisLine: style.axisLine, axisLabel: style.axisLabel },
xAxis: { type: 'category', data: xAxisData, axisLine: style.axisLine, axisLabel: style.axisLabel },
yAxis: { type: 'value', min: 0, max: 100, axisLine: { show: false }, axisLabel: style.axisLabel, splitLine: style.splitLine },
series: [{
name: t('QualityTrend.seriesName'),
name: '合格率',
type: 'bar',
barWidth: '45%',
itemStyle: {
@ -107,52 +104,16 @@ onMounted(async () => {
{ offset: 1, color: 'rgba(139,92,246,0.10)' }
])
},
data: seriesDataRef.value,
data: seriesData,
markLine: {
symbol: ['none', 'none'],
label: {
show: true,
color: '#fff',
fontSize: 12,
formatter: (p: any) => t('QualityTrend.markLineAverage', { value: p?.value ?? 0 })
},
label: { show: true, color: '#fff', fontSize: 12, formatter: '平均 {c}%' },
lineStyle: { color: colors.cyan, width: 2, type: 'dashed' },
data: [{ type: 'average', name: t('QualityTrend.markLineAverageName') }]
data: [{ type: 'average', name: '平均值' }]
}
}]
})
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
const chart = instance()
if (!chart) return
chart.setOption(
{
xAxis: { data: xAxisDataRef.value },
series: [
{
name: t('QualityTrend.seriesName'),
data: seriesDataRef.value,
markLine: {
symbol: ['none', 'none'],
label: {
show: true,
color: '#fff',
fontSize: 12,
formatter: (p: any) => t('QualityTrend.markLineAverage', { value: p?.value ?? 0 })
},
lineStyle: { color: colors.cyan, width: 2, type: 'dashed' },
data: [{ type: 'average', name: t('QualityTrend.markLineAverageName') }]
}
}
]
},
true
)
}
)
</script>
<style scoped>

@ -5,9 +5,9 @@
<span class="card-title-icon">
<Icon icon="fa-solid:bell" />
</span>
<span>{{ t('RealAlarm.title') }}</span>
<span>实时报警信息</span>
</div>
<span class="tag">{{ t('RealAlarm.tag') }}</span>
<span class="tag">滚动展示</span>
</div>
<div class="card-body">
<ul ref="listRef" class="alarm-list">
@ -28,39 +28,22 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import { DeviceWarningRecordApi, DeviceWarningRecordVO } from '@/api/iot/deviceWarningRecord'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
const route = useRoute()
const orgId = route.query.orgId
const { t } = useI18n('Dashboard8')
const localeStore = useLocaleStore()
const alarms = ref<any[]>([])
const isAnimating = ref(false)
let timer: ReturnType<typeof setInterval> | null = null
const mapAlarmLevel = (alarmLevel: unknown) => {
const raw = String(alarmLevel ?? '').trim()
if (raw === '2' || raw === '严重' || raw.toLowerCase() === 'severe') {
return { type: 'danger', levelText: t('RealAlarm.levelSevere') }
}
if (raw === '1' || raw === '警告' || raw.toLowerCase() === 'warning' || raw.toLowerCase() === 'warn') {
return { type: 'warn', levelText: t('RealAlarm.levelWarn') }
}
if (raw === '0' || raw === '提示' || raw.toLowerCase() === 'info' || raw.toLowerCase() === 'notice') {
return { type: 'safe', levelText: t('RealAlarm.levelInfo') }
}
return { type: 'safe', levelText: raw || t('RealAlarm.levelInfo') }
}
const getAlarms = async () => {
try {
const data = await DeviceWarningRecordApi.getList({ orgId }) || []
console.log('data',data)
alarms.value = data.map((item: DeviceWarningRecordVO) => {
//
@ -71,12 +54,29 @@ const getAlarms = async () => {
}
//
const mapped = mapAlarmLevel(item.alarmLevel)
let type = 'safe'
let levelText = item.alarmLevel
if (item.alarmLevel === '2') {
type = 'danger'
levelText = '严重'
} else if (item.alarmLevel === '1') {
type = 'warn'
levelText = '警告'
} else if (item.alarmLevel === '0') {
type = 'safe'
levelText = '提示'
} else {
//
if (item.alarmLevel === '严重') type = 'danger'
else if (item.alarmLevel === '警告') type = 'warn'
else if (item.alarmLevel === '提示') type = 'safe'
}
return {
time: timeStr,
level: mapped.levelText,
type: mapped.type,
level: levelText,
type: type,
msg: `${item.deviceName}-${item.ruleName}`
}
})
@ -107,13 +107,6 @@ onMounted(() => {
getAlarms()
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
getAlarms()
}
)
onUnmounted(() => {
if (timer) clearInterval(timer)
})

@ -5,12 +5,12 @@
<span class="card-title-icon">
<Icon icon="fa-solid:table-list" />
</span>
<span>{{ t('TaskBoard.title') }}</span>
<span>产线任务看板</span>
</div>
<div class="legend-inline">
<span class="tag">{{ t('TaskBoard.tag') }}</span>
<span class="dot" style="background: var(--green);"></span> {{ t('TaskBoard.statusCompleted') }}
<span class="dot" style="background: var(--warn);"></span> {{ t('TaskBoard.statusLowProgress') }}
<span class="tag">实时刷新 · 滚动展示</span>
<span class="dot" style="background: var(--green);"></span> 已完成
<span class="dot" style="background: var(--warn);"></span> 进度偏低
</div>
</div>
<div class="card-body">
@ -18,12 +18,12 @@
<table class="task-table">
<thead>
<tr>
<th>{{ t('TaskBoard.columns.lineName') }}</th>
<th>{{ t('TaskBoard.columns.planNo') }}</th>
<th>{{ t('TaskBoard.columns.productName') }}</th>
<th>{{ t('TaskBoard.columns.planQty') }}</th>
<th>{{ t('TaskBoard.columns.doneQty') }}</th>
<th>{{ t('TaskBoard.columns.passRate') }}</th>
<th>产线名称</th>
<th>计划单</th>
<th>产品名称</th>
<th>计划数量</th>
<th>完工数量</th>
<th>合格率</th>
</tr>
</thead>
<tbody ref="tbodyRef">
@ -53,11 +53,9 @@
import { ref, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import { PlanApi, PlanVO } from '@/api/mes/plan'
import { useI18n } from '@/hooks/web/useI18n'
const route = useRoute()
const orgId = route.query.orgId
const { t } = useI18n('Dashboard8')
const colors = {
blue: '#1e90ff',

@ -5,7 +5,7 @@
<span class="card-title-icon">
<Icon icon="fa-solid:gauge-high" />
</span>
<span>{{ t('TodayOps.title') }}</span>
<span>今日开机率/稼动率</span>
</div>
<span class="chip">{{ currentLineName }}</span>
</div>
@ -16,15 +16,11 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { useChart, colors } from '../utils'
import { DeviceOperationRecordApi, type DeviceOperationRecordVO } from '@/api/iot/deviceOperationRecord'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
const { init, instance } = useChart('chart-today')
const { t } = useI18n('Dashboard8')
const localeStore = useLocaleStore()
type LineMetric = { name: string; run: number; avail: number }
const lineMetrics = ref<LineMetric[]>([])
@ -98,7 +94,7 @@ const gaugeOption = (run: number, avail: number) => {
progress: { show: true, width: 8, itemStyle: { color: colors.blue } },
detail: { formatter: '{value}%', color: '#fff', fontSize: 26, offsetCenter: ['0%', '-5%'] },
title: { show: true, color: '#a8b7d8', fontSize: 12, offsetCenter: ['0%', '-25%'] },
data: [{ value: run, name: t('TodayOps.powerOnRate') }]
data: [{ value: run, name: '开机率' }]
},
{
type: 'gauge',
@ -116,7 +112,7 @@ const gaugeOption = (run: number, avail: number) => {
progress: { show: true, width: 8, itemStyle: { color: colors.green } },
detail: { formatter: '{value}%', color: '#fff', fontSize: 20, offsetCenter: ['0%', '45%'] },
title: { show: true, color: '#a8b7d8', fontSize: 12, offsetCenter: ['0%', '25%'] },
data: [{ value: avail, name: t('TodayOps.utilizationRate') }]
data: [{ value: avail, name: '稼动率' }]
}
]
}
@ -125,7 +121,7 @@ const gaugeOption = (run: number, avail: number) => {
const renderCurrent = () => {
const list = lineMetrics.value
if (!list.length) {
currentLineName.value = t('TodayOps.empty')
currentLineName.value = '暂无数据'
const c = instance()
if (c) c.setOption(gaugeOption(0, 0))
return
@ -174,13 +170,6 @@ onMounted(async () => {
startRotate()
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
renderCurrent()
}
)
onUnmounted(() => {
if (timer) clearInterval(timer)
})

@ -5,11 +5,11 @@
<span class="card-title-icon">
<Icon icon="fa-solid:chart-line" />
</span>
<span>{{ t('WeekTrend.title') }}</span>
<span>周生产趋势</span>
</div>
<div class="legend-inline">
<span class="dot" style="background: var(--accent);"></span> {{ t('WeekTrend.legendOutput') }}
<span class="dot" style="background: var(--purple);"></span> {{ t('WeekTrend.legendPlan') }}
<span class="dot" style="background: var(--accent);"></span> 产量
<span class="dot" style="background: var(--purple);"></span> 计划
</div>
</div>
<div class="card-body">
@ -19,18 +19,14 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import * as echarts from 'echarts'
import { colors, style } from '../utils'
import { PlanApi } from '@/api/mes/plan'
import { useI18n } from '@/hooks/web/useI18n'
import { useLocaleStore } from '@/store/modules/locale'
const route = useRoute()
const orgId = route.query.orgId
const { t } = useI18n('Dashboard8')
const localeStore = useLocaleStore()
const weekDays = ref()
const weekPlan = ref()
@ -39,8 +35,10 @@ const weekReal = ref()
const chartRef = ref<HTMLElement | null>(null)
let chart: echarts.ECharts | null = null
const buildOption = () => {
return {
const initChart = () => {
if (!chartRef.value) return
chart = echarts.init(chartRef.value, 'dark', { renderer: 'canvas' })
chart.setOption({
backgroundColor: 'transparent',
tooltip: { trigger: 'axis' },
legend: { top: 0, right: 0, textStyle: style.legendText },
@ -49,7 +47,7 @@ const buildOption = () => {
yAxis: { type: 'value', axisLine: style.axisLine, axisLabel: style.axisLabel, splitLine: style.splitLine },
series: [
{
name: t('WeekTrend.seriesPlanOutput'),
name: '计划产量',
type: 'line',
smooth: true,
showSymbol: false,
@ -57,7 +55,7 @@ const buildOption = () => {
data: weekPlan.value
},
{
name: t('WeekTrend.seriesActualOutput'),
name: '实际产量',
type: 'line',
smooth: true,
showSymbol: true,
@ -73,13 +71,7 @@ const buildOption = () => {
data: weekReal.value
}
]
}
}
const initChart = () => {
if (!chartRef.value) return
chart = echarts.init(chartRef.value, 'dark', { renderer: 'canvas' })
chart.setOption(buildOption())
})
}
const resizeHandler = () => {
@ -93,15 +85,9 @@ onMounted( async() => {
weekPlan.value = data.weekPlan
weekReal.value = data.weekReal
initChart()
// console.log(data)
})
watch(
() => localeStore.getCurrentLocale.lang,
() => {
chart?.setOption(buildOption(), true)
}
)
onUnmounted(() => {
window.removeEventListener('resize', resizeHandler)
chart?.dispose()

Loading…
Cancel
Save