Skip to content

Commit

Permalink
Merge pull request #199 from little3201/develop
Browse files Browse the repository at this point in the history
1. 抽取国际化配置为组件、补充国际化内容;2. 路由动态加载;3.图标更换为mdi; 4. dark模式背景设置
  • Loading branch information
little3201 authored Aug 13, 2024
2 parents 2106ca9 + 76f7ae5 commit dbf962e
Show file tree
Hide file tree
Showing 44 changed files with 1,572 additions and 615 deletions.
Binary file modified .yarn/install-state.gz
Binary file not shown.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.24.0",
"msw": "^2.3.0",
"msw": "^2.3.5",
"pinia-plugin-persistedstate": "^3.2.1",
"postcss": "^8.4.38",
"typescript": "^5.4.4"
},
Expand Down
2 changes: 1 addition & 1 deletion public/mockServiceWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* - Please do NOT serve this file on production.
*/

const PACKAGE_VERSION = '2.3.0'
const PACKAGE_VERSION = '2.3.5'
const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set()
Expand Down
12 changes: 6 additions & 6 deletions quasar.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,21 @@ module.exports = configure(function (ctx) {
// https://github.com/quasarframework/quasar/tree/dev/extras
extras: [
// 'ionicons-v4',
// 'mdi-v5',
'mdi-v7',
// 'fontawesome-v6',
// 'eva-icons',
// 'themify',
// 'line-awesome',
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!

'roboto-font', // optional, you are not bound to it
'material-symbols-rounded' // optional, you are not bound to it
'roboto-font' // optional, you are not bound to it
// 'material-symbols-rounded' // optional, you are not bound to it
],

// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
build: {
target: {
browser: ['es2022', 'edge88', 'firefox115', 'chrome115', 'safari14'],
browser: ['es2022', 'edge115', 'firefox115', 'chrome115', 'safari14'],
node: 'node20'
},

Expand Down Expand Up @@ -125,7 +125,7 @@ module.exports = configure(function (ctx) {
}
},

iconSet: 'material-symbols-rounded', // Quasar icon set
iconSet: 'mdi-v7', // Quasar icon set
lang: 'en-US', // Quasar language pack

// For special cases outside of where the auto-import strategy can have an impact
Expand All @@ -136,7 +136,7 @@ module.exports = configure(function (ctx) {
// directives: [],

// Quasar plugins
plugins: ['Notify', 'SessionStorage']
plugins: ['Notify', 'Cookies']
},

// animations: 'all', // --- includes all animations
Expand Down
5 changes: 5 additions & 0 deletions src/api/privileges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { api } from 'boot/axios'

export const retrievePrivilegeTree = (username: string) => {
return api.get(`/privileges/${username}/tree`)
}
8 changes: 6 additions & 2 deletions src/boot/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { boot } from 'quasar/wrappers'
import { createI18n } from 'vue-i18n'
import messages from 'src/i18n'
import { messages } from 'src/i18n'

import { useLocaleStore } from 'stores/locale-store'

const localeStore = useLocaleStore()

export default boot(({ app }) => {
const i18n = createI18n({
locale: 'en-US',
legacy: false,
locale: localeStore.lang || 'zh-CN',
messages
})

Expand Down
66 changes: 63 additions & 3 deletions src/boot/router.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
import { boot } from 'quasar/wrappers'
import { useUserStore } from 'stores/user-store'
import { Cookies } from 'quasar'
import type { RouteRecordRaw } from 'vue-router'
import type { PrivilegeTreeNode } from 'src/models'

export default boot(({ router, store }) => {
router.beforeEach((to, from, next) => {
// Now you need to add your authentication logic here, like calling an API endpoint
const userStore = useUserStore(store)
if (userStore.getUsername) {
if (userStore.user?.username && Cookies.get('logged_in')) {
if (to.path === '/login') {
next('/')
next({ path: '/' })
} else {
next()
// 获取权限,注册路由表
if (to.path !== '/' && !userStore.routes.length) {
const routes = generateRoutes(userStore.privileges as PrivilegeTreeNode[])

// 动态添加可访问路由表
userStore.updateRoutes(routes)
routes.forEach((route) => {
router.addRoute(route as RouteRecordRaw)
})
// 捕获所有未匹配的路径,放在配置的末尾
router.addRoute({
path: '/:cacheAll(.*)*',
name: 'ErrorNotFound',
component: () => import('src/pages/ErrorNotFound.vue')
})

const redirectPath = from.query.redirect || to.path
const redirect = decodeURIComponent(redirectPath as string)
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect }
next(nextData)
} else {
next()
}
}
} else {
if (to.path === '/login') {
Expand All @@ -20,3 +45,38 @@ export default boot(({ router, store }) => {
}
})
})

// 生成路由
const MainLayout = () => import('src/layouts/MainLayout.vue')
const BlankLayout = () => import('src/layouts/BlankLayout.vue')

const modules = import.meta.glob('../pages/**/*.{vue,tsx}')

export const generateRoutes = (routes: PrivilegeTreeNode[]): RouteRecordRaw[] => {
const res: RouteRecordRaw[] = []
for (const route of routes) {
const data: RouteRecordRaw = {
path: route.path as string,
name: route.name,
redirect: route.redirect,
component: null,
children: []
}
if (route.component) {
const comModule = modules[`../${route.component}.vue`]
const component = route.component as string
if (comModule) {
// 动态加载路由文件
data.component = comModule
} else if (component.includes('#')) {
data.component = component === '#' ? MainLayout : BlankLayout
}
}
// recursive child routes
if (route.children) {
data.children = generateRoutes(route.children)
}
res.push(data)
}
return res
}
26 changes: 15 additions & 11 deletions src/components/EssentialLink.vue
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
<template>
<q-item v-ripple exact :to="link">
<q-item clickable v-ripple exact :to="pathResolve(parentPath, path)">
<q-item-section v-if="icon" avatar>
<q-icon :name="icon" />
</q-item-section>

<q-item-section>
<q-item-label>{{ $t(title) }}</q-item-label>
<q-item-label>{{ $t(name) }}</q-item-label>
</q-item-section>
</q-item>
</template>

<script setup lang="ts">
export interface EssentialLinkProps {
title: string;
link?: string;
icon?: string;
}
withDefaults(defineProps<EssentialLinkProps>(), {
title: '',
link: '#',
icon: ''
import { pathResolve } from 'src/utils'
withDefaults(defineProps<{
name: string
path: string
icon: string
parentPath?: string
}>(), {
name: '',
path: '#',
icon: '',
parentPath: ''
})
</script>
27 changes: 27 additions & 0 deletions src/components/EssentialList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<template>
<template v-for="link in essentialLinks" :key="link.name">
<q-expansion-item v-if="link.children" :icon="link.icon" :label="$t(link.name)">
<q-card>
<q-card-section>
<!-- children -->
<EssentialList :essentialLinks="link.children" :parent-path="pathResolve(parentPath, link.path)" />
</q-card-section>
</q-card>
</q-expansion-item>
<!-- single item -->
<EssentialLink v-else v-bind="{ ...link, parentPath }" />
</template>
</template>

<script setup lang="ts">
import EssentialLink from 'components/EssentialLink.vue'
import type { PrivilegeTreeNode } from 'src/models'
import { pathResolve } from 'src/utils'
withDefaults(defineProps<{
essentialLinks: PrivilegeTreeNode[]
parentPath?: string
}>(), {
parentPath: ''
})
</script>
34 changes: 34 additions & 0 deletions src/components/LanguageSelector.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<q-btn title="language" icon="mdi-translate" round flat dense>
<q-menu>
<q-list dense separator>
<q-item clickable v-close-popup v-for="option in localeOptions" :key="option.value"
:active="locale === option.value" @click="changeLocale(option.value)">
<q-item-section>{{ option.label }}</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { localeOptions } from 'src/i18n'
import { useLocaleStore } from 'stores/locale-store'
const { locale } = useI18n({ useScope: 'global' })
const localeStore = useLocaleStore()
function changeLocale(lang: string = 'en-US') {
locale.value = lang
localeStore.changeLang(lang)
changeHtmlLang(lang)
}
function changeHtmlLang(lang: string) {
const htmlElement = document.querySelector('html')
if (htmlElement) {
htmlElement.setAttribute('lang', lang)
}
}
</script>
14 changes: 14 additions & 0 deletions src/components/ThemeToogle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<template>
<q-toggle size="sm" v-model="$q.dark.isActive" icon="mdi-weather-night" unchecked-icon="mdi-white-balance-sunny"
:color="$q.dark.isActive ? 'black' : ''" @update:model-value="handleChange" />
</template>

<script setup lang="ts">
import { useQuasar } from 'quasar'
const $q = useQuasar()
function handleChange(value: boolean) {
$q.dark.set(value)
}
</script>
10 changes: 7 additions & 3 deletions src/css/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@
}

.bg-negative-gradient {
background: radial-gradient(circle at center bottom, #e05969, $negative);
background: radial-gradient(circle at center left, $red-6, $negative);
}

.bg-positive-gradient {
background: radial-gradient(circle at center right, #26A69A, $positive);
background: radial-gradient(circle at center right, $light-green, $positive);
}

.bg-warning-gradient {
background: radial-gradient(circle at bottom left, #cf9900, $warning);
background: radial-gradient(circle at center top, $amber, $warning);
}

.rounded-full {
border-radius: 50%;
}

.bg-dark-page {
background-color: var(--q-dark-page);
}
Loading

0 comments on commit dbf962e

Please sign in to comment.