黑马vue商城-day01-项目初始化登录功能开发

本文详细记录了一次使用Vue.js搭建项目的过程,包括初始化码云项目,安装VueCLI,设置环境变量,创建数据库,安装并配置Element UI,实现登录组件的布局、表单验证、数据绑定及登录功能,包括登录前的预校验、登录后的行为,如保存token和跳转。同时,文章还涉及到了路由守卫的设置和退出功能的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码仓库

https://gitee.com/zzaxg/itheima_shop_zz
image-20210224221106217

初始化

老规矩,先来初始化一个码云项目

先来安装 Vue cli 脚手架

npm install -g @vue/cli-service-global

image-20210224222359194

好家伙,见面礼,百度一哈

image-20210224222422745

加个环境变量,还是不行

image-20210224223748305

切换到管理员,ok

vue ui 后面都是可视化的东西了

咱们先把 eslint 相关的所有东西干掉

image-20210224224809045

按需导入哈

插件里面搜索 elementui axios 就行

后台的启动

初始化一个 叫做 mydb 的 utf-8 的数据库

把db/mydb.sql 运行一下就行了

然后 npm i 安装依赖

image-20210224225937457

改成自己的数据库密码

node app.js

express写的后台,很显然

image-20210224230141803

测试一下登录接口

image-20210224230301264

删除不要的组件

<template>
  <div id="app">
    app根组件
  </div>
</template>

<script>


export default {
  name: 'app',
  components: {

  }
}
</script>

<style>
</style>
image-20210224231327835

把 router 里面的内容清除掉

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [
  
]

const router = new VueRouter({
  routes
})

export default router

把除了app.vue根组件之外的其他组件删掉

创建登录组件

在components 里面创建一个 空组件 Login.vue

<template>
  
</template>

<script>
export default {

}
</script>

<style lang="less" scoped>

添加路由规则

import Vue from "vue";
import VueRouter from "vue-router";
import Login from "../components/Login.vue";

Vue.use(VueRouter);

const routes = [
  {
    path: "/login",
    component: Login,
  },
];

const router = new VueRouter({
  routes,
});

export default router;

根组件中添加路由占位符

<template>
  <div id="app">
    app根组件
    <!-- 路由占位符 -->
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "app",
  components: {},
};
</script>

<style></style>
image-20210224232435477

测试

添加 / 重定向到 login

登录组件布局

image-20210225100510534

安装 less-loader 和 less 开发时依赖

重启项目

https://blog.youkuaiyun.com/qq_42430948/article/details/113552673

这里存在 less-loader 版本过高的问题

TypeError: this.getOptions is not a function

npm uninstall less-loader
npm install less-loader@5.0.0

image-20210225101758212

<template>
  <div class="login_container">Login</div>
</template>

<script>
export default {

}
</script>

<style lang="less" scoped>
.login_container {
    background-color: #2b4b6b;
}
</style>
/* 全局样式表  */
html,
body,
#app {
    height: 100%;
    margin: 0;
    padding: 0;
}

导入全局样式表,清除边距

// file: main.js
// 导入全局样式表
import './assets/css/global.css'

image-20210225102409173

生效了

<template>
  <div class="login_container">
      <div class="login_box">

      </div>
  </div>
</template>

<script>
export default {

}
</script>

<style lang="less" scoped>
.login_container {
    background-color: #2b4b6b;
    height: 100%;
}
.login_box{
    width: 450px;
    height: 250px;
    background-color: #fff;
    border-radius: 3px;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}
</style>

image-20210225102907784

登录组件头部布局

<template>
  <div class="login_container">
    <div class="login_box">
      <div class="avatar_box">
        <img src="../assets/zz.jpg" alt="" />
      </div>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style lang="less" scoped>
.login_container {
  background-color: #2b4b6b;
  height: 100%;
}
.login_box {
  width: 450px;
  height: 250px;
  background-color: #fff;
  border-radius: 3px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  .avatar_box {
    height: 130px;
    width: 130px;
    border: 1px solid #eee;
    border-radius: 50%;
    padding: 10px;
    box-shadow: 0 0 10px #ddd;
    position: absolute;
    left: 50%;
    transform: translate(-50%, -50%);
    background-color: #fff;
    img {
      height: 100%;
      width: 100%;
      border-radius: 50%;
      background-color: #eee;
    }
  }
}
</style>

image-20210225104329671

登陆组件表单布局

按需导入需要的组件

// file: plugin/elementui.js
import Vue from "vue";
import { Button, Form, FormItem, Input } from "element-ui";

Vue.use(Button);
Vue.use(Form);
Vue.use(FormItem);
Vue.use(Input);
<template>
  <div class="login_container">
    <div class="login_box">
      <!-- 头像区域 -->
      <div class="avatar_box">
        <img src="../assets/zz.jpg" alt="" />
      </div>
      <!-- 登录表单区域 -->
      <el-form label-width="0px" class="login_form">
        <!-- 用户名 -->
        <el-form-item> <el-input></el-input></el-form-item>
        <!-- 密码 -->
        <el-form-item> <el-input></el-input></el-form-item>
        <!-- 按钮区域 -->
        <el-form-item class="btns">
            <el-button type="primary">登录</el-button>
            <el-button type="info">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style lang="less" scoped>
.login_container {
  background-color: #2b4b6b;
  height: 100%;
}
.login_box {
  width: 450px;
  height: 300px;
  background-color: #fff;
  border-radius: 3px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  .avatar_box {
    height: 130px;
    width: 130px;
    border: 1px solid #eee;
    border-radius: 50%;
    padding: 10px;
    box-shadow: 0 0 10px #ddd;
    position: absolute;
    left: 50%;
    transform: translate(-50%, -50%);
    background-color: #fff;
    img {
      height: 100%;
      width: 100%;
      border-radius: 50%;
      background-color: #eee;
    }
  }
}

.login_form {
    position: absolute;
    bottom: 0px;
    width: 100%;
    padding: 0 20px;
    box-sizing: border-box;
}

.btns {
    display: flex;
    // 尾部对其
    justify-content: flex-end;
}
</style>
image-20210225110417520

登录组件表单小图标布局

image-20210225111213742

把 font 目录放到我们的 assets 目录下面

// main.js
// 导入 字体图标
import './assets/fonts/iconfont.css'
<!-- 按钮区域 -->
<el-form-item class="btns">
    <el-button type="primary">登录</el-button>
    <el-button type="info">重置</el-button>
</el-form-item>
image-20210225111716972

登录组件表单的数据绑定

  • el-form :model
  • el-input v-model
  • type=“password” 密码
<template>
  <div class="login_container">
    <div class="login_box">
      <!-- 头像区域 -->
      <div class="avatar_box">
        <img src="../assets/zz.jpg" alt="" />
      </div>
      <!-- 登录表单区域 -->
      <el-form label-width="0px" class="login_form" :model="loginForm">
        <!-- 用户名 -->
        <el-form-item>
          <el-input prefix-icon="iconfont icon-user" v-model="loginForm.username"></el-input
        ></el-form-item>
        <!-- 密码 -->
        <el-form-item>
          <el-input prefix-icon="iconfont icon-3702mima" v-model="loginForm.password" type="password"></el-input
        ></el-form-item>
        <!-- 按钮区域 -->
        <el-form-item class="btns">
          <el-button type="primary">登录</el-button>
          <el-button type="info">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {
    data() {
        return {
            // 这是登录表单的数据绑定对象
            loginForm: {
                username: '',
                password: ''
            }
        }
    },
};
</script>

image-20210225112353828

登录表单的数据验证

  • el-form 指定 :rules
  • data中定义验证对象
  • el-form-item中使用 prop 指定验证对象
<template>
  <div class="login_container">
    <div class="login_box">
      <!-- 头像区域 -->
      <div class="avatar_box">
        <img src="../assets/zz.jpg" alt="" />
      </div>
      <!-- 登录表单区域 -->
      <el-form
        label-width="0px"
        class="login_form"
        :model="loginForm"
        :rules="loginFormRules"
      >
        <!-- 用户名 -->
        <el-form-item prop="username">
          <el-input
            prefix-icon="iconfont icon-user"
            v-model="loginForm.username"
          ></el-input
        ></el-form-item>
        <!-- 密码 -->
        <el-form-item prop="password">
          <el-input
            prefix-icon="iconfont icon-3702mima"
            v-model="loginForm.password"
            type="password"
          ></el-input
        ></el-form-item>
        <!-- 按钮区域 -->
        <el-form-item class="btns">
          <el-button type="primary">登录</el-button>
          <el-button type="info">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 这是登录表单的数据绑定对象
      loginForm: {
        username: "",
        password: "",
      },
      //   这是表单的验证规则对象
      loginFormRules: {
        //   验证用户名是否合法
        username: [
          { required: true, message: "请输入登录名称", trigger: "blur" },
          {
            min: 3,
            max: 10,
            message: "长度在 3 到 10 个字符",
            trigger: "blur",
          },
        ],
        //   验证密码是否合法
        password: [
          { required: true, message: "请输入登录密码", trigger: "blur" },
          {
            min: 6,
            max: 15,
            message: "长度在 6 到 15 个字符",
            trigger: "blur",
          },
        ],
      },
    };
  },
};
</script>
image-20210225113211469

登录表单实现重置

image-20210225113423541

需要拿到表单对象,使用 ref

image-20210225113726937

打印出 this

image-20210225113752639
<template>
  <div class="login_container">
    <div class="login_box">
      <!-- 头像区域 -->
      <div class="avatar_box">
        <img src="../assets/zz.jpg" alt="" />
      </div>
      <!-- 登录表单区域 -->
      <el-form
        ref="loginFormRef"
        label-width="0px"
        class="login_form"
        :model="loginForm"
        :rules="loginFormRules"
      >
        <!-- 用户名 -->
        <el-form-item prop="username">
          <el-input
            prefix-icon="iconfont icon-user"
            v-model="loginForm.username"
          ></el-input
        ></el-form-item>
        <!-- 密码 -->
        <el-form-item prop="password">
          <el-input
            prefix-icon="iconfont icon-3702mima"
            v-model="loginForm.password"
            type="password"
          ></el-input
        ></el-form-item>
        <!-- 按钮区域 -->
        <el-form-item class="btns">
          <el-button type="primary">登录</el-button>
          <el-button type="info" @click="resetLoginForm">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 这是登录表单的数据绑定对象
      loginForm: {
        username: "",
        password: "",
      },
      //   这是表单的验证规则对象
      loginFormRules: {
        //   验证用户名是否合法
        username: [
          { required: true, message: "请输入登录名称", trigger: "blur" },
          {
            min: 3,
            max: 10,
            message: "长度在 3 到 10 个字符",
            trigger: "blur",
          },
        ],
        //   验证密码是否合法
        password: [
          { required: true, message: "请输入登录密码", trigger: "blur" },
          {
            min: 6,
            max: 15,
            message: "长度在 6 到 15 个字符",
            trigger: "blur",
          },
        ],
      },
    };
  },
  methods: {
    //   点击重置按钮,重置登录表单
    resetLoginForm() {
      // console.log(this);
      this.$refs.loginFormRef.resetFields();
    },
  },
};
</script>
image-20210225113931172 image-20210225113944560

点击之后

  • 给 form 添加 ref
  • 用this.$refs.xxx 去引用对象

登录组件登录前的预校验

image-20210225114239095

login() {
  this.$refs.loginFormRef.validate((valid) => {
    console.log(valid);
  });
},

登录组件根据预验证是否发起请求

// 引入 axios
import axios from 'axios'
// 配置请求的根路径
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
Vue.prototype.$http = axios

配置 axios

login() {
  this.$refs.loginFormRef.validate(async (valid) => {
    // console.log(valid);
    if (!valid) return
    const {data: res} = await this.$http.post('login', this.loginForm)
    // console.log(res);
    if (res.meta.status !== 200) return console.log('登录失败');
    console.log('登录成功');
  });
},
image-20210225134504428

登录组件配置弹框提示

导入弹框

Vue.prototype.$message = Message

得挂载到全局

login() {
  this.$refs.loginFormRef.validate(async (valid) => {
    // console.log(valid);
    if (!valid) return;
    const { data: res } = await this.$http.post("login", this.loginForm);
    // console.log(res);
    if (res.meta.status !== 200) return this.$message.error("登录失败!");
    this.$message.success("登录成功");
  });
},
image-20210225134904281

登录组件登录成功后的行为

login() {
  this.$refs.loginFormRef.validate(async (valid) => {
    // console.log(valid);
    if (!valid) return;
    const { data: res } = await this.$http.post("login", this.loginForm);
    // console.log(res);
    if (res.meta.status !== 200) return this.$message.error("登录失败!");
    this.$message.success("登录成功");
    /**
     * 1. 将登录成功之后的 token 保存到客户端的 sessionStorage 中
     *  1.1 项目中出了登录之外的其他API接口,必须在登录之后才能访问
     *  1.2 token 只应在当前网站打开之后生效,所以将  token 保存在 sessionStorage 中
     * 2. 通过编程时导航跳转到后台主页,路由地址是 /home
     */
    // console.log(res);
    window.sessionStorage.setItem("token", res.data.token);
    this.$router.push("/home");
  });
},
image-20210225135850377

新增一个 Home 空组件和路由

image-20210225140017030

路由导航守卫控制访问权限

如果用户没有登录,但是直接通过 URL 访问特定页面,需要重新导航到登录页面

// 挂载路由导航守卫
router.beforeEach((to, from, next) => {
  //  to 将要访问的路径
  // from 代表从哪个路径跳转而来
  // next 是一个函数, 表示放行
  // next() 放行 next('/login') 强制跳转

  if (to.path == "/login") return next();
  // 获取 token
  const tokenStr = window.sessionStorage.getItem("token");
  if (!tokenStr) return next("/login");
  next();
});

测试,直接访问 /home 会跳转到登陆页面

退出功能实现原理

销毁 token

<template>
  <div>
    <el-button type="info" @click="logout">退出</el-button>
  </div>
</template>

<script>
export default {
  data() {
    return {};
  },
  methods: {
    logout() {
      window.sessionStorage.clear();
      this.$router.push("/login");
    },
  },
};
</script>

<style></style>

点击退出按钮退出

处理语法警告

下次

优化 element-ui按需组件导入形式

import { Button, Form, FormItem, Input, Message } from "element-ui";

这个其实我就是这样做的

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值