├── static ├── .gitkeep ├── tabs │ ├── home.png │ ├── orders.png │ ├── home-active.png │ └── orders-active.png ├── font │ ├── iconfont.ttf │ ├── iconfont.woff │ ├── iconfont.woff2 │ ├── demo.html │ └── iconfont.css ├── images │ ├── delete.png │ ├── user.png │ └── empty_list.png └── tabbar │ ├── icon_book.png │ ├── icon_chart.png │ ├── icon_home.png │ ├── icon_mine.png │ ├── icon_book_HL.png │ ├── icon_chart_HL.png │ ├── icon_home_HL.png │ ├── icon_mine_HL.png │ ├── icon_release.png │ └── 未命名文件夹 │ ├── icon_home.png │ ├── icon_mine.png │ ├── icon_home_HL.png │ ├── icon_mine_HL.png │ └── icon_release.png ├── src ├── pages │ ├── my │ │ ├── main.json │ │ ├── main.js │ │ └── index.vue │ ├── categoryList │ │ ├── main.json │ │ ├── main.js │ │ └── index.vue │ ├── accountBooks │ │ ├── main.json │ │ ├── main.js │ │ └── index.vue │ ├── addCategory │ │ ├── main.json │ │ ├── main.js │ │ └── index.vue │ ├── addBooking │ │ ├── main.json │ │ ├── main.js │ │ └── index.vue │ └── index │ │ ├── main.json │ │ ├── main.js │ │ └── index.vue ├── components │ ├── card.vue │ ├── timePicker.vue │ ├── tabBar.vue │ ├── movableSort.vue │ └── keyboard.vue ├── stylus │ ├── variables.styl │ └── index.styl ├── main.js ├── utils │ ├── login.js │ ├── api.js │ ├── index.js │ └── fly.js ├── app.json ├── App.vue └── store │ └── index.js ├── screenshots ├── 1.png ├── 2.png ├── 3.png ├── 4.png └── pic.jpg ├── config ├── prod.env.js ├── dev.env.js └── index.js ├── .postcssrc.js ├── .editorconfig ├── .gitignore ├── package.swan.json ├── project.swan.json ├── server ├── utils │ └── index.js ├── routes │ ├── index.js │ └── accounting.js ├── config.js ├── nodemon.json ├── controllers │ ├── deleteAccounting.js │ ├── getCategoryList.js │ ├── deleteCategory.js │ ├── addCategory.js │ ├── updateCategoryList.js │ ├── getAccountingDaysNCounts.js │ ├── getAccountingDetails.js │ ├── addRecord.js │ ├── getAmount.js │ ├── getAccountingList.js │ └── login.js ├── middlewares │ ├── userCheck.js │ ├── authorizationMiddleware.js │ └── errorCatch.js ├── models │ ├── accList.js │ └── user.js ├── package.json └── index.js ├── index.html ├── .babelrc ├── README.md ├── project.config.json └── package.json /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/my/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "我的" 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/categoryList/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "" 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/accountBooks/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "我的账本" 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/addCategory/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "开始记账" 3 | } 4 | -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/screenshots/1.png -------------------------------------------------------------------------------- /screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/screenshots/2.png -------------------------------------------------------------------------------- /screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/screenshots/3.png -------------------------------------------------------------------------------- /screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/screenshots/4.png -------------------------------------------------------------------------------- /screenshots/pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/screenshots/pic.jpg -------------------------------------------------------------------------------- /static/tabs/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabs/home.png -------------------------------------------------------------------------------- /static/font/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/font/iconfont.ttf -------------------------------------------------------------------------------- /static/images/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/images/delete.png -------------------------------------------------------------------------------- /static/images/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/images/user.png -------------------------------------------------------------------------------- /static/tabs/orders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabs/orders.png -------------------------------------------------------------------------------- /static/font/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/font/iconfont.woff -------------------------------------------------------------------------------- /static/font/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/font/iconfont.woff2 -------------------------------------------------------------------------------- /src/pages/addBooking/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "支出记账", 3 | "disableScroll":true 4 | } 5 | -------------------------------------------------------------------------------- /static/images/empty_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/images/empty_list.png -------------------------------------------------------------------------------- /static/tabbar/icon_book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/icon_book.png -------------------------------------------------------------------------------- /static/tabbar/icon_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/icon_chart.png -------------------------------------------------------------------------------- /static/tabbar/icon_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/icon_home.png -------------------------------------------------------------------------------- /static/tabbar/icon_mine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/icon_mine.png -------------------------------------------------------------------------------- /static/tabs/home-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabs/home-active.png -------------------------------------------------------------------------------- /static/tabs/orders-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabs/orders-active.png -------------------------------------------------------------------------------- /static/tabbar/icon_book_HL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/icon_book_HL.png -------------------------------------------------------------------------------- /static/tabbar/icon_chart_HL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/icon_chart_HL.png -------------------------------------------------------------------------------- /static/tabbar/icon_home_HL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/icon_home_HL.png -------------------------------------------------------------------------------- /static/tabbar/icon_mine_HL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/icon_mine_HL.png -------------------------------------------------------------------------------- /static/tabbar/icon_release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/icon_release.png -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"', 3 | API_ROOT: '"https://bkdemo.juheworld.cn"' 4 | } 5 | -------------------------------------------------------------------------------- /src/pages/my/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './index' 3 | 4 | const app = new Vue(App) 5 | app.$mount() 6 | -------------------------------------------------------------------------------- /static/tabbar/未命名文件夹/icon_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/未命名文件夹/icon_home.png -------------------------------------------------------------------------------- /static/tabbar/未命名文件夹/icon_mine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/未命名文件夹/icon_mine.png -------------------------------------------------------------------------------- /src/pages/accountBooks/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './index' 3 | 4 | const app = new Vue(App) 5 | app.$mount() 6 | -------------------------------------------------------------------------------- /src/pages/addBooking/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './index' 3 | 4 | const app = new Vue(App) 5 | app.$mount() 6 | -------------------------------------------------------------------------------- /src/pages/addCategory/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './index' 3 | 4 | const app = new Vue(App) 5 | app.$mount() 6 | -------------------------------------------------------------------------------- /src/pages/categoryList/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './index' 3 | 4 | const app = new Vue(App) 5 | app.$mount() 6 | -------------------------------------------------------------------------------- /static/tabbar/未命名文件夹/icon_home_HL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/未命名文件夹/icon_home_HL.png -------------------------------------------------------------------------------- /static/tabbar/未命名文件夹/icon_mine_HL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/未命名文件夹/icon_mine_HL.png -------------------------------------------------------------------------------- /static/tabbar/未命名文件夹/icon_release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/txn513/mpvue-bookKeeping/HEAD/static/tabbar/未命名文件夹/icon_release.png -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-mpvue-wxss": {} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/index/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "闪记账", 3 | "navigationBarBackgroundColor": "#f9db61", 4 | "disableScroll":true, 5 | "enablePullDownRefresh": true 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | *.suo 11 | *.ntvs* 12 | *.njsproj 13 | *.sln 14 | -------------------------------------------------------------------------------- /package.swan.json: -------------------------------------------------------------------------------- 1 | { 2 | "appid": "wxcdc43238cb0b0c20", 3 | "setting": { 4 | "urlCheck": false 5 | }, 6 | "condition": { 7 | "swan": { 8 | "current": -1, 9 | "list": [] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /project.swan.json: -------------------------------------------------------------------------------- 1 | { 2 | "appid": "wxcdc43238cb0b0c20", 3 | "setting": { 4 | "urlCheck": false 5 | }, 6 | "condition": { 7 | "swan": { 8 | "current": -1, 9 | "list": [] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /server/utils/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | localDate(v) { 3 | const d = new Date(v || Date.now()); 4 | d.setMinutes(d.getMinutes() - d.getTimezoneOffset()); 5 | return d.toISOString(); 6 | }, 7 | } -------------------------------------------------------------------------------- /server/routes/index.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router') 2 | const loginController = require('../controllers/login.js') 3 | 4 | let router = new Router() 5 | // 登录接口 6 | router.get('/login', loginController) 7 | 8 | module.exports = router -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mpvue-koa2 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"', 6 | // API_ROOT: '"http://192.168.31.133:5000"' 7 | API_ROOT: '"https://bkdemo.juheworld.cn"' 8 | }) 9 | -------------------------------------------------------------------------------- /src/components/card.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | 20 | -------------------------------------------------------------------------------- /server/config.js: -------------------------------------------------------------------------------- 1 | const CONF = { 2 | port: '5757', 3 | rootPathname: '/data/release/weapp', 4 | 5 | // 微信小程序 App ID 6 | appId: 'wxcdc43238cb0b0c20', 7 | 8 | // 微信小程序 App Secret 9 | appSecret: '7e642d3b0691654d9b83bc16cbc9beac', 10 | 11 | } 12 | 13 | module.exports = CONF; 14 | -------------------------------------------------------------------------------- /src/pages/index/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './index' 3 | 4 | // add this to handle exception 5 | Vue.config.errorHandler = function (err) { 6 | if (console && console.error) { 7 | console.error(err) 8 | } 9 | } 10 | 11 | const app = new Vue(App) 12 | app.$mount() 13 | -------------------------------------------------------------------------------- /server/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "restartable": "rs", 3 | "ignore": [ 4 | ".git", 5 | "node_modules/**/node_modules" 6 | ], 7 | "verbose": true, 8 | "execMap": { 9 | "js": "node --harmony" 10 | }, 11 | "env": { 12 | "NODE_ENV": "local", 13 | "DEBUG": "*,-nodemon:*,-nodemon,-knex:pool" 14 | }, 15 | "ext": "js json" 16 | } 17 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["istanbul"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/controllers/deleteAccounting.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const AccList = mongoose.model("AccList"); 3 | 4 | module.exports = async (ctx, next) => { 5 | let res = await AccList.update( 6 | {'_id': ctx.request.body.id}, 7 | {$set: {'isDeleted' : 1}} 8 | ) 9 | if (res.nModified == 1) { 10 | ctx.body = { statusCode: 200, message: '删除成功', code: 1}; 11 | } else { 12 | ctx.body = { statusCode: 200, message: '删除失败', code: 2}; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/controllers/getCategoryList.js: -------------------------------------------------------------------------------- 1 | module.exports = async (ctx, next) => { 2 | let user = ctx.state.userDoc 3 | 4 | if (ctx.request.body.recordType == 0) { 5 | ctx.body = { statusCode: 200, message: '获取支出类别成功', data: { 6 | categoryList: user.payCategoryList 7 | }}; 8 | } else if (ctx.request.body.recordType == 1){ 9 | ctx.body = { statusCode: 200, message: '获取收入类别成功', data: { 10 | categoryList: user.incomeCategoryList 11 | }}; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/pages/accountBooks/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /server/middlewares/userCheck.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const User = mongoose.model("User"); 3 | module.exports = async (ctx, next) => { 4 | console.log(ctx.state.user) 5 | if (!ctx.state.user || (ctx.state.user && !ctx.state.user.openid)) { 6 | await next() 7 | } else { 8 | let user = await User.userCheck(ctx.state.user.openid) 9 | if (user) { 10 | ctx.state.userDoc = user 11 | await next() 12 | } else { 13 | ctx.throw(500,'用户信息查询失败') 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /server/controllers/deleteCategory.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const User = mongoose.model("User"); 4 | module.exports = async (ctx, next) => { 5 | let user = ctx.state.userDoc 6 | let recordType = ctx.request.body.recordType 7 | if (recordType == 0) { 8 | user.payCategoryList.pull({_id: ctx.request.body.id}) 9 | } else if (recordType == 1) { 10 | user.incomeCategoryList.pull({_id: ctx.request.body.id}) 11 | } 12 | await user.save() 13 | ctx.body = { statusCode: 200, message: '删除类别成功', code: 1}; 14 | } 15 | -------------------------------------------------------------------------------- /server/middlewares/authorizationMiddleware.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken') //创建token 2 | const mongoose = require('mongoose') 3 | const secret = 'appIdSessionId' //生成Token 的秘钥 4 | 5 | export default async (ctx, next) => { 6 | let token = ctx.header.authorization.split(' ')[0] 7 | if (token) { 8 | try { 9 | let decoded = await jwt.verify(token, secret) 10 | ctx.body = { statusCode: 200, message: 'token验证成功', Token: user.token }; 11 | } catch (err) { 12 | if (err) throw err; 13 | } 14 | } 15 | await next() 16 | } -------------------------------------------------------------------------------- /server/controllers/addCategory.js: -------------------------------------------------------------------------------- 1 | const request = require('request') 2 | const mongoose = require('mongoose') 3 | const config = require('../config.js') 4 | 5 | const User = mongoose.model("User"); 6 | module.exports = async (ctx, next) => { 7 | let user = ctx.state.userDoc 8 | 9 | if (ctx.request.body.recordType == 0) { 10 | user.payCategoryList.push(ctx.request.body) 11 | } else if (ctx.request.body.recordType == 1){ 12 | user.incomeCategoryList.push(ctx.request.body) 13 | } 14 | await user.save() 15 | ctx.body = { statusCode: 200, message: '添加类别成功', code: 1}; 16 | } 17 | -------------------------------------------------------------------------------- /server/controllers/updateCategoryList.js: -------------------------------------------------------------------------------- 1 | const request = require('request') 2 | const mongoose = require('mongoose') 3 | const config = require('../config.js') 4 | const utils = require('../utils') 5 | 6 | const User = mongoose.model("User"); 7 | module.exports = async (ctx, next) => { 8 | let user = ctx.state.userDoc 9 | 10 | if (ctx.request.body.recordType == 0) { 11 | user.payCategoryList = ctx.request.body.list 12 | } else if (ctx.request.body.recordType == 1){ 13 | user.incomeCategoryList = ctx.request.body.list 14 | } 15 | await user.save() 16 | ctx.body = { statusCode: 200, message: '更新类别成功', code: 1}; 17 | } 18 | -------------------------------------------------------------------------------- /server/middlewares/errorCatch.js: -------------------------------------------------------------------------------- 1 | module.exports = async (ctx, next) => { 2 | try { 3 | await next(); 4 | } catch (err) { 5 | // console.log('error top---------------', err) 6 | ctx.status = err.status || 500; 7 | // console.log(err.originalError.name) 8 | if (err.originalError && err.originalError.name == 'TokenExpiredError') { 9 | ctx.body = {code: err.originalError.name, message: err.originalError ? err.originalError.message : err.message} 10 | } else { 11 | ctx.body = err.originalError ? err.originalError.message : err.message 12 | } 13 | ctx.app.emit('error', err, ctx); 14 | } 15 | } -------------------------------------------------------------------------------- /src/stylus/variables.styl: -------------------------------------------------------------------------------- 1 | //主题1 2 | themeColor = #f9db61 3 | fontColor = #000 4 | 5 | //白色 6 | whiteColor = #ffffff 7 | //黑色 8 | blackColor = #000000 9 | //通用灰色 10 | backgroundGray = #f5f5f5 11 | grayColor = #dbdbdb 12 | darkGrayColor = #888 13 | //背景色 14 | backGroundColor = #f4f5f5 15 | //边框色 16 | borderColor = #e1e1e1 17 | //消息提醒框背景色 18 | messageBackgroundColor = red 19 | //主文本颜色 20 | textColor = #666666 21 | 22 | indexHeaderBar = 120px 23 | indexHeaderBarTop = 20px 24 | indexHeaderBarBottom = 100px 25 | 26 | 27 | isPhoneXBottom = 66px 28 | 29 | bottomBtnHeight = 100px 30 | 31 | tabbarBottomHeight = 150px 32 | 33 | fontColorGray = #343233 34 | keyBoardHeight = 508px -------------------------------------------------------------------------------- /server/models/accList.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const utils = require('../utils') 3 | 4 | const Schema = mongoose.Schema; 5 | const UserSchema = new Schema({ 6 | openid: String, 7 | createAt: {type:Date,default: () => { return utils.localDate() } }, 8 | updateAt: {type:Date,default: () => { return utils.localDate() } }, 9 | recordYear: Number, 10 | recordMonth: Number, 11 | recordDay: Number, 12 | price: Number, 13 | recordType: Number, 14 | icon: String, 15 | title: String, 16 | category: Number, 17 | bookId: {type: String, default: ''}, 18 | remark: String, 19 | isDeleted: {type: Number, default: 0} 20 | }); 21 | 22 | 23 | 24 | mongoose.model('AccList', UserSchema); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 技术栈 2 | 3 | > mpvue + vuex + koa2 + mongoose 4 | 5 | ## 线上版本 6 | 7 | ![pic](screenshots/pic.jpg) 8 | 9 | 10 | ## 运行 11 | 12 | ### 运行前端 13 | 14 | ```shell 15 | # 安装依赖 16 | npm install 17 | 18 | # 运行开发环境 19 | npm run dev 20 | 21 | # 用小程序开发工具导入dist下的项目 22 | ``` 23 | 24 | ### 运行后端 25 | 26 | ```shell 27 | # 进入server目录 28 | cd server 29 | 30 | # 安装依赖 31 | npm install 32 | 33 | # 运行开发环境 34 | npm run dev 35 | ``` 36 | 37 | ### 开启mongo 38 | 39 | ```shell 40 | # 安装 41 | # 移步 https://www.runoob.com/mongodb/mongodb-window-install.html 42 | 43 | # 开启 44 | mongod 45 | ``` 46 | 47 | ## 页面展示 48 | 49 | ![1](screenshots/1.png) ![1](screenshots/2.png) 50 | 51 | 52 | 53 | ![3](screenshots/3.png) ![4](screenshots/4.png) 54 | 55 | -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目配置文件。", 3 | "setting": { 4 | "urlCheck": false, 5 | "es6": false, 6 | "postcss": true, 7 | "minified": true, 8 | "newFeature": true, 9 | "autoAudits": false, 10 | "checkInvalidKey": true 11 | }, 12 | "miniprogramRoot": "dist/wx/", 13 | "compileType": "miniprogram", 14 | "appid": "wxcdc43238cb0b0c20", 15 | "projectname": "bookKeeping-mpvue", 16 | "simulatorType": "wechat", 17 | "simulatorPluginLibVersion": {}, 18 | "condition": { 19 | "search": { 20 | "current": -1, 21 | "list": [] 22 | }, 23 | "conversation": { 24 | "current": -1, 25 | "list": [] 26 | }, 27 | "game": { 28 | "currentL": -1, 29 | "list": [] 30 | }, 31 | "miniprogram": { 32 | "current": -1, 33 | "list": [] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "dev": "nodemon --config nodemon.json index.js", 9 | "prod": "cross-env NODE_ENV=production pm2 start index.js --name='bookKeepingDemo'" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "jsonwebtoken": "^8.5.1", 16 | "koa": "^2.7.0", 17 | "koa-bodyparser": "^4.2.1", 18 | "koa-jwt": "^3.5.1", 19 | "koa-router": "^7.4.0", 20 | "koa2-cors": "^2.0.6", 21 | "moment": "^2.24.0", 22 | "mongoose": "^5.5.8", 23 | "request": "^2.88.0" 24 | }, 25 | "devDependencies": { 26 | "cross-env": "^5.2.0", 27 | "nodemon": "^1.19.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server/controllers/getAccountingDaysNCounts.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const moment = require('moment') 3 | 4 | const AccList = mongoose.model("AccList"); 5 | 6 | let days = 0; 7 | module.exports = async (ctx, next) => { 8 | let todayDate = moment() 9 | 10 | let aggregate = await AccList.aggregate([ 11 | {$match: {'openid':ctx.state.user.openid, 'isDeleted': 0}}, 12 | { $sort : { 'createAt': 1} } 13 | ]) 14 | if (aggregate && aggregate.length > 0) { 15 | days = todayDate.diff(aggregate[0].createAt, 'days') + 1 16 | 17 | return ctx.body = { statusCode: 200, message: '获取记账天数成功', code: 1, data: { 18 | days: days, 19 | counts: aggregate.length 20 | }}; 21 | } 22 | 23 | ctx.body = { statusCode: 200, message: '获取记账天数成功', code: 1, data: { 24 | days: 0, 25 | counts: 0 26 | }}; 27 | } 28 | -------------------------------------------------------------------------------- /server/controllers/getAccountingDetails.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const AccList = mongoose.model("AccList"); 4 | module.exports = async (ctx, next) => { 5 | let aggregate = null 6 | aggregate = await AccList.aggregate([ 7 | {$match: { 8 | 'openid':ctx.state.user.openid, 9 | '_id': mongoose.Types.ObjectId(ctx.request.body.id), 10 | }}, 11 | { 12 | $project : { 13 | _id: '$_id', 14 | recordYear: '$recordYear', 15 | recordMonth: '$recordMonth', 16 | recordDay: '$recordDay', 17 | bookId: '$bookId', 18 | remark: '$remark', 19 | icon: '$icon', 20 | title: '$title', 21 | recordType: '$recordType', 22 | price: '$price' 23 | } 24 | } 25 | 26 | ]); 27 | 28 | ctx.body = { statusCode: 200, message: '获取记账详情', code: 1, data: aggregate[0]}; 29 | } 30 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | import store from './store/index' 4 | import fly from './utils/fly' 5 | 6 | Vue.config.productionTip = false 7 | App.mpType = 'app' 8 | Vue.prototype.$fly = fly; 9 | Vue.prototype.$store = store 10 | 11 | const app = new Vue(App) 12 | app.$mount() 13 | 14 | // console.log(getApp()) 15 | Vue.prototype.globalData = getApp().globalData 16 | 17 | // icon图标 18 | Vue.prototype.globalData.iconArray = { 19 | payIcon: ["icon-yundong", 20 | "icon-youxi", 21 | "icon-yundong1", 22 | "icon-lvyou", 23 | "icon-gouwu", 24 | "icon-jiaotong", 25 | "icon-shenghuo", 26 | "icon-xuexi-", 27 | "icon-shengri", 28 | "icon-canju", 29 | "icon-yiliao", 30 | "icon-huaban"] 31 | , 32 | incomeIcon: [ 33 | "icon-lijin", 34 | "icon-licai", 35 | "icon-jianzhi", 36 | "icon-yuangonggongzi" 37 | ] 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/utils/login.js: -------------------------------------------------------------------------------- 1 | function login () { 2 | return new Promise((resolve, reject) => { 3 | wx.login({ 4 | success: function (res) { 5 | if (res.code) { 6 | //发起网络请求 7 | wx.request({ 8 | url: `${process.env.API_ROOT}/user/login`, 9 | data: { 10 | code: res.code 11 | }, 12 | success:res=>{ 13 | console.log(res.data) 14 | // self.globalData.Token = res.data.Token 15 | // store.commit('AUTH_SUCCESS', res.data.Token) 16 | resolve(res) 17 | } 18 | }) 19 | } else { 20 | reject(res) 21 | console.log('登录失败!' + res.errMsg) 22 | } 23 | } 24 | }); 25 | }) 26 | } 27 | 28 | export default login -------------------------------------------------------------------------------- /src/utils/api.js: -------------------------------------------------------------------------------- 1 | import http from './fly' 2 | 3 | // export const getIndex = params => { return http.get('/index/', params)}; 4 | 5 | // 新增记账记录 6 | export const addRecord = params => { return http.post('/accounting/addRecord', params)}; 7 | 8 | // 新增记账类别 9 | export const addCategory = params => { return http.post('/accounting/addCategory', params)}; 10 | 11 | // 获取类别列表 12 | export const getCategoryList = params => { return http.post('/accounting/getCategoryList', params)}; 13 | 14 | // 获取记账列表 15 | export const getAccountingList = params => {return http.post('/accounting/getAccountingList', params)}; 16 | 17 | // 更新类别 18 | export const updateCategoryList = params => { return http.post('/accounting/updateCategoryList', params)}; 19 | 20 | // 删除类别 21 | export const deleteCategory = params => { return http.post('/accounting/deleteCategory', params)}; 22 | 23 | // 获取记账总和 (月度和年度) 24 | export const getAmount = params => { return http.post('/accounting/getAmount', params)}; 25 | 26 | export const deleteAccounting = params => { return http.post('/accounting/deleteAccounting', params)}; 27 | 28 | //deleteAccounting -------------------------------------------------------------------------------- /server/controllers/addRecord.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const AccList = mongoose.model("AccList"); 4 | module.exports = async (ctx, next) => { 5 | let _id = ctx.request.body._id 6 | 7 | if (_id) { 8 | let res = await AccList.update( 9 | {'_id': _id}, 10 | {'$set': { 11 | 'recordYear': ctx.request.body.recordYear, 12 | 'recordMonth': ctx.request.body.recordMonth, 13 | 'recordDay': ctx.request.body.recordDay, 14 | 'price': ctx.request.body.price, 15 | 'title': ctx.request.body.title, 16 | 'icon': ctx.request.body.icon, 17 | 'remark': ctx.request.body.remark, 18 | 'bookId': ctx.request.body.bookId 19 | }} 20 | ).exec() 21 | ctx.body = { statusCode: 200, message: '更新成功', code: 1, data: res}; 22 | } else { 23 | delete ctx.request.body._id 24 | ctx.request.body.openid = ctx.state.user.openid 25 | let acc = new AccList(ctx.request.body) 26 | await acc.save() 27 | ctx.body = { statusCode: 200, message: '添加成功', code: 1}; 28 | } 29 | } -------------------------------------------------------------------------------- /src/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/main", 4 | "pages/addBooking/main", 5 | "pages/categoryList/main", 6 | "pages/addCategory/main", 7 | "pages/my/main" 8 | ], 9 | "window": { 10 | "backgroundTextStyle": "light", 11 | "navigationBarBackgroundColor": "#f9db61", 12 | "navigationBarTitleText": "WeChat", 13 | "navigationBarTextStyle": "black" 14 | }, 15 | "tabBar": { 16 | "color": "#999", 17 | "backgroundColor": "#fafafa", 18 | "selectedColor": "#333", 19 | "borderStyle": "white", 20 | 21 | "list": [{ 22 | "text": "首页", 23 | "pagePath": "pages/index/main", 24 | "iconPath": "/static/tabbar/icon_home.png", 25 | "selectedIconPath": "static/tabbar/icon_home_HL.png" 26 | }, 27 | { 28 | "text": "记账", 29 | "pagePath": "pages/index/main", 30 | "iconPath": "/static/tabbar/icon_release.png", 31 | "selectedIconPath": "/static/tabbar/icon_release.png" 32 | }, 33 | { 34 | "text": "我的", 35 | "pagePath": "pages/my/main", 36 | "iconPath": "/static/tabbar/icon_mine.png", 37 | "selectedIconPath": "/static/tabbar/icon_mine_HL.png" 38 | }], 39 | 40 | 41 | "position": "bottom" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/components/timePicker.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 51 | 52 | 70 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | function formatNumber (n) { 2 | const str = n.toString() 3 | return str[1] ? str : `0${str}` 4 | } 5 | 6 | function getTodayDate (d) { 7 | let date = d ? new Date(d) : new Date() 8 | return { 9 | year: date.getFullYear(), 10 | month: addZero(date.getMonth() + 1), 11 | day: addZero(date.getDate()) 12 | } 13 | } 14 | 15 | function addZero (num){ 16 | if (isNaN(num)) return 17 | if (parseInt(num) < 10 && parseInt(num) > 0) { 18 | return '0' + num; 19 | } else { 20 | return num 21 | } 22 | } 23 | 24 | // function getSystemInfo (vue) { 25 | // // console.log('utils',vue.globalData) 26 | // if (!vue.globalData) { 27 | // vue.globalData = {} 28 | // } else if (vue.globalData.systemInfo) { 29 | // return 30 | // } 31 | // return new Promise((resolve, reject) => { 32 | // wx.getSystemInfo({ 33 | // success(res) { 34 | // vue.globalData.systemInfo = res; 35 | // resolve(res) 36 | // console.log(vue) 37 | // }, 38 | // fail(err){ 39 | // reject(err) 40 | // } 41 | // }); 42 | // }) 43 | // } 44 | 45 | export function formatTime (date) { 46 | const year = date.getFullYear() 47 | const month = date.getMonth() + 1 48 | const day = date.getDate() 49 | 50 | const hour = date.getHours() 51 | const minute = date.getMinutes() 52 | const second = date.getSeconds() 53 | 54 | const t1 = [year, month, day].map(formatNumber).join('/') 55 | const t2 = [hour, minute, second].map(formatNumber).join(':') 56 | 57 | return `${t1} ${t2}` 58 | } 59 | 60 | export default { 61 | formatNumber, 62 | formatTime, 63 | getTodayDate, 64 | // getSystemInfo 65 | } 66 | -------------------------------------------------------------------------------- /server/routes/accounting.js: -------------------------------------------------------------------------------- 1 | const Router = require('koa-router') 2 | const addRecordController = require('../controllers/addRecord.js') 3 | const addCategoryController = require('../controllers/addCategory.js') 4 | const getCategoryListController = require('../controllers/getCategoryList.js') 5 | const getAccountingListController = require('../controllers/getAccountingList.js') 6 | const updateCategoryListController = require('../controllers/updateCategoryList.js') 7 | const deleteCategoryController = require('../controllers/deleteCategory.js') 8 | const getAmountController = require('../controllers/getAmount.js') 9 | const deleteAccountingController = require('../controllers/deleteAccounting.js') 10 | const getAccountingDetailsController = require('../controllers/getAccountingDetails.js') 11 | const getAccountingDaysNCountsController = require('../controllers/getAccountingDaysNCounts.js') 12 | 13 | let router = new Router() 14 | // 添加记账 15 | router.post('/addRecord', addRecordController) 16 | 17 | // 新增类别 18 | router.post('/addCategory', addCategoryController) 19 | 20 | // 获取类别列表 21 | router.post('/getCategoryList', getCategoryListController) 22 | 23 | // 获取记账列表(支出+收入) 24 | router.post('/getAccountingList', getAccountingListController) 25 | 26 | // 更新类别 27 | router.post('/updateCategoryList', updateCategoryListController) 28 | 29 | // 删除类别 30 | router.post('/deleteCategory', deleteCategoryController) 31 | 32 | // 获取记账总和(支出+收入) 33 | router.post('/getAmount', getAmountController) 34 | 35 | // 删除一条记账 36 | router.post('/deleteAccounting', deleteAccountingController) 37 | 38 | // 查询记账详情 39 | router.post('/getAccountingDetails', getAccountingDetailsController) 40 | 41 | // 查询记账天数 42 | router.get('/getAccountingDaysNCounts', getAccountingDaysNCountsController) 43 | 44 | 45 | 46 | 47 | module.exports = router -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 75 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa'); 2 | const app = new Koa(); 3 | const Router = require('koa-router') 4 | const path = require('path') 5 | const cors = require('koa2-cors') 6 | const fs = require('fs') 7 | const bodyParser = require('koa-bodyparser') 8 | const jwtKoa = require('koa-jwt') //验证token 9 | const jwt = require('jsonwebtoken') //创建token 10 | const mongoose = require('mongoose') 11 | const models = path.join(__dirname, './models'); 12 | const secret = 'appIdSessionId' //生成Token 的秘钥 13 | let router = new Router(); 14 | 15 | 16 | // 遍历 models 并引入 17 | fs.readdirSync(models) 18 | .filter(file => ~file.search(/^[^.].*\.js$/)) 19 | .forEach(file => require(path.join(models, file))); 20 | 21 | // 引入自定义中间件 22 | const userCheck = require('./middlewares/userCheck') 23 | const errorCatch = require('./middlewares/errorCatch') 24 | 25 | // 错误统一处理 26 | app.use(errorCatch); 27 | 28 | app.use(bodyParser()) 29 | 30 | //全局路由除了path 以外都需要携带token去请求 31 | app.use(jwtKoa({secret:secret}).unless({ 32 | path: [/\/user\/login/] 33 | })) 34 | 35 | app.use(userCheck) 36 | // 引入路由分发 37 | let userRoutes = require('./routes') 38 | let addAccountingRoutes = require('./routes/accounting.js') 39 | router.use('/user',userRoutes.routes()) 40 | router.use('/accounting',addAccountingRoutes.routes()) 41 | 42 | app.use(router.routes()) 43 | app.use(router.allowedMethods()) 44 | 45 | 46 | app.on('error', function(err){ 47 | console.log('errorEmiter',err); 48 | }) 49 | 50 | 51 | // 连接 mongoDB 52 | connect() 53 | function connect() { 54 | mongoose.connection 55 | .on('error', console.log) 56 | .on('disconnected', connect) 57 | .once('open', ()=> { 58 | app.listen(5000, () => console.log('[Server] starting at port 5000')) 59 | }); 60 | return mongoose.connect('mongodb://localhost:27017/bookkeepingDemo', { useNewUrlParser: true }); 61 | // return mongoose.connect('mongodb://test:test@localhost:27017/bookkeepingDemo', { useNewUrlParser: true }); 62 | } 63 | -------------------------------------------------------------------------------- /server/models/user.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const utils = require('../utils') 3 | 4 | const Schema = mongoose.Schema; 5 | const UserSchema = new Schema({ 6 | openid:String, 7 | userInfo: { 8 | nickName:String, 9 | avatarUrl:String, 10 | age: Number, 11 | gender: String, 12 | device: String, 13 | city: String, 14 | province: String, 15 | country: String, 16 | language: String, 17 | }, 18 | phoneNum: Number, 19 | platform: {type: Number, default: 0}, // 平台 20 | createAt:{type:Date,default: () => { return utils.localDate() } }, 21 | updateAt: {type:Date,default: () => { return utils.localDate() } }, 22 | token:String, 23 | defaultBook: String, 24 | bookList: [{ 25 | createAt: {type:Date,default: () => { return utils.localDate() } }, 26 | updateAt: {type:Date,default: () => { return utils.localDate() } }, 27 | bookName: String, 28 | color: String, 29 | initialDefault: {type:Boolean, default: false}, 30 | type: {type:String, default: 'default'}, 31 | }], 32 | payCategoryList: [ 33 | { 34 | createAt: {type:Date,default: () => { return utils.localDate() } }, 35 | updateAt: {type:Date,default: () => { return utils.localDate() } }, 36 | categoryName: String, 37 | iconClassName: String, 38 | isDefault: {type: Boolean, default: false} 39 | } 40 | ], 41 | incomeCategoryList: [ 42 | { 43 | createAt: {type:Date,default: () => { return utils.localDate() } }, 44 | updateAt: {type:Date,default: () => { return utils.localDate() } }, 45 | categoryName: String, 46 | iconClassName: String, 47 | isDefault: {type: Boolean, default: false} 48 | } 49 | ] 50 | }); 51 | 52 | UserSchema.statics = { 53 | 54 | // check user exists or not 55 | userCheck: function(openid) { 56 | if (!openid) return null 57 | return this.findOne({openid}) 58 | .exec(); 59 | }, 60 | 61 | updateToken: function (openid,token) { 62 | return this.updateOne({openid}, {token}) 63 | .exec() 64 | }, 65 | 66 | 67 | }; 68 | 69 | 70 | mongoose.model('User', UserSchema); -------------------------------------------------------------------------------- /src/stylus/index.styl: -------------------------------------------------------------------------------- 1 | // @import "./mixin.styl" 2 | // @import "./reset.styl" 3 | // @import "./common.styl" 4 | @import "./variables.styl" 5 | 6 | .isPhoneX-padding 7 | padding-bottom isPhoneXBottom !important 8 | .isPhoneX-bottom 9 | bottom isPhoneXBottom !important 10 | .isPhoneX-margin 11 | margin-bottom isPhoneXBottom !important 12 | 13 | // 图标列表 14 | .icon-wrap 15 | display:flex; 16 | flex-wrap:wrap; 17 | align-content:flex-start; 18 | justify-content:flex-start; 19 | .icon-item 20 | width (750px / 4) 21 | height (750px / 4) 22 | display flex 23 | align-items center 24 | justify-content center 25 | flex-direction column 26 | .circle 27 | width 110px 28 | height 110px 29 | background #f5f5f5 30 | border-radius 50% 31 | &.with-title 32 | margin-top 20px 33 | .icon-name 34 | margin-top 10px 35 | font-size 30px 36 | text-align center 37 | color fontColorGray 38 | &.active 39 | .circle 40 | background themeColor 41 | 42 | 43 | .bottomBtn 44 | height bottomBtnHeight 45 | width 100% 46 | position fixed 47 | bottom 0 48 | left 0 49 | background #fff 50 | font-size 30px 51 | text-align center 52 | line-height 100px 53 | box-shadow 0 0 2px #C4C4C4 54 | &.isIphoneX 55 | padding-bottom isPhoneXBottom 56 | 57 | .tab 58 | display flex 59 | position fixed 60 | top 0 61 | left 0 62 | width 100% 63 | height 100px 64 | background themeColor 65 | z-index 100 66 | .tab-item 67 | position relative 68 | flex 1 69 | height 100px 70 | box-sizing border-box 71 | color fontColor 72 | font-size 30px 73 | text-align center 74 | line-height 100px 75 | &.active:after 76 | content '' 77 | display block 78 | position absolute 79 | height 4px 80 | width 30% 81 | margin-left (- @width / 2) 82 | bottom: 0; 83 | left 50% 84 | background #000 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | var fileExtConfig = { 4 | swan: { 5 | template: 'swan', 6 | script: 'js', 7 | style: 'css', 8 | platform: 'swan' 9 | }, 10 | tt: { 11 | template: 'ttml', 12 | script: 'js', 13 | style: 'ttss', 14 | platform: 'tt' 15 | }, 16 | wx: { 17 | template: 'wxml', 18 | script: 'js', 19 | style: 'wxss', 20 | platform: 'wx' 21 | }, 22 | my: { 23 | template: 'axml', 24 | script: 'js', 25 | style: 'acss', 26 | platform: 'my' 27 | } 28 | } 29 | var fileExt = fileExtConfig[process.env.PLATFORM] 30 | 31 | module.exports = { 32 | build: { 33 | env: require('./prod.env'), 34 | index: path.resolve(__dirname, `../dist/${fileExt.platform}/index.html`), 35 | assetsRoot: path.resolve(__dirname, `../dist/${fileExt.platform}`), 36 | assetsSubDirectory: '', 37 | assetsPublicPath: '/', 38 | productionSourceMap: false, 39 | // Gzip off by default as many popular static hosts such as 40 | // Surge or Netlify already gzip all static assets for you. 41 | // Before setting to `true`, make sure to: 42 | // npm install --save-dev compression-webpack-plugin 43 | productionGzip: false, 44 | productionGzipExtensions: ['js', 'css'], 45 | // Run the build command with an extra argument to 46 | // View the bundle analyzer report after build finishes: 47 | // `npm run build --report` 48 | // Set to `true` or `false` to always turn it on or off 49 | bundleAnalyzerReport: process.env.npm_config_report, 50 | fileExt: fileExt 51 | }, 52 | dev: { 53 | env: require('./dev.env'), 54 | port: 8080, 55 | // 在小程序开发者工具中不需要自动打开浏览器 56 | autoOpenBrowser: false, 57 | assetsSubDirectory: '', 58 | assetsPublicPath: '/', 59 | proxyTable: {}, 60 | // CSS Sourcemaps off by default because relative paths are "buggy" 61 | // with this option, according to the CSS-Loader README 62 | // (https://github.com/webpack/css-loader#sourcemaps) 63 | // In our experience, they generally work as expected, 64 | // just be aware of this issue when enabling this option. 65 | cssSourceMap: false, 66 | fileExt: fileExt 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /server/controllers/getAmount.js: -------------------------------------------------------------------------------- 1 | const request = require('request') 2 | const mongoose = require('mongoose') 3 | const config = require('../config.js') 4 | 5 | const User = mongoose.model("User"); 6 | const AccList = mongoose.model("AccList"); 7 | module.exports = async (ctx, next) => { 8 | let recordYear = ctx.request.body.recordYear 9 | let recordMonth = ctx.request.body.recordMonth 10 | let bookId = ctx.request.body.bookId || '' 11 | 12 | let aggregate = null 13 | 14 | if (recordMonth) { 15 | aggregate = AccList.aggregate([ 16 | { $match: 17 | { 18 | 'openid':ctx.state.user.openid, 19 | 'recordYear': recordYear, 20 | 'recordMonth': recordMonth, 21 | 'isDeleted': 0 22 | } 23 | }, 24 | { 25 | $addFields: { 26 | "payAmount" : {$cond: { if: { $eq: [ "$recordType", 0 ] }, then: "$price", else: 0 }}, 27 | "incomeAmount" : {$cond: { if: { $eq: [ "$recordType", 1 ] }, then: "$price", else: 0 }} 28 | } 29 | } 30 | ]) 31 | if (bookId !== 'all') { 32 | aggregate = aggregate.match({ 'bookId': bookId}) 33 | } 34 | 35 | aggregate = await aggregate.group({ 36 | '_id': null, 37 | 'payAmount': { $sum: "$payAmount" }, 38 | 'incomeAmount': { $sum: "$incomeAmount" }, 39 | }) 40 | } else { 41 | aggregate = AccList.aggregate([ 42 | { $match: 43 | { 44 | 'openid':ctx.state.user.openid, 45 | 'recordYear': recordYear, 46 | 'isDeleted': 0 47 | } 48 | }, 49 | { 50 | $addFields: { 51 | "payAmount" : {$cond: { if: { $eq: [ "$recordType", 0 ] }, then: "$price", else: 0 }}, 52 | "incomeAmount" : {$cond: { if: { $eq: [ "$recordType", 1 ] }, then: "$price", else: 0 }} 53 | } 54 | } 55 | ]) 56 | 57 | if (bookId !== 'all') { 58 | aggregate = aggregate.match({ 'bookId': bookId}) 59 | } 60 | 61 | aggregate = await aggregate.group({ 62 | '_id': null, 63 | 'payAmount': { $sum: "$payAmount" }, 64 | 'incomeAmount': { $sum: "$incomeAmount" }, 65 | }) 66 | } 67 | 68 | ctx.body = { statusCode: 200, message: '获取记账总和成功', data: aggregate[0]}; 69 | } 70 | -------------------------------------------------------------------------------- /server/controllers/getAccountingList.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const AccList = mongoose.model("AccList"); 3 | 4 | module.exports = async (ctx, next) => { 5 | let recordYear = ctx.request.body.recordYear 6 | let recordMonth = ctx.request.body.recordMonth 7 | let bookId = ctx.request.body.bookId || '' 8 | 9 | let aggregate = null 10 | if (recordMonth) { 11 | aggregate = AccList.aggregate([ 12 | { $match: { 13 | 'openid':ctx.state.user.openid, 14 | 'recordYear': recordYear, 15 | 'recordMonth': recordMonth, 16 | 'isDeleted': 0 17 | } 18 | }, 19 | { $sort : { 'recordDay': 1} }, 20 | { 21 | $addFields: { 22 | "payAmount" : {$cond: { if: { $eq: [ "$recordType", 0 ] }, then: "$price", else: 0 }}, 23 | "incomeAmount" : {$cond: { if: { $eq: [ "$recordType", 1 ] }, then: "$price", else: 0 }} 24 | } 25 | } 26 | ]) 27 | if (bookId !== 'all') { 28 | aggregate = aggregate.match({ 'bookId': bookId}) 29 | } 30 | aggregate = aggregate.group({ 31 | '_id': '$recordDay', 32 | 'payAmount': { $sum: "$payAmount" }, 33 | 'incomeAmount': { $sum: "$incomeAmount" }, 34 | "content": { 35 | $push: { 36 | _id: "$_id", 37 | icon: "$icon", 38 | title: "$title" , 39 | price: '$price', 40 | recordType: '$recordType', 41 | bookId: '$bookId', 42 | remark: '$remark' 43 | } 44 | } 45 | }).sort({'_id': -1}) 46 | 47 | let data = await aggregate.exec() 48 | 49 | ctx.body = { statusCode: 200, message: '获取记账列表成功', data: { 50 | categoryList: data 51 | }}; 52 | } else { 53 | // aggregate = await AccList.aggregate([ 54 | // {$match: {'openid':ctx.state.user.openid}}, 55 | // // {$unwind: '$accountingList'}, 56 | // { $match: 57 | // { 58 | // 'recordYear': recordYear, 59 | // } 60 | // }, 61 | // { 62 | // $group: { 63 | // '_id': '$recordMonth', 64 | // "content": { 65 | // // $push: '$accountingList' 66 | // } 67 | // } 68 | // } 69 | // ]) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bookKeeping-mpvue", 3 | "version": "1.0.0", 4 | "mpvueTemplateProjectVersion": "0.1.0", 5 | "description": "A Mpvue project", 6 | "author": "Lance Tian", 7 | "private": true, 8 | "scripts": { 9 | "dev:wx": "node build/dev-server.js wx", 10 | "start:wx": "npm run dev:wx", 11 | "build:wx": "node build/build.js wx", 12 | "dev:swan": "node build/dev-server.js swan", 13 | "start:swan": "npm run dev:swan", 14 | "build:swan": "node build/build.js swan", 15 | "dev:tt": "node build/dev-server.js tt", 16 | "start:tt": "npm run dev:tt", 17 | "build:tt": "node build/build.js tt", 18 | "dev:my": "node build/dev-server.js my", 19 | "start:my": "npm run dev:my", 20 | "build:my": "node build/build.js my", 21 | "dev": "node build/dev-server.js wx", 22 | "start": "npm run dev", 23 | "build": "node build/build.js wx" 24 | }, 25 | "dependencies": { 26 | "echarts": "^4.2.1", 27 | "flyio": "^0.6.14", 28 | "mpvue": "^2.0.0", 29 | "mpvue-echarts": "^0.3.1", 30 | "vuex": "^3.0.1" 31 | }, 32 | "devDependencies": { 33 | "babel-core": "^6.22.1", 34 | "babel-loader": "^7.1.1", 35 | "babel-plugin-transform-runtime": "^6.22.0", 36 | "babel-preset-env": "^1.3.2", 37 | "babel-preset-stage-2": "^6.22.0", 38 | "babel-register": "^6.22.0", 39 | "chalk": "^2.4.2", 40 | "connect-history-api-fallback": "^1.3.0", 41 | "copy-webpack-plugin": "^4.5.1", 42 | "css-loader": "^0.28.11", 43 | "cssnano": "^3.10.0", 44 | "eventsource-polyfill": "^0.9.6", 45 | "express": "^4.16.3", 46 | "extract-text-webpack-plugin": "^3.0.2", 47 | "file-loader": "^1.1.11", 48 | "friendly-errors-webpack-plugin": "^1.7.0", 49 | "glob": "^7.1.2", 50 | "html-webpack-plugin": "^3.2.0", 51 | "http-proxy-middleware": "^0.18.0", 52 | "mkdirp": "^0.5.1", 53 | "mpvue-loader": "^2.0.0", 54 | "mpvue-template-compiler": "^2.0.0", 55 | "mpvue-webpack-target": "^1.0.3", 56 | "optimize-css-assets-webpack-plugin": "^3.2.0", 57 | "ora": "^2.0.0", 58 | "portfinder": "^1.0.13", 59 | "postcss-loader": "^2.1.4", 60 | "postcss-mpvue-wxss": "^1.0.0", 61 | "prettier": "~1.12.1", 62 | "px2rpx-loader": "^0.1.10", 63 | "relative": "^3.0.2", 64 | "rimraf": "^2.6.0", 65 | "semver": "^5.7.0", 66 | "shelljs": "^0.8.3", 67 | "stylus": "^0.54.5", 68 | "stylus-loader": "^3.0.2", 69 | "uglifyjs-webpack-plugin": "^1.2.5", 70 | "url-loader": "^1.0.1", 71 | "vue-style-loader": "^4.1.0", 72 | "webpack": "^3.11.0", 73 | "webpack-bundle-analyzer": "^2.2.1", 74 | "webpack-dev-middleware-hard-disk": "^1.12.0", 75 | "webpack-merge": "^4.1.0", 76 | "webpack-mpvue-asset-plugin": "^2.0.0", 77 | "webpack-mpvue-vendor-plugin": "^2.0.0" 78 | }, 79 | "engines": { 80 | "node": ">= 4.0.0", 81 | "npm": ">= 3.0.0" 82 | }, 83 | "browserslist": [ 84 | "> 1%", 85 | "last 2 versions", 86 | "not ie <= 8" 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /src/utils/fly.js: -------------------------------------------------------------------------------- 1 | import Fly from "flyio/dist/npm/wx" 2 | import store from '@/store' 3 | import login from '@/utils/login' 4 | 5 | const fly = new Fly 6 | const timeThreshold = 1000 7 | 8 | //定义公共headers 9 | // fly.config.headers={xx:5,bb:6,dd:7} 10 | //设置超时 11 | fly.config.timeout=10000; 12 | //设置请求基地址 13 | fly.config.baseURL=process.env.API_ROOT; 14 | 15 | 16 | 17 | 18 | //添加请求拦截器 19 | fly.interceptors.request.use((request)=>{ 20 | // console.log(store.state.status) 21 | wx.showLoading({ 22 | title: '加载中...', 23 | mask: true, 24 | }) 25 | let token = store.state.token 26 | if (!token || store.state.status == 'error') { 27 | console.log('走这') 28 | fly.lock(); 29 | 30 | store.commit('AUTH_REQUEST') 31 | return login().then(res => { 32 | store.commit('AUTH_SUCCESS', res.data.Token) 33 | request.headers["Authorization"]=`Bearer ${res.data.Token}` 34 | return request 35 | }).finally(() => fly.unlock()) 36 | .catch(err => { 37 | store.commit('AUTH_ERROR') 38 | }) 39 | 40 | // return new Promise((resolve, reject) => { 41 | // store.commit('AUTH_REQUEST') 42 | // wx.login({ 43 | // success: function (res) { 44 | // if (res.code) { 45 | // //发起网络请求 46 | // wx.request({ 47 | // url: `${process.env.API_ROOT}/user/login`, 48 | // data: { 49 | // code: res.code 50 | // }, 51 | // success:res=>{ 52 | // // console.log(res.data) 53 | // store.commit('AUTH_SUCCESS', res.data.Token) 54 | // request.headers["Authorization"]=`Bearer ${res.data.Token}` 55 | // fly.unlock(); 56 | // resolve(request) 57 | 58 | // } 59 | // }) 60 | // } else { 61 | // fly.unlock(); 62 | // reject(res) 63 | // store.commit('AUTH_ERROR') 64 | // console.log('登录失败!' + res.errMsg) 65 | // } 66 | // } 67 | // }); 68 | // }) 69 | } else { 70 | //给所有请求添加自定义header 71 | request.headers["Authorization"]=`Bearer ${token}` 72 | return request; 73 | } 74 | 75 | }) 76 | 77 | //添加响应拦截器,响应拦截器会在then/catch处理之前执行 78 | fly.interceptors.response.use(function (response) { 79 | wx.hideLoading(); 80 | 81 | //只将请求结果的data字段返回 82 | return response.data 83 | },function (err) { 84 | wx.hideLoading(); 85 | console.log(err) 86 | // 87 | if (err.status == 401 && err.response.data.code == 'TokenExpiredError') { 88 | console.log(this.lastResTime) 89 | // console.log((new Date() - this.lastResTime )) 90 | if (this.lastResTime && ((new Date() - this.lastResTime) < timeThreshold )) { 91 | return fly.request(err.request) 92 | } 93 | this.lock(); 94 | store.commit('AUTH_REQUEST') 95 | return login().then(res => { 96 | store.commit('AUTH_SUCCESS', res.data.Token) 97 | this.lastResTime = new Date() 98 | }).finally(() => this.unlock()) 99 | .then(() => { 100 | return fly.request(err.request); 101 | }) 102 | .catch(res => { 103 | store.commit('AUTH_ERROR') 104 | console.log('登录失败!' + res.errMsg) 105 | }) 106 | } else { 107 | // return fly.request(err.request); 108 | // wx.switchTab({ 109 | // url: "/pages/index/main" 110 | // }) 111 | } 112 | // console.log(err) 113 | // return Promise.resolve('sssssss') 114 | } 115 | ) 116 | 117 | export default fly -------------------------------------------------------------------------------- /src/pages/my/index.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 65 | 66 | 143 | -------------------------------------------------------------------------------- /src/components/tabBar.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 91 | 92 | 145 | -------------------------------------------------------------------------------- /src/pages/addCategory/index.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 112 | 113 | 155 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | const Vue = require('vue') 2 | const Vuex = require('vuex') 3 | 4 | Vue.use(Vuex) 5 | import { getCategoryList, getAccountingList } from '@/utils/api' 6 | export default new Vuex.Store({ 7 | state: { 8 | status: '', 9 | token: wx.getStorageSync('token') || '', 10 | user : {}, 11 | systemInfo: null, 12 | pList: null, 13 | iList: null, 14 | cateListNeedRefresh: false, 15 | accListNeedRefresh: false, 16 | accList: null, 17 | rootUrl: wx.getStorageSync('API_ROOT'), 18 | indexListCount: 0, 19 | // plist: null 20 | }, 21 | mutations: { 22 | ['AUTH_REQUEST'](state) { 23 | state.status = 'loading'; 24 | }, 25 | ['AUTH_SUCCESS'](state, token) { 26 | state.status = 'success'; 27 | state.token = token; 28 | wx.setStorageSync('token', token) 29 | }, 30 | ['AUTH_ERROR'](state) { 31 | state.status = 'error'; 32 | }, 33 | ['GET_SYSTEMINFO_SUCCESS'](state, systemInfo) { 34 | state.systemInfo = systemInfo; 35 | }, 36 | ['GET_PAYLIST'](state, payList){ 37 | state.pList = payList 38 | }, 39 | ['GET_INCOMELIST'](state, incomeList){ 40 | state.iList = incomeList 41 | }, 42 | ['CATELIST_NEED_REFRESH'](state){ 43 | state.cateListNeedRefresh = true 44 | }, 45 | ['CATELIST_REFRESH_DONE'](state){ 46 | state.cateListNeedRefresh = false 47 | }, 48 | ['GET_ACCLIST'](state, accList){ 49 | state.accList = accList 50 | }, 51 | ['ACCLIST_NEED_REFRESH'](state){ 52 | state.accListNeedRefresh = true 53 | }, 54 | ['ACCLIST_REFRESH_DONE'](state){ 55 | state.accListNeedRefresh = false 56 | }, 57 | ['INDEX_LIST_COUNT'](state, count){ 58 | state.indexListCount = count 59 | } 60 | }, 61 | actions: { 62 | login({commit, state}){ 63 | return new Promise((resolve, reject) => { 64 | wx.login({ 65 | success: function (res) { 66 | if (res.code) { 67 | //发起网络请求 68 | wx.request({ 69 | url: `${state.rootUrl}/user/login`, 70 | data: { 71 | code: res.code 72 | }, 73 | success:res=>{ 74 | console.log(res.data) 75 | // self.globalData.Token = res.data.Token 76 | commit('AUTH_SUCCESS', res.data.Token) 77 | resolve(res) 78 | }, 79 | fail: err => { 80 | reject(err) 81 | } 82 | }) 83 | } else { 84 | reject(res) 85 | console.log('登录失败!' + res.errMsg) 86 | } 87 | } 88 | }); 89 | }) 90 | }, 91 | getSystemInfo({commit, state}){ 92 | return new Promise((resolve, reject) => { 93 | if (state.systemInfo) { 94 | resolve(state.systemInfo) 95 | } else { 96 | wx.getSystemInfo({ 97 | success(res) { 98 | commit('GET_SYSTEMINFO_SUCCESS', res) 99 | resolve(res) 100 | }, 101 | fail(err){ 102 | reject(err) 103 | } 104 | }); 105 | } 106 | 107 | }) 108 | }, 109 | getCategoryList({commit, state}, recordType){ 110 | return new Promise((resolve, reject)=> { 111 | getCategoryList({recordType}).then(res => { 112 | 113 | if (recordType == 0) { 114 | commit('GET_PAYLIST', res.data.categoryList) 115 | } else if (recordType == 1) { 116 | commit('GET_INCOMELIST', res.data.categoryList) 117 | } 118 | resolve(res) 119 | }).catch(err => { 120 | reject(err) 121 | }) 122 | }) 123 | }, 124 | getAccountingList({commit, state}, params){ 125 | 126 | new Promise ((resolve, reject)=> { 127 | getAccountingList(params).then(res => { 128 | console.log(res) 129 | commit('GET_ACCLIST', res.data.categoryList)// 130 | commit('INDEX_LIST_COUNT', res.data.count)//INDEX_LIST_COUNT 131 | commit("ACCLIST_REFRESH_DONE") 132 | 133 | resolve(res) 134 | }).catch(err => { 135 | reject(err) 136 | }) 137 | }) 138 | 139 | }, 140 | }, 141 | getters: { 142 | isIphoneX: state => { 143 | console.log(state.systemInfo) 144 | return state.systemInfo ? state.systemInfo.model.includes("iPhone X") : false 145 | }, 146 | incomeList: state => { 147 | return state.incomeCategoryList 148 | }, 149 | pList: state=> state.pList, 150 | iList: state=> state.iList, 151 | accList: state => state.accList, 152 | 153 | 154 | } 155 | }) -------------------------------------------------------------------------------- /static/font/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Transfonter demo 10 | 11 | 176 | 177 | 178 |
179 |
180 |

iconfont

181 |
.your-style {
182 |     font-family: 'iconfont';
183 |     font-weight: normal;
184 |     font-style: normal;
185 | }
186 |
187 |

188 | abcdefghijklmnopqrstuvwxyz
189 | ABCDEFGHIJKLMNOPQRSTUVWXYZ
190 | 0123456789.:,;()*!?'@#<>$%&^+-=~ 191 |

192 |

The quick brown fox jumps over the lazy dog.

193 |

The quick brown fox jumps over the lazy dog.

194 |

The quick brown fox jumps over the lazy dog.

195 |

The quick brown fox jumps over the lazy dog.

196 |

The quick brown fox jumps over the lazy dog.

197 |

The quick brown fox jumps over the lazy dog.

198 |

The quick brown fox jumps over the lazy dog.

199 |

The quick brown fox jumps over the lazy dog.

200 |

The quick brown fox jumps over the lazy dog.

201 |

The quick brown fox jumps over the lazy dog.

202 |

The quick brown fox jumps over the lazy dog.

203 |
204 |
205 |
206 | 207 | -------------------------------------------------------------------------------- /server/controllers/login.js: -------------------------------------------------------------------------------- 1 | const request = require('request') 2 | const mongoose = require('mongoose') 3 | const jwt = require('jsonwebtoken') //创建token 4 | const secret = 'appIdSessionId' //生成Token 的秘钥 5 | const config = require('../config.js') 6 | const utils = require('../utils') 7 | 8 | let expiresIn = 60 * 60 * 24 9 | if(process.env.NODE_ENV == 'local'){ 10 | expiresIn = 60 * 60 11 | } 12 | module.exports = async (ctx, next) => { 13 | let resultData = '' 14 | function getAppId(appid, secret, js_code) { 15 | return new Promise((resolve, reject) => { 16 | request.get( 17 | { 18 | url: "https://api.weixin.qq.com/sns/jscode2session", 19 | json: true, 20 | qs: { 21 | grant_type: "authorization_code", 22 | appid: appid, 23 | secret: secret, 24 | js_code: js_code 25 | } 26 | }, 27 | (error, response, body) => { 28 | if (!error && response.statusCode == 200) { 29 | if (body.errcode) { 30 | return reject(body.errmsg) 31 | } 32 | // console.log("小程序端返回的结果") 33 | // console.log("[openid]", body.openid) 34 | // console.log("[session_key]", body.session_key) 35 | resolve(body); 36 | } else { 37 | reject(error); 38 | } 39 | } 40 | ); 41 | }); 42 | } 43 | try { 44 | resultData = await getAppId(config.appId, config.appSecret, ctx.query.code) 45 | } catch (err) { 46 | ctx.throw(500, err) 47 | } 48 | 49 | const User = mongoose.model("User"); 50 | 51 | let user = await User.userCheck(resultData.openid) 52 | if (user) { 53 | console.log("开始对比数据"); 54 | let token = user.token 55 | 56 | try { 57 | let decoded = await jwt.verify(token, secret) 58 | ctx.body = { statusCode: 200, message: '校验成功', Token: user.token }; 59 | console.log("token校验成功",decoded); 60 | } catch (err) { 61 | if (err && err.name == 'TokenExpiredError') { 62 | let userToken={ 63 | openid: resultData.openid 64 | } 65 | 66 | const token = jwt.sign(userToken,secret,{expiresIn: expiresIn }) //token签名 67 | User.updateToken(resultData.openid, token) 68 | console.log("token更新成功"); 69 | 70 | ctx.body = { statusCode: 200 , message: '更新token成功', Token:token}; 71 | } 72 | else if (err) throw err; 73 | } 74 | 75 | } else { 76 | let userToken={ 77 | openid: resultData.openid 78 | } 79 | const token = jwt.sign(userToken,secret,{expiresIn: expiresIn }) //token签名 有效期为24小时 80 | 81 | // 添加默认支出类型 82 | let payCategoryList= [ 83 | { 84 | createAt: utils.localDate(), 85 | updateAt: utils.localDate(), 86 | iconClassName: 'icon-canju', 87 | categoryName: '饮食', 88 | key: 0, 89 | isDefault: true 90 | }, 91 | { 92 | createAt: utils.localDate(), 93 | updateAt: utils.localDate(), 94 | iconClassName: 'icon-youxi', 95 | categoryName: '娱乐', 96 | key: 1, 97 | isDefault: true 98 | }, 99 | { 100 | createAt: utils.localDate(), 101 | updateAt: utils.localDate(), 102 | iconClassName: 'icon-lvyou', 103 | categoryName: '旅游', 104 | key: 2, 105 | isDefault: true 106 | }, 107 | { 108 | createAt: utils.localDate(), 109 | updateAt: utils.localDate(), 110 | iconClassName: 'icon-jiaotong', 111 | categoryName: '交通', 112 | key: 3, 113 | isDefault: true 114 | }, 115 | { 116 | createAt: utils.localDate(), 117 | updateAt: utils.localDate(), 118 | iconClassName: 'icon-gouwu', 119 | categoryName: '购物', 120 | key: 4, 121 | isDefault: true 122 | }, 123 | { 124 | createAt: utils.localDate(), 125 | updateAt: utils.localDate(), 126 | iconClassName: 'icon-huaban', 127 | categoryName: '医疗', 128 | key: 5, 129 | isDefault: true 130 | }, 131 | { 132 | createAt: utils.localDate(), 133 | updateAt: utils.localDate(), 134 | iconClassName: 'icon-xuexi-', 135 | categoryName: '学习', 136 | key: 6, 137 | isDefault: true 138 | } 139 | ] 140 | 141 | let incomeCategoryList = [ 142 | { 143 | createAt: utils.localDate(), 144 | updateAt: utils.localDate(), 145 | iconClassName: "icon-yuangonggongzi", 146 | categoryName: "工资", 147 | key: 0, 148 | isDefault: true 149 | }, 150 | { 151 | createAt: utils.localDate(), 152 | updateAt: utils.localDate(), 153 | iconClassName: "icon-lijin", 154 | categoryName: "礼金", 155 | key: 1, 156 | isDefault: true 157 | }, 158 | { 159 | createAt: utils.localDate(), 160 | updateAt: utils.localDate(), 161 | iconClassName: "icon-licai", 162 | categoryName: "理财", 163 | key: 2, 164 | isDefault: true 165 | }, 166 | { 167 | createAt: utils.localDate(), 168 | updateAt: utils.localDate(), 169 | iconClassName: "icon-jianzhi", 170 | categoryName: "兼职", 171 | key: 3, 172 | isDefault: true 173 | }, 174 | ] 175 | 176 | 177 | let newUser = new User({ 178 | openid: resultData.openid, 179 | token: token, 180 | payCategoryList, 181 | incomeCategoryList 182 | }); 183 | 184 | try { 185 | await newUser.save() 186 | console.log("注册成功"); 187 | ctx.body = { statusCode: 200 , message: '获取token成功',Token:token}; 188 | } catch (error) { 189 | console.log(error); 190 | ctx.throw(500, error) 191 | } 192 | 193 | } 194 | 195 | next() 196 | 197 | // User.userCheck(resultData.openid).then(async user => { 198 | // console.log(user) 199 | 200 | // }) 201 | } -------------------------------------------------------------------------------- /src/components/movableSort.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 203 | 204 | 280 | -------------------------------------------------------------------------------- /src/pages/categoryList/index.vue: -------------------------------------------------------------------------------- 1 | 81 | 82 | 246 | 247 | 352 | -------------------------------------------------------------------------------- /src/components/keyboard.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 260 | 261 | 363 | 364 | 365 | 366 | -------------------------------------------------------------------------------- /src/pages/addBooking/index.vue: -------------------------------------------------------------------------------- 1 | 91 | 92 | 321 | 322 | 417 | -------------------------------------------------------------------------------- /src/pages/index/index.vue: -------------------------------------------------------------------------------- 1 | 120 | 121 | 300 | 301 | 451 | -------------------------------------------------------------------------------- /static/font/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'iconfont'; 3 | src: url(data:application/font-woff2;charset=utf-8;base64,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) format('woff2'), 4 | url(data:application/font-woff;charset=utf-8;base64,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) format('woff'), 5 | url('iconfont.ttf') format('truetype'); 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | .iconfont { 11 | font-family: "iconfont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-yundong:before { 19 | content: "\e600"; 20 | } 21 | 22 | .icon-lijin:before { 23 | content: "\e658"; 24 | } 25 | 26 | .icon-licai:before { 27 | content: "\e65b"; 28 | } 29 | 30 | .icon-jianzhi:before { 31 | content: "\e65d"; 32 | } 33 | 34 | .icon-youxi:before { 35 | content: "\e643"; 36 | } 37 | 38 | .icon-rili:before { 39 | content: "\e638"; 40 | } 41 | 42 | .icon-yundong1:before { 43 | content: "\e807"; 44 | } 45 | 46 | .icon-yidongheng:before { 47 | content: "\e645"; 48 | } 49 | 50 | .icon-lvyou:before { 51 | content: "\e657"; 52 | } 53 | 54 | .icon-shanchu:before { 55 | content: "\e625"; 56 | } 57 | 58 | .icon-yuangonggongzi:before { 59 | content: "\e63e"; 60 | } 61 | 62 | .icon-gouwu:before { 63 | content: "\e616"; 64 | } 65 | 66 | .icon-jiaotong:before { 67 | content: "\e61e"; 68 | } 69 | 70 | .icon-icon11:before { 71 | content: "\e60a"; 72 | } 73 | 74 | .icon-shanchu1:before { 75 | content: "\e612"; 76 | } 77 | 78 | .icon-shenghuo:before { 79 | content: "\e605"; 80 | } 81 | 82 | .icon-jinqian:before { 83 | content: "\e651"; 84 | } 85 | 86 | .icon-xuexi-:before { 87 | content: "\e609"; 88 | } 89 | 90 | .icon-shengri:before { 91 | content: "\e619"; 92 | } 93 | 94 | .icon-canju:before { 95 | content: "\e61a"; 96 | } 97 | 98 | .icon-jiahao:before { 99 | content: "\e61c"; 100 | } 101 | 102 | .icon-yiliao:before { 103 | content: "\e7f1"; 104 | } 105 | 106 | .icon-huaban:before { 107 | content: "\e60c"; 108 | } 109 | 110 | .icon-Icons_ToolBar_ArrowRight:before { 111 | content: "\e61b"; 112 | } 113 | 114 | .icon-op0:before { 115 | content: "\e728"; 116 | } 117 | 118 | .icon-op2:before { 119 | content: "\e741"; 120 | } 121 | 122 | .icon-op3:before { 123 | content: "\e748"; 124 | } 125 | 126 | .icon-op1:before { 127 | content: "\e778"; 128 | } 129 | 130 | .icon--biaodanfenlei:before { 131 | content: "\e694"; 132 | } 133 | 134 | .icon-quchujinzhi-copy:before { 135 | content: "\e666"; 136 | } 137 | 138 | 139 | 140 | --------------------------------------------------------------------------------