Hugo | Small Fix:剧目对比按钮


2025-11-13
4369 字
因为评的剧多了,想在评价的时候显示一个对比坐标系,所以有了这个界面。

最近写的音乐剧评价打分比较多,就想做个按钮,点击以后开界面,界面里面有一个各个剧目的各个分数项的对比,下面卡片式显示分数和评语。然后提了需求,让 AI 写了代码,调试了一天,最终效果如下。

其他剧目评分评语参考

剧目 综合 剧本 音乐 舞美 Bonus
德米安 2.8 3 2.5 3 -
锦衣卫之刀与花 2 1 2.5 2 2.5
面试 2.4 2 2 2.5 3
人间失格 3.7 2 4 5 -
Tick, Tick, Boom 3.3 3 3.5 3.5 -

详细评语

德米安:2.8/5
德米安
剧本:3/5, 无功无过地还原了原著的表意。剧情编排虽然散漫但①小剧场就这样②原著本来没什么剧情,原谅了。
舞美:3/5, 基本达到演出表达的需求,切割空间和变化偶尔有些新意。
音乐:2.5/5, 是即使看懂了剧情也不觉得很动人的音乐,能不能听完全取决于演唱者水平。说得上好听的只有返场三首歌。
Bonus: 没什么额外好说的。
锦衣卫之刀与花:2/5
锦衣卫之刀与花
剧本:1/5, 抛去开心麻花常用的性相关笑点和试图让演员卖肉的意图,剧本从剧情核心设定、剧情逻辑、人物塑造、主题表达各个方面都有各种各样的问题,但起伏节奏还算有可取之处,故有此分。
舞美:2/5, 打光布景简陋,无设计性(或者说设计得没有表达),但是高饱和配色非常“出片”,或许是为什么这个戏能卖不错。
音乐:2.5/5, 旋律性不错,有些歌还蛮好听;歌词与歌词之间有严重的画风不统一的倒错,文白夹杂;音乐和剧情衔接运用的方式很无聊;电音、戏曲等元素运用十分刻板,毫无新意。比同创作者别的作品差。
Bonus:2.5/5, 舞蹈:打斗舞蹈还挺好看,虽然更高级的表意没有但是排布得还是有一些意思。(但也不算特别有意思只是相对有意思)。
面试:2.4/5
面试
剧本:2/5, 按理,说明白话就应该及格了,但是我觉得这个剧本甚至没有故事,只是在玩一些如果写成小说会比做成音乐剧精彩的东西。
舞美:2.5/5, 基本达到演出表达的需求,灯光和窗户的使用、人物角色的切换偶尔有些新意。
音乐:2/5, 是即使看懂了剧情也不觉得很动人的音乐,没歌难听也没歌好听。
Bonus:3/5, 作为考察演员剧本解读能力、演技基本功、唱歌基本功和唱商的戏来说有格外的优势。
人间失格:3.7/5
人间失格
剧本:2/5, 其实是及格了的 3 分因为基本脉络还算清晰,但是扣了半分是我太不喜欢这个戏的一些内核了,以及感觉剧情安排非常粗糙再扣半分。
舞美:5/5, 令人震撼。感觉看过这么多场中国外国戏这是第一个从装置到灯光到舞台到换场都让我觉得不仅有设计而且绝美绝好看的音乐剧。如果只花 280 看舞美,我都觉得值回票价。
音乐:4/5, 扣半分在没有特别鲜明的风格化(但是还是有一定风格的)。扣另外半分在音乐感觉不够灵动。但除此之外几乎完美,不管是动机的使用,和声美丽好听的程度,旋律对人物心情的衬托,还是和剧情的衔接。
Bonus: 没什么额外想说的。觉得剧本有很屎的内核但是有些场面很好看打消了我的这种吃屎之感。就不偏不倚吧,不额外调控。
Tick, Tick, Boom :3.3/5
Tick, Tick, Boom
剧本:3/5, 朴实无华。其实我不喜欢讲创作者题材的剧目我觉得有点 cliche 了,这个戏也没什么新意,但是流畅动情之处也确实动情,所以综合来说及格了。
舞美:3.5/5, 空间上上下下的其实也只能说是准确无误地传达了剧情想要的效果,有一些小新意,但美感设计感稍逊。
音乐:3.5/5, 有一些地方写得很灵,但整体上没有很出彩,4 分,偶尔在旋律和声上有点太经典音乐剧而缺乏美感和新意,很多看出来是重点曲目的歌曲现在听起来有点过时,扣了半分。(有些经典不过时只不过这个有点过时)
Bonus: 没什么额外想说的。

代码过程操作:

剧目基础信息存储 | JSON

用了 json 格式记录数据,操作流程如下:

在个人博客的根目录(和 contentthemesstatic 这些文件夹同级)创建名为 data 的文件夹。因为之后可能还有其他的数据,所以我在 data 下面创建了名为 musicals 的文件夹。之后在文件夹内,以不同剧目的名字创建 json 格式的文件,记录想要对比的基础数据,单项示范如下:文件名 TTB.json

json
 1{
 2  "id": "TTB",
 3  "title": "Tick, Tick, Boom ",
 4  "cover": "/img/posts/theater/202510-TickTickBoom.jpg",
 5  "ratings": {
 6    "综合": 3.3,
 7    "剧本": 3,
 8    "音乐": 3.5,
 9    "舞美": 3.5,
10    "Bonus": null
11  },
12  "reviews": {
13    "剧本": "朴实无华。其实我不喜欢讲创作者题材的剧目我觉得有点 cliche 了,这个戏也没什么新意,但是流畅动情之处也确实动情,所以综合来说及格了。",
14    "音乐": "有一些地方写得很灵,但整体上没有很出彩,4 分,偶尔在旋律和声上有点太经典音乐剧而缺乏美感和新意,很多看出来是重点曲目的歌曲现在听起来有点过时,扣了半分。(有些经典不过时只不过这个有点过时)",
15    "舞美": "空间上上下下的其实也只能说是准确无误地传达了剧情想要的效果,有一些小新意,但美感设计感稍逊。",
16    "Bonus": "没什么额外想说的。"
17  }
18}

短代码数据呈现 | HTML

之后用短代码实现按钮以及页面的编写。在 shortcodes 文件夹下面创建 play-comparison.html,放入代码如下。

go-html-template
  1<button class="comparison-trigger" onclick="togglePlayComparison()">
  2  {{ .Get "text" | default "查看剧目对比" }}
  3</button>
  4
  5<div id="playComparison" class="comparison-modal">
  6  <div class="comparison-container">
  7    <!-- 头部独立出来,不参与滚动 -->
  8    <div class="comparison-header">
  9      <h3>{{ .Get "text" | default "查看剧目对比" }}</h3>
 10      <button class="close-btn" onclick="togglePlayComparison()">×</button>
 11    </div>
 12    <!-- 内容区域,单独滚动 -->
 13    <div class="comparison-scrollable">
 14      <div class="comparison-content">
 15      <div class="comparison-table-container">
 16        <table class="theater-comparison-table">
 17          <thead>
 18          <tr>
 19          <th>剧目</th>
 20          {{ $categories := slice "综合" "剧本" "音乐" "舞美" "Bonus" }}
 21          {{ range $category := $categories }}<th>{{ $category }}</th>      {{ end }}
 22          </tr>
 23          </thead>
 24          
 25          <tbody>
 26          {{ range $play := site.Data.musicals }}
 27          <tr>
 28          <td class="play-name">{{ $play.title }}</td>
 29          {{ $categories := slice "综合" "剧本" "音乐" "舞美" "Bonus" }}
 30            {{ range $category := $categories }}
 31              <td class="rating-cell">
 32                {{ if index $play.ratings $category }}
 33                {{ $rating := index $play.ratings $category }}
 34                {{ $rating }}
 35                {{ else }} -
 36                {{ end }}
 37              </td>
 38            {{ end }}
 39          </tr>
 40          {{ end }}
 41          </tbody>
 42        </table>
 43    </div>
 44  
 45    <div class="reviews-section">
 46      <h4>详细评语</h4>
 47      {{ range $play := site.Data.musicals }}
 48      <div class="theater-review">
 49        <h5>{{ $play.title }}{{ if $play.ratings.综合 }}{{ $play.ratings.综合 }}/5{{ end }}</h5>
 50        <div class="review-content-whole">
 51        {{ if $play.cover }}
 52          <div class="review-cover">
 53          <img src="{{ $play.cover }}" alt="{{ $play.title }}" onerror="this.style.display='none'">
 54          </div>
 55        {{ end }}
 56
 57      <div class="review-scripts">
 58      {{ if $play.reviews.剧本 }}
 59      <div class="review-item">
 60      <strong>剧本:{{ if $play.ratings.剧本 }}{{ $play.ratings.剧本 }}/5,{{ end }}</strong>
 61      <span class="review-content">{{ $play.reviews.剧本 }}</span>
 62      </div>
 63      {{ end }}
 64
 65      {{ if $play.reviews.舞美 }}
 66      <div class="review-item">
 67      <strong>舞美:{{ if $play.ratings.舞美 }}{{ $play.ratings.舞美 }}/5,{{ end }}</strong>
 68      <span class="review-content">{{ $play.reviews.舞美 }}</span>
 69      </div>
 70      {{ end }}
 71      
 72      {{ if $play.reviews.音乐 }}
 73      <div class="review-item">
 74      <strong>音乐:{{ if $play.ratings.音乐 }}{{ $play.ratings.音乐 }}/5,{{ end }}</strong>
 75      <span class="review-content">{{ $play.reviews.音乐 }}</span>
 76      </div>
 77      {{ end }}
 78      
 79      {{ if $play.reviews.Bonus }}
 80      <div class="review-item">
 81      <strong>Bonus:{{ if $play.ratings.Bonus }}{{ $play.ratings.Bonus }}/5,{{ end }}</strong>
 82      <span class="review-content">{{ $play.reviews.Bonus }}</span>
 83      </div>
 84      {{ end }}
 85    </div>
 86    </div>
 87    </div>
 88    {{ end }}
 89    </div>
 90    </div>
 91    </div>
 92  </div>
 93</div>
 94
 95<script>
 96function togglePlayComparison() {
 97  const modal = document.getElementById('playComparison');
 98  if (modal.style.display === 'block') {
 99    modal.style.display = 'none';
100    document.body.style.overflow = 'auto';
101  } else {
102    modal.style.display = 'block';
103    document.body.style.overflow = 'hidden';
104  }
105}
106
107window.onclick = function(event) {
108  const modal = document.getElementById('playComparison');
109  if (event.target === modal) {
110    togglePlayComparison();
111  }
112}
113
114document.addEventListener('keydown', function(event) {
115  if (event.key === 'Escape') {
116    const modal = document.getElementById('playComparison');
117    if (modal.style.display === 'block') {
118      togglePlayComparison();
119    }
120  }
121});
122
123</script>

如果需要放按钮,就在需要摆放的 markdown 正文里放短代码就好。为了防止渲染,在标签前面多加了一个中文句号,自己使用记得删掉~

{{。< play-comparison text=" 其他剧目评分评语参考 " >}}

美化样式 | CSS

用 CSS 进行样式化。

css
  1/*剧目集中展示评分*/
  2
  3/* 触发按钮样式 */
  4.comparison-trigger {
  5  background: linear-gradient(135deg, #55a290 0%, #9055a2 100%);
  6  color: white;
  7  border: none;
  8  display: block;
  9  padding: 10px 20px;
 10  border-radius: 5px;
 11  cursor: pointer;
 12  font-size: 16px;
 13  font-weight: 500;
 14  transition: all 0.3s ease;
 15  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
 16  margin: 10px auto 40px auto;
 17  width: fit-content;
 18}
 19.comparison-trigger:hover {
 20  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
 21}
 22
 23/* 小屏幕调整 */
 24@media (max-width: 768px) {
 25  .comparison-trigger {
 26    width: 100%;
 27  }
 28}
 29/* 模态框样式 */
 30.comparison-modal {
 31  display: none;
 32  position: fixed;
 33  z-index: 10000;
 34  left: 0;
 35  top: 0;
 36  width: 100%;
 37  height: 100%;
 38  background-color: rgba(0, 0, 0, 0.5);
 39  backdrop-filter: blur(5px);
 40}
 41
 42/* 主容器 - 居中且固定高度 */
 43.comparison-container {
 44  position: absolute;
 45  top: 50%;
 46  left: 50%;
 47  transform: translate(-50%, -50%);
 48  width: 95%;
 49  max-width: 1000px;
 50  height: 80vh; /* 视窗高度的80% */
 51  border-radius: 5px;
 52  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
 53  display: flex;
 54  flex-direction: column;
 55  animation: modalSlideIn 0.3s ease;
 56  overflow: hidden; /* 防止内容溢出 */
 57}
 58@keyframes modalSlideIn {
 59  from {
 60    opacity: 0;
 61    transform: translate(-50%, -48%); /* 微调动画起始位置 */
 62  }
 63  to {
 64    opacity: 1;
 65    transform: translate(-50%, -50%);
 66  }
 67}
 68
 69/* 头部样式 - 固定高度,不参与滚动 */
 70.comparison-header {
 71  display: flex;
 72  justify-content: space-between;
 73  align-items: center;
 74  padding: 20px 30px;
 75  background: linear-gradient(135deg, #55a290 0%, #764ba2 100%);
 76  color: white;
 77  flex-shrink: 0; /* 防止头部被压缩 */
 78  z-index: 100;
 79}
 80.comparison-header h3 {
 81  margin: 0;
 82  font-size: 1.5em;
 83  color: white;
 84}
 85.close-btn {
 86  background: none;
 87  border: none;
 88  color: white;
 89  font-size: 28px;
 90  cursor: pointer;
 91  padding: 0;
 92  width: 40px;
 93  height: 40px;
 94  display: flex;
 95  align-items: center;
 96  justify-content: center;
 97  border-radius: 50%;
 98  transition: background-color 0.3s;
 99  line-height: 1;
100}
101.close-btn:hover {
102  background-color: rgba(255, 255, 255, 0.2);
103}
104
105/* 可滚动区域 */
106.comparison-scrollable {
107  flex: 1; /* 占据剩余空间 */
108  overflow-y: auto; /* 垂直滚动 */
109  overflow-x: hidden; /* 隐藏水平滚动 */
110}
111
112/* 内容区域 */
113.comparison-content {
114  padding: 0;
115}
116
117/* 表格容器 */
118.comparison-table-container {
119  padding: 20px;
120  overflow-x: auto;
121  background-color: #fefefe;
122  display: block;
123  margin: 0 auto;
124}
125
126/* 表格样式 */
127.theater-comparison-table {
128  width: fit-content;
129  border-collapse: collapse;
130  margin-bottom: 20px;
131  font-size: 14px;
132  display: block;
133  margin: 0 auto;
134}
135.theater-comparison-table th,
136.theater-comparison-table td {
137  padding: 12px 15px;
138  text-align: center !important;
139  border: 1px solid #e1e1e1;
140  vertical-align: middle;
141}
142.theater-comparison-table th {
143  background-color: #f8f9fa;
144  font-weight: 600;
145  color: #333;
146}
147.theater-comparison-table thead {
148  tr {
149    th{
150    background-color: #55a290;
151    color: white;
152    }
153  }
154}
155.theater-comparison-table tr:nth-child(even){
156  background-color: #edf5f3;
157}
158.theater-comparison-table tr:nth-child(even):hover {
159  background-color: #ddece8;
160}
161
162/* 剧目名称列样式 - 允许换行 */
163.play-name {
164  font-weight: 600;
165  text-align: center;
166  white-space: normal !important;
167  word-wrap: break-word;
168  position: sticky;
169  left: 0;
170  z-index: 5;
171  min-width: 120px;
172  max-width: 150px;
173}
174
175/* 评分单元格样式 - 不允许换行 */
176.rating-cell {
177  font-size: 14px !important;
178  color: #2c3e50;
179  min-width: 80px;
180  white-space: nowrap;
181}
182
183/* 评语部分 */
184.reviews-section {
185  padding: 20px 30px;
186  border-top: 1px solid #e1e1e1;
187  background-color: #fafafa;
188}
189.reviews-section h4 {
190  margin-bottom: 20px;
191  color: #333;
192  border-bottom: 2px solid #5567a2;
193  padding-bottom: 8px;
194  font-size: 1.3em;
195  font-weight: 600;
196}
197.theater-review {
198  background: white;
199  padding: 20px;
200  margin-bottom: 20px;
201  border-radius: 5px;
202  box-shadow: 0 2px 10px rgba(144, 85, 162, 0.5);
203  border-left: 4px solid #5567a2;
204}
205.theater-review:last-child {
206  margin-bottom: 0;
207}
208.theater-review h5 {
209  margin: 0 0 15px 0;
210  color: #5567a2;
211  font-size: 1.2em;
212  font-weight: 600;
213  border-bottom: 1px solid #e1e1e1;
214  padding-bottom: 8px;
215}
216.review-item {
217  margin-bottom: 0.75em;
218  line-height: 1.55;
219  color: #555;
220  padding: 0;
221  border-bottom: none;
222}
223.review-item:last-child {
224  margin-bottom: 0;
225}
226.review-item strong {
227  color: #333;
228  font-weight: 600;
229  font-size: 0.95em;
230  display: inline;
231}
232.review-content {
233  color: #555;
234  line-height: 1.55;
235  display: inline;
236}
237
238/* 响应式调整 */
239@media (max-width: 768px) {
240  .comparison-container {
241    width: 98%;
242    height: 90vh; /* 小屏幕时高度更大 */
243  }
244  .comparison-header {
245    padding: 15px 20px;
246  }
247  .comparison-header h3 {
248    font-size: 1.2em;
249  }
250  .comparison-table-container {
251    padding: 10px;
252  }
253  .theater-comparison-table th,
254  .theater-comparison-table td {
255    padding: 8px 10px;
256    font-size: 0.9em;
257  }
258  .play-name {
259    min-width: 100px;
260    max-width: 120px;
261    font-size: 0.9em;
262  }
263  .rating-cell {
264    min-width: 50px;
265    font-size: 0.9em;
266  }
267  .reviews-section {
268    padding: 15px 20px;
269  }
270  .theater-review {
271    padding: 15px;
272  }
273}
274@media (max-width: 480px) {
275  .comparison-trigger {
276    padding: 8px 16px;
277    font-size: 16px;
278    width: 100%;
279  }
280  .comparison-header {
281    gap: 10px;
282    text-align: center;
283  }
284  .play-name {
285    min-width: 50px;
286    font-size: 0.85em;
287  }
288  .comparison-container {
289    height: 95vh; /* 小屏幕几乎全屏 */
290  }
291}
292
293/* 评语 - 包含封面和内容 */
294.review-title {
295  flex: 1;
296}
297.review-title h5 {
298  margin: 0;
299  color: #5567a2;
300  font-size: 1.4em;
301  font-weight: 600;
302  line-height: 1.3;
303}
304.review-content-whole {
305  display: flex;
306  flex-direction: row;
307}
308.review-cover {
309  display: block;
310  flex-shrink: 0;
311  width: 120px;
312  height: 160px;
313  border-radius: 5px;
314  overflow: hidden;
315  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
316}
317.review-cover img {
318  width: 100%;
319  height: 100%;
320  object-fit: cover;
321  display: block;
322}
323.review-scripts {
324  display: block;
325  flex: 1;
326  margin-left: 20px;
327}
328
329/* 小屏幕样式 - 768px以下隐藏封面 */
330@media (max-width: 768px) {
331
332  /* 小屏幕隐藏封面 */  
333  .review-cover {
334    display: none; /* 小屏幕隐藏封面 */
335  }
336  .review-scripts {
337    margin-left: 0;
338  }
339}

可以自己注意改一下颜色样式什么的,我的是适配了我自己的主题~

结束了!

祝大家使用愉快~