HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

Websocket直播间聊天室教程 - GoEasy快速实现聊天室

直播 WEBSOCKET

最近两年直播那个火啊,真的是无法形容!经常有朋友问起,我想实现一个直播间聊天或者我想开发一个聊天室, 要如何开始呢?
image

今天小编就手把手的教你用GoEasy做一个聊天室,当然也可以用于直播间内的互动。全套源码已经开源。

uniapp websocket聊天室体验demo:https://ext.dcloud.net.cn/plugin?id=5212

本教程主要目的是为大家介绍实现思路,为了确保本教程能帮助到使用不同前端技术的朋友,采用了HTML + JQuery的方式,后续还会推出Uniapp(vue/nvue)和小程序版本,大家可以持续关注。

我们这次要实现的聊天室,有两个界面,分别是:

  • 登录界面
  • 聊天室界面

    登录

    image

对于登录界面,我们期望:

  • 用户可以输入自己的昵称
  • 用户可以选择自己喜欢的头像
  • 用户可以选择进入不同的聊天室(直播间)

实现步骤

登录界面的实现,不用多说,因为真的是So Easy! 一个简单的界面,只包含三个简单的逻辑:

  • 验证是否输入昵称
  • 验证是否选择一个头像
  • 根据选择进入相应的聊天室

下边重点讲一下聊天室的实现。

聊天室(直播间)

image

当我们进入一个聊天室后,我们期望:

  • 用户能看到当前有多少用户在线,这个数字能够实时的更新
  • 用户能看到当前在线用户们的头像,而且能够实时的更新
  • 如果有用户进入或离开聊天室
    a. 聊天室会有“XXX进来了"或"XXX离开了"的提示
    b. 在线用户的数字和用户的头像列表会随之自动更新
  • 用户可以在聊天里发言
  • 用户可以发送道具:火箭或者比心

实现步骤

第一步:聊天室界面显示

1. 初始化:

当用户选择了一个聊天室,显示聊天室界面之前,我们首先要进行以下初始化工作:

  • 初始化当前用户currentUser,用户id,昵称,头像
  • 初始化当前聊天室ID: currentRoomId
  • 初始化GoEasy对象,注意一定要加上userId参数(可以是该用户的uuid或id等唯一标识,只有设置了userId的客户端在上下线时,才会触发上下线提醒)。同时需要将头像和昵称放入userData,当我们收到一个用户上线提醒的时候,我们需要知道这个用户的头像和昵称。
  • 初始化onlineUsers,onlineUsers是用来存放当前聊天室在线用户数和在线用户列表。 将当前聊天室Id (currentRoomId)作为channel,执行goEasy.hereNow查询此刻聊天室在线用户数和用户列表,赋值给onlineUsers。除了在进入聊天室的时候初始化onlineUsers,当有用户进入或离开时,也会动态的更新onlineUsers。
  • 以当前聊天室的id(currentRoomId)作为channel,执行subscriber方法监听和接收聊天室新消息。
  • 以当前聊天室的id(currentRoomId)作为channel,执行subscriberPresence监听用户进入和离开事件。

参考代码:service.js

//初始化聊天室  
this.joinRoom = function(userId,nickName, avatar, roomID) {  
        //初始化当前用户  
        this.currentUser = new User(userId, nickName, avatar);  
        //初始化当前聊天室id  
        this.currentRoomId = roomID;  
        //初始化goeasy,建立长连接  
        this.goeasy = new GoEasy({  
            host: "hangzhou.goeasy.io",  
            appkey: "您的appkey",  
            userId: this.currentUser.id,  
            userData: '{"nickname":"' + this.currentUser.nickname + '","avatar":"' + this.currentUser.avatar + '"}',  
            onConnected: function () {  
                console.log( "GoEasy connect successfully.")  
            },  
            onDisconnected: function () {  
                console.log("GoEasy disconnected.")  
            }  
        });  
        //查询当前在线用户列表,初始化onlineUsers对象  
        this.initialOnlineUsers();  
        //监听用户上下线提醒,实时更新onlineUsers对象  
        this.subscriberPresence();  
        //监听和接收新消息  
        this.subscriberNewMessage();  
};

2. 页面展示:

完成初始化之后,就跳转到直播间界面,在页面上显示以下数据:

  • 当前聊天室的名称
  • 聊天记录,并且显示聊天室界面
  • 展示聊天室界面

参考代码:controller.js

//页面切换到聊天室界面  
function showChatRoom() {  
    //更新房间名  
    $("#chatRoom-header").find(".current-chatRoom-name").text(loginCommand.roomName);  

    //加载聊天历史  
    var chatHistory = service.loadChatHistory();  
    chatHistory.forEach(function (item) {  
        //展示发送的消息  
        var otherPerson = createCurrentChatRoomPerson(item.senderNickname + ":", item.content)  
        $(".chatRoom-content-box").append($(otherPerson));  
    });  

    //隐藏登录界面  
    $(".chat-login-box").hide();  
    // //显示聊天界面  
    $(".chatRoom-box").show();  
    // //滑动到最后一行  
    scrollBottom();  
}

至此,我们已经完成了goeasy长连接的初始化,和一个聊天室静态展示。接下来,我们一起来看看如何让这个聊天室能够动起来。

第二步:聊天室互动

1. 实时更新在线用户数和头像列表

之前在service.initialOnlineUsers方法已经初始化onlineUsers对象,但聊天室随时都有用户进进出出,所以我们接下来还需要能够在有用户上线或下线的时候能够实时的更新onlineUsers,并且实时显示在页面上。
当我们收到一个用户上线提醒,我们将新上线的用户的信息存入在线用户对象onlineUsers里,当有用户离开时,在本地在线用户列表里删除。

参考代码:service.js

//监听用户上下线时间,维护onlineUsers对象  
this.subscriberPresence = function() {  
    var self = this;  
    this.goeasy.subscribePresence({  
        channel: this.currentRoomId,  
        onPresence: function(presenceEvents) {  
            presenceEvents.events.forEach(function(event) {  
                var userId = event.userId;  
                var count = presenceEvents.clientAmount;  
                //更新onlineUsers在线用户数  
                self.onlineUsers.count = count;  
                //如果有用户进入聊天室  
                if (event.action == "join" || event.action == "online") {  
                    var userData = JSON.parse(event.userData);  
                    var nickName = userData.nickname;  
                    var avatar = userData.avatar;  
                    var user = new User(userId, nickName, avatar);  
                    //将新用户加入onlineUsers列表  
                    self.onlineUsers.users.push(user);  
                    //触发界面的更新  
                    self.onJoinRoom(user.nickname, user.avatar);  
                } else {  
                    for (var i = 0; i < self.onlineUsers.users.length; i++) {  
                        var leavingUser = self.onlineUsers.users[i];  
                        if (leavingUser.id == userId) {  
                            var nickName = leavingUser.nickname;  
                            var avatar = leavingUser.avatar;  
                            //将离开的用户从onlineUsers中删掉  
                            self.onlineUsers.users.splice(i, 1);  
                            //触发界面的更新  
                            self.onLeaveRoom(nickName, avatar);  
                        }  
                    }  
                }  
            });  
        },  
        onSuccess : function () {  
            console.log("监听成功")  
        },  
        onFailed : function () {  
            console.log("监听失败")  
        }  
    });  
};

2. 发送消息

  • 初始化一个chatMessage对象,包含发送方id,昵称,消息内容,消息类型为chat
  • 将chatMessage转换为一个Json格式的字符串
  • 调用GoEasy的Publish方法,完成消息的发送

参考代码(service.js)

this.sendMessage = function(content) {  
    var message = new ChatMessage(this.currentUser.id,this.currentUser.nickname, MessageType.CHAT, content);  
    var self = this;  
    this.goeasy.publish({  
        channel: self.currentRoomId,  
        message: JSON.stringify(message),  
        onSuccess: function() {  
            console.log("消息发布成功。");  
        },  
        onFailed: function(error) {  
            console.log("消息发送失败,错误编码:" + error.code + " 错误信息:" + error.content);  
        }  
    });  
};

3. 接收和显示新消息/道具

之前我们已经在初始化页面的时候执行了service.subscriberNewMessage(),当我们收到一条消息时:

  • 根据消息类型判断是一条聊天消息,还是一个道具
  • 如果收到的是一条聊天消息,直接显示到界面
  • 如果是道具,就播放动画

参考代码(service.js)

//监听消息或道具  
this.subscriberNewMessage = function() {  
    var self = this;  
    this.goeasy.subscribe({  
        channel: this.currentRoomId, //替换为您自己的channel  
        onMessage: function(message) {  
            var chatMessage = JSON.parse(message.content);  
            //todo:事实上不推荐在前端收到时保存, 一个用户开多个窗口,会导致重复保存, 建议所有消息都是都在发送时在服务器端保存,这里只是为了演示  
            self.restapi.saveChatMessage(self.currentRoomId, chatMessage);  
            //如果收到的是一个消息,就显示为消息  
            if (chatMessage.type == MessageType.CHAT) {  
                var selfSent = chatMessage.senderUserId == self.currentUser.id;  
                var content = JSON.parse(message.content);  
                self.onNewMessage(chatMessage.senderNickname, content, selfSent);  
            }  
            //如果收到的是一个道具,就播放道具动画  
            if (chatMessage.type == MessageType.PROP) {  
                if (chatMessage.content == Prop.ROCKET) {  
                    self.onNewRocket(chatMessage.senderNickname);  
                }  
                if (chatMessage.content == Prop.HEART) {  
                    self.onNewHeart(chatMessage.senderNickname);  
                }  
            }  
        }  
    });  
};

4. 发送和接收并展示道具

其实和发送消息的实现几乎是一样的,具体代码请参考service.js的sendProp方法,controller.js的onNewHeart()方法。动画的播放,使用了TweenMax这个库,主要是为了展示一个实现思路,小编也不知道这个库是否有很好的兼容性,以及是否能够用在Uniapp和小程序下,知道的朋友可以留言分享给大家。

this.sendProp = function(prop) {  
    var self = this;  
    var message = new ChatMessage(this.currentUser.id,this.currentUser.nickname, MessageType.PROP, prop);  
    this.goeasy.publish({  
        channel: self.currentRoomId,  
        message: JSON.stringify(message),  
        onSuccess: function() {  
            console.log("道具发布成功。");  
        },  
        onFailed: function(error) {  
            console.log("道具发送失败,错误编码:" + error.code + " 错误信息:" + error.content);  
        }  
    });  
};

至此,一个聊天室就搞定了,是不是很简单?

如果阅读本文或开发中有任何问题,也欢迎在GoEasy官网(https://www.goeasy.io)添加GoEasy为好友,来获得更多技术支持。

GoEasy系列教程:

继续阅读 »

最近两年直播那个火啊,真的是无法形容!经常有朋友问起,我想实现一个直播间聊天或者我想开发一个聊天室, 要如何开始呢?
image

今天小编就手把手的教你用GoEasy做一个聊天室,当然也可以用于直播间内的互动。全套源码已经开源。

uniapp websocket聊天室体验demo:https://ext.dcloud.net.cn/plugin?id=5212

本教程主要目的是为大家介绍实现思路,为了确保本教程能帮助到使用不同前端技术的朋友,采用了HTML + JQuery的方式,后续还会推出Uniapp(vue/nvue)和小程序版本,大家可以持续关注。

我们这次要实现的聊天室,有两个界面,分别是:

  • 登录界面
  • 聊天室界面

    登录

    image

对于登录界面,我们期望:

  • 用户可以输入自己的昵称
  • 用户可以选择自己喜欢的头像
  • 用户可以选择进入不同的聊天室(直播间)

实现步骤

登录界面的实现,不用多说,因为真的是So Easy! 一个简单的界面,只包含三个简单的逻辑:

  • 验证是否输入昵称
  • 验证是否选择一个头像
  • 根据选择进入相应的聊天室

下边重点讲一下聊天室的实现。

聊天室(直播间)

image

当我们进入一个聊天室后,我们期望:

  • 用户能看到当前有多少用户在线,这个数字能够实时的更新
  • 用户能看到当前在线用户们的头像,而且能够实时的更新
  • 如果有用户进入或离开聊天室
    a. 聊天室会有“XXX进来了"或"XXX离开了"的提示
    b. 在线用户的数字和用户的头像列表会随之自动更新
  • 用户可以在聊天里发言
  • 用户可以发送道具:火箭或者比心

实现步骤

第一步:聊天室界面显示

1. 初始化:

当用户选择了一个聊天室,显示聊天室界面之前,我们首先要进行以下初始化工作:

  • 初始化当前用户currentUser,用户id,昵称,头像
  • 初始化当前聊天室ID: currentRoomId
  • 初始化GoEasy对象,注意一定要加上userId参数(可以是该用户的uuid或id等唯一标识,只有设置了userId的客户端在上下线时,才会触发上下线提醒)。同时需要将头像和昵称放入userData,当我们收到一个用户上线提醒的时候,我们需要知道这个用户的头像和昵称。
  • 初始化onlineUsers,onlineUsers是用来存放当前聊天室在线用户数和在线用户列表。 将当前聊天室Id (currentRoomId)作为channel,执行goEasy.hereNow查询此刻聊天室在线用户数和用户列表,赋值给onlineUsers。除了在进入聊天室的时候初始化onlineUsers,当有用户进入或离开时,也会动态的更新onlineUsers。
  • 以当前聊天室的id(currentRoomId)作为channel,执行subscriber方法监听和接收聊天室新消息。
  • 以当前聊天室的id(currentRoomId)作为channel,执行subscriberPresence监听用户进入和离开事件。

参考代码:service.js

//初始化聊天室  
this.joinRoom = function(userId,nickName, avatar, roomID) {  
        //初始化当前用户  
        this.currentUser = new User(userId, nickName, avatar);  
        //初始化当前聊天室id  
        this.currentRoomId = roomID;  
        //初始化goeasy,建立长连接  
        this.goeasy = new GoEasy({  
            host: "hangzhou.goeasy.io",  
            appkey: "您的appkey",  
            userId: this.currentUser.id,  
            userData: '{"nickname":"' + this.currentUser.nickname + '","avatar":"' + this.currentUser.avatar + '"}',  
            onConnected: function () {  
                console.log( "GoEasy connect successfully.")  
            },  
            onDisconnected: function () {  
                console.log("GoEasy disconnected.")  
            }  
        });  
        //查询当前在线用户列表,初始化onlineUsers对象  
        this.initialOnlineUsers();  
        //监听用户上下线提醒,实时更新onlineUsers对象  
        this.subscriberPresence();  
        //监听和接收新消息  
        this.subscriberNewMessage();  
};

2. 页面展示:

完成初始化之后,就跳转到直播间界面,在页面上显示以下数据:

  • 当前聊天室的名称
  • 聊天记录,并且显示聊天室界面
  • 展示聊天室界面

参考代码:controller.js

//页面切换到聊天室界面  
function showChatRoom() {  
    //更新房间名  
    $("#chatRoom-header").find(".current-chatRoom-name").text(loginCommand.roomName);  

    //加载聊天历史  
    var chatHistory = service.loadChatHistory();  
    chatHistory.forEach(function (item) {  
        //展示发送的消息  
        var otherPerson = createCurrentChatRoomPerson(item.senderNickname + ":", item.content)  
        $(".chatRoom-content-box").append($(otherPerson));  
    });  

    //隐藏登录界面  
    $(".chat-login-box").hide();  
    // //显示聊天界面  
    $(".chatRoom-box").show();  
    // //滑动到最后一行  
    scrollBottom();  
}

至此,我们已经完成了goeasy长连接的初始化,和一个聊天室静态展示。接下来,我们一起来看看如何让这个聊天室能够动起来。

第二步:聊天室互动

1. 实时更新在线用户数和头像列表

之前在service.initialOnlineUsers方法已经初始化onlineUsers对象,但聊天室随时都有用户进进出出,所以我们接下来还需要能够在有用户上线或下线的时候能够实时的更新onlineUsers,并且实时显示在页面上。
当我们收到一个用户上线提醒,我们将新上线的用户的信息存入在线用户对象onlineUsers里,当有用户离开时,在本地在线用户列表里删除。

参考代码:service.js

//监听用户上下线时间,维护onlineUsers对象  
this.subscriberPresence = function() {  
    var self = this;  
    this.goeasy.subscribePresence({  
        channel: this.currentRoomId,  
        onPresence: function(presenceEvents) {  
            presenceEvents.events.forEach(function(event) {  
                var userId = event.userId;  
                var count = presenceEvents.clientAmount;  
                //更新onlineUsers在线用户数  
                self.onlineUsers.count = count;  
                //如果有用户进入聊天室  
                if (event.action == "join" || event.action == "online") {  
                    var userData = JSON.parse(event.userData);  
                    var nickName = userData.nickname;  
                    var avatar = userData.avatar;  
                    var user = new User(userId, nickName, avatar);  
                    //将新用户加入onlineUsers列表  
                    self.onlineUsers.users.push(user);  
                    //触发界面的更新  
                    self.onJoinRoom(user.nickname, user.avatar);  
                } else {  
                    for (var i = 0; i < self.onlineUsers.users.length; i++) {  
                        var leavingUser = self.onlineUsers.users[i];  
                        if (leavingUser.id == userId) {  
                            var nickName = leavingUser.nickname;  
                            var avatar = leavingUser.avatar;  
                            //将离开的用户从onlineUsers中删掉  
                            self.onlineUsers.users.splice(i, 1);  
                            //触发界面的更新  
                            self.onLeaveRoom(nickName, avatar);  
                        }  
                    }  
                }  
            });  
        },  
        onSuccess : function () {  
            console.log("监听成功")  
        },  
        onFailed : function () {  
            console.log("监听失败")  
        }  
    });  
};

2. 发送消息

  • 初始化一个chatMessage对象,包含发送方id,昵称,消息内容,消息类型为chat
  • 将chatMessage转换为一个Json格式的字符串
  • 调用GoEasy的Publish方法,完成消息的发送

参考代码(service.js)

this.sendMessage = function(content) {  
    var message = new ChatMessage(this.currentUser.id,this.currentUser.nickname, MessageType.CHAT, content);  
    var self = this;  
    this.goeasy.publish({  
        channel: self.currentRoomId,  
        message: JSON.stringify(message),  
        onSuccess: function() {  
            console.log("消息发布成功。");  
        },  
        onFailed: function(error) {  
            console.log("消息发送失败,错误编码:" + error.code + " 错误信息:" + error.content);  
        }  
    });  
};

3. 接收和显示新消息/道具

之前我们已经在初始化页面的时候执行了service.subscriberNewMessage(),当我们收到一条消息时:

  • 根据消息类型判断是一条聊天消息,还是一个道具
  • 如果收到的是一条聊天消息,直接显示到界面
  • 如果是道具,就播放动画

参考代码(service.js)

//监听消息或道具  
this.subscriberNewMessage = function() {  
    var self = this;  
    this.goeasy.subscribe({  
        channel: this.currentRoomId, //替换为您自己的channel  
        onMessage: function(message) {  
            var chatMessage = JSON.parse(message.content);  
            //todo:事实上不推荐在前端收到时保存, 一个用户开多个窗口,会导致重复保存, 建议所有消息都是都在发送时在服务器端保存,这里只是为了演示  
            self.restapi.saveChatMessage(self.currentRoomId, chatMessage);  
            //如果收到的是一个消息,就显示为消息  
            if (chatMessage.type == MessageType.CHAT) {  
                var selfSent = chatMessage.senderUserId == self.currentUser.id;  
                var content = JSON.parse(message.content);  
                self.onNewMessage(chatMessage.senderNickname, content, selfSent);  
            }  
            //如果收到的是一个道具,就播放道具动画  
            if (chatMessage.type == MessageType.PROP) {  
                if (chatMessage.content == Prop.ROCKET) {  
                    self.onNewRocket(chatMessage.senderNickname);  
                }  
                if (chatMessage.content == Prop.HEART) {  
                    self.onNewHeart(chatMessage.senderNickname);  
                }  
            }  
        }  
    });  
};

4. 发送和接收并展示道具

其实和发送消息的实现几乎是一样的,具体代码请参考service.js的sendProp方法,controller.js的onNewHeart()方法。动画的播放,使用了TweenMax这个库,主要是为了展示一个实现思路,小编也不知道这个库是否有很好的兼容性,以及是否能够用在Uniapp和小程序下,知道的朋友可以留言分享给大家。

this.sendProp = function(prop) {  
    var self = this;  
    var message = new ChatMessage(this.currentUser.id,this.currentUser.nickname, MessageType.PROP, prop);  
    this.goeasy.publish({  
        channel: self.currentRoomId,  
        message: JSON.stringify(message),  
        onSuccess: function() {  
            console.log("道具发布成功。");  
        },  
        onFailed: function(error) {  
            console.log("道具发送失败,错误编码:" + error.code + " 错误信息:" + error.content);  
        }  
    });  
};

至此,一个聊天室就搞定了,是不是很简单?

如果阅读本文或开发中有任何问题,也欢迎在GoEasy官网(https://www.goeasy.io)添加GoEasy为好友,来获得更多技术支持。

GoEasy系列教程:

收起阅读 »

uni-app自定义长按事件


<view   @touchmove="handletouchmove" @touchstart="handletouchstart" @touchend="handletouchend" >  
</view>  

             handletouchstart(e) {  
                this.timeOutEvent = setTimeout(() => {  
                    this.onLongPress(e)  
                }, 1000); //这里设置定时器,定义长按1000毫秒触发长按事件,时间可以自己改,  
                return false;  
            },  
            handletouchend() {  
                clearTimeout(this.time); //清除定时器    
                if (this.time != 0) {  
                    //处理点击时间  
                }  
                return false;  
            },  
            handletouchmove() {  
                clearTimeout(this.time); //清除定时器    
                this.time = 0;  
            },  
           onLongPress(e) {  
                    // 处理长按事件  
           }  
继续阅读 »

<view   @touchmove="handletouchmove" @touchstart="handletouchstart" @touchend="handletouchend" >  
</view>  

             handletouchstart(e) {  
                this.timeOutEvent = setTimeout(() => {  
                    this.onLongPress(e)  
                }, 1000); //这里设置定时器,定义长按1000毫秒触发长按事件,时间可以自己改,  
                return false;  
            },  
            handletouchend() {  
                clearTimeout(this.time); //清除定时器    
                if (this.time != 0) {  
                    //处理点击时间  
                }  
                return false;  
            },  
            handletouchmove() {  
                clearTimeout(this.time); //清除定时器    
                this.time = 0;  
            },  
           onLongPress(e) {  
                    // 处理长按事件  
           }  
收起阅读 »

uniAPP开发、团队开发uniapp

uniapp模板 5+App开发 移动APP uniapp

需求明确
沟通、整理和明确客户需求,撰写文档,搭建功能脑图架构
交互体验
用户体验设计、用户场景模拟、原型设计
UI设计
界面、色彩视觉设计、图标设计及布局设计
功能开发
服务器端、苹果及安卓端、前端H5开发/接口开发
测试验收
BUG修改、功能调整和优化、验收文档完善,上线应用市场
售后服务
后续技术维护、持续跟进、项目运营支撑
一⑤8叁2一①伍0玖九

继续阅读 »

需求明确
沟通、整理和明确客户需求,撰写文档,搭建功能脑图架构
交互体验
用户体验设计、用户场景模拟、原型设计
UI设计
界面、色彩视觉设计、图标设计及布局设计
功能开发
服务器端、苹果及安卓端、前端H5开发/接口开发
测试验收
BUG修改、功能调整和优化、验收文档完善,上线应用市场
售后服务
后续技术维护、持续跟进、项目运营支撑
一⑤8叁2一①伍0玖九

收起阅读 »

Sliver Rest Wp api:全功能的WordPress api工具

WordPress

Sliver Rest Wp api:全功能的WordPress api工具:无须会后台,轻松构建你自己的任何项目

关于Sliver Rest Wp api

Sliver Rest Wp api的前身是SliverRingApi。后来在某一天一个客户告诉我他网站出了很严重的安全性问题,还说他那边的人查出来问题就出在我的接口上,我当时一脸懵逼。我那接口全都是是get各种查,怎么会出问题?

当然,即便是不知道到底问题出在了谁身上(我自己的官网演示站三五个月都没闹动),我还是停止了继续发售SliverRingApi并且准备重构,也就是你现在看到的这个版本。

和SliverRingApi一样Sliver Rest Wp api包含了已有的所有功能,并且还将包含SliverRingApi所列出来的那些开发的所有功能,但这还远远不够,在未来的日子里我还将把本工具打造的更加强大,更加持久,所有第三方登录注册、所有支付、所有实用的功能,市面上有的,你能想得到的,我将全部给你!

Sliver Rest Wp api能做什么

在开始之前,我需要说明一下本工具采用的技术架构:WordPress+lumen

WordPress

如果你用过WordPress我相信你会知道它有多强大,如果你没有用过,那么我建议你去折腾一下,它绝对是开源建站程序中最优秀的一个,而且市场占有率极高,虽然在官方和大多数人看来,把WordPress定义成了一款博客程序或者cms但其实它远远不止于此,它能做的更多,所有的、全类型的都能做。

Lumen

最快的api开发框架。

传统的app或者前后端分离或者网站开发方式

试想一下,如果你现在要做一个app、一个前后端分离的网站你需要做什么?

一、开发一个后端管理程序?

那么为什么不能用WordPress呢,它免费开源,插件、主题数十万有余,开发者可文档满地都是,你用它来当你的后端,来管理你的内容难道不是相对于你重新开发来说更完美的嘛,尤其是对于不会开发后端程序的人来说。

二、再为你的app或者前后端分离的网站开发一套api?

那么为什么不用本套工具呢,本套工具的开发者也就是本人,折腾WordPress四年有余,对WordPress的底层以及特性都了如膝盖(离了如指掌还略远),而本接口则高度的利用了WordPress的特性并且结合了Lumen快速开发api的优越性,做到了快速、安全、方便、拓展于一体。

而且为了考虑不同类型的用途、不用类型程序的开发,我还做了特殊的设计,总而言之,只要你会前端,你可以利用它做任何事情!

所以,现在你的后端管理程序有了,api接口也有了,你只需要写你的前端(那正是我所不擅长的),其他所有事情就交给我来就好了!

与WP Rest Api相比

总所周知WordPress自己也有一套api叫做rest api,那么我为什么还要写这套工具呢:

与rest api相比更灵活,更安全
结合lumen(不知道这个的可以去百度一下)
结合了更多第三方功能:登录注册、支付等等
不需要安装插件(现在市面上大多数rest api拓展都推荐装一堆插件)
你可以禁用WordPress的rest api(据说这玩意是不安全的),不会影响本套工具的功能

能做什么?

最后在总结一下:

为了那些所有不会后端只会前端的开发者提供所有开发接口+详细文档
结合WordPress+lumen为你提供更快、更高效、更安全的api
帮助所有只会前端不会后端的开发者实现开发所有类型项目的梦想(接口即服务)

得了,不多说了:你自己来看吧,Sliver Rest Wp api:全功能的WordPress api工具):轻松构建你自己的任何项目

已有接口

文章类

sliver-rest-wp-api文章列表接口
sliver-rest-wp-api:文章阅读接口

分类类

sliver-rest-wp-api:获取网站分类列表
根据父级分类获取子分类
获取分类信息接口
获取分类下的标签列表

继续阅读 »

Sliver Rest Wp api:全功能的WordPress api工具:无须会后台,轻松构建你自己的任何项目

关于Sliver Rest Wp api

Sliver Rest Wp api的前身是SliverRingApi。后来在某一天一个客户告诉我他网站出了很严重的安全性问题,还说他那边的人查出来问题就出在我的接口上,我当时一脸懵逼。我那接口全都是是get各种查,怎么会出问题?

当然,即便是不知道到底问题出在了谁身上(我自己的官网演示站三五个月都没闹动),我还是停止了继续发售SliverRingApi并且准备重构,也就是你现在看到的这个版本。

和SliverRingApi一样Sliver Rest Wp api包含了已有的所有功能,并且还将包含SliverRingApi所列出来的那些开发的所有功能,但这还远远不够,在未来的日子里我还将把本工具打造的更加强大,更加持久,所有第三方登录注册、所有支付、所有实用的功能,市面上有的,你能想得到的,我将全部给你!

Sliver Rest Wp api能做什么

在开始之前,我需要说明一下本工具采用的技术架构:WordPress+lumen

WordPress

如果你用过WordPress我相信你会知道它有多强大,如果你没有用过,那么我建议你去折腾一下,它绝对是开源建站程序中最优秀的一个,而且市场占有率极高,虽然在官方和大多数人看来,把WordPress定义成了一款博客程序或者cms但其实它远远不止于此,它能做的更多,所有的、全类型的都能做。

Lumen

最快的api开发框架。

传统的app或者前后端分离或者网站开发方式

试想一下,如果你现在要做一个app、一个前后端分离的网站你需要做什么?

一、开发一个后端管理程序?

那么为什么不能用WordPress呢,它免费开源,插件、主题数十万有余,开发者可文档满地都是,你用它来当你的后端,来管理你的内容难道不是相对于你重新开发来说更完美的嘛,尤其是对于不会开发后端程序的人来说。

二、再为你的app或者前后端分离的网站开发一套api?

那么为什么不用本套工具呢,本套工具的开发者也就是本人,折腾WordPress四年有余,对WordPress的底层以及特性都了如膝盖(离了如指掌还略远),而本接口则高度的利用了WordPress的特性并且结合了Lumen快速开发api的优越性,做到了快速、安全、方便、拓展于一体。

而且为了考虑不同类型的用途、不用类型程序的开发,我还做了特殊的设计,总而言之,只要你会前端,你可以利用它做任何事情!

所以,现在你的后端管理程序有了,api接口也有了,你只需要写你的前端(那正是我所不擅长的),其他所有事情就交给我来就好了!

与WP Rest Api相比

总所周知WordPress自己也有一套api叫做rest api,那么我为什么还要写这套工具呢:

与rest api相比更灵活,更安全
结合lumen(不知道这个的可以去百度一下)
结合了更多第三方功能:登录注册、支付等等
不需要安装插件(现在市面上大多数rest api拓展都推荐装一堆插件)
你可以禁用WordPress的rest api(据说这玩意是不安全的),不会影响本套工具的功能

能做什么?

最后在总结一下:

为了那些所有不会后端只会前端的开发者提供所有开发接口+详细文档
结合WordPress+lumen为你提供更快、更高效、更安全的api
帮助所有只会前端不会后端的开发者实现开发所有类型项目的梦想(接口即服务)

得了,不多说了:你自己来看吧,Sliver Rest Wp api:全功能的WordPress api工具):轻松构建你自己的任何项目

已有接口

文章类

sliver-rest-wp-api文章列表接口
sliver-rest-wp-api:文章阅读接口

分类类

sliver-rest-wp-api:获取网站分类列表
根据父级分类获取子分类
获取分类信息接口
获取分类下的标签列表

收起阅读 »

UniAPP这些天使用心得

用uniApp有一段时间了,给我的第一感受是,第一次对程序开发失去了信心。
不知道是什么鬼才发明了这个东西,搞出来折磨人。
难用不说,经常是出一个错,你根本定位不到错误的位置,更奇葩的是,在游览器能显示正常的东西,在移动端就显示一片空白。
官方出的调试工具,竟然也能有bug,用有bug的调试工具去调试bug,真奇怪。
不得不说,开发uniAPP,真是一件令人沮丧的事,可惜我人微言轻,不能说服领导用flutter。
最后,我只想为我这些天掉的头发说一句,UniAPP,我可去你m的吧

继续阅读 »

用uniApp有一段时间了,给我的第一感受是,第一次对程序开发失去了信心。
不知道是什么鬼才发明了这个东西,搞出来折磨人。
难用不说,经常是出一个错,你根本定位不到错误的位置,更奇葩的是,在游览器能显示正常的东西,在移动端就显示一片空白。
官方出的调试工具,竟然也能有bug,用有bug的调试工具去调试bug,真奇怪。
不得不说,开发uniAPP,真是一件令人沮丧的事,可惜我人微言轻,不能说服领导用flutter。
最后,我只想为我这些天掉的头发说一句,UniAPP,我可去你m的吧

收起阅读 »

uniapp中使用sqlite对本地缓存下数据进行处理

uniapp SQLite

先说下我决定用sqlite的条件:

主要是流程处理,需要在无网络的情况下实现,数据量多的时候用h5的缓存完全不够,在看了文档之后选择使用SQLite ,早起在mui的时候使用的indexDB;

因为在社区也没收到具体的,所以写下记录下也和小伙伴分享下,有啥问题可以互相交流下。

该文档中用到的两个点 (SQLite 和vue中的mixin)

我这有两个环境,我先说一个简单;

  1. 主页菜单进去 到列表界面 从该步骤开始缓存本地数据;
  2. 从列表点击进入到详情,并在详情操作。有网络正常,无网络时存入表中,当切换到有网情况后,进行提交;

使用SQLite时需要先开权限,在配置文件中,如下图

SQLite官方demo ; 在hbuilderX 下新建demo pages/API/sqlite/sqlite 下官方提供的sqlite使用。为了使用方便,我把需要的单独提出来;

该出贴出function 代码,不做具体说明;

function openComDB(name, path, callback) {  
    plus.sqlite.openDatabase({  
        name: name,  
        path: path,  
        success: function(e) {  
            // plus.nativeUI.alert('打开数据库成功');  
            callback(e)  
        },  
        fail: function(e) {  
            // plus.nativeUI.alert("打开数据库失败");  
            callback(e);  
        }  
    })  
}  

function executeSQL(name, sql, callback) {  
    plus.sqlite.selectSql({  
        name: name,  
        sql: sql,  
        success: function(e) {  
            // console.log("查询数据库:" + name + ",表:" + sql + ";的");  
            // console.log(JSON.stringify(e));  
            callback(e);  
        },  
        fail: function(e) {  
            console.log("查询数据库失败:" + JSON.stringify(e));  
            callback(e);  
        }  
    })  
}  

export{  
openComDB,  
executeSQL  
}

该出 进入正题;
在需要用到的vue文件下,引入上方function; 路径是自己的;

import {openComDB,executeSQL,dropSQL} from '../../common/env.js'

说下简单的思路:
1.进入lists界面,先判断是否有网络,有网络正常调用,无网络时需要判断本地数据库中是否有数据,无数据则第一次进入。有数据需要调用本地数据中的数据;
ps
在有些uni使用上可能会和现在的有点出入,因为这个是早期写的NFC写入的功能,用的nvue的,还是weex的模式下。
贴上部分相关代码


created() {  
            this.getNetworkType(); //初始化网络当前状态;  
            uni.onNavigationBarButtonTap((e) => {    
 //该处是因为的导航栏右边加了两个筛选条件。可以忽略。 不过如果做离线需要筛选的,筛选条件等数据同样需要缓存  
                if (e.index == 1) {  
                    this.pickType();  
                } else if (e.index == 0) {  
                    this.pickBuild()  
                }  
            })  
        },  
methods: {  
    getNetworkType() {  
                //获取网络信息  
                uni.getNetworkType({  
                    success: res => {  
                        this.netWork = res.networkType;  
                        this.isOpenDB();  
                    }  
                })  
            },  
 isOpenDB() {  
                console.log('是否打开数据库');  
                var isOpen = plus.sqlite.isOpenDatabase({  
                    name: 'nfc', //数据库的名字  
                    path: '_doc/nfcList.db' //地址  
                });  
                console.log(!isOpen);  

                if (!isOpen) {  
                    console.log('Unoepned:' + isOpen);  
                    // plus.nativeUI.alert('Unopened!');  
                    this.openDB(); //打開DB  
                } else {  
                    // plus.nativeUI.alert('Opened!');  
                    this.isNet();  
                    // this.getLocalType();  
                }  
            },  
openDB() {  
                //SQLite      
                openComDB('nfc', '_doc/nfcList.db', res => {  
                    console.log('打开数据库');  
                    this.isNet();  
                });  
            },  
 isNet(){  
                                    //网络问题;  
                if (this.netWork == 'wifi' || this.netWork == '4g') {  
//在有网络情况下,会先情况之前的表,为了防止没有及时更新到数据。update我是嫌麻烦,没这样写。就这样暴力写了。  
                    console.log('wifi || 4g ');  
                    this.dropTable("pointLists");  
                    this.dropTable("codeTypeTable");  
                    this.dropTable("codeTable");  
                    this.dropTable("statusTable")  
//删了之后创建表  
                    this.createCodeTable();  
                    this.createCodeTypeTable();  
                    this.createBuildTable();  
                    this.createLists();  
                    this.createUpateStatus(); // 离线时更新  
//然后把数据插入进去  
                    this.getTabType(); //初始化格式  

                    this.getPonitType(); //获取code。默认值  
                    this.getCodeType(); // 类型下的选择项;  
                    this.getBuilds(); // 获取建筑物数据  

                } else {  
                    //无网络时;  
                    console.log('初始化无网络');  
                    this.getTabType(); //初始化格式  
                    this.locCodeTypeItem();  
                    this.selBuildFun();  
                }  
  //上面贴的drop,create,insert 我会在下面贴出部分代码。不会全部贴.SQL语句不会的建议百度找文档多看下,无非就是增删改查  
                        },  

        }

整个代码贴上太长了,我还是分段写吧;

本身就是个带tab类型的列表。延用nvue 的 weex的形式,未改成uniapp形式,如需参照此处tab,需要看之前的官方demo。 建议用官方新的uniapp模式,我是懒得改。

//初始化  
getTabType() {  
                //初始化列表;  
                let ary = [];  
                for (let i = 0; i < this.tabBars.length; i++) {  
                    let aryItem = {  
                        loadingText: "",  
                        data: [],  
                        pageNum: 1  
                    }  
                    ary.push(aryItem);  
                }  
                this.newsitems = ary;  
                if(this.netWork == "wifi" || this.netWork == "4g"){  
                    this.getPointList() //默认加载未处理;   有网络下,正常调用接口  
                }else{   
                    this.selPointList()    //无网络下调用数据库表中的数据;  
                }  

            },

再有网络调用接口时需要将 数据insert到创建的数据表中;

创建数据库中的表

我的思路是整个app为一个数据库,有各种不同的表。 目前只有nfc中用到了,所以数据库的name 就取名 nfc 了,未进行修改;

现在正儿八经的创建表;

用简单的建筑信息为例;根据自己需要的进行创建表;

createBuildTable() {  
                //创建建筑物类型表;  
                var sqlTable = 'create table if not exists buildTable("id" INT(10) NOT NULL UNIQUE,"name" CHAR,"gridCode" CHAR)'  
                executeSQL('nfc', sqlTable, res => {})  
            },

在有网调用接口时,insert 表;需要与上方创建的完全对应,最后一个不加 “,” 只有不对应就会报错

insertBuildCode() {  

                for (var i = 0; i < this.builds.length; i++) {  
                    var sqlInsert = "insert into buildTable values('";  
                    sqlInsert += this.builds[i].id + "','";  
                    sqlInsert += this.builds[i].name + "','";  
                    sqlInsert += this.builds[i].gridCode + "'";  
                    sqlInsert += ')';  
                    executeSQL('nfc', sqlInsert, res => {  

                    })  
                }  
                this.getLocBuilds()   //插入成功后,就可以查看结果。此处用到就直接赋值,没用到,或者别处用到,就在别处调用  

            },  
getLocBuilds() {  
                executeSQL('nfc', 'select * from buildTable', res => {  
                    console.log("建筑物查询结果");  

                    this.builds = res;  
                    this.selBuildFun()  
                })  
            },

查看就这样,就写个简单的例子。列表数据比较多。就不放上。如果详情的数据是通过detailById 接口调用,list表的需要将详情的数据加进去。

当整个数据存上后,就是点击list跳转到detail界面。
detail正常的vue文件。
有网络时正常查看,进行提交操作。
无网络时,查看本地数据库的表。
所有创建和插入都是在list进行的,detail.vue中 进行 查看,和更改,如果需要删除 也可。 (因为在list就很有可能是无网络的情况,所以所有的数据都在列表获取到了)
list ---> detail

可以带id ,也可以带json、建议带唯一的id就可以了,可以在detail select 出需要的数据;
我这里是觉着数据已经在list存上了,detail不如直接使用,不管用网络无网络,本身做提交,后台也不会不停的改数据。
比较严谨的可以有网络时候调用详情接口,无网络时在本地查。

查询代码上面贴了列子此处就不放上了。

简单说一下离线提交。会用到VUE的mixin。

离线提交的思路 。在list的时候需要创建 提交的 表 locSubTable(随便起的名字方便后面提到) ,并在最后 加上 flag 字段 (这个自己随意) 类型INT 。因为没有布尔。所以在插入的时候需要用flag 0,1 进行判断是否需要缓存提交。
0 为 false 1 为 true ;

在detail 内,若 是离线时进行提交,在 locSubTable 进行insert 。最后flag 插入 为 0 ;

在无网络切换至 wifi/4g下,查询 locSubTable 表 where flag = 0 的数据。 返回的 res.length = 0 的时候,无离线提交。

ren.length 有数据需要循环提交。接口提交成功之后需要。update locSubTable 中的 flag = 1 。表示缓存的数据已经被成功提交掉。

还需要更新下list 表的数据,将状态从 未处理 update 为 已处理 (此处更改是因为我的列表是tab类型。流程处理后的状态需要及时更新掉。)

写之前是打算贴代码的,写上之后发现不知道从哪里下手贴,只能说下处理的思路。

后面简单说下mixin的使用。是为了在全局监听网络变化,并能在app下进行离线提交。
在社区找到了uniapp是可以支持的。不过好像用的也不多。(此处该贴上官方链接,找到补上)

因为监听网络切换的时候,界面不会只停留在当前页。但是每个页面都写上也太多了,后来在vue的文档中找到了mixin 混入

《当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项 》
在写的时候也发现的一些坑,会一起记录下。(如果写完发现太多了那我就是被疫情在家憋惨了,憋成了话痨,尴尬)

再common中新建 mixin.js ; mixin的基础使用,可以在vue官方文档查找。

 var isLoc ={  
data() {  
        return {}  // 有需要的data,也可以加上,我没有用到。  
    },  
methoad:{  
//因为需要在其他地方查数据库,所以依旧在最开始打开数据库  
isOpenDB() {  
            console.log("mixin 中是否打开数据库");  
            var isOpen = plus.sqlite.isOpenDatabase({  
                name: 'nfc',  
                path: '_doc/nfcList.db'  
            });  
            console.log("数据库是否打开:" + !isOpen);  
            if (!isOpen) {  
                console.log('unopen:' + isOpen)  
                this.openDB()  
            }  
        },  
        openDB() {  
            openComDB('nfc', '_doc/nfcList.db', res => {  
                console.log("mixin:打开数据库");  
                this.getLocUser()  
            })  
        },  
selStatusList() {  
            //查询是否有离线写入,未提交数据; flag == 0  false。  
            // console.log("切换至网络,查询是否有缓存未提交数据");  
            executeSQL('nfc', 'select * from statusTable where flag = 0', res => {  
                // console.log(res)  
                // console.log(res.length);  
                for (var i = 0; i < res.length; i++) {  
                    this.submitPointFun(res[i].id); //返回的参数根据实际情况来的。  
                }  

            })  
        },  
        submitPointFun(curId) { // 提交巡检接口  
            //提交接口  
            getData({id: curId},data => {   //填自己的接口、本身这个我封了下,按照自己的来。  
                    urlFuc("xxxxxxxx", data, res => {  
                        console.log("当前ID:" + curId + "提交成功");  
                        //返回 成功后,  对列表的数据进行update 。  
                        this.updatePointStatus(curId);  
                        this.updatePointList(curId)  
                    });  
                });  
        },  
    updatePointStatus(curId) {  

            // 切换至有网络后,提交成功后,更新已提交成功数据 更新巡检点状态  
            //修改flag = 1  - true ;  
            var updateSQL = 'update statusTable set flag = 1 where id = ' + curId;  
            executeSQL('nfc', updateSQL, res => {  
                console.log("更新数据");  
            })  

        },  
        updatePointList(curId) {  
            // 状态更改后,更改列表改ID的数据  
            var updateSQL = 'update  pointLists set status = "已关联" where id = ' + curId;  
            console.log(updateSQL);  
            executeSQL('nfc', updateSQL, res => {  
                console.log("更新列表数据")  
            })  

        },  
    },  
},  
created() {  
        this.isOpenDB();   
        uni.onNetworkStatusChange((res) => {  

            //监听网络  
            console.log('MIXIN 下监听网络');  
            // console.log(res.isConnected);  
            // console.log(res.networkType);  
            if (res.networkType == 'none') {  
                console.log('无网络');  
                // that.seleceByType();  
            } else if (res.networkType == 'wifi' || res.networkType == '4g') {  
                console.log('wifi');  
//切换到有网络时,需要查看是否有离线数据,并进行提交。  
                this.selStatusList(); //查询是否有离线数据  
                this.selLocTaskList();  
                // that.getNfCList();  
            }  
        })  
    }  
}  

export {  
    isLoc   
}

引用

import {  
        isLocData  
    } from '../../common/mixin.js'  
export default {  
        mixins: [isLocData],  
data(){}  

}  

遇到的坑,
最开始想放在app.vue下的。 但是安装后会白屏。后来改到main.vue下。

这是一个比较简单的使用,后面还有一个有点复杂的使用,离线提交的时候上报多选择数据和 图片,备注等信息。 (离线图片的时候,需要注意进程关掉再开,之前的缓存图片地址就没了,会导致离线上传时图片的丢失。所以建议不要手动关掉进程,或者将图片存下来,成功后再删掉。)

之后因为图片临时缓存的问题,我在本地存了,提交成功后,删除本地存的文件。(离线中mixin.js一样)
imageList ,当前图片显示,因为会多张图上传。

var files = [];  
                console.log(data.length);  
                var that = this;  
                //将临时文件存为本地文件,不受进程关闭的影响  
                for (var i = 0; i < data.length; i++) {  
                    uni.saveFile({  
                        tempFilePath: data[i],  
                        success: img => {  
                            var savedFilePath = img.savedFilePath;  
                            console.log(savedFilePath);  
                            that.imageList.push(savedFilePath)  
                            console.log("tupian ")  
                            console.log(that.imageList)  
                            // files.push(savedFilePath);  
                            // console.log(files);  
                            // console.log(JSON.stringify(files));  
                        }  
                    })  
                }  

成功后记得删除本地

removeSaveFiles(filePath) {  
                //移除本地存储文件;  
                console.log("需要删除的地址")  
                console.log(filePath)  
                uni.removeSavedFile({  
                    filePath: filePath,  
                    complete: function(res) {  
                        console.log(res);  
                    }  
                });  

            },

状态也有 未处理,处理中,已结束。多个tabs。用法还是一样的。不做说明了。开始以为就一点点的。没想到会写这么多。

希望有帮助,这个sqlite 也是这次的时候使用过的,之前也没有。毕竟是个前端,搞的时候也问了后台一些思路。可能不是很完美,如果有什么好的建议也可以和我说。

补充说明

有小伙伴在指出

文档中 增删改 使用

    void plus.sqlite.executeSql(options);  

查询 使用


void plus.sqlite.selectSql(options);  

最开始没发现,我封装的 plus.sqlite.selectSql(options); 增删改查都可以使用,也没碰到什么问题。

建议大家按照官方的来使用。

继续阅读 »

先说下我决定用sqlite的条件:

主要是流程处理,需要在无网络的情况下实现,数据量多的时候用h5的缓存完全不够,在看了文档之后选择使用SQLite ,早起在mui的时候使用的indexDB;

因为在社区也没收到具体的,所以写下记录下也和小伙伴分享下,有啥问题可以互相交流下。

该文档中用到的两个点 (SQLite 和vue中的mixin)

我这有两个环境,我先说一个简单;

  1. 主页菜单进去 到列表界面 从该步骤开始缓存本地数据;
  2. 从列表点击进入到详情,并在详情操作。有网络正常,无网络时存入表中,当切换到有网情况后,进行提交;

使用SQLite时需要先开权限,在配置文件中,如下图

SQLite官方demo ; 在hbuilderX 下新建demo pages/API/sqlite/sqlite 下官方提供的sqlite使用。为了使用方便,我把需要的单独提出来;

该出贴出function 代码,不做具体说明;

function openComDB(name, path, callback) {  
    plus.sqlite.openDatabase({  
        name: name,  
        path: path,  
        success: function(e) {  
            // plus.nativeUI.alert('打开数据库成功');  
            callback(e)  
        },  
        fail: function(e) {  
            // plus.nativeUI.alert("打开数据库失败");  
            callback(e);  
        }  
    })  
}  

function executeSQL(name, sql, callback) {  
    plus.sqlite.selectSql({  
        name: name,  
        sql: sql,  
        success: function(e) {  
            // console.log("查询数据库:" + name + ",表:" + sql + ";的");  
            // console.log(JSON.stringify(e));  
            callback(e);  
        },  
        fail: function(e) {  
            console.log("查询数据库失败:" + JSON.stringify(e));  
            callback(e);  
        }  
    })  
}  

export{  
openComDB,  
executeSQL  
}

该出 进入正题;
在需要用到的vue文件下,引入上方function; 路径是自己的;

import {openComDB,executeSQL,dropSQL} from '../../common/env.js'

说下简单的思路:
1.进入lists界面,先判断是否有网络,有网络正常调用,无网络时需要判断本地数据库中是否有数据,无数据则第一次进入。有数据需要调用本地数据中的数据;
ps
在有些uni使用上可能会和现在的有点出入,因为这个是早期写的NFC写入的功能,用的nvue的,还是weex的模式下。
贴上部分相关代码


created() {  
            this.getNetworkType(); //初始化网络当前状态;  
            uni.onNavigationBarButtonTap((e) => {    
 //该处是因为的导航栏右边加了两个筛选条件。可以忽略。 不过如果做离线需要筛选的,筛选条件等数据同样需要缓存  
                if (e.index == 1) {  
                    this.pickType();  
                } else if (e.index == 0) {  
                    this.pickBuild()  
                }  
            })  
        },  
methods: {  
    getNetworkType() {  
                //获取网络信息  
                uni.getNetworkType({  
                    success: res => {  
                        this.netWork = res.networkType;  
                        this.isOpenDB();  
                    }  
                })  
            },  
 isOpenDB() {  
                console.log('是否打开数据库');  
                var isOpen = plus.sqlite.isOpenDatabase({  
                    name: 'nfc', //数据库的名字  
                    path: '_doc/nfcList.db' //地址  
                });  
                console.log(!isOpen);  

                if (!isOpen) {  
                    console.log('Unoepned:' + isOpen);  
                    // plus.nativeUI.alert('Unopened!');  
                    this.openDB(); //打開DB  
                } else {  
                    // plus.nativeUI.alert('Opened!');  
                    this.isNet();  
                    // this.getLocalType();  
                }  
            },  
openDB() {  
                //SQLite      
                openComDB('nfc', '_doc/nfcList.db', res => {  
                    console.log('打开数据库');  
                    this.isNet();  
                });  
            },  
 isNet(){  
                                    //网络问题;  
                if (this.netWork == 'wifi' || this.netWork == '4g') {  
//在有网络情况下,会先情况之前的表,为了防止没有及时更新到数据。update我是嫌麻烦,没这样写。就这样暴力写了。  
                    console.log('wifi || 4g ');  
                    this.dropTable("pointLists");  
                    this.dropTable("codeTypeTable");  
                    this.dropTable("codeTable");  
                    this.dropTable("statusTable")  
//删了之后创建表  
                    this.createCodeTable();  
                    this.createCodeTypeTable();  
                    this.createBuildTable();  
                    this.createLists();  
                    this.createUpateStatus(); // 离线时更新  
//然后把数据插入进去  
                    this.getTabType(); //初始化格式  

                    this.getPonitType(); //获取code。默认值  
                    this.getCodeType(); // 类型下的选择项;  
                    this.getBuilds(); // 获取建筑物数据  

                } else {  
                    //无网络时;  
                    console.log('初始化无网络');  
                    this.getTabType(); //初始化格式  
                    this.locCodeTypeItem();  
                    this.selBuildFun();  
                }  
  //上面贴的drop,create,insert 我会在下面贴出部分代码。不会全部贴.SQL语句不会的建议百度找文档多看下,无非就是增删改查  
                        },  

        }

整个代码贴上太长了,我还是分段写吧;

本身就是个带tab类型的列表。延用nvue 的 weex的形式,未改成uniapp形式,如需参照此处tab,需要看之前的官方demo。 建议用官方新的uniapp模式,我是懒得改。

//初始化  
getTabType() {  
                //初始化列表;  
                let ary = [];  
                for (let i = 0; i < this.tabBars.length; i++) {  
                    let aryItem = {  
                        loadingText: "",  
                        data: [],  
                        pageNum: 1  
                    }  
                    ary.push(aryItem);  
                }  
                this.newsitems = ary;  
                if(this.netWork == "wifi" || this.netWork == "4g"){  
                    this.getPointList() //默认加载未处理;   有网络下,正常调用接口  
                }else{   
                    this.selPointList()    //无网络下调用数据库表中的数据;  
                }  

            },

再有网络调用接口时需要将 数据insert到创建的数据表中;

创建数据库中的表

我的思路是整个app为一个数据库,有各种不同的表。 目前只有nfc中用到了,所以数据库的name 就取名 nfc 了,未进行修改;

现在正儿八经的创建表;

用简单的建筑信息为例;根据自己需要的进行创建表;

createBuildTable() {  
                //创建建筑物类型表;  
                var sqlTable = 'create table if not exists buildTable("id" INT(10) NOT NULL UNIQUE,"name" CHAR,"gridCode" CHAR)'  
                executeSQL('nfc', sqlTable, res => {})  
            },

在有网调用接口时,insert 表;需要与上方创建的完全对应,最后一个不加 “,” 只有不对应就会报错

insertBuildCode() {  

                for (var i = 0; i < this.builds.length; i++) {  
                    var sqlInsert = "insert into buildTable values('";  
                    sqlInsert += this.builds[i].id + "','";  
                    sqlInsert += this.builds[i].name + "','";  
                    sqlInsert += this.builds[i].gridCode + "'";  
                    sqlInsert += ')';  
                    executeSQL('nfc', sqlInsert, res => {  

                    })  
                }  
                this.getLocBuilds()   //插入成功后,就可以查看结果。此处用到就直接赋值,没用到,或者别处用到,就在别处调用  

            },  
getLocBuilds() {  
                executeSQL('nfc', 'select * from buildTable', res => {  
                    console.log("建筑物查询结果");  

                    this.builds = res;  
                    this.selBuildFun()  
                })  
            },

查看就这样,就写个简单的例子。列表数据比较多。就不放上。如果详情的数据是通过detailById 接口调用,list表的需要将详情的数据加进去。

当整个数据存上后,就是点击list跳转到detail界面。
detail正常的vue文件。
有网络时正常查看,进行提交操作。
无网络时,查看本地数据库的表。
所有创建和插入都是在list进行的,detail.vue中 进行 查看,和更改,如果需要删除 也可。 (因为在list就很有可能是无网络的情况,所以所有的数据都在列表获取到了)
list ---> detail

可以带id ,也可以带json、建议带唯一的id就可以了,可以在detail select 出需要的数据;
我这里是觉着数据已经在list存上了,detail不如直接使用,不管用网络无网络,本身做提交,后台也不会不停的改数据。
比较严谨的可以有网络时候调用详情接口,无网络时在本地查。

查询代码上面贴了列子此处就不放上了。

简单说一下离线提交。会用到VUE的mixin。

离线提交的思路 。在list的时候需要创建 提交的 表 locSubTable(随便起的名字方便后面提到) ,并在最后 加上 flag 字段 (这个自己随意) 类型INT 。因为没有布尔。所以在插入的时候需要用flag 0,1 进行判断是否需要缓存提交。
0 为 false 1 为 true ;

在detail 内,若 是离线时进行提交,在 locSubTable 进行insert 。最后flag 插入 为 0 ;

在无网络切换至 wifi/4g下,查询 locSubTable 表 where flag = 0 的数据。 返回的 res.length = 0 的时候,无离线提交。

ren.length 有数据需要循环提交。接口提交成功之后需要。update locSubTable 中的 flag = 1 。表示缓存的数据已经被成功提交掉。

还需要更新下list 表的数据,将状态从 未处理 update 为 已处理 (此处更改是因为我的列表是tab类型。流程处理后的状态需要及时更新掉。)

写之前是打算贴代码的,写上之后发现不知道从哪里下手贴,只能说下处理的思路。

后面简单说下mixin的使用。是为了在全局监听网络变化,并能在app下进行离线提交。
在社区找到了uniapp是可以支持的。不过好像用的也不多。(此处该贴上官方链接,找到补上)

因为监听网络切换的时候,界面不会只停留在当前页。但是每个页面都写上也太多了,后来在vue的文档中找到了mixin 混入

《当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项 》
在写的时候也发现的一些坑,会一起记录下。(如果写完发现太多了那我就是被疫情在家憋惨了,憋成了话痨,尴尬)

再common中新建 mixin.js ; mixin的基础使用,可以在vue官方文档查找。

 var isLoc ={  
data() {  
        return {}  // 有需要的data,也可以加上,我没有用到。  
    },  
methoad:{  
//因为需要在其他地方查数据库,所以依旧在最开始打开数据库  
isOpenDB() {  
            console.log("mixin 中是否打开数据库");  
            var isOpen = plus.sqlite.isOpenDatabase({  
                name: 'nfc',  
                path: '_doc/nfcList.db'  
            });  
            console.log("数据库是否打开:" + !isOpen);  
            if (!isOpen) {  
                console.log('unopen:' + isOpen)  
                this.openDB()  
            }  
        },  
        openDB() {  
            openComDB('nfc', '_doc/nfcList.db', res => {  
                console.log("mixin:打开数据库");  
                this.getLocUser()  
            })  
        },  
selStatusList() {  
            //查询是否有离线写入,未提交数据; flag == 0  false。  
            // console.log("切换至网络,查询是否有缓存未提交数据");  
            executeSQL('nfc', 'select * from statusTable where flag = 0', res => {  
                // console.log(res)  
                // console.log(res.length);  
                for (var i = 0; i < res.length; i++) {  
                    this.submitPointFun(res[i].id); //返回的参数根据实际情况来的。  
                }  

            })  
        },  
        submitPointFun(curId) { // 提交巡检接口  
            //提交接口  
            getData({id: curId},data => {   //填自己的接口、本身这个我封了下,按照自己的来。  
                    urlFuc("xxxxxxxx", data, res => {  
                        console.log("当前ID:" + curId + "提交成功");  
                        //返回 成功后,  对列表的数据进行update 。  
                        this.updatePointStatus(curId);  
                        this.updatePointList(curId)  
                    });  
                });  
        },  
    updatePointStatus(curId) {  

            // 切换至有网络后,提交成功后,更新已提交成功数据 更新巡检点状态  
            //修改flag = 1  - true ;  
            var updateSQL = 'update statusTable set flag = 1 where id = ' + curId;  
            executeSQL('nfc', updateSQL, res => {  
                console.log("更新数据");  
            })  

        },  
        updatePointList(curId) {  
            // 状态更改后,更改列表改ID的数据  
            var updateSQL = 'update  pointLists set status = "已关联" where id = ' + curId;  
            console.log(updateSQL);  
            executeSQL('nfc', updateSQL, res => {  
                console.log("更新列表数据")  
            })  

        },  
    },  
},  
created() {  
        this.isOpenDB();   
        uni.onNetworkStatusChange((res) => {  

            //监听网络  
            console.log('MIXIN 下监听网络');  
            // console.log(res.isConnected);  
            // console.log(res.networkType);  
            if (res.networkType == 'none') {  
                console.log('无网络');  
                // that.seleceByType();  
            } else if (res.networkType == 'wifi' || res.networkType == '4g') {  
                console.log('wifi');  
//切换到有网络时,需要查看是否有离线数据,并进行提交。  
                this.selStatusList(); //查询是否有离线数据  
                this.selLocTaskList();  
                // that.getNfCList();  
            }  
        })  
    }  
}  

export {  
    isLoc   
}

引用

import {  
        isLocData  
    } from '../../common/mixin.js'  
export default {  
        mixins: [isLocData],  
data(){}  

}  

遇到的坑,
最开始想放在app.vue下的。 但是安装后会白屏。后来改到main.vue下。

这是一个比较简单的使用,后面还有一个有点复杂的使用,离线提交的时候上报多选择数据和 图片,备注等信息。 (离线图片的时候,需要注意进程关掉再开,之前的缓存图片地址就没了,会导致离线上传时图片的丢失。所以建议不要手动关掉进程,或者将图片存下来,成功后再删掉。)

之后因为图片临时缓存的问题,我在本地存了,提交成功后,删除本地存的文件。(离线中mixin.js一样)
imageList ,当前图片显示,因为会多张图上传。

var files = [];  
                console.log(data.length);  
                var that = this;  
                //将临时文件存为本地文件,不受进程关闭的影响  
                for (var i = 0; i < data.length; i++) {  
                    uni.saveFile({  
                        tempFilePath: data[i],  
                        success: img => {  
                            var savedFilePath = img.savedFilePath;  
                            console.log(savedFilePath);  
                            that.imageList.push(savedFilePath)  
                            console.log("tupian ")  
                            console.log(that.imageList)  
                            // files.push(savedFilePath);  
                            // console.log(files);  
                            // console.log(JSON.stringify(files));  
                        }  
                    })  
                }  

成功后记得删除本地

removeSaveFiles(filePath) {  
                //移除本地存储文件;  
                console.log("需要删除的地址")  
                console.log(filePath)  
                uni.removeSavedFile({  
                    filePath: filePath,  
                    complete: function(res) {  
                        console.log(res);  
                    }  
                });  

            },

状态也有 未处理,处理中,已结束。多个tabs。用法还是一样的。不做说明了。开始以为就一点点的。没想到会写这么多。

希望有帮助,这个sqlite 也是这次的时候使用过的,之前也没有。毕竟是个前端,搞的时候也问了后台一些思路。可能不是很完美,如果有什么好的建议也可以和我说。

补充说明

有小伙伴在指出

文档中 增删改 使用

    void plus.sqlite.executeSql(options);  

查询 使用


void plus.sqlite.selectSql(options);  

最开始没发现,我封装的 plus.sqlite.selectSql(options); 增删改查都可以使用,也没碰到什么问题。

建议大家按照官方的来使用。

收起阅读 »

团队开发uniapp、软硬件对接结合开发

App 5+App开发 移动APP uniapp

团队开发uniapp、软硬件对接结合开发
uniapp定制开发
1.需求项目沟通明确

  1. 工期和报价明确
  2. UI设计图确认
  3. 前端切图样式调整编写代码
  4. 后台搭建
  5. 上线前内部测试
  6. 填充内容
    一⑤八叁2壹1伍0九玖

团队开发uniapp、软硬件对接结合开发
uniapp定制开发
1.需求项目沟通明确

  1. 工期和报价明确
  2. UI设计图确认
  3. 前端切图样式调整编写代码
  4. 后台搭建
  5. 上线前内部测试
  6. 填充内容
    一⑤八叁2壹1伍0九玖

main.js Vue.prototype 全局变量的挂载与动态赋值

Vue 全局变量

在main.js中,Vue.prototype.$appName = “XXX” 可以用来定义一些我们不常变化的全局属性或者方法的。
但是有时候我们想动态改变它。特别是从远程获取最新的json信息,然后动态更新这个main,js里面定义的东西。
所以在定义、挂载这些变量的时候,可以这样写:
Vue.prototype.$appName = {'value':'XXXX'}
调用的时候,我们用var abc = this.$appName.value 进行调用
然后再从远程获取新的信息时,可以动态更新此值
this.$appName.value = 'YYYY'

继续阅读 »

在main.js中,Vue.prototype.$appName = “XXX” 可以用来定义一些我们不常变化的全局属性或者方法的。
但是有时候我们想动态改变它。特别是从远程获取最新的json信息,然后动态更新这个main,js里面定义的东西。
所以在定义、挂载这些变量的时候,可以这样写:
Vue.prototype.$appName = {'value':'XXXX'}
调用的时候,我们用var abc = this.$appName.value 进行调用
然后再从远程获取新的信息时,可以动态更新此值
this.$appName.value = 'YYYY'

收起阅读 »

《重点是:#FFF级的小白用uniapp,50就能做出一个通用社交app》

今儿数据丢了,必须强颜欢笑啊。。。掐指一算,而今迈步刚好50天

回顾历史啊,,,想当天。。。。

50天前,我连js的全拼都记不住,就像脑袋里没那属性,或者是给冒泡了

但是50天后的现在,,,世事变幻莫测,天旋地转,物是人非,哈哈,我有了一个自己写的,能注册、登录、改资料、发布、回复、聚合等等的社交小app!

我要感谢uniApp,感谢builderX,感谢uniCloud,感谢ccTv。。。嗯,连这cctv不用驼峰都觉着别扭。。。

当然,我这离回梦、林举那些高人还有十万八千里路,,,但是,至少现在,嗯,以后app那点事儿可就蒙不了我了哈。。。

毕竟哥也算是个程序员了诶巴扎黑

so,千言万语汇成一句话,uniApp、uniApp、uni——App!

继续阅读 »

今儿数据丢了,必须强颜欢笑啊。。。掐指一算,而今迈步刚好50天

回顾历史啊,,,想当天。。。。

50天前,我连js的全拼都记不住,就像脑袋里没那属性,或者是给冒泡了

但是50天后的现在,,,世事变幻莫测,天旋地转,物是人非,哈哈,我有了一个自己写的,能注册、登录、改资料、发布、回复、聚合等等的社交小app!

我要感谢uniApp,感谢builderX,感谢uniCloud,感谢ccTv。。。嗯,连这cctv不用驼峰都觉着别扭。。。

当然,我这离回梦、林举那些高人还有十万八千里路,,,但是,至少现在,嗯,以后app那点事儿可就蒙不了我了哈。。。

毕竟哥也算是个程序员了诶巴扎黑

so,千言万语汇成一句话,uniApp、uniApp、uni——App!

收起阅读 »

贡献一个知识点,文档里肯定不会告诉你的东西

where查询和聚合不能一起用。

比如:想连表查询的话只能用match+lookup。

至于为什么,应该是国家机密吧。毕竟连where和聚合不能一起用都要保密。。。这天气真是热了

so,鉴于聚合是民生刚需,所以请大家奔走相告,早用match早超生。

where查询和聚合不能一起用。

比如:想连表查询的话只能用match+lookup。

至于为什么,应该是国家机密吧。毕竟连where和聚合不能一起用都要保密。。。这天气真是热了

so,鉴于聚合是民生刚需,所以请大家奔走相告,早用match早超生。

tailwind.css 对于小程序按钮样式的影响

微信小程序
/* 罪魁祸首 */  
/**  
 * Correct the inability to style clickable types in iOS and Safari.  
 */  
button,  
[type="button"],  
[type="reset"],  
[type="submit"] {  
  -webkit-appearance: button;  
}  
/** 解决方案 */  
button, [type="button"], [type="reset"], [type="submit"] {  
    -moz-appearance: none;  
    -webkit-appearance: none;  
}  

tailwind.css 对于小程序按钮样式的影响,前两天发现小程序按钮的默认样式一直去不掉,今天才发现是因为 tailwind.css 导致的。 他有一段上面那样的 css 采用了系统自定义的按钮样式导致你怎么覆盖 button::after 都没有用

》》》》 原文地址
》》》》 demo代码

崮生的 uni-app 最佳实践

继续阅读 »
/* 罪魁祸首 */  
/**  
 * Correct the inability to style clickable types in iOS and Safari.  
 */  
button,  
[type="button"],  
[type="reset"],  
[type="submit"] {  
  -webkit-appearance: button;  
}  
/** 解决方案 */  
button, [type="button"], [type="reset"], [type="submit"] {  
    -moz-appearance: none;  
    -webkit-appearance: none;  
}  

tailwind.css 对于小程序按钮样式的影响,前两天发现小程序按钮的默认样式一直去不掉,今天才发现是因为 tailwind.css 导致的。 他有一段上面那样的 css 采用了系统自定义的按钮样式导致你怎么覆盖 button::after 都没有用

》》》》 原文地址
》》》》 demo代码

崮生的 uni-app 最佳实践

收起阅读 »

基于uni-app的高颜值极速记账小程序

微信小程序

【项目背景】

日常生活中,多少都会有记账的需求。(今天请客花了多少钱?今天工资收了多少?....本月有几张信用卡要还?还款日期、还款金额分别是多少?...)
通常的记账软件,80%都要求下载APP,并且操作较为繁琐。

故开发此小程序,名曰“小熙记账”。熙通“禧”,意为:幸福;吉祥。

记一笔收入,买一份礼物,给自己一个小确幸。

【开发背景】基于uni-app,用最优雅的代码敏捷开发

【产品目标】行云流水般的极速记账体验

【功能列表】

1、极速记账:三秒记账,打完收枪

2、收支统计:按月查统计图

3、多账户管理:储蓄账户、信用账户、其他账户,支持账户之间转账

4、流水查询:查看所有记账流水

5、账目总览:累计、当月收支情况,还款任务列表,近三月大额支出

【产品截图】

【即刻体验】

继续阅读 »

【项目背景】

日常生活中,多少都会有记账的需求。(今天请客花了多少钱?今天工资收了多少?....本月有几张信用卡要还?还款日期、还款金额分别是多少?...)
通常的记账软件,80%都要求下载APP,并且操作较为繁琐。

故开发此小程序,名曰“小熙记账”。熙通“禧”,意为:幸福;吉祥。

记一笔收入,买一份礼物,给自己一个小确幸。

【开发背景】基于uni-app,用最优雅的代码敏捷开发

【产品目标】行云流水般的极速记账体验

【功能列表】

1、极速记账:三秒记账,打完收枪

2、收支统计:按月查统计图

3、多账户管理:储蓄账户、信用账户、其他账户,支持账户之间转账

4、流水查询:查看所有记账流水

5、账目总览:累计、当月收支情况,还款任务列表,近三月大额支出

【产品截图】

【即刻体验】

收起阅读 »