You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
besure_web/src/views/mes/dvrepair/DvRepairForm.vue

1105 lines
38 KiB
Vue

<template>
<div class="dv-repair-panel">
<div class="dv-repair-panel__header">
<div class="dv-repair-panel__title">{{ dialogTitle }}</div>
<el-button text @click="closeForm">
<Icon icon="ep:close" />
</el-button>
</div>
<div class="dv-repair-dialog" v-loading="formLoading">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px">
<section class="dv-repair-section">
<div class="dv-repair-section__title">基本信息</div>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.repairCode')" prop="repairCode">
<template #label>
<span class="dv-repair-label">
{{ t('EquipmentManagement.DvRepair.repairCode') }}
<el-tooltip :content="t('EquipmentManagement.DvRepair.repairCode')" placement="top">
<Icon icon="ep:question-filled" />
</el-tooltip>
</span>
</template>
<div class="dv-repair-code-row">
<el-input
v-model="formData.repairCode"
:disabled="repairCodeDisabled"
:placeholder="t('common.code')"
/>
<el-switch
v-model="formData.isCode"
:disabled="formType === 'update' || formType === 'repair' || formType === 'detail'"
/>
</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.repairName')" prop="repairName" required>
<el-input
v-model="formData.repairName"
:placeholder="t('EquipmentManagement.DvRepair.placeholderRepairName')"
:disabled="formType === 'repair' || isDetailMode"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.requireDate')" prop="requireDate" required>
<el-date-picker
v-model="formData.requireDate"
type="date"
value-format="x"
:placeholder="t('EquipmentManagement.DvRepair.placeholderRequireDate')"
class="!w-full"
:disabled="formType === 'repair' || isDetailMode"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.acceptedBy')" prop="acceptedBy">
<el-select
v-model="formData.acceptedBy"
filterable
clearable
:placeholder="t('EquipmentManagement.DvRepair.placeholderAcceptedBy')"
class="!w-full"
:disabled="isRepairMetaReadonly"
>
<el-option v-for="item in users" :key="String(item.id)" :label="item.nickname" :value="String(item.id)" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.confirmBy')" prop="confirmBy">
<el-select
v-model="formData.confirmBy"
filterable
clearable
:placeholder="t('EquipmentManagement.DvRepair.placeholderConfirmBy')"
:disabled="isRepairMetaReadonly"
>
<el-option v-for="item in users" :key="String(item.id)" :label="item.nickname" :value="String(item.id)" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.status')">
<el-input :model-value="statusLabel" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.faultLevel')" prop="faultLevel">
<template #label>
<span class="dv-repair-label is-required">
{{ t('EquipmentManagement.DvRepair.faultLevel') }}
</span>
</template>
<el-radio-group v-model="formData.faultLevel" :disabled="isRepairMetaReadonly">
<el-radio v-for="dict in getStrDictOptions(DICT_TYPE.FAILURE_LEVEL)" :key="String(dict.value)" :label="String(dict.value)">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.isShutdown')" prop="isShutdown">
<template #label>
<span class="dv-repair-label is-required">
{{ t('EquipmentManagement.DvRepair.isShutdown') }}
</span>
</template>
<el-radio-group v-model="formData.isShutdown" :disabled="isRepairMetaReadonly">
<el-radio :label="true">{{ t('common.yes') }}</el-radio>
<el-radio :label="false">{{ t('common.no') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</section>
<section class="dv-repair-section">
<div class="dv-repair-section__title">维修对象</div>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.machineryTypeId')" prop="machineryTypeId">
<el-radio-group v-model="formData.machineryTypeId" :disabled="isBaseInfoReadonly">
<el-radio :label="1">{{ t('EquipmentManagement.DvRepair.machineryTypeDevice') }}</el-radio>
<el-radio :label="2">{{ t('EquipmentManagement.DvRepair.machineryTypeKeyItem') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.device')" prop="deviceId">
<el-select
v-model="formData.deviceId"
filterable
clearable
:loading="deviceLoading"
:disabled="isBaseInfoReadonly"
:placeholder="t('EquipmentManagement.DvRepair.placeholderDevice')"
class="!w-full"
>
<el-option v-for="opt in deviceOptions" :key="String(opt.value)" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
</el-col>
<el-col v-if="showComponentSelect" :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.component')" prop="componentId">
<el-select
v-model="formData.componentId"
clearable
:loading="componentLoading"
:disabled="isBaseInfoReadonly"
:placeholder="t('EquipmentManagement.DvRepair.placeholderComponent')"
class="!w-full"
>
<el-option v-for="opt in componentOptions" :key="String(opt.value)" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</section>
<section class="dv-repair-section">
<div class="dv-repair-section__title">故障信息</div>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item :label="t('EquipmentManagement.DvRepair.faultPhenomenon')" prop="faultPhenomenon">
<template #label>
<span class="dv-repair-label is-required">
{{ t('EquipmentManagement.DvRepair.faultPhenomenon') }}
</span>
</template>
<el-input
v-model="formData.faultPhenomenon"
:placeholder="t('EquipmentManagement.DvRepair.placeholderFaultPhenomenon')"
:disabled="isFaultInfoReadonly"
:validate-event="false"
/>
</el-form-item>
<el-form-item :label="t('EquipmentManagement.DvRepair.faultDescription')" prop="faultDescription">
<el-input
v-model="formData.faultDescription"
type="textarea"
:rows="4"
:placeholder="t('EquipmentManagement.DvRepair.placeholderFaultDescription')"
:disabled="isFaultInfoReadonly"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item :label="t('EquipmentManagement.DvRepair.faultImages')">
<UploadImgs
v-model="faultImagesValue"
:limit="9"
:disabled="isFaultInfoReadonly"
width="120px"
height="120px"
class="dv-repair-upload"
>
<template #tip>
<span>最多上传 9 张现场图片</span>
</template>
</UploadImgs>
</el-form-item>
</el-col>
</el-row>
</section>
<section v-if="showRepairSection" class="dv-repair-section">
<div class="dv-repair-section__title">处理结果</div>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.repairResult')" prop="repairStatus">
<template #label>
<span class="dv-repair-label" :class="{ 'is-required': formType === 'repair' }">
{{ t('EquipmentManagement.DvRepair.repairResult') }}
</span>
</template>
<el-select
v-model="formData.repairStatus"
class="!w-full"
:placeholder="t('EquipmentManagement.DvRepair.placeholderRepairResult')"
:disabled="repairFieldsDisabled"
>
<el-option :label="t('EquipmentManagement.DvRepair.repairResultOk')" :value="1" />
<el-option :label="t('EquipmentManagement.DvRepair.repairResultNg')" :value="2" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.finishDate')" prop="finishDate">
<template #label>
<span class="dv-repair-label" :class="{ 'is-required': formType === 'repair' }">
{{ t('EquipmentManagement.DvRepair.finishDate') }}
</span>
</template>
<el-date-picker
v-model="formData.finishDate"
type="date"
value-format="x"
:placeholder="t('EquipmentManagement.DvRepair.placeholderFinishDate')"
class="!w-full"
:disabled="repairFieldsDisabled"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.confirmDate')" prop="confirmDate">
<template #label>
<span class="dv-repair-label" :class="{ 'is-required': formType === 'repair' }">
{{ t('EquipmentManagement.DvRepair.confirmDate') }}
</span>
</template>
<el-date-picker
v-model="formData.confirmDate"
type="date"
value-format="x"
:placeholder="t('EquipmentManagement.DvRepair.placeholderConfirmDate')"
class="!w-full"
:disabled="repairFieldsDisabled"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.downtimeDuration')" prop="downtimeDuration">
<el-input
v-model="formData.downtimeDuration"
:placeholder="t('EquipmentManagement.DvRepair.placeholderDowntimeDuration')"
:disabled="repairFieldsDisabled"
>
<template #append>小时</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.faultReason')" prop="faultReason">
<el-input
v-model="formData.faultReason"
:placeholder="t('EquipmentManagement.DvRepair.placeholderFaultReason')"
:disabled="repairFieldsDisabled"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.handlingMeasures')" prop="handlingMeasures">
<el-input
v-model="formData.handlingMeasures"
:placeholder="t('EquipmentManagement.DvRepair.placeholderHandlingMeasures')"
:disabled="repairFieldsDisabled"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item :label="t('EquipmentManagement.DvRepair.replacementParts')" prop="replacementParts">
<el-input
v-model="formData.replacementParts"
:placeholder="t('EquipmentManagement.DvRepair.placeholderReplacementParts')"
:disabled="repairFieldsDisabled"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="14">
<el-form-item :label="t('EquipmentManagement.DvRepair.repairContent')" prop="repairContent">
<el-input
v-model="formData.repairContent"
type="textarea"
:rows="4"
:maxlength="1000"
show-word-limit
:placeholder="t('EquipmentManagement.DvRepair.placeholderRepairContent')"
:disabled="repairFieldsDisabled"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item :label="t('EquipmentManagement.DvRepair.repairedImages')">
<UploadImgs
v-model="repairedImagesValue"
:limit="9"
:disabled="repairFieldsDisabled"
width="120px"
height="120px"
class="dv-repair-upload"
>
<template #tip>
<span>最多上传 9 张维修后图片</span>
</template>
</UploadImgs>
</el-form-item>
</el-col>
</el-row>
</section>
<section class="dv-repair-section">
<div class="dv-repair-section__title">创建备注 / 补充说明</div>
<el-form-item prop="remark" label-width="0">
<el-input
v-model="formData.remark"
type="textarea"
:rows="3"
maxlength="300"
show-word-limit
:placeholder="t('EquipmentManagement.DvRepair.placeholderRemark')"
:disabled="formType === 'repair' || isDetailMode"
/>
</el-form-item>
</section>
</el-form>
<!-- 子表的表单
<el-tabs v-model="subTabsName">
<el-tab-pane :label="t('EquipmentManagement.DvRepair.repairLineTitle')" name="dvRepairLine">
<DvRepairLineForm ref="dvRepairLineFormRef" :repair-id="formData.id" :line-mode="lineMode" />
</el-tab-pane>
</el-tabs>
-->
</div>
<div class="dv-repair-footer">
<el-button @click="closeForm">{{ t('common.cancel') }}</el-button>
<el-button v-if="!isDetailMode" @click="submitForm" type="primary" :disabled="formLoading">
{{ submitButtonText }}
</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { DvRepairApi, DvRepairVO } from '@/api/mes/dvrepair'
import { DeviceLedgerApi, DeviceLedgerVO } from '@/api/mes/deviceledger'
import { RepairItemsApi } from '@/api/mes/repairItems'
import { getSimpleUserList, UserVO } from '@/api/system/user'
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
/** 设备维修记录 表单 */
defineOptions({ name: 'DvRepairForm' })
type RepairFormType = 'create' | 'update' | 'repair' | 'detail' | ''
interface DvRepairFormData extends Partial<DvRepairVO> {
deviceId?: number
componentId?: number
isShutdown?: boolean
isCode?: boolean
faultLevel?: string
repairStatus?: string
faultPhenomenon?: string
faultDescription?: string
faultImages?: string
downtimeDuration?: string
faultReason?: string
handlingMeasures?: string
replacementParts?: string
repairContent?: string
repairedImages?: string
}
const { t } = useI18n()
const message = useMessage()
const createDefaultFormData = (): DvRepairFormData => ({
id: undefined,
repairCode: undefined,
repairName: undefined,
deviceId: undefined,
componentId: undefined,
machineryId: undefined,
machineryCode: undefined,
machineryName: undefined,
machineryBrand: undefined,
machinerySpec: undefined,
machineryTypeId: 1,
requireDate: undefined,
finishDate: undefined,
confirmDate: undefined,
repairResult: '',
repairStatus: '',
faultPhenomenon: undefined,
faultDescription: undefined,
faultImages: '',
downtimeDuration: undefined,
faultReason: undefined,
handlingMeasures: undefined,
replacementParts: undefined,
repairContent: undefined,
repairedImages: '',
acceptedBy: undefined,
confirmBy: undefined,
faultLevel: undefined,
isShutdown: undefined,
status: undefined,
remark: undefined,
isCode: true
})
const dialogTitle = ref('')
const formLoading = ref(false)
const formType = ref<RepairFormType>('')
const isHydrating = ref(false)
const emit = defineEmits(['success', 'closed'])
const formData = ref<DvRepairFormData>(createDefaultFormData())
const isDetailMode = computed(() => formType.value === 'detail')
const repairCodeDisabled = computed(() => formData.value.isCode === true || formType.value === 'update' || formType.value === 'repair' || formType.value === 'detail')
const isBaseInfoReadonly = computed(() => formType.value === 'repair' || formType.value === 'detail')
const isRepairMetaReadonly = computed(() => formType.value === 'detail')
const isFaultInfoReadonly = computed(() => formType.value === 'repair' || formType.value === 'detail')
const currentStatusValue = computed(() => {
const value = formData.value.status
return value === '' || value === null || value === undefined ? '' : String(value)
})
const currentRepairStatusValue = computed(() => {
const value = formData.value.repairStatus
return value === '' || value === null || value === undefined ? '0' : String(value)
})
const showRepairSection = computed(() => {
if (formType.value === 'repair') return true
if (formType.value === 'update') return currentStatusValue.value === '1'
if (formType.value === 'detail') return currentRepairStatusValue.value !== '0'
return false
})
const repairFieldsDisabled = computed(() => formType.value !== 'repair')
const submitButtonText = computed(() => {
if (formType.value === 'create') return '保存'
if (formType.value === 'repair') return '保存并提交'
return t('common.ok')
})
const statusLabel = computed(() => {
const status = formData.value.status
if (status === '' || status === null || status === undefined) return t('EquipmentManagement.DvRepair.statusPending')
return String(status) === '1'
? t('EquipmentManagement.DvRepair.statusFinished')
: t('EquipmentManagement.DvRepair.statusPending')
})
const showComponentSelect = computed(() => formData.value.machineryTypeId === 2)
const deviceLoading = ref(false)
const deviceOptions = ref<{ label: string; value: number; raw?: DeviceLedgerVO }[]>([])
const deviceOptionsLoaded = ref(false)
const componentLoading = ref(false)
const componentOptions = ref<{ label: string; value: number }[]>([])
const users = ref<UserVO[]>([])
const splitImageValue = (value: unknown) => {
if (Array.isArray(value)) return value.map((item) => String(item).trim()).filter(Boolean)
if (typeof value !== 'string') return []
return value
.split(',')
.map((item) => item.trim())
.filter(Boolean)
}
const normalizeImageString = (value: unknown) => splitImageValue(value).join(',')
const normalizeRepairStatusValue = (value: unknown) => {
if (value === undefined || value === null || value === '') return ''
const num = Number(value)
if (!Number.isNaN(num) && num === 0) return ''
return Number.isNaN(num) ? value : num
}
const faultImagesValue = computed<string[]>({
get: () => splitImageValue(formData.value.faultImages),
set: (value) => {
formData.value.faultImages = value.join(',')
}
})
const repairedImagesValue = computed<string[]>({
get: () => splitImageValue(formData.value.repairedImages),
set: (value) => {
formData.value.repairedImages = value.join(',')
}
})
const ensureUsersLoaded = async () => {
if (users.value.length) return
users.value = (await getSimpleUserList()) ?? []
}
const normalizeUserId = (value: any) => {
if (value === undefined || value === null || value === '') return undefined
const str = String(value)
if (/^\d+$/.test(str)) return str
const matched = users.value.find((u) => u.nickname === str)
return matched ? String(matched.id) : str
}
const upsertDeviceOption = (
options: { label: string; value: number; raw?: DeviceLedgerVO }[],
option: { label: string; value: number; raw?: DeviceLedgerVO }
) => {
const existed = options.some((item) => item.value === option.value)
return existed ? options : [option, ...options]
}
const toComponentOption = (item: any) => {
const id = typeof item?.id === 'number' ? item.id : Number(item?.id)
if (Number.isNaN(id)) return undefined
const code = item?.code ?? item?.componentCode ?? item?.subjectCode
const name = item?.name ?? item?.componentName ?? item?.subjectName
const label = `${code ?? ''} ${name ?? ''}`.trim() || String(id)
return { label, value: id }
}
const loadComponentOptionsByDeviceId = async (deviceId: number) => {
componentLoading.value = true
try {
const data = await RepairItemsApi.getComponentList(deviceId)
const rows = (Array.isArray(data) ? data : data?.list ?? data?.data ?? []) as any[]
componentOptions.value = rows.map(toComponentOption).filter(Boolean) as { label: string; value: number }[]
} finally {
componentLoading.value = false
}
}
const ensureDeviceOptionsLoaded = async () => {
if (deviceOptionsLoaded.value) return
deviceLoading.value = true
try {
const data = await DeviceLedgerApi.getDeviceLedgerList()
const rows = (Array.isArray(data) ? data : data?.list ?? data?.data ?? []) as DeviceLedgerVO[]
deviceOptions.value = rows
.filter((item) => typeof item?.id === 'number')
.map((item) => ({
label: `${item.deviceCode ?? ''} ${item.deviceName ?? ''}`.trim(),
value: item.id,
raw: item
}))
deviceOptionsLoaded.value = true
} finally {
deviceLoading.value = false
}
}
watch(
() => formData.value.machineryTypeId,
() => {
if (isHydrating.value) return
formData.value.deviceId = undefined
formData.value.componentId = undefined
formData.value.machineryId = undefined
formData.value.machineryCode = undefined
formData.value.machineryName = undefined
formData.value.machineryBrand = undefined
formData.value.machinerySpec = undefined
componentOptions.value = []
setLineRows([])
nextTick(() => {
formRef.value?.clearValidate?.(['deviceId', 'componentId', 'machineryCode', 'machineryName'])
})
}
)
watch(
() => formData.value.deviceId,
async (deviceId) => {
if (isHydrating.value) return
formData.value.componentId = undefined
componentOptions.value = []
if (typeof deviceId !== 'number') {
setLineRows([])
return
}
const option = deviceOptions.value.find((item) => item.value === deviceId)
if (option?.raw) {
formData.value.machineryCode = option.raw.deviceCode
formData.value.machineryName = option.raw.deviceName
formData.value.machinerySpec = option.raw.deviceSpec
formData.value.machineryBrand = option.raw.deviceBrand
}
if (formData.value.machineryTypeId === 2) {
await loadComponentOptionsByDeviceId(deviceId)
}
if (formData.value.machineryTypeId === 1) {
await prefillLinesByDeviceOrComponent({ deviceId, deviceType: 1 })
}
}
)
watch(
() => formData.value.componentId,
async (componentId) => {
if (isHydrating.value) return
if (formData.value.machineryTypeId !== 2) return
const deviceId = formData.value.deviceId
if (typeof deviceId !== 'number' || typeof componentId !== 'number') {
setLineRows([])
return
}
await prefillLinesByDeviceOrComponent({ deviceId, componentId, deviceType: 2 })
}
)
const formRules = reactive({
repairCode: [
{
validator: (_: any, value: any, callback: any) => {
if (formType.value === 'update') {
callback()
return
}
if (formData.value.isCode === true) {
callback()
return
}
if (value === undefined || value === null || String(value).trim() === '') {
callback(new Error(t('EquipmentManagement.DvRepair.validatorRepairCodeRequired')))
return
}
callback()
},
trigger: 'blur'
}
],
repairName: [{ required: true, message: t('EquipmentManagement.DvRepair.validatorRepairNameRequired'), trigger: 'blur' }],
deviceId: [{ required: true, message: t('EquipmentManagement.DvRepair.validatorMachineryIdRequired'), trigger: 'change' }],
componentId: [
{
validator: (_: any, value: any, callback: any) => {
if (formData.value.machineryTypeId !== 2) {
callback()
return
}
if (value === undefined || value === null || value === '') {
callback(new Error(t('EquipmentManagement.DvRepair.validatorComponentRequired')))
return
}
callback()
},
trigger: 'change'
}
],
machineryCode: [{ required: true, message: t('EquipmentManagement.DvRepair.validatorMachineryCodeRequired'), trigger: 'blur' }],
machineryName: [{ required: true, message: t('EquipmentManagement.DvRepair.validatorMachineryNameRequired'), trigger: 'blur' }],
machineryTypeId: [{ required: true, message: t('EquipmentManagement.DvRepair.validatorMachineryTypeIdRequired'), trigger: 'change' }],
faultPhenomenon: [
{
validator: (_: any, value: any, callback: any) => {
if (value === undefined || value === null || String(value).trim() === '') {
callback(new Error(t('EquipmentManagement.DvRepair.validatorFaultPhenomenonRequired')))
return
}
callback()
}
}
],
requireDate: [
{
validator: (_: any, value: any, callback: any) => {
if (!value) {
callback(new Error(t('EquipmentManagement.DvRepair.validatorRequireDateRequired')))
return
}
callback()
},
trigger: 'change'
}
],
faultLevel: [
{
validator: (_: any, value: any, callback: any) => {
if (value === undefined || value === null || String(value).trim() === '') {
callback(new Error(t('EquipmentManagement.DvRepair.validatorFaultLevelRequired')))
return
}
callback()
},
trigger: 'change'
}
],
isShutdown: [
{
validator: (_: any, value: any, callback: any) => {
if (value === undefined || value === null || value === '') {
callback(new Error(t('EquipmentManagement.DvRepair.validatorIsShutdownRequired')))
return
}
callback()
},
trigger: 'change'
}
],
finishDate: [
{
validator: (_: any, value: any, callback: any) => {
if (formType.value === 'repair' && !value) {
callback(new Error(t('EquipmentManagement.DvRepair.validatorFinishDateRequired')))
return
}
callback()
},
trigger: 'change'
}
],
confirmDate: [
{
validator: (_: any, value: any, callback: any) => {
if (formType.value === 'repair' && !value) {
callback(new Error(t('EquipmentManagement.DvRepair.validatorConfirmDateRequired')))
return
}
callback()
},
trigger: 'change'
}
],
repairStatus: [
{
validator: (_: any, value: any, callback: any) => {
if (formType.value === 'repair' && !value) {
callback(new Error(t('EquipmentManagement.DvRepair.validatorRepairResultRequired')))
return
}
callback()
},
trigger: 'change'
}
]
})
const formRef = ref()
watch(
() => formData.value.isCode,
(value) => {
if (value === true) {
formData.value.repairCode = undefined
}
formRef.value?.clearValidate?.(['repairCode'])
}
)
const subTabsName = ref('dvRepairLine')
const dvRepairLineFormRef = ref()
const normalizeDeviceOrComponentList = (data: any) => {
return (Array.isArray(data) ? data : data?.list ?? data?.data ?? []) as any[]
}
const toDvRepairLineRow = (item: any) => {
const subjectId = item?.subjectId ?? item?.id
return {
id: undefined,
repairId: formData.value.id,
subjectId,
subjectCode: item?.subjectCode ?? item?.subject_code ?? item?.code,
subjectName: item?.subjectName ?? item?.subject_name ?? item?.name,
subjectType: item?.subjectType ?? item?.subject_type ?? item?.type,
subjectContent: item?.subjectContent ?? item?.projectContent ?? item?.subject_content,
subjectStandard: item?.subjectStandard ?? item?.judgmentCriteria ?? item?.subject_standard,
malfunction: undefined,
malfunctionUrl: undefined,
repairDes: undefined,
remark: undefined
}
}
const setLineRows = (rows: any[]) => {
dvRepairLineFormRef.value?.setData(rows)
}
const prefillLinesByDeviceOrComponent = async (params: { deviceId: number; componentId?: number; deviceType: number }) => {
if (formType.value !== 'create') return
const data = await RepairItemsApi.getDeviceOrComponentList(params)
const list = normalizeDeviceOrComponentList(data)
setLineRows(list.map(toDvRepairLineRow))
}
let openRequestId = 0
const open = async (type: string, id?: number) => {
const currentOpenId = ++openRequestId
dialogTitle.value = type === 'repair' ? '维修处理结果' : t('action.' + type)
formType.value = type as RepairFormType
resetForm()
await ensureUsersLoaded()
await ensureDeviceOptionsLoaded()
if (currentOpenId !== openRequestId) return
if (id) {
formLoading.value = true
try {
isHydrating.value = true
const detail = await DvRepairApi.getDvRepair(id)
formData.value = {
...createDefaultFormData(),
...detail,
requireDate: detail?.requireDate ?? undefined,
finishDate: detail?.finishDate ?? undefined,
confirmDate: detail?.confirmDate ?? undefined,
repairResult: detail?.repairResult ?? '',
repairStatus: detail?.repairStatus === undefined || detail?.repairStatus === null || detail?.repairStatus === ''
? normalizeRepairStatusValue(detail?.repairResult)
: normalizeRepairStatusValue(detail?.repairStatus),
faultImages: normalizeImageString(detail?.faultImages),
repairedImages: normalizeImageString(detail?.repairedImages),
acceptedBy: normalizeUserId(detail?.acceptedBy),
confirmBy: normalizeUserId(detail?.confirmBy),
faultLevel: detail?.faultLevel === undefined || detail?.faultLevel === null ? undefined : String(detail?.faultLevel)
}
if (currentOpenId !== openRequestId) return
const typeId = typeof formData.value.machineryTypeId === 'number'
? formData.value.machineryTypeId
: Number(formData.value.machineryTypeId)
formData.value.machineryTypeId = Number.isNaN(typeId) ? formData.value.machineryTypeId : typeId
const deviceId = formData.value.deviceId
const resolvedDeviceId = typeof deviceId === 'number' ? deviceId : formData.value.machineryId
if (typeof resolvedDeviceId === 'number') {
formData.value.deviceId = resolvedDeviceId
const label = `${formData.value.machineryCode ?? ''} ${formData.value.machineryName ?? ''}`.trim() || `ID:${resolvedDeviceId}`
deviceOptions.value = upsertDeviceOption(deviceOptions.value, { label, value: resolvedDeviceId })
}
if (formData.value.machineryTypeId === 2 && typeof formData.value.deviceId === 'number') {
await loadComponentOptionsByDeviceId(formData.value.deviceId)
const componentId = typeof formData.value.componentId === 'number'
? formData.value.componentId
: Number(formData.value.componentId)
if (!Number.isNaN(componentId)) {
formData.value.componentId = componentId
}
}
await nextTick()
formRef.value?.clearValidate?.()
} finally {
isHydrating.value = false
formLoading.value = false
}
} else {
await nextTick()
formRef.value?.clearValidate?.()
}
}
defineExpose({ open })
const closeForm = () => {
openRequestId++
emit('closed')
}
onBeforeUnmount(() => {
openRequestId++
})
const submitForm = async () => {
await formRef.value.validate()
if (dvRepairLineFormRef.value) {
try {
await dvRepairLineFormRef.value.validate()
} catch {
subTabsName.value = 'dvRepairLine'
return
}
}
formLoading.value = true
try {
const data = { ...(formData.value as DvRepairFormData) } as DvRepairFormData & Record<string, any>
data.acceptedBy = normalizeUserId(data.acceptedBy)
data.confirmBy = normalizeUserId(data.confirmBy)
data.faultLevel = data.faultLevel === undefined || data.faultLevel === null || String(data.faultLevel).trim() === '' ? undefined : String(data.faultLevel)
data.faultImages = normalizeImageString(data.faultImages)
data.repairedImages = normalizeImageString(data.repairedImages)
data.repairResult = data.repairStatus
if (formType.value === 'repair') {
data.status = '1'
}
if (typeof formData.value.deviceId === 'number') {
data.machineryId = formData.value.deviceId
}
data.deviceId = formData.value.deviceId
data.componentId = formData.value.machineryTypeId === 2 ? formData.value.componentId : undefined
const lineList = dvRepairLineFormRef.value?.getData?.() || []
if (formType.value === 'repair') {
await DvRepairApi.updateDvRepairStatus({
id: data.id,
requireDate: data.requireDate,
finishDate: data.finishDate,
confirmDate: data.confirmDate,
repairStatus: data.repairStatus,
repairResult: data.repairResult,
faultLevel: data.faultLevel,
isShutdown: data.isShutdown,
faultPhenomenon: data.faultPhenomenon,
faultDescription: data.faultDescription,
faultImages: data.faultImages,
downtimeDuration: data.downtimeDuration,
faultReason: data.faultReason,
handlingMeasures: data.handlingMeasures,
replacementParts: data.replacementParts,
repairContent: data.repairContent,
repairedImages: data.repairedImages,
remark: data.remark,
updateReqVOList: lineList
})
message.success(t('common.updateSuccess'))
} else {
;(data as any).dvRepairLines = lineList
if (formType.value === 'create') {
await DvRepairApi.createDvRepair(data as any)
message.success(t('common.createSuccess'))
} else {
await DvRepairApi.updateDvRepair(data as any)
message.success(t('common.updateSuccess'))
}
}
emit('success')
closeForm()
} finally {
formLoading.value = false
}
}
const resetForm = () => {
formData.value = createDefaultFormData()
deviceOptionsLoaded.value = false
componentOptions.value = []
formRef.value?.resetFields()
formRef.value?.clearValidate?.()
}
</script>
<style lang="scss" scoped>
.dv-repair-panel {
background: #fff;
border-radius: 12px;
box-shadow: var(--el-box-shadow-light);
}
.dv-repair-panel__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
border-bottom: 1px solid #ebeef5;
}
.dv-repair-panel__title {
color: #1f2937;
font-size: 18px;
font-weight: 600;
}
.dv-repair-dialog {
max-height: calc(100vh - 220px);
padding: 20px 20px 0;
padding-right: 16px;
overflow-y: auto;
}
.dv-repair-section {
margin-bottom: 16px;
padding: 16px 18px 8px;
background: #fff;
border: 1px solid #ebeef5;
border-radius: 10px;
box-shadow: 0 4px 14px rgb(15 23 42 / 4%);
}
.dv-repair-section__title {
position: relative;
margin-bottom: 16px;
padding-left: 10px;
color: #1f2937;
font-size: 15px;
font-weight: 600;
&::before {
position: absolute;
top: 2px;
left: 0;
width: 3px;
height: 16px;
background: var(--el-color-primary);
border-radius: 999px;
content: '';
}
}
.dv-repair-label {
display: inline-flex;
gap: 4px;
align-items: center;
&.is-required::before {
color: var(--el-color-danger);
content: '*';
}
}
.dv-repair-code-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 12px;
align-items: center;
width: 100%;
}
.dv-repair-upload {
width: 100%;
:deep(.el-upload--picture-card) {
width: 120px;
height: 120px;
}
}
.dv-repair-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
width: 100%;
padding: 16px 20px 20px;
border-top: 1px solid #ebeef5;
}
@media (max-width: 1200px) {
.dv-repair-dialog :deep(.el-col) {
max-width: 100%;
flex: 0 0 100%;
}
}
@media (max-width: 768px) {
.dv-repair-panel__header {
padding: 14px 16px;
}
.dv-repair-dialog {
max-height: none;
padding: 16px 16px 0;
overflow-y: visible;
}
.dv-repair-footer {
padding: 16px;
}
}
</style>