diff --git a/.eslintignore b/.eslintignore
index aa8e45f..a043991 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1 +1 @@
-src/
\ No newline at end of file
+# 取消忽略 src 目录,让 ESLint 正常检查源代码
\ No newline at end of file
diff --git a/check_unused_dicts.js b/check_unused_dicts.js
new file mode 100644
index 0000000..9f4cd97
--- /dev/null
+++ b/check_unused_dicts.js
@@ -0,0 +1,94 @@
+const fs = require('fs');
+const path = require('path');
+
+const baseDir = path.join(__dirname, 'src/views/backOfficeSystem');
+
+// Find all .vue files with proxy.$dict
+function findVueFiles(dir) {
+ let results = [];
+ const files = fs.readdirSync(dir);
+ for (const file of files) {
+ const filePath = path.join(dir, file);
+ const stat = fs.statSync(filePath);
+ if (stat.isDirectory()) {
+ results = results.concat(findVueFiles(filePath));
+ } else if (file.endsWith('.vue')) {
+ const content = fs.readFileSync(filePath, 'utf-8');
+ if (content.includes('proxy.$dict')) {
+ results.push(filePath);
+ }
+ }
+ }
+ return results;
+}
+
+// Parse dict variables and their usage
+function analyzeFile(filePath) {
+ const content = fs.readFileSync(filePath, 'utf-8');
+
+ // Find the dict destructuring pattern
+ // Pattern: const { VAR1, VAR2, ... } = proxy.$dict("KEY1", "KEY2", ...)
+
+ // Extract the destructured variable names
+ const destructMatch = content.match(/const\s*\{([^}]+)\}\s*=\s*proxy\.\$dict\s*\(/);
+ if (!destructMatch) return null;
+
+ const varsStr = destructMatch[1];
+ const dictVars = varsStr.split(',').map(v => v.trim().replace(/\/\/.*$/, '').trim()).filter(v => v && !v.startsWith('//'));
+
+ // Extract the dict keys
+ const dictCallMatch = content.match(/proxy\.\$dict\s*\(([^)]+)\)/s);
+ if (!dictCallMatch) return null;
+
+ const dictKeysStr = dictCallMatch[1];
+ const dictKeys = dictKeysStr.split(',').map(k => k.trim().replace(/['"]/g, '').replace(/\/\/.*$/, '').trim()).filter(k => k && !k.startsWith('//'));
+
+ // Now check which dict vars are actually used in the file
+ // Remove the dict declaration part first
+ const scriptContent = content.replace(/const\s*\{[^}]+\}\s*=\s*proxy\.\$dict\s*\([^)]+\)[^;\n]*;?/s, '');
+
+ const unusedVars = [];
+ const usedVars = [];
+
+ for (const varName of dictVars) {
+ if (!varName) continue;
+ // Check if the variable name appears elsewhere in the file (outside the dict declaration)
+ // Look for: varName in template, searchConfiger, getMultiDictVal, DictTag :options, :dict= etc.
+ const regex = new RegExp('\\b' + varName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\b');
+ const matches = scriptContent.match(regex);
+ if (matches && matches.length > 0) {
+ usedVars.push(varName);
+ } else {
+ unusedVars.push(varName);
+ }
+ }
+
+ return {
+ filePath,
+ dictVars,
+ dictKeys,
+ unusedVars,
+ usedVars
+ };
+}
+
+const vueFiles = findVueFiles(baseDir);
+console.log(`Found ${vueFiles.length} files with proxy.$dict\n`);
+
+let totalUnused = 0;
+const filesWithUnused = [];
+
+for (const filePath of vueFiles) {
+ const result = analyzeFile(filePath);
+ if (result && result.unusedVars.length > 0) {
+ const relPath = path.relative(__dirname, filePath).replace(/\\/g, '/');
+ console.log(`\n${relPath}:`);
+ console.log(` Unused: ${result.unusedVars.join(', ')}`);
+ filesWithUnused.push(result);
+ totalUnused += result.unusedVars.length;
+ }
+}
+
+console.log(`\n\n=== Summary ===`);
+console.log(`Total files with unused dicts: ${filesWithUnused.length}`);
+console.log(`Total unused dict variables: ${totalUnused}`);
diff --git a/docs/navigation-issue-solution.md b/docs/navigation-issue-solution.md
new file mode 100644
index 0000000..80a5569
--- /dev/null
+++ b/docs/navigation-issue-solution.md
@@ -0,0 +1,129 @@
+# 首页导航跳转问题分析与解决方案
+
+## 问题现象
+
+接口请求完成之前,首页的导航菜单无法跳转;接口请求完成后,导航跳转正常。
+
+---
+
+## 问题根源
+
+### 核心问题:SideBarMenu.vue 的页面刷新逻辑
+
+```javascript
+// 问题代码(已修复)
+if (router.getRoutes().length <= 7 && store.state.permission.routeReady <= 1) {
+ setTimeout(() => {
+ router.go(0); // 触发页面刷新!
+ }, 200);
+}
+```
+
+当动态路由还没添加完成时,这个条件会触发页面不断刷新,导致导航不可用。
+
+---
+
+## 已完成的修复
+
+### 1. 修改 `src/store/modules/permission.js`
+
+**修改内容**:优化 `routeReady` 状态管理(0: 未开始 → 1: 进行中 → 2: 完成)
+
+```javascript
+actions: {
+ filterRoutes(context, menus) {
+ // 开始处理,标记为进行中
+ context.commit('setRouteReady', 1);
+
+ let routes = [];
+ if (menus && menus.length > 0) {
+ routes = filter(privateRoutes, menus);
+ }
+ routes.push({ path: '/:catchAll(.*)', redirect: '/404' });
+
+ context.commit('setRoutes', routes);
+ // 处理完成,标记为已完成
+ context.commit('setRouteReady', 2); // ← 新增:完成时设为 2
+
+ return routes;
+ }
+}
+```
+
+### 2. 修改 `src/permission.js`
+
+**修改内容**:移除了在路由守卫开始时设置 `setRouteReady(1)` 的代码,让 `filterRoutes` action 统一管理状态。
+
+### 3. 修改 `src/layout/components/SideBar/SideBarMenu.vue`
+
+**修改内容**:移除自动刷新页面的逻辑,改为监听路由加载状态
+
+```javascript
+// 原代码(已移除):
+// if (router.getRoutes().length <= 7 && store.state.permission.routeReady <= 1) {
+// store.commit("user/setIsReady", {});
+// setTimeout(() => {
+// router.go(0);
+// }, 200);
+// }
+
+// 新代码:监听路由加载完成状态
+if (store.state.permission.routeReady !== 2) {
+ const unwatch = watch(
+ () => store.state.permission.routeReady,
+ (val) => {
+ if (val === 2) {
+ unwatch();
+ }
+ },
+ { immediate: true }
+ );
+}
+```
+
+### 4. 修改 `src/utils/route.js`
+
+**修改内容**:添加空值安全检查,避免 `deptId` 或 `roleList` 为空时报错
+
+```javascript
+// 原代码(可能报错):
+// const { deptBizType, deptLevel } = getItem('deptId')[0]
+
+// 新代码(安全):
+const deptIdData = getItem('deptId');
+const deptInfo = deptIdData && deptIdData.length > 0 ? deptIdData[0] : {};
+const deptBizType = deptInfo.deptBizType || '';
+const deptLevel = deptInfo.deptLevel || '';
+const roleListData = getItem('roleList') || [];
+const roleList = roleListData.filter(item => item.roleCode == 'JS_666666').length > 0;
+const xjLsit = roleListData.filter(item => item.roleCode == 'JS_999999').length > 0;
+```
+
+---
+
+## 修复后的流程
+
+```
+登录成功 → window.location.href = '/' → 页面加载
+ ↓
+permission.js 路由守卫执行
+ ↓
+filterRoutes 开始执行 → routeReady = 1(进行中)
+ ↓
+动态路由添加完成 → routeReady = 2(完成)
+ ↓
+SideBarMenu.vue 监听到 routeReady === 2
+ ↓
+导航菜单正常渲染,可以跳转
+```
+
+---
+
+## 修复文件列表
+
+| 文件路径 | 修改内容 |
+|---------|---------|
+| `src/store/modules/permission.js` | 优化 routeReady 状态管理(0→1→2) |
+| `src/permission.js` | 移除重复的 setRouteReady 调用 |
+| `src/layout/components/SideBar/SideBarMenu.vue` | 移除自动刷新逻辑,改为监听状态 |
+| `src/utils/route.js` | 添加空值安全检查 |
diff --git a/docs/菜单权限逻辑文档.md b/docs/菜单权限逻辑文档.md
new file mode 100644
index 0000000..0690672
--- /dev/null
+++ b/docs/菜单权限逻辑文档.md
@@ -0,0 +1,463 @@
+# 菜单权限逻辑文档
+
+## 概述
+
+本项目采用**动态路由注册**方案实现权限控制。用户登录后,系统根据后端返回的菜单权限码动态注册路由,无权限的路由**根本不会注册**到 Vue Router,从根本上杜绝了越权访问的可能。
+
+---
+
+## 整体流程图
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ 用户登录 │
+└─────────────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────────────┐
+│ 调用登录接口 │
+│ 返回:jwtToken、menuList、menuCodeSet、deptList 等 │
+└─────────────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────────────┐
+│ 存储权限数据 │
+│ - localStorage.menusPermission = menuCodeSet(菜单权限码集合) │
+│ - Vuex: user.userInfo.permission.menus = menuCodeSet │
+└─────────────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────────────┐
+│ 路由守卫(permission.js) │
+│ 首次进入时:获取 menusPermission → 调用 filterRoutes 动态注册路由 │
+└─────────────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────────────┐
+│ Vuex: permission/filterRoutes │
+│ 1. 根据 menusPermission 过滤 privateRoutes │
+│ 2. 通过 router.addRoute() 动态注册有权限的路由 │
+│ 3. 最后注册 404 兜底路由 │
+└─────────────────────────────────────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────────────┐
+│ SideBarMenu.vue 组件 │
+│ 从已注册的路由中筛选并渲染侧边栏菜单 │
+└─────────────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 核心文件说明
+
+| 文件路径 | 作用 |
+|---------|------|
+| `src/router/index.js` | 路由配置,定义 `publicRoutes`(公开路由)和 `privateRoutes`(私有路由) |
+| `src/store/modules/permission.js` | 权限模块,处理路由过滤和动态注册逻辑 |
+| `src/store/modules/user.js` | 用户模块,处理登录和退出登录 |
+| `src/permission.js` | 路由守卫,控制路由初始化时机 |
+| `src/utils/route.js` | 路由工具函数,处理菜单生成 |
+| `src/layout/components/SideBar/SideBarMenu.vue` | 侧边栏菜单组件,渲染权限菜单 |
+| `src/views/error/404.vue` | 无权限/页面不存在页面 |
+| `src/directives/permission.js` | 按钮级权限指令 |
+
+---
+
+## 路由分类
+
+### 公开路由(publicRoutes)
+
+应用启动时静态注册,所有用户都能访问:
+
+| 路由路径 | 说明 |
+|---------|------|
+| `/login` | 登录页 |
+| `/oatuh_login` | OAuth 登录页 |
+| `/zeroTrust_login` | 零信任登录页 |
+| `/` | 首页 |
+| `/401` | 无权限页(保留) |
+| `/404` | 页面不存在/无权限页 |
+| `/mapNavigation` | 地图导航 |
+| `/KeyPopulations` | 重点人详情 |
+| `/deploymentApproval` | 布控审核 |
+| `/clueVerification` | 线索核实 |
+| 其他特殊路由... | 无需菜单权限校验的业务路由 |
+
+### 私有路由(privateRoutes)
+
+登录后根据权限动态注册,包含所有业务功能页面。
+
+---
+
+## 详细逻辑分析
+
+### 1. 登录阶段 - 获取并存储权限
+
+**文件**: `src/store/modules/user.js`
+
+```javascript
+// 登录成功后存储权限数据
+this.commit("user/setToken", data.jwtToken);
+this.commit("user/setMenuList", data.menuList);
+setItem("menusPermission", data.menuCodeSet); // 核心:菜单权限码集合
+
+this.commit("user/setUserInfo", {
+ token: data.jwtToken,
+ permission: {
+ buttonPermission: ["removeTest", "viewTest"],
+ menus: data.menuCodeSet
+ },
+ menuList: data.menuList,
+ deptList: data.deptList
+});
+```
+
+**权限码示例**:
+```javascript
+["FourColorWarning", "YjData", "IntelligentControl", "userList", "departmentList", ...]
+```
+
+---
+
+### 2. 路由守卫 - 控制初始化
+
+**文件**: `src/permission.js`
+
+```javascript
+const whiteList = ['/login', '/oatuh_login', '/404', '/401', '/zeroTrust_login',
+ '/focusExploration', '/clueVerification', '/deploymentApproval'];
+
+let routesInitialized = false;
+
+router.beforeEach(async (to, from, next) => {
+ if (store.getters.token) {
+ if (!routesInitialized) {
+ // ★ 首次进入:动态注册路由
+ routesInitialized = true;
+ const afterMenuList = getItem('menusPermission');
+
+ // 根据权限动态注册路由
+ await store.dispatch('permission/filterRoutes', afterMenuList);
+
+ // 重新导航,确保刚注册的路由能正确匹配
+ next({ ...to, replace: true });
+ return;
+ }
+ next();
+ } else {
+ // 未登录:白名单放行,否则跳转登录
+ if (whiteList.indexOf(to.path) > -1) {
+ next();
+ } else {
+ // 跳转登录逻辑...
+ }
+ }
+});
+```
+
+---
+
+### 3. 权限过滤与动态注册
+
+**文件**: `src/store/modules/permission.js`
+
+```javascript
+import router from '@/router'
+import { publicRoutes, privateRoutes } from '@/router'
+
+/**
+ * 递归过滤路由(保留 component 引用)
+ * 规则:
+ * 1. 路由有 name 且在权限列表中 → 保留
+ * 2. 路由无 name 但有子路由 → 检查子路由权限,有权限子路由则保留父路由
+ */
+function filter(data, menus) {
+ const result = []
+
+ data.forEach(route => {
+ const newRoute = { ...route } // 浅拷贝,保留 component 引用
+
+ if (route.name && menus?.includes(route.name)) {
+ // 有权限:递归处理子路由
+ if (route.children && route.children.length > 0) {
+ newRoute.children = filter(route.children, menus)
+ }
+ result.push(newRoute)
+ } else if (!route.name && route.children && route.children.length > 0) {
+ // 父路由无 name:检查子路由
+ const filteredChildren = filter(route.children, menus)
+ if (filteredChildren.length > 0) {
+ newRoute.children = filteredChildren
+ result.push(newRoute)
+ }
+ }
+ })
+
+ return result
+}
+
+actions: {
+ filterRoutes(context, menus) {
+ let routes = []
+
+ if (menus && menus.length > 0) {
+ routes = filter(privateRoutes, menus)
+ }
+
+ // ★★★ 关键:动态添加路由到 Vue Router ★★★
+ routes.forEach(route => {
+ router.addRoute(route)
+ })
+
+ // 404 兜底路由必须最后添加
+ router.addRoute({
+ path: '/:catchAll(.*)',
+ redirect: '/404'
+ })
+
+ context.commit('setRoutes', routes)
+ return routes
+ }
+}
+```
+
+**重要说明**:
+- 不能使用 `JSON.parse(JSON.stringify())` 深拷贝,会丢失 `component` 函数引用导致页面空白
+- 使用 `{ ...route }` 浅拷贝保留 `component` 引用
+
+---
+
+### 4. 侧边栏菜单渲染
+
+**文件**: `src/layout/components/SideBar/SideBarMenu.vue`
+
+```javascript
+// 从已注册的路由中获取并过滤
+const routes = computed(() => {
+ const fRoutes = filterRoutes(router.getRoutes());
+ const data = fRoutes.filter((item) => !EXCLUDE_NAMES.includes(item.name));
+
+ const menusPermission = getItem("menusPermission");
+
+ if (menusPermission === null || menusPermission === undefined) {
+ return [];
+ }
+
+ const menusSet = new Set(menusPermission.map((item) => `${item}`));
+
+ const permissionFiltered = menusSet.size
+ ? filterRoutesByMenusPermission(data, menusSet)
+ : [];
+
+ return generateMenus(permissionFiltered);
+});
+```
+
+---
+
+### 5. 404 无权限页面
+
+**文件**: `src/views/error/404.vue`
+
+无权限访问时统一跳转此页面,显示提示信息和操作按钮:
+
+```vue
+
+ 您没有权限访问此页面,请联系上级部门添加相关权限。404
+ 无权限访问
+