xwt
2025-08-21 bfffb0306f8e37ea39624499c55306837bf23ecf
优化
已修改5个文件
2119 ■■■■ 文件已修改
manifest.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/QC/LLJ/List.vue 56 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/QC/RKJ/Add.vue 1225 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/QC/RKJ/List.vue 832 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
store/index.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
manifest.json
@@ -2,7 +2,7 @@
    "name" : "GS-MES-AP",
    "appid" : "__UNI__F08FAE3",
    "description" : "",
    "versionName" : "1.1.3.1",
    "versionName" : "1.1.3.3",
    "versionCode" : 1,
    "transformPx" : false,
    /* 5+App特有相关 */
pages/QC/LLJ/List.vue
@@ -16,7 +16,7 @@
                </view>
                <!-- 新增搜索框和按钮 -->
                <view class="search-container">
                    <input class="search-input" v-model="searchValue" :placeholder="'请输入'+options[optionsIndex]"
                    <input class="search-input" v-model="searchValue" :placeholder="optionsIndex === 0 ? '选择项目时显示全部数据' : '请输入'+options[optionsIndex]"
                        @confirm="handleSearch" />
                    <button class="search-button" @click="handleSearch">搜索</button>
                </view>
@@ -42,7 +42,10 @@
                    <text class="badge normal" v-if="item.first == 1">首次来料</text>
                    <text class="card-title">检验单号: {{item.releaseNo}}</text>
                    <view v-if="item.userName == null && item.activeTab == 0">
                            <text class="status pending" :class="{ 'emergency-pending': item.lotNo1 === '紧急放行,请勿验退!' }">
                            <text class="status pending" :class="{
                                'emergency-pending': item.lotNo1 === '紧急放行,请勿验退!',
                                'unmaintained-not-emergency': isUnmaintainedAndNotEmergency(item)
                            }">
                            {{ item.lotNo1 === '紧急放行,请勿验退!' ? '紧急放行/' + getStatusText(item) : getStatusText(item) }}
                            </text>
                            
@@ -65,7 +68,7 @@
                    <view class="info-row">
                        <view class="info-item">
                            <text class="info-label">项目</text>
                            <text class="info-content">{{item.projectCodes}}</text>
                            <text class="info-content">{{item.DEPARTMENTNAME}}</text>
                        </view>
                        <view class="info-item" v-if="item.extendNo1!=null">
                            <text class="info-label">技改状态</text>
@@ -126,7 +129,7 @@
                pageIndex: 1,
                limit: 20,
                IQCJL: 0,
                options: ['项目', '物料编号', '物料名称', '供应商', '采购员','到货单号','检验单号','物料规格'],
                options: ['项目', '物料编号', '物料名称', '供应商', '到货单号','检验单号','物料规格'],
                searchValue: ''
            }
@@ -139,21 +142,28 @@
            //搜索
            onOptionsChange(e) {
                this.optionsIndex = e.detail.value;
                // 当选择项目时,清空搜索值
                if (this.optionsIndex === 0) {
                    this.searchValue = '';
                }
                // 根据选择的选项设置搜索字段
                const fieldMap = {
                    0: 'projectCodes', // 项目
                    0: 'DEPARTMENTNAME', // 项目
                    1: 'itemNo', // 物料编号
                    2: 'itemName', // 物料名称
                    3: 'suppName', // 供应商
                    4: 'purchaser' ,// 采购员
                    5: 'lotNO' ,// 到货单号
                    6: 'releaseNO' ,// 检验单号
                    7: 'ItemModel',//物料规格
                    4: 'lotNo' ,// 到货单号
                    5: 'releaseNo' ,// 检验单号
                    6: 'itemModel',//物料规格
                };
                this.selectedField = fieldMap[this.optionsIndex];
            },
            //搜索按钮点击事件
            handleSearch() {
                // 当选择项目时,清空搜索值
                if (this.optionsIndex === 0) {
                    this.searchValue = '';
                }
                this.init();
            },
            init() {
@@ -349,6 +359,23 @@
                }
                // 如果有检验项目,显示"待检验"
                return '待检验';
            },
            // 判断是否为未维护且非紧急放行
            isUnmaintainedAndNotEmergency(item) {
                // 检查是否为未维护(没有检验项目)
                const isUnmaintained = !item.inspectionItemCount || item.inspectionItemCount === 0;
                // 检查是否为非紧急放行
                const isNotEmergency = item.lotNo1 !== '紧急放行,请勿验退!';
                return isUnmaintained && isNotEmergency;
            },
            // 判断是否为待分配状态(物料没有维护检验员或者该物料未在V_LLJ_USER里)
            isUnmaintainedItem(item) {
                // 检查物料是否在V_LLJ_USER视图中(即是否有维护检验员)
                // 如果item.fcode为null或undefined,说明该物料未在V_LLJ_USER视图中
                // 或者该物料没有维护检验员
                return !item.fcode || item.fcode === null || item.fcode === '';
            }
        }
    }
@@ -513,6 +540,11 @@
        color: white;
    }
    .badge.unmaintained {
        background-color: #1e8449;
        color: white;
    }
    .badge.emergency {
        background-color: #ff4d4f;
        color: white;
@@ -663,5 +695,11 @@
        background-color: #ff0000; /* 红色背景 */
        color: white;
    }
    .status.unmaintained-not-emergency {
        background-color: #9c27b0; /* 紫色背景 */
        color: white;
        box-shadow: 0 0 8px rgba(156, 39, 176, 0.3);
    }
    }
</style>
pages/QC/RKJ/Add.vue
@@ -50,7 +50,7 @@
                    <input type="number" 
                           v-model="formData.quantity" 
                           placeholder="请输入送检数量"
                           class="form-input"
                           class="large-quantity-input"
                           @input="onQuantityChange"/>
                </view>
            </view>
@@ -76,6 +76,11 @@
                <view class="form-row" v-if="formData.planQty">
                    <label class="form-label">工单数量:</label>
                    <span class="form-value">{{formData.planQty}}</span>
                </view>
                <view class="form-row" v-if="formData.rbillNo">
                    <label class="form-label">送检批次:</label>
                    <span class="form-value">{{formData.rbillNo}}</span>
                </view>
            </view>
        </view>
@@ -158,9 +163,42 @@
            <view class="info-value">{{formData.billNo || formData.rBillNo}}</view>
        </view>
        <view class="dropdown-row">
            <view class="info-label">送检批次:</view>
            <view class="info-value">{{formData.rbillNo}}</view>
        </view>
        <!-- 新增下拉框区域 -->
        <view class="dropdown-row">
            <view class="info-label">不良原因:</view>
            <picker v-if="!isUpdate" :value="badreasonIndex" :range="badreasonOptions" @change="onBadreasonChange">
                <view class="picker-text" :class="{ 'selected': badreason }">{{ badreason || '请选择不良原因' }}</view>
            </picker>
            <view v-else class="info-value">{{ badreason }}</view>
        </view>
        <view class="dropdown-row">
            <view class="info-label">所属车间:</view>
            <picker v-if="!isUpdate" :value="workshopIndex" :range="workshopOptions" @change="onWorkshopChange">
                <view class="picker-text" :class="{ 'selected': WORKSHOP }">{{ WORKSHOP || '请选择所属车间' }}</view>
            </picker>
            <view v-else class="info-value">{{ WORKSHOP }}</view>
        </view>
        <view class="dropdown-row">
            <view class="info-label">评审状态:</view>
            <picker v-if="!isUpdate" :value="pstypeIndex" :range="pstypeOptions" @change="onPstypeChange">
                <view class="picker-text" :class="{ 'selected': PSTYPE }">{{ PSTYPE || '请选择评审状态' }}</view>
            </picker>
            <view v-else class="info-value">{{ PSTYPE }}</view>
        </view>
        <view class="dropdown-row">
            <view class="info-label">不良描述:</view>
            <input v-if="!isUpdate" v-model="formData.fngDesc" placeholder="请输入不良描述" class="input-field" @blur="saveFngDesc" />
            <view v-else class="info-value">{{ formData.fngDesc }}</view>
        </view>
        <!-- 表单上方操作按钮区 -->
        <view class="top-action-buttons">
            <button class="action-btn" v-if="tableData.length === 0 && formData.fsubmit != 1" @click="getInspectionItems">获取检验项目</button>
            <button class="action-btn" v-if="formData.fsubmit != 1" @click="getInspectionItems">获取检验项目</button>
        </view>
        <!-- 检验项目表格 -->
@@ -203,8 +241,8 @@
        <!-- 表单下方操作按钮区 -->
        <view class="bottom-action-buttons">
            <button class="action-btn small" @click="toImage">上传/查看图片</button>
            <button class="action-btn small" @click="viewAttachmentInfo">查看附件信息</button>
            <button class="action-btn small" @click="saveRemarks" v-if="formData.fsubmit != 1">添加不合格描述</button>
            <button class="action-btn small" @click="cleanResult" v-if="formData.fsubmit != 1">清除检验结果</button>
            <button class="action-btn small primary" @click="submitInspection" v-if="formData.fsubmit != 1 && tableData.length > 0">提交检验</button>
        </view>
@@ -220,6 +258,106 @@
                </form>
                <button class="updateBut" @click="edit">修改</button>
                <button @click="showPopup = !showPopup">取消</button>
            </view>
        </view>
        <!-- 附件详情弹窗 -->
        <view v-if="showAttachmentDetail" class="overlay">
            <view class="popup attachment-detail-popup">
                <h3 class="attachment-popup-title">附件详情</h3>
                <div class="attachment-popup-divider"></div>
                <div v-if="selectedAttachment" class="attachment-detail-content">
                    <div class="attachment-detail-row"><span class="attachment-label">ID:</span><span>{{ Math.trunc(selectedAttachment.id) }}</span></div>
                    <div class="attachment-detail-row"><span class="attachment-label">附件名:</span><span>{{ selectedAttachment.fattach }}</span></div>
                    <div class="attachment-detail-row"><span class="attachment-label">类型:</span><span>{{ selectedAttachment.ftype }}</span></div>
                    <div class="attachment-detail-row"><span class="attachment-label">版本:</span><span>{{ selectedAttachment.fversion }}</span></div>
                    <div class="attachment-detail-row"><span class="attachment-label">受控日期:</span><span>{{ selectedAttachment.fdate }}</span></div>
                    <div class="attachment-detail-row"><span class="attachment-label">上传人:</span><span>{{ selectedAttachment.createBy }}</span></div>
                    <div class="attachment-detail-row"><span class="attachment-label">上传时间:</span><span>{{ selectedAttachment.createDate }}</span></div>
                    <div class="attachment-actions-detail">
                        <button class="attachment-action-btn preview-btn"
                            @click="previewFtpFile(selectedAttachment)"
                            v-if="isPreviewable(selectedAttachment.fattach)">
                            🔍 在线预览
                        </button>
                        <button class="attachment-action-btn download-btn"
                            @click="downloadAttachment(selectedAttachment)">
                            📥 下载文件
                        </button>
                    </div>
                </div>
                <div v-else class="attachment-detail-empty">暂无附件信息</div>
                <button class="attachment-popup-close" @click="closeAttachmentDetail">返回附件列表</button>
            </view>
        </view>
        <!-- 附件列表弹窗 -->
        <view v-if="showAttachmentPopup" class="overlay">
            <view class="popup" style="width: 60vw; max-width: 500px;">
                <h3>附件列表</h3>
                <div v-if="attachmentsLoading">加载中...</div>
                <div v-else-if="attachments.length === 0">暂无附件</div>
                <ul class="attachment-list" v-else>
                    <li v-for="item in attachments" :key="item.id">
                        <div class="attachment-info">
                            <span class="attachment-name" @click="showAttachmentDetailDialog(item)">
                                {{ item.fattach }}
                            </span>
                            <div class="attachment-meta">
                                <span class="attachment-type">{{ item.ftype || '未知类型' }}</span>
                            </div>
                        </div>
                        <div class="attachment-actions">
                            <button class="secondary-btn" @click="showAttachmentDetailDialog(item)">详情</button>
                            <button class="secondary-btn preview-btn" @click="previewFtpFile(item)"
                                v-if="isPreviewable(item.fattach)">预览</button>
                            <button class="secondary-btn" @click="downloadAttachment(item)">下载</button>
                        </div>
                    </li>
                </ul>
                <button class="attachment-popup-close" @click="closeAttachmentPopup">关闭</button>
            </view>
        </view>
        <!-- 文件预览弹窗 -->
        <view v-if="showFilePreviewPopup" class="overlay">
            <view class="popup file-preview-popup">
                <h3 class="file-preview-title">{{ previewTitle }}</h3>
                <div class="file-preview-divider"></div>
                <div class="file-preview-content">
                    <!-- 文本内容预览 -->
                    <pre v-if="previewType === 'text'">{{ previewContent }}</pre>
                    <!-- 图片内容预览 -->
                    <view v-else-if="previewType === 'image'" class="image-preview-container">
                        <image
                            :src="previewContent"
                            mode="aspectFit"
                            class="preview-image-clickable"
                            @click="previewImageInPopup"
                            style="width: 100%; max-height: 400px; cursor: pointer;"
                        />
                        <div class="image-zoom-hint">点击图片可放大查看</div>
                    </view>
                    <!-- Excel 等 Office 文件提示 -->
                    <view v-else-if="previewType === 'excel'" class="unsupported-preview">
                        <view class="unsupported-icon">📊</view>
                        <view class="unsupported-text">Excel 文件暂不支持在线预览</view>
                        <view class="unsupported-hint">请点击下载按钮获取完整文件</view>
                    </view>
                    <!-- 不支持的文件类型 -->
                    <view v-else class="unsupported-preview">
                        <view class="unsupported-icon">📄</view>
                        <view class="unsupported-text">此文件格式暂不支持预览</view>
                        <view class="unsupported-hint">请点击下载按钮获取完整文件</view>
                    </view>
                </div>
                <div class="file-preview-actions">
                    <button v-if="previewType !== 'text'" class="file-preview-btn download-btn" @click="downloadPreviewFile">📥 下载文件</button>
                    <button class="file-preview-btn close-btn" @click="closeFilePreview">关闭</button>
                </div>
            </view>
        </view>
    </view>
@@ -249,7 +387,8 @@
             workShop: "", // 工作车间
             lineName: "", // 线体名称
             quantity: "", // 送检数量
             planQty: "" // 工单计划数量
             planQty: "", // 工单计划数量
             fngDesc: "" // 不良描述
           },
    
          DAA020List: [],
@@ -275,6 +414,28 @@
          showPopup: false,
          departmentList: [], // 车间列表
          selectedDepartmentId: "", // 选中的车间ID
          // 附件相关数据
          attachments: [],
          showAttachmentPopup: false,
          attachmentsLoading: false,
          selectedAttachment: null,
          showAttachmentDetail: false,
          showFilePreviewPopup: false,
          previewContent: '',
          previewTitle: '',
          previewItemNo: '',
          previewType: '', // 'text', 'image', 'excel', 'unsupported'
          // 新增下拉框相关数据
          badreason: '',
          PSTYPE: '',
          WORKSHOP: '',
          badreasonOptions: ['', '外观不良', '尺寸不良', '包装不良', '性能不良', '装配不良', '安规不良'],
          badreasonIndex: 0,
          workshopOptions: ['', '生产一部', '生产二部', '注塑车间', '其他'],
          workshopIndex: 0,
          pstypeOptions: ['', '特采/让步使用', '挑选/返工使用', '退货', '待判'],
          pstypeIndex: 0,
        };
      },
      onLoad(options) {
@@ -451,6 +612,7 @@
            this.formData.itemId = ""; // 清空物料ID
            this.formData.itemModel = "";
            this.formData.planQty = "";
            this.formData.rbillNo = ""; // 清空送检批次号
            this.tableData = [];
          })
        },
@@ -488,6 +650,7 @@
                this.formData.itemId = ""; // 清空物料ID
                this.formData.itemModel = "";
                this.formData.planQty = "";
                this.formData.rbillNo = ""; // 清空送检批次号
                this.tableData = [];
              });
            }
@@ -504,19 +667,21 @@
            this.formData.itemNo = "";
            this.formData.itemId = "";
            this.formData.billNo = "";
            this.formData.rbillNo = "";
            this.formData.quantity = "";
            this.formData.itemModel = "";
            this.tableData = [];
            return;
          }
          
          // 自动填充物料信息
          this.formData.itemName = data.daa003; // 产品名称
          this.formData.itemNo = data.daa002; // 产品编码
          this.formData.itemId = data.itemId || ""; // 物料ID
          this.formData.billNo = data.daa001; // 工单号作为billNo
          this.formData.itemModel = data.daa004 || ""; // 产品规格
          this.formData.planQty = data.daa008 || ""; // 工单数量
                     // 自动填充物料信息
           this.formData.itemName = data.daa003; // 产品名称
           this.formData.itemNo = data.daa002; // 产品编码
           this.formData.itemId = data.itemId || ""; // 物料ID
           this.formData.billNo = data.daa001; // 工单号作为billNo
           this.formData.rbillNo = "无源单"; // 送检批次号(使用默认值)
           this.formData.itemModel = data.daa004 || ""; // 产品规格
           this.formData.planQty = data.daa008 || ""; // 工单数量
          // 不清空送检数量,保持用户已输入的值
          this.tableData = [];
        },
@@ -591,6 +756,17 @@
              this.formData.itemName = data.itemName;
              this.formData.itemModel = data.itemModel;
              this.formData.daa015 = data.daa015;
              // 加载下拉框字段数据
              this.badreason = data.blyy || '';
              this.PSTYPE = data.pszt || '';
              this.WORKSHOP = data.sscj || '';
              this.formData.fngDesc = data.fngDesc || ''; // 加载不良描述
              // 设置 picker 索引
              this.badreasonIndex = this.badreasonOptions.indexOf(this.badreason);
              this.workshopIndex = this.workshopOptions.indexOf(this.WORKSHOP);
              this.pstypeIndex = this.pstypeOptions.indexOf(this.PSTYPE);
              
              // 设置生产线名称(如果有lineNo)
              if (this.formData.lineNo && this.formData.workShop) {
@@ -870,10 +1046,21 @@
                 return;
             }
             
             // 检查钉钉推送条件:PSZT为待判,且不良原因、不良描述、所属车间不为空
             const shouldPushToDingTalk = this.PSTYPE === '待判' &&
                                         this.badreason &&
                                         this.formData.fngDesc &&
                                         this.WORKSHOP;
             let confirmMessage = '确定要提交此检验单吗?提交后将无法修改。';
             if (shouldPushToDingTalk) {
                 confirmMessage += '\n\n满足钉钉推送条件,将自动推送到钉钉审批流程。';
             }
             // 确认提交
             uni.showModal({
                 title: '确认提交',
                 content: '确定要提交此检验单吗?提交后将无法修改。',
                 content: confirmMessage,
                 success: (res) => {
                     if (res.confirm) {
                         this.$post({
@@ -884,7 +1071,11 @@
                             }
                         }).then(res => {
                             if (res.status == 0) {
                                 this.$showMessage("检验单提交成功!");
                                 let successMessage = "检验单提交成功!";
                                 if (shouldPushToDingTalk) {
                                     successMessage += "\n已推送到钉钉审批流程。";
                                 }
                                 this.$showMessage(successMessage);
                                 // 更新本地状态
                                 this.formData.fsubmit = 1;
                                 // 刷新数据
@@ -899,7 +1090,523 @@
                     }
                 }
             });
         }
         },
         viewAttachmentInfo() {
            this.showAttachmentPopup = true; // 先弹窗
            this.attachmentsLoading = true;
            this.attachments = [];
            this.$post({
              url: "/RKJ/getAttachments",
              data: { itemNo: this.formData.itemNo }
            }).then(res => {
              this.attachmentsLoading = false;
              if (res.status === 0) {
                this.attachments = res.data.tbBillList;
                // 为每个附件设置默认可用状态
                this.attachments.forEach((item, index) => {
                  this.$set(item, 'ftpAvailable', true); // 默认认为文件可用
                  this.$set(item, 'checking', false);
                });
              } else if (res.status === 1 && res.message === "该检验单未上传附件信息!") {
                uni.showToast({ title: res.message, icon: "none" });
              } else {
                uni.showToast({ title: "获取附件失败", icon: "none" });
              }
            });
          },
        downloadAttachment(item) {
          // 去除所有空格、全角空格、回车、换行
          const fileName = item.fattach.replace(/[\s\u3000\r\n]+/g, '').trim();
          // 使用配置的服务器地址和FTP服务器地址
          const url = this.$store.state.serverInfo.serverAPI + "/RKJ/DownloadFtpFile?itemNo=" + encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) + "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer);
          // 检查运行环境
          // #ifdef H5
          // H5环境:使用浏览器下载
          this.downloadFileInBrowser(url, fileName);
          // #endif
          // #ifdef APP-PLUS
          // APP环境:使用uni.downloadFile
          this.downloadFileInApp(url, fileName);
          // #endif
          // #ifdef MP
          // 小程序环境:使用uni.downloadFile
          this.downloadFileInApp(url, fileName);
          // #endif
        },
        // 在浏览器中下载文件
        downloadFileInBrowser(url, fileName) {
          uni.showLoading({ title: '正在准备下载...' });
          // 方法1:创建隐藏的a标签下载
          try {
            const link = document.createElement('a');
            link.href = url;
            link.download = fileName;
            link.style.display = 'none';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            uni.hideLoading();
            uni.showToast({
              title: '下载已开始',
              icon: 'success',
              duration: 2000
            });
          } catch (error) {
            console.log('a标签下载失败,尝试window.open方式:', error);
            // 方法2:使用window.open
            try {
              window.open(url, '_blank');
              uni.hideLoading();
              uni.showToast({
                title: '下载已开始',
                icon: 'success',
                duration: 2000
              });
            } catch (error2) {
              console.log('window.open下载失败,尝试fetch方式:', error2);
              // 方法3:使用fetch下载
              this.downloadFileWithFetch(url, fileName);
            }
          }
        },
        // 使用fetch下载文件
        downloadFileWithFetch(url, fileName) {
          fetch(url)
            .then(response => {
              if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
              }
              return response.blob();
            })
            .then(blob => {
              // 创建blob URL
              const blobUrl = window.URL.createObjectURL(blob);
              // 创建下载链接
              const link = document.createElement('a');
              link.href = blobUrl;
              link.download = fileName;
              link.style.display = 'none';
              document.body.appendChild(link);
              link.click();
              document.body.removeChild(link);
              // 释放blob URL
              window.URL.revokeObjectURL(blobUrl);
              uni.hideLoading();
              uni.showToast({
                title: '下载成功',
                icon: 'success',
                duration: 2000
              });
            })
            .catch(error => {
              console.error('Fetch下载失败:', error);
              uni.hideLoading();
              if (error.message.includes('404')) {
                uni.showModal({
                  title: '文件不存在',
                  content: `该附件在FTP服务器上不存在`,
                  showCancel: false
                });
              } else {
                uni.showModal({
                  title: '下载失败',
                  content: `下载失败: ${error.message}`,
                  showCancel: false
                });
              }
            });
        },
        // 在APP中下载文件
        downloadFileInApp(url, fileName) {
          // #ifdef APP-PLUS
          uni.showLoading({ title: '从FTP服务器下载中...' });
          // Android 获取存储路径
          const saveDir = plus.os.name === 'Android' ? plus.io.convertLocalFileSystemURL('_downloads/') : plus.io.convertLocalFileSystemURL('_documents/');
          const filePath = `${saveDir}${fileName}`;
          const downloadTask = uni.downloadFile({
            url: url,
            filePath: filePath, // 指定保存路径
            success: (res) => {
              uni.hideLoading();
              if (res.statusCode === 200) {
                const fileInfo = {
                  name: fileName,
                  path: res.filePath || filePath,
                  tempPath: res.tempFilePath
                };
                uni.showModal({
                  title: '下载成功',
                  content: `文件已保存到:${fileInfo.path}`,
                  showCancel: true,
                  confirmText: '打开文件',
                  cancelText: '确定',
                  success: (modalRes) => {
                    if (modalRes.confirm) {
                      // 用户选择打开文件
                      this.openFileInApp(fileInfo);
                    }
                  }
                });
              } else if (res.statusCode === 404) {
                uni.showModal({
                  title: '文件不存在',
                  content: `该附件在FTP服务器上不存在`,
                  showCancel: false
                });
              } else {
                uni.showModal({
                  title: '下载失败',
                  content: `状态码:${res.statusCode}`,
                  showCancel: false
                });
              }
            },
            fail: (error) => {
              uni.hideLoading();
              console.error('下载失败:', error);
              uni.showModal({
                title: '下载失败',
                content: `网络错误:${error.errMsg}`,
                showCancel: false
              });
            }
          });
          // 监听下载进度
          downloadTask.onProgressUpdate((res) => {
            const progress = Math.round(res.progress);
            uni.showLoading({
              title: `下载中 ${progress}%`,
              mask: true
            });
          });
          // #endif
          // #ifdef MP
          // 小程序环境的简化实现
          uni.showLoading({ title: '下载中...' });
          uni.downloadFile({
            url: url,
            success: (res) => {
              uni.hideLoading();
              if (res.statusCode === 200) {
                uni.showToast({ title: '下载完成', icon: 'success' });
              }
            },
            fail: (error) => {
              uni.hideLoading();
              uni.showModal({ title: '下载失败', content: error.errMsg, showCancel: false });
            }
          });
          // #endif
        },
        // APP中打开文件
        openFileInApp(fileInfo) {
          // #ifdef APP-PLUS
          if (typeof plus !== 'undefined') {
            const filePath = fileInfo.path || fileInfo.tempPath;
            // 尝试打开文件
            plus.runtime.openFile(filePath, {}, (error) => {
              console.error('打开文件失败:', error);
              uni.showModal({
                title: '无法打开',
                content: '系统中没有找到能打开此文件的应用程序',
                showCancel: false
              });
            });
          }
          // #endif
        },
        // 预览FTP文件
        previewFtpFile(item) {
          const fileName = item.fattach.replace(/[\s\u3000\r\n]+/g, '').trim();
          const fileExt = fileName.split('.').pop().toLowerCase();
          // 检查文件类型是否支持预览
          if (!this.isPreviewable(fileName)) {
            uni.showModal({
              title: '不支持预览',
              content: '该文件类型不支持在线预览,请下载后查看',
              showCancel: false
            });
            return;
          }
          const previewUrl = this.$store.state.serverInfo.serverAPI + "/RKJ/PreviewFtpFile?itemNo=" + encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) + "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer);
          // 根据文件类型进行不同的预览处理
          if (['pdf'].includes(fileExt)) {
            this.previewPdfFile(previewUrl, fileName);
          } else if (['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(fileExt)) {
            this.previewImageFile(previewUrl, fileName);
          } else if (['txt'].includes(fileExt)) {
            this.previewTextFile(previewUrl, fileName);
          } else if (['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'].includes(fileExt)) {
            this.previewOfficeFile(previewUrl, fileName);
          } else {
            // 尝试通用预览
            this.previewGenericFile(previewUrl, fileName);
          }
        },
        // 预览PDF文件
        previewPdfFile(url, fileName) {
          // 先下载PDF文件,转为base64后预览
          uni.request({
            url: url,
            method: 'GET',
            responseType: 'arraybuffer',
            success: (res) => {
              if (res.statusCode === 200) {
                const base64Data = uni.arrayBufferToBase64(res.data);
                // 存储到全局变量
                getApp().globalData.tempPDF = base64Data;
                uni.navigateTo({
                  url: `/pages/fileView/pdfView`
                });
              } else {
                this.handlePreviewError(res.statusCode, fileName);
              }
            },
            fail: (error) => {
              this.handlePreviewError(0, fileName, error.errMsg);
            }
          });
        },
        // 预览图片文件
        previewImageFile(url, fileName) {
          // #ifdef APP-PLUS
          // APP环境:先下载到本地再预览,避免网络图片加载问题
          uni.showLoading({ title: '加载图片...' });
          uni.downloadFile({
            url: url,
            success: (res) => {
              uni.hideLoading();
              if (res.statusCode === 200) {
                // 使用本地临时路径
                uni.navigateTo({
                  url: `/pages/fileView/imageView?url=${encodeURIComponent(res.tempFilePath)}`
                });
              } else {
                this.handlePreviewError(res.statusCode, fileName);
              }
            },
            fail: (error) => {
              uni.hideLoading();
              this.handlePreviewError(0, fileName, error.errMsg);
            }
          });
          // #endif
          // #ifdef H5 || MP
          // H5和小程序:直接使用网络URL
          uni.navigateTo({
            url: `/pages/fileView/imageView?url=${encodeURIComponent(url)}`
          });
          // #endif
        },
        // 预览文本文件
        previewTextFile(url, fileName) {
          // 文本文件直接显示在弹窗中
          uni.showLoading({ title: '加载文件内容...' });
          uni.request({
            url: url,
            method: 'GET',
            success: (res) => {
              uni.hideLoading();
              if (res.statusCode === 200) {
                const fileType = this.getFileType(fileName);
                if (fileType === 'text') {
                  // 文本文件:显示内容
                  this.showFilePreview(res.data, fileName);
                } else if (fileType === 'image') {
                  // 图片文件:显示图片URL
                  this.showFilePreview(url, fileName);
                } else {
                  // 其他文件类型:显示提示信息
                  this.showFilePreview('', fileName);
                }
              } else {
                this.handlePreviewError(res.statusCode, fileName);
              }
            },
            fail: (error) => {
              uni.hideLoading();
              this.handlePreviewError(0, fileName, error.errMsg);
            }
          });
        },
        // 检测文件类型
        getFileType(fileName) {
          const fileExt = fileName.split('.').pop().toLowerCase();
          if (['txt', 'log', 'md', 'csv', 'json', 'xml'].includes(fileExt)) {
            return 'text';
          } else if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(fileExt)) {
            return 'image';
          } else if (['xls', 'xlsx', 'doc', 'docx', 'ppt', 'pptx'].includes(fileExt)) {
            return 'excel';
          } else {
            return 'unsupported';
          }
        },
        // 显示文件预览弹窗
        showFilePreview(content, fileName) {
          this.previewContent = content;
          this.previewTitle = fileName;
          this.previewItemNo = this.selectedAttachment?.itemNo || '';
          this.previewType = this.getFileType(fileName);
          this.showFilePreviewPopup = true;
        },
        // 关闭文件预览弹窗
        closeFilePreview() {
          this.showFilePreviewPopup = false;
          this.previewContent = '';
          this.previewTitle = '';
          this.previewItemNo = '';
          this.previewType = '';
        },
        // 下载预览文件
        downloadPreviewFile() {
          const item = { fattach: this.previewTitle, itemNo: this.previewItemNo };
          this.downloadAttachment(item);
          this.closeFilePreview();
        },
        // 在弹窗中预览图片(放大功能)
        previewImageInPopup() {
          // 使用uni.previewImage API实现图片放大预览
          uni.previewImage({
            current: this.previewContent, // 当前显示图片的链接
            urls: [this.previewContent], // 需要预览的图片链接列表
            loop: false, // 是否开启图片轮播
            indicator: 'default', // 图片指示器类型
            longPressActions: {
              itemList: ['发送给朋友', '保存图片', '收藏'],
              success: function (data) {
                console.log('选中了第' + (data.tapIndex + 1) + '个按钮');
              },
              fail: function (err) {
                console.log(err.errMsg);
              }
            },
            success: () => {
              console.log('图片预览成功');
            },
            fail: (err) => {
              console.error('图片预览失败:', err);
              uni.showToast({
                title: '图片预览失败',
                icon: 'none'
              });
            }
          });
        },
        // 预览Office文件
        previewOfficeFile(url, fileName) {
          // 先检查Excel文件,使用专门的Excel预览页面
          const fileExt = fileName.split('.').pop().toLowerCase();
          if (['xls', 'xlsx'].includes(fileExt)) {
            // Excel文件预览
            uni.request({
              url: url,
              method: 'GET',
              responseType: 'arraybuffer',
              success: (res) => {
                if (res.statusCode === 200) {
                  const base64Data = uni.arrayBufferToBase64(res.data);
                  // 存储 Base64 数据到本地存储
                  uni.setStorageSync('excelBase64Data', base64Data);
                  uni.navigateTo({
                    url: `/pages/fileView/excelView`
                  });
                } else {
                  this.handlePreviewError(res.statusCode, fileName);
                }
              },
              fail: (error) => {
                this.handlePreviewError(0, fileName, error.errMsg);
              }
            });
          } else if (['doc', 'docx'].includes(fileExt)) {
            // Word文件,尝试使用Word预览页面或者微软在线预览
            try {
              const officePreviewUrl = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}`;
              // 如果有webView页面,使用webView预览
              this.previewGenericFile(officePreviewUrl, fileName);
            } catch (error) {
              this.handlePreviewError(0, fileName, '不支持此Office文件类型的预览');
            }
          } else {
            // 其他Office文件,使用微软在线预览服务
            const officePreviewUrl = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}`;
            this.previewGenericFile(officePreviewUrl, fileName);
          }
        },
        // 通用文件预览
        previewGenericFile(url, fileName) {
          // 由于没有通用的webView页面,显示提示并提供下载
          uni.showModal({
            title: '文件预览',
            content: `文件 "${fileName}" 需要下载后查看,是否立即下载?`,
            showCancel: true,
            confirmText: '下载',
            cancelText: '取消',
            success: (res) => {
              if (res.confirm) {
                const item = { fattach: fileName, itemNo: this.selectedAttachment.itemNo };
                this.downloadAttachment(item);
              }
            }
          });
        },
        // 处理预览错误
        handlePreviewError(statusCode, fileName, errorMsg = '') {
          let message = '';
          if (statusCode === 404) {
            message = `文件 ${fileName} 在FTP服务器上不存在`;
          } else if (statusCode === 0) {
            message = `预览失败:${errorMsg}`;
          } else {
            message = `预览失败,状态码:${statusCode}`;
          }
          uni.showModal({
            title: '预览失败',
            content: message,
            showCancel: true,
            confirmText: '下载',
            cancelText: '取消',
            success: (res) => {
              if (res.confirm) {
                // 用户选择下载文件
                const item = { fattach: fileName, itemNo: this.selectedAttachment.itemNo };
                this.downloadAttachment(item);
              }
            }
          });
      },
      onShow() {
        //每次进入页面都会执行的方法
@@ -907,6 +1614,104 @@
          this.init();
        }
      },
        // 附件相关方法
        closeAttachmentPopup() {
          this.showAttachmentPopup = false;
        },
        showAttachmentDetailDialog(item) {
          console.log('查看详情', item);
          this.selectedAttachment = item;
          this.showAttachmentPopup = false;
          this.showAttachmentDetail = true;
          console.log('showAttachmentDetail:', this.showAttachmentDetail);
        },
        closeAttachmentDetail() {
          this.showAttachmentDetail = false;
          this.selectedAttachment = null;
          this.showAttachmentPopup = true;
        },
        isPreviewable(filename) {
          if (!filename) return false;
          const ext = filename.trim().split('.').pop().toLowerCase();
          // 支持在线预览的文件类型
          return [
            'pdf',           // PDF文件
            'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp',  // 图片文件
            'txt', 'log', 'md',  // 文本文件
            'doc', 'docx',   // Word文档
            'xls', 'xlsx',   // Excel表格
            'ppt', 'pptx',   // PowerPoint演示文稿
            'csv'            // CSV文件
          ].includes(ext);
        },
        // 下拉框事件处理方法
        onBadreasonChange(e) {
          const index = e.detail.value;
          this.badreasonIndex = index;
          this.badreason = this.badreasonOptions[index];
          this.saveDropdownFields('badreason');
        },
        onWorkshopChange(e) {
          const index = e.detail.value;
          this.workshopIndex = index;
          this.WORKSHOP = this.workshopOptions[index];
          this.saveDropdownFields('WORKSHOP');
        },
        onPstypeChange(e) {
          const index = e.detail.value;
          this.pstypeIndex = index;
          this.PSTYPE = this.pstypeOptions[index];
          this.saveDropdownFields('PSTYPE');
        },
        saveDropdownFields(fieldName) {
          // 直接保存到数据库
          const requestData = {
            gid: this.formData.id,
            releaseNo: this.formData.releaseNo,
            BLYY: this.badreason || '',
            SSCJ: this.WORKSHOP || '',
            PSZT: this.PSTYPE || ''
          };
          console.log('发送的数据:', requestData);
          console.log('badreason:', this.badreason);
          console.log('WORKSHOP:', this.WORKSHOP);
          console.log('PSTYPE:', this.PSTYPE);
          console.log('选择的字段:', fieldName);
          this.$post({
            url: "/RKJ/saveDropdownFields",
            data: requestData
          }).then(res => {
            if (res && res.data && res.data.data && res.data.data.tbBillList && res.data.data.tbBillList.length > 0) {
              // 延迟重新加载数据,确保数据库更新完成
              setTimeout(() => {
                this.init();
              }, 500);
            }
          }).catch(err => {
            console.error('保存失败:', err);
          });
        },
        saveFngDesc() {
          // 保存不良描述到数据库
          this.$post({
            url: "/RKJ/saveFngDesc",
            data: {
              gid: this.formData.id,
              fngDesc: this.formData.fngDesc
            }
          }).then(res => {
            if (res.status == 0) {
              this.$showMessage("不良描述保存成功");
            } else {
              this.$showMessage("不良描述保存失败");
            }
          }).catch(error => {
            console.error("保存不良描述失败:", error);
            this.$showMessage("保存不良描述失败,请重试");
          });
        }
      }
    };
    </script>
    
@@ -996,7 +1801,7 @@
    }
    
    .form-label {
        width: 120px;
        width: 100px;
        font-weight: 500;
        color: #34495e;
        flex-shrink: 0;
@@ -1040,22 +1845,55 @@
    
    .form-input {
        flex: 1;
        padding: 15px 12px;
        border: 2px solid #e9ecef;
        border-radius: 8px;
        min-width: 200px;
        padding: 8px 12px;
        border: 1px solid #ddd;
        border-radius: 4px;
        font-size: 14px;
        background-color: white;
        color: #2c3e50;
        transition: all 0.3s ease;
        min-width: 200px;
        width: 100%;
        min-height: 50px;
        box-sizing: border-box;
    }
    
    .form-input:focus {
    .large-quantity-input {
        min-width: 200px !important;
        padding: 8px 12px !important;
        font-size: 14px !important;
        border: 1px solid #ddd !important;
        border-radius: 4px !important;
        background-color: white !important;
        transition: all 0.3s ease;
        font-weight: normal !important;
        text-align: left !important;
        flex: 1;
    }
    .large-quantity-input:focus {
        border-color: #3498db;
        box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
        box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
        outline: none;
        background-color: white;
    }
    .large-quantity-input::placeholder {
        color: #95a5a6;
        font-size: 16px;
    }
    .input-field {
        width: 100%;
        padding: 8px 12px;
        border: 1px solid #ddd;
        border-radius: 4px;
        font-size: 14px;
        background-color: white;
        box-sizing: border-box;
        min-height: 36px;
    }
    .input-field:focus {
        outline: none;
        border-color: #3498db;
        box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
    }
    
    .form-hint {
@@ -1481,4 +2319,341 @@
            max-width: none;
        }
    }
    /* 附件相关样式 */
    .attachment-detail-popup {
        width: 80vw;
        max-width: 500px;
        max-height: 70vh;
        display: flex;
        flex-direction: column;
    }
    .attachment-popup-title {
        font-size: 22px;
        font-weight: 700;
        color: #222;
        margin-bottom: 8px;
        letter-spacing: 1px;
        text-align: center;
    }
    .attachment-popup-divider {
        height: 1px;
        background: linear-gradient(90deg,#e0e7ef 0%,#f5f7fa 100%);
        margin-bottom: 18px;
    }
    .attachment-detail-content {
        margin-bottom: 18px;
    }
    .attachment-detail-row {
        display: flex;
        align-items: center;
        margin-bottom: 8px;
        font-size: 15px;
    }
    .attachment-label {
        min-width: 80px;
        color: #1976d2;
        font-weight: 500;
        margin-right: 8px;
    }
    .attachment-detail-empty {
        color: #888;
        text-align: center;
        margin: 30px 0;
        font-size: 16px;
    }
    .attachment-popup-close {
        margin-top: 18px;
        width: 100%;
        background: linear-gradient(90deg,#e0e0e0 0%,#f5f7fa 100%);
        color: #444;
        border-radius: 8px;
        font-size: 16px;
        padding: 10px 0;
        border: none;
        font-weight: 600;
        letter-spacing: 1px;
        transition: background 0.2s, color 0.2s;
        box-shadow: 0 2px 8px rgba(60,60,60,0.06);
    }
    .attachment-popup-close:hover {
        background: linear-gradient(90deg,#bdbdbd 0%,#e0e0e0 100%);
        color: #1976d2;
    }
    /* 附件详情页面的操作按钮 */
    .attachment-actions-detail {
        margin: 20px 0;
        display: flex;
        gap: 12px;
        justify-content: center;
        flex-wrap: wrap;
    }
    .attachment-action-btn {
        padding: 10px 20px;
        border: none;
        border-radius: 8px;
        font-size: 14px;
        font-weight: 600;
        cursor: pointer;
        transition: all 0.3s ease;
        display: flex;
        align-items: center;
        justify-content: center;
        min-width: 120px;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
    .attachment-action-btn.preview-btn {
        background: linear-gradient(135deg, #4CAF50, #45a049);
        color: white;
    }
    .attachment-action-btn.preview-btn:hover {
        background: linear-gradient(135deg, #45a049, #3d8b40);
        transform: translateY(-1px);
        box-shadow: 0 4px 8px rgba(0,0,0,0.15);
    }
    .attachment-action-btn.download-btn {
        background: linear-gradient(135deg, #2196F3, #1976D2);
        color: white;
    }
    .attachment-action-btn.download-btn:hover {
        background: linear-gradient(135deg, #1976D2, #1565C0);
        transform: translateY(-1px);
        box-shadow: 0 4px 8px rgba(0,0,0,0.15);
    }
    /* 文件预览弹窗样式 */
    .file-preview-popup {
        width: 80vw;
        max-width: 600px;
        max-height: 70vh;
        display: flex;
        flex-direction: column;
    }
    .file-preview-title {
        font-size: 18px;
        font-weight: 700;
        color: #222;
        margin-bottom: 8px;
        text-align: center;
        word-break: break-all;
    }
    .file-preview-divider {
        height: 1px;
        background: linear-gradient(90deg,#e0e7ef 0%,#f5f7fa 100%);
        margin-bottom: 16px;
    }
    .file-preview-content {
        flex: 1;
        max-height: 400px;
        overflow-y: auto;
        background: #f8fafc;
        border-radius: 8px;
        padding: 16px;
        margin-bottom: 16px;
        border: 1px solid #e2e8f0;
    }
    .file-preview-content pre {
        font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
        font-size: 12px;
        line-height: 1.5;
        color: #2d3748;
        white-space: pre-wrap;
        word-wrap: break-word;
        margin: 0;
    }
    /* 图片预览样式 */
    .image-preview-container {
        display: flex;
        justify-content: center;
        align-items: center;
        min-height: 200px;
    }
    /* 不支持文件类型的提示样式 */
    .unsupported-preview {
        text-align: center;
        padding: 40px 20px;
        color: #666;
    }
    .unsupported-icon {
        font-size: 48px;
        margin-bottom: 16px;
    }
    .unsupported-text {
        font-size: 16px;
        font-weight: 600;
        color: #333;
        margin-bottom: 8px;
    }
    .unsupported-hint {
        font-size: 14px;
        color: #999;
        line-height: 1.4;
    }
    .file-preview-actions {
        display: flex;
        gap: 12px;
        justify-content: center;
    }
    .file-preview-btn {
        padding: 8px 20px;
        border: none;
        border-radius: 6px;
        font-size: 14px;
        font-weight: 600;
        cursor: pointer;
        transition: all 0.3s ease;
        display: flex;
        align-items: center;
        justify-content: center;
        min-width: 120px;
    }
    .file-preview-btn.download-btn {
        background: linear-gradient(135deg, #2196F3, #1976D2);
        color: white;
    }
    .file-preview-btn.download-btn:hover {
        background: linear-gradient(135deg, #1976D2, #1565C0);
        transform: translateY(-1px);
    }
    .file-preview-btn.close-btn {
        background: linear-gradient(135deg, #e0e0e0, #bdbdbd);
        color: #444;
    }
    .file-preview-btn.close-btn:hover {
        background: linear-gradient(135deg, #bdbdbd, #9e9e9e);
        transform: translateY(-1px);
    }
    /* 列表弹窗美化 */
    .attachment-list {
        padding: 0;
        margin: 0;
        list-style: none;
        max-height: 300px;
        overflow-y: auto;
    }
    .attachment-list li {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 12px 0;
        border-bottom: 1px solid #f0f0f0;
    }
    .attachment-info {
        flex: 1;
        margin-right: 10px;
    }
    .attachment-name {
        color: #3498db;
        cursor: pointer;
        font-weight: 500;
        transition: color 0.2s;
        display: block;
        margin-bottom: 4px;
    }
    .attachment-name:hover {
        color: #217dbb;
        text-decoration: underline;
    }
    .attachment-meta {
        font-size: 12px;
    }
    .attachment-type {
        color: #7f8c8d;
        font-style: italic;
    }
    .attachment-actions {
        display: flex;
        gap: 8px;
        flex-shrink: 0;
    }
    .attachment-list .secondary-btn {
        padding: 4px 10px;
        font-size: 13px;
        border-radius: 3px;
        background: #f5f7fa;
        color: #333;
        border: 1px solid #dbe2ea;
        transition: background 0.2s, color 0.2s;
    }
    .attachment-list .secondary-btn:hover {
        background: #e6f0fa;
        color: #1976d2;
    }
    .preview-btn {
        background: #e8f5e8 !important;
        color: #2e7d2e !important;
        border-color: #a5d6a5 !important;
    }
    .preview-btn:hover {
        background: #d4eecc !important;
        color: #1e5f1e !important;
    }
    /* 图片放大预览相关样式 */
    .preview-image-clickable {
        transition: transform 0.2s ease;
        border-radius: 8px;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    }
    .preview-image-clickable:hover {
        transform: scale(1.02);
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    }
    .image-zoom-hint {
        text-align: center;
        margin-top: 8px;
        font-size: 12px;
        color: #666;
        font-style: italic;
    }
    .image-preview-container {
        position: relative;
        display: flex;
        flex-direction: column;
        align-items: center;
    }
</style>
pages/QC/RKJ/List.vue
@@ -1,441 +1,441 @@
<template>
  <view class="inspection-app">
    <!-- 刷新页面后的顶部提示框 -->
    <view class="tips" :class="{ 'tips-ani': tipShow }">刷新成功</view>
    <!-- 顶部筛选区 -->
    <view class="filter-section">
      <view class="filter-controls">
        <!-- 状态切换标签 -->
        <view class="status-tabs">
          <button :class="['tab-button', current === 0 ? 'active' : '']" @click="onClickItem({currentIndex: 0})">
            {{items[0]}}
          </button>
          <button :class="['tab-button', current === 1 ? 'active' : '']" @click="onClickItem({currentIndex: 1})">
            {{items[1]}}
          </button>
    <view class="inspection-app">
      <!-- 刷新页面后的顶部提示框 -->
      <view class="tips" :class="{ 'tips-ani': tipShow }">刷新成功</view>
      <!-- 顶部筛选区 -->
      <view class="filter-section">
        <view class="filter-controls">
          <!-- 状态切换标签 -->
          <view class="status-tabs">
            <button :class="['tab-button', current === 0 ? 'active' : '']" @click="onClickItem({currentIndex: 0})">
              {{items[0]}}
            </button>
            <button :class="['tab-button', current === 1 ? 'active' : '']" @click="onClickItem({currentIndex: 1})">
              {{items[1]}}
            </button>
          </view>
        </view>
      </view>
    </view>
    <!-- 检验单列表 -->
    <view class="inspection-list">
      <!-- 检验单卡片 -->
      <view class="inspection-card" v-for="item in data" :key="item.id" @click="navigateToDetail(item)">
        <view class="card-header">
          <text class="card-title">检验单号: {{item.releaseNo}}</text>
          <text class="status pass" v-if="item.fcheckResu === '合格'">合格</text>
          <text class="status unqualified" v-if="item.fcheckResu === '不合格'">不合格</text>
          <text class="status pending" v-if="!item.fcheckResu">待检验</text>
        </view>
        <view class="card-body">
          <view class="info-row">
            <view class="info-item">
              <text class="info-label">送检批次号</text>
              <text class="info-content">{{item.billNo}}</text>
      <!-- 检验单列表 -->
      <view class="inspection-list">
        <!-- 检验单卡片 -->
        <view class="inspection-card" v-for="item in data" :key="item.id" @click="navigateToDetail(item)">
          <view class="card-header">
            <text class="card-title">检验单号: {{item.releaseNo}}</text>
            <text class="status pass" v-if="item.fcheckResu === '合格'">合格</text>
            <text class="status unqualified" v-if="item.fcheckResu === '不合格'">不合格</text>
            <text class="status pending" v-if="!item.fcheckResu">待检验</text>
          </view>
          <view class="card-body">
            <view class="info-row">
              <view class="info-item">
                <text class="info-label">工单号</text>
                <text class="info-content">{{item.billNo}}</text>
              </view>
              <view class="info-item">
                <text class="info-label">产线</text>
                <text class="info-content">{{item.daa015}}</text>
              </view>
            </view>
            <view class="info-item">
              <text class="info-label">产线</text>
              <text class="info-content">{{item.daa015}}</text>
            <view class="info-row">
              <view class="info-item">
                <text class="info-label">物料信息</text>
                <text class="info-content">{{item.itemNo}}<br>{{item.itemName}}</text>
              </view>
              <view class="info-item">
                <text class="info-label">产品型号</text>
                <text class="info-content">{{item.itemModel}}</text>
              </view>
            </view>
            <view class="info-row">
              <view class="info-item">
                <text class="info-label">送检数量</text>
                <text class="info-content highlight">{{item.quantity}}</text>
              </view>
              <view class="info-item">
                <text class="info-label">创建人</text>
                <text class="info-content">{{item.createBy}}</text>
              </view>
            </view>
            <view class="info-row">
              <view class="info-item">
                <text class="info-label">送检批次</text>
                <text class="info-content">{{item.rbillNo}}</text>
              </view>
              <view class="info-item">
                <text class="info-label">检验人</text>
                <text class="info-content">{{item.fcheckBy}}</text>
              </view>
            </view>
            <view class="meta-info">
              <text class="meta-item">
                <uni-icons type="calendar" size="14" color="#95a5a6"></uni-icons>
                {{item.createDate}}
              </text>
              <text class="meta-item" v-if="item.fcheckResu">
                <uni-icons type="checkmarkempty" size="14" color="#95a5a6"></uni-icons>
                检测结果: {{item.fcheckResu}}
              </text>
            </view>
          </view>
          <view class="info-row">
            <view class="info-item">
              <text class="info-label">物料信息</text>
              <text class="info-content">{{item.itemNo}}<br>{{item.itemName}}</text>
            </view>
            <view class="info-item">
              <text class="info-label">产品型号</text>
              <text class="info-content">{{item.itemModel}}</text>
            </view>
          <view class="card-actions">
            <button class="primary" @click.stop="navigateToDetail(item)">
              {{current === 0 ? '开始检验' : '查看详情'}}
            </button>
          </view>
          <view class="info-row">
            <view class="info-item">
              <text class="info-label">送检数量</text>
              <text class="info-content highlight">{{item.quantity}}</text>
            </view>
            <view class="info-item">
              <text class="info-label">报工人</text>
              <text class="info-content">{{item.bgr}}</text>
            </view>
          </view>
          <view class="info-row">
            <view class="info-item">
              <text class="info-label">检验人</text>
              <text class="info-content">{{item.fcheckBy}}</text>
            </view>
            <view class="info-item">
              <text class="info-label">创建人</text>
              <text class="info-content">{{item.createBy}}</text>
            </view>
          </view>
          <view class="meta-info">
            <text class="meta-item">
              <uni-icons type="calendar" size="14" color="#95a5a6"></uni-icons>
              {{item.createDate}}
            </text>
            <text class="meta-item" v-if="item.fcheckResu">
              <uni-icons type="checkmarkempty" size="14" color="#95a5a6"></uni-icons>
              检测结果: {{item.fcheckResu}}
            </text>
          </view>
        </view>
        <view class="card-actions">
          <button class="primary" @click.stop="navigateToDetail(item)">
            {{current === 0 ? '开始检验' : '查看详情'}}
          </button>
        </view>
      </view>
      <!-- 新增按钮 -->
      <view class="plus-button" @click="handleFabClick">
        +
      </view>
    </view>
    <!-- 新增按钮 -->
    <view class="plus-button" @click="handleFabClick">
      +
    </view>
  </view>
</template>
<script>
export default {
  components: {},
  data() {
    return {
      items: ['未提交', '已提交'],
      current: 0,
      data: [],
      pageIndex: 1,
      limit: 20,
      totalPage: 0,
      totalCount: 0,
      noData: false, // 没有更多数据了
      isLoading: false, // 是否正在加载
      tipShow: false
    };
  },
  onLoad() {
    //页面加载时调用的事件
    this.init();
  },
  methods: {
    init() {
      let result = "未完成";
      if (this.current == 1) {
        result = "已完成";
      }
      if (this.isLoading) return; // 如果正在加载则不继续执行
      this.isLoading = true;
  </template>
  <script>
  export default {
    components: {},
    data() {
      return {
        items: ['未提交', '已提交'],
        current: 0,
        data: [],
        pageIndex: 1,
        limit: 20,
        totalPage: 0,
        totalCount: 0,
        noData: false, // 没有更多数据了
        isLoading: false, // 是否正在加载
        tipShow: false
      };
    },
    onLoad() {
      //页面加载时调用的事件
      this.$post({
        url: "/RKJ/getPage",
        data: {
          pageIndex: this.pageIndex,
          limit: this.limit,
          createUser: this.$loginInfo.account,
          result: result
        }
      }).then(res => {
        if (this.pageIndex === 1) {
          // 如果是第一页,直接覆盖原数据
          this.data = res.data.tbBillList;
        } else {
      this.init();
    },
    methods: {
            init() {
          if (res.data.tbBillList.length > 0) {
            // 如果是下一页,追加新数据
            this.data = [...this.data, ...res.data.tbBillList];
        let fsubmit = null; // 默认查询所有未提交的记录(包括fsubmit = 0和fsubmit为空)
        if (this.current == 1) {
          fsubmit = "1"; // 已提交
        }
        if (this.isLoading) return; // 如果正在加载则不继续执行
        this.isLoading = true;
        //页面加载时调用的事件
        this.$post({
          url: "/RKJ/getPage",
          data: {
            pageIndex: this.pageIndex,
            limit: this.limit,
            createUser: this.$loginInfo.account,
            fsubmit: fsubmit
          }
        }).then(res => {
          if (this.pageIndex === 1) {
            // 如果是第一页,直接覆盖原数据
            this.data = res.data.tbBillList;
          } else {
            if (res.data.tbBillList.length > 0) {
              // 如果是下一页,追加新数据
              this.data = [...this.data, ...res.data.tbBillList];
            }
          }
          this.totalCount = res.data.totalCount;
          this.totalPage = Math.ceil(this.totalCount / this.limit);
          this.noData = this.pageIndex >= this.totalPage;
          this.isLoading = false; // 结束加载
        }).catch(() => {
          this.isLoading = false; // 出现错误时结束加载
        });
      },
      handleFabClick() {
        uni.navigateTo({
          url: 'Add?id'
        });
      },
      onClickItem(index) {
        if (this.current !== index.currentIndex) {
          this.current = index.currentIndex;
          this.data = [];
          this.pageIndex = 1;
          this.init();
        }
        this.totalCount = res.data.totalCount;
        this.totalPage = Math.ceil(this.totalCount / this.limit);
        this.noData = this.pageIndex >= this.totalPage;
        this.isLoading = false; // 结束加载
      }).catch(() => {
        this.isLoading = false; // 出现错误时结束加载
      });
      },
      navigateToDetail(item) {
        uni.navigateTo({
          url: 'Add?id=' + item.id + '&releaseNo=' + item.releaseNo
        });
      },
    },
    handleFabClick() {
      uni.navigateTo({
        url: 'Add?id'
      });
    /**
     * 下拉刷新回调函数
     */
    onPullDownRefresh() {
      this.pageIndex = 1;
      //重新执行一遍查询
      this.init();
      this.tipShow = true;
      //关闭动画
      uni.stopPullDownRefresh();
      setTimeout(function () {
        this.tipShow = false;
      }, 3000);
    },
    onClickItem(index) {
      if (this.current !== index.currentIndex) {
        this.current = index.currentIndex;
        this.data = [];
        this.pageIndex = 1;
        this.init();
      }
    /**
     * 上拉加载回调函数
     */
    onReachBottom() {
      if (this.noData || this.isLoading) return;
      this.pageIndex++;
      this.init(); // 加载更多数据
    },
    navigateToDetail(item) {
      uni.navigateTo({
        url: 'Add?id=' + item.id + '&releaseNo=' + item.releaseNo
      });
    },
  },
  /**
   * 下拉刷新回调函数
   */
  onPullDownRefresh() {
    this.pageIndex = 1;
    //重新执行一遍查询
    this.init();
    this.tipShow = true;
    //关闭动画
    uni.stopPullDownRefresh();
    setTimeout(function () {
      this.tipShow = false;
    }, 3000);
  },
  /**
   * 上拉加载回调函数
   */
  onReachBottom() {
    if (this.noData || this.isLoading) return;
    this.pageIndex++;
    this.init(); // 加载更多数据
  },
  onShow() {
    //每次进入页面都会执行的方法
    this.pageIndex = 1;
    this.data = [];
    //this.current = 0
    this.init();
    onShow() {
      //每次进入页面都会执行的方法
      this.pageIndex = 1;
      this.data = [];
      //this.current = 0
      this.init();
    }
  };
  </script>
  <style scoped>
  /* 基础样式重置 */
  .inspection-app {
    padding: 10px;
    background-color: #f5f7fa;
    min-height: 100vh;
  }
};
</script>
<style scoped>
/* 基础样式重置 */
.inspection-app {
  padding: 10px;
  background-color: #f5f7fa;
  min-height: 100vh;
}
/* 顶部筛选区 */
.filter-section {
  margin-bottom: 24px;
}
.filter-controls {
  display: flex;
  justify-content: center;
  align-items: center;
}
.status-tabs {
  display: flex;
  border-radius: 4px;
  overflow: hidden;
  background-color: #ecf0f1;
  width: 300px;
}
.tab-button {
  padding: 0px 16px;
  border: none;
  background: none;
  font-size: 14px;
  transition: all 0.3s;
  margin: 0;
  height: 35px;
  flex: 1;
}
.tab-button.active {
  background-color: #3498db;
  color: white;
}
/* 检验单列表 */
.inspection-list {
  display: flex;
  flex-direction: column;
  gap: 20px;
}
/* 检验单卡片 */
.inspection-card {
  background-color: white;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
  overflow: hidden;
  transition: all 0.3s;
}
.card-header {
  padding: 16px;
  border-bottom: 1px solid #eee;
  display: flex;
  align-items: center;
}
.card-title {
  font-size: 16px;
  font-weight: 600;
  flex: 1;
  margin: 0 8px;
}
.status {
  font-size: 12px;
  padding: 4px 8px;
  border-radius: 4px;
  font-weight: 500;
}
.status.pending {
  background-color: #f39c12;
  color: white;
}
.status.pass {
  background-color: #00cd00;
  color: white;
}
.status.unqualified {
  background-color: #ff0000;
  color: white;
}
.card-body {
  padding: 16px;
}
.info-row {
  display: flex;
  margin-bottom: 12px;
  gap: 16px;
}
.info-item {
  flex: 1;
}
.info-label {
  display: block;
  font-size: 12px;
  color: #7f8c8d;
  margin-bottom: 4px;
}
.info-content {
  font-size: 14px;
  color: #2c3e50;
  line-height: 1.5;
}
.highlight {
  font-weight: 600;
  color: #2c3e50;
  font-size: 16px;
}
.meta-info {
  display: flex;
  gap: 16px;
  font-size: 12px;
  color: #95a5a6;
  margin-top: 12px;
}
.meta-item {
  display: flex;
  align-items: center;
  gap: 4px;
}
.card-actions {
  padding: 12px 16px;
  border-top: 1px solid #eee;
  display: flex;
  gap: 8px;
}
button {
  padding: 8px 16px;
  border: 1px solid #ddd;
  border-radius: 4px;
  background-color: white;
  font-size: 14px;
  transition: all 0.2s;
  flex: 1;
}
button.primary {
  background-color: #3498db;
  color: white;
  border-color: #2980b9;
}
.plus-button {
  position: fixed;
  bottom: 20px;
  right: 20px;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background-color: #3498db;
  color: #ffffff;
  text-align: center;
  line-height: 59px;
  font-size: 24px;
  cursor: pointer;
  z-index: 1000;
  margin-bottom: 35px;
}
.tips {
  color: #67c23a;
  font-size: 14px;
  line-height: 40px;
  text-align: center;
  background-color: #f0f9eb;
  height: 0;
  opacity: 0;
  transform: translateY(-100%);
  transition: all 0.3s;
}
.tips-ani {
  transform: translateY(0);
  height: 40px;
  opacity: 1;
}
/* 响应式设计 */
@media (min-width: 768px) {
  /* 顶部筛选区 */
  .filter-section {
    margin-bottom: 24px;
  }
  .filter-controls {
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .status-tabs {
    display: flex;
    border-radius: 4px;
    overflow: hidden;
    background-color: #ecf0f1;
    width: 300px;
  }
  .tab-button {
    padding: 0px 16px;
    border: none;
    background: none;
    font-size: 14px;
    transition: all 0.3s;
    margin: 0;
    height: 35px;
    flex: 1;
  }
  .tab-button.active {
    background-color: #3498db;
    color: white;
  }
  /* 检验单列表 */
  .inspection-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
    display: flex;
    flex-direction: column;
    gap: 20px;
  }
  /* 检验单卡片 */
  .inspection-card {
    background-color: white;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
    overflow: hidden;
    transition: all 0.3s;
  }
  .card-header {
    padding: 16px;
    border-bottom: 1px solid #eee;
    display: flex;
    align-items: center;
  }
  .card-title {
    font-size: 16px;
    font-weight: 600;
    flex: 1;
    margin: 0 8px;
  }
  .status {
    font-size: 12px;
    padding: 4px 8px;
    border-radius: 4px;
    font-weight: 500;
  }
  .status.pending {
    background-color: #f39c12;
    color: white;
  }
  .status.pass {
    background-color: #00cd00;
    color: white;
  }
  .status.unqualified {
    background-color: #ff0000;
    color: white;
  }
  .card-body {
    padding: 16px;
  }
  
  .info-row {
    flex-direction: row;
  }
}
@media (min-width: 300px) {
  .inspection-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    display: flex;
    margin-bottom: 12px;
    gap: 16px;
  }
  
  .info-row {
    flex-direction: row;
  .info-item {
    flex: 1;
  }
}
</style>
  .info-label {
    display: block;
    font-size: 12px;
    color: #7f8c8d;
    margin-bottom: 4px;
  }
  .info-content {
    font-size: 14px;
    color: #2c3e50;
    line-height: 1.5;
  }
  .highlight {
    font-weight: 600;
    color: #2c3e50;
    font-size: 16px;
  }
  .meta-info {
    display: flex;
    gap: 16px;
    font-size: 12px;
    color: #95a5a6;
    margin-top: 12px;
  }
  .meta-item {
    display: flex;
    align-items: center;
    gap: 4px;
  }
  .card-actions {
    padding: 12px 16px;
    border-top: 1px solid #eee;
    display: flex;
    gap: 8px;
  }
  button {
    padding: 8px 16px;
    border: 1px solid #ddd;
    border-radius: 4px;
    background-color: white;
    font-size: 14px;
    transition: all 0.2s;
    flex: 1;
  }
  button.primary {
    background-color: #3498db;
    color: white;
    border-color: #2980b9;
  }
  .plus-button {
    position: fixed;
    bottom: 20px;
    right: 20px;
    width: 60px;
    height: 60px;
    border-radius: 50%;
    background-color: #3498db;
    color: #ffffff;
    text-align: center;
    line-height: 59px;
    font-size: 24px;
    cursor: pointer;
    z-index: 1000;
    margin-bottom: 35px;
  }
  .tips {
    color: #67c23a;
    font-size: 14px;
    line-height: 40px;
    text-align: center;
    background-color: #f0f9eb;
    height: 0;
    opacity: 0;
    transform: translateY(-100%);
    transition: all 0.3s;
  }
  .tips-ani {
    transform: translateY(0);
    height: 40px;
    opacity: 1;
  }
  /* 响应式设计 */
  @media (min-width: 768px) {
    .inspection-list {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
    }
    .info-row {
      flex-direction: row;
    }
  }
  @media (min-width: 300px) {
    .inspection-list {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    }
    .info-row {
      flex-direction: row;
    }
  }
  </style>
store/index.js
@@ -10,9 +10,9 @@
            networkFlag:'内网', 
            serverURLInt:'http://192.168.11.251:10055',//服务器体检 10.0.1.104:10054
            serverURL:'http://localhost:10055',//本地调试地址
            //serverAPI:'http://localhost:5184/api',//当前正在使用的服务器,默认为外网  localhost
            serverAPI:'http://localhost:5184/api',//当前正在使用的服务器,默认为外网  localhost
            //serverAPI:'http://192.168.1.22:10054/api',//内网 
            serverAPI:'http://36.26.21.214:10055/api',
            serverAPI:'http://36.26.21.214:10054/api',
            ftpServer:'ftp://36.26.21.214',//FTP服务器地址
        }
    },