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.
1105 lines
38 KiB
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>
|