feat: 重构后台路由并使用嵌套路由;添加图片上传功能

- 拆分 AdminView 为多个子页面组件,使用嵌套路由
- 拆分 MerchantView 为多个子页面组件,使用嵌套路由
- 创建 AdminLayout 和 MerchantLayout 布局组件
- 修复编译错误:IconSend 重复导入、IconDatabase 不存在
- 修复 HomeView 缺失 onMounted 导入
- 添加文件上传后端接口和静态资源映射
- 为商品和轮播图添加图片上传功能(支持预览、清除)
This commit is contained in:
wangziqi
2026-02-10 15:14:23 +08:00
parent a7ce0a089e
commit d6451cf188
29 changed files with 4948 additions and 479 deletions

View File

@@ -1,25 +1,37 @@
package com.maternalmall.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final AuthInterceptor authInterceptor;
@Value("${app.upload-path:./uploads}")
private String uploadPath;
public WebMvcConfig(AuthInterceptor authInterceptor) {
this.authInterceptor = authInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor).addPathPatterns("/**");
registry.addInterceptor(authInterceptor).addPathPatterns("/**")
.excludePathPatterns("/uploads/**");
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("*").allowedHeaders("*");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/uploads/**")
.addResourceLocations("file:" + uploadPath + "/");
}
}

View File

@@ -0,0 +1,73 @@
package com.maternalmall.controller;
import com.maternalmall.common.ApiResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
@RestController
@RequestMapping("/api")
public class FileUploadController {
@Value("${app.upload-path:./uploads}")
private String uploadPath;
@Value("${app.upload-url-prefix:/uploads}")
private String uploadUrlPrefix;
@PostMapping("/upload")
public ApiResponse<String> uploadFile(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ApiResponse.error("请选择要上传的文件");
}
try {
// 创建上传目录
Path uploadDir = Paths.get(uploadPath);
if (!Files.exists(uploadDir)) {
Files.createDirectories(uploadDir);
}
// 生成唯一文件名
String originalFilename = file.getOriginalFilename();
String extension = "";
if (originalFilename != null && originalFilename.contains(".")) {
extension = originalFilename.substring(originalFilename.lastIndexOf("."));
}
String newFilename = UUID.randomUUID().toString().replace("-", "") + extension;
// 保存文件
Path filePath = uploadDir.resolve(newFilename);
file.transferTo(filePath.toFile());
// 返回文件URL
String fileUrl = uploadUrlPrefix + "/" + newFilename;
return ApiResponse.success(fileUrl);
} catch (IOException e) {
return ApiResponse.error("文件上传失败: " + e.getMessage());
}
}
@PostMapping("/upload/image")
public ApiResponse<String> uploadImage(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ApiResponse.error("请选择要上传的图片");
}
// 检查文件类型
String contentType = file.getContentType();
if (contentType == null || !contentType.startsWith("image/")) {
return ApiResponse.error("只能上传图片文件");
}
return uploadFile(file);
}
}