HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

在manifest,json中填入的密钥,打包成apk后被解包,在AndroidManifest.xml中可以明文看到

安全漏洞

如题,请问这个问题作为开发者要怎么规避?

在manifest,json中填入的密钥,打包成apk后被解包,在AndroidManifest.xml中可以明文看到

如题,请问这个问题作为开发者要怎么规避?

在manifest,json中填入的密钥,打包成apk后被解包,在AndroidManifest.xml中可以明文看到

经纬度圆形围栏校验

<el-popover
placement="right"
width="400"
trigger="click">
<el-table :data="gridData">
<el-table-column width="150" property="date" label="日期"></el-table-column>
<el-table-column width="100" property="name" label="姓名"></el-table-column>
<el-table-column width="300" property="address" label="地址"></el-table-column>
</el-table>
<el-button slot="reference">click 激活</el-button>
</el-popover>

// 计算点到圆心的距离的函数
function distance(lat1, lng1, lat2, lng2) {
const radius = 6378137.0; // 地球半径
const rad = Math.PI / 180.0;
const lat1rad = lat1 rad;
const lat2rad = lat2
rad;
const a = Math.sin(lat1rad) Math.sin(lat2rad) + Math.cos(lat1rad) Math.cos(lat2rad) Math.cos((lng2 - lng1) rad);
return radius * Math.acos(a);
}

// 检查点是否在圆内的函数
function isPointInCircle(lat, lng, circleLat, circleLng, radius) {
const d = distance(lat, lng, circleLat, circleLng);
return d <= radius;
}

// 使用示例
const pointLat = 39.9903; // 点的纬度
const pointLng = 116.4814; // 点的经度
const circleLat = 39.9109; // 圆心的纬度
const circleLng = 116.4119; // 圆心的经度
const radius = 1000; // 圆的半径,单位为米

const isInside = isPointInCircle(pointLat, pointLng, circleLat, circleLng, radius);
console.log(isInside ? '点在圆内' : '点在圆外');

<dv-scroll-board :config="config" style="width:500px;height:220px" /> headerBGC oddRowBGC evenRowBGC waitTime rowNum

时间禁用
timeRange: '', // 绑定值
pickerOptions: {
// 时间跨度限制为2天
onPick: time => {
if (time.minDate && !time.maxDate) {
// 当选择开始日期时,‌设置结束日期的最小值为开始日期
this.timeRange = time.minDate;
} else if (time.maxDate) {
// 当选择结束日期时,‌清除开始日期的值
this.timeRange = '';
}
},
disabledDate: time => {
const today = new Date(); // 当前日期
if (this.timeRange) {
// 如果设置了开始日期,‌则禁止选择今天的日期之前的日期作为结束日期
return time.getTime() < today.getTime();
} else {
// 如果没有设置开始日期,‌则允许选择任何日期(‌这里可以根据需求自定义)‌
return false;
}
}
}

@@@@
<el-upload
action="https://jsonplaceholder.typicode.com/posts/"
list-type="picture-card"
on-preview="handlePictureCardPreview"
on-remove="handleRemove"> <i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
<script>
export default {
data() {
return {
dialogImageUrl: '',
dialogVisible: false
};
},
methods: {
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
}
}
</script>
继续阅读 »

<el-popover
placement="right"
width="400"
trigger="click">
<el-table :data="gridData">
<el-table-column width="150" property="date" label="日期"></el-table-column>
<el-table-column width="100" property="name" label="姓名"></el-table-column>
<el-table-column width="300" property="address" label="地址"></el-table-column>
</el-table>
<el-button slot="reference">click 激活</el-button>
</el-popover>

// 计算点到圆心的距离的函数
function distance(lat1, lng1, lat2, lng2) {
const radius = 6378137.0; // 地球半径
const rad = Math.PI / 180.0;
const lat1rad = lat1 rad;
const lat2rad = lat2
rad;
const a = Math.sin(lat1rad) Math.sin(lat2rad) + Math.cos(lat1rad) Math.cos(lat2rad) Math.cos((lng2 - lng1) rad);
return radius * Math.acos(a);
}

// 检查点是否在圆内的函数
function isPointInCircle(lat, lng, circleLat, circleLng, radius) {
const d = distance(lat, lng, circleLat, circleLng);
return d <= radius;
}

// 使用示例
const pointLat = 39.9903; // 点的纬度
const pointLng = 116.4814; // 点的经度
const circleLat = 39.9109; // 圆心的纬度
const circleLng = 116.4119; // 圆心的经度
const radius = 1000; // 圆的半径,单位为米

const isInside = isPointInCircle(pointLat, pointLng, circleLat, circleLng, radius);
console.log(isInside ? '点在圆内' : '点在圆外');

<dv-scroll-board :config="config" style="width:500px;height:220px" /> headerBGC oddRowBGC evenRowBGC waitTime rowNum

时间禁用
timeRange: '', // 绑定值
pickerOptions: {
// 时间跨度限制为2天
onPick: time => {
if (time.minDate && !time.maxDate) {
// 当选择开始日期时,‌设置结束日期的最小值为开始日期
this.timeRange = time.minDate;
} else if (time.maxDate) {
// 当选择结束日期时,‌清除开始日期的值
this.timeRange = '';
}
},
disabledDate: time => {
const today = new Date(); // 当前日期
if (this.timeRange) {
// 如果设置了开始日期,‌则禁止选择今天的日期之前的日期作为结束日期
return time.getTime() < today.getTime();
} else {
// 如果没有设置开始日期,‌则允许选择任何日期(‌这里可以根据需求自定义)‌
return false;
}
}
}

@@@@
<el-upload
action="https://jsonplaceholder.typicode.com/posts/"
list-type="picture-card"
on-preview="handlePictureCardPreview"
on-remove="handleRemove"> <i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
<script>
export default {
data() {
return {
dialogImageUrl: '',
dialogVisible: false
};
},
methods: {
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
}
}
</script>
收起阅读 »

【安卓权限说明顶部蒙层】uniapp(vue2)项目全局监听权限,HBuilderX (4.0+) android 平台支持;上架华为应用市场审核所需

应用上架 华为应用市场 权限 Android 安卓

安卓离线打包(本地打包)需要添加一些sdk,具体看这篇文章https://ask.dcloud.net.cn/article/41380


更新时间:2024-08-12 14:29
旧版时间:2024-08-12 14:06


测试部分,可能有bug
目前发现bug未解决
1、保存代码,代码热更新后,触发监听会报Error: [JS Framework] Failed to receiveTasks, instance (1) is not available.这个错误,重新编译正常

模拟器效果截图

permissionListener.js

/**  
 * @author cai  
 * @time 2024-08-12 14:29  
 * uni.createRequestPermissionListener()  
 * 文档地址:https://uniapp.dcloud.net.cn/api/system/create-request-permission-listener.html  
 * 注意:HBuilderX (4.0+) android 平台支持;HBuilderX 4.01 Vue2项目需要使用自定义基座测试监听权限申请的功能,标准基座暂不支持测试。  
 */  
// #ifndef APP  
export default null;  
// #endif   
// #ifdef APP  
const { osName } = uni.getSystemInfoSync();  
let permissionListener = null;  
// 是安卓平台,同时有uni.createRequestPermissionListener这个api  
if (osName === "android" && uni.createRequestPermissionListener) {  
    permissionListener = uni.createRequestPermissionListener();  
}  
let canRunListener = true;  // 是否可以执行所有监听方法  
let canStopListener = true; // 是否可以执行取消所有监听方法,避免唤起权限会触发App.vue的onHide生命周期  
const permissionEnums = {  
    "ACCESS_COARSE_LOCATION": {  
        name: "定位",  
        explain: "展示附近店铺、填写收货地址等相关功能"  
    },  
    "ACCESS_FINE_LOCATION": {  
        name: "定位",  
        explain: "展示附近店铺、填写收货地址等相关功能"  
    },  
    "READ_EXTERNAL_STORAGE": {  
        name: "存储",  
        explain: "上传图片、上传视频等相关功能"  
    },  
    "CAMERA": {  
        name: "相机",  
        explain: "扫二维码、拍摄图片等相关功能"  
    },  
    "WRITE_EXTERNAL_STORAGE": {  
        name: "存储",  
        explain: "把图片保存到相册等相关功能"  
    }  
}  

/**  
 * 权限说明文字  
 * @param {String} = permissionName  例如:ACCESS_COARSE_LOCATION  
 */  
const texts = (permissionName) => {  
    let title = "";  
    let content = "";  
    let permissionInfo = permissionEnums[permissionName] || null;  
    if (permissionInfo) {  
        const { name, explain } = permissionInfo;  
        title = `${name}权限使用说明`;  
        content = `将获取${name}权限,用于${explain}`;  
    } else {  
        title = "";  
        content = "";  
    }  
    return {  
        title,  
        content  
    }  
};  

/**  
 * 绘画顶部权限说明  
 * 文档地址:https://www.html5plus.org/doc/zh_cn/nativeobj.html  
 * @function drawView title标题,content描述使用说明  
 * @function hideView 隐藏顶部权限说明  
 */  
let view = null;  
const drawView = ({ title, content }) => {  
    console.log("drawView方法的参数值:", title, content);  
    if (view || !title || !content) return;     // 没有标题和内容则return出去  
    const { windowTop, windowWidth, statusBarHeight } = uni.getSystemInfoSync();  
    const topHeight = windowTop + statusBarHeight;  
    const distance = {  
        box: 10,    // 盒子距离视图两边的距离  
        text: 20    // 文字距离视图两边的距离  
    }  
    // 标题的相关样式  
    const titleStyle = {  
        size: 16,  
        height: 16,  
        top: `${topHeight + 22}`,  
        color: "#000",  
    }  
    // 内容的相关样式  
    const contentStyle = {  
        size: 14,  
        height: 0,  
        top: `${parseInt(titleStyle.top) + titleStyle.height + 6}`,  
        color: "#656563",  
    }  
    const contentLength = content.length;   // 权限说明内容文字长度  
    const contentWidth = windowWidth - distance.text * 2;   // 内容的宽度  
    const contentRowCount = Math.floor(contentWidth / contentStyle.size);   // 一行占几个文字  
    const contentRows = Math.ceil(contentLength / contentRowCount);     // 当前内容占几行  
    contentStyle.height = contentRows * (contentStyle.size + 4);    // 内容的高度  
    /**  
     * @description 计算盒子的高度  
     * 获取content到盒子顶部距离:parseInt(contentStyle.top) - topHeight - distance.box  
     * content的高度:contentStyle.height  
     * 获取content到盒子底部的距离:(distance.text - distance.box)  
     */  
    const boxHeight = (parseInt(contentStyle.top) - topHeight - distance.box) + contentStyle.height + (distance.text - distance.box);  
    view = new plus.nativeObj.View('per-modal', {  
        top: '0',  
        left: '0',  
        width: '100%',  
        backgroundColor: 'rgba(0,0,0,0.2)'  
    })  
    view.drawRect({  
        color: '#fff',  
        radius: '5px',  
    }, {  
        top: `${topHeight + distance.box}px`,  
        left: `${distance.box}px`,  
        right: `${distance.box}px`,  
        height: `${boxHeight}px`  
    })  
    view.drawText(title, {  
        top: `${titleStyle.top}px`,  
        left: `${distance.text}px`,  
        left: `${distance.text}px`,  
        height: `${titleStyle.height}px`  
    }, {  
        size: `${titleStyle.size}px`,  
        align: "left",  
        color: titleStyle.color,  
        weight: "bold"  
    })  
    view.drawText(content, {  
        top: `${contentStyle.top}px`,  
        left: `${distance.text}px`,  
        right: `${distance.text}px`,  
        height: `${contentStyle.height}px`,  
    }, {  
        size: `${contentStyle.size}px`,  
        lineSpacing: "2px",  
        align: "left",  
        color: contentStyle.color,  
        verticalAlign: "top",  
        whiteSpace: "normal"  
    })  
    let timer = setTimeout(() => {  
        view && view.show();  
        clearTimeout(timer);  
        timer = null;  
    }, 200)  
}  
// 关闭顶部权限说明  
const hideView = () => {  
    if (view) {  
        view.hide();  
        view = null;  
    }  
}  

// 监听权限方法  
const listenerFunc = () => {  
    stopFunc();     // 取消所有监听方法  
    if (canRunListener && permissionListener) {  
        let permissionName = "";    // 权限名称  
        let hasConfirm = false;     // 是否有权限弹窗(触发permissionListener.onConfirm这个回调)  
        canRunListener = false;  
        canStopListener = false;  
        // 监听申请系统权限  
        permissionListener.onRequest((e) => {  
            console.log("permissionListener.onRequest回调:", e);  
            if (Array.isArray(e) && e.length > 0) {  
                const stringToArray = e[0].split(".");  
                permissionName = stringToArray[stringToArray.length - 1];  
                console.log("权限名称:", permissionName);  
            }  
        });  
        // 监听弹出系统权限授权框  
        permissionListener.onConfirm((e) => {  
            console.log("permissionListener.onConfirm回调:", e);  
            hasConfirm = true;  
            if (permissionName) {  
                drawView(texts(permissionName));  
            }  
        });  
        // 监听权限申请完成  
        permissionListener.onComplete((e) => {  
            console.log("permissionListener.onComplete回调:", e);  
            // e.length === 0:权限列表无值,则不继续做相对逻辑  
            if (e.length === 0) return;  
            let name = "";      // 权限名称  
            let explain = "";   // 权限说明  
            if (permissionName && permissionEnums[permissionName]) {  
                name = permissionEnums[permissionName].name;  
                explain = permissionEnums[permissionName].explain;  
            }  
            const Manifest = plus.android.importClass("android.Manifest");  
            const MainActivity = plus.android.runtimeMainActivity();  
            const permissionStatus = MainActivity.checkSelfPermission(Manifest.permission[permissionName]);  
            console.log("当前权限状态:", permissionStatus);  
            /**  
             * @description 永久拒绝该权限,则引导用户前往设置页  
             * permissionStatus != 0:权限状态是拒绝  
             * !hasConfirm:没有permissionListener.onConfirm这个回调  
             */  
            if (permissionStatus != 0 && !hasConfirm && name && explain) {  
                uni.showModal({  
                    title: "温馨提示",  
                    content: `开启${name}权限后,才能${explain}`,  
                    showCancel: true,  
                    confirmText: "去设置",  
                    success: (res) => {  
                        if (res.confirm) {  
                            uni.openAppAuthorizeSetting();  
                        }  
                    }  
                })  
                return;  
            }  
            canStopListener = true;  
            hideView();  
        });  
    }  
}   

// 取消所有监听方法  
const stopFunc = () => {  
    if (canStopListener && permissionListener) {  
        console.log("执行permissionListener.stop()方法");  
        canRunListener = true;  
        hideView();  
        permissionListener.stop();  
    }  
}  

let exportObj = null;  
if (permissionListener) {  
    exportObj = {  
        listenerFunc,  
        stopFunc  
    };  
} else {  
    exportObj = null;  
}  

export default exportObj;  
// #endif

App.vue

    /**  
     * @author cai  
     * @time 2024-07-24 10:52  
     */  
    import permissionListener from  "@/utils/permissionListener.js";  
    export default {  
        onLaunch: function() {  
            console.log('App Launch')  
        },  
        onShow: function() {  
            permissionListener && permissionListener.listenerFunc();  
            console.log('App Show')   
        },  
        onHide: function() {   
            permissionListener && permissionListener.stopFunc();  
            console.log('App Hide')  
        }  
    }

↓↓↓ 各位大佬点点赞

继续阅读 »

安卓离线打包(本地打包)需要添加一些sdk,具体看这篇文章https://ask.dcloud.net.cn/article/41380


更新时间:2024-08-12 14:29
旧版时间:2024-08-12 14:06


测试部分,可能有bug
目前发现bug未解决
1、保存代码,代码热更新后,触发监听会报Error: [JS Framework] Failed to receiveTasks, instance (1) is not available.这个错误,重新编译正常

模拟器效果截图

permissionListener.js

/**  
 * @author cai  
 * @time 2024-08-12 14:29  
 * uni.createRequestPermissionListener()  
 * 文档地址:https://uniapp.dcloud.net.cn/api/system/create-request-permission-listener.html  
 * 注意:HBuilderX (4.0+) android 平台支持;HBuilderX 4.01 Vue2项目需要使用自定义基座测试监听权限申请的功能,标准基座暂不支持测试。  
 */  
// #ifndef APP  
export default null;  
// #endif   
// #ifdef APP  
const { osName } = uni.getSystemInfoSync();  
let permissionListener = null;  
// 是安卓平台,同时有uni.createRequestPermissionListener这个api  
if (osName === "android" && uni.createRequestPermissionListener) {  
    permissionListener = uni.createRequestPermissionListener();  
}  
let canRunListener = true;  // 是否可以执行所有监听方法  
let canStopListener = true; // 是否可以执行取消所有监听方法,避免唤起权限会触发App.vue的onHide生命周期  
const permissionEnums = {  
    "ACCESS_COARSE_LOCATION": {  
        name: "定位",  
        explain: "展示附近店铺、填写收货地址等相关功能"  
    },  
    "ACCESS_FINE_LOCATION": {  
        name: "定位",  
        explain: "展示附近店铺、填写收货地址等相关功能"  
    },  
    "READ_EXTERNAL_STORAGE": {  
        name: "存储",  
        explain: "上传图片、上传视频等相关功能"  
    },  
    "CAMERA": {  
        name: "相机",  
        explain: "扫二维码、拍摄图片等相关功能"  
    },  
    "WRITE_EXTERNAL_STORAGE": {  
        name: "存储",  
        explain: "把图片保存到相册等相关功能"  
    }  
}  

/**  
 * 权限说明文字  
 * @param {String} = permissionName  例如:ACCESS_COARSE_LOCATION  
 */  
const texts = (permissionName) => {  
    let title = "";  
    let content = "";  
    let permissionInfo = permissionEnums[permissionName] || null;  
    if (permissionInfo) {  
        const { name, explain } = permissionInfo;  
        title = `${name}权限使用说明`;  
        content = `将获取${name}权限,用于${explain}`;  
    } else {  
        title = "";  
        content = "";  
    }  
    return {  
        title,  
        content  
    }  
};  

/**  
 * 绘画顶部权限说明  
 * 文档地址:https://www.html5plus.org/doc/zh_cn/nativeobj.html  
 * @function drawView title标题,content描述使用说明  
 * @function hideView 隐藏顶部权限说明  
 */  
let view = null;  
const drawView = ({ title, content }) => {  
    console.log("drawView方法的参数值:", title, content);  
    if (view || !title || !content) return;     // 没有标题和内容则return出去  
    const { windowTop, windowWidth, statusBarHeight } = uni.getSystemInfoSync();  
    const topHeight = windowTop + statusBarHeight;  
    const distance = {  
        box: 10,    // 盒子距离视图两边的距离  
        text: 20    // 文字距离视图两边的距离  
    }  
    // 标题的相关样式  
    const titleStyle = {  
        size: 16,  
        height: 16,  
        top: `${topHeight + 22}`,  
        color: "#000",  
    }  
    // 内容的相关样式  
    const contentStyle = {  
        size: 14,  
        height: 0,  
        top: `${parseInt(titleStyle.top) + titleStyle.height + 6}`,  
        color: "#656563",  
    }  
    const contentLength = content.length;   // 权限说明内容文字长度  
    const contentWidth = windowWidth - distance.text * 2;   // 内容的宽度  
    const contentRowCount = Math.floor(contentWidth / contentStyle.size);   // 一行占几个文字  
    const contentRows = Math.ceil(contentLength / contentRowCount);     // 当前内容占几行  
    contentStyle.height = contentRows * (contentStyle.size + 4);    // 内容的高度  
    /**  
     * @description 计算盒子的高度  
     * 获取content到盒子顶部距离:parseInt(contentStyle.top) - topHeight - distance.box  
     * content的高度:contentStyle.height  
     * 获取content到盒子底部的距离:(distance.text - distance.box)  
     */  
    const boxHeight = (parseInt(contentStyle.top) - topHeight - distance.box) + contentStyle.height + (distance.text - distance.box);  
    view = new plus.nativeObj.View('per-modal', {  
        top: '0',  
        left: '0',  
        width: '100%',  
        backgroundColor: 'rgba(0,0,0,0.2)'  
    })  
    view.drawRect({  
        color: '#fff',  
        radius: '5px',  
    }, {  
        top: `${topHeight + distance.box}px`,  
        left: `${distance.box}px`,  
        right: `${distance.box}px`,  
        height: `${boxHeight}px`  
    })  
    view.drawText(title, {  
        top: `${titleStyle.top}px`,  
        left: `${distance.text}px`,  
        left: `${distance.text}px`,  
        height: `${titleStyle.height}px`  
    }, {  
        size: `${titleStyle.size}px`,  
        align: "left",  
        color: titleStyle.color,  
        weight: "bold"  
    })  
    view.drawText(content, {  
        top: `${contentStyle.top}px`,  
        left: `${distance.text}px`,  
        right: `${distance.text}px`,  
        height: `${contentStyle.height}px`,  
    }, {  
        size: `${contentStyle.size}px`,  
        lineSpacing: "2px",  
        align: "left",  
        color: contentStyle.color,  
        verticalAlign: "top",  
        whiteSpace: "normal"  
    })  
    let timer = setTimeout(() => {  
        view && view.show();  
        clearTimeout(timer);  
        timer = null;  
    }, 200)  
}  
// 关闭顶部权限说明  
const hideView = () => {  
    if (view) {  
        view.hide();  
        view = null;  
    }  
}  

// 监听权限方法  
const listenerFunc = () => {  
    stopFunc();     // 取消所有监听方法  
    if (canRunListener && permissionListener) {  
        let permissionName = "";    // 权限名称  
        let hasConfirm = false;     // 是否有权限弹窗(触发permissionListener.onConfirm这个回调)  
        canRunListener = false;  
        canStopListener = false;  
        // 监听申请系统权限  
        permissionListener.onRequest((e) => {  
            console.log("permissionListener.onRequest回调:", e);  
            if (Array.isArray(e) && e.length > 0) {  
                const stringToArray = e[0].split(".");  
                permissionName = stringToArray[stringToArray.length - 1];  
                console.log("权限名称:", permissionName);  
            }  
        });  
        // 监听弹出系统权限授权框  
        permissionListener.onConfirm((e) => {  
            console.log("permissionListener.onConfirm回调:", e);  
            hasConfirm = true;  
            if (permissionName) {  
                drawView(texts(permissionName));  
            }  
        });  
        // 监听权限申请完成  
        permissionListener.onComplete((e) => {  
            console.log("permissionListener.onComplete回调:", e);  
            // e.length === 0:权限列表无值,则不继续做相对逻辑  
            if (e.length === 0) return;  
            let name = "";      // 权限名称  
            let explain = "";   // 权限说明  
            if (permissionName && permissionEnums[permissionName]) {  
                name = permissionEnums[permissionName].name;  
                explain = permissionEnums[permissionName].explain;  
            }  
            const Manifest = plus.android.importClass("android.Manifest");  
            const MainActivity = plus.android.runtimeMainActivity();  
            const permissionStatus = MainActivity.checkSelfPermission(Manifest.permission[permissionName]);  
            console.log("当前权限状态:", permissionStatus);  
            /**  
             * @description 永久拒绝该权限,则引导用户前往设置页  
             * permissionStatus != 0:权限状态是拒绝  
             * !hasConfirm:没有permissionListener.onConfirm这个回调  
             */  
            if (permissionStatus != 0 && !hasConfirm && name && explain) {  
                uni.showModal({  
                    title: "温馨提示",  
                    content: `开启${name}权限后,才能${explain}`,  
                    showCancel: true,  
                    confirmText: "去设置",  
                    success: (res) => {  
                        if (res.confirm) {  
                            uni.openAppAuthorizeSetting();  
                        }  
                    }  
                })  
                return;  
            }  
            canStopListener = true;  
            hideView();  
        });  
    }  
}   

// 取消所有监听方法  
const stopFunc = () => {  
    if (canStopListener && permissionListener) {  
        console.log("执行permissionListener.stop()方法");  
        canRunListener = true;  
        hideView();  
        permissionListener.stop();  
    }  
}  

let exportObj = null;  
if (permissionListener) {  
    exportObj = {  
        listenerFunc,  
        stopFunc  
    };  
} else {  
    exportObj = null;  
}  

export default exportObj;  
// #endif

App.vue

    /**  
     * @author cai  
     * @time 2024-07-24 10:52  
     */  
    import permissionListener from  "@/utils/permissionListener.js";  
    export default {  
        onLaunch: function() {  
            console.log('App Launch')  
        },  
        onShow: function() {  
            permissionListener && permissionListener.listenerFunc();  
            console.log('App Show')   
        },  
        onHide: function() {   
            permissionListener && permissionListener.stopFunc();  
            console.log('App Hide')  
        }  
    }

↓↓↓ 各位大佬点点赞

收起阅读 »

小程序开发需求,找公司或工作室首次开发和长期维护

外包

微信小程序开发需求,前端+运营端+服务器端
前端uniapp,运营管理端H5, 后端uniCloud/或其他nodejs框架
找公司或工作室首次开发,前端页面完成了大概70%,需要按照功能/页面细化及对接后端,并完成未完成的页面部分。后端未有,可使用uniCloud-admin。
首次开发后,可合作长期维护迭代,因此需要稳定的公司或者工作室。

具体需求见附件。
有合适的公司或工作室请留下联系方式或email到618457@qq.com。

继续阅读 »

微信小程序开发需求,前端+运营端+服务器端
前端uniapp,运营管理端H5, 后端uniCloud/或其他nodejs框架
找公司或工作室首次开发,前端页面完成了大概70%,需要按照功能/页面细化及对接后端,并完成未完成的页面部分。后端未有,可使用uniCloud-admin。
首次开发后,可合作长期维护迭代,因此需要稳定的公司或者工作室。

具体需求见附件。
有合适的公司或工作室请留下联系方式或email到618457@qq.com。

收起阅读 »

android 和 ios app 打开唤起支付宝支付就差个s,无语...

                        var alipay=this.iosOrAndroid()?'alipay':'alipays'  
                            plus.runtime.openURL(alipay+"://platformapi/startapp?appId=20000067&url=" +  
                                encodeURIComponent(this.shareUrl('/pages/index/payMember1?record_id='+res.data.record_id+'&level='+this.parmar.level+'&days='+this.parmar.days)))  
继续阅读 »
                        var alipay=this.iosOrAndroid()?'alipay':'alipays'  
                            plus.runtime.openURL(alipay+"://platformapi/startapp?appId=20000067&url=" +  
                                encodeURIComponent(this.shareUrl('/pages/index/payMember1?record_id='+res.data.record_id+'&level='+this.parmar.level+'&days='+this.parmar.days)))  
收起阅读 »

uni.chooseimage出现的选择相册内容页面上面的按钮,无法自定义颜色,都是绿色的

uni.chooseimage出现的选择相册内容页面上面的按钮,无法自定义颜色,都是绿色的

uni.chooseimage出现的选择相册内容页面上面的按钮,无法自定义颜色,都是绿色的

uni-app-x webview组件不支持获取网页内容解决方案

Webview uni-app-x

问题

uni-app-x的webview组件貌似少了很多的接口,其中就包括获取网页信息,包括dom节点等等

思路

当webview加载完毕之后使用webview上下文向页面注入uni.webview.1.5.5.js代码,然后再注入一段操作dom的js代码,并将结果通过postmessage接口传回uts。话不多说,直接上代码

<template>  
    <web-view id="web-view" :src="url" :webview-styles="styles" @load="Load" @message="Message"></web-view>  
</template>  

<script>  
    let fs = uni.getFileSystemManager()  
    export default {  
        data() {  
            return {  
                url: '',  
                src: '',  
                styles: { progress: { color: "#07C160" } },  
                webviewContext: null as WebviewContext | null,  
                javascriptString: `  
                !function(document) {  
                    var title = document.title;  
                    uni.webView.postMessage({  
                        data: {  
                            documentTitle: title  
                        }  
                    })  
                }(document)  
                `  
            };  
        },  
        onLoad(options : OnLoadOptions) {  
            // 接受url参数  
            this.url = options["url"] ?? ""  
        },  
        onReady() {  
            // 创建webview上下文对象  
            this.webviewContext = uni.createWebviewContext('web-view', this)  
        },  
        methods: {  
            Load(e : UniWebViewLoadEvent) {  
                // 页面重新加载时获取页面链接地址  
                this.src = e.detail.src  
                // 读取库文件内容  
                fs.readFile({  
                    encoding: 'utf-8',  
                    filePath: '/hybrid/html/uni.webview.1.5.5.js',  
                    success: (res : ReadFileSuccessResult) => {  
                        // 将库文件代码和执行代码注入到网页  
                        this.webviewContext?.evalJS(res.data + this.javascriptString)  
                    }  
                } as ReadFileOptions)  
            },  
            // 接收网页传入的消息  
            Message(e : UniWebViewMessageEvent) {  
                let documentTitle = e.detail.data[0]['documentTitle'] as string  
                if (documentTitle == '') {  
                    documentTitle = this.src  
                }  
                // 设置网页标题  
                uni.setNavigationBarTitle({  
                    title: documentTitle  
                })  
            }  

        }  
    }  
</script>  

<style lang="scss">  

</style>
继续阅读 »

问题

uni-app-x的webview组件貌似少了很多的接口,其中就包括获取网页信息,包括dom节点等等

思路

当webview加载完毕之后使用webview上下文向页面注入uni.webview.1.5.5.js代码,然后再注入一段操作dom的js代码,并将结果通过postmessage接口传回uts。话不多说,直接上代码

<template>  
    <web-view id="web-view" :src="url" :webview-styles="styles" @load="Load" @message="Message"></web-view>  
</template>  

<script>  
    let fs = uni.getFileSystemManager()  
    export default {  
        data() {  
            return {  
                url: '',  
                src: '',  
                styles: { progress: { color: "#07C160" } },  
                webviewContext: null as WebviewContext | null,  
                javascriptString: `  
                !function(document) {  
                    var title = document.title;  
                    uni.webView.postMessage({  
                        data: {  
                            documentTitle: title  
                        }  
                    })  
                }(document)  
                `  
            };  
        },  
        onLoad(options : OnLoadOptions) {  
            // 接受url参数  
            this.url = options["url"] ?? ""  
        },  
        onReady() {  
            // 创建webview上下文对象  
            this.webviewContext = uni.createWebviewContext('web-view', this)  
        },  
        methods: {  
            Load(e : UniWebViewLoadEvent) {  
                // 页面重新加载时获取页面链接地址  
                this.src = e.detail.src  
                // 读取库文件内容  
                fs.readFile({  
                    encoding: 'utf-8',  
                    filePath: '/hybrid/html/uni.webview.1.5.5.js',  
                    success: (res : ReadFileSuccessResult) => {  
                        // 将库文件代码和执行代码注入到网页  
                        this.webviewContext?.evalJS(res.data + this.javascriptString)  
                    }  
                } as ReadFileOptions)  
            },  
            // 接收网页传入的消息  
            Message(e : UniWebViewMessageEvent) {  
                let documentTitle = e.detail.data[0]['documentTitle'] as string  
                if (documentTitle == '') {  
                    documentTitle = this.src  
                }  
                // 设置网页标题  
                uni.setNavigationBarTitle({  
                    title: documentTitle  
                })  
            }  

        }  
    }  
</script>  

<style lang="scss">  

</style>
收起阅读 »

zui-svg-icon自动生成icon

做了个监听文件改动的webpack插件,监听 /assets/svg-icons 下的文件改动,自动执行 npm run svgicon

就不用每次新增icon都跑一遍脚本

文件一: 根目录下创建:build/icon-watcher.js

const fs = require('fs');  
const path = require('path');  
const {  
    exec  
} = require('child_process');  

let timer = null;  

const watchDir = path.resolve(__dirname, '../assets/svg-icons');  
const projectRoot = path.resolve(__dirname, '../');  

class IconWatcherPlugin {  
    apply(compiler) {  
        compiler.hooks.afterEmit.tap('IconWatcherPlugin', () => {  
            console.log('zui-svg-icon 开始监听/assets/svg-icons文件改动')  
            fs.watch(watchDir, {  
                recursive: true  
            }, (eventType, filename) => {  
                if (filename) {  

                    if (timer) {  
                        clearTimeout(timer);  
                    }  
                    timer = setTimeout(() => {  
                        console.log(`zui-svg-icon 改动类型: ${eventType}`);  
                        console.log(`zui-svg-icon 文件名: ${filename}`);  
                        exec('npm run svgicon', {  
                            cwd: projectRoot  
                        }, (error, stdout, stderr) => {  
                            if (error) {  
                                console.error(`zui-svg-icon  exec error: ${error}`);  
                                console.error(`zui-svg-icon  stderr: ${stderr}`);  
                                return;  
                            }  
                            console.log(`zui-svg-icon stdout: ${stdout}`);  
                        });  
                    }, 500)  
                }  
            });  

        });  
    }  
}  

module.exports = IconWatcherPlugin;

vue.config.js中引入

javascript  

const IconWatcherPlugin = require('./build/icon-watcher');  

module.exports = {  
        // ....   
    configureWebpack: {  
        plugins: [new IconWatcherPlugin()],  
    }  
}

package.json中配置脚本

json  

{  

    "scripts": {  
        "svgicon": "node ./uni_modules/zui-svg-icon/tools/generate-svg-icon.js"  
    }  
}
继续阅读 »

做了个监听文件改动的webpack插件,监听 /assets/svg-icons 下的文件改动,自动执行 npm run svgicon

就不用每次新增icon都跑一遍脚本

文件一: 根目录下创建:build/icon-watcher.js

const fs = require('fs');  
const path = require('path');  
const {  
    exec  
} = require('child_process');  

let timer = null;  

const watchDir = path.resolve(__dirname, '../assets/svg-icons');  
const projectRoot = path.resolve(__dirname, '../');  

class IconWatcherPlugin {  
    apply(compiler) {  
        compiler.hooks.afterEmit.tap('IconWatcherPlugin', () => {  
            console.log('zui-svg-icon 开始监听/assets/svg-icons文件改动')  
            fs.watch(watchDir, {  
                recursive: true  
            }, (eventType, filename) => {  
                if (filename) {  

                    if (timer) {  
                        clearTimeout(timer);  
                    }  
                    timer = setTimeout(() => {  
                        console.log(`zui-svg-icon 改动类型: ${eventType}`);  
                        console.log(`zui-svg-icon 文件名: ${filename}`);  
                        exec('npm run svgicon', {  
                            cwd: projectRoot  
                        }, (error, stdout, stderr) => {  
                            if (error) {  
                                console.error(`zui-svg-icon  exec error: ${error}`);  
                                console.error(`zui-svg-icon  stderr: ${stderr}`);  
                                return;  
                            }  
                            console.log(`zui-svg-icon stdout: ${stdout}`);  
                        });  
                    }, 500)  
                }  
            });  

        });  
    }  
}  

module.exports = IconWatcherPlugin;

vue.config.js中引入

javascript  

const IconWatcherPlugin = require('./build/icon-watcher');  

module.exports = {  
        // ....   
    configureWebpack: {  
        plugins: [new IconWatcherPlugin()],  
    }  
}

package.json中配置脚本

json  

{  

    "scripts": {  
        "svgicon": "node ./uni_modules/zui-svg-icon/tools/generate-svg-icon.js"  
    }  
}
收起阅读 »

仿抖音短视频上下滑动视频组件分享

列表流畅滑动 微信小程序 短视频

分享短视频小程序上下滑动组件,支持微信小程序,抖音小程序,h5端,APP端。喜欢的小伙伴们下载使用,仅提供前端组件。

分享短视频小程序上下滑动组件,支持微信小程序,抖音小程序,h5端,APP端。喜欢的小伙伴们下载使用,仅提供前端组件。

现在插件广告收入低的过分了吧

现在插件广告收入低的过分了吧。哪怕是新上线的插件,每条广告的平均收入仅在3分啊

现在插件广告收入低的过分了吧。哪怕是新上线的插件,每条广告的平均收入仅在3分啊

hbuilderx开发的app获取公钥和MD5的方法

app备案

阿里云提示需要安装jadx-gui和使用mac来获取app的公钥和MD5这些信息。

这样就比较麻烦了,使用uniapp开发就是为了跨系统开发,假如还需要mac或jadx-gui,就不灵活了。

所以这里建议使用下面方式来获取公钥和md5值

ios查询方法:

https://www.yunedit.com/iosmd5

android查询方法:

https://www.yunedit.com/androidmd5​​​​​​

继续阅读 »

阿里云提示需要安装jadx-gui和使用mac来获取app的公钥和MD5这些信息。

这样就比较麻烦了,使用uniapp开发就是为了跨系统开发,假如还需要mac或jadx-gui,就不灵活了。

所以这里建议使用下面方式来获取公钥和md5值

ios查询方法:

https://www.yunedit.com/iosmd5

android查询方法:

https://www.yunedit.com/androidmd5​​​​​​

收起阅读 »

在global.scss文件中使用scss变量无效??

scss

在scss文件中使用scss变量无效??
经测试,在App端无效果,但是在Web中可以生效

但是在app.vue文件中使用可以生效

在uni.scss文件导入variables.scss和通过脚手架配置都不生效

继续阅读 »

在scss文件中使用scss变量无效??
经测试,在App端无效果,但是在Web中可以生效

但是在app.vue文件中使用可以生效

在uni.scss文件导入variables.scss和通过脚手架配置都不生效

收起阅读 »