效果展现:
https://blog.csdn.net/qq_42239520/article/details/88534955html
所用技术清单
项目地址:项目地址
vue代码地址:vue代码地址前端
项目部分过程笔记:vue
项目结构ios
Secondhanbook / 项目目录 apps v1 __init__.py 导入 urls urls.py 路由配置 forms.py models.py views user_views.py 用户相关视图 book_vews.py 书籍相关视图 config.py 配置文件 run.py 项目启动文件 manage.py 数据库迁移文件 Secondhanbook.py 初始化文件 utils 工具类目录 ...... ......
1. Flask_RESTful 的返回 和 缓存时候的自定义返回git
flask_restful 在视图方法上加上marshal_with 装饰器,并传递你返回的模式。但当自定义时,须要返回本身的模式github
return marshal(books,self.resource_fields,envelope='data') resource_fields:自定义返回模式 data: 返回时,数据包装
2. 使用Flask_RESTful 下表单验证以及csrf防护redis
每当用户进入提交Post请求的页面,实例化一个表单form,返回csrf_tokenvuex
csrf_token = form.csrf_token.current_token return restful.success(data=csrf_token)
3.路由配置数据库
api = Api(prefix='/api') #用户相关 api.add_resource(Login,'/login/',endpoint='login') ...... #书籍相关 api.add_resource(BookAddView,'/bookadd/',endpoint='bookadd') ......
4.使用itsdangerous生成临时身份令牌
生成令牌json
def generate_auth_token(self,expiration=172800):#两天过时 s = Serializer(config.BaseConfig.SECRET_KEY,expires_in=expiration) return s.dumps({'id':self.id})
身份检验
def verify_auth_token(token): s = Serializer(config.BaseConfig.SECRET_KEY) try: data = s.loads(token) except Exception as e: return None user = FrontUserModel.query.get(data['id']) return user
5.配置文件的书写方式,类继承的方式
class BaseConfig(object): pass class DevelopmentConfig(BaseConfig): pass class OnlineConfig(BaseConfig): pass
6. celery 处理费时任务
@celery.task def send_mail(subject,recipients,user_id): ...... @celery.task def BookCacheAdd(books): ......
7.redis 存储数据bytes 问题解决
cache = redis.StrictRedis(host='127.0.0.1',port=6379,db=0,decode_responses=True)
8. 封装jsonfy的返回
class HttpCode(object): Ok = 200 ParamerError = 400 Unauth = 401 ServerError = 500 def RestfulResult(code,message,data): return jsonify({'code':code,'message':message,'data':data}) def success(message="",data=None): return RestfulResult(HttpCode.Ok,message=message,data=data) ......
9.对axios 提交bytes类型数据进行form验证
from werkzeug.datastructures import MultiDict ...... myform = json.loads((request.data.decode('utf-8'))) form = LoginForm(MultiDict(myform)) ......
10. flask 接收 接收多个文件axios 上传的多个文件
** AddBookView ** files = request.files for file in files.values(): filename = upload.change_filename(secure_filename(file.filename)) **upload.vue** for (var i = 0; i < this.files.length; i++) { this.formdata.append('file' + i, this.files[i]) } axios.post('api/bookadd/', this.formdata, { headers: {'Content-Type': 'multipart/form-data'} }).then(this.handleAxiosDone)
11.表单对files, 输入字段验证
from werkzeug.datastructures import CombinedMultiDict form = Yourform(CombinedMultiDict([request.form, request.files])) // 合并 数据和文件 文件验证: 定义表单时,采用FileField这个类型 验证器导入:flask_wtf.file flask_wtf.file.FileRequired 验证是否为空 flask_wtf.file.FileAllowed 验证上传文件后缀名
项目结构
...... src common footer 页脚 alert 提示 fade 动画 gallary 画廊 ...... store ... vuex相关 pages home components header.vue ...... Home.vue detail me sign ...... ......
1. flask_result 返回时,提供默认值
返回数据时,当axios 未返回渲染到页面时,使用变量出错。
**** 解决方法: 定义默认值 例若有使用book.owner.username , book.owner.avatar时,不然报错无定义,并没有法显示。
book: { owner: {......Object} },
2. vuex store一个Message,供于消息提醒
项目基本上每一个页面都有操做结果的提醒,封装一个消息提醒的组件 alert.vue
<div v-show="isshow" class="alertBox" :style="{background:this.$store.state.color}"> {{mymessage}} </div> ...... computed: { mymessage () { return this.$store.state.message } }, watch: { mymessage (e) { this.isshow = true } }
state: { message: '默认值', color: '' }, mutations: { msgchange (state, res) { state.message = res.message // color 自定义消息 state.color = res.color // color 自定义颜色 setTimeout(() => { state.message = '' state.color = '' }, 3000) } } **** 重点:必须重置,不然下一次同样的消息将不显示 组件写一个发送提醒消息的方法,方便屡次调用 handleemit (message, color) { this.$store.commit('msgchange', {message: message, color: color}) }
3. 登录注册:
利用 vue mounted 生命周期函数请求后端返回的csrf_token , 以及检验本地 localStorage,token是否过时。首次登录成功返回存储token
localStorage.setExpire('token', res.headers.token, 1000 * 60 * 60 * 24 * 2) // 设置两天过时 注册发送email 激活帐号
4 . 使用better-scroll 加载更多
swiper 盒子必须小于content高度才能滚动
能够滚动后,页面将不能点击,解决:**** this.scroll = new BScroll(this.$refs.wrapper, { click: true, ...... 监听下拉方法,加载更多 pullUpLoad: { // 当上拉距离超过盒子高度的的时候,就派发一个上拉加载的事件(触发条件) threshold: 0 } 监听事件 this.scroll.on('pullingUp', () => { axios.get('/api/booklist/?start=' + this.start).then(this.handleAxiosSuccess) }) *** 对于下拉加载更多,双重遍历,遍历页码对应的数据 v-for="(p,index) in page" v-for="item in booklist[index]" :key="item.id" // booklist[index] 为第几回下拉的返回的数据
5. vue-awesome-swiper
图片点击事件 监听事件: on: { click: function (e) { window.open(e.target.src) // 跳转到网页 } } 使用: <swiper :options="swiperOption"> swiperOption为参数{ loop: true,effect: 'fade'......} <!-- slides --> <swiper-slide v-for ='item of swiperList' :key="item.id"> <img class="swiper-img" :src="item.url" alt=""> // 传递图片url </swiper-slide> </swiper> 画廊 组件关键参数: // observer启动动态检查器(OB/观众/观看者),当改变swiper的样式(例如隐藏/显示)或者修改swiper的子元素时,自动初始化swiper。 // 默认false observer: true, observeParents: true
6. 过滤器,传递data中的值,而且使用v-html 显示
v-html="$options.filters.filemotion(comment.content,emotions)" // emotions: [] 是data中自定义的值 本过滤器是对,表情的插入表情标签的过滤 [赞] [哈哈] 替换成 图片 方法: emotions格式 : filemotion (value, emotions) { value = value.replace(/(\[.+?\])/g, (e, e1) => { for (var i in emotions) { if ((emotions[i].value.indexOf(e1)) > -1) { return '<img src="' + emotions[i].icon + '">' } } }) return value }
7. axios 更改请求头:
axios.post('/api/comment/',参数, { headers: { 'Content-Type': 'application/json' ...... } })
8. Proxytable设置跨域,进行数据交互
proxyTable: { '/api': { target: 'http://127.0.0.1:5000', //目标接口域名 changeOrigin: true, //是否跨域 pathRewrite: { '^/api': '/v1/api/' //重写接口 } } }
9.router 模式
路由 mode="history"模式
当前端项目结合到flask 项目中,当vue 路由为history模式时,出现“刷新页面报错404”的问题
这是由于这是单页应用…实际上是由于调用了history.pushState API 因此全部的跳转之类的操做 都是经过router来实现的,解决这个问题很简单,只须要在后台配置若是URL匹配不到任何静态资源
进入新页面,不回到页面顶部解决:
scrollBehavior (to, from, savedPosition) { return { x: 0, y: 0 } },