【web】2、集成插件

1、element-plus

官网地址:设计 | Element Plus

安装 plus 及 icon 图标库

1.1 官网提供plus安装方法:

1.2 官网提供 icon 安装方法

1.3 安装

bash 复制代码
pnpm install element-plus @element-plus/icons-vue

main.ts全局安装element-plus,element-plus默认支持语言英语设置为中文

javascript 复制代码
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css'
//@ts-ignore忽略当前文件ts类型的检测否则有红色提示(打包会失败)
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
app.use(ElementPlus, {
    locale: zhCn
})

1.4 图标全局配置

javascript 复制代码
// main.ts

// 如果您正在使用CDN引入,请删除下面一行。
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

2、SVG图标配置

在开发项目的时候经常会用到svg矢量图,而且我们使用SVG以后,页面上加载的不再是图片资源,

这对页面性能来说是个很大的提升,而且我们SVG文件比img要小的很多,放在项目中几乎不占用资源。

官网地址:iconfont-阿里巴巴矢量图标库

2.1 安装svg图标库

bash 复制代码
pnpm install vite-plugin-svg-icons -D

2.2 vite.config.ts 中配置插件

javascript 复制代码
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import path from 'path'
export default () => {
  return {
    plugins: [
      createSvgIconsPlugin({
        // Specify the icon folder to be cached
        iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
        // Specify symbolId format
        symbolId: 'icon-[dir]-[name]',
      }),
    ],
  }
}

2.3 main.ts中导入插件

javascript 复制代码
import 'virtual:svg-icons-register'

3、集成sass

我们目前在组件内部已经可以使用scss样式,因为在配置styleLint工具的时候,项目当中已经安装过sass sass-loader,因此我们再组件内可以使用scss语法!!!需要加上lang="scss"

html 复制代码
<style scoped lang="scss"></style>

3.1 引入全局样式

新建目录 :src/styles

reset.scss

在styles下 创建reset.scss 文件

css 复制代码
html {
  -webkit-text-size-adjust: 100%
}

html:focus-within {
  scroll-behavior: smooth
}

body {
  -webkit-text-size-adjust: 100%;
  -moz-text-size-adjust: 100%;
  text-size-adjust: 100%;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  position: relative;
  text-rendering: optimizeSpeed;
  width: 100%
}

*, :after, :before {
  box-sizing: border-box
}

a:not([class]) {
  -webkit-text-decoration-skip: ink;
  text-decoration-skip-ink: auto
}

a, abbr, acronym, address, applet, article, aside, audio, b, big, blockquote, body, canvas, caption, center, cite, code, dd, del, details, dfn, div, dl, dt, em, embed, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup, html, i, iframe, img, ins, kbd, label, legend, li, mark, menu, nav, object, ol, output, p, pre, q, ruby, s, samp, section, small, span, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, time, tr, tt, u, ul, var, video {
  border: 0;
  font-size: 100%;
  font: inherit;
  margin: 0;
  padding: 0;
  vertical-align: baseline
}

:focus {
  outline: 0
}

article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section {
  display: block
}

ol, ul {
  list-style: none
}

blockquote, q {
  quotes: none
}

blockquote:after, blockquote:before, q:after, q:before {
  content: "";
  content: none
}

input, input:required {
  box-shadow: none
}

input:-webkit-autofill, input:-webkit-autofill:active, input:-webkit-autofill:focus, input:-webkit-autofill:hover {
  -webkit-box-shadow: inset 0 0 0 30px #fff
}

input[type=search]::-webkit-search-cancel-button, input[type=search]::-webkit-search-decoration, input[type=search]::-webkit-search-results-button, input[type=search]::-webkit-search-results-decoration {
  -webkit-appearance: none;
  -moz-appearance: none
}

input[type=search] {
  -webkit-appearance: none;
  -moz-appearance: none
}

input:focus {
  outline: none
}

audio, canvas, video {
  display: inline-block;
  max-width: 100%
}

audio:not([controls]) {
  display: none;
  height: 0
}

[hidden] {
  display: none
}

a:active, a:hover {
  outline: none
}

img {
  height: auto;
  max-width: 100%;
  vertical-align: middle
}

img, picture {
  display: inline-block
}

button, input {
  line-height: normal
}

button, html input[type=button], input[type=reset], input[type=submit] {
  -webkit-appearance: button;
  background: transparent;
  border: 0;
  cursor: pointer
}

button[disabled], html input[disabled] {
  cursor: default
}

[disabled] {
  pointer-events: none
}

input[type=checkbox], input[type=radio] {
  padding: 0
}

input[type=search] {
  -webkit-appearance: textfield;
  box-sizing: content-box
}

input[type=search]::-webkit-search-cancel-button, input[type=search]::-webkit-search-decoration {
  -webkit-appearance: none
}

button::-moz-focus-inner, input::-moz-focus-inner {
  border: 0;
  padding: 0
}

button {
  background: transparent;
  border: 0
}

textarea {
  overflow: auto;
  resize: vertical;
  vertical-align: top
}

table {
  border-collapse: collapse;
  border-spacing: 0;
  text-indent: 0
}

hr {
  background: #000;
  border: 0;
  box-sizing: content-box;
  height: 1px;
  line-height: 0;
  margin: 0;
  overflow: visible;
  padding: 0;
  page-break-after: always;
  width: 100%
}

pre {
  font-family: monospace, monospace;
  font-size: 100%
}

a {
  background-color: transparent
}

abbr[title] {
  border-bottom: none;
  text-decoration: none
}

code, kbd, pre, samp {
  font-family: monospace, monospace
}

small, sub, sup {
  font-size: 75%
}

sub, sup {
  line-height: 0;
  position: relative;
  vertical-align: baseline
}

sub {
  bottom: -5px
}

sup {
  top: -5px
}

button, input, optgroup, select, textarea {
  font-family: inherit;
  font-size: 100%;
  line-height: 1;
  margin: 0;
  padding: 0
}

button, input {
  overflow: visible
}

button, select {
  text-transform: none
}

[type=button], [type=reset], [type=submit], button {
  -webkit-appearance: button
}

[type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner, button::-moz-focus-inner {
  border-style: none;
  outline: 0;
  padding: 0
}

legend {
  border: 0;
  color: inherit;
  display: block;
  max-width: 100%;
  white-space: normal;
  width: 100%
}

fieldset {
  min-width: 0
}

body:not(:-moz-handler-blocked) fieldset {
  display: block
}

progress {
  vertical-align: baseline
}

[type=number]::-webkit-inner-spin-button, [type=number]::-webkit-outer-spin-button {
  height: auto
}

[type=search] {
  -webkit-appearance: textfield;
  outline-offset: -2px
}

[type=search]::-webkit-search-decoration {
  -webkit-appearance: none
}

::-webkit-file-upload-button {
  -webkit-appearance: button;
  font: inherit
}

summary {
  display: list-item
}

template {
  display: none
}

index.scss

在styles下新建index.scss

css 复制代码
@import "./reset.scss";

在main.ts中引入 index.scss

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

global.scss

在styles下新建global.scss

css 复制代码
//项目提供scss全局变量

将global.scss配置为全局样式组件

在vite.config.ts文件配置如下:

javascript 复制代码
export default defineConfig((config) => {
	css: {
      preprocessorOptions: {
        scss: {
          javascriptEnabled: true,
          additionalData: '@import "./src/styles/global.scss";',
        },
      },
    },
}

@import "./src/styles/variable.less";后面的;不要忘记,不然会报错!

4、封装全局组件

4.1配置全局组件引入

4.1.1 创建index.ts

src/components 下新建index.ts文件

TypeScript 复制代码
export default {
  install(app: any) {

  },
}

4.1.2 main.ts中引入

TypeScript 复制代码
import globalComponent from '@/components'

//引入全局组件
app.use(globalComponent)

4.2 将element-plus/icon-vue 配置为全局引入

index.ts中加入icon-vue的全局处理代码片段

TypeScript 复制代码
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import type { App, Component } from 'vue';

export default {
  install(app: any) {

    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
      app.component(key, component)
    }

  },
}

4.2 配置全局SvgIcon

4.2.1编写SvgIcon自定义组件

src/components/SvgIcon/index.vue

html 复制代码
<template>
  <!-- svg:图标外层容器节点,内部需要与use标签结合使用 -->
  <svg :style="{ width, height }">
    <!-- xlink:href执行用哪一个图标,属性值务必#icon-图标名字 -->
    <!-- use标签fill属性可以设置图标的颜色 -->
    <use :xlink:href="prefix + name" :fill="color"></use>
  </svg>
</template>

<script setup lang="ts" name="SvgIcon">
//接受父组件传递过来的参数
defineProps({
  //xlink:href属性值前缀
  prefix: {
    type: String,
    default: '#icon-',
  },
  //提供使用的图标名字
  name: String,
  //接受父组件传递颜色
  color: {
    type: String,
    default: '',
  },
  //接受父组件传递过来的图标的宽度
  width: {
    type: String,
    default: '16px',
  },
  //接受父组件传递过来的图标的高度
  height: {
    type: String,
    default: '16px',
  },
})
</script>

<style scoped></style>

4.2.2 引入到全局组件中

TypeScript 复制代码
import SvgIcon from '@/components/SvgIcon/index.vue'
const allGlobalComponents: any = {
  SvgIcon,
}

export default {
  install(app: any) {
    Object.keys(allGlobalComponents).forEach((key) => {
      app.component(key, allGlobalComponents[key])
    })
  },
}

4.2.3 引用

html 复制代码
<SvgIcon name="full" color="#fff" width="100%" height="100%"></SvgIcon>

4.3配置全局三级下拉选项

4.3.1 编写 Category组件

src/components/Category/index.vue

html 复制代码
<template>
  <el-card class="select-container">
    <el-form inline>
      <el-form-item label="一级分类">
        <el-select
          v-model="category.c1Id"
          @change="changeC1"
          :disabled="scene == 1"
        >
          <el-option
            v-for="item in category.c1List"
            :key="item.id"
            :label="item.name"
            :value="item.id"
          ></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="二级分类">
        <el-select
          v-model="category.c2Id"
          @change="changeC2"
          :disabled="scene == 1"
        >
          <el-option
            v-for="item in category.c2List"
            :key="item.id"
            :label="item.name"
            :value="item.id"
          ></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="三级分类">
        <el-select
          v-model="category.c3Id"
          @change="
            props.setIds({
              c1Id: category.c1Id,
              c2Id: category.c2Id,
              c3Id: category.c3Id,
            })
          "
          :disabled="scene == 1"
        >
          <el-option
            v-for="item in category.c3List"
            :key="item.id"
            :label="item.name"
            :value="item.id"
          ></el-option>
        </el-select>
      </el-form-item>
    </el-form>
  </el-card>
</template>

<script setup lang="ts">
import { onMounted, reactive } from 'vue'

const props = defineProps(['getCategory', 'setIds', 'scene'])
let category = reactive<any>({
  c1List: [],
  c2List: [],
  c3List: [],
  c1Id: '',
  c2Id: '',
  c3Id: '',
})

const changeC1 = async () => {
  category.c2Id = ''
  category.c3Id = ''
  props.setIds({
    c1Id: '',
    c2Id: '',
    c3Id: '',
  })
  let res = await props.getCategory(2, category.c1Id)
  if (res.length > 0) {
    category.c2List = res
  }
}

const changeC2 = async () => {
  category.c3Id = ''
  props.setIds({
    c1Id: '',
    c2Id: '',
    c3Id: '',
  })
  let res = await props.getCategory(3, category.c2Id)
  if (res.length > 0) {
    category.c3List = res
  }
}

onMounted(async () => {
  let res = await props.getCategory(1, 0)
  if (res.length > 0) {
    category.c1List = res
  }
})
</script>
<script lang="ts">
export default {
  name: 'Category',
}
</script>

<style scoped lang="scss">
.el-form-item {
  .el-select {
    width: 200px;
  }
}
</style>

4.3.2 引入到全局组件中

TypeScript 复制代码
import Category from '@/components/category/index.vue'

const allGlobalComponents: any = {
  Category,
}

export default {
  install(app: any) {
    Object.keys(allGlobalComponents).forEach((key) => {
      app.component(key, allGlobalComponents[key])
    })
    
  },
}

4.3.3 stores存储属性值

TypeScript 复制代码
import { defineStore } from 'pinia'
import { reqCategoryList } from '@/api/product/attr'
import { ElMessage } from 'element-plus'

export const useProductAttrStore = defineStore('ProductAttr', () => {
  const getCategory = async (type: number, categoryId: number) => {
    const result = await reqCategoryList(type, categoryId)
    if (result.code == 200) {
      return result.data
    } else {
      ElMessage({
        message: '查询分类失败',
        type: 'error',
      })
      return []
    }
  }
  return { getCategory }
})

4.3.4 引用

html 复制代码
<Category
      :getCategory="attrStore.getCategory"
      :setIds="setIds"
      :scene="scene"
    />
<script setup lang="ts">
import { useProductAttrStore } from '@/stores/models/product/attr'
import { reactive, ref } from 'vue'

let scene = ref(0)
let attrValueList = ref([])

const attrStore = useProductAttrStore()

const attrParams = reactive({
  attrName: '',
  attrValueList: [],
  categoryId: '',
  categoryLevel: 3,
})

const ids = reactive({
  c1Id: '',
  c2Id: '',
  c3Id: '',
})

const setIds = async (idData: any) => {
  Object.assign(ids, idData)
  await getAttrValueList()
}
const getAttrValueList = async () => {
  if (ids.c3Id != '') {
    scene.value = 0
    let result = await reqAttrInfoList(ids.c1Id, ids.c2Id, ids.c3Id)
    if (result.code === 200) {
      attrParams.categoryId = ids.c3Id
      attrValueList.value = result.data
    }
  } else {
    attrValueList.value = []
  }
}

</script>

4.4 分页pagination

4.4.1 编写分页组件

html 复制代码
<template>
  <el-pagination
    v-model:current-page="page_attr.currentPage"
    v-model:page-size="page_attr.pageSize"
    :page-sizes="[1, 2, 3, 4, 5]"
    background
    layout="prev, pager, next, -> ,total, sizes"
    :total="page_attr.total"
    @size-change="handleSizeChange"
    @current-change="handleCurrentChange"
  />
</template>

<script setup lang="ts">
import { onMounted, reactive } from 'vue'

const props = defineProps(['select'])

//分页器
const page_attr = reactive({
  currentPage: 1,
  pageSize: 2,
  total: 10,
})

const handleSizeChange = (val: number) => {
  page_attr.pageSize = val
  page_attr.currentPage = 1
  handleCurrentChange()
}
const handleCurrentChange = async () => {
  page_attr.total = await props.select(
    page_attr.currentPage,
    page_attr.pageSize,
  )
}

onMounted(async () => {
  await handleCurrentChange()
})
</script>
<script lang="ts">
export default {
  name: 'Pagination',
}
</script>

<style scoped lang="scss"></style>

4.4.2 引入到全局组件中

html 复制代码
import Pagination from '@/components/pagination/index.vue'

const allGlobalComponents: any = {
  Pagination,
}

export default {
  install(app: any) {
    Object.keys(allGlobalComponents).forEach((key) => {
      app.component(key, allGlobalComponents[key])
    })
  },
}

4.4.3 引用

html 复制代码
<Pagination :select="select" />

<script setup lang="ts">

let tradeMarkList = ref()

//分页
const select = async (currentPage: number, pageSize: number) => {
  //下面为具体型业务逻辑
  let result = await reqTradeMarkList(currentPage, pageSize)
  tradeMarkList.value = result.data.records
  return result.data.total
}
</script>
相关推荐
xjt_09012 分钟前
浅析Web存储系统
前端
foxhuli22939 分钟前
禁止ifrmare标签上的文件,实现自动下载功能,并且隐藏工具栏
前端
青皮桔1 小时前
CSS实现百分比水柱图
前端·css
失落的多巴胺1 小时前
使用deepseek制作“喝什么奶茶”随机抽签小网页
javascript·css·css3·html5
DataGear1 小时前
如何在DataGear 5.4.1 中快速制作SQL服务端分页的数据表格看板
javascript·数据库·sql·信息可视化·数据分析·echarts·数据可视化
影子信息1 小时前
vue 前端动态导入文件 import.meta.glob
前端·javascript·vue.js
青阳流月1 小时前
1.vue权衡的艺术
前端·vue.js·开源
RunsenLIu1 小时前
基于Vue.js + Node.js + MySQL实现的图书销售管理系统
vue.js·mysql·node.js
样子20181 小时前
Vue3 之dialog弹框简单制作
前端·javascript·vue.js·前端框架·ecmascript
kevin_水滴石穿1 小时前
Vue 中报错 TypeError: crypto$2.getRandomValues is not a function
前端·javascript·vue.js