pyt
2025-04-28 df9f801ab54f87a55ffd453d252b70544d436ac1
feat
3个文件已修改
583 ■■■■ 已修改文件
src/layouts/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/view/systemManage/role/addEdit.vue 448 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/view/systemManage/role/detail.vue 134 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/index.vue
@@ -89,6 +89,7 @@
        };
    },
    created() {
        console.log(JSON.parse(localStorage.getItem('userInfo')).roles[0].roleId);
    },
    methods: {
        ...mapMutations(['clearToken']),
src/view/systemManage/role/addEdit.vue
@@ -5,9 +5,19 @@
        <div></div>
        <div>角色信息</div>
      </div>
      <el-form ref="form" :inline="true" :model="form" :rules="rules" label-width="80px" class="demo-form-inline">
      <el-form
        ref="form"
        :inline="true"
        :model="form"
        :rules="rules"
        label-width="80px"
        class="demo-form-inline"
      >
        <el-form-item label="角色名称" prop="roleName">
          <el-input v-model="form.roleName" placeholder="请输入角色名称"></el-input>
          <el-input
            v-model="form.roleName"
            placeholder="请输入角色名称"
          ></el-input>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" placeholder="请输入备注"></el-input>
@@ -17,67 +27,18 @@
        <div></div>
        <div>操作权限</div>
      </div>
      <div>
        <div class="header">
          <div class="w20">模块名称</div>
          <div class="sconed">
            <div class="subpage">
              <div class="title">页面名称</div>
              <div class="btns">权限</div>
            </div>
          </div>
        </div>
        <div v-for="item in menu" :key="item.menuId">
          <div class="row">
            <div class="w20">
              <el-checkbox v-model="item.selected" @change="(e) => {
                setCheckStatus1(item.menuId, e)
              }" :checked="item.selected">
                {{ item.menuName }}
              </el-checkbox>
            </div>
            <div class="sconed">
              <div class="subpage"
                v-if="(item.children.length > 0 && item.children[0].children.length > 0) || item.children[0].children.menuType != 'F'">
                <div v-for="item1 in item.children" :key="item1.menuId" class="two">
                  <div class="left">
                    <el-checkbox v-model="item1.selected" @change="(e) => {
                      setCheckStatus2(item1.menuId, e, item.menuId)
                    }" :checked="item1.selected">
                      {{ item1.menuName }}
                    </el-checkbox>
                  </div>
                  <div class="right">
                    <div v-for="item2 in item1.children" :key="item2.menuId">
                      <el-checkbox v-model="item2.selected" @change="(e) => {
                        setCheckStatus3(item2.menuId, e, item1.menuId, item.menuId)
                      }" :checked="item2.selected">
                        {{ item2.menuName }}
                      </el-checkbox>
                    </div>
                  </div>
                </div>
              </div>
              <div class="subpage" v-else>
                <div class="two">
                  <!-- <div class="left">
                  </div> -->
                  <div class="right">
                    <div v-for="item1 in item.children" :key="item1.menuId">
                      <el-checkbox v-model="item1.selected" @change="(e) => {
                        setCheckStatus2(item1.menuId, e, item.menuId,)
                      }" :checked="item1.selected">
                        {{ item1.menuName }}
                      </el-checkbox>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      <div class="tree-container">
        <el-tree
          ref="permissionTree"
          :data="menu"
          :props="defaultProps"
          show-checkbox
          node-key="menuId"
          default-expand-all
          :check-strictly="true"
          @check="handleCheckChange"
        >
        </el-tree>
      </div>
      <div class="btn_box">
@@ -89,7 +50,7 @@
</template>
<script>
import { roleInfoFromUserId, getRoleInfo, add, edit } from './service.js'
import { roleInfoFromUserId, getRoleInfo, add, edit } from "./service.js";
export default {
  components: {},
  props: {},
@@ -105,28 +66,158 @@
        ],
      },
      menu: [],
      defaultProps: {
        children: "children",
        label: "menuName",
      },
    };
  },
  computed: {},
  watch: {},
  created() {
    roleInfoFromUserId().then(res => {
    roleInfoFromUserId().then((res) => {
      // 确保菜单数据正确处理
      this.menu = this.processMenuData(res);
      if (this.$route.query.roleId) {
        getRoleInfo({ id: this.$route.query.roleId }).then(resp => {
          this.menu = this.setSelectedIds(res.data.data, resp.data.data.menus || []);
        getRoleInfo({ id: this.$route.query.roleId }).then((resp) => {
          // 设置表单数据
          const roleData = resp;
          this.form = {
            roleName: resp.data.data.roleName,
            remark: resp.data.data.remark,
            roleId: resp.data.data.roleId
          }
        })
      } else {
        this.menu = res.data.data
            roleName: roleData.roleName,
            remark: roleData.remark,
            roleId: roleData.roleId,
          };
          // 设置选中的菜单项
          this.$nextTick(() => {
            if (roleData.menus && roleData.menus.length > 0) {
              // 直接使用后端返回的选中节点
              this.$refs.permissionTree.setCheckedKeys(roleData.menus);
            }
          });
        });
      }
    })
    });
  },
  mounted() { },
  mounted() {},
  methods: {
    // 处理菜单数据,为每个节点添加必要的属性
    processMenuData(menuData) {
      // 如果返回的是嵌套在data.data中的数据
      if (menuData.data && menuData.data.data) {
        menuData = menuData.data.data;
      }
      function traverse(item) {
        // 确保有children属性
        if (!item.children) {
          item.children = [];
        }
        // 递归处理子节点
        if (item.children && item.children.length > 0) {
          item.children.forEach(traverse);
        }
      }
      // 克隆一份数据,避免直接修改原数据
      const clonedData = JSON.parse(JSON.stringify(menuData));
      clonedData.forEach(traverse);
      return clonedData;
    },
    // 复选框状态变化事件处理
    handleCheckChange(data, checked) {
      // 获取树实例
      const tree = this.$refs.permissionTree;
      // 如果是父节点被点击
      if (data.children && data.children.length > 0) {
        // 获取当前节点的选中状态
        const isChecked = tree.getCheckedKeys().includes(data.menuId);
        if (isChecked) {
          // 如果父节点被选中,选中所有子节点
          const childIds = this.getAllChildIds(data);
          const currentCheckedKeys = tree.getCheckedKeys();
          const newCheckedKeys = [
            ...new Set([...currentCheckedKeys, ...childIds]),
          ];
          tree.setCheckedKeys(newCheckedKeys);
        } else {
          // 如果父节点被取消选中,取消选中所有子节点
          const childIds = this.getAllChildIds(data);
          const currentCheckedKeys = tree.getCheckedKeys();
          const newCheckedKeys = currentCheckedKeys.filter(
            (key) => !childIds.includes(key)
          );
          tree.setCheckedKeys(newCheckedKeys);
        }
      } else {
        // 如果是子节点被点击,检查父节点状态
        this.updateParentNodeState(data);
      }
    },
    // 获取节点的所有子节点ID
    getAllChildIds(node) {
      let ids = [];
      if (node.children && node.children.length > 0) {
        node.children.forEach((child) => {
          ids.push(child.menuId);
          ids = ids.concat(this.getAllChildIds(child));
        });
      }
      return ids;
    },
    // 更新父节点状态
    updateParentNodeState(node) {
      const tree = this.$refs.permissionTree;
      const parentNode = this.findParentNode(this.menu, node.menuId);
      if (parentNode) {
        const allChildren = this.getAllChildIds(parentNode);
        const checkedChildren = allChildren.filter((id) =>
          tree.getCheckedKeys().includes(id)
        );
        if (checkedChildren.length === 0) {
          // 如果没有子节点被选中,取消选中父节点
          tree.setChecked(parentNode.menuId, false);
        } else if (checkedChildren.length === allChildren.length) {
          // 如果所有子节点都被选中,选中父节点
          tree.setChecked(parentNode.menuId, true);
        } else {
          // 如果部分子节点被选中,设置父节点为半选状态
          tree.setChecked(parentNode.menuId, false);
          const node = tree.getNode(parentNode.menuId);
          if (node) {
            node.indeterminate = true;
          }
        }
        // 递归更新更上层的父节点
        this.updateParentNodeState(parentNode);
      }
    },
    // 查找节点的父节点
    findParentNode(nodes, targetId, parent = null) {
      for (let node of nodes) {
        if (node.menuId === targetId) {
          return parent;
        }
        if (node.children && node.children.length > 0) {
          const found = this.findParentNode(node.children, targetId, node);
          if (found) return found;
        }
      }
      return null;
    },
    setSelectedIds(arr, selectKeyList) {
      function traverse(item) {
        item.selected = selectKeyList.includes(item.menuId);
@@ -137,32 +228,44 @@
      arr.forEach(traverse);
      return arr;
    },
    onSubmit() {
      this.$refs['form'].validate((valid) => {
      this.$refs["form"].validate((valid) => {
        if (valid) {
          if (this.getSelectedIds(this.menu).length == 0) {
            this.$baseMessage('请勾选操作权限', 'warning')
            return
          // 获取选中的节点ID列表
          const checkedKeys = this.$refs.permissionTree.getCheckedKeys();
          const halfCheckedKeys =
            this.$refs.permissionTree.getHalfCheckedKeys();
          // 合并完全选中和半选中的节点
          const allCheckedKeys = [...checkedKeys, ...halfCheckedKeys];
          if (allCheckedKeys.length === 0) {
            this.$message.warning("请勾选操作权限");
            return;
          }
          let obj = {
            ...this.form,
            menuIds: this.getSelectedIds(this.menu)
          }
            menuIds: allCheckedKeys,
          };
          if (this.$route.query && this.$route.query.roleId) {
            obj.roleId = this.$route.query.roleId
            obj.roleId = this.$route.query.roleId;
            edit(obj).then(() => {
              this.$baseMessage('保存成功', 'success')
              this.$router.go(-1)
            })
              this.$message.success("保存成功");
              this.$router.go(-1);
            });
          } else {
            add(obj).then(() => {
              this.$baseMessage('保存成功', 'success')
              this.$router.go(-1)
            })
              this.$message.success("保存成功");
              this.$router.go(-1);
            });
          }
        }
      })
      });
    },
    getSelectedIds(arr) {
      let result = [];
      function traverse(item) {
@@ -181,75 +284,6 @@
      }
      return result;
    },
    setCheckStatus1(id, status) { //点击第1级
      if (!status) {
        this.menu = this.menu.map(item => {
          if (item.menuId == id) {
            item.selected = status
            if (item.children.length > 0) {
              item.children = item.children.map(item1 => {
                item1.selected = status
                if (item1.children.length > 0) {
                  item1.children = item1.children.map(item2 => {
                    item2.selected = status
                    return { ...item2 }
                  })
                }
                return { ...item1 }
              })
            }
          }
          return { ...item }
        })
      } else {
        this.menu = this.menu.map(item => {
          if (item.menuId == id) {
            item.selected = true
          }
          return { ...item }
        })
      }
    },
    setCheckStatus2(id, status, aId) { //点击第2级
      this.menu = this.menu.map(item => {
        if (item.menuId == aId) {
          item.selected = true
          if (item.children.length > 0) {
            item.children = item.children.map(item1 => {
              if (item1.menuId == id) {
                item1.selected = status
              }
              return { ...item1 }
            })
          }
        }
        return { ...item }
      })
    },
    setCheckStatus3(id, status, bId, aId) {//点击第3级
      this.menu = this.menu.map(item => {
        if (item.menuId == aId) {
          item.selected = true
          if (item.children.length > 0) {
            item.children = item.children.map(item1 => {
              if (item1.menuId == bId) {
                item1.selected = true
                if (item1.children.length > 0) {
                  item1.children = item1.children.map(item2 => {
                    if (item2.menuId == id) {
                      item2.selected = status
                    }
                    return { ...item2 }
                  })
                }
              }
              return { ...item1 }
            })
          }
        }
        return { ...item }
      })
    }
  },
};
</script>
@@ -266,7 +300,7 @@
    div:first-child {
      width: 4px;
      height: 16px;
      background: #598DEC;
      background: #598dec;
      margin-right: 8px;
    }
  }
@@ -277,78 +311,36 @@
  }
}
.el-checkbox {
  display: flex;
  align-items: center;
}
.row,
.header {
  display: flex;
  align-items: center;
.tree-container {
  padding: 20px;
  border: 1px solid #e8e8e8;
  .w20 {
    width: 15%;
    padding: 8px 20px;
  }
  .sconed {
    flex: 1;
    .subpage {
      .title {
        border: 1px solid #e8e8e8;
        border-top: none;
        border-bottom: none;
      }
      .two {
        display: flex;
        align-items: center;
        border: 1px solid #e8e8e8;
        border-top: none;
        border-right: none;
        .left {
          width: 200px;
          padding: 13px 20px;
          border-right: 1px solid #e8e8e8;
        }
        .right {
          display: flex;
          flex: 1;
          div {
            padding: 13px 0 13px 20px;
          }
        }
      }
      .two:last-child {
        border-bottom: none;
      }
      .btns {
        display: flex;
        align-items: center;
        padding: 0 20px;
      }
    }
  }
  border-radius: 4px;
  margin: 10px 0;
  background-color: #fff;
  max-height: 600px;
  overflow-y: auto;
}
.header {
  background-color: #e8e8e8;
/* 树节点样式优化 */
::v-deep .el-tree-node__content {
  height: 40px;
  padding: 0 10px;
}
  .subpage {
    display: flex;
  }
::v-deep .el-tree-node__label {
  font-size: 14px;
}
  .title {
    width: 200px;
    padding: 8px 20px;
  }
::v-deep .el-tree-node.is-current > .el-tree-node__content {
  background-color: #f0f7ff;
}
/* 复选框样式优化 */
::v-deep .el-checkbox__inner {
  border-radius: 2px;
}
::v-deep .el-tree-node:hover > .el-tree-node__content {
  background-color: #f5f7fa;
}
</style>
src/view/systemManage/role/detail.vue
@@ -7,62 +7,23 @@
    <el-card>
      <el-tabs v-model="activeName">
        <el-tab-pane label="操作权限" name="first">
          <div class="header">
            <div class="w20">模块名称</div>
            <div class="sconed">
              <div class="subpage">
                <div class="title">页面名称</div>
                <div class="btns">权限</div>
              </div>
            </div>
          </div>
          <div v-for="item in menu" :key="item.menuId">
            <div class="row">
              <div class="w20">
                <el-checkbox disabled :checked="item.selected">
                  {{ item.menuName }}
                </el-checkbox>
              </div>
              <div class="sconed">
                <div class="subpage"
                  v-if="(item.children.length > 0 && item.children[0].children.length > 0) || item.children[0].children.menuType != 'F'">
                  <div v-for="item1 in item.children" :key="item1.menuId" class="two">
                    <div class="left">
                      <el-checkbox disabled :checked="item1.selected">
                        {{ item1.menuName }}
                      </el-checkbox>
                    </div>
                    <div class="right">
                      <div v-for="item2 in item1.children" :key="item2.menuId">
                        <el-checkbox disabled :checked="item2.selected">
                          {{ item2.menuName }}
                        </el-checkbox>
                      </div>
                    </div>
                  </div>
                </div>
                <div class="subpage" v-else>
                  <div class="two">
                    <!-- <div class="left">
                    </div> -->
                    <div class="right">
                      <div v-for="item1 in item.children" :key="item1.menuId">
                        <el-checkbox disabled :checked="item1.selected">
                          {{ item1.menuName }}
                        </el-checkbox>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          <div class="tree-container">
            <el-tree
              ref="permissionTree"
              :data="menu"
              :props="defaultProps"
              show-checkbox
              node-key="menuId"
              default-expand-all
              :check-strictly="true"
              >
            </el-tree>
          </div>
        </el-tab-pane>
        <el-tab-pane label="人员列表" name="second">
          <el-form :inline="true" class="demo-form-inline">
            <div style="display: flex;justify-content: space-between;">
            <div style="display: flex;">
              <el-form-item label="人员搜索">
                <el-input v-model="nickNameOrPhone" placeholder="请输入姓名/联系电话"></el-input>
              </el-form-item>
@@ -137,6 +98,13 @@
      form: {},
      activeName: 'first',
      menu: [],
      defaultProps: {
        children: 'children',
        label: 'menuName',
        disabled: (data,node) => {
          return {...node,disabled:true}
        }
      },
      data: [],
      nickNameOrPhone: '',
      status: '',
@@ -160,15 +128,22 @@
  watch: {},
  created() {
    roleInfoFromUserId().then(res => {
      this.menu = this.processMenuData(res);
      getRoleInfo({ id: this.$route.query.roleId }).then(resp => {
        this.menu = this.setSelectedIds(res.data.data, resp.data.data.menus);
        this.form = {
          roleName: resp.data.data.roleName,
          remark: resp.data.data.remark,
        }
      })
    })
    this.getListData()
          roleName: resp.roleName,
          remark: resp.remark,
        };
        this.$nextTick(() => {
          if (resp.menus && resp.menus.length > 0) {
            this.$refs.permissionTree.setCheckedKeys(resp.menus);
          }
        });
      });
    });
    this.getListData();
  },
  mounted() { },
  methods: {
@@ -187,15 +162,21 @@
        this.listLoading = false
      }, 500)
    },
    setSelectedIds(arr, selectKeyList) {
    processMenuData(menuData) {
      function traverse(item) {
        item.selected = selectKeyList.includes(item.menuId);
        if (!item.children) {
          item.children = [];
        }
        if (item.children && item.children.length > 0) {
          item.children.forEach(traverse);
        }
      }
      arr.forEach(traverse);
      return arr;
      const clonedData = JSON.parse(JSON.stringify(menuData));
      clonedData.forEach(traverse);
      return clonedData;
    },
    reset() {
      this.nickNameOrPhone = ''
@@ -325,8 +306,31 @@
  }
}
/* 当复选框禁用时覆盖默认样式 */
.tree-container {
  padding: 20px;
  border: 1px solid #e8e8e8;
  border-radius: 4px;
  margin: 10px 0;
  background-color: #fff;
  max-height: 600px;
  overflow-y: auto;
}
/* 树节点样式优化 */
::v-deep .el-tree-node__content {
  height: 40px;
  padding: 0 10px;
}
::v-deep .el-tree-node__label {
  font-size: 14px;
}
::v-deep .el-tree-node.is-current > .el-tree-node__content {
  background-color: #f0f7ff;
}
/* 禁用状态下的复选框样式 */
::v-deep .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner {
  background-color: #409eff;
  border-color: #409eff;
@@ -335,4 +339,8 @@
::v-deep .el-checkbox.is-disabled .el-checkbox__inner::after {
  border-color: #fff;
}
::v-deep .el-tree-node__content:hover {
  background-color: #f5f7fa;
}
</style>