【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>
相关推荐
睿智的海鸥2 分钟前
html css js做的在线签到例子
javascript·css·html
LiamHong_6 分钟前
简单了解 HTTP 请求方法
前端·后端·http·学习方法·web
蓝胖子的多啦A梦14 分钟前
【问题已解决】Vue管理后台,点击登录按钮,会发起两次网络请求(竟然是vscode Compile Hero编译插件导致的)
前端·vue.js·vscode
Lsx-codeShare24 分钟前
Nginx Http缓存的必要性!启发式缓存有什么弊端?
前端·javascript·nginx·http·缓存·webpack
南七小僧24 分钟前
Android Studio 解决AAPT: error: file failed to compile
前端·javascript·vue.js
陈琦鹏25 分钟前
zoom缩放问题(关于ElementPlus、Echarts、Vue3draggable等组件偏移问题)
前端·javascript·echarts
盏茶作酒42 分钟前
解决expand-change第一次展开无数据显示与实现
开发语言·前端·javascript
Wang's Blog1 小时前
Webpack: 开发 PWA、Node、Electron 应用
前端·webpack·electron
Code blocks1 小时前
小试牛刀-区块链代币锁仓(Web页面)
前端·区块链·智能合约
面朝大海,春不暖,花不开1 小时前
常见Web认证方式对比
前端