提交 861d956d authored 作者: 王鹏飞's avatar 王鹏飞

feat: 新增app配置

上级 0d8397a9
......@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" href="https://zws-imgs-pub.ezijing.com/pc/base/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>商业数据分析实验室</title>
<title></title>
</head>
<body>
<div id="app"></div>
......
......@@ -12,6 +12,7 @@
"@tinymce/tinymce-vue": "^5.0.0",
"@vant/area-data": "^1.3.2",
"@vueuse/core": "^9.3.1",
"@vueuse/head": "^0.9.8",
"@vueuse/math": "^9.3.1",
"ali-oss": "^6.17.1",
"axios": "^1.1.3",
......@@ -911,6 +912,19 @@
}
}
},
"node_modules/@vueuse/head": {
"version": "0.9.8",
"resolved": "https://registry.npmjs.org/@vueuse/head/-/head-0.9.8.tgz",
"integrity": "sha512-zt8+JksoVFKxRvmABlaUHA62w+8nOcD8cJnaJ0+SHcr6xaIP3GXgh7/n2TzUoWw4l3d9UxRNs+tapgHdsQ7RbA==",
"dependencies": {
"@vueuse/shared": "^9.3.0",
"@zhead/schema": "^0.8.5",
"@zhead/schema-vue": "^0.8.5"
},
"peerDependencies": {
"vue": ">=2.7 || >=3"
}
},
"node_modules/@vueuse/math": {
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/@vueuse/math/-/math-9.3.1.tgz",
......@@ -1000,6 +1014,41 @@
"node": ">=10.0.0"
}
},
"node_modules/@zhead/schema": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@zhead/schema/-/schema-0.8.5.tgz",
"integrity": "sha512-1S3Otr2zpl1zwP72dNseVXQNG9tnTQ6hHUEUYwINvBjRj6bHcUwdE+Itc9OLxnGAJT/7p8P7GHGo5sshXJNJsA==",
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
}
},
"node_modules/@zhead/schema-raw": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@zhead/schema-raw/-/schema-raw-0.8.5.tgz",
"integrity": "sha512-Aq+9mksf5zbtj7HYluT6PVyfpQ6z7mja9MzjFxg76Vt+Q9i0oL1XN6ZYaCXImWRafwbyAxjFQ5aUCVyFn79OpA==",
"dependencies": {
"@zhead/schema": "0.8.5"
},
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
}
},
"node_modules/@zhead/schema-vue": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@zhead/schema-vue/-/schema-vue-0.8.5.tgz",
"integrity": "sha512-6aXjYy3fZVeYBLrHcJQqzqwzC/2tafRO5UxZEgBHnryRnzeLNZV6nTptDvIPWiJObMoJTK21vbg3gkfLNQg84g==",
"dependencies": {
"@vueuse/shared": "^9.2.0",
"@zhead/schema": "0.8.5",
"@zhead/schema-raw": "0.8.5"
},
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
},
"peerDependencies": {
"vue": ">=2.7 || >=3"
}
},
"node_modules/acorn": {
"version": "8.8.0",
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.8.0.tgz",
......@@ -5611,6 +5660,16 @@
}
}
},
"@vueuse/head": {
"version": "0.9.8",
"resolved": "https://registry.npmjs.org/@vueuse/head/-/head-0.9.8.tgz",
"integrity": "sha512-zt8+JksoVFKxRvmABlaUHA62w+8nOcD8cJnaJ0+SHcr6xaIP3GXgh7/n2TzUoWw4l3d9UxRNs+tapgHdsQ7RbA==",
"requires": {
"@vueuse/shared": "^9.3.0",
"@zhead/schema": "^0.8.5",
"@zhead/schema-vue": "^0.8.5"
}
},
"@vueuse/math": {
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/@vueuse/math/-/math-9.3.1.tgz",
......@@ -5654,6 +5713,29 @@
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.6.tgz",
"integrity": "sha512-HHXP9hskkFQHy8QxxUXkS7946FFIhYVfGqsk0WLwllmexN9x/+R4UBLvurHEuyXRfVEObVR8APuQehykLviwSQ=="
},
"@zhead/schema": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@zhead/schema/-/schema-0.8.5.tgz",
"integrity": "sha512-1S3Otr2zpl1zwP72dNseVXQNG9tnTQ6hHUEUYwINvBjRj6bHcUwdE+Itc9OLxnGAJT/7p8P7GHGo5sshXJNJsA=="
},
"@zhead/schema-raw": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@zhead/schema-raw/-/schema-raw-0.8.5.tgz",
"integrity": "sha512-Aq+9mksf5zbtj7HYluT6PVyfpQ6z7mja9MzjFxg76Vt+Q9i0oL1XN6ZYaCXImWRafwbyAxjFQ5aUCVyFn79OpA==",
"requires": {
"@zhead/schema": "0.8.5"
}
},
"@zhead/schema-vue": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/@zhead/schema-vue/-/schema-vue-0.8.5.tgz",
"integrity": "sha512-6aXjYy3fZVeYBLrHcJQqzqwzC/2tafRO5UxZEgBHnryRnzeLNZV6nTptDvIPWiJObMoJTK21vbg3gkfLNQg84g==",
"requires": {
"@vueuse/shared": "^9.2.0",
"@zhead/schema": "0.8.5",
"@zhead/schema-raw": "0.8.5"
}
},
"acorn": {
"version": "8.8.0",
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.8.0.tgz",
......
......@@ -18,6 +18,7 @@
"@tinymce/tinymce-vue": "^5.0.0",
"@vant/area-data": "^1.3.2",
"@vueuse/core": "^9.3.1",
"@vueuse/head": "^0.9.8",
"@vueuse/math": "^9.3.1",
"ali-oss": "^6.17.1",
"axios": "^1.1.3",
......
<script setup>
import { useHead } from '@vueuse/head'
import { useAppConfig } from '@/composables/useAppConfig'
const appConfig = useAppConfig()
useHead({
title: appConfig.title
})
</script>
<template>
<RouterView />
</template>
......
<script lang="ts">
export default { name: 'AppFooter' }
</script>
<template>
<footer class="app-footer">
<p>技术支持:清控紫荆(北京)教育科技股份有限公司</p>
</footer>
</template>
<style lang="scss">
.app-footer {
padding: 10px 0;
font-size: 14px;
line-height: 30px;
text-align: center;
background-color: #fff;
}
</style>
......@@ -7,7 +7,8 @@ import type { IMenuItem } from '@/types'
import { CaretBottom } from '@element-plus/icons-vue'
import { useUserStore } from '@/stores/user'
import { useMenuStore } from '@/stores/menu'
import { useAppConfig } from '@/composables/useAppConfig'
const appConfig = useAppConfig()
const route = useRoute()
const router = useRouter()
......@@ -15,7 +16,14 @@ const userStore = useUserStore()
const userInfo = userStore.user
const menuStore = useMenuStore()
const menus = $computed(() => menuStore.menus)
const menus = $computed(() => {
// 大赛系统隐藏学生端、教师端的菜单
if (appConfig.isGame && userStore.role?.id !== 6) {
return []
} else {
return menuStore.menus
}
})
const defaultActive = computed(() => {
// 扁平菜单
......@@ -52,10 +60,15 @@ function handleClick(path: string) {
<template>
<header class="app-header">
<div class="app-header-left">
<div class="logo">
<router-link to="/"><img src="https://zws-imgs-pub.ezijing.com/pc/base/ezijing-logo.svg" /></router-link>
</div>
<h1 class="app-name">商业数据分析实验室</h1>
<template v-if="appConfig.logo">
<div class="logo">
<router-link to="/"><img :src="appConfig.logo" /></router-link>
</div>
<div class="line"></div>
</template>
<h1 class="app-name">
<router-link to="/">{{ appConfig.title }}</router-link>
</h1>
</div>
<div class="app-header-nav">
<el-menu mode="horizontal" router :default-active="defaultActive">
......@@ -65,8 +78,7 @@ function handleClick(path: string) {
:popper-offset="0"
popper-class="sub-menu-popper"
v-permission="item.tag"
v-if="item.children"
>
v-if="item.children">
<template #title>
{{ item.name }}
</template>
......@@ -75,8 +87,7 @@ function handleClick(path: string) {
v-for="subitem in item.children"
:key="subitem.path"
v-permission="subitem.tag"
@click="handleClick(subitem.path)"
>
@click="handleClick(subitem.path)">
{{ subitem.name }}
</el-menu-item>
</el-sub-menu>
......@@ -134,15 +145,17 @@ function handleClick(path: string) {
.app-header-left {
display: flex;
align-items: center;
.line {
margin: 0 20px;
height: 30px;
border-left: 2px solid var(--main-color);
}
.app-name {
margin-left: 20px;
padding: 0 15px;
font-size: 28px;
font-family: Source Han Sans CN;
color: #333;
font-weight: 500;
line-height: 30px;
border-left: 2px solid var(--main-color);
white-space: nowrap;
}
}
......
......@@ -6,19 +6,22 @@ export default { name: 'AppLayout' }
import AppHeader from './Header.vue'
import AppAside from './Aside.vue'
import AppMain from './Main.vue'
withDefaults(defineProps<{ sidebar?: boolean; hasTitle?: boolean }>(), {
import AppFooter from './Footer.vue'
withDefaults(defineProps<{ sidebar?: boolean; footer?: boolean }>(), {
sidebar: false,
hasTitle: true
footer: false
})
</script>
<template>
<div class="app-layout">
<app-header :hasTitle="hasTitle"></app-header>
<AppHeader></AppHeader>
<div class="app-layout-container">
<app-aside v-if="sidebar"></app-aside>
<app-main></app-main>
<AppAside v-if="sidebar"></AppAside>
<AppMain></AppMain>
</div>
<AppFooter v-if="footer"></AppFooter>
</div>
</template>
......@@ -28,9 +31,12 @@ withDefaults(defineProps<{ sidebar?: boolean; hasTitle?: boolean }>(), {
min-height: 100vh;
margin: 0 auto;
background-color: #ecf2f7;
display: flex;
flex-direction: column;
}
.app-layout-container {
min-height: calc(100vh - 66px);
// min-height: calc(100vh - 66px);
flex: 1;
display: flex;
}
</style>
const appConfigList = [
{
title: '商业数据分析实验室',
logo: 'https://zws-imgs-pub.ezijing.com/pc/base/ezijing-logo.svg',
hosts: ['saas-lab']
},
{
isGame: true,
title: '商业数据分析竞赛平台',
hosts: ['saas-game']
}
]
export function useAppConfig() {
const found = appConfigList.find(item => {
return item.hosts.find(host => location.host.includes(host))
})
const appConfig = found || appConfigList[0]
return { ...appConfig }
}
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { createHead } from '@vueuse/head'
import App from './App.vue'
import router from './router'
......@@ -7,7 +8,6 @@ import router from './router'
import ElementPlus from 'element-plus'
import '@/assets/styles/element/index.scss'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import AppCard from '@/components/base/AppCard.vue'
import AppList from '@/components/base/AppList.vue'
import AppUpload from '@/components/base/AppUpload.vue'
......@@ -17,6 +17,7 @@ import modules from './modules'
import { permissionDirective } from '@/utils/permission'
const app = createApp(App)
const head = createHead()
// 注册公共组件
app.component('AppCard', AppCard).component('AppList', AppList).component('AppUpload', AppUpload)
app.directive('permission', permissionDirective)
......@@ -25,6 +26,7 @@ modules({ router })
app.use(createPinia())
app.use(router)
app.use(head)
app.use(ElementPlus, { locale: zhCn })
app.mount('#app')
import type { RouteRecordRaw } from 'vue-router'
import AppLayout from '@/components/layout/Index.vue'
import { useAppConfig } from '@/composables/useAppConfig'
const appConfig = useAppConfig()
export const routes: Array<RouteRecordRaw> = [
{
path: '/',
component: AppLayout,
props: { sidebar: false },
props: { sidebar: false, footer: appConfig.isGame },
children: [{ path: '', component: () => import('./views/Index.vue') }]
}
]
import type { SystemDictionary } from '@/types'
export interface ExperimentItem {
id: string
name: string
......
<script setup lang="ts">
import AppMessage from '@/components/Message.vue'
import Total from '../components/Total.vue'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const AdminHome = defineAsyncComponent(() => import('../components/AdminHome.vue'))
const TeacherHome = defineAsyncComponent(() => import('../components/TeacherHome.vue'))
const StudentHome = defineAsyncComponent(() => import('../components/StudentHome.vue'))
</script>
<template>
<div class="home">
<Total></Total>
<AdminHome v-if="userStore.role?.id === 6"></AdminHome>
<TeacherHome v-else-if="userStore.role?.id === 5"></TeacherHome>
<StudentHome v-else></StudentHome>
</div>
<AppMessage></AppMessage>
</template>
<style lang="scss" scoped>
.home {
margin: 0 30px;
}
</style>
<script setup lang="ts"></script>
<template>
<div></div>
</template>
<script setup lang="ts">
import AppMessage from '@/components/Message.vue'
import Total from '../components/Total.vue'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const AdminHome = defineAsyncComponent(() => import('../components/AdminHome.vue'))
const TeacherHome = defineAsyncComponent(() => import('../components/TeacherHome.vue'))
const StudentHome = defineAsyncComponent(() => import('../components/StudentHome.vue'))
import { useAppConfig } from '@/composables/useAppConfig'
const appConfig = useAppConfig()
const DefaultHome = defineAsyncComponent(() => import('./Default.vue'))
const GameHome = defineAsyncComponent(() => import('./Game.vue'))
</script>
<template>
<div class="home">
<Total></Total>
<AdminHome v-if="userStore.role?.id === 6"></AdminHome>
<TeacherHome v-else-if="userStore.role?.id === 5"></TeacherHome>
<StudentHome v-else></StudentHome>
</div>
<AppMessage></AppMessage>
<component :is="appConfig.isGame ? GameHome : DefaultHome"></component>
</template>
<style lang="scss" scoped>
.home {
margin: 0 30px;
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论