Merge remote-tracking branch 'origin/main'

main
黄伟杰 5 days ago
commit 943a2c5a3a

@ -179,8 +179,8 @@ export default {
},
Dashboard3: {
Header: {
title: '设备运行总览大屏标题',
subTitle: 'IOT 实时监控中心副标题',
title: '制品车间设备运行总览',
subTitle: 'IOT 实时监控中心',
currentTime: '当前时间:',
lastUpdate: '最后更新:',
refreshRate: '刷新频率:',

@ -71,6 +71,25 @@
:label="unit.name"
:value="unit.id"
/>
<template #footer>
<div v-if="!isAddingUnit" class="px-10px py-6px">
<el-button text bg size="small" @click.stop="onAddUnit('unit')">
+ 新增单位
</el-button>
</div>
<div v-else class="px-10px py-6px">
<el-input
v-model="unitInputName"
size="small"
placeholder="请输入单位名称"
class="mb-6px"
/>
<div class="flex gap-6px">
<el-button type="primary" size="small" @click.stop="onConfirmAddUnit('unit')">确定</el-button>
<el-button size="small" @click.stop="clearAddUnit()">取消</el-button>
</div>
</div>
</template>
</el-select>
</el-form-item>
</el-col>
@ -83,6 +102,25 @@
:label="unit.name"
:value="unit.id"
/>
<template #footer>
<div v-if="!isAddingPurchaseUnit" class="px-10px py-6px">
<el-button text bg size="small" @click.stop="onAddUnit('purchaseUnit')">
+ 新增单位
</el-button>
</div>
<div v-else class="px-10px py-6px">
<el-input
v-model="unitInputName"
size="small"
placeholder="请输入单位名称"
class="mb-6px"
/>
<div class="flex gap-6px">
<el-button type="primary" size="small" @click.stop="onConfirmAddUnit('purchaseUnit')">确定</el-button>
<el-button size="small" @click.stop="clearAddUnit()">取消</el-button>
</div>
</div>
</template>
</el-select>
</el-form-item>
</el-col>
@ -298,6 +336,7 @@
<el-form-item>
<el-button type="primary" @click="handlePackagingSchemeSearch">{{ t('FactoryModeling.ProductInformation.searchButtonText') }}</el-button>
<el-button @click="resetPackagingSchemeSearch">{{ t('FactoryModeling.ProductInformation.resetButtonText') }}</el-button>
<el-button type="primary" plain @click="openPackagingSchemeAdd">{{ t('action.create') }}</el-button>
</el-form-item>
</el-form>
<el-table
@ -392,6 +431,7 @@
<el-button @click="supplierDialogVisible = false">{{ t('common.cancel') }}</el-button>
</template>
</Dialog>
<PackagingSolutionForm ref="packagingSolutionFormRef" @success="handlePackagingSchemeSuccess" />
</template>
<script setup lang="ts">
import { ProductApi, ProductVO } from '@/api/erp/product/product'
@ -402,6 +442,7 @@ import TableSelectDialog from '@/components/TableSelectDialog/TableSelectDialog.
import { DeviceLedgerApi } from '@/api/mes/deviceledger'
import { MoldBrandApi } from '@/api/erp/mold'
import { PackagingSchemeApi } from '@/api/erp/stock/packagingScheme'
import PackagingSolutionForm from '@/views/erp/stock/packagingSolution/PackagingSolutionForm.vue'
import { SupplierApi } from '@/api/erp/purchase/supplier'
import { CommonStatusEnum } from '@/utils/constants'
import { defaultProps, handleTree } from '@/utils/tree'
@ -805,6 +846,19 @@ const resetPackagingSchemeSearch = () => {
searchPackagingSchemeFormRef.value?.resetFields()
handlePackagingSchemeSearch()
}
const packagingSolutionFormRef = ref()
const openPackagingSchemeAdd = () => {
packagingSolutionFormRef.value?.open('create')
}
const handlePackagingSchemeSuccess = () => {
//
getPackagingSchemeDialogList()
PackagingSchemeApi.getPackagingSchemePage({ pageNo: 1, pageSize: 100, status: 0 }).then((data) => {
packagingSchemeList.value = data.list || []
})
}
//
const supplierDialogVisible = ref(false)
const supplierDialogLoading = ref(false)
@ -1045,6 +1099,56 @@ const handlePurchaseUnitChange = (val: any) => {
formData.value.purchaseUnitName = unit?.name ?? undefined
}
const isAddingUnit = ref(false)
const isAddingPurchaseUnit = ref(false)
const unitInputName = ref('')
const onAddUnit = (type: 'unit' | 'purchaseUnit') => {
if (type === 'unit') {
isAddingUnit.value = true
} else {
isAddingPurchaseUnit.value = true
}
}
const onConfirmAddUnit = async (type: 'unit' | 'purchaseUnit') => {
if (!unitInputName.value.trim()) {
message.warning('请输入单位名称')
return
}
formLoading.value = true
try {
await ProductUnitApi.createProductUnit({
name: unitInputName.value.trim(),
status: CommonStatusEnum.ENABLE,
primaryFlag: 'Y'
} as ProductUnitVO)
//
const newList = await ProductUnitApi.getProductUnitSimpleList()
unitList.value = newList
//
const newUnit = newList.find((u: any) => u.name === unitInputName.value.trim())
if (newUnit) {
if (type === 'unit') {
formData.value.unitId = newUnit.id
} else {
formData.value.purchaseUnitId = newUnit.id
handlePurchaseUnitChange(newUnit.id)
}
}
clearAddUnit()
message.success('新增单位成功')
} finally {
formLoading.value = false
}
}
const clearAddUnit = () => {
unitInputName.value = ''
isAddingUnit.value = false
isAddingPurchaseUnit.value = false
}
const getQrcodeRefreshUrl = () => {
if (!formData.value.id || !formData.value.barCode) return ''
return `/erp/product/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.barCode))}`

@ -962,7 +962,7 @@ const deviceStatisticsItems = computed(() => [
{ key: 'total', label: '总数', value: deviceStatistics.total },
{ key: 'running', label: '运行数', value: deviceStatistics.running },
{ key: 'standby', label: '待机数', value: deviceStatistics.standby },
{ key: 'fault', label: '故障数', value: deviceStatistics.fault },
/* { key: 'fault', label: '故障数', value: deviceStatistics.fault },*/
{ key: 'alarm', label: '报警数', value: deviceStatistics.alarm },
{ key: 'offline', label: '离线数', value: deviceStatistics.offline }
])
@ -971,7 +971,7 @@ const normalizeDeviceOperatingStatus = (value: string | number | undefined): Dev
const text = String(value ?? '').trim().toLowerCase()
if (['运行', '运行中', 'running', 'run', '1'].includes(text)) return 'running'
if (['待机', '待机中', 'standby', 'idle', '2'].includes(text)) return 'standby'
if (['故障', '故障中', 'fault', 'error', '3'].includes(text)) return 'fault'
/*if (['故障', '故障中', 'fault', 'error', '3'].includes(text)) return 'fault'*/
if (['报警', '报警中', 'alarm', 'warning', '4', '5'].includes(text)) return 'alarm'
return 'offline'
}
@ -2047,7 +2047,7 @@ const handleShowDeviceAlarmHistory = async () => {
<style lang="scss" scoped>
.device-statistics {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 10px;
margin-bottom: 16px;
}
@ -2086,7 +2086,7 @@ const handleShowDeviceAlarmHistory = async () => {
}
.device-statistics__item--alarm .device-statistics__value {
color: #e6a23c;
color: #f56c6c;
}
.device-statistics__item--offline .device-statistics__value,

@ -5,7 +5,8 @@
<header class="dashboard-header">
<div class="brand">
<div class="brand-icon">
<Icon icon="ep:cpu" />
<!-- <Icon icon="ep:cpu" />-->
<img alt="" class="mr-10px h-40px w-40px" src="@/assets/imgs/logo.png" />
</div>
<div class="brand-text">{{ t('Header.subTitle') }}</div>
</div>
@ -189,30 +190,25 @@ const getMetricValue = (keys: string[]) => {
return foundKey ? metricValueMap.value[foundKey] : undefined
}
const summaryMap = computed(() => {
return overviewData.value.summary.reduce<Record<string, number>>((acc, item) => {
acc[item.status] = Number(item.percent) || 0
return acc
}, {})
})
const calcDeviceRate = (count: number, total: number) => {
if (!total) return 0
return Number(((count / total) * 100).toFixed(1))
}
const statusCards = computed(() => [
{
key: 'total',
label: t('Metrics.total'),
icon: 'ep:connection',
value:
overviewData.value.totalDevices ||
getMetricValue(['total', 'deviceTotal', 'totalDevices']) ||
overviewData.value.timelineRows.length,
value: totalDeviceCount.value,
tone: 'total'
},
{
key: 'online',
label: t('Metrics.online'),
icon: 'ep:cpu',
value: Math.max(0, totalDeviceCount.value - offlineDeviceCount.value),
percent: 100 - (summaryMap.value.offline || 0),
value: onlineDeviceCount.value,
percent: calcDeviceRate(onlineDeviceCount.value, totalDeviceCount.value),
tone: 'online'
},
{
@ -220,31 +216,31 @@ const statusCards = computed(() => [
label: t('Metrics.offline'),
icon: 'ep:link',
value: offlineDeviceCount.value,
percent: summaryMap.value.offline || 0,
percent: calcDeviceRate(offlineDeviceCount.value, totalDeviceCount.value),
tone: 'offline'
},
{
key: 'running',
label: t('Metrics.running'),
icon: 'ep:video-play',
value: getMetricValue(['running', 'run', 'runningDevices']) ?? getRowsByStatus('running').length,
percent: summaryMap.value.running || 0,
value: runningDeviceCount.value,
percent: calcDeviceRate(runningDeviceCount.value, totalDeviceCount.value),
tone: 'running'
},
{
key: 'standby',
label: t('Metrics.standby'),
icon: 'ep:video-pause',
value: getMetricValue(['standby', 'idle', 'standbyDevices']) ?? getRowsByStatus('standby').length,
percent: summaryMap.value.standby || 0,
value: standbyDeviceCount.value,
percent: calcDeviceRate(standbyDeviceCount.value, totalDeviceCount.value),
tone: 'standby'
},
{
key: 'fault',
label: t('Metrics.fault'),
icon: 'ep:bell',
value: getMetricValue(['fault', 'alarm', 'faultDevices']) ?? getRowsByStatus('fault').length,
percent: summaryMap.value.fault || 0,
value: faultDeviceCount.value,
percent: calcDeviceRate(faultDeviceCount.value, totalDeviceCount.value),
tone: 'fault'
}
])
@ -267,15 +263,22 @@ const detailRows = computed<DetailRow[]>(() => {
})
const totalDetailPages = computed(() => Math.max(1, Math.ceil(detailRows.value.length / detailPageSize)))
const totalDeviceCount = computed(
() =>
overviewData.value.totalDevices ||
getMetricValue(['total', 'deviceTotal', 'totalDevices']) ||
detailRows.value.length
const runningDeviceCount = computed(
() => getMetricValue(['running', 'run', 'runningDevices']) ?? getRowsByStatus('running').length
)
const standbyDeviceCount = computed(
() => getMetricValue(['standby', 'idle', 'standbyDevices']) ?? getRowsByStatus('standby').length
)
const faultDeviceCount = computed(
() => getMetricValue(['fault', 'alarm', 'faultDevices']) ?? getRowsByStatus('fault').length
)
const offlineDeviceCount = computed(
() => getMetricValue(['offline', 'offlineDevices']) ?? getRowsByStatus('offline').length
)
const onlineDeviceCount = computed(
() => runningDeviceCount.value + standbyDeviceCount.value + faultDeviceCount.value
)
const totalDeviceCount = computed(() => onlineDeviceCount.value + offlineDeviceCount.value)
const pagedRows = computed(() => {
const start = (detailPage.value - 1) * detailPageSize
return detailRows.value.slice(start, start + detailPageSize)
@ -575,8 +578,8 @@ onUnmounted(() => {
.brand-icon {
display: grid;
width: 34px;
height: 34px;
width: 40px;
height: 40px;
color: #8cd3ff;
background: rgb(78 157 255 / 16%);
border: 1px solid rgb(93 177 255 / 40%);

Loading…
Cancel
Save