Appearance
实现 孕期与预产期计算 工具
分类:general | 标签:孕期、预产期、产检 计算预产期孕周与产检时间轴
2.1 功能说明
计算预产期孕周与产检时间轴
2.1.1 使用指南
功能说明
根据末次月经第一天(LMP)推算预产期、当前孕周孕天,并生成一份完整的产检时间轴, 标出每一项检查的建议时间窗与当前状态。也支持用 B 超测得的孕周反推,适合月经周期不规律或记不清末次月经的情况。 全部计算在浏览器本地完成,不上传任何信息。
计算规则
预产期采用国际通用的内格勒规则(Naegele's rule):预产期 = 末次月经第一天 + 280 天(即 40 周)。 这一规则默认月经周期为 28 天、排卵发生在第 14 天。
民间常说的「月份加 9(或减 3)、日期加 7」是同一规则的口算简化版。由于各月天数不同, 两种算法在部分月份会相差 1–3 天,本工具统一采用更精确的 280 天计法。
若你的周期不是 28 天,工具会自动做周期校正:预产期 = 末次月经 + 280 天 +(实际周期 − 28)。 例如周期 35 天,排卵推迟约 7 天,预产期相应顺延 7 天。周期短于 28 天则提前。
孕周按「满周 + 天」表示,孕 0 周 0 天即末次月经当天。临床上说的「怀孕 40 周」是从末次月经算起, 比实际受精时间早约 2 周,这是产科通用口径,不是计算错误。
怎么用
1. 「按末次月经」页签:选择末次月经第一天,填入平时的月经周期天数(默认 28 天);
2. 「按 B 超反推」页签:填入做 B 超的日期和当时报告上的孕周孕天,工具会倒推出等效末次月经与预产期;
3. 「计算基准日」默认是今天,也可以改成任意日期,用来查看某一天的孕周;
4. 下方时间轴列出各阶段产检项目,已过期、进行中、未开始分别用不同颜色标记;
5. 结果和时间轴都可以一键复制或下载为 TXT。
关于产检时间轴
时间轴依据国内常规产检安排整理,共 {{ CHECKUPS.length }} 个节点,覆盖建卡、NT、唐筛、无创、大排畸、 糖耐、小排畸到足月前的每周复查。常规产检频率为:孕 28 周前每 4 周一次,28–36 周每 2 周一次, 36 周后每周一次,总计约 10–13 次。
重要提醒
预产期只是一个统计上的参考日,真正在预产期当天分娩的比例不足 5%, 孕 37–42 周之间分娩都属于足月,属正常范围。
本工具仅供参考,不能替代医生诊断。各医院的产检项目名称、时间窗和具体要求存在差异, 双胎、多胎、高龄、既往病史等情况的产检方案与本表差别较大,请一切以产科医生和《母子健康手册》的安排为准。 如出现腹痛、出血、胎动异常等情况,请立即就医。
2.2 代码实现
2.2.1 组件结构
本工具是一个基于 Vue 3 <script setup> 语法的单文件组件(SFC),统一包裹在 ToolPageShell 组件内,由它提供页面标题、工具 ID、分类与「使用指南」插槽等通用外壳;核心业务逻辑(响应式状态、计算属性、事件处理函数)全部写在 <script setup> 中,输入/输出通过 el-input、el-button 等 Element Plus 组件与用户交互,所有数据均在浏览器本地处理,不会上传服务器。
2.2.2 核心逻辑概览
<script setup> 中定义的主要函数/方法:
todayStr()fmtISO()parseDate()addDays()diffDays()fmtCN()weekdayCN()resetAll()summaryText()copySummary()downloadTxt()
2.2.3 关键实现代码
以下是该工具的完整 <script setup> 实现(核心代码)。模板(<template>)与样式(<style>)已省略。
vue
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import ToolPageShell from '@/components/ToolPageShell.vue'
const MS_DAY = 86400000
// 产检时间轴:startDay / endDay 为距末次月经的天数
const CHECKUPS = [
{ key: 'confirm', name: '确认怀孕', w: [4, 5], desc: '停经后可用早孕试纸或抽血查 HCG 确认怀孕,同时开始补充叶酸(每日 0.4–0.8 mg)。' },
{ key: 'early-us', name: '首次阴道 B 超', w: [6, 8], desc: '确认宫内孕、排除宫外孕,观察胎心胎芽与孕囊数量,核对孕周。' },
{ key: 'card', name: '建档建卡(首次产检)', w: [9, 12], desc: '在计划分娩的医院建立围产保健档案,完成血常规、尿常规、肝肾功能、血型、传染病筛查、心电图等基础检查。' },
{ key: 'nt', name: 'NT 检查', w: [11, 13], desc: '超声测量胎儿颈项透明层厚度,筛查染色体异常风险。时间窗较严格(11 周 0 天至 13 周 6 天),务必提前预约。' },
{ key: 'nipt', name: '无创 DNA 产前检测(NIPT)', w: [12, 22], desc: '抽取孕妇外周血检测胎儿染色体非整倍体,对 21/18/13 三体检出率高,最佳时间为 12–22 周。可与唐筛二选一。' },
{ key: 'down', name: '唐氏筛查(中期血清学筛查)', w: [15, 20], desc: '抽血评估 21 三体、18 三体和神经管缺陷风险。若已做无创 DNA 可不再重复。高风险需进一步做羊水穿刺。' },
{ key: 'amnio', name: '羊水穿刺(如需要)', w: [16, 22], desc: '仅在唐筛或无创提示高风险、高龄、既往异常孕产史等情况下进行,属有创检查,需医生评估后决定。' },
{ key: 'anomaly', name: '大排畸(系统超声筛查)', w: [20, 24], desc: '系统性筛查胎儿结构畸形,检查时间较长,建议提前预约。此阶段羊水量适中、胎儿结构清晰,是最佳观察窗口。' },
{ key: 'ogtt', name: '糖耐量试验(OGTT)', w: [24, 28], desc: '筛查妊娠期糖尿病。检查前需空腹 8–12 小时,喝糖水后分别测空腹、1 小时、2 小时血糖。' },
{ key: 'anemia', name: '血常规复查与补铁评估', w: [24, 28], desc: '孕中晚期血容量增加,易出现生理性贫血,需复查血红蛋白,必要时补铁。' },
{ key: 'small-anomaly', name: '小排畸(生长发育超声)', w: [28, 32], desc: '评估胎儿生长发育、羊水量、胎盘位置与成熟度,复查部分晚显性结构异常。' },
{ key: 'gbs', name: 'B 族链球菌(GBS)筛查', w: [35, 37], desc: '取阴道及直肠分泌物培养,阳性者分娩时需预防性使用抗生素,降低新生儿感染风险。' },
{ key: 'nst', name: '胎心监护(NST)', w: [32, 40], desc: '通常从 32–34 周开始,之后每次产检都做,评估胎儿宫内状态。孕晚期需每日自数胎动。' },
{ key: 'weekly', name: '每周产检', w: [36, 40], desc: '36 周后每周一次,检查宫高腹围、血压体重、胎位与胎心,评估分娩方式,做好待产准备。' },
{ key: 'term', name: '进入足月', w: [37, 42], desc: '孕 37 周起即为足月,随时可能临产。出现规律宫缩、见红或破水应立即前往医院。' },
{ key: 'overdue', name: '预产期后监测', w: [40, 42], desc: '超过预产期需加密监测胎心与羊水,通常满 41 周仍未临产会考虑引产,超过 42 周为过期妊娠。' }
]
const mode = ref('lmp')
const lmpDate = ref(todayStr())
const cycleDays = ref(28)
const scanDate = ref(todayStr())
const scanWeeks = ref(8)
const scanDays = ref(0)
const baseDate = ref(todayStr())
const timelineFilter = ref('all')
function todayStr () {
const d = new Date()
return fmtISO(d)
}
function fmtISO (d) {
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return y + '-' + m + '-' + day
}
// 以本地时区正午构造日期,避免夏令时与时区造成的整天偏移
function parseDate (s) {
if (!s) return null
const [y, m, d] = s.split('-').map(Number)
if (!y || !m || !d) return null
return new Date(y, m - 1, d, 12, 0, 0, 0)
}
function addDays (date, n) {
const d = new Date(date.getTime())
d.setDate(d.getDate() + n)
return d
}
function diffDays (a, b) {
return Math.round((a.getTime() - b.getTime()) / MS_DAY)
}
function fmtCN (d) {
return d.getFullYear() + ' 年 ' + (d.getMonth() + 1) + ' 月 ' + d.getDate() + ' 日'
}
function weekdayCN (d) {
return '星期' + '日一二三四五六'.charAt(d.getDay())
}
const emptyHint = computed(() =>
mode.value === 'lmp' ? '请选择末次月经第一天' : '请填写 B 超日期与当时孕周')
// 有效末次月经(B 超模式下为反推值)
const effectiveLmp = computed(() => {
if (mode.value === 'scan') {
const sd = parseDate(scanDate.value)
if (!sd) return null
return addDays(sd, -(scanWeeks.value * 7 + scanDays.value))
}
return parseDate(lmpDate.value)
})
const res = computed(() => {
const lmp = effectiveLmp.value
const base = parseDate(baseDate.value)
if (!lmp || !base) return null
// 预产期:LMP + 280 天,并按月经周期校正(B 超反推时不再校正)
const adjust = mode.value === 'scan' ? 0 : cycleDays.value - 28
const edd = addDays(lmp, 280 + adjust)
// 孕龄同样以校正后的口径计算,保证「孕 40 周 = 预产期当天」
const daysPregnant = diffDays(base, lmp) - adjust
const weeks = Math.floor(daysPregnant / 7)
const days = ((daysPregnant % 7) + 7) % 7
const remain = diffDays(edd, base)
let warn = ''
let warnType = 'info'
if (daysPregnant < 0) {
warn = '计算基准日早于末次月经,请检查日期是否填反'
warnType = 'error'
} else if (daysPregnant > 300) {
warn = '按此日期已超过 42 周(过期妊娠范围),请确认末次月经或基准日是否正确'
warnType = 'warning'
} else if (weeks >= 42) {
warn = '已超过 42 周,属过期妊娠,请及时就医'
warnType = 'warning'
} else if (weeks >= 37) {
warn = '已进入足月,随时可能临产,请做好待产准备'
warnType = 'success'
}
let trimester = '孕早期'
if (weeks >= 28) trimester = '孕晚期'
else if (weeks >= 14) trimester = '孕中期'
if (daysPregnant < 0) trimester = '尚未开始'
const percent = Math.max(0, Math.min(100, Math.round(daysPregnant / 280 * 100)))
return {
lmp,
edd,
base,
daysPregnant: Math.max(0, daysPregnant),
weeks,
days,
percent,
trimester,
warn,
warnType,
gaText: daysPregnant < 0 ? '尚未开始' : weeks + ' 周 ' + days + ' 天',
remainText: remain > 0 ? '还有 ' + remain + ' 天' : remain === 0 ? '就是今天' : '已超过 ' + (-remain) + ' 天',
eddText: fmtCN(edd),
eddWeekday: weekdayCN(edd),
lmpText: fmtCN(lmp),
baseText: fmtCN(base) + '(' + weekdayCN(base) + ')',
conceptionText: fmtCN(addDays(lmp, 14 + adjust)) + ' 前后',
t1EndText: fmtCN(addDays(lmp, 13 * 7 + 6 + adjust)),
t2EndText: fmtCN(addDays(lmp, 27 * 7 + 6 + adjust)),
termText: fmtCN(addDays(lmp, 37 * 7 + adjust))
}
})
const progressColor = computed(() => {
if (!res.value) return '#3883fa'
if (res.value.weeks >= 37) return '#67c23a'
if (res.value.weeks >= 28) return '#e6a23c'
return '#3883fa'
})
const timeline = computed(() => {
if (!res.value) return []
const adjust = mode.value === 'scan' ? 0 : cycleDays.value - 28
const cur = res.value.daysPregnant
return CHECKUPS.map(c => {
const startDay = c.w[0] * 7
const endDay = c.w[1] * 7 + 6
const startDate = addDays(res.value.lmp, startDay + adjust)
const endDate = addDays(res.value.lmp, endDay + adjust)
let status = 'future'
if (cur > endDay) status = 'past'
else if (cur >= startDay) status = 'current'
return {
key: c.key,
name: c.name,
desc: c.desc,
weekText: c.w[0] + '–' + c.w[1] + ' 周',
rangeText: fmtCN(startDate) + ' 至 ' + fmtCN(endDate),
status,
statusText: status === 'past' ? '已过期' : status === 'current' ? '正当时' : '未开始',
type: status === 'past' ? 'info' : status === 'current' ? 'success' : 'primary'
}
})
})
const visibleTimeline = computed(() =>
timelineFilter.value === 'all' ? timeline.value : timeline.value.filter(t => t.status !== 'past'))
function resetAll () {
mode.value = 'lmp'
lmpDate.value = todayStr()
cycleDays.value = 28
scanDate.value = todayStr()
scanWeeks.value = 8
scanDays.value = 0
baseDate.value = todayStr()
timelineFilter.value = 'all'
}
function summaryText () {
const r = res.value
if (!r) return ''
const lines = [
'【孕期计算结果】',
'末次月经:' + r.lmpText + (mode.value === 'scan' ? '(由 B 超孕周反推)' : ''),
mode.value === 'lmp' ? '月经周期:' + cycleDays.value + ' 天' : 'B 超日期:' + fmtCN(parseDate(scanDate.value)) + ',当时 ' + scanWeeks.value + ' 周 ' + scanDays.value + ' 天',
'预产期:' + r.eddText + '(' + r.eddWeekday + ')',
'计算基准日:' + r.baseText,
'当前孕周:' + r.gaText + '(' + r.trimester + ')',
'距预产期:' + r.remainText,
'受精时间约:' + r.conceptionText,
'',
'【产检时间轴】'
]
for (const t of timeline.value) {
lines.push('· ' + t.name + '(孕 ' + t.weekText + ',' + t.rangeText + ')[' + t.statusText + ']')
lines.push(' ' + t.desc)
}
lines.push('')
lines.push('提示:预产期为参考日,孕 37–42 周分娩均属足月。本结果仅供参考,请以产科医生的安排为准。')
return lines.join('\n')
}
async function copySummary () {
try {
await navigator.clipboard.writeText(summaryText())
ElMessage.success('已复制计算结果与产检时间轴')
} catch {
ElMessage.warning('复制失败,请手动选择复制')
}
}
function downloadTxt () {
const blob = new Blob([summaryText()], { type: 'text/plain;charset=utf-8' })
const a = document.createElement('a')
a.href = URL.createObjectURL(blob)
a.download = '孕期计算与产检时间轴.txt'
a.click()
URL.revokeObjectURL(a.href)
}2.3 效果截图
