Detailed configuration of Vue3 + TypeScript + Vite + Eslint

foreword

In the process of using Vue3 + TypeScript, this writing method is indeed more comfortable after adapting. Through Vue3's setup syntax sugar, I can write all the things of a functional module into a fragment, instead of as before, A function involves variables, methods, watches, computed, etc. one by one, and when it needs to be maintained, it has to be turned up and down.
And some bigwigs on the Internet, including a few front-end friends around me, are criticizing TypeScript. After all, they are used to streaking and suddenly add layers of clothes to you. Haha, it is inevitable that you will not adapt. When I first started using TypeScript, I was quite uncomfortable, and the editor was often red. However, ts can help me be more rigorous when writing code, and avoid various errors when running the project. . Personally, I would rather have a red article in the editor than be full of mistakes in the browser. After all, my level is not high, I only have so much knowledge.
Here, I still want to talk about the relationship between ts and js. ts is a superset of js. It is simple and vulgar to understand. ts adds a type system to js. ts provides all the functions of js. If you want to use ts well The premise is to master js proficiently.

Preparatory work before building the project

You can read the start item of the vite Chinese document, vite requires Node.js14.18 or above, I use nvm, and installed version 14.19.1.
https://cn.vitejs.dev/vite Chinese document
https://blog.csdn.net/m0_64697285/article/details/127318141Refer to this blogger's article to install the nvm tool

Build the vite project

编辑器终端中,使用npm构建vite项目
npm create vite@latest

输入项目名
? Project name: » vue3_demo

选择Vue框架
? Select a framework: » - Use arrow-keys. Return to submit.
    Vanilla
>   Vue
    React
    Preact
    Lit
    Svelte
    Others

选择TypeScript
? Select a variant: » - Use arrow-keys. Return to submit.
    JavaScript
>   TypeScript
    Customize with create-vue ↗
    Nuxt ↗

进入创建的项目  可以直接控制台 cd vue3_demo 也可以编辑器打开文件夹vue3_demo
npm install
npm run dev

Configure eslint and prettier

vscode install plugin

  • Prettier - Code formatter (formatting)
  • eslint
  • Stylelint
1.安装插件
npm install -D eslint eslint-plugin-vue
2.进行eslint初始化
npm init @eslint/config

下面是我自己配置时候的选项,根据自己的需要去选择
? How would you like to use ESLint? ... => To check syntax and find problems
? What type of modules does your project use? ... => JavaScript modules (import/export)
? Which framework does your project use? ... => Vue.js
Does your project use TypeScript? => Yes
? Where does your code run? ... => Browser, Node
? What format do you want your config file to be in? ... => JavaScript
? Would you like to install them now? => Yes
? Which package manager do you want to use? ...  => npm

运行完后,根目录下会生成.eslintrc.cjs文件
下面是我自己的配置项,非常简单的配置
module.exports = {
    
    
  env: {
    
    
    browser: true,
    es2021: true,
    node: true
  },
  extends: [
    'eslint:recommended',
    'plugin:vue/vue3-essential',
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended'  // 这里是将prettier作为插件被eslint使用,避免两者有冲突,放在extends最下方
  ],
  overrides: [],
  parser: 'vue-eslint-parser',
  parserOptions: {
    
    
    ecmaVersion: 'latest',
    sourceType: 'module',
    parser: '@typescript-eslint/parser'
  },
  plugins: ['vue', '@typescript-eslint'],
  rules: {
    
    
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'max-len': ['error', 100], // 强制一行的最大长度
    'no-shadow': 'off', // 禁止变量声明与外层作用域的变量同名
    'no-unused-vars': 'warn', // 禁止出现未使用过的变量
    eqeqeq: 'off', // 要求使用 === 和 !==
    'prefer-const': 'warn', // 要求使用 const 声明那些声明后不再被修改的变量
    '@typescript-eslint/no-empty-function': 'off', // 不允许空函数
    '@typescript-eslint/no-explicit-any': 'off' // 禁止使用 any 类型
  }
}


由于是vite构建的项目,需要添加插件,让vite识别eslint相关配置
npm install vite-plugin-eslint --save-dev

修改vite.config.ts
import eslint from 'vite-plugin-eslint' // 新增
plugins: [vue(), eslint()], // 新增 eslint()

配置编辑器保存后eslint自动修复代码
vscode => setting.json
{
    
    
  "workbench.colorTheme": "Bluloco Light",
  "[json]": {
    
    
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascript]": {
    
    
    "editor.defaultFormatter": "vscode.typescript-language-features"
  },
  "editor.fontSize": 16,
  "[vue]": {
    
    
    "editor.defaultFormatter": "Vue.volar"
  },
  "workbench.editor.enablePreview": false,
  "[typescript]": {
    
    
    "editor.defaultFormatter": "vscode.typescript-language-features"
  },
  "javascript.format.insertSpaceBeforeFunctionParenthesis": true,
  "typescript.format.insertSpaceBeforeFunctionParenthesis": true,
  "eslint.validate": [
    "vue",
    "html",
    "javascript",
    "graphql",
    "javascriptreact",
    "json",
    "typescript",
    "typescriptreact",
    "js",
    "ts",
    "md"
  ],
  "prettier.singleQuote": true,
  "prettier.semi": false,
  "[jsonc]": {
    
    
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "workbench.iconTheme": "vscode-icons",
  "editor.fontWeight": "normal",
  "[css]": {
    
    
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "cursorcode.accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkN1VWI0NkVHT3BHaXI4TDh3VkRQRCJ9.eyJpc3MiOiJodHRwczovL2N1cnNvci51cy5hdXRoMC5jb20vIiwic3ViIjoiZ2l0aHVifDEyMjAxMzc0OCIsImF1ZCI6WyJodHRwczovL2N1cnNvci51cy5hdXRoMC5jb20vYXBpL3YyLyIsImh0dHBzOi8vY3Vyc29yLnVzLmF1dGgwLmNvbS91c2VyaW5mbyJdLCJpYXQiOjE2ODI0MTUyNTksImV4cCI6MTY4MjUwMTY1OSwiYXpwIjoiS2JaVVI0MWNZN1c2elJTZHBTVUo3STdtTFlCS09DbUIiLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIG9mZmxpbmVfYWNjZXNzIn0.vfr0VLYkPSI4lXmGPt2I7oDbqsqLmjpMCiBAxpeauiVb6B_7Xb7s3OOoa6l2mSAMl1gnd7uqdNhtqk78pOxoo9Kt3mTfqTO-KlhJ3ppUG5m9NN6LIDcoxsYa8KeplvYPmgRD9vcz1P22xQjHc--g4bgrG85pjBB92irqpzCBKQkW9JRqi514vRF0oVWRoh7aX6NafnMKqjtqeLch8T4tuEPHmH88hAM5TOKLaTGO3fr4Iu7hqTRHOntxiWMJdr38YTIUEIkjIfrFxX5mHIL2mPUur6y9gyIsBpAoB3BJqWK6-xxJyMYtbJmkReinOdsj4Irt31Vu1ddm18P5Vb3FIQ",
  "volar.format.initialIndent": {
    
    
    "html": true
  },
  "prettier.enable": true,
  "editor.codeActionsOnSave": {
    
    
    "source.fixAll": true,
    "source.fixAll.eslint": true,
    "source.fixAll.stylelint": true
  },
  "stylelint.enable": true,
  "scss.lint.unknownAtRules": "ignore",
  "stylelint.validate": ["css", "scss", "vue"],
  "editor.tabSize": 2,
  "prettier.useTabs": true,
  "editor.formatOnSave": true, // 开启保存文件自动格式化代码
  "editor.defaultFormatter": "esbenp.prettier-vscode", // 默认的代码格式化工具
  "prettier.requireConfig": true,
  "settingsSync.ignoredExtensions": [] // 需要Prettier的配置文件
}

修改tsconfig.json
"compilerOptions": {
    
    
	"types": ["vite/client"], // 新增,解决eslint对import.meta.env的报错
}

最后,配置prettier自动修复另外一些代码
npm install -D prettier
npm install  -D eslint-config-prettier // eslint兼容的插件
npm install -D eslint-plugin-prettier // eslint的prettier 

根目录下新建.prettierrc.cjs
module.exports = {
    
    
  // 一行最多一百字符
  printWidth: 100,
  // 使用2个空格缩进
  tabWidth: 2,
  // 不适用缩进符而使用空格
  useTabs: false,
  // 不尾随分号
  semi: false,
  // 使用单引号
  singleQuote: true,
  // 多行逗号分隔的语法中,最后一行不加逗号
  trailingComma: 'none',
  // 单个参数的箭头函数不加括号
  arrowParens: 'avoid',
  // 对象大括号内两边是否加空格
  bracketSpacing: true
}

Create new production and development, configure development and production environment variables

与src同级,创建.env.production与.env.development文件
字段须VITE开头,否则不识别,也可以自行配置后,使用其他字段
想用其他字段 参考  https://blog.csdn.net/weixin_45547638/article/details/127277857

// .env.development
# 开发环境
VITE_BASE_URL = http://192.168.50.207:8080/api
VITE_APP_TITLE = 开发环境

// .env.production
# 生产环境
VITE_BASE_URL = http://192.168.50.161:5173/api
VITE_APP_TITLE = 生产环境

Modify tsconfig.json

// 在compilerOptions中新增
{
    
    
  "compilerOptions": {
    
    
    ...
    "baseUrl": ".",     // 未设置baseUrl,不允许使用非相对路径
    "paths": {
    
    
      "@": ["src"],
      "@/*": ["src/*"]
    }
},

Modify vite.config.ts

// vite.config.ts
import {
    
     defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import {
    
     resolve } from 'path'
export default (({
    
     mode }) => {
    
    

  在启动项目时,控制台打印环境。只是给自己看当前属于什么环境和BASE_URL
  console.log(mode)
  const BASE_URL = loadEnv(mode, process.cwd()).VITE_BASE_URL
  console.log(BASE_URL)
  return defineConfig({
    
    
  	// base: './',
  	plugins: [vue()],
  	// 配置别名
  	resolve: {
    
    
  	  alias: {
    
    
	    "@": resolve(__dirname, './src')
	  }
  	}
  })
})
// 会报错: 找不到名称'__dirname'
path模块是node.js的内置模块,node.js默认不支持ts文件
安装@type/node依赖包    npm install @types/node --save-dev

Modify package.json

// package.json 修改scripts中内容,修改后为
"scripts": {
    
    
  "start": "vite --host --open",  // npm start启动项目,启动后自动打开浏览器,并在控制台输出BASE_URL
  "build": "vue-tsc && vite build",
  "preview": "vite preview"
},

The initial construction of the basic Vite + Vue3 project is completed, and the tools required for the project need to be configured next

Integrate the Element Plus framework and install Less

// 第一步 npm安装
npm install element-plus --save
// 第二步 参考Element Plus官网,可完整引入或按需导入  我选择按需导入
// 按需导入需要安装以下两个插件
npm install -D unplugin-vue-components unplugin-auto-import
// 修改vite.config.ts
import {
    
     defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import {
    
     resolve } from 'path'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {
    
     ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default (({
    
     mode }) => {
    
    
  console.log(mode)
  const BASE_URL = loadEnv(mode, process.cwd()).VITE_BASE_URL
  console.log(BASE_URL)
  return defineConfig({
    
    
  	// base: './',
  	plugins: [
  	  vue(),
  	  AutoImport({
    
    
        resolvers: [ElementPlusResolver()]
      }),
      Components({
    
    
        resolvers: [ElementPlusResolver()]
      }),
  	],
  	// 配置别名
  	resolve: {
    
    
  	  alias: {
    
    
	    "@": resolve(__dirname, './src')
	  }
  	}
  })
})
// 安装Element Plus Icon
npm install @element-plus/icons-vue
// 修改main.ts
import {
    
     createApp } from 'vue'
import './style.css'
import App from './App.vue'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    
    
  app.component(key, component)
}
app.mount('#app')
// 安装Less
npm i less

Integrate Pinia and learn about Pinia's persistence tools

// npm安装
npm install pinia
// 先在main.ts中引入pinia
import {
    
     createApp } from 'vue'
import './style.css'
import App from './App.vue'
import {
    
     createPinia } from 'pinia'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia).mount('#app')
// src下新建store文件夹 根据不同业务需要创建不同的 use_textName_Store.ts 文件
// 安装pinia-plugin-persist 实现数据持久化储存
npm i pinia-plugin-persist --save
// 新建useUserStore.ts
import {
    
     defineStore } from "pinia";
export const useUserStore = defineStore('user', {
    
    
  state: () => {
    
    
    return {
    
    
      userName: sessionStorage.getItem("userName"),
      phone: sessionStorage.getItem("phone"),
      token: sessionStorage.getItem("satoken"),
    }
  },
  getters: {
    
    },
  actions: {
    
    
    setUser (user: any) {
    
    
      this.userName = user.userName
      this.phone = user.phone
      sessionStorage.setItem("userName", user.userName)
      sessionStorage.setItem("phone", user.phone)
    },
    setToken (token: any) {
    
    
      this.token = token
      sessionStorage.setItem(token.tokenName, token.tokenValue)
    }
  },
  // 开启数据缓存 默认sessionStorage state属性全部储存
  persist: {
    
    
    enabled: true,
  }
  // 也可以指定sessionStorage或者localStorage  指定持久化字段
  persist: {
    
    
    enabled: true,
    strategies: [
      {
    
    
        storage: localStorage,
        paths: ['id'],//指定要持久化的字段
      }
    ]
  }
})
// Pinia和VueX相比,在单个文件中最大的差别,就是去掉了饱受诟病的mutations
// 在.vue文件中使用pinia=>useUserStore
import {
    
     useUserStore } from '@/store/userStore';
const userStore = useUserStore()

Integrate Vue-Router and do dynamic routing function

// npm安装Vue-Router
npm install vue-router@4
// 网上有很多关于vue3动态路由的文档博客,配合ts使用,不过我试用了很多,效果都不尽人意,最后,自己总结了可行的动态路由方式。没有配合后端来完成动态路由,后期研究明白,会在文章中补充的。
// src下新建router文件夹 => 新建index.ts 存放不需要权限的路由及beforeEach
import {
    
     createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
const staticRoutes: Array<RouteRecordRaw> = [
  {
    
    
    path: '/login',
    name: 'Login',
    meta: {
    
    
      hidden: true,
      title: '登录页'
    },
    component: () => import('@/views/login/index.vue')
  },
  {
    
    
    path: '/',
    name: 'layout',
    component: () => import('@/layout/index.vue'),
    redirect: '/home',
    meta: {
    
    
      hidden: false
    },
    children: [
      {
    
    
        path: '/home',
        name: 'home',
        component: () => import('@/views/home/index.vue'),
        meta: {
    
    
          title: '首页',
          icon: 'House'
        }
      }
    ]
  },
  {
    
    
    path: '/404',
    meta: {
    
    
      hidden: true,
      title: '404'
    },
    component: () => import('@/views/error_page/404.vue')
  },
]

const router = createRouter({
    
    
  history: createWebHistory(),
  routes: staticRoutes
})
export default router

// router文件夹下 新建trendsRouter.ts  存放动态路由内容
import {
    
     RouteRecordRaw } from "vue-router";

const trendsRouter: Array<RouteRecordRaw> = [
  {
    
    
    path: '/echarts',
    name: 'echarts',
    component: () => import('@/views/echarts/index.vue'),
    meta: {
    
    
      title: '可视化',
      icon: 'House'
    }
  }
]

export default trendsRouter

// 在index.ts中,引入trendsRouter.ts,并补充router.beforeEach
import {
    
     createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import trendsRouter from "./trendsRouter";
const staticRoutes: Array<RouteRecordRaw> = [
  {
    
    
    path: '/login',
    name: 'Login',
    meta: {
    
    
      hidden: true,
      title: '登录页'
    },
    component: () => import('@/views/login/index.vue')
  },
  {
    
    
    path: '/',
    name: 'layout',
    component: () => import('@/layout/index.vue'),
    redirect: '/home',
    meta: {
    
    
      hidden: false
    },
    children: [
      {
    
    
        path: '/home',
        name: 'home',
        component: () => import('@/views/home/index.vue'),
        meta: {
    
    
          title: '首页',
          icon: 'House'
        }
      }
    ]
  },
  {
    
    
    path: '/404',
    meta: {
    
    
      hidden: true,
      title: '404'
    },
    component: () => import('@/views/error_page/404.vue')
  },
]

const router = createRouter({
    
    
  history: createWebHistory(),
  routes: staticRoutes
})
// 创建一个变量,用来判断是否已经动态添加了路由  防止陷入动态路由死循环
let needLoad = true
// 配置白名单
const whiteList = ['/login']
router.beforeEach((to, from, next) => {
    
    
  const token = sessionStorage.getItem("satoken")
  if (to.meta.title) {
    
    
    document.title = to.meta.title as string
  }
  if (!token) {
    
    
    if (whiteList.indexOf(to.path) !== -1) {
    
    
      next()
    } else {
    
    
      next('/login')
    }
  } else {
    
    
    if (to.path === '/login') {
    
    
      next('/home')
    } else {
    
    
      if (needLoad) {
    
    
        // 这里用router.options.routes  是因为在实际应用中,我发现直接router.addRoute动态添加路由后,页面总是为空,后来发现新增的路由并没有添加成功,通过此方法,可以有效保证动态添加路由成功并成功跳转
        const current: any = router.options.routes
        trendsRouter.forEach(v => {
    
    
          current[1].children.push(v)
          router.addRoute('layout',v)
        })
        router.addRoute({
    
    
          path: '/:catchAll(.*)',
          redirect: '/404'
        },)
        needLoad = false
        // 防止空白页面或者404  动态添加完后,路由重新跳转
        next({
    
     ...to, replace: true })
      } else {
    
    
        next()
      }
    }
  }
})
export default router

// 在main.ts中引入router
import {
    
     createApp } from 'vue'
import './style.css'
import App from './App.vue'
import {
    
     createPinia } from 'pinia'
import router from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const pinia = createPinia()
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    
    
  app.component(key, component)
}
app.use(pinia).use(router).mount('#app')


// 在页面中使用router
import {
    
     useRouter, useRoute } from 'vue-router';
const router = useRouter()
const route = useRoute()


当然,目前仅是动态添加没有问题,页面跳转没有问题,如果sidebar选项在动态添加的路由上,我刷新页面的话,浏览器会有警告,但并不影响页面显示及路由跳转
 [Vue Router warn]: No match found for location with path "/echarts"
 
关于路由配置及动态路由,各位有什么好的意见或者方法,可以评论给我,多谢多谢!!!

Integrate Axios , encapsulate request and interface integration

// npm安装axios
npm install axios
// 个人习惯将request及接口管理文件夹都放在一起
// src下新建http文件夹 http => 新建request.ts
import axios from 'axios'
// 页面顶部的进度条,使用起来简单
// npm i nprogress   npm i --save-dev @types/nprogress
import Nprogress from 'nprogress'
import 'nprogress/nprogress.css'
import {
    
     ElMessage } from 'element-plus'

const request = axios.create({
    
    
  baseURL: import.meta.env.VITE_BASE_URL,
  timeout: 5000
})
const NERWORK_ERROR = '服务器错误,请稍后重试!'

request.interceptors.request.use(config => {
    
    
  Nprogress.start()
  const token = sessionStorage.getItem("satoken")
  config.headers.satoken = token
  return config
}, error => {
    
    
  Nprogress.done()
  return Promise.reject(error)
})

request.interceptors.response.use(res => {
    
    
  Nprogress.done()
  if (res.status === 200) {
    
    
    return res.data
  } else {
    
    
    ElMessage.error(NERWORK_ERROR)
    return Promise.reject(NERWORK_ERROR)
  }
})

class Http {
    
    
  get = function (url: string, params: any) {
    
    
    return request({
    
    
      url: url,
      method: 'get',
      headers: {
    
     'Content-Type': 'multipart/form-data' },
      params
    })
  }
  post = function (url: string, data: any) {
    
    
    return request({
    
    
      url: url,
      method: 'post',
      data
    })
  }
}
const http = new Http()
export {
    
     http }

// http文件夹 => 新建interface文件夹  用于管理各个业务场景下的接口
// interface文件夹 => 新建user.ts  管理用户的注册登录登出,用户信息等接口
class Apis {
    
    
  register = `user/register`
  login = `user/login`
  refresh = `user/hello`
}
const user = new Apis()
export {
    
     user }

// http文件夹 => 新建http.ts 整合所有接口文件与request
import {
    
     http } from "./request";
import {
    
     user } from './interface/user'
const register = (data: any) => {
    
    
  return http.post(user.register, data)
}
const login = (data: any) => {
    
    
  return http.post(user.login, data)
}
const refresh = (data: any) => {
    
    
  return http.post(user.refresh, data)
}
export default {
    
    
  register,
  login,
  refresh
}

// 在main.ts中引入http 全局挂载  个人喜欢用$http
import {
    
     createApp } from 'vue'
import './style.css'
import App from './App.vue'
import {
    
     createPinia } from 'pinia'
import router from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import http from './http/http'

const pinia = createPinia()
const app = createApp(App)
// vue3中 不能再使用vue.prototype.$http
app.config.globalProperties.$http = http;
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    
    
  app.component(key, component)
}
app.use(pinia).use(router).mount('#app')

// 在页面中使用$http,也不能再是简单的通过this.$http来使用,为了避免每个页面都返货的获取上下文,我简单封了一个获取全局的方法
// src => 新建utils文件夹 => 新建getCurrentInstance.ts
import {
    
     getCurrentInstance, ComponentInternalInstance } from 'vue';
export default function useCurrentInstance() {
    
    
  const currentInstance = getCurrentInstance() as ComponentInternalInstance;
  const proxy = currentInstance.appContext.config.globalProperties;
  // 所有通过app.config.globalProperties挂载的,都在proxy中
  return proxy
}
// 页面中
import useCurrentInstance  from '@/utils/getCurrentInstance';
const {
    
     $http } = useCurrentInstance();
// 通过$http.login(data) 来登录。。。

// 配置axios的过程中,可能会有Property does not exist on type 'AxiosResponse'报错
// 如果有报错, src => 新建shims-vue.d.ts
import {
    
     AxiosRequestConfig } from "axios";
declare module 'axios' {
    
    
  interface AxiosInstance {
    
    
    (config: AxiosRequestConfig): Promise<any>
  }
}

Integrate Mock.js , separate front-end and back-end to simulate data by yourself

// npm安装mock.js
npm install mockjs
// src => 新建mock文件夹 => 新建type.ts
// typeScript写参数接口,这个东西看着J里J气的,哈哈,越来越像Java了
export interface MockParams {
    
    
  url: string,
  type: string,
  data?: any,
  params?: any,
  response(option?: any): Record<string, unknown>
}

// mock文件夹 => 新建api文件夹 => 新建user.ts  模拟用户注册登录等
function paramObj (url: any) {
    
    
  const search = url.split('?')[1]
  if (!search) {
    
    
    return {
    
    }
  }
  return JSON.parse(
    '{"' +
    decodeURIComponent(search)
      .replace(/"/g, '\\"')
      .replace(/&/g, '","')
      .replace(/=/g, '":"') +
    '"}')
}

const userLogin = {
    
    
  url: `user/login`,
  type: "post",
  response: (config: any) => {
    
    
    const {
    
     userName, userPassword } = JSON.parse(config.body)
    if (userName === 'wq19970106' && userPassword === 'Wq19970106..') {
    
    
      return {
    
    
        code: 20000,
        message: '登录成功',
        data: {
    
    
          tokenName: 'satoken',
          tokenValue: 'dsadfhasoidqwehdasd',
          userName: '王琪',
          phone: '15582868787'
        }
      }
    } else {
    
    
      return {
    
    
        code: 40010,
        message: '账号或密码错误'
      }
    }
  }
}

const userRegister = {
    
    
  url: `/user/register`,
  type: 'post',
  response: (config: any) => {
    
    
    const {
    
     userName, userPassword } = JSON.parse(config.body)
    if (userName !== 'wq19970106' && userPassword) {
    
    
      return {
    
    
        code: 20000,
        message: '注册成功'
      }
    } else {
    
    
      return {
    
    
        code: 40010,
        message: '账号已存在'
      }
    }
  }
}

const user = [userLogin, userRegister]
export default user

// mock文件夹 => 新建index.ts
import Mock from 'mockjs'
import {
    
     MockParams } from './type'
import user from './api/user'

const mocks = [...user]
export function mockRequest() {
    
    
  let i: MockParams
  for(i of mocks){
    
    
    Mock.mock(new RegExp(i.url), i.type || 'get', i.response)
  }
}

// 在main.ts中引入使用
import {
    
     createApp } from 'vue'
import './style.css'
import App from './App.vue'
import {
    
     createPinia } from 'pinia'
import {
    
     mockRequest } from './mock'
import router from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import http from './http/http'
const pinia = createPinia()
const app = createApp(App)
app.config.globalProperties.$http = http;
if(process.env.NODE_ENV === 'development'){
    
    
  mockRequest()
}
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    
    
  app.component(key, component)
}
app.use(pinia).use(router).mount('#app')


// 在配置mock.js过程中  控制台会有typeScript关于mock的报错
npm install @types/mockjs
// shims-vue.d.ts 添加
declare module 'mockjs'

Introducing Moment.js and Lodash

// npm安装
npm install moment
npm i --save lodash
// moment是关于时间的工具类,内容全面,功能强大,同样的还有day.js 本人更习惯moment
// main.ts中引入moment,全局挂载
import moment from 'moment'
app.config.globalProperties.$moment = moment;
// 在页面中同样使用封好的getCurrentInstance
import useCurrentInstance  from '@/utils/getCurrentInstance';
const {
    
     $http, $moment } = useCurrentInstance();
const now = ref($moment().format("LTS"));
const timer = setInterval(function () {
    
    
  now.value = $moment().format("LTS")
}, 1000)
// lodash是一款很实用的js工具类,包含数组、集合、字符串等操作函数,我用的更多的是防抖之类的方法

Integrate Echarts

// npm安装
npm install echarts
// utils文件夹 => 新建echarts.ts 用于按需导入echarts
import * as echarts from 'echarts/core';
import {
    
    
  BarChart,
  // 系列类型的定义后缀都为 SeriesOption
  BarSeriesOption,
  LineChart,
  LineSeriesOption
} from 'echarts/charts';
import {
    
    
  TitleComponent,
  // 组件类型的定义后缀都为 ComponentOption
  TitleComponentOption,
  TooltipComponent,
  TooltipComponentOption,
  ToolboxComponent,
  GridComponent,
  GridComponentOption,
  // 数据集组件
  DatasetComponent,
  DatasetComponentOption,
  // 内置数据转换器组件 (filter, sort)
  TransformComponent,
  LegendComponent
} from 'echarts/components';
import {
    
     LabelLayout, UniversalTransition } from 'echarts/features';
import {
    
     CanvasRenderer } from 'echarts/renderers';

// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
type ECOption = echarts.ComposeOption<
  | BarSeriesOption
  | LineSeriesOption
  | TitleComponentOption
  | TooltipComponentOption
  | GridComponentOption
  | DatasetComponentOption
>;

// 注册必须的组件
echarts.use([
  TitleComponent,
  TooltipComponent,
  ToolboxComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  BarChart,
  LineChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer,
  LegendComponent
]);

export default echarts

// 修改main.ts
import {
    
     createApp } from 'vue'
import './style.css'
import App from './App.vue'
import {
    
     createPinia } from 'pinia'
import {
    
     mockRequest } from './mock'
import router from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import moment from 'moment'
import http from './http/http'
import echarts from './utils/echarts'
// echarts.resize()会有浏览器警告,npm i default-passive-events -S
import 'default-passive-events'
const pinia = createPinia()
const app = createApp(App)
app.config.globalProperties.$moment = moment;
app.config.globalProperties.$http = http;
app.config.globalProperties.$echarts = echarts
if(process.env.NODE_ENV === 'development'){
    
    
  mockRequest()
}
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    
    
  app.component(key, component)
}
app.use(pinia).use(router).mount('#app')
// 在需要的页面中使用echarts
<template>
  <div class="echarts">
    <div id="main" ref="mainDom"></div>
  </div>
</template>
<script lang='ts' setup>
import {
    
     ref, nextTick, watch } from 'vue';
import useCurrentInstance from '@/utils/getCurrentInstance'
import {
    
     useUserStore } from '@/store/userStore';
const userStore = useUserStore()
const {
    
     $echarts } = useCurrentInstance()
const mainDom = ref(null)
let myEcharts: any
const initEchart = () => {
    
    
  myEcharts = $echarts.init(mainDom.value)
  let options = {
    
    
    title: {
    
    
      text: '数据报表',
      textStyle: {
    
    
        color: '#567788',
        fontStyle: 'oblique',
        fontWeight: 'lighter'
      }
    },
    tooltip: {
    
    
      trigger: 'axis',
      axisPointer: {
    
    
        type: 'cross',
        label: {
    
    
          backgroundColor: '#6a7985'
        }
      }
    },
    //类型分类
    legend: {
    
    
      data: ['用户1', '用户2', '用户3', '用户4', '用户5']
    },
    toolbox: {
    
    
      feature: {
    
    
        saveAsImage: {
    
    }
      }
    },
    //坐标
    grid: {
    
    
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true//如果坐标图上xy轴显示的有标示,一定要为true,否则展示不全,内容会被隔挡
    },
    //X轴的命名
    xAxis: [
      {
    
    
        type: 'category',
        boundaryGap: false,
        data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
      }
    ],
    yAxis: [
      {
    
    
        type: 'value'
      }
    ],
    series: [
      //bar--柱状图
      //line -- 线图
      {
    
    
        name: '用户1',
        type: 'line',
        stack: 'Total',
        areaStyle: {
    
    },
        emphasis: {
    
    
          focus: 'series'
        },
        data: [120, 132, 101, 134, 90, 230, 210]
      },
      {
    
    
        name: '用户2',
        type: 'line',
        stack: 'Total',
        areaStyle: {
    
    },
        emphasis: {
    
    
          focus: 'series'
        },
        data: [240, 182, 291, 234, 290, 330, 310]
      },
      {
    
    
        name: '用户3',
        type: 'line',
        stack: 'Total',
        areaStyle: {
    
    },
        emphasis: {
    
    
          focus: 'series'
        },
        data: [120, 232, 2101, 154, 190, 330, 410]
      },
      {
    
    
        name: '用户4',
        type: 'line',
        stack: 'Total',
        areaStyle: {
    
    },
        emphasis: {
    
    
          focus: 'series'
        },
        data: [360, 332, 301, 334, 390, 330, 320]
      },
      {
    
    
        name: '用户5',
        type: 'line',
        stack: 'Total',
        label: {
    
    
          show: true,
          position: 'top'
        },
        areaStyle: {
    
    },
        emphasis: {
    
    
          focus: 'series'
        },
        data: [820, 932, 901, 934, 1290, 1330, 1320]
      }
    ]
  }
  myEcharts.setOption(options)
  window.addEventListener('resize', () => {
    
    
    myEcharts.resize()
  })
}
nextTick(()=> {
    
    
  initEchart()
})
watch(userStore, () => {
    
    
  myEcharts.dispose()
  setTimeout(() => {
    
    
    initEchart()
  },300);
})


</script>
<style lang='less' scoped>
.echarts {
    
    
  width: 100%;
  height: 100%;
  position: relative;

  #main {
    
    
    width: 45%;
    height: 45%;
    position: absolute;
    top: 10px;
    left: 10px;
    border: 1px solid black;
  }
}
</style>

It's over, ask the boss for advice!

So far, the most basic vue3 project built by vite has been built. If there is anything wrong, please feel free to point it out. Thank you *500!

Guess you like

Origin blog.csdn.net/weixin_58421147/article/details/130379267