前端小练-仿掘金导航栏

前言

闲的,你信嘛,还得开发一个基本的门户社区网站,来给到Hlang,不然我怕说工作量不够。那么这个的话,其实也很好办,主要是这个门户网站的UI写起来麻烦一点,其他的就直接套用组件,和以前的老代码就行了。为了快速开发,我直接用royi-cloud去做了。反正自己搭建一个也是这样,还要自己重新搭建环境,不如直接加速。像这种类型的网站,没有啥技巧,无法是做做防抖,节流,对接点啥支付等等的玩意。一个礼拜就能开发好,前提是前端好写。不过也确实不难,这种东西。其他的就是套用组件,没啥,后端的话,原来是整合人人开源一套的,但是MP,我实在是不喜欢,越写越觉得MP有点呆。懒得改写,不如直接换架构。这也是为啥直接用ruoyi了。

废话不多说,看看效果:

我甚至为此用New Bing生成了一个Logo。

首先,没错,我的临时毕设方案有两个部分: 1. 基于Python实现的中文编程解释器。因为主打的是教学,锻炼编程思维,因此不考虑任何性能,只考虑开发成本和周期,要是用C++怼,那我觉得我一开始的算法开发平台也不是不能做完。 2. 给这个玩意,做一个技术交流社区,这个社区的名字叫做Hlang,但是它不限于Hlang,其实就是个博客社区套壳。然后里面很多功能会直接套用白洞。但是所有内容会重构,反正现在开发一个这种玩意顶多一个礼拜(包括前端估计两个礼拜顶天了)

项目结构

废话不多说,先来看到我们的项目结构: 在这里导航栏是一个组件。 在App.vue里面直接调用:

html 复制代码
<script setup>
import { RouterLink, RouterView } from 'vue-router'
import Header from './components/Header.vue';
</script>
<template>
  <header>
    <Header></Header>
  </header>
  <div class="main">
    <div class="placeholder"></div> 
    <RouterView />
  </div>
</template>

<style scoped>
  .main{
    width: 100%;
    background-color: rgb(242,243,245);
  }
  .placeholder{
    height: 80px;
    width: 100%;
  }
  header {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 42px;
    background-color: #fff; /* 设置背景颜色 */
    padding: 20px; /* 添加内边距 */
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
    z-index: 999; /* 设置堆叠顺序,确保 header 位于其他内容之上 */
  }
</style>

这里注意的是,为了让head固定,我用fixed布局,然后的话为了避免挡住内容,于是在这里我用了一个div把main里面的东西顶下去。 就是这个:

html 复制代码
    <div class="placeholder"></div> 

导航实现

那么接下来就是我们的导航了。 首先是这个导航的基本结构:

html 复制代码
<template>
    <el-menu
      :default-active="activeIndex"
      class="el-menu-header"
      mode="horizontal"
      @select="handleSelect"
    
    >
      <!-- <span style="width: 50px;"></span> -->
      <el-menu-item index="1">
        <span>
           <img
             style="width: 50px;height: 50px"
             src="../../public/favicon.ico"
           >
        </span>
      </el-menu-item>


      <el-menu-item index="2">
        <template #title>社区</template>
      </el-menu-item>

      <el-menu-item index="3">
        <template #title>说说</template>
      </el-menu-item>

      <div class="h-search">
        <el-autocomplete
            style="height: 50px; width: 350px;margin-top: 12px;"
            v-model="state1"
            :fetch-suggestions="querySearch"
            clearable
            placeholder="探索社区"
            @select="handleSelect"
            />
        <el-button style="margin-top: 12px;margin-left: 5px;" :icon="Search" circle />
      </div>

      <div class="h-show">
        <div class="custom-loader"></div>
      </div>

      <el-sub-menu index="4">
        <template #title>
          <el-button type="primary">创作中心</el-button>
        </template>
        <div class="w-center">
          <div class="w-c-item">
            <img class="w-c-it-img" src="~@/assets/image/write.png">
            <div class="w-c-it-text">
              写文章
            </div>
          </div>

          <div class="w-c-item">
            <img class="w-c-it-img" src="~@/assets/image/shuoshuo.png">
            <div class="w-c-it-text">
              写说说
            </div>
          </div>

          <div class="w-c-item">
            <img class="w-c-it-img" src="~@/assets/image/chaogao.png">
            <div class="w-c-it-text">
              草稿箱
            </div>
          </div>

        </div>
      </el-sub-menu>

      <div class="message-tip">
        <el-badge :value="100" :max="10" class="item">
          <img class="message-img" src="~@/assets/image/notic.png">
        </el-badge>
      </div>

      <div class="avatar hover-rotate">
        <img class="avatar-img" src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg">
      </div>

    </el-menu>
  </template>

整体用到是flex布局: 然后的话,导航最外层用到还是element-plus的导航,但是里面的css重新改了一下。 首先是间隔,为了避免改动源代码出现问题,我直接在menu里面多了一个属性:

css 复制代码
.el-menu-header{
    gap: 20px;
    padding-left: 100px;
}

然后在这里设置: 也可以实现效果。

创作中心

在这里的话,前面几个按钮都是直接使用的组件,主要是后面这个东西。借鉴了掘金的这个操作: 这部分代码结构是这样的:

html 复制代码
   <el-sub-menu index="4">
        <template #title>
          <el-button type="primary">创作中心</el-button>
        </template>
        <div class="w-center">
          <div class="w-c-item">
            <img class="w-c-it-img" src="~@/assets/image/write.png">
            <div class="w-c-it-text">
              写文章
            </div>
          </div>

          <div class="w-c-item">
            <img class="w-c-it-img" src="~@/assets/image/shuoshuo.png">
            <div class="w-c-it-text">
              写说说
            </div>
          </div>

          <div class="w-c-item">
            <img class="w-c-it-img" src="~@/assets/image/chaogao.png">
            <div class="w-c-it-text">
              草稿箱
            </div>
          </div>

        </div>
      </el-sub-menu>

直接这样写,就可以实现那种自定义的效果。 那么css代码是这样的:

css 复制代码
.w-c-it-img{
  margin-top: 10px;
  width: 40%;
  height: 40px;
}

.w-c-it-text{
  margin-top: 10px;
  width: 100%;
  height: 30px;
  font-size: 14px;
  text-align: center;
}
.w-c-item:hover{
  border-radius: 10px;
  -webkit-box-shadow: 5px 5px 50px 0px rgba(105,170,214,1);
  -moz-box-shadow: 5px 5px 50px 0px rgba(105,170,214,1);
  box-shadow: 5px 5px 50px 0px rgba(105,170,214,1);
}
.w-c-item{
  cursor: pointer;
  margin-top: 20px;
  width: 80px;
  height: 80px;
  display: flex;
  flex-direction: column;
  justify-items: center;
  align-items: center;
}
.w-center{
  border-radius: 30px;
  height: 120px;
  display: flex;
  padding-left: 60px;
  gap: 20px;
  width: 350px;
}

移动小球

这玩意其实就是这个: 这玩意会动。其实就是用来占位置的,实在是没有啥功能了。留个空,反正也是微服务架构,后面上个功能也好办。

html 复制代码
  <div class="h-show">
        <div class="custom-loader"></div>
      </div>
css 复制代码
.custom-loader {
  margin-top: 12px;
  width: 120px;
  height: 22px;
  border-radius: 40px;
  color: #E4E4ED;
  position: relative;
  overflow: hidden;
}

.custom-loader::before {
  content: "";
  position: absolute;
  margin: 2px;
  width: 14px;
  top: 0;
  bottom: 0;
  left: -20px;
  border-radius: inherit;
  background: #2898dd;
  box-shadow: -10px 0 12px 3px #83daf1;
  clip-path: polygon(0 5%, 100% 0,100% 100%,0 95%,-30px 50%);
  animation: ct4 1s infinite linear;
}

@keyframes ct4 {
  100% {left: calc(100% + 20px)}
}

消息提示

这个消息提示和头像是类似的,就一起说了。其实原来我一直用错了,就是那个消息提示显示的个数,原来做的时候忙个数老是和图标有很大间距。因为原来一直套用的是el-button 这个玩意样式不好改,于是直接在div上面加。

html 复制代码
 <div class="message-tip">
        <el-badge :value="100" :max="10" class="item">
          <img class="message-img" src="~@/assets/image/notic.png">
        </el-badge>
</div>
css 复制代码
.message-img{
  cursor: pointer;
  width: 25px;
  height: 30px;
}

.message-img:hover{
  border-radius: 10px;
  -webkit-box-shadow: 0px 0px 24px 6px rgba(5,143,235,1);
  -moz-box-shadow: 0px 0px 24px 6px rgba(5,143,235,1);
  box-shadow: 0px 0px 24px 6px rgba(5,143,235,1);
}

.message-tip{
  margin-top: 12px;
  width: 30px;
}

完整代码

okey,接下来是完整代码:

html 复制代码
<template>
    <el-menu
      :default-active="activeIndex"
      class="el-menu-header"
      mode="horizontal"
      @select="handleSelect"
    
    >
      <!-- <span style="width: 50px;"></span> -->
      <el-menu-item index="1">
        <span>
           <img
             style="width: 50px;height: 50px"
             src="../../public/favicon.ico"
           >
        </span>
      </el-menu-item>


      <el-menu-item index="2">
        <template #title>社区</template>
      </el-menu-item>

      <el-menu-item index="3">
        <template #title>说说</template>
      </el-menu-item>

      <div class="h-search">
        <el-autocomplete
            style="height: 50px; width: 350px;margin-top: 12px;"
            v-model="state1"
            :fetch-suggestions="querySearch"
            clearable
            placeholder="探索社区"
            @select="handleSelect"
            />
        <el-button style="margin-top: 12px;margin-left: 5px;" :icon="Search" circle />
      </div>

      <div class="h-show">
        <div class="custom-loader"></div>
      </div>

      <el-sub-menu index="4">
        <template #title>
          <el-button type="primary">创作中心</el-button>
        </template>
        <div class="w-center">
          <div class="w-c-item">
            <img class="w-c-it-img" src="~@/assets/image/write.png">
            <div class="w-c-it-text">
              写文章
            </div>
          </div>

          <div class="w-c-item">
            <img class="w-c-it-img" src="~@/assets/image/shuoshuo.png">
            <div class="w-c-it-text">
              写说说
            </div>
          </div>

          <div class="w-c-item">
            <img class="w-c-it-img" src="~@/assets/image/chaogao.png">
            <div class="w-c-it-text">
              草稿箱
            </div>
          </div>

        </div>
      </el-sub-menu>

      <div class="message-tip">
        <el-badge :value="100" :max="10" class="item">
          <img class="message-img" src="~@/assets/image/notic.png">
        </el-badge>
      </div>

      <div class="avatar hover-rotate">
        <img class="avatar-img" src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg">
      </div>

    </el-menu>
  </template>


<script lang="ts" setup>
import { ref,onMounted } from 'vue'
import {Search} from '@element-plus/icons-vue'


const activeIndex = ref('1')
const state1 = ref('')

interface RestaurantItem {
  value: string
  link: string
}
const restaurants = ref<RestaurantItem[]>([])

const querySearch = (queryString: string, cb: any) => {
  const results = queryString
    ? restaurants.value.filter(createFilter(queryString))
    : restaurants.value
  // call callback function to return suggestions
  cb(results)
}
const createFilter = (queryString: string) => {
  return (restaurant: RestaurantItem) => {
    return (
      restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
    )
  }
}
const loadAll = () => {
  return [
    { value: 'vue', link: 'https://github.com/vuejs/vue' },
    { value: 'element', link: 'https://github.com/ElemeFE/element' },
    { value: 'cooking', link: 'https://github.com/ElemeFE/cooking' },
    { value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' },
    { value: 'vuex', link: 'https://github.com/vuejs/vuex' },
    { value: 'vue-router', link: 'https://github.com/vuejs/vue-router' },
    { value: 'babel', link: 'https://github.com/babel/babel' },
  ]
}



const handleSelect = (item: RestaurantItem) => {
  console.log(item)
}

onMounted(() => {
  restaurants.value = loadAll()
})
</script>


<style scoped>

.avatar{
  cursor: pointer;
  margin-left: 60px;
  width: 40px;
  height: 40px;
  border-radius: 100px;
}

.hover-rotate {
  transition: transform 0.5s ease-in-out;
}

.hover-rotate:hover {
  transform: rotate(360deg);
}

.avatar-img{
  border-radius: 100px;
  margin-top: 10px;
  width: 100%;
  height: 100%;
}
.message-img{
  cursor: pointer;
  width: 25px;
  height: 30px;
}

.message-img:hover{
  border-radius: 10px;
  -webkit-box-shadow: 0px 0px 24px 6px rgba(5,143,235,1);
  -moz-box-shadow: 0px 0px 24px 6px rgba(5,143,235,1);
  box-shadow: 0px 0px 24px 6px rgba(5,143,235,1);
}

.message-tip{
  margin-top: 12px;
  width: 30px;
}
.w-c-it-img{
  margin-top: 10px;
  width: 40%;
  height: 40px;
}

.w-c-it-text{
  margin-top: 10px;
  width: 100%;
  height: 30px;
  font-size: 14px;
  text-align: center;
}
.w-c-item:hover{
  border-radius: 10px;
  -webkit-box-shadow: 5px 5px 50px 0px rgba(105,170,214,1);
  -moz-box-shadow: 5px 5px 50px 0px rgba(105,170,214,1);
  box-shadow: 5px 5px 50px 0px rgba(105,170,214,1);
}
.w-c-item{
  cursor: pointer;
  margin-top: 20px;
  width: 80px;
  height: 80px;
  display: flex;
  flex-direction: column;
  justify-items: center;
  align-items: center;
}
.w-center{
  border-radius: 30px;
  height: 120px;
  display: flex;
  padding-left: 60px;
  gap: 20px;
  width: 350px;
}
.custom-loader {
  margin-top: 12px;
  width: 120px;
  height: 22px;
  border-radius: 40px;
  color: #E4E4ED;
  position: relative;
  overflow: hidden;
}

.custom-loader::before {
  content: "";
  position: absolute;
  margin: 2px;
  width: 14px;
  top: 0;
  bottom: 0;
  left: -20px;
  border-radius: inherit;
  background: #2898dd;
  box-shadow: -10px 0 12px 3px #83daf1;
  clip-path: polygon(0 5%, 100% 0,100% 100%,0 95%,-30px 50%);
  animation: ct4 1s infinite linear;
}

@keyframes ct4 {
  100% {left: calc(100% + 20px)}
}
.h-search{
    display: flex;
    
}
.el-menu-header{
    gap: 20px;
    padding-left: 100px;
}
</style>
  

后面多写写特效啥的,锻炼锻炼美感,可惜的就是,先前写的前端主页用不了了。可恶,没办法风格不合适。

真的有一说一,CSDN的页面设计实在是有点那啥,先前whitehole的页面设计就是照着csdn来的,写道后面发现不行,改成掘金的一些风格,结果有些地方不伦不类,写到后面有点像知乎页面。这次的话,就直接照着掘金借鉴了(狗头)

相关推荐
顾平安1 小时前
Promise/A+ 规范 - 中文版本
前端
聚名网1 小时前
域名和服务器是什么?域名和服务器是什么关系?
服务器·前端
桃园码工1 小时前
4-Gin HTML 模板渲染 --[Gin 框架入门精讲与实战案例]
前端·html·gin·模板渲染
沈剑心1 小时前
如何在鸿蒙系统上实现「沉浸式」页面?
前端·harmonyos
一棵开花的树,枝芽无限靠近你1 小时前
【PPTist】组件结构设计、主题切换
前端·笔记·学习·编辑器
m0_748237051 小时前
Chrome 关闭自动添加https
前端·chrome
prall1 小时前
实战小技巧:下划线转驼峰篇
前端·typescript
开心工作室_kaic2 小时前
springboot476基于vue篮球联盟管理系统(论文+源码)_kaic
前端·javascript·vue.js
川石教育2 小时前
Vue前端开发-缓存优化
前端·javascript·vue.js·缓存·前端框架·vue·数据缓存
搏博2 小时前
使用Vue创建前后端分离项目的过程(前端部分)
前端·javascript·vue.js