代码教程
Vue单点登录组件使用指南与封装方法
一、基础使用方法
(一)前置条件
-
确保项目已安装以下依赖:
npm install vue-router pinia axios
-
项目中已创建以下文件结构:
src/ ├── stores/ │ └── auth.ts # 认证状态管理 ├── router/ │ └── index.ts # 路由配置 ├── components/ │ ├── BaseButton.vue # 基础按钮组件 │ └── BaseInput.vue # 基础输入组件 └── views/ └── LoginView.vue # 登录视图
(二)使用示例
-
在路由配置中添加登录页面:
// 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;
-
在主应用中使用:
<!-- 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;
六、总结
通过以上方法,你可以:
- 使用单点登录组件:简单集成Login组件到你的应用中
- 自定义基础组件:根据设计需求修改BaseButton和BaseInput组件样式
- 管理认证状态:使用Pinia store管理用户认证状态
- 增强表单验证:添加更完善的表单验证逻辑
- 保护路由:使用路由守卫保护需要认证的页面
这些组件和方法可以作为企业级应用单点登录功能的基础,根据实际需求可以进一步扩展和优化。
代码获取方式
关注我获取更多内容