diff --git a/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs b/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs
new file mode 100644
index 0000000..cd8c53c
--- /dev/null
+++ b/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs
@@ -0,0 +1,96 @@
+using MiAssessment.Admin.Filters;
+using MiAssessment.Admin.Models.Common;
+using MiAssessment.Admin.Models.Config;
+using MiAssessment.Admin.Services;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace MiAssessment.Admin.Controllers;
+
+///
+/// 后台配置管理控制器
+///
+[ApiController]
+[Route("api/admin/config")]
+[Authorize]
+public class ConfigController : ControllerBase
+{
+ private readonly IAdminConfigService _configService;
+ private readonly ILogger _logger;
+
+ ///
+ /// 配置键常量
+ ///
+ private static class ConfigKeys
+ {
+ public const string Upload = "upload_setting";
+ }
+
+ public ConfigController(IAdminConfigService configService, ILogger logger)
+ {
+ _configService = configService;
+ _logger = logger;
+ }
+
+ ///
+ /// 获取上传配置
+ ///
+ /// 上传配置
+ [HttpGet("upload/get")]
+ public async Task> GetUploadConfig()
+ {
+ try
+ {
+ var config = await _configService.GetConfigAsync(ConfigKeys.Upload);
+ return ApiResponse.Success(config ?? new UploadSetting { Type = "1" });
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "获取上传配置失败");
+ return ApiResponse.Error(AdminErrorCodes.InternalError, "获取上传配置失败");
+ }
+ }
+
+ ///
+ /// 更新上传配置
+ ///
+ /// 上传配置
+ /// 更新结果
+ [HttpPost("upload/update")]
+ [OperationLog("配置管理", "更新上传配置")]
+ public async Task> UpdateUploadConfig([FromBody] UploadSetting request)
+ {
+ if (string.IsNullOrWhiteSpace(request.Type))
+ {
+ return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "存储类型不能为空");
+ }
+
+ // 验证腾讯云COS配置
+ if (request.Type == "3")
+ {
+ if (string.IsNullOrWhiteSpace(request.AppId))
+ return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "AppId不能为空");
+ if (string.IsNullOrWhiteSpace(request.AccessKeyId))
+ return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "SecretId不能为空");
+ if (string.IsNullOrWhiteSpace(request.AccessKeySecret))
+ return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "SecretKey不能为空");
+ if (string.IsNullOrWhiteSpace(request.Bucket))
+ return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "存储桶名称不能为空");
+ if (string.IsNullOrWhiteSpace(request.Region))
+ return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "地域不能为空");
+ if (string.IsNullOrWhiteSpace(request.Domain))
+ return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "访问域名不能为空");
+ }
+
+ try
+ {
+ var result = await _configService.UpdateConfigAsync(ConfigKeys.Upload, request);
+ return ApiResponse.Success(result, "配置更新成功");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "更新上传配置失败");
+ return ApiResponse.Error(AdminErrorCodes.InternalError, "更新上传配置失败");
+ }
+ }
+}
diff --git a/server/MiAssessment/src/MiAssessment.Admin/Extensions/ServiceCollectionExtensions.cs b/server/MiAssessment/src/MiAssessment.Admin/Extensions/ServiceCollectionExtensions.cs
index a5a4ab0..477963a 100644
--- a/server/MiAssessment/src/MiAssessment.Admin/Extensions/ServiceCollectionExtensions.cs
+++ b/server/MiAssessment/src/MiAssessment.Admin/Extensions/ServiceCollectionExtensions.cs
@@ -51,6 +51,7 @@ public static class ServiceCollectionExtensions
services.AddScoped();
services.AddScoped();
services.AddScoped();
+ services.AddScoped();
services.AddScoped();
services.AddSingleton();
diff --git a/server/MiAssessment/src/MiAssessment.Admin/Models/Config/UploadSetting.cs b/server/MiAssessment/src/MiAssessment.Admin/Models/Config/UploadSetting.cs
new file mode 100644
index 0000000..5b16945
--- /dev/null
+++ b/server/MiAssessment/src/MiAssessment.Admin/Models/Config/UploadSetting.cs
@@ -0,0 +1,42 @@
+namespace MiAssessment.Admin.Models.Config;
+
+///
+/// 上传配置
+///
+public class UploadSetting
+{
+ ///
+ /// 存储类型 1本地 2阿里云 3腾讯云
+ ///
+ public string Type { get; set; } = "1";
+
+ ///
+ /// 腾讯云AppId
+ ///
+ public string? AppId { get; set; }
+
+ ///
+ /// 存储桶名称
+ ///
+ public string? Bucket { get; set; }
+
+ ///
+ /// 地域
+ ///
+ public string? Region { get; set; }
+
+ ///
+ /// SecretId
+ ///
+ public string? AccessKeyId { get; set; }
+
+ ///
+ /// SecretKey
+ ///
+ public string? AccessKeySecret { get; set; }
+
+ ///
+ /// 访问域名
+ ///
+ public string? Domain { get; set; }
+}
diff --git a/server/MiAssessment/src/MiAssessment.Admin/Services/AdminConfigService.cs b/server/MiAssessment/src/MiAssessment.Admin/Services/AdminConfigService.cs
new file mode 100644
index 0000000..e39348e
--- /dev/null
+++ b/server/MiAssessment/src/MiAssessment.Admin/Services/AdminConfigService.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Text.Json;
+using System.Threading.Tasks;
+using MiAssessment.Admin.Data;
+using MiAssessment.Admin.Entities;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+
+namespace MiAssessment.Admin.Services;
+
+///
+/// 后台配置服务实现
+///
+public class AdminConfigService : IAdminConfigService
+{
+ private readonly AdminDbContext _dbContext;
+ private readonly ILogger _logger;
+
+ private static readonly JsonSerializerOptions JsonOptions = new()
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = false
+ };
+
+ public AdminConfigService(AdminDbContext dbContext, ILogger logger)
+ {
+ _dbContext = dbContext;
+ _logger = logger;
+ }
+
+ ///
+ public async Task GetConfigAsync(string key)
+ {
+ var config = await _dbContext.AdminConfigs
+ .AsNoTracking()
+ .FirstOrDefaultAsync(c => c.ConfigKey == key);
+ return config?.ConfigValue;
+ }
+
+ ///
+ public async Task GetConfigAsync(string key) where T : class
+ {
+ var value = await GetConfigAsync(key);
+ if (string.IsNullOrEmpty(value))
+ return null;
+
+ try
+ {
+ return JsonSerializer.Deserialize(value, JsonOptions);
+ }
+ catch (JsonException ex)
+ {
+ _logger.LogWarning(ex, "反序列化配置失败: {Key}", key);
+ return null;
+ }
+ }
+
+ ///
+ public async Task UpdateConfigAsync(string key, string value)
+ {
+ var config = await _dbContext.AdminConfigs
+ .FirstOrDefaultAsync(c => c.ConfigKey == key);
+
+ if (config == null)
+ {
+ // 创建新配置
+ config = new AdminConfig
+ {
+ ConfigKey = key,
+ ConfigValue = value,
+ CreatedAt = DateTime.Now
+ };
+ _dbContext.AdminConfigs.Add(config);
+ }
+ else
+ {
+ // 更新现有配置
+ config.ConfigValue = value;
+ config.UpdatedAt = DateTime.Now;
+ }
+
+ await _dbContext.SaveChangesAsync();
+ return true;
+ }
+
+ ///
+ public async Task UpdateConfigAsync(string key, T value) where T : class
+ {
+ var jsonValue = JsonSerializer.Serialize(value, JsonOptions);
+ return await UpdateConfigAsync(key, jsonValue);
+ }
+}
diff --git a/server/MiAssessment/src/MiAssessment.Admin/Services/IAdminConfigService.cs b/server/MiAssessment/src/MiAssessment.Admin/Services/IAdminConfigService.cs
new file mode 100644
index 0000000..d1477db
--- /dev/null
+++ b/server/MiAssessment/src/MiAssessment.Admin/Services/IAdminConfigService.cs
@@ -0,0 +1,41 @@
+using System.Threading.Tasks;
+
+namespace MiAssessment.Admin.Services;
+
+///
+/// 后台配置服务接口
+///
+public interface IAdminConfigService
+{
+ ///
+ /// 获取配置值
+ ///
+ /// 配置键
+ /// 配置值
+ Task GetConfigAsync(string key);
+
+ ///
+ /// 获取配置值并反序列化
+ ///
+ /// 配置类型
+ /// 配置键
+ /// 配置对象
+ Task GetConfigAsync(string key) where T : class;
+
+ ///
+ /// 更新配置值
+ ///
+ /// 配置键
+ /// 配置值
+ /// 是否成功
+ Task UpdateConfigAsync(string key, string value);
+
+ ///
+ /// 更新配置值(序列化对象)
+ ///
+ /// 配置类型
+ /// 配置键
+ /// 配置对象
+ /// 是否成功
+ Task UpdateConfigAsync(string key, T value) where T : class;
+}
diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts
new file mode 100644
index 0000000..3329f37
--- /dev/null
+++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts
@@ -0,0 +1,46 @@
+/**
+ * System Config API - 系统配置 API
+ * @module api/system/config
+ */
+import { request, type ApiResponse } from '@/utils/request'
+
+/**
+ * 上传配置
+ */
+export interface UploadSetting {
+ /** 存储类型 1本地 2阿里云 3腾讯云 */
+ type: string
+ /** 腾讯云AppId */
+ AppId?: string
+ /** 存储桶名称 */
+ Bucket?: string
+ /** 地域 */
+ Region?: string
+ /** SecretId */
+ AccessKeyId?: string
+ /** SecretKey */
+ AccessKeySecret?: string
+ /** 访问域名 */
+ Domain?: string
+}
+
+/**
+ * 获取上传配置
+ */
+export function getUploadConfig(): Promise> {
+ return request({
+ url: '/admin/config/upload/get',
+ method: 'get'
+ })
+}
+
+/**
+ * 更新上传配置
+ */
+export function updateUploadConfig(data: UploadSetting): Promise> {
+ return request({
+ url: '/admin/config/upload/update',
+ method: 'post',
+ data
+ })
+}
diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/store/modules/permission.ts b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/store/modules/permission.ts
index 7de5952..8086fd1 100644
--- a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/store/modules/permission.ts
+++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/store/modules/permission.ts
@@ -4,8 +4,8 @@ import type { RouteRecordRaw } from 'vue-router'
import { getUserMenus, type MenuTree } from '@/api/menu'
import Layout from '@/layout/index.vue'
-// 视图模块映射
-const viewModules = import.meta.glob('@/views/**/*.vue')
+// 视图模块映射 - 使用相对路径格式
+const viewModules = import.meta.glob('/src/views/**/*.vue')
export const usePermissionStore = defineStore('permission', () => {
const routes = ref([])
@@ -82,7 +82,13 @@ export const usePermissionStore = defineStore('permission', () => {
// 加载组件
function loadComponent(component: string) {
const path = `/src/views/${component}.vue`
- return viewModules[path] || (() => import('@/views/error/404.vue'))
+
+ if (viewModules[path]) {
+ return viewModules[path]
+ }
+
+ console.warn(`Component not found: ${component}, path: ${path}`)
+ return () => import('@/views/error/404.vue')
}
// 重置状态
diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/upload.vue b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/upload.vue
new file mode 100644
index 0000000..66a4331
--- /dev/null
+++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/upload.vue
@@ -0,0 +1,356 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 本地存储
+
+
+
+
+
+ 腾讯云COS
+
+
+
+
+
+
+
+ 文件将存储在服务器本地 uploads 目录,适合小型项目或测试环境。
+
+
+
+
+ 腾讯云COS配置
+
+
+ 请前往腾讯云控制台获取相关配置信息。确保存储桶已开启跨域访问(CORS)。
+
+
+
+
+ 腾讯云账号的AppId,可在账号信息中查看
+
+
+
+
+ API密钥的SecretId
+
+
+
+
+ API密钥的SecretKey,请妥善保管
+
+
+
+
+ 完整的存储桶名称,包含AppId后缀
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 存储桶所在地域
+
+
+
+
+ https://
+
+ 用于访问文件的域名,可使用CDN加速域名
+
+
+
+
+
+
+
+ 保存配置
+
+
+
+ 重置
+
+
+
+ 测试连接
+
+
+
+
+
+
+
+
+
+