diff --git a/.env.development b/.env.development index fc339fe..32502d1 100644 --- a/.env.development +++ b/.env.development @@ -9,9 +9,9 @@ VITE_APP_ENV = 'development' # 开发环境 VITE_APP_BASE_API = '/dev-api' # # 开发环境 接口代理地址 -# VITE_APP_PROXY_API = 'http://192.168.1.222:8080' +VITE_APP_PROXY_API = 'http://192.168.1.222:8080' # 开发环境 接口代理地址 -VITE_APP_PROXY_API = 'http://192.168.1.215:8080' +# VITE_APP_PROXY_API = 'http://192.168.1.215:8080' # 图片基础地址 VITE_IMG_URL = 'http://192.168.1.222:8080' diff --git a/src/api/system/ChargeType/index.ts b/src/api/system/ChargeType/index.ts new file mode 100644 index 0000000..a0d3c59 --- /dev/null +++ b/src/api/system/ChargeType/index.ts @@ -0,0 +1,63 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { ChargeTypeVO, ChargeTypeForm, ChargeTypeQuery } from './type'; + +/** + * 查询收费类型列表 + * @param query + * @returns {*} + */ + +export const listChargeType = (query?: ChargeTypeQuery): AxiosPromise => { + return request({ + url: '/chargeType/list', + method: 'get', + params: query + }); +}; + +/** + * 查询收费类型详细 + * @param id + */ +export const getChargeType = (id: string | number): AxiosPromise => { + return request({ + url: '/chargeType/' + id, + method: 'get' + }); +}; + +/** + * 新增收费类型 + * @param data + */ +export const addChargeType = (data: ChargeTypeForm) => { + return request({ + url: '/chargeType', + method: 'post', + data: data + }); +}; + +/** + * 修改收费类型 + * @param data + */ +export const updateChargeType = (data: ChargeTypeForm) => { + return request({ + url: '/chargeType', + method: 'put', + data: data + }); +}; + +/** + * 删除收费类型 + * @param id + */ +export const delChargeType = (id: string | number | Array) => { + return request({ + url: '/chargeType/' + id, + method: 'delete' + }); +}; diff --git a/src/api/system/ChargeType/type.ts b/src/api/system/ChargeType/type.ts new file mode 100644 index 0000000..ffed686 --- /dev/null +++ b/src/api/system/ChargeType/type.ts @@ -0,0 +1,66 @@ +export interface ChargeTypeVO { + /** + * 收费类型表id + */ + id: string | number; + + /** + * 收费类型名称 + */ + name: string; + + /** + * 状态 0启用 1停用 + */ + status: string; +} + +export interface ChargeTypeForm extends BaseEntity { + /** + * 收费类型表id + */ + id?: string | number; + + /** + * 收费类型名称 + */ + name?: string; + + /** + * 状态 0启用 1停用 + */ + status?: string; +} + +export interface ChargeTypeQuery { + /** + * 收费类型名称 + */ + name?: string; + + /** + * 状态 0启用 1停用 + */ + status?: string; + + /** + * 日期范围参数 + */ + params?: any; +} +export interface ChargeType2Query extends PageQuery { + /** + * 收费类型名称 + */ + name?: string; + + /** + * 状态 0启用 1停用 + */ + status?: string; + + /** + * 日期范围参数 + */ + params?: any; +} diff --git a/src/api/system/SdbChargeItem/type.ts b/src/api/system/SdbChargeItem/type.ts index f86524d..b585450 100644 --- a/src/api/system/SdbChargeItem/type.ts +++ b/src/api/system/SdbChargeItem/type.ts @@ -8,6 +8,10 @@ export interface ChargeItemVO { * 项目名称 */ name: string; + /** + * 收费类型 + */ + typeId: string; /** * 计费方式 0单次收费 1周期收费 2仪表收费 diff --git a/src/api/system/house/type.ts b/src/api/system/house/type.ts index 7435862..46e2a85 100644 --- a/src/api/system/house/type.ts +++ b/src/api/system/house/type.ts @@ -6,7 +6,7 @@ export interface BuildingVo { /** * 0住宅 1办公 2商用 3公寓 */ - buildingUse: number; + buildingUse: string; /** * 楼栋结构 */ @@ -68,7 +68,7 @@ export interface HouseType { export interface addBuildingType { buildingName: string; - villageId: number; + villageId?: number; buildStructure?: string; buildTall?: string; buildToward?: string; diff --git a/src/api/system/resident/type.ts b/src/api/system/resident/type.ts index e4632fe..6fd41f2 100644 --- a/src/api/system/resident/type.ts +++ b/src/api/system/resident/type.ts @@ -3,7 +3,7 @@ export interface ResidentVO { * 主键 住户管理id */ userId: string | number; - + id: string; /** * 住户编号 */ diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue index c454020..b54ecc0 100644 --- a/src/components/Breadcrumb/index.vue +++ b/src/components/Breadcrumb/index.vue @@ -19,57 +19,41 @@ const permissionStore = usePermissionStore(); const levelList = ref([]); const getBreadcrumb = () => { - // only show routes with meta.title - let matched = []; - const pathNum = findPathNum(route.path); - // multi-level menu - if (pathNum > 2) { - const reg = /\/\w+/gi; - const pathList = route.path.match(reg).map((item, index) => { - if (index !== 0) item = item.slice(1); - return item; - }); - getMatched(pathList, permissionStore.defaultRoutes, matched); - } else { - matched = route.matched.filter((item) => item.meta && item.meta.title); - } - // 判断是否为首页 - if (!isDashboard(matched[0])) { - matched = [{ path: '/index', meta: { title: '首页' } }].concat(matched); - } - levelList.value = matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false); -}; -const findPathNum = (str, char = '/') => { - if (typeof str !== 'string' || str.length === 0) return 0; - return str.split(char).length - 1; -}; -const getMatched = (pathList, routeList, matched) => { - const data = routeList.find((item) => item.path == pathList[0] || (item.name += '').toLowerCase() == pathList[0]); - if (data) { - matched.push(data); - if (data.children && pathList.length) { - pathList.shift(); - getMatched(pathList, data.children, matched); + // 1. 先获取原生的 matched + let matched = route.matched.filter((item) => item.meta && item.meta.title); + + // 🔥 关键:如果当前路由有 parentTitle,手动插入虚拟父级 + if (route.meta?.parentTitle) { + // 找到倒数第二个路由(通常是二级列表页) + const lastIndex = matched.length - 1; + if (lastIndex >= 0) { + // 创建虚拟父级路由对象 + const virtualParent = { + path: matched[lastIndex].path, + meta: { title: route.meta.parentTitle } + }; + + // 插入到倒数第二个位置 + matched = [...matched.slice(0, lastIndex), virtualParent, matched[lastIndex]]; } } + + // 2. 过滤掉设置了 breadcrumb: false 的路由 + levelList.value = matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false); + + console.log('最终面包屑:', levelList.value); }; -const isDashboard = (route: RouteLocationMatched) => { - const name = route && (route.name as string); - if (!name) { - return false; - } - return name.trim() === 'Index'; -}; + const handleLink = (item) => { const { redirect, path } = item; redirect ? router.push(redirect) : router.push(path); }; watchEffect(() => { - // if you go to the redirect page, do not update the breadcrumbs if (route.path.startsWith('/redirect/')) return; getBreadcrumb(); }); + onMounted(() => { getBreadcrumb(); }); diff --git a/src/permission.ts b/src/permission.ts index aaa01bf..db1a1b0 100644 --- a/src/permission.ts +++ b/src/permission.ts @@ -26,14 +26,6 @@ const isWhiteList = (path: string) => { router.beforeEach(async (to, from) => { NProgress.start(); - // ============================================== - // 🚥 调试日志:每次跳转都打印(方便看你跳去了哪) - // ============================================== - console.log('=========================================================='); - console.log('🔀 路由跳转:', from.path, ' → ', to.path); - console.log('🔐 是否有Token:', !!getToken()); - console.log('🚦 动态路由是否已加载:', isDynamicRoutesAdded); - const hasToken = getToken(); const userStore = useUserStore(); const permissionStore = usePermissionStore(); @@ -44,29 +36,16 @@ router.beforeEach(async (to, from) => { // 已登录 且 去登录页 → 跳首页 if (to.path === '/login') { - console.log('✅ 已登录,访问登录页,直接跳首页'); NProgress.done(); return '/'; } // 白名单页面直接放行 if (isWhiteList(to.path)) { - console.log('✅ 白名单页面,直接放行'); return; } - // ============================================== - // ✅ 【关键】只在【没有角色 + 没有加载过路由】时执行一次 - // ============================================== - console.log('👤 当前用户角色:', userStore.roles); - console.log('🧪 判断条件:roles.length === 0 && !isDynamicRoutesAdded'); - console.log('🧪 结果:', userStore.roles.length === 0 && !isDynamicRoutesAdded); - if (userStore.roles.length === 0 && !isDynamicRoutesAdded) { - console.log('=========================================================='); - console.log('🚀 【首次初始化】获取用户信息 + 生成动态路由(只执行一次)'); - console.log('=========================================================='); - isRelogin.show = true; // 获取用户信息 @@ -79,12 +58,10 @@ router.beforeEach(async (to, from) => { return '/'; } - console.log('✅ 用户信息获取成功:', res); isRelogin.show = false; // 生成动态路由 const accessRoutes = await permissionStore.generateRoutes(); - console.log('✅ 动态路由生成成功,数量:', accessRoutes.length); // 添加路由(避免重复添加) accessRoutes.forEach((route) => { @@ -95,7 +72,6 @@ router.beforeEach(async (to, from) => { // ✅ 标记:路由已添加 isDynamicRoutesAdded = true; - console.log('✅ 动态路由添加完成,标记 isDynamicRoutesAdded = true'); // 管理员登录强制跳转 if (res.user.userId === 1 && from.path === '/login') { diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts index 1169cc8..029b433 100644 --- a/src/store/modules/permission.ts +++ b/src/store/modules/permission.ts @@ -9,9 +9,46 @@ import ParentView from '@/components/ParentView/index.vue'; import InnerLink from '@/layout/components/InnerLink/index.vue'; import { ref } from 'vue'; import { createCustomNameComponent } from '@/utils/createCustomNameComponent'; +import { ElNotification } from 'element-plus'; // 确保引入了这个 // 匹配views里面所有的.vue文件 const modules = import.meta.glob('./../../views/**/*.vue'); + +const transformRoutes = (backendRoutes: any[]): any[] => { + // 1. 深拷贝 + const processedRoutes = JSON.parse(JSON.stringify(backendRoutes)); + + // 2. 创建2级路由映射表(key: activeMenu, value: 标题) + const level2TitleMap = new Map(); + + processedRoutes.forEach((level1Route: any) => { + if (!level1Route.children) return; + + level1Route.children.forEach((level2Route: any) => { + if (!level2Route.hidden) { + const fullPath = level1Route.path.endsWith('/') ? `${level1Route.path}${level2Route.path}` : `${level1Route.path}/${level2Route.path}`; + level2TitleMap.set(fullPath, level2Route.meta?.title); + } + }); + }); + + // 3. 给三级路由的 meta 加 parentTitle(不做嵌套,保持扁平) + processedRoutes.forEach((level1Route: any) => { + if (!level1Route.children) return; + + level1Route.children.forEach((route: any) => { + if (route.hidden && route.meta?.activeMenu) { + const parentTitle = level2TitleMap.get(route.meta.activeMenu); + if (parentTitle) { + // 🔥 关键:只加 parentTitle,不修改 children + route.meta.parentTitle = parentTitle; + } + } + }); + }); + + return processedRoutes; +}; export const usePermissionStore = defineStore('permission', () => { const routes = ref([]); const addRoutes = ref([]); @@ -45,16 +82,31 @@ export const usePermissionStore = defineStore('permission', () => { const setSidebarRouters = (routes: RouteRecordRaw[]): void => { sidebarRouters.value = routes; }; + + // -------------------------- + // 修改:generateRoutes 函数,加入转换逻辑 + // -------------------------- const generateRoutes = async (): Promise => { const res = await getRouters(); const { data } = res; - const sdata = JSON.parse(JSON.stringify(data)); - const rdata = JSON.parse(JSON.stringify(data)); - const defaultData = JSON.parse(JSON.stringify(data)); + console.log('后端原始路由:', data); + + // 🔥 关键:先把后端扁平路由转成嵌套格式 + const nestedData = transformRoutes(data); + console.log('转换后的嵌套路由:', nestedData); + + // 深拷贝数据(保留原有逻辑) + const sdata = JSON.parse(JSON.stringify(nestedData)); + const rdata = JSON.parse(JSON.stringify(nestedData)); + const defaultData = JSON.parse(JSON.stringify(nestedData)); + + // 后续使用转换后的 nestedData const sidebarRoutes = filterAsyncRouter(sdata); const rewriteRoutes = filterAsyncRouter(rdata, undefined, true); const defaultRoutes = filterAsyncRouter(defaultData); + const asyncRoutes = filterDynamicRoutes(dynamicRoutes); + asyncRoutes.forEach((route) => { router.addRoute(route); }); @@ -191,7 +243,7 @@ function duplicateRouteChecker(localRoutes: Route[], routes: Route[]) { const nameList: string[] = []; allRoutes.forEach((route) => { - const name = route.name.toString(); + const name = route.name?.toString(); if (name && nameList.includes(name)) { const message = `路由名称: [${name}] 重复, 会造成 404`; console.error(message); @@ -202,6 +254,8 @@ function duplicateRouteChecker(localRoutes: Route[], routes: Route[]) { }); return; } - nameList.push(route.name.toString()); + if (name) { + nameList.push(name); + } }); } diff --git a/src/views/monitor/logininfor/index.vue b/src/views/monitor/logininfor/index.vue index da928cc..fc5354e 100644 --- a/src/views/monitor/logininfor/index.vue +++ b/src/views/monitor/logininfor/index.vue @@ -1,5 +1,7 @@