前端登录注册完成

FRONT_a
helloworld180 2 months ago
commit c56336c24c

30
.gitignore vendored

@ -0,0 +1,30 @@
# 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?
*.tsbuildinfo

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

@ -0,0 +1 @@
Subproject commit a1fe07390918869ecf655649d88f55be55f8b404

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/点名.svg">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

@ -0,0 +1,8 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}

1124
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,20 @@
{
"name": "name-nacing-system",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.7",
"vue": "^3.4.29",
"vue-router": "^4.3.3"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.5",
"vite": "^5.3.1"
}
}

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1727709759865" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6581" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M485.568 906.496c-0.64-0.192-145.088 10.368-209.216-16.256-86.528-35.968-117.184-62.592-117.184-79.488 0-15.936-2.944-98.112 77.248-123.2a4319.36 4319.36 0 0 1 155.264-43.136 25.216 25.216 0 0 0 22.08-24.832V541.888a24.064 24.064 0 0 0-7.68-17.664c-69.248-66.56-97.728-131.456-97.728-243.584 0-53.312 14.656-91.008 39.296-119.744 26.496-31.104 68.032-46.912 123.264-46.912 159.104 0 169.408 128.384 169.408 171.392a291.072 291.072 0 0 1-26.56 122.048c-13.056 28.544-26.624 44.736-26.752 45.12a24.256 24.256 0 0 0 3.776 35.2 26.88 26.88 0 0 0 36.672-3.648c2.56-3.072 65.088-77.696 65.088-198.72 0-65.024-16.832-109.376-52.544-150.72C600.256 88.448 541.76 64 470.976 64 335.808 64 256.256 140.416 256.256 280.704c0 60.864 8.192 104.256 25.024 148.928a340.48 340.48 0 0 0 80.256 122.368v46.784c-36.928 6.72-73.408 15.552-109.312 26.368-39.232 12.16-70.848 25.536-93.824 40.256-34.176 21.632-51.456 117.76-51.456 145.344 0 45.76 48.64 83.968 149.056 125.504 67.712 28.032 213.12 17.664 215.872 18.368l6.848 1.024a25.92 25.92 0 0 0 25.28-18.496 24.704 24.704 0 0 0-18.432-30.656z" p-id="6582" fill="#8755f2"></path><path d="M937.024 515.776a26.624 26.624 0 0 0-37.376 4.16c-9.152 11.456-199.808 266.624-228.224 268.928-27.712 2.432-122.752-90.112-122.752-90.112a26.496 26.496 0 0 0-36.224 4.032 26.56 26.56 0 0 0 1.856 36.48c10.304 9.664 101.056 82.048 123.072 98.304l25.6 18.816c5.12 3.264 10.752 5.376 16.576 4.736a23.68 23.68 0 0 0 17.536-9.152c6.208-7.36 35.52-41.344 35.776-41.792l208.448-257.216a26.56 26.56 0 0 0-4.288-37.184z" p-id="6583" fill="#8755f2"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,26 @@
<template>
<div>
<router-view/>
</div>
</template>
<style>
/* 让页面铺满整个屏幕,不出现滚动条 */
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: auto;
}
</style>
<script>
export default {
name: "Layout",
}
</script>

@ -0,0 +1,121 @@
.login-register-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-image: linear-gradient(to right, rgb(219, 219, 245), #f4dfe3);
}
.form-box {
background: rgba(211, 197, 252, 1);
padding: 100px 110px;
padding-top: 15px;
border-radius: 40px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
width: 350px;
}
h2 {
text-align: center;
color: #FFFFFF;
margin-bottom: 30px;
font-size: 45px;
}
.tab-switch {
display: flex;
margin-bottom: 20px;
}
.tab-switch span {
margin-right: 25px;
font-size: 22px;
padding: 3px 7px;
border-radius: 3px;
cursor: pointer;
color: #FFFFFF;
}
.tab-switch span.active {
border-bottom: 4px solid #FFFFFF;
}
.input-group {
position: relative;
display: flex;
align-items: center;
width: 100%;
padding: 10px 0px;
border-radius: 20px;
box-shadow: 0px 0px 10px 0px #D5E8FF;
background-color: rgba(243, 243, 243, 0.5);
border: 1px solid #FFFFFF;
margin-top: 20px;
}
.input-group input:focus {
outline: none;
/* box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.5); */
}
.input-icon {
width: 25px;
height: 25px;
margin-left: 15px;
margin-right: 5px;
}
.placeholder {
color: #FFFFFF;
}
input {
flex: 1;
height: 100%;
border: none;
background: transparent;
padding: 0 10px;
color: #615EF8;
font-size: 16px;
}
.forgot-password {
text-align: right;
margin-top: 15px;
}
.forgot-password a {
color: #FFFFFF;
text-decoration: none;
}
.submit-btn {
width: 100%;
padding: 12px 0;
margin-top: 15px;
background-color: #8755F2;
color: #FFFFFF;
border-radius: 20px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
box-shadow: 0px 0px 10px 0px #D5E8FF;
border: 1px solid #FFFFFF;
}
.submit-btn:hover {
background-color: #6231F5;
}
/* 隐藏默认的密码切换图标 */
input[type="password"]::-ms-reveal,
input[type="password"]::-ms-clear,
input[type="password"]::-webkit-contacts-auto-fill-button,
input[type="password"]::-webkit-credentials-auto-fill-button {
display: none !important;
}
/* 对于Chrome浏览器 */
input[type="password"]::-webkit-inner-spin-button,
input[type="password"]::-webkit-outer-spin-button,
input[type="password"]::-webkit-search-cancel-button {
-webkit-appearance: none;
margin: 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

@ -0,0 +1,21 @@
// 引入createApp用于创建应用
import { createApp } from 'vue'
// 引入App.vue文件
import App from './App.vue'
import router from './router'
// import ElementUI from 'element-ui';
// import 'element-ui/lib/theme-chalk/index.css';
// Vue.use(ElementUI);
// new Vue({
// el: '#app',
// render: h => h(App)
// });
const app = createApp(App)
app.use(router)
app.mount('#app')

@ -0,0 +1,26 @@
import { createRouter, createWebHistory} from 'vue-router'
// 引入路由组件
import login from '@/views/login2.vue'
import home from '@/views/home.vue'
// 创建路由器
const router = createRouter({
// 路由器的工作模式
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
// name: 'home',
component: login
},
{
path:'/home',
component: home
}
]
})
export default router

@ -0,0 +1,18 @@
// 存储、获取、删除token的文件
// 定义 setToken 函数,用于将 token 存储到 localStorage
export function setToken(token) {
localStorage.setItem('authToken', token);
}
// 定义 getToken 函数,用于从 localStorage 获取 token
export function getToken() {
return localStorage.getItem('authToken');
}
// 定义 removeToken 函数,用于从 localStorage 删除 token
export function removeToken() {
localStorage.removeItem('authToken');
}

@ -0,0 +1,89 @@
// axiosConfig.js
import axios from 'axios';
import {getToken} from '@/token/auth' // 注意这里使用了解构赋值来导入getToken函数
// 创建axios实例
const service = axios.create({
baseURL: 'http://example.com/api', // 配置基础URL
timeout: 5000, // 请求超时时间
});
// 请求拦截器
service.interceptors.request.use(
config => {
// 在发送请求之前做些什么
const token = getToken(); // 获取token的方式取决于你的应用
if (token) {
config.headers['Authorization'] = `Bearer ${token}`; // 设置token
}
return config;
},
error => {
// 对请求错误做些什么
console.error('Request Error:', error);
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
response => {
// 对响应数据做点什么
const res = response.data;
// 你可以根据实际情况在这里添加一些通用的响应处理逻辑
// 例如,根据返回的状态码判断请求是否成功
if (res.code !== 200) {
// 业务错误处理,比如弹窗提示等
return Promise.reject(new Error(res.message || 'Error'));
} else {
return res;
}
},
error => {
// 对响应错误做点什么
if (error.response) {
// 请求已发出但服务器响应的状态码不在2xx的范围
console.error('Error status:', error.response.status);
console.error('Error data:', error.response.data);
} else if (error.request) {
// 请求已发出,但没有收到响应
console.error('Error request:', error.request);
} else {
// 在设置请求时触发错误
console.error('Error message:', error.message);
}
return Promise.reject(error);
}
);
export default service;
// 使用
// 导入封装好的axios实例
// import axios from './axiosConfig';
// 登录方法
// methods: {
// async login() {
// try {
// const response = await axios.post('/api/login', {
// username: this.username,
// password: this.password
// });
// // 假设token在响应的data字段中
// const token = response.data.token;
// // 存储token
// setToken(token);
// // 登录成功后的操作,比如跳转到主页
// this.$router.push('/home');
// } catch (error) {
// // 处理登录错误
// console.error('Login Error:', error);
// }
// }
// }

@ -0,0 +1,61 @@
<template>
<div class="home">
<div class="content">
<div class="leftIcon">
</div>
<div class="select">
<div class="selectItem">
导入文件
</div>
<div class="selectItem">
开始点名
</div>
<div class="selectItem">
规则设置
</div>
<div class="selectItem">
查看排行
</div>
</div>
</div>
</div>
</template>
<script>
</script>
<style scoped>
.home {
background-image: url('../assets/image/bgcImg.png'); /* 图片路径 */
width:100%;
height:100%;
position:fixed;
background-size:100% 100%;
}
.content {
height:100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 120px;
}
.leftIcon {
width: 700px;
height: 800px;
background-color: #fff;
}
.selectItem {
padding: 25px 130px;
border-radius: 50px;
background-color: #A182FF;
color: #FFFFFF;
font-size: 35px;
font-weight: bold;
margin-bottom: 65px;
cursor: pointer;
}
.selectItem:hover {
background-color: #6231F5;
}
</style>

@ -0,0 +1,187 @@
<template>
<div class="login-register-container">
<div class="form-box">
<h2>让我们开始点名</h2>
<div class="tab-switch">
<span
:class="{ active: activeTab === 'login' }"
@click="switchTab('login')"
>
登录
</span>
<span
:class="{ active: activeTab === 'register' }"
@click="switchTab('register')"
>
注册
</span>
</div>
<!-- 登录表单 -->
<form v-if="activeTab === 'login'" @submit.prevent="handleLogin">
<div class="input-group">
<img src="../assets/image/person.png" alt="" class="input-icon">
<input
v-model="loginForm.phone"
type="text"
placeholder="请输入您的手机号"
placeholder-class="placeholder"
>
</div>
<div class="input-group">
<img src="../assets/image/password.png" alt="" class="input-icon">
<input
v-model="loginForm.password"
:type="showPassword ? 'text' : 'password'"
placeholder="请输入您的密码"
>
<img src="../assets/image/eye.png"
v-if="showPassword"
class="input-icon"
@click="togglePasswordVisibility"
style="cursor: pointer;margin-right: 15px;"
>
<img src="../assets/image/eyeClose.png"
v-if="!showPassword"
class="input-icon"
@click="togglePasswordVisibility"
style="cursor: pointer;margin-right: 15px;"
>
</div>
<div class="forgot-password">
<a href="#">忘记密码?</a>
</div>
<button type="submit" class="submit-btn"> </button>
</form>
<!-- 注册表单 -->
<form v-else @submit.prevent="handleRegister">
<div class="input-group">
<img src="../assets/image/person.png" alt="" class="input-icon">
<input
v-model="registerForm.phone"
type="text"
placeholder="请输入您的手机号"
>
</div>
<div class="input-group">
<img src="../assets/image/password.png" alt="" class="input-icon">
<input
v-model="registerForm.password"
:type="showPassword ? 'text' : 'password'"
placeholder="请输入您的密码"
>
<img src="../assets/image/eye.png"
v-if="showPassword"
class="input-icon"
@click="togglePasswordVisibility"
style="cursor: pointer;margin-right: 15px;"
>
<img src="../assets/image/eyeClose.png"
v-if="!showPassword"
class="input-icon"
@click="togglePasswordVisibility"
style="cursor: pointer;margin-right: 15px;"
>
</div>
<div class="input-group">
<img src="../assets/image/password.png" alt="" class="input-icon">
<input
v-model="registerForm.confirmPassword"
:type="showConfirmPassword ? 'text' : 'password'"
placeholder="再次确认密码"
>
<img src="../assets/image/eye.png"
v-if="showPassword"
class="input-icon"
@click="togglePasswordVisibility"
style="cursor: pointer;margin-right: 15px;"
>
<img src="../assets/image/eyeClose.png"
v-if="!showPassword"
class="input-icon"
@click="togglePasswordVisibility"
style="cursor: pointer;margin-right: 15px;"
>
</div>
<button type="submit" class="submit-btn" style="margin-top: 25px;">注册</button>
</form>
</div>
</div>
</template>
<script>
import axios from '@/utils/axiosConfig';
import {setToken} from '@/token/auth'
export default {
data() {
return {
activeTab: 'login',
showPassword: false,
showConfirmPassword: false,
loginForm: {
phone: '',
password: ''
},
registerForm: {
phone: '',
password: '',
confirmPassword: ''
}
}
},
methods: {
switchTab(tab) {
this.activeTab = tab;
},
togglePasswordVisibility() {
this.showPassword = !this.showPassword;
},
//
async handleLogin() {
try {
const response = await axios.post('/teacher/login', {
username: this.loginForm.phone,
password: this.loginForm.password
});
// tokendata
const token = response.data.token;
// token
setToken(token);
//
this.$router.push('/home');
} catch (error) {
//
console.error('Login Error:', error);
}
},
//
async handleRegister() {
if (this.registerForm.password !== this.registerForm.confirmPassword) {
alert('两次输入的密码不一致');
return;
}
try {
//
const response = await axios.post('/teacher/register', {
username: this.registerForm.phone,
password: this.registerForm.password,
});
//
console.log('注册成功', response);
} catch (error) {
//
console.error('注册失败', error);
}
}
}
}
</script>
<style scoped>
@import '../assets/css/login.css'
</style>

@ -0,0 +1,16 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
Loading…
Cancel
Save