| | |
| | | Bom用料清单 |
| | | </button> |
| | | <button class="action-btn small" @click="viewAttachmentInfo">查看附件信息</button> |
| | | <button class="action-btn small danger" @click="showDeleteConfirmDialog" v-if="formData.id && formData.fsubmit != 1">删除单据</button> |
| | | <button class="action-btn small" @click="saveRemarks" v-if="formData.fsubmit != 1">添加不合格描述</button> |
| | | <button class="action-btn small primary" @click="submitInspection" |
| | | v-if="formData.fsubmit != 1 && tableData.length > 0">提交检验</button> |
| | |
| | | </div> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 删除确认弹窗 --> |
| | | <view v-if="showDeleteConfirm" class="overlay"> |
| | | <view class="popup delete-confirm-popup"> |
| | | <h3 class="delete-confirm-title">⚠️ 确认删除</h3> |
| | | <div class="delete-confirm-divider"></div> |
| | | <div class="delete-confirm-content"> |
| | | <view class="delete-warning-icon">🗑️</view> |
| | | <view class="delete-warning-text">您确定要删除此检验单吗?</view> |
| | | <view class="delete-warning-detail">删除后将无法恢复,请谨慎操作!</view> |
| | | <view class="delete-countdown"> |
| | | <view class="countdown-text">确认按钮将在 <text class="countdown-number">{{ deleteCountdown }}</text> 秒后可用</view> |
| | | <view class="countdown-progress"> |
| | | <view class="countdown-bar" :style="{ width: countdownProgress + '%' }"></view> |
| | | </view> |
| | | </view> |
| | | </div> |
| | | <div class="delete-confirm-actions"> |
| | | <button class="delete-confirm-btn cancel-btn" @click="cancelDelete">取消</button> |
| | | <button class="delete-confirm-btn confirm-btn" |
| | | :disabled="deleteCountdown > 0" |
| | | :class="{ 'disabled': deleteCountdown > 0 }" |
| | | @click="confirmDelete">确认删除</button> |
| | | </div> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | workshopIndex: 0, |
| | | pstypeOptions: ['', '特采/让步使用', '挑选/返工使用', '退货', '待判'], |
| | | pstypeIndex: 0, |
| | | |
| | | // 删除确认相关数据 |
| | | showDeleteConfirm: false, |
| | | deleteCountdown: 5, |
| | | countdownProgress: 0, |
| | | deleteTimer: null, |
| | | }; |
| | | }, |
| | | onLoad(options) { |
| | |
| | | } |
| | | }, |
| | | |
| | | // 显示删除确认弹窗 |
| | | showDeleteConfirmDialog() { |
| | | this.showDeleteConfirm = true; |
| | | this.deleteCountdown = 5; |
| | | this.countdownProgress = 0; |
| | | this.startDeleteCountdown(); |
| | | }, |
| | | |
| | | // 开始倒计时 |
| | | startDeleteCountdown() { |
| | | this.deleteTimer = setInterval(() => { |
| | | this.deleteCountdown--; |
| | | this.countdownProgress = ((5 - this.deleteCountdown) / 5) * 100; |
| | | |
| | | if (this.deleteCountdown <= 0) { |
| | | clearInterval(this.deleteTimer); |
| | | this.deleteTimer = null; |
| | | } |
| | | }, 1000); |
| | | }, |
| | | |
| | | // 取消删除 |
| | | cancelDelete() { |
| | | this.showDeleteConfirm = false; |
| | | if (this.deleteTimer) { |
| | | clearInterval(this.deleteTimer); |
| | | this.deleteTimer = null; |
| | | } |
| | | this.deleteCountdown = 5; |
| | | this.countdownProgress = 0; |
| | | }, |
| | | |
| | | // 确认删除 |
| | | confirmDelete() { |
| | | if (this.deleteCountdown > 0) { |
| | | return; |
| | | } |
| | | |
| | | this.showDeleteConfirm = false; |
| | | if (this.deleteTimer) { |
| | | clearInterval(this.deleteTimer); |
| | | this.deleteTimer = null; |
| | | } |
| | | |
| | | this.removeXJ(); |
| | | }, |
| | | |
| | | removeXJ() { |
| | | if (this.formData.id) { |
| | | this.$post({ |
| | |
| | | background-color: #2980b9; |
| | | } |
| | | |
| | | .action-btn.danger { |
| | | background-color: #e74c3c; |
| | | color: #fff; |
| | | } |
| | | |
| | | .action-btn.danger:hover { |
| | | background-color: #c0392b; |
| | | } |
| | | |
| | | /* 小尺寸按钮样式 */ |
| | | .action-btn.small { |
| | | padding: 10px 12px; |
| | |
| | | flex-direction: column; |
| | | align-items: center; |
| | | } |
| | | |
| | | /* 删除确认弹窗样式 */ |
| | | .delete-confirm-popup { |
| | | width: 90vw; |
| | | max-width: 400px; |
| | | max-height: 70vh; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .delete-confirm-title { |
| | | font-size: 20px; |
| | | font-weight: 700; |
| | | color: #e74c3c; |
| | | margin-bottom: 8px; |
| | | text-align: center; |
| | | letter-spacing: 1px; |
| | | } |
| | | |
| | | .delete-confirm-divider { |
| | | height: 2px; |
| | | background: linear-gradient(90deg, #e74c3c 0%, #c0392b 100%); |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .delete-confirm-content { |
| | | text-align: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .delete-warning-icon { |
| | | font-size: 48px; |
| | | margin-bottom: 16px; |
| | | animation: shake 0.5s ease-in-out infinite alternate; |
| | | } |
| | | |
| | | @keyframes shake { |
| | | 0% { transform: translateX(-2px); } |
| | | 100% { transform: translateX(2px); } |
| | | } |
| | | |
| | | .delete-warning-text { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .delete-warning-detail { |
| | | font-size: 14px; |
| | | color: #7f8c8d; |
| | | margin-bottom: 20px; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | .delete-countdown { |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | padding: 16px; |
| | | margin: 16px 0; |
| | | border: 1px solid #e9ecef; |
| | | } |
| | | |
| | | .countdown-text { |
| | | font-size: 14px; |
| | | color: #495057; |
| | | margin-bottom: 8px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .countdown-number { |
| | | font-weight: 700; |
| | | color: #e74c3c; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .countdown-progress { |
| | | width: 100%; |
| | | height: 6px; |
| | | background: #e9ecef; |
| | | border-radius: 3px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .countdown-bar { |
| | | height: 100%; |
| | | background: linear-gradient(90deg, #e74c3c, #c0392b); |
| | | border-radius: 3px; |
| | | transition: width 1s ease; |
| | | } |
| | | |
| | | .delete-confirm-actions { |
| | | display: flex; |
| | | gap: 12px; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .delete-confirm-btn { |
| | | padding: 12px 24px; |
| | | border: none; |
| | | border-radius: 8px; |
| | | font-size: 16px; |
| | | 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); |
| | | } |
| | | |
| | | .delete-confirm-btn.cancel-btn { |
| | | background: linear-gradient(135deg, #95a5a6, #7f8c8d); |
| | | color: white; |
| | | } |
| | | |
| | | .delete-confirm-btn.cancel-btn:hover { |
| | | background: linear-gradient(135deg, #7f8c8d, #6c7b7d); |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | .delete-confirm-btn.confirm-btn { |
| | | background: linear-gradient(135deg, #e74c3c, #c0392b); |
| | | color: white; |
| | | } |
| | | |
| | | .delete-confirm-btn.confirm-btn:hover:not(.disabled) { |
| | | background: linear-gradient(135deg, #c0392b, #a93226); |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | .delete-confirm-btn.disabled { |
| | | background: #bdc3c7; |
| | | color: #7f8c8d; |
| | | cursor: not-allowed; |
| | | transform: none; |
| | | box-shadow: none; |
| | | } |
| | | </style> |
| | |
| | | <!-- 顶部筛选区 --> |
| | | <view class="filter-section"> |
| | | <view class="filter-controls"> |
| | | <!-- 查询条件选择器 --> |
| | | <view class="dropdown-filter"> |
| | | <picker @change="onOptionsChange" :value="optionsIndex" :range="options"> |
| | | <view class="picker">{{options[optionsIndex]}}</view> |
| | | </picker> |
| | | </view> |
| | | |
| | | <!-- 搜索框 --> |
| | | <view class="search-container"> |
| | | <input class="search-input" v-model="searchValue" :placeholder="'请输入'+options[optionsIndex]" @confirm="btnclicked" /> |
| | | <button class="search-button" @click="btnclicked">搜索</button> |
| | | </view> |
| | | |
| | | <!-- 状态切换标签 --> |
| | | <view class="status-tabs"> |
| | | <button :class="['tab-button', current === 0 ? 'active' : '']" @click="onClickItem({currentIndex: 0})"> |
| | | {{items[0]}} |
| | | 未提交({{unsubmittedCount}}) |
| | | </button> |
| | | <button :class="['tab-button', current === 1 ? 'active' : '']" @click="onClickItem({currentIndex: 1})"> |
| | | {{items[1]}} |
| | | 已提交({{submittedCount}}) |
| | | </button> |
| | | </view> |
| | | </view> |
| | |
| | | totalCount: 0, |
| | | noData: false, // 没有更多数据了 |
| | | isLoading: false, // 是否正在加载 |
| | | tipShow: false |
| | | tipShow: false, |
| | | searchValue: '', |
| | | // ===== 新增搜索功能相关数据 ===== |
| | | optionsIndex: 0, // 当前选择的查询条件索引 |
| | | options: ['工单', '检验单号', '产线', '物料编码', '物料名称'], // 查询条件选项 |
| | | selectedField: 'billNo', // 当前选择的查询字段,默认为工单 |
| | | // ===== 新增数量统计变量 ===== |
| | | unsubmittedCount: 0, // 未提交数量 |
| | | submittedCount: 0 // 已提交数量 |
| | | }; |
| | | }, |
| | | onLoad() { |
| | |
| | | this.init(); |
| | | }, |
| | | methods: { |
| | | init() { |
| | | // ===== 新增查询条件选择方法 ===== |
| | | onOptionsChange(e) { |
| | | this.optionsIndex = e.detail.value; |
| | | // 根据选择的选项设置搜索字段 |
| | | const fieldMap = { |
| | | 0: 'billNo', // 工单 |
| | | 1: 'releaseNo', // 检验单号 |
| | | 2: 'daa015', // 产线 |
| | | 3: 'itemNo', // 物料编码 |
| | | 4: 'itemName' // 物料名称 |
| | | }; |
| | | this.selectedField = fieldMap[this.optionsIndex]; |
| | | }, |
| | | |
| | | //搜索框点击事件 |
| | | btnclicked() { |
| | | this.pageIndex = 1; |
| | | this.data = []; |
| | | this.init(); |
| | | }, |
| | | |
| | | init() { |
| | | //获取搜索条件内容 |
| | | let SearchValue = this.searchValue; |
| | | |
| | | let fsubmit = null; // 默认查询所有未提交的记录(包括fsubmit = 0和fsubmit为空) |
| | | if (this.current == 1) { |
| | |
| | | pageIndex: this.pageIndex, |
| | | limit: this.limit, |
| | | createUser: this.$loginInfo.account, |
| | | fsubmit: fsubmit |
| | | fsubmit: fsubmit, |
| | | SearchValue: SearchValue, |
| | | selectedIndex: this.optionsIndex, // 新增:搜索条件索引 |
| | | searchField: this.selectedField // 新增:搜索字段名 |
| | | } |
| | | }).then(res => { |
| | | if (this.pageIndex === 1) { |
| | |
| | | } |
| | | this.totalCount = res.data.totalCount; |
| | | this.totalPage = Math.ceil(this.totalCount / this.limit); |
| | | |
| | | // 设置数量统计 - 参考XJ的实现方式 |
| | | if (this.current === 1) { |
| | | this.submittedCount = res.data.totalCount; |
| | | } else { |
| | | this.unsubmittedCount = res.data.totalCount; |
| | | } |
| | | |
| | | this.noData = this.pageIndex >= this.totalPage; |
| | | this.isLoading = false; // 结束加载 |
| | |
| | | this.isLoading = false; // 出现错误时结束加载 |
| | | }); |
| | | }, |
| | | |
| | | handleFabClick() { |
| | | uni.navigateTo({ |
| | | url: 'Add?id' |
| | |
| | | |
| | | .filter-controls { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | } |
| | | |
| | | /* 查询条件选择器 */ |
| | | .dropdown-filter { |
| | | min-width: 80px; |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .picker { |
| | | padding: 8px 12px; |
| | | border: 1px solid #ddd; |
| | | border-radius: 4px; |
| | | background-color: white; |
| | | font-size: 14px; |
| | | min-width: 80px; |
| | | } |
| | | |
| | | /* 搜索框样式 */ |
| | | .search-container { |
| | | display: flex; |
| | | flex: 1; |
| | | margin-right: 10px; |
| | | height: 36px; |
| | | min-width: 200px; |
| | | } |
| | | |
| | | .search-input { |
| | | flex: 1; |
| | | padding: 8px 12px; |
| | | border: 1px solid #ddd; |
| | | border-radius: 4px 0 0 4px; |
| | | font-size: 14px; |
| | | background-color: white; |
| | | } |
| | | |
| | | .search-button { |
| | | padding: 0 12px; |
| | | border: 1px solid #3498db; |
| | | border-radius: 0 4px 4px 0; |
| | | background-color: #3498db; |
| | | color: white; |
| | | font-size: 14px; |
| | | margin: 0; |
| | | } |
| | | |
| | | .status-tabs { |
| | |
| | | border-radius: 4px; |
| | | overflow: hidden; |
| | | background-color: #ecf0f1; |
| | | width: 300px; |
| | | flex: 1; |
| | | min-width: 200px; |
| | | } |
| | | |
| | | .tab-button { |
| | |
| | | <!-- 表单下方操作按钮区 --> |
| | | <view class="bottom-action-buttons"> |
| | | <button class="action-btn small" v-if="!isUpdate && !isShowTable" @click="toImage">上传/查看图片</button> |
| | | <button class="action-btn small" v-if="!isUpdate && !isShowTable" @click="viewAttachmentInfo">查看附件信息</button> |
| | | <button class="action-btn small" v-if="!isUpdate && !isShowTable" @click="fetchDrawingNumber(formData.itemNo)">调取PLM图纸</button> |
| | | <button class="action-btn small" v-if="!isUpdate && !isShowTable" @click="getBom">Bom用料清单</button> |
| | | <button class="action-btn small" v-if="!isUpdate && !isShowTable && formData.xjGenFlag == 0" @click="saveXJ">生成巡检</button> |
| | | <button class="action-btn small" v-if="false" @click="removeXJ">删除单据</button> |
| | | <button class="action-btn small danger" v-if="!isUpdate && !isShowTable && formData.fsubmit !== 1" @click="showDeleteConfirmDialog">删除单据</button> |
| | | <button class="action-btn small" v-if="!isUpdate && !isShowTable" @click="saveRemarks">添加不合格描述</button> |
| | | <button class="action-btn small" v-if="!isUpdate && formData.fsubmit == 0 && !isShowTable" @click="getGenUpdate">获取检验项目</button> |
| | | <button class="action-btn small primary" v-if="!isUpdate && formData.fsubmit ==0 && !isShowTable" @click="submitInspection">提交检验</button> |
| | |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 附件列表弹窗 --> |
| | | <view v-if="showAttachmentPopup" class="overlay"> |
| | | <view class="popup attachment-list-popup"> |
| | | <div class="attachment-popup-header"> |
| | | <h3 class="attachment-popup-title">附件列表</h3> |
| | | <button class="attachment-close-btn" @click="closeAttachmentPopup">关闭</button> |
| | | </div> |
| | | |
| | | <div class="attachment-popup-content"> |
| | | <div v-if="attachmentsLoading" class="attachment-loading"> |
| | | <div class="loading-spinner"></div> |
| | | <span class="loading-text">正在加载附件...</span> |
| | | </div> |
| | | |
| | | <div v-else-if="attachments.length === 0" class="attachment-empty"> |
| | | <div class="empty-icon">📁</div> |
| | | <div class="empty-text">暂无附件</div> |
| | | <div class="empty-hint">该物料暂未上传任何附件</div> |
| | | </div> |
| | | |
| | | <div v-else class="attachment-list"> |
| | | <div v-for="item in attachments" :key="item.id" class="attachment-item"> |
| | | <div class="attachment-info"> |
| | | <div class="file-type-badge" :class="getFileTypeClass(item.fattach)"> |
| | | {{ getFileTypeIcon(item.fattach) }} |
| | | </div> |
| | | <div class="attachment-details"> |
| | | <div class="attachment-name" @click="showAttachmentDetailDialog(item)"> |
| | | {{ item.fattach }} |
| | | </div> |
| | | <div class="attachment-meta"> |
| | | <span class="meta-type">{{ item.ftype || '未知类型' }}</span> |
| | | <span v-if="item.fversion" class="meta-version">v{{ item.fversion }}</span> |
| | | <span v-if="item.fdate" class="meta-date">{{ formatDate(item.fdate) }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="attachment-actions"> |
| | | <button class="btn-secondary" @click="showAttachmentDetailDialog(item)">详情</button> |
| | | <button v-if="isPreviewable(item.fattach)" |
| | | class="btn-primary" |
| | | @click="previewFtpFile(item)">预览</button> |
| | | <button class="btn-success" @click="downloadAttachment(item)">下载</button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 附件详情弹窗 --> |
| | | <view v-if="showAttachmentDetail" class="overlay"> |
| | | <view class="popup attachment-detail-popup"> |
| | | <div class="attachment-popup-header"> |
| | | <h3 class="attachment-popup-title">附件详情</h3> |
| | | <button class="attachment-close-btn" @click="closeAttachmentDetail">返回</button> |
| | | </div> |
| | | |
| | | <div class="attachment-popup-content"> |
| | | <div v-if="selectedAttachment" class="attachment-detail-content"> |
| | | <div class="attachment-detail-header"> |
| | | <div class="file-type-badge large" :class="getFileTypeClass(selectedAttachment.fattach)"> |
| | | {{ getFileTypeIcon(selectedAttachment.fattach) }} |
| | | </div> |
| | | <div class="attachment-detail-title"> |
| | | {{ selectedAttachment.fattach }} |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="attachment-detail-info"> |
| | | <div class="info-row"> |
| | | <div class="info-item"> |
| | | <text class="info-label">ID</text> |
| | | <text class="info-content">{{ Math.trunc(selectedAttachment.id) }}</text> |
| | | </div> |
| | | <div class="info-item"> |
| | | <text class="info-label">类型</text> |
| | | <text class="info-content">{{ selectedAttachment.ftype || '未知类型' }}</text> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="info-row" v-if="selectedAttachment.fversion"> |
| | | <div class="info-item"> |
| | | <text class="info-label">版本</text> |
| | | <text class="info-content">{{ selectedAttachment.fversion }}</text> |
| | | </div> |
| | | <div class="info-item" v-if="selectedAttachment.fdate"> |
| | | <text class="info-label">受控日期</text> |
| | | <text class="info-content">{{ formatDate(selectedAttachment.fdate) }}</text> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="info-row" v-if="selectedAttachment.createBy"> |
| | | <div class="info-item"> |
| | | <text class="info-label">上传人</text> |
| | | <text class="info-content">{{ selectedAttachment.createBy }}</text> |
| | | </div> |
| | | <div class="info-item" v-if="selectedAttachment.createDate"> |
| | | <text class="info-label">上传时间</text> |
| | | <text class="info-content">{{ formatDate(selectedAttachment.createDate) }}</text> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="attachment-detail-actions"> |
| | | <button v-if="isPreviewable(selectedAttachment.fattach)" |
| | | class="btn-primary" |
| | | @click="previewFtpFile(selectedAttachment)">预览</button> |
| | | <button class="btn-success" @click="downloadAttachment(selectedAttachment)">下载</button> |
| | | </div> |
| | | </div> |
| | | <div v-else class="attachment-detail-empty"> |
| | | <div class="empty-icon">❌</div> |
| | | <div class="empty-text">暂无附件信息</div> |
| | | </div> |
| | | </div> |
| | | </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 v-if="showDeleteConfirm" class="overlay"> |
| | | <view class="popup delete-confirm-popup"> |
| | | <h3 class="delete-confirm-title">⚠️ 确认删除</h3> |
| | | <div class="delete-confirm-divider"></div> |
| | | <div class="delete-confirm-content"> |
| | | <view class="delete-warning-icon">🗑️</view> |
| | | <view class="delete-warning-text">您确定要删除此检验单吗?</view> |
| | | <view class="delete-warning-detail">删除后将无法恢复,请谨慎操作!</view> |
| | | <view class="delete-countdown"> |
| | | <view class="countdown-text">确认按钮将在 <text class="countdown-number">{{ deleteCountdown }}</text> 秒后可用</view> |
| | | <view class="countdown-progress"> |
| | | <view class="countdown-bar" :style="{ width: countdownProgress + '%' }"></view> |
| | | </view> |
| | | </view> |
| | | </div> |
| | | <div class="delete-confirm-actions"> |
| | | <button class="delete-confirm-btn cancel-btn" @click="cancelDelete">取消</button> |
| | | <button class="delete-confirm-btn confirm-btn" |
| | | :disabled="deleteCountdown > 0" |
| | | :class="{ 'disabled': deleteCountdown > 0 }" |
| | | @click="confirmDelete">确认删除</button> |
| | | </div> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | <script> |
| | |
| | | statusUser: "", |
| | | SJ_MJ: '首检', |
| | | fsubmit:"", |
| | | mnum: 1, // 添加 mnum 字段 |
| | | dnum: "", // 添加 dnum 字段 |
| | | }, |
| | | |
| | | DAA020List: [], |
| | |
| | | |
| | | SJ_MJList: ['首检', '末检'], |
| | | SJ_MJIndex: 0, |
| | | |
| | | // 附件相关数据 |
| | | showAttachmentPopup: false, |
| | | showAttachmentDetail: false, |
| | | showFilePreviewPopup: false, |
| | | attachments: [], |
| | | attachmentsLoading: false, |
| | | selectedAttachment: null, |
| | | previewTitle: '', |
| | | previewContent: '', |
| | | previewType: '', |
| | | previewFileUrl: '', |
| | | |
| | | // 删除确认相关数据 |
| | | showDeleteConfirm: false, |
| | | deleteCountdown: 5, |
| | | countdownProgress: 0, |
| | | deleteTimer: null |
| | | }; |
| | | }, |
| | | onLoad(options) { |
| | |
| | | return '检验项目详情'; |
| | | } |
| | | }, |
| | | // 显示删除确认弹窗 |
| | | showDeleteConfirmDialog() { |
| | | this.showDeleteConfirm = true; |
| | | this.deleteCountdown = 5; |
| | | this.countdownProgress = 0; |
| | | this.startDeleteCountdown(); |
| | | }, |
| | | |
| | | // 开始倒计时 |
| | | startDeleteCountdown() { |
| | | this.deleteTimer = setInterval(() => { |
| | | this.deleteCountdown--; |
| | | this.countdownProgress = ((5 - this.deleteCountdown) / 5) * 100; |
| | | |
| | | if (this.deleteCountdown <= 0) { |
| | | clearInterval(this.deleteTimer); |
| | | this.deleteTimer = null; |
| | | } |
| | | }, 1000); |
| | | }, |
| | | |
| | | // 取消删除 |
| | | cancelDelete() { |
| | | this.showDeleteConfirm = false; |
| | | if (this.deleteTimer) { |
| | | clearInterval(this.deleteTimer); |
| | | this.deleteTimer = null; |
| | | } |
| | | this.deleteCountdown = 5; |
| | | this.countdownProgress = 0; |
| | | }, |
| | | |
| | | // 确认删除 |
| | | confirmDelete() { |
| | | if (this.deleteCountdown > 0) { |
| | | return; |
| | | } |
| | | |
| | | this.showDeleteConfirm = false; |
| | | if (this.deleteTimer) { |
| | | clearInterval(this.deleteTimer); |
| | | this.deleteTimer = null; |
| | | } |
| | | |
| | | this.removeXJ(); |
| | | }, |
| | | |
| | | removeXJ() { |
| | | if (this.formData.id) { |
| | | this.$post({ |
| | |
| | | }).then(res => { |
| | | let tbBillListElement = res.data.tbBillList[0]; |
| | | if (tbBillListElement) { |
| | | // 保留原有的 mnum 和 dnum 默认值 |
| | | const originalMnum = this.formData.mnum; |
| | | const originalDnum = this.formData.dnum; |
| | | this.formData = tbBillListElement; |
| | | // 如果后端没有返回这些字段,使用默认值 |
| | | if (this.formData.mnum === undefined || this.formData.mnum === null) { |
| | | this.formData.mnum = originalMnum || 1; |
| | | } |
| | | if (this.formData.dnum === undefined || this.formData.dnum === null) { |
| | | this.formData.dnum = originalDnum || ""; |
| | | } |
| | | this.$post({ |
| | | url: "/SJ/getQSItems", |
| | | data: { |
| | |
| | | return; |
| | | } |
| | | |
| | | // 先获取检验单的详细信息,包含mnum和dnum |
| | | // 参数验证 |
| | | if (!this.$loginInfo.account) { |
| | | this.$showMessage("用户信息不完整,无法获取检验项目"); |
| | | return; |
| | | } |
| | | |
| | | // 直接使用 formData 中的值 |
| | | const requestData = { |
| | | id: this.formData.id, |
| | | no: this.formData.billNo, |
| | | user: this.$loginInfo.account, |
| | | mnum: this.formData.mnum || 1, |
| | | dnum: this.formData.dnum || null |
| | | }; |
| | | |
| | | // 添加调试日志 |
| | | console.log("SJ GenUpdate 请求参数:", requestData); |
| | | |
| | | this.$post({ |
| | | url: "/SJ/getQSItems", |
| | | data: { |
| | | pid: this.formData.id |
| | | } |
| | | }).then(res => { |
| | | if (res.data.tbBillList && res.data.tbBillList.length > 0) { |
| | | // 从第一个检验项目中获取mnum和dnum |
| | | const firstItem = res.data.tbBillList[0]; |
| | | const mnum = firstItem.mnum || 1; |
| | | const dnum = firstItem.dnum || ""; |
| | | |
| | | // 调用GenUpdate |
| | | this.$post({ |
| | | url: "/SJ/GenUpdate", |
| | | data: { |
| | | id: this.formData.id, |
| | | no: this.formData.billNo, |
| | | user: this.$loginInfo.account, |
| | | mnum: mnum, |
| | | dnum: dnum |
| | | } |
| | | }).then(genRes => { |
| | | if (genRes.data.result === 0) { |
| | | this.$showMessage("获取检验项目成功"); |
| | | this.init(); |
| | | // 获取成功后直接进入填写阶段 |
| | | this.isShowTable = false; |
| | | this.isUpdate = false; |
| | | } else { |
| | | this.$showMessage(genRes.data.message || "获取失败"); |
| | | } |
| | | }); |
| | | url: "/SJ/GenUpdate", |
| | | data: requestData |
| | | }).then(genRes => { |
| | | console.log("SJ GenUpdate 响应:", genRes); |
| | | if (genRes.data.result === 0) { |
| | | this.$showMessage("获取检验项目成功"); |
| | | this.init(); |
| | | // 获取成功后直接进入填写阶段 |
| | | this.isShowTable = false; |
| | | this.isUpdate = false; |
| | | } else { |
| | | // 如果没有检验项目,使用默认值 |
| | | this.$post({ |
| | | url: "/SJ/GenUpdate", |
| | | data: { |
| | | id: this.formData.id, |
| | | no: this.formData.billNo, |
| | | user: this.$loginInfo.account, |
| | | mnum: 1, |
| | | dnum: "" |
| | | } |
| | | }).then(genRes => { |
| | | if (genRes.data.result === 0) { |
| | | this.$showMessage("获取检验项目成功"); |
| | | this.init(); |
| | | // 获取成功后直接进入填写阶段 |
| | | this.isShowTable = false; |
| | | this.isUpdate = false; |
| | | } else { |
| | | this.$showMessage(genRes.data.message || "获取失败"); |
| | | } |
| | | this.$showMessage(genRes.data.message || "获取失败"); |
| | | } |
| | | }).catch(err => { |
| | | console.error("SJ GenUpdate 错误:", err); |
| | | this.$showMessage("获取检验项目失败:" + (err.message || "未知错误")); |
| | | }); |
| | | }, |
| | | |
| | | // 附件相关方法 |
| | | viewAttachmentInfo() { |
| | | this.showAttachmentPopup = true; |
| | | this.attachmentsLoading = true; |
| | | this.attachments = []; |
| | | this.$post({ |
| | | url: "/SJ/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" }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | closeAttachmentPopup() { |
| | | this.showAttachmentPopup = false; |
| | | }, |
| | | |
| | | showAttachmentDetailDialog(item) { |
| | | this.selectedAttachment = item; |
| | | this.showAttachmentPopup = false; |
| | | this.showAttachmentDetail = true; |
| | | }, |
| | | |
| | | 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', |
| | | 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', |
| | | 'txt', 'log', 'md', |
| | | 'doc', 'docx', |
| | | 'xls', 'xlsx', |
| | | 'ppt', 'pptx', |
| | | 'csv' |
| | | ].includes(ext); |
| | | }, |
| | | |
| | | // 获取文件类型图标 |
| | | getFileTypeIcon(filename) { |
| | | if (!filename) return '📄'; |
| | | const ext = filename.trim().split('.').pop().toLowerCase(); |
| | | const iconMap = { |
| | | 'pdf': '📕', |
| | | 'jpg': '🖼️', 'jpeg': '🖼️', 'png': '🖼️', 'gif': '🖼️', 'bmp': '🖼️', 'webp': '🖼️', |
| | | 'txt': '📝', 'log': '📝', 'md': '📝', |
| | | 'doc': '📘', 'docx': '📘', |
| | | 'xls': '📊', 'xlsx': '📊', |
| | | 'ppt': '📙', 'pptx': '📙', |
| | | 'csv': '📊', |
| | | 'zip': '📦', 'rar': '📦', '7z': '📦', |
| | | 'dwg': '🏗️', 'dxf': '🏗️' |
| | | }; |
| | | return iconMap[ext] || '📄'; |
| | | }, |
| | | |
| | | // 获取文件类型CSS类 |
| | | getFileTypeClass(filename) { |
| | | if (!filename) return 'file-unknown'; |
| | | const ext = filename.trim().split('.').pop().toLowerCase(); |
| | | const classMap = { |
| | | 'pdf': 'file-pdf', |
| | | 'jpg': 'file-image', 'jpeg': 'file-image', 'png': 'file-image', 'gif': 'file-image', 'bmp': 'file-image', 'webp': 'file-image', |
| | | 'txt': 'file-text', 'log': 'file-text', 'md': 'file-text', |
| | | 'doc': 'file-word', 'docx': 'file-word', |
| | | 'xls': 'file-excel', 'xlsx': 'file-excel', |
| | | 'ppt': 'file-powerpoint', 'pptx': 'file-powerpoint', |
| | | 'csv': 'file-excel', |
| | | 'zip': 'file-archive', 'rar': 'file-archive', '7z': 'file-archive', |
| | | 'dwg': 'file-cad', 'dxf': 'file-cad' |
| | | }; |
| | | return classMap[ext] || 'file-unknown'; |
| | | }, |
| | | |
| | | // 格式化日期 |
| | | formatDate(dateString) { |
| | | if (!dateString) return ''; |
| | | try { |
| | | const date = new Date(dateString); |
| | | return date.toLocaleDateString('zh-CN', { |
| | | year: 'numeric', |
| | | month: '2-digit', |
| | | day: '2-digit', |
| | | hour: '2-digit', |
| | | minute: '2-digit' |
| | | }); |
| | | } catch (e) { |
| | | return dateString; |
| | | } |
| | | }, |
| | | |
| | | downloadAttachment(item) { |
| | | const fileName = item.fattach.replace(/[\s\u3000\r\n]+/g, '').trim(); |
| | | const downloadUrl = this.$store.state.serverInfo.serverAPI + "/SJ/DownloadFtpFile?itemNo=" + |
| | | encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) + |
| | | "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer); |
| | | |
| | | uni.downloadFile({ |
| | | url: downloadUrl, |
| | | success: (res) => { |
| | | if (res.statusCode === 200) { |
| | | uni.showToast({ title: '下载成功', icon: 'success' }); |
| | | } else { |
| | | uni.showToast({ title: '下载失败', icon: 'none' }); |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | console.error('下载失败:', err); |
| | | uni.showToast({ title: '下载失败,请重试', icon: 'none' }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | 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 + "/SJ/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 { |
| | | this.previewOfficeFile(previewUrl, fileName); |
| | | } |
| | | }, |
| | | |
| | | previewPdfFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewContent = url; |
| | | this.previewType = 'pdf'; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | }, |
| | | |
| | | previewImageFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewContent = url; |
| | | this.previewType = 'image'; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | }, |
| | | |
| | | previewTextFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewType = 'text'; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | |
| | | uni.request({ |
| | | url: url, |
| | | method: 'GET', |
| | | success: (res) => { |
| | | this.previewContent = res.data; |
| | | }, |
| | | fail: (err) => { |
| | | this.previewContent = '预览失败,请下载后查看'; |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | previewOfficeFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewContent = ''; |
| | | this.previewType = 'excel'; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | }, |
| | | |
| | | closeFilePreview() { |
| | | this.showFilePreviewPopup = false; |
| | | this.showAttachmentDetail = true; |
| | | }, |
| | | |
| | | downloadPreviewFile() { |
| | | if (this.previewFileUrl && this.selectedAttachment) { |
| | | this.downloadAttachment(this.selectedAttachment); |
| | | } |
| | | }, |
| | | |
| | | previewImageInPopup() { |
| | | // 在新窗口中打开图片进行放大查看 |
| | | if (this.previewContent) { |
| | | uni.previewImage({ |
| | | urls: [this.previewContent], |
| | | current: this.previewContent |
| | | }); |
| | | } |
| | | } |
| | | }, |
| | | onShow() { |
| | |
| | | background-color: #2980b9; |
| | | } |
| | | |
| | | .action-btn.danger { |
| | | background-color: #e74c3c; |
| | | color: #fff; |
| | | } |
| | | |
| | | .action-btn.danger:hover { |
| | | background-color: #c0392b; |
| | | } |
| | | |
| | | /* 小尺寸按钮样式 */ |
| | | .action-btn.small { |
| | | padding: 10px 12px; |
| | |
| | | overflow-x: auto; |
| | | } |
| | | } |
| | | |
| | | /* 附件相关样式 */ |
| | | .attachment-list-popup { |
| | | width: 80vw; |
| | | max-width: 800px; |
| | | max-height: 85vh; |
| | | } |
| | | |
| | | .attachment-detail-popup { |
| | | width: 70vw; |
| | | max-width: 600px; |
| | | } |
| | | |
| | | .attachment-popup-header { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #eee; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | background-color: white; |
| | | } |
| | | |
| | | .attachment-popup-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | margin: 0; |
| | | } |
| | | |
| | | .attachment-close-btn { |
| | | padding: 8px 16px; |
| | | border: 1px solid #ddd; |
| | | border-radius: 4px; |
| | | background-color: white; |
| | | font-size: 14px; |
| | | transition: all 0.2s; |
| | | color: #2c3e50; |
| | | } |
| | | |
| | | .attachment-close-btn:hover { |
| | | background-color: #f8f9fa; |
| | | } |
| | | |
| | | .attachment-popup-content { |
| | | padding: 16px; |
| | | max-height: 60vh; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | /* 加载状态 */ |
| | | .attachment-loading { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 40px 20px; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .loading-spinner { |
| | | width: 32px; |
| | | height: 32px; |
| | | border: 3px solid #f3f3f3; |
| | | border-top: 3px solid #3498db; |
| | | border-radius: 50%; |
| | | animation: spin 1s linear infinite; |
| | | } |
| | | |
| | | @keyframes spin { |
| | | 0% { transform: rotate(0deg); } |
| | | 100% { transform: rotate(360deg); } |
| | | } |
| | | |
| | | .loading-text { |
| | | font-size: 14px; |
| | | color: #7f8c8d; |
| | | } |
| | | |
| | | /* 空状态 */ |
| | | .attachment-empty { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 40px 20px; |
| | | gap: 12px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .empty-icon { |
| | | font-size: 36px; |
| | | opacity: 0.6; |
| | | } |
| | | |
| | | .empty-text { |
| | | font-size: 16px; |
| | | color: #7f8c8d; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .empty-hint { |
| | | font-size: 14px; |
| | | color: #95a5a6; |
| | | } |
| | | |
| | | /* 附件列表布局 */ |
| | | .attachment-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .attachment-item { |
| | | background-color: white; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); |
| | | overflow: hidden; |
| | | transition: all 0.3s; |
| | | border: 1px solid #eee; |
| | | } |
| | | |
| | | .attachment-item:hover { |
| | | box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .attachment-info { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #eee; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .file-type-badge { |
| | | width: 40px; |
| | | height: 40px; |
| | | border-radius: 8px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 20px; |
| | | background: #f8f9fa; |
| | | border: 2px solid #e9ecef; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .file-type-badge.large { |
| | | width: 56px; |
| | | height: 56px; |
| | | font-size: 28px; |
| | | } |
| | | |
| | | .file-type-badge.file-pdf { background: #ffe6e6; border-color: #ffcccc; } |
| | | .file-type-badge.file-image { background: #e6f3ff; border-color: #cce7ff; } |
| | | .file-type-badge.file-text { background: #e6ffe6; border-color: #ccffcc; } |
| | | .file-type-badge.file-word { background: #e6f0ff; border-color: #cce0ff; } |
| | | .file-type-badge.file-excel { background: #e6ffe6; border-color: #ccffcc; } |
| | | .file-type-badge.file-powerpoint { background: #fff0e6; border-color: #ffe0cc; } |
| | | .file-type-badge.file-archive { background: #f0e6ff; border-color: #e0ccff; } |
| | | .file-type-badge.file-cad { background: #e6fff0; border-color: #ccffe0; } |
| | | .file-type-badge.file-unknown { background: #f5f5f5; border-color: #e0e0e0; } |
| | | |
| | | .attachment-details { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .attachment-name { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | cursor: pointer; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | margin-bottom: 8px; |
| | | transition: color 0.2s; |
| | | } |
| | | |
| | | .attachment-name:hover { |
| | | color: #3498db; |
| | | } |
| | | |
| | | .attachment-meta { |
| | | display: flex; |
| | | gap: 16px; |
| | | font-size: 12px; |
| | | color: #95a5a6; |
| | | } |
| | | |
| | | .meta-type { |
| | | background-color: #ecf0f1; |
| | | padding: 2px 6px; |
| | | border-radius: 10px; |
| | | color: #7f8c8d; |
| | | } |
| | | |
| | | .meta-version { |
| | | background-color: #e8f5e8; |
| | | padding: 2px 6px; |
| | | border-radius: 10px; |
| | | color: #2e7d32; |
| | | } |
| | | |
| | | .meta-date { |
| | | background-color: #fff3e0; |
| | | padding: 2px 6px; |
| | | border-radius: 10px; |
| | | color: #f57c00; |
| | | } |
| | | |
| | | .attachment-actions { |
| | | padding: 12px 16px; |
| | | border-top: 1px solid #eee; |
| | | display: flex; |
| | | gap: 8px; |
| | | background-color: #f8f9fa; |
| | | } |
| | | |
| | | /* 按钮样式 */ |
| | | .btn-secondary { |
| | | padding: 8px 16px; |
| | | border: 1px solid #ddd; |
| | | border-radius: 4px; |
| | | background-color: white; |
| | | font-size: 14px; |
| | | transition: all 0.2s; |
| | | color: #2c3e50; |
| | | flex: 1; |
| | | } |
| | | |
| | | .btn-secondary:hover { |
| | | background-color: #f8f9fa; |
| | | } |
| | | |
| | | .btn-primary { |
| | | padding: 8px 16px; |
| | | border: 1px solid #3498db; |
| | | border-radius: 4px; |
| | | background-color: #3498db; |
| | | color: white; |
| | | font-size: 14px; |
| | | transition: all 0.2s; |
| | | flex: 1; |
| | | } |
| | | |
| | | .btn-primary:hover { |
| | | background-color: #2980b9; |
| | | } |
| | | |
| | | .btn-success { |
| | | padding: 8px 16px; |
| | | border: 1px solid #2ecc71; |
| | | border-radius: 4px; |
| | | background-color: #2ecc71; |
| | | color: white; |
| | | font-size: 14px; |
| | | transition: all 0.2s; |
| | | flex: 1; |
| | | } |
| | | |
| | | .btn-success:hover { |
| | | background-color: #27ae60; |
| | | } |
| | | |
| | | /* 附件详情样式 */ |
| | | .attachment-detail-header { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | padding-bottom: 16px; |
| | | border-bottom: 1px solid #eee; |
| | | } |
| | | |
| | | .attachment-detail-title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | flex: 1; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .attachment-detail-info { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | |
| | | .attachment-detail-actions { |
| | | padding: 12px 16px; |
| | | border-top: 1px solid #eee; |
| | | display: flex; |
| | | gap: 8px; |
| | | background-color: #f8f9fa; |
| | | } |
| | | |
| | | /* 文件预览弹窗样式 */ |
| | | .file-preview-popup { |
| | | width: 80vw; |
| | | max-width: 800px; |
| | | max-height: 80vh; |
| | | } |
| | | |
| | | .file-preview-title { |
| | | padding: 20px; |
| | | margin: 0; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | border-bottom: 1px solid #eee; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .file-preview-content { |
| | | padding: 20px; |
| | | max-height: 60vh; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .file-preview-content pre { |
| | | white-space: pre-wrap; |
| | | word-wrap: break-word; |
| | | font-family: 'Courier New', monospace; |
| | | font-size: 12px; |
| | | line-height: 1.4; |
| | | background: #f8f9fa; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | border: 1px solid #e9ecef; |
| | | } |
| | | |
| | | .image-preview-container { |
| | | text-align: center; |
| | | } |
| | | |
| | | .image-zoom-hint { |
| | | margin-top: 10px; |
| | | font-size: 12px; |
| | | color: #7f8c8d; |
| | | } |
| | | |
| | | .unsupported-preview { |
| | | text-align: center; |
| | | padding: 40px 20px; |
| | | } |
| | | |
| | | .unsupported-icon { |
| | | font-size: 48px; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .unsupported-text { |
| | | font-size: 16px; |
| | | color: #7f8c8d; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .unsupported-hint { |
| | | font-size: 14px; |
| | | color: #95a5a6; |
| | | } |
| | | |
| | | .file-preview-actions { |
| | | padding: 15px 20px; |
| | | border-top: 1px solid #eee; |
| | | display: flex; |
| | | gap: 10px; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .file-preview-btn { |
| | | padding: 8px 16px; |
| | | border-radius: 4px; |
| | | border: none; |
| | | font-size: 14px; |
| | | cursor: pointer; |
| | | transition: all 0.2s; |
| | | } |
| | | |
| | | .file-preview-btn.download-btn { |
| | | background: #2ecc71; |
| | | color: white; |
| | | } |
| | | |
| | | .file-preview-btn.close-btn { |
| | | background: #95a5a6; |
| | | color: white; |
| | | } |
| | | |
| | | .file-preview-btn:hover { |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 2px 8px rgba(0,0,0,0.15); |
| | | } |
| | | |
| | | /* 删除确认弹窗样式 */ |
| | | .delete-confirm-popup { |
| | | width: 90vw; |
| | | max-width: 400px; |
| | | max-height: 70vh; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .delete-confirm-title { |
| | | font-size: 20px; |
| | | font-weight: 700; |
| | | color: #e74c3c; |
| | | margin-bottom: 8px; |
| | | text-align: center; |
| | | letter-spacing: 1px; |
| | | } |
| | | |
| | | .delete-confirm-divider { |
| | | height: 2px; |
| | | background: linear-gradient(90deg, #e74c3c 0%, #c0392b 100%); |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .delete-confirm-content { |
| | | text-align: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .delete-warning-icon { |
| | | font-size: 48px; |
| | | margin-bottom: 16px; |
| | | animation: shake 0.5s ease-in-out infinite alternate; |
| | | } |
| | | |
| | | @keyframes shake { |
| | | 0% { transform: translateX(-2px); } |
| | | 100% { transform: translateX(2px); } |
| | | } |
| | | |
| | | .delete-warning-text { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .delete-warning-detail { |
| | | font-size: 14px; |
| | | color: #7f8c8d; |
| | | margin-bottom: 20px; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | .delete-countdown { |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | padding: 16px; |
| | | margin: 16px 0; |
| | | border: 1px solid #e9ecef; |
| | | } |
| | | |
| | | .countdown-text { |
| | | font-size: 14px; |
| | | color: #495057; |
| | | margin-bottom: 8px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .countdown-number { |
| | | font-weight: 700; |
| | | color: #e74c3c; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .countdown-progress { |
| | | width: 100%; |
| | | height: 6px; |
| | | background: #e9ecef; |
| | | border-radius: 3px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .countdown-bar { |
| | | height: 100%; |
| | | background: linear-gradient(90deg, #e74c3c, #c0392b); |
| | | border-radius: 3px; |
| | | transition: width 1s ease; |
| | | } |
| | | |
| | | .delete-confirm-actions { |
| | | display: flex; |
| | | gap: 12px; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .delete-confirm-btn { |
| | | padding: 12px 24px; |
| | | border: none; |
| | | border-radius: 8px; |
| | | font-size: 16px; |
| | | 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); |
| | | } |
| | | |
| | | .delete-confirm-btn.cancel-btn { |
| | | background: linear-gradient(135deg, #95a5a6, #7f8c8d); |
| | | color: white; |
| | | } |
| | | |
| | | .delete-confirm-btn.cancel-btn:hover { |
| | | background: linear-gradient(135deg, #7f8c8d, #6c7b7d); |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | .delete-confirm-btn.confirm-btn { |
| | | background: linear-gradient(135deg, #e74c3c, #c0392b); |
| | | color: white; |
| | | } |
| | | |
| | | .delete-confirm-btn.confirm-btn:hover:not(.disabled) { |
| | | background: linear-gradient(135deg, #c0392b, #a93226); |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | .delete-confirm-btn.disabled { |
| | | background: #bdc3c7; |
| | | color: #7f8c8d; |
| | | cursor: not-allowed; |
| | | transform: none; |
| | | box-shadow: none; |
| | | } |
| | | </style> |
| | |
| | | 不合格描述 |
| | | </button> |
| | | |
| | | <button class="btn attachment-btn" @tap="viewAttachmentInfo"> |
| | | <uni-icons type="folder" size="16" color="#fff"></uni-icons> |
| | | 查看附件信息 |
| | | </button> |
| | | |
| | | <input v-if="!isAllCompleted" |
| | | v-model="formData.fcheckResu" |
| | | type="text" |
| | |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 附件列表弹窗 --> |
| | | <view v-if="showAttachmentPopup" class="overlay"> |
| | | <view class="popup attachment-list-popup"> |
| | | <div class="attachment-popup-header"> |
| | | <h3 class="attachment-popup-title">附件列表</h3> |
| | | <button class="attachment-close-btn" @click="closeAttachmentPopup">关闭</button> |
| | | </div> |
| | | |
| | | <div class="attachment-popup-content"> |
| | | <div v-if="attachmentsLoading" class="attachment-loading"> |
| | | <div class="loading-spinner"></div> |
| | | <span class="loading-text">正在加载附件...</span> |
| | | </div> |
| | | |
| | | <div v-else-if="attachments.length === 0" class="attachment-empty"> |
| | | <div class="empty-icon">📁</div> |
| | | <div class="empty-text">暂无附件</div> |
| | | <div class="empty-hint">该物料暂未上传任何附件</div> |
| | | </div> |
| | | |
| | | <div v-else class="attachment-list"> |
| | | <div v-for="item in attachments" :key="item.id" class="attachment-item"> |
| | | <div class="attachment-info"> |
| | | <div class="file-type-badge" :class="getFileTypeClass(item.fattach)"> |
| | | {{ getFileTypeIcon(item.fattach) }} |
| | | </div> |
| | | <div class="attachment-details"> |
| | | <div class="attachment-name" @click="showAttachmentDetailDialog(item)"> |
| | | {{ item.fattach }} |
| | | </div> |
| | | <div class="attachment-meta"> |
| | | <span class="meta-type">{{ item.ftype || '未知类型' }}</span> |
| | | <span v-if="item.fversion" class="meta-version">v{{ item.fversion }}</span> |
| | | <span v-if="item.fdate" class="meta-date">{{ formatDate(item.fdate) }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="attachment-actions"> |
| | | <button class="btn-secondary" @click="showAttachmentDetailDialog(item)">详情</button> |
| | | <button v-if="isPreviewable(item.fattach)" |
| | | class="btn-primary" |
| | | @click="previewFtpFile(item)">预览</button> |
| | | <button class="btn-success" @click="downloadAttachment(item)">下载</button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 附件详情弹窗 --> |
| | | <view v-if="showAttachmentDetail" class="overlay"> |
| | | <view class="popup attachment-detail-popup"> |
| | | <div class="attachment-popup-header"> |
| | | <h3 class="attachment-popup-title">附件详情</h3> |
| | | <button class="attachment-close-btn" @click="closeAttachmentDetail">返回</button> |
| | | </div> |
| | | |
| | | <div class="attachment-popup-content"> |
| | | <div v-if="selectedAttachment" class="attachment-detail-content"> |
| | | <div class="attachment-detail-header"> |
| | | <div class="file-type-badge large" :class="getFileTypeClass(selectedAttachment.fattach)"> |
| | | {{ getFileTypeIcon(selectedAttachment.fattach) }} |
| | | </div> |
| | | <div class="attachment-detail-title"> |
| | | {{ selectedAttachment.fattach }} |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="attachment-detail-info"> |
| | | <div class="info-row"> |
| | | <div class="info-item"> |
| | | <text class="info-label">ID</text> |
| | | <text class="info-content">{{ Math.trunc(selectedAttachment.id) }}</text> |
| | | </div> |
| | | <div class="info-item"> |
| | | <text class="info-label">类型</text> |
| | | <text class="info-content">{{ selectedAttachment.ftype || '未知类型' }}</text> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="info-row" v-if="selectedAttachment.fversion"> |
| | | <div class="info-item"> |
| | | <text class="info-label">版本</text> |
| | | <text class="info-content">{{ selectedAttachment.fversion }}</text> |
| | | </div> |
| | | <div class="info-item" v-if="selectedAttachment.fdate"> |
| | | <text class="info-label">受控日期</text> |
| | | <text class="info-content">{{ formatDate(selectedAttachment.fdate) }}</text> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="info-row" v-if="selectedAttachment.createBy"> |
| | | <div class="info-item"> |
| | | <text class="info-label">上传人</text> |
| | | <text class="info-content">{{ selectedAttachment.createBy }}</text> |
| | | </div> |
| | | <div class="info-item" v-if="selectedAttachment.createDate"> |
| | | <text class="info-label">上传时间</text> |
| | | <text class="info-content">{{ formatDate(selectedAttachment.createDate) }}</text> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="attachment-detail-actions"> |
| | | <button v-if="isPreviewable(selectedAttachment.fattach)" |
| | | class="btn-primary" |
| | | @click="previewFtpFile(selectedAttachment)">预览</button> |
| | | <button class="btn-success" @click="downloadAttachment(selectedAttachment)">下载</button> |
| | | </div> |
| | | </div> |
| | | <div v-else class="attachment-detail-empty"> |
| | | <div class="empty-icon">❌</div> |
| | | <div class="empty-text">暂无附件信息</div> |
| | | </div> |
| | | </div> |
| | | </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> |
| | | </template> |
| | | |
| | |
| | | remarksPopup: false, |
| | | // ===== 新增LLJ样式相关数据 ===== |
| | | currentTab: 0, |
| | | tabs: [] |
| | | tabs: [], |
| | | // ===== 附件相关数据 ===== |
| | | showAttachmentPopup: false, |
| | | showAttachmentDetail: false, |
| | | showFilePreviewPopup: false, |
| | | attachments: [], |
| | | attachmentsLoading: false, |
| | | selectedAttachment: null, |
| | | previewTitle: '', |
| | | previewContent: '', |
| | | previewType: '', |
| | | previewFileUrl: '' |
| | | } |
| | | }, |
| | | computed: { |
| | |
| | | }).then(res => { |
| | | this.formData = res.data.tbBillList[0]; |
| | | this.formData.billNo = this.billNo; |
| | | |
| | | console.log("获取到的 formData:", this.formData); |
| | | console.log("itemNo 值:", this.formData.itemNo); |
| | | |
| | | if (this.formData.imageData) { |
| | | this.isShowImg = true; |
| | | this.base64Image = 'data:image/jpeg;base64,' + this.formData.imageData; |
| | |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | |
| | | // ===== 附件相关方法 ===== |
| | | viewAttachmentInfo() { |
| | | this.showAttachmentPopup = true; |
| | | this.attachmentsLoading = true; |
| | | this.attachments = []; |
| | | |
| | | // 先获取物料编码,然后获取附件 |
| | | this.$post({ |
| | | url: "/SJ/GetPage", |
| | | data: { |
| | | pageIndex: 1, |
| | | limit: 1, |
| | | id: this.gid |
| | | } |
| | | }).then(res => { |
| | | let tbBillListElement = res.data.tbBillList[0]; |
| | | if (tbBillListElement && tbBillListElement.itemNo) { |
| | | console.log("获取到的物料编码:", tbBillListElement.itemNo); |
| | | |
| | | // 使用获取到的物料编码调用附件接口 |
| | | return this.$post({ |
| | | url: "/SJ/getAttachments", |
| | | data: { itemNo: tbBillListElement.itemNo } |
| | | }); |
| | | } else { |
| | | throw new Error("未找到物料编码信息"); |
| | | } |
| | | }).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" }); |
| | | } |
| | | }).catch(err => { |
| | | this.attachmentsLoading = false; |
| | | console.error("获取附件失败:", err); |
| | | uni.showToast({ title: "获取附件失败,请重试", icon: "none" }); |
| | | }); |
| | | }, |
| | | |
| | | closeAttachmentPopup() { |
| | | this.showAttachmentPopup = false; |
| | | }, |
| | | |
| | | showAttachmentDetailDialog(item) { |
| | | this.selectedAttachment = item; |
| | | this.showAttachmentPopup = false; |
| | | this.showAttachmentDetail = true; |
| | | }, |
| | | |
| | | 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', |
| | | 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', |
| | | 'txt', 'log', 'md', |
| | | 'doc', 'docx', |
| | | 'xls', 'xlsx', |
| | | 'ppt', 'pptx', |
| | | 'csv' |
| | | ].includes(ext); |
| | | }, |
| | | |
| | | // 获取文件类型图标 |
| | | getFileTypeIcon(filename) { |
| | | if (!filename) return '📄'; |
| | | const ext = filename.trim().split('.').pop().toLowerCase(); |
| | | const iconMap = { |
| | | 'pdf': '📕', |
| | | 'jpg': '🖼️', 'jpeg': '🖼️', 'png': '🖼️', 'gif': '🖼️', 'bmp': '🖼️', 'webp': '🖼️', |
| | | 'txt': '📝', 'log': '📝', 'md': '📝', |
| | | 'doc': '📘', 'docx': '📘', |
| | | 'xls': '📊', 'xlsx': '📊', |
| | | 'ppt': '📙', 'pptx': '📙', |
| | | 'csv': '📊', |
| | | 'zip': '📦', 'rar': '📦', '7z': '📦', |
| | | 'dwg': '🏗️', 'dxf': '🏗️' |
| | | }; |
| | | return iconMap[ext] || '📄'; |
| | | }, |
| | | |
| | | // 获取文件类型CSS类 |
| | | getFileTypeClass(filename) { |
| | | if (!filename) return 'file-unknown'; |
| | | const ext = filename.trim().split('.').pop().toLowerCase(); |
| | | const classMap = { |
| | | 'pdf': 'file-pdf', |
| | | 'jpg': 'file-image', 'jpeg': 'file-image', 'png': 'file-image', 'gif': 'file-image', 'bmp': 'file-image', 'webp': 'file-image', |
| | | 'txt': 'file-text', 'log': 'file-text', 'md': 'file-text', |
| | | 'doc': 'file-word', 'docx': 'file-word', |
| | | 'xls': 'file-excel', 'xlsx': 'file-excel', |
| | | 'ppt': 'file-powerpoint', 'pptx': 'file-powerpoint', |
| | | 'csv': 'file-excel', |
| | | 'zip': 'file-archive', 'rar': 'file-archive', '7z': 'file-archive', |
| | | 'dwg': 'file-cad', 'dxf': 'file-cad' |
| | | }; |
| | | return classMap[ext] || 'file-unknown'; |
| | | }, |
| | | |
| | | // 格式化日期 |
| | | formatDate(dateString) { |
| | | if (!dateString) return ''; |
| | | try { |
| | | const date = new Date(dateString); |
| | | return date.toLocaleDateString('zh-CN', { |
| | | year: 'numeric', |
| | | month: '2-digit', |
| | | day: '2-digit', |
| | | hour: '2-digit', |
| | | minute: '2-digit' |
| | | }); |
| | | } catch (e) { |
| | | return dateString; |
| | | } |
| | | }, |
| | | |
| | | downloadAttachment(item) { |
| | | const fileName = item.fattach.replace(/[\s\u3000\r\n]+/g, '').trim(); |
| | | const downloadUrl = this.$store.state.serverInfo.serverAPI + "/SJ/DownloadFtpFile?itemNo=" + |
| | | encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) + |
| | | "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer); |
| | | |
| | | uni.downloadFile({ |
| | | url: downloadUrl, |
| | | success: (res) => { |
| | | if (res.statusCode === 200) { |
| | | uni.showToast({ title: '下载成功', icon: 'success' }); |
| | | } else { |
| | | uni.showToast({ title: '下载失败', icon: 'none' }); |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | console.error('下载失败:', err); |
| | | uni.showToast({ title: '下载失败,请重试', icon: 'none' }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | 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 + "/SJ/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 { |
| | | this.previewOfficeFile(previewUrl, fileName); |
| | | } |
| | | }, |
| | | |
| | | previewPdfFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewContent = url; |
| | | this.previewType = 'pdf'; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | }, |
| | | |
| | | previewImageFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewContent = url; |
| | | this.previewType = 'image'; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | }, |
| | | |
| | | previewTextFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewType = 'text'; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | |
| | | uni.request({ |
| | | url: url, |
| | | method: 'GET', |
| | | success: (res) => { |
| | | this.previewContent = res.data; |
| | | }, |
| | | fail: (err) => { |
| | | this.previewContent = '预览失败,请下载后查看'; |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | previewOfficeFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewContent = ''; |
| | | this.previewType = 'excel'; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | }, |
| | | |
| | | closeFilePreview() { |
| | | this.showFilePreviewPopup = false; |
| | | this.showAttachmentDetail = true; |
| | | }, |
| | | |
| | | downloadPreviewFile() { |
| | | if (this.previewFileUrl && this.selectedAttachment) { |
| | | this.downloadAttachment(this.selectedAttachment); |
| | | } |
| | | }, |
| | | |
| | | previewImageInPopup() { |
| | | // 在新窗口中打开图片进行放大查看 |
| | | if (this.previewContent) { |
| | | uni.previewImage({ |
| | | urls: [this.previewContent], |
| | | current: this.previewContent |
| | | }); |
| | | } |
| | | } |
| | | }, |
| | | onLoad(options) { |
| | |
| | | .upload-btn { |
| | | background-color: #909399; |
| | | color: #fff; |
| | | padding: 0 10px; |
| | | padding: 10px 12px; |
| | | margin: 0; |
| | | font-size: 14px; |
| | | min-height: 44px; |
| | | white-space: nowrap; |
| | | flex-shrink: 0; |
| | | min-width: 80px; |
| | | max-width: 120px; |
| | | flex: 1; |
| | | } |
| | | |
| | | .attachment-btn { |
| | | background-color: #17a2b8; |
| | | color: #fff; |
| | | padding: 10px 12px; |
| | | margin: 0; |
| | | font-size: 14px; |
| | | min-height: 44px; |
| | | white-space: nowrap; |
| | | flex-shrink: 0; |
| | | min-width: 80px; |
| | | max-width: 120px; |
| | | flex: 1; |
| | | } |
| | | } |
| | | } |
| | |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | z-index: 1000; |
| | | z-index: 2000; |
| | | } |
| | | |
| | | .popup { |
| | | background-color: #fff; |
| | | padding: 20px; |
| | | border: 1px solid #ccc; |
| | | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); |
| | | padding: 0; |
| | | border: none; |
| | | box-shadow: 0 8px 32px rgba(60,60,60,0.18); |
| | | width: 68vw; |
| | | height: 25vh; |
| | | border-radius: 8px; |
| | | max-width: 600px; |
| | | border-radius: 16px; |
| | | overflow: hidden; |
| | | position: relative; |
| | | z-index: 2001; |
| | | max-height: 80vh; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .popup-header { |
| | |
| | | background-color: #95a5a6; |
| | | color: white; |
| | | } |
| | | |
| | | &:hover { |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 2px 8px rgba(0,0,0,0.15); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | .input-label { |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .input-wrapper { |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .upload-btn, |
| | | .attachment-btn { |
| | | min-width: 70px; |
| | | max-width: 100px; |
| | | padding: 8px 10px; |
| | | font-size: 13px; |
| | | min-height: 40px; |
| | | } |
| | | } |
| | | |
| | | /* 附件相关样式 */ |
| | | .attachment-list-popup { |
| | | width: 80vw; |
| | | max-width: 800px; |
| | | max-height: 85vh; |
| | | } |
| | | |
| | | .attachment-detail-popup { |
| | | width: 70vw; |
| | | max-width: 600px; |
| | | } |
| | | |
| | | .attachment-popup-header { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #eee; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | background-color: white; |
| | | } |
| | | |
| | | .attachment-popup-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | margin: 0; |
| | | } |
| | | |
| | | .attachment-close-btn { |
| | | padding: 8px 16px; |
| | | border: 1px solid #ddd; |
| | | border-radius: 4px; |
| | | background-color: white; |
| | | font-size: 14px; |
| | | transition: all 0.2s; |
| | | color: #2c3e50; |
| | | } |
| | | |
| | | .attachment-close-btn:hover { |
| | | background-color: #f8f9fa; |
| | | } |
| | | |
| | | .attachment-popup-content { |
| | | padding: 16px; |
| | | max-height: 60vh; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | /* 加载状态 */ |
| | | .attachment-loading { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 40px 20px; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .loading-spinner { |
| | | width: 32px; |
| | | height: 32px; |
| | | border: 3px solid #f3f3f3; |
| | | border-top: 3px solid #3498db; |
| | | border-radius: 50%; |
| | | animation: spin 1s linear infinite; |
| | | } |
| | | |
| | | @keyframes spin { |
| | | 0% { transform: rotate(0deg); } |
| | | 100% { transform: rotate(360deg); } |
| | | } |
| | | |
| | | .loading-text { |
| | | font-size: 14px; |
| | | color: #7f8c8d; |
| | | } |
| | | |
| | | /* 空状态 */ |
| | | .attachment-empty { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 40px 20px; |
| | | gap: 12px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .empty-icon { |
| | | font-size: 36px; |
| | | opacity: 0.6; |
| | | } |
| | | |
| | | .empty-text { |
| | | font-size: 16px; |
| | | color: #7f8c8d; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .empty-hint { |
| | | font-size: 14px; |
| | | color: #95a5a6; |
| | | } |
| | | |
| | | /* 附件列表布局 */ |
| | | .attachment-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .attachment-item { |
| | | background-color: white; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); |
| | | overflow: hidden; |
| | | transition: all 0.3s; |
| | | border: 1px solid #eee; |
| | | } |
| | | |
| | | .attachment-item:hover { |
| | | box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .attachment-info { |
| | | padding: 16px; |
| | | border-bottom: 1px solid #eee; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .file-type-badge { |
| | | width: 40px; |
| | | height: 40px; |
| | | border-radius: 8px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 20px; |
| | | background: #f8f9fa; |
| | | border: 2px solid #e9ecef; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .file-type-badge.large { |
| | | width: 56px; |
| | | height: 56px; |
| | | font-size: 28px; |
| | | } |
| | | |
| | | .file-type-badge.file-pdf { background: #ffe6e6; border-color: #ffcccc; } |
| | | .file-type-badge.file-image { background: #e6f3ff; border-color: #cce7ff; } |
| | | .file-type-badge.file-text { background: #e6ffe6; border-color: #ccffcc; } |
| | | .file-type-badge.file-word { background: #e6f0ff; border-color: #cce0ff; } |
| | | .file-type-badge.file-excel { background: #e6ffe6; border-color: #ccffcc; } |
| | | .file-type-badge.file-powerpoint { background: #fff0e6; border-color: #ffe0cc; } |
| | | .file-type-badge.file-archive { background: #f0e6ff; border-color: #e0ccff; } |
| | | .file-type-badge.file-cad { background: #e6fff0; border-color: #ccffe0; } |
| | | .file-type-badge.file-unknown { background: #f5f5f5; border-color: #e0e0e0; } |
| | | |
| | | .attachment-details { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .attachment-name { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | cursor: pointer; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | margin-bottom: 8px; |
| | | transition: color 0.2s; |
| | | } |
| | | |
| | | .attachment-name:hover { |
| | | color: #3498db; |
| | | } |
| | | |
| | | .attachment-meta { |
| | | display: flex; |
| | | gap: 16px; |
| | | font-size: 12px; |
| | | color: #95a5a6; |
| | | } |
| | | |
| | | .meta-type { |
| | | background-color: #ecf0f1; |
| | | padding: 2px 6px; |
| | | border-radius: 10px; |
| | | color: #7f8c8d; |
| | | } |
| | | |
| | | .meta-version { |
| | | background-color: #e8f5e8; |
| | | padding: 2px 6px; |
| | | border-radius: 10px; |
| | | color: #2e7d32; |
| | | } |
| | | |
| | | .meta-date { |
| | | background-color: #fff3e0; |
| | | padding: 2px 6px; |
| | | border-radius: 10px; |
| | | color: #f57c00; |
| | | } |
| | | |
| | | .attachment-actions { |
| | | padding: 12px 16px; |
| | | border-top: 1px solid #eee; |
| | | display: flex; |
| | | gap: 8px; |
| | | background-color: #f8f9fa; |
| | | } |
| | | |
| | | /* 按钮样式 */ |
| | | .btn-secondary { |
| | | padding: 8px 16px; |
| | | border: 1px solid #ddd; |
| | | border-radius: 4px; |
| | | background-color: white; |
| | | font-size: 14px; |
| | | transition: all 0.2s; |
| | | color: #2c3e50; |
| | | flex: 1; |
| | | } |
| | | |
| | | .btn-secondary:hover { |
| | | background-color: #f8f9fa; |
| | | } |
| | | |
| | | .btn-primary { |
| | | padding: 8px 16px; |
| | | border: 1px solid #3498db; |
| | | border-radius: 4px; |
| | | background-color: #3498db; |
| | | color: white; |
| | | font-size: 14px; |
| | | transition: all 0.2s; |
| | | flex: 1; |
| | | } |
| | | |
| | | .btn-primary:hover { |
| | | background-color: #2980b9; |
| | | } |
| | | |
| | | .btn-success { |
| | | padding: 8px 16px; |
| | | border: 1px solid #2ecc71; |
| | | border-radius: 4px; |
| | | background-color: #2ecc71; |
| | | color: white; |
| | | font-size: 14px; |
| | | transition: all 0.2s; |
| | | flex: 1; |
| | | } |
| | | |
| | | .btn-success:hover { |
| | | background-color: #27ae60; |
| | | } |
| | | |
| | | /* 附件详情样式 */ |
| | | .attachment-detail-header { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | padding-bottom: 16px; |
| | | border-bottom: 1px solid #eee; |
| | | } |
| | | |
| | | .attachment-detail-title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | flex: 1; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .attachment-detail-info { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | |
| | | .attachment-detail-actions { |
| | | padding: 12px 16px; |
| | | border-top: 1px solid #eee; |
| | | display: flex; |
| | | gap: 8px; |
| | | background-color: #f8f9fa; |
| | | } |
| | | |
| | | /* 文件预览弹窗样式 */ |
| | | .file-preview-popup { |
| | | width: 80vw; |
| | | max-width: 800px; |
| | | max-height: 80vh; |
| | | } |
| | | |
| | | .file-preview-title { |
| | | padding: 20px; |
| | | margin: 0; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | border-bottom: 1px solid #eee; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .file-preview-content { |
| | | padding: 20px; |
| | | max-height: 60vh; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .file-preview-content pre { |
| | | white-space: pre-wrap; |
| | | word-wrap: break-word; |
| | | font-family: 'Courier New', monospace; |
| | | font-size: 12px; |
| | | line-height: 1.4; |
| | | background: #f8f9fa; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | border: 1px solid #e9ecef; |
| | | } |
| | | |
| | | .image-preview-container { |
| | | text-align: center; |
| | | } |
| | | |
| | | .image-zoom-hint { |
| | | margin-top: 10px; |
| | | font-size: 12px; |
| | | color: #7f8c8d; |
| | | } |
| | | |
| | | .unsupported-preview { |
| | | text-align: center; |
| | | padding: 40px 20px; |
| | | } |
| | | |
| | | .unsupported-icon { |
| | | font-size: 48px; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .unsupported-text { |
| | | font-size: 16px; |
| | | color: #7f8c8d; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .unsupported-hint { |
| | | font-size: 14px; |
| | | color: #95a5a6; |
| | | } |
| | | |
| | | .file-preview-actions { |
| | | padding: 15px 20px; |
| | | border-top: 1px solid #eee; |
| | | display: flex; |
| | | gap: 10px; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .file-preview-btn { |
| | | padding: 8px 16px; |
| | | border-radius: 4px; |
| | | border: none; |
| | | font-size: 14px; |
| | | cursor: pointer; |
| | | transition: all 0.2s; |
| | | } |
| | | |
| | | .file-preview-btn.download-btn { |
| | | background: #2ecc71; |
| | | color: white; |
| | | } |
| | | |
| | | .file-preview-btn.close-btn { |
| | | background: #95a5a6; |
| | | color: white; |
| | | } |
| | | |
| | | .file-preview-btn:hover { |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 2px 8px rgba(0,0,0,0.15); |
| | | } |
| | | </style> |
| | |
| | | </button> |
| | | <button class="action-btn secondary" v-if="!isUpdate && !isShowTable" @click="viewAttachmentInfo">查看附件信息</button> |
| | | |
| | | <button class="action-btn danger" v-if="!isUpdate && !formData.fcheckResu && !isShowTable && formData.fsubmit !== 1" @click="removeXJ">删除单据</button> |
| | | <button class="action-btn danger" v-if="!isUpdate && !formData.fcheckResu && !isShowTable && formData.fsubmit !== 1" @click="showDeleteConfirmDialog">删除单据</button> |
| | | <button class="action-btn warning" v-if="!isUpdate && !isShowTable && formData.fsubmit !== 1" @click="saveRemarks">添加不合格描述</button> |
| | | |
| | | <!-- 检验项目管理按钮 --> |
| | |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 删除确认弹窗 --> |
| | | <view v-if="showDeleteConfirm" class="overlay"> |
| | | <view class="popup delete-confirm-popup"> |
| | | <h3 class="delete-confirm-title">⚠️ 确认删除</h3> |
| | | <div class="delete-confirm-divider"></div> |
| | | <div class="delete-confirm-content"> |
| | | <view class="delete-warning-icon">🗑️</view> |
| | | <view class="delete-warning-text">您确定要删除此检验单吗?</view> |
| | | <view class="delete-warning-detail">删除后将无法恢复,请谨慎操作!</view> |
| | | <view class="delete-countdown"> |
| | | <view class="countdown-text">确认按钮将在 <text class="countdown-number">{{ deleteCountdown }}</text> 秒后可用</view> |
| | | <view class="countdown-progress"> |
| | | <view class="countdown-bar" :style="{ width: countdownProgress + '%' }"></view> |
| | | </view> |
| | | </view> |
| | | </div> |
| | | <div class="delete-confirm-actions"> |
| | | <button class="delete-confirm-btn cancel-btn" @click="cancelDelete">取消</button> |
| | | <button class="delete-confirm-btn confirm-btn" |
| | | :disabled="deleteCountdown > 0" |
| | | :class="{ 'disabled': deleteCountdown > 0 }" |
| | | @click="confirmDelete">确认删除</button> |
| | | </div> |
| | | </view> |
| | | </view> |
| | | |
| | | |
| | | </view> |
| | | </template> |
| | |
| | | previewTitle: '', |
| | | previewContent: '', |
| | | previewType: '', |
| | | previewFileUrl: '' |
| | | previewFileUrl: '', |
| | | |
| | | // 删除确认相关数据 |
| | | showDeleteConfirm: false, |
| | | deleteCountdown: 5, |
| | | countdownProgress: 0, |
| | | deleteTimer: null |
| | | |
| | | }; |
| | | }, |
| | |
| | | } else { |
| | | return '检验项目详情'; |
| | | } |
| | | }, |
| | | |
| | | // 显示删除确认弹窗 |
| | | showDeleteConfirmDialog() { |
| | | this.showDeleteConfirm = true; |
| | | this.deleteCountdown = 5; |
| | | this.countdownProgress = 0; |
| | | this.startDeleteCountdown(); |
| | | }, |
| | | |
| | | // 开始倒计时 |
| | | startDeleteCountdown() { |
| | | this.deleteTimer = setInterval(() => { |
| | | this.deleteCountdown--; |
| | | this.countdownProgress = ((5 - this.deleteCountdown) / 5) * 100; |
| | | |
| | | if (this.deleteCountdown <= 0) { |
| | | clearInterval(this.deleteTimer); |
| | | this.deleteTimer = null; |
| | | } |
| | | }, 1000); |
| | | }, |
| | | |
| | | // 取消删除 |
| | | cancelDelete() { |
| | | this.showDeleteConfirm = false; |
| | | if (this.deleteTimer) { |
| | | clearInterval(this.deleteTimer); |
| | | this.deleteTimer = null; |
| | | } |
| | | this.deleteCountdown = 5; |
| | | this.countdownProgress = 0; |
| | | }, |
| | | |
| | | // 确认删除 |
| | | confirmDelete() { |
| | | if (this.deleteCountdown > 0) { |
| | | return; |
| | | } |
| | | |
| | | this.showDeleteConfirm = false; |
| | | if (this.deleteTimer) { |
| | | clearInterval(this.deleteTimer); |
| | | this.deleteTimer = null; |
| | | } |
| | | |
| | | this.removeXJ(); |
| | | }, |
| | | |
| | | removeXJ() { |
| | |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | white-space: nowrap; |
| | | flex-shrink: 0; |
| | | min-width: 80px; |
| | | max-width: 120px; |
| | | flex: 1; |
| | | } |
| | | |
| | | .action-btn:hover { |
| | |
| | | padding: 8px 10px; |
| | | font-size: 11px; |
| | | min-height: 40px; |
| | | flex: 1; |
| | | } |
| | | |
| | | .action-btn.small { |
| | |
| | | font-size: 28px; |
| | | } |
| | | } |
| | | |
| | | /* 删除确认弹窗样式 */ |
| | | .delete-confirm-popup { |
| | | width: 90vw; |
| | | max-width: 400px; |
| | | max-height: 70vh; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .delete-confirm-title { |
| | | font-size: 20px; |
| | | font-weight: 700; |
| | | color: #e74c3c; |
| | | margin-bottom: 8px; |
| | | text-align: center; |
| | | letter-spacing: 1px; |
| | | } |
| | | |
| | | .delete-confirm-divider { |
| | | height: 2px; |
| | | background: linear-gradient(90deg, #e74c3c 0%, #c0392b 100%); |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .delete-confirm-content { |
| | | text-align: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .delete-warning-icon { |
| | | font-size: 48px; |
| | | margin-bottom: 16px; |
| | | animation: shake 0.5s ease-in-out infinite alternate; |
| | | } |
| | | |
| | | @keyframes shake { |
| | | 0% { transform: translateX(-2px); } |
| | | 100% { transform: translateX(2px); } |
| | | } |
| | | |
| | | .delete-warning-text { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .delete-warning-detail { |
| | | font-size: 14px; |
| | | color: #7f8c8d; |
| | | margin-bottom: 20px; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | .delete-countdown { |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | padding: 16px; |
| | | margin: 16px 0; |
| | | border: 1px solid #e9ecef; |
| | | } |
| | | |
| | | .countdown-text { |
| | | font-size: 14px; |
| | | color: #495057; |
| | | margin-bottom: 8px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .countdown-number { |
| | | font-weight: 700; |
| | | color: #e74c3c; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .countdown-progress { |
| | | width: 100%; |
| | | height: 6px; |
| | | background: #e9ecef; |
| | | border-radius: 3px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .countdown-bar { |
| | | height: 100%; |
| | | background: linear-gradient(90deg, #e74c3c, #c0392b); |
| | | border-radius: 3px; |
| | | transition: width 1s ease; |
| | | } |
| | | |
| | | .delete-confirm-actions { |
| | | display: flex; |
| | | gap: 12px; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .delete-confirm-btn { |
| | | padding: 12px 24px; |
| | | border: none; |
| | | border-radius: 8px; |
| | | font-size: 16px; |
| | | 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); |
| | | } |
| | | |
| | | .delete-confirm-btn.cancel-btn { |
| | | background: linear-gradient(135deg, #95a5a6, #7f8c8d); |
| | | color: white; |
| | | } |
| | | |
| | | .delete-confirm-btn.cancel-btn:hover { |
| | | background: linear-gradient(135deg, #7f8c8d, #6c7b7d); |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | .delete-confirm-btn.confirm-btn { |
| | | background: linear-gradient(135deg, #e74c3c, #c0392b); |
| | | color: white; |
| | | } |
| | | |
| | | .delete-confirm-btn.confirm-btn:hover:not(.disabled) { |
| | | background: linear-gradient(135deg, #c0392b, #a93226); |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | .delete-confirm-btn.disabled { |
| | | background: #bdc3c7; |
| | | color: #7f8c8d; |
| | | cursor: not-allowed; |
| | | transform: none; |
| | | box-shadow: none; |
| | | } |
| | | </style> |
| | |
| | | <!-- 顶部筛选区 --> |
| | | <view class="filter-section"> |
| | | <view class="filter-controls"> |
| | | <!-- 查询条件选择器 --> |
| | | <view class="dropdown-filter"> |
| | | <picker @change="onOptionsChange" :value="optionsIndex" :range="options"> |
| | | <view class="picker">{{options[optionsIndex]}}</view> |
| | | </picker> |
| | | </view> |
| | | |
| | | <!-- 搜索框 --> |
| | | <view class="search-container"> |
| | | <input class="search-input" v-model="searchValue" :placeholder="'请输入'+options[optionsIndex]" @confirm="btnclicked" /> |
| | | <button class="search-button" @click="btnclicked">搜索</button> |
| | | </view> |
| | | |
| | | <!-- 状态切换标签 --> |
| | | <view class="status-tabs"> |
| | | <button :class="['tab-button', current === 0 ? 'active' : '']" @click="onClickItem({currentIndex: 0})"> |
| | | {{items[0]}} |
| | | 未提交({{unsubmittedCount}}) |
| | | </button> |
| | | <button :class="['tab-button', current === 1 ? 'active' : '']" @click="onClickItem({currentIndex: 1})"> |
| | | {{items[1]}} |
| | | 已提交({{submittedCount}}) |
| | | </button> |
| | | </view> |
| | | </view> |
| | |
| | | items: ['未提交', '已提交'], |
| | | current: 0, |
| | | data: [], |
| | | tipShow: false // 是否显示顶部提示框 |
| | | tipShow: false, // 是否显示顶部提示框 |
| | | searchValue: '', |
| | | // ===== 新增搜索功能相关数据 ===== |
| | | optionsIndex: 0, // 当前选择的查询条件索引 |
| | | options: ['工单', '检验单号', '产线', '物料编码', '物料名称'], // 查询条件选项 |
| | | selectedField: 'billNo', // 当前选择的查询字段,默认为工单 |
| | | // ===== 新增数量统计变量 ===== |
| | | unsubmittedCount: 0, // 未提交数量 |
| | | submittedCount: 0 // 已提交数量 |
| | | }; |
| | | }, |
| | | onLoad() { |
| | |
| | | this.init(); |
| | | }, |
| | | methods: { |
| | | // ===== 新增查询条件选择方法 ===== |
| | | onOptionsChange(e) { |
| | | this.optionsIndex = e.detail.value; |
| | | // 根据选择的选项设置搜索字段 |
| | | const fieldMap = { |
| | | 0: 'billNo', // 工单 |
| | | 1: 'releaseNo', // 检验单号 |
| | | 2: 'daa020', // 产线 |
| | | 3: 'itemNo', // 物料编码 |
| | | 4: 'itemName' // 物料名称 |
| | | }; |
| | | this.selectedField = fieldMap[this.optionsIndex]; |
| | | }, |
| | | |
| | | //搜索框点击事件 |
| | | btnclicked() { |
| | | this.init(); |
| | | }, |
| | | |
| | | init() { |
| | | |
| | | //获取搜索条件内容 |
| | | let SearchValue = this.searchValue; |
| | | |
| | | let result = "未完成"; |
| | | if (this.current === 1) { |
| | | result = "已完成"; |
| | |
| | | pageIndex: 1, |
| | | limit: 20, |
| | | createUser: this.$loginInfo.account, |
| | | result: result |
| | | result: result, |
| | | SearchValue: SearchValue, |
| | | selectedIndex: this.optionsIndex, // 新增:搜索条件索引 |
| | | searchField: this.selectedField // 新增:搜索字段名 |
| | | } |
| | | }).then(res => { |
| | | this.data = res.data.tbBillList; |
| | | // 设置数量统计 |
| | | if (this.current === 1) { |
| | | this.submittedCount = res.totalCount; |
| | | } else { |
| | | this.unsubmittedCount = res.totalCount; |
| | | } |
| | | }) |
| | | }, |
| | | handleFabClick() { |
| | |
| | | |
| | | .filter-controls { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | } |
| | | |
| | | /* 查询条件选择器 */ |
| | | .dropdown-filter { |
| | | min-width: 80px; |
| | | margin-right: 10px; |
| | | } |
| | | |
| | | .picker { |
| | | padding: 8px 12px; |
| | | border: 1px solid #ddd; |
| | | border-radius: 4px; |
| | | background-color: white; |
| | | font-size: 14px; |
| | | min-width: 80px; |
| | | } |
| | | |
| | | /* 搜索框样式 */ |
| | | .search-container { |
| | | display: flex; |
| | | flex: 1; |
| | | margin-right: 10px; |
| | | height: 36px; |
| | | min-width: 200px; |
| | | } |
| | | |
| | | .search-input { |
| | | flex: 1; |
| | | padding: 8px 12px; |
| | | border: 1px solid #ddd; |
| | | border-radius: 4px 0 0 4px; |
| | | font-size: 14px; |
| | | background-color: white; |
| | | } |
| | | |
| | | .search-button { |
| | | padding: 0 12px; |
| | | border: 1px solid #3498db; |
| | | border-radius: 0 4px 4px 0; |
| | | background-color: #3498db; |
| | | color: white; |
| | | font-size: 14px; |
| | | margin: 0; |
| | | } |
| | | |
| | | .status-tabs { |
| | |
| | | border-radius: 4px; |
| | | overflow: hidden; |
| | | background-color: #ecf0f1; |
| | | width: 300px; |
| | | flex: 1; |
| | | min-width: 200px; |
| | | } |
| | | |
| | | .tab-button { |
| | |
| | | serverURLInt:'http://192.168.11.251:10055',//服务器体检 10.0.1.104:10054 |
| | | serverURL:'http://localhost:10055',//本地调试地址 |
| | | //serverAPI:'http://localhost:5184/api',//当前正在使用的服务器,默认为外网 localhost |
| | | //serverAPI:'http://192.168.1.22:10054/api',//内网 |
| | | serverAPI:'http://36.26.21.214:10054/api', |
| | | serverAPI:'http://192.168.1.22:10054/api',//内网 |
| | | //serverAPI:'http://36.26.21.214:10054/api', |
| | | ftpServer:'ftp://36.26.21.214',//FTP服务器地址 |
| | | } |
| | | }, |