Vue单点登录组件使用指南与封装方法

代码教程

Vue单点登录组件使用指南与封装方法

一、基础使用方法

(一)前置条件

  1. 确保项目已安装以下依赖:

    npm install vue-router pinia axios
    
  2. 项目中已创建以下文件结构:

    src/
    ├── stores/
    │   └── auth.ts         # 认证状态管理
    ├── router/
    │   └── index.ts        # 路由配置
    ├── components/
    │   ├── BaseButton.vue  # 基础按钮组件
    │   └── BaseInput.vue   # 基础输入组件
    └── views/
        └── LoginView.vue   # 登录视图
    

(二)使用示例

  1. 在路由配置中添加登录页面:

    // router/index.ts
    import { createRouter, createWebHistory } from 'vue-router';
    import LoginView from '../views/LoginView.vue';
    import DashboardView from '../views/DashboardView.vue';
    
    const routes = [
      {
        path: '/login',
        name: 'Login',
        component: LoginView,
      },
      {
        path: '/dashboard',
        name: 'Dashboard',
        component: DashboardView,
        meta: { requiresAuth: true },
      },
    ];
    
    const router = createRouter({
      history: createWebHistory(),
      routes,
    });
    
    export default router;
    
  2. 在主应用中使用:

    <!-- App.vue -->
    <template>
      <div id="app">
        <router-view />
      </div>
    </template>
    
    <script setup>
    // 导入必要的模块
    </script>
    

二、组件封装方法

(一)BaseInput组件封装

<!-- components/BaseInput.vue -->
<template>
  <div class="form-group">
    <input
      :type="type"
      :value="modelValue"
      :placeholder="placeholder"
      :disabled="disabled"
      class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary"
      @input="$emit('update:modelValue', $event.target.value)"
    />
  </div>
</template>

<script setup>
import { defineProps, defineEmits } from 'vue';

const props = defineProps({
  modelValue: {
    type: [String, Number],
    required: true,
  },
  type: {
    type: String,
    default: 'text',
  },
  placeholder: {
    type: String,
    default: '',
  },
  disabled: {
    type: Boolean,
    default: false,
  },
});

const emits = defineEmits(['update:modelValue']);
</script>

(二)BaseButton组件封装

<!-- components/BaseButton.vue -->
<template>
  <button
    :type="type"
    :disabled="disabled"
    class="px-4 py-2 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors duration-200"
    @click="handleClick"
  >
    <slot />
  </button>
</template>

<script setup>
import { defineProps, defineEmits } from 'vue';

const props = defineProps({
  type: {
    type: String,
    default: 'button',
  },
  disabled: {
    type: Boolean,
    default: false,
  },
});

const emits = defineEmits(['click']);

const handleClick = (event) => {
  if (!props.disabled) {
    emits('click', event);
  }
};
</script>

三、认证状态管理

(一)创建Auth Store

// stores/auth.ts
import { defineStore } from 'pinia';
import axios from 'axios';

export const useAuthStore = defineStore('auth', {
  state: () => ({
    token: localStorage.getItem('token') || null,
    user: JSON.parse(localStorage.getItem('user') || 'null'),
  }),
  getters: {
    isAuthenticated: (state) => !!state.token,
  },
  actions: {
    async login(username: string, password: string) {
      try {
        const response = await axios.post('/api/auth/login', {
          username,
          password,
        });
        
        const { token, user } = response.data;
        
        // 存储Token和用户信息
        this.token = token;
        this.user = user;
        
        // 保存到localStorage
        localStorage.setItem('token', token);
        localStorage.setItem('user', JSON.stringify(user));
        
        // 设置axios请求头
        axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
        
        return true;
      } catch (error) {
        this.logout();
        throw error;
      }
    },
    
    logout() {
      this.token = null;
      this.user = null;
      localStorage.removeItem('token');
      localStorage.removeItem('user');
      delete axios.defaults.headers.common['Authorization'];
    },
  },
});

(二)创建axios实例

// utils/axios.ts
import axios from 'axios';

const service = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 5000,
});

// 请求拦截器
service.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    console.error('Request error:', error);
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  (response) => {
    return response.data;
  },
  (error) => {
    if (error.response.status === 401) {
      // 处理未授权错误
      localStorage.removeItem('token');
      localStorage.removeItem('user');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

export default service;

四、表单验证增强

(一)添加表单验证

<!-- views/LoginView.vue -->
<template>
  <div class="login-container">
    <div class="login-form">
      <h2>单点登录系统</h2>
      
      <div class="mb-4">
        <BaseInput
          v-model="formData.username"
          placeholder="用户名"
          :error="errors.username"
        />
        <p v-if="errors.username" class="text-red-500 text-sm mt-1">
          {{ errors.username }}
        </p>
      </div>
      
      <div class="mb-6">
        <BaseInput
          v-model="formData.password"
          type="password"
          placeholder="密码"
          :error="errors.password"
        />
        <p v-if="errors.password" class="text-red-500 text-sm mt-1">
          {{ errors.password }}
        </p>
      </div>
      
      <BaseButton @click="handleLogin" :disabled="isLoading">
        {{ isLoading ? '登录中...' : '登录' }}
      </BaseButton>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { useAuthStore } from '@/stores/auth';
import { useRouter } from 'vue-router';

const formData = ref({
  username: '',
  password: '',
});

const errors = ref({
  username: '',
  password: '',
});

const isLoading = ref(false);
const authStore = useAuthStore();
const router = useRouter();

const validateForm = () => {
  let isValid = true;
  
  if (!formData.value.username.trim()) {
    errors.value.username = '请输入用户名';
    isValid = false;
  } else {
    errors.value.username = '';
  }
  
  if (!formData.value.password.trim()) {
    errors.value.password = '请输入密码';
    isValid = false;
  } else if (formData.value.password.length < 6) {
    errors.value.password = '密码长度至少6个字符';
    isValid = false;
  } else {
    errors.value.password = '';
  }
  
  return isValid;
};

const handleLogin = async () => {
  if (!validateForm()) return;
  
  isLoading.value = true;
  
  try {
    await authStore.login(formData.value.username, formData.value.password);
    router.push('/dashboard');
  } catch (error) {
    console.error('Login error:', error);
    errors.value.common = error.message || '登录失败,请重试';
  } finally {
    isLoading.value = false;
  }
};
</script>

五、完整使用示例

(一)在主应用中集成

// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import { createPinia } from 'pinia';

const app = createApp(App);

app.use(createPinia());
app.use(router);

app.mount('#app');

(二)路由守卫配置

// router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import LoginView from '../views/LoginView.vue';
import DashboardView from '../views/DashboardView.vue';
import { useAuthStore } from '../stores/auth';

const routes = [
  {
    path: '/login',
    name: 'Login',
    component: LoginView,
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: DashboardView,
    meta: { requiresAuth: true },
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

// 全局前置守卫
router.beforeEach((to, from, next) => {
  const authStore = useAuthStore();
  
  if (to.meta.requiresAuth && !authStore.isAuthenticated) {
    next({ name: 'Login' });
  } else {
    next();
  }
});

export default router;

六、总结

通过以上方法,你可以:

  1. 使用单点登录组件:简单集成Login组件到你的应用中
  2. 自定义基础组件:根据设计需求修改BaseButton和BaseInput组件样式
  3. 管理认证状态:使用Pinia store管理用户认证状态
  4. 增强表单验证:添加更完善的表单验证逻辑
  5. 保护路由:使用路由守卫保护需要认证的页面

这些组件和方法可以作为企业级应用单点登录功能的基础,根据实际需求可以进一步扩展和优化。


代码获取方式

【夸克网盘】点击查看


关注我获取更多内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值