| | |
| | | <template> |
| | | <el-dialog v-model="visible" :title="modalOptions.title" @close="close" draggable> |
| | | <RepairForm ref="repairFormRef" :id="id" /> |
| | | <template #footer> |
| | | <el-button type="primary" @click="sendForm" :loading="loading"> |
| | | {{ modalOptions.confirmText }} |
| | | </el-button> |
| | | <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | <FormDialog |
| | | v-model="visible" |
| | | :title="id ? '编辑设备报修' : '新增设备报修'" |
| | | width="800px" |
| | | @confirm="sendForm" |
| | | @cancel="handleCancel" |
| | | @close="handleClose" |
| | | > |
| | | <el-form :model="form" label-width="100px"> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="设备名称"> |
| | | <el-select v-model="form.deviceLedgerId" @change="setDeviceModel" filterable> |
| | | <el-option |
| | | v-for="(item, index) in deviceOptions" |
| | | :key="index" |
| | | :label="item.deviceName" |
| | | :value="item.id" |
| | | ></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="规格型号"> |
| | | <el-input |
| | | v-model="form.deviceModel" |
| | | placeholder="请输入规格型号" |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="报修日期"> |
| | | <el-date-picker |
| | | v-model="form.repairTime" |
| | | placeholder="请选择报修日期" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | type="date" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="报修人"> |
| | | <el-input v-model="form.repairName" placeholder="请输入报修人" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row v-if="id"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="报修状态"> |
| | | <el-select v-model="form.status"> |
| | | <el-option label="待维修" :value="0"></el-option> |
| | | <el-option label="完结" :value="1"></el-option> |
| | | <el-option label="失败" :value="2"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="故障现象"> |
| | | <el-input |
| | | v-model="form.remark" |
| | | :rows="2" |
| | | type="textarea" |
| | | placeholder="请输入故障现象" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="现场照片"> |
| | | <div class="repair-upload-area"> |
| | | <el-upload |
| | | v-model:file-list="uploadFileList" |
| | | :action="uploadUrl" |
| | | :headers="uploadHeaders" |
| | | accept="image/*" |
| | | list-type="picture-card" |
| | | :limit="uploadLimit" |
| | | :on-success="handleUploadSuccess" |
| | | :on-remove="handleRemoveFile" |
| | | :before-upload="beforeUpload" |
| | | > |
| | | <el-icon><Plus /></el-icon> |
| | | </el-upload> |
| | | <div v-if="repairFileList.length === 0" class="upload-tip">请上传现场照片,最多 {{ uploadLimit }} 张</div> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </FormDialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { useModal } from "@/hooks/useModal"; |
| | | import RepairForm from "../Form/RepairForm.vue"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { |
| | | addRepair, |
| | | editRepair, |
| | | getRepairById, |
| | | } from "@/api/equipmentManagement/repair"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { Plus } from "@element-plus/icons-vue"; |
| | | import dayjs from "dayjs"; |
| | | import useFormData from "@/hooks/useFormData"; |
| | | import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { getToken } from "@/utils/auth"; |
| | | |
| | | defineOptions({ |
| | | name: "设备报修弹窗", |
| | |
| | | |
| | | const emits = defineEmits(["ok"]); |
| | | |
| | | const repairFormRef = ref(); |
| | | const { |
| | | id, |
| | | visible, |
| | | loading, |
| | | openModal, |
| | | modalOptions, |
| | | handleConfirm, |
| | | closeModal, |
| | | } = useModal({ title: "设备报修" }); |
| | | const id = ref(); |
| | | const visible = ref(false); |
| | | const loading = ref(false); |
| | | |
| | | const userStore = useUserStore(); |
| | | const deviceOptions = ref([]); |
| | | |
| | | // 现场照片上传(与 APP 字段一致:fileList / commonFileList) |
| | | const uploadUrl = import.meta.env.VITE_APP_BASE_API + "/file/upload"; |
| | | const uploadLimit = 10; |
| | | const uploadFileList = ref([]); |
| | | const repairFileList = ref([]); |
| | | const uploadHeaders = { Authorization: "Bearer " + getToken() }; |
| | | const baseApi = import.meta.env.VITE_APP_BASE_API || ""; |
| | | |
| | | const formatFileUrl = (url) => { |
| | | if (!url) return ""; |
| | | if (url.startsWith("http://") || url.startsWith("https://")) return url; |
| | | const path = url.replace(/^\/+/, ""); |
| | | return path ? baseApi + "/" + path : baseApi; |
| | | }; |
| | | |
| | | const loadDeviceName = async () => { |
| | | const { data } = await getDeviceLedger(); |
| | | deviceOptions.value = data; |
| | | }; |
| | | |
| | | const { form, resetForm } = useFormData({ |
| | | deviceLedgerId: undefined, |
| | | deviceName: undefined, |
| | | deviceModel: undefined, |
| | | repairTime: dayjs().format("YYYY-MM-DD"), |
| | | repairName: userStore.nickName, |
| | | remark: undefined, |
| | | status: 0, |
| | | }); |
| | | |
| | | const setDeviceModel = (deviceId) => { |
| | | const option = deviceOptions.value.find((item) => item.id === deviceId); |
| | | form.deviceModel = option.deviceModel; |
| | | }; |
| | | |
| | | const beforeUpload = (file) => { |
| | | const isImage = file.type.startsWith("image/"); |
| | | if (!isImage) { |
| | | ElMessage.warning("只能上传图片"); |
| | | return false; |
| | | } |
| | | if (repairFileList.value.length >= uploadLimit) { |
| | | ElMessage.warning(`最多上传 ${uploadLimit} 张图片`); |
| | | return false; |
| | | } |
| | | return true; |
| | | }; |
| | | |
| | | const handleUploadSuccess = (res, file) => { |
| | | if (res?.code !== 200 && res?.code !== 0) { |
| | | ElMessage.error(res?.msg || "上传失败"); |
| | | const idx = uploadFileList.value.findIndex((f) => f.uid === file.uid); |
| | | if (idx > -1) uploadFileList.value.splice(idx, 1); |
| | | return; |
| | | } |
| | | const d = res?.data !== undefined ? res.data : res; |
| | | if (!d) return; |
| | | const item = { |
| | | uid: file.uid, |
| | | id: d.id, |
| | | url: d.url || d.downloadUrl || d.tempPath, |
| | | downloadUrl: d.downloadUrl || d.url, |
| | | bucketFilename: d.bucketFilename || d.originalFilename || file.name, |
| | | originalFilename: d.originalFilename || d.bucketFilename || file.name, |
| | | name: d.bucketFilename || d.originalFilename || file.name, |
| | | size: d.size ?? d.byteSize ?? file.size, |
| | | byteSize: d.byteSize ?? d.size ?? file.size, |
| | | uploadResponse: res, |
| | | }; |
| | | repairFileList.value.push(item); |
| | | }; |
| | | |
| | | const handleRemoveFile = (file) => { |
| | | const targetUrl = file.url || file.response?.data?.url || file.response?.data?.downloadUrl; |
| | | repairFileList.value = repairFileList.value.filter( |
| | | (f) => f.uid !== file.uid && (f.url || f.downloadUrl) !== targetUrl |
| | | ); |
| | | }; |
| | | |
| | | const setForm = (data) => { |
| | | form.deviceLedgerId = data.deviceLedgerId; |
| | | form.deviceName = data.deviceName; |
| | | form.deviceModel = data.deviceModel; |
| | | form.repairTime = data.repairTime; |
| | | form.repairName = data.repairName; |
| | | form.remark = data.remark; |
| | | form.status = data.status; |
| | | const list = data.fileList || data.commonFileList || []; |
| | | repairFileList.value = (Array.isArray(list) ? list : []).map((f, i) => ({ |
| | | uid: f.id || "existing-" + i, |
| | | id: f.id, |
| | | url: f.url || f.downloadUrl, |
| | | downloadUrl: f.downloadUrl || f.url, |
| | | bucketFilename: f.bucketFilename || f.originalFilename || f.name, |
| | | originalFilename: f.originalFilename || f.bucketFilename || f.name, |
| | | name: f.bucketFilename || f.originalFilename || f.name || "图片", |
| | | size: f.size ?? f.byteSize, |
| | | byteSize: f.byteSize ?? f.size, |
| | | ...f, |
| | | })); |
| | | uploadFileList.value = repairFileList.value.map((f, i) => ({ |
| | | uid: f.uid || "existing-" + i, |
| | | name: f.name || f.bucketFilename || "图片", |
| | | url: formatFileUrl(f.url || f.downloadUrl), |
| | | status: "success", |
| | | })); |
| | | }; |
| | | |
| | | const sendForm = async () => { |
| | | loading.value = true; |
| | | const form = await repairFormRef.value.getForm(); |
| | | const { code } = id.value |
| | | ? await editRepair({ id: unref(id), ...form }) |
| | | : await addRepair(form); |
| | | if (code == 200) { |
| | | ElMessage.success(`${id ? "编辑" : "新增"}报修成功`); |
| | | closeModal(); |
| | | emits("ok"); |
| | | try { |
| | | const fileList = repairFileList.value.map((f) => { |
| | | const d = f.uploadResponse?.data !== undefined ? f.uploadResponse.data : f.uploadResponse; |
| | | return d ? { ...d } : (f.id || f.url ? { ...f } : null); |
| | | }).filter(Boolean); |
| | | const submitData = { ...form }; |
| | | if (fileList.length) submitData.fileList = fileList; |
| | | const { code } = id.value |
| | | ? await editRepair({ id: unref(id), ...submitData }) |
| | | : await addRepair(submitData); |
| | | if (code == 200) { |
| | | ElMessage.success(`${id.value ? "编辑" : "新增"}报修成功`); |
| | | visible.value = false; |
| | | emits("ok"); |
| | | } |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | loading.value = false; |
| | | }; |
| | | |
| | | const handleCancel = () => { |
| | | resetForm(); |
| | | repairFileList.value = []; |
| | | uploadFileList.value = []; |
| | | visible.value = false; |
| | | }; |
| | | |
| | | const handleClose = () => { |
| | | resetForm(); |
| | | repairFileList.value = []; |
| | | uploadFileList.value = []; |
| | | visible.value = false; |
| | | }; |
| | | |
| | | const openAdd = async () => { |
| | | openModal(); |
| | | id.value = undefined; |
| | | repairFileList.value = []; |
| | | uploadFileList.value = []; |
| | | visible.value = true; |
| | | await nextTick(); |
| | | await repairFormRef.value.loadDeviceName(); |
| | | await loadDeviceName(); |
| | | }; |
| | | |
| | | const openEdit = async (id) => { |
| | | const { data } = await getRepairById(id); |
| | | openModal(id); |
| | | const openEdit = async (editId) => { |
| | | const { data } = await getRepairById(editId); |
| | | id.value = editId; |
| | | visible.value = true; |
| | | await nextTick(); |
| | | await repairFormRef.value.loadDeviceName(); |
| | | await repairFormRef.value.setForm(data); |
| | | }; |
| | | |
| | | const close = () => { |
| | | repairFormRef.value.resetForm(); |
| | | closeModal(); |
| | | await loadDeviceName(); |
| | | setForm(data); |
| | | }; |
| | | |
| | | defineExpose({ |
| | |
| | | openEdit, |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .repair-upload-area { |
| | | :deep(.el-upload-list--picture-card) { |
| | | --el-upload-list-picture-card-size: 100px; |
| | | } |
| | | } |
| | | .upload-tip { |
| | | font-size: 12px; |
| | | color: var(--el-text-color-secondary); |
| | | margin-top: 8px; |
| | | } |
| | | </style> |