基础vue3前端登陆注册界面以及主页面设计

发布于:2025-02-10 ⋅ 阅读:(43) ⋅ 点赞:(0)

1.下载依赖

"@element-plus/icons": "^0.0.11",
"@element-plus/icons-vue": "^2.3.1",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/vue-fontawesome": "^3.0.8",
"axios": "^1.7.9",
"element-plus": "^2.9.3",
"vue": "^3.5.13",
"vue-router": "^4.5.0"

2.注册组件

import { createApp } from 'vue';
import App from './App.vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import './assets/css/global.css';
import zhCn from 'element-plus/es/locale/lang/zh-cn';
// 导入所有需要的图标组件
import * as Icons from '@element-plus/icons-vue';
import {router} from "./router/router.js";
// 创建 Vue 应用实例
const app = createApp(App);
// 使用路由
app.use(router);
// 使用 Element Plus,并设置语言为中文
app.use(ElementPlus, {
    locale: zhCn,
});
// 动态注册所有图标组件
Object.keys(Icons).forEach(key => {
    app.component(key, Icons[key]);
});
// 挂载应用到 #app 元素上
app.mount('#app');
​

3.编写登陆界面

<template>
  <div class="login-container">
    <!-- 背景轮播图 -->
    <el-carousel :interval="5000" indicator-position="none" class="background-carousel" height="100vh">
      <el-carousel-item v-for="(image, index) in backgroundImages" :key="index">
        <div class="carousel-image" :style="{ backgroundImage: `url(${image})` }"></div>
      </el-carousel-item>
    </el-carousel>
​
    <!-- 登录框 -->
    <div class="login-box">
      <div class="title-container">
        <h3 class="title">实验室信息管理系统</h3>
      </div>
      <el-form ref="formRef" :model="form" :rules="rules" class="login-form">
        <el-form-item prop="number">
          <el-input v-model="form.number" placeholder="请输入学工号" prefix-icon="User"></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input v-model="form.password" placeholder="请输入密码" show-password prefix-icon="Lock"></el-input>
        </el-form-item>
        <div class="button-group">
          <el-button type="primary" @click="handleLogin" class="login-btn">登 录</el-button>
          <el-button type="success" @click="goRegister" class="register-btn">注 册</el-button>
        </div>
      </el-form>
    </div>
  </div>
</template>
​
​
<script setup>
import { ref, reactive } from 'vue';
import { ElMessage } from 'element-plus';
import { useRouter } from 'vue-router';
​
// 创建路由实例
const router = useRouter();
​
// 表单引用
const formRef = ref(null);
​
// 表单数据
const form = reactive({
  number: '',
  password: ''
});
​
// 表单规则
const rules = reactive({
  number: [
    { required: true, message: '请输入学号/学工号', trigger: 'blur' }
  ],
  password: [
    { required: true, message: '请输入密码', trigger: 'blur' }
  ]
});
​
// 背景图片数组
const backgroundImages = [
  new URL('https://pic.meitukk.com/uploads/meitukk/dm/20230621/230621120150827.jpg').href,
  new URL('https://pic.meitukk.com/uploads/meitukk/dm/20230622/230622045223599.jpg').href,
  new URL('https://img.shetu66.com/2022/10/28/1666928035720956.jpg').href,
  new URL('https://n.sinaimg.cn/sinacn10116/600/w1920h1080/20190326/2c30-hutwezf6832339.jpg').href,
  new URL('https://c-ssl.duitang.com/uploads/blog/202303/15/20230315122347_db706.jpg').href
];
​
// 登录方法
const handleLogin = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      // 这里添加登录逻辑,例如调用API进行验证
      console.log('submit!');
      ElMessage.success('登录成功');
      // 假设登录成功后跳转到主页
      router.push('/');
    } else {
      console.log('error submit!!');
      return false;
    }
  });
};
​
// 注册方法
const goRegister = () => {
  // 这里添加注册逻辑,例如跳转到注册页面
  router.push('/register');
};
</script>
</script>
<style scoped>
.login-container {
  width: 100vw; /* 使用视口宽度 */
  height: 100vh; /* 使用视口高度 */
  overflow: hidden;
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
}
​
.background-carousel {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%; /* 修改为全屏高度 */
  z-index: -1; /* 确保轮播图在最底层 */
  overflow: hidden;
}
​
.carousel-image {
  width: 100%;
  height: 100%;
  background-size: cover;
  background-position: center; /* 确保背景图片居中 */
  background-repeat: no-repeat;
}
​
.login-box {
  width: 400px;
  padding: 40px;
  background-color: rgba(255, 255, 255, 0.8);
  border-radius: 10px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  text-align: center;
  position: relative;
  z-index: 1; /* 确保登录框在轮播图之上 */
}
​
.title-container {
  margin-bottom: 30px;
}
​
.title {
  color: #2c3e50;
  font-size: 26px;
  font-weight: bold;
}
​
.login-form {
  padding: 0 30px;
}
​
.button-group {
  display: flex;
  justify-content: space-between;
}
​
.login-btn,
.register-btn {
  width: 48%;
  margin-top: 15px;
}
</style>
  • 实现效果

4.编写注册页面

<template>
  <div class="register-container">
    <!-- 背景轮播图 -->
    <transition name="fade" mode="out-in">
      <el-carousel v-if="carouselLoaded" :interval="5000" indicator-position="none" class="background-carousel" height="100vh">
        <el-carousel-item v-for="(image, index) in backgroundImages" :key="index">
          <div class="carousel-image" :style="{ backgroundImage: `url(${image})` }"></div>
        </el-carousel-item>
      </el-carousel>
    </transition>
​
    <!-- 注册框 -->
    <div class="register-box">
      <div class="title-container">
        <h3 class="title">欢迎注册</h3>
      </div>
      <el-form ref="formRef" :model="form" :rules="rules" class="register-form" @submit.prevent>
        <el-form-item prop="number">
          <el-input v-model="form.number" placeholder="请输入学工号"></el-input>
        </el-form-item>
        <el-form-item prop="nickname">
          <el-input v-model="form.nickname" placeholder="请输入姓名"></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input v-model="form.password" placeholder="请输入密码" show-password></el-input>
        </el-form-item>
        <el-form-item prop="confirm">
          <el-input v-model="form.confirm" placeholder="请再次输入密码" show-password></el-input>
        </el-form-item>
        <div class="button-group">
          <el-button type="primary" @click="handleRegister" class="register-btn">注 册</el-button>
          <el-button type="success" @click="goLogin" class="login-btn">登 录</el-button>
        </div>
      </el-form>
    </div>
  </div>
</template>
<script setup>
import {onMounted, reactive, ref} from 'vue';
import {useRouter} from 'vue-router';
import {ElMessage} from 'element-plus';
import axios from 'axios';
​
// 创建路由实例
const router = useRouter();
​
// 表单引用
const formRef = ref(null);
​
// 表单数据
const form = reactive({
  number: '',
  nickname: '',
  password: '',
  confirm: ''
});
​
// 表单规则
const rules = reactive({
  number: [
    { required: true, message: '请输入学工号', trigger: 'blur' }
  ],
  nickname: [
    { required: true, message: '请输入姓名', trigger: 'blur' }
  ],
  password: [
    { required: true, message: '请输入密码', trigger: 'blur' }
  ],
  confirm: [
    { required: true, message: '请确认密码', trigger: 'blur' }
  ]
});
​
// 背景图片加载状态
const carouselLoaded = ref(false);
const backgroundImages = ref([]);
​
onMounted(async () => {
  try {
    // 使用相对路径引入图片
    // 直接将图片路径赋值给 backgroundImages
    backgroundImages.value = [
      new URL('https://pic.meitukk.com/uploads/meitukk/dm/20230621/230621120150827.jpg').href,
      new URL('https://pic.meitukk.com/uploads/meitukk/dm/20230622/230622045223599.jpg').href,
      new URL('https://img.shetu66.com/2022/10/28/1666928035720956.jpg').href,
      new URL('https://n.sinaimg.cn/sinacn10116/600/w1920h1080/20190326/2c30-hutwezf6832339.jpg').href,
      new URL('https://c-ssl.duitang.com/uploads/blog/202303/15/20230315122347_db706.jpg').href
​
    ];
    carouselLoaded.value = true;
  } catch (error) {
    console.error('Failed to load images:', error);
  }
});
​
// 注册方法
const handleRegister = () => {
  if (form.password !== form.confirm) {
    ElMessage({
      type: 'error',
      message: '密码不一致'
    });
    return;
  }
  formRef.value.validate((valid) => {
    if (valid) {
      axios.post('http://localhost:9090/user/register', form)
          .then(res => {
            if (res.data.code === '0') {
              ElMessage({
                type: 'success',
                message: '注册成功'
              });
              router.push('/login');
            } else {
              ElMessage({
                type: 'error',
                message: res.data.msg
              });
            }
          })
          .catch(error => {
            console.error('There was an error!', error);
            ElMessage({
              type: 'error',
              message: '服务器错误,请稍后再试'
            });
          });
    }
  });
};
​
// 登录方法
const goLogin = () => {
  router.push('/login');
};
</script>
<style scoped>
.register-container {
  width: 100vw; /* 使用视口宽度 */
  height: 100vh; /* 使用视口高度 */
  overflow: hidden;
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
}
.background-carousel {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%; /* 修改为全屏高度 */
  z-index: -1; /* 确保轮播图在最底层 */
  overflow: hidden;
}
.carousel-image {
  width: 100%;
  height: 100%;
  background-size: cover;
  background-position: center; /* 确保背景图片居中 */
  background-repeat: no-repeat;
  transition: opacity 0.5s ease;
}
.register-box {
  width: 400px;
  padding: 40px;
  background-color: rgba(255, 255, 255, 0.8);
  border-radius: 10px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  text-align: center;
  position: relative;
  z-index: 1; /* 确保注册框在轮播图之上 */
}
​
.title-container {
  margin-bottom: 30px;
}
​
.title {
  color: #2c3e50;
  font-size: 30px;
  font-weight: bold;
}
​
.register-form {
  padding: 0 30px;
}
​
.button-group {
  display: flex;
  justify-content: space-between;
  margin-top: 20px;
}
​
.register-btn,
.login-btn {
  width: 48%; 
}
​
/* 隐藏前缀图标 */
.el-input__prefix {
  display: none;
}
</style>
  • 实现效果

5.编写主页面

  • 主界面父组件

<script setup>
import Header from "../components/Header.vue";
import Aside from "../components/Aside.vue";
</script>
​
<template>
  <div>
    <!--  头部  -->
    <Header />
    <!--   主体   -->
    <div style="display: flex">
      <!--      侧边栏  -->
      <Aside />
      <!--     内容区域   -->
      <router-view style="flex: 1"/>
    </div>
  </div>
</template>
<style scoped>
​
</style>
  • 子组件侧边栏

<template>
  <div class="aside-container">
    <el-menu default-active="1-1" class="el-menu-vertical-demo" :collapse="isCollapsed" @open="handleOpen" @close="handleClose">
      <!-- 个人中心 -->
      <el-sub-menu index="1-1">
        <template #title>
          <el-icon><User /></el-icon>
          <span>个人中心</span>
        </template>
        <el-menu-item index="1-1-1">
          <el-icon><HomeFilled /></el-icon>
          <span>信息维护</span>
        </el-menu-item>
      </el-sub-menu>
      <!-- 系统管理 -->
      <el-sub-menu index="1-2">
        <template #title>
          <el-icon><Setting /></el-icon>
          <span>系统管理</span>
        </template>
        <el-menu-item index="1-2-1">
          <el-icon><VideoCamera /></el-icon>
          <span>设备管理</span>
        </el-menu-item>
        <el-menu-item index="1-2-2">
          <el-icon><Mic /></el-icon>
          <span>学生管理</span>
        </el-menu-item>
        <el-menu-item index="1-2-3">
          <el-icon><IceTea /></el-icon>
          <span>实验室管理</span>
        </el-menu-item>
      </el-sub-menu>
      <!-- 记录查询 -->
      <el-sub-menu index="1-3">
        <template #title>
          <el-icon><MessageBox /></el-icon>
          <span>记录查询</span>
        </template>
​
          <el-menu-item index="1-3-1">
            <el-icon><Document /></el-icon>
            <span>设备申请记录</span>
          </el-menu-item>
          <el-menu-item index="1-3-2">
            <el-icon><DeleteFilled /></el-icon>
            <span>设备报废记录</span>
          </el-menu-item>
          <el-menu-item index="1-3-3">
            <el-icon><TrendCharts /></el-icon>
            <span>设备借出记录</span>
          </el-menu-item>
          <el-menu-item index="1-3-4">
            <el-icon><Calendar /></el-icon>
            <span>实验室预约记录</span>
          </el-menu-item>
​
      </el-sub-menu>
      <!-- 设备相关申请 -->
      <el-sub-menu index="1-4">
        <template #title>
          <el-icon><SetUp /></el-icon>
          <span>设备相关申请</span>
        </template>
          <el-menu-item index="1-4-1">
            <el-icon><Delete /></el-icon>
            <span>设备报废申请</span>
          </el-menu-item>
          <el-menu-item index="1-4-2">
            <el-icon><Tickets /></el-icon>
            <span>设备使用申请</span>
          </el-menu-item>
          <el-menu-item index="1-4-3">
            <el-icon><CircleCheckFilled /></el-icon>
            <span>设备归还申请</span>
          </el-menu-item>
      </el-sub-menu>
      <!-- 实验室相关申请 -->
      <el-sub-menu index="1-5">
        <template #title>
          <el-icon><Plus /></el-icon>
          <span>实验室相关申请</span>
        </template>
          <el-menu-item index="1-5-1" >
            <el-icon><Ticket /></el-icon>
            <span>预约实验室</span>
          </el-menu-item>
          <el-menu-item index="1-5-2">
            <el-icon><CircleCloseFilled /></el-icon>
            <span>归还实验室</span>
          </el-menu-item>
      </el-sub-menu>
​
      <!-- 审批进度 -->
      <el-sub-menu index="1-6">
        <template #title>
          <el-icon><Check /></el-icon>
          <span>审批进度</span>
        </template>
          <el-menu-item index="1-6-1">
            <el-icon><InfoFilled /></el-icon>
            <span>设备申请审批</span>
          </el-menu-item>
          <el-menu-item index="1-6-2">
            <el-icon><DeleteFilled /></el-icon>
            <span>设备报废审批</span>
          </el-menu-item>
          <el-menu-item index="1-6-3">
            <el-icon><Ticket /></el-icon>
            <span>实验室预约审批</span>
          </el-menu-item>
      </el-sub-menu>
      <!-- 数据可视化 -->
      <el-sub-menu index="1-8">
        <template #title>
          <el-icon><PieChart /></el-icon> 设备数据可视化
        </template>
          <el-menu-item index="1-8-1">
            <el-icon><DataAnalysis /></el-icon> 设备数据可视化
          </el-menu-item>
      </el-sub-menu>
    </el-menu>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import {
  Setting,
  User,
  HomeFilled,
  VideoCamera,
  Mic,
  IceTea,
  MessageBox,
  Document,
  DeleteFilled,
  TrendCharts,
  Calendar,
  SetUp,
  Delete,
  Tickets,
  CircleCheckFilled,
  Plus,
  Ticket,
  CircleCloseFilled,
  Check,
  InfoFilled
} from '@element-plus/icons-vue';
​
// 控制菜单折叠状态
const isCollapsed = ref(false);
​
// 处理子菜单打开事件
const handleOpen = (key, keyPath) => {
  console.log('opened sub menu:', key, keyPath);
};
​
// 处理子菜单关闭事件
const handleClose = (key, keyPath) => {
  console.log('closed sub menu:', key, keyPath);
};
</script>
<style scoped>
.aside-container {
  width: 200px;
  transition: width 0.3s ease;
}
​
.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 200px;
  min-height: 400px;
}
​
.el-menu {
  border-right: none;
}
​
.el-menu-item [class^=el-icon-]+span {
  margin-left: 8px;
}
​
.menu-item-group-title {
  padding: 8px 20px;
  color: #a8abb2;
  font-size: 12px;
  line-height: 24px;
  background-color: #f5f7fa;
}
​
.el-menu-item.is-active {
  background-color: #ecf5ff;
  color: #409eff;
}
​
.el-sub-menu__title:hover,
.el-menu-item:hover {
  background-color: #eef1f6 !important;
}
​
@media (max-width: 768px) {
  .aside-container {
    width: 64px;
  }
​
  .el-menu-vertical-demo:not(.el-menu--collapse) {
    width: 64px;
  }
​
  .el-sub-menu__title span,
  .el-menu-item span {
    display: none;
  }
​
  .el-sub-menu__icon-arrow {
    display: none;
  }
}
</style>
  • 子组件头部导航栏

<template>
  <header class="app-header">
    <div class="header-content">
      <div class="logo">实验室信息管理系统</div>
      <div class="user-action">
        <el-dropdown trigger="click" @command="handleCommand">
          <span class="el-dropdown-link user-profile" @click.stop>
            <el-avatar :size="30" :src="defaultAvatar" style="vertical-align: middle;"></el-avatar>
            <span class="username">{{ username }}</span>
            <el-icon class="el-icon--right"><arrow-down /></el-icon>
          </span>
          <template #dropdown>
            <el-dropdown-menu>
              <el-dropdown-item command="changePassword">修改密码</el-dropdown-item>
              <el-dropdown-item command="logout">退出系统</el-dropdown-item>
            </el-dropdown-menu>
          </template>
        </el-dropdown>
        <!-- 修改密码对话框 -->
        <el-dialog
            title="修改密码"
            v-model="dialogVisible"
            width="400px"
            center
            @close="resetForm"
        >
          <el-form ref="passwordForm" :model="form" :rules="rules" label-width="100px">
            <el-form-item label="旧密码" prop="oldPassword">
              <el-input v-model="form.oldPassword" type="password"></el-input>
            </el-form-item>
            <el-form-item label="新密码" prop="newPassword">
              <el-input v-model="form.newPassword" type="password"></el-input>
            </el-form-item>
            <el-form-item label="确认密码" prop="confirmPassword">
              <el-input v-model="form.confirmPassword" type="password"></el-input>
            </el-form-item>
          </el-form>
          <template #footer>
            <span class="dialog-footer">
              <el-button @click="dialogVisible = false">取消</el-button>
              <el-button type="primary" @click="submitForm('passwordForm')">确定</el-button>
            </span>
          </template>
        </el-dialog>
      </div>
    </div>
  </header>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { ArrowDown } from '@element-plus/icons-vue';
// 假定用户名称和默认头像链接
const username = ref('张三');
const defaultAvatar = ref('https://pic.meitukk.com/uploads/meitukk/dm/20230621/230621120150827.jpg');
​
// 对话框可见性控制
const dialogVisible = ref(false);
​
// 表单数据和验证规则
const form = reactive({
  oldPassword: '',
  newPassword: '',
  confirmPassword: ''
});
​
const rules = {
  oldPassword: [{ required: true, message: '请输入旧密码', trigger: 'blur' }],
  newPassword: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
  confirmPassword: [
    { required: true, message: '请再次输入新密码', trigger: 'blur' },
    ({ value }, callback) => {
      if (value !== form.newPassword) {
        callback(new Error('两次输入的密码不一致'));
      } else {
        callback();
      }
    }
  ]
};
​
// 提交表单的方法
const submitForm = (formName) => {
  // 这里可以添加提交逻辑,例如发送请求到服务器等
  console.log('Submit:', form);
  dialogVisible.value = false;
};
​
// 重置表单的方法
const resetForm = () => {
  form.oldPassword = '';
  form.newPassword = '';
  form.confirmPassword = '';
};
​
// 下拉菜单命令处理
const handleCommand = (command) => {
  if (command === 'changePassword') {
    dialogVisible.value = true;
  } else if (command === 'logout') {
    // 处理登出逻辑
    console.log('Logout');
  }
};
</script>
<style scoped>
.app-header {
  height: 60px;
  line-height: 60px;
  background-color: #304156;
  color: #F8F8FF;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
​
.header-content {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
​
.logo {
  font-weight: bold;
  font-size: 18px;
}
​
.user-action {
  display: flex;
  align-items: center;
}
​
.user-profile {
  cursor: pointer;
  display: flex;
  align-items: center;
  color: #F8F8FF;
}
​
.username {
  margin-left: 8px;
}
​
.el-dropdown-link {
  display: flex;
  align-items: center;
}
​
@media (max-width: 768px) {
  .logo {
    font-size: 16px;
  }
​
  .user-action {
    flex-direction: column;
    align-items: flex-end;
  }
​
  .user-profile {
    flex-direction: column;
    align-items: flex-start;
  }
​
  .el-avatar {
    margin-bottom: 5px;
  }
}
​
/* 新增样式 */
.el-dropdown-menu__item {
  text-align: left;
}
​
.dialog-footer {
  text-align: right;
}
​
.el-dialog__header {
  background-color: #f5f7fa;
  padding: 16px 20px;
  border-bottom: 1px solid #ebeef5;
}
​
.el-dialog__body {
  padding: 20px;
}
​
.el-form-item {
  margin-bottom: 18px;
}
​
.el-input__inner {
  border-radius: 4px;
}
</style>
  • 实现效果

6.编写路由

import { createRouter, createWebHistory } from 'vue-router'
import LayOut from "../layout/LayOut.vue";
import Login from "../views/Login.vue";
import Register from "../views/Register.vue";
export const router = createRouter({
    history: createWebHistory(),
    routes:[
        {
            path: '/',
            name: 'Layout',
            component: LayOut,
        },
        {
            path:'/login',
            name:'Login',
            component:Login
        },
        {
            path:'/register',
            name:'Register',
            component:Register
        }
    ]
})

7.全局样式

*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}


网站公告

今日签到

点亮在社区的每一天
去签到