黄伟杰 1 day ago
commit a1b5847161

@ -64,7 +64,8 @@ export default {
no: 'No',
code: 'Auto-generate on Save',
enabled: 'Enabled',
disabled: 'Disabled'
disabled: 'Disabled',
Refresh:'Refresh'
},
file:{
title:'Choose File',
@ -619,6 +620,7 @@ export default {
latestChangeTime: 'Latest Change Time',
stockIn: 'Inbound',
stockOut: 'Outbound',
tabAll:'All',
cardLabels: {
sku: 'SKU',
stockSummary: 'Stock Summary',
@ -2749,6 +2751,7 @@ export default {
dialogBarCodePlaceholder: 'Please enter code',
dialogNameLabel: 'Name',
dialogNamePlaceholder: 'Please enter name',
dialogCategoryTypeLabel: 'Type',
dialogCategoryLabel: 'Category',
dialogCategoryPlaceholder: 'Please select category',
dialogUnitLabel: 'Inventory Unit',
@ -2801,7 +2804,43 @@ export default {
images: 'Image',
categoryTree: 'Material Category',
addCategory: 'Add Category',
refreshTree: 'Refresh'
refreshTree: 'Refresh',
tableDetailAction: 'Details',
detailTitle: 'Material Details',
detailLabelText: '{label}:',
listSeparator: ', ',
dialogDeviceLabel: 'Related Devices',
dialogMoldLabel: 'Related Molds',
detailCountText: '{label}: {count}',
defaultPackagingSchemeText: 'Default Reference Scheme: {name}',
defaultSupplierText: 'Default Supplier: {name}',
totalCountText: '{count} total',
exportFilename: 'Products.xls',
materialSummaryTitle: 'Material Attribute Summary',
categorySummaryTitle: '{category} Attribute Summary',
addUnitButton: '+ Add Unit',
unitNamePlaceholder: 'Please enter unit name',
dialogDevicePlaceholder: 'Click to select devices',
dialogMoldPlaceholder: 'Click to select molds',
qrcodePrintTitle: '{name} QR Code Print Preview',
defaultProductName: 'Product',
qrcodeRefreshConfirm: 'Refresh this product QR code?',
deviceSelectTitle: 'Select Devices',
deviceCodeLabel: 'Device Code',
codePlaceholder: 'Please enter code',
deviceNameLabel: 'Device Name',
namePlaceholder: 'Please enter name',
moldSelectTitle: 'Select Molds',
moldCodeLabel: 'Mold Code',
moldNameLabel: 'Mold Name',
defaultSupplierColumn: 'Default Supplier',
deviceModelLabel: 'Device Model',
workshopLabel: 'Workshop',
moldModelLabel: 'Mold Model',
deviceIdFallback: 'Device ID: {id}',
moldIdFallback: 'Mold ID: {id}',
unitNameRequired: 'Please enter unit name',
addUnitSuccess: 'Unit added successfully'
},
AutocodeRule: {
moduleName: 'Code Rules',
@ -3048,6 +3087,7 @@ export default {
dialogPrimaryUnitPlaceholder: 'Please select primary unit',
dialogChangeRateLabel: 'Conversion Rate',
dialogChangeRatePlaceholder: 'Please enter conversion rate',
dialogChangeRateTooltip: 'Conversion rate to the primary unit. For example: s = 1000 ms, so the conversion rate is 1000.',
dialogStatusLabel: 'Unit Status',
dialogCancelButton: 'Cancel',
dialogSubmitButton: 'Confirm',
@ -3065,7 +3105,9 @@ export default {
'Upload success: {createCount}; Update success: {updateCount}; Update failed: {failureCount};',
importFailed: 'Upload failed, please upload again!',
importFileLimit: 'Only one file can be uploaded!',
nameEnColumn:'English Unit Name'
nameEnColumn: 'English Unit Name',
exportFilename: 'ProductUnits.xls',
importTemplateFilename: 'UnitImportTemplate.xls'
},
CalHoliday: {
setWorkingDays: 'set working days',
@ -5773,6 +5815,75 @@ export default {
emptyDescription: 'No data',
defaultFieldLabelPrefix: 'Field ',
backToList: 'Back to History List',
loadLineTreeFailed: 'Failed to load production line tree',
analysisPageTitle: 'Multi-point Device History Analysis',
analysisDeviceName: 'Device Name',
analysisDeviceCode: 'Device Code',
analysisProductionLine: 'Production Line',
analysisLatestCollectionTime: 'Latest Collection Time',
analysisSaveButton: 'Save Analysis',
analysisRefreshButton: 'Refresh',
analysisConditionTitle: 'Analysis Conditions',
analysisStartTimeLabel: 'Start Time',
analysisStartTimePlaceholder: 'Select start time',
analysisEndTimeLabel: 'End Time',
analysisEndTimePlaceholder: 'Select end time',
analysisPointSelectionLabel: 'Point Selection',
analysisPointSearchPlaceholder: 'Search point name or code',
analysisSelectProcessPoints: 'Select Process Points',
analysisClearPoints: 'Clear',
analysisNoPointSelected: 'No points selected',
analysisStartButton: 'Start Analysis',
analysisResetButton: 'Reset Conditions',
analysisTrendTitle: 'Multi-point History Trend Analysis',
analysisResultTitle: 'Analysis Results',
analysisResultTip: 'Statistics are aggregated by point; abnormal results are highlighted',
analysisPointNameColumn: 'Point Name',
analysisPointCodeColumn: 'Point Code',
analysisUnitColumn: 'Unit',
analysisMaxColumn: 'Maximum',
analysisMinColumn: 'Minimum',
analysisAvgColumn: 'Average',
analysisRangeColumn: 'Range',
analysisHistoryDetailTitle: 'History Details',
analysisHistoryDetailTip: 'Displays a subset of the raw collected data',
analysisSelectPointWarning: 'Select at least one point',
analysisTimeRangeWarning: 'The time range cannot exceed 24 hours, and the end time must be later than the start time',
analysisSuccessMessage: 'Analysis completed; charts and statistics have been updated',
analysisNoDataMessage: 'No matching data found',
analysisFailedLog: 'Analysis failed:',
analysisFailedMessage: 'Analysis failed. Check the network connection or parameters.',
analysisHours: '{hours} hours',
analysisNotSet: 'Not set',
analysisSelectedPointCount: 'Selected Points',
analysisSelectedPointDesc: 'Currently included in trend and statistical analysis',
analysisTimeSpan: 'Analysis Time Span',
analysisTimeRangeText: '{start} to {end}',
analysisTimeRangeNotSet: 'Time range not set',
analysisMaxRangePoint: 'Largest Range Point',
analysisMinRangePoint: 'Smallest Range Point',
analysisHighestPoint: 'Highest Value Point',
analysisRangeValue: 'Range {value}',
analysisHighestValue: 'Highest {value}',
analysisExportTaskMessage: 'An export task has been created for the analysis results',
analysisEndTimeAdjusted: 'The time range exceeded 24 hours; the end time was adjusted automatically',
analysisStartTimeAdjusted: 'The time range exceeded 24 hours; the start time was adjusted automatically',
analysisEmptyChartText: 'Select analysis conditions and click "Start Analysis"',
analysisSelectPointFirst: 'Select points first',
analysisSetTimeRange: 'Set the start and end time',
analysisGroupTemperature: 'Temperature',
analysisGroupProcess: 'Process',
analysisGroupEnergy: 'Energy',
analysisPointUpperTemp: 'Upper Temperature',
analysisPointMiddleTemp: 'Middle Temperature',
analysisPointLowerTemp: 'Lower Temperature',
analysisPointReturnAirTemp: 'Return Air Temperature',
analysisPointActualTemp: 'Actual Temperature',
analysisPointConveyorSpeed: 'Conveyor Speed',
analysisPointMainTankLevel: 'Main Tank Level',
analysisPointDryerPower: 'Dryer Power Consumption',
exportFilename: 'HistoryDataDevice.xls'
},
DeviceParamAnalysis: {

@ -64,7 +64,8 @@ export default {
no: '否',
code: '编码保存后自动生成',
enabled: '启用',
disabled: '停用'
disabled: '停用',
Refresh:'刷新'
},
file:{
title:'选取文件',
@ -619,6 +620,7 @@ export default {
latestChangeTime: '最近变动时间',
stockIn: '入库',
stockOut: '出库',
tabAll: '全部',
cardLabels: {
sku: 'SKU',
stockSummary: '库存摘要',
@ -4110,7 +4112,43 @@ export default {
images: '图片',
categoryTree: '物料分类',
addCategory: '新增分类',
refreshTree: '刷新'
refreshTree: '刷新',
tableDetailAction: '详情',
detailTitle: '物料详情',
detailLabelText: '{label}',
listSeparator: '、',
dialogDeviceLabel: '关联设备',
dialogMoldLabel: '关联模具',
detailCountText: '{label}{count} 个',
defaultPackagingSchemeText: '默认参考方案:{name}',
defaultSupplierText: '默认供应商:{name}',
totalCountText: '共 {count} 个',
exportFilename: '产品.xls',
materialSummaryTitle: '物料属性摘要',
categorySummaryTitle: '{category}属性摘要',
addUnitButton: '+ 新增单位',
unitNamePlaceholder: '请输入单位名称',
dialogDevicePlaceholder: '点击选择设备',
dialogMoldPlaceholder: '点击选择模具',
qrcodePrintTitle: '{name}二维码打印预览',
defaultProductName: '产品',
qrcodeRefreshConfirm: '确认刷新该产品二维码吗?',
deviceSelectTitle: '选择设备',
deviceCodeLabel: '设备编号',
codePlaceholder: '请输入编号',
deviceNameLabel: '设备名称',
namePlaceholder: '请输入名称',
moldSelectTitle: '选择模具',
moldCodeLabel: '模具编号',
moldNameLabel: '模具名称',
defaultSupplierColumn: '是否默认供应商',
deviceModelLabel: '设备型号',
workshopLabel: '所属车间',
moldModelLabel: '模具型号',
deviceIdFallback: '设备ID:{id}',
moldIdFallback: '模具ID:{id}',
unitNameRequired: '请输入单位名称',
addUnitSuccess: '新增单位成功'
},
AutocodeRule: {
moduleName: '编码规则',
@ -4356,6 +4394,7 @@ export default {
dialogPrimaryUnitPlaceholder: '请选择主单位',
dialogChangeRateLabel: '换算比例',
dialogChangeRatePlaceholder: '请输入换算比例',
dialogChangeRateTooltip: '与主单位换算比例s = 1000 ms换算比例为1000',
dialogStatusLabel: '单位状态',
dialogCancelButton: '取 消',
dialogSubmitButton: '确 定',
@ -4374,7 +4413,9 @@ export default {
'上传成功数量:{createCount};更新成功数量:{updateCount};更新失败数量:{failureCount}',
importFailed: '上传失败,请您重新上传!',
importFileLimit: '最多只能上传一个文件!',
nameEnColumn:'单位英文名称'
nameEnColumn: '单位英文名称',
exportFilename: '产品单位.xls',
importTemplateFilename: '单位导入模版.xls'
},
//假日管理
CalHoliday: {
@ -5667,6 +5708,75 @@ export default {
emptyDescription: '暂无数据',
defaultFieldLabelPrefix: '字段',
backToList: '返回历史记录列表',
loadLineTreeFailed: '获取产线树失败',
analysisPageTitle: '单设备多点位历史数据分析',
analysisDeviceName: '设备名称',
analysisDeviceCode: '设备编码',
analysisProductionLine: '所属产线',
analysisLatestCollectionTime: '最新采集时间',
analysisSaveButton: '保存分析方案',
analysisRefreshButton: '刷新',
analysisConditionTitle: '分析条件',
analysisStartTimeLabel: '开始时间',
analysisStartTimePlaceholder: '选择开始时间',
analysisEndTimeLabel: '结束时间',
analysisEndTimePlaceholder: '选择结束时间',
analysisPointSelectionLabel: '点位选择',
analysisPointSearchPlaceholder: '搜索点位名称或编码',
analysisSelectProcessPoints: '选择工艺参数',
analysisClearPoints: '清空',
analysisNoPointSelected: '未选择点位',
analysisStartButton: '开始分析',
analysisResetButton: '重置条件',
analysisTrendTitle: '多点位历史趋势分析',
analysisResultTitle: '分析结果',
analysisResultTip: '按点位聚合统计,异常结果已高亮',
analysisPointNameColumn: '点位名称',
analysisPointCodeColumn: '点位编码',
analysisUnitColumn: '单位',
analysisMaxColumn: '最大值',
analysisMinColumn: '最小值',
analysisAvgColumn: '平均值',
analysisRangeColumn: '波动值',
analysisHistoryDetailTitle: '历史明细',
analysisHistoryDetailTip: '展示部分原始采集数据',
analysisSelectPointWarning: '请至少选择一个点位',
analysisTimeRangeWarning: '时间范围不能超过24小时且结束时间必须晚于开始时间',
analysisSuccessMessage: '已重新分析,图表与统计结果已更新',
analysisNoDataMessage: '未查询到相关数据',
analysisFailedLog: '分析失败:',
analysisFailedMessage: '分析失败,请检查网络连接或参数设置',
analysisHours: '{hours}小时',
analysisNotSet: '未设置',
analysisSelectedPointCount: '已选点位数',
analysisSelectedPointDesc: '当前参与趋势与统计分析',
analysisTimeSpan: '分析时间跨度',
analysisTimeRangeText: '{start} 至 {end}',
analysisTimeRangeNotSet: '未设置时间范围',
analysisMaxRangePoint: '最大波动点位',
analysisMinRangePoint: '最小波动点位',
analysisHighestPoint: '最高值点位',
analysisRangeValue: '波动 {value}',
analysisHighestValue: '最高 {value}',
analysisExportTaskMessage: '分析结果已生成导出任务',
analysisEndTimeAdjusted: '时间范围超过24小时已自动调整结束时间',
analysisStartTimeAdjusted: '时间范围超过24小时已自动调整开始时间',
analysisEmptyChartText: '请选择分析条件后点击“开始分析”',
analysisSelectPointFirst: '请先选择点位',
analysisSetTimeRange: '请设置开始和结束时间',
analysisGroupTemperature: '温度类',
analysisGroupProcess: '工艺类',
analysisGroupEnergy: '能耗类',
analysisPointUpperTemp: '上层温度',
analysisPointMiddleTemp: '中层温度',
analysisPointLowerTemp: '下层温度',
analysisPointReturnAirTemp: '回风温度',
analysisPointActualTemp: '实际温度',
analysisPointConveyorSpeed: '输送速度',
analysisPointMainTankLevel: '主缸水位',
analysisPointDryerPower: '干燥柜用电量',
exportFilename: '历史数据设备.xls'
},
DeviceParamAnalysis: {

@ -57,14 +57,14 @@
<template #footer>
<div v-if="!isAddingUnit" class="px-10px py-6px">
<el-button text bg size="small" @click.stop="onAddUnit('unit')">
+ 新增单位
{{ t('FactoryModeling.ProductInformation.addUnitButton') }}
</el-button>
</div>
<div v-else class="px-10px py-6px">
<el-input v-model="unitInputName" size="small" placeholder="请输入单位名称" class="mb-6px" />
<el-input v-model="unitInputName" size="small" :placeholder="t('FactoryModeling.ProductInformation.unitNamePlaceholder')" 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>
<el-button type="primary" size="small" @click.stop="onConfirmAddUnit('unit')">{{ t('common.ok') }}</el-button>
<el-button size="small" @click.stop="clearAddUnit()">{{ t('common.cancel') }}</el-button>
</div>
</div>
</template>
@ -81,15 +81,15 @@
<template #footer>
<div v-if="!isAddingPurchaseUnit" class="px-10px py-6px">
<el-button text bg size="small" @click.stop="onAddUnit('purchaseUnit')">
+ 新增单位
{{ t('FactoryModeling.ProductInformation.addUnitButton') }}
</el-button>
</div>
<div v-else class="px-10px py-6px">
<el-input v-model="unitInputName" size="small" placeholder="请输入单位名称" class="mb-6px" />
<el-input v-model="unitInputName" size="small" :placeholder="t('FactoryModeling.ProductInformation.unitNamePlaceholder')" 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>
@click.stop="onConfirmAddUnit('purchaseUnit')">{{ t('common.ok') }}</el-button>
<el-button size="small" @click.stop="clearAddUnit()">{{ t('common.cancel') }}</el-button>
</div>
</div>
</template>
@ -117,14 +117,14 @@
</el-form-item>
</el-col>
<!-- <el-col v-if="isProductCategory" :span="12">
<el-form-item label="关联设备" prop="devices">
<el-input :model-value="deviceDisplayText" placeholder="点击选择设备" readonly
<el-form-item :label="t('FactoryModeling.ProductInformation.dialogDeviceLabel')" prop="devices">
<el-input :model-value="deviceDisplayText" :placeholder="t('FactoryModeling.ProductInformation.dialogDevicePlaceholder')" readonly
@click="openDeviceSelectDialog" />
</el-form-item>
</el-col>
<el-col v-if="isProductCategory" :span="12">
<el-form-item label="关联模具" prop="molds">
<el-input :model-value="moldDisplayText" placeholder="点击选择模具" readonly @click="openMoldSelectDialog" />
<el-form-item :label="t('FactoryModeling.ProductInformation.dialogMoldLabel')" prop="molds">
<el-input :model-value="moldDisplayText" :placeholder="t('FactoryModeling.ProductInformation.dialogMoldPlaceholder')" readonly @click="openMoldSelectDialog" />
</el-form-item>
</el-col> -->
<el-col v-if="formData.categoryType === 1" :span="12">
@ -187,9 +187,9 @@
<div class="flex items-start gap-20px">
<el-form-item :label="t('FactoryModeling.ProductInformation.qrcode')" prop="qrcodeUrl" class="flex-1">
<QrcodeActionCard :image-url="formData.qrcodeUrl" :print-id="formData.id" :print-template-type="1"
:print-title="`${formData.name || '产品'}二维码打印预览`"
:print-title="t('FactoryModeling.ProductInformation.qrcodePrintTitle', { name: formData.name || t('FactoryModeling.ProductInformation.defaultProductName') })"
:empty-text="t('FactoryModeling.ProductInformation.qrcodeEmpty')" :refresh-url="getQrcodeRefreshUrl()"
:refresh-disabled="!formData.id || !formData.barCode" refresh-confirm-text="确认刷新该产品二维码吗?"
:refresh-disabled="!formData.id || !formData.barCode" :refresh-confirm-text="t('FactoryModeling.ProductInformation.qrcodeRefreshConfirm')"
:template-json="formData.templateJson" :print-data="buildPrintData()"
@refresh-success="handleQrcodeRefreshSuccess" />
</el-form-item>
@ -222,16 +222,16 @@
</el-button>
</div>
</div>
<TableSelectDialog ref="deviceSelectDialogRef" title="选择设备" :columns="deviceColumns"
<TableSelectDialog ref="deviceSelectDialogRef" :title="t('FactoryModeling.ProductInformation.deviceSelectTitle')" :columns="deviceColumns"
:fetch-api="fetchDeviceLedgerPage" row-key="id" @confirm="handleDeviceSelectConfirm"
:query-params="mergedQueryParams">
<template #header>
<el-form ref="searchFormRef" :model="searchParams" :inline="true">
<el-form-item label="设备编号" prop="deviceCode">
<el-input v-model="searchParams.deviceCode" placeholder="请输入编号" clearable />
<el-form-item :label="t('FactoryModeling.ProductInformation.deviceCodeLabel')" prop="deviceCode">
<el-input v-model="searchParams.deviceCode" :placeholder="t('FactoryModeling.ProductInformation.codePlaceholder')" clearable />
</el-form-item>
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="searchParams.deviceName" placeholder="请输入名称" clearable />
<el-form-item :label="t('FactoryModeling.ProductInformation.deviceNameLabel')" prop="deviceName">
<el-input v-model="searchParams.deviceName" :placeholder="t('FactoryModeling.ProductInformation.namePlaceholder')" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">{{ t('FactoryModeling.ProductInformation.searchButtonText')
@ -241,15 +241,15 @@
</el-form>
</template>
</TableSelectDialog>
<TableSelectDialog ref="moldSelectDialogRef" title="选择模具" :columns="moldColumns" :fetch-api="MoldBrandApi.getMoldPage"
<TableSelectDialog ref="moldSelectDialogRef" :title="t('FactoryModeling.ProductInformation.moldSelectTitle')" :columns="moldColumns" :fetch-api="MoldBrandApi.getMoldPage"
row-key="id" @confirm="handleMoldSelectConfirm" :query-params="mergedMoldQueryParams">
<template #header>
<el-form ref="searchMoldFormRef" :model="searchMoldParams" :inline="true">
<el-form-item label="模具编号" prop="code">
<el-input v-model="searchMoldParams.code" placeholder="请输入编号" clearable />
<el-form-item :label="t('FactoryModeling.ProductInformation.moldCodeLabel')" prop="code">
<el-input v-model="searchMoldParams.code" :placeholder="t('FactoryModeling.ProductInformation.codePlaceholder')" clearable />
</el-form-item>
<el-form-item label="模具名称" prop="name">
<el-input v-model="searchMoldParams.name" placeholder="请输入名称" clearable />
<el-form-item :label="t('FactoryModeling.ProductInformation.moldNameLabel')" prop="name">
<el-input v-model="searchMoldParams.name" :placeholder="t('FactoryModeling.ProductInformation.namePlaceholder')" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleMoldSearch">{{
@ -337,7 +337,7 @@
<el-table-column :label="t('ErpPurchase.Supplier.contact')" prop="contact" minWidth="100" />
<el-table-column :label="t('ErpPurchase.Supplier.mobile')" prop="mobile" minWidth="120" />
<el-table-column :label="t('ErpPurchase.Supplier.telephone')" prop="telephone" minWidth="120" />
<el-table-column label="是否默认供应商" width="130" align="center">
<el-table-column :label="t('FactoryModeling.ProductInformation.defaultSupplierColumn')" width="130" align="center">
<template #default="{ row }">
<el-radio v-model="supplierDefaultId" :label="row.id"
:disabled="!supplierSelectedRows.some((item) => item.id === row.id)">
@ -537,25 +537,25 @@ const toMoldRows = (molds: { id: string | number; name: string }[]) => {
}
const deviceDisplayText = computed(() => {
if (!formData.value.devices.length) return ''
if (!selectedDeviceRows.value.length) return formData.value.devices.map((item) => item.name).join('、')
return selectedDeviceRows.value.map((item) => item.deviceName || item.name || item.code || `ID:${item.id}`).join('、')
if (!selectedDeviceRows.value.length) return formData.value.devices.map((item) => item.name).join(t('FactoryModeling.ProductInformation.listSeparator'))
return selectedDeviceRows.value.map((item) => item.deviceName || item.name || item.code || `ID:${item.id}`).join(t('FactoryModeling.ProductInformation.listSeparator'))
})
const moldDisplayText = computed(() => {
if (!formData.value.molds.length) return ''
if (!selectedMoldRows.value.length) return formData.value.molds.map((item) => item.name).join('、')
return selectedMoldRows.value.map((item) => item.name || item.code || `ID:${item.id}`).join('、')
if (!selectedMoldRows.value.length) return formData.value.molds.map((item) => item.name).join(t('FactoryModeling.ProductInformation.listSeparator'))
return selectedMoldRows.value.map((item) => item.name || item.code || `ID:${item.id}`).join(t('FactoryModeling.ProductInformation.listSeparator'))
})
const deviceColumns = [
{ label: '设备编号', prop: 'deviceCode', minWidth: 140 },
{ label: '设备名称', prop: 'deviceName', minWidth: 160 },
{ label: '设备型号', prop: 'deviceModel', minWidth: 140 },
{ label: '所属车间', prop: 'workshopName', minWidth: 140 }
{ label: t('FactoryModeling.ProductInformation.deviceCodeLabel'), prop: 'deviceCode', minWidth: 140 },
{ label: t('FactoryModeling.ProductInformation.deviceNameLabel'), prop: 'deviceName', minWidth: 160 },
{ label: t('FactoryModeling.ProductInformation.deviceModelLabel'), prop: 'deviceModel', minWidth: 140 },
{ label: t('FactoryModeling.ProductInformation.workshopLabel'), prop: 'workshopName', minWidth: 140 }
]
const moldColumns = [
{ label: '模具编码', prop: 'code', minWidth: 140 },
{ label: '模具名称', prop: 'name', minWidth: 160 },
{ label: '模具型号', prop: 'brandName', minWidth: 140 },
{ label: '状态', prop: 'status', minWidth: 100 }
{ label: t('FactoryModeling.ProductInformation.moldCodeLabel'), prop: 'code', minWidth: 140 },
{ label: t('FactoryModeling.ProductInformation.moldNameLabel'), prop: 'name', minWidth: 160 },
{ label: t('FactoryModeling.ProductInformation.moldModelLabel'), prop: 'brandName', minWidth: 140 },
{ label: t('FactoryModeling.ProductInformation.tableStatusColumn'), prop: 'status', minWidth: 100 }
]
const fetchDeviceLedgerPage = (params: Record<string, any>) => {
return DeviceLedgerApi.getDeviceLedgerPage({
@ -582,7 +582,7 @@ const handleDeviceSelectConfirm = (payload: { ids: (number | string)[]; rows: an
const id = String(item.id)
return {
id,
name: item.deviceName || item.name || item.code || `设备ID:${id}`
name: item.deviceName || item.name || item.code || t('FactoryModeling.ProductInformation.deviceIdFallback', { id })
}
})
.filter((item): item is { id: string | number; name: string } => Boolean(item))
@ -594,7 +594,7 @@ const handleMoldSelectConfirm = (payload: { ids: (number | string)[]; rows: any[
const id = String(item.id)
return {
id,
name: item.name || item.code || `模具ID:${id}`
name: item.name || item.code || t('FactoryModeling.ProductInformation.moldIdFallback', { id })
}
})
.filter((item): item is { id: string | number; name: string } => Boolean(item))
@ -707,7 +707,7 @@ const displayPackagingSchemeText = computed(() => {
return scheme?.name || ''
})
.filter(Boolean)
.join('、')
.join(t('FactoryModeling.ProductInformation.listSeparator'))
})
const openPackagingSchemeDialog = async () => {
@ -834,7 +834,7 @@ const displaySupplierText = computed(() => {
return supplier?.name || ''
})
.filter(Boolean)
.join('、')
.join(t('FactoryModeling.ProductInformation.listSeparator'))
})
const openSupplierDialog = async () => {
@ -1053,7 +1053,7 @@ const onAddUnit = (type: 'unit' | 'purchaseUnit') => {
const onConfirmAddUnit = async (type: 'unit' | 'purchaseUnit') => {
if (!unitInputName.value.trim()) {
message.warning('请输入单位名称')
message.warning(t('FactoryModeling.ProductInformation.unitNameRequired'))
return
}
formLoading.value = true
@ -1077,7 +1077,7 @@ const onConfirmAddUnit = async (type: 'unit' | 'purchaseUnit') => {
}
}
clearAddUnit()
message.success('新增单位成功')
message.success(t('FactoryModeling.ProductInformation.addUnitSuccess'))
} finally {
formLoading.value = false
}

@ -83,7 +83,7 @@
<div class="product-page-right">
<ContentWrap>
<el-table ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" :max-height="planTableMaxHeight" :row-class-name="getRowClassName">
<el-table-column :label="t('FactoryModeling.ProductInformation.tableBarCodeColumn')" align="center" prop="barCode"
<el-table-column :label="t('FactoryModeling.ProductInformation.tableBarCodeColumn')" align="center" prop="barCode" width="180px"
sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.dialogCategoryTypeLabel')" align="center" prop="categoryType" sortable>
<template #default="scope">
@ -91,7 +91,7 @@
</template>
</el-table-column>
<el-table-column :label="t('FactoryModeling.ProductInformation.tableNameColumn')" align="left" prop="name"
width="220px" sortable />
sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableStandardColumn')" align="center"
prop="standard" sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableCategoryColumn')" align="center"
@ -109,7 +109,7 @@
<el-table-column :label="t('FactoryModeling.ProductInformation.tableOperateColumn')" align="center" width="210px">
<template #default="scope">
<el-button link type="primary" @click="handleShowDetail(scope.row.id)">
详情
{{ t('FactoryModeling.ProductInformation.tableDetailAction') }}
</el-button>
<el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['erp:product:update']">
{{ t('FactoryModeling.ProductInformation.tableEditAction') }}
@ -127,7 +127,7 @@
<ContentWrap v-if="detailVisible" class="product-detail-wrap" v-loading="detailLoading">
<template v-if="productDetail">
<div class="detail-header">
<div class="detail-title">物料详情</div>
<div class="detail-title">{{ t('FactoryModeling.ProductInformation.detailTitle') }}</div>
<el-button text @click="closeDetail">
<Icon icon="ep:close" />
</el-button>
@ -163,33 +163,33 @@
<div class="detail-info">
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogBarCodeLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogBarCodeLabel') }) }}</span>
<span class="field-value">{{ formatValue(productDetail.barCode) }}</span>
</div>
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogCategoryTypeLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogCategoryTypeLabel') }) }}</span>
<span class="field-value">{{ formatCategoryType(productDetail.categoryType) }}</span>
</div>
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogNameLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogNameLabel') }) }}</span>
<span class="field-value">{{ formatValue(productDetail.name) }}</span>
</div>
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogCategoryLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogCategoryLabel') }) }}</span>
<span class="field-value">{{ formatValue(productDetail.subCategoryName) }}</span>
</div>
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogUnitLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogUnitLabel') }) }}</span>
<span class="field-value">{{ formatValue(productDetail.unitName) }}</span>
</div>
<!-- 物料/备件类型供应商 -->
<template v-if="productDetail.categoryType === 2 || productDetail.categoryType === 3">
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogPurchaseUnitLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogPurchaseUnitLabel') }) }}</span>
<span class="field-value">{{ formatValue(productDetail.purchaseUnitName) }}</span>
</div>
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogPurchaseUnitConvertLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogPurchaseUnitConvertLabel') }) }}</span>
<span class="field-value">{{ formatPurchaseUnitConvert(productDetail) }}</span>
</div>
<!-- <div class="detail-field">
@ -198,49 +198,49 @@
</div> -->
</template>
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogStandardLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogStandardLabel') }) }}</span>
<span class="field-value">{{ formatValue(productDetail.standard || productDetail.deviceSpec || productDetail.model) }}</span>
</div>
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogExpiryDayLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogExpiryDayLabel') }) }}</span>
<span class="field-value">{{ formatValue(productDetail.expiryDay) }}</span>
</div>
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogStatusLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogStatusLabel') }) }}</span>
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="productDetail.status" />
</div>
<!-- 产品类型关联设备关联模具 -->
<template v-if="productDetail.categoryType === 1">
<div class="detail-field">
<span class="field-label">关联设备</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogDeviceLabel') }) }}</span>
<span class="field-value">{{ formatDeviceText(productDetail) }}</span>
</div>
<div class="detail-field">
<span class="field-label">关联模具</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogMoldLabel') }) }}</span>
<span class="field-value">{{ formatMoldText(productDetail) }}</span>
</div>
</template>
<!-- 备件类型备件等级是否易损件采购周期品牌 -->
<template v-if="productDetail.categoryType === 3">
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogSparePartLevelLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogSparePartLevelLabel') }) }}</span>
<span class="field-value">{{ formatSparePartLevel(productDetail.sparePartLevel) }}</span>
</div>
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogFragileFlagLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogFragileFlagLabel') }) }}</span>
<span class="field-value">{{ productDetail.fragileFlag === 1 ? t('common.yes') : productDetail.fragileFlag === 0 ? t('common.no') : '-' }}</span>
</div>
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogPurchaseCycleLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogPurchaseCycleLabel') }) }}</span>
<span class="field-value">{{ formatValue(productDetail.purchaseCycle) }}</span>
</div>
<div class="detail-field">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogBrandLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogBrandLabel') }) }}</span>
<span class="field-value">{{ formatValue(productDetail.brand) }}</span>
</div>
</template>
<div class="detail-field detail-field--full">
<span class="field-label">{{ t('FactoryModeling.ProductInformation.dialogRemarkLabel') }}</span>
<span class="field-label">{{ t('FactoryModeling.ProductInformation.detailLabelText', { label: t('FactoryModeling.ProductInformation.dialogRemarkLabel') }) }}</span>
<span class="field-value">{{ formatValue(productDetail.remark) }}</span>
</div>
</div>
@ -251,22 +251,22 @@
<template v-if="productDetail.categoryType === 1">
<div class="summary-item">
<Icon icon="ep:box" class="summary-icon" />
<span>{{ t('FactoryModeling.ProductInformation.dialogPackagingSchemeLabel') }}{{ packagingSchemeCount }} </span>
<span>{{ t('FactoryModeling.ProductInformation.detailCountText', { label: t('FactoryModeling.ProductInformation.dialogPackagingSchemeLabel'), count: packagingSchemeCount }) }}</span>
</div>
<div class="summary-item">
<Icon icon="ep:document-checked" class="summary-icon" />
<span>默认参考方案{{ defaultPackagingSchemeText }}</span>
<span>{{ t('FactoryModeling.ProductInformation.defaultPackagingSchemeText', { name: defaultPackagingSchemeText }) }}</span>
</div>
</template>
<!-- 物料/备件类型供应商摘要 -->
<template v-if="productDetail.categoryType === 2 || productDetail.categoryType === 3">
<div class="summary-item">
<Icon icon="ep:office-building" class="summary-icon" />
<span>{{ t('FactoryModeling.ProductInformation.dialogSupplierLabel') }}{{ supplierCount }} </span>
<span>{{ t('FactoryModeling.ProductInformation.detailCountText', { label: t('FactoryModeling.ProductInformation.dialogSupplierLabel'), count: supplierCount }) }}</span>
</div>
<div class="summary-item">
<Icon icon="ep:star" class="summary-icon" />
<span>默认供应商{{ defaultSupplierText }}</span>
<span>{{ t('FactoryModeling.ProductInformation.defaultSupplierText', { name: defaultSupplierText }) }}</span>
</div>
</template>
@ -274,7 +274,7 @@
<div v-if="productDetail.categoryType === 1" class="detail-table-box">
<div class="detail-table-title">
<span>{{ t('FactoryModeling.ProductInformation.dialogPackagingSchemeLabel') }}</span>
<span> {{ packagingSchemeCount }} </span>
<span>{{ t('FactoryModeling.ProductInformation.totalCountText', { count: packagingSchemeCount }) }}</span>
</div>
<el-table :data="productDetail.packagingSchemes || []" border size="small" :show-overflow-tooltip="true">
<el-table-column :label="t('ErpStock.PackagingScheme.name')" prop="packagingSchemeName" min-width="130" />
@ -286,7 +286,7 @@
<div v-else-if="productDetail.categoryType === 2 || productDetail.categoryType === 3" class="detail-table-box">
<div class="detail-table-title">
<span>{{ t('FactoryModeling.ProductInformation.dialogSupplierLabel') }}</span>
<span> {{ supplierCount }} </span>
<span>{{ t('FactoryModeling.ProductInformation.totalCountText', { count: supplierCount }) }}</span>
</div>
<el-table :data="productDetail.suppliers || []" border size="small" :show-overflow-tooltip="true">
<el-table-column :label="t('ErpPurchase.Supplier.name')" prop="supplierName" min-width="160">
@ -489,7 +489,7 @@ const handleExport = async () => {
await message.exportConfirm()
exportLoading.value = true
const data = await ProductApi.exportProduct(queryParams)
download.excel(data, '产品.xls')
download.excel(data, t('FactoryModeling.ProductInformation.exportFilename'))
} catch {
} finally {
exportLoading.value = false
@ -565,7 +565,9 @@ const defaultPackagingSchemeText = computed(() => {
const detailSummaryTitle = computed(() => {
const categoryType = formatCategoryType(productDetail.value?.categoryType)
return categoryType === '-' ? '物料属性摘要' : `${categoryType}属性摘要`
return categoryType === '-'
? t('FactoryModeling.ProductInformation.materialSummaryTitle')
: t('FactoryModeling.ProductInformation.categorySummaryTitle', { category: categoryType })
})
//
@ -584,16 +586,16 @@ const defaultSupplierText = computed(() => {
const formatDeviceText = (detail: any) => {
if (!detail?.devices?.length && !detail?.deviceList?.length) return '-'
const list = detail.devices || detail.deviceList || []
return list.map((item: any) => item.name || item.deviceName || `ID:${item.id}`).join('、') || '-'
return list.map((item: any) => item.name || item.deviceName || `ID:${item.id}`).join(t('FactoryModeling.ProductInformation.listSeparator')) || '-'
}
const formatMoldText = (detail: any) => {
if (!detail?.molds?.length && !detail?.moldList?.length) return '-'
const list = detail.molds || detail.moldList || []
return list.map((item: any) => item.name || item.code || `ID:${item.id}`).join('、') || '-'
return list.map((item: any) => item.name || item.code || `ID:${item.id}`).join(t('FactoryModeling.ProductInformation.listSeparator')) || '-'
}
const formatSupplierText = (detail: any) => {
if (!detail?.suppliers?.length) return '-'
return detail.suppliers.map((item: any) => item.supplierName || `ID:${item.supplierId}`).join('、') || '-'
return detail.suppliers.map((item: any) => item.supplierName || `ID:${item.supplierId}`).join(t('FactoryModeling.ProductInformation.listSeparator')) || '-'
}
const formatSparePartLevel = (value: any) => {
if (value === undefined || value === null || value === '') return '-'

@ -58,7 +58,7 @@
<template #label>
<span>
{{ t('FactoryModeling.ProductUnit.dialogChangeRateLabel') }}
<el-tooltip content="与主单位换算比例s = 1000 ms换算比例为1000" placement="top">
<el-tooltip :content="t('FactoryModeling.ProductUnit.dialogChangeRateTooltip')" placement="top">
<Icon icon="ep:question-filled" />
</el-tooltip>
</span>

@ -139,6 +139,6 @@ const handleExceed = (): void => {
/** 下载模板操作 */
const importTemplate = async () => {
const res = await ProductUnitApi.importProductUnitTemplate()
download.excel(res, '单位导入模版.xls')
download.excel(res, t('FactoryModeling.ProductUnit.importTemplateFilename'))
}
</script>

@ -251,7 +251,7 @@ const handleExport = async () => {
//
exportLoading.value = true
const data = await ProductUnitApi.exportProductUnit(queryParams)
download.excel(data, '产品单位.xls')
download.excel(data, t('FactoryModeling.ProductUnit.exportFilename'))
} catch {
} finally {
exportLoading.value = false

@ -50,7 +50,7 @@
<div class="stock-overview__tables">
<ContentWrap :title="t('ErpStock.Overview.stockDetail')" class="stock-overview__stock">
<el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane label="全部" name="" />
<el-tab-pane :label="t('ErpStock.Overview.tabAll')" name="" />
<el-tab-pane v-for="item in categoryTypeOptions" :key="item.value" :label="item.label"
:name="String(item.value)" />
</el-tabs>

@ -61,7 +61,9 @@
<!-- 列表 -->
<ContentWrap>
<el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane label="全部" name="" />
<el-tab-pane :label="t('ErpStock.Overview.tabAll')" name="" />
<el-tab-pane v-for="item in categoryTypeOptions" :key="item.value" :label="item.label"
:name="String(item.value)" />
</el-tabs>

@ -5,17 +5,17 @@
<el-page-header :content="pageTitle" @back="goBack" />
<el-descriptions class="device-info" :column="5" border>
<el-descriptions-item label="设备名称">{{ deviceData.name }}</el-descriptions-item>
<el-descriptions-item label="设备编码">{{ deviceData.code }}</el-descriptions-item>
<el-descriptions-item :label="t('DataCollection.HistoryData.analysisDeviceName')">{{ deviceData.name }}</el-descriptions-item>
<el-descriptions-item :label="t('DataCollection.HistoryData.analysisDeviceCode')">{{ deviceData.code }}</el-descriptions-item>
<!-- <el-descriptions-item label="设备类型">{{ deviceData.type }}</el-descriptions-item>-->
<el-descriptions-item label="所属产线">{{ deviceData.productionLine }}</el-descriptions-item>
<el-descriptions-item label="最新采集时间">{{ deviceData.lastCollectTime }}</el-descriptions-item>
<el-descriptions-item :label="t('DataCollection.HistoryData.analysisProductionLine')">{{ deviceData.productionLine }}</el-descriptions-item>
<el-descriptions-item :label="t('DataCollection.HistoryData.analysisLatestCollectionTime')">{{ deviceData.lastCollectTime }}</el-descriptions-item>
</el-descriptions>
<div class="action-buttons">
<el-button type="primary" @click="handleSaveAnalysis">
<Icon icon="ep:document-checked" />
保存分析方案
{{ t('DataCollection.HistoryData.analysisSaveButton') }}
</el-button>
<!-- <el-button @click="handleExport">
<Icon icon="ep:download" />
@ -23,7 +23,7 @@
</el-button>-->
<el-button @click="handleRefresh">
<Icon icon="ep:refresh" />
刷新
{{ t('DataCollection.HistoryData.analysisRefreshButton') }}
</el-button>
</div>
</el-card>
@ -32,7 +32,7 @@
<el-card class="filter-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">分析条件</span>
<span class="card-title">{{ t('DataCollection.HistoryData.analysisConditionTitle') }}</span>
</div>
</template>
@ -56,11 +56,11 @@
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="开始时间">
<el-form-item :label="t('DataCollection.HistoryData.analysisStartTimeLabel')">
<el-date-picker
v-model="queryParams.startTime"
type="datetime"
placeholder="选择开始时间"
:placeholder="t('DataCollection.HistoryData.analysisStartTimePlaceholder')"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
@ -70,11 +70,11 @@
</el-col>
<el-col :span="6">
<el-form-item label="结束时间">
<el-form-item :label="t('DataCollection.HistoryData.analysisEndTimeLabel')">
<el-date-picker
v-model="queryParams.endTime"
type="datetime"
placeholder="选择结束时间"
:placeholder="t('DataCollection.HistoryData.analysisEndTimePlaceholder')"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
@ -96,18 +96,18 @@
</el-col>
</el-row>
<el-form-item label="点位选择">
<el-form-item :label="t('DataCollection.HistoryData.analysisPointSelectionLabel')">
<div class="point-selector-card">
<div class="selector-header">
<el-input
v-model="pointKeyword"
placeholder="搜索点位名称或编码"
:placeholder="t('DataCollection.HistoryData.analysisPointSearchPlaceholder')"
clearable
style="width: 200px; margin-right: 10px"
@input="handlePointSearch"
/>
<el-button @click="selectAllTemperature"></el-button>
<el-button @click="clearAllPoints"></el-button>
<el-button @click="selectAllTemperature">{{ t('DataCollection.HistoryData.analysisSelectProcessPoints') }}</el-button>
<el-button @click="clearAllPoints">{{ t('DataCollection.HistoryData.analysisClearPoints') }}</el-button>
</div>
<div class="selected-tags">
@ -120,7 +120,7 @@
{{ point.name }}
</el-tag>
<span v-if="selectedPoints.length === 0" style="color: #98a2b3">
未选择点位
{{ t('DataCollection.HistoryData.analysisNoPointSelected') }}
</span>
</div>
@ -162,11 +162,11 @@
<el-form-item>
<el-button type="primary" @click="handleAnalyze">
<Icon icon="ep:search" />
开始分析
{{ t('DataCollection.HistoryData.analysisStartButton') }}
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" />
重置条件
{{ t('DataCollection.HistoryData.analysisResetButton') }}
</el-button>
</el-form-item>
</el-form>
@ -189,7 +189,7 @@
<el-card class="chart-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">多点位历史趋势分析</span>
<span class="card-title">{{ t('DataCollection.HistoryData.analysisTrendTitle') }}</span>
<div class="chart-toolbar">
<el-radio-group v-model="chartMode" size="small">
<!-- <el-radio-button label="all">全部显示</el-radio-button>-->
@ -221,33 +221,33 @@
<el-card class="result-card" shadow="never" style="width: 44%; margin-right: 0.6%; display: inline-block; vertical-align: top;">
<template #header>
<div class="card-header">
<span class="card-title">分析结果</span>
<span class="card-title">{{ t('DataCollection.HistoryData.analysisResultTitle') }}</span>
<span style="color: #667085; font-size: 13px">
按点位聚合统计异常结果已高亮
{{ t('DataCollection.HistoryData.analysisResultTip') }}
</span>
</div>
</template>
<el-table :data="resultData" style="width: 100%">
<el-table-column prop="name" label="点位名称" width="120" />
<el-table-column prop="code" label="点位编码" width="140" />
<el-table-column prop="unit" label="单位" width="70" />
<el-table-column prop="max" label="最大值" width="110">
<el-table-column prop="name" :label="t('DataCollection.HistoryData.analysisPointNameColumn')" width="120" />
<el-table-column prop="code" :label="t('DataCollection.HistoryData.analysisPointCodeColumn')" width="140" />
<el-table-column prop="unit" :label="t('DataCollection.HistoryData.analysisUnitColumn')" width="70" />
<el-table-column prop="max" :label="t('DataCollection.HistoryData.analysisMaxColumn')" width="110">
<template #default="{ row }">
<span :class="row.maxClass">{{ formatValue(row.max, row.unit) }}</span>
</template>
</el-table-column>
<el-table-column prop="min" label="最小值" width="110">
<el-table-column prop="min" :label="t('DataCollection.HistoryData.analysisMinColumn')" width="110">
<template #default="{ row }">
<span :class="row.minClass">{{ formatValue(row.min, row.unit) }}</span>
</template>
</el-table-column>
<el-table-column prop="avg" label="平均值" width="110">
<el-table-column prop="avg" :label="t('DataCollection.HistoryData.analysisAvgColumn')" width="110">
<template #default="{ row }">
{{ formatValue(row.avg, row.unit) }}
</template>
</el-table-column>
<el-table-column prop="range" label="波动值" width="110">
<el-table-column prop="range" :label="t('DataCollection.HistoryData.analysisRangeColumn')" width="110">
<template #default="{ row }">
<span :class="row.rangeClass">{{ formatValue(row.range, row.unit) }}</span>
</template>
@ -285,15 +285,15 @@
<el-card class="detail-card" shadow="never" style="width: 55.4%; display: inline-block; vertical-align: top;">
<template #header>
<div class="card-header">
<span class="card-title">历史明细</span>
<span class="card-title">{{ t('DataCollection.HistoryData.analysisHistoryDetailTitle') }}</span>
<span style="color: #667085; font-size: 13px">
展示部分原始采集数据
{{ t('DataCollection.HistoryData.analysisHistoryDetailTip') }}
</span>
</div>
</template>
<el-table :data="historyData" style="width: 100%" height="360">
<el-table-column prop="time" label="采集时间" width="150" />
<el-table-column prop="time" :label="t('DataCollection.HistoryData.tableCollectionTimeColumn')" width="150" />
<el-table-column
v-for="point in displayPoints"
:key="point.code.toLowerCase()"
@ -365,9 +365,10 @@ import {DeviceApi, DeviceVO} from "@/api/iot/device";
const router = useRouter()
const route = useRoute();
let query = route.query;
const { t } = useI18n()
//
const pageTitle = '单设备多点位历史数据分析'
const pageTitle = computed(() => t('DataCollection.HistoryData.analysisPageTitle'))
//
const deviceData = reactive({
@ -405,26 +406,26 @@ const queryParams = reactive({
//
const pointGroups = ref([
{
group: '温度类',
group: t('DataCollection.HistoryData.analysisGroupTemperature'),
points: [
{ name: '上层温度', code: 'UpperTemp', unit: '℃', upper: 86, lower: 72 },
{ name: '中层温度', code: 'MiddleTemp', unit: '℃', upper: 86, lower: 72 },
{ name: '下层温度', code: 'LowerTemp', unit: '℃', upper: 84, lower: 70 },
{ name: '回风温度', code: 'ReturnAirTemp', unit: '℃', upper: 78, lower: 62 },
{ name: '实际温度', code: 'ActualTemp', unit: '℃', upper: 86, lower: 72 },
{ name: t('DataCollection.HistoryData.analysisPointUpperTemp'), code: 'UpperTemp', unit: '℃', upper: 86, lower: 72 },
{ name: t('DataCollection.HistoryData.analysisPointMiddleTemp'), code: 'MiddleTemp', unit: '℃', upper: 86, lower: 72 },
{ name: t('DataCollection.HistoryData.analysisPointLowerTemp'), code: 'LowerTemp', unit: '℃', upper: 84, lower: 70 },
{ name: t('DataCollection.HistoryData.analysisPointReturnAirTemp'), code: 'ReturnAirTemp', unit: '℃', upper: 78, lower: 62 },
{ name: t('DataCollection.HistoryData.analysisPointActualTemp'), code: 'ActualTemp', unit: '℃', upper: 86, lower: 72 },
]
},
{
group: '工艺类',
group: t('DataCollection.HistoryData.analysisGroupProcess'),
points: [
{ name: '输送速度', code: 'ConveyorSpeed', unit: 'm/min', upper: 18, lower: 10 },
{ name: '主缸水位', code: 'MainTankLevel', unit: 'mm', upper: 680, lower: 520 }
{ name: t('DataCollection.HistoryData.analysisPointConveyorSpeed'), code: 'ConveyorSpeed', unit: 'm/min', upper: 18, lower: 10 },
{ name: t('DataCollection.HistoryData.analysisPointMainTankLevel'), code: 'MainTankLevel', unit: 'mm', upper: 680, lower: 520 }
]
},
{
group: '能耗类',
group: t('DataCollection.HistoryData.analysisGroupEnergy'),
points: [
{ name: '干燥柜用电量', code: 'DryerPower', unit: 'kWh', upper: 48, lower: 20 }
{ name: t('DataCollection.HistoryData.analysisPointDryerPower'), code: 'DryerPower', unit: 'kWh', upper: 48, lower: 20 }
]
}
])
@ -556,12 +557,12 @@ function getValueClass(value, point) {
const analyzing = ref(false)
async function handleAnalyze() {
if (selectedPointCodes.value.length === 0) {
ElMessage.warning('请至少选择一个点位')
ElMessage.warning(t('DataCollection.HistoryData.analysisSelectPointWarning'))
return
}
//
if (!validateTimeRange(queryParams.startTime, queryParams.endTime)) {
ElMessage.warning('时间范围不能超过24小时且结束时间必须晚于开始时间')
ElMessage.warning(t('DataCollection.HistoryData.analysisTimeRangeWarning'))
return
}
analyzing.value = true
@ -591,15 +592,15 @@ async function handleAnalyze() {
updateChart()
calculateAnalysis()
ElMessage.success('已重新分析,图表与统计结果已更新')
ElMessage.success(t('DataCollection.HistoryData.analysisSuccessMessage'))
} else {
//
ElMessage.warning('未查询到相关数据')
ElMessage.warning(t('DataCollection.HistoryData.analysisNoDataMessage'))
initializeEmptyCharts()
}
} catch (error) {
console.error('分析失败:', error)
ElMessage.error('分析失败,请检查网络连接或参数设置')
console.error(t('DataCollection.HistoryData.analysisFailedLog'), error)
ElMessage.error(t('DataCollection.HistoryData.analysisFailedMessage'))
//
initializeEmptyCharts()
} finally {
@ -683,7 +684,9 @@ async function calculateAnalysis() {
historyData.value = res.data;*/
//
const timeInterval = calculateTimeInterval(queryParams.startTime, queryParams.endTime)
const intervalText = timeInterval > 0 ? `${timeInterval}小时` : '未设置'
const intervalText = timeInterval > 0
? t('DataCollection.HistoryData.analysisHours', { hours: timeInterval })
: t('DataCollection.HistoryData.analysisNotSet')
//
let maxRangePoint = null
@ -713,30 +716,30 @@ async function calculateAnalysis() {
//
// ...
summaryData.value = [
{title: '已选点位数', value: selectedPoints.value.length, desc: '当前参与趋势与统计分析'},
{title: '分析时间跨度', value: intervalText, desc: queryParams.startTime && queryParams.endTime
? `${queryParams.startTime.substring(0, 16)}${queryParams.endTime.substring(0, 16)}`
: '未设置时间范围' },
{title: t('DataCollection.HistoryData.analysisSelectedPointCount'), value: selectedPoints.value.length, desc: t('DataCollection.HistoryData.analysisSelectedPointDesc')},
{title: t('DataCollection.HistoryData.analysisTimeSpan'), value: intervalText, desc: queryParams.startTime && queryParams.endTime
? t('DataCollection.HistoryData.analysisTimeRangeText', { start: queryParams.startTime.substring(0, 16), end: queryParams.endTime.substring(0, 16) })
: t('DataCollection.HistoryData.analysisTimeRangeNotSet') },
/* { title: '异常点位数', value: 1, desc: '存在超限或波动异常', warn: true },*/
/*{title: '最大波动点位', value: '中层温度', desc: '波动 8.3℃'},*/
{
title: '最大波动点位',
title: t('DataCollection.HistoryData.analysisMaxRangePoint'),
value: maxRangePoint ? maxRangePoint.name : '-',
desc: maxRangePoint ? `波动 ${formatValue(maxRangePoint.range, maxRangePoint.unit)}` : '-',
desc: maxRangePoint ? t('DataCollection.HistoryData.analysisRangeValue', { value: formatValue(maxRangePoint.range, maxRangePoint.unit) }) : '-',
warn: maxRangePoint && maxRangePoint.range > 0
},
/*{title: '最小波动点位', value: '中层温度', desc: '波动 8.3℃'},*/
{
title: '最小波动点位',
title: t('DataCollection.HistoryData.analysisMinRangePoint'),
value: minRangePoint ? minRangePoint.name : '-',
desc: minRangePoint ? `波动 ${formatValue(minRangePoint.range, minRangePoint.unit)}` : '-'
desc: minRangePoint ? t('DataCollection.HistoryData.analysisRangeValue', { value: formatValue(minRangePoint.range, minRangePoint.unit) }) : '-'
},
/*{title: '', value: '', desc: ' 87.4'},
/!* { title: '平均稳定性评分', value: 86, desc: '基于超限次数和波动幅度' }*!/*/
{
title: '最高值点位',
title: t('DataCollection.HistoryData.analysisHighestPoint'),
value: maxValuePoint ? maxValuePoint.name : '-',
desc: maxValuePoint ? `最高 ${formatValue(maxValuePoint.max, maxValuePoint.unit)}` : '-',
desc: maxValuePoint ? t('DataCollection.HistoryData.analysisHighestValue', { value: formatValue(maxValuePoint.max, maxValuePoint.unit) }) : '-',
warn: maxValuePoint && maxValuePoint.max > 85 // 85
},
]
@ -854,7 +857,6 @@ function resetQuery() {
const formLoading = ref(false)
const emit = defineEmits(['success']) // success
const message = useMessage() //
const { t } = useI18n()
async function handleSaveAnalysis() {
//
formLoading.value = true
@ -883,7 +885,7 @@ async function handleSaveAnalysis() {
}
function handleExport() {
ElMessage.success('分析结果已生成导出任务')
ElMessage.success(t('DataCollection.HistoryData.analysisExportTaskMessage'))
}
function handleRefresh() {
@ -971,7 +973,7 @@ function handleStartTimeChange(value) {
const start = new Date(value)
const end = new Date(start.getTime() + 24 * 60 * 60 * 1000)
queryParams.endTime = formatDateTime(end)
ElMessage.warning('时间范围超过24小时已自动调整结束时间')
ElMessage.warning(t('DataCollection.HistoryData.analysisEndTimeAdjusted'))
}
}
}
@ -983,7 +985,7 @@ function handleEndTimeChange(value) {
const end = new Date(value)
const start = new Date(end.getTime() - 24 * 60 * 60 * 1000)
queryParams.startTime = formatDateTime(start)
ElMessage.warning('时间范围超过24小时已自动调整开始时间')
ElMessage.warning(t('DataCollection.HistoryData.analysisStartTimeAdjusted'))
}
}
}
@ -1015,7 +1017,7 @@ function initializeEmptyCharts() {
if (oneChartInstance) {
oneChartInstance.setOption({
title: {
text: '请选择分析条件后点击"开始分析"',
text: t('DataCollection.HistoryData.analysisEmptyChartText'),
left: 'center',
top: 'center',
textStyle: {
@ -1035,29 +1037,29 @@ function initializeEmptyCharts() {
historyData.value = []
summaryData.value = [
{
title: '已选点位数',
title: t('DataCollection.HistoryData.analysisSelectedPointCount'),
value: 0,
desc: '请先选择点位'
desc: t('DataCollection.HistoryData.analysisSelectPointFirst')
},
{
title: '分析时间跨度',
value: '未设置',
desc: '请设置开始和结束时间'
title: t('DataCollection.HistoryData.analysisTimeSpan'),
value: t('DataCollection.HistoryData.analysisNotSet'),
desc: t('DataCollection.HistoryData.analysisSetTimeRange')
},
{
title: '最大波动点位',
title: t('DataCollection.HistoryData.analysisMaxRangePoint'),
value: '-',
desc: '无数据'
desc: t('DataCollection.HistoryData.emptyDescription')
},
{
title: '最小波动点位',
title: t('DataCollection.HistoryData.analysisMinRangePoint'),
value: '-',
desc: '无数据'
desc: t('DataCollection.HistoryData.emptyDescription')
},
{
title: '最高值点位',
title: t('DataCollection.HistoryData.analysisHighestPoint'),
value: '-',
desc: '无数据'
desc: t('DataCollection.HistoryData.emptyDescription')
}
]
}

@ -32,7 +32,7 @@
:data="lineTreeData"
:props="{ label: 'code', value: 'code', children: 'children' }"
check-strictly
placeholder="请选择产线编码"
:placeholder="t('DataCollection.HistoryData.searchLineCodePlaceholder')"
clearable
filterable
class="!w-240px"
@ -44,7 +44,7 @@
:data="lineTreeData"
:props="{ label: 'name', value: 'name', children: 'children' }"
check-strictly
placeholder="请选择产线名称"
:placeholder="t('DataCollection.HistoryData.searchLineNamePlaceholder')"
clearable
filterable
class="!w-240px"
@ -150,7 +150,7 @@
<div class="mb-10px flex items-center justify-between text-sm text-gray-500">
<div>
<el-button type="primary" link @click="closeHistoryPage">
<Icon icon="ep:arrow-left" class="mr-5px" /> 返回历史记录列表
<Icon icon="ep:arrow-left" class="mr-5px" /> {{ t('DataCollection.HistoryData.backToList') }}
</el-button>
</div>
<div>
@ -231,7 +231,7 @@ const fetchLineTree = async () => {
const res = await DeviceLineApi.getDeviceLineTree()
lineTreeData.value = Array.isArray(res) ? res : (res?.data || res?.list || [])
} catch (e) {
console.error('获取产线树失败', e)
console.error(t('DataCollection.HistoryData.loadLineTreeFailed'), e)
} finally {
lineTreeLoading.value = false
}

@ -4,7 +4,7 @@
<div class="single-device-dialog__header">
<div>{{ t('DataCollection.RealTimeMonitoring.dialogDeviceNameLabel') }}{{ deviceName || '-' }}</div>
<div class="single-device-dialog__header-right">
<el-button :icon="Refresh" :loading="loading" size="small" @click="fetchList"></el-button>
<el-button :icon="Refresh" :loading="loading" size="small" @click="fetchList">{{t('common.Refresh')}}</el-button>
<span>{{ t('DataCollection.RealTimeMonitoring.dialogCollectionTimeLabel') }}{{ displayTime }}</span>
</div>
</div>

@ -31,7 +31,7 @@
:data="lineTreeData"
:props="{ label: 'code', value: 'code', children: 'children' }"
check-strictly
placeholder="请选择产线编码"
:placeholder="t('DataCollection.RealTimeMonitoring.searchLineCodePlaceholder')"
clearable
filterable
class="!w-240px"

@ -80,7 +80,7 @@ const calcPercent = (part: number, total: number) => {
const loadOverview = async () => {
try {
const res: any = await DashboardApi.getDeviceOperationalStatus({ orgId, deviceType: 2 })
const res: any = await DashboardApi.getDeviceOperationalStatus({ orgId, deviceType: 1 })
const data = (res && typeof res === 'object' ? (res.data ?? res) : {}) as any
const totalDevices = Number(data.totalDevices ?? 0)

Loading…
Cancel
Save