好的,我来为您创建一个基于Vue和Element UI的CMS管理系统的登录页面。我们将使用Vue 3和Element Plus(Element UI的Vue 3版本)来构建这个页面。
首先,让我们创建登录页面组件:
src/views/Login.vue
这个Vue组件创建了一个简洁、现代的登录页面,具有以下特点:
el-card、el-form、el-input和el-button组件构建界面。setup 语法糖)编写逻辑代码。:deep())来自定义Element Plus组件的样式。要使用这个登录页面,你需要在你的Vue路由配置中添加这个组件。例如:
import { createRouter, createWebHistory } from 'vue-router'
import Login from '@/views/Login.vue'
const routes = [
{
path: '/login',
name: 'Login',
component: Login
},
// ... 其他路由
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router同时,确保你的项目中已经正确安装和配置了Element Plus。你可以在main.js中全局引入Element Plus:
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(ElementPlus)
app.use(router)
app.mount('#app')这个登录页面提供了一个良好的起点。在实际的CMS系统中,你还需要:
如果你需要进一步的帮助,比如实现其他CMS功能或页面,请随时告诉我。
非常好,我们来逐步实现这些功能。我们将使用Pinia作为状态管理库,因为它是Vue 3的推荐选择。
首先,让我们设置API调用、Pinia存储、路由守卫,然后更新登录组件以包含记住密码和忘记密码功能。
// src/services/api.ts
import axios from 'axios'
const api = axios.create({
baseURL: 'https://api.example.com', // 替换为你的实际API地址
timeout: 5000,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
export const login = async (username: string, password: string) => {
const response = await api.post('/login', { username, password })
return response.data
}
export const forgotPassword = async (email: string) => {
const response = await api.post('/forgot-password', { email })
return response.data
}
export default api// src/stores/auth.ts
import { defineStore } from 'pinia'
import { login } from '@/services/api'
interface User {
id: number
username: string
email: string
}
export const useAuthStore = defineStore('auth', {
state: () => ({
user: null as User | null,
token: null as string | null,
}),
getters: {
isAuthenticated: (state) => !!state.token,
},
actions: {
async login(username: string, password: string) {
const { user, token } = await login(username, password)
this.user = user
this.token = token
localStorage.setItem('token', token)
},
logout() {
this.user = null
this.token = null
localStorage.removeItem('token')
},
initializeAuth() {
const token = localStorage.getItem('token')
if (token) {
this.token = token
// 这里你可能需要调用一个API来获取用户信息
}
},
},
})// src/router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import Login from '@/views/Login.vue'
import Dashboard from '@/views/Dashboard.vue'
const routes: Array<RouteRecordRaw> = [
{
path: '/login',
name: 'Login',
component: Login,
meta: { requiresAuth: false }
},
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard,
meta: { requiresAuth: true }
},
// 添加其他路由...
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
router.beforeEach((to, from, next) => {
const authStore = useAuthStore()
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!authStore.isAuthenticated) {
next({ name: 'Login', query: { redirect: to.fullPath } })
} else {
next()
}
} else {
next()
}
})
export default router<!-- src/views/Login.vue -->
<template>
<div class="login-container">
<el-card class="login-card">
<template #header>
<h2 class="login-title">CMS 登录</h2>
</template>
<el-form :model="loginForm" :rules="rules" ref="loginFormRef" @submit.prevent="handleLogin">
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
placeholder="用户名"
prefix-icon="User"
/>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
placeholder="密码"
prefix-icon="Lock"
show-password
/>
</el-form-item>
<el-form-item>
<el-checkbox v-model="loginForm.rememberMe">记住我</el-checkbox>
<el-button type="text" @click="showForgotPasswordDialog" class="forgot-password">
忘记密码?
</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" native-type="submit" :loading="loading" class="login-button">
{{ loading ? '登录中...' : '登录' }}
</el-button>
</el-form-item>
</el-form>
</el-card>
<el-dialog v-model="forgotPasswordVisible" title="忘记密码" width="30%">
<el-form :model="forgotPasswordForm" :rules="forgotPasswordRules" ref="forgotPasswordFormRef">
<el-form-item prop="email">
<el-input v-model="forgotPasswordForm.email" placeholder="请输入您的邮箱" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="forgotPasswordVisible = false">取消</el-button>
<el-button type="primary" @click="handleForgotPassword" :loading="forgotPasswordLoading">
提交
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { User, Lock } from '@element-plus/icons-vue'
import { useRouter, useRoute } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { forgotPassword } from '@/services/api'
const router = useRouter()
const route = useRoute()
const authStore = useAuthStore()
const loginFormRef = ref(null)
const forgotPasswordFormRef = ref(null)
const loading = ref(false)
const forgotPasswordLoading = ref(false)
const forgotPasswordVisible = ref(false)
const loginForm = reactive({
username: '',
password: '',
rememberMe: false
})
const forgotPasswordForm = reactive({
email: ''
})
const rules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
]
}
const forgotPasswordRules = {
email: [
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
]
}
const handleLogin = () => {
loginFormRef.value.validate(async (valid) => {
if (valid) {
loading.value = true
try {
await authStore.login(loginForm.username, loginForm.password)
if (loginForm.rememberMe) {
localStorage.setItem('rememberedUser', loginForm.username)
} else {
localStorage.removeItem('rememberedUser')
}
ElMessage.success('登录成功')
const redirectPath = route.query.redirect as string || '/dashboard'
router.push(redirectPath)
} catch (error) {
ElMessage.error('登录失败,请检查用户名和密码')
} finally {
loading.value = false
}
} else {
ElMessage.error('请正确填写登录信息')
}
})
}
const showForgotPasswordDialog = () => {
forgotPasswordVisible.value = true
}
const handleForgotPassword = () => {
forgotPasswordFormRef.value.validate(async (valid) => {
if (valid) {
forgotPasswordLoading.value = true
try {
await forgotPassword(forgotPasswordForm.email)
ElMessage.success('重置密码链接已发送到您的邮箱')
forgotPasswordVisible.value = false
} catch (error) {
ElMessage.error('发送重置密码链接失败,请稍后重试')
} finally {
forgotPasswordLoading.value = false
}
}
})
}
// 检查是否有记住的用户名
const rememberedUser = localStorage.getItem('rememberedUser')
if (rememberedUser) {
loginForm.username = rememberedUser
loginForm.rememberMe = true
}
</script>
<style scoped>
/* ... 保持原有的样式 ... */
.forgot-password {
float: right;
}
:deep(.el-checkbox__label) {
color: #ffffff;
}
</style>// src/main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
import { useAuthStore } from './stores/auth'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.use(ElementPlus)
app.use(router)
const authStore = useAuthStore()
authStore.initializeAuth()
app.mount('#app')这些更改实现了以下功能:
请注意,这个实现假设你的后端API已经准备好处理这些请求。你可能需要根据你的实际API调整api.ts文件中的请求。
此外,为了增强安全性,你可能还想考虑以下几点:
如果你需要进一步的帮助或者想要实现CMS的其他功能,请随时告诉我。