vite.config.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import path from 'node:path'
  2. import { defineConfig, loadEnv } from 'vite'
  3. import detectPort from 'detect-port'
  4. import vue from '@vitejs/plugin-vue'
  5. import vueJsx from '@vitejs/plugin-vue-jsx'
  6. import AutoImport from 'unplugin-auto-import/vite'
  7. import Components from 'unplugin-vue-components/vite'
  8. import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
  9. import { createStyleImportPlugin, VxeTableResolve } from 'vite-plugin-style-import'
  10. import Unocss from 'unocss/vite'
  11. import { lazyImport, VxeResolver } from 'vite-plugin-lazy-import'
  12. import { createProjectShellAliases, resolveProjectShellComponentsDir } from './vite.shell-aliases'
  13. const DEFAULT_BACKEND_TARGET = 'http://127.0.0.1:8086'
  14. /**
  15. * Docker 开发会把 VITE_BASE_PATH 设为 host.docker.internal;若宿主机直接 pnpm dev 仍残留
  16. * .env.development.local,代理会连不上并导致登录页「服务器错误」。非 Docker 时自动回退本机地址。
  17. */
  18. function resolveViteBackendTarget(rawTarget: string | undefined): string {
  19. const target = (rawTarget || DEFAULT_BACKEND_TARGET).trim()
  20. const isDockerDev = process.env.DOCKER_DEV === '1' || process.env.DOCKER_DEV === 'true'
  21. if (!isDockerDev && target.includes('host.docker.internal')) {
  22. console.warn(
  23. `[vite] VITE_BASE_PATH=${target} 仅适用于 Docker 容器内;已回退代理至 ${DEFAULT_BACKEND_TARGET}`
  24. )
  25. return DEFAULT_BACKEND_TARGET
  26. }
  27. return target
  28. }
  29. export default defineConfig(async ({ mode }) => {
  30. const DEFAULT_PORT = 9012
  31. const env = loadEnv(mode, process.cwd(), '')
  32. const backendTarget = resolveViteBackendTarget(env.VITE_BASE_PATH)
  33. const port = await detectPort(DEFAULT_PORT)
  34. const shellComponentsDir = resolveProjectShellComponentsDir()
  35. const shellSrc = path.join(process.cwd(), 'node_modules/@fskj-admin/shell/src')
  36. const shellSrcPattern = new RegExp(shellSrc.replace(/[\\/]/g, '[\\\\/]'))
  37. return {
  38. base: '/',
  39. define: {
  40. 'import.meta.env.VITE_BASE_PATH': JSON.stringify(backendTarget)
  41. },
  42. plugins: [
  43. vue(),
  44. vueJsx(),
  45. Unocss(),
  46. AutoImport({
  47. imports: ['vue', 'vue-router', 'pinia', '@vueuse/core'],
  48. dts: 'src/auto-import.d.ts',
  49. // shell 与业务仓 SFC 均依赖 auto-import(ref/computed 等);须覆盖 alias 解析后的 shell 绝对路径
  50. include: [
  51. shellSrcPattern,
  52. /[\\/]node_modules[\\/]@fskj-admin[\\/]shell[\\/]/,
  53. /[\\/]src[\\/]/,
  54. /\.vue$/,
  55. /\.vue\?vue/,
  56. /\.vue\?vue&type=script/
  57. ],
  58. // 默认 exclude 含 /node_modules/,会跳过 npm 内 @fskj-admin/shell,导致 BasicLayout 等 computed 未注入
  59. exclude: [
  60. /[\\/]node_modules[\\/](?!.*@fskj-admin[\\/]shell[\\/])/,
  61. /[\\/]\.git[\\/]/
  62. ],
  63. viteOptimizeDeps: true
  64. }),
  65. Components({
  66. dirs: ['src/components', shellComponentsDir],
  67. extensions: ['vue'],
  68. dts: 'src/components.d.ts',
  69. // 与 AutoImport 一致:须处理 npm 内 @fskj-admin/shell 的 SFC,否则 platformops 等页
  70. // 使用的 BasicLeftTree / ProTable 等壳组件无法自动注入(Failed to resolve component)
  71. include: [
  72. shellSrcPattern,
  73. /[\\/]node_modules[\\/]@fskj-admin[\\/]shell[\\/]/,
  74. /[\\/]src[\\/]/,
  75. /\.vue$/,
  76. /\.vue\?vue/,
  77. /\.vue\?vue&type=script/
  78. ],
  79. exclude: [
  80. /[\\/]node_modules[\\/](?!.*@fskj-admin[\\/]shell[\\/])/,
  81. /[\\/]\.git[\\/]/
  82. ]
  83. }),
  84. createSvgIconsPlugin({
  85. iconDirs: [path.resolve(process.cwd(), 'src/assets/svg')],
  86. symbolId: 'icon-[dir]-[name]'
  87. }),
  88. createStyleImportPlugin({
  89. resolves: [VxeTableResolve()]
  90. }),
  91. lazyImport({
  92. resolvers: [
  93. VxeResolver({ libraryName: 'vxe-table' }),
  94. VxeResolver({ libraryName: 'vxe-pc-ui' })
  95. ]
  96. })
  97. ],
  98. css: {
  99. preprocessorOptions: {
  100. scss: {
  101. silenceDeprecations: ['legacy-js-api']
  102. }
  103. }
  104. },
  105. resolve: {
  106. alias: [
  107. {
  108. find: '@/components/form/ElImageUploadOne.vue',
  109. replacement: path.resolve(process.cwd(), 'src/components/form/ElImageUploadOne.vue')
  110. },
  111. {
  112. find: '@/components/form/ElImageUploadMore.vue',
  113. replacement: path.resolve(process.cwd(), 'src/components/form/ElImageUploadMore.vue')
  114. },
  115. {
  116. find: '@/components/form/ImageUploadTip.vue',
  117. replacement: path.resolve(process.cwd(), 'src/components/form/ImageUploadTip.vue')
  118. },
  119. {
  120. find: '@/components/form/ElEditor.vue',
  121. replacement: path.resolve(process.cwd(), 'src/components/form/ElEditor.vue')
  122. },
  123. {
  124. find: '@/components/layout/GlobalMainNav.vue',
  125. replacement: path.resolve(process.cwd(), 'src/components/layout/GlobalMainNav.vue')
  126. },
  127. {
  128. find: '@/utils/formatMainNavLabel',
  129. replacement: path.resolve(process.cwd(), 'src/utils/formatMainNavLabel.ts')
  130. },
  131. // pnpm + shell 源码直引时 dedupe 可能失效,须强制单例 pinia/vue-router
  132. {
  133. find: 'pinia',
  134. replacement: path.resolve(process.cwd(), 'node_modules/pinia/dist/pinia.mjs')
  135. },
  136. {
  137. find: 'vue-router',
  138. replacement: path.resolve(process.cwd(), 'node_modules/vue-router/dist/vue-router.mjs')
  139. },
  140. // xe-utils 当前安装版本无 es 构建,改走 dist UMD 构建
  141. {
  142. find: 'xe-utils',
  143. replacement: path.resolve(process.cwd(), 'node_modules/xe-utils/dist/xe-utils.umd.js')
  144. },
  145. // dayjs 主入口为 UMD(dayjs.min.js),element-plus ESM 需 default 导出
  146. {
  147. find: /^dayjs\/plugin\/(.+)\.js$/,
  148. replacement: path.resolve(process.cwd(), 'node_modules/dayjs/esm/plugin/$1/index.js')
  149. },
  150. {
  151. find: /^dayjs$/,
  152. replacement: path.resolve(process.cwd(), 'node_modules/dayjs/esm/index.js')
  153. },
  154. ...createProjectShellAliases()
  155. ],
  156. // monorepo + pnpm 下避免 pinia/vue 多实例,否则 getActivePinia() 报错
  157. dedupe: ['pinia', 'vue', 'vue-router', '@vue/runtime-core']
  158. },
  159. optimizeDeps: {
  160. exclude: [
  161. '@fskj-admin/shell',
  162. '@shell/router/appRouterHolder',
  163. '@shell/router/createAppRouter',
  164. '@shell/router/businessRouterExtension',
  165. '@shell/home/homeExtension',
  166. '@shell/bootstrap/createAdminApp',
  167. '@shell/router/platformOpsRouterExtension',
  168. '@shell/platformops/register',
  169. '@shell/platformops/essentialRoutes',
  170. '@shell/stores/user',
  171. '@shell/stores/router',
  172. '@shell/stores/menu',
  173. '@shell/stores/theme',
  174. '@shell/stores/permission',
  175. '@/stores/user',
  176. '@/stores/permission',
  177. '@shell/utils/utils'
  178. ],
  179. // shell 被 exclude 后其 CJS 依赖须单独预打包;与框架仓对齐,避免 dev 首次点「菜单配置」触发 optimized dependencies changed 整页刷新
  180. entries: [
  181. './index.html',
  182. './src/main.ts',
  183. './node_modules/@fskj-admin/shell/src/platformops/views/subsystem/index.vue',
  184. './node_modules/@fskj-admin/shell/src/platformops/views/subsystem/subsystemMenu.vue'
  185. ],
  186. include: [
  187. 'pinia',
  188. 'vue',
  189. 'vue-router',
  190. 'xe-utils',
  191. 'nprogress',
  192. 'lodash',
  193. 'jsencrypt',
  194. 'echarts',
  195. 'vue-cropper',
  196. 'splitpanes',
  197. '@element-plus/icons-vue',
  198. 'element-plus',
  199. 'vxe-table',
  200. 'vxe-pc-ui',
  201. '@vxe-ui/plugin-render-element'
  202. ],
  203. needsInterop: ['xe-utils', 'nprogress', 'lodash']
  204. },
  205. server: {
  206. host: '0.0.0.0',
  207. port,
  208. open: '/#/login',
  209. // dev 启动时预热应用维护/菜单配置相关 shell 页面,减少首次点击菜单配置时的依赖发现与 reload
  210. warmup: {
  211. clientFiles: [
  212. './src/main.ts',
  213. './node_modules/@fskj-admin/shell/src/platformops/views/subsystem/index.vue',
  214. './node_modules/@fskj-admin/shell/src/platformops/views/subsystem/subsystemMenu.vue',
  215. './node_modules/@fskj-admin/shell/src/platformops/views/subsystem/components/menuButton.vue',
  216. './node_modules/@fskj-admin/shell/src/platformops/views/subsystem/components/menuList.vue',
  217. './node_modules/@fskj-admin/shell/src/platformops/views/subsystem/components/menuForm.vue',
  218. './node_modules/@fskj-admin/shell/src/platformops/views/subsystem/components/menuPermission.vue'
  219. ]
  220. },
  221. proxy: {
  222. [env.VITE_BASE_API]: {
  223. target: backendTarget,
  224. changeOrigin: true
  225. // 勿 strip /admin:后端 jeesharp.web.adminPath=/admin,登录等白名单为 /admin/login
  226. }
  227. }
  228. },
  229. build: {
  230. chunkSizeWarningLimit: 1500
  231. }
  232. }
  233. })