@@ -703,15 +703,27 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
703703
704704 // 获取所有选中的蓝本路径
705705 function getSelectedBlueprints() {
706- const cbs = blueprintListEl.querySelectorAll('input[type="checkbox"]:checked');
706+ const globalCb = document.getElementById("cbGlobalBlueprint");
707+ if (globalCb && globalCb.checked) {
708+ // 全局蓝本:返回所有蓝本路径
709+ return blueprintEntries.map(e => typeof e === "string" ? e : e.path);
710+ }
711+ // 局部蓝本:返回勾选的
712+ const cbs = blueprintListEl.querySelectorAll('.local-blueprint-cb:checked');
707713 return Array.from(cbs).map(cb => cb.value);
708714 }
709715
710- // 全选/取消全选
716+ // 全选/取消全选(只影响局部蓝本)
711717 document.getElementById("btnSelectAll").addEventListener("click", () => {
712- const cbs = blueprintListEl.querySelectorAll('input[type="checkbox"]');
713- const allChecked = Array.from(cbs).every(cb => cb.checked);
714- cbs.forEach(cb => { cb.checked = !allChecked; });
718+ const globalCb = document.getElementById("cbGlobalBlueprint");
719+ if (globalCb && globalCb.checked) {
720+ // 如果全局已勾选,提示用户先取消全局
721+ addLog("已勾选全局蓝本,无需全选局部。如需局部测试,请先取消全局勾选。", "warn");
722+ return;
723+ }
724+ const localCbs = blueprintListEl.querySelectorAll('.local-blueprint-cb');
725+ const allChecked = Array.from(localCbs).every(cb => cb.checked);
726+ localCbs.forEach(cb => { cb.checked = !allChecked; });
715727 });
716728
717729 // 扫描按钮
@@ -921,6 +933,32 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
921933 blueprintListEl.innerHTML = '<div style="color:var(--muted);padding:8px;text-align:center;font-size:12px">未找到蓝本文件</div>';
922934 return;
923935 }
936+
937+ // 全局蓝本选项(固定在顶部)
938+ const globalItem = document.createElement("label");
939+ globalItem.style.cssText = "display:flex;align-items:center;gap:4px;padding:4px 4px;border-radius:4px;cursor:pointer;font-size:12px;font-weight:600;background:var(--bg-secondary,#1e1e1e);border:1px solid var(--border);margin-bottom:4px";
940+ globalItem.title = "勾选后将测试所有蓝本";
941+
942+ const globalCb = document.createElement("input");
943+ globalCb.type = "checkbox";
944+ globalCb.id = "cbGlobalBlueprint";
945+ globalCb.checked = true; // 默认勾选全局
946+ globalCb.style.cssText = "flex-shrink:0;width:14px;height:14px";
947+
948+ const globalLabel = document.createElement("span");
949+ globalLabel.textContent = "🌍 全局蓝本(测试所有)";
950+ globalLabel.style.cssText = "flex:1;color:var(--fg)";
951+
952+ globalItem.appendChild(globalCb);
953+ globalItem.appendChild(globalLabel);
954+ blueprintListEl.appendChild(globalItem);
955+
956+ // 分割线
957+ const divider = document.createElement("div");
958+ divider.style.cssText = "height:1px;background:var(--border);margin:4px 0";
959+ blueprintListEl.appendChild(divider);
960+
961+ // 局部蓝本列表
924962 entries.forEach((entry, i) => {
925963 const path = typeof entry === "string" ? entry : entry.path;
926964 const appName = entry.appName || "";
@@ -937,15 +975,18 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
937975 const tooltipText = desc ? (desc + "\\n场景:" + scenarios + " 步骤:" + steps + "\\n" + path) : ("场景:" + scenarios + " 步骤:" + steps + "\\n" + path);
938976
939977 const item = document.createElement("label");
978+ item.className = "local-blueprint-item";
940979 item.style.cssText = "display:flex;align-items:flex-start;gap:4px;padding:3px 4px;border-radius:4px;cursor:pointer;font-size:12px;line-height:1.4;overflow:hidden;width:100%;box-sizing:border-box";
941980 item.title = tooltipText;
942981 item.addEventListener("mouseenter", () => { item.style.background = "var(--hover-bg,#2a2d2e)"; });
943982 item.addEventListener("mouseleave", () => { item.style.background = "transparent"; });
944983
945984 const cb = document.createElement("input");
946985 cb.type = "checkbox";
986+ cb.className = "local-blueprint-cb";
947987 cb.value = path;
948- cb.checked = (i === 0);
988+ cb.checked = false; // 默认不选(因为全局已选)
989+ cb.disabled = true; // 默认禁用(因为全局已选)
949990 cb.style.cssText = "margin-top:2px;flex-shrink:0;width:14px;height:14px";
950991
951992 const fileName = path.replace(/\\\\/g, "/").split("/").pop() || path;
@@ -960,6 +1001,33 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
9601001 item.appendChild(info);
9611002 blueprintListEl.appendChild(item);
9621003 });
1004+
1005+ // 全局蓝本checkbox交互逻辑
1006+ globalCb.addEventListener("change", () => {
1007+ const localItems = blueprintListEl.querySelectorAll(".local-blueprint-item");
1008+ const localCbs = blueprintListEl.querySelectorAll(".local-blueprint-cb");
1009+ if (globalCb.checked) {
1010+ // 勾选全局 → 局部全部禁用+灰化
1011+ localCbs.forEach(cb => { cb.disabled = true; cb.checked = false; });
1012+ localItems.forEach(item => {
1013+ item.style.opacity = "0.5";
1014+ item.title = "已勾选全局蓝本,无需勾选局部。如需局部测试,请取消全局勾选。";
1015+ });
1016+ } else {
1017+ // 取消全局 → 局部恢复可选
1018+ localCbs.forEach(cb => { cb.disabled = false; });
1019+ localItems.forEach((item, i) => {
1020+ item.style.opacity = "1";
1021+ const entry = entries[i];
1022+ const desc = entry.description || "";
1023+ const scenarios = entry.scenarioCount || 0;
1024+ const steps = entry.stepCount || 0;
1025+ const path = entry.path;
1026+ item.title = desc ? (desc + "\\n场景:" + scenarios + " 步骤:" + steps + "\\n" + path) : ("场景:" + scenarios + " 步骤:" + steps + "\\n" + path);
1027+ });
1028+ }
1029+ });
1030+
9631031 // 填入第一个路径到手动输入框
9641032 const firstPath = typeof entries[0] === "string" ? entries[0] : entries[0].path;
9651033 inputBlueprintPath.value = firstPath;
0 commit comments