mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-11-04 17:43:58 +08:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					f7f8802272 | ||
| 
						 | 
					c6910dff02 | ||
| 
						 | 
					ad299d0dbb | ||
| 
						 | 
					8b124d1050 | ||
| 
						 | 
					ff41080dbd | 
@@ -117,6 +117,25 @@ public class SugarAopService : ISugarAopService
 | 
			
		||||
        db.Aop.DataExecuted = (value, entity) =>
 | 
			
		||||
        {
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        db.Aop.OnLogExecuted = (sql, pars) =>
 | 
			
		||||
        {
 | 
			
		||||
            //执行时间超过1秒
 | 
			
		||||
            if (db.Ado.SqlExecutionTime.TotalSeconds > 1)
 | 
			
		||||
            {
 | 
			
		||||
                //代码CS文件名
 | 
			
		||||
                var fileName = db.Ado.SqlStackTrace.FirstFileName;
 | 
			
		||||
                //代码行数
 | 
			
		||||
                var fileLine = db.Ado.SqlStackTrace.FirstLine;
 | 
			
		||||
                //方法名
 | 
			
		||||
                var FirstMethodName = db.Ado.SqlStackTrace.FirstMethodName;
 | 
			
		||||
 | 
			
		||||
                DbContext.WriteLog($"{fileName}-{FirstMethodName}-{fileLine} 执行时间超过1秒");
 | 
			
		||||
                DbContext.WriteLogWithSql(UtilMethods.GetNativeSql(sql, pars));
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ public sealed class SqlSugarOption : ConnectionConfig
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 是否控制台显示Sql语句
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool IsShowSql { get; set; }
 | 
			
		||||
    public bool? IsShowSql { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 更新数据
 | 
			
		||||
 
 | 
			
		||||
@@ -98,6 +98,21 @@ public class Startup : AppStartup
 | 
			
		||||
        CodeFirstUtils.CodeFirst(fullName!);//CodeFirst
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            using var db = DbContext.GetDB<SysOperateLog>();
 | 
			
		||||
            if (db.CurrentConnectionConfig.DbType == SqlSugar.DbType.Sqlite)
 | 
			
		||||
            {
 | 
			
		||||
                if (!db.DbMaintenance.IsAnyIndex("idx_operatelog_optime_date"))
 | 
			
		||||
                {
 | 
			
		||||
                    var indexsql = "CREATE INDEX idx_operatelog_optime_date ON sys_operatelog(strftime('%Y-%m-%d', OpTime));";
 | 
			
		||||
                    db.Ado.ExecuteCommand(indexsql);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //删除在线用户统计
 | 
			
		||||
        var verificatInfoService = App.RootServices.GetService<IVerificatInfoService>();
 | 
			
		||||
        verificatInfoService.RemoveAllClientId();
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ public static class ILoggerExtensions
 | 
			
		||||
    /// <param name="logger"></param>
 | 
			
		||||
    /// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static IDisposable ScopeContext(this ILogger logger, IDictionary<object, object> properties)
 | 
			
		||||
    public static IDisposable ScopeContext(this ILogger logger, IDictionary<string, object> properties)
 | 
			
		||||
    {
 | 
			
		||||
        if (logger == null) throw new ArgumentNullException(nameof(logger));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,11 +26,11 @@ public static class LogContextExtensions
 | 
			
		||||
    /// <param name="key">键</param>
 | 
			
		||||
    /// <param name="value">值</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static LogContext Set(this LogContext logContext, object key, object value)
 | 
			
		||||
    public static LogContext Set(this LogContext logContext, string key, object value)
 | 
			
		||||
    {
 | 
			
		||||
        if (logContext == null || key == null) return logContext;
 | 
			
		||||
 | 
			
		||||
        logContext.Properties ??= new Dictionary<object, object>();
 | 
			
		||||
        logContext.Properties ??= new Dictionary<string, object>();
 | 
			
		||||
 | 
			
		||||
        logContext.Properties.Remove(key);
 | 
			
		||||
        logContext.Properties.Add(key, value);
 | 
			
		||||
@@ -43,7 +43,7 @@ public static class LogContextExtensions
 | 
			
		||||
    /// <param name="logContext"></param>
 | 
			
		||||
    /// <param name="properties"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static LogContext SetRange(this LogContext logContext, IDictionary<object, object> properties)
 | 
			
		||||
    public static LogContext SetRange(this LogContext logContext, IDictionary<string, object> properties)
 | 
			
		||||
    {
 | 
			
		||||
        if (logContext == null
 | 
			
		||||
            || properties == null
 | 
			
		||||
@@ -63,7 +63,7 @@ public static class LogContextExtensions
 | 
			
		||||
    /// <param name="logContext"></param>
 | 
			
		||||
    /// <param name="key">键</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static object Get(this LogContext logContext, object key)
 | 
			
		||||
    public static object Get(this LogContext logContext, string key)
 | 
			
		||||
    {
 | 
			
		||||
        if (logContext == null
 | 
			
		||||
            || key == null
 | 
			
		||||
@@ -80,7 +80,7 @@ public static class LogContextExtensions
 | 
			
		||||
    /// <param name="logContext"></param>
 | 
			
		||||
    /// <param name="key">键</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static T Get<T>(this LogContext logContext, object key)
 | 
			
		||||
    public static T Get<T>(this LogContext logContext, string key)
 | 
			
		||||
    {
 | 
			
		||||
        var value = logContext.Get(key);
 | 
			
		||||
        return value.ChangeType<T>();
 | 
			
		||||
 
 | 
			
		||||
@@ -84,7 +84,7 @@ public static class StringLoggingExtensions
 | 
			
		||||
    /// <param name="message"></param>
 | 
			
		||||
    /// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static StringLoggingPart ScopeContext(this string message, IDictionary<object, object> properties)
 | 
			
		||||
    public static StringLoggingPart ScopeContext(this string message, IDictionary<string, object> properties)
 | 
			
		||||
    {
 | 
			
		||||
        return StringLoggingPart.Default().SetMessage(message).ScopeContext(properties);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ public sealed class LogContext : IDisposable
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 日志上下文数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public IDictionary<object, object> Properties { get; set; }
 | 
			
		||||
    public IDictionary<string, object> Properties { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 原生日志上下文数据
 | 
			
		||||
 
 | 
			
		||||
@@ -96,7 +96,7 @@ public sealed partial class StringLoggingPart
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public StringLoggingPart ScopeContext(IDictionary<object, object> properties)
 | 
			
		||||
    public StringLoggingPart ScopeContext(IDictionary<string, object> properties)
 | 
			
		||||
    {
 | 
			
		||||
        if (properties == null) return this;
 | 
			
		||||
        LogContext = new LogContext { Properties = properties };
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ public static class Log
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<object, object> properties)
 | 
			
		||||
    public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<string, object> properties)
 | 
			
		||||
    {
 | 
			
		||||
        return GetLogger(StringLoggingPart.Default().ScopeContext(properties));
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
//下载文件
 | 
			
		||||
export function blazor_downloadFile(url, fileName, dtoObject) {
 | 
			
		||||
export async function blazor_downloadFile(url, fileName, dtoObject) {
 | 
			
		||||
    const params = new URLSearchParams();
 | 
			
		||||
 | 
			
		||||
    // 将 dtoObject 的属性添加到 URLSearchParams 中
 | 
			
		||||
@@ -12,97 +12,92 @@ export function blazor_downloadFile(url, fileName, dtoObject) {
 | 
			
		||||
    // 构建完整的 URL
 | 
			
		||||
    const fullUrl = `${url}?${params.toString()}`;
 | 
			
		||||
 | 
			
		||||
    // 发起 fetch 请求
 | 
			
		||||
    fetch(fullUrl)
 | 
			
		||||
        .then(response => {
 | 
			
		||||
            // 获取响应头中的 content-disposition
 | 
			
		||||
            const dispositionHeader = response.headers.get('content-disposition');
 | 
			
		||||
            let resolvedFileName = fileName;
 | 
			
		||||
    try {
 | 
			
		||||
        // 发起 fetch 请求
 | 
			
		||||
        const response = await fetch(fullUrl);
 | 
			
		||||
 | 
			
		||||
            if (dispositionHeader) {
 | 
			
		||||
                // 解析出文件名
 | 
			
		||||
                const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(dispositionHeader);
 | 
			
		||||
                const serverFileName = match && match[1] ? match[1].replace(/['"]/g, '') : null;
 | 
			
		||||
                if (serverFileName) {
 | 
			
		||||
                    resolvedFileName = serverFileName;
 | 
			
		||||
                }
 | 
			
		||||
        // 获取响应头中的 content-disposition
 | 
			
		||||
        const dispositionHeader = response.headers.get('content-disposition');
 | 
			
		||||
        let resolvedFileName = fileName;
 | 
			
		||||
 | 
			
		||||
        if (dispositionHeader) {
 | 
			
		||||
            // 解析出文件名
 | 
			
		||||
            const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(dispositionHeader);
 | 
			
		||||
            const serverFileName = match && match[1] ? match[1].replace(/['"]/g, '') : null;
 | 
			
		||||
            if (serverFileName) {
 | 
			
		||||
                resolvedFileName = serverFileName;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // 将响应转换为 blob 对象
 | 
			
		||||
            return response.blob().then(blob => {
 | 
			
		||||
                // 创建临时的文件 URL
 | 
			
		||||
                const fileUrl = window.URL.createObjectURL(blob);
 | 
			
		||||
        // 将响应转换为 blob 对象
 | 
			
		||||
        const blob = await response.blob();
 | 
			
		||||
 | 
			
		||||
                // 创建一个 <a> 元素并设置下载链接和文件名
 | 
			
		||||
                const anchorElement = document.createElement('a');
 | 
			
		||||
                anchorElement.href = fileUrl;
 | 
			
		||||
                anchorElement.download = resolvedFileName;
 | 
			
		||||
                anchorElement.style.display = 'none';
 | 
			
		||||
        // 创建临时的文件 URL
 | 
			
		||||
        const fileUrl = window.URL.createObjectURL(blob);
 | 
			
		||||
 | 
			
		||||
                // 将 <a> 元素添加到 body 中并触发下载
 | 
			
		||||
                document.body.appendChild(anchorElement);
 | 
			
		||||
                anchorElement.click();
 | 
			
		||||
                document.body.removeChild(anchorElement);
 | 
			
		||||
        // 创建一个 <a> 元素并设置下载链接和文件名
 | 
			
		||||
        const anchorElement = document.createElement('a');
 | 
			
		||||
        anchorElement.href = fileUrl;
 | 
			
		||||
        anchorElement.download = resolvedFileName;
 | 
			
		||||
        anchorElement.style.display = 'none';
 | 
			
		||||
 | 
			
		||||
                // 撤销临时的文件 URL
 | 
			
		||||
                window.URL.revokeObjectURL(fileUrl);
 | 
			
		||||
            });
 | 
			
		||||
        })
 | 
			
		||||
        .catch(error => {
 | 
			
		||||
            console.error('DownFile error ', error);
 | 
			
		||||
        });
 | 
			
		||||
        // 将 <a> 元素添加到 body 中并触发下载
 | 
			
		||||
        document.body.appendChild(anchorElement);
 | 
			
		||||
        anchorElement.click();
 | 
			
		||||
        document.body.removeChild(anchorElement);
 | 
			
		||||
 | 
			
		||||
        // 撤销临时的文件 URL
 | 
			
		||||
        window.URL.revokeObjectURL(fileUrl);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        console.error('DownFile error ', error);
 | 
			
		||||
        throw error;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//下载文件
 | 
			
		||||
export function postJson_downloadFile(url, fileName, jsonBody) {
 | 
			
		||||
    const params = new URLSearchParams();
 | 
			
		||||
export async function postJson_downloadFile(url, fileName, jsonBody) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // 发起 fetch 请求
 | 
			
		||||
    fetch(url, {
 | 
			
		||||
        method: 'POST',
 | 
			
		||||
        headers: {
 | 
			
		||||
            'Content-Type': 'application/json'
 | 
			
		||||
        },
 | 
			
		||||
        body: jsonBody
 | 
			
		||||
    })
 | 
			
		||||
        .then(response => {
 | 
			
		||||
            // 获取响应头中的 content-disposition
 | 
			
		||||
            const dispositionHeader = response.headers.get('content-disposition');
 | 
			
		||||
            let resolvedFileName = fileName;
 | 
			
		||||
 | 
			
		||||
            if (dispositionHeader) {
 | 
			
		||||
                // 解析出文件名
 | 
			
		||||
                const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(dispositionHeader);
 | 
			
		||||
                const serverFileName = match && match[1] ? match[1].replace(/['"]/g, '') : null;
 | 
			
		||||
                if (serverFileName) {
 | 
			
		||||
                    resolvedFileName = serverFileName;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 将响应转换为 blob 对象
 | 
			
		||||
            return response.blob().then(blob => {
 | 
			
		||||
                // 创建临时的文件 URL
 | 
			
		||||
                const fileUrl = window.URL.createObjectURL(blob);
 | 
			
		||||
 | 
			
		||||
                // 创建一个 <a> 元素并设置下载链接和文件名
 | 
			
		||||
                const anchorElement = document.createElement('a');
 | 
			
		||||
                anchorElement.href = fileUrl;
 | 
			
		||||
                anchorElement.download = resolvedFileName;
 | 
			
		||||
                anchorElement.style.display = 'none';
 | 
			
		||||
 | 
			
		||||
                // 将 <a> 元素添加到 body 中并触发下载
 | 
			
		||||
                document.body.appendChild(anchorElement);
 | 
			
		||||
                anchorElement.click();
 | 
			
		||||
                document.body.removeChild(anchorElement);
 | 
			
		||||
 | 
			
		||||
                // 撤销临时的文件 URL
 | 
			
		||||
                window.URL.revokeObjectURL(fileUrl);
 | 
			
		||||
            });
 | 
			
		||||
        })
 | 
			
		||||
        .catch(error => {
 | 
			
		||||
            console.error('downfile error ', error);
 | 
			
		||||
    try {
 | 
			
		||||
        const response = await fetch(url, {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            headers: {
 | 
			
		||||
                'Content-Type': 'application/json'
 | 
			
		||||
            },
 | 
			
		||||
            body: jsonBody
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const dispositionHeader = response.headers.get('content-disposition');
 | 
			
		||||
        let resolvedFileName = fileName;
 | 
			
		||||
 | 
			
		||||
        if (dispositionHeader) {
 | 
			
		||||
            const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(dispositionHeader);
 | 
			
		||||
            const serverFileName = match && match[1] ? match[1].replace(/['"]/g, '') : null;
 | 
			
		||||
            if (serverFileName) {
 | 
			
		||||
                resolvedFileName = serverFileName;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const blob = await response.blob();
 | 
			
		||||
        const fileUrl = window.URL.createObjectURL(blob);
 | 
			
		||||
 | 
			
		||||
        const anchorElement = document.createElement('a');
 | 
			
		||||
        anchorElement.href = fileUrl;
 | 
			
		||||
        anchorElement.download = resolvedFileName;
 | 
			
		||||
        anchorElement.style.display = 'none';
 | 
			
		||||
 | 
			
		||||
        document.body.appendChild(anchorElement);
 | 
			
		||||
        anchorElement.click();
 | 
			
		||||
        document.body.removeChild(anchorElement);
 | 
			
		||||
 | 
			
		||||
        window.URL.revokeObjectURL(fileUrl);
 | 
			
		||||
 | 
			
		||||
        return true; // 唯一新增的返回值
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        console.error('downfile error ', error);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
<Project>
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<PluginVersion>10.6.23</PluginVersion>
 | 
			
		||||
		<ProPluginVersion>10.6.23</ProPluginVersion>
 | 
			
		||||
		<AuthenticationVersion>2.1.7</AuthenticationVersion>
 | 
			
		||||
		<PluginVersion>10.6.27</PluginVersion>
 | 
			
		||||
		<ProPluginVersion>10.6.27</ProPluginVersion>
 | 
			
		||||
		<AuthenticationVersion>2.1.8</AuthenticationVersion>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
 
 | 
			
		||||
@@ -26,28 +26,53 @@ internal sealed class GatewayExportService : IGatewayExportService
 | 
			
		||||
 | 
			
		||||
    private IJSRuntime JSRuntime { get; set; }
 | 
			
		||||
 | 
			
		||||
    public async Task OnChannelExport(ExportFilter exportFilter)
 | 
			
		||||
    public async Task<bool> OnChannelExport(ExportFilter exportFilter)
 | 
			
		||||
    {
 | 
			
		||||
        await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js");
 | 
			
		||||
        string url = "api/gatewayExport/channel";
 | 
			
		||||
        string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx";
 | 
			
		||||
        await ajaxJS.InvokeVoidAsync("postJson_downloadFile", url, fileName, exportFilter.ToJsonString());
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js");
 | 
			
		||||
            string url = "api/gatewayExport/channel";
 | 
			
		||||
            string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx";
 | 
			
		||||
            return await ajaxJS.InvokeAsync<bool>("postJson_downloadFile", url, fileName, exportFilter.ToJsonString());
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task OnDeviceExport(ExportFilter exportFilter)
 | 
			
		||||
    public async Task<bool> OnDeviceExport(ExportFilter exportFilter)
 | 
			
		||||
    {
 | 
			
		||||
        await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js");
 | 
			
		||||
        string url = "api/gatewayExport/device";
 | 
			
		||||
        string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx";
 | 
			
		||||
        await ajaxJS.InvokeVoidAsync("postJson_downloadFile", url, fileName, exportFilter.ToJsonString());
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js");
 | 
			
		||||
            string url = "api/gatewayExport/device";
 | 
			
		||||
            string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx";
 | 
			
		||||
            return await ajaxJS.InvokeAsync<bool>("postJson_downloadFile", url, fileName, exportFilter.ToJsonString());
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task OnVariableExport(ExportFilter exportFilter)
 | 
			
		||||
    public async Task<bool> OnVariableExport(ExportFilter exportFilter)
 | 
			
		||||
    {
 | 
			
		||||
        await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js");
 | 
			
		||||
        string url = "api/gatewayExport/variable";
 | 
			
		||||
        string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx";
 | 
			
		||||
        await ajaxJS.InvokeVoidAsync("postJson_downloadFile", url, fileName, exportFilter.ToJsonString());
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js");
 | 
			
		||||
            string url = "api/gatewayExport/variable";
 | 
			
		||||
            string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx";
 | 
			
		||||
            return await ajaxJS.InvokeAsync<bool>("postJson_downloadFile", url, fileName, exportFilter.ToJsonString());
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,18 +35,30 @@ public sealed class HybridGatewayExportService : IGatewayExportService
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task OnChannelExport(ExportFilter exportFilter)
 | 
			
		||||
    public async Task<bool> OnChannelExport(ExportFilter exportFilter)
 | 
			
		||||
    {
 | 
			
		||||
        exportFilter.QueryPageOptions.IsPage = false;
 | 
			
		||||
        exportFilter.QueryPageOptions.IsVirtualScroll = false;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        var sheets = await _channelService.ExportChannelAsync(exportFilter).ConfigureAwait(false);
 | 
			
		||||
        var path = await _importExportService.CreateFileAsync<Device>(sheets, "Channel", false).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
        Open(path);
 | 
			
		||||
            exportFilter.QueryPageOptions.IsPage = false;
 | 
			
		||||
            exportFilter.QueryPageOptions.IsVirtualScroll = false;
 | 
			
		||||
 | 
			
		||||
            var sheets = await _channelService.ExportChannelAsync(exportFilter).ConfigureAwait(false);
 | 
			
		||||
            var path = await _importExportService.CreateFileAsync<Device>(sheets, "Channel", false).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
            Open(path);
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void Open(string path)
 | 
			
		||||
    private static bool Open(string path)
 | 
			
		||||
    {
 | 
			
		||||
        path = System.IO.Path.GetDirectoryName(path); // Ensure the path is absolute
 | 
			
		||||
 | 
			
		||||
@@ -63,25 +75,47 @@ public sealed class HybridGatewayExportService : IGatewayExportService
 | 
			
		||||
        {
 | 
			
		||||
            System.Diagnostics.Process.Start("open", path);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task OnDeviceExport(ExportFilter exportFilter)
 | 
			
		||||
    public async Task<bool> OnDeviceExport(ExportFilter exportFilter)
 | 
			
		||||
    {
 | 
			
		||||
        exportFilter.QueryPageOptions.IsPage = false;
 | 
			
		||||
        exportFilter.QueryPageOptions.IsVirtualScroll = false;
 | 
			
		||||
        var sheets = await _deviceService.ExportDeviceAsync(exportFilter).ConfigureAwait(false);
 | 
			
		||||
        var path = await _importExportService.CreateFileAsync<Device>(sheets, "Device", false).ConfigureAwait(false);
 | 
			
		||||
        Open(path);
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            exportFilter.QueryPageOptions.IsPage = false;
 | 
			
		||||
            exportFilter.QueryPageOptions.IsVirtualScroll = false;
 | 
			
		||||
            var sheets = await _deviceService.ExportDeviceAsync(exportFilter).ConfigureAwait(false);
 | 
			
		||||
            var path = await _importExportService.CreateFileAsync<Device>(sheets, "Device", false).ConfigureAwait(false);
 | 
			
		||||
            Open(path);
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task OnVariableExport(ExportFilter exportFilter)
 | 
			
		||||
    public async Task<bool> OnVariableExport(ExportFilter exportFilter)
 | 
			
		||||
    {
 | 
			
		||||
        exportFilter.QueryPageOptions.IsPage = false;
 | 
			
		||||
        exportFilter.QueryPageOptions.IsVirtualScroll = false;
 | 
			
		||||
        var sheets = await _variableService.ExportVariableAsync(exportFilter).ConfigureAwait(false);
 | 
			
		||||
        var path = await _importExportService.CreateFileAsync<Variable>(sheets, "Variable", false).ConfigureAwait(false);
 | 
			
		||||
        Open(path);
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            exportFilter.QueryPageOptions.IsPage = false;
 | 
			
		||||
            exportFilter.QueryPageOptions.IsVirtualScroll = false;
 | 
			
		||||
            var sheets = await _variableService.ExportVariableAsync(exportFilter).ConfigureAwait(false);
 | 
			
		||||
            var path = await _importExportService.CreateFileAsync<Variable>(sheets, "Variable", false).ConfigureAwait(false);
 | 
			
		||||
            Open(path);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ namespace ThingsGateway.Gateway.Application;
 | 
			
		||||
 | 
			
		||||
public interface IGatewayExportService
 | 
			
		||||
{
 | 
			
		||||
    Task OnChannelExport(ExportFilter exportFilter);
 | 
			
		||||
    Task OnDeviceExport(ExportFilter exportFilter);
 | 
			
		||||
    Task OnVariableExport(ExportFilter exportFilter);
 | 
			
		||||
    Task<bool> OnChannelExport(ExportFilter exportFilter);
 | 
			
		||||
    Task<bool> OnDeviceExport(ExportFilter exportFilter);
 | 
			
		||||
    Task<bool> OnVariableExport(ExportFilter exportFilter);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,8 @@ using SqlSugar;
 | 
			
		||||
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Authentication;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Gateway.Application;
 | 
			
		||||
 | 
			
		||||
[AppStartup(-100)]
 | 
			
		||||
@@ -25,6 +27,9 @@ public class Startup : AppStartup
 | 
			
		||||
{
 | 
			
		||||
    public void Configure(IServiceCollection services)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        ProAuthentication.TryGetAuthorizeInfo(out var authorizeInfo);
 | 
			
		||||
 | 
			
		||||
        services.AddConfigurableOptions<ChannelThreadOptions>();
 | 
			
		||||
        services.AddConfigurableOptions<GatewayLogOptions>();
 | 
			
		||||
        services.AddConfigurableOptions<RpcLogOptions>();
 | 
			
		||||
@@ -125,8 +130,35 @@ public class Startup : AppStartup
 | 
			
		||||
        }
 | 
			
		||||
        catch { }
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            using var db = DbContext.GetDB<BackendLog>();
 | 
			
		||||
            if (db.CurrentConnectionConfig.DbType == SqlSugar.DbType.Sqlite)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (!db.DbMaintenance.IsAnyIndex("idx_backendlog_logtime_date"))
 | 
			
		||||
                {
 | 
			
		||||
                    var indexsql = "CREATE INDEX idx_backendlog_logtime_date ON backend_log(strftime('%Y-%m-%d', LogTime));";
 | 
			
		||||
                    db.Ado.ExecuteCommand(indexsql);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch { }
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            using var db = DbContext.GetDB<RpcLog>();
 | 
			
		||||
            if (db.CurrentConnectionConfig.DbType == SqlSugar.DbType.Sqlite)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (!db.DbMaintenance.IsAnyIndex("idx_rpclog_logtime_date"))
 | 
			
		||||
                {
 | 
			
		||||
                    var indexsql = "CREATE INDEX idx_rpclog_logtime_date ON rpc_log(strftime('%Y-%m-%d', LogTime));";
 | 
			
		||||
                    db.Ado.ExecuteCommand(indexsql);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch { }
 | 
			
		||||
 | 
			
		||||
        serviceProvider.GetService<IHostApplicationLifetime>().ApplicationStarted.Register(() =>
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
	<Import Project="$(SolutionDir)Version.props" />
 | 
			
		||||
	<Import Project="$(SolutionDir)PackNuget.props" />
 | 
			
		||||
 
 | 
			
		||||
@@ -220,9 +220,10 @@ public partial class ChannelTable : IDisposable
 | 
			
		||||
 | 
			
		||||
    private async Task ExcelExportAsync(ITableExportContext<ChannelRuntime> tableExportContext, bool all = false)
 | 
			
		||||
    {
 | 
			
		||||
        bool ret;
 | 
			
		||||
        if (all)
 | 
			
		||||
        {
 | 
			
		||||
            await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
 | 
			
		||||
            ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
@@ -230,16 +231,16 @@ public partial class ChannelTable : IDisposable
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                case ChannelDevicePluginTypeEnum.PluginName:
 | 
			
		||||
                    await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName });
 | 
			
		||||
                    ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName });
 | 
			
		||||
                    break;
 | 
			
		||||
                case ChannelDevicePluginTypeEnum.Channel:
 | 
			
		||||
                    await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id });
 | 
			
		||||
                    ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id });
 | 
			
		||||
                    break;
 | 
			
		||||
                case ChannelDevicePluginTypeEnum.Device:
 | 
			
		||||
                    await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType });
 | 
			
		||||
                    ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType });
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
 | 
			
		||||
                    ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -247,7 +248,8 @@ public partial class ChannelTable : IDisposable
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 返回 true 时自动弹出提示框
 | 
			
		||||
        await ToastService.Default();
 | 
			
		||||
        if (ret)
 | 
			
		||||
            await ToastService.Default();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async Task ExcelChannelAsync(ITableExportContext<ChannelRuntime> tableExportContext)
 | 
			
		||||
 
 | 
			
		||||
@@ -530,20 +530,21 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
 | 
			
		||||
    }
 | 
			
		||||
    async Task ExportCurrentChannel(ContextMenuItem item, object value)
 | 
			
		||||
    {
 | 
			
		||||
        bool ret;
 | 
			
		||||
        if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return;
 | 
			
		||||
 | 
			
		||||
        if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
 | 
			
		||||
        {
 | 
			
		||||
            await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), DeviceId = channelRuntime.Id });
 | 
			
		||||
            ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), DeviceId = channelRuntime.Id });
 | 
			
		||||
        }
 | 
			
		||||
        else if (channelDeviceTreeItem.TryGetPluginName(out var pluginName))
 | 
			
		||||
        {
 | 
			
		||||
            //插件名称
 | 
			
		||||
            await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), PluginName = pluginName });
 | 
			
		||||
            ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), PluginName = pluginName });
 | 
			
		||||
        }
 | 
			
		||||
        else if (channelDeviceTreeItem.TryGetPluginType(out var pluginType))
 | 
			
		||||
        {
 | 
			
		||||
            await GatewayExportService.OnChannelExport(new ExportFilter() { QueryPageOptions = new(), PluginType = pluginType });
 | 
			
		||||
            ret = await GatewayExportService.OnChannelExport(new ExportFilter() { QueryPageOptions = new(), PluginType = pluginType });
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
@@ -551,14 +552,17 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 返回 true 时自动弹出提示框
 | 
			
		||||
        await ToastService.Default();
 | 
			
		||||
        if (ret)
 | 
			
		||||
            await ToastService.Default();
 | 
			
		||||
    }
 | 
			
		||||
    async Task ExportAllChannel(ContextMenuItem item, object value)
 | 
			
		||||
    {
 | 
			
		||||
        await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
 | 
			
		||||
        bool ret;
 | 
			
		||||
        ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
 | 
			
		||||
 | 
			
		||||
        // 返回 true 时自动弹出提示框
 | 
			
		||||
        await ToastService.Default();
 | 
			
		||||
        if (ret)
 | 
			
		||||
            await ToastService.Default();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1065,25 +1069,26 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
 | 
			
		||||
 | 
			
		||||
    async Task ExportCurrentDevice(ContextMenuItem item, object value)
 | 
			
		||||
    {
 | 
			
		||||
        bool ret;
 | 
			
		||||
        if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return;
 | 
			
		||||
 | 
			
		||||
        if (channelDeviceTreeItem.TryGetDeviceRuntime(out var deviceRuntime))
 | 
			
		||||
        {
 | 
			
		||||
            await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), DeviceId = deviceRuntime.Id });
 | 
			
		||||
            ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), DeviceId = deviceRuntime.Id });
 | 
			
		||||
        }
 | 
			
		||||
        else if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
 | 
			
		||||
        {
 | 
			
		||||
            await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), ChannelId = channelRuntime.Id });
 | 
			
		||||
            ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), ChannelId = channelRuntime.Id });
 | 
			
		||||
        }
 | 
			
		||||
        else if (channelDeviceTreeItem.TryGetPluginName(out var pluginName))
 | 
			
		||||
        {
 | 
			
		||||
            //插件名称
 | 
			
		||||
            await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginName = pluginName });
 | 
			
		||||
            ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginName = pluginName });
 | 
			
		||||
        }
 | 
			
		||||
        else if (channelDeviceTreeItem.TryGetPluginType(out var pluginType))
 | 
			
		||||
        {
 | 
			
		||||
            //采集
 | 
			
		||||
            await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginType = pluginType });
 | 
			
		||||
            ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginType = pluginType });
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
@@ -1091,14 +1096,17 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 返回 true 时自动弹出提示框
 | 
			
		||||
        await ToastService.Default();
 | 
			
		||||
        if (ret)
 | 
			
		||||
            await ToastService.Default();
 | 
			
		||||
    }
 | 
			
		||||
    async Task ExportAllDevice(ContextMenuItem item, object value)
 | 
			
		||||
    {
 | 
			
		||||
        await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() });
 | 
			
		||||
        bool ret;
 | 
			
		||||
        ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() });
 | 
			
		||||
 | 
			
		||||
        // 返回 true 时自动弹出提示框
 | 
			
		||||
        await ToastService.Default();
 | 
			
		||||
        if (ret)
 | 
			
		||||
            await ToastService.Default();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async Task ImportDevice(ContextMenuItem item, object value)
 | 
			
		||||
 
 | 
			
		||||
@@ -221,9 +221,10 @@ public partial class DeviceTable : IDisposable
 | 
			
		||||
 | 
			
		||||
    private async Task ExcelExportAsync(ITableExportContext<DeviceRuntime> tableExportContext, bool all = false)
 | 
			
		||||
    {
 | 
			
		||||
        bool ret;
 | 
			
		||||
        if (all)
 | 
			
		||||
        {
 | 
			
		||||
            await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() });
 | 
			
		||||
            ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() });
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
@@ -231,16 +232,16 @@ public partial class DeviceTable : IDisposable
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                case ChannelDevicePluginTypeEnum.PluginName:
 | 
			
		||||
                    await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName });
 | 
			
		||||
                    ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName });
 | 
			
		||||
                    break;
 | 
			
		||||
                case ChannelDevicePluginTypeEnum.Channel:
 | 
			
		||||
                    await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id });
 | 
			
		||||
                    ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id });
 | 
			
		||||
                    break;
 | 
			
		||||
                case ChannelDevicePluginTypeEnum.Device:
 | 
			
		||||
                    await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType });
 | 
			
		||||
                    ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType });
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() });
 | 
			
		||||
                    ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() });
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -248,7 +249,8 @@ public partial class DeviceTable : IDisposable
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 返回 true 时自动弹出提示框
 | 
			
		||||
        await ToastService.Default();
 | 
			
		||||
        if (ret)
 | 
			
		||||
            await ToastService.Default();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async Task ExcelDeviceAsync(ITableExportContext<DeviceRuntime> tableExportContext)
 | 
			
		||||
 
 | 
			
		||||
@@ -268,9 +268,10 @@ public partial class VariableRuntimeInfo : IDisposable
 | 
			
		||||
 | 
			
		||||
    private async Task ExcelExportAsync(ITableExportContext<VariableRuntime> tableExportContext, bool all = false)
 | 
			
		||||
    {
 | 
			
		||||
        bool ret;
 | 
			
		||||
        if (all)
 | 
			
		||||
        {
 | 
			
		||||
            await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new() });
 | 
			
		||||
            ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new() });
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
@@ -278,16 +279,16 @@ public partial class VariableRuntimeInfo : IDisposable
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                case ChannelDevicePluginTypeEnum.PluginName:
 | 
			
		||||
                    await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName });
 | 
			
		||||
                    ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName });
 | 
			
		||||
                    break;
 | 
			
		||||
                case ChannelDevicePluginTypeEnum.Channel:
 | 
			
		||||
                    await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id });
 | 
			
		||||
                    ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id });
 | 
			
		||||
                    break;
 | 
			
		||||
                case ChannelDevicePluginTypeEnum.Device:
 | 
			
		||||
                    await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType });
 | 
			
		||||
                    ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType });
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new() });
 | 
			
		||||
                    ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new() });
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -295,7 +296,8 @@ public partial class VariableRuntimeInfo : IDisposable
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 返回 true 时自动弹出提示框
 | 
			
		||||
        await ToastService.Default();
 | 
			
		||||
        if (ret)
 | 
			
		||||
            await ToastService.Default();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async Task ExcelVariableAsync(ITableExportContext<VariableRuntime> tableExportContext)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<Project>
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <Version>10.6.23</Version>
 | 
			
		||||
    <Version>10.6.27</Version>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user