Ver código fonte

feat: 新增完善角色权限模块

叶静 3 semanas atrás
pai
commit
0576dac81e

+ 15 - 2
src/app/admin/api/index.js

@@ -82,7 +82,14 @@ export default {
     // 角色管理
     role: {
       // 角色组
-      ...CRUD('admin/auth/role'),
+      ...CRUD('role'),
+      // 更新个人信息
+      roleList: (data) =>
+        request({
+          url: 'role/list',
+          method: 'POST',
+          data,
+        }),
       select: () =>
         request({
           url: 'admin/auth/role/select',
@@ -91,7 +98,7 @@ export default {
     },
     // 菜单权限
     access: {
-      ...CRUD('admin/auth/access'),
+      ...CRUD('permission'),
       list: (params) =>
         request({
           url: 'admin/auth/access',
@@ -104,6 +111,12 @@ export default {
           method: 'GET',
           params,
         }),
+      // 获取权限树
+      getTree: () =>
+        request({
+          url: 'permission/getTree',
+          method: 'GET',
+        }),
     },
     adminLog: {
       ...CRUD('admin/auth/adminLog'),

+ 636 - 62
src/app/admin/views/auth/access/components/sa-access.vue

@@ -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();

+ 105 - 32
src/app/admin/views/auth/access/edit.vue

@@ -28,21 +28,20 @@
           <el-radio-group v-model="form.model.type" @change="changeType">
             <el-radio label="menu">菜单</el-radio>
             <el-radio label="page">页面</el-radio>
-            <el-radio label="modal">弹框</el-radio>
-            <el-radio label="api">权限</el-radio>
+            <el-radio label="api">动作</el-radio>
           </el-radio-group>
         </el-form-item>
         <el-form-item label="标题" prop="title">
           <el-input v-model="form.model.title"></el-input>
         </el-form-item>
         <el-form-item label="标识" prop="name">
-          <el-input v-model="form.model.name"></el-input>
+          <el-input v-model="form.model.name" placeholder="请输入前端排版标识"></el-input>
         </el-form-item>
         <el-form-item v-if="form.model.type != 'api'" label="图标" prop="icon">
           <sa-uploader v-model="form.model.icon" mode="input" fileType="image"></sa-uploader>
         </el-form-item>
-        <el-form-item v-if="form.model.type != 'api'" label="参数" prop="params">
-          <el-input v-model="form.model.params" placeholder="请输入参数"></el-input>
+        <el-form-item v-if="form.model.type != 'api'" label="跳转地址" prop="url">
+          <el-input v-model="form.model.url" placeholder="请输入跳转地址"></el-input>
         </el-form-item>
         <el-form-item label="权重" prop="weigh">
           <el-input v-model="form.model.weigh" placeholder="请输入权重" type="number"></el-input>
@@ -50,13 +49,11 @@
         <el-form-item label="状态" prop="status">
           <div>
             <el-radio-group v-model="form.model.status">
-              <el-radio label="show">显示</el-radio>
-              <el-radio label="hidden">隐藏</el-radio>
+              <el-radio label="normal">正常</el-radio>
               <el-radio label="disabled">禁用</el-radio>
             </el-radio-group>
             <div class="tip">
-              <div>显示:该权限将作为菜单展示</div>
-              <div>隐藏:该权限将不在菜单中显示</div>
+              <div>正常:该权限正常可用</div>
               <div>禁用:禁用该权限后,除了超级管理员都将无法访问此权限</div>
             </div>
           </div>
@@ -114,20 +111,8 @@
       </el-form>
     </el-main>
     <el-footer class="sa-footer--submit">
-      <el-button
-        v-if="modal.params.type == 'add'"
-        v-auth="'admin.auth.access.add'"
-        type="primary"
-        @click="confirm"
-        >确定</el-button
-      >
-      <el-button
-        v-if="modal.params.type == 'edit'"
-        v-auth="'admin.auth.access.edit'"
-        type="primary"
-        @click="confirm"
-        >更新</el-button
-      >
+      <el-button v-if="modal.params.type == 'add'" type="primary" @click="confirm">确定</el-button>
+      <el-button v-if="modal.params.type == 'edit'" type="primary" @click="confirm">更新</el-button>
     </el-footer>
   </el-container>
 </template>
@@ -172,13 +157,13 @@
   let formRef = ref(null);
   const form = reactive({
     model: {
-      parent_id: props.modal.params.parent_id,
+      parent_id: props.modal.params.parent_id || '',
       title: '',
       type: 'menu',
       name: '',
       icon: '',
-      params: '',
-      status: 'show',
+      url: '',
+      status: 'normal',
       weigh: '',
       api: [],
     },
@@ -196,11 +181,55 @@
 
   const loading = ref(false);
 
+  // 数据转换函数:将新接口数据转换为表单需要的格式
+  function transformDetailData(data) {
+    return {
+      parent_id: data.parentId || null,
+      title: data.name || '', // 使用name作为title
+      type: getFormType(data.type), // 转换type格式
+      name: data.composingKey || '', // 使用composingKey作为标识
+      icon: data.logo || '',
+      url: data.url || '', // 跳转地址
+      status: getFormStatus(data.status), // 转换status格式
+      weigh: data.seq || 0, // 使用seq作为权重
+      api: [], // API权限暂时为空,可根据需要扩展
+    };
+  }
+
+  // 转换type字段:数字转换为字符串
+  function getFormType(type) {
+    const typeMap = {
+      0: 'menu', // 菜单
+      1: 'page', // 页面
+      2: 'api', // 权限/动作
+    };
+    return typeMap[String(type)] || 'menu';
+  }
+
+  // 转换status字段:数字转换为字符串
+  function getFormStatus(status) {
+    const statusMap = {
+      0: 'normal', // 正常
+      1: 'disabled', // 禁用
+    };
+    return statusMap[String(status)] || 'normal';
+  }
+
   // 获取详情
   async function getDetail(id) {
     loading.value = true;
-    const { code, data } = await admin.auth.access.detail(id);
-    code == '200' && (form.model = data);
+    try {
+      const response = await admin.auth.access.detail(id);
+
+      if (response.success && response.data) {
+        // 转换数据格式
+        form.model = transformDetailData(response.data);
+      } else {
+        console.error('获取权限详情失败:', response.message);
+      }
+    } catch (error) {
+      console.error('获取权限详情异常:', error);
+    }
 
     loading.value = false;
   }
@@ -222,18 +251,62 @@
     form.model.api.splice(index, 1);
   }
 
+  // 转换提交数据:将表单数据转换为后端需要的格式
+  function transformSubmitData(formData) {
+    const isAdd = props.modal.params.type == 'add' || props.modal.params.type == 'append';
+
+    return {
+      id: isAdd ? 0 : props.modal.params.id || 0, // 新增时ID为0,编辑时传递原ID
+      parentId: formData.parent_id || '',
+      name: formData.title || '', // 使用title作为name
+      eName: formData.title || '', // 使用title作为eName
+      logo: formData.icon || '',
+      composingKey: formData.name || '', // 使用name作为composingKey
+      type: getSubmitType(formData.type), // 转换type格式
+      status: getSubmitStatus(formData.status), // 转换status格式
+      seq: formData.weigh || 0, // 使用weigh作为seq
+      url: formData.url || '', // 跳转地址
+      isAction: formData.type === 'api' ? '1' : '0', // 是否为动作
+      children: [], // 子节点为空数组
+      createTime: '', // 创建时间由后端处理
+      createUserId: '', // 创建用户由后端处理
+      updateTime: '', // 更新时间由后端处理
+      updateUserId: '', // 更新用户由后端处理
+    };
+  }
+
+  // 转换type字段:字符串转换为数字
+  function getSubmitType(type) {
+    const typeMap = {
+      menu: 0, // 菜单
+      page: 1, // 页面
+      api: 2, // 动作
+    };
+    return typeMap[type] || 0;
+  }
+
+  // 转换status字段:字符串转换为数字
+  function getSubmitStatus(status) {
+    const statusMap = {
+      normal: 0, // 正常
+      disabled: 1, // 禁用
+    };
+    return statusMap[status] || 0;
+  }
+
   // 表单关闭时提交
   function confirm() {
     // 表单验证
     unref(formRef).validate(async (valid) => {
       if (!valid) return;
-      let submitForm = cloneDeep(form.model);
-      if (!submitForm.parent_id) submitForm.parent_id = 0;
-      submitForm.type == 'page' ? (submitForm.api = doUnique(submitForm.api || [], 'name')) : '';
+
+      // 转换提交数据格式
+      let submitForm = transformSubmitData(form.model);
+
       const { code, data } =
         props.modal.params.type == 'add' || props.modal.params.type == 'append'
           ? await admin.auth.access.add(submitForm)
-          : await admin.auth.access.edit(props.modal.params.id, submitForm);
+          : await admin.auth.access.edit(submitForm);
       code == '200' && emit('modalCallBack', { event: 'confirm', data });
     });
   }

+ 8 - 12
src/app/admin/views/auth/role/edit.vue

@@ -35,7 +35,7 @@
 <script setup>
   import { reactive, ref, unref, onMounted } from 'vue';
   import { ElMessage } from 'element-plus';
-  import { roleMockData } from '@/sheep/mock/role';
+  import admin from '@/app/admin/api';
   import SaAccess from '../access/components/sa-access.vue';
   import { cloneDeep } from 'lodash';
 
@@ -66,9 +66,9 @@
   // 获取详情
   async function getDetail(id) {
     try {
-      const result = roleMockData.getDetail(id);
-      if (result.code == '200') {
-        form.model = { ...result.data };
+      const { code, data } = await admin.auth.role.detail(id);
+      if (code == 200) {
+        form.model = { ...data };
 
         // 确保rules是数组格式,用于sa-access组件
         if (!form.model.rules || !Array.isArray(form.model.rules)) {
@@ -76,8 +76,6 @@
         }
 
         console.log('角色详情数据:', form.model);
-      } else {
-        ElMessage.error(result.msg);
       }
     } catch (error) {
       console.error('获取详情失败:', error);
@@ -113,16 +111,14 @@
           submitForm.permissions_text = permissions.join('、');
         }
 
-        const result =
+        const { code } =
           props.modal.params.type == 'add'
-            ? roleMockData.create(submitForm)
-            : roleMockData.update(props.modal.params.id, submitForm);
+            ? await admin.auth.role.add(submitForm)
+            : await admin.auth.role.edit(props.modal.params.id, submitForm);
 
-        if (result.code == '200') {
+        if (code == 200) {
           ElMessage.success(props.modal.params.type == 'add' ? '创建成功' : '更新成功');
           emit('modalCallBack', { event: 'confirm' });
-        } else {
-          ElMessage.error(result.msg);
         }
       } catch (error) {
         console.error('提交失败:', error);

+ 30 - 73
src/app/admin/views/auth/role/index.vue

@@ -16,54 +16,35 @@
           class="sa-table"
           :data="table.data"
           @sort-change="fieldFilter"
-          @row-dblclick="editRow"
           row-key="id"
           stripe
         >
           <template #empty>
             <sa-empty />
           </template>
-          <el-table-column prop="name" label="角色名称" min-width="150" sortable="custom">
-            <template #default="scope">
-              <span class="sa-table-line-1">{{ scope.row.name || '-' }}</span>
-            </template>
-          </el-table-column>
-          <el-table-column label="授权功能" min-width="300">
-            <template #default="scope">
-              <span class="sa-table-line-2">{{ scope.row.permissions_text || '-' }}</span>
-            </template>
-          </el-table-column>
-          <el-table-column label="角色备注" min-width="200">
-            <template #default="scope">
-              <span class="sa-table-line-1">{{ scope.row.remark || '-' }}</span>
-            </template>
-          </el-table-column>
-          <el-table-column label="状态" min-width="100" align="center">
-            <template #default="scope">
-              <el-tag :type="getStatusType(scope.row.status)">
-                {{ scope.row.status_text || '-' }}
-              </el-tag>
-            </template>
+          <el-table-column prop="id" label="ID" min-width="100" sortable="custom">
           </el-table-column>
-          <el-table-column prop="create_time" label="创建时间" min-width="160" sortable="custom">
+          <el-table-column prop="name" label="角色名称" min-width="200" sortable="custom">
             <template #default="scope">
-              {{ scope.row.create_time || '-' }}
+              <span class="sa-table-line-1">{{ scope.row.name || '-' }}</span>
             </template>
           </el-table-column>
           <el-table-column fixed="right" label="操作" min-width="120">
             <template #default="scope">
-              <el-button link type="primary" @click="editRow(scope.row)">编辑</el-button>
-              <el-popconfirm
-                width="fit-content"
-                confirm-button-text="确认"
-                cancel-button-text="取消"
-                title="确认删除这条记录?"
-                @confirm="deleteRow(scope.row)"
-              >
-                <template #reference>
-                  <el-button link type="danger">删除</el-button>
-                </template>
-              </el-popconfirm>
+              <template v-if="scope.row.id != 1">
+                <el-button link type="primary" @click="editRow(scope.row)">编辑</el-button>
+                <el-popconfirm
+                  width="fit-content"
+                  confirm-button-text="确认"
+                  cancel-button-text="取消"
+                  title="确认删除这条记录?"
+                  @confirm="deleteApi(scope.row.id)"
+                >
+                  <template #reference>
+                    <el-button link type="danger">删除</el-button>
+                  </template>
+                </el-popconfirm>
+              </template>
             </template>
           </el-table-column>
         </el-table>
@@ -79,9 +60,8 @@
 
 <script setup>
   import { onMounted, reactive, ref } from 'vue';
-  import { ElMessage } from 'element-plus';
+  import admin from '@/app/admin/api';
   import { useModal, usePagination } from '@/sheep/hooks';
-  import { roleMockData } from '@/sheep/mock/role';
   import RoleEdit from './edit.vue';
   const { pageData } = usePagination();
 
@@ -93,39 +73,26 @@
   });
   const loading = ref(true);
 
-  // 获取状态类型
-  function getStatusType(status) {
-    const statusMap = {
-      normal: 'success',
-      disabled: 'danger',
-    };
-    return statusMap[status] || 'info';
-  }
-
   // 获取数据
   async function getData(page, searchParams = {}) {
     if (page) pageData.page = page;
     loading.value = true;
 
     try {
-      let params = {
-        page: pageData.page,
+      const { code, data } = await admin.auth.role.roleList({
+        page: 0,
         size: pageData.size,
-        order: table.order,
-        sort: table.sort,
         ...searchParams,
-      };
+      });
 
-      const result = roleMockData.getList(params);
-      if (result.code == '200') {
-        table.data = result.data.data;
-        pageData.page = result.data.current_page;
-        pageData.size = result.data.per_page;
-        pageData.total = result.data.total;
+      if (code == 200) {
+        table.data = data.list;
+        pageData.page = data.pageNum;
+        pageData.size = data.pageSize;
+        pageData.total = data.total;
       }
     } catch (error) {
       console.error('获取数据失败:', error);
-      ElMessage.error('获取数据失败');
     } finally {
       loading.value = false;
     }
@@ -168,20 +135,10 @@
     );
   }
 
-  // 删除角色
-  async function deleteRow(row) {
-    try {
-      const result = roleMockData.delete(row.id);
-      if (result.code == '200') {
-        ElMessage.success('删除成功');
-        getData();
-      } else {
-        ElMessage.error(result.msg);
-      }
-    } catch (error) {
-      console.error('删除失败:', error);
-      ElMessage.error('删除失败');
-    }
+  // 删除api 单独批量可以直接调用
+  async function deleteApi(id) {
+    await admin.auth.role.delete(id);
+    getData();
   }
 
   onMounted(() => {

+ 5 - 4
src/app/shop/admin/order/order/index.vue

@@ -381,7 +381,7 @@
       // 构建请求参数
       const requestData = {
         page: pageData.page,
-        limit: pageData.size,
+        size: pageData.size,
         ...searchParams,
       };
 
@@ -398,7 +398,7 @@
 
       if (response && response.code == '200' && response.data) {
         // 映射后端返回的字段到前端需要的格式
-        table.data = (response.data.list || []).map((item) => ({
+        table.data = (response.data.orders.data || []).map((item) => ({
           id: item.id,
           orderId: item.orderId, // 订单号
           realName: item.realName, // 用户姓名
@@ -421,8 +421,9 @@
           orderInfoVO: item.orderInfoVO, // 订单商品信息
         }));
 
-        pageData.page = response.data.current_page || pageData.page;
-        pageData.total = response.data.total || 0;
+        pageData.page = response.data.orders.current_page || pageData.page;
+        pageData.size = response.data.orders.per_page || pageData.size;
+        pageData.total = response.data.orders.total || 0;
       } else {
         table.data = [];
         console.error('获取订单数据失败:', response);