因为人事档案具备涉密性,所以本利用没有应用后盾服务,全副性能都在 APP 本地实现。
开发工具采纳 APICloud Studio3,基于 VSCode 的(PS:比基于 Atom 的 autio2 好用太多);
数据库采纳 sqllite,没有应用 UI 框架,集体感觉 AVM 自身反对的 flex 布局配合自写 CSS 款式,齐全能够实现市面上所有的UI框架的元素,这个取决于集体功力。
一、我的项目思维脑图
二、性能介绍
1、人员花名册
2、编制状况
3、集体核心
三、技术要点
手势明码验证,本地数据库操作,语音播报。
用到的模块
我的项目文件目录
援用一下官网的对于目录构造的介绍
四、性能开发详解
1、首页导航
零碎首页应用 tabLayout,能够将相干参数配置在 JSON 文件中,再在 config.xml 中将 content 的值设置成该 JSON 文件的门路。如果底部导航没有非凡需要这里强烈建议大家应用 tabLayout 为 APP 进行布局,官网曾经将各类手机屏幕及不同的分辨率进行了适配,免去了很多对于适配方面的问题。
app.json 文件内容,对于 json 文件的命名是没有限度的,我习惯用 app。
{
"name": "root",
"textOffset": 6,
"color": "#999999",
"selectedColor": "#006aff",
"scrollEnabled": false,
"hideNavigationBar": false,
"bgColor": "#fff",
"navigationBar": {
"background": "#006aff",
"shadow": "rgba(0,0,0,0)",
"color": "#fff",
"fontSize": 18,
"hideBackButton": true
},
"tabBar": {
"background": "#fff",
"shadow": "#eee",
"color": "#5E5E5E",
"selectedColor": "#006aff",
"textOffset": 3,
"fontSize": 11,
"scrollEnabled": true,
"index": 1,
"preload": 0,
"frames": [
{
"title": "编制状况",
"name": "home",
"url": "./pages/records/organ"
},
{
"title": "人员花名册",
"name": "course",
"url": "./pages/person/organ"
},
{
"title": "集体核心",
"name": "user",
"url": "./pages/main/main"
}
],
"list": [
{
"text": "编制",
"iconPath": "./image/authoried-o.png",
"selectedIconPath": "./image/authoried.png"
},
{
"text": "人员",
"iconPath": "./image/person-o.png",
"selectedIconPath": "./image/person.png"
},
{
"text": "我的",
"iconPath": "./image/user-o.png",
"selectedIconPath": "./image/user.png"
}
]
}
}
2、列表显示及分页
通过上拉刷新和下拉操作,配合 JS 办法实现分页查问性能。
<template name='list'>
<scroll-view scroll-y class="main" enable-back-to-top refresher-enabled refresher-triggered={refresherTriggered} onrefresherrefresh={this.onrefresherrefresh} onscrolltolower={this.onscrolltolower}>
<view class="item-box">
<view class="item" data-id={item.id} v-for="(item, index) in personList" tapmode onclick="openTab">
<image class="avator" src={item.photo} mode="widthFix"></image>
<text class="item-title">{item.name}</text>
<text class="item-sub-title">{item.nation}</text>
</view>
</view>
<view class="footer">
<text class="loadDesc">{loadStateDesc}</text>
</view>
</scroll-view>
</template>
<script>
import $util from "../../utils/utils.js"
export default {
name: 'list',
data() {
return{personList:[],
skip: 0,
refresherTriggered: false,
haveMoreData: true,
loading: false,
organid:0
}
},
computed: {loadStateDesc(){if (this.data.loading || this.data.haveMoreData) {return '加载中...';} else if (this.personList.length > 0) {return '没有更多啦';} else {return '临时没有内容';}
}
},
methods: {apiready(){
this.data.organid = api.pageParam.id;
this.loadData(false);
// 更换头像
api.addEventListener({name: 'setavator'}, (ret, err) => {this.loadData();
});
// 新增人员信息
api.addEventListener({name: 'addperson'}, (ret, err) => {this.loadData();
});
// 删除人员信息
api.addEventListener({name: 'delperson'}, (ret, err) => {this.loadData();
});
if(api.getPrefs({sync: true,key: 'role'})=='99'){
// 增加编辑按钮
api.setNavBarAttr({
rightButtons: [{text: '新增'}]
});
// 监听右上角按钮点击事件
api.addEventListener({name: 'navitembtn'}, (ret, err) => {if (ret.type == 'right') {
$util.openWin({
name: 'personadd',
url: 'personadd.stml',
title: '新增人员信息',
pageParam:{organid:this.data.organid}
});
}
});
}
},
loadData(loadMore) {if (this.data.loading) {return;}
api.showProgress();
this.data.loading = true;
var limit = 15;
var skip = loadMore?(this.data.skip+1)*limit:0;
// console.log('select id,name,grade,sex,nation,photo from authority where organ ='+this.data.organid+'order by id limit'+limit+'offset'+skip);
var db = api.require('db');
db.selectSql({
name: 'doc',
sql: 'select id,name,grade,sex,nation,photo from authorized where organ ='+this.data.organid+'order by id limit'+limit+'offset'+skip
}, (ret, err)=> {// console.log(JSON.stringify(ret));
// console.log(JSON.stringify(err));
if (ret.status) {
let records = ret.data;
this.data.haveMoreData = records.length == limit;
if (loadMore) {this.data.personList = this.data.personList.concat(records);
} else {this.data.personList = records;}
this.data.skip = skip;
} else {
this.data.recordsList = records;
api.toast({msg:err.msg})
}
this.data.loading = false;
this.data.refresherTriggered = false;
api.hideProgress();});
},
/* 下拉刷新页面 */
onrefresherrefresh(){
this.data.refresherTriggered = true;
this.loadData(false);
},
onscrolltolower() {if (this.data.haveMoreData) {this.loadData(true);
}
},
openTab(e){
let id = e.currentTarget.dataset.id;
$util.openWin({
name: "personinfo",
url: 'personinfo.stml',
title: '人员信息',
pageParam:{id:id}
});
}
}
}
</script>
3、表单提交
采纳 AVM 自带的 from 控件,通过 onsubmit 进行数据提交
4、头像图片上传及 base64 转码
因为是本地 sqllite 数据库,人员头像图片须要转成 base64 编码存储到数据库中。通过官网模块 trans 进行图片转码操作。
<image class="avator" src={this.data.src} mode="widthFix" onclick="setavator"></image>
setavator(){
api.actionSheet({
cancelTitle: '勾销',
buttons: ['拍照', '关上相册']
}, (ret, err) => {if (ret.buttonIndex == 3) {return false;}
var sourceType = (ret.buttonIndex == 1) ? 'camera' : 'album';
api.getPicture({
sourceType: sourceType,
allowEdit: true,
quality: 20,
destinationType:'url'
}, (ret, err) => {if (ret && ret.data) {var trans = api.require('trans');
trans.decodeImgToBase64({imgPath: ret.data}, (ret, err) => {// console.log(JSON.stringify(ret));
// console.log(JSON.stringify(err));
if (ret.status) {
let b64 = "data:image/jpeg;base64,"+ret.base64Str;
this.data.src = b64;
} else {
api.toast({msg:'照片上传失败,请从新抉择!'})
}
});
}
});
});
},
5、sqllite 数据库 db 模块
因为数据库文件须要存储的利用安装文件中,所有须要官网 fs 模块配合应用来进行数据库文件的操作。
copyDB(){var fs = api.require('fs');
fs.copyTo({
oldPath: 'widget://db/doc.db',
newPath: 'fs://db'
}, function(ret, err) {// console.log(JSON.stringify(ret));
// console.log(JSON.stringify(err));
if (ret.status) {// console.log(JSON.stringify(ret));
api.toast({msg:'拷贝数据库胜利!'})
} else {// console.log(JSON.stringify(err));
api.toast({msg:JSON.stringify(err)
})
}
});
},
openDB(){var db = api.require('db');
db.subfile({directory:'fs://db'}, (ret, err)=> {// console.log(JSON.stringify(ret));
// console.log(JSON.stringify(err));
if (ret.status) {// console.log(JSON.stringify(ret));
// 关上数据库
db.openDatabase({
name: 'doc',
path: ret.files[0]
}, (ret, err)=> {// console.log(JSON.stringify(ret));
// console.log(JSON.stringify(err));
if (ret.status) {// console.log(JSON.stringify(ret));
api.toast({msg:'关上数据库胜利!'})
} else {// console.log(JSON.stringify(err));
api.toast({msg:JSON.stringify(err)
})
}
});
} else {// console.log(JSON.stringify(err));
api.toast({msg:JSON.stringify(err)
})
}
});
},
closeDB(){var db = api.require('db');
db.closeDatabase({name: 'doc'}, function(ret, err) {if (ret.status) {console.log(JSON.stringify(ret));
api.toast({msg:'敞开数据库胜利'})
} else {// console.log(JSON.stringify(err));
api.toast({msg:JSON.stringify(err)
})
}
});
},
updateDB(){var fs = api.require('fs');
var db = api.require('db');
db.closeDatabase({name: 'doc'}, (ret, err) => {if (ret.status) {
// 拷贝文件
fs.copyTo({
oldPath: 'widget://doc.db',
newPath: 'fs://db/'
}, (ret, err) => {if (ret.status) {
db.subfile({directory:'fs://db'}, (ret, err)=> {if(ret.status){
// 关上数据库
db.openDatabase({
name: 'doc',
path: ret.files[0]
}, (ret, err)=> {if (ret.status) {
api.toast({msg:'数据库更新胜利!'})
} else {
api.toast({msg:JSON.stringify(err)
})
}
});
}
else{
api.toast({msg:JSON.stringify(err)
})
}
})
} else {
api.toast({msg:JSON.stringify(err)
})
}
});
} else {
api.toast({msg:JSON.stringify(err)
})
}
});
},
6、语音播报性能
采纳官网提供的 IFLyVoice 模块,须要留神的是,根底资源文件和发音人资源文件(.jet 文件)须要到科大讯飞开发者平台进行下载导入的我的项目中。还有对数字的解读不是准确,尤其是年份,最好不要用数字,而是用中文。
// 增加朗诵按钮
api.setNavBarAttr({
rightButtons: [{text: '朗诵'}]
});
// 监听右上角按钮点击事件
api.addEventListener({name: 'navitembtn'}, (ret, err) => {// console.log(JSON.stringify(this.data.info));
if (ret.type == 'right') {var IFlyVoice = api.require('IFlyVoice');
IFlyVoice.initSpeechSynthesizer((ret)=>{// console.log(JSON.stringify(ret));
});
IFlyVoice.startSynthetic({
text:this.data.info,
commonPath_Android:'widget://res/android/common.jet',
pronouncePath_Android:'widget://res/android/xiaoyan.jet',
pronounceName:'xiaoyan',
speed:40
},(ret,err)=>{// console.log(JSON.stringify(ret));
// console.log(JSON.stringify(err));
if (ret.status) {// console.log('合成胜利');
} else {// console.log(JSON.stringify(err));
}
});
}
});
7、手势密码保护
手势密码保护因为官网模块存在款式问题,及原生模块存在遮罩问题,所以采纳了平台上提供的 H5 模块。APICloud 弱小之处在这里进行了酣畅淋漓的体现,通过 AVM 及原生模块无奈实现的性能,能够再用 H5 的形式来实现!牛逼!!!!,通过设置全局变量来记录是否已设置手机明码,每次利用启动通过这个变量来判断是否开启手势密码保护。
this.data.islock =api.getPrefs({sync: true,key: 'islock'});
if(this.data.islock=='Y'){
api.openFrame({
name: 'h5lock',
url:'../../html/h5lock.html'
})
}
else{
api.toast({msg:'您还没有设置手势明码,为了数据安全,请尽快设置。'})
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>H5lock</title>
<style type="text/css">
body {
text-align: center;
background-color: #000000;
}
.title {
/*color: #87888a;*/
margin-top: 85px;
font-size: 20px;
font-weight:lighter;
}
</style>
</head>
<body>
<script type="text/javascript" src="../script/H5lock.js"></script>
<script type="text/javascript">
var opt = {
chooseType: 3, // 3 , 4 , 5,
width: 300, // lock wrap width
height: 300, // lock wrap height
container: 'element', // the id attribute of element
inputEnd: function (psw){} // when draw end param is password string}
var lock = new H5lock(opt);
lock.init();
</script>
</body>
</html>
(function(){window.H5lock = function(obj){
this.height = obj.height;
this.width = obj.width;
this.chooseType = Number(window.localStorage.getItem('chooseType')) || obj.chooseType;
this.devicePixelRatio = window.devicePixelRatio || 1;
};
H5lock.prototype.drawCle = function(x, y) { // 初始化解锁明码面板 小圆圈
this.ctx.strokeStyle = '#87888a';// 明码的点点默认的色彩
this.ctx.lineWidth = 2;
this.ctx.beginPath();
this.ctx.arc(x, y, this.r, 0, Math.PI * 2, true);
this.ctx.closePath();
this.ctx.stroke();}
H5lock.prototype.drawPoint = function(style) { // 初始化圆心
for (var i = 0 ; i < this.lastPoint.length ; i++) {
this.ctx.fillStyle = style;
this.ctx.beginPath();
this.ctx.arc(this.lastPoint[i].x, this.lastPoint[i].y, this.r / 2.5, 0, Math.PI * 2, true);
this.ctx.closePath();
this.ctx.fill();}
}
H5lock.prototype.drawStatusPoint = function(type) { // 初始化状态线条
for (var i = 0 ; i < this.lastPoint.length ; i++) {
this.ctx.strokeStyle = type;
this.ctx.beginPath();
this.ctx.arc(this.lastPoint[i].x, this.lastPoint[i].y, this.r, 0, Math.PI * 2, true);
this.ctx.closePath();
this.ctx.stroke();}
}
H5lock.prototype.drawLine = function(style, po, lastPoint) {//style: 色彩 解锁轨迹
this.ctx.beginPath();
this.ctx.strokeStyle = style;
this.ctx.lineWidth = 3;
this.ctx.moveTo(this.lastPoint[0].x, this.lastPoint[0].y);
for (var i = 1 ; i < this.lastPoint.length ; i++) {this.ctx.lineTo(this.lastPoint[i].x, this.lastPoint[i].y);
}
this.ctx.lineTo(po.x, po.y);
this.ctx.stroke();
this.ctx.closePath();}
H5lock.prototype.createCircle = function() {// 创立解锁点的坐标,依据 canvas 的大小来平均分配半径
var n = this.chooseType;
var count = 0;
this.r = this.ctx.canvas.width / (1 + 4 * n);// 公式计算
this.lastPoint = [];
this.arr = [];
this.restPoint = [];
var r = this.r;
for (var i = 0 ; i < n ; i++) {for (var j = 0 ; j < n ; j++) {
count++;
var obj = {
x: j * 4 * r + 3 * r,
y: i * 4 * r + 3 * r,
index: count
};
this.arr.push(obj);
this.restPoint.push(obj);
}
}
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
for (var i = 0 ; i < this.arr.length ; i++) {this.drawCle(this.arr[i].x, this.arr[i].y);
}
//return arr;
}
H5lock.prototype.getPosition = function(e) {// 获取 touch 点绝对于 canvas 的坐标
var rect = e.currentTarget.getBoundingClientRect();
var po = {x: (e.touches[0].clientX - rect.left)*this.devicePixelRatio,
y: (e.touches[0].clientY - rect.top)*this.devicePixelRatio
};
return po;
}
H5lock.prototype.update = function(po) {// 外围变换办法在 touchmove 时候调用
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
for (var i = 0 ; i < this.arr.length ; i++) { // 每帧先把面板画进去
this.drawCle(this.arr[i].x, this.arr[i].y);
}
this.drawPoint('#27AED5');// 每帧花轨迹
this.drawStatusPoint('#27AED5');// 每帧花轨迹
this.drawLine('#27AED5',po , this.lastPoint);// 每帧画圆心
// if (this.lastPoint.length == 4) {
// // debugger
// }
for (var i = 0 ; i < this.restPoint.length ; i++) {if (Math.abs(po.x - this.restPoint[i].x) < this.r && Math.abs(po.y - this.restPoint[i].y) < this.r) {this.drawPoint(this.restPoint[i].x, this.restPoint[i].y);
this.lastPoint.push(this.restPoint[i]);
this.restPoint.splice(i, 1);
break;
}
}
}
H5lock.prototype.checkPass = function(psw1, psw2) {// 检测明码
var p1 = '',
p2 = '';
for (var i = 0 ; i < psw1.length ; i++) {p1 += psw1[i].index + psw1[i].index;
}
for (var i = 0 ; i < psw2.length ; i++) {p2 += psw2[i].index + psw2[i].index;
}
return p1 === p2;
}
H5lock.prototype.storePass = function(psw) {// touchend 完结之后对明码和状态的解决
if (this.pswObj.step == 1) {if (this.checkPass(this.pswObj.fpassword, psw)) {
this.pswObj.step = 2;
this.pswObj.spassword = psw;
document.getElementById('title').innerHTML = '明码保留胜利';
this.drawStatusPoint('#2CFF26');
this.drawPoint('#2CFF26');
window.localStorage.setItem('passwordxx', JSON.stringify(this.pswObj.spassword));
window.localStorage.setItem('chooseType', this.chooseType);
} else {document.getElementById('title').innerHTML = '两次不统一,从新输出';
this.drawStatusPoint('red');
this.drawPoint('red');
delete this.pswObj.step;
}
} else if (this.pswObj.step == 2) {if (this.checkPass(this.pswObj.spassword, psw)) {var title = document.getElementById("title");
title.style.color = "#2CFF26";
title.innerHTML = '解锁胜利';
this.drawStatusPoint('#2CFF26');// 小点点外圈高亮
this.drawPoint('#2CFF26');
this.drawLine('#2CFF26',this.lastPoint[this.lastPoint.length-1] , this.lastPoint);// 每帧画圆心
api.closeFrame();} else if (psw.length < 4) {this.drawStatusPoint('red');
this.drawPoint('red');
this.drawLine('red',this.lastPoint[this.lastPoint.length-1] , this.lastPoint);// 每帧画圆心
var title = document.getElementById("title");
title.style.color = "red";
title.innerHTML = '请连贯 4 个点';
} else {this.drawStatusPoint('red');
this.drawPoint('red');
this.drawLine('red',this.lastPoint[this.lastPoint.length-1] , this.lastPoint);// 每帧画圆心
var title = document.getElementById("title");
title.style.color = "red";
title.innerHTML = '手势明码谬误,请重试';
}
} else {
this.pswObj.step = 1;
this.pswObj.fpassword = psw;
document.getElementById('title').innerHTML = '再次输出';
}
}
H5lock.prototype.makeState = function() {if (this.pswObj.step == 2) {// document.getElementById('updatePassword').style.display = 'block';
//document.getElementById('chooseType').style.display = 'none';
var title = document.getElementById("title");
title.style.color = "#87888a";
title.innerHTML = '请解锁';
} else if (this.pswObj.step == 1) {//document.getElementById('chooseType').style.display = 'none';
// document.getElementById('updatePassword').style.display = 'none';
} else {// document.getElementById('updatePassword').style.display = 'none';
//document.getElementById('chooseType').style.display = 'block';
}
}
H5lock.prototype.setChooseType = function(type){
chooseType = type;
init();}
H5lock.prototype.updatePassword = function(){window.localStorage.removeItem('passwordxx');
window.localStorage.removeItem('chooseType');
this.pswObj = {};
document.getElementById('title').innerHTML = '绘制解锁图案';
this.reset();}
H5lock.prototype.initDom = function(){var wrap = document.createElement('div');
var str = '<h4 id="title"class="title"style="color:#87888a"> 请绘制您的图形明码 </h4>';
wrap.setAttribute('style','position: absolute;top:0;left:0;right:0;bottom:0;');
var canvas = document.createElement('canvas');
canvas.setAttribute('id','canvas');
canvas.style.cssText = 'background-color: #000;display: inline-block;margin-top: 76px;';
wrap.innerHTML = str;
wrap.appendChild(canvas);
var width = this.width || 320;
var height = this.height || 320;
document.body.appendChild(wrap);
// 高清屏锁放
canvas.style.width = width + "px";
canvas.style.height = height + "px";
canvas.height = height * this.devicePixelRatio;
canvas.width = width * this.devicePixelRatio;
}
H5lock.prototype.init = function() {this.initDom();
this.pswObj = window.localStorage.getItem('passwordxx') ? {
step: 2,
spassword: JSON.parse(window.localStorage.getItem('passwordxx'))
} : {};
this.lastPoint = [];
this.makeState();
this.touchFlag = false;
this.canvas = document.getElementById('canvas');
this.ctx = this.canvas.getContext('2d');
this.createCircle();
this.bindEvent();}
H5lock.prototype.reset = function() {this.makeState();
this.createCircle();}
H5lock.prototype.bindEvent = function() {
var self = this;
this.canvas.addEventListener("touchstart", function (e) {e.preventDefault();// 某些 android 的 touchmove 不宜触发 所以减少此行代码
var po = self.getPosition(e);
for (var i = 0 ; i < self.arr.length ; i++) {if (Math.abs(po.x - self.arr[i].x) < self.r && Math.abs(po.y - self.arr[i].y) < self.r) {
self.touchFlag = true;
self.drawPoint(self.arr[i].x,self.arr[i].y);
self.lastPoint.push(self.arr[i]);
self.restPoint.splice(i,1);
break;
}
}
}, false);
this.canvas.addEventListener("touchmove", function (e) {if (self.touchFlag) {self.update(self.getPosition(e));
}
}, false);
this.canvas.addEventListener("touchend", function (e) {if (self.touchFlag) {
self.touchFlag = false;
self.storePass(self.lastPoint);
setTimeout(function(){self.reset();
}, 1000);
}
}, false);
// document.getElementById('updatePassword').addEventListener('click', function(){// self.updatePassword();
// });
}
})();
8、设置手势明码
登录胜利之后,在集体核心来设置手势明码。可通过参数设置来初始化明码强度。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>H5lock</title>
<style type="text/css">
body {
text-align: center;
background-color: #000000;
}
.title {
/*color: #87888a;*/
margin-top: 85px;
font-size: 20px;
font-weight:lighter;
}
.reset{
position: relative;
top: 200px;
font-size: 20px;
text-align: center;
}
</style>
</head>
<body>
<script type="text/javascript" src="../script/setH5lock.js"></script>
<script type="text/javascript">
var opt = {
chooseType: 3, // 3 , 4 , 5,
width: 300, // lock wrap width
height: 300, // lock wrap height
container: 'element', // the id attribute of element
inputEnd: function (psw){} // when draw end param is password string}
var lock = new H5lock(opt);
lock.init();
</script>
</body>
</html>
9、批改明码
零碎默认设定了用户的初始密码,用户登录零碎后会提醒进行明码批改,批改后的明码进行了 MD5 加密,因为没有后盾零碎,所以明码的 MD5 加密,采纳了 JS 来进行加密。通过开发工具调试控制台装置 js 插件
装置胜利之后会在文件目录中显示
而后在用的中央间接引入即可。
import $md5 from '../../node_modules/js-md5/build/md5.min.js'
var db = api.require('db');
db.executeSql({
name: 'doc',
sql: "update user set password ='"+ md5(ret.text) +"'where id = 1"
}, (ret, err)=> {// console.log(JSON.stringify(ret));
// console.log(JSON.stringify(err));
if (ret.status) {
api.alert({
title: '音讯揭示',
msg: '明码批改胜利,请从新登陆',
}, (ret, err) => {
// 革除用户信息
api.removePrefs({key: 'username'});
api.removePrefs({key: 'userid'});
api.removePrefs({key: 'password'});
$util.openWin({
name: 'login',
url: '../main/login.stml',
title: '',
hideNavigationBar:true
});
});
} else {
api.toast({msg:JSON.stringify(err)
})
}
});
10、封装工具类插件 utils.js
在须要用到插件中通用办法的中央,间接援用即可。
import $util from "../../utils/utils.js"
const $util = {openWin(param){
var param = {
name: param.name,
url: param.url,
title: param.title||'',
pageParam: param.pageParam||{},
hideNavigationBar: param.hideNavigationBar || false,
navigationBar:{
background:'#1492ff',
shadow: '#fff',
color: '#fff'
}
};
if (this.isApp()) {api.openTabLayout(param);
} else {api.openWin(param);
}
},
isApp(){if (api.platform && api.platform == 'app') {return true;}
return false;
},
fitRichText(richtext, width){var str = `<img style="max-width:${width}px;"`;
var result = richtext.replace(/\<img/gi, str);
return result;
},
isLogin(){if(api.getPrefs({sync: true,key: 'userid'})){return true;}
return false;
},
openDataBase(){var fs = api.require('fs');
var db = api.require('db');
db.subfile({directory:'fs://db'}, (ret, err)=> {if(ret.status){
// 关上数据库
db.openDatabase({
name: 'doc',
path: ret.files[0]
}, (ret, err)=> {if (ret.status) {
// api.toast({
// msg:'关上数据库胜利!'
// })
} else {
api.toast({msg:JSON.stringify(err)
})
}
});
}
else{
// 拷贝文件
fs.copyTo({
oldPath: 'widget://doc.db',
newPath: 'fs://db/'
}, function(ret, err) {if (ret.status) {
db.subfile({directory:'fs://db'}, (ret, err)=> {if(ret.status){
// 关上数据库
db.openDatabase({
name: 'doc',
path: ret.files[0]
}, (ret, err)=> {if (ret.status) { } else {
api.toast({msg:JSON.stringify(err)
})
}
});
}
else{
api.toast({msg:JSON.stringify(err)
})
}
})
} else {
api.toast({msg:JSON.stringify(err)
})
}
});
}
})
}
}
export default $util;
11、用户性能权限
零碎分为 2 级用户,管理员账号和领导账号。通过角色 ID 进行辨别,管理员账号有信息的增删改查性能,领导账号只有信息的查问性能。
用于登录胜利之后将用户信息进行缓存。
// 登陆 APP
submit() {api.showProgress();
// console.log("select id,username,password from user where username ='"+this.data.user+"'and password ='"+md5(this.data.psw)+"'");
var db = api.require('db');
db.selectSql({
name: 'doc',
sql: "select id,username,password,role from user where username ='"+this.data.user+"'and password ='"+md5(this.data.psw)+"'"
}, (ret, err)=> {// console.log(JSON.stringify(ret));
// console.log(JSON.stringify(err));
if (ret.status) {if(ret.data.length==1){api.setPrefs({key:'username',value:ret.data[0].username});
api.setPrefs({key:'userid',value:ret.data[0].id});
api.setPrefs({key:'password',value:ret.data[0].password});
api.setPrefs({key:'role',value:ret.data[0].role});
api.sendEvent({name: 'loginsuccess',});
api.closeWin();}
else{
api.toast({msg:'登陆失败,请输出正确的用户名和明码'})
}
} else {
api.toast({msg:JSON.stringify(err)
})
}
api.hideProgress();});
}
在须要验证用户权限的中央通过获取角色 ID,进行逻辑判断。
if(api.getPrefs({sync: true,key: 'role'})=='99'){
// 增加编辑按钮
api.setNavBarAttr({
rightButtons: [{text: '编辑'}]
});
// 监听右上角按钮点击事件
api.addEventListener({name: 'navitembtn'}, (ret, err) => {if (ret.type == 'right') {
$util.openWin({
name: 'personedit',
url: 'personedit.stml',
title: '人员信息编辑',
pageParam:{id:this.data.id}
});
}
});
}
else{
// 增加朗诵按钮
api.setNavBarAttr({
rightButtons: [{text: '朗诵'}]
});
// 监听右上角按钮点击事件
api.addEventListener({name: 'navitembtn'}, (ret, err) => {// console.log(JSON.stringify(this.data.info));
if (ret.type == 'right') {var IFlyVoice = api.require('IFlyVoice');
IFlyVoice.initSpeechSynthesizer((ret)=>{// console.log(JSON.stringify(ret));
});
IFlyVoice.startSynthetic({
text:this.data.info,
commonPath_Android:'widget://res/android/common.jet',
pronouncePath_Android:'widget://res/android/xiaoyan.jet',
pronounceName:'xiaoyan',
speed:40
},(ret,err)=>{// console.log(JSON.stringify(ret));
// console.log(JSON.stringify(err));
if (ret.status) {// console.log('合成胜利');
} else {// console.log(JSON.stringify(err));
}
});
}
});
}
12、双击退出应用程序
利用如果不做任何解决,在利用初始页面登程 keyback 事件,会弹出提示框提醒是否退出程序,体验感极差。针对此进行了优化,因为利用首页采纳了 tablayout,所以只须要在 tablayout 默认选中项的索引页面中增加双击 keyback 事件的监听,并通过 api.toast 进行提醒。
在登录页面也须要增加此监听,应为用户退出登录之后,会主动跳转至登录页,如果不做解决,用户点击物理返回键就会导致用户在没有登录的状况下跳回上一页。加了此监听事件用户点击返回键不做解决,双击会提醒退出程序。
// 监听返回 双击退出程序
api.setPrefs({
key: 'time_last',
value: '0'
});
api.addEventListener({name : 'keyback'}, (ret, err) => {var time_last = api.getPrefs({sync: true,key: 'time_last'});
var time_now = Date.parse(new Date());
if (time_now - time_last > 2000) {api.setPrefs({key:'time_last',value:time_now});
api.toast({
msg : '再按一次退出 APP',
duration : 2000,
location : 'bottom'
});
} else {
api.closeWidget({silent : true});
}
});
13、利用动静权限
安卓 10 之后,对利用的权限要求进步,不在像老版本一样配置上就会主动获取,必须进行提醒。
根据官网给出的教程进行了动静权限的设置。
增加 mianfest.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<application name="targetSdkVersion" value="28"/>
</manifest>
在零碎索引页进行动静权限获取揭示,本零碎只波及到了文件存储权限的获取,如须要获取多个权限,在 List[] 数组中持续增加须要的权限,而后依据增加的权限个数,做相应的几个判断即可。
let limits=[];
// 获取权限
var resultList = api.hasPermission({list: ['storage']
});
if (resultList[0].granted) {// 已受权,能够持续下一步操作} else {limits.push(resultList[0].name);
}
if(limits.length>0){
api.requestPermission({list: limits,}, (res) => {});
}