因为评的剧多了,想在评价的时候显示一个对比坐标系,所以有了这个界面。
最近写的音乐剧评价打分比较多,就想做个按钮,点击以后开界面,界面里面有一个各个剧目的各个分数项的对比,下面卡片式显示分数和评语。然后提了需求,让 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 | - |
代码过程操作:
用了 json 格式记录数据,操作流程如下:
在个人博客的根目录(和 content、themes、static 这些文件夹同级)创建名为 data 的文件夹。因为之后可能还有其他的数据,所以我在 data 下面创建了名为 musicals 的文件夹。之后在文件夹内,以不同剧目的名字创建 json 格式的文件,记录想要对比的基础数据,单项示范如下:文件名 TTB.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}之后用短代码实现按钮以及页面的编写。在 shortcodes 文件夹下面创建 play-comparison.html,放入代码如下。
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 进行样式化。
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}可以自己注意改一下颜色样式什么的,我的是适配了我自己的主题~
祝大家使用愉快~