| | |
| | | <FormDialog |
| | | v-model="visible" |
| | | :title="id ? '编辑设备保养计划' : '新增设备保养计划'" |
| | | width="500px" |
| | | width="640px" |
| | | @confirm="sendForm" |
| | | @cancel="handleCancel" |
| | | @close="handleClose" |
| | | > |
| | | <el-form :model="form" label-width="100px"> |
| | | <el-form :model="form" :rules="rules" label-width="100px"> |
| | | <el-form-item label="设备名称"> |
| | | <el-select |
| | | v-model="form.deviceLedgerId" |
| | |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="录入人"> |
| | | <el-input |
| | | :model-value="registrantDisplayName" |
| | | disabled |
| | | placeholder="当前登录用户" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="保养人" prop="maintenancePerson"> |
| | | <el-select |
| | | v-model="form.createUser" |
| | | placeholder="请选择" |
| | | v-model="form.maintenancePerson" |
| | | filterable |
| | | default-first-option |
| | | :reserve-keyword="false" |
| | | clearable |
| | | placeholder="请选择保养人" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in userList" |
| | | :key="item.userId" |
| | | :key="'mp-' + item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="保养项目"> |
| | | <el-input |
| | | v-model="form.maintenanceLocation" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入保养项目" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item v-if="id" label="保修状态"> |
| | | <el-select v-model="form.status"> |
| | |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="附件"> |
| | | <el-upload |
| | | :http-request="handlePlanFileUpload" |
| | | :file-list="planFileList" |
| | | :on-remove="handlePlanFileRemove" |
| | | multiple |
| | | list-type="picture-card" |
| | | > |
| | | <el-icon><Plus /></el-icon> |
| | | </el-upload> |
| | | <span v-if="!id" class="upload-tip">可先选择附件,保存计划后自动关联到本计划</span> |
| | | </el-form-item> |
| | | </el-form> |
| | | </FormDialog> |
| | | </template> |
| | |
| | | editUpkeep, |
| | | getUpkeepById, |
| | | } from "@/api/equipmentManagement/upkeep"; |
| | | import { |
| | | listMaintenanceTaskFiles, |
| | | bindMaintenanceTaskFile, |
| | | uploadMaintenanceTaskFile, |
| | | delMaintenanceTaskFile, |
| | | } from "@/api/equipmentManagement/maintenanceTaskFile"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { Plus } from "@element-plus/icons-vue"; |
| | | import useFormData from "@/hooks/useFormData"; |
| | | import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; |
| | | import { onMounted } from "vue"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { computed, nextTick, onMounted, ref, unref } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import request from "@/utils/request"; |
| | | |
| | | defineOptions({ |
| | | name: "设备保养新增计划", |
| | |
| | | |
| | | const emits = defineEmits(["ok"]); |
| | | |
| | | const userStore = useUserStore(); |
| | | const javaApi = import.meta.env.VITE_APP_BASE_API; |
| | | |
| | | const pendingTempFiles = ref([]); |
| | | const planFileList = ref([]); |
| | | |
| | | const registrantDisplayName = computed( |
| | | () => userStore.nickName || userStore.name || "当前登录用户" |
| | | ); |
| | | |
| | | const rules = { |
| | | maintenancePerson: [{ required: true, message: "请选择保养人", trigger: "change" }], |
| | | }; |
| | | |
| | | const syncCreateUserFromLogin = () => { |
| | | if (userStore.id != null && userStore.id !== "") { |
| | | form.createUser = userStore.id; |
| | | } |
| | | }; |
| | | |
| | | const id = ref(); |
| | | const visible = ref(false); |
| | | const loading = ref(false); |
| | | |
| | | const deviceOptions = ref([]); |
| | | const userList = ref([]); |
| | | |
| | | const loadDeviceName = async () => { |
| | | const { data } = await getDeviceLedger(); |
| | | deviceOptions.value = data; |
| | | }; |
| | | |
| | | const loadUserList = async () => { |
| | | const res = await userListNoPageByTenantId(); |
| | | userList.value = res.data || []; |
| | | }; |
| | | |
| | | const { form, resetForm } = useFormData({ |
| | | deviceLedgerId: undefined, // 设备Id |
| | | deviceName: undefined, // 设备名称 |
| | | deviceModel: undefined, // 规格型号 |
| | | maintenancePlanTime: undefined, // 计划保养日期 |
| | | createUser: undefined, // 录入人 |
| | | status: 0, //保修状态 |
| | | deviceLedgerId: undefined, |
| | | deviceName: undefined, |
| | | deviceModel: undefined, |
| | | maintenanceLocation: undefined, |
| | | maintenancePlanTime: undefined, |
| | | maintenancePerson: undefined, |
| | | createUser: undefined, |
| | | status: 0, |
| | | }); |
| | | |
| | | const setDeviceModel = (deviceId) => { |
| | | const option = deviceOptions.value.find((item) => item.id === deviceId); |
| | | form.deviceModel = option.deviceModel; |
| | | if (option) { |
| | | form.deviceModel = option.deviceModel; |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * @desc 设置表单内容 |
| | | * @param data 设备信息 |
| | | */ |
| | | const resetAttachmentState = () => { |
| | | pendingTempFiles.value = []; |
| | | planFileList.value = []; |
| | | }; |
| | | |
| | | const normalizeFilePreviewUrl = (url = "") => { |
| | | if (!url) return ""; |
| | | if (url.startsWith("http")) return url; |
| | | if (url.startsWith("/profile")) return javaApi + url; |
| | | return url; |
| | | }; |
| | | |
| | | const loadPlanFiles = async (planId) => { |
| | | if (!planId) return; |
| | | const res = await listMaintenanceTaskFiles({ |
| | | current: 1, |
| | | size: 100, |
| | | deviceMaintenanceId: planId, |
| | | }); |
| | | const records = res?.data?.records || []; |
| | | planFileList.value = records.map((item) => ({ |
| | | name: item.name, |
| | | url: normalizeFilePreviewUrl(item.url), |
| | | status: "success", |
| | | uid: `saved-${item.id}`, |
| | | fileId: item.id, |
| | | })); |
| | | }; |
| | | |
| | | const uploadTempFile = (file) => { |
| | | const fd = new FormData(); |
| | | fd.append("file", file); |
| | | fd.append("type", "16"); |
| | | return request({ |
| | | url: "/file/upload", |
| | | method: "post", |
| | | data: fd, |
| | | headers: { "Content-Type": "multipart/form-data" }, |
| | | }); |
| | | }; |
| | | |
| | | const handlePlanFileUpload = async (options) => { |
| | | const { file, onSuccess, onError } = options; |
| | | try { |
| | | if (id.value) { |
| | | const fd = new FormData(); |
| | | fd.append("file", file); |
| | | fd.append("deviceMaintenanceId", String(id.value)); |
| | | const res = await uploadMaintenanceTaskFile(fd); |
| | | if (res.code === 200) { |
| | | await loadPlanFiles(id.value); |
| | | onSuccess(res); |
| | | ElMessage.success("附件上传成功"); |
| | | } else { |
| | | onError(new Error(res.msg || "上传失败")); |
| | | } |
| | | return; |
| | | } |
| | | const res = await uploadTempFile(file); |
| | | if (res.code !== 200) { |
| | | onError(new Error(res.msg || "上传失败")); |
| | | return; |
| | | } |
| | | const data = res.data || {}; |
| | | pendingTempFiles.value.push({ |
| | | tempId: data.tempId, |
| | | name: data.originalName || file.name, |
| | | }); |
| | | onSuccess(res); |
| | | planFileList.value.push({ |
| | | name: data.originalName || file.name, |
| | | url: "", |
| | | status: "success", |
| | | uid: data.tempId, |
| | | tempId: data.tempId, |
| | | }); |
| | | } catch (e) { |
| | | onError(e); |
| | | ElMessage.error("附件上传失败"); |
| | | } |
| | | }; |
| | | |
| | | const handlePlanFileRemove = async (file) => { |
| | | if (file.fileId) { |
| | | try { |
| | | await delMaintenanceTaskFile(file.fileId); |
| | | await loadPlanFiles(id.value); |
| | | } catch (e) { |
| | | ElMessage.error("删除附件失败"); |
| | | } |
| | | return; |
| | | } |
| | | const tempId = file.tempId || file.uid; |
| | | pendingTempFiles.value = pendingTempFiles.value.filter((f) => f.tempId !== tempId); |
| | | planFileList.value = planFileList.value.filter((f) => (f.tempId || f.uid) !== tempId); |
| | | }; |
| | | |
| | | const bindPendingFiles = async (planId) => { |
| | | if (!pendingTempFiles.value.length) return; |
| | | for (const item of pendingTempFiles.value) { |
| | | await bindMaintenanceTaskFile({ |
| | | tempId: item.tempId, |
| | | name: item.name, |
| | | deviceMaintenanceId: planId, |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const setForm = (data) => { |
| | | form.deviceLedgerId = data.deviceLedgerId; |
| | | form.deviceName = data.deviceName; |
| | | form.deviceModel = data.deviceModel; |
| | | form.createUser = Number(data.createUser); |
| | | form.maintenanceLocation = data.maintenanceLocation; |
| | | form.status = data.status; |
| | | syncCreateUserFromLogin(); |
| | | if (data.maintenancePersonId) { |
| | | form.maintenancePerson = data.maintenancePersonId; |
| | | } else if (data.maintenancePerson) { |
| | | const matched = userList.value.find( |
| | | (u) => u.nickName === data.maintenancePerson |
| | | ); |
| | | if (matched) { |
| | | form.maintenancePerson = matched.userId; |
| | | } |
| | | } |
| | | form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format( |
| | | "YYYY-MM-DD HH:mm:ss" |
| | | ); |
| | | }; |
| | | |
| | | // 用户列表 |
| | | const userList = ref([]); |
| | | const buildSubmitPayload = () => { |
| | | const payload = { ...form }; |
| | | const maintenancePersonUserId = form.maintenancePerson; |
| | | if (maintenancePersonUserId) { |
| | | const maintainer = userList.value.find( |
| | | (u) => String(u.userId) === String(maintenancePersonUserId) |
| | | ); |
| | | if (maintainer) { |
| | | payload.maintenancePersonId = maintainer.userId; |
| | | payload.maintenancePerson = maintainer.nickName; |
| | | } |
| | | } |
| | | return payload; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | loadDeviceName(); |
| | | userListNoPage().then((res) => { |
| | | userList.value = res.data; |
| | | }); |
| | | loadUserList(); |
| | | }); |
| | | |
| | | const openEdit = async (editId) => { |
| | | resetAttachmentState(); |
| | | if (!userList.value.length) { |
| | | await loadUserList(); |
| | | } |
| | | const { data } = await getUpkeepById(editId); |
| | | id.value = editId; |
| | | visible.value = true; |
| | | await nextTick(); |
| | | setForm(data); |
| | | await loadPlanFiles(editId); |
| | | }; |
| | | |
| | | const sendForm = async () => { |
| | | syncCreateUserFromLogin(); |
| | | loading.value = true; |
| | | try { |
| | | const { code } = id.value |
| | | ? await editUpkeep({ id: unref(id), ...form }) |
| | | : await addUpkeep(form); |
| | | if (code == 200) { |
| | | ElMessage.success(`${id.value ? "编辑" : "新增"}计划成功`); |
| | | visible.value = false; |
| | | emits("ok"); |
| | | const payload = buildSubmitPayload(); |
| | | if (id.value) { |
| | | const { code } = await editUpkeep({ id: unref(id), ...payload }); |
| | | if (code == 200) { |
| | | ElMessage.success("编辑计划成功"); |
| | | visible.value = false; |
| | | emits("ok"); |
| | | } |
| | | } else { |
| | | const res = await addUpkeep(payload); |
| | | if (res.code == 200) { |
| | | const planId = res.data?.id; |
| | | if (planId) { |
| | | await bindPendingFiles(planId); |
| | | } |
| | | ElMessage.success("新增计划成功"); |
| | | visible.value = false; |
| | | emits("ok"); |
| | | } |
| | | } |
| | | } finally { |
| | | loading.value = false; |
| | |
| | | |
| | | const handleCancel = () => { |
| | | resetForm(); |
| | | resetAttachmentState(); |
| | | visible.value = false; |
| | | }; |
| | | |
| | | const handleClose = () => { |
| | | resetForm(); |
| | | resetAttachmentState(); |
| | | visible.value = false; |
| | | }; |
| | | |
| | | const openModal = () => { |
| | | id.value = undefined; |
| | | resetForm(); |
| | | resetAttachmentState(); |
| | | syncCreateUserFromLogin(); |
| | | if (userStore.id != null && userStore.id !== "") { |
| | | form.maintenancePerson = userStore.id; |
| | | } |
| | | visible.value = true; |
| | | }; |
| | | |
| | |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped></style> |
| | | <style lang="scss" scoped> |
| | | .upload-tip { |
| | | display: block; |
| | | font-size: 12px; |
| | | color: #999; |
| | | margin-top: 8px; |
| | | line-height: 1.4; |
| | | } |
| | | </style> |