Browse Source

fix: 路由配置问题

master
guoapeng 2 weeks ago
parent
commit
4a4c05ff37
  1. 2
      package.json
  2. 284
      src/components/common/FTKeyboard/index.vue
  3. 35
      src/layouts/default.vue
  4. 4
      src/router/index.ts
  5. 220
      src/router/routes.ts
  6. 2
      src/views/home/index.vue
  7. 79
      src/views/login/index.vue

2
package.json

@ -62,6 +62,8 @@
"postcss-url": "^10.1.3",
"postcss-viewport-units": "^0.1.6",
"postcss-write-svg": "^3.0.1",
"simple-keyboard": "^3.8.64",
"simple-keyboard-layouts": "^3.4.102",
"vue": "^3.5.13",
"vue-chart-3": "^3.1.8",
"vue-i18n": "^11.1.6",

284
src/components/common/FTKeyboard/index.vue

@ -0,0 +1,284 @@
<script setup lang="ts">
import 'simple-keyboard/build/css/index.css'
import Keyboard from 'simple-keyboard'
import layout from 'simple-keyboard-layouts/build/layouts/chinese.js'
import { onUnmounted, ref } from 'vue'
defineOptions({
inheritAttrs: false,
})
const props = defineProps({
layoutName: {
type: String,
default: 'default',
},
// layoutNamenumber
precision: {
type: Number,
default: 2,
},
//
isOpen: {
type: Boolean,
default: true,
},
})
const emits = defineEmits(['onChange', 'enter', 'close', 'focus'])
const model = defineModel<string>()
const keyboard = ref<any>(null)
const visible = ref(false)
const inputRef = ref()
const popoverRef = ref()
const entering = ref(false)
const width = ref(1000)
if (props.layoutName === 'number')
width.value = 300
const displayDefault = ref({
'{bksp}': 'backspace',
'{lock}': 'caps',
'{enter}': 'enter',
'{tab}': 'tab',
'{shift}': 'shift',
'{change}': 'en',
'{space}': 'space',
'{clear}': '清空',
'{close}': '关闭',
'{arrowleft}': '←',
'{arrowright}': '→',
})
const open = () => {
if (visible.value)
return
inputRef.value.focus()
emits('focus')
visible.value = true
}
const focusInput = () => {
if (visible.value)
return
emits('focus')
if (props.isOpen)
visible.value = true
}
// const blurInput = debounce(() => {
// if (!entering.value) {
// handleClose()
// } else {
// entering.value = false
// }
// }, 100)
const blurInput = () => {
setTimeout(() => {
if (!entering.value) {
handleClose()
}
else {
entering.value = false
}
}, 300)
}
const afterEnter = () => {
//
const prevKeyboard = document.querySelectorAll('.init-keyboard')
if (prevKeyboard.length > 0)
prevKeyboard[0]?.remove()
keyboard.value = new Keyboard('simple-keyboard', {
onChange,
onKeyPress,
onInit,
layout: {
//
default: [
'` 1 2 3 4 5 6 7 8 9 0 - = {bksp}',
'{tab} q w e r t y u i o p [ ] \\',
'{lock} a s d f g h j k l ; \' {enter}',
'{change} z x c v b n m , . / {clear}',
'{arrowleft} {arrowright} {space} {close}',
],
//
shift: [
'~ ! @ # $ % ^ & * ( ) _ + {bksp}',
'{tab} Q W E R T Y U I O P { } |',
'{lock} A S D F G H J K L : " {enter}',
'{change} Z X C V B N M < > ? {clear}',
'{arrowleft} {arrowright} {space} {close}',
],
//
number: ['7 8 9', '4 5 6', '1 2 3', '. 0 {bksp}', '{arrowleft} {arrowright} {clear} {close}'],
},
layoutName: props.layoutName,
display: displayDefault.value,
theme: 'hg-theme-default init-keyboard', // class
})
}
const beforeLeave = () => {
visible.value = false
entering.value = false
inputRef.value.blur()
displayDefault.value['{change}'] = 'en'
document.removeEventListener('click', handlePopClose)
}
const onInit = (keyboard: any) => {
keyboard.setInput(model.value)
keyboard.setCaretPosition(inputRef.value?.ref.selectionEnd)
document.addEventListener('click', handlePopClose)
}
const onChange = (input: any) => {
model.value = input
emits('onChange', input)
}
const onKeyPress = (button: any) => {
if (button === '{lock}')
return handleLock()
if (button === '{change}')
return handleChange()
if (button === '{clear}')
return handleClear()
if (button === '{enter}')
return handleEnter()
if (button === '{close}')
return handleClose()
if (button === '{arrowleft}')
return handleArrow(0)
if (button === '{arrowright}')
return handleArrow(1)
}
const handleLock = () => {
entering.value = true
const currentLayout = keyboard.value.options.layoutName
const shiftToggle = currentLayout === 'default' ? 'shift' : 'default'
keyboard.value.setOptions({
layoutName: shiftToggle,
})
}
const handleChange = () => {
entering.value = true
const layoutCandidates = keyboard.value.options.layoutCandidates
//
if (layoutCandidates !== null && layoutCandidates !== undefined) {
displayDefault.value['{change}'] = 'en'
keyboard.value.setOptions({
layoutName: 'default',
layoutCandidates: null,
display: displayDefault.value,
})
}
else {
displayDefault.value['{change}'] = 'cn'
keyboard.value.setOptions({
layoutName: 'default',
layoutCandidates: (layout as any).layoutCandidates,
display: displayDefault.value,
})
}
}
const handleClear = () => {
keyboard.value.clearInput()
model.value = ''
}
const handleEnter = () => {
emits('enter')
}
const handleClose = () => {
if (props.layoutName === 'number') {
//
model.value = model.value?.replace(new RegExp(`(\\d+)\\.(\\d{${props.precision}}).*$`), '$1.$2').replace(/\.$/, '')
}
popoverRef.value.hide()
emits('close')
}
const handleArrow = (num: number) => {
//
const index = keyboard.value.getCaretPositionEnd()
if (num === 0 && index - 1 >= 0) {
keyboard.value.setCaretPosition(index - 1)
}
else if (num === 1 && index + 1 <= (model.value?.length || 0)) {
keyboard.value.setCaretPosition(index + 1)
}
}
const handlePopClose = (e: any) => {
//
if (
(e.target as Element).closest('.keyboard-popper')
|| e.target === inputRef.value?.ref
|| /hg-candidate-box/.test(e.target.className)
) {
entering.value = true
const index = keyboard.value.getCaretPositionEnd()
inputRef.value.ref.selectionStart = inputRef.value.ref.selectionEnd = index
inputRef.value.focus()
}
}
const close = () => {
handleClose()
}
onUnmounted(() => {
//
document.removeEventListener('click', handlePopClose)
})
defineExpose({ inputRef, visible, open, close })
</script>
<template>
<el-input
ref="inputRef"
v-model="model"
v-bind="$attrs"
@focus="focusInput"
@blur="blurInput"
@keyup.enter="handleEnter"
>
<template v-for="(item, index) in $slots" :key="index" #[index]>
<slot :name="index" />
</template>
</el-input>
<el-popover
ref="popoverRef"
:visible="visible"
:virtual-ref="inputRef"
virtual-triggering
placement="bottom"
:width="width"
:show-arrow="false"
:hide-after="0"
popper-style="padding: 0px;color:#000"
:persistent="false"
popper-class="keyboard-popper"
@after-enter="afterEnter"
@before-leave="beforeLeave"
>
<div class="simple-keyboard" />
</el-popover>
</template>
<style>
.hg-theme-default .hg-button.hg-button-arrowleft,
.hg-theme-default .hg-button.hg-button-arrowright {
max-width: 70px;
}
.hg-theme-default .hg-button.hg-button-close {
max-width: 100px;
}
.hg-layout-number .hg-button.hg-button-close {
max-width: none;
}
.hg-layout-number .hg-button.hg-button-bksp {
max-width: 92px;
}
</style>

35
src/layouts/default.vue

@ -4,6 +4,7 @@ import WifiUnconnSvg from 'assets/images/wifi-unconn.svg'
import ErrorEventsModal from 'components/system/ErrorEventsModal.vue'
import NetReconnection from 'components/system/NetReconnection.vue'
import { formatDateTime, openFullscreen } from 'libs/utils'
import { authRoutes } from 'router/routes'
import { useDeviceStore } from 'stores/deviceStore'
import { computed, onMounted, onUnmounted, ref, watch, watchEffect } from 'vue'
import { useI18n } from 'vue-i18n'
@ -11,14 +12,13 @@ import { useRouter } from 'vue-router'
import { getDeviceStatus } from '@/libs/deviceComm'
import { FtMessageBox } from '@/libs/messageBox'
import { generateRoutes } from '@/router/routes'
import { useHomeStore } from '@/stores/homeStore'
import { useLiquidStore } from '@/stores/liquidStore'
import { useSealStore } from '@/stores/sealStore'
import { useSystemStore } from '@/stores/systemStore'
const routes = generateRoutes()
const childrenRoutes = routes.find(r => r.path === '/')?.children || []
// const routes = generateRoutes()
// const childrenRoutes = routes.find(r => r.path === '/')?.children || []
const { locale } = useI18n()
const router = useRouter()
const liquidStore = useLiquidStore()
@ -41,6 +41,25 @@ const websocketConnected = ref(systemStore.websocketConnected)
let touchStartTime = 0
let touchCount = 0
const userData = localStorage.getItem('user')
const userInfo = userData ? JSON.parse(userData) : {}
const userRole = userInfo.roleType || ''
const deviceType = __DEVICE_TYPE__
const menuList = computed(() => {
return authRoutes.filter((item) => {
if (
(deviceType === deviceStore.deviceTypeMap.LargeSpaceDM_B && (item.name === 'seal' || item.name === 'liquid'))
|| (deviceType === deviceStore.deviceTypeMap.LargeSpaceDM && item.name === 'seal')
|| (deviceType === deviceStore.deviceTypeMap.SmallSpaceDM && item.name === 'seal')
|| (deviceType === deviceStore.deviceTypeMap.DrawBarDM && item.name === 'liquid')
) {
return false
}
return !(item.name === 'debug' && userRole !== 'admin')
})
})
onMounted(() => {
// 3
onFullScreen()
@ -167,10 +186,6 @@ const statusMap = {
name: '设备告警信息',
},
}
const deviceType = computed(() => {
return __DEVICE_TYPE__
})
</script>
<template>
@ -183,7 +198,7 @@ const deviceType = computed(() => {
<div class="header-menu">
<div class="aside">
<el-tag
v-for="item in childrenRoutes.filter(item => item.meta!.isDefault)"
v-for="item in menuList.filter(item => item.meta!.isDefault)"
:key="item.path"
class="menu-tag"
:class="{ 'aside-item-active': router.currentRoute.value.path.includes(item.path) }"
@ -217,7 +232,7 @@ const deviceType = computed(() => {
<el-option
v-for="language in languages"
:key="language.value"
style="height: 2rem;font-size: 1rem"
style="height: 2rem; font-size: 1rem"
:value="language.value"
:label="language.name"
>
@ -296,7 +311,7 @@ const deviceType = computed(() => {
<style scoped lang="scss">
.main {
box-sizing: border-box;
height: 100%;
height: 100vh;
background: #fafafa;
.header {
height: 50px;

4
src/router/index.ts

@ -4,13 +4,13 @@ import { createRouter, createWebHashHistory } from 'vue-router'
import { getToken } from '@/libs/token'
import { generateRoutes } from './routes'
import routes from './routes'
const wsClient = createWebSocket()
const router = createRouter({
history: createWebHashHistory(),
routes: generateRoutes(),
routes,
})
router.beforeEach((to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {

220
src/router/routes.ts

@ -13,136 +13,114 @@ import s_formula from 'assets/images/menuIcon/s-formula.svg'
import s_liquid from 'assets/images/menuIcon/s-liquid.svg'
import s_seal from 'assets/images/menuIcon/s-seal.svg'
import s_setting from 'assets/images/menuIcon/s-setting.svg'
import { useDeviceStore } from 'stores/deviceStore'
import pinia from 'stores/index'
import type { RouteRecordRaw } from 'vue-router'
export function generateRoutes(): RouteRecordRaw[] {
const deviceStore = useDeviceStore(pinia)
const userData = localStorage.getItem('user')
const userInfo = userData ? JSON.parse(userData) : {}
const userRole = userInfo.roleType || ''
const deviceType = __DEVICE_TYPE__
const allRoutes: RouteRecordRaw[] = [
{
path: '/home',
name: 'home',
component: () => import('views/home/index.vue'),
meta: {
isDefault: true,
title: '消毒',
icon: n_disinfect,
activeIcon: s_disinfect,
},
children: [
{
path: 'config',
name: 'config',
component: () => import('components/home/config.vue'),
},
{
path: 'chart',
name: 'chart',
component: () => import('views/home/chart.vue'),
},
],
const authRoutes: RouteRecordRaw[] = [
{
path: '/home',
name: 'home',
component: () => import('views/home/index.vue'),
meta: {
isDefault: true,
title: '消毒',
icon: n_disinfect,
activeIcon: s_disinfect,
},
{
path: '/liquid',
name: 'liquid',
component: () => import('views/liquid/index.vue'),
meta: {
isDefault: true,
title: '消毒液',
icon: n_liquid,
activeIcon: s_liquid,
keepAlive: true,
children: [
{
path: 'config',
name: 'config',
component: () => import('components/home/config.vue'),
},
},
{
path: '/seal',
name: 'seal',
component: () => import('views/seal/index.vue'),
meta: {
isDefault: true,
title: '密封测试',
icon: n_seal,
activeIcon: s_seal,
{
path: 'chart',
name: 'chart',
component: () => import('views/home/chart.vue'),
},
],
},
{
path: '/liquid',
name: 'liquid',
component: () => import('views/liquid/index.vue'),
meta: {
isDefault: true,
title: '消毒液',
icon: n_liquid,
activeIcon: s_liquid,
keepAlive: true,
},
{
path: '/formula',
name: 'formula',
component: () => import('views/formula/index.vue'),
meta: {
isDefault: true,
title: '配方',
icon: n_formula,
activeIcon: s_formula,
},
},
{
path: '/seal',
name: 'seal',
component: () => import('views/seal/index.vue'),
meta: {
isDefault: true,
title: '密封测试',
icon: n_seal,
activeIcon: s_seal,
},
{
path: '/debug',
name: 'debug',
component: () => import('views/debug/index.vue'),
meta: {
isDefault: true,
title: '测试',
icon: n_debug,
activeIcon: s_debug,
},
},
{
path: '/formula',
name: 'formula',
component: () => import('views/formula/index.vue'),
meta: {
isDefault: true,
title: '配方',
icon: n_formula,
activeIcon: s_formula,
},
{
path: '/audit',
name: 'audit',
component: () => import('views/audit/index.vue'),
meta: {
isDefault: true,
title: '审计',
icon: n_audit,
activeIcon: s_audit,
},
},
{
path: '/debug',
name: 'debug',
component: () => import('views/debug/index.vue'),
meta: {
isDefault: true,
title: '测试',
icon: n_debug,
activeIcon: s_debug,
},
{
path: '/setting',
name: 'setting',
component: () => import('views/setting/index.vue'),
meta: {
isDefault: true,
title: '设置',
icon: n_setting,
activeIcon: s_setting,
},
},
{
path: '/audit',
name: 'audit',
component: () => import('views/audit/index.vue'),
meta: {
isDefault: true,
title: '审计',
icon: n_audit,
activeIcon: s_audit,
},
},
{
path: '/setting',
name: 'setting',
component: () => import('views/setting/index.vue'),
meta: {
isDefault: true,
title: '设置',
icon: n_setting,
activeIcon: s_setting,
},
]
},
]
const filteredRoutes = allRoutes.filter((item) => {
if (
(deviceType === deviceStore.deviceTypeMap.LargeSpaceDM_B && (item.name === 'seal' || item.name === 'liquid'))
|| (deviceType === deviceStore.deviceTypeMap.LargeSpaceDM && item.name === 'seal')
|| (deviceType === deviceStore.deviceTypeMap.SmallSpaceDM && item.name === 'seal')
|| (deviceType === deviceStore.deviceTypeMap.DrawBarDM && item.name === 'liquid')
) {
return false
}
if (item.name === 'debug' && userRole !== 'admin') {
return false
}
return true
})
const routes: RouteRecordRaw[] = [
{
path: '/login',
name: 'login',
component: () => import('../views/login/index.vue'),
},
{
path: '/',
component: () => import('../layouts/default.vue'),
redirect: '/home',
children: authRoutes,
},
]
return [
{
path: '/',
component: () => import('../layouts/default.vue'),
redirect: '/home',
children: filteredRoutes,
},
{
path: '/login',
name: 'login',
component: () => import('../views/login/index.vue'),
},
]
}
export { authRoutes }
export default routes

2
src/views/home/index.vue

@ -1,4 +1,5 @@
<script lang="ts" setup>
import InputKey from 'components/common/FTKeyboard/index.vue'
import Environment from 'components/home/Environment.vue'
import HomeFormula from 'components/home/HomeFormula.vue'
import HomeLogLevel from 'components/home/HomeLogLevel.vue'
@ -100,6 +101,7 @@ const deviceType = computed(() => {
</el-card>
</div>
<div class="home-grid-item home-right">
<InputKey />
<!-- 正在进行的配方 -->
<div class="top">
<HomeFormula />

79
src/views/login/index.vue

@ -63,27 +63,29 @@ const loginHandle = async () => {
return
}
loading.value = true
login(loginForm.value as User.Login).then(async (res) => {
if (res.ackcode === 0) {
setToken('login success')
//
const params = {
className: 'UserMgrService',
fnName: 'getLoginUser',
}
syncSendCmd(params).then((res) => {
if (res.ackcode === 0) {
localStorage.setItem('user', JSON.stringify(res.rely.loginUser))
deviceStore.setDeviceState(res.rely.loginUser)
login(loginForm.value as User.Login)
.then(async (res) => {
if (res.ackcode === 0) {
setToken('login success')
//
const params = {
className: 'UserMgrService',
fnName: 'getLoginUser',
}
})
await router.push('/home')
}
loading.value = false
}).catch(() => {
loading.value = false
FtMessage.error('网络连接异常,请检查网络')
})
syncSendCmd(params).then((res) => {
if (res.ackcode === 0) {
localStorage.setItem('user', JSON.stringify(res.rely.loginUser))
deviceStore.setDeviceState(res.rely.loginUser)
}
})
await router.push('/home')
}
loading.value = false
})
.catch(() => {
loading.value = false
FtMessage.error('网络连接异常,请检查网络')
})
}
catch (e) {
loading.value = false
@ -100,7 +102,7 @@ const loginHandle = async () => {
<div class="login-box">
<div class="login-box-content">
<div>
<img :src="logo" style="width:20vw" alt="">
<img :src="logo" style="width: 20vw" alt="">
</div>
<br>
<el-form ref="formRef" class="input-username" :model="loginForm" :rules="rules" style="width: 100%">
@ -115,7 +117,14 @@ const loginHandle = async () => {
trigger: ['blur', 'change'],
}"
>
<el-input v-model="loginForm.name" v-prevent-keyboard size="large" name="name" placeholder="用户名" @focus="openKeyboard">
<el-input
v-model="loginForm.name"
v-prevent-keyboard
size="large"
name="name"
placeholder="用户名"
@focus="openKeyboard"
>
<template #prepend>
<img class="input-icon" :src="user" alt="">
</template>
@ -133,7 +142,15 @@ const loginHandle = async () => {
trigger: ['blur', 'change'],
}"
>
<el-input v-model="loginForm.pwd" v-prevent-keyboard size="large" name="pwd" placeholder="密码" type="password" @focus="openKeyboard">
<el-input
v-model="loginForm.pwd"
v-prevent-keyboard
size="large"
name="pwd"
placeholder="密码"
type="password"
@focus="openKeyboard"
>
<template #prepend>
<img class="input-icon" :src="password" alt="">
</template>
@ -156,7 +173,7 @@ const loginHandle = async () => {
v-model="inputValue"
:is-visible="keyboardVisible"
:keyboard-type="keyboardType"
@update-keyboard-visible="(visible: boolean) => keyboardVisible = visible"
@update-keyboard-visible="(visible: boolean) => (keyboardVisible = visible)"
@confirm="keyboardVisible = false"
@close="keyboardVisible = false"
/>
@ -167,7 +184,7 @@ const loginHandle = async () => {
<style scoped lang="scss">
.login-box {
background: url("assets/images/background-login.svg") no-repeat center;
background: url('assets/images/background-login.svg') no-repeat center;
display: flex;
justify-content: center;
align-items: center;
@ -183,8 +200,8 @@ const loginHandle = async () => {
img {
width: 50px;
}
.title{
color: #8799AB;
.title {
color: #8799ab;
font-weight: 500;
font-size: 18px;
margin: 15px 0 30px;
@ -194,19 +211,19 @@ const loginHandle = async () => {
.input-icon {
width: 15px !important;
}
.input-username{
.input-username {
font-size: 14px;
color: #2C3E59;
color: #2c3e59;
// margin-bottom: 10px;
margin-top: 3rem;
}
.input-title {
font-size: 14px;
color: #2C3E59;
color: #2c3e59;
// margin-bottom: 10px;
margin-top: 4rem;
}
.login-btn{
.login-btn {
height: 2.5vh;
display: flex;
align-items: center;

Loading…
Cancel
Save