【Ionic+AngularJS 开发】之『个人日常管理』App(二)
准备工作
资源
预装工具
安装bower
npm install -g bower
bower install ngCordova
(*由于网络获取资源的原因,后面几次建项目后都无法下载到,自己便复制了原来的ngCordova目录(到YourProject\wwww\lib目录下),发现也是可以使用的)
下载好后,在项目的index.hmtl进行引用:
<script src="lib/ngCordova/dist/ng-cordova.js">
日历工具
安装插件
本项目需要(安装)的插件有:
插件名 | 说明 | 扩展阅读 |
---|---|---|
cordova-plugin-x-toast | 消息提示,使用方法如:$cordovaToast.showShortBottom('屏幕下方提示'); (*仅限平台运行,浏览器调试无效,所以在PC调试时应注意其引起的错误而导致后面代码没执行) |
cordova ionic消息提示 |
cordova-sqlite-storage | sqlite数据库 |
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
1 angular.module('pdm' 2 , ['ionic' 3 , 'ngCordova' 4 ]) 5 .config(function ($stateProvider, $urlRouterProvider, $ionicConfigProvider) { 6 7 //在android下,tab位置为top,如果想修改其位置在底部,加上下面一句代码: 8 $ionicConfigProvider.tabs.position('bottom'); 9 10 //... 11 12 }) 13 .run(function ($ionicPlatform) { 14 //... 15 }) 16 17 // 自定义服务:$alertPopup 18 .service('$alertPopup', 19 ['$ionicPopup' 20 , function ($ionicPopup) { 21 return function (content, title) { 22 if (title == undefined || title == null)title = '提示'; 23 var alertPopup = $ionicPopup.alert({ 24 title: title, 25 template: content 26 }); 27 28 alertPopup.then(function (res) { 29 log('alertPopup.then: ' + res); 30 }); 31 } 32 }]) 33 34 // 自定义服务:$db 35 .service('$db', ['$cordovaSQLite', '$alertPopup', '$cordovaToast' 36 , function ($cordovaSQLite, $alertPopup, $cordovaToast) { 37 // 初始化数据表 38 var db = null; 39 try { 40 var _dbName = 'sk'; 41 if (!(window.cordova && window.SQLitePlugin)) { 42 // 创建数据库对象 43 db = window.openDatabase(_dbName, '1.0', _dbName, 100 * 1024 * 1024); 44 45 // web-sql 执行sql方式 46 // 首次创建记账表 47 db.transaction( 48 function (transaction) { 49 transaction.executeSql("CREATE TABLE IF NOT EXISTS Finacial_KeepAccount " + 50 "( id integer primary key" + 51 ", account text " + 52 ", SuitType text " + 53 ", ItemText text " + 54 ", MoneyFlowDirect text " + 55 ", Cash REAL " + 56 ", AccountType text " + 57 ", RecordDate text " + 58 ", Remark text" + 59 ")"); 60 } 61 ); 62 63 // 自定义执行sql方式 64 // 首次创建日常表 65 $cordovaSQLite.execute(db, 'CREATE TABLE IF NOT EXISTS Life_DailyActivity(id integer primary key' + 66 ', account text' + 67 ', Date text' + 68 ', Business text' + 69 ', Study text' + 70 ', Health text' + 71 ', Sport text' + 72 ', Others text' + 73 ', Remark text' + 74 ')'); 75 76 } 77 else { 78 $alertPopup('fail create ' + _dbName + '.db'); 79 } 80 } catch (e) { 81 $alertPopup('fail init: ' + e.toString(), '$db Err'); 82 } 83 84 // 内部函数 85 function db_exec(sql, param, succ_callback, err_callback){ 86 if (param == undefined || param == null) param = []; 87 $cordovaSQLite.execute(db, sql, param) 88 .then(function (rst) { 89 if (succ_callback == undefined)log('exec: ' + sql); 90 else succ_callback(rst); 91 }, function (err) { 92 if (err_callback == undefined)$alertPopup('exec error: ' + err.message); 93 else err_callback(err); 94 }); 95 } 96 97 // 外部可调用接口 98 return { 99 // 执行sql 100 _exec: function (sql, param, succ_callback, err_callback) { 101 db_exec(sql, param, succ_callback, err_callback); 102 }, 103 // 获取数据 104 get: function (tbl, cndt, callback) { 105 var sql = 'SELECT * FROM ' + tbl + ' WHERE 1=1 '; 106 if (cndt != undefined && cndt != '')sql += (' AND ' + cndt); 107 db_exec(sql, [], 108 function (rst) { 109 var data = []; 110 for (var i = 0; i < rst.rows.length; i++) data.push(rst.rows.item(i)); 111 callback(data); 112 }); 113 }, 114 // 添加 115 add: function (tbl, fields, valueArr, silenceExec) { 116 var _param = ''; 117 for (var i = 0; i < fields.split(',').length; i++)_param += ',?'; 118 _param = _param.substr(1); 119 var sql = 'INSERT INTO ' + tbl + '(' + fields + ') values(' + _param + ')'; 120 db_exec(sql, valueArr, 121 function (rst) { 122 if (silenceExec == undefined || silenceExec != true) 123 if(!g_debug) 124 $cordovaToast.showShortCenter('add to ' + tbl + ' success'); 125 }); 126 }, 127 // 更新 128 update: function (tbl, fields, valueArr, cndt, silenceExec) { 129 var fv = ''; 130 var flds = fields.split(','); 131 for (var i = 0; i < flds.length; i++) fv += (', ' + flds[i] + '=? '); 132 fv = fv.substr(1); 133 var sql = 'UPDATE ' + tbl + ' SET ' + fv + ' WHERE ' + cndt; 134 db_exec(sql, valueArr, 135 function (rst) { 136 if (silenceExec == undefined || silenceExec != true) 137 if(!g_debug) 138 $cordovaToast.showShortCenter('update ' + tbl + ' success'); 139 }); 140 }, 141 // 删除 142 delete: function (tbl, cndt, silenceExec) { 143 var sql = 'DELETE FROM ' + tbl + ' WHERE ' + cndt; 144 db_exec(sql, [], 145 function (rst) { 146 if (silenceExec == undefined || silenceExec != true) 147 if(!g_debug) 148 $cordovaToast.showShortCenter('delete from ' + tbl + ' success'); 149 }); 150 } 151 152 } 153 }]) 154 155 156 ;
自定义服务:$alertPopup
为方便项目内调用,对$ionicPopup进行封装,也方便日后扩展。
自定义服务:$db
此$db服务基本就是一个DAL层了,封装了基本的CRUD功能,并根据项目需要做了一些“默认处理”(在程序初始化时,自动创建记账和日常表等)。
(*这个sqlite文件物理路径很难找,有什么方法可以快速定位,还望知道的园友赐教:))
记账视图
HTML部分
1 <ion-view view-title="DailyKeeper"> 2 <ion-nav-title><b>记账</b></ion-nav-title> 3 <div class="bar bar-subheader bar-dark"> 4 <h2 class="title"> 5 <a class="button button-icon icon ion-plus-circled" ng-click="showDetail()"></a> 6 </h2> 7 </div> 8 9 <ion-content class="has-tabs has-subheader"> 10 <ion-list> 11 <div ng-repeat="da in dailyAccount"> 12 <div class="item item-divider" style="display: {{da.ext_displayDivider}}"> 13 {{da.RecordDate}} 14 </div> 15 <ion-item class="item-remove-animate item-icon-right" 16 type="item-text-wrap" ng-click="showDetail({{da}})" style="color: {{da.ext_TextColor}}"> 17 【{{da.SuitType}}】{{da.ItemText}} 18 <br/>{{da.Cash}} 19 <i class="icon ion-chevron-right icon-accessory"></i> 20 21 <ion-option-button class="button-assertive" ng-click="remove(da)"> 22 Delete 23 </ion-option-button> 24 </ion-item> 25 </div> 26 </ion-list> 27 </ion-content> 28 29 <!--弹出内容--> 30 <script id="detail.html" type="text/ng-template"> 31 <ion-modal-view> 32 <ion-header-bar> 33 <h1 class="title">{{currDA.title}}</h1> 34 <button class="button" ng-click="closeDetail()">关闭</button> 35 </ion-header-bar> 36 <ion-content> 37 <div class="item-input-inset"> 38 <i class="icon ion-android-calendar"></i> 39 <input type="date" ng-model="currDA.RecordDate"> 40 </div> 41 <div class="item item-input-inset"> 42 <select 43 ng-model="currDA.SuitType" 44 ng-options="value.SuitType as value.SuitType group by value.MainClass for value in Finacial_SuitClass"> 45 <option value=""> -账目类型- </option> 46 </select> 47 <label class="item-input-wrapper"> 48 <input type="text" ng-model="currDA.ItemText" placeholder="消费项"> 49 </label> 50 </div> 51 <div class="item item-input-inset"> 52 <label class="item-input-wrapper"> 53 <input type="number" ng-model="currDA.Cash" placeholder="金额"> 54 </label> 55 <label class="toggle"> 56 <input type="checkbox" ng-model="currDA.Income"> 57 <div class="track"> 58 <div class="handle"></div> 59 </div> 60 </label>(入账) 61 </div> 62 <div class="item item-input-inset"> 63 <textarea style="width: 100%" ng-model="currDA.Remark" placeholder="备注"></textarea> 64 </div> 65 <div class="item-input-inset"> 66 <button class="button button-block button-positive" ng-click="save()"> 67 Save 68 </button> 69 </div> 70 </ion-content> 71 </ion-modal-view> 72 </script> 73 </ion-view>
JavaScript部分
1 angular.module('pdm') 2 .controller('Ctrl_DailyKeeper', 3 ['$scope', '$ionicModal', '$db', '$cordovaToast', '$ionicPopup', '$alertPopup' 4 , function ($scope, $ionicModal, $db, $cordovaToast, $ionicPopup, $alertPopup) { 5 6 // BLL 7 $scope.getKA = function (callback, cndt) { 8 var sql = "SELECT * FROM Finacial_KeepAccount WHERE 1=1 "; 9 if (cndt != undefined && cndt != '')sql += (' AND ' + cndt); 10 sql += ' ORDER BY RecordDate desc'; 11 $db._exec(sql, [], function (rst) { 12 if (rst.rows.length == 0) { 13 if(!g_debug) 14 $cordovaToast.showShortCenter('load ka success but no data'); 15 //return; 16 } 17 var data = []; 18 for (var i = 0; i < rst.rows.length; i++) data.push(rst.rows.item(i)); 19 callback(data); 20 }); 21 }; 22 $scope.addKA = function (SuitType, ItemText, MoneyFlowDirect, Cash, AccountType, RecordDate, Remark) { 23 $db.add('Finacial_KeepAccount' 24 , 'SuitType,ItemText,MoneyFlowDirect,Cash,AccountType,RecordDate,Remark, account' 25 , [SuitType, ItemText, MoneyFlowDirect, Cash, AccountType, RecordDate, Remark, g_user]); 26 }; 27 $scope.updateKA = function (id, SuitType, ItemText, MoneyFlowDirect, Cash, AccountType, RecordDate, Remark) { 28 $db.update('Finacial_KeepAccount' 29 , 'SuitType,ItemText,MoneyFlowDirect,Cash,AccountType,RecordDate,Remark' 30 , [SuitType, ItemText, MoneyFlowDirect, Cash, AccountType, RecordDate, Remark] 31 , 'id=' + id.toString()); 32 }; 33 $scope.deleteKA = function (id) { 34 $db.delete('Finacial_KeepAccount', 'id=' + id.toString()); 35 }; 36 37 38 $scope.Finacial_SuitClass = [ 39 {MainClass: '基本生活', SuitType: '餐饮饮食'} 40 , {MainClass: '基本生活', SuitType: '柴米油盐'} 41 , {MainClass: '美容化妆', SuitType: '服饰装扮'} 42 , {MainClass: '收入', SuitType: '福利津贴'} 43 , {MainClass: '收入', SuitType: '工资'} 44 , {MainClass: '美容化妆', SuitType: '化妆品美容'} 45 , {MainClass: '交通通讯', SuitType: '话费网费'} 46 , {MainClass: '交通通讯', SuitType: '交通费'} 47 , {MainClass: '人情往来', SuitType: '借出'} 48 , {MainClass: '投资', SuitType: '理财投资'} 49 , {MainClass: '文化娱乐', SuitType: '旅游娱乐'} 50 , {MainClass: '收入', SuitType: '其他收入'} 51 , {MainClass: '其他支出', SuitType: '其他支出'} 52 , {MainClass: '人情往来', SuitType: '人际往来'} 53 , {MainClass: '基本生活', SuitType: '日常用品'} 54 , {MainClass: '文化娱乐', SuitType: '书报音像'} 55 , {MainClass: '文化娱乐', SuitType: '数码产品'} 56 , {MainClass: '基本生活', SuitType: '水果零食'} 57 , {MainClass: '基本生活', SuitType: '物业水电'} 58 , {MainClass: '人情往来', SuitType: '孝敬长辈'} 59 , {MainClass: '基本生活', SuitType: '医药保健'} 60 , {MainClass: '文化娱乐', SuitType: '运动健身'} 61 ]; 62 $scope.currDA = { 63 title: '新增' 64 , id: 0 65 , RecordDate: new Date() 66 , SuitType: '' 67 , ItemText: '' 68 , Cash: 0 69 , Income: false 70 , Remark: '' 71 } 72 73 $scope.arrageData = function () { 74 var _data = $scope.dailyAccount; 75 76 if (_data.length > 0) { 77 _data[0].ext_displayDivider = ''; 78 79 if (_data.length > 1) { 80 var lastDA = _data[0]; 81 for (var i = 1; i < _data.length; i++) { 82 _data[i].ext_displayDivider = 'none'; 83 if (new Date(_data[i].RecordDate) < new Date(lastDA.RecordDate)) { 84 _data[i].ext_displayDivider = ''; 85 lastDA = _data[i]; 86 } 87 } 88 } 89 } 90 }; 91 92 $scope.remove = function (da) { 93 $ionicPopup.confirm({ 94 title: 'Confrim', 95 template: 'Do you really want to delete?', 96 scope: $scope, 97 buttons: [ 98 { 99 text: '<b>Yes</b>', 100 type: 'button-positive', 101 onTap: function (e) { 102 //$scope.dailyAccount.splice($scope.dailyAccount.indexOf(da), 1); 103 $scope.deleteKA(da.id); 104 $scope.loadDate(); 105 } 106 }, 107 { 108 type: 'button-canceldark', 109 text: '<b>Cancel</b>', 110 onTap: function (e) { 111 console.log('cancel delete'); 112 } 113 } 114 ] 115 }); 116 }; 117 118 $scope.showDetail = function (da) { 119 if (da == undefined) { 120 // 新增 121 $scope.currDA.title = '新增'; 122 123 $scope.currDA.id = 0; 124 $scope.currDA.RecordDate = new Date(); 125 $scope.currDA.SuitType = ''; 126 $scope.currDA.ItemText = ''; 127 $scope.currDA.Cash = 0; 128 $scope.currDA.Income = false; 129 $scope.currDA.Remark = ''; 130 } else { 131 // 读取 132 $scope.currDA.title = '编辑'; 133 134 $scope.getKA(function (data) { 135 if (data.length > 0) { 136 var item = data[0]; 137 138 $scope.currDA.id = item.id; 139 $scope.currDA.RecordDate = new Date(item.RecordDate); 140 $scope.currDA.SuitType = item.SuitType; 141 $scope.currDA.ItemText = item.ItemText; 142 $scope.currDA.Cash = item.Cash; 143 $scope.currDA.Income = (item.MoneyFlowDirect == '入账'); 144 $scope.currDA.Remark = item.Remark; 145 } 146 } 147 , ' id = ' + da.id); 148 } 149 150 $scope.openModal(); 151 } 152 153 $scope.save = function () { 154 //log(angular.toJson($scope.currDA)); 155 156 if ($scope.currDA.SuitType == '' 157 || $scope.currDA.SuitType.indexOf('账目类型') >= 0) { 158 $alertPopup('账目类型没有选定哦'); 159 return; 160 } 161 162 var _moneyFlowDirection = '出账'; 163 if ($scope.currDA.Income) _moneyFlowDirection = '入账'; 164 165 if ($scope.currDA.id == 0) { 166 // 新增 167 $scope.addKA( 168 $scope.currDA.SuitType 169 , $scope.currDA.ItemText 170 , _moneyFlowDirection 171 , $scope.currDA.Cash 172 , '我的钱包' 173 , dateFormat($scope.currDA.RecordDate, 'ymd') 174 , $scope.currDA.Remark 175 ); 176 $scope.closeDetail(); 177 $scope.loadDate(); 178 } 179 else { 180 // 更新 181 $ionicPopup.confirm({ 182 title: 'Confrim', 183 template: 'Do you really want to update?', 184 scope: $scope, 185 buttons: [ 186 { 187 text: '<b>Yes</b>', 188 type: 'button-positive', 189 onTap: function (e) { 190 $scope.updateKA( 191 $scope.currDA.id 192 , $scope.currDA.SuitType 193 , $scope.currDA.ItemText 194 , _moneyFlowDirection 195 , $scope.currDA.Cash 196 , '我的钱包' 197 , dateFormat($scope.currDA.RecordDate, 'ymd') 198 , $scope.currDA.Remark 199 ); 200 $scope.closeDetail(); 201 $scope.loadDate(); 202 } 203 }, 204 { 205 type: 'button-canceldark', 206 text: '<b>Cancel</b>', 207 onTap: function (e) { 208 console.log('cancel update'); 209 } 210 } 211 ] 212 }); 213 } 214 } 215 216 // 弹窗 217 $ionicModal.fromTemplateUrl('detail.html', { 218 scope: $scope, 219 animation: 'slide-in-up' 220 }).then(function (modal) { 221 $scope.modal = modal; 222 }); 223 $scope.openModal = function () { 224 $scope.modal.show(); 225 }; 226 $scope.closeDetail = function () { 227 $scope.modal.hide(); 228 } 229 $scope.$on('$destroy', function () { 230 $scope.modal.remove(); 231 }); 232 233 $scope.loadDate = function () { 234 $scope.getKA(function (data) { 235 for (var i = 0; i < data.length; i++) { 236 var _d = data[i]; 237 _d['ext_displayDivider'] = 'none'; 238 _d['ext_TextColor'] = 'black'; 239 if (_d.MoneyFlowDirect == '入账')_d['ext_TextColor'] = 'blue'; 240 } 241 $scope.dailyAccount = data; 242 $scope.arrageData(); 243 }); 244 } 245 246 // start 247 $scope.loadDate(); 248 249 }]) 250 ;
说明:
- arrageData()函数根据(按日期倒序)排序好的数据,设置当日最后一条数据(因为是倒序,所以采用最后一条)的ext_displayDivider属性为none,如此实现在“日期-当日各项收支项”的显示效果——按日分割后来发觉也可以用Ionic的Card,当然也许也有第三方控件可以直接用了。
- $ionicModal调用的弹窗功能,弹出的是一个完整的页面,本项目为了简便,就直接写在了同页面里“< script id="detail.html" type="text/ng-template">”
日常视图
HTML部分
1 <ion-view view-title="DailyActivity"> 2 <ion-nav-title><b>日常</b></ion-nav-title> 3 <ion-content class="has-tabs"> 4 <br/> 5 <div id='calendar'></div> 6 </ion-content> 7 8 <!--弹出内容--> 9 <script id="detail.html" type="text/ng-template"> 10 <ion-modal-view> 11 <ion-header-bar> 12 <h1 class="title">活动 / 计划</h1> 13 <button class="button" ng-click="closeDetail()">关闭</button> 14 </ion-header-bar> 15 <ion-content> 16 <div class="item-input-inset"> 17 <label class="item-input-wrapper"> 18 <i class="icon ion-android-calendar"></i> 19 <span>{{act.tDate}}</span> 20 </label> 21 22 <span>{{act.id}}</span> 23 24 <!--<a class="button button-small" ng-click="save()">Save</a>--> 25 </div> 26 <div class="list card"> 27 <div class="item item-divider"> 28 事务 29 </div> 30 <div class="item item-body"> 31 <label class="item-input"> 32 <textarea style="background-color: whitesmoke" ng-model="act.Business"></textarea> 33 </label> 34 </div> 35 <div class="item item-divider"> 36 学习 37 </div> 38 <div class="item item-body"> 39 <label class="item-input"> 40 <textarea style="background-color: whitesmoke" ng-model="act.Study"></textarea> 41 </label> 42 </div> 43 <div class="item item-divider"> 44 健康 45 </div> 46 <div class="item item-body"> 47 <label class="item-input"> 48 <textarea style="background-color: whitesmoke" ng-model="act.Health"></textarea> 49 </label> 50 </div> 51 <div class="item item-divider"> 52 运动 53 </div> 54 <div class="item item-body"> 55 <label class="item-input"> 56 <textarea style="background-color: whitesmoke" ng-model="act.Sport"></textarea> 57 </label> 58 </div> 59 <div class="item item-divider"> 60 其他 61 </div> 62 <div class="item item-body"> 63 <label class="item-input"> 64 <textarea style="background-color: whitesmoke" ng-model="act.Others"></textarea> 65 </label> 66 </div> 67 </div> 68 </ion-content> 69 </ion-modal-view> 70 </script> 71 </ion-view>
JavaScript部分
1 angular.module('pdm') 2 .controller('Ctrl_DailyActivity', 3 ['$scope', '$ionicModal', '$db', '$cordovaToast', '$alertPopup' 4 , function ($scope, $ionicModal, $db, $cordovaToast, $alertPopup) { 5 6 // BLL 7 $scope.getDA = function (callback, cndt) { 8 var sql = "SELECT * FROM Life_DailyActivity WHERE 1=1 "; 9 if (cndt != undefined && cndt != '')sql += (' AND ' + cndt); 10 sql += ' ORDER BY Date'; 11 $db._exec(sql, [], function (rst) { 12 var data = []; 13 for (var i = 0; i < rst.rows.length; i++) data.push(rst.rows.item(i)); 14 callback(data); 15 }); 16 }; 17 $scope.addDA = function (Date, Business, Study, Health, Sport, Others, Remark, callback) { 18 var tbl = 'Life_DailyActivity'; 19 var fields = 'account,Date,Business,Study,Health,Sport,Others,Remark'; 20 var valueArr = [g_user, Date, Business, Study, Health, Sport, Others, Remark]; 21 var _param = ''; 22 for (var i = 0; i < fields.split(',').length; i++)_param += ',?'; 23 _param = _param.substr(1); 24 var sql = 'INSERT INTO ' + tbl + '(' + fields + ') values(' + _param + ')'; 25 $db._exec(sql, valueArr, 26 function (rst) { 27 if (callback != undefined && callback != null) callback(rst); 28 else $cordovaToast.showShortCenter('add to ' + tbl + ' success'); 29 }); 30 }; 31 $scope.updateDA = function (Date, Business, Study, Health, Sport, Others, Remark) { 32 $db.update('Life_DailyActivity' 33 , 'Business,Study,Health,Sport,Others,Remark' 34 , [Business, Study, Health, Sport, Others, Remark] 35 , "Date='" + Date + "'" 36 , true); 37 } 38 39 40 $scope.editing = false; 41 $scope.act = { 42 id: 0, 43 tDate: dateFormat(new Date(), 'ymd'), 44 Business: '', 45 Study: '', 46 Health: '', 47 Sport: '', 48 Others: '', 49 Remark: '' 50 }; 51 var _lastDate = $scope.act.tDate; 52 53 $scope.loadData = function () { 54 $scope.getDA(function (data) { 55 $scope.act.Business = ''; 56 $scope.act.Study = ''; 57 $scope.act.Health = ''; 58 $scope.act.Sport = ''; 59 $scope.act.Others = ''; 60 $scope.act.Remark = ''; 61 62 if (data.length > 0) { 63 var item = data[0]; 64 $scope.act.id = item.id; 65 $scope.act.Business = item.Business; 66 $scope.act.Study = item.Study; 67 $scope.act.Health = item.Health; 68 $scope.act.Sport = item.Sport; 69 $scope.act.Others = item.Others; 70 $scope.act.Remark = item.Remark; 71 72 if ($scope.act.id > 0) { 73 $db._exec("delete from Life_DailyActivity where Date='" + $scope.act.tDate + "' and id!=" + $scope.act.id); 74 } 75 } else { 76 $scope.addDA($scope.act.tDate 77 , $scope.act.Business 78 , $scope.act.Study 79 , $scope.act.Health 80 , $scope.act.Sport 81 , $scope.act.Others 82 , $scope.act.Remark 83 , function (rst) { 84 $scope.act.id = rst.insertId; 85 } 86 ); 87 } 88 }, 89 "Date='" + $scope.act.tDate + "'"); 90 } 91 92 $scope.save = function () { 93 $scope.updateDA($scope.act.tDate 94 , $scope.act.Business 95 , $scope.act.Study 96 , $scope.act.Health 97 , $scope.act.Sport 98 , $scope.act.Others 99 , $scope.act.Remark 100 ); 101 } 102 103 // 监听数据变化 104 $scope.$watch('act.Business', function (newValue, oldValue, scope) { 105 if ($scope.editing && newValue != oldValue)$scope.save(); 106 }); 107 $scope.$watch('act.Study', function (newValue, oldValue, scope) { 108 if ($scope.editing && newValue != oldValue)$scope.save(); 109 }); 110 $scope.$watch('act.Health', function (newValue, oldValue, scope) { 111 if ($scope.editing && newValue != oldValue)$scope.save(); 112 }); 113 $scope.$watch('act.Sport', function (newValue, oldValue, scope) { 114 if ($scope.editing && newValue != oldValue)$scope.save(); 115 }); 116 $scope.$watch('act.Others', function (newValue, oldValue, scope) { 117 if ($scope.editing && newValue != oldValue)$scope.save(); 118 }); 119 120 121 $scope.initData = function () { 122 var events_data = []; 123 124 // 日常 125 $scope.getDA(function (data) { 126 var op = []; 127 op['Business'] = '#387EF5'; 128 op['Study'] = '#FFC900'; 129 op['Health'] = '#EF473A'; 130 op['Sport'] = '#33CD5F'; 131 op['Others'] = '#B2B2B2'; 132 133 for (var i = 0; i < data.length; i++) { 134 var dd = data[i]; 135 for (var k in op) { 136 if (dd[k.toString()] != undefined && dd[k.toString()] != '') { 137 var item = []; 138 item['color'] = op[k]; 139 item['title'] = dd[k.toString()].replace('\n','|').substring(0, 10); 140 item['start'] = new Date(dd['Date']); 141 events_data.push(item); 142 } 143 } 144 } 145 146 $('#calendar').fullCalendar('destroy'); 147 $('#calendar').fullCalendar({ 148 header: { 149 left: 'prev,next today', 150 center: 'title', 151 right: 'month'//,agendaWeek,agendaDay' 152 }, 153 firstDay: 1, 154 events: events_data, 155 // 点击空白 156 dayClick: function (date, allDay, jsEvent, view) { 157 var selDate = $.fullCalendar.formatDate(date, 'yyyy-MM-dd');//格式化日期 158 $scope.act.tDate = selDate; 159 160 $scope.loadData(); 161 $scope.openModal(); 162 }, 163 //单击事件项时触发 164 eventClick: function (calEvent, jsEvent, view) { 165 $scope.act.tDate = dateFormat(calEvent.start,'ymd'); 166 167 $scope.loadData(); 168 $scope.openModal(); 169 } 170 }); 171 }); 172 } 173 174 175 // 弹窗 176 $ionicModal.fromTemplateUrl('detail.html', { 177 scope: $scope, 178 animation: 'slide-in-up' 179 }).then(function (modal) { 180 $scope.modal = modal; 181 }); 182 $scope.openModal = function () { 183 $scope.modal.show(); 184 185 $scope.editing = true; 186 }; 187 $scope.closeDetail = function () { 188 $scope.initData(); 189 $scope.editing = false; 190 191 $scope.modal.hide(); 192 } 193 $scope.$on('$destroy', function () { 194 $scope.modal.remove(); 195 }); 196 197 // start 198 $scope.initData(); 199 200 }]) 201 ;
说明:
- 日常数据的录入,采用了“即变即更新”的模式,这里使用$watch函数来监听数据变化。同时为了数据更新功能的便利性,在用户点击某一日弹框时,自动判断当日数据是否存在,不存在则插入空数据。
打包发布
生成Android平台安装包
使用命令:
cordova platform add android cordova build android
(*注意,如果以上步骤出错,常见原因有:
- 安装的Android SDK和打包的SDK版本不对,下载相应SDK
- 环境变量没有配置好
- 安装最新node.js
)
*附录
【源码文件】
【APK文件】