const AUTH_KEY = { album: 'albumAuthorized', camera: 'cameraAuthorized', record: 'microphoneAuthorized' } const WX_SCOPE = { camera: 'scope.camera', record: 'scope.record' } let modalOpen = false function t(vm, key, fallback) { const fn = vm && vm.$t ? vm.$t.bind(vm) : null if (!fn) return fallback try { const v = fn(key) return v && v !== key ? v : fallback } catch (_) { return fallback } } function isPermissionErrMsg(msg) { const m = String(msg || '').toLowerCase() return ( m.indexOf('auth') !== -1 || m.indexOf('deny') !== -1 || m.indexOf('permission') !== -1 || m.indexOf('authorize') !== -1 || m.indexOf('不允许') !== -1 || m.indexOf('拒绝') !== -1 || m.indexOf('权限') !== -1 ) } export function openAppPermissionSettings() { if (typeof uni.openAppAuthorizeSetting === 'function') { uni.openAppAuthorizeSetting({}) return } // #ifdef APP-PLUS try { const os = String(plus.os.name || '').toLowerCase() if (os === 'ios') { plus.runtime.openURL('app-settings:') return } } catch (_) {} // #endif if (typeof uni.openSetting === 'function') { uni.openSetting({}) } } function readAppAuthorize(kind) { const key = AUTH_KEY[kind] if (!key || typeof uni.getAppAuthorizeSetting !== 'function') return 'unknown' try { const setting = uni.getAppAuthorizeSetting() || {} return setting[key] || 'not determined' } catch (_) { return 'unknown' } } function contentKeyForKind(kind) { if (kind === 'album') return 'dynamics.publishAlbumPermissionContent' if (kind === 'record') return 'dynamics.publishRecordPermissionContent' return 'dynamics.publishCameraPermissionContent' } export function promptMediaPermission(vm, kind) { if (modalOpen) return modalOpen = true uni.showModal({ title: t(vm, 'dynamics.publishMediaPermissionTitle', '提示'), content: t(vm, contentKeyForKind(kind), '请在系统设置中开启相应权限后重试'), confirmText: t(vm, 'dynamics.publishMediaPermissionConfirm', '去设置'), cancelText: t(vm, 'dynamics.publishMediaPermissionCancel', '取消'), success: (res) => { modalOpen = false if (res.confirm) openAppPermissionSettings() }, fail: () => { modalOpen = false } }) } function ensureWeixinScope(scope, vm, kind) { return new Promise((resolve) => { uni.getSetting({ success: (res) => { const auth = (res && res.authSetting) || {} if (auth[scope] === false) { promptMediaPermission(vm, kind) resolve(false) return } if (auth[scope] === true) { resolve(true) return } uni.authorize({ scope, success: () => resolve(true), fail: () => resolve(false) }) }, fail: () => resolve(true) }) }) } export async function ensureMediaPermission(vm, kinds) { const list = Array.isArray(kinds) ? kinds : [kinds] for (let i = 0; i < list.length; i++) { const kind = list[i] // #ifdef MP-WEIXIN if (kind === 'camera' || kind === 'record') { const scope = WX_SCOPE[kind] const ok = await ensureWeixinScope(scope, vm, kind) if (!ok) return false continue } // 微信小程序读相册由 chooseImage/chooseVideo 触发,fail 回调兜底 continue // #endif const status = readAppAuthorize(kind) if (status === 'denied') { promptMediaPermission(vm, kind) return false } } return true } function wrapMediaFail(vm, kind, fail) { return (err) => { if (isPermissionErrMsg(err && err.errMsg)) { // 首次拒绝仅静默结束,下次点击时由 ensureMediaPermission 检测 denied 再引导去设置 return } if (typeof fail === 'function') fail(err) } } export function showMediaSourceSheet(vm, options = {}) { const { type = 'image', onAlbum, onCamera } = options const isVideo = type === 'video' uni.showActionSheet({ itemList: [ t(vm, 'dynamics.publishPickAlbum', '从相册选择'), t( vm, isVideo ? 'dynamics.publishPickCameraVideo' : 'dynamics.publishPickCamera', isVideo ? '拍摄视频' : '拍照' ) ], success: (res) => { if (res.tapIndex === 0 && onAlbum) onAlbum() else if (res.tapIndex === 1 && onCamera) onCamera() } }) } export async function chooseImageWithPermission(vm, source, options = {}) { const kind = source === 'camera' ? 'camera' : 'album' const ok = await ensureMediaPermission(vm, kind) if (!ok) return const { fail, ...rest } = options uni.chooseImage({ ...rest, sourceType: [source], fail: wrapMediaFail(vm, kind, fail) }) } export async function chooseVideoWithPermission(vm, source, options = {}) { const kinds = source === 'camera' ? ['camera', 'record'] : ['album'] const ok = await ensureMediaPermission(vm, kinds) if (!ok) return const { fail, ...rest } = options const failKind = source === 'camera' ? 'camera' : 'album' uni.chooseVideo({ ...rest, sourceType: [source], fail: wrapMediaFail(vm, failKind, fail) }) }