- 新建 /asset-center/packaging-materials 独立页面,三栏布局完整实现 - 左栏: 一级分类树(5类) + 二级品类列表 + 新建品类 - 中栏: 包材档案列表,支持搜索、状态筛选、复制新增、启停 - 右栏: 详情预览/编辑面板,按品类动态渲染模板字段 - 资产中心包装材料库改为可点击卡片,跳转至独立页面
264 lines
6.5 KiB
JavaScript
264 lines
6.5 KiB
JavaScript
/** @type {import('next').NextConfig} */
|
||
const isProduction = process.env.NODE_ENV === 'production'
|
||
const defaultAllowedDevOrigins = ['localhost', '127.0.0.1']
|
||
const extraAllowedDevOrigins = (process.env.NEXT_ALLOWED_DEV_ORIGINS || '')
|
||
.split(',')
|
||
.map((origin) => origin.trim())
|
||
.filter(Boolean)
|
||
const allowedDevOrigins = Array.from(new Set([...defaultAllowedDevOrigins, ...extraAllowedDevOrigins]))
|
||
|
||
const nextConfig = {
|
||
// 启用standalone输出,用于Docker部署
|
||
output: 'standalone',
|
||
|
||
// 启用构建时类型检查
|
||
typescript: {
|
||
ignoreBuildErrors: false,
|
||
},
|
||
|
||
// 服务器外部包配置(Next.js 16新配置)
|
||
serverExternalPackages: ['sharp', 'three', 'gsap'],
|
||
|
||
// Turbopack配置(Next.js 16默认启用)
|
||
// 注意:worktree 中 node_modules 为 symlink/junction,Turbopack 不支持,
|
||
// 本地构建暂时注释;部署时在独立 clone 的目录内构建可以启用
|
||
// turbopack: {},
|
||
|
||
// 本地开发允许 localhost 和 127.0.0.1 复用同一套 dev 资源,避免 UI 改动只在其中一个地址生效
|
||
allowedDevOrigins,
|
||
|
||
// 图像优化配置
|
||
images: {
|
||
remotePatterns: [
|
||
{
|
||
protocol: 'http',
|
||
hostname: 'localhost',
|
||
port: '9000',
|
||
pathname: '/**',
|
||
},
|
||
{
|
||
protocol: 'http',
|
||
hostname: 'minio',
|
||
port: '9000',
|
||
pathname: '/**',
|
||
},
|
||
{
|
||
protocol: 'https',
|
||
hostname: 'threejs.org',
|
||
pathname: '/**',
|
||
}
|
||
],
|
||
unoptimized: true, // 在容器环境中禁用优化以提高性能
|
||
formats: ['image/webp', 'image/avif']
|
||
},
|
||
|
||
// 静态资源配置
|
||
assetPrefix: process.env.NODE_ENV === 'production' ? undefined : undefined,
|
||
|
||
// 压缩配置
|
||
compress: true,
|
||
|
||
// 性能优化
|
||
poweredByHeader: false,
|
||
generateEtags: false,
|
||
|
||
// Webpack配置
|
||
webpack: (config, { dev, isServer }) => {
|
||
// 支持3D文件格式
|
||
config.module.rules.push({
|
||
test: /\.(glb|gltf|fbx|obj|dae)$/,
|
||
type: 'asset/resource',
|
||
generator: {
|
||
filename: 'static/models/[name].[hash][ext]'
|
||
}
|
||
})
|
||
|
||
// 支持音频文件(用于3D场景)
|
||
config.module.rules.push({
|
||
test: /\.(mp3|wav|ogg)$/,
|
||
type: 'asset/resource',
|
||
generator: {
|
||
filename: 'static/audio/[name].[hash][ext]'
|
||
}
|
||
})
|
||
|
||
// 支持着色器文件
|
||
config.module.rules.push({
|
||
test: /\.(glsl|vs|fs|vert|frag)$/,
|
||
type: 'asset/source'
|
||
})
|
||
|
||
// 优化Three.js打包
|
||
if (!isServer) {
|
||
config.resolve.fallback = {
|
||
...config.resolve.fallback,
|
||
fs: false,
|
||
path: false,
|
||
crypto: false
|
||
}
|
||
}
|
||
|
||
// 生产环境优化
|
||
if (!dev) {
|
||
// 启用代码分割
|
||
config.optimization.splitChunks = {
|
||
chunks: 'all',
|
||
cacheGroups: {
|
||
// Three.js单独打包
|
||
three: {
|
||
test: /[\\/]node_modules[\\/](three|@three)[\\/]/,
|
||
name: 'three',
|
||
chunks: 'all',
|
||
priority: 10
|
||
},
|
||
// GSAP单独打包
|
||
gsap: {
|
||
test: /[\\/]node_modules[\\/]gsap[\\/]/,
|
||
name: 'gsap',
|
||
chunks: 'all',
|
||
priority: 10
|
||
},
|
||
// 其他vendor库
|
||
vendor: {
|
||
test: /[\\/]node_modules[\\/]/,
|
||
name: 'vendors',
|
||
chunks: 'all',
|
||
priority: 5
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return config
|
||
},
|
||
|
||
// 环境变量配置
|
||
env: {
|
||
CUSTOM_KEY: process.env.CUSTOM_KEY,
|
||
},
|
||
|
||
// 重定向配置
|
||
async redirects() {
|
||
const redirects = [
|
||
...(!isProduction
|
||
? [
|
||
{
|
||
source: '/:path*',
|
||
has: [
|
||
{
|
||
type: 'host',
|
||
value: '127.0.0.1',
|
||
},
|
||
],
|
||
destination: 'http://localhost:3000/:path*',
|
||
permanent: false,
|
||
},
|
||
]
|
||
: []),
|
||
// 将根路径重定向到主页
|
||
{
|
||
source: '/home',
|
||
destination: '/',
|
||
permanent: true,
|
||
},
|
||
]
|
||
|
||
return redirects
|
||
},
|
||
|
||
// 头部配置(安全性和性能)
|
||
async headers() {
|
||
return [
|
||
{
|
||
source: '/(.*)',
|
||
headers: [
|
||
// 安全头部
|
||
{
|
||
key: 'X-Frame-Options',
|
||
value: 'DENY'
|
||
},
|
||
{
|
||
key: 'X-Content-Type-Options',
|
||
value: 'nosniff'
|
||
},
|
||
{
|
||
key: 'Referrer-Policy',
|
||
value: 'origin-when-cross-origin'
|
||
}
|
||
],
|
||
},
|
||
{
|
||
// Next 静态资源在生产环境可长期缓存;开发环境必须禁用缓存,否则浏览器会继续复用旧 bundle。
|
||
source: '/_next/static/:path*',
|
||
headers: [
|
||
{
|
||
key: 'Cache-Control',
|
||
value: isProduction
|
||
? 'public, max-age=31536000, immutable'
|
||
: 'no-store, max-age=0, must-revalidate'
|
||
}
|
||
],
|
||
},
|
||
{
|
||
// Draco 解码器文件可长期缓存(静态文件)
|
||
source: '/draco/:path*',
|
||
headers: [
|
||
{
|
||
key: 'Cache-Control',
|
||
value: 'public, max-age=31536000, immutable'
|
||
}
|
||
],
|
||
},
|
||
{
|
||
// 上传资源(模型/图片等)属于静态文件,但文件名未必带 hash,使用较保守的缓存策略
|
||
source: '/uploads/:path*',
|
||
headers: [
|
||
{
|
||
key: 'Cache-Control',
|
||
value: 'public, max-age=86400'
|
||
}
|
||
],
|
||
},
|
||
{
|
||
// 3D模型文件特殊缓存策略
|
||
source: '/models/:path*',
|
||
headers: [
|
||
{
|
||
key: 'Cache-Control',
|
||
value: 'public, max-age=86400, s-maxage=31536000'
|
||
},
|
||
{
|
||
key: 'Access-Control-Allow-Origin',
|
||
value: '*'
|
||
}
|
||
],
|
||
},
|
||
{
|
||
// API路由CORS配置
|
||
source: '/api/:path*',
|
||
headers: [
|
||
// API 返回的是动态数据,必须禁用缓存,否则会出现“恢复/删除后列表不更新”等问题
|
||
{
|
||
key: 'Cache-Control',
|
||
value: 'no-store'
|
||
},
|
||
{
|
||
key: 'Access-Control-Allow-Origin',
|
||
value: '*'
|
||
},
|
||
{
|
||
key: 'Access-Control-Allow-Methods',
|
||
value: 'GET, POST, PUT, DELETE, OPTIONS'
|
||
},
|
||
{
|
||
key: 'Access-Control-Allow-Headers',
|
||
value: 'Content-Type, Authorization'
|
||
}
|
||
],
|
||
}
|
||
]
|
||
}
|
||
}
|
||
|
||
module.exports = nextConfig
|