diff --git a/capi-ui/src/api/menu.ts b/capi-ui/src/api/menu.ts new file mode 100644 index 0000000..f5d6062 --- /dev/null +++ b/capi-ui/src/api/menu.ts @@ -0,0 +1,45 @@ +import request from '@/utils/request' + +/** + * 菜单模块接口定义 + */ +export interface MenuItem { + menuId: string + menuName: string + menuCode: string + moduleCode: string + cicon: string + chref: string + ftenantId: string + fflowId: string | null + fflowTaskId: string | null + fflowState: string | null +} + +export interface ModuleItem { + moduleName: string + moduleCode: string + icon: string + href: string + menus: MenuItem[] +} + +export interface MenuResponse { + code: number + message: string + result: ModuleItem[] +} + +/** + * 获取模块菜单数据 + */ +export const getModuleMenus = async (): Promise => { + const response = await request.get('/Sys/login/getModules') + + if (response.code !== 200) { + console.error('获取菜单数据失败:', response.message) + throw new Error(`获取菜单失败: ${response.message}`) + } + + return response.result || [] +} diff --git a/capi-ui/src/components/HelloWorld.vue b/capi-ui/src/components/HelloWorld.vue deleted file mode 100644 index 4e67297..0000000 --- a/capi-ui/src/components/HelloWorld.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - - - diff --git a/capi-ui/src/components/Layout/AdminLayout.vue b/capi-ui/src/components/Layout/AdminLayout.vue deleted file mode 100644 index 6f7803b..0000000 --- a/capi-ui/src/components/Layout/AdminLayout.vue +++ /dev/null @@ -1,1083 +0,0 @@ - - - - - \ No newline at end of file diff --git a/capi-ui/src/components/Layout/AppLayout.vue b/capi-ui/src/components/Layout/AppLayout.vue new file mode 100644 index 0000000..614c92f --- /dev/null +++ b/capi-ui/src/components/Layout/AppLayout.vue @@ -0,0 +1,652 @@ + + + + + diff --git a/capi-ui/src/components/Layout/Main.vue b/capi-ui/src/components/Layout/Main.vue new file mode 100644 index 0000000..4949d2a --- /dev/null +++ b/capi-ui/src/components/Layout/Main.vue @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/capi-ui/src/components/Layout/styles/app-layout.css b/capi-ui/src/components/Layout/styles/app-layout.css new file mode 100644 index 0000000..bbd7e6a --- /dev/null +++ b/capi-ui/src/components/Layout/styles/app-layout.css @@ -0,0 +1,511 @@ +/* 基础样式 */ +* { margin: 0; padding: 0; box-sizing: border-box; } +html, body, .app-container { height: 100vh; overflow: hidden; } +.app-container { display: flex; flex-direction: column; } + +/* 顶部导航栏样式 */ +.header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px; + height: 64px; + width: 100%; + background: linear-gradient(90deg, #e6f7ff 0%, #f0f9ff 100%); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); +} + +.logo { + display: flex; + align-items: center; + padding-right: 52px; + border-right: 0px solid rgba(24, 144, 255, 0.1); + gap: 10px; +} + +.logo-img { + width: 65px; + height: 65px; + object-fit: contain; + vertical-align: middle; +} + +.system-name { + font-size: 18px; + font-weight: 600; + color: #1890ff; +} + +.header-actions { display: flex; align-items: center; gap: 16px; } + +.search-box { + display: flex; + align-items: center; + background: rgba(255, 255, 255, 0.8); + border-radius: 4px; + padding: 0 12px; + height: 36px; + width: 240px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); +} + +.search-icon { color: #8c8c8c; margin-right: 8px; } +.search-input { + background: transparent; + border: none; + outline: none; + color: #333; + width: 100%; + font-size: 14px; +} +.search-input::placeholder { color: #b3b3b3; } + +.action-icons { + display: flex; + align-items: center; + gap: 16px; + padding-right: 16px; + border-right: 1px solid rgba(24, 144, 255, 0.1); +} + +.action-icon { + color: #1890ff; + font-size: 18px; + cursor: pointer; + transition: all 0.2s; +} +.action-icon:hover { color: #096dd9; transform: scale(1.1); } + +.user-avatar { cursor: pointer; } +.avatar-img { + width: 36px; + height: 36px; + border-radius: 50%; + object-fit: cover; + border: 2px solid transparent; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + transition: all 0.2s; +} +.user-avatar:hover .avatar-img { + border-color: #1890ff; + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(24, 144, 255, 0.2); +} + +.user-menu { width: 160px; border-radius: 6px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); } +.logout-menu-item { color: #f5222d !important; } + +/* 左侧菜单样式 - 核心区域 */ +.side-menu-container { + background: #fff; + border-right: 1px solid #e0e0e0; + transition: all 0.3s ease; + position: relative; + display: flex; + flex-direction: column; + z-index: 10; /* 确保菜单在内容上方 */ +} + +/* 折叠状态的侧边栏 */ +.sider-collapsed { + width: 64px !important; +} + +/* 菜单加载状态 */ +.menu-loading { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; +} + +/* 菜单滚动容器 */ +.menu-scroll-container { + flex: 1; + padding: 16px 0; + overflow-y: auto; + padding-bottom: 60px; /* 为底部按钮留出空间 */ +} + +/* 父级菜单项容器 */ +.parent-menu-item { + margin-bottom: 4px; + border-radius: 4px; + overflow: hidden; +} + +/* 父级菜单标题基础样式 */ +.parent-menu-header { + height: 44px; + padding: 0 16px; + display: flex; + align-items: center; + justify-content: space-between; + cursor: pointer; + background-color: #fff; + transition: all 0.2s ease; + border-left: 3px solid transparent; +} + +/* 展开状态父菜单交互 */ +.parent-menu-header:hover { + background-color: #f5f9ff; + border-left-color: #1890ff; +} + +/* 折叠状态父菜单样式 */ +.parent-menu-header.collapsed-mode { + justify-content: center; + padding: 0; + border-left: none; + height: 48px; /* 增大点击区域 */ + width: 100%; +} +.parent-menu-header.collapsed-mode:hover { + background-color: #f5f9ff; + border-left: none; +} + +/* 折叠状态tooltip占位元素 */ +.collapsed-tooltip-placeholder { + display: inline-block; + width: 24px; + height: 24px; +} + +/* 父级菜单图标 */ +.parent-menu-icon { + color: #1890ff; + font-size: 16px; + margin-right: 12px; +} +/* 折叠状态图标 */ +.parent-menu-header.collapsed-mode .parent-menu-icon { + margin-right: 0; + font-size: 20px; /* 折叠时图标放大 */ +} + +/* 父级菜单文字 */ +.parent-menu-title { + flex: 1; + font-size: 14px; + color: #333; + font-weight: 500; +} + +/* 父级菜单箭头 */ +.parent-menu-caret { + color: #888; + font-size: 14px; + transition: transform 0.2s ease; +} + +/* 展开状态子菜单列表容器 */ +.sub-menu-list { + background-color: #fafafa; + overflow: hidden; + max-height: 0; + transition: max-height 0.3s ease-out; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.03); +} + +/* 展开状态子菜单显示 */ +.sub-menu-visible { + max-height: 500px; + transition: max-height 0.3s ease-in; +} + +/* 展开状态子菜单项样式 */ +.sub-menu-item { + height: 38px; + padding: 0 16px 0 44px; + display: flex; + align-items: center; + cursor: pointer; + transition: all 0.2s ease; + font-size: 14px; + color: #666; + border-left: 2px solid transparent; +} + +/* 子菜单图标 */ +.sub-menu-icon { + color: #666; + font-size: 14px; + margin-right: 10px; +} + +/* 展开状态子菜单项交互 */ +.sub-menu-item:hover { + background-color: #f0f7ff; + color: #1890ff; + padding-left: 42px; + border-left-color: #1890ff; +} +.sub-menu-item:hover .sub-menu-icon { + color: #1890ff; +} + +/* 子菜单项选中样式 */ +.sub-menu-item-active { + background-color: #e6f4ff; + color: #1890ff; + font-weight: 500; + padding-left: 42px; + border-left-color: #1890ff; +} +.sub-menu-item-active .sub-menu-icon { + color: #1890ff; +} + +/* 折叠状态:悬浮子菜单样式 */ +:deep(.collapsed-sub-menu) { + min-width: 180px !important; + border-radius: 6px !important; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15) !important; + padding: 6px 0 !important; + z-index: 999 !important; /* 确保悬浮菜单在最上层 */ +} +:deep(.collapsed-sub-menu .ant-menu-item) { + height: 40px !important; + line-height: 40px !important; + padding: 0 16px !important; + transition: all 0.2s ease !important; + display: flex !important; + align-items: center !important; + gap: 8px !important; +} +:deep(.collapsed-sub-menu .ant-menu-item:hover) { + background-color: #f5f9ff !important; + color: #1890ff !important; +} +:deep(.collapsed-sub-menu .ant-menu-item-selected) { + background-color: #e6f4ff !important; + color: #1890ff !important; +} +:deep(.collapsed-sub-menu .ant-menu-item .anticon) { + font-size: 14px !important; +} + +/* 折叠/展开按钮 */ +.collapse-trigger { + position: absolute; + bottom: 16px; + right: -18px; + background: #fff; + border: 1px solid #e0e0e0; + border-radius: 50%; + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + z-index: 11; /* 确保按钮在菜单上方 */ + color: #1890ff; + transition: all 0.2s ease; +} +.collapse-trigger:hover { + color: #096dd9; + box-shadow: 0 2px 4px rgba(24, 144, 255, 0.15); + transform: scale(1.05); +} + +/* 右侧内容区样式 */ +.right-content { + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; + background: #f8f9fa; +} + +.tab-bar { + background: #fff; + border-bottom: 1px solid #e0e0e0; + padding: 0 20px; + height: 44px; + display: flex; + align-items: center; + gap: 8px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03); + transition: all 0.3s ease; +} + +.console-tab { + height: 32px; + padding: 0 16px; + border-radius: 6px 6px 0 0; + border: none; + cursor: pointer; + font-size: 14px; + display: flex; + align-items: center; + color: #666; + background: transparent; + transition: all 0.2s ease; +} + +.console-tab-icon { margin-right: 6px; font-size: 14px; } +.console-tab--active { + background: #e8f4ff !important; + color: #1890ff; + font-weight: 500; + box-shadow: 0 1px 2px rgba(24, 144, 255, 0.1); +} +.console-tab:hover:not(.console-tab--active) { + background: #f5f9ff; + color: #1890ff; +} + +.tab-nav-btn { + width: 28px; + height: 28px; + padding: 0; + color: #666; + margin: 0; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; +} +.tab-nav-btn:hover { background: #f5f9ff; color: #1890ff; } +.tab-nav-btn:disabled { color: #ccc !important; cursor: not-allowed; background: transparent !important; } + +.google-tabs { flex: 1; overflow: hidden; } +:deep(.ant-tabs-nav) { height: 100%; margin: 0 !important; } +:deep(.ant-tabs-nav-list) { height: 100%; align-items: center; } +:deep(.ant-tabs-tab) { + height: 36px; + padding: 0 16px; + margin: 0 2px; + border-radius: 6px 6px 0 0; + background: transparent; + transition: all 0.2s ease; + font-size: 14px; + color: #666; +} +:deep(.ant-tabs-tab:hover:not(.ant-tabs-tab-active)) { background: #f5f9ff; color: #1890ff; } +:deep(.ant-tabs-tab.ant-tabs-tab-active) { + background: #e8f4ff !important; + color: #1890ff !important; + font-weight: 500; + box-shadow: 0 1px 2px rgba(24, 144, 255, 0.1); + border-bottom: 2px solid #1890ff; +} +:deep(.ant-tabs-ink-bar) { display: none; } + +.tab-title-container { + display: flex; + align-items: center; + gap: 6px; + height: 100%; + padding: 0 4px; +} +.tab-title-text { white-space: nowrap; } +.tab-close-btn { + width: 16px; + height: 16px; + display: none; + align-items: center; + justify-content: center; + border-radius: 50%; + color: #999; + cursor: pointer; + transition: all 0.2s; +} +.tab-title-container:hover .tab-close-btn { display: inline-flex; } +:deep(.ant-tabs-tab-active) .tab-close-btn { color: #1890ff; } +.tab-close-btn:hover { background: #d1eaff; color: #1890ff; } + +.close-all-btn { + color: #666; + margin-left: 2px; + font-size: 13px; + display: flex; + align-items: center; + border-radius: 4px; + padding: 0 10px; +} +.close-all-btn:hover { color: #1890ff; background: #f5f9ff; } +.close-all-btn:disabled { color: #ccc !important; cursor: not-allowed; background: transparent !important; } +.close-icon { margin-right: 4px; font-size: 12px; } + +.main-content { + flex: 1; + padding: 16px; + overflow-y: auto; +} + +.home-content { + background: #fff; + padding: 24px; + border-radius: 8px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + height: 100%; + overflow: auto; + min-height: calc(100vh - 64px - 44px - 32px); +} + +/* 内容加载状态 */ +.content-loading { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + min-height: 300px; +} + +/* 组件加载错误样式 */ +.component-error { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + min-height: 300px; + color: #f5222d; + padding: 20px; + text-align: center; +} + +.error-icon { + font-size: 48px; + margin-bottom: 16px; +} + +.error-path { + font-family: monospace; + margin-top: 8px; + color: #8c8c8c; + background: #f5f5f5; + padding: 4px 8px; + border-radius: 4px; + word-break: break-all; + max-width: 90%; +} + +/* 退出对话框样式 */ +:deep(.logout-modal-wrapper .ant-modal) { width: 480px !important; } +.logout-body { text-align: center; padding: 8px 0 4px; } +.logout-title { + font-size: 20px; + font-weight: 600; + margin-bottom: 12px; + text-align: left; + color: #333; +} +.logout-desc { + font-size: 16px; + color: #555; + margin-bottom: 32px; + text-align: left; +} +.logout-actions { + display: flex; + justify-content: flex-end; + gap: 12px; + margin-top: 24px; +} \ No newline at end of file diff --git a/capi-ui/src/router/index.ts b/capi-ui/src/router/index.ts index 83542aa..bf8dc57 100644 --- a/capi-ui/src/router/index.ts +++ b/capi-ui/src/router/index.ts @@ -1,26 +1,30 @@ -import { createRouter, createWebHistory } from 'vue-router' +// router/index.ts +import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' -const routes = [ - { - path: '/', - redirect: '/login' - }, +/* 1. 固定路由 */ +const constantRoutes: RouteRecordRaw[] = [ + { path: '/', redirect: '/login' }, { path: '/login', name: 'Login', component: () => import('@/views/login/Login.vue') }, { - path: '/dashboard', - name: 'Dashboard', - component: () => import('@/views/sys/Dashboard.vue'), + path: '/index', + name: 'Index', + component: () => import('@/views/sys/index.vue'), meta: { requiresAuth: true } + }, + { + path: '/biz/data/index', // 路由PATH,与chref对应 + name: 'DataIndex', + component: () => import('@/biz/data/index.vue') // 对应的组件 } ] const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), - routes + routes: [...constantRoutes] }) export default router \ No newline at end of file diff --git a/capi-ui/src/views/biz/data/index.vue b/capi-ui/src/views/biz/data/index.vue new file mode 100644 index 0000000..eef08bc --- /dev/null +++ b/capi-ui/src/views/biz/data/index.vue @@ -0,0 +1,162 @@ + + + + + \ No newline at end of file diff --git a/capi-ui/src/views/sys/Dashboard.vue b/capi-ui/src/views/sys/Dashboard.vue index 7783308..41e5bbf 100644 --- a/capi-ui/src/views/sys/Dashboard.vue +++ b/capi-ui/src/views/sys/Dashboard.vue @@ -1,48 +1,20 @@ - \ No newline at end of file diff --git a/capi-ui/src/views/sys/main.vue b/capi-ui/src/views/sys/main.vue index 02409e3..eb28630 100644 --- a/capi-ui/src/views/sys/main.vue +++ b/capi-ui/src/views/sys/main.vue @@ -1,4 +1,6 @@