From a05368d30602d3073eb77023da406077a79604b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Fri, 27 Mar 2026 14:40:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=99=BB=E5=BD=95):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=B7=AF=E7=94=B1=E5=AE=88=E5=8D=AB=E5=92=8C=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=E5=AF=BC=E8=88=AA=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(表单验证): 为用户、角色和菜单管理添加表单验证规则 test(e2e): 增加页面导航超时时间和网络空闲等待 refactor(数据库): 移除Flyway配置并更新数据源配置 --- .../src/main/resources/application.yml | 7 +++ .../manage/db/config/FlywayConfig.java | 30 ----------- novalon-manage-web/e2e/pages/DashboardPage.ts | 48 ++++++++++------- novalon-manage-web/e2e/pages/LoginPage.ts | 2 +- novalon-manage-web/src/router/index.ts | 23 ++++++++ novalon-manage-web/src/views/system/Login.vue | 3 +- .../src/views/system/MenuManagement.vue | 52 +++++++++++++++++-- .../src/views/system/RoleManagement.vue | 30 ++++++++++- .../src/views/system/UserManagement.vue | 31 ++++++++++- 9 files changed, 168 insertions(+), 58 deletions(-) delete mode 100644 novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/config/FlywayConfig.java diff --git a/novalon-manage-api/manage-app/src/main/resources/application.yml b/novalon-manage-api/manage-app/src/main/resources/application.yml index cb49c4f..6f39f49 100644 --- a/novalon-manage-api/manage-app/src/main/resources/application.yml +++ b/novalon-manage-api/manage-app/src/main/resources/application.yml @@ -14,6 +14,13 @@ spring: max-idle-time: 30m max-life-time: 1h acquire-timeout: 5s + datasource: + url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:55432}/${DB_NAME:manage_system} + username: ${DB_USERNAME:postgres} + password: ${DB_PASSWORD:postgres} + driver-class-name: org.postgresql.Driver + flyway: + enabled: false security: user: name: disabled diff --git a/novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/config/FlywayConfig.java b/novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/config/FlywayConfig.java deleted file mode 100644 index 9ff7273..0000000 --- a/novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/config/FlywayConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.novalon.manage.db.config; - -import org.flywaydb.core.Flyway; -import org.springframework.boot.autoconfigure.flyway.FlywayProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; - -import javax.sql.DataSource; - -@Configuration -public class FlywayConfig { - - @Bean - @Profile("!test") - public Flyway flyway(DataSource dataSource, FlywayProperties flywayProperties) { - Flyway flyway = Flyway.configure() - .dataSource(dataSource) - .locations(flywayProperties.getLocations().toArray(new String[0])) - .baselineOnMigrate(true) - .baselineVersion("0") - .table("flyway_schema_history") - .validateOnMigrate(true) - .outOfOrder(false) - .load(); - - flyway.migrate(); - return flyway; - } -} \ No newline at end of file diff --git a/novalon-manage-web/e2e/pages/DashboardPage.ts b/novalon-manage-web/e2e/pages/DashboardPage.ts index c690474..08c0a0f 100644 --- a/novalon-manage-web/e2e/pages/DashboardPage.ts +++ b/novalon-manage-web/e2e/pages/DashboardPage.ts @@ -35,83 +35,93 @@ export class DashboardPage { async navigateToUserManagement() { const systemMenu = this.page.locator('.el-sub-menu__title:has-text("系统管理")'); await systemMenu.click(); - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(1000); await this.userManagementLink.click(); - await this.page.waitForURL('**/users', { timeout: 10000 }); + await this.page.waitForURL('**/users', { timeout: 30000 }); + await this.page.waitForLoadState('networkidle'); } async navigateToRoleManagement() { const systemMenu = this.page.locator('.el-sub-menu__title:has-text("系统管理")'); await systemMenu.click(); - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(1000); await this.roleManagementLink.click(); - await this.page.waitForURL('**/roles', { timeout: 10000 }); + await this.page.waitForURL('**/roles', { timeout: 30000 }); + await this.page.waitForLoadState('networkidle'); } async navigateToMenuManagement() { const systemMenu = this.page.locator('.el-sub-menu__title:has-text("系统管理")'); await systemMenu.click(); - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(1000); await this.menuManagementLink.click(); - await this.page.waitForURL('**/menus', { timeout: 10000 }); + await this.page.waitForURL('**/menus', { timeout: 30000 }); + await this.page.waitForLoadState('networkidle'); } async navigateToSystemConfig() { const configMenu = this.page.locator('.el-sub-menu__title:has-text("系统配置")'); await configMenu.click(); - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(1000); await this.systemConfigLink.click(); - await this.page.waitForURL('**/sys/config', { timeout: 10000 }); + await this.page.waitForURL('**/sys/config', { timeout: 30000 }); + await this.page.waitForLoadState('networkidle'); } async navigateToNoticeManagement() { const notifyMenu = this.page.locator('.el-sub-menu__title:has-text("通知中心")'); await notifyMenu.click(); - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(1000); await this.noticeManagementLink.click(); - await this.page.waitForURL('**/notice', { timeout: 10000 }); + await this.page.waitForURL('**/notice', { timeout: 30000 }); + await this.page.waitForLoadState('networkidle'); } async navigateToFileManagement() { const fileMenu = this.page.locator('.el-sub-menu__title:has-text("文件管理")'); await fileMenu.click(); - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(1000); await this.fileManagementLink.click(); - await this.page.waitForURL('**/files', { timeout: 10000 }); + await this.page.waitForURL('**/files', { timeout: 30000 }); + await this.page.waitForLoadState('networkidle'); } async navigateToAudit() { const auditMenu = this.page.locator('.el-sub-menu__title:has-text("审计中心")'); await auditMenu.click(); - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(1000); } async navigateToOperationLog() { await this.navigateToAudit(); await this.operationLogLink.click(); - await this.page.waitForURL('**/oplog', { timeout: 10000 }); + await this.page.waitForURL('**/oplog', { timeout: 30000 }); + await this.page.waitForLoadState('networkidle'); } async navigateToLoginLog() { await this.navigateToAudit(); await this.loginLogLink.click(); - await this.page.waitForURL('**/loginlog', { timeout: 10000 }); + await this.page.waitForURL('**/loginlog', { timeout: 30000 }); + await this.page.waitForLoadState('networkidle'); } async navigateToNotification() { const notifyMenu = this.page.locator('.el-sub-menu__title:has-text("通知中心")'); await notifyMenu.click(); - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(1000); await this.noticeManagementLink.click(); - await this.page.waitForURL('**/notification', { timeout: 10000 }); + await this.page.waitForURL('**/notification', { timeout: 30000 }); + await this.page.waitForLoadState('networkidle'); } async navigateToDictionary() { const configMenu = this.page.locator('.el-sub-menu__title:has-text("系统配置")'); await configMenu.click(); - await this.page.waitForTimeout(500); + await this.page.waitForTimeout(1000); await this.dictionaryLink.click(); - await this.page.waitForURL('**/dict', { timeout: 10000 }); + await this.page.waitForURL('**/dict', { timeout: 30000 }); + await this.page.waitForLoadState('networkidle'); } async getUsername(): Promise { diff --git a/novalon-manage-web/e2e/pages/LoginPage.ts b/novalon-manage-web/e2e/pages/LoginPage.ts index 8dc2a9b..54eee43 100644 --- a/novalon-manage-web/e2e/pages/LoginPage.ts +++ b/novalon-manage-web/e2e/pages/LoginPage.ts @@ -31,7 +31,7 @@ export class LoginPage { console.log('Clicked login button'); try { - await this.page.waitForURL('**/dashboard', { timeout: 10000 }); + await this.page.waitForURL('**/dashboard', { timeout: 30000 }); console.log('Successfully navigated to dashboard'); await this.page.waitForLoadState('networkidle'); console.log('Network idle achieved'); diff --git a/novalon-manage-web/src/router/index.ts b/novalon-manage-web/src/router/index.ts index bef31fd..a5d13cf 100644 --- a/novalon-manage-web/src/router/index.ts +++ b/novalon-manage-web/src/router/index.ts @@ -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 diff --git a/novalon-manage-web/src/views/system/Login.vue b/novalon-manage-web/src/views/system/Login.vue index ecbdffb..0c551bb 100644 --- a/novalon-manage-web/src/views/system/Login.vue +++ b/novalon-manage-web/src/views/system/Login.vue @@ -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 { diff --git a/novalon-manage-web/src/views/system/MenuManagement.vue b/novalon-manage-web/src/views/system/MenuManagement.vue index 0add316..4a38e5b 100644 --- a/novalon-manage-web/src/views/system/MenuManagement.vue +++ b/novalon-manage-web/src/views/system/MenuManagement.vue @@ -92,10 +92,15 @@ width="500px" > - + @@ -107,7 +112,10 @@ check-strictly /> - + - + @@ -176,10 +189,33 @@ const dataSource = ref([]) const menuTree = ref([]) 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('操作失败') + } } } diff --git a/novalon-manage-web/src/views/system/RoleManagement.vue b/novalon-manage-web/src/views/system/RoleManagement.vue index c91bf43..4b61376 100644 --- a/novalon-manage-web/src/views/system/RoleManagement.vue +++ b/novalon-manage-web/src/views/system/RoleManagement.vue @@ -128,17 +128,21 @@ width="500px" > ({ roleName: '', roleKey: '', @@ -243,6 +249,22 @@ const formState = reactive([]) @@ -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) + } } } diff --git a/novalon-manage-web/src/views/system/UserManagement.vue b/novalon-manage-web/src/views/system/UserManagement.vue index 1df8ed4..37e60d2 100644 --- a/novalon-manage-web/src/views/system/UserManagement.vue +++ b/novalon-manage-web/src/views/system/UserManagement.vue @@ -133,11 +133,14 @@ width="500px" > ({ username: '', password: '', @@ -251,6 +256,24 @@ const formState = reactive([]) 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) + } } }