socket 客户端——登录、注册

socket 客户端——登录、注册

新增登录、注册、上传头像接口

src/api/user/index.js

/**
 * @author Wuner
 * @date 2022/9/27 14:08
 * @description user 用户模块
 */
import { post } from '@/utils/http';

/**
 * 登录
 * @param data
 * @param data.username => 用户名
 * @param data.password => 密码
 * @returns
 */

const url = '/socket-chat-back/user';

export const login = (data) => {
  return post(`${url}/login`, data);
};

/**
 * 登录
 * @param data
 * @param data.username => 用户名
 * @param data.password => 密码
 * @param data.avatar => 头像路径
 * @returns
 */
export const register = (data) => {
  return post(`${url}/register`, data);
};
  • post 请求新增 headers
  • src/utils/http.js
const post = (url, data, headers) => {
  return axios({
    method: 'post',
    url,
    data,
    headers,
  });
};

src/api/upload/index.js

import { post } from '@/utils/http';

const url = '/socket-chat-back/upload';

/**
 * 上传头像
 * @param file 图片文件
 */
export const uploadAvatar = (file) => {
  const formData = new FormData();
  formData.append('img', file);

  return post(`${url}/avatar`, formData, {
    'Content-Type': 'multipart/form-data',
  });
};

vuex 新增登录信息获取及缓存

/**
 * @author Wuner
 * @date 2020/12/9 18:00
 * @description 用户模块
 */
import Session from '@/utils/session';
import { login } from '@/api/api/user';

const storeState = Session.get('storeState');

const state = (storeState && storeState.user) || {
  // 用户信息
  userinfo: {
    username: '',
    nickname: '',
    avatar: '',
  },
};

const getters = {};

const mutations = {
  /**
   * 设置用户信息
   * @param state
   * @param data
   */
  setUserinfo(state, data) {
    state.userinfo = data;
  },
};

const actions = {
  /**
   * 登录
   * @param ***mit
   * @param user
   * @returns {Promise<void>}
   */
  login({ ***mit }, user) {
    return new Promise(async (resolve, reject) => {
      try {
        const data = await login(user);
        ***mit('setUserinfo', data.userinfo);
        resolve(data);
      } catch (e) {
        console.log(e);
        reject(e);
      }
    });
  },
};

export default { namespaced: true, state, getters, mutations, actions };

新增登录、注册页面

  • 登录页面
  • src/view/login/index.vue
<template>
  <div class="login">
    <img class="login-logo" src="../../assets/logo.png" alt="" />
    <div class="login-input">
      <van-field
        v-model="username"
        left-icon="manager"
        clearable
        placeholder="请输入用户名"
      />
      <van-field
        v-model="password"
        left-icon="lock"
        type="password"
        clearable
        placeholder="请输入密码"
      />
    </div>
    <div class="login-remember">
      <van-checkbox v-model="checked">记住我的账号和密码</van-checkbox>
      <span @click="onRegister">新用户</span>
    </div>
    <div class="login-btn" @click="onLogin">登录</div>
  </div>
</template>

<script>
import { remove, set, get } from '@/utils/***mon-utils';
import { mapActions } from 'vuex';
import { createSocket } from '@/utils/socket';

export default {
  data() {
    return {
      username: '',
      password: '',
      // 是否记住密码
      checked: false,
    };
  },
  methods: {
    ...mapActions('user', ['login']),
    /**
     * 登录
     */
    onLogin() {
      const { username, password, checked } = this;
      if (!username) {
        this.$toast('请输入用户名');
        return;
      }
      if (!password) {
        this.$toast('请输入密码');
        return;
      }
      this.login({ username, password })
        .then(() => {
          // 记住密码时,缓存用户账号信息
          if (checked) {
            set('checked', checked);
            set('password', password);
            set('username', username);
          } else {
            remove('password');
            remove('checked');
            remove('username');
          }
          // 创建 socket 服务
          createSocket(username);
          this.$router.push('/');
        })
        .catch((e) => {
          this.$toast(e);
        });
    },
    /**
     * 跳转注册页面
     */
    onRegister() {
      this.$router.push({ name: 'register' });
    },
  },
  created() {
    // 记住密码时,获取缓存的账号信息进行赋值
    if (get('checked')) {
      this.username = get('username');
      this.checked = get('checked');
      this.password = get('password');
    }
  },
};
</script>
<style lang="less">
.input {
  .van-field__left-icon {
    .van-icon {
      color: #999;
    }
  }

  .checkbox {
    font-size: 14px;
    margin-top: 20px;
    margin-left: 10px;

    .van-checkbox__icon {
      width: 10px;
      height: 10px;
      line-height: 10px;
    }

    .van-icon {
      font-size: 10px;
    }
  }
}
</style>
<style scoped lang="less">
.login {
  padding: 0 16px;

  &-logo {
    width: 150px;
    display: block;
    margin: 50px auto;
  }

  &-input {
    .van-cell {
      border-bottom: 1px solid #999;
    }

    .van-icon {
      color: #999;
    }

    .van-cell + .van-cell {
      margin-top: 20px;
    }
  }

  &-remember {
    display: flex;
    justify-content: space-between;
    margin-top: 8px;
    align-items: center;

    span {
      color: #0f88e4;
      font-size: 14px;
    }
  }

  &-btn {
    margin: 30px auto;
    height: 36px;
    background-color: #0f88e4;
    display: flex;
    justify-content: center;
    align-items: center;
    color: #fff;
    font-size: 14px;
    border-radius: 6px;
  }
}
</style>
  • 注册页面
  • src/view/register/index.vue
<template>
  <div class="register">
    <img class="register-logo" src="../../assets/logo.png" alt="" />
    <div class="register-input">
      <p class="register-input-desc">上传头像</p>
      <van-uploader
        v-model="fileList"
        :after-read="afterRead"
        :before-read="beforeRead"
        @delete="onDelete"
        :max-count="1"
      />
      <van-field
        v-model="username"
        left-icon="manager"
        clearable
        placeholder="请输入用户名"
      />
      <van-field
        v-model="nickname"
        left-icon="manager"
        clearable
        placeholder="请输入昵称"
      />
      <van-field
        v-model="password"
        left-icon="lock"
        type="password"
        clearable
        placeholder="请输入密码"
      />
      <van-field
        v-model="password1"
        left-icon="lock"
        type="password"
        clearable
        placeholder="请再次输入密码"
      />
    </div>
    <div class="register-btn" @click="onRegister">注册</div>
  </div>
</template>

<script>
import { uploadAvatar } from '@/api/api/upload';
import { register } from '@/api/api/user';

export default {
  data() {
    return {
      nickname: '',
      username: '',
      password: '',
      password1: '',
      fileList: [],
      avatar: '',
    };
  },
  methods: {
    /**
     * 注册
     */
    onRegister() {
      const { nickname, username, password, password1, avatar } = this;
      if (!avatar) {
        this.$toast('请上传头像');
        return;
      }
      if (!username) {
        this.$toast('请输入用户名');
        return;
      }
      if (!nickname) {
        this.$toast('请输入昵称');
        return;
      }
      if (!password) {
        this.$toast('请输入密码');
        return;
      }
      if (!password1) {
        this.$toast('请再次输入密码');
        return;
      }
      if (password !== password1) {
        this.$toast('两次输入的密码不一致');
        return;
      }
      register({ nickname, username, password, avatar })
        .then(() => {
          this.$toast('注册成功');
          this.$router.replace({ name: 'login' });
        })
        .catch((e) => {
          this.$toast(e);
        });
    },
    /**
     * 文件读取前的回调函数,返回 false 可终止文件读取,
     * @param file
     * @returns {boolean}
     */
    beforeRead(file) {
      // 判断文件是否是 jpg 和 png 格式
      if (!['image/jpeg', 'image/png'].includes(file.type)) {
        this.$toast('请上传 jpg 或 png 格式图片');
        return false;
      }
      return true;
    },
    /**
     * 文件读取完成后的回调函数
     * @param file
     */
    afterRead(file) {
      file.status = 'uploading';
      file.message = '上传中...';
      uploadAvatar(file.file)
        .then((data) => {
          file.status = 'done';
          this.avatar = data.imgPath;
        })
        .catch(() => {
          file.status = 'failed';
          file.message = '上传失败';
        });
    },
    /**
     * 删除图片时触发
     */
    onDelete() {
      this.avatar = '';
    },
  },
};
</script>
<style lang="less">
.input {
  .van-field__left-icon {
    .van-icon {
      color: #999;
    }
  }

  .checkbox {
    font-size: 14px;
    margin-top: 20px;
    margin-left: 10px;

    .van-checkbox__icon {
      width: 10px;
      height: 10px;
      line-height: 10px;
    }

    .van-icon {
      font-size: 10px;
    }
  }
}
</style>
<style scoped lang="less">
.register {
  &-logo {
    width: 150px;
    display: block;
    margin: 50px auto;
  }

  &-input {
    margin: 0 30px;

    .van-cell {
      border-bottom: 1px solid #999;
    }

    .van-icon {
      color: #999;
    }

    .van-cell + .van-cell {
      margin-top: 20px;
    }

    &-desc {
      font-size: 12px;
      color: #999999;
      padding: 4px 0;
    }
  }

  &-btn {
    margin: 30px auto;
    width: 90%;
    height: 36px;
    background-color: #0f88e4;
    display: flex;
    justify-content: center;
    align-items: center;
    color: #fff;
    font-size: 14px;
    border-radius: 6px;
  }
}
</style>
  • 我的页面增加用户信息展示
  • src/view/mine/index.vue
<template>
  <div class="mine">
    <div class="mine-header">
      <img :src="userinfo.avatar" alt="" />
      <div>
        <p class="mine-header-nickname">{{ userinfo.nickname }}</p>
        <p class="mine-header-username">用户名:{{ userinfo.username }}</p>
      </div>
    </div>
  </div>
</template>

<script>
import { mapState } from 'vuex';

export default {
  name: 'mine',
  data() {
    return {};
  },
  ***puted: {
    ...mapState('user', ['userinfo']),
  },
};
</script>

<style scoped lang="less">
.mine {
  min-height: 100vh;
  background-color: #f5f5f5;

  &-header {
    display: flex;
    padding: 16px;
    background-color: white;

    img {
      width: 60px;
      height: 60px;
      border-radius: 4px;
    }

    div {
      margin-left: 8px;
      display: flex;
      flex-wrap: wrap;
      align-content: space-between;

      p {
        width: 100%;
      }
    }

    &-nickname {
      font-size: 20px;
      font-weight: bold;
    }

    &-username {
      font-size: 12px;
      color: #666;
    }
  }
}
</style>
  • 新增头部导航栏
  • 在登录等页面不需要出现返回按钮
  • src/App.vue
<van-nav-bar
  :title="$route.meta.title"
  :left-arrow="!['message', 'friends', 'mine', 'login'].includes($route.name)"
  @click-left="$router.back()"
/>

路由配置

src/router/index.js

{
  path: '/login',
  name: 'login',
  ***ponent: () =>
    import(/*webpackChunkName: "index"*/ '@/view/login/index.vue'),
  meta: {
    title: '登录',
  },
},
{
  path: '/register',
  name: 'register',
  ***ponent: () =>
    import(/*webpackChunkName: "index"*/ '@/view/register/index.vue'),
  meta: {
    title: '注册',
  },
},

去除假数据

  • main.js
  • 之前写的假数据,我们直接干掉
router.beforeEach((to, from, next) => {
  if (store.state.user.userinfo.username && !window.socket) {
    createSocket(store.state.user.userinfo.username);
    next();
  } else {
    next();
  }
});

配置跨域请求

config/index.js

proxyTable: {
  '/socket-chat-back/': {
    target: 'http://127.0.0.1:4000', //测试环境
    changeOrigin: true,
  },
},

演示

项目地址

转载请说明出处内容投诉
CSS教程_站长资源网 » socket 客户端——登录、注册

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买