上一篇我们介绍了医生排班列表,当点击某一个医生就进入了医生的主页。在医生主页里需要展示医生的基本信息、执业的科室信息、最近7天的排班情况。如果患者选择某一天,可以看到当天的号源情况,是否有号是否满号。本篇我们来实现一下具体的功能。
在这里插入图片描述

1 创建API

对于这种有多类信息的,我们都是需要先写后端的API来完成数据的组装,供前端页面进行数据绑定。

点击扩展管理,创建API
在这里插入图片描述
输入方法的名称和标识
在这里插入图片描述
在编写API的时候,因为是要获取最近7天的数据,意味着要查询排班日期。在云函数里日期是用UTC格式存储的,比北京时间要早个8小时,所以我们先创建一个工具函数来修正时间的误差

// 辅助函数:获取当前北京时间的 0点0分0秒 的毫秒值
function getBjTodayStart() {
  const OFFSET = 8 * 60 * 60 * 1000; // 北京时间偏移量
  const now = new Date();
  
  // 1. 将当前 UTC 时间转换为北京时间
  // 原理:人为加上偏移量,得到的 Date 对象在 getUTCHours() 时即为北京时间的小时
  const bjNow = new Date(now.getTime() + OFFSET);
  
  // 2. 将北京时间的时间部分归零
  // 注意:这里使用 setUTCHours,因为 bjNow 实际上是一个“伪装”成 UTC 的北京时间对象
  bjNow.setUTCHours(0, 0, 0, 0);
  
  // 3. 还原回真实的时间戳
  // 此时 bjNow.getTime() 是北京时间0点的“伪”时间戳,减去偏移量才是真实的全球统一时间戳
  return bjNow.getTime() - OFFSET;
}

// 辅助函数:判断两个毫秒值是否属于同一天(基于北京时间)
function isSameBjDay(ts1, ts2) {
  const OFFSET = 8 * 60 * 60 * 1000;
  const d1 = new Date(ts1 + OFFSET);
  const d2 = new Date(ts2 + OFFSET);
  return d1.getUTCFullYear() === d2.getUTCFullYear() &&
         d1.getUTCMonth() === d2.getUTCMonth() &&
         d1.getUTCDate() === d2.getUTCDate();
}

module.exports = async function (params, context) {
  const time = getBjTodayStart()

  // 这里返回数据,和出参结构映射
  return {time,currentTime:new Date().getTime()};
};

点击方法测试,可以看到当前返回的时间和数据库存储的日期的毫秒值是一致的
在这里插入图片描述
不理解这个,你在测试的时候,发现总是获取不到当天的数据。

解决了日期的问题后,我们就需要考虑,我们给前端返回什么样的数据结构。通常我们需要构造一个JSON的数据,需要考虑返回医生的基本信息,执业的科室,未来七天的出诊记录,结构如下:

{
  "baseInfo": { "name": "张三", "title": "主任医师", ... },
  "practiceList": [ // 按科室分组
    {
      "deptId": "d1",
      "deptName": "心内科",
      "calendar": [ // 该科室下的所有排班扁平化数据
        { "date": 1701234567000, "status": 1, "workType": "1" }, 
        ...
      ]
    },
    { "deptId": "d2", "deptName": "国际部", "calendar": [...] }
  ],
  "config": {
    "defaultActiveDate": 1701234567000 // 后端算好的,最近的有号日期
  }
}

贴入如下代码就可以返回上述结构的数据

/**
 * 获取医生详情及其排班数据
 * 
 * 逻辑:
 * 1. 计算北京时间今天凌晨的时间戳,确保匹配数据库 0:0:0 的记录
 * 2. 并行查询医生基础信息 (t_personnel) 和 未来7天排班 (t_schedule)
 * 3. 对排班数据按科室进行分组聚合
 * 4. 智能决策前端默认选中的日期
 */

// 辅助函数:判断两个毫秒值是否属于同一天(基于北京时间)
function isSameBjDay(ts1, ts2) {
  const OFFSET = 8 * 60 * 60 * 1000;
  const d1 = new Date(Number(ts1) + OFFSET);
  const d2 = new Date(Number(ts2) + OFFSET);
  return d1.getUTCFullYear() === d2.getUTCFullYear() &&
         d1.getUTCMonth() === d2.getUTCMonth() &&
         d1.getUTCDate() === d2.getUTCDate();
}

module.exports = async function (params, context) {
  try {
    const { doctorId, targetDate } = params;

    if (!doctorId) {
      return { code: -1, msg: '医生ID不能为空' };
    }

    // 1. 获取北京时间今天凌晨 00:00:00 的时间戳 (参考 getDoctorList.js 逻辑)
    const now = new Date();
    const utcTime = now.getTime();
    const beijingTime = new Date(utcTime + 8 * 60 * 60 * 1000);
    const todayStart = new Date(
      beijingTime.getUTCFullYear(),
      beijingTime.getUTCMonth(),
      beijingTime.getUTCDate(),
      0, 0, 0, 0
    ).getTime() - 8 * 60 * 60 * 1000;

    const sevenDaysEnd = todayStart + 7 * 24 * 60 * 60 * 1000 - 1;

    // 2. 查询排班:使用 V2 版本 API,同时关联医生(doctor_id)和科室(dept_id)信息
    const scheduleRes = await context.callModel({
      dataSourceName: 't_schedule',
      methodName: 'wedaGetRecordsV2',
      params: {
        filter: {
          where: {
            $and: [
              { doctor_id: { $eq: doctorId } },
              { work_date: { $gte: todayStart } },
              { work_date: { $lte: sevenDaysEnd } },
              { status: { $eq: '1' } } // 仅查询有效排班
            ]
          }
        },
        select: {
          "$master": true,
          "dept_id": true,   // 关联科室信息
          "doctor_id": true  // 关联医生信息,直接获取头像、姓名、简介等
        },
        orderBy: [{ work_date: 'ASC' }],
        pageSize: 200
      }
    });

    let schedules = [];
    if (scheduleRes && scheduleRes.records) {
      schedules = scheduleRes.records;
    }
    
    if (schedules.length === 0) {
      return { code: 404, msg: '该医生近期无排班信息' };
    }

    // 从排班记录的关联字段中提取医生基础信息
    const doctorData = schedules[0].doctor_id || {};
    if (!doctorData._id) {
      return { code: 404, msg: '医生信息异常' };
    }
    
    // 3. 数据清洗与多科室分组
    const practiceMap = new Map();
    let firstAvailableDate = null;

    schedules.forEach(sch => {
      const deptObj = sch.dept_id || {};
      const deptId = deptObj._id || deptObj;
      const deptName = deptObj.name || '其他科室';

      if (!practiceMap.has(deptId)) {
        practiceMap.set(deptId, {
          deptId: deptId,
          deptName: deptName,
          calendar: []
        });
      }

      practiceMap.get(deptId).calendar.push({
        _id: sch._id,
        date: sch.work_date,
        workType: sch.work_type,
        fee: sch.reg_fee,
        total: sch.total_count,
        surplus: sch.surplus_count
      });

      if (!firstAvailableDate && sch.surplus_count > 0) {
        firstAvailableDate = sch.work_date;
      }
    });

    // 4. 智能决策活动日期
    let activeDate = null;
    if (targetDate && schedules.some(s => isSameBjDay(s.work_date, targetDate))) {
      activeDate = Number(targetDate);
    } else {
      activeDate = firstAvailableDate || todayStart;
    }

    // 5. 返回结果
    return {
      code: 0,
      msg: 'success',
      baseInfo: {
        name: doctorData.name,
        title: doctorData.title,
        avatar: doctorData.avatar_url || doctorData.avatar, // 兼容字段名
        intro: doctorData.intro || doctorData.description,
        skillTags: doctorData.skill_tags || []
      },
      practiceList: Array.from(practiceMap.values()),
      config: {
        activeDate: activeDate,
        serverTime: Date.now()
      }
    };

  } catch (error) {
    console.error('获取医生详情失败:', error);
    return {
      code: -1,
      msg: error.message || '系统繁忙'
    };
  }
};

2 创建页面

API定义好之后,我们需要创建一个医生的主页。点击创建页面的图标
在这里插入图片描述
输入页面的名称
在这里插入图片描述
在获取医生信息时候,我们需要从列表页传入医生的数据标识,选中页面组件,创建一个URL参数
在这里插入图片描述
在这里插入图片描述
给医生列表设置点击事件
在这里插入图片描述
打开医生主页,并且传入医生的数据标识
在这里插入图片描述

3 创建变量

为了显示医生的信息,我们先需要创建一个外部API,用来从后端查询医生
在这里插入图片描述
在这里插入图片描述

4 搭建页面布局

有了数据之后,就可以搭建页面布局了。先在第一行搭建医生的基本信息,在列里添加普通容器,里边添加两个普通容器
在这里插入图片描述
第一个普通容器里添加图片组件,地址绑定医生的头像
在这里插入图片描述
第二个普通容器里添加两个文本组件,一个绑定医生的姓名,一个从排班信息里绑定医生的科室
在这里插入图片描述
在这里插入图片描述
然后将父容器的布局调整为横向排列
在这里插入图片描述
第二行的列里添加顶部选项卡组件
在这里插入图片描述
绑定标签

$w.queryDoctor.data.practiceList.map((item,index)=>({
  label:item.deptName,
  value:index
}))

在这里插入图片描述
在第三行的列里添加一个普通容器
在这里插入图片描述
里边添加一个文本组件,内容设置为出诊时间
在这里插入图片描述
下边添加一个循环展示组件,绑定我们的出诊时间字段
在这里插入图片描述

$w.queryDoctor.data.practiceList[$w.topTab1.selectedValue].calendar

然后依次绑定挂号信息即可
在这里插入图片描述
在这里插入图片描述

总结

本篇我们讲解了如何通过API来一次将我们医生主页需要的信息都获取到。这里先是考虑了数据库的日期的时差问题,接着在云函数里对于医生的多点执业进行了分组。有了数据结构完善的API,前端布局搭建就非常方便。所以低代码并不是那种想的非常简单,我配一下界面就可以完事的,当然了现在agent各种吹,什么只需要一个输入框其他啥都不用干,AI完全自主决策那就是另外一回事了。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐