服务打包初步结构
This commit is contained in:
@@ -1,24 +1,22 @@
|
||||
/**
|
||||
* @description 应用运行时配置文件
|
||||
* @author yslg
|
||||
* @since 2025-12-06
|
||||
* @description 应用运行时配置文件 (支持 Docker 环境变量替换)
|
||||
*
|
||||
* 说明:
|
||||
* 1. 此文件在生产环境中被加载,用于覆盖内置配置
|
||||
* 2. Docker 部署时,可通过挂载此文件来修改配置,无需重新构建
|
||||
* 3. 配置结构必须与 packages/shared/src/config/index.ts 中的 AppRuntimeConfig 保持一致
|
||||
* 占位符说明:
|
||||
* - __PLACEHOLDER__ 格式的值会在 Docker 启动时被环境变量替换
|
||||
* - 如果环境变量未设置,将使用默认值
|
||||
*
|
||||
* 使用示例(Docker):
|
||||
* docker run -v /path/to/app-config.js:/app/public/app-config.js my-app:latest
|
||||
* Docker 部署:
|
||||
* 1. 通过 volume 挂载覆盖此文件
|
||||
* 2. 或通过启动脚本替换占位符
|
||||
*/
|
||||
|
||||
window.APP_RUNTIME_CONFIG = {
|
||||
// 环境标识
|
||||
env: 'production',
|
||||
env: '__APP_ENV__',
|
||||
|
||||
// API 配置
|
||||
api: {
|
||||
baseUrl: '/api',
|
||||
baseUrl: '__API_BASE_URL__',
|
||||
timeout: 30000
|
||||
},
|
||||
|
||||
@@ -27,12 +25,12 @@ window.APP_RUNTIME_CONFIG = {
|
||||
|
||||
// 文件配置
|
||||
file: {
|
||||
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||
downloadUrl: '__API_BASE_URL__/urban-lifeline/file/download/',
|
||||
uploadUrl: '__API_BASE_URL__/urban-lifeline/file/upload',
|
||||
maxSize: {
|
||||
image: 5, // MB
|
||||
video: 100, // MB
|
||||
document: 10 // MB
|
||||
image: 5,
|
||||
video: 100,
|
||||
document: 10
|
||||
},
|
||||
acceptTypes: {
|
||||
image: 'image/*',
|
||||
@@ -44,20 +42,23 @@ window.APP_RUNTIME_CONFIG = {
|
||||
// Token 配置
|
||||
token: {
|
||||
key: 'token',
|
||||
refreshThreshold: 300000 // 5分钟
|
||||
refreshThreshold: 300000
|
||||
},
|
||||
|
||||
// 公共资源路径
|
||||
publicImgPath: '/img',
|
||||
publicWebPath: '/',
|
||||
publicImgPath: '__PUBLIC_PATH__/img',
|
||||
publicWebPath: '__PUBLIC_PATH__',
|
||||
|
||||
// 单点登录配置
|
||||
sso: {
|
||||
platformUrl: '/', // platform 平台地址
|
||||
workcaseUrl: '/workcase', // workcase 服务地址
|
||||
biddingUrl: '/bidding' // bidding 服务地址
|
||||
platformUrl: '__SSO_PLATFORM_URL__',
|
||||
workcaseUrl: '__SSO_WORKCASE_URL__',
|
||||
biddingUrl: '__SSO_BIDDING_URL__'
|
||||
},
|
||||
|
||||
// AES 加密密钥
|
||||
aesSecretKey: '__AES_SECRET_KEY__',
|
||||
|
||||
// 功能开关
|
||||
features: {
|
||||
enableDebug: false,
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
/**
|
||||
* @description Bidding 应用运行时配置
|
||||
* @author yslg
|
||||
* @since 2025-12-06
|
||||
* @description 应用运行时配置
|
||||
*
|
||||
* 配置加载策略:
|
||||
* 1. 开发环境:使用下面定义的开发配置
|
||||
* 1. 开发环境:使用内置开发配置
|
||||
* 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js)
|
||||
* 3. Docker部署:替换 app-config.js 文件实现配置外挂
|
||||
*
|
||||
* 配置结构说明:
|
||||
* 此文件的配置结构与 app-config.js 完全对应
|
||||
* 修改 app-config.js 后,这里的配置会自动应用
|
||||
* 3. Docker部署:启动时替换 app-config.js 中的占位符
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
@@ -43,12 +37,12 @@ export interface AppRuntimeConfig {
|
||||
};
|
||||
publicImgPath: string;
|
||||
publicWebPath: string;
|
||||
// 单点登录配置
|
||||
sso?: {
|
||||
platformUrl: string; // platform 平台地址
|
||||
workcaseUrl: string; // workcase 服务地址
|
||||
biddingUrl: string; // bidding 服务地址
|
||||
platformUrl: string;
|
||||
workcaseUrl: string;
|
||||
biddingUrl: string;
|
||||
};
|
||||
aesSecretKey?: string;
|
||||
features?: {
|
||||
enableDebug?: boolean;
|
||||
enableMockData?: boolean;
|
||||
@@ -57,101 +51,80 @@ export interface AppRuntimeConfig {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 配置定义(与 app-config.js 结构一致)
|
||||
// 环境检测
|
||||
// ============================================
|
||||
const isDev = (import.meta as any).env?.DEV ?? false;
|
||||
|
||||
// ============================================
|
||||
// 开发环境配置
|
||||
// ============================================
|
||||
const devConfig: AppRuntimeConfig = {
|
||||
env: 'development',
|
||||
|
||||
api: {
|
||||
// 开发环境通过 Vite 代理转发到后端,避免浏览器直接跨域
|
||||
// 实际请求路径示例:/api/... → 由代理转发到实际后端
|
||||
baseUrl: '/api',
|
||||
timeout: 30000
|
||||
},
|
||||
|
||||
baseUrl: '/',
|
||||
|
||||
file: {
|
||||
// 通过 Nginx → Gateway 访问文件服务,使用 /urban-lifeline 前缀
|
||||
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||
maxSize: {
|
||||
image: 5,
|
||||
video: 100,
|
||||
document: 10
|
||||
},
|
||||
maxSize: { image: 5, video: 100, document: 10 },
|
||||
acceptTypes: {
|
||||
image: 'image/*',
|
||||
video: 'video/*',
|
||||
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
|
||||
}
|
||||
},
|
||||
|
||||
token: {
|
||||
key: 'token',
|
||||
refreshThreshold: 300000
|
||||
},
|
||||
|
||||
publicImgPath: 'http://localhost:7002/img',
|
||||
publicWebPath: 'http://localhost:7002',
|
||||
|
||||
// 单点登录配置
|
||||
publicImgPath: '/img',
|
||||
publicWebPath: '/',
|
||||
sso: {
|
||||
platformUrl: '/', // 通过nginx访问platform
|
||||
workcaseUrl: '/workcase', // 通过nginx访问workcase
|
||||
biddingUrl: '/bidding' // 通过nginx访问bidding
|
||||
platformUrl: '/',
|
||||
workcaseUrl: '/workcase',
|
||||
biddingUrl: '/bidding'
|
||||
},
|
||||
|
||||
aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=',
|
||||
features: {
|
||||
enableDebug: true,
|
||||
enableMockData: false
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// 生产环境默认配置(兜底)
|
||||
// ============================================
|
||||
const prodDefaultConfig: AppRuntimeConfig = {
|
||||
env: 'production',
|
||||
|
||||
api: {
|
||||
baseUrl: '/api',
|
||||
timeout: 30000
|
||||
},
|
||||
|
||||
baseUrl: '/',
|
||||
|
||||
file: {
|
||||
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||
maxSize: {
|
||||
image: 5,
|
||||
video: 100,
|
||||
document: 10
|
||||
},
|
||||
maxSize: { image: 5, video: 100, document: 10 },
|
||||
acceptTypes: {
|
||||
image: 'image/*',
|
||||
video: 'video/*',
|
||||
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
|
||||
}
|
||||
},
|
||||
|
||||
token: {
|
||||
key: 'token',
|
||||
refreshThreshold: 300000
|
||||
},
|
||||
|
||||
publicImgPath: '/img',
|
||||
publicWebPath: '/',
|
||||
|
||||
// 单点登录配置(生产环境通过nginx代理)
|
||||
sso: {
|
||||
platformUrl: '/',
|
||||
workcaseUrl: '/workcase',
|
||||
biddingUrl: '/bidding'
|
||||
},
|
||||
|
||||
aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=',
|
||||
features: {
|
||||
enableDebug: false,
|
||||
enableMockData: false
|
||||
@@ -162,92 +135,82 @@ const prodDefaultConfig: AppRuntimeConfig = {
|
||||
// 配置加载
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 检查值是否为未替换的占位符
|
||||
*/
|
||||
const isPlaceholder = (value: any): boolean => {
|
||||
return typeof value === 'string' && value.startsWith('__') && value.endsWith('__');
|
||||
};
|
||||
|
||||
/**
|
||||
* 深度合并配置,跳过占位符值
|
||||
*/
|
||||
const mergeConfig = (target: any, source: any): any => {
|
||||
const result = { ...target };
|
||||
for (const key in source) {
|
||||
const value = source[key];
|
||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
result[key] = mergeConfig(target[key] || {}, value);
|
||||
} else if (!isPlaceholder(value)) {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取运行时配置
|
||||
* 生产环境优先从 window.APP_RUNTIME_CONFIG 读取(app-config.js 注入)
|
||||
*/
|
||||
const getRuntimeConfig = (): AppRuntimeConfig => {
|
||||
if (isDev) {
|
||||
console.log('[配置] 开发环境,使用内置配置');
|
||||
console.log('[Config] 开发环境,使用内置配置');
|
||||
return devConfig;
|
||||
}
|
||||
|
||||
// 生产环境:尝试读取外部配置
|
||||
try {
|
||||
const runtimeConfig = (window as any).APP_RUNTIME_CONFIG;
|
||||
if (runtimeConfig && typeof runtimeConfig === 'object') {
|
||||
console.log('[配置] 加载外部配置 app-config.js');
|
||||
console.log('[配置] API地址:', runtimeConfig.api?.baseUrl);
|
||||
console.log('[配置] 环境:', runtimeConfig.env || 'production');
|
||||
return runtimeConfig as AppRuntimeConfig;
|
||||
const merged = mergeConfig(prodDefaultConfig, runtimeConfig);
|
||||
console.log('[Config] 加载运行时配置', merged);
|
||||
return merged;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[配置] 无法读取外部配置,使用默认配置', e);
|
||||
console.warn('[Config] 无法读取外部配置', e);
|
||||
}
|
||||
|
||||
console.log('[配置] 使用默认生产配置');
|
||||
console.log('[Config] 使用默认生产配置');
|
||||
return prodDefaultConfig;
|
||||
};
|
||||
|
||||
// 当前应用配置
|
||||
// 当前配置
|
||||
const config = getRuntimeConfig();
|
||||
console.log('[配置] 当前配置', config);
|
||||
|
||||
// ============================================
|
||||
// 导出配置(向后兼容)
|
||||
// 导出
|
||||
// ============================================
|
||||
|
||||
// 单独导出常用配置项
|
||||
// AES 密钥
|
||||
export const AES_SECRET_KEY = config.aesSecretKey || 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=';
|
||||
|
||||
// 常用配置项
|
||||
export const API_BASE_URL = config.api.baseUrl;
|
||||
export const FILE_DOWNLOAD_URL = config.file.downloadUrl;
|
||||
export const FILE_UPLOAD_URL = config.file.uploadUrl;
|
||||
export const PUBLIC_IMG_PATH = config.publicImgPath;
|
||||
export const PUBLIC_WEB_PATH = config.publicWebPath;
|
||||
|
||||
// 导出完整配置对象
|
||||
// 完整配置对象
|
||||
export const APP_CONFIG = {
|
||||
// 应用标题
|
||||
title: '泰豪电源招投标系统',
|
||||
|
||||
// 环境标识
|
||||
env: config.env || 'production',
|
||||
|
||||
// 应用基础路径
|
||||
baseUrl: config.baseUrl,
|
||||
|
||||
// API 配置
|
||||
api: {
|
||||
baseUrl: config.api.baseUrl,
|
||||
timeout: config.api.timeout
|
||||
},
|
||||
|
||||
// 文件配置
|
||||
file: {
|
||||
downloadUrl: config.file.downloadUrl,
|
||||
uploadUrl: config.file.uploadUrl,
|
||||
maxSize: config.file.maxSize,
|
||||
acceptTypes: config.file.acceptTypes
|
||||
},
|
||||
|
||||
// Token 配置
|
||||
token: {
|
||||
key: config.token.key,
|
||||
refreshThreshold: config.token.refreshThreshold
|
||||
},
|
||||
|
||||
// 公共路径
|
||||
api: config.api,
|
||||
file: config.file,
|
||||
token: config.token,
|
||||
publicImgPath: config.publicImgPath,
|
||||
publicWebPath: config.publicWebPath,
|
||||
|
||||
// 单点登录配置
|
||||
sso: config.sso || {
|
||||
platformUrl: '/',
|
||||
workcaseUrl: '/workcase',
|
||||
biddingUrl: '/bidding'
|
||||
},
|
||||
|
||||
// 功能开关
|
||||
sso: config.sso || { platformUrl: '/', workcaseUrl: '/workcase', biddingUrl: '/bidding' },
|
||||
features: config.features || {}
|
||||
};
|
||||
|
||||
// 默认导出
|
||||
export default APP_CONFIG;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import { federation } from '@module-federation/vite'
|
||||
import { resolve, dirname } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import fs from 'fs'
|
||||
@@ -8,61 +9,95 @@ import fs from 'fs'
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
|
||||
export default defineConfig(({ mode }) => ({
|
||||
// 开发和生产环境都通过nginx代理访问/bidding
|
||||
base: '/bidding/',
|
||||
|
||||
plugins: [
|
||||
vue({
|
||||
script: {
|
||||
defineModel: true,
|
||||
propsDestructure: true
|
||||
}
|
||||
}),
|
||||
vueJsx()
|
||||
],
|
||||
|
||||
define: {
|
||||
__VUE_OPTIONS_API__: true,
|
||||
__VUE_PROD_DEVTOOLS__: true,
|
||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true
|
||||
},
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src')
|
||||
}
|
||||
},
|
||||
|
||||
server: {
|
||||
port: 7002,
|
||||
host: true,
|
||||
cors: true,
|
||||
open: '/bidding/', // 开发时自动打开到 /bidding/ 路径
|
||||
// HTTPS 配置(使用 mkcert 生成的本地开发证书)
|
||||
https: {
|
||||
key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'),
|
||||
cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem')
|
||||
// 开发环境 shared 模块地址
|
||||
const DEV_SHARED_URL = 'https://localhost:7000/shared/mf-manifest.json'
|
||||
// 生产环境使用相对路径,通过 Nginx 代理访问
|
||||
const PROD_SHARED_URL = '/shared/mf-manifest.json'
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const isDev = mode === 'development'
|
||||
const sharedEntry = isDev ? DEV_SHARED_URL : PROD_SHARED_URL
|
||||
|
||||
return {
|
||||
base: '/bidding/',
|
||||
|
||||
plugins: [
|
||||
vue({
|
||||
script: {
|
||||
defineModel: true,
|
||||
propsDestructure: true
|
||||
}
|
||||
}),
|
||||
vueJsx(),
|
||||
federation({
|
||||
name: 'bidding',
|
||||
remotes: {
|
||||
shared: {
|
||||
type: 'module',
|
||||
name: 'shared',
|
||||
entry: sharedEntry
|
||||
}
|
||||
},
|
||||
shared: {
|
||||
vue: {},
|
||||
'vue-router': {},
|
||||
'element-plus': {},
|
||||
axios: {}
|
||||
}
|
||||
})
|
||||
],
|
||||
|
||||
define: {
|
||||
__VUE_OPTIONS_API__: true,
|
||||
__VUE_PROD_DEVTOOLS__: true,
|
||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true
|
||||
},
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8180',
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/api/, '')
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
'vue-vendor': ['vue', 'vue-router', 'pinia'],
|
||||
'element-plus': ['element-plus']
|
||||
},
|
||||
|
||||
server: {
|
||||
port: 7002,
|
||||
host: true,
|
||||
cors: true,
|
||||
open: '/bidding/',
|
||||
https: (() => {
|
||||
try {
|
||||
return {
|
||||
key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'),
|
||||
cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem')
|
||||
}
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
})(),
|
||||
hmr: {
|
||||
path: '/@vite/client',
|
||||
port: 7002
|
||||
},
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8180',
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/api/, '')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
'vue-vendor': ['vue', 'vue-router', 'pinia'],
|
||||
'element-plus': ['element-plus']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
/**
|
||||
* @description 应用运行时配置文件
|
||||
* @author yslg
|
||||
* @since 2025-12-06
|
||||
* @description 应用运行时配置文件 (支持 Docker 环境变量替换)
|
||||
*
|
||||
* 说明:
|
||||
* 1. 此文件在生产环境中被加载,用于覆盖内置配置
|
||||
* 2. Docker 部署时,可通过挂载此文件来修改配置,无需重新构建
|
||||
* 3. 配置结构必须与 packages/shared/src/config/index.ts 中的 AppRuntimeConfig 保持一致
|
||||
* 占位符说明:
|
||||
* - __PLACEHOLDER__ 格式的值会在 Docker 启动时被环境变量替换
|
||||
* - 如果环境变量未设置,将使用默认值
|
||||
*
|
||||
* 使用示例(Docker):
|
||||
* docker run -v /path/to/app-config.js:/app/public/app-config.js my-app:latest
|
||||
* Docker 部署:
|
||||
* 1. 通过 volume 挂载覆盖此文件
|
||||
* 2. 或通过启动脚本替换占位符
|
||||
*/
|
||||
|
||||
window.APP_RUNTIME_CONFIG = {
|
||||
// 环境标识
|
||||
env: 'production',
|
||||
env: '__APP_ENV__',
|
||||
|
||||
// API 配置
|
||||
api: {
|
||||
baseUrl: '/api',
|
||||
baseUrl: '__API_BASE_URL__',
|
||||
timeout: 30000
|
||||
},
|
||||
|
||||
@@ -27,12 +25,12 @@ window.APP_RUNTIME_CONFIG = {
|
||||
|
||||
// 文件配置
|
||||
file: {
|
||||
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||
downloadUrl: '__API_BASE_URL__/urban-lifeline/file/download/',
|
||||
uploadUrl: '__API_BASE_URL__/urban-lifeline/file/upload',
|
||||
maxSize: {
|
||||
image: 5, // MB
|
||||
video: 100, // MB
|
||||
document: 10 // MB
|
||||
image: 5,
|
||||
video: 100,
|
||||
document: 10
|
||||
},
|
||||
acceptTypes: {
|
||||
image: 'image/*',
|
||||
@@ -44,20 +42,23 @@ window.APP_RUNTIME_CONFIG = {
|
||||
// Token 配置
|
||||
token: {
|
||||
key: 'token',
|
||||
refreshThreshold: 300000 // 5分钟
|
||||
refreshThreshold: 300000
|
||||
},
|
||||
|
||||
// 公共资源路径
|
||||
publicImgPath: '/img',
|
||||
publicWebPath: '/',
|
||||
publicImgPath: '__PUBLIC_PATH__/img',
|
||||
publicWebPath: '__PUBLIC_PATH__',
|
||||
|
||||
// 单点登录配置
|
||||
sso: {
|
||||
platformUrl: '/', // platform 平台地址
|
||||
workcaseUrl: '/workcase', // workcase 服务地址
|
||||
biddingUrl: '/bidding' // bidding 服务地址
|
||||
platformUrl: '__SSO_PLATFORM_URL__',
|
||||
workcaseUrl: '__SSO_WORKCASE_URL__',
|
||||
biddingUrl: '__SSO_BIDDING_URL__'
|
||||
},
|
||||
|
||||
// AES 加密密钥
|
||||
aesSecretKey: '__AES_SECRET_KEY__',
|
||||
|
||||
// 功能开关
|
||||
features: {
|
||||
enableDebug: false,
|
||||
|
||||
@@ -1,28 +1,12 @@
|
||||
/**
|
||||
* @description Platform 应用运行时配置
|
||||
* @author yslg
|
||||
* @since 2025-12-06
|
||||
* @description 应用运行时配置
|
||||
*
|
||||
* 配置加载策略:
|
||||
* 1. 开发环境:使用下面定义的开发配置
|
||||
* 1. 开发环境:使用内置开发配置
|
||||
* 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js)
|
||||
* 3. Docker部署:替换 app-config.js 文件实现配置外挂
|
||||
*
|
||||
* 配置结构说明:
|
||||
* 此文件的配置结构与 app-config.js 完全对应
|
||||
* 修改 app-config.js 后,这里的配置会自动应用
|
||||
* 3. Docker部署:启动时替换 app-config.js 中的占位符
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// AES 加密密钥
|
||||
// ============================================
|
||||
/**
|
||||
* AES 加密密钥(与后端保持一致)
|
||||
* 对应后端配置:security.aes.secret-key
|
||||
* Base64 编码的 32 字节密钥(256 位)
|
||||
*/
|
||||
export const AES_SECRET_KEY = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=' // Base64 编码,解码后是 "12345678901234567890123456789012" (32字节)
|
||||
|
||||
// ============================================
|
||||
// 类型定义
|
||||
// ============================================
|
||||
@@ -53,12 +37,12 @@ export interface AppRuntimeConfig {
|
||||
};
|
||||
publicImgPath: string;
|
||||
publicWebPath: string;
|
||||
// 单点登录配置
|
||||
sso?: {
|
||||
platformUrl: string; // platform 平台地址
|
||||
workcaseUrl: string; // workcase 服务地址
|
||||
biddingUrl: string; // bidding 服务地址
|
||||
platformUrl: string;
|
||||
workcaseUrl: string;
|
||||
biddingUrl: string;
|
||||
};
|
||||
aesSecretKey?: string;
|
||||
features?: {
|
||||
enableDebug?: boolean;
|
||||
enableMockData?: boolean;
|
||||
@@ -67,101 +51,80 @@ export interface AppRuntimeConfig {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 配置定义(与 app-config.js 结构一致)
|
||||
// 环境检测
|
||||
// ============================================
|
||||
const isDev = (import.meta as any).env?.DEV ?? false;
|
||||
|
||||
// ============================================
|
||||
// 开发环境配置
|
||||
// ============================================
|
||||
const devConfig: AppRuntimeConfig = {
|
||||
env: 'development',
|
||||
|
||||
api: {
|
||||
// 开发环境通过 Vite 代理转发到后端,避免浏览器直接跨域
|
||||
// 实际请求路径示例:/api/... → 由代理转发到实际后端
|
||||
baseUrl: '/api',
|
||||
timeout: 30000
|
||||
},
|
||||
|
||||
baseUrl: '/',
|
||||
|
||||
file: {
|
||||
// 通过 Nginx → Gateway 访问文件服务,使用 /urban-lifeline 前缀
|
||||
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||
maxSize: {
|
||||
image: 5,
|
||||
video: 100,
|
||||
document: 10
|
||||
},
|
||||
maxSize: { image: 5, video: 100, document: 10 },
|
||||
acceptTypes: {
|
||||
image: 'image/*',
|
||||
video: 'video/*',
|
||||
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
|
||||
}
|
||||
},
|
||||
|
||||
token: {
|
||||
key: 'token',
|
||||
refreshThreshold: 300000
|
||||
},
|
||||
|
||||
publicImgPath: 'http://localhost:7001/img',
|
||||
publicWebPath: 'http://localhost:7001',
|
||||
|
||||
// 单点登录配置
|
||||
publicImgPath: '/img',
|
||||
publicWebPath: '/',
|
||||
sso: {
|
||||
platformUrl: '/', // 通过nginx访问platform
|
||||
workcaseUrl: '/workcase', // 通过nginx访问workcase
|
||||
biddingUrl: '/bidding' // 通过nginx访问bidding
|
||||
platformUrl: '/',
|
||||
workcaseUrl: '/workcase',
|
||||
biddingUrl: '/bidding'
|
||||
},
|
||||
|
||||
aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=',
|
||||
features: {
|
||||
enableDebug: true,
|
||||
enableMockData: false
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// 生产环境默认配置(兜底)
|
||||
// ============================================
|
||||
const prodDefaultConfig: AppRuntimeConfig = {
|
||||
env: 'production',
|
||||
|
||||
api: {
|
||||
baseUrl: '/api',
|
||||
timeout: 30000
|
||||
},
|
||||
|
||||
baseUrl: '/',
|
||||
|
||||
file: {
|
||||
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||
maxSize: {
|
||||
image: 5,
|
||||
video: 100,
|
||||
document: 10
|
||||
},
|
||||
maxSize: { image: 5, video: 100, document: 10 },
|
||||
acceptTypes: {
|
||||
image: 'image/*',
|
||||
video: 'video/*',
|
||||
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
|
||||
}
|
||||
},
|
||||
|
||||
token: {
|
||||
key: 'token',
|
||||
refreshThreshold: 300000
|
||||
},
|
||||
|
||||
publicImgPath: '/img',
|
||||
publicWebPath: '/',
|
||||
|
||||
// 单点登录配置(生产环境通过nginx代理)
|
||||
sso: {
|
||||
platformUrl: '/',
|
||||
workcaseUrl: '/workcase',
|
||||
biddingUrl: '/bidding'
|
||||
},
|
||||
|
||||
aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=',
|
||||
features: {
|
||||
enableDebug: false,
|
||||
enableMockData: false
|
||||
@@ -172,95 +135,86 @@ const prodDefaultConfig: AppRuntimeConfig = {
|
||||
// 配置加载
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 检查值是否为未替换的占位符
|
||||
*/
|
||||
const isPlaceholder = (value: any): boolean => {
|
||||
return typeof value === 'string' && value.startsWith('__') && value.endsWith('__');
|
||||
};
|
||||
|
||||
/**
|
||||
* 深度合并配置,跳过占位符值
|
||||
*/
|
||||
const mergeConfig = (target: any, source: any): any => {
|
||||
const result = { ...target };
|
||||
for (const key in source) {
|
||||
const value = source[key];
|
||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
result[key] = mergeConfig(target[key] || {}, value);
|
||||
} else if (!isPlaceholder(value)) {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取运行时配置
|
||||
* 生产环境优先从 window.APP_RUNTIME_CONFIG 读取(app-config.js 注入)
|
||||
*/
|
||||
const getRuntimeConfig = (): AppRuntimeConfig => {
|
||||
if (isDev) {
|
||||
console.log('[配置] 开发环境,使用内置配置');
|
||||
console.log('[Config] 开发环境,使用内置配置');
|
||||
return devConfig;
|
||||
}
|
||||
|
||||
// 生产环境:尝试读取外部配置
|
||||
try {
|
||||
const runtimeConfig = (window as any).APP_RUNTIME_CONFIG;
|
||||
if (runtimeConfig && typeof runtimeConfig === 'object') {
|
||||
console.log('[配置] 加载外部配置 app-config.js');
|
||||
console.log('[配置] API地址:', runtimeConfig.api?.baseUrl);
|
||||
console.log('[配置] 环境:', runtimeConfig.env || 'production');
|
||||
return runtimeConfig as AppRuntimeConfig;
|
||||
// 合并配置,未替换的占位符使用默认值
|
||||
const merged = mergeConfig(prodDefaultConfig, runtimeConfig);
|
||||
console.log('[Config] 加载运行时配置', merged);
|
||||
return merged;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[配置] 无法读取外部配置,使用默认配置', e);
|
||||
console.warn('[Config] 无法读取外部配置', e);
|
||||
}
|
||||
|
||||
console.log('[配置] 使用默认生产配置');
|
||||
console.log('[Config] 使用默认生产配置');
|
||||
return prodDefaultConfig;
|
||||
};
|
||||
|
||||
// 当前应用配置
|
||||
// 当前配置
|
||||
const config = getRuntimeConfig();
|
||||
console.log('[配置] 当前配置', config);
|
||||
|
||||
// ============================================
|
||||
// 导出配置(向后兼容)
|
||||
// 导出
|
||||
// ============================================
|
||||
|
||||
// 单独导出常用配置项
|
||||
// AES 密钥
|
||||
export const AES_SECRET_KEY = config.aesSecretKey || 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=';
|
||||
|
||||
// 常用配置项
|
||||
export const API_BASE_URL = config.api.baseUrl;
|
||||
export const FILE_DOWNLOAD_URL = config.file.downloadUrl;
|
||||
export const FILE_UPLOAD_URL = config.file.uploadUrl;
|
||||
export const PUBLIC_IMG_PATH = config.publicImgPath;
|
||||
export const PUBLIC_WEB_PATH = config.publicWebPath;
|
||||
|
||||
// 导出完整配置对象
|
||||
// 完整配置对象
|
||||
export const APP_CONFIG = {
|
||||
// 应用标题
|
||||
title: '泰豪电源 AI 数智化平台',
|
||||
name: '泰豪电源 AI 数智化平台',
|
||||
version: '1.0.0',
|
||||
copyright: '泰豪电源',
|
||||
|
||||
// 环境标识
|
||||
env: config.env || 'production',
|
||||
|
||||
// 应用基础路径
|
||||
baseUrl: config.baseUrl,
|
||||
|
||||
// API 配置
|
||||
api: {
|
||||
baseUrl: config.api.baseUrl,
|
||||
timeout: config.api.timeout
|
||||
},
|
||||
|
||||
// 文件配置
|
||||
file: {
|
||||
downloadUrl: config.file.downloadUrl,
|
||||
uploadUrl: config.file.uploadUrl,
|
||||
maxSize: config.file.maxSize,
|
||||
acceptTypes: config.file.acceptTypes
|
||||
},
|
||||
|
||||
// Token 配置
|
||||
token: {
|
||||
key: config.token.key,
|
||||
refreshThreshold: config.token.refreshThreshold
|
||||
},
|
||||
|
||||
// 公共路径
|
||||
api: config.api,
|
||||
file: config.file,
|
||||
token: config.token,
|
||||
publicImgPath: config.publicImgPath,
|
||||
publicWebPath: config.publicWebPath,
|
||||
|
||||
// 单点登录配置
|
||||
sso: config.sso || {
|
||||
platformUrl: '/',
|
||||
workcaseUrl: '/workcase',
|
||||
biddingUrl: '/bidding'
|
||||
},
|
||||
|
||||
// 功能开关
|
||||
sso: config.sso || { platformUrl: '/', workcaseUrl: '/workcase', biddingUrl: '/bidding' },
|
||||
features: config.features || {}
|
||||
};
|
||||
|
||||
// 默认导出
|
||||
export default APP_CONFIG;
|
||||
|
||||
@@ -52,7 +52,7 @@ declare module 'shared/api' {
|
||||
import type { AxiosResponse, AxiosRequestConfig } from 'axios'
|
||||
|
||||
interface ApiInstance {
|
||||
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
|
||||
get<T = any>(url: string,data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
|
||||
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
|
||||
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
|
||||
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
|
||||
@@ -61,8 +61,6 @@ declare module 'shared/api' {
|
||||
|
||||
export const api: ApiInstance
|
||||
export const TokenManager: any
|
||||
export const authAPI: any
|
||||
export const fileAPI: any
|
||||
}
|
||||
|
||||
declare module 'shared/api/auth' {
|
||||
@@ -81,26 +79,52 @@ declare module 'shared/api/ai' {
|
||||
|
||||
// ============ types模块 ==================
|
||||
declare module 'shared/types' {
|
||||
export type { BaseDTO, BaseVO } from '../../../shared/src/types/base'
|
||||
|
||||
// 基础类型
|
||||
export interface OrderField {
|
||||
field: string
|
||||
order: 'ASC' | 'DESC'
|
||||
}
|
||||
|
||||
export interface BaseDTO {
|
||||
optsn?: string
|
||||
creator?: string
|
||||
updater?: string
|
||||
deptPath?: string
|
||||
remark?: string
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
deleteTime?: string
|
||||
deleted?: boolean
|
||||
limit?: number
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
orderFields?: OrderField[]
|
||||
}
|
||||
|
||||
export interface BaseVO extends BaseDTO {
|
||||
id?: string
|
||||
creatorName?: string
|
||||
updaterName?: string
|
||||
}
|
||||
|
||||
// 重新导出 response
|
||||
export type { ResultDomain } from '../../../shared/src/types/response'
|
||||
|
||||
|
||||
// 重新导出 page
|
||||
export type { PageDomain, PageParam, PageRequest } from '../../../shared/src/types/page'
|
||||
|
||||
|
||||
// 重新导出 auth
|
||||
export type { LoginParam, LoginDomain } from '../../../shared/src/types/auth'
|
||||
|
||||
|
||||
// 重新导出 sys
|
||||
export type { SysUserVO, SysConfigVO, TbSysViewDTO } from '../../../shared/src/types/sys'
|
||||
|
||||
|
||||
// 重新导出 file
|
||||
export type { TbSysFileDTO } from '../../../shared/src/types/file'
|
||||
|
||||
|
||||
// 重新导出 ai
|
||||
export type {
|
||||
TbKnowledge,
|
||||
export type {
|
||||
TbKnowledge,
|
||||
TbKnowledgeFile,
|
||||
TbAgent,
|
||||
PromptCard,
|
||||
@@ -108,10 +132,17 @@ declare module 'shared/types' {
|
||||
TbChatMessage,
|
||||
DifyFileInfo,
|
||||
ChatPrepareData,
|
||||
StopChatParams,
|
||||
CommentMessageParams
|
||||
CreateChatParam,
|
||||
StopChatParam,
|
||||
CommentMessageParam,
|
||||
ChatListParam,
|
||||
ChatMessageListParam,
|
||||
SSEMessageData,
|
||||
SSECallbacks,
|
||||
SSETask,
|
||||
TbKnowledgeFileLog
|
||||
} from '../../../shared/src/types/ai'
|
||||
|
||||
|
||||
// 重新导出 menu
|
||||
export type { MenuItem, toMenuItem, toMenuItems } from '../../../shared/src/types/menu'
|
||||
}
|
||||
|
||||
@@ -9,80 +9,93 @@ import fs from 'fs'
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
|
||||
export default defineConfig({
|
||||
// Platform 应用的基础路径
|
||||
base: '/platform/',
|
||||
|
||||
plugins: [
|
||||
vue({
|
||||
script: {
|
||||
defineModel: true,
|
||||
propsDestructure: true
|
||||
}
|
||||
}),
|
||||
vueJsx(),
|
||||
federation({
|
||||
name: 'platform',
|
||||
remotes: {
|
||||
shared: {
|
||||
type: 'module',
|
||||
name: 'shared',
|
||||
entry: 'https://org.xyzh.yslg/shared/remoteEntry.js'
|
||||
// 开发环境 shared 模块地址
|
||||
const DEV_SHARED_URL = 'https://localhost:7000/shared/mf-manifest.json'
|
||||
// 生产环境使用相对路径,通过 Nginx 代理访问
|
||||
const PROD_SHARED_URL = '/shared/mf-manifest.json'
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const isDev = mode === 'development'
|
||||
const sharedEntry = isDev ? DEV_SHARED_URL : PROD_SHARED_URL
|
||||
|
||||
return {
|
||||
base: '/platform/',
|
||||
|
||||
plugins: [
|
||||
vue({
|
||||
script: {
|
||||
defineModel: true,
|
||||
propsDestructure: true
|
||||
}
|
||||
}),
|
||||
vueJsx(),
|
||||
federation({
|
||||
name: 'platform',
|
||||
remotes: {
|
||||
shared: {
|
||||
type: 'module',
|
||||
name: 'shared',
|
||||
entry: sharedEntry
|
||||
}
|
||||
},
|
||||
shared: {
|
||||
vue: {},
|
||||
'vue-router': {},
|
||||
'element-plus': {},
|
||||
axios: {}
|
||||
}
|
||||
})
|
||||
],
|
||||
|
||||
define: {
|
||||
__VUE_OPTIONS_API__: true,
|
||||
__VUE_PROD_DEVTOOLS__: true,
|
||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true
|
||||
},
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src')
|
||||
}
|
||||
},
|
||||
|
||||
server: {
|
||||
port: 7001,
|
||||
host: true,
|
||||
cors: true,
|
||||
open: '/',
|
||||
https: (() => {
|
||||
try {
|
||||
return {
|
||||
key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'),
|
||||
cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem')
|
||||
}
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
})(),
|
||||
hmr: {
|
||||
path: '/@vite/client',
|
||||
port: 7001
|
||||
},
|
||||
shared: {
|
||||
vue: {},
|
||||
'vue-router': {},
|
||||
'element-plus': {},
|
||||
axios: {}
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8180',
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/api/, '')
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
|
||||
define: {
|
||||
__VUE_OPTIONS_API__: true,
|
||||
__VUE_PROD_DEVTOOLS__: true,
|
||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true
|
||||
},
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src')
|
||||
}
|
||||
},
|
||||
|
||||
server: {
|
||||
port: 7001,
|
||||
host: true,
|
||||
cors: true,
|
||||
open: '/', // 开发时自动打开到根路径
|
||||
// HTTPS 配置(使用 mkcert 生成的本地开发证书)
|
||||
https: {
|
||||
key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'),
|
||||
cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem')
|
||||
},
|
||||
hmr: {
|
||||
// 修复 base 路径导致的 WebSocket 连接问题
|
||||
path: '/@vite/client',
|
||||
port: 7001
|
||||
},
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8180',
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/api/, '')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
'vue-vendor': ['vue', 'vue-router', 'pinia'],
|
||||
'element-plus': ['element-plus']
|
||||
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
'vue-vendor': ['vue', 'vue-router', 'pinia'],
|
||||
'element-plus': ['element-plus']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
/**
|
||||
* @description 应用运行时配置文件
|
||||
* @author yslg
|
||||
* @since 2025-12-06
|
||||
* @description 应用运行时配置文件 (支持 Docker 环境变量替换)
|
||||
*
|
||||
* 说明:
|
||||
* 1. 此文件在生产环境中被加载,用于覆盖内置配置
|
||||
* 2. Docker 部署时,可通过挂载此文件来修改配置,无需重新构建
|
||||
* 3. 配置结构必须与 packages/shared/src/config/index.ts 中的 AppRuntimeConfig 保持一致
|
||||
* 占位符说明:
|
||||
* - __PLACEHOLDER__ 格式的值会在 Docker 启动时被环境变量替换
|
||||
* - 如果环境变量未设置,将使用默认值
|
||||
*
|
||||
* 使用示例(Docker):
|
||||
* docker run -v /path/to/app-config.js:/app/public/app-config.js my-app:latest
|
||||
* Docker 部署:
|
||||
* 1. 通过 volume 挂载覆盖此文件
|
||||
* 2. 或通过启动脚本替换占位符
|
||||
*/
|
||||
|
||||
window.APP_RUNTIME_CONFIG = {
|
||||
// 环境标识
|
||||
env: 'production',
|
||||
env: '__APP_ENV__',
|
||||
|
||||
// API 配置
|
||||
api: {
|
||||
baseUrl: '/api',
|
||||
baseUrl: '__API_BASE_URL__',
|
||||
timeout: 30000
|
||||
},
|
||||
|
||||
@@ -27,12 +25,12 @@ window.APP_RUNTIME_CONFIG = {
|
||||
|
||||
// 文件配置
|
||||
file: {
|
||||
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||
downloadUrl: '__API_BASE_URL__/urban-lifeline/file/download/',
|
||||
uploadUrl: '__API_BASE_URL__/urban-lifeline/file/upload',
|
||||
maxSize: {
|
||||
image: 5, // MB
|
||||
video: 100, // MB
|
||||
document: 10 // MB
|
||||
image: 5,
|
||||
video: 100,
|
||||
document: 10
|
||||
},
|
||||
acceptTypes: {
|
||||
image: 'image/*',
|
||||
@@ -44,18 +42,26 @@ window.APP_RUNTIME_CONFIG = {
|
||||
// Token 配置
|
||||
token: {
|
||||
key: 'token',
|
||||
refreshThreshold: 300000 // 5分钟
|
||||
refreshThreshold: 300000
|
||||
},
|
||||
|
||||
// 公共资源路径
|
||||
publicImgPath: '/img',
|
||||
publicWebPath: '/',
|
||||
publicImgPath: '__PUBLIC_PATH__/img',
|
||||
publicWebPath: '__PUBLIC_PATH__',
|
||||
|
||||
// 单点登录配置
|
||||
sso: {
|
||||
platformUrl: '/', // platform 平台地址
|
||||
workcaseUrl: '/workcase', // workcase 服务地址
|
||||
biddingUrl: '/bidding' // bidding 服务地址
|
||||
platformUrl: '__SSO_PLATFORM_URL__',
|
||||
workcaseUrl: '__SSO_WORKCASE_URL__',
|
||||
biddingUrl: '__SSO_BIDDING_URL__'
|
||||
},
|
||||
|
||||
// AES 加密密钥
|
||||
aesSecretKey: '__AES_SECRET_KEY__',
|
||||
|
||||
// Jitsi 视频会议配置
|
||||
jitsi: {
|
||||
serverUrl: '__JITSI_SERVER_URL__'
|
||||
},
|
||||
|
||||
// 功能开关
|
||||
|
||||
@@ -1,28 +1,12 @@
|
||||
/**
|
||||
* @description 应用运行时配置
|
||||
* @author yslg
|
||||
* @since 2025-12-06
|
||||
*
|
||||
* 配置加载策略:
|
||||
* 1. 开发环境:使用下面定义的开发配置
|
||||
* 1. 开发环境:使用内置开发配置
|
||||
* 2. 生产环境:从 window.APP_RUNTIME_CONFIG 读取(来自 app-config.js)
|
||||
* 3. Docker部署:替换 app-config.js 文件实现配置外挂
|
||||
*
|
||||
* 配置结构说明:
|
||||
* 此文件的配置结构与 app-config.js 完全对应
|
||||
* 修改 app-config.js 后,这里的配置会自动应用
|
||||
* 3. Docker部署:启动时替换 app-config.js 中的占位符
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// AES 加密密钥
|
||||
// ============================================
|
||||
/**
|
||||
* AES 加密密钥(与后端保持一致)
|
||||
* 对应后端配置:security.aes.secret-key
|
||||
* Base64 编码的 32 字节密钥(256 位)
|
||||
*/
|
||||
export const AES_SECRET_KEY = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=' // Base64 编码,解码后是 "12345678901234567890123456789012" (32字节)
|
||||
export const AGENT_ID = '17664699513920001'
|
||||
// ============================================
|
||||
// 类型定义
|
||||
// ============================================
|
||||
@@ -53,11 +37,14 @@ export interface AppRuntimeConfig {
|
||||
};
|
||||
publicImgPath: string;
|
||||
publicWebPath: string;
|
||||
// 单点登录配置
|
||||
sso?: {
|
||||
platformUrl: string; // platform 平台地址
|
||||
workcaseUrl: string; // workcase 服务地址
|
||||
biddingUrl: string; // bidding 服务地址
|
||||
platformUrl: string;
|
||||
workcaseUrl: string;
|
||||
biddingUrl: string;
|
||||
};
|
||||
aesSecretKey?: string;
|
||||
jitsi?: {
|
||||
serverUrl: string;
|
||||
};
|
||||
features?: {
|
||||
enableDebug?: boolean;
|
||||
@@ -67,103 +54,86 @@ export interface AppRuntimeConfig {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 配置定义(与 app-config.js 结构一致)
|
||||
// 环境检测
|
||||
// ============================================
|
||||
const isDev = (import.meta as any).env?.DEV ?? false;
|
||||
|
||||
// ============================================
|
||||
// 开发环境配置
|
||||
// ============================================
|
||||
const devConfig: AppRuntimeConfig = {
|
||||
env: 'development',
|
||||
|
||||
api: {
|
||||
// 开发环境通过 Vite 代理转发到后端,避免浏览器直接跨域
|
||||
// 实际请求路径示例:/api/... → 由代理转发到实际后端
|
||||
baseUrl: '/api',
|
||||
timeout: 30000
|
||||
},
|
||||
|
||||
baseUrl: '/',
|
||||
|
||||
file: {
|
||||
// 通过 Nginx → Gateway 访问文件服务,使用 /urban-lifeline 前缀
|
||||
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||
maxSize: {
|
||||
image: 5,
|
||||
video: 100,
|
||||
document: 10
|
||||
},
|
||||
maxSize: { image: 5, video: 100, document: 10 },
|
||||
acceptTypes: {
|
||||
image: 'image/*',
|
||||
video: 'video/*',
|
||||
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
|
||||
}
|
||||
},
|
||||
|
||||
token: {
|
||||
key: 'token',
|
||||
refreshThreshold: 300000
|
||||
},
|
||||
|
||||
publicImgPath: 'http://localhost:5173/img',
|
||||
publicWebPath: 'http://localhost:5173',
|
||||
|
||||
// 单点登录配置
|
||||
// 推荐:开发环境也通过nginx访问(http://localhost)
|
||||
// 备选:直接访问各服务端口(platformUrl: 'http://localhost:7001')
|
||||
publicImgPath: '/img',
|
||||
publicWebPath: '/',
|
||||
sso: {
|
||||
platformUrl: '/', // 通过nginx访问platform
|
||||
workcaseUrl: '/workcase', // 通过nginx访问workcase
|
||||
biddingUrl: '/bidding' // 通过nginx访问bidding
|
||||
platformUrl: '/',
|
||||
workcaseUrl: '/workcase',
|
||||
biddingUrl: '/bidding'
|
||||
},
|
||||
aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=',
|
||||
jitsi: {
|
||||
serverUrl: 'https://meet.example.com'
|
||||
},
|
||||
|
||||
features: {
|
||||
enableDebug: true,
|
||||
enableMockData: false
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// 生产环境默认配置(兜底)
|
||||
// ============================================
|
||||
const prodDefaultConfig: AppRuntimeConfig = {
|
||||
env: 'production',
|
||||
|
||||
api: {
|
||||
baseUrl: '/api',
|
||||
timeout: 30000
|
||||
},
|
||||
|
||||
baseUrl: '/',
|
||||
|
||||
file: {
|
||||
downloadUrl: '/api/file/download/',
|
||||
uploadUrl: '/api/file/upload',
|
||||
maxSize: {
|
||||
image: 5,
|
||||
video: 100,
|
||||
document: 10
|
||||
},
|
||||
downloadUrl: '/api/urban-lifeline/file/download/',
|
||||
uploadUrl: '/api/urban-lifeline/file/upload',
|
||||
maxSize: { image: 5, video: 100, document: 10 },
|
||||
acceptTypes: {
|
||||
image: 'image/*',
|
||||
video: 'video/*',
|
||||
document: '.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx'
|
||||
}
|
||||
},
|
||||
|
||||
token: {
|
||||
key: 'token',
|
||||
refreshThreshold: 300000
|
||||
},
|
||||
|
||||
publicImgPath: '/img',
|
||||
publicWebPath: '/',
|
||||
|
||||
// 单点登录配置(生产环境通过nginx代理)
|
||||
sso: {
|
||||
platformUrl: '/',
|
||||
workcaseUrl: '/workcase',
|
||||
biddingUrl: '/bidding'
|
||||
},
|
||||
|
||||
aesSecretKey: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=',
|
||||
jitsi: {
|
||||
serverUrl: 'https://meet.example.com'
|
||||
},
|
||||
features: {
|
||||
enableDebug: false,
|
||||
enableMockData: false
|
||||
@@ -174,96 +144,90 @@ const prodDefaultConfig: AppRuntimeConfig = {
|
||||
// 配置加载
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 检查值是否为未替换的占位符
|
||||
*/
|
||||
const isPlaceholder = (value: any): boolean => {
|
||||
return typeof value === 'string' && value.startsWith('__') && value.endsWith('__');
|
||||
};
|
||||
|
||||
/**
|
||||
* 深度合并配置,跳过占位符值
|
||||
*/
|
||||
const mergeConfig = (target: any, source: any): any => {
|
||||
const result = { ...target };
|
||||
for (const key in source) {
|
||||
const value = source[key];
|
||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
result[key] = mergeConfig(target[key] || {}, value);
|
||||
} else if (!isPlaceholder(value)) {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取运行时配置
|
||||
* 生产环境优先从 window.APP_RUNTIME_CONFIG 读取(app-config.js 注入)
|
||||
*/
|
||||
const getRuntimeConfig = (): AppRuntimeConfig => {
|
||||
if (isDev) {
|
||||
console.log('[配置] 开发环境,使用内置配置');
|
||||
console.log('[Config] 开发环境,使用内置配置');
|
||||
return devConfig;
|
||||
}
|
||||
|
||||
// 生产环境:尝试读取外部配置
|
||||
try {
|
||||
const runtimeConfig = (window as any).APP_RUNTIME_CONFIG;
|
||||
if (runtimeConfig && typeof runtimeConfig === 'object') {
|
||||
console.log('[配置] 加载外部配置 app-config.js');
|
||||
console.log('[配置] API地址:', runtimeConfig.api?.baseUrl);
|
||||
console.log('[配置] 环境:', runtimeConfig.env || 'production');
|
||||
return runtimeConfig as AppRuntimeConfig;
|
||||
const merged = mergeConfig(prodDefaultConfig, runtimeConfig);
|
||||
console.log('[Config] 加载运行时配置', merged);
|
||||
return merged;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[配置] 无法读取外部配置,使用默认配置', e);
|
||||
console.warn('[Config] 无法读取外部配置', e);
|
||||
}
|
||||
|
||||
console.log('[配置] 使用默认生产配置');
|
||||
console.log('[Config] 使用默认生产配置');
|
||||
return prodDefaultConfig;
|
||||
};
|
||||
|
||||
// 当前应用配置
|
||||
// 当前配置
|
||||
const config = getRuntimeConfig();
|
||||
console.log('[配置] 当前配置', config);
|
||||
|
||||
// ============================================
|
||||
// 导出配置(向后兼容)
|
||||
// 导出
|
||||
// ============================================
|
||||
|
||||
// 单独导出常用配置项
|
||||
// AES 密钥
|
||||
export const AES_SECRET_KEY = config.aesSecretKey || 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=';
|
||||
|
||||
// AI Agent ID
|
||||
export const AGENT_ID = '17664699513920001';
|
||||
|
||||
// 常用配置项
|
||||
export const API_BASE_URL = config.api.baseUrl;
|
||||
export const FILE_DOWNLOAD_URL = config.file.downloadUrl;
|
||||
export const FILE_UPLOAD_URL = config.file.uploadUrl;
|
||||
export const PUBLIC_IMG_PATH = config.publicImgPath;
|
||||
export const PUBLIC_WEB_PATH = config.publicWebPath;
|
||||
|
||||
// 文件上传大小限制(100MB)
|
||||
export const FILE_MAX_SIZE = 100 * 1024 * 1024;
|
||||
|
||||
// 导出完整配置对象
|
||||
// Jitsi 配置
|
||||
export const JITSI_SERVER_URL = config.jitsi?.serverUrl || 'https://meet.example.com';
|
||||
|
||||
// 完整配置对象
|
||||
export const APP_CONFIG = {
|
||||
// 应用标题
|
||||
title: '泰豪电源 AI 数智化平台',
|
||||
|
||||
// 环境标识
|
||||
title: '泰豪电源工单系统',
|
||||
env: config.env || 'production',
|
||||
|
||||
// 应用基础路径
|
||||
baseUrl: config.baseUrl,
|
||||
|
||||
// API 配置
|
||||
api: {
|
||||
baseUrl: config.api.baseUrl,
|
||||
timeout: config.api.timeout
|
||||
},
|
||||
|
||||
// 文件配置
|
||||
file: {
|
||||
downloadUrl: config.file.downloadUrl,
|
||||
uploadUrl: config.file.uploadUrl,
|
||||
maxSize: config.file.maxSize,
|
||||
acceptTypes: config.file.acceptTypes
|
||||
},
|
||||
|
||||
// Token 配置
|
||||
token: {
|
||||
key: config.token.key,
|
||||
refreshThreshold: config.token.refreshThreshold
|
||||
},
|
||||
|
||||
// 公共路径
|
||||
api: config.api,
|
||||
file: config.file,
|
||||
token: config.token,
|
||||
publicImgPath: config.publicImgPath,
|
||||
publicWebPath: config.publicWebPath,
|
||||
|
||||
// 单点登录配置
|
||||
sso: config.sso || {
|
||||
platformUrl: '/',
|
||||
workcaseUrl: '/workcase',
|
||||
biddingUrl: '/bidding'
|
||||
},
|
||||
|
||||
// 功能开关
|
||||
sso: config.sso || { platformUrl: '/', workcaseUrl: '/workcase', biddingUrl: '/bidding' },
|
||||
jitsi: config.jitsi || { serverUrl: 'https://meet.example.com' },
|
||||
features: config.features || {}
|
||||
};
|
||||
|
||||
// 默认导出
|
||||
export default APP_CONFIG;
|
||||
|
||||
@@ -9,84 +9,97 @@ import fs from 'fs'
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
|
||||
export default defineConfig(({ mode }) => ({
|
||||
// 开发和生产环境都通过nginx代理访问/workcase
|
||||
base: '/workcase/',
|
||||
|
||||
plugins: [
|
||||
vue({
|
||||
script: {
|
||||
defineModel: true,
|
||||
propsDestructure: true
|
||||
}
|
||||
}),
|
||||
vueJsx(),
|
||||
federation({
|
||||
name: 'workcase',
|
||||
remotes: {
|
||||
shared: {
|
||||
type: 'module',
|
||||
name: 'shared',
|
||||
entry: 'https://org.xyzh.yslg/shared/remoteEntry.js'
|
||||
// 开发环境 shared 模块地址
|
||||
const DEV_SHARED_URL = 'https://localhost:7000/shared/mf-manifest.json'
|
||||
// 生产环境使用相对路径,通过 Nginx 代理访问
|
||||
const PROD_SHARED_URL = '/shared/mf-manifest.json'
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const isDev = mode === 'development'
|
||||
const sharedEntry = isDev ? DEV_SHARED_URL : PROD_SHARED_URL
|
||||
|
||||
return {
|
||||
base: '/workcase/',
|
||||
|
||||
plugins: [
|
||||
vue({
|
||||
script: {
|
||||
defineModel: true,
|
||||
propsDestructure: true
|
||||
}
|
||||
}),
|
||||
vueJsx(),
|
||||
federation({
|
||||
name: 'workcase',
|
||||
remotes: {
|
||||
shared: {
|
||||
type: 'module',
|
||||
name: 'shared',
|
||||
entry: sharedEntry
|
||||
}
|
||||
},
|
||||
shared: {
|
||||
vue: {},
|
||||
'vue-router': {},
|
||||
'element-plus': {},
|
||||
axios: {}
|
||||
}
|
||||
})
|
||||
],
|
||||
|
||||
define: {
|
||||
__VUE_OPTIONS_API__: true,
|
||||
__VUE_PROD_DEVTOOLS__: true,
|
||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true,
|
||||
global: 'globalThis'
|
||||
},
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src')
|
||||
}
|
||||
},
|
||||
|
||||
server: {
|
||||
port: 7003,
|
||||
host: true,
|
||||
cors: true,
|
||||
open: '/workcase/',
|
||||
https: (() => {
|
||||
try {
|
||||
return {
|
||||
key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'),
|
||||
cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem')
|
||||
}
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
})(),
|
||||
hmr: {
|
||||
path: '/@vite/client',
|
||||
port: 7003
|
||||
},
|
||||
shared: {
|
||||
vue: {},
|
||||
'vue-router': {},
|
||||
'element-plus': {},
|
||||
axios: {}
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8180',
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
rewrite: (path: string) => path.replace(/^\/api/, '')
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
|
||||
define: {
|
||||
__VUE_OPTIONS_API__: true,
|
||||
__VUE_PROD_DEVTOOLS__: true,
|
||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true,
|
||||
global: 'globalThis'
|
||||
},
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src')
|
||||
}
|
||||
},
|
||||
|
||||
server: {
|
||||
port: 7003,
|
||||
host: true,
|
||||
cors: true,
|
||||
open: '/workcase/', // 开发时自动打开到 /workcase/ 路径
|
||||
// HTTPS 配置(使用 mkcert 生成的本地开发证书)
|
||||
https: {
|
||||
key: fs.readFileSync('C:/Users/FK05/443/localhost+3-key.pem'),
|
||||
cert: fs.readFileSync('C:/Users/FK05/443/localhost+3.pem')
|
||||
},
|
||||
hmr: {
|
||||
// 修复 base 路径导致的 WebSocket 连接问题
|
||||
path: '/@vite/client',
|
||||
port: 7003
|
||||
},
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8180',
|
||||
changeOrigin: true,
|
||||
ws: true, // 启用 WebSocket 代理
|
||||
rewrite: (path: string) => path.replace(/^\/api/, '')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
'vue-vendor': ['vue', 'vue-router', 'pinia'],
|
||||
'element-plus': ['element-plus']
|
||||
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
'vue-vendor': ['vue', 'vue-router', 'pinia'],
|
||||
'element-plus': ['element-plus']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user