Ver código fonte

提交客户管家前端初始代码

Lijy 1 ano atrás
pai
commit
f3550d9b47
23 arquivos alterados com 2368 adições e 0 exclusões
  1. 24 0
      fhKeeper/formulahousekeeper/customerBuler-crm/.gitignore
  2. 3 0
      fhKeeper/formulahousekeeper/customerBuler-crm/.vscode/extensions.json
  3. 18 0
      fhKeeper/formulahousekeeper/customerBuler-crm/README.md
  4. 13 0
      fhKeeper/formulahousekeeper/customerBuler-crm/index.html
  5. 1711 0
      fhKeeper/formulahousekeeper/customerBuler-crm/package-lock.json
  6. 26 0
      fhKeeper/formulahousekeeper/customerBuler-crm/package.json
  7. 1 0
      fhKeeper/formulahousekeeper/customerBuler-crm/public/vite.svg
  8. 7 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/App.vue
  9. 1 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/vue.svg
  10. 38 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/components/HelloWorld.vue
  11. 15 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/main.ts
  12. 8 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/index.vue
  13. 112 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/login.vue
  14. 28 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/router/index.ts
  15. 18 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/store/index.ts
  16. 79 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/style.css
  17. 41 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/errorStatusCode.ts
  18. 84 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/request copy.ts
  19. 66 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/request.ts
  20. 7 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/vite-env.d.ts
  21. 30 0
      fhKeeper/formulahousekeeper/customerBuler-crm/tsconfig.json
  22. 11 0
      fhKeeper/formulahousekeeper/customerBuler-crm/tsconfig.node.json
  23. 27 0
      fhKeeper/formulahousekeeper/customerBuler-crm/vite.config.ts

+ 24 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/.gitignore

@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 3 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/.vscode/extensions.json

@@ -0,0 +1,3 @@
+{
+  "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
+}

+ 18 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/README.md

@@ -0,0 +1,18 @@
+# Vue 3 + TypeScript + Vite
+
+This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
+
+## Recommended IDE Setup
+
+- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
+
+## Type Support For `.vue` Imports in TS
+
+TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
+
+If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
+
+1. Disable the built-in TypeScript Extension
+   1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
+   2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
+2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.

+ 13 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/index.html

@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Vite + Vue + TS</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>

Diferenças do arquivo suprimidas por serem muito extensas
+ 1711 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/package-lock.json


+ 26 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/package.json

@@ -0,0 +1,26 @@
+{
+  "name": "customerbuler-crm",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vue-tsc && vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "axios": "^1.6.7",
+    "element-plus": "^2.5.6",
+    "pinia": "^2.1.7",
+    "vue": "^3.4.19",
+    "vue-router": "^4.3.0"
+  },
+  "devDependencies": {
+    "@types/node": "^20.11.24",
+    "@vitejs/plugin-vue": "^5.0.4",
+    "sass": "^1.71.1",
+    "typescript": "^5.2.2",
+    "vite": "^5.1.4",
+    "vue-tsc": "^1.8.27"
+  }
+}

Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/public/vite.svg


+ 7 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/App.vue

@@ -0,0 +1,7 @@
+<template>
+  <router-view></router-view>
+</template>
+
+<script setup lang="ts"></script>
+
+<style scoped></style>

+ 1 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/vue.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

+ 38 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/components/HelloWorld.vue

@@ -0,0 +1,38 @@
+<script setup lang="ts">
+import { ref } from 'vue'
+
+defineProps<{ msg: string }>()
+
+const count = ref(0)
+</script>
+
+<template>
+  <h1>{{ msg }}</h1>
+
+  <div class="card">
+    <button type="button" @click="count++">count is {{ count }}</button>
+    <p>
+      Edit
+      <code>components/HelloWorld.vue</code> to test HMR
+    </p>
+  </div>
+
+  <p>
+    Check out
+    <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
+      >create-vue</a
+    >, the official Vue + Vite starter
+  </p>
+  <p>
+    Install
+    <a href="https://github.com/vuejs/language-tools" target="_blank">Volar</a>
+    in your IDE for a better DX
+  </p>
+  <p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
+</template>
+
+<style scoped>
+.read-the-docs {
+  color: #888;
+}
+</style>

+ 15 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/main.ts

@@ -0,0 +1,15 @@
+import { createApp } from 'vue'
+import { createPinia } from 'pinia'
+import ElementPlus from 'element-plus'
+import 'element-plus/dist/index.css'
+import App from './App.vue'
+import router from './router/index'
+import './style.css'
+
+const app = createApp(App)
+
+app
+  .use(ElementPlus)
+  .use(createPinia())
+  .use(router)
+  .mount('#app')

+ 8 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/index.vue

@@ -0,0 +1,8 @@
+<template>
+    <div>
+        indexyemian
+    </div>
+</template>
+<script setup lang="ts">
+</script>
+<style scoped lang="scss"></style>

+ 112 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/login.vue

@@ -0,0 +1,112 @@
+<template>
+    <div @click="tologin">登录</div>
+    <h2> {{ store.name }}</h2>
+    <h1 @click="changeName">修改名称</h1>
+    <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick" />
+    <div @click="networkRequest()">点击发起网络请求</div>
+</template>
+<script lang="ts" setup>
+import { useRoute, useRouter } from 'vue-router'
+import { useStore } from "../store/index";
+import { post } from '@/utils/request'
+
+const router = useRouter()
+const store = useStore()
+function tologin() {
+    router.push({
+        name: 'index'
+    })
+}
+function changeName(): void {
+    store.name = '索索'
+}
+
+async function networkRequest(): Promise<void> {
+    console.log('发起请求')
+    const requestUrl = '/user/loginAdmin'
+    const data = {
+        username: '18130408100',
+        password: '220926'
+    }
+    // proxy.$post(requestUrl, data)
+    // .then((response: any) => {
+    //     console.log(response, '<=== 请求返回的数据')
+    // })
+    const res = await post(requestUrl, data)
+    console.log(res, '<=== 请求返回的数据')
+}
+
+
+interface Tree {
+    label: string
+    children?: Tree[]
+}
+
+const handleNodeClick = (data: Tree) => {
+    console.log(data)
+}
+
+const data: Tree[] = [
+    {
+        label: 'Level one 1',
+        children: [
+            {
+                label: 'Level two 1-1',
+                children: [
+                    {
+                        label: 'Level three 1-1-1',
+                    },
+                ],
+            },
+        ],
+    },
+    {
+        label: 'Level one 2',
+        children: [
+            {
+                label: 'Level two 2-1',
+                children: [
+                    {
+                        label: 'Level three 2-1-1',
+                    },
+                ],
+            },
+            {
+                label: 'Level two 2-2',
+                children: [
+                    {
+                        label: 'Level three 2-2-1',
+                    },
+                ],
+            },
+        ],
+    },
+    {
+        label: 'Level one 3',
+        children: [
+            {
+                label: 'Level two 3-1',
+                children: [
+                    {
+                        label: 'Level three 3-1-1',
+                    },
+                ],
+            },
+            {
+                label: 'Level two 3-2',
+                children: [
+                    {
+                        label: 'Level three 3-2-1',
+                    },
+                ],
+            },
+        ],
+    },
+]
+
+const defaultProps = {
+    children: 'children',
+    label: 'label',
+}
+</script>
+

+ 28 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/router/index.ts

@@ -0,0 +1,28 @@
+import { createRouter, createWebHistory, } from 'vue-router'
+import Login from '../pages/login.vue';
+
+export const routes = [
+    {
+        path: '/',
+        redirect: '/login'
+    },
+    {
+        name: 'login',
+        path: '/login',
+        component: Login
+    },
+    {
+        name: 'index',
+        path: '/index',
+        component: () => import("../pages/index.vue")
+    }
+]
+const router = createRouter({
+    scrollBehavior: () => ({ left: 0, top: 0 }),
+    history: createWebHistory(),
+    routes,
+})
+router.beforeEach((_to, _from, next) => {
+    next()
+})
+export default router

+ 18 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/store/index.ts

@@ -0,0 +1,18 @@
+import { defineStore, acceptHMRUpdate } from "pinia";
+export const useStore = defineStore({
+    id: "index",
+    state: () => ({name: "old name",}),
+    getters: {
+        myName: (state) => {
+            return `getters ${state.name}`
+        }
+    },
+    actions: {
+        changeName(name: string) {
+            this.name = name
+        }
+    },
+});
+if (import.meta.hot) {
+    import.meta.hot.accept(acceptHMRUpdate(useStore, import.meta.hot))
+}

+ 79 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/style.css

@@ -0,0 +1,79 @@
+:root {
+  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+  line-height: 1.5;
+  font-weight: 400;
+
+  color-scheme: light dark;
+  color: rgba(255, 255, 255, 0.87);
+  background-color: #242424;
+
+  font-synthesis: none;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+a {
+  font-weight: 500;
+  color: #646cff;
+  text-decoration: inherit;
+}
+a:hover {
+  color: #535bf2;
+}
+
+body {
+  margin: 0;
+  display: flex;
+  place-items: center;
+  min-width: 320px;
+  min-height: 100vh;
+}
+
+h1 {
+  font-size: 3.2em;
+  line-height: 1.1;
+}
+
+button {
+  border-radius: 8px;
+  border: 1px solid transparent;
+  padding: 0.6em 1.2em;
+  font-size: 1em;
+  font-weight: 500;
+  font-family: inherit;
+  background-color: #1a1a1a;
+  cursor: pointer;
+  transition: border-color 0.25s;
+}
+button:hover {
+  border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+  outline: 4px auto -webkit-focus-ring-color;
+}
+
+.card {
+  padding: 2em;
+}
+
+#app {
+  max-width: 1280px;
+  margin: 0 auto;
+  padding: 2rem;
+  text-align: center;
+}
+
+@media (prefers-color-scheme: light) {
+  :root {
+    color: #213547;
+    background-color: #ffffff;
+  }
+  a:hover {
+    color: #747bff;
+  }
+  button {
+    background-color: #f9f9f9;
+  }
+}

+ 41 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/errorStatusCode.ts

@@ -0,0 +1,41 @@
+export const showMessage = (status: number | string): string => {
+    let message: string = "";
+    switch (status) {
+      case 400:
+        message = "请求错误(400)";
+        break;
+      case 401:
+        message = "未授权,请重新登录(401)";
+        break;
+      case 403:
+        message = "拒绝访问(403)";
+        break;
+      case 404:
+        message = "请求出错(404)";
+        break;
+      case 408:
+        message = "请求超时(408)";
+        break;
+      case 500:
+        message = "服务器错误(500)";
+        break;
+      case 501:
+        message = "服务未实现(501)";
+        break;
+      case 502:
+        message = "网络错误(502)";
+        break;
+      case 503:
+        message = "服务不可用(503)";
+        break;
+      case 504:
+        message = "网络超时(504)";
+        break;
+      case 505:
+        message = "HTTP版本不受支持(505)";
+        break;
+      default:
+        message = `连接出错(${status})!`;
+    }
+    return `${message},请检查网络或联系管理员!`;
+  };

+ 84 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/request copy.ts

@@ -0,0 +1,84 @@
+/**axios封装* 请求拦截、相应拦截、错误统一处理*/
+import axios from 'axios';
+import router from '../router/index'
+//  let protocol = window.location.protocol; //协议
+//  let host = window.location.host; //主机
+//  axios.defaults.baseURL = protocol + "//" + host;
+axios.defaults.baseURL = '/api'
+axios.interceptors.request.use( 
+    //响应拦截
+    async config => {
+        // 每次发送请求之前判断vuex中是否存在token        
+        // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
+        // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断 
+        config.headers.token = sessionStorage.getItem('token')
+        return config;
+    },
+    error => {
+        return Promise.reject(error);
+    }
+)
+// 响应拦截器
+axios.interceptors.response.use(
+    response => {
+        if (response.status === 200) {return Promise.resolve(response); 
+            //进行中        
+        } else {
+            return Promise.reject(response); //失败       
+        }},// 服务器状态码不是200的情况    
+        error => {
+            if (error.response.status) {
+                switch (error.response.status) {
+                    // 401: 未登录                
+                    // 未登录则跳转登录页面,并携带当前页面的路径                
+                    // 在登录成功后返回当前页面,这一步需要在登录页操作。                
+                    case 401:// 自定义过期之后的操作break// 403 token过期                
+                    // 登录过期对用户进行提示                
+                    // 清除本地token和清空vuex中token对象                
+                    // 跳转登录页面                
+                    case 403:sessionStorage.clear()
+                    break// 404请求不存在                
+                    case 404:break;// 其他错误,直接抛出错误提示                
+                    default:
+                }
+                return Promise.reject(error.response);
+            }
+        }
+);
+/** * get方法,对应get请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */
+const $get = (url: string, params: object) => {
+    return new Promise(
+        (resolve, reject) => {
+            axios.get(url, {params: params,
+        }).then(res => {
+            resolve(res.data);
+        }).catch(err => {
+            reject(err.data)
+        })});
+}
+/** * post方法,对应post请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */
+const $post = (url: string, params: object) => {
+    return new Promise((resolve, reject) => {
+        axios.post(url, params, {
+            headers: {
+                'Content-type': ' application/x-www-form-urlencoded; charset=UTF-8',
+            }
+        })
+        //是将对象 序列化成URL的形式,以&进行拼接   
+        .then(res => {
+            resolve(res.data);
+        })
+        .catch(err => {
+            reject(err.data)
+        })
+    });
+}
+// 下面是将get和post方法挂载到vue原型上供全局使用、
+// vue2.x中是通 Vue.prototype 来绑定的,像这样Vue.prototype.$toast = Toast。在vue3中取消了Vue.prototype,推荐使用globalProperties来绑定,
+export default {
+    install: (app: any) => {
+        app.config.globalProperties['$get'] = $get;
+        app.config.globalProperties['$post'] = $post;
+        app.config.globalProperties['$axios'] = axios;
+    }
+}

+ 66 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/request.ts

@@ -0,0 +1,66 @@
+import axios from "axios";
+import { showMessage } from "./errorStatusCode"; // 引入状态码文件
+import type { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios";
+import { ElNotification } from "element-plus";
+
+// 创建axios实例
+const instance = axios.create({
+  baseURL: "/api", // 设置API的基础URL
+});
+
+// 请求拦截器
+instance.interceptors.request.use(
+  (config: AxiosRequestConfig): any => {
+    // 可在请求发送前对config进行修改,如添加请求头等
+    const headers = config.headers || {};
+    headers["Authorization"] = "Bxxx";
+    config.headers = headers;
+    return config;
+  },
+  (error: AxiosError) => {
+    // 处理请求错误
+    return Promise.reject(error);
+  }
+);
+
+// 响应拦截器
+instance.interceptors.response.use(
+  (response: AxiosResponse) => {
+    // 对响应数据进行处理
+    if (response.status != 200) {
+      ElNotification({
+        message: showMessage(response.status), // 传入响应码,匹配响应码对应信息,
+        type: "error",
+      });
+    }
+    return response;
+  },
+  (error: AxiosError) => {
+    // 处理响应错误
+    return Promise.reject(error);
+  }
+);
+
+// 封装GET请求
+export async function get<T>(url: string, params?: any): Promise<T> {
+  return instance
+    .get<T>(url, { params })
+    .then((response) => response.data)
+    .catch((error) => {
+      throw error;
+    });
+}
+
+// 封装POST请求
+export async function post<T>(url: string, data?: any): Promise<T> {
+  return instance
+    .post<T>(url, data, {
+      headers: {
+        "Content-type": " application/x-www-form-urlencoded; charset=UTF-8",
+      },
+    })
+    .then((response) => response.data)
+    .catch((error) => {
+      throw error;
+    });
+}

+ 7 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/vite-env.d.ts

@@ -0,0 +1,7 @@
+/// <reference types="vite/client" />
+declare module "*.vue" {
+    import type { DefineComponent } from "vue";
+    const vueComponent: DefineComponent<{}, {}, any>;
+    export default vueComponent;
+}
+

+ 30 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/tsconfig.json

@@ -0,0 +1,30 @@
+{
+  "compilerOptions": {
+    "target": "ES2020",
+    "useDefineForClassFields": true,
+    "module": "ESNext",
+    "lib": ["ES2020", "DOM", "DOM.Iterable"],
+    "skipLibCheck": true,
+
+    /* Bundler mode */
+    /* "moduleResolution": "bundler" */
+    "moduleResolution": "node",
+    "allowImportingTsExtensions": true,
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "jsx": "preserve",
+
+    /* Linting */
+    "strict": true,
+    /* "noUnusedLocals": true 是否开始不使用变量报错 */
+    "noUnusedLocals": false,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true,
+    "paths": {
+      "@/*": ["./src/*"]
+    }
+  },
+  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
+  "references": [{ "path": "./tsconfig.node.json" }]
+}

+ 11 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/tsconfig.node.json

@@ -0,0 +1,11 @@
+{
+  "compilerOptions": {
+    "composite": true,
+    "skipLibCheck": true,
+    "module": "ESNext",
+    "moduleResolution": "bundler",
+    "allowSyntheticDefaultImports": true,
+    "strict": true
+  },
+  "include": ["vite.config.ts"]
+}

+ 27 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/vite.config.ts

@@ -0,0 +1,27 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+
+import { resolve } from 'path'
+
+const target = 'http://47.101.180.183:10010'
+
+export default defineConfig({
+  plugins: [vue()],
+  server: {
+    port: 19123,
+    proxy: {
+      '/api': { // 这里的'/api'表示需要转发到的接口路径前缀
+        target, // 将请求转发到的目标地址
+        changeOrigin: true, // 支持跨域
+        rewrite: (path) => path.replace(/^\/api/, '') // 去除请求路径中的'/api'前缀
+      },
+    },
+  },
+  resolve: {
+    alias: {
+      '@': resolve(__dirname, './src')
+    },
+    //extensions: [".ts", ".js", ".vue", ".json", ".mjs"],
+    extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"]
+  }
+})