Skip to main content

moregeek program

使用APICloud AVM框架开发人事档案管理助手app实战-多极客编程

由于人事档案具有涉密性,所以本应用没有使用后台服务,全部功能都在APP本地实现。

开发工具采用 APICloud Studio3,基于VSCode的(PS:比基于Atom的autio2好用太多);

数据库采用sqllite,没有使用UI框架,个人觉得AVM本身支持的flex布局配合自写CSS样式,完全可以实现市面上所有的UI框架的元素,这个取决于个人功力。


一、项目思维脑图

使用APICloud AVM框架开发人事档案管理助手app实战_多端开发

二、功能介绍

1、人员花名册

2、编制情况

3、个人中心


三、技术要点

手势密码验证,本地数据库操作,语音播报。


用到的模块

使用APICloud AVM框架开发人事档案管理助手app实战_项目实战_02


项目文件目录

使用APICloud AVM框架开发人事档案管理助手app实战_apicloud_03


引用一下官方的关于目录结构的介绍

使用APICloud AVM框架开发人事档案管理助手app实战_app开发_04


四、功能开发详解

1、首页导航

系统首页使用tabLayout,可以将相关参数配置在JSON文件中,再在config.xml中将content的值设置成该JSON文件的路径。如果底部导航没有特殊需求这里强烈建议大家使用tabLayout为APP进行布局,官方已经将各类手机屏幕及不同的分辨率进行了适配,免去了很多关于适配方面的问题。

使用APICloud AVM框架开发人事档案管理助手app实战_apicloud_05

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"
}
]
}
}

使用APICloud AVM框架开发人事档案管理助手app实战_apicloud_06使用APICloud AVM框架开发人事档案管理助手app实战_app开发_07

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>

使用APICloud AVM框架开发人事档案管理助手app实战_项目实战_08

3、表单提交

采用AVM自带的from控件,通过onsubmit进行数据提交


4、头像图片上传及base64转码

由于是本地sqllite数据库,人员头像图片需要转成base64编码存储到数据库中。通过官方模块trans进行图片转码操作。

使用APICloud AVM框架开发人事档案管理助手app实战_前端开发_09

<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:'照片上传失败,请重新选择!'
})
}
});
}
});
});
},

使用APICloud AVM框架开发人事档案管理助手app实战_apicloud_10

5、sqllite数据库 db模块

由于数据库文件需要存储的应用安装文件中,所有需要官方fs模块配合使用来进行数据库文件的操作。

使用APICloud AVM框架开发人事档案管理助手app实战_前端开发_11

使用APICloud AVM框架开发人事档案管理助手app实战_项目实战_12

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)
})
}
});
},

使用APICloud AVM框架开发人事档案管理助手app实战_多端开发_13

6、语音播报功能

采用官方提供的IFLyVoice模块,需要注意的是,基础资源文件和发音人资源文件(.jet文件)需要到科大讯飞开发者平台进行下载导入的项目中。还有对数字的解读不是精确,尤其是年份,最好不要用数字,而是用中文。

使用APICloud AVM框架开发人事档案管理助手app实战_项目实战_14

//添加朗读按钮
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));
}
});
}
});

使用APICloud AVM框架开发人事档案管理助手app实战_apicloud_15使用APICloud AVM框架开发人事档案管理助手app实战_前端开发_16

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();
// });
}
})();

使用APICloud AVM框架开发人事档案管理助手app实战_apicloud_17使用APICloud AVM框架开发人事档案管理助手app实战_apicloud_18

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>

使用APICloud AVM框架开发人事档案管理助手app实战_项目实战_19使用APICloud AVM框架开发人事档案管理助手app实战_多端开发_20

9、修改密码

系统默认设定了用户的初始密码,用户登录系统后会提示进行密码修改,修改后的密码进行了MD5加密,由于没有后台系统,所以密码的MD5加密,采用了JS来进行加密。通过开发工具调试控制台安装js插件

使用APICloud AVM框架开发人事档案管理助手app实战_前端开发_21

安装成功之后会在文件目录中显示

使用APICloud AVM框架开发人事档案管理助手app实战_多端开发_22

然后在用的地方直接引入即可。

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)
})
}
});

使用APICloud AVM框架开发人事档案管理助手app实战_项目实战_23使用APICloud AVM框架开发人事档案管理助手app实战_前端开发_24

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;

使用APICloud AVM框架开发人事档案管理助手app实战_前端开发_25使用APICloud AVM框架开发人事档案管理助手app实战_项目实战_26

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));
}
});
}
});
}

使用APICloud AVM框架开发人事档案管理助手app实战_多端开发_27使用APICloud AVM框架开发人事档案管理助手app实战_项目实战_28

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
});
}
});

使用APICloud AVM框架开发人事档案管理助手app实战_app开发_29

13、应用动态权限

安卓10之后,对应用的权限要求提高,不在像老版本一样配置上就会自动获取,必须进行提示。

依据官方给出的教程进行了动态权限的设置。

添加 mianfest.xml文件

使用APICloud AVM框架开发人事档案管理助手app实战_前端开发_30

<?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) => {

});
}


©著作权归作者所有:来自51CTO博客作者蒙特卡洛法的原创作品,请联系作者获取转载授权,否则将追究法律责任
使用APICloud AVM框架开发人事档案管理助手app实战
https://blog.51cto.com/u_14151679/5172035

Android C++系列:Linux信号(一)-多极客编程

​信号的概念信号的编号kill -lkill -lHUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM URG STOP TSTP CONT CHLD TTIN TTOU IO XCPU XFSZ VTALRM PROF WINCH INFO USR1 USR2信号机制man 7 signalTerm Default

HMS Core定位服务在生活服务类App中可以自动填写收货地址啦-多极客编程

在涉及团购、外卖、快递、家政、物流、搬家等生活服务类的App、小程序中,填写收货地址是用户高频使用的功能。这一功能通常采取让用户手动填写的解决方案,例如上下拉动选择浙江省-->杭州市-->西湖区-->西溪街道,再切换到姓名输入框输入姓名-->电话输入框输入电话等一系列的操作。从中我们不难发现手动输入地址不仅费时费力,而且一不小心还会出现选错地址的现象。那有没有什么方法能帮助

Android C++系列:Linux信号(三)-多极客编程

​可重入函数不含全局变量和静态变量是可重入函数的一个要素可重入函数见man 7 signal在信号捕捉函数里应使用可重入函数在信号捕捉函数里禁止调用不可重入函数例如:strtok就是一个不可重入函数,因为strtok内部维护了一个内部静态指针,保存上一 次切割到的位置,如果信号的捕捉函数中也去调用strtok函数,则会造成切割字符串混乱, 应用strtok_r版本,r表示可重入。信号引起的竞态和异

前端新手快速上手APICloud App开发-多极客编程

之前因为毕设的原因接触到了APICloud这个平台,后来又研究了一下如何从0到1的开发一款app,今天就简单给大家演示一下使用APICloud制作一个app的全流程。首先​需要到APICloud官网进行注册,然后​下载好官方的开发工具APICloud Studio3,这个开发工具和APICloud官网的控制台是同步的,同一个账号下,在官网的控制台里和开发工具中都可以简单的管理自己的应用,还是很方便

【JVM】10道不得不会的JVM面试题-多极客编程

我是JavaPub,专注于面试、副业,技术人的成长记录。 以下是 JVM 面试题,相信大家都会有种及眼熟又陌生的感觉、看过可能在短暂的面试后又马上忘记了。JavaPub在这里整理这些容易忘记的重点知识及解答,建议收藏,经常温习查阅。 评论区见 @[toc] JVM 基于JDK8 1. 说一说JVM的主要组成部分 点击放大看,一图胜千文 方法区和堆是所有线程共享的内存区域;而虚拟机栈、本地方法栈和

基于 Serverless 架构的头像漫画风处理小程序-多极客编程

前言我一直都想要有一个漫画版的头像,奈何手太笨,用了很多软件 “捏不出来”,所以就在想着,是否可以基于 AI 实现这样一个功能,并部署到 Serverless 架构上让更多人来尝试使用呢?后端项目后端项目采用业界鼎鼎有名的动漫风格转化滤镜库 AnimeGAN 的 v2 版本,效果大概如下:关于这个模型的具体的信息,在这里不做详细的介绍和说明。通过与 Python Web 框架结合,将 AI 模型通

使用APICloud AVM多端框架开发app通讯录功能-多极客编程

一、效果展示二、项目结构图三、数据结构图1、​服务端的原始好友数据结构​​​2、​按字母分类排序后的好友数据结构​​​3、​字母导航数据结构​​​四、功能实现的思路​​本项目基于APICloud AVM框架编写,因此思路要转变下比如标签的用法、CSS样式表的写法、项目的目录结构、dom的操作等都不一样了,完全是Vue、React的编程思维。​​​​微信通讯录功能是将所有联系人根据字母首字拼音排序分

使用APICloud开发物流仓储app项目实践-多极客编程

一、前端思维导图二、项目原型图(UI图)初版原型地址​​墨刀原型链接​​ 《外贸类》 ,涉及的页面大概有20+,有兴趣可以去招人做一下UI。三、前端app项目创建1、前端APICloud后台(​​www.apicloud.com/console)创建app,创建混合的开发的app。​​2、下载调试工具自定义loader安装包自定义loader是一个安卓或iOS的安装包,可以装到收集端,从而进行js

使用APICloud平台实现朋友圈功能-多极客编程

 一、效果展示​​二、项目结构图以及用到的模块​三、主要功能 1、下拉刷新上啦加载更多(mescroll.js) 2、点赞评论 3、导航背景透明渐变效果 4、图像预览(UIPhotoViewer) 5、图像压缩 6、定位附近地点(aMap) 7、图像批量上传 四、功能点详解 1、下拉刷新和上拉加载我用的是 ​​mescroll.js​​​(自带图像懒加载,官方网站有详细使用说明文档) 实现的思路是

前端新手快速上手APICloud App开发-多极客编程

之前因为毕设的原因接触到了APICloud这个平台,后来又研究了一下如何从0到1的开发一款app,今天就简单给大家演示一下使用APICloud制作一个app的全流程。首先​需要到APICloud官网进行注册,然后​下载好官方的开发工具APICloud Studio3,这个开发工具和APICloud官网的控制台是同步的,同一个账号下,在官网的控制台里和开发工具中都可以简单的管理自己的应用,还是很方便

使用APICloud & 科大讯飞SDK快速实现语音识别功能-多极客编程

语音识别功能已经是一个很普及的功能,在特定情境下,能带给人们方便的交互的体验,比如驾驶时使用语音进行唤醒手机,各类智能音响产品,语音控制智能电视等。本文主要介绍在APICloud平台使用科大讯飞的SDK快速实现语音识别功能。一、效果预览二、功能实现在注册好APICloud账号后,进入控制台,添加iflyRecognition模块。iflyRecognition模块封装了科大讯飞的SDK 的语音听写

APP优化及积分榜进阶下篇【MUI+Flask+MongoDB】-多极客编程

@[toc] 一,前言 接上篇。上篇提到:今天又有一个小需求:如下:排行榜可以查看班级排名。需要解决的问题:1,注册时候需要增加班级属性。2,数据库需要一个小的改变。3,为了友好性,我在个人积分榜增加了段位的显示。非常的友好。4,排行榜增加班级排名。 而我就写了前三个需求,今天写一下第四个需求。 二,后端——班级排名 首先,定义一个sum变量,里面放mongo表的所有数据。 list_clss=[