media-picker-permission.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. const AUTH_KEY = {
  2. album: 'albumAuthorized',
  3. camera: 'cameraAuthorized',
  4. record: 'microphoneAuthorized'
  5. }
  6. const WX_SCOPE = {
  7. camera: 'scope.camera',
  8. record: 'scope.record'
  9. }
  10. let modalOpen = false
  11. function t(vm, key, fallback) {
  12. const fn = vm && vm.$t ? vm.$t.bind(vm) : null
  13. if (!fn) return fallback
  14. try {
  15. const v = fn(key)
  16. return v && v !== key ? v : fallback
  17. } catch (_) {
  18. return fallback
  19. }
  20. }
  21. function isPermissionErrMsg(msg) {
  22. const m = String(msg || '').toLowerCase()
  23. return (
  24. m.indexOf('auth') !== -1 ||
  25. m.indexOf('deny') !== -1 ||
  26. m.indexOf('permission') !== -1 ||
  27. m.indexOf('authorize') !== -1 ||
  28. m.indexOf('不允许') !== -1 ||
  29. m.indexOf('拒绝') !== -1 ||
  30. m.indexOf('权限') !== -1
  31. )
  32. }
  33. export function openAppPermissionSettings() {
  34. if (typeof uni.openAppAuthorizeSetting === 'function') {
  35. uni.openAppAuthorizeSetting({})
  36. return
  37. }
  38. // #ifdef APP-PLUS
  39. try {
  40. const os = String(plus.os.name || '').toLowerCase()
  41. if (os === 'ios') {
  42. plus.runtime.openURL('app-settings:')
  43. return
  44. }
  45. } catch (_) {}
  46. // #endif
  47. if (typeof uni.openSetting === 'function') {
  48. uni.openSetting({})
  49. }
  50. }
  51. function readAppAuthorize(kind) {
  52. const key = AUTH_KEY[kind]
  53. if (!key || typeof uni.getAppAuthorizeSetting !== 'function') return 'unknown'
  54. try {
  55. const setting = uni.getAppAuthorizeSetting() || {}
  56. return setting[key] || 'not determined'
  57. } catch (_) {
  58. return 'unknown'
  59. }
  60. }
  61. function contentKeyForKind(kind) {
  62. if (kind === 'album') return 'dynamics.publishAlbumPermissionContent'
  63. if (kind === 'record') return 'dynamics.publishRecordPermissionContent'
  64. return 'dynamics.publishCameraPermissionContent'
  65. }
  66. export function promptMediaPermission(vm, kind) {
  67. if (modalOpen) return
  68. modalOpen = true
  69. uni.showModal({
  70. title: t(vm, 'dynamics.publishMediaPermissionTitle', '提示'),
  71. content: t(vm, contentKeyForKind(kind), '请在系统设置中开启相应权限后重试'),
  72. confirmText: t(vm, 'dynamics.publishMediaPermissionConfirm', '去设置'),
  73. cancelText: t(vm, 'dynamics.publishMediaPermissionCancel', '取消'),
  74. success: (res) => {
  75. modalOpen = false
  76. if (res.confirm) openAppPermissionSettings()
  77. },
  78. fail: () => {
  79. modalOpen = false
  80. }
  81. })
  82. }
  83. function ensureWeixinScope(scope, vm, kind) {
  84. return new Promise((resolve) => {
  85. uni.getSetting({
  86. success: (res) => {
  87. const auth = (res && res.authSetting) || {}
  88. if (auth[scope] === false) {
  89. promptMediaPermission(vm, kind)
  90. resolve(false)
  91. return
  92. }
  93. if (auth[scope] === true) {
  94. resolve(true)
  95. return
  96. }
  97. uni.authorize({
  98. scope,
  99. success: () => resolve(true),
  100. fail: () => resolve(false)
  101. })
  102. },
  103. fail: () => resolve(true)
  104. })
  105. })
  106. }
  107. export async function ensureMediaPermission(vm, kinds) {
  108. const list = Array.isArray(kinds) ? kinds : [kinds]
  109. for (let i = 0; i < list.length; i++) {
  110. const kind = list[i]
  111. // #ifdef MP-WEIXIN
  112. if (kind === 'camera' || kind === 'record') {
  113. const scope = WX_SCOPE[kind]
  114. const ok = await ensureWeixinScope(scope, vm, kind)
  115. if (!ok) return false
  116. continue
  117. }
  118. // 微信小程序读相册由 chooseImage/chooseVideo 触发,fail 回调兜底
  119. continue
  120. // #endif
  121. const status = readAppAuthorize(kind)
  122. if (status === 'denied') {
  123. promptMediaPermission(vm, kind)
  124. return false
  125. }
  126. }
  127. return true
  128. }
  129. function wrapMediaFail(vm, kind, fail) {
  130. return (err) => {
  131. if (isPermissionErrMsg(err && err.errMsg)) {
  132. // 首次拒绝仅静默结束,下次点击时由 ensureMediaPermission 检测 denied 再引导去设置
  133. return
  134. }
  135. if (typeof fail === 'function') fail(err)
  136. }
  137. }
  138. export function showMediaSourceSheet(vm, options = {}) {
  139. const { type = 'image', onAlbum, onCamera } = options
  140. const isVideo = type === 'video'
  141. uni.showActionSheet({
  142. itemList: [
  143. t(vm, 'dynamics.publishPickAlbum', '从相册选择'),
  144. t(
  145. vm,
  146. isVideo ? 'dynamics.publishPickCameraVideo' : 'dynamics.publishPickCamera',
  147. isVideo ? '拍摄视频' : '拍照'
  148. )
  149. ],
  150. success: (res) => {
  151. if (res.tapIndex === 0 && onAlbum) onAlbum()
  152. else if (res.tapIndex === 1 && onCamera) onCamera()
  153. }
  154. })
  155. }
  156. export async function chooseImageWithPermission(vm, source, options = {}) {
  157. const kind = source === 'camera' ? 'camera' : 'album'
  158. const ok = await ensureMediaPermission(vm, kind)
  159. if (!ok) return
  160. const { fail, ...rest } = options
  161. uni.chooseImage({
  162. ...rest,
  163. sourceType: [source],
  164. fail: wrapMediaFail(vm, kind, fail)
  165. })
  166. }
  167. export async function chooseVideoWithPermission(vm, source, options = {}) {
  168. const kinds = source === 'camera' ? ['camera', 'record'] : ['album']
  169. const ok = await ensureMediaPermission(vm, kinds)
  170. if (!ok) return
  171. const { fail, ...rest } = options
  172. const failKind = source === 'camera' ? 'camera' : 'album'
  173. uni.chooseVideo({
  174. ...rest,
  175. sourceType: [source],
  176. fail: wrapMediaFail(vm, failKind, fail)
  177. })
  178. }