|
@@ -17,13 +17,17 @@
|
|
|
/>
|
|
|
</template>
|
|
|
<template v-if="type == 'list'">
|
|
|
- <el-button
|
|
|
- v-auth="'admin.auth.access.add'"
|
|
|
- class="is-link"
|
|
|
- type="primary"
|
|
|
- @click="onAdd(val.pdata.id, level)"
|
|
|
+ <el-button class="is-link" type="primary" @click="onAdd(val.pdata.id, level)"
|
|
|
>+添加</el-button
|
|
|
>
|
|
|
+ <!-- <el-button
|
|
|
+ class="is-link"
|
|
|
+ type="success"
|
|
|
+ @click="initMenuPermissions"
|
|
|
+ :loading="initLoading"
|
|
|
+ >
|
|
|
+ 初始化菜单
|
|
|
+ </el-button> -->
|
|
|
</template>
|
|
|
</slot>
|
|
|
</div>
|
|
@@ -62,7 +66,6 @@
|
|
|
</div>
|
|
|
<div v-if="type == 'list'" class="sa-flex">
|
|
|
<el-icon
|
|
|
- v-auth="'admin.auth.access.detail'"
|
|
|
class="edit sa-m-r-8"
|
|
|
@click.stop="onEdit(element.id, index, level)"
|
|
|
>
|
|
@@ -76,11 +79,7 @@
|
|
|
@confirm="onDelete(element.id, index, level)"
|
|
|
>
|
|
|
<template #reference>
|
|
|
- <el-icon
|
|
|
- v-auth="'admin.auth.access.delete'"
|
|
|
- class="delete sa-m-r-8"
|
|
|
- @click.stop
|
|
|
- >
|
|
|
+ <el-icon class="delete sa-m-r-8" @click.stop>
|
|
|
<Delete />
|
|
|
</el-icon>
|
|
|
</template>
|
|
@@ -107,10 +106,11 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
- import { nextTick, onMounted, reactive, watch } from 'vue';
|
|
|
+ import { nextTick, onMounted, reactive, watch, ref } from 'vue';
|
|
|
import admin from '@/app/admin/api';
|
|
|
import SaDraggable from 'vuedraggable';
|
|
|
import { useModal } from '@/sheep/hooks';
|
|
|
+ import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
import { menuRulesData } from '@/sheep/local-data/admin';
|
|
|
|
|
|
import { isEmpty } from 'lodash';
|
|
@@ -134,6 +134,7 @@
|
|
|
});
|
|
|
|
|
|
let manualChecked = false;
|
|
|
+ let isSelectingPermission = false; // 新增标志,表示正在进行权限选择操作
|
|
|
|
|
|
const state = reactive({
|
|
|
loading: false,
|
|
@@ -142,15 +143,22 @@
|
|
|
newIds: [],
|
|
|
show: {},
|
|
|
});
|
|
|
+
|
|
|
+ // 初始化loading状态
|
|
|
+ const initLoading = ref(false);
|
|
|
watch(
|
|
|
() => props.modelValue,
|
|
|
(newValue) => {
|
|
|
state.checkedIds = newValue || [];
|
|
|
|
|
|
- // 当modelValue变化时,重新计算权限状态
|
|
|
+ // 当modelValue变化时,重新计算权限状态(无论数据是否为空都要计算)
|
|
|
+ let appItem = initAppItem();
|
|
|
if (state.app.length > 0) {
|
|
|
- let appItem = initAppItem();
|
|
|
initCalculate(state.app, appItem);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果不是权限选择操作导致的变化,才重置展开状态
|
|
|
+ if (!isSelectingPermission) {
|
|
|
calculateShow(appItem);
|
|
|
}
|
|
|
},
|
|
@@ -161,39 +169,333 @@
|
|
|
watch(
|
|
|
() => state.app,
|
|
|
(newApp) => {
|
|
|
- if (newApp.length > 0) {
|
|
|
- let appItem = initAppItem();
|
|
|
+ // 无论数据是否为空都要初始化显示状态
|
|
|
+ let appItem = initAppItem();
|
|
|
+ if (newApp && newApp.length > 0) {
|
|
|
initCalculate(newApp, appItem);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果不是权限选择操作导致的变化,才重置展开状态
|
|
|
+ if (!isSelectingPermission) {
|
|
|
calculateShow(appItem);
|
|
|
}
|
|
|
},
|
|
|
);
|
|
|
|
|
|
+ // 数据转换函数:将新接口数据转换为组件需要的格式
|
|
|
+ function transformPermissionData(data) {
|
|
|
+ if (!Array.isArray(data)) {
|
|
|
+ console.warn('transformPermissionData: data is not an array', data);
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('🔄 transformPermissionData - 输入数据:', data);
|
|
|
+
|
|
|
+ const result = data.map((item) => {
|
|
|
+ console.log('🔄 transformPermissionData - 处理项目:', item);
|
|
|
+
|
|
|
+ const transformed = {
|
|
|
+ id: item.id,
|
|
|
+ title: item.name, // 使用name作为title
|
|
|
+ icon: item.logo || 'menu', // 使用logo作为icon,默认为menu
|
|
|
+ children:
|
|
|
+ item.children && Array.isArray(item.children)
|
|
|
+ ? transformPermissionData(item.children)
|
|
|
+ : [],
|
|
|
+ // 保留原始数据以备后用
|
|
|
+ _original: item,
|
|
|
+ };
|
|
|
+
|
|
|
+ console.log('🔄 transformPermissionData - 转换结果:', transformed);
|
|
|
+ return transformed;
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log('🔄 transformPermissionData - 最终结果:', result);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
async function getData() {
|
|
|
state.loading = true;
|
|
|
|
|
|
try {
|
|
|
- // 使用本地menuRulesData数据
|
|
|
- if (menuRulesData.code == '200') {
|
|
|
+ // 调用新的权限树接口
|
|
|
+ const response = await admin.auth.access.getTree();
|
|
|
+
|
|
|
+ if (response.success && response.data !== null && response.data !== undefined) {
|
|
|
+ console.log('🔍 getData - 原始响应数据:', response.data);
|
|
|
+
|
|
|
+ // 转换数据格式(即使是空数组也要处理)
|
|
|
+ const transformedData = Array.isArray(response.data)
|
|
|
+ ? transformPermissionData(response.data)
|
|
|
+ : [];
|
|
|
+
|
|
|
+ console.log('🔍 getData - 转换后数据:', transformedData);
|
|
|
+
|
|
|
// 根据type类型处理数据
|
|
|
if (props.type === 'select') {
|
|
|
- // 权限选择模式,使用完整的菜单数据
|
|
|
- state.app = menuRulesData.data.menu || [];
|
|
|
+ // 权限选择模式,使用完整的权限数据
|
|
|
+ state.app = transformedData;
|
|
|
} else {
|
|
|
- // 其他模式(如list),也使用菜单数据
|
|
|
- state.app = menuRulesData.data.menu || [];
|
|
|
+ // 其他模式(如list),也使用权限数据
|
|
|
+ state.app = transformedData;
|
|
|
}
|
|
|
|
|
|
- // 只加载数据,不立即计算权限状态
|
|
|
- // 权限状态计算会在watch中处理
|
|
|
+ // 初始化显示状态(无论数据是否为空都要初始化)
|
|
|
+ let appItem = initAppItem();
|
|
|
+ if (transformedData.length > 0) {
|
|
|
+ initCalculate(transformedData, appItem);
|
|
|
+ }
|
|
|
+ calculateShow(appItem);
|
|
|
+ } else {
|
|
|
+ console.error('获取权限数据失败:', response.message || '数据为空');
|
|
|
+ state.app = [];
|
|
|
+
|
|
|
+ // 即使没有数据也要初始化显示状态
|
|
|
+ let appItem = initAppItem();
|
|
|
+ calculateShow(appItem);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
- console.error('获取菜单数据失败:', error);
|
|
|
+ console.error('获取权限数据异常:', error);
|
|
|
+ state.app = [];
|
|
|
+
|
|
|
+ // 异常情况下也要初始化显示状态
|
|
|
+ let appItem = initAppItem();
|
|
|
+ calculateShow(appItem);
|
|
|
}
|
|
|
|
|
|
state.loading = false;
|
|
|
}
|
|
|
|
|
|
+ // 🎯 新方案:保持选中状态的数据刷新函数
|
|
|
+ async function refreshDataKeepSelected() {
|
|
|
+ // 1. 保存当前所有层级的选中状态
|
|
|
+ const selectedStates = [];
|
|
|
+
|
|
|
+ for (let level = 0; level < Object.keys(state.show).length; level++) {
|
|
|
+ const showData = state.show[level];
|
|
|
+ if (showData && showData.index !== null && showData.index !== undefined && showData.data) {
|
|
|
+ const selectedItem = showData.data[showData.index];
|
|
|
+ if (selectedItem && selectedItem.id) {
|
|
|
+ selectedStates.push({
|
|
|
+ level: level,
|
|
|
+ itemId: selectedItem.id,
|
|
|
+ itemTitle: selectedItem.title,
|
|
|
+ selectedIndex: showData.index,
|
|
|
+ });
|
|
|
+ console.log(
|
|
|
+ `💾 保存第${level}层选中状态: ${selectedItem.title} (ID: ${selectedItem.id}, 索引: ${showData.index})`,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('💾 保存的选中状态:', selectedStates);
|
|
|
+
|
|
|
+ // 2. 重新获取数据(这会重置state.show)
|
|
|
+ await getData();
|
|
|
+
|
|
|
+ // 3. 恢复选中状态
|
|
|
+ if (selectedStates.length > 0) {
|
|
|
+ console.log('🔄 开始恢复选中状态...');
|
|
|
+
|
|
|
+ // 逐层恢复选中状态
|
|
|
+ for (const selectedState of selectedStates) {
|
|
|
+ const { level, itemId, itemTitle } = selectedState;
|
|
|
+ console.log(`🔄 恢复第${level}层选中状态: ${itemTitle} (ID: ${itemId})`);
|
|
|
+
|
|
|
+ // 在当前层级查找目标项目
|
|
|
+ const currentLevelData = state.show[level] ? state.show[level].data : [];
|
|
|
+ const foundIndex = currentLevelData.findIndex((item) => item.id === itemId);
|
|
|
+
|
|
|
+ if (foundIndex !== -1) {
|
|
|
+ const foundItem = currentLevelData[foundIndex];
|
|
|
+ console.log(`✅ 找到目标项目: ${foundItem.title} (索引: ${foundIndex})`);
|
|
|
+
|
|
|
+ // 使用calculateShow来正确展开并选中这一层
|
|
|
+ await calculateShow(foundItem, foundIndex, level);
|
|
|
+
|
|
|
+ console.log(`✅ 第${level}层选中状态恢复完成`);
|
|
|
+ } else {
|
|
|
+ console.log(`⚠️ 第${level}层找不到目标项目 ID: ${itemId}`);
|
|
|
+ break; // 如果某一层找不到,停止恢复
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('✅ 选中状态恢复完成');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 🎯 新方案:局部更新数据函数
|
|
|
+ async function updateLocalData(operation, newData, parentId, targetId) {
|
|
|
+ console.log(`🔄 局部更新数据: ${operation}`, { newData, parentId, targetId });
|
|
|
+
|
|
|
+ if (operation === 'add') {
|
|
|
+ // 新增:将新数据添加到指定父级的children中
|
|
|
+ await handleLocalAdd(newData, parentId);
|
|
|
+ } else if (operation === 'edit') {
|
|
|
+ // 编辑:找到目标项目并更新其数据
|
|
|
+ await handleLocalEdit(newData, targetId);
|
|
|
+ } else if (operation === 'delete') {
|
|
|
+ // 删除:从数据中移除目标项目
|
|
|
+ await handleLocalDelete(targetId);
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('✅ 局部数据更新完成');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理新增操作
|
|
|
+ async function handleLocalAdd(newData, parentId) {
|
|
|
+ console.log(`📝 处理新增: 父级ID=${parentId}`, newData);
|
|
|
+
|
|
|
+ if (!newData) {
|
|
|
+ console.error('❌ 新增数据为空');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 转换新数据格式
|
|
|
+ const transformedNewData = {
|
|
|
+ id: newData.id,
|
|
|
+ title: newData.name || '新权限',
|
|
|
+ icon: newData.logo || 'menu',
|
|
|
+ children: [],
|
|
|
+ _original: newData,
|
|
|
+ };
|
|
|
+
|
|
|
+ if (!parentId || parentId === '' || parentId === '0') {
|
|
|
+ // 添加到根级
|
|
|
+ state.app.push(transformedNewData);
|
|
|
+ // 更新第0层的数据
|
|
|
+ if (state.show[0]) {
|
|
|
+ state.show[0].data = state.app;
|
|
|
+ }
|
|
|
+ console.log('✅ 已添加到根级');
|
|
|
+ } else {
|
|
|
+ // 添加到指定父级
|
|
|
+ const parentItem = findItemById(state.app, parentId);
|
|
|
+ if (parentItem) {
|
|
|
+ if (!parentItem.children) {
|
|
|
+ parentItem.children = [];
|
|
|
+ }
|
|
|
+ parentItem.children.push(transformedNewData);
|
|
|
+
|
|
|
+ // 更新对应层级的显示数据
|
|
|
+ updateShowDataForParent(parentId, parentItem.children);
|
|
|
+ console.log(`✅ 已添加到父级 ${parentId}`);
|
|
|
+ } else {
|
|
|
+ console.error(`❌ 找不到父级项目 ID: ${parentId}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理编辑操作
|
|
|
+ async function handleLocalEdit(newData, targetId) {
|
|
|
+ console.log(`✏️ 处理编辑: 目标ID=${targetId}`, newData);
|
|
|
+
|
|
|
+ if (!newData) {
|
|
|
+ console.error('❌ 编辑数据为空');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 🔧 验证数据格式
|
|
|
+ if (!newData.name && !newData.id) {
|
|
|
+ console.error('❌ 编辑数据格式不正确,缺少必要字段:', newData);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const targetItem = findItemById(state.app, targetId);
|
|
|
+ if (targetItem) {
|
|
|
+ // 更新项目数据
|
|
|
+ targetItem.title = newData.name || newData.label || targetItem.title;
|
|
|
+ targetItem.icon = newData.logo || newData.icon || targetItem.icon;
|
|
|
+ targetItem._original = { ...targetItem._original, ...newData };
|
|
|
+
|
|
|
+ // 更新所有相关的显示数据
|
|
|
+ updateAllShowData();
|
|
|
+ console.log(`✅ 已更新项目 ${targetId}:`, {
|
|
|
+ title: targetItem.title,
|
|
|
+ icon: targetItem.icon,
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ console.error(`❌ 找不到目标项目 ID: ${targetId}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理删除操作
|
|
|
+ async function handleLocalDelete(targetId) {
|
|
|
+ console.log(`🗑️ 处理删除: 目标ID=${targetId}`);
|
|
|
+
|
|
|
+ const result = removeItemById(state.app, targetId);
|
|
|
+ if (result.success) {
|
|
|
+ // 更新所有相关的显示数据
|
|
|
+ updateAllShowData();
|
|
|
+ console.log(`✅ 已删除项目 ${targetId}`);
|
|
|
+ } else {
|
|
|
+ console.error(`❌ 找不到要删除的项目 ID: ${targetId}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 递归查找项目
|
|
|
+ function findItemById(items, targetId) {
|
|
|
+ for (const item of items) {
|
|
|
+ if (item.id === targetId) {
|
|
|
+ return item;
|
|
|
+ }
|
|
|
+ if (item.children && item.children.length > 0) {
|
|
|
+ const found = findItemById(item.children, targetId);
|
|
|
+ if (found) return found;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 递归删除项目
|
|
|
+ function removeItemById(items, targetId) {
|
|
|
+ for (let i = 0; i < items.length; i++) {
|
|
|
+ if (items[i].id === targetId) {
|
|
|
+ items.splice(i, 1);
|
|
|
+ return { success: true };
|
|
|
+ }
|
|
|
+ if (items[i].children && items[i].children.length > 0) {
|
|
|
+ const result = removeItemById(items[i].children, targetId);
|
|
|
+ if (result.success) return result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return { success: false };
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新指定父级的显示数据
|
|
|
+ function updateShowDataForParent(parentId, newChildren) {
|
|
|
+ // 遍历所有层级,找到对应的父级并更新其子级数据
|
|
|
+ for (const [level, showData] of Object.entries(state.show)) {
|
|
|
+ if (showData.data) {
|
|
|
+ const parentItem = showData.data.find((item) => item.id === parentId);
|
|
|
+ if (parentItem) {
|
|
|
+ // 找到了父级,更新下一层级的数据
|
|
|
+ const nextLevel = parseInt(level) + 1;
|
|
|
+ if (state.show[nextLevel]) {
|
|
|
+ state.show[nextLevel].data = newChildren;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新所有显示数据
|
|
|
+ function updateAllShowData() {
|
|
|
+ // 更新第0层
|
|
|
+ if (state.show[0]) {
|
|
|
+ state.show[0].data = state.app;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新其他层级
|
|
|
+ for (const [level, showData] of Object.entries(state.show)) {
|
|
|
+ if (parseInt(level) > 0 && showData.pdata && showData.pdata.children) {
|
|
|
+ showData.data = showData.pdata.children;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
function initAppItem() {
|
|
|
let appItem = {
|
|
|
id: 0,
|
|
@@ -325,6 +627,7 @@
|
|
|
|
|
|
function handleSelect(checked, item) {
|
|
|
manualChecked = true;
|
|
|
+ isSelectingPermission = true; // 设置权限选择标志
|
|
|
|
|
|
// 计算所有子元素
|
|
|
doChecked(item, checked);
|
|
@@ -335,6 +638,11 @@
|
|
|
state.newIds = [];
|
|
|
getCheckedIds(state.app);
|
|
|
emit('update:modelValue', state.newIds);
|
|
|
+
|
|
|
+ // 延迟重置标志,确保watch执行完毕
|
|
|
+ nextTick(() => {
|
|
|
+ isSelectingPermission = false;
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
function doChecked(item, checked) {
|
|
@@ -375,32 +683,31 @@
|
|
|
|
|
|
async function onEnd(e, level, pdata) {
|
|
|
if (e.newIndex != e.oldIndex) {
|
|
|
- // 拖动元素修改weigh
|
|
|
- pdata.children[e.oldIndex].weigh = pdata.children[e.newIndex].weigh + 1;
|
|
|
- await admin.auth.access.edit(pdata.children[e.oldIndex].id, {
|
|
|
- weigh: pdata.children[e.oldIndex].weigh,
|
|
|
- });
|
|
|
+ try {
|
|
|
+ // 拖动元素修改seq(排序)
|
|
|
+ const draggedItem = pdata.children[e.oldIndex];
|
|
|
+ const targetItem = pdata.children[e.newIndex];
|
|
|
|
|
|
- // 更新数据
|
|
|
- const { data } = await admin.auth.access[props.type]({
|
|
|
- parent_id: pdata.id,
|
|
|
- role_id: props.role_id,
|
|
|
- });
|
|
|
- pdata.children = data;
|
|
|
- state.show[level].data = data;
|
|
|
+ // 更新排序值
|
|
|
+ const newSeq = targetItem._original?.seq ? targetItem._original.seq - 1 : e.newIndex - 1;
|
|
|
|
|
|
- if (level != 0) {
|
|
|
- state.show[level].index = null;
|
|
|
- }
|
|
|
- // 清除多余数据
|
|
|
- for (let key in state.show) {
|
|
|
- if (key > level) {
|
|
|
- delete state.show[key];
|
|
|
- }
|
|
|
+ // 调用编辑接口更新排序
|
|
|
+ await admin.auth.access.edit({
|
|
|
+ id: draggedItem.id,
|
|
|
+ seq: newSeq,
|
|
|
+ });
|
|
|
+
|
|
|
+ // 🎯 新方案:拖拽排序后保持选中状态刷新
|
|
|
+ console.log('✅ 拖拽排序成功,保持选中状态刷新数据');
|
|
|
+ await refreshDataKeepSelected();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('更新排序失败:', error);
|
|
|
+ // 如果更新失败,重新获取数据恢复状态
|
|
|
+ await getData();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- function onAdd(id = 0, level) {
|
|
|
+ function onAdd(id = null, level) {
|
|
|
useModal(
|
|
|
AccessEdit,
|
|
|
{
|
|
@@ -409,12 +716,16 @@
|
|
|
parent_id: id,
|
|
|
},
|
|
|
{
|
|
|
- confirm: async () => {
|
|
|
- const { data } = await admin.auth.access[props.type]({
|
|
|
- parent_id: id,
|
|
|
- role_id: props.role_id,
|
|
|
- });
|
|
|
- state.show[level].data = data;
|
|
|
+ confirm: async (result) => {
|
|
|
+ console.log('📝 新增回调数据:', result);
|
|
|
+
|
|
|
+ if (result && result.event === 'confirm') {
|
|
|
+ // 🎯 新方案:新增操作成功,保持选中状态刷新
|
|
|
+ console.log('✅ 新增操作成功,保持选中状态刷新数据');
|
|
|
+ await refreshDataKeepSelected();
|
|
|
+ } else {
|
|
|
+ console.warn('⚠️ 新增操作未确认,不执行任何操作');
|
|
|
+ }
|
|
|
},
|
|
|
},
|
|
|
);
|
|
@@ -428,22 +739,31 @@
|
|
|
id: id,
|
|
|
},
|
|
|
{
|
|
|
- confirm: async (res) => {
|
|
|
- state.show[level].data[index] = res.data;
|
|
|
+ confirm: async (result) => {
|
|
|
+ console.log('📝 编辑回调数据:', result);
|
|
|
+
|
|
|
+ if (result && result.event === 'confirm') {
|
|
|
+ // 🎯 新方案:编辑操作成功,保持选中状态刷新
|
|
|
+ console.log('✅ 编辑操作成功,保持选中状态刷新数据');
|
|
|
+ await refreshDataKeepSelected();
|
|
|
+ } else {
|
|
|
+ console.warn('⚠️ 编辑操作未确认,不执行任何操作');
|
|
|
+ }
|
|
|
},
|
|
|
},
|
|
|
);
|
|
|
}
|
|
|
|
|
|
async function onDelete(id, index, level) {
|
|
|
- const { code } = await admin.auth.access.delete(id);
|
|
|
- if (code == '200') {
|
|
|
- state.show[level].data.splice(index, 1);
|
|
|
- for (let key in state.show) {
|
|
|
- if (key > level) {
|
|
|
- delete state.show[key];
|
|
|
- }
|
|
|
+ try {
|
|
|
+ const { code } = await admin.auth.access.delete({ id });
|
|
|
+ if (code == '200') {
|
|
|
+ // 🎯 新方案:删除操作成功,保持选中状态刷新
|
|
|
+ console.log('✅ 删除操作成功,保持选中状态刷新数据');
|
|
|
+ await refreshDataKeepSelected();
|
|
|
}
|
|
|
+ } catch (error) {
|
|
|
+ console.error('删除权限失败:', error);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -464,7 +784,7 @@
|
|
|
|
|
|
watch(
|
|
|
() => props.role_id,
|
|
|
- (newVal) => {
|
|
|
+ () => {
|
|
|
if (props.isChangeParentId) {
|
|
|
state.checkedIds = [];
|
|
|
}
|
|
@@ -482,6 +802,260 @@
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
+
|
|
|
+ // 递归收集所有权限ID
|
|
|
+ const collectAllPermissionIds = (items) => {
|
|
|
+ const ids = [];
|
|
|
+ items.forEach((item) => {
|
|
|
+ ids.push(item.id);
|
|
|
+ if (item.children && item.children.length > 0) {
|
|
|
+ ids.push(...collectAllPermissionIds(item.children));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return ids;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 清空所有现有权限
|
|
|
+ const clearAllPermissions = async () => {
|
|
|
+ console.log('🗑️ 开始清空现有权限...');
|
|
|
+
|
|
|
+ // 获取现有权限树
|
|
|
+ const response = await admin.auth.access.getTree();
|
|
|
+ if (response.success && response.data && response.data.length > 0) {
|
|
|
+ // 收集所有权限ID
|
|
|
+ const allIds = collectAllPermissionIds(response.data);
|
|
|
+ console.log(`📋 找到 ${allIds.length} 个权限需要删除`);
|
|
|
+
|
|
|
+ // 逐个删除(从子级开始删除,避免外键约束问题)
|
|
|
+ for (let i = allIds.length - 1; i >= 0; i--) {
|
|
|
+ const id = allIds[i];
|
|
|
+ try {
|
|
|
+ const deleteResponse = await admin.auth.access.delete({ id });
|
|
|
+ if (deleteResponse.code === '200') {
|
|
|
+ console.log(`✅ 删除权限成功: ID ${id}`);
|
|
|
+ } else {
|
|
|
+ console.warn(`⚠️ 删除权限失败: ID ${id}`, deleteResponse);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.warn(`⚠️ 删除权限异常: ID ${id}`, error);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 延迟避免请求过快
|
|
|
+ await new Promise((resolve) => setTimeout(resolve, 100));
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('🎯 现有权限清空完成');
|
|
|
+ } else {
|
|
|
+ console.log('📝 没有找到现有权限,跳过清空步骤');
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 菜单权限初始化方法
|
|
|
+ const initMenuPermissions = async () => {
|
|
|
+ try {
|
|
|
+ // 确认对话框
|
|
|
+ await ElMessageBox.confirm(
|
|
|
+ '此操作将清空所有现有权限并重新初始化菜单权限,是否继续?\n⚠️ 注意:此操作不可逆!',
|
|
|
+ '初始化确认',
|
|
|
+ {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning',
|
|
|
+ dangerouslyUseHTMLString: true,
|
|
|
+ },
|
|
|
+ );
|
|
|
+
|
|
|
+ initLoading.value = true;
|
|
|
+ console.log('🚀 开始菜单权限初始化...');
|
|
|
+
|
|
|
+ // 1. 首先清空所有现有权限
|
|
|
+ await clearAllPermissions();
|
|
|
+
|
|
|
+ // 数据转换函数
|
|
|
+ const transformMenuData = (item, parentId = '') => ({
|
|
|
+ id: 0,
|
|
|
+ parentId: parentId,
|
|
|
+ name: item.title,
|
|
|
+ eName: item.name,
|
|
|
+ logo: item.icon || '',
|
|
|
+ composingKey: item.name,
|
|
|
+ type: item.type === 'menu' ? 0 : item.type === 'page' ? 1 : 2,
|
|
|
+ status: item.status === 'show' ? 0 : 1,
|
|
|
+ seq: item.weigh || 0,
|
|
|
+ url: '',
|
|
|
+ isAction: item.type === 'api' ? '1' : '0',
|
|
|
+ children: [],
|
|
|
+ createTime: '',
|
|
|
+ createUserId: '',
|
|
|
+ updateTime: '',
|
|
|
+ updateUserId: '',
|
|
|
+ });
|
|
|
+
|
|
|
+ // 根据权限名称和父ID查找新创建的权限ID
|
|
|
+ const findPermissionId = async (name, parentId = '') => {
|
|
|
+ console.log(`🔍 查找权限: ${name}, 父ID: "${parentId}"`);
|
|
|
+
|
|
|
+ const response = await admin.auth.access.getTree();
|
|
|
+ if (response.success && response.data) {
|
|
|
+ console.log(`🔍 获取到的权限树数据:`, response.data);
|
|
|
+
|
|
|
+ // 🎯 关键修复:对获取到的数据进行转换,确保有_original字段
|
|
|
+ const transformedData = Array.isArray(response.data)
|
|
|
+ ? transformPermissionData(response.data)
|
|
|
+ : [];
|
|
|
+
|
|
|
+ const findInTree = (items, level = 0) => {
|
|
|
+ const indent = ' '.repeat(level);
|
|
|
+
|
|
|
+ for (const item of items) {
|
|
|
+ console.log(`${indent}当前检查的item:`, item);
|
|
|
+
|
|
|
+ // 现在可以安全使用_original字段了
|
|
|
+ const originalData = item._original || {};
|
|
|
+ const itemName = originalData.name || '';
|
|
|
+ const itemParentId = originalData.parentId || '';
|
|
|
+
|
|
|
+ // 备用匹配:如果原始数据没有name,尝试使用转换后的title
|
|
|
+ const fallbackName = item.title || '';
|
|
|
+
|
|
|
+ console.log(
|
|
|
+ `${indent}检查权限: 名称="${itemName}", 备用名称="${fallbackName}", 父ID="${itemParentId}"`,
|
|
|
+ );
|
|
|
+ console.log(`${indent}原始数据:`, originalData);
|
|
|
+ console.log(`${indent}item._original存在:`, !!item._original);
|
|
|
+
|
|
|
+ // 匹配权限名称和父ID(优先使用原始数据的name字段,备用title字段)
|
|
|
+ const nameMatches = itemName === name || (itemName === '' && fallbackName === name);
|
|
|
+
|
|
|
+ if (nameMatches) {
|
|
|
+ // 对于顶级权限,parentId可能是空字符串、null、undefined或0
|
|
|
+ const isTopLevel = !parentId || parentId === '' || parentId === '0';
|
|
|
+ const itemIsTopLevel = !itemParentId || itemParentId === '' || itemParentId === '0';
|
|
|
+
|
|
|
+ console.log(
|
|
|
+ `${indent}🔍 匹配检查: 查找="${name}" vs 权限="${itemName}" vs 备用="${fallbackName}"`,
|
|
|
+ );
|
|
|
+ console.log(`${indent}🔍 父ID检查: 查找="${parentId}" vs 权限="${itemParentId}"`);
|
|
|
+ console.log(
|
|
|
+ `${indent}🔍 顶级检查: 查找顶级=${isTopLevel}, 权限顶级=${itemIsTopLevel}`,
|
|
|
+ );
|
|
|
+
|
|
|
+ // 🎯 修复:父ID匹配逻辑
|
|
|
+ const parentIdMatches =
|
|
|
+ (isTopLevel && itemIsTopLevel) || String(itemParentId) === String(parentId);
|
|
|
+
|
|
|
+ console.log(`${indent}🔍 父ID匹配结果: ${parentIdMatches}`);
|
|
|
+ console.log(
|
|
|
+ `${indent}🔍 详细比较: "${String(itemParentId)}" === "${String(parentId)}" = ${String(itemParentId) === String(parentId)}`,
|
|
|
+ );
|
|
|
+
|
|
|
+ if (parentIdMatches) {
|
|
|
+ const matchedName = itemName || fallbackName;
|
|
|
+ console.log(`${indent}✅ 找到匹配权限: ${matchedName}, ID: ${item.id}`);
|
|
|
+ return item.id;
|
|
|
+ } else {
|
|
|
+ console.log(`${indent}❌ 父ID不匹配,跳过此权限`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (item.children && item.children.length > 0) {
|
|
|
+ const found = findInTree(item.children, level + 1);
|
|
|
+ if (found) return found;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ };
|
|
|
+
|
|
|
+ const result = findInTree(transformedData); // 🎯 使用转换后的数据
|
|
|
+ console.log(`🎯 查找结果: ${result ? `找到ID ${result}` : '未找到'}`);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('❌ 获取权限树失败');
|
|
|
+ return null;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 递归创建函数(修改版:处理后端不返回ID的情况)
|
|
|
+ const createRecursive = async (items, parentId = '', level = 0) => {
|
|
|
+ const indent = ' '.repeat(level);
|
|
|
+
|
|
|
+ for (let i = 0; i < items.length; i++) {
|
|
|
+ const item = items[i];
|
|
|
+
|
|
|
+ console.log(`${indent}📝 创建权限: ${item.title} (${item.type}), 父ID: "${parentId}"`);
|
|
|
+
|
|
|
+ // 创建当前权限
|
|
|
+ const permissionData = transformMenuData(item, parentId);
|
|
|
+ console.log(`${indent}📋 提交数据:`, permissionData);
|
|
|
+
|
|
|
+ const response = await admin.auth.access.add(permissionData);
|
|
|
+
|
|
|
+ if (response.code === '200') {
|
|
|
+ console.log(`${indent}✅ 创建成功: ${item.title}`);
|
|
|
+
|
|
|
+ // 如果有子权限,需要先查找新创建的权限ID
|
|
|
+ if (item.children && item.children.length > 0) {
|
|
|
+ console.log(
|
|
|
+ `${indent}🔍 需要创建 ${item.children.length} 个子权限,正在查找父权限ID...`,
|
|
|
+ );
|
|
|
+
|
|
|
+ // 等待一下确保数据已保存
|
|
|
+ await new Promise((resolve) => setTimeout(resolve, 500));
|
|
|
+
|
|
|
+ // 查找新创建的权限ID
|
|
|
+ const newId = await findPermissionId(item.title, parentId);
|
|
|
+
|
|
|
+ if (newId) {
|
|
|
+ console.log(`${indent}🎯 找到权限ID: ${newId}`);
|
|
|
+ console.log(`${indent}📁 开始创建子权限...`);
|
|
|
+ await createRecursive(item.children, newId, level + 1);
|
|
|
+ } else {
|
|
|
+ console.error(`${indent}❌ 无法找到新创建的权限ID: ${item.title}`);
|
|
|
+ console.error(`${indent}🔍 查找条件: 名称="${item.title}", 父ID="${parentId}"`);
|
|
|
+
|
|
|
+ // 尝试再次查找,增加等待时间
|
|
|
+ console.log(`${indent}⏳ 等待更长时间后重试...`);
|
|
|
+ await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
|
+
|
|
|
+ const retryId = await findPermissionId(item.title, parentId);
|
|
|
+ if (retryId) {
|
|
|
+ console.log(`${indent}🎯 重试成功,找到权限ID: ${retryId}`);
|
|
|
+ await createRecursive(item.children, retryId, level + 1);
|
|
|
+ } else {
|
|
|
+ throw new Error(`无法找到新创建的权限ID: ${item.title}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ console.error(`${indent}❌ 创建失败: ${item.title}`, response);
|
|
|
+ throw new Error(`创建权限失败: ${item.title} - ${response.message || '未知错误'}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 延迟避免请求过快
|
|
|
+ await new Promise((resolve) => setTimeout(resolve, 300));
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 2. 开始创建新的权限结构
|
|
|
+ console.log('🏗️ 开始创建新的权限结构...');
|
|
|
+ const menuData = menuRulesData.data.menu;
|
|
|
+ await createRecursive(menuData);
|
|
|
+
|
|
|
+ console.log('🎉 菜单权限初始化完成!');
|
|
|
+ ElMessage.success('菜单权限初始化成功!');
|
|
|
+
|
|
|
+ // 3. 重新获取数据刷新界面
|
|
|
+ await getData();
|
|
|
+ } catch (error) {
|
|
|
+ if (error.message !== 'cancel') {
|
|
|
+ console.error('💥 初始化失败:', error);
|
|
|
+ ElMessage.error(`初始化失败: ${error.message}`);
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ initLoading.value = false;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
// 无论是新增还是编辑都需要加载权限数据
|
|
|
getData();
|