优化目录结构,以及插件

This commit is contained in:
Gary Fu
2023-12-23 19:45:40 +08:00
parent 5adcc63839
commit bb40594f2a
22 changed files with 1237 additions and 805 deletions

1469
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
<script setup>
import { useGlobalConfigStore } from '@/stores/globalConfigStore'
import { useGlobalConfigStore } from '@/stores/GlobalConfigStore'
import { $changeLocale, elementLocale } from '@/messages'
const globalConfigStore = useGlobalConfigStore()

View File

@@ -17,3 +17,24 @@ html, body, #app, .index-container {
.index-aside .el-menu {
border-right: 0 none;
}
.el-menu-left:not(.el-menu--collapse) {
width: 250px;
min-height: 400px;
}
/**
* slide-fade 动画
*/
.slide-fade-enter-active {
transition: all 0.3s ease-out;
}
.slide-fade-leave-active {
transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter-from,
.slide-fade-leave-to {
transform: translateX(20px);
opacity: 0;
}

View File

@@ -2,13 +2,13 @@
defineProps({
icon: {
type: String,
required: true
required: false
}
})
</script>
<template>
<el-icon>
<el-icon v-if="icon">
<component
:is="icon"
/>

View File

@@ -0,0 +1,34 @@
<script setup>
defineProps({
menuItem: {
type: Object,
required: true
},
index: {
type: String,
required: false
}
})
</script>
<template>
<el-menu-item
:route="menuItem.route"
v-bind="menuItem.attrs"
:index="index"
@click="menuItem.click&&menuItem.click()"
>
<template #title>
<common-icon
:icon="menuItem.icon"
/>
<span v-if="menuItem.labelKey||menuItem.label">
{{ menuItem.labelKey?$t(menuItem.labelKey):menuItem.label }}
</span>
</template>
</el-menu-item>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,72 @@
<script setup>
import { computed } from 'vue'
const props = defineProps({
menus: {
type: Array,
required: true
},
collapse: {
type: Boolean
}
})
const menuItems = computed(() => {
return filterMenus(props.menus)
})
const calcIcon = menuItem => {
if (menuItem.iconIf) {
menuItem.icon = menuItem.iconIf(menuItem)
}
return menuItem.icon
}
const filterMenus = menus => menus.filter(menu => !menu.disabled)
.map(menu => {
menu.icon = calcIcon(menu)
if (menu.children && menu.children.length) {
menu.children = filterMenus(menu.children)
}
return menu
})
</script>
<template>
<el-menu
v-bind="$attrs"
router
:collapse="collapse"
>
<template v-for="(menuItem, index) in menuItems">
<el-sub-menu
v-if="menuItem.children && menuItem.children.length"
:key="menuItem.index||index"
:index="menuItem.index"
v-bind="menuItem.attrs"
>
<template #title>
<common-icon
:icon="menuItem.icon"
/>
<span v-if="menuItem.labelKey||menuItem.label">
{{ menuItem.labelKey?$t(menuItem.labelKey):menuItem.label }}
</span>
</template>
<common-menu-item
v-for="(childMenu, childIdx) in menuItem.children"
:key="childMenu.index||childIdx"
:index="childMenu.index"
:menu-item="childMenu"
/>
</el-sub-menu>
<common-menu-item
v-else
:key="menuItem.index||index"
:index="menuItem.index"
:menu-item="menuItem"
/>
</template>
</el-menu>
</template>
<style scoped>
</style>

View File

@@ -1,14 +1,19 @@
import { defineAsyncComponent } from 'vue'
import CommonIcon from '@/components/common-icon/index.vue'
import CommonInput from '@/components/common-input/index.vue'
import CommonMenu from '@/components/common-menu/index.vue'
import CommonMenuItem from '@/components/common-menu-item/index.vue'
/**
* 自定义通用组件自动注册
*/
export default {
install (app) {
const components = import.meta.glob('./*/index.vue')
for (const [filePath, componentFn] of Object.entries(components)) {
const compName = filePath.split('/')[1]
app.component(compName, defineAsyncComponent(componentFn))
}
/**
* @param Vue {import('vue').App} IDEIDEA插件似乎不能正常提示vue3组件注册参数名写成Vue才能提示
*/
install (Vue) {
Vue.component('CommonIcon', CommonIcon)
Vue.component('CommonInput', CommonInput)
Vue.component('CommonMenu', CommonMenu)
Vue.component('CommonMenuItem', CommonMenuItem)
}
}

View File

@@ -0,0 +1,19 @@
/**
* 全局布局模式
* @readonly
* @enum {string}
*/
export const GlobalLayoutMode = {
LEFT: 'left',
TOP: 'top'
}
/**
* 全局语言
* @readonly
* @enum {string}
*/
export const GlobalLocales = {
CN: 'zh-CN',
EN: 'en-US'
}

View File

@@ -1,20 +1,21 @@
<script setup>
defineProps({
collapseLeft: {
type: Boolean
}
})
import { useGlobalConfigStore } from '@/stores/GlobalConfigStore'
const globalConfigStore = useGlobalConfigStore()
</script>
<template>
<el-scrollbar>
<el-menu
:collapse="collapseLeft"
class="el-menu-left"
:collapse="globalConfigStore.isCollapseLeft"
:default-openeds="['1']"
>
<el-menu-item index="0">
<el-menu-item
index="0"
route="/"
>
<span>Simple Element Plus</span>
<div v-if="collapseLeft">
<div v-if="globalConfigStore.isCollapseLeft">
SEP
</div>
</el-menu-item>

View File

@@ -1,102 +1,15 @@
<script setup>
import { useBaseTopMenus } from '@/services/GlobalService'
import { Check } from '@element-plus/icons-vue'
import { useGlobalConfigStore } from '@/stores/globalConfigStore'
const topMenus = useBaseTopMenus()
const globalConfigStore = useGlobalConfigStore()
const emit = defineEmits(['update:collapseLeft'])
const updateCollapseLeft = () => {
globalConfigStore.collapseLeft()
emit('update:collapseLeft', globalConfigStore.isCollapseLeft)
}
defineProps({
collapseLeft: {
type: Boolean
}
})
</script>
<template>
<el-menu
<common-menu
router
mode="horizontal"
:ellipsis="false"
>
<el-menu-item @click="updateCollapseLeft">
<template #title>
<el-icon v-if="!globalConfigStore.isCollapseLeft">
<Fold />
</el-icon>
<el-icon v-if="globalConfigStore.isCollapseLeft">
<Expand />
</el-icon>
<span>&nbsp;</span>
</template>
</el-menu-item>
<div class="flex-grow" />
<el-sub-menu index="1">
<template #title>
<span>{{ $t('common.label.language') }}</span>
</template>
<el-menu-item
index="1-1"
@click="$changeLocale('zh-CN')"
>
<common-icon
v-if="globalConfigStore.currentLocale==='zh-CN'"
icon="check"
:menus="topMenus"
/>
{{ $t('common.label.langCn') }}
</el-menu-item>
<el-menu-item
index="1-2"
@click="$changeLocale('en-US')"
>
<common-icon
v-if="globalConfigStore.currentLocale==='en-US'"
icon="check"
/>
{{ $t('common.label.langEn') }}
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
{{ $t('common.label.theme') }}
</template>
<el-menu-item
index="2-1"
@click="globalConfigStore.changeTheme(false)"
>
<el-icon v-if="!globalConfigStore.isDarkTheme">
<Check />
</el-icon>
{{ $t('common.label.themeDefault') }}
</el-menu-item>
<el-menu-item
index="2-2"
@click="globalConfigStore.changeTheme(true)"
>
<el-icon v-if="globalConfigStore.isDarkTheme">
<Check />
</el-icon>
{{ $t('common.label.themeDark') }}
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="3">
<template #title>
{{ $t('common.label.personalCenter') }}
</template>
<el-menu-item index="3-1">
{{ $t('common.label.personalInfo') }}
</el-menu-item>
<el-menu-item index="3-2">
{{ $t('common.label.about') }}
</el-menu-item>
<el-menu-item index="3-3">
{{ $t('common.label.logout') }}
</el-menu-item>
</el-sub-menu>
</el-menu>
</template>
<style scoped>

View File

@@ -8,7 +8,7 @@ import messages from '@/messages'
import commons from '@/components'
import App from '@/App.vue'
import router from '@/router'
import router from '@/route/routes'
import './assets/main.css'

View File

@@ -6,7 +6,8 @@ import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import en from 'element-plus/dist/locale/en.mjs'
import 'dayjs/locale/zh-cn'
import dayjs from 'dayjs'
import { useGlobalConfigStore } from '@/stores/globalConfigStore'
import { useGlobalConfigStore } from '@/stores/GlobalConfigStore'
import { GlobalLocales } from '@/consts/GlobalConstants'
const DEFAULT_LOCALE = 'zh-CN'
dayjs.locale(DEFAULT_LOCALE) // dayjs的语言配置
@@ -25,16 +26,36 @@ export const elementLocale = ref({ // 用于element-plus
localeData: zhCn
})
export const $changeLocale = function (locale) {
export const changeMessages = locale => {
i18n.global.locale.value = locale
elementLocale.value.localeData = locale === DEFAULT_LOCALE ? zhCn : en
dayjs.locale(locale.toLowerCase())
}
export const $changeLocale = locale => {
useGlobalConfigStore().changeLocale(locale)
}
/**
* @param cn
* @param en
* @param {boolean} replaceEmpty 为空是否用不为空的数据代替
* @returns {*}
*/
export const $i18nMsg = function (cn, en, replaceEmpty) {
const { currentLocale } = useGlobalConfigStore()
console.log(currentLocale)
if (currentLocale === GlobalLocales.CN) {
return replaceEmpty ? (cn || en) : cn
}
return replaceEmpty ? (en || cn) : en
}
export default {
install (app) {
app.use(i18n)
app.config.globalProperties.$changeLocale = $changeLocale
Object.assign(app.config.globalProperties, {
$changeLocale,
$i18nMsg
})
}
}

30
src/route/routes.js Normal file
View File

@@ -0,0 +1,30 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '@/views/HomeView.vue'
const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
children: [{
path: 'about',
name: 'about',
component: () => import('@/views/AboutView.vue')
}, {
path: 'personal',
name: 'personal',
component: () => import('@/views/PersonalInfo.vue')
},
{
path: '/:pathMatch(.*)*',
name: 'notFound',
component: () => import('@/views/404.vue')
}]
}
],
scrollBehavior: () => ({ left: 0, top: 0 })
})
export default router

View File

@@ -1,23 +0,0 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue')
}
]
})
export default router

View File

@@ -0,0 +1,63 @@
import { GlobalLocales } from '@/consts/GlobalConstants'
import { useGlobalConfigStore } from '@/stores/GlobalConfigStore'
import { ref } from 'vue'
export const useBaseTopMenus = () => {
const globalConfigStore = useGlobalConfigStore()
return ref([
{
iconIf: () => globalConfigStore.isCollapseLeft ? 'expand' : 'fold',
click: globalConfigStore.collapseLeft
},
{
labelKey: 'common.label.language',
index: 'language',
children: [
{
iconIf: () => GlobalLocales.CN === globalConfigStore.currentLocale ? 'check' : '',
labelKey: 'common.label.langCn',
click: () => globalConfigStore.changeLocale(GlobalLocales.CN)
},
{
iconIf: () => GlobalLocales.EN === globalConfigStore.currentLocale ? 'check' : '',
labelKey: 'common.label.langEn',
click: () => globalConfigStore.changeLocale(GlobalLocales.EN)
}
]
},
{
labelKey: 'common.label.theme',
index: 'theme',
children: [
{
iconIf: () => !globalConfigStore.isDarkTheme ? 'check' : '',
labelKey: 'common.label.themeDefault',
click: () => globalConfigStore.changeTheme(false)
},
{
iconIf: () => globalConfigStore.isDarkTheme ? 'check' : '',
labelKey: 'common.label.themeDark',
click: () => globalConfigStore.changeTheme(true)
}
]
},
{
labelKey: 'common.label.personalCenter',
index: 'personal',
children: [
{
labelKey: 'common.label.personalInfo',
index: '/personal'
},
{
labelKey: 'common.label.about',
index: '/about'
},
{
labelKey: 'common.label.logout',
index: '/logout'
}
]
}
])
}

View File

@@ -0,0 +1,41 @@
import { ref } from 'vue'
import { defineStore } from 'pinia'
import { useDark } from '@vueuse/core'
import { GlobalLayoutMode, GlobalLocales } from '@/consts/GlobalConstants'
import { changeMessages } from '@/messages'
export const useGlobalConfigStore = defineStore('globalConfig', () => {
const currentLocale = ref(GlobalLocales.CN)
const isDarkTheme = useDark()
const isCollapseLeft = ref(false)
const layoutMode = ref(GlobalLayoutMode.LEFT)
return {
currentLocale,
isDarkTheme,
isCollapseLeft,
layoutMode,
changeLocale (locale) {
if (Object.values(GlobalLocales).includes(locale)) {
currentLocale.value = locale
} else {
throw new Error(`Locale ${locale} is not supported.`)
}
changeMessages(locale)
},
changeTheme (dark) {
isDarkTheme.value = dark
},
collapseLeft () {
isCollapseLeft.value = !isCollapseLeft.value
},
changeLayout (layout) {
if (Object.values(GlobalLayoutMode).includes(layout)) {
layoutMode.value = layout
} else {
throw new Error(`Layout ${layout} is not supported.`)
}
}
}
}, {
persist: true
})

View File

@@ -1,25 +0,0 @@
import { ref } from 'vue'
import { defineStore } from 'pinia'
import { useDark } from '@vueuse/core'
export const useGlobalConfigStore = defineStore('globalConfig', () => {
const currentLocale = ref('zh-CN')
const isDarkTheme = useDark()
const isCollapseLeft = ref(false)
return {
currentLocale,
isDarkTheme,
isCollapseLeft,
changeLocale (locale) {
currentLocale.value = locale
},
changeTheme (dark) {
isDarkTheme.value = dark
},
collapseLeft () {
isCollapseLeft.value = !isCollapseLeft.value
}
}
}, {
persist: true
})

View File

@@ -1,6 +1,6 @@
import { defineStore, createPinia } from 'pinia'
import piniaPluginPersistedState from 'pinia-plugin-persistedstate'
import { useGlobalConfigStore } from '@/stores/globalConfigStore'
import { useGlobalConfigStore } from '@/stores/GlobalConfigStore'
export const useStore = defineStore('store', () => {
return {

16
src/views/404.vue Normal file
View File

@@ -0,0 +1,16 @@
<script setup>
</script>
<template>
<div>
<h2>404</h2>
<p>
<strong>Page Not Found</strong>
</p>
</div>
</template>
<style scoped>
</style>

View File

@@ -1,15 +1,8 @@
<template>
<div class="about">
<h1>This is an about page</h1>
<div>
<strong>This is an about page</strong>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>

View File

@@ -1,17 +1,6 @@
<script setup>
import LeftMenu from '@/layout/LeftMenu.vue'
import TopNav from '@/layout/TopNav.vue'
import { ref } from 'vue'
import dayjs from 'dayjs'
const collapseLeft = ref(false)
const value1 = ref(new Date())
const testDay = function () {
return dayjs.weekdays()
}
</script>
<template>
@@ -20,20 +9,24 @@ const testDay = function () {
class="index-aside"
width="auto"
>
<left-menu v-model:collapse-left="collapseLeft" />
<left-menu />
</el-aside>
<el-container>
<el-header>
<top-nav v-model:collapse-left="collapseLeft" />
<top-nav />
</el-header>
<el-main>
<el-date-picker
v-model="value1"
type="date"
placeholder="Pick a day"
<router-view v-slot="{ Component, route }">
<transition
name="slide-fade"
mode="out-in"
>
<component
:is="Component"
:key="route.fullPath"
/>
{{ testDay() }}
{{ $i18n.locale }}
</transition>
</router-view>
</el-main>
</el-container>
</el-container>

View File

@@ -0,0 +1,13 @@
<script setup>
</script>
<template>
<div>
<strong>{{ $t('common.label.personalInfo') }}</strong>
</div>
</template>
<style scoped>
</style>