artalk 2.2.6 → 2.2.9
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.
- package/dist/Artalk.css +1 -1
- package/dist/Artalk.es.js +282 -121
- package/dist/Artalk.iife.js +1 -1
- package/dist/Artalk.js +1 -1
- package/index.html +10 -8
- package/package.json +2 -2
- package/src/api/index.ts +33 -1
- package/src/artalk.ts +40 -12
- package/src/components/comment.ts +54 -22
- package/src/components/editor-plugs/emoticons-plug.ts +2 -2
- package/src/components/editor-plugs/preview-plug.ts +1 -1
- package/src/components/editor.ts +16 -3
- package/src/components/list-lite.ts +64 -78
- package/src/components/list-nest.ts +66 -0
- package/src/components/list.ts +37 -17
- package/src/components/pagination.ts +1 -1
- package/src/components/sidebar-layer.ts +2 -2
- package/src/context.ts +1 -1
- package/src/defaults.ts +3 -1
- package/src/lib/checker/index.ts +1 -1
- package/src/lib/utils.ts +30 -0
- package/src/style/_color.less +2 -2
- package/src/style/comment.less +21 -0
- package/types/artalk-config.d.ts +5 -2
|
@@ -1,17 +1,20 @@
|
|
|
1
|
+
import { ListData, CommentData, NotifyData, ApiVersionData } from '~/types/artalk-data'
|
|
1
2
|
import Context from '../context'
|
|
2
3
|
import Component from '../lib/component'
|
|
3
4
|
import * as Utils from '../lib/utils'
|
|
4
5
|
import * as Ui from '../lib/ui'
|
|
5
6
|
import Api from '../api'
|
|
6
7
|
import Comment from './comment'
|
|
7
|
-
import { ListData, CommentData, NotifyData, ApiVersionData } from '~/types/artalk-data'
|
|
8
8
|
import Pagination from './pagination'
|
|
9
9
|
import ReadMoreBtn from './read-more-btn'
|
|
10
|
+
import * as ListNest from './list-nest'
|
|
10
11
|
import { backendMinVersion } from '../../package.json'
|
|
11
12
|
|
|
12
13
|
export default class ListLite extends Component {
|
|
13
14
|
protected $commentsWrap: HTMLElement
|
|
14
|
-
|
|
15
|
+
|
|
16
|
+
protected commentList: Comment[] = [] // Note: 无层级结构 + 无须排列
|
|
17
|
+
protected get commentDataList() { return this.commentList.map(c => c.data) }
|
|
15
18
|
|
|
16
19
|
protected data?: ListData
|
|
17
20
|
protected isLoading: boolean = false
|
|
@@ -21,6 +24,9 @@ export default class ListLite extends Component {
|
|
|
21
24
|
/** 平铺模式 */
|
|
22
25
|
public flatMode = false
|
|
23
26
|
|
|
27
|
+
/** 嵌套模式 */
|
|
28
|
+
public nestSortBy: ListNest.SortByType = 'DATE_DESC'
|
|
29
|
+
|
|
24
30
|
/** 分页 */
|
|
25
31
|
public pageMode: 'pagination'|'read-more' = 'pagination'
|
|
26
32
|
public pageSize: number = 20 // 每次请求获取量
|
|
@@ -49,6 +55,9 @@ export default class ListLite extends Component {
|
|
|
49
55
|
</div>`)
|
|
50
56
|
this.$commentsWrap = this.$el.querySelector('.atk-list-comments-wrap')!
|
|
51
57
|
|
|
58
|
+
// 评论为空时显示字符
|
|
59
|
+
if (ctx.conf.noComment) this.noCommentText = ctx.conf.noComment
|
|
60
|
+
|
|
52
61
|
// 评论时间自动更新
|
|
53
62
|
window.setInterval(() => {
|
|
54
63
|
this.$el.querySelectorAll<HTMLElement>('[data-atk-comment-date]').forEach(el => {
|
|
@@ -262,7 +271,7 @@ export default class ListLite extends Component {
|
|
|
262
271
|
/** 刷新界面 */
|
|
263
272
|
public refreshUI() {
|
|
264
273
|
// 无评论
|
|
265
|
-
const isNoComment = this.
|
|
274
|
+
const isNoComment = this.commentList.length <= 0
|
|
266
275
|
let $noComment = this.$commentsWrap.querySelector<HTMLElement>('.atk-list-no-comment')
|
|
267
276
|
|
|
268
277
|
if (isNoComment) {
|
|
@@ -280,8 +289,11 @@ export default class ListLite extends Component {
|
|
|
280
289
|
}
|
|
281
290
|
|
|
282
291
|
/** 创建新评论 */
|
|
283
|
-
protected createComment(
|
|
284
|
-
|
|
292
|
+
protected createComment(cData: CommentData, ctxData?: CommentData[]) {
|
|
293
|
+
if (!ctxData) ctxData = this.commentDataList
|
|
294
|
+
|
|
295
|
+
const comment = new Comment(this.ctx, cData)
|
|
296
|
+
comment.flatMode = this.flatMode
|
|
285
297
|
comment.afterRender = () => {
|
|
286
298
|
if (this.renderComment) this.renderComment(comment)
|
|
287
299
|
}
|
|
@@ -290,6 +302,9 @@ export default class ListLite extends Component {
|
|
|
290
302
|
this.refreshUI()
|
|
291
303
|
}
|
|
292
304
|
|
|
305
|
+
// 子评论查找回复对象
|
|
306
|
+
comment.replyTo = (cData.rid ? ctxData.find(c => c.id === cData.rid) : undefined)
|
|
307
|
+
|
|
293
308
|
return comment
|
|
294
309
|
}
|
|
295
310
|
|
|
@@ -300,31 +315,16 @@ export default class ListLite extends Component {
|
|
|
300
315
|
this.putCommentFlatMode(commentData, srcData, 'append')
|
|
301
316
|
})
|
|
302
317
|
} else {
|
|
303
|
-
this.
|
|
318
|
+
this.importCommentsNest(srcData)
|
|
304
319
|
}
|
|
305
320
|
}
|
|
306
321
|
|
|
307
322
|
// 导入评论 · 嵌套模式
|
|
308
|
-
private
|
|
309
|
-
// 查找并导入所有子评论
|
|
310
|
-
const loadChildren = (parentC: Comment) => {
|
|
311
|
-
const children = srcData.filter(o => o.rid === parentC.data.id)
|
|
312
|
-
children.forEach((childData: CommentData) => {
|
|
313
|
-
const childC = this.createComment(childData)
|
|
314
|
-
childC.render()
|
|
315
|
-
|
|
316
|
-
// 插入到父评论中
|
|
317
|
-
parentC.putChild(childC)
|
|
318
|
-
|
|
319
|
-
// 递归加载子评论
|
|
320
|
-
loadChildren(childC)
|
|
321
|
-
})
|
|
322
|
-
}
|
|
323
|
-
|
|
323
|
+
private importCommentsNest(srcData: CommentData[]) {
|
|
324
324
|
// 遍历 root 评论
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
const rootC = this.createComment(
|
|
325
|
+
const rootNodes = ListNest.makeNestCommentNodeList(srcData, this.nestSortBy, this.conf.nestMax)
|
|
326
|
+
rootNodes.forEach((rootNode: ListNest.CommentNode) => {
|
|
327
|
+
const rootC = this.createComment(rootNode.comment, srcData)
|
|
328
328
|
rootC.render()
|
|
329
329
|
|
|
330
330
|
// 显示并播放渐入动画
|
|
@@ -332,10 +332,25 @@ export default class ListLite extends Component {
|
|
|
332
332
|
rootC.playFadeInAnim()
|
|
333
333
|
|
|
334
334
|
// 评论放入 comments 总表中
|
|
335
|
-
this.
|
|
335
|
+
this.commentList.push(rootC)
|
|
336
336
|
|
|
337
337
|
// 加载子评论
|
|
338
|
-
|
|
338
|
+
const that = this
|
|
339
|
+
;(function loadChildren(parentC: Comment, parentNode: ListNest.CommentNode) {
|
|
340
|
+
parentNode.children.forEach((node: ListNest.CommentNode) => {
|
|
341
|
+
const childD = node.comment
|
|
342
|
+
const childC = that.createComment(childD, srcData)
|
|
343
|
+
childC.render()
|
|
344
|
+
|
|
345
|
+
// 插入到父评论中
|
|
346
|
+
parentC.putChild(childC)
|
|
347
|
+
|
|
348
|
+
that.commentList.push(childC)
|
|
349
|
+
|
|
350
|
+
// 递归加载子评论
|
|
351
|
+
loadChildren(childC, node)
|
|
352
|
+
})
|
|
353
|
+
})(rootC, rootNode)
|
|
339
354
|
|
|
340
355
|
// 限高检测
|
|
341
356
|
rootC.checkHeightLimit()
|
|
@@ -343,18 +358,13 @@ export default class ListLite extends Component {
|
|
|
343
358
|
}
|
|
344
359
|
|
|
345
360
|
/** 导入评论 · 平铺模式 */
|
|
346
|
-
private putCommentFlatMode(cData: CommentData,
|
|
361
|
+
private putCommentFlatMode(cData: CommentData, ctxData: CommentData[], insertMode: 'append'|'prepend') {
|
|
347
362
|
if (cData.is_collapsed) cData.is_allow_reply = false
|
|
348
|
-
const comment = this.createComment(cData)
|
|
349
|
-
if (cData.rid !== 0) {
|
|
350
|
-
const rComment = srcData.find(o => o.id === cData.rid)
|
|
351
|
-
if (rComment) comment.replyTo = rComment
|
|
352
|
-
}
|
|
363
|
+
const comment = this.createComment(cData, ctxData)
|
|
353
364
|
comment.render()
|
|
354
365
|
|
|
355
366
|
// 将评论导入 comments 总表中
|
|
356
|
-
|
|
357
|
-
if (insertMode === 'prepend') this.comments.unshift(comment)
|
|
367
|
+
this.commentList.push(comment)
|
|
358
368
|
|
|
359
369
|
// 可见评论添加到界面
|
|
360
370
|
// 注:不可见评论用于显示 “引用内容”
|
|
@@ -375,17 +385,18 @@ export default class ListLite extends Component {
|
|
|
375
385
|
if (!this.flatMode) {
|
|
376
386
|
// 嵌套模式
|
|
377
387
|
const comment = this.createComment(commentData)
|
|
378
|
-
comment.render()
|
|
379
388
|
|
|
380
389
|
if (commentData.rid === 0) {
|
|
381
390
|
// root评论 新增
|
|
391
|
+
comment.render()
|
|
382
392
|
this.$commentsWrap.prepend(comment.getEl())
|
|
383
|
-
this.
|
|
393
|
+
this.commentList.push(comment)
|
|
384
394
|
} else {
|
|
385
395
|
// 子评论 新增
|
|
386
396
|
const parent = this.findComment(commentData.rid)
|
|
387
397
|
if (parent) {
|
|
388
|
-
|
|
398
|
+
comment.render()
|
|
399
|
+
parent.putChild(comment, (this.nestSortBy === 'DATE_ASC' ? 'append' : 'prepend'))
|
|
389
400
|
|
|
390
401
|
// 若父评论存在 “子评论部分” 限高,取消限高
|
|
391
402
|
comment.getParents().forEach((p) => {
|
|
@@ -400,7 +411,7 @@ export default class ListLite extends Component {
|
|
|
400
411
|
comment.playFadeInAnim() // 播放评论渐出动画
|
|
401
412
|
} else {
|
|
402
413
|
// 平铺模式
|
|
403
|
-
const comment = this.putCommentFlatMode(commentData, this.
|
|
414
|
+
const comment = this.putCommentFlatMode(commentData, this.commentDataList, 'prepend')
|
|
404
415
|
Ui.scrollIntoView(comment.getEl()) // 滚动到可见
|
|
405
416
|
}
|
|
406
417
|
|
|
@@ -412,47 +423,22 @@ export default class ListLite extends Component {
|
|
|
412
423
|
this.ctx.trigger('comments-loaded')
|
|
413
424
|
}
|
|
414
425
|
|
|
415
|
-
/** 遍历操作 Comment (包括子评论) */
|
|
416
|
-
protected eachComment(commentList: Comment[], action: (comment: Comment, levelList: Comment[]) => boolean|void) {
|
|
417
|
-
if (commentList.length === 0) return
|
|
418
|
-
commentList.every((item) => {
|
|
419
|
-
if (action(item, commentList) === false) return false
|
|
420
|
-
this.eachComment(item.getChildren(), action)
|
|
421
|
-
return true
|
|
422
|
-
})
|
|
423
|
-
}
|
|
424
|
-
|
|
425
426
|
/** 查找评论项 */
|
|
426
|
-
protected findComment(id: number
|
|
427
|
-
|
|
428
|
-
let comment: Comment|null = null
|
|
429
|
-
this.eachComment(src, (item) => {
|
|
430
|
-
if (item.data.id === id) {
|
|
431
|
-
comment = item
|
|
432
|
-
return false
|
|
433
|
-
}
|
|
434
|
-
return true
|
|
435
|
-
})
|
|
436
|
-
|
|
437
|
-
return comment
|
|
427
|
+
protected findComment(id: number): Comment|undefined {
|
|
428
|
+
return this.commentList.find(c => c.data.id === id)
|
|
438
429
|
}
|
|
439
430
|
|
|
440
431
|
/** 删除评论 */
|
|
441
|
-
public deleteComment(
|
|
442
|
-
let
|
|
443
|
-
if (typeof
|
|
444
|
-
findComment = this.findComment(
|
|
445
|
-
if (!findComment) throw Error(`未找到评论 ${
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
levelList.splice(levelList.indexOf(item), 1)
|
|
452
|
-
return false
|
|
453
|
-
}
|
|
454
|
-
return true
|
|
455
|
-
})
|
|
432
|
+
public deleteComment(_comment: number|Comment) {
|
|
433
|
+
let comment: Comment
|
|
434
|
+
if (typeof _comment === 'number') {
|
|
435
|
+
const findComment = this.findComment(_comment)
|
|
436
|
+
if (!findComment) throw Error(`未找到评论 ${_comment}`)
|
|
437
|
+
comment = findComment
|
|
438
|
+
} else comment = _comment
|
|
439
|
+
|
|
440
|
+
comment.getEl().remove()
|
|
441
|
+
this.commentList.splice(this.commentList.indexOf(comment), 1)
|
|
456
442
|
|
|
457
443
|
if (this.data) this.data.total -= 1 // 评论数减 1
|
|
458
444
|
|
|
@@ -463,7 +449,7 @@ export default class ListLite extends Component {
|
|
|
463
449
|
public clearAllComments() {
|
|
464
450
|
this.$commentsWrap.innerHTML = ''
|
|
465
451
|
this.data = undefined
|
|
466
|
-
this.
|
|
452
|
+
this.commentList = []
|
|
467
453
|
}
|
|
468
454
|
|
|
469
455
|
/** 更新未读数据 */
|
|
@@ -472,7 +458,7 @@ export default class ListLite extends Component {
|
|
|
472
458
|
|
|
473
459
|
// 高亮评论
|
|
474
460
|
if (this.unreadHighlight === true) {
|
|
475
|
-
this.
|
|
461
|
+
this.commentList.forEach((comment) => {
|
|
476
462
|
const notify = this.unread.find(o => o.comment_id === comment.data.id)
|
|
477
463
|
if (notify) {
|
|
478
464
|
comment.setUnread(true)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { CommentData } from '~/types/artalk-data'
|
|
2
|
+
|
|
3
|
+
export type CommentNode = {
|
|
4
|
+
id: number
|
|
5
|
+
comment: CommentData
|
|
6
|
+
children: CommentNode[]
|
|
7
|
+
parent?: CommentNode
|
|
8
|
+
level: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type SortByType = 'DATE_DESC'|'DATE_ASC'|'SRC_INDEX'|'VOTE_UP_DESC'
|
|
12
|
+
|
|
13
|
+
// 构建树状结构列表
|
|
14
|
+
export function makeNestCommentNodeList(srcData: CommentData[], sortBy: SortByType = 'DATE_DESC', nestMax = 2) {
|
|
15
|
+
const nodeList: CommentNode[] = []
|
|
16
|
+
|
|
17
|
+
const roots = srcData.filter((o) => o.rid === 0)
|
|
18
|
+
roots.forEach((root: CommentData) => {
|
|
19
|
+
const rootNode: CommentNode = {
|
|
20
|
+
id: root.id,
|
|
21
|
+
comment: root,
|
|
22
|
+
children: [],
|
|
23
|
+
level: 1,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
rootNode.parent = rootNode
|
|
27
|
+
nodeList.push(rootNode)
|
|
28
|
+
|
|
29
|
+
;(function loadChildren(parentNode: CommentNode) {
|
|
30
|
+
const children = srcData.filter((o) => o.rid === parentNode.id)
|
|
31
|
+
if (children.length === 0) return
|
|
32
|
+
if (parentNode.level >= nestMax) parentNode = parentNode.parent!
|
|
33
|
+
children.forEach((child: CommentData) => {
|
|
34
|
+
const childNode: CommentNode = {
|
|
35
|
+
id: child.id,
|
|
36
|
+
comment: child,
|
|
37
|
+
children: [],
|
|
38
|
+
parent: parentNode,
|
|
39
|
+
level: parentNode.level + 1,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
parentNode.children.push(childNode)
|
|
43
|
+
loadChildren(childNode)
|
|
44
|
+
})
|
|
45
|
+
})(rootNode)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// 排序
|
|
49
|
+
const sortFunc = (a: CommentNode, b: CommentNode): number => {
|
|
50
|
+
let v = a.id - b.id
|
|
51
|
+
if (sortBy === 'DATE_ASC') v = +new Date(a.comment.date) - +new Date(b.comment.date)
|
|
52
|
+
else if (sortBy === 'DATE_DESC') v = +new Date(b.comment.date) - +new Date(a.comment.date)
|
|
53
|
+
else if (sortBy === 'SRC_INDEX') v = srcData.indexOf(a.comment) - srcData.indexOf(b.comment)
|
|
54
|
+
else if (sortBy === 'VOTE_UP_DESC') v = b.comment.vote_up - a.comment.vote_up
|
|
55
|
+
return v
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
(function sortLevels(nodes: CommentNode[]) {
|
|
59
|
+
nodes.forEach((node) => {
|
|
60
|
+
node.children = node.children.sort(sortFunc)
|
|
61
|
+
sortLevels(node.children)
|
|
62
|
+
})
|
|
63
|
+
})(nodeList)
|
|
64
|
+
|
|
65
|
+
return nodeList
|
|
66
|
+
}
|
package/src/components/list.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import '../style/list.less'
|
|
2
2
|
|
|
3
|
+
import { ListData } from '~/types/artalk-data'
|
|
3
4
|
import Context from '../context'
|
|
4
5
|
import * as Utils from '../lib/utils'
|
|
5
6
|
import * as Ui from '../lib/ui'
|
|
6
7
|
import Api from '../api'
|
|
7
8
|
import ListHTML from './html/list.html?raw'
|
|
8
9
|
import ListLite from './list-lite'
|
|
9
|
-
import { ListData } from '~/types/artalk-data'
|
|
10
10
|
|
|
11
11
|
export default class List extends ListLite {
|
|
12
12
|
private $closeCommentBtn!: HTMLElement
|
|
@@ -112,11 +112,19 @@ export default class List extends ListLite {
|
|
|
112
112
|
super.onLoad(data, offset)
|
|
113
113
|
|
|
114
114
|
// 检测锚点跳转
|
|
115
|
-
this.checkGoToCommentByUrlHash()
|
|
115
|
+
if (!this.goToCommentFounded) this.checkGoToCommentByUrlHash()
|
|
116
|
+
|
|
117
|
+
// 防止评论框被吞
|
|
118
|
+
if (this.ctx.conf.editorTravel === true) {
|
|
119
|
+
this.ctx.trigger('editor-travel-back')
|
|
120
|
+
}
|
|
116
121
|
}
|
|
117
122
|
|
|
123
|
+
private goToCommentFounded = false
|
|
124
|
+
public goToCommentDelay = true
|
|
125
|
+
|
|
118
126
|
/** 跳到评论项位置 - 根据 `location.hash` */
|
|
119
|
-
public
|
|
127
|
+
public checkGoToCommentByUrlHash() {
|
|
120
128
|
let commentId = Number(Utils.getQueryParam('atk_comment')) // same as backend GetReplyLink()
|
|
121
129
|
if (!commentId) {
|
|
122
130
|
const match = window.location.hash.match(/#atk-comment-([0-9]+)/)
|
|
@@ -125,6 +133,14 @@ export default class List extends ListLite {
|
|
|
125
133
|
}
|
|
126
134
|
if (!commentId) return
|
|
127
135
|
|
|
136
|
+
const comment = this.findComment(commentId)
|
|
137
|
+
if (!comment) { // 若找不到评论
|
|
138
|
+
// 自动翻页
|
|
139
|
+
if (this.readMoreBtn) this.readMoreBtn.click()
|
|
140
|
+
else if (this.pagination) this.pagination.next()
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
128
144
|
// 已阅 API
|
|
129
145
|
const notifyKey = Utils.getQueryParam('atk_notify_key')
|
|
130
146
|
if (notifyKey) {
|
|
@@ -137,20 +153,24 @@ export default class List extends ListLite {
|
|
|
137
153
|
})
|
|
138
154
|
}
|
|
139
155
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
156
|
+
// 若父评论存在 “子评论部分” 限高,取消限高
|
|
157
|
+
comment.getParents().forEach((p) => {
|
|
158
|
+
if (p.$children) p.heightLimitRemove(p.$children)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
const goTo = () => {
|
|
162
|
+
Ui.scrollIntoView(comment.getEl(), false)
|
|
163
|
+
|
|
164
|
+
comment.getEl().classList.remove('atk-flash-once')
|
|
165
|
+
window.setTimeout(() => {
|
|
166
|
+
comment.getEl().classList.add('atk-flash-once')
|
|
167
|
+
}, 150)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!this.goToCommentDelay) goTo()
|
|
171
|
+
else window.setTimeout(() => goTo(), 350)
|
|
172
|
+
|
|
173
|
+
this.goToCommentFounded = true
|
|
154
174
|
}
|
|
155
175
|
|
|
156
176
|
/** 管理员设置页面信息 */
|
|
@@ -38,7 +38,7 @@ export default class Pagination {
|
|
|
38
38
|
`<div class="atk-pagination-wrap">
|
|
39
39
|
<div class="atk-pagination">
|
|
40
40
|
<div class="atk-btn atk-btn-prev">Prev</div>
|
|
41
|
-
<input type="text" class="atk-input" />
|
|
41
|
+
<input type="text" class="atk-input" aria-label="Enter the number of page" />
|
|
42
42
|
<div class="atk-btn atk-btn-next">Next</div>
|
|
43
43
|
</div>
|
|
44
44
|
</div>`)
|
|
@@ -3,11 +3,11 @@ import '../style/sidebar-layer.less'
|
|
|
3
3
|
import Context from '@/context'
|
|
4
4
|
import Component from '@/lib/component'
|
|
5
5
|
import * as Utils from '@/lib/utils'
|
|
6
|
-
import SidebarHTML from './html/sidebar-layer.html?raw'
|
|
7
6
|
import * as Ui from '@/lib/ui'
|
|
7
|
+
import { SidebarShowPayload } from '~/types/event'
|
|
8
|
+
import SidebarHTML from './html/sidebar-layer.html?raw'
|
|
8
9
|
import Layer from './layer'
|
|
9
10
|
import Api from '../api'
|
|
10
|
-
import { SidebarShowPayload } from '~/types/event'
|
|
11
11
|
|
|
12
12
|
export default class SidebarLayer extends Component {
|
|
13
13
|
public layer?: Layer
|
package/src/context.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { marked as libMarked } from 'marked'
|
|
2
|
-
import User from './lib/user'
|
|
3
2
|
import ArtalkConfig from '~/types/artalk-config'
|
|
4
3
|
import { EventPayloadMap, Event, EventScopeType, Handler } from '~/types/event'
|
|
4
|
+
import User from './lib/user'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Artalk Context
|
package/src/defaults.ts
CHANGED
|
@@ -20,7 +20,8 @@ const defaults: ArtalkConfig = {
|
|
|
20
20
|
listSort: true,
|
|
21
21
|
pvEl: '#ArtalkPV',
|
|
22
22
|
flatMode: 'auto',
|
|
23
|
-
|
|
23
|
+
nestMax: 3,
|
|
24
|
+
|
|
24
25
|
gravatar: {
|
|
25
26
|
default: 'mp',
|
|
26
27
|
mirror: 'https://sdn.geekzu.org/avatar/',
|
|
@@ -40,6 +41,7 @@ const defaults: ArtalkConfig = {
|
|
|
40
41
|
imgUpload: true,
|
|
41
42
|
reqTimeout: 15000,
|
|
42
43
|
versionCheck: true,
|
|
44
|
+
useBackendConf: false,
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
export default defaults
|
package/src/lib/checker/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import Context from '@/context'
|
|
2
2
|
import Layer from '@/components/layer'
|
|
3
3
|
import Dialog from '@/components/dialog'
|
|
4
|
+
import { CheckerPayload } from '~/types/event'
|
|
4
5
|
import * as Utils from '../utils'
|
|
5
6
|
import * as Ui from '../ui'
|
|
6
|
-
import { CheckerPayload } from '~/types/event'
|
|
7
7
|
import CaptchaChecker from './captcha-checker'
|
|
8
8
|
import AdminChecker from './admin-checker'
|
|
9
9
|
|
package/src/lib/utils.ts
CHANGED
|
@@ -293,3 +293,33 @@ export function getURLBasedOnApi(ctx: Context, path: string) {
|
|
|
293
293
|
export function getURLBasedOn(baseURL: string, path: string) {
|
|
294
294
|
return `${baseURL.replace(/\/$/, '')}/${path.replace(/^\//, '')}`
|
|
295
295
|
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Performs a deep merge of `source` into `target`.
|
|
299
|
+
* Mutates `target` only but not its objects and arrays.
|
|
300
|
+
*
|
|
301
|
+
* @author inspired by [jhildenbiddle](https://stackoverflow.com/a/48218209).
|
|
302
|
+
* @link https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6
|
|
303
|
+
*/
|
|
304
|
+
export function mergeDeep(target: any, source: any) {
|
|
305
|
+
const isObject = (obj: any) => obj && typeof obj === 'object'
|
|
306
|
+
|
|
307
|
+
if (!isObject(target) || !isObject(source)) {
|
|
308
|
+
return source
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
Object.keys(source).forEach(key => {
|
|
312
|
+
const targetValue = target[key]
|
|
313
|
+
const sourceValue = source[key]
|
|
314
|
+
|
|
315
|
+
if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
|
|
316
|
+
target[key] = targetValue.concat(sourceValue)
|
|
317
|
+
} else if (isObject(targetValue) && isObject(sourceValue)) {
|
|
318
|
+
target[key] = mergeDeep({ ...targetValue}, sourceValue)
|
|
319
|
+
} else {
|
|
320
|
+
target[key] = sourceValue
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
return target
|
|
325
|
+
}
|
package/src/style/_color.less
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
--at-color-font: #2a2e2e;
|
|
3
3
|
--at-color-deep: #2a2e2e;
|
|
4
4
|
--at-color-sub: #757575;
|
|
5
|
-
--at-color-grey: #
|
|
6
|
-
--at-color-meta: #
|
|
5
|
+
--at-color-grey: #747474;
|
|
6
|
+
--at-color-meta: #697182;
|
|
7
7
|
--at-color-border: #eceff2;
|
|
8
8
|
--at-color-light: #4285f4;
|
|
9
9
|
--at-color-bg: #FFF;
|
package/src/style/comment.less
CHANGED
|
@@ -83,6 +83,27 @@
|
|
|
83
83
|
color: var(--at-color-main);
|
|
84
84
|
text-decoration: none;
|
|
85
85
|
}
|
|
86
|
+
|
|
87
|
+
&.atk-reply-at {
|
|
88
|
+
margin-left: 2px;
|
|
89
|
+
|
|
90
|
+
& > .atk-arrow:before {
|
|
91
|
+
content: '';
|
|
92
|
+
vertical-align: middle;
|
|
93
|
+
transform: rotate(90deg);
|
|
94
|
+
border-bottom: 4px solid var(--at-color-grey);
|
|
95
|
+
border-left: 3px solid transparent;
|
|
96
|
+
border-right: 3px solid transparent;
|
|
97
|
+
display: inline-block;
|
|
98
|
+
margin-top: -1px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
& > .atk-nick {
|
|
102
|
+
color: var(--at-color-main);
|
|
103
|
+
cursor: pointer;
|
|
104
|
+
margin-left: 6px;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
86
107
|
}
|
|
87
108
|
|
|
88
109
|
.badge() {
|
package/types/artalk-config.d.ts
CHANGED
|
@@ -74,8 +74,8 @@ export default interface ArtalkConfig {
|
|
|
74
74
|
/** 平铺模式 */
|
|
75
75
|
flatMode?: boolean|'auto'
|
|
76
76
|
|
|
77
|
-
/**
|
|
78
|
-
|
|
77
|
+
/** 嵌套模式 · 最大层数 */
|
|
78
|
+
nestMax?: number
|
|
79
79
|
|
|
80
80
|
/** 显示 UA 徽标 */
|
|
81
81
|
uaBadge?: boolean
|
|
@@ -88,6 +88,9 @@ export default interface ArtalkConfig {
|
|
|
88
88
|
|
|
89
89
|
/** 版本检测 */
|
|
90
90
|
versionCheck?: boolean
|
|
91
|
+
|
|
92
|
+
/** 复用后端配置 */
|
|
93
|
+
useBackendConf?: boolean
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
export interface LocalUser {
|