spring
2 天以前 6e763136fdf4469143ebbae0b717eb8e9b0ca954
src/pages/routingInspection/detail/indexJX.vue
@@ -34,6 +34,9 @@
        保存
      </wd-button>
      <view class="placeholder"></view>
      <view class="scan-info">
        <text class="scan-device-text">当前扫码机台: {{ scannedDeviceModel || "未扫码" }}</text>
      </view>
      <view class="scan-wrapper" @click="openScan">
        <wd-icon name="scan" size="24px" color="#0D867F"></wd-icon>
      </view>
@@ -70,20 +73,20 @@
        <wd-form-item label="生产长度" prop="actuallyLength">
          {{ formatValue(recordData.fixedInfo?.actuallyLength, "m") }}
        </wd-form-item>
        <wd-form-item label="张力设置" prop="twistTension">
          {{ formatValue(recordData.fixedInfo?.twistTension, "N/m") }}
        <wd-form-item label="张力设置" prop="tensionSetting">
          {{ formatValue(recordData.fixedInfo?.tensionSetting, "N/m") }}
        </wd-form-item>
        <!-- 绞制外径(可编辑) -->
        <wd-form-item label="绞制外径" prop="twistDiameter" required>
        <wd-form-item label="绞合外径" prop="twistedOuterDiameter" required>
          <template v-if="isEdit">
            <wd-input
              v-model="formData.twistDiameter"
              placeholder="请输入绞制外径(mm)"
              v-model="formData.twistedOuterDiameter"
              placeholder="请输入绞合外径(mm)"
              type="number"
            />
          </template>
          <template v-else>
            {{ formatValue(formData.twistDiameter, "mm") }}
            {{ formatValue(formData.twistedOuterDiameter, "mm") }}
          </template>
        </wd-form-item>
      </wd-col>
@@ -138,6 +141,7 @@
        style="padding-bottom: 10px"
      >
        <wd-form-item
          prop="structureItemsGroup"
          :label="formatValue(item.structureName)"
          label-width="400rpx"
          style="color: red"
@@ -184,6 +188,7 @@
          :label="formatValue(item.twistName)"
          label-width="400rpx"
          style="color: red"
          prop="inspectTwistGroup"
          required
        ></wd-form-item>
        <wd-form-item label="绞向" prop="direction" required>
@@ -203,7 +208,12 @@
        </wd-form-item>
        <wd-form-item label="节距" prop="pitch" required>
          <template v-if="isEdit">
            <wd-input v-model="item.pitch" placeholder="请输入节距(mm)" type="number" />
            <wd-input
              v-model="item.pitch"
              placeholder="请输入节距(mm)"
              type="number"
              @input="updatePitchRatio(item)"
            />
          </template>
          <template v-else>
            {{ formatValue(item.pitch, "mm") }}
@@ -223,17 +233,18 @@
      <wd-col :span="24">
        <wd-form-item label="产品外观" prop="productAppearance" required>
          <template v-if="isEdit">
            <wd-checkbox-group
              v-model="formData.productAppearance"
              inline
              v-for="(opt, idx) in appearanceOptions"
              :key="idx"
              style="text-align: justify"
            >
              <wd-checkbox :modelValue="opt.value" style="width: 100px">
            <view style="display: flex; flex-wrap: wrap; gap: 10px">
              <wd-checkbox
                v-for="(opt, idx) in appearanceOptions"
                :key="idx"
                :value="opt.value"
                :modelValue="formData.productAppearance.includes(opt.value)"
                @click="handleAppearanceClick(opt.value)"
                style="width: 100px"
              >
                {{ opt.label }}
              </wd-checkbox>
            </wd-checkbox-group>
            </view>
          </template>
          <template v-else>
            {{ formatProductAppearance(formData.productAppearance) }}
@@ -290,40 +301,15 @@
      <view style="margin: 10rpx">
        <text class="title">{{ "附件" }}</text>
      </view>
      <view class="attachment-grid">
        <!-- 已上传附件(包含新上传的) -->
        <wd-col v-for="(file, index) in allFiles" :key="file.id || index" class="attachment-item">
          <wd-img :width="80" :height="80" :src="file.url" @click="previewImage(file.url)">
            <template #error>
              <view class="error-wrap">加载失败</view>
            </template>
            <template #loading>
              <view class="loading-wrap">
                <wd-loading />
              </view>
            </template>
          </wd-img>
          <!-- 编辑模式下显示删除按钮 -->
          <!-- <wd-icon
            v-if="isEdit"
            name="close-circle"
            class="delete-icon"
            @click.stop="deleteFile(file.id || index)"
          ></wd-icon> -->
        </wd-col>
        <!-- 上传按钮(仅编辑模式显示) -->
        <wd-col v-if="isEdit" class="attachment-item upload-btn">
          <wd-upload
            :multiple="true"
            :max-count="5"
            :before-upload="beforeUpload"
            @success="handleUploadSuccess"
            @fail="handleUploadFail"
          >
            <view class="upload-icon">+</view>
          </wd-upload>
        </wd-col>
      </view>
      <wd-col :span="24">
        <AttachmentUpload
          :detailData="detailData"
          :isEdit="isEdit"
          :deviceType="paramsType"
          ref="attachmentRef"
          v-if="detailDataLoaded"
        />
      </wd-col>
    </wd-row>
    <wd-popup v-model="show" custom-style="border-radius:32rpx;" @close="handleClose">
@@ -331,32 +317,45 @@
        <img :src="previewImageUrl" alt="预览图片" style="width: 100%; height: auto" />
      </div>
    </wd-popup>
    <Scan ref="scanRef" emitName="scan" />
    <wd-toast />
  </view>
</template>
<script setup lang="ts">
import { ref, reactive, computed } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { ref, reactive, computed, onUnmounted } from "vue";
import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
import RoutingInspectionApi from "@/api/routingInspection/routingInspection";
import Scan from "@/components/scan/index.vue";
import { useToast } from "wot-design-uni";
import AttachmentUpload from "../upload.vue";
import { useUserStore } from "@/store/modules/user";
import { useScanCode } from "@/composables/useScanCode";
// 核心状态
const paramsType = ref("");
const paramsId = ref("");
const recordData = ref<any>({ structureInfo: { files: [], structureRecordResult: {} } });
const show = ref(false);
const previewImageUrl = ref("");
const isEdit = ref(false);
const tempFiles = ref<any[]>([]); // 临时存储新上传的附件
const deviceUid = ref("");
const scanRef = ref();
const toast = useToast();
const attachmentRef = ref<any>(null);
const detailData = reactive<any>({});
const detailDataLoaded = ref(false);
// 表单临时数据(编辑模式绑定)
// 获取当前登录用户信息
const userStore = useUserStore();
const userInfo: any = computed(() => userStore.userInfo);
// 使用扫码管理 composable(全局监听器,不随页面切换关闭)
const {
  deviceUid,
  deviceModel: scannedDeviceModel,
  loadFromCache,
  enableListener,
} = useScanCode("scanJX");
const formData = reactive({
  twistDiameter: "", // 绞制外径
  twistedOuterDiameter: "", // 绞制外径
  structureFormula: "", // 成品结构
  structureItems: [], // 结构标准值和实测
  inspectTwist: [], // 绞线工艺质量控制
@@ -365,7 +364,6 @@
  sampleComplete: "", // 样品是否齐全
});
// 选项数据
const twistDirectionOptions = [
  { label: "左向", value: "左向" },
  { label: "右向", value: "右向" },
@@ -387,47 +385,51 @@
  { label: "否", value: "否" },
];
// 合并原有附件和新上传附件
const allFiles = computed(() => {
  return [...(recordData.value.structureInfo?.files || []), ...tempFiles.value];
});
// 初始化表单数据(接口数据同步到表单)
const initFormData = () => {
  const structureResult = recordData.value.structureInfo?.structureRecordResult || {};
  const inspectionResult = recordData.value.inspectionResult || {};
  // 基础可编辑字段
  formData.twistDiameter = inspectionResult.twistDiameter || "";
  formData.twistedOuterDiameter =
    recordData.value.structureInfo.structureRecordResult.twistedOuterDiameter || "";
  formData.structureFormula = structureResult.inspectStructure?.structureFormula || "";
  formData.sampleComplete = inspectionResult.sampleComplete || "";
  formData.conclusion = structureResult.conclusion || "";
  // 修复产品外观和结论的数据格式
  formData.productAppearance = Array.isArray(structureResult.productAppearance)
  // 初始化产品外观
  const appearance = Array.isArray(structureResult.productAppearance)
    ? structureResult.productAppearance
    : structureResult.productAppearance
    ? [structureResult.productAppearance]
    : [];
      ? [structureResult.productAppearance]
      : [];
  formData.productAppearance = appearance;
  // 循环类字段(深拷贝避免原数据污染)
  formData.structureItems = JSON.parse(
    JSON.stringify(structureResult.inspectStructure?.structureItems || [])
  );
  formData.inspectTwist = JSON.parse(JSON.stringify(structureResult.inspectTwist || []));
  // 初始化绞向数据(确保有值)
  formData.inspectTwist.forEach((item: any) => {
    if (!item.direction) item.direction = "";
  });
};
// 获取详情数据
const getDetailData = async (id: string, deviceType: string) => {
  try {
    const response = await RoutingInspectionApi.getStrandedInspectionStructureInfoById({ id });
    recordData.value = response.data;
    detailData.value = response.data.structureInfo;
    // 如果记录人为空,默认设置为当前登录用户
    if (recordData.value.structureInfo && !recordData.value.structureInfo.createUserName) {
      recordData.value.structureInfo.createUserName =
        userInfo.value?.nickName || userInfo.value?.userName || "";
    }
    console.log("detailData.value", detailData.value);
    tempFiles.value = []; // 清空临时文件
    initFormData(); // 数据返回后初始化表单
    detailDataLoaded.value = true; // 数据加载完成后,渲染子组件
    console.log("父组件-数据就绪后打印");
  } catch (error) {
    console.error("获取详情失败:", error);
    uni.showToast({ title: "加载失败", icon: "error" });
@@ -436,8 +438,14 @@
// 页面加载
onLoad((options: any) => {
  paramsId.value = options.id;
  getDetailData(options.id, options.deviceType);
  try {
    paramsId.value = options.id;
    paramsType.value = options.deviceType;
    getDetailData(options.id, options.deviceType);
  } catch (error) {
    console.error("获取详情失败:", error);
    uni.showToast({ title: "加载失败", icon: "error" });
  }
});
// 编辑模式切换
@@ -448,15 +456,16 @@
// 取消编辑(重置表单)
const close = () => {
  isEdit.value = false;
  tempFiles.value = []; // 取消时清空临时上传的文件
  initFormData(); // 恢复原始数据
  tempFiles.value = [];
  initFormData();
};
// 保存编辑(含必填项校验)
const saveList = async () => {
  // 1. 基础字段校验
  if (!formData.structureFormula) return uni.showToast({ title: "成品结构为必填项", icon: "none" });
  if (!formData.twistDiameter) return uni.showToast({ title: "绞制外径为必填项", icon: "none" });
  if (!formData.twistedOuterDiameter)
    return uni.showToast({ title: "绞制外径为必填项", icon: "none" });
  if (!formData.productAppearance.length)
    return uni.showToast({ title: "产品外观为必填项", icon: "none" });
  if (!formData.conclusion) return uni.showToast({ title: "结论为必填项", icon: "none" });
@@ -480,15 +489,24 @@
    if (!item.pitchRatio)
      return uni.showToast({ title: `${item.twistName}节径比为必填项`, icon: "none" });
  }
  console.log("1111", deviceUid.value);
  if (!deviceUid.value) return uni.showToast({ title: "请扫描二维码", icon: "none" });
  // 4. 提交数据到接口
  // 验证扫码数据(从缓存或新扫码获取)
  console.log("保存前检查 deviceUid:", deviceUid.value);
  if (!deviceUid.value) {
    return uni.showToast({
      title: "请先扫描设备二维码",
      icon: "none",
      duration: 2000,
    });
  }
  const { newFiles } = attachmentRef.value.getSubmitFiles();
  console.log("newFiles", newFiles);
  const allFileIds = [...newFiles];
  try {
    const res = await RoutingInspectionApi.strandedPatrolCheckInspection({
      deviceUid: deviceUid.value,
      id: paramsId.value,
      result: {
        twistDiameter: formData.twistDiameter,
        twistedOuterDiameter: formData.twistedOuterDiameter,
        structureFormula: formData.structureFormula,
        structureItems: formData.structureItems,
        inspectTwist: formData.inspectTwist,
@@ -499,13 +517,24 @@
      inspectionResult: {
        sampleComplete: formData.sampleComplete,
      },
      processInspectionAttachmentList: tempFiles.value.map((f) => f.url), // 新上传的附件
      processInspectionAttachmentList: allFileIds,
    });
    if (res.code === 200) {
      uni.showToast({ title: "保存成功", icon: "success" });
      isEdit.value = false;
      getDetailData(paramsId.value, recordData.value.deviceType); // 刷新数据
      // 设置刷新标记,告诉列表页需要刷新
      uni.setStorageSync("needRefreshInspectionList", true);
      uni.showToast({
        title: "保存成功",
        icon: "success",
        duration: 1500,
      });
      // 延迟返回列表页,让用户看到成功提示
      setTimeout(() => {
        uni.navigateBack({
          delta: 1,
        });
      }, 1500);
    } else {
      uni.showModal({ title: res.msg || "保存失败", icon: "error" });
    }
@@ -513,61 +542,6 @@
    console.error("保存失败:", e);
    uni.showModal({ title: e.message || "保存失败", icon: "error" });
  }
};
// 附件上传前置校验
const beforeUpload = (file: any) => {
  // 限制图片大小不超过2M
  const maxSize = 2 * 1024 * 1024;
  if (file.size > maxSize) {
    uni.showToast({ title: "图片大小不能超过2M", icon: "none" });
    return false;
  }
  return true;
};
// 附件上传成功回调
const handleUploadSuccess = (res: any) => {
  // 假设接口返回格式: { url: 'xxx', id: 'xxx' }
  if (Array.isArray(res)) {
    tempFiles.value = [
      ...tempFiles.value,
      ...res.map((file) => ({
        ...file,
        id: `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, // 生成临时ID
      })),
    ];
  } else {
    tempFiles.value.push({
      ...res,
      id: `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
    });
  }
};
// 附件上传失败回调
const handleUploadFail = (err: any) => {
  uni.showToast({ title: "附件上传失败", icon: "error" });
};
// 删除附件
const deleteFile = (id: string | number) => {
  // 删除临时文件
  tempFiles.value = tempFiles.value.filter((file) => file.id !== id);
  // 如果是原有文件,需要标记删除(实际项目中可能需要接口交互)
  if (typeof id !== "string" || !id.startsWith("temp-")) {
    // 这里可以实现删除原有文件的逻辑
    recordData.value.structureInfo.files = recordData.value.structureInfo.files.filter(
      (file: any, index: number) => index !== id
    );
  }
};
// 图片预览
const previewImage = (url: string) => {
  previewImageUrl.value = url;
  show.value = true;
};
const handleClose = () => {
@@ -582,11 +556,11 @@
    case 1:
      return "danger"; // 已驳回
    case 2:
      return "info"; // 待审核
      return "primary"; // 待审核
    case 3:
      return "success"; // 通过
    default:
      return "info";
      return "default";
  }
};
@@ -628,36 +602,90 @@
  });
};
const openScan = () => {
  scanRef.value.triggerScan();
// 计算节径比
const calculatePitchRatio = (pitch: string, dia: string) => {
  // 如果pitch或dia为空,则返回"-"
  if (!pitch || !dia) return "-";
  // 将pitch和dia转换为浮点数
  const pitchNum = parseFloat(pitch);
  const diaNum = parseFloat(dia);
  // 如果pitchNum或diaNum是NaN,或者diaNum为0,则返回"-"
  if (isNaN(pitchNum) || isNaN(diaNum) || diaNum === 0) return "-";
  // 计算pitchNum和diaNum的比值,并保留两位小数
  return (pitchNum / diaNum).toFixed(2);
};
const getScanCode = (params: any) => {
  console.log("完整参数:", params);
  let codeObj = {};
  try {
    codeObj = JSON.parse(params.code);
  } catch (err) {
    console.error("JSON解析失败:", err);
    toast.error("扫码数据异常");
    return; // 解析失败直接返回,避免后续错误
// 更新节径比(当节距变化时自动计算)
const updatePitchRatio = (item: any) => {
  // 使用绞合外径作为直径来计算节径比
  const dia = item.dia;
  item.pitchRatio = calculatePitchRatio(item.pitch, dia);
};
// 处理产品外观选择的互斥逻辑
const handleAppearanceClick = (value: string) => {
  const currentValues = [...formData.productAppearance];
  const isCurrentlyChecked = currentValues.includes(value);
  let newSelection: string[] = [];
  if (value === "无外观问题") {
    if (isCurrentlyChecked) {
      // 取消选中"无外观问题"
      newSelection = [];
    } else {
      // 选中"无外观问题",清空其他选项
      newSelection = ["无外观问题"];
    }
  } else {
    // 点击其他选项
    if (isCurrentlyChecked) {
      // 取消选中该选项
      newSelection = currentValues.filter((v) => v !== value);
    } else {
      // 选中该选项,移除"无外观问题"
      const filteredValues = currentValues.filter((v) => v !== "无外观问题");
      newSelection = [...filteredValues, value];
    }
  }
  deviceUid.value = codeObj?.uid;
  toast.success("扫码成功");
  formData.productAppearance = newSelection;
};
// 确保先移除再添加监听
const setupScanListener = () => {
  uni.$off("scan", getScanCode); // 先移除旧的
  uni.$on("scan", getScanCode); // 再添加新的
const openScan = () => {
  console.log("indexJX - 点击扫码按钮(全局扫码模式,无需手动触发)");
  // 全局扫码模式下,硬件扫码会自动触发,无需手动调用
  uni.showToast({
    title: "请使用扫码枪扫描",
    icon: "none",
  });
};
onUnmounted(() => {
  // 开启广播监听事件
  uni.$off("scan", getScanCode);
  console.log("离开1");
});
onMounted(() => {
  // 开启广播监听事件
  setupScanListener();
  console.log("显示1");
// 页面显示时的处理
onShow(() => {
  console.log("========== indexJX - onShow 触发 ==========");
  // 重新启用监听器(确保监听器有效)
  enableListener();
  // 加载缓存(更新UI显示)
  const cachedData = loadFromCache();
  // 如果没有缓存数据,提示用户需要扫码
  if (!cachedData || !cachedData.uid) {
    console.log("⚠️ 未检测到扫码缓存,用户需要扫描设备二维码");
    // 在编辑模式下才提示
    if (isEdit.value) {
      setTimeout(() => {
        uni.showToast({
          title: "请扫描设备二维码后再保存",
          icon: "none",
          duration: 2000,
        });
      }, 500);
    }
  }
});
</script>
@@ -687,6 +715,18 @@
  flex: 1;
}
.scan-info {
  display: flex;
  align-items: center;
  margin-right: 10px;
  .scan-device-text {
    font-size: 14px;
    color: #0d867f;
    font-weight: 500;
  }
}
.scan-wrapper {
  width: 38px;
  height: 38px;