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.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
|
@ -91,7 +92,99 @@ namespace ShengShengBuXi.Hubs
|
|||
/// </summary>
|
||||
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 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>
|
||||
/// 初始化音频Hub
|
||||
/// </summary>
|
||||
|
|
@ -1328,5 +1421,76 @@ namespace ShengShengBuXi.Hubs
|
|||
_logger.LogInformation($"客户端获取当前显示模式: {Context.ConnectionId}, 类型: {clientInfo.ClientType}");
|
||||
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">
|
||||
<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 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>
|
||||
<div class="tab-content" id="myTabContent">
|
||||
<!-- 系统配置 -->
|
||||
|
|
@ -294,6 +297,128 @@
|
|||
</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>
|
||||
|
|
@ -302,7 +427,7 @@
|
|||
</div>
|
||||
|
||||
@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>
|
||||
|
|
@ -363,77 +488,54 @@
|
|||
// 初始化SignalR连接
|
||||
function initSignalR() {
|
||||
log("初始化SignalR连接...");
|
||||
updateConnectionStatus("连接中...", "warning");
|
||||
|
||||
// 创建连接
|
||||
connection = new signalR.HubConnectionBuilder()
|
||||
.withUrl("/audiohub")
|
||||
.configureLogging(signalR.LogLevel.Debug) // 使用Debug级别以获取更多日志
|
||||
.withUrl("/audioHub")
|
||||
.withAutomaticReconnect()
|
||||
.build();
|
||||
|
||||
// 注册连接事件处理程序
|
||||
// 注册连接事件处理程序
|
||||
connection.onreconnecting(error => {
|
||||
log("重新连接中: " + (error ? error.message : "未知错误"));
|
||||
updateConnectionStatus("重连中...", "warning");
|
||||
});
|
||||
|
||||
connection.onreconnected(connectionId => {
|
||||
log("已重新连接,ID: " + connectionId);
|
||||
updateConnectionStatus("已连接", "success");
|
||||
// 重新注册
|
||||
registerAsAdmin();
|
||||
|
||||
// 设置连接状态变化的处理
|
||||
// setConnectionStatusHandlers();
|
||||
|
||||
// 设置消息处理
|
||||
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()
|
||||
.then(() => {
|
||||
log("已连接到服务器,连接ID: " + connection.connectionId);
|
||||
.then(function() {
|
||||
log("SignalR连接成功");
|
||||
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 => {
|
||||
log("连接失败: " + err);
|
||||
.catch(function(err) {
|
||||
log("SignalR连接失败: " + err);
|
||||
updateConnectionStatus("连接失败", "danger");
|
||||
setTimeout(() => startConnection(), 5000);
|
||||
});
|
||||
}
|
||||
|
||||
// 注册为管理员
|
||||
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");
|
||||
setTimeout(initSignalR, 5000); // 5秒后重试
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1012,6 +1114,25 @@
|
|||
log("错误: " + message);
|
||||
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() {
|
||||
log("页面已加载");
|
||||
|
|
@ -1307,6 +1561,17 @@
|
|||
setTimeout(getRecentRecordings, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// 切换到显示配置标签页时自动加载显示配置
|
||||
document.getElementById('display-config-tab').addEventListener('click', function() {
|
||||
if (connection && connection.state === signalR.HubConnectionState.Connected) {
|
||||
log("切换到显示配置标签页,自动加载显示配置");
|
||||
setTimeout(getDisplayConfig, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化显示配置默认值
|
||||
resetDisplayConfig();
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
|
@ -155,10 +155,7 @@
|
|||
size: 80 // 大涟漪的大小
|
||||
}
|
||||
},
|
||||
// 预设的中文句子数组
|
||||
sentences: [
|
||||
|
||||
]
|
||||
};
|
||||
|
||||
let currentSentenceIndex = 0; // 当前句子索引
|
||||
|
|
@ -354,6 +351,19 @@
|
|||
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()
|
||||
.then(function () {
|
||||
|
|
@ -362,6 +372,19 @@
|
|||
hubConnection.invoke("RegisterClient", 3, "Display")
|
||||
.then(function() {
|
||||
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");
|
||||
})
|
||||
|
|
@ -369,7 +392,7 @@
|
|||
console.log("开始接收显示文本");
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error("注册客户端失败:", err);
|
||||
console.error("客户端操作失败:", 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 () {
|
||||
$('#magazine').turn({
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="config.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\js\jquery.ripples.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</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