<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>吃了該怎麼辦?食品有害物質攝取試算</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+TC:wght@500;700;900&family=Noto+Sans+TC:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root{
--paper:#EFF1EA;
--paper-dim:#E6E9DF;
--ink:#1F231E;
--ink-soft:#565B4F;
--line:#C7CCB9;
--line-dark:#9AA189;
--safe:#2F6E5C;
--safe-bg:#E1EBE3;
--warn:#B8843A;
--warn-bg:#F3E9D6;
--danger:#A23E2E;
--danger-bg:#F3E1DB;
--card:#FBFAF6;
}
*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{
background:var(--paper);
background-image:
repeating-linear-gradient(0deg, rgba(0,0,0,0.015) 0px, rgba(0,0,0,0.015) 1px, transparent 1px, transparent 3px);
color:var(--ink);
font-family:'Noto Sans TC', sans-serif;
padding:32px 16px 80px;
min-height:100vh;
}
.wrap{max-width:760px;margin:0 auto;}
/* Letterhead */
.letterhead{
border-bottom:3px double var(--ink);
padding-bottom:16px;
margin-bottom:28px;
display:flex;
justify-content:space-between;
align-items:flex-end;
flex-wrap:wrap;
gap:10px;
}
.letterhead .eyebrow{
font-family:'IBM Plex Mono', monospace;
font-size:11px;
letter-spacing:0.18em;
color:var(--ink-soft);
text-transform:uppercase;
}
h1{
font-family:'Noto Serif TC', serif;
font-weight:900;
font-size:clamp(28px,5vw,40px);
margin:4px 0 0;
letter-spacing:0.02em;
line-height:1.15;
}
.doc-no{
font-family:'IBM Plex Mono', monospace;
font-size:12px;
color:var(--ink-soft);
text-align:right;
}
.subtitle{
font-size:14.5px;
color:var(--ink-soft);
margin:10px 0 0;
line-height:1.7;
}
/* Card */
.card{
background:var(--card);
border:1px solid var(--line-dark);
border-radius:2px;
box-shadow:0 1px 0 var(--line), 0 8px 24px -12px rgba(20,24,18,0.25);
padding:26px 24px;
margin-bottom:22px;
position:relative;
}
.card-title{
font-family:'IBM Plex Mono', monospace;
font-size:12px;
letter-spacing:0.12em;
text-transform:uppercase;
color:var(--ink-soft);
margin:0 0 18px;
display:flex;
align-items:center;
gap:10px;
}
.card-title::after{
content:"";
flex:1;
height:1px;
background:var(--line);
}
/* Form */
.grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
.field{margin-bottom:16px;}
.field.full{grid-column:1/-1;}
label{
display:block;
font-size:12.5px;
font-weight:700;
color:var(--ink);
margin-bottom:6px;
}
label .opt{font-weight:400;color:var(--ink-soft);}
input[type="text"], input[type="number"], select{
width:100%;
padding:10px 11px;
background:#fff;
border:1px solid var(--line-dark);
border-radius:2px;
font-family:'Noto Sans TC', sans-serif;
font-size:14.5px;
color:var(--ink);
}
input:focus, select:focus{
outline:2px solid var(--safe);
outline-offset:1px;
}
.amt-row{display:flex; gap:8px;}
.amt-row input{flex:2;}
.amt-row select{flex:1;min-width:88px;}
.hint{font-size:12px;color:var(--ink-soft);margin-top:5px;line-height:1.5;}
.form-cols{
display:grid;
grid-template-columns:1fr 1fr;
gap:0 24px;
align-items:start;
}
.form-col{display:flex;flex-direction:column;}
.form-col .field{margin-bottom:16px;}
.readout{
padding:10px 11px;
background:var(--paper-dim);
border:1px dashed var(--line-dark);
border-radius:2px;
font-family:'IBM Plex Mono', monospace;
font-size:13.5px;
color:var(--ink-soft);
min-height:20px;
line-height:1.6;
}
.readout.filled{color:var(--ink); font-weight:600;}
.example-btn{
font-family:'IBM Plex Mono', monospace;
font-size:12px;
background:none;
border:1px solid var(--line-dark);
color:var(--ink-soft);
padding:6px 12px;
border-radius:2px;
cursor:pointer;
}
.example-btn:hover{border-color:var(--ink); color:var(--ink);}
.calc-btn{
width:100%;
padding:14px;
background:var(--ink);
color:var(--paper);
border:none;
border-radius:2px;
font-family:'Noto Serif TC', serif;
font-weight:700;
font-size:16px;
letter-spacing:0.06em;
cursor:pointer;
margin-top:6px;
}
.calc-btn:hover{background:#333a2d;}
/* Result */
#result{display:none;}
.result-header{
display:flex;
justify-content:space-between;
align-items:center;
gap:16px;
margin-bottom:20px;
}
.food-tag{
font-family:'Noto Serif TC', serif;
font-weight:700;
font-size:19px;
}
.food-meta{
font-family:'IBM Plex Mono', monospace;
font-size:12px;
color:var(--ink-soft);
}
/* Stamp */
.stamp{
font-family:'Noto Serif TC', serif;
font-weight:900;
font-size:15px;
letter-spacing:0.12em;
border:3px solid currentColor;
border-radius:6px;
padding:6px 12px;
transform:rotate(-7deg);
display:inline-block;
white-space:nowrap;
mix-blend-mode:multiply;
opacity:0.88;
}
.stamp::before{content:"※ ";}
.stamp.safe{color:var(--safe);}
.stamp.warn{color:var(--warn);}
.stamp.danger{color:var(--danger);}
/* Gauge */
.gauge-block{margin-bottom:22px;}
.gauge-label-row{
display:flex;justify-content:space-between;align-items:baseline;
font-family:'IBM Plex Mono', monospace;
font-size:12px;color:var(--ink-soft);margin-bottom:6px;
}
.gauge-track{
position:relative;
height:14px;
background:var(--paper-dim);
border:1px solid var(--line-dark);
border-radius:7px;
overflow:hidden;
}
.gauge-fill{
height:100%;
border-radius:7px 0 0 7px;
transition:width 0.6s ease;
}
.gauge-marker{
position:absolute;top:-4px;
width:2px;height:22px;
background:var(--ink);
}
.gauge-ticks{
display:flex;justify-content:space-between;
font-family:'IBM Plex Mono', monospace;
font-size:10px;color:var(--ink-soft);margin-top:4px;
}
/* Data rows */
.datarow{
display:flex;justify-content:space-between;align-items:baseline;
padding:10px 0;
border-top:1px dashed var(--line);
gap:12px;
}
.datarow .k{font-size:13px;color:var(--ink-soft);}
.datarow .v{
font-family:'IBM Plex Mono', monospace;
font-weight:600;font-size:14.5px;
text-align:right;
}
.datarow .v small{font-weight:400;color:var(--ink-soft);font-size:11.5px;}
.datarow.qty-sensitive{background:var(--safe-bg); margin:0 -12px; padding:10px 12px; border-top:1px dashed var(--line);}
.verdict-box{
margin-top:18px;
padding:16px 16px;
border-left:4px solid var(--ink);
background:var(--paper-dim);
border-radius:0 2px 2px 0;
font-size:14px;
line-height:1.75;
}
.verdict-box.safe{border-color:var(--safe);background:var(--safe-bg);}
.verdict-box.warn{border-color:var(--warn);background:var(--warn-bg);}
.verdict-box.danger{border-color:var(--danger);background:var(--danger-bg);}
.verdict-box h3{
margin:0 0 8px;font-family:'Noto Serif TC', serif;font-size:15px;
}
.verdict-box p{white-space:pre-line;}
.section-title{
font-family:'Noto Serif TC', serif;
font-weight:700;font-size:14.5px;
margin:22px 0 8px;
display:flex;align-items:center;gap:8px;
}
.section-title .num{
font-family:'IBM Plex Mono', monospace;
font-size:11px;color:var(--ink-soft);
border:1px solid var(--line-dark);
border-radius:50%;
width:20px;height:20px;
display:flex;align-items:center;justify-content:center;
}
.body-text{font-size:13.5px;line-height:1.85;color:var(--ink);}
.body-text + .body-text{margin-top:6px;}
.footer-note{
font-size:11.5px;color:var(--ink-soft);
line-height:1.8;
border-top:1px solid var(--line);
padding-top:14px;margin-top:26px;
}
.footer-note b{color:var(--ink);}
@media (max-width:560px){
.grid{grid-template-columns:1fr;}
.form-cols{grid-template-columns:1fr;}
.letterhead{flex-direction:column;align-items:flex-start;}
.doc-no{text-align:left;}
.result-header{flex-direction:column;align-items:flex-start;}
}
</style>
</head>
<body>
<div class="wrap">
<div class="letterhead">
<div>
<div class="eyebrow">食品有害物質 · 攝取風險試算</div>
<h1>吃了該怎麼辦?</h1>
<p class="subtitle">輸入食物、攝取量與檢出的有害物質含量,換算成對照法規限量與國際安全攝取值的結果,並提供實用建議。</p>
</div>
<div class="doc-no">試算單編號<br>NO. FS-CALC</div>
</div>
<!-- INPUT CARD -->
<div class="card">
<p class="card-title">輸入資料</p>
<div class="form-cols">
<!-- LEFT COLUMN: raw inputs -->
<div class="form-col">
<div class="field">
<label>食物名稱</label>
<input type="text" id="foodName" placeholder="例:大豆沙拉油">
</div>
<div class="field">
<label>攝取頻率</label>
<select id="frequency">
<option value="1">只有這一次(偶發)</option>
<option value="daily" selected>每天固定吃這個量</option>
<option value="weekly">大約每週一次</option>
<option value="monthly">大約每月一次</option>
</select>
</div>
<div class="field">
<label>攝取量</label>
<div class="amt-row">
<input type="number" id="foodAmount" placeholder="10" min="0" step="any">
<select id="foodUnit">
<option value="g">公克 g</option>
<option value="kg">公斤 kg</option>
<option value="ml">毫升 ml</option>
</select>
</div>
<div class="hint">ml 以密度約等於水估算,油脂類會略有誤差。</div>
</div>
<div class="field">
<label>有害物質</label>
<select id="substance"></select>
</div>
<div class="field">
<label>檢出含量</label>
<div class="amt-row">
<input type="number" id="concValue" placeholder="8.1" min="0" step="any">
<select id="concUnit">
<option value="ugkg">μg/kg (ppb)</option>
<option value="mgkg">mg/kg (ppm)</option>
</select>
</div>
</div>
<div class="field">
<label>自訂法規限量 <span class="opt">(選填,μg/kg)</span></label>
<input type="number" id="customLimit" placeholder="留空則用預設參考值" min="0" step="any">
</div>
</div>
<!-- RIGHT COLUMN: body weight input + live computed read-outs -->
<div class="form-col">
<div class="field">
<label>體重 <span class="opt">(用於換算每公斤體重攝取量,預設 60kg)</span></label>
<input type="number" id="bodyWeight" placeholder="60" value="60" min="1" step="any">
</div>
<div class="field">
<label>本次攝取該物質總量</label>
<div class="readout" id="formIntake">按下方「試算結果」後顯示</div>
</div>
<div class="field">
<label>換算本次個人每公斤攝取量</label>
<div class="readout" id="formPerKg">按下方「試算結果」後顯示</div>
</div>
<div class="field">
<label>連續吃幾年 <span class="opt">(用於估算長期累計量)</span></label>
<input type="number" id="years" placeholder="1" value="1" min="0" step="any">
</div>
<div class="field">
<label>連續吃幾年累計量 <span class="opt">(不考慮排出)</span></label>
<div class="readout" id="formCumulative">按下方「試算結果」後顯示</div>
<div class="hint">⚠️ 這是假設身體完全不代謝、不排出所算出的理論疊加上限,<b>不等於</b>實際體內蓄積量。大多數物質會經由代謝與排泄清除,實際負荷通常遠低於此數字;只有少數難以排出的蓄積型物質(如部分重金屬)才比較接近這個假設。</div>
</div>
</div>
</div>
<div class="field full" style="display:flex; justify-content:space-between; align-items:center; gap:10px; flex-wrap:wrap; margin-top:4px;">
<button class="example-btn" id="exampleBtn">套用範例:大豆沙拉油 10g/苯駢芘 8.1 μg/kg</button>
</div>
<button class="calc-btn" id="calcBtn">試算結果</button>
</div>
<!-- RESULT CARD -->
<div class="card" id="result">
<p class="card-title">試算結果</p>
<div class="result-header">
<div>
<div class="food-tag" id="rFoodName">—</div>
<div class="food-meta" id="rFoodMeta">—</div>
</div>
<div class="stamp" id="rStamp">—</div>
</div>
<div class="gauge-block">
<div class="gauge-label-row"><span id="gaugeLabel">濃度 vs 法規限量</span><span id="gaugePct">0%</span></div>
<div class="gauge-track"><div class="gauge-fill" id="gaugeFill" style="width:0%"></div><div class="gauge-marker" id="gaugeMarker" style="left:100%"></div></div>
<div class="gauge-ticks"><span>0</span><span id="gaugeMax">限量 →</span></div>
</div>
<div class="datarow"><span class="k" id="rRefLabel">對照基準</span><span class="v" id="rRefValue">—</span></div>
<div class="datarow" id="complianceRow" style="display:none;"><span class="k">產品合規性 <small style="color:var(--ink-soft);font-weight:400;">◂ 產品本身濃度,與攝取量無關</small></span><span class="v" id="rComplianceValue">—</span></div>
<p id="unitNote" style="display:none; font-size:12px; color:var(--ink-soft); background:var(--warn-bg); border:1px solid var(--warn); border-radius:2px; padding:10px 12px; margin:10px 0 0; line-height:1.7;"></p>
<div class="verdict-box" id="verdictBox">
<h3 id="verdictTitle">判讀</h3>
<p id="verdictText">—</p>
</div>
<div class="section-title"><span class="num">1</span>吃了會怎樣?</div>
<p class="body-text" id="effectText">—</p>
<div class="section-title"><span class="num">2</span>該怎麼辦?</div>
<p class="body-text" id="adviceText">—</p>
<div class="footer-note">
<b>重要提醒:</b>本工具僅供食品安全教育與初步風險理解使用,非醫療診斷或法規判定依據;不同食品類別的法定限量可能不同,實際合規與否請以衛福部食藥署最新公告為準。若您或家人疑似大量誤食受污染食品並出現不適(如噁心、腹痛、血尿、呼吸困難等),請立即就醫並攜帶產品包裝或單據;對法規或檢驗結果有疑義,可洽詢當地衛生局或食藥署 1919 食安專線。
</div>
</div>
<div style="text-align:center; font-family:'IBM Plex Mono', monospace; font-size:11px; color:var(--ink-soft); margin-top:8px;">
參考依據:食品中污染物質及毒素衛生標準(衛福部)/JECFA/EFSA 公開資料,僅供參考
</div>
</div>
<script>
const UG = 1; // base unit micrograms
const SUBSTANCES = {
benzopyrene: {
label: "苯駢芘 Benzo[a]pyrene(BaP)",
noThreshold: true,
defaultLimit: 2.0, // ug/kg, 食用油脂類
limitNote: "食用油脂類法定限量 2.0 μg/kg(燒烤煙燻肉類 5.0、嬰兒食品 1.0,依食品類別而異,實際請對照公告表)",
bgMean: 0.004, bgHigh: 0.01,
bgNote: "JECFA/EFSA 估算歐洲一般人口平均每日經由正常飲食(穀類、油脂、燒烤煙燻食品等)攝取苯駢芘的背景值約 4 ng/kg 體重/天,高頻食用者約 10 ng/kg 體重/天",
effect: "國際癌症研究總署(IARC)列為第一級致癌物,是燃燒不完全時常見的多環芳香烴。它的風險屬於「長期累積型」——單次少量食用不會有立即急性症狀,但長期反覆攝取超標食品,會提高罹癌風險。",
advice: "若確認是受污染批號,應立即停止食用並依公告辦理退換貨。已經吃下肚的部分不需要恐慌,微量苯駢芘可經由人體代謝排出:可多攝取富含維生素C、E的深色蔬果幫助抗氧化,多吃全穀雜糧增加膳食纖維促進排出,並維持足量飲水與適度運動。真正該做的是往後避免長期食用來源不明或已知超標的油品、燒烤及煙燻食物。"
},
aflatoxin: {
label: "黃麴毒素 Aflatoxin B1",
noThreshold: true,
defaultLimit: 10, // ug/kg 穀類堅果
limitNote: "穀類、堅果等一般食品限量約 10 μg/kg(嬰幼兒食品更嚴格,約 0.1–0.5 μg/kg)",
bgMean: 0.0017,
bgNote: "EFSA 估算歐盟一般人口平均每日黃麴毒素B1飲食暴露量約 0.93–2.45 ng/kg 體重/天(此處取中位約 1.7 ng/kg),主要來自穀類與堅果製品",
effect: "IARC 第一級致癌物,主要影響肝臟。極高劑量誤食(如大量食用嚴重發霉的穀物)可能造成急性肝損傷,但這種情況罕見;一般人的風險來自長期低劑量攝取與肝癌風險的關聯。",
advice: "發霉的花生、穀類、堅果應整包丟棄,不要「切掉發霉部分繼續吃」——毒素會擴散到看似正常的部分。存放時保持乾燥低溫、儘早食用完畢。若懷疑短時間內吃下大量發霉食物並出現噁心、嘔吐、黃疸等症狀,應立即就醫。"
},
lead: {
label: "鉛 Lead(Pb)",
noThreshold: true,
defaultLimit: null,
limitNote: "依食品類別而異(如米類限量約 0.2 mg/kg),建議於「自訂法規限量」欄位輸入對應類別的公告值",
bgMean: 0.5, bgHigh: 1.24,
bgNote: "EFSA 估算歐洲成人平均每日經由一般飲食(穀類、肉類為主要來源)攝取鉛的背景值約 0.5 μg/kg 體重/天,高消費族群約 1.24 μg/kg 體重/天",
effect: "重金屬中毒,對兒童神經發育影響尤其明顯,可能造成學習與行為問題;成人長期暴露與高血壓、腎功能受損有關。JECFA 已於 2010 年撤銷過去的每週容許攝取量,因為研究顯示鉛沒有已知的安全暴露閾值。",
advice: "偶爾單次微量攝取通常不會有急性症狀,真正的風險在於「長期、反覆」食用同一個超標來源。孕婦與幼兒應特別留意來源不明的進口香料、老舊釉彩餐具盛裝的食物。若確認某項產品超標,應停止購買並更換供應來源。"
},
cadmium: {
label: "鎘 Cadmium(Cd)",
noThreshold: false,
defaultLimit: null,
limitNote: "依食品類別而異(如白米限量約 0.4 mg/kg)",
refDosePerDay: 2.5 / 7, // TWI 2.5 ug/kg bw/week -> per day
refBasis: "EFSA 每週容許攝取量(TWI)2.5 μg/kg 體重/週,換算每日約 0.357 μg/kg 體重",
effect: "鎘主要蓄積在腎臟,長期暴露可能造成腎小管功能受損,過去也曾與骨骼病變(痛痛病)的個案有關。單次一般食品攝取量極少造成立即症狀。",
advice: "飲食多樣化、避免長期單一依賴某種高鎘食物(如動物內臟、特定產地稻米、甲殼類頭胸部)能有效分散風險。若試算結果偏高,建議降低該項食物的食用頻率,而非完全禁食。"
},
mercury: {
label: "汞(甲基汞,常見於魚類)",
noThreshold: false,
defaultLimit: null,
limitNote: "依魚種而異,大型掠食性魚類(鮪魚、旗魚、鯊魚)限量較嚴格",
refDosePerDay: 1.6 / 7, // JECFA PTWI methylmercury 1.6 ug/kg bw/week
refBasis: "JECFA 甲基汞每週暫定容許攝取量 1.6 μg/kg 體重/週,換算每日約 0.229 μg/kg 體重",
effect: "具神經毒性,孕婦、備孕婦女與幼兒是高風險族群,可能影響胎兒神經發育;成人長期高量暴露可能出現肢體麻木、視野縮小等症狀。",
advice: "孕婦、備孕及幼兒建議限制鮪魚、旗魚、油魚、鯊魚等大型掠食性魚類的食用頻率,改選鯖魚、鮭魚、吳郭魚、鯛魚等低汞選擇。一般成人偶爾食用不需過度恐慌,重點在避免「高頻率、大型掠食性魚種」的組合。"
},
arsenic: {
label: "無機砷 Inorganic Arsenic",
noThreshold: true,
defaultLimit: null,
limitNote: "米製品:糙米約 0.35 mg/kg、白米約 0.2 mg/kg(供嬰幼兒食品製造用者限量更嚴格)",
bgMean: 0.2, bgHigh: 0.33,
bgNote: "EFSA 估算成人平均每日無機砷飲食暴露量約 0.09–0.38 μg/kg 體重/天(此處取中位約 0.2),主要來自米飯與穀類;EFSA另訂有 0.06 μg/kg 體重/天的皮膚癌風險參考點,供對照",
effect: "IARC 第一級致癌物,長期暴露與皮膚、膀胱、肺癌風險相關。飲食中的主要來源是米飯與米製品,因為水稻容易從土壤與灌溉水中吸收砷。",
advice: "建議主食來源多樣化(米飯、麵食、其他穀類交替),淘米及以較多水量烹煮並瀝去多餘水分,有助降低米飯中的砷含量。幼兒副食品可選擇標示低砷檢驗的米製品。"
},
melamine: {
label: "三聚氰胺 Melamine",
noThreshold: false,
defaultLimit: 2500, // 2.5 mg/kg general food
limitNote: "一般食品限量 2.5 mg/kg;嬰幼兒配方食品限量更嚴格,約 1 mg/kg",
refDosePerDay: 200, // WHO provisional TDI 0.2 mg/kg bw/day
refBasis: "WHO 暫訂每日容許攝取量(TDI)0.2 mg/kg 體重/天",
effect: "高劑量攝取可能造成腎結石與急性腎損傷,嬰幼兒風險最高(如 2008 年毒奶粉事件多數受害者為嬰兒)。一般食品中的微量檢出通常來自包裝材料遷移,風險遠低於刻意摻雜事件。",
advice: "選購有檢驗合格標示的乳製品與食品。若懷疑幼兒短時間內攝入大量受污染乳製品,並出現血尿、排尿困難、腹痛、躁動不安等症狀,應立即就醫。"
},
dehp: {
label: "塑化劑 DEHP",
noThreshold: false,
defaultLimit: null,
limitNote: "依用途分別規範於容器溶出限量與添加物殘留基準,個案而異",
refDosePerDay: 50, // 0.05 mg/kg bw/day
refBasis: "歐盟/台灣參考之每日容許攝取量(TDI)0.05 mg/kg 體重/天",
effect: "屬內分泌干擾物質,動物實驗顯示長期高劑量暴露可能影響生殖與發育功能。2011 年台灣「塑化劑事件」為不肖業者違法將 DEHP 摻入起雲劑所致,非食品天然成分。",
advice: "避免用保鮮膜或塑膠容器盛裝高溫、高油脂或酸性食物;選擇玻璃、不鏽鋼或標示不含塑化劑的容器。已知受污染批號產品應立即停用並依公告辦理退換。"
},
bpa: {
label: "雙酚A BPA",
noThreshold: false,
defaultLimit: null,
limitNote: "依容器材質溶出標準規範",
refDosePerDay: 4, // old EFSA TDI 4 ug/kg bw/day (2015)
refBasis: "EFSA 2015 年舊版每日容許攝取量 4 μg/kg 體重/天(注意:EFSA 2023 年將新版建議值大幅下修至約 0.0002 μg/kg 體重/天,新標準目前仍具科學爭議、尚未被多數國家法規採用,此處以較常用的舊值試算供參考)",
effect: "內分泌干擾物質,可能影響生殖發育與代謝功能,常見於聚碳酸酯容器與部分罐頭內塗層的微量溶出。",
advice: "避免用聚碳酸酯(PC,回收碼 7)材質容器盛裝高溫食物或反覆微波,減少長期食用同一來源罐頭食品的頻率,可選擇玻璃、不鏽鋼或標示 BPA free 的容器替代。"
},
nitrite: {
label: "亞硝酸鹽(以亞硝酸根計)",
noThreshold: false,
defaultLimit: null,
limitNote: "依食品類別規範(如肉製品發色劑限量約 0.07 g/kg)",
refDosePerDay: 60, // JECFA ADI 0.06 mg/kg bw/day
refBasis: "JECFA 每日容許攝取量(ADI)0.06 mg/kg 體重/天(以亞硝酸根計)",
effect: "高劑量可能造成變性血紅素血症(尤其嬰兒風險較高),長期攝取也與體內形成亞硝胺(潛在致癌物)的疑慮有關,常見於香腸、火腿、臘肉等醃漬肉品的發色劑。",
advice: "減少加工醃漬肉品的食用頻率,搭配富含維生素C的蔬果一起食用,有助抑制亞硝胺的形成。未滿一歲的嬰兒不建議食用硝酸鹽含量偏高的葉菜濃湯或加工肉品。"
},
acrylamide: {
label: "丙烯醯胺 Acrylamide",
noThreshold: true,
defaultLimit: null,
limitNote: "台灣目前無強制限量標準,僅有參考指標(如歐盟基準水平),此欄可留空",
bgMean: 0.5, bgHigh: 1.9,
bgNote: "EFSA 估算歐洲成人平均每日丙烯醯胺飲食暴露量約 0.3–1.1 μg/kg 體重/天(此處取中位約 0.5),高消費族群約 1.9 μg/kg 體重/天,主要來自油炸澱粉類食品與咖啡",
effect: "高溫油炸、烘烤澱粉類食品(洋芋片、薯條、咖啡、餅乾)時天然產生的反應副產物。動物實驗顯示具致癌性與神經毒性疑慮,人體流行病學證據仍在累積中,屬於「愈少愈好」而非有明確安全上限的物質。",
advice: "減少高溫油炸澱粉類食品的食用頻率,烹調時避免過度金黃焦褐(顏色愈深通常代表丙烯醯胺愈多),可改用蒸、煮、炒等較低溫的烹調方式。"
},
custom: {
label: "其他/自訂物質",
noThreshold: true,
defaultLimit: null,
limitNote: "請於下方輸入您所知的法規限量或安全參考值(μg/kg),若不清楚可留空僅看攝取總量",
effect: "此物質未內建於資料庫,請自行查閱衛福部食藥署公告或相關文獻確認其健康效應與安全攝取值。",
advice: "建議先確認食品來源是否為公告回收批號,並以官方公布的處理方式為準;如有不適,請洽醫療院所並攜帶產品資訊。"
}
};
const substSelect = document.getElementById('substance');
Object.entries(SUBSTANCES).forEach(([key, s]) => {
const opt = document.createElement('option');
opt.value = key;
opt.textContent = s.label;
substSelect.appendChild(opt);
});
document.getElementById('exampleBtn').addEventListener('click', () => {
document.getElementById('foodName').value = "大豆沙拉油";
document.getElementById('foodAmount').value = 10;
document.getElementById('foodUnit').value = "g";
document.getElementById('frequency').value = "daily";
document.getElementById('substance').value = "benzopyrene";
document.getElementById('bodyWeight').value = 60;
document.getElementById('concValue').value = 8.1;
document.getElementById('concUnit').value = "ugkg";
document.getElementById('customLimit').value = "";
document.getElementById('years').value = 5;
});
function fmt(n, digits=3){
if (n === null || n === undefined || isNaN(n)) return "—";
if (n === 0) return "0";
if (Math.abs(n) < 0.001) return n.toExponential(2);
return Number(n.toPrecision(digits)).toLocaleString('en-US', {maximumFractionDigits: 6});
}
document.getElementById('calcBtn').addEventListener('click', () => {
const foodName = document.getElementById('foodName').value.trim() || "(未命名食物)";
const amount = parseFloat(document.getElementById('foodAmount').value);
const unit = document.getElementById('foodUnit').value;
const freq = document.getElementById('frequency').value;
const subKey = document.getElementById('substance').value;
const bw = parseFloat(document.getElementById('bodyWeight').value) || 60;
const concVal = parseFloat(document.getElementById('concValue').value);
const concUnit = document.getElementById('concUnit').value;
const customLimit = parseFloat(document.getElementById('customLimit').value);
const years = parseFloat(document.getElementById('years').value);
if (isNaN(amount) || isNaN(concVal)) {
alert("請輸入攝取量與檢出含量。");
return;
}
const sub = SUBSTANCES[subKey];
// normalize amount to kg
let amountKg;
if (unit === 'g') amountKg = amount / 1000;
else if (unit === 'kg') amountKg = amount;
else amountKg = amount / 1000; // ml approx
// normalize concentration to ug/kg
const concUgKg = concUnit === 'mgkg' ? concVal * 1000 : concVal;
// total intake in ug (this occasion)
const intakeUg = amountKg * concUgKg;
const perKgBw = intakeUg / bw; // ug/kg bw, this occasion
// frequency-adjusted daily-equivalent (for TDI/ADI comparison)
let dailyFactor;
if (freq === '1') dailyFactor = null; // single occasion, no averaging
else if (freq === 'daily') dailyFactor = 1;
else if (freq === 'weekly') dailyFactor = 1/7;
else if (freq === 'monthly') dailyFactor = 1/30;
const dailyEquivPerKgBw = dailyFactor === null ? null : perKgBw * dailyFactor;
// cumulative amount over N years, ignoring metabolism/excretion (naive worst-case ceiling)
const occurrencesPerYear = dailyFactor === null ? null : 365 * dailyFactor;
const yearsVal = isNaN(years) ? 1 : years;
const cumulativeUg = occurrencesPerYear === null ? intakeUg : intakeUg * occurrencesPerYear * yearsVal;
const cumulativePerKgBw = cumulativeUg / bw;
const effectiveLimit = !isNaN(customLimit) ? customLimit : sub.defaultLimit;
// Build result
document.getElementById('rFoodName').textContent = foodName;
const freqLabelMap = {'1':'偶發單次','daily':'每天固定攝取','weekly':'每週一次','monthly':'每月一次'};
document.getElementById('rFoodMeta').textContent =
`${sub.label} / 攝取 ${amount} ${unit==='g'?'g':unit==='kg'?'kg':'ml'} / 檢出 ${concVal} ${concUnit==='mgkg'?'mg/kg':'μg/kg'} / ${freqLabelMap[freq]}`;
const formIntakeEl = document.getElementById('formIntake');
const formPerKgEl = document.getElementById('formPerKg');
const formCumulativeEl = document.getElementById('formCumulative');
formIntakeEl.className = 'readout filled';
formPerKgEl.className = 'readout filled';
formCumulativeEl.className = 'readout filled';
formIntakeEl.innerHTML = `${fmt(intakeUg)} μg <small>(= ${fmt(intakeUg/1000,4)} mg)</small>`;
formPerKgEl.innerHTML = `${fmt(perKgBw,4)} μg/kg體重 <small>/ 本次,除以您的體重</small>`;
if (occurrencesPerYear === null) {
formCumulativeEl.innerHTML = `不適用 <small>(偶發單次沒有「連續吃幾年」的概念,數字同「本次攝取總量」)</small>`;
} else {
formCumulativeEl.innerHTML =
`${fmt(cumulativeUg)} μg <small>(≈ ${fmt(cumulativeUg/1000,4)} mg,約 ${fmt(occurrencesPerYear,3)} 次/年 × ${fmt(yearsVal,3)} 年)</small><br>` +
`${fmt(cumulativePerKgBw,4)} μg/kg體重(累計,理論值)`;
}
const unitNoteEl = document.getElementById('unitNote');
unitNoteEl.style.display = 'none';
document.getElementById('complianceRow').style.display = 'none';
let stampClass, stampText, verdictClass, verdictTitle, verdictText;
let gaugePct = 0, gaugeLabel, gaugeMaxLabel, refLabel, refValueText;
if (!sub.noThreshold && sub.refDosePerDay) {
// threshold-based substance
refLabel = "安全攝取基準";
refValueText = `${fmt(sub.refDosePerDay,3)} μg/kg 體重/天`;
document.getElementById('rRefLabel').textContent = refLabel;
document.getElementById('rRefValue').innerHTML = `${refValueText} <small>(${sub.refBasis})</small>`;
if (dailyEquivPerKgBw === null) {
gaugeLabel = "單次攝取(未換算長期平均)";
gaugePct = Math.min(100, (perKgBw / sub.refDosePerDay) * 100);
gaugeMaxLabel = "安全基準 →";
if (gaugePct < 100) {
stampClass='safe'; stampText='單次無虞'; verdictClass='safe';
verdictTitle = "單次攝取量遠低於長期安全基準";
verdictText = `這是單次/偶發攝取,即使把整份量除以體重,也只達到每日安全攝取基準的 ${fmt(gaugePct,2)}%。單次少量食用一般不需擔心,真正該留意的是「長期反覆」吃這類食物的頻率。`;
} else {
stampClass='warn'; stampText='單次偏高'; verdictClass='warn';
verdictTitle = "單次攝取量已達到安全基準量級";
verdictText = `這一次的攝取量,若換算成每公斤體重,已達到(或超過)每日安全攝取基準。雖然是單次事件,但建議留意身體反應,並避免短期內重複食用同一來源。`;
}
} else {
gaugePct = Math.min(999, (dailyEquivPerKgBw / sub.refDosePerDay) * 100);
gaugeLabel = "長期平均攝取量 vs 安全基準";
gaugeMaxLabel = "100% 安全基準 →";
document.getElementById('formPerKg').innerHTML += ` <br><small style="color:var(--ink-soft)">換算長期平均:${fmt(dailyEquivPerKgBw,4)} μg/kg 體重/天</small>`;
if (gaugePct < 10) {
stampClass='safe'; stampText='風險很低'; verdictClass='safe';
verdictTitle = "遠低於安全攝取基準";
verdictText = `以此頻率長期食用,估算攝取量僅為國際安全基準的 ${fmt(gaugePct,2)}%,風險很低,不需特別調整飲食習慣。`;
} else if (gaugePct < 50) {
stampClass='safe'; stampText='尚在安全範圍'; verdictClass='safe';
verdictTitle = "在安全攝取基準之內";
verdictText = `以此頻率長期食用,估算攝取量約為安全基準的 ${fmt(gaugePct,2)}%,仍在容許範圍內,但建議飲食多樣化,避免單一來源長期偏高。`;
} else if (gaugePct < 100) {
stampClass='warn'; stampText='建議留意'; verdictClass='warn';
verdictTitle = "已接近安全攝取基準,建議降低頻率";
verdictText = `以此頻率長期食用,估算攝取量已達安全基準的 ${fmt(gaugePct,2)}%。建議降低食用頻率或減少單次份量,並搭配其他食物來源分散風險。`;
} else {
stampClass='danger'; stampText='超過基準'; verdictClass='danger';
verdictTitle = "長期平均攝取量已超過安全基準";
verdictText = `以此頻率長期食用,估算攝取量已達安全基準的 ${fmt(gaugePct,2)}%,超出建議範圍。建議明顯降低此食物的食用頻率或份量,並留意是否有相關症狀,必要時諮詢醫師或營養師。`;
}
}
} else {
// no-threshold substance: primary read-out is personal dose vs typical daily background dietary exposure
// (this DOES scale with how much you eat), compliance vs legal limit is shown separately (does NOT scale with amount)
document.getElementById('complianceRow').style.display = 'none';
if (sub.bgMean) {
const doseForCompare = dailyFactor === null ? perKgBw : dailyEquivPerKgBw;
const bgRatio = doseForCompare / sub.bgMean;
refLabel = "個人攝取量 vs 一般人日常飲食背景暴露量";
document.getElementById('rRefLabel').textContent = refLabel;
document.getElementById('rRefValue').innerHTML =
`約為背景值的 ${fmt(bgRatio*100,3)}% <small>(背景值 ${fmt(sub.bgMean,3)} μg/kg體重/天${sub.bgHigh?`,高頻食用者約 ${fmt(sub.bgHigh,3)}`:''})</small>`;
document.getElementById('formPerKg').innerHTML +=
(dailyFactor !== null && dailyFactor !== 1) ? ` <br><small style="color:var(--ink-soft)">換算長期平均:${fmt(dailyEquivPerKgBw,5)} μg/kg 體重/天</small>` : '';
gaugePct = Math.min(999, bgRatio * 100);
gaugeLabel = dailyFactor === null ? "本次額外攝取 vs 日常背景值" : "個人攝取量 vs 日常背景值";
gaugeMaxLabel = "100% = 日常背景值 →";
const freqPhrase = dailyFactor === null ? "這一次額外吃下的量" : "以此頻率長期食用,估算平均每天攝取的量";
if (bgRatio < 1) {
stampClass='safe'; stampText='低於日常背景值'; verdictClass='safe';
verdictTitle = "低於一般人日常飲食背景暴露量";
verdictText = `${freqPhrase},只有一般人每天正常飲食中本來就會吃下的背景量的 ${fmt(bgRatio*100,2)}%。這個量在人體日常代謝負荷範圍內,不需要特別擔心。`;
} else if (bgRatio < 3) {
stampClass='safe'; stampText='與背景值相近'; verdictClass='safe';
verdictTitle = "與一般人日常飲食背景暴露量相近";
verdictText = `${freqPhrase},大約是一般人每天正常飲食背景暴露量的 ${fmt(bgRatio,2)} 倍。仍在一般飲食中常見的波動範圍內,不需恐慌,但也不建議把這個來源當作長期固定攝取的食物。`;
} else if (bgRatio < 10) {
stampClass='warn'; stampText=`約${fmt(bgRatio,2)}倍背景值`; verdictClass='warn';
verdictTitle = "明顯高於一般人日常飲食背景暴露量";
verdictText = `${freqPhrase},是一般人每天正常飲食背景暴露量的 ${fmt(bgRatio,2)} 倍。建議降低這項食物的食用頻率或份量,並搭配其他來源分散風險;此類物質風險與長期累積總量有關,並非吃這一次就會有立即症狀。`;
} else {
stampClass='danger'; stampText=`約${fmt(bgRatio,2)}倍背景值`; verdictClass='danger';
verdictTitle = "遠高於一般人日常飲食背景暴露量";
verdictText = `${freqPhrase},達到一般人每天正常飲食背景暴露量的 ${fmt(bgRatio,2)} 倍,明顯偏高。建議大幅降低或停止食用這項來源,並留意身體是否有異常反應;此類物質屬於長期累積型風險,若能確認來源是受污染批號,應立即依公告辦理退換貨。`;
}
} else {
// fallback for substances without a background-exposure reference (e.g. custom)
refLabel = "對照基準";
document.getElementById('rRefLabel').textContent = refLabel;
document.getElementById('rRefValue').textContent = "此物質尚無內建的日常背景暴露量參考值,僅顯示攝取總量供參考";
gaugePct = 0;
gaugeLabel = "缺少參考值,無法繪製比例";
gaugeMaxLabel = "";
stampClass='warn'; stampText='待確認'; verdictClass='warn';
verdictTitle = "尚無足夠參考值,僅供參考攝取總量";
verdictText = `此物質未內建安全參考值,本次試算僅算出攝取總量,無法判斷風險高低。建議查閱衛福部食藥署公告或相關文獻確認其安全攝取範圍。`;
}
// secondary: is the product itself compliant with the regulatory concentration limit?
// this does NOT depend on how much you ate — it's a property of the food itself
if (effectiveLimit && effectiveLimit > 0) {
const ratio = concUgKg / effectiveLimit;
document.getElementById('complianceRow').style.display = 'flex';
document.getElementById('complianceRow').className = 'datarow ' + (ratio > 1 ? '' : '');
if (ratio <= 1) {
document.getElementById('rComplianceValue').innerHTML = `<span style="color:var(--safe)">符合限量</span> <small>(檢出濃度為限量的 ${fmt(ratio*100,3)}%,限量 ${fmt(effectiveLimit)} μg/kg)</small>`;
} else {
document.getElementById('rComplianceValue').innerHTML = `<span style="color:var(--danger)">超標 ${fmt(ratio,3)} 倍</span> <small>(限量 ${fmt(effectiveLimit)} μg/kg)</small>`;
// if the product itself is over the legal limit, that's a hard override regardless of personal dose
stampClass='danger'; stampText=`產品超標 ${fmt(ratio,2)} 倍`; verdictClass='danger';
verdictTitle = `這批食物本身濃度超過法規限量約 ${fmt(ratio,3)} 倍`;
verdictText = `不論這次吃了多少,這批食物本身的檢出濃度已超過法定限量,屬於不合格產品,應停止食用、依公告辦理退換貨並保留單據。\n\n另外,以您本次的攝取量換算,個人劑量約為一般人日常飲食背景暴露量的 ${fmt((dailyFactor === null ? perKgBw : dailyEquivPerKgBw) / (sub.bgMean||1) ,2)} 倍(見上方「對照基準」),這部分反映的是您吃下肚的健康風險程度,兩者請分開看待。`;
}
unitNoteEl.style.display = 'block';
unitNoteEl.innerHTML = `⚠️ 單位提醒:「產品合規性」比的是檢出濃度(μg/kg <b>食品</b>)跟法規限量,跟攝取量無關;「對照基準」比的是您的個人劑量(μg/kg <b>體重</b>)跟日常背景暴露量,會隨攝取量變動。兩者分母不同,請分開解讀。`;
}
}
document.getElementById('rStamp').textContent = stampText;
document.getElementById('rStamp').className = 'stamp ' + stampClass;
document.getElementById('gaugeLabel').textContent = gaugeLabel;
document.getElementById('gaugePct').textContent = fmt(Math.min(gaugePct,999),3) + '%';
document.getElementById('gaugeFill').style.width = Math.min(gaugePct,100) + '%';
document.getElementById('gaugeFill').style.background =
stampClass==='safe' ? 'var(--safe)' : stampClass==='warn' ? 'var(--warn)' : 'var(--danger)';
document.getElementById('gaugeMarker').style.left = Math.min(gaugePct,100) + '%';
document.getElementById('gaugeMax').textContent = gaugeMaxLabel;
document.getElementById('verdictBox').className = 'verdict-box ' + verdictClass;
document.getElementById('verdictTitle').textContent = verdictTitle;
document.getElementById('verdictText').textContent = verdictText;
document.getElementById('effectText').textContent = sub.effect + (sub.limitNote ? `(法規限量參考:${sub.limitNote})` : '');
document.getElementById('adviceText').textContent = sub.advice;
document.getElementById('result').style.display = 'block';
document.getElementById('result').scrollIntoView({behavior:'smooth', block:'start'});
});
</script>
</body>
</html>
沒有留言:
張貼留言