|
@@ -0,0 +1,500 @@
|
|
|
+/**
|
|
|
+ * 微信小程序web-view文件访问助手
|
|
|
+ * 专门处理微信小程序web-view中的文件访问问题
|
|
|
+ */
|
|
|
+
|
|
|
+// 检测是否在微信小程序web-view中
|
|
|
+export function isWechatMiniProgram() {
|
|
|
+ return typeof wx !== 'undefined' && wx.miniProgram
|
|
|
+}
|
|
|
+
|
|
|
+// 检测是否在微信环境中
|
|
|
+export function isWechatEnvironment() {
|
|
|
+ return /micromessenger/i.test(navigator.userAgent)
|
|
|
+}
|
|
|
+
|
|
|
+// 检测安卓系统
|
|
|
+export function isAndroid() {
|
|
|
+ return /android/i.test(navigator.userAgent)
|
|
|
+}
|
|
|
+
|
|
|
+// 获取微信小程序环境信息
|
|
|
+export function getWechatEnvironment() {
|
|
|
+ return new Promise((resolve) => {
|
|
|
+ if (typeof wx !== 'undefined' && wx.miniProgram) {
|
|
|
+ wx.miniProgram.getEnv((res) => {
|
|
|
+ resolve({
|
|
|
+ isMiniProgram: true,
|
|
|
+ env: res.miniprogram ? 'miniprogram' : 'webview',
|
|
|
+ version: res.version || 'unknown'
|
|
|
+ })
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ resolve({
|
|
|
+ isMiniProgram: false,
|
|
|
+ env: 'browser',
|
|
|
+ version: 'unknown'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 微信小程序文件选择器
|
|
|
+export function createWechatFileSelector(options = {}) {
|
|
|
+ const {
|
|
|
+ count = 9, // 最多可以选择的文件数量
|
|
|
+ type = 'all', // 文件类型:all, image, video, file
|
|
|
+ sizeType = ['original', 'compressed'], // 所选的图片的尺寸:original, compressed
|
|
|
+ sourceType = ['album', 'camera'], // 选择图片的来源:album, camera
|
|
|
+ onSuccess = () => {},
|
|
|
+ onFail = () => {},
|
|
|
+ onCancel = () => {}
|
|
|
+ } = options
|
|
|
+
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ if (!isWechatMiniProgram()) {
|
|
|
+ reject(new Error('不在微信小程序环境中'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据文件类型选择不同的API
|
|
|
+ if (type === 'image') {
|
|
|
+ // 选择图片
|
|
|
+ wx.chooseImage({
|
|
|
+ count,
|
|
|
+ sizeType,
|
|
|
+ sourceType,
|
|
|
+ success: (res) => {
|
|
|
+ const files = res.tempFilePaths.map((path, index) => ({
|
|
|
+ path,
|
|
|
+ name: `image_${Date.now()}_${index}.jpg`,
|
|
|
+ type: 'image/jpeg',
|
|
|
+ size: 0 // 微信小程序中无法直接获取文件大小
|
|
|
+ }))
|
|
|
+ onSuccess(files)
|
|
|
+ resolve(files)
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ onFail(err)
|
|
|
+ reject(err)
|
|
|
+ },
|
|
|
+ cancel: () => {
|
|
|
+ onCancel()
|
|
|
+ reject(new Error('用户取消选择'))
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else if (type === 'video') {
|
|
|
+ // 选择视频
|
|
|
+ wx.chooseVideo({
|
|
|
+ sourceType,
|
|
|
+ maxDuration: 60,
|
|
|
+ camera: 'back',
|
|
|
+ success: (res) => {
|
|
|
+ const file = {
|
|
|
+ path: res.tempFilePath,
|
|
|
+ name: `video_${Date.now()}.mp4`,
|
|
|
+ type: 'video/mp4',
|
|
|
+ size: 0,
|
|
|
+ duration: res.duration,
|
|
|
+ width: res.width,
|
|
|
+ height: res.height
|
|
|
+ }
|
|
|
+ onSuccess([file])
|
|
|
+ resolve([file])
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ onFail(err)
|
|
|
+ reject(err)
|
|
|
+ },
|
|
|
+ cancel: () => {
|
|
|
+ onCancel()
|
|
|
+ reject(new Error('用户取消选择'))
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 选择文件(微信小程序基础库2.16.0+支持)
|
|
|
+ if (wx.chooseMessageFile) {
|
|
|
+ wx.chooseMessageFile({
|
|
|
+ count,
|
|
|
+ type: 'file',
|
|
|
+ success: (res) => {
|
|
|
+ const files = res.tempFiles.map((file) => ({
|
|
|
+ path: file.path,
|
|
|
+ name: file.name,
|
|
|
+ type: file.type || 'application/octet-stream',
|
|
|
+ size: file.size
|
|
|
+ }))
|
|
|
+ onSuccess(files)
|
|
|
+ resolve(files)
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ onFail(err)
|
|
|
+ reject(err)
|
|
|
+ },
|
|
|
+ cancel: () => {
|
|
|
+ onCancel()
|
|
|
+ reject(new Error('用户取消选择'))
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 降级到选择图片
|
|
|
+ wx.chooseImage({
|
|
|
+ count,
|
|
|
+ sizeType,
|
|
|
+ sourceType,
|
|
|
+ success: (res) => {
|
|
|
+ const files = res.tempFilePaths.map((path, index) => ({
|
|
|
+ path,
|
|
|
+ name: `file_${Date.now()}_${index}.jpg`,
|
|
|
+ type: 'image/jpeg',
|
|
|
+ size: 0
|
|
|
+ }))
|
|
|
+ onSuccess(files)
|
|
|
+ resolve(files)
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ onFail(err)
|
|
|
+ reject(err)
|
|
|
+ },
|
|
|
+ cancel: () => {
|
|
|
+ onCancel()
|
|
|
+ reject(new Error('用户取消选择'))
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 上传文件到服务器
|
|
|
+export function uploadFileToServer(filePath, options = {}) {
|
|
|
+ const {
|
|
|
+ url = '/api/upload',
|
|
|
+ name = 'file',
|
|
|
+ header = {},
|
|
|
+ formData = {},
|
|
|
+ onProgress = () => {},
|
|
|
+ onSuccess = () => {},
|
|
|
+ onFail = () => {}
|
|
|
+ } = options
|
|
|
+
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ if (!isWechatMiniProgram()) {
|
|
|
+ reject(new Error('不在微信小程序环境中'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const uploadTask = wx.uploadFile({
|
|
|
+ url,
|
|
|
+ filePath,
|
|
|
+ name,
|
|
|
+ header,
|
|
|
+ formData,
|
|
|
+ success: (res) => {
|
|
|
+ try {
|
|
|
+ const data = JSON.parse(res.data)
|
|
|
+ onSuccess(data)
|
|
|
+ resolve(data)
|
|
|
+ } catch (error) {
|
|
|
+ onFail(error)
|
|
|
+ reject(error)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ onFail(err)
|
|
|
+ reject(err)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 监听上传进度
|
|
|
+ uploadTask.onProgressUpdate((res) => {
|
|
|
+ onProgress({
|
|
|
+ progress: res.progress,
|
|
|
+ totalBytesSent: res.totalBytesSent,
|
|
|
+ totalBytesExpectedToSend: res.totalBytesExpectedToSend
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 获取文件信息
|
|
|
+export function getFileInfo(filePath) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ if (!isWechatMiniProgram()) {
|
|
|
+ reject(new Error('不在微信小程序环境中'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ wx.getFileInfo({
|
|
|
+ filePath,
|
|
|
+ success: (res) => {
|
|
|
+ resolve({
|
|
|
+ size: res.size,
|
|
|
+ digest: res.digest
|
|
|
+ })
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ reject(err)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 保存文件到相册
|
|
|
+export function saveFileToAlbum(filePath) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ if (!isWechatMiniProgram()) {
|
|
|
+ reject(new Error('不在微信小程序环境中'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ wx.saveImageToPhotosAlbum({
|
|
|
+ filePath,
|
|
|
+ success: () => {
|
|
|
+ resolve()
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ reject(err)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 预览文件
|
|
|
+export function previewFile(filePath, fileType = 'image') {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ if (!isWechatMiniProgram()) {
|
|
|
+ reject(new Error('不在微信小程序环境中'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fileType === 'image') {
|
|
|
+ wx.previewImage({
|
|
|
+ urls: [filePath],
|
|
|
+ success: () => {
|
|
|
+ resolve()
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ reject(err)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 对于非图片文件,可以尝试打开文档
|
|
|
+ wx.openDocument({
|
|
|
+ filePath,
|
|
|
+ success: () => {
|
|
|
+ resolve()
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ reject(err)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 检查文件访问权限
|
|
|
+export function checkFilePermission() {
|
|
|
+ return new Promise((resolve) => {
|
|
|
+ if (!isWechatMiniProgram()) {
|
|
|
+ resolve({
|
|
|
+ hasPermission: false,
|
|
|
+ message: '不在微信小程序环境中'
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 尝试获取相册权限
|
|
|
+ wx.getSetting({
|
|
|
+ success: (res) => {
|
|
|
+ const hasAlbumPermission = res.authSetting['scope.writePhotosAlbum']
|
|
|
+ const hasCameraPermission = res.authSetting['scope.camera']
|
|
|
+
|
|
|
+ resolve({
|
|
|
+ hasPermission: hasAlbumPermission && hasCameraPermission,
|
|
|
+ albumPermission: hasAlbumPermission,
|
|
|
+ cameraPermission: hasCameraPermission,
|
|
|
+ message: hasAlbumPermission && hasCameraPermission
|
|
|
+ ? '文件访问权限正常'
|
|
|
+ : '需要授权相册和相机权限'
|
|
|
+ })
|
|
|
+ },
|
|
|
+ fail: () => {
|
|
|
+ resolve({
|
|
|
+ hasPermission: false,
|
|
|
+ message: '无法检测权限状态'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 请求文件访问权限
|
|
|
+export function requestFilePermission() {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ if (!isWechatMiniProgram()) {
|
|
|
+ reject(new Error('不在微信小程序环境中'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ wx.authorize({
|
|
|
+ scope: 'scope.writePhotosAlbum',
|
|
|
+ success: () => {
|
|
|
+ resolve({
|
|
|
+ success: true,
|
|
|
+ message: '权限授权成功'
|
|
|
+ })
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ reject({
|
|
|
+ success: false,
|
|
|
+ message: '权限授权失败',
|
|
|
+ error: err
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 显示权限设置引导
|
|
|
+export function showPermissionGuide() {
|
|
|
+ return new Promise((resolve) => {
|
|
|
+ if (!isWechatMiniProgram()) {
|
|
|
+ resolve()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ wx.showModal({
|
|
|
+ title: '需要权限',
|
|
|
+ content: '为了正常使用文件功能,请在设置中开启相册和相机权限',
|
|
|
+ confirmText: '去设置',
|
|
|
+ cancelText: '取消',
|
|
|
+ success: (res) => {
|
|
|
+ if (res.confirm) {
|
|
|
+ wx.openSetting({
|
|
|
+ success: (settingRes) => {
|
|
|
+ resolve(settingRes)
|
|
|
+ },
|
|
|
+ fail: () => {
|
|
|
+ resolve()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ resolve()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 创建文件选择器组件
|
|
|
+export function createFileSelectorComponent() {
|
|
|
+ return {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ isWechat: false,
|
|
|
+ isMiniProgram: false,
|
|
|
+ hasPermission: false,
|
|
|
+ selectedFiles: [],
|
|
|
+ uploading: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ mounted() {
|
|
|
+ this.checkEnvironment()
|
|
|
+ },
|
|
|
+
|
|
|
+ methods: {
|
|
|
+ async checkEnvironment() {
|
|
|
+ this.isWechat = isWechatEnvironment()
|
|
|
+ this.isMiniProgram = isWechatMiniProgram()
|
|
|
+
|
|
|
+ if (this.isMiniProgram) {
|
|
|
+ const env = await getWechatEnvironment()
|
|
|
+ console.log('微信环境信息:', env)
|
|
|
+
|
|
|
+ const permission = await checkFilePermission()
|
|
|
+ this.hasPermission = permission.hasPermission
|
|
|
+
|
|
|
+ if (!permission.hasPermission) {
|
|
|
+ this.$message.warning(permission.message)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ async selectFiles(type = 'all') {
|
|
|
+ try {
|
|
|
+ const files = await createWechatFileSelector({
|
|
|
+ count: 9,
|
|
|
+ type,
|
|
|
+ onSuccess: (files) => {
|
|
|
+ this.selectedFiles = files
|
|
|
+ this.$message.success(`已选择 ${files.length} 个文件`)
|
|
|
+ },
|
|
|
+ onFail: (error) => {
|
|
|
+ this.$message.error(`选择文件失败: ${error.errMsg || error.message}`)
|
|
|
+ },
|
|
|
+ onCancel: () => {
|
|
|
+ this.$message.info('用户取消选择')
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ return files
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(`选择文件失败: ${error.message}`)
|
|
|
+ throw error
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ async uploadFiles(files) {
|
|
|
+ this.uploading = true
|
|
|
+
|
|
|
+ try {
|
|
|
+ const uploadPromises = files.map(file =>
|
|
|
+ uploadFileToServer(file.path, {
|
|
|
+ url: '/api/upload',
|
|
|
+ onProgress: (progress) => {
|
|
|
+ console.log(`文件 ${file.name} 上传进度: ${progress.progress}%`)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ )
|
|
|
+
|
|
|
+ const results = await Promise.all(uploadPromises)
|
|
|
+ this.$message.success('所有文件上传成功')
|
|
|
+ return results
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(`上传失败: ${error.message}`)
|
|
|
+ throw error
|
|
|
+ } finally {
|
|
|
+ this.uploading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ async requestPermission() {
|
|
|
+ try {
|
|
|
+ await requestFilePermission()
|
|
|
+ this.hasPermission = true
|
|
|
+ this.$message.success('权限授权成功')
|
|
|
+ } catch (error) {
|
|
|
+ await showPermissionGuide()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export default {
|
|
|
+ isWechatMiniProgram,
|
|
|
+ isWechatEnvironment,
|
|
|
+ isAndroid,
|
|
|
+ getWechatEnvironment,
|
|
|
+ createWechatFileSelector,
|
|
|
+ uploadFileToServer,
|
|
|
+ getFileInfo,
|
|
|
+ saveFileToAlbum,
|
|
|
+ previewFile,
|
|
|
+ checkFilePermission,
|
|
|
+ requestFilePermission,
|
|
|
+ showPermissionGuide,
|
|
|
+ createFileSelectorComponent
|
|
|
+}
|