Files
property-resident-side/property-uniapp-project/pageSubPack/api/useImageUpload.js
2026-07-21 09:41:54 +08:00

191 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 服务器地址(与 request.js 保持一致)
const baseUrl = 'https://wuyeapi.ckdzkj.com';
/**
* 图片上传 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
}
}