uniapp app权限说明弹框2024.5.23更新

华为上架被拒绝

用uni-app开发的app,上架华为被拒,问题如下:

您的应用在运行时,未见向用户告知权限申请的目的,向用户索取(电话、相机、存储)等权限,不符合华为应用市场审核标准。

测试步骤:任意招聘信息详情页-电话联系,申请电话权限;点击置顶推广-保存二维码到相册,申请存储权限;点击发布-任意服务-上传图片-拍摄/从相册选择,申请相机、存储权限;修改建议:APP在调用终端权限时,应同步告知用户申请该权限的目的。请排查应用内所有权限申请行为,确保均符合要求。

本文是分文pinia版本和vuex版本

20214.4.23 uniapp权限弹框视频

pinia版本 

<template>
	<view>
		<button @tap="applyCameraPermission('CAMERA')">申请相机权限</button>
		<button @tap="applyPhonePermission('SET_CALL_PHONE')">申请电话权限</button>
		<button @tap="applyReadexternal('READ_EXTERNAL_STORAGE')">读取照片</button>
		<button @click="nextpage">
			跳转
		</button>
	</view>
</template>

<script setup>
import { usePermission } from "/store/permission.js"
const permissionStore = usePermission()

const applyCameraPermission = async (permission) => {
	/* #ifdef APP */
	if (!await permissionStore.requstPermission(permission)) return
	/* #endif */
	uni.chooseImage({
		count: 1,
		sizeType: ['original', 'compressed'],
		sourceType: ['camera'],
		success: (res) => {
			console.log(res)
		}
	});
}
const applyPhonePermission = async (permission) => {
	/* #ifdef APP */
	if (!await permissionStore.requstPermission(permission)) return
	/* #endif */
	uni.makePhoneCall({
		phoneNumber: '10086'
	});
}
const applyReadexternal = async (permission) => {
	/* #ifdef APP */
	if (!await permissionStore.requstPermission(permission)) return
	/* #endif */
	uni.chooseImage({
		count: 1,
		sizeType: ['original', 'compressed'],
		sourceType: ['album'],
		success: (res) => {
			console.log(res)
		}
	});
}

const nextpage = () => {
	uni.navigateTo({
		url: '/pages/index/index'
	});
}
</script>

<style></style>

pinia权限管理仓库

import {
	defineStore
} from 'pinia'

export const usePermission = defineStore('permission', {
	state: () => ({
		dialogView: null,
		permissionListener: null,
		list: [
			{
				name: "READ_CALENDAR",
				title: "手机状态权限申请说明:",
				content: "uni-app正在申请手机日历日历状态权限,允许或拒绝均不会获取任何隐私信息。",
			}, {
				name: "CALL_PHONE",
				title: "拨打电话权限申请说明:",
				content: "uni-app正在申请拨打电话权限,允许或拒绝均不会获取任何隐私信息。",
			}, {
				name: "CAMERA",
				title: "读取存储权限申请说明:",
				content: "uni-app正在申请摄像头权限,允许或拒绝均不会获取任何隐私信息。",
			}, {
				name: "READ_EXTERNAL_STORAGE",
				title: "读取存储权限申请说明:",
				content: "uni-app正在申请读取存储权限,允许或拒绝均不会获取任何隐私信息。",
			}
		]
	}),
	getters: {

	},
	actions: {
		//监听权限申请
		async requstPermission(permissionID) {
			return new Promise((resolve, reject) => {
				try {
					// if (!uni.getSystemInfoSync().platform == 'android') return resolve(true) 
					/**
					 * @description plus.navigator.checkPermission 检查应用是否获取指定权限 
					 * 有些权限检测不到 就继续下面的代码,比如相册权限就可以直接检测,就很方便,授权情况下不需要再走下面代码了
					 * checkPermission 返回参数
					 * @params undetermined 未确定
					 * @params authorized 授权
					 */
					let checkPermission = plus.navigator.checkPermission('android.permission.' + permissionID)
					if (checkPermission == 'authorized') return resolve(true)
					//判断是否自己在list里面配置了这个权限
					let index = this.list.findIndex(item => item.name == permissionID)
					if (index == -1) throw new Error('这个权限没有配置')
					//唤起原生权限说明弹框
					this.requstPermissionDialog(index)
					//授权检测回调
					plus.android.requestPermissions(
						[
							'android.permission.' + permissionID  //单个权限
							// 'android.permission.CAMERA', 'android.permission.READ_EXTERNAL_STORAGE'  //多个权限
						],
						(resultObj) => {
							console.log(resultObj, 'resultObj');
							// 权限申请结果
							/**
							 * @description resultObj.deniedAlways 永久拒绝授权
							 * 多个权限返回结果可能是{"granted":["android.permission.CAMERA"],"deniedPresent":[],"deniedAlways":["android.permission.READ_EXTERNAL_STORAGE"]}
							 * 这个情况就是我同时授权相册和相机,但是只允许了相机,没有授权相册
							 * 这个时候 可以通过deniedAlways 查看哪个权限被永久拒绝了,然后自行在设置弹框内容
							 * 所以可以自己判断细分一下,我下面的代码是先判断了是否有永久拒绝的权限,然后直接弹框提示用户去设置
							 */
							if (resultObj.deniedAlways && resultObj.deniedAlways.length > 0) {
								uni.showModal({
									title: '提示',
									content: '操作权限已被拒绝,请手动前往设置',
									confirmText: "立即设置",
									success: (res) => {
										if (res.confirm) {
											this.gotoAppPermissionSetting()
										} else {
											resolve(false)
										}
									}
								})
								console.log('永久拒绝授权');
							} else if (resultObj.deniedPresent && resultObj.deniedPresent.length > 0) {
								resolve(false)
								console.log('拒绝授权');
							} else
								if (resultObj.granted && resultObj.granted.length > 0) {
									resolve(true)
									console.log('授权成功');
								}
						},
						(error) => {
							reject(false)
							console.log('申请权限错误:',error);
						}
					);
				} catch (err) {
					reject(false)
                    console.log(err);
				}
			})
		},
		//监听弹框
		requstPermissionDialog(index) {
			try {
				if (!this.permissionListener) this.permissionListener = uni.createRequestPermissionListener()
				const dialogData = this.list[index]
				this.permissionListener.onConfirm((res) => {
					this.dialogStyle(dialogData, true)
				})
				this.permissionListener.onComplete(async (res) => {
					this.dialogStyle({}, false)
				})
			} catch (err) {
				console.log('监听弹框错误', err);
			}
		},
		//弹框样式
		dialogStyle({ title = '', content = '' }, status) {
			try {
				if (!status) return this.dialogView.close()
				const systemInfo = uni.getSystemInfoSync();
				const statusBarHeight = systemInfo.statusBarHeight;
				const navigationBarHeight = systemInfo.platform === 'android' ? 48 :
					44;
				const totalHeight = statusBarHeight + navigationBarHeight;
				this.dialogView = new plus.nativeObj.View('per-modal', {
					top: '0px',
					left: '0px',
					width: '100%',
					backgroundColor: '#444',
					//opacity: .5;
				})
				this.dialogView.drawRect({
					color: '#fff',
					radius: '5px'
				}, {
					top: totalHeight + 'px',
					left: '5%',
					width: '90%',
					height: "100px",
				})
				this.dialogView.drawText(title, {
					top: totalHeight + 5 + 'px',
					left: "8%",
					height: "30px"
				}, {
					align: "left",
					color: "#000",
				})
				this.dialogView.drawText(content, {
					top: totalHeight + 35 + 'px',
					height: "60px",
					left: "8%",
					width: "84%"
				}, {
					whiteSpace: 'normal',
					size: "14px",
					align: "left",
					color: "#656563"
				})
				this.dialogView.show()
			} catch (e) {
				console.log(e, '权限说明弹框样式错误');
			}
		},
		//跳转到app权限设置页面
		gotoAppPermissionSetting() {
			if (!uni.getSystemInfoSync().platform == 'android') {
				var UIApplication = plus.ios.import("UIApplication");
				var application2 = UIApplication.sharedApplication();
				var NSURL2 = plus.ios.import("NSURL");
				// var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");		
				var setting2 = NSURL2.URLWithString("app-settings:");
				application2.openURL(setting2);

				plus.ios.deleteObject(setting2);
				plus.ios.deleteObject(NSURL2);
				plus.ios.deleteObject(application2);
			} else {
				// console.log(plus.device.vendor);
				var Intent = plus.android.importClass("android.content.Intent");
				var Settings = plus.android.importClass("android.provider.Settings");
				var Uri = plus.android.importClass("android.net.Uri");
				var mainActivity = plus.android.runtimeMainActivity();
				var intent = new Intent();
				intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
				var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
				intent.setData(uri);
				mainActivity.startActivity(intent);
			}
		}
	}
})

vuex版本 

<template>
    <view>
        <button @click="takePhoto">
            拍照
        </button>
        <button @click="getLocation">
            获取定位
        </button>
    </view>
</template>

<script>
import { mapActions } from 'vuex'
export default {
    methods: {
        ...mapActions('permissionToast', ['requstPermission']),
        //获取定位
        async getLocation() {
            /* #ifdef APP-PLUS */
            if (!await this.requstPermission('ACCESS_FINE_LOCATION')) return
            /* #endif */
            uni.getLocation({
                type: 'gcj02',
                success: res => {
                    console.log(res)
                }
            })
        },
        //拍照
        async takePhoto() {
            /* #ifdef APP-PLUS */
            if (!await this.requstPermission('CAMERA')) return
            /* #endif */
            uni.chooseImage({
                count: 1,
                success: res => {
                    console.log(res)
                }
            })
        }

    }
}
</script>

在vuex里面创建permissionToast模块

permissionToast.js

const state = {
    dialogView: null,
    permissionListener: null,
    list: [{
        name: "CAMERA",
        title: "读取存储权限申请说明:",
        content: "uni-app正在申请摄像头权限,允许或拒绝均不会获取任何隐私信息。",
    }, {
        name: "ACCESS_FINE_LOCATION",
        title: "位置权限说明:",
        content: "需要获取您的位置信息,方便您填写收货地址信息,以便为您准确提供商品配送以及售后服务。",
    }
    ]
}
const actions = {
    //权限获取
    //监听权限申请
    async requstPermission({
        state,
        dispatch
    }, permissionID) {
        return new Promise((resolve, reject) => {
            try {
                // if (!uni.getSystemInfoSync().platform == 'android') return resolve(true) 
                /**
                 * @description plus.navigator.checkPermission 检查应用是否获取指定权限 
                 * 有些权限检测不到 就继续下面的代码,比如相册权限就可以直接检测,就很方便,授权情况下不需要再走下面代码了
                 * checkPermission 返回参数
                 * @params undetermined 未确定
                 * @params authorized 授权
                 */
                let checkPermission = plus.navigator.checkPermission('android.permission.' + permissionID)
                if (checkPermission == 'authorized') return resolve(true)
                //判断是否自己在list里面配置了这个权限
                let index = state.list.findIndex(item => item.name == permissionID)
                console.log(index,789789789798,permissionID);
                if (index == -1) throw new Error('这个权限没有配置')
                //唤起原生权限说明弹框
                dispatch('requstPermissionDialog', index)
                //授权检测回调
                plus.android.requestPermissions(
                    [
                        'android.permission.' + permissionID  //单个权限
                        // 'android.permission.CAMERA', 'android.permission.READ_EXTERNAL_STORAGE'  //多个权限
                    ],
                    (resultObj) => {
                        console.log(resultObj, 'resultObj');
                        // 权限申请结果
                        /**
                         * @description resultObj.deniedAlways 永久拒绝授权
                         * 多个权限返回结果可能是{"granted":["android.permission.CAMERA"],"deniedPresent":[],"deniedAlways":["android.permission.READ_EXTERNAL_STORAGE"]}
                         * 这个情况就是我同时授权相册和相机,但是只允许了相机,没有授权相册
                         * 这个时候 可以通过deniedAlways 查看哪个权限被永久拒绝了,然后自行在设置弹框内容
                         * 所以可以自己判断细分一下,我下面的代码是先判断了是否有永久拒绝的权限,然后直接弹框提示用户去设置
                         */
                        if (resultObj.deniedAlways && resultObj.deniedAlways.length > 0) {
                            uni.showModal({
                                title: '提示',
                                content: '操作权限已被拒绝,请手动前往设置',
                                confirmText: "立即设置",
                                success: (res) => {
                                    if (res.confirm) {
                                        dispatch('gotoAppPermissionSetting')
                                    } else {
                                        resolve(false)
                                    }
                                }
                            })
                            console.log('永久拒绝授权');
                        } else if (resultObj.deniedPresent && resultObj.deniedPresent.length > 0) {
                            resolve(false)
                            console.log('拒绝授权');
                        } else
                            if (resultObj.granted && resultObj.granted.length > 0) {
                                resolve(true)
                                console.log('授权成功');
                            }
                    },
                    (error) => {
                        reject(false)
                        console.log('申请权限错误:', error);
                    }
                );
            } catch (err) {
                reject(false)
                console.log(err);
            }
        })
    },
    //监听弹框
    requstPermissionDialog({
        state,
        dispatch
    }, index) {
        try {
            console.log(index);
            let permissionListener = state.permissionListener;
            if (!permissionListener) permissionListener = uni.createRequestPermissionListener()
            const dialogData = state.list[index]
            permissionListener.onConfirm((res) => {
                dispatch('dialogStyle', {
                    ...dialogData,
                    status: true
                })
            })
            permissionListener.onComplete(async (res) => {
                permissionListener.stop()
                dispatch('dialogStyle', {
                    title: '',
                    content: '',
                    status: false
                })
            })
        } catch (err) {
            console.log('监听弹框错误', err);
        }
    },
    //弹框样式
    dialogStyle({ state }, data) {
        console.log(data);
        const { title, content, status } = data
        try {
            if (!status) return state.dialogView.close()
            const systemInfo = uni.getSystemInfoSync();
            const statusBarHeight = systemInfo.statusBarHeight;
            const navigationBarHeight = systemInfo.platform === 'android' ? 48 :
                44;
            const totalHeight = statusBarHeight + navigationBarHeight;
            state.dialogView = new plus.nativeObj.View('per-modal', {
                top: '0px',
                left: '0px',
                width: '100%',
                backgroundColor: '#444',
                //opacity: .5;
            })
            state.dialogView.drawRect({
                color: '#fff',
                radius: '5px'
            }, {
                top: totalHeight + 'px',
                left: '5%',
                width: '90%',
                height: "100px",
            })
            state.dialogView.drawText(title, {
                top: totalHeight + 5 + 'px',
                left: "8%",
                height: "30px"
            }, {
                align: "left",
                color: "#000",
            })
            state.dialogView.drawText(content, {
                top: totalHeight + 35 + 'px',
                height: "60px",
                left: "8%",
                width: "84%"
            }, {
                whiteSpace: 'normal',
                size: "14px",
                align: "left",
                color: "#656563"
            })
            state.dialogView.show()
        } catch (e) {
            console.log(e, '权限说明弹框样式错误');
        }
    },
    //跳转到app权限设置页面
    gotoAppPermissionSetting() {
        if (!uni.getSystemInfoSync().platform == 'android') {
            var UIApplication = plus.ios.import("UIApplication");
            var application2 = UIApplication.sharedApplication();
            var NSURL2 = plus.ios.import("NSURL");
            // var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");		
            var setting2 = NSURL2.URLWithString("app-settings:");
            application2.openURL(setting2);

            plus.ios.deleteObject(setting2);
            plus.ios.deleteObject(NSURL2);
            plus.ios.deleteObject(application2);
        } else {
            // console.log(plus.device.vendor);
            var Intent = plus.android.importClass("android.content.Intent");
            var Settings = plus.android.importClass("android.provider.Settings");
            var Uri = plus.android.importClass("android.net.Uri");
            var mainActivity = plus.android.runtimeMainActivity();
            var intent = new Intent();
            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
            var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
            intent.setData(uri);
            mainActivity.startActivity(intent);
        }
    }
}
export default {
    namespaced: true,
    state,
    actions
};

更多permissionID 值域清单权限  App权限判断和提示 - DCloud 插件市场

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值