gongchunyi
昨天 c2ded572eff614cf2a259e263e60a5e8feb3e676
src/views/equipmentManagement/repair/index.vue
@@ -41,6 +41,24 @@
            @change="getTableData"
        />
      </el-form-item>
      <el-form-item label="报修日期">
        <el-date-picker
            v-model="filters.repairTimeStr"
            type="date"
            placeholder="请选择报修日期"
            size="default"
            @change="(date) => handleDateChange(date,2)"
        />
      </el-form-item>
      <el-form-item label="维修日期">
        <el-date-picker
            v-model="filters.maintenanceTimeStr"
            type="date"
            placeholder="请选择维修日期"
            size="default"
            @change="(date) => handleDateChange(date,1)"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
@@ -50,21 +68,16 @@
      <div class="actions">
        <el-text class="mx-1" size="large">设备报修</el-text>
        <div>
          <el-button
            type="primary"
            icon="Plus"
            :disabled="multipleList.length !== 1"
            @click="addMaintain"
          >
            新增维修
          </el-button>
          <el-button type="success" icon="Van" @click="addRepair">
            新增报修
          </el-button>
          <el-button @click="handleOut">
            导出
          </el-button>
          <el-button
            type="danger"
            icon="Delete"
            :disabled="multipleList.length <= 0"
            :disabled="multipleList.length <= 0 || hasFinishedStatus"
            @click="delRepairByIds(multipleList.map((item) => item.id))"
          >
            批量删除
@@ -72,35 +85,52 @@
        </div>
      </div>
      <PIMTable
        rowKey="id"
        isSelection
        :column="columns"
        :tableData="dataList"
        :page="{
          rowKey="id"
          isSelection
          :column="columns"
          :tableData="dataList"
          :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @selection-change="handleSelectionChange"
        @pagination="changePage"
          @selection-change="handleSelectionChange"
          @pagination="changePage"
      >
        <template #statusRef="{ row }">
          <el-tag v-if="row.status === 2" type="danger">失败</el-tag>
          <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.status === 0" type="warning">待维修</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button
            type="primary"
            text
            icon="editPen"
            link
            :disabled="row.status === 1"
            @click="editRepair(row.id)"
          >
            编辑
          </el-button>
          <el-button
            type="info"
            link
            :disabled="row.status === 1"
            @click="openAttachment(row)"
          >
            附件
          </el-button>
          <el-button
            type="success"
            link
            :disabled="row.status === 1"
            @click="addMaintain(row)"
          >
            维修
          </el-button>
          <el-button
            type="danger"
            text
            icon="delete"
            link
            :disabled="row.status === 1"
            @click="delRepairByIds(row.id)"
          >
            删除
@@ -108,23 +138,79 @@
        </template>
      </PIMTable>
    </div>
    <RepairModal ref="repairModalRef" @ok="getTableData" />
    <MaintainModal ref="maintainModalRef" @ok="getTableData" />
    <RepairModal ref="repairModalRef" @ok="getTableData"/>
    <MaintainModal ref="maintainModalRef" @ok="getTableData"/>
    <el-dialog v-model="attachment.visible" title="附件" width="860px" @closed="onAttachmentClosed">
      <div v-loading="attachment.loading" class="attachment-wrap">
        <div class="attachment-actions">
          <el-upload
            v-model:file-list="attachment.fileList"
            :action="upload.url"
            multiple
            ref="attachmentFileUpload"
            auto-upload
            :headers="upload.headers"
            :data="{ deviceRepairId: attachment.deviceRepairId }"
            :before-upload="handleAttachmentBeforeUpload"
            :on-error="handleAttachmentUploadError"
            :on-success="handleAttachmentUploadSuccess"
            :on-preview="handleAttachmentPreview"
            :on-remove="handleAttachmentRemove"
            list-type="picture-card"
            :limit="9"
            accept="image/png,image/jpeg,image/jpg"
          >
            +
          </el-upload>
        </div>
        <el-empty v-if="!attachment.loading && attachment.files.length === 0" description="暂无附件" />
      </div>
      <template #footer>
        <el-button @click="attachment.visible = false">关闭</el-button>
      </template>
    </el-dialog>
    <el-dialog
      v-model="attachment.previewVisible"
      title="图片预览"
      width="70%"
      append-to-body
    >
      <div class="attachment-preview-wrap">
        <img
          v-if="attachment.previewUrl"
          :src="attachment.previewUrl"
          alt="preview"
          class="attachment-preview-img"
        />
      </div>
    </el-dialog>
  </div>
</template>
<script setup>
import { usePaginationApi } from "@/hooks/usePaginationApi";
import { getRepairPage, delRepair } from "@/api/equipmentManagement/repair";
import { onMounted } from "vue";
import { onMounted, getCurrentInstance, computed } from "vue";
import {usePaginationApi} from "@/hooks/usePaginationApi";
import {
  getRepairPage,
  delRepair,
  getRepairFileList,
  deleteRepairFile,
} from "@/api/equipmentManagement/repair";
import RepairModal from "./Modal/RepairModal.vue";
import { ElMessageBox, ElMessage } from "element-plus";
import {ElMessageBox, ElMessage} from "element-plus";
import dayjs from "dayjs";
import MaintainModal from "./Modal/MaintainModal.vue";
import { getToken } from "@/utils/auth";
defineOptions({
  name: "设备报修",
});
const {proxy} = getCurrentInstance();
const javaApi = proxy?.javaApi || "";
// 模态框实例
const repairModalRef = ref();
@@ -132,6 +218,57 @@
// 表格多选框选中项
const multipleList = ref([]);
const attachment = reactive({
  visible: false,
  loading: false,
  deviceRepairId: undefined,
  files: [],
  fileList: [],
  previewVisible: false,
  previewUrl: "",
});
const getFileAccessUrl = (file = {}) => {
  if (file?.link) {
    if (String(file.link).startsWith('http')) return file.link;
    return normalizeFileUrl(file.link);
  }
  return normalizeFileUrl(file?.url || '');
};
const normalizeFileUrl = (rawUrl = '') => {
  let fileUrl = rawUrl || '';
  if (fileUrl && fileUrl.indexOf('\\') > -1) {
    const lowerPath = fileUrl.toLowerCase();
    const uploadPathIndex = lowerPath.indexOf('uploadpath');
    if (uploadPathIndex > -1) {
      fileUrl = fileUrl
        .substring(uploadPathIndex)
        .replace(/\\/g, '/');
    } else {
      fileUrl = fileUrl.replace(/\\/g, '/');
    }
  }
  fileUrl = fileUrl.replace(/^\/?uploadPath/, '/profile');
  if (!fileUrl.startsWith('http')) {
    if (!fileUrl.startsWith('/')) fileUrl = '/' + fileUrl;
    fileUrl = javaApi + fileUrl;
  }
  return fileUrl;
};
const attachmentUploadAction = "/device/repair/uploadFile";
const upload = reactive({
  url: import.meta.env.VITE_APP_BASE_API + attachmentUploadAction,
  headers: {
    Authorization: "Bearer " + getToken(),
  },
});
// 表格钩子
const {
@@ -143,75 +280,101 @@
  resetFilters,
  onCurrentChange,
} = usePaginationApi(
  getRepairPage,
  {
    searchText: undefined,
  },
  [
    getRepairPage,
    {
      label: "设备名称",
      align: "center",
      prop: "deviceName",
      deviceName: undefined,
      deviceModel: undefined,
      remark: undefined,
      maintenanceName: undefined,
      repairTimeStr: undefined,
      maintenanceTimeStr: undefined,
    },
    {
      label: "规格型号",
      align: "center",
      prop: "deviceModel",
    },
    {
      label: "报修日期",
      align: "center",
      prop: "repairTime",
      formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"),
    },
    {
      label: "报修人",
      align: "center",
      prop: "repairName",
    },
    {
      label: "故障现象",
      align: "center",
      prop: "remark",
    },
    {
      label: "维修人",
      align: "center",
      prop: "maintenanceName",
    },
    {
      label: "维修结果",
      align: "center",
      prop: "maintenanceResult",
    },
    {
      label: "维修日期",
      align: "center",
      prop: "maintenanceTime",
      formatData: (cell) => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
    },
    {
      label: "状态",
      align: "center",
      prop: "status",
      dataType: "slot",
      slot: "statusRef",
    },
    {
      fixed: "right",
      label: "操作",
      dataType: "slot",
      slot: "operation",
      align: "center",
      width: "200px",
    },
  ]
    [
      {
        label: "设备名称",
        align: "center",
        prop: "deviceName",
      },
      {
        label: "规格型号",
        align: "center",
        prop: "deviceModel",
      },
      {
        label: "报修日期",
        align: "center",
        prop: "repairTime",
        formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"),
      },
      {
        label: "报修人",
        align: "center",
        prop: "repairName",
      },
      {
        label: "故障现象",
        align: "center",
        prop: "remark",
      },
      {
        label: "维修人",
        align: "center",
        prop: "maintenanceName",
      },
      {
        label: "维修结果",
        align: "center",
        prop: "maintenanceResult",
      },
      {
        label: "维修日期",
        align: "center",
        prop: "maintenanceTime",
        formatData: (cell) => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
      },
      {
        label: "状态",
        align: "center",
        prop: "status",
        dataType: "slot",
        slot: "statusRef",
      },
      {
        fixed: "right",
        label: "操作",
        dataType: "slot",
        slot: "operation",
        align: "center",
        width: "300px",
      },
    ]
);
// type === 1 维修 2报修间
const handleDateChange = (value, type) => {
  filters.maintenanceTimeStr = null
  filters.c = null
  if (type === 1) {
    if (value) {
      filters.maintenanceTimeStr = dayjs(value).format("YYYY-MM-DD");
    }
  } else {
    if (value) {
      filters.repairTimeStr = dayjs(value).format("YYYY-MM-DD");
    }
  }
  getTableData();
};
// 多选后做什么
const handleSelectionChange = (selectionList) => {
  multipleList.value = selectionList;
};
// 检查选中的记录中是否有完结状态的
const hasFinishedStatus = computed(() => {
  return multipleList.value.some(item => item.status === 1)
})
// 新增报修
const addRepair = () => {
@@ -224,27 +387,143 @@
};
// 新增维修
const addMaintain = () => {
  const row = multipleList.value[0];
const addMaintain = (row) => {
  maintainModalRef.value.open(row.id, row);
};
const changePage = ({ page }) => {
const changePage = ({page, limit}) => {
  pagination.currentPage = page;
  pagination.pageSize = limit;
  onCurrentChange(page);
};
// 单行删除
const delRepairByIds = async (ids) => {
  // 检查是否有完结状态的记录
  const idsArray = Array.isArray(ids) ? ids : [ids];
  const hasFinished = idsArray.some(id => {
    const record = dataList.value.find(item => item.id === id);
    return record && record.status === 1;
  });
  if (hasFinished) {
    ElMessage.warning('不能删除状态为完结的记录');
    return;
  }
  ElMessageBox.confirm("确认删除报修数据, 此操作不可逆?", "警告", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(async () => {
    const { code } = await delRepair(ids);
    const {code} = await delRepair(ids);
    if (code === 200) {
      ElMessage.success("删除成功");
      getTableData();
    }
  });
};
// 导出
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        proxy.download("/device/repair/export", {}, "设备报修.xlsx");
      })
      .catch(() => {
        ElMessage.info("已取消");
      });
};
const openAttachment = async (row) => {
  attachment.fileList = [];
  attachment.visible = true;
  attachment.deviceRepairId = row?.id;
  await refreshAttachmentList();
};
const refreshAttachmentList = async () => {
  if (!attachment.deviceRepairId) return;
  attachment.loading = true;
  try {
    const res = await getRepairFileList(attachment.deviceRepairId);
    attachment.files = Array.isArray(res?.data) ? res.data : [];
    attachment.fileList = attachment.files.map((item) => ({
      id: item.id,
      name: item.name,
      url: getFileAccessUrl(item),
    }));
  } finally {
    attachment.loading = false;
  }
};
const onAttachmentClosed = () => {
  attachment.loading = false;
  attachment.deviceRepairId = undefined;
  attachment.files = [];
  attachment.fileList = [];
  attachment.previewVisible = false;
  attachment.previewUrl = "";
};
const handleAttachmentBeforeUpload = (file) => {
  const isImage = ["image/png", "image/jpeg", "image/jpg"].includes(file.type);
  if (!isImage) {
    ElMessage.error("只能上传 png/jpg/jpeg 图片");
    return false;
  }
  return true;
};
const handleAttachmentUploadSuccess = async (res) => {
  if (res?.code === 200) {
    ElMessage.success("上传成功");
    await refreshAttachmentList();
  }
};
const handleAttachmentUploadError = () => {
  ElMessage.error("上传失败");
};
const handleAttachmentPreview = (file) => {
  const rawUrl = file?.url || file?.response?.data?.link || file?.response?.data?.url || "";
  if (!rawUrl) {
    ElMessage.warning("图片地址无效,无法预览");
    return;
  }
  attachment.previewUrl = normalizeFileUrl(rawUrl);
  attachment.previewVisible = true;
};
const handleAttachmentRemove = async (file) => {
  // 仅移除前端未入库文件时,不调用删除接口
  const matched = attachment.files.find((item) => item.id === file?.id)
    || attachment.files.find((item) => item.name === file?.name);
  if (!matched) return;
  try {
    await confirmDeleteAttachment(matched);
  } finally {
    // 取消删除时,el-upload 已先移除,刷新一次保持与后端一致
    await refreshAttachmentList();
  }
};
const confirmDeleteAttachment = (fileRow) => {
  return ElMessageBox.confirm("确认删除该附件?", "警告", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(async () => {
    const { code } = await deleteRepairFile(fileRow.id);
    if (code === 200) {
      ElMessage.success("删除成功");
      await refreshAttachmentList();
    }
  });
};
@@ -258,9 +537,35 @@
.table_list {
  margin-top: unset;
}
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
}
.attachment-wrap {
  min-height: 240px;
}
.attachment-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}
.attachment-preview-wrap {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 360px;
}
.attachment-preview-img {
  max-width: 100%;
  max-height: 70vh;
  object-fit: contain;
}
</style>