修改了一些内容
This commit is contained in:
parent
12d3276679
commit
806b72542f
|
@ -114,6 +114,7 @@
|
|||
"Ref": true,
|
||||
"VNode": true,
|
||||
"WritableComputedRef": true,
|
||||
"toValue": true
|
||||
"toValue": true,
|
||||
"filterRouters": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"js-error-collection": "^1.0.8",
|
||||
"mitt": "3.0.1",
|
||||
"moment-mini": "2.29.4",
|
||||
"mqtt": "^5.5.0",
|
||||
"nprogress": "0.2.0",
|
||||
"only-allow": "^1.2.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
|
|
|
@ -19,6 +19,7 @@ export interface IUserRole {
|
|||
roleId: number
|
||||
roleName: string
|
||||
roleType: string
|
||||
type: string
|
||||
}
|
||||
|
||||
export async function getMyInfo(): Promise<IUser> {
|
||||
|
|
|
@ -1,22 +1,36 @@
|
|||
import NProgress from 'nprogress'
|
||||
import type { RouteRecordName } from 'vue-router'
|
||||
import Layout from '@/layout/default/index.vue'
|
||||
import router, { asyncRoutes, constantRoutes } from '@/router'
|
||||
import 'nprogress/nprogress.css'
|
||||
import { useBasicStore } from '@/store/basic'
|
||||
import NProgress from 'nprogress'
|
||||
import 'nprogress/nprogress.css'
|
||||
import type { RouteRecordName } from 'vue-router'
|
||||
import { isArray } from 'xe-utils'
|
||||
import type { RouterTypes } from '~/basic'
|
||||
|
||||
export function filterRouters<
|
||||
T extends {
|
||||
meta?: { roles?: string[] }
|
||||
children?: T[]
|
||||
}
|
||||
>(menuList: T[], role: string) {
|
||||
const out = menuList.filter((it) => it.meta?.roles?.includes(role) ?? true)
|
||||
for (const t of out) {
|
||||
if (t.children) filterRouters(t.children, role)
|
||||
}
|
||||
menuList.length = 0
|
||||
menuList.push(...out)
|
||||
}
|
||||
|
||||
//从数据库设置文本路由
|
||||
export function setRouterFromDatabase(menuList) {
|
||||
const basicStore = useBasicStore()
|
||||
const accessRoutes = menuList
|
||||
const accessRoutes: RouterTypes = menuList
|
||||
|
||||
// @ts-ignore
|
||||
const views = import.meta.glob('../views/**/*.vue') as Record<string, () => Promise<Component>>
|
||||
|
||||
function scanRouter(items: any[]) {
|
||||
for (const item of items) {
|
||||
if (item.component === 'Layout') item.component = async () => Layout
|
||||
if (item.component === 'Layout') item.component = () => import('@/layout/default/index.vue')
|
||||
else if (typeof item.component === 'string') {
|
||||
let url: string = item.component
|
||||
if (url.startsWith('@/')) {
|
||||
|
@ -39,6 +53,7 @@ export function setRouterFromDatabase(menuList) {
|
|||
}
|
||||
|
||||
scanRouter(accessRoutes)
|
||||
|
||||
accessRoutes.forEach((route) => router.addRoute(route))
|
||||
asyncRoutes.forEach((item) => router.addRoute(item))
|
||||
basicStore.setFilterAsyncRoutes(accessRoutes)
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import router from '@/router'
|
||||
import { progressClose, progressStart, setRouterFromDatabase } from '@/hooks/use-permission'
|
||||
import router, { roleRoutes } from '@/router'
|
||||
import { filterRouters, progressClose, progressStart, setRouterFromDatabase } from '@/hooks/use-permission'
|
||||
import { useBasicStore } from '@/store/basic'
|
||||
import { getMyInfo, getMyRole } from '@/api/user'
|
||||
import { langTitle } from '@/hooks/use-common'
|
||||
import { getRouterList } from '@/api/router'
|
||||
|
||||
//路由进入前拦截
|
||||
//to:将要进入的页面 vue-router4.0 不推荐使用next()
|
||||
const whiteList = ['/login', '/404', '/401', '/500'] // no redirect whitelist
|
||||
const whiteList = ['/mqtt', '/login', '/404', '/401', '/500'] // no redirect whitelist
|
||||
router.beforeEach(async (to) => {
|
||||
progressStart()
|
||||
document.title = langTitle(to.meta?.title) // i18 page title
|
||||
|
@ -15,34 +14,35 @@ router.beforeEach(async (to) => {
|
|||
if (to.path === '/500') {
|
||||
return true
|
||||
}
|
||||
if (to.path === '/login') {
|
||||
return true
|
||||
}
|
||||
//1.判断token
|
||||
if (basicStore.token) {
|
||||
if (to.path === '/login') {
|
||||
return '/'
|
||||
} else {
|
||||
//2.判断是否获取用户信息
|
||||
if (!basicStore.getUserInfo) {
|
||||
try {
|
||||
const [userData, userRole] = await Promise.all([getMyInfo(), getMyRole()])
|
||||
const routes = await getRouterList({ roleId: userRole.roleId })
|
||||
//3.动态路由权限筛选
|
||||
setRouterFromDatabase(routes)
|
||||
//4.保存用户信息到store
|
||||
basicStore.setUserInfo({
|
||||
userInfo: userData,
|
||||
roles: [userRole.roleType],
|
||||
codes: [userRole.roleId]
|
||||
})
|
||||
//5.再次执行路由跳转
|
||||
return { ...to, replace: true }
|
||||
} catch (e) {
|
||||
console.error(`route permission error${e}`)
|
||||
progressClose()
|
||||
return `/500?redirect=${to.path}`
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
//2.判断是否获取用户信息
|
||||
if (!basicStore.getUserInfo) {
|
||||
try {
|
||||
const [userData, userRole] = await Promise.all([getMyInfo(), getMyRole()])
|
||||
//3.保存用户信息到store
|
||||
basicStore.setUserInfo({
|
||||
userInfo: userData,
|
||||
roles: [userRole.roleType],
|
||||
codes: [userRole.roleId]
|
||||
})
|
||||
//const routes = await getRouterList({ roleId: userRole.roleId })
|
||||
const routes = roleRoutes
|
||||
filterRouters(routes as any, userRole.type)
|
||||
//4.动态路由权限筛选
|
||||
setRouterFromDatabase(routes)
|
||||
//5.再次执行路由跳转
|
||||
return { ...to, replace: true }
|
||||
} catch (e) {
|
||||
console.error(`route permission error${e}`)
|
||||
progressClose()
|
||||
return `/500?redirect=${to.path}`
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if (!whiteList.includes(to.path)) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import type { RouterTypes } from '~/basic'
|
||||
import type { BootstrapIcons, RouterTypes } from '~/basic'
|
||||
import settings from '@/settings'
|
||||
import { userRoute } from '@/router/modules/user'
|
||||
const Layout = () => import('@/layout/default/index.vue')
|
||||
|
@ -39,11 +39,18 @@ export const constantRoutes: RouterTypes = [
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/mqtt',
|
||||
name: 'MQTT',
|
||||
component: () => import('@/views/mqtt/index.vue'),
|
||||
//using el svg icon, the elSvgIcon first when at the same time using elSvgIcon and icon
|
||||
meta: { title: 'MQTT', icon: 'list' as BootstrapIcons, affix: true }
|
||||
},
|
||||
userRoute
|
||||
]
|
||||
|
||||
//角色和code数组动态路由
|
||||
export const roleCodeRoutes: RouterTypes = []
|
||||
export const roleRoutes: RouterTypes = []
|
||||
/**
|
||||
* asyncRoutes
|
||||
* the routes that need to be dynamically loaded based on user roles
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
import Layout from '@/layout/default/index.vue'
|
||||
const BasicDemo = {
|
||||
path: '/basic-demo',
|
||||
component: Layout,
|
||||
meta: { title: 'Basic Demo', icon: 'eye-open' },
|
||||
alwaysShow: true,
|
||||
children: [
|
||||
{
|
||||
path: 'hook',
|
||||
component: () => import('@/views/basic-demo/hook/index.vue'),
|
||||
name: 'Hook',
|
||||
meta: { title: 'Hook' }
|
||||
},
|
||||
{
|
||||
path: 'pinia',
|
||||
component: () => import('@/views/basic-demo/pinia/index.vue'),
|
||||
name: 'Pinia',
|
||||
meta: { title: 'Pinia' }
|
||||
},
|
||||
{
|
||||
path: 'mock',
|
||||
component: () => import('@/views/basic-demo/mock/index.vue'),
|
||||
name: 'Mock',
|
||||
meta: { title: 'Mock' }
|
||||
},
|
||||
{
|
||||
path: 'svg-icon',
|
||||
component: () => import('@/views/basic-demo/svg-icon/index.vue'),
|
||||
name: 'SvgIcon',
|
||||
meta: { title: 'Svg Icon' }
|
||||
},
|
||||
{
|
||||
path: 'parent-children',
|
||||
component: () => import('@/views/basic-demo/parent-children/index.vue'),
|
||||
name: 'Parent',
|
||||
meta: { title: 'Parent Children' }
|
||||
},
|
||||
{
|
||||
path: 'second-keep-alive',
|
||||
component: () => import('@/views/basic-demo/keep-alive/index.vue'),
|
||||
name: 'SecondKeepAlive',
|
||||
//cachePage: cachePage when page enter, default false
|
||||
//leaveRmCachePage: remove cachePage when page leave, default false
|
||||
meta: { title: 'Second KeepAlive', cachePage: true, leaveRmCachePage: false }
|
||||
},
|
||||
{
|
||||
path: 'second-child',
|
||||
name: 'SecondChild',
|
||||
hidden: true,
|
||||
component: () => import('@/views/basic-demo/keep-alive/second-child.vue'),
|
||||
meta: { title: 'SecondChild', cachePage: true, activeMenu: '/basic-demo/second-keep-alive' }
|
||||
},
|
||||
{
|
||||
path: 'third-child',
|
||||
name: 'ThirdChild',
|
||||
hidden: true,
|
||||
component: () => import('@/views/basic-demo/keep-alive/third-child.vue'),
|
||||
meta: { title: 'ThirdChild', cachePage: true, activeMenu: '/basic-demo/second-keep-alive' }
|
||||
},
|
||||
//tab-keep-alive
|
||||
{
|
||||
path: 'tab-keep-alive',
|
||||
component: () => import('@/views/basic-demo/keep-alive/tab-keep-alive.vue'),
|
||||
name: 'TabKeepAlive',
|
||||
//closeTabRmCache: remove cachePage when tabs close, default false
|
||||
meta: { title: 'Tab KeepAlive', cachePage: true, closeTabRmCache: true }
|
||||
},
|
||||
//third-keep-alive
|
||||
{
|
||||
path: 'third-keep-alive',
|
||||
name: 'ThirdKeepAlive',
|
||||
component: () => import('@/views/basic-demo/keep-alive/third-keep-alive.vue'),
|
||||
//注:移除父容器页面缓存会把子页面一起移除了
|
||||
meta: { title: 'Third KeepAlive', cachePage: true, leaveRmCachePage: false },
|
||||
alwaysShow: true,
|
||||
children: [
|
||||
{
|
||||
path: 'second-children',
|
||||
name: 'SecondChildren',
|
||||
component: () => import('@/views/basic-demo/keep-alive/third-children/SecondChildren.vue'),
|
||||
meta: { title: 'SecondChild', cachePage: true, leaveRmCachePage: true }
|
||||
},
|
||||
{
|
||||
path: 'third-children',
|
||||
name: 'ThirdChildren',
|
||||
component: () => import('@/views/basic-demo/keep-alive/third-children/ThirdChildren.vue'),
|
||||
meta: { title: 'ThirdChild', cachePage: true, leaveRmCachePage: false }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'worker',
|
||||
component: () => import('@/views/basic-demo/worker/index.vue'),
|
||||
name: 'Worker',
|
||||
meta: { title: 'Worker' }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default BasicDemo
|
|
@ -1,42 +0,0 @@
|
|||
/** When your routing table is too long, you can split it into small modules**/
|
||||
|
||||
import Layout from '@/layout/default/index.vue'
|
||||
|
||||
const chartsRouter = {
|
||||
path: '/charts',
|
||||
component: Layout,
|
||||
redirect: 'noRedirect',
|
||||
name: 'Charts',
|
||||
meta: {
|
||||
title: 'Charts',
|
||||
icon: 'chart'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'keyboard',
|
||||
component: () => import('@/views/charts/keyboard.vue'),
|
||||
name: 'KeyboardChart',
|
||||
meta: { title: 'Keyboard Chart', noCache: true }
|
||||
},
|
||||
{
|
||||
path: 'line',
|
||||
component: () => import('@/views/charts/line.vue'),
|
||||
name: 'LineChart',
|
||||
meta: { title: 'Line Chart', noCache: true }
|
||||
},
|
||||
{
|
||||
path: 'mix-chart',
|
||||
component: () => import('@/views/charts/mix-chart.vue'),
|
||||
name: 'MixChart',
|
||||
meta: { title: 'Mix Chart', noCache: true }
|
||||
},
|
||||
{
|
||||
path: 'echarts-demo',
|
||||
component: () => import('@/views/charts/echarts-demo.vue'),
|
||||
name: 'EchartsDemo',
|
||||
meta: { title: 'Echarts Demo', noCache: true }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default chartsRouter
|
|
@ -1,48 +0,0 @@
|
|||
import Layout from '@/layout/default/index.vue'
|
||||
|
||||
const directive = {
|
||||
path: '/directive',
|
||||
component: Layout,
|
||||
meta: { title: 'Directive', icon: 'education' },
|
||||
alwaysShow: true,
|
||||
children: [
|
||||
{
|
||||
path: 'copy',
|
||||
component: () => import('@/views/directive/copy.vue'),
|
||||
name: 'copy',
|
||||
meta: { title: 'v-copy' }
|
||||
},
|
||||
{
|
||||
path: 'debounce',
|
||||
component: () => import('@/views/directive/debounce.vue'),
|
||||
name: 'debounce',
|
||||
meta: { title: 'v-debounce' }
|
||||
},
|
||||
{
|
||||
path: 'longpress',
|
||||
component: () => import('@/views/directive/longpress.vue'),
|
||||
name: 'longpress',
|
||||
meta: { title: 'v-longpress' }
|
||||
},
|
||||
{
|
||||
path: 'watermark',
|
||||
component: () => import('@/views/directive/watermark.vue'),
|
||||
name: 'watermark',
|
||||
meta: { title: 'v-watermark' }
|
||||
},
|
||||
{
|
||||
path: 'waves',
|
||||
component: () => import('@/views/directive/waves.vue'),
|
||||
name: 'waves',
|
||||
meta: { title: 'v-waves' }
|
||||
},
|
||||
{
|
||||
path: 'clickoutside',
|
||||
component: () => import('@/views/directive/clickoutside.vue'),
|
||||
name: 'clickoutside',
|
||||
meta: { title: 'v-clickoutside' }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default directive
|
|
@ -1,23 +0,0 @@
|
|||
import Layout from '@/layout/default/index.vue'
|
||||
const excel = {
|
||||
path: '/excel',
|
||||
component: Layout,
|
||||
meta: { title: 'EXCEL', icon: 'excel' },
|
||||
alwaysShow: true,
|
||||
children: [
|
||||
{
|
||||
path: 'exportExcel',
|
||||
component: () => import('@/views/excel/exportExcel.vue'),
|
||||
name: 'exportExcel',
|
||||
meta: { title: 'exportExcel' }
|
||||
},
|
||||
{
|
||||
path: 'importExcel',
|
||||
component: () => import('@/views/excel/importExcel.vue'),
|
||||
name: 'importExcel',
|
||||
meta: { title: 'importExcel' }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default excel
|
|
@ -1,14 +0,0 @@
|
|||
import Layout from '@/layout/default/index.vue'
|
||||
const guid = {
|
||||
path: '/guide',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/guide/index.vue'),
|
||||
name: 'Guide',
|
||||
meta: { title: 'Guide', icon: 'guide' }
|
||||
}
|
||||
]
|
||||
}
|
||||
export default guid
|
|
@ -1,35 +0,0 @@
|
|||
import Layout from '@/layout/default/index.vue'
|
||||
const other = {
|
||||
path: '/other',
|
||||
component: Layout,
|
||||
meta: { title: 'Other', icon: 'eye-open' },
|
||||
alwaysShow: true,
|
||||
children: [
|
||||
{
|
||||
path: 'count-to',
|
||||
component: () => import('@/views/other/count-to.vue'),
|
||||
name: 'CountTo',
|
||||
meta: { title: 'CountTo' }
|
||||
},
|
||||
{
|
||||
path: 'd3',
|
||||
component: () => import('@/views/other/d3/index.vue'),
|
||||
name: 'd3',
|
||||
meta: { title: 'd3' }
|
||||
},
|
||||
{
|
||||
path: 'drag-pane',
|
||||
component: () => import('@/views/other/drag-pane.vue'),
|
||||
name: 'DragPane',
|
||||
meta: { title: 'DragPane' }
|
||||
},
|
||||
{
|
||||
path: 'signboard',
|
||||
component: () => import('@/views/other/signboard/index.vue'),
|
||||
name: 'signboard',
|
||||
meta: { title: 'signboard' }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default other
|
|
@ -1,17 +0,0 @@
|
|||
import Layout from '@/layout/default/index.vue'
|
||||
const richText = {
|
||||
path: '/rich-text',
|
||||
component: Layout,
|
||||
meta: { title: 'Rich Text', icon: 'clipboard' },
|
||||
alwaysShow: true,
|
||||
children: [
|
||||
{
|
||||
path: 'tinymce',
|
||||
name: 'Tinymce',
|
||||
component: () => import('@/views/rich-text/TinymceExample.vue'),
|
||||
meta: { title: 'Tinymce', icon: 'nested' }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default richText
|
|
@ -1,23 +0,0 @@
|
|||
import Layout from '@/layout/default/index.vue'
|
||||
const table = {
|
||||
path: '/table',
|
||||
component: Layout,
|
||||
meta: { title: 'Table', icon: 'table' },
|
||||
alwaysShow: true,
|
||||
children: [
|
||||
{
|
||||
path: 'dynamic-table',
|
||||
name: 'DynamicTable',
|
||||
component: () => import('@/views/table/dynamic-table.vue'),
|
||||
meta: { title: 'Dynamic Table', icon: 'nested' }
|
||||
},
|
||||
{
|
||||
path: 'vxe-table',
|
||||
name: 'VxeTable',
|
||||
component: () => import('@/views/table/vxe-table.vue'),
|
||||
meta: { title: 'Vxe Table', icon: 'nested' }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default table
|
99
src/views/mqtt/index.vue
Normal file
99
src/views/mqtt/index.vue
Normal file
|
@ -0,0 +1,99 @@
|
|||
<script setup lang="ts">
|
||||
import type { MqttClient } from 'mqtt'
|
||||
import mqtt from 'mqtt'
|
||||
import { delay } from '@/utils/common-util'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
let mqttClient: MqttClient | null = $ref()
|
||||
const jobCancel: Ref<(() => void) | null> = ref() as any
|
||||
|
||||
const topic = $ref('V2X/RSU/R328328/RSM/UP/DAWNLINE')
|
||||
const url = $ref('mqtt://localhost:15675/mqtt')
|
||||
const username = $ref('root')
|
||||
const password = $ref('123456')
|
||||
const clientId = $ref('R328328')
|
||||
const file: File = $ref()
|
||||
let status = $ref(false)
|
||||
|
||||
function connect() {
|
||||
mqttClient = mqtt.connect(url, {
|
||||
username,
|
||||
password,
|
||||
clientId
|
||||
})
|
||||
mqttClient.on('connect', (it) => {
|
||||
status = true
|
||||
})
|
||||
|
||||
mqttClient.on('error', (it) => {
|
||||
status = false
|
||||
console.error(it)
|
||||
})
|
||||
}
|
||||
|
||||
async function disconnect() {
|
||||
jobCancel.value?.()
|
||||
await mqttClient?.endAsync()
|
||||
mqttClient = null
|
||||
status = false
|
||||
}
|
||||
|
||||
async function publish() {
|
||||
const text = await file.text()
|
||||
const list = JSON.parse(text) as any[]
|
||||
let i = 0
|
||||
const next = () => list[(i = i++ % list.length)]
|
||||
;(async () => {
|
||||
let exit = false
|
||||
jobCancel.value = () => {
|
||||
exit = true
|
||||
jobCancel.value = null
|
||||
}
|
||||
while (!exit) {
|
||||
const item = next()
|
||||
const json = JSON.stringify(item)
|
||||
mqttClient!.publish(topic, json, {})
|
||||
|
||||
await delay(100)
|
||||
}
|
||||
})().catch((e) => console.error(e))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-container>
|
||||
<el-main style="max-width: 1024px; margin: 0 auto">
|
||||
<el-form label-width="auto">
|
||||
<el-form-item label="地址">
|
||||
<el-input v-model="url" />
|
||||
</el-form-item>
|
||||
<el-form-item label="TOPIC">
|
||||
<el-input v-model="topic" />
|
||||
</el-form-item>
|
||||
<el-form-item label="ClientID">
|
||||
<el-input v-model="clientId" />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名">
|
||||
<el-input v-model="username" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<el-input v-model="password" />
|
||||
</el-form-item>
|
||||
<el-form-item label="文件">
|
||||
<input type="file" accept="application/json" @change="file = $event.target?.['files']?.[0]" />
|
||||
</el-form-item>
|
||||
<el-form-item label="过滤器">
|
||||
<el-table />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button v-if="!status" type="primary" @click="connect">连接</el-button>
|
||||
<el-button v-else type="danger" @click="disconnect">断开</el-button>
|
||||
<el-button v-if="!jobCancel" type="primary" :disabled="!status" @click="publish">发送</el-button>
|
||||
<el-button v-else type="danger" :disabled="!status" @click="() => jobCancel?.()">停止</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
1
typings/auto-imports.d.ts
vendored
1
typings/auto-imports.d.ts
vendored
|
@ -27,6 +27,7 @@ declare global {
|
|||
const elLoading: typeof import('../src/hooks/use-element')['elLoading']
|
||||
const elMessage: typeof import('../src/hooks/use-element')['elMessage']
|
||||
const elNotify: typeof import('../src/hooks/use-element')['elNotify']
|
||||
const filterRouters: typeof import('../src/hooks/use-permission')['filterRouters']
|
||||
const freshRouter: typeof import('../src/hooks/use-permission')['freshRouter']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
|
|
Loading…
Reference in New Issue
Block a user