| | |
| | | </view> |
| | | |
| | | <view class="upload-bar"> |
| | | <view class="btn-upload" @click="chooseFile">上传附件</view> |
| | | <view class="btn-upload" @tap="chooseFile">上传附件</view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | |
| | | uni.showToast({ title: '缺少检验记录ID', icon: 'none' }) |
| | | return |
| | | } |
| | | uni.chooseFile({ |
| | | count: 1, |
| | | success: (res) => { |
| | | const path = res?.tempFiles?.[0]?.path |
| | | const name = res?.tempFiles?.[0]?.name |
| | | if (!path) return |
| | | uploadOne(path, name) |
| | | const pickByChooseFile = () => { |
| | | if (typeof uni.chooseFile !== 'function') return false |
| | | uni.chooseFile({ |
| | | count: 1, |
| | | success: (res) => { |
| | | const file = res?.tempFiles?.[0] |
| | | const path = file?.path |
| | | const name = file?.name |
| | | if (!path) return |
| | | uploadOne(path, name) |
| | | }, |
| | | fail: () => { |
| | | // chooseFile 在部分 App 机型/基座可能会失败,做降级 |
| | | pickByChooseImage() |
| | | } |
| | | }) |
| | | return true |
| | | } |
| | | |
| | | const pickByChooseImage = () => { |
| | | if (typeof uni.chooseImage !== 'function') { |
| | | uni.showToast({ title: '当前版本不支持选择文件', icon: 'none' }) |
| | | return |
| | | } |
| | | }) |
| | | uni.chooseImage({ |
| | | count: 1, |
| | | success: (res) => { |
| | | const path = res?.tempFilePaths?.[0] |
| | | if (!path) return |
| | | uploadOne(path, '图片附件') |
| | | }, |
| | | fail: () => { |
| | | uni.showToast({ title: '选择失败', icon: 'none' }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // App/H5 统一走 chooseFile,失败时降级 chooseImage |
| | | const ok = pickByChooseFile() |
| | | if (!ok) pickByChooseImage() |
| | | } |
| | | |
| | | const uploadOne = (filePath, originalName) => { |
| | |
| | | } |
| | | |
| | | const previewFile = (f) => { |
| | | const url = f?.url |
| | | const rawUrl = f?.url |
| | | if (!rawUrl) return |
| | | |
| | | const url = normalizeUrl(rawUrl) |
| | | const name = f?.name || '附件' |
| | | if (!url) return |
| | | // H5/APP 统一用外部打开 |
| | | uni.navigateTo({ |
| | | url: `/pages/inspectionUpload/filePreview?url=${encodeURIComponent(url)}` |
| | | |
| | | // 图片优先用原生预览 |
| | | if (isImageUrl(url) || isImageName(name)) { |
| | | uni.previewImage({ urls: [url] }) |
| | | return |
| | | } |
| | | |
| | | // 非图片:下载后打开 |
| | | const token = getToken() |
| | | uni.showLoading({ title: '打开中...', mask: true }) |
| | | uni.downloadFile({ |
| | | url, |
| | | header: token ? { Authorization: 'Bearer ' + token } : undefined, |
| | | success: (res) => { |
| | | const filePath = res?.tempFilePath |
| | | if (!filePath) { |
| | | uni.hideLoading() |
| | | uni.showToast({ title: '打开失败', icon: 'none' }) |
| | | return |
| | | } |
| | | uni.openDocument({ |
| | | filePath, |
| | | showMenu: true, |
| | | success: () => { |
| | | uni.hideLoading() |
| | | }, |
| | | fail: () => { |
| | | uni.hideLoading() |
| | | uni.showToast({ title: '打开失败', icon: 'none' }) |
| | | } |
| | | }) |
| | | }, |
| | | fail: () => { |
| | | uni.hideLoading() |
| | | uni.showToast({ title: '下载失败', icon: 'none' }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const normalizeUrl = (u) => { |
| | | const s = String(u || '').trim() |
| | | if (!s) return '' |
| | | if (s.startsWith('http://') || s.startsWith('https://')) return s |
| | | if (s.startsWith('//')) return 'https:' + s |
| | | // 系统里附件预览/下载一般走 fileUrl |
| | | if (s.startsWith('/')) return `${config.fileUrl}${s}` |
| | | return `${config.fileUrl}/${s.replace(/^\//, '')}` |
| | | } |
| | | |
| | | const isImageName = (name) => { |
| | | const n = String(name || '').toLowerCase() |
| | | return /\.(png|jpe?g|gif|bmp|webp|heic)$/.test(n) |
| | | } |
| | | |
| | | const isImageUrl = (url) => { |
| | | const u = String(url || '').toLowerCase() |
| | | return /\.(png|jpe?g|gif|bmp|webp|heic)(\?|#|$)/.test(u) |
| | | } |
| | | |
| | | const confirmDelete = (f) => { |
| | |
| | | .file-list { margin: 24rpx; background: #fff; border-radius: 16rpx; padding: 12rpx 24rpx; } |
| | | .file-item { padding: 20rpx 0; border-bottom: 1rpx solid #eee; display: flex; justify-content: space-between; align-items: center; gap: 16rpx; } |
| | | .file-item:last-child { border-bottom: 0; } |
| | | .file-name { font-size: 28rpx; color: #333; } |
| | | .file-actions { display: flex; gap: 20rpx; } |
| | | .file-info { flex: 1; min-width: 0; } |
| | | .file-name { |
| | | display: block; |
| | | width: 420rpx; |
| | | max-width: 100%; |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | .file-actions { display: flex; gap: 20rpx; flex-shrink: 0; } |
| | | .btn-link { color: #2979ff; font-size: 26rpx; } |
| | | .btn-link.danger { color: #f56c6c; } |
| | | .empty { text-align: center; padding: 60rpx 0; color: #999; font-size: 28rpx; } |
| | | .upload-bar { position: fixed; left: 0; right: 0; bottom: 0; padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom)); background: #fff; box-shadow: 0 -4rpx 16rpx rgba(0,0,0,0.04); } |
| | | .upload-bar { position: fixed; left: 0; right: 0; bottom: 0; padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom)); background: #fff; box-shadow: 0 -4rpx 16rpx rgba(0,0,0,0.04); z-index: 20; } |
| | | .btn-upload { height: 88rpx; border-radius: 999rpx; background: #2979ff; color: #fff; font-size: 30rpx; display: flex; align-items: center; justify-content: center; } |
| | | </style> |
| | | |