Kaynağa Gözat

feat: 初始化项目

lxf 1 hafta önce
ebeveyn
işleme
35843041ae

+ 3 - 0
.env.development

@@ -0,0 +1,3 @@
+VITE_SERVER=https://feiniaotech-dev.sysuimars.cn/
+VITE_PYSERVER=https://birds-api-1-2.sysuimars.cn/
+VITE_MOCK=True

+ 3 - 0
.env.production

@@ -0,0 +1,3 @@
+VITE_SERVER=https://birdseye-api.feiniaotech.sysuimars.cn/
+VITE_PYSERVER=https://birds-api-1-2.sysuimars.cn/
+VITE_MOCK=True

+ 3 - 0
.gitignore

@@ -26,9 +26,12 @@ build/
 bld/
 bld/
 [Bb]in/
 [Bb]in/
 [Oo]bj/
 [Oo]bj/
+dist/
+package-lock.json
 
 
 # Visual Studio 2015 cache/options directory
 # Visual Studio 2015 cache/options directory
 .vs/
 .vs/
+.vscode/
 # Uncomment if you have tasks that create the project's static files in wwwroot
 # Uncomment if you have tasks that create the project's static files in wwwroot
 #wwwroot/
 #wwwroot/
 
 

+ 13 - 0
index.html

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

+ 28 - 0
package.json

@@ -0,0 +1,28 @@
+{
+  "name": "my-project",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vue-tsc -b && vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "axios": "^1.16.1",
+    "normalize.css": "^8.0.1",
+    "nprogress": "^0.2.0",
+    "qs": "^6.15.2",
+    "vue": "^3.5.34",
+    "vue-router": "^5.0.7",
+    "vuex": "^4.1.0"
+  },
+  "devDependencies": {
+    "@types/node": "^24.12.3",
+    "@vitejs/plugin-vue": "^6.0.6",
+    "@vue/tsconfig": "^0.9.1",
+    "typescript": "~6.0.2",
+    "vite": "^8.0.12",
+    "vue-tsc": "^3.2.8"
+  }
+}

+ 1 - 0
public/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>

+ 3 - 0
src/App.vue

@@ -0,0 +1,3 @@
+<template>
+  <router-view />
+</template>

+ 45 - 0
src/api/config.js

@@ -0,0 +1,45 @@
+const newServer = VE_ENV.SERVER
+const pyServer = VE_ENV.PYSERVER
+const oldServer = 'https://birdseye-api.sysuimars.com/'
+
+export default {
+  base_url: oldServer + 'site/',
+  base_dev_site_url: newServer + 'site/',
+  base_mini_url: oldServer + 'mini/',
+  base_dev_url: newServer + 'mini/',
+  base_py_url: pyServer,
+  image_url: oldServer + 'images/',
+  mini_key: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9',
+  base_img_url: 'https://birdseye-img.sysuimars.com/',
+  base_img_url2: 'https://birdseye-img-ali-cdn.sysuimars.com/',
+  base_img_url3: 'https://birdseye-img.sysuimars.com/',
+  base_img_url4: 'https://xyz2.sysuimars.cn/',
+  base_video_url: 'https://minio.sysuimars.com/sysuimars/',
+  resize: '?x-oss-process=image/resize,w_1000',
+  getOptBody: (opt) => JSON.parse(opt.body),
+  igSuccessUrl: [
+    'get',
+    'page',
+    'index',
+    'sign',
+    'List',
+    'info',
+    'Info',
+    'Data',
+    'list',
+    'find',
+    'insert',
+    'Insert',
+    'image',
+    'Image',
+    'plugin_weather_report',
+    'plugin_weather_warning_record',
+    'poi',
+    'mini_credential',
+    'garden',
+    'updateStatus',
+    'mini_pop_control',
+    'mini_session_store',
+    'check',
+  ],
+}

+ 8 - 0
src/api/modules/app.js

@@ -0,0 +1,8 @@
+import config from '../config.js'
+
+export default {
+  sendValidate: {
+    url: config.base_dev_url + 'sms/validate',
+    type: 'get',
+  },
+}

+ 4 - 0
src/config.js

@@ -0,0 +1,4 @@
+export default {
+  dev_mock: false,
+  pro_mock: true,
+}

+ 63 - 0
src/env.d.ts

@@ -0,0 +1,63 @@
+/// <reference types="vite/client" />
+
+declare const VE_ENV: {
+  MODE: string
+  SERVER: string
+  PYSERVER: string
+  MOCK: string
+}
+
+interface ImportMetaEnv {
+  readonly VITE_SERVER: string
+  readonly VITE_PYSERVER: string
+  readonly VITE_MOCK: string
+}
+
+interface ImportMeta {
+  readonly env: ImportMetaEnv
+}
+
+interface Window {
+  VE_API?: Record<string, Record<string, unknown>>
+}
+
+declare module '*.vue' {
+  import type { DefineComponent } from 'vue'
+  const component: DefineComponent<object, object, unknown>
+  export default component
+}
+
+declare module '@/router/index.js' {
+  import type { Router } from 'vue-router'
+  const router: Router
+  export default router
+}
+
+declare module '@/store/index.js' {
+  import type { Store } from 'vuex'
+  const store: Store<unknown>
+  export default store
+}
+
+declare module '@/plugins/axios.js' {
+  import type { Plugin } from 'vue'
+  import type { Router } from 'vue-router'
+  import type { Store } from 'vuex'
+
+  type AxiosPluginOptions = {
+    router: Router
+    store: Store<unknown>
+    opt: string
+  }
+
+  const axiosPlugin: Plugin<[AxiosPluginOptions]>
+  export default axiosPlugin
+}
+
+export {}
+
+declare module 'vue' {
+  interface ComponentCustomProperties {
+    VE_API: Record<string, Record<string, (...args: unknown[]) => Promise<unknown>>>
+  }
+}

+ 16 - 0
src/main.ts

@@ -0,0 +1,16 @@
+import { createApp } from 'vue'
+import 'normalize.css/normalize.css'
+import 'nprogress/nprogress.css'
+import './style.css'
+import App from './App.vue'
+import router from './router/index.js'
+import store from './store/index.js'
+import axios from './plugins/axios.js'
+
+const app = createApp(App)
+
+app.use(store)
+app.use(router)
+app.use(axios as import('vue').Plugin, { router, store, opt: 'VE_API' })
+
+app.mount('#app')

+ 179 - 0
src/plugins/axios.js

@@ -0,0 +1,179 @@
+import axios from 'axios'
+import NProgress from 'nprogress'
+import apiConfig from '../api/config.js'
+
+const { igSuccessUrl } = apiConfig
+
+function pathVariable(url, obj) {
+  const prefix = url.indexOf('{')
+  if (prefix > -1) {
+    const suffix = url.indexOf('}')
+    const key = url.substr(prefix + 1, suffix - prefix - 1)
+    url = url.replace('{' + key + '}', obj[key])
+    return pathVariable(url, obj)
+  }
+  return url
+}
+
+const install = (app, { router, store, opt }) => {
+  const config = {
+    Global: true,
+  }
+
+  const _axios = axios.create(config)
+  let ve_loading
+  let ve_message = null
+  let loadingCount = 0
+
+  function getIsShow(url) {
+    for (const s of igSuccessUrl) {
+      if (url.indexOf(s) > -1) {
+        return true
+      }
+    }
+    return false
+  }
+
+  _axios.interceptors.request.use(
+    (reqConfig) => {
+      NProgress.done()
+      if (reqConfig.Global && getIsShow(reqConfig.url) === false) {
+        // 可按需开启 loading
+      }
+      loadingCount++
+      const token = store.getters.token
+      token && (reqConfig.headers.token = token)
+      reqConfig.headers.appId = 'wx628f62793fdf5251'
+      return reqConfig
+    },
+    (error) => Promise.reject(error),
+  )
+
+  _axios.interceptors.response.use(
+    (response) => {
+      loadingCount--
+      if (loadingCount <= 0) {
+        NProgress.done()
+        if (ve_loading) ve_loading.close()
+      }
+      let type = ''
+      if (response.data.code == 0 || response.data.code === 1) {
+        type = 'success'
+      } else {
+        type = 'error'
+      }
+      if (type === 'error' || getIsShow(response.request.responseURL) === false) {
+        if (ve_message) {
+          ve_message.close()
+          ve_message = null
+        }
+        if (response.data.msg) {
+          console.log(response.data.msg)
+        }
+      }
+      return response.data
+    },
+    (error) => {
+      loadingCount--
+      if (loadingCount <= 0) {
+        NProgress.done()
+        ve_loading && ve_loading.close()
+      }
+      if (error && error.response) {
+        let message = ''
+        switch (error.response.status) {
+          case 400:
+            message = '请求错误'
+            break
+          case 401: {
+            message = '未授权,请登录'
+            router.replace({ name: 'DevLogin' })
+            break
+          }
+          case 403:
+            message = '没有权限,拒绝访问'
+            break
+          case 404:
+            message = '请求地址出错'
+            break
+          case 408:
+            message = '请求超时'
+            break
+          case 500:
+            message = '服务内部错误'
+            break
+          case 501:
+            message = '服务未实现'
+            break
+          case 502:
+            message = '网关错误'
+            break
+          case 503:
+            message = '服务不可用'
+            break
+          case 504:
+            message = '网关超时'
+            break
+          case 505:
+            message = 'HTTP版本不受支持'
+            break
+          default:
+            break
+        }
+        if (ve_message) {
+          ve_message.close()
+          ve_message = null
+        }
+        if (message) console.error(message)
+      }
+      return Promise.reject(error)
+    },
+  )
+
+  const method = {
+    post: (url, p, reqConfig) => _axios.post(url, p, reqConfig),
+    get: (url, p, reqConfig) =>
+      _axios.get(url, Object.assign({}, reqConfig, { params: p })),
+    put: (url, p, reqConfig) => _axios.put(url, p, reqConfig),
+    delete: (url, p, reqConfig) => _axios.delete(url, reqConfig),
+  }
+
+  const api = {}
+  const modules = import.meta.glob('../api/modules/*.js', { eager: true })
+
+  Object.entries(modules).forEach(([filePath, mod]) => {
+    const fileName = filePath.replace(/.*\/([^/]+)\.js$/, '$1')
+    api[fileName] = {}
+    const obj = mod.default ?? mod
+    Object.keys(obj).forEach((item) => {
+      api[fileName][item] = (p, reqConfig = {}) => {
+        if (
+          VE_ENV.MOCK === 'True' &&
+          obj[item].mockCondition &&
+          obj[item].mockCondition(p)
+        ) {
+          return obj[item].mockFun(p)
+        }
+
+        if (store.state.app.authkey) {
+          if (obj[item].type === 'get') {
+            p = p || {}
+            p.key = store.state.app.authkey
+          } else {
+            obj[item].url =
+              obj[item].url +
+              (obj[item].url.indexOf('?') > -1
+                ? '&key=' + store.state.app.authkey
+                : '?key=' + store.state.app.authkey)
+          }
+        }
+        return method[obj[item].type](pathVariable(obj[item].url, p), p, reqConfig)
+      }
+    })
+  })
+
+  window[opt] = api
+  app.config.globalProperties[opt] = api
+}
+
+export default { install }

+ 8 - 0
src/router/globalRoutes.js

@@ -0,0 +1,8 @@
+export default [
+  {
+    path: '/',
+    name: 'Home',
+    meta: { title: '首页' },
+    component: () => import('@/views/Home.vue'),
+  },
+]

+ 11 - 0
src/router/index.js

@@ -0,0 +1,11 @@
+import { createRouter, createWebHashHistory } from 'vue-router'
+import globalRoutes from './globalRoutes.js'
+import mainRoutes from './mainRoutes.js'
+
+const router = createRouter({
+  history: createWebHashHistory(),
+  scrollBehavior: () => ({ top: 0 }),
+  routes: globalRoutes.concat(mainRoutes),
+})
+
+export default router

+ 1 - 0
src/router/mainRoutes.js

@@ -0,0 +1 @@
+export default []

+ 4 - 0
src/store/getters.js

@@ -0,0 +1,4 @@
+export default {
+  token: (state) => state.app.token,
+  uname: (state) => state.app.uname,
+}

+ 10 - 0
src/store/index.js

@@ -0,0 +1,10 @@
+import { createStore } from 'vuex'
+import getters from './getters.js'
+import app from './modules/app/index.js'
+
+export default createStore({
+  getters,
+  modules: {
+    app,
+  },
+})

+ 34 - 0
src/store/modules/app/index.js

@@ -0,0 +1,34 @@
+import { SET_TOKEN, SET_KEY, SET_UNAME } from './type.js'
+
+export default {
+  namespaced: true,
+  state: {
+    token: localStorage.getItem('token') || '',
+    authkey: null,
+    uname: sessionStorage.getItem('uname') || '',
+  },
+  mutations: {
+    [SET_TOKEN](state, token) {
+      state.token = token
+      localStorage.setItem('token', state.token)
+    },
+    [SET_KEY](state, authkey) {
+      state.authkey = authkey
+    },
+    [SET_UNAME](state, uname) {
+      state.uname = uname
+      sessionStorage.setItem('uname', state.uname)
+    },
+  },
+  actions: {
+    [SET_TOKEN]({ commit }, token) {
+      commit(SET_TOKEN, token)
+    },
+    [SET_KEY]({ commit }, authkey) {
+      commit(SET_KEY, authkey)
+    },
+    [SET_UNAME]({ commit }, uname) {
+      commit(SET_UNAME, uname)
+    },
+  },
+}

+ 3 - 0
src/store/modules/app/type.js

@@ -0,0 +1,3 @@
+export const SET_TOKEN = 'SET_TOKEN'
+export const SET_KEY = 'SET_KEY'
+export const SET_UNAME = 'SET_UNAME'

+ 296 - 0
src/style.css

@@ -0,0 +1,296 @@
+:root {
+  --text: #6b6375;
+  --text-h: #08060d;
+  --bg: #fff;
+  --border: #e5e4e7;
+  --code-bg: #f4f3ec;
+  --accent: #aa3bff;
+  --accent-bg: rgba(170, 59, 255, 0.1);
+  --accent-border: rgba(170, 59, 255, 0.5);
+  --social-bg: rgba(244, 243, 236, 0.5);
+  --shadow:
+    rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
+
+  --sans: system-ui, 'Segoe UI', Roboto, sans-serif;
+  --heading: system-ui, 'Segoe UI', Roboto, sans-serif;
+  --mono: ui-monospace, Consolas, monospace;
+
+  font: 18px/145% var(--sans);
+  letter-spacing: 0.18px;
+  color-scheme: light dark;
+  color: var(--text);
+  background: var(--bg);
+  font-synthesis: none;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+
+  @media (max-width: 1024px) {
+    font-size: 16px;
+  }
+}
+
+@media (prefers-color-scheme: dark) {
+  :root {
+    --text: #9ca3af;
+    --text-h: #f3f4f6;
+    --bg: #16171d;
+    --border: #2e303a;
+    --code-bg: #1f2028;
+    --accent: #c084fc;
+    --accent-bg: rgba(192, 132, 252, 0.15);
+    --accent-border: rgba(192, 132, 252, 0.5);
+    --social-bg: rgba(47, 48, 58, 0.5);
+    --shadow:
+      rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
+  }
+
+  #social .button-icon {
+    filter: invert(1) brightness(2);
+  }
+}
+
+body {
+  margin: 0;
+}
+
+h1,
+h2 {
+  font-family: var(--heading);
+  font-weight: 500;
+  color: var(--text-h);
+}
+
+h1 {
+  font-size: 56px;
+  letter-spacing: -1.68px;
+  margin: 32px 0;
+  @media (max-width: 1024px) {
+    font-size: 36px;
+    margin: 20px 0;
+  }
+}
+h2 {
+  font-size: 24px;
+  line-height: 118%;
+  letter-spacing: -0.24px;
+  margin: 0 0 8px;
+  @media (max-width: 1024px) {
+    font-size: 20px;
+  }
+}
+p {
+  margin: 0;
+}
+
+code,
+.counter {
+  font-family: var(--mono);
+  display: inline-flex;
+  border-radius: 4px;
+  color: var(--text-h);
+}
+
+code {
+  font-size: 15px;
+  line-height: 135%;
+  padding: 4px 8px;
+  background: var(--code-bg);
+}
+
+.counter {
+  font-size: 16px;
+  padding: 5px 10px;
+  border-radius: 5px;
+  color: var(--accent);
+  background: var(--accent-bg);
+  border: 2px solid transparent;
+  transition: border-color 0.3s;
+  margin-bottom: 24px;
+
+  &:hover {
+    border-color: var(--accent-border);
+  }
+  &:focus-visible {
+    outline: 2px solid var(--accent);
+    outline-offset: 2px;
+  }
+}
+
+.hero {
+  position: relative;
+
+  .base,
+  .framework,
+  .vite {
+    inset-inline: 0;
+    margin: 0 auto;
+  }
+
+  .base {
+    width: 170px;
+    position: relative;
+    z-index: 0;
+  }
+
+  .framework,
+  .vite {
+    position: absolute;
+  }
+
+  .framework {
+    z-index: 1;
+    top: 34px;
+    height: 28px;
+    transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
+      scale(1.4);
+  }
+
+  .vite {
+    z-index: 0;
+    top: 107px;
+    height: 26px;
+    width: auto;
+    transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
+      scale(0.8);
+  }
+}
+
+#app {
+  width: 1126px;
+  max-width: 100%;
+  margin: 0 auto;
+  text-align: center;
+  border-inline: 1px solid var(--border);
+  min-height: 100svh;
+  display: flex;
+  flex-direction: column;
+  box-sizing: border-box;
+}
+
+#center {
+  display: flex;
+  flex-direction: column;
+  gap: 25px;
+  place-content: center;
+  place-items: center;
+  flex-grow: 1;
+
+  @media (max-width: 1024px) {
+    padding: 32px 20px 24px;
+    gap: 18px;
+  }
+}
+
+#next-steps {
+  display: flex;
+  border-top: 1px solid var(--border);
+  text-align: left;
+
+  & > div {
+    flex: 1 1 0;
+    padding: 32px;
+    @media (max-width: 1024px) {
+      padding: 24px 20px;
+    }
+  }
+
+  .icon {
+    margin-bottom: 16px;
+    width: 22px;
+    height: 22px;
+  }
+
+  @media (max-width: 1024px) {
+    flex-direction: column;
+    text-align: center;
+  }
+}
+
+#docs {
+  border-right: 1px solid var(--border);
+
+  @media (max-width: 1024px) {
+    border-right: none;
+    border-bottom: 1px solid var(--border);
+  }
+}
+
+#next-steps ul {
+  list-style: none;
+  padding: 0;
+  display: flex;
+  gap: 8px;
+  margin: 32px 0 0;
+
+  .logo {
+    height: 18px;
+  }
+
+  a {
+    color: var(--text-h);
+    font-size: 16px;
+    border-radius: 6px;
+    background: var(--social-bg);
+    display: flex;
+    padding: 6px 12px;
+    align-items: center;
+    gap: 8px;
+    text-decoration: none;
+    transition: box-shadow 0.3s;
+
+    &:hover {
+      box-shadow: var(--shadow);
+    }
+    .button-icon {
+      height: 18px;
+      width: 18px;
+    }
+  }
+
+  @media (max-width: 1024px) {
+    margin-top: 20px;
+    flex-wrap: wrap;
+    justify-content: center;
+
+    li {
+      flex: 1 1 calc(50% - 8px);
+    }
+
+    a {
+      width: 100%;
+      justify-content: center;
+      box-sizing: border-box;
+    }
+  }
+}
+
+#spacer {
+  height: 88px;
+  border-top: 1px solid var(--border);
+  @media (max-width: 1024px) {
+    height: 48px;
+  }
+}
+
+.ticks {
+  position: relative;
+  width: 100%;
+
+  &::before,
+  &::after {
+    content: '';
+    position: absolute;
+    top: -4.5px;
+    border: 5px solid transparent;
+  }
+
+  &::before {
+    left: 0;
+    border-left-color: var(--border);
+  }
+  &::after {
+    right: 0;
+    border-right-color: var(--border);
+  }
+}

+ 16 - 0
src/views/Home.vue

@@ -0,0 +1,16 @@
+<script setup lang="ts">
+</script>
+
+<template>
+  <div class="home">
+    <h1>认养 H5</h1>
+    <p>基础配置已就绪:Vue Router、Vuex、Axios(VE_API)</p>
+  </div>
+</template>
+
+<style scoped>
+.home {
+  padding: 24px;
+  text-align: center;
+}
+</style>

+ 21 - 0
tsconfig.app.json

@@ -0,0 +1,21 @@
+{
+  "extends": "@vue/tsconfig/tsconfig.dom.json",
+  "compilerOptions": {
+    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+    "types": ["vite/client"],
+    "baseUrl": ".",
+    "paths": {
+      "@/*": ["src/*"]
+    },
+    "allowJs": true,
+    "checkJs": false,
+    "ignoreDeprecations": "6.0",
+
+    /* Linting */
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "erasableSyntaxOnly": true,
+    "noFallthroughCasesInSwitch": true
+  },
+  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "src/**/*.js", "src/env.d.ts"]
+}

+ 7 - 0
tsconfig.json

@@ -0,0 +1,7 @@
+{
+  "files": [],
+  "references": [
+    { "path": "./tsconfig.app.json" },
+    { "path": "./tsconfig.node.json" }
+  ]
+}

+ 24 - 0
tsconfig.node.json

@@ -0,0 +1,24 @@
+{
+  "compilerOptions": {
+    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+    "target": "es2023",
+    "lib": ["ES2023"],
+    "module": "esnext",
+    "types": ["node"],
+    "skipLibCheck": true,
+
+    /* Bundler mode */
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": true,
+    "verbatimModuleSyntax": true,
+    "moduleDetection": "force",
+    "noEmit": true,
+
+    /* Linting */
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "erasableSyntaxOnly": true,
+    "noFallthroughCasesInSwitch": true
+  },
+  "include": ["vite.config.ts"]
+}

+ 26 - 0
vite.config.ts

@@ -0,0 +1,26 @@
+import { defineConfig, loadEnv } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import path from 'path'
+
+// https://vite.dev/config/
+export default defineConfig(({ mode }) => {
+  const env = loadEnv(mode, process.cwd(), '')
+  const isProd = mode === 'production'
+
+  return {
+    plugins: [vue()],
+    resolve: {
+      alias: {
+        '@': path.resolve(__dirname, 'src'),
+      },
+    },
+    define: {
+      VE_ENV: {
+        MODE: JSON.stringify(isProd ? 'production' : 'development'),
+        SERVER: JSON.stringify(env.VITE_SERVER ?? ''),
+        PYSERVER: JSON.stringify(env.VITE_PYSERVER ?? ''),
+        MOCK: JSON.stringify(env.VITE_MOCK ?? 'False'),
+      },
+    },
+  }
+})