黄伟杰 5 hours ago
commit 8d92aa4f59

@ -15,6 +15,8 @@ export interface DeviceLedgerVO {
deviceSpec: string // 设备规格
deviceType: string | number // 设备类型
deviceLine?: string | number // 设备产线
topCategoryId?: number // 顶级分类 id
topCategoryName?: string // 顶级分类名称
deviceTypeName?: string // 设备类型名称
supplier: string // 供应商
workshop: string // 所属车间

@ -9,6 +9,7 @@
:disabled="disabled"
:drag="drag"
:http-request="httpRequest"
:accept="acceptTypes"
:limit="props.limit"
:multiple="props.limit > 1"
:on-error="excelUploadError"
@ -102,6 +103,18 @@ const nameMap = ref<Record<string, string>>({})
const { uploadUrl, httpRequest } = useUpload()
const acceptTypes = computed(() =>
props.fileType
.map((type: string) => {
const normalizedType = String(type).trim()
if (!normalizedType) return ''
if (normalizedType.startsWith('.') || normalizedType.includes('/')) return normalizedType
return `.${normalizedType}`
})
.filter(Boolean)
.join(',')
)
const emitUploadingChange = () => {
emit('uploading-change', uploadPendingCount.value > 0)
}

@ -1401,7 +1401,11 @@ export default {
actualCapacity: 'Actual Capacity',
Detail: {
invalidId: 'Invalid device ID'
}
},
DeviceQRCode: 'Device QR Code',
codeTitle:'Scan to view device details',
featureDescription:'SupportsSpotCheck/repair/maintenance',
BasicInformation:'Device Basic Information'
},
// Capacity Report
CapacityReport: {

@ -1391,7 +1391,11 @@ export default {
actualCapacity: '实际产能',
Detail: {
invalidId: '无效的设备ID'
}
},
DeviceQRCode: '设备二维码',
codeTitle:'扫码查看设备详情',
featureDescription:'支持点检 / 报修 / 维保',
BasicInformation:'设备基本信息'
},
// 产能报表
CapacityReport: {

@ -46,11 +46,8 @@
<!-- 左侧表单 -->
<div class="mold-operate-page__form">
<el-form ref="operateFormRef" :model="operateFormData" :rules="operateFormRules" label-width="100px">
<el-form-item :label="t('MoldManagement.MoldBrandPage.targetLine')" prop="lineId">
<el-select v-model="operateFormData.lineId" filterable clearable
:placeholder="t('MoldManagement.MoldBrandPage.placeholderTargetLine')" class="!w-full">
<el-option v-for="line in lineOptions" :key="line.id" :label="line.name" :value="String(line.id)" />
</el-select>
<el-form-item :label="t('MoldManagement.MoldBrandPage.targetLine')">
<el-input v-model="displayTopCategory" disabled :placeholder="'选择目标设备后自动显示'" class="!w-full" />
</el-form-item>
<el-form-item :label="t('MoldManagement.MoldBrandPage.targetDevice')" prop="deviceId">
<el-select v-model="operateFormData.deviceId" filterable clearable
@ -115,7 +112,7 @@
import { MoldBrandApi, type MoldBrandVO } from '@/api/erp/mold'
import { DeviceLedgerApi, type DeviceLedgerVO } from '@/api/mes/deviceledger'
import { MoldOperateApi, type MoldOperateVO } from '@/api/mes/moldoperate'
import { OrganizationApi, type OrganizationVO } from '@/api/mes/organization'
import { DeviceLineApi, type DeviceLineTreeVO } from '@/api/mes/deviceline'
import { getSimpleUserList, type UserVO } from '@/api/system/user'
import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
@ -148,16 +145,16 @@ const operateFormData = reactive({
remark: undefined as string | undefined
})
//
// 线
const operateFormRules = reactive({
lineId: [{ required: true, message: t('MoldManagement.MoldBrandPage.validatorTargetLineRequired'), trigger: 'blur' }],
deviceId: [{ required: true, message: t('MoldManagement.MoldBrandPage.validatorTargetDeviceRequired'), trigger: 'blur' }],
operateTime: [{ required: true, message: t('MoldManagement.MoldBrandPage.validatorOperateTimeRequired'), trigger: 'blur' }],
operatorId: [{ required: true, message: t('MoldManagement.MoldBrandPage.validatorOperatorRequired'), trigger: 'blur' }]
})
//
const lineOptions = ref<OrganizationVO[]>([])
const deviceLineTree = ref<DeviceLineTreeVO[]>([])
const displayTopCategory = ref('')
const operatorOptions = ref<UserVO[]>([])
//
@ -177,10 +174,10 @@ const getImageList = (images?: string) => {
const initOperateOptions = async () => {
try {
const data = await OrganizationApi.getOrganizationList()
lineOptions.value = Array.isArray(data) ? data : []
const data = await DeviceLineApi.getDeviceLineTree()
deviceLineTree.value = Array.isArray(data) ? data : []
} catch {
lineOptions.value = []
deviceLineTree.value = []
}
try {
operatorOptions.value = (await getSimpleUserList()) ?? []
@ -189,6 +186,34 @@ const initOperateOptions = async () => {
}
}
// 线
watch(() => operateFormData.deviceId, async (deviceId) => {
displayTopCategory.value = ''
if (!deviceId) return
try {
const detail = await DeviceLedgerApi.getDeviceLedger(deviceId)
if (detail?.deviceLine != null) {
operateFormData.lineId = detail.deviceLine
}
//
displayTopCategory.value = detail?.topCategoryName || ''
// fallback: 线
if (!displayTopCategory.value && detail?.deviceLine != null && deviceLineTree.value.length) {
const lineId = Number(detail.deviceLine)
const containsId = (nodes: DeviceLineTreeVO[] | undefined, id: number): boolean => {
if (!nodes) return false
return nodes.some((n) => n.id === id || containsId(n.children, id))
}
for (const root of deviceLineTree.value) {
if (root.id === lineId || containsId(root.children, lineId)) {
displayTopCategory.value = root.name
break
}
}
}
} catch {}
})
const fetchRecentOperateList = async () => {
if (!props.mold?.id) return
recentLoading.value = true
@ -217,6 +242,7 @@ const submitOperateForm = async () => {
operateType: props.type,
moldId: props.mold?.id,
lineId: operateFormData.lineId,
lineName: displayTopCategory.value,
deviceId: operateFormData.deviceId,
remark: operateFormData.remark,
operateTime: operateFormData.operateTime,
@ -240,6 +266,7 @@ const open = async () => {
operateFormData.operateTime = undefined
operateFormData.operatorId = undefined
operateFormData.remark = undefined
displayTopCategory.value = ''
await initOperateOptions()
recentPageNo.value = 1
await fetchRecentOperateList()

@ -2,7 +2,7 @@
<div ref="detailPageRef" class="device-ledger-detail-page">
<ContentWrap>
<div v-loading="detailLoading" class="device-ledger-detail-body">
<div class="device-ledger-detail-title">设备基本信息</div>
<div class="device-ledger-detail-title">{{t('EquipmentManagement.EquipmentLedger.BasicInformation')}}</div>
<div class="device-ledger-detail-main" >
<el-form
ref="formRef"
@ -176,6 +176,8 @@
<UploadFile
:is-show-tip="false"
v-model="formData.fileUrl"
:file-type="materialFileTypes"
:file-size="100"
:limit="9"
@uploading-change="handleFileUploadingChange"
/>
@ -426,8 +428,6 @@ const parseIdsValue = (value: any): number[] => {
const selectedRows = ref<any[]>([]) //
const bjSelectedRows = ref<any[]>([]) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
code: undefined as string | undefined,
name: undefined as string | undefined,
description: undefined as string | undefined,
@ -734,6 +734,25 @@ const formLoading = ref(false)
const fileUploading = ref(false)
const formType = ref('update')
const formRef = ref()
const materialFileTypes = [
'png',
'jpg',
'jpeg',
'webp',
'doc',
'docx',
'xls',
'xlsx',
'ppt',
'pptx',
'pdf',
'zip',
'rar',
'7z',
'dwg',
'dxf',
'mp4'
]
const formData = ref<any>({
...initFormData()
})
@ -1442,5 +1461,3 @@ onBeforeUnmount(() => {

@ -89,7 +89,7 @@
</el-descriptions>
<div v-if="detailData?.qrcodeUrl" class="device-ledger-detail-qrcode">
<div class="device-ledger-detail-qrcode-title">设备二维码</div>
<div class="device-ledger-detail-qrcode-title">{{t('EquipmentManagement.EquipmentLedger.DeviceQRCode')}}</div>
<QrcodeActionCard
:image-url="detailData.qrcodeUrl"
:print-id="detailData.id || detailData.deviceCode"
@ -104,8 +104,8 @@
:print-data="buildDetailPrintData()"
:show-refresh="false"
/>
<div class="device-ledger-detail-qrcode-desc">扫码查看设备详情</div>
<div class="device-ledger-detail-qrcode-sub">支持点检 / 报修 / 维保</div>
<div class="device-ledger-detail-qrcode-desc">{{t('EquipmentManagement.EquipmentLedger.codeTitle')}}</div>
<div class="device-ledger-detail-qrcode-sub">{{t('EquipmentManagement.EquipmentLedger.featureDescription')}}</div>
</div>
</div>
@ -1128,14 +1128,14 @@ onMounted(async () => {
.device-ledger-detail-qrcode-desc {
margin-top: 12px;
color: var(--el-text-color-regular);
font-size: 13px;
font-size: 11px;
line-height: 20px;
}
.device-ledger-detail-qrcode-sub {
margin-top: 2px;
color: var(--el-text-color-secondary);
font-size: 12px;
font-size: 9px;
line-height: 18px;
}

@ -26,10 +26,8 @@
@click="openMoldSelectDialog"
/>
</el-form-item>
<el-form-item :label="t('MoldManagement.MoldOperate.productionLine')" prop="lineId">
<el-select v-model="formData.lineId" filterable clearable :placeholder="t('MoldManagement.MoldOperate.placeholderProductionLine')" class="!w-full">
<el-option v-for="line in lineOptions" :key="line.id" :label="line.name" :value="line.id" />
</el-select>
<el-form-item :label="t('MoldManagement.MoldOperate.productionLine')" prop="lineId" v-if="formData.operateType != null">
<el-input :model-value="displayLineName" readonly />
</el-form-item>
<el-form-item :label="t('MoldManagement.MoldOperate.operateTime')" prop="operateTime">
<el-date-picker
@ -113,7 +111,7 @@
import { MoldOperateApi } from '@/api/mes/moldoperate'
import { MoldBrandApi, MoldVO } from '@/api/erp/mold'
import { DeviceLedgerApi, DeviceLedgerVO } from '@/api/mes/deviceledger'
import { OrganizationApi, OrganizationVO } from '@/api/mes/organization'
import { DeviceLineApi, DeviceLineTreeVO } from '@/api/mes/deviceline'
import { getSimpleUserList, type UserVO } from '@/api/system/user'
import TableSelectDialog from '@/components/TableSelectDialog/TableSelectDialog.vue'
@ -123,7 +121,8 @@ defineOptions({ name: 'MoldOperateForm' })
const { t } = useI18n() //
const message = useMessage() //
const lineOptions = ref<OrganizationVO[]>([])
const deviceLineTree = ref<DeviceLineTreeVO[]>([])
const displayTopCategory = ref('')
const operatorOptions = ref<UserVO[]>([])
const initializingOperateType = ref(false)
@ -169,7 +168,6 @@ const validateMoldId = (_rule: any, value: any, callback: (error?: Error) => voi
const formRules = reactive({
operateType: [{ required: true, message: t('MoldManagement.MoldOperate.validatorOperateTypeRequired'), trigger: 'blur' }],
deviceId: [{ required: true, message: t('MoldManagement.MoldOperate.validatorDeviceRequired'), trigger: 'change' }],
lineId: [{ required: true, message: t('MoldManagement.MoldOperate.validatorProductionLineRequired'), trigger: 'change' }],
moldId: [{ required: true, validator: validateMoldId, trigger: 'change' }],
operateTime: [{ required: true, message: t('MoldManagement.MoldOperate.validatorOperateTimeRequired'), trigger: 'blur' }],
operatorId: [{ required: true, message: t('MoldManagement.MoldOperate.validatorOperatorRequired'), trigger: 'change' }],
@ -265,6 +263,7 @@ const submitForm = async () => {
deviceId: raw.deviceId,
moldId: raw.moldId,
lineId: raw.lineId,
lineName: displayTopCategory.value, // 线
operateTime: raw.operateTime,
operatorId: raw.operatorId,
remark: raw.remark,
@ -292,12 +291,12 @@ const submitForm = async () => {
}
const ensureLineOptionsLoaded = async () => {
if (lineOptions.value.length) return
if (deviceLineTree.value.length) return
try {
const data = await OrganizationApi.getOrganizationList()
lineOptions.value = Array.isArray(data) ? data : []
const data = await DeviceLineApi.getDeviceLineTree()
deviceLineTree.value = Array.isArray(data) ? data : []
} catch {
lineOptions.value = []
deviceLineTree.value = []
}
try {
operatorOptions.value = (await getSimpleUserList()) ?? []
@ -347,6 +346,7 @@ const resetForm = () => {
selectedMoldRows.value = []
ids.value = []
moldIds.value = []
displayTopCategory.value = ''
formRef.value?.resetFields()
}
const deviceColumns = [
@ -377,12 +377,42 @@ const fetchMoldBrandPage = async (params: Record<string, any>) => {
}
const selectedDeviceRows = ref<any[]>([])
const selectedMoldRows = ref<any[]>([])
const handleDeviceSelectConfirm = (payload: { ids: (number | string)[]; rows: any[] }) => {
const handleDeviceSelectConfirm = async (payload: { ids: (number | string)[]; rows: any[] }) => {
const row = payload.rows[0]
if (row) {
selectedDeviceRows.value = [row]
formData.value.deviceId = Number(row.id)
ids.value = [Number(row.id)]
const deviceId = Number(row.id)
formData.value.deviceId = deviceId
ids.value = [deviceId]
// deviceLine
try {
const detail = await DeviceLedgerApi.getDeviceLedger(deviceId)
console.log('设备详情:', { detail, topCategoryName: detail?.topCategoryName, topCategoryId: detail?.topCategoryId, deviceLine: detail?.deviceLine })
if (detail?.deviceLine != null) {
formData.value.lineId = detail.deviceLine
}
//
displayTopCategory.value = detail?.topCategoryName || ''
// fallback: 线DeviceLineTree
if (!displayTopCategory.value && detail?.deviceLine != null && deviceLineTree.value.length) {
const lineId = Number(detail.deviceLine)
console.log('fallback 匹配: deviceLine=', lineId, '产线树根数=', deviceLineTree.value.length)
//
const containsId = (nodes: DeviceLineTreeVO[] | undefined, id: number): boolean => {
if (!nodes) return false
return nodes.some((n) => n.id === id || containsId(n.children, id))
}
for (const root of deviceLineTree.value) {
if (root.id === lineId || containsId(root.children, lineId)) {
displayTopCategory.value = root.name
console.log('产线匹配成功:', root.name)
break
}
}
}
} catch (e) {
console.error('获取设备详情失败', e)
}
} else {
selectedDeviceRows.value = []
formData.value.deviceId = undefined
@ -481,6 +511,10 @@ const moldDisplayText = computed(() => {
return map.get(formData.value.moldId) || ''
})
const displayLineName = computed(() => displayTopCategory.value)
const openCriticalComponentDialog = async () => {
searchParams.deviceCode=''

Loading…
Cancel
Save