feat(登录): 添加路由守卫和异步导航处理

fix(表单验证): 为用户、角色和菜单管理添加表单验证规则

test(e2e): 增加页面导航超时时间和网络空闲等待

refactor(数据库): 移除Flyway配置并更新数据源配置
This commit is contained in:
张翔
2026-03-27 14:40:55 +08:00
parent af44c23f21
commit a05368d306
9 changed files with 168 additions and 58 deletions
+23
View File
@@ -76,4 +76,27 @@ const router = createRouter({
routes
})
router.beforeEach((to, from, next) => {
try {
const token = localStorage.getItem('token')
if (to.path === '/login') {
if (token) {
next('/')
} else {
next()
}
} else {
if (token) {
next()
} else {
next('/login')
}
}
} catch (error) {
console.error('路由守卫错误:', error)
next('/login')
}
})
export default router
@@ -77,7 +77,8 @@ const onFinish = async () => {
localStorage.setItem('userId', res.userId)
localStorage.setItem('username', res.username)
ElMessage.success('登录成功')
router.push('/')
await router.push('/')
} catch (error: any) {
ElMessage.error(error.response?.data?.message || '登录失败')
} finally {
@@ -92,10 +92,15 @@
width="500px"
>
<el-form
ref="formRef"
:model="formState"
:rules="formRules"
label-width="100px"
>
<el-form-item label="菜单名称">
<el-form-item
label="菜单名称"
prop="menuName"
>
<el-input v-model="formState.menuName" />
</el-form-item>
<el-form-item label="父级菜单">
@@ -107,7 +112,10 @@
check-strictly
/>
</el-form-item>
<el-form-item label="菜单类型">
<el-form-item
label="菜单类型"
prop="menuType"
>
<el-select v-model="formState.menuType">
<el-option
value="M"
@@ -126,16 +134,21 @@
<el-form-item
v-if="formState.menuType !== 'F'"
label="路由地址"
prop="perms"
>
<el-input v-model="formState.perms" />
</el-form-item>
<el-form-item
v-if="formState.menuType === 'C'"
label="组件路径"
prop="component"
>
<el-input v-model="formState.component" />
</el-form-item>
<el-form-item label="排序">
<el-form-item
label="排序"
prop="orderNum"
>
<el-input-number v-model="formState.orderNum" />
</el-form-item>
<el-form-item label="状态">
@@ -176,10 +189,33 @@ const dataSource = ref([])
const menuTree = ref<any[]>([])
const modalVisible = ref(false)
const modalTitle = ref('')
const formRef = ref()
const formState = reactive({
id: null, menuName: '', parentId: 0, menuType: 'C', perms: '', component: '', orderNum: 0, status: '0'
})
const formRules = {
menuName: [
{ required: true, message: '请输入菜单名称', trigger: 'blur' },
{ min: 2, max: 50, message: '菜单名称长度在 2 到 50 个字符', trigger: 'blur' }
],
menuType: [
{ required: true, message: '请选择菜单类型', trigger: 'change' }
],
perms: [
{ required: true, message: '请输入路由地址', trigger: 'blur' },
{ pattern: /^\/[a-zA-Z0-9/_-]*$/, message: '路由地址格式不正确,应以/开头', trigger: 'blur' }
],
component: [
{ required: true, message: '请输入组件路径', trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9/-]+$/, message: '组件路径格式不正确', trigger: 'blur' }
],
orderNum: [
{ required: true, message: '请输入排序', trigger: 'blur' },
{ type: 'number', min: 0, message: '排序必须大于等于0', trigger: 'blur' }
]
}
const fetchData = async () => {
loading.value = true
try {
@@ -223,7 +259,11 @@ const handleDelete = async (row: any) => {
}
const handleModalOk = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
if (formState.id) {
await request.put(`/menus/${formState.id}`, formState)
} else {
@@ -232,8 +272,10 @@ const handleModalOk = async () => {
ElMessage.success('操作成功')
modalVisible.value = false
fetchData()
} catch {
ElMessage.error('操作失败')
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('操作失败')
}
}
}
@@ -128,17 +128,21 @@
width="500px"
>
<el-form
ref="formRef"
:model="formState"
:rules="formRules"
label-width="80px"
>
<el-form-item
label="角色名称"
prop="roleName"
required
>
<el-input v-model="formState.roleName" />
</el-form-item>
<el-form-item
label="角色标识"
prop="roleKey"
required
>
<el-input
@@ -148,6 +152,7 @@
</el-form-item>
<el-form-item
label="显示顺序"
prop="roleSort"
required
>
<el-input-number
@@ -235,6 +240,7 @@ const sortInfo = reactive({
const modalVisible = ref(false)
const modalTitle = ref('')
const formRef = ref()
const formState = reactive<CreateRoleRequest & { id?: number; status?: RoleStatus }>({
roleName: '',
roleKey: '',
@@ -243,6 +249,22 @@ const formState = reactive<CreateRoleRequest & { id?: number; status?: RoleStatu
status: RoleStatus.ACTIVE
})
const formRules = {
roleName: [
{ required: true, message: '请输入角色名称', trigger: 'blur' },
{ min: 2, max: 50, message: '角色名称长度在 2 到 50 个字符', trigger: 'blur' }
],
roleKey: [
{ required: true, message: '请输入角色标识', trigger: 'blur' },
{ min: 2, max: 50, message: '角色标识长度在 2 到 50 个字符', trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9_-]+$/, message: '角色标识只能包含字母、数字、下划线和横线', trigger: 'blur' }
],
roleSort: [
{ required: true, message: '请输入显示顺序', trigger: 'blur' },
{ type: 'number', min: 1, message: '显示顺序必须大于0', trigger: 'blur' }
]
}
const permissionDialogVisible = ref(false)
const permissionTreeRef = ref()
const permissionTree = ref<any[]>([])
@@ -332,7 +354,11 @@ const handleDelete = async (row: Role) => {
}
const handleModalOk = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
if (formState.id) {
const updateData: UpdateRoleRequest = {
roleName: formState.roleName,
@@ -355,7 +381,9 @@ const handleModalOk = async () => {
modalVisible.value = false
fetchData()
} catch (error) {
handleApiError(error)
if (error !== 'cancel') {
handleApiError(error)
}
}
}
@@ -133,11 +133,14 @@
width="500px"
>
<el-form
ref="formRef"
:model="formState"
:rules="formRules"
label-width="80px"
>
<el-form-item
label="用户名"
prop="username"
required
>
<el-input
@@ -148,6 +151,7 @@
<el-form-item
v-if="!formState.id"
label="密码"
prop="password"
required
>
<el-input
@@ -241,6 +245,7 @@ const sortInfo = reactive({
const modalVisible = ref(false)
const modalTitle = ref('')
const formRef = ref()
const formState = reactive<CreateUserRequest & { id?: number; status?: UserStatus }>({
username: '',
password: '',
@@ -251,6 +256,24 @@ const formState = reactive<CreateUserRequest & { id?: number; status?: UserStatu
status: UserStatus.ACTIVE
})
const formRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '用户名长度在 3 到 20 个字符', trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9_]+$/, message: '用户名只能包含字母、数字和下划线', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 20, message: '密码长度在 6 到 20 个字符', trigger: 'blur' }
],
email: [
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
],
phone: [
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
]
}
const roleDialogVisible = ref(false)
const selectedRoles = ref<number[]>([])
const allRoles = ref<{ key: number; label: string }[]>([])
@@ -342,7 +365,11 @@ const handleDelete = async (row: User) => {
}
const handleModalOk = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
if (formState.id) {
const updateData: UpdateUserRequest = {
nickname: formState.nickname,
@@ -367,7 +394,9 @@ const handleModalOk = async () => {
modalVisible.value = false
fetchData()
} catch (error) {
handleApiError(error)
if (error !== 'cancel') {
handleApiError(error)
}
}
}