Nuxtjs结合Element-ui框架应用项目详细记录---搜索框/登录请求/vuex/封装axios

387 阅读3分钟

一、搜索框与跳转

  • element组件: 1.Tabs 标签页 2.Input 输入框
  • 思路:
  1. 添加tab布局
  2. 点击tab栏切换时currentOption记录当前状态变量,判断currentOption是否等于索引index以决定是否给高亮样式.同时搜索框占位符placeholder为当前状态的数据.
  3. 点击放大镜或回车进行搜索时url传参跳转页面后再匹配数据.
  4. 如果是机票项直接跳转页面.
  • pages/index.vue组件

template部分

<!-- 搜索框 -->
    <div class="banner-content">
      <div class="search-bar">
        <!-- tab栏 -->
        <el-row type="flex" class="search-tab">
          <!-- span:每个分类 -->
          <!-- options:循环tab的数组数据 -->
          <!-- index:索引 -->
          <!-- action:样式,索引是否等于当前点击状态,等于布尔值决定是否赋值样式 -->
          <!-- handleOption:当前点击的tab,传入索引 -->
          <!-- currentOption:当前激活项 -->
          <span
            v-for="(item, index) in options"
            :key="index"
            :class="{ active: index === currentOption }"
            @click="handleOption(index)"
          >
            <i>{{ item.name }}</i>
          </span>
        </el-row>
        <!-- 输入框 -->
        <!-- placeholder:当前激活项的输入框占位符 -->
        <!-- searchValue:动态绑定输入框的值 -->
        <!-- handleSearch:点击放大镜或回车键调用事件 -->
        <el-row type="flex" align="middle" class="search-input">
          <input
            :placeholder="options[currentOption].placeholder"
            v-model="searchValue"
            @keyup.enter="handleSearch"
          />
          <i class="el-icon-search" @click="handleSearch"></i>
        </el-row>
      </div>
    </div>

data部分

 // 搜索框数据
      options: [
        { name: "攻略", placeholder: "搜索城市攻略", pageUrl: "/post?city=" },
        { name: "酒店", placeholder: "搜索城市酒店", pageUrl: "/hotel?city=" },
        { name: "机票", placeholder: "请输入出发地", pageUrl: "/air" },
      ],
      searchValue: "", //搜索框关键词
      currentOption: 0, //当前状态选项

methods部分

methods: {
    handleSearch() {
      console.log("搜索");
      // 点击回车或者放大镜时在url里传参,跳转响应页面,再讲参数进行搜索
      // searchValue:当前用户输入数据
      this.$router.push(
        this.options[this.currentOption].pageUrl + this.searchValue
      );
    },
    // 点击tab切换,index:当前点击的索引currentOption:当前激活项
    handleOption(index) {
      //飞机票因为业务比较复杂,判断如果是机票索引直接跳转
      if (index === 2) {
        this.$router.push(this.options[index].pageUrl + this.searchValue);
      }
      this.currentOption = index;
    },
  },

效果

在这里插入图片描述

二、登录注册页布局

思路:

  1. 点击时获取当前索引赋值给currentTab
  2. 切换状态保留当前状态赋值currentTab.
  3. 当索引index值等同currentTab时,高亮样式

pages/user/login.vue

<template>
  <div class="container">
    <!-- 主要内容 -->
    <el-row type="flex" justify="center" align="middle" class="main">
      <div class="form-wrapper">
        <!-- 表单头部tab -->
        <el-row type="flex" justify="center" class="tabs">
          <!-- currentTab:当前点击状态 -->
          <!-- index:索引 -->
          <!-- handleChangeTab:点击获取当前索引赋值给currentTab -->
          <!-- active:为true时赋值active样式 -->
          <span
            :class="{ active: currentTab === index }"
            v-for="(item, index) in [`登录`, `注册`]"
            :key="index"
            @click="handleChangeTab(index)"
          >
            {{ item }}
          </span>
        </el-row>

        <!-- 登录功能组件 -->
        <!-- <LoginForm v-if="currentTab == 0"/> -->

        <!-- 注册功能组件 -->
        <!-- <RegisterForm v-if="currentTab == 1"/> -->
      </div>
    </el-row>
  </div>
</template>

<script>
export default {
  data() {
    return {
      currentTab: 0, //当前状态
    };
  },
  methods: {
    handleChangeTab(index) {
      // 点击获取当前索引赋值给currentTab
      this.currentTab = index;
    },
  },
};
</script>

<style scoped lang="less">
.container {
  background: url(http://157.122.54.189:9095/assets/images/th03.jfif) center 0;
  height: 700px;
  min-width: 1000px;

  .main {
    width: 1000px;
    height: 100%;
    margin: 0 auto;
    position: relative;

    .form-wrapper {
      width: 400px;
      margin: 0 auto;
      background: #fff;
      box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.1);
      overflow: hidden;

      .tabs {
        span {
          display: block;
          width: 50%;
          height: 50px;
          box-sizing: border-box;
          border-top: 2px #eee solid;
          background: #eee;
          line-height: 48px;
          text-align: center;
          cursor: pointer;
          color: #666;

          &.active {
            color: orange;
            border-top-color: orange;
            background: #fff;
            font-weight: bold;
          }
        }
      }
    }
  }
}
</style>

效果

在这里插入图片描述

三、form表单组件发送登录请求

  • 思路:
  1. form表单布局
  2. 绑定输入框数据
  3. 表单验证三步走:绑定整个from表单数据,定义rules校验规则,表单每一项对应prop并设置在el-form-item上。Element------validate表单验证代码示例与校验规则
  4. 获取光标时清除校验提升客户体验.
  5. 失去焦点一次校验,点击提交二次校验, 通过验证发送登录请求,并给出提示

components/user/LoginForm.vue

<template>
  <!-- :model:收集表单数据 -->
  <!-- ref:表单标识 -->
  <!-- rules:校验用户输入的数据 -->
  <!-- el-form-item:输入框 -->
  <!-- v-model:绑定该项输入框的值 -->
  <!-- prop:必须绑定在el-form-item,这里的username非form.username -->
  <!-- focus:聚焦时触发事件 -->
  <el-form :model="form" ref="form" :rules="rules" class="form">
    <el-form-item class="form-item" prop="username">
      <el-input
        placeholder="用户名/手机"
        v-model="form.username"
        @focus="clearUsername"
      >
      </el-input>
    </el-form-item>

    <el-form-item class="form-item" prop="password">
      <el-input
        placeholder="密码"
        type="password"
        v-model="form.password"
        @focus="clearPassword"
      >
      </el-input>
    </el-form-item>

    <p class="form-text">
      <nuxt-link to="#">忘记密码</nuxt-link>
    </p>
    <!-- 点击触发事件提交表单 -->
    <el-button class="submit" type="primary" @click="handleLoginSubmit">
      登录
    </el-button>
  </el-form>
</template>

<script>
import { login } from "@/components/user/user";
export default {
  data() {
    return {
      form: {
        username: "13800138000", //用户名数据
        password: "123456", //密码数据
      },
      //定义校验规则,这是个数组,可以多组校验
      rules: {
        username: [
          {
            required: true, //必须传参
            message: "请输入合法的手机号/用户名", //不传参时显示字段
            trigger: "blur", //失去焦点触发一次校验
          },
        ],
        password: [
          {
            required: true, //必须传参
            message: "请输入密码", //不传参时显示字段
            trigger: "blur", //失去焦点触发一次校验
          },
          {
            min: 6, //最小6位
            message: "密码不能小于六位", //不传参时显示字段
            trigger: "blur", //失去焦点触发一次校验
          },
        ],
      },
    };
  },
  methods: {
    //   提交表单
    async handleLoginSubmit() {
      // 点击按钮时触发函数二次校验
      console.log(this.form);
      const valid = this.$refs.form.validate();
      //为true发送接口
      if (valid) {
        const res = await login(this.form);
        console.log(res);
        //发送成功返回token提示登录成功
        if (res.data.token) {
          this.$message({
            message: "登录成功",
            type: "success",
          });
        } else {
          this.$message.error("登录错误");
        }
      }
    },
    // 聚焦时移除用户名校验
    clearUsername() {
      this.$refs.form.clearValidate("username");
    },
    // 聚焦时移除密码校验
    clearPassword() {
      this.$refs.form.clearValidate("password");
    },
  },
};
</script>

<style scoped lang="less">
.form {
  padding: 25px;
}

.form-item {
  margin-bottom: 20px;
}

.form-text {
  font-size: 12px;
  color: #409eff;
  text-align: right;
  line-height: 1;
}

.submit {
  width: 100%;
  margin-top: 10px;
}
</style>

表单引入pages/user/login.vue

// 引入登录
import LoginForm from "@/components/user/LoginForm";

注册

 components: {
    LoginForm, //登录表单
  },

打开注释的登录组件代码

<!-- 登录功能组件 -->
        <LoginForm v-if="currentTab == 0" />

四、封装axios

utils/http.js封装基路径

//创建一个axios
import axios from 'axios'
axios.defaults.baseURL
const http = axios.create({
    baseURL: 'http://157.122.54.189:9095'
})
http.interceptors.response
export default http

user/user.js

// 引入基准路径
import axios from '@/utils/http'

//登陆接口,post请求
export const login = (data) => {
    return axios({
        url: '/accounts/login',
        method: 'post',
        data
    })
}

五、发送登录请求效果

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

六、vuex管理用户信息步骤(store)

  1. 创建store仓库,为后期储存做准备
  2. 登录时判断成功后储存用户信息
  3. 头部判断储存仓库中有没有用户信息,有的话隐藏登录/注册按钮,渲染用户信息

创建数据仓库store/user.js

// 用户管理
export const state = () => ({
    // 采用接口返回的数据结构
    userInfo: {
    },
})

//修改state里面的数据,必须使用mutations里面的函数
export const mutations = {
    userInfo(state, data) {
        //每个mutations可以接收到两个参数
        //第一个是state对象本身
        //第二个是调用函数是额外添加的数据
        state.userInfo = data
        //外面调用这个函数,并且传入数据,即可改变userInfo
    }
};

渲染头部组件components/PageHeader.vue

  • 在原先静态登录/注册信息中更改为如下代码
  <!-- 如果用户存在则展示用户信息,用户数据来自store -->
        <!-- 判断token数据存不存在,存在则显示 -->
        <el-dropdown v-if="userInfo.token">
          <el-row type="flex" align="middle" class="el-dropdown-link">
            <nuxt-link to="#">
              <!-- 判断图片有没有,拼接图片路径 -->
              <img
                v-if="userInfo.user.defaultAvatar"
                :src="$axios.defaults.baseURL + userInfo.user.defaultAvatar"
              />
              <!-- 渲染名字 -->
              {{ userInfo.user.nickname }}
            </nuxt-link>
            <i class="el-icon-caret-bottom el-icon--right"></i>
          </el-row>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item>
              <nuxt-link to="#">个人中心</nuxt-link>
            </el-dropdown-item>
            <el-dropdown-item>
              <div @click="handleLogout">退出</div>
            </el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
computed: {
    userInfo() {
      return this.$store.state.user.userInfo;
    },
  }

user/LoginForm表单组件中判断成功储存数据

  • this.$store.commit("user/userInfo", res.data);
 if (res.data.token) {
          this.$message({
            message: "登录成功",
            type: "success",
          });
          this.$store.commit("user/userInfo", res.data); //储存用户信息
        } 

七、储存与调用数据过程图

在这里插入图片描述

八、用户信息渲染效果

在这里插入图片描述

九、vuex(store)利用插件持久化到本地储存

vuex(store)利用插件持久化到本地储存的方法