|
|
@@ -7,77 +7,47 @@
|
|
|
<span>{{ val.pdata?.title }}</span>
|
|
|
<slot name="add" :pdata="val.pdata">
|
|
|
<template v-if="val.pdata">
|
|
|
- <el-checkbox
|
|
|
- v-if="multiple"
|
|
|
- class="sa-m-r-8"
|
|
|
- v-model="val.pdata.checked"
|
|
|
- :indeterminate="val.pdata.indeterminate"
|
|
|
- label="全选"
|
|
|
- @update:model-value="handleSelect($event, val.pdata)"
|
|
|
- />
|
|
|
+ <el-checkbox v-if="multiple" class="sa-m-r-8" v-model="val.pdata.checked"
|
|
|
+ :indeterminate="val.pdata.indeterminate" :label="t('modules.auth.selectAll')"
|
|
|
+ @update:model-value="handleSelect($event, val.pdata)" />
|
|
|
</template>
|
|
|
<template v-if="type == 'list'">
|
|
|
- <el-button class="is-link" type="primary" @click="onAdd(val.pdata.id, level)"
|
|
|
- >+添加</el-button
|
|
|
- >
|
|
|
+ <el-button class="is-link" type="primary" @click="onAdd(val.pdata.id, level)">{{
|
|
|
+ t('modules.auth.addPermission') }}</el-button>
|
|
|
<!-- <el-button
|
|
|
class="is-link"
|
|
|
type="success"
|
|
|
@click="initMenuPermissions"
|
|
|
:loading="initLoading"
|
|
|
>
|
|
|
- 初始化菜单
|
|
|
+ {{ t('modules.auth.initMenu') }}
|
|
|
</el-button> -->
|
|
|
</template>
|
|
|
</slot>
|
|
|
</div>
|
|
|
<template v-if="val.data?.length > 0">
|
|
|
- <sa-draggable
|
|
|
- v-model="val.data"
|
|
|
- :animation="300"
|
|
|
- handle=".sortable-drag"
|
|
|
- item-key="element"
|
|
|
- @end="onEnd($event, level, val.pdata)"
|
|
|
- >
|
|
|
+ <sa-draggable v-model="val.data" :animation="300" handle=".sortable-drag" item-key="element"
|
|
|
+ @end="onEnd($event, level, val.pdata)">
|
|
|
<template #item="{ element, index }">
|
|
|
- <div
|
|
|
- :class="['node', 'sa-flex sa-row-between', val.index == index ? 'is-active' : '']"
|
|
|
- @click="onClick(element, index, level)"
|
|
|
- >
|
|
|
+ <div :class="['node', 'sa-flex sa-row-between', val.index == index ? 'is-active' : '']"
|
|
|
+ @click="onClick(element, index, level)">
|
|
|
<!-- multiple -->
|
|
|
- <el-checkbox
|
|
|
- v-if="multiple"
|
|
|
- class="sa-m-r-8"
|
|
|
- v-model="element.checked"
|
|
|
- :indeterminate="element.indeterminate"
|
|
|
- @click.stop
|
|
|
- @change="handleSelect($event, element)"
|
|
|
- />
|
|
|
+ <el-checkbox v-if="multiple" class="sa-m-r-8" v-model="element.checked"
|
|
|
+ :indeterminate="element.indeterminate" @click.stop @change="handleSelect($event, element)" />
|
|
|
<slot class="label" :data="element" :level="level">
|
|
|
<div class="item sa-flex sa-row-between">
|
|
|
<div class="sa-flex">
|
|
|
- <sa-svg
|
|
|
- v-if="type == 'list'"
|
|
|
- class="sortable-drag sa-m-r-8"
|
|
|
- name="sa-round"
|
|
|
- ></sa-svg>
|
|
|
+ <sa-svg v-if="type == 'list'" class="sortable-drag sa-m-r-8" name="sa-round"></sa-svg>
|
|
|
<sa-icon class="icon sa-m-r-4" :icon="element.icon" size="16" />
|
|
|
<div>{{ element.title }}</div>
|
|
|
</div>
|
|
|
<div v-if="type == 'list'" class="sa-flex">
|
|
|
- <el-icon
|
|
|
- class="edit sa-m-r-8"
|
|
|
- @click.stop="onEdit(element.id, index, level)"
|
|
|
- >
|
|
|
+ <el-icon class="edit sa-m-r-8" @click.stop="onEdit(element.id, index, level)">
|
|
|
<Edit />
|
|
|
</el-icon>
|
|
|
- <el-popconfirm
|
|
|
- width="fit-content"
|
|
|
- confirm-button-text="确认"
|
|
|
- cancel-button-text="取消"
|
|
|
- title="确认删除这条记录?"
|
|
|
- @confirm="onDelete(element.id, index, level)"
|
|
|
- >
|
|
|
+ <el-popconfirm width="fit-content" :confirm-button-text="t('common.confirm')"
|
|
|
+ :cancel-button-text="t('common.cancel')" :title="t('modules.auth.confirmDeleteRecord')"
|
|
|
+ @confirm="onDelete(element.id, index, level)">
|
|
|
<template #reference>
|
|
|
<el-icon class="delete sa-m-r-8" @click.stop>
|
|
|
<Delete />
|
|
|
@@ -97,7 +67,7 @@
|
|
|
</sa-draggable>
|
|
|
</template>
|
|
|
<template v-if="!val.loading && val.data.length == 0">
|
|
|
- <div class="empty">暂无数据</div>
|
|
|
+ <div class="empty">{{ t('modules.auth.noData') }}</div>
|
|
|
</template>
|
|
|
</el-scrollbar>
|
|
|
</template>
|
|
|
@@ -106,1007 +76,1023 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
- 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';
|
|
|
- import AccessEdit from '../edit.vue';
|
|
|
- const emit = defineEmits(['update:modelValue']);
|
|
|
- const props = defineProps({
|
|
|
- type: String,
|
|
|
- isChangeParentId: Boolean,
|
|
|
- role_id: {
|
|
|
- type: [String, Number],
|
|
|
- default: 0,
|
|
|
- },
|
|
|
- modelValue: {
|
|
|
- type: Array,
|
|
|
- default: [],
|
|
|
- },
|
|
|
- multiple: {
|
|
|
- type: Boolean,
|
|
|
- default: false,
|
|
|
- },
|
|
|
- });
|
|
|
-
|
|
|
- let manualChecked = false;
|
|
|
- let isSelectingPermission = false; // 新增标志,表示正在进行权限选择操作
|
|
|
-
|
|
|
- const state = reactive({
|
|
|
- loading: false,
|
|
|
- app: [],
|
|
|
- checkedIds: props.modelValue,
|
|
|
- newIds: [],
|
|
|
- show: {},
|
|
|
- });
|
|
|
-
|
|
|
- // 初始化loading状态
|
|
|
- const initLoading = ref(false);
|
|
|
- watch(
|
|
|
- () => props.modelValue,
|
|
|
- (newValue) => {
|
|
|
- // 如果正在进行权限选择操作,只更新checkedIds,不触发其他计算
|
|
|
- if (isSelectingPermission) {
|
|
|
- state.checkedIds = newValue || [];
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
+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 { useI18n } from 'vue-i18n';
|
|
|
+
|
|
|
+import { isEmpty } from 'lodash';
|
|
|
+import AccessEdit from '../edit.vue';
|
|
|
+
|
|
|
+const { t } = useI18n();
|
|
|
+const emit = defineEmits(['update:modelValue']);
|
|
|
+const props = defineProps({
|
|
|
+ type: String,
|
|
|
+ isChangeParentId: Boolean,
|
|
|
+ role_id: {
|
|
|
+ type: [String, Number],
|
|
|
+ default: 0,
|
|
|
+ },
|
|
|
+ modelValue: {
|
|
|
+ type: Array,
|
|
|
+ default: [],
|
|
|
+ },
|
|
|
+ multiple: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+let manualChecked = false;
|
|
|
+let isSelectingPermission = false; // 新增标志,表示正在进行权限选择操作
|
|
|
+
|
|
|
+const state = reactive({
|
|
|
+ loading: false,
|
|
|
+ app: [],
|
|
|
+ checkedIds: props.modelValue,
|
|
|
+ newIds: [],
|
|
|
+ show: {},
|
|
|
+});
|
|
|
+
|
|
|
+// 初始化loading状态
|
|
|
+const initLoading = ref(false);
|
|
|
+watch(
|
|
|
+ () => props.modelValue,
|
|
|
+ (newValue) => {
|
|
|
+ // 如果正在进行权限选择操作,只更新checkedIds,不触发其他计算
|
|
|
+ if (isSelectingPermission) {
|
|
|
state.checkedIds = newValue || [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- // 当modelValue变化时,重新计算权限状态(无论数据是否为空都要计算)
|
|
|
- if (state.app.length > 0) {
|
|
|
- let appItem = initAppItem();
|
|
|
- initCalculate(state.app, appItem);
|
|
|
- calculateShow(appItem);
|
|
|
- }
|
|
|
- },
|
|
|
- { immediate: true },
|
|
|
- );
|
|
|
-
|
|
|
- // 监听app数据变化,确保新增时也能显示
|
|
|
- watch(
|
|
|
- () => state.app,
|
|
|
- (newApp) => {
|
|
|
- // 如果正在进行权限选择操作,跳过所有处理
|
|
|
- if (isSelectingPermission) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ state.checkedIds = newValue || [];
|
|
|
|
|
|
- // 无论数据是否为空都要初始化显示状态
|
|
|
+ // 当modelValue变化时,重新计算权限状态(无论数据是否为空都要计算)
|
|
|
+ if (state.app.length > 0) {
|
|
|
let appItem = initAppItem();
|
|
|
- if (newApp && newApp.length > 0) {
|
|
|
- initCalculate(newApp, appItem);
|
|
|
- }
|
|
|
-
|
|
|
+ initCalculate(state.app, appItem);
|
|
|
calculateShow(appItem);
|
|
|
- },
|
|
|
- );
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { immediate: true },
|
|
|
+);
|
|
|
+
|
|
|
+// 监听app数据变化,确保新增时也能显示
|
|
|
+watch(
|
|
|
+ () => state.app,
|
|
|
+ (newApp) => {
|
|
|
+ // 如果正在进行权限选择操作,跳过所有处理
|
|
|
+ if (isSelectingPermission) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- // 数据转换函数:将新接口数据转换为组件需要的格式
|
|
|
- function transformPermissionData(data) {
|
|
|
- if (!Array.isArray(data)) {
|
|
|
- console.warn('transformPermissionData: data is not an array', data);
|
|
|
- return [];
|
|
|
+ // 无论数据是否为空都要初始化显示状态
|
|
|
+ let appItem = initAppItem();
|
|
|
+ if (newApp && newApp.length > 0) {
|
|
|
+ initCalculate(newApp, appItem);
|
|
|
}
|
|
|
|
|
|
- const result = data.map((item) => {
|
|
|
- const transformed = {
|
|
|
- id: item.id,
|
|
|
- title: item.name || item.label, // 优先使用name,备用label
|
|
|
- icon: item.logo || 'menu', // 使用logo作为icon,默认为menu
|
|
|
- children:
|
|
|
- item.children && Array.isArray(item.children)
|
|
|
- ? transformPermissionData(item.children)
|
|
|
- : [],
|
|
|
- // 根据hasRelevance字段设置选中状态
|
|
|
- checked: item.hasRelevance === '1',
|
|
|
- indeterminate: false,
|
|
|
- // 保留原始数据以备后用
|
|
|
- _original: item,
|
|
|
- };
|
|
|
-
|
|
|
- console.log('🔄 transformPermissionData - 转换结果:', transformed);
|
|
|
- return transformed;
|
|
|
- });
|
|
|
+ calculateShow(appItem);
|
|
|
+ },
|
|
|
+);
|
|
|
|
|
|
- console.log('🔄 transformPermissionData - 最终结果:', result);
|
|
|
- return result;
|
|
|
+// 数据转换函数:将新接口数据转换为组件需要的格式
|
|
|
+function transformPermissionData(data) {
|
|
|
+ if (!Array.isArray(data)) {
|
|
|
+ console.warn('transformPermissionData: data is not an array', data);
|
|
|
+ return [];
|
|
|
}
|
|
|
|
|
|
- async function getData() {
|
|
|
- state.loading = true;
|
|
|
+ const result = data.map((item) => {
|
|
|
+ const transformed = {
|
|
|
+ id: item.id,
|
|
|
+ title: item.name || item.label, // 优先使用name,备用label
|
|
|
+ icon: item.logo || 'menu', // 使用logo作为icon,默认为menu
|
|
|
+ children:
|
|
|
+ item.children && Array.isArray(item.children)
|
|
|
+ ? transformPermissionData(item.children)
|
|
|
+ : [],
|
|
|
+ // 根据hasRelevance字段设置选中状态
|
|
|
+ checked: item.hasRelevance === '1',
|
|
|
+ indeterminate: false,
|
|
|
+ // 保留原始数据以备后用
|
|
|
+ _original: item,
|
|
|
+ };
|
|
|
|
|
|
- try {
|
|
|
- let permissionData = [];
|
|
|
+ console.log('🔄 transformPermissionData - 转换结果:', transformed);
|
|
|
+ return transformed;
|
|
|
+ });
|
|
|
|
|
|
- // 根据type类型获取不同的数据
|
|
|
- if (props.type === 'select' && props.role_id) {
|
|
|
- // 权限选择模式且有角色ID,获取角色详情中的权限数据
|
|
|
- console.log('🔍 获取角色权限数据, role_id:', props.role_id);
|
|
|
- const roleResponse = await admin.auth.role.detail(props.role_id);
|
|
|
+ console.log('🔄 transformPermissionData - 最终结果:', result);
|
|
|
+ return result;
|
|
|
+}
|
|
|
|
|
|
- if (roleResponse.code == 200 && roleResponse.data && roleResponse.data.permissions) {
|
|
|
- console.log('🔍 getData - 角色权限数据:', roleResponse.data.permissions);
|
|
|
- permissionData = roleResponse.data.permissions;
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 其他模式,获取完整权限树
|
|
|
- const response = await admin.auth.access.getTree();
|
|
|
+async function getData() {
|
|
|
+ state.loading = true;
|
|
|
|
|
|
- if (response.success && response.data !== null && response.data !== undefined) {
|
|
|
- console.log('🔍 getData - 权限树数据:', response.data);
|
|
|
- permissionData = response.data;
|
|
|
- }
|
|
|
+ try {
|
|
|
+ let permissionData = [];
|
|
|
+
|
|
|
+ // 根据type类型获取不同的数据
|
|
|
+ if (props.type === 'select' && props.role_id) {
|
|
|
+ // 权限选择模式且有角色ID,获取角色详情中的权限数据
|
|
|
+ console.log('🔍 获取角色权限数据, role_id:', props.role_id);
|
|
|
+ const roleResponse = await admin.auth.role.detail(props.role_id);
|
|
|
+
|
|
|
+ if (roleResponse.code == 200 && roleResponse.data && roleResponse.data.permissions) {
|
|
|
+ console.log('🔍 getData - 角色权限数据:', roleResponse.data.permissions);
|
|
|
+ permissionData = roleResponse.data.permissions;
|
|
|
}
|
|
|
+ } else {
|
|
|
+ // 其他模式,获取完整权限树
|
|
|
+ const response = await admin.auth.access.getTree();
|
|
|
|
|
|
- // 转换数据格式(即使是空数组也要处理)
|
|
|
- const transformedData = Array.isArray(permissionData)
|
|
|
- ? transformPermissionData(permissionData)
|
|
|
- : [];
|
|
|
+ if (response.success && response.data !== null && response.data !== undefined) {
|
|
|
+ console.log('🔍 getData - 权限树数据:', response.data);
|
|
|
+ permissionData = response.data;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- console.log('🔍 getData - 转换后数据:', transformedData);
|
|
|
+ // 转换数据格式(即使是空数组也要处理)
|
|
|
+ const transformedData = Array.isArray(permissionData)
|
|
|
+ ? transformPermissionData(permissionData)
|
|
|
+ : [];
|
|
|
|
|
|
- state.app = transformedData;
|
|
|
+ console.log('🔍 getData - 转换后数据:', transformedData);
|
|
|
|
|
|
- // 初始化显示状态(无论数据是否为空都要初始化)
|
|
|
- let appItem = initAppItem();
|
|
|
- if (transformedData.length > 0) {
|
|
|
- initCalculate(transformedData, appItem);
|
|
|
- }
|
|
|
+ state.app = transformedData;
|
|
|
|
|
|
- // 如果不是权限选择操作导致的变化,才重置展开状态
|
|
|
- if (!isSelectingPermission) {
|
|
|
- calculateShow(appItem);
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('获取权限数据异常:', error);
|
|
|
- state.app = [];
|
|
|
+ // 初始化显示状态(无论数据是否为空都要初始化)
|
|
|
+ let appItem = initAppItem();
|
|
|
+ if (transformedData.length > 0) {
|
|
|
+ initCalculate(transformedData, appItem);
|
|
|
+ }
|
|
|
|
|
|
- // 异常情况下也要初始化显示状态
|
|
|
- let appItem = initAppItem();
|
|
|
- if (!isSelectingPermission) {
|
|
|
- calculateShow(appItem);
|
|
|
- }
|
|
|
+ // 如果不是权限选择操作导致的变化,才重置展开状态
|
|
|
+ if (!isSelectingPermission) {
|
|
|
+ calculateShow(appItem);
|
|
|
}
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取权限数据异常:', error);
|
|
|
+ state.app = [];
|
|
|
|
|
|
- state.loading = false;
|
|
|
+ // 异常情况下也要初始化显示状态
|
|
|
+ let appItem = initAppItem();
|
|
|
+ if (!isSelectingPermission) {
|
|
|
+ calculateShow(appItem);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // 🎯 新方案:保持选中状态的数据刷新函数
|
|
|
- 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})`,
|
|
|
- );
|
|
|
- }
|
|
|
+ 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);
|
|
|
+ console.log('💾 保存的选中状态:', selectedStates);
|
|
|
|
|
|
- // 2. 重新获取数据(这会重置state.show)
|
|
|
- await getData();
|
|
|
+ // 2. 重新获取数据(这会重置state.show)
|
|
|
+ await getData();
|
|
|
|
|
|
- // 3. 恢复选中状态
|
|
|
- if (selectedStates.length > 0) {
|
|
|
- console.log('🔄 开始恢复选中状态...');
|
|
|
+ // 3. 恢复选中状态
|
|
|
+ if (selectedStates.length > 0) {
|
|
|
+ console.log('🔄 开始恢复选中状态...');
|
|
|
|
|
|
- // 逐层恢复选中状态
|
|
|
- for (const selectedState of selectedStates) {
|
|
|
- const { level, itemId, itemTitle } = selectedState;
|
|
|
- console.log(`🔄 恢复第${level}层选中状态: ${itemTitle} (ID: ${itemId})`);
|
|
|
+ // 逐层恢复选中状态
|
|
|
+ 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);
|
|
|
+ // 在当前层级查找目标项目
|
|
|
+ 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})`);
|
|
|
+ if (foundIndex !== -1) {
|
|
|
+ const foundItem = currentLevelData[foundIndex];
|
|
|
+ console.log(`✅ 找到目标项目: ${foundItem.title} (索引: ${foundIndex})`);
|
|
|
|
|
|
- // 使用calculateShow来正确展开并选中这一层
|
|
|
- await calculateShow(foundItem, foundIndex, level);
|
|
|
+ // 使用calculateShow来正确展开并选中这一层
|
|
|
+ await calculateShow(foundItem, foundIndex, level);
|
|
|
|
|
|
- console.log(`✅ 第${level}层选中状态恢复完成`);
|
|
|
- } else {
|
|
|
- console.log(`⚠️ 第${level}层找不到目标项目 ID: ${itemId}`);
|
|
|
- break; // 如果某一层找不到,停止恢复
|
|
|
- }
|
|
|
+ console.log(`✅ 第${level}层选中状态恢复完成`);
|
|
|
+ } else {
|
|
|
+ console.log(`⚠️ 第${level}层找不到目标项目 ID: ${itemId}`);
|
|
|
+ break; // 如果某一层找不到,停止恢复
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- console.log('✅ 选中状态恢复完成');
|
|
|
+ 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);
|
|
|
}
|
|
|
|
|
|
- // 🎯 新方案:局部更新数据函数
|
|
|
- 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('✅ 局部数据更新完成');
|
|
|
+}
|
|
|
|
|
|
- console.log('✅ 局部数据更新完成');
|
|
|
+// 处理新增操作
|
|
|
+async function handleLocalAdd(newData, parentId) {
|
|
|
+ console.log(`📝 处理新增: 父级ID=${parentId}`, newData);
|
|
|
+
|
|
|
+ if (!newData) {
|
|
|
+ console.error('❌ 新增数据为空');
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- // 处理新增操作
|
|
|
- async function handleLocalAdd(newData, parentId) {
|
|
|
- console.log(`📝 处理新增: 父级ID=${parentId}`, newData);
|
|
|
+ // 转换新数据格式
|
|
|
+ const transformedNewData = {
|
|
|
+ id: newData.id,
|
|
|
+ title: newData.name || '新权限',
|
|
|
+ icon: newData.logo || 'menu',
|
|
|
+ children: [],
|
|
|
+ _original: newData,
|
|
|
+ };
|
|
|
|
|
|
- if (!newData) {
|
|
|
- console.error('❌ 新增数据为空');
|
|
|
- return;
|
|
|
+ if (!parentId || parentId === '' || parentId === '0') {
|
|
|
+ // 添加到根级
|
|
|
+ state.app.push(transformedNewData);
|
|
|
+ // 更新第0层的数据
|
|
|
+ if (state.show[0]) {
|
|
|
+ state.show[0].data = state.app;
|
|
|
}
|
|
|
-
|
|
|
- // 转换新数据格式
|
|
|
- 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 = [];
|
|
|
}
|
|
|
- console.log('✅ 已添加到根级');
|
|
|
- } else {
|
|
|
- // 添加到指定父级
|
|
|
- const parentItem = findItemById(state.app, parentId);
|
|
|
- if (parentItem) {
|
|
|
- if (!parentItem.children) {
|
|
|
- parentItem.children = [];
|
|
|
- }
|
|
|
- parentItem.children.push(transformedNewData);
|
|
|
+ parentItem.children.push(transformedNewData);
|
|
|
|
|
|
- // 更新对应层级的显示数据
|
|
|
- updateShowDataForParent(parentId, parentItem.children);
|
|
|
- console.log(`✅ 已添加到父级 ${parentId}`);
|
|
|
- } else {
|
|
|
- console.error(`❌ 找不到父级项目 ID: ${parentId}`);
|
|
|
- }
|
|
|
+ // 更新对应层级的显示数据
|
|
|
+ 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;
|
|
|
- }
|
|
|
+// 处理编辑操作
|
|
|
+async function handleLocalEdit(newData, targetId) {
|
|
|
+ console.log(`✏️ 处理编辑: 目标ID=${targetId}`, newData);
|
|
|
|
|
|
- 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}`);
|
|
|
- }
|
|
|
+ if (!newData) {
|
|
|
+ console.error('❌ 编辑数据为空');
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- // 处理删除操作
|
|
|
- async function handleLocalDelete(targetId) {
|
|
|
- console.log(`🗑️ 处理删除: 目标ID=${targetId}`);
|
|
|
+ // 🔧 验证数据格式
|
|
|
+ if (!newData.name && !newData.id) {
|
|
|
+ console.error('❌ 编辑数据格式不正确,缺少必要字段:', newData);
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- const result = removeItemById(state.app, targetId);
|
|
|
- if (result.success) {
|
|
|
- // 更新所有相关的显示数据
|
|
|
- updateAllShowData();
|
|
|
- console.log(`✅ 已删除项目 ${targetId}`);
|
|
|
- } else {
|
|
|
- console.error(`❌ 找不到要删除的项目 ID: ${targetId}`);
|
|
|
- }
|
|
|
+ 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;
|
|
|
- }
|
|
|
+// 递归查找项目
|
|
|
+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 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;
|
|
|
+ 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;
|
|
|
- }
|
|
|
+// 更新所有显示数据
|
|
|
+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;
|
|
|
- }
|
|
|
+ // 更新其他层级
|
|
|
+ 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,
|
|
|
- title: '应用',
|
|
|
- checked: true,
|
|
|
- indeterminate: false,
|
|
|
- children: state.app,
|
|
|
- };
|
|
|
- let allData = [];
|
|
|
- flattenData(state.app, allData);
|
|
|
- if (state.checkedIds.length == 0) {
|
|
|
- appItem.checked = false;
|
|
|
+}
|
|
|
+
|
|
|
+function initAppItem() {
|
|
|
+ let appItem = {
|
|
|
+ id: 0,
|
|
|
+ title: t('modules.auth.application'),
|
|
|
+ checked: true,
|
|
|
+ indeterminate: false,
|
|
|
+ children: state.app,
|
|
|
+ };
|
|
|
+ let allData = [];
|
|
|
+ flattenData(state.app, allData);
|
|
|
+ if (state.checkedIds.length == 0) {
|
|
|
+ appItem.checked = false;
|
|
|
+ appItem.indeterminate = false;
|
|
|
+ } else {
|
|
|
+ if (allData.length == state.checkedIds.length) {
|
|
|
+ appItem.checked = true;
|
|
|
appItem.indeterminate = false;
|
|
|
} else {
|
|
|
- if (allData.length == state.checkedIds.length) {
|
|
|
- appItem.checked = true;
|
|
|
- appItem.indeterminate = false;
|
|
|
- } else {
|
|
|
- appItem.checked = false;
|
|
|
- appItem.indeterminate = true;
|
|
|
- }
|
|
|
+ appItem.checked = false;
|
|
|
+ appItem.indeterminate = true;
|
|
|
}
|
|
|
- return appItem;
|
|
|
}
|
|
|
+ return appItem;
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化选中数据
|
|
|
+function initCalculate(data, parent = {}) {
|
|
|
+ data.forEach((item) => {
|
|
|
+ item.parent = parent;
|
|
|
+
|
|
|
+ // 兼容数字和字符串ID的比较
|
|
|
+ const itemIdStr = String(item.id);
|
|
|
+ const itemIdNum = Number(item.id);
|
|
|
+ const isChecked = state.checkedIds.some((checkedId) => {
|
|
|
+ const checkedIdStr = String(checkedId);
|
|
|
+ const checkedIdNum = Number(checkedId);
|
|
|
+ return checkedIdStr === itemIdStr || checkedIdNum === itemIdNum;
|
|
|
+ });
|
|
|
|
|
|
- // 初始化选中数据
|
|
|
- function initCalculate(data, parent = {}) {
|
|
|
- data.forEach((item) => {
|
|
|
- item.parent = parent;
|
|
|
-
|
|
|
- // 兼容数字和字符串ID的比较
|
|
|
- const itemIdStr = String(item.id);
|
|
|
- const itemIdNum = Number(item.id);
|
|
|
- const isChecked = state.checkedIds.some((checkedId) => {
|
|
|
- const checkedIdStr = String(checkedId);
|
|
|
- const checkedIdNum = Number(checkedId);
|
|
|
- return checkedIdStr === itemIdStr || checkedIdNum === itemIdNum;
|
|
|
- });
|
|
|
+ // 先递归处理子项
|
|
|
+ if (!isEmpty(item.children)) {
|
|
|
+ initCalculate(item.children, item);
|
|
|
|
|
|
- // 先递归处理子项
|
|
|
- if (!isEmpty(item.children)) {
|
|
|
- initCalculate(item.children, item);
|
|
|
-
|
|
|
- // 检查子项的选中状态
|
|
|
- const checkedChildren = item.children.filter((child) => child.checked);
|
|
|
- const indeterminateChildren = item.children.filter((child) => child.indeterminate);
|
|
|
-
|
|
|
- // 父级状态完全根据子级状态来判断,不考虑父级本身是否在选中列表中
|
|
|
- if (checkedChildren.length === item.children.length) {
|
|
|
- // 所有子项都选中 - 父级全选
|
|
|
- item.checked = true;
|
|
|
- item.indeterminate = false;
|
|
|
- } else if (checkedChildren.length > 0 || indeterminateChildren.length > 0) {
|
|
|
- // 部分子项选中或有半选状态 - 父级半选
|
|
|
- item.checked = false;
|
|
|
- item.indeterminate = true;
|
|
|
- } else {
|
|
|
- // 没有子项选中 - 父级未选
|
|
|
- item.checked = false;
|
|
|
- item.indeterminate = false;
|
|
|
- }
|
|
|
+ // 检查子项的选中状态
|
|
|
+ const checkedChildren = item.children.filter((child) => child.checked);
|
|
|
+ const indeterminateChildren = item.children.filter((child) => child.indeterminate);
|
|
|
+
|
|
|
+ // 父级状态完全根据子级状态来判断,不考虑父级本身是否在选中列表中
|
|
|
+ if (checkedChildren.length === item.children.length) {
|
|
|
+ // 所有子项都选中 - 父级全选
|
|
|
+ item.checked = true;
|
|
|
+ item.indeterminate = false;
|
|
|
+ } else if (checkedChildren.length > 0 || indeterminateChildren.length > 0) {
|
|
|
+ // 部分子项选中或有半选状态 - 父级半选
|
|
|
+ item.checked = false;
|
|
|
+ item.indeterminate = true;
|
|
|
} else {
|
|
|
- // 叶子节点,直接根据是否在选中列表中设置状态
|
|
|
- item.checked = isChecked;
|
|
|
+ // 没有子项选中 - 父级未选
|
|
|
+ item.checked = false;
|
|
|
item.indeterminate = false;
|
|
|
}
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 扁平化数据
|
|
|
- function flattenData(data, arr) {
|
|
|
- data.forEach((item) => {
|
|
|
- arr.push(item);
|
|
|
- if (!isEmpty(item.children)) {
|
|
|
- flattenData(item.children, arr);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- async function calculateShow(item, index = null, level = 0) {
|
|
|
- // 选中展开
|
|
|
- if (level != 0) {
|
|
|
- state.show[level].index = index;
|
|
|
+ } else {
|
|
|
+ // 叶子节点,直接根据是否在选中列表中设置状态
|
|
|
+ item.checked = isChecked;
|
|
|
+ item.indeterminate = false;
|
|
|
}
|
|
|
+ });
|
|
|
+}
|
|
|
|
|
|
- // 清除多余数据
|
|
|
- for (let key in state.show) {
|
|
|
- if (key > level) {
|
|
|
- delete state.show[key];
|
|
|
- }
|
|
|
+// 扁平化数据
|
|
|
+function flattenData(data, arr) {
|
|
|
+ data.forEach((item) => {
|
|
|
+ arr.push(item);
|
|
|
+ if (!isEmpty(item.children)) {
|
|
|
+ flattenData(item.children, arr);
|
|
|
}
|
|
|
+ });
|
|
|
+}
|
|
|
|
|
|
- // loading
|
|
|
- state.show[Number(level) + 1] = {
|
|
|
- loading: true,
|
|
|
- };
|
|
|
+async function calculateShow(item, index = null, level = 0) {
|
|
|
+ // 选中展开
|
|
|
+ if (level != 0) {
|
|
|
+ state.show[level].index = index;
|
|
|
+ }
|
|
|
|
|
|
- if (isEmpty(item.children)) {
|
|
|
- item.children = [];
|
|
|
+ // 清除多余数据
|
|
|
+ for (let key in state.show) {
|
|
|
+ if (key > level) {
|
|
|
+ delete state.show[key];
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- state.show[Number(level) + 1] = {
|
|
|
- index: null,
|
|
|
- data: item.children,
|
|
|
- pdata: item,
|
|
|
- loading: false,
|
|
|
- };
|
|
|
+ // loading
|
|
|
+ state.show[Number(level) + 1] = {
|
|
|
+ loading: true,
|
|
|
+ };
|
|
|
+
|
|
|
+ if (isEmpty(item.children)) {
|
|
|
+ item.children = [];
|
|
|
}
|
|
|
|
|
|
- async function onClick(item, index, level) {
|
|
|
- calculateShow(item, index, level);
|
|
|
- nextTick(() => {
|
|
|
- let left =
|
|
|
- document.getElementById('scrollWrap').scrollWidth -
|
|
|
- document.getElementById('scrollWrap').offsetWidth;
|
|
|
- document.getElementById('scrollWrap').scrollTo({
|
|
|
- top: 0,
|
|
|
- left: left,
|
|
|
- behavior: 'smooth',
|
|
|
- });
|
|
|
+ state.show[Number(level) + 1] = {
|
|
|
+ index: null,
|
|
|
+ data: item.children,
|
|
|
+ pdata: item,
|
|
|
+ loading: false,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+async function onClick(item, index, level) {
|
|
|
+ calculateShow(item, index, level);
|
|
|
+ nextTick(() => {
|
|
|
+ let left =
|
|
|
+ document.getElementById('scrollWrap').scrollWidth -
|
|
|
+ document.getElementById('scrollWrap').offsetWidth;
|
|
|
+ document.getElementById('scrollWrap').scrollTo({
|
|
|
+ top: 0,
|
|
|
+ left: left,
|
|
|
+ behavior: 'smooth',
|
|
|
+ });
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function handleSelect(checked, item) {
|
|
|
+ // 设置标志,阻止watch触发
|
|
|
+ isSelectingPermission = true;
|
|
|
+ manualChecked = true;
|
|
|
+
|
|
|
+ // 计算所有子元素
|
|
|
+ doChecked(item, checked);
|
|
|
+
|
|
|
+ // 计算父元素
|
|
|
+ doCheckedParent(item);
|
|
|
+
|
|
|
+ // 收集选中的权限ID
|
|
|
+ const newIds = [];
|
|
|
+ getCheckedIds(state.app, newIds);
|
|
|
+
|
|
|
+ // 直接发送事件,不更新state.newIds避免触发watch
|
|
|
+ emit('update:modelValue', newIds);
|
|
|
+
|
|
|
+ // 延迟重置标志
|
|
|
+ setTimeout(() => {
|
|
|
+ isSelectingPermission = false;
|
|
|
+ }, 50);
|
|
|
+}
|
|
|
+
|
|
|
+function doChecked(item, checked) {
|
|
|
+ item.checked = checked;
|
|
|
+ item.indeterminate = false;
|
|
|
+ if (!isEmpty(item.children)) {
|
|
|
+ item.children.forEach((i) => {
|
|
|
+ i.checked = checked;
|
|
|
+ i.indeterminate = false;
|
|
|
+ doChecked(i, checked);
|
|
|
});
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+function doCheckedParent(i) {
|
|
|
+ // 如果有父级
|
|
|
+ if (!isEmpty(i.parent)) {
|
|
|
+ if (!isEmpty(i.parent.children)) {
|
|
|
+ // 部分选中
|
|
|
+ i.parent.checked = false;
|
|
|
+ i.parent.indeterminate = true;
|
|
|
+
|
|
|
+ // 全选中
|
|
|
+ if (i.parent.children.every((k) => k.checked)) {
|
|
|
+ i.parent.checked = true;
|
|
|
+ i.parent.indeterminate = false;
|
|
|
+ }
|
|
|
|
|
|
- function handleSelect(checked, item) {
|
|
|
- // 设置标志,阻止watch触发
|
|
|
- isSelectingPermission = true;
|
|
|
- manualChecked = true;
|
|
|
-
|
|
|
- // 计算所有子元素
|
|
|
- doChecked(item, checked);
|
|
|
-
|
|
|
- // 计算父元素
|
|
|
- doCheckedParent(item);
|
|
|
-
|
|
|
- // 收集选中的权限ID
|
|
|
- const newIds = [];
|
|
|
- getCheckedIds(state.app, newIds);
|
|
|
+ // 未选中
|
|
|
+ if (!i.parent.children.some((k) => k.checked || k.indeterminate)) {
|
|
|
+ i.parent.checked = false;
|
|
|
+ i.parent.indeterminate = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ doCheckedParent(i.parent);
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- // 直接发送事件,不更新state.newIds避免触发watch
|
|
|
- emit('update:modelValue', newIds);
|
|
|
+async function onEnd(e, level, pdata) {
|
|
|
+ if (e.newIndex != e.oldIndex) {
|
|
|
+ try {
|
|
|
+ // 拖动元素修改seq(排序)
|
|
|
+ const draggedItem = pdata.children[e.oldIndex];
|
|
|
+ const targetItem = pdata.children[e.newIndex];
|
|
|
|
|
|
- // 延迟重置标志
|
|
|
- setTimeout(() => {
|
|
|
- isSelectingPermission = false;
|
|
|
- }, 50);
|
|
|
- }
|
|
|
+ // 更新排序值
|
|
|
+ const newSeq = targetItem._original?.seq ? targetItem._original.seq - 1 : e.newIndex - 1;
|
|
|
|
|
|
- function doChecked(item, checked) {
|
|
|
- item.checked = checked;
|
|
|
- item.indeterminate = false;
|
|
|
- if (!isEmpty(item.children)) {
|
|
|
- item.children.forEach((i) => {
|
|
|
- i.checked = checked;
|
|
|
- i.indeterminate = false;
|
|
|
- doChecked(i, checked);
|
|
|
+ // 调用编辑接口更新排序
|
|
|
+ await admin.auth.access.edit({
|
|
|
+ id: draggedItem.id,
|
|
|
+ seq: newSeq,
|
|
|
});
|
|
|
+
|
|
|
+ // 🎯 新方案:拖拽排序后保持选中状态刷新
|
|
|
+ console.log('✅ 拖拽排序成功,保持选中状态刷新数据');
|
|
|
+ await refreshDataKeepSelected();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('更新排序失败:', error);
|
|
|
+ // 如果更新失败,重新获取数据恢复状态
|
|
|
+ await getData();
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- function doCheckedParent(i) {
|
|
|
- // 如果有父级
|
|
|
- if (!isEmpty(i.parent)) {
|
|
|
- if (!isEmpty(i.parent.children)) {
|
|
|
- // 部分选中
|
|
|
- i.parent.checked = false;
|
|
|
- i.parent.indeterminate = true;
|
|
|
-
|
|
|
- // 全选中
|
|
|
- if (i.parent.children.every((k) => k.checked)) {
|
|
|
- i.parent.checked = true;
|
|
|
- i.parent.indeterminate = false;
|
|
|
+}
|
|
|
+function onAdd(id = null, level) {
|
|
|
+ useModal(
|
|
|
+ AccessEdit,
|
|
|
+ {
|
|
|
+ title: t('common.add'),
|
|
|
+ type: 'add',
|
|
|
+ parent_id: id,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ confirm: async (result) => {
|
|
|
+ console.log('📝 新增回调数据:', result);
|
|
|
+
|
|
|
+ if (result && result.event === 'confirm') {
|
|
|
+ // 🎯 新方案:新增操作成功,保持选中状态刷新
|
|
|
+ console.log('✅ 新增操作成功,保持选中状态刷新数据');
|
|
|
+ await refreshDataKeepSelected();
|
|
|
+ } else {
|
|
|
+ console.warn('⚠️ 新增操作未确认,不执行任何操作');
|
|
|
}
|
|
|
-
|
|
|
- // 未选中
|
|
|
- if (!i.parent.children.some((k) => k.checked || k.indeterminate)) {
|
|
|
- i.parent.checked = false;
|
|
|
- i.parent.indeterminate = false;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ );
|
|
|
+}
|
|
|
+function onEdit(id, index, level) {
|
|
|
+ useModal(
|
|
|
+ AccessEdit,
|
|
|
+ {
|
|
|
+ title: t('common.edit'),
|
|
|
+ type: 'edit',
|
|
|
+ id: id,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ confirm: async (result) => {
|
|
|
+ console.log('📝 编辑回调数据:', result);
|
|
|
+
|
|
|
+ if (result && result.event === 'confirm') {
|
|
|
+ // 🎯 新方案:编辑操作成功,保持选中状态刷新
|
|
|
+ console.log('✅ 编辑操作成功,保持选中状态刷新数据');
|
|
|
+ await refreshDataKeepSelected();
|
|
|
+ } else {
|
|
|
+ console.warn('⚠️ 编辑操作未确认,不执行任何操作');
|
|
|
}
|
|
|
- }
|
|
|
- doCheckedParent(i.parent);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+async function onDelete(id, index, level) {
|
|
|
+ try {
|
|
|
+ const { code } = await admin.auth.access.delete({ id });
|
|
|
+ if (code == '200') {
|
|
|
+ // 🎯 新方案:删除操作成功,保持选中状态刷新
|
|
|
+ console.log('✅ 删除操作成功,保持选中状态刷新数据');
|
|
|
+ await refreshDataKeepSelected();
|
|
|
}
|
|
|
+ } catch (error) {
|
|
|
+ console.error('删除权限失败:', error);
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- async function onEnd(e, level, pdata) {
|
|
|
- if (e.newIndex != e.oldIndex) {
|
|
|
- try {
|
|
|
- // 拖动元素修改seq(排序)
|
|
|
- const draggedItem = pdata.children[e.oldIndex];
|
|
|
- const targetItem = pdata.children[e.newIndex];
|
|
|
+// 移除可能导致递归更新的watch监听器
|
|
|
+// 权限选择的更新通过handleSelect函数直接处理
|
|
|
|
|
|
- // 更新排序值
|
|
|
- const newSeq = targetItem._original?.seq ? targetItem._original.seq - 1 : e.newIndex - 1;
|
|
|
+watch(
|
|
|
+ () => props.role_id,
|
|
|
+ () => {
|
|
|
+ if (props.isChangeParentId) {
|
|
|
+ state.checkedIds = [];
|
|
|
+ }
|
|
|
+ getData();
|
|
|
+ },
|
|
|
+);
|
|
|
|
|
|
- // 调用编辑接口更新排序
|
|
|
- await admin.auth.access.edit({
|
|
|
- id: draggedItem.id,
|
|
|
- seq: newSeq,
|
|
|
- });
|
|
|
+function getCheckedIds(data, targetArray = null) {
|
|
|
+ const idsArray = targetArray || state.newIds;
|
|
|
+
|
|
|
+ data.forEach((i) => {
|
|
|
+ // 收集真正选中的权限(全选状态)
|
|
|
+ if (i.checked && !i.indeterminate) {
|
|
|
+ idsArray.push(i.id + '');
|
|
|
+ }
|
|
|
+ // 收集半选状态的父级权限(当子级被选中时,父级也要包含)
|
|
|
+ else if (i.indeterminate) {
|
|
|
+ idsArray.push(i.id + '');
|
|
|
+ }
|
|
|
|
|
|
- // 🎯 新方案:拖拽排序后保持选中状态刷新
|
|
|
- console.log('✅ 拖拽排序成功,保持选中状态刷新数据');
|
|
|
- await refreshDataKeepSelected();
|
|
|
+ if (!isEmpty(i.children)) {
|
|
|
+ getCheckedIds(i.children, idsArray);
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 递归收集所有权限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.error('更新排序失败:', error);
|
|
|
- // 如果更新失败,重新获取数据恢复状态
|
|
|
- await getData();
|
|
|
+ console.warn(`⚠️ 删除权限异常: ID ${id}`, error);
|
|
|
}
|
|
|
+
|
|
|
+ // 延迟避免请求过快
|
|
|
+ await new Promise((resolve) => setTimeout(resolve, 100));
|
|
|
}
|
|
|
+
|
|
|
+ console.log('🎯 现有权限清空完成');
|
|
|
+ } else {
|
|
|
+ console.log('📝 没有找到现有权限,跳过清空步骤');
|
|
|
}
|
|
|
- function onAdd(id = null, level) {
|
|
|
- useModal(
|
|
|
- AccessEdit,
|
|
|
+};
|
|
|
+
|
|
|
+// 菜单权限初始化方法
|
|
|
+const initMenuPermissions = async () => {
|
|
|
+ try {
|
|
|
+ // 确认对话框
|
|
|
+ await ElMessageBox.confirm(
|
|
|
+ t('modules.auth.confirmInit'),
|
|
|
+ t('modules.auth.initConfirmTitle'),
|
|
|
{
|
|
|
- title: '新建',
|
|
|
- type: 'add',
|
|
|
- parent_id: id,
|
|
|
- },
|
|
|
- {
|
|
|
- confirm: async (result) => {
|
|
|
- console.log('📝 新增回调数据:', result);
|
|
|
-
|
|
|
- if (result && result.event === 'confirm') {
|
|
|
- // 🎯 新方案:新增操作成功,保持选中状态刷新
|
|
|
- console.log('✅ 新增操作成功,保持选中状态刷新数据');
|
|
|
- await refreshDataKeepSelected();
|
|
|
- } else {
|
|
|
- console.warn('⚠️ 新增操作未确认,不执行任何操作');
|
|
|
- }
|
|
|
- },
|
|
|
+ confirmButtonText: t('common.confirm'),
|
|
|
+ cancelButtonText: t('common.cancel'),
|
|
|
+ type: 'warning',
|
|
|
+ dangerouslyUseHTMLString: true,
|
|
|
},
|
|
|
);
|
|
|
- }
|
|
|
- function onEdit(id, index, level) {
|
|
|
- useModal(
|
|
|
- AccessEdit,
|
|
|
- {
|
|
|
- title: '编辑',
|
|
|
- type: 'edit',
|
|
|
- id: id,
|
|
|
- },
|
|
|
- {
|
|
|
- confirm: async (result) => {
|
|
|
- console.log('📝 编辑回调数据:', result);
|
|
|
-
|
|
|
- if (result && result.event === 'confirm') {
|
|
|
- // 🎯 新方案:编辑操作成功,保持选中状态刷新
|
|
|
- console.log('✅ 编辑操作成功,保持选中状态刷新数据');
|
|
|
- await refreshDataKeepSelected();
|
|
|
- } else {
|
|
|
- console.warn('⚠️ 编辑操作未确认,不执行任何操作');
|
|
|
- }
|
|
|
- },
|
|
|
- },
|
|
|
- );
|
|
|
- }
|
|
|
|
|
|
- async function onDelete(id, index, level) {
|
|
|
- try {
|
|
|
- const { code } = await admin.auth.access.delete({ id });
|
|
|
- if (code == '200') {
|
|
|
- // 🎯 新方案:删除操作成功,保持选中状态刷新
|
|
|
- console.log('✅ 删除操作成功,保持选中状态刷新数据');
|
|
|
- await refreshDataKeepSelected();
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('删除权限失败:', error);
|
|
|
- }
|
|
|
- }
|
|
|
+ initLoading.value = true;
|
|
|
+ console.log('🚀 开始菜单权限初始化...');
|
|
|
|
|
|
- // 移除可能导致递归更新的watch监听器
|
|
|
- // 权限选择的更新通过handleSelect函数直接处理
|
|
|
+ // 1. 首先清空所有现有权限
|
|
|
+ await clearAllPermissions();
|
|
|
|
|
|
- watch(
|
|
|
- () => props.role_id,
|
|
|
- () => {
|
|
|
- if (props.isChangeParentId) {
|
|
|
- state.checkedIds = [];
|
|
|
- }
|
|
|
- getData();
|
|
|
- },
|
|
|
- );
|
|
|
+ // 数据转换函数
|
|
|
+ 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: '',
|
|
|
+ });
|
|
|
|
|
|
- function getCheckedIds(data, targetArray = null) {
|
|
|
- const idsArray = targetArray || state.newIds;
|
|
|
+ // 根据权限名称和父ID查找新创建的权限ID
|
|
|
+ const findPermissionId = async (name, parentId = '') => {
|
|
|
+ console.log(`🔍 查找权限: ${name}, 父ID: "${parentId}"`);
|
|
|
|
|
|
- data.forEach((i) => {
|
|
|
- // 收集真正选中的权限(全选状态)
|
|
|
- if (i.checked && !i.indeterminate) {
|
|
|
- idsArray.push(i.id + '');
|
|
|
- }
|
|
|
- // 收集半选状态的父级权限(当子级被选中时,父级也要包含)
|
|
|
- else if (i.indeterminate) {
|
|
|
- idsArray.push(i.id + '');
|
|
|
- }
|
|
|
+ const response = await admin.auth.access.getTree();
|
|
|
+ if (response.success && response.data) {
|
|
|
+ console.log(`🔍 获取到的权限树数据:`, response.data);
|
|
|
|
|
|
- if (!isEmpty(i.children)) {
|
|
|
- getCheckedIds(i.children, idsArray);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
+ // 🎯 关键修复:对获取到的数据进行转换,确保有_original字段
|
|
|
+ const transformedData = Array.isArray(response.data)
|
|
|
+ ? transformPermissionData(response.data)
|
|
|
+ : [];
|
|
|
|
|
|
- // 递归收集所有权限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 findInTree = (items, level = 0) => {
|
|
|
+ const indent = ' '.repeat(level);
|
|
|
|
|
|
- // 清空所有现有权限
|
|
|
- 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);
|
|
|
- }
|
|
|
+ for (const item of items) {
|
|
|
+ // 现在可以安全使用_original字段了
|
|
|
+ const originalData = item._original || {};
|
|
|
+ const itemName = originalData.name || '';
|
|
|
+ const itemParentId = originalData.parentId || '';
|
|
|
|
|
|
- // 延迟避免请求过快
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 100));
|
|
|
- }
|
|
|
+ // 备用匹配:如果原始数据没有name,尝试使用转换后的title
|
|
|
+ const fallbackName = item.title || '';
|
|
|
|
|
|
- console.log('🎯 现有权限清空完成');
|
|
|
- } else {
|
|
|
- console.log('📝 没有找到现有权限,跳过清空步骤');
|
|
|
- }
|
|
|
- };
|
|
|
+ // 匹配权限名称和父ID(优先使用原始数据的name字段,备用title字段)
|
|
|
+ const nameMatches = itemName === name || (itemName === '' && fallbackName === name);
|
|
|
|
|
|
- // 菜单权限初始化方法
|
|
|
- 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: '',
|
|
|
- });
|
|
|
+ if (nameMatches) {
|
|
|
+ // 对于顶级权限,parentId可能是空字符串、null、undefined或0
|
|
|
+ const isTopLevel = !parentId || parentId === '' || parentId === '0';
|
|
|
+ const itemIsTopLevel = !itemParentId || itemParentId === '' || itemParentId === '0';
|
|
|
|
|
|
- // 根据权限名称和父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) {
|
|
|
- // 现在可以安全使用_original字段了
|
|
|
- const originalData = item._original || {};
|
|
|
- const itemName = originalData.name || '';
|
|
|
- const itemParentId = originalData.parentId || '';
|
|
|
-
|
|
|
- // 备用匹配:如果原始数据没有name,尝试使用转换后的title
|
|
|
- const fallbackName = item.title || '';
|
|
|
-
|
|
|
- // 匹配权限名称和父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';
|
|
|
-
|
|
|
- // 🎯 修复:父ID匹配逻辑
|
|
|
- const parentIdMatches =
|
|
|
- (isTopLevel && itemIsTopLevel) || String(itemParentId) === String(parentId);
|
|
|
-
|
|
|
- if (parentIdMatches) {
|
|
|
- const matchedName = itemName || fallbackName;
|
|
|
- return item.id;
|
|
|
- } else {
|
|
|
- console.log(`${indent}❌ 父ID不匹配,跳过此权限`);
|
|
|
- }
|
|
|
- }
|
|
|
+ // 🎯 修复:父ID匹配逻辑
|
|
|
+ const parentIdMatches =
|
|
|
+ (isTopLevel && itemIsTopLevel) || String(itemParentId) === String(parentId);
|
|
|
|
|
|
- if (item.children && item.children.length > 0) {
|
|
|
- const found = findInTree(item.children, level + 1);
|
|
|
- if (found) return found;
|
|
|
+ if (parentIdMatches) {
|
|
|
+ const matchedName = itemName || fallbackName;
|
|
|
+ return item.id;
|
|
|
+ } else {
|
|
|
+ console.log(`${indent}❌ 父ID不匹配,跳过此权限`);
|
|
|
}
|
|
|
}
|
|
|
- return null;
|
|
|
- };
|
|
|
|
|
|
- const result = findInTree(transformedData); // 🎯 使用转换后的数据
|
|
|
- console.log(`🎯 查找结果: ${result ? `找到ID ${result}` : '未找到'}`);
|
|
|
- return result;
|
|
|
- }
|
|
|
+ if (item.children && item.children.length > 0) {
|
|
|
+ const found = findInTree(item.children, level + 1);
|
|
|
+ if (found) return found;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ };
|
|
|
|
|
|
- console.log('❌ 获取权限树失败');
|
|
|
- return null;
|
|
|
- };
|
|
|
+ const result = findInTree(transformedData); // 🎯 使用转换后的数据
|
|
|
+ console.log(`🎯 查找结果: ${result ? `找到ID ${result}` : '未找到'}`);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
|
|
|
- // 递归创建函数(修改版:处理后端不返回ID的情况)
|
|
|
- const createRecursive = async (items, parentId = '', level = 0) => {
|
|
|
- const indent = ' '.repeat(level);
|
|
|
+ console.log('❌ 获取权限树失败');
|
|
|
+ return null;
|
|
|
+ };
|
|
|
|
|
|
- for (let i = 0; i < items.length; i++) {
|
|
|
- const item = items[i];
|
|
|
+ // 递归创建函数(修改版:处理后端不返回ID的情况)
|
|
|
+ const createRecursive = async (items, parentId = '', level = 0) => {
|
|
|
+ const indent = ' '.repeat(level);
|
|
|
|
|
|
- console.log(`${indent}📝 创建权限: ${item.title} (${item.type}), 父ID: "${parentId}"`);
|
|
|
+ for (let i = 0; i < items.length; i++) {
|
|
|
+ const item = items[i];
|
|
|
|
|
|
- // 创建当前权限
|
|
|
- const permissionData = transformMenuData(item, parentId);
|
|
|
- console.log(`${indent}📋 提交数据:`, permissionData);
|
|
|
+ console.log(`${indent}📝 创建权限: ${item.title} (${item.type}), 父ID: "${parentId}"`);
|
|
|
|
|
|
- const response = await admin.auth.access.add(permissionData);
|
|
|
+ // 创建当前权限
|
|
|
+ const permissionData = transformMenuData(item, parentId);
|
|
|
+ console.log(`${indent}📋 提交数据:`, permissionData);
|
|
|
|
|
|
- if (response.code === '200') {
|
|
|
- console.log(`${indent}✅ 创建成功: ${item.title}`);
|
|
|
+ const response = await admin.auth.access.add(permissionData);
|
|
|
|
|
|
- // 如果有子权限,需要先查找新创建的权限ID
|
|
|
- if (item.children && item.children.length > 0) {
|
|
|
- console.log(
|
|
|
- `${indent}🔍 需要创建 ${item.children.length} 个子权限,正在查找父权限ID...`,
|
|
|
- );
|
|
|
+ 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));
|
|
|
+ // 等待一下确保数据已保存
|
|
|
+ await new Promise((resolve) => setTimeout(resolve, 500));
|
|
|
|
|
|
- // 查找新创建的权限ID
|
|
|
- const newId = await findPermissionId(item.title, parentId);
|
|
|
+ // 查找新创建的权限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);
|
|
|
+ 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 {
|
|
|
- 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}`);
|
|
|
- }
|
|
|
+ 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));
|
|
|
+ } else {
|
|
|
+ console.error(`${indent}❌ 创建失败: ${item.title}`, response);
|
|
|
+ throw new Error(`创建权限失败: ${item.title} - ${response.message || '未知错误'}`);
|
|
|
}
|
|
|
- };
|
|
|
|
|
|
- // 2. 开始创建新的权限结构
|
|
|
- console.log('🏗️ 开始创建新的权限结构...');
|
|
|
- const menuData = menuRulesData.data.menu;
|
|
|
- await createRecursive(menuData);
|
|
|
+ // 延迟避免请求过快
|
|
|
+ await new Promise((resolve) => setTimeout(resolve, 300));
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- console.log('🎉 菜单权限初始化完成!');
|
|
|
- ElMessage.success('菜单权限初始化成功!');
|
|
|
+ // 2. 开始创建新的权限结构
|
|
|
+ console.log('🏗️ 开始创建新的权限结构...');
|
|
|
+ const menuData = menuRulesData.data.menu;
|
|
|
+ await createRecursive(menuData);
|
|
|
|
|
|
- // 3. 重新获取数据刷新界面
|
|
|
- await getData();
|
|
|
- } catch (error) {
|
|
|
- if (error.message !== 'cancel') {
|
|
|
- console.error('💥 初始化失败:', error);
|
|
|
- ElMessage.error(`初始化失败: ${error.message}`);
|
|
|
- }
|
|
|
- } finally {
|
|
|
- initLoading.value = false;
|
|
|
+ console.log('🎉 菜单权限初始化完成!');
|
|
|
+ ElMessage.success(t('modules.auth.initSuccess'));
|
|
|
+
|
|
|
+ // 3. 重新获取数据刷新界面
|
|
|
+ await getData();
|
|
|
+ } catch (error) {
|
|
|
+ if (error.message !== 'cancel') {
|
|
|
+ console.error('💥 初始化失败:', error);
|
|
|
+ ElMessage.error(t('modules.auth.initFailed', { message: error.message }));
|
|
|
}
|
|
|
- };
|
|
|
+ } finally {
|
|
|
+ initLoading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
- onMounted(() => {
|
|
|
- // 无论是新增还是编辑都需要加载权限数据
|
|
|
- getData();
|
|
|
- });
|
|
|
+onMounted(() => {
|
|
|
+ // 无论是新增还是编辑都需要加载权限数据
|
|
|
+ getData();
|
|
|
+});
|
|
|
</script>
|
|
|
<style lang="scss" scoped>
|
|
|
- .sa-access {
|
|
|
- font-size: 14px;
|
|
|
- color: var(--sa-subtitle);
|
|
|
- .el-main {
|
|
|
- display: flex;
|
|
|
- transition: all 3s;
|
|
|
+.sa-access {
|
|
|
+ font-size: 14px;
|
|
|
+ color: var(--sa-subtitle);
|
|
|
+
|
|
|
+ .el-main {
|
|
|
+ display: flex;
|
|
|
+ transition: all 3s;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-scrollbar {
|
|
|
+ flex-shrink: 0;
|
|
|
+ width: 258px;
|
|
|
+ border-right: 1px solid var(--sa-border);
|
|
|
+ border-left: 1px solid var(--sa-border);
|
|
|
+ margin: 0 8px;
|
|
|
+ background: var(--sa-background-assist);
|
|
|
+
|
|
|
+ &:first-of-type {
|
|
|
+ margin-left: 0;
|
|
|
}
|
|
|
- .el-scrollbar {
|
|
|
- flex-shrink: 0;
|
|
|
- width: 258px;
|
|
|
- border-right: 1px solid var(--sa-border);
|
|
|
- border-left: 1px solid var(--sa-border);
|
|
|
- margin: 0 8px;
|
|
|
- background: var(--sa-background-assist);
|
|
|
- &:first-of-type {
|
|
|
- margin-left: 0;
|
|
|
- }
|
|
|
- &:last-of-type {
|
|
|
- margin-right: 0;
|
|
|
- }
|
|
|
+
|
|
|
+ &:last-of-type {
|
|
|
+ margin-right: 0;
|
|
|
}
|
|
|
- .title {
|
|
|
+ }
|
|
|
+
|
|
|
+ .title {
|
|
|
+ height: 32px;
|
|
|
+ padding: 0 16px;
|
|
|
+ border-bottom: 1px solid var(--sa-border);
|
|
|
+ }
|
|
|
+
|
|
|
+ .node {
|
|
|
+ width: inherit;
|
|
|
+ padding: 0 16px;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ .item {
|
|
|
+ flex: 1;
|
|
|
height: 32px;
|
|
|
- padding: 0 16px;
|
|
|
- border-bottom: 1px solid var(--sa-border);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
}
|
|
|
- .node {
|
|
|
- width: inherit;
|
|
|
- padding: 0 16px;
|
|
|
- cursor: pointer;
|
|
|
- .item {
|
|
|
- flex: 1;
|
|
|
- height: 32px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- }
|
|
|
- .sa-icon {
|
|
|
- width: 16px !important;
|
|
|
- height: 16px !important;
|
|
|
- }
|
|
|
- .edit {
|
|
|
- color: var(--el-color-primary);
|
|
|
- }
|
|
|
- .delete {
|
|
|
- color: var(--el-color-danger);
|
|
|
- }
|
|
|
- .arrow-right {
|
|
|
- width: 14px;
|
|
|
- height: 14px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- }
|
|
|
- &.is-active {
|
|
|
- background: var(--t-bg-active);
|
|
|
- color: var(--el-color-primary);
|
|
|
- }
|
|
|
+
|
|
|
+ .sa-icon {
|
|
|
+ width: 16px !important;
|
|
|
+ height: 16px !important;
|
|
|
}
|
|
|
- .empty {
|
|
|
- width: inherit;
|
|
|
- text-align: center;
|
|
|
- min-height: 400px;
|
|
|
- line-height: 400px;
|
|
|
+
|
|
|
+ .edit {
|
|
|
+ color: var(--el-color-primary);
|
|
|
}
|
|
|
+
|
|
|
+ .delete {
|
|
|
+ color: var(--el-color-danger);
|
|
|
+ }
|
|
|
+
|
|
|
+ .arrow-right {
|
|
|
+ width: 14px;
|
|
|
+ height: 14px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.is-active {
|
|
|
+ background: var(--t-bg-active);
|
|
|
+ color: var(--el-color-primary);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .empty {
|
|
|
+ width: inherit;
|
|
|
+ text-align: center;
|
|
|
+ min-height: 400px;
|
|
|
+ line-height: 400px;
|
|
|
}
|
|
|
+}
|
|
|
</style>
|