vue3中,Object.keys(newData).forEach(key => { props.nodeData[key] = newData[key] })与直接修改props.nodeDat

一、问题:

vue3中,Object.keys(newData).forEach(key => { props.nodeData[key] = newData[key]  })与直接修改props.nodeData的区别:

在 Vue 中,直接修改 props 是不推荐的,因为它违反了 Vue 的单向数据流原则。然而,如果你确实需要在子组件中修改从父组件传递过来的对象,可以考虑以下两种方法:

解决方法:

1. 直接赋值 props.nodeData = newData

这种方法直接将 newData 赋值给 props.nodeData,从而覆盖原有的对象。这种方法简单直接,但在 Vue 中不推荐使用,因为它直接修改了 props,这会导致以下问题:

  • 破坏了 Vue 的响应式系统,因为父组件不会意识到子组件对 props 的修改。

  • 在开发模式下,Vue 会发出警告,提醒你不要直接修改 props

  • 如果父组件后续更新了 props.nodeData,子组件中的修改可能会被覆盖。

2. 使用 Object.keys 遍历更新

这种方法通过遍历 newData 的键,并逐一将值赋给 props.nodeData 的对应键。这种方法虽然避免了直接赋值,但仍然直接修改了 props,因此也存在上述问题。

Object.keys(newData).forEach(key => {
  props.nodeData[key] = newData[key]
})

推荐的方法:避免直接修改 props

为了避免直接修改 props,推荐使用以下方法:

推荐方法:

1. 使用本地状态和事件通信

在子组件中创建一个本地状态来存储 props 的副本,并在需要更新时通过事件通知父组件进行更新。

<template>
  <div>
    <!-- 使用本地状态进行展示和编辑 -->
    <input v-model="localNodeData.name" />
    <button @click="updateNodeData">更新</button>
  </div>
</template>

<script setup>
import { ref, watch, defineProps, defineEmits } from 'vue';

const props = defineProps({
  nodeData: Object
});

const emit = defineEmits(['updateNodeData']);

// 创建本地状态,存储 props 的副本
const localNodeData = ref(JSON.parse(JSON.stringify(props.nodeData)));

// 监听父组件传递的 nodeData 的变化,更新本地状态
watch(() => props.nodeData, (newVal) => {
  localNodeData.value = JSON.parse(JSON.stringify(newVal));
}, { deep: true });

// 更新数据的方法
const updateNodeData = () => {
  emit('updateNodeData', localNodeData.value);
};
</script>

在父组件中:

<template>
  <child-component :node-data="parentNodeData" @updateNodeData="handleUpdateNodeData" />
</template>

<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const parentNodeData = ref({ name: '初始名称', value: '初始值' });

const handleUpdateNodeData = (newData) => {
  parentNodeData.value = newData;
};
</script>
2、这种方法的优点包括:
  • 遵循 Vue 的单向数据流原则。

  • 确保父组件和子组件之间的数据一致性。

  • 避免直接修改 props,从而避免 Vue 的警告和潜在的 bug。

3、总结

虽然直接赋值 props.nodeData = newData 和使用 Object.keys 遍历更新都可以在短期内实现数据的更新,但它们都直接修改了 props,这是不推荐的做法。推荐使用本地状态和事件通信的方式,这样可以确保数据流的清晰和可控,同时避免潜在的问题。

二、但是,如果父组件的nodeData也是props接收的,又该怎么改呢?

1、解决方法:

如果父组件的 parentNodeData 也是通过 props 从更上层的组件接收过来的,那么需要确保在每一层组件中都遵循 Vue 的单向数据流原则。这种情况下,可以通过在每一层组件中使用本地状态和事件通信来避免直接修改 props

以下是一个完整的示例,展示了如何在多层组件中处理这种情况:

假设的组件结构
  1. 爷爷组件(GrandparentComponent):传递初始数据。

  2. 父组件(ParentComponent):接收数据并通过事件更新数据。

  3. 子组件(ChildComponent):接收数据并通过事件更新数据。

爷爷组件(GrandparentComponent)
<template>
  <div>
    <parent-component :initial-data="grandparentData" @update-data="handleUpdateData" />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import ParentComponent from './ParentComponent.vue';

const grandparentData = ref({ name: '爷爷初始名称', value: '爷爷初始值' });

const handleUpdateData = (newData) => {
  grandparentData.value = newData;
};
</script>
父组件(ParentComponent)
<template>
  <div>
    <!-- 使用本地状态进行展示和编辑 -->
    <child-component :node-data="localParentData" @updateNodeData="handleUpdateNodeData" />
  </div>
</template>

<script setup>
import { ref, watch, defineProps, defineEmits } from 'vue';
import ChildComponent from './ChildComponent.vue';

const props = defineProps({
  initialData: Object
});

const emit = defineEmits(['update-data']);

// 创建本地状态,存储 props 的副本
const localParentData = ref(JSON.parse(JSON.stringify(props.initialData)));

// 监听父组件传递的 initialData 的变化,更新本地状态
watch(() => props.initialData, (newVal) => {
  localParentData.value = JSON.parse(JSON.stringify(newVal));
}, { deep: true });

// 更新数据的方法
const handleUpdateNodeData = (newData) => {
  localParentData.value = newData;
  emit('update-data', newData);
};
</script>
子组件(ChildComponent)
<template>
  <div>
    <!-- 使用本地状态进行展示和编辑 -->
    <input v-model="localNodeData.name" />
    <input v-model="localNodeData.value" />
    <button @click="updateNodeData">更新</button>
  </div>
</template>

<script setup>
import { ref, watch, defineProps, defineEmits } from 'vue';

const props = defineProps({
  nodeData: Object
});

const emit = defineEmits(['updateNodeData']);

// 创建本地状态,存储 props 的副本
const localNodeData = ref(JSON.parse(JSON.stringify(props.nodeData)));

// 监听父组件传递的 nodeData 的变化,更新本地状态
watch(() => props.nodeData, (newVal) => {
  localNodeData.value = JSON.parse(JSON.stringify(newVal));
}, { deep: true });

// 更新数据的方法
const updateNodeData = () => {
  emit('updateNodeData', localNodeData.value);
};
</script>

2、总结

通过在每一层组件中使用本地状态和事件通信,可以避免直接修改 props,从而确保数据流的清晰和可控。这种做法的优点包括:

  • 遵循 Vue 的单向数据流原则。

  • 确保每一层组件之间的数据一致性。

  • 避免直接修改 props,从而避免 Vue 的警告和潜在的 bug。

  • 使组件更加模块化和可维护。

如果项目中需要频繁处理这种情况,可以考虑使用 Vuex 或 Pinia 这样的状态管理库,以简化多层组件之间的状态管理。

<template> <div class="role_wrapper"> <div class="conys"> <!-- 搜索过滤区域 --> <div class="search-container"> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" class="search-form"> <el-form-item label="Role Name" prop="roleName"> <el-input v-model="queryParams.roleName" placeholder="Enter role name" clearable style="width: 240px" /> </el-form-item> <el-form-item label="Role Able" prop="roleAble"> <el-select v-model="queryParams.roleAble" placeholder="Select role able" clearable> <el-option v-for="item in roleAbleList" :key="item.value" :label="item.desc" :value="item.value"> </el-option> </el-select> </el-form-item> <el-form-item label="Create Time" prop="createTime" class="date-picker-item"> <el-date-picker v-model="dateRange" type="daterange" range-separator="-" start-placeholder="Start date" @change="handleDateChange()" end-placeholder="End date" value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%;"> </el-date-picker> </el-form-item> <el-form-item class="button-group"> <el-button type="primary" size="mini" @click="handleQuery">Search</el-button> <el-button size="mini" @click="resetQuery">Reset</el-button> </el-form-item> </el-form> </div> <!-- 细横线 --> <el-row :gutter="10"> <div class="custom-line"></div> </el-row> <!-- 操作按钮区域 --> <div class="operation-buttons"> <el-button type="primary" size="mini" @click="handleAdd">Add</el-button> <el-button size="mini" @click="handleExport">Export</el-button> <el-button type="danger" size="mini" @click="handleDelete" :disabled="selectedRoleIds.length === 0"> Delete </el-button> </div> <!-- 数据表格 --> <div class="role-list"> <el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55"></el-table-column> <el-table-column label="Role Code" align="center" prop="code" /> <el-table-column label="Role Name" align="center" prop="roleName" :show-overflow-tooltip="true" /> <el-table-column label="Rank" align="center" prop="rank" /> <el-table-column label="Role Able" align="center" prop="roleAble"> <template #default="scope"> <el-tag :type="scope.row.roleAbleDesc === &#39;yes&#39; ? &#39;success&#39; : &#39;danger&#39;"> {{ scope.row.roleAbleDesc }} </el-tag> </template> </el-table-column> <el-table-column label="Data Scope" align="center" prop="dataScopeDesc" :show-overflow-tooltip="true" /> <el-table-column label="Remark" align="center" prop="remark" :show-overflow-tooltip="true" /> <el-table-column label="Create Time" align="center" prop="createTime" width="180" /> <el-table-column label="Operate" align="center" class-name="small-padding fixed-width"> <template #default="scope"> <el-button size="mini" type="text" icon="el-icon-edit" @click="handleEdit(scope.row)"> </el-button> <el-button size="mini" type="text" icon="el-icon-view" @click="handleDetail(scope.row)"> </el-button> <el-button size="mini" type="text" icon="el-icon-lock" @click="handlePermission(scope.row)"> </el-button> <el-button size="mini" type="text" icon="el-icon-user" @click="handleUsers(scope.row)"> </el-button> </template> </el-table-column> </el-table> </div> <!-- 分页控件 --> <div class="pagination-block"> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="currentPage" :page-size="pageSize" layout="total, prev, pager, next" :total="total"> </el-pagination> </div> </div> <!-- 添加/编辑角色抽屉 - 60%大小 --> <el-drawer title="" :visible.sync="drawerVisible" :with-header="false" size="60%"> <div class="drawerContent"> <div class="drawerHeader"> <div style="display: flex; align-items: center"> <i style="font-size: 30px; margin-right: 5px; cursor: pointer" class="el-icon-close" @click="drawerVisible = false"></i> <span>{{ isEdit ? &#39;Edit Role&#39; : &#39;Add Role&#39; }}</span> </div> </div> <div class="form-container"> <el-form :model="roleForm" ref="roleForm" :rules="rules" label-width="120px"> <!-- 第一行:RoleCode 和 RoleName 并排 --> <el-row :gutter="20" class="form-row"> <el-col :span="12"> <el-form-item label="Role Code" prop="code"> <el-input v-model="roleForm.code" placeholder="Enter role code" :disabled="isEdit"></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="Role Name" prop="roleName"> <el-input v-model="roleForm.roleName" placeholder="Enter role name"></el-input> </el-form-item> </el-col> </el-row> <!-- 第二行:Rank 和 Role Able 并排 --> <el-row :gutter="20" class="form-row"> <el-col :span="12"> <el-form-item label="Rank" prop="rank"> <el-input v-model="roleForm.rank" placeholder="Enter rank"></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="Role Able" prop="roleAble"> <el-select v-model="roleForm.roleAble" placeholder="Select role able"> <el-option value="YES" label="YES"></el-option> <el-option value="NO" label="NO"></el-option> </el-select> </el-form-item> </el-col> </el-row> <!-- 第三行:Comment 单独一行 --> <el-row class="form-row"> <el-col :span="24"> <el-form-item label="Comment" prop="remark"> <el-input v-model="roleForm.remark" placeholder="Enter comment" type="textarea" rows="3"></el-input> </el-form-item> </el-col> </el-row> <!-- 第四行:Menu Permissions 文字 --> <el-row class="form-row"> <el-col :span="24"> <el-form-item class="menu-permission-label"> <label>Menu Permissions</label> <div class="permission-legend"> <span class="permission-item"><i class="el-icon-check" style="color: #409EFF;"></i> 查看</span> <span class="permission-item"><i class="el-icon-circle-plus" style="color: #67C23A;"></i> 新增</span> <span class="permission-item"><i class="el-icon-edit" style="color: #E6A23C;"></i> 编辑</span> <span class="permission-item"><i class="el-icon-delete" style="color: #F56C6C;"></i> 删除</span> </div> </el-form-item> </el-col> </el-row> <!-- 第五行:Tree 树形控件 - 带增删改查权限 --> <el-row class="form-row tree-row"> <el-col :span="24"> <div class="tree-container"> <div v-if="menuPermissionsLoading" class="debug-info">Loading menu permissions...</div> <div v-if="!menuPermissionsLoading && (!menuPermissions || menuPermissions.length === 0)" class="debug-info"> No menu permission data available </div> <el-tree v-loading="menuPermissionsLoading" :data="menuPermissions" node-key="id" ref="permissionTree" :props="menuPermissionProps" :expand-on-click-node="false" :default-expand-all="true"> <template #default="{ node, data }"> <div class="tree-node-content"> <span class="tree-node-label level-${node.level}" :title="getNodeLabel(data)"> {{ getNodeLabel(data) }} </span> <div class="permission-checkboxes"> <el-checkbox v-model="data.permissions.query" @change="handlePermissionChange(data, &#39;query&#39;, $event)" size="mini"> <i class="el-icon-check" style="color: #409EFF;"></i> 查看 </el-checkbox> <el-checkbox v-model="data.permissions.add" @change="handlePermissionChange(data, &#39;add&#39;, $event)" size="mini"> <i class="el-icon-circle-plus" style="color: #67C23A;"></i> 新增 </el-checkbox> <el-checkbox v-model="data.permissions.update" @change="handlePermissionChange(data, &#39;update&#39;, $event)" size="mini"> <i class="el-icon-edit" style="color: #E6A23C;"></i> 编辑 </el-checkbox> <el-checkbox v-model="data.permissions.delete" @change="handlePermissionChange(data, &#39;delete&#39;, $event)" size="mini"> <i class="el-icon-delete" style="color: #F56C6C;"></i> 删除 </el-checkbox> </div> </div> </template> </el-tree> </div> </el-col> </el-row> </el-form> </div> <div class="drawer-footer"> <el-button @click="drawerVisible = false">Cancel</el-button> <el-button type="primary" @click="submitForm">Save</el-button> </div> </div> </el-drawer> <!-- 数据权限抽屉 - 60%大小 --> <el-drawer title="" :visible.sync="dataPermissionDrawerVisible" :with-header="false" size="60%"> <div class="drawerContent"> <div class="drawerHeader"> <div style="display: flex; align-items: center"> <i style="font-size: 30px; margin-right: 5px; cursor: pointer" class="el-icon-close" @click="dataPermissionDrawerVisible = false"></i> <span>Data Permissions</span> </div> </div> <div class="form-container"> <el-form :model="dataPermissionForm" ref="dataPermissionForm" label-width="140px"> <!-- 第一行:RoleName 和 Extend of Competence 并排 --> <el-row :gutter="20" class="form-row"> <el-col :span="12"> <el-form-item label="Role Name"> <el-input v-model="dataPermissionForm.roleName" placeholder="Role name" disabled></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="Extend of Competence" class="no-wrap-label"> <el-select v-model="dataPermissionForm.competence" placeholder="Select extend of competence" @change="handleCompetenceChange"> <el-option value="all" label="All Data Permissions"></el-option> <el-option value="personal" label="Personal Data Permissions"></el-option> <el-option value="custom" label="Custom Data Permissions"></el-option> </el-select> </el-form-item> </el-col> </el-row> <!-- 第二行:Data Permissions 文字 --> <el-row class="form-row" v-if="showDataPermissionTree"> <el-col :span="24"> <el-form-item class="menu-permission-label"> <label>Data Permissions</label> </el-form-item> </el-col> </el-row> <!-- 第三行:Data Permissions 树形控件 - 仅当选择Custom时显示 --> <el-row class="form-row tree-row" v-if="showDataPermissionTree"> <el-col :span="24"> <div class="tree-container"> <div v-if="dataPermissions.length === 0" class="debug-info"> No data permission data available </div> <el-tree :data="dataPermissions" show-checkbox node-key="id" ref="dataPermissionTree" :props="defaultProps" @check-change="handleDataPermissionCheckChange" :default-expand-all="true"> <template #default="{ node, data }"> <span class="tree-node-label level-${node.level}" :title="data.label || &#39;Unnamed Node&#39;"> {{ data.label || &#39;Unnamed Node&#39; }} </span> </template> </el-tree> </div> </el-col> </el-row> </el-form> </div> <div class="drawer-footer"> <el-button @click="dataPermissionDrawerVisible = false">Cancel</el-button> <el-button type="primary" @click="submitDataPermission">Save</el-button> </div> </div> </el-drawer> </div> </template> <script> import objurl from "@/common/interface/url.js"; import objaxios from "axios"; import objstorage from "@/common/localstorage/storage.js"; import moment from &#39;moment-timezone&#39;; export default { components: {}, data() { return { // 加载状态 loading: false, // 菜单权限加载状态 menuPermissionsLoading: false, // 角色列表数据 roleList: [], // 选中的角色ID selectedRoleIds: [], // 查询参数 queryParams: { endCreateTime: "", pageNum: 1, pageSize: 10, roleAble: "", roleName: "", startCreateTime: "" }, // 分页参数 currentPage: 1, pageSize: 10, total: 0, // 日期范围 dateRange: [], // Role Able列表 roleAbleList: [ { desc: &#39;YES&#39;, value: "YES" }, { desc: &#39;NO&#39;, value: "NO" } ], // 抽屉显示状态 drawerVisible: false, // 数据权限抽屉显示状态 dataPermissionDrawerVisible: false, // 是否编辑模式 isEdit: false, // 角色表单数据 roleForm: { id: &#39;&#39;, code: &#39;&#39;, roleName: &#39;&#39;, rank: &#39;&#39;, roleAble: &#39;YES&#39;, remark: &#39;&#39;, // 权限细分存储 - 确保初始化为数组 permissions: { query: [], add: [], update: [], delete: [] }, dataScope: &#39;ALL&#39;, locationGroupIdList: [] }, // 数据权限表单数据 dataPermissionForm: { roleId: &#39;&#39;, roleName: &#39;&#39;, competence: &#39;&#39;, dataPermissions: [] }, // 控制是否显示数据权限树形控件 showDataPermissionTree: false, // 菜单权限树形数据(从接口加载) menuPermissions: [], // 数据权限树形数据 dataPermissions: [ { id: 1, label: &#39;User Data&#39;, children: [ { id: 11, label: &#39;Basic Information&#39; }, { id: 12, label: &#39;Contact Details&#39; }, { id: 13, label: &#39;Activity Logs&#39; } ] }, { id: 2, label: &#39;Content Data&#39;, children: [ { id: 21, label: &#39;Published Content&#39; }, { id: 22, label: &#39;Draft Content&#39; }, { id: 23, label: &#39;Archived Content&#39; } ] } ], // 树形控件配置 defaultProps: { children: &#39;children&#39;, label: &#39;label&#39; }, // 菜单权限树形控件配置 menuPermissionProps: { children: &#39;children&#39;, label: &#39;name&#39; }, // 表单验证规则 rules: { code: [ { required: true, message: &#39;Please enter role code&#39;, trigger: &#39;blur&#39; } ], roleName: [ { required: true, message: &#39;Please enter role name&#39;, trigger: &#39;blur&#39; } ], rank: [ { required: true, message: &#39;Please enter rank&#39;, trigger: &#39;blur&#39; }, { validator: (rule, value, callback) => { if (/^[0-9]+$/.test(value) || typeof value === &#39;number&#39; && !isNaN(value)) { callback(); } else { callback(new Error(&#39;Rank must be a number&#39;)); } }, trigger: &#39;blur&#39; } ], roleAble: [ { required: true, message: &#39;Please select role able&#39;, trigger: &#39;change&#39; } ] } }; }, methods: { // 获取节点标签 getNodeLabel(data) { return data.label || data.name || data.path || `Node ${data.id}`; }, // 将扁平数据转换为树形结构 buildTree(data) { if (!Array.isArray(data)) { console.warn(&#39;Invalid data format for building tree:&#39;, data); return []; } const map = {}; const tree = []; data.forEach(item => { if (!item || typeof item !== &#39;object&#39;) { console.warn(&#39;Invalid item in tree data:&#39;, item); return; } // 确保每个节点都有完整的权限结构和权限码 const nodeData = { ...item, id: item.id || Date.now() + Math.random(), name: item.name || item.path || `Node ${item.id || Date.now()}`, permissions: { query: false, add: false, update: false, delete: false }, // 确保权限码字段存在,避免undefined queryPermissionCode: item.queryPermissionCode || item.queryCode || &#39;&#39;, addPermissionCode: item.addPermissionCode || item.addCode || &#39;&#39;, updatePermissionCode: item.updatePermissionCode || item.updateCode || &#39;&#39;, deletePermissionCode: item.deletePermissionCode || item.deleteCode || &#39;&#39;, children: item.children || [] }; map[nodeData.id] = nodeData; }); data.forEach(item => { if (!item || typeof item !== &#39;object&#39;) return; const node = map[item.id]; if (!node) return; const parentId = item.parentId !== undefined && item.parentId !== null ? item.parentId : 0; if (parentId === 0 || parentId === null || parentId === undefined) { tree.push(node); } else { const parentNode = map[parentId]; if (parentNode) { parentNode.children.push(node); } else { tree.push(node); } } }); return tree; }, // 级联选择所有子节点权限 cascadeSelectChildren(node, permissionType) { if (!node || !node.children || node.children.length === 0) { return; } node.children.forEach(child => { // 强制设置子节点权限状态 child.permissions[permissionType] = true; // 获取子节点权限码 const permissionCode = child[`${permissionType}PermissionCode`]; // 确保权限码有效且未被添加 if (permissionCode && permissionCode.trim() !== &#39;&#39;) { // 检查是否已存在,不存在则添加 if (!this.roleForm.permissions[permissionType].includes(permissionCode)) { this.roleForm.permissions[permissionType].push(permissionCode); console.log(`Added child permission ${permissionType}: ${permissionCode}`); } } // 递归处理更深层级的子节点 this.cascadeSelectChildren(child, permissionType); }); }, // 级联取消所有子节点权限 cascadeDeselectChildren(node, permissionType) { if (!node || !node.children || node.children.length === 0) { return; } node.children.forEach(child => { // 强制取消子节点权限状态 child.permissions[permissionType] = false; // 获取子节点权限码 const permissionCode = child[`${permissionType}PermissionCode`]; // 从权限数组中移除 if (permissionCode && permissionCode.trim() !== &#39;&#39;) { this.roleForm.permissions[permissionType] = this.roleForm.permissions[permissionType].filter( code => code !== permissionCode ); console.log(`Removed child permission ${permissionType}: ${permissionCode}`); } // 递归处理更深层级的子节点 this.cascadeDeselectChildren(child, permissionType); }); }, // 处理权限变化 - 核心修复方法 handlePermissionChange(node, permissionType, value) { // 直接使用传入的node对象,避免再次查找 if (!node) { console.warn(&#39;Node is undefined&#39;); return; } // 更新节点的权限状态 node.permissions[permissionType] = value; // 获取当前节点的权限码 const permissionCode = node[`${permissionType}PermissionCode`]; console.log(`Processing ${permissionType} permission for node ${node.id}:`, { value, permissionCode, currentPermissions: [...this.roleForm.permissions[permissionType]] }); // 验证权限码是否有效 const isValidCode = permissionCode && typeof permissionCode === &#39;string&#39; && permissionCode.trim() !== &#39;&#39;; if (value) { // 勾选状态:添加权限码 if (isValidCode) { // 确保权限码不存在才添加 if (!this.roleForm.permissions[permissionType].includes(permissionCode)) { this.roleForm.permissions[permissionType].push(permissionCode); console.log(`Added ${permissionType} permission: ${permissionCode}`); } } else { console.warn(`Invalid ${permissionType} permission code for node ${node.id}:`, permissionCode); } // 级联选择子节点(如果是一级菜单) if (node.level === 1) { this.cascadeSelectChildren(node, permissionType); } } else { // 取消勾选:移除权限码 if (isValidCode) { const initialLength = this.roleForm.permissions[permissionType].length; this.roleForm.permissions[permissionType] = this.roleForm.permissions[permissionType].filter( code => code !== permissionCode ); if (this.roleForm.permissions[permissionType].length < initialLength) { console.log(`Removed ${permissionType} permission: ${permissionCode}`); } } // 级联取消子节点(如果是一级菜单) if (node.level === 1) { this.cascadeDeselectChildren(node, permissionType); } } // 输出当前权限数组状态,用于调试 console.log(`Current ${permissionType} permissions:`, [...this.roleForm.permissions[permissionType]]); }, // 递归设置节点层级 setNodeLevels(nodes, level = 1) { nodes.forEach(node => { node.level = level; // 明确设置节点层级 if (node.children && node.children.length) { this.setNodeLevels(node.children, level + 1); } }); }, // 新增角色接口调用 systemroleinsert(roleData) { var _this = this; this.loading = true; // 设置请求头 objaxios.defaults.headers.common["Authorization"] = objstorage.get("userinfo").tokenHead + objstorage.get("userinfo").token; // 处理权限编码 const queryPermissions = roleData.permissions.query.filter(code => code && code.trim() !== &#39;&#39;); const addPermissions = roleData.permissions.add.filter(code => code && code.trim() !== &#39;&#39;); const updatePermissions = roleData.permissions.update.filter(code => code && code.trim() !== &#39;&#39;); const deletePermissions = roleData.permissions.delete.filter(code => code && code.trim() !== &#39;&#39;); // 拼接权限编码字符串 const queryPermissionCode = queryPermissions.join(&#39;,&#39;); const addPermissionCode = addPermissions.join(&#39;,&#39;); const updatePermissionCode = updatePermissions.join(&#39;,&#39;); const deletePermissionCode = deletePermissions.join(&#39;,&#39;); const applicationPermissionCodes = [...queryPermissions, ...addPermissions, ...updatePermissions, ...deletePermissions].join(&#39;,&#39;); console.log(&#39;Submitting permissions:&#39;, { queryPermissionCode, addPermissionCode, updatePermissionCode, deletePermissionCode }); // 构建请求参数 const requestData = { addPermissionCode: addPermissionCode, applicationPermissionCodes: applicationPermissionCodes, code: roleData.code, dataScope: roleData.dataScope || &#39;ALL&#39;, deletePermissionCode: deletePermissionCode, id: 0, locationGroupIdList: roleData.locationGroupIdList || [], queryPermissionCode: queryPermissionCode, rank: Number(roleData.rank) || 0, remark: roleData.remark || &#39;&#39;, roleAble: roleData.roleAble || &#39;NO&#39;, roleName: roleData.roleName, updatePermissionCode: updatePermissionCode }; console.log("requestData") console.log(requestData) return objaxios .post(objurl.systemroleinsert, requestData) .then(function (res) { _this.loading = false; if (res.data.code === "200") { _this.$message.success(&#39;Role created successfully&#39;); _this.drawerVisible = false; _this.getRoleList(); } else if (res.data.code === 2) { _this.$message.error(res.data.message); objstorage.remove("userinfo"); _this.$router.replace("/"); } else { _this.$message.error(res.data.message || &#39;Failed to create role&#39;); } }) .catch(function (error) { _this.loading = false; console.error(&#39;Error creating role:&#39;, error); _this.$message.error(&#39;Network error, please try again later&#39;); }); }, // 获取菜单权限列表 pagerouteall(){ var _this = this; this.menuPermissionsLoading = true; objaxios.defaults.headers.common["Authorization"] = objstorage.get("userinfo").tokenHead + objstorage.get("userinfo").token; objaxios .get(objurl.pagerouteall) .then(function (res) { _this.menuPermissionsLoading = false; if (res.data.code === "200") { _this.menuPermissions = _this.buildTree(res.data.data || []); // 设置节点层级 _this.setNodeLevels(_this.menuPermissions); console.log(&#39;Menu permissions loaded:&#39;, _this.menuPermissions); } else if (res.data.code === 2) { _this.$message.error(res.data.message); objstorage.remove("userinfo"); _this.$router.replace("/"); } else { _this.$message.error(res.data.message || &#39;Failed to load menu permissions&#39;); _this.menuPermissions = []; } }) .catch(function (error) { _this.menuPermissionsLoading = false; console.error(&#39;Error fetching menu permissions:&#39;, error); _this.$message.error(&#39;Network error, please try again later&#39;); _this.menuPermissions = []; }); }, // 调用接口获取角色列表 systemrolepage() { var _this = this; this.loading = true; objaxios.defaults.headers.common["Authorization"] = objstorage.get("userinfo").tokenHead + objstorage.get("userinfo").token; objaxios .post(objurl.systemrolepage, this.queryParams) .then(function (res) { _this.loading = false; if (res.data.code === "200") { _this.roleList = res.data.data.list || []; _this.total = parseInt(res.data.data.total) || 0; _this.currentPage = res.data.data.pageNum || 1; _this.pageSize = res.data.data.pageSize || 10; } else if (res.data.code === 2) { _this.$message.error(res.data.message); objstorage.remove("userinfo"); _this.$router.replace("/"); } else { _this.$message.error(res.data.message || &#39;Failed to load role list&#39;); } }) .catch(function (error) { _this.loading = false; console.error(&#39;Error loading role list:&#39;, error); _this.$message.error(&#39;Network error, please try again later&#39;); }); }, // 日期范围变化 handleDateChange() { this.queryParams.startCreateTime = this.dateRange && this.dateRange[0] ? this.dateRange[0] : ""; this.queryParams.endCreateTime = this.dateRange && this.dateRange[1] ? this.dateRange[1] : ""; }, // 查询角色列表 getRoleList() { this.systemrolepage(); }, // 搜索按钮操作 handleQuery() { this.queryParams.pageNum = 1; this.getRoleList(); }, // 重置按钮操作 resetQuery() { this.$refs.queryForm.resetFields(); this.dateRange = []; this.queryParams = { endCreateTime: "", pageNum: 1, pageSize: 10, roleAble: "", roleName: "", startCreateTime: "" }; this.getRoleList(); }, // 处理选择变化 handleSelectionChange(selection) { this.selectedRoleIds = selection.map(item => item.id); }, // 分页大小变化 handleSizeChange(val) { this.pageSize = val; this.queryParams.pageSize = val; this.queryParams.pageNum = 1; this.getRoleList(); }, // 当前页变化 handleCurrentChange(val) { this.currentPage = val; this.queryParams.pageNum = val; this.getRoleList(); }, // 添加角色 handleAdd() { if (this.menuPermissionsLoading) { this.$message.info(&#39;Permission data is loading, please wait...&#39;); return; } this.isEdit = false; // 重置角色表单,确保权限数组为空数组 this.roleForm = { id: &#39;&#39;, code: &#39;&#39;, roleName: &#39;&#39;, rank: &#39;&#39;, roleAble: &#39;YES&#39;, remark: &#39;&#39;, permissions: { query: [], add: [], update: [], delete: [] }, dataScope: &#39;ALL&#39;, locationGroupIdList: [] }; this.$nextTick(() => { if (this.menuPermissions.length) { this.resetPermissionTree(this.menuPermissions); } }); this.drawerVisible = true; }, // 重置权限树 resetPermissionTree(nodes) { nodes.forEach(node => { node.permissions = { query: false, add: false, update: false, delete: false }; if (node.children && node.children.length) { this.resetPermissionTree(node.children); } }); }, // 编辑角色 handleEdit(row) { this.isEdit = true; this.roleForm = { id: row.id, code: row.code || &#39;&#39;, roleName: row.roleName || &#39;&#39;, rank: row.rank || &#39;&#39;, roleAble: row.roleAble || &#39;YES&#39;, remark: row.remark || &#39;&#39;, permissions: { query: row.queryPermissionCode ? row.queryPermissionCode.split(&#39;,&#39;).filter(Boolean) : [], add: row.addPermissionCode ? row.addPermissionCode.split(&#39;,&#39;).filter(Boolean) : [], update: row.updatePermissionCode ? row.updatePermissionCode.split(&#39;,&#39;).filter(Boolean) : [], delete: row.deletePermissionCode ? row.deletePermissionCode.split(&#39;,&#39;).filter(Boolean) : [] }, dataScope: row.dataScope || &#39;ALL&#39;, locationGroupIdList: row.locationGroupIdList || [] }; this.$nextTick(() => { this.setPermissionTreeState(this.menuPermissions); }); this.drawerVisible = true; }, // 设置权限树状态 setPermissionTreeState(nodes) { nodes.forEach(node => { node.permissions.query = this.roleForm.permissions.query.includes(node.queryPermissionCode); node.permissions.add = this.roleForm.permissions.add.includes(node.addPermissionCode); node.permissions.update = this.roleForm.permissions.update.includes(node.updatePermissionCode); node.permissions.delete = this.roleForm.permissions.delete.includes(node.deletePermissionCode); if (node.children && node.children.length) { this.setPermissionTreeState(node.children); } }); }, // 查看角色详情 handleDetail(row) { const roleData = JSON.stringify(row); this.$router.push({ path: "/RoleDetails", query: { roleData: roleData } }); }, // 角色权限设置 handlePermission(row) { this.dataPermissionForm = { roleId: row.id, roleName: row.roleName || &#39;&#39;, competence: this.getCompetenceValue(row.dataScopeValue), dataPermissions: row.dataPermissions || [] }; this.showDataPermissionTree = this.dataPermissionForm.competence === &#39;custom&#39;; this.$nextTick(() => { if (this.$refs.dataPermissionTree && row.dataPermissions) { this.$refs.dataPermissionTree.setCheckedKeys(row.dataPermissions); } }); this.dataPermissionDrawerVisible = true; }, // 将数据范围值转换为对应的competence值 getCompetenceValue(dataScopeValue) { switch(dataScopeValue) { case 1: return &#39;all&#39;; case 3: return &#39;personal&#39;; default: return &#39;custom&#39;; } }, // 处理权限范围变化 handleCompetenceChange(value) { this.showDataPermissionTree = value === &#39;custom&#39;; if (value !== &#39;custom&#39;) { this.dataPermissionForm.dataPermissions = []; if (this.$refs.dataPermissionTree) { this.$refs.dataPermissionTree.setCheckedKeys([]); } } }, // 处理数据权限选择变化 handleDataPermissionCheckChange() { this.dataPermissionForm.dataPermissions = this.$refs.dataPermissionTree.getCheckedKeys(true); }, // 角色用户管理 handleUsers(row) { this.$router.push({ path: "/AssignUsers", query: { roleId: row.id, roleName: row.roleName } }); }, // 导出角色列表 handleExport() { this.$message.info(&#39;Export functionality would download role list here&#39;); }, // 删除选中角色 handleDelete() { this.$confirm(&#39;Are you sure you want to delete the selected roles?&#39;, &#39;Warning&#39;, { confirmButtonText: &#39;OK&#39;, cancelButtonText: &#39;Cancel&#39;, type: &#39;warning&#39; }).then(() => { this.roleList = this.roleList.filter(role => !this.selectedRoleIds.includes(role.id)); this.total = this.roleList.length; this.selectedRoleIds = []; this.$message.success(&#39;Deletion successful&#39;); }).catch(() => { this.$message.info(&#39;Deletion cancelled&#39;); }); }, // 提交角色表单 submitForm() { this.$refs.roleForm.validate((valid) => { if (valid) { console.log(&#39;Final permissions before submit:&#39;, { ...this.roleForm.permissions }); this.roleForm.rank = Number(this.roleForm.rank) || 0; if (this.isEdit) { const index = this.roleList.findIndex(role => role.id === this.roleForm.id); if (index !== -1) { this.roleList.splice(index, 1, { ...this.roleList[index], ...this.roleForm, queryPermissionCode: this.roleForm.permissions.query.filter(Boolean).join(&#39;,&#39;), addPermissionCode: this.roleForm.permissions.add.filter(Boolean).join(&#39;,&#39;), updatePermissionCode: this.roleForm.permissions.update.filter(Boolean).join(&#39;,&#39;), deletePermissionCode: this.roleForm.permissions.delete.filter(Boolean).join(&#39;,&#39;) }); this.$message.success(&#39;Update successful&#39;); } this.drawerVisible = false; } else { this.systemroleinsert(this.roleForm); } } else { return false; } }); }, // 提交数据权限表单 submitDataPermission() { const index = this.roleList.findIndex(role => role.id === this.dataPermissionForm.roleId); if (index !== -1) { if (this.showDataPermissionTree) { this.dataPermissionForm.dataPermissions = this.$refs.dataPermissionTree.getCheckedKeys(true); } let dataScopeValue = 2; if (this.dataPermissionForm.competence === &#39;all&#39;) { dataScopeValue = 1; } else if (this.dataPermissionForm.competence === &#39;personal&#39;) { dataScopeValue = 3; } this.roleList[index] = { ...this.roleList[index], dataPermissions: this.dataPermissionForm.dataPermissions, competence: this.dataPermissionForm.competence, dataScopeValue, dataScopeDesc: this.getCompetenceDesc(dataScopeValue) }; this.$message.success(&#39;Data permissions updated successfully&#39;); this.dataPermissionDrawerVisible = false; } }, // 获取权限范围描述 getCompetenceDesc(value) { switch(value) { case 1: return "所有部门"; case 3: return "本人部门"; default: return "自定义"; } } }, created() { this.getRoleList(); this.pagerouteall(); } }; </script> <style lang=&#39;stylus&#39; rel=&#39;stylesheet/stylus&#39;> /* 样式部分之前保持一致 */ html, body { overflow-x: hidden; margin: 0; padding: 0; } .role_wrapper { flex: 1; width: 100%; min-width: 1100px; .conys { height: 100%; width: 100%; position: relative; padding: 15px; box-sizing: border-box; overflow-x: visible; } .search-container { width: 100%; overflow: visible; .search-form { display: flex; align-items: center; flex-wrap: nowrap; width: 100%; .el-form-item { margin-bottom: 0 !important; margin-right: 10px; display: flex; align-items: center; } .date-picker-item { min-width: 300px; } .button-group { margin-left: auto; margin-right: 0; } } } .operation-buttons { margin: 15px 0; display: flex; gap: 10px; flex-wrap: wrap; } .role-list { margin-top: 10px; width: 100%; overflow-x: visible; } .pagination-block { display: flex; justify-content: flex-end; margin-top: 15px; padding-right: 10px; } .custom-line { height: 2px; background: #ddd; width: 100%; margin: 15px 0; } .drawerContent { padding: 20px; height: 100%; box-sizing: border-box; position: relative; overflow-y: auto; overflow-x: hidden; } .drawerHeader { margin-bottom: 15px; padding-bottom: 8px; border-bottom: 1px solid #eee; span { font-size: 18px; font-weight: bold; } } .form-container { width: 100%; padding-right: 10px; box-sizing: border-box; } .form-row { margin-bottom: 12px; } .menu-permission-label { .el-form-item__label { float: none; display: inline-block; white-space: nowrap; padding-right: 0; margin-bottom: 5px; } .el-form-item__content { margin-left: 0 !important; } } .permission-legend { margin-left: 8px; .permission-item { margin-right: 12px; font-size: 11px; display: inline-flex; align-items: center; i { margin-right: 2px; } } } .no-wrap-label { .el-form-item__label { white-space: nowrap; width: auto !important; padding-right: 10px; } } .tree-row { margin-top: -15px; padding-left: 140px; box-sizing: border-box; } .tree-container { border: 1px solid #e4e7ed; border-radius: 4px; padding: 10px; max-height: 320px; overflow-y: auto; width: 100%; box-sizing: border-box; } .tree-node-content { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: 6px 0; flex-wrap: wrap; } .tree-node-label { margin-right: 10px; vertical-align: middle; flex: 1; min-width: 150px; white-space: normal; word-wrap: break-word; padding-left: 5px; line-height: 1.5; } .level-1 { font-weight: bold; font-size: 15px; color: #303133; } .level-2 { font-weight: 500; font-size: 14px; color: #4E5969; } .level-3, .level-4 { font-size: 13px; color: #606266; } .permission-checkboxes { display: inline-block; vertical-align: middle; white-space: nowrap; min-width: 180px; .el-checkbox { margin-right: 8px; margin-bottom: 2px; font-size: 12px; i { margin-right: 2px; font-size: 12px; } } } .drawer-footer { position: absolute; bottom: 20px; right: 20px; display: flex; gap: 10px; .el-button { min-width: 80px; } } .debug-info { color: #666; padding: 10px; text-align: center; font-style: italic; border: 1px dashed #ccc; margin-bottom: 10px; } } .el-table { width: 100%; overflow-x: visible; } .el-date-editor--daterange { width: 100% !important; } .el-form-item__content { align-items: center; } .el-form-item__label { padding-right: 10px; width: 120px !important; } .el-form[label-width="140px"] .el-form-item__label { width: 140px !important; } .el-tree-node__content { height: auto !important; padding: 2px 0 !important; width: 100% !important; } .el-tree { width: 100% !important; overflow-x: hidden !important; } .el-tree-node { white-space: normal !important; } .el-drawer { overflow-x: hidden !important; } </style> 新增弹窗功能内,选中了权限(增删改查)。但是queryPermissionCode等都是空。给出修复后的全部代码。不要省略。以vue格式给出,不要html格式
最新发布
09-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值