Compare commits

...

3 Commits

Author SHA1 Message Date
Fromsko 909814869d feat: 修复嵌套规则错误 2024-09-15 20:44:33 +08:00
Fromsko c5e0cfce28 feat:修复页面失衡 2024-09-14 03:28:59 +08:00
Fromsko ed3bee5a4e feat: 欢迎页面配置 2024-09-13 19:58:18 +08:00
19 changed files with 3093 additions and 77 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -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"

2684
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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>

View File

@ -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')

View File

@ -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

View File

@ -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 }
})

View File

@ -1,5 +1,9 @@
/*
/**
File: color_log.ts
Author: Fromsko
Created At: 2024-09-14
GitHub: https://github.com/fromsko
Description: 好看的浏览器控制台输出
*/
const useColorLogOutPut = () => {

91
src/utils/log4j.ts Normal file
View File

@ -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)
}

View File

@ -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'

View File

@ -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 {

48
src/utils/util.ts Normal file
View File

@ -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
}
}
}

View File

@ -29,7 +29,7 @@
</template>
<script>
import storage from '@/stores/storage'
import storage from '@/utils/storage'
const clearToken = () => {
storage.clearItem('token')

View File

@ -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>

View File

@ -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 {

View File

@ -1,9 +0,0 @@
<script setup lang="ts">
import TheWelcome from '../components/TheWelcome.vue'
</script>
<template>
<main>
<TheWelcome />
</main>
</template>

View File

@ -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({

View File

@ -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>

View File

@ -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 // 确保指定的端口
}
})