From 7154d7eb017e3018f30d321a3a9be890f15ea93a Mon Sep 17 00:00:00 2001 From: zpc Date: Mon, 23 Feb 2026 12:35:28 +0800 Subject: [PATCH] =?UTF-8?q?feat(content):=20=E6=96=B0=E5=A2=9E=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E5=AF=BC=E8=88=AA=E5=85=A5=E5=8F=A3=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新建 home_navigations 表,独立管理首页卡片导航 - 回退 assessment_types 表的 LinkUrl 字段 - 后台管理:ContentController 新增导航 CRUD 接口 - 小程序 API:HomeController 新增 getNavigationList 接口 - 前端:首页改用 navigationList 数据源,支持配置化跳转 - 数据库已插入3条导航记录(多元测评/学业规划/学科测评) --- .../Controllers/ContentController.cs | 154 ++++++++++++++++ .../Data/AdminBusinessDbContext.cs | 5 + .../Entities/AssessmentType.cs | 6 - .../Entities/HomeNavigation.cs | 62 +++++++ .../Models/Assessment/AssessmentTypeDto.cs | 5 - .../Assessment/CreateAssessmentTypeRequest.cs | 6 - .../Assessment/UpdateAssessmentTypeRequest.cs | 6 - .../Content/CreateHomeNavigationRequest.cs | 38 ++++ .../Models/Content/HomeNavigationDto.cs | 47 +++++ .../Content/HomeNavigationQueryRequest.cs | 19 ++ .../Content/UpdateHomeNavigationRequest.cs | 44 +++++ .../Services/AssessmentService.cs | 4 - .../Services/ContentService.cs | 171 ++++++++++++++++++ .../Services/Interfaces/IContentService.cs | 34 ++++ .../Controllers/HomeController.cs | 26 +++ .../Interfaces/IHomeService.cs | 10 + .../MiAssessment.Core/Services/HomeService.cs | 24 ++- .../Data/MiAssessmentDbContext.cs | 41 ++++- .../Entities/AssessmentType.cs | 6 - .../Entities/HomeNavigation.cs | 62 +++++++ .../Models/Home/AssessmentTypeDto.cs | 5 - .../Models/Home/HomeNavigationDto.cs | 32 ++++ uniapp/api/home.js | 9 + uniapp/pages/index/index.vue | 43 +++-- 24 files changed, 802 insertions(+), 57 deletions(-) create mode 100644 server/MiAssessment/src/MiAssessment.Admin.Business/Entities/HomeNavigation.cs create mode 100644 server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/CreateHomeNavigationRequest.cs create mode 100644 server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/HomeNavigationDto.cs create mode 100644 server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/HomeNavigationQueryRequest.cs create mode 100644 server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/UpdateHomeNavigationRequest.cs create mode 100644 server/MiAssessment/src/MiAssessment.Model/Entities/HomeNavigation.cs create mode 100644 server/MiAssessment/src/MiAssessment.Model/Models/Home/HomeNavigationDto.cs diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Controllers/ContentController.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Controllers/ContentController.cs index 2bc5ceb..820c497 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Controllers/ContentController.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Controllers/ContentController.cs @@ -400,6 +400,160 @@ public class ContentController : BusinessControllerBase #endregion + #region HomeNavigation 首页导航接口 + + /// + /// 获取首页导航列表 + /// + /// 查询参数 + /// 分页首页导航列表 + [HttpGet("navigation/getList")] + [BusinessPermission("content:view")] + public async Task GetNavigationList([FromQuery] HomeNavigationQueryRequest request) + { + try + { + var result = await _contentService.GetNavigationListAsync(request); + return Ok(result); + } + catch (BusinessException ex) + { + return Error(ex.Code, ex.Message); + } + catch (Exception) + { + return Error(ErrorCodes.SystemError, "获取首页导航列表失败"); + } + } + + /// + /// 创建首页导航 + /// + /// 创建请求 + /// 新创建的导航ID + [HttpPost("navigation/create")] + [BusinessPermission("content:create")] + public async Task CreateNavigation([FromBody] CreateHomeNavigationRequest request) + { + if (string.IsNullOrWhiteSpace(request.Name)) + { + return ValidationError("导航名称不能为空"); + } + + try + { + var id = await _contentService.CreateNavigationAsync(request); + return Ok(id); + } + catch (BusinessException ex) + { + return Error(ex.Code, ex.Message); + } + catch (Exception) + { + return Error(ErrorCodes.SystemError, "创建首页导航失败"); + } + } + + /// + /// 更新首页导航 + /// + /// 更新请求 + /// 更新结果 + [HttpPost("navigation/update")] + [BusinessPermission("content:update")] + public async Task UpdateNavigation([FromBody] UpdateHomeNavigationRequest request) + { + if (request.Id <= 0) + { + return ValidationError("导航ID无效"); + } + + if (string.IsNullOrWhiteSpace(request.Name)) + { + return ValidationError("导航名称不能为空"); + } + + try + { + var result = await _contentService.UpdateNavigationAsync(request); + return Ok(result); + } + catch (BusinessException ex) + { + return Error(ex.Code, ex.Message); + } + catch (Exception) + { + return Error(ErrorCodes.SystemError, "更新首页导航失败"); + } + } + + /// + /// 删除首页导航(软删除) + /// + /// 删除请求 + /// 删除结果 + [HttpPost("navigation/delete")] + [BusinessPermission("content:delete")] + public async Task DeleteNavigation([FromBody] DeleteRequest request) + { + if (request.Id <= 0) + { + return ValidationError("导航ID无效"); + } + + try + { + var result = await _contentService.DeleteNavigationAsync(request.Id); + return Ok(result); + } + catch (BusinessException ex) + { + return Error(ex.Code, ex.Message); + } + catch (Exception) + { + return Error(ErrorCodes.SystemError, "删除首页导航失败"); + } + } + + /// + /// 更新首页导航状态 + /// + /// 状态更新请求 + /// 更新结果 + [HttpPost("navigation/updateStatus")] + [BusinessPermission("content:update")] + public async Task UpdateNavigationStatus([FromBody] UpdateStatusRequest request) + { + if (request.Id <= 0) + { + return ValidationError("导航ID无效"); + } + + if (request.Status < 0 || request.Status > 1) + { + return ValidationError("状态值无效,只能为0(禁用)或1(启用)"); + } + + try + { + var result = await _contentService.UpdateNavigationStatusAsync(request.Id, request.Status); + return Ok(result); + } + catch (BusinessException ex) + { + return Error(ex.Code, ex.Message); + } + catch (Exception) + { + return Error(ErrorCodes.SystemError, "更新首页导航状态失败"); + } + } + + #endregion + #region 私有方法 /// diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs index 9b5794a..e4de907 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs @@ -34,6 +34,11 @@ public class AdminBusinessDbContext : DbContext /// public DbSet Promotions { get; set; } = null!; + /// + /// 首页导航入口表 + /// + public DbSet HomeNavigations { get; set; } = null!; + #endregion #region 测评管理 diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/AssessmentType.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/AssessmentType.cs index 9b6666f..a15d4e2 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/AssessmentType.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/AssessmentType.cs @@ -36,12 +36,6 @@ public class AssessmentType [MaxLength(500)] public string? ImageUrl { get; set; } - /// - /// 跳转链接URL - /// - [MaxLength(500)] - public string? LinkUrl { get; set; } - /// /// 详情横幅图URL /// diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/HomeNavigation.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/HomeNavigation.cs new file mode 100644 index 0000000..2df4ac2 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/HomeNavigation.cs @@ -0,0 +1,62 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace MiAssessment.Admin.Business.Entities; + +/// +/// 首页导航入口表 +/// +[Table("home_navigations")] +public class HomeNavigation +{ + /// + /// 主键ID + /// + [Key] + public long Id { get; set; } + + /// + /// 导航名称 + /// + [Required] + [MaxLength(50)] + public string Name { get; set; } = null!; + + /// + /// 图标图片URL + /// + [MaxLength(500)] + public string? ImageUrl { get; set; } + + /// + /// 跳转链接URL + /// + [MaxLength(500)] + public string? LinkUrl { get; set; } + + /// + /// 排序 + /// + public int Sort { get; set; } + + /// + /// 状态:0即将上线 1已上线 + /// + public int Status { get; set; } = 1; + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 更新时间 + /// + public DateTime UpdateTime { get; set; } + + /// + /// 软删除标记 + /// + public bool IsDeleted { get; set; } +} diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Assessment/AssessmentTypeDto.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Assessment/AssessmentTypeDto.cs index 693120e..5a48868 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Assessment/AssessmentTypeDto.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Assessment/AssessmentTypeDto.cs @@ -25,11 +25,6 @@ public class AssessmentTypeDto /// public string? ImageUrl { get; set; } - /// - /// 跳转链接URL - /// - public string? LinkUrl { get; set; } - /// /// 详情横幅图URL /// diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Assessment/CreateAssessmentTypeRequest.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Assessment/CreateAssessmentTypeRequest.cs index 8e89f88..038a6ed 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Assessment/CreateAssessmentTypeRequest.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Assessment/CreateAssessmentTypeRequest.cs @@ -27,12 +27,6 @@ public class CreateAssessmentTypeRequest [MaxLength(500, ErrorMessage = "图片URL不能超过500个字符")] public string? ImageUrl { get; set; } - /// - /// 跳转链接URL - /// - [MaxLength(500, ErrorMessage = "跳转链接URL不能超过500个字符")] - public string? LinkUrl { get; set; } - /// /// 详情横幅图URL /// diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Assessment/UpdateAssessmentTypeRequest.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Assessment/UpdateAssessmentTypeRequest.cs index 9295f24..51601aa 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Assessment/UpdateAssessmentTypeRequest.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Assessment/UpdateAssessmentTypeRequest.cs @@ -33,12 +33,6 @@ public class UpdateAssessmentTypeRequest [MaxLength(500, ErrorMessage = "图片URL不能超过500个字符")] public string? ImageUrl { get; set; } - /// - /// 跳转链接URL - /// - [MaxLength(500, ErrorMessage = "跳转链接URL不能超过500个字符")] - public string? LinkUrl { get; set; } - /// /// 详情横幅图URL /// diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/CreateHomeNavigationRequest.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/CreateHomeNavigationRequest.cs new file mode 100644 index 0000000..998f6f8 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/CreateHomeNavigationRequest.cs @@ -0,0 +1,38 @@ +using System.ComponentModel.DataAnnotations; + +namespace MiAssessment.Admin.Business.Models.Content; + +/// +/// 创建首页导航请求 +/// +public class CreateHomeNavigationRequest +{ + /// + /// 导航名称(必填) + /// + [Required(ErrorMessage = "导航名称不能为空")] + [MaxLength(50, ErrorMessage = "导航名称不能超过50个字符")] + public string Name { get; set; } = null!; + + /// + /// 图标图片URL + /// + [MaxLength(500, ErrorMessage = "图片URL不能超过500个字符")] + public string? ImageUrl { get; set; } + + /// + /// 跳转链接URL + /// + [MaxLength(500, ErrorMessage = "跳转链接URL不能超过500个字符")] + public string? LinkUrl { get; set; } + + /// + /// 排序 + /// + public int Sort { get; set; } + + /// + /// 状态:0即将上线 1已上线 + /// + public int Status { get; set; } = 1; +} diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/HomeNavigationDto.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/HomeNavigationDto.cs new file mode 100644 index 0000000..34669b1 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/HomeNavigationDto.cs @@ -0,0 +1,47 @@ +namespace MiAssessment.Admin.Business.Models.Content; + +/// +/// 首页导航入口 DTO +/// +public class HomeNavigationDto +{ + /// + /// 主键ID + /// + public long Id { get; set; } + + /// + /// 导航名称 + /// + public string Name { get; set; } = null!; + + /// + /// 图标图片URL + /// + public string? ImageUrl { get; set; } + + /// + /// 跳转链接URL + /// + public string? LinkUrl { get; set; } + + /// + /// 排序 + /// + public int Sort { get; set; } + + /// + /// 状态:0即将上线 1已上线 + /// + public int Status { get; set; } + + /// + /// 状态名称 + /// + public string StatusName { get; set; } = null!; + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } +} diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/HomeNavigationQueryRequest.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/HomeNavigationQueryRequest.cs new file mode 100644 index 0000000..13d1628 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/HomeNavigationQueryRequest.cs @@ -0,0 +1,19 @@ +using MiAssessment.Admin.Business.Models.Common; + +namespace MiAssessment.Admin.Business.Models.Content; + +/// +/// 首页导航查询请求 +/// +public class HomeNavigationQueryRequest : PagedRequest +{ + /// + /// 名称筛选 + /// + public string? Name { get; set; } + + /// + /// 状态筛选 + /// + public int? Status { get; set; } +} diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/UpdateHomeNavigationRequest.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/UpdateHomeNavigationRequest.cs new file mode 100644 index 0000000..b67dc9a --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/UpdateHomeNavigationRequest.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; + +namespace MiAssessment.Admin.Business.Models.Content; + +/// +/// 更新首页导航请求 +/// +public class UpdateHomeNavigationRequest +{ + /// + /// 导航ID(必填) + /// + [Required(ErrorMessage = "导航ID不能为空")] + public long Id { get; set; } + + /// + /// 导航名称(必填) + /// + [Required(ErrorMessage = "导航名称不能为空")] + [MaxLength(50, ErrorMessage = "导航名称不能超过50个字符")] + public string Name { get; set; } = null!; + + /// + /// 图标图片URL + /// + [MaxLength(500, ErrorMessage = "图片URL不能超过500个字符")] + public string? ImageUrl { get; set; } + + /// + /// 跳转链接URL + /// + [MaxLength(500, ErrorMessage = "跳转链接URL不能超过500个字符")] + public string? LinkUrl { get; set; } + + /// + /// 排序 + /// + public int Sort { get; set; } + + /// + /// 状态:0即将上线 1已上线 + /// + public int Status { get; set; } +} diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentService.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentService.cs index fd0167d..01e97e3 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentService.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentService.cs @@ -83,7 +83,6 @@ public class AssessmentService : IAssessmentService Name = t.Name, Code = t.Code, ImageUrl = t.ImageUrl, - LinkUrl = t.LinkUrl, DetailImageUrl = t.DetailImageUrl, IntroContent = t.IntroContent, Price = t.Price, @@ -110,7 +109,6 @@ public class AssessmentService : IAssessmentService Name = t.Name, Code = t.Code, ImageUrl = t.ImageUrl, - LinkUrl = t.LinkUrl, DetailImageUrl = t.DetailImageUrl, IntroContent = t.IntroContent, Price = t.Price, @@ -163,7 +161,6 @@ public class AssessmentService : IAssessmentService Name = request.Name, Code = request.Code, ImageUrl = request.ImageUrl, - LinkUrl = request.LinkUrl, DetailImageUrl = request.DetailImageUrl, IntroContent = request.IntroContent, Price = request.Price, @@ -225,7 +222,6 @@ public class AssessmentService : IAssessmentService assessmentType.Name = request.Name; assessmentType.Code = request.Code; assessmentType.ImageUrl = request.ImageUrl; - assessmentType.LinkUrl = request.LinkUrl; assessmentType.DetailImageUrl = request.DetailImageUrl; assessmentType.IntroContent = request.IntroContent; assessmentType.Price = request.Price; diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ContentService.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ContentService.cs index 41d0fc1..04a5e5a 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ContentService.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ContentService.cs @@ -555,6 +555,15 @@ public class ContentService : IContentService return PositionNames.TryGetValue(position, out var name) ? name : "未知"; } + /// + /// 导航状态名称映射 + /// + private static readonly Dictionary NavigationStatusNames = new() + { + { 0, "即将上线" }, + { 1, "已上线" } + }; + /// /// 验证 Position 值 /// @@ -568,4 +577,166 @@ public class ContentService : IContentService } #endregion + + #region HomeNavigation 首页导航操作 + + /// + public async Task> GetNavigationListAsync(HomeNavigationQueryRequest request) + { + var query = _dbContext.HomeNavigations + .AsNoTracking() + .Where(n => !n.IsDeleted); + + if (!string.IsNullOrWhiteSpace(request.Name)) + { + query = query.Where(n => n.Name.Contains(request.Name)); + } + + if (request.Status.HasValue) + { + query = query.Where(n => n.Status == request.Status.Value); + } + + var total = await query.CountAsync(); + + var items = await query + .OrderByDescending(n => n.Sort) + .ThenByDescending(n => n.CreateTime) + .Skip(request.Skip) + .Take(request.PageSize) + .Select(n => new HomeNavigationDto + { + Id = n.Id, + Name = n.Name, + ImageUrl = n.ImageUrl, + LinkUrl = n.LinkUrl, + Sort = n.Sort, + Status = n.Status, + StatusName = NavigationStatusNames.ContainsKey(n.Status) ? NavigationStatusNames[n.Status] : "未知", + CreateTime = n.CreateTime + }) + .ToListAsync(); + + return PagedResult.Create(items, total, request.Page, request.PageSize); + } + + /// + public async Task GetNavigationByIdAsync(long id) + { + var nav = await _dbContext.HomeNavigations + .AsNoTracking() + .Where(n => n.Id == id && !n.IsDeleted) + .Select(n => new HomeNavigationDto + { + Id = n.Id, + Name = n.Name, + ImageUrl = n.ImageUrl, + LinkUrl = n.LinkUrl, + Sort = n.Sort, + Status = n.Status, + StatusName = NavigationStatusNames.ContainsKey(n.Status) ? NavigationStatusNames[n.Status] : "未知", + CreateTime = n.CreateTime + }) + .FirstOrDefaultAsync(); + + if (nav == null) + { + throw new BusinessException(ErrorCodes.ContentNotFound, "首页导航不存在"); + } + + return nav; + } + + /// + public async Task CreateNavigationAsync(CreateHomeNavigationRequest request) + { + if (string.IsNullOrWhiteSpace(request.Name)) + { + throw new BusinessException(ErrorCodes.ParamError, "导航名称不能为空"); + } + + var entity = new HomeNavigation + { + Name = request.Name, + ImageUrl = request.ImageUrl, + LinkUrl = request.LinkUrl, + Sort = request.Sort, + Status = request.Status, + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + IsDeleted = false + }; + + _dbContext.HomeNavigations.Add(entity); + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation("创建首页导航成功,ID: {Id}, 名称: {Name}", entity.Id, entity.Name); + return entity.Id; + } + + /// + public async Task UpdateNavigationAsync(UpdateHomeNavigationRequest request) + { + var entity = await _dbContext.HomeNavigations + .Where(n => n.Id == request.Id && !n.IsDeleted) + .FirstOrDefaultAsync(); + + if (entity == null) + { + throw new BusinessException(ErrorCodes.ContentNotFound, "首页导航不存在"); + } + + if (string.IsNullOrWhiteSpace(request.Name)) + { + throw new BusinessException(ErrorCodes.ParamError, "导航名称不能为空"); + } + + entity.Name = request.Name; + entity.ImageUrl = request.ImageUrl; + entity.LinkUrl = request.LinkUrl; + entity.Sort = request.Sort; + entity.Status = request.Status; + entity.UpdateTime = DateTime.Now; + + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation("更新首页导航成功,ID: {Id}", entity.Id); + return true; + } + + /// + public async Task DeleteNavigationAsync(long id) + { + var entity = await _dbContext.HomeNavigations.FindAsync(id); + if (entity == null || entity.IsDeleted) return false; + + entity.IsDeleted = true; + entity.UpdateTime = DateTime.Now; + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation("删除首页导航成功,ID: {Id}", id); + return true; + } + + /// + public async Task UpdateNavigationStatusAsync(long id, int status) + { + var entity = await _dbContext.HomeNavigations + .Where(n => n.Id == id && !n.IsDeleted) + .FirstOrDefaultAsync(); + + if (entity == null) + { + throw new BusinessException(ErrorCodes.ContentNotFound, "首页导航不存在"); + } + + entity.Status = status; + entity.UpdateTime = DateTime.Now; + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation("更新首页导航状态成功,ID: {Id}, 状态: {Status}", id, status); + return true; + } + + #endregion } diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/Interfaces/IContentService.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/Interfaces/IContentService.cs index a9dcd76..631989f 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/Interfaces/IContentService.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/Interfaces/IContentService.cs @@ -109,4 +109,38 @@ public interface IContentService Task UpdatePromotionStatusAsync(long id, int status); #endregion + + #region HomeNavigation 首页导航操作 + + /// + /// 获取首页导航列表 + /// + Task> GetNavigationListAsync(HomeNavigationQueryRequest request); + + /// + /// 根据ID获取首页导航 + /// + Task GetNavigationByIdAsync(long id); + + /// + /// 创建首页导航 + /// + Task CreateNavigationAsync(CreateHomeNavigationRequest request); + + /// + /// 更新首页导航 + /// + Task UpdateNavigationAsync(UpdateHomeNavigationRequest request); + + /// + /// 删除首页导航(软删除) + /// + Task DeleteNavigationAsync(long id); + + /// + /// 更新首页导航状态 + /// + Task UpdateNavigationStatusAsync(long id, int status); + + #endregion } diff --git a/server/MiAssessment/src/MiAssessment.Api/Controllers/HomeController.cs b/server/MiAssessment/src/MiAssessment.Api/Controllers/HomeController.cs index fd82ffa..6655aa5 100644 --- a/server/MiAssessment/src/MiAssessment.Api/Controllers/HomeController.cs +++ b/server/MiAssessment/src/MiAssessment.Api/Controllers/HomeController.cs @@ -85,6 +85,32 @@ public class HomeController : ControllerBase } } + /// + /// 获取首页导航入口列表 + /// + /// + /// GET /api/home/getNavigationList + /// + /// 返回所有启用状态的首页导航入口,按Sort降序排列 + /// 不需要用户登录认证 + /// + /// 导航入口列表 + [HttpGet("getNavigationList")] + [ProducesResponseType(typeof(ApiResponse>), StatusCodes.Status200OK)] + public async Task>> GetNavigationList() + { + try + { + var navigations = await _homeService.GetNavigationListAsync(); + return ApiResponse>.Success(navigations); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to get navigation list"); + return ApiResponse>.Fail("获取导航入口列表失败"); + } + } + /// /// 获取宣传图列表 /// diff --git a/server/MiAssessment/src/MiAssessment.Core/Interfaces/IHomeService.cs b/server/MiAssessment/src/MiAssessment.Core/Interfaces/IHomeService.cs index 5f2a375..f08cccb 100644 --- a/server/MiAssessment/src/MiAssessment.Core/Interfaces/IHomeService.cs +++ b/server/MiAssessment/src/MiAssessment.Core/Interfaces/IHomeService.cs @@ -1,5 +1,6 @@ using MiAssessment.Model.Models.Home; + namespace MiAssessment.Core.Interfaces; /// @@ -25,6 +26,15 @@ public interface IHomeService /// 测评类型列表 Task> GetAssessmentListAsync(); + /// + /// 获取首页导航入口列表 + /// + /// + /// 返回所有启用状态的首页导航入口,按Sort降序排列 + /// + /// 导航入口列表 + Task> GetNavigationListAsync(); + /// /// 获取宣传图列表 /// diff --git a/server/MiAssessment/src/MiAssessment.Core/Services/HomeService.cs b/server/MiAssessment/src/MiAssessment.Core/Services/HomeService.cs index 53249a3..aa0acf0 100644 --- a/server/MiAssessment/src/MiAssessment.Core/Services/HomeService.cs +++ b/server/MiAssessment/src/MiAssessment.Core/Services/HomeService.cs @@ -66,7 +66,6 @@ public class HomeService : IHomeService Name = a.Name, Code = a.Code, ImageUrl = a.ImageUrl ?? string.Empty, - LinkUrl = a.LinkUrl, Price = a.Price, Status = a.Status }) @@ -76,6 +75,29 @@ public class HomeService : IHomeService return assessmentTypes; } + /// + public async Task> GetNavigationListAsync() + { + _logger.LogDebug("获取首页导航入口列表"); + + var navigations = await _dbContext.HomeNavigations + .AsNoTracking() + .Where(n => n.Status == 1 && !n.IsDeleted) + .OrderByDescending(n => n.Sort) + .Select(n => new HomeNavigationDto + { + Id = n.Id, + Name = n.Name, + ImageUrl = n.ImageUrl ?? string.Empty, + LinkUrl = n.LinkUrl, + Status = n.Status + }) + .ToListAsync(); + + _logger.LogDebug("获取到 {Count} 条导航入口记录", navigations.Count); + return navigations; + } + /// public async Task> GetPromotionListAsync() { diff --git a/server/MiAssessment/src/MiAssessment.Model/Data/MiAssessmentDbContext.cs b/server/MiAssessment/src/MiAssessment.Model/Data/MiAssessmentDbContext.cs index f7bd34c..8604538 100644 --- a/server/MiAssessment/src/MiAssessment.Model/Data/MiAssessmentDbContext.cs +++ b/server/MiAssessment/src/MiAssessment.Model/Data/MiAssessmentDbContext.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using MiAssessment.Model.Entities; using Microsoft.EntityFrameworkCore; @@ -61,6 +61,8 @@ public partial class MiAssessmentDbContext : DbContext public virtual DbSet Orders { get; set; } + public virtual DbSet HomeNavigations { get; set; } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { // Connection string is configured in Program.cs via dependency injection @@ -1009,6 +1011,43 @@ public partial class MiAssessmentDbContext : DbContext entity.Property(e => e.UpdateTime).HasDefaultValueSql("(getdate())"); }); + // ==================== 首页导航表配置 ==================== + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("pk_home_navigations"); + + entity.ToTable("home_navigations", tb => tb.HasComment("首页导航入口表")); + + entity.HasIndex(e => e.Status, "ix_home_navigations_status"); + entity.HasIndex(e => e.Sort, "ix_home_navigations_sort"); + + entity.Property(e => e.Id) + .HasComment("主键ID"); + entity.Property(e => e.Name) + .HasMaxLength(50) + .HasComment("导航名称"); + entity.Property(e => e.ImageUrl) + .HasMaxLength(500) + .HasComment("图标图片URL"); + entity.Property(e => e.LinkUrl) + .HasMaxLength(500) + .HasComment("跳转链接URL"); + entity.Property(e => e.Sort) + .HasComment("排序,越大越靠前"); + entity.Property(e => e.Status) + .HasDefaultValue(1) + .HasComment("状态:0即将上线 1已上线"); + entity.Property(e => e.CreateTime) + .HasDefaultValueSql("(getdate())") + .HasComment("创建时间"); + entity.Property(e => e.UpdateTime) + .HasDefaultValueSql("(getdate())") + .HasComment("更新时间"); + entity.Property(e => e.IsDeleted) + .HasDefaultValue(false) + .HasComment("软删除标记"); + }); + OnModelCreatingPartial(modelBuilder); } diff --git a/server/MiAssessment/src/MiAssessment.Model/Entities/AssessmentType.cs b/server/MiAssessment/src/MiAssessment.Model/Entities/AssessmentType.cs index bc20938..efcb949 100644 --- a/server/MiAssessment/src/MiAssessment.Model/Entities/AssessmentType.cs +++ b/server/MiAssessment/src/MiAssessment.Model/Entities/AssessmentType.cs @@ -36,12 +36,6 @@ public class AssessmentType [MaxLength(500)] public string? ImageUrl { get; set; } - /// - /// 跳转链接URL - /// - [MaxLength(500)] - public string? LinkUrl { get; set; } - /// /// 详情横幅图URL /// diff --git a/server/MiAssessment/src/MiAssessment.Model/Entities/HomeNavigation.cs b/server/MiAssessment/src/MiAssessment.Model/Entities/HomeNavigation.cs new file mode 100644 index 0000000..849c410 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Model/Entities/HomeNavigation.cs @@ -0,0 +1,62 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace MiAssessment.Model.Entities; + +/// +/// 首页导航入口表 +/// +[Table("home_navigations")] +public class HomeNavigation +{ + /// + /// 主键ID + /// + [Key] + public long Id { get; set; } + + /// + /// 导航名称 + /// + [Required] + [MaxLength(50)] + public string Name { get; set; } = null!; + + /// + /// 图标图片URL + /// + [MaxLength(500)] + public string? ImageUrl { get; set; } + + /// + /// 跳转链接URL + /// + [MaxLength(500)] + public string? LinkUrl { get; set; } + + /// + /// 排序 + /// + public int Sort { get; set; } + + /// + /// 状态:0即将上线 1已上线 + /// + public int Status { get; set; } = 1; + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 更新时间 + /// + public DateTime UpdateTime { get; set; } + + /// + /// 软删除标记 + /// + public bool IsDeleted { get; set; } +} diff --git a/server/MiAssessment/src/MiAssessment.Model/Models/Home/AssessmentTypeDto.cs b/server/MiAssessment/src/MiAssessment.Model/Models/Home/AssessmentTypeDto.cs index 35ad622..ecfea5b 100644 --- a/server/MiAssessment/src/MiAssessment.Model/Models/Home/AssessmentTypeDto.cs +++ b/server/MiAssessment/src/MiAssessment.Model/Models/Home/AssessmentTypeDto.cs @@ -25,11 +25,6 @@ public class AssessmentTypeDto /// public string ImageUrl { get; set; } = null!; - /// - /// 跳转链接URL - /// - public string? LinkUrl { get; set; } - /// /// 价格 /// diff --git a/server/MiAssessment/src/MiAssessment.Model/Models/Home/HomeNavigationDto.cs b/server/MiAssessment/src/MiAssessment.Model/Models/Home/HomeNavigationDto.cs new file mode 100644 index 0000000..97282d5 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Model/Models/Home/HomeNavigationDto.cs @@ -0,0 +1,32 @@ +namespace MiAssessment.Model.Models.Home; + +/// +/// 首页导航入口数据传输对象 +/// +public class HomeNavigationDto +{ + /// + /// 导航ID + /// + public long Id { get; set; } + + /// + /// 导航名称 + /// + public string Name { get; set; } = null!; + + /// + /// 图标图片URL + /// + public string ImageUrl { get; set; } = null!; + + /// + /// 跳转链接URL + /// + public string? LinkUrl { get; set; } + + /// + /// 状态:0即将上线 1已上线 + /// + public int Status { get; set; } +} diff --git a/uniapp/api/home.js b/uniapp/api/home.js index 4dc6836..d004ebc 100644 --- a/uniapp/api/home.js +++ b/uniapp/api/home.js @@ -20,6 +20,14 @@ export function getAssessmentList() { return get('/home/getAssessmentList') } +/** + * 获取首页导航入口列表 + * @returns {Promise} + */ +export function getNavigationList() { + return get('/home/getNavigationList') +} + /** * 获取宣传图列表 * @returns {Promise} @@ -31,5 +39,6 @@ export function getPromotionList() { export default { getBannerList, getAssessmentList, + getNavigationList, getPromotionList } diff --git a/uniapp/pages/index/index.vue b/uniapp/pages/index/index.vue index c379542..940b394 100644 --- a/uniapp/pages/index/index.vue +++ b/uniapp/pages/index/index.vue @@ -44,7 +44,7 @@ - + 专业测评 @@ -52,9 +52,9 @@ @@ -99,7 +99,7 @@ import { ref, computed, onMounted } from 'vue' import { useUserStore } from '@/store/user.js' import { useNavbar } from '@/composables/useNavbar.js' -import { getBannerList, getAssessmentList, getPromotionList } from '@/api/home.js' +import { getBannerList, getAssessmentList, getNavigationList, getPromotionList } from '@/api/home.js' import Loading from '@/components/Loading/index.vue' const userStore = useUserStore() @@ -110,6 +110,7 @@ const pageLoading = ref(true) const isRefreshing = ref(false) const bannerList = ref([]) const assessmentList = ref([]) +const navigationList = ref([]) const promotionList = ref([]) // 导航栏样式已直接在模板中绑定 @@ -142,6 +143,20 @@ async function loadAssessmentList() { } } +/** + * 加载首页导航入口数据 + */ +async function loadNavigationList() { + try { + const res = await getNavigationList() + if (res && res.code === 0 && res.data) { + navigationList.value = Array.isArray(res.data) ? res.data : (res.data.list || []) + } + } catch (error) { + console.error('加载导航入口失败:', error) + } +} + /** * 加载宣传图数据 */ @@ -169,6 +184,7 @@ async function initPageData() { await Promise.all([ loadBannerList(), loadAssessmentList(), + loadNavigationList(), loadPromotionList() ]) } finally { @@ -203,13 +219,13 @@ function handleBannerClick(item) { } /** - * 处理测评入口点击 + * 处理导航入口点击 */ -function handleAssessmentClick(item) { - // 即将上线的测评,弹出提示 +function handleNavigationClick(item) { + // 即将上线的导航,弹出提示 if (item.status === 0) { uni.showToast({ - title: '该测评暂未开放', + title: '该功能暂未开放', icon: 'none', duration: 2000 }) @@ -219,18 +235,11 @@ function handleAssessmentClick(item) { // 根据后台配置的跳转链接进行导航 if (!item.linkUrl) return - // 如果是测评页面,自动拼接 typeId 和 typeName 参数 - let url = item.linkUrl - if (url.includes('/pages/assessment/')) { - const separator = url.includes('?') ? '&' : '?' - url = `${url}${separator}typeId=${item.id}&typeName=${encodeURIComponent(item.name || '')}` - } - uni.navigateTo({ - url, + url: item.linkUrl, fail: () => { // 如果是tabBar页面,使用switchTab - uni.switchTab({ url }) + uni.switchTab({ url: item.linkUrl }) } }) }