21
This commit is contained in:
parent
da590c2939
commit
15357e0fec
|
|
@ -0,0 +1,29 @@
|
|||
-- =============================================
|
||||
-- Business Pages Table - Add Icon Fields
|
||||
-- 学业邑规划 - MiAssessment
|
||||
--
|
||||
-- 新增团队页 icon 配置字段:
|
||||
-- IconUrl - 未选中状态图标
|
||||
-- ActiveIconUrl - 选中状态图标
|
||||
--
|
||||
-- Database: SQL Server 2022
|
||||
-- =============================================
|
||||
|
||||
USE [MiAssessment_Business];
|
||||
GO
|
||||
|
||||
-- 新增 IconUrl 字段
|
||||
IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'[dbo].[business_pages]') AND name = 'IconUrl')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[business_pages] ADD [IconUrl] NVARCHAR(500) NULL;
|
||||
PRINT N'Column IconUrl added successfully';
|
||||
END
|
||||
GO
|
||||
|
||||
-- 新增 ActiveIconUrl 字段
|
||||
IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'[dbo].[business_pages]') AND name = 'ActiveIconUrl')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[business_pages] ADD [ActiveIconUrl] NVARCHAR(500) NULL;
|
||||
PRINT N'Column ActiveIconUrl added successfully';
|
||||
END
|
||||
GO
|
||||
|
|
@ -24,7 +24,19 @@ public class BusinessPage
|
|||
public string Title { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 图片URL
|
||||
/// 图标URL(未选中状态)
|
||||
/// </summary>
|
||||
[MaxLength(500)]
|
||||
public string? IconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标URL(选中状态)
|
||||
/// </summary>
|
||||
[MaxLength(500)]
|
||||
public string? ActiveIconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 详情图片URL
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MaxLength(500)]
|
||||
|
|
|
|||
|
|
@ -16,7 +16,17 @@ public class BusinessPageDto
|
|||
public string Title { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 图片URL(长图)
|
||||
/// 图标URL(未选中状态)
|
||||
/// </summary>
|
||||
public string? IconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标URL(选中状态)
|
||||
/// </summary>
|
||||
public string? ActiveIconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 详情图片URL(长图)
|
||||
/// </summary>
|
||||
public string ImageUrl { get; set; } = null!;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,19 @@ public class CreateBusinessPageRequest
|
|||
public string Title { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 图片URL(必填,长图)
|
||||
/// 图标URL(未选中状态)
|
||||
/// </summary>
|
||||
[MaxLength(500, ErrorMessage = "图标URL长度不能超过500个字符")]
|
||||
public string? IconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标URL(选中状态)
|
||||
/// </summary>
|
||||
[MaxLength(500, ErrorMessage = "选中图标URL长度不能超过500个字符")]
|
||||
public string? ActiveIconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 详情图片URL(必填,长图)
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "图片URL不能为空")]
|
||||
[MaxLength(500, ErrorMessage = "图片URL长度不能超过500个字符")]
|
||||
|
|
|
|||
|
|
@ -21,7 +21,19 @@ public class UpdateBusinessPageRequest
|
|||
public string Title { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 图片URL(必填,长图)
|
||||
/// 图标URL(未选中状态)
|
||||
/// </summary>
|
||||
[MaxLength(500, ErrorMessage = "图标URL长度不能超过500个字符")]
|
||||
public string? IconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标URL(选中状态)
|
||||
/// </summary>
|
||||
[MaxLength(500, ErrorMessage = "选中图标URL长度不能超过500个字符")]
|
||||
public string? ActiveIconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 详情图片URL(必填,长图)
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "图片URL不能为空")]
|
||||
[MaxLength(500, ErrorMessage = "图片URL长度不能超过500个字符")]
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ public class BusinessPageService : IBusinessPageService
|
|||
{
|
||||
Id = p.Id,
|
||||
Title = p.Title,
|
||||
IconUrl = p.IconUrl,
|
||||
ActiveIconUrl = p.ActiveIconUrl,
|
||||
ImageUrl = p.ImageUrl,
|
||||
HasActionButton = p.HasActionButton,
|
||||
ActionButtonText = p.ActionButtonText,
|
||||
|
|
@ -90,6 +92,8 @@ public class BusinessPageService : IBusinessPageService
|
|||
{
|
||||
Id = p.Id,
|
||||
Title = p.Title,
|
||||
IconUrl = p.IconUrl,
|
||||
ActiveIconUrl = p.ActiveIconUrl,
|
||||
ImageUrl = p.ImageUrl,
|
||||
HasActionButton = p.HasActionButton,
|
||||
ActionButtonText = p.ActionButtonText,
|
||||
|
|
@ -126,6 +130,8 @@ public class BusinessPageService : IBusinessPageService
|
|||
var entity = new BusinessPage
|
||||
{
|
||||
Title = request.Title,
|
||||
IconUrl = request.IconUrl,
|
||||
ActiveIconUrl = request.ActiveIconUrl,
|
||||
ImageUrl = request.ImageUrl,
|
||||
HasActionButton = request.HasActionButton,
|
||||
ActionButtonText = request.ActionButtonText,
|
||||
|
|
@ -166,6 +172,8 @@ public class BusinessPageService : IBusinessPageService
|
|||
|
||||
// 更新字段
|
||||
entity.Title = request.Title;
|
||||
entity.IconUrl = request.IconUrl;
|
||||
entity.ActiveIconUrl = request.ActiveIconUrl;
|
||||
entity.ImageUrl = request.ImageUrl;
|
||||
entity.HasActionButton = request.HasActionButton;
|
||||
entity.ActionButtonText = request.ActionButtonText;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import type { PagedRequest, PagedResult, UpdateStatusRequest } from '@/types/com
|
|||
export interface BusinessPageItem {
|
||||
id: number
|
||||
title: string
|
||||
iconUrl?: string
|
||||
activeIconUrl?: string
|
||||
imageUrl: string
|
||||
hasActionButton: boolean
|
||||
actionButtonText?: string
|
||||
|
|
@ -31,6 +33,8 @@ export interface BusinessPageQuery extends PagedRequest {
|
|||
/** 创建请求 */
|
||||
export interface CreateBusinessPageRequest {
|
||||
title: string
|
||||
iconUrl?: string
|
||||
activeIconUrl?: string
|
||||
imageUrl: string
|
||||
hasActionButton: boolean
|
||||
actionButtonText?: string
|
||||
|
|
|
|||
|
|
@ -150,6 +150,24 @@
|
|||
<el-input v-model="state.formData.title" placeholder="请输入标题" maxlength="100" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="图标(未选中)">
|
||||
<ImageUpload
|
||||
v-model="state.formData.iconUrl"
|
||||
placeholder="点击上传未选中图标"
|
||||
tip="建议尺寸64x64px,png格式"
|
||||
:max-size="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="图标(选中)">
|
||||
<ImageUpload
|
||||
v-model="state.formData.activeIconUrl"
|
||||
placeholder="点击上传选中图标"
|
||||
tip="建议尺寸64x64px,png格式"
|
||||
:max-size="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="介绍图片" prop="imageUrl" required>
|
||||
<ImageUpload
|
||||
v-model="state.formData.imageUrl"
|
||||
|
|
@ -215,6 +233,8 @@ import { DictSelect, ImageUpload } from '@/components'
|
|||
interface FormData {
|
||||
id?: number
|
||||
title: string
|
||||
iconUrl: string
|
||||
activeIconUrl: string
|
||||
imageUrl: string
|
||||
hasActionButton: boolean
|
||||
actionButtonText: string
|
||||
|
|
@ -269,6 +289,8 @@ const formRules: FormRules = {
|
|||
function getDefaultFormData(): FormData {
|
||||
return {
|
||||
title: '',
|
||||
iconUrl: '',
|
||||
activeIconUrl: '',
|
||||
imageUrl: '',
|
||||
hasActionButton: false,
|
||||
actionButtonText: '',
|
||||
|
|
@ -325,6 +347,8 @@ function handleEdit(row: BusinessPageItem) {
|
|||
state.formData = {
|
||||
id: row.id,
|
||||
title: row.title || '',
|
||||
iconUrl: row.iconUrl || '',
|
||||
activeIconUrl: row.activeIconUrl || '',
|
||||
imageUrl: row.imageUrl,
|
||||
hasActionButton: row.hasActionButton,
|
||||
actionButtonText: row.actionButtonText || '',
|
||||
|
|
@ -376,6 +400,8 @@ async function handleSubmit() {
|
|||
const fd = state.formData
|
||||
const data: CreateBusinessPageRequest | UpdateBusinessPageRequest = {
|
||||
title: fd.title,
|
||||
iconUrl: fd.iconUrl || undefined,
|
||||
activeIconUrl: fd.activeIconUrl || undefined,
|
||||
imageUrl: fd.imageUrl,
|
||||
hasActionButton: fd.hasActionButton,
|
||||
actionButtonText: fd.hasActionButton ? fd.actionButtonText : undefined,
|
||||
|
|
|
|||
|
|
@ -32,18 +32,28 @@ public class TeamService : ITeamService
|
|||
{
|
||||
_logger.LogDebug("获取团队介绍信息");
|
||||
|
||||
var images = await _dbContext.Promotions
|
||||
// 从 business_pages 表查询启用状态的业务页,按排序降序
|
||||
var pages = await _dbContext.BusinessPages
|
||||
.AsNoTracking()
|
||||
.Where(p => p.Position == 2 && p.Status == 1 && !p.IsDeleted)
|
||||
.Where(p => p.Status == 1 && !p.IsDeleted)
|
||||
.OrderByDescending(p => p.Sort)
|
||||
.Select(p => p.ImageUrl)
|
||||
.Select(p => new TeamPageItemDto
|
||||
{
|
||||
Id = p.Id,
|
||||
Title = p.Title ?? "",
|
||||
IconUrl = p.IconUrl,
|
||||
ActiveIconUrl = p.ActiveIconUrl,
|
||||
ImageUrl = p.ImageUrl
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
_logger.LogDebug("获取到 {Count} 张团队介绍图片", images.Count);
|
||||
_logger.LogDebug("获取到 {Count} 个团队业务页", pages.Count);
|
||||
|
||||
return new TeamInfoDto
|
||||
{
|
||||
Images = images
|
||||
// 兼容旧版:保留 images 字段
|
||||
Images = pages.Select(p => p.ImageUrl).ToList(),
|
||||
Items = pages
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -422,9 +422,15 @@ public partial class MiAssessmentDbContext : DbContext
|
|||
entity.Property(e => e.Title)
|
||||
.HasMaxLength(100)
|
||||
.HasComment("标题");
|
||||
entity.Property(e => e.IconUrl)
|
||||
.HasMaxLength(500)
|
||||
.HasComment("图标URL(未选中状态)");
|
||||
entity.Property(e => e.ActiveIconUrl)
|
||||
.HasMaxLength(500)
|
||||
.HasComment("图标URL(选中状态)");
|
||||
entity.Property(e => e.ImageUrl)
|
||||
.HasMaxLength(500)
|
||||
.HasComment("图片URL");
|
||||
.HasComment("详情图片URL");
|
||||
entity.Property(e => e.ShowButton)
|
||||
.HasComment("是否显示操作按钮");
|
||||
entity.Property(e => e.ButtonText)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,19 @@ public class BusinessPage
|
|||
public string? Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图片URL
|
||||
/// 图标URL(未选中状态)
|
||||
/// </summary>
|
||||
[MaxLength(500)]
|
||||
public string? IconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标URL(选中状态)
|
||||
/// </summary>
|
||||
[MaxLength(500)]
|
||||
public string? ActiveIconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 详情图片URL
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MaxLength(500)]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,43 @@ namespace MiAssessment.Model.Models.Team;
|
|||
public class TeamInfoDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 团队介绍图片列表
|
||||
/// 团队介绍图片列表(兼容旧版)
|
||||
/// </summary>
|
||||
public List<string> Images { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 团队业务页列表(含标题、icon、详情图)
|
||||
/// </summary>
|
||||
public List<TeamPageItemDto> Items { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 团队页单项 DTO
|
||||
/// </summary>
|
||||
public class TeamPageItemDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 主键ID
|
||||
/// </summary>
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标题
|
||||
/// </summary>
|
||||
public string Title { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 图标URL(未选中状态)
|
||||
/// </summary>
|
||||
public string? IconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图标URL(选中状态)
|
||||
/// </summary>
|
||||
public string? ActiveIconUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 详情图片URL
|
||||
/// </summary>
|
||||
public string ImageUrl { get; set; } = null!;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,57 @@
|
|||
<!-- 加载状态 -->
|
||||
<Loading v-if="pageLoading" type="page" :loading="true" />
|
||||
|
||||
<!-- 团队介绍图片 -->
|
||||
<view v-else class="team-content">
|
||||
<template v-if="teamImages.length > 0">
|
||||
<image
|
||||
v-for="(item, index) in teamImages"
|
||||
:key="index"
|
||||
:src="item"
|
||||
mode="scaleToFill"
|
||||
class="team-image"
|
||||
/>
|
||||
<template v-else>
|
||||
<!-- 有数据 -->
|
||||
<template v-if="items.length > 0">
|
||||
<!-- 自定义导航栏 -->
|
||||
<Navbar title="团队" :show-back="false" background-color="#F5A623" text-color="light" />
|
||||
|
||||
<!-- 顶部 tab 栏,可横向滚动 -->
|
||||
<scroll-view
|
||||
scroll-x
|
||||
:scroll-left="scrollLeft"
|
||||
class="tab-scroll"
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="tab-list">
|
||||
<view
|
||||
v-for="(item, index) in items"
|
||||
:key="item.id"
|
||||
class="tab-item"
|
||||
:class="{ 'tab-item--active': activeIndex === index }"
|
||||
@click="handleTabClick(index)"
|
||||
>
|
||||
<!-- icon 区域 -->
|
||||
<view class="tab-icon-wrap">
|
||||
<!-- icon -->
|
||||
<image
|
||||
v-if="activeIndex === index && item.activeIconUrl"
|
||||
:src="item.activeIconUrl"
|
||||
mode="aspectFit"
|
||||
class="tab-icon"
|
||||
/>
|
||||
<image
|
||||
v-else-if="item.iconUrl"
|
||||
:src="item.iconUrl"
|
||||
mode="aspectFit"
|
||||
class="tab-icon"
|
||||
/>
|
||||
</view>
|
||||
<!-- 标题 -->
|
||||
<text class="tab-title">{{ item.title }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 详情图片 -->
|
||||
<view class="detail-content">
|
||||
<image
|
||||
:src="currentItem.imageUrl"
|
||||
mode="widthFix"
|
||||
class="detail-image"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 无数据时显示空状态 -->
|
||||
|
|
@ -20,22 +61,38 @@
|
|||
<image src="/static/ic_empty.png" mode="aspectFit" class="empty-icon" />
|
||||
<text class="empty-text">暂无团队介绍</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 团队页面
|
||||
* 展示团队介绍图片,全屏无导航栏
|
||||
* 顶部可配置 tab 栏(标题+icon),点击切换对应详情图片
|
||||
* tab 超出屏幕时可横向滚动
|
||||
*/
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getTeamInfo } from '@/api/team.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
import Navbar from '@/components/Navbar/index.vue'
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
const teamImages = ref([])
|
||||
const items = ref([])
|
||||
const activeIndex = ref(0)
|
||||
const scrollLeft = ref(0)
|
||||
|
||||
// 当前选中项
|
||||
const currentItem = computed(() => {
|
||||
return items.value[activeIndex.value] || {}
|
||||
})
|
||||
|
||||
/**
|
||||
* 切换 tab
|
||||
*/
|
||||
function handleTabClick(index) {
|
||||
activeIndex.value = index
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载团队介绍数据
|
||||
|
|
@ -45,13 +102,18 @@ async function loadTeamInfo() {
|
|||
try {
|
||||
const res = await getTeamInfo()
|
||||
if (res && res.code === 0 && res.data) {
|
||||
const images = res.data.images || res.data.list || res.data
|
||||
if (Array.isArray(images)) {
|
||||
teamImages.value = images.map(item =>
|
||||
typeof item === 'string' ? item : (item.imageUrl || item)
|
||||
)
|
||||
} else {
|
||||
teamImages.value = []
|
||||
// 优先使用新版 items 数据
|
||||
if (res.data.items && res.data.items.length > 0) {
|
||||
items.value = res.data.items
|
||||
} else if (res.data.images && res.data.images.length > 0) {
|
||||
// 兼容旧版:纯图片列表
|
||||
items.value = res.data.images.map((url, index) => ({
|
||||
id: index,
|
||||
title: `介绍${index + 1}`,
|
||||
iconUrl: '',
|
||||
activeIconUrl: '',
|
||||
imageUrl: typeof url === 'string' ? url : (url.imageUrl || '')
|
||||
}))
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -75,14 +137,70 @@ onMounted(() => {
|
|||
background-color: $bg-white;
|
||||
}
|
||||
|
||||
.team-content {
|
||||
.team-image {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: block;
|
||||
// tab 横向滚动区域
|
||||
.tab-scroll {
|
||||
white-space: nowrap;
|
||||
background-color: $bg-white;
|
||||
padding: $spacing-lg 0;
|
||||
}
|
||||
|
||||
.tab-list {
|
||||
display: inline-flex;
|
||||
padding: 0 $spacing-lg;
|
||||
gap: $spacing-xl;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
width: 140rpx;
|
||||
}
|
||||
|
||||
.tab-icon-wrap {
|
||||
position: relative;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: $spacing-xs;
|
||||
}
|
||||
|
||||
.tab-icon {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
|
||||
.tab-title {
|
||||
font-size: $font-size-sm;
|
||||
color: $text-secondary;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 140rpx;
|
||||
}
|
||||
|
||||
.tab-item--active {
|
||||
.tab-title {
|
||||
color: $text-color;
|
||||
font-weight: $font-weight-medium;
|
||||
}
|
||||
}
|
||||
|
||||
// 详情图片区域
|
||||
.detail-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.detail-image {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
// 空状态
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user