312
This commit is contained in:
parent
b00924a691
commit
7cf247a873
24
ShengShengBuXi.ConsoleApp/ShengShengBuXi.ConsoleApp.sln
Normal file
24
ShengShengBuXi.ConsoleApp/ShengShengBuXi.ConsoleApp.sln
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.5.2.0
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShengShengBuXi.ConsoleApp", "ShengShengBuXi.ConsoleApp.csproj", "{FE0A1BEA-BEF1-CDEB-D076-0AD5E44F7491}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{FE0A1BEA-BEF1-CDEB-D076-0AD5E44F7491}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FE0A1BEA-BEF1-CDEB-D076-0AD5E44F7491}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FE0A1BEA-BEF1-CDEB-D076-0AD5E44F7491}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FE0A1BEA-BEF1-CDEB-D076-0AD5E44F7491}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {9BAF9C30-5594-4165-981C-6FACBF081E8D}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
|
|
@ -7,6 +7,7 @@ using Microsoft.Extensions.Logging;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
|
@ -91,7 +92,99 @@ namespace ShengShengBuXi.Hubs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly int _sentenceHistoryLimit = 20;
|
private static readonly int _sentenceHistoryLimit = 20;
|
||||||
|
|
||||||
|
// 客户端注册信息
|
||||||
|
private static Dictionary<string, ClientInfo> _clientsDict = new Dictionary<string, ClientInfo>();
|
||||||
|
// 用于线程安全访问
|
||||||
|
private static object _clientsLock = new object();
|
||||||
|
|
||||||
|
// 文本显示队列
|
||||||
|
private static List<DisplayText> _displayTextList = new List<DisplayText>();
|
||||||
|
private static object _displayTextListLock = new object();
|
||||||
|
|
||||||
|
// 监控文本列表
|
||||||
private static List<MonitorText> _monitorTextList = new List<MonitorText>();
|
private static List<MonitorText> _monitorTextList = new List<MonitorText>();
|
||||||
|
private static object _monitorTextListLock = new object();
|
||||||
|
|
||||||
|
// 配置信息
|
||||||
|
private static string _displayConfig = "{}";
|
||||||
|
private static object _displayConfigLock = new object();
|
||||||
|
|
||||||
|
// 管理员配置保存路径
|
||||||
|
private static readonly string ConfigDirectory = Path.Combine(Directory.GetCurrentDirectory(), "config");
|
||||||
|
private static readonly string DisplayConfigPath = Path.Combine(ConfigDirectory, "display.json");
|
||||||
|
|
||||||
|
// 用于初始化配置
|
||||||
|
static AudioHub()
|
||||||
|
{
|
||||||
|
// 确保配置目录存在
|
||||||
|
if (!Directory.Exists(ConfigDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(ConfigDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载显示配置
|
||||||
|
LoadDisplayConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载显示配置
|
||||||
|
private static void LoadDisplayConfig()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(DisplayConfigPath))
|
||||||
|
{
|
||||||
|
string configJson = File.ReadAllText(DisplayConfigPath);
|
||||||
|
lock (_displayConfigLock)
|
||||||
|
{
|
||||||
|
_displayConfig = configJson;
|
||||||
|
}
|
||||||
|
Console.WriteLine("加载显示配置成功");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 创建默认配置
|
||||||
|
string defaultConfig = @"{
|
||||||
|
""leftContainer"": {
|
||||||
|
""turnPageHeight"": 0.8,
|
||||||
|
""fontSize"": ""16px"",
|
||||||
|
""typewriterSpeed"": 50
|
||||||
|
},
|
||||||
|
""rightContainer"": {
|
||||||
|
""fontSize"": ""24px"",
|
||||||
|
""fontWeight"": ""normal"",
|
||||||
|
""fontStyle"": ""normal"",
|
||||||
|
""typewriterSpeed"": 50
|
||||||
|
},
|
||||||
|
""waterEffect"": {
|
||||||
|
""enabled"": true,
|
||||||
|
""minInterval"": 800,
|
||||||
|
""maxInterval"": 2000,
|
||||||
|
""simultaneousDrops"": 3,
|
||||||
|
""fadeOutSpeed"": 0.1,
|
||||||
|
""centerBias"": 0.5,
|
||||||
|
""largeDrop"": {
|
||||||
|
""probability"": 0.2,
|
||||||
|
""size"": 9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
lock (_displayConfigLock)
|
||||||
|
{
|
||||||
|
_displayConfig = defaultConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存默认配置
|
||||||
|
File.WriteAllText(DisplayConfigPath, defaultConfig);
|
||||||
|
Console.WriteLine("创建默认显示配置成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"加载显示配置出错: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化音频Hub
|
/// 初始化音频Hub
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -1328,5 +1421,76 @@ namespace ShengShengBuXi.Hubs
|
||||||
_logger.LogInformation($"客户端获取当前显示模式: {Context.ConnectionId}, 类型: {clientInfo.ClientType}");
|
_logger.LogInformation($"客户端获取当前显示模式: {Context.ConnectionId}, 类型: {clientInfo.ClientType}");
|
||||||
return _configurationService.CurrentConfig.DisplayType;
|
return _configurationService.CurrentConfig.DisplayType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取显示配置
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>显示配置的JSON字符串</returns>
|
||||||
|
public string GetDisplayConfig()
|
||||||
|
{
|
||||||
|
lock (_displayConfigLock)
|
||||||
|
{
|
||||||
|
return _displayConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存显示配置
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configJson">配置的JSON字符串</param>
|
||||||
|
/// <returns>是否保存成功</returns>
|
||||||
|
public async Task<bool> SaveDisplayConfig(string configJson)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 尝试解析JSON以验证格式
|
||||||
|
JsonDocument.Parse(configJson);
|
||||||
|
|
||||||
|
// 保存到文件
|
||||||
|
await File.WriteAllTextAsync(DisplayConfigPath, configJson);
|
||||||
|
|
||||||
|
// 更新内存中的配置
|
||||||
|
lock (_displayConfigLock)
|
||||||
|
{
|
||||||
|
_displayConfig = configJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知所有显示客户端
|
||||||
|
await NotifyDisplayConfig();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"保存显示配置出错: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通知所有显示客户端更新配置
|
||||||
|
/// </summary>
|
||||||
|
private async Task NotifyDisplayConfig()
|
||||||
|
{
|
||||||
|
string configJson;
|
||||||
|
lock (_displayConfigLock)
|
||||||
|
{
|
||||||
|
configJson = _displayConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<string> displayClientIds = new List<string>();
|
||||||
|
lock (_clients)
|
||||||
|
{
|
||||||
|
displayClientIds = _clients
|
||||||
|
.Where(c => c.Value.ClientType.ToString() == "Display")
|
||||||
|
.Select(c => c.Key)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var clientId in displayClientIds)
|
||||||
|
{
|
||||||
|
await Clients.Client(clientId).SendAsync("ReceiveDisplayConfig", configJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
60
ShengShengBuXi/Models/MonitorText.cs
Normal file
60
ShengShengBuXi/Models/MonitorText.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ShengShengBuXi.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 监控文本模型
|
||||||
|
/// </summary>
|
||||||
|
public class MonitorText
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 唯一标识符
|
||||||
|
/// </summary>
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示文本内容
|
||||||
|
/// </summary>
|
||||||
|
public string Text { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime Timestamp { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否来自真实用户(否则为系统预设文本)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRealUser { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 原始语音识别结果ID(如果有)
|
||||||
|
/// </summary>
|
||||||
|
public string RecognitionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 文本优先级(真实用户优先显示)
|
||||||
|
/// </summary>
|
||||||
|
public int Priority => IsRealUser ? 10 : 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否处理过
|
||||||
|
/// </summary>
|
||||||
|
public bool IsProcessed { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理后的文字
|
||||||
|
/// </summary>
|
||||||
|
public string CompletedText { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 录音文件路径
|
||||||
|
/// </summary>
|
||||||
|
public string RecordingPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 识别文字的文件的路径
|
||||||
|
/// </summary>
|
||||||
|
public string TextFilePath { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -40,6 +40,9 @@
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link" id="recognition-tab" data-bs-toggle="tab" data-bs-target="#recognition" type="button" role="tab" aria-controls="recognition" aria-selected="false">识别结果</button>
|
<button class="nav-link" id="recognition-tab" data-bs-toggle="tab" data-bs-target="#recognition" type="button" role="tab" aria-controls="recognition" aria-selected="false">识别结果</button>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="display-config-tab" data-bs-toggle="tab" data-bs-target="#display-config" type="button" role="tab" aria-controls="display-config" aria-selected="false">显示配置</button>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content" id="myTabContent">
|
<div class="tab-content" id="myTabContent">
|
||||||
<!-- 系统配置 -->
|
<!-- 系统配置 -->
|
||||||
|
|
@ -294,6 +297,128 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 显示配置 -->
|
||||||
|
<div class="tab-pane fade" id="display-config" role="tabpanel" aria-labelledby="display-config-tab">
|
||||||
|
<div class="mt-3">
|
||||||
|
<div class="d-flex justify-content-end mb-3">
|
||||||
|
<button type="button" class="btn btn-success me-2" onclick="saveDisplayConfig()">保存配置</button>
|
||||||
|
<button type="button" class="btn btn-primary me-2" onclick="getDisplayConfig()">刷新配置</button>
|
||||||
|
<button type="button" class="btn btn-secondary" onclick="resetDisplayConfig()">重置默认值</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="display-config-form" class="mt-3">
|
||||||
|
<div class="row">
|
||||||
|
<!-- 左侧容器配置 -->
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5>左侧容器配置</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="leftTurnPageHeight" class="form-label">翻页高度比例</label>
|
||||||
|
<input type="number" class="form-control" id="leftTurnPageHeight" step="0.1" min="0.1" max="1.0" value="0.8">
|
||||||
|
<div class="form-text">页面高度比例,值范围0.1-1.0</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="leftFontSize" class="form-label">字体大小</label>
|
||||||
|
<input type="text" class="form-control" id="leftFontSize" value="16px">
|
||||||
|
<div class="form-text">左侧历史记录字体大小,例如16px</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="leftTypewriterSpeed" class="form-label">打字效果速度</label>
|
||||||
|
<input type="number" class="form-control" id="leftTypewriterSpeed" min="10" max="500" value="50">
|
||||||
|
<div class="form-text">数值越小,打字效果速度越快</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧容器配置 -->
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5>右侧容器配置</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="rightFontSize" class="form-label">字体大小</label>
|
||||||
|
<input type="text" class="form-control" id="rightFontSize" value="24px">
|
||||||
|
<div class="form-text">右侧主要显示文本的字体大小</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="rightFontWeight" class="form-label">字体粗细</label>
|
||||||
|
<select class="form-select" id="rightFontWeight">
|
||||||
|
<option value="normal">正常</option>
|
||||||
|
<option value="bold">粗体</option>
|
||||||
|
<option value="bolder">很粗</option>
|
||||||
|
<option value="lighter">细体</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="rightFontStyle" class="form-label">字体风格</label>
|
||||||
|
<select class="form-select" id="rightFontStyle">
|
||||||
|
<option value="normal">正常</option>
|
||||||
|
<option value="italic">斜体</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="rightTypewriterSpeed" class="form-label">打字效果速度</label>
|
||||||
|
<input type="number" class="form-control" id="rightTypewriterSpeed" min="10" max="500" value="50">
|
||||||
|
<div class="form-text">数值越小,打字效果速度越快</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 水波纹效果配置 -->
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5>水波纹效果配置</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-3 form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="waterEffectEnabled" checked>
|
||||||
|
<label class="form-check-label" for="waterEffectEnabled">启用水波纹效果</label>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="waterMinInterval" class="form-label">最小间隔(毫秒)</label>
|
||||||
|
<input type="number" class="form-control" id="waterMinInterval" min="100" max="5000" value="800">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="waterMaxInterval" class="form-label">最大间隔(毫秒)</label>
|
||||||
|
<input type="number" class="form-control" id="waterMaxInterval" min="100" max="10000" value="2000">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="waterSimultaneousDrops" class="form-label">同时涟漪数量</label>
|
||||||
|
<input type="number" class="form-control" id="waterSimultaneousDrops" min="1" max="10" value="3">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="waterFadeOutSpeed" class="form-label">淡出速度</label>
|
||||||
|
<input type="number" class="form-control" id="waterFadeOutSpeed" min="0.01" max="1" step="0.01" value="0.1">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="waterCenterBias" class="form-label">中心倾向</label>
|
||||||
|
<input type="number" class="form-control" id="waterCenterBias" min="0" max="1" step="0.1" value="0.5">
|
||||||
|
<div class="form-text">0表示随机,1表示集中在中心</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="waterLargeDropProbability" class="form-label">大水滴概率</label>
|
||||||
|
<input type="number" class="form-control" id="waterLargeDropProbability" min="0" max="1" step="0.1" value="0.2">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="waterLargeDropSize" class="form-label">大水滴大小</label>
|
||||||
|
<input type="number" class="form-control" id="waterLargeDropSize" min="3" max="20" value="9">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -302,7 +427,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
@* <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.6/signalr.min.js"></script> *@
|
@* <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.6/signalr.min.js"></script> *@
|
||||||
<script src="~/lib/microsoft-signalr/signalr.min.js"></script>
|
<script src="~/lib/microsoft-signalr/signalr.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -363,77 +488,54 @@
|
||||||
// 初始化SignalR连接
|
// 初始化SignalR连接
|
||||||
function initSignalR() {
|
function initSignalR() {
|
||||||
log("初始化SignalR连接...");
|
log("初始化SignalR连接...");
|
||||||
updateConnectionStatus("连接中...", "warning");
|
|
||||||
|
|
||||||
// 创建连接
|
// 创建连接
|
||||||
connection = new signalR.HubConnectionBuilder()
|
connection = new signalR.HubConnectionBuilder()
|
||||||
.withUrl("/audiohub")
|
.withUrl("/audioHub")
|
||||||
.configureLogging(signalR.LogLevel.Debug) // 使用Debug级别以获取更多日志
|
|
||||||
.withAutomaticReconnect()
|
.withAutomaticReconnect()
|
||||||
.build();
|
.build();
|
||||||
|
// 注册连接事件处理程序
|
||||||
// 注册连接事件处理程序
|
|
||||||
connection.onreconnecting(error => {
|
connection.onreconnecting(error => {
|
||||||
log("重新连接中: " + (error ? error.message : "未知错误"));
|
log("重新连接中: " + (error ? error.message : "未知错误"));
|
||||||
updateConnectionStatus("重连中...", "warning");
|
updateConnectionStatus("重连中...", "warning");
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.onreconnected(connectionId => {
|
|
||||||
log("已重新连接,ID: " + connectionId);
|
// 设置连接状态变化的处理
|
||||||
updateConnectionStatus("已连接", "success");
|
// setConnectionStatusHandlers();
|
||||||
// 重新注册
|
|
||||||
registerAsAdmin();
|
// 设置消息处理
|
||||||
|
connection.on("ReceiveServerMessage", function(message) {
|
||||||
|
log("服务器消息: " + message);
|
||||||
|
showMessage(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.onclose(error => {
|
// 启动连接
|
||||||
log("连接已关闭: " + (error ? error.message : ""));
|
|
||||||
updateConnectionStatus("已断开", "danger");
|
|
||||||
// 5秒后重连
|
|
||||||
setTimeout(() => startConnection(), 5000);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 注册服务器消息处理程序
|
|
||||||
setupSignalRHandlers();
|
|
||||||
|
|
||||||
// 建立连接
|
|
||||||
startConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开始连接
|
|
||||||
function startConnection() {
|
|
||||||
log("正在连接到服务器...");
|
|
||||||
|
|
||||||
connection.start()
|
connection.start()
|
||||||
.then(() => {
|
.then(function() {
|
||||||
log("已连接到服务器,连接ID: " + connection.connectionId);
|
log("SignalR连接成功");
|
||||||
updateConnectionStatus("已连接", "success");
|
updateConnectionStatus("已连接", "success");
|
||||||
registerAsAdmin();
|
|
||||||
|
// 注册为管理员客户端
|
||||||
|
connection.invoke("RegisterClient", 1, "WebAdmin")
|
||||||
|
.then(function() {
|
||||||
|
log("注册为管理员客户端成功");
|
||||||
|
// 获取最新配置
|
||||||
|
getLatestConfig();
|
||||||
|
// 获取显示配置
|
||||||
|
getDisplayConfig();
|
||||||
|
// 获取客户端列表
|
||||||
|
//getClientList();
|
||||||
|
setTimeout(getRecentRecordings, 1000);
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
log("注册为管理员客户端失败: " + err);
|
||||||
|
showMessage("注册为管理员客户端失败,请刷新页面重试", "danger");
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(function(err) {
|
||||||
log("连接失败: " + err);
|
log("SignalR连接失败: " + err);
|
||||||
updateConnectionStatus("连接失败", "danger");
|
updateConnectionStatus("连接失败", "danger");
|
||||||
setTimeout(() => startConnection(), 5000);
|
setTimeout(initSignalR, 5000); // 5秒后重试
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注册为管理员
|
|
||||||
function registerAsAdmin() {
|
|
||||||
if (!connection || connection.state !== signalR.HubConnectionState.Connected) {
|
|
||||||
log("无法注册:未连接");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log("正在注册为管理员客户端...");
|
|
||||||
|
|
||||||
.then(() => {
|
|
||||||
log("已注册为管理员客户端");
|
|
||||||
// 注册成功后立即获取配置和录音列表
|
|
||||||
getLatestConfig();
|
|
||||||
setTimeout(getRecentRecordings, 1000);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
log("注册失败: " + err);
|
|
||||||
showMessage("注册失败: " + err, "danger");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1012,6 +1114,25 @@
|
||||||
log("错误: " + message);
|
log("错误: " + message);
|
||||||
showMessage("错误: " + message, "danger");
|
showMessage("错误: " + message, "danger");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 显示配置更新结果
|
||||||
|
connection.on("DisplayConfigUpdated", (success, message) => {
|
||||||
|
log("显示配置更新结果: " + message);
|
||||||
|
showMessage(message, success ? "success" : "danger");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 接收显示配置
|
||||||
|
connection.on("ReceiveDisplayConfig", (configJson) => {
|
||||||
|
log("接收到显示配置");
|
||||||
|
try {
|
||||||
|
const config = JSON.parse(configJson);
|
||||||
|
loadDisplayConfigToForm(config);
|
||||||
|
log("显示配置已成功加载");
|
||||||
|
} catch (error) {
|
||||||
|
log("解析显示配置失败: " + error);
|
||||||
|
showMessage("解析显示配置失败: " + error, "danger");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新客户端列表
|
// 更新客户端列表
|
||||||
|
|
@ -1294,6 +1415,139 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取显示配置
|
||||||
|
function getDisplayConfig() {
|
||||||
|
if (!connection || connection.state !== signalR.HubConnectionState.Connected) {
|
||||||
|
showMessage("未连接到服务器", "danger");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log("正在获取显示配置...");
|
||||||
|
connection.invoke("GetDisplayConfig")
|
||||||
|
.then(function (configJson) {
|
||||||
|
log("获取显示配置成功");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const config = JSON.parse(configJson);
|
||||||
|
|
||||||
|
// 填充表单
|
||||||
|
if (config.leftContainer) {
|
||||||
|
document.getElementById('leftTurnPageHeight').value = config.leftContainer.turnPageHeight || 0.8;
|
||||||
|
document.getElementById('leftFontSize').value = config.leftContainer.fontSize || '16px';
|
||||||
|
document.getElementById('leftTypewriterSpeed').value = config.leftContainer.typewriterSpeed || 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.rightContainer) {
|
||||||
|
document.getElementById('rightFontSize').value = config.rightContainer.fontSize || '24px';
|
||||||
|
document.getElementById('rightFontWeight').value = config.rightContainer.fontWeight || 'normal';
|
||||||
|
document.getElementById('rightFontStyle').value = config.rightContainer.fontStyle || 'normal';
|
||||||
|
document.getElementById('rightTypewriterSpeed').value = config.rightContainer.typewriterSpeed || 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.waterEffect) {
|
||||||
|
document.getElementById('waterEffectEnabled').checked = config.waterEffect.enabled !== undefined ? config.waterEffect.enabled : true;
|
||||||
|
document.getElementById('waterMinInterval').value = config.waterEffect.minInterval || 800;
|
||||||
|
document.getElementById('waterMaxInterval').value = config.waterEffect.maxInterval || 2000;
|
||||||
|
document.getElementById('waterSimultaneousDrops').value = config.waterEffect.simultaneousDrops || 3;
|
||||||
|
document.getElementById('waterFadeOutSpeed').value = config.waterEffect.fadeOutSpeed || 0.1;
|
||||||
|
document.getElementById('waterCenterBias').value = config.waterEffect.centerBias || 0.5;
|
||||||
|
|
||||||
|
if (config.waterEffect.largeDrop) {
|
||||||
|
document.getElementById('waterLargeDropProbability').value = config.waterEffect.largeDrop.probability || 0.2;
|
||||||
|
document.getElementById('waterLargeDropSize').value = config.waterEffect.largeDrop.size || 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showMessage("显示配置加载成功", "success");
|
||||||
|
} catch (error) {
|
||||||
|
log("解析显示配置失败: " + error);
|
||||||
|
showMessage("解析显示配置失败: " + error, "danger");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
log("获取显示配置出错: " + err);
|
||||||
|
showMessage("获取显示配置出错: " + err, "danger");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存显示配置
|
||||||
|
function saveDisplayConfig() {
|
||||||
|
if (!connection || connection.state !== signalR.HubConnectionState.Connected) {
|
||||||
|
showMessage("未连接到服务器", "danger");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收集表单数据
|
||||||
|
const config = {
|
||||||
|
leftContainer: {
|
||||||
|
turnPageHeight: parseFloat(document.getElementById('leftTurnPageHeight').value),
|
||||||
|
fontSize: document.getElementById('leftFontSize').value,
|
||||||
|
typewriterSpeed: parseInt(document.getElementById('leftTypewriterSpeed').value)
|
||||||
|
},
|
||||||
|
rightContainer: {
|
||||||
|
fontSize: document.getElementById('rightFontSize').value,
|
||||||
|
fontWeight: document.getElementById('rightFontWeight').value,
|
||||||
|
fontStyle: document.getElementById('rightFontStyle').value,
|
||||||
|
typewriterSpeed: parseInt(document.getElementById('rightTypewriterSpeed').value)
|
||||||
|
},
|
||||||
|
waterEffect: {
|
||||||
|
enabled: document.getElementById('waterEffectEnabled').checked,
|
||||||
|
minInterval: parseInt(document.getElementById('waterMinInterval').value),
|
||||||
|
maxInterval: parseInt(document.getElementById('waterMaxInterval').value),
|
||||||
|
simultaneousDrops: parseInt(document.getElementById('waterSimultaneousDrops').value),
|
||||||
|
fadeOutSpeed: parseFloat(document.getElementById('waterFadeOutSpeed').value),
|
||||||
|
centerBias: parseFloat(document.getElementById('waterCenterBias').value),
|
||||||
|
largeDrop: {
|
||||||
|
probability: parseFloat(document.getElementById('waterLargeDropProbability').value),
|
||||||
|
size: parseInt(document.getElementById('waterLargeDropSize').value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const configJson = JSON.stringify(config);
|
||||||
|
log("正在保存显示配置: " + configJson);
|
||||||
|
|
||||||
|
connection.invoke("SaveDisplayConfig", configJson)
|
||||||
|
.then(function (success) {
|
||||||
|
if (success) {
|
||||||
|
log("显示配置保存成功");
|
||||||
|
showMessage("显示配置保存成功", "success");
|
||||||
|
// 重载配置
|
||||||
|
getDisplayConfig();
|
||||||
|
} else {
|
||||||
|
log("显示配置保存失败");
|
||||||
|
showMessage("显示配置保存失败", "danger");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
log("保存显示配置出错: " + err);
|
||||||
|
showMessage("保存显示配置出错: " + err, "danger");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置显示配置为默认值
|
||||||
|
function resetDisplayConfig() {
|
||||||
|
document.getElementById('leftTurnPageHeight').value = 0.8;
|
||||||
|
document.getElementById('leftFontSize').value = '16px';
|
||||||
|
document.getElementById('leftTypewriterSpeed').value = 50;
|
||||||
|
|
||||||
|
document.getElementById('rightFontSize').value = '24px';
|
||||||
|
document.getElementById('rightFontWeight').value = 'normal';
|
||||||
|
document.getElementById('rightFontStyle').value = 'normal';
|
||||||
|
document.getElementById('rightTypewriterSpeed').value = 50;
|
||||||
|
|
||||||
|
document.getElementById('waterEffectEnabled').checked = true;
|
||||||
|
document.getElementById('waterMinInterval').value = 800;
|
||||||
|
document.getElementById('waterMaxInterval').value = 2000;
|
||||||
|
document.getElementById('waterSimultaneousDrops').value = 3;
|
||||||
|
document.getElementById('waterFadeOutSpeed').value = 0.1;
|
||||||
|
document.getElementById('waterCenterBias').value = 0.5;
|
||||||
|
document.getElementById('waterLargeDropProbability').value = 0.2;
|
||||||
|
document.getElementById('waterLargeDropSize').value = 9;
|
||||||
|
|
||||||
|
showMessage("已重置为默认配置,请点击保存按钮保存", "info");
|
||||||
|
}
|
||||||
|
|
||||||
// 页面加载完成后初始化
|
// 页面加载完成后初始化
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
log("页面已加载");
|
log("页面已加载");
|
||||||
|
|
@ -1307,6 +1561,17 @@
|
||||||
setTimeout(getRecentRecordings, 500);
|
setTimeout(getRecentRecordings, 500);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 切换到显示配置标签页时自动加载显示配置
|
||||||
|
document.getElementById('display-config-tab').addEventListener('click', function() {
|
||||||
|
if (connection && connection.state === signalR.HubConnectionState.Connected) {
|
||||||
|
log("切换到显示配置标签页,自动加载显示配置");
|
||||||
|
setTimeout(getDisplayConfig, 500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化显示配置默认值
|
||||||
|
resetDisplayConfig();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
}
|
}
|
||||||
|
|
@ -155,10 +155,7 @@
|
||||||
size: 80 // 大涟漪的大小
|
size: 80 // 大涟漪的大小
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 预设的中文句子数组
|
|
||||||
sentences: [
|
|
||||||
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let currentSentenceIndex = 0; // 当前句子索引
|
let currentSentenceIndex = 0; // 当前句子索引
|
||||||
|
|
@ -354,6 +351,19 @@
|
||||||
displayText(text);
|
displayText(text);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 接收显示配置的处理函数
|
||||||
|
hubConnection.on("ReceiveDisplayConfig", function (configJson) {
|
||||||
|
console.log("收到显示配置");
|
||||||
|
try {
|
||||||
|
const newConfig = JSON.parse(configJson);
|
||||||
|
// 更新全局配置
|
||||||
|
updateConfig(newConfig);
|
||||||
|
console.log("已更新显示配置");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("解析显示配置失败:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 启动连接
|
// 启动连接
|
||||||
hubConnection.start()
|
hubConnection.start()
|
||||||
.then(function () {
|
.then(function () {
|
||||||
|
|
@ -362,6 +372,19 @@
|
||||||
hubConnection.invoke("RegisterClient", 3, "Display")
|
hubConnection.invoke("RegisterClient", 3, "Display")
|
||||||
.then(function() {
|
.then(function() {
|
||||||
console.log("注册为显示客户端成功");
|
console.log("注册为显示客户端成功");
|
||||||
|
// 获取显示配置
|
||||||
|
return hubConnection.invoke("GetDisplayConfig");
|
||||||
|
})
|
||||||
|
.then(function(configJson) {
|
||||||
|
if(configJson) {
|
||||||
|
console.log("已获取显示配置");
|
||||||
|
try {
|
||||||
|
const config = JSON.parse(configJson);
|
||||||
|
updateConfig(config);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("解析显示配置失败:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
// 开始接收显示文本
|
// 开始接收显示文本
|
||||||
return hubConnection.invoke("StartReceivingDisplayText");
|
return hubConnection.invoke("StartReceivingDisplayText");
|
||||||
})
|
})
|
||||||
|
|
@ -369,7 +392,7 @@
|
||||||
console.log("开始接收显示文本");
|
console.log("开始接收显示文本");
|
||||||
})
|
})
|
||||||
.catch(function(err) {
|
.catch(function(err) {
|
||||||
console.error("注册客户端失败:", err);
|
console.error("客户端操作失败:", err);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function (err) {
|
||||||
|
|
@ -385,6 +408,67 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新配置
|
||||||
|
function updateConfig(newConfig) {
|
||||||
|
// 更新左侧容器配置
|
||||||
|
if (newConfig.leftContainer) {
|
||||||
|
CONFIG.leftContainer.turnPageHeight = newConfig.leftContainer.turnPageHeight || CONFIG.leftContainer.turnPageHeight;
|
||||||
|
CONFIG.leftContainer.fontSize = newConfig.leftContainer.fontSize || CONFIG.leftContainer.fontSize;
|
||||||
|
CONFIG.leftContainer.typewriterSpeed = newConfig.leftContainer.typewriterSpeed || CONFIG.leftContainer.typewriterSpeed;
|
||||||
|
|
||||||
|
// 应用字体大小到CSS
|
||||||
|
document.documentElement.style.setProperty('--left-font-size', CONFIG.leftContainer.fontSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新右侧容器配置
|
||||||
|
if (newConfig.rightContainer) {
|
||||||
|
CONFIG.rightContainer.fontSize = newConfig.rightContainer.fontSize || CONFIG.rightContainer.fontSize;
|
||||||
|
CONFIG.rightContainer.fontWeight = newConfig.rightContainer.fontWeight || CONFIG.rightContainer.fontWeight;
|
||||||
|
CONFIG.rightContainer.fontStyle = newConfig.rightContainer.fontStyle || CONFIG.rightContainer.fontStyle;
|
||||||
|
CONFIG.rightContainer.typewriterSpeed = newConfig.rightContainer.typewriterSpeed || CONFIG.rightContainer.typewriterSpeed;
|
||||||
|
|
||||||
|
// 应用到CSS
|
||||||
|
document.documentElement.style.setProperty('--right-font-size', CONFIG.rightContainer.fontSize);
|
||||||
|
document.documentElement.style.setProperty('--right-font-weight', CONFIG.rightContainer.fontWeight);
|
||||||
|
document.documentElement.style.setProperty('--right-font-style', CONFIG.rightContainer.fontStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新水波纹效果配置
|
||||||
|
if (newConfig.waterEffect) {
|
||||||
|
CONFIG.waterEffect.enabled = newConfig.waterEffect.enabled !== undefined ? newConfig.waterEffect.enabled : CONFIG.waterEffect.enabled;
|
||||||
|
CONFIG.waterEffect.minInterval = newConfig.waterEffect.minInterval || CONFIG.waterEffect.minInterval;
|
||||||
|
CONFIG.waterEffect.maxInterval = newConfig.waterEffect.maxInterval || CONFIG.waterEffect.maxInterval;
|
||||||
|
CONFIG.waterEffect.simultaneousDrops = newConfig.waterEffect.simultaneousDrops || CONFIG.waterEffect.simultaneousDrops;
|
||||||
|
CONFIG.waterEffect.fadeOutSpeed = newConfig.waterEffect.fadeOutSpeed || CONFIG.waterEffect.fadeOutSpeed;
|
||||||
|
CONFIG.waterEffect.centerBias = newConfig.waterEffect.centerBias !== undefined ? newConfig.waterEffect.centerBias : CONFIG.waterEffect.centerBias;
|
||||||
|
|
||||||
|
if (newConfig.waterEffect.largeDrop) {
|
||||||
|
CONFIG.waterEffect.largeDrop.probability = newConfig.waterEffect.largeDrop.probability !== undefined ?
|
||||||
|
newConfig.waterEffect.largeDrop.probability : CONFIG.waterEffect.largeDrop.probability;
|
||||||
|
CONFIG.waterEffect.largeDrop.size = newConfig.waterEffect.largeDrop.size || CONFIG.waterEffect.largeDrop.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用水波纹效果配置
|
||||||
|
if ($('.water-effect').data('ripples')) {
|
||||||
|
// 如果ripples已初始化,先销毁
|
||||||
|
$('.water-effect').ripples('destroy');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CONFIG.waterEffect.enabled) {
|
||||||
|
$('.water-effect').show();
|
||||||
|
$('.water-effect').ripples({
|
||||||
|
resolution: 1024,
|
||||||
|
dropRadius: 1.5,
|
||||||
|
perturbance: 0
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$('.water-effect').hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("配置已更新:", CONFIG);
|
||||||
|
}
|
||||||
|
|
||||||
// 页面加载完成后初始化
|
// 页面加载完成后初始化
|
||||||
$(window).ready(function () {
|
$(window).ready(function () {
|
||||||
$('#magazine').turn({
|
$('#magazine').turn({
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Content Update="config.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Update="wwwroot\js\jquery.ripples.js">
|
<Content Update="wwwroot\js\jquery.ripples.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
|
||||||
25
ShengShengBuXi/config/display.json
Normal file
25
ShengShengBuXi/config/display.json
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"leftContainer": {
|
||||||
|
"turnPageHeight": 0.55,
|
||||||
|
"fontSize": "16px",
|
||||||
|
"typewriterSpeed": 50
|
||||||
|
},
|
||||||
|
"rightContainer": {
|
||||||
|
"fontSize": "40px",
|
||||||
|
"fontWeight": "700",
|
||||||
|
"fontStyle": "italic",
|
||||||
|
"typewriterSpeed": 330
|
||||||
|
},
|
||||||
|
"waterEffect": {
|
||||||
|
"enabled": true,
|
||||||
|
"minInterval": 1600,
|
||||||
|
"maxInterval": 8000,
|
||||||
|
"simultaneousDrops": 2,
|
||||||
|
"fadeOutSpeed": 2000,
|
||||||
|
"centerBias": 0.6,
|
||||||
|
"largeDrop": {
|
||||||
|
"probability": 0.2,
|
||||||
|
"size": 80
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user