【vue3后台项目】登录页面ui及图标组件封装

构建登录页面

1.创建views/login/index.vue文件;

views/login/index.vue 复制代码
<template>
  <div class="login-container">
    <el-form class="login-form">
      <div class="title-container">
        <h3 class="title">用户登录</h3>
      </div>
      <el-form-item>
        <span class="svg-container">
          <el-icon><Avatar /></el-icon>
        </span>
        <el-input placeholder="username" name="username" type="text"></el-input>
      </el-form-item>
      <el-form-item>
        <span class="svg-container">
          <el-icon><Avatar /></el-icon>
        </span>
        <el-input placeholder="password" name="password"></el-input>
        <span class="show-pwd">
          <el-icon><Avatar /></el-icon>
        </span>
      </el-form-item>
      <el-button type="primary" style="width: 100%; margin-bottom: 30px"
        >登录</el-button
      >
    </el-form>
  </div>
</template>

<script setup>
import { Avatar } from '@element-plus/icons'
</script>

<style lang="scss" scoped>
$bg: #2d3a4b;
$dark_gray: #889aa4;
$light_gray: #eee;
$cursor: #fff;

.login-container {
  min-height: 100%;
  width: 100%;
  background-color: $bg;
  overflow: hidden;
  .login-form {
    position: relative;
    width: 520px;
    max-width: 100%;
    padding: 160px 35px;
    margin: 0 auto;
    overflow: hidden;
    .title-container {
      width: 100%;
      padding-bottom: 20px;
      text-align: center;
      font-weight: bold;
      color: #fff;
      font-size: 26px;
    }
    ::v-deep .el-form-item {
      border: 1px solid rgba(255, 255, 255, 0.1);
      background-color: rgba(0, 0, 0, 0.1);
      border: 5px;
      color: #454545;
    }
    .svg-container {
      display: inline-block;
      text-align: center;
    }
    ::v-deep .el-input {
      display: inline-block;
      width: 85%;
      height: 47%;

      input {
        background: transparent;
        border: 0px;
        border-radius: 0px;
        color: $light_gray;
        padding: 12px 5px 12px 15px;
        caret-color: $cursor;
      }
    }
  }
  .svg-container {
    padding: 6px 5px 6px 15px;
    color: $dark_gray;
    vertical-align: middle;
    display: inline-block;
  }
  .show-pwd {
    position: absolute;
    right: 10px;
    top: 7px;
    font-size: 16px;
    color: $dark_gray;
    cursor: pointer;
    user-select: none;
  }
}
</style>

2.在route/index.js中增加一下路由配置;

route/index.js 复制代码
import { createRouter, createWebHashHistory } from 'vue-router'

const publicRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index')
  }
]

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

export default router

3.创建全局的style

style/index.scss 复制代码
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
    Microsoft YaHei, Arial, sans-serif;
}

#app {
  height: 100%;
}

*,
*:before,
*:after {
  box-sizing: inherit;
  margin: 0;
  padding: 0;
}

a:focus,
a:active {
  outline: none;
}

a,
a:focus,
a:hover {
  cursor: pointer;
  color: inherit;
  text-decoration: none;
}

div:focus {
  outline: none;
}

.clearfix {
  &:after {
    visibility: hidden;
    display: block;
    font-size: 0;
    content: ' ';
    clear: both;
    height: 0;
  }
}

main.ts中导入:

main.ts 复制代码
import '@/styles/index.scss'

Icon图标

之前都是通过路径引入图标:

html 复制代码
<img src="../assets/icons/user.svg" />

显然是很不方便的,此次封装的目的就是为了简化写法和可复用性;

在我们项目中该所使用的icon图标,一共分为两类:

1.element-plus的图标;

2.自定义的svg图标;

对于element-plus的图标我们可以直接通过el-icon来进行显示,但是自定义图标的话,我们需要自定义组件,来显示自定义的svg图标;

这个组件需要拥有两种能力:

1.显示外部svg图标;

2.现实项目内的svg图标;

创建图标组件

创建utils/validate.js;

utils/validate.js 复制代码
/**
 * 判断是否为外部资源
 */
export function isExternal(path) {
  return /^(https?:|mailto:|tel:)/.test(path)
}

创建components/SvgIcon/index.vue;

components/SvgIcon/index.vue 复制代码
<template>
  <!--展示外部图标-->
  <div
    v-if="isExternal"
    :style="styleExternalIcon"
    class="svg-external-icon svg-icon"
    :class="className"
  ></div>
  <!--展示内部图标-->
  <svg v-else class="svg-icon" :class="className" aria-hidden="true">
    <use :xlink:href="iconName"></use>
  </svg>
</template>

<script setup>
import { defineProps, computed } from 'vue'
import { isExternal as external } from '@/utils/validate'
const props = defineProps({
  // icon图标
  icon: {
    type: String,
    required: true
  },
  // 图标类名
  className: {
    type: String,
    default: ''
  }
})
/**
 *判断当前图标是否为外部图标
 */
const isExternal = computed(() => external(props.icon))
/**
 * 外部图标样式
 */
const styleExternalIcon = computed(() => ({
  mask: `url(${props.icon}) no-repeat 50% 50%`,
  '-webkit-mask': `url(${props.icon}) no-repeat 50% 50%`
}))
/**
 * 内部图标
 */
const iconName = computed(() => `#icon-${props.icon}`)
</script>

<style lang="scss" scoped>
.svg-icon {
  /** 将icon大小设置和字体大小一致,后续在通过svg-icon使用icon的时候,可直接设置图标的font-size即可控制图标的大小 */
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  /** 因icon大小被设置为字体大小一致,而span等标签的下边缘会和字体的基线对齐,顾需设置一个往下的偏移比例,来纠正视觉上的未对齐效果*/
  fill: currentColor;
  /** 定义元素的颜色,currentColor是一个变量,这个变量就表示当前元素的color,如果当前元素未设置color值,则从父元素继承 */
  overflow: hidden;
}

.svg-external-icon {
  background: currentColor;
  mask-size: cover !important;
  display: inline-block;
}
</style>

接着在login页面使用图标组件,这里是一个外部图标的显示:

views/login/index.vue 复制代码
<span class="svg-container">
  <SvgIcon icon="https://res.lgdsunday.club/user.svg"></SvgIcon>
</span>

外部图标成功显示出来;

处理内部svg图标显示

处理内部svg图标显示的步骤:

1.首先导入所有的svg图标: 大家可以去我的git仓库直接拷贝src/icons/svg文件夹到自己的目录下;

2.在icons目录下创建index.js文件,该文件中需要完成两件事情,这个过程类似于Element-plus的导入:

  • 导入所有的svg图标;
  • 完成SvgIcon的全局注册;

webpack导入依赖

创建icons/svg/index.js

icons/svg/index.js 复制代码
import SvgIcon from '@/components/SvgIcon'
const svgRequire = require.context('./svg', false, /\.svg$/)
// 返回一个Require函数,可以接受一个request的参数,用于require的导入;
// 该函数提供了三个属性,可以通过svgRequire.keys()获取所有的svg图标;
// 遍历图标,把图标作为request参数传入到svgRequire导入函数中,完成本地svg图标的导入;

svgRequire.keys().forEach((icon) => {
  svgRequire(icon)
})

export default (app) => {
  app.component('svg-icon', SvgIcon)
}

在main.js中导入;

main.js 复制代码
// 导入svgIcon
import installIcons from '@/icons'

installIcons(app)

使用svg-sprite-loader处理svg图标

svg-sprite-loader是webpack中专门用来处理svg图标的一个loader,详情🔎; 我们需要做两件事情:

1.下载该loader,执行

js 复制代码
npm i svg-sprite-loader@6.0.9 --save

2.创建vue.config.js文件,新增如下配置,详情🔎:

vue.config.js 复制代码
const path = require('path')
function resolve(dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  chainWebapack: (config) => {
    // 内置的svg处理排除执行目录下的文件
    config.module.rule('svg').exclude.add(resolve('src/icons')).end()
    config.module
      .rule('svg-sprite-loader')
      .test(/\.svg$/)
      .include.add(resolve('src/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
      .end()
  }
}

接着去login页面引入图标组件:

js 复制代码
<span class="svg-container">
  <SvgIcon icon="user"></SvgIcon>
</span>

图标可以正常显示了!

相关推荐
FRing031 分钟前
vue2干货总结8(路由模块封装、router-link、声明式导航、路由重定向、编程式导航)
开发语言·前端·vue.js·vue2
SameX11 分钟前
HarmonyOS ArkUI Dialog 复用 用 @CustomDialog + enum type 做多类型统一管理的做法
前端
AiMan27 分钟前
豆包爱学还是不可以导出左侧所有对话,只能单容导出,方法不对吗
前端
无懈可击43 分钟前
Vue 可视化AI表单设计器 FcDesigner v3.6 版本发布,支持导出 SQL功能!
vue.js·开源·ai编程
AiMan1 小时前
Grok生成的内容能否下载吗?
前端
徐小夕1 小时前
JitArchives,一款基于JitWord协同AI文档打造的企业级电子档案系统
前端·vue.js·github
陈随易1 小时前
Node.js 2026上半年都有哪些性能提升?
前端·后端·程序员
墨林陌2 小时前
AI 热点日报(2026-11-09):苹果代理式Siri曝光,Anthropic推进IPO,编码智能体实测翻车
前端
vipxieliang2 小时前
JavaScript 迭代器与生成器:控制异步与惰性求值
前端·javascript
Hilaku2 小时前
Event Loop 这篇你看完就不用再看别的了!
前端·javascript·程序员