已添加2个文件
已修改14个文件
1482 ■■■■ 文件已修改
src/api/equipmentManagement/repair.js 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages.json 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/equipmentManagement/repair/acceptance.vue 259 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/equipmentManagement/repair/add.vue 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/equipmentManagement/repair/detail.vue 362 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/equipmentManagement/repair/index.vue 80 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/equipmentManagement/repair/maintain.vue 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/qualityManagement/finalInspection/add.vue 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/qualityManagement/finalInspection/detail.vue 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/qualityManagement/finalInspection/index.vue 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/qualityManagement/materialInspection/add.vue 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/qualityManagement/materialInspection/detail.vue 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/qualityManagement/materialInspection/index.vue 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/qualityManagement/processInspection/add.vue 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/qualityManagement/processInspection/detail.vue 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/qualityManagement/processInspection/index.vue 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/equipmentManagement/repair.js
@@ -100,4 +100,17 @@
    url: `/device/repair/file/${fileId}`,
    method: "delete",
  });
};
/**
 * @desc è®¾å¤‡æŠ¥ä¿®éªŒæ”¶
 * @param {验收参数} data
 * @returns
 */
export const acceptRepair = (data) => {
  return request({
    url: "/device/repair/acceptance",
    method: "post",
    data,
  });
};
src/pages.json
@@ -598,6 +598,20 @@
      }
    },
    {
      "path": "pages/equipmentManagement/repair/detail",
      "style": {
        "navigationBarTitleText": "设备报修详情",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/equipmentManagement/repair/acceptance",
      "style": {
        "navigationBarTitleText": "设备报修验收",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/equipmentManagement/upkeep/index",
      "style": {
        "navigationBarTitleText": "设备保养",
src/pages/equipmentManagement/repair/acceptance.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,259 @@
<template>
  <view class="repair-acceptance">
    <PageHeader title="设备报修验收"
                @back="goBack" />
    <view class="section"
          v-if="detail">
      <view class="section-title">报修信息</view>
      <view class="info-item">
        <text class="info-label">设备名称</text>
        <text class="info-value">{{ detail.deviceName || '-' }}</text>
      </view>
      <view class="info-item">
        <text class="info-label">验收人</text>
        <text class="info-value">{{ detail.acceptanceName || '-' }}</text>
      </view>
    </view>
    <u-form ref="formRef"
            :model="form"
            label-width="110">
      <u-cell-group title="验收信息">
        <u-form-item label="验收人"
                     border-bottom>
          <u-input v-model="form.acceptanceName"
                   disabled
                   placeholder="验收人" />
        </u-form-item>
        <u-form-item label="验收时间"
                     required
                     border-bottom>
          <u-input v-model="form.acceptanceTime"
                   placeholder="请选择验收时间"
                   readonly
                   @click="showDatePicker = true" />
          <template #right>
            <u-icon name="arrow-right"
                    @click="showDatePicker = true" />
          </template>
        </u-form-item>
        <u-form-item label="验收备注"
                     required
                     border-bottom>
          <u-textarea v-model="form.acceptanceRemark"
                      placeholder="请输入验收备注"
                      :maxlength="200"
                      count
                      :autoHeight="true" />
        </u-form-item>
      </u-cell-group>
      <view class="footer-btns">
        <u-button class="cancel-btn"
                  @click="goBack">取消</u-button>
        <u-button class="save-btn"
                  type="primary"
                  :loading="loading"
                  @click="submitAcceptance">确认验收</u-button>
      </view>
    </u-form>
    <up-datetime-picker :show="showDatePicker"
                        v-model="pickerDateValue"
                        mode="datetime"
                        @confirm="onDateConfirm"
                        @cancel="showDatePicker = false" />
  </view>
</template>
<script setup>
  import { ref, onMounted } from "vue";
  import { onLoad } from "@dcloudio/uni-app";
  import PageHeader from "@/components/PageHeader.vue";
  import useUserStore from "@/store/modules/user";
  import { getRepairById, acceptRepair } from "@/api/equipmentManagement/repair";
  import dayjs from "dayjs";
  defineOptions({ name: "设备报修验收" });
  const userStore = useUserStore();
  const repairId = ref("");
  const detail = ref(null);
  const loading = ref(false);
  const showDatePicker = ref(false);
  const pickerDateValue = ref(Date.now());
  const form = ref({
    id: "",
    acceptanceName: "",
    acceptanceTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
    acceptanceRemark: "",
  });
  const showToast = message => {
    uni.showToast({ title: message, icon: "none" });
  };
  const getCurrentUserName = () =>
    (userStore.nickName || userStore.name || "").trim();
  const canAccept = acceptanceName => {
    const current = getCurrentUserName();
    const target = (acceptanceName || "").trim();
    return current && target && current === target;
  };
  const loadDetail = async () => {
    if (!repairId.value) {
      showToast("参数错误");
      return;
    }
    try {
      uni.showLoading({ title: "加载中...", mask: true });
      const { code, data } = await getRepairById(repairId.value);
      if (code !== 200) {
        showToast("获取详情失败");
        return;
      }
      detail.value = data;
      if (Number(data.status) !== 3) {
        showToast("当前状态不可验收");
        setTimeout(() => goBack(), 1500);
        return;
      }
      if (!canAccept(data.acceptanceName)) {
        showToast("仅指定验收人可进行验收");
        setTimeout(() => goBack(), 1500);
        return;
      }
      form.value.id = data.id;
      form.value.acceptanceName = data.acceptanceName || "";
      form.value.acceptanceTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
    } catch (e) {
      showToast("获取详情失败");
    } finally {
      uni.hideLoading();
    }
  };
  const onDateConfirm = e => {
    form.value.acceptanceTime = dayjs(e.value).format("YYYY-MM-DD HH:mm:ss");
    pickerDateValue.value = e.value;
    showDatePicker.value = false;
  };
  const submitAcceptance = async () => {
    if (!form.value.acceptanceTime?.trim()) {
      showToast("请选择验收时间");
      return;
    }
    if (!form.value.acceptanceRemark?.trim()) {
      showToast("请输入验收备注");
      return;
    }
    try {
      loading.value = true;
      const { code, msg } = await acceptRepair({
        id: form.value.id,
        acceptanceTime: form.value.acceptanceTime,
        acceptanceRemark: form.value.acceptanceRemark.trim(),
      });
      if (code === 200) {
        showToast("验收成功");
        setTimeout(() => goBack(), 1500);
      } else {
        showToast(msg || "验收失败");
        loading.value = false;
      }
    } catch (e) {
      loading.value = false;
      showToast("验收失败");
    }
  };
  const goBack = () => {
    uni.removeStorageSync("repairId");
    uni.navigateBack();
  };
  onLoad(options => {
    repairId.value = options?.id || uni.getStorageSync("repairId") || "";
    form.value.id = repairId.value;
  });
  onMounted(() => {
    loadDetail();
  });
</script>
<style scoped lang="scss">
  @import "@/static/scss/form-common.scss";
  .repair-acceptance {
    min-height: 100vh;
    background: #f8f9fa;
    padding-bottom: 5rem;
  }
  .section {
    margin: 12px 16px;
    background: #fff;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
  }
  .section-title {
    padding: 14px 16px;
    font-size: 16px;
    font-weight: 600;
    color: #303133;
    border-bottom: 1px solid #f0f0f0;
  }
  .info-item {
    display: flex;
    padding: 12px 16px;
    border-bottom: 1px solid #f8f8f8;
  }
  .info-label {
    width: 90px;
    font-size: 14px;
    color: #606266;
  }
  .info-value {
    flex: 1;
    font-size: 14px;
    color: #303133;
    text-align: right;
  }
  .footer-btns {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    background: #fff;
    display: flex;
    justify-content: space-around;
    align-items: center;
    padding: 0.75rem 0;
    box-shadow: 0 -0.125rem 0.5rem rgba(0, 0, 0, 0.05);
    z-index: 1000;
  }
  .cancel-btn {
    font-size: 1rem;
    color: #ffffff;
    width: 6.375rem;
    background: #c7c9cc;
    border-radius: 2.5rem;
  }
  .save-btn {
    font-size: 1rem;
    color: #ffffff;
    width: 14rem;
    background: linear-gradient(140deg, #00baff 0%, #006cfb 100%);
    border-radius: 2.5rem;
  }
</style>
src/pages/equipmentManagement/repair/add.vue
@@ -62,11 +62,19 @@
          </template>
        </u-form-item>
        <u-form-item label="报修人"
                     prop="repairName"
                     prop="maintenanceName"
                     required
                     border-bottom>
          <u-input v-model="form.repairName"
          <u-input v-model="form.maintenanceName"
                   placeholder="请输入报修人"
                   clearable />
        </u-form-item>
        <u-form-item label="验收人"
                     prop="acceptanceName"
                     required
                     border-bottom>
          <u-input v-model="form.acceptanceName"
                   placeholder="请输入验收人"
                   clearable />
        </u-form-item>
        <u-form-item label="故障现象"
@@ -150,6 +158,9 @@
  } from "@/api/equipmentManagement/repair";
  import dayjs from "dayjs";
  import { formatDateToYMD } from "@/utils/ruoyi";
  import useUserStore from "@/store/modules/user";
  const userStore = useUserStore();
  const showToast = message => {
    uni.showToast({
      title: message,
@@ -195,7 +206,12 @@
    repairTime: [
      { required: true, trigger: "change", message: "请选择报修日期" },
    ],
    repairName: [{ required: true, trigger: "blur", message: "请输入报修人" }],
    maintenanceName: [
      { required: true, trigger: "blur", message: "请输入报修人" },
    ],
    acceptanceName: [
      { required: true, trigger: "blur", message: "请输入验收人" },
    ],
    remark: [{ required: true, trigger: "blur", message: "请输入故障现象" }],
  };
@@ -204,7 +220,9 @@
    deviceLedgerId: undefined, // è®¾å¤‡ID
    deviceModel: undefined, // è§„格型号
    repairTime: dayjs().format("YYYY-MM-DD"), // æŠ¥ä¿®æ—¥æœŸ
    repairName: undefined, // æŠ¥ä¿®äºº
    status: "0",
    maintenanceName: userStore.nickName || userStore.name || "", // æŠ¥ä¿®äºº
    acceptanceName: undefined, // éªŒæ”¶äºº
    remark: undefined, // æ•…障现象
  });
@@ -413,10 +431,11 @@
  // æŠ¥ä¿®çŠ¶æ€é€‰é¡¹
  const repairStatusOptions = ref([
    { name: "待维修", value: "0" },
    { name: "完结", value: "1" },
    { name: "失败", value: "2" },
    { name: "待验收", value: "3" },
    { name: "完成", value: "1" },
    { name: "维修失败", value: "2" },
  ]);
  const repairStatusText = ref("");
  const repairStatusText = ref("待维修");
  // æ‰“开报修状态选择器
  const openRepairStatusPicker = () => {
@@ -458,12 +477,19 @@
          form.value.deviceLedgerId = data.deviceLedgerId;
          form.value.deviceModel = data.deviceModel;
          form.value.repairTime = dayjs(data.repairTime).format("YYYY-MM-DD");
          form.value.repairName = data.repairName;
          form.value.maintenanceName =
            data.maintenanceName || data.repairName || "";
          form.value.acceptanceName = data.acceptanceName || "";
          form.value.remark = data.remark;
          form.value.status =
            data.status !== undefined && data.status !== null
              ? String(data.status)
              : "0";
          await fetchRepairFileList(id);
          repairStatusText.value =
            repairStatusOptions.value.find(item => item.value == data.status)
              ?.name || "";
            repairStatusOptions.value.find(
              item => item.value == String(data.status)
            )?.name || "待维修";
          // è®¾ç½®è®¾å¤‡åç§°æ˜¾ç¤º
          const device = deviceOptions.value.find(
            item => item.id === data.deviceLedgerId
@@ -476,8 +502,13 @@
        showToast("获取详情失败");
      }
    } else {
      // æ–°å¢žæ¨¡å¼
      operationType.value = "add";
      form.value.status = "0";
      repairStatusText.value = "待维修";
      if (!form.value.maintenanceName) {
        form.value.maintenanceName =
          userStore.nickName || userStore.name || "";
      }
    }
  };
@@ -604,9 +635,18 @@
      } else if (!form.value.repairTime || form.value.repairTime.trim() === "") {
        isValid = false;
        errorMessage = "请选择报修日期";
      } else if (!form.value.repairName || form.value.repairName.trim() === "") {
      } else if (
        !form.value.maintenanceName ||
        form.value.maintenanceName.trim() === ""
      ) {
        isValid = false;
        errorMessage = "请输入报修人";
      } else if (
        !form.value.acceptanceName ||
        form.value.acceptanceName.trim() === ""
      ) {
        isValid = false;
        errorMessage = "请输入验收人";
      } else if (!form.value.remark || form.value.remark.trim() === "") {
        isValid = false;
        errorMessage = "请输入故障现象";
@@ -621,7 +661,11 @@
      const id = getPageId();
      // å‡†å¤‡æäº¤æ•°æ®
      const submitData = { ...form.value };
      const submitData = {
        ...form.value,
        status: Number(form.value.status ?? 0),
        repairName: form.value.maintenanceName,
      };
      const result = id
        ? await editRepair({ id: id, ...submitData })
src/pages/equipmentManagement/repair/detail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,362 @@
<template>
  <view class="repair-detail">
    <PageHeader title="设备报修详情"
                @back="goBack" />
    <view v-if="detail"
          class="detail-content">
      <!-- 1. æŠ¥ä¿®ç™»è®° -->
      <view class="section">
        <view class="section-title">
          <text class="section-num">1</text>
          <text>报修登记</text>
        </view>
        <view class="info-grid">
          <view class="info-item">
            <text class="info-label">设备名称</text>
            <text class="info-value">{{ detail.deviceName || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">规格型号</text>
            <text class="info-value">{{ detail.deviceModel || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">报修日期</text>
            <text class="info-value">{{ formatDate(detail.repairTime) || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">报修人</text>
            <text class="info-value">{{ detail.repairName || detail.maintenanceName || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">验收人</text>
            <text class="info-value">{{ detail.acceptanceName || '-' }}</text>
          </view>
          <view class="info-item"
                v-if="Number(detail.status) !== 0">
            <text class="info-label">维修人</text>
            <text class="info-value">{{ detail.maintenancePerson || detail.maintenanceName || '-' }}</text>
          </view>
          <view class="info-item full">
            <text class="info-label">故障现象</text>
            <text class="info-value">{{ detail.remark || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">当前状态</text>
            <view class="info-value">
              <u-tag :type="getStatusTagType(detail.status)"
                     size="small">{{ getStatusText(detail.status) }}</u-tag>
            </view>
          </view>
        </view>
        <view class="image-section">
          <text class="image-title">设备问题图片</text>
          <view v-if="repairImageList.length"
                class="image-list">
            <image v-for="(file, index) in repairImageList"
                   :key="file.id || index"
                   :src="getFileAccessUrl(file)"
                   mode="aspectFill"
                   class="repair-image"
                   @click="previewImage(index)" />
          </view>
          <view v-else
                class="no-image">
            <up-icon name="photo"
                     size="40"
                     color="#c0c4cc" />
            <text>暂无设备问题图片</text>
          </view>
        </view>
      </view>
      <!-- 2. ç»´ä¿®å¤„理 -->
      <view class="section"
            v-if="showMaintenanceSection">
        <view class="section-title">
          <text class="section-num">2</text>
          <text>维修处理</text>
        </view>
        <view class="info-grid">
          <view class="info-item">
            <text class="info-label">维修人</text>
            <text class="info-value">{{ detail.maintenancePerson || detail.maintenanceName || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">维修时间</text>
            <text class="info-value">{{ formatDateTime(detail.maintenanceTime) || '-' }}</text>
          </view>
          <view class="info-item full">
            <text class="info-label">维修结果</text>
            <text class="info-value">{{ detail.maintenanceResult || '-' }}</text>
          </view>
        </view>
      </view>
      <!-- 3. éªŒæ”¶ -->
      <view class="section"
            v-if="showAcceptanceSection">
        <view class="section-title">
          <text class="section-num">3</text>
          <text>验收</text>
        </view>
        <view class="info-grid">
          <view class="info-item">
            <text class="info-label">验收人</text>
            <text class="info-value">{{ detail.acceptanceName || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">验收时间</text>
            <text class="info-value">{{ formatDateTime(detail.acceptanceTime) || '-' }}</text>
          </view>
          <view class="info-item full">
            <text class="info-label">验收备注</text>
            <text class="info-value">{{ detail.acceptanceRemark || '-' }}</text>
          </view>
        </view>
      </view>
    </view>
    <view v-else
          class="loading-wrap">
      <text>加载中...</text>
    </view>
  </view>
</template>
<script setup>
  import { ref, computed, onMounted } from "vue";
  import { onLoad } from "@dcloudio/uni-app";
  import PageHeader from "@/components/PageHeader.vue";
  import config from "@/config";
  import {
    getRepairById,
    getRepairFileList,
  } from "@/api/equipmentManagement/repair";
  import dayjs from "dayjs";
  defineOptions({ name: "设备报修详情" });
  const repairId = ref("");
  const detail = ref(null);
  const repairImageList = ref([]);
  const STATUS_MAP = {
    0: "待维修",
    3: "待验收",
    1: "完成",
    2: "维修失败",
  };
  const getStatusText = status => STATUS_MAP[Number(status)] || "-";
  const getStatusTagType = status => {
    const map = { 0: "error", 3: "warning", 1: "success", 2: "error" };
    return map[Number(status)] || "info";
  };
  const showMaintenanceSection = computed(() => Number(detail.value?.status) !== 0);
  const showAcceptanceSection = computed(() => Number(detail.value?.status) === 1);
  const formatDate = dateStr => {
    if (!dateStr) return "";
    return dayjs(dateStr).format("YYYY-MM-DD");
  };
  const formatDateTime = dateStr => {
    if (!dateStr) return "";
    return dayjs(dateStr).format("YYYY-MM-DD HH:mm:ss");
  };
  const normalizeFileUrl = (rawUrl = "") => {
    let fileUrl = rawUrl || "";
    const javaApi = config.baseUrl;
    const localPrefixes = ["wxfile://", "file://", "content://", "blob:", "data:"];
    if (localPrefixes.some(prefix => fileUrl.startsWith(prefix))) {
      return fileUrl;
    }
    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 && !fileUrl.startsWith("http")) {
      if (!fileUrl.startsWith("/")) fileUrl = "/" + fileUrl;
      fileUrl = javaApi + fileUrl;
    }
    return fileUrl;
  };
  const getFileAccessUrl = (file = {}) => {
    if (file?.link) {
      if (String(file.link).startsWith("http")) return file.link;
      return normalizeFileUrl(file.link);
    }
    return normalizeFileUrl(file?.url || "");
  };
  const previewImage = index => {
    const urls = repairImageList.value
      .map(item => getFileAccessUrl(item))
      .filter(Boolean);
    if (!urls.length) return;
    uni.previewImage({ urls, current: urls[index] || urls[0] });
  };
  const loadDetail = async () => {
    if (!repairId.value) return;
    try {
      uni.showLoading({ title: "加载中...", mask: true });
      const { code, data } = await getRepairById(repairId.value);
      if (code === 200) {
        detail.value = data;
      } else {
        uni.showToast({ title: "获取详情失败", icon: "none" });
      }
      const fileRes = await getRepairFileList(repairId.value);
      if (fileRes?.code === 200) {
        repairImageList.value = Array.isArray(fileRes.data) ? fileRes.data : [];
      }
    } catch (e) {
      uni.showToast({ title: "获取详情失败", icon: "none" });
    } finally {
      uni.hideLoading();
    }
  };
  const goBack = () => {
    uni.navigateBack();
  };
  onLoad(options => {
    repairId.value = options?.id || uni.getStorageSync("repairId") || "";
  });
  onMounted(() => {
    loadDetail();
  });
</script>
<style scoped lang="scss">
  .repair-detail {
    min-height: 100vh;
    background: #f5f6f8;
    padding-bottom: 24px;
  }
  .detail-content {
    padding: 12px 16px;
  }
  .section {
    background: #fff;
    border-radius: 12px;
    margin-bottom: 16px;
    overflow: hidden;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
  }
  .section-title {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 14px 16px;
    font-size: 16px;
    font-weight: 600;
    color: #303133;
    border-bottom: 1px solid #f0f0f0;
  }
  .section-num {
    width: 22px;
    height: 22px;
    line-height: 22px;
    text-align: center;
    border-radius: 50%;
    background: #2c7be5;
    color: #fff;
    font-size: 12px;
    font-weight: 600;
  }
  .info-grid {
    padding: 8px 16px 12px;
    display: flex;
    flex-wrap: wrap;
  }
  .info-item {
    width: 50%;
    padding: 10px 8px 10px 0;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    gap: 4px;
    &.full {
      width: 100%;
    }
  }
  .info-label {
    font-size: 13px;
    color: #909399;
  }
  .info-value {
    font-size: 14px;
    color: #303133;
    word-break: break-all;
  }
  .image-section {
    padding: 0 16px 16px;
    border-top: 1px solid #f5f5f5;
    margin-top: 4px;
    padding-top: 12px;
  }
  .image-title {
    font-size: 13px;
    color: #909399;
    display: block;
    margin-bottom: 10px;
  }
  .image-list {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
  }
  .repair-image {
    width: 80px;
    height: 80px;
    border-radius: 6px;
    background: #f5f5f5;
  }
  .no-image {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 24px;
    color: #c0c4cc;
    font-size: 13px;
    gap: 8px;
    background: #fafafa;
    border-radius: 8px;
  }
  .loading-wrap {
    padding: 40px;
    text-align: center;
    color: #909399;
  }
</style>
src/pages/equipmentManagement/repair/index.vue
@@ -37,10 +37,8 @@
              <text class="item-id">设备名称:{{ item.deviceName }}</text>
            </view>
            <view class="status-tag">
              <u-tag v-if="item.status === 1"
                     type="success">完结</u-tag>
              <u-tag v-if="item.status === 0"
                     type="error">待维修</u-tag>
              <u-tag :type="getStatusTagType(item.status)"
                     size="small">{{ getStatusText(item.status) }}</u-tag>
            </view>
          </view>
          <up-divider></up-divider>
@@ -55,15 +53,20 @@
            </view>
            <view class="detail-row">
              <text class="detail-label">报修人</text>
              <text class="detail-value">{{ item.repairName || '-' }}</text>
              <text class="detail-value">{{ item.maintenanceName || item.repairName || '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">验收人</text>
              <text class="detail-value">{{ item.acceptanceName || '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">故障现象</text>
              <text class="detail-value">{{ item.remark || '-' }}</text>
            </view>
            <view class="detail-row">
            <view class="detail-row"
                  v-if="Number(item.status) !== 0">
              <text class="detail-label">维修人</text>
              <text class="detail-value">{{ item.maintenanceName || '-' }}</text>
              <text class="detail-value">{{ item.maintenancePerson || item.maintenanceName || '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">维修结果</text>
@@ -76,19 +79,33 @@
          </view>
          <!-- æŒ‰é’®åŒºåŸŸ -->
          <view class="action-buttons">
            <u-button type="info"
                      size="small"
                      plain
                      class="action-btn"
                      @click="goDetail(item.id)">
              è¯¦æƒ…
            </u-button>
            <u-button type="primary"
                      size="small"
                      class="action-btn"
                      :disabled="item.status === 1"
                      :disabled="Number(item.status) !== 0"
                      @click="edit(item.id)">
              ç¼–辑
            </u-button>
            <u-button type="warning"
                      size="small"
                      class="action-btn"
                      :disabled="item.status === 1"
                      :disabled="Number(item.status) !== 0"
                      @click="addMaintain(item.id)">
              æ–°å¢žç»´ä¿®
            </u-button>
            <u-button type="success"
                      size="small"
                      class="action-btn"
                      :disabled="!canAccept(item)"
                      @click="goAcceptance(item.id)">
              éªŒæ”¶
            </u-button>
            <u-button type="error"
                      size="small"
@@ -130,6 +147,30 @@
  };
  const userStore = useUserStore();
  const STATUS_MAP = {
    0: "待维修",
    3: "待验收",
    1: "完成",
    2: "维修失败",
  };
  const getStatusText = status => STATUS_MAP[Number(status)] || "-";
  const getStatusTagType = status => {
    const map = { 0: "error", 3: "warning", 1: "success", 2: "error" };
    return map[Number(status)] || "info";
  };
  const getCurrentUserName = () =>
    (userStore.nickName || userStore.name || "").trim();
  const canAccept = item => {
    if (Number(item?.status) !== 3) return false;
    const current = getCurrentUserName();
    const target = (item?.acceptanceName || "").trim();
    return Boolean(current && target && current === target);
  };
  // æœç´¢å…³é”®è¯
  const searchKeyword = ref("");
@@ -205,6 +246,24 @@
    });
  };
  const goDetail = id => {
    if (!id) return;
    uni.navigateTo({
      url: `/pages/equipmentManagement/repair/detail?id=${id}`,
    });
  };
  const goAcceptance = id => {
    if (!id) {
      showToast("参数错误");
      return;
    }
    uni.setStorageSync("repairId", id);
    uni.navigateTo({
      url: `/pages/equipmentManagement/repair/acceptance?id=${id}`,
    });
  };
  // ç¼–辑 - è·³è½¬åˆ°add页面,通过id区分新增还是编辑
  const edit = id => {
    if (!id) return;
@@ -262,6 +321,7 @@
  }
  .action-buttons {
    gap: 8px; // ä¸Žå…¬å…±æ ·å¼ä¸­çš„ 12px ä¸åŒ
    flex-wrap: wrap;
    gap: 8px;
  }
</style>
src/pages/equipmentManagement/repair/maintain.vue
@@ -12,10 +12,10 @@
      <u-cell-group title="维修信息"
                    inset>
        <u-form-item prop="maintenanceName"
                     label="报修人"
                     label="维修人"
                     required>
          <u-input v-model="form.maintenanceName"
                   placeholder="请输入报修人"
                   placeholder="请输入维修人"
                   clearable />
        </u-form-item>
        <u-form-item prop="maintenanceResult"
@@ -177,7 +177,7 @@
  // è¡¨å•验证规则
  const formRules = {
    maintenanceName: [
      { required: true, trigger: "blur", message: "请输入报修人" },
      { required: true, trigger: "blur", message: "请输入维修人" },
    ],
    maintenanceResult: [
      { required: true, trigger: "blur", message: "请输入维修结果" },
@@ -187,11 +187,11 @@
    ],
  };
  const repairStatusOptions = ref([
    { name: "待维修", value: "0" },
    { name: "完结", value: "1" },
    { name: "失败", value: "2" },
    { name: "待验收", value: "3" },
    { name: "完成", value: "1" },
    { name: "维修失败", value: "2" },
  ]);
  const repairStatusText = ref("完结");
  const repairStatusText = ref("待验收");
  // æ‰“开报修状态选择器
  const openRepairStatusPicker = () => {
    uni.showActionSheet({
@@ -204,10 +204,10 @@
  };
  // ä½¿ç”¨ ref å£°æ˜Žè¡¨å•数据
  const form = ref({
    maintenanceName: userStore.nickName || "", // é»˜è®¤ä½¿ç”¨å½“前用户昵称
    maintenanceResult: undefined, // ç»´ä¿®ç»“æžœ
    maintenanceTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // ç»´ä¿®æ—¥æœŸï¼ˆåªæ˜¾ç¤ºæ—¥æœŸï¼‰
    status: "1",
    maintenanceName: userStore.nickName || userStore.name || "",
    maintenanceResult: undefined,
    maintenanceTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
    status: "3",
    sparePartsIds: [],
  });
@@ -222,10 +222,10 @@
  // é‡ç½®è¡¨å•数据和校验状态
  const resetForm = () => {
    form.value = {
      maintenanceName: userStore.nickName || "",
      maintenanceName: userStore.nickName || userStore.name || "",
      maintenanceResult: undefined,
      maintenanceTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
      status: "1",
      status: "3",
      sparePartsIds: [],
    };
    selectedSpareParts.value = [];
@@ -411,9 +411,9 @@
  // åˆå§‹åŒ–表单数据
  const initForm = async () => {
    form.value.status = "1";
    // è®¾ç½®æŠ¥ä¿®äººä¸ºå½“前用户昵称
    form.value.maintenanceName = userStore.nickName || "";
    form.value.status = "3";
    form.value.maintenanceName = userStore.nickName || userStore.name || "";
    repairStatusText.value = "待验收";
    // è®¾ç½®å½“前日期(只包含年月日)
    form.value.maintenanceTime = dayjs().format("YYYY-MM-DD HH:mm:ss");
src/pages/qualityManagement/finalInspection/add.vue
@@ -67,14 +67,32 @@
                  placeholder="请输入单位"
                  disabled />
      </up-form-item>
      <up-form-item label="数量"
      <up-form-item label="总数量"
                    prop="quantity"
                    required
                    border-bottom>
        <up-input v-model="form.quantity"
                  type="number"
                  placeholder="请输入数量"
                  placeholder="请输入总数量"
                  :disabled="processQuantityDisabled" />
      </up-form-item>
      <up-form-item label="合格数量"
                    prop="qualifiedQuantity"
                    required
                    border-bottom>
        <up-input v-model="form.qualifiedQuantity"
                  type="number"
                  placeholder="请输入合格数量"
                  clearable />
      </up-form-item>
      <up-form-item label="不合格数量"
                    prop="unqualifiedQuantity"
                    required
                    border-bottom>
        <up-input v-model="form.unqualifiedQuantity"
                  type="number"
                  placeholder="请输入不合格数量"
                  clearable />
      </up-form-item>
      <up-form-item label="检测单位"
                    prop="checkCompany"
@@ -82,19 +100,6 @@
        <up-input v-model="form.checkCompany"
                  placeholder="请输入检测单位"
                  clearable />
      </up-form-item>
      <up-form-item label="检测结果"
                    prop="checkResult"
                    required
                    border-bottom>
        <up-input v-model="form.checkResult"
                  placeholder="请选择检测结果"
                  readonly
                  @click="showResultSheet" />
        <template #right>
          <up-icon @click="showResultSheet = true"
                   name="arrow-right" />
        </template>
      </up-form-item>
      <up-form-item label="检验员"
                    prop="checkName"
@@ -207,12 +212,6 @@
                     @select="selectModel"
                     @close="showModelSheet = false"
                     title="选择规格型号" />
    <!-- æ£€æµ‹ç»“果选择 -->
    <up-action-sheet :show="showResultSheet"
                     :actions="resultSheetOptions"
                     @select="selectResult"
                     @close="showResultSheet = false"
                     title="选择检测结果" />
    <!-- æ£€éªŒå‘˜é€‰æ‹© -->
    <up-action-sheet :show="showInspectorSheet"
                     :actions="userSheetOptions"
@@ -339,8 +338,6 @@
  const showProductTree = ref(false);
  // è§„格型号选择
  const showModelSheet = ref(false);
  // æ£€æµ‹ç»“果选择
  const showResultSheet = ref(false);
  // æ£€éªŒå‘˜é€‰æ‹©
  const showInspectorSheet = ref(false);
  // æŒ‡æ ‡é€‰æ‹©
@@ -358,8 +355,9 @@
    testStandardId: "",
    unit: "",
    quantity: "",
    qualifiedQuantity: "",
    unqualifiedQuantity: "",
    checkCompany: "",
    checkResult: "",
    productMainId: null,
    purchaseLedgerId: null,
  });
@@ -379,11 +377,6 @@
  const modelOptions = ref([]);
  // æ£€éªŒå‘˜åˆ—表
  const userList = ref([]);
  // æ£€æµ‹ç»“果选项
  const resultOptions = ref([
    { label: "合格", value: "合格" },
    { label: "不合格", value: "不合格" },
  ]);
  // æŒ‡æ ‡é€‰é¡¹
  const testStandardOptions = ref([]);
  // å½“前产品ID
@@ -408,13 +401,6 @@
    return modelOptions.value.map(item => ({
      name: item.model,
      value: item.id,
    }));
  });
  const resultSheetOptions = computed(() => {
    return resultOptions.value.map(item => ({
      name: item.label,
      value: item.value,
    }));
  });
@@ -446,10 +432,13 @@
    ],
    unit: [{ required: false, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
    checkResult: [
      { required: true, message: "请选择检测结果", trigger: "change" },
    qualifiedQuantity: [
      { required: true, message: "请输入合格数量", trigger: "blur" },
    ],
    unqualifiedQuantity: [
      { required: true, message: "请输入不合格数量", trigger: "blur" },
    ],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
  };
  // æ˜¯å¦ä¸ºç¼–辑模式
@@ -554,12 +543,6 @@
      modelOptions.value.find(item => item.id == value)?.model || "";
    form.value.unit =
      modelOptions.value.find(item => item.id == value)?.unit || "";
  };
  // é€‰æ‹©æ£€æµ‹ç»“æžœ
  const selectResult = e => {
    form.value.checkResult = e.value;
    showResultSheet.value = false;
  };
  // é€‰æ‹©æ£€éªŒå‘˜
@@ -683,16 +666,41 @@
      //   showToast("请选择工序");
      //   return;
      // }
      if (!form.value.quantity) {
        showToast("请输入数量");
      if (!form.value.quantity && form.value.quantity !== 0) {
        showToast("请输入总数量");
        return;
      }
      if (
        form.value.qualifiedQuantity === "" ||
        form.value.qualifiedQuantity === undefined
      ) {
        showToast("请输入合格数量");
        return;
      }
      if (
        form.value.unqualifiedQuantity === "" ||
        form.value.unqualifiedQuantity === undefined
      ) {
        showToast("请输入不合格数量");
        return;
      }
      const qty = Number(form.value.quantity);
      const qf = Number(form.value.qualifiedQuantity);
      const uq = Number(form.value.unqualifiedQuantity);
      if (Number.isNaN(qty) || qty < 0) {
        showToast("总数量格式不正确");
        return;
      }
      if (Number.isNaN(qf) || qf < 0 || Number.isNaN(uq) || uq < 0) {
        showToast("合格/不合格数量格式不正确");
        return;
      }
      if (qf + uq !== qty) {
        showToast("合格数量与不合格数量之和须等于总数量");
        return;
      }
      if (!form.value.productId) {
        showToast("请选择产品");
        return;
      }
      if (!form.value.checkResult) {
        showToast("请选择检测结果");
        return;
      }
@@ -706,7 +714,9 @@
      }
      const data = { ...form.value, qualityInspectParams: tableData.value };
      data.quantity = Number(data.quantity);
      data.quantity = qty;
      data.qualifiedQuantity = qf;
      data.unqualifiedQuantity = uq;
      if (isEdit.value) {
        const res = await qualityInspectUpdate(data);
        showToast("保存成功");
@@ -745,8 +755,9 @@
        testStandardId: "",
        unit: "",
        quantity: "",
        qualifiedQuantity: "",
        unqualifiedQuantity: "",
        checkCompany: "",
        checkResult: "",
        productMainId: null,
        purchaseLedgerId: null,
      };
@@ -767,8 +778,9 @@
        testStandardId: "1",
        unit: "kg",
        quantity: 1000,
        qualifiedQuantity: 1000,
        unqualifiedQuantity: 0,
        checkCompany: "第三方检测机构",
        checkResult: "合格",
        productMainId: null,
        purchaseLedgerId: null,
      };
@@ -847,8 +859,9 @@
        testStandardId: "",
        unit: "",
        quantity: "",
        qualifiedQuantity: "",
        unqualifiedQuantity: "",
        checkCompany: "",
        checkResult: "",
        productMainId: null,
        purchaseLedgerId: null,
      };
src/pages/qualityManagement/finalInspection/detail.vue
@@ -15,10 +15,10 @@
          </view>
          <text class="header-title">{{ detailData.productName || '-' }}</text>
          <view class="status-tags">
            <u-tag :type="getTagType(detailData.checkResult)"
            <u-tag :type="getPassRateTagType(detailData.passRate)"
                   size="small"
                   class="status-tag">
              {{ detailData.checkResult || '-' }}
              åˆæ ¼çއ {{ formatPassRate(detailData.passRate) }}
            </u-tag>
            <u-tag :type="getStateTagType(detailData.inspectState)"
                   size="small"
@@ -58,8 +58,20 @@
            <text class="detail-value">{{ detailData.unit || '-' }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">数量</text>
            <text class="detail-value">{{ detailData.quantity || 0 }}</text>
            <text class="detail-label">总数量</text>
            <text class="detail-value">{{ detailData.quantity ?? '-' }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">合格数量</text>
            <text class="detail-value">{{ detailData.qualifiedQuantity ?? '-' }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">不合格数量</text>
            <text class="detail-value">{{ detailData.unqualifiedQuantity ?? '-' }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">合格率</text>
            <text class="detail-value">{{ formatPassRate(detailData.passRate) }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">检测单位</text>
@@ -160,16 +172,22 @@
    return dayjs(date).format("YYYY-MM-DD");
  };
  // èŽ·å–æ ‡ç­¾ç±»åž‹
  const getTagType = result => {
    switch (result) {
      case "合格":
        return "success";
      case "不合格":
        return "error";
      default:
        return "info";
    }
  const formatPassRate = val => {
    if (val === null || val === undefined || val === "") return "-";
    const n = Number(val);
    if (Number.isNaN(n)) return String(val);
    if (n > 0 && n <= 1) return `${(n * 100).toFixed(1)}%`;
    return `${Number.isInteger(n) ? n : Number(n.toFixed(1))}%`;
  };
  const getPassRateTagType = val => {
    if (val === null || val === undefined || val === "") return "info";
    let n = Number(val);
    if (Number.isNaN(n)) return "info";
    if (n > 0 && n <= 1) n *= 100;
    if (n >= 100) return "success";
    if (n >= 60) return "warning";
    return "error";
  };
  // èŽ·å–çŠ¶æ€æ ‡ç­¾ç±»åž‹
src/pages/qualityManagement/finalInspection/index.vue
@@ -75,11 +75,11 @@
              </view>
            </view>
            <view class="status-tags">
              <u-tag :type="getTagType(item.checkResult)"
                     v-if="item.checkResult!=null"
              <u-tag :type="getPassRateTagType(item.passRate)"
                     v-if="item.passRate != null && item.passRate !== ''"
                     size="mini"
                     class="status-tag">
                {{ item.checkResult }}
                {{ formatPassRate(item.passRate) }}
              </u-tag>
              <u-tag :type="getStateTagType(item.inspectState)"
                     size="mini"
@@ -107,8 +107,16 @@
              <text class="detail-value">{{ item.checkName || '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">数量</text>
              <text class="detail-label">总数量</text>
              <text class="detail-value">{{ item.quantity || 0 }} {{ item.unit || '' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">合格数量</text>
              <text class="detail-value">{{ item.qualifiedQuantity ?? '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">不合格数量</text>
              <text class="detail-value">{{ item.unqualifiedQuantity ?? '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">检测单位</text>
@@ -315,11 +323,31 @@
    return inspectState ? "checkmark-circle" : "time";
  };
  // èŽ·å–æ ‡ç­¾ç±»åž‹
  const getTagType = checkResult => {
    if (checkResult === "合格") return "success";
    if (checkResult === "不合格") return "error";
    return "default";
  const formatPassRate = val => {
    if (val === null || val === undefined || val === "") return "-";
    const n = Number(val);
    if (Number.isNaN(n)) return String(val);
    if (n > 0 && n <= 1) return `${(n * 100).toFixed(1)}%`;
    return `${Number.isInteger(n) ? n : Number(n.toFixed(1))}%`;
  };
  const getPassRateTagType = val => {
    if (val === null || val === undefined || val === "") return "default";
    let n = Number(val);
    if (Number.isNaN(n)) return "default";
    if (n > 0 && n <= 1) n *= 100;
    if (n >= 100) return "success";
    if (n >= 60) return "warning";
    return "error";
  };
  const isFullPassByPassRate = item => {
    const v = item.passRate;
    if (v === null || v === undefined || v === "") return false;
    const n = Number(v);
    if (Number.isNaN(n)) return false;
    if (n > 0 && n <= 1) return n >= 1;
    return n >= 100;
  };
  // èŽ·å–çŠ¶æ€æ ‡ç­¾ç±»åž‹
@@ -383,7 +411,7 @@
          item => !item.inspectState
        ).length;
        qualifiedCount.value = inspectionList.value.filter(
          item => item.checkResult === "合格"
          isFullPassByPassRate
        ).length;
      })
      .catch(err => {
src/pages/qualityManagement/materialInspection/add.vue
@@ -67,14 +67,32 @@
                  placeholder="请输入单位"
                  disabled />
      </up-form-item>
      <up-form-item label="数量"
      <up-form-item label="总数量"
                    prop="quantity"
                    required
                    border-bottom>
        <up-input v-model="form.quantity"
                  type="number"
                  placeholder="请输入数量"
                  placeholder="请输入总数量"
                  :disabled="supplierQuantityDisabled" />
      </up-form-item>
      <up-form-item label="合格数量"
                    prop="qualifiedQuantity"
                    required
                    border-bottom>
        <up-input v-model="form.qualifiedQuantity"
                  type="number"
                  placeholder="请输入合格数量"
                  clearable />
      </up-form-item>
      <up-form-item label="不合格数量"
                    prop="unqualifiedQuantity"
                    required
                    border-bottom>
        <up-input v-model="form.unqualifiedQuantity"
                  type="number"
                  placeholder="请输入不合格数量"
                  clearable />
      </up-form-item>
      <up-form-item label="检测单位"
                    prop="checkCompany"
@@ -82,19 +100,6 @@
        <up-input v-model="form.checkCompany"
                  placeholder="请输入检测单位"
                  clearable />
      </up-form-item>
      <up-form-item label="检测结果"
                    prop="checkResult"
                    required
                    border-bottom>
        <up-input v-model="form.checkResult"
                  placeholder="请选择检测结果"
                  readonly
                  @click="showResultSheet" />
        <template #right>
          <up-icon @click="showResultSheet = true"
                   name="arrow-right" />
        </template>
      </up-form-item>
      <up-form-item label="检验员"
                    prop="checkName"
@@ -214,12 +219,6 @@
                     @select="selectModel"
                     @close="showModelSheet = false"
                     title="选择规格型号" />
    <!-- æ£€æµ‹ç»“果选择 -->
    <up-action-sheet :show="showResultSheet"
                     :actions="resultSheetOptions"
                     @select="selectResult"
                     @close="showResultSheet = false"
                     title="选择检测结果" />
    <!-- æ£€éªŒå‘˜é€‰æ‹© -->
    <up-action-sheet :show="showInspectorSheet"
                     :actions="userSheetOptions"
@@ -345,8 +344,6 @@
  const showProductTree = ref(false);
  // è§„格型号选择
  const showModelSheet = ref(false);
  // æ£€æµ‹ç»“果选择
  const showResultSheet = ref(false);
  // æ£€éªŒå‘˜é€‰æ‹©
  const showInspectorSheet = ref(false);
  // æŒ‡æ ‡é€‰æ‹©
@@ -364,8 +361,9 @@
    testStandardId: "",
    unit: "",
    quantity: "",
    qualifiedQuantity: "",
    unqualifiedQuantity: "",
    checkCompany: "",
    checkResult: "",
    productMainId: null,
    purchaseLedgerId: null,
  });
@@ -385,11 +383,6 @@
  const modelOptions = ref([]);
  // æ£€éªŒå‘˜åˆ—表
  const userList = ref([]);
  // æ£€æµ‹ç»“果选项
  const resultOptions = ref([
    { label: "合格", value: "合格" },
    { label: "不合格", value: "不合格" },
  ]);
  // æŒ‡æ ‡é€‰é¡¹
  const testStandardOptions = ref([]);
  // å½“前产品ID
@@ -414,13 +407,6 @@
    return modelOptions.value.map(item => ({
      name: item.model,
      value: item.id,
    }));
  });
  const resultSheetOptions = computed(() => {
    return resultOptions.value.map(item => ({
      name: item.label,
      value: item.value,
    }));
  });
@@ -452,10 +438,13 @@
    ],
    unit: [{ required: false, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
    checkResult: [
      { required: true, message: "请选择检测结果", trigger: "change" },
    qualifiedQuantity: [
      { required: true, message: "请输入合格数量", trigger: "blur" },
    ],
    unqualifiedQuantity: [
      { required: true, message: "请输入不合格数量", trigger: "blur" },
    ],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
  };
  // æ˜¯å¦ä¸ºç¼–辑模式
@@ -560,12 +549,6 @@
      modelOptions.value.find(item => item.id == value)?.model || "";
    form.value.unit =
      modelOptions.value.find(item => item.id == value)?.unit || "";
  };
  // é€‰æ‹©æ£€æµ‹ç»“æžœ
  const selectResult = e => {
    form.value.checkResult = e.value;
    showResultSheet.value = false;
  };
  // é€‰æ‹©æ£€éªŒå‘˜
@@ -689,16 +672,41 @@
        showToast("请选择供应商");
        return;
      }
      if (!form.value.quantity) {
        showToast("请输入数量");
      if (!form.value.quantity && form.value.quantity !== 0) {
        showToast("请输入总数量");
        return;
      }
      if (
        form.value.qualifiedQuantity === "" ||
        form.value.qualifiedQuantity === undefined
      ) {
        showToast("请输入合格数量");
        return;
      }
      if (
        form.value.unqualifiedQuantity === "" ||
        form.value.unqualifiedQuantity === undefined
      ) {
        showToast("请输入不合格数量");
        return;
      }
      const qty = Number(form.value.quantity);
      const qf = Number(form.value.qualifiedQuantity);
      const uq = Number(form.value.unqualifiedQuantity);
      if (Number.isNaN(qty) || qty < 0) {
        showToast("总数量格式不正确");
        return;
      }
      if (Number.isNaN(qf) || qf < 0 || Number.isNaN(uq) || uq < 0) {
        showToast("合格/不合格数量格式不正确");
        return;
      }
      if (qf + uq !== qty) {
        showToast("合格数量与不合格数量之和须等于总数量");
        return;
      }
      if (!form.value.productId) {
        showToast("请选择产品");
        return;
      }
      if (!form.value.checkResult) {
        showToast("请选择检测结果");
        return;
      }
@@ -712,7 +720,9 @@
      }
      const data = { ...form.value, qualityInspectParams: tableData.value };
      data.quantity = Number(data.quantity);
      data.quantity = qty;
      data.qualifiedQuantity = qf;
      data.unqualifiedQuantity = uq;
      if (isEdit.value) {
        const res = await qualityInspectUpdate(data);
        showToast("保存成功");
@@ -751,8 +761,9 @@
        testStandardId: "",
        unit: "",
        quantity: "",
        qualifiedQuantity: "",
        unqualifiedQuantity: "",
        checkCompany: "",
        checkResult: "",
        productMainId: null,
        purchaseLedgerId: null,
      };
@@ -773,8 +784,9 @@
        testStandardId: "1",
        unit: "kg",
        quantity: 1000,
        qualifiedQuantity: 1000,
        unqualifiedQuantity: 0,
        checkCompany: "第三方检测机构",
        checkResult: "合格",
        productMainId: null,
        purchaseLedgerId: null,
      };
@@ -853,8 +865,9 @@
        testStandardId: "",
        unit: "",
        quantity: "",
        qualifiedQuantity: "",
        unqualifiedQuantity: "",
        checkCompany: "",
        checkResult: "",
        productMainId: null,
        purchaseLedgerId: null,
      };
src/pages/qualityManagement/materialInspection/detail.vue
@@ -15,10 +15,10 @@
          </view>
          <text class="header-title">{{ detailData.productName || '-' }}</text>
          <view class="status-tags">
            <u-tag :type="getTagType(detailData.checkResult)"
            <u-tag :type="getPassRateTagType(detailData.passRate)"
                   size="small"
                   class="status-tag">
              {{ detailData.checkResult || '-' }}
              åˆæ ¼çއ {{ formatPassRate(detailData.passRate) }}
            </u-tag>
            <u-tag :type="getStateTagType(detailData.inspectState)"
                   size="small"
@@ -58,8 +58,20 @@
            <text class="detail-value">{{ detailData.unit || '-' }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">数量</text>
            <text class="detail-value">{{ detailData.quantity || 0 }}</text>
            <text class="detail-label">总数量</text>
            <text class="detail-value">{{ detailData.quantity ?? '-' }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">合格数量</text>
            <text class="detail-value">{{ detailData.qualifiedQuantity ?? '-' }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">不合格数量</text>
            <text class="detail-value">{{ detailData.unqualifiedQuantity ?? '-' }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">合格率</text>
            <text class="detail-value">{{ formatPassRate(detailData.passRate) }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">检测单位</text>
@@ -160,16 +172,23 @@
    return dayjs(date).format("YYYY-MM-DD");
  };
  // èŽ·å–æ ‡ç­¾ç±»åž‹
  const getTagType = result => {
    switch (result) {
      case "合格":
        return "success";
      case "不合格":
        return "error";
      default:
        return "info";
    }
  // æ ¼å¼åŒ–合格率(支持 0–1 å°æ•°æˆ–百分数)
  const formatPassRate = val => {
    if (val === null || val === undefined || val === "") return "-";
    const n = Number(val);
    if (Number.isNaN(n)) return String(val);
    if (n > 0 && n <= 1) return `${(n * 100).toFixed(1)}%`;
    return `${Number.isInteger(n) ? n : Number(n.toFixed(1))}%`;
  };
  const getPassRateTagType = val => {
    if (val === null || val === undefined || val === "") return "info";
    let n = Number(val);
    if (Number.isNaN(n)) return "info";
    if (n > 0 && n <= 1) n *= 100;
    if (n >= 100) return "success";
    if (n >= 60) return "warning";
    return "error";
  };
  // èŽ·å–çŠ¶æ€æ ‡ç­¾ç±»åž‹
src/pages/qualityManagement/materialInspection/index.vue
@@ -75,11 +75,11 @@
              </view>
            </view>
            <view class="status-tags">
              <u-tag :type="getTagType(item.checkResult)"
                     v-if="item.checkResult!=null"
              <u-tag :type="getPassRateTagType(item.passRate)"
                     v-if="item.passRate != null && item.passRate !== ''"
                     size="mini"
                     class="status-tag">
                {{ item.checkResult }}
                {{ formatPassRate(item.passRate) }}
              </u-tag>
              <u-tag :type="getStateTagType(item.inspectState)"
                     size="mini"
@@ -107,8 +107,16 @@
              <text class="detail-value">{{ item.checkName || '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">数量</text>
              <text class="detail-label">总数量</text>
              <text class="detail-value">{{ item.quantity || 0 }} {{ item.unit || '' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">合格数量</text>
              <text class="detail-value">{{ item.qualifiedQuantity ?? '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">不合格数量</text>
              <text class="detail-value">{{ item.unqualifiedQuantity ?? '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">检测单位</text>
@@ -315,11 +323,31 @@
    return inspectState ? "checkmark-circle" : "time";
  };
  // èŽ·å–æ ‡ç­¾ç±»åž‹
  const getTagType = checkResult => {
    if (checkResult === "合格") return "success";
    if (checkResult === "不合格") return "error";
    return "default";
  const formatPassRate = val => {
    if (val === null || val === undefined || val === "") return "-";
    const n = Number(val);
    if (Number.isNaN(n)) return String(val);
    if (n > 0 && n <= 1) return `${(n * 100).toFixed(1)}%`;
    return `${Number.isInteger(n) ? n : Number(n.toFixed(1))}%`;
  };
  const getPassRateTagType = val => {
    if (val === null || val === undefined || val === "") return "default";
    let n = Number(val);
    if (Number.isNaN(n)) return "default";
    if (n > 0 && n <= 1) n *= 100;
    if (n >= 100) return "success";
    if (n >= 60) return "warning";
    return "error";
  };
  const isFullPassByPassRate = item => {
    const v = item.passRate;
    if (v === null || v === undefined || v === "") return false;
    const n = Number(v);
    if (Number.isNaN(n)) return false;
    if (n > 0 && n <= 1) return n >= 1;
    return n >= 100;
  };
  // èŽ·å–çŠ¶æ€æ ‡ç­¾ç±»åž‹
@@ -383,7 +411,7 @@
          item => !item.inspectState
        ).length;
        qualifiedCount.value = inspectionList.value.filter(
          item => item.checkResult === "合格"
          isFullPassByPassRate
        ).length;
      })
      .catch(err => {
src/pages/qualityManagement/processInspection/add.vue
@@ -67,14 +67,32 @@
                  placeholder="请输入单位"
                  disabled />
      </up-form-item>
      <up-form-item label="数量"
      <up-form-item label="总数量"
                    prop="quantity"
                    required
                    border-bottom>
        <up-input v-model="form.quantity"
                  type="number"
                  placeholder="请输入数量"
                  placeholder="请输入总数量"
                  :disabled="processQuantityDisabled" />
      </up-form-item>
      <up-form-item label="合格数量"
                    prop="qualifiedQuantity"
                    required
                    border-bottom>
        <up-input v-model="form.qualifiedQuantity"
                  type="number"
                  placeholder="请输入合格数量"
                  clearable />
      </up-form-item>
      <up-form-item label="不合格数量"
                    prop="unqualifiedQuantity"
                    required
                    border-bottom>
        <up-input v-model="form.unqualifiedQuantity"
                  type="number"
                  placeholder="请输入不合格数量"
                  clearable />
      </up-form-item>
      <up-form-item label="检测单位"
                    prop="checkCompany"
@@ -82,19 +100,6 @@
        <up-input v-model="form.checkCompany"
                  placeholder="请输入检测单位"
                  clearable />
      </up-form-item>
      <up-form-item label="检测结果"
                    prop="checkResult"
                    required
                    border-bottom>
        <up-input v-model="form.checkResult"
                  placeholder="请选择检测结果"
                  readonly
                  @click="showResultSheet" />
        <template #right>
          <up-icon @click="showResultSheet = true"
                   name="arrow-right" />
        </template>
      </up-form-item>
      <up-form-item label="检验员"
                    prop="checkName"
@@ -207,12 +212,6 @@
                     @select="selectModel"
                     @close="showModelSheet = false"
                     title="选择规格型号" />
    <!-- æ£€æµ‹ç»“果选择 -->
    <up-action-sheet :show="showResultSheet"
                     :actions="resultSheetOptions"
                     @select="selectResult"
                     @close="showResultSheet = false"
                     title="选择检测结果" />
    <!-- æ£€éªŒå‘˜é€‰æ‹© -->
    <up-action-sheet :show="showInspectorSheet"
                     :actions="userSheetOptions"
@@ -339,8 +338,6 @@
  const showProductTree = ref(false);
  // è§„格型号选择
  const showModelSheet = ref(false);
  // æ£€æµ‹ç»“果选择
  const showResultSheet = ref(false);
  // æ£€éªŒå‘˜é€‰æ‹©
  const showInspectorSheet = ref(false);
  // æŒ‡æ ‡é€‰æ‹©
@@ -358,8 +355,9 @@
    testStandardId: "",
    unit: "",
    quantity: "",
    qualifiedQuantity: "",
    unqualifiedQuantity: "",
    checkCompany: "",
    checkResult: "",
    productMainId: null,
    purchaseLedgerId: null,
  });
@@ -379,11 +377,6 @@
  const modelOptions = ref([]);
  // æ£€éªŒå‘˜åˆ—表
  const userList = ref([]);
  // æ£€æµ‹ç»“果选项
  const resultOptions = ref([
    { label: "合格", value: "合格" },
    { label: "不合格", value: "不合格" },
  ]);
  // æŒ‡æ ‡é€‰é¡¹
  const testStandardOptions = ref([]);
  // å½“前产品ID
@@ -408,13 +401,6 @@
    return modelOptions.value.map(item => ({
      name: item.model,
      value: item.id,
    }));
  });
  const resultSheetOptions = computed(() => {
    return resultOptions.value.map(item => ({
      name: item.label,
      value: item.value,
    }));
  });
@@ -446,10 +432,13 @@
    ],
    unit: [{ required: false, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
    checkResult: [
      { required: true, message: "请选择检测结果", trigger: "change" },
    qualifiedQuantity: [
      { required: true, message: "请输入合格数量", trigger: "blur" },
    ],
    unqualifiedQuantity: [
      { required: true, message: "请输入不合格数量", trigger: "blur" },
    ],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
  };
  // æ˜¯å¦ä¸ºç¼–辑模式
@@ -554,12 +543,6 @@
      modelOptions.value.find(item => item.id == value)?.model || "";
    form.value.unit =
      modelOptions.value.find(item => item.id == value)?.unit || "";
  };
  // é€‰æ‹©æ£€æµ‹ç»“æžœ
  const selectResult = e => {
    form.value.checkResult = e.value;
    showResultSheet.value = false;
  };
  // é€‰æ‹©æ£€éªŒå‘˜
@@ -683,16 +666,41 @@
        showToast("请选择工序");
        return;
      }
      if (!form.value.quantity) {
        showToast("请输入数量");
      if (!form.value.quantity && form.value.quantity !== 0) {
        showToast("请输入总数量");
        return;
      }
      if (
        form.value.qualifiedQuantity === "" ||
        form.value.qualifiedQuantity === undefined
      ) {
        showToast("请输入合格数量");
        return;
      }
      if (
        form.value.unqualifiedQuantity === "" ||
        form.value.unqualifiedQuantity === undefined
      ) {
        showToast("请输入不合格数量");
        return;
      }
      const qty = Number(form.value.quantity);
      const qf = Number(form.value.qualifiedQuantity);
      const uq = Number(form.value.unqualifiedQuantity);
      if (Number.isNaN(qty) || qty < 0) {
        showToast("总数量格式不正确");
        return;
      }
      if (Number.isNaN(qf) || qf < 0 || Number.isNaN(uq) || uq < 0) {
        showToast("合格/不合格数量格式不正确");
        return;
      }
      if (qf + uq !== qty) {
        showToast("合格数量与不合格数量之和须等于总数量");
        return;
      }
      if (!form.value.productId) {
        showToast("请选择产品");
        return;
      }
      if (!form.value.checkResult) {
        showToast("请选择检测结果");
        return;
      }
@@ -706,7 +714,9 @@
      }
      const data = { ...form.value, qualityInspectParams: tableData.value };
      data.quantity = Number(data.quantity);
      data.quantity = qty;
      data.qualifiedQuantity = qf;
      data.unqualifiedQuantity = uq;
      if (isEdit.value) {
        const res = await qualityInspectUpdate(data);
        showToast("保存成功");
@@ -745,8 +755,9 @@
        testStandardId: "",
        unit: "",
        quantity: "",
        qualifiedQuantity: "",
        unqualifiedQuantity: "",
        checkCompany: "",
        checkResult: "",
        productMainId: null,
        purchaseLedgerId: null,
      };
@@ -767,8 +778,9 @@
        testStandardId: "1",
        unit: "kg",
        quantity: 1000,
        qualifiedQuantity: 1000,
        unqualifiedQuantity: 0,
        checkCompany: "第三方检测机构",
        checkResult: "合格",
        productMainId: null,
        purchaseLedgerId: null,
      };
@@ -847,8 +859,9 @@
        testStandardId: "",
        unit: "",
        quantity: "",
        qualifiedQuantity: "",
        unqualifiedQuantity: "",
        checkCompany: "",
        checkResult: "",
        productMainId: null,
        purchaseLedgerId: null,
      };
src/pages/qualityManagement/processInspection/detail.vue
@@ -15,10 +15,10 @@
          </view>
          <text class="header-title">{{ detailData.productName || '-' }}</text>
          <view class="status-tags">
            <u-tag :type="getTagType(detailData.checkResult)"
            <u-tag :type="getPassRateTagType(detailData.passRate)"
                   size="small"
                   class="status-tag">
              {{ detailData.checkResult || '-' }}
              åˆæ ¼çއ {{ formatPassRate(detailData.passRate) }}
            </u-tag>
            <u-tag :type="getStateTagType(detailData.inspectState)"
                   size="small"
@@ -58,8 +58,20 @@
            <text class="detail-value">{{ detailData.unit || '-' }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">数量</text>
            <text class="detail-value">{{ detailData.quantity || 0 }}</text>
            <text class="detail-label">总数量</text>
            <text class="detail-value">{{ detailData.quantity ?? '-' }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">合格数量</text>
            <text class="detail-value">{{ detailData.qualifiedQuantity ?? '-' }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">不合格数量</text>
            <text class="detail-value">{{ detailData.unqualifiedQuantity ?? '-' }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">合格率</text>
            <text class="detail-value">{{ formatPassRate(detailData.passRate) }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">检测单位</text>
@@ -160,16 +172,22 @@
    return dayjs(date).format("YYYY-MM-DD");
  };
  // èŽ·å–æ ‡ç­¾ç±»åž‹
  const getTagType = result => {
    switch (result) {
      case "合格":
        return "success";
      case "不合格":
        return "error";
      default:
        return "info";
    }
  const formatPassRate = val => {
    if (val === null || val === undefined || val === "") return "-";
    const n = Number(val);
    if (Number.isNaN(n)) return String(val);
    if (n > 0 && n <= 1) return `${(n * 100).toFixed(1)}%`;
    return `${Number.isInteger(n) ? n : Number(n.toFixed(1))}%`;
  };
  const getPassRateTagType = val => {
    if (val === null || val === undefined || val === "") return "info";
    let n = Number(val);
    if (Number.isNaN(n)) return "info";
    if (n > 0 && n <= 1) n *= 100;
    if (n >= 100) return "success";
    if (n >= 60) return "warning";
    return "error";
  };
  // èŽ·å–çŠ¶æ€æ ‡ç­¾ç±»åž‹
src/pages/qualityManagement/processInspection/index.vue
@@ -75,11 +75,11 @@
              </view>
            </view>
            <view class="status-tags">
              <u-tag :type="getTagType(item.checkResult)"
                     v-if="item.checkResult!=null"
              <u-tag :type="getPassRateTagType(item.passRate)"
                     v-if="item.passRate != null && item.passRate !== ''"
                     size="mini"
                     class="status-tag">
                {{ item.checkResult }}
                {{ formatPassRate(item.passRate) }}
              </u-tag>
              <u-tag :type="getStateTagType(item.inspectState)"
                     size="mini"
@@ -107,8 +107,16 @@
              <text class="detail-value">{{ item.checkName || '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">数量</text>
              <text class="detail-label">总数量</text>
              <text class="detail-value">{{ item.quantity || 0 }} {{ item.unit || '' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">合格数量</text>
              <text class="detail-value">{{ item.qualifiedQuantity ?? '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">不合格数量</text>
              <text class="detail-value">{{ item.unqualifiedQuantity ?? '-' }}</text>
            </view>
            <view class="detail-row">
              <text class="detail-label">检测单位</text>
@@ -315,11 +323,31 @@
    return inspectState ? "checkmark-circle" : "time";
  };
  // èŽ·å–æ ‡ç­¾ç±»åž‹
  const getTagType = checkResult => {
    if (checkResult === "合格") return "success";
    if (checkResult === "不合格") return "error";
    return "default";
  const formatPassRate = val => {
    if (val === null || val === undefined || val === "") return "-";
    const n = Number(val);
    if (Number.isNaN(n)) return String(val);
    if (n > 0 && n <= 1) return `${(n * 100).toFixed(1)}%`;
    return `${Number.isInteger(n) ? n : Number(n.toFixed(1))}%`;
  };
  const getPassRateTagType = val => {
    if (val === null || val === undefined || val === "") return "default";
    let n = Number(val);
    if (Number.isNaN(n)) return "default";
    if (n > 0 && n <= 1) n *= 100;
    if (n >= 100) return "success";
    if (n >= 60) return "warning";
    return "error";
  };
  const isFullPassByPassRate = item => {
    const v = item.passRate;
    if (v === null || v === undefined || v === "") return false;
    const n = Number(v);
    if (Number.isNaN(n)) return false;
    if (n > 0 && n <= 1) return n >= 1;
    return n >= 100;
  };
  // èŽ·å–çŠ¶æ€æ ‡ç­¾ç±»åž‹
@@ -383,7 +411,7 @@
          item => !item.inspectState
        ).length;
        qualifiedCount.value = inspectionList.value.filter(
          item => item.checkResult === "合格"
          isFullPassByPassRate
        ).length;
      })
      .catch(err => {