因为评的剧多了,想在评价的时候显示一个对比坐标系,所以有了这个界面。
最近写的音乐剧评价打分比较多,就想做个按钮,点击以后开界面,界面里面有一个各个剧目的各个分数项的对比,下面卡片式显示分数和评语。然后提了需求,让 AI 写了代码,调试了一天,最终效果如下。
代码过程操作:
用了 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}可以自己注意改一下颜色样式什么的,我的是适配了我自己的主题~
祝大家使用愉快~
这里是 20251121 的分界线。使用以后我发现,由于短代码触发的模态框只是界面显隐,但都记录在网页里,导致我所有增加按钮的页面,都会把按钮点击后打开的页面内的文字记录到网页总字数里,在搜索的时候,也会因为搜索到模态框里面的信息,而让使用了短代码的博文出现在搜索结果里。
因此在调试后,认为最好的解决方式是,创立一个新的对比页面,而短代码放置的按钮只负责触发模态框,模态框里面打开的内容则是对比页面的内容。这样因为对比内容都放在另外的页面里,搜索结果和字数都不会有问题。
JSON 的文件格式还是保留。
哦对,因为数据变多了以后排列顺序会有变化,所以额外增加排序选择~
创建 content/musical-comparison.md,在文件的信息部分增加如下信息,指定页面标题和网页模板。
1title= "音乐剧评价汇总"
2layout="musical-comparison"其次创建 layouts/_default/musical-comparison.html.
不同主题模板的基础格式不同,我放在这里作参考,大家可以根据自己的主题自行调整。
1{{ define "main" }}
2<div class="col-xs-12 col-sm-8 col-md-9">
3 {{ partial "mobile_nav_toggle.html" . }}
4
5<div class="play-comparison-body">
6 <div class="comparison-container">
7 <div class="comparison-header">
8 <h3>剧目评分对比</h3>
9 <button class="close-btn" onclick="if (window.opener) { window.close(); } else { history.back(); }">×</button>
10 </div>
11 <div class="comparison-scrollable">
12 <div class="comparison-content">
13
14 <div class="comparison-table-container">
15 <table class="theater-comparison-table">
16 <thead>
17 <tr>
18 <th>剧目</th>
19 {{ $categories := slice "综合" "剧本" "音乐" "舞美" "Bonus" }}
20 {{ range $category := $categories }}
21 <th>{{ $category }}</th>
22 {{ end }}
23 </tr>
24 </thead>
25 <tbody id="comparisonTableBody">
26 {{ range $play := site.Data.musicals }}
27 <tr data-title="{{ $play.title }}" data-rating="{{ $play.ratings.综合 }}">
28 <td class="play-name">{{ $play.title }}</td>
29 {{ $categories := slice "综合" "剧本" "音乐" "舞美" "Bonus" }}
30 {{ range $category := $categories }}
31 <td class="rating-cell" data-rating="{{ if index $play.ratings $category }}{{ index $play.ratings $category }}{{ end }}">
32 {{ if index $play.ratings $category }}
33 {{ $rating := index $play.ratings $category }}
34 {{ $rating | lang.FormatNumber 1 }}
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
47 <div class="reviews-header">
48 <h4>详细评语</h4>
49 <div class="sort-options">
50 <span class="sort-label">排序:</span>
51 <div class="sort-buttons">
52 <button class="sort-btn active" data-sort="default">默认</button>
53 <button class="sort-btn" data-sort="rating-desc">评分 ↓</button>
54 <button class="sort-btn" data-sort="rating-asc">评分 ↑</button>
55 </div>
56 </div>
57 </div>
58
59 <div id="reviewsContainer">
60 {{ range $play := site.Data.musicals }}
61 <div class="theater-review" data-title="{{ $play.title }}" data-rating="{{ $play.ratings.综合 }}">
62 <h5>{{ $play.title }}{{ if $play.ratings.综合 }}:{{ $play.ratings.综合 | lang.FormatNumber 1 }}/5{{ end }}</h5>
63 <div class="review-content-whole">
64 {{ if $play.cover }}
65 <div class="review-cover">
66 <img src="{{ $play.cover }}" alt="{{ $play.title }}" onerror="this.style.display='none'">
67 </div>
68 {{ end }}
69 <div class="review-scripts">
70 <!-- 剧本 -->
71 {{ if $play.reviews.剧本 }}
72 <div class="review-item">
73 <strong>剧本:{{ if $play.ratings.剧本 }}{{ $play.ratings.剧本 | lang.FormatNumber 1 }}/5,{{ end }}</strong>
74 <span class="review-content">{{ $play.reviews.剧本 }}</span>
75 </div>
76 {{ end }}
77 <!-- 音乐 -->
78 {{ if $play.reviews.音乐 }}
79 <div class="review-item">
80 <strong>音乐:{{ if $play.ratings.音乐 }}{{ $play.ratings.音乐 | lang.FormatNumber 1 }}/5,{{ end }}</strong>
81 <span class="review-content">{{ $play.reviews.音乐 }}</span>
82 </div>
83 {{ end }}
84 <!-- 舞美 -->
85 {{ if $play.reviews.舞美 }}
86 <div class="review-item">
87 <strong>舞美:{{ if $play.ratings.舞美 }}{{ $play.ratings.舞美 | lang.FormatNumber 1 }}/5,{{ end }}</strong>
88 <span class="review-content">{{ $play.reviews.舞美 }}</span>
89 </div>
90 {{ end }}
91 <!-- Bonus -->
92 {{ if $play.reviews.Bonus }}
93 <div class="review-item">
94 <strong>Bonus:{{ if $play.ratings.Bonus }}{{ $play.ratings.Bonus | lang.FormatNumber 1 }}/5,{{ end }}</strong>
95 <span class="review-content">{{ $play.reviews.Bonus }}</span>
96 </div>
97 {{ end }}
98 </div>
99 </div>
100 </div>
101 {{ end }}
102 </div>
103 </div>
104
105 </div>
106 </div>
107 </div>
108
109<script>
110// 排序功能
111document.addEventListener('DOMContentLoaded', function() {
112 const sortButtons = document.querySelectorAll('.sort-btn');
113 const reviewsContainer = document.getElementById('reviewsContainer');
114 const tableBody = document.getElementById('comparisonTableBody');
115 // 按钮排序功能
116 sortButtons.forEach(button => {
117
118 button.addEventListener('click', function() {
119 // 移除其他按钮的active状态
120 sortButtons.forEach(btn => btn.classList.remove('active'));
121 // 添加当前按钮的active状态
122 this.classList.add('active');
123 const sortType = this.getAttribute('data-sort');
124 sortReviews(sortType);
125 sortTable(sortType);
126 });
127 });
128
129 function sortReviews(sortType) {
130 const reviews = Array.from(reviewsContainer.querySelectorAll('.theater-review'));
131 reviews.sort((a, b) => {
132 const titleA = a.getAttribute('data-title');
133 const titleB = b.getAttribute('data-title');
134 const ratingA = parseFloat(a.getAttribute('data-rating')) || 0;
135 const ratingB = parseFloat(b.getAttribute('data-rating')) || 0;
136 switch(sortType) {
137 case 'rating-desc':
138 return ratingB - ratingA;
139 case 'rating-asc':
140 return ratingA - ratingB;
141 case 'default':
142 default:
143 return titleA.localeCompare(titleB);
144 }
145 });
146 reviewsContainer.innerHTML = '';
147 reviews.forEach(review => {
148 reviewsContainer.appendChild(review);
149 });
150 }
151
152 function sortTable(sortType) {
153 const rows = Array.from(tableBody.querySelectorAll('tr'));
154 rows.sort((a, b) => {
155 const titleA = a.getAttribute('data-title');
156 const titleB = b.getAttribute('data-title');
157 const ratingA = parseFloat(a.getAttribute('data-rating')) || 0;
158 const ratingB = parseFloat(b.getAttribute('data-rating')) || 0;
159 switch(sortType) {
160 case 'rating-desc':
161 return ratingB - ratingA;
162 case 'rating-asc':
163 return ratingA - ratingB;
164 case 'default':
165 default:
166 return titleA.localeCompare(titleB);
167 }
168 });
169 tableBody.innerHTML = '';
170 rows.forEach(row => {
171 tableBody.appendChild(row);
172 });
173 }
174});
175</script>
176
177</div>
178<a id="bottom"></a>
179
180</div>
181{{ end }}主要是需要短代码唤起模态框,在模态框里引用 localhost:1313/musical-comparison 页面的页面元素。
1<button class="comparison-trigger" onclick="togglePlayComparison()">
2 {{ .Get "text" | default "查看剧目对比" }}
3</button>
4
5<div id="playComparisonModal" class="comparison-modal">
6 <div class="modal-content">
7 <div class="modal-header">
8 <h3>剧目评分对比</h3>
9 <button class="close-btn" onclick="togglePlayComparison()">×</button>
10 </div>
11 <div class="modal-body">
12 <div id="comparisonContent" class="modal-scrollable">
13 <div class="loading">加载中...</div>
14 </div>
15 </div>
16 </div>
17</div>
18
19<script>
20// 确保DOM加载完成
21document.addEventListener('DOMContentLoaded', function() {
22 // 初始化模态框状态
23 const modal = document.getElementById('playComparisonModal');
24 const content = document.getElementById('comparisonContent');
25 // 确保模态框初始状态正确
26 modal.style.display = 'none';
27});
28
29function togglePlayComparison() {
30 const modal = document.getElementById('playComparisonModal');
31 const content = document.getElementById('comparisonContent');
32 // 使用getComputedStyle确保跨浏览器兼容
33 const isOpen = window.getComputedStyle(modal).display === 'block';
34 if (isOpen) {
35 modal.style.display = 'none';
36 document.body.style.overflow = 'auto';
37 } else {
38 // 确保模态框层级最高
39 modal.style.display = 'block';
40 modal.style.zIndex = '10000';
41 document.body.style.overflow = 'hidden';
42 // 强制重绘以确保显示
43 modal.offsetHeight;
44 if (shouldLoadContent(content)) {
45 loadComparisonContent(content);
46 }
47 }
48}
49
50function shouldLoadContent(content) {
51 return content.innerHTML.includes('loading') || content.children.length <= 1;
52}
53
54function loadComparisonContent(content) {
55 content.innerHTML = '<div class="loading">加载中...</div>';
56 fetch('/musical-comparison/')
57 .then(response => {
58 if (!response.ok) throw new Error('Network response was not ok');
59 return response.text();
60 })
61 .then(html => {
62 const parser = new DOMParser();
63 const doc = parser.parseFromString(html, 'text/html');
64 const mainContent = doc.querySelector('.comparison-content');
65 if (mainContent) {
66 content.innerHTML = mainContent.innerHTML;
67 bindSortEvents();
68 } else {
69 content.innerHTML = '<p>加载失败,内容格式错误</p>';
70 }
71 })
72 .catch(error => {
73 console.error('加载内容失败:', error);
74 content.innerHTML = '<p>加载失败,请刷新页面重试</p>';
75 });
76}
77
78// 其他函数保持不变...
79function bindSortEvents() {
80 const sortButtons = document.querySelectorAll('.sort-btn');
81 const reviewsContainer = document.getElementById('reviewsContainer');
82 const tableBody = document.getElementById('comparisonTableBody');
83 if (!sortButtons.length || !reviewsContainer || !tableBody) return;
84 sortButtons.forEach(button => {
85 button.addEventListener('click', function() {
86 sortButtons.forEach(btn => btn.classList.remove('active'));
87 this.classList.add('active');
88 const sortType = this.getAttribute('data-sort');
89 sortReviews(sortType);
90 sortTable(sortType);
91 });
92 });
93}
94
95function sortReviews(sortType) {
96 const reviewsContainer = document.getElementById('reviewsContainer');
97 if (!reviewsContainer) return;
98 const reviews = Array.from(reviewsContainer.querySelectorAll('.theater-review'));
99 reviews.sort((a, b) => {
100 const titleA = a.getAttribute('data-title');
101 const titleB = b.getAttribute('data-title');
102 const ratingA = parseFloat(a.getAttribute('data-rating')) || 0;
103 const ratingB = parseFloat(b.getAttribute('data-rating')) || 0;
104 switch(sortType) {
105 case 'rating-desc':
106 return ratingB - ratingA;
107 case 'rating-asc':
108 return ratingA - ratingB;
109 case 'default':
110 default:
111 return titleA.localeCompare(titleB);
112 }
113 });
114 reviewsContainer.innerHTML = '';
115 reviews.forEach(review => {
116 reviewsContainer.appendChild(review);
117 });
118}
119
120function sortTable(sortType) {
121 const tableBody = document.getElementById('comparisonTableBody');
122 if (!tableBody) return;
123 const rows = Array.from(tableBody.querySelectorAll('tr'));
124 rows.sort((a, b) => {
125 const titleA = a.getAttribute('data-title');
126 const titleB = b.getAttribute('data-title');
127 const ratingA = parseFloat(a.getAttribute('data-rating')) || 0;
128 const ratingB = parseFloat(b.getAttribute('data-rating')) || 0;
129 switch(sortType) {
130 case 'rating-desc':
131 return ratingB - ratingA;
132 case 'rating-asc':
133 return ratingA - ratingB;
134 case 'default':
135 default:
136 return titleA.localeCompare(titleB);
137 }
138 });
139 tableBody.innerHTML = '';
140 rows.forEach(row => {
141 tableBody.appendChild(row);
142 });
143}
144
145// 事件监听 - 使用更兼容的方式
146document.addEventListener('click', function(event) {
147 const modal = document.getElementById('playComparisonModal');
148 if (event.target === modal) {
149 togglePlayComparison();
150 }
151});
152
153document.addEventListener('keydown', function(event) {
154 if (event.key === 'Escape') {
155 const modal = document.getElementById('playComparisonModal');
156 if (window.getComputedStyle(modal).display === 'block') {
157 togglePlayComparison();
158 }
159 }
160});
161</script>懒得单独说修改了,干脆替代式贴过来。
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
20.comparison-trigger:hover {
21 box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
22}
23
24/* 小屏幕调整 */
25@media (max-width: 768px) {
26 .comparison-trigger {
27 width: 100%;
28 }
29}
30
31/* 模态框样式 */
32/* 浏览器兼容性修复 */
33.comparison-modal {
34 display: none;
35 position: fixed;
36 z-index: 10000;
37 left: 0;
38 top: 0;
39 width: 100%;
40 height: 100%;
41 background-color: rgba(0, 0, 0, 0.5);
42 /* 为Edge添加备用背景色 */
43 background-color: rgba(0, 0, 0, 0.5)\9; /* IE/Edge */
44 backdrop-filter: blur(5px);
45 /* 确保在Edge中正确显示 */
46 -ms-overflow-style: none; /* IE/Edge */
47}
48
49/* 模态框内容容器 */
50/* 修复Edge中的模态框定位 */
51.modal-content {
52 position: absolute;
53 top: 50%;
54 left: 50%;
55 transform: translate(-50%, -50%);
56 width: 95%;
57 max-width: 1000px;
58 height: 80vh;
59 border-radius: 5px;
60 display: flex;
61 flex-direction: column;
62 animation: modalSlideIn 0.3s ease;
63 overflow: hidden;
64 background: transparent;
65 /* 为Edge添加边框和阴影 */
66 border: 0px;
67 box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
68 /* Edge兼容性 */
69 -ms-transform: translate(-50%, -50%);
70}
71
72/* 确保模态框在Edge中正确显示 */
73@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
74 /* IE10+ CSS styles */
75 .comparison-modal {
76 background-color: rgba(0, 0, 0, 0.7);
77 }
78 .modal-content {
79 position: fixed;
80 margin: 0;
81 }
82}
83
84@keyframes modalSlideIn {
85 from {
86 opacity: 0;
87 transform: translate(-50%, -48%);
88 }
89 to {
90 opacity: 1;
91 transform: translate(-50%, -50%);
92 }
93}
94
95/* 模态框头部 */
96.modal-header {
97 display: flex;
98 align-items: center;
99 padding: 20px 30px;
100 background: linear-gradient(135deg, #55a290 0%, #764ba2 100%);
101 color: white;
102 flex-shrink: 0;
103 z-index: 100;
104}
105
106.modal-header h3 {
107 margin: 0;
108 font-size: 1.5em;
109 color: white;
110 text-align: left;
111 flex:1;
112}
113
114/* 模态框主体 - 确保有滚动 */
115.modal-body {
116 flex: 1;
117 overflow: hidden;
118 display: flex;
119 flex-direction: column;
120 background: white;
121 /* Edge flex兼容性 */
122 -ms-flex: 1 1 auto;
123 padding: 0;
124}
125
126/* 模态框内的滚动容器 */
127.modal-scrollable {
128 flex: 1;
129 overflow-y: auto;
130 overflow-x: hidden;
131 /* Edge兼容性 */
132 -ms-overflow-style: none;
133 -ms-flex: 1 1 auto;
134}
135
136/* 确保独立页面的滚动容器在模态框内也有效 */
137.comparison-scrollable {
138 flex: 1;
139 overflow-y: auto;
140 overflow-x: hidden;
141 height: 100%;
142}
143
144/* 主容器 - 用于独立页面 */
145.comparison-container {
146 position: relative;
147 width: 100%;
148 min-height: 100vh;
149 margin: 0;
150 border-radius: 5px;
151 box-shadow: none;
152 display: flex;
153 flex-direction: column;
154 overflow: hidden;
155 background: white;
156}
157
158/* 头部样式 - 固定高度,不参与滚动 */
159.comparison-header {
160 display: flex;
161 justify-content: space-between;
162 align-items: center;
163 padding: 20px 30px;
164 background: linear-gradient(135deg, #55a290 0%, #764ba2 100%);
165 color: white;
166 flex-shrink: 0;
167}
168
169.comparison-header h3 {
170 margin: 0;
171 font-size: 1.5em;
172 color: white;
173}
174
175.close-btn {
176 background: none;
177 border: none;
178 color: white;
179 font-size: 28px;
180 cursor: pointer;
181 padding: 0;
182 width: 40px;
183 height: 40px;
184 display: flex;
185 align-items: center;
186 justify-content: center;
187 border-radius: 50%;
188 transition: background-color 0.3s;
189 line-height: 1;
190 margin-left: auto;
191}
192
193.close-btn:hover {
194 background-color: rgba(255, 255, 255, 0.2);
195}
196
197/* 可滚动区域 */
198.comparison-scrollable {
199 flex: 1;
200 overflow-y: auto;
201 overflow-x: hidden;
202}
203
204/* 内容区域 */
205.comparison-content {
206 padding: 0;
207}
208
209/* 表格容器 */
210.comparison-table-container {
211 padding: 10px;
212 overflow-x: auto;
213 background-color: #fefefe;
214 display: block;
215 margin: 0 auto;
216}
217
218/* 表格样式 */
219.theater-comparison-table {
220 width: fit-content;
221 border-collapse: collapse;
222 margin-bottom: 20px;
223 font-size: 14px;
224 display: block;
225 margin: 0 auto;
226}
227
228.theater-comparison-table th,
229.theater-comparison-table td {
230 padding: 12px 15px;
231 text-align: center !important;
232 border: 1px solid #e1e1e1;
233 vertical-align: middle;
234}
235
236.theater-comparison-table th {
237 background-color: #f8f9fa;
238 font-weight: 600;
239 color: #333;
240}
241
242.theater-comparison-table thead tr th {
243 background: none;
244 background-color: #55a290;
245 color: white;
246}
247
248.theater-comparison-table tr:nth-child(even){
249 background-color: #edf5f3;
250}
251
252.theater-comparison-table tr:nth-child(even):hover {
253 background-color: #ddece8;
254}
255
256/* 剧目名称列样式 - 允许换行 */
257.play-name {
258 font-weight: 600;
259 text-align: center;
260 white-space: normal !important;
261 word-wrap: break-word;
262 position: sticky;
263 left: 0;
264 z-index: 5;
265 min-width: 120px;
266 max-width: 150px;
267}
268
269/* 评分单元格样式 - 不允许换行 */
270.rating-cell {
271 font-size: 14px !important;
272 color: #2c3e50;
273 min-width: 80px;
274 white-space: nowrap;
275}
276
277/* 评语部分 */
278.reviews-section {
279 padding: 20px 30px;
280 border-top: 1px solid #e1e1e1;
281 background-color: #fafafa;
282}
283
284.reviews-section h4 {
285 margin-bottom: 20px;
286 color: #333;
287 border-bottom: 2px solid #5567a2;
288 padding-bottom: 8px;
289 font-size: 1.3em;
290 font-weight: 600;
291}
292
293.theater-review {
294 background: white;
295 padding: 20px;
296 margin-bottom: 20px;
297 border-radius: 5px;
298 box-shadow: 0 2px 10px rgba(144, 85, 162, 0.5);
299 position: relative;
300 overflow: hidden;
301}
302
303.theater-review::before {
304 content: '';
305 position: absolute;
306 left: 0;
307 top: 0px;
308 bottom: 0px;
309 width: 4px;
310 background: linear-gradient(to bottom, #5567a2 10%, #9055a2 90%);
311 border-radius: 0 2px 2px 0;
312}
313
314.theater-review:last-child {
315 margin-bottom: 0;
316}
317
318.theater-review h5 {
319 margin: 0 0 15px 0;
320 color: #5567a2;
321 font-size: 1.2em;
322 font-weight: 600;
323 border-bottom: 1px solid #e1e1e1;
324 padding-bottom: 8px;
325}
326
327.review-item {
328 margin-bottom: 0.75em;
329 line-height: 1.55;
330 color: #555;
331 padding: 0;
332 border-bottom: none;
333}
334
335.review-item:last-child {
336 margin-bottom: 0;
337}
338
339.review-item strong {
340 color: #333;
341 font-weight: 600;
342 font-size: 0.95em;
343 display: inline;
344}
345
346.review-content {
347 color: #555;
348 line-height: 1.55;
349 display: inline;
350}
351
352/* 响应式调整 */
353@media (max-width: 768px) {
354 .modal-content {
355 width: 98%;
356 height: 90vh;
357 }
358 .comparison-container {
359 width: 100%;
360 }
361 .comparison-header,
362 .modal-header {
363 padding: 15px 20px;
364 }
365 .comparison-header h3,
366 .modal-header h3 {
367 font-size: 1.2em;
368 }
369 .comparison-table-container {
370 padding: 10px;
371 }
372 .theater-comparison-table th,
373 .theater-comparison-table td {
374 padding: 8px 10px;
375 font-size: 0.9em;
376 }
377 .play-name {
378 min-width: 100px;
379 max-width: 120px;
380 font-size: 0.9em;
381 }
382 .rating-cell {
383 min-width: 50px;
384 font-size: 0.9em;
385 }
386 .reviews-section {
387 padding: 15px 20px;
388 }
389 .theater-review {
390 padding: 15px;
391 }
392}
393
394@media (max-width: 480px) {
395 .comparison-trigger {
396 padding: 8px 16px;
397 font-size: 16px;
398 width: 100%;
399 }
400 .comparison-header,
401 .modal-header {
402 gap: 10px;
403 text-align: center;
404 }
405 .play-name {
406 min-width: 50px;
407 font-size: 0.85em;
408 }
409 .modal-content {
410 height: 95vh;
411 }
412}
413
414/* 评语 - 包含封面和内容 */
415.review-content-whole {
416 display: flex;
417 flex-direction: row;
418}
419
420.review-cover {
421 display: block;
422 flex-shrink: 0;
423 width: 120px;
424 height: 160px;
425 border-radius: 5px;
426 overflow: hidden;
427 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
428}
429
430.review-cover img {
431 width: 100%;
432 height: 100%;
433 object-fit: cover;
434 display: block;
435}
436
437.review-scripts {
438 display: block;
439 flex: 1;
440 margin-left: 20px;
441}
442
443/* 小屏幕样式 - 768px以下隐藏封面 */
444
445@media (max-width: 768px) {
446 .review-cover {
447 display: none;
448 }
449 .review-scripts {
450 margin-left: 0;
451 }
452}
453
454/* 排序功能样式 */
455.reviews-header {
456 display: flex;
457 justify-content: space-between;
458 align-items: center;
459 margin-bottom: 20px;
460 flex-wrap: wrap;
461 gap: 15px;
462}
463
464.reviews-header h4 {
465 margin: 0;
466 color: #333;
467 border-bottom: 2px solid #5567a2;
468 padding-bottom: 8px;
469 font-size: 1.3em;
470 font-weight: 600;
471}
472
473.sort-options {
474 display: flex;
475 align-items: center;
476 gap: 10px;
477}
478
479.sort-label {
480 color: #666;
481 font-size: 0.9em;
482 font-weight: 500;
483}
484
485.sort-buttons {
486 display: flex;
487 background: #f5f5f5;
488 border-radius: 6px;
489 padding: 4px;
490 gap: 2px;
491}
492
493.sort-btn {
494 background: transparent;
495 min-width: 70px;
496 border: none;
497 padding: 8px 12px;
498 border-radius: 4px;
499 cursor: pointer;
500 font-size: 0.85em;
501 font-weight: 500;
502 color: #666;
503 transition: all 0.3s ease;
504 white-space: nowrap;
505}
506
507.sort-btn:hover {
508 background: rgba(85, 102, 162, 0.1);
509 color: #5567a2;
510}
511
512.sort-btn.active {
513 background: #5567a2;
514 color: white;
515}
516
517/* 加载状态 */
518.loading {
519 display: flex;
520 justify-content: center;
521 align-items: center;
522 height: 200px;
523 font-size: 1.1em;
524 color: #666;
525}
526
527/* 响应式调整 */
528@media (max-width: 768px) {
529 .reviews-header {
530 flex-direction: column;
531 align-items: flex-start;
532 }
533 .sort-options {
534 width: 100%;
535 justify-content: flex-start;
536 }
537 .sort-buttons {
538 flex: 1;
539 justify-content: space-between;
540 }
541 .sort-btn {
542 flex: 1;
543 text-align: center;
544 font-size: 0.8em;
545 padding: 8px 6px;
546 }
547}
548
549@media (max-width: 480px) {
550 .sort-buttons {
551 gap: 4px;
552 }
553 .sort-btn {
554 padding: 10px;
555 }
556}好啦!现在就完美了!