# vue3_admin
**Repository Path**: littleluoCode/vue3_admin
## Basic Information
- **Project Name**: vue3_admin
- **Description**: 基于Vue 3的现代化后台管理系统模板,集成最新前端技术栈,提供高效、易用的管理界面解决方案。
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-02-16
- **Last Updated**: 2025-03-04
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Vue 3 + TypeScript + Vite 硅谷甄选平台
## 一、项目初始化
```sh
yarn create vite
cd vue3_admin
yarn
yarn dev
```
## 二、项目配置
### 2.1 ESLint + Prettier 配置(新版)
[ESLint 中文官网](https://eslint.nodejs.cn/)
[Prettier 中文官网](https://www.prettier.cn/)
**详细参考掘金大佬 [2024年|ESlint9+Prettier从0开始配置教程2024年,教你从0开始认识和配置ESlint。本文将从0开 - 掘金](https://juejin.cn/post/7402696141495779363)**
安装 `ESLint` 、`prettier`等相关插件
```sh
yarn add eslint -D
yarn add prettier -D
yarn add @eslint/js -D
yarn add globals -D
yarn add eslint-plugin-vue typescript-eslint -D
yarn add @stylistic/eslint-plugin -D
yarn add eslint-plugin-prettier -D
```
在项目**根目录**中创建一份配置文件,从`ESlint9.x`开始,建议直接用`eslint.config.js`
```js
import globals from 'globals';
import tseslint from 'typescript-eslint';
import pluginVue from 'eslint-plugin-vue';
import eslint from '@eslint/js';
import stylistic from '@stylistic/eslint-plugin';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
/** @type {import('eslint').Linter.Config[]} */
export default tseslint.config(
{
ignores: ['node_modules', 'dist', 'public'],
files: ['**/*.{js,mjs,cjs,ts,vue}'],
},
/** js推荐配置 */
eslint.configs.recommended,
/** ts推荐配置 */
...tseslint.configs.recommended,
/** vue推荐配置 */
...pluginVue.configs['flat/recommended'],
/** stylistic 配置 */
stylistic.configs.customize({
indent: 2, // 缩进为 2 个空格
quotes: 'single', // 单引号
semi: true, // 语句末尾需要分号
jsx: true, // 允许在 JSX 中使用单引号
arrowParens: 'always', // 箭头函数参数需要括号
}),
/**
* javascript 规则
*/
{
files: ['**/*.{js,mjs,cjs,vue}'],
rules: {
'no-console': 'error',
},
},
/**
* 配置全局变量
*/
{
languageOptions: {
globals: {
...globals.browser,
/** 追加一些其他自定义全局规则 */
wx: true,
},
},
},
/**
* Vue 规则
*/
{
files: ['**/*.vue'],
languageOptions: {
parserOptions: {
/** typescript项目需要用到这个 */
parser: tseslint.parser,
ecmaVersion: 'latest',
/** 允许在.vue 文件中使用 JSX */
ecmaFeatures: {
jsx: true,
},
},
},
rules: {
// 在这里追加 vue 规则
'vue/no-mutating-props': [
'off',
{
shallowOnly: true,
},
],
'vue/html-indent': ['error', 2],
'vue/multi-word-component-names': 'off',
'vue/max-attributes-per-line': 'off',
},
},
/**
* typescript 规则
*/
{
files: ['**/*.{ts,tsx,vue}'],
rules: {},
},
/**
* prettier 配置
* off与它冲突的ESlint规则
* 并且启用自己的Recommended规则
* 会合并根目录下的prettier.config.js 文件
* @see https://prettier.io/docs/en/options
*/
eslintPluginPrettierRecommended,
);
```
在根目录创建一份`prettier.config.js`,配置如下
```js
/**
* @type {import('prettier').Config}
* @see https://www.prettier.cn/docs/options.html
*/
export default {
trailingComma: 'all',
singleQuote: true,
semi: true,
printWidth: 80,
arrowParens: 'always',
proseWrap: 'always',
endOfLine: 'auto',
experimentalTernaries: false,
tabWidth: 2,
useTabs: false,
quoteProps: 'consistent',
jsxSingleQuote: false,
bracketSpacing: true,
bracketSameLine: false,
jsxBracketSameLine: false,
vueIndentScriptAndStyle: false,
singleAttributePerLine: false,
};
```
`package.json` 文件中增加以下代码
```json
"scripts": {
"lint": "eslint src",
"fix": "eslint src --fix",
}
```
### 2.2 ESLint 代码检查工具配置(旧版)
[ESLint 中文官网](https://eslint.nodejs.cn/)
```sh
yarn add eslint -D
yarn eslint --init
```
`yarn eslint --init` 后会生成一份 ESLint 配置文件 `eslint.config.js`
修改如下配置
```js
import globals from 'globals';
import pluginJs from '@eslint/js';
import tseslint from 'typescript-eslint';
import pluginVue from 'eslint-plugin-vue';
/** @type {import('eslint').Linter.Config[]} */
export default [
{
files: ['**/*.{js,mjs,cjs,ts,vue}'],
languageOptions: {
parserOptions: {
parser: '@typescript-eslint/parser',
sourceType: 'module',
ecmaVersion: 'latest',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true,
},
},
},
rules: {
// eslint(https://eslint.bootcss.com/docs/rules/)
'no-var': 'error', // 要求使用 let 或 const 而不是 var
'no-multiple-empty-lines': ['warn', { max: 1 }], // 不允许多个空行
'no-console':
process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger':
process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-unexpected-multiline': 'error', // 禁止空余的多行
'no-useless-escape': 'off', // 禁止不必要的转义字符
// typeScript (https://typescript-eslint.io/rules)
'@typescript-eslint/no-unused-vars': 'error', // 禁止定义未使用的变量
'@typescript-eslint/prefer-ts-expect-error': 'error', // 禁止使用 @ts-ignore
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-namespace': 'off', // 禁止使用自定义 TypeScript 模块和命名空间。
'@typescript-eslint/semi': 'off',
// eslint-plugin-vue (https://eslint.vuejs.org/rules/)
'vue/multi-word-component-names': 'off', // 要求组件名称始终为 “-” 链接的单词
'vue/script-setup-uses-vars': 'error', // 防止
```
在src文件夹目录下创建一个index.ts文件:用于注册components文件夹内部全部全局组件!!!
```
import SvgIcon from './SvgIcon/index.vue';
import type { App, Component } from 'vue';
const components: { [name: string]: Component } = { SvgIcon };
export default {
install(app: App) {
Object.keys(components).forEach((key: string) => {
app.component(key, components[key]);
})
}
}
```
在入口文件引入src/index.ts文件,通过app.use方法安装自定义插件
```
import gloablComponent from './components/index';
app.use(gloablComponent);
```
### 5. 集成 Sass
我们目前在组件内部已经可以使用scss样式,因为在配置styleLint工具的时候,项目当中已经安装过sass sass-loader,因此我们再组件内可以使用scss语法!!!需要加上lang="scss"
```vue
```
接下来我们为项目添加一些全局的样式
在src/styles目录下创建一个index.scss文件,当然项目中需要用到清除默认样式,因此在index.scss引入reset.scss
```scss
@use ./reset.scss
```
`reset.scss`
```scss
/**
* Modern CSS Reset Tweaks
* ==================================================
* A collection of modern CSS reset and normalization styles
* to ensure consistent behavior across browsers, OS and devices.
*/
/* Ensure consistent font resizing on mobile devices */
html {
-webkit-text-size-adjust: 100%;
&:focus-within {
scroll-behavior: smooth;
}
}
/* Basic body setup for layout and text rendering optimization */
body {
text-size-adjust: 100%;
position: relative;
width: 100%;
min-height: 100vh;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeSpeed;
}
/* Apply box-sizing globally for consistent element sizing */
*,
::after,
::before {
box-sizing: border-box;
}
/* Style unclassed links for better accessibility */
a:not([class]) {
text-decoration-skip-ink: auto;
}
/**
* CSS Reset Tweaks
* Based on Eric Meyer's CSS Reset v2.0-modified (public domain)
* URL: http://meyerweb.com/eric/tools/css/reset/
*/
a,
abbr,
acronym,
address,
applet,
article,
aside,
audio,
b,
big,
blockquote,
body,
br,
button,
canvas,
caption,
center,
cite,
code,
col,
colgroup,
data,
datalist,
dd,
del,
details,
dfn,
div,
dl,
dt,
em,
embed,
fieldset,
figcaption,
figure,
footer,
form,
h1,
h2,
h3,
h4,
h5,
h6,
head,
header,
hgroup,
hr,
html,
i,
iframe,
img,
input,
ins,
kbd,
label,
legend,
li,
link,
main,
map,
mark,
menu,
meta,
meter,
nav,
noscript,
object,
ol,
optgroup,
option,
output,
p,
param,
picture,
pre,
progress,
q,
rb,
rp,
rt,
rtc,
ruby,
s,
samp,
script,
section,
select,
small,
source,
span,
strong,
style,
svg,
sub,
summary,
sup,
table,
tbody,
td,
template,
textarea,
tfoot,
th,
thead,
time,
title,
tr,
track,
tt,
u,
ul,
var,
video,
wbr {
font-size: 100%;
font: inherit;
margin: 0;
padding: 0;
border: 0;
vertical-align: baseline;
}
/* Add focus styles to improve accessibility */
:focus {
outline: 0;
}
/* Normalize HTML5 elements for older browsers */
article,
aside,
details,
embed,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
object,
section {
display: block;
}
canvas,
iframe {
max-width: 100%;
height: auto;
display: block
}
/* Remove default list styling */
ol,
ul {
list-style: none;
}
/* Normalize quote styling */
blockquote,
q {
quotes: none;
&:before,
&:after {
content: '';
content: none;
}
}
/* Reset and normalize form inputs */
input:required,
input {
box-shadow: none;
}
/* Autofill styling for better compatibility */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 30px white inset;
}
/* Improve appearance of search inputs */
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;
appearance: none;
}
input[type=search] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
textarea {
overflow: auto;
vertical-align: top;
resize: vertical;
}
input {
&:focus {
outline: none;
}
}
video {
background: #000;
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address styling not present in IE 7/8/9, Firefox 3, and Safari 4.
*/
[hidden] {
display: none;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: none;
}
/**
* Make media easier to work with
*/
audio,
img,
picture,
svg,
video {
max-width: 100%;
display: inline-block;
vertical-align: middle;
height: auto;
}
/**
* Address Firefox 3+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+.
* Correct `select` style inheritance in Firefox 4+ and Opera.
*/
button,
select {
text-transform: none;
}
button,
html input[type="button"],
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button;
appearance: button;
cursor: pointer;
border: 0;
background: transparent;
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/* Additional attribute handling for accessibility */
[disabled],
[disabled="true"],
[aria-disabled="true"] {
pointer-events: none;
}
/**
* Address box sizing set to content-box in IE 8/9.
*/
input[type="checkbox"],
input[type="radio"] {
padding: 0;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield;
appearance: textfield;
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Remove inner padding and border in Firefox 3+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
button {
border: 0;
background: transparent;
}
textarea {
overflow: auto;
vertical-align: top;
resize: vertical;
}
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
text-indent: 0;
}
/**
* Based on normalize.css v8.0.1
* github.com/necolas/normalize.css
*/
hr {
box-sizing: content-box;
overflow: visible;
background: #000;
border: 0;
height: 1px;
line-height: 0;
margin: 0;
padding: 0;
page-break-after: always;
width: 100%;
}
/**
* Correct the inheritance and scaling of font size in all browsers.
*/
pre {
font-family: monospace, monospace;
font-size: 100%;
}
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none;
text-decoration: none;
}
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 75%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -5px;
}
sup {
top: -5px;
}
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
font-size: 100%;
line-height: 1;
margin: 0;
padding: 0;
}
/**
* Show the overflow in IE and Edge.
*/
button,
input {
/* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
*/
button,
select {
/* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
outline: 0;
}
legend {
color: inherit;
white-space: normal;
display: block;
border: 0;
max-width: 100%;
width: 100%;
}
fieldset {
min-width: 0;
}
body:not(:-moz-handler-blocked) fieldset {
display: block;
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield;
appearance: textfield;
/* 1 */
outline-offset: -2px;
/* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button;
/* 1 */
font: inherit;
/* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
template {
display: none;
}
```
在入口文件 `main.ts`引入
```ts
// 引入 sass 全局样式
import '@/styles/index.scss';
```
但是你会发现在src/styles/index.scss全局样式文件中没有办法使用$变量.因此需要给项目中引入全局变量$.
在styles/variable.scss创建一个variable.scss文件!
在vite.config.ts文件配置如下:
```
export default defineConfig({
plugins: [
vue(),
// element-plus 自动导入插件
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
// svg插件
createSvgIconsPlugin({
// Specify the icon folder to be cached
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
// Specify symbolId format
symbolId: 'icon-[dir]-[name]',
}),
],
resolve: {
// 设置文件./src路径为 @
alias: [
{
find: '@',
replacement: resolve(__dirname, './src'),
},
],
},
// scss全局变量
css: {
preprocessorOptions: {
scss: {
additionalData: `
@use '@/styles/variable.scss' as *;
`,
},
},
},
});
```
### 6. 集成 Mock
安装依赖:https://www.npmjs.com/package/vite-plugin-mock
```sh
yarn add -D vite-plugin-mock mockjs
```
在 vite.config.js 配置文件启用插件。
```
import { viteMockServe } from 'vite-plugin-mock'
import vue from '@vitejs/plugin-vue'
export default ({ command })=> {
return {
plugins: [
vue(),
viteMockServe({
enabled: command === 'serve',
}),
],
}
}
```
在根目录创建mock文件夹:去创建我们需要mock数据与接口!!!
在mock文件夹内部创建一个user.ts文件
```
// 用户信息数据
/**
* 创建用户列表,返回包含用户信息的数组。
* @returns {Array} 包含用户信息的数组,每个用户信息为一个对象。
*/
function createUserList() {
return [
{
userId: 1,
avatar:
'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
username: 'admin',
password: '111111',
desc: '平台管理员',
roles: ['平台管理员'],
buttons: ['cuser.detail'],
routes: ['home'],
token: 'Admin Token',
},
{
userId: 2,
avatar:
'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
username: 'system',
password: '111111',
desc: '系统管理员',
roles: ['系统管理员'],
buttons: ['cuser.detail', 'cuser.user'],
routes: ['home'],
token: 'System Token',
},
];
}
/**
* 导出一个包含模拟接口配置的数组,用于模拟用户登录和获取用户信息的接口响应。
* @type {Array}
*/
export default [
// 用户登录接口
{
url: '/api/user/login', // 请求地址
method: 'post', // 请求方式
response: ({ body }) => {
// 获取请求体携带过来的用户名与密码
const { username, password } = body;
// 调用获取用户信息函数,用于判断是否有此用户
const checkUser = createUserList().find(
(item) => item.username === username && item.password === password,
);
// 没有用户返回失败信息
if (!checkUser) {
return { code: 201, data: { message: '账号或者密码不正确' } };
}
// 如果有返回成功信息
const { token } = checkUser;
return { code: 200, data: { token } };
},
},
// 获取用户信息
{
url: '/api/user/info',
method: 'get',
response: (request) => {
// 获取请求头携带token
const token = request.headers.token;
// 查看用户信息是否包含有次token用户
const checkUser = createUserList().find((item) => item.token === token);
// 没有返回失败的信息
if (!checkUser) {
return { code: 201, data: { message: '获取用户信息失败' } };
}
// 如果有返回成功信息
return { code: 200, data: { checkUser } };
},
},
];
```
### 7. 集成 axios 并二次封装
安装 `axios`
```
yarn add axios
```
创建文件`src/utils/request.ts` 对`axios` 进行二次封装
```ts
// request.ts
// 二次封装 axios,使用请求与响应拦截器
// 引入 axios
import axios from 'axios';
// 利用 axios 对象的 create 方法创建一个 axios 实例
const request = axios.create({
// 配置对象
// 基础路径,发请求的时候,路径当中会出现 baseURL 会携带 /api
baseURL: import.meta.env.VITE_APP_BASE_API,
// 请求超时时间
timeout: 5000,
});
// 请求拦截器
request.interceptors.request.use(
(config) => {
// config 配置对象,里面有一个属性很重要,headers,给服务器端携带 token
// console.log(config);
return config;
},
(error) => {
// 错误提示
return Promise.reject(error);
},
);
// 响应拦截器
request.interceptors.response.use(
(response) => {
// 响应成功的回调
// console.log(response);
return response.data;
},
(error) => {
// 响应失败的回调
// console.log(error);
let message = '';
const status = error.response.status;
switch (status) {
case 401:
message = 'TOKEN 过期';
break;
case 403:
message = '无权访问';
break;
case 404:
message = '请求地址错误';
break;
case 500:
message = '服务器出现问题';
break;
default:
message = '网络出现问题';
break;
}
// 提示错误信息
ElMessage({
type: 'error',
message,
duration: 2000,
});
return Promise.reject(error);
},
);
// 对外暴露
export default request;
```
### 8. 统一 API 接口管理
在 `src` 目录下新建 `api` 文件夹
目录结构如下
```
src/
├── api/
│ ├── user/
│ │ ├── index.ts
│ ├── product/
│ │ ├── index.ts
│ └── permission/
│ ├── index.ts
```
在 `src` 目录下创建 `types` 文件夹用于管理项目中所有的ts类型
目录结构如下
```
src/
├── types/
│ ├── api/
│ │ ├── user.ts
```
`src/api/user/index.ts` 添加代码
```ts
// 统一管理用户相关的接口
import request from '@/utils/request';
import type {
LoginForm,
LoginResponseData,
UserResponseData,
} from '@/types/api/user';
enum API {
// 获取用户信息
USERINFO_URL = '/user/info',
LOGIN_URL = '/user/login',
}
// 登录接口
export const reqLogin = (data: LoginForm) =>
request.post(API.LOGIN_URL, data);
// 获取用户信息接口
export const reqUserInfo = () =>
request.get(API.USERINFO_URL);
```
`src/types/api/user.ts` 添加代码
```ts
// 登录接口需要携带的参数 ts 类型
export interface LoginForm {
username: string;
password: string;
}
interface DataType {
token: string;
}
// 登录接口返回数据类型
export interface LoginResponseData {
code: number;
data: {
token: DataType;
};
}
interface UserInfo {
userId: number;
avatar: string;
username: string;
password: string;
desc: string;
roles: string[];
buttons: string[];
routes: string[];
token: string;
}
interface User {
checkUser: UserInfo;
}
// 定义服务器返回用户信息相关的数据类型
export interface UserResponseData {
code: number;
data: User;
}
```
## 四、问题集锦
**大部分问题都来自 `ESLint` ,总结:不装 ESLint B事没有**
### 1. ESLint 配置问题
`ESLint` 在版本 `9.0.0` 之后引入了新的配置系统,称为“Flat Config”,使用 `eslint.config.js` 作为配置文件。而传统的配置文件如`.eslintrc.cjs`属于旧版配置系统。
```js
import globals from 'globals';
import tseslint from 'typescript-eslint';
import pluginVue from 'eslint-plugin-vue';
import eslint from '@eslint/js';
import stylistic from '@stylistic/eslint-plugin';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import autoImportConfig from './auto-import.config.js';
/** @type {import('eslint').Linter.Config[]} */
export default tseslint.config(
{
ignores: ['node_modules', 'dist', 'public'],
files: ['**/*.{js,mjs,cjs,ts,vue}'],
},
/** js推荐配置 */
eslint.configs.recommended,
/** ts推荐配置 */
...tseslint.configs.recommended,
/** vue推荐配置 */
...pluginVue.configs['flat/recommended'],
/** stylistic 配置 */
stylistic.configs.customize({
indent: 2, // 缩进为 2 个空格
quotes: 'single', // 单引号
semi: true, // 语句末尾需要分号
jsx: true, // 允许在 JSX 中使用单引号
arrowParens: 'always', // 箭头函数参数需要括号
}),
/**
* javascript 规则
*/
{
files: ['**/*.{js,mjs,cjs,vue}'],
rules: {
'no-console': 'off',
},
},
/**
* 配置全局变量
*/
{
languageOptions: {
globals: {
...autoImportConfig.globals,
...globals.browser,
...globals.node,
/** 追加一些其他自定义全局规则 */
},
},
},
/**
* Vue 规则
*/
{
files: ['**/*.vue'],
languageOptions: {
parser: pluginVue.parser, // 使用 vue-eslint-parser
parserOptions: {
/** typescript项目需要用到这个 */
parser: tseslint.parser,
ecmaVersion: 'latest',
/** 允许在.vue 文件中使用 JSX */
ecmaFeatures: {
jsx: true,
},
},
},
rules: {
// 在这里追加 vue 规则
'vue/no-mutating-props': [
'off',
{
shallowOnly: true,
},
],
'vue/html-indent': ['error', 2],
'vue/multi-word-component-names': 'off',
'vue/max-attributes-per-line': 'off',
},
},
/**
* typescript 规则
*/
{
files: ['**/*.{ts,tsx,vue}'],
rules: {},
},
/**
* prettier 配置
* off与它冲突的ESlint规则
* 并且启用自己的Recommended规则
* 会合并根目录下的prettier.config.js 文件
* @see https://prettier.io/docs/en/options
*/
eslintPluginPrettierRecommended,
);
```
### 2. Element-Plus 自动导入插件配置问题
#### 2.1 vite.config.ts 关键配置
```ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
// 引入element-plus
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
const pathSrc = resolve(__dirname, 'src');
// https://cn.vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
// element-plus 自动导入插件
AutoImport({
// Auto import functions from Vue, e.g. ref, reactive, toRef...
// 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
imports: ['vue', 'vue-router'],
// Auto import functions from Element Plus, e.g. ElMessage, ElMessageBox... (with style)
// 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
resolvers: [
ElementPlusResolver({
importStyle: 'sass', // 按需加载样式(需安装 sass)
directives: true, // 自动导入指令(如 v-loading)
}),
// Auto import icon components
// 自动导入图标组件
IconsResolver({
prefix: 'i', // 统一图标前缀为
enabledCollections: ['ep'], // Element Plus 官方图标
}),
],
vueTemplate: true, // 启用 Vue 模板支持
dts: resolve(__dirname, 'auto-imports.d.ts'),
eslintrc: {
enabled: true
filepath: './auto-import.config.js',
}, // 生成 ESLint 配置
}),
Components({
resolvers: [
// Auto register icon components
// 自动注册图标组件
IconsResolver({
prefix: 'i', // 与 AutoImport 一致
enabledCollections: ['ep'],
// 增加分辨率别名防止 SVG 冲突
alias: {
'i-svg': 'svg',
'i-custom': 'custom',
},
}),
// Auto register Element Plus components
// 自动导入 Element Plus 组件
ElementPlusResolver(),
],
dts: resolve(__dirname, 'components.d.ts'),
types: [
{
from: 'vue-router',
names: ['RouterLink', 'RouterView'], // 自动注册路由组件
},
],
})
]
})
```
#### 2.2 tsconfig 配置
在 `tsconfig.app.json` 中添加
```json
"include": [
"**/*.d.ts" // auto-imports.d.ts 和 components.d.ts 这两个文件
]
```
完整代码如下
```json
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"incremental": true, // 添加 incremental 选项以启用增量编译
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
/* src别名 */
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"**/*.d.ts" // auto-imports.d.ts 和 components.d.ts 这两个文件
]
}
```
#### 2.3 eslint 配置
在 `eslint.config.js` 中添加以下代码
```js
import globals from 'globals';
import tseslint from 'typescript-eslint';
// 自动导入配置
import autoImportConfig from './auto-import.config.js';
/** @type {import('eslint').Linter.Config[]} */
export default tseslint.config(
//
/**
* 配置全局变量
* 放在最后确保优先级
*/
{
languageOptions: {
globals: {
...autoImportConfig.globals,
},
},
},
);
```
完整代码如下
```js
import globals from 'globals';
import tseslint from 'typescript-eslint';
import pluginVue from 'eslint-plugin-vue';
import eslint from '@eslint/js';
// eslint-stylistic 插件
import stylistic from '@stylistic/eslint-plugin';
// eslint-plugin-prettier 插件
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
// 自动导入配置
import autoImportConfig from './auto-import.config.js';
/** @type {import('eslint').Linter.Config[]} */
export default tseslint.config(
{
ignores: ['node_modules', 'dist', 'public'],
files: ['**/*.{js,mjs,cjs,ts,vue}'],
},
/** js推荐配置 */
eslint.configs.recommended,
/** ts推荐配置 */
...tseslint.configs.recommended,
/** vue推荐配置 */
...pluginVue.configs['flat/recommended'],
/** stylistic 配置 */
stylistic.configs.customize({
indent: 2, // 缩进为 2 个空格
quotes: 'single', // 单引号
semi: true, // 语句末尾需要分号
jsx: true, // 允许在 JSX 中使用单引号
arrowParens: 'always', // 箭头函数参数需要括号
}),
/**
* javascript 规则
*/
{
files: ['**/*.{js,mjs,cjs,vue}'],
rules: {
'no-console': 'off', // 是否允许使用 console.log()
},
},
/**
* 配置全局变量
*/
{
languageOptions: {
globals: {
...autoImportConfig.globals,
...globals.browser,
...globals.node,
/** 追加一些其他自定义全局规则 */
},
},
},
/**
* Vue 规则
*/
{
files: ['**/*.vue'],
languageOptions: {
parser: pluginVue.parser, // 使用 vue-eslint-parser
parserOptions: {
/** typescript项目需要用到这个 */
parser: tseslint.parser,
ecmaVersion: 'latest',
/** 允许在.vue 文件中使用 JSX */
ecmaFeatures: {
jsx: true,
},
},
},
rules: {
// 在这里追加 vue 规则
'vue/no-mutating-props': [
'off',
{
shallowOnly: true,
},
],
'vue/html-indent': ['error', 2],
'vue/multi-word-component-names': 'off',
'vue/max-attributes-per-line': 'off',
},
},
/**
* typescript 规则
*/
{
files: ['**/*.{ts,tsx,vue}'],
rules: {},
},
/**
* prettier 配置
* off与它冲突的ESlint规则
* 并且启用自己的Recommended规则
* 会合并根目录下的prettier.config.js 文件
* @see https://prettier.io/docs/en/options
*/
eslintPluginPrettierRecommended,
);
```