Browse Source

Merge branch 'master' of WEP/App_V2.3 into dev

YueYunyun 1 year ago
parent
commit
caeff8d022
100 changed files with 7648 additions and 110 deletions
  1. 1 1
      .gitignore
  2. 1 1
      SourceCode/WeApp.Core/Configuration/IwbConsts.cs
  3. 11 10
      SourceCode/WeApp.Web/App_Start/RouteConfig.cs
  4. 658 0
      SourceCode/WeApp.Web/Content/Css/ExerciseV2/screen.css
  5. 0 0
      SourceCode/WeApp.Web/Content/Css/ExerciseV2/screen.min.css
  6. 205 0
      SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-bg.css
  7. 0 0
      SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-bg.min.css
  8. 213 0
      SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-cmd.css
  9. 0 0
      SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-cmd.min.css
  10. 293 0
      SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-cmd_leader.css
  11. 0 0
      SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-cmd_leader.min.css
  12. 392 0
      SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-zhb.css
  13. 0 0
      SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-zhb.min.css
  14. 1 1
      SourceCode/WeApp.Web/Content/Css/Home.css
  15. 1 1
      SourceCode/WeApp.Web/Content/Css/Home.min.css
  16. 82 80
      SourceCode/WeApp.Web/Content/Css/iwb.style.css
  17. 0 0
      SourceCode/WeApp.Web/Content/Css/iwb.style.min.css
  18. BIN
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/bg.jpg
  19. BIN
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/bg.png
  20. BIN
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/bg_header_ruler.png
  21. BIN
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/btn-active.png
  22. BIN
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/btn.png
  23. BIN
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/timer_1.png
  24. BIN
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/timer_2.png
  25. 48 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/title_icon.svg
  26. 5 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/toggle.svg
  27. 1 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/wait_leader.svg
  28. 3 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/wait_public.svg
  29. 2 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/wait_stu.svg
  30. 3 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/wait_zhb.svg
  31. 3 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000000251.svg
  32. 1 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000000252.svg
  33. 7 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000000253.svg
  34. 4 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000000254.svg
  35. 3 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000000255.svg
  36. 7 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000010101.svg
  37. 3 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000010102.svg
  38. 2 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-add-role-2.svg
  39. 2 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-add-role.svg
  40. 5 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-checkbox-selected.svg
  41. 3 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-checkbox.svg
  42. 1 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-user.svg
  43. 14 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon_bg-select.svg
  44. 29 0
      SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon_bg.svg
  45. BIN
      SourceCode/WeApp.Web/Content/Image/Upload/home.png
  46. BIN
      SourceCode/WeApp.Web/Content/Image/bg_V2.png
  47. BIN
      SourceCode/WeApp.Web/Content/Image/logo.png
  48. BIN
      SourceCode/WeApp.Web/Content/Image/logo_bk.png
  49. BIN
      SourceCode/WeApp.Web/Content/Image/logo_bk2.png
  50. 6 0
      SourceCode/WeApp.Web/Content/Image/tittle_logo.svg
  51. 8 0
      SourceCode/WeApp.Web/Content/Scss/ExerciseV2/_stu-body.scss
  52. 34 0
      SourceCode/WeApp.Web/Content/Scss/ExerciseV2/_stu-box.scss
  53. 36 0
      SourceCode/WeApp.Web/Content/Scss/ExerciseV2/_stu-box_header.scss
  54. 119 0
      SourceCode/WeApp.Web/Content/Scss/ExerciseV2/_stu-form.scss
  55. 610 0
      SourceCode/WeApp.Web/Content/Scss/ExerciseV2/screen.scss
  56. 118 0
      SourceCode/WeApp.Web/Content/Scss/ExerciseV2/stu-bg.scss
  57. 58 0
      SourceCode/WeApp.Web/Content/Scss/ExerciseV2/stu-cmd.scss
  58. 152 0
      SourceCode/WeApp.Web/Content/Scss/ExerciseV2/stu-cmd_leader.scss
  59. 280 0
      SourceCode/WeApp.Web/Content/Scss/ExerciseV2/stu-zhb.scss
  60. 11 9
      SourceCode/WeApp.Web/Content/Scss/style.scss
  61. 11 0
      SourceCode/WeApp.Web/Content/V2/Config/Screen/JY2001000010027.json
  62. BIN
      SourceCode/WeApp.Web/Content/V2/JY2001000010027/01.mp4
  63. BIN
      SourceCode/WeApp.Web/Content/V2/JY2001000010027/desc.png
  64. 803 0
      SourceCode/WeApp.Web/Controllers/ExerciseV2Controller.cs
  65. 7 6
      SourceCode/WeApp.Web/Controllers/PlayController.cs
  66. 18 0
      SourceCode/WeApp.Web/Properties/PublishProfiles/WeApp.pubxml
  67. 1 1
      SourceCode/WeApp.Web/Resources/Guids.iwbx
  68. 66 0
      SourceCode/WeApp.Web/Views/ExerciseV2/Display.cshtml
  69. 106 0
      SourceCode/WeApp.Web/Views/ExerciseV2/Enter.cshtml
  70. 208 0
      SourceCode/WeApp.Web/Views/ExerciseV2/Gis.cshtml
  71. 360 0
      SourceCode/WeApp.Web/Views/ExerciseV2/Headquarter.cshtml
  72. 50 0
      SourceCode/WeApp.Web/Views/ExerciseV2/Index.cshtml
  73. 342 0
      SourceCode/WeApp.Web/Views/ExerciseV2/Leader.cshtml
  74. 68 0
      SourceCode/WeApp.Web/Views/ExerciseV2/Manual.cshtml
  75. 71 0
      SourceCode/WeApp.Web/Views/ExerciseV2/Public.cshtml
  76. 75 0
      SourceCode/WeApp.Web/Views/ExerciseV2/PublicWait.cshtml
  77. 22 0
      SourceCode/WeApp.Web/Views/ExerciseV2/ScreenModel.cs
  78. 61 0
      SourceCode/WeApp.Web/Views/ExerciseV2/SpecWait.cshtml
  79. 361 0
      SourceCode/WeApp.Web/Views/ExerciseV2/Specialist.cshtml
  80. 9 0
      SourceCode/WeApp.Web/Views/ExerciseV2/StuReport.cshtml
  81. 175 0
      SourceCode/WeApp.Web/Views/ExerciseV2/Student.cshtml
  82. 93 0
      SourceCode/WeApp.Web/Views/ExerciseV2/WaitRport.cshtml
  83. 318 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/Index.cshtml
  84. 318 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/IndexWithScore.cshtml
  85. 136 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/Wait.cshtml
  86. 1 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_.cshtml
  87. 7 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Center_1.cshtml
  88. 3 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Center_2.cshtml
  89. 8 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Center_3.cshtml
  90. 5 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Left_1.cshtml
  91. 22 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Left_2.cshtml
  92. 4 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Left_3.cshtml
  93. 83 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Right_1.cshtml
  94. 4 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Right_2.cshtml
  95. 1 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Right_3.cshtml
  96. 39 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Top.cshtml
  97. 26 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/__/_AreaBox.cshtml
  98. 64 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/__/_BaseInfo.cshtml
  99. 138 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/__/_Chart_Cmd.cshtml
  100. 128 0
      SourceCode/WeApp.Web/Views/ExerciseV2/_Play/__/_Chart_Cmd2.cshtml

+ 1 - 1
.gitignore

@@ -184,7 +184,7 @@ publish/
 *.azurePubxml
 # Note: Comment the next line if you want to checkin your web deploy settings,
 # but database connection strings (with potential passwords) will be unencrypted
-*.pubxml
+# *.pubxml
 *.publishproj
 
 # Microsoft Azure Web App publish settings. Comment the next line if you want to

+ 1 - 1
SourceCode/WeApp.Core/Configuration/IwbConsts.cs

@@ -19,7 +19,7 @@ namespace WeApp.Configuration
         public const string ConnectionStringName = "Default";
         public const string LocalizationCookieName = "WeApp.Localization.CultureName";
         public const string DefaultLanguage = "zh-Hans";
-        public const string Version = WeEngineConst.Version;
+        public const string Version = "2.2.3";
 
         public const bool MultiTenancyEnabled = false;
         public const bool MultiLanguageEnabled = false;

+ 11 - 10
SourceCode/WeApp.Web/App_Start/RouteConfig.cs

@@ -10,18 +10,19 @@ namespace WeApp
         {
             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
             routes.MapRoute("M", url: "M", defaults: new { controller = "MsgMonitor", action = "Index", id = UrlParameter.Optional });
-            routes.MapRoute("P", url: "P", defaults: new { controller = "Play", action = "Index", id = UrlParameter.Optional });
-            routes.MapRoute("P1", url: "P1", defaults: new { controller = "Play", action = "IndexWithScore", id = UrlParameter.Optional });
-            routes.MapRoute("E", url: "E/{id}", defaults: new { controller = "Exercise", action = "ExportSql", id = UrlParameter.Optional });
+            routes.MapRoute("P", url: "P", defaults: new { controller = "ExerciseV2", action = "Play", id = UrlParameter.Optional });
+            routes.MapRoute("P1", url: "P1", defaults: new { controller = "ExerciseV2", action = "PlayWithScore", id = UrlParameter.Optional });
+            routes.MapRoute("E", url: "E/{id}", defaults: new { controller = "ExerciseV2", action = "ExportSql", id = UrlParameter.Optional });
             routes.MapRoute("T", url: "T/{id}", defaults: new { controller = "Home", action = "Test", id = UrlParameter.Optional });
             routes.MapRoute("R", url: "R", defaults: new { controller = "Home", action = "Refresh", id = UrlParameter.Optional });
-            routes.MapRoute("G", url: "G", defaults: new { controller = "Exercise", action = "Gis", id = UrlParameter.Optional });
-            routes.MapRoute("D", url: "D", defaults: new { controller = "Exercise", action = "Display", id = UrlParameter.Optional });
-            routes.MapRoute("Gis", url: "Gis", defaults: new { controller = "Exercise", action = "Gis", id = UrlParameter.Optional });
-            routes.MapRoute("Qs", url: "Qs/{id}", defaults: new { controller = "Exercise", action = "QueryScore", id = UrlParameter.Optional });
-            routes.MapRoute("QueryScore", url: "QueryScore/{id}", defaults: new { controller = "Exercise", action = "QueryScore", id = UrlParameter.Optional });
-            routes.MapRoute("Select", "S", new { controller = "Exercise", action = "Select", id = 0 });
-            routes.MapRoute("SelectPage", "S{id}", new { controller = "Exercise", action = "Select", id = 0 });
+            routes.MapRoute("G", url: "G", defaults: new { controller = "ExerciseV2", action = "Gis", id = UrlParameter.Optional });
+            routes.MapRoute("D", url: "D", defaults: new { controller = "ExerciseV2", action = "Display", id = UrlParameter.Optional });
+            routes.MapRoute("Gis", url: "Gis", defaults: new { controller = "ExerciseV2", action = "Gis", id = UrlParameter.Optional });
+            routes.MapRoute("Qs", url: "Qs/{id}", defaults: new { controller = "ExerciseV2", action = "QueryScore", id = UrlParameter.Optional });
+            routes.MapRoute("QueryScore", url: "QueryScore/{id}", defaults: new { controller = "ExerciseV2", action = "QueryScore", id = UrlParameter.Optional });
+            routes.MapRoute("SelectV1", "SV1", new { controller = "Exercise", action = "Select", id = 0 });
+            routes.MapRoute("Select", "S", new { controller = "ExerciseV2", action = "Select", id = 0 });
+            routes.MapRoute("SelectPage", "S{id}", new { controller = "ExerciseV2", action = "Select", id = 0 });
 
             //ASP.NET Web API Route Config
             routes.MapHttpRoute(

+ 658 - 0
SourceCode/WeApp.Web/Content/Css/ExerciseV2/screen.css

@@ -0,0 +1,658 @@
+@charset "UTF-8";
+body {
+  --mc: #03AEBC;
+  --tc: #36536D;
+  --brc: #80BAEF;
+  --bgc: #e9f2fd;
+  --bg: rgba(255,255,255,0.4);
+  font-family: "Source Han Sans";
+}
+
+.box .box-header {
+  display: flex;
+  height: 35px;
+  width: 100%;
+  /*background: linear-gradient(90deg,#b2d9f5,#c2d8f4);*/
+  background: linear-gradient(90deg, rgba(82, 197, 233, 0.411765) 0%, rgba(59, 221, 243, 0.12549) 35.99%, rgba(67, 219, 252, 0.00784314) 87.02%);
+  box-shadow: inset 0px -2px 4px rgba(117, 185, 248, 0.52);
+  border-bottom: 1px solid rgba(98, 156, 215, 0.05);
+  position: relative;
+}
+.box .box-header .title {
+  --w: 47px;
+  display: flex;
+  position: relative;
+  height: 100%;
+  padding-left: 5px;
+  margin-left: var(--w);
+  font-size: 18px;
+  font-weight: 600;
+  color: #094B88;
+  align-items: center;
+}
+.box .box-header .title:before {
+  content: "";
+  position: absolute;
+  left: calc(1px - var(--w));
+  height: 100%;
+  width: var(--w);
+  background: url("/Content/Image/ExerciseV2/title_icon.svg") no-repeat;
+  background-size: 100% 100%;
+}
+
+.btn {
+  margin: 5px auto;
+  padding: 8px 20px;
+  background: #03AEBC;
+  border-radius: 8px;
+  color: #DAEDFF;
+  font-size: 16px;
+}
+.btn.btn-small {
+  font-size: 14px;
+  padding: 3px 12px;
+  border-radius: 4px;
+}
+.btn:hover {
+  background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
+  border-color: rgba(0, 0, 0, 0.1);
+  color: #DAEDFF;
+  font-weight: 600;
+}
+
+.vb-btn {
+  color: #176bbc;
+  font-size: 14px;
+  padding: 5px 10px;
+  background-image: url("/Content/Image/ExerciseV2/btn.png");
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  font-weight: 600;
+}
+.vb-btn.active {
+  color: #fff;
+  background-image: url("/Content/Image/ExerciseV2/btn-active.png");
+}
+
+.form .form-control {
+  background: var(--bgc);
+  color: var(--tc);
+  border-radius: 4px;
+  border: 1px solid #b5cce2;
+  font-size: 1rem;
+}
+.form .form-control:active, .form .form-control:focus {
+  border: 1px solid var(--brc);
+}
+
+.select2-container {
+  --h: 38px;
+  font-size: 1rem;
+}
+.select2-container.select2-container--open .select2-selection--single {
+  border-color: var(--brc);
+}
+.select2-container .select2-selection--single {
+  border-radius: 4px !important;
+  background: var(--bgc);
+  border: 1px solid #b5cce2;
+  height: var(--h);
+  line-height: var(--h);
+  color: var(--tc);
+  padding: 0 15px;
+}
+.select2-container .select2-selection--single .select2-selection__rendered {
+  color: inherit;
+  margin: 0;
+  padding: 0;
+  line-height: var(--h);
+  font-size: 1rem;
+}
+.select2-container .select2-selection--single .select2-selection__arrow {
+  right: 8px;
+  height: var(--h);
+}
+.select2-container .select2-dropdown {
+  background: #fefefe;
+  border: 1px solid #b5cce2;
+  border-radius: 4px;
+  margin-top: 2px;
+}
+.select2-container .select2-dropdown .select2-search {
+  padding: 8px 10px;
+}
+.select2-container .select2-dropdown .select2-search .select2-search__field {
+  border: 1px solid var(--brc);
+  background: var(--bgc);
+  color: var(--tc);
+  border-radius: 4px;
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option {
+  color: var(--tc);
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option.select2-results__option--highlighted {
+  background: #e7edf4;
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option.select2-results__option[aria-selected=true] {
+  background: #ecf3fc;
+  font-weight: 600;
+}
+
+body {
+  /*background: #3c3c3c;*/
+  /*background: #005f61;*/
+  /*background-image: linear-gradient(150deg,#005f61,#007f61);*/
+  font-size: 22px;
+  color: var(--mc);
+  width: 100vw;
+}
+
+.main-box {
+  border-left: 0;
+  box-sizing: padding-box;
+  padding: 0;
+  margin: 0;
+}
+.main-box .main-box-body {
+  margin: 0 30px 0;
+}
+.main-box > div {
+  /* width: calc(100vw - 20px) !important;
+  height: calc(100% - 20px) !important;*/
+  display: flex;
+  flex: 1 auto;
+}
+
+.box {
+  margin: 0 10px;
+  font-size: 1.25rem;
+}
+.box > .box {
+  margin: 0;
+  width: 100%;
+  height: 100%;
+  border: 2px solid #fff;
+  border-radius: 10px;
+}
+.box .box-header {
+  height: 50px;
+  border-radius: 10px 10px 0 0;
+  position: relative;
+}
+.box .box-header .title {
+  --w: 50px;
+  font-size: 22px;
+}
+.box .box-body {
+  background: linear-gradient(174.54deg, rgba(204, 224, 248, 0.76) 0.23%, rgba(242, 249, 255, 0.76) 120.71%);
+  overflow-y: auto;
+  margin: 0;
+  padding: 10px 10px 0;
+  height: calc(100% - 50px);
+}
+.box .scene-box-body {
+  background: linear-gradient(174.54deg, rgba(204, 224, 248, 0.76) 0.23%, rgba(242, 249, 255, 0.76) 120.71%);
+  overflow-y: auto;
+  margin: 0;
+  padding: 10px 10px 0;
+  height: calc(100% - 50px);
+}
+.box .scene-box-body .box-body {
+  background: none;
+  margin: 0;
+  padding: 0;
+}
+.box .media-box {
+  width: 98%;
+  margin: 10px auto 0;
+  border: 2px solid #4585CC;
+  border-radius: 5px;
+  background: var(--bg);
+}
+.box .media-box .body-content {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+  /*max-width: 100%;
+  max-height: 100%;*/
+  overflow: hidden;
+}
+.box .media-box .body-content span {
+  color: #4585CC;
+  font-size: 30px;
+  font-weight: 600;
+  display: block;
+}
+.box .media-box .body-content img, .box .media-box .body-content video {
+  width: auto;
+  height: 100%;
+  padding: 0;
+  overflow: hidden;
+}
+.box .scene-box {
+  width: 100%;
+  margin: 8px 0;
+  display: flex;
+  flex-direction: column;
+}
+.box .scene-box .title {
+  width: 100%;
+  color: #fff;
+  background: none;
+  position: relative;
+  height: 35px;
+  border-bottom: 2px solid #4585CC;
+}
+.box .scene-box .title span.text {
+  display: inline-block;
+  padding: 0px 20px;
+  line-height: 35px;
+  text-align: center;
+  min-width: 250px;
+  max-width: 300px;
+  background: #4585CC;
+  position: relative;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  font-weight: 600;
+}
+.box .scene-box .title span.text:after {
+  position: absolute;
+  content: "";
+  right: -1px;
+  height: 0;
+  width: 0;
+  border-left: 10px solid #4585CC;
+  border-bottom: 18px solid #4585CC;
+  border-right: 10px solid none;
+  border-top: 18px solid none;
+}
+.box .scene-box .title .attach-box, .box .scene-box .title .tool {
+  position: absolute;
+  font-size: 0.875rem;
+  right: 40px;
+  top: 5px;
+}
+.box .scene-box .title .tool {
+  right: 10px;
+  top: 10px;
+  color: #4585CC;
+  opacity: 1;
+}
+.box .scene-box .title .attach1 {
+  color: #fff;
+  background: #4585CC;
+  margin: 0 5px;
+  padding: 3px 8px;
+}
+.box .scene-box .guide-box {
+  margin: 10px 0;
+  padding: 10px;
+  background: var(--bg);
+  border-radius: 5px;
+}
+.box .scene-box .guide-box p {
+  margin: 0;
+  font-size: 13px;
+}
+.box .scene-box .guide-box .guide-title {
+  font-weight: 600;
+}
+.box .scene-box .desc {
+  padding: 8px;
+  color: #36536D;
+  background: var(--bg);
+  white-space: normal;
+  word-break: break-word;
+  min-height: 40px;
+  font-size: 16px;
+}
+.box .scene-box.handled {
+  /* --bc: #e5e5e5;
+  --bc2: #108F85;*/
+}
+.box .scene-box.handled .title {
+  background: var(--bc);
+}
+.box .scene-box.handled .title span.text {
+  font-size: 90%;
+  font-weight: 400;
+  background: #4585CC;
+}
+.box .scene-box.handled .title span.text:after {
+  border-left-color: #4585CC;
+  border-bottom-color: #4585CC;
+  border-right-color: none;
+  border-top-color: none;
+}
+.box .scene-box.current-scene {
+  border: 2px solid #4585CC;
+}
+.box .scene-box.flash-scene {
+  animation: flash 2s 10;
+}
+.box .log-box {
+  font-size: 18px;
+  width: calc(100% - 20px);
+  margin: 8px auto;
+  display: flex;
+  align-items: center;
+  justify-content: left;
+  color: #415B73;
+  background: var(--bg);
+  min-height: 60px;
+  padding: 5px 10px;
+  position: relative;
+  /*&:after {
+      content: "";
+      position: absolute;
+      right: 0;
+      bottom: 0;
+      width: 0;
+      height: 0;
+      border-left: 15px solid transparent;
+      border-top: 15px solid transparent;
+      border-right: 15px solid #d9d9d9;
+      border-bottom: 15px solid #d9d9d9;
+  }*/
+}
+.box .log-box .role {
+  font-weight: 600;
+  white-space: normal;
+  color: #415B73;
+  width: 180px;
+  text-align: center;
+}
+.box .log-box .name {
+  word-break: break-word;
+}
+.box .log-box .word {
+  word-break: break-word;
+  text-align: left;
+  width: calc(100% - 200px);
+  color: var(--tc);
+  text-indent: 2em;
+}
+.box .log-box .reviews-box {
+  position: absolute;
+  right: 20px;
+  bottom: 5px;
+  color: var(--mc);
+  cursor: pointer;
+}
+.box .log-box .reviews-box .disabled {
+  cursor: not-allowed;
+}
+.box .log-box .reviews-box i {
+  font-size: 15px;
+  margin: 0 5px;
+}
+.box .log-box .reviews-box .fas:nth-child(1) {
+  color: #007bff;
+}
+.box .log-box .reviews-box .fas:nth-child(2) {
+  color: #dc3545;
+}
+.box .log-box:before {
+  content: "";
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 0;
+  height: 0;
+  border-left: 15px solid transparent;
+  border-top: 15px solid transparent;
+  border-right: 15px solid transparent;
+  border-bottom: 15px solid transparent;
+}
+.box .log-box.send {
+  background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.2), transparent);
+  /*&:after {
+      border-right: 15px solid var(--mc);
+      border-bottom: 15px solid var(--mc);
+  }*/
+}
+.box .log-box.send.specialist {
+  padding: 5px 10px 25px;
+  min-height: 90px;
+}
+.box .log-box.send:before {
+  border-left: 15px solid var(--mc);
+  border-top: 15px solid var(--mc);
+}
+.box .log-box.send .role {
+  color: #03AEBC;
+}
+.box .role-box {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: center;
+}
+.box .role-box .role {
+  width: calc(50% - 20px);
+  height: 50px;
+  margin: 6px;
+  padding: 0 5px;
+  font-size: 14px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  text-align: center;
+  white-space: normal;
+  word-break: break-all;
+  color: var(--tc);
+  border: 1px solid var(--brc);
+  background: var(--bg);
+  border-radius: 5px;
+  cursor: pointer;
+  overflow: hidden;
+}
+.box .score-box {
+  font-size: 14px;
+  margin: 5px;
+  padding: 8px 15px;
+  display: flex;
+  justify-content: space-between;
+  border-radius: 5px;
+  color: #262626;
+  background: var(--bg);
+}
+.box .score-box .name {
+  width: 130px;
+  line-height: 30px;
+  border-radius: 5px;
+  background: #03AEBC;
+  color: #fff;
+  text-align: center;
+  font-size: 100%;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.box .score-box .score {
+  font-size: 110%;
+  font-weight: 600;
+  color: #094B88;
+  line-height: 30px;
+  text-align: center;
+}
+.box .score-box .score span {
+  padding-left: 10px;
+  font-size: 1rem;
+  font-weight: 400;
+}
+.box .score-box.system-score .name {
+  font-size: 16px;
+  font-weight: 600;
+}
+.box.plan-box {
+  margin: 5px 10px;
+  border: 1px solid #a5d4f4;
+  border-radius: 5px 5px 0 0;
+  display: flex;
+  flex-direction: column;
+  background: linear-gradient(174.54deg, rgba(204, 224, 248, 0.76) 0.23%, rgba(242, 249, 255, 0.76) 120.71%);
+}
+.box.plan-box .plan-name {
+  width: 100%;
+  background: #a5d4f4;
+  color: #fff;
+  padding: 5px 0;
+  text-align: center;
+  cursor: pointer;
+}
+.box.plan-box .plan-role {
+  width: 100%;
+  padding: 5px 15px;
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: flex-start;
+}
+.box.plan-box .plan-role .role {
+  width: calc(25% - 12px);
+  height: 50px;
+  font-size: 14px;
+  padding: 0 5px;
+  margin: 6px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  text-align: center;
+  white-space: normal;
+  word-break: break-all;
+  color: var(--tc);
+  border: 1px solid var(--brc);
+  background: var(--bg);
+  border-radius: 5px;
+  cursor: pointer;
+  overflow: hidden;
+}
+
+.modal .modal-header {
+  color: var(--tc);
+}
+.modal .modal-header .close {
+  font-size: 30px;
+}
+.modal .modal-footer {
+  margin: 0 auto;
+}
+.modal .modal-footer .btn-outline-iwb {
+  background: #F5F5F5;
+  color: #36536D;
+  border: 1px solid #F3F3F3;
+}
+.modal .modal-footer .btn-iwb {
+  margin: 0 15px;
+  padding: 8px 20px;
+  background: #03AEBC;
+  border-color: #03AEBC;
+  border-radius: 8px;
+  color: #DAEDFF;
+  font-size: 16px;
+}
+
+.load-box {
+  position: fixed;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 40px;
+  font-weight: 400;
+  color: #094B88;
+  z-index: 1000;
+  background: rgba(255, 255, 255, 0.6);
+}
+.load-box .loading {
+  margin-top: -10%;
+}
+.load-box .loading:after {
+  overflow: hidden;
+  display: inline-block;
+  vertical-align: bottom;
+  animation: ellipsis 2s infinite;
+  content: "…";
+}
+
+.question {
+  display: none;
+}
+.question button {
+  --w: 80px;
+  width: var(--w);
+  height: var(--w);
+  border-radius: var(--w);
+  position: absolute;
+  left: 50%;
+  top: 40%;
+  transform: translateX(-50%) translateY(-50%);
+  z-index: 5;
+  background-color: #dc3545;
+  color: #fff;
+  border: none;
+  outline: none;
+}
+.question button:hover {
+  background-color: #bd2130;
+}
+.question button i {
+  font-size: 40px;
+}
+.question button:not(:hover) {
+  animation: 1s shine ease-in-out infinite;
+}
+.question button:not(:hover) i {
+  animation: 1s flashing ease-in-out infinite;
+}
+
+@keyframes shine {
+  0% {
+    filter: drop-shadow(0 0 12px #dc3545);
+  }
+  50% {
+    filter: drop-shadow(0 0 12px #bd2130);
+  }
+  100% {
+    filter: drop-shadow(0 0 12px #dc3545);
+  }
+}
+@keyframes flashing {
+  0% {
+    opacity: 1;
+    transform: scale(1);
+  }
+  50% {
+    opacity: 0.5;
+    transform: scale(0.9);
+  }
+  100% {
+    opacity: 1;
+    transform: scale(1);
+  }
+}
+@keyframes shine {
+  0% {
+    filter: drop-shadow(0 0 5px #c82333);
+  }
+  100% {
+    filter: drop-shadow(0 0 15px #c82333);
+  }
+}
+@keyframes flash {
+  0% {
+    transform: scale(1, 1.05);
+  }
+  50% {
+    transform: scale(0.98, 0.85);
+  }
+  100% {
+    transform: scale(1, 1.05);
+  }
+}

File diff suppressed because it is too large
+ 0 - 0
SourceCode/WeApp.Web/Content/Css/ExerciseV2/screen.min.css


+ 205 - 0
SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-bg.css

@@ -0,0 +1,205 @@
+body {
+  --mc: #03AEBC;
+  --tc: #36536D;
+  --brc: #80BAEF;
+  --bgc: #e9f2fd;
+  --bg: rgba(255,255,255,0.4);
+  font-family: "Source Han Sans";
+}
+
+.btn {
+  margin: 5px auto;
+  padding: 8px 20px;
+  background: #03AEBC;
+  border-radius: 8px;
+  color: #DAEDFF;
+  font-size: 16px;
+}
+.btn.btn-small {
+  font-size: 14px;
+  padding: 3px 12px;
+  border-radius: 4px;
+}
+.btn:hover {
+  background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
+  border-color: rgba(0, 0, 0, 0.1);
+  color: #DAEDFF;
+  font-weight: 600;
+}
+
+.vb-btn {
+  color: #176bbc;
+  font-size: 14px;
+  padding: 5px 10px;
+  background-image: url("/Content/Image/ExerciseV2/btn.png");
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  font-weight: 600;
+}
+.vb-btn.active {
+  color: #fff;
+  background-image: url("/Content/Image/ExerciseV2/btn-active.png");
+}
+
+.form .form-control {
+  background: var(--bgc);
+  color: var(--tc);
+  border-radius: 4px;
+  border: 1px solid #b5cce2;
+  font-size: 1rem;
+}
+.form .form-control:active, .form .form-control:focus {
+  border: 1px solid var(--brc);
+}
+
+.select2-container {
+  --h: 38px;
+  font-size: 1rem;
+}
+.select2-container.select2-container--open .select2-selection--single {
+  border-color: var(--brc);
+}
+.select2-container .select2-selection--single {
+  border-radius: 4px !important;
+  background: var(--bgc);
+  border: 1px solid #b5cce2;
+  height: var(--h);
+  line-height: var(--h);
+  color: var(--tc);
+  padding: 0 15px;
+}
+.select2-container .select2-selection--single .select2-selection__rendered {
+  color: inherit;
+  margin: 0;
+  padding: 0;
+  line-height: var(--h);
+  font-size: 1rem;
+}
+.select2-container .select2-selection--single .select2-selection__arrow {
+  right: 8px;
+  height: var(--h);
+}
+.select2-container .select2-dropdown {
+  background: #fefefe;
+  border: 1px solid #b5cce2;
+  border-radius: 4px;
+  margin-top: 2px;
+}
+.select2-container .select2-dropdown .select2-search {
+  padding: 8px 10px;
+}
+.select2-container .select2-dropdown .select2-search .select2-search__field {
+  border: 1px solid var(--brc);
+  background: var(--bgc);
+  color: var(--tc);
+  border-radius: 4px;
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option {
+  color: var(--tc);
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option.select2-results__option--highlighted {
+  background: #e7edf4;
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option.select2-results__option[aria-selected=true] {
+  background: #ecf3fc;
+  font-weight: 600;
+}
+
+.box {
+  width: 100%;
+  position: relative;
+  font-family: "Source Han Sans";
+  background: none;
+}
+.box .body {
+  width: 100%;
+  height: calc(100vh - 100px);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+.box .body .icon {
+  --w: 240px;
+  width: var(--w);
+  height: var(--w);
+  position: relative;
+  margin-bottom: 50px;
+  background: url("/Content/Image/ExerciseV2/timer_1.png") no-repeat 100%/100%;
+}
+.box .body .icon:after {
+  content: "";
+  display: block;
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: var(--w);
+  height: var(--w);
+  background: url("/Content/Image/ExerciseV2/timer_2.png") no-repeat 100%/100%;
+  animation: spin 4s linear infinite;
+}
+.box .body .icon .ico {
+  position: absolute;
+  width: 90px;
+  height: 90px;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
+}
+.box .body .name {
+  font-weight: 500;
+  font-size: 40px;
+  display: flex;
+  color: #094B88;
+  letter-spacing: 5px;
+  padding-left: 5px;
+  margin-bottom: 20px;
+}
+.box .body .group {
+  width: 150px;
+  padding: 8px;
+  text-align: center;
+  font-size: 18px;
+  color: #fff;
+  background: #094B88;
+  letter-spacing: 2px;
+  opacity: 0.6;
+  border-radius: 40px;
+}
+.box.public .title {
+  letter-spacing: 5px;
+  padding-left: 5px;
+}
+.box.public .body .icon {
+  --w: 180px;
+}
+.box.public .body .icon .ico {
+  width: 120px;
+  height: 120px;
+}
+.box.public .body .name {
+  font-size: 40px;
+  letter-spacing: 10px;
+  padding-left: 10px;
+}
+
+@media (min-width: 1200px) {
+  .box.public .body .icon {
+    --w: 300px;
+  }
+
+  .box.public .body .name {
+    font-size: 90px;
+  }
+}
+@keyframes spin {
+  0% {
+    transform: rotate(0deg);
+  }
+  50% {
+    transform: rotate(180deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}

File diff suppressed because it is too large
+ 0 - 0
SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-bg.min.css


+ 213 - 0
SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-cmd.css

@@ -0,0 +1,213 @@
+body {
+  --mc: #03AEBC;
+  --tc: #36536D;
+  --brc: #80BAEF;
+  --bgc: #e9f2fd;
+  --bg: rgba(255,255,255,0.4);
+  font-family: "Source Han Sans";
+}
+
+.box .box-header {
+  display: flex;
+  height: 35px;
+  width: 100%;
+  /*background: linear-gradient(90deg,#b2d9f5,#c2d8f4);*/
+  background: linear-gradient(90deg, rgba(82, 197, 233, 0.411765) 0%, rgba(59, 221, 243, 0.12549) 35.99%, rgba(67, 219, 252, 0.00784314) 87.02%);
+  box-shadow: inset 0px -2px 4px rgba(117, 185, 248, 0.52);
+  border-bottom: 1px solid rgba(98, 156, 215, 0.05);
+  position: relative;
+}
+.box .box-header .title {
+  --w: 47px;
+  display: flex;
+  position: relative;
+  height: 100%;
+  padding-left: 5px;
+  margin-left: var(--w);
+  font-size: 18px;
+  font-weight: 600;
+  color: #094B88;
+  align-items: center;
+}
+.box .box-header .title:before {
+  content: "";
+  position: absolute;
+  left: calc(1px - var(--w));
+  height: 100%;
+  width: var(--w);
+  background: url("/Content/Image/ExerciseV2/title_icon.svg") no-repeat;
+  background-size: 100% 100%;
+}
+
+.box {
+  width: 100%;
+  position: relative;
+  font-family: "Source Han Sans";
+  background: none;
+  display: flex;
+  flex-direction: column;
+}
+.box .box-title {
+  width: 100%;
+  text-align: center;
+  font-weight: 600;
+  font-size: 38px;
+  color: #fff;
+  margin-top: 6px;
+}
+.box .box-body {
+  margin: 10px 30px 40px;
+  border: 2px solid #fff;
+  border-radius: 5px;
+}
+.box .body-card {
+  background: #e9f2fd;
+  border-radius: 5px;
+  padding: 10px;
+  margin-bottom: 10px;
+  display: flex;
+  position: relative;
+}
+
+.btn {
+  margin: 5px auto;
+  padding: 8px 20px;
+  background: #03AEBC;
+  border-radius: 8px;
+  color: #DAEDFF;
+  font-size: 16px;
+}
+.btn.btn-small {
+  font-size: 14px;
+  padding: 3px 12px;
+  border-radius: 4px;
+}
+.btn:hover {
+  background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
+  border-color: rgba(0, 0, 0, 0.1);
+  color: #DAEDFF;
+  font-weight: 600;
+}
+
+.vb-btn {
+  color: #176bbc;
+  font-size: 14px;
+  padding: 5px 10px;
+  background-image: url("/Content/Image/ExerciseV2/btn.png");
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  font-weight: 600;
+}
+.vb-btn.active {
+  color: #fff;
+  background-image: url("/Content/Image/ExerciseV2/btn-active.png");
+}
+
+.form .form-control {
+  background: var(--bgc);
+  color: var(--tc);
+  border-radius: 4px;
+  border: 1px solid #b5cce2;
+  font-size: 1rem;
+}
+.form .form-control:active, .form .form-control:focus {
+  border: 1px solid var(--brc);
+}
+
+.select2-container {
+  --h: 38px;
+  font-size: 1rem;
+}
+.select2-container.select2-container--open .select2-selection--single {
+  border-color: var(--brc);
+}
+.select2-container .select2-selection--single {
+  border-radius: 4px !important;
+  background: var(--bgc);
+  border: 1px solid #b5cce2;
+  height: var(--h);
+  line-height: var(--h);
+  color: var(--tc);
+  padding: 0 15px;
+}
+.select2-container .select2-selection--single .select2-selection__rendered {
+  color: inherit;
+  margin: 0;
+  padding: 0;
+  line-height: var(--h);
+  font-size: 1rem;
+}
+.select2-container .select2-selection--single .select2-selection__arrow {
+  right: 8px;
+  height: var(--h);
+}
+.select2-container .select2-dropdown {
+  background: #fefefe;
+  border: 1px solid #b5cce2;
+  border-radius: 4px;
+  margin-top: 2px;
+}
+.select2-container .select2-dropdown .select2-search {
+  padding: 8px 10px;
+}
+.select2-container .select2-dropdown .select2-search .select2-search__field {
+  border: 1px solid var(--brc);
+  background: var(--bgc);
+  color: var(--tc);
+  border-radius: 4px;
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option {
+  color: var(--tc);
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option.select2-results__option--highlighted {
+  background: #e7edf4;
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option.select2-results__option[aria-selected=true] {
+  background: #ecf3fc;
+  font-weight: 600;
+}
+
+.box .body {
+  padding: 10px;
+  background: #d8e7f9;
+  background: linear-gradient(174.54deg, rgba(204, 224, 248, 0.76) 0.23%, rgba(242, 249, 255, 0.76) 120.71%);
+  display: flex;
+  flex-direction: column;
+}
+.box .role-box {
+  display: flex;
+  /*  .form-control, .select2-selection--single {
+      border: none;
+  }*/
+}
+.box .role-box > div {
+  width: 100%;
+  padding: 0;
+}
+.box .select-box, .box .input-box, .box .cmd-box {
+  background: none;
+}
+.box .input-box {
+  display: none;
+  margin-left: 15px;
+}
+.box .cmd-box {
+  position: relative;
+  padding: 0;
+}
+.box .cmd-box .txt {
+  padding: 10px;
+}
+.box .cmd-box .btn {
+  position: absolute;
+  right: 15px;
+  bottom: 15px;
+  margin: 5px auto;
+  width: 120px;
+}
+.box .cmd-box .help-box {
+  position: absolute;
+  left: 15px;
+  bottom: 15px;
+  width: 70%;
+}

File diff suppressed because it is too large
+ 0 - 0
SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-cmd.min.css


+ 293 - 0
SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-cmd_leader.css

@@ -0,0 +1,293 @@
+body {
+  --mc: #03AEBC;
+  --tc: #36536D;
+  --brc: #80BAEF;
+  --bgc: #e9f2fd;
+  --bg: rgba(255,255,255,0.4);
+  font-family: "Source Han Sans";
+}
+
+.box .box-header {
+  display: flex;
+  height: 35px;
+  width: 100%;
+  /*background: linear-gradient(90deg,#b2d9f5,#c2d8f4);*/
+  background: linear-gradient(90deg, rgba(82, 197, 233, 0.411765) 0%, rgba(59, 221, 243, 0.12549) 35.99%, rgba(67, 219, 252, 0.00784314) 87.02%);
+  box-shadow: inset 0px -2px 4px rgba(117, 185, 248, 0.52);
+  border-bottom: 1px solid rgba(98, 156, 215, 0.05);
+  position: relative;
+}
+.box .box-header .title {
+  --w: 47px;
+  display: flex;
+  position: relative;
+  height: 100%;
+  padding-left: 5px;
+  margin-left: var(--w);
+  font-size: 18px;
+  font-weight: 600;
+  color: #094B88;
+  align-items: center;
+}
+.box .box-header .title:before {
+  content: "";
+  position: absolute;
+  left: calc(1px - var(--w));
+  height: 100%;
+  width: var(--w);
+  background: url("/Content/Image/ExerciseV2/title_icon.svg") no-repeat;
+  background-size: 100% 100%;
+}
+
+.box {
+  width: 100%;
+  position: relative;
+  font-family: "Source Han Sans";
+  background: none;
+  display: flex;
+  flex-direction: column;
+}
+.box .box-title {
+  width: 100%;
+  text-align: center;
+  font-weight: 600;
+  font-size: 38px;
+  color: #fff;
+  margin-top: 6px;
+}
+.box .box-body {
+  margin: 10px 30px 40px;
+  border: 2px solid #fff;
+  border-radius: 5px;
+}
+.box .body-card {
+  background: #e9f2fd;
+  border-radius: 5px;
+  padding: 10px;
+  margin-bottom: 10px;
+  display: flex;
+  position: relative;
+}
+
+.btn {
+  margin: 5px auto;
+  padding: 8px 20px;
+  background: #03AEBC;
+  border-radius: 8px;
+  color: #DAEDFF;
+  font-size: 16px;
+}
+.btn.btn-small {
+  font-size: 14px;
+  padding: 3px 12px;
+  border-radius: 4px;
+}
+.btn:hover {
+  background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
+  border-color: rgba(0, 0, 0, 0.1);
+  color: #DAEDFF;
+  font-weight: 600;
+}
+
+.vb-btn {
+  color: #176bbc;
+  font-size: 14px;
+  padding: 5px 10px;
+  background-image: url("/Content/Image/ExerciseV2/btn.png");
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  font-weight: 600;
+}
+.vb-btn.active {
+  color: #fff;
+  background-image: url("/Content/Image/ExerciseV2/btn-active.png");
+}
+
+.form .form-control {
+  background: var(--bgc);
+  color: var(--tc);
+  border-radius: 4px;
+  border: 1px solid #b5cce2;
+  font-size: 1rem;
+}
+.form .form-control:active, .form .form-control:focus {
+  border: 1px solid var(--brc);
+}
+
+.select2-container {
+  --h: 38px;
+  font-size: 1rem;
+}
+.select2-container.select2-container--open .select2-selection--single {
+  border-color: var(--brc);
+}
+.select2-container .select2-selection--single {
+  border-radius: 4px !important;
+  background: var(--bgc);
+  border: 1px solid #b5cce2;
+  height: var(--h);
+  line-height: var(--h);
+  color: var(--tc);
+  padding: 0 15px;
+}
+.select2-container .select2-selection--single .select2-selection__rendered {
+  color: inherit;
+  margin: 0;
+  padding: 0;
+  line-height: var(--h);
+  font-size: 1rem;
+}
+.select2-container .select2-selection--single .select2-selection__arrow {
+  right: 8px;
+  height: var(--h);
+}
+.select2-container .select2-dropdown {
+  background: #fefefe;
+  border: 1px solid #b5cce2;
+  border-radius: 4px;
+  margin-top: 2px;
+}
+.select2-container .select2-dropdown .select2-search {
+  padding: 8px 10px;
+}
+.select2-container .select2-dropdown .select2-search .select2-search__field {
+  border: 1px solid var(--brc);
+  background: var(--bgc);
+  color: var(--tc);
+  border-radius: 4px;
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option {
+  color: var(--tc);
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option.select2-results__option--highlighted {
+  background: #e7edf4;
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option.select2-results__option[aria-selected=true] {
+  background: #ecf3fc;
+  font-weight: 600;
+}
+
+.box .box-body {
+  position: relative;
+}
+.box .box-body .body {
+  padding: 10px;
+  background: #d8e7f9;
+  background: linear-gradient(174.54deg, rgba(204, 224, 248, 0.76) 0.23%, rgba(242, 249, 255, 0.76) 120.71%);
+  display: flex;
+  flex-direction: column;
+}
+.box .logs-box {
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+}
+.box .logs-box .log-box {
+  display: flex;
+  padding: 10px;
+  background: rgba(255, 255, 255, 0.6);
+  border-radius: 8px;
+  margin: 5px 0;
+  overflow: hidden;
+}
+.box .logs-box .log-box .content {
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  padding: 5px;
+  padding-right: 15px;
+}
+.box .logs-box .log-box .content .title {
+  font-weight: 600;
+  color: #03AEBC;
+  font-size: 14px;
+}
+.box .logs-box .log-box .content .text {
+  color: #36536D;
+}
+.box .logs-box .log-box .btn-box {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 5px;
+}
+.box .logs-box .log-box .btn-box .btn {
+  font-size: 14px;
+  width: 90px;
+}
+.box .logs-box .log-box.send .btn {
+  background: #DE5E5E;
+}
+.box .logs-box .empty {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  color: #36536D;
+  font-size: 16px;
+  opacity: 0.4;
+}
+
+.question {
+  display: none;
+}
+.question button {
+  --w: 80px;
+  width: var(--w);
+  height: var(--w);
+  border-radius: var(--w);
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  transform: translateX(-50%) translateY(-50%);
+  z-index: 5;
+  color: #fff;
+  border: none;
+  outline: none;
+}
+.question button:hover {
+  background-color: #bd2130;
+}
+.question button i {
+  font-size: 40px;
+}
+.question button:not(:hover) {
+  animation: 1s shine ease-in-out infinite;
+}
+.question button:not(:hover) i {
+  animation: 1s flashing ease-in-out infinite;
+}
+
+.question-text {
+  font-size: 16px;
+  margin-bottom: 10px;
+  font-weight: 600;
+  color: var(--mc);
+}
+
+@keyframes shine {
+  0% {
+    filter: drop-shadow(0 0 12px #dc3545);
+  }
+  50% {
+    filter: drop-shadow(0 0 12px #bd2130);
+  }
+  100% {
+    filter: drop-shadow(0 0 12px #dc3545);
+  }
+}
+@keyframes flashing {
+  0% {
+    opacity: 1;
+    transform: scale(1);
+  }
+  50% {
+    opacity: 0.5;
+    transform: scale(0.9);
+  }
+  100% {
+    opacity: 1;
+    transform: scale(1);
+  }
+}

File diff suppressed because it is too large
+ 0 - 0
SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-cmd_leader.min.css


+ 392 - 0
SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-zhb.css

@@ -0,0 +1,392 @@
+body {
+  --mc: #03AEBC;
+  --tc: #36536D;
+  --brc: #80BAEF;
+  --bgc: #e9f2fd;
+  --bg: rgba(255,255,255,0.4);
+  font-family: "Source Han Sans";
+}
+
+.box .box-header {
+  display: flex;
+  height: 35px;
+  width: 100%;
+  /*background: linear-gradient(90deg,#b2d9f5,#c2d8f4);*/
+  background: linear-gradient(90deg, rgba(82, 197, 233, 0.411765) 0%, rgba(59, 221, 243, 0.12549) 35.99%, rgba(67, 219, 252, 0.00784314) 87.02%);
+  box-shadow: inset 0px -2px 4px rgba(117, 185, 248, 0.52);
+  border-bottom: 1px solid rgba(98, 156, 215, 0.05);
+  position: relative;
+}
+.box .box-header .title {
+  --w: 47px;
+  display: flex;
+  position: relative;
+  height: 100%;
+  padding-left: 5px;
+  margin-left: var(--w);
+  font-size: 18px;
+  font-weight: 600;
+  color: #094B88;
+  align-items: center;
+}
+.box .box-header .title:before {
+  content: "";
+  position: absolute;
+  left: calc(1px - var(--w));
+  height: 100%;
+  width: var(--w);
+  background: url("/Content/Image/ExerciseV2/title_icon.svg") no-repeat;
+  background-size: 100% 100%;
+}
+
+.box {
+  width: 100%;
+  position: relative;
+  font-family: "Source Han Sans";
+  background: none;
+  display: flex;
+  flex-direction: column;
+}
+.box .box-title {
+  width: 100%;
+  text-align: center;
+  font-weight: 600;
+  font-size: 38px;
+  color: #fff;
+  margin-top: 6px;
+}
+.box .box-body {
+  margin: 10px 30px 40px;
+  border: 2px solid #fff;
+  border-radius: 5px;
+}
+.box .body-card {
+  background: #e9f2fd;
+  border-radius: 5px;
+  padding: 10px;
+  margin-bottom: 10px;
+  display: flex;
+  position: relative;
+}
+
+.btn {
+  margin: 5px auto;
+  padding: 8px 20px;
+  background: #03AEBC;
+  border-radius: 8px;
+  color: #DAEDFF;
+  font-size: 16px;
+}
+.btn.btn-small {
+  font-size: 14px;
+  padding: 3px 12px;
+  border-radius: 4px;
+}
+.btn:hover {
+  background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
+  border-color: rgba(0, 0, 0, 0.1);
+  color: #DAEDFF;
+  font-weight: 600;
+}
+
+.vb-btn {
+  color: #176bbc;
+  font-size: 14px;
+  padding: 5px 10px;
+  background-image: url("/Content/Image/ExerciseV2/btn.png");
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  font-weight: 600;
+}
+.vb-btn.active {
+  color: #fff;
+  background-image: url("/Content/Image/ExerciseV2/btn-active.png");
+}
+
+.form .form-control {
+  background: var(--bgc);
+  color: var(--tc);
+  border-radius: 4px;
+  border: 1px solid #b5cce2;
+  font-size: 1rem;
+}
+.form .form-control:active, .form .form-control:focus {
+  border: 1px solid var(--brc);
+}
+
+.select2-container {
+  --h: 38px;
+  font-size: 1rem;
+}
+.select2-container.select2-container--open .select2-selection--single {
+  border-color: var(--brc);
+}
+.select2-container .select2-selection--single {
+  border-radius: 4px !important;
+  background: var(--bgc);
+  border: 1px solid #b5cce2;
+  height: var(--h);
+  line-height: var(--h);
+  color: var(--tc);
+  padding: 0 15px;
+}
+.select2-container .select2-selection--single .select2-selection__rendered {
+  color: inherit;
+  margin: 0;
+  padding: 0;
+  line-height: var(--h);
+  font-size: 1rem;
+}
+.select2-container .select2-selection--single .select2-selection__arrow {
+  right: 8px;
+  height: var(--h);
+}
+.select2-container .select2-dropdown {
+  background: #fefefe;
+  border: 1px solid #b5cce2;
+  border-radius: 4px;
+  margin-top: 2px;
+}
+.select2-container .select2-dropdown .select2-search {
+  padding: 8px 10px;
+}
+.select2-container .select2-dropdown .select2-search .select2-search__field {
+  border: 1px solid var(--brc);
+  background: var(--bgc);
+  color: var(--tc);
+  border-radius: 4px;
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option {
+  color: var(--tc);
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option.select2-results__option--highlighted {
+  background: #e7edf4;
+}
+.select2-container .select2-dropdown .select2-results .select2-results__option.select2-results__option[aria-selected=true] {
+  background: #ecf3fc;
+  font-weight: 600;
+}
+
+.box .box-body .body {
+  padding: 10px;
+  background: #d8e7f9;
+  background: linear-gradient(174.54deg, rgba(204, 224, 248, 0.76) 0.23%, rgba(242, 249, 255, 0.76) 120.71%);
+  display: flex;
+  flex-direction: column;
+}
+.box .box-body .body .stu {
+  display: none !important;
+}
+.box .box-body .body .check-box-icon {
+  background: url("/Content/Image/ExerciseV2/zhb/zh_icon-checkbox.svg") no-repeat;
+  background-size: 100% 100%;
+}
+.box .box-body .body .check .check-box-icon {
+  background-image: url("/Content/Image/ExerciseV2/zhb/zh_icon-checkbox-selected.svg");
+}
+.box .box-body .body .select {
+  justify-content: space-between;
+}
+.box .box-body .body .select .select-box {
+  display: flex;
+}
+.box .box-body .body .select .select-box .select-item {
+  height: 100px;
+  margin-left: 15px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  position: relative;
+}
+.box .box-body .body .select .select-box .select-item .check-box-icon {
+  position: absolute;
+  top: 5px;
+  left: -8px;
+  width: 15px;
+  height: 15px;
+}
+.box .box-body .body .select .select-box .select-item .icon {
+  width: 80px;
+  height: 80px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background: url("/Content/Image/ExerciseV2/zhb/zh_icon_bg.svg") no-repeat;
+  background-size: 100% 100%;
+}
+.box .box-body .body .select .select-box .select-item .icon img {
+  width: 35px;
+  height: 35px;
+}
+.box .box-body .body .select .select-box .select-item .text {
+  text-align: center;
+  color: #094B88;
+}
+.box .box-body .body .select .select-box .select-item.active .icon {
+  background-image: url("/Content/Image/ExerciseV2/zhb/zh_icon_bg-select.svg");
+}
+.box .box-body .body .select .select-box .select-item.active .text {
+  font-weight: 600;
+}
+.box .box-body .body .select .add-role {
+  position: relative;
+  width: 100px;
+  height: 100px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  background: #4585cc;
+  border-radius: 8px;
+}
+.box .box-body .body .select .add-role .icon {
+  width: 80px;
+  height: 50px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.box .box-body .body .select .add-role .icon img {
+  width: 35px;
+  height: 35px;
+}
+.box .box-body .body .select .add-role .text {
+  margin-top: 5px;
+  text-align: center;
+  color: #fff;
+}
+.box .box-body .body .select .add-role:before {
+  content: "";
+  position: absolute;
+  left: -20px;
+  height: 80%;
+  border-left: 1px solid #c0dffb;
+}
+.box .box-body .body .role .content-box {
+  width: 100%;
+  height: calc(100vh - 380px);
+}
+.box .box-body .body .role .role-content {
+  display: none;
+}
+.box .box-body .body .role .role-content.active {
+  display: flex;
+  flex-direction: column;
+}
+.box .box-body .body .role .role-select {
+  height: 35px;
+  margin-bottom: 10px;
+  color: #094B88;
+  padding-left: 20px;
+  display: flex;
+  align-items: center;
+}
+.box .box-body .body .role .role-select .check-box-icon {
+  margin-right: 10px;
+  width: 20px;
+  height: 20px;
+}
+.box .box-body .body .role .role-select span {
+  color: #5f8bb6;
+  font-weight: 400;
+  padding-left: 3px;
+}
+.box .box-body .body .role .role-select.check {
+  font-weight: 600;
+}
+.box .box-body .body .role .role-box {
+  display: flex;
+  flex-wrap: wrap;
+}
+.box .box-body .body .role .role-box .role-item {
+  position: relative;
+  width: 100px;
+  height: 100px;
+  margin-left: 15px;
+  margin-bottom: 15px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  background: #FFFFFF;
+  border: 1px solid #b2d9f5;
+  border-radius: 8px;
+}
+.box .box-body .body .role .role-box .role-item .icon {
+  width: 100%;
+  height: 40px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.box .box-body .body .role .role-box .role-item .icon img {
+  width: 35px;
+  height: 35px;
+}
+.box .box-body .body .role .role-box .role-item .text {
+  width: 100%;
+  margin-top: 10px;
+  padding: 0 10px;
+  line-height: 1.3;
+  text-align: center;
+  color: #094B88;
+  font-size: 14px;
+}
+.box .box-body .body .role .role-box .role-item .close {
+  position: absolute;
+  top: 5px;
+  right: 8px;
+  font-size: 1.5rem;
+  font-weight: 700;
+  line-height: 1;
+  color: #094B88;
+  text-shadow: 0 1px 0 #fff;
+  opacity: 0.8;
+}
+.box .box-body .body .role .role-box .role-item.check .text {
+  font-weight: 600;
+}
+.box .box-body .body .role .role-box .role-item.add {
+  flex-direction: column;
+  background: #FFFFFF;
+}
+.box .box-body .body .role .role-box .role-item.add .text {
+  font-size: 12px;
+}
+.box .box-body .body .add-box {
+  position: absolute;
+  display: none;
+  left: calc(50% - 150px);
+  top: 100px;
+  padding: 25px 20px 20px;
+  width: 300px;
+  flex-direction: column;
+}
+.box .box-body .body .add-box input {
+  padding: 5px 10px;
+  font-size: 14px;
+  border: 1px solid #DAEDFF;
+  border-radius: 5px;
+  outline: none;
+}
+.box .box-body .body .add-box input:active, .box .box-body .body .add-box input:focus {
+  border: 1px solid #DAEDFF;
+  outline: none;
+}
+.box .box-body .body .add-box .btn-box {
+  display: flex;
+  justify-content: center;
+  margin-top: 15px;
+}
+.box .box-body .body .add-box .btn {
+  margin: 0 10px;
+  min-width: 80px;
+}
+.box .box-body .body .add-box .btn.cancel {
+  background: #DAEDFF;
+  color: #03AEBC;
+  border: 1px solid;
+}
+.box .box-body .btn {
+  min-width: 150px;
+}

File diff suppressed because it is too large
+ 0 - 0
SourceCode/WeApp.Web/Content/Css/ExerciseV2/stu-zhb.min.css


+ 1 - 1
SourceCode/WeApp.Web/Content/Css/Home.css

@@ -11,7 +11,7 @@
 }
 .home .page {
   position: relative;
-  background: rgba(255, 255, 255, 0.3);
+  background: hsla(0deg, 0%, 100%, 0.3);
   overflow: hidden;
   z-index: 0;
   margin: -120px auto 0;

+ 1 - 1
SourceCode/WeApp.Web/Content/Css/Home.min.css

@@ -1 +1 @@
-.content-wrapper{padding:0 !important;}.home{height:calc(100vh - 96px);overflow:hidden;display:flex;justify-content:center;align-items:center;}.home .page{position:relative;background:rgba(255,255,255,.3);overflow:hidden;z-index:0;margin:-120px auto 0;overflow:hidden;min-width:70%;border-radius:15px;}.home .page::before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;margin:-30px;z-index:-1;}.home .page .page-title,.home .page .page-title-sm{cursor:none;width:100%;height:80px;font-size:35px;font-weight:600;letter-spacing:10px;padding-top:0;color:#eee;display:flex;align-items:center;justify-content:center;}.home .page .page-title-sm{font-size:30px;color:#fff;}.home .page .page-title-sm span{border:3px solid #fff;padding:0 10px 10px 20px;}@media(min-width:768px){.home .page .page-title,.home .page .page-title-sm{height:130px;font-size:65px;letter-spacing:10px;padding-top:10px;}.home .page .page-title-sm{font-size:50px;}.home .page .page-title-sm span{border-width:4px;}}@media(min-width:1200px){.home .page .page-title,.home .page .page-title-sm{height:140px;font-size:75px;letter-spacing:10px;padding-top:20px;}.home .page .page-title-sm{font-size:60px;}.home .page .page-title-sm span{border-width:5px;}}
+.content-wrapper{padding:0!important;}.home{height:calc(100vh - 96px);overflow:hidden;display:flex;justify-content:center;align-items:center;}.home .page{position:relative;background:hsla(0deg,0%,100%,.3);overflow:hidden;z-index:0;margin:-120px auto 0;overflow:hidden;min-width:70%;border-radius:15px;}.home .page::before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;margin:-30px;z-index:-1;}.home .page .page-title,.home .page .page-title-sm{cursor:none;width:100%;height:80px;font-size:35px;font-weight:600;letter-spacing:10px;padding-top:0;color:#eee;display:flex;align-items:center;justify-content:center;}.home .page .page-title-sm{font-size:30px;color:#fff;}.home .page .page-title-sm span{border:3px solid #fff;padding:0 10px 10px 20px;}@media(min-width:768px){.home .page .page-title,.home .page .page-title-sm{height:130px;font-size:65px;letter-spacing:10px;padding-top:10px;}.home .page .page-title-sm{font-size:50px;}.home .page .page-title-sm span{border-width:4px;}}@media(min-width:1200px){.home .page .page-title,.home .page .page-title-sm{height:140px;font-size:75px;letter-spacing:10px;padding-top:20px;}.home .page .page-title-sm{font-size:60px;}.home .page .page-title-sm span{border-width:5px;}}

+ 82 - 80
SourceCode/WeApp.Web/Content/Css/iwb.style.css

@@ -1,5 +1,6 @@
 @charset "UTF-8";
 /*$main-color: #007bff;*/
+/*$main-color: #11998e;*/
 body .sidebar {
   overflow-x: hidden !important;
 }
@@ -13,10 +14,10 @@ body .content-wrapper {
   padding: 5px 10px;
 }
 body .content-wrapper .dropdown-menu {
-  border-color: #11998e;
+  border-color: #03aebc;
 }
 body .content-wrapper .dropdown-menu .dropdown-item {
-  color: #11998e;
+  color: #03aebc;
   border-bottom: 1px solid #eee;
 }
 body .content-wrapper .dropdown-menu .dropdown-item:first-child {
@@ -24,12 +25,12 @@ body .content-wrapper .dropdown-menu .dropdown-item:first-child {
 }
 body .content-wrapper .dropdown-menu .dropdown-item:active, body .content-wrapper .dropdown-menu .dropdown-item.active {
   color: #fff !important;
-  background-color: #11998e;
+  background-color: #03aebc;
 }
 body .content-wrapper .dropdown-menu .dropdown-item:focus, body .content-wrapper .dropdown-menu .dropdown-item:hover {
   color: #fff;
   text-decoration: none;
-  background-color: #11998e;
+  background-color: #03aebc;
 }
 body label.iwb-label {
   text-align: right;
@@ -45,10 +46,10 @@ body label.iwb-label-required::before {
   text-align: right;
 }
 body .text-iwb {
-  color: #11998e;
+  color: #03aebc;
 }
 body .text-p, body.text-primary {
-  color: #11998e;
+  color: #03aebc;
 }
 body .text-s, body.text-success {
   color: #28a745;
@@ -64,25 +65,25 @@ body .text-d, body.text-danger {
 }
 
 .nav-tabs {
-  border-bottom: 1px solid #11998e;
+  border-bottom: 1px solid #03aebc;
 }
 .nav-tabs .nav-item.show .nav-link {
   color: #fff;
-  background-color: #11998e;
-  border-color: #11998e;
+  background-color: #03aebc;
+  border-color: #03aebc;
 }
 .nav-tabs .nav-link {
-  color: #11998e;
+  color: #03aebc;
 }
 .nav-tabs .nav-link:focus, .nav-tabs .nav-link:hover {
-  border-color: #11998e #11998e #11998e;
-  color: #11998e;
+  border-color: #03aebc #03aebc #03aebc;
+  color: #03aebc;
   font-weight: 600;
 }
 .nav-tabs .nav-link.active {
   color: #fff;
-  background-color: #11998e;
-  border-color: #11998e;
+  background-color: #03aebc;
+  border-color: #03aebc;
 }
 
 .label {
@@ -101,12 +102,12 @@ body .text-d, body.text-danger {
   border-color: #6c757d;
 }
 .label.label-iwb {
-  background-color: #11998e;
-  border-color: #11998e;
+  background-color: #03aebc;
+  border-color: #03aebc;
 }
 .label.label-p, .label.label-primary {
-  background-color: #11998e;
-  border-color: #11998e;
+  background-color: #03aebc;
+  border-color: #03aebc;
 }
 .label.label-s, .label.label-success {
   background-color: #28a745;
@@ -128,14 +129,14 @@ body .text-d, body.text-danger {
 
 .badge.badge-iwb {
   color: #fff;
-  background-color: #11998e;
-  border-color: #11998e;
+  background-color: #03aebc;
+  border-color: #03aebc;
 }
 
 .btn.btn-iwb {
   color: #fff;
-  background-color: #11998e;
-  border-color: #11998e;
+  background-color: #03aebc;
+  border-color: #03aebc;
   box-shadow: none;
 }
 
@@ -145,29 +146,29 @@ body .text-d, body.text-danger {
 }
 
 .btn.btn-default {
-  color: #11998e;
-  border-color: #11998e;
+  color: #03aebc;
+  border-color: #03aebc;
 }
 
 .btn-outline-iwb {
-  color: #11998e;
-  border-color: #11998e;
+  color: #03aebc;
+  border-color: #03aebc;
 }
 
 .btn-outline-iwb:hover {
   color: #fff;
-  background-color: #11998e;
-  border-color: #11998e;
+  background-color: #03aebc;
+  border-color: #03aebc;
 }
 
 .btn.btn-gold {
   border-radius: 25px;
   color: #eee;
   background-color: #ffa600;
-  background-color: #ffa600;
+  background-color: hsl(39deg, 100%, 50%);
   border: none;
   box-shadow: inset #fffeff 0 0.3em 0.3em, inset #000000 0 -0.1em 0.3em, #cc8500 0 0.1em 3px, #996300 0 0.3em 1px, #000000 0 0.5em 5px !important;
-  box-shadow: inset rgba(255, 254, 255, 0.6) 0 0.3em 0.3em, inset rgba(0, 0, 0, 0.15) 0 -0.1em 0.3em, #cc8500 0 0.1em 3px, #996300 0 0.3em 1px, rgba(0, 0, 0, 0.2) 0 0.5em 5px !important;
+  box-shadow: inset rgba(255, 254, 255, 0.6) 0 0.3em 0.3em, inset rgba(0, 0, 0, 0.15) 0 -0.1em 0.3em, hsl(39deg, 100%, 40%) 0 0.1em 3px, hsl(39deg, 100%, 30%) 0 0.3em 1px, rgba(0, 0, 0, 0.2) 0 0.5em 5px !important;
 }
 .btn.btn-gold:hover, .btn.btn-gold:focus, .btn.btn-gold:active {
   color: #fff;
@@ -189,7 +190,7 @@ body .text-d, body.text-danger {
 
 .bg-iwb {
   color: #fff;
-  background-color: #11998e !important;
+  background-color: #03aebc !important;
 }
 .bg-iwb > a {
   color: inherit !important;
@@ -220,17 +221,17 @@ body .text-d, body.text-danger {
 }
 
 .card.card-iwb.card-outline {
-  border-top: 3px solid #11998e;
+  border-top: 3px solid #03aebc;
 }
 .card.card-iwb:not(.card-outline) > .card-header {
-  background-color: #11998e;
+  background-color: #03aebc;
   color: #fff !important;
 }
 .card.card-iwb:not(.card-outline) > .card-header a.nav-link {
   color: inherit !important;
 }
 .card.card-iwb:not(.card-outline) > .card-header a.nav-link.active {
-  color: #11998e !important;
+  color: #03aebc !important;
   background: #fff;
   border-color: #fff;
 }
@@ -254,8 +255,8 @@ body .text-d, body.text-danger {
   padding: 10px 15px;
 }
 .card.card-gold.card-outline-tabs > .card-header a.active {
-  color: #ffa600;
-  border-top: 3px solid #ffa600;
+  color: hsl(39deg, 100%, 50%);
+  border-top: 3px solid hsl(39deg, 100%, 50%);
 }
 .card.card-dl.card-outline-tabs > .card-header .nav-tabs {
   border-bottom: 1px solid #049e9a;
@@ -276,7 +277,7 @@ body .text-d, body.text-danger {
   display: inline-block;
   position: relative;
   padding-left: 30px;
-  color: #11998e;
+  color: #03aebc;
   margin-bottom: 10px;
   cursor: pointer;
   font-size: 0.875rem;
@@ -332,26 +333,26 @@ body .text-d, body.text-danger {
   color: #fb483a;
 }
 .iwb-checkbox.iwb-gold {
-  color: #ffa600;
+  color: hsl(39deg, 100%, 50%);
 }
 .iwb-checkbox.iwb-dl {
   color: #049e9a;
 }
 
 .tooltip .tooltip-inner {
-  background: #11998e;
+  background: #03aebc;
 }
 .tooltip.bs-tooltip-left .arrow:before {
-  border-left-color: #11998e;
+  border-left-color: #03aebc;
 }
 .tooltip.bs-tooltip-right .arrow:before {
-  border-right-color: #11998e;
+  border-right-color: #03aebc;
 }
 .tooltip.bs-tooltip-top .arrow:before {
-  border-top-color: #11998e;
+  border-top-color: #03aebc;
 }
 .tooltip.bs-tooltip-bottom .arrow:before {
-  border-bottom-color: #11998e;
+  border-bottom-color: #03aebc;
 }
 
 .daterangepicker {
@@ -431,7 +432,7 @@ body .text-d, body.text-danger {
 
 .dropdown-menu .dropdown-item:active, .dropdown-menu .dropdown-item.active {
   color: #fff !important;
-  background-color: #11998e;
+  background-color: #03aebc;
 }
 
 .modal-backdrop.show {
@@ -439,7 +440,7 @@ body .text-d, body.text-danger {
 }
 
 .modal .modal-header {
-  color: #11998e;
+  color: #03aebc;
   padding-top: 10px;
   padding-bottom: 10px;
 }
@@ -455,10 +456,10 @@ body .text-d, body.text-danger {
   padding-bottom: 10px;
 }
 .modal .dropdown-menu {
-  border-color: #11998e;
+  border-color: #03aebc;
 }
 .modal .dropdown-menu .dropdown-item {
-  color: #11998e;
+  color: #03aebc;
   border-bottom: 1px solid #eee;
 }
 .modal .dropdown-menu .dropdown-item:first-child {
@@ -466,12 +467,12 @@ body .text-d, body.text-danger {
 }
 .modal .dropdown-menu .dropdown-item:active, .modal .dropdown-menu .dropdown-item.active {
   color: #fff !important;
-  background-color: #11998e;
+  background-color: #03aebc;
 }
 .modal .dropdown-menu .dropdown-item:focus, .modal .dropdown-menu .dropdown-item:hover {
   color: #fff;
   text-decoration: none;
-  background-color: #11998e;
+  background-color: #03aebc;
 }
 
 form .form-group {
@@ -496,7 +497,7 @@ form .form-control {
   z-index: 1;
 }
 form .form-control:focus, form .form-control:active {
-  border-color: #11998e;
+  border-color: #03aebc;
 }
 form .form-control:disabled, form .form-control[readonly] {
   background-color: #e9ecef;
@@ -521,8 +522,8 @@ form .search-input + label.error {
 }
 form .input-group > .search-icon > .input-group-text {
   color: #fff;
-  background-color: #11998e;
-  border: 1px solid #11998e;
+  background-color: #03aebc;
+  border: 1px solid #03aebc;
   font-weight: 400;
   width: 35px;
 }
@@ -544,8 +545,8 @@ form .input-group > .search-icon > .input-group-text {
 }
 .input-group-sm .select2-container .selection > .select2-selection--multiple .select2-selection__choice {
   margin-top: 0.325rem;
-  background-color: #11998e;
-  border-color: #11998e;
+  background-color: #03aebc;
+  border-color: #03aebc;
   font-size: 85%;
 }
 .input-group-sm .select2-container .selection > .select2-selection--multiple .select2-selection__choice__remove {
@@ -555,14 +556,14 @@ form .input-group > .search-icon > .input-group-text {
   color: #fff;
 }
 .input-group-sm .select2-container.select2-container--focus .select2-selection, .input-group-sm .select2-container.select2-container--open .select2-selection {
-  border-color: #11998e;
+  border-color: #03aebc;
 }
 .input-group-sm .select2-container.select2-container--open .select2-search--inline .select2-search__field {
-  border: 1px solid #11998e;
+  border: 1px solid #03aebc;
   margin-top: 4px;
 }
 .input-group-sm .select2-container .select2-dropdown {
-  border-color: #11998e;
+  border-color: #03aebc;
 }
 .input-group-sm .form-control.error + .select2-container .select2-selection {
   border-color: #dc3545;
@@ -594,14 +595,14 @@ form .input-group > .search-icon > .input-group-text {
 }
 .iwb-bootstrap-table .table-action {
   cursor: pointer;
-  color: #11998e;
+  color: #03aebc;
   margin: 0 8px;
   padding: 0 2px;
   font-size: 14px;
   font-weight: 600;
 }
 .iwb-bootstrap-table .table-action:hover {
-  border-bottom: 2px solid #11998e;
+  border-bottom: 2px solid #03aebc;
   padding-bottom: 2px;
 }
 .iwb-bootstrap-table .table-action i {
@@ -625,7 +626,7 @@ form .input-group > .search-icon > .input-group-text {
 }
 .iwb-bootstrap-table .fixed-table-container .fixed-table-header thead {
   /*background-color: #D1DCF9;*/
-  background-color: #11998e;
+  background-color: #03aebc;
   background-image: linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5));
 }
 .iwb-bootstrap-table .fixed-table-container .fixed-table-header thead .th-inner.sortable {
@@ -639,12 +640,12 @@ form .input-group > .search-icon > .input-group-text {
   background-color: rgba(0, 0, 0, 0);
 }
 .iwb-bootstrap-table .fixed-table-container .fixed-table-body thead {
-  background-color: #11998e;
+  background-color: #03aebc;
   background-image: linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5));
   /*background-color: #EBEEF7;*/
 }
 .iwb-bootstrap-table .fixed-table-container .fixed-table-body tbody .selected td {
-  background-color: #109085;
+  background-color: #03a5b2;
   /*background-color: #0074f0;*/
   color: #eee;
 }
@@ -658,12 +659,12 @@ form .input-group > .search-icon > .input-group-text {
 }
 .iwb-bootstrap-table .fixed-table-container .fixed-table-body .table-hover > tbody > tr:hover {
   /*background-color: #D1DCF9;*/
-  background-color: #11998e;
+  background-color: #03aebc;
   background-image: linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5));
 }
 .iwb-bootstrap-table .fixed-table-pagination {
   /*background-color: #D1DCF9;*/
-  background-color: #11998e;
+  background-color: #03aebc;
   background-image: linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5));
   padding: 5px;
   border: 1px solid #ddd;
@@ -685,14 +686,14 @@ form .input-group > .search-icon > .input-group-text {
 .search-card > .card-header .card-title {
   font-size: 0.875rem;
   line-height: 21px;
-  color: #11998e;
+  color: #03aebc;
 }
 .search-card > .card-header .card-tool {
   position: absolute;
   right: 3px;
 }
 .search-card > .card-header .card-tool i, .search-card > .card-header .card-tool span {
-  color: #11998e;
+  color: #03aebc;
 }
 .search-card > .card-body {
   padding: 10px 10px 0;
@@ -711,8 +712,8 @@ form .input-group > .search-icon > .input-group-text {
   min-width: 20px;
   text-align: right;
   color: #fff;
-  background-color: #11998e;
-  border: 1px solid #11998e;
+  background-color: #03aebc;
+  border: 1px solid #03aebc;
   font-weight: bold;
 }
 .search-card .search-unit .select2 {
@@ -760,11 +761,12 @@ form .input-group > .search-icon > .input-group-text {
   margin: 0;
   padding: 10px 0 0;
   text-indent: 1em;
+  color: #36536D;
 }
 .swal-modal .swal-title {
   font-size: 16px;
   font-weight: 600;
-  color: #333;
+  color: #094B88;
   line-height: 35px;
 }
 .swal-modal .swal-title:before {
@@ -792,33 +794,33 @@ form .input-group > .search-icon > .input-group-text {
 }
 .swal-modal .swal-footer {
   text-align: right;
-  margin-top: 0px;
+  margin-top: 10px;
   padding: 5px 10px;
 }
 .swal-modal .swal-footer .swal-button-container {
   margin: 0;
 }
 .swal-modal .swal-footer .swal-button-container .swal-button {
-  background-color: #0074f0;
-  border: 1px solid #0074f0;
+  border: 1px solid #03AEBC;
+  background: #03AEBC;
   color: #fff;
   border-radius: 3px;
   box-shadow: none;
-  font-weight: 600;
+  font-weight: 400;
   font-size: 14px;
   padding: 4px 20px;
   margin-left: 15px;
   cursor: pointer;
 }
 .swal-modal .swal-footer .swal-button-container .swal-button.swal-button--cancel {
-  color: #0074f0;
-  background-color: #fff;
-  border: 1px solid #0074f0;
+  background: #F5F5F5;
+  color: #36536D;
+  border: 1px solid #F3F3F3;
 }
 
 .iwb-info-box .box-header {
   text-indent: 2em;
-  color: #11998e;
+  color: #03aebc;
   margin-top: 15px;
   margin-bottom: 15px;
   font-size: 1.5rem;
@@ -828,7 +830,7 @@ form .input-group > .search-icon > .input-group-text {
   padding-bottom: 10px;
 }
 .iwb-info-box .box-footer {
-  color: #11998e;
+  color: #03aebc;
   text-align: center;
   background: transparent;
   margin-top: 15px;
@@ -868,7 +870,7 @@ form .input-group > .search-icon > .input-group-text {
   padding: 0 15px 0 0;
 }
 .notification-menu .notification-item .item-state .no-read {
-  color: #11998e;
+  color: #03aebc;
   cursor: pointer;
   position: relative;
 }
@@ -929,7 +931,7 @@ form .input-group > .search-icon > .input-group-text {
   box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.25);
   background-color: #f6f6f6;
   background-color: #f6f6f6;
-  border: 1px solid #11998e;
+  border: 1px solid #03aebc;
   overflow: hidden;
 }
 #avatar-modal .avatar-wrapper img {

File diff suppressed because it is too large
+ 0 - 0
SourceCode/WeApp.Web/Content/Css/iwb.style.min.css


BIN
SourceCode/WeApp.Web/Content/Image/ExerciseV2/bg.jpg


BIN
SourceCode/WeApp.Web/Content/Image/ExerciseV2/bg.png


BIN
SourceCode/WeApp.Web/Content/Image/ExerciseV2/bg_header_ruler.png


BIN
SourceCode/WeApp.Web/Content/Image/ExerciseV2/btn-active.png


BIN
SourceCode/WeApp.Web/Content/Image/ExerciseV2/btn.png


BIN
SourceCode/WeApp.Web/Content/Image/ExerciseV2/timer_1.png


BIN
SourceCode/WeApp.Web/Content/Image/ExerciseV2/timer_2.png


+ 48 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/title_icon.svg

@@ -0,0 +1,48 @@
+<svg width="50" height="36" viewBox="0 0 50 36" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <g filter="url(#filter0_d_72_1428)">
+    <path d="M14 11H24L20 25H10L14 11Z" fill="#1EA2FF" />
+    <path d="M14 11H24L20 25H10L14 11Z" fill="url(#paint0_linear_72_1428)" fill-opacity="0.39"
+      style="mix-blend-mode:overlay" />
+  </g>
+  <g filter="url(#filter1_d_72_1428)">
+    <path d="M32 11H42L38 25H28L32 11Z" fill="#09E0F3" />
+    <path d="M32 11H42L38 25H28L32 11Z" fill="url(#paint1_linear_72_1428)" fill-opacity="0.39"
+      style="mix-blend-mode:overlay" />
+  </g>
+  <defs>
+    <filter id="filter0_d_72_1428" x="0" y="1" width="34" height="34" filterUnits="userSpaceOnUse"
+      color-interpolation-filters="sRGB">
+      <feFlood flood-opacity="0" result="BackgroundImageFix" />
+      <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
+        result="hardAlpha" />
+      <feOffset />
+      <feGaussianBlur stdDeviation="5" />
+      <feComposite in2="hardAlpha" operator="out" />
+      <feColorMatrix type="matrix" values="0 0 0 0 0.207843 0 0 0 0 0.623529 0 0 0 0 0.909804 0 0 0 0.34 0" />
+      <feBlend mode="screen" in2="BackgroundImageFix" result="effect1_dropShadow_72_1428" />
+      <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_72_1428" result="shape" />
+    </filter>
+    <filter id="filter1_d_72_1428" x="18" y="1" width="34" height="34" filterUnits="userSpaceOnUse"
+      color-interpolation-filters="sRGB">
+      <feFlood flood-opacity="0" result="BackgroundImageFix" />
+      <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
+        result="hardAlpha" />
+      <feOffset />
+      <feGaussianBlur stdDeviation="5" />
+      <feComposite in2="hardAlpha" operator="out" />
+      <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.788235 0 0 0 0 0.862745 0 0 0 0.79 0" />
+      <feBlend mode="screen" in2="BackgroundImageFix" result="effect1_dropShadow_72_1428" />
+      <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_72_1428" result="shape" />
+    </filter>
+    <linearGradient id="paint0_linear_72_1428" x1="23" y1="18.5385" x2="10.9886" y2="19.4679"
+      gradientUnits="userSpaceOnUse">
+      <stop stop-color="white" />
+      <stop offset="1" />
+    </linearGradient>
+    <linearGradient id="paint1_linear_72_1428" x1="36" y1="11.5385" x2="34.8414" y2="24.9863"
+      gradientUnits="userSpaceOnUse">
+      <stop stop-color="white" />
+      <stop offset="1" stop-color="white" stop-opacity="0" />
+    </linearGradient>
+  </defs>
+</svg>

+ 5 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/toggle.svg

@@ -0,0 +1,5 @@
+<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M31.2956 18.5912H8.70478C7.76468 18.5912 7.00195 17.8285 7.00195 16.8884C7.00195 15.9483 7.76468 15.1855 8.70478 15.1855H31.2956C32.2357 15.1855 32.9984 15.9483 32.9984 16.8884C32.9984 17.8285 32.2357 18.5912 31.2956 18.5912Z" fill="white"/>
+<path d="M8.70478 18.591C8.26843 18.591 7.83208 18.4243 7.50216 18.0908C6.83522 17.4239 6.83522 16.349 7.50216 15.6821L13.8842 9.30001C14.5511 8.63307 15.626 8.63307 16.293 9.30001C16.9599 9.96695 16.9599 11.0419 16.293 11.7088L9.9074 18.0944C9.57748 18.4279 9.14113 18.591 8.70478 18.591ZM31.2956 25.8351H8.70478C7.76468 25.8351 7.00195 25.0724 7.00195 24.1323C7.00195 23.1922 7.76468 22.4295 8.70478 22.4295H31.2956C32.2357 22.4295 32.9984 23.1922 32.9984 24.1323C32.9984 25.0724 32.2357 25.8351 31.2956 25.8351Z" fill="white"/>
+<path d="M24.9138 32.2141C24.4774 32.2141 24.0411 32.0474 23.7111 31.7139C23.0442 31.0469 23.0442 29.9685 23.7111 29.3051L30.0932 22.9231C30.7601 22.2561 31.835 22.2561 32.502 22.9231C33.1689 23.59 33.1689 24.6685 32.502 25.3318L26.1164 31.7174C25.7829 32.0509 25.3501 32.2141 24.9138 32.2141Z" fill="white"/>
+</svg>

File diff suppressed because it is too large
+ 1 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/wait_leader.svg


+ 3 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/wait_public.svg

@@ -0,0 +1,3 @@
+<svg width="82" height="92" viewBox="0 0 82 92" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M80.2862 39.9183C78.8784 41.6897 69.6382 43.319 63.4682 45.206C61.4707 46.0376 59.2758 46.2763 57.1462 45.8934C53.62 44.74 52.7376 41.1576 49.4328 39.9084C47.8763 44.2674 46.5709 49.2312 44.8954 53.2135C51.2438 54.694 57.7443 55.6127 59.8693 61.0127C61.3539 64.245 62.4628 67.637 63.1741 71.1221C64.691 79.265 70.7751 92.6031 57.8864 91.9785C56.786 91.9256 55.8573 90.6566 55.3649 89.8602C52.7938 85.6961 52.0833 70.4413 48.5372 68.356C45.2082 66.5475 41.4236 65.7504 37.648 66.0624C34.2308 71.8061 31.2367 83.0259 27.6742 83.0391C21.9172 83.0556 5.67424 75.5802 1.81426 71.5682C0.958352 70.0412 0.638406 68.2716 0.905443 66.5416C1.29491 65.5519 1.90664 64.665 2.69339 63.9493C3.48014 63.2336 4.42086 62.7083 5.4429 62.414C9.95062 61.3597 15.5192 67.7446 21.3058 68.3989C24.6932 58.6465 29.9809 48.286 31.2797 35.8237L31.3359 35.447H30.8203C30.2684 35.48 30.1593 35.5494 29.928 35.8006C24.2074 38.3915 20.559 55.1236 12.687 53.6927C9.15748 53.0515 8.98563 49.218 9.49457 46.3527C15.0796 40.7346 18.2357 31.3061 24.6668 26.6034C27.2224 24.7695 30.1105 23.4506 33.17 22.7203C34.2238 22.5023 35.2899 22.3489 36.3624 22.2609C39.5093 21.6189 42.7588 21.6772 45.8806 22.4317C49.0024 23.1861 51.92 24.6183 54.4263 26.6265C57.7311 29.1183 59.2943 34.5844 62.3942 35.8799C64.0631 36.5772 76.5584 29.7991 80.24 34.211C80.8381 35.0435 81.1634 36.041 81.1711 37.0661C81.1788 38.0912 80.8685 39.0934 80.2829 39.9349L80.2862 39.9183ZM49.2213 20.6482C46.8467 20.6608 44.5411 19.8497 42.6975 18.3531C40.8538 16.8565 39.5861 14.7669 39.1102 12.4404C38.6344 10.1139 38.98 7.6944 40.088 5.5941C41.196 3.4938 42.998 1.84264 45.1869 0.921945C47.3758 0.00124532 49.8162 -0.132038 52.0924 0.544803C54.3686 1.22164 56.3396 2.66673 57.6698 4.63387C59 6.60101 59.607 8.9685 59.3874 11.333C59.1677 13.6975 58.1351 15.9127 56.4654 17.6012C54.5468 19.5321 51.9433 20.6272 49.2213 20.6482Z" fill="white"/>
+</svg>

File diff suppressed because it is too large
+ 2 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/wait_stu.svg


+ 3 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/wait_zhb.svg

@@ -0,0 +1,3 @@
+<svg width="92" height="92" viewBox="0 0 92 92" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M67.9888 72.4037C68.5516 72.0257 69.0651 71.6519 69.5334 71.1836L88.4217 52.3036C90.0876 50.6286 91.0228 48.3623 91.0228 45.9999C91.0228 43.6376 90.0876 41.3713 88.4217 39.6963L69.5334 20.808C67.8586 19.1433 65.5932 18.209 63.2318 18.209C60.8705 18.209 58.6051 19.1433 56.9303 20.808L50.3206 27.4177L58.29 35.383C59.6826 36.7726 60.7875 38.4233 61.5413 40.2406C62.2952 42.0578 62.6832 44.0058 62.6832 45.9732C62.6832 47.9406 62.2952 49.8887 61.5413 51.7059C60.7875 53.5231 59.6826 55.1738 58.29 56.5635C57.7141 57.128 56.9398 57.4442 56.1333 57.4442C55.3269 57.4442 54.5526 57.128 53.9767 56.5635C53.4122 55.9876 53.096 55.2133 53.096 54.4068C53.096 53.6004 53.4122 52.8261 53.9767 52.2501C55.6413 50.5754 56.5757 48.3099 56.5757 45.9486C56.5757 43.5872 55.6413 41.3218 53.9767 39.647L46.0073 31.6776L35.0966 20.8121C34.9076 20.6232 34.768 20.4835 34.579 20.3438C32.8717 18.8916 30.6814 18.1334 28.4417 18.2194C26.202 18.3053 24.0762 19.2292 22.4852 20.808L3.60107 39.6922C1.93515 41.3671 1 43.6335 1 45.9958C1 48.3582 1.93515 50.6245 3.60107 52.2994L22.4852 71.1836C24.1602 72.8495 26.4265 73.7847 28.7889 73.7847C31.1512 73.7847 33.4175 72.8495 35.0925 71.1836L41.6981 64.5288L33.7328 56.5635C32.3402 55.1738 31.2353 53.5231 30.4815 51.7059C29.7276 49.8887 29.3396 47.9406 29.3396 45.9732C29.3396 44.0058 29.7276 42.0578 30.4815 40.2406C31.2353 38.4233 32.3402 36.7726 33.7328 35.383C34.3087 34.8185 35.083 34.5023 35.8894 34.5023C36.6959 34.5023 37.4702 34.8185 38.0461 35.383C38.6106 35.9589 38.9268 36.7332 38.9268 37.5396C38.9268 38.3461 38.6106 39.1204 38.0461 39.6963C36.3814 41.3711 35.4471 43.6365 35.4471 45.9979C35.4471 48.3592 36.3814 50.6247 38.0461 52.2994L56.9303 71.1836C58.3689 72.599 60.2421 73.489 62.2481 73.7103C64.2541 73.9316 66.2762 73.4714 67.9888 72.4037Z" fill="white"/>
+</svg>

+ 3 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000000251.svg

@@ -0,0 +1,3 @@
+<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M41.2937 29.2312C41.2937 27.1562 40.1625 22.5687 37.7062 17.9312C37.6625 19.4812 36.4062 23.2375 35.4812 24.2375C34.8625 22.4875 34.7062 10.8062 19.3 6.875C20.2375 18.4937 8.6687 18.325 8.6687 30.8625C8.6687 37.525 12.6625 43.2562 18.3812 45.8C17.075 38.1125 24.6687 37.125 23.4437 29.2062C31.4437 33.9937 33.325 39.5937 31.1625 45.975C37.55 43.3 41.8062 36.6062 41.2937 29.2312Z" fill="white"/>
+</svg>

File diff suppressed because it is too large
+ 1 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000000252.svg


+ 7 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000000253.svg

@@ -0,0 +1,7 @@
+<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <g opacity="0.9">
+    <path
+      d="M37.127 33.6343C37.4732 33.9976 38.1524 35.6592 37.1407 36.6875C36.9434 36.8923 36.707 37.0555 36.4454 37.1673C36.1839 37.2791 35.9026 37.3373 35.6182 37.3384H35.5958C35.2975 37.334 35.0031 37.2699 34.73 37.1498C34.457 37.0297 34.2108 36.856 34.0059 36.6392C28.9073 31.3135 23.7032 25.9033 18.5318 20.5601C16.9053 18.875 16.1231 18.0498 15.4332 17.3223C14.6007 16.4434 13.8814 15.686 11.9742 13.7227C11.8453 13.5884 11.7237 13.4521 11.6065 13.3208C11.4341 13.1255 11.2711 12.9434 11.0967 12.7788C10.7003 12.4009 10.1763 11.8169 10.1832 11.0967C10.1876 10.6084 10.4371 10.1309 10.9244 9.68066C11.6046 9.04883 12.7081 8.5918 14.0928 10.0132C17.1724 13.1543 18.9864 15.0303 20.9063 17.0161C22.4312 18.5933 24.0089 20.2246 26.3751 22.6538C28.3326 24.6504 29.8848 26.2378 31.3853 27.7729C33.1666 29.5947 34.8492 31.3154 37.1192 33.6284L37.127 33.6343ZM16.8687 22.1631C16.003 21.29 15.1597 21.272 14.6461 22.6958C13.7799 25.1055 12.7799 27.4692 11.8834 29.8794C11.1812 31.7759 11.605 32.3936 13.6163 32.4185C16.2574 32.4487 18.8922 32.4243 21.733 32.4243C20.3697 36.7793 19.0679 40.8613 17.7589 44.9629C17.5533 45.5923 17.4625 46.1797 18.0616 46.5254C18.5709 46.8213 19.0557 46.5073 19.4429 46.0835C22.4166 42.8579 25.3866 39.6291 28.3531 36.397C29.129 35.5493 29.0987 34.6709 28.3111 33.8652C24.5201 29.9424 20.706 26.0417 16.8687 22.1631ZM37.5425 20.166C35.5626 20.1602 33.6119 20.166 31.2676 20.166C31.9024 19.0762 32.3453 18.2573 32.8179 17.4155C34.8472 13.9268 36.9005 10.4331 38.9112 6.91748C39.8023 5.36768 39.5113 4.72021 37.961 4.71436C32.3272 4.68994 26.7056 4.71436 21.0792 4.68994C20.3336 4.68994 19.9337 4.88525 19.7398 5.70752C19.4376 7.04541 18.9703 8.354 18.5528 9.65723C18.3521 10.269 18.3653 10.771 18.8555 11.292C23.3751 16.1562 27.8796 21.0358 32.3692 25.9307C33.066 26.6929 34.0108 26.7544 34.5865 26.269C36.0777 25.0021 37.4579 23.61 38.712 22.1079C39.5289 21.1201 38.8814 20.1699 37.5425 20.166Z"
+      fill="white" />
+  </g>
+</svg>

+ 4 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000000254.svg

@@ -0,0 +1,4 @@
+<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M45.9722 25.0894C42.3031 31.1073 9.23142 20.0609 7.03629 18.7226C5.8028 17.9705 4.58024 16.8336 5.27221 15.6986C5.50425 15.318 6.27615 15.0382 7.4481 14.8438L7.61572 9.98185C7.63881 9.32472 8.23542 8.83547 8.8851 8.94725L9.32004 9.02132C10.1109 9.15389 10.8017 9.62634 11.2118 10.3145L13.7155 14.5259C15.6763 14.5514 17.8522 14.6522 20.1137 14.8191L15.7551 5.75255C15.4101 5.0341 15.9265 4.20233 16.7204 4.19227L17.5341 4.1803C18.8911 4.164 20.1957 4.7077 21.1334 5.68494L31.1929 16.1577C35.8361 16.9517 39.8345 18.0005 41.8325 19.2187C42.366 19.544 42.8946 19.8849 43.3884 20.2372L38.7729 19.6652L37.9429 20.8279L45.0989 21.667C46.2464 22.8048 46.6994 23.8967 45.9722 25.0894ZM25.0523 26.5885L26.4268 34.3951C26.5101 34.87 26.8973 35.2366 27.3788 35.2878L28.1467 35.3738C29.5462 35.5325 30.8941 34.7742 31.4841 33.4931L33.8425 28.3635C33.9888 28.0471 33.8003 27.6758 33.4616 27.6045L25.7052 25.9611C25.3263 25.8793 24.9861 26.2079 25.0523 26.5885Z" fill="white"/>
+<rect x="4.89062" y="37.4502" width="42.4265" height="3.38509" rx="1.69254" fill="white"/>
+</svg>

File diff suppressed because it is too large
+ 3 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000000255.svg


+ 7 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000010101.svg

@@ -0,0 +1,7 @@
+<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <g opacity="0.9">
+    <path
+      d="M37.127 33.6343C37.4732 33.9976 38.1524 35.6592 37.1407 36.6875C36.9434 36.8923 36.707 37.0555 36.4454 37.1673C36.1839 37.2791 35.9026 37.3373 35.6182 37.3384H35.5958C35.2975 37.334 35.0031 37.2699 34.73 37.1498C34.457 37.0297 34.2108 36.856 34.0059 36.6392C28.9073 31.3135 23.7032 25.9033 18.5318 20.5601C16.9053 18.875 16.1231 18.0498 15.4332 17.3223C14.6007 16.4434 13.8814 15.686 11.9742 13.7227C11.8453 13.5884 11.7237 13.4521 11.6065 13.3208C11.4341 13.1255 11.2711 12.9434 11.0967 12.7788C10.7003 12.4009 10.1763 11.8169 10.1832 11.0967C10.1876 10.6084 10.4371 10.1309 10.9244 9.68066C11.6046 9.04883 12.7081 8.5918 14.0928 10.0132C17.1724 13.1543 18.9864 15.0303 20.9063 17.0161C22.4312 18.5933 24.0089 20.2246 26.3751 22.6538C28.3326 24.6504 29.8848 26.2378 31.3853 27.7729C33.1666 29.5947 34.8492 31.3154 37.1192 33.6284L37.127 33.6343ZM16.8687 22.1631C16.003 21.29 15.1597 21.272 14.6461 22.6958C13.7799 25.1055 12.7799 27.4692 11.8834 29.8794C11.1812 31.7759 11.605 32.3936 13.6163 32.4185C16.2574 32.4487 18.8922 32.4243 21.733 32.4243C20.3697 36.7793 19.0679 40.8613 17.7589 44.9629C17.5533 45.5923 17.4625 46.1797 18.0616 46.5254C18.5709 46.8213 19.0557 46.5073 19.4429 46.0835C22.4166 42.8579 25.3866 39.6291 28.3531 36.397C29.129 35.5493 29.0987 34.6709 28.3111 33.8652C24.5201 29.9424 20.706 26.0417 16.8687 22.1631ZM37.5425 20.166C35.5626 20.1602 33.6119 20.166 31.2676 20.166C31.9024 19.0762 32.3453 18.2573 32.8179 17.4155C34.8472 13.9268 36.9005 10.4331 38.9112 6.91748C39.8023 5.36768 39.5113 4.72021 37.961 4.71436C32.3272 4.68994 26.7056 4.71436 21.0792 4.68994C20.3336 4.68994 19.9337 4.88525 19.7398 5.70752C19.4376 7.04541 18.9703 8.354 18.5528 9.65723C18.3521 10.269 18.3653 10.771 18.8555 11.292C23.3751 16.1562 27.8796 21.0358 32.3692 25.9307C33.066 26.6929 34.0108 26.7544 34.5865 26.269C36.0777 25.0021 37.4579 23.61 38.712 22.1079C39.5289 21.1201 38.8814 20.1699 37.5425 20.166Z"
+      fill="white" />
+  </g>
+</svg>

+ 3 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-JY2012000010102.svg

@@ -0,0 +1,3 @@
+<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M37.6758 25.3988C36.0125 21.8952 30.0267 18.0423 30.0267 18.0423L27.3643 36.2573C27.3643 36.2573 24.0377 33.4557 24.0377 32.0549C23.7057 29.9522 28.6955 18.0423 28.6955 18.0423L25.369 19.0936L21.3784 18.0423C17.72 18.3917 9.73571 28.2004 8.40453 31.7039C7.40843 34.8566 8.40453 38.7095 9.73571 39.4099C12.0646 40.8106 17.0528 40.4613 17.0528 40.4613L16.501 45.7909H34.5627L33.5003 33.5822C33.5003 33.5822 35.0148 37.6581 35.3484 39.7608C35.6805 41.8621 35.6252 45.7909 35.6252 45.7909H41.9953C41.9806 45.4669 41.9196 43.9322 41.6662 39.4099C41.3342 33.4558 39.671 29.2518 37.6758 25.3988ZM24.7034 38.36C24.3714 38.7095 21.3784 39.0589 18.384 39.0589C15.3895 39.0589 12.0646 39.0589 11.399 38.36C10.7349 37.6581 9.40372 36.6083 10.0678 33.8066C10.0678 29.9522 17.72 21.8952 18.384 21.1948C18.384 21.1948 22.3745 31.3529 23.3737 33.1047C24.3714 34.8566 25.3689 35.9063 25.3689 35.9063C25.3689 35.9063 25.369 37.6581 24.7034 38.36ZM41.9953 45.7909C41.9999 45.8936 41.9999 45.875 41.9999 45.7909H41.9953ZM25.6378 16.6999C29.6093 16.6999 32.8283 13.4398 32.8283 9.41769C32.8283 5.39716 29.6093 2.13867 25.6378 2.13867C21.6647 2.13867 18.4441 5.39716 18.4441 9.41769C18.4441 13.4398 21.6647 16.6999 25.6378 16.6999Z" fill="white"/>
+</svg>

File diff suppressed because it is too large
+ 2 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-add-role-2.svg


File diff suppressed because it is too large
+ 2 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-add-role.svg


+ 5 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-checkbox-selected.svg

@@ -0,0 +1,5 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="20" height="20" rx="4" fill="#03AEBC"/>
+<rect width="2.19983" height="7.69941" rx="1.09992" transform="matrix(0.707087 -0.707127 0.707087 0.707127 3 9.6666)" fill="white"/>
+<rect width="2.19983" height="12.0991" rx="1.09992" transform="matrix(-0.707087 -0.707127 0.707087 -0.707127 8.44531 15.1113)" fill="white"/>
+</svg>

+ 3 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-checkbox.svg

@@ -0,0 +1,3 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect opacity="0.8" x="0.5" y="0.5" width="19" height="19" rx="4" stroke="#094B88" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"/>
+</svg>

File diff suppressed because it is too large
+ 1 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon-user.svg


File diff suppressed because it is too large
+ 14 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon_bg-select.svg


File diff suppressed because it is too large
+ 29 - 0
SourceCode/WeApp.Web/Content/Image/ExerciseV2/zhb/zh_icon_bg.svg


BIN
SourceCode/WeApp.Web/Content/Image/Upload/home.png


BIN
SourceCode/WeApp.Web/Content/Image/bg_V2.png


BIN
SourceCode/WeApp.Web/Content/Image/logo.png


BIN
SourceCode/WeApp.Web/Content/Image/logo_bk.png


BIN
SourceCode/WeApp.Web/Content/Image/logo_bk2.png


+ 6 - 0
SourceCode/WeApp.Web/Content/Image/tittle_logo.svg

@@ -0,0 +1,6 @@
+<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M69.8499 31.4267C71.1825 30.927 72.6485 31.5933 73.1482 32.926C73.1815 32.9926 73.1815 33.0592 73.2148 33.1259C74.0478 36.0577 74.4809 39.1229 74.4809 42.1547C74.4809 60.4122 59.6884 75.1714 41.4643 75.1714C30.0701 75.1714 20.0086 69.3743 14.0783 60.6121C12.8789 58.8463 13.6452 56.4142 15.6441 55.6812L18.9092 54.4485C20.5083 53.8488 22.3074 54.3819 23.3402 55.7479C27.4715 61.2451 34.0681 64.81 41.4643 64.81C53.9913 64.81 64.1195 54.6817 64.1195 42.1547C64.1195 41.3551 64.0862 40.5888 63.9862 39.8225L39.7319 57.447C38.3992 58.4132 36.5002 57.447 36.5668 55.7812L36.9 43.8871L35.2342 44.5202L20.2418 50.184L10.5467 53.8488L8.88092 54.4818C7.68153 54.9483 6.81531 53.3157 7.84811 52.5495L9.68052 51.2168L16.5104 46.2526L18.9092 44.5202L26.472 38.9896C33.1352 34.1587 39.4654 29.561 43.1302 26.8623C44.4628 25.8962 46.3285 26.8957 46.2952 28.5282L45.9287 40.4555L47.9943 39.6892C47.9943 39.6892 54.9908 37.0572 62.6202 34.1587L65.752 32.9926L69.8499 31.4267Z" fill="#316EF3"/>
+<path d="M35.2342 44.5202L36.9 43.8871L45.9287 40.4555L46.2952 28.5282C46.3285 26.8957 44.4628 25.8962 43.1302 26.8623C39.4654 29.561 33.1352 34.1587 26.472 38.9896L18.9091 44.5202L16.5104 46.2526L9.68052 51.2168L7.84811 52.5495C6.81531 53.3157 7.68153 54.9483 8.88092 54.4818L10.5467 53.8488L20.2418 50.184L35.2342 44.5202Z" fill="#316EF3"/>
+<path d="M67.2511 16.5C59.3884 7.07144 47.0947 2.80691 35.0342 4.50606C24.7394 5.97199 15.4108 12.5354 10.2135 21.4975C7.14836 26.8282 5.54917 32.8918 5.51586 39.0221C5.48254 41.6208 7.81468 44.1528 10.5133 44.0196C13.2453 43.8863 15.4774 41.8207 15.5108 39.0221C15.5108 37.9226 15.5774 36.8565 15.6773 35.7904C15.7107 35.5905 15.8439 34.8242 15.6773 35.757C15.7107 35.5238 15.744 35.3239 15.7773 35.0907C15.8772 34.491 16.0105 33.9246 16.1438 33.3249C16.377 32.3254 16.6768 31.3259 17.0433 30.3264C17.1433 30.0599 17.643 28.7939 17.2765 29.6601C17.5097 29.127 17.743 28.594 18.0095 28.0609C18.4759 27.1614 18.9757 26.2618 19.5087 25.3956C19.7753 24.9625 20.0751 24.5627 20.375 24.1295C20.5082 23.963 20.6415 23.7631 20.7748 23.5965C20.2084 24.3628 20.8081 23.5632 20.9413 23.3966C22.3073 21.7974 23.7732 20.2981 25.439 18.9988C24.7061 19.5985 25.7722 18.7656 26.0054 18.599C26.4385 18.2991 26.8383 18.0326 27.2714 17.7661C28.2043 17.1997 29.1705 16.6666 30.17 16.2002C30.4365 16.0669 31.6359 15.5672 30.803 15.9003C31.2694 15.7004 31.7692 15.5339 32.2689 15.3673C33.335 15.0008 34.4011 14.7009 35.5006 14.4677C36.0003 14.3678 36.5334 14.2678 37.0665 14.1679C37.6661 14.0679 36.9332 14.1679 36.8666 14.2012C37.1664 14.1679 37.4663 14.1346 37.7661 14.1012C38.9655 14.0013 40.1649 13.968 41.3643 14.0013C43.9629 14.0679 45.2623 14.2678 48.0275 15.1341C48.6939 15.334 49.3269 15.5672 49.9599 15.8004C50.0598 15.8337 50.8261 16.1669 50.1598 15.867C50.4596 16.0003 50.7595 16.1336 51.0926 16.3001C52.292 16.8998 53.4581 17.5662 54.5909 18.2991C54.8574 18.499 55.1239 18.6656 55.4238 18.8655C55.557 18.9655 55.6903 19.0654 55.8236 19.1654C55.557 18.9322 55.5237 18.9321 55.7903 19.132C56.3233 19.5318 56.8231 19.9983 57.3228 20.4314C58.3556 21.3976 59.3218 22.4304 60.2546 23.5298C61.9871 25.5955 65.5186 25.3289 67.3177 23.5298C69.3167 21.5308 68.9835 18.5657 67.2511 16.5Z" fill="#01A4FF"/>
+<path d="M27.209 34.3545C29.785 34.3545 31.8733 32.2662 31.8733 29.6902C31.8733 27.1142 29.785 25.0259 27.209 25.0259C24.633 25.0259 22.5447 27.1142 22.5447 29.6902C22.5447 32.2662 24.633 34.3545 27.209 34.3545Z" fill="#00E0FF"/>
+</svg>

+ 8 - 0
SourceCode/WeApp.Web/Content/Scss/ExerciseV2/_stu-body.scss

@@ -0,0 +1,8 @@
+body {
+    --mc: #03AEBC;
+    --tc: #36536D;
+    --brc: #80BAEF;
+    --bgc: #e9f2fd;
+    --bg: rgba(255,255,255,0.4);
+    font-family: 'Source Han Sans';
+}

+ 34 - 0
SourceCode/WeApp.Web/Content/Scss/ExerciseV2/_stu-box.scss

@@ -0,0 +1,34 @@
+@import "_stu-box_header.scss";
+
+.box {
+    width: 100%;
+    position: relative;
+    font-family: 'Source Han Sans';
+    background: none;
+    display: flex;
+    flex-direction: column;
+
+    .box-title {
+        width: 100%;
+        text-align: center;
+        font-weight: 600;
+        font-size: 38px;
+        color: #fff;
+        margin-top: 6px
+    }
+
+    .box-body {
+        margin: 10px 30px 40px;
+        border: 2px solid #fff;
+        border-radius: 5px;
+    }
+
+    .body-card {
+        background: #e9f2fd;
+        border-radius: 5px;
+        padding: 10px;
+        margin-bottom: 10px;
+        display: flex;
+        position: relative;
+    }
+}

+ 36 - 0
SourceCode/WeApp.Web/Content/Scss/ExerciseV2/_stu-box_header.scss

@@ -0,0 +1,36 @@
+.box {
+    .box-header {
+        display: flex;
+        height: 35px;
+        width: 100%;
+        /*background: linear-gradient(90deg,#b2d9f5,#c2d8f4);*/
+        background: linear-gradient(90deg, rgba(82, 197, 233, 0.411765) 0%, rgba(59, 221, 243, 0.12549) 35.99%, rgba(67, 219, 252, 0.00784314) 87.02%);
+        box-shadow: inset 0px -2px 4px rgba(117, 185, 248, 0.52);
+        border-bottom: 1px solid rgba(98, 156, 215, 0.05);
+        position: relative;
+
+
+        .title {
+            --w: 47px;
+            display: flex;
+            position: relative;
+            height: 100%;
+            padding-left: 5px;
+            margin-left: var(--w);
+            font-size: 18px;
+            font-weight: 600;
+            color: #094B88;
+            align-items: center;
+
+            &:before {
+                content: "";
+                position: absolute;
+                left: calc(1px - var(--w));
+                height: 100%;
+                width: var(--w);
+                background: url("/Content/Image/ExerciseV2/title_icon.svg") no-repeat;
+                background-size: 100% 100%;
+            }
+        }
+    }
+}

+ 119 - 0
SourceCode/WeApp.Web/Content/Scss/ExerciseV2/_stu-form.scss

@@ -0,0 +1,119 @@
+
+.btn {
+    margin: 5px auto;
+    padding: 8px 20px;
+    background: #03AEBC;
+    border-radius: 8px;
+    color: #DAEDFF;
+    font-size: 16px;
+
+    &.btn-small {
+        font-size: 14px;
+        padding: 3px 12px;
+        border-radius: 4px;
+    }
+
+    &:hover {
+        background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
+        border-color: rgba(0, 0, 0, 0.1);
+        color: #DAEDFF;
+        font-weight:600;
+    }
+}
+
+.vb-btn {
+    color: #176bbc;
+    font-size: 14px;
+    padding: 5px 10px;
+    background-image: url("/Content/Image/ExerciseV2/btn.png");
+    background-repeat: no-repeat;
+    background-size: 100% 100%;
+    font-weight: 600;
+
+    &.active {
+        color: #fff;
+        background-image: url("/Content/Image/ExerciseV2/btn-active.png");
+    }
+}
+.form {
+    .form-control {
+        background: var(--bgc);
+        color: var(--tc);
+        border-radius: 4px;
+        border: 1px solid #b5cce2;
+        font-size: 1rem;
+
+        &:active, &:focus {
+            border: 1px solid var(--brc);
+        }
+    }
+}
+.select2-container {
+    --h: 38px;
+    font-size: 1rem;
+
+
+    &.select2-container--open {
+        .select2-selection--single {
+            border-color: var(--brc);
+        }
+    }
+
+    .select2-selection--single {
+        border-radius: 4px !important;
+        background: var(--bgc);
+        border: 1px solid #b5cce2;
+        height: var(--h);
+        line-height: var(--h);
+        color: var(--tc);
+        padding: 0 15px;
+
+        .select2-selection__rendered {
+            color: inherit;
+            margin: 0;
+            padding: 0;
+            line-height: var(--h);
+            font-size: 1rem;
+        }
+
+        .select2-selection__arrow {
+            right: 8px;
+            height: var(--h);
+        }
+    }
+
+    .select2-dropdown {
+        background: #fefefe;
+        border: 1px solid #b5cce2;
+        border-radius: 4px;
+        margin-top: 2px;
+
+        .select2-search {
+            padding: 8px 10px;
+
+            .select2-search__field {
+                border: 1px solid var(--brc);
+                background: var(--bgc);
+                color: var(--tc);
+                border-radius: 4px;
+            }
+        }
+
+        .select2-results {
+
+            .select2-results__option {
+                color: var(--tc);
+
+                &.select2-results__option--highlighted {
+                    background: #e7edf4;
+                }
+
+                &.select2-results__option[aria-selected=true] {
+                    background: #ecf3fc;
+                    font-weight: 600;
+                }
+            }
+        }
+    }
+}
+

+ 610 - 0
SourceCode/WeApp.Web/Content/Scss/ExerciseV2/screen.scss

@@ -0,0 +1,610 @@
+@import "./_stu-body";
+@import "./_stu-box_header";
+@import "./_stu-form";
+
+
+body {
+    /*background: #3c3c3c;*/
+    /*background: #005f61;*/
+    /*background-image: linear-gradient(150deg,#005f61,#007f61);*/
+    font-size: 22px;
+    color: var(--mc);
+    width: 100vw;
+}
+
+.main-box {
+    border-left: 0;
+    box-sizing: padding-box;
+    padding: 0;
+    margin:0;
+
+    .main-box-body {
+        margin: 0 30px 0;
+    }
+
+    > div {
+        /* width: calc(100vw - 20px) !important;
+        height: calc(100% - 20px) !important;*/
+        display: flex;
+        flex: 1 auto;
+    }
+}
+
+.box {
+    margin: 0 10px;
+    font-size: 1.25rem;
+
+    > .box {
+        margin: 0;
+        width: 100%;
+        height: 100%;
+        border: 2px solid #fff;
+        border-radius: 10px;
+    }
+
+    .box-header {
+        height: 50px;
+        border-radius: 10px 10px 0 0;
+        position: relative;
+
+        .title {
+            --w: 50px;
+            font-size: 22px;
+        }
+    }
+
+    .box-body {
+        background: linear-gradient(174.54deg, rgba(204, 224, 248, 0.76) 0.23%, rgba(242, 249, 255, 0.76) 120.71%);
+        overflow-y: auto;
+        margin: 0;
+        padding: 10px 10px 0;
+        height: calc(100% - 50px);
+    }
+
+    .scene-box-body {
+        background: linear-gradient(174.54deg, rgba(204, 224, 248, 0.76) 0.23%, rgba(242, 249, 255, 0.76) 120.71%);
+        overflow-y: auto;
+        margin: 0;
+        padding: 10px 10px 0;
+        height: calc(100% - 50px);
+
+        .box-body {
+            background: none;
+            margin: 0;
+            padding: 0;
+        }
+    }
+
+    .media-box {
+        width: 98%;
+        margin: 10px auto 0;
+        border: 2px solid #4585CC;
+        border-radius: 5px;
+        background: var(--bg);
+
+        .body-content {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            width: 100%;
+            height: 100%;
+            /*max-width: 100%;
+            max-height: 100%;*/
+            overflow: hidden;
+
+            span {
+                color: #4585CC;
+                font-size: 30px;
+                font-weight: 600;
+                display: block;
+            }
+
+            img, video {
+                width: auto;
+                height: 100%;
+                padding: 0;
+                overflow: hidden;
+            }
+        }
+    }
+
+    .scene-box {
+        width: 100%;
+        margin: 8px 0;
+        display: flex;
+        flex-direction: column;
+
+        .title {
+            width: 100%;
+            color: #fff;
+            background: none;
+            position: relative;
+            height: 35px;
+            border-bottom: 2px solid #4585CC;
+
+            span.text {
+                display: inline-block;
+                padding: 0px 20px;
+                line-height: 35px;
+                text-align: center;
+                min-width: 250px;
+                max-width: 300px;
+                background: #4585CC;
+                position: relative;
+                overflow: hidden;
+                white-space: nowrap;
+                text-overflow: ellipsis;
+                font-weight: 600;
+
+                &:after {
+                    position: absolute;
+                    content: "";
+                    right: -1px;
+                    height: 0;
+                    width: 0;
+                    border-left: 10px solid #4585CC;
+                    border-bottom: 18px solid #4585CC;
+                    border-right: 10px solid none;
+                    border-top: 18px solid none;
+                }
+            }
+
+            .attach-box, .tool {
+                position: absolute;
+                font-size: 0.875rem;
+                right: 40px;
+                top: 5px;
+            }
+
+            .tool {
+                right: 10px;
+                top: 10px;
+                color: #4585CC;
+                opacity: 1;
+            }
+
+            .attach1 {
+                color: #fff;
+                background: #4585CC;
+                margin: 0 5px;
+                padding: 3px 8px;
+            }
+        }
+
+        .guide-box {
+            margin:10px 0;
+            padding: 10px;
+            background: var(--bg);
+            border-radius: 5px;
+
+            p {
+                margin: 0;
+                font-size: 13px;
+            }
+
+            .guide-title {
+                font-weight: 600;
+            }
+        }
+
+        .desc {
+            padding: 8px;
+            color: #36536D;
+            background: var(--bg);
+            white-space: normal;
+            word-break: break-word;
+            min-height: 40px;
+            font-size: 16px;
+        }
+
+        &.handled {
+            //--bc: rgba(0,0,0,.1);
+            /* --bc: #e5e5e5;
+            --bc2: #108F85;*/
+
+            .title {
+                background: var(--bc);
+
+                span.text {
+                    font-size: 90%;
+                    font-weight: 400;
+                    background: #4585CC;
+
+                    &:after {
+                        //background-image: linear-gradient(rgba(0,0,0,.1),rgba(0,0,0,.1));
+                        border-left-color: #4585CC;
+                        border-bottom-color: #4585CC;
+                        border-right-color: none;
+                        border-top-color: none;
+                    }
+                }
+            }
+        }
+
+        &.current-scene {
+            border: 2px solid #4585CC;
+        }
+
+        &.flash-scene {
+            animation: flash 2s 10;
+        }
+    }
+
+    .log-box {
+        font-size: 18px;
+        width: calc(100% - 20px);
+        margin: 8px auto;
+        display: flex;
+        align-items: center;
+        justify-content: left;
+        color: #415B73;
+        background: var(--bg);
+        min-height: 60px;
+        padding: 5px 10px;
+        position: relative;
+
+        .role {
+            font-weight: 600;
+            white-space: normal;
+            color: #415B73;
+            width: 180px;
+            text-align: center;
+        }
+
+        .name {
+            word-break: break-word;
+        }
+
+        .word {
+            word-break: break-word;
+            text-align: left;
+            width: calc(100% - 200px);
+            color: var(--tc);
+            text-indent: 2em;
+        }
+
+        .reviews-box {
+            position: absolute;
+            right: 20px;
+            bottom: 5px;
+            color: var(--mc);
+            cursor: pointer;
+
+            .disabled {
+                cursor: not-allowed;
+            }
+
+            i {
+                font-size: 15px;
+                margin: 0 5px;
+            }
+
+            .fas:nth-child(1) {
+                color: #007bff;
+            }
+
+            .fas:nth-child(2) {
+                color: #dc3545;
+            }
+        }
+
+        &:before {
+            content: "";
+            position: absolute;
+            left: 0;
+            top: 0;
+            width: 0;
+            height: 0;
+            border-left: 15px solid transparent;
+            border-top: 15px solid transparent;
+            border-right: 15px solid transparent;
+            border-bottom: 15px solid transparent;
+        }
+        /*&:after {
+            content: "";
+            position: absolute;
+            right: 0;
+            bottom: 0;
+            width: 0;
+            height: 0;
+            border-left: 15px solid transparent;
+            border-top: 15px solid transparent;
+            border-right: 15px solid #d9d9d9;
+            border-bottom: 15px solid #d9d9d9;
+        }*/
+        &.send {
+            background-image: linear-gradient(-45deg, rgba(255,255,255,.2), transparent);
+
+            &.specialist {
+                padding: 5px 10px 25px;
+                min-height: 90px;
+            }
+
+            &:before {
+                border-left: 15px solid var(--mc);
+                border-top: 15px solid var(--mc);
+            }
+
+            .role {
+                color: #03AEBC;
+            }
+            /*&:after {
+                border-right: 15px solid var(--mc);
+                border-bottom: 15px solid var(--mc);
+            }*/
+        }
+    }
+
+    .role-box {
+        display: flex;
+        flex-wrap: wrap;
+        justify-content: center;
+
+        .role {
+            width: calc(50% - 20px);
+            height: 50px;
+            margin: 6px;
+            padding: 0 5px;
+            font-size: 14px;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            text-align: center;
+            white-space: normal;
+            word-break: break-all;
+            color: var(--tc);
+            border: 1px solid var(--brc);
+            background: var(--bg);
+            border-radius: 5px;
+            cursor: pointer;
+            overflow: hidden;
+        }
+    }
+
+    .score-box {
+        font-size: 14px;
+        margin: 5px;
+        padding: 8px 15px;
+        display: flex;
+        justify-content: space-between;
+        border-radius: 5px;
+        color: #262626;
+        background: var(--bg);
+
+
+        .name {
+            width: 130px;
+            line-height: 30px;
+            border-radius: 5px;
+            background: #03AEBC;
+            color: #fff;
+            text-align: center;
+            font-size: 100%;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+        }
+
+        .score {
+            font-size: 110%;
+            font-weight: 600;
+            color: #094B88;
+            line-height: 30px;
+            text-align: center;
+
+            span {
+                padding-left: 10px;
+                font-size: 1rem;
+                font-weight: 400;
+            }
+        }
+
+        &.system-score {
+            .name {
+                font-size: 16px;
+                font-weight: 600;
+            }
+        }
+    }
+
+    &.plan-box {
+        margin: 5px 10px;
+        border: 1px solid #a5d4f4;
+        border-radius: 5px 5px 0 0;
+        display: flex;
+        flex-direction: column;
+        background: linear-gradient(174.54deg, rgba(204, 224, 248, 0.76) 0.23%, rgba(242, 249, 255, 0.76) 120.71%);
+
+        .plan-name {
+            width: 100%;
+            background: #a5d4f4;
+            color: #fff;
+            padding: 5px 0;
+            text-align: center;
+            cursor: pointer;
+        }
+
+        .plan-role {
+            width: 100%;
+            padding: 5px 15px;
+            display: flex;
+            flex-wrap: wrap;
+            justify-content: flex-start;
+
+            .role {
+                width: calc(25% - 12px);
+                height: 50px;
+                font-size: 14px;
+                padding: 0 5px;
+                margin: 6px;
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                text-align: center;
+                white-space: normal;
+                word-break: break-all;
+                color: var(--tc);
+                border: 1px solid var(--brc);
+                background: var(--bg);
+                border-radius: 5px;
+                cursor: pointer;
+                overflow: hidden;
+            }
+        }
+    }
+}
+
+.modal {
+    .modal-header {
+        color: var(--tc);
+
+        .modal-title-span {
+        }
+
+        .close {
+            font-size: 30px;
+        }
+    }
+
+    .modal-footer {
+        margin: 0 auto;
+
+        .btn-outline-iwb {
+            background: #F5F5F5;
+            color: #36536D;
+            border: 1px solid #F3F3F3;
+        }
+
+        .btn-iwb {
+            margin: 0 15px;
+            padding: 8px 20px;
+            background: #03AEBC;
+            border-color: #03AEBC;
+            border-radius: 8px;
+            color: #DAEDFF;
+            font-size: 16px;
+        }
+    }
+}
+
+.load-box {
+    position: fixed;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    font-size: 40px;
+    font-weight: 400;
+    color: #094B88;
+    z-index: 1000;
+    background: rgba(255,255,255,0.6);
+
+    .loading {
+        margin-top: -10%;
+    }
+
+    .loading:after {
+        overflow: hidden;
+        display: inline-block;
+        vertical-align: bottom;
+        animation: ellipsis 2s infinite;
+        content: "\2026";
+    }
+}
+
+.question {
+    display: none;
+
+    button {
+        --w: 80px;
+        width: var(--w);
+        height: var(--w);
+        border-radius: var(--w);
+        position: absolute;
+        left: 50%;
+        top: 40%;
+        transform: translateX(-50%) translateY(-50%);
+        z-index: 5;
+        background-color: #dc3545;
+        color: #fff;
+        border: none;
+        outline: none;
+
+        &:hover {
+            background-color: #bd2130;
+        }
+
+        i {
+            font-size: 40px;
+        }
+
+        &:not(:hover) {
+            animation: 1s shine ease-in-out infinite;
+
+            i {
+                animation: 1s flashing ease-in-out infinite;
+            }
+        }
+    }
+}
+
+@keyframes shine {
+    0% {
+        filter: drop-shadow(0 0 12px #dc3545);
+    }
+
+    50% {
+        filter: drop-shadow(0 0 12px #bd2130);
+    }
+
+    100% {
+        filter: drop-shadow(0 0 12px #dc3545);
+    }
+}
+
+@keyframes flashing {
+    0% {
+        opacity: 1;
+        transform: scale(1)
+    }
+
+    50% {
+        opacity: 0.5;
+        transform: scale(0.9)
+    }
+
+    100% {
+        opacity: 1;
+        transform: scale(1)
+    }
+}
+
+@keyframes shine {
+    0% {
+        filter: drop-shadow(0 0 5px #c82333);
+    }
+
+    100% {
+        filter: drop-shadow(0 0 15px #c82333);
+    }
+}
+
+@keyframes flash {
+    0% {
+        transform: scale(1.0,1.05);
+        //background-image: linear-gradient(rgba(0,0,0,.2),rgba(0,0,0,.2))
+    }
+
+    50% {
+        transform: scale(0.98,0.85);
+        //background-image: linear-gradient(rgba(0,0,0,.5),rgba(0,0,0,.5))
+    }
+
+    100% {
+        transform: scale(1.0,1.05);
+        //background-image: linear-gradient(rgba(0,0,0,.2),rgba(0,0,0,.2));
+    }
+}

+ 118 - 0
SourceCode/WeApp.Web/Content/Scss/ExerciseV2/stu-bg.scss

@@ -0,0 +1,118 @@
+@import "./_stu-body";
+@import "./_stu-form";
+
+.box {
+    width: 100%;
+    position: relative;
+    font-family: 'Source Han Sans';
+    background: none;
+
+    .body {
+        width: 100%;
+        height: calc(100vh - 100px);
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+
+        .icon {
+            --w: 240px;
+            width: var(--w);
+            height: var(--w);
+            position: relative;
+            margin-bottom: 50px;
+            background: url('/Content/Image/ExerciseV2/timer_1.png') no-repeat 100%/100%;
+
+            &:after {
+                content: "";
+                display: block;
+                position: absolute;
+                top: 0;
+                left: 0;
+                width: var(--w);
+                height: var(--w);
+                background: url('/Content/Image/ExerciseV2/timer_2.png') no-repeat 100%/100%;
+                animation: spin 4s linear infinite;
+            }
+
+            .ico {
+                position: absolute;
+                width: 90px;
+                height: 90px;
+                left: 50%;
+                top: 50%;
+                transform: translate(-50%, -50%);
+            }
+        }
+
+        .name {
+            font-weight: 500;
+            font-size: 40px;
+            display: flex;
+            color: #094B88;
+            letter-spacing: 5px;
+            padding-left: 5px;
+            margin-bottom: 20px;
+        }
+
+        .group {
+            width: 150px;
+            padding: 8px;
+            text-align: center;
+            font-size: 18px;
+            color: #fff;
+            background: #094B88;
+            letter-spacing: 2px;
+            opacity: 0.6;
+            border-radius: 40px;
+        }
+    }
+
+    &.public {
+        .title {
+            letter-spacing: 5px;
+            padding-left: 5px;
+        }
+
+        .body {
+            .icon {
+                --w: 180px;
+
+                .ico {
+                    width: 120px;
+                    height: 120px;
+                }
+            }
+
+            .name {
+                font-size: 40px;
+                letter-spacing: 10px;
+                padding-left: 10px;
+            }
+        }
+    }
+}
+
+@media(min-width:1200px) {
+    .box.public .body .icon {
+        --w: 300px;
+    }
+
+    .box.public .body .name {
+        font-size: 90px;
+    }
+}
+
+@keyframes spin {
+    0% {
+        transform: rotate(0deg);
+    }
+
+    50% {
+        transform: rotate(180deg);
+    }
+
+    100% {
+        transform: rotate(360deg);
+    }
+}

+ 58 - 0
SourceCode/WeApp.Web/Content/Scss/ExerciseV2/stu-cmd.scss

@@ -0,0 +1,58 @@
+@import "./_stu-body";
+@import "./_stu-box";
+@import "./_stu-form";
+
+.box {
+    .body {
+        padding: 10px;
+        background: #d8e7f9;
+        background: linear-gradient(174.54deg, rgba(204, 224, 248, 0.76) 0.23%, rgba(242, 249, 255, 0.76) 120.71%);
+        display: flex;
+        flex-direction: column;
+    }
+
+    .role-box {
+        display: flex;
+
+        & > div {
+            width: 100%;
+            padding: 0;
+        }
+        /*  .form-control, .select2-selection--single {
+            border: none;
+        }*/
+    }
+
+    .select-box, .input-box, .cmd-box {
+        background: none;
+    }
+
+    .input-box {
+        display: none;
+        margin-left: 15px;
+    }
+
+    .cmd-box {
+        position: relative;
+        padding: 0;
+
+        .txt {
+            padding: 10px;
+        }
+
+        .btn {
+            position: absolute;
+            right: 15px;
+            bottom: 15px;
+            margin: 5px auto;
+            width: 120px;
+        }
+
+        .help-box {
+            position: absolute;
+            left: 15px;
+            bottom: 15px;
+            width: 70%;
+        }
+    }
+}

+ 152 - 0
SourceCode/WeApp.Web/Content/Scss/ExerciseV2/stu-cmd_leader.scss

@@ -0,0 +1,152 @@
+@import "./_stu-body";
+@import "./_stu-box";
+@import "./_stu-form";
+
+.box {
+    .box-body {
+        position: relative;
+
+        .body {
+            padding: 10px;
+            background: #d8e7f9;
+            background: linear-gradient(174.54deg, rgba(204, 224, 248, 0.76) 0.23%, rgba(242, 249, 255, 0.76) 120.71%);
+            display: flex;
+            flex-direction: column;
+        }
+    }
+
+    .logs-box {
+        width: 100%;
+        display: flex;
+        flex-direction: column;
+
+        .log-box {
+            display: flex;
+            padding: 10px;
+            background: rgba(255, 255, 255, 0.6);
+            border-radius: 8px;
+            margin: 5px 0;
+            overflow: hidden;
+
+            .content {
+                width: 100%;
+                display: flex;
+                flex-direction: column;
+                padding: 5px;
+                padding-right: 15px;
+
+                .title {
+                    font-weight: 600;
+                    color: #03AEBC;
+                    font-size: 14px;
+                }
+
+                .text {
+                    color: #36536D;
+                }
+            }
+
+            .btn-box {
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                padding: 5px;
+
+                .btn {
+                    font-size: 14px;
+                    width: 90px;
+                }
+            }
+
+            &.send {
+                .btn {
+                    background: #DE5E5E;
+                }
+            }
+        }
+
+        .empty {
+            width: 100%;
+            height: 100%;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            color: #36536D;
+            font-size: 16px;
+            opacity: .4;
+        }
+    }
+}
+
+.question {
+    display: none;
+
+    button {
+        --w: 80px;
+        width: var(--w);
+        height: var(--w);
+        border-radius: var(--w);
+        position: absolute;
+        left: 50%;
+        top: 50%;
+        transform: translateX(-50%) translateY(-50%);
+        z-index: 5;
+        color: #fff;
+        border: none;
+        outline: none;
+
+        &:hover {
+            background-color: #bd2130;
+        }
+
+        i {
+            font-size: 40px;
+        }
+
+        &:not(:hover) {
+            animation: 1s shine ease-in-out infinite;
+
+            i {
+                animation: 1s flashing ease-in-out infinite;
+            }
+        }
+    }
+}
+
+.question-text {
+    font-size: 16px;
+    margin-bottom: 10px;
+    font-weight: 600;
+    color: var(--mc);
+}
+
+@keyframes shine {
+    0% {
+        filter: drop-shadow(0 0 12px #dc3545);
+    }
+
+    50% {
+        filter: drop-shadow(0 0 12px #bd2130);
+    }
+
+    100% {
+        filter: drop-shadow(0 0 12px #dc3545);
+    }
+}
+
+@keyframes flashing {
+    0% {
+        opacity: 1;
+        transform: scale(1)
+    }
+
+    50% {
+        opacity: 0.5;
+        transform: scale(0.9)
+    }
+
+    100% {
+        opacity: 1;
+        transform: scale(1)
+    }
+}

+ 280 - 0
SourceCode/WeApp.Web/Content/Scss/ExerciseV2/stu-zhb.scss

@@ -0,0 +1,280 @@
+@import "./_stu-body";
+@import "./_stu-box";
+@import "./_stu-form";
+.box {
+    .box-body {
+        .body {
+            padding: 10px;
+            background: #d8e7f9;
+            background: linear-gradient(174.54deg, rgba(204, 224, 248, 0.76) 0.23%, rgba(242, 249, 255, 0.76) 120.71%);
+            display: flex;
+            flex-direction: column;
+
+
+            .stu {
+                display: none !important;
+            }
+
+            .check-box-icon {
+                background: url("/Content/Image/ExerciseV2/zhb/zh_icon-checkbox.svg") no-repeat;
+                background-size: 100% 100%;
+            }
+
+            .check {
+                .check-box-icon {
+                    background-image: url("/Content/Image/ExerciseV2/zhb/zh_icon-checkbox-selected.svg");
+                }
+            }
+
+            .select {
+                justify-content: space-between;
+
+                .select-box {
+                    display: flex;
+
+                    .select-item {
+                        height: 100px;
+                        margin-left: 15px;
+                        display: flex;
+                        flex-direction: column;
+                        align-items: center;
+                        position: relative;
+
+                        .check-box-icon {
+                            position: absolute;
+                            top: 5px;
+                            left: -8px;
+                            width: 15px;
+                            height: 15px;
+                        }
+
+                        .icon {
+                            width: 80px;
+                            height: 80px;
+                            display: flex;
+                            justify-content: center;
+                            align-items: center;
+                            background: url("/Content/Image/ExerciseV2/zhb/zh_icon_bg.svg") no-repeat;
+                            background-size: 100% 100%;
+
+                            img {
+                                width: 35px;
+                                height: 35px;
+                            }
+                        }
+
+                        .text {
+                            text-align: center;
+                            color: #094B88;
+                        }
+
+                        &.active {
+                            .icon {
+                                background-image: url("/Content/Image/ExerciseV2/zhb/zh_icon_bg-select.svg")
+                            }
+
+                            .text {
+                                font-weight: 600;
+                            }
+                        }
+                    }
+                }
+
+                .add-role {
+                    position: relative;
+                    width: 100px;
+                    height: 100px;
+                    display: flex;
+                    flex-direction: column;
+                    align-items: center;
+                    justify-content: center;
+                    background: #4585cc;
+                    border-radius: 8px;
+
+                    .icon {
+                        width: 80px;
+                        height: 50px;
+                        display: flex;
+                        justify-content: center;
+                        align-items: center;
+
+                        img {
+                            width: 35px;
+                            height: 35px;
+                        }
+                    }
+
+                    .text {
+                        margin-top: 5px;
+                        text-align: center;
+                        color: #fff;
+                    }
+
+                    &:before {
+                        content: "";
+                        position: absolute;
+                        left: -20px;
+                        height: 80%;
+                        border-left: 1px solid #c0dffb;
+                    }
+                }
+            }
+
+            .role {
+                .content-box {
+                    width: 100%;
+                    height: calc(100vh - 380px);
+                }
+
+                .role-content {
+                    display: none;
+
+                    &.active {
+                        display: flex;
+                        flex-direction: column;
+                    }
+                }
+
+                .role-select {
+                    height: 35px;
+                    margin-bottom: 10px;
+                    color: #094B88;
+                    padding-left: 20px;
+                    display: flex;
+                    align-items: center;
+
+                    .check-box-icon {
+                        margin-right: 10px;
+                        width: 20px;
+                        height: 20px;
+                    }
+
+                    span {
+                        color: #5f8bb6;
+                        font-weight: 400;
+                        padding-left: 3px;
+                    }
+
+                    &.check {
+                        font-weight: 600;
+                    }
+                }
+
+                .role-box {
+                    display: flex;
+                    flex-wrap: wrap;
+
+                    .role-item {
+                        position: relative;
+                        width: 100px;
+                        height: 100px;
+                        margin-left: 15px;
+                        margin-bottom: 15px;
+                        display: flex;
+                        flex-direction: column;
+                        align-items: center;
+                        justify-content: center;
+                        background: #FFFFFF;
+                        border: 1px solid #b2d9f5;
+                        border-radius: 8px;
+
+                        .icon {
+                            width: 100%;
+                            height: 40px;
+                            display: flex;
+                            justify-content: center;
+                            align-items: center;
+
+                            img {
+                                width: 35px;
+                                height: 35px;
+                            }
+                        }
+
+                        .text {
+                            width: 100%;
+                            margin-top: 10px;
+                            padding: 0 10px;
+                            line-height: 1.3;
+                            text-align: center;
+                            color: #094B88;
+                            font-size: 14px;
+                        }
+
+                        .close {
+                            position: absolute;
+                            top: 5px;
+                            right: 8px;
+                            font-size: 1.5rem;
+                            font-weight: 700;
+                            line-height: 1;
+                            color: #094B88;
+                            text-shadow: 0 1px 0 #fff;
+                            opacity: 0.8;
+                        }
+
+                        &.check {
+                            .text {
+                                font-weight: 600;
+                            }
+                        }
+
+
+                        &.add {
+                            flex-direction: column;
+                            background: #FFFFFF;
+
+                            .text {
+                                font-size: 12px;
+                            }
+                        }
+                    }
+                }
+            }
+
+            .add-box {
+                position: absolute;
+                display: none;
+                left: calc(50% - 150px);
+                top: 100px;
+                padding: 25px 20px 20px;
+                width: 300px;
+                flex-direction: column;
+
+                input {
+                    padding: 5px 10px;
+                    font-size: 14px;
+                    border: 1px solid #DAEDFF;
+                    border-radius: 5px;
+                    outline: none;
+
+                    &:active, &:focus {
+                        border: 1px solid #DAEDFF;
+                        outline: none;
+                    }
+                }
+
+                .btn-box {
+                    display: flex;
+                    justify-content: center;
+                    margin-top: 15px
+                }
+
+                .btn {
+                    margin: 0 10px;
+                    min-width: 80px;
+
+                    &.cancel {
+                        background: #DAEDFF;
+                        color: #03AEBC;
+                        border: 1px solid;
+                    }
+                }
+            }
+        }
+
+        .btn {
+            min-width: 150px;
+        }
+    }
+}

+ 11 - 9
SourceCode/WeApp.Web/Content/Scss/style.scss

@@ -1,5 +1,6 @@
 /*$main-color: #007bff;*/
-$main-color: #11998e;
+/*$main-color: #11998e;*/
+$main-color: #03aebc;
 
 @mixin dropdownItem {
     border-color: $main-color;
@@ -940,12 +941,13 @@ form {
         margin: 0;
         padding: 10px 0 0;
         text-indent: 1em;
+        color: #36536D;
     }
 
     .swal-title {
         font-size: 16px;
         font-weight: 600;
-        color: #333;
+        color: #094B88;
         line-height: 35px;
 
         &:before {
@@ -987,28 +989,28 @@ form {
 
     .swal-footer {
         text-align: right;
-        margin-top: 0px;
+        margin-top: 10px;
         padding: 5px 10px;
 
         .swal-button-container {
             margin: 0;
 
             .swal-button {
-                background-color: #0074f0;
-                border: 1px solid #0074f0;
+                border: 1px solid #03AEBC;
+                background: #03AEBC;
                 color: #fff;
                 border-radius: 3px;
                 box-shadow: none;
-                font-weight: 600;
+                font-weight: 400;
                 font-size: 14px;
                 padding: 4px 20px;
                 margin-left: 15px;
                 cursor: pointer;
 
                 &.swal-button--cancel {
-                    color: #0074f0;
-                    background-color: #fff;
-                    border: 1px solid #0074f0;
+                    background: #F5F5F5;
+                    color: #36536D;
+                    border: 1px solid #F3F3F3;
                 }
             }
         }

+ 11 - 0
SourceCode/WeApp.Web/Content/V2/Config/Screen/JY2001000010027.json

@@ -0,0 +1,11 @@
+{
+  "WaitVideo": "/Content/V2/JY2001000010027/01.mp4",
+  "DescImage": "/Content/V2/JY2001000010027/desc.png",
+  "HelpImages": [
+    "/Content/V2/Help/01.png",
+    "/Content/V2/Help/02.png",
+    "/Content/V2/Help/03.png",
+    "/Content/V2/Help/04.png",
+    "/Content/V2/Help/05.png"
+  ]
+}

BIN
SourceCode/WeApp.Web/Content/V2/JY2001000010027/01.mp4


BIN
SourceCode/WeApp.Web/Content/V2/JY2001000010027/desc.png


+ 803 - 0
SourceCode/WeApp.Web/Controllers/ExerciseV2Controller.cs

@@ -0,0 +1,803 @@
+using Abp.Auditing;
+using Abp.Authorization;
+using Abp.Domain.Repositories;
+using Abp.Web.Models;
+using Abp.Web.Mvc.Authorization;
+
+using IwbZero.ToolCommon.LogHelpers;
+using IwbZero.ToolCommon.StringModel;
+
+using System;
+using System.Data.Entity;
+using System.IO;
+using System.Speech.Synthesis;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Web.Mvc;
+
+using WeApp.BaseSystem.Query;
+using WeApp.CommonManager.Export;
+using WeApp.Configuration;
+using WeApp.TrainingCamp;
+using WeApp.TrainingCamp.Dto;
+using WeApp.TrainingCampGroup.Dto;
+using WeApp.TrainingPortrait;
+using WeApp.Views.Shared.Camp;
+
+namespace WeApp.Controllers
+{
+    /// <summary>
+    /// 演练实施
+    /// </summary>
+    [AbpAllowAnonymous, DisableAuditing]
+    public class ExerciseV2Controller : IwbControllerBase
+    {
+        public ExerciseV2Controller(QueryAppService queryApp, IRepository<CampGroupInfo, string> groupRepository, IRepository<CampInfo, string> campRepository, PortraitAppService portraitApp, ExportManger exportManger)
+        {
+            QueryApp = queryApp;
+            GroupRepository = groupRepository;
+            CampRepository = campRepository;
+            PortraitApp = portraitApp;
+            _exportManger = exportManger;
+        }
+
+        protected QueryAppService QueryApp { get; }
+        public const string SelectDataKey = "SELECTDATA";
+        private int TimeOut = 60 * 5;
+        protected IRepository<CampInfo, string> CampRepository { get; }
+        protected IRepository<CampGroupInfo, string> GroupRepository { get; }
+        protected PortraitAppService PortraitApp { get; }
+        private readonly ExportManger _exportManger;
+
+        public ActionResult Index()
+        {
+            Session["GroupNo"] = "";
+            Session["CampNo"] = "";
+            return View();
+        }
+
+        public ActionResult Clear()
+        {
+            Session["GroupNo"] = "";
+            Session["CampNo"] = "";
+            return Content("");
+        }
+
+        public ActionResult Select(int? id)
+        {
+            if (id == null)
+            {
+                return RedirectToAction("Index");
+            }
+            switch (id)
+            {
+                case 1:
+                    return RedirectToAction("Student");
+
+                case 2:
+                    return RedirectToAction("Leader");
+
+                case 3:
+                    return RedirectToAction("Public");
+
+                case 4:
+                    return RedirectToAction("Specialist");
+
+                case 5:
+                    return RedirectToAction("Manual");
+
+                case 6:
+                    return RedirectToAction("Gis");
+
+                default:
+                    return RedirectToAction("Index");
+            }
+        }
+
+        /// <summary>
+        /// 学员指挥部页面
+        /// </summary>
+        /// <returns></returns>
+        public ActionResult HeadquarterStu()
+        {
+            return RedirectToAction("Headquarter");
+        }
+
+        /// <summary>
+        /// 指挥长组建指挥部
+        /// </summary>
+        /// <returns></returns>
+        public ActionResult HeadquarterLeader()
+        {
+            return RedirectToAction("Headquarter", new { id = 1 });
+        }
+
+        /// <summary>
+        /// 组建指挥部
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<ActionResult> Headquarter(int? id)
+        {
+            ViewBag.IsLeader = id == 1;
+            string groupNo = (string)Session["GroupNo"];
+            if (groupNo.IsEmpty())
+            {
+                if (id == 1)
+                {
+                    TempData[SelectDataKey] = new SelectCampModel
+                    {
+                        PageUrl = "/ExerciseV2/Leader",
+                        PageTitle = "指挥长屏选择培训营",
+                        HasGroup = true
+                    };
+                    return RedirectToAction("SelectCamp");
+                }
+
+                TempData[SelectDataKey] = new SelectCampModel
+                {
+                    PageUrl = "/ExerciseV2/Student",
+                    PageTitle = "学员屏选择培训营",
+                    HasGroup = true
+                };
+                return RedirectToAction("SelectCamp");
+            }
+            Session["GroupNo"] = groupNo;
+            Session.Timeout = 60 * 5;
+            var group = await GroupRepository.GetAllIncluding(a => a.CampInfo).FirstOrDefaultAsync(a => a.Id == groupNo);
+            if (group == null)
+            {
+                CheckErrors($"未查到到编号为【{groupNo}】的培训营分组!");
+                return Content("");
+            }
+            if (group.CampInfo.CampState == CampStateDefinition.End)
+            {
+                CheckErrors($"培训营已演练结束!");
+            }
+            if (group.CampGroupState == CampGroupStateDefinition.End)
+            {
+                CheckErrors($"培训营分组已演练结束!");
+            }
+            ViewBag.Group = ObjectMapper.Map<CampGroupDto>(group);
+            switch (group.CampGroupState)
+            {
+                case CampGroupStateDefinition.New:
+                    return View("Enter");
+
+                case CampGroupStateDefinition.Ready:
+                    return View("Enter");
+
+                case CampGroupStateDefinition.HeadQuarterBuilding:
+                    ViewBag.HasBuilded = false;
+                    return View();
+
+                case CampGroupStateDefinition.HeadQuarterBuilded:
+                    ViewBag.HasBuilded = true;
+                    return View();
+            }
+
+            if (id == 1)
+            {
+                return RedirectToAction("Leader");
+            }
+            return RedirectToAction("Student");
+        }
+
+        /// <summary>
+        /// 学员页面
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<ActionResult> Student(string id)
+        {
+            string groupNo = id.IsEmpty() ? (string)Session["GroupNo"] : id;
+            if (groupNo.IsEmpty())
+            {
+                TempData[SelectDataKey] = new SelectCampModel
+                {
+                    PageUrl = "/ExerciseV2/Student",
+                    PageTitle = "学员屏选择培训营",
+                    HasGroup = true
+                };
+                return RedirectToAction("SelectCamp");
+            }
+            var group = await GroupRepository.GetAllIncluding(a => a.CampInfo).FirstOrDefaultAsync(a => a.Id == groupNo);
+            if (group == null)
+            {
+                CheckErrors($"未查到到编号为【{groupNo}】的培训营分组!");
+                return Content("");
+            }
+            Session["GroupNo"] = groupNo;
+            Session.Timeout = TimeOut;
+            if (group.CampGroupState != CampGroupStateDefinition.Run)
+            {
+                string text;
+                switch (group.CampGroupState)
+                {
+                    case CampGroupStateDefinition.Report:
+                        text = "正在生成演练报告";
+                        break;
+
+                    case CampGroupStateDefinition.End:
+                        text = "演练报告已生成";
+                        break;
+
+                    default:
+                        return RedirectToAction("HeadquarterStu");
+                }
+
+                ViewBag.IsLeader = false;
+                ViewBag.Group = ObjectMapper.Map<CampGroupDto>(group);
+                ViewBag.Text = text;
+                return View("WaitRport");
+            }
+
+            ViewBag.Group = ObjectMapper.Map<CampGroupDto>(group);
+
+            return View();
+        }
+
+        /// <summary>
+        /// 指挥长页面
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<ActionResult> Leader(string id)
+        {
+            string groupNo = id.IsEmpty() ? (string)Session["GroupNo"] : id;
+            if (groupNo.IsEmpty())
+            {
+                TempData[SelectDataKey] = new SelectCampModel
+                {
+                    PageUrl = "/ExerciseV2/Leader",
+                    PageTitle = "指挥长屏选择培训营",
+                    HasGroup = true
+                };
+                return RedirectToAction("SelectCamp");
+            }
+            var group = await GroupRepository.GetAllIncluding(a => a.CampInfo).FirstOrDefaultAsync(a => a.Id == groupNo);
+            if (group == null)
+            {
+                CheckErrors($"未查到到编号为【{groupNo}】的培训营分组!");
+                return Content("");
+            }
+
+            Session["GroupNo"] = groupNo;
+            Session.Timeout = TimeOut;
+            if (group.CampGroupState != CampGroupStateDefinition.Run)
+            {
+                string text;
+                switch (group.CampGroupState)
+                {
+                    case CampGroupStateDefinition.Report:
+                        text = "正在生成演练报告";
+                        break;
+
+                    case CampGroupStateDefinition.End:
+                        text = "演练报告已生成";
+                        break;
+
+                    default:
+                        return RedirectToAction("HeadquarterLeader");
+                }
+
+                ViewBag.IsLeader = true;
+                ViewBag.Group = ObjectMapper.Map<CampGroupDto>(group);
+                ViewBag.Text = text;
+                return View("WaitRport");
+            }
+
+            //if (group.CampGroupState != CampGroupStateDefinition.Run)
+            //{
+            //    return RedirectToAction("HeadquarterLeader");
+            //}
+
+            ViewBag.Group = ObjectMapper.Map<CampGroupDto>(group);
+
+            return View();
+        }
+
+        /// <summary>
+        /// 公共屏页面
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<ActionResult> Public(string id)
+        {
+            string groupNo = id.IsEmpty() ? (string)Session["GroupNo"] : id;
+            if (groupNo.IsEmpty())
+            {
+                TempData[SelectDataKey] = new SelectCampModel
+                {
+                    PageUrl = "/ExerciseV2/Public",
+                    PageTitle = "公共屏选择培训营",
+                    HasGroup = true
+                };
+                return RedirectToAction("SelectCamp");
+            }
+            var group = await GroupRepository.GetAllIncluding(a => a.CampInfo).FirstOrDefaultAsync(a => a.Id == groupNo);
+            if (group == null)
+            {
+                CheckErrors($"未查到到编号为【{groupNo}】的培训营分组!");
+                return Content("");
+            }
+
+            Session["GroupNo"] = groupNo;
+            Session.Timeout = TimeOut;
+            ViewBag.Group = ObjectMapper.Map<CampGroupDto>(group);
+            if (group.CampGroupState != CampGroupStateDefinition.Run)
+            {
+                string text = "";
+                switch (group.CampGroupState)
+                {
+                    case CampGroupStateDefinition.New:
+                        text = "";
+                        break;
+
+                    case CampGroupStateDefinition.Ready:
+                        text = "培训营已就绪";
+                        break;
+
+                    case CampGroupStateDefinition.HeadQuarterBuilding:
+                        text = "正在组建指挥部";
+                        break;
+
+                    case CampGroupStateDefinition.HeadQuarterBuilded:
+                        text = "等待演练开始";
+                        break;
+
+                    case CampGroupStateDefinition.Report:
+                        text = "正在生成演练报告";
+                        break;
+
+                    case CampGroupStateDefinition.End:
+                        return RedirectToAction("StuReport", new { id = groupNo });
+                }
+
+                ViewBag.State = group.CampGroupState;
+                ViewBag.Text = text;
+                return View("PublicWait");
+            }
+
+            return View();
+        }
+
+        /// <summary>
+        /// 公共屏页面
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [AccessOriginal]
+        public async Task<ActionResult> Display(string id)
+        {
+            var group = await GroupRepository.GetAllIncluding(a => a.CampInfo).FirstOrDefaultAsync(a => a.Id == id);
+            if (group == null)
+            {
+                CheckErrors($"未查到到编号为【{id}】的培训营分组!");
+                return Content("");
+            }
+
+            ViewBag.Group = ObjectMapper.Map<CampGroupDto>(group);
+            if (group.CampGroupState != CampGroupStateDefinition.Run)
+            {
+                string text = "";
+                switch (group.CampGroupState)
+                {
+                    case CampGroupStateDefinition.New:
+                        text = "";
+                        break;
+
+                    case CampGroupStateDefinition.Ready:
+                        text = "培训营已就绪";
+                        break;
+
+                    case CampGroupStateDefinition.HeadQuarterBuilding:
+                        text = "正在组建指挥部";
+                        break;
+
+                    case CampGroupStateDefinition.HeadQuarterBuilded:
+                        text = "等待演练开始";
+                        break;
+
+                    case CampGroupStateDefinition.Report:
+                        text = "正在生成演练报告";
+                        break;
+
+                    case CampGroupStateDefinition.End:
+                        return RedirectToAction("StuReport", new { id });
+                }
+
+                ViewBag.State = group.CampGroupState;
+                ViewBag.Text = text;
+                return View("PublicWait");
+            }
+
+            return View("_Run/Display");
+        }
+
+        /// <summary>
+        /// 专家屏页面
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<ActionResult> Specialist(string id)
+        {
+            CampInfo camp;
+            if (id == "1")
+            {
+                camp = await CampRepository.FirstOrDefaultAsync(a => a.CampState == CampStateDefinition.Run);
+                if (camp == null)
+                {
+                    CheckErrors("未查询到正在运行的培训营!");
+                    return Content("Error");
+                }
+            }
+            else
+            {
+                string campNo = id.IsEmpty() ? (string)Session["CampNo"] : id;
+                if (campNo.IsEmpty())
+                {
+                    TempData[SelectDataKey] = new SelectCampModel
+                    {
+                        PageUrl = "/ExerciseV2/Specialist",
+                        PageTitle = "专家屏选择培训营",
+                        HasGroup = false
+                    };
+                    return RedirectToAction("SelectCamp");
+                }
+                camp = await CampRepository.FirstOrDefaultAsync(a => a.Id == campNo);
+                if (camp == null)
+                {
+                    CheckErrors($"未查到到编号为【{campNo}】的培训营!");
+                    return null;
+                }
+
+                Session["CampNo"] = campNo;
+                Session.Timeout = TimeOut;
+            }
+
+            ViewBag.Camp = ObjectMapper.Map<CampDto>(camp);
+            ViewBag.Groups = await QueryApp.GetCampGroups(id);
+            if (camp.CampState != CampStateDefinition.Run)
+            {
+                var text = camp.CampState != CampStateDefinition.End ? "等待演练开始" : "演练已结束";
+                ViewBag.Text = text;
+                return View("SpecWait");
+            }
+            return View();
+        }
+
+
+        /// <summary>
+        /// 主屏页面
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<ActionResult> Play(string id)
+        {
+            CampInfo camp;
+            if (id == "1")
+            {
+                camp = await CampRepository.FirstOrDefaultAsync(a => a.CampState == CampStateDefinition.Run);
+                if (camp == null)
+                {
+                    CheckErrors("未查询到正在运行的培训营!");
+                    return Content("Error");
+                }
+            }
+            else
+            {
+                string campNo = id.IsEmpty() ? (string)Session["CampNo"] : id;
+                if (campNo.IsEmpty())
+                {
+                    TempData[ExerciseController.SelectDataKey] = new SelectCampModel
+                    {
+                        PageUrl = "/Play/Index",
+                        PageTitle = "演示屏选择培训营",
+                        HasGroup = false
+                    };
+                    return RedirectToAction("SelectCamp", "ExerciseV2");
+                }
+                camp = await CampRepository.FirstOrDefaultAsync(a => a.Id == campNo);
+                if (camp == null)
+                {
+                    CheckErrors($"未查到到编号为【{campNo}】的培训营!");
+                    return null;
+                }
+
+                Session["CampNo"] = campNo;
+                Session.Timeout = TimeOut;
+            }
+
+            ViewBag.Camp = ObjectMapper.Map<CampDto>(camp);
+            if (camp.CampState != CampStateDefinition.Run)
+            {
+                return View("_Play/Wait");
+            }
+            ViewBag.Groups = await QueryApp.GetCampGroups(id);
+            return View("_Play/Index");
+        }
+
+        /// <summary>
+        /// 主屏页面
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<ActionResult> PlayWithScore(string id)
+        {
+            CampInfo camp;
+            if (id == "1")
+            {
+                camp = await CampRepository.FirstOrDefaultAsync(a => a.CampState == CampStateDefinition.Run);
+                if (camp == null)
+                {
+                    CheckErrors("未查询到正在运行的培训营!");
+                    return Content("Error");
+                }
+            }
+            else
+            {
+                string campNo = id.IsEmpty() ? (string)Session["CampNo"] : id;
+                if (campNo.IsEmpty())
+                {
+                    TempData[ExerciseController.SelectDataKey] = new SelectCampModel
+                    {
+                        PageUrl = "/Play/IndexWithScore",
+                        PageTitle = "演示屏选择培训营",
+                        HasGroup = false
+                    };
+                    return RedirectToAction("SelectCamp", "ExerciseV2" );
+                }
+                camp = await CampRepository.FirstOrDefaultAsync(a => a.Id == campNo);
+                if (camp == null)
+                {
+                    CheckErrors($"未查到到编号为【{campNo}】的培训营!");
+                    return null;
+                }
+
+                Session["CampNo"] = campNo;
+                Session.Timeout = TimeOut;
+            }
+
+            ViewBag.Camp = ObjectMapper.Map<CampDto>(camp);
+            if (camp.CampState != CampStateDefinition.Run)
+            {
+                return View("_Play/Wait");
+            }
+            ViewBag.Groups = await QueryApp.GetCampGroups(id);
+            return View("_Play/IndexWithScore");
+        }
+
+        /// <summary>
+        /// 演练报告
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<ActionResult> StuReport(string id)
+        {
+            string groupNo = id.IsEmpty() ? (string)Session["GroupNo"] : id;
+            if (groupNo.IsEmpty())
+            {
+                TempData[SelectDataKey] = new SelectCampModel
+                {
+                    PageUrl = "/ExerciseV2/StuReport",
+                    PageTitle = "演练报告选择培训营",
+                    HasGroup = true
+                };
+                return RedirectToAction("SelectCamp");
+            }
+            var group = await PortraitApp.GetGroupReportBase(id);
+            if (group == null)
+            {
+                CheckErrors($"未查到到编号为【{groupNo}】的培训营分组!");
+                return Content("");
+            }
+            return View(group);
+        }
+
+        /// <summary>
+        /// 手动记录演练页面
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<ActionResult> Manual(string id)
+        {
+            string campNo = id.IsEmpty() ? (string)Session["CampNo"] : id;
+            if (campNo.IsEmpty())
+            {
+                TempData[SelectDataKey] = new SelectCampModel
+                {
+                    PageUrl = "/ExerciseV2/Manual",
+                    PageTitle = "培训营演练情况手动记录",
+                    HasGroup = false
+                };
+                return RedirectToAction("SelectCamp");
+            }
+            var camp = await CampRepository.FirstOrDefaultAsync(a => a.Id == campNo);
+            if (camp == null)
+            {
+                CheckErrors($"未查到到编号为【{campNo}】的培训营!");
+            }
+            if (camp?.CampState == CampStateDefinition.End)
+            {
+                CheckErrors($"培训营已结束!");
+            }
+            //if (camp?.CampState!=CampStateDefinition.Run)
+            //{
+            //    CheckErrors($"培训营未开始!");
+            //}
+            Session["CampNo"] = campNo;
+            Session.Timeout = TimeOut;
+            ViewBag.CampName = camp?.Name;
+            ViewBag.Groups = await QueryApp.GetCampGroups(id);
+            return View();
+        }
+
+        /// <summary>
+        /// GIS页面
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<ActionResult> Gis(string id)
+        {
+            string campNo = id.IsEmpty() ? (string)Session["CampNo"] : id;
+            if (campNo.IsEmpty())
+            {
+                TempData[SelectDataKey] = new SelectCampModel
+                {
+                    PageUrl = "/ExerciseV2/Gis",
+                    PageTitle = "培训营GIS推演实时路径",
+                    HasGroup = false
+                };
+                return RedirectToAction("SelectCamp");
+            }
+            var camp = await CampRepository.FirstOrDefaultAsync(a => a.Id == campNo);
+            if (camp == null)
+            {
+                CheckErrors($"未查到到编号为【{campNo}】的培训营!");
+            }
+            if (camp?.CampState == CampStateDefinition.End)
+            {
+                CheckErrors($"培训营已结束!");
+            }
+            //if (camp?.CampState!=CampStateDefinition.Run)
+            //{
+            //    CheckErrors($"培训营未开始!");
+            //}
+            Session["CampNo"] = campNo;
+            Session.Timeout = TimeOut;
+            ViewBag.CampNo = camp?.Id;
+            ViewBag.CampName = camp?.Name;
+            //ViewBag.Groups = await QueryApp.GetCampGroups(id);
+            return View();
+        }
+
+        [AbpMvcAuthorize]
+        public async Task<ActionResult> ExportSql(string id)
+        {
+            string campNo = id.IsEmpty() ? (string)Session["CampNo"] : id;
+            if (campNo.IsEmpty())
+            {
+                TempData[SelectDataKey] = new SelectCampModel
+                {
+                    PageUrl = "/ExerciseV2/ExportSql",
+                    PageTitle = "导出SQL选择培训营",
+                    HasGroup = false
+                };
+                return RedirectToAction("SelectCampAll");
+            }
+
+            Session["CampNo"] = campNo;
+            Session.Timeout = TimeOut;
+            var path = await _exportManger.ExportSql(id);
+            return Content($"{AppDomain.CurrentDomain.BaseDirectory}{path}");
+        }
+
+        ///// <summary>
+        ///// 选择培训营页面
+        ///// </summary>
+        ///// <param name="url"></param>
+        ///// <param name="title"></param>
+        ///// <param name="hasGroup"></param>
+        ///// <returns></returns>
+        //public async Task<ActionResult> Select(string url,string title,bool hasGroup)
+        //{
+        //    string str = await QueryApp.GetCampSelectStr(CampStateDefinition.Audit, CampStateDefinition.Run);
+        //    ViewBag.Select = new SelectCampModel()
+        //    {
+        //        HasGroup = hasGroup,
+        //        PageTitle = title,
+        //        PageUrl = url,
+        //        CampSelect = str
+        //    };
+        //    return View("_Select");
+        //}
+
+        /// <summary>
+        /// 选择培训营页面
+        /// </summary>
+        /// <returns></returns>
+        public async Task<ActionResult> SelectCamp()
+        {
+            SelectCampModel model = (SelectCampModel)TempData[SelectDataKey];
+            if (model == null)
+            {
+                return RedirectToAction("Student");
+            }
+            string str = await QueryApp.GetCampSelectStr(CampStateDefinition.Audit, CampStateDefinition.Run);
+            model.CampSelect = str;
+            ViewBag.Select = model;
+            return View("_Select");
+        }
+
+        /// <summary>
+        /// 选择培训营页面
+        /// </summary>
+        /// <returns></returns>
+        public async Task<ActionResult> SelectCampAll()
+        {
+            SelectCampModel model = (SelectCampModel)TempData[SelectDataKey];
+            if (model == null)
+            {
+                return RedirectToAction("Student");
+            }
+            string str = await QueryApp.GetCampSelectStr(CampStateDefinition.Audit, CampStateDefinition.Run, CampStateDefinition.End);
+            model.CampSelect = str;
+            ViewBag.Select = model;
+            return View("_Select");
+        }
+
+        [HttpGet]
+        [WrapResult(false)]
+        public ActionResult Voice(string txt, int? rate = null, int? vol = null)
+        {
+            using (MemoryStream ms = new MemoryStream())
+            {
+                var t = new Thread(() =>
+                {
+                    using (SpeechSynthesizer sp = new SpeechSynthesizer())
+                    {
+                        try
+                        {
+                            sp.Rate = rate ?? 0;
+                            sp.Volume = vol ?? 90;
+                            sp.SetOutputToWaveStream(ms);
+                            sp.Speak(txt);
+                        }
+                        catch (Exception ex)
+                        {
+                            this.LogError(ex);
+                            throw;
+                        }
+                    }
+                });
+                t.Start();
+                t.Join();
+                ms.Position = 0;
+                var buBytes = ms.GetBuffer();
+                return new FileStreamResult(new MemoryStream(buBytes), "application/wav");
+            }
+        }
+
+        [HttpGet]
+        [WrapResult(false)]
+        public async Task<ActionResult> QueryScore(string id)
+        {
+            if (id.IsEmpty())
+            {
+                var camp = await CampRepository.FirstOrDefaultAsync(a => a.CampState == CampStateDefinition.Run);
+                if (camp == null)
+                {
+                    CheckErrors("未查询到正在运行的培训营!");
+                    return Content("Error");
+                }
+
+                id = camp.Id;
+            }
+
+            var score = await QueryApp.GetScoreInfo(id);
+
+            return Json(score, JsonRequestBehavior.AllowGet);
+        }
+    }
+}

+ 7 - 6
SourceCode/WeApp.Web/Controllers/PlayController.cs

@@ -32,7 +32,7 @@ namespace WeApp.Controllers
         }
 
         /// <summary>
-        /// 专家屏页面
+        /// 屏页面
         /// </summary>
         /// <param name="id"></param>
         /// <returns></returns>
@@ -79,12 +79,13 @@ namespace WeApp.Controllers
             }
             ViewBag.Groups = await QueryApp.GetCampGroups(id);
             return View();
-        }   /// <summary>
+        }
 
-            /// 专家屏页面
-            /// </summary>
-            /// <param name="id"></param>
-            /// <returns></returns>
+        /// <summary>
+        /// 主屏页面
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
         public async Task<ActionResult> IndexWithScore(string id)
         {
             CampInfo camp;

+ 18 - 0
SourceCode/WeApp.Web/Properties/PublishProfiles/WeApp.pubxml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+https://go.microsoft.com/fwlink/?LinkID=208121.
+-->
+<Project>
+  <PropertyGroup>
+    <DeleteExistingFiles>false</DeleteExistingFiles>
+    <ExcludeApp_Data>false</ExcludeApp_Data>
+    <LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
+    <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
+    <LastUsedPlatform>Any CPU</LastUsedPlatform>
+    <PublishProvider>FileSystem</PublishProvider>
+    <PublishUrl>D:\01Work\1005_WePlatform\We_Relese\App\Relese</PublishUrl>
+    <WebPublishMethod>FileSystem</WebPublishMethod>
+    <_TargetId>Folder</_TargetId>
+    <SiteUrlToLaunchAfterPublish />
+  </PropertyGroup>
+</Project>

+ 1 - 1
SourceCode/WeApp.Web/Resources/Guids.iwbx

@@ -1,5 +1,5 @@
 <Guids>
-  <Guid type="DataLib" step="1" last-id="1012" last-date="2022/12/7 13:51:03">
+  <Guid type="DataLib" step="1" last-id="20101" last-date="2024/6/14 14:56:27">
   </Guid>
   <Guid type="UserNo" step="1" last-id="10001" last-date="1900/1/1 0:00:00">
   </Guid>

+ 66 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/Display.cshtml

@@ -0,0 +1,66 @@
+@using WeApp.TrainingCampGroup.Dto
+@using WeApp.Views.Exercise
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Stu.cshtml";
+    CampGroupDto group = ViewBag.Group;
+    ViewBag.Title = "公共屏(" + group.Name + ")";
+    string id = group.Id,
+             campNo = group.CampNo;
+    var model = new ScreenModel(1, id, campNo, true);
+}
+
+@*@section css{
+        <link href="~/Content/Css/Exercise/public.min.css" rel="stylesheet" />
+    }*@
+
+<div class="main-box" style="height: 100vh; width: 100vw; box-sizing: padding-box; position: relative">
+    <div style="width: 100%; height: 100%;position: relative">
+        @Html.Partial("_Screen", model)
+    </div>
+</div>
+@section scripts
+{
+    <script>
+        @*$(function() {
+            window.GetScenes('@(id)');
+            window.GetLogs('@(id)');
+            window.GetRoles('@(id)');
+            window.GetScore('@(id)');
+            window.OverlayScrollbar($('.box-body'));
+        });
+        abp.signalr.connect(['@(id)']);*@
+        $(function() {
+
+        });
+        iwbHub.client.getReloadAll = function (msg) {
+            console.log('getReloadAll: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)'|| data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadAll: ', e);
+                }
+            }
+        };
+        iwbHub.client.getReloadPublic = function(msg) {
+            console.log('getReloadPublic: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)' || data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadPublic: ', e);
+                }
+            }
+        };
+    </script>
+}

+ 106 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/Enter.cshtml

@@ -0,0 +1,106 @@
+@using WeApp.TrainingCampGroup.Dto
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Stu.cshtml";
+    CampGroupDto group = ViewBag.Group;
+    bool isLeader = ViewBag.IsLeader;
+    string id = group.Id,
+        campNo = group.CampNo,
+        campName=group.CampName;
+
+    var name = (isLeader ? "指挥长" : "组员");
+    ViewBag.Title = name + "屏(" + group.Name + ")";
+}
+@section styles{
+    <link href="~/Content/Css/ExerciseV2/stu-bg.min.css" rel="stylesheet" />
+}
+@Html.Partial("Layout/V2/_Bg",true)
+<div class="box no-select" style="">
+    <div id="bg_title">
+        <span class="title">@(campName)</span>
+    </div>
+    <div class="body">
+        <div class="icon">
+            <img class="ico" src="/Content/Image/ExerciseV2/@(isLeader? "wait_leader":"wait_stu").svg" />
+        </div>
+        <div class="name">@(name)</div>
+        <div class="group">@(group.Name)</div>
+    </div>
+</div>
+@section scripts
+{
+    <script>
+        abp.signalr.connect(['@(id)','@(campNo)']);
+
+        var state = 0;
+        function Go(isLeader) {
+            if (state) {
+                window.location.href = isLeader ? "/Exercise/HeadquarterLeader" : "/Exercise/HeadquarterStu";
+            } else {
+                abp.message.warn("演练还未开始,请稍后再试!");
+            }
+
+        }
+
+    </script>
+
+    <script id="hub">
+        abp.signalr.connect(['@(id)','@(campNo)']);
+        iwbHub.client.getReloadAll = function (msg) {
+            console.log('getReloadAll: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)'|| data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadAll: ', e);
+                }
+            }
+        };
+        @if (isLeader)
+        {
+            <text>
+                iwbHub.client.getReloadLeader = function (msg) {
+                    console.log('getReloadLeader: ', msg);
+                    if (msg) {
+                        try {
+                            var data = JSON.parse(msg);
+                            if (data) {
+                                if (data.no == '@(id)'|| data.no == '@(campNo)') {
+                                    window.location.reload();
+                                }
+                            }
+                        } catch (e) {
+                            console.log('getReloadLeader: ', e);
+                        }
+                    }
+                };
+            </text>
+        }
+        else
+        {
+            <text>
+        iwbHub.client.getReloadStu = function (msg) {
+            console.log('getReloadStu: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)'|| data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadStu: ', e);
+                }
+            }
+        };
+            </text>
+        }
+    </script>
+}
+
+

+ 208 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/Gis.cshtml

@@ -0,0 +1,208 @@
+@using WeApp.TrainingCampGroup.Dto
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Spec.cshtml";
+    string campName = ViewBag.CampName;
+    ViewBag.Title = "GIS(" + campName + ")";
+   
+}
+<style>
+    body {
+        background: #f5f5f5;
+    }
+
+    .slide {
+        color: rgb(81, 106, 248)
+    }
+
+    .tab-content-box {
+        width: 100vw;
+        max-width: 1300px;
+        margin: 0 auto;
+        height: 100vh;
+        overflow: auto;
+        background: #000;
+        position: relative;
+    }
+
+    #carousel {
+        position: static;
+        width: 100%;
+        cursor: pointer;
+    }
+
+
+    .tab-content-box .carousel-control {
+        width: 80px;
+        background: #4c4c4c;
+    }
+
+
+    .tab-content-box  .carousel-indicators {
+        left: 80px;
+        /*text-align: left;*/
+    }
+
+
+    .tab-content-box .carousel-indicators li {
+        display: none;
+        margin-left: 5px;
+    }
+    .tab-box, .tab-box ul, .tab-box li {
+        float: left;
+    }
+
+    .tab-box {
+        margin-top: 20px;
+        margin-left: 5px;
+        width: 150px;
+        position: absolute;
+    }
+
+    .tab-box ul, .tab-box li {
+        width: 100%;
+        line-height: 37px;
+    }
+
+    .nav-tabs {
+        border-bottom: none;
+    }
+
+    .nav-tabs > li > a {
+        font-size: 15px;
+        color: #678CEB;
+        border-radius: 0 35px 35px 0;
+        border: 1px solid #678CEB;
+        padding: 8px 15px;
+    }
+
+    .nav > li > a:hover, .nav > li > a:active, .nav > li > a:focus {
+        color: #eee;
+        background: #678CEB;
+        border-color: #678CEB;
+    }
+
+    .nav-tabs > li > a.active, .nav-tabs > li > a.active:focus, .nav-tabs > li > a.active:hover {
+        cursor: default;
+        color: #eee;
+        border: 1px solid rgb(81, 106, 248);
+        background: rgb(81, 106, 248);
+        cursor: none;
+    }
+    .path-box {
+        height: 100vh;
+        background: transparent;
+        margin: 0 auto;
+
+    }
+    .path-box .carousel .carousel-inner {
+        height: auto;
+        max-width: 550px;
+    }
+    .tab-content-box .carousel-item, .tab-content-box .item {
+        max-width: 550px;
+        margin: 0 auto;
+    }
+    .tab-box-border {
+        position: absolute;
+        width: 100%;
+        max-width: 570px;
+        height: 100%;
+        border: 2px dashed #f00;
+        border-top: none;
+        border-bottom: none;
+        top: 0;
+        left: calc(50% - 285px);
+        margin: 0 auto;
+        cursor: none;
+        z-index: 1;
+    }
+    .gis {
+        height: 100vh;
+    }
+</style>
+<div class="" style="min-width: 100vw; min-height: 100vh;">
+    <div class="tab-box" style="z-index: 1000">
+        <ul id="info-tab" class="nav nav-tabs" role="tablist" style="float: left">
+            <li role="presentation" style="float: left"><a href="#t0" role="tab" class="active" data-toggle="tab">Start</a></li>
+            <li role="presentation" class="" style="float: left"><a href="#t1" role="tab" data-toggle="tab">Running Path</a></li>
+            <li role="presentation" class="" style="float: left"><a href="#t2" role="tab" data-toggle="tab">Gis</a></li>
+
+        </ul>
+
+    </div>
+    <div id="info-content" class="tab-content">
+        <div class="tab-box-border"></div>
+
+        <div id="t0" role="tabpanel" class="tab-pane active">
+            <div class="tab-content-box">
+                <div class="title">
+
+                </div>
+            </div>
+        </div>
+
+        <div id="t1" role="tabpanel" class="tab-pane" style="position: relative">
+            <div class="tab-content-box">
+                <div id="group-path-box"></div>
+            </div>
+        </div>
+
+        <div id="t2" role="tabpanel" class="tab-pane">
+            <div class="tab-content-box" style="position: relative">
+                <div class="gis item" id="container"></div>
+            </div>
+
+        </div>
+
+
+    </div>
+
+
+</div>
+
+@section scripts{
+    <script src="https://webapi.amap.com/maps?v=1.4.10&key=4a93c6fbce674fbb621275fa23804d93&plugin=AMap.PolyEditor"></script>
+    <script src="~/Content/Js/scene-path.js"></script>
+    <script>
+        $(function () {
+            ScenePath();
+            setInterval(ScenePath, 1000 * 30);
+        });
+    </script>
+    <script id="path">
+        function ScenePath() {
+            $.iwbAjax5({
+                url: abp.appUrl + 'Query/GetCampRunningInfos?no=@(ViewBag.CampNo)',
+                success: function(res) {
+                    var str = FormatterScenePath(res);
+                    $('#group-path-box').html(str);
+                }
+            });
+        }
+    </script>
+    <script id="gis">
+        var map = new AMap.Map('container',
+            {
+                resizeEnable: true,
+                zoom: 12,
+                center: [121.527209, 31.228674]
+            });
+        AMap.plugin(['AMap.ToolBar', 'AMap.Scale', 'AMap.OverView'],
+            function () {
+                map.addControl(new AMap.ToolBar());
+                map.addControl(new AMap.Scale());
+                map.addControl(new AMap.OverView({ isOpen: true }));
+
+            }
+        );
+    </script>
+    <script>
+        
+
+
+
+    </script>
+}
+
+
+

+ 360 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/Headquarter.cshtml

@@ -0,0 +1,360 @@
+@using WeApp.TrainingCampGroup.Dto
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Stu.cshtml";
+    CampGroupDto group = ViewBag.Group;
+    bool isLeader = ViewBag.IsLeader, hasBuilded = ViewBag.HasBuilded;
+    string id = group.Id,
+        groupName = group.Name,
+        campNo = group.CampNo;
+    ViewBag.Title = (isLeader ? "指挥长组建指挥部" : "学员屏指挥部") + "(" + groupName + ")";
+    var name = hasBuilded ? groupName + "指挥部——" + (isLeader ? "指挥长" : "组员") : isLeader ? "指挥长组建指挥部" : "指挥部组建中...";
+    var title = hasBuilded ? "指挥部" : "创建指挥部";
+    var stu = isLeader ? "" : "stu";
+}
+@section css{
+    <link href="~/Content/Css/ExerciseV2/stu-zhb.min.css?v=1.1" rel="stylesheet" />
+}
+@Html.Partial("Layout/V2/_Bg",false)
+<div class="box no-select">
+    <div id="bg_title">
+        <span class="title">@(title)</span>
+    </div>
+    <div class="box-body">
+        <div class="box-header">
+            <span class="title">@name</span>
+        </div>
+        <div class="body" style="margin-top: 5px">
+            @if (!hasBuilded)
+            {
+                <div class="body-card select">
+                    <div class="select-box">
+                    </div>
+                    @if (isLeader)
+                    {
+                        <div class="add-role" data-id="self" onclick="GroupToggle(this)">
+                            <div class="icon">
+                                <img src="/Content/Image/ExerciseV2/zhb/zh_icon-add-role.svg" alt="" />
+                            </div>
+                            <div class="text">自定义角色</div>
+                        </div>
+                    }
+                </div>
+            }
+            <div class="body-card role">
+                <div class="content-box">
+                    @if (!hasBuilded && isLeader)
+                    {
+                        <div class="role-content self" id="c_self">
+                            <div class="role-box">
+                                <div class="role-item add" id="add-role" onclick="$('#add-role').hide();$('#add-box').fadeIn(800).css('display', 'flex');$('#self-role-text').focus();">
+                                    <div class="icon">
+                                        <img src="~/Content/Image/ExerciseV2/zhb/zh_icon-add-role-2.svg" />
+                                    </div>
+                                    <div class="text">点击添加角色</div>
+                                </div>
+                            </div>
+                            <div class="add-box" id="add-box">
+                                <input type="text" id="self-role-text" value="" pattern="请输入新增角色名称" />
+                                <div class="btn-box">
+                                    <button type="button" class="btn btn-small cancel" onclick="$('#add-box').fadeOut(500);$('#add-role').fadeIn(800).css('display', 'flex');">取消</button>
+                                    <button type="button" class="btn btn-small submit" onclick="CreateSelfRole()">添加</button>
+                                </div>
+                            </div>
+                        </div>
+                    }
+                    else
+                    {
+                        <div class="role-content" style="display: none"></div>
+                    }
+                </div>
+            </div>
+            @if (!hasBuilded && isLeader)
+            {
+                <div class="body-card">
+                    <div class="btn" onclick="CreateGroupRole()">创建指挥部</div>
+                </div>
+
+                <div style="display: none">
+                    <img src="/Content/Image/ExerciseV2/zhb/zh_icon-checkbox.svg" style="opacity: 0" />
+                    <img src="/Content/Image/ExerciseV2/zhb/zh_icon-checkbox-selected.svg" style="opacity: 0" />
+                </div>
+            }
+        </div>
+    </div>
+    
+    <div style="position: absolute; left: 0; bottom: 0; color: #ddd; opacity: 0.5;">@(groupName)</div>
+</div>
+
+@section scripts
+{
+    <script>
+        $(function () {
+            resizeHeight_head();
+            window.addEventListener('resize', resizeHeight_head);
+            @if (hasBuilded)
+            {
+                <text>
+                    GetGroupRoles();
+                </text>
+            }
+            else
+            {
+                <text>
+                    GetCampRoleGroups();
+                </text>
+            }
+
+            $('#self-role-text').keydown(function(e) {
+                var theEvent = window.event || e;
+                var code = theEvent.keyCode || theEvent.which || theEvent.charCode;
+                if (code == 13) {
+                    CreateSelfRole();
+                }
+            });
+        });
+
+        function resizeHeight_head() {
+            resizeHeight((h, wh) => {
+                 var height = 355;
+                  @if (hasBuilded)
+                  {
+                      <text>
+                 height = 175;
+                     </text>
+
+                  }
+                  else if (!isLeader)
+                  {
+                      <text>
+                 height = 305;
+                     </text>
+                  }
+                 $(".content-box").height(wh - h - height)
+            })
+        }
+
+        var selectStr = '<div class="check-box-icon"></div>已选择<span>(点击取消)</span>';
+        var noSelectStr = '<div class="check-box-icon"></div>未选择<span>(点击选择)</span>';
+        var roleInfo = '<div class="role-item {1}"  data-name="{0}"><div class="icon"><img src="/Content/Image/ExerciseV2/zhb/zh_icon-user.svg" alt="" /></div><div class="text">{0}</div>{2}</div> ';
+
+        function GetGroupRoles() {
+            $.iwbAjax4({
+                url: abp.appUrl + "Query/GetGroupRoleInfos?no=@(id)",
+                success: function(res) {
+                    FormatterBuildGroupRole(res);
+                    OverlayScrollbar($('.content-box'));
+
+                }
+            });
+        }
+
+        function GetCampRoleGroups() {
+            $.iwbAjax4({
+                url: abp.appUrl + "Query/GetCampRoleGroupInfos?no=@(campNo)",
+                success: function(res) {
+                    FormatterGroupRole(res);
+                    OverlayScrollbar($('.content-box'));
+
+                }
+            });
+        }
+
+        function FormatterBuildGroupRole(data) {
+            if (data && data.length > 0) {
+                var str = '<div class="role-content active"><div class="role-box">';
+                data.forEach(function(r) {
+                    str += roleInfo.format(r, "", "");
+                });
+                str += '</div></div>';
+                $('.content-box').html(str);
+            }
+        }
+
+        function FormatterGroupRole(data) {
+            var str1 = "", str2 = "";
+            if (data && data.length > 0) {
+                data.forEach(function(v) {
+                    str1 += FormatterGroup(v);
+                    str2 += FormatterRole(v);
+                });
+            }
+            if (str1) {
+                $('.role .content-box .role-content').before(str2);
+                $('.select-box').html(str1);
+                $('.select-box .select-item:first-child').trigger("click");
+            }
+        }
+
+        function FormatterGroup(data) {
+            var str = '';
+            if (data) {
+                str += '<div class="select-item" id="t_{0}" data-id="{0}" onclick="GroupToggle(this)">'.format(data.id);
+                str += '<div class="check-box-icon @(stu)" data-id="{0}" onclick="GroupCheckToggle(this)"></div>'.format(data.id);
+                str += '<div class="icon"><img data-id="{0}" src="/Content/Image/ExerciseV2/zhb/zh_icon-{0}.svg"/></div >'
+                    .format(data.id);
+                str += '<span class="text">{0}</span>'.format(data.roleGroupName);
+                str += '</div>';
+            }
+            return str;
+        }
+
+        function FormatterRole(data) {
+            var str = '';
+            if (data) {
+                str += '<div class="role-content" id="c_{0}">'.format(data.id);
+                str += '<div class="role-select @(stu)"  data-id="{1}" onclick="GroupCheckToggle(this)">{0}</div>'.format(noSelectStr,
+                    data.id);
+                str += '<div class="role-box">';
+                if (data.roleNames) {
+                    var arr = data.roleNames.split(',');
+                    arr.forEach(function(v) {
+                        str += roleInfo.format(v, "", "");
+                    });
+                }
+                str += '</div>';
+                str += '</div>';
+            }
+            return str;
+        }
+
+        function GroupToggle(that) {
+            var e = window.event;
+            e.stopPropagation();
+            e.preventDefault();
+            var $that = $(that);
+            var id = $that.data('id');
+            $('.role-content').removeClass('active');
+            $('#c_' + id).addClass('active');
+            $('.select-box .select-item').removeClass('active');
+            $that.addClass('active');
+        }
+
+        function GroupCheckToggle(that) {
+            var e = window.event;
+            e.stopPropagation();
+            e.preventDefault();
+            var $that = $(that);
+            var id = $that.data('id');
+            if ($('#t_' + id).hasClass('check')) {
+                $('#t_' + id).removeClass('check');
+                $('#c_' + id).find('.role-select').removeClass('check').html(noSelectStr);
+                $('#c_' + id).find('.role-item').removeClass('check');
+            } else {
+                $('#t_' + id).addClass('check');
+                $('#c_' + id).find('.role-select').addClass('check').html(selectStr);
+                $('#c_' + id).find('.role-item').addClass('check');
+            }
+
+        }
+
+        function CreateSelfRole() {
+            var name = $('#self-role-text').val();
+            if (name) {
+                var str = roleInfo.format(name,
+                    " check",
+                    '<span class="close" onclick="$(this).parent().remove()">&times;</span>');
+                $('#add-role').before(str);
+                $('#self-role-text').val('');
+            }
+            $('#add-box').hide();
+            $('#add-role').fadeIn(800).css('display', 'flex');
+        }
+
+        function CreateGroupRole() {
+            var groupNo = "@(id)";
+            abp.message.confirm("您确认提交吗?提交后不可再更改。",
+                "创建指挥部",
+                function (isConfirmed) {
+                    if (isConfirmed) {
+                        var roleGroupNos = [], selfRoleNames = [];
+                        $(".select-item.check").each(function (i, v) {
+                            var no = $(v).data("id");
+                            if (no != 'self') {
+                                roleGroupNos.push(no);
+                            }
+                        });
+                        $("#c_self .check").each(function (i, v) {
+                            selfRoleNames.push($(v).data("name"));
+                        });
+                        console.log(roleGroupNos, selfRoleNames);
+                        if (roleGroupNos.length <= 0 && selfRoleNames.length <= 0) {
+                            abp.message.warn("您还未选择角色。");
+                            return;
+                        }
+                        $.iwbAjax1({
+                            url: abp.appUrl + "/Eval/CreateGroupRole",
+                            isAlert: false,
+                            isValidate: false,
+                            data: { GroupNo: groupNo, RoleGroupNos: roleGroupNos, SelfRoleNames: selfRoleNames },
+                            success: function () {
+                                //window.iwbHub.server.sendPageState(JSON.stringify({ groupNo: groupNo, cmd: "reload" }));
+                                //window.iwbHub.server.sendPageState(JSON.stringify({ groupNo: groupNo, cmd: "reload-public" }));
+                                //window.iwbHub.server.sendRefreshRole(groupNo);
+                                window.location.reload();
+                                abp.message.success("角色预案创建成功");
+                            }
+                        });
+                    }
+                });
+        }
+    </script>
+
+    <script id="hub">
+        abp.signalr.connect(['@(id)', '@(campNo)']);
+        iwbHub.client.getReloadAll = function(msg) {
+            console.log('getReloadAll: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)' || data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadAll: ', e);
+                }
+            }
+        };
+        @if (isLeader)
+        {
+            <text>
+                iwbHub.client.getReloadLeader = function(msg) {
+                    console.log('getReloadLeader: ', msg);
+                    if (msg) {
+                        try {
+                            var data = JSON.parse(msg);
+                            if (data) {
+                                if (data.no == '@(id)' || data.no == '@(campNo)') {
+                                    window.location.reload();
+                                }
+                            }
+                        } catch (e) {
+                            console.log('getReloadLeader: ', e);
+                        }
+                    }
+                };
+            </text>
+        }
+        else
+        {
+            <text>
+        iwbHub.client.getReloadStu = function(msg) {
+            console.log('getReloadStu: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)' || data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadStu: ', e);
+                }
+            }
+        };
+            </text>
+        }
+    </script>
+}

+ 50 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/Index.cshtml

@@ -0,0 +1,50 @@
+@{
+    ViewBag.Title = "页面选择";
+    Layout = "~/Views/Shared/Layout/_Layout.Stu.cshtml";
+}
+@section styles{
+
+    <link href="~/Content/Css/ExerciseV2/stu-bg.min.css" rel="stylesheet" />
+    <style>
+        .box {
+            width: 100vw;
+            height: 100vh;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+        }
+        #bg_title {
+            position: absolute;
+            top: 0;
+        }
+        .btn-box {
+            width: 400px;
+            display: flex;
+            flex-direction: column;
+        }
+        .btn-box .btn {
+            margin: 8px 0;
+            font-size: 16px!important;
+            padding: 10px;
+
+        }
+
+    </style>
+
+
+}
+@Html.Partial("Layout/V2/_Bg", true)
+
+<div class="box">
+    <div id="bg_title">
+        <span class="title">页面中心</span>
+    </div>
+    <div class="btn-box">
+        <a class="btn" href="@Url.Action("Select", "ExerciseV2", new { id = 4 })">专家屏</a>
+        <a class="btn" href="@Url.Action("Select", "ExerciseV2", new { id = 3 })">公共屏</a>
+        <a class="btn" href="@Url.Action("Select", "ExerciseV2", new { id = 1 })">组员</a>
+        <a class="btn" href="@Url.Action("Select", "ExerciseV2", new { id = 2 })">指挥长</a>
+        <a class="btn" href="@Url.Action("Play", "ExerciseV2")">主屏</a>
+        <a class="btn" href="@Url.Action("Select", "ExerciseV2", new { id = 5 })">手动记录演练情况</a>
+    </div>
+</div>

+ 342 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/Leader.cshtml

@@ -0,0 +1,342 @@
+@using WeApp.Configuration
+@using WeApp.TrainingCampGroup.Dto
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Stu.cshtml";
+    CampGroupDto group = ViewBag.Group;
+    ViewBag.Title = "指挥长屏(" + group.Name + ")";
+    string id = group.Id,
+        groupName = group.Name,
+        campNo = group.CampNo;
+}
+@section css{
+    <link href="~/Content/Css/ExerciseV2/stu-cmd_leader.min.css" rel="stylesheet" />
+}
+@Html.Partial("Layout/V2/_Bg", false)
+<div class="box  no-select" style="">
+    <div id="bg_title">
+        <span class="title">决策指令下达</span>
+    </div>
+    <div class="box-body ">
+        <div class="box-header">
+            <span class="title">@(groupName)指挥部 —— 指挥长</span>
+        </div>
+        <div class="body">
+            <div class="body-card" id="cmd-box">
+                <div id="log-box" class="logs-box">
+                    <div class='empty'>暂无指令,等待组员发送指令</div>
+                </div>
+            </div>
+            <div class="body-card">
+                <div class="btn" onclick="SubmitNextScene()">进入下一阶段场景</div>
+            </div>
+        </div>
+    </div>
+    <div style="position: absolute; left: 0; bottom: 0; color: #ddd; opacity: 0.5;">@(groupName)</div>
+</div>
+<div class="question" title="点击处理来电" onclick="ShowQuestion()">
+    <button type="button" class="btn-danger" style="--w: 80px;">
+        <i class="fa fa-phone-volume"></i>
+    </button>
+</div>
+<div class="modal fade" id="modal" tabindex="-1">
+    <div class="modal-dialog modal-lg">
+        <div class="modal-content">
+            <div class="modal-header" style="cursor: move;">
+                <h4 class="modal-title">
+                    <span>处置电话提问</span>
+                </h4>
+                <button type="button" class="close" data-dismiss="modal"></button>
+            </div>
+            <div class="modal-body">
+                <form class="form-horizontal " id="form">
+                    <input id="id" name="id" type="hidden" value="">
+                    <input id="campNo" name="campNo" type="hidden" value="">
+                    <input id="groupNo" name="groupNo" type="hidden" value="">
+                    <div class="question-text">
+                    </div>
+                    <div class="form-group row">
+                        <div class="" style="width: 100%">
+                            <div class="input-group input-group-sm ">
+                                <textarea rows="5" class="form-control required" id="handleContent" name="handleContent" placeholder="请输入处理内容..." value="" style="" aria-required="true"></textarea>
+                            </div>
+                        </div>
+                    </div>
+                </form>
+            </div>
+            <div class="modal-footer" style="text-align: center;">
+                <button type="button" class="btn btn-sm btn-outline-iwb waves-effect" data-dismiss="modal" style="min-width:100px;">取消</button>
+                <button type="button" class="btn btn-iwb btn-sm save-btn waves-effect" style="min-width:100px;">保存</button>
+            </div>
+        </div>
+    </div>
+</div>
+<audio id="voiceAudio" style="display: none">
+    <source type="audio/wav" />
+</audio>
+@section scripts
+{
+    <script>
+        $(function () {
+            resizeHeight_Leader();
+            window.addEventListener('resize', resizeHeight_Leader);
+            GetLog();
+            GetQuestionLogs();
+
+            OverlayScrollbar($('#cmd-box'));
+            $('#handleContent').keydown(function(e) {
+                var theEvent = window.event || e;
+                var code = theEvent.keyCode || theEvent.which || theEvent.charCode;
+                if (code == 13) {
+                    $('#modal .save-btn').click();
+                }
+            });
+        });
+
+        function resizeHeight_Leader() {
+            resizeHeight((h, wh) => {
+                var height = 225;
+                $(".logs-box").height(wh - h - height)
+            })
+        }
+
+        function RoleHandle(that) {
+            MsgConfirm("您确认下达这条指令吗?",
+                "下达指令",
+                function() {
+                    var $that = $(that).closest('.log-box');
+                    var id = $that.data('id');
+                    $.iwbAjax4({
+                        url: abp.appUrl + "Eval/OperationScene?id=" + id,
+                        success: function() {
+                            $that.addClass('send');
+                            $that.find('.btn-box').html('<div class="btn btn-small">已下达</div>');
+
+                        }
+                    });
+                });
+        }
+
+        function SubmitNextScene() {
+            var log = $("#log-box .log-box.send");
+            if (!log || log.length <= 0) {
+                abp.message.warn("请至少下达一条指令后再操作!");
+                return;
+            }
+            MsgConfirm("您确认进入到下一情景吗?",
+                "情景流转",
+                function() {
+
+                    $.iwbAjax4({
+                        url: abp.appUrl + "Eval/NextFlowNode?groupNo=@(id)&campNo=none",
+                        success: function(res) {
+                            $("#log-box").html("<div class='empty'>暂无指令,等待组员发送指令</div>");
+                        }
+                    });
+                });
+        }
+
+        function GetLog() {
+            $.iwbAjax5({
+                url: abp.appUrl + "Query/GetCurrentSceneLog?no=@(id)",
+                success: function(res) {
+                    if (res) {
+                        $("#box-body").empty();
+                        var str = '';
+                        for (var i = 0; i < res.length; i++) {
+                            var item = res[i];
+                            str += FormatLog(item);
+                        }
+                        if (str) {
+                            $("#log-box").html(str);
+                        } else {
+                            $("#log-box").html("<div class='empty'>暂无指令,等待组员发送指令</div>");
+
+                        }
+                    }
+                }
+            });
+        }
+
+        function FormatLog(data) {
+            var str = "";
+            if (data) {
+                var h = 50 + (Math.floor(data.word.length / 60)+1) * 21,
+                    msg = data.word ? data.word : "",
+                    state = data.logState == '@(LogStateDefinition.New)' ? '' : 'send',
+                    btn = data.logState == '@(LogStateDefinition.New)' ? '<div class="btn btn-small" onclick="RoleHandle(this)">下达指令</div>' : '<div class="btn btn-small">已下达</div>';
+                console.log(h)
+                str = '<div class="log-box {0}" data-id="{1}" style="min-height:{5}px"><div class="content"><div class="title">{2}</div><div class="text">{3}</div></div><div class="btn-box">{4}</div></div>'.format(state, data.id, data.role, msg, btn,h);
+            }
+            return str;
+        }
+
+        function GetQuestionLogs() {
+            $.iwbAjax4({
+                url: abp.appUrl + "query/GetQuestionLogs?no=@(id)",
+                success: function(res) {
+                    if (res && res.length) {
+                        res.forEach(v => {
+                            if (_phoneLogIds.indexOf(v) < 0) {
+                                _phoneLogIds.push(v);
+                            }
+                        });
+                        PhoneQuestion();
+                    }
+                }
+            });
+        }
+
+        var _phoneLogIds = [], _currentPhoneLogId, _currentPhoneQuestionText, _voiceText, isPhoning = false;
+
+        function PhoneQuestion() {
+            if (isPhoning) {
+                return;
+            }
+            _currentPhoneLogId = _phoneLogIds.shift();
+            if (_currentPhoneLogId) {
+                isPhoning = true;
+                $('.question').fadeIn();
+            }
+        }
+
+        function ShowQuestion() {
+            if (_currentPhoneLogId) {
+                if (!_currentPhoneQuestionText) {
+                    $.iwbAjax4({
+                        url: abp.appUrl + "query/GetQuestionByLog?no=" + _currentPhoneLogId,
+                        success: function(res) {
+                            _voiceText = _currentPhoneQuestionText = res.content;
+                            $('#modal .question-text').html(_currentPhoneQuestionText);
+                            Show();
+                        }
+                    });
+                } else {
+                    Show();
+                }
+            }
+
+            function Show() {
+                if (_voiceText) {
+                    //voice(_voiceText);
+                    playVoice(_voiceText);
+                    _voiceText = undefined;
+                }
+                OpenModal({
+                    url: abp.appUrl + 'eval/OperationPhone',
+                    data: { id: _currentPhoneLogId, campNo: '@(campNo)', groupNo: '@(group.Id)' },
+                    success: function() {
+                        $('.question').fadeOut();
+                        _currentPhoneLogId = undefined;
+                        _currentPhoneQuestionText = undefined;
+                        isPhoning = false;
+                        if (_phoneLogIds.length) {
+                            setTimeout(PhoneQuestion, 1500);
+                        }
+                    }
+                });
+            }
+        }
+
+        function voice(text) {
+            if (!text) {
+                return;
+            }
+            try {
+                // 语音播报
+                var utterThis = new window.SpeechSynthesisUtterance();
+                utterThis.text = text; //播放内容按
+                window.speechSynthesis.speak(utterThis);
+            } catch (e) {
+                console.error("语音播报出错:", e);
+            }
+
+        }
+
+        function playVoice(callText) {
+            //$.iwbAjax5({
+            //    url: abp.appUrl+`Query/voice?txt=${callText}&rate=0`,
+            //    success: function(res) {
+            //        var audioPlay = document.getElementById("voiceAudio");
+            //        audioPlay.src = res;
+            //        audioPlay.play();
+            //    }
+            //});
+            if (!callText) {
+                return;
+            }
+            var audioPlay = document.getElementById("voiceAudio");
+            audioPlay.src = "/Exercise/voice?txt=" + callText;
+            audioPlay.play();
+        }
+    </script>
+
+    <script>
+        abp.signalr.connect(['@(id)', '@(campNo)']);
+        iwbHub.client.getReloadAll = function(msg) {
+            console.log('getReloadAll: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)' || data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadAll: ', e);
+                }
+            }
+        };
+        iwbHub.client.getReloadLeader = function(msg) {
+            console.log('getReloadLeader: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)' || data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadLeader: ', e);
+                }
+            }
+        };
+        iwbHub.client.getOperationLog = function(msg) {
+            console.log('getOperationLog: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)') {
+                            $('#log-box .empty').remove();
+                            var str = FormatLog(data);
+                            $('#log-box').prepend(str);
+                        }
+                    }
+                } catch (e) {
+                    console.log('getOperationLog: ', e);
+                }
+            }
+        };
+        iwbHub.client.getPhoneQuestion = function(msg) {
+            console.log('getPhoneQuestion: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (!data) {
+                        return;
+                    }
+                    if (data.no == '@(id)') {
+                        if (_phoneLogIds.indexOf(v) < 0) {
+                            _phoneLogIds.push(data.id);
+                        }
+                        PhoneQuestion();
+                    };
+                } catch (e) {
+                    console.log('getPhoneQuestion: ', e);
+                }
+            }
+        };
+    </script>
+}

+ 68 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/Manual.cshtml

@@ -0,0 +1,68 @@
+@using WeApp.TrainingCampGroup.Dto
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Spec.cshtml";
+    string campName = ViewBag.CampName;
+    ViewBag.Title = "手动记录演练情况(" + campName + ")";
+    List<CampGroupDto> groups = ViewBag.Groups;
+    string groupSelects = "<option value=\"\">请选择培训营分组...</option>";
+    if (groups != null && groups.Any())
+    {
+        foreach (var g in groups)
+        {
+            groupSelects += "<option value=\"" + g.Id + "\">" + g.Name + "</option>";
+        }
+    }
+}
+
+<link href="~/Content/Css/ExerciseV2/stu-cmd.css" rel="stylesheet" />
+@Html.Partial("Layout/V2/_Bg", true)
+<div class="box">
+    <div id="bg_title">
+        <span class="title">演练情况记录</span>
+    </div>
+    <div class="form-box" style="height: calc(100vh - 126px); display: flex; justify-content: center; align-items: center; ">
+        <form class="form" style="width: 500px; margin-top: -10%; ">
+            <div class="form-group">
+                <select id="group" class="form-control" style="width: 100%">
+                    @Html.Raw(groupSelects)
+                </select>
+            </div>
+            <div class="form-group">
+                <textarea id="txt" class="form-control" style="width: 100%; height: 200px;" placeholder="请输入内容..."></textarea>
+            </div>
+            <div id="btn" class="btn" style="padding: 10px 20px;width: 100%"  onclick="Go()">记录演练实时情况</div>
+        </form>
+    </div>
+</div>
+@section scripts
+{
+    <script src="~/Content/Js/scene-path.js"></script>
+    <script>
+        $('.form #group').select2();
+        function Go() {
+            var no = $("#group").val();
+            if (!no) {
+                abp.message.warn("请选择一个培训营分组!");
+                return;
+            }
+            var txt = $("#txt").val();
+            if (!txt) {
+                abp.message.warn("请输入内容!");
+                return;
+            }
+            $("#btn").prop("disable", true);
+            $.iwbAjax1({
+                url: abp.appUrl + "Eval/ManualLog",
+                data: { no: no, msg: txt },
+                success: function () {
+                    $("#txt").val("");
+                }
+            });
+            setTimeout(function () { $("#btn").prop("disable", false); }, 1000 * 10);
+        }
+
+    
+    </script>
+}
+
+

+ 71 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/Public.cshtml

@@ -0,0 +1,71 @@
+@using WeApp.TrainingCampGroup.Dto
+@using WeApp.Views.ExerciseV2
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Stu.cshtml";
+    CampGroupDto group = ViewBag.Group;
+    ViewBag.Title = "公共屏(" + group.Name + ")";
+    string id = group.Id,
+        groupName = group.Name,
+        campNo = group.CampNo;
+    var model = new ScreenModel(1, id, campNo, true);
+}
+
+@Html.Partial("Camp/_Effect", id + "," + campNo)
+@Html.Partial("Layout/V2/_Bg",false)
+
+<div class="main-box box" style="height: 100vh; width: 100vw;padding: 0;margin: 0; position: relative">
+    <div id="bg_title">
+        <span class="title">@(groupName)</span>
+    </div>
+
+    <div class="main-box-body">
+        @Html.Partial("_Screen", model)
+    </div>
+
+    @*<div style="width: calc(100% - 100px); height: calc(100% - 180px); position: relative;">
+    </div>*@
+</div>
+@section scripts
+{
+    <script>
+        $(function() {
+            resizeHeight_Screen();
+            window.addEventListener('resize', resizeHeight_Screen);
+        });
+        function resizeHeight_Screen() {
+            resizeHeight((h, wh) => {
+                $(".main-box .main-box-body").height(wh - h - 30).css("marginTop", "-20px");
+            })
+        }
+        iwbHub.client.getReloadAll = function (msg) {
+            console.log('getReloadAll: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)'|| data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadAll: ', e);
+                }
+            }
+        };
+        iwbHub.client.getReloadPublic = function(msg) {
+            console.log('getReloadPublic: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)' || data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadPublic: ', e);
+                }
+            }
+        };
+    </script>
+}

+ 75 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/PublicWait.cshtml

@@ -0,0 +1,75 @@
+@using WeApp.TrainingCampGroup.Dto
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Stu.cshtml";
+    CampGroupDto group = ViewBag.Group;
+    string id = group.Id,
+        campNo = group.CampNo,
+        groupName = group.Name,
+        text = ViewBag.Text;
+    ViewBag.Title = "公共屏(" + group.Name + ")";
+}
+
+
+@section styles{
+    <link href="~/Content/Css/ExerciseV2/stu-bg.min.css" rel="stylesheet" />
+}
+@Html.Partial("Layout/V2/_Bg",true)
+<div class="box public no-select" style="">
+    <div id="bg_title">
+        <span class="title">@(groupName)</span>
+    </div>
+    <div class="body ">
+        <div class="icon">
+            @if (group.CampGroupState == 102)
+            {
+                <img class="ico" src="/Content/Image/ExerciseV2/wait_zhb.svg" />
+            }
+            else
+            {
+                <img class="ico" src="/Content/Image/ExerciseV2/wait_public.svg" />
+            }
+            @* <img class="img" src="/Content/Image/ExerciseV2/wait_timer.svg" /> *@
+        </div>
+        <div class="name">@(text)</div>
+    </div>
+</div>
+
+@section scripts
+{
+    <script id="hub">
+ 
+     
+        abp.signalr.connect(['@(id)','@(campNo)']);
+        iwbHub.client.getReloadAll = function (msg) {
+            console.log('getReloadAll: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)'|| data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadAll: ', e);
+                }
+            }
+        };
+        iwbHub.client.getReloadPublic = function (msg) {
+            console.log('getReloadPublic: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)'|| data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadPublic: ', e);
+                }
+            }
+        };
+    
+    </script>
+}

+ 22 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/ScreenModel.cs

@@ -0,0 +1,22 @@
+namespace WeApp.Views.ExerciseV2
+{
+    public class ScreenModel
+    {
+        public ScreenModel()
+        {
+        }
+
+        public ScreenModel(int type,string groupNo, string campNo, bool withScript)
+        {
+            Type = type;
+            GroupNo = groupNo;
+            CampNo = campNo;
+            WithScript = withScript;
+        }
+
+        public int Type { get; set; }
+        public string GroupNo { get; set; }
+        public string CampNo { get; set; }
+        public bool WithScript { get; set; }
+    }
+}

+ 61 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/SpecWait.cshtml

@@ -0,0 +1,61 @@
+@using WeApp.TrainingCamp.Dto
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Spec.cshtml";
+    CampDto camp = ViewBag.Camp;
+    string text = ViewBag.Text;
+    ViewBag.Title = "专家屏(" + camp.Name + ")";
+}
+
+@section styles{
+    <link href="~/Content/Css/ExerciseV2/stu-bg.min.css" rel="stylesheet" />
+}
+@Html.Partial("Layout/V2/_Bg", true)
+<div class="box public no-select" style="">
+    <div id="bg_title">
+        <span class="title">@(camp.Name)</span>
+    </div>
+    <div class="body ">
+        <div class="icon">
+            <img class="ico" src="/Content/Image/ExerciseV2/wait_public.svg" />
+            @* <img class="img" src="/Content/Image/ExerciseV2/wait_timer.svg" /> *@
+        </div>
+        <div class="name">@(text)</div>
+    </div>
+</div>
+
+@section scripts
+{
+    <script id="hub">
+        abp.signalr.connect(['@(camp.Id)']);
+        iwbHub.client.getReloadAll = function (msg) {
+            console.log('getReloadAll: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(camp.Id)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadAll: ', e);
+                }
+            }
+        };
+        iwbHub.client.getReloadSpec = function(msg) {
+            console.log('getReloadSpec: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(camp.Id)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadSpec: ', e);
+                }
+            }
+        };
+    </script>
+}

+ 361 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/Specialist.cshtml

@@ -0,0 +1,361 @@
+@using WeApp.TrainingCamp.Dto
+@using WeApp.TrainingCampGroup.Dto
+@using WeApp.Views.ExerciseV2
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Spec.cshtml";
+    CampDto camp = ViewBag.Camp;
+    ViewBag.Title = "专家屏(" + camp.Name + ")";
+    List<CampGroupDto> groups = ViewBag.Groups;
+    string id = camp.Id;
+    string groupSelects = "<option value=\"\">请选择培训营分组...</option>";
+    string groupNos = "['" + id + "'";
+    if (groups != null && groups.Any())
+    {
+        foreach (var g in groups)
+        {
+            groupSelects += "<option value=\"" + g.Id + "\">" + g.Name + "</option>";
+            groupNos += ",'" + g.Id + "'";
+        }
+    }
+
+    groupNos += "]";
+}
+<style>
+   
+    body {
+        height: 100vh;
+    }
+    .main-box {
+        margin: 0 !important;
+    }
+    .btn-box {
+        position: absolute;
+        top: 15px;
+        left: 50px;
+    }
+    .btn-box .vb-btn {
+        padding: 8px 20px;
+        font-size: 16px;
+    }
+    .carousel {
+        display: flex;
+        flex-direction: column;
+    }
+
+    .carousel-indicators {
+        margin: 0;
+        z-index: 10;
+        position: relative;
+        top: auto;
+        bottom: auto;
+        left: auto;
+        right: auto;
+        align-items: flex-end;
+    }
+
+        .carousel-indicators li {
+            color: #fff;
+            height: 100%;
+            width: auto;
+            text-indent: 0;
+            background: none;
+            border-radius: 0;
+            margin: 0;
+            padding: 0;
+            border: none;
+            position: relative;
+            display: none;
+            cursor: default;
+        }
+
+        .carousel-indicators li.active {
+            display: flex;
+            font-size: 100%;
+            display: flex;
+            margin: 0;
+            padding: 0;
+            border: none;
+        }
+
+            .carousel-indicators li img {
+                cursor: pointer;
+                position: absolute;
+                width: 40px;
+                right: -50px;
+                top: 50%;
+                transform: translateY(-50%);
+            }
+
+    .groups {
+        position: absolute;
+        left: 50%;
+        transform: translateX(-50%);
+        top: 100%;
+        width: 300px;
+        padding: 5px 0;
+        display: none;
+        flex-direction: column;
+        background: #fff;
+        border: 1px solid rgba(255, 255, 255, 0.2);
+        border-radius: 8px;
+        z-index: 10;
+    }
+        .groups .group {
+            font-size: 16px;
+            padding: 10px;
+            color: #36536D;
+            font-weight: 400;
+            cursor: pointer;
+        }
+            .groups .group.active {
+                background: rgba(9, 75, 136, 0.1);
+            }
+            .groups .group:hover {
+                font-weight: 600;
+            }
+            .carousel-inner {
+                width: 100%;
+            }
+
+    .carousel, .carousel-inner, .carousel-item {
+        position: unset;
+        padding: 0 10px;
+    }
+
+ 
+</style>
+@Html.Partial("Layout/V2/_Bg", false)
+<div class="main-box box">
+    <div class="btn-box">
+        <span onclick="Carousel(this)" class="vb-btn active" style="margin-right: 15px;">停止轮播</span>
+        <span onclick="ScenePath()" class="vb-btn" style="margin-right: 15px;">情景路径</span>
+        <span onclick="Reviews()" class="vb-btn" style="margin-right: 15px;">我要点评</span>
+    </div>
+    @if (groups != null)
+    {
+        <div id="Carousel" class="carousel slide" data-ride="carousel" data-pause="false" data-interval="30000" style="width: 100%; height: 100%;">
+            <div id="bg_title">
+                <ol class="carousel-indicators">
+                    @{
+                        int i = 0;
+                        string active;
+                        foreach (var group in groups)
+                        {
+                            active = i == 0 ? "active" : "";
+                            <li data-target="#Carousel" data-slide-to="@(i)" class="@(active)" data-id="@(group.Id)">
+                                @(group.Name)
+                                <img src="/Content/Image/ExerciseV2/toggle.svg" alt=""  onclick="$('.groups .group').removeClass('active'); $('.groups').css('display','flex').find('#ci_group_@(group.Id)').addClass('active')"/>
+                            </li>
+                            i++;
+                        }
+                    }
+                    <div class="groups">
+                        @{
+                            i = 0;
+                            foreach (var group in groups)
+                            {
+                                active = i == 0 ? "active" : "";
+                                <div id="ci_group_@(group.Id)" data-target="#Carousel" data-slide-to="@(i)" class="group @(active)" data-id="@(group.Id)" onclick="$('.groups').css('display', 'none')">
+                                    @(group.Name)
+                                </div>
+                                i++;
+                            }
+                        }
+                    </div>
+                </ol>
+           
+            </div>
+            <div class="carousel-inner">
+                @{
+                    i = 0;
+                    foreach (var g in groups)
+                    {
+                        active = i == 0 ? "active" : "";
+                        <div class="carousel-item @(active)" style="height: 100%; width: 100%;" data-id="@(g.Id)">
+                            @{
+                                var model = new ScreenModel(2, g.Id, id, i == 0);
+                                 <div style="display: flex; width: 100%; height: 100%;"> 
+                                     @Html.Partial("_Screen", model) 
+                                 </div> 
+                            }
+                        </div>
+                        i++;
+                    }
+                }
+            </div>
+        </div>
+    }
+</div>
+@section scripts
+{
+    <script src="~/Content/Js/scene-path.js"></script>
+    <script>
+
+        $(function () {
+            resizeHeight_Screen();
+            window.addEventListener('resize', resizeHeight_Screen);
+
+            @if (groups != null && groups.Any())
+            {
+                <text>
+                    $("#Carousel").on("slide.bs.carousel",
+                        function(e) {
+                            //console.log(e)
+                            var no = $(e.relatedTarget).data('id');
+                            console.log(no, $("#ci_group_" + no));
+                            //window.Scroll2Bottom($('#scene_' + no + ' .box-body'));
+                            $(".media-box .body-content").each(function() {
+                                $(this).html('<span>播放区</span>');
+                            });
+                            $('#scene_' + no + ' .box-body .body-content').find('.flash-scene')
+                                .removeClass('flash-scene');
+                            $('#scene_' + no + ' .box-body .body-content').find('.current-scene')
+                                .removeClass('current-scene');
+                            
+                        });
+                </text>
+            }
+        });
+      
+        function resizeHeight_Screen() {
+            resizeHeight((h, wh) => {
+                $(".main-box .carousel-inner").height(wh - h - 30).css("marginTop", "-20px");
+            })
+        }
+        function Carousel(that) {
+            var $that = $(that);
+            if ($that.hasClass("active")) {
+                console.log('pause');
+                $("#Carousel").carousel('pause');
+                $that.removeClass("active").text('开始轮播');
+            } else {
+                console.log('cycle');
+                $("#Carousel").carousel('cycle');
+                $("#Carousel").carousel('next');
+                $that.addClass("active").text('停止轮播');
+                
+            }
+        }
+
+        function ScenePath() {
+            $.iwbAjax4({
+                url: abp.appUrl + 'Query/GetCampRunningInfos?no=@(id)',
+                success: function(res) {
+                    if (res) {
+                        var str = FormatterScenePath(res);
+                        if (str) {
+                            str = '<div class="modal-body" style="height:800px;padding:0;">{0}</div>'.format(str);
+                            $(document).iwbModal('create',
+                                {
+                                    modal: 'modal-scene-path',
+                                    modaltitle: '情景路径详情',
+                                    modalFooter: '<div class="modal-footer" style="text-align: center;"><button type="button" class="btn btn-sm btn-outline-iwb waves-effect" data-dismiss="modal" style="min-width:100px;">取消</button></div>',
+                                    width: 1000,
+                                    modalBody: str,
+                                    topHeight:10,
+                                    save: function() {
+                                        $('#modal-scene-path').modal('hide');
+                                    }
+                                });
+                        } else {
+                            abp.message.warn("未查询到情景信息!");
+                        }
+                    } else {
+                        abp.message.warn("未查询到情景信息!");
+                    }
+
+                }
+            });
+        }
+
+        function Reviews() {
+            var str = '<div class="modal-body" style="padding:20;"><div class="form-group"><select id="group" class="form-control" style="width: 100%">@Html.Raw(groupSelects)</select></div><div class="form-group"><textarea id="txt" class="form-control" style="width: 100%; height: 150px;border: 1px solid #aaa;" placeholder="请输入点评内容..."></textarea></div></div>';
+
+            $(document).iwbModal('create',
+                {
+                    modal: 'modal-reviews',
+                    modaltitle: '专家点评',
+                    modalSize: 'modal-lg',
+                    modalBody: str,
+                    data: null,
+                    shownAfter: function () {
+                        var $modal = $("#modal-reviews");
+                        $modal.css('display', 'block');
+                        var topHeight = $(window).height() - $modal.find('.modal-dialog').height() - 150;
+                        if (topHeight < 50) {
+                            topHeight = 50;
+                        }
+                        $modal.find('.modal-dialog').animate({ 'marginTop': topHeight / 2 + "px" });
+                    },
+                    save: function () {
+                        var no = $("#modal-reviews #group").val();
+                        if (!no) {
+                            abp.message.warn("请选择一个培训营分组!");
+                            return;
+                        }
+                        var txt = $("#modal-reviews #txt").val();
+                        if (!txt) {
+                            abp.message.warn("请输入内容!");
+                            return;
+                        }
+                        $.iwbAjax1({
+                            url: abp.appUrl + "Eval/ZhuanJiaLog",
+                            data: { no: no, msg: txt },
+                            success: function () {
+                                $("#txt").val("");
+                            }
+                        });
+                        //$('#modal-reviews').modal('hide');
+                    }
+                });
+        }
+        iwbHub.client.getReloadAll = function (msg) {
+            console.log('getReloadAll: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadAll: ', e);
+                }
+            }
+        };
+
+        iwbHub.client.getReloadSpec = function(msg) {
+            console.log('getReloadSpec: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        //if (data.no == '@(id)'||@(groupNos).indexOf(data.no)>=0) {
+                        if (data.no == '@(id)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadSpec: ', e);
+                }
+            }
+        };
+
+        iwbHub.client.getGroupRole = function(msg) {
+            console.log('getGroupRole: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (@(Html.Raw(groupNos)).indexOf(data.no) >= 0) {
+                            window.GetRoles(data.no);
+                        }
+                    }
+                } catch (e) {
+                    console.log('getGroupRole: ', e);
+                }
+            }
+        };
+    </script>
+    }

+ 9 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/StuReport.cshtml

@@ -0,0 +1,9 @@
+@using WeApp.TrainingPortrait.Dto
+@using WeApp.Views.Shared.Camp
+@model GroupReportDto
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Stu.cshtml";
+    ViewBag.Title = $"{(Model.Name.IsEmpty() ? "" : $"[{Model.Name}] ")}演练报告";
+}
+
+@Html.Partial("Camp/_Report", new ReportModel(Model, false))

+ 175 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/Student.cshtml

@@ -0,0 +1,175 @@
+@using WeApp.TrainingCampGroup.Dto
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Stu.cshtml";
+    CampGroupDto group = ViewBag.Group;
+    ViewBag.Title = "学员屏(" + group.Name + ")";
+    string id = group.Id,
+        groupName = group.Name,
+        campNo = group.CampNo;
+}
+@section css{
+    <link href="~/Content/Css/ExerciseV2/stu-cmd.min.css" rel="stylesheet" />
+}
+@Html.Partial("Layout/V2/_Bg", false)
+<div class="box public no-select" style="">
+    <div class="box-title">决策指令下达</div>
+    <div class="box-body ">
+        <div class="box-header">
+            <span class="title">@(groupName)指挥部 —— 组员</span>
+        </div>
+        <div class="body">
+            <div class="form">
+                <div class="role-box">
+                    <div class="body-card select-box">
+                        <select class="form-control " id="role-select" name="role" style="width: 100%" autocomplete="off" placeholder="请选择角色" onchange="$(this).val()=='self'?$('.input-box').show():$('.input-box').hide()"></select>
+                    </div>
+                    <div class="body-card input-box ">
+                        <input class="form-control " id="role-input" name="self-role" style="width: 100%" autocomplete="off" placeholder="请输入自定义角色" />
+                    </div>
+                </div>
+                <div class="body-card cmd-box">
+                    <textarea class="form-control txt" rows="6" name="message" placeholder="请输入指令内容"></textarea>
+                    <button type="button" class="btn btn-submit" onclick="RoleHandle(this)">提交</button>
+                    <div class="help-box">
+                        <select class="form-control" name="help" style="width: 100%" autocomplete="off" data-placeholder="提示信息"></select>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div style="position: absolute; left: 0; bottom: 0; color: #ddd; opacity: 0.5;">@(groupName)</div>
+</div>
+
+@section scripts
+{
+    <script>
+        $(function () {
+            resizeHeight_Stu();
+            window.addEventListener('resize', resizeHeight_Stu);
+            $.iwbAjax4({
+                url: abp.appUrl + 'Query/GetGroupRoleSelectStr?no=@(id)',
+                success: function (res) {
+                    var str = "<option value='self'>自定义角色</option>"
+                    if (res) {
+                        res += str
+                        $('#role-select').html(res).select2({ minimumResultsForSearch: -1 });
+                    } else {
+                        $('#role-select').html(str).select2({ minimumResultsForSearch: -1 });
+                    }
+                }
+            });
+            $.iwbAjax4({
+                url: abp.appUrl + 'Query/GetStuHelpContentSelectStrByCamp?no=@(campNo)',
+                success: function(res) {
+                    if (res) {
+                        //$('.help-box').show();
+                        $('select[name="help"]').html(`<option value="">请选择提示信息</option>` + res).select2({ minimumResultsForSearch: -1 });
+                    } else {
+                        $('.help-box').hide();
+                    }
+                }
+            });
+
+            $('.txt').keydown(function (e) {
+                var theEvent = window.event || e;
+                var code = theEvent.keyCode || theEvent.which || theEvent.charCode;
+                if (code == 13) {
+                    RoleHandle(this);
+                }
+            });
+            $('select[name="help"]').on('change',
+                function () {
+                    if ($(this).val()) {
+                        var $txt = $(this).closest('.cmd-box').find('.txt');
+                        var text = $txt.val() + " " + $(this).find('option:selected').text()
+                        if (text.length<500) {
+                            $txt.val(text);
+                        } else {
+                            abp.message.warn('指令输入不超过500字符。');
+                        }
+                        $(this).val('');
+                    }
+                });
+        });
+
+        function resizeHeight_Stu() {
+            var fs = 38;
+            var h = $("#bg_header").height();
+            var newFs = fs * h / 126;
+            $(".box .box-title").height(h)
+                .css("fontSize", newFs + "px");
+            resizeHeight((h, wh) => {
+                var height = 215;
+                $(".txt").height(wh - h - height)
+            })
+        }
+
+        function RoleHandle(that) {
+            var $that = $(that).closest('.form'),
+                role = $that.find('.form-control[name="role"]').val(),
+                word = $that.find('textarea').val();
+            if (!role) {
+                abp.message.warn('请选择角色后再提交!');
+                return;
+            }
+            if (role == 'self') {
+                role = $that.find('.form-control[name="self-role"]').val()
+                if (!role) {
+                    abp.message.warn('请填写角色后再提交!');
+                    return;
+                }
+            }
+
+            if (!word) {
+                abp.message.warn('请输入指令后再提交!');
+                return;
+            }
+
+            if ( word.length >= 500) {
+                abp.message.warn('指令输入不超过500字符。');
+                return;
+            }
+
+            $.iwbAjax4({
+                url: abp.appUrl + 'Eval/SaveOperationScene',
+                data: { groupNo: '@(id)', behaviorRole: role, behaviorWord: word},
+                success: function() {
+                    $that.find('textarea').val('').focus();
+                }
+            });
+        }
+    </script>
+    <script id="hub">
+        abp.signalr.connect(['@(id)','@(campNo)']);
+        iwbHub.client.getReloadAll = function (msg) {
+            console.log('getReloadAll: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)'|| data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadAll: ', e);
+                }
+            }
+        };
+        iwbHub.client.getReloadStu = function (msg) {
+            console.log('getReloadStu: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)'|| data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadStu: ', e);
+                }
+            }
+        };
+    </script>
+}

+ 93 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/WaitRport.cshtml

@@ -0,0 +1,93 @@
+@using WeApp.TrainingCampGroup.Dto
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.Stu.cshtml";
+    CampGroupDto group = ViewBag.Group;
+    bool isLeader = ViewBag.IsLeader;
+    string id = group.Id,
+        campNo = group.CampNo,
+        text = ViewBag.Text;
+
+    var name = (isLeader ? "指挥长" : "组员");
+    ViewBag.Title = name + "屏(" + group.Name + ")";
+}
+@section styles{
+    <link href="~/Content/Css/ExerciseV2/stu-bg.min.css" rel="stylesheet" />
+}
+@Html.Partial("Layout/V2/_Bg", true)
+<div class="box public no-select" style="">
+    <div id="bg_title">
+        <span class="title">@(group.Name)</span>
+    </div>
+    <div class="body ">
+        <div class="icon">
+            <img class="ico" src="/Content/Image/ExerciseV2/wait_public.svg" />
+            @* <img class="img" src="/Content/Image/ExerciseV2/wait_timer.svg" /> *@
+        </div>
+        <div class="name">@(text)</div>
+    </div>
+</div>
+@section scripts
+{
+    <script>
+        abp.signalr.connect(['@(id)','@(campNo)']);
+    </script>
+
+    <script id="hub">
+        abp.signalr.connect(['@(id)','@(campNo)']);
+        iwbHub.client.getReloadAll = function (msg) {
+            console.log('getReloadAll: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)'|| data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadAll: ', e);
+                }
+            }
+        };
+        @if (isLeader)
+        {
+            <text>
+                iwbHub.client.getReloadLeader = function (msg) {
+                    console.log('getReloadLeader: ', msg);
+                    if (msg) {
+                        try {
+                            var data = JSON.parse(msg);
+                            if (data) {
+                                if (data.no == '@(id)'|| data.no == '@(campNo)') {
+                                    window.location.reload();
+                                }
+                            }
+                        } catch (e) {
+                            console.log('getReloadLeader: ', e);
+                        }
+                    }
+                };
+            </text>
+        }
+        else
+        {
+            <text>
+        iwbHub.client.getReloadStu = function (msg) {
+            console.log('getReloadStu: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(id)'|| data.no == '@(campNo)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadStu: ', e);
+                }
+            }
+        };
+            </text>
+        }
+    </script>
+}

File diff suppressed because it is too large
+ 318 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/Index.cshtml


File diff suppressed because it is too large
+ 318 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/IndexWithScore.cshtml


+ 136 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/Wait.cshtml

@@ -0,0 +1,136 @@
+@using WeApp.TrainingCamp.Dto
+@{
+    Layout = "~/Views/Shared/Layout/_Layout.None.cshtml";
+    ViewBag.Title = "演练大屏";
+    CampDto camp = ViewBag.Camp;
+}
+@section styles{
+    <link href="~/Content/Css/Play/v2.min.css" rel="stylesheet" />
+}
+@Html.Partial("Camp/_Effect", camp.Id)
+<div class="main-title">
+    @Html.Partial("_/_Top", camp)
+</div>
+<div class="main-area" style="flex-direction:column">
+    <div class="area-h" style="height: calc(var(--mh) * 0.685)">
+        <div class="area-v" style="width: calc(var(--mw) * 0.22);">
+            <div class="area-h card shadow card-box " style="height: calc(var(--mh) * 0.32)">
+                <div class="card-header">
+                    <div class="card-title">
+                        <i class="fa fa-file-invoice"></i>
+                        演练基本信息
+                    </div>
+                </div>
+                <div class="card-body p-0">
+                    @Html.Partial("__/_BaseInfo", camp)
+                </div>
+            </div>
+            <div class="area-h card shadow card-box " style="height: calc(var(--mh) * 0.68)">
+                <div class="card-header">
+                    <div class="card-title">
+                        <i class="fa fa-file-invoice"></i>
+                        知识信息
+                    </div>
+                </div>
+                <div class="card-body p-0">
+                    @Html.Partial("__/_Desc", camp)
+                </div>
+            </div>
+        </div>
+        <div class="area-v" style="width: calc(var(--mw) * 0.78 );">
+            <div class="card shadow card-box">
+                <div class="card-body p-0">
+                    @Html.Partial("__/_Video", camp)
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="area-h" style="height: calc(var(--mh) * 0.315)">
+        <div class="area-v">
+            <div class=" card shadow card-box">
+                <div class="card-header">
+                    <div class="card-title">
+                        <i class="fa fa-file-invoice"></i>
+                        操作信息
+                    </div>
+                </div>
+                <div class="card-body p-0">
+                    @Html.Partial("__/_Help", camp)
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+@section scripts{
+    <script>
+
+        $(function () {
+            $('#time-down').hide();
+            abp.signalr.connect(['@(camp.Id)']);
+
+            //window.PlayLoop("/Content/V2/Video/01.mp4");
+            $.iwbGet({
+                url: "/Content/V2/Config/Screen/@(camp.Id).json",
+                success: function (res) {
+                    console.log("===========>", res);
+                    if (res) {
+                        //暖场视频
+                        window.PlayLoop(res.WaitVideo);
+                        //操作文档图片
+                        window.LoadHelpImages(res.HelpImages);
+                        //课程描述图片(知识信息)
+                        window.LoadDescImage(res.DescImage);
+                    }
+                },
+                error: function () {
+                    $.iwbGet({
+                        url: "/Content/V2/Config/Screen/default.json",
+                        success: function (res) {
+                            console.log("=====DEFAULT======>", res);
+                            if (res) {
+                                //暖场视频
+                                window.PlayLoop(res.WaitVideo);
+                                //操作文档图片
+                                window.LoadHelpImages(res.HelpImages);
+                                //课程描述图片(知识信息)
+                                window.LoadDescImage(res.DescImage);
+                            }
+                        }
+                    });
+                }
+            });
+        });
+
+        function Scroll2Bottom(that) {
+            var instance = window.OverlayScrollbars($(that)[0]);
+            if (!instance) {
+                OverlayScrollbar($(that));
+                instance = window.OverlayScrollbars($(that)[0]);
+            }
+            if (instance) {
+                try {
+                    instance.scroll({ y: "100%" });
+                } catch (e) {
+
+                }
+            }
+        }
+
+        function Scroll2Top(that) {
+            var instance = window.OverlayScrollbars($(that)[0]);
+            if (!instance) {
+                //, { className: "os-theme-round-dark" }
+                OverlayScrollbar($(that));
+                instance = window.OverlayScrollbars($(that)[0]);
+            }
+            if (instance) {
+                try {
+                    instance.scroll({ y: "0%" });
+                } catch (e) {
+
+                }
+            }
+        }
+    </script>
+
+}

+ 1 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_.cshtml

@@ -0,0 +1 @@
+

+ 7 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Center_1.cshtml

@@ -0,0 +1,7 @@
+@model WeApp.TrainingCamp.Dto.CampDto
+
+<div class="area">
+    @Html.Partial("/Views/Play/__/_Chart_SG.cshtml")
+</div>
+
+

+ 3 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Center_2.cshtml

@@ -0,0 +1,3 @@
+<div class="area">
+    @Html.Partial("/Views/Play/__/_Chart_WL.cshtml")
+</div>

+ 8 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Center_3.cshtml

@@ -0,0 +1,8 @@
+@model WeApp.TrainingCamp.Dto.CampDto
+<div class="area-v" style="width: calc(var(--mw) * 0.5);">
+
+    @Html.Partial("/Views/Play/__/_Chart_RoundScore.cshtml")
+</div>
+<div class="area-v" style="width: calc(var(--mw) * 0.5);">
+    @Html.Partial("/Views/Play/__/_Chart_Cmd.cshtml")
+</div>

+ 5 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Left_1.cshtml

@@ -0,0 +1,5 @@
+@model WeApp.TrainingCamp.Dto.CampDto
+
+@Html.Partial("/Views/Play/__/_BaseInfo.cshtml", Model)
+<div class="">
+</div>

+ 22 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Left_2.cshtml

@@ -0,0 +1,22 @@
+@using WeApp.Helpers
+@model WeApp.TrainingCamp.Dto.CampDto
+
+
+<div class="area" >
+    @Html.Partial("/Views/Play/__/_EnvInfo.cshtml", Model)
+</div>
+
+@using (Html.BeginScripts())
+{
+    <script id="left2-script">
+        function s(type) {
+            if (type) {
+                $('#left2_2').fadeOut();
+                $('#left2_1').fadeIn();
+            } else {
+                $('#left2_1').fadeOut();
+                $('#left2_2').fadeIn();
+            }
+        }
+    </script>
+}

+ 4 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Left_3.cshtml

@@ -0,0 +1,4 @@
+@model WeApp.TrainingCamp.Dto.CampDto
+<div class="area">
+    @Html.Partial("/Views/Play/__/_Video.cshtml")
+</div>

+ 83 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Right_1.cshtml

@@ -0,0 +1,83 @@
+@using WeApp.Helpers
+@model List<WeApp.TrainingCampGroup.Dto.CampGroupDto>
+
+@if (Model?.Count > 0)
+{
+    <div class="scene-group-box" id="scene-group">
+
+        @for (int i = 0; i < Model.Count; i++)
+        {
+            var group = Model[i];
+            <span class="group-name @(i == Model?.Count - 1 ? "show" : "")">当前组别:@(group.Name)</span>
+        }
+
+        <div class="p-0" style="width: 100%; height: 100%; overflow: hidden;">
+            @for (int i = 0; i < Model.Count; i++)
+            {
+                var group = Model[i];
+                <div class="area-v group-box @(i == Model?.Count - 1 ? "show" : "")" data-index="@(i)">
+                    <div class="area-h p-0" style="height: calc(100% * 0.3)">
+                        <div class="area p-0">
+                            @Html.Partial("/Views/Play/__/_SceneInfo.cshtml", $"{group.Id}#{(i == 0 ? 1 : 0)}#2")
+                        </div>
+                    </div>
+                    <div class="area-h p-0" style="height: calc(100% * 0.24)">
+                        <div class="area p-0">
+                            @Html.Partial("/Views/Play/__/_SceneScore.cshtml", $"{group.Id}#{(i == 0 ? 1 : 0)}#2")
+                        </div>
+                    </div>
+                    <div class="area-h p-0" style="height: calc(100% * 0.48)">
+                        <div class="area p-0">
+                            @Html.Partial("/Views/Play/__/_SceneFlow.cshtml", $"{group.Id}#{(i == 0 ? 1 : 0)}#2")
+                        </div>
+                    </div>
+                </div>
+            }
+        </div>
+    </div>
+}
+
+@using (Html.BeginScripts())
+{
+<script>
+    var $sceneGroupsBox = $('#scene-group');
+    var groupCount = @(Model?.Count);
+
+    //function ScrollGroup() {
+
+    //    var $current = $sceneGroupsBox.find('.scene-group-box.show');
+    //    var nextIndex = Number($current.data('index')) + 1;
+    //    nextIndex = nextIndex >= groupCount ? 0 : nextIndex;
+    //    var $next = $sceneGroupsBox.find('.scene-group-box').eq(nextIndex);
+    //    $current.addClass("active");
+    //    $next.addClass("next");
+    //    setTimeout(() => {
+    //            $current.removeClass("active").removeClass("show");
+    //            $next.removeClass("next").addClass("show");
+    //        },
+    //        500);
+    //    setTimeout(ScrollGroup, 1000 * 3);
+
+    //}
+
+    function ScrollGroup() {
+        var $current = $sceneGroupsBox.find('.group-box.show'),$currentName = $sceneGroupsBox.find('.group-name.show');
+        var nextIndex = Number($current.data('index')) + 1;
+        nextIndex = nextIndex >= groupCount ? 0 : nextIndex;
+        var $next = $sceneGroupsBox.find('.group-box').eq(nextIndex), $nextName = $sceneGroupsBox.find('.group-name').eq(nextIndex);
+        $current.fadeOut(300, () => {
+            $current.removeClass("show");
+            $currentName.removeClass("show");
+            $next.fadeIn(800, () => { $next.addClass("show");window.Scroll2Bottom($next.find('.scroll-box')); });
+            $nextName.fadeIn(800, () => { $nextName.addClass("show") });
+        });
+        $currentName.fadeOut(300);
+        setTimeout(ScrollGroup, 1000 * 30);
+
+    }
+
+    $(function() {
+        ScrollGroup();
+    });
+</script>
+}

+ 4 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Right_2.cshtml

@@ -0,0 +1,4 @@
+@model WeApp.TrainingCamp.Dto.CampDto
+<div class="area py-0">
+    @Html.Partial("/Views/Play/__/_Log.cshtml", Model)
+</div>

+ 1 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Right_3.cshtml

@@ -0,0 +1 @@
+

+ 39 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/_/_Top.cshtml

@@ -0,0 +1,39 @@
+@using WeApp.Helpers
+@model WeApp.TrainingCamp.Dto.CampDto
+@Html.Partial("/Views/Play/__/_Title.cshtml", Model)
+
+@using (Html.BeginScripts())
+{
+    <script>
+        iwbHub.client.getReloadAll = function (msg) {
+            console.log('getReloadAll: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(Model.Id)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadAll: ', e);
+                }
+            }
+        };
+        iwbHub.client.getReloadPlay = function(msg) {
+            console.log('getReloadPlay: ', msg);
+            if (msg) {
+                try {
+                    var data = JSON.parse(msg);
+                    if (data) {
+                        if (data.no == '@(Model.Id)') {
+                            window.location.reload();
+                        }
+                    }
+                } catch (e) {
+                    console.log('getReloadPlay: ', e);
+                }
+            }
+        };
+    </script>
+}

+ 26 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/__/_AreaBox.cshtml

@@ -0,0 +1,26 @@
+@model string
+@{
+    var arr = Model.Split('#');
+    string name = arr[0], id = arr[1], body = "";
+    if (arr.Length > 2)
+    {
+        body = arr[2];
+    }
+}
+<div class="card card-vber" id="@id">
+    @*<div class="card-header">
+            <div class="card-title">
+                <span class="title-before"></span>
+                <span>@name</span>
+                <span class="title-after"></span>
+            </div>-->
+           <div class="card-tools">
+                    <button type="button" class="btn btn-tool" data-card-widget="maximize"><i class="fas fa-expand"></i></button>
+                </div
+         </div>>*@
+    <div class="card-body">
+        <div class="scroll-box">
+            <div class="box-body">@Html.Raw(body)</div>
+        </div>
+    </div>
+</div>

+ 64 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/__/_BaseInfo.cshtml

@@ -0,0 +1,64 @@
+@using WeApp.Helpers
+@model WeApp.TrainingCamp.Dto.CampDto
+@{
+    string id = "base-info";
+}
+@Html.Partial("__/_AreaBox", $"演练信息#{id}")
+<div style="display: none" id="@id-pre">
+    <div class="base-info">
+        @*<h4>演练基本信息</h4>*@
+        <dl><dt>名称:</dt><dd>@Model.Name</dd></dl>
+        <dl><dt>目标:</dt><dd>@Model.Description </dd></dl>
+        <dl><dt>演练阶段:</dt><dd id="step"></dd></dl>
+        <dl class="d-flex align-items-center" id="step-progress">
+            <dt class="progress " style="width: 85%; border-radius: 10px; background: #041f3c;">
+                <span class="progress-bar bg-success progress-bar-striped" style="width: 0%; border-radius: 10px;"></span>
+            </dt>
+            <dd>
+                <span>0</span>%
+            </dd>
+        </dl>
+    </div>
+</div>
+
+@using (Html.BeginScripts())
+{
+    <script id="base-info-script">
+        var $baseInfoBody = $('#@id .box-body');
+        $baseInfoBody.html($('#@id-pre').html());
+        $(function() {
+            GetStepInfo('@(Model.Id)');
+            OverlayScrollbar($baseInfoBody.closest('.scroll-box'));
+        });
+
+        function GetStepInfo(no) {
+            $.iwbAjax4({
+                url: abp.appUrl + 'Query/GetCampStep?no=' + no,
+                success: function(res) {
+                    StepFormatter(res);
+                }
+            });
+        }
+
+        function StepFormatter(data) {
+            if (data) {
+                $("#step").html(data.name);
+                $('#step-progress .progress-bar').animate({ "width": `${data.progress}%` });
+                $('#step-progress dd span').html(data.progress);
+            }
+        }
+        iwbHub.client.getUpdateStep = function(msg) {
+            console.log('getUpdateStep: ', msg);
+            if (msg) {
+                try {
+                    var data = typeof msg == 'string' ? JSON.parse(msg) : msg;
+                    if (data) {
+                        StepFormatter(data);
+                    }
+                } catch (e) {
+                    console.log('getUpdateStep: ', e);
+                }
+            }
+        };
+    </script>
+}

+ 138 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/__/_Chart_Cmd.cshtml

@@ -0,0 +1,138 @@
+@using WeApp.Helpers
+<div class="area" id="chart-cmd"></div>
+@using (Html.BeginScripts())
+{
+    <script>
+        //指令
+        var cmdChart = echarts.init(document.getElementById('chart-cmd'));
+        function CmdChart(data, category) {
+            if (data && data.length > 0) {
+                //console.log("=====>CMD", data);
+            } else {
+                console.log("CMD-Null");
+                return;
+            }
+            window.titleOpt.text = "处置指令统计示意图";
+
+            var series = [], nData = [], titleOpts = [window.titleOpt];
+            for (var i = 0; i < data.length; i++) {
+                var item = data[i];
+                for (var ii = 0; ii < item.data.length; ii++) {
+                    if (i == 0) {
+                        nData.push([]);
+                    } 
+                    nData[ii].push({
+                        name: item.name,
+                        value: item.data[ii],
+                        itemStyle:{
+                        color: roundColor[i % 3],
+                     }
+                      })
+                }
+               
+            }
+            
+            for (var i = 0; i < 3; i++) {
+                var item = nData[i];
+                titleOpts.push({
+                    subtext: "第" + (i + 1) + "组",
+                    left: i == 0 ? '16.66%' : i == 1 ? "50%" : i == 2 ? "83.33%" : "",
+                    top: '75%',
+                    subtextStyle: {
+                        color: textColor[1],
+                    },
+                    textAlign: 'center'
+                });
+                series.push({
+                    //name: "第"+i+"组",
+                    type: "pie",
+                    radius: '80%',
+                    center: ['50%', '50%'],
+                    data: item,
+                    left: i * 33.3333 + '%',
+                    right: (2 - i) * 33.3333 + '%',
+                    top: 0,
+                    bottom: 0,
+                    label: {
+                        position: 'inside',
+                        formatter: ' {c} 条'
+                    },
+                    
+                });
+            }
+            //series.push({
+            //    name: item.name,
+            //    type: "pie",
+            //    data: item.data,
+            //    stack: "cmd",
+            //    label: {
+            //        normal: {
+            //            show: true,
+            //            //formatter: '{a} : {c} 条',
+            //            formatter: ' {a} ',
+            //            color: "#eee"
+            //        }
+            //    },
+            //    barMaxWidth: 50,
+            //    barMinHeight: 20,
+            //    itemStyle: {
+            //        color: {
+            //            type: 'linear',
+            //            x: 0,
+            //            y: 0,
+            //            x2: 1,
+            //            y2: 0,
+            //            colorStops: [
+            //                {
+            //                    offset: 0,
+            //                    color: roundColor[i % 3] // 0% 处的颜色
+            //                }, {
+            //                    offset: 1,
+            //                    color: '#27303f' // 100% 处的颜色
+            //                }
+            //            ]
+            //        }
+            //    }
+            //    //itemStyle: {
+            //    //    color: {
+            //    //        type: 'linear',
+            //    //        x: 0,
+            //    //        y: 0,
+            //    //        x2: 0,
+            //    //        y2: 1,
+            //    //        colorStops: [
+            //    //            {
+            //    //                offset: 0,
+            //    //                color: roundColor[i % 3] // 0% 处的颜色
+            //    //            }, {
+            //    //                offset: 1,
+            //    //                color: '#27303f' // 100% 处的颜色
+            //    //            }
+            //    //        ]
+            //    //    },
+            //    //    barBorderRadius: [50, 50, 0, 0]
+            //    //}
+            //});
+            var option = {
+                grid: {
+                    top: 40,
+                    bottom: 20,
+                    left: 30,
+                    right: 10,
+                    borderColor: textColor[1]
+                },
+                legend: {
+                    right: 20,
+                    textStyle: {
+                        color: textColor[1]
+                    },
+                    icon: 'circle'
+                },
+                title: titleOpts,
+                series: series
+            };
+            //console.log(option);
+            cmdChart.setOption(option);
+        }
+    </script>
+}

+ 128 - 0
SourceCode/WeApp.Web/Views/ExerciseV2/_Play/__/_Chart_Cmd2.cshtml

@@ -0,0 +1,128 @@
+@using WeApp.Helpers
+<div class="area" id="chart-cmd"></div>
+@using (Html.BeginScripts())
+{
+    <script>
+        //指令
+        var cmdChart = echarts.init(document.getElementById('chart-cmd'));
+        function CmdChart(data, category) {
+            if (data && data.length > 0) {
+                //console.log("CMD", data);
+            } else {
+                console.log("CMD-Null");
+                return;
+            }
+            var series = [];
+            for (var i = 0; i < data.length; i++) {
+                var item = data[i];
+
+                series.push({
+                    name: item.name,
+                    type: "bar",
+                    data: item.data,
+                    stack: "cmd",
+                    label: {
+                        normal: {
+                            show: true,
+                            //formatter: '{a} : {c} 条',
+                            formatter: ' {c} ',
+                            color: "#eee"
+                        }
+                    },
+                    barMaxWidth: 50,
+                    barMinHeight: 20,
+                    itemStyle: {
+                        color: {
+                            type: 'linear',
+                            x: 0,
+                            y: 0,
+                            x2: 1,
+                            y2: 0,
+                            colorStops: [
+                                {
+                                    offset: 0,
+                                    color: roundColor[i % 3] // 0% 处的颜色
+                                }, {
+                                    offset: 1,
+                                    color: '#27303f' // 100% 处的颜色
+                                }
+                            ]
+                        }
+                    }
+                    //itemStyle: {
+                    //    color: {
+                    //        type: 'linear',
+                    //        x: 0,
+                    //        y: 0,
+                    //        x2: 0,
+                    //        y2: 1,
+                    //        colorStops: [
+                    //            {
+                    //                offset: 0,
+                    //                color: roundColor[i % 3] // 0% 处的颜色
+                    //            }, {
+                    //                offset: 1,
+                    //                color: '#27303f' // 100% 处的颜色
+                    //            }
+                    //        ]
+                    //    },
+                    //    barBorderRadius: [50, 50, 0, 0]
+                    //}
+                });
+            }
+            window.titleOpt.text = "处置指令统计示意图";
+            var option = {
+                grid: {
+                    top: 40,
+                    bottom: 20,
+                    left: 30,
+                    right: 10,
+                    borderColor: textColor[1]
+                },
+                legend: {
+                    right: 20,
+                    textStyle: {
+                        color: textColor[1]
+                    },
+                    icon: 'circle'
+                },
+                title: window.titleOpt,
+                xAxis: {
+                    type: 'category',
+                    data: category,
+                    axisLabel: {
+                        color: textColor[1]
+                    },
+                    axisLine: {
+                        show: true,
+                        lineStyle: {
+                            color: textColor[0]
+                        }
+                    }
+                },
+                yAxis: {
+                    type: 'value',
+                    splitNumber: 4,
+                    axisLabel: {
+                        color: textColor[1]
+                    },
+                    axisLine: {
+                        show: false,
+                        lineStyle: {
+                            color: textColor[0]
+                        }
+                    },
+                    splitLine: {
+                        show: true,
+                        lineStyle: {
+                            color: textColor[0]
+                        }
+                    }
+                },
+                series: series
+            };
+            //console.log(option);
+            cmdChart.setOption(option);
+        }
+    </script>
+}

Some files were not shown because too many files changed in this diff