artalk 2.3.4 → 2.4.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.
- package/dist/Artalk.css +1 -1
- package/dist/Artalk.es.js +3171 -3008
- package/dist/Artalk.iife.js +1 -1
- package/dist/Artalk.js +1 -1
- package/dist/ArtalkLite.css +1 -1
- package/dist/ArtalkLite.es.js +3124 -2980
- package/dist/ArtalkLite.iife.js +1 -1
- package/dist/ArtalkLite.js +1 -1
- package/dist/src/api/admin.d.ts +10 -0
- package/dist/src/api/api-base.d.ts +14 -0
- package/dist/src/api/captcha.d.ts +14 -0
- package/dist/src/api/comment.d.ts +29 -0
- package/dist/src/api/index.d.ts +31 -0
- package/dist/src/api/page.d.ts +22 -0
- package/dist/src/api/request.d.ts +12 -0
- package/dist/src/api/site.d.ts +17 -0
- package/dist/src/api/system.d.ts +13 -0
- package/dist/src/api/upload.d.ts +11 -0
- package/dist/src/api/user.d.ts +28 -0
- package/dist/src/artalk.d.ts +56 -0
- package/dist/src/comment/actions.d.ts +17 -0
- package/dist/src/comment/comment.d.ts +67 -0
- package/dist/src/comment/index.d.ts +2 -0
- package/dist/src/comment/render.d.ts +76 -0
- package/dist/src/components/action-btn.d.ts +52 -0
- package/dist/src/components/dialog.d.ts +18 -0
- package/dist/src/components/pagination.d.ts +30 -0
- package/dist/src/components/read-more-btn.d.ts +33 -0
- package/dist/src/context.d.ts +72 -0
- package/dist/src/defaults.d.ts +3 -0
- package/dist/src/editor/editor.d.ts +84 -0
- package/dist/src/editor/index.d.ts +2 -0
- package/dist/src/editor/plugs/editor-plug.d.ts +26 -0
- package/dist/src/editor/plugs/emoticons-plug.d.ts +36 -0
- package/dist/src/editor/plugs/header-input-plug.d.ts +13 -0
- package/dist/src/editor/plugs/preview-plug.d.ts +10 -0
- package/dist/src/editor/plugs/upload-plug.d.ts +10 -0
- package/dist/src/i18n/en.d.ts +69 -0
- package/dist/src/i18n/index.d.ts +294 -0
- package/dist/src/i18n/zh-CN.d.ts +3 -0
- package/dist/src/layer/index.d.ts +3 -0
- package/dist/src/layer/layer.d.ts +32 -0
- package/dist/src/layer/sidebar-layer.d.ts +20 -0
- package/dist/src/lib/checker/admin-checker.d.ts +3 -0
- package/dist/src/lib/checker/captcha-checker.d.ts +3 -0
- package/dist/src/lib/checker/index.d.ts +42 -0
- package/dist/src/lib/component.d.ts +12 -0
- package/dist/src/lib/detect.d.ts +10 -0
- package/dist/src/lib/stat.d.ts +15 -0
- package/dist/src/lib/ui.d.ts +23 -0
- package/dist/src/lib/user.d.ts +13 -0
- package/dist/src/lib/utils.d.ts +39 -0
- package/dist/src/list/index.d.ts +2 -0
- package/dist/src/list/list-lite.d.ts +63 -0
- package/dist/src/list/list-nest.d.ts +10 -0
- package/dist/src/list/list.d.ts +26 -0
- package/dist/src/main.d.ts +4 -0
- package/dist/types/artalk-config.d.ts +132 -0
- package/dist/types/artalk-data.d.ts +204 -0
- package/dist/types/context.d.ts +146 -0
- package/dist/types/event.d.ts +28 -0
- package/dist/types/global.d.ts +0 -0
- package/dist/types/plug.d.ts +4 -0
- package/index.html +3 -3
- package/package.json +5 -5
- package/src/api/admin.ts +18 -0
- package/src/api/api-base.ts +27 -0
- package/src/api/captcha.ts +24 -0
- package/src/api/comment.ts +84 -0
- package/src/api/index.ts +25 -366
- package/src/api/page.ts +85 -0
- package/src/api/site.ts +51 -0
- package/src/api/system.ts +39 -0
- package/src/api/upload.ts +29 -0
- package/src/api/user.ts +75 -0
- package/src/artalk.ts +50 -46
- package/src/comment/actions.ts +3 -3
- package/src/comment/comment.ts +6 -9
- package/src/comment/render.ts +76 -40
- package/src/components/pagination.ts +0 -1
- package/src/context.ts +31 -31
- package/src/editor/editor.ts +2 -4
- package/src/editor/plugs/header-input-plug.ts +1 -1
- package/src/editor/plugs/upload-plug.ts +1 -1
- package/src/i18n/index.ts +117 -2
- package/src/layer/sidebar-layer.ts +1 -3
- package/src/lib/checker/admin-checker.ts +1 -1
- package/src/lib/checker/captcha-checker.ts +3 -3
- package/src/lib/stat.ts +2 -2
- package/src/lib/ui.ts +6 -0
- package/src/list/list-lite.ts +25 -25
- package/src/list/list.ts +3 -4
- package/src/main.ts +3 -0
- package/src/style/main.less +6 -0
- package/types/context.d.ts +4 -1
- package/types/global.d.ts +0 -2
- package/vite-base.config.ts +9 -5
- package/vite.config.ts +5 -1
- package/types/index.d.ts +0 -3
package/src/comment/render.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import '../style/comment.less'
|
|
2
|
-
|
|
3
1
|
import * as Utils from '../lib/utils'
|
|
4
2
|
import * as Ui from '../lib/ui'
|
|
5
3
|
import ActionBtn from '../components/action-btn'
|
|
@@ -49,7 +47,7 @@ export default class CommentRender {
|
|
|
49
47
|
this.renderReplyAt()
|
|
50
48
|
this.renderReplyTo()
|
|
51
49
|
this.renderPending()
|
|
52
|
-
this.
|
|
50
|
+
this.renderActions()
|
|
53
51
|
|
|
54
52
|
this.recoveryChildrenWrap()
|
|
55
53
|
|
|
@@ -63,7 +61,7 @@ export default class CommentRender {
|
|
|
63
61
|
$avatarImg.src = this.comment.getGravatarURL()
|
|
64
62
|
if (this.data.link) {
|
|
65
63
|
const $avatarA = Utils.createElement<HTMLLinkElement>('<a target="_blank" rel="noreferrer noopener nofollow"></a>')
|
|
66
|
-
$avatarA.href = this.data.link
|
|
64
|
+
$avatarA.href = Utils.isValidURL(this.data.link) ? this.data.link : `https://${this.data.link}`
|
|
67
65
|
$avatarA.append($avatarImg)
|
|
68
66
|
$avatar.append($avatarA)
|
|
69
67
|
} else {
|
|
@@ -73,17 +71,26 @@ export default class CommentRender {
|
|
|
73
71
|
|
|
74
72
|
/** 初始化 - 评论信息 */
|
|
75
73
|
private renderHeader() {
|
|
74
|
+
this.renderHeader_Nick()
|
|
75
|
+
this.renderHeader_VerifyBadge()
|
|
76
|
+
this.renderHeader_Date()
|
|
77
|
+
this.renderHeader_UABadge()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private renderHeader_Nick() {
|
|
76
81
|
this.$headerNick = this.$el.querySelector<HTMLElement>('.atk-nick')!
|
|
77
82
|
|
|
78
83
|
if (this.data.link) {
|
|
79
84
|
const $nickA = Utils.createElement<HTMLLinkElement>('<a target="_blank" rel="noreferrer noopener nofollow"></a>')
|
|
80
85
|
$nickA.innerText = this.data.nick
|
|
81
|
-
$nickA.href = this.data.link
|
|
86
|
+
$nickA.href = Utils.isValidURL(this.data.link) ? this.data.link : `https://${this.data.link}`
|
|
82
87
|
this.$headerNick.append($nickA)
|
|
83
88
|
} else {
|
|
84
89
|
this.$headerNick.innerText = this.data.nick
|
|
85
90
|
}
|
|
91
|
+
}
|
|
86
92
|
|
|
93
|
+
private renderHeader_VerifyBadge() {
|
|
87
94
|
this.$headerBadgeWrap = this.$el.querySelector<HTMLElement>('.atk-badge-wrap')!
|
|
88
95
|
this.$headerBadgeWrap.innerHTML = ''
|
|
89
96
|
|
|
@@ -100,11 +107,15 @@ export default class CommentRender {
|
|
|
100
107
|
const $pinnedBadge = Utils.createElement(`<span class="atk-pinned-badge">${this.ctx.$t('pin')}</span>`) // 置顶徽章
|
|
101
108
|
this.$headerBadgeWrap.append($pinnedBadge)
|
|
102
109
|
}
|
|
110
|
+
}
|
|
103
111
|
|
|
112
|
+
private renderHeader_Date() {
|
|
104
113
|
const $date = this.$el.querySelector<HTMLElement>('.atk-date')!
|
|
105
114
|
$date.innerText = this.comment.getDateFormatted()
|
|
106
115
|
$date.setAttribute('data-atk-comment-date', String(+new Date(this.data.date)))
|
|
116
|
+
}
|
|
107
117
|
|
|
118
|
+
private renderHeader_UABadge() {
|
|
108
119
|
if (this.ctx.conf.uaBadge) {
|
|
109
120
|
let $uaWrap = this.$header.querySelector('atk-ua-wrap')
|
|
110
121
|
if (!$uaWrap) {
|
|
@@ -115,8 +126,9 @@ export default class CommentRender {
|
|
|
115
126
|
$uaWrap.innerHTML = ''
|
|
116
127
|
const $uaBrowser = Utils.createElement(`<span class="atk-ua ua-browser"></span>`)
|
|
117
128
|
const $usOS = Utils.createElement(`<span class="atk-ua ua-os"></span>`)
|
|
118
|
-
|
|
119
|
-
$
|
|
129
|
+
const uaInfo = this.comment.getUserUA()
|
|
130
|
+
$uaBrowser.innerText = uaInfo.browser
|
|
131
|
+
$usOS.innerText = uaInfo.os
|
|
120
132
|
$uaWrap.append($uaBrowser)
|
|
121
133
|
$uaWrap.append($usOS)
|
|
122
134
|
}
|
|
@@ -196,39 +208,55 @@ export default class CommentRender {
|
|
|
196
208
|
}
|
|
197
209
|
|
|
198
210
|
/** 初始化 - 评论操作按钮 */
|
|
199
|
-
private
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
211
|
+
private renderActions() {
|
|
212
|
+
this.renderActions_Vote()
|
|
213
|
+
this.renderActions_Reply()
|
|
214
|
+
|
|
215
|
+
// 管理员操作
|
|
216
|
+
this.renderActions_Collapse()
|
|
217
|
+
this.renderActions_Moderator()
|
|
218
|
+
this.renderActions_Pin()
|
|
219
|
+
this.renderActions_Edit()
|
|
220
|
+
this.renderActions_Del()
|
|
221
|
+
}
|
|
207
222
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
223
|
+
// 操作按钮 - 投票
|
|
224
|
+
private renderActions_Vote() {
|
|
225
|
+
if (!this.ctx.conf.vote) return // 关闭投票功能
|
|
226
|
+
|
|
227
|
+
// 赞同按钮
|
|
228
|
+
this.voteBtnUp = new ActionBtn(this.ctx, () => `${this.ctx.$t('voteUp')} (${this.data.vote_up || 0})`).appendTo(this.$actions)
|
|
229
|
+
this.voteBtnUp.setClick(() => {
|
|
230
|
+
this.comment.getActions().vote('up')
|
|
231
|
+
})
|
|
216
232
|
|
|
217
|
-
//
|
|
218
|
-
if (this.
|
|
219
|
-
|
|
220
|
-
this
|
|
221
|
-
|
|
222
|
-
e.stopPropagation() // 防止穿透
|
|
223
|
-
if (!this.cConf.onReplyBtnClick) {
|
|
224
|
-
this.ctx.replyComment(this.data, this.$el)
|
|
225
|
-
} else {
|
|
226
|
-
this.cConf.onReplyBtnClick()
|
|
227
|
-
}
|
|
233
|
+
// 反对按钮
|
|
234
|
+
if (this.ctx.conf.voteDown) {
|
|
235
|
+
this.voteBtnDown = new ActionBtn(this.ctx, () => `${this.ctx.$t('voteDown')} (${this.data.vote_down || 0})`).appendTo(this.$actions)
|
|
236
|
+
this.voteBtnDown.setClick(() => {
|
|
237
|
+
this.comment.getActions().vote('down')
|
|
228
238
|
})
|
|
229
239
|
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 操作按钮 - 回复
|
|
243
|
+
private renderActions_Reply() {
|
|
244
|
+
if (!this.data.is_allow_reply) return // 不允许回复
|
|
245
|
+
|
|
246
|
+
const replyBtn = Utils.createElement(`<span>${this.ctx.$t('reply')}</span>`)
|
|
247
|
+
this.$actions.append(replyBtn)
|
|
248
|
+
replyBtn.addEventListener('click', (e) => {
|
|
249
|
+
e.stopPropagation() // 防止穿透
|
|
250
|
+
if (!this.cConf.onReplyBtnClick) {
|
|
251
|
+
this.ctx.replyComment(this.data, this.$el)
|
|
252
|
+
} else {
|
|
253
|
+
this.cConf.onReplyBtnClick()
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
}
|
|
230
257
|
|
|
231
|
-
|
|
258
|
+
// 操作按钮 - 折叠
|
|
259
|
+
private renderActions_Collapse() {
|
|
232
260
|
const collapseBtn = new ActionBtn(this.ctx, {
|
|
233
261
|
text: () => (this.data.is_collapsed ? this.ctx.$t('expand') : this.ctx.$t('collapse')),
|
|
234
262
|
adminOnly: true
|
|
@@ -237,8 +265,10 @@ export default class CommentRender {
|
|
|
237
265
|
collapseBtn.setClick(() => {
|
|
238
266
|
this.comment.getActions().adminEdit('collapsed', collapseBtn)
|
|
239
267
|
})
|
|
268
|
+
}
|
|
240
269
|
|
|
241
|
-
|
|
270
|
+
// 操作按钮 - 审核
|
|
271
|
+
private renderActions_Moderator() {
|
|
242
272
|
const pendingBtn = new ActionBtn(this.ctx, {
|
|
243
273
|
text: () => (this.data.is_pending ? this.ctx.$t('pending') : this.ctx.$t('approved')),
|
|
244
274
|
adminOnly: true
|
|
@@ -247,18 +277,22 @@ export default class CommentRender {
|
|
|
247
277
|
pendingBtn.setClick(() => {
|
|
248
278
|
this.comment.getActions().adminEdit('pending', pendingBtn)
|
|
249
279
|
})
|
|
280
|
+
}
|
|
250
281
|
|
|
251
|
-
|
|
282
|
+
// 操作按钮 - 置顶
|
|
283
|
+
private renderActions_Pin() {
|
|
252
284
|
const pinnedBtn = new ActionBtn(this.ctx, {
|
|
253
285
|
text: () => (this.data.is_pinned ? this.ctx.$t('unpin') : this.ctx.$t('pin')),
|
|
254
286
|
adminOnly: true
|
|
255
287
|
})
|
|
256
288
|
pinnedBtn.appendTo(this.$actions)
|
|
257
289
|
pinnedBtn.setClick(() => {
|
|
258
|
-
this.comment.getActions().adminEdit('pinned',
|
|
290
|
+
this.comment.getActions().adminEdit('pinned', pinnedBtn)
|
|
259
291
|
})
|
|
292
|
+
}
|
|
260
293
|
|
|
261
|
-
|
|
294
|
+
// 操作按钮 - 编辑
|
|
295
|
+
private renderActions_Edit() {
|
|
262
296
|
const editBtn = new ActionBtn(this.ctx, {
|
|
263
297
|
text: this.ctx.$t('edit'),
|
|
264
298
|
adminOnly: true
|
|
@@ -267,8 +301,10 @@ export default class CommentRender {
|
|
|
267
301
|
editBtn.setClick(() => {
|
|
268
302
|
this.ctx.editComment(this.data, this.$el)
|
|
269
303
|
})
|
|
304
|
+
}
|
|
270
305
|
|
|
271
|
-
|
|
306
|
+
// 操作按钮 - 删除
|
|
307
|
+
private renderActions_Del() {
|
|
272
308
|
const delBtn = new ActionBtn(this.ctx, {
|
|
273
309
|
text: this.ctx.$t('delete'),
|
|
274
310
|
confirm: true,
|
package/src/context.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { marked as libMarked } from 'marked'
|
|
|
2
2
|
import ArtalkConfig from '~/types/artalk-config'
|
|
3
3
|
import { CommentData, NotifyData } from '~/types/artalk-data'
|
|
4
4
|
import { Event } from '~/types/event'
|
|
5
|
-
import {
|
|
5
|
+
import getI18n, { I18n } from './i18n'
|
|
6
6
|
import User from './lib/user'
|
|
7
7
|
import ContextApi from '../types/context'
|
|
8
8
|
import Editor from './editor'
|
|
@@ -12,6 +12,7 @@ import SidebarLayer, { SidebarShowPayload } from './layer/sidebar-layer'
|
|
|
12
12
|
import CheckerLauncher, { CheckerCaptchaPayload, CheckerPayload } from './lib/checker'
|
|
13
13
|
import Layer, { GetLayerWrap } from './layer'
|
|
14
14
|
import Api from './api'
|
|
15
|
+
import List from './list'
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Artalk Context
|
|
@@ -20,7 +21,7 @@ export default class Context implements ContextApi {
|
|
|
20
21
|
/* 持有同事类 */
|
|
21
22
|
private api!: Api
|
|
22
23
|
private editor!: Editor
|
|
23
|
-
private list
|
|
24
|
+
private list?: ListLite
|
|
24
25
|
private sidebarLayer!: SidebarLayer
|
|
25
26
|
private checkerLauncher!: CheckerLauncher
|
|
26
27
|
|
|
@@ -99,24 +100,29 @@ export default class Context implements ContextApi {
|
|
|
99
100
|
comment.getEl().remove()
|
|
100
101
|
this.commentList.splice(this.commentList.indexOf(comment), 1)
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
if (this.list) {
|
|
104
|
+
const listData = this.list.getData()
|
|
105
|
+
if (listData) listData.total -= 1 // 评论数减 1
|
|
104
106
|
|
|
105
|
-
|
|
107
|
+
this.list.refreshUI()
|
|
108
|
+
}
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
public clearAllComments() {
|
|
109
|
-
this.list
|
|
110
|
-
|
|
112
|
+
if (this.list) {
|
|
113
|
+
this.list.getCommentsWrapEl().innerHTML = ''
|
|
114
|
+
this.list.clearData()
|
|
115
|
+
}
|
|
116
|
+
|
|
111
117
|
this.commentList = []
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
public insertComment(commentData: CommentData) {
|
|
115
|
-
this.list
|
|
121
|
+
this.list?.insertComment(commentData)
|
|
116
122
|
}
|
|
117
123
|
|
|
118
124
|
public updateComment(commentData: CommentData): void {
|
|
119
|
-
this.list
|
|
125
|
+
this.list?.updateComment(commentData)
|
|
120
126
|
}
|
|
121
127
|
|
|
122
128
|
public replyComment(commentData: CommentData, $comment: HTMLElement, scroll?: boolean): void {
|
|
@@ -136,12 +142,12 @@ export default class Context implements ContextApi {
|
|
|
136
142
|
}
|
|
137
143
|
|
|
138
144
|
public updateNotifies(notifies: NotifyData[]): void {
|
|
139
|
-
this.list
|
|
145
|
+
this.list?.updateUnread(notifies)
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
/* 评论列表 */
|
|
143
149
|
public listReload(): void {
|
|
144
|
-
this.list
|
|
150
|
+
this.list?.reload()
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
public reload(): void {
|
|
@@ -149,7 +155,15 @@ export default class Context implements ContextApi {
|
|
|
149
155
|
}
|
|
150
156
|
|
|
151
157
|
public listRefreshUI(): void {
|
|
152
|
-
this.list
|
|
158
|
+
this.list?.refreshUI()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
public listHashGotoCheck(): void {
|
|
162
|
+
if (!this.list || this.list !instanceof List) return
|
|
163
|
+
const list = this.list as List
|
|
164
|
+
|
|
165
|
+
list.goToCommentDelay = false
|
|
166
|
+
list.checkGoToCommentByUrlHash()
|
|
153
167
|
}
|
|
154
168
|
|
|
155
169
|
/* 编辑器 */
|
|
@@ -240,15 +254,7 @@ export default class Context implements ContextApi {
|
|
|
240
254
|
|
|
241
255
|
/* i18n */
|
|
242
256
|
public $t(key: keyof I18n, args: {[key: string]: string} = {}): string {
|
|
243
|
-
|
|
244
|
-
if (typeof locales === 'string') {
|
|
245
|
-
locales = internalLocales[locales]
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
let str = locales?.[key] || key
|
|
249
|
-
str = str.replace(/\{\s*(\w+?)\s*\}/g, (_, token) => args[token] || '')
|
|
250
|
-
|
|
251
|
-
return str
|
|
257
|
+
return getI18n(this.conf.locale, key, args)
|
|
252
258
|
}
|
|
253
259
|
|
|
254
260
|
public setDarkMode(darkMode: boolean): void {
|
|
@@ -257,20 +263,14 @@ export default class Context implements ContextApi {
|
|
|
257
263
|
this.conf.darkMode = darkMode
|
|
258
264
|
this.trigger('conf-updated')
|
|
259
265
|
|
|
260
|
-
if (this.conf.darkMode)
|
|
261
|
-
|
|
262
|
-
} else {
|
|
263
|
-
this.$root.classList.remove(darkModeClassName)
|
|
264
|
-
}
|
|
266
|
+
if (this.conf.darkMode) this.$root.classList.add(darkModeClassName)
|
|
267
|
+
else this.$root.classList.remove(darkModeClassName)
|
|
265
268
|
|
|
266
269
|
// for Layer
|
|
267
270
|
const { $wrap: $layerWrap } = GetLayerWrap(this)
|
|
268
271
|
if ($layerWrap) {
|
|
269
|
-
if (this.conf.darkMode)
|
|
270
|
-
|
|
271
|
-
} else {
|
|
272
|
-
$layerWrap.classList.remove(darkModeClassName)
|
|
273
|
-
}
|
|
272
|
+
if (this.conf.darkMode) $layerWrap.classList.add(darkModeClassName)
|
|
273
|
+
else $layerWrap.classList.remove(darkModeClassName)
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
}
|
package/src/editor/editor.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import '@/style/editor.less'
|
|
2
|
-
|
|
3
1
|
import { CommentData } from '~/types/artalk-data'
|
|
4
2
|
import Context from '~/types/context'
|
|
5
3
|
import Component from '../lib/component'
|
|
@@ -326,7 +324,7 @@ export default class Editor extends Component {
|
|
|
326
324
|
/** 创建评论 */
|
|
327
325
|
public async submitAdd() {
|
|
328
326
|
try {
|
|
329
|
-
const nComment = await this.ctx.getApi().add({
|
|
327
|
+
const nComment = await this.ctx.getApi().comment.add({
|
|
330
328
|
content: this.getFinalContent(),
|
|
331
329
|
nick: this.user.data.nick,
|
|
332
330
|
email: this.user.data.email,
|
|
@@ -364,7 +362,7 @@ export default class Editor extends Component {
|
|
|
364
362
|
email: this.$email.value,
|
|
365
363
|
link: this.$link.value,
|
|
366
364
|
}
|
|
367
|
-
const nComment = await this.ctx.getApi().commentEdit({
|
|
365
|
+
const nComment = await this.ctx.getApi().comment.commentEdit({
|
|
368
366
|
...this.editComment, ...saveData
|
|
369
367
|
})
|
|
370
368
|
this.ctx.updateComment(nComment)
|
|
@@ -39,7 +39,7 @@ export default class HeaderInputPlug extends EditorPlug {
|
|
|
39
39
|
this.queryUserInfo.timeout = window.setTimeout(() => {
|
|
40
40
|
this.queryUserInfo.timeout = null // 清理
|
|
41
41
|
|
|
42
|
-
const {req, abort} = this.ctx.getApi().userGet(
|
|
42
|
+
const {req, abort} = this.ctx.getApi().user.userGet(
|
|
43
43
|
this.ctx.user.data.nick, this.ctx.user.data.email
|
|
44
44
|
)
|
|
45
45
|
this.queryUserInfo.abortFunc = abort
|
|
@@ -99,7 +99,7 @@ export default class UploadPlug extends EditorPlug {
|
|
|
99
99
|
try {
|
|
100
100
|
if (!this.ctx.conf.imgUploader) {
|
|
101
101
|
// 使用 ArtalkGo 进行图片上传
|
|
102
|
-
resp = await this.ctx.getApi().imgUpload(file)
|
|
102
|
+
resp = await this.ctx.getApi().upload.imgUpload(file)
|
|
103
103
|
} else {
|
|
104
104
|
// 使用自定义的图片上传器
|
|
105
105
|
resp = {img_url: await this.ctx.conf.imgUploader(file)}
|
package/src/i18n/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import zhCN from './zh-CN'
|
|
|
3
3
|
|
|
4
4
|
export type I18n = typeof en
|
|
5
5
|
|
|
6
|
-
// @note the
|
|
6
|
+
// @note the key of language is followed by `ISO 639`
|
|
7
7
|
// https://en.wikipedia.org/wiki/ISO_639
|
|
8
8
|
// https://datatracker.ietf.org/doc/html/rfc5646#section-2.1
|
|
9
9
|
export const internal = {
|
|
@@ -14,8 +14,123 @@ export const internal = {
|
|
|
14
14
|
|
|
15
15
|
export const external = {
|
|
16
16
|
'jp-JP': {},
|
|
17
|
-
'zh-TW': {
|
|
17
|
+
'zh-TW': {
|
|
18
|
+
/* Editor */
|
|
19
|
+
placeholder: '鍵入內容...',
|
|
20
|
+
noComment: '「此時無聲勝有聲」',
|
|
21
|
+
send: '發送評論',
|
|
22
|
+
save: '保存評論',
|
|
23
|
+
nick: '昵稱',
|
|
24
|
+
email: '郵箱',
|
|
25
|
+
link: '網址',
|
|
26
|
+
emoticon: '表情',
|
|
27
|
+
preview: '預覽',
|
|
28
|
+
image: '圖片',
|
|
29
|
+
uploadFail: '上傳失敗',
|
|
30
|
+
commentFail: '評論失敗',
|
|
31
|
+
restoredMsg: '內容已自動恢復',
|
|
32
|
+
onlyAdminCanReply: '僅管理員可評論',
|
|
33
|
+
uploadLoginMsg: '填入你的名字郵箱才能上傳哦',
|
|
34
|
+
|
|
35
|
+
/* List */
|
|
36
|
+
counter: '{count} 條評論',
|
|
37
|
+
sortLatest: '最新',
|
|
38
|
+
sortOldest: '最早',
|
|
39
|
+
sortBest: '最熱',
|
|
40
|
+
sortAuthor: '作者',
|
|
41
|
+
openComment: '打開評論',
|
|
42
|
+
closeComment: '關閉評論',
|
|
43
|
+
listLoadFailMsg: '無法獲取評論列表數據',
|
|
44
|
+
listRetry: '點擊重新獲取',
|
|
45
|
+
loadMore: '加載更多',
|
|
46
|
+
|
|
47
|
+
/* Comment */
|
|
48
|
+
admin: '管理員',
|
|
49
|
+
reply: '回覆',
|
|
50
|
+
voteUp: '贊同',
|
|
51
|
+
voteDown: '反對',
|
|
52
|
+
voteFail: '投票失敗',
|
|
53
|
+
readMore: '閱讀更多',
|
|
54
|
+
actionConfirm: '確認操作',
|
|
55
|
+
collapse: '摺疊',
|
|
56
|
+
collapsed: '已摺疊',
|
|
57
|
+
collapsedMsg: '該評論已被系統或管理員摺疊',
|
|
58
|
+
expand: '展開',
|
|
59
|
+
approved: '已審',
|
|
60
|
+
pending: '待審',
|
|
61
|
+
pendingMsg: '審核中,僅本人可見。',
|
|
62
|
+
edit: '編輯',
|
|
63
|
+
editCancel: '取消編輯',
|
|
64
|
+
delete: '刪除',
|
|
65
|
+
deleteConfirm: '確認刪除',
|
|
66
|
+
pin: '置頂',
|
|
67
|
+
unpin: '取消置頂',
|
|
68
|
+
|
|
69
|
+
/* Time */
|
|
70
|
+
seconds: '秒前',
|
|
71
|
+
minutes: '分鐘前',
|
|
72
|
+
hours: '小時前',
|
|
73
|
+
days: '天前',
|
|
74
|
+
now: '剛剛',
|
|
75
|
+
|
|
76
|
+
/* Checker */
|
|
77
|
+
adminCheck: '鍵入密碼來驗證管理員身份:',
|
|
78
|
+
captchaCheck: '鍵入驗證碼繼續:',
|
|
79
|
+
confirm: '確認',
|
|
80
|
+
cancel: '取消',
|
|
81
|
+
|
|
82
|
+
/* Sidebar */
|
|
83
|
+
msgCenter: '通知中心',
|
|
84
|
+
ctrlCenter: '控制中心',
|
|
85
|
+
|
|
86
|
+
/* General */
|
|
87
|
+
frontend: '前端',
|
|
88
|
+
backend: '後端',
|
|
89
|
+
loading: '加載中',
|
|
90
|
+
loadFail: '加載失敗',
|
|
91
|
+
editing: '修改中',
|
|
92
|
+
editFail: '修改失敗',
|
|
93
|
+
deleting: '刪除中',
|
|
94
|
+
deleteFail: '刪除失敗',
|
|
95
|
+
reqGot: '請求響應',
|
|
96
|
+
reqAborted: '請求超時或意外終止',
|
|
97
|
+
},
|
|
18
98
|
'fr-FR': {},
|
|
19
99
|
'de-DE': {},
|
|
20
100
|
'bn-IN': {},
|
|
21
101
|
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* get a locale object by language name
|
|
105
|
+
*/
|
|
106
|
+
function getLocaleSet(lang: string): I18n {
|
|
107
|
+
// normalize a key of language to `ISO 639`
|
|
108
|
+
lang = lang.replace(
|
|
109
|
+
/^([a-zA-Z]+)(-[a-zA-Z]+)?$/,
|
|
110
|
+
(_, p1: string, p2: string) => (p1.toLowerCase() + (p2 || '').toUpperCase())
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
// case not found
|
|
114
|
+
if (!internal[lang]) {
|
|
115
|
+
return internal.en // use `en` by default
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return internal[lang]
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* get a i18n message by key
|
|
123
|
+
*/
|
|
124
|
+
function getI18n(locale: I18n|string, key: keyof I18n, args: {[key: string]: string} = {}) {
|
|
125
|
+
if (typeof locale === 'string') {
|
|
126
|
+
locale = getLocaleSet(locale)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let str = locale?.[key] || key
|
|
130
|
+
str = str.replace(/\{\s*(\w+?)\s*\}/g, (_, token) => args[token] || '')
|
|
131
|
+
|
|
132
|
+
return str
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { getLocaleSet, getI18n }
|
|
136
|
+
export default getI18n
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import '../style/sidebar-layer.less'
|
|
2
|
-
|
|
3
1
|
import Context from '~/types/context'
|
|
4
2
|
import Component from '@/lib/component'
|
|
5
3
|
import * as Utils from '@/lib/utils'
|
|
@@ -62,7 +60,7 @@ export default class SidebarLayer extends Component {
|
|
|
62
60
|
|
|
63
61
|
// 管理员身份验证 (若身份失效,弹出验证窗口)
|
|
64
62
|
;(async () => {
|
|
65
|
-
const resp = await this.ctx.getApi().loginStatus()
|
|
63
|
+
const resp = await this.ctx.getApi().user.loginStatus()
|
|
66
64
|
if (resp.is_admin && !resp.is_login) {
|
|
67
65
|
this.layer?.hide()
|
|
68
66
|
this.firstShow = true
|
|
@@ -4,7 +4,7 @@ import { Checker } from '.'
|
|
|
4
4
|
|
|
5
5
|
const CaptchaChecker: Checker = {
|
|
6
6
|
request(that, ctx, inputVal) {
|
|
7
|
-
return that.ctx.getApi().captchaCheck(inputVal)
|
|
7
|
+
return that.ctx.getApi().captcha.captchaCheck(inputVal)
|
|
8
8
|
},
|
|
9
9
|
|
|
10
10
|
body(that, ctx) {
|
|
@@ -34,7 +34,7 @@ const CaptchaChecker: Checker = {
|
|
|
34
34
|
if (stop) return
|
|
35
35
|
let isPass = false
|
|
36
36
|
try {
|
|
37
|
-
const resp = await that.ctx.getApi().captchaStatus()
|
|
37
|
+
const resp = await that.ctx.getApi().captcha.captchaStatus()
|
|
38
38
|
isPass = resp.is_pass
|
|
39
39
|
} catch { isPass = false }
|
|
40
40
|
if (isPass) {
|
|
@@ -60,7 +60,7 @@ const CaptchaChecker: Checker = {
|
|
|
60
60
|
// 刷新验证码
|
|
61
61
|
elem.querySelector<HTMLElement>('.atk-captcha-img')!.onclick = () => {
|
|
62
62
|
const imgEl = elem.querySelector('.atk-captcha-img')
|
|
63
|
-
that.ctx.getApi().captchaGet()
|
|
63
|
+
that.ctx.getApi().captcha.captchaGet()
|
|
64
64
|
.then((imgData) => {
|
|
65
65
|
imgEl!.setAttribute('src', imgData)
|
|
66
66
|
})
|
package/src/lib/stat.ts
CHANGED
|
@@ -21,7 +21,7 @@ export async function initCountWidget(p: CountConf) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// PV
|
|
24
|
-
const curtPagePvNum = p.pvAdd ? await p.ctx.getApi().pv() : undefined
|
|
24
|
+
const curtPagePvNum = p.pvAdd ? await p.ctx.getApi().page.pv() : undefined
|
|
25
25
|
const pvEl = p.ctx.conf.pvEl
|
|
26
26
|
if (pvEl && document.querySelector(pvEl)) {
|
|
27
27
|
handleStatCount(p, {
|
|
@@ -52,7 +52,7 @@ export async function handleStatCount(
|
|
|
52
52
|
.filter((pageKey) => pageCounts[pageKey] === undefined)
|
|
53
53
|
queryPageKeys = [...new Set(queryPageKeys)] // 去重
|
|
54
54
|
if (queryPageKeys.length > 0) {
|
|
55
|
-
const counts: any = await p.ctx.getApi().stat(args.api, queryPageKeys)
|
|
55
|
+
const counts: any = await p.ctx.getApi().page.stat(args.api, queryPageKeys)
|
|
56
56
|
pageCounts = { ...pageCounts, ...counts }
|
|
57
57
|
}
|
|
58
58
|
|
package/src/lib/ui.ts
CHANGED
|
@@ -32,6 +32,12 @@ export function hideLoading(parentElem: HTMLElement) {
|
|
|
32
32
|
if ($loading) $loading.style.display = 'none'
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/** 元素加载动画 */
|
|
36
|
+
export function setLoading(val: boolean, parentElem: HTMLElement) {
|
|
37
|
+
if (val) showLoading(parentElem)
|
|
38
|
+
else hideLoading(parentElem)
|
|
39
|
+
}
|
|
40
|
+
|
|
35
41
|
/** 元素是否用户可见 */
|
|
36
42
|
export function isVisible(el: HTMLElement, viewport: HTMLElement = document.documentElement) {
|
|
37
43
|
const viewportHeight = viewport.clientHeight
|