artalk 2.2.11 → 2.3.1

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 (78) hide show
  1. package/dist/Artalk.css +1 -1
  2. package/dist/Artalk.es.js +1872 -1143
  3. package/dist/Artalk.iife.js +1 -1
  4. package/dist/Artalk.js +1 -1
  5. package/dist/ArtalkLite.css +1 -0
  6. package/dist/ArtalkLite.es.js +5123 -0
  7. package/dist/ArtalkLite.iife.js +1 -0
  8. package/dist/ArtalkLite.js +1 -0
  9. package/index.html +5 -3
  10. package/package.json +7 -5
  11. package/src/api/index.ts +24 -4
  12. package/src/api/request.ts +50 -81
  13. package/src/artalk.ts +131 -127
  14. package/src/comment/actions.ts +86 -0
  15. package/src/{components/html → comment}/comment.html +1 -1
  16. package/src/comment/comment.ts +191 -0
  17. package/src/comment/index.ts +3 -0
  18. package/src/comment/render.ts +425 -0
  19. package/src/components/action-btn.ts +6 -3
  20. package/src/components/dialog.ts +7 -3
  21. package/src/components/read-more-btn.ts +6 -2
  22. package/src/context.ts +204 -13
  23. package/src/defaults.ts +15 -4
  24. package/src/editor/editor.html +18 -0
  25. package/src/editor/editor.ts +419 -0
  26. package/src/editor/index.ts +3 -0
  27. package/src/editor/plugs/editor-plug.ts +51 -0
  28. package/src/{components/editor-plugs → editor/plugs}/emoticons-plug.less +2 -1
  29. package/src/{components/editor-plugs → editor/plugs}/emoticons-plug.ts +65 -63
  30. package/src/editor/plugs/header-input-plug.ts +78 -0
  31. package/src/{components/editor-plugs → editor/plugs}/preview-plug.less +0 -0
  32. package/src/editor/plugs/preview-plug.ts +40 -0
  33. package/src/editor/plugs/upload-plug.ts +124 -0
  34. package/src/i18n/en.ts +78 -0
  35. package/src/i18n/index.ts +21 -0
  36. package/src/i18n/zh-CN.ts +82 -0
  37. package/src/layer/index.ts +4 -0
  38. package/src/{components → layer}/layer.ts +9 -9
  39. package/src/{components/html → layer}/sidebar-layer.html +0 -0
  40. package/src/{components → layer}/sidebar-layer.ts +17 -47
  41. package/src/lib/checker/admin-checker.ts +7 -7
  42. package/src/lib/checker/captcha-checker.ts +5 -6
  43. package/src/lib/checker/index.ts +22 -17
  44. package/src/lib/component.ts +7 -2
  45. package/src/lib/stat.ts +63 -0
  46. package/src/lib/ui.ts +3 -10
  47. package/src/lib/user.ts +20 -3
  48. package/src/lib/utils.ts +23 -9
  49. package/src/list/index.ts +3 -0
  50. package/src/{components → list}/list-lite.ts +83 -111
  51. package/src/{components → list}/list-nest.ts +0 -0
  52. package/src/{components/html → list}/list.html +3 -6
  53. package/src/{components → list}/list.ts +44 -42
  54. package/src/style/_variables.less +1 -2
  55. package/src/style/comment.less +26 -13
  56. package/src/style/editor.less +22 -14
  57. package/src/style/list.less +6 -9
  58. package/src/style/main.less +9 -8
  59. package/src/style/pagination.less +1 -1
  60. package/test/ArtalkCount.html +16 -0
  61. package/test/ArtalkI18n.html +15 -0
  62. package/test/ArtalkLite.html +15 -0
  63. package/types/artalk-config.d.ts +50 -32
  64. package/types/context.d.ts +128 -0
  65. package/types/event.d.ts +6 -66
  66. package/types/global.d.ts +1 -0
  67. package/types/plug.d.ts +4 -0
  68. package/{vite-common.config.ts → vite-base.config.ts} +6 -4
  69. package/vite-demo.config.ts +2 -4
  70. package/vite-lite.config.ts +22 -0
  71. package/vite.config.ts +9 -18
  72. package/src/components/_component.ts.txt +0 -10
  73. package/src/components/comment.ts +0 -605
  74. package/src/components/editor-plugs/_example-plug.ts.txt +0 -29
  75. package/src/components/editor-plugs/editor-plug.ts +0 -20
  76. package/src/components/editor-plugs/preview-plug.ts +0 -48
  77. package/src/components/editor.ts +0 -604
  78. package/src/components/html/editor.html +0 -18
@@ -1,25 +1,21 @@
1
- import { ListData, CommentData, NotifyData, ApiVersionData } from '~/types/artalk-data'
2
- import Context from '../context'
1
+ import { ListData, CommentData, NotifyData } from '~/types/artalk-data'
2
+ import Context from '~/types/context'
3
3
  import Component from '../lib/component'
4
4
  import * as Utils from '../lib/utils'
5
5
  import * as Ui from '../lib/ui'
6
- import Api from '../api'
7
- import Comment from './comment'
8
- import Pagination from './pagination'
9
- import ReadMoreBtn from './read-more-btn'
6
+ import Comment from '../comment'
7
+ import Pagination from '../components/pagination'
8
+ import ReadMoreBtn from '../components/read-more-btn'
10
9
  import * as ListNest from './list-nest'
11
10
  import { backendMinVersion } from '../../package.json'
12
11
 
13
12
  export default class ListLite extends Component {
14
13
  protected $commentsWrap: HTMLElement
15
14
 
16
- protected commentList: Comment[] = [] // Note: 无层级结构 + 无须排列
17
- protected get commentDataList() { return this.commentList.map(c => c.data) }
18
-
19
15
  protected data?: ListData
20
16
  protected isLoading: boolean = false
21
17
 
22
- public noCommentText = '无内容' // 无评论时显示
18
+ public noCommentText: string // 无评论时显示
23
19
 
24
20
  /** 平铺模式 */
25
21
  public flatMode = false
@@ -56,7 +52,7 @@ export default class ListLite extends Component {
56
52
  this.$commentsWrap = this.$el.querySelector('.atk-list-comments-wrap')!
57
53
 
58
54
  // 评论为空时显示字符
59
- if (ctx.conf.noComment) this.noCommentText = ctx.conf.noComment
55
+ this.noCommentText = ctx.conf.noComment || ctx.$t('noComment')
60
56
 
61
57
  // 嵌套排序方式
62
58
  this.nestSortBy = this.ctx.conf.nestSort || 'DATE_ASC'
@@ -65,12 +61,21 @@ export default class ListLite extends Component {
65
61
  window.setInterval(() => {
66
62
  this.$el.querySelectorAll<HTMLElement>('[data-atk-comment-date]').forEach(el => {
67
63
  const date = el.getAttribute('data-atk-comment-date')
68
- el.innerText = Utils.timeAgo(new Date(Number(date)))
64
+ el.innerText = Utils.timeAgo(new Date(Number(date)), this.ctx)
69
65
  })
70
66
  }, 30 * 1000) // 30s 更新一次
67
+ }
68
+
69
+ public getData() {
70
+ return this.data
71
+ }
71
72
 
72
- // 事件监听
73
- this.ctx.on('unread-update', (data) => (this.updateUnread(data.notifies)))
73
+ public clearData() {
74
+ this.data = undefined
75
+ }
76
+
77
+ public getCommentsWrapEl() {
78
+ return this.$commentsWrap
74
79
  }
75
80
 
76
81
  /** 评论获取 */
@@ -93,15 +98,15 @@ export default class ListLite extends Component {
93
98
  showLoading()
94
99
 
95
100
  // 事件通知(开始加载评论)
96
- this.ctx.trigger('comments-load')
101
+ this.ctx.trigger('list-load')
97
102
 
98
103
  // 清空评论(加载按钮)
99
- if (this.pageMode === 'read-more' && offset === 0) { this.clearAllComments() }
104
+ if (this.pageMode === 'read-more' && offset === 0) { this.ctx.clearAllComments() }
100
105
 
101
106
  // 请求评论数据
102
107
  let listData: ListData
103
108
  try {
104
- listData = await new Api(this.ctx).get(offset, this.pageSize, this.flatMode, this.paramsEditor)
109
+ listData = await this.ctx.getApi().get(offset, this.pageSize, this.flatMode, this.paramsEditor)
105
110
  } catch (e: any) {
106
111
  this.onError(e.msg || String(e), offset, e.data)
107
112
  throw e
@@ -125,14 +130,14 @@ export default class ListLite extends Component {
125
130
 
126
131
  protected onLoad(data: ListData, offset: number) {
127
132
  // 清空评论(分页条)
128
- if (this.pageMode === 'pagination') { this.clearAllComments() }
133
+ if (this.pageMode === 'pagination') { this.ctx.clearAllComments() }
129
134
 
130
135
  this.data = data
131
136
 
132
137
  // 版本检测
133
138
  const feMinVersion = data.api_version?.fe_min_version || '0.0.0'
134
- if (this.ctx.conf.versionCheck && this.versionCheck('前端', feMinVersion, ARTALK_VERSION)) return
135
- if (this.ctx.conf.versionCheck && this.versionCheck('后端', backendMinVersion, data.api_version?.version)) return
139
+ if (this.ctx.conf.versionCheck && this.versionCheck('frontend', feMinVersion, ARTALK_VERSION)) return
140
+ if (this.ctx.conf.versionCheck && this.versionCheck('backend', backendMinVersion, data.api_version?.version)) return
136
141
 
137
142
  // 图片上传功能
138
143
  if (data.conf && typeof data.conf.img_upload === "boolean") {
@@ -148,8 +153,8 @@ export default class ListLite extends Component {
148
153
  // 加载后事件
149
154
  this.refreshUI()
150
155
 
151
- this.ctx.trigger('unread-update', { notifies: data.unread || [] })
152
- this.ctx.trigger('comments-loaded')
156
+ this.ctx.updateNotifies(data.unread || [])
157
+ this.ctx.trigger('list-loaded')
153
158
  this.ctx.trigger('conf-updated')
154
159
 
155
160
  if (this.onAfterLoad) this.onAfterLoad(data)
@@ -179,11 +184,12 @@ export default class ListLite extends Component {
179
184
  onClick: async (o) => {
180
185
  await this.fetchComments(o)
181
186
  },
187
+ text: this.ctx.$t('loadMore'),
182
188
  })
183
189
  this.$el.append(this.readMoreBtn.$el)
184
190
 
185
191
  // 滚动到底部自动加载
186
- if (this.conf.pagination?.autoLoad) {
192
+ if (this.conf.pagination.autoLoad) {
187
193
  // 添加滚动事件监听
188
194
  const at = this.scrollListenerAt || document
189
195
  if (this.autoLoadScrollEvent) at.removeEventListener('scroll', this.autoLoadScrollEvent) // 解除原有
@@ -210,7 +216,7 @@ export default class ListLite extends Component {
210
216
  pageSize: this.pageSize,
211
217
  onChange: async (o) => {
212
218
  if (this.ctx.conf.editorTravel === true) {
213
- this.ctx.trigger('editor-travel-back') // 防止评论框被吞
219
+ this.ctx.editorTravelBack() // 防止评论框被吞
214
220
  }
215
221
 
216
222
  await this.fetchComments(o)
@@ -237,14 +243,14 @@ export default class ListLite extends Component {
237
243
 
238
244
  // 加载更多按钮显示错误
239
245
  if (offset !== 0 && this.pageMode === 'read-more') {
240
- this.readMoreBtn?.showErr(`获取失败`)
246
+ this.readMoreBtn?.showErr(this.$t('loadFail'))
241
247
  return
242
248
  }
243
249
 
244
250
  // 显示错误对话框
245
- const $err = Utils.createElement(`<span>${msg},无法获取评论列表数据<br/></span>`)
251
+ const $err = Utils.createElement(`<span>${msg},${this.$t('listLoadFailMsg')}<br/></span>`)
246
252
 
247
- const $retryBtn = Utils.createElement('<span style="cursor:pointer;">点击重新获取</span>')
253
+ const $retryBtn = Utils.createElement(`<span style="cursor:pointer;">${this.$t('listRetry')}</span>`)
248
254
  $retryBtn.onclick = () => (this.fetchComments(0))
249
255
  $err.appendChild($retryBtn)
250
256
 
@@ -264,9 +270,9 @@ export default class ListLite extends Component {
264
270
  sidebarView = `sites|${JSON.stringify(viewLoadParam)}`
265
271
  }
266
272
 
267
- adminBtn.onclick = () => (this.ctx.trigger('sidebar-show', {
268
- view: sidebarView
269
- }))
273
+ adminBtn.onclick = () => this.ctx.showSidebar({
274
+ view: sidebarView as any
275
+ })
270
276
 
271
277
  Ui.setError(this.$el, $err)
272
278
  }
@@ -274,7 +280,7 @@ export default class ListLite extends Component {
274
280
  /** 刷新界面 */
275
281
  public refreshUI() {
276
282
  // 无评论
277
- const isNoComment = this.commentList.length <= 0
283
+ const isNoComment = this.ctx.getCommentList().length <= 0
278
284
  let $noComment = this.$commentsWrap.querySelector<HTMLElement>('.atk-list-no-comment')
279
285
 
280
286
  if (isNoComment) {
@@ -288,25 +294,35 @@ export default class ListLite extends Component {
288
294
  }
289
295
 
290
296
  // 仅管理员显示控制
291
- this.ctx.trigger('check-admin-show-el')
297
+ this.ctx.checkAdminShowEl()
298
+ }
299
+
300
+ /** 重新加载列表 */
301
+ public reload() {
302
+ this.fetchComments(0)
292
303
  }
293
304
 
294
305
  /** 创建新评论 */
295
306
  protected createComment(cData: CommentData, ctxData?: CommentData[]) {
296
- if (!ctxData) ctxData = this.commentDataList
307
+ if (!ctxData) ctxData = this.ctx.getCommentDataList()
308
+
309
+ const comment = new Comment(this.ctx, cData, {
310
+ isFlatMode: this.flatMode,
311
+ afterRender: () => {
312
+ if (this.renderComment) this.renderComment(comment)
313
+ },
314
+ onDelete: (c: Comment) => {
315
+ this.ctx.deleteComment(c)
316
+ this.refreshUI()
317
+ },
318
+ replyTo: (cData.rid ? ctxData.find(c => c.id === cData.rid) : undefined)
319
+ })
297
320
 
298
- const comment = new Comment(this.ctx, cData)
299
- comment.flatMode = this.flatMode
300
- comment.afterRender = () => {
301
- if (this.renderComment) this.renderComment(comment)
302
- }
303
- comment.onDelete = (c) => {
304
- this.deleteComment(c)
305
- this.refreshUI()
306
- }
321
+ // 渲染元素
322
+ comment.render()
307
323
 
308
- // 子评论查找回复对象
309
- comment.replyTo = (cData.rid ? ctxData.find(c => c.id === cData.rid) : undefined)
324
+ // 放入 comment 总表中
325
+ this.ctx.getCommentList().push(comment)
310
326
 
311
327
  return comment
312
328
  }
@@ -328,14 +344,10 @@ export default class ListLite extends Component {
328
344
  const rootNodes = ListNest.makeNestCommentNodeList(srcData, this.nestSortBy, this.conf.nestMax)
329
345
  rootNodes.forEach((rootNode: ListNest.CommentNode) => {
330
346
  const rootC = this.createComment(rootNode.comment, srcData)
331
- rootC.render()
332
347
 
333
348
  // 显示并播放渐入动画
334
349
  this.$commentsWrap.appendChild(rootC.getEl())
335
- rootC.playFadeInAnim()
336
-
337
- // 评论放入 comments 总表中
338
- this.commentList.push(rootC)
350
+ rootC.getRender().playFadeAnim()
339
351
 
340
352
  // 加载子评论
341
353
  const that = this
@@ -343,20 +355,17 @@ export default class ListLite extends Component {
343
355
  parentNode.children.forEach((node: ListNest.CommentNode) => {
344
356
  const childD = node.comment
345
357
  const childC = that.createComment(childD, srcData)
346
- childC.render()
347
358
 
348
359
  // 插入到父评论中
349
360
  parentC.putChild(childC)
350
361
 
351
- that.commentList.push(childC)
352
-
353
362
  // 递归加载子评论
354
363
  loadChildren(childC, node)
355
364
  })
356
365
  })(rootC, rootNode)
357
366
 
358
367
  // 限高检测
359
- rootC.checkHeightLimit()
368
+ rootC.getRender().checkHeightLimit()
360
369
  })
361
370
  }
362
371
 
@@ -364,21 +373,17 @@ export default class ListLite extends Component {
364
373
  private putCommentFlatMode(cData: CommentData, ctxData: CommentData[], insertMode: 'append'|'prepend') {
365
374
  if (cData.is_collapsed) cData.is_allow_reply = false
366
375
  const comment = this.createComment(cData, ctxData)
367
- comment.render()
368
-
369
- // 将评论导入 comments 总表中
370
- this.commentList.push(comment)
371
376
 
372
377
  // 可见评论添加到界面
373
378
  // 注:不可见评论用于显示 “引用内容”
374
379
  if (cData.visible) {
375
380
  if (insertMode === 'append') this.$commentsWrap.append(comment.getEl())
376
381
  if (insertMode === 'prepend') this.$commentsWrap.prepend(comment.getEl())
377
- comment.playFadeInAnim()
382
+ comment.getRender().playFadeAnim()
378
383
  }
379
384
 
380
385
  // 平铺评论插入后 · 内容限高检测
381
- comment.checkHeightLimit()
386
+ comment.getRender().checkHeightLimit()
382
387
 
383
388
  return comment
384
389
  }
@@ -391,30 +396,27 @@ export default class ListLite extends Component {
391
396
 
392
397
  if (commentData.rid === 0) {
393
398
  // root评论 新增
394
- comment.render()
395
399
  this.$commentsWrap.prepend(comment.getEl())
396
- this.commentList.push(comment)
397
400
  } else {
398
401
  // 子评论 新增
399
- const parent = this.findComment(commentData.rid)
402
+ const parent = this.ctx.findComment(commentData.rid)
400
403
  if (parent) {
401
- comment.render()
402
404
  parent.putChild(comment, (this.nestSortBy === 'DATE_ASC' ? 'append' : 'prepend'))
403
405
 
404
406
  // 若父评论存在 “子评论部分” 限高,取消限高
405
407
  comment.getParents().forEach((p) => {
406
- if (p.$children) p.heightLimitRemove(p.$children)
408
+ p.getRender().heightLimitRemoveForChildren()
407
409
  })
408
410
  }
409
411
  }
410
412
 
411
- comment.checkHeightLimit()
413
+ comment.getRender().checkHeightLimit()
412
414
 
413
415
  Ui.scrollIntoView(comment.getEl()) // 滚动到可以见
414
- comment.playFadeInAnim() // 播放评论渐出动画
416
+ comment.getRender().playFadeAnim() // 播放评论渐出动画
415
417
  } else {
416
418
  // 平铺模式
417
- const comment = this.putCommentFlatMode(commentData, this.commentDataList, 'prepend')
419
+ const comment = this.putCommentFlatMode(commentData, this.ctx.getCommentDataList(), 'prepend')
418
420
  Ui.scrollIntoView(comment.getEl()) // 滚动到可见
419
421
  }
420
422
 
@@ -423,36 +425,8 @@ export default class ListLite extends Component {
423
425
 
424
426
  // 评论新增后
425
427
  this.refreshUI()
426
- this.ctx.trigger('comments-loaded')
427
- }
428
-
429
- /** 查找评论项 */
430
- protected findComment(id: number): Comment|undefined {
431
- return this.commentList.find(c => c.data.id === id)
432
- }
433
-
434
- /** 删除评论 */
435
- public deleteComment(_comment: number|Comment) {
436
- let comment: Comment
437
- if (typeof _comment === 'number') {
438
- const findComment = this.findComment(_comment)
439
- if (!findComment) throw Error(`未找到评论 ${_comment}`)
440
- comment = findComment
441
- } else comment = _comment
442
-
443
- comment.getEl().remove()
444
- this.commentList.splice(this.commentList.indexOf(comment), 1)
445
-
446
- if (this.data) this.data.total -= 1 // 评论数减 1
447
-
448
- this.refreshUI()
449
- }
450
-
451
- /** 清空所有评论 */
452
- public clearAllComments() {
453
- this.$commentsWrap.innerHTML = ''
454
- this.data = undefined
455
- this.commentList = []
428
+ this.ctx.trigger('list-loaded')
429
+ this.ctx.trigger('list-inserted', commentData)
456
430
  }
457
431
 
458
432
  /** 更新未读数据 */
@@ -461,33 +435,31 @@ export default class ListLite extends Component {
461
435
 
462
436
  // 高亮评论
463
437
  if (this.unreadHighlight === true) {
464
- this.commentList.forEach((comment) => {
465
- const notify = this.unread.find(o => o.comment_id === comment.data.id)
438
+ this.ctx.getCommentList().forEach((comment) => {
439
+ const notify = this.unread.find(o => o.comment_id === comment.getID())
466
440
  if (notify) {
467
- comment.setUnread(true)
468
- comment.setOpenURL(notify.read_link)
469
- comment.openEvt = () => {
470
- this.unread = this.unread.filter(o => o.comment_id !== comment.data.id) // remove
471
- this.ctx.trigger('unread-update', {
472
- notifies: this.unread
473
- })
474
- }
441
+ comment.getRender().setUnread(true)
442
+ comment.getRender().setOpenAction(() => {
443
+ window.open(notify.read_link)
444
+ this.unread = this.unread.filter(o => o.comment_id !== comment.getID()) // remove
445
+ this.ctx.updateNotifies(this.unread)
446
+ })
475
447
  } else {
476
- comment.setUnread(false)
448
+ comment.getRender().setUnread(false)
477
449
  }
478
450
  })
479
451
  }
480
452
  }
481
453
 
482
454
  /** 版本检测 */
483
- public versionCheck(name: '前端'|'后端', needVersion: string, curtVersion: string): boolean {
455
+ public versionCheck(name: 'frontend'|'backend', needVersion: string, curtVersion: string): boolean {
484
456
  const needUpdate = Utils.versionCompare(needVersion, curtVersion) === 1
485
457
  if (needUpdate) {
486
458
  // 需要更新
487
- const errEl = Utils.createElement(`<div>Artalk ${name}版本已过时,请更新以获得完整体验<br/>`
459
+ const errEl = Utils.createElement(`<div>Artalk ${this.$t(name)}版本已过时,请更新以获得完整体验<br/>`
488
460
  + `如果你是管理员,请前往 “<a href="https://artalk.js.org/" target="_blank">官方文档</a>” 获得帮助`
489
461
  + `<br/><br/>`
490
- + `<span style="color: var(--at-color-meta);">当前${name}版本 ${curtVersion},需求版本 >= ${needVersion}</span><br/><br/>`
462
+ + `<span style="color: var(--at-color-meta);">当前${this.$t(name)}版本 ${curtVersion},需求版本 >= ${needVersion}</span><br/><br/>`
491
463
  + `</div>`)
492
464
  const ignoreBtn = Utils.createElement('<span style="cursor:pointer;">忽略</span>')
493
465
  ignoreBtn.onclick = () => {
File without changes
@@ -1,16 +1,13 @@
1
1
  <div class="atk-list">
2
2
  <div class="atk-list-header">
3
3
  <div class="atk-comment-count">
4
- <div class="atk-text">
5
- <span class="atk-comment-count-num">0</span>
6
- 条评论
7
- </div>
4
+ <div class="atk-text"></div>
8
5
  </div>
9
6
  <div class="atk-right-action">
10
- <span data-action="admin-close-comment" class="atk-hide" atk-only-admin-show>关闭评论</span>
7
+ <span data-action="admin-close-comment" class="atk-hide" atk-only-admin-show></span>
11
8
  <span data-action="open-sidebar" class="atk-hide atk-on">
12
9
  <span class="atk-unread-badge" style="display: none;"></span>
13
- <div class="atk-text">通知中心</div>
10
+ <div class="atk-text"></div>
14
11
  </span>
15
12
  </div>
16
13
  </div>
@@ -1,11 +1,10 @@
1
1
  import '../style/list.less'
2
2
 
3
- import { ListData } from '~/types/artalk-data'
4
- import Context from '../context'
3
+ import { ListData, NotifyData } from '~/types/artalk-data'
4
+ import Context from '~/types/context'
5
5
  import * as Utils from '../lib/utils'
6
6
  import * as Ui from '../lib/ui'
7
- import Api from '../api'
8
- import ListHTML from './html/list.html?raw'
7
+ import ListHTML from './list.html?raw'
9
8
  import ListLite from './list-lite'
10
9
 
11
10
  export default class List extends ListLite {
@@ -13,6 +12,7 @@ export default class List extends ListLite {
13
12
  private $openSidebarBtn!: HTMLElement
14
13
  private $unreadBadge!: HTMLElement
15
14
  private $commentCount!: HTMLElement
15
+ private $commentCountNum!: HTMLElement
16
16
  private $dropdownWrap?: HTMLElement
17
17
 
18
18
  constructor (ctx: Context) {
@@ -25,24 +25,29 @@ export default class List extends ListLite {
25
25
  this.$el = el
26
26
 
27
27
  // 平铺模式
28
- let flatMode = false
29
- if (this.ctx.conf.flatMode === 'auto') {
28
+ if (this.ctx.conf.flatMode === true) {
29
+ this.flatMode = true // 遵循配置总是开启平铺模式
30
+ } else if (this.conf.nestMax && this.conf.nestMax <= 1) {
31
+ this.flatMode = true // 嵌套层数值无效时,强制开启平铺模式
32
+ } else if (this.ctx.conf.flatMode === 'auto') {
33
+ // 自动判断启用平铺模式
30
34
  if (window.matchMedia("(max-width: 768px)").matches)
31
- flatMode = true
32
- } else if (this.ctx.conf.flatMode === true) {
33
- flatMode = true
35
+ this.flatMode = true
34
36
  }
35
- this.flatMode = flatMode
36
37
 
37
38
  // 分页模式
38
- this.pageMode = this.conf.pagination?.readMore ? 'read-more' : 'pagination'
39
- this.pageSize = this.conf.pagination?.pageSize || 20
39
+ this.pageMode = this.conf.pagination.readMore ? 'read-more' : 'pagination'
40
+ this.pageSize = this.conf.pagination.pageSize || 20
40
41
  this.repositionAt = this.$el
41
42
 
42
43
  // 操作按钮
43
44
  this.initListActionBtn()
44
45
 
45
46
  this.$commentCount = this.$el.querySelector('.atk-comment-count')!
47
+ this.$commentCount.innerHTML = this.$t('counter', {
48
+ count: '<span class="atk-comment-count-num">0</span>',
49
+ })
50
+ this.$commentCountNum = this.$commentCount.querySelector('.atk-comment-count-num')!
46
51
 
47
52
  // 评论列表排序 Dropdown 下拉选择层
48
53
  if (this.ctx.conf.listSort) {
@@ -53,13 +58,6 @@ export default class List extends ListLite {
53
58
  this.$el.querySelector<HTMLElement>('.atk-copyright')!.innerHTML = `Powered By <a href="https://artalk.js.org" target="_blank" title="Artalk v${ARTALK_VERSION}">Artalk</a>`
54
59
 
55
60
  // event listen
56
- this.ctx.on('list-reload', () => (this.fetchComments(0))) // 刷新评论
57
- this.ctx.on('list-refresh-ui', () => (this.refreshUI()))
58
- this.ctx.on('list-import', (data) => (this.importComments(data)))
59
- this.ctx.on('list-insert', (data) => (this.insertComment(data)))
60
- this.ctx.on('list-delete', (comment) => (this.deleteComment(comment.id)))
61
- this.ctx.on('list-update', (updateData) => { updateData(this.data);this.refreshUI() } )
62
- this.ctx.on('unread-update', (data) => (this.showUnreadBadge(data.notifies?.length || 0)))
63
61
  }
64
62
 
65
63
  private initListActionBtn() {
@@ -69,7 +67,7 @@ export default class List extends ListLite {
69
67
  this.$unreadBadge = this.$el.querySelector('.atk-unread-badge')!
70
68
 
71
69
  this.$openSidebarBtn.addEventListener('click', () => {
72
- this.ctx.trigger('sidebar-show')
70
+ this.ctx.showSidebar()
73
71
  })
74
72
 
75
73
  // 关闭评论按钮
@@ -85,7 +83,7 @@ export default class List extends ListLite {
85
83
  public refreshUI() {
86
84
  super.refreshUI()
87
85
 
88
- this.$el.querySelector<HTMLElement>('.atk-comment-count-num')!.innerText = String(Number(this.data?.total) || 0)
86
+ this.$commentCountNum.innerText = String(Number(this.data?.total) || 0)
89
87
 
90
88
  // 已输入个人信息
91
89
  if (!!this.ctx.user.data.nick && !!this.ctx.user.data.email) {
@@ -95,16 +93,17 @@ export default class List extends ListLite {
95
93
  }
96
94
 
97
95
  // 仅管理员显示控制
98
- this.ctx.trigger('check-admin-show-el')
99
- this.$openSidebarBtn.querySelector<HTMLElement>('.atk-text')!.innerText = (!this.ctx.user.data.isAdmin) ? '通知中心' : '控制中心'
96
+ this.ctx.checkAdminShowEl()
97
+ this.$openSidebarBtn.querySelector<HTMLElement>('.atk-text')!
98
+ .innerText = (!this.ctx.user.data.isAdmin) ? this.$t('msgCenter') : this.$t('ctrlCenter')
100
99
 
101
100
  // 关闭评论
102
101
  if (!!this.data && !!this.data.page && this.data.page.admin_only === true) {
103
- this.ctx.trigger('editor-close')
104
- this.$closeCommentBtn.innerHTML = '打开评论'
102
+ this.ctx.editorClose()
103
+ this.$closeCommentBtn.innerHTML = this.$t('openComment')
105
104
  } else {
106
- this.ctx.trigger('editor-open')
107
- this.$closeCommentBtn.innerHTML = '关闭评论'
105
+ this.ctx.editorOpen()
106
+ this.$closeCommentBtn.innerHTML = this.$t('closeComment')
108
107
  }
109
108
  }
110
109
 
@@ -116,7 +115,7 @@ export default class List extends ListLite {
116
115
 
117
116
  // 防止评论框被吞
118
117
  if (this.ctx.conf.editorTravel === true) {
119
- this.ctx.trigger('editor-travel-back')
118
+ this.ctx.editorTravelBack()
120
119
  }
121
120
  }
122
121
 
@@ -133,7 +132,7 @@ export default class List extends ListLite {
133
132
  }
134
133
  if (!commentId) return
135
134
 
136
- const comment = this.findComment(commentId)
135
+ const comment = this.ctx.findComment(commentId)
137
136
  if (!comment) { // 若找不到评论
138
137
  // 自动翻页
139
138
  if (this.readMoreBtn) this.readMoreBtn.click()
@@ -144,18 +143,16 @@ export default class List extends ListLite {
144
143
  // 已阅 API
145
144
  const notifyKey = Utils.getQueryParam('atk_notify_key')
146
145
  if (notifyKey) {
147
- new Api(this.ctx).markRead(notifyKey)
146
+ this.ctx.getApi().markRead(notifyKey)
148
147
  .then(() => {
149
148
  this.unread = this.unread.filter(o => o.comment_id !== commentId)
150
- this.ctx.trigger('unread-update', {
151
- notifies: this.unread
152
- })
149
+ this.updateUnread(this.unread)
153
150
  })
154
151
  }
155
152
 
156
153
  // 若父评论存在 “子评论部分” 限高,取消限高
157
154
  comment.getParents().forEach((p) => {
158
- if (p.$children) p.heightLimitRemove(p.$children)
155
+ p.getRender().heightLimitRemoveForChildren()
159
156
  })
160
157
 
161
158
  const goTo = () => {
@@ -177,18 +174,18 @@ export default class List extends ListLite {
177
174
  public adminPageEditSave () {
178
175
  if (!this.data || !this.data.page) return
179
176
 
180
- this.ctx.trigger('editor-show-loading')
181
- new Api(this.ctx).pageEdit(this.data.page)
177
+ this.ctx.editorShowLoading()
178
+ this.ctx.getApi().pageEdit(this.data.page)
182
179
  .then((page) => {
183
180
  if (this.data)
184
181
  this.data.page = { ...page }
185
182
  this.refreshUI()
186
183
  })
187
184
  .catch(err => {
188
- this.ctx.trigger('editor-notify', { msg: `修改页面数据失败:${err.msg || String(err)}`, type: 'e'})
185
+ this.ctx.editorShowNotify(`${this.$t('editFail')}: ${err.msg || String(err)}`, 'e')
189
186
  })
190
187
  .finally(() => {
191
- this.ctx.trigger('editor-hide-loading')
188
+ this.ctx.editorHideLoading()
192
189
  })
193
190
  }
194
191
 
@@ -216,10 +213,10 @@ export default class List extends ListLite {
216
213
 
217
214
  // 下拉列表
218
215
  const dropdownList = [
219
- ['最新', () => { reloadUseParamsEditor(p => { p.sort_by = 'date_desc' }) }],
220
- ['最热', () => { reloadUseParamsEditor(p => { p.sort_by = 'vote' }) }],
221
- ['最早', () => { reloadUseParamsEditor(p => { p.sort_by = 'date_asc' }) }],
222
- ['作者', () => { reloadUseParamsEditor(p => { p.view_only_admin = true }) }],
216
+ [this.$t('sortLatest'), () => { reloadUseParamsEditor(p => { p.sort_by = 'date_desc' }) }],
217
+ [this.$t('sortBest'), () => { reloadUseParamsEditor(p => { p.sort_by = 'vote' }) }],
218
+ [this.$t('sortOldest'), () => { reloadUseParamsEditor(p => { p.sort_by = 'date_asc' }) }],
219
+ [this.$t('sortAuthor'), () => { reloadUseParamsEditor(p => { p.view_only_admin = true }) }],
223
220
  ]
224
221
 
225
222
  // 列表项点击事件
@@ -254,4 +251,9 @@ export default class List extends ListLite {
254
251
 
255
252
  this.$dropdownWrap.append($dropdown)
256
253
  }
254
+
255
+ public updateUnread(notifies: NotifyData[]): void {
256
+ super.updateUnread(notifies)
257
+ this.showUnreadBadge(notifies?.length || 0)
258
+ }
257
259
  }
@@ -1,4 +1,3 @@
1
- @font-family: "Helvetica Neue", "PingFangSC-Light", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
2
1
  @font-size: 14px;
3
2
  @line-height: 1.6em;
4
- @block-radius: 4px;
3
+ @block-radius: 6px;