[记录三] Vue(全家桶)+node+koa2+mysql+nginx+redis,全栈博客项目前端部分完善

导语:

暑假在家闲着无事,就琢磨着做一个web博客练练手,现在已经做完了,把过程分享出来给大家看看,分享一下学习经验。这是第三篇,主要讲node,webpack和vue-cli环境的搭建,使用vue全家桶,写好路由,构建静态页面,完善前端的一些功能。

微信搜索 【web小馆】,回复 “全栈博客项目”,即可获取 项目源码和后续的实战文章教程

一,编写子组件

1,消息提示框子组件toast

在这里插入图片描述

这个主要是z-index还有居中等要素。

<template>
  <div class="toast" v-show="toastShow">
    {{toastStr}}
  </div>
</template>

<script>
export default {
  props: {
    toastShow: {
      type: Boolean,
      default: false
    },
    toastStr: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
    }
  }
}
</script>

<style>
.toast {
  position: fixed;
  z-index: 2100;
  left: 50%;
  top: 45%;
  transition: all .5s;
  -webkit-transform: translateX(-50%) translateY(-50%);
  -moz-transform: translateX(-50%) translateY(-50%);
  -ms-transform: translateX(-50%) translateY(-50%);
  -o-transform: translateX(-50%) translateY(-50%);
  transform: translateX(-50%) translateY(-50%);
  text-align: center;
  border-radius: 5px;
  color: #FFF;
  background: rgba(17, 17, 17, 0.7);
  height: 45px;
  line-height: 45px;
  padding: 0 15px;
  max-width: 150px;
}
</style>

2,登陆注册弹出框子组件login

在这里插入图片描述
在这里插入图片描述
弹出层内点击转换登陆和注册。

<template>
  <div class="login">
    <div class="modal-body">
      <!-- Nav tabs -->
    <ul class="nav nav-tabs">
        <li v-for="(item,index) in tabsParam" @click="toggleTabs(index)" :class="{active:index == nowIndex}" :key="index">{{item}}</li>
    </ul>
    <!-- Tab panes -->
    <div class="tab-content">
        <div class="tab-pane fade in" id="login" v-show="nowIndex === 0">
            <div class="signup-form-container text-center">
                <form class="mb-0" @submit.prevent="_login($event)">
                  <div class="form-group">
                      <input type="text" class="form-control" name="username" placeholder="*用户名或邮箱">
                  </div>
                  <div class="form-group">
                      <input type="password" class="form-control" name="password" placeholder="*密码">
                  </div>
                  <button type="submit" class="go-login btn btn--primary btn--block"><i class="fa fa-bullseye"></i> 安全登录</button>
                  <!-- <a href="#" class="forget-password">忘记密码?</a> -->
                </form>
                <!-- form  end -->
            </div>
            <!-- .signup-form end -->
        </div>
        <div class="tab-pane fade in " id="signup" v-show="nowIndex === 1">
            <form class="mb-0" @submit.prevent="_signup($event)">
              <div class="form-group">
                  <input type="text" class="form-control" name="username" placeholder="输入账号">
              </div>
              <div class="form-group">
                  <input type="text" class="form-control" name="realname" placeholder="输入用户名">
              </div>
              <!-- .form-group end -->
              <div class="form-group">
                  <input type="email" class="form-control" name="email" placeholder="绑定邮箱">
              </div>
                    <!-- .form-group end -->
              <div class="form-group">
                  <input type="password" class="form-control" name="password" placeholder="密码最小长度为6">
              </div>
              <div class="form-group">
                  <input type="password" class="form-control" name="password1" placeholder="再次输入密码">
              </div>
              <button type="submit" class="go-register btn btn--primary btn--block"><i class="fa fa-bullseye"></i> 立即注册</button>
            </form>
            <!-- form  end -->
        </div>
    </div>
  </div>
  </div>

</template>

<script>
import { login, signup } from "@/api/user"
import { ContactList } from 'vant';
export default {
  data(){
    return {
      tabsParam : ['登陆', '注册'],
      nowIndex:0,//默认第一个tab为激活状态
    }
  },
  methods: {
    toggleTabs:function(index){
      this.nowIndex=index;
    },
    _login() {
      var formData = new FormData(event.target);
      login(formData).then(res => {
        if (res.data.errno == 0) {
          console.log(res)
          let userinfo = res.data.data
          this.$store.commit('updateUserStatus',userinfo);
        }
        this.$emit('closelogin',res.data.errno)
      })
    },
    _signup(event) {
      let message = event.target
      if (message.password.value != message.password1.value) {
        // console.log('buyiy')
        this.$emit('passwordNoSame')
        return
      }
      var formData = new FormData(message)
      formData.email = message.email.value
      formData.username = message.username.value
      formData.password = message.password.value
      formData.realname = message.realname.value
      // console.log(formData)
      signup(formData).then(res => {
        if (res.data.errno == 0) {
          console.log(res)
        }
        this.$emit('closesign', res.data.errno)
      })
    }

  },
  mounted() {
    console.log(this.$store.getters.getCurrentUser)
    console.log(this.$store.getters.getIsLogin)
  },
}
</script>

<style scoped>
.login{
  border: none;
  box-shadow: none;
  position: relative;
  background-color: transparent;
}
.model-body{
  position: relative;
  padding: 15px;
}
.nav-tabs{
  border-bottom: none;
    text-align: center;
    padding-left: 0;
    padding: 10px;
    list-style: none;
    margin: 0;
}
.nav-tabs li{
  float: none;
  display: inline-block;
  margin: 0 5px;
}
.tab-content{
  padding: 20px;
    background-color: #ffffff;
    border-radius: 4px;
}
.form-group{
  margin-bottom: 15px;
  position: relative;
}
.form-control{
  border-radius: 0;
  font-family: Lato,sans-serif;
  line-height: 30px;
  margin-bottom: 10px;
  padding: 0 12px;
  transition: border-color cubic-bezier(0.77,0,0.175,1);
  border: 1px solid #dad9d9;
  font-size: 14px;
  border-radius: 10px;
}
.active{
  color: #69d78a;
}
.tabnav{
  color: red;
}
.mb-0{
  text-align: center;
}
.btn{
  background-color: #69d78a;
  border: 1px solid #69d78a;
  color: white;
  padding: 5px 10px;
  border-radius: 10px;
  font-size: 14px;
}
</style>

3,右拉信息框right

在这里插入图片描述

<template>
  <div class="right">
    <div class="img-div">
      <img src="../../assets/logo.jpg" class="logo">
      <p class="rightp">轻松学算法</p>
      <p class="rightp">小程序:轻松学算法</p>
    </div>
    <div class="button_div">
      <van-button type="primary" size="mini" to="/home">首页</van-button>
    </div>
  </div>
</template>

<script>
import Vue from 'vue';
import { Button } from 'vant';

Vue.use(Button);
export default {

}
</script>

<style scoped>
.img-div{
  margin-top: 30px;
  text-align: center;
}
.logo{
  height: 160px;
  width: 160px;
}
.rightp{
  font-size: 14px;
  margin-top: 0;
}
.button_div{
  padding-left: 10px
}
</style>

4,个人信息组件information

在这里插入图片描述

<template>
  <div class="information">
    <toast :toastShow = "isLoading" :toastStr = "str"></toast>
    <van-popup v-model="show" style="border-radius: 10px;">
      <div class="inputdiv">
        <input class="input" v-model="newName">
        <button class="but" @click= "chance">修改昵称</button>
      </div>
    </van-popup>
    <topnav @back= "back" :text= "text"></topnav>
    <div class="userimgdiv">
      <div class="text">头像</div>
      <div class="imgdiv">
        <img :src="avatar" class="img">
        <van-icon name="arrow" class="myicon" size="20"/>
      </div>
      <input id="fileImage" type="file" @change="avatarChange($event)">
    </div>
    <div class="item" @click="showPopup">
      <div class="textitem">昵称</div>
      <div class="textdiv">
        <div class="textitem" v-html="realname"></div>
        <van-icon name="arrow" class="myiconitem" size="20"/>
      </div>
    </div>
    <div class="item">
      <div class="textitem">账号</div>
      <div class="textdiv">
        <div class="textitem" v-html="username"></div>
      </div>
    </div>
  </div>
</template>

<script>

import toast from '@/base/toast/toast'
import topnav from '@/components/top-nav/top-nav'
import Vue from 'vue';
import { Icon } from 'vant';
import { upavatar, upname } from '@/api/user'

Vue.use(Icon);
export default {
  data() {
    return {
      text: '个人信息',
      show: false,     //弹出层的显示
      isLoading: false,     //消息提示框的显示
      str: '',             //消息提示框的内容
      newName: ''
    }
  },
  methods: {
    back() {
      this.$router.back()
    },
    showPopup() {
      this.show = true;
    },
    chance() {        // 修改接口
      this.show = false;
      // console.log(this.newName)
      let param = new FormData()
      param.append('name', this.newName)
      upname(param).then(res => {
        if (res.data.errno == 0) {
          let userinfo = this.$store.getters.getCurrentUser
          userinfo.realname = this.newName
          this.toastshow('修改成功')
        }
      })
    },
    toastshow(str) {
      this.showloginstate = false;
      this.isLoading = true             //提示框的显示
      this.str = str
      setTimeout(() => {
        this.isLoading = false              //提示框的消失
        this.str = ''
      }, 1500);
    },
    avatarChange(el) {
      if (!this.$store.getters.getIsLogin) {
        return
      }
      let file = el.target.files[0]
      console.log(file)
      let param = new FormData() // 创建form对象
      param.append('file', file) // 通过append向form对象添加数据
      param.append('chunk', '0') // 添加form表单中其他数据
      console.log(param.get('file')) // FormData私有类对象,访问不到,可以通过get判断值是否传进去

      upavatar(param).then(res => {
        if (res.data.errno == 0) {
          let userinfo = this.$store.getters.getCurrentUser
          userinfo.avatar = res.data.data.avatar_url
          this.$store.commit('updateUserStatus',userinfo);
          this.toastshow('修改成功')
        } else {
          this.toastshow('修改失败')
        }

      })
    }
  },
  computed: {
    avatar() {
      if (this.$store.getters.getIsLogin){
        return this.$store.getters.getCurrentUser.avatar
      } else {
        return 'http://localhost:8000/api/file/avatar?pic=logo.png'
      }
    },
    realname() {
      if (this.$store.getters.getIsLogin){
        return this.$store.getters.getCurrentUser.realname
      } else {
        return '轻松学算法'
      }
    },
    username() {
      if (this.$store.getters.getIsLogin){
        return this.$store.getters.getCurrentUser.username
      } else {
        return 'xiaomizhou'
      }
    },
  },
  components: {
    topnav,
    toast
  }
}
</script>

<style scoped>
.information{
  position: fixed;
  top: 50px;
  bottom: 0;
  right: 0;
  left: 0;
  z-index: 1000;
  background-color: white;
}
.userimgdiv{
  height: 60px;
  display: flex;
  padding: 5px 0;
  border-bottom: 1.5px solid #eeeeee;
  margin: 0px 10px;
}
.text{
  font-size: 14px;
  margin:15px 5px 5px 5px;
}
.imgdiv{
  margin-left: auto;
  height: 60px;
  width: 68px;
  padding: 8px 5px 8px 8px;
  display: flex;
}
.img{
  height: 44px;
  width: 44px;
  border-radius: 10px;
}
.myicon{
  margin-top: 10px;
  margin-left: 5px;
}
.item{
  height: 40px;
  display: flex;
  padding: 5px 0;
  border-bottom: 1.5px solid #eeeeee;
  margin: 0px 10px;
}
.textitem{
  font-size: 14px;
  margin:8px 5px 5px 5px;
}
.textdiv{
  margin-left: auto;
  padding: 4px;
  display: flex;
}
.myiconitem{
  margin-top: 7px;
  margin-left: 5px;
}
.inputdiv{
  height: 160px;
  width: 280px;
  border-radius: 10px;
}
.input{
  margin: 40px 0 20px 0;
  display:block;
  margin-left:auto;
  margin-right:auto;
  border: 1px solid #a5a4a4;
  border-radius: 8px;
  height: 26px;
  padding: 2px 8px 2px 8px;
  font-size: 14px;
}
.but{
  margin: 30px 0 20px 0;
  display:block;
  margin-left:auto;
  margin-right:auto;
  border: 1px solid #a5a4a4;
  border-radius: 8px;
  height: 30px;
  padding: 3px 8px 3px 8px;
  font-size: 14px;
}
#fileImage
{
  height: 70px;
  width: 100%;
  overflow: hidden;
  cursor: pointer;
  opacity: 0;
  position: absolute;
}
</style>

在这里插入图片描述
你们的赞就是对我最大的鼓励,谢谢啦~~~

补充:

微信搜索【web小馆】,回复全栈博客项目,即可获取项目源码和后续的实战文章教程。每天用最简单朴实的语言,潜移默化的提升你的计算机基础知识和前端技术。小米粥,一个专注的web全栈工程师,我们下期再见!

在这里插入图片描述
node后台

猜你喜欢

转载自blog.csdn.net/gitchatxiaomi/article/details/108149927
今日推荐