挑戰30分鐘寫個使用者登入註冊系統(前端)- 我的全棧獨立開發日記

admin0000發表於2023-04-07

挑戰30分鐘寫個使用者登入註冊系統(前端)- 我的全棧獨立開發日記

前言

技術棧是 uni-app + uView2.0 + Vue.js

本教程的影片版本已釋出在B站,歡迎交流~

戳我看影片:靜夜Coding-無人聲程式設計-機械鍵盤純享版

準備工作

下載UI元件uView UI,作為uni-app外掛引入,下載地址在這裡

登入頁面

先寫好HTML骨架:

新建頁面檔案index.vue,檔案路徑是pages/index/index.vue

<template>
  <view class="login-page">
    <u--image src="/static/login-bg.png" class="image-bg" mode="aspectFit" width="100%"></u--image>
    <view class="form-content">
      <u--form>
        <u-form-item>
          <u-icon name="account" size="24px"></u-icon>
          <u--input placeholder="請輸入使用者名稱" border="bottom" v-model="username"></u--input>
        </u-form-item>
        <u-form-item>
          <u-icon name="lock" size="24px"></u-icon>
          <u--input placeholder="請輸入密碼" border="bottom" v-model="password" clearable :password="showPwd">
            <template slot="suffix">
              <view class="slot-list">
                <u-icon :name="showPwd ? 'eye-off' : 'eye-fill'" @click="showPassword"
                        style="padding: 0 5px;"
                ></u-icon>
                <u--text text="忘記密碼" size="12"></u--text>
              </view>
            </template>
          </u--input>
        </u-form-item>
      </u--form>
    </view>

    <view class="btn-list">
      <u-button text="立即登入" type="primary" size="large" @click="onSubmit"></u-button>
      <u-button text="註冊" type="primary" size="large"
                :plain="true" :hairline="true" @click="navRegister"
      ></u-button>
    </view>
    <view class="third-party-login">
      <u-icon name="weixin-circle-fill" size="36px" color="#2cba00"></u-icon>
      <u-icon name="zhifubao-circle-fill" size="36px" color="#019be1"></u-icon>
      <u-icon name="weibo-circle-fill" size="36px" color="#df2125"></u-icon>
    </view>
    <u-radio-group v-model="agreeTerms" style="justify-content: center;align-items: center">
      <u-radio shape="circle" name="1"></u-radio>
      <span style="font-size: 12px">我已閱讀並同意
      <span style="color: #3c9cff">《使用者協議》</span><span style="color: #3c9cff">《隱私協議》</span>
      </span>
    </u-radio-group>
    <u-toast ref="uToast"></u-toast>
  </view>
</template>

頁面雛形就出來了

Laravel

接下里我們用css修飾頁面佈局和顏色:

這裡使用了flex佈局

<style>
.login-page {
  display: flex;
  flex-direction: column;
  padding: 2rem 2rem;
}

.image-bg {
  align-items: center;
}

.form-content {
  padding: 1rem 0rem;
}

.third-party-login {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  padding: 1rem 2rem;
  gap: 0.5rem;
}

.slot-list {
  display: flex;
  flex-direction: row;
}

.btn-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

</style>

嗯,現在頁面就很漂亮了

Laravel

接下來補充業務邏輯程式碼:

使用javascript編寫邏輯,它負責包括提交表單、請求API介面、與後端互動和控制動態彈窗等工作

還需要用到瀏覽器的本地儲存能力localStorage來儲存後端返回的登入token,以便後續訪問其他需要登入的介面

<script>
import {userAccountLogin} from '../../config/api.js'

export default {
  data() {
    return {
      username: "",
      password: "",
      showPwd: true,
      agreeTerms: 1,
    }
  },

  methods: {
    showPassword() {
      this.showPwd = !this.showPwd
    },

    // 提交表單
    onSubmit() {
      if (this.agreeTerms != 1) {
        this.$refs.uToast.show({
          message: '請先勾選同意使用者協議'
        })
        return
      }

      userAccountLogin({
        username: this.username,
        password: this.password
      }).then(res => {
        this.$refs.uToast.show({
          message: '登入成功',
          complete() {
            // 儲存登入態TOKEN
            uni.setStorageSync('token', res.token)
            uni.navigateTo({
              url: '/pages/index/userinfo'
            })
          }
        })
      })
    },

    // 跳轉註冊頁面
    navRegister() {
      uni.navigateTo({
        url: '/pages/index/register'
      })
    }
  }
}

</script>

登入成功會跳轉到使用者資訊頁面

使用者資訊頁面

這裡我們寫一個簡單的使用者卡片來展示使用者資料,然後還有一個修改暱稱的功能,點選修改按鈕,會跳出彈窗給使用者修改自己的暱稱。

一共涉及到兩個和後端互動的介面,一是獲取使用者資訊,而是修改使用者暱稱。

Laravel

新建頁面 userinfo.vue

<template>
  <view>
    <div class="usercard">
      <div class="baseinfo">
        <div class="avatar">
          <u--image src="/static/logo.png" mode="aspectFit" width="60px"></u--image>
        </div>
        <div class="nameinfo">
          <p>暱稱:{{ userinfo.nickname == '' ? '使用者' + userinfo.id : userinfo.nickname }}</p>
          <p>UID:{{ userinfo.id }}</p>
        </div>
      </div>
      <div class="userinfo">
        <p>使用者名稱:{{ userinfo.username }}</p>
        <p>註冊時間:{{ userinfo.created_at }}</p>
      </div>
    </div>
    <div class="edit-info">
      <u-popup :show="show" mode="center" @close="close" @open="open" round="10"
               overlayOpacity="0.3" customStyle="width:70%;padding:10px;">
        <view>
          <u--form>
            <u-form-item>
              <u-icon name="account" size="24px"></u-icon>
              <u--input placeholder="請輸入新的暱稱" v-model="nickname"></u--input>
            </u-form-item>
          </u--form>
          <u-button @click="updateInfo" type="primary" icon="checkmark" size="small">
            儲存
          </u-button>
        </view>
      </u-popup>
      <u-button @click="show=true" type="primary" icon="edit-pen">修改暱稱</u-button>
    </div>
  </view>
</template>

<script>
import {userAccountGetInfo, userAccountUpdateInfo} from '../../config/api.js'

export default {
  data() {
    return {
      userinfo: {},
      show: false,
      nickname: "",
    }
  },
  methods: {
    open() {

    },
    close() {
      this.show = false
    },
    getUserInfo() {
      userAccountGetInfo().then(data => {
        this.userinfo = data
        this.nickname = data.nickname
      })
    },
    updateInfo() {
      userAccountUpdateInfo({
        nickname: this.nickname
      }).then(() => {
        this.close()
      })
      // 重新整理使用者資訊
      this.getUserInfo()
    }
  },
  onLoad() {
    this.getUserInfo()
  }
}
</script>


<style lang="scss">
.baseinfo {
  display: flex;
  flex-direction: row;
  align-items: center;
  width: 80%;
  height: 80px;
  padding: 1rem;
  gap: 0.5rem;

  .avatar {
    display: flex;
  }

  .nameinfo {
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
}

.userinfo {
  padding: 0 1rem 1rem 1rem;

  p {
    margin-bottom: 10px;
  }
}

.usercard {
  padding: 0.5rem;
  border-radius: 20px;
  border: 1px solid $u-border-color;
  margin: 5px;
  background: linear-gradient(275deg, $u-primary-disabled, $u-primary-light);
  box-shadow: 14px 8px 14px 3px rgba(43, 98, 169, 0.5);
}

</style>

細心的讀者會注意到css樣式的style標籤有一個屬性<style lang="scss">和登入頁面的有些不同,這裡我們是用了scss語法來使得css可以支援巢狀語法,這樣對於多層巢狀的樣式程式碼來說便於閱讀和維護。

接下來我們繼續完成註冊頁面

註冊頁面

註冊頁面的骨架和登入頁面大同小異,都要求使用者填寫使用者名稱和密碼,只不過多了一個確認密碼的欄位。稍微修改下javascript程式碼就可以了。

Laravel

新建頁面 register.vue

<template>
  <view class="page-content">
    <u--image src="/static/register-bg.png" mode="aspectFit"></u--image>
    <view class="form-content">
      <u--form>
        <u-form-item>
          <u-icon name="account" size="24px"></u-icon>
          <u--input placeholder="請輸入使用者名稱" border="bottom" v-model="username"></u--input>
        </u-form-item>
        <u-form-item>
          <u-icon name="lock" size="24px"></u-icon>
          <u--input placeholder="請輸入密碼" border="bottom" v-model="password" clearable :password="showPwd">
            <template slot="suffix">
              <view class="slot-list">
                <u-icon :name="showPwd ? 'eye-off' : 'eye-fill'" @click="showPassword"
                        style="padding: 0 5px;"
                ></u-icon>
              </view>
            </template>
          </u--input>
        </u-form-item>
        <u-form-item>
          <u-icon name="lock" size="24px"></u-icon>
          <u--input placeholder="請再次輸入密碼" border="bottom" v-model="confirmPassword" clearable :password="showPwd">
            <template slot="suffix">
              <view class="slot-list">
                <u-icon :name="showPwd ? 'eye-off' : 'eye-fill'" @click="showPassword"
                        style="padding: 0 5px;"
                ></u-icon>
              </view>
            </template>
          </u--input>
        </u-form-item>
      </u--form>
    </view>

    <view class="btn-list">
      <u-button text="立即註冊" type="primary" size="large" @click="onSubmit"></u-button>
    </view>
    <u-radio-group v-model="agreeTerms" style="justify-content: center;align-items: center">
      <u-radio shape="circle" name="1"></u-radio>
      <span style="font-size: 12px">我已閱讀並同意
      <span style="color: #3c9cff">《使用者協議》</span><span style="color: #3c9cff">《隱私協議》</span>
      </span>
    </u-radio-group>
    <u-toast ref="uToast"></u-toast>
  </view>
</template>

<script>
import {userAccountRegister} from '../../config/api.js'

export default {
  data() {
    return {
      username: "",
      password: "",
      confirmPassword: "",
      agreeTerms: 0,
      showPwd: true,
    }
  },
  methods: {
    showPassword() {
      this.showPwd = !this.showPwd
    },
    onSubmit() {
      if (this.agreeTerms != 1) {
        this.$refs.uToast.show({
          message: '請先勾選同意使用者協議'
        })
        return
      }
      if (this.password !== this.confirmPassword) {
        this.$refs.uToast.show({
          message: '兩次輸入的密碼不一致'
        })
        return
      }

      userAccountRegister({
        username: this.username,
        password: this.password
      }).then(() => {
        this.$refs.uToast.show({
          message: '註冊成功',
          complete() {
            // 跳轉登入
            uni.navigateTo({
              url: '/pages/index/index'
            })
          }
        })
      })
    },
  }
}
</script>

<style lang="scss">
.page-content {
  display: flex;
  flex-direction: column;
  padding: 2rem 2rem;
}

.image-bg {
  align-items: center;
}

.form-content {
  padding: 1rem 0rem;
}

.slot-list {
  display: flex;
  flex-direction: row;
}

.btn-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding-bottom: 1rem;
}
</style>

關於

我是一名獨立開發者,只工作不上班,帶你探索業界正流行的現代化全棧專案。
以下是近期會更新的一系列文章,歡迎關注~

  • 使用者-登入註冊-後端
  • 使用者-登入註冊-前端
  • 使用者-錢包充值-後端
  • 使用者-錢包充值-前端
  • 營銷-簽到有禮-後端
  • 營銷-簽到有禮-前端
  • 營銷-簽到有禮-後端單元測試
  • 營銷-限時秒殺-後端
  • 營銷-限時秒殺-前端
  • 營銷-優惠券-後端
  • 營銷-優惠券-前端
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章