feat(content): 新增首页导航入口独立管理模块

- 新建 home_navigations 表,独立管理首页卡片导航
- 回退 assessment_types 表的 LinkUrl 字段
- 后台管理:ContentController 新增导航 CRUD 接口
- 小程序 API:HomeController 新增 getNavigationList 接口
- 前端:首页改用 navigationList 数据源,支持配置化跳转
- 数据库已插入3条导航记录(多元测评/学业规划/学科测评)
This commit is contained in:
zpc 2026-02-23 12:35:28 +08:00
parent 68b5d1d024
commit 7154d7eb01
24 changed files with 802 additions and 57 deletions

View File

@ -400,6 +400,160 @@ public class ContentController : BusinessControllerBase
#endregion
#region HomeNavigation
/// <summary>
/// 获取首页导航列表
/// </summary>
/// <param name="request">查询参数</param>
/// <returns>分页首页导航列表</returns>
[HttpGet("navigation/getList")]
[BusinessPermission("content:view")]
public async Task<IActionResult> 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, "获取首页导航列表失败");
}
}
/// <summary>
/// 创建首页导航
/// </summary>
/// <param name="request">创建请求</param>
/// <returns>新创建的导航ID</returns>
[HttpPost("navigation/create")]
[BusinessPermission("content:create")]
public async Task<IActionResult> 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, "创建首页导航失败");
}
}
/// <summary>
/// 更新首页导航
/// </summary>
/// <param name="request">更新请求</param>
/// <returns>更新结果</returns>
[HttpPost("navigation/update")]
[BusinessPermission("content:update")]
public async Task<IActionResult> 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, "更新首页导航失败");
}
}
/// <summary>
/// 删除首页导航(软删除)
/// </summary>
/// <param name="request">删除请求</param>
/// <returns>删除结果</returns>
[HttpPost("navigation/delete")]
[BusinessPermission("content:delete")]
public async Task<IActionResult> 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, "删除首页导航失败");
}
}
/// <summary>
/// 更新首页导航状态
/// </summary>
/// <param name="request">状态更新请求</param>
/// <returns>更新结果</returns>
[HttpPost("navigation/updateStatus")]
[BusinessPermission("content:update")]
public async Task<IActionResult> 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
/// <summary>

View File

@ -34,6 +34,11 @@ public class AdminBusinessDbContext : DbContext
/// </summary>
public DbSet<Promotion> Promotions { get; set; } = null!;
/// <summary>
/// 首页导航入口表
/// </summary>
public DbSet<HomeNavigation> HomeNavigations { get; set; } = null!;
#endregion
#region

View File

@ -36,12 +36,6 @@ public class AssessmentType
[MaxLength(500)]
public string? ImageUrl { get; set; }
/// <summary>
/// 跳转链接URL
/// </summary>
[MaxLength(500)]
public string? LinkUrl { get; set; }
/// <summary>
/// 详情横幅图URL
/// </summary>

View File

@ -0,0 +1,62 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MiAssessment.Admin.Business.Entities;
/// <summary>
/// 首页导航入口表
/// </summary>
[Table("home_navigations")]
public class HomeNavigation
{
/// <summary>
/// 主键ID
/// </summary>
[Key]
public long Id { get; set; }
/// <summary>
/// 导航名称
/// </summary>
[Required]
[MaxLength(50)]
public string Name { get; set; } = null!;
/// <summary>
/// 图标图片URL
/// </summary>
[MaxLength(500)]
public string? ImageUrl { get; set; }
/// <summary>
/// 跳转链接URL
/// </summary>
[MaxLength(500)]
public string? LinkUrl { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
/// <summary>
/// 状态0即将上线 1已上线
/// </summary>
public int Status { get; set; } = 1;
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdateTime { get; set; }
/// <summary>
/// 软删除标记
/// </summary>
public bool IsDeleted { get; set; }
}

View File

@ -25,11 +25,6 @@ public class AssessmentTypeDto
/// </summary>
public string? ImageUrl { get; set; }
/// <summary>
/// 跳转链接URL
/// </summary>
public string? LinkUrl { get; set; }
/// <summary>
/// 详情横幅图URL
/// </summary>

View File

@ -27,12 +27,6 @@ public class CreateAssessmentTypeRequest
[MaxLength(500, ErrorMessage = "图片URL不能超过500个字符")]
public string? ImageUrl { get; set; }
/// <summary>
/// 跳转链接URL
/// </summary>
[MaxLength(500, ErrorMessage = "跳转链接URL不能超过500个字符")]
public string? LinkUrl { get; set; }
/// <summary>
/// 详情横幅图URL
/// </summary>

View File

@ -33,12 +33,6 @@ public class UpdateAssessmentTypeRequest
[MaxLength(500, ErrorMessage = "图片URL不能超过500个字符")]
public string? ImageUrl { get; set; }
/// <summary>
/// 跳转链接URL
/// </summary>
[MaxLength(500, ErrorMessage = "跳转链接URL不能超过500个字符")]
public string? LinkUrl { get; set; }
/// <summary>
/// 详情横幅图URL
/// </summary>

View File

@ -0,0 +1,38 @@
using System.ComponentModel.DataAnnotations;
namespace MiAssessment.Admin.Business.Models.Content;
/// <summary>
/// 创建首页导航请求
/// </summary>
public class CreateHomeNavigationRequest
{
/// <summary>
/// 导航名称(必填)
/// </summary>
[Required(ErrorMessage = "导航名称不能为空")]
[MaxLength(50, ErrorMessage = "导航名称不能超过50个字符")]
public string Name { get; set; } = null!;
/// <summary>
/// 图标图片URL
/// </summary>
[MaxLength(500, ErrorMessage = "图片URL不能超过500个字符")]
public string? ImageUrl { get; set; }
/// <summary>
/// 跳转链接URL
/// </summary>
[MaxLength(500, ErrorMessage = "跳转链接URL不能超过500个字符")]
public string? LinkUrl { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
/// <summary>
/// 状态0即将上线 1已上线
/// </summary>
public int Status { get; set; } = 1;
}

View File

@ -0,0 +1,47 @@
namespace MiAssessment.Admin.Business.Models.Content;
/// <summary>
/// 首页导航入口 DTO
/// </summary>
public class HomeNavigationDto
{
/// <summary>
/// 主键ID
/// </summary>
public long Id { get; set; }
/// <summary>
/// 导航名称
/// </summary>
public string Name { get; set; } = null!;
/// <summary>
/// 图标图片URL
/// </summary>
public string? ImageUrl { get; set; }
/// <summary>
/// 跳转链接URL
/// </summary>
public string? LinkUrl { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
/// <summary>
/// 状态0即将上线 1已上线
/// </summary>
public int Status { get; set; }
/// <summary>
/// 状态名称
/// </summary>
public string StatusName { get; set; } = null!;
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
}

View File

@ -0,0 +1,19 @@
using MiAssessment.Admin.Business.Models.Common;
namespace MiAssessment.Admin.Business.Models.Content;
/// <summary>
/// 首页导航查询请求
/// </summary>
public class HomeNavigationQueryRequest : PagedRequest
{
/// <summary>
/// 名称筛选
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 状态筛选
/// </summary>
public int? Status { get; set; }
}

View File

@ -0,0 +1,44 @@
using System.ComponentModel.DataAnnotations;
namespace MiAssessment.Admin.Business.Models.Content;
/// <summary>
/// 更新首页导航请求
/// </summary>
public class UpdateHomeNavigationRequest
{
/// <summary>
/// 导航ID必填
/// </summary>
[Required(ErrorMessage = "导航ID不能为空")]
public long Id { get; set; }
/// <summary>
/// 导航名称(必填)
/// </summary>
[Required(ErrorMessage = "导航名称不能为空")]
[MaxLength(50, ErrorMessage = "导航名称不能超过50个字符")]
public string Name { get; set; } = null!;
/// <summary>
/// 图标图片URL
/// </summary>
[MaxLength(500, ErrorMessage = "图片URL不能超过500个字符")]
public string? ImageUrl { get; set; }
/// <summary>
/// 跳转链接URL
/// </summary>
[MaxLength(500, ErrorMessage = "跳转链接URL不能超过500个字符")]
public string? LinkUrl { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
/// <summary>
/// 状态0即将上线 1已上线
/// </summary>
public int Status { get; set; }
}

View File

@ -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;

View File

@ -555,6 +555,15 @@ public class ContentService : IContentService
return PositionNames.TryGetValue(position, out var name) ? name : "未知";
}
/// <summary>
/// 导航状态名称映射
/// </summary>
private static readonly Dictionary<int, string> NavigationStatusNames = new()
{
{ 0, "即将上线" },
{ 1, "已上线" }
};
/// <summary>
/// 验证 Position 值
/// </summary>
@ -568,4 +577,166 @@ public class ContentService : IContentService
}
#endregion
#region HomeNavigation
/// <inheritdoc />
public async Task<PagedResult<HomeNavigationDto>> 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<HomeNavigationDto>.Create(items, total, request.Page, request.PageSize);
}
/// <inheritdoc />
public async Task<HomeNavigationDto> 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;
}
/// <inheritdoc />
public async Task<long> 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;
}
/// <inheritdoc />
public async Task<bool> 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;
}
/// <inheritdoc />
public async Task<bool> 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;
}
/// <inheritdoc />
public async Task<bool> 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
}

View File

@ -109,4 +109,38 @@ public interface IContentService
Task<bool> UpdatePromotionStatusAsync(long id, int status);
#endregion
#region HomeNavigation
/// <summary>
/// 获取首页导航列表
/// </summary>
Task<PagedResult<HomeNavigationDto>> GetNavigationListAsync(HomeNavigationQueryRequest request);
/// <summary>
/// 根据ID获取首页导航
/// </summary>
Task<HomeNavigationDto> GetNavigationByIdAsync(long id);
/// <summary>
/// 创建首页导航
/// </summary>
Task<long> CreateNavigationAsync(CreateHomeNavigationRequest request);
/// <summary>
/// 更新首页导航
/// </summary>
Task<bool> UpdateNavigationAsync(UpdateHomeNavigationRequest request);
/// <summary>
/// 删除首页导航(软删除)
/// </summary>
Task<bool> DeleteNavigationAsync(long id);
/// <summary>
/// 更新首页导航状态
/// </summary>
Task<bool> UpdateNavigationStatusAsync(long id, int status);
#endregion
}

View File

@ -85,6 +85,32 @@ public class HomeController : ControllerBase
}
}
/// <summary>
/// 获取首页导航入口列表
/// </summary>
/// <remarks>
/// GET /api/home/getNavigationList
///
/// 返回所有启用状态的首页导航入口按Sort降序排列
/// 不需要用户登录认证
/// </remarks>
/// <returns>导航入口列表</returns>
[HttpGet("getNavigationList")]
[ProducesResponseType(typeof(ApiResponse<List<HomeNavigationDto>>), StatusCodes.Status200OK)]
public async Task<ApiResponse<List<HomeNavigationDto>>> GetNavigationList()
{
try
{
var navigations = await _homeService.GetNavigationListAsync();
return ApiResponse<List<HomeNavigationDto>>.Success(navigations);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get navigation list");
return ApiResponse<List<HomeNavigationDto>>.Fail("获取导航入口列表失败");
}
}
/// <summary>
/// 获取宣传图列表
/// </summary>

View File

@ -1,5 +1,6 @@
using MiAssessment.Model.Models.Home;
namespace MiAssessment.Core.Interfaces;
/// <summary>
@ -25,6 +26,15 @@ public interface IHomeService
/// <returns>测评类型列表</returns>
Task<List<AssessmentTypeDto>> GetAssessmentListAsync();
/// <summary>
/// 获取首页导航入口列表
/// </summary>
/// <remarks>
/// 返回所有启用状态的首页导航入口按Sort降序排列
/// </remarks>
/// <returns>导航入口列表</returns>
Task<List<HomeNavigationDto>> GetNavigationListAsync();
/// <summary>
/// 获取宣传图列表
/// </summary>

View File

@ -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;
}
/// <inheritdoc />
public async Task<List<HomeNavigationDto>> 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;
}
/// <inheritdoc />
public async Task<List<PromotionDto>> GetPromotionListAsync()
{

View File

@ -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<Order> Orders { get; set; }
public virtual DbSet<HomeNavigation> 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<HomeNavigation>(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);
}

View File

@ -36,12 +36,6 @@ public class AssessmentType
[MaxLength(500)]
public string? ImageUrl { get; set; }
/// <summary>
/// 跳转链接URL
/// </summary>
[MaxLength(500)]
public string? LinkUrl { get; set; }
/// <summary>
/// 详情横幅图URL
/// </summary>

View File

@ -0,0 +1,62 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MiAssessment.Model.Entities;
/// <summary>
/// 首页导航入口表
/// </summary>
[Table("home_navigations")]
public class HomeNavigation
{
/// <summary>
/// 主键ID
/// </summary>
[Key]
public long Id { get; set; }
/// <summary>
/// 导航名称
/// </summary>
[Required]
[MaxLength(50)]
public string Name { get; set; } = null!;
/// <summary>
/// 图标图片URL
/// </summary>
[MaxLength(500)]
public string? ImageUrl { get; set; }
/// <summary>
/// 跳转链接URL
/// </summary>
[MaxLength(500)]
public string? LinkUrl { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
/// <summary>
/// 状态0即将上线 1已上线
/// </summary>
public int Status { get; set; } = 1;
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdateTime { get; set; }
/// <summary>
/// 软删除标记
/// </summary>
public bool IsDeleted { get; set; }
}

View File

@ -25,11 +25,6 @@ public class AssessmentTypeDto
/// </summary>
public string ImageUrl { get; set; } = null!;
/// <summary>
/// 跳转链接URL
/// </summary>
public string? LinkUrl { get; set; }
/// <summary>
/// 价格
/// </summary>

View File

@ -0,0 +1,32 @@
namespace MiAssessment.Model.Models.Home;
/// <summary>
/// 首页导航入口数据传输对象
/// </summary>
public class HomeNavigationDto
{
/// <summary>
/// 导航ID
/// </summary>
public long Id { get; set; }
/// <summary>
/// 导航名称
/// </summary>
public string Name { get; set; } = null!;
/// <summary>
/// 图标图片URL
/// </summary>
public string ImageUrl { get; set; } = null!;
/// <summary>
/// 跳转链接URL
/// </summary>
public string? LinkUrl { get; set; }
/// <summary>
/// 状态0即将上线 1已上线
/// </summary>
public int Status { get; set; }
}

View File

@ -20,6 +20,14 @@ export function getAssessmentList() {
return get('/home/getAssessmentList')
}
/**
* 获取首页导航入口列表
* @returns {Promise<Object>}
*/
export function getNavigationList() {
return get('/home/getNavigationList')
}
/**
* 获取宣传图列表
* @returns {Promise<Object>}
@ -31,5 +39,6 @@ export function getPromotionList() {
export default {
getBannerList,
getAssessmentList,
getNavigationList,
getPromotionList
}

View File

@ -44,7 +44,7 @@
</view>
<!-- 专业测评入口 -->
<view class="assessment-section" v-if="assessmentList.length > 0">
<view class="assessment-section" v-if="navigationList.length > 0">
<view class="section-header">
<view class="section-indicator"></view>
<text class="section-title">专业测评</text>
@ -52,9 +52,9 @@
<view class="assessment-grid">
<view
class="assessment-card"
v-for="(item, index) in assessmentList"
v-for="(item, index) in navigationList"
:key="index"
@click="handleAssessmentClick(item)"
@click="handleNavigationClick(item)"
>
<!-- 即将上线标签 -->
<view v-if="item.status === 0" class="coming-soon-tag">
@ -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 })
}
})
}