Vue 菜单管理,全选 半选 el-checkbox-group el-checkbox indeterminate

注意:indeterminate = false ,表示没被选中,提交的时候没有值

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>权限管理系统</title>
  <link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css">
  <script src="https://unpkg.com/vue@3"></script>
  <script src="https://unpkg.com/element-plus"></script>
  <style>
    .menu-container {
      max-width: 800px;
      margin: 20px auto;
      padding: 20px;
      background: #fff;
      border-radius: 8px;
      box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
    }
    .menu-row {
      margin-bottom: 20px;
      border-bottom: 1px solid #eee;
      padding-bottom: 15px;
    }
    .menu-row:last-child {
      border-bottom: none;
    }
    .menu-col {
      /*display: flex;*/
      /*flex-direction: column;*/
    }
    .menu-sub {
      margin: 10px 0;
      padding: 10px;
      background: #f9f9f9;
      border-radius: 4px;
    }
    .menu-func-group {
      margin: 10px 0 0 25px;
      padding: 10px;
      background: #f5f5f5;
      border-radius: 4px;
    }
    .hint {
      font-size: 12px;
      color: #888;
      margin: 0 0 8px 0;
    }
    .menu-level3 {
      display: flex;
      flex-wrap: wrap;
      gap: 15px;
    }
    .header {
      text-align: center;
      margin-bottom: 20px;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="menu-container">
      <div class="header">
        <h2>角色权限管理</h2>
        <p>请为角色分配菜单和功能权限</p>
      </div>
      
      <el-form :model="form" :rules="rules" label-width="120px">
        <el-form-item label="角色名称" prop="RoleName">
          <el-input v-model="form.RoleName" placeholder="请输入角色名称"></el-input>
        </el-form-item>
        
        <el-form-item label="角色描述" prop="RoleDescription">
          <el-input v-model="form.RoleDescription" type="textarea" placeholder="请输入角色描述"></el-input>
        </el-form-item>
        
        <h3>菜单权限</h3>
        
        <div v-for="menu in menuOptions" :key="menu.Id" class="menu-row">
          <!-- 一级菜单 -->
          <el-form-item :label="menu.Name">
            <!-- 二级菜单 -->
            <el-checkbox-group v-model="checkedMenuIds" class="menu-col menu-level2">
              <div v-for="sub in menu.Children" :key="sub.Id" class="menu-sub">
                <el-checkbox
                  :label="sub.Id"
                  :key="sub.Id"
                  :indeterminate="sub.indeterminate"
                  @change="(val) => handleSubMenuChange(val, sub)"
                >
                  {{ sub.Name }}
                </el-checkbox>
                <!-- 三级菜单(功能权限) -->
                <div class="menu-func-group">
                  <p class="hint">功能权限:</p>
                  <el-checkbox-group
                    v-model="checkedFunctionIds"
                    class="menu-col menu-level3"
                    @change="(value) => handleFunctionChange(value, sub)"
                  >
                    <el-checkbox
                      v-for="subFunc in sub.Functions"
                      :key="subFunc.Id"
                      :label="subFunc.Id"
                      :value="subFunc.Id"
                    >
                      {{ subFunc.Name }}
                    </el-checkbox>
                  </el-checkbox-group>
                </div>
              </div>
            </el-checkbox-group>
          </el-form-item>
        </div>
        
        <el-form-item>
          <el-button type="primary" @click="submitForm">提交</el-button>
          <el-button @click="resetForm">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>

  <script>
    const { createApp, ref, reactive, onMounted } = Vue;
    
    createApp({
      setup() {
        // 为每个二级菜单添加 indeterminate 状态
        const menuOptions = ref([
          {
            Id: '1',
            Name: '系统管理',
            Children: [
              {
                Id: '1-1',
                Name: '用户管理',
                indeterminate: false,
                Functions: [
                  { Id: '1-1-1', Name: '新增用户' },
                  { Id: '1-1-2', Name: '编辑用户' },
                  { Id: '1-1-3', Name: '删除用户' }
                ]
              },
              {
                Id: '1-2',
                Name: '角色管理',
                indeterminate: false,
                Functions: [
                  { Id: '1-2-1', Name: '新增角色' },
                  { Id: '1-2-2', Name: '编辑角色' },
                  { Id: '1-2-3', Name: '删除角色' }
                ]
              }
            ]
          },
          {
            Id: '2',
            Name: '业务管理',
            Children: [
              {
                Id: '2-1',
                Name: '订单管理',
                indeterminate: false,
                Functions: [
                  { Id: '2-1-1', Name: '创建订单' },
                  { Id: '2-1-2', Name: '审核订单' },
                  { Id: '2-1-3', Name: '取消订单' }
                ]
              },
              {
                Id: '2-2',
                Name: '客户管理',
                indeterminate: false,
                Functions: [
                  { Id: '2-2-1', Name: '新增客户' },
                  { Id: '2-2-2', Name: '编辑客户' },
                  { Id: '2-2-3', Name: '删除客户' }
                ]
              }
            ]
          }
        ]);
        
        const functionOptions = ref([]);
        const checkedMenuIds = ref([]);
        const checkedFunctionIds = ref([]);
        const detailInfo = ref({});
        
        const form = reactive({
          RoleName: '',
          RoleDescription: ''
        });
        
        const rules = reactive({
          RoleName: [{ required: true, message: "角色名称不能为空", trigger: "blur" }],
          RoleDescription: [{ required: true, message: "角色描述不能为空", trigger: "blur" }],
        });
        
        // 处理二级菜单勾选变化
        const handleSubMenuChange = (checked, subMenu) => {
          if (checked) {
            // 选中二级菜单时,添加所有功能权限
            subMenu.Functions.forEach(func => {
              if (!checkedFunctionIds.value.includes(func.Id)) {
                checkedFunctionIds.value.push(func.Id);
              }
            });
            subMenu.indeterminate = false;
          } else {
            // 取消二级菜单时,移除所有功能权限
            subMenu.Functions.forEach(func => {
              const index = checkedFunctionIds.value.indexOf(func.Id);
              if (index > -1) {
                checkedFunctionIds.value.splice(index, 1);
              }
            });
            subMenu.indeterminate = false;
          }
          
          // 更新所有二级菜单的不确定状态
          updateAllIndeterminateStates();
        };
        
        // 处理功能权限勾选变化
        const handleFunctionChange = (value, subMenu) => {
          const allFunctions = subMenu.Functions.map(f => f.Id);
          const allChecked = allFunctions.every(id => checkedFunctionIds.value.includes(id));
          const anyChecked = allFunctions.some(id => checkedFunctionIds.value.includes(id));
          
          // 更新当前二级菜单的选中状态
          if (allChecked) {
            // 如果所有功能都被选中,确保二级菜单被选中
            if (!checkedMenuIds.value.includes(subMenu.Id)) {
              checkedMenuIds.value.push(subMenu.Id);
            }
            subMenu.indeterminate = false;
          } else if (anyChecked) {
            // 如果部分功能被选中,设置不确定状态
            subMenu.indeterminate = true;
            // 确保二级菜单不被选中(因为是不确定状态)
            const index = checkedMenuIds.value.indexOf(subMenu.Id);
            if (index > -1) {
              checkedMenuIds.value.splice(index, 1);
            }
          } else {
            // 如果没有功能被选中,确保二级菜单不被选中
            subMenu.indeterminate = false;
            const index = checkedMenuIds.value.indexOf(subMenu.Id);
            if (index > -1) {
              checkedMenuIds.value.splice(index, 1);
            }
          }
        };
        
        // 更新所有二级菜单的不确定状态
        const updateAllIndeterminateStates = () => {
          menuOptions.value.forEach(menu => {
            menu.Children.forEach(subMenu => {
              const allFunctions = subMenu.Functions.map(f => f.Id);
              const anyChecked = allFunctions.some(id => checkedFunctionIds.value.includes(id));
              const allChecked = allFunctions.every(id => checkedFunctionIds.value.includes(id));
              
              if (anyChecked && !allChecked) {
                subMenu.indeterminate = true;
              } else {
                subMenu.indeterminate = false;
              }
            });
          });
        };
        
        // 初始化时设置一些默认选中项,方便演示
        onMounted(() => {
          checkedMenuIds.value = ['1-1'];
          checkedFunctionIds.value = ['1-1-1', '1-1-2', '2-1-1'];
          updateAllIndeterminateStates();
        });
        
        const submitForm = () => {
          console.log('选中的菜单ID:', checkedMenuIds.value);
          console.log('选中的功能ID:', checkedFunctionIds.value);
          ElMessage.success('提交成功!');
        };
        
        const resetForm = () => {
          checkedMenuIds.value = [];
          checkedFunctionIds.value = [];
          form.RoleName = '';
          form.RoleDescription = '';
          
          // 重置所有不确定状态
          menuOptions.value.forEach(menu => {
            menu.Children.forEach(subMenu => {
              subMenu.indeterminate = false;
            });
          });
        };
        
        return {
          menuOptions,
          functionOptions,
          checkedMenuIds,
          checkedFunctionIds,
          detailInfo,
          form,
          rules,
          handleSubMenuChange,
          handleFunctionChange,
          submitForm,
          resetForm
        };
      }
    }).use(ElementPlus).mount('#app');
  </script>
</body>
</html>

image

posted @ 2025-09-04 13:58  VipSoft  阅读(11)  评论(0)    收藏  举报