style:设备维修单字段调整

master
黄伟杰 2 weeks ago
parent ecba2589c6
commit e3a3d9330e

@ -799,6 +799,89 @@ export default {
repairProcessing: 'Processing',
repairCompleted: 'Completed'
},
equipmentMaintenance: {
moduleName: 'Equipment Repair',
createTitle: 'Create Equipment Repair',
editTitle: 'Edit Equipment Repair',
detailTitle: 'Equipment Repair Detail',
repairTitle: 'Repair Processing',
basicInfo: 'Basic Info',
repairObject: 'Repair Object',
repairCode: 'Repair Order No.',
repairName: 'Repair Order Name',
requireDate: 'Report Date',
acceptedBy: 'Repair Technician',
confirmBy: 'Inspector',
machineryTypeId: 'Equipment Type',
machineryTypeDevice: 'Equipment',
machineryTypeKeyItem: 'Key Component',
device: 'Equipment',
component: 'Key Component',
faultLevel: 'Failure Level',
isShutdown: 'Shutdown Required',
status: 'Document Status',
faultInfo: 'Fault Info',
faultPhenomenon: 'Fault Phenomenon',
faultDescription: 'Fault Description',
faultImages: 'Fault Images',
repairResultSection: 'Repair Result',
repairResult: 'Repair Result',
finishDate: 'Completion Date',
confirmDate: 'Acceptance Date',
downtimeDuration: 'Downtime Duration',
faultReason: 'Fault Cause',
handlingMeasures: 'Handling Measures',
replacementParts: 'Replacement Parts',
repairContent: 'Repair Content',
repairedImages: 'Post-repair Images',
remark: 'Remark',
autoCode: 'Auto Generate',
searchPlaceholder: 'Enter order no. or equipment code',
empty: 'No repair records',
statusPending: 'Pending Repair',
statusPassed: 'Passed',
statusRejected: 'Rejected',
placeholderRepairCode: 'Enter repair order no.',
placeholderRepairName: 'Enter repair order name',
placeholderRequireDate: 'Select report date',
placeholderAcceptedBy: 'Select repair technician',
placeholderConfirmBy: 'Select inspector',
placeholderDevice: 'Select equipment',
placeholderComponent: 'Select key component',
placeholderFaultPhenomenon: 'Enter fault phenomenon',
placeholderFaultDescription: 'Enter fault description',
placeholderFinishDate: 'Select completion date',
placeholderConfirmDate: 'Select acceptance date',
placeholderDowntimeDuration: 'Enter downtime duration in hours',
placeholderFaultReason: 'Enter fault cause',
placeholderHandlingMeasures: 'Enter handling measures',
placeholderReplacementParts: 'Enter replacement parts',
placeholderRepairContent: 'Enter repair content',
placeholderRemark: 'Enter remark',
loadDetailFailed: 'Failed to load details',
scanUnrecognized: 'No content recognized',
scanEquipmentRequired: 'Please scan an equipment QR code',
deviceNotFound: 'Matching equipment not found',
scanFailed: 'Scan failed',
maxUploadCount: 'You can upload up to 9 images',
saving: 'Saving',
saveSuccess: 'Saved successfully',
submitSuccess: 'Submitted successfully',
submitFailed: 'Submit failed',
noId: 'Missing repair order ID',
confirmDeleteContent: 'Delete repair order "{code}"?',
validatorRepairCodeRequired: 'Please enter the repair order no.',
validatorRepairNameRequired: 'Please enter the repair order name',
validatorDeviceRequired: 'Please select equipment',
validatorComponentRequired: 'Please select key component',
validatorRequireDateRequired: 'Please select the report date',
validatorFaultLevelRequired: 'Please select the failure level',
validatorIsShutdownRequired: 'Please select whether shutdown is required',
validatorFaultPhenomenonRequired: 'Please enter the fault phenomenon',
validatorRepairStatusRequired: 'Please select the repair result',
validatorFinishDateRequired: 'Please select the completion date',
validatorConfirmDateRequired: 'Please select the acceptance date'
},
criticalComponent: {
moduleName: 'Critical Component',
subTitle: 'Equipment critical component management',

@ -799,6 +799,89 @@ export default {
repairProcessing: '处理中',
repairCompleted: '已完成'
},
equipmentMaintenance: {
moduleName: '设备维修',
createTitle: '新增设备维修',
editTitle: '编辑设备维修',
detailTitle: '设备维修详情',
repairTitle: '维修处理',
basicInfo: '基本信息',
repairObject: '维修对象',
repairCode: '维修单编号',
repairName: '维修单名称',
requireDate: '报修日期',
acceptedBy: '维修人员',
confirmBy: '验收人员',
machineryTypeId: '设备类型',
machineryTypeDevice: '设备',
machineryTypeKeyItem: '关键件',
device: '设备',
component: '关键件',
faultLevel: '故障等级',
isShutdown: '是否停机',
status: '单据状态',
faultInfo: '故障信息',
faultPhenomenon: '故障现象',
faultDescription: '故障描述',
faultImages: '故障图片',
repairResultSection: '处理结果',
repairResult: '维修结果',
finishDate: '完成日期',
confirmDate: '验收日期',
downtimeDuration: '停机时长',
faultReason: '故障原因',
handlingMeasures: '处理措施',
replacementParts: '更换配件',
repairContent: '维修内容',
repairedImages: '维修后图片',
remark: '备注',
autoCode: '自动生成',
searchPlaceholder: '请输入单号或设备编码',
empty: '暂无维修记录',
statusPending: '待维修',
statusPassed: '通过',
statusRejected: '不通过',
placeholderRepairCode: '请输入维修单编号',
placeholderRepairName: '请输入维修单名称',
placeholderRequireDate: '请选择报修日期',
placeholderAcceptedBy: '请选择维修人员',
placeholderConfirmBy: '请选择验收人员',
placeholderDevice: '请选择设备',
placeholderComponent: '请选择关键件',
placeholderFaultPhenomenon: '请输入故障现象',
placeholderFaultDescription: '请输入故障描述',
placeholderFinishDate: '请选择完成日期',
placeholderConfirmDate: '请选择验收日期',
placeholderDowntimeDuration: '请输入停机时长,单位小时',
placeholderFaultReason: '请输入故障原因',
placeholderHandlingMeasures: '请输入处理措施',
placeholderReplacementParts: '请输入更换配件',
placeholderRepairContent: '请输入维修内容',
placeholderRemark: '请输入备注',
loadDetailFailed: '加载详情失败',
scanUnrecognized: '未识别到内容',
scanEquipmentRequired: '请扫描设备二维码',
deviceNotFound: '未找到对应设备',
scanFailed: '扫码失败',
maxUploadCount: '最多上传 9 张图片',
saving: '保存中',
saveSuccess: '保存成功',
submitSuccess: '提交成功',
submitFailed: '提交失败',
noId: '缺少维修单 ID',
confirmDeleteContent: '确定删除维修单“{code}”吗?',
validatorRepairCodeRequired: '请输入维修单编号',
validatorRepairNameRequired: '请输入维修单名称',
validatorDeviceRequired: '请选择设备',
validatorComponentRequired: '请选择关键件',
validatorRequireDateRequired: '请选择报修日期',
validatorFaultLevelRequired: '请选择故障等级',
validatorIsShutdownRequired: '请选择是否停机',
validatorFaultPhenomenonRequired: '请输入故障现象',
validatorRepairStatusRequired: '请选择维修结果',
validatorFinishDateRequired: '请选择完成日期',
validatorConfirmDateRequired: '请选择验收日期'
},
criticalComponent: {
moduleName: '设备关键件',
subTitle: '设备关键件管理',

@ -93,7 +93,7 @@ import useDictStore from '@/store/modules/dict'
const { t } = useI18n()
const dictStore = useDictStore()
const resetFilterText = '\u91cd\u7f6e'
const resetFilterText = computed(() => t('functionCommon.reset'))
const searchKeyword = ref('')
const selectedStatus = ref('')
const selectedLineId = ref('')

@ -4,98 +4,133 @@
<scroll-view scroll-y class="form-scroll">
<view class="section-card">
<view class="section-title">基本信息</view>
<view class="section-title">{{ t('equipmentMaintenance.basicInfo') }}</view>
<view class="form-item">
<text class="form-label">维修单编号</text>
<text class="form-label">{{ t('equipmentMaintenance.repairCode') }}</text>
<view class="code-row">
<input
v-model="formData.repairCode"
class="form-input code-input"
type="text"
placeholder="请输入维修单编号"
:placeholder="t('equipmentMaintenance.placeholderRepairCode')"
:disabled="repairCodeDisabled"
/>
<view v-if="mode === 'create'" class="auto-code-wrap" @click="toggleAutoCode">
<text class="auto-code-text">自动生成</text>
<text class="auto-code-text">{{ t('equipmentMaintenance.autoCode') }}</text>
<switch :checked="Boolean(formData.isCode)" color="#1f4b79" @change="toggleAutoCode" />
</view>
</view>
</view>
<view class="form-item">
<text class="form-label">维修单名称</text>
<input v-model="formData.repairName" class="form-input" type="text" placeholder="请输入维修单名称" :disabled="readonlyBase" />
<text class="form-label required">{{ t('equipmentMaintenance.repairName') }}</text>
<input v-model="formData.repairName" class="form-input" type="text" :placeholder="t('equipmentMaintenance.placeholderRepairName')" :disabled="readonlyBase" />
</view>
<view class="form-item">
<text class="form-label">报修日期</text>
<text class="form-label required">{{ t('equipmentMaintenance.requireDate') }}</text>
<picker mode="date" :value="datePickerValue(formData.requireDate)" :disabled="readonlyBase" @change="(e) => onDateChange('requireDate', e)">
<view :class="['picker-field', !formData.requireDate ? 'is-placeholder' : '']">{{ formData.requireDate || '请选择报修日期' }}</view>
<view :class="['picker-field', !formData.requireDate ? 'is-placeholder' : '']">{{ formData.requireDate || t('equipmentMaintenance.placeholderRequireDate') }}</view>
</picker>
</view>
<view class="form-item">
<text class="form-label">维修人员</text>
<text class="form-label">{{ t('equipmentMaintenance.acceptedBy') }}</text>
<picker :range="userLabels" :value="acceptedByIndex" :disabled="readonlyMeta" @change="(e) => onUserChange('acceptedBy', e)">
<view :class="['picker-field', !formData.acceptedBy ? 'is-placeholder' : '']">{{ acceptedByLabel }}</view>
</picker>
</view>
<view class="form-item">
<text class="form-label">验收人员</text>
<text class="form-label">{{ t('equipmentMaintenance.confirmBy') }}</text>
<picker :range="userLabels" :value="confirmByIndex" :disabled="readonlyMeta" @change="(e) => onUserChange('confirmBy', e)">
<view :class="['picker-field', !formData.confirmBy ? 'is-placeholder' : '']">{{ confirmByLabel }}</view>
</picker>
</view>
<view class="form-item">
<view class="form-label-row">
<text class="form-label form-label-inline">设备</text>
<view v-if="!readonlyBase" class="field-scan-btn" @click="handleDeviceScan">
<u-icon name="scan" size="18" color="#1f4b79"></u-icon>
</view>
</view>
<picker :range="deviceLabels" :value="deviceIndex" :disabled="readonlyBase" @change="onDeviceChange">
<view :class="['picker-field', !formData.deviceId ? 'is-placeholder' : '']">{{ selectedDeviceLabel }}</view>
</picker>
<view class="form-item inline-radio">
<text class="form-label required">{{ t('equipmentMaintenance.faultLevel') }}</text>
<radio-group class="radio-group" :disabled="readonlyMeta" @change="onFaultLevelChange">
<label v-for="item in faultLevelOptions" :key="String(item.value)" class="radio-item">
<radio :checked="String(formData.faultLevel || '') === String(item.value)" :disabled="readonlyMeta" :value="String(item.value)" color="#1f4b79" />
<text>{{ item.label }}</text>
</label>
</radio-group>
</view>
<view class="form-item inline-radio">
<text class="form-label">是否停机</text>
<text class="form-label required">{{ t('equipmentMaintenance.isShutdown') }}</text>
<radio-group class="radio-group" :disabled="readonlyMeta" @change="onShutdownChange">
<label class="radio-item">
<radio :checked="formData.isShutdown === true" :disabled="readonlyMeta" value="true" color="#1f4b79" />
<text></text>
<text>{{ t('functionCommon.yes') }}</text>
</label>
<label class="radio-item">
<radio :checked="formData.isShutdown === false" :disabled="readonlyMeta" value="false" color="#1f4b79" />
<text></text>
<text>{{ t('functionCommon.no') }}</text>
</label>
</radio-group>
</view>
<view class="form-item">
<text class="form-label">单据状态</text>
<text class="form-label">{{ t('equipmentMaintenance.status') }}</text>
<view class="value-field">{{ statusLabel }}</view>
</view>
</view>
<view class="section-card">
<view class="section-title">故障信息</view>
<view class="section-title">{{ t('equipmentMaintenance.repairObject') }}</view>
<view class="form-item inline-radio">
<text class="form-label">{{ t('equipmentMaintenance.machineryTypeId') }}</text>
<radio-group class="radio-group" :disabled="readonlyBase" @change="onMachineryTypeChange">
<label class="radio-item">
<radio :checked="Number(formData.machineryTypeId || 1) === 1" :disabled="readonlyBase" value="1" color="#1f4b79" />
<text>{{ t('equipmentMaintenance.machineryTypeDevice') }}</text>
</label>
<label class="radio-item">
<radio :checked="Number(formData.machineryTypeId || 1) === 2" :disabled="readonlyBase" value="2" color="#1f4b79" />
<text>{{ t('equipmentMaintenance.machineryTypeKeyItem') }}</text>
</label>
</radio-group>
</view>
<view class="form-item">
<view class="form-label-row">
<text class="form-label form-label-inline required">{{ t('equipmentMaintenance.device') }}</text>
<view v-if="!readonlyBase" class="field-scan-btn" @click="handleDeviceScan">
<u-icon name="scan" size="18" color="#1f4b79"></u-icon>
</view>
</view>
<picker :range="deviceLabels" :value="deviceIndex" :disabled="readonlyBase" @change="onDeviceChange">
<view :class="['picker-field', !formData.deviceId ? 'is-placeholder' : '']">{{ selectedDeviceLabel }}</view>
</picker>
</view>
<view v-if="showComponentSelect" class="form-item">
<text class="form-label required">{{ t('equipmentMaintenance.component') }}</text>
<picker :range="componentLabels" :value="componentIndex" :disabled="readonlyBase" @change="onComponentChange">
<view :class="['picker-field', !formData.componentId ? 'is-placeholder' : '']">{{ selectedComponentLabel }}</view>
</picker>
</view>
</view>
<view class="section-card">
<view class="section-title">{{ t('equipmentMaintenance.faultInfo') }}</view>
<view class="form-item">
<text class="form-label required">故障现象</text>
<input v-model="formData.faultPhenomenon" class="form-input" type="text" placeholder="请输入故障现象" :disabled="readonlyFault" />
<text class="form-label required">{{ t('equipmentMaintenance.faultPhenomenon') }}</text>
<input v-model="formData.faultPhenomenon" class="form-input" type="text" :placeholder="t('equipmentMaintenance.placeholderFaultPhenomenon')" :disabled="readonlyFault" />
</view>
<view class="form-item">
<text class="form-label">故障描述</text>
<textarea v-model="formData.faultDescription" class="form-textarea" :disabled="readonlyFault" maxlength="500" placeholder="请输入故障描述" />
<text class="form-label">{{ t('equipmentMaintenance.faultDescription') }}</text>
<textarea v-model="formData.faultDescription" class="form-textarea" :disabled="readonlyFault" maxlength="500" :placeholder="t('equipmentMaintenance.placeholderFaultDescription')" />
</view>
<view class="form-item">
<text class="form-label">故障图片</text>
<text class="form-label">{{ t('equipmentMaintenance.faultImages') }}</text>
<view class="image-list">
<view v-for="(item, index) in faultImageList" :key="`${item}-${index}`" class="image-item">
<image class="preview-image" :src="item" mode="aspectFill" @click="previewImages(faultImageList, index)" />
@ -109,63 +144,63 @@
</view>
<view v-if="showRepairSection" class="section-card">
<view class="section-title">处理结果</view>
<view class="section-title">{{ t('equipmentMaintenance.repairResultSection') }}</view>
<view class="form-item inline-radio">
<text class="form-label required">维修结果</text>
<text class="form-label required">{{ t('equipmentMaintenance.repairResult') }}</text>
<radio-group class="radio-group" :disabled="readonlyRepair" @change="onRepairStatusChange">
<label class="radio-item">
<radio :checked="String(formData.repairStatus || '0') === '1'" :disabled="readonlyRepair" value="1" color="#1f4b79" />
<text>通过</text>
<text>{{ t('equipmentMaintenance.statusPassed') }}</text>
</label>
<label class="radio-item">
<radio :checked="String(formData.repairStatus || '0') === '2'" :disabled="readonlyRepair" value="2" color="#1f4b79" />
<text>不通过</text>
<text>{{ t('equipmentMaintenance.statusRejected') }}</text>
</label>
</radio-group>
</view>
<view class="form-item">
<text class="form-label required">完成日期</text>
<text class="form-label required">{{ t('equipmentMaintenance.finishDate') }}</text>
<picker mode="date" :value="datePickerValue(formData.finishDate)" :disabled="readonlyRepair" @change="(e) => onDateChange('finishDate', e)">
<view :class="['picker-field', !formData.finishDate ? 'is-placeholder' : '']">{{ formData.finishDate || '请选择完成日期' }}</view>
<view :class="['picker-field', !formData.finishDate ? 'is-placeholder' : '']">{{ formData.finishDate || t('equipmentMaintenance.placeholderFinishDate') }}</view>
</picker>
</view>
<view class="form-item">
<text class="form-label required">验收日期</text>
<text class="form-label required">{{ t('equipmentMaintenance.confirmDate') }}</text>
<picker mode="date" :value="datePickerValue(formData.confirmDate)" :disabled="readonlyRepair" @change="(e) => onDateChange('confirmDate', e)">
<view :class="['picker-field', !formData.confirmDate ? 'is-placeholder' : '']">{{ formData.confirmDate || '请选择验收日期' }}</view>
<view :class="['picker-field', !formData.confirmDate ? 'is-placeholder' : '']">{{ formData.confirmDate || t('equipmentMaintenance.placeholderConfirmDate') }}</view>
</picker>
</view>
<view class="form-item">
<text class="form-label">停机时长</text>
<input v-model="formData.downtimeDuration" class="form-input" type="digit" placeholder="请输入停机时长,单位小时" :disabled="readonlyRepair" />
<text class="form-label">{{ t('equipmentMaintenance.downtimeDuration') }}</text>
<input v-model="formData.downtimeDuration" class="form-input" type="digit" :placeholder="t('equipmentMaintenance.placeholderDowntimeDuration')" :disabled="readonlyRepair" />
</view>
<view class="form-item">
<text class="form-label">故障原因</text>
<input v-model="formData.faultReason" class="form-input" type="text" placeholder="请输入故障原因" :disabled="readonlyRepair" />
<text class="form-label">{{ t('equipmentMaintenance.faultReason') }}</text>
<input v-model="formData.faultReason" class="form-input" type="text" :placeholder="t('equipmentMaintenance.placeholderFaultReason')" :disabled="readonlyRepair" />
</view>
<view class="form-item">
<text class="form-label">处理措施</text>
<input v-model="formData.handlingMeasures" class="form-input" type="text" placeholder="请输入处理措施" :disabled="readonlyRepair" />
<text class="form-label">{{ t('equipmentMaintenance.handlingMeasures') }}</text>
<input v-model="formData.handlingMeasures" class="form-input" type="text" :placeholder="t('equipmentMaintenance.placeholderHandlingMeasures')" :disabled="readonlyRepair" />
</view>
<view class="form-item">
<text class="form-label">更换配件</text>
<input v-model="formData.replacementParts" class="form-input" type="text" placeholder="请输入更换配件" :disabled="readonlyRepair" />
<text class="form-label">{{ t('equipmentMaintenance.replacementParts') }}</text>
<input v-model="formData.replacementParts" class="form-input" type="text" :placeholder="t('equipmentMaintenance.placeholderReplacementParts')" :disabled="readonlyRepair" />
</view>
<view class="form-item">
<text class="form-label">维修内容</text>
<textarea v-model="formData.repairContent" class="form-textarea" :disabled="readonlyRepair" maxlength="1000" placeholder="请输入维修内容" />
<text class="form-label">{{ t('equipmentMaintenance.repairContent') }}</text>
<textarea v-model="formData.repairContent" class="form-textarea" :disabled="readonlyRepair" maxlength="1000" :placeholder="t('equipmentMaintenance.placeholderRepairContent')" />
</view>
<view class="form-item">
<text class="form-label">维修后图片</text>
<text class="form-label">{{ t('equipmentMaintenance.repairedImages') }}</text>
<view class="image-list">
<view v-for="(item, index) in repairedImageList" :key="`${item}-${index}`" class="image-item">
<image class="preview-image" :src="item" mode="aspectFill" @click="previewImages(repairedImageList, index)" />
@ -179,15 +214,15 @@
</view>
<view class="section-card last-section">
<view class="section-title">备注</view>
<view class="section-title">{{ t('equipmentMaintenance.remark') }}</view>
<view class="form-item no-border">
<textarea v-model="formData.remark" class="form-textarea" :disabled="isReadonly" maxlength="300" placeholder="请输入备注" />
<textarea v-model="formData.remark" class="form-textarea" :disabled="isReadonly" maxlength="300" :placeholder="t('equipmentMaintenance.placeholderRemark')" />
</view>
</view>
</scroll-view>
<view class="page-footer">
<view class="footer-btn cancel" @click="goBack"></view>
<view class="footer-btn cancel" @click="goBack">{{ t('dashboard.back') }}</view>
<view v-if="!isReadonly" class="footer-btn confirm" @click="submitForm">{{ submitButtonText }}</view>
</view>
</view>
@ -196,6 +231,7 @@
<script setup>
import { computed, reactive, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import { getDeviceLedgerList } from '@/api/mes/deviceLedger'
import {
@ -205,12 +241,18 @@ import {
updateDvRepairStatus,
uploadRepairImage
} from '@/api/mes/dvrepair'
import { getCriticalComponentList } from '@/api/mes/criticalComponent'
import { getSimpleUserList } from '@/api/system/user'
import { DICT_TYPE, initAllDict } from '@/utils/dict'
import useDictStore from '@/store/modules/dict'
const { t } = useI18n()
const dictStore = useDictStore()
const mode = ref('create')
const repairId = ref('')
const users = ref([])
const deviceOptions = ref([])
const componentOptions = ref([])
const loading = ref(false)
const formData = reactive({
@ -219,6 +261,7 @@ const formData = reactive({
repairName: '',
isCode: true,
deviceId: undefined,
componentId: undefined,
machineryId: undefined,
machineryCode: '',
machineryName: '',
@ -232,6 +275,7 @@ const formData = reactive({
repairResult: '0',
acceptedBy: '',
confirmBy: '',
faultLevel: '',
isShutdown: undefined,
status: '0',
faultPhenomenon: '',
@ -247,10 +291,10 @@ const formData = reactive({
})
const pageTitle = computed(() => {
if (mode.value === 'repair') return '维修处理'
if (mode.value === 'update') return '编辑设备维修'
if (mode.value === 'detail') return '设备维修详情'
return '新增设备维修'
if (mode.value === 'repair') return t('equipmentMaintenance.repairTitle')
if (mode.value === 'update') return t('equipmentMaintenance.editTitle')
if (mode.value === 'detail') return t('equipmentMaintenance.detailTitle')
return t('equipmentMaintenance.createTitle')
})
const isReadonly = computed(() => mode.value === 'detail')
const readonlyBase = computed(() => mode.value === 'repair' || mode.value === 'detail')
@ -259,26 +303,52 @@ const readonlyFault = computed(() => mode.value === 'repair' || mode.value === '
const readonlyRepair = computed(() => mode.value !== 'repair')
const repairCodeDisabled = computed(() => Boolean(formData.isCode) || mode.value !== 'create')
const showRepairSection = computed(() => mode.value === 'repair' || mode.value === 'detail' || String(formData.repairStatus || '0') !== '0' || String(formData.status || '0') === '1')
const submitButtonText = computed(() => '保存')
const submitButtonText = computed(() => t('functionCommon.save'))
const statusLabel = computed(() => {
const normalized = String(formData.repairStatus || '0')
if (normalized === '1') return '通过'
if (normalized === '2') return '不通过'
return '待维修'
if (normalized === '1') return t('equipmentMaintenance.statusPassed')
if (normalized === '2') return t('equipmentMaintenance.statusRejected')
return t('equipmentMaintenance.statusPending')
})
const userLabels = computed(() => users.value.map((item) => item.nickname || item.name || String(item.id || '')))
const deviceLabels = computed(() => deviceOptions.value.map((item) => item.label))
const faultLevelOptions = computed(() => {
const dicts = dictStore.getDict(DICT_TYPE.FAILURE_LEVEL) || []
return dicts
.filter((item) => item?.value !== null && item?.value !== undefined && String(item.value) !== '')
.map((item) => ({
label: item.label,
value: String(item.value)
}))
})
const acceptedByIndex = computed(() => findUserIndex(formData.acceptedBy))
const confirmByIndex = computed(() => findUserIndex(formData.confirmBy))
const acceptedByLabel = computed(() => resolveUserLabel(formData.acceptedBy, '请选择维修人员'))
const confirmByLabel = computed(() => resolveUserLabel(formData.confirmBy, '请选择验收人员'))
const acceptedByLabel = computed(() => resolveUserLabel(formData.acceptedBy, t('equipmentMaintenance.placeholderAcceptedBy')))
const confirmByLabel = computed(() => resolveUserLabel(formData.confirmBy, t('equipmentMaintenance.placeholderConfirmBy')))
const showComponentSelect = computed(() => Number(formData.machineryTypeId || 1) === 2)
const deviceIndex = computed(() => {
const index = deviceOptions.value.findIndex((item) => String(item.value) === String(formData.deviceId || ''))
return index >= 0 ? index : 0
})
const selectedDeviceLabel = computed(() => {
const current = deviceOptions.value.find((item) => String(item.value) === String(formData.deviceId || ''))
return current?.label || '请选择设备'
return current?.label || t('equipmentMaintenance.placeholderDevice')
})
const filteredComponentOptions = computed(() => {
const source = Array.isArray(componentOptions.value) ? componentOptions.value : []
const currentDeviceId = String(formData.deviceId || '').trim()
if (!currentDeviceId) return source
const matched = source.filter((item) => matchesComponentDevice(item?.raw, currentDeviceId))
return matched.length ? matched : source
})
const componentLabels = computed(() => filteredComponentOptions.value.map((item) => item.label))
const componentIndex = computed(() => {
const index = filteredComponentOptions.value.findIndex((item) => String(item.value) === String(formData.componentId || ''))
return index >= 0 ? index : 0
})
const selectedComponentLabel = computed(() => {
const current = filteredComponentOptions.value.find((item) => String(item.value) === String(formData.componentId || ''))
return current?.label || t('equipmentMaintenance.placeholderComponent')
})
const faultImageList = computed(() => splitImages(formData.faultImages))
const repairedImageList = computed(() => splitImages(formData.repairedImages))
@ -286,7 +356,8 @@ const repairedImageList = computed(() => splitImages(formData.repairedImages))
onLoad(async (options) => {
mode.value = String(options?.mode || 'create')
repairId.value = String(options?.id || '')
await Promise.all([fetchUsers(), fetchDevices()])
await initAllDict()
await Promise.all([fetchUsers(), fetchDevices(), fetchComponents()])
if (repairId.value) {
await fetchDetail(repairId.value)
}
@ -320,6 +391,23 @@ async function fetchDevices() {
}
}
async function fetchComponents() {
try {
const res = await getCriticalComponentList()
const root = res && res.data !== undefined ? res.data : res
const data = Array.isArray(root) ? root : (Array.isArray(root?.data) ? root.data : [])
componentOptions.value = (Array.isArray(data) ? data : [])
.filter((item) => item && item.id !== undefined && item.id !== null)
.map((item) => ({
value: item.id,
label: `${item.code || item.componentCode || ''} ${item.name || item.componentName || ''}`.trim() || String(item.id),
raw: item
}))
} catch (error) {
componentOptions.value = []
}
}
async function fetchDetail(id) {
loading.value = true
try {
@ -331,6 +419,7 @@ async function fetchDetail(id) {
formData.repairName = inputValue(detail?.repairName)
formData.isCode = detail?.isCode === undefined ? true : Boolean(detail?.isCode)
formData.deviceId = detail?.deviceId ?? detail?.machineryId
formData.componentId = detail?.componentId ?? detail?.criticalComponentId
formData.machineryId = detail?.machineryId ?? detail?.deviceId
formData.machineryCode = inputValue(detail?.machineryCode)
formData.machineryName = inputValue(detail?.machineryName)
@ -344,6 +433,7 @@ async function fetchDetail(id) {
formData.repairResult = normalizedRepairStatus
formData.acceptedBy = normalizeUserId(detail?.acceptedBy)
formData.confirmBy = normalizeUserId(detail?.confirmBy)
formData.faultLevel = detail?.faultLevel === undefined || detail?.faultLevel === null ? '' : String(detail?.faultLevel)
formData.isShutdown = detail?.isShutdown === undefined || detail?.isShutdown === null ? undefined : Boolean(detail?.isShutdown)
formData.status = detail?.status === undefined || detail?.status === null ? '0' : String(detail?.status)
formData.faultPhenomenon = inputValue(detail?.faultPhenomenon)
@ -373,8 +463,18 @@ async function fetchDetail(id) {
})
}
}
if (formData.componentId !== undefined && formData.componentId !== null) {
const exists = componentOptions.value.some((item) => String(item.value) === String(formData.componentId))
if (!exists) {
componentOptions.value.unshift({
value: formData.componentId,
label: `${detail?.componentCode || detail?.criticalComponentCode || ''} ${detail?.componentName || detail?.criticalComponentName || ''}`.trim() || String(formData.componentId),
raw: detail
})
}
}
} catch (error) {
uni.showToast({ title: '加载详情失败', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.loadDetailFailed'), icon: 'none' })
} finally {
loading.value = false
}
@ -413,24 +513,42 @@ function onDeviceChange(event) {
applyDeviceOption(current)
}
function onComponentChange(event) {
const index = Number(event?.detail?.value || 0)
const current = filteredComponentOptions.value[index]
formData.componentId = current ? current.value : undefined
}
function onMachineryTypeChange(event) {
const nextValue = Number(event?.detail?.value || 1) || 1
formData.machineryTypeId = nextValue
formData.deviceId = undefined
formData.componentId = undefined
formData.machineryId = undefined
formData.machineryCode = ''
formData.machineryName = ''
formData.machineryBrand = ''
formData.machinerySpec = ''
}
async function handleDeviceScan() {
try {
const res = await uni.scanCode({ scanType: ['qrCode', 'barCode'] })
const scan = parseEquipmentScanResult(res?.result)
if (!scan.id) {
uni.showToast({ title: '请扫描设备二维码', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.scanEquipmentRequired'), icon: 'none' })
return
}
const matched = deviceOptions.value.find((item) => String(item.value) === String(scan.id))
if (!matched) {
uni.showToast({ title: '未找到对应设备', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.deviceNotFound'), icon: 'none' })
return
}
applyDeviceOption(matched)
} catch (error) {
const message = String(error?.errMsg || '')
if (message.includes('cancel')) return
uni.showToast({ title: '扫码失败', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.scanFailed'), icon: 'none' })
}
}
@ -459,18 +577,38 @@ function parseEquipmentScanResult(result) {
function applyDeviceOption(current) {
formData.deviceId = current.value
formData.machineryId = current.value
formData.componentId = undefined
formData.machineryCode = inputValue(current.raw?.deviceCode)
formData.machineryName = inputValue(current.raw?.deviceName)
formData.machineryBrand = inputValue(current.raw?.deviceBrand)
formData.machinerySpec = inputValue(current.raw?.deviceSpec)
}
function matchesComponentDevice(component, deviceId) {
if (!component || !deviceId) return false
const candidates = [
component.deviceId,
component.machineryId,
component.deviceLedgerId,
component.deviceLedgerid,
component.parentDeviceId,
component.parentMachineryId,
component.ownerDeviceId,
component.ownerMachineryId
]
return candidates.some((item) => item !== undefined && item !== null && String(item) === String(deviceId))
}
function onShutdownChange(event) {
const value = String(event?.detail?.value || '')
if (value === 'true') formData.isShutdown = true
if (value === 'false') formData.isShutdown = false
}
function onFaultLevelChange(event) {
formData.faultLevel = String(event?.detail?.value || '')
}
function onRepairStatusChange(event) {
const value = String(event?.detail?.value || '0')
formData.repairStatus = value
@ -552,13 +690,13 @@ async function chooseImages(field) {
const currentCount = splitImages(formData[field]).length
const remain = Math.max(0, 9 - currentCount)
if (!remain) {
uni.showToast({ title: '最多上传 9 张图片', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.maxUploadCount'), icon: 'none' })
return
}
const res = await uni.chooseImage({ count: remain, sizeType: ['compressed'] })
const files = Array.isArray(res?.tempFilePaths) ? res.tempFilePaths : []
if (!files.length) return
uni.showLoading({ title: '上传中', mask: true })
uni.showLoading({ title: t('functionCommon.uploading'), mask: true })
const uploaded = []
for (const filePath of files) {
const uploadRes = await uploadRepairImage(filePath)
@ -576,7 +714,7 @@ async function chooseImages(field) {
} catch (error) {
const message = String(error?.errMsg || '')
if (!message.includes('cancel')) {
uni.showToast({ title: '图片上传失败', icon: 'none' })
uni.showToast({ title: t('functionCommon.uploadImageFailed'), icon: 'none' })
}
} finally {
uni.hideLoading()
@ -608,7 +746,8 @@ function buildPayload() {
machineryName: formData.machineryName || undefined,
machineryBrand: formData.machineryBrand || undefined,
machinerySpec: formData.machinerySpec || undefined,
machineryTypeId: 1,
machineryTypeId: Number(formData.machineryTypeId || 1) || 1,
componentId: Number(formData.machineryTypeId || 1) === 2 ? formData.componentId : undefined,
requireDate: toTimestamp(formData.requireDate),
finishDate: toTimestamp(formData.finishDate),
confirmDate: toTimestamp(formData.confirmDate),
@ -616,6 +755,7 @@ function buildPayload() {
repairResult: normalizeRepairStatus(formData.repairStatus),
acceptedBy: normalizeUserId(formData.acceptedBy) || undefined,
confirmBy: normalizeUserId(formData.confirmBy) || undefined,
faultLevel: formData.faultLevel || undefined,
isShutdown: formData.isShutdown,
status: mode.value === 'repair' ? '1' : String(formData.status || '0'),
faultPhenomenon: formData.faultPhenomenon.trim(),
@ -633,36 +773,48 @@ function buildPayload() {
async function submitForm() {
if (!formData.isCode && !formData.repairCode.trim()) {
uni.showToast({ title: '请输入维修单编号', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.validatorRepairCodeRequired'), icon: 'none' })
return
}
if (!formData.repairName.trim()) {
uni.showToast({ title: '请输入维修单名称', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.validatorRepairNameRequired'), icon: 'none' })
return
}
if (!formData.deviceId) {
uni.showToast({ title: '请选择设备', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.validatorDeviceRequired'), icon: 'none' })
return
}
if (showComponentSelect.value && !formData.componentId) {
uni.showToast({ title: t('equipmentMaintenance.validatorComponentRequired'), icon: 'none' })
return
}
if (!formData.requireDate) {
uni.showToast({ title: '请选择报修日期', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.validatorRequireDateRequired'), icon: 'none' })
return
}
if (!formData.faultLevel) {
uni.showToast({ title: t('equipmentMaintenance.validatorFaultLevelRequired'), icon: 'none' })
return
}
if (formData.isShutdown === undefined) {
uni.showToast({ title: t('equipmentMaintenance.validatorIsShutdownRequired'), icon: 'none' })
return
}
if (!formData.faultPhenomenon.trim()) {
uni.showToast({ title: '请输入故障现象', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.validatorFaultPhenomenonRequired'), icon: 'none' })
return
}
if (mode.value === 'repair') {
if (String(formData.repairStatus || '0') === '0') {
uni.showToast({ title: '请选择维修结果', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.validatorRepairStatusRequired'), icon: 'none' })
return
}
if (!formData.finishDate) {
uni.showToast({ title: '请选择完成日期', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.validatorFinishDateRequired'), icon: 'none' })
return
}
if (!formData.confirmDate) {
uni.showToast({ title: '请选择验收日期', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.validatorConfirmDateRequired'), icon: 'none' })
return
}
}
@ -670,10 +822,10 @@ async function submitForm() {
const payload = buildPayload()
try {
uni.showLoading({ title: '保存中', mask: true })
uni.showLoading({ title: t('equipmentMaintenance.saving'), mask: true })
if (mode.value === 'create') {
await createDvRepair(payload)
uni.showToast({ title: '新增成功', icon: 'success' })
uni.showToast({ title: t('functionCommon.createSuccess'), icon: 'success' })
} else if (mode.value === 'repair') {
await updateDvRepairStatus({
id: payload.id,
@ -682,6 +834,8 @@ async function submitForm() {
confirmDate: payload.confirmDate,
repairStatus: payload.repairStatus,
repairResult: payload.repairResult,
faultLevel: payload.faultLevel,
isShutdown: payload.isShutdown,
faultPhenomenon: payload.faultPhenomenon,
faultDescription: payload.faultDescription || null,
faultImages: payload.faultImages,
@ -694,16 +848,16 @@ async function submitForm() {
remark: payload.remark || '',
updateReqVOList: []
})
uni.showToast({ title: '提交成功', icon: 'success' })
uni.showToast({ title: t('equipmentMaintenance.submitSuccess'), icon: 'success' })
} else {
await updateDvRepair(payload)
uni.showToast({ title: '保存成功', icon: 'success' })
uni.showToast({ title: t('equipmentMaintenance.saveSuccess'), icon: 'success' })
}
setTimeout(() => {
goBack()
}, 300)
} catch (error) {
uni.showToast({ title: mode.value === 'repair' ? '提交失败' : '保存失败', icon: 'none' })
uni.showToast({ title: mode.value === 'repair' ? t('equipmentMaintenance.submitFailed') : t('functionCommon.saveFailed'), icon: 'none' })
} finally {
uni.hideLoading()
}

@ -1,6 +1,6 @@
<template>
<view class="page-container">
<NavBar title="设备维修" />
<NavBar :title="t('equipmentMaintenance.moduleName')" />
<view class="filter-bar">
<view class="keyword-box">
@ -8,7 +8,7 @@
v-model="searchKeyword"
class="keyword-input"
type="text"
placeholder="请输入单号或设备编码"
:placeholder="t('equipmentMaintenance.searchPlaceholder')"
placeholder-class="placeholder"
:focus="keywordFocus"
confirm-type="search"
@ -18,11 +18,11 @@
</view>
<picker :range="statusLabels" :value="statusPickerIndex" @change="onStatusFilterChange">
<view class="status-box">
<text :class="['status-box-text', selectedStatus === '' ? 'placeholder' : '']">{{ selectedStatusLabel }}</text>
<text class="status-box-text">{{ selectedStatusLabel }}</text>
<uni-icons type="bottom" size="14" color="#9ca3af"></uni-icons>
</view>
</picker>
<view class="reset-filter-btn" @click="resetFilters"></view>
<view class="reset-filter-btn" @click="resetFilters">{{ t('functionCommon.reset') }}</view>
<view class="scan-btn" @click="handleScan">
<uni-icons type="scan" size="22" color="#111827"></uni-icons>
</view>
@ -67,10 +67,10 @@
</view>
</view>
<view v-if="loading && pageNo === 1" class="state-text">...</view>
<view v-else-if="!list.length" class="state-text empty">暂无维修记录</view>
<view v-else-if="loadingMore" class="state-text">加载更多中...</view>
<view v-else-if="finished" class="state-text">没有更多了</view>
<view v-if="loading && pageNo === 1" class="state-text">{{ t('functionCommon.loading') }}</view>
<view v-else-if="!list.length" class="state-text empty">{{ t('equipmentMaintenance.empty') }}</view>
<view v-else-if="loadingMore" class="state-text">{{ t('functionCommon.loadingMore') }}</view>
<view v-else-if="finished" class="state-text">{{ t('functionCommon.noMore') }}</view>
</view>
</scroll-view>
@ -87,14 +87,16 @@
<script setup>
import { computed, nextTick, ref } from 'vue'
import { onLoad, onReachBottom, onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import {
deleteDvRepair,
getDvRepairPage
} from '@/api/mes/dvrepair'
const { t } = useI18n()
const searchKeyword = ref('')
const selectedStatus = ref('0')
const selectedStatus = ref('')
const list = ref([])
const loading = ref(false)
const loadingMore = ref(false)
@ -108,20 +110,21 @@ const showGoTop = ref(false)
const keywordFocus = ref(false)
const selectedMachineryId = ref('')
const statusOptions = [
{ label: '待维修', value: '0' },
{ label: '通过', value: '1' },
{ label: '不通过', value: '2' }
]
const statusOptions = computed(() => [
{ label: t('functionCommon.all'), value: '' },
{ label: t('equipmentMaintenance.statusPending'), value: '0' },
{ label: t('equipmentMaintenance.statusPassed'), value: '1' },
{ label: t('equipmentMaintenance.statusRejected'), value: '2' }
])
const statusLabels = computed(() => statusOptions.map((item) => item.label))
const statusLabels = computed(() => statusOptions.value.map((item) => item.label))
const statusPickerIndex = computed(() => {
const index = statusOptions.findIndex((item) => item.value === selectedStatus.value)
const index = statusOptions.value.findIndex((item) => item.value === selectedStatus.value)
return index >= 0 ? index : 0
})
const selectedStatusLabel = computed(() => {
const current = statusOptions.find((item) => item.value === selectedStatus.value)
return current ? current.label : '待维修'
const current = statusOptions.value.find((item) => item.value === selectedStatus.value)
return current ? current.label : t('functionCommon.all')
})
onLoad(async () => {
@ -160,7 +163,6 @@ async function fetchList(reset) {
repairCode: keyword || undefined,
machineryCode: keyword || undefined,
machineryId: selectedMachineryId.value || undefined,
repairResult: selectedStatus.value === '' ? undefined : selectedStatus.value,
repairStatus: selectedStatus.value === '' ? undefined : selectedStatus.value
}
const res = await getDvRepairPage(params)
@ -172,7 +174,7 @@ async function fetchList(reset) {
if (!reset) {
pageNo.value = Math.max(1, pageNo.value - 1)
}
uni.showToast({ title: '加载失败', icon: 'none' })
uni.showToast({ title: t('functionCommon.loadFailed'), icon: 'none' })
} finally {
loading.value = false
loadingMore.value = false
@ -203,7 +205,7 @@ function handleSearch() {
async function resetFilters() {
searchKeyword.value = ''
selectedStatus.value = '0'
selectedStatus.value = ''
selectedMachineryId.value = ''
activateKeywordFocus()
await fetchList(true)
@ -211,7 +213,7 @@ async function resetFilters() {
function onStatusFilterChange(event) {
const index = Number(event?.detail?.value || 0)
selectedStatus.value = statusOptions[index]?.value ?? ''
selectedStatus.value = statusOptions.value[index]?.value ?? ''
fetchList(true)
}
@ -220,12 +222,12 @@ async function handleScan() {
const res = await uni.scanCode({ scanType: ['qrCode', 'barCode'] })
const result = String(res?.result || '').trim()
if (!result) {
uni.showToast({ title: '未识别到内容', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.scanUnrecognized'), icon: 'none' })
return
}
const scan = parseEquipmentScanResult(result)
if (!scan.id) {
uni.showToast({ title: '请扫描设备二维码', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.scanEquipmentRequired'), icon: 'none' })
return
}
selectedMachineryId.value = scan.id
@ -234,7 +236,7 @@ async function handleScan() {
} catch (error) {
const message = String(error?.errMsg || '')
if (message.includes('cancel')) return
uni.showToast({ title: '扫码失败', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.scanFailed'), icon: 'none' })
}
}
@ -275,7 +277,7 @@ function isProcessedRepair(value) {
function openCard(item) {
if (item?.id === undefined || item?.id === null) {
uni.showToast({ title: '缺少维修单 ID', icon: 'none' })
uni.showToast({ title: t('equipmentMaintenance.noId'), icon: 'none' })
return
}
if (canRepair(item)) {
@ -299,16 +301,16 @@ function confirmDelete(item) {
const id = item?.id
if (id === undefined || id === null) return
uni.showModal({
title: '确认删除',
content: `确定删除维修单“${textValue(item?.repairCode)}”吗?`,
title: t('functionCommon.confirmDelete'),
content: t('equipmentMaintenance.confirmDeleteContent', { code: textValue(item?.repairCode) }),
success: async (res) => {
if (!res.confirm) return
try {
await deleteDvRepair(String(id))
uni.showToast({ title: '删除成功', icon: 'success' })
uni.showToast({ title: t('functionCommon.deleteSuccess'), icon: 'success' })
await fetchList(true)
} catch (error) {
uni.showToast({ title: '删除失败', icon: 'none' })
uni.showToast({ title: t('functionCommon.deleteFailed'), icon: 'none' })
}
}
})
@ -331,9 +333,9 @@ async function loadMore() {
function getRepairStatusText(value) {
const normalized = value === '' || value === null || value === undefined ? '0' : String(value)
if (normalized === '1') return '通过'
if (normalized === '2') return '不通过'
return '待维修'
if (normalized === '1') return t('equipmentMaintenance.statusPassed')
if (normalized === '2') return t('equipmentMaintenance.statusRejected')
return t('equipmentMaintenance.statusPending')
}
function getRepairStatusTextClass(value) {

@ -4,6 +4,7 @@ import { Ref, ref, toRefs } from "vue";
export enum DICT_TYPE {
INFRA_BOOLEAN_STRING = "infra_boolean_string",
FAILURE_LEVEL = "failure_level",
ERP_MOLD_STATUS = "erp_mold_status",
ERP_AUDIT_STATUS = "erp_audit_status",
MES_ORG_TYPE = "mes_org_type",

Loading…
Cancel
Save