This commit is contained in:
snltty
2023-12-02 22:15:40 +08:00
parent 26430402c4
commit a664ee8813
57 changed files with 4041 additions and 999 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -117,4 +117,287 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="label15.Text" xml:space="preserve">
<value>每项1024长度0项保留不可用</value>
</data>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
</value>
</data>
</root>

View File

@@ -32,6 +32,7 @@ namespace cmonitor.libs
this.length = length;
this.itemSize = itemSize;
bytes = new byte[length * itemSize];
states = new ShareMemoryState[length];
}
public void InitLocal()
@@ -100,6 +101,7 @@ namespace cmonitor.libs
byte[] valuesBytes;
byte[] gloablBytes;
ShareMemoryState[] states = Array.Empty<ShareMemoryState>();
private void InitStateValues()
{
gloablBytes = new byte[bytes.Length];
@@ -118,18 +120,15 @@ namespace cmonitor.libs
{
for (int index = 0; index < length; index++)
{
for (int i = 0; i < valuesBytes.Length; i++)
{
ShareMemoryState state = (ShareMemoryState)valuesBytes[i];
bool result = ReadState(accessorLocal, index, state);
if (result)
ShareMemoryState state = ReadState(accessorLocal, index);
if (state != states[index])
{
states[index] = state;
stateAction(index, state);
}
}
}
}
}
private void SyncMemory()
{
if (accessorGlobal != null && accessorLocal != null)
@@ -141,7 +140,7 @@ namespace cmonitor.libs
for (int index = 0; index < length; index++)
{
//检查更新状态
if (ReadState(accessorGlobal, index, ShareMemoryState.Updated) == false)
if (ReadStateIf(accessorGlobal, index, ShareMemoryState.Updated) == false)
{
continue;
}
@@ -152,7 +151,7 @@ namespace cmonitor.libs
{
accessorLocal.WriteArray(_index, gloablBytes, _index, itemSize);
}
WriteState(accessorGlobal, index, ShareMemoryState.Updated, false);
WriteStateIf(accessorGlobal, index, ShareMemoryState.Updated, false);
}
}
}
@@ -172,13 +171,13 @@ namespace cmonitor.libs
for (int index = 0; index < length; index++)
{
//state
bool _updated = ReadState(accessorLocal, index, ShareMemoryState.Updated);
bool _updated = ReadStateIf(accessorLocal, index, ShareMemoryState.Updated);
if (_updated == false)
{
continue;
}
WriteState(accessorLocal, index, ShareMemoryState.Updated, false);
WriteStateIf(accessorLocal, index, ShareMemoryState.Updated, false);
updated |= _updated;
//key length
@@ -310,7 +309,14 @@ namespace cmonitor.libs
return false;
}
private bool ReadState(IShareMemory accessor, int index, ShareMemoryState state)
private ShareMemoryState ReadState(IShareMemory accessor, int index)
{
if (accessor == null) return ShareMemoryState.None;
ShareMemoryState stateByte = (ShareMemoryState)accessor.ReadByte(index * itemSize);
return stateByte;
}
private bool ReadStateIf(IShareMemory accessor, int index, ShareMemoryState state)
{
if (accessor == null) return false;
@@ -320,29 +326,35 @@ namespace cmonitor.libs
public bool ReadUpdated(int index)
{
if (accessorLocal != null)
return ReadState(accessorLocal, index, ShareMemoryState.Updated);
return ReadStateIf(accessorLocal, index, ShareMemoryState.Updated);
if (accessorGlobal != null)
return ReadState(accessorGlobal, index, ShareMemoryState.Updated);
return ReadStateIf(accessorGlobal, index, ShareMemoryState.Updated);
return false;
}
public bool ReadClosed(int index)
{
if (accessorLocal != null)
return ReadState(accessorLocal, index, ShareMemoryState.Closed);
return ReadStateIf(accessorLocal, index, ShareMemoryState.Closed);
if (accessorGlobal != null)
return ReadState(accessorGlobal, index, ShareMemoryState.Closed);
return ReadStateIf(accessorGlobal, index, ShareMemoryState.Closed);
return false;
}
public bool ReadRunning(int index)
{
if (accessorLocal != null)
return ReadState(accessorLocal, index, ShareMemoryState.Running);
return ReadStateIf(accessorLocal, index, ShareMemoryState.Running);
if (accessorGlobal != null)
return ReadState(accessorGlobal, index, ShareMemoryState.Running);
return ReadStateIf(accessorGlobal, index, ShareMemoryState.Running);
return false;
}
private void WriteState(IShareMemory accessor, int index, ShareMemoryState state, bool value)
private void WriteState(IShareMemory accessor, int index, ShareMemoryState state)
{
if (accessor == null) return;
byte stateByte = (byte)state;
accessor.WriteByte(index * itemSize, stateByte);
}
private void WriteStateIf(IShareMemory accessor, int index, ShareMemoryState state, bool value)
{
if (accessor == null) return;
byte stateValue = accessor.ReadByte(index * itemSize);
@@ -359,19 +371,19 @@ namespace cmonitor.libs
}
public void WriteUpdated(int index, bool updated = true)
{
WriteState(accessorLocal, index, ShareMemoryState.Updated, updated);
WriteState(accessorGlobal, index, ShareMemoryState.Updated, updated);
WriteStateIf(accessorLocal, index, ShareMemoryState.Updated, updated);
WriteStateIf(accessorGlobal, index, ShareMemoryState.Updated, updated);
}
public void WriteClosed(int index, bool closed = true)
{
WriteState(accessorLocal, index, ShareMemoryState.Closed, closed);
WriteState(accessorGlobal, index, ShareMemoryState.Closed, closed);
WriteStateIf(accessorLocal, index, ShareMemoryState.Closed, closed);
WriteStateIf(accessorGlobal, index, ShareMemoryState.Closed, closed);
WriteUpdated(index, true);
}
public void WriteRunning(int index, bool running = true)
{
WriteState(accessorLocal, index, ShareMemoryState.Running, running);
WriteState(accessorGlobal, index, ShareMemoryState.Running, running);
WriteStateIf(accessorLocal, index, ShareMemoryState.Running, running);
WriteStateIf(accessorGlobal, index, ShareMemoryState.Running, running);
WriteUpdated(index, true);
}
}
@@ -387,6 +399,7 @@ namespace cmonitor.libs
public enum ShareMemoryState : byte
{
None = 0,
Updated = 0b0000_0001,
Closed = 0b0000_0010,
Running = 0b0000_0100,

View File

@@ -7,19 +7,21 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "cmonitor", "cmonitor\cmonit
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "common.libs", "common.libs\common.libs.csproj", "{00EECF97-99EB-4B12-AAEF-ED2363914275}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cmonitor.sas.service", "cmonitor.sas.service\cmonitor.sas.service.csproj", "{0ED28F51-04B0-4B2D-B47E-EB1715F0CE57}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "cmonitor.sas.service", "cmonitor.sas.service\cmonitor.sas.service.csproj", "{0ED28F51-04B0-4B2D-B47E-EB1715F0CE57}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cmonitor.libs", "cmonitor.libs\cmonitor.libs.csproj", "{9080618D-367F-474E-AACA-A3C2F294448F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "cmonitor.libs", "cmonitor.libs\cmonitor.libs.csproj", "{9080618D-367F-474E-AACA-A3C2F294448F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cmonitor.install.win", "cmonitor.install.win\cmonitor.install.win.csproj", "{4EB9FFB0-B05C-4C4E-BD44-796715E47585}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "cmonitor.install.win", "cmonitor.install.win\cmonitor.install.win.csproj", "{4EB9FFB0-B05C-4C4E-BD44-796715E47585}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "llock.win", "llock.win\llock.win.csproj", "{6F2602B3-221D-475D-B643-D54987605644}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "llock.win", "llock.win\llock.win.csproj", "{6F2602B3-221D-475D-B643-D54987605644}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "wallpaper.win", "wallpaper.win\wallpaper.win.csproj", "{41C61263-4F73-4C62-BD78-8C3612C8F98B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "wallpaper.win", "wallpaper.win\wallpaper.win.csproj", "{41C61263-4F73-4C62-BD78-8C3612C8F98B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "notify.win", "notify.win\notify.win.csproj", "{886D781B-9A41-4DD1-8B94-490AE6476E67}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "notify.win", "notify.win\notify.win.csproj", "{886D781B-9A41-4DD1-8B94-490AE6476E67}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "message.win", "message.win\message.win.csproj", "{BCA983E7-928F-4353-BF98-4ACCBB1B173E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "message.win", "message.win\message.win.csproj", "{BCA983E7-928F-4353-BF98-4ACCBB1B173E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cmonitor.snatch.win", "cmonitor.snatch.win\cmonitor.snatch.win.csproj", "{5267B401-6818-407C-8323-E6C8A3CC01D6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -196,6 +198,24 @@ Global
{BCA983E7-928F-4353-BF98-4ACCBB1B173E}.ReleaseLinux|x64.Build.0 = Release|Any CPU
{BCA983E7-928F-4353-BF98-4ACCBB1B173E}.ReleaseLinux|x86.ActiveCfg = Release|Any CPU
{BCA983E7-928F-4353-BF98-4ACCBB1B173E}.ReleaseLinux|x86.Build.0 = Release|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.Debug|x64.ActiveCfg = Debug|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.Debug|x64.Build.0 = Debug|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.Debug|x86.ActiveCfg = Debug|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.Debug|x86.Build.0 = Debug|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.Release|Any CPU.Build.0 = Release|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.Release|x64.ActiveCfg = Release|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.Release|x64.Build.0 = Release|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.Release|x86.ActiveCfg = Release|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.Release|x86.Build.0 = Release|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.ReleaseLinux|Any CPU.ActiveCfg = Release|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.ReleaseLinux|Any CPU.Build.0 = Release|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.ReleaseLinux|x64.Build.0 = Release|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.ReleaseLinux|x86.ActiveCfg = Release|Any CPU
{5267B401-6818-407C-8323-E6C8A3CC01D6}.ReleaseLinux|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

303
cmonitor.snatch.win/MainForm.Designer.cs generated Normal file
View File

@@ -0,0 +1,303 @@
namespace cmonitor.snatch.win
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
groupTypeQuestion = new GroupBox();
groupResult = new GroupBox();
inputWrong = new TextBox();
label3 = new Label();
inputRight = new TextBox();
label2 = new Label();
inputJoin = new TextBox();
label1 = new Label();
groupTypeInput = new GroupBox();
inputAnswer = new TextBox();
groupTypeSelect = new GroupBox();
checkB = new CheckBox();
checkF = new CheckBox();
checkA = new CheckBox();
checkC = new CheckBox();
checkE = new CheckBox();
checkD = new CheckBox();
btnConfirm = new Button();
panel1 = new Panel();
inputQuestion = new TextBox();
groupTypeQuestion.SuspendLayout();
groupResult.SuspendLayout();
groupTypeInput.SuspendLayout();
groupTypeSelect.SuspendLayout();
panel1.SuspendLayout();
SuspendLayout();
//
// groupTypeQuestion
//
groupTypeQuestion.Controls.Add(groupResult);
groupTypeQuestion.Controls.Add(groupTypeInput);
groupTypeQuestion.Controls.Add(groupTypeSelect);
groupTypeQuestion.Controls.Add(btnConfirm);
groupTypeQuestion.Controls.Add(panel1);
groupTypeQuestion.Location = new Point(12, 3);
groupTypeQuestion.Name = "groupTypeQuestion";
groupTypeQuestion.Size = new Size(350, 433);
groupTypeQuestion.TabIndex = 1;
groupTypeQuestion.TabStop = false;
groupTypeQuestion.Text = "看题";
//
// groupResult
//
groupResult.Controls.Add(inputWrong);
groupResult.Controls.Add(label3);
groupResult.Controls.Add(inputRight);
groupResult.Controls.Add(label2);
groupResult.Controls.Add(inputJoin);
groupResult.Controls.Add(label1);
groupResult.Location = new Point(15, 252);
groupResult.Name = "groupResult";
groupResult.Size = new Size(320, 59);
groupResult.TabIndex = 12;
groupResult.TabStop = false;
groupResult.Text = "答题结果";
//
// inputWrong
//
inputWrong.ForeColor = Color.Red;
inputWrong.Location = new Point(252, 24);
inputWrong.Name = "inputWrong";
inputWrong.Size = new Size(58, 23);
inputWrong.TabIndex = 5;
//
// label3
//
label3.AutoSize = true;
label3.ForeColor = Color.Red;
label3.Location = new Point(218, 27);
label3.Name = "label3";
label3.Size = new Size(32, 17);
label3.TabIndex = 4;
label3.Text = "错误";
//
// inputRight
//
inputRight.ForeColor = Color.Green;
inputRight.Location = new Point(146, 24);
inputRight.Name = "inputRight";
inputRight.Size = new Size(62, 23);
inputRight.TabIndex = 3;
//
// label2
//
label2.AutoSize = true;
label2.ForeColor = Color.Green;
label2.Location = new Point(110, 27);
label2.Name = "label2";
label2.Size = new Size(32, 17);
label2.TabIndex = 2;
label2.Text = "正确";
//
// inputJoin
//
inputJoin.Location = new Point(39, 24);
inputJoin.Name = "inputJoin";
inputJoin.Size = new Size(60, 23);
inputJoin.TabIndex = 1;
//
// label1
//
label1.AutoSize = true;
label1.Location = new Point(4, 27);
label1.Name = "label1";
label1.Size = new Size(32, 17);
label1.TabIndex = 0;
label1.Text = "参与";
//
// groupTypeInput
//
groupTypeInput.Controls.Add(inputAnswer);
groupTypeInput.Location = new Point(15, 320);
groupTypeInput.Name = "groupTypeInput";
groupTypeInput.Size = new Size(320, 56);
groupTypeInput.TabIndex = 11;
groupTypeInput.TabStop = false;
groupTypeInput.Text = "输入你的答案";
//
// inputAnswer
//
inputAnswer.Location = new Point(12, 22);
inputAnswer.Name = "inputAnswer";
inputAnswer.Size = new Size(298, 23);
inputAnswer.TabIndex = 0;
//
// groupTypeSelect
//
groupTypeSelect.Controls.Add(checkB);
groupTypeSelect.Controls.Add(checkF);
groupTypeSelect.Controls.Add(checkA);
groupTypeSelect.Controls.Add(checkC);
groupTypeSelect.Controls.Add(checkE);
groupTypeSelect.Controls.Add(checkD);
groupTypeSelect.Location = new Point(15, 317);
groupTypeSelect.Name = "groupTypeSelect";
groupTypeSelect.Size = new Size(320, 56);
groupTypeSelect.TabIndex = 10;
groupTypeSelect.TabStop = false;
groupTypeSelect.Text = "选择你的答案";
//
// checkB
//
checkB.AutoSize = true;
checkB.Location = new Point(65, 25);
checkB.Name = "checkB";
checkB.Size = new Size(35, 21);
checkB.TabIndex = 5;
checkB.Text = "B";
checkB.UseVisualStyleBackColor = true;
//
// checkF
//
checkF.AutoSize = true;
checkF.Location = new Point(277, 25);
checkF.Name = "checkF";
checkF.Size = new Size(33, 21);
checkF.TabIndex = 9;
checkF.Text = "F";
checkF.UseVisualStyleBackColor = true;
//
// checkA
//
checkA.AutoSize = true;
checkA.Location = new Point(12, 25);
checkA.Name = "checkA";
checkA.Size = new Size(35, 21);
checkA.TabIndex = 4;
checkA.Text = "A";
checkA.UseVisualStyleBackColor = true;
//
// checkC
//
checkC.AutoSize = true;
checkC.Location = new Point(118, 25);
checkC.Name = "checkC";
checkC.Size = new Size(35, 21);
checkC.TabIndex = 8;
checkC.Text = "C";
checkC.UseVisualStyleBackColor = true;
//
// checkE
//
checkE.AutoSize = true;
checkE.Location = new Point(225, 25);
checkE.Name = "checkE";
checkE.Size = new Size(34, 21);
checkE.TabIndex = 6;
checkE.Text = "E";
checkE.UseVisualStyleBackColor = true;
//
// checkD
//
checkD.AutoSize = true;
checkD.Location = new Point(170, 25);
checkD.Name = "checkD";
checkD.Size = new Size(36, 21);
checkD.TabIndex = 7;
checkD.Text = "D";
checkD.UseVisualStyleBackColor = true;
//
// btnConfirm
//
btnConfirm.Location = new Point(125, 387);
btnConfirm.Name = "btnConfirm";
btnConfirm.Size = new Size(99, 32);
btnConfirm.TabIndex = 2;
btnConfirm.Text = "确定提交";
btnConfirm.UseVisualStyleBackColor = true;
btnConfirm.Click += OnConfirm;
//
// panel1
//
panel1.Controls.Add(inputQuestion);
panel1.Location = new Point(15, 28);
panel1.Name = "panel1";
panel1.Size = new Size(320, 218);
panel1.TabIndex = 3;
//
// inputQuestion
//
inputQuestion.Dock = DockStyle.Fill;
inputQuestion.Location = new Point(0, 0);
inputQuestion.Multiline = true;
inputQuestion.Name = "inputQuestion";
inputQuestion.Size = new Size(320, 218);
inputQuestion.TabIndex = 0;
//
// MainForm
//
AutoScaleDimensions = new SizeF(7F, 17F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(374, 448);
Controls.Add(groupTypeQuestion);
Icon = (Icon)resources.GetObject("$this.Icon");
Name = "MainForm";
Text = "互动答题";
Load += OnLoad;
groupTypeQuestion.ResumeLayout(false);
groupResult.ResumeLayout(false);
groupResult.PerformLayout();
groupTypeInput.ResumeLayout(false);
groupTypeInput.PerformLayout();
groupTypeSelect.ResumeLayout(false);
groupTypeSelect.PerformLayout();
panel1.ResumeLayout(false);
panel1.PerformLayout();
ResumeLayout(false);
}
#endregion
private GroupBox groupTypeQuestion;
private Button btnConfirm;
private Panel panel1;
private TextBox inputQuestion;
private CheckBox checkA;
private CheckBox checkB;
private CheckBox checkC;
private CheckBox checkD;
private CheckBox checkE;
private CheckBox checkF;
private GroupBox groupTypeSelect;
private GroupBox groupTypeInput;
private TextBox inputAnswer;
private GroupBox groupResult;
private Label label1;
private TextBox inputWrong;
private Label label3;
private TextBox inputRight;
private Label label2;
private TextBox inputJoin;
}
}

View File

@@ -0,0 +1,349 @@
using cmonitor.libs;
using System.Diagnostics;
using System.Text;
using System.Text.Json;
namespace cmonitor.snatch.win
{
public partial class MainForm : Form
{
protected override CreateParams CreateParams
{
get
{
const int WS_EX_APPWINDOW = 0x40000;
const int WS_EX_TOOLWINDOW = 0x80;
CreateParams cp = base.CreateParams;
cp.ExStyle &= (~WS_EX_APPWINDOW);
cp.ExStyle |= WS_EX_TOOLWINDOW;
return cp;
}
}
SnatchQuestionInfo snatchQuesionInfo;
SnatchAnswerInfo snatchAnswerInfo;
private readonly ShareMemory shareMemory;
private int shareQuestionIndex;
private int shareAnswerIndex;
private string key = "SnatchAnswer";
private DateTime startTime = new DateTime(1970, 1, 1);
public MainForm(string shareMkey, int shareMLength, int shareItemMLength, int shareQuestionIndex, int shareAnswerIndex)
{
StartPosition = FormStartPosition.CenterScreen;
MaximizeBox = false;
MinimizeBox = false;
ControlBox = false;
ShowInTaskbar = false;
TopMost = true;
InitializeComponent();
this.shareQuestionIndex = shareQuestionIndex;
this.shareAnswerIndex = shareAnswerIndex;
this.shareMemory = new ShareMemory(shareMkey, shareMLength, shareItemMLength);
}
CheckBox[] options = Array.Empty<CheckBox>();
private void OnLoad(object sender, EventArgs e)
{
options = new CheckBox[] { checkA, checkB, checkC, checkD, checkE, checkF };
shareMemory.InitLocal();
shareMemory.WriteRunning(shareQuestionIndex, true);
shareMemory.WriteClosed(shareQuestionIndex, false);
shareMemory.WriteRunning(shareAnswerIndex, true);
shareMemory.WriteClosed(shareAnswerIndex, false);
shareMemory.StateAction((index, state) =>
{
if (shareQuestionIndex == index)
{
if ((state & ShareMemoryState.Updated) == ShareMemoryState.Updated)
{
ReadQuestionMemory();
CheckState();
}
if ((state & ShareMemoryState.Closed) == ShareMemoryState.Closed)
{
WriteAnswerConfirmIfState(SnatchState.Ask, false, string.Empty);
}
}
else if (shareAnswerIndex == index)
{
if ((state & ShareMemoryState.Updated) == ShareMemoryState.Updated)
{
ReadAnswerMemory();
CheckState();
}
}
});
shareMemory.Loop();
ReadMemory();
CheckState();
}
private string GetAnswer()
{
if (snatchQuesionInfo.Type == SnatchType.Select)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < snatchQuesionInfo.Option; i++)
{
if (options[i].Checked)
sb.Append((char)(i + 65));
}
return sb.ToString();
}
else if (snatchQuesionInfo.Type == SnatchType.Input)
{
return inputAnswer.Text.Trim();
}
return string.Empty;
}
private void OnConfirm(object sender, EventArgs e)
{
if (snatchQuesionInfo.End)
{
MessageBox.Show($"答题已结束");
return;
}
if (snatchAnswerInfo.State == SnatchState.Confirm && snatchQuesionInfo.Repeat == false)
{
MessageBox.Show($"已答题,不可重复作答");
return;
}
if (snatchAnswerInfo.Times > snatchQuesionInfo.Max)
{
MessageBox.Show($"超过重复答题次数上线");
return;
}
string answer = GetAnswer();
if (string.IsNullOrWhiteSpace(answer))
{
MessageBox.Show($"请给出你的答案");
return;
}
loading = true;
Task.Run(() =>
{
CheckBtn();
WriteAnswerConfirm(answer == snatchQuesionInfo.Correct, answer);
loading = false;
CheckState();
});
}
private void CloseClear()
{
shareMemory.WriteRunning(shareQuestionIndex, false);
shareMemory.WriteClosed(shareQuestionIndex, true);
shareMemory.WriteRunning(shareAnswerIndex, false);
shareMemory.WriteClosed(shareAnswerIndex, true);
Application.ExitThread();
Application.Exit();
Process.GetCurrentProcess().Kill();
}
private void ReadQuestionMemory()
{
try
{
string result = shareMemory.GetItemValue(shareQuestionIndex);
if (string.IsNullOrWhiteSpace(result))
{
snatchQuesionInfo = new SnatchQuestionInfo { Question = "测试测试", Correct = "ABC", Option = 5, Type = SnatchType.Select };
}
else
{
snatchQuesionInfo = JsonSerializer.Deserialize<SnatchQuestionInfo>(result);
}
}
catch (Exception)
{
CloseClear();
}
}
private void ReadAnswerMemory()
{
try
{
string result = shareMemory.GetItemValue(shareAnswerIndex);
if (string.IsNullOrWhiteSpace(result))
{
snatchAnswerInfo = new SnatchAnswerInfo { Result = false, ResultStr = "ABC", State = SnatchState.Ask, };
}
else
{
snatchAnswerInfo = JsonSerializer.Deserialize<SnatchAnswerInfo>(result);
}
}
catch (Exception)
{
CloseClear();
}
}
private void ReadMemory()
{
ReadQuestionMemory();
ReadAnswerMemory();
}
private void WriteAnswerConfirmIfState(SnatchState state, bool result, string resultStr)
{
if (snatchAnswerInfo.State == state)
{
WriteAnswerConfirm(result, resultStr);
}
}
private void WriteAnswerConfirm(bool result, string resultStr)
{
snatchAnswerInfo.State = SnatchState.Confirm;
snatchAnswerInfo.Result = result;
snatchAnswerInfo.ResultStr = resultStr;
snatchAnswerInfo.Time = (long)(DateTime.UtcNow.Subtract(startTime)).TotalMilliseconds;
snatchAnswerInfo.Times++;
shareMemory.Update(shareAnswerIndex, key, JsonSerializer.Serialize(snatchAnswerInfo));
}
bool loading = false;
private void EnableBtn(string text)
{
this.Invoke(() =>
{
btnConfirm.Text = text;
btnConfirm.Enabled = true;
});
}
private void DisableBtn(string text)
{
this.Invoke(() =>
{
btnConfirm.Text = text;
btnConfirm.Enabled = false;
});
}
private void CheckOptions()
{
for (int i = 0; i < options.Length; i++)
{
options[i].Visible = i < snatchQuesionInfo.Option;
options[i].Checked = false;
}
if (string.IsNullOrWhiteSpace(snatchAnswerInfo.ResultStr) == false && snatchAnswerInfo.ResultStr.Length <= snatchQuesionInfo.Option)
{
for (int i = 0; i < snatchAnswerInfo.ResultStr.Length; i++)
{
char option = snatchAnswerInfo.ResultStr[i];
if (option >= 'A' && option <= 'Z' && option - 'A' < options.Length)
{
options[(option - 'A')].Checked = true;
}
}
}
}
private void CheckBtn()
{
if (loading)
{
DisableBtn("操作中..");
}
else if (snatchAnswerInfo.State == SnatchState.Confirm)
{
btnConfirm.ForeColor = (snatchAnswerInfo.Result ? Color.Green : Color.Red);
EnableBtn(snatchAnswerInfo.Result ? "答题正确" : "答题错误");
}
else if (snatchQuesionInfo.End)
{
EnableBtn("已结束");
}
else
{
EnableBtn("确定提交");
}
}
private void CheckState()
{
this.Invoke(() =>
{
CheckOptions();
CheckBtn();
groupTypeSelect.Visible = false;
groupTypeInput.Visible = false;
if (snatchQuesionInfo.Type == SnatchType.Select)
{
groupTypeSelect.Visible = true;
}
else if (snatchQuesionInfo.Type == SnatchType.Input)
{
groupTypeInput.Visible = true;
}
inputQuestion.Text = snatchQuesionInfo.Question;
inputJoin.Text = snatchQuesionInfo.Join.ToString();
inputRight.Text = snatchQuesionInfo.Right.ToString();
inputWrong.Text = snatchQuesionInfo.Wrong.ToString();
});
}
sealed class SnatchQuestionInfo
{
public SnatchType Type { get; set; }
/// <summary>
/// 问题
/// </summary>
public string Question { get; set; }
/// <summary>
/// 正确答案
/// </summary>
public string Correct { get; set; }
/// <summary>
/// 选项数
/// </summary>
public int Option { get; set; }
/// <summary>
/// 最多答题数
/// </summary>
public int Max { get; set; } = int.MaxValue;
/// <summary>
/// 已结束
/// </summary>
public bool End { get; set; } = false;
/// <summary>
/// 重复答题
/// </summary>
public bool Repeat { get; set; } = true;
public int Join { get; set; } = 0;
public int Right { get; set; } = 0;
public int Wrong { get; set; } = 0;
}
sealed class SnatchAnswerInfo
{
public SnatchState State { get; set; }
public bool Result { get; set; }
public long Time { get; set; }
public int Times { get; set; }
public string ResultStr { get; set; }
}
enum SnatchState : byte
{
None = 0,
Ask = 1,
Confirm = 2,
}
enum SnatchType : byte
{
None = 0,
Select = 1,
Input = 2,
}
}
}

View File

@@ -0,0 +1,400 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
</value>
</data>
</root>

View File

@@ -0,0 +1,32 @@
namespace cmonitor.snatch.win
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] arg)
{
string shareMkey = "cmonitor/share";
int shareMLength = 10;
int shareItemMLength = 1024;
int shareQuestionIndex = 5;
int shareAnswerIndex = 6;
if (arg.Length > 0)
{
shareMkey = arg[0];
shareMLength = int.Parse(arg[1]);
shareItemMLength = int.Parse(arg[2]);
shareQuestionIndex = int.Parse(arg[3]);
shareAnswerIndex = int.Parse(arg[4]);
}
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new MainForm(shareMkey, shareMLength, shareItemMLength, shareQuestionIndex, shareAnswerIndex));
}
}
}

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC 清单选项
如果想要更改 Windows 用户帐户控制级别,请使用
以下节点之一替换 requestedExecutionLevel 节点。
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
元素。
-->
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
Windows 版本的列表。取消评论适当的元素,
Windows 将自动选择最兼容的环境。 -->
<!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 -->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
<!-- Windows 8 -->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
<!-- Windows 8.1 -->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
<!-- Windows 10 -->
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
</application>
</compatibility>
<!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI无需
选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
<!--
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
-->
<!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFrameworks>net7.0-windows;net8.0-windows</TargetFrameworks>
<Nullable>disable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<Configurations>Debug;Release;ReleaseLinux</Configurations>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Content Include="favicon.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\cmonitor.libs\cmonitor.libs.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Update="MainForm.cs">
<SubType>Form</SubType>
</Compile>
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,7 +1,7 @@
import { sendWebsocketMsg } from './request'
export const shareUpdate = (name, item) => {
export const shareUpdate = (names, item) => {
return sendWebsocketMsg('share/update', {
name, item
names, item
});
}

View File

@@ -0,0 +1,26 @@
import { sendWebsocketMsg } from './request'
export const addGroup = (data) => {
return sendWebsocketMsg('snatch/AddGroup', data);
}
export const delGroup = (username, id) => {
return sendWebsocketMsg('snatch/DeleteGroup', {
username, id
});
}
export const add = (data) => {
return sendWebsocketMsg('snatch/add', data);
}
export const del = (username, groupid, id) => {
return sendWebsocketMsg('snatch/del', {
username, groupid, id
});
}
export const update = (names, question) => {
return sendWebsocketMsg('snatch/update', {
names, item: question
});
}

View File

@@ -34,9 +34,16 @@ export default {
</script>
<style lang="stylus" scoped>
// @media (min-width: 768px) {
// .main-wrap {
// background-color: #1c77ac !important;
// background-image: none !important;
// // background-image: url('../assets/bg.jpg') !important;
// }
// }
@media (min-width: 768px) {
.main-wrap {
background-image: url('../assets/bg.jpg') !important;
max-width: 39rem;
}
}

View File

@@ -1,14 +1,15 @@
<template>
<div class="device-list-wrap absolute flex flex-column flex-nowarp" id="device-list-wrap">
<div class="content flex-1 flex">
<div class="items flex-1 relative scrollbar-1">
<Items></Items>
</div>
<div class="active-device flex flex-column" v-if="globalData.pc">
<div class="head">
<div class="content flex-1 flex flex-column">
<div class="head" v-if="globalData.pc">
<Head></Head>
</div>
<div class="items flex-1 relative scrollbar-1">
<Items></Items>
</div>
<!-- <div class="active-device flex flex-column" v-if="globalData.pc">
<div class="flex-1 prev">
@@ -19,7 +20,7 @@
</div>
</div>
</div>
</div>
</div> -->
</div>
<div class="foot" v-if="!globalData.pc">
<div class="foot-options">
@@ -68,24 +69,35 @@ export default {
</script>
<style lang="stylus" scoped>
@media (min-width: 768px) {
.active-device {
width: calc(100% - 41rem) !important;
}
// @media (min-width: 768px) {
// .active-device {
// // width: calc(100% - 41rem) !important;
// }
.items {
height: 100%;
padding: 1rem 0 !important;
box-sizing: border-box;
border-right: 1px solid #999;
background-color: rgba(255, 255, 255, 0.3);
}
.foot {
display: none;
}
}
// .items {
// padding: 1rem 1rem 0 1rem !important;
// height: auto;
// width: 80.6rem;
// // padding: 1rem 0 !important;
// box-sizing: border-box;
// // border-right: 1px solid #999;
// // background-color: rgba(255, 255, 255, 0.3);
// display: flex;
// display: -ms-flex;
// display: -o-flex;
// flex-wrap: wrap;
// justify-content: space-between;
// }
// .foot {
// display: none;
// }
// }
// @media (min-width: 768px) {
// .items {
// max-width: 39rem;
// }
// }
.device-list-wrap {
.content {
position: relative;

View File

@@ -58,7 +58,7 @@ export default {
showEdit: false,
list: computed(() => {
let user = globalData.value.usernames[globalData.value.username];
if (user) {
if (user && user.Windows) {
if (state.group == 0 && user.Windows.length > 0) {
state.group = user.Windows[0].ID;
}

View File

@@ -62,7 +62,7 @@ export default {
showEdit: false,
groups: computed(() => {
let user = globalData.value.usernames[globalData.value.username];
if (user) {
if (user && user.Windows) {
if (state.group == 0 && user.Windows.length > 0) {
state.group = user.Windows[0].ID;
}

View File

@@ -80,6 +80,14 @@ export default {
}
}
}
.inner {
padding: 0.6rem;
&>div {
padding: 0.6rem 0;
}
}
}
}
</style>

View File

@@ -58,7 +58,7 @@ export default {
showEdit: false,
list: computed(() => {
let user = globalData.value.usernames[globalData.value.username];
if (user) {
if (user && user.Processs) {
if (state.group == 0 && user.Processs.length > 0) {
state.group = user.Processs[0].ID;
}

View File

@@ -81,7 +81,7 @@ export default {
showEdit: false,
groups: computed(() => {
let user = globalData.value.usernames[globalData.value.username];
if (user) {
if (user && user.Processs) {
if (state.group == 0 && user.Processs.length > 0) {
state.group = user.Processs[0].ID;
}

View File

@@ -58,7 +58,7 @@ export default {
showEdit: false,
list: computed(() => {
let user = globalData.value.usernames[globalData.value.username];
if (user) {
if (user && user.Rules) {
if (state.group == 0 && user.Rules.length > 0) {
state.group = user.Rules[0].ID;
}

View File

@@ -43,7 +43,7 @@ export default {
loading: false,
group: 0,
groups: computed(() => {
if (user.value) {
if (user.value && user.value.Rules) {
if (state.group == 0 && user.value.Rules.length > 0) {
state.group = user.value.Rules[0].ID;
}
@@ -52,7 +52,7 @@ export default {
return [];
}),
rule: computed(() => {
if (user) {
if (user && user.value.Rules) {
let rule = user.value.Rules.filter(c => c.ID == state.group)[0];
if (rule) {
return rule;

View File

@@ -1,5 +1,5 @@
<template>
<el-dialog class="options" title="设置" destroy-on-close v-model="state.show" center :close-on-click-modal="false" align-center width="94%">
<el-dialog class="options-center" title="设置" destroy-on-close v-model="state.show" center :close-on-click-modal="false" align-center width="94%">
<div class="setting-wrap">
<el-form ref="ruleFormRef" :model="state.form" :rules="state.rules" label-width="100px">
<el-form-item label="报告延迟(ms)" prop="ReportDelay">

View File

@@ -0,0 +1,19 @@
<template>
<div>
<!-- <ChooseDig v-if="pluginState.setting.showSetting" v-model="pluginState.setting.showSetting"></ChooseDig> -->
</div>
</template>
<script>
import { injectPluginState } from '../../provide';
// import ChooseDig from './ChooseDig.vue'
export default {
components: {},
setup() {
const pluginState = injectPluginState();
return { pluginState }
}
}
</script>
<style lang="stylus" scoped></style>

View File

@@ -52,7 +52,7 @@ export default {
state.loading = true;
let value = JSON.parse(JSON.stringify(props.data.Share.Lock.Value));
value.val = 'reject';
shareUpdate(props.data.MachineName, {
shareUpdate([props.data.MachineName], {
index: props.data.Share.Lock.Index,
value: JSON.stringify(value)
}).then((res) => {
@@ -72,7 +72,7 @@ export default {
let value = JSON.parse(JSON.stringify(props.data.Share.Lock.Value));
value.val = 'confirm';
props.data.Share.Lock.Value.notify = false;
shareUpdate(props.data.MachineName, {
shareUpdate([props.data.MachineName], {
index: props.data.Share.Lock.Index,
value: JSON.stringify(value)
}).then((res) => {
@@ -94,7 +94,7 @@ export default {
const handleStarChange = () => {
state.loading = true;
let value = JSON.parse(JSON.stringify(props.data.Share.Lock.Value));
shareUpdate(props.data.MachineName, {
shareUpdate([props.data.MachineName], {
index: props.data.Share.Lock.Index,
value: JSON.stringify(value)
}).then((res) => {

View File

@@ -47,7 +47,6 @@ export default {
},
init() {
},
lockTypes: { code: '代码', lock: '锁屏', cmonitor: '班长', flag: '学习目标', class: '课程', 'remark-block': '图形化点评', 'remark-cpp': 'C++点评' },
update(item, report) {
if (!report.Share) return;
@@ -64,7 +63,7 @@ export default {
item.Share.Lock.Value.star1 = json.star1 || 0;
item.Share.Lock.Value.star2 = json.star2 || 0;
item.Share.Lock.Value.star3 = json.star3 || 0;
item.Share.Lock.TypeText = this.lockTypes[json.type];
item.Share.Lock.TypeText = json.typeText;
}
}
if (report.Share.KeyBoard) {

View File

@@ -0,0 +1,36 @@
<template>
<div class="item">
<div class="subitem">
<span class="label">互动抢答</span>
<el-button @click="handleSnatchUse">开始</el-button>
<el-button @click="handleSnatchQuestion">录题</el-button>
</div>
</div>
<SnatchTemplate v-if="pluginState.shareSnatch.showTemplate" v-model="pluginState.shareSnatch.showTemplate"></SnatchTemplate>
<SnatchUse v-if="pluginState.shareSnatch.showUse" v-model="pluginState.shareSnatch.showUse"></SnatchUse>
</template>
<script>
import { injectPluginState } from '@/views/device/provide';
import SnatchTemplate from './template/Template.vue'
import SnatchUse from './use/Use.vue'
export default {
components: { SnatchTemplate, SnatchUse },
setup() {
const pluginState = injectPluginState();
const handleSnatchQuestion = () => {
pluginState.value.shareSnatch.showTemplate = true;
}
const handleSnatchUse = () => {
pluginState.value.shareSnatch.showUse = true;
}
return { pluginState, handleSnatchUse, handleSnatchQuestion }
}
}
</script>
<style lang="stylus" scoped></style>

View File

@@ -0,0 +1,41 @@
export default {
field() {
return {
ShareSnatch: {
Question: { Index: 0, Value: { Type: 1, Question: '', Correct: '', Option: 0, Max: 0, End: false, Repeat: false, Join: 0, Right: 0, Wrong: 0 } },
Answer: { Index: 0 },
}
};
},
state: {
shareSnatch: {
showTemplate: false,
showUse: true
}
},
init() {
},
update(item, report) {
if (!report.Share) return;
console.log(report.Share);
if (report.Share.SnatchQuestion) {
item.ShareSnatch.Question.Index = report.Share.SnatchQuestion.Index;
const json = JSON.parse(report.Share.SnatchQuestion.Value);
item.ShareSnatch.Question.Value.Type = json.Type;
item.ShareSnatch.Question.Value.Question = json.Question;
item.ShareSnatch.Question.Value.Correct = json.Correct;
item.ShareSnatch.Question.Value.Option = json.Option;
item.ShareSnatch.Question.Value.Max = json.Max;
item.ShareSnatch.Question.Value.End = json.End;
item.ShareSnatch.Question.Value.Repeat = json.Repeat;
item.ShareSnatch.Question.Value.Join = json.Join;
item.ShareSnatch.Question.Value.Right = json.Right;
item.ShareSnatch.Question.Value.Wrong = json.Wrong;
}
if (report.Share.SnatchAnswer) {
item.ShareSnatch.Answer.Index = report.Share.SnatchAnswer.Index;
}
}
}

View File

@@ -0,0 +1,135 @@
<template>
<div class="snatchs-items-wrap flex flex-nowrap flex-column">
<div class="head t-c flex">
<el-button @click="handleAdd()">添加项</el-button>
</div>
<div class="flex-1">
<div class="prevs-wrap">
<el-table :data="state.list" size="small" border stripe style="width: 100%" height="50vh">
<el-table-column prop="Name" label="名称" />
<el-table-column label="操作" width="110">
<template #default="scope">
<template v-if="scope.row.ID > 1">
<el-button size="small" @click="handleAdd(scope.row)">
<el-icon>
<EditPen />
</el-icon>
</el-button>
<el-popconfirm title="删除不可逆,是否确定?" @confirm="handleDel(scope.row)">
<template #reference>
<el-button size="small" type="danger">
<el-icon>
<Delete />
</el-icon>
</el-button>
</template>
</el-popconfirm>
</template>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-dialog :title="`${state.currentItem.ID==0?'添加项':'修改项'}`" destroy-on-close v-model="state.showEdit" center :close-on-click-modal="false" align-center width="80%">
<div>
<el-input v-model="state.currentItem.Name" size="large" placeholder="分组名称" />
</div>
<template #footer>
<el-button @click="handleEditCancel"> </el-button>
<el-button type="success" plain :loading="state.loading" @click="handleEditSubmit"> </el-button>
</template>
</el-dialog>
</div>
</template>
<script>
import { reactive } from '@vue/reactivity';
import { computed } from '@vue/runtime-core';
import { ElMessage } from 'element-plus';
import { addGroup, delGroup } from '@/apis/snatch'
import { injectGlobalData } from '@/views/provide';
export default {
setup() {
const globalData = injectGlobalData();;
const state = reactive({
loading: false,
currentItem: { ID: 0, Name: '' },
showEdit: false,
list: computed(() => {
let user = globalData.value.usernames[globalData.value.username];
if (user && user.Snatchs) {
if (state.group == 0 && user.Snatchs.length > 0) {
state.group = user.Snatchs[0].ID;
}
return user.Snatchs;
}
return [];
})
});
const handleAdd = (item) => {
item = item || { Name: '', ID: 0 };
state.currentItem.Name = item.Name;
state.currentItem.ID = item.ID;
state.showEdit = true;
}
const handleDel = (item) => {
state.loading = true;
delGroup(globalData.value.username, item.ID).then((error) => {
state.loading = false;
if (error) {
ElMessage.error(error);
} else {
ElMessage.success('操作成功!');
globalData.value.updateRuleFlag = Date.now();
}
}).catch((e) => {
state.loading = false;
ElMessage.error('操作失败!');
})
}
const handleEditCancel = () => {
state.showEdit = false;
}
const handleEditSubmit = () => {
state.currentItem.Name = state.currentItem.Name.replace(/^\s|\s$/g, '');
if (!state.currentItem.Name) {
return;
}
state.loading = true;
addGroup({
UserName: globalData.value.username,
Group: state.currentItem
}).then((error) => {
state.loading = false;
if (error) {
ElMessage.error(error);
} else {
ElMessage.success('操作成功!');
state.showEdit = false;
globalData.value.updateRuleFlag = Date.now();
}
}).catch((e) => {
state.loading = false;
ElMessage.error('操作失败!');
})
}
return { state, handleAdd, handleDel, handleEditCancel, handleEditSubmit }
}
}
</script>
<style lang="stylus" scoped>
.snatchs-items-wrap {
.head {
width: 100%;
padding-bottom: 1rem;
}
.prevs-wrap {
height: 100%;
position: relative;
}
}
</style>

View File

@@ -0,0 +1,229 @@
<template>
<div class="snatchs-items-wrap flex flex-nowrap flex-column">
<div class="head t-c flex">
<el-select v-model="state.group" placeholder="选择一个分组" style="width:13rem">
<el-option v-for="item in state.groups" :key="item.ID" :label="item.Name" :value="item.ID" />
</el-select>
<span class="flex-1"></span>
<el-button @click="handleAdd()">添加项</el-button>
</div>
<div class="flex-1">
<div class="prevs-wrap">
<el-table :data="state.list" size="small" border stripe style="width: 100%" height="50vh">
<el-table-column prop="Name" label="名称"></el-table-column>
<el-table-column label="操作" width="110">
<template #default="scope">
<el-button size="small" @click="handleAdd(scope.row)">
<el-icon>
<EditPen />
</el-icon>
</el-button>
<el-popconfirm title="删除不可逆,是否确定?" @confirm="handleDel(scope.row)">
<template #reference>
<el-button size="small" type="danger">
<el-icon>
<Delete />
</el-icon>
</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-dialog :title="`${state.currentItem.ID==0?'添加项':'修改项'}`" destroy-on-close v-model="state.showEdit" center :close-on-click-modal="false" align-center width="94%">
<div>
<el-form ref="formDom" :rules="state.rules" :model="state.currentItem" label-width="0">
<el-form-item prop="Name">
<el-input v-model="state.currentItem.Name" placeholder="名称" maxlength="30" show-word-limit />
</el-form-item>
<el-form-item prop="Question">
<el-input type="textarea" resize="none" rows="6" v-model="state.currentItem.Question" placeholder="题目内容" maxlength="250" show-word-limit />
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="8">
<el-form-item prop="Type">
<el-select style="width:90%;" v-model="state.currentItem.Type" placeholder="类别">
<el-option v-for="item in state.types" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label-width="0" prop="Repeat">
<el-checkbox v-model="state.currentItem.Repeat" label="重复答题" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="上限" label-width="4rem" prop="Max">
<el-input v-model="state.currentItem.Max" placeholder="最多答题多少次" />
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item v-if="state.currentItem.Type == 1">
<ul style="width:100%;">
<template v-for="(item,index) in state.currentItem.Options" :key="index">
<li class="flex" style="margin-bottom:.6rem">
<span>{{String.fromCharCode(index+65)}}</span>
<el-input style="width:12rem;" v-model="item.Text" placeholder="" />
<span class="flex-1"></span>
<div>
<el-checkbox size="small" v-model="item.Value" label="答案" style="margin-right:.6rem" />
<el-button size="small" @click="handleAddOption(index)">添加</el-button>
<el-button size="small" @click="handleDelOption(index)">删除</el-button>
</div>
</li>
</template>
</ul>
</el-form-item>
<el-form-item v-if="state.currentItem.Type == 2">
<el-input v-model="state.currentItem.Correct" placeholder="简答题答案" />
</el-form-item>
</el-form>
</div>
<template #footer>
<el-button @click="handleEditCancel"> </el-button>
<el-button type="success" plain :loading="state.loading" @click="handleEditSubmit"> </el-button>
</template>
</el-dialog>
</div>
</template>
<script>
import { reactive, ref } from '@vue/reactivity';
import { computed } from '@vue/runtime-core';
import { ElMessage } from 'element-plus';
import { add, del } from '@/apis/snatch'
import { injectGlobalData } from '@/views/provide';
export default {
setup() {
const globalData = injectGlobalData();;
const state = reactive({
loading: false,
group: 0,
currentItem: { ID: 0, Name: '', Type: 1, Question: '', Options: [{ Text: '', Value: false }], Correct: '', Max: 65535, Repeat: true },
rules: {
Name: [
{ required: true, message: '名称必填', trigger: 'blur' }
],
Question: [
{ required: true, message: '内容必填', trigger: 'blur' }
],
},
types: [
{ label: '选择题', value: 1 },
{ label: '简答题', value: 2 },
],
showEdit: false,
groups: computed(() => {
let user = globalData.value.usernames[globalData.value.username];
if (user && user.Snatchs) {
if (state.group == 0 && user.Snatchs.length > 0) {
state.group = user.Snatchs[0].ID;
}
return user.Snatchs;
}
return [];
}),
list: computed(() => {
let group = state.groups.filter(c => c.ID == state.group)[0];
if (group) return group.List;
return [];
})
});
const handleAdd = (item) => {
item = item || { ID: 0, Name: '', Type: 1, Question: '', Options: [{ Text: '', Value: false }], Correct: '', Max: 65535, Repeat: true };
state.currentItem.Name = item.Name;
state.currentItem.Type = item.Type;
state.currentItem.ID = item.ID;
state.currentItem.Question = item.Question;
state.currentItem.Options = item.Options;
state.currentItem.Correct = item.Correct;
state.currentItem.Max = item.Max;
state.currentItem.Repeat = item.Repeat;
state.showEdit = true;
}
const handleDel = (item) => {
state.loading = true;
del(globalData.value.username, state.group, item.ID).then((error) => {
state.loading = false;
if (error) {
ElMessage.error(error);
} else {
ElMessage.success('操作成功!');
globalData.value.updateRuleFlag = Date.now();
}
}).catch((e) => {
state.loading = false;
ElMessage.error('操作失败!');
})
}
const handleEditCancel = () => {
state.showEdit = false;
}
const formDom = ref(null);
const handleEditSubmit = () => {
formDom.value.validate((valid) => {
if (!valid) return;
state.loading = true;
const json = JSON.parse(JSON.stringify(state.currentItem));
json.Max = +json.Max;
json.Type = +json.Type;
json.Name = json.Name.replace(/^\s|\s$/g, '');
json.Correct = json.Correct.replace(/^\s|\s$/g, '');
if (json.Type == 2 && !json.Correct) {
return;
}
add({
UserName: globalData.value.username,
GroupID: state.group,
Item: json
}).then((error) => {
state.loading = false;
if (error) {
ElMessage.error(error);
} else {
ElMessage.success('操作成功!');
state.showEdit = false;
globalData.value.updateRuleFlag = Date.now();
}
}).catch((e) => {
state.loading = false;
ElMessage.error('操作失败!');
});
});
}
const handleAddOption = (index) => {
if (state.currentItem.Options.length >= 6) return;
state.currentItem.Options.splice(index + 1, 0, { Text: '', Value: false });
}
const handleDelOption = (index) => {
if (state.currentItem.Options.length <= 1) return;
state.currentItem.Options.splice(index, 1);
}
return { state, formDom, handleAdd, handleDel, handleEditCancel, handleEditSubmit, handleAddOption, handleDelOption }
}
}
</script>
<style lang="stylus" scoped>
.snatchs-items-wrap {
.head {
width: 100%;
padding-bottom: 1rem;
}
.prevs-wrap {
height: 100%;
position: relative;
}
}
</style>

View File

@@ -0,0 +1,50 @@
<template>
<el-dialog class="options" title="互动抢答模板" destroy-on-close v-model="state.show" center :close-on-click-modal="false" align-center width="94%">
<el-tabs type="border-card">
<el-tab-pane label="模板分组">
<Groups></Groups>
</el-tab-pane>
<el-tab-pane label="模板列表">
<Items></Items>
</el-tab-pane>
</el-tabs>
<template #footer>
<el-button @click="handleCancel"> </el-button>
</template>
</el-dialog>
</template>
<script>
import { reactive } from '@vue/reactivity';
import { watch } from '@vue/runtime-core';
import Groups from './Groups.vue'
import Items from './Items.vue'
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
components: { Groups, Items },
setup(props, { emit }) {
const state = reactive({
show: props.modelValue,
loading: false,
});
watch(() => state.show, (val) => {
if (!val) {
setTimeout(() => {
emit('update:modelValue', val);
}, 300);
}
});
const handleCancel = () => {
state.show = false;
}
return {
state, handleCancel
}
}
}
</script>
<style lang="stylus" scoped></style>

View File

@@ -0,0 +1,232 @@
<template>
<div class="snatchs-items-wrap flex flex-nowrap flex-column">
<div class="flex-1">
<div class="prevs-wrap">
<el-form ref="formDom" :rules="state.rules" :model="state.currentItem" label-width="0">
<el-form-item>
<el-row class="w-100">
<el-col :span="12">
<el-form-item>
<el-select v-model="state.group" placeholder="选择一个分组" style="width:13rem">
<el-option v-for="item in state.groups" :key="item.ID" :label="item.Name" :value="item.ID" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
<el-select @change="handleItemChange" v-model="state.item" placeholder="选择一个模板" style="width:13rem">
<el-option v-for="item in state.list" :key="item.ID" :label="item.Name" :value="item.ID" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item prop="Name">
<el-input v-model="state.currentItem.Name" placeholder="名称" maxlength="30" show-word-limit />
</el-form-item>
<el-form-item prop="Question">
<el-input type="textarea" resize="none" rows="4" v-model="state.currentItem.Question" placeholder="题目内容" maxlength="250" show-word-limit />
</el-form-item>
<el-form-item>
<el-row class="w-100">
<el-col :span="8">
<el-form-item prop="Type">
<el-select style="width:90%;" v-model="state.currentItem.Type" placeholder="类别">
<el-option v-for="item in state.types" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label-width="0" prop="Repeat">
<el-checkbox v-model="state.currentItem.Repeat" label="重复答题" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="上限" label-width="4rem" prop="Max">
<el-input v-model="state.currentItem.Max" placeholder="最多答题多少次" />
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item v-if="state.currentItem.Type == 1">
<ul class="w-100">
<template v-for="(item,index) in state.currentItem.Options" :key="index">
<li class="flex" style="margin-bottom:.6rem">
<span>{{String.fromCharCode(index+65)}}</span>
<el-input style="width:11rem;" v-model="item.Text" placeholder="" />
<span class="flex-1"></span>
<div>
<el-checkbox size="small" v-model="item.Value" label="答案" style="margin-right:.6rem" />
<el-button size="small" @click="handleAddOption(index)">添加</el-button>
<el-button size="small" @click="handleDelOption(index)">删除</el-button>
</div>
</li>
</template>
</ul>
</el-form-item>
<el-form-item v-if="state.currentItem.Type == 2">
<el-input v-model="state.currentItem.Correct" placeholder="简答题答案" />
</el-form-item>
<el-form-item>
<div class="t-c w-100">
<el-button type="success" :loading="state.loading" @click="handleEditSubmit">确定开始</el-button>
</div>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script>
import { reactive, ref } from '@vue/reactivity';
import { computed, onMounted } from '@vue/runtime-core';
import { ElMessage } from 'element-plus';
import { update } from '@/apis/snatch'
import { injectGlobalData } from '@/views/provide';
import { injectPluginState } from '@/views/device/provide';
export default {
setup() {
const globalData = injectGlobalData();
const pluginState = injectPluginState();
const state = reactive({
loading: false,
group: 0,
item: 0,
currentItem: { ID: 0, Name: '', Type: 1, Question: '', Options: [{ Text: '', Value: false }], Correct: '', Max: 65535, Repeat: true },
rules: {
Name: [
{ required: true, message: '名称必填', trigger: 'blur' }
],
Question: [
{ required: true, message: '内容必填', trigger: 'blur' }
],
},
types: [
{ label: '选择题', value: 1 },
{ label: '简答题', value: 2 },
],
groups: computed(() => {
let user = globalData.value.usernames[globalData.value.username];
if (user && user.Snatchs) {
if (state.group == 0 && user.Snatchs.length > 0) {
state.group = user.Snatchs[0].ID;
}
return user.Snatchs;
}
return [];
}),
list: computed(() => {
let group = state.groups.filter(c => c.ID == state.group)[0];
if (group) {
if (state.item == 0 && group.List.length > 0) {
state.item = group.List[0].ID;
}
return group.List;
}
return [];
})
});
const handleItemChange = () => {
const item = state.list.filter(c => c.ID == state.item)[0] || { ID: 0, Name: '', Type: 1, Question: '', Options: [{ Text: '', Value: false }], Correct: '', Max: 65535, Repeat: true };
state.currentItem.Type = item.Type;
state.currentItem.ID = item.ID;
state.currentItem.Name = item.Name;
state.currentItem.Question = item.Question;
state.currentItem.Options = item.Options;
state.currentItem.Correct = item.Correct;
state.currentItem.Max = item.Max;
state.currentItem.Repeat = item.Repeat;
}
onMounted(() => {
handleItemChange();
});
const formDom = ref(null);
const handleEditSubmit = () => {
formDom.value.validate((valid) => {
if (!valid) {
return;
}
const names = pluginState.value.command.devices.map(c => c.MachineName);
if (names.length == 0) {
ElMessage.error('请至少选择一个设备');
return;
}
const json = JSON.parse(JSON.stringify(state.currentItem));
json.Max = +json.Max;
json.Type = +json.Type;
json.Name = json.Name.replace(/^\s|\s$/g, '');
json.Correct = json.Correct.replace(/^\s|\s$/g, '');
const corrects = json.Options.reduce((arr, value, index) => {
if (value.Value) arr.push(String.fromCharCode(index + 65));
return arr;
}, []).join('');
if (json.Type == 1 && corrects.length <= 0) {
ElMessage.error('至少有一个正确答案');
return;
} else if (json.Type == 2 && !json.Correct) {
ElMessage.error('请输入正确答案');
return;
}
const question = {
Type: json.Type,
Question: json.Question,
Correct: json.Type == 1 ? corrects : json.Correct,
Option: json.Options.length,
Max: json.Max,
End: false,
Repeat: json.Repeat,
Join: names.length,
Right: 0,
Wrong: 0,
}
state.loading = true;
update(names, question).then((res) => {
state.loading = false;
if (res) {
ElMessage.success('操作成功!');
} else {
ElMessage.error('操作失败!');
}
}).catch((e) => {
state.loading = false;
ElMessage.error('操作失败!');
});
});
}
const handleAddOption = (index) => {
if (state.currentItem.Options.length >= 6) return;
state.currentItem.Options.splice(index + 1, 0, { Text: '', Value: false });
}
const handleDelOption = (index) => {
if (state.currentItem.Options.length <= 1) return;
state.currentItem.Options.splice(index, 1);
}
return { state, formDom, handleEditSubmit, handleItemChange, handleAddOption, handleDelOption }
}
}
</script>
<style lang="stylus" scoped>
.snatchs-items-wrap {
.head {
width: 100%;
padding-bottom: 1rem;
}
.prevs-wrap {
height: 100%;
position: relative;
}
}
</style>

View File

@@ -0,0 +1,232 @@
<template>
<div class="snatchs-items-wrap flex flex-nowrap flex-column">
<div class="flex-1">
<div class="prevs-wrap">
<el-form ref="formDom" :rules="state.rules" :model="state.currentItem" label-width="0">
<el-form-item>
<el-row class="w-100">
<el-col :span="12">
<el-form-item>
<el-select v-model="state.group" placeholder="选择一个分组" style="width:13rem">
<el-option v-for="item in state.groups" :key="item.ID" :label="item.Name" :value="item.ID" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
<el-select @change="handleItemChange" v-model="state.item" placeholder="选择一个模板" style="width:13rem">
<el-option v-for="item in state.list" :key="item.ID" :label="item.Name" :value="item.ID" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item prop="Name">
<el-input v-model="state.currentItem.Name" placeholder="名称" maxlength="30" show-word-limit />
</el-form-item>
<el-form-item prop="Question">
<el-input type="textarea" resize="none" rows="4" v-model="state.currentItem.Question" placeholder="题目内容" maxlength="250" show-word-limit />
</el-form-item>
<el-form-item>
<el-row class="w-100">
<el-col :span="8">
<el-form-item prop="Type">
<el-select style="width:90%;" v-model="state.currentItem.Type" placeholder="类别">
<el-option v-for="item in state.types" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label-width="0" prop="Repeat">
<el-checkbox v-model="state.currentItem.Repeat" label="重复答题" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="上限" label-width="4rem" prop="Max">
<el-input v-model="state.currentItem.Max" placeholder="最多答题多少次" />
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item v-if="state.currentItem.Type == 1">
<ul class="w-100">
<template v-for="(item,index) in state.currentItem.Options" :key="index">
<li class="flex" style="margin-bottom:.6rem">
<span>{{String.fromCharCode(index+65)}}</span>
<el-input style="width:11rem;" v-model="item.Text" placeholder="" />
<span class="flex-1"></span>
<div>
<el-checkbox size="small" v-model="item.Value" label="答案" style="margin-right:.6rem" />
<el-button size="small" @click="handleAddOption(index)">添加</el-button>
<el-button size="small" @click="handleDelOption(index)">删除</el-button>
</div>
</li>
</template>
</ul>
</el-form-item>
<el-form-item v-if="state.currentItem.Type == 2">
<el-input v-model="state.currentItem.Correct" placeholder="简答题答案" />
</el-form-item>
<el-form-item>
<div class="t-c w-100">
<el-button type="success" :loading="state.loading" @click="handleEditSubmit">确定开始</el-button>
</div>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script>
import { reactive, ref } from '@vue/reactivity';
import { computed, onMounted } from '@vue/runtime-core';
import { ElMessage } from 'element-plus';
import { update } from '@/apis/snatch'
import { injectGlobalData } from '@/views/provide';
import { injectPluginState } from '@/views/device/provide';
export default {
setup() {
const globalData = injectGlobalData();
const pluginState = injectPluginState();
const state = reactive({
loading: false,
group: 0,
item: 0,
currentItem: { ID: 0, Name: '', Type: 1, Question: '', Options: [{ Text: '', Value: false }], Correct: '', Max: 65535, Repeat: true },
rules: {
Name: [
{ required: true, message: '名称必填', trigger: 'blur' }
],
Question: [
{ required: true, message: '内容必填', trigger: 'blur' }
],
},
types: [
{ label: '选择题', value: 1 },
{ label: '简答题', value: 2 },
],
groups: computed(() => {
let user = globalData.value.usernames[globalData.value.username];
if (user && user.Snatchs) {
if (state.group == 0 && user.Snatchs.length > 0) {
state.group = user.Snatchs[0].ID;
}
return user.Snatchs;
}
return [];
}),
list: computed(() => {
let group = state.groups.filter(c => c.ID == state.group)[0];
if (group) {
if (state.item == 0 && group.List.length > 0) {
state.item = group.List[0].ID;
}
return group.List;
}
return [];
})
});
const handleItemChange = () => {
const item = state.list.filter(c => c.ID == state.item)[0] || { ID: 0, Name: '', Type: 1, Question: '', Options: [{ Text: '', Value: false }], Correct: '', Max: 65535, Repeat: true };
state.currentItem.Type = item.Type;
state.currentItem.ID = item.ID;
state.currentItem.Name = item.Name;
state.currentItem.Question = item.Question;
state.currentItem.Options = item.Options;
state.currentItem.Correct = item.Correct;
state.currentItem.Max = item.Max;
state.currentItem.Repeat = item.Repeat;
}
onMounted(() => {
handleItemChange();
});
const formDom = ref(null);
const handleEditSubmit = () => {
formDom.value.validate((valid) => {
if (!valid) {
return;
}
const names = pluginState.value.command.devices.map(c => c.MachineName);
if (names.length == 0) {
ElMessage.error('请至少选择一个设备');
return;
}
const json = JSON.parse(JSON.stringify(state.currentItem));
json.Max = +json.Max;
json.Type = +json.Type;
json.Name = json.Name.replace(/^\s|\s$/g, '');
json.Correct = json.Correct.replace(/^\s|\s$/g, '');
const corrects = json.Options.reduce((arr, value, index) => {
if (value.Value) arr.push(String.fromCharCode(index + 65));
return arr;
}, []).join('');
if (json.Type == 1 && corrects.length <= 0) {
ElMessage.error('至少有一个正确答案');
return;
} else if (json.Type == 2 && !json.Correct) {
ElMessage.error('请输入正确答案');
return;
}
const question = {
Type: json.Type,
Question: json.Question,
Correct: json.Type == 1 ? corrects : json.Correct,
Option: json.Options.length,
Max: json.Max,
End: false,
Repeat: json.Repeat,
Join: names.length,
Right: 0,
Wrong: 0,
}
state.loading = true;
update(names, question).then((res) => {
state.loading = false;
if (res) {
ElMessage.success('操作成功!');
} else {
ElMessage.error('操作失败!');
}
}).catch((e) => {
state.loading = false;
ElMessage.error('操作失败!');
});
});
}
const handleAddOption = (index) => {
if (state.currentItem.Options.length >= 6) return;
state.currentItem.Options.splice(index + 1, 0, { Text: '', Value: false });
}
const handleDelOption = (index) => {
if (state.currentItem.Options.length <= 1) return;
state.currentItem.Options.splice(index, 1);
}
return { state, formDom, handleEditSubmit, handleItemChange, handleAddOption, handleDelOption }
}
}
</script>
<style lang="stylus" scoped>
.snatchs-items-wrap {
.head {
width: 100%;
padding-bottom: 1rem;
}
.prevs-wrap {
height: 100%;
position: relative;
}
}
</style>

View File

@@ -0,0 +1,50 @@
<template>
<el-dialog class="options" title="使用互动答题" destroy-on-close v-model="state.show" center :close-on-click-modal="false" align-center width="94%">
<div style="border:1px solid #ddd;padding:.6rem">
<Info v-if="running"></Info>
<Item v-else></Item>
</div>
<template #footer>
<el-button @click="handleCancel"> </el-button>
</template>
</el-dialog>
</template>
<script>
import { reactive } from '@vue/reactivity';
import { computed, watch } from '@vue/runtime-core';
import Item from './Item.vue'
import Info from './Info.vue'
import { injectPluginState } from '@/views/device/provide';
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
components: { Item, Info },
setup(props, { emit }) {
const pluginState = injectPluginState();
console.log(pluginState.value);
const running = computed(() => pluginState.value.ShareSnatch.Question.End == false);
const state = reactive({
show: props.modelValue,
loading: false,
});
watch(() => state.show, (val) => {
if (!val) {
setTimeout(() => {
emit('update:modelValue', val);
}, 300);
}
});
const handleCancel = () => {
state.show = false;
}
return {
running, state, handleCancel
}
}
}
</script>
<style lang="stylus" scoped></style>

View File

@@ -115,13 +115,13 @@ export default {
</script>
<style lang="stylus" scoped>
@media (min-width: 768px) {
.device-item {
width: 39rem !important;
background-color: rgba(255, 255, 255, 1) !important;
}
}
// @media (min-width: 768px) {
// .device-item {
// width: 39rem !important;
// background-color: rgba(255, 255, 255, 1) !important;
// margin: 0 0 0.6rem 0 !important;
// }
// }
.device-item {
// border: 1px solid #ddd;
font-size: 1.6rem;

View File

@@ -112,6 +112,9 @@ export default {
globalData.value.currentDevice = globalData.value.devices[middleItem.index];
}
if (globalData.value.pc) {
} else {
for (let i = 0; i < items.length; i++) {
let style = 'z-index:9;background-color:rgba(255,255,255,1);';
const dist = Math.abs((middleItem.index - i));
@@ -126,6 +129,8 @@ export default {
}
items[i].style = style;
}
}
//有哪些需要报告
const reportDoms = doms.filter(item => item.index >= middleItem.index - 2 && item.index <= middleItem.index + 2).map(c => c.index);

View File

@@ -20,10 +20,10 @@ export const provideGlobalData = () => {
}),
currentDevice: { MachineName: '' },
reportNames: [],
pc: window.innerWidth > 768
pc: false,//window.innerWidth > 768
});
window.addEventListener('resize', () => {
globalData.value.pc = window.innerWidth > 768;
globalData.value.pc = false;// window.innerWidth > 768;
});
provide(globalDataSymbol, globalData);

View File

@@ -38,6 +38,8 @@ using System.Text.Json.Serialization;
using cmonitor.server.client.reports.keyboard;
using cmonitor.server.client.reports.wallpaper;
using common.libs.winapis;
using cmonitor.server.client.reports.snatch;
using cmonitor.server.service.messengers.snatch;
namespace cmonitor
@@ -198,6 +200,11 @@ namespace cmonitor
else if (OperatingSystem.IsLinux()) serviceCollection.AddSingleton<ISystem, SystemLinux>();
else if (OperatingSystem.IsMacOS()) serviceCollection.AddSingleton<ISystem, SystemMacOS>();
serviceCollection.AddSingleton<SnatchReport>();
if (OperatingSystem.IsWindows()) serviceCollection.AddSingleton<ISnatch, SnatchWindows>();
else if (OperatingSystem.IsLinux()) serviceCollection.AddSingleton<ISnatch, SnatchLinux>();
else if (OperatingSystem.IsMacOS()) serviceCollection.AddSingleton<ISnatch, SnatchMacOS>();
serviceCollection.AddSingleton<ShareReport>();
serviceCollection.AddSingleton<CommandReport>();
@@ -223,6 +230,7 @@ namespace cmonitor
serviceCollection.AddSingleton<SettingMessenger>();
serviceCollection.AddSingleton<KeyboardMessenger>();
serviceCollection.AddSingleton<SystemMessenger>();
serviceCollection.AddSingleton<SnatchMessenger>();
//api
serviceCollection.AddSingleton<RuleConfig>();
@@ -242,6 +250,7 @@ namespace cmonitor
serviceCollection.AddSingleton<SettingClientService>();
serviceCollection.AddSingleton<SystemClientService>();
serviceCollection.AddSingleton<KeyboardClientService>();
serviceCollection.AddSingleton<SnatchClientService>();
//web
@@ -265,7 +274,7 @@ namespace cmonitor
config.ScreenDelay = int.Parse(dic["screen-delay"]);
config.Elevated = dic.ContainsKey("elevated");
Logger.Instance.Debug($"config:{config.ToJson()}");
Logger.Instance.Debug($"config:{config.ToJsonFormat()}");
//Logger.Instance.Debug($"args:{string.Join(" ", args)}");
config.IsCLient = dic.ContainsKey("mode") && dic["mode"].Contains("client");
@@ -371,6 +380,9 @@ namespace cmonitor
public const int ShareMemoryLLockIndex = 3;
//SAS
public const int ShareMemorySASIndex = 4;
//抢答,问题,和回答分开存,为了保证数据安全性
public const int ShareSnatchQuestionIndex = 5;
public const int ShareSnatchAnswerIndex = 6;
}

View File

@@ -1,4 +1,6 @@
using common.libs.database;
using cmonitor.server.client.reports.snatch;
using common.libs.database;
using MemoryPack;
using System.ComponentModel.DataAnnotations.Schema;
namespace cmonitor.server.api
@@ -16,8 +18,9 @@ namespace cmonitor.server.api
{
UserNames = new Dictionary<string, UserNameInfo> { { "snltty", new UserNameInfo {
Rules = new List<RulesInfo>{ new RulesInfo { ID = 1, Name = "默认" } },
Processs = new List<GroupInfo>{ new GroupInfo { ID = 1, Name = "默认" } },
Windows = new List<WindowGroupInfo> { new WindowGroupInfo { ID = 1, Name = "默认" } },
Processs = new List<GroupInfo>{ new GroupInfo { ID = 2, Name = "默认" } },
Windows = new List<WindowGroupInfo> { new WindowGroupInfo { ID = 3, Name = "默认" } },
Snatchs = new List<SnatchGroupInfo>{ new SnatchGroupInfo { ID=4, Name="默认" } }
} } }
};
UserNames = config.UserNames;
@@ -26,7 +29,7 @@ namespace cmonitor.server.api
}
public Dictionary<string, UserNameInfo> UserNames { get; set; } = new Dictionary<string, UserNameInfo>();
private uint maxid = 0;
private uint maxid = 100;
public uint MaxID
{
get => maxid; set
@@ -341,6 +344,122 @@ namespace cmonitor.server.api
return string.Empty;
}
public string AddSnatchGroup(UpdateSnatchGroupInfo updateGroupInfo)
{
lock (lockObj)
{
if (UserNames.TryGetValue(updateGroupInfo.UserName, out UserNameInfo userNameInfo) == false)
{
return "不存在此管理用户";
}
if (userNameInfo.Windows.FirstOrDefault(c => c.Name == updateGroupInfo.Group.Name && c.ID != updateGroupInfo.Group.ID) != null)
{
return "已存在同名记录";
}
//添加
if (updateGroupInfo.Group.ID == 0)
{
updateGroupInfo.Group.ID = Interlocked.Increment(ref maxid);
userNameInfo.Snatchs.Add(updateGroupInfo.Group);
Save();
return string.Empty;
}
//修改
SnatchGroupInfo old = userNameInfo.Snatchs.FirstOrDefault(c => c.ID == updateGroupInfo.Group.ID);
if (old == null)
{
return "不存在记录,无法修改";
}
old.Name = updateGroupInfo.Group.Name;
Save();
}
return string.Empty;
}
public string DeleteSnatchGroup(DeleteSnatchGroupInfo deleteGroupInfo)
{
lock (lockObj)
{
if (UserNames.TryGetValue(deleteGroupInfo.UserName, out UserNameInfo userNameInfo) == false)
{
return "不存在此管理用户";
}
userNameInfo.Snatchs.Remove(userNameInfo.Snatchs.FirstOrDefault(c => c.ID == deleteGroupInfo.ID));
Save();
}
return string.Empty;
}
public string AddSnatch(AddSnatchItemInfo updateItem)
{
lock (lockObj)
{
if (UserNames.TryGetValue(updateItem.UserName, out UserNameInfo userNameInfo) == false)
{
return "不存在此管理用户";
}
SnatchGroupInfo group = userNameInfo.Snatchs.FirstOrDefault(c => c.ID == updateItem.GroupID);
if (group == null)
{
return "不存在此分组";
}
if (group.List.FirstOrDefault(c => c.Name == updateItem.Item.Name && c.ID != updateItem.Item.ID) != null)
{
return "已存在同名记录";
}
//添加
if (updateItem.Item.ID == 0)
{
updateItem.Item.ID = Interlocked.Increment(ref maxid);
group.List.Add(updateItem.Item);
Save();
return string.Empty;
}
//修改
SnatchItemInfo old = group.List.FirstOrDefault(c => c.ID == updateItem.Item.ID);
if (old == null)
{
return "不存在记录,无法修改";
}
old.Type = updateItem.Item.Type;
old.Name = updateItem.Item.Name;
old.Question = updateItem.Item.Question;
old.Options = updateItem.Item.Options;
old.Correct = updateItem.Item.Correct;
old.Max = updateItem.Item.Max;
old.Repeat = updateItem.Item.Repeat;
Save();
}
return string.Empty;
}
public string DelSnatch(DeletedSnatchItemInfo deletedFileNameInfo)
{
lock (lockObj)
{
if (UserNames.TryGetValue(deletedFileNameInfo.UserName, out UserNameInfo userNameInfo) == false)
{
return "不存在此管理用户";
}
SnatchGroupInfo group = userNameInfo.Snatchs.FirstOrDefault(c => c.ID == deletedFileNameInfo.GroupID);
if (group == null)
{
return "不存在此分组";
}
group.List.Remove(group.List.FirstOrDefault(c => c.ID == deletedFileNameInfo.ID));
Save();
}
return string.Empty;
}
public void Save()
{
configDataProvider.Save(this).Wait();
@@ -353,6 +472,7 @@ namespace cmonitor.server.api
public List<GroupInfo> Processs { get; set; } = new List<GroupInfo>();
public List<string> Devices { get; set; } = new List<string>();
public List<WindowGroupInfo> Windows { get; set; } = new List<WindowGroupInfo>();
public List<SnatchGroupInfo> Snatchs { get; set; } = new List<SnatchGroupInfo>();
}
public sealed class UpdateDevicesInfo
{
@@ -464,4 +584,69 @@ namespace cmonitor.server.api
public uint GroupID { get; set; }
public uint ID { get; set; }
}
public sealed class SnatchGroupInfo
{
public uint ID { get; set; }
public string Name { get; set; }
public List<SnatchItemInfo> List { get; set; } = new List<SnatchItemInfo>();
}
public sealed class UpdateSnatchGroupInfo
{
public string UserName { get; set; }
public SnatchGroupInfo Group { get; set; }
}
public sealed class DeleteSnatchGroupInfo
{
public string UserName { get; set; }
public uint ID { get; set; }
}
public sealed class AddSnatchItemInfo
{
public string UserName { get; set; }
public uint GroupID { get; set; }
public SnatchItemInfo Item { get; set; }
}
public sealed class DeletedSnatchItemInfo
{
public string UserName { get; set; }
public uint GroupID { get; set; }
public uint ID { get; set; }
}
public sealed class SnatchItemInfo
{
public uint ID { get; set; }
public string Name { get; set; }
public SnatchType Type { get; set; }
/// <summary>
/// 问题
/// </summary>
public string Question { get; set; }
/// <summary>
/// 选项数
/// </summary>
public List<SnatchItemOptionInfo> Options { get; set; }
/// <summary>
/// 答案
/// </summary>
public string Correct { get; set; }
/// <summary>
/// 最多答题次数
/// </summary>
public int Max { get; set; }
/// <summary>
/// 重复答题
/// </summary>
public bool Repeat { get; set; }
}
public sealed class SnatchItemOptionInfo
{
public string Text { get; set;}
public bool Value { get; set; }
}
}

View File

@@ -19,24 +19,28 @@ namespace cmonitor.server.api.services
public async Task<bool> Update(ClientServiceParamsInfo param)
{
ShareUpdateInfo info = param.Content.DeJson<ShareUpdateInfo>();
if(signCaching.Get(info.Name,out SignCacheInfo cache))
byte[] bytes = MemoryPackSerializer.Serialize(info.Item);
for (int i = 0; i < info.Names.Length; i++)
{
return await messengerSender.SendOnly(new MessageRequestWrap
if (signCaching.Get(info.Names[i], out SignCacheInfo cache))
{
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = cache.Connection,
MessengerId = (ushort)ShareMessengerIds.Update,
Payload = MemoryPackSerializer.Serialize(info.Item)
Payload = bytes
});
}
}
return false;
return true;
}
}
public sealed class ShareUpdateInfo
{
public string Name { get; set; }
public string[] Names { get; set; }
public ShareItemInfo Item { get; set; }
}
}

View File

@@ -0,0 +1,65 @@
using cmonitor.server.client.reports.snatch;
using cmonitor.server.service;
using cmonitor.server.service.messengers.sign;
using cmonitor.server.service.messengers.snatch;
using common.libs.extends;
using MemoryPack;
namespace cmonitor.server.api.services
{
public sealed class SnatchClientService : IClientService
{
private readonly MessengerSender messengerSender;
private readonly RuleConfig ruleConfig;
private readonly SignCaching signCaching;
public SnatchClientService(RuleConfig ruleConfig, MessengerSender messengerSender, SignCaching signCaching)
{
this.ruleConfig = ruleConfig;
this.messengerSender = messengerSender;
this.signCaching = signCaching;
}
public string AddGroup(ClientServiceParamsInfo param)
{
return ruleConfig.AddSnatchGroup(param.Content.DeJson<UpdateSnatchGroupInfo>());
}
public string DeleteGroup(ClientServiceParamsInfo param)
{
return ruleConfig.DeleteSnatchGroup(param.Content.DeJson<DeleteSnatchGroupInfo>());
}
public string Add(ClientServiceParamsInfo param)
{
return ruleConfig.AddSnatch(param.Content.DeJson<AddSnatchItemInfo>());
}
public string Del(ClientServiceParamsInfo param)
{
return ruleConfig.DelSnatch(param.Content.DeJson<DeletedSnatchItemInfo>());
}
public async Task<bool> Update(ClientServiceParamsInfo param)
{
SnatchUpdateInfo info = param.Content.DeJson<SnatchUpdateInfo>();
byte[] bytes = MemoryPackSerializer.Serialize(info.Item);
for (int i = 0; i < info.Names.Length; i++)
{
if (signCaching.Get(info.Names[i], out SignCacheInfo cache))
{
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = cache.Connection,
MessengerId = (ushort)SnatchMessengerIds.Update,
Payload = bytes
});
}
}
return true;
}
}
public sealed class SnatchUpdateInfo
{
public string[] Names { get; set; }
public SnatchQuestionInfo Item { get; set; }
}
}

View File

@@ -55,7 +55,7 @@ namespace cmonitor.server.client
IPAddress[] ips = new IPAddress[] { config.Server };
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
Logger.Instance.Info($"get ip:{ips.ToJson()}");
Logger.Instance.Info($"get ip:{ips.ToJsonFormat()}");
if (ips.Length == 0) return;
foreach (IPAddress ip in ips)

View File

@@ -105,7 +105,7 @@ namespace cmonitor.server.client.reports
if (reportObj.Count > 0)
{
string json = reportObj.ToJsonDefault();
string json = reportObj.ToJson();
byte[] res = MemoryPackSerializer.Serialize(json);
await messengerSender.SendOnly(new MessageRequestWrap
{

View File

@@ -182,7 +182,7 @@ namespace cmonitor.server.client.reports.active
}
activeWindowInfo.Title = buff.ToString();
activeWindowInfo.FileName = filename;
activeWindowInfo.FileName = Path.GetFileName(filename);
activeWindowInfo.Desc = desc;
activeWindowInfo.Pid = id;

View File

@@ -15,7 +15,7 @@ using common.libs.helpers;
namespace cmonitor.server.client.reports.screen
{
public sealed class ScreenWIndowsDxgi
public sealed class ScreenWindowsDxgi
{
private Adapter1 adapter;
private Device mDevice;
@@ -37,10 +37,10 @@ namespace cmonitor.server.client.reports.screen
private readonly Config config;
public ScreenWIndowsDxgi(int whichMonitor, Config config)
public ScreenWindowsDxgi(int whichMonitor, Config config)
: this(0, whichMonitor, config) { }
public ScreenWIndowsDxgi(int whichGraphicsCardAdapter, int whichOutputDevice, Config config)
public ScreenWindowsDxgi(int whichGraphicsCardAdapter, int whichOutputDevice, Config config)
{
this.whichGraphicsCardAdapter = whichGraphicsCardAdapter;
this.whichOutputDevice = whichOutputDevice;

View File

@@ -7,11 +7,11 @@ namespace cmonitor.server.client.reports.screen
{
public sealed class ScreenWindows : IScreen
{
private readonly ScreenWIndowsDxgi dxgiDesktop;
private readonly ScreenWindowsDxgi dxgiDesktop;
private readonly ScreenWindowsGdi gdiDesktop;
public ScreenWindows(Config config)
{
dxgiDesktop = new ScreenWIndowsDxgi(0, config);
dxgiDesktop = new ScreenWindowsDxgi(0, config);
gdiDesktop = new ScreenWindowsGdi(config);
}

View File

@@ -64,6 +64,10 @@ namespace cmonitor.server.client.reports.share
{
return dic.TryGetValue(key, out item);
}
public bool GetShareRunning(int index)
{
return shareMemory.ReadRunning(index);
}
public void UpdateShare(ShareItemInfo shareItemInfo)
{
if (shareMemory != null)
@@ -78,7 +82,7 @@ namespace cmonitor.server.client.reports.share
private void ShareMemoryStateChanged(int index, ShareMemoryState state)
{
if (index == 0 && state == ShareMemoryState.Closed)
if (index == 0 && (state & ShareMemoryState.Closed) == ShareMemoryState.Closed)
{
shareMemory.WriteClosed(0, false);
shareMemory.WriteRunning(0, false);

View File

@@ -0,0 +1,62 @@
using MemoryPack;
namespace cmonitor.server.client.reports.snatch
{
public interface ISnatch
{
public void Set(SnatchQuestionInfo snatchQuestionInfo);
}
[MemoryPackable]
public sealed partial class SnatchQuestionInfo
{
public SnatchType Type { get; set; }
/// <summary>
/// 问题
/// </summary>
public string Question { get; set; }
/// <summary>
/// 正确答案
/// </summary>
public string Correct { get; set; }
/// <summary>
/// 选项数
/// </summary>
public int Option { get; set; }
/// <summary>
/// 最多答题次数
/// </summary>
public int Max { get; set; } = int.MaxValue;
/// <summary>
/// 已结束
/// </summary>
public bool End { get; set; } = false;
/// <summary>
/// 重复答题
/// </summary>
public bool Repeat { get; set; } = true;
public int Join { get; set; } = 0;
public int Right { get; set; } = 0;
public int Wrong { get; set; } = 0;
}
public sealed class SnatchAnswerInfo
{
public SnatchState State { get; set; }
public bool Result { get; set; }
public long Time { get; set; }
public string ResultStr { get; set; }
}
public enum SnatchState : byte
{
None = 0,
Ask = 1,
Confirm = 2,
}
public enum SnatchType : byte
{
None = 0,
Select = 1,
Input = 2,
}
}

View File

@@ -0,0 +1,9 @@
namespace cmonitor.server.client.reports.snatch
{
public sealed class SnatchLinux : ISnatch
{
public void Set(SnatchQuestionInfo snatchQuestionInfo)
{
}
}
}

View File

@@ -0,0 +1,9 @@
namespace cmonitor.server.client.reports.snatch
{
public sealed class SnatchMacOS : ISnatch
{
public void Set(SnatchQuestionInfo snatchQuestionInfo)
{
}
}
}

View File

@@ -0,0 +1,25 @@
namespace cmonitor.server.client.reports.snatch
{
public sealed class SnatchReport : IReport
{
public string Name => "Snatch";
private readonly ISnatch snatch;
public SnatchReport(ISnatch snatch)
{
this.snatch = snatch;
snatch.Set(new SnatchQuestionInfo { Correct = string.Empty, End = true, Join=0, Max=0, Option=0, Question=string.Empty, Repeat=false, Right=0, Type= SnatchType.Select,Wrong=0});
}
public object GetReports(ReportType reportType)
{
return null;
}
public void Update(SnatchQuestionInfo snatchQuestionInfo)
{
snatch.Set(snatchQuestionInfo);
}
}
}

View File

@@ -0,0 +1,49 @@
using cmonitor.server.client.reports.share;
using common.libs;
using common.libs.extends;
namespace cmonitor.server.client.reports.snatch
{
public sealed class SnatchWindows : ISnatch
{
private readonly Config config;
private readonly ShareReport shareReport;
public SnatchWindows(Config config, ShareReport shareReport)
{
this.config = config;
this.shareReport = shareReport;
}
public void Set(SnatchQuestionInfo snatchQuestionInfo)
{
bool running = shareReport.GetShareRunning(Config.ShareSnatchQuestionIndex);
//更新问题
shareReport.UpdateShare(new ShareItemInfo
{
Index = Config.ShareSnatchQuestionIndex,
Key = "SnatchQuestion",
Value = snatchQuestionInfo.ToJson()
});
//未启动,并且未结束
if (running == false && snatchQuestionInfo.End == false)
{
//启动
shareReport.UpdateShare(new ShareItemInfo
{
Index = Config.ShareSnatchAnswerIndex,
Key = "SnatchAnswer",
Value = new SnatchAnswerInfo
{
Result = false,
ResultStr = string.Empty,
State = SnatchState.Ask,
Time = 0,
}.ToJson()
});
CommandHelper.Windows(string.Empty, new string[] {
$"start cmonitor.snatch.win.exe {config.ShareMemoryKey} {config.ShareMemoryLength} {config.ShareMemoryItemSize} {Config.ShareSnatchQuestionIndex} {Config.ShareSnatchAnswerIndex}"
});
}
}
}
}

View File

@@ -0,0 +1,23 @@
using cmonitor.server.client.reports.snatch;
using MemoryPack;
namespace cmonitor.server.service.messengers.snatch
{
public sealed class SnatchMessenger : IMessenger
{
private readonly SnatchReport snatchReport;
public SnatchMessenger(SnatchReport snatchReport)
{
this.snatchReport = snatchReport;
}
[MessengerId((ushort)SnatchMessengerIds.Update)]
public void Update(IConnection connection)
{
SnatchQuestionInfo question = MemoryPackSerializer.Deserialize<SnatchQuestionInfo>(connection.ReceiveRequestWrap.Payload.Span);
snatchReport.Update(question);
}
}
}

View File

@@ -0,0 +1,9 @@
namespace cmonitor.server.service.messengers.snatch
{
public enum SnatchMessengerIds : ushort
{
Update = 1600,
None = 1699
}
}

View File

@@ -85,7 +85,7 @@ namespace common.libs.database
//Logger.Instance.Error($"save:{fileName}");
//Logger.Instance.Error(model.ToJsonIndented());
await File.WriteAllTextAsync(fileName, model.ToJsonIndented(), Encoding.UTF8).ConfigureAwait(false);
await File.WriteAllTextAsync(fileName, model.ToJsonFormat(), Encoding.UTF8).ConfigureAwait(false);
}
catch (Exception)
{

View File

@@ -24,18 +24,14 @@ namespace common.libs.extends
WriteIndented = true,
Converters = { new IPAddressJsonConverter(), new IPEndpointJsonConverter(), new DateTimeConverter() }
};
public static string ToJsonDefault(this object obj)
public static string ToJson(this object obj)
{
return JsonSerializer.Serialize(obj);
}
public static string ToJson(this object obj)
public static string ToJsonFormat(this object obj)
{
return JsonSerializer.Serialize(obj, jsonSerializerOptions);
}
public static string ToJsonIndented(this object obj)
{
return JsonSerializer.Serialize(obj, jsonSerializerOptionsIndented);
}
public static T DeJson<T>(this string json)
{
return JsonSerializer.Deserialize<T>(json, options: jsonSerializerOptions);