zoukankan      html  css  js  c++  java
  • Flask + vue 前后端分离的 二手书App

    一个Flask + vue 前后端分离的 二手书App

    效果展示:
    https://blog.csdn.net/qq_42239520/article/details/88534955

    所用技术清单
    在这里插入图片描述
    项目地址:项目地址
    vue代码地址:vue代码地址

    项目部分过程笔记:

    后台:

    项目结构

    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 的返回 和 缓存时候的自定义返回

    flask_restful 在视图方法上加上marshal_with 装饰器,并传递你返回的模式。但当自定义时,需要返回自己的模式

    return marshal(books,self.resource_fields,envelope='data')
    	resource_fields:自定义返回模式
    	data:  返回时,数据包装
    

    2. 使用Flask_RESTful 下表单验证以及csrf防御

    每当用户进入提交Post请求的页面,实例化一个表单form,返回csrf_token

    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生成临时身份令牌
    生成令牌

        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 }
      },
    
  • 相关阅读:
    300. Longest Increasing Subsequence_算法有误
    LIS (DP)_代码
    pthread_detach pthread_create实例
    pthread_detach
    DP(动态规划)
    括号匹配(二)
    gdb调试遇到的问题
    matplotlib 显示中文
    一个奇怪的编码 big5-hkscs
    python 重载 __hash__ __eq__
  • 原文地址:https://www.cnblogs.com/donghaoblogs/p/10523983.html
Copyright © 2011-2022 走看看