You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue+Pinia+Tailwind实现未登录触发登录模态框方案问询

实现需登录功能的登录引导逻辑(Vue+Pinia+Tailwind)

所需条件

  • 项目已集成Pinia并完成全局注册
  • NavBar组件已在全局或页面中引入,登录模态框的显示状态可被全局控制
  • 已明确哪些功能需要登录权限(比如收藏、下单、个人中心操作等)

实现步骤

1. 创建Pinia全局状态管理Store

新建stores/authStore.js,统一管理用户登录状态和登录模态框的显示:

import { defineStore } from 'pinia'

export const useAuthStore = defineStore('auth', {
  state: () => ({
    isLoggedIn: false, // 用户是否已登录
    showLoginModal: false // 登录模态框显示状态
  }),
  actions: {
    // 打开登录模态框
    openLoginModal() {
      this.showLoginModal = true
    },
    // 关闭登录模态框
    closeLoginModal() {
      this.showLoginModal = false
      // 可选:关闭时重置表单输入
    },
    // 登录成功后更新状态
    loginSuccess() {
      this.isLoggedIn = true
      this.closeLoginModal()
    }
  }
})

2. 改造NavBar.vue中的登录模态框

将模态框的显示状态绑定到Pinia的全局状态,同时更新关闭、登录逻辑:

<template>
  <!-- NavBar其他内容 -->
  <!-- 登录模态框 -->
  <div v-if="authStore.showLoginModal" class="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-75">
    <div class="relative bg-white lg:w-[70vw] w-[90vw] h-fit rounded-3xl">
      <img src="@/assets/images/Modal/Intersect.png" alt="" class="w-full absolute rounded-t-xl">

      <!-- 关闭按钮 -->
      <div class="relative top-0 flex justify-end">
        <button class="absolute pr-4 pt-4" @click="authStore.closeLoginModal">
          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="5" stroke="black" class="w-7 h-7">
            <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
          </svg>
        </button>
      </div>

      <div class="relative z-10 flex flex-col items-start justify-center h-fit lg:mt-[5rem] mt-[5rem] lg:ml-[6%] m-8">
        <div class="mt-5 mb-5">
          <h2 class="text-2xl lg:text-3xl font-bold">Welcome!</h2>
          <p class="text-xs lg:text-sm">Please log in to your account.</p>
        </div>

        <form @submit.prevent="handleLogin" class="lg:w-[40%] h-fit md:w-[40%] w-full mr-8">
          <!-- 用户名输入 -->
          <div class="relative mb-4 w-full md:w-365">
            <label for="username" class="block text-gray-700">Enter Username</label>
            <div class="relative">
              <img src="@/assets/images/Modal/profile.png" class="absolute inset-y-0 left-0 mx-5 my-3" style="width: 15px;" alt="Username Icon" />
              <input type="text" id="username" v-model="username" @input="validateUsername" class="w-full h-8 py-5 pl-10 border border-black rounded-xl" placeholder="Username">
              <span v-if="usernameError" class="text-red-500 text-xs">{{ usernameError }}</span>
            </div>
          </div>
          <!-- 密码输入 -->
          <div class="relative mb-4">
            <label for="lpassword" class="w-full text-gray-700">Enter Password</label>
            <div class="relative w-full">
              <img src="@/assets/images/Modal/profile.png" class="absolute inset-y-0 left-0 mx-5 my-3" style="width: 15px;" alt="Username Icon" />
              <img v-if="showPassword" src="@/assets/images/Modal/view.png" class="absolute inset-y-0 right-0 mx-3 my-3" style="width: 15px; cursor: pointer;" alt="Eye Icon" @click="togglePasswordVisibility">
              <img v-else src="@/assets/images/Modal/hidden.png" class="absolute inset-y-0 right-0 mx-3 my-3" style="width: 15px; cursor: pointer;" alt="Eye Icon" @click="togglePasswordVisibility">
              <input id="lpassword" v-model="lpassword" @input="validateLPassword" :type="showPassword ? 'text' : 'password'" class="w-full h-8 py-5 pl-10 border border-black rounded-xl" placeholder="Password">
              <span v-if="lpasswordError" class="text-red-500 text-xs">{{ lpasswordError }}</span>
            </div>
          </div>

          <div class="hidden lg:block lg:flex items-center justify-between mb-4">
            <p class="text-xs">Don't have an account? <button @click="openPrivacyModal" class="text-xs font-bold text-blue-600">Sign Up</button></p>
            <button @click="openForgotModal" class="text-xs text-blue-600 font-bold">Forgot Password?</button>
          </div>

          <div class="hidden lg:block lg:mt-10 text-center">
            <button type="submit" class="lg:w-[8rem] w-full lg:px-4 py-2 text-white disabled:bg-blue-400 bg-blue-600 rounded-md" :disabled="!isLoginFormValid">Login</button>
          </div>

          <div class="lg:hidden lg:mt-10 text-center">
            <button type="submit" :disabled="!isLoginFormValid" class="lg:w-[8rem] w-full lg:px-4 py-2 lg:mb-10 text-white disabled:bg-blue-400  bg-blue-600 rounded-md">Login</button>
            <button @click="openForgotModal" class="text-xs text-blue-600 mt-1">Forgot Password?</button>
            <button @click="openPrivacyModal" class="w-full mt-8 py-2 border border-blue-400 text-blue-600 bg-white rounded-md">Sign Up</button>
          </div>
        </form>
      </div>
    </div>
  </div>
</template>

<script setup>
import { useAuthStore } from '@/stores/authStore'
import { ref, computed } from 'vue'

const authStore = useAuthStore()
const username = ref('')
const lpassword = ref('')
const showPassword = ref(false)
const usernameError = ref('')
const lpasswordError = ref('')

// 用户名验证逻辑(保留原有逻辑)
const validateUsername = () => {
  // 你的验证代码
}
// 密码验证逻辑(保留原有逻辑)
const validateLPassword = () => {
  // 你的验证代码
}
// 表单有效性判断
const isLoginFormValid = computed(() => {
  return !usernameError.value && !lpasswordError.value && username.value && lpassword.value
})

// 切换密码可见性
const togglePasswordVisibility = () => {
  showPassword.value = !showPassword.value
}

// 登录处理
const handleLogin = async () => {
  try {
    // 替换为实际登录API请求
    // const res = await api.login(username.value, lpassword.value)
    // 登录成功更新全局状态
    authStore.loginSuccess()
    // 可选:登录成功后执行用户原本要触发的操作
  } catch (err) {
    console.error('登录失败', err)
  }
}

// 打开注册模态框(根据实际逻辑实现)
const openPrivacyModal = () => {
  // 你的注册模态框逻辑
}
// 打开忘记密码模态框(根据实际逻辑实现)
const openForgotModal = () => {
  // 你的忘记密码模态框逻辑
}
</script>

3. 封装登录校验工具函数

新建utils/auth.js,实现通用的登录校验逻辑,避免重复代码:

import { useAuthStore } from '@/stores/authStore'

export const requireAuth = (callback) => {
  const authStore = useAuthStore()
  if (authStore.isLoggedIn) {
    // 用户已登录,执行原功能
    callback?.()
  } else {
    // 用户未登录,唤起登录模态框
    authStore.openLoginModal()
    // 可选:记录用户待执行操作,登录成功后自动触发
    // authStore.setPendingAction(callback)
  }
}

4. 在需要登录的功能中调用校验逻辑

比如在收藏按钮、下单按钮等组件中使用:

<template>
  <button @click="handleFavorite" class="bg-blue-500 text-white px-4 py-2 rounded">收藏</button>
</template>

<script setup>
import { requireAuth } from '@/utils/auth'

const handleFavorite = () => {
  requireAuth(() => {
    // 这里写收藏功能的实际逻辑
    console.log('执行收藏操作')
  })
}
</script>

关键细节说明

  • 模态框的显示/隐藏完全由Pinia全局状态控制,确保在任何页面调用都能保持一致性
  • 登录成功后必须更新isLoggedIn状态,避免重复唤起模态框
  • 若需要支持登录后自动执行用户原操作,可在Pinia中添加pendingAction状态,登录成功后调用该函数

内容的提问来源于stack exchange,提问作者Yvette Medrano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 17:15:00