From 7235cbde16ad4ac19f19d9a9a96e12d71a23ebce Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期六, 16 五月 2026 13:42:00 +0800
Subject: [PATCH] 优化报修表单,新增验收人字段,调整状态管理,提升报修详情展示效果。

---
 src/pages/equipmentManagement/repair/detail.vue     |  362 ++++++++++++++++++++++++++++++++
 src/pages/equipmentManagement/repair/acceptance.vue |  259 +++++++++++++++++++++++
 2 files changed, 621 insertions(+), 0 deletions(-)

diff --git a/src/pages/equipmentManagement/repair/acceptance.vue b/src/pages/equipmentManagement/repair/acceptance.vue
new file mode 100644
index 0000000..0c24129
--- /dev/null
+++ b/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>
diff --git a/src/pages/equipmentManagement/repair/detail.vue b/src/pages/equipmentManagement/repair/detail.vue
new file mode 100644
index 0000000..8e62232
--- /dev/null
+++ b/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>

--
Gitblit v1.9.3