diff --git a/src/api/initHome.ts b/src/api/initHome.ts index 658a3394..daf6d767 100644 --- a/src/api/initHome.ts +++ b/src/api/initHome.ts @@ -1,7 +1,7 @@ import server from '@/utils/request'; // 更新全部菜单 -export const updateMenus = (data: any) => server.path(`/menu/iot/_all`, data) +export const updateMenus = (data: any) => server.patch(`/menu/iot/_all`, data) // 添加角色 export const addRole = (data: any) => server.post(`/role`, data) diff --git a/src/api/iot-card/cardManagement.ts b/src/api/iot-card/cardManagement.ts index 768aba60..0a4f53d5 100644 --- a/src/api/iot-card/cardManagement.ts +++ b/src/api/iot-card/cardManagement.ts @@ -121,4 +121,16 @@ export const edit = (data: any) => server.put(`/network/card/${data.id}`, data); * 根据id查看详情 * @param id */ -export const queryDetail = (id: any) => server.get(`/network/card/${id}`); \ No newline at end of file +export const queryDetail = (id: any) => server.get(`/network/card/${id}`); + +/** + * 查询物联卡充值缴费日志 + * @param data + */ +export const queryRechargeList = (data: any) => server.post(`/network/card/recharge/_log`, data) + +/** + * 充值 + * @param data + */ +export const recharge = (data: any) => server.post(`/network/card/_recharge`, data) \ No newline at end of file diff --git a/src/api/link/accessConfig.ts b/src/api/link/accessConfig.ts index 0aaa1986..869dab76 100644 --- a/src/api/link/accessConfig.ts +++ b/src/api/link/accessConfig.ts @@ -36,7 +36,7 @@ export const undeploy = (id: string) => export const deploy = (id: string) => server.post(`/gateway/device/${id}/_startup`); -export const del = (id: string) => server.remove(`/gateway/device/${id}`); +export const remove = (id: string) => server.remove(`/gateway/device/${id}`); export const getResourcesCurrent = () => server.get(`/network/resources/alive/_current`); diff --git a/src/api/notice/config.ts b/src/api/notice/config.ts index c75aa1f5..bbb21c3d 100644 --- a/src/api/notice/config.ts +++ b/src/api/notice/config.ts @@ -1,5 +1,5 @@ import { patch, post, get, remove } from '@/utils/request' -import { TemplateFormData } from '@/views/notice/Template/types' +import type { TemplateFormData } from '@/views/notice/Template/types' export default { // 列表 diff --git a/src/api/notice/template.ts b/src/api/notice/template.ts index 45c90010..088f37aa 100644 --- a/src/api/notice/template.ts +++ b/src/api/notice/template.ts @@ -1,5 +1,5 @@ import { patch, post, get, remove } from '@/utils/request' -import { BindConfig } from '@/views/notice/Template/types' +import type { BindConfig } from '@/views/notice/Template/types' export default { // 列表 diff --git a/src/components/CardBox/index.vue b/src/components/CardBox/index.vue index cb958ce7..0915064a 100644 --- a/src/components/CardBox/index.vue +++ b/src/components/CardBox/index.vue @@ -231,6 +231,13 @@ const handleClick = () => { :deep(.card-item-content-title) { cursor: pointer; + font-size: 16px; + font-weight: 700; + color: @primary-color; + width: calc(100% - 100px); + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } :deep(.card-item-heard-name) { diff --git a/src/components/Layout/BasicLayoutPage.vue b/src/components/Layout/BasicLayoutPage.vue index 054f54ad..ebc3adc4 100644 --- a/src/components/Layout/BasicLayoutPage.vue +++ b/src/components/Layout/BasicLayoutPage.vue @@ -21,6 +21,7 @@ import { ProLayout } from '@/components/Layout' import DefaultSetting from '../../../config/config' import { useMenuStore } from '@/store/menu' +import { clearMenuItem } from 'components/Layout/utils' type StateType = { collapsed: boolean @@ -39,7 +40,7 @@ const layoutConf = reactive({ siderWidth: DefaultSetting.layout.siderWidth, logo: DefaultSetting.layout.logo, title: DefaultSetting.layout.title, - menuData: menu.menus, + menuData: clearMenuItem(menu.siderMenus), }); const state = reactive({ diff --git a/src/components/Layout/components/Header/index.tsx b/src/components/Layout/components/Header/index.tsx index c4bfc014..a804a3a2 100644 --- a/src/components/Layout/components/Header/index.tsx +++ b/src/components/Layout/components/Header/index.tsx @@ -32,7 +32,7 @@ export const HeaderView = defineComponent({ height: `${headerHeight.value}px`, lineHeight: `${headerHeight.value}px`, width: `100%`, - + background: 'transparent' }} /> :nth-child(1) { - min-height: 100%; - } - } + //.children-full-height { + // > :nth-child(1) { + // min-height: 100%; + // } + //} } } \ No newline at end of file diff --git a/src/components/Layout/components/SiderMenu/SiderMenu.less b/src/components/Layout/components/SiderMenu/SiderMenu.less index 6b230073..bc177192 100644 --- a/src/components/Layout/components/SiderMenu/SiderMenu.less +++ b/src/components/Layout/components/SiderMenu/SiderMenu.less @@ -13,4 +13,8 @@ flex-direction: column; height: 100%; } + + .ant-menu-inline { + background: transparent; + } } diff --git a/src/components/Search/Search.vue b/src/components/Search/Search.vue index fcad9502..4abeb789 100644 --- a/src/components/Search/Search.vue +++ b/src/components/Search/Search.vue @@ -286,6 +286,7 @@ handleItems() .JSearch-warp { padding: 24px; background-color: #fff; + margin-bottom: 24px; .JSearch-content { display: flex; diff --git a/src/components/Table/index.module.less b/src/components/Table/index.module.less index a0e94e07..98a53a92 100644 --- a/src/components/Table/index.module.less +++ b/src/components/Table/index.module.less @@ -10,17 +10,23 @@ .jtable-body-header-right { display: flex; gap: 8px; - .jtable-setting-item { - color: rgba(0, 0, 0, 0.75); - font-size: 16px; - cursor: pointer; + align-items: center; + .jtable-body-header-right-button { + display: flex; + margin-left: 10px; + gap: 8px; + .jtable-setting-item { + color: rgba(0, 0, 0, 0.75); + font-size: 16px; + cursor: pointer; - &:hover { - color: @primary-color-hover; - } + &:hover { + color: @primary-color-hover; + } - &.active { - color: @primary-color-active; + &.active { + color: @primary-color-active; + } } } } diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx index fb778495..774b95b8 100644 --- a/src/components/Table/index.tsx +++ b/src/components/Table/index.tsx @@ -73,6 +73,7 @@ const JTable = defineComponent({ slots: [ 'headerTitle', // 顶部左边插槽 'card', // 卡片内容 + 'rightExtraRender' ], emits: [ 'modelChange', // 切换卡片和表格 @@ -254,16 +255,22 @@ const JTable = defineComponent({ {slots.headerTitle && slots.headerTitle()}
-
{ - _model.value = ModelEnum.CARD - }}> - -
-
{ - _model.value = ModelEnum.TABLE - }}> - -
+ {/* 顶部右边插槽 */} + {slots.rightExtraRender && slots.rightExtraRender()} + { + !props.model &&
+
{ + _model.value = ModelEnum.CARD + }}> + +
+
{ + _model.value = ModelEnum.TABLE + }}> + +
+
+ }
{/* content */} diff --git a/src/main.ts b/src/main.ts index c69804cf..8452514e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,17 +1,14 @@ import { createApp } from 'vue' import App from './App.vue' import store from './store' -import router from './router' import components from './components' +import router from './router' import './style.less' import 'ant-design-vue/es/notification/style/css'; -import Antd from 'ant-design-vue/es' const app = createApp(App) app.use(store) -app.use(router) -app.use(components) -app.use(Antd) - -app.mount('#app') + .use(router) + .use(components) + .mount('#app') diff --git a/src/router/index.ts b/src/router/index.ts index 91ae60a0..abe29a44 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -3,6 +3,8 @@ import menus, { LoginPath } from './menu' import { cleanToken, getToken } from '@/utils/comm' import { useUserInfo } from '@/store/userInfo' import { useSystem } from '@/store/system' +import NotFindPage from '@/views/404.vue' +import { useMenuStore } from 'store/menu' const router = createRouter({ history: createWebHashHistory(), @@ -27,22 +29,23 @@ router.beforeEach((to, from, next) => { } else { const userInfo = useUserInfo() const system = useSystem() + const menu = useMenuStore() - if (!userInfo.userInfos.username) { + if (!menu.menuData.length) { userInfo.getUserInfo().then(() => { system.getSystemVersion().then((menuData: any[]) => { menuData.forEach(r => { - router.addRoute('main', r) + router.addRoute('base', r) }) - const redirect = decodeURIComponent((from.query.redirect as string) || to.path) - if(to.path === redirect) { - next({ ...to, replace: true }) - } else { - next({ path: redirect }) - } + router.addRoute('base',{ + path: '/:pathMatch(.*)', + name: 'error', + component: () => NotFindPage + }) + + next({ ...to, replace: true }) }) }).catch(() => { - console.log('userInfo', userInfo) cleanToken() next({ path: LoginPath }) }) diff --git a/src/router/menu.ts b/src/router/menu.ts index 4f044c8a..2a043f4f 100644 --- a/src/router/menu.ts +++ b/src/router/menu.ts @@ -1,24 +1,7 @@ export const LoginPath = '/login' export default [ - // { - // path: '/', - // redirect: LoginPath - // }, - // { - // path: '/init', - // component: () => import('@/view/InitPage.vue') - // }, - // { - // path: LoginPath, - // name: 'login', - // component: () => import('@/view/Login/index.vue') - // }, - // { - // path: '/initsetting', - // component: () => import('@/view/Login/initSet.vue') - // } - + { path: '/*', redirect: '/'}, // start: 测试用, 可删除 { path: '/login', @@ -32,10 +15,6 @@ export default [ path: '/account/center/bind', component: () => import('@/views/account/Center/bind/index.vue') }, - { - path: '/iot/home', - component: () => import('@/views/home/index.vue') - }, { path: '/table', component: () => import('@/views/demo/table/index.vue') @@ -48,129 +27,13 @@ export default [ path: '/search', component: () => import('@/views/demo/Search.vue') }, - { - path: '/notice/Config', - component: () => import('@/views/notice/Config/index.vue') - }, - { - path: '/notice/Config/detail/:id', - component: () => import('@/views/notice/Config/Detail/index.vue') - }, - { - path: '/notice/Template', - component: () => import('@/views/notice/Template/index.vue') - }, - { - path: '/notice/Template/detail/:id', - component: () => import('@/views/notice/Template/Detail/index.vue') - }, + // end: 测试用, 可删除 - // 设备管理 - { - path: '/device/instance', - component: () => import('@/views/device/Instance/index.vue') - }, - { - // path: '/device/Instance/detail/:id', - // component: () => import('@/views/device/Instance/detail.vue') - path: '/device/instance/detail/:id', - component: () => import('@/views/device/Instance/Detail/index.vue') - }, - // link 运维管理 - { - path: '/link/log', - component: () => import('@/views/link/Log/index.vue') - }, - { - path: '/link/certificate', - component: () => import('@/views/link/Certificate/index.vue') - }, - { - path: '/link/certificate/detail/:type/:id', - component: () => import('@/views/link/Certificate/Detail/index.vue') - }, - { - path: '/link/accessConfig', - component: () => import('@/views/link/AccessConfig/index.vue') - }, - { - path: '/link/accessConfig/detail/add', - component: () => import('@/views/link/AccessConfig/Detail/index.vue') - }, - // system 系统管理 - { - path:'/system/Basis', - component: ()=>import('@/views/system/Basis/index.vue') - }, - { - path:'/system/api', - component: ()=>import('@/views/system/apiPage/index.vue') - }, - { - path:'/system/Role', - component: ()=>import('@/views/system/Role/index.vue') - }, - { - path:'/system/Role/detail/:id', - component: ()=>import('@/views/system/Role/Detail/index.vue') - }, - { - path:'/system/Permission', - component: ()=>import('@/views/system/Permission/index.vue') - }, - { - path:'/system/Menu', - component: ()=>import('@/views/system/Menu/index.vue') - }, - { - path:'/system/Menu/detail/:id', - component: ()=>import('@/views/system/Menu/Detail/index.vue') - }, // 初始化 { path: '/init-home', component: () => import('@/views/init-home/index.vue') }, - // 物联卡 iot-card - { - path: '/iot-card/Home', - component: () => import('@/views/iot-card/Home/index.vue') - }, - { - path: '/iot-card/Dashboard', - component: () => import('@/views/iot-card/Dashboard/index.vue') - }, - { - path: '/iot-card/CardManagement', - component: () => import('@/views/iot-card/CardManagement/index.vue') - }, - { - path: '/iot-card/CardManagement/Detail', - component: () => import('@/views/iot-card/CardManagement/Detail/index.vue') - }, - { - path: '/iot-card/Recharge', - component: () => import('@/views/iot-card/Recharge/index.vue') - }, - // 北向输出 - { - path: '/northbound/DuerOS', - component: () => import('@/views/northbound/DuerOS/index.vue') - }, - { - path: '/northbound/AliCloud', - component: () => import('@/views/northbound/AliCloud/index.vue') - }, - // 产品分类 - { - path: '/iot/device/Category', - component: () => import('@/views/device/Category/index.vue') - } , - // 产品 - { - path: '/iot/device/Product', - component: () => import('@/views/device/Product/index.vue') - } ] \ No newline at end of file diff --git a/src/store/menu.ts b/src/store/menu.ts index 42f1946d..7d1a675e 100644 --- a/src/store/menu.ts +++ b/src/store/menu.ts @@ -1,11 +1,14 @@ import { defineStore } from "pinia"; import { queryOwnThree } from '@/api/system/menu' import { filterAsnycRouter } from '@/utils/menu' +import { cloneDeep } from 'lodash-es' export const useMenuStore = defineStore({ id: 'menu', state: () => ({ menus: {}, + menuData: [], + siderMenus: [], menusKey: [] }), getters: { @@ -51,7 +54,8 @@ export const useMenuStore = defineStore({ ]; const resp = await queryOwnThree({ paging: false, terms: params }) if (resp.success) { - const menus = filterAsnycRouter(resp.result) + const silderMenus = filterAsnycRouter(cloneDeep(resp.result)) + const menus = filterAsnycRouter(cloneDeep(resp.result)) menus.push({ path: '/', redirect: menus[0]?.path, @@ -59,7 +63,9 @@ export const useMenuStore = defineStore({ hideInMenu: true } }) - this.menus = menus + this.menuData = menus + this.siderMenus = silderMenus + console.log('silderMenus', silderMenus) res(menus) } }) diff --git a/src/utils/menu.ts b/src/utils/menu.ts index 22230e84..228ce8be 100644 --- a/src/utils/menu.ts +++ b/src/utils/menu.ts @@ -1,11 +1,99 @@ -const pagesComponent = import.meta.glob('../views/system/**/*.vue', { eager: true }); +const pagesComponent = import.meta.glob('../views/**/*.vue', { eager: true }); import { BlankLayoutPage, BasicLayoutPage } from 'components/Layout' -type ExtraRouteItem = { - code: string - name: string - url?: string -} +/** + * 权限信息 + */ +export type PermissionInfo = { + permission: string; + actions: string[]; +}; + +/** + * 按钮信息 + */ +export type MenuButtonInfo = { + id: string; + name: string; + permissions: PermissionInfo; + createTime: number; + describe?: string; + options: Record; +}; + + +export type MenuItem = { + id: string; + /** + * 名称 + */ + name: string; + /** + * 编码 + */ + code: string; + /** + * 所属应用 + */ + application: string; + /** + * 描述 + */ + describe: string; + /** + * url,路由 + */ + url: string; + /** + * 图标 + */ + icon: string; + /** + * 状态, 0为禁用,1为启用 + */ + status: number; + /** + * 绑定权限信息 + */ + permissions: PermissionInfo[]; + /** + * 按钮定义信息 + */ + buttons: MenuButtonInfo[]; + /** + * 其他配置信息 + */ + options: Record; + /** + * 父级ID + */ + parentId: string; + /** + * 树结构路径 + */ + path: string; + /** + * 排序序号 + */ + sortIndex: number; + /** + * 树层级 + */ + level: number; + createTime: number; + redirect?: string; + children?: MenuItem[]; + accessSupport?: { text: string; value: string }; + appId?: string; //应用id + isShow?: boolean; + meta?: { + title?: string + icon?: string + [key: string]: any + }, + component?: any +}; + // 额外子级路由 const extraRouteObj = { 'media/Cascade': { @@ -59,53 +147,83 @@ const extraRouteObj = { const resolveComponent = (name: any) => { - // TODO 暂时用system进行测试 const importPage = pagesComponent[`../views/${name}/index.vue`]; - // if (!importPage) { - // throw new Error(`Unknown page ${name}. Is it located under Pages with a .vue extension?`); - // } - + if (!importPage) { + console.warn(`Unknown page ${name}. Is it located under Pages with a .vue extension?`) + } //@ts-ignore - return !importPage ? BlankLayoutPage : importPage.default - // return importPage.default + return !!importPage ? importPage.default : undefined } -const findChildrenRoute = (code: string, url: string): ExtraRouteItem[] => { +const findChildrenRoute = (code: string, url: string): MenuItem[] => { if (extraRouteObj[code]) { - return extraRouteObj[code].children.map((route: ExtraRouteItem) => { + return extraRouteObj[code].children.map((route: MenuItem) => { return { url: `${url}/${route.code}`, code: route.code, - name: route.name + name: route.name, + isShow: false } }) } return [] } +const findDetailRouteItem = (code: string, url: string): Partial | null => { + const detailComponent = resolveComponent(`${code}/Detail`) + if (detailComponent) { + return { + url: `${url}/Detail/:id`, + component: detailComponent, + name: '详情信息', + isShow: false + } + } + return null +} + +const findDetailRoutes = (routes: any[]): any[] => { + const newRoutes: any[] = [] + routes.forEach((route: any) => { + newRoutes.push(route) + const detail = findDetailRouteItem(route.code, route.url) + if (detail) { + newRoutes.push(detail) + } + }) + return newRoutes +} + export function filterAsnycRouter(asyncRouterMap: any, parentCode = '', level = 1) { + return asyncRouterMap.map((route: any) => { route.path = `${route.url}` route.meta = { icon: route.icon, - title: route.name + title: route.name, + hideInMenu: route.isShow === false } // 查看是否有隐藏子路由 - route.children = route.children && route.children.length ? [...route.children, ...findChildrenRoute(route.code, route.url)] : findChildrenRoute(route.code, route.url) - - // TODO 查看是否具有详情页 - // route.children = [...route.children, ] - + const extraChildren = findChildrenRoute(route.code, route.url) + route.children = route.children && route.children.length ? [...route.children, ...extraChildren] : extraChildren + route.children = findDetailRoutes(route.children) if (route.children && route.children.length) { - route.component = () => level === 1 ? BasicLayoutPage : BlankLayoutPage + // TODO 查看是否具有详情页 route.children = filterAsnycRouter(route.children, `${parentCode}/${route.code}`, level + 1) - route.redirect = route.children[0].url + const showChildren = route.children.some((r: any) => !r.meta.hideInMenu) + if (showChildren) { + route.component = () => level === 1 ? BasicLayoutPage : BlankLayoutPage + route.redirect = route.children[0].url + } else { + route.component = resolveComponent(route.code) || BlankLayoutPage; + } } else { - route.component = resolveComponent(route.code); + console.log(route.code) + route.component = route.component || resolveComponent(route.code) || BlankLayoutPage; } - console.log(route.code, route) + delete route.name return route }) } \ No newline at end of file diff --git a/src/views/404.vue b/src/views/404.vue new file mode 100644 index 00000000..7794d959 --- /dev/null +++ b/src/views/404.vue @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/src/views/device/Instance/index.vue b/src/views/device/Instance/index.vue index 52fd4ef4..4792f526 100644 --- a/src/views/device/Instance/index.vue +++ b/src/views/device/Instance/index.vue @@ -1,221 +1,234 @@