Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions src/components/BootstrapBlazor.HikVision/Component1.razor

This file was deleted.

6 changes: 0 additions & 6 deletions src/components/BootstrapBlazor.HikVision/Component1.razor.css

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,20 +1,5 @@
@namespace BootstrapBlazor.Components
@inherits BootstrapModuleComponentBase

<div @attributes="AdditionalAttributes" class="@ClassString" id="@Id">
<div id="@PreviewId" class="bb-hik-preview" style="width: 500px; height: 300px;"></div>
<div class="bb-hik-controls">
<button class="btn btn-primary bb-hik-login">
<span>登录</span>
</button>
<button class="btn btn-primary bb-hik-logout">
<span>退出</span>
</button>
<button class="btn btn-primary bb-hik-start">
<span>开始预览</span>
</button>
<button class="btn btn-primary bb-hik-stop">
<span>停止预览</span>
</button>
</div>
<div @attributes="AdditionalAttributes" class="@ClassString" id="@Id" style="@StyleString">
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

using Microsoft.AspNetCore.Components;

namespace BootstrapBlazor.Components;

/// <summary>
Expand All @@ -10,9 +12,89 @@ namespace BootstrapBlazor.Components;
[JSModuleAutoLoader("./_content/BootstrapBlazor.HikVision/Components/HikVision.razor.js")]
public partial class HikVision
{
private string PreviewId => $"{Id}_preview";
/// <summary>
/// 获得/设置 网络摄像机 IP 地址
/// </summary>
[Parameter]
public string? Ip { get; set; }

/// <summary>
/// 获得/设置 网络摄像机 端口号 默认值 80
/// </summary>
[Parameter]
public int Port { get; set; } = 80;

/// <summary>
Comment on lines +18 to +27
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The LoginType property is documented as defaulting to LoginType.Http but no default value is assigned.

Right now the property just uses the enum’s default (zero) value. If LoginType.Http is ever changed to a non-zero value, the actual default will no longer match the documentation. To avoid this, initialize explicitly, e.g. public LoginType LoginType { get; set; } = LoginType.Http;.

/// 获得/设置 网络摄像机 登录用户名
/// </summary>
[Parameter]
public string? UserName { get; set; }

/// <summary>
/// 获得/设置 网络摄像机 登录密码
/// </summary>
[Parameter]
public string? Password { get; set; }

/// <summary>
/// 获得/设置 网络摄像机 登录类型 默认值 <see cref="LoginType.Http"/>
/// </summary>
[Parameter]
public LoginType LoginType { get; set; }
Comment on lines +15 to +43
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The component parameters Ip, Port, UserName, Password, and LoginType (lines 15-43) are defined but never used within the component. These parameters appear to be intended for configuration but there's no logic to automatically login with these credentials. Consider either:

  1. Removing these unused parameters if they're not needed
  2. Adding an auto-login feature in OnAfterRenderAsync if auto-login is the intended behavior
  3. Documenting that consumers should use the Login method explicitly with these parameters

Copilot uses AI. Check for mistakes.

/// <summary>
/// 获得/设置 视频图像窗口宽度 默认值 500px
/// </summary>
[Parameter]
public string? Width { get; set; }

/// <summary>
/// 获得/设置 视频图像窗口高度 默认值 300px
/// </summary>
[Parameter]
public string? Height { get; set; }

private string? ClassString => CssBuilder.Default("bb-hik")
.AddClassFromAttributes(AdditionalAttributes)
.Build();

private string? StyleString => CssBuilder.Default()
.AddClass($"width: {Width};", !string.IsNullOrEmpty(Width))
.AddClass($"height: {Height};", !string.IsNullOrEmpty(Height))
Comment on lines +62 to +63
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AddClass method should not be used to add CSS style properties. Based on the pattern used in other components in this codebase (e.g., AntDesignIcon, ElementIcon), AddClass is only appropriate for CSS custom properties (like --variable-name). For regular CSS properties like width and height, you should build the style string directly without using AddClass. Consider:

private string? StyleString => CssBuilder.Default()
    .AddClass($"width: {Width}; height: {Height};", !string.IsNullOrEmpty(Width) || !string.IsNullOrEmpty(Height))
    .AddStyleFromAttributes(AdditionalAttributes)
    .Build();

Or more simply, construct the style string without CssBuilder for these properties.

Suggested change
.AddClass($"width: {Width};", !string.IsNullOrEmpty(Width))
.AddClass($"height: {Height};", !string.IsNullOrEmpty(Height))
.AddStyle(!string.IsNullOrEmpty(Width) ? $"width: {Width};" : null)
.AddStyle(!string.IsNullOrEmpty(Height) ? $"height: {Height};" : null)

Copilot uses AI. Check for mistakes.
.AddStyleFromAttributes(AdditionalAttributes)
.Build();

/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnParametersSet()
{
base.OnParametersSet();

Width ??= "500px";
Height ??= "300px";
}

/// <summary>
/// 登录方法
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <param name="loginType"></param>
Comment on lines +81 to +85
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter documentation is empty. Consider providing meaningful descriptions for each parameter, for example:

/// <param name="ip">网络摄像机 IP 地址</param>
/// <param name="port">网络摄像机端口号</param>
/// <param name="userName">登录用户名</param>
/// <param name="password">登录密码</param>
/// <param name="loginType">登录类型</param>
Suggested change
/// <param name="ip"></param>
/// <param name="port"></param>
/// <param name="userName"></param>
/// <param name="password"></param>
/// <param name="loginType"></param>
/// <param name="ip">网络摄像机 IP 地址</param>
/// <param name="port">网络摄像机端口号</param>
/// <param name="userName">登录用户名</param>
/// <param name="password">登录密码</param>
/// <param name="loginType">登录类型</param>

Copilot uses AI. Check for mistakes.
/// <returns></returns>
public async Task Login(string ip, string port, string userName, string password, LoginType loginType = LoginType.Http)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The port parameter is a string while the component-level Port parameter is an int, leading to an inconsistent API surface.

Since Port is an int on the component, consider making this parameter int port as well (or adding an overload that omits port and uses the Port property), unless the JS interop layer strictly requires a string. This avoids confusion and extra conversions for callers.

Suggested implementation:

    /// <summary>
    /// 登录方法
    /// </summary>
    /// <param name="ip">设备 IP 地址</param>
    /// <param name="port">设备端口号</param>
    /// <param name="userName">用户名</param>
    /// <param name="password">密码</param>
    /// <param name="loginType">登录类型</param>
    /// <returns></returns>
    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);
    }

    /// <summary>
    /// 使用组件级 Port 属性的登录方法
    /// </summary>
    /// <param name="ip">设备 IP 地址</param>
    /// <param name="userName">用户名</param>
    /// <param name="password">密码</param>
    /// <param name="loginType">登录类型</param>
    /// <returns></returns>
    public async Task Login(string ip, string userName, string password, LoginType loginType = LoginType.Http)
    {
        await Login(ip, Port, userName, password, loginType);
    }

    /// <summary>
    /// 登出方法
    /// </summary>
    /// <returns></returns>
    public async Task Logout()
    {
        await InvokeVoidAsync("logout", Id);
    }
  1. Ensure the component declares an int Port { get; set; } (or similar) property; the new overload depends on it.
  2. If the JS login function actually requires port as a string, adjust the call to InvokeVoidAsync to pass port.ToString(CultureInfo.InvariantCulture) and add using System.Globalization; at the top of the file.

Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The port parameter type should be int instead of string to match:

  1. The component's Port property which is int (line 25)
  2. The interface IHikVision.Login which expects int port
  3. The service implementation DefaultHicVision.Login which expects int port

This inconsistency will cause type conversion issues when calling this method.

Suggested change
public async Task Login(string ip, string 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)

Copilot uses AI. Check for mistakes.
{
await InvokeVoidAsync("login", Id, ip, port, userName, password, (int)loginType);
}

/// <summary>
/// 登出方法
/// </summary>
/// <returns></returns>
public async Task Logout()
{
await InvokeVoidAsync("logout", Id);
}
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JavaScript module exports startRealPlay and stopRealPlay functions (line 13 of HikVision.razor.js), but this component doesn't provide corresponding public C# methods to invoke them. Consider adding:

/// <summary>
/// 开始实时预览画面方法
/// </summary>
/// <returns></returns>
public async Task StartRealPlay()
{
    await InvokeVoidAsync("startRealPlay", Id);
}

/// <summary>
/// 停止实时预览画面方法
/// </summary>
/// <returns></returns>
public async Task StopRealPlay()
{
    await InvokeVoidAsync("stopRealPlay", Id);
}

This would provide a complete API for consumers of the component.

Suggested change
}
}
/// <summary>
/// 开始实时预览画面方法
/// </summary>
/// <returns></returns>
public async Task StartRealPlay()
{
await InvokeVoidAsync("startRealPlay", Id);
}
/// <summary>
/// 停止实时预览画面方法
/// </summary>
/// <returns></returns>
public async Task StopRealPlay()
{
await InvokeVoidAsync("stopRealPlay", Id);
}

Copilot uses AI. Check for mistakes.
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,11 @@ export async function init(id) {
return;
}

const previewId = `${id}_preview`;
await initVision(previewId);

const controls = el.querySelector('.bb-hik-controls');
if (controls) {
EventHandler.on(controls, 'click', '.bb-hik-login', async e => {
console.log('login');
await login(previewId, '47.121.113.151', 9980, 'admin', 'vhbn8888', 1)
});
EventHandler.on(controls, 'click', '.bb-hik-logout', e => {
console.log('logout');
logout(previewId);
});
EventHandler.on(controls, 'click', '.bb-hik-start', e => {
console.log('start');
startRealPlay(previewId);
});
EventHandler.on(controls, 'click', '.bb-hik-stop', e => {
console.log('stop');
stopRealPlay(previewId);
});
}
await initVision(id);
}

export function dispose(id) {
const el = document.getElementById(id);
if (el !== null) {
const controls = el.querySelector('.bb-hik-controls');
if (controls) {
EventHandler.off(controls, 'click');
}
}
export { login, logout, startRealPlay, stopRealPlay }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Exporting login/logout/startRealPlay/stopRealPlay without local bindings will cause a module parse/runtime error.

In ES modules, named exports like export { login } require login (and the others) to exist as local bindings (declared or imported) in this file. If these functions only exist as globals from another script, this export will fail. Either import them into this module or drop the named export and keep using the globals directly.


const previewId = `${id}_preview`;
disposeVision(previewId);
export function dispose(id) {
disposeVision(id);
}
Loading