# 移动端记账本 **Repository Path**: kennana/mobile_account_book ## Basic Information - **Project Name**: 移动端记账本 - **Description**: 移动端记账本 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2018-12-27 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # account ## Project setup ``` npm install ``` ### Compiles and hot-reloads for development ``` npm run serve ``` ### Compiles and minifies for production ``` npm run build ``` ### Run your tests ``` npm run test ``` ### Lints and fixes files ``` npm run lint ``` ### Customize configuration See [Configuration Reference](https://cli.vuejs.org/config/). #### 两个目的 **第一个目的**:`这一个移动端记账本的创作来源呢,其实是我在学习了理财的课程之后,突然想为自己写一个记账的东西来记录自己每天花费的钱,从而可以降低那些不必要的开销,从而达到理财的第一步。` **当然还有另一个目的就是**:`做这个移动端简单的项目,主要是为了熟悉vue.js,从项目构建到完成目录,以及后台数据库的设计,后台逻辑的处理,全程由我自己一个人完成,这个项目历史大概有1个多月吧,虽然项目看起来很小,但是简单亦不简单啊,这个时间段遇到很多问题,都是自己一个人靠着百度,自己理解解决了问题,总的来说,这个项目对我来还是蛮重要的,还是我的第一个开源项目吧,希望大佬不要嘲笑,看到的尽管留言,给出完善的意见,感谢` #### 项目用的技术展 ###### holderjs:一开始设计原型的时候使用站位图插件 其用法如下: holder.js 可以帮我们快速生成占位图片,而且还能定义占位图片的一些基本样式。 用法简单,可以直接去官网下载,或直接引用Bootcss的CDN : 1、基本:默认单位为px,用小写的x连接图片的宽高: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190120233525155.png) 2、使用p代表%: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190120233540592.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzcyODY2,size_16,color_FFFFFF,t_70) 3、让占位图片在缩放长宽比,可以加上auto参数: 4、配色方案: holder.js共定义了6种占位图片配色,分别是:sky、vine、lava、gray、industrial、social 配色可以通过theme参数设置: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190120233557612.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzcyODY2,size_16,color_FFFFFF,t_70) 5、随机配色: #### 优秀的远程服务插件 axios:地址入口 : https://www.kancloud.cn/yunye/axios/234845 用法如下: 安装 使用 npm: $ npm install axios 使用 bower: $ bower install axios 使用 cdn: Example 执行 GET 请求 // 为给定 ID 的 user 创建请求 axios.get('/user?ID=12345') .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); // 可选地,上面的请求可以这样做 axios.get('/user', { params: { ID: 12345 } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); 执行 POST 请求 axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); 执行多个并发请求 function getUserAccount() { return axios.get('/user/12345'); } function getUserPermissions() { return axios.get('/user/12345/permissions'); } axios.all([getUserAccount(), getUserPermissions()]) .then(axios.spread(function (acct, perms) { // 两个请求现在都执行完成 })); axios API 可以通过向 axios 传递相关配置来创建请求 axios(config) // 发送 POST 请求 axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } }); axios(url[, config]) // 发送 GET 请求(默认的方法) axios('/user/12345'); 请求方法的别名 为方便起见,为所有支持的请求方法提供了别名 axios.request(config) axios.get(url[, config]) axios.delete(url[, config]) axios.head(url[, config]) axios.post(url[, data[, config]]) axios.put(url[, data[, config]]) axios.patch(url[, data[, config]]) #### 自己封装了一下字体响应式改变 rem.js ``` // 设置字体大小 setFontSize(){ (function(doc,win){ var docEl = doc.documentElement, //文档根标签 resizeEvent = 'orientationchange' in window ? 'orientationchang' :'resize'; //viewport变化事件源 var rescale = function(){ //重置方法 win.clientWidth = docEl.clientWidth; if (!win.clientWidth) return; // 改变DOM根节点fontSize大小的值; // (屏幕宽度/设计图宽度) = 缩放或扩大的比例值; docEl.style.fontSize = 40 * (win.clientWidth / 750) + 'px'; } if (!doc.addEventListener) return; win.addEventListener(resizeEvent, rescale, false); doc.addEventListener('DOMContentLoaded', rescale, false); })(document, window); } ``` #### 自己对axios.post()和axios.get()方法进行二次封装 axios_get_post.js ``` // eslint-disable-next-line /* eslint-disable */ // 对 axios.get() 和 axios.post()进行封装 import axios from 'axios' import qs from 'qs' /* post 方式 axios({ method: 'post', url: '/user/123456', data: { username: 'ken', password: '123456' } }) axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }) get 方式 axios({ method: 'get', url: '/user/123456', }) // 可选地,上面的请求可以这样做 axios.get('/user', { params: { ID: 12345 } }) //伪代码的编写 axios({ method: 'post' || 'get', url: '/user/123456', data: { username: 'ken', password: '123456', } || params: { ID: '123456' } }) */ export default function axios_get_post(params){ return new Promise((resolve, reject)=>{ let opt = { method: params.method || 'get', url: params.url, headers: { 'Content-Type':'application/x-www-form-urlencoded' }, } if(params.method == 'post'){ opt.data = params.data }else { // opt.params = params } // 拦截处理 axios.interceptors.request.use((req) => { if (req.method === 'post') { // 转换成JSON格式 req.data = qs.stringify(req.data); } return req; }, (error) => Promise.reject(error)); axios(opt).then(res=>{ resolve(res.data) }).catch(err=>{ reject(err) }) }) } ``` #### 当初封装上述函数的时候遇到一个问题如下: axios.js以post方式传递数据后台获取不到数据,这到底是什么原因 #### mint-ui的插件的使用 在这个项目中使用的最多就是MessageBox()这个组件 npm 安装 推荐使用 npm 的方式安装,它能更好地和 webpack 打包工具配合使用。 npm i mint-ui@1 -S CDN 目前可以通过 unpkg.com/mint-ui@1 获取到最新版本的资源,在页面上引入 js 和 css 文件即可开始使用。 Hello world 通过 CDN 的方式我们可以很容易地使用 Mint UI 写出一个 Hello world 页面。
按钮
#### Message box使用 引入 import { MessageBox } from 'mint-ui'; 例子 以标题与内容字符串为参数进行调用 MessageBox('提示', '操作成功'); 或者传入一个对象 MessageBox({ title: '提示', message: '确定执行此操作?', showCancelButton: true }); 此外,MessageBox 还提供了 alert、confirm 和 prompt 三个方法,它们都返回一个 Promise MessageBox.alert(message, title); MessageBox.alert('操作成功').then(action => { ... }); MessageBox.confirm(message, title); MessageBox.confirm('确定执行此操作?').then(action => { ... }); MessageBox.prompt(message, title); MessageBox.prompt('请输入姓名').then(({ value, action }) => { ... }); #### 滚动加载数据组件 Infinite scroll 引入 import { InfiniteScroll } from 'mint-ui'; Vue.use(InfiniteScroll); 例子 为 HTML 元素添加 v-infinite-scroll 指令即可使用无限滚动。滚动该元素,当其底部与被滚动元素底部的距离小于给定的阈值(通过 infinite-scroll-distance 设置)时,绑定到 v-infinite-scroll 指令的方法就会被触发。 loadMore() { this.loading = true; setTimeout(() => { let last = this.list[this.list.length - 1]; for (let i = 1; i <= 10; i++) { this.list.push(last + i); } this.loading = false; }, 2500); } #### 自己还封装一个简单的滚动加载组件 之前有些过一篇关于这个组件的博客文章:https://blog.csdn.net/qq_36772866/article/details/86530684 也在知乎写过同样的文章: https://zhuanlan.zhihu.com/p/55123532 码云地址如下:https://gitee.com/kennana/vue_component.git 只需要:`git clone https://gitee.com/kennana/vue_component.git` ``` cd vue_component //进入vue_component目录 npm install //执行此命令 npm run serve //执行此命令即可将项目跑起来 ``` #### 遇到跨域问题的解决方案如下 vue.js请求后台遇到跨域引爆这篇文章 #### Vue CLI3本地代理配置 vue-cli3的本地代理配置 #### 使用了vue-lazyload图片懒加载 一. vue lazyload插件: 插件地址:https://github.com/hilongjw/vue-lazyload demo:http://hilongjw.github.io/vue-lazyload/ 二. 简单使用实例: 这个插件还是蛮好用的,就是感觉这个插件的开发文档有点太啰嗦了,一股脑把所有的api扩展都罗列出来,源码中并没有可以运行的实例提供。 其实这个插件做简单使用的话是很简单的,看官方文档的话反而被误导了,可以先按下边的实例实现简单引用,后边再根据开发文档做扩展。 1. 安装插件: ``` npm install vue-lazyload --save-dev ``` 2. main.js引入插件: ``` import VueLazyLoad from 'vue-lazyload' Vue.use(VueLazyLoad,{ error:'./static/error.png', loading:'./static/loading.png' }) ``` 3. vue文件中将需要懒加载的图片绑定 v-bind:src 修改为 v-lazy ``` ``` #### vue-router的使用 ``` // 在进入下一个路由的时候,就获取到下一个页面的title显示出来 // 需要注册一个全局守卫 router.beforeEach((to, from, next)=>{ if(to.path=='/'){ //如果即将进入登录页面就是直接放心进入 next() }else{ if(to.meta.requiresAuth && !localStorage.getItem('token')){ // 验证token是否存在 // to.meta.requiresAuth 是否为真 next({ path: '/', }) }else{ if(to.meta.title){ document.title = to.meta.title } next(); } } }) ``` router.js ``` // eslint-disable-next-line /* eslint-disable */ import Vue from 'vue' import Router from 'vue-router' // import Login from "./views/Login.vue" Vue.use(Router) export default new Router({ routes:[ { name: 'login', path: '/', component: ()=>import("./views/Login.vue"), meta: { title: '登录' }, }, { name: 'register', path: '/register', component: ()=>import("./views/Register.vue"), meta: { title: "注册" } }, { name: 'addinfo', path: '/addinfo', component: ()=>import("./views/AddInfo.vue"), meta: { title: '记账', requiresAuth: true, } }, { name: 'editinfo', path: '/editinfo/:id', component: ()=>import("./views/EditInfo.vue"), meta: { title: '编辑', requiresAuth: true, } }, { name: 'showinfo', path: '/showinfo', component: ()=>import("./views/ShowInfo.vue"), meta: { title: '显示信息', requiresAuth: true, } }, { name: 'editriqi', path: '/editriji', component: ()=>import("./views/EditRiQi.vue"), meta: { title: '写日记', requiresAuth: true } }, { name: 'info', path: '/info/:id', component: ()=>import("./views/Info.vue"), meta: { title: '详细信息', requiresAuth: true } } ] }) ``` #### 此间遇到一个问题就是前台传递数据给后台,但是后台获取不到数据,数据格式是Request Payload ``` 最近在调试代码时发现有Request Payload的情况,从网上查一些文件, 也都有较多的描述。下面我只是说明一下大家没有注意的地方 关于HTTP请求,都是通过URL及参数向后台发送数据。 主要方式有GET, POST。对这两种方式,GET的参数都会放在URL的后面, 一般称之为query参数。 POST的都放在HTTP的报文BODY里,可以query参数的形式, 也可以multipart格式,还有一种JSON格式,即Request Payload格式。 multipart, Request Payload是通过request Header中的ContentType区分的: multipart格式:ContentType: multipart/form-data;boundary=--xxxxxxx, 注意对multipart的格式都要有boundary做为BODY中的参数分隔符, (关于该格式的讲解以后再写) Request Payload格式:ContentType: application/json 在后台的处理中对这三种格式的处理是不相同的。 GET格式都在URL后面,以key1=value1&key2=value2的KV格式存在, 且不会很长(协议规定为1024个字节,但现在浏览器都会适当加长一些)。 后台处理这种参数时可以使用同步处理,因为报文头收到后参数也就收全了。 POST时参数也可以使用上面的KV格式存在,但是会放在报文体中。 当数据量不大时,一般也会和报文头一起收到。 但数据量大的时会被拆分到多个报文中。因此必须使用异步方式收取。收全后处理同GET相同。 对于multipart格式,需要使用流方式边收边解析,因为有可能是大文件上传。 对于RequestPayload格式,可能也是异步发送(这个没有验证过), 但数据量一般不会太大,因此它是一个JSON格式,因此必须等报文收全后才能处理。 目前对JSON格式的支持比较普遍,都有相关的函数来解析JSON字符串, 直接生成JSON对象,因此这种方式也是最方便的。 特别是使用nodejs server时就可以直接在代码中使用了。 ``` #### 解决方法就是: ``` import qs from "qs" axios.interceptors.request.use((req) => { if (req.method === 'post') { // 转换成JSON格式 req.data = qs.stringify(req.data); } return req; }, (error) => Promise.reject(error)); ``` #### http协议的Request Payload 和 Form Data 的区别 https://www.cnblogs.com/xuzhudong/p/8487119.html https://blog.csdn.net/zwq912318834/article/details/79930423 #### Axios传参的两种方式,表单数据和json字符串(Form Data和Request Payload) ``` 第一种方式:Form Data return request({ headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, transformRequest: [function(data) { //在请求之前对data传参进行格式转换 data = Qs.stringify(data) return data }], url: '/test/add', //接口地址 method: 'post', //请求类型 params: {}, data: { 'name': params.name, //传的参数 'jobId': params.jobId, 'department': params.department, 'phone': params.phone, 'position ': params.position, 'permis': params.permis, 'entryTime': params.entryTime } }) ``` ``` 第二种方式:Json字符串 return request({ headers: { 'Content-Type': 'application/json' }, transformRequest: [function(data) { data = JSON.stringify(data) return data }], url: '/test/add', method: 'post', params: {}, data: { 'name': params.name, //传的参数 'jobId': params.jobId, 'department': params.department, 'phone': params.phone, 'position ': params.position, 'permis': params.permis, 'entryTime': params.entryTime } }) ``` ## 前台讲完将后台 #### php不能获取前台传过来post的数据 ``` 原因是 Content-Type类型设置为payload了 ``` #### 浅谈php接收POST数据的三种方式 ``` 在Web开发中,当用户使用浏览器向服务器POST提交数据时, 我们使用php接受用户POST到服务器的数据,并对数据进行解析和相应的处理. 以下是php接受POST数据的几种方式: 一.$_POST 方式接受数据   $_POST 方式是由通过HTTP的POST方法传递过来的数据组成的数组,   是一个自动全局变量. 注:只能接收Content-Type:application/x-www-form-urlencode提交的数据. 也就是只能接收表单POST过来的数据. 二.GLOBLES[‘HTTP_RAW_POST_DATA’]   如果访问原始POST数据不是php能够识别的文档类型,   比如:text/xml 或者soap等等,我们可以用$GLOBLES[‘HTTP_RAW_POST_DATA’]来接收,   $HTTP_RAW_POST_DATA变量包含有原始POST数据.此变量仅在碰到未识别的MIME数据时产生. 注: $HTTP_RAW_POST_DATA对于enctype=”multipart/form-data”表单数据不可用, 也就是说使用$HTTP_RAW_POST_DATA无法接受网页表单post过来的数据. 三. file_get_content(“php://input”);   如果访问原始POST数据, 更好的方法是使用file_get_content(“php://input”);   对于未指定Content-Type的POST数据,可以使用该方法读取POST原始数据,   包括二进制流也可以.和$HTTP_RAW_POST_DATA比起来.   它带来的生存眼里更小,并且不需要任何特殊的php.ini设置. 注: php://input不能用于 enctype=”multipart/form-data”. 例如: $postStr = file_get_contents("php://input"); //获取POST数据 四.名词解释 1.MIME数据类型:多用途互联网邮件扩展(MIME, Multipurpose Internet Mail Extension)是一个互联网标准,它扩展了电子邮箱标准, 使其能够支持ASCII字符, 二进制格式附件等多种格式的邮件消息.MIME规定了用于表示各种各样的数据类型的符号化方法.此外,在万维网中使用的HTTP协议中也使用MIME的框架. 2.原始数据:原始数据是指尚未处理的数据, 这些数据需要经过萃取,组织甚至分析与格式化后才能呈现给他人看. ``` #### php的header函数之设置content-type ``` //定义编码 header( 'Content-Type:text/html;charset=utf-8 '); //Atom header('Content-type: application/atom+xml'); //CSS header('Content-type: text/css'); //Javascript header('Content-type: text/javascript'); //JPEG Image header('Content-type: image/jpeg'); //JSON header('Content-type: application/json'); //PDF header('Content-type: application/pdf'); //RSS header('Content-Type: application/rss+xml; charset=ISO-8859-1'); //Text (Plain) header('Content-type: text/plain'); //XML header('Content-type: text/xml'); // ok header('HTTP/1.1 200 OK'); //设置一个404头: header('HTTP/1.1 404 Not Found'); //设置地址被永久的重定向 header('HTTP/1.1 301 Moved Permanently'); //转到一个新地址 header('Location: http://www.example.org/'); //文件延迟转向: header('Refresh: 10; url=http://www.example.org/'); print 'You will be redirected in 10 seconds'; //当然,也可以使用html语法实现 //