Vue3 + Ts + ElementPlus + Vite2 从零搭建后台管理系统(二)

6,025 阅读2分钟

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 组件:

image.png

image.png