diff --git a/config/config.dev.ts b/config/config.dev.ts new file mode 100644 index 0000000..4b6c4cf --- /dev/null +++ b/config/config.dev.ts @@ -0,0 +1,15 @@ +import { defineConfig } from '@umijs/max'; +import routes from './routes/base'; + +export default defineConfig({ + routes, + define: { + APP_ENV: process.env.NODE_ENV || 'development', + COMMON_URL: 'http://10.0.1.231:31300', // 基建服务 + MATERIAL_URL: 'http://10.0.0.222', + CABINET_HOST: '/cabinet/', + VIDEO_HOST: '/video/', + ALGORITHM_HOST: '/algorithm/', + MATERIAL_HOST: '/material/' + }, +}); diff --git a/config/config.prod.ts b/config/config.prod.ts index fed0ba4..cffed3f 100644 --- a/config/config.prod.ts +++ b/config/config.prod.ts @@ -1,6 +1,8 @@ import { defineConfig } from '@umijs/max'; +import routes from './routes'; export default defineConfig({ + routes, define: { APP_ENV: 'production', CABINET_HOST: '/cabinet/', diff --git a/config/config.test.ts b/config/config.test.ts new file mode 100644 index 0000000..74fe059 --- /dev/null +++ b/config/config.test.ts @@ -0,0 +1,15 @@ +import { defineConfig } from '@umijs/max'; +import routes from './routes'; + +export default defineConfig({ + routes, + define: { + APP_ENV: 'test', + CABINET_HOST: '/cabinet/', + VIDEO_HOST: '/video/', + ALGORITHM_HOST: '/algorithm/', + MATERIAL_HOST: '/material/', + COMMON_URL: 'http://10.0.1.231:31300', // 基建服务 + MATERIAL_URL: 'http://10.0.0.222', + }, +}); diff --git a/config/config.ts b/config/config.ts index f8c9515..a6e7228 100644 --- a/config/config.ts +++ b/config/config.ts @@ -1,5 +1,7 @@ import { defineConfig } from '@umijs/max'; -import routes from './routes'; +import pxtorem from 'postcss-pxtorem'; + +const rootFontSize = 80 // 设计稿尺寸:32px = 1rem; @default 80 export default defineConfig({ favicons: ['/assets/logo.jpg'], @@ -15,10 +17,42 @@ export default defineConfig({ }, }, mfsu: false, - routes, npmClient: 'pnpm', - define: { - APP_ENV: process.env.NODE_ENV || 'development', - COMMON_URL: 'http://10.0.0.204:30058', // 基建服务 + alias: { + '@src': '/src', + '@components': '/src/components', + '@constants': '/src/constants', + '@common': '/src/common', + '@utils': '/src/utils', + '@public': '/public', }, + define: { + // ROUTES: routes, + // PLATFORM_TITLE: pkg.description, + ROOT_FONT_SIZE: rootFontSize, + APP_LIST: [ + { + name: 'video', // 视频分析 + entry: '//localhost:30088/', + }, + { + name: 'cabinet', // 盒子管理 + entry: '//localhost:30068/', + }, + { + name: 'material', // 物料仓 + entry: '//localhost:30098/', + }, + { + name: 'algorithm', // 算法分析 + entry: '//localhost:30078/', + }, + ] + }, + extraPostCSSPlugins: [ + pxtorem({ + rootValue: rootFontSize,//结果为:设计稿元素尺寸/16,比如元素宽320px,最终页面会换算成 20rem + propList: ['*'], + }), + ] }); diff --git a/config/routes/base.ts b/config/routes/base.ts new file mode 100644 index 0000000..279260a --- /dev/null +++ b/config/routes/base.ts @@ -0,0 +1,44 @@ +const routes = [ + { + path: '/', + redirect: 'material/home', + }, + { + name: '测试页(开发环境)', + path: '/test', + routes: [ + { + name: '性能测试', + path: '/test/demo', + component: '@/pages/test', + }, + ], + }, + { + path: '/', + component: '@/layouts/BaseLayout/index.tsx', + routes: [ + { + name: 'cabinet', + path: '/cabinet/*', + microApp: 'cabinet', + }, + { + name: 'algorithm', + path: '/algorithm/*', + microApp: 'algorithm', + }, + { + name: 'video', + path: '/video/*', + microApp: 'video', + }, + ], + }, + { + name: '物料库', + path: '/material/*', + microApp: 'material', + }, +]; +export default routes; diff --git a/config/routes/index.ts b/config/routes/index.ts index cdafe02..5e51a80 100644 --- a/config/routes/index.ts +++ b/config/routes/index.ts @@ -1,28 +1,9 @@ -const routes = [ - { - name: 'home', - path: '/', - redirect: '/material/home', - }, - { - name: 'cabinet', - path: '/cabinet/*', - microApp: 'cabinet', - }, - { - name: 'algorithm', - path: '/algorithm/*', - microApp: 'algorithm', - }, - { - name: 'video', - path: '/video/*', - microApp: 'video', - }, - { - name: '物料库', - path: '/material/*', - microApp: 'material', - }, -]; -export default routes; +import BaseRoutes from './base' + +const routes = BaseRoutes.concat([{ + name: 'home', + path: '/', + redirect: '/material/home', +}]) + +export default routes \ No newline at end of file diff --git a/config/routes/types.d.ts b/config/routes/types.d.ts new file mode 100644 index 0000000..302ab47 --- /dev/null +++ b/config/routes/types.d.ts @@ -0,0 +1,67 @@ +export interface RouteProps { + icon: string; + // https://beta-pro.ant.design/docs/advanced-menu + // --- + // 新页面打开 + target: '_blank'; + // 不展示顶栏 + headerRender: boolean; + // 不展示页脚 + footerRender: boolean; + // 不展示菜单 + menuRender: boolean; + // 不展示菜单顶栏 + menuHeaderRender: boolean; + // 权限配置,需要与 plugin-access 插件配合使用 + access: 'canRead'; + // 隐藏子菜单 + hideChildrenInMenu: boolean; + // 隐藏自己和子菜单 + hideInMenu: boolean; + // 在面包屑中隐藏 + hideInBreadcrumb: boolean; + // 子项往上提,仍旧展示, + flatMenu: boolean; + /** + * @name false 时不展示顶栏 + */ + headerRender?: boolean; + /** + * @name false 时不展示页脚 + */ + footerRender?: boolean; + /** + * @name false 时不展示菜单 + */ + menuRender?: boolean; + /** + * @name false 时不展示菜单顶栏 + */ + menuHeaderRender?: boolean; + + /** + * @name 固定顶栏 + **/ + fixedHeader: boolean; + + /** + * @name 固定菜单 + */ + fixSiderbar: boolean; + + /** + * @name theme for nav menu + * @name 导航菜单的主题 + */ + navTheme: 'dark' | 'light' | 'realDark' | undefined; + /** + * @name nav menu position: `side` or `top` + * @name 导航菜单的位置 + * @description side 为正常模式,top菜单显示在顶部,mix 两种兼有 + */ + layout: 'side' | 'top' | 'mix'; + /** + * @name 顶部导航的主题,mix 模式生效 + */ + headerTheme: 'dark' | 'light'; +} diff --git a/deploy/build.sh b/deploy/build.sh index 00a1980..6fcd92c 100755 --- a/deploy/build.sh +++ b/deploy/build.sh @@ -1,6 +1,6 @@ pnpm install -pnpm run build:master +pnpm run build:test mkdir -p ./app/public/ diff --git a/global.d.ts b/global.d.ts index 2db7b8b..4c489c1 100644 --- a/global.d.ts +++ b/global.d.ts @@ -1,5 +1,8 @@ declare const APP_ENV: string; +declare const APP_LIST: any[]; declare const COMMON_URL: string; +declare const MATERIAL_URL: string; declare const VIDEO_HOST: string; declare const ALGORITHM_HOST: string; declare const MATERIAL_HOST: string; +declare const ROOT_FONT_SIZE: number; diff --git a/package.json b/package.json index a643b2c..eb6829c 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "author": "dev <710328466@qq.com>", "scripts": { "build:master": "cross-env APP_ENV=production max build", + "build:test": "cross-env UMI_ENV=test max build", "comp": "cross-env max g component", "dev": "max dev", "format": "prettier --cache --write .", @@ -14,10 +15,12 @@ "start": "npm run dev" }, "dependencies": { + "@ant-design/cssinjs": "^1.21.0", "@ant-design/icons": "^5.3.0", "@ant-design/pro-components": "^2.6.49", "@umijs/max": "^4.1.1", "@zhst/func": "^0.8.1", + "@zhst/hooks": "^0.15.0", "@zhst/request": "^0.12.1", "@zhst/slave": "^0.7.1", "antd": "^5.14.1", @@ -28,6 +31,7 @@ "@types/react-dom": "^18.2.19", "husky": "^9.0.11", "lint-staged": "^15.2.2", + "postcss-pxtorem": "^6.1.0", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^3.2.4", "prettier-plugin-packagejson": "^2.4.11", diff --git a/src/app.ts b/src/app.ts index 5d0ce4b..7e5d606 100644 --- a/src/app.ts +++ b/src/app.ts @@ -20,46 +20,20 @@ export const useQiankunStateForSlave = useFatherAction; */ const hostname = location.port ? `${location.hostname}:${location.port}` : location.hostname -export const qiankun = { - apps: [ +const initApp = (appList: any[]) => { + return appList.map(app => ( { - name: 'cabinet', // 盒子管理 - entry: - APP_ENV === 'production' - ? `//${hostname}/cabinet/` - : '//localhost:30088/', + name: app.name, + entry: APP_ENV === 'production' ? `//${hostname}/${app.name}/` : app.entry, props: {}, singular: false, credentials: true, - }, - { - name: 'video', // AI 智能分析仓 - entry: - APP_ENV === 'production' - ? `//${hostname}/video/` - : '//localhost:30068/', - singular: false, - credentials: true, - }, - { - name: 'algorithm', // AI 算法分析 - entry: - APP_ENV === 'production' - ? `//${hostname}/algorithm/` - : '//localhost:30078/', - singular: false, - credentials: true, - }, - { - name: 'material', // 物料库 - entry: - APP_ENV === 'production' - ? `//${hostname}/material/` - : '//localhost:30098/', - singular: false, - credentials: true, - }, - ], + } + )) +} + +export const qiankun = { + apps: initApp(APP_LIST), lifeCycles: { // 所有子应用在挂载完成时,打印 props 信息 async afterMount() { diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000..bd6d386 Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/global.less b/src/global.less index e209c16..ef5d060 100644 --- a/src/global.less +++ b/src/global.less @@ -1,4 +1,51 @@ -* { - padding: 0; - margin: 0; +#logo { + justify-content: center; } + +#root-master .ant-pro-layout .ant-pro-layout-content { + padding: 0; +} + +#root-master .ant-layout-header .ant-menu { + justify-content: center; + .ant-menu-item { + color: #191919; + } +} + +#root-master .ant-layout-header .ant-pro-base-menu-horizontal-item-text { + color: #fff; +} + +.ant-menu .ant-menu-item-only-child { + color: #191919!important; +} + +#root-master .ant-pro-page-container { + .ant-page-header { + padding: 7px 0; + + .ant-page-header-heading { + padding: 0 20px; + } + + .ant-page-header-heading-title { + font-size: 14px; + line-height: 20px; + color: #191919; + } + } + + .ant-layout-content { + padding: 0; + } + + .ant-pro-page-container-children-container { + padding: 0; + margin: 0 20px 20px 20px; + } +} +.ant-menu-light.ant-menu-horizontal>.ant-menu-item, +.ant-menu-light.ant-menu-horizontal>.ant-menu-submenu { + border-radius: 0px; +} \ No newline at end of file diff --git a/src/layouts/BaseLayout/defaultRoutes.ts b/src/layouts/BaseLayout/defaultRoutes.ts new file mode 100644 index 0000000..c4f0ec7 --- /dev/null +++ b/src/layouts/BaseLayout/defaultRoutes.ts @@ -0,0 +1,140 @@ +import { RouteProps } from "config/routes/types"; + +const formatRoutes = ({ routes = [], prefix = null }) => { + return routes.map((route) => { + const { name, path, routes: children,...rest } = route; + return { + name, + path: route.routes?.length ? path : `/${prefix}${path}`, + routes: formatRoutes({ routes: children, prefix }), + ...rest, + }; + }); +} + + +// ?------------ 视频模块路由 ----------------- +const videoRoutes = [ + { + name: '实时监控', + path: '/realTimeMonitor', + component: '@/pages/RealTimeMonitor', + }, + { + name: '预警记录', + path: '/alarmManage', + routes: [ + { + name: '预警记录', + path: '/alarmManage/AlarmRecord', + component: '@/pages/AlarmRecord', + }, + { + name: '误报库', + path: '/alarmManage/errorLibrary', + component: '@/pages/errorLibrary', + }, + ] + }, + { + name: '人流量统计', + path: '/trafficCount', + component: '@/pages/TrafficCount', + }, +] + +const algorithmRoutes = [ + { + name: '算法管理', + path: '/algorithmManage/algorithmManage', + routes: [ + { + name: '算法配置', + path: '/algorithmManage/algorithmConfig', + component: '@/pages/AlgorithmManage', + }, + { + name: '时间模版', + path: '/algorithmManage/timeTemplate', + component: '@/pages/BoxManage/TimeTemplate', + }, + ], + }, + { + name: '摄像头管理', + path: '/cameraManage', + component: '@/pages/cameraManage/index', + }, + { + name: '系统配置', + path: '/systemConfig', + routes: [ + // { + // name: '电子地图配置', + // path: '/systemConfig/mapConfig', + // component: '@/pages/SystemConfig/MapConfig', + // }, + { + name: '修改密码', + path: '/systemConfig/editPassword', + component: '@/pages/SystemConfig/EditPassword', + }, + { + name: '组织管理', + path: '/systemConfig/organizeManage', + component: '@/pages/SystemConfig/OrganizeManage', + }, + { + name: '角色管理', + path: '/systemConfig/roleManage', + component: '@/pages/SystemConfig/RoleManage', + }, + { + name: '用户管理', + path: '/systemConfig/userManage', + component: '@/pages/SystemConfig/UserManage', + }, + ], + }, +] + +const routes = [ + { + name: '首页', + path: '/home', + hideInMenu: true, + component: '@/pages/home', + }, + { + name: '登录', + path: '/login', + hideInMenu: true, + component: '@/pages/login', + }, + { + name: '未找到页面', + path: '/*', + layout: false, + hideInMenu: true, + component: '@/pages/404', + }, + { + name: '无权限页面', + path: '/403', + layout: false, + hideInMenu: true, + component: '@/pages/403', + }, + + { + name: '修改密码', + path: '/video/editPassword', + component: '@/pages/EditPassword', + hideInMenu: true, + }, + ...formatRoutes({ routes: videoRoutes, prefix: 'video' }), + ...formatRoutes({ routes: algorithmRoutes, prefix: 'algorithm'}) +]; + +export default routes; + diff --git a/src/layouts/BaseLayout/index.less b/src/layouts/BaseLayout/index.less new file mode 100644 index 0000000..e69de29 diff --git a/src/layouts/BaseLayout/index.tsx b/src/layouts/BaseLayout/index.tsx new file mode 100644 index 0000000..91b01d1 --- /dev/null +++ b/src/layouts/BaseLayout/index.tsx @@ -0,0 +1,131 @@ +// import { SettingOutlined, ShopOutlined } from '@ant-design/icons'; +import logo from '@/assets/logo.png'; +import { + MicroAppLink, + Outlet, + history, + useLocation, + useModel, +} from '@umijs/max'; +import { px2remTransformer, StyleProvider } from '@ant-design/cssinjs'; +import { ProConfigProvider, ProLayout, ProSettings } from '@ant-design/pro-components'; +import React, { useState } from 'react'; +import slave from '@zhst/slave'; +import routes from './defaultRoutes' +import styles from './index.less'; +import { BankOutlined } from '@ant-design/icons'; +import { Dropdown, App } from 'antd'; + +const Layout = (props) => { + const [settings] = useState | undefined>({ + layout: 'top', + }); + const location = useLocation(); + const [pathname, setPathname] = useState('') + const px2rem = px2remTransformer({ + rootValue: ROOT_FONT_SIZE, // 32px = 1rem; @default 16 + }); + const { userInfo = {}, hasAuth } = useModel('user') || {}; + + console.log(userInfo) + + return ( +
+ + + + { + return ( + { + history.push('/algorithm/editPassword'); + setPathname('/algorithm/editPassword') + }, + }, + { + key: 'logout', + label: 退出登录, + onClick: () => slave.logOut(`${COMMON_URL || location.origin}/material/login`), + }, + ], + }} + > + {dom} + + ); + }, + }} + actionsRender={() => { + return [ + // + // + // , + // + //
+ // + // AI算法仓平台 + //
+ //
+ ]; + }} + location={{ + pathname, + }} + menuItemRender={(item: { path: string }, dom) => ( +
{ + history.push(item.path || '/'); + setPathname(item.path); + }} + > + {dom} +
+ )} + {...settings} + > + +
+
+
+
+
+ ); +}; + +export default Layout; diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index e4b51fc..f845d9b 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -1,13 +1,13 @@ -import { SettingOutlined, ShopOutlined } from '@ant-design/icons'; +// import { SettingOutlined, ShopOutlined } from '@ant-design/icons'; import { Outlet } from '@umijs/max'; -import { FloatButton } from 'antd'; +// import { FloatButton } from 'antd'; import React from 'react'; import styles from './index.less'; const Layout = () => { return (
- { onClick={() => (location.href = 'http://10.0.0.204:30080')} /> } /> - + */}
); diff --git a/src/models/user.ts b/src/models/user.ts new file mode 100644 index 0000000..3b46bb2 --- /dev/null +++ b/src/models/user.ts @@ -0,0 +1,36 @@ +// 全局共享数据 +import userController from '../service/user'; +import { get } from '@zhst/func'; +import { useRequest } from '@zhst/hooks'; +import { useState } from 'react'; + +const useUser = () => { + const [userInfo, setUserInfo] = useState(null); + const [userRights, setUserRights] = useState(null); + // @ts-ignore + const { run, loading: userLoading } = useRequest(async () => { + let res = await userController.postUserInfo(); + let userRightsRes = await userController.getUserRights({ + userId: res.userInfo.userId, + }); + setUserRights(userRightsRes); + setUserInfo(res.userInfo); + }); + + // 判断用户是否具有某一模块的路由权限 + const hasAuth = (key: string) => { + const funcs = get(userRights, 'functions') + ? JSON.parse(get(userRights, 'functions')) + : []; + return funcs.some((item: string) => item === key); + }; + + return { + userInfo, + run, + userLoading, + hasAuth, + }; +}; + +export default useUser; diff --git a/src/pages/error/index.tsx b/src/pages/error/index.tsx index 91db175..fbcbe85 100644 --- a/src/pages/error/index.tsx +++ b/src/pages/error/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; -export default function HomePage() { +export default function ErrorPage() { return (

error

diff --git a/src/pages/test/index.less b/src/pages/test/index.less new file mode 100644 index 0000000..10cc4b3 --- /dev/null +++ b/src/pages/test/index.less @@ -0,0 +1,4 @@ +.ant-card-head { + position: relative; + z-index: 2; +} \ No newline at end of file diff --git a/src/pages/test/index.tsx b/src/pages/test/index.tsx new file mode 100644 index 0000000..2c89b69 --- /dev/null +++ b/src/pages/test/index.tsx @@ -0,0 +1,14 @@ +import { MicroAppWithMemoHistory } from '@umijs/max'; +import { Card } from 'antd' +import React from 'react'; +import './index.less' + +export default function TestPage() { + return ( + + + + ) +} diff --git a/src/service/user/UserController.ts b/src/service/user/UserController.ts new file mode 100644 index 0000000..526c95a --- /dev/null +++ b/src/service/user/UserController.ts @@ -0,0 +1,30 @@ +/* eslint-disable */ +import { request } from '@umijs/max'; + +export async function postUserInfo(options?: { [key: string]: any }) { + return request('/v1/ma/BusinessApi/GetUserInfo', { + method: 'POST', + ...(options || {}), + }); +} + +// 获取菜单 +export async function postMenuList(options?: { [key: string]: any }) { + return request('/v1/ma/BusinessApi/GetAlgorithmsNav', { + method: 'POST', + data: {}, + ...(options || {}), + }); +} + +// 获取当前用户路由权限 +export async function getUserRights( + params: { userId: string }, + options?: { [key: string]: any }, +) { + return request('/v1/ma/BusinessApi/GetUserRights', { + method: 'POST', + data: params, + ...(options || {}), + }); +} diff --git a/src/service/user/index.ts b/src/service/user/index.ts new file mode 100644 index 0000000..fe90da1 --- /dev/null +++ b/src/service/user/index.ts @@ -0,0 +1,5 @@ +/* eslint-disable */ + +import * as UserController from './UserController'; + +export default UserController diff --git a/src/service/user/typings.d.ts b/src/service/user/typings.d.ts new file mode 100644 index 0000000..8439b48 --- /dev/null +++ b/src/service/user/typings.d.ts @@ -0,0 +1,55 @@ +/* eslint-disable */ +// 该文件由 OneAPI 自动生成,请勿手动修改! + +declare namespace USER_API { + + interface PageInfo { + current?: number; + pageSize?: number; + total?: number; + list?: Array>; + } + + interface PageInfo_UserInfo_ { + current?: number; + pageSize?: number; + total?: number; + list?: Array; + } + + interface Result { + success?: boolean; + errorMessage?: string; + data?: Record; + } + + interface Result_PageInfo_UserInfo__ { + success?: boolean; + errorMessage?: string; + data?: PageInfo_UserInfo_; + } + + interface Response { + code: number; + data: Data; + message: string; + [property: string]: any; + } + + interface Data { + navValue: string; + [property: string]: any; + } + + interface Result_UserInfo_ { + success?: boolean; + errorMessage?: string; + data?: UserInfo; + } + + interface UserInfo { + username?: string; + userId?: string; + [property: string]: any; + } +} diff --git a/tsconfig.json b/tsconfig.json index 313d395..3a3d63c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,15 @@ { "compilerOptions": { - "jsx": "react" + "jsx": "react", + "paths": { + "@src/*": ["./src/*"], + "@public/*": ["./public/*"], + "@/*": ["./src/*"], + "@components/*": ["./src/components/*"], + "@constants/*": ["./src/constants/*"], + "@common/*": ["./src/common/*"], + "@utils/*": ["./src/utils/*"], + }, }, "extends": "./src/.umi/tsconfig.json" }