diff --git a/src/api/types/menu.d.ts b/src/api/types/menu.d.ts
index 5267ce1..aac6430 100644
--- a/src/api/types/menu.d.ts
+++ b/src/api/types/menu.d.ts
@@ -11,7 +11,7 @@ export interface MenuFormParams {
type: number; // 类型:1菜单2按钮(number类型)
status?: number; // 状态:1启用2禁用(可选,编辑时用)
id?: number; // 菜单ID(仅编辑/删除时传)
- parent_id?: number | null; // 上级菜单ID(number类型)
+ parent_id?: string | null; // 上级菜单ID(number类型)
}
/** 删除菜单参数类型 */
diff --git a/src/views/system/menuManagement/index.vue b/src/views/system/menuManagement/index.vue
index 0afe24f..a952cb6 100644
--- a/src/views/system/menuManagement/index.vue
+++ b/src/views/system/menuManagement/index.vue
@@ -56,7 +56,7 @@
-
+
@@ -67,10 +67,21 @@
-
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
-
+
@@ -81,7 +92,7 @@
- 取消
+ { { dialogVisible = false, dialogType = 'add' } }">取消
确定
@@ -92,61 +103,9 @@ import type { FormInstance, FormRules } from 'element-plus'
import { Tools, List, HomeFilled, UserFilled, Platform, BellFilled, GoodsFilled, WarningFilled, Plus } from '@element-plus/icons-vue'
import { getMenuApi, addMenuApi, deleteMenuApi, editMenuApi } from '@/api/modules/menu'
+import { rules } from './rules'
-//menu数据
-const menuTree = ref([])
-
-// 定义菜单表单数据
-const menuFormRef = ref()
-// 核心:表单验证规则
-const menuFormRules = reactive({
- // 1. 菜单名称:必填 + 长度限制
- name: [
- { required: true, message: '请输入菜单名称', trigger: 'blur' },
- { min: 2, max: 20, message: '菜单名称长度在 2 到 20 个字符之间', trigger: 'blur' }
- ],
- // 2. 父级菜单:必填(默认0,无需手动填)
- parentId: [{ required: true, message: '请选择父级菜单', trigger: 'change' }
- ],
- // 3. 路由路径:必填 + 格式校验(以/开头,仅包含字母/数字/下划线/斜杠)
- path: [
- { required: true, message: '请输入路由路径', trigger: 'blur' },
- { pattern: /^\/[a-zA-Z0-9_\/]+$/, message: '路由路径必须以/开头,仅包含字母、数字、下划线和斜杠', trigger: 'blur' }
- ],
- // 4. 后端路由:可选,但填了就必须符合格式
- api: [
- { required: true, pattern: /(^$)|(^\/[a-zA-Z0-9_\/]+$)/, message: '后端路由若填写,必须以/开头,仅包含字母、数字、下划线和斜杠', trigger: 'blur' }
- ],
- // 5. 图标名称:可选,但填了就必须是指定范围(匹配你提供的图标列表)
- icon: [
- {
- required: true,
- pattern: /(^$)|(^Tools$|^List$|^HomeFilled$|^UserFilled$|^Platform$|^BellFilled$|^GoodsFilled$|^WarningFilled$)/
- , message: '一级菜单有图标二级菜单没有', trigger: 'blur'
- }
- ],
- // 6. 排序:必填 + 数字 + 最小值0
- sort: [
- { required: true, message: '请输入排序值', trigger: 'blur' },
- { type: 'number', min: 0, message: '排序值必须是大于等于0的数字', trigger: 'blur' }
- ],
- // 7. 菜单类型:必填(单选框默认1,无需手动选)
- type: [
- { required: true, message: '请选择菜单类型', trigger: 'change' },
- { type: 'number', enum: [1, 2], message: '菜单类型只能是1(菜单)或2(按钮)', trigger: 'change' }
- ]
-})
-const menuForm = ref({
- name: '', // 菜单名称
- parentId: '0', // 默认一级菜单
- path: '', // 路由路径
- api: '', // 后端路由
- icon: '', // 图标名称
- sort: 1, // 排序(默认1)
- type: 1 // 默认是菜单类型
-})
-
-// 定义图标映射表(和侧边栏逻辑一致)
+// 定义图标映射表和选择图标选项列表
const iconMap: Record = {
Tools,
List,
@@ -157,6 +116,36 @@ const iconMap: Record = {
GoodsFilled,
WarningFilled
}
+// 图标配置(可随时扩展)
+const iconOptions = [
+ { label: "无图标", value: "无图标" },
+ { label: "Tools(工具)", value: "Tools", icon: Tools },
+ { label: "List(列表)", value: "List", icon: List },
+ { label: "HomeFilled(首页)", value: "HomeFilled", icon: HomeFilled },
+ { label: "UserFilled(用户)", value: "UserFilled", icon: UserFilled },
+ { label: "Platform(平台)", value: "Platform", icon: Platform },
+ { label: "BellFilled(铃铛)", value: "BellFilled", icon: BellFilled },
+ { label: "GoodsFilled(商品)", value: "GoodsFilled", icon: GoodsFilled },
+ { label: "WarningFilled(警告)", value: "WarningFilled", icon: WarningFilled },
+]
+
+//menu数据
+const menuTree = ref([])
+
+// 定义菜单表单数据
+const menuForm = ref({
+ name: '', // 菜单名称
+ parentId: '0', // 默认一级菜单
+ path: '', // 路由路径
+ api: '', // 后端路由
+ icon: '', // 图标名称
+ sort: 1, // 排序(默认1)
+ type: 1 // 默认是菜单类型
+})
+const menuFormRef = ref()
+// 核心:表单验证规则
+const menuFormRules = rules(menuForm.value, iconOptions)
+
// 定义弹窗显示状态
const dialogVisible = ref(false)
// 新增:区分新增/编辑模式
@@ -172,7 +161,7 @@ onMounted(async () => {
const res = await getMenuApi()
menuTree.value = filterEnabledMenus(res)
} catch (err) {
- ElMessage.error('获取菜单列表失败')
+ console.error('获取菜单列表失败')
}
})
@@ -200,6 +189,7 @@ const handleAddMenu = () => {
type: 1
}
dialogVisible.value = true
+ console.log('dialogType.value', dialogType.value);
}
//打开编辑弹窗
@@ -209,7 +199,7 @@ const handleEditMenu = (row: any) => {
menuForm.value = {
name: row.name,
- parentId: row.parentId,
+ parentId: row.parent_id,
path: row.path,
api: row.api,
icon: row.icon,
@@ -228,6 +218,8 @@ const handleSubmitMenu = async () => {
...menuForm.value,
parent_id: menuForm.value.parentId
})
+ const res = await getMenuApi()
+ menuTree.value = filterEnabledMenus(res)
} else {
await editMenuApi({
id: currentEditId.value || 0,
@@ -235,6 +227,8 @@ const handleSubmitMenu = async () => {
parent_id: menuForm.value.parentId,
...menuForm.value
})
+ const res = await getMenuApi()
+ menuTree.value = filterEnabledMenus(res)
}
dialogVisible.value = false
} catch (err) {
@@ -246,6 +240,8 @@ const handleSubmitMenu = async () => {
const handleDeleteMenu = async (row: any) => {
try {
await deleteMenuApi({ id: row.id })
+ const res = await getMenuApi()
+ menuTree.value = filterEnabledMenus(res)
} catch (err) {
console.log('删除菜单失败', err)
}
diff --git a/src/views/system/menuManagement/mock.ts b/src/views/system/menuManagement/mock.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/src/views/system/menuManagement/rules.ts b/src/views/system/menuManagement/rules.ts
new file mode 100644
index 0000000..6b03c27
--- /dev/null
+++ b/src/views/system/menuManagement/rules.ts
@@ -0,0 +1,55 @@
+import type { FormRules } from 'element-plus'
+
+export const rules = (menuForm: any, iconOptions: any[]): FormRules => {
+ return {
+ // 1. 菜单名称:必填 + 长度限制
+ name: [
+ { required: true, message: '请输入菜单名称', trigger: 'blur' },
+ { min: 2, max: 20, message: '菜单名称长度在 2 到 20 个字符之间', trigger: 'blur' }
+ ],
+ // 2. 父级菜单:必填(默认0,无需手动填)
+ parentId: [{ required: true, message: '请选择父级菜单', trigger: 'change' }
+ ],
+ // 3. 路由路径:必填 + 格式校验(以/开头,仅包含字母/数字/下划线/斜杠)
+ path: [
+ { required: true, message: '请输入路由路径', trigger: 'blur' },
+ { pattern: /^\/[a-zA-Z0-9_\/]+$/, message: '路由路径必须以/开头,仅包含字母、数字、下划线和斜杠', trigger: 'blur' }
+ ],
+ // 4. 后端路由:可选,但填了就必须符合格式
+ api: [
+ { required: true, pattern: /(^$)|(^\/[a-zA-Z0-9_\/]+$)/, message: '后端路由若填写,必须以/开头,仅包含字母、数字、下划线和斜杠', trigger: 'blur' }
+ ],
+ // 5. 图标名称:可选,但填了就必须是指定范围(匹配你提供的图标列表)
+ icon: [
+ {
+ validator: (rule, value, callback) => {
+ // 一级菜单必须选图标
+ if (menuForm.parentId === "0" && !value) {
+ callback(new Error("一级菜单必须选择图标"))
+ }
+ // 若填写了图标,必须是指定范围内的
+ else if (
+ value &&
+ !iconOptions.some(item => item.value === value)
+ ) {
+ callback(new Error("请选择预设图标"))
+ }
+ else {
+ callback()
+ }
+ },
+ trigger: "change",
+ },
+ ],
+ // 6. 排序:必填 + 数字 + 最小值0
+ sort: [
+ { required: true, message: '请输入排序值', trigger: 'blur' },
+ { type: 'number', min: 0, message: '排序值必须是大于等于0的数字', trigger: 'blur' }
+ ],
+ // 7. 菜单类型:必填(单选框默认1,无需手动选)
+ type: [
+ { required: true, message: '请选择菜单类型', trigger: 'change' },
+ { type: 'number', enum: [1, 2], message: '菜单类型只能是1(菜单)或2(按钮)', trigger: 'change' }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/views/user/ForgotPassword.vue b/src/views/user/ForgotPassword.vue
index 0fa7b4b..f3c4e2c 100644
--- a/src/views/user/ForgotPassword.vue
+++ b/src/views/user/ForgotPassword.vue
@@ -156,7 +156,7 @@ const sendCodeApi = async () => {
const sendParams = {
email: forgotPasswordForm.value.email,
mobile: forgotPasswordForm.value.phone,
- reg_type: activeTab.value === 'email' ? 1 : 2,
+ reg_type: getStorage('forgotType', 'session') === 'email' ? 1 : 2,
type:'transfer_code'
}
return userStore.sendCode(sendParams)
@@ -166,10 +166,9 @@ const sendCodeApi = async () => {
const handleSendCode = async () => {
// 第二步:确定类型、接收方、对应的倒计时状态和发送函数
const type: string = getStorage('forgotType', 'session') || 'email'
+
const receiver = type === 'phone' ? forgotPasswordForm.value.phone : forgotPasswordForm.value.email;
const currentCaptchaState = type === 'phone' ? phoneCaptchaState : emailCaptchaState;
- const sendFunc = type === 'phone' ? "sendPhoneCaptcha" : "sendEmailCaptcha";
-
// 第三步:调用封装的发送函数(传正确的函数,而非字符串)
await sendCaptcha(
type,