Compare commits
3 Commits
Author | SHA1 | Date |
---|---|---|
Fromsko | 909814869d | |
Fromsko | c5e0cfce28 | |
Fromsko | ed3bee5a4e |
|
@ -15,8 +15,9 @@
|
|||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"axios": "^1.7.7",
|
||||
"element-plus": "^2.8.2",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.29",
|
||||
"log4js": "^6.9.1",
|
||||
"pinia": "^2.2.2",
|
||||
"vue": "^3.5.4",
|
||||
"vue-router": "^4.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -30,6 +31,7 @@
|
|||
"npm-run-all2": "^6.2.0",
|
||||
"sass-embedded": "^1.78.0",
|
||||
"typescript": "~5.4.0",
|
||||
"unplugin-element-plus": "^0.8.0",
|
||||
"vite": "^5.3.1",
|
||||
"vitest": "^1.6.0",
|
||||
"vue-tsc": "^2.0.21"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,6 +10,6 @@ export default {
|
|||
|
||||
<style lang="sass">
|
||||
@import "@/assets/style/reset.css"
|
||||
@import "@/assets/style/oneui.css"
|
||||
@import "@/assets/style/index.scss"
|
||||
@import "@/assets/style/oneui.css"
|
||||
</style>
|
||||
|
|
14
src/main.ts
14
src/main.ts
|
@ -1,22 +1,28 @@
|
|||
// 程序入口
|
||||
import api from "@/api"
|
||||
import App from '@/App.vue'
|
||||
import router from '@/router'
|
||||
import storage from '@/stores/storage'
|
||||
import request from '@/utils/request'
|
||||
import storage from '@/utils/storage'
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import { createPinia } from 'pinia'
|
||||
import { createApp } from 'vue'
|
||||
|
||||
const app = createApp(App)
|
||||
const pinia = createPinia()
|
||||
|
||||
app.config.globalProperties.$request = request
|
||||
app.config.globalProperties.$api = api
|
||||
app.config.globalProperties.$storage = storage
|
||||
|
||||
app.use(ElementPlus)
|
||||
app.use(createPinia())
|
||||
// 引入图标组件
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(key, component)
|
||||
}
|
||||
|
||||
app.use(router)
|
||||
app.use(pinia)
|
||||
app.use(ElementPlus)
|
||||
|
||||
app.mount('#app')
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// index.js - router
|
||||
import LoginA from "@/components/Login/LoginA.vue"
|
||||
import storage from "@/stores/storage"
|
||||
import useColorLogOutPut from "@/utils/color_log"
|
||||
import storage from "@/utils/storage"
|
||||
import About from "@/views/About.vue"
|
||||
import ApiPage from '@/views/ApiPage.vue'
|
||||
import Base from "@/views/Base.vue"
|
||||
|
@ -34,19 +33,12 @@ const routes = [
|
|||
{
|
||||
name: 'Login',
|
||||
path: '/Login',
|
||||
component: LoginA,
|
||||
// component: () => import("@/components/Login/LoginA.vue"),
|
||||
component: () => import("@/views/Login.vue"),
|
||||
meta: {
|
||||
title: "登陆页^_^"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Welcome',
|
||||
path: '/Welcome',
|
||||
component: Welcome,
|
||||
meta: {
|
||||
title: "欢迎页"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'demo',
|
||||
path: '/demo',
|
||||
|
@ -62,7 +54,15 @@ const routes = [
|
|||
meta: {
|
||||
title: "关于"
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Welcome',
|
||||
path: '/Welcome',
|
||||
component: Welcome,
|
||||
meta: {
|
||||
title: "欢迎页"
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
@ -74,29 +74,42 @@ const router = createRouter({
|
|||
})
|
||||
|
||||
|
||||
//导航守卫
|
||||
// 导航守卫
|
||||
router.beforeEach((to, from, next) => {
|
||||
// 设置标签信息
|
||||
const { meta: { title } } = to
|
||||
document.title = title as string || '自定义名称'
|
||||
const { meta: { title } } = to;
|
||||
document.title = title as string || '自定义名称';
|
||||
const urlPath: string = to.path.toLowerCase();
|
||||
const checkUserInfo: boolean = storage.getItem('userInfo');
|
||||
|
||||
// 判断是否在 (注册|登录)
|
||||
if (to.path == '/Login') return next()
|
||||
if (to.path == '/register') return next()
|
||||
const tokenStr = storage.getItem('token')
|
||||
if (!tokenStr) return next('/Login')
|
||||
next()
|
||||
// 处理不同路由的逻辑
|
||||
switch (urlPath) {
|
||||
case '/login':
|
||||
case '/register':
|
||||
log.info("用户访问登录|注册");
|
||||
next();
|
||||
break;
|
||||
default:
|
||||
if (urlPath !== '/' && !checkUserInfo) {
|
||||
log.warning(`用户无权但是访问了:> ${urlPath}`);
|
||||
next('/login'); // 重定向到登录
|
||||
} else {
|
||||
log.success(`正常访问了: ${urlPath}`);
|
||||
next(); // 允许继续访问
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// 全局后置守卫
|
||||
router.afterEach((to, from, failure) => {
|
||||
// console.log('全局后置守卫', to, from, failure)
|
||||
// next()
|
||||
})
|
||||
|
||||
// 全局解析守
|
||||
router.beforeResolve((to, from, next) => {
|
||||
log.info('我是全局解析守卫')
|
||||
next()
|
||||
})
|
||||
|
||||
// // 全局后置守卫
|
||||
// router.afterEach((to, from, failure) => {
|
||||
// // console.log('全局后置守卫', to, from, failure)
|
||||
// // next()
|
||||
// })
|
||||
export default router
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
const count = ref(0)
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment }
|
||||
})
|
|
@ -1,5 +1,9 @@
|
|||
/*
|
||||
好看的浏览器控制台输出
|
||||
/**
|
||||
File: color_log.ts
|
||||
Author: Fromsko
|
||||
Created At: 2024-09-14
|
||||
GitHub: https://github.com/fromsko
|
||||
Description: 好看的浏览器控制台输出
|
||||
*/
|
||||
|
||||
const useColorLogOutPut = () => {
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
File: log4j.ts
|
||||
Author: Fromsko
|
||||
Created At: 2024-09-14
|
||||
GitHub: https://github.com/fromsko
|
||||
Description: Log4j 封装日志
|
||||
*/
|
||||
import * as log4js from "log4js";
|
||||
|
||||
const levels = {
|
||||
'trace': log4js.levels.TRACE,
|
||||
'debug': log4js.levels.DEBUG,
|
||||
'info': log4js.levels.INFO,
|
||||
'warn': log4js.levels.WARN,
|
||||
'error': log4js.levels.ERROR,
|
||||
'fatal': log4js.levels.FATAL,
|
||||
}
|
||||
|
||||
log4js.configure({
|
||||
appenders: {
|
||||
console: { type: 'console' }
|
||||
},
|
||||
categories: {
|
||||
default: {
|
||||
appenders: ['out', 'app'],
|
||||
level: 'debug',
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 日志输出, level 为 debug
|
||||
* @param {string} content
|
||||
*/
|
||||
export const debug = (content: string) => {
|
||||
let logger = log4js.getLogger('debug')
|
||||
logger.level = levels.debug
|
||||
logger.debug(content)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 日志输出, level 为 trace
|
||||
* @param {string} content
|
||||
*/
|
||||
export const trace = (content: string) => {
|
||||
let logger = log4js.getLogger('trace')
|
||||
logger.level = levels.trace
|
||||
logger.trace(content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志输出, level 为 fatal
|
||||
* @param {string} content
|
||||
*/
|
||||
export const fatal = (content: string) => {
|
||||
let logger = log4js.getLogger('fatal')
|
||||
logger.level = levels.fatal
|
||||
logger.fatal(content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志输出, level 为 info
|
||||
* @param {string} content
|
||||
*/
|
||||
export const info = (content: string) => {
|
||||
let logger = log4js.getLogger('info')
|
||||
logger.level = levels.info
|
||||
logger.info(content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志输出, level 为 warn
|
||||
* @param {string} content
|
||||
*/
|
||||
export const warn = (content: string) => {
|
||||
let logger = log4js.getLogger('warn')
|
||||
logger.level = levels.warn
|
||||
logger.warn(content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志输出, level 为 error
|
||||
* @param {string} content
|
||||
*/
|
||||
export const error = (content: string) => {
|
||||
let logger = log4js.getLogger('error')
|
||||
logger.level = levels.error
|
||||
logger.error(content)
|
||||
}
|
||||
|
|
@ -1,3 +1,10 @@
|
|||
/**
|
||||
File: request.ts
|
||||
Author: Fromsko
|
||||
Created At: 2024-09-11
|
||||
GitHub: https://github.com/fromsko
|
||||
Description: Axios 请求封装
|
||||
*/
|
||||
import config from '@/config'
|
||||
import router from '@/router'
|
||||
import axios from 'axios'
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
/**
|
||||
* storage 封装
|
||||
* @author fromsko
|
||||
*/
|
||||
/**
|
||||
File: storage.ts
|
||||
Author: Fromsko
|
||||
Created At: 2024-09-14
|
||||
GitHub: https://github.com/fromsko
|
||||
Description: storage 封装
|
||||
*/
|
||||
import config from "@/config"
|
||||
|
||||
export default {
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
File: util.ts
|
||||
Author: Fromsko
|
||||
Created At: 2024-09-14
|
||||
GitHub: https://github.com/fromsko
|
||||
Description: 工具函数
|
||||
*/
|
||||
import * as log4js from "./log4j";
|
||||
|
||||
const CODE = {
|
||||
SUCCESS: 200,
|
||||
PARAM_ERROR: 10001, // 参数错误
|
||||
USER_ACCOUNT_ERROR: 20001, // 用户未登录
|
||||
USER_LOGIN_ERROR: 30001,
|
||||
BUSINESS_ERROR: 40001, //
|
||||
AUTH_ERROR: 50001, // 认证失败
|
||||
}
|
||||
module.exports = {
|
||||
/**
|
||||
* 分页结构封装
|
||||
* @param {number} pageNum
|
||||
* @param {number} pageSize
|
||||
*/
|
||||
pager({ pageNum = 1, pageSize = 10 }: any) {
|
||||
pageNum *= 1;
|
||||
pageSize *= 1;
|
||||
const skipIndex = (pageNum - 1) * pageSize;
|
||||
return {
|
||||
page: {
|
||||
pageNum,
|
||||
pageSize
|
||||
},
|
||||
skipIndex
|
||||
}
|
||||
},
|
||||
success(data = '', msg = '', code = CODE.SUCCESS) {
|
||||
log4js.info("正确")
|
||||
return {
|
||||
code, data, msg
|
||||
}
|
||||
},
|
||||
fail(msg = '', code = CODE.BUSINESS_ERROR) {
|
||||
log4js.debug('错误')
|
||||
return {
|
||||
code, msg
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import storage from '@/stores/storage'
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
const clearToken = () => {
|
||||
storage.clearItem('token')
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<header class="site-header">
|
||||
<nav class="nav_jsxs">
|
||||
<img class="logo_jsxs" style="float: left" />
|
||||
<router-link to="/Welcome">欢迎</router-link>
|
||||
<router-link to="/Login">登录</router-link>
|
||||
<router-link to="/About">关于</router-link>
|
||||
</nav>
|
||||
|
|
|
@ -1,10 +1,71 @@
|
|||
<template>
|
||||
<div class="basic-layout">
|
||||
<div class="nav-side"></div>
|
||||
<div class="content-right">
|
||||
<div :class="['nav-side', isCollapse ? 'fold' : 'unfold']">
|
||||
<!-- 系统 LOGO -->
|
||||
<div class="logo">
|
||||
<img src="./../assets/logo.svg" alt="" />
|
||||
<span>Manager</span>
|
||||
</div>
|
||||
<!-- 导航菜单 -->
|
||||
<el-menu
|
||||
default-active="1"
|
||||
class="nav-menu"
|
||||
background-color="#001529"
|
||||
text-color="#fff"
|
||||
router
|
||||
:collapse="isCollapse"
|
||||
>
|
||||
<el-sub-menu index="1">
|
||||
<template #title>
|
||||
<el-icon><setting /></el-icon>
|
||||
<span>系统管理</span>
|
||||
</template>
|
||||
<el-menu-item-group>
|
||||
<el-menu-item index="1-1">用户管理</el-menu-item>
|
||||
<el-menu-item index="1-2">文件管理</el-menu-item>
|
||||
<el-menu-item index="1-3">接口管理</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
</el-sub-menu>
|
||||
<el-menu-item index="2">
|
||||
<el-icon><Menu /></el-icon>
|
||||
<span>应用集</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3">
|
||||
<el-icon><QuestionFilled /></el-icon>
|
||||
<span>关于页</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
<!-- 上层导航栏 -->
|
||||
<div :class="['content-right', isCollapse ? 'fold' : 'unfold']">
|
||||
<div class="nav-top">
|
||||
<div class="bread">面包屑</div>
|
||||
<div class="user-info">用户</div>
|
||||
<div class="nav-left">
|
||||
<div class="nav-fold" @click="toggle">
|
||||
<el-icon><Fold /></el-icon>
|
||||
</div>
|
||||
<div class="bread">面包屑</div>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<el-badge :is-dot="true" class="notice" type="danger">
|
||||
<el-icon><Bell /></el-icon>
|
||||
</el-badge>
|
||||
<el-dropdown @command="handleCommand">
|
||||
<span class="user-link">
|
||||
{{ userInfo.userName }}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="email"
|
||||
>邮箱: {{ userInfo.userEmail }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item command="logout">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
<div class="main-page">
|
||||
|
@ -15,10 +76,30 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import storage from '@/utils/storage'
|
||||
|
||||
export default {
|
||||
name: 'home',
|
||||
methods: {},
|
||||
methods: {
|
||||
handleCommand(key: string) {
|
||||
if (key == 'email') return
|
||||
storage.clearItem('userInfo')
|
||||
this.$router.push('/')
|
||||
},
|
||||
toggle() {
|
||||
this.isCollapse = !this.isCollapse
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isCollapse: false,
|
||||
userInfo: {
|
||||
userName: 'Fromsko',
|
||||
userEmail: '1614355756@qq.com',
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -33,12 +114,47 @@ export default {
|
|||
background-color: #001529;
|
||||
color: #fff;
|
||||
overflow-y: auto;
|
||||
transition: width 0.5s;
|
||||
transition: width 0.5s ease; // 设置平滑的宽度过渡
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
height: 50px;
|
||||
img {
|
||||
margin: 0 16px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
.nav-menu {
|
||||
height: calc(100vh - 60px);
|
||||
border-right: none;
|
||||
transition: height 0.3s ease; // 设置平滑的高度过渡
|
||||
overflow: hidden;
|
||||
}
|
||||
&.fold {
|
||||
width: 64px;
|
||||
.nav-menu {
|
||||
// 设置折叠状态时的高度
|
||||
height: calc(100vh - 60px);
|
||||
}
|
||||
}
|
||||
&.unfold {
|
||||
width: 200px;
|
||||
.nav-menu {
|
||||
height: calc(100vh - 60px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-right {
|
||||
margin-left: 200px;
|
||||
|
||||
&.fold {
|
||||
margin-left: 64px;
|
||||
}
|
||||
&.unfold {
|
||||
margin-right: 200px;
|
||||
}
|
||||
.nav-top {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
|
@ -46,6 +162,25 @@ export default {
|
|||
justify-content: space-between;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 0 20px;
|
||||
.nav-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.nav-fold {
|
||||
margin-right: 10px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
.user-info {
|
||||
.notice {
|
||||
line-height: 30px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.user-link {
|
||||
cursor: pointer;
|
||||
color: #409eff;
|
||||
line-height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import TheWelcome from '../components/TheWelcome.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<TheWelcome />
|
||||
</main>
|
||||
</template>
|
|
@ -34,7 +34,7 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import storage from '@/stores/storage'
|
||||
import storage from '@/utils/storage'
|
||||
import { Lock, UserFilled } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { defineComponent, reactive, ref } from 'vue'
|
||||
|
@ -53,13 +53,16 @@ const handleSubmit = () => {
|
|||
|
||||
if (loginForm.qqNumber && loginForm.password) {
|
||||
isLogin.value = true
|
||||
storage.setItem('token', 'sk-Passed')
|
||||
storage.setItem('userInfo', 'sk-Passed')
|
||||
ElMessage.success('登录成功')
|
||||
} else {
|
||||
ElMessage.error('请填写完整的登录信息')
|
||||
}
|
||||
isSending.value = false
|
||||
setTimeout(router.push('/'), 1500)
|
||||
let timer = setInterval(async () => {
|
||||
await router.push('/')
|
||||
clearInterval(timer)
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
defineComponent({
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
<template>
|
||||
<h1>欢迎页面</h1>
|
||||
<router-link to="/login">去登陆</router-link>
|
||||
<div class="welcome">
|
||||
<div class="content">
|
||||
<div class="sub-title">欢迎体验</div>
|
||||
<div class="title">通用后台管理系统</div>
|
||||
<div class="desc">Vue3 + ElementPlus + Node + Mongo 打造</div>
|
||||
</div>
|
||||
<div class="img"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -10,4 +16,35 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
<style>
|
||||
.welcome {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
.content {
|
||||
position: relative;
|
||||
.sub-title {
|
||||
font-size: 30px;
|
||||
line-height: 42px;
|
||||
color: #333;
|
||||
}
|
||||
.title {
|
||||
font-size: 40px;
|
||||
line-height: 62px;
|
||||
color: #409eff;
|
||||
}
|
||||
.desc {
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.img {
|
||||
margin-right: 50px;
|
||||
background-image: url('../assets/images/qq-logo.png');
|
||||
width: 250px;
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import ElementPlus from 'unplugin-element-plus/vite'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
ElementPlus({})
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
|
@ -14,6 +16,7 @@ export default defineConfig({
|
|||
}
|
||||
},
|
||||
server: {
|
||||
ws: false
|
||||
hmr: false, // 关闭 HMR
|
||||
port: 5173 // 确保指定的端口
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue