340 lines
8.0 KiB
Vue
340 lines
8.0 KiB
Vue
<template>
|
||
<view class="image-upload-page">
|
||
<view class="content">
|
||
<!-- 上传区域 -->
|
||
<view class="upload-area" @click="chooseImage">
|
||
<view v-if="!imageUrl" class="upload-placeholder">
|
||
<u-icon color="#ccc" name="camera" size="60"></u-icon>
|
||
<text class="upload-text">点击选择图片</text>
|
||
<text class="upload-hint">支持 JPG、PNG 格式</text>
|
||
</view>
|
||
<image v-else :src="imageUrl" class="preview-image" mode="aspectFit"></image>
|
||
</view>
|
||
|
||
<!-- 上传按钮 -->
|
||
<button
|
||
:disabled="!selectedImagePath || uploading"
|
||
:loading="uploading"
|
||
class="upload-btn"
|
||
@click="uploadImage"
|
||
>
|
||
{{ uploading ? '上传中...' : '上传图片' }}
|
||
</button>
|
||
|
||
<!-- 结果展示 -->
|
||
<view v-if="uploadResult" class="result-section">
|
||
<view class="result-item">
|
||
<text class="result-label">上传状态:</text>
|
||
<text class="result-value success">成功</text>
|
||
</view>
|
||
<view class="result-item">
|
||
<text class="result-label">图片URL:</text>
|
||
<text class="result-url" @click="copyUrl">{{ uploadResult }}</text>
|
||
</view>
|
||
<button class="copy-btn" @click="copyUrl">复制URL</button>
|
||
</view>
|
||
|
||
<!-- 错误信息 -->
|
||
<view v-if="errorMessage" class="error-section">
|
||
<text class="error-text">{{ errorMessage }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { getQiniuUploadToken, uploadToQiniu } from '@/api/upload.js'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
selectedImagePath: '', // 选择的图片路径
|
||
imageUrl: '', // 预览图片URL
|
||
uploadResult: '', // 上传成功后的完整URL
|
||
uploading: false, // 上传状态
|
||
errorMessage: '', // 错误信息
|
||
qiniuToken: '', // 七牛云上传token
|
||
}
|
||
},
|
||
|
||
onLoad() {
|
||
this.getQiniuToken()
|
||
},
|
||
|
||
methods: {
|
||
// 选择图片
|
||
chooseImage() {
|
||
uni.chooseImage({
|
||
count: 1,
|
||
sizeType: ['compressed'],
|
||
sourceType: ['album', 'camera'],
|
||
success: res => {
|
||
this.selectedImagePath = res.tempFilePaths[0]
|
||
this.imageUrl = res.tempFilePaths[0]
|
||
this.uploadResult = ''
|
||
this.errorMessage = ''
|
||
},
|
||
fail: err => {
|
||
console.error('选择图片失败:', err)
|
||
uni.showToast({
|
||
title: '选择图片失败',
|
||
icon: 'none',
|
||
})
|
||
},
|
||
})
|
||
},
|
||
|
||
// 上传图片
|
||
async uploadImage() {
|
||
if (!this.selectedImagePath) {
|
||
uni.showToast({
|
||
title: '请先选择图片',
|
||
icon: 'none',
|
||
})
|
||
return
|
||
}
|
||
|
||
this.uploading = true
|
||
this.errorMessage = ''
|
||
|
||
try {
|
||
// 确保有token
|
||
if (!this.qiniuToken) {
|
||
await this.getQiniuToken()
|
||
}
|
||
|
||
// 生成唯一文件名
|
||
const key = `uploads/${Date.now()}_${Math.random().toString(36).slice(2)}.jpg`
|
||
|
||
// 上传到七牛云
|
||
const result = await uploadToQiniu(this.selectedImagePath, this.qiniuToken, key)
|
||
|
||
// 构建完整的图片URL
|
||
this.uploadResult = `https://api.ccttiot.com/${result.key}`
|
||
|
||
uni.showToast({
|
||
title: '上传成功',
|
||
icon: 'success',
|
||
})
|
||
|
||
// 返回上一页并传递图片URL
|
||
const pages = getCurrentPages()
|
||
const prevPage = pages[pages.length - 2]
|
||
|
||
if (prevPage) {
|
||
// 如果上一页存在,可以通过事件总线或其他方式传递数据
|
||
// 这里我们通过URL参数的方式返回
|
||
uni.navigateBack({
|
||
success: () => {
|
||
// 延迟执行,确保页面已经返回
|
||
setTimeout(() => {
|
||
uni.$emit('image-upload-success', this.uploadResult)
|
||
}, 100)
|
||
},
|
||
})
|
||
} else {
|
||
// 如果没有上一页,直接返回URL参数
|
||
uni.navigateBack({
|
||
delta: 1,
|
||
success: () => {
|
||
// 通过URL参数传递结果
|
||
const currentPage = getCurrentPages()[getCurrentPages().length - 1]
|
||
if (currentPage && currentPage.onLoad) {
|
||
currentPage.onLoad({
|
||
imageUrl: encodeURIComponent(this.uploadResult),
|
||
})
|
||
}
|
||
},
|
||
})
|
||
}
|
||
} catch (error) {
|
||
console.error('上传失败:', error)
|
||
this.errorMessage = error.message || '上传失败'
|
||
uni.showToast({
|
||
title: '上传失败',
|
||
icon: 'none',
|
||
})
|
||
} finally {
|
||
this.uploading = false
|
||
}
|
||
},
|
||
|
||
// 获取七牛云token
|
||
async getQiniuToken() {
|
||
try {
|
||
const res = await getQiniuUploadToken()
|
||
console.log('Token响应:', res)
|
||
|
||
if (res.code === 200) {
|
||
// 尝试不同的可能字段名
|
||
const token = res.data?.token || res.data?.uploadToken || res.token || res.data
|
||
if (token) {
|
||
this.qiniuToken = token
|
||
console.log('Token获取成功:', token.substring(0, 20) + '...')
|
||
} else {
|
||
throw new Error('Token字段不存在')
|
||
}
|
||
} else {
|
||
throw new Error(res.msg || '获取Token失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('获取Token失败:', error)
|
||
this.errorMessage = `获取上传凭证失败: ${error.message}`
|
||
uni.showToast({
|
||
title: '获取上传凭证失败',
|
||
icon: 'none',
|
||
})
|
||
}
|
||
},
|
||
|
||
// 复制URL
|
||
copyUrl() {
|
||
if (this.uploadResult) {
|
||
uni.setClipboardData({
|
||
data: this.uploadResult,
|
||
success: () => {
|
||
uni.showToast({
|
||
title: 'URL已复制',
|
||
icon: 'success',
|
||
})
|
||
},
|
||
})
|
||
}
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.image-upload-page {
|
||
min-height: 100vh;
|
||
background: #f5f5f5;
|
||
|
||
.content {
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.upload-area {
|
||
width: 100%;
|
||
height: 400rpx;
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 30rpx;
|
||
border: 2rpx dashed #ddd;
|
||
|
||
.upload-placeholder {
|
||
text-align: center;
|
||
|
||
.upload-text {
|
||
display: block;
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
margin-top: 20rpx;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.upload-hint {
|
||
display: block;
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
.preview-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 16rpx;
|
||
}
|
||
}
|
||
|
||
.upload-btn {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
line-height: 88rpx;
|
||
background: #007aff;
|
||
color: #fff;
|
||
border-radius: 12rpx;
|
||
font-size: 32rpx;
|
||
border: none;
|
||
margin-bottom: 30rpx;
|
||
|
||
&:active {
|
||
background: #0056cc;
|
||
}
|
||
|
||
&:disabled {
|
||
background: #ccc;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
.result-section {
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 30rpx;
|
||
|
||
.result-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
.result-label {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: bold;
|
||
margin-right: 20rpx;
|
||
min-width: 120rpx;
|
||
}
|
||
|
||
.result-value {
|
||
font-size: 28rpx;
|
||
|
||
&.success {
|
||
color: #52c41a;
|
||
}
|
||
}
|
||
|
||
.result-url {
|
||
flex: 1;
|
||
font-size: 24rpx;
|
||
color: #007aff;
|
||
word-break: break-all;
|
||
line-height: 1.4;
|
||
}
|
||
}
|
||
|
||
.copy-btn {
|
||
width: 100%;
|
||
height: 70rpx;
|
||
line-height: 70rpx;
|
||
background: #f0f0f0;
|
||
color: #333;
|
||
border-radius: 8rpx;
|
||
font-size: 28rpx;
|
||
border: none;
|
||
|
||
&:active {
|
||
background: #e0e0e0;
|
||
}
|
||
}
|
||
}
|
||
|
||
.error-section {
|
||
background: #fff2f0;
|
||
border: 1rpx solid #ffccc7;
|
||
border-radius: 12rpx;
|
||
padding: 20rpx;
|
||
|
||
.error-text {
|
||
font-size: 26rpx;
|
||
color: #ff4d4f;
|
||
line-height: 1.4;
|
||
}
|
||
}
|
||
}
|
||
</style>
|