Vue3 + Ts + ElementPlus + Vite2 从零搭建后台管理系统(二)
上一章主要讲 Vite 所要常用的配置 Vue3 + Ts + ElementPlus + Vite2 从零搭建后台管理系统(一)
1. 添加 vuex
- 安装
npm install vuex@next --save
在目录 src 下新建 store/index.ts index.ts:
import { InjectionKey } from 'vue'
import { createStore, useStore as baseUseStore, Store } from 'vuex'
import type { App } from 'vue'
// InjectionKey 将store安装到Vue应用程序时提供类型,将类型传递InjectionKey给useStore方法
// 手动声明 state 类型
export interface State {
count: number
}
// 定义注入类型
const key: InjectionKey<Store<State>> = Symbol()
const store = createStore<State>({
state() {
return {
count: 0
}
},
mutations: {
increment(state: State) {
state.count++
}
}
})
// 将类型注入useStore,似乎无效
export function useStore() {
return baseUseStore(key)
}
export function setupStore(app: App<Element>) {
app.use(store, key)
}
export default store
main.ts:
import { createApp } from 'vue'
import { setupStore } from './store' // 状态管理
import App from './App.vue'
import SvgIcon from './components/SvgIcon/index.vue'
import './styles/index.scss'
const app = createApp(App)
app.component('svg-icon', SvgIcon)
setupStore(app) // 引入状态管理
app.mount('#app')
在 App.vue 中即可测试
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<!-- <HelloWorld msg="Hello Vue 3 + TypeScript + Vite" /> -->
<svg-icon iconClass="bug"></svg-icon>
<button @click="addCount">count is: {{ count }}</button>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue'
import { useStore } from 'store/index'
import HelloWorld from './components/HelloWorld.vue'
// console.log('import----', import.meta.env);
export default defineComponent({
name: 'App',
components: {
HelloWorld
},
setup() {
const store = useStore()
const count: any = computed({
get() {
return store.state.count
},
set(value) {
store.commit('increment', value)
}
})
function addCount() {
count.value += 1
}
return {
count,
addCount
}
}
})
</script>
<style></style>
2. 添加 vue-router
- 安装
npm install vue-router@next
在目录 src 下新建 router/index.ts index.ts:
import { createRouter, createWebHistory } from 'vue-router'
import type { App } from 'vue'
import HelloWorld from '../components/HelloWorld.vue'
const routerHistory = createWebHistory()
// createWebHashHistory hash 路由
// createWebHistory history 路由
// createMemoryHistory 带缓存 history 路由
const router = createRouter({
history: routerHistory,
routes: [
{
path: '/',
component: HelloWorld
}
]
})
// 删除/重置路由
export function resetRoute(): void {
router.getRoutes().forEach(route => {
const { name } = route
if (name) {
router.hasRoute(name) && router.removeRoute(name)
}
})
}
export function setupRouter(app: App<Element>) {
app.use(router)
}
export default router
main.ts:
import { createApp } from 'vue'
import { setupStore } from './store' // 状态管理
import router, { setupRouter } from './router' // 路由
import App from './App.vue'
import SvgIcon from './components/SvgIcon/index.vue'
import './styles/index.scss'
const app = createApp(App)
app.component('svg-icon', SvgIcon)
setupRouter(app) // 引入路由
setupStore(app) // 引入状态管理
router.isReady().then(() => {
app.mount('#app')
})
3. 添加 axios
- 安装
npm install axios --save
在目录 src 下新建 utils/request.ts
request.ts:
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
import { ElMessage, ElMessageBox } from 'element-plus'
import router, { resetRoute } from '@/router'
function getSession(key: string) {
let json: any = window.sessionStorage.getItem(key)
return JSON.parse(json)
}
export const PATH_URL: string = 'http://mockjs.test.cn' || import.meta.env.VITE_API_URL
// 配置新建一个 axios 实例
const service: AxiosInstance = axios.create({
baseURL: PATH_URL,
timeout: 50000,
headers: { 'Content-Type': 'application/json' }
})
// 添加请求拦截器
service.interceptors.request.use(
(config: AxiosRequestConfig) => {
if (getSession('token')) {
config.headers.common['Authorization'] = `${getSession('token')}`
}
return config
},
(error: AxiosError) => {
// 对请求错误做些什么
return Promise.reject(error)
}
)
// 添加响应拦截器
service.interceptors.response.use(
(response: AxiosResponse) => {
const res = response.data
if (res.code && res.code !== 0) {
// `token` 过期或者账号已在别处登录
if (res.code === 401 || res.code === 4001) {
window.sessionStorage.clear() // 清除浏览器全部临时缓存
router.push('/login') // 去登录页面
resetRoute() // 删除/重置路由
ElMessageBox.alert('你已被登出,请重新登录', '提示', {})
.then(() => {})
.catch(() => {})
}
return Promise.reject(service.interceptors.response)
} else {
return response.data
}
},
(error: AxiosError) => {
if (error.message.indexOf('timeout') != -1) {
ElMessage.error('网络超时')
} else if (error.message == 'Network Error') {
ElMessage.error('网络连接错误')
} else {
ElMessage.error(error.message)
}
return Promise.reject(error)
}
)
export default service
4. 添加 element-plus
全局引入比较简单这里按需引入
-
安装
npm install element-plus --save
npm install vite-plugin-style-import -D
在目录 src 下新建 libs/element.ts
// 按需加载element
import type { App } from 'vue'
import {
ElAlert,
ElAside,
ElAutocomplete,
ElAvatar,
ElBacktop,
ElBadge,
ElBreadcrumb,
ElBreadcrumbItem,
ElButton,
ElButtonGroup,
ElCalendar,
ElCard,
ElCarousel,
ElCarouselItem,
ElCascader,
ElCascaderPanel,
ElCheckbox,
ElCheckboxButton,
ElCheckboxGroup,
ElCol,
ElCollapse,
ElCollapseItem,
ElCollapseTransition,
ElColorPicker,
ElContainer,
ElDatePicker,
ElDialog,
ElDivider,
ElDrawer,
ElDropdown,
ElDropdownItem,
ElDropdownMenu,
ElFooter,
ElForm,
ElFormItem,
ElHeader,
ElIcon,
ElImage,
ElInput,
ElInputNumber,
ElLink,
ElMain,
ElMenu,
ElMenuItem,
ElMenuItemGroup,
ElOption,
ElOptionGroup,
ElPageHeader,
ElPagination,
ElPopconfirm,
ElPopover,
ElPopper,
ElProgress,
ElRadio,
ElRadioButton,
ElRadioGroup,
ElRate,
ElRow,
ElScrollbar,
ElSelect,
ElSlider,
ElStep,
ElSteps,
ElSubmenu,
ElSwitch,
ElTabPane,
ElTable,
ElTableColumn,
ElTabs,
ElTag,
ElTimePicker,
ElTimeSelect,
ElTimeline,
ElTimelineItem,
ElTooltip,
ElTransfer,
ElTree,
ElUpload,
ElInfiniteScroll,
ElLoading,
ElMessage,
ElMessageBox,
ElNotification
} from 'element-plus'
import locale from 'element-plus/lib/locale'
import lang from 'element-plus/lib/locale/lang/zh-cn'
// 设置语言
locale.use(lang)
const components = [
ElAlert,
ElAside,
ElAutocomplete,
ElAvatar,
ElBacktop,
ElBadge,
ElBreadcrumb,
ElBreadcrumbItem,
ElButton,
ElButtonGroup,
ElCalendar,
ElCard,
ElCarousel,
ElCarouselItem,
ElCascader,
ElCascaderPanel,
ElCheckbox,
ElCheckboxButton,
ElCheckboxGroup,
ElCol,
ElCollapse,
ElCollapseItem,
ElCollapseTransition,
ElColorPicker,
ElContainer,
ElDatePicker,
ElDialog,
ElDivider,
ElDrawer,
ElDropdown,
ElDropdownItem,
ElDropdownMenu,
ElFooter,
ElForm,
ElFormItem,
ElHeader,
ElIcon,
ElImage,
ElInput,
ElInputNumber,
ElLink,
ElMain,
ElMenu,
ElMenuItem,
ElMenuItemGroup,
ElOption,
ElOptionGroup,
ElPageHeader,
ElPagination,
ElPopconfirm,
ElPopover,
ElPopper,
ElProgress,
ElRadio,
ElRadioButton,
ElRadioGroup,
ElRate,
ElRow,
ElScrollbar,
ElSelect,
ElSlider,
ElStep,
ElSteps,
ElSubmenu,
ElSwitch,
ElTabPane,
ElTable,
ElTableColumn,
ElTabs,
ElTag,
ElTimePicker,
ElTimeSelect,
ElTimeline,
ElTimelineItem,
ElTooltip,
ElTransfer,
ElTree,
ElUpload
]
const plugins = [ElInfiniteScroll, ElLoading, ElMessage, ElMessageBox, ElNotification]
export function setupElement(app: App<Element>): void {
components.forEach((component: any) => {
app.component(component.name, component)
})
plugins.forEach((plugin: any) => {
app.use(plugin)
})
// 全局配置
app.config.globalProperties.$ELEMENT = { size: 'small', zIndex: 3000 }
}
然后,将 vite.config.js 修改为:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import styleImport from 'vite-plugin-style-import'
import { svgBuilder } from './src/plugins/svgBuilder'
const resolve = (dir: string) => path.join(__dirname, dir)
// 环境变量 https://blog.csdn.net/chendf__/article/details/115676683
// https://vitejs.dev/config/
export default defineConfig({
base: './',
plugins: [
vue(),
[svgBuilder('./src/assets/icons/svg/')], // 这里已经将src/icons/svg/下的svg全部导入,无需再单独导入],
styleImport({
libs: [
{
libraryName: 'element-plus',
esModule: true,
ensureStyleFile: true,
resolveStyle: name => {
name = name.slice(3)
return `element-plus/packages/theme-chalk/src/${name}.scss`
},
resolveComponent: name => {
return `element-plus/lib/${name}`
}
}
]
})
],
resolve: {
alias: {
'@': resolve('src'),
comps: resolve('src/components'),
apis: resolve('src/apis'),
views: resolve('src/views'),
utils: resolve('src/utils'),
store: resolve('src/store'),
routes: resolve('src/routes'),
styles: resolve('src/styles')
}
},
server: {
//服务器主机名
host: '',
//端口号
port: 3088,
//设为 true 时若端口已被占用则会直接退出,而不是尝试下一个可用端口
strictPort: false,
//服务器启动时自动在浏览器中打开应用程序,当此值为字符串时,会被用作 URL 的路径名
open: false,
//自定义代理规则
proxy: {
// 选项写法
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
})
然后可以在就可以使用 Element UI 组件: