HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

app端uni.request,requestType无效

登录 api

将 uni.request换成uni.downloadFile
// #ifdef H5
let blob = new Blob([ArrayBuffer])
this.codeSrc = URL.createObjectURL(blob)
// #endif
// #ifdef APP-PLUS
uni.downloadFile({url:'',success:res=>{
this.codeSrc = res.tempFilePath
}})
// #endif

继续阅读 »

将 uni.request换成uni.downloadFile
// #ifdef H5
let blob = new Blob([ArrayBuffer])
this.codeSrc = URL.createObjectURL(blob)
// #endif
// #ifdef APP-PLUS
uni.downloadFile({url:'',success:res=>{
this.codeSrc = res.tempFilePath
}})
// #endif

收起阅读 »

uni-app纯webview项目代码示例,包括返回按钮的处理以及user-agent的更改

Webview

这个方法需要nvue,你可以新建项目时,把默认的index.vue改成index.nvue,然后用下面的代码全部覆盖

<script>  
    export default {  
        onLoad() {  
            const url = 'https://tools.qvdd.cn/request-headers' // 改成你自己的链接  
            const uaAppend = 'your_ua_content' // 改成你自己的ua  
            plus.navigator.setUserAgent(plus.navigator.getUserAgent() + ' ' + uaAppend)  
            const sysInfo = uni.getSystemInfoSync()  
            const styles = { // https://www.html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewStyles  
                width: sysInfo.safeArea.width,  
                top: sysInfo.statusBarHeight,  
                bottom: 0,  
                scalable: true, // 默认不能缩放,在访问PC页面时体验不好  
                errorPage: '/hybrid/html/error.html', // 网页打不开时打开这个页面  
                cachemode: 'cacheElseNetwork' // 默认是 default  
            }  
            const wv = plus.webview.open(url, 'id', styles)  
            plus.key.addEventListener('backbutton', ()=>{  
                wv.canBack((e)=>{  
                    if (e.canBack) {  
                        wv.back()  
                    } else {  
                        uni.showModal({  
                            title: '是否退出App?',  
                            success: (res) => {  
                                if (res.confirm) {  
                                    plus.runtime.quit()  
                                }  
                            }  
                        })  
                    }  
                })  
            })  
        }  
    }  
</script>

其中的/hybrid/html/error.html可以自己放个漂亮的404模板,我是简单这样写的:

<!DOCTYPE html>  
<html>  
    <head>  
        <meta charset="utf-8" />  
        <meta name="viewport" content="width=device-width, initial-scale=1">  
        <title>error</title>  
    </head>  
    <body>  
        <div style="padding: 15px;">  
            网页打开失败  
        </div>  
    </body>  
</html>

pages.json里设置"navigationStyle": "custom",隐藏顶部标题栏
原文出处:https://coding3.com/archives/uniapp-webview.html

继续阅读 »

这个方法需要nvue,你可以新建项目时,把默认的index.vue改成index.nvue,然后用下面的代码全部覆盖

<script>  
    export default {  
        onLoad() {  
            const url = 'https://tools.qvdd.cn/request-headers' // 改成你自己的链接  
            const uaAppend = 'your_ua_content' // 改成你自己的ua  
            plus.navigator.setUserAgent(plus.navigator.getUserAgent() + ' ' + uaAppend)  
            const sysInfo = uni.getSystemInfoSync()  
            const styles = { // https://www.html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewStyles  
                width: sysInfo.safeArea.width,  
                top: sysInfo.statusBarHeight,  
                bottom: 0,  
                scalable: true, // 默认不能缩放,在访问PC页面时体验不好  
                errorPage: '/hybrid/html/error.html', // 网页打不开时打开这个页面  
                cachemode: 'cacheElseNetwork' // 默认是 default  
            }  
            const wv = plus.webview.open(url, 'id', styles)  
            plus.key.addEventListener('backbutton', ()=>{  
                wv.canBack((e)=>{  
                    if (e.canBack) {  
                        wv.back()  
                    } else {  
                        uni.showModal({  
                            title: '是否退出App?',  
                            success: (res) => {  
                                if (res.confirm) {  
                                    plus.runtime.quit()  
                                }  
                            }  
                        })  
                    }  
                })  
            })  
        }  
    }  
</script>

其中的/hybrid/html/error.html可以自己放个漂亮的404模板,我是简单这样写的:

<!DOCTYPE html>  
<html>  
    <head>  
        <meta charset="utf-8" />  
        <meta name="viewport" content="width=device-width, initial-scale=1">  
        <title>error</title>  
    </head>  
    <body>  
        <div style="padding: 15px;">  
            网页打开失败  
        </div>  
    </body>  
</html>

pages.json里设置"navigationStyle": "custom",隐藏顶部标题栏
原文出处:https://coding3.com/archives/uniapp-webview.html

收起阅读 »

uni-app开发微信小商店的一些坑

小程序插件 插件 经验分享

小商店插件版本:1.1.54 基础库版本:2.9.5以上
根据官方文档引入小商店组件,初始化小商店插件,

但是在跳转插件页面时一直报错:has not init plugin

经过一番查找,发现uniapp中的onLaunch方法里的this跟小程序原生组件onLaunch里的this不一致,


在uniapp中需要使用this.$scope来初始化插件

修改后问题解决

继续阅读 »

小商店插件版本:1.1.54 基础库版本:2.9.5以上
根据官方文档引入小商店组件,初始化小商店插件,

但是在跳转插件页面时一直报错:has not init plugin

经过一番查找,发现uniapp中的onLaunch方法里的this跟小程序原生组件onLaunch里的this不一致,


在uniapp中需要使用this.$scope来初始化插件

修改后问题解决

收起阅读 »

任何uniap问题咨询我

Debug

专注前端10年,小问题免费咨询,大问题付费咨询 联系QQ728045048

专注前端10年,小问题免费咨询,大问题付费咨询 联系QQ728045048

交友软件包含聊天支付,会员系统,前后台管理

交友软件包含聊天支付,会员系统,动态,前后台管理一套出售,有需要的联系QQ728045048

交友软件包含聊天支付,会员系统,动态,前后台管理一套出售,有需要的联系QQ728045048

开源知识付费系统—— 万岳知识付费系统开源WEB+UNIAPP版

源码分享 技术分享 uni_app

项目地址

系统演示

目录结构

  • 前端代码 knowledge_uni_app目录
  • 后台代码 knowledge_admin目录

    Web版地址

  • 教师端地址: https://demo.sdwanyue.com/teacher 账号:13866666666 密码:123456
  • 后台地址: https://demo.sdwanyue.com/admin 账号: demo 密码: 123456

    项目介绍

    万岳知识付费系统打造沉浸式学习体验,提升教学质量,还原真实课堂。知识付费功能包含热门精选、在线直播、付费视频、付费音频、付费阅读等营销功能,实现用户快速裂变。提高用户工作效率和收入是成为知识付费的刚需,可以从海量信息中寻找到适合自身的产品,利用碎片化时间和少许资金就能获得自己需要的信息。

    万岳知识付费深刻理解用户诉求,紧盯市场需求。帮助大家低成本高效率体验知识付费平台,以利用互联网让人们生活更美好为使命,精益求精,创新研发,为客户创造更多价值!

  • 所有使用到的框架或者组件都是基于开源项目,代码保证100%开源。
  • 系统功能通用,无论是个人还是企业都可以利用该系统快速搭建一个属于自己的知识付费系统。

    系统前端采用uni-app+socket.io+WebRtc核心技术, 接口采用PhalApi框架配合TP5.1框架ThinkCMF,系统功能如下:

    功能展示

开源版使用须知

  • 允许用于个人学习、教学案例

  • 开源版不适合商用,商用请购买商业版

  • 禁止将本项目的代码和资源进行任何形式的出售,产生的一切任何后果责任由侵权者自负

    商业合作

    • 如果你想使用功能更完善的知识付费系统,请联系QQ客服: 2415408120 获取专业版
    • 如果您想基于知识付费系统进行定制开发,我们提供有偿定制服务支持!
    • 其他合作模式不限,欢迎来撩!
    • 官网地址:http://www.sdwanyue.com

继续阅读 »

项目地址

系统演示

目录结构

  • 前端代码 knowledge_uni_app目录
  • 后台代码 knowledge_admin目录

    Web版地址

  • 教师端地址: https://demo.sdwanyue.com/teacher 账号:13866666666 密码:123456
  • 后台地址: https://demo.sdwanyue.com/admin 账号: demo 密码: 123456

    项目介绍

    万岳知识付费系统打造沉浸式学习体验,提升教学质量,还原真实课堂。知识付费功能包含热门精选、在线直播、付费视频、付费音频、付费阅读等营销功能,实现用户快速裂变。提高用户工作效率和收入是成为知识付费的刚需,可以从海量信息中寻找到适合自身的产品,利用碎片化时间和少许资金就能获得自己需要的信息。

    万岳知识付费深刻理解用户诉求,紧盯市场需求。帮助大家低成本高效率体验知识付费平台,以利用互联网让人们生活更美好为使命,精益求精,创新研发,为客户创造更多价值!

  • 所有使用到的框架或者组件都是基于开源项目,代码保证100%开源。
  • 系统功能通用,无论是个人还是企业都可以利用该系统快速搭建一个属于自己的知识付费系统。

    系统前端采用uni-app+socket.io+WebRtc核心技术, 接口采用PhalApi框架配合TP5.1框架ThinkCMF,系统功能如下:

    功能展示

开源版使用须知

  • 允许用于个人学习、教学案例

  • 开源版不适合商用,商用请购买商业版

  • 禁止将本项目的代码和资源进行任何形式的出售,产生的一切任何后果责任由侵权者自负

    商业合作

    • 如果你想使用功能更完善的知识付费系统,请联系QQ客服: 2415408120 获取专业版
    • 如果您想基于知识付费系统进行定制开发,我们提供有偿定制服务支持!
    • 其他合作模式不限,欢迎来撩!
    • 官网地址:http://www.sdwanyue.com

收起阅读 »

在横竖屏切换后 页面错乱问题的解决办法

横竖屏切换

需求:用户来到首页时候是竖屏的,点开详情页横屏展示详细信息,用户点击返回回到首页
问题:从横屏到竖屏或者竖屏到横屏都有可能出现页面错乱的问题,且还容易闪退!
原因:
因为页面使用rpx为单位的;如果用的是px为单位就不会;因为rpx是根据屏幕尺寸来计算的,回到首页时候,页面获取到的屏幕尺寸还是横屏的尺寸;
测试:
home.vue:

onShow(){  
            uni.getSystemInfo({  
                success: (res) => {  
                    uni.showModal({  
                        title:"sW:"+res.screenWidth+';sh:'+res.screenHeight+';ww:'+res.windowWidth+';wh:'+res.windowHeight  
                    })  
                }  
            })  
}

detail.vue:

methods: {  
            go_home(){  
                uni.showTabBar();  
                // #ifdef APP-PLUS  
                plus.screen.unlockOrientation(); //解除锁定屏幕方向  
                plus.screen.lockOrientation('portrait-primary');   
                // #endif  
                uni.reLaunch({  
                    url:"../index/index"  
                })  
            },

首次进入首页弹窗内容如下:

sw:393;sh:857;ww:393;wh:807

进入详情页 点击首页后弹窗内容如下

sw:873;sh:393;ww:873;wh:343

很明显系统提前获取了错误的页面尺寸;

解决办法就呼之欲出了,我只需要保证在首页时候拿到正常的屏幕尺寸:
detail.vue

methods: {  
            go_home_real(){  
                uni.getSystemInfo({  
                    success: (res) => {  
                        if(res.screenWidth <= res.screenWidth) {  
                            clearInterval(this.dingshiqi)  
                            uni.reLaunch({  
                                url:"../index/index"  
                            })  
                        }  
                    }  
                })  
            },  
            go_home(){  
                uni.showTabBar();  
                // #ifdef APP-PLUS  
                plus.screen.unlockOrientation(); //解除锁定屏幕方向  
                plus.screen.lockOrientation('portrait-primary');   
                // #endif  
                this.dingshiqi = setInterval(this.go_home_real,300);//每隔0.3秒检测一次屏幕是否已经竖过来了 如果是就跳转  

            },  
}

跳转时候做个检测
屏幕尺寸改变时候会触发onResize 也可以在这上面作文章;

data() {  
    return {  
        portraitUrl:'',  
        landScapeUrl:'',  
    }  
},  
onResize() {  
    let _this = this  
    uni.getSystemInfo({    
        success: function(res) {      
            if (res.windowWidth > res.windowHeight) {  
                if(_this.landScapeUrl) {  
                    uni.reLaunch({  
                        url:_this.landScapeUrl  
                    })  
                }  
            } else {    
               if(_this.portraitUrl) {  
                    uni.reLaunch({  
                        url:_this.portraitUrl  
                    })  
               }  
            }    
        }    
    })  
},  
methods: {  
    go_home(){  
        uni.showTabBar();  
        this.portraitUrl = '../index/index';  
        console.log("go_home")  
        // #ifdef APP-PLUS  
        plus.screen.unlockOrientation(); //解除锁定屏幕方向  
        plus.screen.lockOrientation('portrait-primary');   
        // #endif  
        console.log("go_home_end")        
    },  
}
继续阅读 »

需求:用户来到首页时候是竖屏的,点开详情页横屏展示详细信息,用户点击返回回到首页
问题:从横屏到竖屏或者竖屏到横屏都有可能出现页面错乱的问题,且还容易闪退!
原因:
因为页面使用rpx为单位的;如果用的是px为单位就不会;因为rpx是根据屏幕尺寸来计算的,回到首页时候,页面获取到的屏幕尺寸还是横屏的尺寸;
测试:
home.vue:

onShow(){  
            uni.getSystemInfo({  
                success: (res) => {  
                    uni.showModal({  
                        title:"sW:"+res.screenWidth+';sh:'+res.screenHeight+';ww:'+res.windowWidth+';wh:'+res.windowHeight  
                    })  
                }  
            })  
}

detail.vue:

methods: {  
            go_home(){  
                uni.showTabBar();  
                // #ifdef APP-PLUS  
                plus.screen.unlockOrientation(); //解除锁定屏幕方向  
                plus.screen.lockOrientation('portrait-primary');   
                // #endif  
                uni.reLaunch({  
                    url:"../index/index"  
                })  
            },

首次进入首页弹窗内容如下:

sw:393;sh:857;ww:393;wh:807

进入详情页 点击首页后弹窗内容如下

sw:873;sh:393;ww:873;wh:343

很明显系统提前获取了错误的页面尺寸;

解决办法就呼之欲出了,我只需要保证在首页时候拿到正常的屏幕尺寸:
detail.vue

methods: {  
            go_home_real(){  
                uni.getSystemInfo({  
                    success: (res) => {  
                        if(res.screenWidth <= res.screenWidth) {  
                            clearInterval(this.dingshiqi)  
                            uni.reLaunch({  
                                url:"../index/index"  
                            })  
                        }  
                    }  
                })  
            },  
            go_home(){  
                uni.showTabBar();  
                // #ifdef APP-PLUS  
                plus.screen.unlockOrientation(); //解除锁定屏幕方向  
                plus.screen.lockOrientation('portrait-primary');   
                // #endif  
                this.dingshiqi = setInterval(this.go_home_real,300);//每隔0.3秒检测一次屏幕是否已经竖过来了 如果是就跳转  

            },  
}

跳转时候做个检测
屏幕尺寸改变时候会触发onResize 也可以在这上面作文章;

data() {  
    return {  
        portraitUrl:'',  
        landScapeUrl:'',  
    }  
},  
onResize() {  
    let _this = this  
    uni.getSystemInfo({    
        success: function(res) {      
            if (res.windowWidth > res.windowHeight) {  
                if(_this.landScapeUrl) {  
                    uni.reLaunch({  
                        url:_this.landScapeUrl  
                    })  
                }  
            } else {    
               if(_this.portraitUrl) {  
                    uni.reLaunch({  
                        url:_this.portraitUrl  
                    })  
               }  
            }    
        }    
    })  
},  
methods: {  
    go_home(){  
        uni.showTabBar();  
        this.portraitUrl = '../index/index';  
        console.log("go_home")  
        // #ifdef APP-PLUS  
        plus.screen.unlockOrientation(); //解除锁定屏幕方向  
        plus.screen.lockOrientation('portrait-primary');   
        // #endif  
        console.log("go_home_end")        
    },  
}
收起阅读 »

问卷调查小程序前后台都有一套

1000米有需要的联系QQ728045048

1000米有需要的联系QQ728045048

C++防止头文件被重复引入的3种方法!

源码分享

在之前我们详细介绍了 C 语言中如何使用宏定义(#ifndef / #define / #endif)来有效避免头文件被重复 #include,此方式在 C++ 多文件编程中也很常用。

举个例子,如下是一个 C++ 项目,其内部含有 school.h 和 student.h 这 2 个头文件以及 main.cpp 源文件,其各自包含的代码为:

//student.h
class Student {
//......
};
//school.h

include "student.h"

class School {
//......
private:
Student stu[50];
};
//main.cpp

include "student.h"

include "school.h"

int main() {
//......
return 0;
}
运行此项目会发现,编译器报“Student 类型重定义”错误。这是因为在 school.h 文件中已经 #include 了一次 "student.h",而在 main.cpp 主程序又同时 #include 了 "school.h" 和 "student.h",即 Student 类的定义被引入了 2 次,C++不允许同一个类被重复定义。

有小伙伴可能想到,既然 School.h 文件中已经引入了 Student 类,那去掉 main.cpp 主程序引入的 student.h 文件不就可以了吗?这样确实可以避免重复引入 Student 类,但此方式并不适用于所有“重复引入”的场景。

C++ 多文件编程中,处理“多次 #include 导致重复引入”问题的方式有以下 3 种。

————————

1) 使用宏定义避免重复引入
在实际多文件开发中,我们往往使用如下的宏定义来避免发生重复引入:

ifndef _NAME_H

define _NAME_H

//头文件内容

endif

其中,_NAME_H 是宏的名称。需要注意的是,这里设置的宏名必须是独一无二的,不要和项目中其他宏的名称相同。

当程序中第一次 #include 该文件时,由于 _NAME_H 尚未定义,所以会定义 _NAME_H 并执行“头文件内容”部分的代码;当发生多次 #include 时,因为前面已经定义了 _NAME_H,所以不会再重复执行“头文件内容”部分的代码。

也就是说,我们可以将前面项目中的 student.h 文件做如下修改:

ifndef _STUDENT_H

define _STUDENT_H

class Student {
//......
};

endif

虽然该项目 main.cpp 文件中仍 #include 了 2 次 "student.h",但鉴于 _STUDENT_H 宏只能定义一次,所以 Student 类也仅会定义一次。再次执行该项目会发现,其可以正常执行。

2) 使用#pragma once避免重复引入
除了前面第一种最常用的方式之外,还可以使用 #pragma one 指令,将其附加到指定文件的最开头位置,则该文件就只会被 #include 一次。

我们知道,#ifndef 是通过定义独一无二的宏来避免重复引入的,这意味着每次引入头文件都要进行识别,所以效率不高。但考虑到 C 和 C++ 都支持宏定义,所以项目中使用 #ifndef 规避可能出现的“头文件重复引入”问题,不会影响项目的可移植性。

和 ifndef 相比,#pragma once 不涉及宏定义,当编译器遇到它时就会立刻知道当前文件只引入一次,所以效率很高。

但值得一提的是,并不是每个版本的编译器都能识别 #pragma once 指令,一些较老版本的编译器就不支持该指令(执行时会发出警告,但编译会继续进行),即 #pragma once 指令的兼容性不是很好。

目前,几乎所有常见的编译器都支持 #pragma once 指令,甚至于 Visual Studio 2017 新建头文件时就会自带该指令。可以这么说,在 C/C++ 中,#pragma once 是一个非标准但却逐渐被很多编译器支持的指令。

除此之外,#pragma once 只能作用于某个具体的文件,而无法向 #ifndef 那样仅作用于指定的一段代码。

这里仍以前面的 "student.h" 文件为例,将其内容修改为:

pragma once

class Student {
//......
};
再次运行项目,同样可以正常执行。

3) 使用_Pragma操作符
C99 标准中新增加了一个和 #pragma 指令类似的 _Pragma 操作符,其可以看做是 #pragma 的增强版,不仅可以实现 #pragma 所有的功能,更重要的是,_Pragma 还能和宏搭配使用。

有关 _Pragma 操作符更多的功能和用法,本节不做详细讲解,这里仅介绍如何用 _Pragma 操作符避免头文件重复引入。

当处理头文件重复引入问题时,可以将如下语句添加到相应文件的开头:

_Pragma("once")

比如,将该语句添加到前面项目中 student.h 文件中的开头位置,再次执行项目,其可以正常执行。

事实上,无论是 C 语言还是 C++,为防止用户重复引入系统库文件,几乎所有库文件中都采用了以上 3 种结构中的一种,这也是为什么重复引入系统库文件编译器也不会报错的原因。

总结
本节介绍了 3 种避免头文件被重复引入的方法,其中 #pragma once 和 _Pragma("once") 可算作一类,其特点是编译效率高,但可移植性差(编译器不支持,会发出警告,但不会中断程序的执行);而 #ifndef 的特点是可移植性高,编译效率差。读者可根据实际情况,挑选最符合实际需要的解决方案。

除非对项目的编译效率有严格的要求,强烈推荐读者选用第一种解决方案,即采用 #ifndef / #define / #endif 组合解决头文件被重复引入。

另外在某些场景中,考虑到编译效率和可移植性,#pragma once 和 #ifndef 经常被结合使用来避免头文件被重复引入。比如说:

pragma once

ifndef _STUDENT_H

define _STUDENT_H

class Student {
//......
};

endif

当编译器可以识别 #pragma once 时,则整个文件仅被编译一次;反之,即便编译器不识别 #pragma once 指令,此时仍有 #ifndef 在发挥作用。

继续阅读 »

在之前我们详细介绍了 C 语言中如何使用宏定义(#ifndef / #define / #endif)来有效避免头文件被重复 #include,此方式在 C++ 多文件编程中也很常用。

举个例子,如下是一个 C++ 项目,其内部含有 school.h 和 student.h 这 2 个头文件以及 main.cpp 源文件,其各自包含的代码为:

//student.h
class Student {
//......
};
//school.h

include "student.h"

class School {
//......
private:
Student stu[50];
};
//main.cpp

include "student.h"

include "school.h"

int main() {
//......
return 0;
}
运行此项目会发现,编译器报“Student 类型重定义”错误。这是因为在 school.h 文件中已经 #include 了一次 "student.h",而在 main.cpp 主程序又同时 #include 了 "school.h" 和 "student.h",即 Student 类的定义被引入了 2 次,C++不允许同一个类被重复定义。

有小伙伴可能想到,既然 School.h 文件中已经引入了 Student 类,那去掉 main.cpp 主程序引入的 student.h 文件不就可以了吗?这样确实可以避免重复引入 Student 类,但此方式并不适用于所有“重复引入”的场景。

C++ 多文件编程中,处理“多次 #include 导致重复引入”问题的方式有以下 3 种。

————————

1) 使用宏定义避免重复引入
在实际多文件开发中,我们往往使用如下的宏定义来避免发生重复引入:

ifndef _NAME_H

define _NAME_H

//头文件内容

endif

其中,_NAME_H 是宏的名称。需要注意的是,这里设置的宏名必须是独一无二的,不要和项目中其他宏的名称相同。

当程序中第一次 #include 该文件时,由于 _NAME_H 尚未定义,所以会定义 _NAME_H 并执行“头文件内容”部分的代码;当发生多次 #include 时,因为前面已经定义了 _NAME_H,所以不会再重复执行“头文件内容”部分的代码。

也就是说,我们可以将前面项目中的 student.h 文件做如下修改:

ifndef _STUDENT_H

define _STUDENT_H

class Student {
//......
};

endif

虽然该项目 main.cpp 文件中仍 #include 了 2 次 "student.h",但鉴于 _STUDENT_H 宏只能定义一次,所以 Student 类也仅会定义一次。再次执行该项目会发现,其可以正常执行。

2) 使用#pragma once避免重复引入
除了前面第一种最常用的方式之外,还可以使用 #pragma one 指令,将其附加到指定文件的最开头位置,则该文件就只会被 #include 一次。

我们知道,#ifndef 是通过定义独一无二的宏来避免重复引入的,这意味着每次引入头文件都要进行识别,所以效率不高。但考虑到 C 和 C++ 都支持宏定义,所以项目中使用 #ifndef 规避可能出现的“头文件重复引入”问题,不会影响项目的可移植性。

和 ifndef 相比,#pragma once 不涉及宏定义,当编译器遇到它时就会立刻知道当前文件只引入一次,所以效率很高。

但值得一提的是,并不是每个版本的编译器都能识别 #pragma once 指令,一些较老版本的编译器就不支持该指令(执行时会发出警告,但编译会继续进行),即 #pragma once 指令的兼容性不是很好。

目前,几乎所有常见的编译器都支持 #pragma once 指令,甚至于 Visual Studio 2017 新建头文件时就会自带该指令。可以这么说,在 C/C++ 中,#pragma once 是一个非标准但却逐渐被很多编译器支持的指令。

除此之外,#pragma once 只能作用于某个具体的文件,而无法向 #ifndef 那样仅作用于指定的一段代码。

这里仍以前面的 "student.h" 文件为例,将其内容修改为:

pragma once

class Student {
//......
};
再次运行项目,同样可以正常执行。

3) 使用_Pragma操作符
C99 标准中新增加了一个和 #pragma 指令类似的 _Pragma 操作符,其可以看做是 #pragma 的增强版,不仅可以实现 #pragma 所有的功能,更重要的是,_Pragma 还能和宏搭配使用。

有关 _Pragma 操作符更多的功能和用法,本节不做详细讲解,这里仅介绍如何用 _Pragma 操作符避免头文件重复引入。

当处理头文件重复引入问题时,可以将如下语句添加到相应文件的开头:

_Pragma("once")

比如,将该语句添加到前面项目中 student.h 文件中的开头位置,再次执行项目,其可以正常执行。

事实上,无论是 C 语言还是 C++,为防止用户重复引入系统库文件,几乎所有库文件中都采用了以上 3 种结构中的一种,这也是为什么重复引入系统库文件编译器也不会报错的原因。

总结
本节介绍了 3 种避免头文件被重复引入的方法,其中 #pragma once 和 _Pragma("once") 可算作一类,其特点是编译效率高,但可移植性差(编译器不支持,会发出警告,但不会中断程序的执行);而 #ifndef 的特点是可移植性高,编译效率差。读者可根据实际情况,挑选最符合实际需要的解决方案。

除非对项目的编译效率有严格的要求,强烈推荐读者选用第一种解决方案,即采用 #ifndef / #define / #endif 组合解决头文件被重复引入。

另外在某些场景中,考虑到编译效率和可移植性,#pragma once 和 #ifndef 经常被结合使用来避免头文件被重复引入。比如说:

pragma once

ifndef _STUDENT_H

define _STUDENT_H

class Student {
//......
};

endif

当编译器可以识别 #pragma once 时,则整个文件仅被编译一次;反之,即便编译器不识别 #pragma once 指令,此时仍有 #ifndef 在发挥作用。

收起阅读 »

调用安卓系统分享功能,分享非媒体文件到微信

微信

在我的 app 中需要分享 json 文件,看了好多文章大多数都是讲如何分享媒体文件的,比如图片音频什么的,其实调用系统分享功能也是比较简单的,但是分享到微信就会遇到找不到资源的问题,要使用 FileProvider 方式的话就必须要通过原生开发辅助才行,经过无数次的尝试发现微信要获取文件必须要 content:// 开头的 Uri 才行,最后使用了参考文档中的第二种方法 scanFile,目前只是为了达到目的所以代码比较粗糙,欢迎大佬指正

大概思路是这样的:

  1. 把要分享的文件从 app 私有目录复制到公共目录,这里选了 Download 目录
  2. 调用 MediaScannerConnection.scanFile 方法,在 onScanCompleted 回调函数中获取正确的 Uri 标识
  3. 再调用系统分享功能即可
// #ifdef APP-PLUS  
plus.android.importClass('android.content.ContentResolver')  

const MainActivity = plus.android.runtimeMainActivity(),  
    Intent = plus.android.importClass('android.content.Intent'),  
    Uri = plus.android.importClass('android.net.Uri'),  
    InputStreamReader = plus.android.importClass('java.io.InputStreamReader'),  
    OutputStreamWriter = plus.android.importClass('java.io.OutputStreamWriter'),  
    BufferedReader = plus.android.importClass('java.io.BufferedReader'),  
    BufferedWriter = plus.android.importClass('java.io.BufferedWriter'),  
    File = plus.android.importClass('java.io.File'),  
    FileInputStream = plus.android.importClass('java.io.FileInputStream'),  
    FileOutputStream = plus.android.importClass('java.io.FileOutputStream'),  
    MediaScannerConnection = plus.android.importClass('android.media.MediaScannerConnection'),  
    Environment = plus.android.importClass('android.os.Environment'),  

    SETTINGS_FILENAME = 'settings.json'  
// #endif  

function share_file() {  
    // #ifndef APP-PLUS  
    return false  
    // #endif  

    // #ifdef APP-PLUS  
    plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, fs => {  
        fs.root.getFile(SETTINGS_FILENAME, {}, file_entry => {  
            const listener = new plus.android.implements('android.media.MediaScannerConnection$OnScanCompletedListener', {  
                'onScanCompleted': (path, uri) => {  
                    console.log('uri: ' + uri.toString())  

                    const intent = new Intent()  
                        .setAction(Intent.ACTION_SEND)  
                        .setType('text/*')  
                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)  
                        .putExtra(Intent.EXTRA_TEXT, 'Share a file')  
                        .putExtra(Intent.EXTRA_STREAM, uri) // Uri.parse(file_entry.fullPath))  
                        .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)  

                    MainActivity.startActivity(Intent.createChooser(intent, 'Share a file'))  
                    // MainActivity.startActivity(intent)  
                    new File(path).deleteOnExit()  
                }  
            })  

            copy_file_to_public_directory(file_entry.fullPath)  
            MediaScannerConnection.scanFile(MainActivity, [get_public_directory() + '/' + SETTINGS_FILENAME], null, listener)  

            return true  
        }, error => {  
            uni.showToast({  
                title: '文件不存在',  
                icon: 'none',  
                duration: 2000  
            })  
        })  
    })  
    // #endif  
}  

function copy_file_to_public_directory(file_path) {  
    // #ifndef APP-PLUS  
    return false  
    // #endif  

    // #ifdef APP-PLUS  
    const input_stream = new FileInputStream(file_path),  
        output_Stream = new FileOutputStream(get_public_directory() + '/' + SETTINGS_FILENAME),  
        file_reader = new BufferedReader(new InputStreamReader(input_stream)),  
        file_writer = new BufferedWriter(new OutputStreamWriter(output_Stream)),  
        content = file_reader.readLine()  

    file_writer.write(content, 0, content.length)  
    file_writer.flush()  

    file_writer.close()  
    file_reader.close()  
    output_Stream.close()  
    input_stream.close()  
    console.log('copy file success')  

    return true  
    // #endif  
}  

function get_public_directory() {  
    // '/storage/emulated/0/Download/temp'  
    // #ifndef APP-PLUS  
    return false  
    // #endif  

    // #ifdef APP-PLUS  
    return new File(Environment.getExternalStorageDirectory(), 'Download/temp').getAbsolutePath()  
    // #endif  
}

参考文档:

继续阅读 »

在我的 app 中需要分享 json 文件,看了好多文章大多数都是讲如何分享媒体文件的,比如图片音频什么的,其实调用系统分享功能也是比较简单的,但是分享到微信就会遇到找不到资源的问题,要使用 FileProvider 方式的话就必须要通过原生开发辅助才行,经过无数次的尝试发现微信要获取文件必须要 content:// 开头的 Uri 才行,最后使用了参考文档中的第二种方法 scanFile,目前只是为了达到目的所以代码比较粗糙,欢迎大佬指正

大概思路是这样的:

  1. 把要分享的文件从 app 私有目录复制到公共目录,这里选了 Download 目录
  2. 调用 MediaScannerConnection.scanFile 方法,在 onScanCompleted 回调函数中获取正确的 Uri 标识
  3. 再调用系统分享功能即可
// #ifdef APP-PLUS  
plus.android.importClass('android.content.ContentResolver')  

const MainActivity = plus.android.runtimeMainActivity(),  
    Intent = plus.android.importClass('android.content.Intent'),  
    Uri = plus.android.importClass('android.net.Uri'),  
    InputStreamReader = plus.android.importClass('java.io.InputStreamReader'),  
    OutputStreamWriter = plus.android.importClass('java.io.OutputStreamWriter'),  
    BufferedReader = plus.android.importClass('java.io.BufferedReader'),  
    BufferedWriter = plus.android.importClass('java.io.BufferedWriter'),  
    File = plus.android.importClass('java.io.File'),  
    FileInputStream = plus.android.importClass('java.io.FileInputStream'),  
    FileOutputStream = plus.android.importClass('java.io.FileOutputStream'),  
    MediaScannerConnection = plus.android.importClass('android.media.MediaScannerConnection'),  
    Environment = plus.android.importClass('android.os.Environment'),  

    SETTINGS_FILENAME = 'settings.json'  
// #endif  

function share_file() {  
    // #ifndef APP-PLUS  
    return false  
    // #endif  

    // #ifdef APP-PLUS  
    plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, fs => {  
        fs.root.getFile(SETTINGS_FILENAME, {}, file_entry => {  
            const listener = new plus.android.implements('android.media.MediaScannerConnection$OnScanCompletedListener', {  
                'onScanCompleted': (path, uri) => {  
                    console.log('uri: ' + uri.toString())  

                    const intent = new Intent()  
                        .setAction(Intent.ACTION_SEND)  
                        .setType('text/*')  
                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)  
                        .putExtra(Intent.EXTRA_TEXT, 'Share a file')  
                        .putExtra(Intent.EXTRA_STREAM, uri) // Uri.parse(file_entry.fullPath))  
                        .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)  

                    MainActivity.startActivity(Intent.createChooser(intent, 'Share a file'))  
                    // MainActivity.startActivity(intent)  
                    new File(path).deleteOnExit()  
                }  
            })  

            copy_file_to_public_directory(file_entry.fullPath)  
            MediaScannerConnection.scanFile(MainActivity, [get_public_directory() + '/' + SETTINGS_FILENAME], null, listener)  

            return true  
        }, error => {  
            uni.showToast({  
                title: '文件不存在',  
                icon: 'none',  
                duration: 2000  
            })  
        })  
    })  
    // #endif  
}  

function copy_file_to_public_directory(file_path) {  
    // #ifndef APP-PLUS  
    return false  
    // #endif  

    // #ifdef APP-PLUS  
    const input_stream = new FileInputStream(file_path),  
        output_Stream = new FileOutputStream(get_public_directory() + '/' + SETTINGS_FILENAME),  
        file_reader = new BufferedReader(new InputStreamReader(input_stream)),  
        file_writer = new BufferedWriter(new OutputStreamWriter(output_Stream)),  
        content = file_reader.readLine()  

    file_writer.write(content, 0, content.length)  
    file_writer.flush()  

    file_writer.close()  
    file_reader.close()  
    output_Stream.close()  
    input_stream.close()  
    console.log('copy file success')  

    return true  
    // #endif  
}  

function get_public_directory() {  
    // '/storage/emulated/0/Download/temp'  
    // #ifndef APP-PLUS  
    return false  
    // #endif  

    // #ifdef APP-PLUS  
    return new File(Environment.getExternalStorageDirectory(), 'Download/temp').getAbsolutePath()  
    // #endif  
}

参考文档:

收起阅读 »

在线客服系统源码h5|thinkphp在线客服完整源码|网页在线客服源码

源码

  法国阿纳托尔曾经说过:企业客服人员早上醒来后通常做的第一件事就是检查手机,看看是否有顾客发来的重要信息,这种行为已经成为我们日常生活方式的一部分。
  
  源码及演示:e.cusscode.top/s

  不管原因是什么,我们都有一套日常使用的通信工具电子邮件、电话、网络会议工具或社交网络。对于一个高效运行的企业而言,拥有一套好的源码搭建的在线客服系统,对于提供企业运行效率至关重要!随着流感大流行使在家工作成为一种新的常态,我们面临着前所未有的沟通方式的变化,这使得这些工具不仅是必不可少的,而且现在是必需的。

  搭建在线客服系统软件的必要性:

  作为全球分布式团队的一部分进行远程工作时,我们必须有一个协作环境。线客服系统在帮助我们保持联系方面起着至关重要的作用。与电子邮件不同,线客服系统提供与全球同事的快速、实时通信。

  选择线客服系统源码有很多因素。为了帮助您选择合适的应用程序,在本文中,我将探讨四个开源聊线客服系统工具(当您需要与同事“面对面”时),然后概述一些您应该在有效的通信应用程序中寻找的功能。

  1、RockChat

  RockChat是一个综合性的交流平台,它将频道分为公共(对任何加入的人开放)或私人(仅限邀请)房间。您还可以直接向登录的用户发送消息;共享文档、链接、照片、视频和gif;进行视频通话;以及在不离开平台的情况下发送音频消息。

  源码开源的,但它的独特之处在于它的自助客服系统。您可以将其下载到您的服务器上,无论它是本地服务器还是公共云上的虚拟专用服务器。多开源项目使用Rocket.Chat作为他们的官方交流平台。它在不断地发展,有新的特性和改进。

  RockChat最让我喜欢的是它能够根据用户需求进行定制,并且它使用机器学习在用户之间进行自动、实时的消息转换。你也可以在移动设备上下载和聊天。

  2、IRC

  IRC是一种实时的、基于文本的通信形式。虽然它是最古老的电子通信形式之一,但在许多著名的软件项目中仍然很流行。

  IRC频道是独立的在线客服系统。它允许你与多人在一个开放的渠道交谈,或与某人私下一对一的交谈。如果频道名以#开头,则可以假定它是正式的,而以#开头的聊天室是非官方的,通常是随意的。

  IRC入门很简单。您的IRC句柄或昵称允许人们找到您,因此它必须是唯一的。但你选择IRC客户完全是你的决定。如果您想要一个比标准IRC客户端功能更丰富的应用程序。

  考虑到它的年龄,你为什么还要在IRC上?原因之一是,它仍然是我们所依赖的许多免费和开源项目的所在地。如果您想参与开源软件和社区,可以选择IRC。

  3、Zulip

  Zulip是一个流行的在线客服系统源码,遵循基于主题的线程模型。在Zulip,你订阅流,就像在IRC频道或Rocket.Chat中一样。但是每个Zulip流都会打开一个独特的主题,这有助于您以后跟踪对话,从而使其更有条理。

  与其他平台一样,它支持emojis、内嵌图像、视频和tweet预览。它还支持LaTeX共享数学公式或公式,并支持标记和语法高亮显示以共享代码。

  Zulip是跨平台的,它提供用于构建您自己的集成的api。我特别喜欢Zulip与GitHub的集成特性:如果我正在处理一个问题,我可以使用Zulip的标记链接回pull请求ID。

  Zulip是开源的(您可以访问源代码在GitHub上),免费使用,但它为内部支持付费,LDAP集成和更多存储。

  4、Let's Chat

  Let's Chat是一个小型团队的自托管的在线客服系统源码解决方案。它运行在Node.js和MongoDB上,只需点击几下,就可以部署到本地服务器或托管服务上。它是免费的,开源的源代码在GitHub上提供。

  Let'sChat与其他开源聊天工具的区别在于它的企业特性:它支持LDAP和Kerberos身份验证。它还具有新用户想要的所有功能:您可以在存档中搜索消息历史记录,并用username之类的名字标记人物。

  我喜欢Let'sChat的地方是它有私人和密码保护的房间、图像嵌入、GIPHY支持和代码粘贴。它在不断地发展,并在bucket中添加更多的特性。

  选择在线客服系统源码的要点:

  各种各样的在线客服系统源码让你很难选择一个。以下是一些选择在线客服系统源码的一般准则。

  具有交互式界面和简单导航的工具是理想的。

  最好是寻找一个功能强大并允许人们以各种方式使用它的工具。

  与您使用的工具的集成可以在您的决策中发挥重要作用。有些工具与GitHub、GitLab和某些应用程序有很好的无缝集成,这是一个很有用的特性。

  使用能够在基于云的服务上托管的工具非常方便。

  聊天服务的安全性应该考虑在内。许多组织和个人都需要在专用服务器上托管服务。

  提供丰富的私人和私人聊天室设置。

  由于人们比以往任何时候都更依赖在线服务,所以有一个备用通信平台是明智之举,由于这些服务在不断更新,您可能会发现自己连接到多个通道,因此集成变得非常有价值。

继续阅读 »

  法国阿纳托尔曾经说过:企业客服人员早上醒来后通常做的第一件事就是检查手机,看看是否有顾客发来的重要信息,这种行为已经成为我们日常生活方式的一部分。
  
  源码及演示:e.cusscode.top/s

  不管原因是什么,我们都有一套日常使用的通信工具电子邮件、电话、网络会议工具或社交网络。对于一个高效运行的企业而言,拥有一套好的源码搭建的在线客服系统,对于提供企业运行效率至关重要!随着流感大流行使在家工作成为一种新的常态,我们面临着前所未有的沟通方式的变化,这使得这些工具不仅是必不可少的,而且现在是必需的。

  搭建在线客服系统软件的必要性:

  作为全球分布式团队的一部分进行远程工作时,我们必须有一个协作环境。线客服系统在帮助我们保持联系方面起着至关重要的作用。与电子邮件不同,线客服系统提供与全球同事的快速、实时通信。

  选择线客服系统源码有很多因素。为了帮助您选择合适的应用程序,在本文中,我将探讨四个开源聊线客服系统工具(当您需要与同事“面对面”时),然后概述一些您应该在有效的通信应用程序中寻找的功能。

  1、RockChat

  RockChat是一个综合性的交流平台,它将频道分为公共(对任何加入的人开放)或私人(仅限邀请)房间。您还可以直接向登录的用户发送消息;共享文档、链接、照片、视频和gif;进行视频通话;以及在不离开平台的情况下发送音频消息。

  源码开源的,但它的独特之处在于它的自助客服系统。您可以将其下载到您的服务器上,无论它是本地服务器还是公共云上的虚拟专用服务器。多开源项目使用Rocket.Chat作为他们的官方交流平台。它在不断地发展,有新的特性和改进。

  RockChat最让我喜欢的是它能够根据用户需求进行定制,并且它使用机器学习在用户之间进行自动、实时的消息转换。你也可以在移动设备上下载和聊天。

  2、IRC

  IRC是一种实时的、基于文本的通信形式。虽然它是最古老的电子通信形式之一,但在许多著名的软件项目中仍然很流行。

  IRC频道是独立的在线客服系统。它允许你与多人在一个开放的渠道交谈,或与某人私下一对一的交谈。如果频道名以#开头,则可以假定它是正式的,而以#开头的聊天室是非官方的,通常是随意的。

  IRC入门很简单。您的IRC句柄或昵称允许人们找到您,因此它必须是唯一的。但你选择IRC客户完全是你的决定。如果您想要一个比标准IRC客户端功能更丰富的应用程序。

  考虑到它的年龄,你为什么还要在IRC上?原因之一是,它仍然是我们所依赖的许多免费和开源项目的所在地。如果您想参与开源软件和社区,可以选择IRC。

  3、Zulip

  Zulip是一个流行的在线客服系统源码,遵循基于主题的线程模型。在Zulip,你订阅流,就像在IRC频道或Rocket.Chat中一样。但是每个Zulip流都会打开一个独特的主题,这有助于您以后跟踪对话,从而使其更有条理。

  与其他平台一样,它支持emojis、内嵌图像、视频和tweet预览。它还支持LaTeX共享数学公式或公式,并支持标记和语法高亮显示以共享代码。

  Zulip是跨平台的,它提供用于构建您自己的集成的api。我特别喜欢Zulip与GitHub的集成特性:如果我正在处理一个问题,我可以使用Zulip的标记链接回pull请求ID。

  Zulip是开源的(您可以访问源代码在GitHub上),免费使用,但它为内部支持付费,LDAP集成和更多存储。

  4、Let's Chat

  Let's Chat是一个小型团队的自托管的在线客服系统源码解决方案。它运行在Node.js和MongoDB上,只需点击几下,就可以部署到本地服务器或托管服务上。它是免费的,开源的源代码在GitHub上提供。

  Let'sChat与其他开源聊天工具的区别在于它的企业特性:它支持LDAP和Kerberos身份验证。它还具有新用户想要的所有功能:您可以在存档中搜索消息历史记录,并用username之类的名字标记人物。

  我喜欢Let'sChat的地方是它有私人和密码保护的房间、图像嵌入、GIPHY支持和代码粘贴。它在不断地发展,并在bucket中添加更多的特性。

  选择在线客服系统源码的要点:

  各种各样的在线客服系统源码让你很难选择一个。以下是一些选择在线客服系统源码的一般准则。

  具有交互式界面和简单导航的工具是理想的。

  最好是寻找一个功能强大并允许人们以各种方式使用它的工具。

  与您使用的工具的集成可以在您的决策中发挥重要作用。有些工具与GitHub、GitLab和某些应用程序有很好的无缝集成,这是一个很有用的特性。

  使用能够在基于云的服务上托管的工具非常方便。

  聊天服务的安全性应该考虑在内。许多组织和个人都需要在专用服务器上托管服务。

  提供丰富的私人和私人聊天室设置。

  由于人们比以往任何时候都更依赖在线服务,所以有一个备用通信平台是明智之举,由于这些服务在不断更新,您可能会发现自己连接到多个通道,因此集成变得非常有价值。

收起阅读 »

uni-app 内置国际化方案说明

uni_app

uni-app 从 3.1.5 版本开始框架内置组件开始完善国际化支持

>+ App平台、H5平台 优化 uni.showModal、uni.showActionSheet 等 API 内置国际化支持
>+ App平台 优化 应用退出提示内置国际化支持
>+ App平台 优化 uni.scanCode、uni.previewImage 等 API 内置国际化支持
>+ H5平台 优化 picker、video 组件内置国际化支持

这些组件内置如下语言

  • 中文简体 zh-Hans
  • 中文繁体 zh-Hant
  • 英语 en
  • 法语 fr
  • 西班牙语 es

组件和接口显示会根据系统语言环境自动切换,未支持的系统语言环境会显示为英文。

uni-app 3.2.5 版本以下当使用 vue-i18n 时,会使用 vue-i18n 设置的语言。

uni-app 3.2.5 版本开始请在 manifest.json 内配置,或者调用 uni.setLocale 进行设置,具体参考 locale.md

继续阅读 »

uni-app 从 3.1.5 版本开始框架内置组件开始完善国际化支持

>+ App平台、H5平台 优化 uni.showModal、uni.showActionSheet 等 API 内置国际化支持
>+ App平台 优化 应用退出提示内置国际化支持
>+ App平台 优化 uni.scanCode、uni.previewImage 等 API 内置国际化支持
>+ H5平台 优化 picker、video 组件内置国际化支持

这些组件内置如下语言

  • 中文简体 zh-Hans
  • 中文繁体 zh-Hant
  • 英语 en
  • 法语 fr
  • 西班牙语 es

组件和接口显示会根据系统语言环境自动切换,未支持的系统语言环境会显示为英文。

uni-app 3.2.5 版本以下当使用 vue-i18n 时,会使用 vue-i18n 设置的语言。

uni-app 3.2.5 版本开始请在 manifest.json 内配置,或者调用 uni.setLocale 进行设置,具体参考 locale.md

收起阅读 »