添加前端页面,筛除不需要的模块

This commit is contained in:
柏码の讲师 2023-12-07 20:08:40 +08:00
parent 55f48b2156
commit 5bf37d63fc
17 changed files with 2326 additions and 0 deletions

28
itbaima-monitor-web/.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}

View File

@ -0,0 +1,29 @@
# my-project-frontend
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Compile and Minify for Production
```sh
npm run build
```

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="//lib.baomitu.com/element-plus/2.2.32/index.css"/>
<title>Vite App</title>
<style>
body {
margin: 0;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="module" src="./src/main.js"></script>
</body>
</html>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

1689
itbaima-monitor-web/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
{
"name": "my-project-frontend",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@vueuse/core": "^10.3.0",
"axios": "^1.4.0",
"element-plus": "^2.3.9",
"vue": "^3.3.4",
"vue-router": "^4.2.4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"unplugin-auto-import": "^0.15.2",
"unplugin-vue-components": "^0.24.1",
"vite": "^4.4.6"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,29 @@
<script setup>
import { useDark, useToggle } from '@vueuse/core'
useDark({
selector: 'html',
attribute: 'class',
valueDark: 'dark',
valueLight: 'light'
})
useDark({
onChanged(dark) { useToggle(dark) }
})
</script>
<template>
<header>
<div class="wrapper">
<router-view/>
</div>
</header>
</template>
<style scoped>
header {
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,14 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import axios from "axios";
import 'element-plus/theme-chalk/dark/css-vars.css'
axios.defaults.baseURL = 'http://localhost:8080'
const app = createApp(App)
app.use(router)
app.mount('#app')

View File

@ -0,0 +1,102 @@
import axios from "axios";
import {ElMessage} from "element-plus";
const authItemName = "authorize"
const accessHeader = () => {
return {
'Authorization': `Bearer ${takeAccessToken()}`
}
}
const defaultError = (error) => {
console.error(error)
ElMessage.error('发生了一些错误,请联系管理员')
}
const defaultFailure = (message, status, url) => {
console.warn(`请求地址: ${url}, 状态码: ${status}, 错误信息: ${message}`)
ElMessage.warning(message)
}
function takeAccessToken() {
const str = localStorage.getItem(authItemName) || sessionStorage.getItem(authItemName);
if(!str) return null
const authObj = JSON.parse(str)
if(new Date(authObj.expire) <= new Date()) {
deleteAccessToken()
ElMessage.warning("登录状态已过期,请重新登录!")
return null
}
return authObj.token
}
function storeAccessToken(remember, token, expire){
const authObj = {
token: token,
expire: expire
}
const str = JSON.stringify(authObj)
if(remember)
localStorage.setItem(authItemName, str)
else
sessionStorage.setItem(authItemName, str)
}
function deleteAccessToken() {
localStorage.removeItem(authItemName)
sessionStorage.removeItem(authItemName)
}
function internalPost(url, data, headers, success, failure, error = defaultError){
axios.post(url, data, { headers: headers }).then(({data}) => {
if(data.code === 200)
success(data.data)
else
failure(data.message, data.code, url)
}).catch(err => error(err))
}
function internalGet(url, headers, success, failure, error = defaultError){
axios.get(url, { headers: headers }).then(({data}) => {
if(data.code === 200)
success(data.data)
else
failure(data.message, data.code, url)
}).catch(err => error(err))
}
function login(username, password, remember, success, failure = defaultFailure){
internalPost('/api/auth/login', {
username: username,
password: password
}, {
'Content-Type': 'application/x-www-form-urlencoded'
}, (data) => {
storeAccessToken(remember, data.token, data.expire)
ElMessage.success(`登录成功,欢迎 ${data.username} 来到我们的系统`)
success(data)
}, failure)
}
function post(url, data, success, failure = defaultFailure) {
internalPost(url, data, accessHeader() , success, failure)
}
function logout(success, failure = defaultFailure){
get('/api/auth/logout', () => {
deleteAccessToken()
ElMessage.success(`退出登录成功,欢迎您再次使用`)
success()
}, failure)
}
function get(url, success, failure = defaultFailure) {
internalGet(url, accessHeader(), success, failure)
}
function unauthorized() {
return !takeAccessToken()
}
export { post, get, login, logout, unauthorized }

View File

@ -0,0 +1,41 @@
import { createRouter, createWebHistory } from 'vue-router'
import { unauthorized } from "@/net";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'welcome',
component: () => import('@/views/WelcomeView.vue'),
children: [
{
path: '',
name: 'welcome-login',
component: () => import('@/views/welcome/LoginPage.vue')
}, {
path: 'forget',
name: 'welcome-forget',
component: () => import('@/views/welcome/ForgetPage.vue')
}
]
}, {
path: '/index',
name: 'index',
component: () => import('@/views/IndexView.vue'),
}
]
})
router.beforeEach((to, from, next) => {
const isUnauthorized = unauthorized()
if(to.name.startsWith('welcome') && !isUnauthorized) {
next('/index')
} else if(to.fullPath.startsWith('/index') && isUnauthorized) {
next('/')
} else {
next()
}
})
export default router

View File

@ -0,0 +1,18 @@
<template>
<div>
<el-button @click="userLogout">退出登录</el-button>
</div>
</template>
<script setup>
import { logout } from '@/net'
import router from "@/router";
function userLogout() {
logout(() => router.push("/"))
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,40 @@
<template>
<div style="width: 100vw;height: 100vh;overflow: hidden;display: flex">
<div style="flex: 1">
<el-image style="width: 100%;height: 100%" fit="cover"
src="https://img1.baidu.com/it/u=4097856652,4033702227&fm=253&fmt=auto&app=120&f=JPEG?w=1422&h=800"/>
</div>
<div class="welcome-title">
<div style="font-size: 30px;font-weight: bold">欢迎来到我们的学习平台</div>
<div style="margin-top: 10px">在这里你可以学习如何使用Java如何搭建网站并且与Java之父密切交流</div>
<div style="margin-top: 5px">在这里你可以同性交友因为都是男的没有学Java的女生</div>
</div>
<div class="right-card">
<router-view v-slot="{ Component }">
<transition name="el-fade-in-linear" mode="out-in">
<component :is="Component" style="height: 100%"/>
</transition>
</router-view>
</div>
</div>
</template>
<script setup>
</script>
<style scoped>
.right-card {
width: 400px;
z-index: 1;
background-color: var(--el-bg-color);
}
.welcome-title {
position: absolute;
bottom: 30px;
left: 30px;
color: white;
text-shadow: 0 0 10px black;
}
</style>

View File

@ -0,0 +1,178 @@
<template>
<div>
<div style="margin: 30px 20px">
<el-steps :active="active" finish-status="success" align-center>
<el-step title="验证电子邮件" />
<el-step title="重新设定密码" />
</el-steps>
</div>
<transition name="el-fade-in-linear" mode="out-in">
<div style="text-align: center;margin: 0 20px;height: 100%" v-if="active === 0">
<div style="margin-top: 80px">
<div style="font-size: 25px;font-weight: bold">重置密码</div>
<div style="font-size: 14px;color: grey">请输入需要重置密码的电子邮件地址</div>
</div>
<div style="margin-top: 50px">
<el-form :model="form" :rules="rules" @validate="onValidate" ref="formRef">
<el-form-item prop="email">
<el-input v-model="form.email" type="email" placeholder="电子邮件地址">
<template #prefix>
<el-icon><Message /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="code">
<el-row :gutter="10" style="width: 100%">
<el-col :span="17">
<el-input v-model="form.code" :maxlength="6" type="text" placeholder="请输入验证码">
<template #prefix>
<el-icon><EditPen /></el-icon>
</template>
</el-input>
</el-col>
<el-col :span="5">
<el-button type="success" @click="validateEmail"
:disabled="!isEmailValid || coldTime > 0">
{{coldTime > 0 ? '请稍后 ' + coldTime + ' 秒' : '获取验证码'}}
</el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
</div>
<div style="margin-top: 70px">
<el-button @click="confirmReset()" style="width: 270px;" type="danger" plain>开始重置密码</el-button>
</div>
</div>
</transition>
<transition name="el-fade-in-linear" mode="out-in">
<div style="text-align: center;margin: 0 20px;height: 100%" v-if="active === 1">
<div style="margin-top: 80px">
<div style="font-size: 25px;font-weight: bold">重置密码</div>
<div style="font-size: 14px;color: grey">请填写您的新密码务必牢记防止丢失</div>
</div>
<div style="margin-top: 50px">
<el-form :model="form" :rules="rules" @validate="onValidate" ref="formRef">
<el-form-item prop="password">
<el-input v-model="form.password" :maxlength="16" type="password" placeholder="新密码">
<template #prefix>
<el-icon><Lock /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password_repeat">
<el-input v-model="form.password_repeat" :maxlength="16" type="password" placeholder="重复新密码">
<template #prefix>
<el-icon><Lock /></el-icon>
</template>
</el-input>
</el-form-item>
</el-form>
</div>
<div style="margin-top: 70px">
<el-button @click="doReset()" style="width: 270px;" type="danger" plain>立即重置密码</el-button>
</div>
</div>
</transition>
</div>
</template>
<script setup>
import {reactive, ref} from "vue";
import {EditPen, Lock, Message} from "@element-plus/icons-vue";
import {get, post} from "@/net";
import {ElMessage} from "element-plus";
import router from "@/router";
const active = ref(0)
const form = reactive({
email: '',
code: '',
password: '',
password_repeat: '',
})
const validatePassword = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'))
} else if (value !== form.password) {
callback(new Error("两次输入的密码不一致"))
} else {
callback()
}
}
const rules = {
email: [
{ required: true, message: '请输入邮件地址', trigger: 'blur' },
{type: 'email', message: '请输入合法的电子邮件地址', trigger: ['blur', 'change']}
],
code: [
{ required: true, message: '请输入获取的验证码', trigger: 'blur' },
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 16, message: '密码的长度必须在6-16个字符之间', trigger: ['blur'] }
],
password_repeat: [
{ validator: validatePassword, trigger: ['blur', 'change'] },
],
}
const formRef = ref()
const isEmailValid = ref(false)
const coldTime = ref(0)
const onValidate = (prop, isValid) => {
if(prop === 'email')
isEmailValid.value = isValid
}
const validateEmail = () => {
coldTime.value = 60
get(`/api/auth/ask-code?email=${form.email}&type=reset`, () => {
ElMessage.success(`验证码已发送到邮箱: ${form.email},请注意查收`)
const handle = setInterval(() => {
coldTime.value--
if(coldTime.value === 0) {
clearInterval(handle)
}
}, 1000)
}, (message) => {
ElMessage.warning(message)
coldTime.value = 0
})
}
const confirmReset = () => {
formRef.value.validate((isValid) => {
if(isValid) {
post('/api/auth/reset-confirm', {
email: form.email,
code: form.code
}, () => active.value++)
}
})
}
const doReset = () => {
formRef.value.validate((isValid) => {
if(isValid) {
post('/api/auth/reset-password', {
email: form.email,
code: form.code,
password: form.password
}, () => {
ElMessage.success('密码重置成功,请重新登录')
router.push('/')
})
}
})
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,78 @@
<template>
<div style="text-align: center;margin: 0 20px">
<div style="margin-top: 150px">
<div style="font-size: 25px;font-weight: bold">登录</div>
<div style="font-size: 14px;color: grey">在进入系统之前请先输入用户名和密码进行登录</div>
</div>
<div style="margin-top: 50px">
<el-form :model="form" :rules="rules" ref="formRef">
<el-form-item prop="username">
<el-input v-model="form.username" maxlength="10" type="text" placeholder="用户名/邮箱">
<template #prefix>
<el-icon>
<User/>
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="form.password" type="password" maxlength="20" style="margin-top: 10px" placeholder="密码">
<template #prefix>
<el-icon>
<Lock/>
</el-icon>
</template>
</el-input>
</el-form-item>
<el-row style="margin-top: 5px">
<el-col :span="12" style="text-align: left">
<el-form-item prop="remember">
<el-checkbox v-model="form.remember" label="记住我"/>
</el-form-item>
</el-col>
<el-col :span="12" style="text-align: right">
<el-link @click="router.push('/forget')">忘记密码</el-link>
</el-col>
</el-row>
</el-form>
</div>
<div style="margin-top: 40px">
<el-button @click="userLogin()" style="width: 270px" type="success" plain>立即登录</el-button>
</div>
</div>
</template>
<script setup>
import {User, Lock} from '@element-plus/icons-vue'
import router from "@/router";
import {reactive, ref} from "vue";
import {login} from '@/net'
const formRef = ref()
const form = reactive({
username: '',
password: '',
remember: false
})
const rules = {
username: [
{ required: true, message: '请输入用户名' }
],
password: [
{ required: true, message: '请输入密码'}
]
}
function userLogin() {
formRef.value.validate((isValid) => {
if(isValid) {
login(form.username, form.password, form.remember, () => router.push("/index"))
}
});
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,25 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})