医院专家展示大屏实战开发

一、项目背景与需求分析

在现代医院信息化建设中,专家信息展示大屏是连接患者与医疗资源的重要桥梁。本文将详细介绍如何使用 ASP.NET Core MVC + Dapper + 原生JavaScript 技术栈,构建一个功能完善、视觉效果出色的医院专家介绍大屏系统。

核心功能需求

  1. 数据展示:实时展示医院专家的基本信息、职称、简介、坐诊时间
  2. 视觉标识:党员医生标识、浙二专家标识区分
  3. 自动轮播:支持多页数据自动切换展示
  4. 响应式布局:自适应不同分辨率的大屏设备
  5. 定时刷新:每30分钟自动更新专家数据

在这里插入图片描述

二、技术架构设计

2.1 整体架构

┌─────────────────────────────────────────────────────────────┐
│                      前端展示层 (View)                      │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  原生JavaScript + CSS3 动画 + Grid弹性布局          │    │
│  │  • 自动分页轮播  • 响应式网格  • 科技感UI设计       │    │ 
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                     控制器层 (Controller)                   │
│  ┌─────────────────────────────────────────────────────┐    │
│  │           ExpertScreenController                    │    │
│  │  • GetExperts() - 获取专家列表API                   │    │  
│  │  • Index() - 大屏展示页面                           │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                      数据访问层                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Dapper (轻量级ORM) + MySQL                         │    │
│  │  • 高性能SQL查询  • 动态数据映射                    │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

2.2 数据库表结构

CREATE TABLE expert_info (
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(50) NOT NULL COMMENT '专家姓名',
    depart_name VARCHAR(100) COMMENT '科室/职称',
    picture VARCHAR(255) COMMENT '照片路径',
    intro TEXT COMMENT '专家介绍',
    work_time VARCHAR(500) COMMENT '专家门诊时间',
    doc_lib_time VARCHAR(500) COMMENT '名医馆坐诊时间',
    isPartyMember TINYINT(1) DEFAULT 0 COMMENT '是否党员',
    isZheErExpert TINYINT(1) DEFAULT 0 COMMENT '是否浙二专家',
    is_del TINYINT(1) DEFAULT 0 COMMENT '是否删除',
    page INT COMMENT '排序页码',
    ordering INT COMMENT '排序序号'
);

三、后端实现详解

3.1 数据模型定义

// Models/ExpertScreen/ExpertModel.cs
namespace QASystem.Models.ExpertScreen
{
    public class ExpertModel
    {
        public int id { get; set; }
        public string name { get; set; }
        public string title { get; set; }
        public string photo { get; set; }
        public string description { get; set; }
        public string schedule { get; set; }
        public string schedule1 { get; set; }
        public bool isPartyMember { get; set; }
        public bool isZheErExpert { get; set; }
    }
    
    public class ExpertDataModel
    { 
        public List<ExpertModel> experts { get; set; }
    }
}

3.2 控制器实现

// Controllers/ExpertScreenController.cs
public class ExpertScreenController : Controller
{
    private ILogger<ExpertScreenController> _logger;
    private readonly string _qasystem;
    private readonly IMysqlService _mysqlService;

    public ExpertScreenController(
        ILogger<ExpertScreenController> logger, 
        IConfiguration configuration, 
        IMysqlService mysqlService)
    {
        _logger = logger;
        _qasystem = configuration.GetConnectionString("qasystem");
        _mysqlService = mysqlService;
    }

    // 大屏展示页面
    public IActionResult Index()
    {
        return View();
    }

    // API:获取专家列表
    public JsonResult GetExperts()
    {
        string sql = @"SELECT
                          id,
                          NAME,
                          depart_name AS title,
                          picture AS photo,
                          intro AS description,
                          work_time AS SCHEDULE,
                          case when doc_lib_time is null then '' else doc_lib_time END AS SCHEDULE1,
                          isPartyMember,
                          isZheErExpert 
                        FROM
                          expert_info 
                        WHERE
                          is_del = 0
                        ORDER BY page,ordering asc";
        
        List<ExpertModel> experts = _mysqlService.DBQuery<ExpertModel>(_qasystem, sql);
        
        ExpertDataModel model = new ExpertDataModel
        {
            experts = experts
        };
        return Json(model);
    }
}

3.3 关键技术点解析

3.3.1 Dapper动态查询

使用Dapper进行数据库操作,相比EF Core更加轻量高效:

// 泛型方法实现动态映射
List<ExpertModel> experts = _mysqlService.DBQuery<ExpertModel>(_qasystem, sql);
3.3.2 SQL优化技巧
-- 使用CASE处理NULL值,确保前端接收空字符串而非null
case when doc_lib_time is null then '' else doc_lib_time END AS SCHEDULE1

四、前端实现详解

4.1 状态管理设计

采用纯JavaScript实现状态管理,避免引入重型框架:

const state = {
    experts: [],           // 当前展示的专家列表
    currentPage: 0,        // 当前页码
    expertsPerPage: 6,     // 每页显示数量
    totalPages: 0,         // 总页数
    autoSwitchInterval: null,  // 自动切换定时器
    isLoading: false,      // 加载状态
    allExperts: [],        // 所有专家数据
    nonZheErExperts: []    // 筛选后的专家
};

4.2 响应式网格布局

根据容器尺寸动态计算每页显示的专家数量:

function calculateExpertsPerPage() {
    const containerWidth = document.querySelector('.experts-container').clientWidth;
    const containerHeight = document.querySelector('.experts-container').clientHeight;
    
    // 卡片宽度基准
    const baseCardWidth = 400;
    
    // 根据宽度计算列数
    const cols = Math.floor((containerWidth - 30) / (baseCardWidth + 30));
    const finalCols = Math.min(Math.max(1, cols), 12); // 限制最多12列
    
    // 根据高度计算行数
    const cardAspectRatio = 1.4;
    const estimatedCardHeight = baseCardWidth / cardAspectRatio;
    const rowsPerPage = Math.max(1, Math.floor(
        (containerHeight - 80) / (estimatedCardHeight + 30)
    ));
    
    // 每页最多60个专家
    state.expertsPerPage = Math.min(60, finalCols * rowsPerPage);
    state.totalPages = Math.ceil(state.experts.length / state.expertsPerPage);
}

4.3 数据获取与容错处理

async function fetchExpertsFromServer() {
    try {
        const response = await fetch('/ExpertScreen/GetExperts', {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            }
        });

        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        const data = await response.json();
        return data;
    } catch (error) {
        console.error('获取专家数据失败:', error);
        // 返回备用模拟数据,确保系统可用性
        return {
            experts: generateMockExperts()
        };
    }
}

4.4 定时数据刷新机制

function setupPeriodicDataFetching() {
    const intervalMs = 30 * 60 * 1000; // 30分钟

    setInterval(async () => {
        console.log('定时更新专家数据...');
        try {
            const data = await fetchExpertsFromServer();
            const allExperts = data.experts || [];
            
            // 更新状态
            state.allExperts = allExperts;
            state.nonZheErExperts = allExperts;
            state.experts = [...state.nonZheErExperts];
            
            // 重新计算分页并渲染
            calculateExpertsPerPage();
            setupPagination();
            renderCurrentPage();
        } catch (error) {
            console.error('定时更新专家数据失败:', error);
        }
    }, intervalMs);
}

4.5 自动轮播实现

function startAutoSwitch() {
    if (state.autoSwitchInterval) {
        clearInterval(state.autoSwitchInterval);
    }

    state.autoSwitchInterval = setInterval(() => {
        nextPage();
    }, 30000); // 30秒切换一次
}

// 页面可见性API优化性能
document.addEventListener('visibilitychange', () => {
    if (document.hidden) {
        stopAutoSwitch();  // 页面隐藏时停止轮播
    } else {
        startAutoSwitch(); // 页面显示时恢复轮播
    }
});

4.6 专家卡片动态渲染

function createExpertCard(expert) {
    const card = document.createElement('div');
    card.className = 'expert-card';
    
    // 党员标识
    const partyBadge = expert.isPartyMember 
        ? `<div class="party-badge"><img src="../images/expert/partyImg.png" alt="党徽"></div>` 
        : '';
    
    // 医院Logo根据专家类型切换
    const logoSrc = expert.isZheErExpert 
        ? '../images/expert/zErlogo.png' 
        : '../images/expert/logo.png';

    card.innerHTML = `
        <div class="expert-photo-container">
            <img src="http://192.9.216.145:9000/expert/${expert.photo}" 
                 alt="${expert.name}" class="expert-photo">
            ${partyBadge}
        </div>
        <div class="hospital-logo">
            <img src="${logoSrc}" alt="医院Logo">
        </div>
        <div class="expert-info">
            <div class="expert-name">${expert.name}</div>
            <div class="expert-title">${expert.title}</div>
            <div class="expert-description">${expert.description}</div>
            <div class="expert-schedule">
                <div class="schedule-title">坐诊时间:</div>
                ${expert.schedule1 != '' ? `
                    <div class="schedule-content">
                        <span style='font-weight:bold'>专家门诊:</span>${expert.schedule};
                        <span style='font-weight:bold'>名医馆:</span>${expert.schedule1}
                    </div>` : `
                    <div class="schedule-content">
                        <span style='font-weight:bold'>专家门诊:</span>${expert.schedule}
                    </div>`}
            </div>
        </div>
    `;
    
    return card;
}

五、UI设计亮点

5.1 科技感视觉风格

body {
    background: #0c1428;
    background-image: 
        radial-gradient(circle at 25% 25%, rgba(11, 36, 129, 0.3) 0%, transparent 50%),
        radial-gradient(circle at 75% 75%, rgba(11, 36, 129, 0.3) 0%, transparent 50%),
        linear-gradient(0deg, #0c1428 0%, #101a30 100%);
}

/* 科技感网格背景 */
body::before {
    content: '';
    position: fixed;
    background-image: 
        linear-gradient(rgba(40, 80, 120, 0.5) 1px, transparent 1px),
        linear-gradient(90deg, rgba(40, 80, 120, 0.5) 1px, transparent 1px);
    background-size: 40px 40px;
    opacity: 0.4;
}

5.2 卡片悬停效果

.expert-card {
    background: rgba(28, 34, 56, 0.9);
    border: 1px solid rgba(0, 191, 255, 0.2);
    transition: all 0.3s ease;
}

.expert-card::before {
    content: '';
    position: absolute;
    height: 2px;
    background: linear-gradient(90deg, transparent, #00e5ff, transparent);
}

.expert-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 0 40px rgba(0, 191, 255, 0.4);
}

六、性能优化策略

6.1 前端优化

优化点 实现方式
懒加载 按需渲染当前页专家卡片
防抖处理 resize事件防抖,避免频繁重绘
定时器管理 页面隐藏时暂停自动切换
图片优化 使用对象存储CDN加速

6.2 后端优化

-- 添加复合索引优化查询
CREATE INDEX idx_expert_sort ON expert_info(page, ordering);
CREATE INDEX idx_expert_active ON expert_info(is_del);

七、项目总结

本系统采用轻量级技术栈,实现了以下核心优势:

  1. 架构简洁:ASP.NET Core MVC + Dapper,学习成本低,维护方便
  2. 性能优异:原生JavaScript无框架依赖,首屏加载快
  3. 扩展性强:模块化设计,易于功能扩展
  4. 稳定可靠:完善的错误处理和降级策略

适用场景

  • 医院大厅专家信息展示大屏
  • 企业人才展示墙
  • 政务公开信息展示系统
  • 任何需要数据可视化大屏的场景

八、源码参考

Controller: Controllers/ExpertScreenController.cs

View: Views/ExpertScreen/Index.cshtml

Models: Models/ExpertScreen/ExpertModel.cs


💡 小贴士:本文完整展示了从需求分析到代码实现的完整流程,适合有一定ASP.NET Core基础的开发者学习参考。如需获取完整源码或有任何疑问,欢迎在评论区留言交流!


标签: #ASP.NET Core #Dapper #大屏展示 #医疗信息化 #前端开发 #JavaScript

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐