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>