HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

H5本地图片不显示、加载不出来的解决方案。

今天发布H5时发现不管在内置浏览器还是外部浏览器,本地图片都不能加载出来,路径是完全正确的,不管改为绝对路径还是相对路径都无法加载,后来打开manifest.json,点击源码视图找到H5配置,router下把"base" : "./",这句注释掉就能显示了,该行功能大概是发布H5时设为相对路径,能够在File协议里打开吧,但是路由会强制变为hash。

继续阅读 »

今天发布H5时发现不管在内置浏览器还是外部浏览器,本地图片都不能加载出来,路径是完全正确的,不管改为绝对路径还是相对路径都无法加载,后来打开manifest.json,点击源码视图找到H5配置,router下把"base" : "./",这句注释掉就能显示了,该行功能大概是发布H5时设为相对路径,能够在File协议里打开吧,但是路由会强制变为hash。

收起阅读 »

onNavigationBarButtonTap监听不生效问题

首先,我先描述一下我的问题,我是安卓手机不生效,但是苹果没有任何问题
然后我在论坛上看了下相关帖子,原因总结有以下几个:
1、单位不能用upx;(但是我用的是px)
2、按钮要是放在pages.json下边的第一个文件那里,会不执行;(完蛋了呢,但是我必须要放在首页啊)
3、如果动态改变titleNView的值,会不执行 (我确实动态改变了text 的值)

然后我的解决办法是根据第3个原因解决的

pages.json代码
"app-plus":{
"titleNView":{
"buttons":[
{
"text":"\ue60c",
"fontSrc":"/static/iconfont.ttf",
"fontSize":"20px",
"redDot": false,
"color":"#FFFFFF"
}
]
}
}

原先写法:
var webView = this.$mp.page.$getAppWebview();
var tn = webView.getStyle().titleNView;
if(this.selectedItem.index!=0){
tn.buttons[0].text= "修改前";
tn.buttons[0].redDot = false;
}else{
tn.buttons[0].text= "修改后";
if(count>0){
tn.buttons[0].redDot = true;
}else{
tn.buttons[0].redDot = false;
}
}
webView.setStyle({
titleNView: tn
});

这样还是不能跳转,然后我换了一种写法,就可以了

var webView = this.$mp.page.$getAppWebview();
if(this.selectedItem.index!=0){
webView.setTitleNViewButtonStyle(0,{text:'修改前',redDot:false});
}else{
if(count>0){
webView.setTitleNViewButtonStyle(0,{text:'修改后',redDot:true});
}else{
webView.setTitleNViewButtonStyle(0,{text:'修改后F',redDot:false});
}
}
如果你们要实现按钮的显示隐藏,可以跟我一样用小技巧,隐藏的时候,把按钮的颜色设置成标题栏的背景颜色。。

继续阅读 »

首先,我先描述一下我的问题,我是安卓手机不生效,但是苹果没有任何问题
然后我在论坛上看了下相关帖子,原因总结有以下几个:
1、单位不能用upx;(但是我用的是px)
2、按钮要是放在pages.json下边的第一个文件那里,会不执行;(完蛋了呢,但是我必须要放在首页啊)
3、如果动态改变titleNView的值,会不执行 (我确实动态改变了text 的值)

然后我的解决办法是根据第3个原因解决的

pages.json代码
"app-plus":{
"titleNView":{
"buttons":[
{
"text":"\ue60c",
"fontSrc":"/static/iconfont.ttf",
"fontSize":"20px",
"redDot": false,
"color":"#FFFFFF"
}
]
}
}

原先写法:
var webView = this.$mp.page.$getAppWebview();
var tn = webView.getStyle().titleNView;
if(this.selectedItem.index!=0){
tn.buttons[0].text= "修改前";
tn.buttons[0].redDot = false;
}else{
tn.buttons[0].text= "修改后";
if(count>0){
tn.buttons[0].redDot = true;
}else{
tn.buttons[0].redDot = false;
}
}
webView.setStyle({
titleNView: tn
});

这样还是不能跳转,然后我换了一种写法,就可以了

var webView = this.$mp.page.$getAppWebview();
if(this.selectedItem.index!=0){
webView.setTitleNViewButtonStyle(0,{text:'修改前',redDot:false});
}else{
if(count>0){
webView.setTitleNViewButtonStyle(0,{text:'修改后',redDot:true});
}else{
webView.setTitleNViewButtonStyle(0,{text:'修改后F',redDot:false});
}
}
如果你们要实现按钮的显示隐藏,可以跟我一样用小技巧,隐藏的时候,把按钮的颜色设置成标题栏的背景颜色。。

收起阅读 »

基于Native.js实现的安卓本地通知,已支持安卓8.0及以上版本

原生推送 Native.JS 通知栏

参考了很多大佬们的写法并进行了优化,最终实现安卓本地通知,已在安卓7.0以及10.0版本真机测试。
在需要生成通知的地方调用方法即可,在uni-app的App.vue的onShow中监听通知点击动作即可。
需要修改的话建议看一下安卓文档,链接https://developer.android.google.cn/reference/kotlin/android/app/Notification.Builder#addAction(android.app.Notification.Action)

附上项目地址,可以拿过来直接测试
https://github.com/BobCp/uniappTools#mynjsjs

方法直接封装在一个js文件中,取名xxx.js

let myNJS = {  
    'aOSNotify':aOSNotify,  
    'aOSReceive':aOSReceive,  
}  
// 获取版本信息  
const osVersion={  
    versionNum:()=>{  
        let SystemVersion = plus.os.version;  
        let VersionNumber = Number(SystemVersion.split('.')[0]);    
        return VersionNumber;  
    }  
}  
/**    
  * android原生通知发送    
  * @param title 通知标题  
  * @param content 通知内容    
  * @param data json对象,存储通知的隐藏数据,用于点击通知时接收使用    
  * @param channelID 字符串,安卓8.0才需要的通知渠道id,类型为字符串   
  * @param channelName 字符串,安卓8.0才需要的通知渠道名称,类型为字符串  
  */    
function aOSNotify(title = '', content = '', data = {}, channelID = '1', channelName = '自定义的通知') {    
    // #ifdef APP-PLUS    
    // var title = '通知标题';    

    console.log('准备通知');    
    console.log(plus.os.name);    

    // Android平台下才使用此推送    
    if (plus.os.name != 'Android') {    
      return false;    
    }  

    //随机生成通知ID    
    var notifyID = Math.floor(Math.random() * 10000) + 1;   
    //先给channel一个默认值  

    //传递参数  
    if (osVersion.versionNum() >= 8){  
        var payload = {  
            'title':title,  
            'msg':content,  
            'channel_id':channelID,  
            'notify_id':notifyID,    
            'data':data    
        };    
    } else {    
        var payload = {  
            'title':title,  
            'msg':content,  
            'notify_id':notifyID,    
            'data':data    
        };        
    }   

    // 获取应用主Activity实例对象  
    var main = plus.android.runtimeMainActivity();    
    // importClass: 导入Java类对象  
    // 导入上下文(官方翻译)  
    var Context = plus.android.importClass("android.content.Context");    
    // 导入通知管理相关  
    var NotificationManager = plus.android.importClass("android.app.NotificationManager");   
    var Notification = plus.android.importClass("android.app.Notification");   
    //   
    var Intent = plus.android.importClass("android.content.Intent");    
    var PendingIntent = plus.android.importClass("android.app.PendingIntent");      
    var nm = main.getSystemService(Context.NOTIFICATION_SERVICE);    

    var intent = new Intent(main, main.getClass());    
    intent.putExtra("receive", JSON.stringify(payload));    

    // PendingIntent.getActivity的第二个参数需要设置为随机数,否则多个通知时会导致前面的通知被后面的通知替换Extra的数据,此处我将其和对应通知的id绑定起来  
    var pendingIntent = PendingIntent.getActivity(main, notifyID, intent, PendingIntent.FLAG_CANCEL_CURRENT);    

    //可能用到R的一些资源文件  
    var r = plus.android.importClass("android.R");  

    var mNotification;  
    //判断当前系统版本在8.0及以上    
    if (osVersion.versionNum() >= 8){  
        if (nm.getNotificationChannel() == null){  
            var NotificationChannel = plus.android.importClass('android.app.NotificationChannel');  
            var channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH);    
            nm.createNotificationChannel(channel);    
        }  
        mNotification = new Notification.Builder(main, channelID);  
        mNotification.setDefaults(Notification.DEFAULT_ALL); // mNotification.setDefaults(~0);  
    } else {    
        mNotification = new Notification.Builder(main);     
        mNotification.setDefaults(Notification.DEFAULT_ALL);  
    }    
    var i= notifyID.toString();  
    mNotification.setVisibility(Notification.VISIBILITY_PUBLIC)     //设置可见性  
    //mNotification.setOngoing(true)        //设置这是否为“正在进行中”通知  
    mNotification.setContentTitle(title + i)       //设置标题    
    mNotification.setContentText(content);       //设置内容    
    mNotification.setAutoCancel(true);       //设置点击消失    
    mNotification.setShowWhen(true);       //显示通知时间,貌似不加这句也能显示     
    mNotification.setSmallIcon(r.drawable.stat_notify_chat);  
    //mNotification.setSmallIcon(17301543);       //设置app通知小图标,暂只能使用R.drawable文件中的默认值   

    mNotification.setTicker(title);       //设置发送到无障碍服务的“ ticker”文本。   
    // mNotification.setDefaults(~0);       //这两行结合上面的setTicker实现悬浮提示过几秒自动消失,基于覆盖考虑,把这个放到最上方  
    mNotification.setPriority(Notification.PRIORITY_HIGH);     //通知优先级   

    //mNotification.setFullScreenIntent(pendingIntent, false);   //弹出式提示不消失  
    mNotification.setContentIntent(pendingIntent);      
    var mNb = mNotification.build();    
    console.log(notifyID);  

    //判断当前系统版本在8.0及以上      
    if (osVersion.versionNum() >= 8){    
        nm.notify(channelID, notifyID, mNb);      
    } else {    
        nm.notify(notifyID, mNb);    
    }    

    //void plus.device.beep(2);//bee bee叫    
    // plus.device.vibrate(300);//震动    

    console.log('通知结束');    
    return true;    
    // #endif    
}  
 /**    
   * android原生通知点击后所执行的内容    
   */    
function aOSReceive() {    
    // #ifdef APP-PLUS    
    if (plus.os.name != 'Android') {    
        return false;    
    }    
    // android原生通知栏接收器(程序退出后无效)    
    var main = plus.android.runtimeMainActivity();    
    var intent = main.getIntent();    
    var message = intent && intent.getExtra != undefined ? intent.getExtra("receive") : null;    
    console.log(message);    
    //console.log(typeof(message))  
    message = message ? JSON.parse(message) : '';    
    // var args = plus.runtime.arguments;  
    // console.log(args)  
    if (message) {    
        //删除当前通知    
        var Context = plus.android.importClass("android.content.Context");    
        var nm = main.getSystemService(Context.NOTIFICATION_SERVICE);    
        console.log(message.notify_id);    
        if (osVersion.versionNum() >= 8){  
            nm.cancel(message.channel_id, message.notify_id);//安卓版本大于等于8的        
        } else {  
            nm.cancel(message.notify_id);//安卓版本小于8的    
        }   
        // nm.cancelAll();    

        // 把消息数据置空,以免再次打开APP时重复执行此处    
        intent.putExtra("receive", '');    

        //用message的数据做点什么  
        uni.showToast({  
            title: message.notify_id.toString(),  
            position:'bottom'  
        })  
    }    
    // #endif    
}   

  export default myNJS

建议在main.js中进行全局注册,按需调整

import myNJS from 'my_utils/myNJS.js'  
Vue.prototype.$myNJS = myNJS

在需要生成通知的逻辑中进行通知生成,这里是测试数据

this.$myNJS.aOSNotify('通知来了', '这是一条自定义的提醒', {    
    params1: 'params1',    
    params2: 'params2',    
    params3: 'params3',    
    params4: 'params4'    
});

在项目中App.vue的onShow中监听通知点击

    onShow: function() {  
        console.log('App Show');  
        // #ifdef APP-PLUS  
        // 通知点击动作触发意图  
        // aOSReceive()  
        this.$myNJS.aOSReceive();    
        // #endif   
    },
继续阅读 »

参考了很多大佬们的写法并进行了优化,最终实现安卓本地通知,已在安卓7.0以及10.0版本真机测试。
在需要生成通知的地方调用方法即可,在uni-app的App.vue的onShow中监听通知点击动作即可。
需要修改的话建议看一下安卓文档,链接https://developer.android.google.cn/reference/kotlin/android/app/Notification.Builder#addAction(android.app.Notification.Action)

附上项目地址,可以拿过来直接测试
https://github.com/BobCp/uniappTools#mynjsjs

方法直接封装在一个js文件中,取名xxx.js

let myNJS = {  
    'aOSNotify':aOSNotify,  
    'aOSReceive':aOSReceive,  
}  
// 获取版本信息  
const osVersion={  
    versionNum:()=>{  
        let SystemVersion = plus.os.version;  
        let VersionNumber = Number(SystemVersion.split('.')[0]);    
        return VersionNumber;  
    }  
}  
/**    
  * android原生通知发送    
  * @param title 通知标题  
  * @param content 通知内容    
  * @param data json对象,存储通知的隐藏数据,用于点击通知时接收使用    
  * @param channelID 字符串,安卓8.0才需要的通知渠道id,类型为字符串   
  * @param channelName 字符串,安卓8.0才需要的通知渠道名称,类型为字符串  
  */    
function aOSNotify(title = '', content = '', data = {}, channelID = '1', channelName = '自定义的通知') {    
    // #ifdef APP-PLUS    
    // var title = '通知标题';    

    console.log('准备通知');    
    console.log(plus.os.name);    

    // Android平台下才使用此推送    
    if (plus.os.name != 'Android') {    
      return false;    
    }  

    //随机生成通知ID    
    var notifyID = Math.floor(Math.random() * 10000) + 1;   
    //先给channel一个默认值  

    //传递参数  
    if (osVersion.versionNum() >= 8){  
        var payload = {  
            'title':title,  
            'msg':content,  
            'channel_id':channelID,  
            'notify_id':notifyID,    
            'data':data    
        };    
    } else {    
        var payload = {  
            'title':title,  
            'msg':content,  
            'notify_id':notifyID,    
            'data':data    
        };        
    }   

    // 获取应用主Activity实例对象  
    var main = plus.android.runtimeMainActivity();    
    // importClass: 导入Java类对象  
    // 导入上下文(官方翻译)  
    var Context = plus.android.importClass("android.content.Context");    
    // 导入通知管理相关  
    var NotificationManager = plus.android.importClass("android.app.NotificationManager");   
    var Notification = plus.android.importClass("android.app.Notification");   
    //   
    var Intent = plus.android.importClass("android.content.Intent");    
    var PendingIntent = plus.android.importClass("android.app.PendingIntent");      
    var nm = main.getSystemService(Context.NOTIFICATION_SERVICE);    

    var intent = new Intent(main, main.getClass());    
    intent.putExtra("receive", JSON.stringify(payload));    

    // PendingIntent.getActivity的第二个参数需要设置为随机数,否则多个通知时会导致前面的通知被后面的通知替换Extra的数据,此处我将其和对应通知的id绑定起来  
    var pendingIntent = PendingIntent.getActivity(main, notifyID, intent, PendingIntent.FLAG_CANCEL_CURRENT);    

    //可能用到R的一些资源文件  
    var r = plus.android.importClass("android.R");  

    var mNotification;  
    //判断当前系统版本在8.0及以上    
    if (osVersion.versionNum() >= 8){  
        if (nm.getNotificationChannel() == null){  
            var NotificationChannel = plus.android.importClass('android.app.NotificationChannel');  
            var channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH);    
            nm.createNotificationChannel(channel);    
        }  
        mNotification = new Notification.Builder(main, channelID);  
        mNotification.setDefaults(Notification.DEFAULT_ALL); // mNotification.setDefaults(~0);  
    } else {    
        mNotification = new Notification.Builder(main);     
        mNotification.setDefaults(Notification.DEFAULT_ALL);  
    }    
    var i= notifyID.toString();  
    mNotification.setVisibility(Notification.VISIBILITY_PUBLIC)     //设置可见性  
    //mNotification.setOngoing(true)        //设置这是否为“正在进行中”通知  
    mNotification.setContentTitle(title + i)       //设置标题    
    mNotification.setContentText(content);       //设置内容    
    mNotification.setAutoCancel(true);       //设置点击消失    
    mNotification.setShowWhen(true);       //显示通知时间,貌似不加这句也能显示     
    mNotification.setSmallIcon(r.drawable.stat_notify_chat);  
    //mNotification.setSmallIcon(17301543);       //设置app通知小图标,暂只能使用R.drawable文件中的默认值   

    mNotification.setTicker(title);       //设置发送到无障碍服务的“ ticker”文本。   
    // mNotification.setDefaults(~0);       //这两行结合上面的setTicker实现悬浮提示过几秒自动消失,基于覆盖考虑,把这个放到最上方  
    mNotification.setPriority(Notification.PRIORITY_HIGH);     //通知优先级   

    //mNotification.setFullScreenIntent(pendingIntent, false);   //弹出式提示不消失  
    mNotification.setContentIntent(pendingIntent);      
    var mNb = mNotification.build();    
    console.log(notifyID);  

    //判断当前系统版本在8.0及以上      
    if (osVersion.versionNum() >= 8){    
        nm.notify(channelID, notifyID, mNb);      
    } else {    
        nm.notify(notifyID, mNb);    
    }    

    //void plus.device.beep(2);//bee bee叫    
    // plus.device.vibrate(300);//震动    

    console.log('通知结束');    
    return true;    
    // #endif    
}  
 /**    
   * android原生通知点击后所执行的内容    
   */    
function aOSReceive() {    
    // #ifdef APP-PLUS    
    if (plus.os.name != 'Android') {    
        return false;    
    }    
    // android原生通知栏接收器(程序退出后无效)    
    var main = plus.android.runtimeMainActivity();    
    var intent = main.getIntent();    
    var message = intent && intent.getExtra != undefined ? intent.getExtra("receive") : null;    
    console.log(message);    
    //console.log(typeof(message))  
    message = message ? JSON.parse(message) : '';    
    // var args = plus.runtime.arguments;  
    // console.log(args)  
    if (message) {    
        //删除当前通知    
        var Context = plus.android.importClass("android.content.Context");    
        var nm = main.getSystemService(Context.NOTIFICATION_SERVICE);    
        console.log(message.notify_id);    
        if (osVersion.versionNum() >= 8){  
            nm.cancel(message.channel_id, message.notify_id);//安卓版本大于等于8的        
        } else {  
            nm.cancel(message.notify_id);//安卓版本小于8的    
        }   
        // nm.cancelAll();    

        // 把消息数据置空,以免再次打开APP时重复执行此处    
        intent.putExtra("receive", '');    

        //用message的数据做点什么  
        uni.showToast({  
            title: message.notify_id.toString(),  
            position:'bottom'  
        })  
    }    
    // #endif    
}   

  export default myNJS

建议在main.js中进行全局注册,按需调整

import myNJS from 'my_utils/myNJS.js'  
Vue.prototype.$myNJS = myNJS

在需要生成通知的逻辑中进行通知生成,这里是测试数据

this.$myNJS.aOSNotify('通知来了', '这是一条自定义的提醒', {    
    params1: 'params1',    
    params2: 'params2',    
    params3: 'params3',    
    params4: 'params4'    
});

在项目中App.vue的onShow中监听通知点击

    onShow: function() {  
        console.log('App Show');  
        // #ifdef APP-PLUS  
        // 通知点击动作触发意图  
        // aOSReceive()  
        this.$myNJS.aOSReceive();    
        // #endif   
    },
收起阅读 »

能否增加一个我自认为很实用的功能?局部格式化

HBuilder

当我前端代码运用freemaker语法时,使用格式化,会对js部分的代码格式化有影响,对js写了之后的相关js提示也失去提示,识别为DOM节点语法,能否增加一个功能选中freemaker甚至其他非正常的语法使其屏蔽该部分使其只能编辑,不影响原有的功能。

当我前端代码运用freemaker语法时,使用格式化,会对js部分的代码格式化有影响,对js写了之后的相关js提示也失去提示,识别为DOM节点语法,能否增加一个功能选中freemaker甚至其他非正常的语法使其屏蔽该部分使其只能编辑,不影响原有的功能。

uni-app从安装到使用,简单介绍

uniapp

简单介绍
uni-app实现了一套代码,同时运行到多个平台,可发布到iOS、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉)等多个平台。
基于通用的前端技术栈,采用vue语法+微信小程序api,比较容易上手
官网:https://uniapp.dcloud.io/

工具安装
开发uni-app需要安装HBuilderX

创建项目
点击左上角【文件】>>【新建】>>【项目】,选择uni-app模板。

uni-app目录结构

uni-app有自己的插件库,直接点击安装即可,而且,它也支持npm包管理,点击【工具】>> 【插件安装】即可配置

调试
1.浏览器
调试之前我们首先要配置一下你的浏览器的安装路径,默认HBuilder是检测不到的
点击【运行】>>【运行到浏览器】>> 【配置web服务器】
配置好了后,在点击【运行】>>【运行到浏览器】>> 选择电脑内的浏览器运行

2.手机调试
数据线连接电脑,点击【运行】>>【运行到手机或模拟器】>> 选择已连接的手机,手机上会安装一个HBuilder app,之后在开发环境中做的所有效果都会渲染到这个app中。
1)安卓手机,需要开启【USB调试】;
2)iphone手机,连接手机后直接运行,会报错,需要获得手机信任,步骤如下:
【设置】>> 【通用】>> 【设备管理】,信任一下HBuilder即可。

3.小程序调试:
点击 【运行】 >> 【运行到小程序模拟器】>> 【运行设置】
需要注意两点:
1)微信开发者工具必须是官方最新版。
2)微信开发者工具需要授权,方法如下:
打开微信开发者工具,点击设置图标,选择【安全】,服务端口选择开启,然后即可进行微信小程序调试
*百度,支付宝,字节跳动小程序步骤和微信类似

打包
1.h5
点击 【发行】 >> 【网站-h5手机版】

2.app(android,ios)
1)配置应用图标,否则是默认的hbuilder的logo

2)按照业务需求配置sdk与模块权限配置,例如用到了video组件,需要在App模块权限配置中勾选VideoPlayer选项,否则app打开会提示报错,还有微信登录授权,支付,分享等;

3)点击 【发行】 >> 【原生APP-云打包】

注:
证书密钥之类的文件需要安卓ios开发人员提供;
安卓测试包可以直接安装,ios测试包需要在打包时添加测试手机id

3.微信小程序
点击 【发行】 >> 【小程序-微信】

再到微信开发者工具中按照小程序发布流程:上传成体验版>> 提交审核>>发布

打包注意:
接口域名要记得手动切换测试环境或线上环境;

缺点:uni-app问世的时间还比较短,有很多地方还不是完善,坑比较多,而且对于使用中的一些bug及问题,官方回应的不是很及时,不过去社区一起讨论,也可以加一下qq群(官网有提供,现有30多个)。

总之,个人认为如果项目针对多个端开发,还是很值得使用uni-app的。

继续阅读 »

简单介绍
uni-app实现了一套代码,同时运行到多个平台,可发布到iOS、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉)等多个平台。
基于通用的前端技术栈,采用vue语法+微信小程序api,比较容易上手
官网:https://uniapp.dcloud.io/

工具安装
开发uni-app需要安装HBuilderX

创建项目
点击左上角【文件】>>【新建】>>【项目】,选择uni-app模板。

uni-app目录结构

uni-app有自己的插件库,直接点击安装即可,而且,它也支持npm包管理,点击【工具】>> 【插件安装】即可配置

调试
1.浏览器
调试之前我们首先要配置一下你的浏览器的安装路径,默认HBuilder是检测不到的
点击【运行】>>【运行到浏览器】>> 【配置web服务器】
配置好了后,在点击【运行】>>【运行到浏览器】>> 选择电脑内的浏览器运行

2.手机调试
数据线连接电脑,点击【运行】>>【运行到手机或模拟器】>> 选择已连接的手机,手机上会安装一个HBuilder app,之后在开发环境中做的所有效果都会渲染到这个app中。
1)安卓手机,需要开启【USB调试】;
2)iphone手机,连接手机后直接运行,会报错,需要获得手机信任,步骤如下:
【设置】>> 【通用】>> 【设备管理】,信任一下HBuilder即可。

3.小程序调试:
点击 【运行】 >> 【运行到小程序模拟器】>> 【运行设置】
需要注意两点:
1)微信开发者工具必须是官方最新版。
2)微信开发者工具需要授权,方法如下:
打开微信开发者工具,点击设置图标,选择【安全】,服务端口选择开启,然后即可进行微信小程序调试
*百度,支付宝,字节跳动小程序步骤和微信类似

打包
1.h5
点击 【发行】 >> 【网站-h5手机版】

2.app(android,ios)
1)配置应用图标,否则是默认的hbuilder的logo

2)按照业务需求配置sdk与模块权限配置,例如用到了video组件,需要在App模块权限配置中勾选VideoPlayer选项,否则app打开会提示报错,还有微信登录授权,支付,分享等;

3)点击 【发行】 >> 【原生APP-云打包】

注:
证书密钥之类的文件需要安卓ios开发人员提供;
安卓测试包可以直接安装,ios测试包需要在打包时添加测试手机id

3.微信小程序
点击 【发行】 >> 【小程序-微信】

再到微信开发者工具中按照小程序发布流程:上传成体验版>> 提交审核>>发布

打包注意:
接口域名要记得手动切换测试环境或线上环境;

缺点:uni-app问世的时间还比较短,有很多地方还不是完善,坑比较多,而且对于使用中的一些bug及问题,官方回应的不是很及时,不过去社区一起讨论,也可以加一下qq群(官网有提供,现有30多个)。

总之,个人认为如果项目针对多个端开发,还是很值得使用uni-app的。

收起阅读 »

分享相关记录

分享

1.h5使用微信sdk分享,分享的icon大小不能太大,控制在5kb以内

1.h5使用微信sdk分享,分享的icon大小不能太大,控制在5kb以内

更新v3注意事项:

v3

1.报错


解决:看看自己封装的组件里面有没有初始值设置为null的,只要有的,都要改成对应的类型, 比如数组就初始化为 arr:[]

1.报错


解决:看看自己封装的组件里面有没有初始值设置为null的,只要有的,都要改成对应的类型, 比如数组就初始化为 arr:[]

害,9名程序员被抓!这次真的活该.....

法律科普

两年时间
这家软件公司开发了50余款APP获利500万元

结果却是警察找上门
9名程序员被抓......

近日,平安时报平台发布了这样一起“程序员犯罪”事件。下面跟一起探究一下事件的缘由,无论何时引以为戒。

事件要从9月下旬说起,一名叫小何(化名)的朋友到派出所报警,表示自己网上炒股被人骗走了近20万元。在案件侦办过程中,民警发现小何手机中下载的一款赌博软件也是诈骗软件。

于是,他们顺藤摸瓜找到了制作和维护该软件的公司——位于深圳的某文化科技有限公司,而成立该公司的犯罪团伙长期盘踞在深圳从事开发赌博软件业务。在经过一个多月侦查后,12月3日,民警一举将李某等9名犯罪嫌疑人抓获。

经审查,该团伙交代, 2018年3月时由李某召集韩某、骆某双等其余几人成立了此公司,后长期从事赌博软件的开发业务,服务于多个赌博集团。仅仅在两年的时间内,他们便制作了50余款赌博APP。这些赌博APP一款便能卖出8万元的价格,他们从中非法获利500万元。

而这些赌博软件经过设置后,都有一开始能赢钱,随后便会一直输钱,以及无法提现等特点。也正是因为这些特点,购买这些赌博软件的买家,其中一些人将购买的软件用于赌博,另一些则将软件用于诈骗。

除制作软件外,该公司还进行一条龙服务,从他们那卖出去的赌博APP,一旦运行时遇到问题,卖家可以继续找他们进行维护,即提供售后服务。明知他人利用软件实施犯罪,但仍为其犯罪提供技术帮助,该公司的行为已经构成帮助信息网络犯罪活动罪。

目前,9名犯罪嫌疑人均已被采取刑事强制措施,公司资金已被冻结,案件还在进一步侦办中。(案件内容及图片来源于平安时报,如有不当请联系删除)

近年来,程序员违法犯罪的案例比之前有所提升,相关新闻经常可以看到。

从有些案件中我们了解到,程序员甚至是无意识的,根本不清楚自己的行为是否为违法犯罪。不管是有意还是无意,都是程序员缺乏相应法律意识的表现。作为一名程序员,不要认为自己只是写代码的,抓人也不是抓自己。要有“雷区”意识,不要涉及黄赌毒相关,或者沾边的开发项目,那怕你只是负责其中一环。

下面为大家分享一下程序员不能碰的职业红线,希望各位程序员朋友无论何时都要绷紧法律这根弦。

切莫心怀侥幸,以身试法,技术无罪,坚守正道。

来源:转载公众号文章,如有不当联系删除。原文链接

继续阅读 »

两年时间
这家软件公司开发了50余款APP获利500万元

结果却是警察找上门
9名程序员被抓......

近日,平安时报平台发布了这样一起“程序员犯罪”事件。下面跟一起探究一下事件的缘由,无论何时引以为戒。

事件要从9月下旬说起,一名叫小何(化名)的朋友到派出所报警,表示自己网上炒股被人骗走了近20万元。在案件侦办过程中,民警发现小何手机中下载的一款赌博软件也是诈骗软件。

于是,他们顺藤摸瓜找到了制作和维护该软件的公司——位于深圳的某文化科技有限公司,而成立该公司的犯罪团伙长期盘踞在深圳从事开发赌博软件业务。在经过一个多月侦查后,12月3日,民警一举将李某等9名犯罪嫌疑人抓获。

经审查,该团伙交代, 2018年3月时由李某召集韩某、骆某双等其余几人成立了此公司,后长期从事赌博软件的开发业务,服务于多个赌博集团。仅仅在两年的时间内,他们便制作了50余款赌博APP。这些赌博APP一款便能卖出8万元的价格,他们从中非法获利500万元。

而这些赌博软件经过设置后,都有一开始能赢钱,随后便会一直输钱,以及无法提现等特点。也正是因为这些特点,购买这些赌博软件的买家,其中一些人将购买的软件用于赌博,另一些则将软件用于诈骗。

除制作软件外,该公司还进行一条龙服务,从他们那卖出去的赌博APP,一旦运行时遇到问题,卖家可以继续找他们进行维护,即提供售后服务。明知他人利用软件实施犯罪,但仍为其犯罪提供技术帮助,该公司的行为已经构成帮助信息网络犯罪活动罪。

目前,9名犯罪嫌疑人均已被采取刑事强制措施,公司资金已被冻结,案件还在进一步侦办中。(案件内容及图片来源于平安时报,如有不当请联系删除)

近年来,程序员违法犯罪的案例比之前有所提升,相关新闻经常可以看到。

从有些案件中我们了解到,程序员甚至是无意识的,根本不清楚自己的行为是否为违法犯罪。不管是有意还是无意,都是程序员缺乏相应法律意识的表现。作为一名程序员,不要认为自己只是写代码的,抓人也不是抓自己。要有“雷区”意识,不要涉及黄赌毒相关,或者沾边的开发项目,那怕你只是负责其中一环。

下面为大家分享一下程序员不能碰的职业红线,希望各位程序员朋友无论何时都要绷紧法律这根弦。

切莫心怀侥幸,以身试法,技术无罪,坚守正道。

来源:转载公众号文章,如有不当联系删除。原文链接

收起阅读 »

升级到3.0.4.20201231并重新云打包后 uni.getStorageSync 取不到数据

升级到 3.0.4.20201231

并重新云打包后 (自定义基座)

uni.getStorageSync 取不到数据

各位是不是有这个问题???

升级到 3.0.4.20201231

并重新云打包后 (自定义基座)

uni.getStorageSync 取不到数据

各位是不是有这个问题???

类似element中select 框带搜索功能(前端实现搜索)

uniapp也是第一次接触,效果图:

上代码:

<template>  
    <u-form-item label="所属城市" label-width="60px">  
        <u-input v-model="region.name"  
            ref="searchInp"  
            @input="filterRegion"  
            @blur="hideSearch"  
            @click="flag = true"/>  
        <view class="popup" v-show="flag" >  
            <view class="sanjiao"></view>  
            <view class="search" v-if="searchVal.length > 0">  
                <u-loading class="search-loading" mode="circle" :show="search_loading"></u-loading>  
                <text class="o-li"  
                @click="handleRegion(item)"  
                v-for="item in searchVal"  
                >{{ item.region_name }}</text>  
            </view>  
            <view class="not-data" v-else>--无匹配数据--</view>  
        </view>  
    </u-form-item>  

</template>  

<script>  
export default {  
    data() {  
        return {  
            flag: false,  
            search_loading: false,  
            searchVal: [],  
            region: {  
                name: '',  
                id: ''  
            },  
            list:[  
                {region_name: '池州', region_id: 10001},  
                {region_name: '惠州', region_id: 10001},  
                {region_name: '徽州', region_id: 10001},  
                {region_name: '凉州', region_id: 10001},  
                {region_name: '常州', region_id: 10001},  
                {region_name: '苏州', region_id: 10001},  
                {region_name: '京州', region_id: 10001},  
                {region_name: '永州', region_id: 10001}  
            ]  
        }  
    },  
    props: {  

    },  
    created() {  
        this.searchVal = this.list;  
    },  
    methods:{  
        handleRegion(item) {  
            this.region.name = item.region_name;  
            this.region.id = item.region_id;  
            this.flag = false;  
            this.$emit("getRegion", this.region);  
        },  
        hideSearch() {  
            setTimeout(() => {  
                this.flag = false;  
            },300)  
        },  
        filterRegion() {  

            let ziduan = this.region.name;  
            let rows = [];  
            this.search_loading = true;  
            rows = this.list.filter(item => {  
                if(item.region_name.indexOf(ziduan) != -1){  
                    return item  
                }  
            })  

            if(ziduan.length > 0 ){  
                this.searchVal = rows;  
                this.search_loading = false;  
            }else{  
                this.searchVal = this.list;  
                this.search_loading = false;  
            }  

        }  
    }  
}  
</script>  

<style lang="scss">  
.popup{  
    padding: 8px 0;  
    position: absolute;  
    top: 10vh;  
    z-index: 99;  
    transition: all 1s ease-in;  

    .sanjiao{  
        display: block;  
        width: 8px;  
        height: 8px;  
        background-color: #fff;  
        position: absolute;  
        top: -4px;  
        left: 30px;  
        transform: rotate(45deg);  

    }  
    .not-data{  
        width: 80vw;  
        border-radius: 5px;  
        line-height: 60rpx;  
        text-align: center;  
        background-color: #fff;  
        margin-top: -8px;  
        box-shadow: 0 -2px 12px 0 rgba(0,0,0,.1);  
        color: #666;  
        font-size: 24rpx;  
    }  
    .search{  
        width: 80vw;  
        max-height: 50vh;  
        overflow: hidden;  
        overflow-y: auto;  
        background-color: #fff;  
        border-radius: 5px;  
        margin-top: -8px;  
        box-shadow: 0 -2px 12px 0 rgba(0,0,0,.1);  
        .o-li{  
            display: block;  
            line-height: 60rpx;  
            padding: 0 5px;  
            &:after{  
                content: "";  
                display: block;  
                width: 100%;  
                height: 1px;  
                background-color: #eee;  
                transform: scaleY(0.5);  
            }  
        }  
        .search-loading{  
            position: absolute;  
            top: 50%;  
            left: 50%;  
            transform: translate(-50%, -50%);  
        }  
    }  
}  
</style>  

注意项: 如果要写成组件,在小程序中是不支持props中的值直接修改的,处理办法是 直接在组件内部增加一个对象,来替换props的值;

继续阅读 »

uniapp也是第一次接触,效果图:

上代码:

<template>  
    <u-form-item label="所属城市" label-width="60px">  
        <u-input v-model="region.name"  
            ref="searchInp"  
            @input="filterRegion"  
            @blur="hideSearch"  
            @click="flag = true"/>  
        <view class="popup" v-show="flag" >  
            <view class="sanjiao"></view>  
            <view class="search" v-if="searchVal.length > 0">  
                <u-loading class="search-loading" mode="circle" :show="search_loading"></u-loading>  
                <text class="o-li"  
                @click="handleRegion(item)"  
                v-for="item in searchVal"  
                >{{ item.region_name }}</text>  
            </view>  
            <view class="not-data" v-else>--无匹配数据--</view>  
        </view>  
    </u-form-item>  

</template>  

<script>  
export default {  
    data() {  
        return {  
            flag: false,  
            search_loading: false,  
            searchVal: [],  
            region: {  
                name: '',  
                id: ''  
            },  
            list:[  
                {region_name: '池州', region_id: 10001},  
                {region_name: '惠州', region_id: 10001},  
                {region_name: '徽州', region_id: 10001},  
                {region_name: '凉州', region_id: 10001},  
                {region_name: '常州', region_id: 10001},  
                {region_name: '苏州', region_id: 10001},  
                {region_name: '京州', region_id: 10001},  
                {region_name: '永州', region_id: 10001}  
            ]  
        }  
    },  
    props: {  

    },  
    created() {  
        this.searchVal = this.list;  
    },  
    methods:{  
        handleRegion(item) {  
            this.region.name = item.region_name;  
            this.region.id = item.region_id;  
            this.flag = false;  
            this.$emit("getRegion", this.region);  
        },  
        hideSearch() {  
            setTimeout(() => {  
                this.flag = false;  
            },300)  
        },  
        filterRegion() {  

            let ziduan = this.region.name;  
            let rows = [];  
            this.search_loading = true;  
            rows = this.list.filter(item => {  
                if(item.region_name.indexOf(ziduan) != -1){  
                    return item  
                }  
            })  

            if(ziduan.length > 0 ){  
                this.searchVal = rows;  
                this.search_loading = false;  
            }else{  
                this.searchVal = this.list;  
                this.search_loading = false;  
            }  

        }  
    }  
}  
</script>  

<style lang="scss">  
.popup{  
    padding: 8px 0;  
    position: absolute;  
    top: 10vh;  
    z-index: 99;  
    transition: all 1s ease-in;  

    .sanjiao{  
        display: block;  
        width: 8px;  
        height: 8px;  
        background-color: #fff;  
        position: absolute;  
        top: -4px;  
        left: 30px;  
        transform: rotate(45deg);  

    }  
    .not-data{  
        width: 80vw;  
        border-radius: 5px;  
        line-height: 60rpx;  
        text-align: center;  
        background-color: #fff;  
        margin-top: -8px;  
        box-shadow: 0 -2px 12px 0 rgba(0,0,0,.1);  
        color: #666;  
        font-size: 24rpx;  
    }  
    .search{  
        width: 80vw;  
        max-height: 50vh;  
        overflow: hidden;  
        overflow-y: auto;  
        background-color: #fff;  
        border-radius: 5px;  
        margin-top: -8px;  
        box-shadow: 0 -2px 12px 0 rgba(0,0,0,.1);  
        .o-li{  
            display: block;  
            line-height: 60rpx;  
            padding: 0 5px;  
            &:after{  
                content: "";  
                display: block;  
                width: 100%;  
                height: 1px;  
                background-color: #eee;  
                transform: scaleY(0.5);  
            }  
        }  
        .search-loading{  
            position: absolute;  
            top: 50%;  
            left: 50%;  
            transform: translate(-50%, -50%);  
        }  
    }  
}  
</style>  

注意项: 如果要写成组件,在小程序中是不支持props中的值直接修改的,处理办法是 直接在组件内部增加一个对象,来替换props的值;

收起阅读 »

Vue3.0仿layer全局对话框组件|vue3自定义弹层

介绍

v3layer弹层 基于vue3.0开发的仿layer.js桌面端对话框组件。支持7+弹窗动画、10+弹窗类型、30+参数配置,拥有流畅的拖拽、缩放、最大化及全屏等功能。轻松实现各种弹窗效果。

vue3.0 mobile移动端自定义弹层组件v3popup

img

V3Layer支持 Msg、Modal、Dialog、Message、Notification、ActionSheet、Toast、Popover 等多种弹窗类型。

img

引入组件

// 在main.js中全局引入组件  
import { createApp } from 'vue'  
import App from './App.vue'  

const app = createApp(App)  

// 引入饿了么vue3组件库  
import ElementPlus from 'element-plus'  
import 'element-plus/lib/theme-chalk/index.css'  

// 引入弹窗组件  
import Vue3Layer from './components/v3layer'  

app.use(ElementPlus)  
app.use(Vue3Layer)  

app.mount('#app')

支持如下两种调用形式。

标签式

<v3-layer   
    v-model="showDialog"  
    title="标题内容"  
    content="<div style='color:#f57b16;padding:30px;'>这里是内容信息!</div>"  
    z-index="1011"  
    lockScroll="false"  
    xclose  
    resize  
    dragOut  
    :btns="[  
        {text: '取消', click: () => showDialog=false},  
        {text: '确认', style: 'color:#f90;', click: handleSure},  
    ]"  
>  
    <template v-slot:content>这里是自定义插槽内容信息!</template>  
</v3-layer>

函数式

let $el = v3layer({  
    title: '标题内容',  
    content: '<div style='color:#f57b16;padding:30px;'>这里是内容信息!</div>',   
    shadeClose: false,  
    zIndex: 1011,  
    lockScroll: false,  
    xclose: true,  
    resize: true,  
    dragOut: true,  
    btns: [  
        {text: '取消', click: () => { $el.close() }},  
        {text: '确认', click: () => handleSure},  
    ]  
});

img

img

如上图:还支持 message popover nofity 三种弹窗类型。

vue2中可以通过prototype来实现挂载全局方法。
vue3中提供了两种全新的挂载全局方法 app.config.globalPropertiesapp.provide

如果使用第一种方式:

// vue2.x中调用  
methods: {  
    showDialog() {  
        this.$v3layer({...})  
    }  
}  

// vue3.x中调用  
setup() {  
    // 获取上下文  
    const { ctx } = getCurrentInstance()  
    ctx.$v3layer({...})  
}

如果使用第二种方式:

// vue2.x中调用  
methods: {  
    showDialog() {  
        this.v3layer({...})  
    }  
}  

// vue3.x中调用  
setup() {  
    const v3layer = inject('v3layer')  

    const showDialog = () => {  
        v3layer({...})  
    }  

    return {  
        v3layer,  
        showDialog  
    }  
}

img

大家感兴趣可以去官网看看文档说明。
https://v3.cn.vuejs.org/api/application-config.html#globalproperties
https://v3.cn.vuejs.org/guide/component-provide-inject.html

img

img

img

img

img

img

img

编码实现

v3layer支持如下参数自定义配置。

|props参数|  
v-model         是否显示弹框  
id              弹窗唯一标识  
title           标题  
content         内容(支持String、带标签内容、自定义插槽内容)***如果content内容比较复杂,推荐使用标签式写法  
type            弹框类型(toast|footer|actionsheet|actionsheetPicker|android|ios|contextmenu|drawer|iframe)  
layerStyle      自定义弹窗样式  
icon            toast图标(loading | success | fail)  
shade           是否显示遮罩层  
shadeClose      是否点击遮罩时关闭弹窗  
lockScroll      是否弹窗出现时将body滚动锁定  
opacity         遮罩层透明度  
xclose          是否显示关闭图标  
xposition       关闭图标位置(left | right | top | bottom)  
xcolor          关闭图标颜色  
anim            弹窗动画(scaleIn | fadeIn | footer | fadeInUp | fadeInDown | fadeInLeft | fadeInRight)  
position        弹出位置(auto | ['100px','50px'] | t | r | b | l | lt | rt | lb | rb)  
drawer          抽屉弹窗(top | right | bottom | left)  
follow          跟随元素定位弹窗(支持元素.kk #kk 或 [e.clientX, e.clientY])  
time            弹窗自动关闭秒数(1、2、3)  
zIndex          弹窗层叠(默认8080)  
teleport        指定挂载节点(默认是挂载组件标签位置,可通过teleport自定义挂载位置) teleport="body | #xxx | .xxx"  
topmost         置顶当前窗口(默认false)  
area            弹窗宽高(默认auto)设置宽度area: '300px' 设置高度area:['', '200px'] 设置宽高area:['350px', '150px']  
maxWidth        弹窗最大宽度(只有当area:'auto'时,maxWidth的设定才有效)  
maximize        是否显示最大化按钮(默认false)  
fullscreen      全屏弹窗(默认false)  
fixed           弹窗是否固定  
drag            拖拽元素(可定义选择器drag:'.xxx' | 禁止拖拽drag:false)  
dragOut         是否允许拖拽到窗口外(默认false)  
lockAxis        限制拖拽方向可选: v 垂直、h 水平,默认不限制  
resize          是否允许拉伸尺寸(默认false)  
btns            弹窗按钮(参数:text|style|disabled|click)  
++++++++++++++++++++++++++++++++++++++++++++++  
|emit事件触发|  
success         层弹出后回调(@success="xxx")  
end             层销毁后回调(@end="xxx")  
++++++++++++++++++++++++++++++++++++++++++++++  
|event事件|  
onSuccess       层打开回调事件  
onEnd           层关闭回调事件

v3layer组件模板

<template>  
    <div ref="elRef" v-show="opened" class="vui__layer" :class="{'vui__layer-closed': closeCls}" :id="id">  
        <!-- //蒙版 -->  
        <div v-if="JSON.parse(shade)" class="vlayer__overlay" @click="shadeClicked" :style="{opacity}"></div>  
        <div class="vlayer__wrap" :class="[''+anim, type&&'popui__'+type, tipArrow]" :style="[layerStyle]">  
            <div v-if="title" class="vlayer__wrap-tit" v-html="title"></div>  
            <div v-if="type=='toast'&&icon" class="vlayer__toast-icon" :class="['vlayer'+icon]" v-html="toastIcon[icon]"></div>  
            <div class="vlayer__wrap-cntbox">  
                <!-- 判断插槽是否存在 -->  
                <template v-if="$slots.content">  
                    <div class="vlayer__wrap-cnt"><slot name="content" /></div>  
                </template>  
                <template v-else>  
                    <template v-if="content">  
                        <iframe v-if="type=='iframe'" scrolling="auto" allowtransparency="true" frameborder="0" :src="content"></iframe>  
                        <!-- message|notify|popover -->  
                        <div v-else-if="type=='message' || type=='notify' || type=='popover'" class="vlayer__wrap-cnt">  
                            <i v-if="icon" class="vlayer-msg__icon" :class="icon" v-html="messageIcon[icon]"></i>  
                            <div class="vlayer-msg__group"><div v-if="title" class="vlayer-msg__title" v-html="title"></div><div v-html="content"></div></div>  
                        </div>  
                        <div v-else class="vlayer__wrap-cnt" v-html="content"></div>  
                    </template>  
                </template>  
                <slot />  
            </div>  
            <div v-if="btns" class="vlayer__wrap-btns">  
                <span v-for="(btn,index) in btns" :key="index" class="btn" :style="btn.style" @click="btnClicked($event,index)" v-html="btn.text"></span>  
            </div>  
            <span v-if="xclose" class="vlayer__xclose" :class="!maximize&&xposition" :style="{'color': xcolor}" @click="close"></span>  
            <span v-if="maximize" class="vlayer__maximize" @click="maximizeClicked($event)"></span>  
            <span v-if="resize" class="vlayer__resize"></span>  
        </div>  
        <!-- 优化拖拽卡顿 -->  
        <div class="vlayer__dragfix"></div>  
    </div>  
</template>

v3layer逻辑处理

<script>  
    import { onMounted, onUnmounted, ref, reactive, watch, toRefs, nextTick } from 'vue'  
    import domUtils from './utils/dom.js'  
    // 索引,蒙层控制,定时器  
    let $index = 0, $locknum = 0, $timer = {}, $closeTimer = null  
    export default {  
        props: {  
            // ...  
        },  
        emits: [  
            'update:modelValue'  
        ],  
        setup(props, context) {  
            const elRef = ref(null);  

            const data = reactive({  
                opened: false,  
                closeCls: '',  
                toastIcon: {  
                    // ...  
                },  
                messageIcon: {  
                    // ...  
                },  
                vlayerOpts: {},  
                tipArrow: null,  
            })  

            onMounted(() => {  
                if(props.modelValue) {  
                    open();  
                }  
                window.addEventListener('resize', autopos, false);  
            })  

            onUnmounted(() => {  
                window.removeEventListener('resize', autopos, false);  
                clearTimeout($closeTimer);  
            })  

            // 监听弹层v-model  
            watch(() => props.modelValue, (val) => {  
                // console.log('V3Layer is now [%s]', val ? 'show' : 'hide')  
                if(val) {  
                    open();  
                }else {  
                    close();  
                }  
            })  

            // 打开弹窗  
            const open = () => {  
                if(data.opened) return;  
                data.opened = true;  
                typeof props.onSuccess === 'function' && props.onSuccess();  

                const dom = elRef.value;  
                // 弹层挂载位置  
                if(props.teleport) {  
                    nextTick(() => {  
                        let teleportNode = document.querySelector(props.teleport);  
                        teleportNode.appendChild(dom);  

                        auto();  
                    })  
                }  

                callback();  
            }  

            // 关闭弹窗  
            const close = () => {  
                if(!data.opened) return;  

                let dom = elRef.value;  
                let vlayero = dom.querySelector('.vlayer__wrap');  
                let ocnt = dom.querySelector('.vlayer__wrap-cntbox');  
                let omax = dom.querySelector('.vlayer__maximize');  

                data.closeCls = true;  
                clearTimeout($closeTimer);  
                $closeTimer = setTimeout(() => {  
                    data.opened = false;  
                    data.closeCls = false;  
                    if(data.vlayerOpts.lockScroll) {  
                        $locknum--;  
                        if(!$locknum) {  
                            document.body.style.paddingRight = '';  
                            document.body.classList.remove('vui__body-hidden');  
                        }  
                    }  
                    if(props.time) {  
                        $index--;  
                    }  
                    // 清除弹窗样式  
                    vlayero.style.width = vlayero.style.height = vlayero.style.top = vlayero.style.left = '';  
                    ocnt.style.height = '';  
                    omax && omax.classList.contains('maximized') && omax.classList.remove('maximized');  

                    data.vlayerOpts.isBodyOverflow && (document.body.style.overflow = '');  

                    context.emit('update:modelValue', false);  
                    typeof props.onEnd === 'function' && props.onEnd();  
                }, 200)  
            }  

            // 弹窗位置  
            const auto = () => {  
                // ...  

                autopos();  

                // 全屏弹窗  
                if(props.fullscreen) {  
                    full();  
                }  

                // 弹窗拖动|缩放  
                move();  
            }  

            const autopos = () => {  
                if(!data.opened) return;  
                let oL, oT  
                let pos = props.position;  
                let isFixed = JSON.parse(props.fixed);  
                let dom = elRef.value;  
                let vlayero = dom.querySelector('.vlayer__wrap');  

                if(!isFixed || props.follow) {  
                    vlayero.style.position = 'absolute';  
                }  

                let area = [domUtils.client('width'), domUtils.client('height'), vlayero.offsetWidth, vlayero.offsetHeight]  

                oL = (area[0] - area[2]) / 2;  
                oT = (area[1] - area[3]) / 2;  

                if(props.follow) {  
                    offset();  
                }else {  
                    typeof pos === 'object' ? (  
                        oL = parseFloat(pos[0]) || 0, oT = parseFloat(pos[1]) || 0  
                    ) : (  
                        pos == 't' ? oT = 0 :   
                        pos == 'r' ? oL = area[0] - area[2] :   
                        pos == 'b' ? oT = area[1] - area[3] :   
                        pos == 'l' ? oL = 0 :   
                        pos == 'lt' ? (oL = 0, oT = 0) :   
                        pos == 'rt' ? (oL = area[0] - area[2], oT = 0) :   
                        pos == 'lb' ? (oL = 0, oT = area[1] - area[3]) :  
                        pos == 'rb' ? (oL = area[0] - area[2], oT = area[1] - area[3]) :   
                        null  
                    )  

                    vlayero.style.left = parseFloat(isFixed ? oL : domUtils.scroll('left') + oL) + 'px';  
                    vlayero.style.top = parseFloat(isFixed ? oT : domUtils.scroll('top') + oT) + 'px';  
                }  
            }  

            // 元素跟随定位  
            const offset = () => {  
                let oW, oH, pS  
                let dom = elRef.value  
                let vlayero = dom.querySelector('.vlayer__wrap');  

                oW = vlayero.offsetWidth;  
                oH = vlayero.offsetHeight;  
                pS = domUtils.getFollowRect(props.follow, oW, oH);  
                data.tipArrow = pS[2];  

                vlayero.style.left = pS[0] + 'px';  
                vlayero.style.top = pS[1] + 'px';  
            }  

            // 最大化弹窗  
            const full = () => {  
                // ...  
            }  

            // 恢复弹窗  
            const restore = () => {  
                let dom = elRef.value;  
                let vlayero = dom.querySelector('.vlayer__wrap');  
                let otit = dom.querySelector('.vlayer__wrap-tit');  
                let ocnt = dom.querySelector('.vlayer__wrap-cntbox');  
                let obtn = dom.querySelector('.vlayer__wrap-btns');  
                let omax = dom.querySelector('.vlayer__maximize');  

                let t = otit ? otit.offsetHeight : 0  
                let b = obtn ? obtn.offsetHeight : 0  

                if(!data.vlayerOpts.lockScroll) {  
                    data.vlayerOpts.isBodyOverflow = false;  
                    document.body.style.overflow = '';  
                }  

                props.maximize && omax.classList.remove('maximized')  

                vlayero.style.left = parseFloat(data.vlayerOpts.rect[0]) + 'px';  
                vlayero.style.top = parseFloat(data.vlayerOpts.rect[1]) + 'px';  
                vlayero.style.width = parseFloat(data.vlayerOpts.rect[2]) + 'px';  
                vlayero.style.height = parseFloat(data.vlayerOpts.rect[3]) + 'px';  
            }  

            // 拖动|缩放弹窗  
            const move = () => {  
                // ...  
            }  

            // 事件处理  
            const callback = () => {  
                // 倒计时关闭  
                if(props.time) {  
                    $index++  
                    // 防止重复点击  
                    if($timer[$index] !== null) clearTimeout($timer[$index])  
                    $timer[$index] = setTimeout(() => {  
                        close();  
                    }, parseInt(props.time) * 1000)  
                }  
            }  

            // 点击最大化按钮  
            const maximizeClicked = (e) => {  
                let o = e.target  
                if(o.classList.contains('maximized')) {  
                    // 恢复  
                    restore();  
                } else {  
                    // 最大化  
                    full();  
                }  
            }  
            // 点击遮罩层  
            const shadeClicked = () => {  
                if(JSON.parse(props.shadeClose)) {  
                    close();  
                }  
            }  
            // 按钮事件  
            const btnClicked = (e, index) => {  
                let btn = props.btns[index]  
                if(!btn.disabled) {  
                    typeof btn.click === 'function' && btn.click(e)  
                }  
            }  

            return {  
                ...toRefs(data),  
                elRef,  
                close,  
                maximizeClicked,  
                shadeClicked,  
                btnClicked,  
            }  
        }  
    }  
</script>

大家可以在此逻辑基础上自行开发一些新功能。

okay,基于vue3开发自定义pc端弹窗组件就分享这么多,希望以上分享对大家有所帮助哈~~

React+Hooks自定义pc端弹窗组件|react.js自定义对话框

链接:https://juejin.cn/post/6913710656428457991/
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

继续阅读 »

介绍

v3layer弹层 基于vue3.0开发的仿layer.js桌面端对话框组件。支持7+弹窗动画、10+弹窗类型、30+参数配置,拥有流畅的拖拽、缩放、最大化及全屏等功能。轻松实现各种弹窗效果。

vue3.0 mobile移动端自定义弹层组件v3popup

img

V3Layer支持 Msg、Modal、Dialog、Message、Notification、ActionSheet、Toast、Popover 等多种弹窗类型。

img

引入组件

// 在main.js中全局引入组件  
import { createApp } from 'vue'  
import App from './App.vue'  

const app = createApp(App)  

// 引入饿了么vue3组件库  
import ElementPlus from 'element-plus'  
import 'element-plus/lib/theme-chalk/index.css'  

// 引入弹窗组件  
import Vue3Layer from './components/v3layer'  

app.use(ElementPlus)  
app.use(Vue3Layer)  

app.mount('#app')

支持如下两种调用形式。

标签式

<v3-layer   
    v-model="showDialog"  
    title="标题内容"  
    content="<div style='color:#f57b16;padding:30px;'>这里是内容信息!</div>"  
    z-index="1011"  
    lockScroll="false"  
    xclose  
    resize  
    dragOut  
    :btns="[  
        {text: '取消', click: () => showDialog=false},  
        {text: '确认', style: 'color:#f90;', click: handleSure},  
    ]"  
>  
    <template v-slot:content>这里是自定义插槽内容信息!</template>  
</v3-layer>

函数式

let $el = v3layer({  
    title: '标题内容',  
    content: '<div style='color:#f57b16;padding:30px;'>这里是内容信息!</div>',   
    shadeClose: false,  
    zIndex: 1011,  
    lockScroll: false,  
    xclose: true,  
    resize: true,  
    dragOut: true,  
    btns: [  
        {text: '取消', click: () => { $el.close() }},  
        {text: '确认', click: () => handleSure},  
    ]  
});

img

img

如上图:还支持 message popover nofity 三种弹窗类型。

vue2中可以通过prototype来实现挂载全局方法。
vue3中提供了两种全新的挂载全局方法 app.config.globalPropertiesapp.provide

如果使用第一种方式:

// vue2.x中调用  
methods: {  
    showDialog() {  
        this.$v3layer({...})  
    }  
}  

// vue3.x中调用  
setup() {  
    // 获取上下文  
    const { ctx } = getCurrentInstance()  
    ctx.$v3layer({...})  
}

如果使用第二种方式:

// vue2.x中调用  
methods: {  
    showDialog() {  
        this.v3layer({...})  
    }  
}  

// vue3.x中调用  
setup() {  
    const v3layer = inject('v3layer')  

    const showDialog = () => {  
        v3layer({...})  
    }  

    return {  
        v3layer,  
        showDialog  
    }  
}

img

大家感兴趣可以去官网看看文档说明。
https://v3.cn.vuejs.org/api/application-config.html#globalproperties
https://v3.cn.vuejs.org/guide/component-provide-inject.html

img

img

img

img

img

img

img

编码实现

v3layer支持如下参数自定义配置。

|props参数|  
v-model         是否显示弹框  
id              弹窗唯一标识  
title           标题  
content         内容(支持String、带标签内容、自定义插槽内容)***如果content内容比较复杂,推荐使用标签式写法  
type            弹框类型(toast|footer|actionsheet|actionsheetPicker|android|ios|contextmenu|drawer|iframe)  
layerStyle      自定义弹窗样式  
icon            toast图标(loading | success | fail)  
shade           是否显示遮罩层  
shadeClose      是否点击遮罩时关闭弹窗  
lockScroll      是否弹窗出现时将body滚动锁定  
opacity         遮罩层透明度  
xclose          是否显示关闭图标  
xposition       关闭图标位置(left | right | top | bottom)  
xcolor          关闭图标颜色  
anim            弹窗动画(scaleIn | fadeIn | footer | fadeInUp | fadeInDown | fadeInLeft | fadeInRight)  
position        弹出位置(auto | ['100px','50px'] | t | r | b | l | lt | rt | lb | rb)  
drawer          抽屉弹窗(top | right | bottom | left)  
follow          跟随元素定位弹窗(支持元素.kk #kk 或 [e.clientX, e.clientY])  
time            弹窗自动关闭秒数(1、2、3)  
zIndex          弹窗层叠(默认8080)  
teleport        指定挂载节点(默认是挂载组件标签位置,可通过teleport自定义挂载位置) teleport="body | #xxx | .xxx"  
topmost         置顶当前窗口(默认false)  
area            弹窗宽高(默认auto)设置宽度area: '300px' 设置高度area:['', '200px'] 设置宽高area:['350px', '150px']  
maxWidth        弹窗最大宽度(只有当area:'auto'时,maxWidth的设定才有效)  
maximize        是否显示最大化按钮(默认false)  
fullscreen      全屏弹窗(默认false)  
fixed           弹窗是否固定  
drag            拖拽元素(可定义选择器drag:'.xxx' | 禁止拖拽drag:false)  
dragOut         是否允许拖拽到窗口外(默认false)  
lockAxis        限制拖拽方向可选: v 垂直、h 水平,默认不限制  
resize          是否允许拉伸尺寸(默认false)  
btns            弹窗按钮(参数:text|style|disabled|click)  
++++++++++++++++++++++++++++++++++++++++++++++  
|emit事件触发|  
success         层弹出后回调(@success="xxx")  
end             层销毁后回调(@end="xxx")  
++++++++++++++++++++++++++++++++++++++++++++++  
|event事件|  
onSuccess       层打开回调事件  
onEnd           层关闭回调事件

v3layer组件模板

<template>  
    <div ref="elRef" v-show="opened" class="vui__layer" :class="{'vui__layer-closed': closeCls}" :id="id">  
        <!-- //蒙版 -->  
        <div v-if="JSON.parse(shade)" class="vlayer__overlay" @click="shadeClicked" :style="{opacity}"></div>  
        <div class="vlayer__wrap" :class="[''+anim, type&&'popui__'+type, tipArrow]" :style="[layerStyle]">  
            <div v-if="title" class="vlayer__wrap-tit" v-html="title"></div>  
            <div v-if="type=='toast'&&icon" class="vlayer__toast-icon" :class="['vlayer'+icon]" v-html="toastIcon[icon]"></div>  
            <div class="vlayer__wrap-cntbox">  
                <!-- 判断插槽是否存在 -->  
                <template v-if="$slots.content">  
                    <div class="vlayer__wrap-cnt"><slot name="content" /></div>  
                </template>  
                <template v-else>  
                    <template v-if="content">  
                        <iframe v-if="type=='iframe'" scrolling="auto" allowtransparency="true" frameborder="0" :src="content"></iframe>  
                        <!-- message|notify|popover -->  
                        <div v-else-if="type=='message' || type=='notify' || type=='popover'" class="vlayer__wrap-cnt">  
                            <i v-if="icon" class="vlayer-msg__icon" :class="icon" v-html="messageIcon[icon]"></i>  
                            <div class="vlayer-msg__group"><div v-if="title" class="vlayer-msg__title" v-html="title"></div><div v-html="content"></div></div>  
                        </div>  
                        <div v-else class="vlayer__wrap-cnt" v-html="content"></div>  
                    </template>  
                </template>  
                <slot />  
            </div>  
            <div v-if="btns" class="vlayer__wrap-btns">  
                <span v-for="(btn,index) in btns" :key="index" class="btn" :style="btn.style" @click="btnClicked($event,index)" v-html="btn.text"></span>  
            </div>  
            <span v-if="xclose" class="vlayer__xclose" :class="!maximize&&xposition" :style="{'color': xcolor}" @click="close"></span>  
            <span v-if="maximize" class="vlayer__maximize" @click="maximizeClicked($event)"></span>  
            <span v-if="resize" class="vlayer__resize"></span>  
        </div>  
        <!-- 优化拖拽卡顿 -->  
        <div class="vlayer__dragfix"></div>  
    </div>  
</template>

v3layer逻辑处理

<script>  
    import { onMounted, onUnmounted, ref, reactive, watch, toRefs, nextTick } from 'vue'  
    import domUtils from './utils/dom.js'  
    // 索引,蒙层控制,定时器  
    let $index = 0, $locknum = 0, $timer = {}, $closeTimer = null  
    export default {  
        props: {  
            // ...  
        },  
        emits: [  
            'update:modelValue'  
        ],  
        setup(props, context) {  
            const elRef = ref(null);  

            const data = reactive({  
                opened: false,  
                closeCls: '',  
                toastIcon: {  
                    // ...  
                },  
                messageIcon: {  
                    // ...  
                },  
                vlayerOpts: {},  
                tipArrow: null,  
            })  

            onMounted(() => {  
                if(props.modelValue) {  
                    open();  
                }  
                window.addEventListener('resize', autopos, false);  
            })  

            onUnmounted(() => {  
                window.removeEventListener('resize', autopos, false);  
                clearTimeout($closeTimer);  
            })  

            // 监听弹层v-model  
            watch(() => props.modelValue, (val) => {  
                // console.log('V3Layer is now [%s]', val ? 'show' : 'hide')  
                if(val) {  
                    open();  
                }else {  
                    close();  
                }  
            })  

            // 打开弹窗  
            const open = () => {  
                if(data.opened) return;  
                data.opened = true;  
                typeof props.onSuccess === 'function' && props.onSuccess();  

                const dom = elRef.value;  
                // 弹层挂载位置  
                if(props.teleport) {  
                    nextTick(() => {  
                        let teleportNode = document.querySelector(props.teleport);  
                        teleportNode.appendChild(dom);  

                        auto();  
                    })  
                }  

                callback();  
            }  

            // 关闭弹窗  
            const close = () => {  
                if(!data.opened) return;  

                let dom = elRef.value;  
                let vlayero = dom.querySelector('.vlayer__wrap');  
                let ocnt = dom.querySelector('.vlayer__wrap-cntbox');  
                let omax = dom.querySelector('.vlayer__maximize');  

                data.closeCls = true;  
                clearTimeout($closeTimer);  
                $closeTimer = setTimeout(() => {  
                    data.opened = false;  
                    data.closeCls = false;  
                    if(data.vlayerOpts.lockScroll) {  
                        $locknum--;  
                        if(!$locknum) {  
                            document.body.style.paddingRight = '';  
                            document.body.classList.remove('vui__body-hidden');  
                        }  
                    }  
                    if(props.time) {  
                        $index--;  
                    }  
                    // 清除弹窗样式  
                    vlayero.style.width = vlayero.style.height = vlayero.style.top = vlayero.style.left = '';  
                    ocnt.style.height = '';  
                    omax && omax.classList.contains('maximized') && omax.classList.remove('maximized');  

                    data.vlayerOpts.isBodyOverflow && (document.body.style.overflow = '');  

                    context.emit('update:modelValue', false);  
                    typeof props.onEnd === 'function' && props.onEnd();  
                }, 200)  
            }  

            // 弹窗位置  
            const auto = () => {  
                // ...  

                autopos();  

                // 全屏弹窗  
                if(props.fullscreen) {  
                    full();  
                }  

                // 弹窗拖动|缩放  
                move();  
            }  

            const autopos = () => {  
                if(!data.opened) return;  
                let oL, oT  
                let pos = props.position;  
                let isFixed = JSON.parse(props.fixed);  
                let dom = elRef.value;  
                let vlayero = dom.querySelector('.vlayer__wrap');  

                if(!isFixed || props.follow) {  
                    vlayero.style.position = 'absolute';  
                }  

                let area = [domUtils.client('width'), domUtils.client('height'), vlayero.offsetWidth, vlayero.offsetHeight]  

                oL = (area[0] - area[2]) / 2;  
                oT = (area[1] - area[3]) / 2;  

                if(props.follow) {  
                    offset();  
                }else {  
                    typeof pos === 'object' ? (  
                        oL = parseFloat(pos[0]) || 0, oT = parseFloat(pos[1]) || 0  
                    ) : (  
                        pos == 't' ? oT = 0 :   
                        pos == 'r' ? oL = area[0] - area[2] :   
                        pos == 'b' ? oT = area[1] - area[3] :   
                        pos == 'l' ? oL = 0 :   
                        pos == 'lt' ? (oL = 0, oT = 0) :   
                        pos == 'rt' ? (oL = area[0] - area[2], oT = 0) :   
                        pos == 'lb' ? (oL = 0, oT = area[1] - area[3]) :  
                        pos == 'rb' ? (oL = area[0] - area[2], oT = area[1] - area[3]) :   
                        null  
                    )  

                    vlayero.style.left = parseFloat(isFixed ? oL : domUtils.scroll('left') + oL) + 'px';  
                    vlayero.style.top = parseFloat(isFixed ? oT : domUtils.scroll('top') + oT) + 'px';  
                }  
            }  

            // 元素跟随定位  
            const offset = () => {  
                let oW, oH, pS  
                let dom = elRef.value  
                let vlayero = dom.querySelector('.vlayer__wrap');  

                oW = vlayero.offsetWidth;  
                oH = vlayero.offsetHeight;  
                pS = domUtils.getFollowRect(props.follow, oW, oH);  
                data.tipArrow = pS[2];  

                vlayero.style.left = pS[0] + 'px';  
                vlayero.style.top = pS[1] + 'px';  
            }  

            // 最大化弹窗  
            const full = () => {  
                // ...  
            }  

            // 恢复弹窗  
            const restore = () => {  
                let dom = elRef.value;  
                let vlayero = dom.querySelector('.vlayer__wrap');  
                let otit = dom.querySelector('.vlayer__wrap-tit');  
                let ocnt = dom.querySelector('.vlayer__wrap-cntbox');  
                let obtn = dom.querySelector('.vlayer__wrap-btns');  
                let omax = dom.querySelector('.vlayer__maximize');  

                let t = otit ? otit.offsetHeight : 0  
                let b = obtn ? obtn.offsetHeight : 0  

                if(!data.vlayerOpts.lockScroll) {  
                    data.vlayerOpts.isBodyOverflow = false;  
                    document.body.style.overflow = '';  
                }  

                props.maximize && omax.classList.remove('maximized')  

                vlayero.style.left = parseFloat(data.vlayerOpts.rect[0]) + 'px';  
                vlayero.style.top = parseFloat(data.vlayerOpts.rect[1]) + 'px';  
                vlayero.style.width = parseFloat(data.vlayerOpts.rect[2]) + 'px';  
                vlayero.style.height = parseFloat(data.vlayerOpts.rect[3]) + 'px';  
            }  

            // 拖动|缩放弹窗  
            const move = () => {  
                // ...  
            }  

            // 事件处理  
            const callback = () => {  
                // 倒计时关闭  
                if(props.time) {  
                    $index++  
                    // 防止重复点击  
                    if($timer[$index] !== null) clearTimeout($timer[$index])  
                    $timer[$index] = setTimeout(() => {  
                        close();  
                    }, parseInt(props.time) * 1000)  
                }  
            }  

            // 点击最大化按钮  
            const maximizeClicked = (e) => {  
                let o = e.target  
                if(o.classList.contains('maximized')) {  
                    // 恢复  
                    restore();  
                } else {  
                    // 最大化  
                    full();  
                }  
            }  
            // 点击遮罩层  
            const shadeClicked = () => {  
                if(JSON.parse(props.shadeClose)) {  
                    close();  
                }  
            }  
            // 按钮事件  
            const btnClicked = (e, index) => {  
                let btn = props.btns[index]  
                if(!btn.disabled) {  
                    typeof btn.click === 'function' && btn.click(e)  
                }  
            }  

            return {  
                ...toRefs(data),  
                elRef,  
                close,  
                maximizeClicked,  
                shadeClicked,  
                btnClicked,  
            }  
        }  
    }  
</script>

大家可以在此逻辑基础上自行开发一些新功能。

okay,基于vue3开发自定义pc端弹窗组件就分享这么多,希望以上分享对大家有所帮助哈~~

React+Hooks自定义pc端弹窗组件|react.js自定义对话框

链接:https://juejin.cn/post/6913710656428457991/
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

写代码害人?抓!公安机关逮捕涉网络诈骗APP技术开发嫌疑人519名

法律科普

12月31日10时,在公安部统一指挥下,北京、辽宁、湖南、广东等28个省区市公安机关同步开展集中收网行动,依法严厉打击从事涉电信网络诈骗APP技术开发的违法犯罪团伙。 截至当日15时,共捣毁违法犯罪窝点158个,抓获违法犯罪嫌疑人519名。

当前,随着信息技术快速发展,各类APP已成为满足人民群众获取信息、投资理财、贷款转账等各类需求的重要渠道,在给日常生产生活带来便利的同时,也被一些不法分子用于违法犯罪活动。公安机关在工作中发现,一些电信网络诈骗犯罪团伙为提高诈骗成功率,打着正规平台的旗号诱导受害人下载虚假APP,并诱骗受害人在该APP上进行投资理财、申请贷款等,进而骗取受害人钱款。

今年以来,通过虚假APP实施的电信网络诈骗案件高发多发,严重危害人民群众财产安全和社会经济秩序。公安部对此高度重视,深入研判该类案件规律特点,对涉电信网络诈骗APP的开发、封装、应用各个环节开展分析研究,部署开展专案侦办,要求全链条打击相关黑灰产业。

经专案组缜密侦查,公安机关梳理出一批违法犯罪线索,涉及全国24个省区市,并初步掌握了大量犯罪事实和证据。在此基础上,公安部部署对从事涉电信网络诈骗APP技术开发的违法犯罪团伙开展集中抓捕行动,严打团伙,捣毁窝点,斩断链条,切实从源头上遏制电信网络诈骗犯罪的高发态势。

据了解,诈骗团伙利用虚假APP实施的电信网络诈骗案件占该类案件约60%,虚假APP已成为整个犯罪链条中不可或缺的关键环节,并由此催生出一个庞大的技术开发灰色产业链,大量违法犯罪人员参与其中。

相关技术开发人员分工明确、团伙作案,围绕电信网络诈骗犯罪团伙的具体需求“量身定制”APP各种诈骗功能,有的负责编写程序代码,有的负责购买域名和租用服务器,有的负责APP的封装和分发。经各环节层层运作,最终将虚假APP贩卖给诈骗团伙。 随后,诈骗团伙根据其不法目的和APP功能特点,将其包装成极具迷惑性的“正规”应用平台,诱骗受害人点击链接或扫描二维码下载APP,进而实施诈骗。

公安部有关负责人表示,打击涉电信网络诈骗APP技术开发违法犯罪活动,是全链条打击电信网络诈骗黑灰产业的重要举措。公安机关将继续保持对此类犯罪的严打高压态势,坚决斩断为电信诈骗等违法犯罪提供技术、资金等各类非法服务的犯罪链条,切实维护人民群众财产安全和合法权益。 同时,公安机关提醒广大群众增强防范意识,对于未知来源的APP不下载、不点击、不扫码,切实守护好自己的钱袋子。

此文章转载自央视新闻,如有不当联系删除。原文链接

继续阅读 »

12月31日10时,在公安部统一指挥下,北京、辽宁、湖南、广东等28个省区市公安机关同步开展集中收网行动,依法严厉打击从事涉电信网络诈骗APP技术开发的违法犯罪团伙。 截至当日15时,共捣毁违法犯罪窝点158个,抓获违法犯罪嫌疑人519名。

当前,随着信息技术快速发展,各类APP已成为满足人民群众获取信息、投资理财、贷款转账等各类需求的重要渠道,在给日常生产生活带来便利的同时,也被一些不法分子用于违法犯罪活动。公安机关在工作中发现,一些电信网络诈骗犯罪团伙为提高诈骗成功率,打着正规平台的旗号诱导受害人下载虚假APP,并诱骗受害人在该APP上进行投资理财、申请贷款等,进而骗取受害人钱款。

今年以来,通过虚假APP实施的电信网络诈骗案件高发多发,严重危害人民群众财产安全和社会经济秩序。公安部对此高度重视,深入研判该类案件规律特点,对涉电信网络诈骗APP的开发、封装、应用各个环节开展分析研究,部署开展专案侦办,要求全链条打击相关黑灰产业。

经专案组缜密侦查,公安机关梳理出一批违法犯罪线索,涉及全国24个省区市,并初步掌握了大量犯罪事实和证据。在此基础上,公安部部署对从事涉电信网络诈骗APP技术开发的违法犯罪团伙开展集中抓捕行动,严打团伙,捣毁窝点,斩断链条,切实从源头上遏制电信网络诈骗犯罪的高发态势。

据了解,诈骗团伙利用虚假APP实施的电信网络诈骗案件占该类案件约60%,虚假APP已成为整个犯罪链条中不可或缺的关键环节,并由此催生出一个庞大的技术开发灰色产业链,大量违法犯罪人员参与其中。

相关技术开发人员分工明确、团伙作案,围绕电信网络诈骗犯罪团伙的具体需求“量身定制”APP各种诈骗功能,有的负责编写程序代码,有的负责购买域名和租用服务器,有的负责APP的封装和分发。经各环节层层运作,最终将虚假APP贩卖给诈骗团伙。 随后,诈骗团伙根据其不法目的和APP功能特点,将其包装成极具迷惑性的“正规”应用平台,诱骗受害人点击链接或扫描二维码下载APP,进而实施诈骗。

公安部有关负责人表示,打击涉电信网络诈骗APP技术开发违法犯罪活动,是全链条打击电信网络诈骗黑灰产业的重要举措。公安机关将继续保持对此类犯罪的严打高压态势,坚决斩断为电信诈骗等违法犯罪提供技术、资金等各类非法服务的犯罪链条,切实维护人民群众财产安全和合法权益。 同时,公安机关提醒广大群众增强防范意识,对于未知来源的APP不下载、不点击、不扫码,切实守护好自己的钱袋子。

此文章转载自央视新闻,如有不当联系删除。原文链接

收起阅读 »