Compare commits
2 Commits
Author | SHA1 | Date |
---|---|---|
|
c7f12cc4cf | |
|
093e176cf5 |
|
@ -73,6 +73,8 @@ export default defineConfigWithVueTs(
|
||||||
|
|
||||||
// allow debugger during development only
|
// allow debugger during development only
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||||
|
// 关闭驼峰命名规则
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -55,9 +55,11 @@
|
||||||
"eslint": "^9.14.0",
|
"eslint": "^9.14.0",
|
||||||
"eslint-plugin-vue": "^9.30.0",
|
"eslint-plugin-vue": "^9.30.0",
|
||||||
"globals": "^15.12.0",
|
"globals": "^15.12.0",
|
||||||
|
"mockjs": "^1.1.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"typescript": "~5.5.3",
|
"typescript": "~5.5.3",
|
||||||
"vite-plugin-checker": "^0.8.0",
|
"vite-plugin-checker": "^0.8.0",
|
||||||
|
"vite-plugin-mock": "^3.0.2",
|
||||||
"vue-tsc": "^2.0.29"
|
"vue-tsc": "^2.0.29"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 859 B After Width: | Height: | Size: 859 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
|
@ -12,7 +12,7 @@ export default defineConfig((ctx) => {
|
||||||
// app boot file (/src/boot)
|
// app boot file (/src/boot)
|
||||||
// --> boot files are part of "main.js"
|
// --> boot files are part of "main.js"
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
||||||
boot: ['i18n', 'axios'],
|
boot: ['i18n', 'axios', 'mock'],
|
||||||
|
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#css
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#css
|
||||||
css: ['app.scss'],
|
css: ['app.scss'],
|
||||||
|
@ -93,13 +93,29 @@ export default defineConfig((ctx) => {
|
||||||
},
|
},
|
||||||
{ server: false },
|
{ server: false },
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// 配置 vite-plugin-mock 插件
|
||||||
|
[
|
||||||
|
'vite-plugin-mock',
|
||||||
|
{
|
||||||
|
mockPath: './src/mock', // 指定 mock 文件目录
|
||||||
|
localEnabled: true, // 开发环境启用 mock
|
||||||
|
prodEnabled: false, // 生产环境禁用 mock
|
||||||
|
injectCode: `
|
||||||
|
import { setupProdMockServer } from './mockProdServer';
|
||||||
|
setupProdMockServer();
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#devserver
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#devserver
|
||||||
devServer: {
|
devServer: {
|
||||||
// https: true,
|
// https: true,
|
||||||
open: true, // opens browser window automatically
|
open: false, // opens browser window automatically
|
||||||
|
allowedHosts: ['fire.lexcubia.com'],
|
||||||
|
port: 22222,
|
||||||
},
|
},
|
||||||
|
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#framework
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#framework
|
||||||
|
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
@ -21,7 +21,7 @@ declare module 'vue-i18n' {
|
||||||
}
|
}
|
||||||
/* eslint-enable @typescript-eslint/no-empty-object-type */
|
/* eslint-enable @typescript-eslint/no-empty-object-type */
|
||||||
|
|
||||||
import { useSettingStore } from 'src/stores/setting-store' // 导入 setting-store
|
import { useSettingStore } from 'src/stores/setting' // 导入 setting-store
|
||||||
|
|
||||||
export default defineBoot(({ app }) => {
|
export default defineBoot(({ app }) => {
|
||||||
const settingStore = useSettingStore() // 初始化 setting-store
|
const settingStore = useSettingStore() // 初始化 setting-store
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
// mock--start
|
||||||
|
import { setupProdMockServer } from 'mock/mockProdServer'
|
||||||
|
// if (process.env.NODE_ENV === 'production') {
|
||||||
|
setupProdMockServer()
|
||||||
|
// }
|
||||||
|
// mock--end
|
|
@ -0,0 +1,33 @@
|
||||||
|
export const ThemeStyleQuasar = {
|
||||||
|
primary: '#1976D2',
|
||||||
|
secondary: '#26A69A',
|
||||||
|
accent: '#9C27B0',
|
||||||
|
positive: '#21BA45',
|
||||||
|
negative: '#C10015',
|
||||||
|
info: '#31CCEC',
|
||||||
|
warning: '#F2C037',
|
||||||
|
light: '#FFFFFF',
|
||||||
|
dark: '#1D1D1D',
|
||||||
|
}
|
||||||
|
export const ThemeStyleElement = {
|
||||||
|
primary: '#409EFF',
|
||||||
|
secondary: '#26A69A',
|
||||||
|
accent: '#9C27B0',
|
||||||
|
positive: '#67C23A',
|
||||||
|
negative: '#F56C6C',
|
||||||
|
info: '#8896b3',
|
||||||
|
warning: '#e6a23c',
|
||||||
|
light: '#FFFFFF',
|
||||||
|
dark: '#1D1D1D',
|
||||||
|
}
|
||||||
|
export const ThemeStyleAnt = {
|
||||||
|
primary: '#1677ff',
|
||||||
|
secondary: '#26A69A',
|
||||||
|
accent: '#9C27B0',
|
||||||
|
positive: '#52c41a',
|
||||||
|
negative: '#f5222d',
|
||||||
|
info: '#fafafa',
|
||||||
|
warning: '#faad14',
|
||||||
|
light: '#FFFFFF',
|
||||||
|
dark: '#141414',
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<template>
|
||||||
|
<q-layout>
|
||||||
|
<q-page-container>
|
||||||
|
<router-view />
|
||||||
|
</q-page-container>
|
||||||
|
</q-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup></script>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<q-layout>
|
||||||
|
<q-page-container> 这里是登录页面(TODO) </q-page-container>
|
||||||
|
</q-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup></script>
|
|
@ -1,60 +0,0 @@
|
||||||
<template>
|
|
||||||
<q-layout view="hHh lpR lff">
|
|
||||||
<q-header reveal elevated class="bg-primary text-white" height-hint="98">
|
|
||||||
<q-toolbar>
|
|
||||||
<q-btn dense flat round icon="menu" @click="toggleLeftDrawer" />
|
|
||||||
|
|
||||||
<q-toolbar-title>
|
|
||||||
<!-- <q-avatar>
|
|
||||||
<img src="https://cdn.quasar.dev/logo-v2/svg/logo-mono-white.svg" />
|
|
||||||
</q-avatar> -->
|
|
||||||
标题
|
|
||||||
</q-toolbar-title>
|
|
||||||
|
|
||||||
<q-btn dense flat round icon="menu" @click="toggleRightDrawer" />
|
|
||||||
</q-toolbar>
|
|
||||||
|
|
||||||
<q-tabs align="left">
|
|
||||||
<q-route-tab to="/page1" label="Page One" />
|
|
||||||
<q-route-tab to="/page2" label="Page Two" />
|
|
||||||
<q-route-tab to="/page3" label="Page Three" />
|
|
||||||
</q-tabs>
|
|
||||||
</q-header>
|
|
||||||
|
|
||||||
<q-drawer show-if-above v-model="leftDrawerOpen" side="left" elevated>
|
|
||||||
<!-- drawer content -->
|
|
||||||
左栏
|
|
||||||
</q-drawer>
|
|
||||||
|
|
||||||
<q-drawer show-if-above v-model="rightDrawerOpen" side="right" elevated>
|
|
||||||
<!-- drawer content -->
|
|
||||||
右栏
|
|
||||||
</q-drawer>
|
|
||||||
|
|
||||||
<q-page-container>
|
|
||||||
<router-view />
|
|
||||||
</q-page-container>
|
|
||||||
|
|
||||||
<q-footer elevated class="bg-grey-8 text-white">
|
|
||||||
<q-toolbar>
|
|
||||||
<q-btn dense flat round icon="menu" @click="toggleLeftDrawer" />
|
|
||||||
<q-toolbar-title>
|
|
||||||
<div>页脚</div>
|
|
||||||
</q-toolbar-title>
|
|
||||||
<q-btn dense flat round icon="menu" @click="toggleRightDrawer" />
|
|
||||||
</q-toolbar>
|
|
||||||
</q-footer>
|
|
||||||
</q-layout>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
const leftDrawerOpen = ref(false)
|
|
||||||
const rightDrawerOpen = ref(false)
|
|
||||||
function toggleLeftDrawer() {
|
|
||||||
leftDrawerOpen.value = !leftDrawerOpen.value
|
|
||||||
}
|
|
||||||
function toggleRightDrawer() {
|
|
||||||
rightDrawerOpen.value = !rightDrawerOpen.value
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<template>
|
||||||
|
<q-page-container>
|
||||||
|
<router-view />
|
||||||
|
</q-page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
// 无额外逻辑
|
||||||
|
</script>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<q-footer elevated class="bg-grey-8 text-white">
|
||||||
|
<q-toolbar>
|
||||||
|
<q-btn dense flat round icon="menu" @click="settingStore.toggleLeftDrawer" />
|
||||||
|
<q-toolbar-title>
|
||||||
|
<div>页脚</div>
|
||||||
|
</q-toolbar-title>
|
||||||
|
<q-btn dense flat round icon="menu" @click="settingStore.toggleRightDrawer" />
|
||||||
|
</q-toolbar>
|
||||||
|
</q-footer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useSettingStore } from 'stores/setting'
|
||||||
|
|
||||||
|
const settingStore = useSettingStore()
|
||||||
|
</script>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<template>
|
||||||
|
<q-header reveal elevated class="bg-primary text-white" height-hint="98">
|
||||||
|
<q-toolbar>
|
||||||
|
<q-btn dense flat round icon="menu" @click="toggleLeftDrawer" />
|
||||||
|
<q-toolbar-title>标题</q-toolbar-title>
|
||||||
|
<q-btn dense flat round icon="menu" @click="toggleRightDrawer" />
|
||||||
|
</q-toolbar>
|
||||||
|
<q-tabs align="left">
|
||||||
|
<q-route-tab to="/page1" label="Page One" />
|
||||||
|
<q-route-tab to="/page2" label="Page Two" />
|
||||||
|
<q-route-tab to="/page3" label="Page Three" />
|
||||||
|
</q-tabs>
|
||||||
|
</q-header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useSettingStore } from 'stores/setting'
|
||||||
|
|
||||||
|
const settingStore = useSettingStore()
|
||||||
|
const toggleLeftDrawer = () => {
|
||||||
|
settingStore.toggleLeftDrawer()
|
||||||
|
}
|
||||||
|
const toggleRightDrawer = () => {
|
||||||
|
settingStore.toggleRightDrawer()
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<template>
|
||||||
|
<q-drawer v-model="settingStore.leftDrawerOpen" side="left" elevated>
|
||||||
|
<!-- 左侧抽屉菜单 -->
|
||||||
|
左栏
|
||||||
|
</q-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useSettingStore } from 'stores/setting'
|
||||||
|
|
||||||
|
const settingStore = useSettingStore()
|
||||||
|
</script>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<template>
|
||||||
|
<q-layout :view="settingStore.layout">
|
||||||
|
<HeaderLayout />
|
||||||
|
<TabsLayout />
|
||||||
|
<LeftDrawerLayout />
|
||||||
|
<RightDrawerLayout />
|
||||||
|
<ContainerLayout />
|
||||||
|
<FooterLayout />
|
||||||
|
</q-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import HeaderLayout from './HeaderLayout.vue'
|
||||||
|
import TabsLayout from './TabsLayout.vue'
|
||||||
|
import LeftDrawerLayout from './LeftDrawerLayout.vue'
|
||||||
|
import RightDrawerLayout from './RightDrawerLayout.vue'
|
||||||
|
import FooterLayout from './FooterLayout.vue'
|
||||||
|
import ContainerLayout from './ContainerLayout.vue'
|
||||||
|
import { useSettingStore } from 'stores/setting'
|
||||||
|
|
||||||
|
const settingStore = useSettingStore()
|
||||||
|
</script>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<template>
|
||||||
|
<q-drawer v-model="settingStore.rightDrawerOpen" side="right" elevated>
|
||||||
|
<!-- 右侧抽屉内容 -->
|
||||||
|
右栏
|
||||||
|
</q-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useSettingStore } from 'stores/setting'
|
||||||
|
|
||||||
|
const settingStore = useSettingStore()
|
||||||
|
</script>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<template>
|
||||||
|
<q-tabs align="left">
|
||||||
|
<q-route-tab to="/page1" label="Page One" />
|
||||||
|
<q-route-tab to="/page2" label="Page Two" />
|
||||||
|
<q-route-tab to="/page3" label="Page Three" />
|
||||||
|
</q-tabs>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
// 无额外逻辑
|
||||||
|
</script>
|
|
@ -0,0 +1,16 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'
|
||||||
|
|
||||||
|
// 定义 mock 接口列表
|
||||||
|
const mockModules: any[] = []
|
||||||
|
|
||||||
|
// 自动加载 mock 文件夹中的所有 mock 数据
|
||||||
|
const modules = import.meta.glob('./modules/*.ts', { eager: true })
|
||||||
|
Object.keys(modules).forEach((key) => {
|
||||||
|
mockModules.push(...(modules[key] as any).default)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 生产环境 mock 服务初始化方法
|
||||||
|
export function setupProdMockServer() {
|
||||||
|
createProdMockServer(mockModules)
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
export interface Theme {
|
||||||
|
accent: string
|
||||||
|
dark: string
|
||||||
|
info: string
|
||||||
|
light: string
|
||||||
|
negative: string
|
||||||
|
positive: string
|
||||||
|
primary: string
|
||||||
|
secondary: string
|
||||||
|
warning: string
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<q-page class="row items-center justify-evenly"> 初始化页面 </q-page>
|
<q-page class="row items-center justify-evenly"> 初始化 </q-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts"></script>
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<template>
|
||||||
|
<q-page>
|
||||||
|
<div>Page 1 Content</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
// 无额外逻辑
|
||||||
|
</script>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<template>
|
||||||
|
<q-page>
|
||||||
|
<div>Page 3 Content</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
// 无额外逻辑
|
||||||
|
</script>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<template>
|
||||||
|
<q-page>
|
||||||
|
<div>Page 2 Content</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
// 无额外逻辑
|
||||||
|
</script>
|
|
@ -1,18 +1,39 @@
|
||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from 'vue-router';
|
||||||
|
import LoginLayout from 'layouts/LoginLayout/LoginLayoutIndex.vue';
|
||||||
|
import MainLayout from 'layouts/MainLayout/MainLayoutIndex.vue';
|
||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'login',
|
||||||
|
component: () => LoginLayout,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
component: () => import('layouts/MainLayout.vue'),
|
component: () => MainLayout,
|
||||||
children: [{ path: '', component: () => import('pages/IndexPage.vue') }],
|
children: [{ path: '', component: () => import('pages/IndexPage.vue') }],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/pageOne',
|
||||||
|
component: () => MainLayout,
|
||||||
|
children: [{ path: '', component: () => import('pages/PageOne.vue') }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/pageTwo',
|
||||||
|
component: () => MainLayout,
|
||||||
|
children: [{ path: '', component: () => import('pages/PageTwo.vue') }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/pageThree',
|
||||||
|
component: () => MainLayout,
|
||||||
|
children: [{ path: '', component: () => import('pages/PageThree.vue') }],
|
||||||
|
},
|
||||||
// Always leave this as last one,
|
// Always leave this as last one,
|
||||||
// but you can also remove it
|
// but you can also remove it
|
||||||
{
|
{
|
||||||
path: '/:catchAll(.*)*',
|
path: '/:catchAll(.*)*',
|
||||||
component: () => import('pages/ErrorNotFound.vue'),
|
component: () => import('pages/ErrorNotFound.vue'),
|
||||||
},
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
export default routes
|
export default routes;
|
|
@ -1,7 +1,7 @@
|
||||||
import { defineStore } from '#q-app/wrappers'
|
import { defineStore } from '#q-app/wrappers'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import { useCounterStore } from './example-store'
|
import { useCounterStore } from './example-store'
|
||||||
import { useSettingStore } from './setting-store' // 确保 setting-store 被导入
|
import { useSettingStore } from './setting' // 确保 setting-store 被导入
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When adding new properties to stores, you should also
|
* When adding new properties to stores, you should also
|
||||||
|
|
|
@ -2,14 +2,15 @@ import { defineStore, acceptHMRUpdate } from 'pinia'
|
||||||
|
|
||||||
export const useSettingStore = defineStore('setting', {
|
export const useSettingStore = defineStore('setting', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
|
layout: 'hHh lpR lff',
|
||||||
theme: 'light',
|
theme: 'light',
|
||||||
language: 'en-US',
|
language: 'en-US',
|
||||||
|
leftDrawerOpen: true,
|
||||||
|
rightDrawerOpen: false,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
isDarkTheme: (state) => state.theme === 'dark',
|
isDarkTheme: (state) => state.theme === 'dark',
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
setTheme(theme: string) {
|
setTheme(theme: string) {
|
||||||
this.theme = theme
|
this.theme = theme
|
||||||
|
@ -17,6 +18,12 @@ export const useSettingStore = defineStore('setting', {
|
||||||
setLanguage(language: string) {
|
setLanguage(language: string) {
|
||||||
this.language = language
|
this.language = language
|
||||||
},
|
},
|
||||||
|
toggleLeftDrawer() {
|
||||||
|
this.leftDrawerOpen = !this.leftDrawerOpen
|
||||||
|
},
|
||||||
|
toggleRightDrawer() {
|
||||||
|
this.rightDrawerOpen = !this.rightDrawerOpen
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { usePermissionStore } from './permission';
|
||||||
|
const permissionStore = usePermissionStore()
|
||||||
|
|
||||||
|
export const useTabMenuStore = defineStore('tabMenu', {
|
||||||
|
state: () => ({
|
||||||
|
tabMenus: [],
|
||||||
|
currentTab: {},
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
base() {
|
||||||
|
return permissionStore.userMenu.filter(item => item.name === permissionStore.defaultPage[0])[0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
AddTabMenu(tab) {
|
||||||
|
// When exiting, the userMenu is cleared
|
||||||
|
// pass it
|
||||||
|
if (this.base) {
|
||||||
|
// If there is no default page, add it
|
||||||
|
if (this.tabMenus.filter(item => item.path === this.base.path).length === 0) {
|
||||||
|
this.tabMenus = this.tabMenus.concat([this.base])
|
||||||
|
this.currentTab = this.base
|
||||||
|
}
|
||||||
|
// To determine whether a tab exists, the tab will not be passed when all menus are closed
|
||||||
|
if (tab && !this.tabMenus.some(item => item.path === tab.path)) {
|
||||||
|
this.tabMenus = this.tabMenus.concat([tab])
|
||||||
|
this.currentTab = tab
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ChangeCurrentTab(tab) {
|
||||||
|
this.currentTab = tab
|
||||||
|
},
|
||||||
|
RemoveTab(tab) {
|
||||||
|
const removeIndex = this.tabMenus.indexOf(tab)
|
||||||
|
this.tabMenus = this.tabMenus.filter(item => item.path !== tab.path)
|
||||||
|
this.currentTab = this.tabMenus[removeIndex - 1]
|
||||||
|
},
|
||||||
|
RemoveRightTab(tab) {
|
||||||
|
const removeIndex = this.tabMenus.indexOf(tab)
|
||||||
|
this.tabMenus = this.tabMenus.slice(0, removeIndex + 1)
|
||||||
|
},
|
||||||
|
RemoveLeftTab(tab) {
|
||||||
|
const removeIndex = this.tabMenus.indexOf(tab)
|
||||||
|
const rightMenu = this.tabMenus.slice(removeIndex)
|
||||||
|
this.tabMenus = [this.base].concat(rightMenu)
|
||||||
|
},
|
||||||
|
DestroyTabMenu() {
|
||||||
|
this.currentTab = ''
|
||||||
|
this.tabMenus = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,113 @@
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { Cookies, SessionStorage } from 'quasar';
|
||||||
|
import { usePermissionStore } from './permission';
|
||||||
|
import { postAction } from 'src/api/manage';
|
||||||
|
|
||||||
|
export const useUserStore = defineStore('user', {
|
||||||
|
state: () => ({
|
||||||
|
token: undefined,
|
||||||
|
username: undefined,
|
||||||
|
nickname: undefined,
|
||||||
|
realName: undefined,
|
||||||
|
avatar: undefined,
|
||||||
|
rememberMe: true,
|
||||||
|
}),
|
||||||
|
getters: {},
|
||||||
|
actions: {
|
||||||
|
async HandleLogin(loginForm) {
|
||||||
|
const res = await postAction('public/login', loginForm)
|
||||||
|
if (res.code === 1) {
|
||||||
|
const token = res.data.token
|
||||||
|
const username = res.data.username
|
||||||
|
const nickname = res.data.nickname
|
||||||
|
const realName = res.data.real_name
|
||||||
|
const avatar = res.data.avatar
|
||||||
|
this.SetToken(token)
|
||||||
|
this.username = username
|
||||||
|
Cookies.set('gqa-username', username)
|
||||||
|
this.nickname = nickname
|
||||||
|
Cookies.set('gqa-nickname', nickname)
|
||||||
|
this.realName = realName
|
||||||
|
Cookies.set('gqa-realName', realName)
|
||||||
|
this.avatar = avatar
|
||||||
|
Cookies.set('gqa-avatar', avatar)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SetToken(token) {
|
||||||
|
this.token = token
|
||||||
|
if (this.rememberMe) {
|
||||||
|
Cookies.set('gqa-token', token)
|
||||||
|
} else {
|
||||||
|
SessionStorage.set('gqa-token', token)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ChangeRememberMe(type) {
|
||||||
|
this.rememberMe = type
|
||||||
|
},
|
||||||
|
HandleLogout() {
|
||||||
|
const permissionStore = usePermissionStore()
|
||||||
|
permissionStore.ClearMenu()
|
||||||
|
SessionStorage.remove('gqa-token')
|
||||||
|
Cookies.remove('gqa-token')
|
||||||
|
Cookies.remove('gqa-username')
|
||||||
|
Cookies.remove('gqa-nickname')
|
||||||
|
Cookies.remove('gqa-realName')
|
||||||
|
Cookies.remove('gqa-avatar')
|
||||||
|
// dont delete dict
|
||||||
|
// LocalStorage.remove('gqa-dict')
|
||||||
|
this.token = undefined
|
||||||
|
this.username = undefined
|
||||||
|
this.nickname = undefined
|
||||||
|
this.realName = undefined
|
||||||
|
this.avatar = undefined
|
||||||
|
},
|
||||||
|
GetToken() {
|
||||||
|
if (SessionStorage.getItem('gqa-token')) {
|
||||||
|
return SessionStorage.getItem('gqa-token')
|
||||||
|
} else if (Cookies.get('gqa-token')) {
|
||||||
|
return Cookies.get('gqa-token')
|
||||||
|
} else {
|
||||||
|
return this.token
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GetUsername() {
|
||||||
|
if (this.username) {
|
||||||
|
return this.username
|
||||||
|
} else if (Cookies.get('gqa-username')) {
|
||||||
|
return Cookies.get('gqa-username')
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GetNickname() {
|
||||||
|
if (this.nickname) {
|
||||||
|
return this.nickname
|
||||||
|
} else if (Cookies.get('gqa-nickname')) {
|
||||||
|
return Cookies.get('gqa-nickname')
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GetRealName() {
|
||||||
|
if (this.realName) {
|
||||||
|
return this.realName
|
||||||
|
} else if (Cookies.get('gqa-realName')) {
|
||||||
|
return Cookies.get('gqa-realName')
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GetAvatar() {
|
||||||
|
if (this.avatar) {
|
||||||
|
return this.avatar
|
||||||
|
} else if (Cookies.get('gqa-avatar')) {
|
||||||
|
return Cookies.get('gqa-avatar')
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,43 @@
|
||||||
|
import XEUtils from 'xe-utils'
|
||||||
|
import { uniqueId } from 'lodash'
|
||||||
|
|
||||||
|
export const HandleAsideMenu = function (menuData, key, parentKey) {
|
||||||
|
// change list to tree
|
||||||
|
// menu to tree: key => name,parentKey => parentCode
|
||||||
|
const menu = ArrayToTree(menuData, key, parentKey)
|
||||||
|
return checkPathAndChildren(menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPathAndChildren(menu) {
|
||||||
|
return menu.map(m => ({
|
||||||
|
...m, path: m.path || uniqueId('gqa-null-path-'), ...m.children
|
||||||
|
? { children: checkPathAndChildren(m.children) }
|
||||||
|
: {}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ArrayToTree = (arrayData, key, parentKey) => {
|
||||||
|
const treeData = XEUtils.toArrayTree(arrayData, {
|
||||||
|
key: key,
|
||||||
|
parentKey: parentKey,
|
||||||
|
// strict: true
|
||||||
|
})
|
||||||
|
return treeData
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TreeToArray = (treeData) => {
|
||||||
|
const data = XEUtils.toTreeArray(treeData)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChangeNullChildren2Array = (data) => {
|
||||||
|
const index = data?.length
|
||||||
|
for (let i = index - 1; i >= 0; i--) {
|
||||||
|
if (data[i].children === null) {
|
||||||
|
data[i].children = []
|
||||||
|
} else {
|
||||||
|
ChangeNullChildren2Array(data[i].children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { date } from 'quasar'
|
||||||
|
|
||||||
|
export const FormatDateTime = (datetime) => {
|
||||||
|
return date.formatDate(datetime, "YYYY-MM-DD HH:mm:ss")
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FormatDate = (datetime) => {
|
||||||
|
return date.formatDate(datetime, "YYYY-MM-DD")
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FormatDateTimeShort = (datetime) => {
|
||||||
|
return date.formatDate(datetime, "YYYYMMDDHHmmss")
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { PrivateRoutes } from 'src/router/routes'
|
||||||
|
const pagesFile = import.meta.glob('../pages/**/*.vue')
|
||||||
|
const pluginsFile = import.meta.glob('../plugins/**/*.vue')
|
||||||
|
|
||||||
|
export const HandleRouter = (menuData) => {
|
||||||
|
const result = []
|
||||||
|
for (let item of menuData) {
|
||||||
|
if (item.path !== '') {
|
||||||
|
const obj = {
|
||||||
|
path: item.path,
|
||||||
|
name: item.name,
|
||||||
|
component: pageImporter(item.component),
|
||||||
|
meta: {
|
||||||
|
hidden: item.hidden,
|
||||||
|
keep_alive: item.keep_alive,
|
||||||
|
title: item.title,
|
||||||
|
icon: item.icon,
|
||||||
|
parent_code: item.parent_code,
|
||||||
|
},
|
||||||
|
redirect: item.redirect,
|
||||||
|
}
|
||||||
|
result.push(obj)
|
||||||
|
} else {
|
||||||
|
if (item.is_link === "yesNo_yes") {
|
||||||
|
delete item.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Replace the children that need authentication routing (empty by default) with the collated routes from the backend.
|
||||||
|
PrivateRoutes[0].children = [...result]
|
||||||
|
// return authentication routes
|
||||||
|
return PrivateRoutes
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageImporter = (component) => {
|
||||||
|
// Vite 版本:
|
||||||
|
let fileKey = []
|
||||||
|
let resultKey = ''
|
||||||
|
if (component.split('/')[0] === 'pages') {
|
||||||
|
fileKey = Object.keys(pagesFile)
|
||||||
|
resultKey = getResultKey(fileKey, component)
|
||||||
|
return pagesFile[resultKey[0]]
|
||||||
|
} else if (component.split('/')[0] === 'plugins') {
|
||||||
|
fileKey = Object.keys(pluginsFile)
|
||||||
|
resultKey = getResultKey(fileKey, component)
|
||||||
|
return pluginsFile[resultKey[0]]
|
||||||
|
} else {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
// Quasar2 Webpack版本:
|
||||||
|
// return () => Promise.resolve(require(`src/${component}.vue`).default)
|
||||||
|
// Quasar1 Webpack版本:
|
||||||
|
// return (resolve) => require([`src/pages/${component}`], resolve)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getResultKey = (fileKey, component) => {
|
||||||
|
const resultKey = fileKey.filter(key => {
|
||||||
|
return key.replace('../', '').replace('.vue', '') === component
|
||||||
|
})
|
||||||
|
return resultKey
|
||||||
|
}
|