Skip to content

Commit e18e226

Browse files
authored
Merge pull request #332 from gao-jian-bin/feat/default-nested-comments
feat: 新增默认树形评论开关,并添加到功能点设置第16项
2 parents 7fac667 + f9f2c5f commit e18e226

2 files changed

Lines changed: 269 additions & 0 deletions

File tree

entrypoints/App.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@
146146
<MenuViewOwnReply :sort="14" v-model="settingData.checked44" v-show="matchesSearch('查看话题内自己回复的楼层数')"/>
147147
<!-- 是否自动打开外链(请谨慎操作,避免打开恶意链接) -->
148148
<MenuAutoOpenLink :sort="15" v-model="settingData.checked58" v-show="matchesSearch('自动打开外链')"/>
149+
<!-- 默认树形评论 -->
150+
<MenuNestedComments :sort="16" v-model="settingData.checked58_nested" v-show="matchesSearch('默认树形评论')"/>
149151

150152
<div class="group-line">编辑器设置</div>
151153
<!-- 编辑器切换 ja 字体 -->
@@ -303,6 +305,7 @@ import MenuAutoLoadTopic from "./SettingComponents/BasicSettings/MenuAutoLoadTop
303305
import MenuViewHistoryRead from "./SettingComponents/BasicSettings/MenuViewHistoryRead.vue";
304306
import MenuObsidianCallouts from "./SettingComponents/BasicSettings/MenuObsidianCallouts.vue";
305307
import MenuAutoOpenLink from "./SettingComponents/BasicSettings/MenuAutoOpenLink.vue";
308+
import MenuNestedComments from "./SettingComponents/BasicSettings/MenuNestedComments.vue";
306309
307310
// 自定义文字
308311
import MenuOtherCss from "./SettingComponents/CustomText/MenuOtherCss.vue";
@@ -441,6 +444,7 @@ export default {
441444
ViewHistoryList,
442445
MenuObsidianCallouts,
443446
MenuAutoOpenLink,
447+
MenuNestedComments,
444448
},
445449
data() {
446450
return {
@@ -527,6 +531,7 @@ export default {
527531
checked55: false,
528532
checked56: false,
529533
checked57: false,
534+
checked58_nested: false,
530535
removePostavatarData: {
531536
enable: false,
532537
showAuthor: false,
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
<template>
2+
<div class="item">
3+
<div class="tit">{{ sort }}. 默认树形评论(打开帖子时自动使用树形评论模式)</div>
4+
<input type="checkbox" :checked="modelValue" @change="$emit('update:modelValue', $event.target.checked)" />
5+
</div>
6+
</template>
7+
8+
<script>
9+
import $ from "jquery";
10+
11+
export default {
12+
props: ["modelValue", "sort"],
13+
emits: ["update:modelValue"],
14+
data() {
15+
return {
16+
processingUrl: false,
17+
intervalId: null,
18+
nestedStyleEl: null,
19+
};
20+
},
21+
created() {
22+
if (this.modelValue) {
23+
this.$nextTick(() => {
24+
// 处理当前URL
25+
this.redirectToNested();
26+
27+
// 开始链接监控
28+
this.startLinkMonitoring();
29+
30+
// 监听浏览器前进后退
31+
window.addEventListener("popstate", this.handlePopState);
32+
33+
// 注入顶级评论分割线样式
34+
this.injectDividerStyle();
35+
});
36+
} else {
37+
// 开关关闭时,如果当前是树形评论URL,还原为普通URL
38+
this.$nextTick(() => {
39+
this.redirectToNormal();
40+
});
41+
}
42+
},
43+
methods: {
44+
/**
45+
* 判断URL是否为普通话题详情页(非嵌套模式)
46+
* 匹配 /t/topic/数字 格式
47+
*/
48+
isTopicUrl(url) {
49+
return /\/t\/topic\/\d+/.test(url) && !/\/nested\//.test(url);
50+
},
51+
52+
/**
53+
* 判断URL是否为树形评论模式
54+
* 匹配 /nested/topic/数字 格式
55+
*/
56+
isNestedUrl(url) {
57+
return /\/nested\/topic\/\d+/.test(url);
58+
},
59+
60+
/**
61+
* 将普通话题URL转换为树形评论URL
62+
*/
63+
toNestedUrl(href) {
64+
const match = href.match(/\/t\/topic\/(\d+)/);
65+
if (!match) return null;
66+
const topicId = match[1];
67+
if (href.startsWith("http")) {
68+
const urlObj = new URL(href);
69+
return `${urlObj.protocol}//${urlObj.host}/nested/topic/${topicId}?sort=old`;
70+
}
71+
return `/nested/topic/${topicId}?sort=old`;
72+
},
73+
74+
/**
75+
* 将树形评论URL还原为普通话题URL
76+
*/
77+
toNormalUrl(href) {
78+
const match = href.match(/\/nested\/topic\/(\d+)/);
79+
if (!match) return null;
80+
const topicId = match[1];
81+
if (href.startsWith("http")) {
82+
const urlObj = new URL(href);
83+
return `${urlObj.protocol}//${urlObj.host}/t/topic/${topicId}`;
84+
}
85+
return `/t/topic/${topicId}`;
86+
},
87+
88+
/**
89+
* 开始监控页面上的所有链接
90+
*/
91+
startLinkMonitoring() {
92+
this.stopLinkMonitoring();
93+
94+
// 立即处理一次
95+
this.modifyTopicLinks();
96+
97+
// 设置定时监控,处理动态加载的内容
98+
this.intervalId = setInterval(() => {
99+
this.modifyTopicLinks();
100+
}, 1000);
101+
},
102+
103+
/**
104+
* 停止链接监控
105+
*/
106+
stopLinkMonitoring() {
107+
if (this.intervalId) {
108+
clearInterval(this.intervalId);
109+
this.intervalId = null;
110+
}
111+
},
112+
113+
/**
114+
* 修改页面上所有话题链接为树形评论链接
115+
*/
116+
modifyTopicLinks() {
117+
$("a").each((_, element) => {
118+
const $el = $(element);
119+
const href = $el.attr("href");
120+
121+
if (!href) return;
122+
123+
// 只处理普通话题链接(排除已经是 nested 的)
124+
if (!this.isTopicUrl(href)) return;
125+
126+
const newUrl = this.toNestedUrl(href);
127+
if (newUrl) {
128+
$el.attr("href", newUrl);
129+
}
130+
});
131+
},
132+
133+
/**
134+
* 将页面上所有树形评论链接还原为普通话题链接
135+
*/
136+
revertTopicLinks() {
137+
$("a").each((_, element) => {
138+
const $el = $(element);
139+
const href = $el.attr("href");
140+
141+
if (!href) return;
142+
143+
if (!this.isNestedUrl(href)) return;
144+
145+
const newUrl = this.toNormalUrl(href);
146+
if (newUrl) {
147+
$el.attr("href", newUrl);
148+
}
149+
});
150+
},
151+
152+
/**
153+
* 处理popstate事件(浏览器前进/后退按钮)
154+
*/
155+
handlePopState() {
156+
if (!this.processingUrl) {
157+
this.redirectToNested();
158+
}
159+
},
160+
161+
/**
162+
* 将当前页面URL重定向为树形评论模式
163+
*/
164+
redirectToNested() {
165+
if (this.processingUrl) return;
166+
this.processingUrl = true;
167+
168+
try {
169+
const currentUrl = window.location.href;
170+
171+
if (this.isTopicUrl(currentUrl)) {
172+
const newUrl = this.toNestedUrl(currentUrl);
173+
if (newUrl && currentUrl !== newUrl) {
174+
window.location.replace(newUrl);
175+
}
176+
}
177+
} finally {
178+
setTimeout(() => {
179+
this.processingUrl = false;
180+
}, 100);
181+
}
182+
},
183+
184+
/**
185+
* 将当前页面URL从树形评论模式还原为普通模式
186+
*/
187+
redirectToNormal() {
188+
if (this.processingUrl) return;
189+
this.processingUrl = true;
190+
191+
try {
192+
const currentUrl = window.location.href;
193+
194+
if (this.isNestedUrl(currentUrl)) {
195+
const newUrl = this.toNormalUrl(currentUrl);
196+
if (newUrl && currentUrl !== newUrl) {
197+
window.location.replace(newUrl);
198+
}
199+
}
200+
} finally {
201+
setTimeout(() => {
202+
this.processingUrl = false;
203+
}, 100);
204+
}
205+
},
206+
207+
/**
208+
* 注入顶级评论之间的分割线 CSS
209+
*/
210+
injectDividerStyle() {
211+
if (this.nestedStyleEl) return;
212+
const style = document.createElement("style");
213+
style.id = "linuxdo-nested-divider-style";
214+
style.textContent = `
215+
.nested-view__roots > .nested-post.--depth-0 + .nested-post.--depth-0 {
216+
border-top: 1px solid var(--primary-low, #e1e4e8);
217+
margin-top: 0.75em;
218+
padding-top: 1em;
219+
}
220+
`;
221+
document.head.appendChild(style);
222+
this.nestedStyleEl = style;
223+
},
224+
225+
/**
226+
* 移除分割线 CSS
227+
*/
228+
removeDividerStyle() {
229+
if (this.nestedStyleEl) {
230+
this.nestedStyleEl.remove();
231+
this.nestedStyleEl = null;
232+
}
233+
},
234+
},
235+
beforeUnmount() {
236+
this.stopLinkMonitoring();
237+
this.removeDividerStyle();
238+
window.removeEventListener("popstate", this.handlePopState);
239+
},
240+
beforeDestroy() {
241+
this.stopLinkMonitoring();
242+
this.removeDividerStyle();
243+
window.removeEventListener("popstate", this.handlePopState);
244+
},
245+
watch: {
246+
modelValue(newVal) {
247+
if (newVal) {
248+
this.redirectToNested();
249+
this.startLinkMonitoring();
250+
window.addEventListener("popstate", this.handlePopState);
251+
this.injectDividerStyle();
252+
} else {
253+
this.stopLinkMonitoring();
254+
window.removeEventListener("popstate", this.handlePopState);
255+
// 关闭时:还原页面链接并跳转回普通模式
256+
this.revertTopicLinks();
257+
this.removeDividerStyle();
258+
this.redirectToNormal();
259+
}
260+
},
261+
},
262+
};
263+
</script>
264+

0 commit comments

Comments
 (0)