192 lines
4.7 KiB
JavaScript
192 lines
4.7 KiB
JavaScript
// 服务器地址(与 request.js 保持一致)
|
||
const baseUrl = 'https://wuyeapi.ckdzkj.com';
|
||
// const baseUrl = "http://192.168.1.6:8080"
|
||
|
||
/**
|
||
* 图片上传 Hook
|
||
* 支持 uniapp 和微信小程序
|
||
* 解决编辑回显后重新上传的问题
|
||
*/
|
||
|
||
import { reactive } from 'vue'
|
||
|
||
/**
|
||
* @param {Object} options 配置
|
||
* @param {string} options.uploadUrl - 上传接口地址
|
||
* @param {string} options.fileKey - 上传文件的字段名,默认 'file'
|
||
* @returns {Object}
|
||
*/
|
||
export function useImageUpload(options = {}) {
|
||
const {
|
||
uploadUrl = '/api/resident/file/uploadImage',
|
||
fileKey = 'file'
|
||
} = options
|
||
|
||
// 使用 reactive 对象存储所有状态
|
||
const state = reactive({
|
||
// 当前显示的图片(前端展示用,支持 base64/blob URL/网络URL)
|
||
previewUrl: '',
|
||
// 服务器返回的 URL(提交时使用)
|
||
serverUrl: '',
|
||
// 临时的文件路径(重新上传时用)
|
||
tempFilePath: null,
|
||
// 上传状态
|
||
uploading: false
|
||
})
|
||
|
||
/**
|
||
* 设置回显图片(编辑时从后端获取)
|
||
* @param {string} url - 后端返回的图片URL
|
||
*/
|
||
const setPreviewUrl = (url) => {
|
||
if (url) {
|
||
// 确保 url 是字符串
|
||
const urlStr = typeof url === 'string' ? url : (url?.url || String(url))
|
||
state.previewUrl = urlStr
|
||
state.serverUrl = urlStr
|
||
state.tempFilePath = null // 重置临时文件,表示没有重新上传
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 选择图片并预览(不上传)
|
||
* @returns {Promise<string>} 返回临时文件路径
|
||
*/
|
||
const chooseImage = () => {
|
||
return new Promise((resolve, reject) => {
|
||
uni.chooseImage({
|
||
count: 1,
|
||
sizeType: ['compressed'],
|
||
sourceType: ['camera', 'album'],
|
||
success: (res) => {
|
||
const path = res.tempFilePaths?.[0]
|
||
if (!path) {
|
||
reject(new Error('选择图片失败'))
|
||
return
|
||
}
|
||
// 确保 path 是字符串
|
||
const pathStr = typeof path === 'string' ? path : String(path)
|
||
// 设置预览
|
||
state.previewUrl = pathStr
|
||
// 记录临时文件路径
|
||
state.tempFilePath = pathStr
|
||
// 置空服务器URL,等实际上传后再获取
|
||
state.serverUrl = ''
|
||
resolve(pathStr)
|
||
},
|
||
fail: (err) => {
|
||
reject(err)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 上传图片到服务器
|
||
* @returns {Promise<Object>} 返回 { url: string }
|
||
*/
|
||
const uploadImage = () => {
|
||
// 如果没有新选择临时文件,直接返回已有的服务器URL
|
||
if (!state.tempFilePath) {
|
||
return Promise.resolve({ url: state.serverUrl })
|
||
}
|
||
|
||
// 如果已经有服务器URL且没有重新选择,也直接返回
|
||
if (state.serverUrl && !state.tempFilePath) {
|
||
return Promise.resolve({ url: state.serverUrl })
|
||
}
|
||
|
||
state.uploading = true
|
||
|
||
return new Promise((resolve, reject) => {
|
||
// 确保 filePath 是字符串
|
||
const filePath = state.tempFilePath
|
||
if (!filePath || typeof filePath !== 'string') {
|
||
state.uploading = false
|
||
reject(new Error('文件路径无效'))
|
||
return
|
||
}
|
||
uni.uploadFile({
|
||
url: baseUrl + uploadUrl,
|
||
filePath: filePath,
|
||
name: fileKey,
|
||
header: {
|
||
'Authorization': 'Bearer ' + (uni.getStorageSync('token') || ''),
|
||
'clientid': 'resident'
|
||
},
|
||
success: (res) => {
|
||
try {
|
||
const data = JSON.parse(res.data)
|
||
if (data.code === 200 && data.data) {
|
||
// 更新服务器URL(确保存的是字符串)
|
||
const url = typeof data.data === 'string' ? data.data : (data.data?.url || '')
|
||
state.serverUrl = url
|
||
// 清空临时文件
|
||
state.tempFilePath = null
|
||
resolve({ url })
|
||
} else {
|
||
uni.showToast({
|
||
title: data.msg || '上传失败',
|
||
icon: 'none'
|
||
})
|
||
reject(new Error(data.msg || '上传失败'))
|
||
}
|
||
} catch (e) {
|
||
uni.showToast({
|
||
title: '上传失败',
|
||
icon: 'none'
|
||
})
|
||
reject(e)
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
uni.showToast({
|
||
title: err.message || '上传失败',
|
||
icon: 'none'
|
||
})
|
||
reject(err)
|
||
},
|
||
complete: () => {
|
||
state.uploading = false
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取最终可提交的URL
|
||
* 如果有新上传的图片,先上传再返回URL
|
||
* 如果没有新上传,直接返回已有的服务器URL
|
||
* @returns {Promise<Object>}
|
||
*/
|
||
const getSubmitUrl = async () => {
|
||
// 如果重新选择了新图片,先上传
|
||
if (state.tempFilePath) {
|
||
return await uploadImage()
|
||
}
|
||
// 否则返回已有的服务器URL
|
||
return { url: state.serverUrl }
|
||
}
|
||
|
||
/**
|
||
* 重置状态
|
||
*/
|
||
const reset = () => {
|
||
state.previewUrl = ''
|
||
state.serverUrl = ''
|
||
state.tempFilePath = null
|
||
state.uploading = false
|
||
}
|
||
|
||
// 返回 reactive state 和方法
|
||
// state 作为 reactive 对象,在模板中通过 idCardFrontUpload.state.previewUrl 访问
|
||
return {
|
||
state,
|
||
setPreviewUrl,
|
||
chooseImage,
|
||
uploadImage,
|
||
getSubmitUrl,
|
||
reset
|
||
}
|
||
}
|