准备工作

资源

预装工具

安装bower

 npm install -g bower

安装ngCordova

bower install ngCordova

(*由于网络获取资源的原因,后面几次建项目后都无法下载到,自己便复制了原来的ngCordova目录(到YourProject\wwww\lib目录下),发现也是可以使用的)

下载好后,在项目的index.hmtl进行引用:

<script src="lib/ngCordova/dist/ng-cordova.js">

日历工具

FullCalendar

安装插件

本项目需要(安装)的插件有:

插件名说明扩展阅读
cordova-plugin-x-toast消息提示,使用方法如:$cordovaToast.showShortBottom('屏幕下方提示');
(*仅限平台运行,浏览器调试无效,所以在PC调试时应注意其引起的错误而导致后面代码没执行)
cordova ionic消息提示
cordova-sqlite-storagesqlite数据库 cordova调用本地SQLite数据库的方法

more...
cordova-plugin-x-socialsharing内容分享 

插件的安装基本命令是:

 cordova plugin add XXXX

安装好后可在YourProject\wwww\lib目录下看到新增的插件目录,这样就可以在项目中引用了(不用使用<script src="xxx">)。
在生成platform后,或需再用

cordova prepare

该命令用以复制文件到平台(并更改一些xml文件的内容)

概念理解

service服务

AngularJS服务是一种单例对象,其主要功能是为实现应用的功能提供数据和对象,通过直接调用服务,可以将复杂的应用功能进行简化或分块化。 按功能的不同,分为内置服务和自定义服务。

AngularJS提供的常用内置服务有:$scope、$http、$window、$location等

自定义服务主要包含以下两种:
1)使用内置的$provide服务
2)调用模块中的服务注册(如factory、service、constant、value等方法)

本项目主要采用service来创建服务(service方法与factory不同的是,它可以接收一个构造函数)

设计与开发

app.js

 angular.module('pdm'
, ['ionic'
, 'ngCordova'
])
.config(function ($stateProvider, $urlRouterProvider, $ionicConfigProvider) { //在android下,tab位置为top,如果想修改其位置在底部,加上下面一句代码:
$ionicConfigProvider.tabs.position('bottom'); //... })
.run(function ($ionicPlatform) {
//...
}) // 自定义服务:$alertPopup
.service('$alertPopup',
['$ionicPopup'
, function ($ionicPopup) {
return function (content, title) {
if (title == undefined || title == null)title = '提示';
var alertPopup = $ionicPopup.alert({
title: title,
template: content
}); alertPopup.then(function (res) {
log('alertPopup.then: ' + res);
});
}
}]) // 自定义服务:$db
.service('$db', ['$cordovaSQLite', '$alertPopup', '$cordovaToast'
, function ($cordovaSQLite, $alertPopup, $cordovaToast) {
// 初始化数据表
var db = null;
try {
var _dbName = 'sk';
if (!(window.cordova && window.SQLitePlugin)) {
// 创建数据库对象
db = window.openDatabase(_dbName, '1.0', _dbName, 100 * 1024 * 1024); // web-sql 执行sql方式
// 首次创建记账表
db.transaction(
function (transaction) {
transaction.executeSql("CREATE TABLE IF NOT EXISTS Finacial_KeepAccount " +
"( id integer primary key" +
", account text " +
", SuitType text " +
", ItemText text " +
", MoneyFlowDirect text " +
", Cash REAL " +
", AccountType text " +
", RecordDate text " +
", Remark text" +
")");
}
); // 自定义执行sql方式
// 首次创建日常表
$cordovaSQLite.execute(db, 'CREATE TABLE IF NOT EXISTS Life_DailyActivity(id integer primary key' +
', account text' +
', Date text' +
', Business text' +
', Study text' +
', Health text' +
', Sport text' +
', Others text' +
', Remark text' +
')'); }
else {
$alertPopup('fail create ' + _dbName + '.db');
}
} catch (e) {
$alertPopup('fail init: ' + e.toString(), '$db Err');
} // 内部函数
function db_exec(sql, param, succ_callback, err_callback){
if (param == undefined || param == null) param = [];
$cordovaSQLite.execute(db, sql, param)
.then(function (rst) {
if (succ_callback == undefined)log('exec: ' + sql);
else succ_callback(rst);
}, function (err) {
if (err_callback == undefined)$alertPopup('exec error: ' + err.message);
else err_callback(err);
});
} // 外部可调用接口
return {
// 执行sql
_exec: function (sql, param, succ_callback, err_callback) {
db_exec(sql, param, succ_callback, err_callback);
},
// 获取数据
get: function (tbl, cndt, callback) {
var sql = 'SELECT * FROM ' + tbl + ' WHERE 1=1 ';
if (cndt != undefined && cndt != '')sql += (' AND ' + cndt);
db_exec(sql, [],
function (rst) {
var data = [];
for (var i = 0; i < rst.rows.length; i++) data.push(rst.rows.item(i));
callback(data);
});
},
// 添加
add: function (tbl, fields, valueArr, silenceExec) {
var _param = '';
for (var i = 0; i < fields.split(',').length; i++)_param += ',?';
_param = _param.substr(1);
var sql = 'INSERT INTO ' + tbl + '(' + fields + ') values(' + _param + ')';
db_exec(sql, valueArr,
function (rst) {
if (silenceExec == undefined || silenceExec != true)
if(!g_debug)
$cordovaToast.showShortCenter('add to ' + tbl + ' success');
});
},
// 更新
update: function (tbl, fields, valueArr, cndt, silenceExec) {
var fv = '';
var flds = fields.split(',');
for (var i = 0; i < flds.length; i++) fv += (', ' + flds[i] + '=? ');
fv = fv.substr(1);
var sql = 'UPDATE ' + tbl + ' SET ' + fv + ' WHERE ' + cndt;
db_exec(sql, valueArr,
function (rst) {
if (silenceExec == undefined || silenceExec != true)
if(!g_debug)
$cordovaToast.showShortCenter('update ' + tbl + ' success');
});
},
// 删除
delete: function (tbl, cndt, silenceExec) {
var sql = 'DELETE FROM ' + tbl + ' WHERE ' + cndt;
db_exec(sql, [],
function (rst) {
if (silenceExec == undefined || silenceExec != true)
if(!g_debug)
$cordovaToast.showShortCenter('delete from ' + tbl + ' success');
});
} }
}]) ;

自定义服务:$alertPopup

为方便项目内调用,对$ionicPopup进行封装,也方便日后扩展。

自定义服务:$db

此$db服务基本就是一个DAL层了,封装了基本的CRUD功能,并根据项目需要做了一些“默认处理”(在程序初始化时,自动创建记账和日常表等)。
(*这个sqlite文件物理路径很难找,有什么方法可以快速定位,还望知道的园友赐教:))

记账视图

HTML部分

 <ion-view view-title="DailyKeeper">
<ion-nav-title><b>记账</b></ion-nav-title>
<div class="bar bar-subheader bar-dark">
<h2 class="title">
<a class="button button-icon icon ion-plus-circled" ng-click="showDetail()"></a>
</h2>
</div> <ion-content class="has-tabs has-subheader">
<ion-list>
<div ng-repeat="da in dailyAccount">
<div class="item item-divider" style="display: {{da.ext_displayDivider}}">
{{da.RecordDate}}
</div>
<ion-item class="item-remove-animate item-icon-right"
type="item-text-wrap" ng-click="showDetail({{da}})" style="color: {{da.ext_TextColor}}">
【{{da.SuitType}}】{{da.ItemText}}
<br/>{{da.Cash}}
<i class="icon ion-chevron-right icon-accessory"></i> <ion-option-button class="button-assertive" ng-click="remove(da)">
Delete
</ion-option-button>
</ion-item>
</div>
</ion-list>
</ion-content> <!--弹出内容-->
<script id="detail.html" type="text/ng-template">
<ion-modal-view>
<ion-header-bar>
<h1 class="title">{{currDA.title}}</h1>
<button class="button" ng-click="closeDetail()">关闭</button>
</ion-header-bar>
<ion-content>
<div class="item-input-inset">
<i class="icon ion-android-calendar"></i>&nbsp;
<input type="date" ng-model="currDA.RecordDate">
</div>
<div class="item item-input-inset">
<select
ng-model="currDA.SuitType"
ng-options="value.SuitType as value.SuitType group by value.MainClass for value in Finacial_SuitClass">
<option value=""> -账目类型- </option>
</select>&nbsp;
<label class="item-input-wrapper">
<input type="text" ng-model="currDA.ItemText" placeholder="消费项">
</label>
</div>
<div class="item item-input-inset">
<label class="item-input-wrapper">
<input type="number" ng-model="currDA.Cash" placeholder="金额">
</label>&nbsp;
<label class="toggle">
<input type="checkbox" ng-model="currDA.Income">
<div class="track">
<div class="handle"></div>
</div>
</label>(入账)
</div>
<div class="item item-input-inset">
<textarea style="width: 100%" ng-model="currDA.Remark" placeholder="备注"></textarea>
</div>
<div class="item-input-inset">
<button class="button button-block button-positive" ng-click="save()">
Save
</button>
</div>
</ion-content>
</ion-modal-view>
</script>
</ion-view>

JavaScript部分

 angular.module('pdm')
.controller('Ctrl_DailyKeeper',
['$scope', '$ionicModal', '$db', '$cordovaToast', '$ionicPopup', '$alertPopup'
, function ($scope, $ionicModal, $db, $cordovaToast, $ionicPopup, $alertPopup) { // BLL
$scope.getKA = function (callback, cndt) {
var sql = "SELECT * FROM Finacial_KeepAccount WHERE 1=1 ";
if (cndt != undefined && cndt != '')sql += (' AND ' + cndt);
sql += ' ORDER BY RecordDate desc';
$db._exec(sql, [], function (rst) {
if (rst.rows.length == 0) {
if(!g_debug)
$cordovaToast.showShortCenter('load ka success but no data');
//return;
}
var data = [];
for (var i = 0; i < rst.rows.length; i++) data.push(rst.rows.item(i));
callback(data);
});
};
$scope.addKA = function (SuitType, ItemText, MoneyFlowDirect, Cash, AccountType, RecordDate, Remark) {
$db.add('Finacial_KeepAccount'
, 'SuitType,ItemText,MoneyFlowDirect,Cash,AccountType,RecordDate,Remark, account'
, [SuitType, ItemText, MoneyFlowDirect, Cash, AccountType, RecordDate, Remark, g_user]);
};
$scope.updateKA = function (id, SuitType, ItemText, MoneyFlowDirect, Cash, AccountType, RecordDate, Remark) {
$db.update('Finacial_KeepAccount'
, 'SuitType,ItemText,MoneyFlowDirect,Cash,AccountType,RecordDate,Remark'
, [SuitType, ItemText, MoneyFlowDirect, Cash, AccountType, RecordDate, Remark]
, 'id=' + id.toString());
};
$scope.deleteKA = function (id) {
$db.delete('Finacial_KeepAccount', 'id=' + id.toString());
}; $scope.Finacial_SuitClass = [
{MainClass: '基本生活', SuitType: '餐饮饮食'}
, {MainClass: '基本生活', SuitType: '柴米油盐'}
, {MainClass: '美容化妆', SuitType: '服饰装扮'}
, {MainClass: '收入', SuitType: '福利津贴'}
, {MainClass: '收入', SuitType: '工资'}
, {MainClass: '美容化妆', SuitType: '化妆品美容'}
, {MainClass: '交通通讯', SuitType: '话费网费'}
, {MainClass: '交通通讯', SuitType: '交通费'}
, {MainClass: '人情往来', SuitType: '借出'}
, {MainClass: '投资', SuitType: '理财投资'}
, {MainClass: '文化娱乐', SuitType: '旅游娱乐'}
, {MainClass: '收入', SuitType: '其他收入'}
, {MainClass: '其他支出', SuitType: '其他支出'}
, {MainClass: '人情往来', SuitType: '人际往来'}
, {MainClass: '基本生活', SuitType: '日常用品'}
, {MainClass: '文化娱乐', SuitType: '书报音像'}
, {MainClass: '文化娱乐', SuitType: '数码产品'}
, {MainClass: '基本生活', SuitType: '水果零食'}
, {MainClass: '基本生活', SuitType: '物业水电'}
, {MainClass: '人情往来', SuitType: '孝敬长辈'}
, {MainClass: '基本生活', SuitType: '医药保健'}
, {MainClass: '文化娱乐', SuitType: '运动健身'}
];
$scope.currDA = {
title: '新增'
, id: 0
, RecordDate: new Date()
, SuitType: ''
, ItemText: ''
, Cash: 0
, Income: false
, Remark: ''
} $scope.arrageData = function () {
var _data = $scope.dailyAccount; if (_data.length > 0) {
_data[0].ext_displayDivider = ''; if (_data.length > 1) {
var lastDA = _data[0];
for (var i = 1; i < _data.length; i++) {
_data[i].ext_displayDivider = 'none';
if (new Date(_data[i].RecordDate) < new Date(lastDA.RecordDate)) {
_data[i].ext_displayDivider = '';
lastDA = _data[i];
}
}
}
}
}; $scope.remove = function (da) {
$ionicPopup.confirm({
title: 'Confrim',
template: 'Do you really want to delete?',
scope: $scope,
buttons: [
{
text: '<b>Yes</b>',
type: 'button-positive',
onTap: function (e) {
//$scope.dailyAccount.splice($scope.dailyAccount.indexOf(da), 1);
$scope.deleteKA(da.id);
$scope.loadDate();
}
},
{
type: 'button-canceldark',
text: '<b>Cancel</b>',
onTap: function (e) {
console.log('cancel delete');
}
}
]
});
}; $scope.showDetail = function (da) {
if (da == undefined) {
// 新增
$scope.currDA.title = '新增'; $scope.currDA.id = 0;
$scope.currDA.RecordDate = new Date();
$scope.currDA.SuitType = '';
$scope.currDA.ItemText = '';
$scope.currDA.Cash = 0;
$scope.currDA.Income = false;
$scope.currDA.Remark = '';
} else {
// 读取
$scope.currDA.title = '编辑'; $scope.getKA(function (data) {
if (data.length > 0) {
var item = data[0]; $scope.currDA.id = item.id;
$scope.currDA.RecordDate = new Date(item.RecordDate);
$scope.currDA.SuitType = item.SuitType;
$scope.currDA.ItemText = item.ItemText;
$scope.currDA.Cash = item.Cash;
$scope.currDA.Income = (item.MoneyFlowDirect == '入账');
$scope.currDA.Remark = item.Remark;
}
}
, ' id = ' + da.id);
} $scope.openModal();
} $scope.save = function () {
//log(angular.toJson($scope.currDA)); if ($scope.currDA.SuitType == ''
|| $scope.currDA.SuitType.indexOf('账目类型') >= 0) {
$alertPopup('账目类型没有选定哦');
return;
} var _moneyFlowDirection = '出账';
if ($scope.currDA.Income) _moneyFlowDirection = '入账'; if ($scope.currDA.id == 0) {
// 新增
$scope.addKA(
$scope.currDA.SuitType
, $scope.currDA.ItemText
, _moneyFlowDirection
, $scope.currDA.Cash
, '我的钱包'
, dateFormat($scope.currDA.RecordDate, 'ymd')
, $scope.currDA.Remark
);
$scope.closeDetail();
$scope.loadDate();
}
else {
// 更新
$ionicPopup.confirm({
title: 'Confrim',
template: 'Do you really want to update?',
scope: $scope,
buttons: [
{
text: '<b>Yes</b>',
type: 'button-positive',
onTap: function (e) {
$scope.updateKA(
$scope.currDA.id
, $scope.currDA.SuitType
, $scope.currDA.ItemText
, _moneyFlowDirection
, $scope.currDA.Cash
, '我的钱包'
, dateFormat($scope.currDA.RecordDate, 'ymd')
, $scope.currDA.Remark
);
$scope.closeDetail();
$scope.loadDate();
}
},
{
type: 'button-canceldark',
text: '<b>Cancel</b>',
onTap: function (e) {
console.log('cancel update');
}
}
]
});
}
} // 弹窗
$ionicModal.fromTemplateUrl('detail.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function (modal) {
$scope.modal = modal;
});
$scope.openModal = function () {
$scope.modal.show();
};
$scope.closeDetail = function () {
$scope.modal.hide();
}
$scope.$on('$destroy', function () {
$scope.modal.remove();
}); $scope.loadDate = function () {
$scope.getKA(function (data) {
for (var i = 0; i < data.length; i++) {
var _d = data[i];
_d['ext_displayDivider'] = 'none';
_d['ext_TextColor'] = 'black';
if (_d.MoneyFlowDirect == '入账')_d['ext_TextColor'] = 'blue';
}
$scope.dailyAccount = data;
$scope.arrageData();
});
} // start
$scope.loadDate(); }])
;

说明:

  • arrageData()函数根据(按日期倒序)排序好的数据,设置当日最后一条数据(因为是倒序,所以采用最后一条)的ext_displayDivider属性为none,如此实现在“日期-当日各项收支项”的显示效果——按日分割后来发觉也可以用Ionic的Card,当然也许也有第三方控件可以直接用了。
  • $ionicModal调用的弹窗功能,弹出的是一个完整的页面,本项目为了简便,就直接写在了同页面里“< script id="detail.html" type="text/ng-template">”

日常视图

HTML部分

 <ion-view view-title="DailyActivity">
<ion-nav-title><b>日常</b></ion-nav-title>
<ion-content class="has-tabs">
<br/>
<div id='calendar'></div>
</ion-content> <!--弹出内容-->
<script id="detail.html" type="text/ng-template">
<ion-modal-view>
<ion-header-bar>
<h1 class="title">活动 / 计划</h1>
<button class="button" ng-click="closeDetail()">关闭</button>
</ion-header-bar>
<ion-content>
<div class="item-input-inset">
<label class="item-input-wrapper">
<i class="icon ion-android-calendar"></i>&nbsp;
<span>{{act.tDate}}</span>
</label>
&nbsp;
<span>{{act.id}}</span>
&nbsp;
<!--<a class="button button-small" ng-click="save()">Save</a>-->
</div>
<div class="list card">
<div class="item item-divider">
事务
</div>
<div class="item item-body">
<label class="item-input">
<textarea style="background-color: whitesmoke" ng-model="act.Business"></textarea>
</label>
</div>
<div class="item item-divider">
学习
</div>
<div class="item item-body">
<label class="item-input">
<textarea style="background-color: whitesmoke" ng-model="act.Study"></textarea>
</label>
</div>
<div class="item item-divider">
健康
</div>
<div class="item item-body">
<label class="item-input">
<textarea style="background-color: whitesmoke" ng-model="act.Health"></textarea>
</label>
</div>
<div class="item item-divider">
运动
</div>
<div class="item item-body">
<label class="item-input">
<textarea style="background-color: whitesmoke" ng-model="act.Sport"></textarea>
</label>
</div>
<div class="item item-divider">
其他
</div>
<div class="item item-body">
<label class="item-input">
<textarea style="background-color: whitesmoke" ng-model="act.Others"></textarea>
</label>
</div>
</div>
</ion-content>
</ion-modal-view>
</script>
</ion-view>

JavaScript部分

 angular.module('pdm')
.controller('Ctrl_DailyActivity',
['$scope', '$ionicModal', '$db', '$cordovaToast', '$alertPopup'
, function ($scope, $ionicModal, $db, $cordovaToast, $alertPopup) { // BLL
$scope.getDA = function (callback, cndt) {
var sql = "SELECT * FROM Life_DailyActivity WHERE 1=1 ";
if (cndt != undefined && cndt != '')sql += (' AND ' + cndt);
sql += ' ORDER BY Date';
$db._exec(sql, [], function (rst) {
var data = [];
for (var i = 0; i < rst.rows.length; i++) data.push(rst.rows.item(i));
callback(data);
});
};
$scope.addDA = function (Date, Business, Study, Health, Sport, Others, Remark, callback) {
var tbl = 'Life_DailyActivity';
var fields = 'account,Date,Business,Study,Health,Sport,Others,Remark';
var valueArr = [g_user, Date, Business, Study, Health, Sport, Others, Remark];
var _param = '';
for (var i = 0; i < fields.split(',').length; i++)_param += ',?';
_param = _param.substr(1);
var sql = 'INSERT INTO ' + tbl + '(' + fields + ') values(' + _param + ')';
$db._exec(sql, valueArr,
function (rst) {
if (callback != undefined && callback != null) callback(rst);
else $cordovaToast.showShortCenter('add to ' + tbl + ' success');
});
};
$scope.updateDA = function (Date, Business, Study, Health, Sport, Others, Remark) {
$db.update('Life_DailyActivity'
, 'Business,Study,Health,Sport,Others,Remark'
, [Business, Study, Health, Sport, Others, Remark]
, "Date='" + Date + "'"
, true);
} $scope.editing = false;
$scope.act = {
id: 0,
tDate: dateFormat(new Date(), 'ymd'),
Business: '',
Study: '',
Health: '',
Sport: '',
Others: '',
Remark: ''
};
var _lastDate = $scope.act.tDate; $scope.loadData = function () {
$scope.getDA(function (data) {
$scope.act.Business = '';
$scope.act.Study = '';
$scope.act.Health = '';
$scope.act.Sport = '';
$scope.act.Others = '';
$scope.act.Remark = ''; if (data.length > 0) {
var item = data[0];
$scope.act.id = item.id;
$scope.act.Business = item.Business;
$scope.act.Study = item.Study;
$scope.act.Health = item.Health;
$scope.act.Sport = item.Sport;
$scope.act.Others = item.Others;
$scope.act.Remark = item.Remark; if ($scope.act.id > 0) {
$db._exec("delete from Life_DailyActivity where Date='" + $scope.act.tDate + "' and id!=" + $scope.act.id);
}
} else {
$scope.addDA($scope.act.tDate
, $scope.act.Business
, $scope.act.Study
, $scope.act.Health
, $scope.act.Sport
, $scope.act.Others
, $scope.act.Remark
, function (rst) {
$scope.act.id = rst.insertId;
}
);
}
},
"Date='" + $scope.act.tDate + "'");
} $scope.save = function () {
$scope.updateDA($scope.act.tDate
, $scope.act.Business
, $scope.act.Study
, $scope.act.Health
, $scope.act.Sport
, $scope.act.Others
, $scope.act.Remark
);
} // 监听数据变化
$scope.$watch('act.Business', function (newValue, oldValue, scope) {
if ($scope.editing && newValue != oldValue)$scope.save();
});
$scope.$watch('act.Study', function (newValue, oldValue, scope) {
if ($scope.editing && newValue != oldValue)$scope.save();
});
$scope.$watch('act.Health', function (newValue, oldValue, scope) {
if ($scope.editing && newValue != oldValue)$scope.save();
});
$scope.$watch('act.Sport', function (newValue, oldValue, scope) {
if ($scope.editing && newValue != oldValue)$scope.save();
});
$scope.$watch('act.Others', function (newValue, oldValue, scope) {
if ($scope.editing && newValue != oldValue)$scope.save();
}); $scope.initData = function () {
var events_data = []; // 日常
$scope.getDA(function (data) {
var op = [];
op['Business'] = '#387EF5';
op['Study'] = '#FFC900';
op['Health'] = '#EF473A';
op['Sport'] = '#33CD5F';
op['Others'] = '#B2B2B2'; for (var i = 0; i < data.length; i++) {
var dd = data[i];
for (var k in op) {
if (dd[k.toString()] != undefined && dd[k.toString()] != '') {
var item = [];
item['color'] = op[k];
item['title'] = dd[k.toString()].replace('\n','|').substring(0, 10);
item['start'] = new Date(dd['Date']);
events_data.push(item);
}
}
} $('#calendar').fullCalendar('destroy');
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month'//,agendaWeek,agendaDay'
},
firstDay: 1,
events: events_data,
// 点击空白
dayClick: function (date, allDay, jsEvent, view) {
var selDate = $.fullCalendar.formatDate(date, 'yyyy-MM-dd');//格式化日期
$scope.act.tDate = selDate; $scope.loadData();
$scope.openModal();
},
//单击事件项时触发
eventClick: function (calEvent, jsEvent, view) {
$scope.act.tDate = dateFormat(calEvent.start,'ymd'); $scope.loadData();
$scope.openModal();
}
});
});
} // 弹窗
$ionicModal.fromTemplateUrl('detail.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function (modal) {
$scope.modal = modal;
});
$scope.openModal = function () {
$scope.modal.show(); $scope.editing = true;
};
$scope.closeDetail = function () {
$scope.initData();
$scope.editing = false; $scope.modal.hide();
}
$scope.$on('$destroy', function () {
$scope.modal.remove();
}); // start
$scope.initData(); }])
;

说明:

  • 日常数据的录入,采用了“即变即更新”的模式,这里使用$watch函数来监听数据变化。同时为了数据更新功能的便利性,在用户点击某一日弹框时,自动判断当日数据是否存在,不存在则插入空数据。

打包发布

生成Android平台安装包

使用命令:

cordova platform add android
cordova build android

(*注意,如果以上步骤出错,常见原因有:

  • 安装的Android SDK和打包的SDK版本不对,下载相应SDK
  • 环境变量没有配置好
  • 安装最新node.js

*附录

【源码文件】

【APK文件】


作者:Ken

出处:http://www.cnblogs.com/glife/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。


05-02 17:30