1.element-plus
硅谷甄选运营平台,UI组件库采用的element-plus,因此需要集成element-plus插件!!!
官网地址:https://element-plus.gitee.io/zh-CN/
由于是后台管理系统 所以我们全部引入
pnpm install element-plus
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const vm = createApp(App)
vm.use(ElementPlus)
vm.mount('#app')
2.element 图标引入
pnpm install @element-plus/icons-vue
全局引入
// 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)
}
也可以按需引入
Icon 图标 | Element Plus (element-plus.org)
3.src文件夹别名配置
tsconfig.app.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
// include也需要配置以下:
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules"]
}
vite.config.ts
import { defineConfig } from 'vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
VueSetupExtend()
],
resolve: {
alias: {
"@": path.resolve("./src") // 相对路径别名配置,使用 @ 代替 src
}
}
})
4.环境变量的配置
.env.development
# 开发环境配置文件
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
VITE_APP_TITLE = '硅谷甄选运营平台'
VITE_APP_BASE_API = '/dev-api'
VITE_SERVE="http://xxx.com"
.env.production
NODE_ENV = 'production'
VITE_APP_TITLE = '硅谷甄选运营平台'
VITE_APP_BASE_API = '/prod-api'
VITE_SERVE="http://yyy.com"
.env.test
NODE_ENV = 'test'
VITE_APP_TITLE = '硅谷甄选运营平台'
VITE_APP_BASE_API = '/test-api'
VITE_SERVE="http://zzz.com"
配置运行命令
package.json
"scripts": {
"dev": "vite --open",
"build:test": "vue-tsc && vite build --mode test",
"build:pro": "vue-tsc && vite build --mode production",
"preview": "vite preview"
},
获取环境变量
console.log(import.meta.env);
5.SVG图标
pnpm install vite-plugin-svg-icons -D
在vite.config.ts
中配置插件
import { defineConfig } from 'vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
VueSetupExtend(),
createSvgIconsPlugin({
// Specify the icon folder to be cached
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
// Specify symbolId format
symbolId: 'icon-[dir]-[name]',
})
],
resolve: {
alias: {
"@": path.resolve("./src") // 相对路径别名配置,使用 @ 代替 src
}
}
})
入口文件导入
import 'virtual:svg-icons-register'
去阿里矢量图库
粘贴进去
在组件中使用
<template>
<div>
<!-- 测试SVG图标的使用 -->
<!-- svg:图标外层容器节点,内部需要与use标签结合使用 -->
<svg >
<!-- xlink:href 执行用哪一个图标,属性值务必 #icon-文件名字 不加后缀 -->
<use xlink:href="#icon-phone" fill="red" width="30px" height="30px">
</use>
</svg>
</div>
</template>
<script setup lang="ts" name="App">
</script>
<style lang="css" scoped>
</style>
1.封装成全局组件
element-plusPlugins.ts
//element组件插件
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'//证明有效的在这里引入
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
export default {
install(app) {
/**
* ElementPlus 国际化的配置
*/
app.use(ElementPlus, {
locale: zhCn,
})
/**
* 全局引入 element-icon
*/
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
}
}
globalComponentsPlugins.ts
//对外暴露插件对象
import SvgIcon from "@/components/svgIcon/SvgIcon.vue";
// svg插件需要配置的代码
import 'virtual:svg-icons-register'
const globalComponents = {SvgIcon}
export default {
//插件对象中
install(app){
for (const [key, component] of Object.entries(globalComponents)) {
app.component(key, component)
}
}
}
SvgIcon 组件
<template>
<!-- 测试SVG图标的使用 -->
<!-- svg:图标外层容器节点,内部需要与use标签结合使用 -->
<svg
:width="width"
:height="height"
>
<!-- xlink:href 执行用哪一个图标,属性值务必 #icon-文件名字 不加后缀 -->
<use
:xlink:href="prefix + iconName"
:fill="color"
></use>
</svg>
</template>
<script setup lang="ts" name="SvgIcon">
withDefaults(
defineProps<{
iconName: string
prefix?: string
color?: string
width?: string
height?: string
}>(),
{
prefix: '#icon-',
color: 'red',
width: '16px',
height: '16px',
},
)
</script>
<style lang="css" scoped>
</style>
main.ts
import { createApp } from 'vue'
import App from '@/App.vue'
//引入自定义插件
import globalComponentsPlugins from './components/globalComponentsPlugins'
import elementPlugins from './components/elementPlugins'
const app = createApp(App)
app.use(globalComponentsPlugins)
app.use(elementPlugins)
app.mount('#app')
6.集成sass
stylelint为css的lint工具。可格式化css代码,检查css语法错误与不合理的写法,指定css书写顺序等。
我们的项目中使用scss作为预处理器,安装以下依赖:
pnpm add sass sass-loader stylelint postcss postcss-scss postcss-html stylelint-config-prettier stylelint-config-recess-order stylelint-config-recommended-scss stylelint-config-standard stylelint-config-standard-vue stylelint-scss stylelint-order stylelint-config-standard-scss -D
.stylelintrc.cjs
配置文件
// @see https://stylelint.bootcss.com/
module.exports = {
extends: [
'stylelint-config-standard', // 配置stylelint拓展插件
'stylelint-config-html/vue', // 配置 vue 中 template 样式格式化
'stylelint-config-standard-scss', // 配置stylelint scss插件
'stylelint-config-recommended-vue/scss', // 配置 vue 中 scss 样式格式化
'stylelint-config-recess-order', // 配置stylelint css属性书写顺序插件,
'stylelint-config-prettier', // 配置stylelint和prettier兼容
],
overrides: [
{
files: ['**/*.(scss|css|vue|html)'],
customSyntax: 'postcss-scss',
},
{
files: ['**/*.(html|vue)'],
customSyntax: 'postcss-html',
},
],
ignoreFiles: [
'**/*.js',
'**/*.jsx',
'**/*.tsx',
'**/*.ts',
'**/*.json',
'**/*.md',
'**/*.yaml',
],
/**
* null => 关闭该规则
* always => 必须
*/
rules: {
'value-keyword-case': null, // 在 css 中使用 v-bind,不报错
'no-descending-specificity': null, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器
'function-url-quotes': 'always', // 要求或禁止 URL 的引号 "always(必须加上引号)"|"never(没有引号)"
'no-empty-source': null, // 关闭禁止空源码
'selector-class-pattern': null, // 关闭强制选择器类名的格式
'property-no-unknown': null, // 禁止未知的属性(true 为不允许)
'block-opening-brace-space-before': 'always', //大括号之前必须有一个空格或不能有空白符
'value-no-vendor-prefix': null, // 关闭 属性值前缀 --webkit-box
'property-no-vendor-prefix': null, // 关闭 属性前缀 -webkit-mask
'selector-pseudo-class-no-unknown': [
// 不允许未知的选择器
true,
{
ignorePseudoClasses: ['global', 'v-deep', 'deep'], // 忽略属性,修改element默认样式的时候能使用到
},
],
},
}
.stylelintignore忽略文件
/node_modules/*
/dist/*
/html/*
/public/*
运行脚本
"scripts": {
"lint:style": "stylelint src/**/*.{css,scss,vue} --cache --fix"
}
最后配置统一的prettier来格式化我们的js和css,html代码
"scripts": {
"dev": "vite --open",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint": "eslint src",
"fix": "eslint src --fix",
"format": "prettier --write \"./**/*.{html,vue,ts,js,json,md}\"",
"lint:eslint": "eslint src/**/*.{ts,vue} --cache --fix",
"lint:style": "stylelint src/**/*.{css,scss,vue} --cache --fix"
},
测试sass语法
<template>
<div>
<h1>测试代码</h1>
</div>
</template>
<script setup lang="ts" name="App">
</script>
<style lang="scss" scoped>
div {
h1 {
color: red;
}
}
</style>
7.清除默认样式
reset.scss
*,
*:after,
*:before {
box-sizing: border-box;
outline: none;
}
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
font: inherit;
font-size: 100%;
margin: 0;
padding: 0;
vertical-align: baseline;
border: 0;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
&:before,
&:after {
content: '';
content: none;
}
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -.5em;
}
sub {
bottom: -.25em;
}
table {
border-spacing: 0;
border-collapse: collapse;
}
input,
textarea,
button {
font-family: inhert;
font-size: inherit;
color: inherit;
}
select {
text-indent: .01px;
text-overflow: '';
border: 0;
border-radius: 0;
-webkit-appearance: none;
-moz-appearance: none;
}
select::-ms-expand {
display: none;
}
code,
pre {
font-family: monospace, monospace;
font-size: 1em;
}
index.scss
//引入清除默认样式
@import url(./reset.scss);
main.ts
import { createApp } from 'vue'
import App from '@/App.vue'
import '@/styles/index.scss'
//引入自定义插件
import globalComponentsPlugins from './components/globalComponentsPlugins'
import elementPlugins from './components/elementPlugins'
const app = createApp(App)
app.use(globalComponentsPlugins)
app.use(elementPlugins)
app.mount('#app')
h1样式被消除了
8.为sass配置全局变量
但是你会发现在src/styles/index.scss全局样式文件中没有办法使用$变量.因此需要给项目中引入全局变量$.
在style/variable.scss创建一个variable.scss文件!
在vite.config.ts文件配置如下:
export default defineConfig((config) => {
css: {
preprocessorOptions: {
scss: {
javascriptEnabled: true,
additionalData: '@import "./src/styles/variable.scss";',
},
},
},
}
variable.scss
//给项目提供scss全局变量
$color:red;
9.axios二次封装
先安装axios
pnpm i axios
封装 request.ts
/* eslint-disable @typescript-eslint/no-explicit-any */
//进行axios 二次封装
import axios from "axios";
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
import { ElMessage } from "element-plus";
import { R } from "@/type/GlobalType";
//第一步:利用axios对象的create方法,去创建一个axios实例(其他的配置:基础路径,超时的时间)
const request = axios.create({
//基础路径
baseURL: import.meta.env.VITE_APP_BASE_API,//基础路径尚会携带/api
timeout: 30000 //超时的时间的设置
})
//第二部 :request实例添加请求与响应拦截器
request.interceptors.request.use((config) => {
//config配置对象,headers属性请求头,经常给服务器端携带公共参数
// config.headers.token = '123'
// console.log(config);
// config.url += "?token=123"
// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
return config;//返回配置对象,执行链
})
//第三步:响应拦截器
request.interceptors.response.use((response) => {
//成功的回调
//简化数据
return response.data;
}, (error) => {
//失败的回调:处理http网络错误
//定义一个变量:存储网络错误的信息
let message = '';
//http状态码
console.log(error);
const status = error.status
if (!status) {
message = '网络路径错误'
ElMessage({
type: 'error',
message
})
//需要返回终结这个对象
return Promise.reject(error);
}
message = error.response.data.message
ElMessage({
type: 'error',
message
})
//需要返回终结这个对象
return Promise.reject(error);
})
export type Query = {
[key: string]: string | number | boolean
}
export type Header = {
[key: string]: string
}
/**
* T 响应的类型 解体
*/
class HttpHandler<T> {
private requestProxy: AxiosInstance = request;
private config: AxiosRequestConfig<any> = {};
get(url: string): this {
this.config.method = 'get';
this.config.url = url;
return this;
}
post(url: string): this {
this.config.method = 'post';
this.config.url = url;
return this;
}
put(url: string): this {
this.config.method = 'put';
this.config.url = url;
return this;
}
delete(url: string): this {
this.config.method = 'delete';
this.config.url = url;
return this;
}
query(params: Query): this {
/**
* 给它赋值
*/
if (!this.config.params) {
this.config.params = params
} else {
Object.assign(this.config.params, params)
}
return this;
}
header(header: Header): this {
for (const [key, value] of Object.entries(header)) {
header[key] = encodeURIComponent(value)
}
/**
* 给它赋值
*/
if (!this.config.headers) {
this.config.headers = header
} else {
const headers: any = this.config.headers
Object.assign(headers, header)
}
return this
}
data(obj: any): this {
/**
* 给它赋值
*/
if (!this.config.data) {
this.config.data = obj
} else {
const d: any = this.config.data
Object.assign(d, obj)
}
return this;
}
excute(): Promise<R<T>> {
return this.requestProxy(this.config)
}
}
export default function $http<T>(): HttpHandler<T> {
return new HttpHandler<T>()
}
function getData() {
$http().post(`/user/getUserInfo/你好`)
.query({
page:1,
size:10,
type:'你好啊'
}).header({
token:'123',
gou:'213'
})
.excute().then((res) => {
userList.value = res.data
}).catch(err => {
console.log(err)
})
}
10.API接口统一管理
type
import { Query } from "@/utils/request";
export interface LoginVo {
userId: number,
userName: string,
token: string
}
export interface LoginForm extends Query {
login: string;
pwd: string;
}
export interface User {
id: number;
userName: string;
login: string;
pwd: string;
createTime: string;
updateTime: string;
createUserId: string;
updateUserId: string;
}
index.ts
/* eslint-disable @typescript-eslint/no-explicit-any */
//统一管理咱们项目用户相关的接口
import $http from "@/utils/request";
import { LoginForm, LoginVo, User } from "./type";
import {AxiosResponse} from "axios";
import { R } from "@/type/GlobalType";
//统一管理接口
const userPrefix = "/user"
enum API {
LOGIN_URL = userPrefix + "/login",
USER_INFO_URL = userPrefix + "/getUserInfo",
}
/**
* 获取用户信息
* @returns user
*/
export function getUserInfo():Promise<AxiosResponse<User[]>> {
return $http()
.get(API.USER_INFO_URL)
.excute()
}
/**
* 登录接口
*/
export function login(data:LoginForm):Promise<AxiosResponse<R<LoginVo>>> {
return $http()
.post(API.LOGIN_URL)
.query(data)
.excute()
}
<template>
<div>
</div>
</template>
<script setup lang="ts" name="App">
import { onMounted } from 'vue';
import { login } from './api/user';
onMounted(()=>{
login({login:'admin',pwd:'admin'}).then(res=>{
console.log(res);
})
})
</script>
<style lang="scss" scoped>
</style>
import { onMounted } from 'vue'
import { getUserInfo } from './api/user'
onMounted(() => {
getUserInfo().then((res) => {
console.log(res.data[0].updateTime)
})
})