超详细的前后端实战项目(Spring系列加上vue3)前端篇(二)(一步步实现+源码)

news2024/11/24 19:28:08

好了,兄弟们,继昨天的项目之后,开始继续敲前端代码,完成前端部分

昨天完成了全局页面的代码,和登录页面的代码,不过昨天的代码还有一些需要补充的,这里添加一下

内容补充:在调用登录接口之后进行一个页面的跳转

先介绍一下router路由的使用吧(知道的兄弟们可以跳过哦)

页面的路由:1.安装vue-router :npm install vue-router @4 (在终端输入命令即可)。2.在index.js中创建路由器并导出。3.main.js中使用,4.<router-view>中展示

(这里因为刚开始创建项目的时候点击了vue Router,脚手架已经帮我们生成好了,那就不用管了,前三步自动完成,我们只管使用就好了)

这里在Login页面中添加

import {useRouter} from 'vue-router'
const router = useRouter()
const login = async()=>{
    let result = await userLoginService(loginData.value);
    renderIcon(result.msg?result.msg:'登录成功');
    router.push('/')
}

添加完这几行代码就OK了,这里我们可以试一下能不能跳转,

emmmm,因为暂时没有进行后端的开发,如果要测试的话,此时要么把调用后端的函数给注释了,要么使用接口测试工具(感觉好麻烦)

<script setup>
// import { h } from 'vue';
// import { IconExclamationCircleFill } from '@arco-design/web-vue/es/icon';
import { ref } from "vue";
// import {userLoginService} from '../api/user'
import {useRouter} from 'vue-router'
const router = useRouter()
const loginData = ref({
  username: "",
  password: "",
  isRead: false,
});


const rules = [
  {
    validator: (value, cb) => {
      return new Promise((resolve) => {
        window.setTimeout(() => {
          if (value !== " ") {
            cb("content not empty");
          }
          resolve();
        }, 1000);
      });
    },
  },
];
// const renderIcon = () => h(IconExclamationCircleFill);
const login = async()=>{
    // let result = await userLoginService(loginData.value);
    // renderIcon(result.msg?result.msg:'登录成功');
    router.push('/')
}

</script>

方便起见,还是用注释吧。

OK,我们来运行一下代码

在点击了Submit之后,成功跳转到了主页面,(记得把函数的注释去掉)

内容补充,GlobalPage.vue的样式修改(改了一下布局,现在能全局了)

<style scoped>
.global{
  height: 100vh;
}
.layout-demo :deep(.arco-layout-header),
.layout-demo :deep(.arco-layout-footer),
.layout-demo :deep(.arco-layout-sider-children),
.layout-demo :deep(.arco-layout-content) {
  display: flex;
  flex-direction: column;
  justify-content: center;
  color: var(--color-white);
  font-size: 16px;
  font-stretch: condensed;
  text-align: center;
}

.layout-demo :deep(.arco-layout-header),
.layout-demo :deep(.arco-layout-footer) {
  height: 64px;
  background-color: var(--color-primary-light-4);
}

.layout-demo :deep(.arco-layout-sider) {
  width: 206px;
  background-color: var(--color-primary-light-3);
}

.layout-demo :deep(.arco-layout-content) {
  background-color: rgb(var(--arcoblue-6));
}

.footer {
  background-color: aliceblue;
  padding: 16px;
  position: sticky;
  bottom: 0;
  left: 0;
  right: 0;
  text-align: center;
  letter-spacing: 3px;
}
</style>

内容补充: 在登录页补充开发一下注册页面。

之前只写了登录页面,当然也得有注册页面,来打造一个

其实只要有了登录页面,对于注册页面也就不难了,

    <div class="login-box" v-if="!isLogin">
      <a-form
        :model="loginData"
        :style="{ width: '600px' }"
        @submit="handleSubmit"
        class="input-box"
      >
        <h2 style="margin-bottom: 60px">Login</h2>
        <a-form-item
          field="name"
          tooltip="Please enter username"
          label="账号"
          class="element"
          :rules="rules"
        >
          <a-input
            v-model="loginData.username"
            placeholder="please enter your username..."
            style="margin-right: 40px"
          />
        </a-form-item>
        <a-form-item field="post" label="密码" class="element" :rules="rules">
          <a-input
            v-model="loginData.password"
            placeholder="please enter your password..."
            style="margin-right: 40px"
          />
        </a-form-item>
        <a-form-item field="isRead">
          <a-checkbox v-model="loginData.isRead">
            I have read the manual
          </a-checkbox>
        </a-form-item>
        <a-form-item>
          <a-button html-type="submit" class="input-box" @click="login"
            >Submit</a-button
          >
        </a-form-item>
        <div class="register-link">
          <a @click="isLogin = !isLogin">Don't have an account? Create Now!</a>
        </div>
      </a-form>
    </div>

这是之前登录页面的代码,我们直接把它复制一份,再把h2标签的login改成register就好了

此时页面上就出现了两个表单,还记得之前的搜素框控制显示隐藏吗,这个也是这样,甚至更加简单,

    <div class="login-box" v-if="!isLogin">在div标签这里用v-if来控制两个,

回忆之前的组件开发三部曲:前端样式代码(html,css)已经完成,下面开始数据绑定与事件

这里我们在script代码中加上

const isLogin = ref(false)  //记得引入ref(之前好像引入过了)

那么:登录页的div:     <div class="login-box" v-if="!isLogin">

           注册页的div:     <div class="login-box" v-if="isLogin">

这样就成功控制了一个显示一个隐藏的效果了(注意其中的逻辑关系,要让login先显示)

然后就是控制页面的转换了,当点击Don't have an account? Create Now!就转换到注册页,那么可以

        <div class="register-link">

          <a @click="isLogin = !isLogin">Don't have an account? Create Now!</a>

        </div>

加一个点击效果进行转换

除此之外,注册页面用的输入框可跟登录页不一样,这里可以去组件库找找

这里可以使用其中的一些输入框和表单校验方法,不过数据绑定这块建议继续用ref

然后我们前往api/user.js,为用户注册写调用后端接口方法(这里跟登录差不多)

import request from '../utils/request'

//创建一个调用登录接口函数
export const userLoginService = (loginData) =>{
    //用urlSearchParams完成传递
    const params = new URLSearchParams()
    for(let key in loginData){
        params.append(key,loginData[key]);
    }
    return request.post('/user/login',params);
}

export const userRegisterService = (registerData) =>{
    //用urlSearchParams完成传递
    const params = new URLSearchParams()
    for(let key in registerData){
        params.append(key,registerData[key]);
    }
    return request.post('/user/register',params);
}

不能说长得很像,只能说根本就是一模一样。。

然后我们在Login登录界面编写调用后端接口方法

const register = async () => {

  let result = await userRegisterService(registerData.value);

  renderIcon(result.msg ? result.msg : "注册成功");

};

然后在上面submit按钮上绑定@click=“register”即可

下面来展示一下登录页目前的全部代码:

<template>
  <div class="container">
    <div class="login-box" v-if="!isLogin">
      <a-form
        :model="loginData"
        :style="{ width: '600px' }"
        @submit="handleSubmit"
        class="input-box"
      >
        <h2 style="margin-bottom: 60px">Login</h2>
        <a-form-item
          field="name"
          tooltip="Please enter username"
          label="账号"
          class="element"
          :rules="rules"
        >
          <a-input
            v-model="loginData.username"
            placeholder="please enter your username..."
            style="margin-right: 40px"
          />
        </a-form-item>
        <a-form-item field="post" label="密码" class="element" :rules="loginRules">
          <a-input
            v-model="loginData.password"
            placeholder="please enter your password..."
            style="margin-right: 40px"
          />
        </a-form-item>
        <a-form-item field="isRead">
          <a-checkbox v-model="loginData.isRead">
            I have read the manual
          </a-checkbox>
        </a-form-item>
        <a-form-item>
          <a-button html-type="submit" class="input-box" @click="login"
            >Enter</a-button
          >
        </a-form-item>
        <div class="register-link">
          <a @click="isLogin = !isLogin">Don't have an account? Create Now!</a>
        </div>
      </a-form>
    </div>

    <div class="login-box" v-if="isLogin">
      <a-form
        :model="loginData"
        :style="{ width: '600px' }"
        @submit="handleSubmit"
        class="input-box"
      >
        <h2 style="margin-bottom: 60px">Register</h2>
        <a-form
          ref="formRef"
          :rules="regRules"
          :model="registerData"
          :style="{ width: '600px' }"
          @submit="handleSubmit"
        >
          <a-form-item field="name" label="账号" validate-trigger="blur">
            <a-input
              v-model="registerData.name"
              placeholder="please enter your username..."
                          style="margin-right: 60px"
            />
          </a-form-item>
          <a-form-item field="password" label="密码" validate-trigger="blur">
            <a-input-password
              v-model="registerData.password"
              placeholder="please enter your password..."
                          style="margin-right: 60px"
            />
          </a-form-item>
          <a-form-item
            field="password2"
            label="确认密码"
            validate-trigger="blur"
          >
            <a-input-password
              v-model="registerData.password2"
              placeholder="please confirm your password..."
                          style="margin-right: 60px"
            />
          </a-form-item>
          <a-form-item field="email" label="email">
            <a-input
              v-model="registerData.email"
              placeholder="please enter your email..."
                          style="margin-right: 60px"
            />
          </a-form-item>
        </a-form>
        <a-form-item>
          <a-button html-type="submit" class="input-box" @click="register"
            >Submit</a-button
          >
        </a-form-item>
        <div class="register-link">
          <a @click="isLogin = !isLogin">Have Count? Login Now!</a>
        </div>
      </a-form>
    </div>
  </div>
</template>
<script setup>
import { h } from "vue";
import { IconExclamationCircleFill } from "@arco-design/web-vue/es/icon";
import { ref } from "vue";
import { userLoginService, userRegisterService } from "../api/user";
import { useRouter } from "vue-router";
const router = useRouter();
const loginData = ref({
  username: "",
  password: "",
  isRead: false,
});

const loginRules = [
  {
    validator: (value, cb) => {
      return new Promise((resolve) => {
        window.setTimeout(() => {
          if (value !== " ") {
            cb("content not empty");
          }
          resolve();
        }, 1000);
      });
    },
  },
];
const renderIcon = () => h(IconExclamationCircleFill);
const login = async () => {
  let result = await userLoginService(loginData.value);
  renderIcon(result.msg ? result.msg : "登录成功");
  router.push("/");
};

const register = async () => {
  let result = await userRegisterService(registerData.value);
  renderIcon(result.msg ? result.msg : "登录成功");
};

//登录注册页显示隐藏
const isLogin = ref(false);

const handleSubmit = ({ values, errors }) => {
  console.log("values:", values, "\nerrors:", errors);
};
const registerData = ref({
  name: "",
  password: "",
  password2: "",
  email: ""
});
 const regRules = {
      name: [
        {
          required: true,
          message:'name is required',
        },
      ],
      password: [
        {
          required: true,
          message:'password is required',
        },
      ],
      password2: [
        {
          required: true,
          message:'password is required',
        },
        {
          validator: (value, cb) => {
            if (value !== registerData.value.password) {
              cb('two passwords do not match')
            } else {
              cb()
            }
          }
        }
      ],
      email: [
        {
          type: 'email',
          required: true,
        }
      ],
  }
  

</script>

<style scoped>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Poppins", sans-serif;
  display: flex;
  justify-content: center;
}
.container {
  width: 100%;
  height: 100vh;
  background: url("../assets/background.jpg") no-repeat;
  background-size: cover;
  background-position: center;
  display: flex;
  justify-content: center;
  align-items: center;
}

.container .login-box {
  position: relative;
  width: 500px;
  height: 580px;
  background-color: transparent;
  border: 2px solid rgba(255, 255, 255, 0.5);
  border-radius: 20px;
  display: flex;
  justify-content: center;
  align-items: center;
  backdrop-filter: blur(15px);
}

.login-box h2 {
  font-size: 28px;
  color: #fff;
  text-align: center;
}

.login-box .input-box {
  position: relative;
  width: 310px;
  margin: 30px 0;
}

.input-box input {
  width: 80%;
  height: 60px;
  background: transparent;
  border: none;
  outline: none;
  font-size: 16px;
  color: #fff;
  padding: 0 2px 0 5px;
}

.input-box input::placeholder {
  color: #f9f9f9;
}

.input-box .icon {
  position: absolute;
  right: 8px;
  color: #fff;
  font-size: 16px;
  line-height: 25px;
}

.login-box .remember-forget {
  margin: -15px 0 15px;
  font-size: 15px;
  color: #fff;
  display: flex;
  justify-content: space-between;
}

.remember-forget label input {
  margin-right: 30px;
}

.login-box button {
  width: 100%;
  height: 40px;
  background: #fff;
  border: none;
  outline: none;
  border-radius: 40px;
  cursor: pointer;
  font-size: 16px;
  color: #000;
  transition: all 0.5s;
}

.login-box button:hover {
  background: #1f73c9;
  color: #fff;
}

.login-box .register-link {
  font-size: 15px;
  color: #fff;
  text-align: center;
  margin: 5px 0 5px;
}

.remember-forget a,
.register-link a {
  color: #fff;
  text-decoration: none;
}

.remember-forget a:hover,
.register-link a:hover {
  text-decoration: underline;
}

/* Responsive Design */
@media (max-width: 460px) {
  .container .login-box {
    width: 350px;
  }

  .login-box .input-box {
    width: 290px;
  }
}

@media (max-width: 360px) {
  .container .login-box {
    width: 100%;
    height: 100vh;
    border: none;
  }
}
.element {
  margin: 20px 0;
}
</style>

全局页面跳转解决:

既然都讲到页面路由了,就多练练吧,

在全局页面(GlobalPage.vue)这一块我们把之前写死的searchPage去掉改成可变的router-view

<template>
  <a-layout style="height: 400px">
    <a-layout-header><global-header></global-header></a-layout-header>
    <a-layout>
      <a-layout-sider theme="dark">Sider</a-layout-sider>
      <a-layout-content><router-view></router-view></a-layout-content><!--就是这里-->

    </a-layout>
    <a-layout-footer>
      <global-footer class="footer"></global-footer>
    </a-layout-footer>
  </a-layout>
</template>

然后就可以开始布置了,现在点击GlobalHeader.vue全局头部,

<template>
  <div class="menu-demo">
    <a-menu mode="horizontal" theme="dark" :default-selected-keys="['1']">
      <a-menu-item
        key="0"
        :style="{ padding: 0, marginRight: '38px' }"
        disabled
        class="logo"
      >
        <div
          :style="{
            width: '180px',
            height: '30px',
            background: 'var(--color-fill-3)',
            cursor: 'text',
          }"
        ><img src="../assets/logo.png" width="180px"></div>
      </a-menu-item>
      <a-menu-item key="1"><router-link to="/main/search">全局搜索</router-link></a-menu-item>
      <a-menu-item key="2"><router-link to="/main/article">文章大全</router-link></a-menu-item>
      <a-menu-item key="3">Cloud Service</a-menu-item>
      <a-menu-item key="4">Cooperation</a-menu-item>
    </a-menu>

这里修改一下a-menu-item标签,(一般在组件库这里会有专门的跳转属性这种,不过今天好像组件库页面打不开了,那暂时就用原始版的router-link来实现一下吧,(之后我会修改一下的))

这里布置了两个页面,全局搜素和文章大全,进行跳转,然后呢,去index.js处创建一下主页面的子路由,

下面,看代码展示

import { createRouter, createWebHashHistory } from 'vue-router'
import MainPage from '../components/GlobalPage.vue'
import LoginPage from '../components/LoginPage.vue'
import SearchPage from '../views/SearchPage.vue'
import ArticlePage from '../views/ArticlePage.vue'
const routes = [
  {
    path: '/',
    name: 'mainPage',
    component: MainPage,
    redirect: '/main/search',
    children: [
      {
        path: '/main/search',
        component: SearchPage
      },
      {
        path: '/main/article',
        component: ArticlePage
      }
    ]
  },
  {
    path: '/login',
    name: 'loginPage',
    component: LoginPage
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

这里我目前添加了全局搜素和文章大全两个子页面(小细节:redirect: '/main/search',这里我使用了一个重定向,让每次进入‘/’页面的时候都会跳转到全局搜素页面处),全局搜素的页面之前已经写过了,那也就不用管了,这里我们添加以下文章大全的页面,在views文件夹下创建ArticlePage.vue文件用<vue>创建基本样子,

<template>
  <div id="article">
    文章篇
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

先随便写一点吧,主要是要能现在展现以下跳转。

目前来看就差不多了,来试试能不能实现跳转。

好嘞,那么现在也就实现了这一功能,

好了,目前主要的一些前端样子也就差不多了,这一期的项目主要还是练练手,就做一些最基本文章管理这些吧。

文章管理界面开发:

既然写界面,我觉得还是先来一手布局比较合适

来吧,组件库见:

这里我没想太多,直接用这个朴实无华的布局吧。

先用代码写个初始布局

<template>
  <div id="article">
        <a-divider />
    <a-layout style="height: 400px;">
      <a-layout-header>Header</a-layout-header>
       <a-divider orientation="left">热点文章</a-divider>
      <a-layout-content>Content</a-layout-content>
      <a-layout-footer>Footer</a-layout-footer>
    </a-layout>
    

  </div>
</template>

<script>
export default {

}
</script>

<style scoped>
body {
  margin: 0;
  background-color: #f5f5f5;
}

/* fade-slide */
.fade-slide-leave-active,
.fade-slide-enter-active {
  transition: all 0.3s;
}

.fade-slide-enter-from {
  transform: translateX(-30px);
  opacity: 0;
}

.fade-slide-leave-to {
  transform: translateX(30px);
  opacity: 0;
}
</style>

这里我加了一个带有文字的分割线

然后就是最关键的content内容区域了,这里可以加上一些样式,(如果对自己的前端没有太自信的话,直接组件库!)

好,看一手,我们先找一个滑动条

就这个了,

将滚动条放入content区域之后,小改一下代码,调整大小,适应一下屏幕大小就好看多了

<template>
  <div id="article">
    <a-divider />
    <a-layout style="height: 760px">
      <a-layout-header class="header">文章管理</a-layout-header>
      <a-divider orientation="left">Cetide-Net</a-divider>
      <a-layout-content>
        <a-scrollbar class="rollBar"
          style="height: 673.34px; overflow: auto; margin: 10px 20px">
          <div class="content">Content</div>
        </a-scrollbar>
      </a-layout-content>
    </a-layout>
  </div>
</template>

<script>
export default {};
</script>

<style scoped>
#article {
  margin: 20px;
}

/* fade-slide */
.fade-slide-leave-active,
.fade-slide-enter-active {
  transition: all 0.3s;
}

.fade-slide-enter-from {
  transform: translateX(-30px);
  opacity: 0;
}

.fade-slide-leave-to {
  transform: translateX(30px);
  opacity: 0;
}
.header {
  font-family: "Satisfy", cursive;
  font-size: 26px;
  color: rgb(93, 186, 216);
  padding-left: 50px;
  text-align: left;
}
.rollBar{
  
}
.content {
  height: 2000px;
  width: 1424px;
  background-color: var(--color-primary-light-4);
}
</style>

然后就是存放文章的了,这里可以找找组件库的列表组件:

这个组件就很奈斯(分页条,评论按键都有),因为这里主要是文章大全,所以就不用加上编辑和删除功能了,不过可以加上一个评论功能

这里加一个显示神评的吧,到时候把点赞数最多的评论放到让文章大全页面都能看到

修改一下代码,嵌入文章大全页面.

<template>
  <div id="article">
    <a-divider />
    <a-layout style="height: 760px">
      <a-layout-header class="header">文章管理</a-layout-header>
      <a-divider orientation="left">Cetide-Net</a-divider>
      <a-layout-content>
        <a-scrollbar class="rollBar"
          style="height: 673.34px; overflow: auto; margin: 10px 20px">
          <div class="content">

  <a-list
    class="list-demo-action-layout"
    :bordered="false"
    :data="dataSource"
    :pagination-props="paginationProps"
  >
    <template #item="{ item }">
      <a-list-item class="list-demo-item" action-layout="vertical">
        <template #actions>
          <span><icon-heart />点赞 83</span>
          <span><icon-star />收藏{{ item.index }}</span>
          <span><icon-message />评论</span>
        </template>
        
        <template #extra>
          <div className="image-area">
            <img alt="arco-design" :src="item.imageSrc" />
          </div>
        </template>

        <a-list-item-meta
          :title="item.title"
          :description="item.description"
        >
          <template #avatar>
            <a-avatar shape="square">
              <img alt="avatar" :src="item.avatar" />
            </a-avatar>
          </template>
        </a-list-item-meta>

        <div style="width:100%; text-align: left; padding-left:20px">
              <a-divider />
          <a-comment author="Socrates" content="Comment body content." datetime="1 hour">
    <template #avatar>
      <a-avatar>
        <img
          alt="avatar"
          src="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp"
        />
      </a-avatar>
    </template>
  </a-comment>
        </div>
      </a-list-item>
    </template>
  </a-list>

          </div>
        </a-scrollbar>
      </a-layout-content>
    </a-layout>
  </div>
</template>

<script>
import { reactive } from 'vue'

const names = ['Socrates', 'Balzac', 'Plato'];
const avatarSrc = [
  '//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/a8c8cdb109cb051163646151a4a5083b.png~tplv-uwbnlip3yd-webp.webp',
  '//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/e278888093bef8910e829486fb45dd69.png~tplv-uwbnlip3yd-webp.webp',
  '//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/9eeb1800d9b78349b24682c3518ac4a3.png~tplv-uwbnlip3yd-webp.webp',
];
const imageSrc = [
  '//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/29c1f9d7d17c503c5d7bf4e538cb7c4f.png~tplv-uwbnlip3yd-webp.webp',
  '//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/04d7bc31dd67dcdf380bc3f6aa07599f.png~tplv-uwbnlip3yd-webp.webp',
  '//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/1f61854a849a076318ed527c8fca1bbf.png~tplv-uwbnlip3yd-webp.webp',
];
const dataSource = new Array(15).fill(null).map((_, index) => {
  return {
    index: index,
    avatar: avatarSrc[index % avatarSrc.length],
    title: names[index % names.length],
    description:
      'Beijing ByteDance Technology Co., Ltd. is an enterprise located in China. ByteDance has products such as TikTok, Toutiao, volcano video and Douyin (the Chinese version of TikTok).',
    imageSrc: imageSrc[index % imageSrc.length],
  };
});

export default {
  setup() {
    return {
      dataSource,
      paginationProps: reactive({
        defaultPageSize: 3,
        total: dataSource.length
      })
    }
  },
}
</script>

<style scoped>
#article {
  margin: 20px;
}

/* fade-slide */
.fade-slide-leave-active,
.fade-slide-enter-active {
  transition: all 0.3s;
}

.fade-slide-enter-from {
  transform: translateX(-30px);
  opacity: 0;
}

.fade-slide-leave-to {
  transform: translateX(30px);
  opacity: 0;
}
.header {
  font-family: "Satisfy", cursive;
  font-size: 26px;
  color: rgb(93, 186, 216);
  padding-left: 50px;
  text-align: left;
}
.rollBar{
  
}
.content {
  /* height: 2000px; */
  width: 1424px;
}

.list-demo-action-layout .image-area {
  width: 183px;
  height: 119px;
  border-radius: 2px;
  overflow: hidden;
  
}

.list-demo-action-layout .list-demo-item {
  padding: 20px 0;
  border-bottom: 1px solid var(--color-fill-3);
}

.list-demo-action-layout .image-area img {
  width: 100%;
}

.list-demo-action-layout .arco-list-item-action .arco-icon {
  margin: 0 4px;
}
</style>

大家一定要看懂一些组件代码才好使用,(后续调用后端接口,使用后端数据库的时候就会用到)

这一期的项目,打算就先做一个文章管理页面,加上用户个人中心的东西就差不多了(主要还是先熟悉熟悉流程,后面可以看看加上一些更好的功能(也不能只是crud哈))

用户个人中心界面开发:

在component文件夹下面创建一个UserCentre.vue页面

先做一个雏形:

然后在index.js中注册页面

  {

    path: '/user',

    name: 'userPage',

    component: UserPage

 }

然后做一个页面跳转吧,这里我们回到GlobalHeader.vue全局导航栏区域,

<template>
  <div class="menu-demo">
    <a-menu mode="horizontal" theme="dark" :default-selected-keys="['1']">
      <a-menu-item
        key="0"
        :style="{ padding: 0, marginRight: '38px' }"
        disabled
        class="logo"
      >
        <div
          :style="{
            width: '180px',
            height: '30px',
            background: 'var(--color-fill-3)',
            cursor: 'text',
          }"
        ><img src="../assets/logo.png" width="180px"></div>
      </a-menu-item>
      <a-menu-item key="1"><router-link to="/main/search">全局搜索</router-link></a-menu-item>
      <a-menu-item key="2"><router-link to="/main/article">文章大全</router-link></a-menu-item>
      <a-menu-item key="3">Cloud Service</a-menu-item>
      <a-menu-item key="4">Cooperation</a-menu-item>
    </a-menu>
    <div class="user-avatar">
        <img src="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp"
          alt="User Avatar" class="avatar-image"/>
      <div
        class="dropdown"
        style="font-family: 'Satisfy', cursive; margin-right: 20px"
      >
        <button class="dropdown-button">
          <img src="../assets/list.png" width="15px" />
        </button>
        <div class="dropdown-content">
          <a href="#">个人信息</a>
          <a href="#">修改密码</a>
          <a href="#">其他属性</a>
          <a href="/#/login">退出登录</a>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup>

</script>




<style scoped>
.logo{
  float: left;
}
.menu-demo {
  box-sizing: border-box;
  width: 100%;
  background-color: var(--color-neutral-2);
}
.user-avatar {
  /* position: absolute;
  right: 0;
  top: 4px; */
}

.avatar-image {
  position: absolute;
  top: 10px;
  right: 16px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  cursor: pointer;
  z-index: 10;
}

.dropdown {
  position: absolute;
  top: 28px;
  right: 50px;
  display: inline-block;
  z-index: 1;
}

.dropdown-button {
  background-color: #303030;
  color: white;
  padding: 5px 0px;
  border: none;
  cursor: pointer;
}

.dropdown-content {
  display: none;
  position: absolute;
  background-color: #f9f9f9;
  min-width: 85px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  z-index: 1;
}

.dropdown-content a {
  color: black;
  padding: 8px 8px;
  text-decoration: none;
  display: block;
}

.dropdown-content a:hover {
  background-color: #f1f1f1;
}

.dropdown:hover .dropdown-content {
  display: block;
}
</style>

这里展示一下之前添加的一点点代码,然后在右上角添加了一个用户头像和一个下拉栏目(感觉代码写的不太好,这边还是建议大家去组件库里找一些比较方便的(这里我是自己写的))

<template>
  <div class="menu-demo">
    <a-menu mode="horizontal" theme="dark" :default-selected-keys="['1']">
      <a-menu-item
        key="0"
        :style="{ padding: 0, marginRight: '38px' }"
        disabled
        class="logo"
      >
        <div
          :style="{
            width: '180px',
            height: '30px',
            background: 'var(--color-fill-3)',
            cursor: 'text',
          }"
        >
          <img src="../assets/logo.png" width="180px" />
        </div>
      </a-menu-item>
      <a-menu-item key="1"
        ><router-link to="/main/search">全局搜索</router-link></a-menu-item
      >
      <a-menu-item key="2"
        ><router-link to="/main/article">文章大全</router-link></a-menu-item
      >
      <a-menu-item key="3">Cloud Service</a-menu-item>
      <a-menu-item key="4">Cooperation</a-menu-item>
    </a-menu>
    <div class="user-avatar">
      <img
        src="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp"
        alt="User Avatar"
        class="avatar-image"
      />
      <div
        class="dropdown"
        style="font-family: 'Satisfy', cursive; margin-right: 20px"
      >
        <button class="dropdown-button">
          <img src="../assets/list.png" width="15px" />
        </button>
        <div class="dropdown-content">
          <router-link to="/user">个人信息</router-link>
          <router-link to="/login">修改密码</router-link>
          <router-link to="/login">其他属性</router-link>
          <router-link to="/login">退出登录</router-link>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup>
</script>




<style scoped>
.logo {
  float: left;
}
.menu-demo {
  box-sizing: border-box;
  width: 100%;
  background-color: var(--color-neutral-2);
}
.user-avatar {
  /* position: absolute;
  right: 0;
  top: 4px; */
}

.avatar-image {
  position: absolute;
  top: 10px;
  right: 16px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  cursor: pointer;
  z-index: 10;
}

.dropdown {
  position: absolute;
  top: 28px;
  right: 50px;
  display: inline-block;
  z-index: 1;
}

.dropdown-button {
  background-color: #303030;
  color: white;
  padding: 5px 0px;
  border: none;
  cursor: pointer;
}

.dropdown-content {
  display: none;
  position: absolute;
  background-color: #f9f9f9;
  min-width: 85px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  z-index: 1;
}

.dropdown-content a {
  color: black;
  padding: 8px 8px;
  text-decoration: none;
  display: block;
}

.dropdown-content a:hover {
  background-color: #f1f1f1;
}

.dropdown:hover .dropdown-content {
  display: block;
}
</style>

那么,这样就可以点击个人中心进行跳转到个人中心了(这里的退出登录bug很大,应该要调用专门的函数,去除登录状态什么的,之后有机会再改一下)

这里刚开始就是这样了,老样子,做个布局吧。

就用这个了

<template>
  <div id="userCenter">
    <a-layout style="height: 100vh">
      <a-layout-header>Header</a-layout-header>
      <a-layout style="margin: 24px 120px">
        <a-layout-sider>Sider</a-layout-sider>
        <a-layout-content>Content</a-layout-content>
      </a-layout>
      <a-layout-footer>Footer</a-layout-footer>
    </a-layout>
  </div>
</template>

<script>
export default {};
</script>

<style>
</style>

稍微搭建一下用户界面吧,明天继续(hhh)(emmm,还以为今天能写完这部分的)

发一下代码吧(感觉这个代码后面要大改。。)

<template>
  <div id="userCenter">
    <a-layout style="height: 100vh">
      <a-layout-header>
        <div class="header">
            <div class="personal-introduce">
              <span class="name">我是小丑</span>
              <span class="sex-icon"></span>
              <span class="level-icon"></span>
            </div><div class="professional">演员,代表作:</div>
        </div>
      </a-layout-header>
      <a-layout style="margin: 24px 120px">
        <a-layout-sider>Sider</a-layout-sider>
        <a-layout-content>Content</a-layout-content>
      </a-layout>
      <a-layout-footer>Footer</a-layout-footer>
    </a-layout>
  </div>
</template>

<script>
export default {};
</script>

<style lang="scss" scoped>
#userCenter {
  background: url("../assets/image.png") no-repeat bottom center / 100%
    100%;
}
.header {
  height: 20vh;
  margin-top: 5%;
   background: url("../assets/back.png") no-repeat center / 90%
    100%;
}

.personal-introduce {
        display: flex;
        justify-content: center;
        align-items: flex-end;
        margin-top: 10px;
        text-shadow: 0px 0px 4px rgba(0, 0, 0, 0.31);
        .name {
          line-height: 29px;
          font-size: 22px;
        }
        .sex-icon {
          display: inline-block;
          width: 16px;
          height: 16px;
          margin: 0px 8px;
          margin-bottom: 4px;
          background: url(../assets/user-images/sex-icon.png) no-repeat center;
          background-size: contain;
        }
        .level-icon {
          display: inline-block;
          width: 16px;
          height: 16px;
          margin-bottom: 4px;
          background: url(../assets/user-images/leval-icon.png) no-repeat center;
          background-size: contain;
        }
      }
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1690783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信息系统项目管理师0129:输入(8项目整合管理—8.7监控项目工作—8.7.1输入)

点击查看专栏目录 文章目录 8.7 监控项目工作8.7.1 输入8.7 监控项目工作 监控项目工作是跟踪、审查和报告整体项目进展,以实现项目管理计划中确定的绩效目标的过程。本过程的主要作用: 让干系人了解项目的当前状态并认可为处理绩效问题而采取的行动;通过成本和进度预测,让…

房地产支持政策加码不断,美克家居全力变革未来可期

2023年我国经济处于恢复发展阶段&#xff0c;而家具制造业“回温”速度明显慢于经济增速&#xff0c;在这一背景下&#xff0c;美克家居如此营收表现并不令人感到意外。而在充沛现金流支撑下&#xff0c;辅以全方位开展降本增效的年度经营规划&#xff0c;公司亏损收窄或已为期…

rclone迁移对象存储之间的数据

1 概述 rclone是一款文件复制工具&#xff0c;既可以用于在linux主机之间复制文件&#xff0c;也可以在对象存储之间复制文件。 rclone的官网为&#xff1a; https://rclone.orgrlcone关于对象存储的官方文档为&#xff1a; https://rclone.org/s32 安装 2.1 yum安装 yum …

【全网最全】2024电工杯数学建模A题前两问完整解答+21页初步参考论文+py代码+保奖思路等(后续会更新成品论文)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片链接&#xff0c;那是获取资料的入口&#xff01; 【全网最全】2024电工杯数学建模A题前两问完整解答21页初步参考论文py代码保奖思路等&#xff08;后续会更新成品论文&#xff09;「首先来看看目前已有…

在线生成数据库er图的工具

网址 https://databasediagram.com/ 其实很早之前我也有类似的想法&#xff0c;根据数据表结构&#xff0c;显示数据表之间的关系图。 当时我还写了一个工具&#xff0c;可惜后来就没怎么用过了。 这个网站和我当时的思路很像&#xff0c;只不过他这个页面显示比我的好得多&…

谈恋爱没经验?那就来刷谈恋爱经验宝宝吧

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星评选TOP 10&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作…

C++青少年简明教程:If选择语句

C青少年简明教程&#xff1a;If选择语句 C中选择语句的语法是&#xff1a; if (条件) { 条件成立时需要执行的语句... } [else { 条件不成立时需要执行的语句... }] 说明&#xff1a; if后面使用一个括号&#xff0c;括号里是条件——关系表达式。 所谓的关系表达式就是判…

报错:找不到或无法加载主类 com.example.SpringbootApplication(idea)

OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended 错误: 找不到或无法加载主类 com.example.SpringbootApplication 原因: java.lang.NoClassDefFoundError: com/example/SpringBootAppli…

上位机图像处理和嵌入式模块部署(mcu中的串口接口)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在mcu开发中&#xff0c;mcu扮演着非常重要的角色。一方面&#xff0c;串口可以帮助我们对固件功能进行调试&#xff0c;另外一方面&#xff0c;串…

小猫咪的奇幻冒险:一个简单的Python小游戏

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、游戏简介与演示 二、游戏开发与运行 1. 环境搭建 2. 代码解析 3. 加速机制 三、游戏…

k近邻和kd树

K近邻 选取k值的时候可以采用交叉验证的方法 一般采用欧氏距离 kd树 采用树这个特殊的数据结构来实现k近邻算法 先假设是二维的情况 下面讲解kd树的完整构造过程 找这个中位数是按照每棵子树来创建的 前提是已经有了一棵kd树,然后来一个实例点

45岁前TVB有型熟男生图流出

凭无线处境剧《爱回家》中饰演律师「严谨」一角成功入屋的张达伦&#xff0c;于2022年约满无线离巢后&#xff0c;正式「卖身」给杜琪峰成为旗下艺人&#xff0c;先后亮相ViuTV剧集及综艺节目&#xff0c;又参与电影演出&#xff0c;作多方面尝试和发展。 日前有网民食完糖水在…

网段与广播域

ip地址与子网掩码做与运算得到网络号&#xff0c;得到的网络号相同就是同一个网段&#xff0c;否则不是&#xff0c;跟他们在什么位置没有任何关系 这里面pc3和前两个pc虽然不在同一个网段&#xff0c;但是pc1发广播包的时候&#xff0c;pc3也能收到&#xff0c;因为路由器的所…

离开谷歌:九年之后,我为何选择离开这家公司

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【学习笔记】Windows GDI绘图(四)矩阵Matrix详解

矩阵Matrix 基于矩阵在GDI绘图的重要性&#xff0c;所以想深入了学习矩阵的相关属性与方法。 先上个本文中所有的函数图例演示吧。 原型&#xff1a; namespace System.Drawing.Drawing2D;public sealed unsafe class Matrix : MarshalByRefObject, IDisposableMatrix类封装…

玩机社区 - 2024年最美社区源码开源

玩机社区 - 2024年最美社区源码开源 教程源码文档都内置到压缩包了 https://pan.baidu.com/s/1xwcscTne-JMbmKEntiuAuA?pwd78oi

22个C语言小白常见问题总结

一.语言使用错误 在打代码的过程中&#xff0c;经常需要在中文与英文中进行转换&#xff0c;因此常出现一些符号一不小心就用错&#xff0c;用成中文。例如&#xff1a;“&#xff1b;”中文中的分号占用了两个字节&#xff0c;而英文中“;”分号只占用一个字节。编译器只能识…

遥感和ENVI技术检测食物污染

对于生产者和消费者来说食物污染都是个问题。家禽污染会引发严重的健康问题&#xff0c;导致严重的危害。美国农业部需要一种方法&#xff0c;使用遥感影像在离开生产线之前探测出受污染的家禽。 解决方案 提起遥感&#xff0c;人们想到的大多是那些太空船和轨道卫星。然而在美…

电脑版网易云音乐听歌识曲

文章目录 流程 流程 电脑网易云音乐的搜索框旁边就是听歌识曲功能

基于PHP的物业管理的设计与实现

第1章 绪论... 1 1.1 研究背景与意义... 1 1.2 国内外发展现状... 2 第2章 关键技术介绍... 3 2.1 PHP语言... 3 2.2 MySQL数据库... 3 2.3 Zend框架... 4 2.4 B/S架构... 4 第3章 系统需求分析... 5 3.1 可行性分析... 5 3.1.1 技术可行性分析... 5 3.1.2 经济可行…