function escapeHTML(str) {
if (!str) return '';
return String(str).replace(/[&<>'"]/g,
tag => ({
'&': '&',
'<': '<',
'>': '>',
"'": ''',
'"': '"'
}[tag])
);
}
let quizActive = false;
let isQuizDL = false, isQuizHV = false;
function toggleQuizDL() {
isQuizDL = !isQuizDL;
const btn = event.currentTarget;
if (isQuizDL) {
btn.className = "px-4 py-2 bg-amber-600 hover:bg-amber-500 text-white font-bold rounded-lg text-xs flex items-center gap-1.5 transition-all flex-shrink-0 shadow-lg shadow-amber-900/50";
} else {
btn.className = "px-4 py-2 bg-stone-800 hover:bg-stone-700 text-amber-500 border border-stone-700 font-bold rounded-lg text-xs flex items-center gap-1.5 transition-all flex-shrink-0";
}
filterDuocLieu();
}
function toggleQuizHV() {
isQuizHV = !isQuizHV;
const btn = event.currentTarget;
if (isQuizHV) {
btn.className = "px-4 py-2 bg-amber-600 hover:bg-amber-500 text-white font-bold rounded-lg text-xs flex items-center gap-1.5 transition-all flex-shrink-0 shadow-lg shadow-amber-900/50";
} else {
btn.className = "px-4 py-2 bg-stone-800 hover:bg-stone-700 text-amber-500 border border-stone-700 font-bold rounded-lg text-xs flex items-center gap-1.5 transition-all flex-shrink-0";
}
filterHuyetVi();
}
function capNhatTongSoTracNghiem() {
let total = 0;
if (typeof questionsData !== 'undefined' && Array.isArray(questionsData)) {
total += questionsData.length;
} else if (Array.isArray(window.questionsData)) {
total += window.questionsData.length;
}
if (typeof database !== 'undefined') total += Object.keys(database).length;
if (typeof duocLieuData !== 'undefined' && Array.isArray(duocLieuData)) total += duocLieuData.length;
if (typeof huyetViData !== 'undefined' && Array.isArray(huyetViData)) total += huyetViData.length;
if (typeof traData !== 'undefined' && Array.isArray(traData)) total += traData.length;
const el = document.getElementById('total-quiz');
if (el) el.innerText = total;
}
const getVal = (id) => document.getElementById(id)?.value ?? '';
function debounce(func, delay = 100) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => func.apply(this, args), delay);
};
}
const debounceSearchLuanTri = debounce(searchLuanTri, 100);
const debounceFilterDuocLieu = debounce(filterDuocLieu, 100);
const debounceFilterHuyetVi = debounce(filterHuyetVi, 100);
const debounceFilterTra = debounce(filterTra, 100);
function capNhatTongSoTrieuChung() {
if (typeof database === 'undefined' || !database) return;
const tatCaTrieuChung = [];
Object.values(database).forEach(hoiChung => {
if (hoiChung?.tc && Array.isArray(hoiChung.tc)) {
tatCaTrieuChung.push(...hoiChung.tc);
}
});
const trieuChungDuyNhat = [...new Set(tatCaTrieuChung.filter(Boolean))];
const elementTong = document.getElementById('total-chung');
if (elementTong) elementTong.innerText = trieuChungDuyNhat.length;
}
window.addEventListener("DOMContentLoaded", () => {
requestAnimationFrame(() => {
try {
capNhatTongSoTrieuChung();
capNhatTongSoTracNghiem();
if (typeof duocLieuData !== 'undefined' && duocLieuData) {
const el = document.getElementById('total-thuoc');
if (el) el.textContent = duocLieuData.length;
}
if (typeof huyetViData !== 'undefined' && huyetViData) {
const el = document.getElementById('total-huyet');
if (el) el.textContent = huyetViData.length;
}
if (typeof traData !== 'undefined' && traData) {
const el = document.getElementById('total-tra');
if (el) el.textContent = traData.length;
}
} catch (err) {
console.error("Lỗi khởi tạo:", err);
}
const savedTab = localStorage.getItem('activeTab') || 'luantri';
switchTab(savedTab);
});
});
// 2. Nạp dữ liệu tương ứng khi người dùng mở từng Tab
async function switchTab(tabName) {
// Chỉ nạp dữ liệu của Tab đó khi người dùng bấm vào
if (tabName === 'luantri') await loadScript('luantridata.js');
if (tabName === 'duoclieu') await loadScript('duoclieudata.js');
if (tabName === 'huyetvi') await loadScript('huyetvidata.js');
if (tabName === 'tra') await loadScript('tradata.js');
if (tabName === 'tracnghiem') await loadScript('questiondata.js');
if (tabName !== 'xemanh') {
localStorage.setItem('activeTab', tabName);
}
const tabs = ['luantri', 'tracnghiem', 'duoclieu', 'huyetvi', 'tra', 'xemanh', 'ai'];
const pascalMap = {
luantri: 'LuanTri',
tracnghiem: 'TracNghiem',
duoclieu: 'DuocLieu',
huyetvi: 'HuyetVi',
tra: 'Tra',
xemanh: 'XemAnh',
ai: 'AI'
};
tabs.forEach(t => {
const pascalCase = pascalMap[t];
const section = document.getElementById('section' + pascalCase);
const btn = document.getElementById('btnTab' + pascalCase);
if (t === tabName) {
if (section) {
section.classList.remove('hidden');
section.style.opacity = '0';
section.style.transform = 'translateY(6px)';
section.style.transition = 'all 0.15s ease';
requestAnimationFrame(() => {
section.style.opacity = '1';
section.style.transform = 'translateY(0)';
});
}
if (btn) btn.classList.add('tab-active', 'text-primary');
if (t === 'duoclieu') filterDuocLieu();
if (t === 'huyetvi') filterHuyetVi();
if (t === 'tra') filterTra();
if (t === 'tracnghiem') capNhatDiemGanNhat();
} else {
if (section) section.classList.add('hidden');
if (btn) btn.classList.remove('tab-active', 'text-primary');
}
});
}
let currentQuizQuestions = [];
let currentQuizIndex = 0;
let quizScore = 0;
let userAnswers = [];
function capNhatDiemGanNhat() {
const history = JSON.parse(localStorage.getItem('quizHistory') || '[]');
const el = document.getElementById('quiz-last-score');
if (el && history.length > 0) {
el.innerHTML = `⏱️ Lần gần nhất (${history[0].date}): ${history[0].score} (${history[0].percent})`;
}
}
function taoCauHoiTamTuDuLieu(category) {
let generated = [];
if ((category === 'luantri' || category === 'all') && typeof database !== 'undefined') {
Object.keys(database).forEach(key => {
const item = database[key];
if (item && item.hc && item.tc && item.tc.length > 0) {
const wrongOptions = Object.values(database)
.map(d => d.hc).filter(hc => hc !== item.hc)
.sort(() => 0.5 - Math.random()).slice(0, 3);
if (wrongOptions.length >= 3) {
const options = [item.hc, ...wrongOptions].sort(() => 0.5 - Math.random());
generated.push({
category: 'luantri',
cau_hoi: `Triệu chứng lâm sàng: "${item.tc.slice(0, 3).join(', ')}..." định hướng đến hội chứng nào?`,
lua_chon: options,
dap_an: options.indexOf(item.hc),
giai_thich: `Biện chứng: ${item.hc} (Pháp trị: ${item.pdt || 'Chưa rõ'})`
});
}
}
});
}
if ((category === 'duoclieu' || category === 'all') && typeof duocLieuData !== 'undefined' && Array.isArray(duocLieuData)) {
duocLieuData.forEach(d => {
if (d && d.ten && d.cong_dung) {
const wrongOptions = duocLieuData
.map(item => item.cong_dung).filter(cd => cd !== d.cong_dung)
.sort(() => 0.5 - Math.random()).slice(0, 3);
if (wrongOptions.length >= 3) {
const options = [d.cong_dung, ...wrongOptions].sort(() => 0.5 - Math.random());
generated.push({
category: 'duoclieu',
cau_hoi: `Vị thuốc "${d.ten}" có công năng chủ trị chính là gì?`,
lua_chon: options,
dap_an: options.indexOf(d.cong_dung),
giai_thich: `Công năng chuẩn: ${d.cong_dung}`
});
}
}
});
}
if ((category === 'huyetvi' || category === 'all') && typeof huyetViData !== 'undefined' && Array.isArray(huyetViData)) {
huyetViData.forEach(h => {
if (h && h.ten && h.chu_tri) {
const wrongOptions = huyetViData
.map(item => item.chu_tri).filter(ct => ct !== h.chu_tri)
.sort(() => 0.5 - Math.random()).slice(0, 3);
if (wrongOptions.length >= 3) {
const options = [h.chu_tri, ...wrongOptions].sort(() => 0.5 - Math.random());
generated.push({
category: 'huyetvi',
cau_hoi: `Huyệt "${h.ten}" có chủ trị đặc hiệu nào sau đây?`,
lua_chon: options,
dap_an: options.indexOf(h.chu_tri),
giai_thich: `Vị trí: ${h.vi_tri || h.dinh_vi || 'Đang cập nhật'}. Chủ trị: ${h.chu_tri}`
});
}
}
});
}
if ((category === 'tra' || category === 'all') && typeof traData !== 'undefined' && Array.isArray(traData)) {
traData.forEach(t => {
if (t && t.ten && t.cong_dung) {
const wrongOptions = traData
.map(item => item.cong_dung).filter(cd => cd !== t.cong_dung)
.sort(() => 0.5 - Math.random()).slice(0, 3);
if (wrongOptions.length >= 3) {
const options = [t.cong_dung, ...wrongOptions].sort(() => 0.5 - Math.random());
generated.push({
category: 'tra',
cau_hoi: `Công dụng chính của "${t.ten}" là gì?`,
lua_chon: options,
dap_an: options.indexOf(t.cong_dung),
giai_thich: `Thành phần: ${t.thanh_phan ? t.thanh_phan.join(', ') : 'Đang cập nhật'}.`
});
}
}
});
}
return generated;
}
function batDauTracNghiem() {
const categorySelect = document.getElementById('quiz-category');
const countSelect = document.getElementById('quiz-count');
const category = categorySelect ? categorySelect.value : 'all';
const count = countSelect ? parseInt(countSelect.value) : 10;
let rawData = [];
if (typeof questionsData !== 'undefined' && Array.isArray(questionsData)) {
rawData = [...questionsData];
} else if (Array.isArray(window.questionsData)) {
rawData = [...window.questionsData];
}
rawData = rawData.concat(taoCauHoiTamTuDuLieu(category));
let pool = [];
if (category === 'all') {
pool = rawData;
} else {
pool = rawData.filter(q => q && q.category === category);
}
if (pool.length === 0) {
alert('Không tìm thấy câu hỏi, hệ thống chuyển sang chế độ Tổng hợp tất cả.');
pool = rawData;
}
currentQuizQuestions = [...pool]
.sort(() => 0.5 - Math.random())
.slice(0, count)
.map(q => {
const specialKeywords = ['cả a và b', 'tất cả', 'không có', 'cả hai', 'đều đúng', 'đều sai'];
const hasSpecialOpt = q.lua_chon && q.lua_chon.some(text =>
specialKeywords.some(keyword => text.toLowerCase().includes(keyword))
);
if (hasSpecialOpt) {
return { ...q };
} else {
const shuffled = q.lua_chon
.map((text, idx) => ({ text, isCorrect: idx === q.dap_an }))
.sort(() => 0.5 - Math.random());
return {
...q,
lua_chon: shuffled.map(item => item.text),
dap_an: shuffled.findIndex(item => item.isCorrect)
};
}
});
currentQuizIndex = 0;
quizScore = 0;
userAnswers = [];
document.getElementById('quiz-setup').classList.add('hidden');
document.getElementById('quiz-result').classList.add('hidden');
document.getElementById('quiz-review').classList.add('hidden');
document.getElementById('quiz-play').classList.remove('hidden');
hienThiCauHoiTracNghiem();
}
function hienThiCauHoiTracNghiem() {
if (currentQuizIndex >= currentQuizQuestions.length) {
hienThiKetQuaTracNghiem();
return;
}
const oldFeedback = document.getElementById('quiz-feedback-box');
if (oldFeedback) oldFeedback.remove();
const q = currentQuizQuestions[currentQuizIndex];
document.getElementById('quiz-progress').innerText = `Câu ${currentQuizIndex + 1}/${currentQuizQuestions.length}`;
document.getElementById('quiz-score-live').innerText = `Điểm: ${quizScore}`;
document.getElementById('quiz-question').innerText = q.cau_hoi;
const optionsContainer = document.getElementById('quiz-options');
optionsContainer.innerHTML = "";
q.lua_chon.forEach((opt, idx) => {
const btn = document.createElement('button');
btn.className = "w-full p-3 text-left bg-stone-900 hover:bg-stone-800 border border-stone-800 rounded text-xs text-stone-200 transition-all flex items-center gap-2";
btn.innerHTML = `${String.fromCharCode(65 + idx)} ${escapeHTML(opt)} `;
btn.onclick = () => chonDapAnTracNghiem(idx);
optionsContainer.appendChild(btn);
});
}
function chonDapAnTracNghiem(selectedIndex) {
const q = currentQuizQuestions[currentQuizIndex];
const isCorrect = selectedIndex === q.dap_an;
if (isCorrect) quizScore++;
userAnswers.push({
cau_hoi: q.cau_hoi,
lua_chon: q.lua_chon,
dap_an_dung: q.dap_an,
lua_chon_cua_ban: selectedIndex,
giai_thich: q.giai_thich,
dung: isCorrect
});
const optionsContainer = document.getElementById('quiz-options');
const buttons = optionsContainer.children;
for (let i = 0; i < buttons.length; i++) {
buttons[i].disabled = true;
buttons[i].classList.add('pointer-events-none', 'opacity-75');
if (i === q.dap_an) {
buttons[i].classList.remove('bg-stone-900', 'border-stone-800');
buttons[i].classList.add('bg-emerald-950/80', 'border-emerald-600', 'text-emerald-300', 'font-bold');
} else if (i === selectedIndex && !isCorrect) {
buttons[i].classList.remove('bg-stone-900', 'border-stone-800');
buttons[i].classList.add('bg-red-950/80', 'border-red-600', 'text-red-300', 'font-bold');
}
}
let feedbackDiv = document.getElementById('quiz-feedback-box');
if (!feedbackDiv) {
feedbackDiv = document.createElement('div');
feedbackDiv.id = 'quiz-feedback-box';
optionsContainer.parentNode.appendChild(feedbackDiv);
}
if (isCorrect) {
feedbackDiv.className = "mt-4 p-3.5 rounded-lg border border-emerald-800 bg-emerald-950/40 text-xs space-y-2 animate-fade-in";
feedbackDiv.innerHTML = `
Chính xác!
💡 ${escapeHTML(q.giai_thich || 'Đã ghi nhận đáp án đúng.')}
Câu tiếp theo
`;
} else {
feedbackDiv.className = "mt-4 p-3.5 rounded-lg border border-red-800 bg-red-950/40 text-xs space-y-2 animate-fade-in";
feedbackDiv.innerHTML = `
Chưa chính xác!
💡 ${escapeHTML(q.giai_thich || 'Xem lại kiến thức chuyên đề này.')}
Câu tiếp theo
`;
}
}
function chuyenCauTiepTheo() {
currentQuizIndex++;
hienThiCauHoiTracNghiem();
}
function hienThiKetQuaTracNghiem() {
document.getElementById('quiz-play').classList.add('hidden');
document.getElementById('quiz-result').classList.remove('hidden');
const total = currentQuizQuestions.length;
document.getElementById('quiz-score-final').innerText = `${quizScore}/${total}`;
let evaluationText = "";
const percent = (quizScore / total) * 100;
if (percent === 100) {
evaluationText = "🏆 Xuất sắc! Bạn nắm rất vững kiến thức Y học cổ truyền chuẩn xác.";
} else if (percent >= 80) {
evaluationText = "👍 Rất tốt! Kiến thức lâm sàng và dược lý của bạn ở mức cao.";
} else if (percent >= 50) {
evaluationText = "📚 Khá ổn, tuy nhiên bạn cần ôn tập thêm về các pháp trị và dược liệu.";
} else {
evaluationText = "💡 Cần cố gắng ôn luyện thêm các chuyên đề Biện chứng luận trị và Dược liệu học.";
}
document.getElementById('quiz-evaluation').innerText = evaluationText;
const history = JSON.parse(localStorage.getItem('quizHistory') || '[]');
history.unshift({
date: new Date().toLocaleDateString('vi-VN'),
score: `${quizScore}/${total}`,
percent: percent.toFixed(0) + '%'
});
localStorage.setItem('quizHistory', JSON.stringify(history.slice(0, 5)));
capNhatDiemGanNhat();
}
function kichHoatTimAnh(keyword = "") {
const kw = keyword.trim() || "Chưa chọn vị thuốc";
const kwEl = document.getElementById('current-image-keyword');
if (kwEl) kwEl.innerText = kw;
const container = document.getElementById('image-links-container');
if (container) {
if (kw === "Chưa chọn vị thuốc") {
container.innerHTML = `Vui lòng chọn dược liệu hoặc huyệt vị từ các tab trước để hiển thị liên kết tra cứu.
`;
} else {
const kwClean = kw.replace(/\(.*?\)/g, '').replace(/[^\w\sàáảãạăắằẳẵặâấầẩẫậèéẻẽẹêếềểễệìíỉĩịòóỏõọôốồổỗộơớờởỡợùúủũụưứừửữựỳýỷỹỵđ]/gi, ' ').trim();
const suffix = kwClean.toLowerCase().includes('huyệt') ? ' huyệt vị YHCT' : ' vị thuốc YHCT';
const q = encodeURIComponent(kwClean + suffix);
const urlGoogle = `https://www.google.com/search?q=${q}&udm=2`;
const urlDDG = `https://duckduckgo.com/?q=${q}&iax=images&ia=images`;
const urlBing = `https://www.bing.com/images/search?q=${q}&form=HDRSC2`;
container.innerHTML = `
Mở trên DuckDuckGo
Mở ảnh trên Google
Mở trên Bing
`;
}
}
switchTab('xemanh');
}
function quayLaiCauHinhQuiz() {
document.getElementById('quiz-result').classList.add('hidden');
document.getElementById('quiz-review').classList.add('hidden');
document.getElementById('quiz-setup').classList.remove('hidden');
}
function hienThiXemLai() {
document.getElementById('quiz-result').classList.add('hidden');
const reviewDiv = document.getElementById('quiz-review');
reviewDiv.classList.remove('hidden');
const listContainer = document.getElementById('quiz-review-list');
listContainer.innerHTML = "";
userAnswers.forEach((ans, index) => {
const item = document.createElement('div');
item.className = `p-3 rounded-lg border text-xs space-y-1.5 ${ans.dung ? 'bg-emerald-950/20 border-emerald-900/50' : 'bg-red-950/20 border-red-900/50'}`;
const yourChoiceText = ans.lua_chon_cua_ban !== undefined ? ans.lua_chon[ans.lua_chon_cua_ban] : "Chưa trả lời";
const correctChoiceText = ans.lua_chon[ans.dap_an_dung];
item.innerHTML = `
Câu ${index + 1}: ${escapeHTML(ans.cau_hoi)}
👉 Lựa chọn của bạn: ${escapeHTML(yourChoiceText)} ${ans.dung ? '✓' : '✗'}
${!ans.dung ? `✔ Đáp án đúng: ${escapeHTML(correctChoiceText)}
` : ''}
💡 Giải thích: ${escapeHTML(ans.giai_thich)}
`;
listContainer.appendChild(item);
});
}
function dongXemLai() {
document.getElementById('quiz-review').classList.add('hidden');
document.getElementById('quiz-result').classList.remove('hidden');
}
function quayLaiTabTruoc() {
const lastTab = localStorage.getItem('activeTab') || 'duoclieu';
switchTab(lastTab);
}
function xemDuocLieu(tenViThuoc) {
if (!tenViThuoc) return;
switchTab('duoclieu');
const searchInput = document.getElementById('searchDuocLieu');
const filterNhom = document.getElementById('filterNhomDuocLieu');
if (searchInput) searchInput.value = tenViThuoc;
if (filterNhom) filterNhom.value = "";
filterDuocLieu();
window.scrollTo({ top: 0, behavior: 'smooth' });
}
window.onload = function() {
setTimeout(() => {
if (typeof duocLieuData !== 'undefined' && duocLieuData && Array.isArray(duocLieuData)) {
const nhomDL = [...new Set(duocLieuData.map(d => d.nhom).filter(Boolean))];
const selectDL = document.getElementById('filterNhomDuocLieu');
if (selectDL) {
selectDL.innerHTML = '-- Tất cả nhóm dược -- ';
nhomDL.forEach(n => { let opt = document.createElement('option'); opt.value = n; opt.innerText = n; selectDL.appendChild(opt); });
}
}
if (typeof huyetViData !== 'undefined' && huyetViData && Array.isArray(huyetViData)) {
const heKinhLac = [...new Set(huyetViData.map(h => h.kinh).filter(Boolean))];
const selectHL = document.getElementById('filterKinhLac');
if (selectHL) {
selectHL.innerHTML = '-- Tất cả hệ thống kinh mạch -- ';
heKinhLac.forEach(k => { let opt = document.createElement('option'); opt.value = k; opt.innerText = k; selectHL.appendChild(opt); });
}
}
if (typeof traData !== 'undefined' && traData && Array.isArray(traData)) {
const nhomTra = [...new Set(traData.map(t => t.nhom).filter(Boolean))];
const selectTra = document.getElementById('filterNhomTra');
if (selectTra) {
selectTra.innerHTML = '-- Tất cả nhóm công dụng -- ';
nhomTra.forEach(n => { let opt = document.createElement('option'); opt.value = n; opt.innerText = n; selectTra.appendChild(opt); });
}
}
updateLuanTri();
}, 20);
};
function startQuizMode() {
if (typeof database === 'undefined' || !database) return;
quizActive = true;
const boLoc = document.getElementById('bo-loc-tam-truc');
if (boLoc) boLoc.classList.add('blur-sm', 'pointer-events-none', 'opacity-40');
const input = document.getElementById('search-input');
if (input) {
input.classList.add('blur-sm', 'pointer-events-none', 'opacity-40');
input.disabled = true;
}
const controls = document.getElementById('quiz-controls');
if (controls) {
controls.innerHTML = `
Ca Tiếp Theo
Thoát
`;
}
loadRandomCase();
}
function loadRandomCase() {
if (!quizActive || typeof database === 'undefined' || !database) return;
const keys = Object.keys(database);
if (keys.length === 0) return;
const randomKey = keys[Math.floor(Math.random() * keys.length)];
const data = database[randomKey];
if (data) {
const parts = randomKey.split('_');
if (parts.length === 3) {
const tp = document.getElementById('tang-phu');
const hn = document.getElementById('han-nhiet');
const ht = document.getElementById('hu-thuc');
if (tp) tp.value = parts[0];
if (hn) hn.value = parts[1];
if (ht) ht.value = parts[2];
}
const hcEl = document.getElementById('hoi-chung');
const pdtEl = document.getElementById('phap-dieu-tri');
const btEl = document.getElementById('bai-thuoc');
if (hcEl) hcEl.innerText = data.hc || "Chưa rõ hội chứng";
if (pdtEl) pdtEl.innerText = data.pdt || "Chưa rõ pháp trị";
if (btEl) btEl.innerText = data.bt || "Chưa rõ cổ phương";
const ul = document.getElementById('trieu-chung');
if (ul) {
ul.innerHTML = "";
if (data.tc && Array.isArray(data.tc)) {
const frag = document.createDocumentFragment();
data.tc.forEach(t => { let li = document.createElement('li'); li.innerText = t; frag.appendChild(li); });
ul.appendChild(frag);
}
}
const divBt = document.getElementById('chi-tiet-bai-thuoc');
if (divBt) {
divBt.innerHTML = "";
if (data.tpbt && Array.isArray(data.tpbt)) {
const frag = document.createDocumentFragment();
data.tpbt.forEach(v => {
let b = document.createElement('button');
b.className = "px-2.5 py-1 text-xs bg-stone-800 text-amber-400 border border-stone-700 rounded transition-all font-medium";
b.innerHTML = ` ${v}`;
b.onclick = () => xemDuocLieu(v); frag.appendChild(b);
});
divBt.appendChild(frag);
}
}
const boLoc = document.getElementById('bo-loc-tam-truc');
if (boLoc) boLoc.classList.add('blur-sm', 'pointer-events-none', 'opacity-40');
if (hcEl) hcEl.className = "text-xl font-bold text-primary transition-all blur-sm select-none cursor-pointer";
if (pdtEl) pdtEl.className = "text-base font-semibold text-emerald-500 transition-all blur-sm select-none cursor-pointer";
if (btEl) btEl.className = "text-lg font-bold text-amber-500 transition-all blur-sm select-none cursor-pointer";
const chiTietBT = document.getElementById('chi-tiet-bai-thuoc');
if (chiTietBT) chiTietBT.className = "flex flex-wrap gap-2 transition-all blur-sm select-none";
const revealAction = () => {
if (hcEl) hcEl.classList.remove('blur-sm', 'select-none');
if (pdtEl) pdtEl.classList.remove('blur-sm', 'select-none');
if (btEl) btEl.classList.remove('blur-sm', 'select-none');
if (chiTietBT) chiTietBT.classList.remove('blur-sm', 'select-none');
if (boLoc) boLoc.classList.remove('blur-sm', 'opacity-40');
};
if (hcEl) hcEl.onclick = revealAction;
if (pdtEl) pdtEl.onclick = revealAction;
if (btEl) btEl.onclick = revealAction;
}
}
function stopQuizMode() {
quizActive = false;
const boLoc = document.getElementById('bo-loc-tam-truc');
if (boLoc) boLoc.classList.remove('blur-sm', 'pointer-events-none', 'opacity-40');
const input = document.getElementById('search-input');
if (input) {
input.classList.remove('blur-sm', 'pointer-events-none', 'opacity-40');
input.disabled = false;
}
const controls = document.getElementById('quiz-controls');
if (controls) {
controls.innerHTML = `
Chế độ Ôn Tập
`;
}
const hcEl = document.getElementById('hoi-chung');
const pdtEl = document.getElementById('phap-dieu-tri');
const btEl = document.getElementById('bai-thuoc');
const chiTietBT = document.getElementById('chi-tiet-bai-thuoc');
if (hcEl) { hcEl.className = "text-xl font-bold text-primary transition-all"; hcEl.onclick = null; }
if (pdtEl) { pdtEl.className = "text-base font-semibold text-emerald-500 transition-all"; pdtEl.onclick = null; }
if (btEl) { btEl.className = "text-lg font-bold text-amber-500 transition-all"; btEl.onclick = null; }
if (chiTietBT) chiTietBT.className = "flex flex-wrap gap-2 transition-all";
updateLuanTri();
}
function highlightText(text, query) {
if (!query || !text) return escapeHTML(text);
const safeText = String(text);
const safeQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(`(${safeQuery})`, 'gi');
const parts = safeText.split(regex);
return parts.map((part, i) => {
if (i % 2 === 1) {
return `${escapeHTML(part)} `;
}
return escapeHTML(part);
}).join('');
}
function removeAccents(str) {
return (str || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
}
function getItemTamTruc(item) {
if (!item) return { tp: '', hn: '', ht: '', bc: '' };
const pl = Array.isArray(item.phanloai) ? item.phanloai : [];
const title = removeAccents(item.hc || '');
const pTP = removeAccents(pl[0] || title);
const pHN = removeAccents(pl[1] || item.han_nhiet || title);
const pHT = removeAccents(pl[2] || item.hu_thuc || title);
const pBC = removeAccents(pl[3] || title);
let tp = '';
if (pTP.includes('tam tieu')) tp = 'Tam_Tieu';
else if (pTP.includes('dai truong')) tp = 'Dai_Truong';
else if (pTP.includes('tieu truong')) tp = 'Tieu_Truong';
else if (pTP.includes('bang quang')) tp = 'Bang_Quang';
else if (pTP.includes('can')) tp = 'Can';
else if (pTP.includes('tam')) tp = 'Tam';
else if (pTP.includes('ty')) tp = 'Ty';
else if (pTP.includes('phe')) tp = 'Phe';
else if (pTP.includes('than')) tp = 'Than';
else if (pTP.includes('dom')) tp = 'Dom';
else if (pTP.includes('vi')) tp = 'Vi';
let hn = 'Binh';
if (pHN.includes('hiep tap')) hn = 'Han nhiet hiep tap';
else if (pHN.includes('nhiet') || pHN.includes('hoa')) hn = 'Nhiet';
else if (pHN.includes('han') || pHN.includes('lanh')) hn = 'Han';
else if (pHN.includes('binh')) hn = 'Binh';
let ht = 'Thuc';
if (pHT.includes('hiep tap')) ht = 'Hu thuc hiep tap';
else if (pHT.includes('hu') || pHT.includes('suy')) ht = 'Hu';
else if (pHT.includes('thuc')) ht = 'Thuc';
let bc = '';
if (pBC.includes('thap nhiet')) bc = 'thap nhiet';
else if (pBC.includes('khi') || pBC.includes('uat') || pBC.includes('tre')) bc = 'khi';
else if (pBC.includes('huyet') || pBC.includes('u')) bc = 'huyet u';
else if (pBC.includes('dam') || pBC.includes('thap')) bc = 'dam';
else if (pBC.includes('hoa')) bc = 'hoa';
return { tp, hn, ht, bc };
}
function syncSelectsWithItem(item) {
const tp = document.getElementById('tang-phu');
const hn = document.getElementById('han-nhiet');
const ht = document.getElementById('hu-thuc');
const bc = document.getElementById('benh-co');
if (!item) {
if (tp) tp.value = '';
if (hn) hn.value = '';
if (ht) ht.value = '';
if (bc) bc.value = '';
return;
}
const info = getItemTamTruc(item);
if (tp) tp.value = info.tp;
if (hn) hn.value = info.hn;
if (ht) ht.value = info.ht;
if (bc) bc.value = info.bc;
}
function renderDetailLuanTri(data, query = "", isEnter = false) {
const hcEl = document.getElementById('hoi-chung');
const pdtEl = document.getElementById('phap-dieu-tri');
const btEl = document.getElementById('bai-thuoc');
const ul = document.getElementById('trieu-chung');
const divBt = document.getElementById('chi-tiet-bai-thuoc');
if (data) {
if (hcEl) hcEl.innerHTML = highlightText(data.hc || "Chưa rõ hội chứng", query);
if (pdtEl) pdtEl.innerText = data.pdt || "Chưa có pháp trị";
if (btEl) btEl.innerText = data.bt || "Đối chứng nghiệm phương";
if (ul) {
ul.innerHTML = "";
if (data.tc && Array.isArray(data.tc)) {
const frag = document.createDocumentFragment();
data.tc.forEach(t => {
let li = document.createElement('li');
li.innerHTML = highlightText(t, query);
frag.appendChild(li);
});
ul.appendChild(frag);
}
}
if (divBt) {
divBt.innerHTML = "";
if (data.tpbt && Array.isArray(data.tpbt)) {
const frag = document.createDocumentFragment();
data.tpbt.forEach(v => {
let btn = document.createElement('button');
btn.className = "px-2.5 py-1 text-xs bg-stone-800 text-amber-400 border border-stone-700 rounded transition-all font-medium";
btn.innerHTML = ` ${highlightText(v, query)}`;
btn.onclick = () => xemDuocLieu(v);
frag.appendChild(btn);
});
divBt.appendChild(frag);
}
}
} else {
if (query && isEnter) xuLyKhongTimThay('Luận Trị', query);
const msg = (query && isEnter)
? ` Đã tự động ghi nhận từ khóa "${escapeHTML(query)}" để bổ sung dữ liệu.`
: (query ? ` Nhấn Enter để gửi từ khóa "${escapeHTML(query)}".` : '');
if (hcEl) hcEl.innerHTML = " Không tìm thấy hội chứng phù hợp ";
if (pdtEl) pdtEl.innerText = "---";
if (btEl) btEl.innerText = "---";
if (ul) ul.innerHTML = `${msg} `;
if (divBt) divBt.innerHTML = "";
syncSelectsWithItem(null);
}
}
function updateLuanTri(query = "") {
if (quizActive || typeof database === 'undefined' || !database) return;
const tp = getVal('tang-phu');
const hn = getVal('han-nhiet');
const ht = getVal('hu-thuc');
const bc = getVal('benh-co');
const searchInput = getVal('search-input').toLowerCase().trim();
const activeQuery = query || searchInput;
let bestMatchData = null;
if (!tp && !hn && !ht && !bc && !activeQuery) {
bestMatchData = Object.values(database)[0] || null;
if (bestMatchData) syncSelectsWithItem(bestMatchData);
} else {
for (let key in database) {
const item = database[key];
if (!item) continue;
const info = getItemTamTruc(item);
const matchTP = !tp || info.tp === tp;
const matchHN = !hn || info.hn === hn;
const matchHT = !ht || info.ht === ht;
const matchBC = !bc || info.bc === bc;
let matchSearch = true;
if (activeQuery) {
const matchHC = (item.hc || '').toLowerCase().includes(activeQuery);
const matchTC = (item.tc || []).some(t => (t || '').toLowerCase().includes(activeQuery));
const matchBT = (item.bt || '').toLowerCase().includes(activeQuery);
const matchTPBT = (item.tpbt || []).some(v => (v || '').toLowerCase().includes(activeQuery));
matchSearch = matchHC || matchTC || matchBT || matchTPBT;
}
if (matchTP && matchHN && matchHT && matchBC && matchSearch) {
bestMatchData = item;
break;
}
}
}
renderDetailLuanTri(bestMatchData, activeQuery);
}
function searchLuanTri(isEnter = false) {
if (quizActive) stopQuizMode();
const input = document.getElementById('search-input');
const dropdown = document.getElementById('search-dropdown');
const query = (input ? input.value : '').toLowerCase().trim();
if (!query) {
if (dropdown) dropdown.classList.add('hidden');
updateLuanTri();
return;
}
const matches = [];
for (let key in database) {
const item = database[key];
if (!item) continue;
const matchHoiChung = (item.hc || '').toLowerCase().includes(query);
const matchTrieuChung = (item.tc || []).some(t => (t || '').toLowerCase().includes(query));
const matchBaiThuoc = (item.bt || '').toLowerCase().includes(query);
const matchViThuoc = (item.tpbt || []).some(v => (v || '').toLowerCase().includes(query));
if (matchHoiChung || matchTrieuChung || matchBaiThuoc || matchViThuoc) {
matches.push({ key, ...item });
if (matches.length >= 30) break;
}
}
if (dropdown) {
if (matches.length > 0) {
dropdown.innerHTML = matches.map(m => `
${highlightText(m.hc, query)}
${escapeHTML(m.tc ? m.tc.join(', ') : '')}
`).join('');
dropdown.classList.remove('hidden');
} else {
dropdown.innerHTML = `Không tìm thấy hội chứng phù hợp
`;
dropdown.classList.remove('hidden');
}
}
if (matches.length > 0) {
selectSearchResult(matches[0].key, false);
} else {
renderDetailLuanTri(null, query, isEnter);
}
}
function selectSearchResult(key, hideDropdown = true) {
const query = getVal('search-input').trim();
if (typeof database === 'undefined' || !database || !database[key]) return;
const item = database[key];
renderDetailLuanTri(item, query);
syncSelectsWithItem(item);
if (hideDropdown) {
const dropdown = document.getElementById('search-dropdown');
if (dropdown) dropdown.classList.add('hidden');
}
}
function xuLyKhongTimThay(tabName, query) {
if (!query || query.trim().length < 2) return;
const cleanQuery = query.trim();
const timeStr = new Date().toLocaleString('vi-VN');
const params = new URLSearchParams();
params.append('form-name', 'gop-y-yhct');
params.append('hoten', 'Hệ thống tự động');
params.append('email', 'system@luantriyhct.com');
params.append('chude', `Từ khóa thiếu: ${tabName}`);
params.append('noidung', `Từ khóa chưa có dữ liệu: "${cleanQuery}" (Thời gian: ${timeStr})`);
fetch('/', {
method: 'POST',
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: params.toString()
}).catch(() => {});
hienThongBaoGhiNhan(tabName, cleanQuery);
}
function hienThongBaoGhiNhan(tabName, query) {
let toast = document.getElementById('feedback-toast');
if (!toast) {
toast = document.createElement('div');
toast.id = 'feedback-toast';
toast.className = 'fixed top-4 right-4 z-50 bg-amber-950/95 border border-amber-600/80 text-amber-200 text-xs px-4 py-3 rounded-lg shadow-2xl flex items-center gap-2.5 transition-all duration-300 transform -translate-y-2 opacity-0';
document.body.appendChild(toast);
}
toast.innerHTML = ` Đã gửi phản hồi từ khóa "${escapeHTML(query)}" (${tabName}) về hệ thống! `;
requestAnimationFrame(() => {
toast.classList.remove('-translate-y-2', 'opacity-0');
toast.classList.add('translate-y-0', 'opacity-100');
});
setTimeout(() => {
toast.classList.remove('translate-y-0', 'opacity-100');
toast.classList.add('-translate-y-2', 'opacity-0');
}, 3500);
}
function filterDuocLieu(isEnter = false) {
if (typeof duocLieuData === 'undefined' || !duocLieuData) return;
const txtRaw = getVal('searchDuocLieu').trim().normalize('NFC');
const txt = txtRaw.toLowerCase();
const group = getVal('filterNhomDuocLieu');
const grid = document.getElementById('gridDuocLieu');
if (!grid) return;
grid.innerHTML = "";
const filteredData = duocLieuData.filter(d => {
if (!d) return false;
const nhom = d.nhom || '';
if (group !== "" && nhom !== group) return false;
if (!txt) return true;
const ten = (d.ten || '').toLowerCase().normalize('NFC');
const tenKhac = (d.ten_khac || '').toLowerCase().normalize('NFC');
const congDung = (d.cong_dung || '').toLowerCase().normalize('NFC');
return ten.includes(txt) || tenKhac.includes(txt) || congDung.includes(txt);
});
if (filteredData.length === 0) {
if (txtRaw && isEnter) xuLyKhongTimThay('Dược Liệu', txtRaw);
const feedbackMsg = (txtRaw && isEnter)
? ` Đã tự động gửi phản hồi từ khóa "${escapeHTML(txtRaw)}" về hệ thống.
`
: (txtRaw ? ` Nhấn Enter để gửi từ khóa "${escapeHTML(txtRaw)}" về hệ thống.
` : '');
grid.innerHTML = `
Không tìm thấy dược liệu phù hợp
${feedbackMsg}
`;
return;
}
const frag = document.createDocumentFragment();
filteredData.slice(0, 50).forEach(d => {
let card = document.createElement('div');
card.className = "bg-dark-box p-4 rounded-lg space-y-3 relative cursor-pointer border-l-4 border-emerald-600/70 hover:scale-[1.01] transition-all duration-150 shadow-md shadow-black/40";
const kiengKy = d.kieng_ky || d.dac_tinh || d.chong_chi_dinh || d.luu_y || "";
const boxHTML = kiengKy ? `
KIÊNG KỴ LÂM SÀNG:
${highlightText(kiengKy, txtRaw)}
` : `
LƯU Ý LÂM SÀNG:
Tuân thủ liều lượng phối ngũ tiêu chuẩn theo y lệnh.
`;
const blurDL = typeof isQuizDL !== 'undefined' && isQuizDL ? 'blur-md transition-all duration-300' : '';
card.innerHTML = `
${escapeHTML(d.nhom || 'Dược liệu')}
${highlightText(d.ten || '', txtRaw)}
CÔNG NĂNG CHỦ TRỊ:
${highlightText(d.cong_dung || '', txtRaw)}
${boxHTML}
`;
card.onclick = (e) => {
const isTitle = e.target.closest('.card-title-el');
const activeQuiz = typeof isQuizDL !== 'undefined' && isQuizDL;
if (activeQuiz) {
const blurEl = card.querySelector('.blur-target');
if (blurEl) blurEl.classList.remove('blur-md');
if (isTitle) {
kichHoatTimAnh(d.ten || '');
}
} else {
if (isTitle) {
kichHoatTimAnh(d.ten || '');
}
}
};
frag.appendChild(card);
});
grid.appendChild(frag);
}
function filterHuyetVi(isEnter = false) {
if (typeof huyetViData === 'undefined' || !huyetViData) return;
const txt = getVal('searchHuyetVi').toLowerCase().trim();
const kinh = getVal('filterKinhLac');
const grid = document.getElementById('gridHuyetVi');
if (!grid) return;
grid.innerHTML = "";
const getKinhTheme = (kinhName) => {
const k = (kinhName || '').toLowerCase();
if (k.includes('phế') || k.includes('đại trường')) return { border: 'border-l-4 border-sky-500', bgBox: 'bg-sky-950/40 border-sky-900/60', text: 'text-sky-400', textLight: 'text-sky-200/90', tag: 'bg-sky-950 text-sky-400' };
if (k.includes('can') || k.includes('đởm')) return { border: 'border-l-4 border-emerald-500', bgBox: 'bg-emerald-950/40 border-emerald-900/60', text: 'text-emerald-400', textLight: 'text-emerald-200/90', tag: 'bg-emerald-950 text-emerald-400' };
if (k.includes('tỳ') || k.includes('vị')) return { border: 'border-l-4 border-amber-500', bgBox: 'bg-amber-950/40 border-amber-900/60', text: 'text-amber-400', textLight: 'text-amber-200/90', tag: 'bg-amber-950 text-amber-400' };
if (k.includes('tâm') || k.includes('tiểu trường') || k.includes('tiêu') || k.includes('bào')) return { border: 'border-l-4 border-rose-500', bgBox: 'bg-rose-950/40 border-rose-900/60', text: 'text-rose-400', textLight: 'text-rose-200/90', tag: 'bg-rose-950 text-rose-400' };
if (k.includes('thận') || k.includes('bàng quang')) return { border: 'border-l-4 border-indigo-500', bgBox: 'bg-indigo-950/40 border-indigo-900/60', text: 'text-indigo-400', textLight: 'text-indigo-200/90', tag: 'bg-indigo-950 text-indigo-400' };
return { border: 'border-l-4 border-teal-500', bgBox: 'bg-teal-950/40 border-teal-900/60', text: 'text-teal-400', textLight: 'text-teal-200/90', tag: 'bg-teal-950 text-teal-400' };
};
const filteredData = huyetViData.filter(h => {
if (!h) return false;
const matchTxt = (h.ten || '').toLowerCase().includes(txt) ||
(h.chu_tri || '').toLowerCase().includes(txt) ||
(h.vi_tri || '').toLowerCase().includes(txt) ||
(h.dinh_vi || '').toLowerCase().includes(txt);
const matchKinh = (kinh === "" || h.kinh === kinh);
return matchTxt && matchKinh;
});
if (filteredData.length === 0) {
if (txt && isEnter) xuLyKhongTimThay('Huyệt Vị', txt);
const feedbackMsg = (txt && isEnter)
? ` Đã tự động gửi phản hồi từ khóa "${escapeHTML(txt)}" về hệ thống.
`
: (txt ? ` Nhấn Enter để gửi từ khóa "${escapeHTML(txt)}" về hệ thống.
` : '');
grid.innerHTML = `
Không tìm thấy huyệt vị phù hợp
${feedbackMsg}
`;
return;
}
const frag = document.createDocumentFragment();
filteredData.slice(0, 50).forEach(h => {
const theme = getKinhTheme(h.kinh);
let card = document.createElement('div');
card.className = `bg-dark-box p-4 rounded-lg space-y-3 relative cursor-pointer ${theme.border} shadow-md shadow-black/40`;
const blurHV = typeof isQuizHV !== 'undefined' && isQuizHV ? 'blur-md transition-all duration-300' : '';
card.innerHTML = `
${escapeHTML(h.kinh || '')}
Huyệt ${highlightText(h.ten || '', txt)}
ĐỊNH VỊ GIẢI PHẪU:
${highlightText(h.vi_tri || h.dinh_vi || 'Đang cập nhật', txt)}
CHỦ TRỊ ĐẶC HIỆU:
${highlightText(h.chu_tri || '', txt)}
`;
card.onclick = (e) => {
const isTitle = e.target.closest('.card-title-el');
const activeQuiz = typeof isQuizHV !== 'undefined' && isQuizHV;
if (activeQuiz) {
const blurEl = card.querySelector('.blur-target');
if (blurEl) blurEl.classList.remove('blur-md');
if (isTitle) {
kichHoatTimAnh('Huyệt ' + (h.ten || ''));
}
} else {
if (isTitle) {
kichHoatTimAnh('Huyệt ' + (h.ten || ''));
}
}
};
frag.appendChild(card);
});
grid.appendChild(frag);
}
function filterTra(isEnter = false) {
if (typeof traData === 'undefined' || !traData) return;
let txt = getVal('searchTra').toLowerCase().trim();
const nhom = getVal('filterNhomTra');
const grid = document.getElementById('gridTra');
if (!grid) return;
grid.innerHTML = "";
let altTxt = txt;
if (txt.includes('dành dành')) altTxt = 'chi tử';
else if (txt.includes('chi tử')) altTxt = 'dành dành';
const filteredData = traData.filter(t => {
if (!t) return false;
const matchTxt = (t.ten || '').toLowerCase().includes(txt) ||
(t.ten || '').toLowerCase().includes(altTxt) ||
(t.cong_dung || '').toLowerCase().includes(txt) ||
(t.cong_dung || '').toLowerCase().includes(altTxt) ||
(t.thanh_phan || []).some(tp => {
const tpLow = (tp || '').toLowerCase();
return tpLow.includes(txt) || tpLow.includes(altTxt);
});
const matchNhom = (nhom === "" || t.nhom === nhom);
return matchTxt && matchNhom;
});
if (filteredData.length === 0) {
if (txt && isEnter) xuLyKhongTimThay('Trà Dược', txt);
const feedbackMsg = (txt && isEnter)
? ` Đã tự động gửi phản hồi từ khóa "${escapeHTML(txt)}" về hệ thống.
`
: (txt ? ` Nhấn Enter để gửi từ khóa "${escapeHTML(txt)}" về hệ thống.
` : '');
grid.innerHTML = `
Không tìm thấy công thức trà phù hợp
${feedbackMsg}
`;
return;
}
const frag = document.createDocumentFragment();
filteredData.slice(0, 50).forEach(t => {
let card = document.createElement('div');
card.className = "bg-dark-box p-4 rounded-lg space-y-3 relative border-l-4 border-amber-600/70 shadow-md shadow-black/40 hover:scale-[1.01] transition-all duration-150";
const tagsHtml = (t.thanh_phan || []).map(tp =>
`
${highlightText(tp, txt)}
`
).join(' ');
card.innerHTML = `
${escapeHTML(t.nhom || '')}
☕ ${highlightText(t.ten || '', txt)}
CÔNG DỤNG CHÍNH:
${highlightText(t.cong_dung || '', txt)}
CÁCH PHA & DÙNG:
${highlightText(t.cach_dung || '', txt)}
THÀNH PHẦN DƯỢC LIỆU:
${tagsHtml}
`;
frag.appendChild(card);
});
grid.appendChild(frag);
}
// 1. Hàm nạp file JS động & Khởi tạo bộ lọc tự động
const loadedScripts = new Set();
function loadScript(src) {
if (loadedScripts.has(src)) return Promise.resolve();
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.onload = () => {
loadedScripts.add(src);
// Cập nhật lại số đếm Header khi nạp file thành công
capNhatTongSoTracNghiem();
// Khởi tạo bộ lọc Dược liệu khi nạp duoclieudata.js
if (src === 'duoclieudata.js' && typeof duocLieuData !== 'undefined') {
document.getElementById('total-thuoc')?.innerText = duocLieuData.length;
const selectDL = document.getElementById('filterNhomDuocLieu');
if (selectDL && selectDL.options.length <= 1) {
const nhomDL = [...new Set(duocLieuData.map(d => d.nhom).filter(Boolean))];
selectDL.innerHTML = '-- Tất cả nhóm dược -- ';
nhomDL.forEach(n => { let opt = document.createElement('option'); opt.value = n; opt.innerText = n; selectDL.appendChild(opt); });
}
}
// Khởi tạo bộ lọc Huyệt vị khi nạp huyetvidata.js
if (src === 'huyetvidata.js' && typeof huyetViData !== 'undefined') {
document.getElementById('total-huyet')?.innerText = huyetViData.length;
const selectHL = document.getElementById('filterKinhLac');
if (selectHL && selectHL.options.length <= 1) {
const heKinhLac = [...new Set(huyetViData.map(h => h.kinh).filter(Boolean))];
selectHL.innerHTML = '-- Tất cả hệ thống kinh mạch -- ';
heKinhLac.forEach(k => { let opt = document.createElement('option'); opt.value = k; opt.innerText = k; selectHL.appendChild(opt); });
}
}
// Khởi tạo bộ lọc Trà dược khi nạp tradata.js
if (src === 'tradata.js' && typeof traData !== 'undefined') {
document.getElementById('total-tra')?.innerText = traData.length;
const selectTra = document.getElementById('filterNhomTra');
if (selectTra && selectTra.options.length <= 1) {
const nhomTra = [...new Set(traData.map(t => t.nhom).filter(Boolean))];
selectTra.innerHTML = '-- Tất cả nhóm công dụng -- ';
nhomTra.forEach(n => { let opt = document.createElement('option'); opt.value = n; opt.innerText = n; selectTra.appendChild(opt); });
}
}
resolve();
};
script.onerror = reject;
document.head.appendChild(script);
});
}
// 3. Chỉ nạp thư viện PDF khi người dùng bấm nút Xuất PDF
async function exportPDF() {
await loadScript('html2pdf.bundle.min.js');
const element = document.getElementById('pdf-area');
if (typeof html2pdf === 'undefined') {
alert('Đang tải bộ công cụ xuất PDF, vui lòng thử lại sau vài giây.');
return;
}
const btn = document.querySelector('button[onclick="exportPDF()"]');
const oldHtml = btn.innerHTML;
btn.innerHTML = ' Đang xuất PDF...';
btn.classList.add('opacity-75', 'pointer-events-none');
html2pdf().from(element).save('Phac-Do-YHCT.pdf').then(() => {
btn.innerHTML = oldHtml;
btn.classList.remove('opacity-75', 'pointer-events-none');
});
}
function formatAIMessage(text) {
if (!text) return '';
let safe = escapeHTML(text);
safe = safe.replace(/\*\*(.*?)\*\*/g, '$1 ');
const lines = safe.split('\n');
const formattedLines = lines.map(line => {
const trimmed = line.trim();
if (trimmed.startsWith('* ') || trimmed.startsWith('- ')) {
return `${trimmed.substring(2)} `;
}
if (/^\d+\.\s/.test(trimmed)) {
return `${trimmed}
`;
}
if (trimmed.includes('⚠️') || trimmed.toLowerCase().includes('lưu ý') || trimmed.toLowerCase().includes('cấp cứu')) {
return `${trimmed}
`;
}
if (trimmed === '') return '
';
return `${trimmed}
`;
});
return formattedLines.join('');
}
async function sendAIWebMessage() {
const inputEl = document.getElementById('ai-input');
const chatBox = document.getElementById('ai-chat-box');
const btnSend = document.querySelector('button[onclick="sendAIWebMessage()"]');
if (!inputEl || !chatBox) return;
let query = inputEl.value.trim();
if (!query) return;
const safeQuery = escapeHTML(query);
chatBox.innerHTML += `
Bạn: ${safeQuery}
`;
inputEl.value = '';
const loadingId = 'ai-loading-' + Date.now();
chatBox.innerHTML += `
Trợ lý AI YHCT đang suy nghĩ & phân tích phác đồ...
`;
chatBox.scrollTop = chatBox.scrollHeight;
if (btnSend) {
btnSend.disabled = true;
btnSend.classList.add('opacity-50', 'pointer-events-none');
}
try {
const res = await fetch('/.netlify/functions/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt: query })
});
const data = await res.json();
const loadingEl = document.getElementById(loadingId);
if (loadingEl) loadingEl.remove();
if (!res.ok) {
const safeErr = escapeHTML(data.error || 'Lỗi hệ thống');
chatBox.innerHTML += `
${safeErr}
`;
} else {
const formattedReply = formatAIMessage(data.reply || 'Không có phản hồi từ AI.');
chatBox.innerHTML += `
Trợ Lý AI YHCT
${formattedReply}
`;
}
} catch (err) {
const loadingEl = document.getElementById(loadingId);
if (loadingEl) loadingEl.remove();
chatBox.innerHTML += `
Lỗi kết nối máy chủ AI. Vui lòng thử lại.
`;
} finally {
if (btnSend) {
btnSend.disabled = false;
btnSend.classList.remove('opacity-50', 'pointer-events-none');
}
chatBox.scrollTop = chatBox.scrollHeight;
}
}
// Biến lưu trạng thái phiên chẩn đoán lâm sàng thông minh
let currentDiagnosisSession = {
step: 0,
candidateList: [],
targetHoiChung: null
};
// 1. Kích hoạt bộ lọc câu hỏi lâm sàng thông minh trước khi chốt phác đồ
function batDauLuanTriThongMinh(query) {
if (typeof database === 'undefined' || !database) return;
// Tìm các hội chứng có chứa triệu chứng hoặc từ khóa người dùng nhập
const matches = Object.keys(database).map(key => {
const item = database[key];
let score = 0;
const q = query.toLowerCase();
if ((item.hc || '').toLowerCase().includes(q)) score += 3;
if ((item.tc || []).some(t => t.toLowerCase().includes(q))) score += 2;
if ((item.pdt || '').toLowerCase().includes(q)) score += 1;
return { key, ...item, score };
}).filter(item => item.score > 0).sort((a, b) => b.score - a.score);
if (matches.length <= 1) {
// Nếu chỉ tìm thấy 1 hoặc không tìm thấy, hiển thị kết quả trực tiếp
if (matches.length === 1) selectSearchResult(matches[0].key, false);
else renderDetailLuanTri(null, query, true);
return;
}
// Nếu có nhiều hội chứng (dư thừa/chồng lấn), khởi động chuỗi câu hỏi phân loại
currentDiagnosisSession = {
step: 0,
candidateList: matches.slice(0, 4), // Lấy tối đa 4 ứng viên tiềm năng nhất
targetHoiChung: null
};
hienThiCauHoiPhanBieuLâmSàng();
}
// 2. Hiển thị giao diện câu hỏi chẩn đoán phân biệt
function hienThiCauHoiPhanBieuLâmSàng() {
const session = currentDiagnosisSession;
const pdfArea = document.getElementById('pdf-area');
if (!pdfArea) return;
if (session.candidateList.length <= 1 || session.step >= 2) {
// Đã chốt được hội chứng chính xác nhất
const finalChoice = session.candidateList[0] || Object.values(database)[0];
selectSearchResult(finalChoice.key, false);
return;
}
// Lấy 2 hội chứng đầu tiên để đặt câu hỏi phân biệt
const hc1 = session.candidateList[0];
const hc2 = session.candidateList[1];
// Lấy triệu chứng đặc trưng khác biệt để hỏi người dùng
const dacTrung1 = hc1.tc[0] || 'Triệu chứng đặc trưng của thể này';
const dacTrung2 = hc2.tc[0] || 'Triệu chứng đặc trưng của thể kia';
pdfArea.innerHTML = `
TRỢ LÝ LÂM SÀNG: HỎI THÊM ĐỂ CHỐT HỘI CHỨNG CHÍNH XÁC
Hệ thống phát hiện các triệu chứng tương đồng giữa hai hội chứng. Vui lòng xác nhận triệu chứng thực tế của bệnh nhân để loại bỏ hội chứng thừa:
👉 Hướng về: ${escapeHTML(hc1.hc)}
Dấu hiệu nổi trội: "${escapeHTML(dacTrung1)}"
👉 Hướng về: ${escapeHTML(hc2.hc)}
Dấu hiệu nổi trội: "${escapeHTML(dacTrung2)}"
Bỏ qua, hiển thị toàn bộ kết quả
`;
}
// 3. Xử lý câu trả lời để loại bỏ hội chứng thừa
function phanHoiLamSang(choiceIndex) {
const session = currentDiagnosisSession;
if (choiceIndex === 0) {
// Giữ lại ứng viên 1, loại bỏ ứng viên 2
session.candidateList.splice(1, 1);
} else {
// Giữ lại ứng viên 2, loại bỏ ứng viên 1 ra đầu bảng
const removed = session.candidateList.splice(1, 1);
session.candidateList[0] = removed[0];
}
session.step++;
hienThiCauHoiPhanBieuLâmSàng();
}
// 4. Hủy bỏ nếu muốn xem ngay
function huyBoChuanDoan() {
updateLuanTri();
}
let touchstartX = 0, touchstartY = 0, touchendX = 0, touchendY = 0;
function handleSwipe(startX, startY, endX, endY) {
const tabsOrder = ['luantri', 'tracnghiem', 'duoclieu', 'huyetvi', 'tra', 'xemanh', 'ai'];
const pascalMap = { luantri: 'LuanTri', tracnghiem: 'TracNghiem', duoclieu: 'DuocLieu', huyetvi: 'HuyetVi', tra: 'Tra', xemanh: 'XemAnh', ai: 'AI' };
const diffX = endX - startX;
const diffY = endY - startY;
const screenWidth = window.innerWidth;
if (startX > 25 && startX < screenWidth - 25 && Math.abs(diffX) > Math.abs(diffY) && Math.abs(diffX) > 50) {
let currentActive = tabsOrder.find(t => {
return document.getElementById('btnTab' + pascalMap[t])?.classList.contains('tab-active');
}) || 'luantri';
let currentIndex = tabsOrder.indexOf(currentActive);
if (diffX < 0) {
if (currentIndex < tabsOrder.length - 1) {
switchTab(tabsOrder[currentIndex + 1]);
}
} else if (diffX > 0) {
if (currentIndex > 0) {
switchTab(tabsOrder[currentIndex - 1]);
}
}
}
}
document.addEventListener('touchstart', e => {
touchstartX = e.touches[0].clientX;
touchstartY = e.touches[0].clientY;
}, { passive: true });
document.addEventListener('touchend', e => {
const endX = e.changedTouches[0].clientX;
const endY = e.changedTouches[0].clientY;
handleSwipe(touchstartX, touchstartY, endX, endY);
}, { passive: true });
document.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && e.target.tagName === 'INPUT') {
e.target.blur();
}
});