互联网医院12医生主页
本篇我们讲解了如何通过API来一次将我们医生主页需要的信息都获取到。这里先是考虑了数据库的日期的时差问题,接着在云函数里对于医生的多点执业进行了分组。有了数据结构完善的API,前端布局搭建就非常方便。所以低代码并不是那种想的非常简单,我配一下界面就可以完事的,当然了现在agent各种吹,什么只需要一个输入框其他啥都不用干,AI完全自主决策那就是另外一回事了。
上一篇我们介绍了医生排班列表,当点击某一个医生就进入了医生的主页。在医生主页里需要展示医生的基本信息、执业的科室信息、最近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完全自主决策那就是另外一回事了。
更多推荐



所有评论(0)