├── .editorconfig ├── .gitignore ├── README.md ├── bower_components └── platform.js │ ├── .bower.json │ ├── LICENSE │ ├── bower.json │ ├── platform.js │ └── yarn.lock ├── config.xml ├── ionic.config.json ├── package.json ├── resources ├── android │ ├── icon │ │ ├── drawable-hdpi-icon.png │ │ ├── drawable-ldpi-icon.png │ │ ├── drawable-mdpi-icon.png │ │ ├── drawable-xhdpi-icon.png │ │ ├── drawable-xxhdpi-icon.png │ │ └── drawable-xxxhdpi-icon.png │ └── splash │ │ ├── drawable-land-hdpi-screen.png │ │ ├── drawable-land-ldpi-screen.png │ │ ├── drawable-land-mdpi-screen.png │ │ ├── drawable-land-xhdpi-screen.png │ │ ├── drawable-land-xxhdpi-screen.png │ │ ├── drawable-land-xxxhdpi-screen.png │ │ ├── drawable-port-hdpi-screen.png │ │ ├── drawable-port-ldpi-screen.png │ │ ├── drawable-port-mdpi-screen.png │ │ ├── drawable-port-xhdpi-screen.png │ │ ├── drawable-port-xxhdpi-screen.png │ │ └── drawable-port-xxxhdpi-screen.png ├── icon.png ├── ios │ ├── icon │ │ ├── icon-40.png │ │ ├── icon-40@2x.png │ │ ├── icon-40@3x.png │ │ ├── icon-50.png │ │ ├── icon-50@2x.png │ │ ├── icon-60.png │ │ ├── icon-60@2x.png │ │ ├── icon-60@3x.png │ │ ├── icon-72.png │ │ ├── icon-72@2x.png │ │ ├── icon-76.png │ │ ├── icon-76@2x.png │ │ ├── icon-83.5@2x.png │ │ ├── icon-small.png │ │ ├── icon-small@2x.png │ │ ├── icon-small@3x.png │ │ ├── icon.png │ │ └── icon@2x.png │ └── splash │ │ ├── Default-568h@2x~iphone.png │ │ ├── Default-667h.png │ │ ├── Default-736h.png │ │ ├── Default-Landscape-736h.png │ │ ├── Default-Landscape@2x~ipad.png │ │ ├── Default-Landscape~ipad.png │ │ ├── Default-Portrait@2x~ipad.png │ │ ├── Default-Portrait~ipad.png │ │ ├── Default@2x~iphone.png │ │ └── Default~iphone.png └── splash.png ├── src ├── app │ ├── app.component.ts │ ├── app.html │ ├── app.module.ts │ ├── app.scss │ └── main.ts ├── assets │ └── icon │ │ ├── avator.jpg │ │ ├── bg.jpg │ │ ├── bg.png │ │ └── favicon.ico ├── components │ └── sticky-direvtive │ │ └── sticky-direvtive.ts ├── declarations.d.ts ├── domain │ ├── Book.ts │ ├── Constants.ts │ ├── Dictionary.ts │ ├── QueryBook.ts │ ├── RestEntity.ts │ └── User.ts ├── index.html ├── manifest.json ├── pages │ ├── add-bill │ │ ├── add-bill.html │ │ ├── add-bill.scss │ │ └── add-bill.ts │ ├── ichart-page │ │ ├── ichart-page.html │ │ ├── ichart-page.scss │ │ └── ichart-page.ts │ ├── login-page │ │ ├── login-page.html │ │ ├── login-page.scss │ │ └── login-page.ts │ ├── month-pop-page │ │ ├── month-pop-page.html │ │ ├── month-pop-page.scss │ │ └── month-pop-page.ts │ ├── results-bill │ │ ├── results-bill.html │ │ ├── results-bill.scss │ │ └── results-bill.ts │ ├── save-bill │ │ ├── save-bill.html │ │ ├── save-bill.scss │ │ └── save-bill.ts │ └── search-bill │ │ ├── search-bill.html │ │ ├── search-bill.scss │ │ └── search-bill.ts ├── providers │ ├── http-service.ts │ └── storage-service.ts ├── service-worker.js ├── service │ └── BookService.ts ├── theme │ └── variables.scss └── utils │ ├── Logger.ts │ └── date-utils.ts ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | 10 | # We recommend you to keep these unchanged 11 | end_of_line = lf 12 | charset = utf-8 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Specifies intentionally untracked files to ignore when using Git 2 | # http://git-scm.com/docs/gitignore 3 | 4 | *~ 5 | *.sw[mnpcod] 6 | *.log 7 | *.tmp 8 | *.tmp.* 9 | log.txt 10 | *.sublime-project 11 | *.sublime-workspace 12 | .vscode/ 13 | npm-debug.log* 14 | 15 | .idea/ 16 | .sass-cache/ 17 | .tmp/ 18 | .versions/ 19 | coverage/ 20 | dist/ 21 | node_modules/ 22 | tmp/ 23 | temp/ 24 | hooks/ 25 | platforms/ 26 | plugins/ 27 | plugins/android.json 28 | plugins/ios.json 29 | www/ 30 | $RECYCLE.BIN/ 31 | 32 | .DS_Store 33 | Thumbs.db 34 | UserInterfaceState.xcuserstate 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## DayBookApp 2 | 3 | Ionic2构建的一款流水账记录App。 4 | 服务端 GitHub 请点击[DayBook](https://github.com/zyqwst/DayBook) 5 | 6 | ## 效果预览 7 | 8 |  9 | 10 |  11 | 12 |  13 | 14 | 15 |  16 | 17 |  18 | 19 |  20 | 21 |  22 | ## 启动说明 23 | DayBookApp运行需要API服务器资源支持,服务端代码请查看[DayBook](https://github.com/zyqwst/DayBook)。 24 | - 服务器启动完成后, 25 | 需要修改App中服务起访问地址,src/providers/http-service.ts文件修改API服务器访问地址 26 | ` hostUrl:string = "http://192.168.1.1:9971"; ` 27 | 28 | 修改成功后,运行命令 29 | `ionic serve` 30 | 浏览器打开`http://localhost:8100`即可打开浏览器看到效果。 31 | `http://localhost:8100/ionic-lab`可以看到android、IOS、WP的效果 32 | 33 | ## 构建Android App 34 | ``` 35 | ionic platform add android 36 | ionic build android 37 | ``` 38 | 运行以上两条命令,提示成功后,在DayBookApp/platforms/android/build/outputs/apk目录下会生成android apk应用。然后可以安装到手机~ 39 | -------------------------------------------------------------------------------- /bower_components/platform.js/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platform.js", 3 | "main": "platform.js", 4 | "ignore": [ 5 | ".*", 6 | "*.log", 7 | "*.md", 8 | "package.json", 9 | "doc", 10 | "test" 11 | ], 12 | "homepage": "https://github.com/bestiejs/platform.js", 13 | "version": "1.3.4", 14 | "_release": "1.3.4", 15 | "_resolution": { 16 | "type": "version", 17 | "tag": "1.3.4", 18 | "commit": "dbe9db964ca25a26712ccb51482111f3773d35ef" 19 | }, 20 | "_source": "https://github.com/bestiejs/platform.js.git", 21 | "_target": "^1.3.4", 22 | "_originalSource": "platform", 23 | "_direct": true 24 | } -------------------------------------------------------------------------------- /bower_components/platform.js/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014-2016 Benjamin Tan 2 | Copyright 2011-2013 John-David Dalton 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /bower_components/platform.js/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platform.js", 3 | "main": "platform.js", 4 | "ignore": [ 5 | ".*", 6 | "*.log", 7 | "*.md", 8 | "package.json", 9 | "doc", 10 | "test" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /bower_components/platform.js/platform.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Platform.js 3 | * Copyright 2014-2016 Benjamin Tan 4 | * Copyright 2011-2013 John-David Dalton 5 | * Available under MIT license 6 | */ 7 | ;(function() { 8 | 'use strict'; 9 | 10 | /** Used to determine if values are of the language type `Object`. */ 11 | var objectTypes = { 12 | 'function': true, 13 | 'object': true 14 | }; 15 | 16 | /** Used as a reference to the global object. */ 17 | var root = (objectTypes[typeof window] && window) || this; 18 | 19 | /** Backup possible global object. */ 20 | var oldRoot = root; 21 | 22 | /** Detect free variable `exports`. */ 23 | var freeExports = objectTypes[typeof exports] && exports; 24 | 25 | /** Detect free variable `module`. */ 26 | var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; 27 | 28 | /** Detect free variable `global` from Node.js or Browserified code and use it as `root`. */ 29 | var freeGlobal = freeExports && freeModule && typeof global == 'object' && global; 30 | if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) { 31 | root = freeGlobal; 32 | } 33 | 34 | /** 35 | * Used as the maximum length of an array-like object. 36 | * See the [ES6 spec](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength) 37 | * for more details. 38 | */ 39 | var maxSafeInteger = Math.pow(2, 53) - 1; 40 | 41 | /** Regular expression to detect Opera. */ 42 | var reOpera = /\bOpera/; 43 | 44 | /** Possible global object. */ 45 | var thisBinding = this; 46 | 47 | /** Used for native method references. */ 48 | var objectProto = Object.prototype; 49 | 50 | /** Used to check for own properties of an object. */ 51 | var hasOwnProperty = objectProto.hasOwnProperty; 52 | 53 | /** Used to resolve the internal `[[Class]]` of values. */ 54 | var toString = objectProto.toString; 55 | 56 | /*--------------------------------------------------------------------------*/ 57 | 58 | /** 59 | * Capitalizes a string value. 60 | * 61 | * @private 62 | * @param {string} string The string to capitalize. 63 | * @returns {string} The capitalized string. 64 | */ 65 | function capitalize(string) { 66 | string = String(string); 67 | return string.charAt(0).toUpperCase() + string.slice(1); 68 | } 69 | 70 | /** 71 | * A utility function to clean up the OS name. 72 | * 73 | * @private 74 | * @param {string} os The OS name to clean up. 75 | * @param {string} [pattern] A `RegExp` pattern matching the OS name. 76 | * @param {string} [label] A label for the OS. 77 | */ 78 | function cleanupOS(os, pattern, label) { 79 | // Platform tokens are defined at: 80 | // http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx 81 | // http://web.archive.org/web/20081122053950/http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx 82 | var data = { 83 | '10.0': '10', 84 | '6.4': '10 Technical Preview', 85 | '6.3': '8.1', 86 | '6.2': '8', 87 | '6.1': 'Server 2008 R2 / 7', 88 | '6.0': 'Server 2008 / Vista', 89 | '5.2': 'Server 2003 / XP 64-bit', 90 | '5.1': 'XP', 91 | '5.01': '2000 SP1', 92 | '5.0': '2000', 93 | '4.0': 'NT', 94 | '4.90': 'ME' 95 | }; 96 | // Detect Windows version from platform tokens. 97 | if (pattern && label && /^Win/i.test(os) && !/^Windows Phone /i.test(os) && 98 | (data = data[/[\d.]+$/.exec(os)])) { 99 | os = 'Windows ' + data; 100 | } 101 | // Correct character case and cleanup string. 102 | os = String(os); 103 | 104 | if (pattern && label) { 105 | os = os.replace(RegExp(pattern, 'i'), label); 106 | } 107 | 108 | os = format( 109 | os.replace(/ ce$/i, ' CE') 110 | .replace(/\bhpw/i, 'web') 111 | .replace(/\bMacintosh\b/, 'Mac OS') 112 | .replace(/_PowerPC\b/i, ' OS') 113 | .replace(/\b(OS X) [^ \d]+/i, '$1') 114 | .replace(/\bMac (OS X)\b/, '$1') 115 | .replace(/\/(\d)/, ' $1') 116 | .replace(/_/g, '.') 117 | .replace(/(?: BePC|[ .]*fc[ \d.]+)$/i, '') 118 | .replace(/\bx86\.64\b/gi, 'x86_64') 119 | .replace(/\b(Windows Phone) OS\b/, '$1') 120 | .replace(/\b(Chrome OS \w+) [\d.]+\b/, '$1') 121 | .split(' on ')[0] 122 | ); 123 | 124 | return os; 125 | } 126 | 127 | /** 128 | * An iteration utility for arrays and objects. 129 | * 130 | * @private 131 | * @param {Array|Object} object The object to iterate over. 132 | * @param {Function} callback The function called per iteration. 133 | */ 134 | function each(object, callback) { 135 | var index = -1, 136 | length = object ? object.length : 0; 137 | 138 | if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) { 139 | while (++index < length) { 140 | callback(object[index], index, object); 141 | } 142 | } else { 143 | forOwn(object, callback); 144 | } 145 | } 146 | 147 | /** 148 | * Trim and conditionally capitalize string values. 149 | * 150 | * @private 151 | * @param {string} string The string to format. 152 | * @returns {string} The formatted string. 153 | */ 154 | function format(string) { 155 | string = trim(string); 156 | return /^(?:webOS|i(?:OS|P))/.test(string) 157 | ? string 158 | : capitalize(string); 159 | } 160 | 161 | /** 162 | * Iterates over an object's own properties, executing the `callback` for each. 163 | * 164 | * @private 165 | * @param {Object} object The object to iterate over. 166 | * @param {Function} callback The function executed per own property. 167 | */ 168 | function forOwn(object, callback) { 169 | for (var key in object) { 170 | if (hasOwnProperty.call(object, key)) { 171 | callback(object[key], key, object); 172 | } 173 | } 174 | } 175 | 176 | /** 177 | * Gets the internal `[[Class]]` of a value. 178 | * 179 | * @private 180 | * @param {*} value The value. 181 | * @returns {string} The `[[Class]]`. 182 | */ 183 | function getClassOf(value) { 184 | return value == null 185 | ? capitalize(value) 186 | : toString.call(value).slice(8, -1); 187 | } 188 | 189 | /** 190 | * Host objects can return type values that are different from their actual 191 | * data type. The objects we are concerned with usually return non-primitive 192 | * types of "object", "function", or "unknown". 193 | * 194 | * @private 195 | * @param {*} object The owner of the property. 196 | * @param {string} property The property to check. 197 | * @returns {boolean} Returns `true` if the property value is a non-primitive, else `false`. 198 | */ 199 | function isHostType(object, property) { 200 | var type = object != null ? typeof object[property] : 'number'; 201 | return !/^(?:boolean|number|string|undefined)$/.test(type) && 202 | (type == 'object' ? !!object[property] : true); 203 | } 204 | 205 | /** 206 | * Prepares a string for use in a `RegExp` by making hyphens and spaces optional. 207 | * 208 | * @private 209 | * @param {string} string The string to qualify. 210 | * @returns {string} The qualified string. 211 | */ 212 | function qualify(string) { 213 | return String(string).replace(/([ -])(?!$)/g, '$1?'); 214 | } 215 | 216 | /** 217 | * A bare-bones `Array#reduce` like utility function. 218 | * 219 | * @private 220 | * @param {Array} array The array to iterate over. 221 | * @param {Function} callback The function called per iteration. 222 | * @returns {*} The accumulated result. 223 | */ 224 | function reduce(array, callback) { 225 | var accumulator = null; 226 | each(array, function(value, index) { 227 | accumulator = callback(accumulator, value, index, array); 228 | }); 229 | return accumulator; 230 | } 231 | 232 | /** 233 | * Removes leading and trailing whitespace from a string. 234 | * 235 | * @private 236 | * @param {string} string The string to trim. 237 | * @returns {string} The trimmed string. 238 | */ 239 | function trim(string) { 240 | return String(string).replace(/^ +| +$/g, ''); 241 | } 242 | 243 | /*--------------------------------------------------------------------------*/ 244 | 245 | /** 246 | * Creates a new platform object. 247 | * 248 | * @memberOf platform 249 | * @param {Object|string} [ua=navigator.userAgent] The user agent string or 250 | * context object. 251 | * @returns {Object} A platform object. 252 | */ 253 | function parse(ua) { 254 | 255 | /** The environment context object. */ 256 | var context = root; 257 | 258 | /** Used to flag when a custom context is provided. */ 259 | var isCustomContext = ua && typeof ua == 'object' && getClassOf(ua) != 'String'; 260 | 261 | // Juggle arguments. 262 | if (isCustomContext) { 263 | context = ua; 264 | ua = null; 265 | } 266 | 267 | /** Browser navigator object. */ 268 | var nav = context.navigator || {}; 269 | 270 | /** Browser user agent string. */ 271 | var userAgent = nav.userAgent || ''; 272 | 273 | ua || (ua = userAgent); 274 | 275 | /** Used to flag when `thisBinding` is the [ModuleScope]. */ 276 | var isModuleScope = isCustomContext || thisBinding == oldRoot; 277 | 278 | /** Used to detect if browser is like Chrome. */ 279 | var likeChrome = isCustomContext 280 | ? !!nav.likeChrome 281 | : /\bChrome\b/.test(ua) && !/internal|\n/i.test(toString.toString()); 282 | 283 | /** Internal `[[Class]]` value shortcuts. */ 284 | var objectClass = 'Object', 285 | airRuntimeClass = isCustomContext ? objectClass : 'ScriptBridgingProxyObject', 286 | enviroClass = isCustomContext ? objectClass : 'Environment', 287 | javaClass = (isCustomContext && context.java) ? 'JavaPackage' : getClassOf(context.java), 288 | phantomClass = isCustomContext ? objectClass : 'RuntimeObject'; 289 | 290 | /** Detect Java environments. */ 291 | var java = /\bJava/.test(javaClass) && context.java; 292 | 293 | /** Detect Rhino. */ 294 | var rhino = java && getClassOf(context.environment) == enviroClass; 295 | 296 | /** A character to represent alpha. */ 297 | var alpha = java ? 'a' : '\u03b1'; 298 | 299 | /** A character to represent beta. */ 300 | var beta = java ? 'b' : '\u03b2'; 301 | 302 | /** Browser document object. */ 303 | var doc = context.document || {}; 304 | 305 | /** 306 | * Detect Opera browser (Presto-based). 307 | * http://www.howtocreate.co.uk/operaStuff/operaObject.html 308 | * http://dev.opera.com/articles/view/opera-mini-web-content-authoring-guidelines/#operamini 309 | */ 310 | var opera = context.operamini || context.opera; 311 | 312 | /** Opera `[[Class]]`. */ 313 | var operaClass = reOpera.test(operaClass = (isCustomContext && opera) ? opera['[[Class]]'] : getClassOf(opera)) 314 | ? operaClass 315 | : (opera = null); 316 | 317 | /*------------------------------------------------------------------------*/ 318 | 319 | /** Temporary variable used over the script's lifetime. */ 320 | var data; 321 | 322 | /** The CPU architecture. */ 323 | var arch = ua; 324 | 325 | /** Platform description array. */ 326 | var description = []; 327 | 328 | /** Platform alpha/beta indicator. */ 329 | var prerelease = null; 330 | 331 | /** A flag to indicate that environment features should be used to resolve the platform. */ 332 | var useFeatures = ua == userAgent; 333 | 334 | /** The browser/environment version. */ 335 | var version = useFeatures && opera && typeof opera.version == 'function' && opera.version(); 336 | 337 | /** A flag to indicate if the OS ends with "/ Version" */ 338 | var isSpecialCasedOS; 339 | 340 | /* Detectable layout engines (order is important). */ 341 | var layout = getLayout([ 342 | { 'label': 'EdgeHTML', 'pattern': 'Edge' }, 343 | 'Trident', 344 | { 'label': 'WebKit', 'pattern': 'AppleWebKit' }, 345 | 'iCab', 346 | 'Presto', 347 | 'NetFront', 348 | 'Tasman', 349 | 'KHTML', 350 | 'Gecko' 351 | ]); 352 | 353 | /* Detectable browser names (order is important). */ 354 | var name = getName([ 355 | 'Adobe AIR', 356 | 'Arora', 357 | 'Avant Browser', 358 | 'Breach', 359 | 'Camino', 360 | 'Electron', 361 | 'Epiphany', 362 | 'Fennec', 363 | 'Flock', 364 | 'Galeon', 365 | 'GreenBrowser', 366 | 'iCab', 367 | 'Iceweasel', 368 | 'K-Meleon', 369 | 'Konqueror', 370 | 'Lunascape', 371 | 'Maxthon', 372 | { 'label': 'Microsoft Edge', 'pattern': 'Edge' }, 373 | 'Midori', 374 | 'Nook Browser', 375 | 'PaleMoon', 376 | 'PhantomJS', 377 | 'Raven', 378 | 'Rekonq', 379 | 'RockMelt', 380 | { 'label': 'Samsung Internet', 'pattern': 'SamsungBrowser' }, 381 | 'SeaMonkey', 382 | { 'label': 'Silk', 'pattern': '(?:Cloud9|Silk-Accelerated)' }, 383 | 'Sleipnir', 384 | 'SlimBrowser', 385 | { 'label': 'SRWare Iron', 'pattern': 'Iron' }, 386 | 'Sunrise', 387 | 'Swiftfox', 388 | 'Waterfox', 389 | 'WebPositive', 390 | 'Opera Mini', 391 | { 'label': 'Opera Mini', 'pattern': 'OPiOS' }, 392 | 'Opera', 393 | { 'label': 'Opera', 'pattern': 'OPR' }, 394 | 'Chrome', 395 | { 'label': 'Chrome Mobile', 'pattern': '(?:CriOS|CrMo)' }, 396 | { 'label': 'Firefox', 'pattern': '(?:Firefox|Minefield)' }, 397 | { 'label': 'Firefox for iOS', 'pattern': 'FxiOS' }, 398 | { 'label': 'IE', 'pattern': 'IEMobile' }, 399 | { 'label': 'IE', 'pattern': 'MSIE' }, 400 | 'Safari' 401 | ]); 402 | 403 | /* Detectable products (order is important). */ 404 | var product = getProduct([ 405 | { 'label': 'BlackBerry', 'pattern': 'BB10' }, 406 | 'BlackBerry', 407 | { 'label': 'Galaxy S', 'pattern': 'GT-I9000' }, 408 | { 'label': 'Galaxy S2', 'pattern': 'GT-I9100' }, 409 | { 'label': 'Galaxy S3', 'pattern': 'GT-I9300' }, 410 | { 'label': 'Galaxy S4', 'pattern': 'GT-I9500' }, 411 | { 'label': 'Galaxy S5', 'pattern': 'SM-G900' }, 412 | { 'label': 'Galaxy S6', 'pattern': 'SM-G920' }, 413 | { 'label': 'Galaxy S6 Edge', 'pattern': 'SM-G925' }, 414 | { 'label': 'Galaxy S7', 'pattern': 'SM-G930' }, 415 | { 'label': 'Galaxy S7 Edge', 'pattern': 'SM-G935' }, 416 | 'Google TV', 417 | 'Lumia', 418 | 'iPad', 419 | 'iPod', 420 | 'iPhone', 421 | 'Kindle', 422 | { 'label': 'Kindle Fire', 'pattern': '(?:Cloud9|Silk-Accelerated)' }, 423 | 'Nexus', 424 | 'Nook', 425 | 'PlayBook', 426 | 'PlayStation Vita', 427 | 'PlayStation', 428 | 'TouchPad', 429 | 'Transformer', 430 | { 'label': 'Wii U', 'pattern': 'WiiU' }, 431 | 'Wii', 432 | 'Xbox One', 433 | { 'label': 'Xbox 360', 'pattern': 'Xbox' }, 434 | 'Xoom' 435 | ]); 436 | 437 | /* Detectable manufacturers. */ 438 | var manufacturer = getManufacturer({ 439 | 'Apple': { 'iPad': 1, 'iPhone': 1, 'iPod': 1 }, 440 | 'Archos': {}, 441 | 'Amazon': { 'Kindle': 1, 'Kindle Fire': 1 }, 442 | 'Asus': { 'Transformer': 1 }, 443 | 'Barnes & Noble': { 'Nook': 1 }, 444 | 'BlackBerry': { 'PlayBook': 1 }, 445 | 'Google': { 'Google TV': 1, 'Nexus': 1 }, 446 | 'HP': { 'TouchPad': 1 }, 447 | 'HTC': {}, 448 | 'LG': {}, 449 | 'Microsoft': { 'Xbox': 1, 'Xbox One': 1 }, 450 | 'Motorola': { 'Xoom': 1 }, 451 | 'Nintendo': { 'Wii U': 1, 'Wii': 1 }, 452 | 'Nokia': { 'Lumia': 1 }, 453 | 'Samsung': { 'Galaxy S': 1, 'Galaxy S2': 1, 'Galaxy S3': 1, 'Galaxy S4': 1 }, 454 | 'Sony': { 'PlayStation': 1, 'PlayStation Vita': 1 } 455 | }); 456 | 457 | /* Detectable operating systems (order is important). */ 458 | var os = getOS([ 459 | 'Windows Phone', 460 | 'Android', 461 | 'CentOS', 462 | { 'label': 'Chrome OS', 'pattern': 'CrOS' }, 463 | 'Debian', 464 | 'Fedora', 465 | 'FreeBSD', 466 | 'Gentoo', 467 | 'Haiku', 468 | 'Kubuntu', 469 | 'Linux Mint', 470 | 'OpenBSD', 471 | 'Red Hat', 472 | 'SuSE', 473 | 'Ubuntu', 474 | 'Xubuntu', 475 | 'Cygwin', 476 | 'Symbian OS', 477 | 'hpwOS', 478 | 'webOS ', 479 | 'webOS', 480 | 'Tablet OS', 481 | 'Tizen', 482 | 'Linux', 483 | 'Mac OS X', 484 | 'Macintosh', 485 | 'Mac', 486 | 'Windows 98;', 487 | 'Windows ' 488 | ]); 489 | 490 | /*------------------------------------------------------------------------*/ 491 | 492 | /** 493 | * Picks the layout engine from an array of guesses. 494 | * 495 | * @private 496 | * @param {Array} guesses An array of guesses. 497 | * @returns {null|string} The detected layout engine. 498 | */ 499 | function getLayout(guesses) { 500 | return reduce(guesses, function(result, guess) { 501 | return result || RegExp('\\b' + ( 502 | guess.pattern || qualify(guess) 503 | ) + '\\b', 'i').exec(ua) && (guess.label || guess); 504 | }); 505 | } 506 | 507 | /** 508 | * Picks the manufacturer from an array of guesses. 509 | * 510 | * @private 511 | * @param {Array} guesses An object of guesses. 512 | * @returns {null|string} The detected manufacturer. 513 | */ 514 | function getManufacturer(guesses) { 515 | return reduce(guesses, function(result, value, key) { 516 | // Lookup the manufacturer by product or scan the UA for the manufacturer. 517 | return result || ( 518 | value[product] || 519 | value[/^[a-z]+(?: +[a-z]+\b)*/i.exec(product)] || 520 | RegExp('\\b' + qualify(key) + '(?:\\b|\\w*\\d)', 'i').exec(ua) 521 | ) && key; 522 | }); 523 | } 524 | 525 | /** 526 | * Picks the browser name from an array of guesses. 527 | * 528 | * @private 529 | * @param {Array} guesses An array of guesses. 530 | * @returns {null|string} The detected browser name. 531 | */ 532 | function getName(guesses) { 533 | return reduce(guesses, function(result, guess) { 534 | return result || RegExp('\\b' + ( 535 | guess.pattern || qualify(guess) 536 | ) + '\\b', 'i').exec(ua) && (guess.label || guess); 537 | }); 538 | } 539 | 540 | /** 541 | * Picks the OS name from an array of guesses. 542 | * 543 | * @private 544 | * @param {Array} guesses An array of guesses. 545 | * @returns {null|string} The detected OS name. 546 | */ 547 | function getOS(guesses) { 548 | return reduce(guesses, function(result, guess) { 549 | var pattern = guess.pattern || qualify(guess); 550 | if (!result && (result = 551 | RegExp('\\b' + pattern + '(?:/[\\d.]+|[ \\w.]*)', 'i').exec(ua) 552 | )) { 553 | result = cleanupOS(result, pattern, guess.label || guess); 554 | } 555 | return result; 556 | }); 557 | } 558 | 559 | /** 560 | * Picks the product name from an array of guesses. 561 | * 562 | * @private 563 | * @param {Array} guesses An array of guesses. 564 | * @returns {null|string} The detected product name. 565 | */ 566 | function getProduct(guesses) { 567 | return reduce(guesses, function(result, guess) { 568 | var pattern = guess.pattern || qualify(guess); 569 | if (!result && (result = 570 | RegExp('\\b' + pattern + ' *\\d+[.\\w_]*', 'i').exec(ua) || 571 | RegExp('\\b' + pattern + ' *\\w+-[\\w]*', 'i').exec(ua) || 572 | RegExp('\\b' + pattern + '(?:; *(?:[a-z]+[_-])?[a-z]+\\d+|[^ ();-]*)', 'i').exec(ua) 573 | )) { 574 | // Split by forward slash and append product version if needed. 575 | if ((result = String((guess.label && !RegExp(pattern, 'i').test(guess.label)) ? guess.label : result).split('/'))[1] && !/[\d.]+/.test(result[0])) { 576 | result[0] += ' ' + result[1]; 577 | } 578 | // Correct character case and cleanup string. 579 | guess = guess.label || guess; 580 | result = format(result[0] 581 | .replace(RegExp(pattern, 'i'), guess) 582 | .replace(RegExp('; *(?:' + guess + '[_-])?', 'i'), ' ') 583 | .replace(RegExp('(' + guess + ')[-_.]?(\\w)', 'i'), '$1 $2')); 584 | } 585 | return result; 586 | }); 587 | } 588 | 589 | /** 590 | * Resolves the version using an array of UA patterns. 591 | * 592 | * @private 593 | * @param {Array} patterns An array of UA patterns. 594 | * @returns {null|string} The detected version. 595 | */ 596 | function getVersion(patterns) { 597 | return reduce(patterns, function(result, pattern) { 598 | return result || (RegExp(pattern + 599 | '(?:-[\\d.]+/|(?: for [\\w-]+)?[ /-])([\\d.]+[^ ();/_-]*)', 'i').exec(ua) || 0)[1] || null; 600 | }); 601 | } 602 | 603 | /** 604 | * Returns `platform.description` when the platform object is coerced to a string. 605 | * 606 | * @name toString 607 | * @memberOf platform 608 | * @returns {string} Returns `platform.description` if available, else an empty string. 609 | */ 610 | function toStringPlatform() { 611 | return this.description || ''; 612 | } 613 | 614 | /*------------------------------------------------------------------------*/ 615 | 616 | // Convert layout to an array so we can add extra details. 617 | layout && (layout = [layout]); 618 | 619 | // Detect product names that contain their manufacturer's name. 620 | if (manufacturer && !product) { 621 | product = getProduct([manufacturer]); 622 | } 623 | // Clean up Google TV. 624 | if ((data = /\bGoogle TV\b/.exec(product))) { 625 | product = data[0]; 626 | } 627 | // Detect simulators. 628 | if (/\bSimulator\b/i.test(ua)) { 629 | product = (product ? product + ' ' : '') + 'Simulator'; 630 | } 631 | // Detect Opera Mini 8+ running in Turbo/Uncompressed mode on iOS. 632 | if (name == 'Opera Mini' && /\bOPiOS\b/.test(ua)) { 633 | description.push('running in Turbo/Uncompressed mode'); 634 | } 635 | // Detect IE Mobile 11. 636 | if (name == 'IE' && /\blike iPhone OS\b/.test(ua)) { 637 | data = parse(ua.replace(/like iPhone OS/, '')); 638 | manufacturer = data.manufacturer; 639 | product = data.product; 640 | } 641 | // Detect iOS. 642 | else if (/^iP/.test(product)) { 643 | name || (name = 'Safari'); 644 | os = 'iOS' + ((data = / OS ([\d_]+)/i.exec(ua)) 645 | ? ' ' + data[1].replace(/_/g, '.') 646 | : ''); 647 | } 648 | // Detect Kubuntu. 649 | else if (name == 'Konqueror' && !/buntu/i.test(os)) { 650 | os = 'Kubuntu'; 651 | } 652 | // Detect Android browsers. 653 | else if ((manufacturer && manufacturer != 'Google' && 654 | ((/Chrome/.test(name) && !/\bMobile Safari\b/i.test(ua)) || /\bVita\b/.test(product))) || 655 | (/\bAndroid\b/.test(os) && /^Chrome/.test(name) && /\bVersion\//i.test(ua))) { 656 | name = 'Android Browser'; 657 | os = /\bAndroid\b/.test(os) ? os : 'Android'; 658 | } 659 | // Detect Silk desktop/accelerated modes. 660 | else if (name == 'Silk') { 661 | if (!/\bMobi/i.test(ua)) { 662 | os = 'Android'; 663 | description.unshift('desktop mode'); 664 | } 665 | if (/Accelerated *= *true/i.test(ua)) { 666 | description.unshift('accelerated'); 667 | } 668 | } 669 | // Detect PaleMoon identifying as Firefox. 670 | else if (name == 'PaleMoon' && (data = /\bFirefox\/([\d.]+)\b/.exec(ua))) { 671 | description.push('identifying as Firefox ' + data[1]); 672 | } 673 | // Detect Firefox OS and products running Firefox. 674 | else if (name == 'Firefox' && (data = /\b(Mobile|Tablet|TV)\b/i.exec(ua))) { 675 | os || (os = 'Firefox OS'); 676 | product || (product = data[1]); 677 | } 678 | // Detect false positives for Firefox/Safari. 679 | else if (!name || (data = !/\bMinefield\b/i.test(ua) && /\b(?:Firefox|Safari)\b/.exec(name))) { 680 | // Escape the `/` for Firefox 1. 681 | if (name && !product && /[\/,]|^[^(]+?\)/.test(ua.slice(ua.indexOf(data + '/') + 8))) { 682 | // Clear name of false positives. 683 | name = null; 684 | } 685 | // Reassign a generic name. 686 | if ((data = product || manufacturer || os) && 687 | (product || manufacturer || /\b(?:Android|Symbian OS|Tablet OS|webOS)\b/.test(os))) { 688 | name = /[a-z]+(?: Hat)?/i.exec(/\bAndroid\b/.test(os) ? os : data) + ' Browser'; 689 | } 690 | } 691 | // Add Chrome version to description for Electron. 692 | else if (name == 'Electron' && (data = (/\bChrome\/([\d.]+)\b/.exec(ua) || 0)[1])) { 693 | description.push('Chromium ' + data); 694 | } 695 | // Detect non-Opera (Presto-based) versions (order is important). 696 | if (!version) { 697 | version = getVersion([ 698 | '(?:Cloud9|CriOS|CrMo|Edge|FxiOS|IEMobile|Iron|Opera ?Mini|OPiOS|OPR|Raven|SamsungBrowser|Silk(?!/[\\d.]+$))', 699 | 'Version', 700 | qualify(name), 701 | '(?:Firefox|Minefield|NetFront)' 702 | ]); 703 | } 704 | // Detect stubborn layout engines. 705 | if ((data = 706 | layout == 'iCab' && parseFloat(version) > 3 && 'WebKit' || 707 | /\bOpera\b/.test(name) && (/\bOPR\b/.test(ua) ? 'Blink' : 'Presto') || 708 | /\b(?:Midori|Nook|Safari)\b/i.test(ua) && !/^(?:Trident|EdgeHTML)$/.test(layout) && 'WebKit' || 709 | !layout && /\bMSIE\b/i.test(ua) && (os == 'Mac OS' ? 'Tasman' : 'Trident') || 710 | layout == 'WebKit' && /\bPlayStation\b(?! Vita\b)/i.test(name) && 'NetFront' 711 | )) { 712 | layout = [data]; 713 | } 714 | // Detect Windows Phone 7 desktop mode. 715 | if (name == 'IE' && (data = (/; *(?:XBLWP|ZuneWP)(\d+)/i.exec(ua) || 0)[1])) { 716 | name += ' Mobile'; 717 | os = 'Windows Phone ' + (/\+$/.test(data) ? data : data + '.x'); 718 | description.unshift('desktop mode'); 719 | } 720 | // Detect Windows Phone 8.x desktop mode. 721 | else if (/\bWPDesktop\b/i.test(ua)) { 722 | name = 'IE Mobile'; 723 | os = 'Windows Phone 8.x'; 724 | description.unshift('desktop mode'); 725 | version || (version = (/\brv:([\d.]+)/.exec(ua) || 0)[1]); 726 | } 727 | // Detect IE 11 identifying as other browsers. 728 | else if (name != 'IE' && layout == 'Trident' && (data = /\brv:([\d.]+)/.exec(ua))) { 729 | if (name) { 730 | description.push('identifying as ' + name + (version ? ' ' + version : '')); 731 | } 732 | name = 'IE'; 733 | version = data[1]; 734 | } 735 | // Leverage environment features. 736 | if (useFeatures) { 737 | // Detect server-side environments. 738 | // Rhino has a global function while others have a global object. 739 | if (isHostType(context, 'global')) { 740 | if (java) { 741 | data = java.lang.System; 742 | arch = data.getProperty('os.arch'); 743 | os = os || data.getProperty('os.name') + ' ' + data.getProperty('os.version'); 744 | } 745 | if (isModuleScope && isHostType(context, 'system') && (data = [context.system])[0]) { 746 | os || (os = data[0].os || null); 747 | try { 748 | data[1] = context.require('ringo/engine').version; 749 | version = data[1].join('.'); 750 | name = 'RingoJS'; 751 | } catch(e) { 752 | if (data[0].global.system == context.system) { 753 | name = 'Narwhal'; 754 | } 755 | } 756 | } 757 | else if ( 758 | typeof context.process == 'object' && !context.process.browser && 759 | (data = context.process) 760 | ) { 761 | if (typeof data.versions == 'object') { 762 | if (typeof data.versions.electron == 'string') { 763 | description.push('Node ' + data.versions.node); 764 | name = 'Electron'; 765 | version = data.versions.electron; 766 | } else if (typeof data.versions.nw == 'string') { 767 | description.push('Chromium ' + version, 'Node ' + data.versions.node); 768 | name = 'NW.js'; 769 | version = data.versions.nw; 770 | } 771 | } else { 772 | name = 'Node.js'; 773 | arch = data.arch; 774 | os = data.platform; 775 | version = /[\d.]+/.exec(data.version) 776 | version = version ? version[0] : 'unknown'; 777 | } 778 | } 779 | else if (rhino) { 780 | name = 'Rhino'; 781 | } 782 | } 783 | // Detect Adobe AIR. 784 | else if (getClassOf((data = context.runtime)) == airRuntimeClass) { 785 | name = 'Adobe AIR'; 786 | os = data.flash.system.Capabilities.os; 787 | } 788 | // Detect PhantomJS. 789 | else if (getClassOf((data = context.phantom)) == phantomClass) { 790 | name = 'PhantomJS'; 791 | version = (data = data.version || null) && (data.major + '.' + data.minor + '.' + data.patch); 792 | } 793 | // Detect IE compatibility modes. 794 | else if (typeof doc.documentMode == 'number' && (data = /\bTrident\/(\d+)/i.exec(ua))) { 795 | // We're in compatibility mode when the Trident version + 4 doesn't 796 | // equal the document mode. 797 | version = [version, doc.documentMode]; 798 | if ((data = +data[1] + 4) != version[1]) { 799 | description.push('IE ' + version[1] + ' mode'); 800 | layout && (layout[1] = ''); 801 | version[1] = data; 802 | } 803 | version = name == 'IE' ? String(version[1].toFixed(1)) : version[0]; 804 | } 805 | // Detect IE 11 masking as other browsers. 806 | else if (typeof doc.documentMode == 'number' && /^(?:Chrome|Firefox)\b/.test(name)) { 807 | description.push('masking as ' + name + ' ' + version); 808 | name = 'IE'; 809 | version = '11.0'; 810 | layout = ['Trident']; 811 | os = 'Windows'; 812 | } 813 | os = os && format(os); 814 | } 815 | // Detect prerelease phases. 816 | if (version && (data = 817 | /(?:[ab]|dp|pre|[ab]\d+pre)(?:\d+\+?)?$/i.exec(version) || 818 | /(?:alpha|beta)(?: ?\d)?/i.exec(ua + ';' + (useFeatures && nav.appMinorVersion)) || 819 | /\bMinefield\b/i.test(ua) && 'a' 820 | )) { 821 | prerelease = /b/i.test(data) ? 'beta' : 'alpha'; 822 | version = version.replace(RegExp(data + '\\+?$'), '') + 823 | (prerelease == 'beta' ? beta : alpha) + (/\d+\+?/.exec(data) || ''); 824 | } 825 | // Detect Firefox Mobile. 826 | if (name == 'Fennec' || name == 'Firefox' && /\b(?:Android|Firefox OS)\b/.test(os)) { 827 | name = 'Firefox Mobile'; 828 | } 829 | // Obscure Maxthon's unreliable version. 830 | else if (name == 'Maxthon' && version) { 831 | version = version.replace(/\.[\d.]+/, '.x'); 832 | } 833 | // Detect Xbox 360 and Xbox One. 834 | else if (/\bXbox\b/i.test(product)) { 835 | if (product == 'Xbox 360') { 836 | os = null; 837 | } 838 | if (product == 'Xbox 360' && /\bIEMobile\b/.test(ua)) { 839 | description.unshift('mobile mode'); 840 | } 841 | } 842 | // Add mobile postfix. 843 | else if ((/^(?:Chrome|IE|Opera)$/.test(name) || name && !product && !/Browser|Mobi/.test(name)) && 844 | (os == 'Windows CE' || /Mobi/i.test(ua))) { 845 | name += ' Mobile'; 846 | } 847 | // Detect IE platform preview. 848 | else if (name == 'IE' && useFeatures) { 849 | try { 850 | if (context.external === null) { 851 | description.unshift('platform preview'); 852 | } 853 | } catch(e) { 854 | description.unshift('embedded'); 855 | } 856 | } 857 | // Detect BlackBerry OS version. 858 | // http://docs.blackberry.com/en/developers/deliverables/18169/HTTP_headers_sent_by_BB_Browser_1234911_11.jsp 859 | else if ((/\bBlackBerry\b/.test(product) || /\bBB10\b/.test(ua)) && (data = 860 | (RegExp(product.replace(/ +/g, ' *') + '/([.\\d]+)', 'i').exec(ua) || 0)[1] || 861 | version 862 | )) { 863 | data = [data, /BB10/.test(ua)]; 864 | os = (data[1] ? (product = null, manufacturer = 'BlackBerry') : 'Device Software') + ' ' + data[0]; 865 | version = null; 866 | } 867 | // Detect Opera identifying/masking itself as another browser. 868 | // http://www.opera.com/support/kb/view/843/ 869 | else if (this != forOwn && product != 'Wii' && ( 870 | (useFeatures && opera) || 871 | (/Opera/.test(name) && /\b(?:MSIE|Firefox)\b/i.test(ua)) || 872 | (name == 'Firefox' && /\bOS X (?:\d+\.){2,}/.test(os)) || 873 | (name == 'IE' && ( 874 | (os && !/^Win/.test(os) && version > 5.5) || 875 | /\bWindows XP\b/.test(os) && version > 8 || 876 | version == 8 && !/\bTrident\b/.test(ua) 877 | )) 878 | ) && !reOpera.test((data = parse.call(forOwn, ua.replace(reOpera, '') + ';'))) && data.name) { 879 | // When "identifying", the UA contains both Opera and the other browser's name. 880 | data = 'ing as ' + data.name + ((data = data.version) ? ' ' + data : ''); 881 | if (reOpera.test(name)) { 882 | if (/\bIE\b/.test(data) && os == 'Mac OS') { 883 | os = null; 884 | } 885 | data = 'identify' + data; 886 | } 887 | // When "masking", the UA contains only the other browser's name. 888 | else { 889 | data = 'mask' + data; 890 | if (operaClass) { 891 | name = format(operaClass.replace(/([a-z])([A-Z])/g, '$1 $2')); 892 | } else { 893 | name = 'Opera'; 894 | } 895 | if (/\bIE\b/.test(data)) { 896 | os = null; 897 | } 898 | if (!useFeatures) { 899 | version = null; 900 | } 901 | } 902 | layout = ['Presto']; 903 | description.push(data); 904 | } 905 | // Detect WebKit Nightly and approximate Chrome/Safari versions. 906 | if ((data = (/\bAppleWebKit\/([\d.]+\+?)/i.exec(ua) || 0)[1])) { 907 | // Correct build number for numeric comparison. 908 | // (e.g. "532.5" becomes "532.05") 909 | data = [parseFloat(data.replace(/\.(\d)$/, '.0$1')), data]; 910 | // Nightly builds are postfixed with a "+". 911 | if (name == 'Safari' && data[1].slice(-1) == '+') { 912 | name = 'WebKit Nightly'; 913 | prerelease = 'alpha'; 914 | version = data[1].slice(0, -1); 915 | } 916 | // Clear incorrect browser versions. 917 | else if (version == data[1] || 918 | version == (data[2] = (/\bSafari\/([\d.]+\+?)/i.exec(ua) || 0)[1])) { 919 | version = null; 920 | } 921 | // Use the full Chrome version when available. 922 | data[1] = (/\bChrome\/([\d.]+)/i.exec(ua) || 0)[1]; 923 | // Detect Blink layout engine. 924 | if (data[0] == 537.36 && data[2] == 537.36 && parseFloat(data[1]) >= 28 && layout == 'WebKit') { 925 | layout = ['Blink']; 926 | } 927 | // Detect JavaScriptCore. 928 | // http://stackoverflow.com/questions/6768474/how-can-i-detect-which-javascript-engine-v8-or-jsc-is-used-at-runtime-in-androi 929 | if (!useFeatures || (!likeChrome && !data[1])) { 930 | layout && (layout[1] = 'like Safari'); 931 | data = (data = data[0], data < 400 ? 1 : data < 500 ? 2 : data < 526 ? 3 : data < 533 ? 4 : data < 534 ? '4+' : data < 535 ? 5 : data < 537 ? 6 : data < 538 ? 7 : data < 601 ? 8 : '8'); 932 | } else { 933 | layout && (layout[1] = 'like Chrome'); 934 | data = data[1] || (data = data[0], data < 530 ? 1 : data < 532 ? 2 : data < 532.05 ? 3 : data < 533 ? 4 : data < 534.03 ? 5 : data < 534.07 ? 6 : data < 534.10 ? 7 : data < 534.13 ? 8 : data < 534.16 ? 9 : data < 534.24 ? 10 : data < 534.30 ? 11 : data < 535.01 ? 12 : data < 535.02 ? '13+' : data < 535.07 ? 15 : data < 535.11 ? 16 : data < 535.19 ? 17 : data < 536.05 ? 18 : data < 536.10 ? 19 : data < 537.01 ? 20 : data < 537.11 ? '21+' : data < 537.13 ? 23 : data < 537.18 ? 24 : data < 537.24 ? 25 : data < 537.36 ? 26 : layout != 'Blink' ? '27' : '28'); 935 | } 936 | // Add the postfix of ".x" or "+" for approximate versions. 937 | layout && (layout[1] += ' ' + (data += typeof data == 'number' ? '.x' : /[.+]/.test(data) ? '' : '+')); 938 | // Obscure version for some Safari 1-2 releases. 939 | if (name == 'Safari' && (!version || parseInt(version) > 45)) { 940 | version = data; 941 | } 942 | } 943 | // Detect Opera desktop modes. 944 | if (name == 'Opera' && (data = /\bzbov|zvav$/.exec(os))) { 945 | name += ' '; 946 | description.unshift('desktop mode'); 947 | if (data == 'zvav') { 948 | name += 'Mini'; 949 | version = null; 950 | } else { 951 | name += 'Mobile'; 952 | } 953 | os = os.replace(RegExp(' *' + data + '$'), ''); 954 | } 955 | // Detect Chrome desktop mode. 956 | else if (name == 'Safari' && /\bChrome\b/.exec(layout && layout[1])) { 957 | description.unshift('desktop mode'); 958 | name = 'Chrome Mobile'; 959 | version = null; 960 | 961 | if (/\bOS X\b/.test(os)) { 962 | manufacturer = 'Apple'; 963 | os = 'iOS 4.3+'; 964 | } else { 965 | os = null; 966 | } 967 | } 968 | // Strip incorrect OS versions. 969 | if (version && version.indexOf((data = /[\d.]+$/.exec(os))) == 0 && 970 | ua.indexOf('/' + data + '-') > -1) { 971 | os = trim(os.replace(data, '')); 972 | } 973 | // Add layout engine. 974 | if (layout && !/\b(?:Avant|Nook)\b/.test(name) && ( 975 | /Browser|Lunascape|Maxthon/.test(name) || 976 | name != 'Safari' && /^iOS/.test(os) && /\bSafari\b/.test(layout[1]) || 977 | /^(?:Adobe|Arora|Breach|Midori|Opera|Phantom|Rekonq|Rock|Samsung Internet|Sleipnir|Web)/.test(name) && layout[1])) { 978 | // Don't add layout details to description if they are falsey. 979 | (data = layout[layout.length - 1]) && description.push(data); 980 | } 981 | // Combine contextual information. 982 | if (description.length) { 983 | description = ['(' + description.join('; ') + ')']; 984 | } 985 | // Append manufacturer to description. 986 | if (manufacturer && product && product.indexOf(manufacturer) < 0) { 987 | description.push('on ' + manufacturer); 988 | } 989 | // Append product to description. 990 | if (product) { 991 | description.push((/^on /.test(description[description.length - 1]) ? '' : 'on ') + product); 992 | } 993 | // Parse the OS into an object. 994 | if (os) { 995 | data = / ([\d.+]+)$/.exec(os); 996 | isSpecialCasedOS = data && os.charAt(os.length - data[0].length - 1) == '/'; 997 | os = { 998 | 'architecture': 32, 999 | 'family': (data && !isSpecialCasedOS) ? os.replace(data[0], '') : os, 1000 | 'version': data ? data[1] : null, 1001 | 'toString': function() { 1002 | var version = this.version; 1003 | return this.family + ((version && !isSpecialCasedOS) ? ' ' + version : '') + (this.architecture == 64 ? ' 64-bit' : ''); 1004 | } 1005 | }; 1006 | } 1007 | // Add browser/OS architecture. 1008 | if ((data = /\b(?:AMD|IA|Win|WOW|x86_|x)64\b/i.exec(arch)) && !/\bi686\b/i.test(arch)) { 1009 | if (os) { 1010 | os.architecture = 64; 1011 | os.family = os.family.replace(RegExp(' *' + data), ''); 1012 | } 1013 | if ( 1014 | name && (/\bWOW64\b/i.test(ua) || 1015 | (useFeatures && /\w(?:86|32)$/.test(nav.cpuClass || nav.platform) && !/\bWin64; x64\b/i.test(ua))) 1016 | ) { 1017 | description.unshift('32-bit'); 1018 | } 1019 | } 1020 | // Chrome 39 and above on OS X is always 64-bit. 1021 | else if ( 1022 | os && /^OS X/.test(os.family) && 1023 | name == 'Chrome' && parseFloat(version) >= 39 1024 | ) { 1025 | os.architecture = 64; 1026 | } 1027 | 1028 | ua || (ua = null); 1029 | 1030 | /*------------------------------------------------------------------------*/ 1031 | 1032 | /** 1033 | * The platform object. 1034 | * 1035 | * @name platform 1036 | * @type Object 1037 | */ 1038 | var platform = {}; 1039 | 1040 | /** 1041 | * The platform description. 1042 | * 1043 | * @memberOf platform 1044 | * @type string|null 1045 | */ 1046 | platform.description = ua; 1047 | 1048 | /** 1049 | * The name of the browser's layout engine. 1050 | * 1051 | * The list of common layout engines include: 1052 | * "Blink", "EdgeHTML", "Gecko", "Trident" and "WebKit" 1053 | * 1054 | * @memberOf platform 1055 | * @type string|null 1056 | */ 1057 | platform.layout = layout && layout[0]; 1058 | 1059 | /** 1060 | * The name of the product's manufacturer. 1061 | * 1062 | * The list of manufacturers include: 1063 | * "Apple", "Archos", "Amazon", "Asus", "Barnes & Noble", "BlackBerry", 1064 | * "Google", "HP", "HTC", "LG", "Microsoft", "Motorola", "Nintendo", 1065 | * "Nokia", "Samsung" and "Sony" 1066 | * 1067 | * @memberOf platform 1068 | * @type string|null 1069 | */ 1070 | platform.manufacturer = manufacturer; 1071 | 1072 | /** 1073 | * The name of the browser/environment. 1074 | * 1075 | * The list of common browser names include: 1076 | * "Chrome", "Electron", "Firefox", "Firefox for iOS", "IE", 1077 | * "Microsoft Edge", "PhantomJS", "Safari", "SeaMonkey", "Silk", 1078 | * "Opera Mini" and "Opera" 1079 | * 1080 | * Mobile versions of some browsers have "Mobile" appended to their name: 1081 | * eg. "Chrome Mobile", "Firefox Mobile", "IE Mobile" and "Opera Mobile" 1082 | * 1083 | * @memberOf platform 1084 | * @type string|null 1085 | */ 1086 | platform.name = name; 1087 | 1088 | /** 1089 | * The alpha/beta release indicator. 1090 | * 1091 | * @memberOf platform 1092 | * @type string|null 1093 | */ 1094 | platform.prerelease = prerelease; 1095 | 1096 | /** 1097 | * The name of the product hosting the browser. 1098 | * 1099 | * The list of common products include: 1100 | * 1101 | * "BlackBerry", "Galaxy S4", "Lumia", "iPad", "iPod", "iPhone", "Kindle", 1102 | * "Kindle Fire", "Nexus", "Nook", "PlayBook", "TouchPad" and "Transformer" 1103 | * 1104 | * @memberOf platform 1105 | * @type string|null 1106 | */ 1107 | platform.product = product; 1108 | 1109 | /** 1110 | * The browser's user agent string. 1111 | * 1112 | * @memberOf platform 1113 | * @type string|null 1114 | */ 1115 | platform.ua = ua; 1116 | 1117 | /** 1118 | * The browser/environment version. 1119 | * 1120 | * @memberOf platform 1121 | * @type string|null 1122 | */ 1123 | platform.version = name && version; 1124 | 1125 | /** 1126 | * The name of the operating system. 1127 | * 1128 | * @memberOf platform 1129 | * @type Object 1130 | */ 1131 | platform.os = os || { 1132 | 1133 | /** 1134 | * The CPU architecture the OS is built for. 1135 | * 1136 | * @memberOf platform.os 1137 | * @type number|null 1138 | */ 1139 | 'architecture': null, 1140 | 1141 | /** 1142 | * The family of the OS. 1143 | * 1144 | * Common values include: 1145 | * "Windows", "Windows Server 2008 R2 / 7", "Windows Server 2008 / Vista", 1146 | * "Windows XP", "OS X", "Ubuntu", "Debian", "Fedora", "Red Hat", "SuSE", 1147 | * "Android", "iOS" and "Windows Phone" 1148 | * 1149 | * @memberOf platform.os 1150 | * @type string|null 1151 | */ 1152 | 'family': null, 1153 | 1154 | /** 1155 | * The version of the OS. 1156 | * 1157 | * @memberOf platform.os 1158 | * @type string|null 1159 | */ 1160 | 'version': null, 1161 | 1162 | /** 1163 | * Returns the OS string. 1164 | * 1165 | * @memberOf platform.os 1166 | * @returns {string} The OS string. 1167 | */ 1168 | 'toString': function() { return 'null'; } 1169 | }; 1170 | 1171 | platform.parse = parse; 1172 | platform.toString = toStringPlatform; 1173 | 1174 | if (platform.version) { 1175 | description.unshift(version); 1176 | } 1177 | if (platform.name) { 1178 | description.unshift(name); 1179 | } 1180 | if (os && name && !(os == String(os).split(' ')[0] && (os == name.split(' ')[0] || product))) { 1181 | description.push(product ? '(' + os + ')' : 'on ' + os); 1182 | } 1183 | if (description.length) { 1184 | platform.description = description.join(' '); 1185 | } 1186 | return platform; 1187 | } 1188 | 1189 | /*--------------------------------------------------------------------------*/ 1190 | 1191 | // Export platform. 1192 | var platform = parse(); 1193 | 1194 | // Some AMD build optimizers, like r.js, check for condition patterns like the following: 1195 | if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { 1196 | // Expose platform on the global object to prevent errors when platform is 1197 | // loaded by a script tag in the presence of an AMD loader. 1198 | // See http://requirejs.org/docs/errors.html#mismatch for more details. 1199 | root.platform = platform; 1200 | 1201 | // Define as an anonymous module so platform can be aliased through path mapping. 1202 | define(function() { 1203 | return platform; 1204 | }); 1205 | } 1206 | // Check for `exports` after `define` in case a build optimizer adds an `exports` object. 1207 | else if (freeExports && freeModule) { 1208 | // Export for CommonJS support. 1209 | forOwn(platform, function(value, key) { 1210 | freeExports[key] = value; 1211 | }); 1212 | } 1213 | else { 1214 | // Export to the global object. 1215 | root.platform = platform; 1216 | } 1217 | }.call(this)); 1218 | -------------------------------------------------------------------------------- /bower_components/platform.js/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | docdown@~0.7.2: 6 | version "0.7.2" 7 | resolved "https://registry.yarnpkg.com/docdown/-/docdown-0.7.2.tgz#f8c68f5836974860e5bb1802bcac52ba8a3ff677" 8 | dependencies: 9 | doctrine "^2.0.0" 10 | lodash "^4.17.2" 11 | 12 | doctrine@^2.0.0: 13 | version "2.0.0" 14 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" 15 | dependencies: 16 | esutils "^2.0.2" 17 | isarray "^1.0.0" 18 | 19 | esutils@^2.0.2: 20 | version "2.0.2" 21 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 22 | 23 | isarray@^1.0.0: 24 | version "1.0.0" 25 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 26 | 27 | lodash@^4.17.2: 28 | version "4.17.4" 29 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" 30 | 31 | qunit-extras@^1.5.0: 32 | version "1.5.0" 33 | resolved "https://registry.yarnpkg.com/qunit-extras/-/qunit-extras-1.5.0.tgz#a64d1c5088ab20c01c0e1b04c72132c397b3964c" 34 | 35 | qunitjs@^1.23.1: 36 | version "1.23.1" 37 | resolved "https://registry.yarnpkg.com/qunitjs/-/qunitjs-1.23.1.tgz#1971cf97ac9be01a64d2315508d2e48e6fd4e719" 38 | 39 | requirejs@^2.3.3: 40 | version "2.3.3" 41 | resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.3.tgz#aa59fd3a0287eaf407959a138228044b5dd6a6a3" 42 | -------------------------------------------------------------------------------- /config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | DayBook 4 | 一款简约的流水账app 5 | Ionic Framework Team 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /ionic.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "记一笔", 3 | "app_id": "", 4 | "v2": true, 5 | "typescript": true 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionic-hello-world", 3 | "author": "Ionic Framework", 4 | "homepage": "http://ionicframework.com/", 5 | "private": true, 6 | "scripts": { 7 | "clean": "ionic-app-scripts clean", 8 | "build": "ionic-app-scripts build", 9 | "ionic:build": "ionic-app-scripts build", 10 | "ionic:serve": "ionic-app-scripts serve" 11 | }, 12 | "dependencies": { 13 | "@angular/common": "4.0.0", 14 | "@angular/compiler": "4.0.0", 15 | "@angular/compiler-cli": "4.0.0", 16 | "@angular/core": "4.0.0", 17 | "@angular/forms": "4.0.0", 18 | "@angular/http": "4.0.0", 19 | "@angular/platform-browser": "4.0.0", 20 | "@angular/platform-browser-dynamic": "4.0.0", 21 | "@ionic-native/core": "^3.6.1", 22 | "@ionic-native/dialogs": "^3.6.1", 23 | "@ionic-native/splash-screen": "3.4.2", 24 | "@ionic-native/status-bar": "3.4.2", 25 | "@ionic/storage": "2.0.1", 26 | "chart.js": "^2.5.0", 27 | "ionic-angular": "3.0.1", 28 | "ionicons": "3.0.0", 29 | "ng2-charts": "^1.5.0", 30 | "rxjs": "5.1.1", 31 | "sw-toolbox": "3.4.0", 32 | "ts-md5": "^1.2.0", 33 | "zone.js": "^0.8.4" 34 | }, 35 | "devDependencies": { 36 | "@ionic/app-scripts": "1.3.0", 37 | "typescript": "~2.2.1" 38 | }, 39 | "cordovaPlugins": [ 40 | "cordova-plugin-console", 41 | "cordova-plugin-whitelist", 42 | "cordova-plugin-device", 43 | "cordova-plugin-statusbar", 44 | "cordova-plugin-splashscreen", 45 | "ionic-plugin-keyboard" 46 | ], 47 | "cordovaPlatforms": [ 48 | "ios", 49 | { 50 | "platform": "ios", 51 | "version": "", 52 | "locator": "ios" 53 | } 54 | ], 55 | "description": "DayBookApp: An Ionic project" 56 | } 57 | -------------------------------------------------------------------------------- /resources/android/icon/drawable-hdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/icon/drawable-hdpi-icon.png -------------------------------------------------------------------------------- /resources/android/icon/drawable-ldpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/icon/drawable-ldpi-icon.png -------------------------------------------------------------------------------- /resources/android/icon/drawable-mdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/icon/drawable-mdpi-icon.png -------------------------------------------------------------------------------- /resources/android/icon/drawable-xhdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/icon/drawable-xhdpi-icon.png -------------------------------------------------------------------------------- /resources/android/icon/drawable-xxhdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/icon/drawable-xxhdpi-icon.png -------------------------------------------------------------------------------- /resources/android/icon/drawable-xxxhdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/icon/drawable-xxxhdpi-icon.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-hdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/splash/drawable-land-hdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-ldpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/splash/drawable-land-ldpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-mdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/splash/drawable-land-mdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-xhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/splash/drawable-land-xhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-xxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/splash/drawable-land-xxhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-land-xxxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/splash/drawable-land-xxxhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-hdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/splash/drawable-port-hdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-ldpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/splash/drawable-port-ldpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-mdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/splash/drawable-port-mdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-xhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/splash/drawable-port-xhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-xxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/splash/drawable-port-xxhdpi-screen.png -------------------------------------------------------------------------------- /resources/android/splash/drawable-port-xxxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/android/splash/drawable-port-xxxhdpi-screen.png -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/icon.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-40.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-40@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-40@3x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-50.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-50@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-60.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-60@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-60@3x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-72.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-72@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-76.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-76@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-83.5@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-small.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-small@2x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon-small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon-small@3x.png -------------------------------------------------------------------------------- /resources/ios/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon.png -------------------------------------------------------------------------------- /resources/ios/icon/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/icon/icon@2x.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-568h@2x~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/splash/Default-568h@2x~iphone.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-667h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/splash/Default-667h.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/splash/Default-736h.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Landscape-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/splash/Default-Landscape-736h.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Landscape@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/splash/Default-Landscape@2x~ipad.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Landscape~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/splash/Default-Landscape~ipad.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Portrait@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/splash/Default-Portrait@2x~ipad.png -------------------------------------------------------------------------------- /resources/ios/splash/Default-Portrait~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/splash/Default-Portrait~ipad.png -------------------------------------------------------------------------------- /resources/ios/splash/Default@2x~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/splash/Default@2x~iphone.png -------------------------------------------------------------------------------- /resources/ios/splash/Default~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/ios/splash/Default~iphone.png -------------------------------------------------------------------------------- /resources/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/resources/splash.png -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from '@angular/core'; 2 | 3 | import { Platform, MenuController, Nav, ToastController, Events, ModalController } from 'ionic-angular'; 4 | 5 | import { AddBillPage } from '../pages/add-bill/add-bill'; 6 | import { SearchBill } from '../pages/search-bill/search-bill'; 7 | 8 | import { StatusBar } from '@ionic-native/status-bar'; 9 | import { SplashScreen } from '@ionic-native/splash-screen'; 10 | import { Constants } from "../domain/Constants"; 11 | import { User } from "../domain/User"; 12 | import { LoginPage } from "../pages/login-page/login-page"; 13 | import { StorageService } from "../providers/storage-service"; 14 | import { HttpService } from "../providers/http-service"; 15 | 16 | @Component({ 17 | selector: 'app', 18 | templateUrl: 'app.html' 19 | }) 20 | export class MyApp { 21 | @ViewChild(Nav) nav: Nav; 22 | // make AddBill the root (or first) page 23 | rootPage = AddBillPage; 24 | backButtonPressed: boolean = false; //用于判断返回键是否触发 25 | pages: Array<{title: string, component: any}>; 26 | hiddenPages: Array<{title: string, component: any}>; 27 | curr_user:User; 28 | constructor( 29 | public platform: Platform, 30 | public menu: MenuController, 31 | public statusBar: StatusBar, 32 | public splashScreen: SplashScreen, 33 | public toastCtrl: ToastController, 34 | public events :Events, 35 | public modalCtrl: ModalController, 36 | public storageService:StorageService, 37 | public httpService:HttpService 38 | ) { 39 | this.initializeApp(); 40 | 41 | // set our app's pages 42 | this.pages = [ 43 | { title: '记一笔', component: AddBillPage }, 44 | { title: '月账单', component: SearchBill }, 45 | ]; 46 | this.initEvent(); 47 | } 48 | initEvent(){ 49 | //set curr_user after login 50 | this.events.subscribe(Constants.CURR_USER,user => this.curr_user = user ); 51 | //set swipe enabled 52 | // this.events.subscribe(Constants.SWIPE_ENABLE,val => this.menu.swipeEnable(val)); 53 | } 54 | 55 | 56 | initializeApp() { 57 | this.platform.ready().then(() => { 58 | 59 | this.statusBar.styleDefault(); 60 | this.splashScreen.hide(); 61 | //注册返回按键事件 62 | this.registerBackButtonAction(); 63 | //登录认证 64 | this.authentication(); 65 | 66 | this.initBillType(); 67 | }); 68 | } 69 | registerBackButtonAction(){ 70 | this.platform.registerBackButtonAction((): any => { 71 | let activeVC = this.nav.getActive(); 72 | let page = activeVC.instance; 73 | if(this.menu.isOpen()){ 74 | this.menu.close(); 75 | return; 76 | } 77 | //当前页面非tab栏 78 | if (!this.nav.canGoBack() || page instanceof LoginPage) { 79 | return this.showExit(); 80 | } 81 | return this.nav.pop(); 82 | }, 101); 83 | } 84 | initBillType(){ 85 | let billtypes = this.storageService.read(Constants.BILL_TYPE); 86 | if(billtypes!=null) return; 87 | this.httpService.httpGetWithAuth("common/dictionary?typeid=1") 88 | .then(result =>{ 89 | if(result.status==-1){ 90 | this.httpService.alert(result.msg); 91 | return; 92 | } 93 | let billTypes = result.object; 94 | this.storageService.write(Constants.BILL_TYPE,billTypes); 95 | }) 96 | .catch(error =>{ 97 | console.log(error); 98 | }); 99 | } 100 | openPage(page) { 101 | this.menu.close(); 102 | this.nav.setRoot(page.component); 103 | } 104 | authentication(){ 105 | if(!this.storageService.read(Constants.CURR_USER)){ 106 | let modal = this.modalCtrl.create(LoginPage); 107 | modal.present(); 108 | } 109 | } 110 | showExit() { 111 | if (this.backButtonPressed) this.platform.exitApp(); 112 | else { 113 | let toast = this.toastCtrl.create({ 114 | message: '再按一次退出应用', 115 | duration: 2000, 116 | position: 'bottom' 117 | }); 118 | toast.present(); 119 | this.backButtonPressed = true; 120 | setTimeout(() => { 121 | this.backButtonPressed = false; 122 | }, 2000) 123 | } 124 | } 125 | logout(){ 126 | this.menu.close(); 127 | let activeVC = this.nav.getActive(); 128 | let page = activeVC.instance; 129 | if(!(page instanceof AddBillPage)){ 130 | this.nav.setRoot(this.rootPage); 131 | } 132 | this.storageService.remove(Constants.CURR_USER); 133 | this.authentication(); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/app/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {{p.title}} 16 | 17 | 18 | 退出登录 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule, ErrorHandler } from '@angular/core'; 3 | import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular'; 4 | import { MyApp } from './app.component'; 5 | import { AddBillPage } from '../pages/add-bill/add-bill'; 6 | import { HttpModule, JsonpModule } from '@angular/http'; 7 | import { HttpService } from '../providers/http-service'; 8 | import {ChartsModule} from 'ng2-charts/charts/charts'; 9 | import '../../node_modules/chart.js/dist/Chart.bundle.min.js'; 10 | 11 | import { BookService } from '../service/BookService'; 12 | import { SaveBillPage} from '../pages/save-bill/save-bill'; 13 | import { SearchBill} from '../pages/search-bill/search-bill'; 14 | import { ResultsBill } from '../pages/results-bill/results-bill'; 15 | 16 | import { StatusBar } from '@ionic-native/status-bar'; 17 | import { SplashScreen } from '@ionic-native/splash-screen'; 18 | import { DatePipe } from '@angular/common'; 19 | import { StorageService } from "../providers/storage-service"; 20 | import { LoginPage } from "../pages/login-page/login-page"; 21 | import { Dialogs } from "@ionic-native/dialogs"; 22 | import { StickyDirevtive } from "../components/sticky-direvtive/sticky-direvtive"; 23 | import { MonthPopPage } from "../pages/month-pop-page/month-pop-page"; 24 | import { IchartPage } from "../pages/ichart-page/ichart-page"; 25 | @NgModule({ 26 | declarations: [ 27 | MyApp, 28 | AddBillPage, 29 | SaveBillPage, 30 | SearchBill, 31 | ResultsBill, 32 | LoginPage, 33 | MonthPopPage, 34 | StickyDirevtive, 35 | IchartPage 36 | ], 37 | imports: [ 38 | BrowserModule, 39 | IonicModule.forRoot(MyApp), 40 | HttpModule, 41 | JsonpModule, 42 | ChartsModule 43 | 44 | ], 45 | bootstrap: [IonicApp], 46 | entryComponents: [ 47 | MyApp, 48 | AddBillPage, 49 | SaveBillPage, 50 | SearchBill, 51 | ResultsBill, 52 | LoginPage, 53 | MonthPopPage, 54 | IchartPage 55 | ], 56 | providers: [ 57 | StatusBar, 58 | SplashScreen, 59 | DatePipe, 60 | HttpService, 61 | BookService, 62 | StorageService, 63 | Dialogs, 64 | {provide: ErrorHandler, useClass: IonicErrorHandler} 65 | ] 66 | }) 67 | export class AppModule {} 68 | -------------------------------------------------------------------------------- /src/app/app.scss: -------------------------------------------------------------------------------- 1 | // http://ionicframework.com/docs/v2/theming/ 2 | 3 | 4 | // App Global Sass 5 | // -------------------------------------------------- 6 | // Put style rules here that you want to apply globally. These 7 | // styles are for the entire app and not just one component. 8 | // Additionally, this file can be also used as an entry point 9 | // to import other Sass files to be included in the output CSS. 10 | // 11 | // Shared Sass variables, which can be used to adjust Ionic's 12 | // default Sass variables, belong in "theme/variables.scss". 13 | // 14 | // To declare rules for a specific mode, create a child rule 15 | // for the .md, .ios, or .wp mode classes. The mode class is 16 | // automatically applied to the element in the app. 17 | app{ 18 | .bg{ 19 | position: absolute; 20 | bottom: 0; 21 | right:10px; 22 | width: 60px; 23 | bottom:0; 24 | 25 | } 26 | .avator{ 27 | width:6em; 28 | height:6em; 29 | border-radius: 50%; 30 | overflow: hidden; 31 | } 32 | } -------------------------------------------------------------------------------- /src/app/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app.module'; 4 | 5 | platformBrowserDynamic().bootstrapModule(AppModule); 6 | -------------------------------------------------------------------------------- /src/assets/icon/avator.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/src/assets/icon/avator.jpg -------------------------------------------------------------------------------- /src/assets/icon/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/src/assets/icon/bg.jpg -------------------------------------------------------------------------------- /src/assets/icon/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/src/assets/icon/bg.png -------------------------------------------------------------------------------- /src/assets/icon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/src/assets/icon/favicon.ico -------------------------------------------------------------------------------- /src/components/sticky-direvtive/sticky-direvtive.ts: -------------------------------------------------------------------------------- 1 | import { Directive } from '@angular/core'; 2 | 3 | 4 | @Directive({ 5 | selector: '[sticky-direvtive]' // Attribute selector 6 | }) 7 | export class StickyDirevtive { 8 | 9 | constructor() { 10 | console.log('Hello StickyDirevtive Directive'); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/declarations.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Declaration files are how the Typescript compiler knows about the type information(or shape) of an object. 3 | They're what make intellisense work and make Typescript know all about your code. 4 | 5 | A wildcard module is declared below to allow third party libraries to be used in an app even if they don't 6 | provide their own type declarations. 7 | 8 | To learn more about using third party libraries in an Ionic app, check out the docs here: 9 | http://ionicframework.com/docs/v2/resources/third-party-libs/ 10 | 11 | For more info on type definition files, check out the Typescript docs here: 12 | https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html 13 | */ 14 | declare module '*'; -------------------------------------------------------------------------------- /src/domain/Book.ts: -------------------------------------------------------------------------------- 1 | export class Book{ 2 | public id:number; 3 | public typeid:number; 4 | public val:number; 5 | public credate:string; 6 | public remark:string; 7 | } -------------------------------------------------------------------------------- /src/domain/Constants.ts: -------------------------------------------------------------------------------- 1 | export const Constants = { 2 | BILL_TYPE :"BILL_TYPE", 3 | /**请求Header 中的权限 */ 4 | AUTHORIZATION :"authorization", 5 | /**当前登录用户 */ 6 | CURR_USER: "CURR_USER", 7 | /**登录用户的标示 */ 8 | HEADER_USER: "APP_USER_ID", 9 | /**可滑动 */ 10 | SWIPE_ENABLE: "SWIPE_ENABLE" 11 | 12 | } -------------------------------------------------------------------------------- /src/domain/Dictionary.ts: -------------------------------------------------------------------------------- 1 | export class Dictionary{ 2 | constructor( 3 | public id:number, 4 | public name:string, 5 | public typeid:number, 6 | public icon:string 7 | ){} 8 | } -------------------------------------------------------------------------------- /src/domain/QueryBook.ts: -------------------------------------------------------------------------------- 1 | export class QueryBook{ 2 | constructor(public id :number, 3 | public typeid :number, 4 | public name :string, 5 | public credate :string, 6 | public val :number, 7 | public orderby :number, 8 | public icon :string, 9 | public remark :string 10 | ){} 11 | } -------------------------------------------------------------------------------- /src/domain/RestEntity.ts: -------------------------------------------------------------------------------- 1 | export class RestEntity{ 2 | constructor( 3 | public errorCode:string, 4 | public status:number, 5 | public msg:string, 6 | public object:any 7 | ){} 8 | } -------------------------------------------------------------------------------- /src/domain/User.ts: -------------------------------------------------------------------------------- 1 | export class User { 2 | public id:number; 3 | public name:string; 4 | public alias:string; 5 | public token?:string; 6 | constructor(){} 7 | } -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ionic App 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Ionic", 3 | "short_name": "Ionic", 4 | "start_url": "index.html", 5 | "display": "standalone", 6 | "icons": [{ 7 | "src": "assets/imgs/logo.png", 8 | "sizes": "512x512", 9 | "type": "image/png" 10 | }], 11 | "background_color": "#4e8ef7", 12 | "theme_color": "#4e8ef7" 13 | } -------------------------------------------------------------------------------- /src/pages/add-bill/add-bill.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 记一笔 7 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 本月支出(元) 25 | 26 | 27 | 28 | 29 | ¥{{amount | number :'.2-2'}} 30 | 31 | 32 | 33 | 3500" class="icon_mood"> 34 | 35 | 36 | 37 | 38 | 本月剩余 {{4000-amount | number:'.2-2'}} 3500" name="flash"> 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 记一笔账 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | {{item.title}} 66 | 67 | 68 | 69 | {{item.amount | number:'.2-2'}} 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/pages/add-bill/add-bill.scss: -------------------------------------------------------------------------------- 1 | page-add-bill { 2 | .showamount{ 3 | background:#3e425b; 4 | padding:1em; 5 | ion-item{ 6 | background:transparent; 7 | border:0; 8 | padding:0; 9 | margin:0; 10 | height:1em; 11 | line-height: 1em; 12 | 13 | } 14 | p{ 15 | color:#FFF; 16 | } 17 | p.show_p{ 18 | font-size:0.7em; 19 | margin:0; 20 | padding:.2em 0; 21 | } 22 | p.amount{ 23 | font-size:2.4em; 24 | margin:0; 25 | padding:0; 26 | margin-left:-0.4em; 27 | font-weight:bold; 28 | } 29 | p.deposit{ 30 | margin:0; 31 | padding:.2em 0; 32 | font-size:0.7em 33 | } 34 | ion-row{ 35 | padding-top:0; 36 | padding-bottom: 0; 37 | ion-col{ 38 | padding-top:0; 39 | padding-bottom: 0; 40 | } 41 | .icon_mood{ 42 | color:#fff; 43 | font-size: 2em; 44 | margin-top:0.4em 45 | } 46 | } 47 | } 48 | 49 | .add_row{ 50 | .add_btn{ 51 | width:100%; 52 | font-weight:bold; 53 | } 54 | } 55 | .list_row{ 56 | *{ 57 | color:#555; 58 | } 59 | ion-avatar { 60 | ion-icon{ 61 | font-size:2em; 62 | color:darkgoldenrod 63 | } 64 | } 65 | button{ 66 | padding-left:0; 67 | } 68 | .item_right p{ 69 | text-align:right; 70 | } 71 | ion-icon{ 72 | color: #f53d3d; 73 | } 74 | .item_title{ 75 | font-size:0.9em; 76 | font-weight:bold; 77 | } 78 | .item_amount{ 79 | font-weight:bold; 80 | font-size:0.9em; 81 | } 82 | .item_time{ 83 | color:#999; 84 | font-size:0.7em; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/pages/add-bill/add-bill.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import { LoadingController, NavController, Events } from 'ionic-angular'; 4 | 5 | import { BookService } from '../../service/BookService'; 6 | import { HttpService } from '../../providers/http-service'; 7 | import { SaveBillPage } from '../../pages/save-bill/save-bill' 8 | import { Constants } from "../../domain/Constants"; 9 | import { QueryBook } from "../../domain/QueryBook"; 10 | import { ResultsBill } from "../results-bill/results-bill"; 11 | import { DateUtils } from "../../utils/date-utils"; 12 | @Component({ 13 | selector: 'page-add-bill', 14 | templateUrl: 'add-bill.html' 15 | }) 16 | /**首页面 */ 17 | export class AddBillPage { 18 | amounts:Array<{type:number,amount:number,title:string,icon?:string,averageDay?:number}>; 19 | amount:number; 20 | books:Array; 21 | constructor(public bookService: BookService, 22 | public httpService: HttpService, 23 | public loadingCtrl: LoadingController, 24 | public navCtrl: NavController, 25 | public events: Events) { 26 | this.initEvent(); 27 | } 28 | 29 | initEvent(){ 30 | this.events.subscribe("LOGIN_SUCCESS",()=>this.init() ); 31 | } 32 | 33 | ionViewDidEnter() { 34 | if(this.httpService.getCurrUser()){ 35 | this.init(); 36 | } 37 | } 38 | ionViewDidLeave() { 39 | console.log('ionViewDidLeave'); 40 | } 41 | ionViewDidLoad() { 42 | 43 | } 44 | init() { 45 | let loader = this.httpService.loading(); 46 | loader.present(); 47 | this.bookService.findAll() 48 | .catch(error => loader.dismiss()) 49 | .then(restEntity =>{ 50 | loader.dismiss(); 51 | if(restEntity.status==-1){ 52 | this.httpService.alert(restEntity.msg); 53 | return; 54 | } 55 | this.books = restEntity.object.results; 56 | this.amounts = this.bookService.calAmount(this.books); 57 | if(this.amounts==null) return; 58 | this.amount = this.amounts[2].amount; 59 | }); 60 | 61 | } 62 | addBill() { 63 | this.navCtrl.push(SaveBillPage); 64 | } 65 | /**跳转到月账单 */ 66 | amountClick(type:number){ 67 | let start = DateUtils.getStrDate(DateUtils.getFirstDayOfMonth(new Date())); 68 | let end = DateUtils.getStrDate(DateUtils.getLastDayOfMonth(new Date())); 69 | let params = [{key:"credate_between",value:start},{key:"credate_betweenand",value:end}]; 70 | this.navCtrl.push(ResultsBill,{params:params}); 71 | } 72 | doRefresh(event) { 73 | this.bookService.findAll() 74 | .catch(error => event.complete()) 75 | .then(restEntity =>{ 76 | event.complete(); 77 | if(restEntity.status==-1){ 78 | this.httpService.alert(restEntity.msg); 79 | return; 80 | } 81 | this.books = restEntity.object.results; 82 | this.amounts = this.bookService.calAmount(this.books); 83 | if(this.amounts==null) return; 84 | this.amount = this.amounts[2].amount; 85 | }); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/pages/ichart-page/ichart-page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 月账单 6 | 7 | 8 | 9 | 10 | 11 | 12 | {{month}}月共消费 13 | ¥{{sumamount | number:'.2-2'}} 14 | 15 | 22 | 23 | -------------------------------------------------------------------------------- /src/pages/ichart-page/ichart-page.scss: -------------------------------------------------------------------------------- 1 | page-ichart-page { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/ichart-page/ichart-page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { NavController, NavParams } from 'ionic-angular'; 3 | import { QueryBook } from "../../domain/QueryBook"; 4 | import { Dictionary } from "../../domain/Dictionary"; 5 | import { StorageService } from "../../providers/storage-service"; 6 | import { Constants } from "../../domain/Constants"; 7 | 8 | /** 9 | * Generated class for the IchartPage page. 10 | * 11 | * See http://ionicframework.com/docs/components/#navigation for more info 12 | * on Ionic pages and navigation. 13 | */ 14 | @Component({ 15 | selector: 'page-ichart-page', 16 | templateUrl: 'ichart-page.html', 17 | }) 18 | export class IchartPage { 19 | data:Array; 20 | billtypes:Dictionary[]; 21 | pieChartLabels:string[]; 22 | pieChartData:number[]; 23 | pieChartType:string = 'pie'; 24 | pieChartOptions:any={position:"bottom"}; 25 | sumamount:number;//本月总消费 26 | month:number; 27 | constructor(public navCtrl: NavController, public navParams: NavParams, 28 | public storageService:StorageService) { 29 | this.init(); 30 | } 31 | 32 | ionViewDidLoad() { 33 | 34 | } 35 | 36 | //初始化chart数据,必须放在constructor中,否则chart无法正常加载 37 | init(){ 38 | this.data = this.navParams.get('data'); 39 | this.month = this.navParams.get('month'); 40 | this.billtypes = this.storageService.read(Constants.BILL_TYPE); 41 | 42 | this.sumamount = 0; 43 | for(let q of this.data){ 44 | this.sumamount += q.val; 45 | } 46 | 47 | if(this.data==null) return; 48 | this.pieChartLabels = new Array(); 49 | this.pieChartData = new Array(); 50 | for(let type of this.billtypes){ 51 | let amount:number = 0; 52 | for(let val of this.data){ 53 | if(val.typeid==type.id){ 54 | amount += val.val; 55 | } 56 | } 57 | this.pieChartData.push(parseFloat(amount.toFixed(2))); 58 | this.pieChartLabels.push(type.name); 59 | } 60 | console.log(this.pieChartLabels); 61 | console.log(this.pieChartData); 62 | 63 | } 64 | 65 | 66 | 67 | // events 68 | public chartClicked(e:any):void { 69 | console.log(e); 70 | } 71 | 72 | public chartHovered(e:any):void { 73 | console.log(e); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/pages/login-page/login-page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 账号 15 | 16 | 17 | 18 | 密码 19 | 20 | 21 | 22 | 23 | 登 录 24 | 25 | 26 | 27 | 28 | 忘记密码? 29 | 注册DayBook 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/pages/login-page/login-page.scss: -------------------------------------------------------------------------------- 1 | page-login-page { 2 | .avator{ 3 | width:6em; 4 | height:6em; 5 | border-radius: 50%; 6 | overflow: hidden; 7 | } 8 | .forgetPwd{ 9 | font-size:0.8em; 10 | a{ 11 | text-decoration: none; 12 | } 13 | a:nth-child(2){ 14 | float:right; 15 | } 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/pages/login-page/login-page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { ViewController } from 'ionic-angular'; 3 | import {Validators, FormBuilder,FormGroup } from '@angular/forms'; 4 | import { Events } from 'ionic-angular'; 5 | import {Md5} from "ts-md5/dist/md5"; 6 | 7 | import { StorageService} from '../../providers/storage-service'; 8 | import { HttpService } from '../../providers/http-service'; 9 | import { User } from '../../domain/User'; 10 | import { Constants } from "../../domain/Constants"; 11 | 12 | @Component({ 13 | selector: 'page-login-page', 14 | templateUrl: 'login-page.html', 15 | }) 16 | export class LoginPage { 17 | loginForm:FormGroup; 18 | constructor(public viewCtrl: ViewController, 19 | public storageService:StorageService, 20 | public httpService :HttpService, 21 | private formBuilder: FormBuilder, 22 | public events:Events) { 23 | this.initForm(); 24 | } 25 | initForm() { 26 | let user = this.storageService.read(Constants.CURR_USER); 27 | this.loginForm = this.formBuilder.group({ 28 | name:[user==null?'':user.alias,Validators.required], 29 | password: ['', Validators.required] 30 | }); 31 | } 32 | login(){ 33 | let loader = this.httpService.loading(); 34 | //发布登录成功消息,刷新首页信息 35 | this.viewCtrl.onDidDismiss(()=>{ 36 | this.events.publish("LOGIN_SUCCESS"); 37 | }); 38 | loader.present(); 39 | let pwd = this.loginForm.controls['password'].value; 40 | this.loginForm.controls['password'].setValue(Md5.hashStr(this.loginForm.controls['password'].value).toString()); 41 | this.httpService.httpPostNoAuth("account/login",this.loginForm.value) 42 | .then(restEntity =>{ 43 | loader.dismiss(); 44 | this.loginForm.controls['password'].setValue(pwd); 45 | if(restEntity.status == -1){ 46 | this.httpService.alert(restEntity.msg,"登录失败"); 47 | return; 48 | } 49 | let user:User = restEntity.object; 50 | this.storageService.write(Constants.CURR_USER,user); 51 | this.events.publish(Constants.CURR_USER,user); 52 | this.viewCtrl.dismiss(); 53 | 54 | }) 55 | .catch( 56 | error => { 57 | loader.dismiss(); 58 | this.loginForm.controls['password'].setValue(pwd); 59 | } 60 | ); 61 | } 62 | forgetPwd(){ 63 | this.httpService.toast('功能完善中'); 64 | } 65 | register(){ 66 | this.httpService.toast('功能完善中'); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/pages/month-pop-page/month-pop-page.html: -------------------------------------------------------------------------------- 1 | 2 | {{i}}月份 3 | 4 | -------------------------------------------------------------------------------- /src/pages/month-pop-page/month-pop-page.scss: -------------------------------------------------------------------------------- 1 | page-month-pop-page { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/month-pop-page/month-pop-page.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { NavController, NavParams, ViewController } from 'ionic-angular'; 3 | 4 | 5 | @Component({ 6 | selector: 'page-month-pop-page', 7 | templateUrl: 'month-pop-page.html', 8 | }) 9 | export class MonthPopPage { 10 | month:Array; 11 | constructor(public viewCtrl: ViewController,public navParams:NavParams) {} 12 | 13 | close(m) { 14 | this.viewCtrl.dismiss(m); 15 | } 16 | 17 | ionViewDidLoad() { 18 | this.month = new Array(); 19 | let m = new Date().getMonth()+1; 20 | for(let i = 1; i<=m;i++){ 21 | this.month.push(i); 22 | } 23 | console.log(this.month); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/results-bill/results-bill.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 账单 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ¥{{sumamount | number:'.2-2'}} 27 | 28 | {{type.name}} 29 | 30 | 31 | 32 | {{month}}月 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | {{item.name}} 42 | {{item.remark}} 43 | 44 | 45 | {{item.val | number:'.2-2'}} 46 | {{item.credate | date:"MM-dd HH:mm"}} 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/pages/results-bill/results-bill.scss: -------------------------------------------------------------------------------- 1 | page-results-bill { 2 | .list_row{ 3 | *{ 4 | color:#555; 5 | } 6 | 7 | button{ 8 | padding-left:0; 9 | } 10 | .item_right p{ 11 | text-align:right; 12 | } 13 | ion-icon{ 14 | color: #f53d3d; 15 | } 16 | .item_title{ 17 | font-size:0.9em; 18 | font-weight:bold; 19 | } 20 | .item_amount{ 21 | font-weight:bold; 22 | font-size:0.9em; 23 | } 24 | .item_time{ 25 | color:#999; 26 | font-size:0.7em; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/pages/results-bill/results-bill.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { NavController, NavParams, PopoverController, ViewController } from 'ionic-angular'; 3 | import { QueryBook } from '../../domain/QueryBook'; 4 | import { BookService } from "../../service/BookService"; 5 | import { HttpService } from "../../providers/http-service"; 6 | import { MonthPopPage } from "../month-pop-page/month-pop-page"; 7 | import { DateUtils } from "../../utils/date-utils"; 8 | import { IchartPage } from "../ichart-page/ichart-page"; 9 | import { Dictionary } from '../../domain/Dictionary'; 10 | import { Constants } from '../../domain/Constants'; 11 | import { StorageService } from '../../providers/storage-service'; 12 | 13 | @Component({ 14 | selector: 'page-results-bill', 15 | templateUrl: 'results-bill.html', 16 | }) 17 | export class ResultsBill { 18 | data:QueryBook[]; 19 | params:Array<{key:string,value:any}>;//查询条件 20 | loader ; 21 | month:any; 22 | event:any; 23 | billTypes:Dictionary[]; 24 | constructor(public navCtrl: NavController, public navParams: NavParams, 25 | private bookService :BookService, 26 | private httpService :HttpService, 27 | private storageService :StorageService, 28 | public popoverCtrl: PopoverController) { 29 | } 30 | typeid_and:number=-1; 31 | sumamount:number;//本月总消费 32 | ionViewDidLoad() { 33 | this.params = this.navParams.get("params"); 34 | this.month = this.params[0].value; 35 | this.query(); 36 | this.init(); 37 | } 38 | init(){ 39 | var billtype = this.storageService.read(Constants.BILL_TYPE); 40 | if(billtype!=null){ 41 | this.billTypes=billtype; 42 | }else{ 43 | this.loader.present(); 44 | this.httpService.httpGetWithAuth("common/dictionary?typeid=1") 45 | .then(result =>{ 46 | this.loader.dismiss(); 47 | this.billTypes = result.object; 48 | this.storageService.write(Constants.BILL_TYPE,this.billTypes); 49 | }) 50 | .catch(error =>{ 51 | this.loader.dismiss(); 52 | console.log(error); 53 | }); 54 | 55 | } 56 | billtype.splice(0,0,new Dictionary(-1,"全部类型",null,null)); 57 | } 58 | typeChanged(){ 59 | this.query(); 60 | } 61 | query(){ 62 | this.params.push({key:"typeid_and",value:(this.typeid_and==-1 ?"":this.typeid_and+"_LONG")}); 63 | this.loader = this.httpService.loading(); 64 | this.loader.present(); 65 | this.bookService.findPage(this.params) 66 | .then(restEntity =>{ 67 | this.loader.dismiss(); 68 | if(restEntity.status==-1){ 69 | this.httpService.alert(restEntity.msg); 70 | }else{ 71 | this.data = restEntity.object.results; 72 | this.sumamount = 0; 73 | for(let q of this.data){ 74 | this.sumamount += q.val; 75 | } 76 | } 77 | }) 78 | .catch( 79 | error => this.httpService.alert(error) 80 | ) 81 | 82 | } 83 | doRefresh(event){ 84 | this.event = event; 85 | this.bookService.findPage(this.params) 86 | .then(restEntity =>{ 87 | event.complete(); 88 | if(restEntity.status==-1){ 89 | this.httpService.alert(restEntity.msg); 90 | }else{ 91 | this.data = restEntity.object.results; 92 | } 93 | }) 94 | .catch( 95 | error => this.httpService.alert(error) 96 | ) 97 | } 98 | presentMonth(ev){ 99 | console.log(ev); 100 | let popover = this.popoverCtrl.create(MonthPopPage); 101 | popover.present({ev}); 102 | popover.onDidDismiss((data)=>{ 103 | if(data==null) return; 104 | let date = new Date(); 105 | this.month = data; 106 | date.setMonth(data-1); 107 | let start = DateUtils.getStrFullDate(DateUtils.getBeginDate(DateUtils.getFirstDayOfMonth(date))); 108 | let end = DateUtils.getStrFullDate(DateUtils.getEndDate(DateUtils.getLastDayOfMonth(date))); 109 | this.params = [{key:"credate_between",value:start},{key:"credate_betweenand",value:end}]; 110 | this.query(); 111 | }); 112 | } 113 | 114 | queryChart(){ 115 | this.navCtrl.push(IchartPage,{'data':this.data,'month':this.month}); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/pages/save-bill/save-bill.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 记一笔 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 日期 16 | 17 | 18 | 19 | 类型 20 | 21 | {{type.name}} 22 | 23 | 24 | 25 | 金额 26 | 27 | 28 | 29 | 备注 30 | 31 | 32 | 保 存 33 | 34 | -------------------------------------------------------------------------------- /src/pages/save-bill/save-bill.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zyqwst/DayBookApp/212453cff4e9038ae0b5e8b79b8a6ca7f4b244c1/src/pages/save-bill/save-bill.scss -------------------------------------------------------------------------------- /src/pages/save-bill/save-bill.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { LoadingController,AlertController } from 'ionic-angular'; 3 | import { DatePipe} from '@angular/common'; 4 | 5 | 6 | import { HttpService } from '../../providers/http-service'; 7 | import { BookService } from '../../service/BookService'; 8 | import { Dictionary } from '../../domain/Dictionary'; 9 | 10 | import { Validators, FormBuilder, FormGroup } from '@angular/forms'; 11 | import { StorageService } from "../../providers/storage-service"; 12 | import { Constants } from "../../domain/Constants"; 13 | @Component({ 14 | selector: 'page-save-bill', 15 | templateUrl: 'save-bill.html' 16 | }) 17 | 18 | export class SaveBillPage{ 19 | 20 | constructor(private bookService :BookService, 21 | private httpService :HttpService, 22 | public loadingCtrl: LoadingController, 23 | private storageService :StorageService, 24 | private datePipe :DatePipe, 25 | private formBuilder: FormBuilder, 26 | private alertCtrl: AlertController 27 | ){} 28 | localStorage; 29 | billTypes:Dictionary[]; 30 | public defaultDate:string; 31 | public bookForm:FormGroup; 32 | ngOnInit(){ 33 | var date = new Date(); 34 | var str = this.datePipe.transform(date, 'yyyy-MM-dd'); 35 | this.defaultDate =str; 36 | this.init(); 37 | this.initForm(); 38 | } 39 | loader = this.httpService.loading(); 40 | init(){ 41 | var billtype = this.storageService.read(Constants.BILL_TYPE); 42 | if(billtype!=null){ 43 | this.billTypes=billtype; 44 | }else{ 45 | this.loader.present(); 46 | 47 | this.httpService.httpGetWithAuth("common/dictionary?typeid=1") 48 | .then(result =>{ 49 | this.loader.dismiss(); 50 | if(result.status==-1){ 51 | this.httpService.alert(result.msg); 52 | return; 53 | } 54 | this.billTypes = result.object; 55 | this.storageService.write(Constants.BILL_TYPE,this.billTypes); 56 | }) 57 | .catch(error =>{ 58 | this.loader.dismiss(); 59 | console.log(error); 60 | }); 61 | 62 | } 63 | 64 | } 65 | 66 | initForm() { 67 | this.bookForm = this.formBuilder.group({ 68 | id:[''], 69 | typeid: ['', Validators.required], 70 | val: ['', Validators.required], 71 | credate:[this.defaultDate, Validators.required], 72 | remark:[''] 73 | }); 74 | } 75 | logForm(){ 76 | this.loader.present(); 77 | this.bookService.saveBook(this.bookForm.value) 78 | .then(restEntity =>{ 79 | this.loader.dismiss(); 80 | if(restEntity.status==-1){ 81 | this.httpService.alert(restEntity.msg); 82 | }else{ 83 | this.initForm(); 84 | this.httpService.toast("保存成功"); 85 | } 86 | }).catch( 87 | error => { 88 | this.loader.dismiss(); 89 | this.httpService.alert(error); 90 | } 91 | ) 92 | } 93 | 94 | 95 | } -------------------------------------------------------------------------------- /src/pages/search-bill/search-bill.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 月账单 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 月份 17 | 18 | 19 | 查 询 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/pages/search-bill/search-bill.scss: -------------------------------------------------------------------------------- 1 | page-search-bill { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/search-bill/search-bill.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { LoadingController,NavController, NavParams,AlertController } from 'ionic-angular'; 3 | import { DatePipe} from '@angular/common'; 4 | 5 | import { BookService } from '../../service/BookService'; 6 | import { HttpService } from '../../providers/http-service'; 7 | import { Dictionary } from '../../domain/Dictionary'; 8 | import { ResultsBill } from '../../pages/results-bill/results-bill'; 9 | import { QueryBook } from '../../domain/QueryBook'; 10 | import { StorageService } from "../../providers/storage-service"; 11 | import { Constants } from "../../domain/Constants"; 12 | @Component({ 13 | selector: 'page-search-bill', 14 | templateUrl: 'search-bill.html', 15 | }) 16 | export class SearchBill { 17 | billTypes:Dictionary[]; 18 | constructor(public navCtrl: NavController, 19 | public navParams: NavParams, 20 | public loadingCtrl: LoadingController, 21 | private bookService :BookService, 22 | private httpService :HttpService, 23 | private storageService :StorageService, 24 | private datePipe :DatePipe, 25 | private alertCtrl: AlertController) { 26 | } 27 | 28 | ionViewDidLoad() { 29 | 30 | } 31 | loader = this.httpService.loading(); 32 | 33 | date = new Date(); 34 | str:string = this.datePipe.transform(this.date, 'yyyy-MM'); 35 | credate_yearAndMonth:string = this.str; 36 | typeid_and:number; 37 | query(){ 38 | let params = [{key:"credate_like",value:this.credate_yearAndMonth}, 39 | {key:"typeid_and",value:(this.typeid_and===undefined ?"":this.typeid_and+"_LONG")}]; 40 | this.navCtrl.push(ResultsBill,{params:params}); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/providers/http-service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Http, Response } from '@angular/http'; 3 | import { Observable } from 'rxjs/Observable'; 4 | import { Headers, RequestOptions } from '@angular/http'; 5 | import { LoadingController,Loading,ToastController } from 'ionic-angular'; 6 | import { Dialogs } from '@ionic-native/dialogs'; 7 | import 'rxjs/add/operator/toPromise'; 8 | import 'rxjs/add/operator/timeout'; 9 | 10 | import { RestEntity } from '../domain/RestEntity'; 11 | import { User } from "../domain/User"; 12 | import { Constants } from "../domain/Constants"; 13 | import { StorageService } from "./storage-service"; 14 | 15 | @Injectable() 16 | export class HttpService { 17 | hostUrl:string = "localhost:9971"; 18 | TIME_OUT:number = 30000; 19 | constructor( 20 | private http: Http, 21 | public loadingCtrl: LoadingController , 22 | public dialogs: Dialogs, 23 | public storageService:StorageService, 24 | public toastCtrl: ToastController 25 | ) { 26 | //this.local = new Storage(LocalStorage); 27 | } 28 | /**带身份验证的get请求 */ 29 | public httpGetWithAuth(url: string):Promise { 30 | url = `${this.hostUrl}/${url}`; 31 | var headers = new Headers(); 32 | let token = this.getToken(); 33 | if(token==null) { 34 | this.alert('异常','Token获取错误'); 35 | return; 36 | } 37 | headers.append(Constants.AUTHORIZATION, token); 38 | let options = new RequestOptions({ headers: headers }); 39 | 40 | return this.http.get(url,options).timeout(this.TIME_OUT).toPromise() 41 | .then(res => res.json() as RestEntity) 42 | .catch(err => { 43 | this.handleError(err); 44 | }); 45 | } 46 | /**不需身份验证的get请求 */ 47 | public httpGetNoAuth(url: string) { 48 | 49 | var headers = new Headers(); 50 | let options = new RequestOptions({ headers: headers }); 51 | return this.http.get(url, options).timeout(this.TIME_OUT).toPromise() 52 | .then(res => res.json()) 53 | .catch(err => { 54 | console.log('访问错误:'+err); 55 | this.handleError(err); 56 | }); 57 | } 58 | /**不带身份验证的post请求 */ 59 | public httpPostNoAuth(url: string, body: any) :Promise{ 60 | url = `${this.hostUrl}/${url}`; 61 | var headers = new Headers(); 62 | headers.append('Content-Type', 'application/json'); 63 | let options = new RequestOptions({ headers: headers }); 64 | return this.http.post(url, body,options).timeout(this.TIME_OUT).toPromise() 65 | .then(res => res.json()) 66 | .catch(err => { 67 | this.handleError(err); 68 | }); 69 | } 70 | public httpPostWithAuth(url: string, body: any) :Promise{ 71 | url = `${this.hostUrl}/${url}`; 72 | var headers = new Headers(); 73 | let token = this.getToken(); 74 | if(token==null) { 75 | this.alert('异常','Token获取错误'); 76 | return; 77 | } 78 | headers.append('Content-Type', 'application/json'); 79 | headers.append(Constants.AUTHORIZATION, token); 80 | let options = new RequestOptions({ headers: headers }); 81 | return this.http.post(url, body,options).timeout(this.TIME_OUT).toPromise() 82 | .then(res => res.json()) 83 | .catch(err => { 84 | this.handleError(err); 85 | }); 86 | } 87 | 88 | private handleError(error: Response) { 89 | this.alert("提示",error.toString()); 90 | return Observable.throw(error.json().error || 'Server Error'); 91 | } 92 | 93 | public loading():Loading{ 94 | let loader = this.loadingCtrl.create({ 95 | spinner:"dots", 96 | content:"loading...", 97 | dismissOnPageChange:true, // 是否在切换页面之后关闭loading框 98 | showBackdrop:false //是否显示遮罩层 99 | }); 100 | return loader; 101 | } 102 | public alert(msg:string,title?:string) { 103 | if(title==null) title='提示'; 104 | this.dialogs.alert(msg,title); 105 | } 106 | public toast(msg:string,time?:number) { 107 | if(!time) time = 3000; 108 | let toast = this.toastCtrl.create({ 109 | message: msg, 110 | duration: time 111 | }); 112 | toast.present(); 113 | } 114 | /**当前登录用户 */ 115 | public getCurrUser():User{ 116 | return this.storageService.read(Constants.CURR_USER); 117 | } 118 | public getToken():string{ 119 | let user = this.getCurrUser(); 120 | if(user==null || user.id==null || user.token==null){this.alert('Token错误,请登录后重试');} 121 | let token = user.id+"_"+user.token; 122 | return token; 123 | } 124 | } -------------------------------------------------------------------------------- /src/providers/storage-service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class StorageService { 5 | 6 | constructor() { } 7 | 8 | write(key: string, value: any) { 9 | if (value) { 10 | value = JSON.stringify(value); 11 | } 12 | localStorage.setItem(key, value); 13 | } 14 | 15 | read(key: string): T { 16 | let value: string = localStorage.getItem(key); 17 | 18 | if (value && value != "undefined" && value != "null") { 19 | return JSON.parse(value); 20 | } 21 | 22 | return null; 23 | } 24 | 25 | remove(key: string) { 26 | localStorage.removeItem(key); 27 | } 28 | 29 | clear() { 30 | sessionStorage.clear(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/service-worker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Check out https://googlechrome.github.io/sw-toolbox/docs/master/index.html for 3 | * more info on how to use sw-toolbox to custom configure your service worker. 4 | */ 5 | 6 | 7 | 'use strict'; 8 | importScripts('./build/sw-toolbox.js'); 9 | 10 | self.toolbox.options.cache = { 11 | name: 'ionic-cache' 12 | }; 13 | 14 | // pre-cache our key assets 15 | self.toolbox.precache( 16 | [ 17 | './build/main.js', 18 | './build/main.css', 19 | './build/polyfills.js', 20 | 'index.html', 21 | 'manifest.json' 22 | ] 23 | ); 24 | 25 | // dynamically cache any other local assets 26 | self.toolbox.router.any('/*', self.toolbox.cacheFirst); 27 | 28 | // for any other requests go to the network, cache, 29 | // and then only use that cached resource if your user goes offline 30 | self.toolbox.router.default = self.toolbox.networkFirst; -------------------------------------------------------------------------------- /src/service/BookService.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { DatePipe} from '@angular/common'; 3 | import { HttpService } from '../providers/http-service'; 4 | import { DateUtils } from "../utils/date-utils"; 5 | import { QueryBook } from "../domain/QueryBook"; 6 | @Injectable() 7 | export class BookService{ 8 | constructor( 9 | private httpService :HttpService,private datePipe :DatePipe 10 | ){} 11 | /**查询当天前后三十天的数据 */ 12 | public findAll(){ 13 | let start = this.datePipe.transform(DateUtils.getDateAfterDays(-30),'yyyy-MM-dd'); 14 | let end = this.datePipe.transform(DateUtils.getDateAfterDays(30),'yyyy-MM-dd'); 15 | return this.httpService.httpGetWithAuth(`book/list?credate_between=${start}&&credate_betweenand=${end}`); 16 | } 17 | public findToday(){ 18 | var date = new Date(); 19 | var str = this.datePipe.transform(date, 'yyyy-MM-dd'); 20 | return this.httpService.httpGetWithAuth(`book/list?credate_like=${str}`); 21 | } 22 | public calAmount(books:Array):Array<{type:number,amount:number,title:string,averageDay?:number}>{ 23 | if(books==null) return null; 24 | let todayAmount=0,weekAmount=0,monthAmount=0; 25 | let amounts:Array = new Array(); 26 | for(let book of books){ 27 | let credate = DateUtils.getBeginDate(new Date(book.credate.replace(/-/g,"/"))); 28 | let firstDayOfWeek = DateUtils.getBeginDate(DateUtils.getFirstDayOfWeek(new Date())); 29 | let lastDayOfWeek = DateUtils.getBeginDate(DateUtils.getLastDayOfWeek(new Date())); 30 | let firstDayOfMonth = DateUtils.getBeginDate(DateUtils.getFirstDayOfMonth(new Date())); 31 | let lastDayOfMonth = DateUtils.getBeginDate(DateUtils.getLastDayOfMonth(new Date())); 32 | 33 | if(credate>= DateUtils.getBeginDate(new Date()) && credate<=DateUtils.getEndDate(new Date())){ 34 | todayAmount += book.val; 35 | } 36 | if(credate >= firstDayOfWeek && credate <= lastDayOfWeek){ 37 | weekAmount += book.val; 38 | } 39 | if(credate >= firstDayOfMonth && credate <= lastDayOfMonth){ 40 | monthAmount += book.val; 41 | } 42 | } 43 | amounts.push({type:0,amount:todayAmount,title:'今天',icon:'ios-calendar-outline'}); 44 | amounts.push({type:1,amount:weekAmount,title:'本周',icon:'md-calendar'}); 45 | amounts.push({type:2,amount:monthAmount,title:'本月',icon:'ios-calendar'}); 46 | return amounts; 47 | } 48 | 49 | public saveBook(body:any){ 50 | return this.httpService.httpPostWithAuth(`book/save`,body); 51 | } 52 | public findPage(params:Array<{key:string,value:any}>){ 53 | let url:string = "book/list?"; 54 | for(let param of params ){ 55 | url = url + param.key + "=" +param.value+"&&"; 56 | } 57 | return this.httpService.httpGetWithAuth(url); 58 | } 59 | } -------------------------------------------------------------------------------- /src/theme/variables.scss: -------------------------------------------------------------------------------- 1 | // Ionic Variables and Theming. For more info, please see: 2 | // http://ionicframework.com/docs/v2/theming/ 3 | $font-path: "../assets/fonts"; 4 | 5 | @import "ionic.globals"; 6 | 7 | 8 | // Shared Variables 9 | // -------------------------------------------------- 10 | // To customize the look and feel of this app, you can override 11 | // the Sass variables found in Ionic's source scss files. 12 | // To view all the possible Ionic variables, see: 13 | // http://ionicframework.com/docs/v2/theming/overriding-ionic-variables/ 14 | 15 | 16 | 17 | 18 | // Named Color Variables 19 | // -------------------------------------------------- 20 | // Named colors makes it easy to reuse colors on various components. 21 | // It's highly recommended to change the default colors 22 | // to match your app's branding. Ionic uses a Sass map of 23 | // colors so you can add, rename and remove colors as needed. 24 | // The "primary" color is the only required color in the map. 25 | 26 | $colors: ( 27 | primary: #387ef5, 28 | secondary: #32db64, 29 | danger: #f53d3d, 30 | light: #f4f4f4, 31 | dark: #222, 32 | toolbar: #3e425b, 33 | ); 34 | 35 | 36 | // App iOS Variables 37 | // -------------------------------------------------- 38 | // iOS only Sass variables can go here 39 | 40 | 41 | 42 | 43 | // App Material Design Variables 44 | // -------------------------------------------------- 45 | // Material Design only Sass variables can go here 46 | 47 | 48 | 49 | 50 | // App Windows Variables 51 | // -------------------------------------------------- 52 | // Windows only Sass variables can go here 53 | 54 | 55 | 56 | 57 | // App Theme 58 | // -------------------------------------------------- 59 | // Ionic apps can have different themes applied, which can 60 | // then be future customized. This import comes last 61 | // so that the above variables are used and Ionic's 62 | // default are overridden. 63 | 64 | @import "ionic.theme.default"; 65 | 66 | 67 | // Ionicons 68 | // -------------------------------------------------- 69 | // The premium icon font for Ionic. For more info, please see: 70 | // http://ionicframework.com/docs/v2/ionicons/ 71 | 72 | @import "ionic.ionicons"; 73 | 74 | 75 | // Fonts 76 | // -------------------------------------------------- 77 | 78 | @import "roboto"; 79 | @import "noto-sans"; 80 | -------------------------------------------------------------------------------- /src/utils/Logger.ts: -------------------------------------------------------------------------------- 1 | export class Logger { 2 | log(msg: any) { console.log(msg); } 3 | error(msg: any) { console.error(msg); } 4 | warn(msg: any) { console.warn(msg); } 5 | } -------------------------------------------------------------------------------- /src/utils/date-utils.ts: -------------------------------------------------------------------------------- 1 | export class DateUtils{ 2 | /**当月第一天 */ 3 | public static getFirstDayOfMonth(date:Date):Date{ 4 | date.setDate(1); 5 | return date; 6 | } 7 | /**当月最后一天 */ 8 | public static getLastDayOfMonth(date:Date):Date{ 9 | var currentMonth=date.getMonth(); 10 | var nextMonth=++currentMonth; 11 | var nextMonthFirstDay:any=new Date(date.getFullYear(),nextMonth,1); 12 | var oneDay=1000*60*60*24; 13 | return new Date(nextMonthFirstDay-oneDay); 14 | } 15 | /**获取本周第一天 */ 16 | public static getFirstDayOfWeek(date:Date):Date{ 17 | let time = date; 18 | time.setDate(time.getDate() - time.getDay() + 1); 19 | return time; 20 | } 21 | /**本周最后一天 */ 22 | public static getLastDayOfWeek(date:Date):Date{ 23 | let time = date; 24 | time.setDate(time.getDate() - time.getDay() + 7); 25 | return time; 26 | } 27 | /**获取本月天数 */ 28 | public static getCountOfMonth(date:Date):number{ 29 | return date.getDate(); 30 | } 31 | /**获取第几天后的日期,负数表示几天前 */ 32 | public static getDateAfterDays(days:number):Date{ 33 | let time = new Date(); 34 | time.setDate(time.getDate()+days); 35 | return time; 36 | } 37 | /**获得 日期 00:00:00 */ 38 | public static getBeginDate(date:Date):Date{ 39 | let year = date.getFullYear(); 40 | let month = date.getMonth(); 41 | let day = date.getDate(); 42 | let time = new Date('1900-01-01 00:00:00'); 43 | time.setFullYear(year); 44 | time.setMonth(month); 45 | time.setDate(day); 46 | return new Date(time); 47 | } 48 | /**获得 日期 023:59:59 */ 49 | public static getEndDate(date:Date):Date{ 50 | let year = date.getFullYear(); 51 | let month = date.getMonth(); 52 | let day = date.getDate(); 53 | let time = new Date('1900-01-01 23:59:59'); 54 | time.setFullYear(year); 55 | time.setMonth(month); 56 | time.setDate(day); 57 | return new Date(time); 58 | } 59 | /**日期转字符串 yyyy-MM-dd */ 60 | public static getStrDate(date: Date): string{ 61 | let year = date.getFullYear(); 62 | let month = date.getMonth()+1; 63 | let day = date.getDate(); 64 | return year + "-" + (month<10?'0'+month:month) + "-" + (day<10?'0'+day:day); 65 | } 66 | /**完整的日期字符串 yyyy-MM-dd HH:mm:ss */ 67 | public static getStrFullDate(date:Date):string{ 68 | let hours = date.getHours(); 69 | let minutes = date.getMinutes(); 70 | let seconds = date.getSeconds(); 71 | return DateUtils.getStrDate(date)+" "+(hours<10?'0'+hours:hours)+":" 72 | +(minutes<10?'0'+minutes:minutes)+":"+(seconds<10?'0'+seconds:seconds); 73 | } 74 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "lib": [ 8 | "dom", 9 | "es2015" 10 | ], 11 | "module": "es2015", 12 | "moduleResolution": "node", 13 | "sourceMap": true, 14 | "target": "es5" 15 | }, 16 | "include": [ 17 | "src/**/*.ts" 18 | ], 19 | "exclude": [ 20 | "node_modules" 21 | ], 22 | "compileOnSave": false, 23 | "atom": { 24 | "rewriteTsconfig": false 25 | } 26 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-duplicate-variable": true, 4 | "no-unused-variable": [ 5 | true 6 | ] 7 | }, 8 | "rulesDirectory": [ 9 | "node_modules/tslint-eslint-rules/dist/rules" 10 | ] 11 | } 12 | --------------------------------------------------------------------------------
24 | 本月支出(元) 25 |
¥{{amount | number :'.2-2'}}
38 | 本月剩余 {{4000-amount | number:'.2-2'}} 3500" name="flash"> 39 |
{{item.title}}
{{item.amount | number:'.2-2'}}
{{item.name}}
{{item.remark}}
{{item.val | number:'.2-2'}}
{{item.credate | date:"MM-dd HH:mm"}}