southnote-mini-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/app.js +22 -0
  2. package/app.json +53 -0
  3. package/app.wxss +15 -0
  4. package/assets/file/badwords.js +63008 -0
  5. package/assets/file/rules.js +38 -0
  6. package/assets/images/emoji/smile001.png +0 -0
  7. package/assets/images/emoji/smile002.png +0 -0
  8. package/assets/images/emoji/smile003.png +0 -0
  9. package/assets/images/emoji/smile004.png +0 -0
  10. package/assets/images/emoji/smile005.png +0 -0
  11. package/assets/images/emoji/smile006.png +0 -0
  12. package/assets/images/emoji/smile007.png +0 -0
  13. package/assets/images/emoji/smile008.png +0 -0
  14. package/assets/images/emoji/smile009.png +0 -0
  15. package/assets/images/emoji/smile010.png +0 -0
  16. package/assets/images/emoji/smile011.png +0 -0
  17. package/assets/images/emoji/smile012.png +0 -0
  18. package/assets/images/emoji/smile013.png +0 -0
  19. package/assets/images/emoji/smile014.png +0 -0
  20. package/assets/images/emoji/smile015.png +0 -0
  21. package/assets/images/emoji/smile016.png +0 -0
  22. package/assets/images/emoji/smile017.png +0 -0
  23. package/assets/images/emoji/smile018.png +0 -0
  24. package/assets/images/emoji/smile019.png +0 -0
  25. package/assets/images/emoji/smile020.png +0 -0
  26. package/assets/images/emoji/smile021.png +0 -0
  27. package/assets/images/emoji/smile022.png +0 -0
  28. package/assets/images/emoji/smile023.png +0 -0
  29. package/assets/images/emoji/smile024.png +0 -0
  30. package/assets/images/emoji/smile025.png +0 -0
  31. package/assets/images/emoji/smile026.png +0 -0
  32. package/assets/images/emoji/smile027.png +0 -0
  33. package/assets/images/emoji/smile028.png +0 -0
  34. package/assets/images/emoji/smile029.png +0 -0
  35. package/assets/images/emoji/smile030.png +0 -0
  36. package/assets/images/emoji/smile031.png +0 -0
  37. package/assets/images/emoji/smile032.png +0 -0
  38. package/assets/images/emoji/smile033.png +0 -0
  39. package/assets/images/emoji/smile034.png +0 -0
  40. package/assets/images/emoji/smile035.png +0 -0
  41. package/assets/images/emoji/smile036.png +0 -0
  42. package/assets/images/emoji/smile037.png +0 -0
  43. package/assets/images/emoji/smile038.png +0 -0
  44. package/assets/images/emoji/smile039.png +0 -0
  45. package/assets/images/emoji/smile040.png +0 -0
  46. package/assets/images/emoji/smile041.png +0 -0
  47. package/assets/images/emoji/smile042.png +0 -0
  48. package/assets/images/emoji/smile043.png +0 -0
  49. package/assets/images/emoji/smile044.png +0 -0
  50. package/assets/images/emoji/smile045.png +0 -0
  51. package/assets/images/emoji/smile046.png +0 -0
  52. package/assets/images/emoji/smile047.png +0 -0
  53. package/assets/images/emoji/smile048.png +0 -0
  54. package/assets/images/emoji/smile049.png +0 -0
  55. package/assets/images/emoji/smile050.png +0 -0
  56. package/assets/images/emoji/smile051.png +0 -0
  57. package/assets/images/emoji/smile052.png +0 -0
  58. package/assets/images/icons/age1.png +0 -0
  59. package/assets/images/icons/age2.png +0 -0
  60. package/assets/images/icons/age3.png +0 -0
  61. package/assets/images/icons/attachment.png +0 -0
  62. package/assets/images/icons/clear.png +0 -0
  63. package/assets/images/icons/edit_input.png +0 -0
  64. package/assets/images/icons/header_placeholder.png +0 -0
  65. package/assets/images/icons/home_collect.png +0 -0
  66. package/assets/images/icons/home_collected.png +0 -0
  67. package/assets/images/icons/home_comment.png +0 -0
  68. package/assets/images/icons/home_like.png +0 -0
  69. package/assets/images/icons/home_liked.png +0 -0
  70. package/assets/images/icons/home_message.png +0 -0
  71. package/assets/images/icons/home_share.png +0 -0
  72. package/assets/images/icons/icon_more.png +0 -0
  73. package/assets/images/icons/input_game.png +0 -0
  74. package/assets/images/icons/logo.png +0 -0
  75. package/assets/images/icons/lv1.png +0 -0
  76. package/assets/images/icons/lv2.png +0 -0
  77. package/assets/images/icons/lv3.png +0 -0
  78. package/assets/images/icons/lv4.png +0 -0
  79. package/assets/images/icons/search.png +0 -0
  80. package/assets/images/icons/sex1.png +0 -0
  81. package/assets/images/icons/sex2.png +0 -0
  82. package/assets/images/icons/sign_empty.png +0 -0
  83. package/assets/images/icons/sign_login.png +0 -0
  84. package/assets/wheel/wheel_bg.png +0 -0
  85. package/assets/wheel/wheel_bg2.png +0 -0
  86. package/assets/wheel/wheel_fore.png +0 -0
  87. package/assets/wheel/wheel_to.png +0 -0
  88. package/components/bottomSheet/bottomSheet.js +473 -0
  89. package/components/bottomSheet/bottomSheet.json +3 -0
  90. package/components/bottomSheet/bottomSheet.wxml +73 -0
  91. package/components/bottomSheet/bottomSheet.wxss +238 -0
  92. package/components/experience-item/experience-item.js +105 -0
  93. package/components/experience-item/experience-item.json +4 -0
  94. package/components/experience-item/experience-item.wxml +39 -0
  95. package/components/experience-item/experience-item.wxss +110 -0
  96. package/components/grid-item/grid-item.js +8 -0
  97. package/components/grid-item/grid-item.json +4 -0
  98. package/components/grid-item/grid-item.wxml +62 -0
  99. package/components/grid-item/grid-item.wxss +191 -0
  100. package/components/post-item/post-item.js +1085 -0
  101. package/components/post-item/post-item.json +4 -0
  102. package/components/post-item/post-item.wxml +427 -0
  103. package/components/post-item/post-item.wxss +525 -0
  104. package/components/wiki-renderer/wiki-renderer.js +636 -0
  105. package/components/wiki-renderer/wiki-renderer.json +12 -0
  106. package/components/wiki-renderer/wiki-renderer.wxml +150 -0
  107. package/components/wiki-renderer/wiki-renderer.wxss +412 -0
  108. package/components/wiki-tabs/wiki-tabs.js +52 -0
  109. package/components/wiki-tabs/wiki-tabs.json +3 -0
  110. package/components/wiki-tabs/wiki-tabs.wxml +28 -0
  111. package/components/wiki-tabs/wiki-tabs.wxss +43 -0
  112. package/index.js +49 -0
  113. package/package.json +35 -0
  114. package/pages/addGame/addGame.js +454 -0
  115. package/pages/addGame/addGame.json +3 -0
  116. package/pages/addGame/addGame.wxml +71 -0
  117. package/pages/addGame/addGame.wxss +110 -0
  118. package/pages/addSubject/addSubject.js +199 -0
  119. package/pages/addSubject/addSubject.json +4 -0
  120. package/pages/addSubject/addSubject.wxml +78 -0
  121. package/pages/addSubject/addSubject.wxss +85 -0
  122. package/pages/commonLogin/commonLogin.js +269 -0
  123. package/pages/commonLogin/commonLogin.json +4 -0
  124. package/pages/commonLogin/commonLogin.wxml +57 -0
  125. package/pages/commonLogin/commonLogin.wxss +177 -0
  126. package/pages/copyOfficial/copyOfficial.js +25 -0
  127. package/pages/copyOfficial/copyOfficial.json +3 -0
  128. package/pages/copyOfficial/copyOfficial.wxml +22 -0
  129. package/pages/copyOfficial/copyOfficial.wxss +49 -0
  130. package/pages/demo/demo.js +23 -0
  131. package/pages/demo/demo.json +3 -0
  132. package/pages/demo/demo.wxml +8 -0
  133. package/pages/demo/demo.wxss +24 -0
  134. package/pages/game/game.js +1254 -0
  135. package/pages/game/game.json +8 -0
  136. package/pages/game/game.wxml +371 -0
  137. package/pages/game/game.wxss +672 -0
  138. package/pages/home/home.js +665 -0
  139. package/pages/home/home.json +10 -0
  140. package/pages/home/home.wxml +115 -0
  141. package/pages/home/home.wxss +231 -0
  142. package/pages/lottery-winners/lottery-winners.js +60 -0
  143. package/pages/lottery-winners/lottery-winners.json +4 -0
  144. package/pages/lottery-winners/lottery-winners.wxml +53 -0
  145. package/pages/lottery-winners/lottery-winners.wxss +127 -0
  146. package/pages/message/messageCommentsAndAt/messageCommentsAndAt.js +178 -0
  147. package/pages/message/messageCommentsAndAt/messageCommentsAndAt.json +5 -0
  148. package/pages/message/messageCommentsAndAt/messageCommentsAndAt.wxml +93 -0
  149. package/pages/message/messageCommentsAndAt/messageCommentsAndAt.wxss +177 -0
  150. package/pages/message/messageEntire/messageEntire.js +112 -0
  151. package/pages/message/messageEntire/messageEntire.json +4 -0
  152. package/pages/message/messageEntire/messageEntire.wxml +44 -0
  153. package/pages/message/messageEntire/messageEntire.wxss +88 -0
  154. package/pages/message/messageLikeAndCollect/messageLikeAndCollect.js +147 -0
  155. package/pages/message/messageLikeAndCollect/messageLikeAndCollect.json +5 -0
  156. package/pages/message/messageLikeAndCollect/messageLikeAndCollect.wxml +84 -0
  157. package/pages/message/messageLikeAndCollect/messageLikeAndCollect.wxss +166 -0
  158. package/pages/message/messageNewFans/messageNewFans.js +139 -0
  159. package/pages/message/messageNewFans/messageNewFans.json +5 -0
  160. package/pages/message/messageNewFans/messageNewFans.wxml +35 -0
  161. package/pages/message/messageNewFans/messageNewFans.wxss +85 -0
  162. package/pages/officialWebview/officialWebview.js +13 -0
  163. package/pages/officialWebview/officialWebview.json +3 -0
  164. package/pages/officialWebview/officialWebview.wxml +2 -0
  165. package/pages/officialWebview/officialWebview.wxss +1 -0
  166. package/pages/post/post.js +1856 -0
  167. package/pages/post/post.json +6 -0
  168. package/pages/post/post.wxml +694 -0
  169. package/pages/post/post.wxss +1003 -0
  170. package/pages/post-editor/post-editor.js +465 -0
  171. package/pages/post-editor/post-editor.json +4 -0
  172. package/pages/post-editor/post-editor.wxml +63 -0
  173. package/pages/post-editor/post-editor.wxss +195 -0
  174. package/pages/postDialog/postDialog.js +560 -0
  175. package/pages/postDialog/postDialog.json +6 -0
  176. package/pages/postDialog/postDialog.wxml +377 -0
  177. package/pages/postDialog/postDialog.wxss +979 -0
  178. package/pages/report-category/report-category.js +29 -0
  179. package/pages/report-category/report-category.json +4 -0
  180. package/pages/report-category/report-category.wxml +14 -0
  181. package/pages/report-category/report-category.wxss +43 -0
  182. package/pages/report-form/report-form.js +65 -0
  183. package/pages/report-form/report-form.json +3 -0
  184. package/pages/report-form/report-form.wxml +11 -0
  185. package/pages/report-form/report-form.wxss +44 -0
  186. package/pages/sevenDaySignDetail/sevenDaySignDetail.js +273 -0
  187. package/pages/sevenDaySignDetail/sevenDaySignDetail.json +3 -0
  188. package/pages/sevenDaySignDetail/sevenDaySignDetail.wxml +82 -0
  189. package/pages/sevenDaySignDetail/sevenDaySignDetail.wxss +173 -0
  190. package/pages/topic/topic.js +266 -0
  191. package/pages/topic/topic.json +6 -0
  192. package/pages/topic/topic.wxml +77 -0
  193. package/pages/topic/topic.wxss +162 -0
  194. package/pages/user/user.js +939 -0
  195. package/pages/user/user.json +5 -0
  196. package/pages/user/user.wxml +179 -0
  197. package/pages/user/user.wxss +340 -0
  198. package/pages/userDatum/userDatum.js +24 -0
  199. package/pages/userDatum/userDatum.json +4 -0
  200. package/pages/userDatum/userDatum.wxml +36 -0
  201. package/pages/userDatum/userDatum.wxss +41 -0
  202. package/pages/userGameLib/userGameLib.js +105 -0
  203. package/pages/userGameLib/userGameLib.json +4 -0
  204. package/pages/userGameLib/userGameLib.wxml +42 -0
  205. package/pages/userGameLib/userGameLib.wxss +119 -0
  206. package/pages/webview/webview.js +25 -0
  207. package/pages/webview/webview.json +3 -0
  208. package/pages/webview/webview.wxml +22 -0
  209. package/pages/webview/webview.wxss +50 -0
  210. package/pages/wheelActivivty/wheelActivivty.js +492 -0
  211. package/pages/wheelActivivty/wheelActivivty.json +8 -0
  212. package/pages/wheelActivivty/wheelActivivty.wxml +131 -0
  213. package/pages/wheelActivivty/wheelActivivty.wxss +459 -0
  214. package/pages/wiki/wiki.js +138 -0
  215. package/pages/wiki/wiki.json +6 -0
  216. package/pages/wiki/wiki.wxml +20 -0
  217. package/pages/wiki/wiki.wxss +44 -0
  218. package/request/JKRequest.js +177 -0
  219. package/services/home.js +603 -0
  220. package/towxml/config.js +291 -0
  221. package/towxml/decode.js +35 -0
  222. package/towxml/decode.json +11 -0
  223. package/towxml/decode.wxml +137 -0
  224. package/towxml/decode.wxss +0 -0
  225. package/towxml/img/img.js +98 -0
  226. package/towxml/img/img.json +3 -0
  227. package/towxml/img/img.wxml +1 -0
  228. package/towxml/img/img.wxss +0 -0
  229. package/towxml/index.js +19 -0
  230. package/towxml/latex/latex.js +53 -0
  231. package/towxml/latex/latex.json +5 -0
  232. package/towxml/latex/latex.wxml +1 -0
  233. package/towxml/latex/latex.wxss +0 -0
  234. package/towxml/parse/highlight/highlight.js +1 -0
  235. package/towxml/parse/highlight/index.js +8 -0
  236. package/towxml/parse/highlight/languages/bash.js +111 -0
  237. package/towxml/parse/highlight/languages/c-like.js +236 -0
  238. package/towxml/parse/highlight/languages/c.js +22 -0
  239. package/towxml/parse/highlight/languages/css.js +132 -0
  240. package/towxml/parse/highlight/languages/dart.js +134 -0
  241. package/towxml/parse/highlight/languages/go.js +63 -0
  242. package/towxml/parse/highlight/languages/htmlbars.js +80 -0
  243. package/towxml/parse/highlight/languages/java.js +125 -0
  244. package/towxml/parse/highlight/languages/javascript.js +265 -0
  245. package/towxml/parse/highlight/languages/json.js +52 -0
  246. package/towxml/parse/highlight/languages/less.js +148 -0
  247. package/towxml/parse/highlight/languages/nginx.js +101 -0
  248. package/towxml/parse/highlight/languages/php.js +161 -0
  249. package/towxml/parse/highlight/languages/python-repl.js +29 -0
  250. package/towxml/parse/highlight/languages/python.js +131 -0
  251. package/towxml/parse/highlight/languages/scss.js +122 -0
  252. package/towxml/parse/highlight/languages/shell.js +22 -0
  253. package/towxml/parse/highlight/languages/typescript.js +215 -0
  254. package/towxml/parse/highlight/languages/xml.js +139 -0
  255. package/towxml/parse/highlight/style/github.wxss +99 -0
  256. package/towxml/parse/highlight/style/monokai.wxss +70 -0
  257. package/towxml/parse/index.js +118 -0
  258. package/towxml/parse/markdown/index.js +67 -0
  259. package/towxml/parse/markdown/markdown.js +4 -0
  260. package/towxml/parse/markdown/plugins/echarts.js +11 -0
  261. package/towxml/parse/markdown/plugins/emoji.js +2 -0
  262. package/towxml/parse/markdown/plugins/ins.js +1 -0
  263. package/towxml/parse/markdown/plugins/latex.js +158 -0
  264. package/towxml/parse/markdown/plugins/mark.js +1 -0
  265. package/towxml/parse/markdown/plugins/sub.js +1 -0
  266. package/towxml/parse/markdown/plugins/sup.js +1 -0
  267. package/towxml/parse/markdown/plugins/todo.js +147 -0
  268. package/towxml/parse/markdown/plugins/yuml.js +20 -0
  269. package/towxml/parse/parse2/Parser.js +492 -0
  270. package/towxml/parse/parse2/Tokenizer.js +903 -0
  271. package/towxml/parse/parse2/domelementtype/index.js +55 -0
  272. package/towxml/parse/parse2/domhandler/index.js +145 -0
  273. package/towxml/parse/parse2/domhandler/node.js +474 -0
  274. package/towxml/parse/parse2/entities/decode.js +179 -0
  275. package/towxml/parse/parse2/entities/decode_codepoint.js +60 -0
  276. package/towxml/parse/parse2/entities/encode.js +77 -0
  277. package/towxml/parse/parse2/entities/escape.js +112 -0
  278. package/towxml/parse/parse2/entities/generated/decode-data-html.js +9 -0
  279. package/towxml/parse/parse2/entities/generated/decode-data-xml.js +9 -0
  280. package/towxml/parse/parse2/entities/generated/encode-html.js +12 -0
  281. package/towxml/parse/parse2/entities/index.js +137 -0
  282. package/towxml/parse/parse2/index.js +8 -0
  283. package/towxml/style/main.wxss +419 -0
  284. package/towxml/style/theme/dark.wxss +73 -0
  285. package/towxml/style/theme/light.wxss +63 -0
  286. package/towxml/table/table.js +11 -0
  287. package/towxml/table/table.json +6 -0
  288. package/towxml/table/table.wxml +24 -0
  289. package/towxml/table/table.wxss +0 -0
  290. package/towxml/todogroup/todogroup.js +20 -0
  291. package/towxml/todogroup/todogroup.json +6 -0
  292. package/towxml/todogroup/todogroup.wxml +12 -0
  293. package/towxml/todogroup/todogroup.wxss +3 -0
  294. package/towxml/towxml.js +22 -0
  295. package/towxml/towxml.json +6 -0
  296. package/towxml/towxml.wxml +5 -0
  297. package/towxml/towxml.wxss +8 -0
  298. package/towxml/yuml/yuml.js +51 -0
  299. package/towxml/yuml/yuml.json +5 -0
  300. package/towxml/yuml/yuml.wxml +5 -0
  301. package/towxml/yuml/yuml.wxss +0 -0
  302. package/utils/auth.js +17 -0
  303. package/utils/base64.js +67 -0
  304. package/utils/compressImage.js +41 -0
  305. package/utils/exp.js +49 -0
  306. package/utils/formatPost.js +18 -0
  307. package/utils/parseContent.js +191 -0
  308. package/utils/query-select.js +9 -0
  309. package/utils/sensitive.js +82 -0
@@ -0,0 +1,1856 @@
1
+ import {
2
+ emojiMap
3
+ } from "../../components/post-item/post-item"
4
+ import {
5
+ queryPostInfo,
6
+ queryPostComments,
7
+ queryPostLikes,
8
+ queryPostShare,
9
+ followUser,
10
+ likePost,
11
+ collectPost,
12
+ votePost,
13
+ queryMainComments,
14
+ deleteContent,
15
+ queryPostVideo
16
+ } from "../../services/home";
17
+ import formatUTC from "../../utils/formatPost.js"
18
+ import {
19
+ getLevelByExp,
20
+ getLevelImg
21
+ } from '../../utils/exp';
22
+ import {
23
+ checkLogin
24
+ } from "../../utils/auth"
25
+
26
+ Page({
27
+ data: {
28
+ item: {}, // 当前帖子详情
29
+ itemDetial: {},
30
+ postId: 0,
31
+ contentParts: [], // 帖子正文
32
+ commentedContentParts: [], // 评论格式化内容
33
+ sharedContentParts: [],
34
+ comments: [], // 评论列表
35
+ likes: [], //赞列表
36
+ shares: [], //转发列表
37
+ showFull: false,
38
+ needShowReadMore: false,
39
+ isLongImage: false,
40
+ isSuperLongImage: false, //是否是超长图
41
+
42
+ isHorizonImage: false, //是否是长>宽的图
43
+ active: 1,
44
+ sortValue: 0, // 默认选中第一个(按热度)
45
+ sortOptions: [{
46
+ text: '按热度',
47
+ value: 0
48
+ },
49
+ {
50
+ text: '按时间',
51
+ value: 1
52
+ }
53
+ ],
54
+
55
+ sharesPage: 1,
56
+ sharesHasMore: true,
57
+ commentsPage: 1,
58
+ commentsHasMore: true,
59
+ likesPage: 1,
60
+ likesHasMore: true,
61
+ isLoadingMore: false, // 防止重复请求
62
+ isCommentIn: false, //是否是点击评论跳转进来的
63
+
64
+ showSheet: false, //评论弹窗
65
+ originPost: {},
66
+ currentReplyComment: null,
67
+ typeName: '单选',
68
+
69
+ showMenuSheet: false, //菜单弹窗
70
+ loading: true,
71
+ },
72
+
73
+ getVoteTitle(isVoted, vote) {
74
+ const typeName = vote.max_choices === 1 ? "单选" : `${vote.max_choices}选`;
75
+ this.setData({
76
+ typeName: typeName
77
+ })
78
+
79
+ if (Date.now() > vote.end_time * 1000) {
80
+ return `投票已结束`;
81
+ }
82
+
83
+ return isVoted ? `已投票` : `参与投票`;
84
+ },
85
+
86
+
87
+ initVoteData(vote) {
88
+ if (!vote) return;
89
+
90
+ const userVotes = vote.user_votes || [];
91
+ const isVoted = userVotes.length > 0;
92
+ const isVoteEnded = Date.now() > vote.end_time * 1000;
93
+
94
+
95
+ // 处理选项
96
+ const voteOptions = vote.options.map(o => ({
97
+ ...o,
98
+ _selected: userVotes.includes(o.id),
99
+ vote_count: o.vote_count || 0
100
+ }));
101
+
102
+ const hasAnyImage = voteOptions.some(o => o.option_image); // 假设选项图片字段是 o.image
103
+
104
+
105
+ // 如果已投票,计算百分比
106
+ if (isVoted || isVoteEnded) {
107
+ const total = voteOptions.reduce((sum, o) => sum + o.vote_count, 0);
108
+ voteOptions.forEach(o => {
109
+ o.percent = total > 0 ? ((o.vote_count / total) * 100).toFixed(1) : '0.0';
110
+ });
111
+ }
112
+
113
+ this.setData({
114
+ voteOptions,
115
+ isVoted,
116
+ selectedVotes: [],
117
+ isVoteEnded: Date.now() > vote.end_time * 1000,
118
+ voteEndTime: vote.end_time ? this.formatTime(vote.end_time) : null,
119
+ voteTitle: this.getVoteTitle(isVoted, vote),
120
+ showVoteImages: hasAnyImage
121
+
122
+ });
123
+ },
124
+
125
+ initLotteryData(lottery) {
126
+ if (!lottery) return;
127
+
128
+ const now = Date.now() / 1000;
129
+ const isEnded = now > lottery.end_time;
130
+
131
+ const conditionTextMap = {
132
+ 1: '评论此贴',
133
+ 2: '转发此贴',
134
+ 3: '评论并转发此贴',
135
+ 4: '发表话题',
136
+ 5: '关注作者',
137
+ 6: '关注游戏'
138
+ };
139
+
140
+ const lotteryConditions = (lottery.conditions || []).map((cond, idx) => {
141
+ let parts = [];
142
+
143
+ switch (cond.condition_type) {
144
+ case 1:
145
+ case 2:
146
+ case 3:
147
+ case 5:
148
+ parts.push({
149
+ text: conditionTextMap[cond.condition_type],
150
+ color: '#333',
151
+ clickable: false
152
+ });
153
+ break;
154
+
155
+ case 4: // 发表话题
156
+ parts.push({
157
+ text: '发表话题',
158
+ color: '#333',
159
+ clickable: false
160
+ });
161
+ parts.push({
162
+ text: `#${cond.condition_value}#`,
163
+ color: '#406ce3',
164
+ clickable: true,
165
+ data: {
166
+ type: 'topic',
167
+ name: cond.condition_value
168
+ }
169
+ });
170
+ break;
171
+
172
+ case 6: // 关注游戏
173
+ parts.push({
174
+ text: '关注游戏',
175
+ color: '#333',
176
+ clickable: false
177
+ });
178
+
179
+
180
+ parts.push({
181
+ text: `$${cond.condition_value.game_name}`,
182
+ color: '#406ce3',
183
+ clickable: true,
184
+ data: {
185
+ type: 'game',
186
+ id: cond.condition_value.game_id
187
+ }
188
+ });
189
+ break;
190
+ }
191
+
192
+ return {
193
+ index: idx + 1,
194
+ parts
195
+ };
196
+ });
197
+
198
+ this.setData({
199
+ lotteryInfo: {
200
+ ...lottery,
201
+ isEnded,
202
+ winnerCount: lottery.winner_count,
203
+ conditions: lotteryConditions,
204
+ endTimeText: this.formatTime(lottery.end_time),
205
+ },
206
+ isLotteryEnded: Date.now() > lottery.end_time * 1000
207
+ });
208
+ },
209
+
210
+ // 点击抽奖条件(话题/游戏)
211
+ onLotteryConditionTap(e) {
212
+ const {
213
+ type,
214
+ name,
215
+ id
216
+ } = e.currentTarget.dataset;
217
+ if (!type) return;
218
+
219
+ if (type === 'topic') {
220
+ wx.navigateTo({
221
+ url: `/pages/topic/topic?topic_name=${encodeURIComponent(name)}`
222
+ });
223
+ } else if (type === 'game') {
224
+ wx.navigateTo({
225
+ url: `/pages/game/game?id=${id}`
226
+ });
227
+ }
228
+ },
229
+
230
+ // 查看中奖名单
231
+ // 查看中奖名单
232
+ onViewWinners() {
233
+ if (!checkLogin()) return;
234
+ const postItem = this.data.item;
235
+
236
+ // 构造抽奖公示页需要的最小帖子对象
237
+ const post = {
238
+ id: postItem.post.id,
239
+ user_id: postItem.user.id, // 发帖人 id(用于判断发起人)
240
+ nickname: postItem.user.nickname, // 发帖人昵称
241
+ avatar: postItem.user.avatar, // 发帖人头像
242
+ cover: postItem.post.imageArray?.[0] || '', // 帖子图(第一张)
243
+ content: postItem.post.content // 帖子内容
244
+ };
245
+
246
+ wx.navigateTo({
247
+ url: `/pages/lottery-winners/lottery-winners?post=${encodeURIComponent(
248
+ JSON.stringify(post)
249
+ )}`
250
+ });
251
+ },
252
+
253
+ // 帖子详情页分享逻辑
254
+ onShareAppMessage() {
255
+ const {
256
+ item,
257
+ postId
258
+ } = this.data;
259
+
260
+ // 取帖子标题或默认文案
261
+ const title = item?.post?.title || '查看帖子详情';
262
+
263
+ return {
264
+ title,
265
+ // 分享路径携带帖子 id 参数
266
+ path: `/pages/post/post?id=${postId}` // 携带用户ID
267
+ };
268
+ },
269
+ // tab切换
270
+ onChange(event) {
271
+ const {
272
+ index
273
+ } = event.detail
274
+ this.setData({
275
+ active: index
276
+ })
277
+ },
278
+
279
+ async onSortChange(e) {
280
+ console.log(e.detail)
281
+ this.setData({
282
+ sortValue: e.detail
283
+ })
284
+
285
+ // TODO: 根据选择重新请求或排序 comments
286
+
287
+ const commentsRes = await queryPostComments(this.data.postId, 1, e.detail === 0 ? 2 : 1)
288
+ // 格式化评论
289
+ const comments = this.formatComments(commentsRes.data)
290
+ this.setData({
291
+ comments
292
+ })
293
+ },
294
+
295
+ onLoad(options) {
296
+ const postId = options.id; // 从路由参数拿帖子 id
297
+ const isCommentIn = options.isCommentIn
298
+ this.setData({
299
+ postId,
300
+ isCommentIn
301
+ })
302
+ this.fetchPostDetail(postId).then(res => {
303
+ const currentUserId = wx.getStorageSync('appuid'); // 当前登录用户id
304
+ const isOwner = this.data.item.user.id === currentUserId;
305
+ this.setData({
306
+ isOwner
307
+ })
308
+ if (this.data.isCommentIn) {
309
+ this.setData({
310
+ showSheet: true,
311
+ originPost: this.data.item,
312
+ commentData: null,
313
+ })
314
+
315
+ }
316
+ });
317
+
318
+
319
+
320
+
321
+ },
322
+
323
+ onFirstComment() {
324
+ if (!checkLogin()) return; // 🔥 登录校验
325
+
326
+ this.setData({
327
+ showSheet: true,
328
+ originPost: this.data.item,
329
+ commentData: null
330
+ })
331
+ },
332
+ onLike(e) {
333
+ if (!checkLogin()) return; // 🔥 登录校验
334
+
335
+
336
+ const post = this.data.item.post;
337
+ const isLiked = post.is_like === 1;
338
+ const newLikeStatus = isLiked ? 0 : 1;
339
+ const newLikeCount = isLiked ? post.like_count - 1 : post.like_count + 1;
340
+
341
+ // 2️⃣ 前端立即更新状态
342
+ this.setData({
343
+ 'item.post.is_like': newLikeStatus,
344
+ 'item.post.like_count': newLikeCount
345
+ });
346
+
347
+ // 3️⃣ 动画效果(缩放+回弹)
348
+ const animation = wx.createAnimation({
349
+ duration: 200,
350
+ timingFunction: 'ease-in-out'
351
+ });
352
+ animation.scale(1.5).step();
353
+ animation.scale(1.0).step();
354
+
355
+ this.setData({
356
+ likeAnimation: animation.export()
357
+ });
358
+
359
+ // 4️⃣ 调用后端接口(type: 1点赞, 2取消点赞)
360
+ likePost(post.id, newLikeStatus === 1 ? 1 : 2)
361
+ .then(res => {
362
+ if (res.code !== 0) {
363
+ // ❌ 接口失败,回滚前端状态
364
+ this.setData({
365
+ 'item.post.is_like': isLiked ? 1 : 0,
366
+ 'item.post.like_count': post.like_count
367
+ });
368
+ wx.showToast({
369
+ title: res.message || '操作失败',
370
+ icon: 'none'
371
+ });
372
+ } else {
373
+ // ✅ 接口成功,跨页面同步数据
374
+ const pages = getCurrentPages();
375
+ pages.forEach(page => {
376
+ // 首页更新 posts
377
+ if (page.route === 'pages/home/home') {
378
+ const posts = page.data.posts.map(p => {
379
+ if (p.post.id === post.id) {
380
+ p.post.is_like = newLikeStatus;
381
+ p.post.like_count = newLikeCount;
382
+ }
383
+ return p;
384
+ });
385
+ page.setData({
386
+ posts
387
+ });
388
+ }
389
+
390
+ // 用户详情页更新 userPosts
391
+ if (page.route === 'pages/user/user') {
392
+ if (Array.isArray(page.data.userPosts)) {
393
+ const userPosts = page.data.userPosts.map(p => {
394
+ if (p.post.id === post.id) {
395
+ p.post.is_like = newLikeStatus;
396
+ p.post.like_count = newLikeCount;
397
+ }
398
+ return p;
399
+ });
400
+ page.setData({
401
+ userPosts
402
+ });
403
+ }
404
+ }
405
+
406
+ // 帖子详情页更新 item
407
+ if (page.route === 'pages/post/post' && page.data.postId === post.id) {
408
+ page.setData({
409
+ 'item.post.is_like': newLikeStatus,
410
+ 'item.post.like_count': newLikeCount
411
+ });
412
+ }
413
+ });
414
+ }
415
+ })
416
+ .catch(err => {
417
+ // ❌ 网络/接口异常
418
+ this.setData({
419
+ 'item.post.is_like': isLiked ? 1 : 0,
420
+ 'item.post.like_count': post.like_count
421
+ });
422
+ wx.showToast({
423
+ title: '网络错误,操作失败',
424
+ icon: 'none'
425
+ });
426
+ });
427
+ },
428
+
429
+ onCollect(e) {
430
+ if (!checkLogin()) return; // 🔥 登录校验
431
+
432
+ const post = this.data.item.post;
433
+ const isCollected = post.is_collect === 1;
434
+ const newCollectStatus = isCollected ? 0 : 1;
435
+ const newCollectCount = isCollected ? post.collect_count - 1 : post.collect_count + 1;
436
+
437
+ // 1️⃣ 前端立即更新状态和收藏数
438
+ this.setData({
439
+ 'item.post.is_collect': newCollectStatus,
440
+ 'item.post.collect_count': newCollectCount
441
+ });
442
+
443
+ // 2️⃣ 动画效果(可选)
444
+ const animation = wx.createAnimation({
445
+ duration: 200,
446
+ timingFunction: 'ease-in-out'
447
+ });
448
+ animation.scale(1.5).step();
449
+ animation.scale(1.0).step();
450
+ this.setData({
451
+ collectAnimation: animation.export()
452
+ });
453
+
454
+ // 3️⃣ 调用后端接口(type: 1收藏, 2取消收藏)
455
+ collectPost(post.id, newCollectStatus === 1 ? 1 : 2)
456
+ .then(res => {
457
+ if (res.code !== 0) {
458
+ // ❌ 接口失败,回滚前端状态和数量
459
+ this.setData({
460
+ 'item.post.is_collect': isCollected,
461
+ 'item.post.collect_count': post.collect_count
462
+ });
463
+ wx.showToast({
464
+ title: res.message || '操作失败',
465
+ icon: 'none'
466
+ });
467
+ } else {
468
+ // ✅ 接口成功,跨页面同步数据
469
+ const pages = getCurrentPages();
470
+ pages.forEach(page => {
471
+ // 首页更新 posts
472
+ if (page.route === 'pages/home/home') {
473
+ const posts = page.data.posts.map(p => {
474
+ if (p.post.id === post.id) {
475
+ p.post.is_collect = newCollectStatus;
476
+ p.post.collect_count = newCollectCount;
477
+ }
478
+ return p;
479
+ });
480
+ page.setData({
481
+ posts
482
+ });
483
+ }
484
+
485
+ // 用户详情页更新 userPosts
486
+ if (page.route === 'pages/user/user') {
487
+ if (Array.isArray(page.data.userPosts)) {
488
+ const userPosts = page.data.userPosts.map(p => {
489
+ if (p.post.id === post.id) {
490
+ p.post.is_collect = newCollectStatus;
491
+ p.post.collect_count = newCollectCount;
492
+ }
493
+ return p;
494
+ });
495
+ page.setData({
496
+ userPosts
497
+ });
498
+ }
499
+ }
500
+
501
+ // 帖子详情页更新 item
502
+ if (page.route === 'pages/post/post' && page.data.postId === post.id) {
503
+ page.setData({
504
+ 'item.post.is_collect': newCollectStatus,
505
+ 'item.post.collect_count': newCollectCount
506
+ });
507
+ }
508
+ });
509
+
510
+ wx.showToast({
511
+ title: newCollectStatus === 1 ? '收藏成功' : '取消收藏',
512
+ icon: 'success'
513
+ });
514
+ }
515
+ })
516
+ .catch(err => {
517
+ // ❌ 网络/接口异常,回滚
518
+ this.setData({
519
+ 'item.post.is_collect': isCollected,
520
+ 'item.post.collect_count': post.collect_count
521
+ });
522
+ wx.showToast({
523
+ title: '网络错误,操作失败',
524
+ icon: 'none'
525
+ });
526
+ });
527
+ },
528
+
529
+ async onCommentSuccess(e) {
530
+ const {
531
+ postId,
532
+ mode,
533
+ content,
534
+ images
535
+ } = e.detail;
536
+
537
+ const commentsRes = await queryPostComments(this.data.postId, 1, this.data.sortValue === 0 ? 2 : 1);
538
+ const comments = this.formatComments(commentsRes.data);
539
+
540
+ // ✅ 当前页面更新评论
541
+ const newCount = (this.data.item.post.comment_count || 0) + 1;
542
+ this.setData({
543
+ 'item.post.comment_count': newCount,
544
+ comments
545
+ });
546
+
547
+ // ✅ 遍历页面栈同步修改
548
+ const pages = getCurrentPages();
549
+ const currentPage = pages[pages.length - 1];
550
+
551
+ pages.forEach(page => {
552
+ if (page === currentPage) return;
553
+ // 首页
554
+ if (page.route === 'pages/home/home') {
555
+ const updatedPosts = page.data.posts.map(p => {
556
+ if (p.post.id === postId) {
557
+ p.post.comment_count = (p.post.comment_count || 0) + 1;
558
+ }
559
+ return p;
560
+ });
561
+ page.setData({
562
+ posts: updatedPosts
563
+ });
564
+ }
565
+
566
+ // 用户详情页
567
+ if (page.route === 'pages/user/user') {
568
+ const updatedPosts = page.data.userPosts?.map(p => {
569
+ if (p.post.id === postId) {
570
+ p.post.comment_count = (p.post.comment_count || 0) + 1;
571
+ }
572
+ return p;
573
+ }) || [];
574
+ page.setData({
575
+ userPosts: updatedPosts
576
+ });
577
+ }
578
+
579
+ // 其他帖子详情页(如果嵌套)
580
+ if (page.route === 'pages/post/post' && page.data.postId === postId) {
581
+ page.setData({
582
+ 'item.post.comment_count': (page.data.item.post.comment_count || 0) + 1
583
+ });
584
+ }
585
+ });
586
+ },
587
+
588
+ async fetchPostDetail(postId) {
589
+ try {
590
+ const [postRes, commentsRes, likesRes, sharesRes] = await Promise.all([
591
+ queryPostInfo(postId),
592
+ queryPostComments(postId, 1, 2),
593
+ queryPostLikes(postId, 1),
594
+ queryPostShare(postId, 1),
595
+ ])
596
+
597
+ const item = this.formatPost(postRes.data);
598
+ // 处理投票相关信息
599
+ this.initVoteData(item.post.vote);
600
+ this.initLotteryData(item.post.lottery);
601
+
602
+
603
+ const comments = this.formatComments(commentsRes.data);
604
+ const shares = this.formatShares(sharesRes.data);
605
+
606
+ // ✅ 处理 commented 转换
607
+ let commentedContentParts = [];
608
+ const commentedArr = postRes.data?.commented;
609
+ if (Array.isArray(commentedArr) && commentedArr.length > 0) {
610
+ const commented = commentedArr[0];
611
+ const parts = [];
612
+
613
+ if (commented.user?.nickname) {
614
+ parts.push({
615
+ text: `@${commented.user.nickname}`,
616
+ color: '#406ce3',
617
+ type: 'at',
618
+ userId: commented.user?.id || ''
619
+ });
620
+ }
621
+
622
+ if (commented.post?.reply_nickname) {
623
+ parts.push({
624
+ text: ' 回复 ',
625
+ color: '#333'
626
+ });
627
+ parts.push({
628
+ text: `@${commented.post.reply_nickname}`,
629
+ color: '#406ce3',
630
+ type: 'at',
631
+ userId: commented.post?.reply_user_id || ''
632
+ });
633
+ }
634
+
635
+ if (commented.post?.status !== 3) {
636
+ parts.push({
637
+ text: ': ',
638
+ color: '#333'
639
+ });
640
+ }
641
+
642
+ if (commented.post?.content) {
643
+ parts.push(...this.parseContent(commented.post.content));
644
+ }
645
+ if (commented.post?.status === 3) {
646
+ parts.push({
647
+ text: '[原文已被删除]',
648
+ color: '#999',
649
+ type: 'deleted'
650
+ });
651
+ }
652
+
653
+ if (commented.post?.images) {
654
+ const imgArray = commented.post.images.split(',').filter(Boolean);
655
+ imgArray.forEach((src, idx) => {
656
+ parts.push({
657
+ text: '[查看图片]',
658
+ type: 'view_img',
659
+ src,
660
+ index: idx,
661
+ from: 'commented',
662
+ color: '#406ce3'
663
+ });
664
+ });
665
+ }
666
+
667
+ commentedContentParts = parts;
668
+ }
669
+
670
+ const contentParts = this.injectTitle(
671
+ this.parseContent(item.post.content, item.post.imageArray, item.shared),
672
+ item.post.title
673
+ );
674
+
675
+ // 提取 title
676
+ const titlePart = contentParts.find(p => p.type === 'title');
677
+
678
+
679
+ if (item.game?.exp !== null) {
680
+ item.user.level = getLevelByExp(item.game?.exp);
681
+ item.user.levelImg = getLevelImg(item.user.level)
682
+ }
683
+
684
+ this.setData({
685
+ loading: false,
686
+ item,
687
+ contentParts,
688
+ hasTitle: !!titlePart,
689
+ titleText: titlePart ? titlePart.text : '',
690
+ commentedContentParts,
691
+ comments,
692
+ likes: Array.isArray(likesRes.data) ? likesRes.data : [],
693
+ shares: shares.map(s => ({
694
+ ...s,
695
+ contentParts: this.parseContent(s.post.content, s.post.imageArray, true)
696
+ }))
697
+ });
698
+
699
+
700
+ if (item.shared) {
701
+ this.setData({
702
+ isLotteryEnded: Date.now() > item.shared?.post?.lottery?.end_time * 1000,
703
+ sharedContentParts: this.injectTitle(
704
+ this.parseContent(item.shared?.post?.content, item.shared?.post?.imageArray, item.shared),
705
+ item.shared?.post?.title
706
+ )
707
+ });
708
+ }
709
+
710
+
711
+ } catch (err) {
712
+ this.setData({
713
+ loading: false,
714
+ })
715
+ console.error("加载帖子详情失败:", err);
716
+ wx.showToast({
717
+ title: "加载失败",
718
+ icon: "none"
719
+ });
720
+ }
721
+ },
722
+
723
+ injectTitle(parts, title) {
724
+ if (!title) return parts;
725
+
726
+ // 标题插入到开头
727
+ parts.unshift({
728
+ text: title,
729
+ color: '#000',
730
+ type: 'title'
731
+ });
732
+
733
+ // 再插入一个换行
734
+ parts.splice(1, 0, {
735
+ type: 'br'
736
+ });
737
+
738
+ return parts;
739
+ },
740
+
741
+ onReachBottom() {
742
+ switch (this.data.active) {
743
+ case 0: // 转发
744
+ this.loadMoreShares();
745
+ break;
746
+ case 1: // 评论
747
+ this.loadMoreComments();
748
+ break;
749
+ case 2: // 赞
750
+ this.loadMoreLikes();
751
+ break;
752
+ }
753
+ },
754
+
755
+ async loadMoreShares() {
756
+ if (!this.data.sharesHasMore || this.data.isLoadingMore) return;
757
+
758
+ this.setData({
759
+ isLoadingMore: true
760
+ });
761
+
762
+ const nextPage = this.data.sharesPage + 1;
763
+ try {
764
+ const res = await queryPostShare(this.data.postId, nextPage);
765
+ const newShares = this.formatShares(res.data).map(s => ({
766
+ ...s,
767
+ contentParts: this.parseContent(s.post.content, s.post.imageArray, true)
768
+ }));
769
+
770
+ this.setData({
771
+ shares: [...this.data.shares, ...newShares],
772
+ sharesPage: nextPage,
773
+ sharesHasMore: newShares.length > 0
774
+ });
775
+ } catch (err) {
776
+ console.error("加载更多转发失败:", err);
777
+ } finally {
778
+ this.setData({
779
+ isLoadingMore: false
780
+ });
781
+ }
782
+ },
783
+
784
+ async loadMoreComments() {
785
+ if (!this.data.commentsHasMore || this.data.isLoadingMore) return;
786
+
787
+ this.setData({
788
+ isLoadingMore: true
789
+ });
790
+
791
+ const nextPage = this.data.commentsPage + 1;
792
+ try {
793
+ const res = await queryPostComments(this.data.postId, nextPage, this.data.sortValue === 0 ? 2 : 1);
794
+ const newComments = this.formatComments(res.data);
795
+
796
+ this.setData({
797
+ comments: [...this.data.comments, ...newComments],
798
+ commentsPage: nextPage,
799
+ commentsHasMore: newComments.length > 0
800
+ });
801
+ } catch (err) {
802
+ console.error("加载更多评论失败:", err);
803
+ } finally {
804
+ this.setData({
805
+ isLoadingMore: false
806
+ });
807
+ }
808
+ },
809
+ async loadMoreLikes() {
810
+ if (!this.data.likesHasMore || this.data.isLoadingMore) return;
811
+
812
+ this.setData({
813
+ isLoadingMore: true
814
+ });
815
+
816
+ const nextPage = this.data.likesPage + 1;
817
+ try {
818
+ const res = await queryPostLikes(this.data.postId, nextPage);
819
+ const newLikes = Array.isArray(res.data) ? res.data : [];
820
+
821
+ this.setData({
822
+ likes: [...this.data.likes, ...newLikes],
823
+ likesPage: nextPage,
824
+ likesHasMore: newLikes.length > 0
825
+ });
826
+ } catch (err) {
827
+ console.error("加载更多点赞失败:", err);
828
+ } finally {
829
+ this.setData({
830
+ isLoadingMore: false
831
+ });
832
+ }
833
+ },
834
+
835
+
836
+
837
+ /**
838
+ * 格式化评论(主评论 + 子评论)
839
+ */
840
+ formatComments(rawComments) {
841
+ if (!Array.isArray(rawComments)) return []
842
+
843
+ return rawComments.map(c => {
844
+ const main = c.main_comment.comment
845
+ const mainImageSrc = main.images ?
846
+ main.images.split(',').filter(Boolean)[0] :
847
+ null;
848
+
849
+ const mainImageMeta = mainImageSrc ? {
850
+ src: mainImageSrc,
851
+ isLong: false,
852
+ isSuperLong: false,
853
+ isHorizon: false
854
+ } : null;
855
+
856
+
857
+
858
+ const mainUser = c.main_comment.user;
859
+ // 处理主评论用户等级
860
+ if (c.main_comment?.game?.exp != null) {
861
+ mainUser.level = getLevelByExp(c.main_comment.game?.exp);
862
+ mainUser.levelImg = getLevelImg(mainUser.level)
863
+ }
864
+
865
+ const contentParts = this.parseContent(main.content || "")
866
+
867
+ // 处理子评论
868
+ const second_comment = Array.isArray(c.second_comment) ?
869
+ c.second_comment.map(sc => {
870
+ const reply = sc.comment;
871
+
872
+ const replyImageSrc = reply.images ?
873
+ reply.images.split(',').filter(Boolean)[0] :
874
+ null;
875
+
876
+ const replyImageMeta = replyImageSrc ? {
877
+ src: replyImageSrc,
878
+ isLong: false,
879
+ isSuperLong: false,
880
+ isHorizon: false
881
+ } : null;
882
+
883
+
884
+ const parts = this.parseContent(sc.comment.content || "")
885
+ return {
886
+ ...sc,
887
+ comment: {
888
+ ...sc.comment,
889
+ formatTime: formatUTC(sc.comment.created_at),
890
+ imageMeta: replyImageMeta
891
+ },
892
+ contentParts: parts
893
+ }
894
+ }) : []
895
+
896
+ return {
897
+ ...c,
898
+ main_comment: {
899
+ ...c.main_comment,
900
+ comment: {
901
+ ...main,
902
+ formatTime: formatUTC(main.created_at),
903
+ imageMeta: mainImageMeta
904
+ },
905
+ contentParts
906
+ },
907
+ second_comment
908
+ }
909
+ })
910
+ },
911
+
912
+ // 格式化转发列表
913
+ // 格式化转发列表
914
+ formatShares(rawShares) {
915
+ if (!Array.isArray(rawShares)) return [];
916
+
917
+ return rawShares.map(s => {
918
+ const post = s.post || {};
919
+ const contentParts = this.parseContent(post.content || "");
920
+ const imageArray = post.images ? post.images.split(',') : [];
921
+
922
+ // ✅ 处理 commented
923
+ let commented = null;
924
+ if (Array.isArray(s.commented) && s.commented.length > 0) {
925
+ const firstCommented = s.commented[0];
926
+ const parts = [];
927
+
928
+ // 添加 @user.nickname
929
+ if (firstCommented.user?.nickname) {
930
+ parts.push({
931
+ text: `@${firstCommented.user.nickname}`,
932
+ color: '#406ce3'
933
+ });
934
+ }
935
+
936
+ // 如果有 reply_nickname
937
+ if (firstCommented.post?.reply_nickname) {
938
+ parts.push({
939
+ text: ' 回复 ',
940
+ color: '#333'
941
+ });
942
+ parts.push({
943
+ text: `@${firstCommented.post.reply_nickname}`,
944
+ color: '#406ce3'
945
+ });
946
+ }
947
+
948
+ // 补冒号
949
+ parts.push({
950
+ text: ': ',
951
+ color: '#333'
952
+ });
953
+
954
+ // 拼接正文
955
+ if (firstCommented.post?.content) {
956
+ parts.push(...this.parseContent(firstCommented.post.content));
957
+ }
958
+
959
+ // 图片数组转成 [查看图片]
960
+ if (firstCommented.post?.images) {
961
+ const imgArray = firstCommented.post.images.split(',');
962
+ imgArray.forEach((src, idx) => {
963
+ parts.push({
964
+ text: '[查看图片]',
965
+ type: 'view_img',
966
+ src,
967
+ index: idx,
968
+ from: 'commented',
969
+ color: '#406ce3'
970
+ });
971
+ });
972
+ }
973
+
974
+ commented = {
975
+ ...firstCommented,
976
+ post: {
977
+ ...firstCommented.post,
978
+ imageArray: firstCommented.post?.images ? firstCommented.post.images.split(',') : []
979
+ },
980
+ contentParts: parts
981
+ };
982
+ }
983
+
984
+ return {
985
+ ...s,
986
+ post: {
987
+ ...post,
988
+ formatTime: formatUTC(post.created_at),
989
+ imageArray
990
+ },
991
+ contentParts,
992
+ commented // ✅ 新增
993
+ };
994
+ });
995
+ },
996
+
997
+
998
+
999
+ onCommentImageLoad(e) {
1000
+ const {
1001
+ width,
1002
+ height
1003
+ } = e.detail;
1004
+ const {
1005
+ level,
1006
+ parentidx,
1007
+ childidx
1008
+ } = e.currentTarget.dataset;
1009
+ const ratio = height / width;
1010
+
1011
+ const imageMeta = {
1012
+ isLong: ratio > 3 && ratio <= 4,
1013
+ isSuperLong: ratio > 4,
1014
+ isHorizon: ratio < 1
1015
+ };
1016
+
1017
+ const comments = [...this.data.comments];
1018
+
1019
+ if (level === 'main') {
1020
+ comments[parentidx].main_comment.comment.imageMeta = imageMeta;
1021
+ }
1022
+
1023
+ if (level === 'reply') {
1024
+ comments[parentidx]
1025
+ .second_comment[childidx]
1026
+ .comment
1027
+ .imageMeta = imageMeta;
1028
+ }
1029
+
1030
+ this.setData({
1031
+ comments
1032
+ });
1033
+ },
1034
+ onImageLoad(e) {
1035
+ const {
1036
+ width,
1037
+ height
1038
+ } = e.detail;
1039
+ if (height / width > 3 && height / width <= 4) {
1040
+ this.setData({
1041
+ isLongImage: true
1042
+ });
1043
+ } else if (height / width < 1) {
1044
+ this.setData({
1045
+ isHorizonImage: true
1046
+ });
1047
+ } else if (height / width > 4) {
1048
+ this.setData({
1049
+ isSuperLongImage: true
1050
+ });
1051
+ }
1052
+
1053
+ },
1054
+
1055
+ // 链接跳转
1056
+ onJumpUrlTap(e) {
1057
+ const url = e.currentTarget.dataset.url;
1058
+
1059
+ if (!url) return;
1060
+
1061
+ wx.navigateTo({
1062
+ url: `/pages/webview/webview?url=${encodeURIComponent(url)}`
1063
+ });
1064
+ },
1065
+
1066
+ onPreviewImage(e) {
1067
+ const src = e.currentTarget.dataset.src;
1068
+ wx.previewImage({
1069
+ current: src,
1070
+ urls: [src]
1071
+ });
1072
+ },
1073
+
1074
+ previewImageMultiple(e) {
1075
+ const {
1076
+ index
1077
+ } = e.currentTarget.dataset;
1078
+ const imgList = this.data.item.post.imageArray;
1079
+ wx.previewImage({
1080
+ current: imgList[index],
1081
+ urls: imgList
1082
+ });
1083
+ },
1084
+ formatPost(item) {
1085
+
1086
+ const newItem = {
1087
+ ...item,
1088
+ post: {
1089
+ ...item.post,
1090
+ formatTime: formatUTC(item.post.created_at),
1091
+ imageArray: item.post.images ? item.post.images.split(',') : []
1092
+ }
1093
+ };
1094
+
1095
+ // 处理转发的原始帖
1096
+ if (item.shared && item.shared.post) {
1097
+ newItem.shared = {
1098
+ ...item.shared,
1099
+ post: {
1100
+ ...item.shared.post,
1101
+ formatTime: formatUTC(item.shared.post.created_at),
1102
+ imageArray: item.shared.post.images ? item.shared.post.images.split(',') : []
1103
+ }
1104
+ };
1105
+ }
1106
+ return newItem;
1107
+ },
1108
+
1109
+ parseContent(content, images = [], isShared) {
1110
+ const parts = [];
1111
+ // ✅ 正则末尾加上 guide 匹配
1112
+ const regex = /(#[^#]+#)|(\{@(.*?)\|(\d+)\})|<font bold=true>|<font bold=false>|<sign_line>|<feature_blog>|<img src='([^']+)'>|<click_img src='[^']+' text='([^']+)'>|<color_grey text='([^']+)'>|<local_img src='([^']+).image'>|<jump_url url='([^']+)'>|(\[\/\d+\])|{\$([^|]+)\|([^}]+)}|{\?([^|]+)\|([^}]+)}/g;
1113
+
1114
+ let lastIndex = 0,
1115
+ match;
1116
+
1117
+ while ((match = regex.exec(content)) !== null) {
1118
+ if (match.index > lastIndex) {
1119
+ const plainText = content.slice(lastIndex, match.index);
1120
+ this.pushWithLineBreak(parts, plainText);
1121
+ }
1122
+
1123
+ if (match[1]) {
1124
+ parts.push({
1125
+ text: match[1],
1126
+ color: '#406ce3',
1127
+ type: 'topic'
1128
+ });
1129
+ } else if (match[2]) {
1130
+ parts.push({
1131
+ text: `@${match[3]}`,
1132
+ color: '#406ce3',
1133
+ type: 'at',
1134
+ userId: match[4]
1135
+ });
1136
+ } else if (match[5]) {
1137
+ parts.push({
1138
+ text: '[图片]',
1139
+ src: match[5],
1140
+ type: 'img'
1141
+ });
1142
+ } else if (match[6]) {
1143
+ parts.push({
1144
+ text: match[6],
1145
+ src: match[6],
1146
+ type: 'click_img'
1147
+ });
1148
+ } else if (match[7]) {
1149
+ parts.push({
1150
+ text: match[7],
1151
+ color: '#999'
1152
+ });
1153
+ } else if (match[8]) {
1154
+ parts.push({
1155
+ text: '',
1156
+ src: match[8],
1157
+ type: 'local_img'
1158
+ });
1159
+ } else if (match[9]) {
1160
+ parts.push({
1161
+ text: '§网页链接',
1162
+ url: match[9],
1163
+ type: 'jump_url',
1164
+ color: '#406ce3'
1165
+ });
1166
+ } else if (match[10]) {
1167
+ const code = match[10].match(/\[\/(\d+)\]/)[1];
1168
+ const file = emojiMap[code];
1169
+ if (file) {
1170
+ parts.push({
1171
+ type: 'emoji',
1172
+ src: `../../assets/images/emoji/${file}`
1173
+ });
1174
+ } else {
1175
+ parts.push({
1176
+ text: match[10],
1177
+ color: '#333'
1178
+ });
1179
+ }
1180
+ } else if (match[11] && match[12]) {
1181
+ parts.push({
1182
+ text: `$${match[11]}`,
1183
+ gameId: match[12],
1184
+ type: 'game',
1185
+ color: '#406ce3'
1186
+ });
1187
+ } else if (match[13] && match[14]) {
1188
+ // ✅ 新增 guide 逻辑
1189
+ parts.push({
1190
+ text: `?${match[13]}`,
1191
+ guideName: match[13],
1192
+ guideGameId: match[14],
1193
+ type: 'guide',
1194
+ color: '#406ce3'
1195
+ });
1196
+ } else if (match[0] === '<font bold=true>') {
1197
+ parts.push({
1198
+ text: '',
1199
+ bold: true
1200
+ });
1201
+ } else if (match[0] === '<font bold=false>') {
1202
+ parts.push({
1203
+ text: '',
1204
+ bold: false
1205
+ });
1206
+ } else if (match[0] === '<sign_line>') {
1207
+ parts.push({
1208
+ text: '————',
1209
+ type: 'line'
1210
+ });
1211
+ } else if (match[0] === '<feature_blog>') {
1212
+ parts.push({
1213
+ text: '[加精]',
1214
+ type: 'feature'
1215
+ });
1216
+ }
1217
+
1218
+ lastIndex = regex.lastIndex;
1219
+ }
1220
+
1221
+ if (lastIndex < content?.length) {
1222
+ this.pushWithLineBreak(parts, content.slice(lastIndex));
1223
+ }
1224
+
1225
+ if (isShared && images.length) {
1226
+ images.forEach((src) => {
1227
+ parts.push({
1228
+ type: 'image',
1229
+ text: '[查看图片]',
1230
+ color: '#406ce3',
1231
+ src
1232
+ });
1233
+ });
1234
+ }
1235
+
1236
+ return parts;
1237
+ },
1238
+ pushWithLineBreak(parts, text) {
1239
+ const segments = text.split(/(\r\n|\n|\r)/);
1240
+ segments.forEach(seg => {
1241
+ if (seg === '\n' || seg === '\r' || seg === '\r\n') {
1242
+ parts.push({
1243
+ type: 'br'
1244
+ });
1245
+ } else if (seg.length > 0) {
1246
+ parts.push({
1247
+ text: seg,
1248
+ color: '#333'
1249
+ });
1250
+ }
1251
+ });
1252
+ },
1253
+
1254
+ showFull() {
1255
+ this.setData({
1256
+ showFull: true
1257
+ });
1258
+ },
1259
+
1260
+ onFollowTap(e) {
1261
+ if (!checkLogin()) return; // 🔥 登录校验
1262
+
1263
+ const user = e.currentTarget.dataset.user;
1264
+ const userId = user.id;
1265
+ const isFans = user.is_fans ?? 0;
1266
+ const nType = isFans === 1 ? 2 : 1; // 1关注,2取消关注
1267
+
1268
+ followUser(userId, nType).then(res => {
1269
+ if (res.code === 0) {
1270
+ // 更新当前页面 item.user.is_fans
1271
+ this.setData({
1272
+ 'item.user.is_fans': isFans === 1 ? 0 : 1
1273
+ });
1274
+
1275
+ const pages = getCurrentPages();
1276
+ pages.forEach(page => {
1277
+
1278
+ // 首页:更新 posts 中对应用户的 is_fans
1279
+ if (page.route === 'pages/home/home') {
1280
+
1281
+ const posts = page.data.posts.map(p => {
1282
+ if (p.user.id === user.id) {
1283
+ p.user.is_fans = isFans === 1 ? 0 : 1; // 修改关注状态
1284
+ }
1285
+ return p;
1286
+ });
1287
+ page.setData({
1288
+ posts
1289
+ });
1290
+ }
1291
+
1292
+ // 用户详情页:更新 userDetail
1293
+ if (page.route === 'pages/user/user') {
1294
+ if (page.data.userDetail?.user?.id === user.id) {
1295
+ page.setData({
1296
+ 'userDetail.user.is_fans': isFans === 1 ? 0 : 1
1297
+ });
1298
+ }
1299
+ }
1300
+
1301
+ // 帖子详情页:更新 item.user.is_fans
1302
+ if (page.route === 'pages/post/post') {
1303
+ if (page.data.item?.user?.id === user.id) {
1304
+ page.setData({
1305
+ 'item.user.is_fans': isFans === 1 ? 0 : 1
1306
+ });
1307
+ }
1308
+ }
1309
+ });
1310
+
1311
+ wx.showToast({
1312
+ title: nType === 1 ? '关注成功' : '已取消关注',
1313
+ icon: 'success'
1314
+ });
1315
+ } else {
1316
+ wx.showToast({
1317
+ title: res.message || '操作失败',
1318
+ icon: 'none'
1319
+ });
1320
+ }
1321
+ }).catch(err => {
1322
+ wx.showToast({
1323
+ title: err,
1324
+ icon: 'none'
1325
+ });
1326
+ });
1327
+ },
1328
+ // 跳转帖子详情
1329
+ goSharedDetail(e) {
1330
+ if (e.target.dataset.video === '1') return
1331
+
1332
+ const postId = e.currentTarget.dataset.id;
1333
+ wx.navigateTo({
1334
+ url: `/pages/post/post?id=${postId}`
1335
+ });
1336
+ },
1337
+
1338
+ gotoWiki(e) {
1339
+ const gameId = e.currentTarget.dataset.id
1340
+ const name = e.currentTarget.dataset.name
1341
+ wx.navigateTo({
1342
+ url: `/pages/wiki/wiki?game_id=${gameId}&name=${encodeURIComponent(name)}`
1343
+ })
1344
+ },
1345
+ /** 点击 跳转 */
1346
+ onPartTap(e) {
1347
+ const {
1348
+ type,
1349
+ id,
1350
+ text
1351
+ } = e.currentTarget.dataset;
1352
+ switch (type) {
1353
+ case 'topic':
1354
+ const topicText = text;
1355
+ const topicName = topicText.replace(/#/g, ''); // 去掉首尾 #
1356
+ wx.navigateTo({
1357
+ url: `/pages/topic/topic?topic_name=${encodeURIComponent(topicName)}`
1358
+ });
1359
+ break;
1360
+
1361
+ case 'at':
1362
+ wx.navigateTo({
1363
+ url: `/pages/user/user?id=${id}`
1364
+ });
1365
+ break;
1366
+
1367
+ case 'game':
1368
+ wx.navigateTo({
1369
+ url: `/pages/game/game?id=${id}`
1370
+ });
1371
+ break;
1372
+
1373
+ }
1374
+ },
1375
+
1376
+
1377
+
1378
+ onShowSheet(topic) {
1379
+ const sheet = this.selectComponent('#bottomSheet');
1380
+ if (sheet) {
1381
+ sheet.setData({
1382
+ selectedTopic: topic,
1383
+ visible: true
1384
+ });
1385
+ }
1386
+ },
1387
+
1388
+
1389
+ createLikeAnimation() {
1390
+ const animation = wx.createAnimation({
1391
+ duration: 300,
1392
+ timingFunction: 'ease-out'
1393
+ });
1394
+ animation.scale(1.6).step().scale(1).step();
1395
+ return animation.export();
1396
+ },
1397
+
1398
+
1399
+ async onCommentLike(e) {
1400
+ if (!checkLogin()) return; // 🔥 登录校验
1401
+
1402
+ const {
1403
+ type,
1404
+ id,
1405
+ idx,
1406
+ parentidx
1407
+ } = e.currentTarget.dataset;
1408
+ if (!id) return;
1409
+
1410
+ // const animation = this.createLikeAnimation();
1411
+ let comments = [...this.data.comments];
1412
+
1413
+ if (type === 'main') {
1414
+ const main = comments[idx].main_comment.comment;
1415
+ const isLiked = main.is_like === 1;
1416
+ const nType = isLiked ? 2 : 1; // 1点赞,2取消
1417
+
1418
+ // 动画 + 乐观更新
1419
+ main.is_like = isLiked ? 0 : 1;
1420
+ main.like_count += isLiked ? -1 : 1;
1421
+ // comments[idx].main_comment.likeAnimation = animation;
1422
+ this.setData({
1423
+ comments
1424
+ });
1425
+ try {
1426
+ await likePost(id, nType);
1427
+
1428
+ } catch (err) {
1429
+ console.error('主评论点赞失败', err);
1430
+ // 回滚
1431
+ main.is_like = isLiked ? 1 : 0;
1432
+ main.like_count += isLiked ? 1 : -1;
1433
+ this.setData({
1434
+ comments
1435
+ });
1436
+ }
1437
+
1438
+ } else if (type === 'reply') {
1439
+ const reply = comments[parentidx].second_comment[idx].comment;
1440
+ const isLiked = reply.is_like === 1;
1441
+ const nType = isLiked ? 2 : 1;
1442
+
1443
+ reply.is_like = isLiked ? 0 : 1;
1444
+ reply.like_count += isLiked ? -1 : 1;
1445
+ // comments[parentidx].second_comment[idx].likeAnimation = animation;
1446
+ this.setData({
1447
+ comments
1448
+ });
1449
+
1450
+ try {
1451
+ await likePost(id, nType);
1452
+ } catch (err) {
1453
+ console.error('子评论点赞失败', err);
1454
+ reply.is_like = isLiked ? 1 : 0;
1455
+ reply.like_count += isLiked ? 1 : -1;
1456
+ this.setData({
1457
+ comments
1458
+ });
1459
+ }
1460
+ }
1461
+ },
1462
+
1463
+ // 点击主评论或子评论,弹出回复框
1464
+ onCommentReplyTap(e) {
1465
+ if (!checkLogin()) return; // 🔥 登录校验
1466
+
1467
+ const {
1468
+ type,
1469
+ idx,
1470
+ parentidx
1471
+ } = e.currentTarget.dataset;
1472
+ let commentData = null;
1473
+
1474
+ if (type === 'main') {
1475
+ commentData = this.data.comments[idx].main_comment.comment;
1476
+ commentData.reply_nickname = this.data.comments[idx].main_comment.user.nickname
1477
+ commentData.avatar = this.data.comments[idx].main_comment.user.avatar
1478
+
1479
+ } else if (type === 'reply') {
1480
+ commentData = this.data.comments[parentidx].second_comment[idx].comment;
1481
+ commentData.avatar = this.data.comments[parentidx].second_comment[idx].user.avatar;
1482
+
1483
+ }
1484
+
1485
+ if (!commentData) return;
1486
+
1487
+ this.setData({
1488
+ currentReplyComment: commentData
1489
+ });
1490
+
1491
+ const sheet = this.selectComponent('#bottomSheet');
1492
+ if (sheet) {
1493
+ sheet.setData({
1494
+ visible: true,
1495
+ originData: this.data.item,
1496
+ commentData: commentData,
1497
+ });
1498
+ }
1499
+ },
1500
+ // 选择投票
1501
+ onSelectVote(e) {
1502
+ if (!checkLogin()) return; // 🔥 登录校验
1503
+
1504
+ const {
1505
+ id
1506
+ } = e.currentTarget.dataset;
1507
+ const disabled = e.currentTarget.dataset.disabled;
1508
+
1509
+ if (disabled) return; // 已投票 或 已结束禁止
1510
+
1511
+ let {
1512
+ selectedVotes,
1513
+ voteOptions
1514
+ } = this.data;
1515
+ const vote = this.data.item.post.vote;
1516
+
1517
+ // 单选
1518
+ if (vote.max_choices === 1) {
1519
+ selectedVotes = [id];
1520
+ } else {
1521
+ // 多选
1522
+ if (selectedVotes.includes(id)) {
1523
+ selectedVotes = selectedVotes.filter(v => v !== id);
1524
+ } else {
1525
+ if (selectedVotes.length >= vote.max_choices) {
1526
+ wx.showToast({
1527
+ title: `最多只能选择${vote.max_choices}个选项`,
1528
+ icon: 'none'
1529
+ });
1530
+ return; // 超过数量,直接返回
1531
+ }
1532
+ selectedVotes.push(id);
1533
+ }
1534
+ }
1535
+
1536
+ // 更新选中状态
1537
+ voteOptions = voteOptions.map(o => ({
1538
+ ...o,
1539
+ _selected: selectedVotes.includes(o.id)
1540
+ }));
1541
+
1542
+ this.setData({
1543
+ selectedVotes,
1544
+ voteOptions
1545
+ });
1546
+ },
1547
+
1548
+
1549
+ // 提交投票
1550
+ submitVote() {
1551
+ if (!checkLogin()) return; // 🔥 登录校验
1552
+
1553
+ const {
1554
+ selectedVotes,
1555
+ item
1556
+ } = this.data;
1557
+
1558
+ if (selectedVotes.length === 0) return;
1559
+ console.log(selectedVotes)
1560
+ votePost(item.post.id, item.post.vote.id, selectedVotes).then(res => {
1561
+ this.fetchPostDetail(item.post.id); // 重新加载获取 vote_count & percent
1562
+ });
1563
+ },
1564
+
1565
+ formatTime(ts) {
1566
+ const d = new Date(ts * 1000);
1567
+
1568
+ const Y = d.getFullYear();
1569
+ const M = String(d.getMonth() + 1).padStart(2, '0');
1570
+ const D = String(d.getDate()).padStart(2, '0');
1571
+ const h = String(d.getHours()).padStart(2, '0');
1572
+ const m = String(d.getMinutes()).padStart(2, '0');
1573
+
1574
+ return `${Y}-${M}-${D} ${h}:${m}`;
1575
+ },
1576
+
1577
+ // 点击更多按钮
1578
+ onMoreTap() {
1579
+ const currentUserId = wx.getStorageSync('appuid'); // 当前登录用户id
1580
+ const isOwner = this.data.item.user.id === currentUserId;
1581
+
1582
+ this.setData({
1583
+ showMenuSheet: true,
1584
+ isOwner
1585
+ });
1586
+ },
1587
+
1588
+ // 关闭弹窗
1589
+ onCloseSheet() {
1590
+ this.setData({
1591
+ showMenuSheet: false
1592
+ });
1593
+ },
1594
+
1595
+ // 弹窗选项点击
1596
+ onDelete() {
1597
+ const postId = this.data.item.post.id;
1598
+
1599
+ deleteContent(postId).then(res => {
1600
+ if (res.code === 0) {
1601
+ wx.showToast({
1602
+ title: '删除成功',
1603
+ icon: 'success'
1604
+ });
1605
+
1606
+ const pages = getCurrentPages();
1607
+ const prevPage = pages[pages.length - 2]; // 🔥 上一个页面
1608
+
1609
+ if (prevPage) {
1610
+ // 首页
1611
+ if (prevPage.route === 'pages/home/home') {
1612
+ const posts = prevPage.data.posts;
1613
+
1614
+ if (Array.isArray(posts)) {
1615
+ prevPage.setData({
1616
+ posts: posts.filter(p => p.post.id !== postId)
1617
+ });
1618
+ } else if (posts && typeof posts === 'object') {
1619
+ const newPosts = {};
1620
+ Object.keys(posts).forEach(key => {
1621
+ newPosts[key] = Array.isArray(posts[key]) ?
1622
+ posts[key].filter(p => p.post.id !== postId) :
1623
+ posts[key];
1624
+ });
1625
+ prevPage.setData({
1626
+ posts: newPosts
1627
+ });
1628
+ }
1629
+ }
1630
+
1631
+ // 用户中心
1632
+ if (prevPage.route === 'pages/user/user') {
1633
+ const userPosts = prevPage.data.userPosts || [];
1634
+ prevPage.setData({
1635
+ userPosts: userPosts.filter(p => p.post.id !== postId)
1636
+ });
1637
+ }
1638
+ }
1639
+
1640
+ // 返回上一页
1641
+ wx.navigateBack();
1642
+
1643
+ } else {
1644
+ wx.showToast({
1645
+ title: res.msg || '删除失败',
1646
+ icon: 'error'
1647
+ });
1648
+ }
1649
+ });
1650
+ },
1651
+
1652
+ // 举报帖子
1653
+ onReport() {
1654
+ wx.navigateTo({
1655
+ url: `/pages/report-category/report-category?postId=${this.data.item.post.id}`,
1656
+ })
1657
+
1658
+ this.triggerEvent('reportPost', {
1659
+ post: this.data.item.post
1660
+ });
1661
+ this.onCloseSheet();
1662
+ },
1663
+
1664
+ // 收藏/取消收藏
1665
+ onFavorite() {
1666
+ const post = this.data.item.post;
1667
+ const nType = post.is_collect === 1 ? 2 : 1; // 1收藏,2取消收藏
1668
+
1669
+ collectPost(post.id, nType).then(res => {
1670
+ if (res.code === 0) {
1671
+ wx.showToast({
1672
+ title: nType === 1 ? '已收藏' : '取消收藏',
1673
+ icon: 'success'
1674
+ });
1675
+
1676
+ // 更新本地状态
1677
+ this.setData({
1678
+ 'item.post.is_collect': nType === 1 ? 1 : 0
1679
+ });
1680
+
1681
+ this.onCloseSheet();
1682
+ } else {
1683
+ wx.showToast({
1684
+ title: res.msg || (nType === 1 ? '收藏失败' : '取消收藏失败'),
1685
+ icon: 'error'
1686
+ });
1687
+ }
1688
+ });
1689
+ },
1690
+
1691
+
1692
+ /**
1693
+ * 展开剩余子评论
1694
+ */
1695
+ async loadMoreReply(e) {
1696
+ const parentidx = e.currentTarget.dataset.parentidx;
1697
+
1698
+ let comments = [...this.data.comments];
1699
+ let parent = comments[parentidx];
1700
+
1701
+ const post_id = this.data.postId;
1702
+ const comment_id = parent.main_comment.comment.id;
1703
+
1704
+ // 初始化每个主评论的分页
1705
+ if (!parent._replyPage) parent._replyPage = 0;
1706
+
1707
+ const nextPage = parent._replyPage + 1;
1708
+
1709
+ try {
1710
+ const res = await queryMainComments(post_id, comment_id, nextPage);
1711
+
1712
+ if (res.code !== 0 || !Array.isArray(res.data)) {
1713
+ wx.showToast({
1714
+ title: '加载失败',
1715
+ icon: 'none'
1716
+ });
1717
+ return;
1718
+ }
1719
+
1720
+ // 格式化新子评论
1721
+ const newReplies = res.data.map(sc => {
1722
+ const parts = this.parseContent(sc.comment.content || "");
1723
+ return {
1724
+ ...sc,
1725
+ comment: {
1726
+ ...sc.comment,
1727
+ formatTime: formatUTC(sc.comment.created_at)
1728
+ },
1729
+ contentParts: parts
1730
+ };
1731
+ });
1732
+
1733
+ // 拼接到原来的 second_comment
1734
+ parent.second_comment = [...parent.second_comment, ...newReplies];
1735
+
1736
+ // 更新页码
1737
+ parent._replyPage = nextPage;
1738
+
1739
+ this.setData({
1740
+ comments
1741
+ });
1742
+
1743
+ } catch (err) {
1744
+ console.error("加载更多子评论失败", err);
1745
+ wx.showToast({
1746
+ title: "网络错误",
1747
+ icon: "none"
1748
+ });
1749
+ }
1750
+ },
1751
+
1752
+ checkDialog() {
1753
+ wx.navigateTo({
1754
+ url: `/pages/postDialog/postDialog?id=${this.data.item.post.id}`,
1755
+ })
1756
+ },
1757
+
1758
+
1759
+ // ****************************视频相关逻辑*************************************
1760
+
1761
+ // 获取视频链接
1762
+ async onBannerVideoPlay(e) {
1763
+
1764
+ const id = e.currentTarget.dataset.id;
1765
+ const type = e.currentTarget.dataset.type;
1766
+ const sharedId = e.currentTarget.dataset.sharedid;
1767
+
1768
+ // 是否是带转发的原始帖的视频
1769
+ const isShared = type === 'shared'
1770
+ let getVideoId = isShared ? sharedId : id
1771
+
1772
+ try {
1773
+ const res = await queryPostVideo(getVideoId);
1774
+
1775
+ if (res.code === 0 && res.data) {
1776
+ let key = isShared ? 'item.shared.post.videoUrl' : 'item.post.videoUrl';
1777
+
1778
+ this.setData({
1779
+ [key]: res.data,
1780
+ isCurrentPlaying: true
1781
+ }, () => {
1782
+ // 2️⃣ video 渲染完成后再创建 IntersectionObserver
1783
+ this.setupVideoObserver();
1784
+ })
1785
+
1786
+ // 此处传的是外层唯一id,也就是当且贴id
1787
+ this.triggerEvent('videoPlay', {
1788
+ id
1789
+ });
1790
+
1791
+
1792
+ } else {
1793
+ throw new Error('无视频地址');
1794
+ }
1795
+ } catch (err) {
1796
+ wx.showToast({
1797
+ title: '视频加载失败',
1798
+ icon: 'none'
1799
+ });
1800
+ } finally {}
1801
+ },
1802
+
1803
+ setupVideoObserver() {
1804
+ const postId = this.data.item.post.id;
1805
+ const observer = this.createIntersectionObserver({
1806
+ thresholds: [0.6] // 关注完全不可见
1807
+ });
1808
+
1809
+ observer.relativeToViewport({
1810
+ top: 0,
1811
+ bottom: 0
1812
+ }).observe(
1813
+ `#bannerVideo-${postId}`,
1814
+ (res) => {
1815
+ const videoContext = wx.createVideoContext(`bannerVideo-${postId}`, this);
1816
+ if (res.intersectionRatio <= 0.6) {
1817
+ videoContext.pause();
1818
+ }
1819
+ }
1820
+ );
1821
+ },
1822
+
1823
+
1824
+ handlePlayVideo() {
1825
+
1826
+ const id = this.data.item.post.id;
1827
+ const videoContext = wx.createVideoContext(`bannerVideo-${id}`, this);
1828
+
1829
+ // 统一逻辑:播放前先通知父页面暂停其他视频
1830
+
1831
+ this.triggerEvent('videoPlay', {
1832
+ id
1833
+ });
1834
+
1835
+ // 播放自己
1836
+ videoContext.play();
1837
+
1838
+ // 标记自己为当前播放的视频
1839
+ this.setData({
1840
+ isCurrentPlaying: true
1841
+ });
1842
+ },
1843
+ // 视频暂停事件
1844
+ onVideoPause(e) {
1845
+ this.setData({
1846
+ isCurrentPlaying: false
1847
+ });
1848
+ },
1849
+ // 父页面传进来的暂停事件
1850
+ pauseVideo() {
1851
+ const videoContext = wx.createVideoContext(`bannerVideo-${this.data.item.post.id}`, this);
1852
+ videoContext.pause();
1853
+ },
1854
+
1855
+
1856
+ });