已修改9个文件
1064 ■■■■■ 文件已修改
src/components/Editor/index.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/imageUpload/index.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config.js 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/manifest.json 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/index.vue 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inspectionUpload/components/formDia.vue 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inspectionUpload/index.vue 546 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/login.vue 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/request.ts 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Editor/index.vue
@@ -1,18 +1,18 @@
<template>
  <view class="editor-container">
    <div class="editor">
      <QuillEditor v-model:content="content"
                   contentType="html"
                   @textChange="(e) => emit('update:modelValue', content)"
                   :options="options"
                   :style="styles" />
<!--      <QuillEditor v-model:content="content"-->
<!--                   contentType="html"-->
<!--                   @textChange="(e) => emit('update:modelValue', content)"-->
<!--                   :options="options"-->
<!--                   :style="styles" />-->
    </div>
  </view>
</template>
<script setup>
  import { ref, computed, watch } from "vue";
  import { QuillEditor } from "@vueup/vue-quill";
  // import { QuillEditor } from "@vueup/vue-quill";
  import "@vueup/vue-quill/dist/vue-quill.snow.css";
  import { getToken } from "@/utils/auth";
src/components/imageUpload/index.vue
@@ -103,7 +103,7 @@
// Props 定义
const props = defineProps({
  modelValue: [String, Object, Array],
  action: { type: String, default: "/common/minioUploads" },
  action: { type: String, default: "/file/upload" },
  data: { type: Object },
  limit: { type: Number, default: 5 },
  fileSize: { type: Number, default: 10 }, // 默认10MB,适合视频
@@ -182,7 +182,7 @@
const testServerConnection = () => {
  return new Promise((resolve) => {
    uni.request({
      url: uploadFileUrl.value.replace('/common/minioUploads', '/common/test'),
      url: uploadFileUrl.value.replace('/common/minioUploads', '/file/upload'),
      method: 'GET',
      timeout: 5000,
      success: (res) => {
@@ -383,6 +383,8 @@
  const uploadTask = uni.uploadFile({
    ...uploadParams,
    success: (res) => {
            console.log('res---', res)
      try {
        if (res.statusCode === 200) {
          const response = JSON.parse(res.data);
src/config.js
@@ -1,11 +1,6 @@
// 应用全局配置
const config = {
  //  baseUrl: 'https://vue.ruoyi.vip/prod-api',
  // baseUrl: 'http://localhost/prod-api',
  baseUrl: 'http://114.132.189.42:9068', // 新疆海川开心
  // baseUrl: 'http://192.168.1.185:9988', // 本地测试
   //cloud后台网关地址
  //  baseUrl: 'http://192.168.10.3:8080',
  baseUrl: 'http://114.132.189.42:9030',
   // 应用信息
   appInfo: {
     // 应用名称
src/manifest.json
@@ -1,6 +1,6 @@
{
    "name" : "信息管理",
    "appid" : "__UNI__E1C100D",
    "name" : "湟水峡信息管理",
    "appid" : "__UNI__6C30F75",
    "description" : "",
    "versionName" : "1.0.0",
    "versionCode" : "100",
@@ -23,8 +23,7 @@
        /* 模块配置 */
        "modules" : {
            "Camera" : {},
            "Barcode" : {},
            "Push" : {}
            "Barcode" : {}
        },
        /* 应用发布信息 */
        "distribute" : {
@@ -53,17 +52,7 @@
                "dSYMs" : false
            },
            /* SDK配置 */
            "sdkConfigs" : {
                "push" : {
                    "unipush" : {
                        "icons" : {
                            "small" : {
                                "ldpi" : "D:/xindao/wenjian/img/logo/app.png"
                            }
                        }
                    }
                }
            },
            "sdkConfigs" : {},
            "icons" : {
                "android" : {
                    "hdpi" : "unpackage/res/icons/72x72.png",
src/pages/index.vue
@@ -18,74 +18,11 @@
    <view class="hero-section">
      <view class="bg-img">
        <view class="hero-content">
          <text class="hero-title">军泰伟业</text>
          <text class="hero-title">湟水峡</text>
        </view>
        <view class="hero-wave"></view>
      </view>
    </view>
    <!--        <view class="notice-section">-->
    <!--            <view class="notice">-->
    <!--                <view class="notice-content">-->
    <!--                    <view class="notice-left">-->
    <!--                        <text class="notice-status">通知</text>-->
    <!--                    </view>-->
    <!--                    <view class="notice-separator"></view>-->
    <!--                    <view class="notice-right">-->
    <!--                        <text class="notice-label">{{currentStatus}}</text>-->
    <!--                        <text class="notice-text">当日销售设备数:<text class="notice-number">{{number}}<text class="notice-unit">个</text></text></text>-->
    <!--                    </view>-->
    <!--                </view>-->
    <!--            </view>-->
    <!--        </view>-->
    <!-- 营销管理模块 -->
    <!--    <view class="common-module marketing-module">-->
    <!--      <view class="module-header">-->
    <!--        <view class="module-title-container">-->
    <!--          <text class="module-title">营销管理</text>-->
    <!--        </view>-->
    <!--      </view>-->
    <!--      <view class="module-content">-->
    <!--        <up-grid :border="false"-->
    <!--                 col="4">-->
    <!--          <up-grid-item v-for="(item, index) in marketingItems"-->
    <!--                        :key="index"-->
    <!--                        @click="handleCommonItemClick(item)">-->
    <!--            <view class="icon-container"-->
    <!--                  :style="{ background: item.bgColor }">-->
    <!--              <up-icon :name="item.icon"-->
    <!--                       :size="58"-->
    <!--                       color="#ffffff"></up-icon>-->
    <!--            </view>-->
    <!--            <text class="item-label">{{item.label}}</text>-->
    <!--          </up-grid-item>-->
    <!--        </up-grid>-->
    <!--      </view>-->
    <!--    </view>-->
    <!--    &lt;!&ndash; 采购管理模块 &ndash;&gt;-->
    <!--    <view class="common-module purchase-module">-->
    <!--      <view class="module-header">-->
    <!--        <view class="module-title-container">-->
    <!--          <text class="module-title">采购管理</text>-->
    <!--        </view>-->
    <!--      </view>-->
    <!--      <view class="module-content">-->
    <!--        <up-grid :border="false"-->
    <!--                 col="4">-->
    <!--          <up-grid-item v-for="(item, index) in purchaseItems"-->
    <!--                        :key="index"-->
    <!--                        @click="handleCommonItemClick(item)">-->
    <!--            <view class="icon-container"-->
    <!--                  :style="{ background: item.bgColor }">-->
    <!--              <up-icon :name="item.icon"-->
    <!--                       :size="58"-->
    <!--                       color="#ffffff"></up-icon>-->
    <!--            </view>-->
    <!--            <text class="item-label">{{item.label}}</text>-->
    <!--          </up-grid-item>-->
    <!--        </up-grid>-->
    <!--      </view>-->
    <!--    </view>-->
    <!--    &lt;!&ndash; 协同办公模块 &ndash;&gt;-->
    <view class="common-module collaboration-module">
      <view class="module-header">
        <view class="module-title-container">
@@ -109,83 +46,30 @@
        </up-grid>
      </view>
    </view>
    <!-- 生产管控模块 -->
    <!--        <view class="common-module production-module">-->
    <!--            <view class="module-header">-->
    <!--                <view class="module-title-container">-->
    <!--                    <text class="module-title">生产管控</text>-->
    <!--                </view>-->
    <!--            </view>-->
    <!--            <view class="module-content">-->
    <!--                <up-grid-->
    <!--                    :border="false"-->
    <!--                    col="4"-->
    <!--                >-->
    <!--                    <up-grid-item-->
    <!--                        v-for="(item, index) in productionItems"-->
    <!--                        :key="index"-->
    <!--                        @click="handleCommonItemClick(item)"-->
    <!--                    >-->
    <!--                        <view class="icon-container" :style="{ background: item.bgColor }">-->
    <!--                            <up-icon-->
    <!--                                :name="item.icon"-->
    <!--                                :size="58"-->
    <!--                                color="#ffffff"-->
    <!--                            ></up-icon>-->
    <!--                        </view>-->
    <!--                        <text class="item-label">{{item.label}}</text>-->
    <!--                    </up-grid-item>-->
    <!--                </up-grid>-->
    <!--            </view>-->
    <!--        </view>-->
    <!-- 生产管控模块 -->
    <view class="common-module equipment-module">
      <view class="module-header">
        <view class="module-title-container">
          <text class="module-title">生产管控</text>
        </view>
      </view>
      <view class="module-content">
        <up-grid :border="false"
                 col="4">
          <up-grid-item v-for="(item, index) in productionItems"
                        :key="index"
                        @click="handleCommonItemClick(item)">
            <view class="icon-container"
                  :style="{ background: item.bgColor }">
              <up-icon :name="item.icon"
                       :size="58"
                       color="#ffffff"></up-icon>
            </view>
            <text class="item-label">{{item.label}}</text>
          </up-grid-item>
        </up-grid>
      </view>
    </view>
    <!-- 设备管理模块 -->
    <!--    <view class="common-module equipment-module">-->
    <!--      <view class="module-header">-->
    <!--        <view class="module-title-container">-->
    <!--          <text class="module-title">设备管理</text>-->
    <!--        </view>-->
    <!--      </view>-->
    <!--      <view class="module-content">-->
    <!--        <up-grid :border="false"-->
    <!--                 col="4">-->
    <!--          <up-grid-item v-for="(item, index) in equipmentItems"-->
    <!--                        :key="index"-->
    <!--                        @click="handleCommonItemClick(item)">-->
    <!--            <view class="icon-container"-->
    <!--                  :style="{ background: item.bgColor }">-->
    <!--              <up-icon :name="item.icon"-->
    <!--                       :size="58"-->
    <!--                       color="#ffffff"></up-icon>-->
    <!--            </view>-->
    <!--            <text class="item-label">{{item.label}}</text>-->
    <!--          </up-grid-item>-->
    <!--        </up-grid>-->
    <!--      </view>-->
    <!--    </view>-->
        <view class="common-module equipment-module">
          <view class="module-header">
            <view class="module-title-container">
              <text class="module-title">设备管理</text>
            </view>
          </view>
          <view class="module-content">
            <up-grid :border="false"
                     col="4">
              <up-grid-item v-for="(item, index) in equipmentItems"
                            :key="index"
                            @click="handleCommonItemClick(item)">
                <view class="icon-container"
                      :style="{ background: item.bgColor }">
                  <up-icon :name="item.icon"
                           :size="58"
                           color="#ffffff"></up-icon>
                </view>
                <text class="item-label">{{item.label}}</text>
              </up-grid-item>
            </up-grid>
          </view>
        </view>
  </view>
</template>
@@ -279,62 +163,6 @@
  // 协同办公功能数据
  const collaborationItems = reactive([
    {
      icon: "/static/images/icon/gongchuguanli@2x.png",
      label: "公出管理",
    },
    {
      icon: "/static/images/icon/qingjiaguanli@2x.png",
      label: "请假管理",
    },
    {
      icon: "/static/images/icon/chuchaiguanli@2x.png",
      label: "出差管理",
    },
    {
      icon: "/static/images/icon/chuchaiguanli@2x.png",
      label: "报销管理",
    },
    {
      icon: "/static/images/icon/chuchaiguanli@2x.png",
      label: "采购管理",
    },
    {
      icon: "/static/images/icon/chuchaiguanli@2x.png",
      label: "报价管理",
    },
    {
      icon: "/static/images/icon/chuchaiguanli@2x.png",
      label: "出库管理",
    },
    {
      icon: "/static/images/icon/qingjiaguanli@2x.png",
      label: "会议设置",
    },
    {
      icon: "/static/images/icon/qingjiaguanli@2x.png",
      label: "会议列表",
    },
    {
      icon: "/static/images/icon/qingjiaguanli@2x.png",
      label: "会议申请",
    },
    {
      icon: "/static/images/icon/qingjiaguanli@2x.png",
      label: "会议审批",
    },
    {
      icon: "/static/images/icon/qingjiaguanli@2x.png",
      label: "会议发布",
    },
    {
      icon: "/static/images/icon/qingjiaguanli@2x.png",
      label: "会议总结",
    },
    {
      icon: "/static/images/icon/xietongshenpi@2x.png",
      label: "协同审批",
    },
    {
      icon: "/static/images/icon/kehubaifang@2x.png",
      label: "客户拜访",
    },
@@ -371,41 +199,9 @@
  // 设备管理功能数据
  const equipmentItems = reactive([
    // {
    //     icon: '/static/images/icon/shebeitaizhang@2x.png',
    //     label: '设备台账',
    // },
    {
      icon: "/static/images/icon/shbeibaoxiu@2x.png",
      label: "设备报修",
    },
    {
      icon: "/static/images/icon/shbeibaoyang@2x.png",
      label: "设备保养",
    },
    {
      icon: "/static/images/icon/xunjianshangchuan@2x.png",
      label: "巡检上传",
    },
    {
      icon: "/static/images/icon/guzhangfenxi@2x.png",
      label: "分析追溯",
      bgColor: "#ff9800",
    },
    {
      icon: "/static/images/icon/zhinengpaidan@2x.png",
      label: "智能派单",
      bgColor: "#ff6b35",
    },
    {
      icon: "/static/images/icon/zuoyezhidao@2x.png",
      label: "作业指导",
      bgColor: "#4caf50",
    },
    {
      icon: "/static/images/icon/jieguoyanzheng@2x.png",
      label: "结果验证",
      bgColor: "#9c27b0",
    },
  ]);
src/pages/inspectionUpload/components/formDia.vue
@@ -21,8 +21,8 @@
            name="before"
            multiple
            :maxCount="10"
            :maxSize="1024 * 1024"
            accept="video/*"
            :maxSize="5 * 1024 * 1024"
            accept="image/*"
            :previewFullImage="true"
          ></u-upload>
        </view>
@@ -36,8 +36,8 @@
            name="after"
            multiple
            :maxCount="10"
            :maxSize="1024 * 1024"
            accept="video/*"
            :maxSize="5 * 1024 * 1024"
            accept="image/*"
            :previewFullImage="true"
          ></u-upload>
        </view>
@@ -51,8 +51,8 @@
            name="issue"
            multiple
            :maxCount="10"
            :maxSize="1024 * 1024"
            accept="video/*"
            :maxSize="5 * 1024 * 1024"
            accept="image/*"
            :previewFullImage="true"
          ></u-upload>
        </view>
@@ -67,8 +67,10 @@
</template>
<script setup>
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { submitInspectionRecord } from '@/api/equipmentManagement/inspection.js'
import { getToken } from '@/utils/auth'
import config from '@/config'
const emit = defineEmits(['closeDia'])
@@ -78,50 +80,106 @@
const issueModelValue = ref([])
const infoData = ref(null)
// 计算上传URL
const uploadFileUrl = computed(() => {
  let baseUrl = '';
  if (process.env.VUE_APP_BASE_API) {
    baseUrl = process.env.VUE_APP_BASE_API;
  } else {
    baseUrl = config.baseUrl;
  }
  return baseUrl + '/file/upload';
})
const uploadSingleFile = async (fileItem, typeValue) => {
  const token = getToken()
  if (!token) throw new Error('用户未登录')
  // H5: u-upload 可能给原生 File(fileItem.file)
  const rawFile = fileItem?.file
  if (rawFile) {
    const formData = new FormData()
    formData.append('file', rawFile, rawFile.name || 'image.jpg')
    formData.append('type', String(typeValue))
    const res = await fetch(uploadFileUrl.value, {
      method: 'POST',
      headers: { Authorization: 'Bearer ' + token },
      body: formData
    })
    const data = await res.json()
    if (data?.code !== 200) throw new Error(data?.msg || '上传失败')
    return {
      url: data?.data?.url,
      name: rawFile.name || 'image.jpg',
      status: 'success'
    }
  }
  // 非 H5 / 兼容:走 uni.uploadFile
  return await new Promise((resolve, reject) => {
    uni.uploadFile({
      url: uploadFileUrl.value,
      filePath: fileItem.url,
      name: 'file',
      header: {
        'Authorization': `Bearer ${token}`
      },
      formData: {
        type: typeValue
      },
      success: (res) => {
        try {
          const data = JSON.parse(res.data)
          if (data.code === 200) {
            resolve({
              url: data.data.url,
              name: fileItem.name,
              status: 'success'
            })
          } else {
            reject(new Error(data.msg || '上传失败'))
          }
        } catch (e) {
          reject(e)
        }
      },
      fail: (err) => reject(err)
    })
  })
}
// 文件上传处理
const afterRead = (event) => {
  const { name, file } = event
  
  // 上传文件到服务器
  uni.uploadFile({
    url: '/api/upload', // 替换为实际的上传接口
    filePath: file.url,
    name: 'file',
    success: (res) => {
      const data = JSON.parse(res.data)
      if (data.code === 200) {
        const fileItem = {
          url: data.data.url,
          name: file.name,
          status: 'success'
        }
        // 根据name添加到对应的数组
        if (name === 'before') {
          beforeModelValue.value.push(fileItem)
        } else if (name === 'after') {
          afterModelValue.value.push(fileItem)
        } else if (name === 'issue') {
          issueModelValue.value.push(fileItem)
        }
        uni.showToast({
          title: '上传成功',
          icon: 'success'
        })
      } else {
        uni.showToast({
          title: '上传失败',
          icon: 'error'
        })
  // 根据上传类型设置不同的type值
  let typeValue = 10 // 默认值
  if (name === 'before') {
    typeValue = 10 // 生产前
  } else if (name === 'after') {
    typeValue = 11 // 生产中
  } else if (name === 'issue') {
    typeValue = 12 // 生产后
  }
  const files = Array.isArray(file) ? file : [file]
  Promise.resolve().then(async () => {
    for (const f of files) {
      const uploaded = await uploadSingleFile(f, typeValue)
      if (name === 'before') {
        beforeModelValue.value.push(uploaded)
      } else if (name === 'after') {
        afterModelValue.value.push(uploaded)
      } else if (name === 'issue') {
        issueModelValue.value.push(uploaded)
      }
    },
    fail: () => {
      uni.showToast({
        title: '上传失败',
        icon: 'error'
      })
    }
    uni.showToast({ title: '上传成功', icon: 'success' })
  }).catch((err) => {
    console.error('上传失败:', err)
    uni.showToast({ title: '上传失败', icon: 'error' })
  })
}
src/pages/inspectionUpload/index.vue
@@ -68,32 +68,6 @@
    </view>
    <!-- 扫码区域 - 全局弹窗 -->
    <view v-if="isScanning" class="qr-scan-overlay">
      <view class="qr-scan-container">
        <view class="scan-header">
          <text class="scan-title">扫描二维码</text>
          <u-button type="error" size="small" @click.stop="stopScan" :customStyle="{
            borderRadius: '15px',
            height: '30px',
            fontSize: '12px'
          }">
            关闭
          </u-button>
        </view>
        <camera class="qr-camera" device-position="back" flash="off" @scancode="handleScanCode"
          @error="handleCameraError"></camera>
        <view class="scan-frame-wrapper">
          <view class="scan-frame"></view>
          <view class="scan-tip">请将二维码放入框内</view>
        </view>
        <u-alert v-if="cameraError" :title="cameraError" type="error" :showIcon="true" :closable="true"
          @close="cameraError = ''" :customStyle="{
            margin: '10px 0'
          }"></u-alert>
      </view>
    </view>
    <!-- 图片上传弹窗 - 原生实现 -->
    <view v-if="showUploadDialog" class="custom-modal-overlay" @click="closeUploadDialog">
      <view class="custom-modal-container" @click.stop>
@@ -162,10 +136,10 @@
              <view v-if="getCurrentFiles().length > 0" class="file-list">
                <view v-for="(file, index) in getCurrentFiles()" :key="index" class="file-item">
                  <view class="file-preview-container">
                    <image v-if="file?.path?.fileType === 'image'"
                      :src="file?.url || file?.tempFilePath?.tempFilePath || file?.path?.tempFilePath"
                    <image v-if="file.type === 'image' || (file.type !== 'video' && !file.type)"
                      :src="file.url || file.tempFilePath || file.path || file.downloadUrl"
                      class="file-preview" mode="aspectFill" />
                    <view v-else class="video-preview">
                    <view v-else-if="file.type === 'video'" class="video-preview">
                      <uni-icons type="videocam" name="videocam" size="18" color="#fff"
                        style="margin-right: 5px;"></uni-icons>
                      <text class="video-text">视频</text>
@@ -294,6 +268,7 @@
import { getLedgerById } from '@/api/equipmentManagement/ledger.js'
import { inspectionTaskList, uploadInspectionTask } from "@/api/inspectionManagement";
import { getToken } from "@/utils/auth";
import config from '@/config'
// 组件引用已移除
@@ -347,7 +322,7 @@
// 上传配置
const uploadConfig = {
  action: "/common/minioUploads",
  action: "/file/upload",
  limit: 10,
  fileSize: 50, // MB
  fileType: ['jpg', 'jpeg', 'png', 'mp4', 'mov'],
@@ -356,15 +331,7 @@
// 计算上传URL
const uploadFileUrl = computed(() => {
  let baseUrl = '';
  if (process.env.VUE_APP_BASE_API) {
    baseUrl = process.env.VUE_APP_BASE_API;
  } else if (process.env.NODE_ENV === 'development') {
    baseUrl = 'http://114.132.189.42:9068';
  } else {
    baseUrl = 'http://114.132.189.42:9068';
  }
    const baseUrl = 'http://114.132.189.42:9030';
  return baseUrl + uploadConfig.action;
})
@@ -377,10 +344,6 @@
// 请求取消标志,用于取消正在进行的请求
let isRequestCancelled = false
// 扫码相关状态
const isScanning = ref(false)
const cameraError = ref('')
const pagesPames = reactive({
  size: 10,
@@ -438,11 +401,6 @@
onUnmounted(() => {
  // 设置取消标志,阻止后续的异步操作
  isRequestCancelled = true
  // 停止扫码
  if (isScanning.value) {
    isScanning.value = false
  }
  // 关闭上传弹窗
  if (showUploadDialog.value) {
@@ -528,114 +486,77 @@
  }
}
// 为指定任务开始扫码
// 为指定任务开始扫码(真机)
const startScanForTask = async (task) => {
  try {
    // 记录当前扫描的任务
    currentScanningTask.value = task
    // 显示扫描界面
    isScanning.value = true
    // 使用uniapp的扫码API
    uni.scanCode({
      success: (res) => {
        handleScanSuccess(res)
      },
      fail: (err) => {
        console.error('扫码失败:', err)
        uni.showToast({
          title: '扫码失败',
          icon: 'error'
        })
        // 关闭扫描界面
        isScanning.value = false
      }
    })
  } catch (e) {
    console.error('启动扫码失败:', e)
    uni.showToast({
      title: '启动扫码失败',
      icon: 'error'
    })
    isScanning.value = false
  }
}
// 停止扫码
const stopScan = () => {
  isScanning.value = false
  currentScanningTask.value = null
}
// 扫码成功处理
const handleScanSuccess = async (result) => {
// 扫码成功处理:校验后打开上传弹窗
const handleScanSuccess = (result) => {
  try {
    // 解析二维码数据,提取deviceId
    let deviceId = ''
    // 检查是否是URL格式
    if (result.result.includes('deviceId=')) {
      // 从URL中提取deviceId
      const url = result.result
      const match = url.match(/deviceId=(\d+)/)
      if (match && match[1]) {
        deviceId = match[1]
      }
    } else {
      // 尝试解析JSON格式
      try {
        const qrData = JSON.parse(result.result)
        deviceId = qrData.deviceId || qrData.qrCodeId || ''
      } catch (e) {
        // 如果不是JSON格式,直接使用结果
        deviceId = result.result
    if (result?.result && typeof result.result === 'string') {
      if (result.result.includes('deviceId=')) {
        const match = result.result.match(/deviceId=(\d+)/)
        if (match && match[1]) deviceId = match[1]
      } else {
        try {
          const qrData = JSON.parse(result.result)
          deviceId = qrData.deviceId || qrData.qrCodeId || ''
        } catch (e) {
          deviceId = result.result
        }
      }
    }
    if (!deviceId) {
      uni.showToast({
        title: '未识别到设备ID',
        icon: 'error'
      })
      isScanning.value = false
      uni.showToast({ title: '未识别到设备ID', icon: 'error' })
      return
    }
    // 获取当前任务的taskId
    const currentTaskId = currentScanningTask.value?.taskId || currentScanningTask.value?.id
    // 对比deviceId和taskId
    if (deviceId === currentTaskId.toString()) {
      uni.showToast({
        title: '识别成功',
        icon: 'success'
      })
      // 先关闭扫描界面
      isScanning.value = false
      // 延迟打开上传弹窗,确保扫描界面完全关闭
      setTimeout(() => {
        openUploadDialog(currentScanningTask.value)
      }, 300)
    } else {
      uni.showToast({
        title: '请扫描正确的设备',
        icon: 'error'
      })
      // 关闭扫描界面
      isScanning.value = false
    if (!currentTaskId) {
      uni.showToast({ title: '任务信息缺失', icon: 'error' })
      return
    }
    if (deviceId === currentTaskId.toString()) {
      uni.showToast({ title: '识别成功', icon: 'success' })
      openUploadDialog(currentScanningTask.value)
    } else {
      uni.showToast({ title: '请扫描正确的设备', icon: 'error' })
    }
  } catch (error) {
    console.error('扫码结果处理失败:', error)
    uni.showToast({
      title: error.message || '数据解析失败',
      title: error?.message || '数据解析失败',
      icon: 'error'
    })
    // 关闭扫描界面
    isScanning.value = false
  }
}
// 打开上传弹窗
const openUploadDialog = (task) => {
@@ -787,10 +708,20 @@
      arr.push(...issueModelValue.value);
    }
    // 传给后端的临时文件ID列表(tempFileIds)
    // 兼容:有些接口可能返回 tempId / tempFileId / id
    let tempFileIds = []
    if (arr !== null && arr.length > 0) {
      tempFileIds = arr
        .map((item) => item?.tempId ?? item?.tempFileId ?? item?.id)
        .filter((v) => v !== undefined && v !== null && v !== '')
    }
    // 提交数据
    infoData.value.storageBlobDTO = arr;
    // 添加异常状态信息
    infoData.value.hasException = hasException.value;
    infoData.value.tempFileIds = tempFileIds;
    const result = await uploadInspectionTask({ ...infoData.value });
    // 检查提交结果
@@ -840,16 +771,6 @@
  }
}
// 摄像头错误处理
const handleCameraError = (error) => {
  cameraError.value = '摄像头访问失败,请检查权限设置'
}
// 扫码事件处理
const handleScanCode = (result) => {
  handleScanSuccess(result)
}
// 查看附件
const viewAttachments = async (task) => {
  try {
@@ -859,32 +780,40 @@
    // 解析新的数据结构
    attachmentList.value = []
    // 生产前附件 (type=0)
    if (task.beforeProduction && Array.isArray(task.beforeProduction)) {
      const beforeFiles = task.beforeProduction.map(file => ({
    // 后端反显字段(你提供的数据结构):
    // - commonFileListBefore:生产前(通常 type=10)
    // - commonFileListAfter:生产中(通常 type=11)
    // - commonFileList:可能是全部/兜底(若包含生产后,一般 type=12)
    const allList = Array.isArray(task?.commonFileList) ? task.commonFileList : []
    const beforeList = Array.isArray(task?.commonFileListBefore)
      ? task.commonFileListBefore
      : allList.filter(f => f?.type === 10)
    const afterList = Array.isArray(task?.commonFileListAfter)
      ? task.commonFileListAfter
      : allList.filter(f => f?.type === 11)
    // 如果后端后续补了 commonFileListIssue,则优先用;否则从 commonFileList 里按 type=12 兜底
    const issueList = Array.isArray(task?.commonFileListIssue)
      ? task.commonFileListIssue
      : allList.filter(f => f?.type === 12)
    const mapToViewFile = (file, viewType) => {
      const u = normalizeFileUrl(file?.url || file?.downloadUrl || '')
      return {
        ...file,
        type: 0 // 确保type为0
      }))
      attachmentList.value.push(...beforeFiles)
        // 用于三标签页分组:0=生产前 1=生产中 2=生产后
        type: viewType,
        name: file?.name || file?.originalFilename || file?.bucketFilename,
        bucketFilename: file?.bucketFilename || file?.name,
        originalFilename: file?.originalFilename || file?.name,
        url: u,
        downloadUrl: u,
        size: file?.size || file?.byteSize,
      }
    }
    // 生产中附件 (type=1)
    if (task.afterProduction && Array.isArray(task.afterProduction)) {
      const afterFiles = task.afterProduction.map(file => ({
        ...file,
        type: 1 // 确保type为1
      }))
      attachmentList.value.push(...afterFiles)
    }
    // 生产后附件 (type=2)
    if (task.productionIssues && Array.isArray(task.productionIssues)) {
      const issueFiles = task.productionIssues.map(file => ({
        ...file,
        type: 2 // 确保type为2
      }))
      attachmentList.value.push(...issueFiles)
    }
    attachmentList.value.push(...beforeList.map(f => mapToViewFile(f, 0)))
    attachmentList.value.push(...afterList.map(f => mapToViewFile(f, 1)))
    attachmentList.value.push(...issueList.map(f => mapToViewFile(f, 2)))
    showAttachmentDialog.value = true
@@ -917,13 +846,13 @@
const getTabType = () => {
  switch (currentUploadType.value) {
    case 'before':
      return 0
      return 10
    case 'after':
      return 1
      return 11
    case 'issue':
      return 2
      return 12
    default:
      return 0
      return 10
  }
}
// 获取当前查看类型的附件
@@ -954,6 +883,41 @@
  const name = file.bucketFilename || file.originalFilename || file.name || ''
  const ext = name.split('.').pop()?.toLowerCase()
  return ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext)
}
// 文件访问基础域(后端要求前缀)
const filePreviewBase = 'http://114.132.189.42:9098'
// 将后端返回的文件地址规范成可访问URL
// 兼容场景:
// - 已经是 http/https:直接返回
// - 以 / 开头:拼接 filePreviewBase
// - Windows 本地路径(如 D:\ruoyi\prod\uploads...\xx.jpg):尝试截取 prod 之后的相对路径并拼接 filePreviewBase
const normalizeFileUrl = (rawUrl) => {
  try {
    if (!rawUrl || typeof rawUrl !== 'string') return ''
    const url = rawUrl.trim()
    if (!url) return ''
    if (/^https?:\/\//i.test(url)) return url
    if (url.startsWith('/')) return `${filePreviewBase}${url}`
    // Windows path -> web path
    if (/^[a-zA-Z]:\\/.test(url)) {
      const normalized = url.replace(/\\/g, '/')
      const idx = normalized.indexOf('/prod/')
      if (idx >= 0) {
        const relative = normalized.slice(idx + '/prod/'.length)
        return `${filePreviewBase}/${relative}`
      }
      // 兜底:无法推断映射规则时,至少把反斜杠变成正斜杠
      return normalized
    }
    // 其他相对路径:直接用 baseUrl 拼一下
    return `${filePreviewBase}/${url.replace(/^\//, '')}`
  } catch (e) {
    return rawUrl || ''
  }
}
// 预览附件
@@ -994,52 +958,80 @@
  })
}
// 使用相机
// 拍照/拍视频(真机优先用 chooseMedia;不支持则降级)
const chooseMedia = (type) => {
  let mediaPamaes = {
    count: 1,
    mediaType: [type || 'image'],
    sizeType: ['compressed', 'original'],
    sourceType: ['camera'],
  if (getCurrentFiles().length >= uploadConfig.limit) {
    uni.showToast({ title: `最多只能选择${uploadConfig.limit}个文件`, icon: 'none' })
    return
  }
  uni.chooseMedia({
    ...mediaPamaes,
    success: (res) => {
      try {
        if (!res.tempFiles || res.tempFiles.length === 0) {
          throw new Error('未获取到图片文件');
  const remaining = uploadConfig.limit - getCurrentFiles().length
  // 优先:chooseMedia(支持 image/video)
  if (typeof uni.chooseMedia === 'function') {
    uni.chooseMedia({
      count: Math.min(remaining, 1),
      mediaType: [type || 'image'],
      sizeType: ['compressed', 'original'],
      sourceType: ['camera'],
      success: (res) => {
        try {
          const files = res?.tempFiles || []
          if (!files.length) throw new Error('未获取到文件')
          files.forEach((tf, idx) => {
            const filePath = tf.tempFilePath || tf.path || ''
            const fileType = tf.fileType || type || 'image'
            const ext = fileType === 'video' ? 'mp4' : 'jpg'
            const file = {
              tempFilePath: filePath,
              path: filePath,
              type: fileType,
              name: `${fileType}_${Date.now()}_${idx}.${ext}`,
              size: tf.size || 0,
              duration: tf.duration || 0,
              createTime: Date.now(),
              uid: Date.now() + Math.random() + idx
            }
            handleBeforeUpload(file)
          })
        } catch (e) {
          console.error('处理拍摄结果失败:', e)
          uni.showToast({ title: '处理文件失败', icon: 'error' })
        }
      },
      fail: (err) => {
        console.error('拍摄失败:', err)
        uni.showToast({ title: '拍摄失败', icon: 'error' })
      }
    })
    return
  }
        const tempFilePath = res.tempFiles[0];
        const tempFile = res.tempFiles && res.tempFiles[0] ? res.tempFiles[0] : {};
        const file = {
          tempFilePath: tempFilePath,
          path: tempFilePath, // 保持兼容性
  // 降级:chooseImage / chooseVideo
  if (type === 'video') {
    chooseVideo()
  } else {
    uni.chooseImage({
      count: 1,
      sizeType: ['compressed', 'original'],
      sourceType: ['camera'],
      success: (res) => {
        const tempFilePath = res?.tempFilePaths?.[0]
        const tempFile = res?.tempFiles?.[0] || {}
        if (!tempFilePath) return
        handleBeforeUpload({
          tempFilePath,
          path: tempFilePath,
          type: 'image',
          name: `photo_${Date.now()}.jpg`,
          size: tempFile.size || 0,
          createTime: new Date().getTime(),
          createTime: Date.now(),
          uid: Date.now() + Math.random()
        };
        handleBeforeUpload(file);
      } catch (error) {
        console.error('处理拍照结果失败:', error);
        uni.showToast({
          title: '处理图片失败',
          icon: 'error'
        });
        })
      }
    },
    fail: (err) => {
      console.error('拍照失败:', err);
      uni.showToast({
        title: '拍照失败: ' + (err.errMsg || '未知错误'),
        icon: 'error'
      });
    }
  })
    })
  }
}
// 拍照
@@ -1193,36 +1185,6 @@
// 上传前校验
const handleBeforeUpload = async (file) => {
  // 检查网络连接
  const hasNetwork = await checkNetworkConnection();
  if (!hasNetwork) {
    uni.showToast({
      title: '网络连接不可用,请检查网络设置',
      icon: 'none'
    });
    return false;
  }
  // 校验文件大小
  if (uploadConfig.fileSize && file.size) {
    const isLt = file.size / 1024 / 1024 < uploadConfig.fileSize;
    if (!isLt) {
      uni.showToast({
        title: `文件大小不能超过 ${uploadConfig.fileSize} MB!`,
        icon: 'none'
      });
      return false;
    }
  }
  // 校验视频时长
  if (file.type === 'video' && file.duration && file.duration > uploadConfig.maxVideoDuration) {
    uni.showToast({
      title: `视频时长不能超过 ${uploadConfig.maxVideoDuration} 秒!`,
      icon: 'none'
    });
    return false;
  }
  // 校验文件类型
  if (uploadConfig.fileType && Array.isArray(uploadConfig.fileType) && uploadConfig.fileType.length > 0) {
@@ -1258,18 +1220,11 @@
  return true;
}
// 文件上传处理
const uploadFile = (file) => {
// 文件上传处理(真机走 uni.uploadFile)
const uploadFile = async (file) => {
  uploading.value = true;
  uploadProgress.value = 0;
  number.value++; // 增加上传计数
  // 确保文件路径正确
  const filePath = file.tempFilePath?.tempFilePath || file.path?.tempFilePath || '';
  if (!filePath) {
    handleUploadError('文件路径不存在');
    return;
  }
  // 确保token存在
  const token = getToken();
@@ -1278,20 +1233,28 @@
    return;
  }
  // 准备上传参数
  const uploadParams = {
  const typeValue = getTabType(); // 生产前:10, 生产中:11, 生产后:12
  uploadWithUniUploadFile(file, file.tempFilePath || file.path || '', typeValue, token);
}
// 使用uni.uploadFile上传(非H5环境或H5回退方案)
const uploadWithUniUploadFile = (file, filePath, typeValue, token) => {
  if (!filePath) {
    handleUploadError('文件路径不存在');
    return;
  }
  const uploadTask = uni.uploadFile({
    url: uploadFileUrl.value,
    filePath: filePath,
    name: 'files',
    name: 'file',
    formData: {
      type: getTabType() || 0
      type: typeValue
    },
    header: {
      'Authorization': `Bearer ${token}`
    }
  };
  const uploadTask = uni.uploadFile({
    ...uploadParams,
    },
    success: (res) => {
      try {
        if (res.statusCode === 200) {
@@ -1348,7 +1311,10 @@
}
// 上传失败处理
const handleUploadError = (message = '上传文件失败', showRetry = true) => {
const handleUploadError = (message = '上传文件失败', showRetry = false) => {
  uploading.value = false;
  uploadProgress.value = 0;
  if (showRetry) {
    uni.showModal({
      title: '上传失败',
@@ -1369,63 +1335,69 @@
// 上传成功回调
const handleUploadSuccess = (res, file) => {
  if (res.code === 200 && res.data && Array.isArray(res.data) && res.data.length > 0) {
    const uploadedFile = res.data[0];
  console.log('上传成功响应:', res);
  // 处理不同的数据结构:可能是数组,也可能是单个对象
  let uploadedFile = null;
  uploadedFile = res.data;
    // 根据当前上传类型设置type字段
    let typeValue = 0; // 默认为生产前
    switch (currentUploadType.value) {
      case 'before':
        typeValue = 0;
        break;
      case 'after':
        typeValue = 1;
        break;
      case 'issue':
        typeValue = 2;
        break;
    }
    // 确保上传的文件数据完整,包含id和type
    const fileData = {
      ...file,
      id: uploadedFile.id, // 添加服务器返回的id
      url: uploadedFile.url || uploadedFile.downloadUrl,
      bucketFilename: uploadedFile.bucketFilename || file.name,
      downloadUrl: uploadedFile.downloadUrl || uploadedFile.url,
      size: uploadedFile.size || file.size,
      createTime: uploadedFile.createTime || new Date().getTime(),
      type: typeValue // 添加类型字段:0=生产前, 1=生产中, 2=生产后
    };
    uploadList.value.push(fileData);
    uploadedSuccessfully();
  } else {
  if (!uploadedFile) {
    console.error('无法解析上传响应数据:', res);
    number.value--; // 上传失败时减少计数
    handleUploadError(res.msg || '上传失败');
    handleUploadError('上传响应数据格式错误', false);
    return;
  }
  // 根据当前上传类型设置type字段
  let typeValue = 0; // 默认为生产前
  switch (currentUploadType.value) {
    case 'before':
      typeValue = 0;
      break;
    case 'after':
      typeValue = 1;
      break;
    case 'issue':
      typeValue = 2;
      break;
  }
  // 确保上传的文件数据完整,包含id和type
  const fileData = {
    ...file,
    id: uploadedFile.id, // 添加服务器返回的id
    tempId: uploadedFile.tempId ?? uploadedFile.tempFileId ?? uploadedFile.id,
    url: uploadedFile.url || uploadedFile.downloadUrl || file.tempFilePath || file.path,
    bucketFilename: uploadedFile.bucketFilename || uploadedFile.originalFilename || file.name,
    downloadUrl: uploadedFile.downloadUrl || uploadedFile.url,
    size: uploadedFile.size || uploadedFile.byteSize || file.size,
    createTime: uploadedFile.createTime || new Date().getTime(),
    type: typeValue // 添加类型字段:0=生产前, 1=生产中, 2=生产后
  };
  uploadList.value.push(fileData);
  // 立即添加到对应的分类,不等待所有文件上传完成
  switch (currentUploadType.value) {
    case 'before':
      beforeModelValue.value.push(fileData);
      break;
    case 'after':
      afterModelValue.value.push(fileData);
      break;
    case 'issue':
      issueModelValue.value.push(fileData);
      break;
  }
  // 重置上传列表(因为已经添加到对应分类了)
  uploadList.value = [];
  number.value = 0;
}
// 上传结束处理
// 上传结束处理(已废弃,现在在handleUploadSuccess中直接处理)
const uploadedSuccessfully = () => {
  if (number.value > 0 && uploadList.value.length === number.value) {
    // 根据当前上传类型,将文件添加到对应的分类
    switch (currentUploadType.value) {
      case 'before':
        beforeModelValue.value = [...beforeModelValue.value, ...uploadList.value];
        break;
      case 'after':
        afterModelValue.value = [...afterModelValue.value, ...uploadList.value];
        break;
      case 'issue':
        issueModelValue.value = [...issueModelValue.value, ...uploadList.value];
        break;
    }
    // 重置状态
    uploadList.value = [];
    number.value = 0;
  }
  // 此函数已不再使用,文件上传成功后立即添加到对应分类
}
// 格式化文件大小
src/pages/login.vue
@@ -6,22 +6,10 @@
        <view class="login-form-content">
            <view class="input-item flex align-center">
                <up-input prefixIcon="account" placeholder="请输入账号" border="bottom"
                                    @blur="getUserLoginFacotryList"
                                    maxlength="30" v-model="loginForm.userName" clearable></up-input>
            </view>
            <view class="input-item flex align-center">
                <up-input prefixIcon="lock" placeholder="请输入密码" border="bottom" maxlength="20" v-model="loginForm.password" clearable type="password"></up-input>
            </view>
            <view class="input-item flex align-center select-container">
                <up-icon name="tags" size="18"></up-icon>
                <up-picker-data
                    v-model="loginForm.factoryId"
                    title="请选择公司"
                    :options="factoryList"
                    valueKey="id"
                    style="width: 100%;"
                    labelKey="name">
                </up-picker-data>
            </view>
            <view>
                <button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button>
@@ -50,23 +38,18 @@
        icon: 'none'
    })
}
import { userLoginFacotryList} from '@/api/login'
import { ref, onMounted } from "vue";
import useUserStore from '@/store/modules/user'
import { getWxCode } from '@/utils/geek';
import { wxLogin } from '@/api/oauth';
import { setToken } from '@/utils/auth';
import View from "@/pages/procurementManagement/procurementLedger/view.vue";
const userStore = useUserStore()
const useWxLogin = ref(false); // 是否使用微信登录
const rememberPassword = ref(false); // 记住密码
const loginForm = ref({
    userName: "",
    password: "",
    factoryId: "",
    currentFatoryName: "",
});
const factoryList = ref([]) // 公司列表
// 保存密码到本地存储
function savePassword() {
@@ -109,30 +92,6 @@
    })
}
function getUserLoginFacotryList() {
    if(loginForm.value.userName){
        userLoginFacotryList({userName:loginForm.value.userName}).then(res => {
            console.log('res',res)
            // 检查res.data是否为数组
            if (res.data && Array.isArray(res.data)) {
                // 重新组装数据格式:deptId变成id,deptName变成name
                factoryList.value = res.data.map(item => ({
                    id: item.deptId,
                    name: item.deptName
                }))
            } else {
                // 如果res.data不是数组,设置为空数组
                factoryList.value = []
            }
        }).catch(error => {
            showToast('获取公司列表失败:', error)
            factoryList.value = []
        })
    }else {
        factoryList.value = []
    }
}
async function handleLogin() {
    if (loginForm.value.userName === "") {
        showToast("请输入您的账号")
@@ -166,7 +125,6 @@
// 页面加载时检查是否有保存的密码
onMounted(() => {
    loadPassword();
    getUserLoginFacotryList()
});
</script>
@@ -219,26 +177,6 @@
                line-height: 20px;
                text-align: left;
                padding-left: 15px;
            }
        }
        .select-container {
            flex: 1;
            border-bottom: 1px solid #e5e5e5;
            padding: 6px 9px;
            :deep(.up-select) {
                border: none;
                background: transparent;
                .up-select__label {
                    font-size: 14px;
                    color: #333;
                }
                .up-select__value {
                    font-size: 14px;
                    color: #333;
                }
            }
        }
        
src/utils/request.ts
@@ -22,12 +22,12 @@
    config.url = url
  }
  // 记录请求参数
  // console.log('请求发送参数:', {
  //   url: (config.baseUrl || baseUrl) + config.url,
  //   method: config.method || 'GET',
  //   headers: config.header,
  //   data: config.data
  // })
  console.log('请求发送参数:', {
    url: (config.baseUrl || baseUrl) + config.url,
    method: config.method || 'GET',
    headers: config.header,
    data: config.data
  })
  return new Promise((resolve, reject) => {
    uni.request({
      method: config.method || 'GET',