基线:全量源码首提(D1 版本控制落地,含第一轮优化 B1-B4/A1/A4/缩放修复)
This commit is contained in:
@@ -0,0 +1,485 @@
|
||||
// ============================================================
|
||||
// CadBridge:WinForms 宿主与网页 CAD(CAD-Viewer)之间的互操作桥
|
||||
// · 下行(C#→网页):PostMessage 发 startPick/zoomToHandle/deleteHandles/clearHighlight
|
||||
// · 上行(网页→C#):WebMessageReceived 收 paPicked/entityErased/paExited
|
||||
// · 扒图结果与实体删除事件以 C# 事件形式提供给主窗口(替代 AutoCAD 版
|
||||
// 的 PaTextSelector.BatchTextsPicked 与 Database.ObjectErased)
|
||||
// ============================================================
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Script.Serialization;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Microsoft.Web.WebView2.WinForms;
|
||||
|
||||
namespace 成套报价软件
|
||||
{
|
||||
public static class CadBridge
|
||||
{
|
||||
private static WebView2 _wv;
|
||||
private static readonly JavaScriptSerializer _json = new JavaScriptSerializer();
|
||||
|
||||
/// <summary>网页 CAD 是否已挂载(WebView2 已附加)。</summary>
|
||||
public static bool IsViewerReady
|
||||
{
|
||||
get { return _wv != null && _wv.CoreWebView2 != null; }
|
||||
}
|
||||
|
||||
/// <summary>网页 CAD 当前打开的图纸名(去扩展名;paPicked 消息携带更新,未带为空)。
|
||||
/// 扒图写入箱号「图纸」列用。</summary>
|
||||
public static string CurrentDrawingName { get; private set; }
|
||||
|
||||
/// <summary>记录网页 CAD 上报的图纸名(去扩展名;路径分隔符兼容 / 与 \)。</summary>
|
||||
private static void SetCurrentDrawingName(string raw)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(raw)) return;
|
||||
try
|
||||
{
|
||||
CurrentDrawingName = System.IO.Path.GetFileNameWithoutExtension(raw.Replace('/', '\\'));
|
||||
}
|
||||
catch { CurrentDrawingName = raw; }
|
||||
}
|
||||
|
||||
/// <summary>网页 CAD 中实体被删除(句柄十六进制字符串)。</summary>
|
||||
public static event Action<string> EntityErased;
|
||||
|
||||
/// <summary>网页 CAD 加载渲染进度变化(文本已拼好,直接显示:
|
||||
/// "加载中 35% · 阶段 PARSE · 12.3s" / "渲染完成 · 45.2s" / "打开失败")。</summary>
|
||||
public static event Action<string> 加载状态;
|
||||
|
||||
/// <summary>网页 CAD 扒图完成一批(图号/箱号/元件)。</summary>
|
||||
public static event Action<MyApp.PaMode, List<MyApp.PaTextResult>> PaPicked;
|
||||
|
||||
/// <summary>用户按 ESC 退出网页扒图连续模式。</summary>
|
||||
public static event Action PaExited;
|
||||
|
||||
/// <summary>用户按 F1(焦点在网页或宿主时都触发)——元件两两合并。</summary>
|
||||
public static event Action F1Requested;
|
||||
|
||||
/// <summary>用户按 F2——元件三个一组合并。</summary>
|
||||
public static event Action F2Requested;
|
||||
|
||||
/// <summary>用户按 F3——元件四个一组合并。</summary>
|
||||
public static event Action F3Requested;
|
||||
|
||||
/// <summary>触发 F1 请求(消息过滤器等宿主侧来源调用)。</summary>
|
||||
public static void RaiseF1()
|
||||
{
|
||||
if (F1Requested != null) F1Requested();
|
||||
}
|
||||
|
||||
/// <summary>触发 F2 请求(消息过滤器等宿主侧来源调用)。</summary>
|
||||
public static void RaiseF2()
|
||||
{
|
||||
if (F2Requested != null) F2Requested();
|
||||
}
|
||||
|
||||
/// <summary>触发 F3 请求(消息过滤器等宿主侧来源调用)。</summary>
|
||||
public static void RaiseF3()
|
||||
{
|
||||
if (F3Requested != null) F3Requested();
|
||||
}
|
||||
|
||||
/// <summary>把 WebView2 挂到桥上(在控件初始化完成后调用一次)。</summary>
|
||||
public static void Attach(WebView2 wv)
|
||||
{
|
||||
if (_wv == wv) return;
|
||||
if (_wv != null) _wv.WebMessageReceived -= OnMessage;
|
||||
_wv = wv;
|
||||
if (_wv != null) _wv.WebMessageReceived += OnMessage;
|
||||
}
|
||||
|
||||
private static void OnMessage(object sender, CoreWebView2WebMessageReceivedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.TryGetWebMessageAsString() != null) return; // 只处理 JSON 消息
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
var msg = _json.Deserialize<Dictionary<string, object>>(e.WebMessageAsJson);
|
||||
if (msg == null || !msg.ContainsKey("type")) return;
|
||||
string type = msg["type"] as string;
|
||||
|
||||
if (type == "paPicked")
|
||||
{
|
||||
int mode = Convert.ToInt32(msg["mode"]);
|
||||
// 网页端随批携带当前图纸文件名(未带时保留上次值)
|
||||
if (msg.ContainsKey("fileName"))
|
||||
SetCurrentDrawingName(Convert.ToString(msg["fileName"]));
|
||||
// 诊断:记录 fileName 是否上报(排查网页旧缓存/未带字段)——logs\cadbridge.log
|
||||
try
|
||||
{
|
||||
string logDir = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs");
|
||||
if (!System.IO.Directory.Exists(logDir)) System.IO.Directory.CreateDirectory(logDir);
|
||||
System.IO.File.AppendAllText(System.IO.Path.Combine(logDir, "cadbridge.log"),
|
||||
DateTime.Now.ToString("HH:mm:ss.fff") + " paPicked mode=" + mode +
|
||||
" fileName=[" + (msg.ContainsKey("fileName") ? Convert.ToString(msg["fileName"]) : "<无字段>") +
|
||||
"] Current=[" + (CurrentDrawingName ?? "") + "]" + Environment.NewLine);
|
||||
}
|
||||
catch { }
|
||||
var results = new List<MyApp.PaTextResult>();
|
||||
var arr = msg["results"] as System.Collections.ArrayList;
|
||||
if (arr != null)
|
||||
{
|
||||
foreach (Dictionary<string, object> item in arr)
|
||||
{
|
||||
results.Add(new MyApp.PaTextResult
|
||||
{
|
||||
Text = Convert.ToString(item["text"]) ?? "",
|
||||
FrameHandle = Convert.ToString(item["frameHandle"]) ?? "",
|
||||
TextHandle = Convert.ToString(item["textHandle"]) ?? ""
|
||||
});
|
||||
}
|
||||
}
|
||||
// WebMessageReceived 在 UI 线程触发,可直接调用主窗口
|
||||
if (PaPicked != null)
|
||||
PaPicked((MyApp.PaMode)mode, results);
|
||||
}
|
||||
else if (type == "drawingName")
|
||||
{
|
||||
// 网页打开图纸时主动上报文件名(不等扒图)——右键新建箱号也要用到
|
||||
SetCurrentDrawingName(Convert.ToString(msg["fileName"]));
|
||||
}
|
||||
else if (type == "drawingOpened")
|
||||
{
|
||||
// ★ 网页图纸就绪(真名通知)——读该图的蒙版回灌。
|
||||
// 存储主位置=图纸旁边的 sidecar 文件 <图纸完整路径>.cadmask.json
|
||||
// (与 PDF 的 xxx.pdf.pdfmask.json 同模式,数据跟图纸文件走);
|
||||
// sidecar 不存在时兜底读软件目录的旧存档(cadbt\扒图蒙版\,迁移期兼容)
|
||||
string key = msg.ContainsKey("key") ? Convert.ToString(msg["key"]) : "";
|
||||
string dwgPath = msg.ContainsKey("dwgPath") ? Convert.ToString(msg["dwgPath"]) : "";
|
||||
// ★ 去重:同 key 3 秒内的重复通知丢弃(实测出现过 16 连发风暴,
|
||||
// 每发都触发回灌+挂起,恢复调度全部错乱)
|
||||
if (key == _lastNotifyKey && (DateTime.Now - _lastNotifyAt).TotalSeconds < 3)
|
||||
{
|
||||
记日志("drawingOpened 重复通知已忽略: [" + key + "]");
|
||||
return;
|
||||
}
|
||||
_lastNotifyKey = key;
|
||||
_lastNotifyAt = DateTime.Now;
|
||||
记日志("收到 drawingOpened key=[" + key + "] dwgPath=[" + dwgPath + "]");
|
||||
_lastMaskKey = key;
|
||||
_lastDwgPath = dwgPath;
|
||||
回灌蒙版文件(key, dwgPath);
|
||||
}
|
||||
else if (type == "framesSaved")
|
||||
{
|
||||
// ★ 网页每次画框/删框/合并后上报——写入该图纸的蒙版存档
|
||||
string key = msg.ContainsKey("key") ? Convert.ToString(msg["key"]) : "";
|
||||
string dwgPath = msg.ContainsKey("dwgPath") ? Convert.ToString(msg["dwgPath"]) : "";
|
||||
if (!string.IsNullOrWhiteSpace(key) && msg.ContainsKey("frames"))
|
||||
{
|
||||
try
|
||||
{
|
||||
string json = _json.Serialize(msg["frames"]);
|
||||
// 主存储:图纸旁边的 sidecar(有完整路径时);兜底:软件目录旧位置
|
||||
if (!string.IsNullOrWhiteSpace(dwgPath))
|
||||
{
|
||||
string sidecar = dwgPath + ".cadmask.json";
|
||||
System.IO.File.WriteAllText(sidecar, json);
|
||||
}
|
||||
else
|
||||
{
|
||||
string f = 蒙版文件路径(key);
|
||||
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(f));
|
||||
System.IO.File.WriteAllText(f, json);
|
||||
}
|
||||
}
|
||||
catch (Exception exW) { 记日志("蒙版文件写入失败[" + key + "]: " + exW.Message); }
|
||||
}
|
||||
}
|
||||
else if (type == "entityErased")
|
||||
{
|
||||
string handle = Convert.ToString(msg["handle"]);
|
||||
if (!string.IsNullOrWhiteSpace(handle) && EntityErased != null)
|
||||
EntityErased(handle);
|
||||
}
|
||||
else if (type == "paError")
|
||||
{
|
||||
string errMsg = Convert.ToString(msg["message"]);
|
||||
记日志("paError: " + errMsg); // ★ 先落日志(模态弹窗会打断渲染,进度类上报绝不弹)
|
||||
System.Windows.Forms.MessageBox.Show(
|
||||
"网页 CAD 返回错误:\n" + errMsg,
|
||||
"扒数据", System.Windows.Forms.MessageBoxButtons.OK,
|
||||
System.Windows.Forms.MessageBoxIcon.Warning);
|
||||
}
|
||||
else if (type == "loadProgress")
|
||||
{
|
||||
// ★ 三段进度:pct 0~100=文件解析;CONVERT_DONE=解析完/正式渲染开始;
|
||||
// RENDER=渲染中(r=渲染百分比);RENDER_DONE=渲染完成(画面就绪,secs=全程耗时)
|
||||
int pct = msg.ContainsKey("pct") ? Convert.ToInt32(msg["pct"]) : 0;
|
||||
string stage = msg.ContainsKey("stage") ? Convert.ToString(msg["stage"]) : "";
|
||||
string secs = msg.ContainsKey("secs") ? Convert.ToString(msg["secs"]) : "0";
|
||||
int r = msg.ContainsKey("r") ? Convert.ToInt32(msg["r"]) : 0;
|
||||
string text;
|
||||
if (pct < 0)
|
||||
{
|
||||
text = "打开失败(文件无法解析)";
|
||||
记日志("[加载] " + text);
|
||||
}
|
||||
else if (stage == "RENDER_DONE")
|
||||
{
|
||||
text = "渲染完成 · " + secs + "s";
|
||||
记日志("[加载] 渲染完成(含正式渲染), 全程 " + secs + " 秒");
|
||||
}
|
||||
else if (stage == "RENDER")
|
||||
{
|
||||
text = "渲染中 " + r + "% · " + secs + "s";
|
||||
if ((DateTime.Now - _lastProgLogAt).TotalSeconds >= 5)
|
||||
{
|
||||
_lastProgLogAt = DateTime.Now;
|
||||
记日志("[加载] " + text);
|
||||
}
|
||||
}
|
||||
else if (stage == "CONVERT_DONE")
|
||||
{
|
||||
text = "解析完成(" + secs + "s), 正式渲染中...";
|
||||
记日志("[加载] 文件解析完成 " + secs + " 秒, 正式渲染开始");
|
||||
}
|
||||
else if (pct >= 100)
|
||||
{
|
||||
text = "解析完成(" + secs + "s)";
|
||||
}
|
||||
else
|
||||
{
|
||||
text = "加载中 " + pct + "% · " + stage + " · " + secs + "s";
|
||||
if ((DateTime.Now - _lastProgLogAt).TotalSeconds >= 5)
|
||||
{
|
||||
_lastProgLogAt = DateTime.Now;
|
||||
记日志("[加载] " + text);
|
||||
}
|
||||
}
|
||||
if (加载状态 != null) 加载状态(text);
|
||||
}
|
||||
else if (type == "webLog")
|
||||
{
|
||||
// ★ 网页端诊断通道:只写 cadbridge.log,绝不弹窗(蒙版恢复各阶段/页面异常)
|
||||
记日志(Convert.ToString(msg["message"]));
|
||||
}
|
||||
else if (type == "f1")
|
||||
{
|
||||
if (F1Requested != null) F1Requested();
|
||||
}
|
||||
else if (type == "f2")
|
||||
{
|
||||
if (F2Requested != null) F2Requested();
|
||||
}
|
||||
else if (type == "f3")
|
||||
{
|
||||
if (F3Requested != null) F3Requested();
|
||||
}
|
||||
else if (type == "paExited")
|
||||
{
|
||||
if (PaExited != null) PaExited();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 消息格式异常直接忽略,不影响宿主
|
||||
}
|
||||
}
|
||||
|
||||
private static void Post(object msg)
|
||||
{
|
||||
if (!IsViewerReady) return;
|
||||
try
|
||||
{
|
||||
_wv.CoreWebView2.PostWebMessageAsJson(_json.Serialize(msg));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// ---------- 蒙版文件持久化(cadbt\扒图蒙版\<图纸名键>.json) ----------
|
||||
|
||||
/// <summary>图纸键(网页 framesKey 同款:cadbt_pa_frames:名字)对应的蒙版文件路径。
|
||||
/// 键里的':'换成'_'并清洗文件名非法字符。</summary>
|
||||
private static string 蒙版文件路径(string key)
|
||||
{
|
||||
string safe = "";
|
||||
char[] bad = System.IO.Path.GetInvalidFileNameChars();
|
||||
foreach (char c in (key ?? ""))
|
||||
{
|
||||
bool invalid = c == ':' || c == '\\' || c == '/';
|
||||
if (!invalid) foreach (char b in bad) if (b == c) { invalid = true; break; }
|
||||
safe += invalid ? '_' : c;
|
||||
}
|
||||
string dir = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cadbt", "扒图蒙版");
|
||||
return System.IO.Path.Combine(dir, safe + ".json");
|
||||
}
|
||||
|
||||
/// <summary>宿主"打开CAD图纸"按钮待加载的图纸完整路径(取文件通道按此回字节)。</summary>
|
||||
internal static string 待加载图纸路径 = "";
|
||||
|
||||
/// <summary>最后一次 drawingOpened 的 key/完整路径(手动"显示蒙版"按钮回灌用)。</summary>
|
||||
private static string _lastMaskKey = "";
|
||||
private static string _lastDwgPath = "";
|
||||
private static string _lastNotifyKey = "";
|
||||
private static DateTime _lastNotifyAt = DateTime.MinValue;
|
||||
/// <summary>中间进度日志节流(loadProgress 每秒一条,日志 5 秒一条)。</summary>
|
||||
private static DateTime _lastProgLogAt = DateTime.MinValue;
|
||||
|
||||
/// <summary>宿主端"打开CAD图纸"按钮:设置待加载路径并通知网页从同源通道加载。
|
||||
/// 蒙版 sidecar(<dwgPath>.cadmask.json)就存在图纸旁边(与 PDF 同模式)。</summary>
|
||||
public static void OpenLocalDrawing(string dwgPath, string name)
|
||||
{
|
||||
待加载图纸路径 = dwgPath;
|
||||
Post(new { type = "openLocalFile", url = "http://localhost:5173/localfile/current", name = name, dwgPath = dwgPath });
|
||||
记日志("宿主打开CAD图纸: " + dwgPath);
|
||||
}
|
||||
|
||||
/// <summary>手动"显示蒙版"按钮:先回灌存档(网页只 stash),再下令绘制——
|
||||
/// 用户点它时图纸必然可见=渲染稳定,与扒图场景同级,绕开自动时机的所有坑。</summary>
|
||||
public static void ReloadMasks()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(_lastMaskKey))
|
||||
{
|
||||
回灌蒙版文件(_lastMaskKey, _lastDwgPath);
|
||||
}
|
||||
Post(new { type = "drawMasks" });
|
||||
记日志("【显示蒙版】按钮: 回灌+绘制指令已发送");
|
||||
}
|
||||
|
||||
/// <summary>读该图纸的蒙版并回灌网页(restoreFramesFromFile)。
|
||||
/// 优先级:①图纸旁边的 sidecar(<dwgPath>.cadmask.json) ②软件目录旧存档(迁移兼容)。</summary>
|
||||
private static void 回灌蒙版文件(string key, string dwgPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key)) return;
|
||||
string json = null, 来源 = "";
|
||||
if (!string.IsNullOrWhiteSpace(dwgPath))
|
||||
{
|
||||
string sidecar = dwgPath + ".cadmask.json";
|
||||
if (System.IO.File.Exists(sidecar))
|
||||
{
|
||||
json = System.IO.File.ReadAllText(sidecar);
|
||||
来源 = "sidecar(图纸同目录)";
|
||||
}
|
||||
}
|
||||
if (json == null)
|
||||
{
|
||||
string f = 蒙版文件路径(key);
|
||||
if (System.IO.File.Exists(f))
|
||||
{
|
||||
json = System.IO.File.ReadAllText(f);
|
||||
来源 = "旧位置(软件目录)";
|
||||
}
|
||||
}
|
||||
if (json == null)
|
||||
{
|
||||
记日志("蒙版存档不存在(跳过回灌): key=" + key + " dwgPath=" + dwgPath);
|
||||
return;
|
||||
}
|
||||
var frames = _json.Deserialize<System.Collections.ArrayList>(json);
|
||||
if (frames != null && frames.Count > 0)
|
||||
{
|
||||
Post(new { type = "restoreFramesFromFile", frames = frames });
|
||||
记日志("蒙版回灌(" + 来源 + "): " + key + " 共 " + frames.Count + " 框");
|
||||
}
|
||||
}
|
||||
catch (Exception exR) { 记日志("蒙版文件回灌失败[" + key + "]: " + exR.Message); }
|
||||
}
|
||||
|
||||
/// <summary>cadbridge 诊断日志(logs\cadbridge.log,与 paPicked 诊断同文件)。</summary>
|
||||
private static void 记日志(string msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
string logDir = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs");
|
||||
if (!System.IO.Directory.Exists(logDir)) System.IO.Directory.CreateDirectory(logDir);
|
||||
System.IO.File.AppendAllText(System.IO.Path.Combine(logDir, "cadbridge.log"),
|
||||
DateTime.Now.ToString("HH:mm:ss.fff") + " " + msg + Environment.NewLine);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送扒图命令。接受原 CAD 命令名(CADPATU/CADPAXH/CADPAYJ/CADPACL)
|
||||
/// 或网页命令名(patu/paxh/payj)。
|
||||
/// </summary>
|
||||
public static void SendPaCommand(string cmd)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(cmd)) return;
|
||||
string c = cmd.Trim().ToUpperInvariant();
|
||||
int mode = 0;
|
||||
if (c == "CADPATU" || c == "PATU") mode = 0;
|
||||
else if (c == "CADPAXH" || c == "PAXH") mode = 1;
|
||||
else if (c == "CADPAYJ" || c == "PAYJ") mode = 2;
|
||||
else if (c == "CADPACL" || c == "PACL")
|
||||
{
|
||||
ClearHighlight();
|
||||
return;
|
||||
}
|
||||
else return;
|
||||
Post(new { type = "startPick", mode = mode });
|
||||
}
|
||||
|
||||
/// <summary>缩放居中到指定 Handle 的实体并高亮(点击表格行定位框)。</summary>
|
||||
public static void ZoomToFrameHandle(string handle)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(handle)) return;
|
||||
Post(new { type = "zoomToHandle", handle = handle });
|
||||
}
|
||||
|
||||
/// <summary>批量删除指定 Handle 的实体(删表格行联动删框)。</summary>
|
||||
public static void DeleteFramesByHandles(IEnumerable<string> handles)
|
||||
{
|
||||
if (handles == null) return;
|
||||
var list = new List<string>();
|
||||
foreach (string h in handles)
|
||||
if (!string.IsNullOrWhiteSpace(h)) list.Add(h);
|
||||
if (list.Count == 0) return;
|
||||
Post(new { type = "deleteHandles", handles = list });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 合并蒙版:保留 keep 句柄的蒙版并扩大到所有 removes 蒙版的并集,
|
||||
/// 被合并行的蒙版从网页 CAD 移除(元件合并时蒙版同步合并)。
|
||||
/// </summary>
|
||||
public static void MergeMasks(string keep, System.Collections.Generic.IEnumerable<string> removes)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(keep)) return;
|
||||
var list = new List<string>();
|
||||
if (removes != null)
|
||||
foreach (string h in removes)
|
||||
if (!string.IsNullOrWhiteSpace(h) && h != keep) list.Add(h);
|
||||
if (list.Count == 0) return;
|
||||
Post(new { type = "mergeMasks", keep = keep, removes = list });
|
||||
}
|
||||
|
||||
/// <summary>清除网页 CAD 中的所有高亮。</summary>
|
||||
public static void ClearHighlight()
|
||||
{
|
||||
Post(new { type = "clearHighlight" });
|
||||
}
|
||||
|
||||
/// <summary>显示箱号组虚线框:把该箱号的箱号图框+全部元件图框求并集,
|
||||
/// 网页 CAD 画一个橙色虚线矩形(点元件表/箱号表行时调用)。</summary>
|
||||
public static void SetGroupFrames(System.Collections.Generic.IEnumerable<string> handles)
|
||||
{
|
||||
if (handles == null) return;
|
||||
var list = new List<string>();
|
||||
foreach (string h in handles)
|
||||
if (!string.IsNullOrWhiteSpace(h)) list.Add(h);
|
||||
Post(new { type = "groupHighlight", handles = list });
|
||||
}
|
||||
|
||||
/// <summary>清除箱号组虚线框(点图号表行时)。</summary>
|
||||
public static void ClearGroupFrames()
|
||||
{
|
||||
Post(new { type = "groupHighlightClear" });
|
||||
}
|
||||
|
||||
/// <summary>画组虚线框并缩放到"装下整个组框"(点箱号/元件行跳转时)。
|
||||
/// focusHandle=点中的那个框:网页端对它画黄色高亮框。</summary>
|
||||
public static void ZoomToGroupFrames(System.Collections.Generic.IEnumerable<string> handles, string focusHandle = null)
|
||||
{
|
||||
if (handles == null) return;
|
||||
var list = new List<string>();
|
||||
foreach (string h in handles)
|
||||
if (!string.IsNullOrWhiteSpace(h)) list.Add(h);
|
||||
Post(new { type = "zoomToGroup", handles = list, focus = focusHandle ?? "" });
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user