| | |
| | | @click.stop="o.clickFun(scope.row)" |
| | | :key="key" |
| | | > |
| | | {{ o.name }} |
| | | {{ typeof o.name === 'function' ? o.name(scope.row) : o.name }} |
| | | </el-button> |
| | | <el-upload |
| | | :action=" |
| | |
| | | }; |
| | | |
| | | const getOperationColor = (operation, row) => { |
| | | const colorValue = typeof operation?.color === 'function' ? operation.color(row) : operation?.color; |
| | | const baseColor = |
| | | operation?.name === "删除" || operation?.name === "delete" |
| | | ? "#D93025" |
| | | : operation?.name === "详情" |
| | | ? "#67C23A" |
| | | : operation?.color || "var(--el-color-primary)"; |
| | | : colorValue || "var(--el-color-primary)"; |
| | | |
| | | if (isOperationDisabled(operation, row)) { |
| | | return fadeColor(baseColor, 0.35); |
| | |
| | | <el-input v-model="form.remarks" placeholder="请输入备注" type="textarea" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="任务状态" prop="isActive"> |
| | | <el-switch |
| | | v-model="form.isActive" |
| | | :active-value="1" |
| | | :inactive-value="0" |
| | | active-text="启用" |
| | | inactive-text="停用" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | |
| | | frequencyType: '', |
| | | frequencyDetail: '', |
| | | week: '', |
| | | time: '' |
| | | time: '', |
| | | isActive: 1 // 默认启用 |
| | | }, |
| | | rules: { |
| | | taskId: [{ required: true, message: "请选择设备", trigger: "change" },], |
| | |
| | | await loadDeviceName(); |
| | | |
| | | if (type === 'edit' && row) { |
| | | form.value = {...row} |
| | | form.value.inspector = form.value.inspectorIds.split(',').map(Number) |
| | | |
| | | form.value = { |
| | | ...data.form, // 先复制默认表单 |
| | | ...row, // 再覆盖 row 中的值 |
| | | } |
| | | // 确保 inspectorIds 存在才进行 split |
| | | if (form.value.inspectorIds) { |
| | | form.value.inspector = form.value.inspectorIds.split(',').map(Number) |
| | | } else { |
| | | form.value.inspector = [] |
| | | } |
| | | // 确保 isActive 有值,默认启用 |
| | | if (form.value.isActive === undefined || form.value.isActive === null) { |
| | | form.value.isActive = 1 |
| | | } |
| | | |
| | | if (row.frequencyDetail) { |
| | | if (row.frequencyType === 'WEEKLY') { |
| | | const parts = row.frequencyDetail.split(',') |
| | | if (parts.length === 2) { |
| | | form.value.week = parts[0] |
| | | form.value.time = parts[1] |
| | | } |
| | | } else if (row.frequencyType === 'DAILY') { |
| | | form.value.frequencyDetail = row.frequencyDetail |
| | | } else { |
| | | form.value.frequencyDetail = row.frequencyDetail |
| | | } |
| | | } |
| | | |
| | | // 如果有设备ID,自动设置设备信息 |
| | | if (form.value.taskId) { |
| | | setDeviceModel(form.value.taskId); |
| | |
| | | frequencyType: '', |
| | | frequencyDetail: '', |
| | | week: '', |
| | | time: '' |
| | | time: '', |
| | | isActive: 1 // 默认启用 |
| | | } |
| | | } |
| | | |
| | |
| | | proxy.$refs["formRef"].validate(async valid => { |
| | | if (valid) { |
| | | try { |
| | | form.value.inspectorIds = form.value.inspector.join(',') |
| | | delete form.value.inspector |
| | | // 确保 inspector 是数组才进行 join |
| | | if (form.value.inspector && Array.isArray(form.value.inspector)) { |
| | | form.value.inspectorIds = form.value.inspector.join(',') |
| | | delete form.value.inspector |
| | | } |
| | | |
| | | if (form.value.frequencyType === 'WEEKLY') { |
| | | let frequencyDetail = '' |
| | |
| | | :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }"> |
| | | <template #inspector="{ row }"> |
| | | <div class="person-tags"> |
| | | <!-- 调试信息,上线时删除 --> |
| | | <!-- {{ console.log('inspector data:', row.inspector) }} --> |
| | | <template v-if="row.inspector && row.inspector.length > 0"> |
| | | <el-tag v-for="(person, index) in row.inspector" |
| | | :key="index" |
| | |
| | | <span v-else |
| | | class="no-data">--</span> |
| | | </div> |
| | | </template> |
| | | <template #statusRef="{ row }"> |
| | | <el-tag v-if="row.isActive === true || row.isActive === 1" type="success">启用</el-tag> |
| | | <el-tag v-else type="danger">停用</el-tag> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | |
| | | import { Delete, Plus } from "@element-plus/icons-vue"; |
| | | import { onMounted, ref, reactive, getCurrentInstance, nextTick } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // 组件引入 |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | |
| | | delTimingTask, |
| | | inspectionTaskList, |
| | | timingTaskList, |
| | | addOrEditTimingTask, |
| | | } from "@/api/inspectionManagement/index.js"; |
| | | |
| | | // 全局变量 |
| | |
| | | // 单选框配置 |
| | | const activeRadio = ref("taskManage"); |
| | | const radios = reactive([ |
| | | { name: "taskManage", label: "定时任务管理" }, |
| | | { name: "task", label: "定时任务记录" }, |
| | | { name: "taskManage", label: "巡检任务管理" }, |
| | | { name: "task", label: "巡检任务记录" }, |
| | | ]); |
| | | |
| | | // 表格数据 |
| | |
| | | prop: "frequencyType", |
| | | label: "频次", |
| | | minWidth: 150, |
| | | // formatter: (_, __, val) => ({ |
| | | // DAILY: "每日", |
| | | // WEEKLY: "每周", |
| | | // MONTHLY: "每月", |
| | | // QUARTERLY: "季度" |
| | | // }[val] || "") |
| | | formatData: params => { |
| | | return params === "DAILY" |
| | | ? "每日" |
| | |
| | | }, |
| | | }, |
| | | { prop: "registrant", label: "登记人", minWidth: 100 }, |
| | | { prop: "createTime", label: "登记日期", minWidth: 100 }, |
| | | { prop: "createTime", label: "登记日期", minWidth: 120, formatData: (cell) => cell ? dayjs(cell).format("YYYY-MM-DD HH:MM:ss") : "-" }, |
| | | ]); |
| | | |
| | | const statusColumn = { |
| | | prop: "isActive", |
| | | label: "任务状态", |
| | | minWidth: 100, |
| | | align: "center", |
| | | dataType: "slot", |
| | | slot: "statusRef", |
| | | }; |
| | | |
| | | // 操作列配置 |
| | | const getOperationColumn = operations => { |
| | | if (!operations || operations.length === 0) return null; |
| | | |
| | | const operationList = operations |
| | | .map(op => { |
| | | switch (op) { |
| | | case "edit": |
| | | return { |
| | | name: "编辑", |
| | | clickFun: handleAdd, |
| | | color: "#409EFF", |
| | | }; |
| | | case "viewFile": |
| | | return { |
| | | name: "查看附件", |
| | | clickFun: viewFile, |
| | | color: "#67C23A", |
| | | }; |
| | | case "toggleActive": |
| | | return { |
| | | name: (row) => row.isActive === true || row.isActive === 1 ? "停用" : "启用", |
| | | clickFun: handleToggleActive, |
| | | color: (row) => row.isActive === true || row.isActive === 1 ? "#F56C6C" : "#67C23A", |
| | | }; |
| | | default: |
| | | return null; |
| | | } |
| | | }) |
| | | .filter(Boolean); |
| | | |
| | | const operationConfig = { |
| | | label: "操作", |
| | | width: 130, |
| | | width: 200, |
| | | fixed: "right", |
| | | dataType: "action", |
| | | operation: operations |
| | | .map(op => { |
| | | switch (op) { |
| | | case "edit": |
| | | return { |
| | | name: "编辑", |
| | | clickFun: handleAdd, |
| | | color: "#409EFF", |
| | | }; |
| | | case "viewFile": |
| | | return { |
| | | name: "查看附件", |
| | | clickFun: viewFile, |
| | | color: "#67C23A", |
| | | }; |
| | | default: |
| | | return null; |
| | | } |
| | | }) |
| | | .filter(Boolean), |
| | | operation: operationList, |
| | | }; |
| | | |
| | | return operationConfig; |
| | |
| | | // 单选变化 |
| | | const radioChange = value => { |
| | | if (value === "taskManage") { |
| | | const operationColumn = getOperationColumn(["edit"]); |
| | | const operationColumn = getOperationColumn(["edit", "toggleActive"]); |
| | | tableColumns.value = [ |
| | | ...columns.value, |
| | | statusColumn, |
| | | ...(operationColumn ? [operationColumn] : []), |
| | | ]; |
| | | operationsArr.value = ["edit"]; |
| | | operationsArr.value = ["edit", "toggleActive"]; |
| | | } else if (value === "task") { |
| | | const operationColumn = getOperationColumn(["viewFile"]); |
| | | tableColumns.value = [ |
| | |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | // 启用/停用切换 |
| | | const handleToggleActive = async (row) => { |
| | | const newStatus = row.isActive === true || row.isActive === 1 ? 0 : 1; |
| | | const actionText = newStatus === 1 ? "启用" : "停用"; |
| | | |
| | | try { |
| | | await proxy.$modal.confirm(`是否确认${actionText}该任务?`) |
| | | } catch { |
| | | return |
| | | } |
| | | |
| | | try { |
| | | await addOrEditTimingTask({ |
| | | id: row.id, |
| | | taskId: row.taskId, |
| | | taskName: row.taskName, |
| | | inspectorIds: row.inspectorIds, |
| | | remarks: row.remarks, |
| | | frequencyType: row.frequencyType, |
| | | frequencyDetail: row.frequencyDetail, |
| | | isActive: newStatus, |
| | | }) |
| | | proxy.$modal.msgSuccess(`${actionText}成功`) |
| | | handleQuery() |
| | | } catch (error) { |
| | | console.error(`${actionText}失败:`, error) |
| | | } |
| | | }; |
| | | |
| | | // 多选变更 |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | |
| | | .then(() => { |
| | | // 根据当前选中的标签页调用不同的导出接口 |
| | | if (activeRadio.value === "taskManage") { |
| | | // 定时任务管理 |
| | | proxy.download("/timingTask/export", {}, "定时任务管理.xlsx"); |
| | | // 保养任务管理 |
| | | proxy.download("/timingTask/export", {}, "保养任务管理.xlsx"); |
| | | } else if (activeRadio.value === "task") { |
| | | // 定时任务记录 |
| | | proxy.download("/inspectionTask/export", {}, "定时任务记录.xlsx"); |
| | | proxy.download("/inspectionTask/export", {}, "保养任务记录.xlsx"); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | |
| | | > |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-width="90px"> |
| | | <el-form-item label="维修人" prop="maintenanceName"> |
| | | <el-input v-model="form.maintenanceName" disabled placeholder="报修时指定的维修人" /> |
| | | <el-input |
| | | v-model="form.maintenanceName" |
| | | :placeholder="isEditableMaintenanceName ? '请输入维修人姓名' : '报修时指定的维修人'" |
| | | :disabled="!isEditableMaintenanceName" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="维修结果" prop="maintenanceResult"> |
| | | <el-input v-model="form.maintenanceResult" placeholder="请输入维修结果" /> |
| | | <el-input |
| | | v-model="form.maintenanceResult" |
| | | type="textarea" |
| | | :autosize="{ minRows: 2, maxRows: 6 }" |
| | | placeholder="请输入维修结果" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="维修状态" prop="status"> |
| | | <el-select v-model="form.status" style="width: 100%"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed } from "vue"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { addMaintain, uploadRepairFile } from "@/api/equipmentManagement/repair"; |
| | | import { REPAIR_FILE_TYPE_MAINTAIN } from "@/api/equipmentManagement/repairFileType.js"; |
| | | import useFormData from "@/hooks/useFormData"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import dayjs from "dayjs"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { UploadFilled } from "@element-plus/icons-vue"; |
| | |
| | | const ATTACH_MAX_MB = 50; |
| | | |
| | | const userStore = useUserStore(); |
| | | const systemUserNames = ref([]); |
| | | |
| | | const isSystemUser = (name) => { |
| | | return systemUserNames.value.includes(name); |
| | | }; |
| | | |
| | | // 维修人是否可编辑:外部人员时可以编辑修改 |
| | | const isEditableMaintenanceName = computed(() => { |
| | | const name = form.maintenanceName; |
| | | if (!name) return false; |
| | | return !isSystemUser(name); |
| | | }); |
| | | |
| | | const loadSystemUsers = async () => { |
| | | const res = await userListNoPage(); |
| | | systemUserNames.value = (res?.data || []).map((u) => u.nickName); |
| | | }; |
| | | |
| | | const rules = { |
| | | maintenanceName: [{ required: true, message: "请选择维修人", trigger: "change" }], |
| | |
| | | const sendForm = async () => { |
| | | const valid = await formRef.value?.validate().catch(() => false); |
| | | if (!valid) return; |
| | | if (form.maintenanceName !== userStore.nickName) { |
| | | const maintenanceName = form.maintenanceName; |
| | | // 如果维修人是系统用户且非当前用户,禁止提交 |
| | | if (isSystemUser(maintenanceName) && maintenanceName !== userStore.nickName) { |
| | | ElMessage.warning("仅指定的维修人可进行维修"); |
| | | return; |
| | | } |
| | |
| | | clearAttachmentQueue(); |
| | | visible.value = true; |
| | | await nextTick(); |
| | | await loadSystemUsers(); |
| | | setForm(row); |
| | | if (row?.maintenanceName && row.maintenanceName !== userStore.nickName) { |
| | | const maintenanceName = row?.maintenanceName; |
| | | // 如果维修人是系统用户且非当前用户,禁止操作 |
| | | if (isSystemUser(maintenanceName) && maintenanceName !== userStore.nickName) { |
| | | ElMessage.warning("仅指定的维修人可进行维修"); |
| | | visible.value = false; |
| | | } |
| | |
| | | <el-form-item label="维修人" required> |
| | | <el-select |
| | | v-model="form.maintenanceName" |
| | | placeholder="请选择维修人" |
| | | placeholder="请选择或输入维修人" |
| | | filterable |
| | | allow-create |
| | | default-first-option |
| | | clearable |
| | | style="width: 100%" |
| | | > |
| | |
| | | return; |
| | | } |
| | | if (!form.maintenanceName) { |
| | | ElMessage.warning("请选择维修人"); |
| | | ElMessage.warning("请选择或输入维修人"); |
| | | return; |
| | | } |
| | | loading.value = true; |
| | |
| | | import RepairDetailModal from "./Modal/RepairDetailModal.vue"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { |
| | | REPAIR_FILE_TYPE_PROBLEM, |
| | | isProblemRepairFile, |
| | |
| | | const acceptanceModalRef = ref(); |
| | | const repairDetailModalRef = ref(); |
| | | const userStore = useUserStore(); |
| | | const systemUserNames = ref([]); |
| | | |
| | | const loadSystemUsers = async () => { |
| | | const res = await userListNoPage(); |
| | | systemUserNames.value = (res?.data || []).map((u) => u.nickName); |
| | | }; |
| | | |
| | | const isSystemUser = (name) => systemUserNames.value.includes(name); |
| | | |
| | | // 表格多选框选中项 |
| | | const multipleList = ref([]); |
| | |
| | | const isCurrentUser = (name) => !!name && name === userStore.nickName; |
| | | |
| | | const canEdit = (row) => row.status === 0; |
| | | /** 仅报修时指定的维修人可维修 */ |
| | | // 仅报修时指定的维修人或外部人员时由报修人提交维修 |
| | | const canMaintain = (row) => { |
| | | if (row.status !== 0) return false; |
| | | if (!row.maintenanceName) return false; |
| | | return isCurrentUser(row.maintenanceName); |
| | | if (isSystemUser(row.maintenanceName)) { |
| | | return isCurrentUser(row.maintenanceName); |
| | | } |
| | | return true; |
| | | }; |
| | | const canDelete = (row) => row.status === 0; |
| | | /** 仅报修时指定的验收人可验收 */ |
| | |
| | | |
| | | onMounted(() => { |
| | | getTableData(); |
| | | loadSystemUsers(); |
| | | }); |
| | | </script> |
| | | |
| | |
| | | <el-form :model="form" label-width="100px"> |
| | | <el-form-item label="实际保养人"> |
| | | <el-input |
| | | v-model="form.maintenanceActuallyName" |
| | | placeholder="请输入实际保养人" |
| | | :model-value="currentUserName" |
| | | disabled |
| | | placeholder="当前登录用户" |
| | | ></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="实际保养日期"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed } from "vue"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { addMaintenance } from "@/api/equipmentManagement/upkeep"; |
| | | import useFormData from "@/hooks/useFormData"; |
| | |
| | | const loading = ref(false); |
| | | const userStore = useUserStore(); |
| | | |
| | | const currentUserName = computed(() => userStore.nickName || userStore.name || ""); |
| | | |
| | | const { form, resetForm } = useFormData({ |
| | | maintenanceActuallyName: undefined, // 实际保养人 |
| | | maintenanceActuallyTime: undefined, // 实际保养日期 |
| | |
| | | }); |
| | | |
| | | const setForm = (data) => { |
| | | form.maintenanceActuallyName = |
| | | data.maintenanceActuallyName ?? userStore.nickName; |
| | | // 实际保养人自动填充当前登录人,不可编辑 |
| | | form.maintenanceActuallyName = userStore.nickName || userStore.name || ""; |
| | | form.maintenanceActuallyTime = |
| | | data.maintenanceActuallyTime |
| | | data.maintenanceActuallyTime |
| | | ? dayjs(data.maintenanceActuallyTime).format("YYYY-MM-DD HH:mm:ss") |
| | | : dayjs().format("YYYY-MM-DD HH:mm:ss"); |
| | | form.maintenanceResult = data.maintenanceResult; |
| | |
| | | const sendForm = async () => { |
| | | loading.value = true; |
| | | try { |
| | | const { code } = await addMaintenance({ id: planId.value, ...form }); |
| | | // 确保实际保养人是当前登录人 |
| | | const payload = { |
| | | ...form, |
| | | maintenanceActuallyName: userStore.nickName || userStore.name || "", |
| | | }; |
| | | const { code } = await addMaintenance({ id: planId.value, ...payload }); |
| | | if (code == 200) { |
| | | ElMessage.success("保养成功"); |
| | | emits("ok"); |
| | |
| | | <FormDialog |
| | | v-model="visible" |
| | | :title="id ? '编辑设备保养计划' : '新增设备保养计划'" |
| | | width="640px" |
| | | width="680px" |
| | | @confirm="sendForm" |
| | | @cancel="handleCancel" |
| | | @close="handleClose" |
| | | > |
| | | <el-form :model="form" label-width="100px"> |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-width="120px"> |
| | | <el-form-item label="设备名称"> |
| | | <el-select |
| | | v-model="form.deviceLedgerId" |
| | |
| | | disabled |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="保养项目"> |
| | | <el-input |
| | | v-model="form.maintenanceLocation" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入保养项目" |
| | | /> |
| | | </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.maintenancePerson" |
| | | filterable |
| | | default-first-option |
| | | :reserve-keyword="false" |
| | | placeholder="请选择保养人" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in userList" |
| | | :key="'mp-' + item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="保养部位" prop="maintenanceLocation"> |
| | | <el-input |
| | | v-model="form.maintenanceLocation" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入保养部位" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="保养内容" prop="maintenanceItems"> |
| | | <el-input |
| | | v-model="form.maintenanceItems" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入保养内容" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item v-if="id" label="保修状态"> |
| | |
| | | <el-option label="失败" :value="2"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="计划保养日期"> |
| | | <el-form-item label="计划保养日期" prop="maintenancePlanTime"> |
| | | <el-date-picker |
| | | style="width: 100%" |
| | | v-model="form.maintenancePlanTime" |
| | |
| | | } 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 { computed, onMounted, ref } from "vue"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { computed, nextTick, onMounted, ref, unref } from "vue"; |
| | | import dayjs from "dayjs"; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import request from "@/utils/request"; |
| | |
| | | |
| | | const pendingTempFiles = ref([]); |
| | | const planFileList = ref([]); |
| | | // 编辑模式下已保存但待删除的文件列表 |
| | | const pendingDeleteFiles = ref([]); |
| | | |
| | | const registrantDisplayName = computed( |
| | | () => userStore.nickName || userStore.name || "当前登录用户" |
| | | ); |
| | | |
| | | const formRef = ref(); |
| | | |
| | | const rules = { |
| | | maintenancePerson: [{ required: true, message: "请选择保养人", trigger: "change" }], |
| | | maintenanceLocation: [{ required: true, message: "请输入保养部位", trigger: "blur" }], |
| | | maintenanceItems: [{ required: true, message: "请输入保养内容", trigger: "blur" }], |
| | | maintenancePlanTime: [{ required: true, message: "请选择计划保养日期", trigger: "change" }], |
| | | }; |
| | | |
| | | const syncCreateUserFromLogin = () => { |
| | | if (userStore.id != null && userStore.id !== "") { |
| | |
| | | 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({ |
| | |
| | | deviceName: undefined, |
| | | deviceModel: undefined, |
| | | maintenanceLocation: undefined, |
| | | maintenanceItems: undefined, |
| | | maintenancePlanTime: undefined, |
| | | maintenancePerson: undefined, |
| | | createUser: undefined, |
| | | status: 0, |
| | | }); |
| | |
| | | const resetAttachmentState = () => { |
| | | pendingTempFiles.value = []; |
| | | planFileList.value = []; |
| | | pendingDeleteFiles.value = []; |
| | | }; |
| | | |
| | | const normalizeFilePreviewUrl = (url = "") => { |
| | |
| | | status: "success", |
| | | uid: `saved-${item.id}`, |
| | | fileId: item.id, |
| | | // 标记为已保存的文件,不是新上传的 |
| | | isNew: false, |
| | | })); |
| | | }; |
| | | |
| | |
| | | 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 || {}; |
| | | const tempId = data.tempId; |
| | | |
| | | // 记录临时文件信息 |
| | | pendingTempFiles.value.push({ |
| | | tempId: data.tempId, |
| | | tempId: tempId, |
| | | name: data.originalName || file.name, |
| | | fileId: id.value ? tempId : undefined, |
| | | }); |
| | | |
| | | onSuccess(res); |
| | | planFileList.value.push({ |
| | | name: data.originalName || file.name, |
| | | url: "", |
| | | status: "success", |
| | | uid: data.tempId, |
| | | tempId: data.tempId, |
| | | uid: tempId, |
| | | tempId: tempId, |
| | | // 标记为新上传的临时文件 |
| | | isNew: true, |
| | | }); |
| | | ElMessage.success("附件上传成功"); |
| | | } 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("删除附件失败"); |
| | | } |
| | | // 从显示列表中移除 |
| | | planFileList.value = planFileList.value.filter((f) => f.uid !== file.uid); |
| | | |
| | | const tempId = file.tempId || file.uid; |
| | | if (file.isNew) { |
| | | // 新上传的临时文件,直接从待上传列表移除 |
| | | pendingTempFiles.value = pendingTempFiles.value.filter((f) => f.tempId !== tempId); |
| | | 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); |
| | | |
| | | if (file.fileId) { |
| | | pendingDeleteFiles.value.push({ |
| | | fileId: file.fileId, |
| | | name: file.name, |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | 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 processPendingDeletes = async () => { |
| | | if (!pendingDeleteFiles.value.length) return; |
| | | for (const file of pendingDeleteFiles.value) { |
| | | await delMaintenanceTaskFile(file.fileId); |
| | | } |
| | | }; |
| | | |
| | |
| | | form.deviceName = data.deviceName; |
| | | form.deviceModel = data.deviceModel; |
| | | form.maintenanceLocation = data.maintenanceLocation; |
| | | form.maintenanceItems = data.maintenanceItems; |
| | | 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 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; |
| | | } |
| | | } |
| | | // 传递临时文件ID列表,由后端统一处理 |
| | | if (pendingTempFiles.value.length > 0) { |
| | | payload.tempFileIds = pendingTempFiles.value.map((f) => f.tempId); |
| | | } |
| | | return payload; |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | loadDeviceName(); |
| | | loadUserList(); |
| | | }); |
| | | |
| | | const openEdit = async (editId) => { |
| | | resetAttachmentState(); |
| | | if (!userList.value.length) { |
| | | await loadUserList(); |
| | | } |
| | | const { data } = await getUpkeepById(editId); |
| | | id.value = editId; |
| | | visible.value = true; |
| | |
| | | |
| | | const sendForm = async () => { |
| | | syncCreateUserFromLogin(); |
| | | const valid = await formRef.value?.validate().catch(() => false); |
| | | if (!valid) return; |
| | | loading.value = true; |
| | | try { |
| | | const payload = buildSubmitPayload(); |
| | | if (id.value) { |
| | | const { code } = await editUpkeep({ id: unref(id), ...form }); |
| | | // 编辑模式:先处理待删除的文件,再保存表单 |
| | | await processPendingDeletes(); |
| | | const { code } = await editUpkeep({ id: unref(id), ...payload }); |
| | | if (code == 200) { |
| | | ElMessage.success("编辑计划成功"); |
| | | visible.value = false; |
| | | emits("ok"); |
| | | } |
| | | } else { |
| | | const res = await addUpkeep(form); |
| | | // 新增模式:tempFileIds 会随 payload 一起传到后端,由后端处理临时文件的关联 |
| | | 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"); |
| | |
| | | resetForm(); |
| | | resetAttachmentState(); |
| | | syncCreateUserFromLogin(); |
| | | if (userStore.id != null && userStore.id !== "") { |
| | | form.maintenancePerson = userStore.id; |
| | | } |
| | | visible.value = true; |
| | | }; |
| | | |
| | |
| | | <el-dialog |
| | | v-model="visible" |
| | | title="保养计划详情" |
| | | width="820px" |
| | | width="960px" |
| | | destroy-on-close |
| | | @closed="onClosed" |
| | | > |
| | |
| | | <el-descriptions :column="2" border size="small"> |
| | | <el-descriptions-item label="设备名称">{{ detail.deviceName || "—" }}</el-descriptions-item> |
| | | <el-descriptions-item label="规格型号">{{ detail.deviceModel || "—" }}</el-descriptions-item> |
| | | <el-descriptions-item label="保养项目" :span="2"> |
| | | <el-descriptions-item label="保养部位" :span="2"> |
| | | <div class="multiline">{{ detail.maintenanceLocation || "—" }}</div> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="保养内容" :span="2"> |
| | | <div class="multiline">{{ detail.maintenanceItems || "—" }}</div> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="保养人">{{ detail.maintenancePerson || "—" }}</el-descriptions-item> |
| | | <el-descriptions-item label="计划保养日期">{{ fmtDate(detail.maintenancePlanTime) }}</el-descriptions-item> |
| | | <el-descriptions-item label="录入人">{{ detail.createUserName || "—" }}</el-descriptions-item> |
| | |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="任务状态"> |
| | | <el-switch |
| | | v-model="form.isActive" |
| | | :active-value="1" |
| | | :inactive-value="0" |
| | | active-text="启用" |
| | | inactive-text="停用" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="保养项目" prop="maintenanceItems"> |
| | | <el-form-item label="保养部位" prop="maintenanceLocation"> |
| | | <el-input |
| | | v-model="form.maintenanceLocation" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入保养部位" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="保养内容" prop="maintenanceItems"> |
| | | <el-input |
| | | v-model="form.maintenanceItems" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入保养项目" |
| | | placeholder="请输入保养内容" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | // 录入人、保养人:用户 id |
| | | inspector: undefined, |
| | | maintenancePerson: undefined, |
| | | maintenanceLocation: '', |
| | | maintenanceItems: '', |
| | | remarks: '', |
| | | frequencyType: '', |
| | |
| | | week: '', |
| | | time: '', |
| | | deviceModel: undefined, // 规格型号 |
| | | registrationDate: '' |
| | | registrationDate: '', |
| | | isActive: 0, // 任务状态:0-停用,1-启用 |
| | | }, |
| | | rules: { |
| | | taskId: [{ required: true, message: "请选择设备", trigger: "change" },], |
| | | inspector: [{ required: true, message: "请选择录入人", trigger: "blur" },], |
| | | maintenancePerson: [{ required: true, message: "请选择保养人", trigger: "change" }], |
| | | maintenanceItems: [{ required: true, message: "请输入保养项目", trigger: "blur" }], |
| | | maintenanceLocation: [{ required: true, message: "请输入保养部位", trigger: "blur" }], |
| | | maintenanceItems: [{ required: true, message: "请输入保养内容", trigger: "blur" }], |
| | | registrationDate: [{ required: true, message: "请选择登记时间", trigger: "change" }], |
| | | frequencyDetail: [{ |
| | | validator: (rule, value, callback) => { |
| | |
| | | taskName: undefined, |
| | | inspector: undefined, |
| | | maintenancePerson: undefined, |
| | | maintenanceLocation: '', |
| | | maintenanceItems: '', |
| | | remarks: '', |
| | | frequencyType: '', |
| | |
| | | week: '', |
| | | time: '', |
| | | deviceModel: undefined, |
| | | registrationDate: '' |
| | | registrationDate: '', |
| | | isActive: 0, |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | // 录入日期:直接使用表单里的 registrationDate 字段 |
| | | // 一些默认状态字段 |
| | | if (payload.status === undefined || payload.status === null || payload.status === '') { |
| | | payload.status = '0' // 默认状态,可按实际枚举调整 |
| | | if (payload.isActive === undefined || payload.isActive === null) { |
| | | payload.isActive = 0 // 默认停用 |
| | | } |
| | | payload.active = true |
| | | payload.deleted = 0 |
| | | |
| | | if (operationType.value === 'edit') { |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-tabs v-model="activeTab" @tab-change="handleTabChange"> |
| | | <!-- 定时任务管理tab --> |
| | | <el-tab-pane label="定时任务管理" name="scheduled"> |
| | | <!-- 保养任务管理tab --> |
| | | <el-tab-pane label="保养任务管理" name="scheduled"> |
| | | <div class="search_form"> |
| | | <el-form :model="scheduledFilters" :inline="true"> |
| | | <el-form-item label="任务名称"> |
| | |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="任务状态"> |
| | | <el-select v-model="scheduledFilters.status" placeholder="请选择任务状态" clearable style="width: 200px"> |
| | | <el-option label="启用" value="1" /> |
| | | <el-option label="停用" value="0" /> |
| | | <el-select v-model="scheduledFilters.isActive" placeholder="请选择任务状态" clearable style="width: 200px"> |
| | | <el-option label="启用" :value="1" /> |
| | | <el-option label="停用" :value="0" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | |
| | | </div> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <el-text class="mx-1" size="large">定时任务管理</el-text> |
| | | <el-text class="mx-1" size="large">保养任务管理</el-text> |
| | | <div> |
| | | <el-button type="primary" icon="Plus" @click="addScheduledTask"> |
| | | 新增任务 |
| | |
| | | @pagination="changeScheduledPage" |
| | | > |
| | | <template #statusRef="{ row }"> |
| | | <el-tag v-if="row.status === 1" type="success">启用</el-tag> |
| | | <el-tag v-if="row.status === 0" type="danger">停用</el-tag> |
| | | <el-tag v-if="row.isActive === 1" type="success">启用</el-tag> |
| | | <el-tag v-if="row.isActive === 0" type="danger">停用</el-tag> |
| | | </template> |
| | | <template #operation="{ row }"> |
| | | <el-button |
| | |
| | | </el-tab-pane> |
| | | |
| | | <!-- 任务记录tab(原设备保养页面) --> |
| | | <el-tab-pane label="任务记录" name="record"> |
| | | <el-tab-pane label="保养任务记录" name="record"> |
| | | <div class="search_form"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="设备名称"> |
| | |
| | | </div> |
| | | <div class="table_list"> |
| | | <div class="actions"> |
| | | <el-text class="mx-1" size="large">任务记录</el-text> |
| | | <el-text class="mx-1" size="large">保养任务记录</el-text> |
| | | <div> |
| | | <el-button type="success" icon="Van" @click="addPlan"> |
| | | 新增计划 |
| | |
| | | }) |
| | | const multipleList = ref([]) |
| | | |
| | | // 定时任务管理tab相关变量 |
| | | // 保养任务管理tab相关变量 |
| | | const scheduledFilters = reactive({ |
| | | taskName: '', |
| | | status: '', |
| | | isActive: '', |
| | | }) |
| | | |
| | | const scheduledDataList = ref([]) |
| | |
| | | }) |
| | | const scheduledMultipleList = ref([]) |
| | | |
| | | // 定时任务管理表格列配置 |
| | | // 保养任务管理表格列配置 |
| | | const scheduledColumns = ref([ |
| | | { prop: "taskName", label: "设备名称"}, |
| | | { |
| | |
| | | { prop: "registrant", label: "登记人", minWidth: 100 }, |
| | | { prop: "maintenancePerson", label: "保养人", minWidth: 100 }, |
| | | { |
| | | prop: "maintenanceLocation", |
| | | label: "保养部位", |
| | | minWidth: 150, |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | prop: "maintenanceItems", |
| | | label: "保养项目", |
| | | label: "保养内容", |
| | | minWidth: 180, |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { prop: "registrationDate", label: "登记日期", minWidth: 100 }, |
| | | { |
| | | label: "任务状态", |
| | | prop: "isActive", |
| | | minWidth: 100, |
| | | dataType: "slot", |
| | | slot: "statusRef", |
| | | align: "center", |
| | | }, |
| | | { |
| | | fixed: "right", |
| | | label: "操作", |
| | |
| | | prop: "deviceModel", |
| | | }, |
| | | { |
| | | label: "保养项目", |
| | | label: "保养部位", |
| | | align: "center", |
| | | prop: "maintenanceLocation", |
| | | minWidth: 150, |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | | label: "保养内容", |
| | | align: "center", |
| | | prop: "maintenanceItems", |
| | | minWidth: 150, |
| | | showOverflowTooltip: true, |
| | | }, |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | // 定时任务管理相关方法 |
| | | // 保养任务管理相关方法 |
| | | const getScheduledTableData = async () => { |
| | | try { |
| | | const params = { |
| | | current: scheduledPagination.currentPage, |
| | | size: scheduledPagination.pageSize, |
| | | taskName: scheduledFilters.taskName || undefined, |
| | | status: scheduledFilters.status || undefined, |
| | | isActive: scheduledFilters.isActive !== '' ? scheduledFilters.isActive : undefined, |
| | | } |
| | | const { code, data } = await deviceMaintenanceTaskList(params) |
| | | if (code === 200) { |
| | |
| | | |
| | | const resetScheduledFilters = () => { |
| | | scheduledFilters.taskName = '' |
| | | scheduledFilters.status = '' |
| | | scheduledFilters.isActive = '' |
| | | getScheduledTableData() |
| | | } |
| | | |