From 169d4ac41562a1612e32baa044c8657d6d78fd56 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 5 Dec 2025 09:04:32 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...kVision.razor => HikVisionWebPlugin.razor} | 3 +- ...n.razor.cs => HikVisionWebPlugin.razor.cs} | 47 +++++++++-- ...n.razor.js => HikVisionWebPlugin.razor.js} | 2 +- .../wwwroot/hikvision.js | 81 ++++++++++++++----- 4 files changed, 106 insertions(+), 27 deletions(-) rename src/components/BootstrapBlazor.HikVision/Components/{HikVision.razor => HikVisionWebPlugin.razor} (79%) rename src/components/BootstrapBlazor.HikVision/Components/{HikVision.razor.cs => HikVisionWebPlugin.razor.cs} (72%) rename src/components/BootstrapBlazor.HikVision/Components/{HikVision.razor.js => HikVisionWebPlugin.razor.js} (92%) diff --git a/src/components/BootstrapBlazor.HikVision/Components/HikVision.razor b/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor similarity index 79% rename from src/components/BootstrapBlazor.HikVision/Components/HikVision.razor rename to src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor index e4b02995..87b87f8e 100644 --- a/src/components/BootstrapBlazor.HikVision/Components/HikVision.razor +++ b/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor @@ -1,5 +1,4 @@ @namespace BootstrapBlazor.Components @inherits BootstrapModuleComponentBase -
-
+
diff --git a/src/components/BootstrapBlazor.HikVision/Components/HikVision.razor.cs b/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.cs similarity index 72% rename from src/components/BootstrapBlazor.HikVision/Components/HikVision.razor.cs rename to src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.cs index a8fa5e3e..0373f60d 100644 --- a/src/components/BootstrapBlazor.HikVision/Components/HikVision.razor.cs +++ b/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.cs @@ -7,10 +7,10 @@ namespace BootstrapBlazor.Components; /// -/// 海康威视网络摄像机组件 +/// 海康威视网络摄像机组件 (Websdk Plugin 插件版本) /// -[JSModuleAutoLoader("./_content/BootstrapBlazor.HikVision/Components/HikVision.razor.js")] -public partial class HikVision +[JSModuleAutoLoader("./_content/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.js")] +public partial class HikVisionWebPlugin { /// /// 获得/设置 网络摄像机 IP 地址 @@ -64,6 +64,16 @@ public partial class HikVision .AddStyleFromAttributes(AdditionalAttributes) .Build(); + /// + /// 获得 Websdk 插件是否初始化成功 + /// + public bool Inited { get; private set; } + + /// + /// 获得 是否已登录 + /// + public bool IsLogined { get; private set; } + /// /// /// @@ -75,6 +85,15 @@ protected override void OnParametersSet() Height ??= "300px"; } + /// + /// + /// + /// + protected override async Task InvokeInitAsync() + { + Inited = await InvokeAsync("init", Id) ?? false; + } + /// /// 登录方法 /// @@ -86,7 +105,8 @@ protected override void OnParametersSet() /// public async Task Login(string ip, int port, string userName, string password, LoginType loginType = LoginType.Http) { - await InvokeVoidAsync("login", Id, ip, port, userName, password, (int)loginType); + ThrowIfNotInited(); + IsLogined = await InvokeAsync("login", Id, ip, port, userName, password, (int)loginType); } /// @@ -95,6 +115,7 @@ public async Task Login(string ip, int port, string userName, string password, L /// public async Task Logout() { + IsLogined = false; await InvokeVoidAsync("logout", Id); } @@ -104,7 +125,10 @@ public async Task Logout() /// public async Task StartRealPlay() { - await InvokeVoidAsync("startRealPlay", Id); + if (IsLogined) + { + await InvokeVoidAsync("startRealPlay", Id); + } } /// @@ -113,6 +137,17 @@ public async Task StartRealPlay() /// public async Task StopRealPlay() { - await InvokeVoidAsync("stopRealPlay", Id); + if (IsLogined) + { + await InvokeVoidAsync("stopRealPlay", Id); + } + } + + private void ThrowIfNotInited() + { + if (!Inited) + { + throw new InvalidOperationException("HikVision Web Plugin not inited"); + } } } diff --git a/src/components/BootstrapBlazor.HikVision/Components/HikVision.razor.js b/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.js similarity index 92% rename from src/components/BootstrapBlazor.HikVision/Components/HikVision.razor.js rename to src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.js index 198f5c39..4665c6d1 100644 --- a/src/components/BootstrapBlazor.HikVision/Components/HikVision.razor.js +++ b/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.js @@ -7,7 +7,7 @@ export async function init(id) { return; } - await initVision(id); + return await initVision(id); } export { login, logout, startRealPlay, stopRealPlay } diff --git a/src/components/BootstrapBlazor.HikVision/wwwroot/hikvision.js b/src/components/BootstrapBlazor.HikVision/wwwroot/hikvision.js index 6581b125..feb26b8a 100644 --- a/src/components/BootstrapBlazor.HikVision/wwwroot/hikvision.js +++ b/src/components/BootstrapBlazor.HikVision/wwwroot/hikvision.js @@ -11,17 +11,20 @@ export async function init(id) { const el = document.getElementById(id); if (el === null) { - return; + return false; } const result = await initWindow(id); if (result.inited === false) { - return; + return false; } Data.set(id, { - iWndIndex: result.iWndIndex + iWndIndex: result.iWndIndex, + inited: true }); + + return true; } const initWindow = id => { @@ -59,6 +62,14 @@ const initWindow = id => { export async function login(id, ip, port, userName, password, loginType) { const vision = Data.get(id); + const { inited, logined } = vision; + if (inited !== true) { + return false; + } + if (logined === true) { + return true; + } + vision.szDeviceIdentify = `${ip}_${port}`; vision.logined = null; vision.loginErrorCode = null; @@ -152,7 +163,7 @@ const getChannelInfo = vision => { const handler = setInterval(() => { if (analog_completed && digital_completed && zero_completed) { clearInterval(handler) - resolve(vision); + resolve(); } }, 16); }); @@ -160,7 +171,11 @@ const getChannelInfo = vision => { export function logout(id) { const vision = Data.get(id); - const { szDeviceIdentify } = vision; + const { szDeviceIdentify, logined } = vision; + if (logined !== true) { + vision.logined = false; + return; + } let completed = null; WebVideoCtrl.I_Logout(szDeviceIdentify).then(() => { @@ -173,13 +188,13 @@ export function logout(id) { const handler = setInterval(() => { if (completed !== null) { clearInterval(handler) - resolve(vision); + resolve(); } }, 16); }); } -export async function startRealPlay(id) { +export async function startRealPlay(id, iStreamType, iChannelID) { const vision = Data.get(id); const { iWndIndex, szDeviceIdentify } = vision; @@ -188,26 +203,28 @@ export async function startRealPlay(id) { const oWndInfo = WebVideoCtrl.I_GetWindowStatus(iWndIndex); const iRtspPort = vision.devicePort.iRtspPort; - const iChannelID = 1; const bZeroChannel = false; - const iStreamType = 1; - + let completed = null; const startRealPlay = function () { WebVideoCtrl.I_StartRealPlay(szDeviceIdentify, { + iWndIndex: iWndIndex, iStreamType: iStreamType, iChannelID: iChannelID, bZeroChannel: bZeroChannel, iPort: iRtspPort, success: function () { - + vision.realPlaying = true; + completed = true; }, error: function (oError) { - + vision.realPlaying = false; + completed = false; } }); }; - if (oWndInfo != null) { + console.log(oWndInfo); + if (oWndInfo !== null) { WebVideoCtrl.I_Stop({ success: function () { startRealPlay(); @@ -217,6 +234,15 @@ export async function startRealPlay(id) { else { startRealPlay(); } + + return new Promise((resolve, reject) => { + const handler = setInterval(() => { + if (completed !== null) { + clearInterval(handler) + resolve(completed); + } + }, 16); + }); } export function stopRealPlay(id) { @@ -224,24 +250,43 @@ export function stopRealPlay(id) { const { iWndIndex, szDeviceIdentify } = vision; const oWndInfo = WebVideoCtrl.I_GetWindowStatus(iWndIndex); + let completed = null; + console.log(oWndInfo); if (oWndInfo !== null) { WebVideoCtrl.I_Stop({ success: function () { - + vision.realPlaying = false; + completed = true; }, error: function (oError) { - + completed = false; } }); } + + return new Promise((resolve, reject) => { + const handler = setInterval(() => { + if (completed !== null) { + clearInterval(handler) + resolve(completed); + } + }, 16); + }); } export function dispose(id) { - stopRealPlay(id); - logout(id); + const vision = Data.get(id); + Data.remove(id); + + const { realPlaying, logined } = vision; + if (realPlaying === true) { + stopRealPlay(id); + } + if (logined === true) { + logout(id); + } WebVideoCtrl.I_DestroyPlugin(); - Data.remove(id); } const getTagNameFirstValue = (xmlDoc, tagName) => { From c11f496871099a324da80d3f79e6885964f69408 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 5 Dec 2025 10:44:35 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20OnInitedAsync?= =?UTF-8?q?=20=E5=9B=9E=E8=B0=83=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/HikVisionWebPlugin.razor.cs | 61 +++++++++++++------ .../Components/HikVisionWebPlugin.razor.js | 5 +- .../wwwroot/hikvision.js | 38 +++++------- 3 files changed, 61 insertions(+), 43 deletions(-) diff --git a/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.cs b/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.cs index 0373f60d..40d87b33 100644 --- a/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.cs +++ b/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.cs @@ -9,7 +9,7 @@ namespace BootstrapBlazor.Components; /// /// 海康威视网络摄像机组件 (Websdk Plugin 插件版本) /// -[JSModuleAutoLoader("./_content/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.js")] +[JSModuleAutoLoader("./_content/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.js", JSObjectReference = true)] public partial class HikVisionWebPlugin { /// @@ -54,6 +54,12 @@ public partial class HikVisionWebPlugin [Parameter] public string? Height { get; set; } + /// + /// 获得/设置 插件初始化完成后回调方法 + /// + [Parameter] + public Func OnInitedAsync { get; set; } + private string? ClassString => CssBuilder.Default("bb-hik") .AddClassFromAttributes(AdditionalAttributes) .Build(); @@ -74,6 +80,11 @@ public partial class HikVisionWebPlugin /// public bool IsLogined { get; private set; } + /// + /// 获得 是否正在实时预览 + /// + public bool IsRealPlaying { get; private set; } + /// /// /// @@ -85,15 +96,6 @@ protected override void OnParametersSet() Height ??= "300px"; } - /// - /// - /// - /// - protected override async Task InvokeInitAsync() - { - Inited = await InvokeAsync("init", Id) ?? false; - } - /// /// 登录方法 /// @@ -103,10 +105,11 @@ protected override async Task InvokeInitAsync() /// /// /// - public async Task Login(string ip, int port, string userName, string password, LoginType loginType = LoginType.Http) + public async Task Login(string ip, int port, string userName, string password, LoginType loginType = LoginType.Http) { ThrowIfNotInited(); - IsLogined = await InvokeAsync("login", Id, ip, port, userName, password, (int)loginType); + IsLogined = await InvokeAsync("login", Id, ip, port, userName, password, (int)loginType) ?? false; + return IsLogined; } /// @@ -115,19 +118,22 @@ public async Task Login(string ip, int port, string userName, string password, L /// public async Task Logout() { + if (IsLogined) + { + await InvokeVoidAsync("logout", Id); + } IsLogined = false; - await InvokeVoidAsync("logout", Id); } /// /// 开始实时预览方法 /// /// - public async Task StartRealPlay() + public async Task StartRealPlay(int streamType, int channelId) { - if (IsLogined) + if (IsLogined && !IsRealPlaying) { - await InvokeVoidAsync("startRealPlay", Id); + IsRealPlaying = await InvokeAsync("startRealPlay", Id, streamType, channelId) ?? false; } } @@ -137,9 +143,13 @@ public async Task StartRealPlay() /// public async Task StopRealPlay() { - if (IsLogined) + if (IsLogined && IsRealPlaying) { - await InvokeVoidAsync("stopRealPlay", Id); + var result = await InvokeAsync("stopRealPlay", Id) ?? false; + if (result) + { + IsRealPlaying = false; + } } } @@ -150,4 +160,19 @@ private void ThrowIfNotInited() throw new InvalidOperationException("HikVision Web Plugin not inited"); } } + + /// + /// 触发 回调方法由 JavaScript 调用 + /// + /// + [JSInvokable] + public async Task TriggerInited(bool inited) + { + Inited = inited; + + if (OnInitedAsync != null) + { + await OnInitedAsync(inited); + } + } } diff --git a/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.js b/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.js index 4665c6d1..88a03384 100644 --- a/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.js +++ b/src/components/BootstrapBlazor.HikVision/Components/HikVisionWebPlugin.razor.js @@ -1,13 +1,14 @@ import { init as initVision, login, logout, startRealPlay, stopRealPlay, dispose as disposeVision } from '../hikvision.js'; import EventHandler from '../../BootstrapBlazor/modules/event-handler.js'; -export async function init(id) { +export async function init(id, invoke) { const el = document.getElementById(id); if (el === null) { return; } - return await initVision(id); + const inited = await initVision(id); + await invoke.invokeMethodAsync('TriggerInited', inited); } export { login, logout, startRealPlay, stopRealPlay } diff --git a/src/components/BootstrapBlazor.HikVision/wwwroot/hikvision.js b/src/components/BootstrapBlazor.HikVision/wwwroot/hikvision.js index feb26b8a..d95263af 100644 --- a/src/components/BootstrapBlazor.HikVision/wwwroot/hikvision.js +++ b/src/components/BootstrapBlazor.HikVision/wwwroot/hikvision.js @@ -33,7 +33,7 @@ const initWindow = id => { bWndFull: true, iWndowType: 1, cbSelWnd: function (xmlDoc) { - result.iWndIndex = getTagNameFirstValue(xmlDoc, "SelectWnd") + result.iWndIndex = parseInt(getTagNameFirstValue(xmlDoc, "SelectWnd")); }, cbDoubleClickWnd: function (iWndIndex, bFullScreen) { @@ -63,7 +63,7 @@ const initWindow = id => { export async function login(id, ip, port, userName, password, loginType) { const vision = Data.get(id); const { inited, logined } = vision; - if (inited !== true) { + if (inited !== true || ip.length === 0 || port <= 0 || userName.length === 0 || password.length === 0) { return false; } if (logined === true) { @@ -95,9 +95,9 @@ export async function login(id, ip, port, userName, password, loginType) { return new Promise((resolve, reject) => { const handler = setInterval(async () => { - if (vision.logined !== void 0) { + if (vision.logined !== null) { clearInterval(handler) - resolve(vision); + resolve(vision.logined); } }, 16); }); @@ -169,7 +169,7 @@ const getChannelInfo = vision => { }); } -export function logout(id) { +export async function logout(id) { const vision = Data.get(id); const { szDeviceIdentify, logined } = vision; if (logined !== true) { @@ -177,21 +177,10 @@ export function logout(id) { return; } - let completed = null; - WebVideoCtrl.I_Logout(szDeviceIdentify).then(() => { - completed = true; - }, () => { - completed = false; - }); + stopRealPlay(id); - return new Promise((resolve, reject) => { - const handler = setInterval(() => { - if (completed !== null) { - clearInterval(handler) - resolve(); - } - }, 16); - }); + await WebVideoCtrl.I_Logout(szDeviceIdentify); + vision.logined = false; } export async function startRealPlay(id, iStreamType, iChannelID) { @@ -247,11 +236,14 @@ export async function startRealPlay(id, iStreamType, iChannelID) { export function stopRealPlay(id) { const vision = Data.get(id); - const { iWndIndex, szDeviceIdentify } = vision; + const { iWndIndex, realPlaying } = vision; + + if (realPlaying !== true) { + return true; + } const oWndInfo = WebVideoCtrl.I_GetWindowStatus(iWndIndex); let completed = null; - console.log(oWndInfo); if (oWndInfo !== null) { WebVideoCtrl.I_Stop({ success: function () { @@ -289,12 +281,12 @@ export function dispose(id) { } -const getTagNameFirstValue = (xmlDoc, tagName) => { +const getTagNameFirstValue = (xmlDoc, tagName, defaultValue = '0') => { const tags = xmlDoc.getElementsByTagName(tagName); if (tags.length > 0) { return tags[0].textContent; } - return null; + return defaultValue; } const getTagNameValues = (xmlDoc, tagName) => {