吃辣条的大妖怪
吃辣条的大妖怪
  • 发布:2020-04-20 17:33
  • 更新:2021-02-18 14:28
  • 阅读:9951

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

分类:uni-app

先说下我决定用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); 增删改查都可以使用,也没碰到什么问题。

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

7 关注 分享
roxu 825053184@qq.com ai666 孙大圣 正儿八经吃豆腐 654253369@qq.com 1078889713@qq.com

要回复文章请先登录注册

1078889713@qq.com

1078889713@qq.com

插眼
2021-02-18 14:28
2576991756@qq.com

2576991756@qq.com

这个配合websocket可以用吗
2021-01-29 15:09
409778140@qq.com

409778140@qq.com

看不懂啊
2020-11-06 10:58
2214155717@qq.com

2214155717@qq.com

如果版本升级,这个数据怎么控制呢
2020-08-31 11:47
lusl@live.cn

lusl@live.cn

条件编译调用 HTML5+

小程序及 H5 等平台是没有 HTML5+ 扩展规范的,因此在 uni-app 调用 HTML5+ 的扩展规范时,需要注意使用条件编译。否则运行到h5、小程序等平台会出现 plus is not defined错误。

这个怎么用啊?https://uniapp.dcloud.net.cn/use-html5plus
上面网址的copy and paste后,报sqlite不认。
2020-08-28 17:46
913524436@qq.com

913524436@qq.com

路径 _doc 是怎么来的
2020-07-30 14:26
412759899@qq.com

412759899@qq.com

请问SQLITE支持H5吗
2020-07-14 09:22
stars云

stars云

有具体的案例么
2020-07-01 10:57
工頁光軍

工頁光軍

数据结构中,就得模拟SQL数据结构来操作!这个不存在你说的这些问题!
2020-04-30 12:59
工頁光軍

工頁光軍

数据库操作的,可以采用多方案,在uniapp有sqlite,storage,io等,在小程序有storage,在h5更多,没有关系型的数据存储可以先读取,通过jsonsql来对数据进行操作,完成增删改查,有关系型的,直接SQL语句就可以,简单的说,要运用SQL语句,在非关系型
2020-04-30 12:57