黄伟杰 1 week ago
commit 0caaccfd71

@ -57,33 +57,39 @@ export interface DashboardTaskListResponse {
// 编码生成记录 API // 编码生成记录 API
export const DashboardApi = { export const DashboardApi = {
// 查询编码生成记录分页 // 查询编码生成记录分页
getProduction: async (params: any) => { getProduction: async (params: any) => {
return await request.get({ url: `/mes/dashboard/getProduction`, params }) return await request.get({ url: `/mes/dashboard/getProduction`, params })
}, },
getPlan: async () => { getPlan: async () => {
return await request.get({ url: `/mes/dashboard/getPlan` }) return await request.get({ url: `/mes/dashboard/getPlan` })
}, },
getDevice: async () => { getDevice: async () => {
return await request.get({ url: `/mes/dashboard/getDevice` }) return await request.get({ url: `/mes/dashboard/getDevice` })
}, },
getMold: async () => { getMold: async () => {
return await request.get({ url: `/mes/dashboard/getMold` }) return await request.get({ url: `/mes/dashboard/getMold` })
}, },
getTodoList: async () => { getTodoList: async () => {
return await request.get({ url: `/mes/dashboard/getTodoList` }) return await request.get({ url: `/mes/dashboard/getTodoList` })
}, },
getDeviceOperationalStatus: async () => { getDeviceOperationalStatus: async () => {
return await request.get({ url: `/iot/device/getDeviceOperationalStatus` }) return await request.get({ url: `/iot/device/getDeviceOperationalStatus` })
}, },
getDeviceRepairLineOptions: async () => { getDeviceRepairLineOptions: async () => {
return await request.get({ url: `/mes/dashboard/getDeviceRepairLineOptions` }) return await request.get({ url: `/mes/dashboard/getDeviceRepairLineOptions` })
}, },
getTaskStatistics: async () => { getDeviceTypePieOptions: async () => {
return await request.get<TaskStatisticsResponse>({ url: `/mes/dashboard/getTaskStatistics` }) return await request.get({ url: `/mes/dashboard/getDeviceTypePieOptions` })
}, },
getAllTaskList: async () => { getMoldTypeBarOptions: async () => {
return await request.get<DashboardTaskListResponse>({ url: `/mes/dashboard/getAllTaskList` }) return await request.get({ url: `/mes/dashboard/getMoldTypeBarOptions` })
} },
getTaskStatistics: async () => {
return await request.get<TaskStatisticsResponse>({ url: `/mes/dashboard/getTaskStatistics` })
},
getAllTaskList: async () => {
return await request.get<DashboardTaskListResponse>({ url: `/mes/dashboard/getAllTaskList` })
}
} }

@ -22,7 +22,10 @@ export interface PlanVO {
productionManager: number // 生产主管 productionManager: number // 生产主管
remark: string // 备注 remark: string // 备注
isEnable: boolean // 是否启用 isEnable: boolean // 是否启用
feedingPipeline: string //制浆线 feedingPipeline: number //制浆线
feedingPipelineName: string
wangongNumber: number
passRate: number
} }
// 生产计划 API // 生产计划 API
@ -93,4 +96,12 @@ export const PlanApi = {
getPlanByTicketType: async (status: number) => { getPlanByTicketType: async (status: number) => {
return await request.get({ url: `/mes/plan/getByTicketType?status=` + status }) return await request.get({ url: `/mes/plan/getByTicketType?status=` + status })
}, },
// 产线任务看板
getProductPlans: async () => {
return await request.get({ url: `/mes/plan/getProductPlans` })
},
// 周生产趋势
getWeekTrend: async () => {
return await request.get({ url: `/mes/plan/getWeekTrend` })
}
} }

@ -220,7 +220,7 @@ v-model="deviceOverviewRange" type="daterange" unlink-panels value-format="YYYY-
</el-row> </el-row>
</el-card> </el-card>
<el-row :gutter="16" class="home-section"> <el-row :gutter="16" class="home-section">
<el-col :xl="12" :lg="8" :md="24" :sm="24" :xs="24" class="mb-16px"> <el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-16px">
<el-card shadow="never"> <el-card shadow="never">
<template #header> <template #header>
<div class="h-3 flex justify-between"> <div class="h-3 flex justify-between">
@ -244,10 +244,10 @@ v-model="deviceOverviewRange" type="daterange" unlink-panels value-format="YYYY-
<el-card shadow="never"> <el-card shadow="never">
<template #header> <template #header>
<div class="h-3 flex justify-between"> <div class="h-3 flex justify-between">
<span>设备分布统计</span> <span>模具分类统计</span>
</div> </div>
</template> </template>
<Echart :options="deviceDistributionBarOptionsData" :height="260" /> <Echart :options="moldTypeBarOptionsData" :height="260" />
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
@ -318,7 +318,6 @@ v-model="deviceOverviewRange" type="daterange" unlink-panels value-format="YYYY-
<script lang="ts" setup> <script lang="ts" setup>
import { set } from 'lodash-es' import { set } from 'lodash-es'
import { EChartsOption } from 'echarts' import { EChartsOption } from 'echarts'
import { formatTime } from '@/utils'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import { useWatermark } from '@/hooks/web/useWatermark' import { useWatermark } from '@/hooks/web/useWatermark'
import type { import type {
@ -326,20 +325,15 @@ import type {
Project, Project,
Notice, Notice,
Shortcut, Shortcut,
ProductionOverview,
ProductionProgressItem, ProductionProgressItem,
DeviceOverview,
TodoTask, TodoTask,
DeviceOperationStatus DeviceOperationStatus
} from './types' } from './types'
import { import {
pieOptions, pieOptions,
barOptions, barOptions,
deviceCategoryPieOptions,
deviceDistributionBarOptions,
deviceOverviewMock,
} from './echarts-data' } from './echarts-data'
import { HomeApi } from '@/api/home/info'
import { WeatherVO } from '@/api/home/info' import { WeatherVO } from '@/api/home/info'
import bannerImg from '@/assets/imgs/banner.png' import bannerImg from '@/assets/imgs/banner.png'
import { DashboardApi,DashboardProductVO,DeviceStatusVO } from '@/api/dashboard' import { DashboardApi,DashboardProductVO,DeviceStatusVO } from '@/api/dashboard'
@ -352,21 +346,10 @@ const { setWatermark } = useWatermark()
const loading = ref(true) const loading = ref(true)
const avatar = userStore.getUser.avatar const avatar = userStore.getUser.avatar
const username = userStore.getUser.nickname const username = userStore.getUser.nickname
const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
const deviceCategoryPieOptionsData = reactive<EChartsOption>(
deviceCategoryPieOptions
) as EChartsOption
const deviceDistributionBarOptionsData = reactive<EChartsOption>(
deviceDistributionBarOptions
) as EChartsOption
const productionOverviewRange = ref<string[]>([]) const productionOverviewRange = ref<string[]>([])
const deviceOverview = ref<DeviceOverview>(deviceOverviewMock)
const formatPercent = (value: number | undefined | null) => { const formatPercent = (value: number | undefined | null) => {
if (value === null || value === undefined) return '0%' if (value === null || value === undefined) return '0%'
@ -374,87 +357,14 @@ const formatPercent = (value: number | undefined | null) => {
return `${value}%` return `${value}%`
} }
const productionOverviewLeft = ref([]) const productionOverviewLeft = ref<{ key: string; label: string; value: number; }[]>([])
const productionOverviewCenter = ref([]) const productionOverviewCenter = ref<{ key: string; label: string; value: number; }[]>([])
const plan = ref<ProductionProgressItem[]>([]) const plan = ref<ProductionProgressItem[]>([])
const todo = ref<TodoTask[]>([]) const todo = ref<TodoTask[]>([])
// const productionOverviewRight = computed(() => [
// {
// key: 'attendanceCount',
// label: '',
// value: productionOverview.value.onTimeRate ?? 0
// },
// {
// key: 'attendanceRate',
// label: '',
// value: formatPercent(productionOverview.value.onTimeRate)
// }
// ])
const deviceOverviewTop = computed(() => [
{
key: 'deviceTotal',
label: '总设备数',
value: deviceOverview.value.deviceTotal?.toLocaleString()
},
{
key: 'realtimeAlarm',
label: '实时告警',
value: deviceOverview.value.needRepair ?? 0
},
{
key: 'running',
label: '运行',
value: deviceOverview.value.running ?? 0
},
{
key: 'stop',
label: '停机',
value: deviceOverview.value.stop ?? 0
},
{
key: 'standby',
label: '待机',
value: 0
},
{
key: 'utilization',
label: '利用率',
value: formatPercent(deviceOverview.value.utilization)
},
{
key: 'faultRate',
label: '故障率',
value: formatPercent(deviceOverview.value.alarmRate)
}
])
const deviceOverviewBottom = computed(() => [
{
key: 'faultCount',
label: '设备故障台数',
value: deviceOverview.value.needRepair ?? 0
},
{
key: 'faultTime',
label: '设备故障时间',
value: 0
},
{
key: 'mtbf',
label: '平均故障间隔时间',
value: 0
},
{
key: 'mttr',
label: '平均故障时间',
value: 0
}
])
const groupBySize = <T,>(list: T[], size: number) => { const groupBySize = <T,>(list: T[], size: number) => {
const result: T[][] = [] const result: T[][] = []
@ -492,15 +402,15 @@ const deviceRepairLineOptions = ref<EChartsOption>({
}, },
grid: { grid: {
left: 40, left: 40,
right: 20, right: 30,
bottom: 30, bottom: 50,
top: 60, top: 60,
containLabel: true containLabel: true
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
data: ['一月', '二月', '三月', '四月', '五月', '六月'] data: []
}, },
yAxis: { yAxis: {
type: 'value' type: 'value'
@ -510,29 +420,96 @@ const deviceRepairLineOptions = ref<EChartsOption>({
name: '维修数量', name: '维修数量',
type: 'line', type: 'line',
smooth: true, smooth: true,
data: [3, 5, 4, 6, 8, 7] // data: [3, 5, 4, 6, 8, 7]
} }
] ]
}) })
const deviceCategoryPieOptionsData = ref<EChartsOption>(
{title: {
text: '设备分类统计',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '设备类别',
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
}
]
}
)
const moldTypeBarOptionsData = ref<EChartsOption>({
title: {
text: '模具分类统计',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: 40,
right: 20,
bottom: 30,
top: 60,
containLabel: true
},
xAxis: {
type: 'category',
// data: ['', '', '', '', ''],
axisTick: {
alignWithLabel: true
}
},
yAxis: {
type: 'value'
},
series: [
{
name: '模具数量',
type: 'bar',
// data: [10, 8, 6, 5, 4]
}
]
}
)
const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
/** 初始化 **/ /** 初始化 **/
onMounted(async () => { onMounted(async () => {
// //
const data = await HomeApi.getWeatherInfo() // const data = await HomeApi.getWeatherInfo()
weatherEnable.value = data.isEnable // weatherEnable.value = data.isEnable
if (data.isEnable && weatherList) { // if (data.isEnable && weatherList) {
weatherList.value = data.casts // weatherList.value = data.casts
todayWeather.value = weatherList.value[0] // todayWeather.value = weatherList.value[0]
weatherCity = data.city // weatherCity = data.city
} // }
production.value = await DashboardApi.getProduction(productionOverviewRange.value) production.value = await DashboardApi.getProduction(productionOverviewRange.value)
plan.value = await DashboardApi.getPlan() plan.value = await DashboardApi.getPlan()
deviceStatusCards.value = await DashboardApi.getDevice() deviceStatusCards.value = await DashboardApi.getDevice()
moldStatusCards.value = await DashboardApi.getMold() moldStatusCards.value = await DashboardApi.getMold()
todo.value = await DashboardApi.getTodoList() todo.value = await DashboardApi.getTodoList()
formData.value = await DashboardApi.getDeviceOperationalStatus() formData.value = await DashboardApi.getDeviceOperationalStatus()
const Options = await DashboardApi.getDeviceRepairLineOptions() const Options = await DashboardApi.getDeviceRepairLineOptions()
deviceRepairLineOptions.value.xAxis.data = Options.xaxis deviceRepairLineOptions.value.xAxis!.data = Options.xaxis
deviceRepairLineOptions.value.series[0].data = Options.series deviceRepairLineOptions.value.series![0].data = Options.series
const deviceTypeOptions = await DashboardApi.getDeviceTypePieOptions()
deviceCategoryPieOptionsData.value.series![0].data = deviceTypeOptions
const moldTypeOptions = await DashboardApi.getMoldTypeBarOptions()
console.log(moldTypeOptions)
moldTypeBarOptionsData.value.xAxis!.data = moldTypeOptions.xaxis
moldTypeBarOptionsData.value.series![0].data = moldTypeOptions.series
productionOverviewLeft.value = production.value.taskItems productionOverviewLeft.value = production.value.taskItems
productionOverviewCenter.value = production.value.planItems productionOverviewCenter.value = production.value.planItems

@ -19,10 +19,10 @@
<Icon icon="fa-regular:clock" class="chip-icon" /> <Icon icon="fa-regular:clock" class="chip-icon" />
<span>{{ timeStr }}</span> <span>{{ timeStr }}</span>
</span> </span>
<span class="chip chip-strong"> <!-- <span class="chip chip-strong">
<Icon icon="fa-solid:circle-nodes" class="chip-icon" /> <Icon icon="fa-solid:circle-nodes" class="chip-icon" />
<span>当前在线产线 <strong>10</strong> </span> <span>当前在线产线 <strong>10</strong> </span>
</span> </span> -->
</div> </div>
</div> </div>
</header> </header>

@ -19,22 +19,22 @@
<div class="metric-card"> <div class="metric-card">
<div class="metric-label">排产单数量</div> <div class="metric-label">排产单数量</div>
<div class="metric-value" ref="dayOrderEl"></div> <div class="metric-value" ref="dayOrderEl"></div>
<div class="metric-extra"><span></span><span>含加急 4 </span></div> <!-- <div class="metric-extra"><span></span><span>含加急 4 </span></div> -->
</div> </div>
<div class="metric-card"> <div class="metric-card">
<div class="metric-label">排产数量</div> <div class="metric-label">排产数量</div>
<div class="metric-value" ref="dayPlanEl"></div> <div class="metric-value" ref="dayPlanEl"></div>
<div class="metric-extra"><span></span><span>含三班倒</span></div> <!-- <div class="metric-extra"><span></span><span>含三班倒</span></div> -->
</div> </div>
<div class="metric-card"> <div class="metric-card">
<div class="metric-label">待生产数量</div> <div class="metric-label">待生产数量</div>
<div class="metric-value warn" ref="dayPendingEl"></div> <div class="metric-value warn" ref="dayPendingEl"></div>
<div class="metric-extra"><span></span><span>预计 4 小时内完成</span></div> <!-- <div class="metric-extra"><span></span><span>预计 4 小时内完成</span></div> -->
</div> </div>
<div class="metric-card"> <div class="metric-card">
<div class="metric-label">产能达成率</div> <div class="metric-label">产能达成率</div>
<div class="metric-value accent" ref="dayRateEl"></div> <div class="metric-value accent" ref="dayRateEl"></div>
<div class="metric-extra"><span>目标 80%</span><span>状态良好</span></div> <!-- <div class="metric-extra"><span>目标 80%</span><span>状态良好</span></div> -->
</div> </div>
</div> </div>
</div> </div>
@ -74,7 +74,7 @@ const initChart = () => {
emphasis: { scale: false }, emphasis: { scale: false },
data: [ data: [
{ value: day.plan - day.pending, name: '已排产', itemStyle: { color: colors.cyan } }, { value: day.plan - day.pending, name: '已排产', itemStyle: { color: colors.cyan } },
{ value: day.pending, name: '待生产', itemStyle: { color: 'rgba(15,23,42,0.8)' } } { value: day.pending, name: '已生产', itemStyle: { color: colors.green } }
] ]
} }
] ]

@ -19,26 +19,26 @@
<thead> <thead>
<tr> <tr>
<th>产线名称</th> <th>产线名称</th>
<th>排产</th> <th>计划</th>
<th>产品名称</th> <th>产品名称</th>
<th>计划数量</th> <th>计划数量</th>
<th>完工数量</th> <th>完工数量</th>
<th>完工</th> <th>合格</th>
</tr> </tr>
</thead> </thead>
<tbody ref="tbodyRef"> <tbody ref="tbodyRef">
<tr v-for="(r, index) in tasks" :key="index"> <tr v-for="(r, index) in tasks" :key="index">
<td>{{ r.line }}</td> <td>{{ r.feedingPipelineName }}</td>
<td :style="{ color: colors.cyan }">{{ r.order }}</td> <td :style="{ color: colors.cyan }">{{ r.code }}</td>
<td :title="r.product">{{ r.product }}</td> <td :title="r.productName">{{ r.productName }}</td>
<td>{{ r.plan }}</td> <td>{{ r.planNumber }}</td>
<td>{{ r.done }}</td> <td>{{ r.wangongNumber }}</td>
<td> <td>
<div style="display:flex; flex-direction:column; gap:4px; align-items:center;"> <div style="display:flex; flex-direction:column; gap:4px; align-items:center;">
<div class="progress-bar"> <div class="progress-bar">
<div class="progress-fill" :style="{ width: getRate(r) + '%', background: getRateColor(r) }"></div> <div class="progress-fill" :style="{ width: getRate(r) + '%', background: getRateColor(r) }"></div>
</div> </div>
<span :style="{ color: getRateColor(r), fontWeight: 700 }">{{ getRate(r) }}%</span> <span :style="{ color: getRateColor(r), fontWeight: 700 }">{{ r.passRate }}%</span>
</div> </div>
</td> </td>
</tr> </tr>
@ -51,6 +51,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue' import { ref, onMounted, onUnmounted } from 'vue'
import { PlanApi, PlanVO } from '@/api/mes/plan'
const colors = { const colors = {
blue: '#1e90ff', blue: '#1e90ff',
@ -61,18 +62,7 @@ const colors = {
danger: '#ef4444' danger: '#ef4444'
} }
const tasks = [ const tasks = ref<PlanVO[]>([])
{ line: '产线A', order: 'ORD-2025121701', product: '智能模组X1', plan: 1200, done: 1080 },
{ line: '产线B', order: 'ORD-2025121702', product: '高密电芯M2', plan: 1500, done: 1120 },
{ line: '产线C', order: 'ORD-2025121703', product: '组装组件A5', plan: 800, done: 760 },
{ line: '产线D', order: 'ORD-2025121704', product: '精密外壳S7', plan: 600, done: 480 },
{ line: '产线E', order: 'ORD-2025121705', product: '控制板K9', plan: 900, done: 855 },
{ line: '产线F', order: 'ORD-2025121706', product: '电源模组P3', plan: 1100, done: 990 },
{ line: '产线G', order: 'ORD-2025121707', product: '线束套件W4', plan: 700, done: 560 },
{ line: '产线H', order: 'ORD-2025121708', product: '散热组件H2', plan: 650, done: 559 },
{ line: '产线I', order: 'ORD-2025121709', product: '传感器C8', plan: 500, done: 450 },
{ line: '产线J', order: 'ORD-2025121710', product: '整机Z3', plan: 1000, done: 800 }
]
const getRate = (r: any) => Math.round((r.done / r.plan) * 100) const getRate = (r: any) => Math.round((r.done / r.plan) * 100)
@ -115,10 +105,12 @@ const startScroll = () => {
}, 5000) }, 5000)
} }
onMounted(() => { onMounted(async () => {
setBodyHeight() setBodyHeight()
startScroll() startScroll()
window.addEventListener('resize', setBodyHeight) window.addEventListener('resize', setBodyHeight)
tasks.value = await PlanApi.getProductPlans()
// console.log(tasks.value)
}) })
onUnmounted(() => { onUnmounted(() => {

@ -22,10 +22,11 @@
import { ref, onMounted, onUnmounted } from 'vue' import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { colors, style } from '../utils' import { colors, style } from '../utils'
import { PlanApi } from '@/api/mes/plan'
const weekDays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] const weekDays = ref()
const weekPlan = [2600, 2800, 3000, 3100, 3200, 2900, 2700] const weekPlan = ref()
const weekReal = [2550, 2750, 2950, 3050, 3180, 2850, 2680] const weekReal = ref()
const chartRef = ref<HTMLElement | null>(null) const chartRef = ref<HTMLElement | null>(null)
let chart: echarts.ECharts | null = null let chart: echarts.ECharts | null = null
@ -38,7 +39,7 @@ const initChart = () => {
tooltip: { trigger: 'axis' }, tooltip: { trigger: 'axis' },
legend: { top: 0, right: 0, textStyle: style.legendText }, legend: { top: 0, right: 0, textStyle: style.legendText },
grid: { top: '20%', left: '6%', right: '6%', bottom: '10%', containLabel: true }, grid: { top: '20%', left: '6%', right: '6%', bottom: '10%', containLabel: true },
xAxis: { type: 'category', data: weekDays, axisLine: style.axisLine, axisLabel: style.axisLabel }, xAxis: { type: 'category', data: weekDays.value, axisLine: style.axisLine, axisLabel: style.axisLabel },
yAxis: { type: 'value', axisLine: style.axisLine, axisLabel: style.axisLabel, splitLine: style.splitLine }, yAxis: { type: 'value', axisLine: style.axisLine, axisLabel: style.axisLabel, splitLine: style.splitLine },
series: [ series: [
{ {
@ -47,7 +48,7 @@ const initChart = () => {
smooth: true, smooth: true,
showSymbol: false, showSymbol: false,
lineStyle: { width: 2, color: colors.purple }, lineStyle: { width: 2, color: colors.purple },
data: weekPlan data: weekPlan.value
}, },
{ {
name: '实际产量', name: '实际产量',
@ -63,7 +64,7 @@ const initChart = () => {
{ offset: 1, color: 'rgba(15,23,42,0.8)' } { offset: 1, color: 'rgba(15,23,42,0.8)' }
]) ])
}, },
data: weekReal data: weekReal.value
} }
] ]
}) })
@ -73,9 +74,14 @@ const resizeHandler = () => {
chart?.resize() chart?.resize()
} }
onMounted(() => { onMounted( async() => {
initChart()
window.addEventListener('resize', resizeHandler) window.addEventListener('resize', resizeHandler)
const data = await PlanApi.getWeekTrend()
weekDays.value = data.weekDays
weekPlan.value = data.weekPlan
weekReal.value = data.weekReal
initChart()
// console.log(data)
}) })
onUnmounted(() => { onUnmounted(() => {

Loading…
Cancel
Save