diff --git a/server/MiAssessment/migrations/add_position_actiontype_to_home_navigations.sql b/server/MiAssessment/migrations/add_position_actiontype_to_home_navigations.sql new file mode 100644 index 0000000..1c3d64c --- /dev/null +++ b/server/MiAssessment/migrations/add_position_actiontype_to_home_navigations.sql @@ -0,0 +1,22 @@ +-- ============================================================ +-- 迁移脚本:home_navigations 表新增 Position 和 ActionType 列 +-- 功能:首页「更多」区域模块化配置 +-- ============================================================ + +-- 1. 新增列 +ALTER TABLE home_navigations ADD Position INT NOT NULL DEFAULT 1; +ALTER TABLE home_navigations ADD ActionType INT NOT NULL DEFAULT 1; + +-- 2. 现有记录设置为专业测评区域 + 跳转页面 +UPDATE home_navigations SET Position = 1, ActionType = 1 WHERE IsDeleted = 0; + +-- 3. 将原 Status=0 的"即将上线"记录改为 ActionType=3, Status=1 +UPDATE home_navigations SET ActionType = 3, Status = 1 WHERE Status = 0 AND IsDeleted = 0; +-- 注意:Status=2 的记录也改为 ActionType=3 +UPDATE home_navigations SET ActionType = 3, Status = 1 WHERE Status = 2 AND IsDeleted = 0; + +-- 4. 插入「更多」区域初始数据 +INSERT INTO home_navigations (Name, ImageUrl, LinkUrl, Sort, Status, Position, ActionType, CreateTime, UpdateTime, IsDeleted) +VALUES + (N'学习方案', NULL, NULL, 2, 1, 2, 3, GETDATE(), GETDATE(), 0), + (N'详细咨询', NULL, NULL, 1, 1, 2, 2, GETDATE(), GETDATE(), 0); diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Controllers/ContentController.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Controllers/ContentController.cs index cee07d6..7aa5cce 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Controllers/ContentController.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Controllers/ContentController.cs @@ -440,6 +440,12 @@ public class ContentController : BusinessControllerBase return ValidationError("导航名称不能为空"); } + // ActionType=1(跳转页面)时 LinkUrl 必填 + if (request.ActionType == 1 && string.IsNullOrWhiteSpace(request.LinkUrl)) + { + return Error(ErrorCodes.ParamError, "跳转链接不能为空"); + } + try { var id = await _contentService.CreateNavigationAsync(request); @@ -474,6 +480,12 @@ public class ContentController : BusinessControllerBase return ValidationError("导航名称不能为空"); } + // ActionType=1(跳转页面)时 LinkUrl 必填 + if (request.ActionType == 1 && string.IsNullOrWhiteSpace(request.LinkUrl)) + { + return Error(ErrorCodes.ParamError, "跳转链接不能为空"); + } + try { var result = await _contentService.UpdateNavigationAsync(request); @@ -532,9 +544,9 @@ public class ContentController : BusinessControllerBase return ValidationError("导航ID无效"); } - if (request.Status < 0 || request.Status > 2) + if (request.Status != 0 && request.Status != 1) { - return ValidationError("状态值无效,只能为0(下线)、1(上线)或2(即将上线)"); + return Error(ErrorCodes.ParamError, "状态值无效"); } try diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/HomeNavigation.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/HomeNavigation.cs index 2df4ac2..90d36cd 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/HomeNavigation.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/HomeNavigation.cs @@ -41,7 +41,17 @@ public class HomeNavigation public int Sort { get; set; } /// - /// 状态:0即将上线 1已上线 + /// 区域标识:1=专业测评区域,2=更多区域 + /// + public int Position { get; set; } = 1; + + /// + /// 动作类型:1=跳转页面,2=弹客服二维码,3=即将上线 + /// + public int ActionType { get; set; } = 1; + + /// + /// 状态:0=禁用,1=启用 /// public int Status { get; set; } = 1; diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/CreateHomeNavigationRequest.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/CreateHomeNavigationRequest.cs index 998f6f8..29ab07c 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/CreateHomeNavigationRequest.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/CreateHomeNavigationRequest.cs @@ -32,7 +32,17 @@ public class CreateHomeNavigationRequest public int Sort { get; set; } /// - /// 状态:0即将上线 1已上线 + /// 区域标识:1=专业测评区域,2=更多区域(默认1) + /// + public int Position { get; set; } = 1; + + /// + /// 动作类型:1=跳转页面,2=弹客服二维码,3=即将上线(默认1) + /// + public int ActionType { get; set; } = 1; + + /// + /// 状态: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 index 34669b1..20d1cb1 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/HomeNavigationDto.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/HomeNavigationDto.cs @@ -31,7 +31,27 @@ public class HomeNavigationDto public int Sort { get; set; } /// - /// 状态:0即将上线 1已上线 + /// 区域标识 + /// + public int Position { get; set; } + + /// + /// 区域名称(专业测评区域/更多区域) + /// + public string PositionName { get; set; } = null!; + + /// + /// 动作类型 + /// + public int ActionType { get; set; } + + /// + /// 动作类型名称(跳转页面/弹客服二维码/即将上线) + /// + public string ActionTypeName { get; set; } = null!; + + /// + /// 状态:0=禁用,1=启用 /// public int Status { 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 index 13d1628..8bb78a4 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/HomeNavigationQueryRequest.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/HomeNavigationQueryRequest.cs @@ -16,4 +16,9 @@ public class HomeNavigationQueryRequest : PagedRequest /// 状态筛选 /// public int? Status { get; set; } + + /// + /// 区域标识筛选 + /// + public int? Position { 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 index b67dc9a..2fef3ec 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/UpdateHomeNavigationRequest.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Content/UpdateHomeNavigationRequest.cs @@ -38,7 +38,17 @@ public class UpdateHomeNavigationRequest public int Sort { get; set; } /// - /// 状态:0即将上线 1已上线 + /// 区域标识:1=专业测评区域,2=更多区域 + /// + public int Position { get; set; } + + /// + /// 动作类型:1=跳转页面,2=弹客服二维码,3=即将上线 + /// + public int ActionType { get; set; } + + /// + /// 状态:0=禁用,1=启用 /// public int Status { get; set; } } diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ContentService.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ContentService.cs index 414b2b0..89c6004 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ContentService.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ContentService.cs @@ -564,13 +564,31 @@ public class ContentService : IContentService } /// - /// 导航状态名称映射 + /// 导航状态名称映射:0=禁用,1=启用 /// private static readonly Dictionary NavigationStatusNames = new() { - { 0, "下线" }, - { 1, "上线" }, - { 2, "即将上线" } + { 0, "禁用" }, + { 1, "启用" } + }; + + /// + /// 导航区域标识名称映射 + /// + private static readonly Dictionary NavigationPositionNames = new() + { + { 1, "专业测评区域" }, + { 2, "更多区域" } + }; + + /// + /// 导航动作类型名称映射 + /// + private static readonly Dictionary ActionTypeNames = new() + { + { 1, "跳转页面" }, + { 2, "弹客服二维码" }, + { 3, "即将上线" } }; /// @@ -583,6 +601,26 @@ public class ContentService : IContentService return NavigationStatusNames.TryGetValue(status, out var name) ? name : "未知"; } + /// + /// 获取导航区域名称 + /// + /// 区域标识 + /// 区域名称 + private static string GetNavigationPositionName(int position) + { + return NavigationPositionNames.TryGetValue(position, out var name) ? name : "未知"; + } + + /// + /// 获取导航动作类型名称 + /// + /// 动作类型 + /// 动作类型名称 + private static string GetActionTypeName(int actionType) + { + return ActionTypeNames.TryGetValue(actionType, out var name) ? name : "未知"; + } + /// /// 验证 Position 值 /// @@ -616,6 +654,11 @@ public class ContentService : IContentService query = query.Where(n => n.Status == request.Status.Value); } + if (request.Position.HasValue) + { + query = query.Where(n => n.Position == request.Position.Value); + } + var total = await query.CountAsync(); var items = await query @@ -630,15 +673,19 @@ public class ContentService : IContentService ImageUrl = n.ImageUrl, LinkUrl = n.LinkUrl, Sort = n.Sort, + Position = n.Position, + ActionType = n.ActionType, Status = n.Status, CreateTime = n.CreateTime }) .ToListAsync(); - // 在内存中映射状态名称(避免 EF Core LINQ 翻译问题) + // 在内存中映射名称(避免 EF Core LINQ 翻译问题) foreach (var item in items) { item.StatusName = GetNavigationStatusName(item.Status); + item.PositionName = GetNavigationPositionName(item.Position); + item.ActionTypeName = GetActionTypeName(item.ActionType); } return PagedResult.Create(items, total, request.Page, request.PageSize); @@ -657,6 +704,8 @@ public class ContentService : IContentService ImageUrl = n.ImageUrl, LinkUrl = n.LinkUrl, Sort = n.Sort, + Position = n.Position, + ActionType = n.ActionType, Status = n.Status, CreateTime = n.CreateTime }) @@ -667,8 +716,10 @@ public class ContentService : IContentService throw new BusinessException(ErrorCodes.NavigationNotFound, "首页导航不存在"); } - // 在内存中映射状态名称 + // 在内存中映射名称 nav.StatusName = GetNavigationStatusName(nav.Status); + nav.PositionName = GetNavigationPositionName(nav.Position); + nav.ActionTypeName = GetActionTypeName(nav.ActionType); return nav; } @@ -681,12 +732,20 @@ public class ContentService : IContentService throw new BusinessException(ErrorCodes.ParamError, "导航名称不能为空"); } + // ActionType=1(跳转页面)时 LinkUrl 必填 + if (request.ActionType == 1 && string.IsNullOrWhiteSpace(request.LinkUrl)) + { + throw new BusinessException(ErrorCodes.ParamError, "跳转链接不能为空"); + } + var entity = new HomeNavigation { Name = request.Name, ImageUrl = request.ImageUrl, LinkUrl = request.LinkUrl, Sort = request.Sort, + Position = request.Position, + ActionType = request.ActionType, Status = request.Status, CreateTime = DateTime.Now, UpdateTime = DateTime.Now, @@ -717,10 +776,18 @@ public class ContentService : IContentService throw new BusinessException(ErrorCodes.ParamError, "导航名称不能为空"); } + // ActionType=1(跳转页面)时 LinkUrl 必填 + if (request.ActionType == 1 && string.IsNullOrWhiteSpace(request.LinkUrl)) + { + throw new BusinessException(ErrorCodes.ParamError, "跳转链接不能为空"); + } + entity.Name = request.Name; entity.ImageUrl = request.ImageUrl; entity.LinkUrl = request.LinkUrl; entity.Sort = request.Sort; + entity.Position = request.Position; + entity.ActionType = request.ActionType; entity.Status = request.Status; entity.UpdateTime = DateTime.Now; diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/business/content.ts b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/business/content.ts index f340094..5bc1654 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/business/content.ts +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/business/content.ts @@ -308,10 +308,18 @@ export interface NavigationItem { linkUrl: string /** 排序值 */ sort: number - /** 状态 (0: 即将上线, 1: 已上线) */ + /** 状态 (0: 禁用, 1: 启用) */ status: number /** 状态名称 */ statusName: string + /** 区域标识 (1: 专业测评区域, 2: 更多区域) */ + position: number + /** 区域名称 */ + positionName: string + /** 动作类型 (1: 跳转页面, 2: 弹客服二维码, 3: 即将上线) */ + actionType: number + /** 动作类型名称 */ + actionTypeName: string /** 创建时间 */ createTime: string } @@ -324,6 +332,8 @@ export interface NavigationQuery extends PagedRequest { name?: string /** 状态筛选 */ status?: number + /** 区域标识筛选 */ + position?: number } /** @@ -340,6 +350,10 @@ export interface CreateNavigationRequest { sort: number /** 状态 */ status: number + /** 区域标识 (1: 专业测评区域, 2: 更多区域) */ + position: number + /** 动作类型 (1: 跳转页面, 2: 弹客服二维码, 3: 即将上线) */ + actionType: number } /** diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/business/content/navigation/index.vue b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/business/content/navigation/index.vue index ab04b2f..50d022f 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/business/content/navigation/index.vue +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/business/content/navigation/index.vue @@ -29,9 +29,14 @@ - - - + + + + + + + + @@ -74,6 +79,12 @@ + + + + + + @@ -124,7 +129,9 @@ import { ref, onMounted } from 'vue' import { useUserStore } from '@/store/user.js' import { useNavbar } from '@/composables/useNavbar.js' import { getBannerList, getNavigationList } from '@/api/home.js' +import { getContactInfo } from '@/api/system.js' import Loading from '@/components/Loading/index.vue' +import Popup from '@/components/Popup/index.vue' const userStore = useUserStore() const { statusBarHeight, navbarHeight, totalNavbarHeight } = useNavbar() @@ -133,7 +140,10 @@ const { statusBarHeight, navbarHeight, totalNavbarHeight } = useNavbar() const pageLoading = ref(true) const isRefreshing = ref(false) const bannerList = ref([]) -const navigationList = ref([]) +const assessmentList = ref([]) +const moreList = ref([]) +const qrcodeUrl = ref('') +const showQrPopup = ref(false) /** * 加载Banner数据 @@ -150,16 +160,44 @@ async function loadBannerList() { } /** - * 加载首页导航入口数据 + * 加载专业测评区域数据(position=1) */ -async function loadNavigationList() { +async function loadAssessmentList() { try { - const res = await getNavigationList() + const res = await getNavigationList({ position: 1 }) if (res && res.code === 0 && res.data) { - navigationList.value = Array.isArray(res.data) ? res.data : (res.data.list || []) + assessmentList.value = Array.isArray(res.data) ? res.data : (res.data.list || []) } } catch (error) { - console.error('加载导航入口失败:', error) + console.error('加载专业测评数据失败:', error) + } +} + +/** + * 加载更多区域数据(position=2) + */ +async function loadMoreList() { + try { + const res = await getNavigationList({ position: 2 }) + if (res && res.code === 0 && res.data) { + moreList.value = Array.isArray(res.data) ? res.data : (res.data.list || []) + } + } catch (error) { + console.error('加载更多区域数据失败:', error) + } +} + +/** + * 加载客服二维码URL + */ +async function loadContactInfo() { + try { + const res = await getContactInfo() + if (res && res.code === 0 && res.data) { + qrcodeUrl.value = res.data.qrcodeUrl || '' + } + } catch (error) { + console.error('加载客服二维码失败:', error) } } @@ -172,7 +210,9 @@ async function initPageData() { userStore.restoreFromStorage() await Promise.all([ loadBannerList(), - loadNavigationList() + loadAssessmentList(), + loadMoreList(), + loadContactInfo() ]) } finally { pageLoading.value = false @@ -199,13 +239,18 @@ function handleBannerClick(item) { } /** - * 处理导航入口点击 + * 处理卡片点击(根据 ActionType 分发) */ -function handleNavigationClick(item) { - if (item.status === 2) { - uni.showToast({ title: '该功能即将上线', icon: 'none', duration: 2000 }) +function handleCardClick(item) { + if (item.actionType === 3) { + uni.showToast({ title: '即将上线', icon: 'none', duration: 2000 }) return } + if (item.actionType === 2) { + showQrPopup.value = true + return + } + // actionType === 1: 跳转页面 if (!item.linkUrl) return uni.navigateTo({ url: item.linkUrl, @@ -222,20 +267,6 @@ function goToPlanner() { uni.navigateTo({ url: '/pages/planner/list/index' }) } -/** - * 跳转学习方案(业务详情页) - */ -function goToStudyPlan() { - uni.navigateTo({ url: '/pages/business/detail/index?type=study-plan' }) -} - -/** - * 跳转详细咨询(业务详情页) - */ -function goToConsult() { - uni.navigateTo({ url: '/pages/business/detail/index?type=consult' }) -} - /** * 下拉刷新 */ @@ -349,24 +380,10 @@ onMounted(() => { .assessment-card { position: relative; flex: 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; height: 240rpx; border-radius: $border-radius-xl; overflow: hidden; - // 第一个卡片 - 红/橙渐变 - &--0 { - background: linear-gradient(135deg, #FF7B6B, #FF5B5B); - } - - // 第二个卡片 - 橙/黄渐变 - &--1 { - background: linear-gradient(135deg, #FFD080, #FFB347); - } - .coming-soon-tag { position: absolute; top: 0; @@ -382,16 +399,9 @@ onMounted(() => { } } - .assessment-icon { - width: 100rpx; - height: 100rpx; - margin-bottom: $spacing-sm; - } - - .assessment-name { - font-size: $font-size-md; - font-weight: $font-weight-bold; - color: $text-white; + .assessment-image { + width: 100%; + height: 100%; } } @@ -412,14 +422,15 @@ onMounted(() => { } } -// 更多 - 两列卡片 +// 更多 - 网格布局,每行2个 .more-grid { display: flex; + flex-wrap: wrap; gap: $spacing-md; } .more-card { - flex: 1; + width: calc(50% - #{$spacing-md} / 2); display: flex; align-items: center; justify-content: space-between; @@ -427,13 +438,12 @@ onMounted(() => { padding: 0 $spacing-lg; border-radius: $border-radius-xl; overflow: hidden; + background-color: $bg-gray; + box-sizing: border-box; - &--plan { - background: linear-gradient(135deg, #FFF5EB, #FFE8D5); - } - - &--consult { - background: linear-gradient(135deg, #FFE8EC, #FFD5DC); + // 奇数个时最后一个占满整行 + &--full { + width: 100%; } &__title {