mirror of
https://gitee.com/fantastic-admin/basic.git
synced 2024-12-10 16:55:00 +08:00
使用 pinia 替换 vuex
This commit is contained in:
parent
65eff15674
commit
5d7461c5cc
@ -46,8 +46,6 @@ module.exports = {
|
|||||||
withDefaults: 'readonly',
|
withDefaults: 'readonly',
|
||||||
// vue-router
|
// vue-router
|
||||||
useRoute: 'readonly',
|
useRoute: 'readonly',
|
||||||
useRouter: 'readonly',
|
useRouter: 'readonly'
|
||||||
// vuex
|
|
||||||
useStore: 'readonly'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,12 +31,12 @@
|
|||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"path-to-regexp": "^6.2.0",
|
"path-to-regexp": "^6.2.0",
|
||||||
|
"pinia": "^2.0.9",
|
||||||
"qs": "^6.10.2",
|
"qs": "^6.10.2",
|
||||||
"screenfull": "^6.0.0",
|
"screenfull": "^6.0.0",
|
||||||
"tinymce": "^5.10.2",
|
"tinymce": "^5.10.2",
|
||||||
"vue": "^3.2.24",
|
"vue": "^3.2.24",
|
||||||
"vue-router": "^4.0.12",
|
"vue-router": "^4.0.12"
|
||||||
"vuex": "^4.0.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^1.10.2",
|
"@vitejs/plugin-vue": "^1.10.2",
|
||||||
|
@ -4,18 +4,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup{{#if isGlobal}} name="{{ properCase name }}"{{/if}}>
|
||||||
export default {
|
// const { proxy } = getCurrentInstance()
|
||||||
{{#if isGlobal}}
|
|
||||||
name: '{{ properCase name }}',
|
|
||||||
{{/if}}
|
|
||||||
props: {},
|
|
||||||
data() {
|
|
||||||
return {}
|
|
||||||
},
|
|
||||||
mounted() {},
|
|
||||||
methods: {}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
// const { proxy } = getCurrentInstance()
|
// const { proxy } = getCurrentInstance()
|
||||||
// const router = useRouter()
|
// const router = useRouter()
|
||||||
// const route = useRoute()
|
// const route = useRoute()
|
||||||
// const store = useStore()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
const state = () => ({})
|
import { defineStore } from 'pinia'
|
||||||
|
import { piniaStore } from '@/store'
|
||||||
|
|
||||||
const getters = {}
|
export const use{{ properCase name }}Store = defineStore(
|
||||||
|
// 唯一ID
|
||||||
|
'{{ camelCase name }}',
|
||||||
|
{
|
||||||
|
state: () => ({}),
|
||||||
|
getters: {},
|
||||||
|
actions: {}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const actions = {}
|
export function use{{ properCase name }}OutsideStore() {
|
||||||
|
return use{{ properCase name }}Store(piniaStore)
|
||||||
const mutations = {}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
namespaced: true,
|
|
||||||
state,
|
|
||||||
actions,
|
|
||||||
getters,
|
|
||||||
mutations
|
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,11 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
actions: data => {
|
actions: () => {
|
||||||
const actions = [
|
const actions = [
|
||||||
{
|
{
|
||||||
type: 'add',
|
type: 'add',
|
||||||
path: `src/store/modules/${data.name}.js`,
|
path: 'src/store/modules/{{camelCase name}}.js',
|
||||||
templateFile: 'plop-templates/store/index.hbs'
|
templateFile: 'plop-templates/store/index.hbs'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -22,6 +22,7 @@ specifiers:
|
|||||||
nprogress: ^0.2.0
|
nprogress: ^0.2.0
|
||||||
path-browserify: ^1.0.1
|
path-browserify: ^1.0.1
|
||||||
path-to-regexp: ^6.2.0
|
path-to-regexp: ^6.2.0
|
||||||
|
pinia: ^2.0.9
|
||||||
plop: ^3.0.4
|
plop: ^3.0.4
|
||||||
postcss-html: ^1.3.0
|
postcss-html: ^1.3.0
|
||||||
postcss-scss: ^4.0.2
|
postcss-scss: ^4.0.2
|
||||||
@ -51,7 +52,6 @@ specifiers:
|
|||||||
vue: ^3.2.24
|
vue: ^3.2.24
|
||||||
vue-eslint-parser: ^8.0.1
|
vue-eslint-parser: ^8.0.1
|
||||||
vue-router: ^4.0.12
|
vue-router: ^4.0.12
|
||||||
vuex: ^4.0.2
|
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@element-plus/icons-vue': 0.2.4_vue@3.2.24
|
'@element-plus/icons-vue': 0.2.4_vue@3.2.24
|
||||||
@ -68,12 +68,12 @@ dependencies:
|
|||||||
nprogress: 0.2.0
|
nprogress: 0.2.0
|
||||||
path-browserify: 1.0.1
|
path-browserify: 1.0.1
|
||||||
path-to-regexp: 6.2.0
|
path-to-regexp: 6.2.0
|
||||||
|
pinia: 2.0.9_vue@3.2.24
|
||||||
qs: 6.10.2
|
qs: 6.10.2
|
||||||
screenfull: 6.0.0
|
screenfull: 6.0.0
|
||||||
tinymce: 5.10.2
|
tinymce: 5.10.2
|
||||||
vue: 3.2.24
|
vue: 3.2.24
|
||||||
vue-router: 4.0.12_vue@3.2.24
|
vue-router: 4.0.12_vue@3.2.24
|
||||||
vuex: 4.0.2_vue@3.2.24
|
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@vitejs/plugin-vue': 1.10.2_vite@2.7.1
|
'@vitejs/plugin-vue': 1.10.2_vite@2.7.1
|
||||||
@ -413,6 +413,10 @@ packages:
|
|||||||
resolution: {integrity: sha1-+OiAWdqkJFFZkkJqDH6lzeB+mb8=, tarball: '@vue/devtools-api/download/@vue/devtools-api-6.0.0-beta.19.tgz'}
|
resolution: {integrity: sha1-+OiAWdqkJFFZkkJqDH6lzeB+mb8=, tarball: '@vue/devtools-api/download/@vue/devtools-api-6.0.0-beta.19.tgz'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@vue/devtools-api/6.0.0-beta.21.1:
|
||||||
|
resolution: {integrity: sha512-FqC4s3pm35qGVeXRGOjTsRzlkJjrBLriDS9YXbflHLsfA9FrcKzIyWnLXoNm+/7930E8rRakXuAc2QkC50swAw==, tarball: '@vue/devtools-api/download/@vue/devtools-api-6.0.0-beta.21.1.tgz'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@vue/reactivity/3.2.24:
|
/@vue/reactivity/3.2.24:
|
||||||
resolution: {integrity: sha512-5eVsO9wfQ5erCMSRBjpqLkkI+LglJS7E0oLZJs2gsChpvOjH2Uwt3Hk1nVv0ywStnWg71Ykn3SyQwtnl7PknOQ==, tarball: '@vue/reactivity/download/@vue/reactivity-3.2.24.tgz'}
|
resolution: {integrity: sha512-5eVsO9wfQ5erCMSRBjpqLkkI+LglJS7E0oLZJs2gsChpvOjH2Uwt3Hk1nVv0ywStnWg71Ykn3SyQwtnl7PknOQ==, tarball: '@vue/reactivity/download/@vue/reactivity-3.2.24.tgz'}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3808,6 +3812,23 @@ packages:
|
|||||||
engines: {node: '>=8.6'}
|
engines: {node: '>=8.6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pinia/2.0.9_vue@3.2.24:
|
||||||
|
resolution: {integrity: sha512-iuYdxLJKQ07YPyOHYH05wNG9eKWqkP/4y4GE8+RqEYtz5fwHgPA5kr6zQbg/DoEJGnR2XCm1w1vdt6ppzL9ATg==, tarball: pinia/download/pinia-2.0.9.tgz}
|
||||||
|
peerDependencies:
|
||||||
|
'@vue/composition-api': ^1.4.0
|
||||||
|
typescript: '>=4.4.4'
|
||||||
|
vue: ^2.6.14 || ^3.2.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@vue/composition-api':
|
||||||
|
optional: true
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@vue/devtools-api': 6.0.0-beta.21.1
|
||||||
|
vue: 3.2.24
|
||||||
|
vue-demi: 0.12.1_vue@3.2.24
|
||||||
|
dev: false
|
||||||
|
|
||||||
/pixelsmith/2.5.0:
|
/pixelsmith/2.5.0:
|
||||||
resolution: {integrity: sha1-ez5SLgjqh3vXC63xWb3D7Ffh6EE=, tarball: pixelsmith/download/pixelsmith-2.5.0.tgz}
|
resolution: {integrity: sha1-ez5SLgjqh3vXC63xWb3D7Ffh6EE=, tarball: pixelsmith/download/pixelsmith-2.5.0.tgz}
|
||||||
engines: {node: '>= 8.0.0'}
|
engines: {node: '>= 8.0.0'}
|
||||||
@ -5477,15 +5498,6 @@ packages:
|
|||||||
'@vue/shared': 3.2.24
|
'@vue/shared': 3.2.24
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/vuex/4.0.2_vue@3.2.24:
|
|
||||||
resolution: {integrity: sha1-+Jbb1b8qDpY/AMZ+m2EN50nMrMk=, tarball: vuex/download/vuex-4.0.2.tgz}
|
|
||||||
peerDependencies:
|
|
||||||
vue: ^3.0.2
|
|
||||||
dependencies:
|
|
||||||
'@vue/devtools-api': 6.0.0-beta.19
|
|
||||||
vue: 3.2.24
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/wcwidth/1.0.1:
|
/wcwidth/1.0.1:
|
||||||
resolution: {integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=, tarball: wcwidth/download/wcwidth-1.0.1.tgz}
|
resolution: {integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=, tarball: wcwidth/download/wcwidth-1.0.1.tgz}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
35
src/App.vue
35
src/App.vue
@ -8,13 +8,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const store = useStore()
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
|
||||||
// 侧边栏主导航当前实际宽度
|
// 侧边栏主导航当前实际宽度
|
||||||
const mainSidebarActualWidth = computed(() => {
|
const mainSidebarActualWidth = computed(() => {
|
||||||
let actualWidth = getComputedStyle(document.documentElement).getPropertyValue('--g-main-sidebar-width')
|
let actualWidth = getComputedStyle(document.documentElement).getPropertyValue('--g-main-sidebar-width')
|
||||||
actualWidth = parseInt(actualWidth)
|
actualWidth = parseInt(actualWidth)
|
||||||
if (['head', 'single'].includes(store.state.settings.menuMode)) {
|
if (['head', 'single'].includes(settingsStore.menuMode)) {
|
||||||
actualWidth = 0
|
actualWidth = 0
|
||||||
}
|
}
|
||||||
return `${actualWidth}px`
|
return `${actualWidth}px`
|
||||||
@ -24,39 +25,39 @@ const mainSidebarActualWidth = computed(() => {
|
|||||||
const subSidebarActualWidth = computed(() => {
|
const subSidebarActualWidth = computed(() => {
|
||||||
let actualWidth = getComputedStyle(document.documentElement).getPropertyValue('--g-sub-sidebar-width')
|
let actualWidth = getComputedStyle(document.documentElement).getPropertyValue('--g-sub-sidebar-width')
|
||||||
actualWidth = parseInt(actualWidth)
|
actualWidth = parseInt(actualWidth)
|
||||||
if (store.state.settings.sidebarCollapse) {
|
if (settingsStore.sidebarCollapse) {
|
||||||
actualWidth = 64
|
actualWidth = 64
|
||||||
}
|
}
|
||||||
return `${actualWidth}px`
|
return `${actualWidth}px`
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => store.state.settings.mode, () => {
|
watch(() => settingsStore.mode, () => {
|
||||||
if (store.state.settings.mode === 'pc') {
|
if (settingsStore.mode === 'pc') {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'sidebarCollapse': store.state.settings.sidebarCollapseLastStatus
|
'sidebarCollapse': settingsStore.sidebarCollapseLastStatus
|
||||||
})
|
})
|
||||||
} else if (store.state.settings.mode === 'mobile') {
|
} else if (settingsStore.mode === 'mobile') {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'sidebarCollapse': true
|
'sidebarCollapse': true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
document.body.setAttribute('data-mode', store.state.settings.mode)
|
document.body.setAttribute('data-mode', settingsStore.mode)
|
||||||
}, {
|
}, {
|
||||||
immediate: true
|
immediate: true
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => store.state.settings.menuMode, () => {
|
watch(() => settingsStore.menuMode, () => {
|
||||||
document.body.setAttribute('data-menu-mode', store.state.settings.menuMode)
|
document.body.setAttribute('data-menu-mode', settingsStore.menuMode)
|
||||||
}, {
|
}, {
|
||||||
immediate: true
|
immediate: true
|
||||||
})
|
})
|
||||||
|
|
||||||
watch([
|
watch([
|
||||||
() => store.state.settings.enableDynamicTitle,
|
() => settingsStore.enableDynamicTitle,
|
||||||
() => store.state.settings.title
|
() => settingsStore.title
|
||||||
], () => {
|
], () => {
|
||||||
if (store.state.settings.enableDynamicTitle && store.state.settings.title) {
|
if (settingsStore.enableDynamicTitle && settingsStore.title) {
|
||||||
let title = store.state.settings.title
|
let title = settingsStore.title
|
||||||
document.title = `${title} - ${import.meta.env.VITE_APP_TITLE}`
|
document.title = `${title} - ${import.meta.env.VITE_APP_TITLE}`
|
||||||
} else {
|
} else {
|
||||||
document.title = import.meta.env.VITE_APP_TITLE
|
document.title = import.meta.env.VITE_APP_TITLE
|
||||||
@ -67,7 +68,7 @@ watch([
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.onresize = () => {
|
window.onresize = () => {
|
||||||
store.commit('settings/setMode', document.documentElement.clientWidth)
|
settingsStore.setMode(document.documentElement.clientWidth)
|
||||||
}
|
}
|
||||||
window.onresize()
|
window.onresize()
|
||||||
})
|
})
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
// import qs from 'qs'
|
// import qs from 'qs'
|
||||||
import router from '@/router/index'
|
import router from '@/router/index'
|
||||||
import store from '@/store/index'
|
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { useUserOutsideStore } from '@/store/modules/user'
|
||||||
|
|
||||||
const toLogin = () => {
|
const toLogin = () => {
|
||||||
store.dispatch('user/logout').then(() => {
|
const userOutsideStore = useUserOutsideStore()
|
||||||
|
userOutsideStore.logout().then(() => {
|
||||||
router.push({
|
router.push({
|
||||||
name: 'login',
|
name: 'login',
|
||||||
query: {
|
query: {
|
||||||
@ -23,12 +24,13 @@ const api = axios.create({
|
|||||||
|
|
||||||
api.interceptors.request.use(
|
api.interceptors.request.use(
|
||||||
request => {
|
request => {
|
||||||
|
const userOutsideStore = useUserOutsideStore()
|
||||||
/**
|
/**
|
||||||
* 全局拦截请求发送前提交的参数
|
* 全局拦截请求发送前提交的参数
|
||||||
* 以下代码为示例,在请求头里带上 token 信息
|
* 以下代码为示例,在请求头里带上 token 信息
|
||||||
*/
|
*/
|
||||||
if (store.getters['user/isLogin']) {
|
if (userOutsideStore.isLogin) {
|
||||||
request.headers['Token'] = store.state.user.token
|
request.headers['Token'] = userOutsideStore.token
|
||||||
}
|
}
|
||||||
// 是否将 POST 请求参数进行字符串化处理
|
// 是否将 POST 请求参数进行字符串化处理
|
||||||
if (request.method === 'post') {
|
if (request.method === 'post') {
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<footer class="copyright">
|
<footer class="copyright">
|
||||||
Copyright © {{ $store.state.settings.copyrightDates }}
|
Copyright © {{ settingsStore.copyrightDates }}
|
||||||
<a v-if="$store.state.settings.copyrightWebsite" :href="$store.state.settings.copyrightWebsite" target="_blank" rel="noopener">{{ $store.state.settings.copyrightCompany }},</a>
|
<a v-if="settingsStore.copyrightWebsite" :href="settingsStore.copyrightWebsite" target="_blank" rel="noopener">{{ settingsStore.copyrightCompany }},</a>
|
||||||
<span v-else>{{ $store.state.settings.copyrightCompany }},</span>
|
<span v-else>{{ settingsStore.copyrightCompany }},</span>
|
||||||
All Rights Reserved
|
All Rights Reserved
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
footer {
|
footer {
|
||||||
margin: 40px 0 20px;
|
margin: 40px 0 20px;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition name="header">
|
<transition name="header">
|
||||||
<header v-if="$store.state.settings.mode === 'pc' && $store.state.settings.menuMode === 'head'">
|
<header v-if="settingsStore.mode === 'pc' && settingsStore.menuMode === 'head'">
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<Logo />
|
<Logo />
|
||||||
<!-- 顶部模式 -->
|
<!-- 顶部模式 -->
|
||||||
<div class="nav">
|
<div class="nav">
|
||||||
<template v-for="(item, index) in $store.getters['menu/routes']">
|
<template v-for="(item, index) in menuStore.transformRoutes">
|
||||||
<div v-if="item.children && item.children.length !== 0" :key="index" class="item" :class="{'active': index == $store.state.menu.headerActived}" @click="switchMenu(index)">
|
<div v-if="item.children && item.children.length !== 0" :key="index" class="item" :class="{'active': index == menuStore.headerActived}" @click="switchMenu(index)">
|
||||||
<svg-icon v-if="item.meta.icon" :name="item.meta.icon" />
|
<svg-icon v-if="item.meta.icon" :name="item.meta.icon" />
|
||||||
<span v-if="item.meta.title">{{ item.meta.title }}</span>
|
<span v-if="item.meta.title">{{ item.meta.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -24,6 +24,11 @@
|
|||||||
import Logo from '../Logo/index.vue'
|
import Logo from '../Logo/index.vue'
|
||||||
import UserMenu from '../UserMenu/index.vue'
|
import UserMenu from '../UserMenu/index.vue'
|
||||||
|
|
||||||
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
import { useMenuStore } from '@/store/modules/menu'
|
||||||
|
const menuStore = useMenuStore()
|
||||||
|
|
||||||
const switchMenu = inject('switchMenu')
|
const switchMenu = inject('switchMenu')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<router-link :to="to" class="title" :class="{'is-link': $store.state.settings.enableDashboard}" :title="title">
|
<router-link :to="to" class="title" :class="{'is-link': settingsStore.enableDashboard}" :title="title">
|
||||||
<img v-if="showLogo" :src="logo" class="logo">
|
<img v-if="showLogo" :src="logo" class="logo">
|
||||||
<span v-if="showTitle">{{ title }}</span>
|
<span v-if="showTitle">{{ title }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
@ -8,7 +8,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import imgLogo from '@/assets/images/logo.png'
|
import imgLogo from '@/assets/images/logo.png'
|
||||||
|
|
||||||
const store = useStore()
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
showLogo: {
|
showLogo: {
|
||||||
@ -26,7 +27,7 @@ const logo = ref(imgLogo)
|
|||||||
|
|
||||||
const to = computed(() => {
|
const to = computed(() => {
|
||||||
let rtn = {}
|
let rtn = {}
|
||||||
if (store.state.settings.enableDashboard) {
|
if (settingsStore.enableDashboard) {
|
||||||
rtn.name = 'dashboard'
|
rtn.name = 'dashboard'
|
||||||
}
|
}
|
||||||
return rtn
|
return rtn
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition name="main-sidebar">
|
<transition name="main-sidebar">
|
||||||
<div v-if="$store.state.settings.menuMode === 'side' || ($store.state.settings.mode === 'mobile' && $store.state.settings.menuMode !== 'single')" class="main-sidebar-container">
|
<div v-if="settingsStore.menuMode === 'side' || (settingsStore.mode === 'mobile' && settingsStore.menuMode !== 'single')" class="main-sidebar-container">
|
||||||
<Logo :show-title="false" class="sidebar-logo" />
|
<Logo :show-title="false" class="sidebar-logo" />
|
||||||
<!-- 侧边栏模式(含主导航) -->
|
<!-- 侧边栏模式(含主导航) -->
|
||||||
<div class="nav">
|
<div class="nav">
|
||||||
<template v-for="(item, index) in $store.getters['menu/routes']">
|
<template v-for="(item, index) in menuStore.transformRoutes">
|
||||||
<div
|
<div
|
||||||
v-if="item.children && item.children.length !== 0" :key="index" :class="{
|
v-if="item.children && item.children.length !== 0" :key="index" :class="{
|
||||||
'item': true,
|
'item': true,
|
||||||
'active': index === $store.state.menu.headerActived
|
'active': index === menuStore.headerActived
|
||||||
}" :title="item.meta.title" @click="switchMenu(index)"
|
}" :title="item.meta.title" @click="switchMenu(index)"
|
||||||
>
|
>
|
||||||
<svg-icon v-if="item.meta.icon" :name="item.meta.icon" />
|
<svg-icon v-if="item.meta.icon" :name="item.meta.icon" />
|
||||||
@ -23,6 +23,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Logo from '../Logo/index.vue'
|
import Logo from '../Logo/index.vue'
|
||||||
|
|
||||||
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
import { useMenuStore } from '@/store/modules/menu'
|
||||||
|
const menuStore = useMenuStore()
|
||||||
|
|
||||||
const switchMenu = inject('switchMenu')
|
const switchMenu = inject('switchMenu')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="search-box" @click.stop>
|
<div class="search-box" @click.stop>
|
||||||
<el-input ref="input" v-model="searchInput" prefix-icon="el-icon-search" placeholder="搜索页面,支持标题、URL模糊查询" clearable @keydown.esc="$eventBus.emit('global-search-toggle')" @keydown.up.prevent="keyUp" @keydown.down.prevent="keyDown" @keydown.enter.prevent="keyEnter" />
|
<el-input ref="input" v-model="searchInput" prefix-icon="el-icon-search" placeholder="搜索页面,支持标题、URL模糊查询" clearable @keydown.esc="$eventBus.emit('global-search-toggle')" @keydown.up.prevent="keyUp" @keydown.down.prevent="keyDown" @keydown.enter.prevent="keyEnter" />
|
||||||
<div v-if="$store.state.settings.mode === 'pc'" class="tips">
|
<div v-if="settingsStore.mode === 'pc'" class="tips">
|
||||||
<div class="tip">
|
<div class="tip">
|
||||||
<span>Alt</span>+<span>S</span>
|
<span>Alt</span>+<span>S</span>
|
||||||
唤醒搜索面板
|
唤醒搜索面板
|
||||||
@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ref="search" class="result" :class="{'mobile': $store.state.settings.mode === 'mobile'}">
|
<div ref="search" class="result" :class="{'mobile': settingsStore.mode === 'mobile'}">
|
||||||
<router-link v-for="(item, index) in resultList" :key="item.path" v-slot="{ href, navigate }" custom :to="isShow ? item.path : ''">
|
<router-link v-for="(item, index) in resultList" :key="item.path" v-slot="{ href, navigate }" custom :to="isShow ? item.path : ''">
|
||||||
<a :ref="`search-item-${index}`" :href="isExternalLink(item.path) ? item.path : href" class="item" :class="{'actived': index === actived}" :target="isExternalLink(item.path) ? '_blank' : '_self'" @click="navigate" @mouseover="actived = index">
|
<a :ref="`search-item-${index}`" :href="isExternalLink(item.path) ? item.path : href" class="item" :class="{'actived': index === actived}" :target="isExternalLink(item.path) ? '_blank' : '_self'" @click="navigate" @mouseover="actived = index">
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
@ -53,7 +53,11 @@
|
|||||||
import { deepClone, isExternalLink } from '@/util'
|
import { deepClone, isExternalLink } from '@/util'
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
const store = useStore()
|
|
||||||
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
import { useMenuStore } from '@/store/modules/menu'
|
||||||
|
const menuStore = useMenuStore()
|
||||||
|
|
||||||
const isShow = ref(false)
|
const isShow = ref(false)
|
||||||
const searchInput = ref('')
|
const searchInput = ref('')
|
||||||
@ -110,12 +114,12 @@ onMounted(() => {
|
|||||||
isShow.value = !isShow.value
|
isShow.value = !isShow.value
|
||||||
})
|
})
|
||||||
proxy.$hotkeys('alt+s', e => {
|
proxy.$hotkeys('alt+s', e => {
|
||||||
if (store.state.settings.enableNavSearch) {
|
if (settingsStore.enableNavSearch) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
isShow.value = true
|
isShow.value = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
store.state.menu.routes.map(item => {
|
menuStore.routes.map(item => {
|
||||||
getSourceList(item.children)
|
getSourceList(item.children)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="['side', 'head', 'single'].includes($store.state.settings.menuMode) || $store.state.settings.mode === 'mobile'" class="sub-sidebar-container" :class="{'is-collapse': $store.state.settings.mode === 'pc' && $store.state.settings.sidebarCollapse}" @scroll="onSidebarScroll">
|
<div v-if="['side', 'head', 'single'].includes(settingsStore.menuMode) || settingsStore.mode === 'mobile'" class="sub-sidebar-container" :class="{'is-collapse': settingsStore.mode === 'pc' && settingsStore.sidebarCollapse}" @scroll="onSidebarScroll">
|
||||||
<Logo
|
<Logo
|
||||||
:show-logo="$store.state.settings.menuMode === 'single'" :class="{
|
:show-logo="settingsStore.menuMode === 'single'" :class="{
|
||||||
'sidebar-logo': true,
|
'sidebar-logo': true,
|
||||||
'sidebar-logo-bg': $store.state.settings.menuMode === 'single',
|
'sidebar-logo-bg': settingsStore.menuMode === 'single',
|
||||||
'shadow': sidebarScrollTop
|
'shadow': sidebarScrollTop
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<!-- 侧边栏模式(无主导航) -->
|
<!-- 侧边栏模式(无主导航) -->
|
||||||
<el-menu
|
<el-menu
|
||||||
:unique-opened="$store.state.settings.sidebarUniqueOpened" :default-openeds="$store.state.menu.defaultOpenedPaths" :default-active="$route.meta.activeMenu || $route.path" :collapse="$store.state.settings.mode === 'pc' && $store.state.settings.sidebarCollapse" :collapse-transition="false" :class="{
|
:unique-opened="settingsStore.sidebarUniqueOpened" :default-openeds="menuStore.defaultOpenedPaths" :default-active="$route.meta.activeMenu || $route.path" :collapse="settingsStore.mode === 'pc' && settingsStore.sidebarCollapse" :collapse-transition="false" :class="{
|
||||||
'is-collapse-without-logo': $store.state.settings.menuMode !== 'single' && $store.state.settings.sidebarCollapse
|
'is-collapse-without-logo': settingsStore.menuMode !== 'single' && settingsStore.sidebarCollapse
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<transition-group name="sub-sidebar">
|
<transition-group name="sub-sidebar">
|
||||||
<template v-for="route in $store.getters['menu/sidebarRoutes']">
|
<template v-for="route in menuStore.sidebarRoutes">
|
||||||
<SidebarItem v-if="route.meta.sidebar !== false" :key="route.path" :item="route" :base-path="route.path" />
|
<SidebarItem v-if="route.meta.sidebar !== false" :key="route.path" :item="route" :base-path="route.path" />
|
||||||
</template>
|
</template>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
@ -26,6 +26,11 @@
|
|||||||
import Logo from '../Logo/index.vue'
|
import Logo from '../Logo/index.vue'
|
||||||
import SidebarItem from '../SidebarItem/index.vue'
|
import SidebarItem from '../SidebarItem/index.vue'
|
||||||
|
|
||||||
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
import { useMenuStore } from '@/store/modules/menu'
|
||||||
|
const menuStore = useMenuStore()
|
||||||
|
|
||||||
const sidebarScrollTop = ref(0)
|
const sidebarScrollTop = ref(0)
|
||||||
|
|
||||||
function onSidebarScroll(e) {
|
function onSidebarScroll(e) {
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<el-drawer v-model="isShow" title="主题配置" direction="rtl" :size="300">
|
<el-drawer v-model="isShow" title="主题配置" direction="rtl" :size="300">
|
||||||
<el-alert title="主题配置可实时预览效果,更多设置请在 src/settings.js 中进行设置,建议在生产环境隐藏主题配置功能" type="error" :closable="false" />
|
<el-alert title="主题配置可实时预览效果,更多设置请在 src/settings.js 中进行设置,建议在生产环境隐藏主题配置功能" type="error" :closable="false" />
|
||||||
<el-divider v-if="$store.state.settings.mode === 'pc'">导航栏模式</el-divider>
|
<el-divider v-if="settingsStore.mode === 'pc'">导航栏模式</el-divider>
|
||||||
<div v-if="$store.state.settings.mode === 'pc'" class="menu-mode">
|
<div v-if="settingsStore.mode === 'pc'" class="menu-mode">
|
||||||
<el-tooltip content="侧边栏模式(含主导航)" placement="top" :show-after="500" :append-to-body="false">
|
<el-tooltip content="侧边栏模式(含主导航)" placement="top" :show-after="500" :append-to-body="false">
|
||||||
<div class="mode mode-side" :class="{'active': menuMode === 'side'}" @click="menuMode = 'side'">
|
<div class="mode mode-side" :class="{'active': menuMode === 'side'}" @click="menuMode = 'side'">
|
||||||
<svg-icon name="el-icon-check" />
|
<svg-icon name="el-icon-check" />
|
||||||
@ -21,7 +21,7 @@
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<el-divider>侧边栏</el-divider>
|
<el-divider>侧边栏</el-divider>
|
||||||
<div v-if="$store.state.settings.mode === 'pc'" class="setting-item">
|
<div v-if="settingsStore.mode === 'pc'" class="setting-item">
|
||||||
<div class="label">折叠按钮</div>
|
<div class="label">折叠按钮</div>
|
||||||
<el-switch v-model="enableSidebarCollapse" />
|
<el-switch v-model="enableSidebarCollapse" />
|
||||||
</div>
|
</div>
|
||||||
@ -36,7 +36,7 @@
|
|||||||
<svg-icon name="el-icon-question-filled" />
|
<svg-icon name="el-icon-question-filled" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<el-switch v-model="switchSidebarAndPageJump" :disabled="['single'].includes($store.state.settings.menuMode)" />
|
<el-switch v-model="switchSidebarAndPageJump" :disabled="['single'].includes(settingsStore.menuMode)" />
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
@ -57,7 +57,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-switch v-model="topbarFixed" />
|
<el-switch v-model="topbarFixed" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="$store.state.settings.mode === 'pc'" class="setting-item">
|
<div v-if="settingsStore.mode === 'pc'" class="setting-item">
|
||||||
<div class="label">面包屑导航</div>
|
<div class="label">面包屑导航</div>
|
||||||
<el-switch v-model="enableBreadcrumb" />
|
<el-switch v-model="enableBreadcrumb" />
|
||||||
</div>
|
</div>
|
||||||
@ -71,7 +71,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-switch v-model="enableNavSearch" />
|
<el-switch v-model="enableNavSearch" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="$store.state.settings.mode === 'pc'" class="setting-item">
|
<div v-if="settingsStore.mode === 'pc'" class="setting-item">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
全屏
|
全屏
|
||||||
<el-tooltip content="该功能使用场景极少,用户习惯于通过窗口“最大化”功能来扩大显示区域,以显示更多内容,并且使用 F11 键也可以进入全屏效果" placement="top" :append-to-body="false">
|
<el-tooltip content="该功能使用场景极少,用户习惯于通过窗口“最大化”功能来扩大显示区域,以显示更多内容,并且使用 F11 键也可以进入全屏效果" placement="top" :append-to-body="false">
|
||||||
@ -141,32 +141,36 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
const store = useStore()
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
import { useMenuStore } from '@/store/modules/menu'
|
||||||
|
const menuStore = useMenuStore()
|
||||||
|
|
||||||
const reload = inject('reload')
|
const reload = inject('reload')
|
||||||
|
|
||||||
const isShow = ref(false)
|
const isShow = ref(false)
|
||||||
|
|
||||||
const menuMode = computed({
|
const menuMode = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.menuMode
|
return settingsStore.menuMode
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('menu/switchHeaderActived', 0)
|
menuStore.switchHeaderActived(0)
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'menuMode': newValue
|
'menuMode': newValue
|
||||||
})
|
})
|
||||||
store.state.settings.menuMode !== 'single' && store.commit('menu/setHeaderActived', route.fullPath)
|
settingsStore.menuMode !== 'single' && menuStore.setHeaderActived(route.fullPath)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const elementSize = computed({
|
const elementSize = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.elementSize
|
return settingsStore.elementSize
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
proxy.$ELEMENT.size = newValue
|
proxy.$ELEMENT.size = newValue
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'elementSize': newValue
|
'elementSize': newValue
|
||||||
})
|
})
|
||||||
reload()
|
reload()
|
||||||
@ -174,130 +178,130 @@ const elementSize = computed({
|
|||||||
})
|
})
|
||||||
const enableSidebarCollapse = computed({
|
const enableSidebarCollapse = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.enableSidebarCollapse
|
return settingsStore.enableSidebarCollapse
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'enableSidebarCollapse': newValue
|
'enableSidebarCollapse': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const sidebarCollapse = computed({
|
const sidebarCollapse = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.sidebarCollapse
|
return settingsStore.sidebarCollapse
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'sidebarCollapse': newValue
|
'sidebarCollapse': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const switchSidebarAndPageJump = computed({
|
const switchSidebarAndPageJump = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.switchSidebarAndPageJump
|
return settingsStore.switchSidebarAndPageJump
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'switchSidebarAndPageJump': newValue
|
'switchSidebarAndPageJump': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const sidebarUniqueOpened = computed({
|
const sidebarUniqueOpened = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.sidebarUniqueOpened
|
return settingsStore.sidebarUniqueOpened
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'sidebarUniqueOpened': newValue
|
'sidebarUniqueOpened': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const topbarFixed = computed({
|
const topbarFixed = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.topbarFixed
|
return settingsStore.topbarFixed
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'topbarFixed': newValue
|
'topbarFixed': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const enableBreadcrumb = computed({
|
const enableBreadcrumb = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.enableBreadcrumb
|
return settingsStore.enableBreadcrumb
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'enableBreadcrumb': newValue
|
'enableBreadcrumb': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const showCopyright = computed({
|
const showCopyright = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.showCopyright
|
return settingsStore.showCopyright
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'showCopyright': newValue
|
'showCopyright': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const enableNavSearch = computed({
|
const enableNavSearch = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.enableNavSearch
|
return settingsStore.enableNavSearch
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'enableNavSearch': newValue
|
'enableNavSearch': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const enableFullscreen = computed({
|
const enableFullscreen = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.enableFullscreen
|
return settingsStore.enableFullscreen
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'enableFullscreen': newValue
|
'enableFullscreen': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const enablePageReload = computed({
|
const enablePageReload = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.enablePageReload
|
return settingsStore.enablePageReload
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'enablePageReload': newValue
|
'enablePageReload': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const enableProgress = computed({
|
const enableProgress = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.enableProgress
|
return settingsStore.enableProgress
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'enableProgress': newValue
|
'enableProgress': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const enableDynamicTitle = computed({
|
const enableDynamicTitle = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.enableDynamicTitle
|
return settingsStore.enableDynamicTitle
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'enableDynamicTitle': newValue
|
'enableDynamicTitle': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const enableDashboard = computed({
|
const enableDashboard = computed({
|
||||||
get: function() {
|
get: function() {
|
||||||
return store.state.settings.enableDashboard
|
return settingsStore.enableDashboard
|
||||||
},
|
},
|
||||||
set: function(newValue) {
|
set: function(newValue) {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
'enableDashboard': newValue
|
'enableDashboard': newValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="topbar-container" :class="{
|
class="topbar-container" :class="{
|
||||||
'fixed': $store.state.settings.topbarFixed,
|
'fixed': settingsStore.topbarFixed,
|
||||||
'shadow': scrollTop
|
'shadow': scrollTop
|
||||||
}" data-fixed-calc-width
|
}" data-fixed-calc-width
|
||||||
>
|
>
|
||||||
<div class="left-box">
|
<div class="left-box">
|
||||||
<div v-if="$store.state.settings.mode === 'mobile' || (['side', 'head', 'single'].includes($store.state.settings.menuMode) && $store.state.settings.enableSidebarCollapse)" class="sidebar-collapse" :class="{'is-collapse': $store.state.settings.sidebarCollapse}" @click="$store.commit('settings/toggleSidebarCollapse')">
|
<div v-if="enableSidebarCollapse" class="sidebar-collapse" :class="{'is-collapse': settingsStore.sidebarCollapse}" @click="settingsStore.toggleSidebarCollapse()">
|
||||||
<svg-icon name="toolbar-collapse" />
|
<svg-icon name="toolbar-collapse" />
|
||||||
</div>
|
</div>
|
||||||
<el-breadcrumb v-if="$store.state.settings.enableBreadcrumb && $store.state.settings.mode === 'pc'" separator-class="el-icon-arrow-right">
|
<el-breadcrumb v-if="settingsStore.enableBreadcrumb && settingsStore.mode === 'pc'" separator-class="el-icon-arrow-right">
|
||||||
<transition-group name="breadcrumb">
|
<transition-group name="breadcrumb">
|
||||||
<template v-for="(item, index) in breadcrumbList">
|
<template v-for="(item, index) in breadcrumbList">
|
||||||
<el-breadcrumb-item v-if="index < breadcrumbList.length - 1" :key="item.path" :to="pathCompile(item.path)">
|
<el-breadcrumb-item v-if="index < breadcrumbList.length - 1" :key="item.path" :to="pathCompile(item.path)">
|
||||||
@ -31,15 +31,24 @@ import { compile } from 'path-to-regexp'
|
|||||||
import { deepClone } from '@/util'
|
import { deepClone } from '@/util'
|
||||||
import UserMenu from '../UserMenu/index.vue'
|
import UserMenu from '../UserMenu/index.vue'
|
||||||
|
|
||||||
const store = useStore()
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
|
||||||
|
const enableSidebarCollapse = computed(() => {
|
||||||
|
return settingsStore.mode === 'mobile' || (
|
||||||
|
['side', 'head', 'single'].includes(settingsStore.menuMode) &&
|
||||||
|
settingsStore.enableSidebarCollapse
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
const breadcrumbList = computed(() => {
|
const breadcrumbList = computed(() => {
|
||||||
let breadcrumbList = []
|
let breadcrumbList = []
|
||||||
if (store.state.settings.enableDashboard) {
|
if (settingsStore.enableDashboard) {
|
||||||
breadcrumbList.push({
|
breadcrumbList.push({
|
||||||
path: '/dashboard',
|
path: '/dashboard',
|
||||||
title: store.state.settings.dashboardTitle
|
title: settingsStore.dashboardTitle
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (route.meta.breadcrumbNeste) {
|
if (route.meta.breadcrumbNeste) {
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="user">
|
<div class="user">
|
||||||
<div class="tools">
|
<div class="tools">
|
||||||
<span v-if="$store.state.settings.mode == 'pc'" class="item item-pro" @click="pro">
|
<span v-if="settingsStore.mode == 'pc'" class="item item-pro" @click="pro">
|
||||||
<svg-icon name="pro" />
|
<svg-icon name="pro" />
|
||||||
<span class="title">查看专业版</span>
|
<span class="title">查看专业版</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="$store.state.settings.enableNavSearch" class="item" @click="$eventBus.emit('global-search-toggle')">
|
<span v-if="settingsStore.enableNavSearch" class="item" @click="$eventBus.emit('global-search-toggle')">
|
||||||
<svg-icon name="search" />
|
<svg-icon name="search" />
|
||||||
</span>
|
</span>
|
||||||
<span v-if="$store.state.settings.mode === 'pc' && isFullscreenEnable && $store.state.settings.enableFullscreen" class="item" @click="fullscreen">
|
<span v-if="settingsStore.mode === 'pc' && isFullscreenEnable && settingsStore.enableFullscreen" class="item" @click="fullscreen">
|
||||||
<svg-icon :name="isFullscreen ? 'fullscreen-exit' : 'fullscreen'" />
|
<svg-icon :name="isFullscreen ? 'fullscreen-exit' : 'fullscreen'" />
|
||||||
</span>
|
</span>
|
||||||
<span v-if="$store.state.settings.enablePageReload" class="item" @click="reload()">
|
<span v-if="settingsStore.enablePageReload" class="item" @click="reload()">
|
||||||
<svg-icon name="toolbar-reload" />
|
<svg-icon name="toolbar-reload" />
|
||||||
</span>
|
</span>
|
||||||
<span v-if="$store.state.settings.enableThemeSetting" class="item" @click="$eventBus.emit('global-theme-toggle')">
|
<span v-if="settingsStore.enableThemeSetting" class="item" @click="$eventBus.emit('global-theme-toggle')">
|
||||||
<svg-icon name="toolbar-theme" />
|
<svg-icon name="toolbar-theme" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -23,12 +23,12 @@
|
|||||||
<el-avatar size="medium">
|
<el-avatar size="medium">
|
||||||
<el-icon><el-icon-user-filled /></el-icon>
|
<el-icon><el-icon-user-filled /></el-icon>
|
||||||
</el-avatar>
|
</el-avatar>
|
||||||
{{ $store.state.user.account }}
|
{{ userStore.account }}
|
||||||
<el-icon><el-icon-caret-bottom /></el-icon>
|
<el-icon><el-icon-caret-bottom /></el-icon>
|
||||||
</div>
|
</div>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu class="user-dropdown">
|
<el-dropdown-menu class="user-dropdown">
|
||||||
<el-dropdown-item v-if="$store.state.settings.enableDashboard" command="dashboard">控制台</el-dropdown-item>
|
<el-dropdown-item v-if="settingsStore.enableDashboard" command="dashboard">控制台</el-dropdown-item>
|
||||||
<el-dropdown-item command="setting">个人设置</el-dropdown-item>
|
<el-dropdown-item command="setting">个人设置</el-dropdown-item>
|
||||||
<el-dropdown-item divided command="logout">退出登录</el-dropdown-item>
|
<el-dropdown-item divided command="logout">退出登录</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
@ -41,9 +41,13 @@
|
|||||||
import screenfull from 'screenfull'
|
import screenfull from 'screenfull'
|
||||||
|
|
||||||
const reload = inject('reload')
|
const reload = inject('reload')
|
||||||
const store = useStore()
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const isFullscreenEnable = computed(() => screenfull.isEnabled)
|
const isFullscreenEnable = computed(() => screenfull.isEnabled)
|
||||||
const isFullscreen = ref(false)
|
const isFullscreen = ref(false)
|
||||||
|
|
||||||
@ -77,7 +81,7 @@ function userCommand(command) {
|
|||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'logout':
|
case 'logout':
|
||||||
store.dispatch('user/logout').then(() => {
|
userStore.logout().then(() => {
|
||||||
router.push({
|
router.push({
|
||||||
name: 'login'
|
name: 'login'
|
||||||
})
|
})
|
||||||
|
@ -3,17 +3,17 @@
|
|||||||
<div id="app-main">
|
<div id="app-main">
|
||||||
<Header />
|
<Header />
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="sidebar-container" :class="{'show': $store.state.settings.mode === 'mobile' && !$store.state.settings.sidebarCollapse}">
|
<div class="sidebar-container" :class="{'show': settingsStore.mode === 'mobile' && !settingsStore.sidebarCollapse}">
|
||||||
<MainSidebar />
|
<MainSidebar />
|
||||||
<SubSidebar />
|
<SubSidebar />
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-mask" :class="{'show': $store.state.settings.mode === 'mobile' && !$store.state.settings.sidebarCollapse}" @click="$store.commit('settings/toggleSidebarCollapse')" />
|
<div class="sidebar-mask" :class="{'show': settingsStore.mode === 'mobile' && !settingsStore.sidebarCollapse}" @click="settingsStore.toggleSidebarCollapse()" />
|
||||||
<div class="main-container" :style="{'padding-bottom': $route.meta.paddingBottom}">
|
<div class="main-container" :style="{'padding-bottom': $route.meta.paddingBottom}">
|
||||||
<Topbar v-if="!($store.state.settings.menuMode === 'head' && !$store.state.settings.enableSidebarCollapse && !$store.state.settings.enableBreadcrumb)" />
|
<Topbar v-if="!(settingsStore.menuMode === 'head' && !settingsStore.enableSidebarCollapse && !settingsStore.enableBreadcrumb)" />
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<router-view v-slot="{ Component, route }">
|
<router-view v-slot="{ Component, route }">
|
||||||
<transition name="main" mode="out-in" appear>
|
<transition name="main" mode="out-in" appear>
|
||||||
<keep-alive :include="$store.state.keepAlive.list">
|
<keep-alive :include="keepAliveStore.list">
|
||||||
<component :is="Component" :key="route.fullPath" />
|
<component :is="Component" :key="route.fullPath" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</transition>
|
</transition>
|
||||||
@ -41,15 +41,21 @@ import BuyIt from './components/BuyIt/index.vue'
|
|||||||
import { isExternalLink } from '@/util'
|
import { isExternalLink } from '@/util'
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
const store = useStore()
|
|
||||||
const routeInfo = useRoute(), router = useRouter()
|
const routeInfo = useRoute(), router = useRouter()
|
||||||
|
|
||||||
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
import { useKeepAliveStore } from '@/store/modules/keepAlive'
|
||||||
|
const keepAliveStore = useKeepAliveStore()
|
||||||
|
import { useMenuStore } from '@/store/modules/menu'
|
||||||
|
const menuStore = useMenuStore()
|
||||||
|
|
||||||
const showCopyright = computed(() => {
|
const showCopyright = computed(() => {
|
||||||
return typeof routeInfo.meta.copyright !== 'undefined' ? routeInfo.meta.copyright : store.state.settings.showCopyright
|
return typeof routeInfo.meta.copyright !== 'undefined' ? routeInfo.meta.copyright : settingsStore.showCopyright
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => store.state.settings.sidebarCollapse, val => {
|
watch(() => settingsStore.sidebarCollapse, val => {
|
||||||
if (store.state.settings.mode === 'mobile') {
|
if (settingsStore.mode === 'mobile') {
|
||||||
if (!val) {
|
if (!val) {
|
||||||
document.querySelector('body').classList.add('hidden')
|
document.querySelector('body').classList.add('hidden')
|
||||||
} else {
|
} else {
|
||||||
@ -59,8 +65,8 @@ watch(() => store.state.settings.sidebarCollapse, val => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
watch(() => routeInfo.path, () => {
|
watch(() => routeInfo.path, () => {
|
||||||
if (store.state.settings.mode === 'mobile') {
|
if (settingsStore.mode === 'mobile') {
|
||||||
store.commit('settings/updateThemeSetting', {
|
settingsStore.updateThemeSetting({
|
||||||
sidebarCollapse: true
|
sidebarCollapse: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -68,7 +74,7 @@ watch(() => routeInfo.path, () => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
proxy.$hotkeys('f5', e => {
|
proxy.$hotkeys('f5', e => {
|
||||||
if (store.state.settings.enablePageReload) {
|
if (settingsStore.enablePageReload) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
reload()
|
reload()
|
||||||
}
|
}
|
||||||
@ -83,12 +89,12 @@ function reload() {
|
|||||||
|
|
||||||
provide('switchMenu', switchMenu)
|
provide('switchMenu', switchMenu)
|
||||||
function switchMenu(index) {
|
function switchMenu(index) {
|
||||||
store.commit('menu/switchHeaderActived', index)
|
menuStore.switchHeaderActived(index)
|
||||||
if (store.state.settings.switchSidebarAndPageJump) {
|
if (settingsStore.switchSidebarAndPageJump) {
|
||||||
if (isExternalLink(store.getters['menu/sidebarRoutesFirstDeepestPath'])) {
|
if (isExternalLink(menuStore.sidebarRoutesFirstDeepestPath)) {
|
||||||
window.open(store.getters['menu/sidebarRoutesFirstDeepestPath'], '_blank')
|
window.open(menuStore.sidebarRoutesFirstDeepestPath, '_blank')
|
||||||
} else {
|
} else {
|
||||||
router.push(store.getters['menu/sidebarRoutesFirstDeepestPath'])
|
router.push(menuStore.sidebarRoutesFirstDeepestPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,9 @@ import { createApp } from 'vue'
|
|||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
import store from './store'
|
import { piniaStore } from './store'
|
||||||
app.use(store)
|
import { useSettingsOutsideStore } from './store/modules/settings'
|
||||||
|
app.use(piniaStore)
|
||||||
|
|
||||||
import router from './router'
|
import router from './router'
|
||||||
app.use(router)
|
app.use(router)
|
||||||
@ -18,7 +19,7 @@ for (var key in ElementIcons) {
|
|||||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||||
app.use(ElementPlus, {
|
app.use(ElementPlus, {
|
||||||
locale: zhCn,
|
locale: zhCn,
|
||||||
size: store.state.settings.elementSize
|
size: useSettingsOutsideStore().elementSize
|
||||||
})
|
})
|
||||||
|
|
||||||
import globalProperties from '@/util/global.properties'
|
import globalProperties from '@/util/global.properties'
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
import store from '@/store'
|
|
||||||
import NProgress from 'nprogress'
|
import NProgress from 'nprogress'
|
||||||
import 'nprogress/nprogress.css' // progress bar style
|
import 'nprogress/nprogress.css' // progress bar style
|
||||||
|
import { useSettingsOutsideStore } from '@/store/modules/settings'
|
||||||
|
import { useKeepAliveOutsideStore } from '@/store/modules/keepAlive'
|
||||||
|
import { useUserOutsideStore } from '@/store/modules/user'
|
||||||
|
import { useMenuOutsideStore } from '@/store/modules/menu'
|
||||||
|
|
||||||
// 固定路由
|
// 固定路由
|
||||||
const constantRoutes = [
|
const constantRoutes = [
|
||||||
@ -23,7 +26,10 @@ const constantRoutes = [
|
|||||||
name: 'dashboard',
|
name: 'dashboard',
|
||||||
component: () => import('@/views/index.vue'),
|
component: () => import('@/views/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: () => store.state.settings.dashboardTitle
|
title: () => {
|
||||||
|
const settingsOutsideStore = useSettingsOutsideStore()
|
||||||
|
return settingsOutsideStore.dashboardTitle
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -66,7 +72,7 @@ import ComponentExtendExample from './modules/component.extend.example'
|
|||||||
import PermissionExample from './modules/permission.example'
|
import PermissionExample from './modules/permission.example'
|
||||||
import MockExample from './modules/mock.example'
|
import MockExample from './modules/mock.example'
|
||||||
import ExternalLinkExample from './modules/external.link.example'
|
import ExternalLinkExample from './modules/external.link.example'
|
||||||
import VideosExample from './modules/videos.example'
|
// import VideosExample from './modules/videos.example'
|
||||||
import EcologyExample from './modules/ecology.example'
|
import EcologyExample from './modules/ecology.example'
|
||||||
import CooperationExample from './modules/cooperation.example'
|
import CooperationExample from './modules/cooperation.example'
|
||||||
|
|
||||||
@ -88,15 +94,15 @@ const asyncRoutes = [
|
|||||||
ExternalLinkExample
|
ExternalLinkExample
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
meta: {
|
// meta: {
|
||||||
title: '教程',
|
// title: '教程',
|
||||||
icon: 'sidebar-videos'
|
// icon: 'sidebar-videos'
|
||||||
},
|
// },
|
||||||
children: [
|
// children: [
|
||||||
...VideosExample
|
// ...VideosExample
|
||||||
]
|
// ]
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
meta: {
|
meta: {
|
||||||
title: '生态',
|
title: '生态',
|
||||||
@ -131,13 +137,16 @@ const router = createRouter({
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.beforeEach(async(to, from, next) => {
|
router.beforeEach(async(to, from, next) => {
|
||||||
store.state.settings.enableProgress && NProgress.start()
|
const settingsOutsideStore = useSettingsOutsideStore()
|
||||||
|
const userOutsideStore = useUserOutsideStore()
|
||||||
|
const menuOutsideStore = useMenuOutsideStore()
|
||||||
|
settingsOutsideStore.enableProgress && NProgress.start()
|
||||||
// 是否已登录
|
// 是否已登录
|
||||||
if (store.getters['user/isLogin']) {
|
if (userOutsideStore.isLogin) {
|
||||||
// 是否已根据权限动态生成并挂载路由
|
// 是否已根据权限动态生成并挂载路由
|
||||||
if (store.state.menu.isGenerate) {
|
if (menuOutsideStore.isGenerate) {
|
||||||
// 导航栏如果不是 single 模式,则需要根据 path 定位主导航的选中状态
|
// 导航栏如果不是 single 模式,则需要根据 path 定位主导航的选中状态
|
||||||
store.state.settings.menuMode !== 'single' && store.commit('menu/setHeaderActived', to.path)
|
settingsOutsideStore.menuMode !== 'single' && menuOutsideStore.setHeaderActived(to.path)
|
||||||
if (to.name) {
|
if (to.name) {
|
||||||
if (to.matched.length !== 0) {
|
if (to.matched.length !== 0) {
|
||||||
// 如果已登录状态下,进入登录页会强制跳转到控制台页面
|
// 如果已登录状态下,进入登录页会强制跳转到控制台页面
|
||||||
@ -146,11 +155,11 @@ router.beforeEach(async(to, from, next) => {
|
|||||||
name: 'dashboard',
|
name: 'dashboard',
|
||||||
replace: true
|
replace: true
|
||||||
})
|
})
|
||||||
} else if (!store.state.settings.enableDashboard && to.name == 'dashboard') {
|
} else if (!settingsOutsideStore.enableDashboard && to.name == 'dashboard') {
|
||||||
// 如果未开启控制台页面,则默认进入侧边栏导航第一个模块
|
// 如果未开启控制台页面,则默认进入侧边栏导航第一个模块
|
||||||
if (store.getters['menu/sidebarRoutes'].length > 0) {
|
if (menuOutsideStore.sidebarRoutes.length > 0) {
|
||||||
next({
|
next({
|
||||||
path: store.getters['menu/sidebarRoutesFirstDeepestPath'],
|
path: menuOutsideStore.sidebarRoutesFirstDeepestPath,
|
||||||
replace: true
|
replace: true
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -170,10 +179,10 @@ router.beforeEach(async(to, from, next) => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let accessRoutes = []
|
let accessRoutes = []
|
||||||
if (!store.state.settings.enableBackendReturnRoute) {
|
if (!settingsOutsideStore.enableBackendReturnRoute) {
|
||||||
accessRoutes = await store.dispatch('menu/generateRoutesAtFront', asyncRoutes)
|
accessRoutes = await menuOutsideStore.generateRoutesAtFront(asyncRoutes)
|
||||||
} else {
|
} else {
|
||||||
accessRoutes = await store.dispatch('menu/generateRoutesAtBack')
|
accessRoutes = await menuOutsideStore.generateRoutesAtBack()
|
||||||
}
|
}
|
||||||
accessRoutes.push(lastRoute)
|
accessRoutes.push(lastRoute)
|
||||||
let removeRoutes = []
|
let removeRoutes = []
|
||||||
@ -183,7 +192,7 @@ router.beforeEach(async(to, from, next) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
// 记录的 accessRoutes 路由数据,在登出时会使用到,不使用 router.removeRoute 是考虑配置的路由可能不一定有设置 name ,则通过调用 router.addRoute() 返回的回调进行删除
|
// 记录的 accessRoutes 路由数据,在登出时会使用到,不使用 router.removeRoute 是考虑配置的路由可能不一定有设置 name ,则通过调用 router.addRoute() 返回的回调进行删除
|
||||||
store.commit('menu/setCurrentRemoveRoutes', removeRoutes)
|
menuOutsideStore.setCurrentRemoveRoutes(removeRoutes)
|
||||||
next({ ...to, replace: true })
|
next({ ...to, replace: true })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -201,14 +210,16 @@ router.beforeEach(async(to, from, next) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.afterEach((to, from) => {
|
router.afterEach((to, from) => {
|
||||||
store.state.settings.enableProgress && NProgress.done()
|
const settingsOutsideStore = useSettingsOutsideStore()
|
||||||
|
const keepAliveOutsideStore = useKeepAliveOutsideStore()
|
||||||
|
settingsOutsideStore.enableProgress && NProgress.done()
|
||||||
// 设置页面 title
|
// 设置页面 title
|
||||||
to.meta.title && store.commit('settings/setTitle', typeof to.meta.title === 'function' ? to.meta.title() : to.meta.title)
|
to.meta.title && settingsOutsideStore.setTitle(typeof to.meta.title === 'function' ? to.meta.title() : to.meta.title)
|
||||||
// 判断当前页面是否开启缓存,如果开启,则将当前页面的 name 信息存入 keep-alive 全局状态
|
// 判断当前页面是否开启缓存,如果开启,则将当前页面的 name 信息存入 keep-alive 全局状态
|
||||||
if (to.meta.cache) {
|
if (to.meta.cache) {
|
||||||
let componentName = to.matched[to.matched.length - 1].components.default.name
|
let componentName = to.matched[to.matched.length - 1].components.default.name
|
||||||
if (componentName) {
|
if (componentName) {
|
||||||
store.commit('keepAlive/add', componentName)
|
keepAliveOutsideStore.add(componentName)
|
||||||
} else {
|
} else {
|
||||||
console.warn('该页面组件未设置组件名,会导致缓存失效,请检查')
|
console.warn('该页面组件未设置组件名,会导致缓存失效,请检查')
|
||||||
}
|
}
|
||||||
@ -220,18 +231,18 @@ router.afterEach((to, from) => {
|
|||||||
switch (typeof from.meta.cache) {
|
switch (typeof from.meta.cache) {
|
||||||
case 'string':
|
case 'string':
|
||||||
if (from.meta.cache != to.name) {
|
if (from.meta.cache != to.name) {
|
||||||
store.commit('keepAlive/remove', componentName)
|
keepAliveOutsideStore.remove(componentName)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'object':
|
case 'object':
|
||||||
if (!from.meta.cache.includes(to.name)) {
|
if (!from.meta.cache.includes(to.name)) {
|
||||||
store.commit('keepAlive/remove', componentName)
|
keepAliveOutsideStore.remove(componentName)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// 如果进入的是 reload 页面,则也将离开页面的缓存清空
|
// 如果进入的是 reload 页面,则也将离开页面的缓存清空
|
||||||
if (to.name == 'reload') {
|
if (to.name == 'reload') {
|
||||||
store.commit('keepAlive/remove', componentName)
|
keepAliveOutsideStore.remove(componentName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,13 +1,2 @@
|
|||||||
import { createStore, createLogger } from 'vuex'
|
import { createPinia } from 'pinia'
|
||||||
|
export const piniaStore = createPinia()
|
||||||
const modules = {}
|
|
||||||
const modulesContext = import.meta.globEager('./modules/*.js')
|
|
||||||
for (const path in modulesContext) {
|
|
||||||
modules[path.slice(10, -3)] = modulesContext[path].default
|
|
||||||
}
|
|
||||||
|
|
||||||
export default createStore({
|
|
||||||
modules: modules,
|
|
||||||
strict: !import.meta.env.PROD,
|
|
||||||
plugins: !import.meta.env.PROD ? [createLogger()] : []
|
|
||||||
})
|
|
||||||
|
@ -1,41 +1,41 @@
|
|||||||
const state = () => ({
|
import { defineStore } from 'pinia'
|
||||||
list: []
|
import { piniaStore } from '@/store'
|
||||||
})
|
|
||||||
|
|
||||||
const getters = {}
|
export const useKeepAliveStore = defineStore(
|
||||||
|
// 唯一ID
|
||||||
const actions = {}
|
'keepAlive',
|
||||||
|
{
|
||||||
const mutations = {
|
state: () => ({
|
||||||
add(state, name) {
|
list: []
|
||||||
if (typeof name === 'string') {
|
}),
|
||||||
!state.list.includes(name) && state.list.push(name)
|
actions: {
|
||||||
} else {
|
add(name) {
|
||||||
name.map(v => {
|
if (typeof name === 'string') {
|
||||||
v && !state.list.includes(v) && state.list.push(v)
|
!this.list.includes(name) && this.list.push(name)
|
||||||
})
|
} else {
|
||||||
|
name.map(v => {
|
||||||
|
v && !this.list.includes(v) && this.list.push(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remove(name) {
|
||||||
|
if (typeof name === 'string') {
|
||||||
|
this.list = this.list.filter(v => {
|
||||||
|
return v !== name
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.list = this.list.filter(v => {
|
||||||
|
return !name.includes(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clean() {
|
||||||
|
this.list = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
remove(state, name) {
|
|
||||||
if (typeof name === 'string') {
|
|
||||||
state.list = state.list.filter(v => {
|
|
||||||
return v != name
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
state.list = state.list.filter(v => {
|
|
||||||
return !name.includes(v)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clean(state) {
|
|
||||||
state.list = []
|
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
export default {
|
export function useKeepAliveOutsideStore() {
|
||||||
namespaced: true,
|
return useKeepAliveStore(piniaStore)
|
||||||
state,
|
|
||||||
actions,
|
|
||||||
getters,
|
|
||||||
mutations
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { piniaStore } from '@/store'
|
||||||
import path from 'path-browserify'
|
import path from 'path-browserify'
|
||||||
import { deepClone, resolveRoutePath } from '@/util'
|
import { deepClone, resolveRoutePath } from '@/util'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
|
|
||||||
|
import { useSettingsStore } from './settings'
|
||||||
|
import { useUserStore } from './user'
|
||||||
|
|
||||||
function hasPermission(permissions, route) {
|
function hasPermission(permissions, route) {
|
||||||
let isAuth = false
|
let isAuth = false
|
||||||
if (route.meta && route.meta.auth) {
|
if (route.meta && route.meta.auth) {
|
||||||
@ -137,160 +142,170 @@ function getDeepestPath(routes, rootPath = '') {
|
|||||||
return retnPath
|
return retnPath
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = () => ({
|
export const useMenuStore = defineStore(
|
||||||
isGenerate: false,
|
// 唯一ID
|
||||||
routes: [],
|
'menu',
|
||||||
defaultOpenedPaths: [],
|
{
|
||||||
headerActived: 0,
|
state: () => ({
|
||||||
currentRemoveRoutes: []
|
isGenerate: false,
|
||||||
})
|
routes: [],
|
||||||
|
defaultOpenedPaths: [],
|
||||||
const getters = {
|
headerActived: 0,
|
||||||
// 由于 getter 的结果不会被缓存,导致导航栏切换时有明显的延迟,该问题会在 Vue 3.2 版本中修复,详看 https://github.com/vuejs/vuex/pull/1883
|
currentRemoveRoutes: []
|
||||||
routes: (state, getters, rootState) => {
|
}),
|
||||||
let routes
|
getters: {
|
||||||
if (rootState.settings.menuMode === 'single') {
|
transformRoutes: state => {
|
||||||
routes = [{ children: [] }]
|
let routes
|
||||||
state.routes.map(item => {
|
const settingsStore = useSettingsStore()
|
||||||
routes[0].children.push(...item.children)
|
if (settingsStore.menuMode === 'single') {
|
||||||
})
|
routes = [{ children: [] }]
|
||||||
} else {
|
state.routes.map(item => {
|
||||||
routes = state.routes
|
routes[0].children.push(...item.children)
|
||||||
}
|
})
|
||||||
return routes
|
|
||||||
},
|
|
||||||
sidebarRoutes: (state, getters) => {
|
|
||||||
return getters.routes.length > 0 ? getters.routes[state.headerActived].children : []
|
|
||||||
},
|
|
||||||
sidebarRoutesFirstDeepestPath: (state, getters) => {
|
|
||||||
return getters.routes.length > 0 ? getDeepestPath(getters.sidebarRoutes[0]) : '/'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const actions = {
|
|
||||||
// 根据权限动态生成路由(前端生成)
|
|
||||||
generateRoutesAtFront({ rootState, dispatch, commit }, asyncRoutes) {
|
|
||||||
// eslint-disable-next-line no-async-promise-executor
|
|
||||||
return new Promise(async resolve => {
|
|
||||||
let accessedRoutes
|
|
||||||
// 如果权限功能开启,则需要对路由数据进行筛选过滤
|
|
||||||
if (rootState.settings.enablePermission) {
|
|
||||||
const permissions = await dispatch('user/getPermissions', null, { root: true })
|
|
||||||
accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions)
|
|
||||||
} else {
|
|
||||||
accessedRoutes = deepClone(asyncRoutes)
|
|
||||||
}
|
|
||||||
commit('setRoutes', accessedRoutes)
|
|
||||||
let routes = []
|
|
||||||
accessedRoutes.map(item => {
|
|
||||||
routes.push(...item.children)
|
|
||||||
})
|
|
||||||
// 将三级及以上路由数据拍平成二级
|
|
||||||
routes.map(item => {
|
|
||||||
if (item.children) {
|
|
||||||
item.children = flatAsyncRoutes(item.children, [{
|
|
||||||
path: item.path,
|
|
||||||
title: item.meta.title
|
|
||||||
}], item.path)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
commit('setDefaultOpenedPaths', routes)
|
|
||||||
resolve(routes)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 生成路由(后端获取)
|
|
||||||
generateRoutesAtBack({ rootState, dispatch, commit }) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
api.get('route/list', {
|
|
||||||
baseURL: '/mock/'
|
|
||||||
}).then(async res => {
|
|
||||||
let asyncRoutes = formatBackRoutes(res.data)
|
|
||||||
let accessedRoutes
|
|
||||||
// 如果权限功能开启,则需要对路由数据进行筛选过滤
|
|
||||||
if (rootState.settings.enablePermission) {
|
|
||||||
const permissions = await dispatch('user/getPermissions', null, { root: true })
|
|
||||||
accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions)
|
|
||||||
} else {
|
} else {
|
||||||
accessedRoutes = deepClone(asyncRoutes)
|
routes = state.routes
|
||||||
}
|
}
|
||||||
commit('setRoutes', accessedRoutes)
|
return routes
|
||||||
let routes = []
|
},
|
||||||
accessedRoutes.map(item => {
|
sidebarRoutes() {
|
||||||
routes.push(...item.children)
|
return this.transformRoutes.length > 0 ? this.transformRoutes[this.headerActived].children : []
|
||||||
|
},
|
||||||
|
sidebarRoutesFirstDeepestPath() {
|
||||||
|
return this.transformRoutes.length > 0 ? getDeepestPath(this.sidebarRoutes[0]) : '/'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
// 根据权限动态生成路由(前端生成)
|
||||||
|
generateRoutesAtFront(asyncRoutes) {
|
||||||
|
// eslint-disable-next-line no-async-promise-executor
|
||||||
|
return new Promise(async resolve => {
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
let accessedRoutes
|
||||||
|
// 如果权限功能开启,则需要对路由数据进行筛选过滤
|
||||||
|
if (settingsStore.enablePermission) {
|
||||||
|
const permissions = await userStore.getPermissions()
|
||||||
|
accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions)
|
||||||
|
} else {
|
||||||
|
accessedRoutes = deepClone(asyncRoutes)
|
||||||
|
}
|
||||||
|
// 设置 routes 数据
|
||||||
|
this.isGenerate = true
|
||||||
|
let newRoutes = deepClone(accessedRoutes)
|
||||||
|
this.routes = newRoutes.filter(item => {
|
||||||
|
return item.children.length != 0
|
||||||
|
})
|
||||||
|
// 将三级及以上路由数据拍平成二级
|
||||||
|
let routes = []
|
||||||
|
accessedRoutes.map(item => {
|
||||||
|
routes.push(...item.children)
|
||||||
|
})
|
||||||
|
routes.map(item => {
|
||||||
|
if (item.children) {
|
||||||
|
item.children = flatAsyncRoutes(item.children, [{
|
||||||
|
path: item.path,
|
||||||
|
title: item.meta.title
|
||||||
|
}], item.path)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 设置 defaultOpenedPaths 数据
|
||||||
|
let defaultOpenedPaths = []
|
||||||
|
routes.map(item => {
|
||||||
|
item.meta.defaultOpened && defaultOpenedPaths.push(item.path)
|
||||||
|
item.children && item.children.map(child => {
|
||||||
|
child.meta.defaultOpened && defaultOpenedPaths.push(path.resolve(item.path, child.path))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.defaultOpenedPaths = defaultOpenedPaths
|
||||||
|
resolve(routes)
|
||||||
})
|
})
|
||||||
// 将三级及以上路由数据拍平成二级
|
},
|
||||||
routes.map(item => {
|
// 生成路由(后端获取)
|
||||||
if (item.children) {
|
generateRoutesAtBack() {
|
||||||
item.children = flatAsyncRoutes(item.children, [{
|
return new Promise(resolve => {
|
||||||
path: item.path,
|
api.get('route/list', {
|
||||||
title: item.meta.title
|
baseURL: '/mock/'
|
||||||
}], item.path)
|
}).then(async res => {
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
let asyncRoutes = formatBackRoutes(res.data)
|
||||||
|
let accessedRoutes
|
||||||
|
// 如果权限功能开启,则需要对路由数据进行筛选过滤
|
||||||
|
if (settingsStore.enablePermission) {
|
||||||
|
const permissions = await userStore.getPermissions()
|
||||||
|
accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions)
|
||||||
|
} else {
|
||||||
|
accessedRoutes = deepClone(asyncRoutes)
|
||||||
|
}
|
||||||
|
// 设置 routes 数据
|
||||||
|
this.isGenerate = true
|
||||||
|
let newRoutes = deepClone(accessedRoutes)
|
||||||
|
this.routes = newRoutes.filter(item => {
|
||||||
|
return item.children.length != 0
|
||||||
|
})
|
||||||
|
// 将三级及以上路由数据拍平成二级
|
||||||
|
let routes = []
|
||||||
|
accessedRoutes.map(item => {
|
||||||
|
routes.push(...item.children)
|
||||||
|
})
|
||||||
|
routes.map(item => {
|
||||||
|
if (item.children) {
|
||||||
|
item.children = flatAsyncRoutes(item.children, [{
|
||||||
|
path: item.path,
|
||||||
|
title: item.meta.title
|
||||||
|
}], item.path)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 设置 defaultOpenedPaths 数据
|
||||||
|
let defaultOpenedPaths = []
|
||||||
|
routes.map(item => {
|
||||||
|
item.meta.defaultOpened && defaultOpenedPaths.push(item.path)
|
||||||
|
item.children && item.children.map(child => {
|
||||||
|
child.meta.defaultOpened && defaultOpenedPaths.push(path.resolve(item.path, child.path))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.defaultOpenedPaths = defaultOpenedPaths
|
||||||
|
resolve(routes)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
invalidRoutes() {
|
||||||
|
this.isGenerate = false
|
||||||
|
this.routes = []
|
||||||
|
this.defaultOpenedPaths = []
|
||||||
|
this.headerActived = 0
|
||||||
|
},
|
||||||
|
// 根据路由判断属于哪个头部导航
|
||||||
|
setHeaderActived(path) {
|
||||||
|
this.routes.map((item, index) => {
|
||||||
|
if (
|
||||||
|
item.children.some(r => {
|
||||||
|
return path.indexOf(r.path + '/') === 0 || path == r.path
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
this.headerActived = index
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
commit('setDefaultOpenedPaths', routes)
|
},
|
||||||
resolve(routes)
|
// 切换头部导航
|
||||||
})
|
switchHeaderActived(index) {
|
||||||
})
|
this.headerActived = index
|
||||||
}
|
},
|
||||||
}
|
// 记录 accessRoutes 路由,用于登出时删除路由
|
||||||
|
setCurrentRemoveRoutes(routes) {
|
||||||
const mutations = {
|
this.currentRemoveRoutes = routes
|
||||||
invalidRoutes(state) {
|
},
|
||||||
state.isGenerate = false
|
// 清空动态路由
|
||||||
state.routes = []
|
removeRoutes() {
|
||||||
state.defaultOpenedPaths = []
|
this.currentRemoveRoutes.forEach(removeRoute => {
|
||||||
state.headerActived = 0
|
removeRoute()
|
||||||
},
|
|
||||||
setRoutes(state, routes) {
|
|
||||||
state.isGenerate = true
|
|
||||||
let newRoutes = deepClone(routes)
|
|
||||||
state.routes = newRoutes.filter(item => {
|
|
||||||
return item.children.length != 0
|
|
||||||
})
|
|
||||||
},
|
|
||||||
setDefaultOpenedPaths(state, routes) {
|
|
||||||
let defaultOpenedPaths = []
|
|
||||||
routes.map(item => {
|
|
||||||
item.meta.defaultOpened && defaultOpenedPaths.push(item.path)
|
|
||||||
item.children && item.children.map(child => {
|
|
||||||
child.meta.defaultOpened && defaultOpenedPaths.push(path.resolve(item.path, child.path))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
state.defaultOpenedPaths = defaultOpenedPaths
|
|
||||||
},
|
|
||||||
// 根据路由判断属于哪个头部导航
|
|
||||||
setHeaderActived(state, path) {
|
|
||||||
state.routes.map((item, index) => {
|
|
||||||
if (
|
|
||||||
item.children.some(r => {
|
|
||||||
return path.indexOf(r.path + '/') === 0 || path == r.path
|
|
||||||
})
|
})
|
||||||
) {
|
this.currentRemoveRoutes = []
|
||||||
state.headerActived = index
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
},
|
|
||||||
// 切换头部导航
|
|
||||||
switchHeaderActived(state, index) {
|
|
||||||
state.headerActived = index
|
|
||||||
},
|
|
||||||
// 记录 accessRoutes 路由,用于登出时删除路由
|
|
||||||
setCurrentRemoveRoutes(state, routes) {
|
|
||||||
state.currentRemoveRoutes = routes
|
|
||||||
},
|
|
||||||
// 清空动态路由
|
|
||||||
removeRoutes(state) {
|
|
||||||
state.currentRemoveRoutes.forEach(removeRoute => {
|
|
||||||
removeRoute()
|
|
||||||
})
|
|
||||||
state.currentRemoveRoutes = []
|
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
export default {
|
export function useMenuOutsideStore() {
|
||||||
namespaced: true,
|
return useMenuStore(piniaStore)
|
||||||
state,
|
|
||||||
actions,
|
|
||||||
getters,
|
|
||||||
mutations
|
|
||||||
}
|
}
|
||||||
|
@ -1,62 +1,58 @@
|
|||||||
/**
|
import { defineStore } from 'pinia'
|
||||||
* 存放全局公用状态
|
import { piniaStore } from '@/store'
|
||||||
*/
|
|
||||||
import settings from '@/settings'
|
import settings from '@/settings'
|
||||||
|
|
||||||
const state = () => ({
|
export const useSettingsStore = defineStore(
|
||||||
...settings,
|
// 唯一ID
|
||||||
// 侧边栏是否收起(用于记录 pc 模式下最后的状态)
|
'settings',
|
||||||
sidebarCollapseLastStatus: settings.sidebarCollapse,
|
{
|
||||||
// 显示模式,支持:mobile、pc
|
state: () => ({
|
||||||
mode: 'pc',
|
...settings,
|
||||||
// 页面标题
|
// 侧边栏是否收起(用于记录 pc 模式下最后的状态)
|
||||||
title: ''
|
sidebarCollapseLastStatus: settings.sidebarCollapse,
|
||||||
})
|
// 显示模式,支持:mobile、pc
|
||||||
|
mode: 'pc',
|
||||||
const getters = {}
|
// 页面标题
|
||||||
|
title: ''
|
||||||
const actions = {}
|
}),
|
||||||
|
actions: {
|
||||||
const mutations = {
|
// 设置访问模式
|
||||||
// 设置访问模式
|
setMode(width) {
|
||||||
setMode(state, width) {
|
if (this.enableMobileAdaptation) {
|
||||||
if (state.enableMobileAdaptation) {
|
// 先判断 UA 是否为移动端设备(手机&平板)
|
||||||
// 先判断 UA 是否为移动端设备(手机&平板)
|
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
|
||||||
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
|
this.mode = 'mobile'
|
||||||
state.mode = 'mobile'
|
} else {
|
||||||
} else {
|
// 如果为桌面设备,再根据页面宽度判断是否需要切换为移动端展示
|
||||||
// 如果为桌面设备,再根据页面宽度判断是否需要切换为移动端展示
|
if (width < 992) {
|
||||||
if (width < 992) {
|
this.mode = 'mobile'
|
||||||
state.mode = 'mobile'
|
} else {
|
||||||
|
this.mode = 'pc'
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
state.mode = 'pc'
|
this.mode = 'pc'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
// 设置网页标题
|
||||||
|
setTitle(title) {
|
||||||
|
this.title = title
|
||||||
|
},
|
||||||
|
// 切换侧边栏导航展开/收起
|
||||||
|
toggleSidebarCollapse() {
|
||||||
|
this.sidebarCollapse = !this.sidebarCollapse
|
||||||
|
if (this.mode == 'pc') {
|
||||||
|
this.sidebarCollapseLastStatus = !this.sidebarCollapseLastStatus
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 更新主题配置
|
||||||
|
updateThemeSetting(data) {
|
||||||
|
Object.assign(this, data)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
state.mode = 'pc'
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
// 设置网页标题
|
|
||||||
setTitle(state, title) {
|
|
||||||
state.title = title
|
|
||||||
},
|
|
||||||
// 切换侧边栏导航展开/收起
|
|
||||||
toggleSidebarCollapse(state) {
|
|
||||||
state.sidebarCollapse = !state.sidebarCollapse
|
|
||||||
if (state.mode == 'pc') {
|
|
||||||
state.sidebarCollapseLastStatus = !state.sidebarCollapseLastStatus
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 更新主题配置
|
|
||||||
updateThemeSetting(state, data) {
|
|
||||||
Object.assign(state, data)
|
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
export default {
|
export function useSettingsOutsideStore() {
|
||||||
namespaced: true,
|
return useSettingsStore(piniaStore)
|
||||||
state,
|
|
||||||
actions,
|
|
||||||
getters,
|
|
||||||
mutations
|
|
||||||
}
|
}
|
||||||
|
@ -1,102 +1,95 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { piniaStore } from '@/store'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
|
|
||||||
const state = () => ({
|
import { useMenuStore } from './menu'
|
||||||
account: localStorage.account || '',
|
|
||||||
token: localStorage.token || '',
|
|
||||||
failure_time: localStorage.failure_time || '',
|
|
||||||
permissions: []
|
|
||||||
})
|
|
||||||
|
|
||||||
const getters = {
|
export const useUserStore = defineStore(
|
||||||
isLogin: state => {
|
// 唯一ID
|
||||||
let retn = false
|
'user',
|
||||||
if (state.token) {
|
{
|
||||||
if (new Date().getTime() < state.failure_time * 1000) {
|
state: () => ({
|
||||||
retn = true
|
account: localStorage.account || '',
|
||||||
|
token: localStorage.token || '',
|
||||||
|
failure_time: localStorage.failure_time || '',
|
||||||
|
permissions: []
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
isLogin: state => {
|
||||||
|
let retn = false
|
||||||
|
if (state.token) {
|
||||||
|
if (new Date().getTime() < state.failure_time * 1000) {
|
||||||
|
retn = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retn
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
login(data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// 通过 mock 进行登录
|
||||||
|
api.post('member/login', data, {
|
||||||
|
baseURL: '/mock/'
|
||||||
|
}).then(res => {
|
||||||
|
localStorage.setItem('account', res.data.account)
|
||||||
|
localStorage.setItem('token', res.data.token)
|
||||||
|
localStorage.setItem('failure_time', res.data.failure_time)
|
||||||
|
this.account = res.data.account
|
||||||
|
this.token = res.data.token
|
||||||
|
this.failure_time = res.data.failure_time
|
||||||
|
resolve()
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const menuStore = useMenuStore()
|
||||||
|
localStorage.removeItem('account')
|
||||||
|
localStorage.removeItem('token')
|
||||||
|
localStorage.removeItem('failure_time')
|
||||||
|
this.account = ''
|
||||||
|
this.token = ''
|
||||||
|
this.failure_time = ''
|
||||||
|
menuStore.invalidRoutes()
|
||||||
|
menuStore.removeRoutes()
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 获取我的权限
|
||||||
|
getPermissions() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
// 通过 mock 获取权限
|
||||||
|
api.get('member/permission', {
|
||||||
|
baseURL: '/mock/',
|
||||||
|
params: {
|
||||||
|
account: this.account
|
||||||
|
}
|
||||||
|
}).then(res => {
|
||||||
|
this.permissions = res.data.permissions
|
||||||
|
resolve(res.data.permissions)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
editPassword(data) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
api.post('member/edit/password', {
|
||||||
|
account: this.account,
|
||||||
|
password: data.password,
|
||||||
|
newpassword: data.newpassword
|
||||||
|
}, {
|
||||||
|
baseURL: '/mock/'
|
||||||
|
}).then(() => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return retn
|
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
const actions = {
|
export function useUserOutsideStore() {
|
||||||
login({ commit }, data) {
|
return useUserStore(piniaStore)
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// 通过 mock 进行登录
|
|
||||||
api.post('member/login', data, {
|
|
||||||
baseURL: '/mock/'
|
|
||||||
}).then(res => {
|
|
||||||
commit('setUserData', res.data)
|
|
||||||
resolve()
|
|
||||||
}).catch(error => {
|
|
||||||
reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
logout({ commit }) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
commit('removeUserData')
|
|
||||||
commit('menu/invalidRoutes', null, { root: true })
|
|
||||||
commit('menu/removeRoutes', null, { root: true })
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 获取我的权限
|
|
||||||
getPermissions({ state, commit }) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
// 通过 mock 获取权限
|
|
||||||
api.get('member/permission', {
|
|
||||||
baseURL: '/mock/',
|
|
||||||
params: {
|
|
||||||
account: state.account
|
|
||||||
}
|
|
||||||
}).then(res => {
|
|
||||||
commit('setPermissions', res.data.permissions)
|
|
||||||
resolve(res.data.permissions)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
editPassword({ state }, data) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
api.post('member/edit/password', {
|
|
||||||
account: state.account,
|
|
||||||
password: data.password,
|
|
||||||
newpassword: data.newpassword
|
|
||||||
}, {
|
|
||||||
baseURL: '/mock/'
|
|
||||||
}).then(() => {
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mutations = {
|
|
||||||
setUserData(state, data) {
|
|
||||||
localStorage.setItem('account', data.account)
|
|
||||||
localStorage.setItem('token', data.token)
|
|
||||||
localStorage.setItem('failure_time', data.failure_time)
|
|
||||||
state.account = data.account
|
|
||||||
state.token = data.token
|
|
||||||
state.failure_time = data.failure_time
|
|
||||||
},
|
|
||||||
removeUserData(state) {
|
|
||||||
localStorage.removeItem('account')
|
|
||||||
localStorage.removeItem('token')
|
|
||||||
localStorage.removeItem('failure_time')
|
|
||||||
state.account = ''
|
|
||||||
state.token = ''
|
|
||||||
state.failure_time = ''
|
|
||||||
},
|
|
||||||
setPermissions(state, permissions) {
|
|
||||||
state.permissions = permissions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
namespaced: true,
|
|
||||||
state,
|
|
||||||
actions,
|
|
||||||
getters,
|
|
||||||
mutations
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import path from 'path-browserify'
|
import path from 'path-browserify'
|
||||||
import store from '@/store'
|
import { useSettingsOutsideStore } from '@/store/modules/settings'
|
||||||
|
import { useUserOutsideStore } from '@/store/modules/user'
|
||||||
|
|
||||||
export function deepClone(target) {
|
export function deepClone(target) {
|
||||||
// 定义一个变量
|
// 定义一个变量
|
||||||
@ -35,8 +36,10 @@ export function deepClone(target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function hasPermission(permission) {
|
function hasPermission(permission) {
|
||||||
if (store.state.settings.enablePermission) {
|
const settingsOutsideStore = useSettingsOutsideStore()
|
||||||
return store.state.user.permissions.some(v => {
|
const userOutsideStore = useUserOutsideStore()
|
||||||
|
if (settingsOutsideStore.enablePermission) {
|
||||||
|
return userOutsideStore.permissions.some(v => {
|
||||||
return v === permission
|
return v === permission
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -4,37 +4,36 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
<div class="desc">抱歉,你访问的页面不存在</div>
|
<div class="desc">抱歉,你访问的页面不存在</div>
|
||||||
<el-button type="primary" @click="goBack">{{ countdown }}秒后,返回首页</el-button>
|
<el-button type="primary" @click="goBack">{{ data.countdown }} 秒后,返回首页</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { onBeforeRouteLeave } from 'vue-router'
|
||||||
beforeRouteLeave(to, from, next) {
|
const router = useRouter()
|
||||||
clearInterval(this.inter)
|
|
||||||
next()
|
const data = ref({
|
||||||
},
|
inter: null,
|
||||||
data() {
|
countdown: 5
|
||||||
return {
|
})
|
||||||
inter: null,
|
|
||||||
countdown: 5
|
onBeforeRouteLeave(() => {
|
||||||
|
clearInterval(data.value.inter)
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
data.value.inter = setInterval(() => {
|
||||||
|
data.value.countdown--
|
||||||
|
if (data.value.countdown == 0) {
|
||||||
|
clearInterval(data.value.inter)
|
||||||
|
goBack()
|
||||||
}
|
}
|
||||||
},
|
}, 1000)
|
||||||
mounted() {
|
})
|
||||||
this.inter = setInterval(() => {
|
|
||||||
this.countdown--
|
function goBack() {
|
||||||
if (this.countdown == 0) {
|
router.push('/')
|
||||||
clearInterval(this.inter)
|
|
||||||
this.goBack()
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
goBack() {
|
|
||||||
this.$router.push('/')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<page-main>
|
<page-main>
|
||||||
<div>层级:1</div>
|
<div>层级:1</div>
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<keep-alive :include="$store.state.keepAlive.list">
|
<keep-alive :include="keepAliveStore.list">
|
||||||
<component :is="Component" />
|
<component :is="Component" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</router-view>
|
</router-view>
|
||||||
@ -11,8 +11,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup name="TabExampleNested1">
|
||||||
export default {
|
import { useKeepAliveStore } from '@/store/modules/keepAlive'
|
||||||
name: 'TabExampleNested1'
|
const keepAliveStore = useKeepAliveStore()
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<page-main>
|
<page-main>
|
||||||
<div>层级:1-1</div>
|
<div>层级:1-1</div>
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<keep-alive :include="$store.state.keepAlive.list">
|
<keep-alive :include="keepAliveStore.list">
|
||||||
<component :is="Component" />
|
<component :is="Component" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</router-view>
|
</router-view>
|
||||||
@ -11,8 +11,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup name="TabExampleNested2">
|
||||||
export default {
|
import { useKeepAliveStore } from '@/store/modules/keepAlive'
|
||||||
name: 'TabExampleNested2'
|
const keepAliveStore = useKeepAliveStore()
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -17,12 +17,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { useKeepAliveStore } from '@/store/modules/keepAlive'
|
||||||
|
const keepAliveStore = useKeepAliveStore()
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'KeepAliveExamplePage',
|
name: 'KeepAliveExamplePage',
|
||||||
beforeRouteEnter(to, from, next) {
|
beforeRouteEnter(to, from, next) {
|
||||||
// 进入页面时,先将当前页面的 name 信息存入 keep-alive 全局状态
|
// 进入页面时,先将当前页面的 name 信息存入 keep-alive 全局状态
|
||||||
next(vm => {
|
next(vm => {
|
||||||
vm.$store.commit('keepAlive/add', vm.$options.name)
|
keepAliveStore.add(vm.$options.name)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
beforeRouteLeave(to, from, next) {
|
beforeRouteLeave(to, from, next) {
|
||||||
@ -31,10 +34,10 @@ export default {
|
|||||||
// 所以下面的代码意思就是,如果目标路由的 name 不是 keepAliveExampleDetail 或者 keepAliveExampleNestedDetail ,则将当前页面的 name 从 keep-alive 中删除
|
// 所以下面的代码意思就是,如果目标路由的 name 不是 keepAliveExampleDetail 或者 keepAliveExampleNestedDetail ,则将当前页面的 name 从 keep-alive 中删除
|
||||||
if (!['keepAliveExampleDetail', 'keepAliveExampleNestedDetail'].includes(to.name)) {
|
if (!['keepAliveExampleDetail', 'keepAliveExampleNestedDetail'].includes(to.name)) {
|
||||||
// 注意:上面校验的是路由的 name ,下面记录的是当前页面的 name
|
// 注意:上面校验的是路由的 name ,下面记录的是当前页面的 name
|
||||||
this.$store.commit('keepAlive/remove', 'KeepAliveExamplePage')
|
keepAliveStore.remove('KeepAliveExamplePage')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.$store.commit('keepAlive/remove', 'KeepAliveExamplePage')
|
keepAliveStore.remove('KeepAliveExamplePage')
|
||||||
}
|
}
|
||||||
next()
|
next()
|
||||||
},
|
},
|
||||||
|
@ -80,15 +80,19 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
<Copyright v-if="$store.state.settings.showCopyright" />
|
<Copyright v-if="settingsStore.showCopyright" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="Login">
|
<script setup name="Login">
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
const store = useStore()
|
|
||||||
const route = useRoute(), router = useRouter()
|
const route = useRoute(), router = useRouter()
|
||||||
|
|
||||||
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const title = import.meta.env.VITE_APP_TITLE
|
const title = import.meta.env.VITE_APP_TITLE
|
||||||
|
|
||||||
// 表单类型,login 登录,reset 重置密码
|
// 表单类型,login 登录,reset 重置密码
|
||||||
@ -146,7 +150,7 @@ function handleLogin() {
|
|||||||
proxy.$refs.loginFormRef.validate(valid => {
|
proxy.$refs.loginFormRef.validate(valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
store.dispatch('user/login', loginForm.value).then(() => {
|
userStore.login(loginForm.value).then(() => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
if (loginForm.value.remember) {
|
if (loginForm.value.remember) {
|
||||||
localStorage.setItem('login_account', loginForm.value.account)
|
localStorage.setItem('login_account', loginForm.value.account)
|
||||||
|
@ -9,21 +9,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { useUserStore } from '@/store/modules/user'
|
||||||
methods: {
|
const userStore = useUserStore()
|
||||||
getPermission() {
|
|
||||||
this.$store.dispatch('user/getPermissions').then(res => {
|
function getPermission() {
|
||||||
this.$notify({
|
userStore.getPermissions().then(res => {
|
||||||
title: '当前用户权限',
|
this.$notify({
|
||||||
dangerouslyUseHTMLString: true,
|
title: '当前用户权限',
|
||||||
message: res.map(p => `<p>${p}</p>`).join('')
|
dangerouslyUseHTMLString: true,
|
||||||
})
|
message: res.map(p => `<p>${p}</p>`).join('')
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
open(url) {
|
}
|
||||||
window.open(url, 'top')
|
function open(url) {
|
||||||
}
|
window.open(url, 'top')
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
<div>
|
<div>
|
||||||
<page-header title="权限验证" />
|
<page-header title="权限验证" />
|
||||||
<page-main>
|
<page-main>
|
||||||
<div v-if="!$store.state.settings.enablePermission">请到 seeting.js 里打开权限功能,再进入该页面查看演示</div>
|
<div v-if="!settingsStore.enablePermission">请到 seeting.js 里打开权限功能,再进入该页面查看演示</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<h3>切换帐号</h3>
|
<h3>切换帐号</h3>
|
||||||
<el-radio-group v-model="account" @change="accountChange">
|
<el-radio-group v-model="userStore.account" @change="accountChange">
|
||||||
<el-radio-button label="admin" />
|
<el-radio-button label="admin" />
|
||||||
<el-radio-button label="test" />
|
<el-radio-button label="test" />
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<h3>帐号权限</h3>
|
<h3>帐号权限</h3>
|
||||||
<div>{{ $store.state.user.permissions }}</div>
|
<div>{{ userStore.permissions }}</div>
|
||||||
<h3>鉴权组件(请对照代码查看)</h3>
|
<h3>鉴权组件(请对照代码查看)</h3>
|
||||||
<div>
|
<div>
|
||||||
<auth value="permission.browse" style="margin-bottom: 10px;">
|
<auth value="permission.browse" style="margin-bottom: 10px;">
|
||||||
@ -69,43 +69,41 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
const { proxy } = getCurrentInstance()
|
||||||
data() {
|
|
||||||
return {
|
import { useSettingsStore } from '@/store/modules/settings'
|
||||||
account: this.$store.state.user.account
|
const settingsStore = useSettingsStore()
|
||||||
}
|
import { useUserStore } from '@/store/modules/user'
|
||||||
},
|
const userStore = useUserStore()
|
||||||
methods: {
|
|
||||||
accountChange(val) {
|
function accountChange(val) {
|
||||||
this.$loading({
|
proxy.$loading({
|
||||||
lock: true,
|
lock: true,
|
||||||
text: '帐号切换中',
|
text: '帐号切换中',
|
||||||
background: 'rgba(0, 0, 0, 0.7)'
|
background: 'rgba(0, 0, 0, 0.7)'
|
||||||
})
|
})
|
||||||
this.$store.dispatch('user/login', {
|
userStore.login({
|
||||||
account: val,
|
account: val,
|
||||||
password: ''
|
password: ''
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
location.reload()
|
location.reload()
|
||||||
}, 1000)
|
}, 1000)
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
permissionCheck(permissions) {
|
function permissionCheck(permissions) {
|
||||||
if (this.$auth(permissions)) {
|
if (proxy.$auth(permissions)) {
|
||||||
this.$message.success('校验通过')
|
proxy.$message.success('校验通过')
|
||||||
} else {
|
} else {
|
||||||
this.$message.error('校验不通过')
|
proxy.$message.error('校验不通过')
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
permissionCheck2(permissions) {
|
function permissionCheck2(permissions) {
|
||||||
if (this.$authAll(permissions)) {
|
if (proxy.$authAll(permissions)) {
|
||||||
this.$message.success('校验通过')
|
proxy.$message.success('校验通过')
|
||||||
} else {
|
} else {
|
||||||
this.$message.error('校验不通过')
|
proxy.$message.error('校验不通过')
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -25,10 +25,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="PersonalEditPassword">
|
<script setup name="PersonalEditPassword">
|
||||||
const store = useStore()
|
|
||||||
const route = useRoute(), router = useRouter()
|
const route = useRoute(), router = useRouter()
|
||||||
const { proxy } = getCurrentInstance()
|
const { proxy } = getCurrentInstance()
|
||||||
|
|
||||||
|
import { useUserStore } from '@/store/modules/user'
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const validatePassword = (rule, value, callback) => {
|
const validatePassword = (rule, value, callback) => {
|
||||||
if (value !== form.value.newpassword) {
|
if (value !== form.value.newpassword) {
|
||||||
callback(new Error('请确认新密码'))
|
callback(new Error('请确认新密码'))
|
||||||
@ -60,12 +62,12 @@ const rules = ref({
|
|||||||
function onSubmit() {
|
function onSubmit() {
|
||||||
proxy.$refs['formRef'].validate(valid => {
|
proxy.$refs['formRef'].validate(valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
store.dispatch('user/editPassword', form.value).then(() => {
|
userStore.editPassword(form.value).then(() => {
|
||||||
proxy.$message({
|
proxy.$message({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: '模拟修改成功,请重新登录'
|
message: '模拟修改成功,请重新登录'
|
||||||
})
|
})
|
||||||
store.dispatch('user/logout').then(() => {
|
userStore.logout().then(() => {
|
||||||
router.push({
|
router.push({
|
||||||
name: 'login',
|
name: 'login',
|
||||||
query: {
|
query: {
|
||||||
|
@ -4,10 +4,7 @@ export default function createAutoImport() {
|
|||||||
return autoImport({
|
return autoImport({
|
||||||
imports: [
|
imports: [
|
||||||
'vue',
|
'vue',
|
||||||
'vue-router',
|
'vue-router'
|
||||||
{
|
|
||||||
'vuex': ['useStore']
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
dts: false
|
dts: false
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user