<%* // --------------------------------------------------------------- // 1. 初始化與設定 // --------------------------------------------------------------- const HKBU_SEARCH_URL = “https://sys01.lib.hkbu.edu.hk/cmed/cmfid/index.php”; const HKBU_DETAIL_BASE = “https://sys01.lib.hkbu.edu.hk/cmed/cmfid/detail.php”; const CMU_DB_PATH = “System/CMU_Offline_Data_Map.json”;

let cmuDB = {}; try { const dbFile = tp.file.find_tfile(CMU_DB_PATH); if (dbFile) { const content = await app.vault.read(dbFile); cmuDB = JSON.parse(content); } else { new Notice(⚠️ 找不到 ${CMU_DB_PATH}); } } catch (e) { new Notice(⚠️ 讀取 CMU 資料庫失敗: ${e.message}); }

function cleanText(text) { if (!text) return ""; return text.trim() .replace(/\s+/g, "")
.replace(/(.)/g, "")
.replace(/【.
?】/g, "")
.replace(/各等分/g, "") .replace(/[\d一二三四五六七八九十半]+[兩錢分斤g克]/g, ""); }

let noteTitle = tp.file.title; let finalOutput = ""; let useFetchedData = false;

let data = { source: "", yamlIngredients: "", bodyIngredients: "", rolesStr: "", function: "", indication: "", category: "" };

// --------------------------------------------------------------- // 2. 核心邏輯 // --------------------------------------------------------------- try { if (noteTitle.startsWith(“未命名”) || noteTitle.startsWith(“Untitled”) || !noteTitle) { noteTitle = await tp.system.prompt(“請輸入要查詢的方劑名稱:”); if (noteTitle) await tp.file.rename(noteTitle); }

if (noteTitle) {
    
    // =========================================================
    // 階段一:搜尋 HKBU
    // =========================================================
    new Notice(`[1/2] 搜尋 HKBU 線上資料庫:${noteTitle}...`);
    let skipToCMU = false;

    try {
        const hkbuRes = await requestUrl({ url: `${HKBU_SEARCH_URL}?qry=${encodeURIComponent(noteTitle)}&lang=cht` });
        const parser = new DOMParser();
        const hkbuDoc = parser.parseFromString(hkbuRes.text, "text/html");
        
        const listTable = hkbuDoc.getElementById("list");
        let candidates = [];

        if (listTable) {
            const rows = listTable.querySelectorAll("tbody tr");
            for (let row of rows) {
                const cols = row.querySelectorAll("td");
                if (cols.length >= 3) {
                    const link = cols[2].querySelector("a");
                    if (link) {
                        const name = link.textContent.trim();
                        const href = link.getAttribute("href");
                        const match = href.match(/id=([A-Z0-9]+)/);
                        if (match) {
                            candidates.push({
                                name: name,
                                id: match[1],
                                func: cols.length >= 9 ? cols[6].textContent.trim() : "",
                                indi: cols.length >= 9 ? cols[8].textContent.trim() : ""
                            });
                        }
                    }
                }
            }
        }

        let selectedHKBU = null;
        const exact = candidates.find(c => c.name === noteTitle);
        
        if (exact) {
            selectedHKBU = exact;
            new Notice(`HKBU 找到完全匹配:${selectedHKBU.name}`);
        } else if (candidates.length > 0) {
            const options = candidates.map(c => c.name);
            options.push("❌ 以上皆非,嘗試搜尋 CMU 本地資料庫");
            const choice = await tp.system.suggester(options, [...candidates, "SKIP"], false, `HKBU 找到近似結果,請選擇:`);
            if (choice === "SKIP") skipToCMU = true;
            else if (choice) selectedHKBU = choice;
            else skipToCMU = true; 
        } else {
            skipToCMU = true; 
        }

        if (selectedHKBU) {
            useFetchedData = true;
            data.function = selectedHKBU.func;
            data.indication = selectedHKBU.indi;
            
            new Notice(`解析 HKBU 詳情...`);
            const detailRes = await requestUrl({ url: `${HKBU_DETAIL_BASE}?id=${selectedHKBU.id}&lang=cht` });
            const detailDoc = parser.parseFromString(detailRes.text, "text/html");
            const allTds = Array.from(detailDoc.querySelectorAll("td"));

            // --- 1. 抓取出處 ---
            for (let td of allTds) {
                if (td.textContent.includes("【出處】") || td.textContent.includes("出處")) {
                    let raw = td.textContent.replace(/【.*?】/g, "").replace("出處", "").replace(/[::]/, "").trim();
                    if (raw.length > 0 && raw.length < 50) {
                        data.source = raw.replace(/[《》]/g, "");
                        break;
                    }
                }
            }

            // --- 2. 抓取分類 ---
            for (let td of allTds) {
                if (td.textContent.includes("分類")) {
                    let raw = td.textContent.replace(/【.*?】/g, "").replace("分類", "").replace(/[::]/, "").trim();
                    if (raw.length < 20) data.category = raw;
                }
            }

            // --- 3. 抓取成分 (新邏輯:優先找 #formula_table) ---
            let rolesMap = { "君": [], "臣": [], "佐": [], "使": [], "其他": [] };
            let allHerbs = [];
            let hasRoles = false; // 標記是否真的抓到了君臣佐使

            // 嘗試直接用 ID 抓取表格 (這是 HKBU 最準確的組成區塊)
            let formulaTable = detailDoc.getElementById("formula_table");
            
            // 如果沒有 ID,嘗試用舊方法找包含「君臣」的表格
            if (!formulaTable) {
                 for (let tbl of detailDoc.querySelectorAll("table")) {
                    if (tbl.textContent.includes("君") && tbl.textContent.includes("臣") && tbl.textContent.length < 2000) {
                        formulaTable = tbl;
                        hasRoles = true; // 這種表格通常有寫君臣佐使
                        break;
                    }
                }
            }

            if (formulaTable) {
                let currentRole = "其他";
                for (let row of formulaTable.querySelectorAll("tr")) {
                    const cells = Array.from(row.querySelectorAll("td"));
                    if (cells.length === 0) continue;
                    let rawTexts = cells.map(c => c.textContent.trim());
                    if (rawTexts[0].includes("【") || rawTexts[0].includes("功效")) continue;
                    
                    let herbName = "";
                    
                    if (hasRoles) {
                        // 標準君臣佐使表格邏輯
                        const roleKeywords = ["君", "臣", "佐", "使"];
                        if (roleKeywords.includes(rawTexts[0])) {
                            currentRole = rawTexts[0];
                            if (rawTexts.length > 1) herbName = rawTexts[1];
                        } else {
                            if (rawTexts[0].length > 0 && rawTexts[0].length < 8) herbName = rawTexts[0];
                        }
                    } else {
                        // 特殊表格 (回陽救急湯類型):沒有君臣欄位,藥名通常在第2欄 (index 1)
                        // 但要注意 rowspan 導致的欄位位移
                        // 簡單暴力法:遍歷所有 cell,只要像藥名就抓
                        for (let cellText of rawTexts) {
                            if (cellText.length > 0 && cellText.length <= 4) {
                                // 排除非藥名的干擾詞
                                const ignore = ["溫裏", "回陽", "祛寒", "通脈", "補益", "脾胃", "固守", "中州", "辛香", "走竄"];
                                if (!ignore.some(k => cellText.includes(k))) {
                                    herbName = cellText;
                                    break; // 這一行抓到一個藥名就夠了
                                }
                            }
                        }
                    }

                    if (herbName) {
                        herbName = cleanText(herbName);
                        const blackList = ["君", "臣", "佐", "使", "功效", "主治", "用法", "【", "】"];
                        let isClean = true;
                        for (let bad of blackList) if (herbName.includes(bad)) isClean = false;
                        if (isClean && herbName.length > 0 && !allHerbs.includes(herbName)) {
                            allHerbs.push(herbName);
                            rolesMap[currentRole].push(herbName);
                        }
                    }
                }
            } 
            
            // 如果連表格都沒抓到,才去試純文字 fallback (略)
            // ...

            // 構建輸出
            if (allHerbs.length > 0) {
                data.yamlIngredients = "\n" + allHerbs.map(h => `  - "[[${h}]]"`).join("\n");
                data.bodyIngredients = allHerbs.map(h => `[[${h}]]`).join("、");
                
                if (hasRoles) {
                    let str = "";
                    ["君", "臣", "佐", "使"].forEach(r => {
                        str += rolesMap[r].length > 0 ? `\t- ${r}:${rolesMap[r].join("、")}\n` : `\t- ${r}:\n`;
                    });
                    data.rolesStr = str;
                } else {
                    // 雖然抓到了成分,但沒分君臣,給出骨架
                     data.rolesStr = "\t- 君:\n\t- 臣:\n\t- 佐:\n\t- 使:\n";
                }
            }
        } 
    } catch (e) {
        console.error("HKBU Search Error:", e);
        skipToCMU = true;
    }

    // =========================================================
    // 階段二:查詢 CMU (本地離線資料庫)
    // =========================================================
    if (!useFetchedData && skipToCMU) {
        // (CMU 邏輯保持不變)
         new Notice(`[2/2] 搜尋 CMU 本地資料庫...`);
        let record = cmuDB[noteTitle];
        if (!record) {
            const allKeys = Object.keys(cmuDB);
            const matches = allKeys.filter(k => k.includes(noteTitle));
            if (matches.length > 0) {
                const choice = await tp.system.suggester(matches, matches, false, `CMU 找到 ${matches.length} 個近似結果:`);
                if (choice) record = cmuDB[choice];
            }
        }
        if (record) {
            useFetchedData = true;
            data.source = record.source;
            data.function = record.function;
            data.indication = record.indication;
            if (record.ingredients && record.ingredients.length > 0) {
                data.bodyIngredients = record.ingredients.map(h => `[[${h}]]`).join("、");
                data.yamlIngredients = "\n" + record.ingredients.map(h => `  - "[[${h}]]"`).join("\n");
            }
            data.rolesStr = "\t- 君:\n\t- 臣:\n\t- 佐:\n\t- 使:\n";
        }
    }
}

} catch (e) { new Notice(錯誤:${e.message}); useFetchedData = false; }

// --------------------------------------------------------------- // 3. 輸出內容 // --------------------------------------------------------------- if (useFetchedData) { finalOutput = `--- tags:

  • 方劑 up:
  • 溫裡劑” 成分:{data.source} 功效: ${data.function}

摘要

簡述重點


組成

  • 成分:{data.rolesStr}

功效與病機

  • 功用:${data.function}
  • 主治:${data.indication}
  • 病機、辨證:

其他

  • 口訣:
  • 備註:

病案

問題

{noteTitle}的組成及君臣佐使為? #flashcard ? {data.bodyIngredients} ${data.rolesStr}

{noteTitle}的主治為? #flashcard ? {data.indication} `;

} else { finalOutput = `--- tags:

  • 方劑 up:
  • 溫裡劑” 成分: 出處: 功效:

摘要

簡述重點


組成

  • 成分:
    • 君:
    • 臣:
    • 佐:
    • 使:

功效與病機

  • 功用:
  • 主治:
  • 病機、辨證:

其他

  • 口訣:
  • 備註:

病案

問題

${noteTitle}的組成及君臣佐使為? flashcard

${noteTitle}的主治為? flashcard `; }

tR += finalOutput; %> <% await tp.file.move(“中藥/中醫方劑學/內/溫裡/” + tp.file.title) %>