artalk 2.1.3 → 2.1.6

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 (62) hide show
  1. package/CNAME +1 -0
  2. package/dist/Artalk.css +1 -1
  3. package/dist/Artalk.es.js +509 -1571
  4. package/dist/Artalk.iife.js +1 -1
  5. package/dist/Artalk.js +1 -1
  6. package/index.html +143 -0
  7. package/package.json +8 -32
  8. package/postcss.config.js +5 -0
  9. package/src/api/index.ts +16 -3
  10. package/src/api/request.ts +4 -2
  11. package/src/artalk.ts +30 -8
  12. package/src/components/action-btn.ts +1 -1
  13. package/src/components/comment.ts +88 -3
  14. package/src/components/editor-plugs/emoticons-plug.less +21 -11
  15. package/src/components/editor-plugs/emoticons-plug.ts +18 -18
  16. package/src/components/editor-plugs/preview-plug.less +1 -1
  17. package/src/components/editor.ts +15 -20
  18. package/src/components/html/comment.html +6 -13
  19. package/src/components/html/editor.html +8 -9
  20. package/src/components/html/list.html +1 -1
  21. package/src/components/html/sidebar-layer.html +8 -0
  22. package/src/components/list-lite.ts +203 -299
  23. package/src/components/list.ts +11 -9
  24. package/src/components/pagination.ts +2 -2
  25. package/src/components/read-more-btn.ts +27 -4
  26. package/src/components/sidebar-layer.ts +143 -0
  27. package/src/defaults.ts +2 -1
  28. package/src/lib/detect.js +3 -1
  29. package/src/lib/utils.ts +27 -1
  30. package/src/style/_color.less +19 -23
  31. package/src/style/_extend.less +0 -80
  32. package/src/style/_variables.less +0 -24
  33. package/src/style/comment.less +191 -197
  34. package/src/style/editor.less +69 -94
  35. package/src/style/list.less +13 -13
  36. package/src/style/main.less +32 -32
  37. package/src/style/pagination.less +6 -6
  38. package/src/style/sidebar-layer.less +92 -0
  39. package/types/artalk-config.d.ts +4 -1
  40. package/types/artalk-data.d.ts +2 -2
  41. package/vite-common.config.ts +15 -0
  42. package/vite-demo.config.ts +14 -0
  43. package/vite.config.ts +36 -0
  44. package/LICENSE +0 -674
  45. package/README.md +0 -84
  46. package/src/components/admin/page-list.ts +0 -194
  47. package/src/components/admin/site-list-floater.ts +0 -98
  48. package/src/components/admin/site-list.ts +0 -289
  49. package/src/components/html/sidebar.html +0 -22
  50. package/src/components/item-text-editor.ts +0 -136
  51. package/src/components/sidebar-view.ts +0 -26
  52. package/src/components/sidebar-views/comments-view.ts +0 -74
  53. package/src/components/sidebar-views/pages-view.ts +0 -61
  54. package/src/components/sidebar-views/setting-view.ts +0 -21
  55. package/src/components/sidebar-views/sites-view.ts +0 -36
  56. package/src/components/sidebar-views/transfer-view.ts +0 -218
  57. package/src/components/sidebar.ts +0 -293
  58. package/src/lib/md5.js +0 -6
  59. package/src/style/item-text-editor.less +0 -35
  60. package/src/style/page-list.less +0 -50
  61. package/src/style/sidebar.less +0 -420
  62. package/src/style/site-list.less +0 -208
@@ -9,50 +9,45 @@ import Pagination from './pagination'
9
9
  import ReadMoreBtn from './read-more-btn'
10
10
 
11
11
  export default class ListLite extends Component {
12
- private $parent: HTMLElement
12
+ protected $commentsWrap: HTMLElement
13
+ protected comments: Comment[] = []
13
14
 
14
- private $commentsWrap: HTMLElement
15
- public comments: Comment[] = []
15
+ protected data?: ListData
16
+ protected isLoading: boolean = false
16
17
 
17
- public data?: ListData
18
- private pageSize: number = 15 // 每次请求获取量
19
- private offset: number = 0
20
- public type?: 'all'|'mentions'|'mine'|'pending'
18
+ public noCommentText = '无内容' // 无评论时显示
21
19
 
22
- public noCommentText: string
23
- public renderComment?: (comment: Comment) => void
24
- public paramsEditor?: (params: any) => void
25
- public onAfterLoad?: (data: ListData) => void
20
+ /** 平铺模式 */
21
+ public flatMode = false
26
22
 
27
- private isLoading: boolean = false
28
- public isFirstLoad = true
23
+ /** 分页 */
24
+ public pageMode: 'pagination'|'read-more' = 'pagination'
25
+ public pageSize: number = 20 // 每次请求获取量
26
+ public scrollListenerAt?: HTMLElement // 监听指定元素上的滚动
27
+ public repositionAt?: HTMLElement // 翻页归位到指定元素
29
28
 
30
- public flatMode?: boolean
29
+ protected pagination?: Pagination // 分页条
30
+ protected readMoreBtn?: ReadMoreBtn // 阅读更多
31
+ protected autoLoadScrollEvent?: any // 自动加载滚动事件
31
32
 
32
- public pageMode: 'pagination'|'read-more' = 'pagination'
33
- public pagination?: Pagination
34
- public readMoreBtn?: ReadMoreBtn
35
- public autoLoadScrollEvent?: any
36
- public autoLoadListenerAt?: HTMLElement
33
+ public renderComment?: (comment: Comment) => void
34
+ public paramsEditor?: (params: any) => void
35
+ public onAfterLoad?: (data: ListData) => void
37
36
 
38
- public unread: NotifyData[] = []
39
- public unreadHighlight = false
37
+ // 未读标记
38
+ protected unread: NotifyData[] = []
39
+ public unreadHighlight?: boolean // 高亮未读
40
40
 
41
- constructor (ctx: Context, $parent: HTMLElement) {
41
+ constructor (ctx: Context) {
42
42
  super(ctx)
43
43
 
44
- this.$parent = $parent
44
+ // 初始化元素
45
45
  this.$el = Utils.createElement(
46
46
  `<div class="atk-list-lite">
47
47
  <div class="atk-list-comments-wrap"></div>
48
48
  </div>`)
49
49
  this.$commentsWrap = this.$el.querySelector('.atk-list-comments-wrap')!
50
50
 
51
- // 查看更多
52
- this.pageSize = this.conf.pagination ? (this.conf.pagination.pageSize || this.pageSize) : this.pageSize
53
-
54
- this.noCommentText = this.conf.noComment || '无评论'
55
-
56
51
  // 评论时间自动更新
57
52
  window.setInterval(() => {
58
53
  this.$el.querySelectorAll<HTMLElement>('[data-atk-comment-date]').forEach(el => {
@@ -61,13 +56,15 @@ export default class ListLite extends Component {
61
56
  })
62
57
  }, 30 * 1000) // 30s 更新一次
63
58
 
59
+ // 事件监听
64
60
  this.ctx.on('unread-update', (data) => (this.updateUnread(data.notifies)))
65
61
  }
66
62
 
67
- public async reqComments(offset: number = 0) {
68
- if (offset === 0 && this.pageMode !== 'pagination') { this.clearAllComments() }
63
+ /** 评论获取 */
64
+ public async fetchComments(offset: number) {
65
+ if (this.isLoading) return
69
66
 
70
- // set loading
67
+ // 加载动画
71
68
  const showLoading = () => {
72
69
  this.isLoading = true
73
70
  if (offset === 0) Ui.showLoading(this.$el)
@@ -80,184 +77,186 @@ export default class ListLite extends Component {
80
77
  else if (this.pageMode === 'read-more') this.readMoreBtn!.setLoading(false)
81
78
  else if (this.pageMode === 'pagination') this.pagination!.setLoading(false)
82
79
  }
83
-
84
80
  showLoading()
81
+
82
+ // 事件通知(开始加载评论)
85
83
  this.ctx.trigger('comments-load')
86
84
 
85
+ // 清空评论(加载按钮)
86
+ if (this.pageMode === 'read-more' && offset === 0) { this.clearAllComments() }
87
+
88
+ // 请求评论数据
87
89
  let listData: ListData
88
90
  try {
89
- listData = await new Api(this.ctx).get(offset, this.type, this.flatMode, this.paramsEditor)
91
+ listData = await new Api(this.ctx).get(offset, this.pageSize, this.flatMode, this.paramsEditor)
90
92
  } catch (e: any) {
91
- this.onError(e.msg || String(e))
93
+ this.onError(e.msg || String(e), offset)
92
94
  throw e
93
95
  } finally {
94
96
  hideLoading()
95
97
  }
96
98
 
97
- // version check
98
- if (this.ctx.conf.versionCheck) {
99
- const needUpdate = this.apiVersionCheck(listData.api_version || {})
100
- if (needUpdate) return
101
- }
102
-
103
- // load data
104
- this.offset = offset
99
+ // 清除原有错误
100
+ Ui.setError(this.$el, null)
105
101
 
102
+ // 装载数据
106
103
  try {
107
104
  this.onLoad(listData, offset)
108
-
109
- if (this.onAfterLoad) {
110
- this.onAfterLoad(listData)
111
- }
112
105
  } catch (e: any) {
113
- this.onError(String(e))
106
+ this.onError(String(e), offset)
114
107
  throw e
115
108
  } finally {
116
109
  hideLoading()
117
110
  }
118
111
  }
119
112
 
120
- public onLoad(data: ListData, offset: number) {
121
- Ui.setError(this.$el, null)
122
-
123
- if (this.pageMode === 'pagination') {
124
- this.clearAllComments()
125
- }
113
+ protected onLoad(data: ListData, offset: number) {
114
+ // 清空评论(分页条)
115
+ if (this.pageMode === 'pagination') { this.clearAllComments() }
126
116
 
127
117
  this.data = data
118
+
119
+ // 版本检测
120
+ if (this.ctx.conf.versionCheck && this.versionCheck(data.api_version)) return
121
+
122
+ // 导入数据
128
123
  this.importComments(data.comments)
129
124
 
130
- // onLoad 时初始化
131
- if (this.isFirstLoad) {
132
- this.onLoadInit()
133
- }
125
+ // 分页方式
126
+ this.refreshPagination(offset, (this.flatMode ? data.total : data.total_roots)) // 初始化
134
127
 
135
- if (this.pageMode === 'pagination') {
136
- this.pagination!.update(offset, this.data?.total_parents || 0)
137
- }
138
- if (this.pageMode === 'read-more') {
139
- if (this.hasMoreComments) this.readMoreBtn!.show()
140
- else this.readMoreBtn!.hide()
141
- }
128
+ // 加载后事件
129
+ this.refreshUI()
142
130
 
143
131
  this.ctx.trigger('unread-update', { notifies: data.unread || [] })
132
+ this.ctx.trigger('comments-loaded')
144
133
 
145
- this.isFirstLoad = false
134
+ if (this.onAfterLoad) this.onAfterLoad(data)
146
135
  }
147
136
 
148
- /** 初始化加载更多 */
149
- onLoadInit() {
150
- if (this.autoLoadScrollEvent) {
151
- const at = this.autoLoadListenerAt || document
152
- at.removeEventListener('scroll', this.autoLoadScrollEvent)
137
+ /** 分页模式 */
138
+ private refreshPagination(offset: number, total: number) {
139
+ const modePagination = (this.pageMode === 'pagination')
140
+ const modeReadMoreBtn = (this.pageMode === 'read-more')
141
+ const initialized = (modePagination) ? !!this.pagination : !!this.readMoreBtn
142
+
143
+ // 初始化
144
+ if (!initialized) {
145
+ this.initPagination()
153
146
  }
154
147
 
148
+ // 更新
149
+ if (modePagination) this.pagination!.update(offset, total)
150
+ if (modeReadMoreBtn) this.readMoreBtn!.update(offset, total)
151
+ }
152
+
153
+ private initPagination() {
154
+ // 加载更多按钮
155
155
  if (this.pageMode === 'read-more') {
156
- // 阅读更多按钮
157
- const readMoreBtn = new ReadMoreBtn({
158
- onClick: async () => {
159
- const offset = this.offset + this.pageSize
160
- await this.reqComments(offset)
156
+ this.readMoreBtn = new ReadMoreBtn({
157
+ pageSize: this.pageSize,
158
+ onClick: async (o) => {
159
+ await this.fetchComments(o)
161
160
  },
162
161
  })
163
- if (this.readMoreBtn) this.readMoreBtn.$el.replaceWith(readMoreBtn.$el)
164
- else this.$el.append(readMoreBtn.$el)
165
- this.readMoreBtn = readMoreBtn
162
+ this.$el.append(this.readMoreBtn.$el)
166
163
 
167
164
  // 滚动到底部自动加载
168
165
  if (this.conf.pagination?.autoLoad) {
166
+ // 添加滚动事件监听
167
+ const at = this.scrollListenerAt || document
168
+ if (this.autoLoadScrollEvent) at.removeEventListener('scroll', this.autoLoadScrollEvent) // 解除原有
169
169
  this.autoLoadScrollEvent = () => {
170
170
  if (this.pageMode !== 'read-more') return
171
- if (!this.hasMoreComments) return
171
+ if (!this.readMoreBtn) return
172
+
173
+ if (!this.readMoreBtn.hasMore) return
172
174
  if (this.isLoading) return
173
175
 
174
176
  const $target = this.$el.querySelector<HTMLElement>('.atk-list-comments-wrap > .atk-comment-wrap:nth-last-child(3)') // 获取倒数第3个评论元素
175
177
  if (!$target) return
176
- if (Ui.isVisible($target, this.autoLoadListenerAt)) {
177
- // 加载更多
178
- this.readMoreBtn!.click()
178
+ if (Ui.isVisible($target, this.scrollListenerAt)) {
179
+ this.readMoreBtn.click() // 自动点击加载更多按钮
179
180
  }
180
181
  }
181
- const at = this.autoLoadListenerAt || document
182
182
  at.addEventListener('scroll', this.autoLoadScrollEvent)
183
183
  }
184
- } else if (this.pageMode === 'pagination') {
185
- // 分页条
186
- const pagination = new Pagination(this.parentCommentsCount, {
184
+ }
185
+
186
+ // 分页条
187
+ if (this.pageMode === 'pagination') {
188
+ this.pagination = new Pagination((!this.flatMode ? this.data!.total_roots : this.data!.total), {
187
189
  pageSize: this.pageSize,
188
- onChange: async (offset) => {
190
+ onChange: async (o) => {
189
191
  if (this.ctx.conf.editorTravel === true) {
190
192
  this.ctx.trigger('editor-travel-back') // 防止评论框被吞
191
193
  }
192
194
 
193
- await this.reqComments(offset)
195
+ await this.fetchComments(o)
196
+
194
197
  // 滚动到第一个评论的位置
195
- if (this.$parent) {
196
- let topPos = 0
197
- if (!this.autoLoadListenerAt && this.$parent) {
198
- topPos = Utils.getOffset(this.$parent).top
199
- }
200
- const at = this.autoLoadListenerAt || window
198
+ if (this.repositionAt) {
199
+ const at = this.scrollListenerAt || window
201
200
  at.scroll({
202
- top: topPos,
201
+ top: this.repositionAt ? Utils.getOffset(this.repositionAt).top : 0,
203
202
  left: 0,
204
203
  })
205
204
  }
206
205
  }
207
206
  })
208
- if (this.pagination) this.pagination.$el.replaceWith(pagination.$el)
209
- else this.$el.append(pagination.$el)
210
- this.pagination = pagination
207
+
208
+ this.$el.append(this.pagination.$el)
211
209
  }
212
210
  }
213
211
 
214
- public onError(msg: any) {
212
+ /** 错误处理 */
213
+ protected onError(msg: any, offset: number) {
215
214
  msg = String(msg)
216
215
  console.error(msg)
217
- if (this.isFirstLoad || this.pageMode === 'pagination') {
218
- const errEl = Utils.createElement(`<span>${msg},无法获取评论列表数据<br/></span>`)
219
- const retryBtn = Utils.createElement('<span style="cursor:pointer;">点击重新获取</span>')
220
- retryBtn.onclick = () => {
221
- this.reqComments(this.offset)
222
- }
223
- errEl.appendChild(retryBtn)
224
- const adminBtn = Utils.createElement('<span atk-only-admin-show> | <span style="cursor:pointer;">打开控制台</span></span>')
225
- adminBtn.onclick = () => {
226
- this.ctx.trigger('sidebar-show')
227
- }
228
- if (!this.ctx.user.data.isAdmin) {
229
- adminBtn.classList.add('atk-hide')
230
- }
231
- errEl.appendChild(adminBtn)
232
- Ui.setError(this.$el, errEl)
233
- } else {
216
+
217
+ // 加载更多按钮显示错误
218
+ if (offset !== 0 && this.pageMode === 'read-more') {
234
219
  this.readMoreBtn?.showErr(`获取失败`)
220
+ return
235
221
  }
222
+
223
+ // 显示错误对话框
224
+ const $err = Utils.createElement(`<span>${msg},无法获取评论列表数据<br/></span>`)
225
+
226
+ const $retryBtn = Utils.createElement('<span style="cursor:pointer;">点击重新获取</span>')
227
+ $retryBtn.onclick = () => (this.fetchComments(0))
228
+ $err.appendChild($retryBtn)
229
+
230
+ const adminBtn = Utils.createElement('<span atk-only-admin-show> | <span style="cursor:pointer;">打开控制台</span></span>')
231
+ adminBtn.onclick = () => (this.ctx.trigger('sidebar-show'))
232
+ if (!this.ctx.user.data.isAdmin) adminBtn.classList.add('atk-hide')
233
+ $err.appendChild(adminBtn)
234
+
235
+ Ui.setError(this.$el, $err)
236
236
  }
237
237
 
238
238
  /** 刷新界面 */
239
239
  public refreshUI() {
240
- // 评论为空界面
241
- const noComment = this.comments.length <= 0
242
- let noCommentEl = this.$commentsWrap.querySelector<HTMLElement>('.atk-list-no-comment')
243
-
244
- if (noComment) {
245
- if (!noCommentEl) {
246
- noCommentEl = Utils.createElement('<div class="atk-list-no-comment"></div>')
247
- this.$commentsWrap.appendChild(noCommentEl)
248
- noCommentEl.innerHTML = this.noCommentText
240
+ // 无评论
241
+ const isNoComment = this.comments.length <= 0
242
+ let $noComment = this.$commentsWrap.querySelector<HTMLElement>('.atk-list-no-comment')
243
+
244
+ if (isNoComment) {
245
+ if (!$noComment) {
246
+ $noComment = Utils.createElement('<div class="atk-list-no-comment"></div>')
247
+ $noComment.innerHTML = this.noCommentText
248
+ this.$commentsWrap.appendChild($noComment)
249
249
  }
250
+ } else {
251
+ $noComment?.remove()
250
252
  }
251
253
 
252
- if (!noComment && noCommentEl)
253
- noCommentEl.remove()
254
-
255
254
  // 仅管理员显示控制
256
255
  this.ctx.trigger('check-admin-show-el')
257
256
  }
258
257
 
259
258
  /** 创建新评论 */
260
- public createComment(data: CommentData) {
259
+ protected createComment(data: CommentData) {
261
260
  const comment = new Comment(this.ctx, data)
262
261
  comment.afterRender = () => {
263
262
  if (this.renderComment) this.renderComment(comment)
@@ -271,216 +270,126 @@ export default class ListLite extends Component {
271
270
  }
272
271
 
273
272
  /** 导入评论 · 通过请求数据 */
274
- public importComments(rawData: CommentData[]) {
275
- // 查找并导入所有子评论
276
- const queryImportChildren = (parentC: Comment) => {
277
- const children = rawData.filter(o => o.rid === parentC.data.id)
278
- if (children.length === 0) return
273
+ public importComments(srcData: CommentData[]) {
274
+ if (this.flatMode) {
275
+ srcData.forEach((commentData: CommentData) => {
276
+ this.putCommentFlatMode(commentData, srcData, 'append')
277
+ })
278
+ } else {
279
+ this.importCommentsNesting(srcData)
280
+ }
281
+ }
279
282
 
280
- children.forEach((itemData: CommentData) => {
281
- itemData.is_allow_reply = parentC.data.is_allow_reply
282
- const childC = this.createComment(itemData)
283
+ // 导入评论 · 嵌套模式
284
+ private importCommentsNesting(srcData: CommentData[]) {
285
+ // 查找并导入所有子评论
286
+ const loadChildren = (parentC: Comment) => {
287
+ const children = srcData.filter(o => o.rid === parentC.data.id)
288
+ children.forEach((childData: CommentData) => {
289
+ const childC = this.createComment(childData)
283
290
  childC.render()
291
+
292
+ // 插入到父评论中
284
293
  parentC.putChild(childC)
285
- queryImportChildren(childC) // 递归
294
+
295
+ // 递归加载子评论
296
+ loadChildren(childC)
286
297
  })
287
298
  }
288
299
 
289
- // 开始处理 rawData
290
- if (!this.flatMode) {
291
- rawData.filter((o) => o.rid === 0).forEach((rootCommentData: CommentData) => {
292
- if (rootCommentData.is_collapsed) rootCommentData.is_allow_reply = false
293
- const rootComment = this.createComment(rootCommentData)
294
- rootComment.render()
295
- this.comments.push(rootComment) // 将评论导入 comments 总表中
300
+ // 遍历 root 评论
301
+ const rootComments = srcData.filter((o) => o.rid === 0)
302
+ rootComments.forEach((rootData: CommentData) => {
303
+ const rootC = this.createComment(rootData)
304
+ rootC.render()
296
305
 
297
- this.$commentsWrap.appendChild(rootComment.getEl())
298
- rootComment.playFadeInAnim()
306
+ // 显示并播放渐入动画
307
+ this.$commentsWrap.appendChild(rootC.getEl())
308
+ rootC.playFadeInAnim()
299
309
 
300
- queryImportChildren(rootComment)
301
- })
302
- } else {
303
- // 平铺模式
304
- rawData.forEach((commentData: CommentData) => {
305
- this.putCommentFlatMode(commentData, rawData, 'append')
306
- })
307
- }
310
+ // 评论放入 comments 总表中
311
+ this.comments.push(rootC)
308
312
 
309
- this.eachComment(this.comments, (c) => {
310
- if (c.getIsRoot()) this.checkMoreHide(c)
311
- })
313
+ // 加载子评论
314
+ loadChildren(rootC)
312
315
 
313
- this.refreshUI()
314
- this.ctx.trigger('comments-loaded')
316
+ // 限高检测
317
+ rootC.checkHeightLimit()
318
+ })
315
319
  }
316
320
 
317
321
  /** 导入评论 · 平铺模式 */
318
- private putCommentFlatMode(commentItem: CommentData, comments: CommentData[], insertMode: 'append'|'prepend') {
319
- if (commentItem.is_collapsed) commentItem.is_allow_reply = false
320
- const comment = this.createComment(commentItem)
321
- if (commentItem.rid !== 0) {
322
- const rComment = comments.find(o => o.id === commentItem.rid)
322
+ private putCommentFlatMode(cData: CommentData, srcData: CommentData[], insertMode: 'append'|'prepend') {
323
+ if (cData.is_collapsed) cData.is_allow_reply = false
324
+ const comment = this.createComment(cData)
325
+ if (cData.rid !== 0) {
326
+ const rComment = srcData.find(o => o.id === cData.rid)
323
327
  if (rComment) comment.replyTo = rComment
324
328
  }
325
329
  comment.render()
326
330
 
327
331
  // 将评论导入 comments 总表中
328
- if (insertMode === 'append') {
329
- this.comments.push(comment)
330
- } else {
331
- this.comments.unshift(comment)
332
- }
333
-
334
- if (commentItem.visible) {
335
- if (insertMode === 'append') {
336
- this.$commentsWrap.appendChild(comment.getEl())
337
- } else {
338
- this.$commentsWrap.prepend(comment.getEl())
339
- }
332
+ if (insertMode === 'append') this.comments.push(comment)
333
+ if (insertMode === 'prepend') this.comments.unshift(comment)
334
+
335
+ // 可见评论添加到界面
336
+ // 注:不可见评论用于显示 “引用内容”
337
+ if (cData.visible) {
338
+ if (insertMode === 'append') this.$commentsWrap.append(comment.getEl())
339
+ if (insertMode === 'prepend') this.$commentsWrap.prepend(comment.getEl())
340
340
  comment.playFadeInAnim()
341
341
  }
342
342
 
343
- this.checkMoreHide(comment)
343
+ // 平铺评论插入后 · 内容限高检测
344
+ comment.checkHeightLimit()
344
345
 
345
346
  return comment
346
347
  }
347
348
 
348
- /** 插入评论 · 首部添加 */
349
+ /** 新增评论 · 首部添加 */
349
350
  public insertComment(commentData: CommentData) {
350
351
  if (!this.flatMode) {
352
+ // 嵌套模式
351
353
  const comment = this.createComment(commentData)
352
354
  comment.render()
353
355
 
354
356
  if (commentData.rid === 0) {
357
+ // root评论 新增
355
358
  this.$commentsWrap.prepend(comment.getEl())
356
359
  this.comments.unshift(comment)
357
- this.checkMoreHide(comment)
358
360
  } else {
359
- // 子评论
361
+ // 子评论 新增
360
362
  const parent = this.findComment(commentData.rid)
361
363
  if (parent) {
362
364
  parent.putChild(comment)
363
- if (parent.$children) this.removeHideMore(parent.$children)
364
- this.eachComment(parent.children, (c) => {
365
- this.checkMoreHide(c)
365
+
366
+ // 若父评论存在 “子评论部分” 限高,取消限高
367
+ comment.getParents().forEach((p) => {
368
+ if (p.$children) p.heightLimitRemove(p.$children)
366
369
  })
367
370
  }
368
371
  }
369
372
 
373
+ comment.checkHeightLimit()
374
+
370
375
  Ui.scrollIntoView(comment.getEl()) // 滚动到可以见
371
376
  comment.playFadeInAnim() // 播放评论渐出动画
372
377
  } else {
378
+ // 平铺模式
373
379
  const comment = this.putCommentFlatMode(commentData, this.comments.map(c => c.data), 'prepend')
374
- Ui.scrollIntoView(comment.getEl()) // 滚动到可以见
375
- }
376
-
377
- if (this.data) this.data.total += 1 // 评论数增加 1
378
- this.refreshUI() // 更新 list 界面
379
- this.ctx.trigger('comments-loaded')
380
- }
381
-
382
- checkMoreHide(c: Comment) {
383
- const check = () => {
384
- // 子评论内容过多隐藏
385
- this.checkMoreHideEl(c, 'children')
386
-
387
- // 评论内容过多隐藏
388
- this.checkMoreHideEl(c, 'content')
389
-
390
- // 平铺模式回复内容
391
- if (c.$replyTo) this.checkMoreHideEl(c, 'replyTo')
392
- }
393
-
394
- // 执行检测
395
- check()
396
-
397
- // 图片加载完后再检测一次
398
- if (c.$content.querySelectorAll('img').length) {
399
- Utils.onImagesLoaded(c.$content, () => {
400
- check()
401
- })
380
+ Ui.scrollIntoView(comment.getEl()) // 滚动到可见
402
381
  }
403
- }
404
382
 
405
- /** 内容过多,限高显示 */
406
- public checkMoreHideEl(comment: Comment, area: 'children'|'content'|'replyTo', allowHeight = 300) {
407
- const childrenH = this.ctx.conf.heightLimit?.children
408
- const contentH = this.ctx.conf.heightLimit?.content
409
-
410
- if (area === 'children' && (!childrenH)) return
411
- if ((area === 'content' || area === 'replyTo') && (!contentH)) return
412
-
413
- // 限高
414
- if (area === 'children') allowHeight = childrenH || 300
415
- else allowHeight = contentH || 200
416
-
417
- // target 准备
418
- let $target!: HTMLElement
419
- if (area === 'children') $target = comment.$children!
420
- else if (area === 'content') $target = comment.$content!
421
- else if (area === 'replyTo') $target = comment.$replyTo!
422
- if (!$target) return
423
-
424
- // 阅读更多按钮
425
- let $hideMoreOpenBtn = $target.querySelector<HTMLElement>('.atk-more-hide-open-btn')
426
-
427
- const removeHideMore = () => {
428
- $target.classList.remove('atk-comment-more-hide')
429
- if ($hideMoreOpenBtn) $hideMoreOpenBtn.remove()
430
- $target.style.height = ''
431
- $target.style.overflow = ''
432
- }
383
+ // 评论数增加 1
384
+ if (this.data) this.data.total += 1
433
385
 
434
- if (Utils.getHeight($target) > allowHeight) {
435
- //console.log('内容过多,需要折叠', $target)
436
- $target.classList.add('atk-comment-more-hide')
437
- $target.style.height = `${allowHeight}px`
438
- $target.style.overflow = 'hidden'
439
- if (!$hideMoreOpenBtn) {
440
- $hideMoreOpenBtn = Utils.createElement(`<div class="atk-more-hide-open-btn">阅读更多</span>`)
441
- $hideMoreOpenBtn.onclick = (e) => {
442
- e.stopPropagation()
443
- removeHideMore()
444
- // 阅读更多过后,再检查隐藏
445
- if (comment.getIsRoot()) {
446
- const children = comment.getChildren()
447
- if (children.length > 1) {
448
- this.eachComment(children, (c) => {
449
- this.checkMoreHideEl(c, 'content', contentH || 200)
450
- })
451
- }
452
- }
453
- }
454
- $target.append($hideMoreOpenBtn)
455
- }
456
- }
457
- }
458
-
459
- public removeHideMore($target: HTMLElement) {
460
- const $hideMoreOpenBtn = $target.querySelector<HTMLElement>('.atk-more-hide-open-btn')
461
- $target.classList.remove('atk-comment-more-hide')
462
- if ($hideMoreOpenBtn) $hideMoreOpenBtn.remove()
463
- $target.style.height = ''
464
- $target.style.overflow = ''
465
- }
466
-
467
- /** 获取评论总数 (包括子评论) */
468
- get commentsCount(): number {
469
- return Number(this.data?.total) || 0
470
- }
471
-
472
- get parentCommentsCount() {
473
- return Number(this.data?.total_parents) || 0
474
- }
475
-
476
- /** 是否还有更多的评论 */
477
- get hasMoreComments(): boolean {
478
- if (!this.data) return false
479
- return (!this.flatMode ? this.data.total_parents : this.data.total) > (this.offset + this.pageSize)
386
+ // 评论新增后
387
+ this.refreshUI()
388
+ this.ctx.trigger('comments-loaded')
480
389
  }
481
390
 
482
391
  /** 遍历操作 Comment (包括子评论) */
483
- public eachComment(commentList: Comment[], action: (comment: Comment, levelList: Comment[]) => boolean|void) {
392
+ protected eachComment(commentList: Comment[], action: (comment: Comment, levelList: Comment[]) => boolean|void) {
484
393
  if (commentList.length === 0) return
485
394
  commentList.every((item) => {
486
395
  if (action(item, commentList) === false) return false
@@ -490,7 +399,7 @@ export default class ListLite extends Component {
490
399
  }
491
400
 
492
401
  /** 查找评论项 */
493
- public findComment(id: number, src?: Comment[]): Comment|null {
402
+ protected findComment(id: number, src?: Comment[]): Comment|null {
494
403
  if (!src) src = this.comments
495
404
  let comment: Comment|null = null
496
405
  this.eachComment(src, (item) => {
@@ -504,13 +413,6 @@ export default class ListLite extends Component {
504
413
  return comment
505
414
  }
506
415
 
507
- /** 获取评论总数 */
508
- public getCommentCount(): number {
509
- let count = 0
510
- this.eachComment(this.comments, () => { count++ })
511
- return count
512
- }
513
-
514
416
  /** 删除评论 */
515
417
  public deleteComment(comment: number|Comment) {
516
418
  let findComment: Comment|null
@@ -540,11 +442,12 @@ export default class ListLite extends Component {
540
442
  this.comments = []
541
443
  }
542
444
 
445
+ /** 更新未读数据 */
543
446
  public updateUnread(notifies: NotifyData[]) {
544
447
  this.unread = notifies
545
448
 
546
449
  // 高亮评论
547
- if (this.unreadHighlight) {
450
+ if (this.unreadHighlight === true) {
548
451
  this.eachComment(this.comments, (comment) => {
549
452
  const notify = this.unread.find(o => o.comment_id === comment.data.id)
550
453
  if (notify) {
@@ -563,7 +466,8 @@ export default class ListLite extends Component {
563
466
  }
564
467
  }
565
468
 
566
- public apiVersionCheck(versionData: ApiVersionData): boolean {
469
+ /** 前端版本检测 */
470
+ public versionCheck(versionData: ApiVersionData): boolean {
567
471
  const needVersion = versionData?.fe_min_version || '0.0.0'
568
472
  const needUpdate = Utils.versionCompare(needVersion, ARTALK_VERSION) === 1
569
473
  if (needUpdate) {
@@ -577,7 +481,7 @@ export default class ListLite extends Component {
577
481
  ignoreBtn.onclick = () => {
578
482
  Ui.setError(this.ctx, null)
579
483
  this.ctx.conf.versionCheck = false
580
- this.reqComments(0)
484
+ this.fetchComments(0)
581
485
  }
582
486
  errEl.append(ignoreBtn)
583
487
  Ui.setError(this.ctx, errEl, '<span class="atk-warn-title">Artalk Warn</span>')