diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..aef8443
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "liveServer.settings.port": 5501
+}
\ No newline at end of file
diff --git a/admin-backend/src/main/java/com/admin/config/SecurityConfig.java b/admin-backend/src/main/java/com/admin/config/SecurityConfig.java
index ff44469..64fd8f8 100644
--- a/admin-backend/src/main/java/com/admin/config/SecurityConfig.java
+++ b/admin-backend/src/main/java/com/admin/config/SecurityConfig.java
@@ -79,6 +79,12 @@ public class SecurityConfig {
.antMatchers("/static/**", "/favicon.ico").permitAll()
// 其他请求需要认证
.anyRequest().authenticated())
+
+ // 允许iframe嵌入
+ .headers(headers -> headers
+ .frameOptions(frame -> frame.disable())
+ .contentTypeOptions(contentType -> contentType.disable())
+ .httpStrictTransportSecurity(hsts -> hsts.disable()))
// 认证提供者
.authenticationProvider(authenticationProvider())
diff --git a/admin-backend/src/main/java/com/admin/config/SwaggerFrameOptionsFilter.java b/admin-backend/src/main/java/com/admin/config/SwaggerFrameOptionsFilter.java
new file mode 100644
index 0000000..e5855e5
--- /dev/null
+++ b/admin-backend/src/main/java/com/admin/config/SwaggerFrameOptionsFilter.java
@@ -0,0 +1,42 @@
+package com.admin.config;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Component
+@Order(Integer.MIN_VALUE) // 最高优先级
+public class SwaggerFrameOptionsFilter extends OncePerRequestFilter {
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
+ FilterChain filterChain) throws ServletException, IOException {
+
+ // 执行过滤器链
+ filterChain.doFilter(request, response);
+
+ // 如果是Swagger相关的请求,强制覆盖响应头
+ String requestURI = request.getRequestURI();
+ if (requestURI != null && (
+ requestURI.contains("/swagger-ui") ||
+ requestURI.contains("/v3/api-docs") ||
+ requestURI.contains("/swagger-ui.html")
+ )) {
+ // 强制移除X-Frame-Options限制
+ response.setHeader("X-Frame-Options", "SAMEORIGIN");
+ response.setHeader("Content-Security-Policy", "frame-ancestors 'self' *");
+ response.setHeader("Access-Control-Allow-Origin", "*");
+ response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
+ response.setHeader("Access-Control-Allow-Headers", "*");
+
+ // 确保这些头被正确设置
+ response.setHeader("X-Frame-Options", "SAMEORIGIN");
+ }
+ }
+}
diff --git a/admin-frontend/.env b/admin-frontend/.env
index 5d8c946..2f309f1 100644
--- a/admin-frontend/.env
+++ b/admin-frontend/.env
@@ -1,2 +1,2 @@
# 通用环境变量
-VITE_APP_TITLE=Admin Management System
\ No newline at end of file
+VITE_APP_TITLE=AI后台管理
\ No newline at end of file
diff --git a/admin-frontend/components.d.ts b/admin-frontend/components.d.ts
index 1146eb7..c632f90 100644
--- a/admin-frontend/components.d.ts
+++ b/admin-frontend/components.d.ts
@@ -19,11 +19,15 @@ declare module 'vue' {
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol']
+ ElCollapse: typeof import('element-plus/es')['ElCollapse']
+ ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDialog: typeof import('element-plus/es')['ElDialog']
+ ElDivider: typeof import('element-plus/es')['ElDivider']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
+ ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
@@ -41,6 +45,7 @@ declare module 'vue' {
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
+ ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
diff --git a/admin-frontend/src/layout/LayoutView.vue b/admin-frontend/src/layout/LayoutView.vue
index d0f3568..2a6f2e0 100644
--- a/admin-frontend/src/layout/LayoutView.vue
+++ b/admin-frontend/src/layout/LayoutView.vue
@@ -115,27 +115,22 @@
\ No newline at end of file
diff --git a/admin-frontend/vite.config.ts b/admin-frontend/vite.config.ts
index 5ef4307..92ede31 100644
--- a/admin-frontend/vite.config.ts
+++ b/admin-frontend/vite.config.ts
@@ -7,17 +7,18 @@ import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vite.dev/config/
-export default defineConfig({
+export default defineConfig(({ mode }) => ({
plugins: [
vue(),
- vueDevTools(),
+ // 仅在开发环境启用 DevTools,避免对生产构建的注入影响打包产物
+ mode === 'development' && vueDevTools(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
- ],
+ ].filter(Boolean),
server: {
port: 3000,
proxy: {
@@ -28,28 +29,12 @@ export default defineConfig({
}
},
build: {
- // 确保构建时使用相对路径
+ // 使用相对路径,便于在静态服务器(如 Live Server)直接打开 dist
assetsDir: 'assets',
- rollupOptions: {
- output: {
- // 修复循环引用问题的分包策略
- manualChunks(id) {
- if (id.includes('node_modules')) {
- if (id.includes('element-plus')) {
- return 'element-plus'
- }
- if (id.includes('vue')) {
- return 'vue'
- }
- if (id.includes('@element-plus/icons-vue')) {
- return 'element-icons'
- }
- // 其他第三方库
- return 'vendor'
- }
- }
- }
- }
+ // 交给 Vite/Rollup 自己做最佳分包,避免对 Vue 这类存在循环依赖的包强制分块
+ // 若线上仍遇到压缩器相关问题,可将 minify 切为 'terser'
+ // minify: 'terser',
+ sourcemap: false,
},
// 设置基础路径为相对路径,适用于静态文件服务器
base: './',
@@ -58,4 +43,4 @@ export default defineConfig({
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
-})
+}))