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

释放双眼,带上耳机,听听看~!

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

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

所用技术清单
Flask + vue 前后端分离的 二手书App
项目地址:项目地址
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 }
  },

给TA打赏
共{{data.count}}人
人已打赏
站长资讯

前后端分离之【接口文档管理及数据模拟工具docdoc与dochelper】

2020-11-9 3:45:22

站长资讯

语音识别的降噪思路和总结

2020-11-9 3:45:24

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索