Vue3+node.js实现登录

文章目录

前端代码实现

效果图

前端代码实现

javascript 复制代码
<template>
    <div class="login-container">
        <el-card class="login-card">
            <template #header>
                <div class="card-header">
                    <span>登录</span>
                </div>
            </template>
            <el-form label-width="80px" ref="formRef" :model="formData" :rules="rules">
                <el-form-item label="用户名" prop="username">
                    <el-input v-model="formData.username" placeholder="请输入用户名" ></el-input>
                </el-form-item>
                <el-form-item label="密码"  prop="password">
                    <el-input v-model="formData.password" type="password" placeholder="请输入密码"></el-input>
                </el-form-item>
                <el-form-item>
                    <el-button style="width: 100%;"  @click="handleSubmit"  type="primary">登录</el-button>
                </el-form-item>
                <el-form-item >
                    <el-button link type="info" @click="toggleToLogin"  >没有账号?去注册</el-button>
                </el-form-item>
            </el-form>
        </el-card>
    </div>
  </template>
  
  
  <script setup>
  import { ElMessage } from 'element-plus';
  import axios from 'axios';
  import { reactive, ref } from 'vue';
  
  //初始化
  const formRef = ref(null)
  //跳转注册页面
  const toggleToLogin = () => {
  location.href = '/register'
  }
  //表单数据
  const formData = ref({
    username:'',
    password:''
  }) 
// 修正校验规则
const rules = reactive({
    username:[
        {required: true, message: '请输入用户名', trigger: 'blur'},
        {min: 3, max: 20, message: '长度3到20个字符', trigger: 'blur'} 
    ],
    password:[
        {required: true, message: '请输入密码', trigger: 'blur'},
        {min: 3, max: 20, message: '长度在3到20个字符', trigger: 'blur'}
    ]
})

// 登录处理
const handleSubmit = async () => {
    const form = formRef.value
    if(!form) return;
    try {
      await form.validate();
      const res = await axios.post('http://localhost:3030/user/login', formData.value);
      
      if(res.data.code === 200) {  
        ElMessage.success( '登录成功');
        
        location.href = '/manager/home';
      } else {
        ElMessage.error( '登录失败');
      }
    } catch (error) {
      console.error('登录请求错误', error);
      ElMessage.error(error.response?.data?.message || '登录失败,请稍后重试');
    }
}
  </script>
  
  
  <style scoped>
  .login-container {
    min-height: 100vh;
    display: flex;
    background-color: aqua;
    align-items: center;
    justify-content: center;
  }
  .login-card {
    width: 400px;
    border-radius: 8px;
  }
  .card-header {
    font-size: 24px;
    font-weight: bold;
    text-align: center;
  }
  
  </style>

后端代码实现

创建文件夹router,在router目录下创建path.js文件

javascript 复制代码
//登录
router.post('/login', async (req,res)=>{
    try {
        const {username, password} = req.body
        
        // 验证参数
        if(!username?.trim() || !password?.trim()){  // 增加trim()检查
            return res.status(400).json({
                code: 400,
                message: '用户名和密码不能为空'
            })
        }

        const sql = 'select * from user where username = ? limit 1'
        const [rows] = await pool.query(sql,[username])
        
        if(rows.length === 0){
            return res.status(401).json({
                code: 401,
                message: '用户名或密码错误'  
            })
        }

        const user = rows[0]
        if(user.password !== password){
            return res.status(401).json({
                code: 401,
                message: '用户名或密码错误'
            })
        } 

        // 删除敏感信息
        delete user.password
        
        success(res, {
            code: 200,
            user,
            message: '登录成功'
        })
    } catch (error) {
        handleError(res, error)
    }
})

跨域处理

前端跨域,在vite.config.js中添加跨域请求

javascript 复制代码
import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueDevTools(),
  ],
  server:{
    proxy:{
      '/api':{target:'http://localhost:3030',changeOrigin:true,
        rewrite:(path) => path.replace(/^\/api/,'')
      }
    }
  },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    },
  },
})

后端跨域,创建app.js,导入路由

javascript 复制代码
const express = require('express')
const cors = require('cors')
const router = require('./router/path')


const app = express()

//解析数据
app.use(express.json())
app.use(express.urlencoded({extended:true}))
//跨域处理
app.use(cors(
    {
        origin:['http://localhost:5173'],
        methods:['GET','POST','PUT','DELETE'],
        credentials:true,
        allowedHeaders:['Content-Type']
    }
))

app.use('/user',router)

const PORT = process.env.PORT || 3030
app.listen(PORT,()=>{
    console.log(`服务正在运行...端口为${PORT}`)
})

实现注册界面

相关推荐
福兮说3 分钟前
URL 编码的七个坑:c++ 传到后端变空格、%25 套娃、截断 emoji 直接报错
开发语言·前端·javascript·node.js·url
小的时候可菜了27 分钟前
JS 异步的本质:从单线程到 thenable(一条进化链)
开发语言·前端·javascript
liangshanbo121533 分钟前
Webpack vs Vite 底层原理高频追问的问题
前端·webpack·node.js
可乐鸡翅yeah_35 分钟前
hls.js 配置 loadStartPosition,点播起始播放位置踩坑
开发语言·javascript·网络协议·ecmascript·m3u8在线
web打印社区1 小时前
Windows 网页静默打印设置步骤:客户端、防火墙与联调清单
开发语言·前端·javascript·chrome·pdf·ecmascript
玖石书1 小时前
HiSH 通过 apk 安装 Node.js、npm 与 pnpm
npm·node.js·鸿蒙·hish
池塘水悠悠1 小时前
nodejs24安装(node.js安装)
node.js
web打印社区2 小时前
JS 静默打印怎么做:纯前端为什么不行,以及最小可跑通写法
开发语言·前端·javascript·websocket·网络协议·http·pdf
web打印社区2 小时前
HTML5 静默打印:前端页面怎么调用打印机且尽量不弹窗
前端·javascript·vue.js·pdf·html·html5
LRL_2 小时前
【K8s 存储】Longhorn v1.12.1 离线安装全流程:外网制作离线包 + 内网部署(附踩坑实录)
linux·windows·node.js