# 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, ); ```