artalk 2.4.1 → 2.4.3
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 +206 -104
- package/dist/Artalk.iife.js +1 -1
- package/dist/Artalk.js +1 -1
- package/dist/ArtalkLite.css +1 -1
- package/dist/ArtalkLite.es.js +206 -104
- package/dist/ArtalkLite.iife.js +1 -1
- package/dist/ArtalkLite.js +1 -1
- package/dist/i18n/jp.js +1 -0
- package/dist/i18n/zh-TW.js +1 -0
- package/dist/src/api/index.d.ts +1 -1
- package/dist/src/api/system.d.ts +7 -0
- package/dist/src/api/user.d.ts +16 -2
- package/dist/src/artalk.d.ts +0 -6
- package/dist/src/context.d.ts +4 -0
- package/dist/src/i18n/index.d.ts +68 -74
- package/dist/src/list/list-lite.d.ts +17 -7
- package/dist/src/list/list.d.ts +1 -0
- package/dist/types/artalk-config.d.ts +5 -1
- package/dist/types/artalk-data.d.ts +23 -0
- package/dist/types/context.d.ts +3 -0
- package/dist/types/event.d.ts +1 -1
- package/package.json +4 -3
- package/scripts/build-i18n.mjs +46 -0
- package/src/api/index.ts +3 -2
- package/src/api/system.ts +7 -0
- package/src/api/user.ts +59 -2
- package/src/artalk.ts +0 -35
- package/src/comment/render.ts +2 -1
- package/src/context.ts +33 -3
- package/src/defaults.ts +2 -2
- package/src/editor/editor.ts +8 -8
- package/src/editor/plugs/upload-plug.ts +1 -1
- package/src/i18n/index.ts +18 -90
- package/src/i18n/jp.ts +83 -0
- package/src/i18n/zh-TW.ts +83 -0
- package/src/layer/sidebar-layer.ts +10 -8
- package/src/lib/checker/admin-checker.ts +4 -1
- package/src/lib/checker/captcha-checker.ts +1 -0
- package/src/lib/stat.ts +9 -1
- package/src/lib/utils.ts +8 -1
- package/src/list/list-lite.ts +68 -26
- package/src/list/list.ts +10 -18
- package/src/style/main.less +17 -0
- package/test/ArtalkI18n.html +1 -0
- package/types/artalk-config.d.ts +5 -1
- package/types/artalk-data.d.ts +23 -0
- package/types/context.d.ts +3 -0
- package/types/event.d.ts +1 -1
package/dist/i18n/jp.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e="undefined"!=typeof globalThis?globalThis:e||self).jp=n()}(this,(function(){"use strict";var e,n,o;return e="jp",n={placeholder:"コメントを残す",noComment:"コメントなし",send:"送信",save:"保存",nick:"ニックネーム",email:"メール",link:"ウェブサイト",emoticon:"絵文字",preview:"プレビュー",image:"画像",uploadFail:"アップロードに失敗しました",commentFail:"コメントに失敗しました",restoredMsg:"コンテンツが復元されました",onlyAdminCanReply:"管理者のみが返信できます",uploadLoginMsg:"アップロードするには、名前とメールアドレスを入力してください",counter:"{count} コメント",sortLatest:"最新のもの",sortOldest:"最も古い",sortBest:"ベスト",sortAuthor:"著者",openComment:"コメントを開く",closeComment:"コメントを閉じる",listLoadFailMsg:"コメントの読み込みに失敗しました",listRetry:"クリックで再試行",loadMore:"もっと読み込む",admin:"管理者",reply:"返信",voteUp:"アップ",voteDown:"ダウン",voteFail:"投票に失敗しました",readMore:"続きを読む",actionConfirm:"確認",collapse:"折りたたむ",collapsed:"折りたたまれた",collapsedMsg:"このコメントは折りたたまれました",expand:"展開",approved:"承認されました",pending:"保留中",pendingMsg:"保留中、コメント投稿者にのみ表示されます",edit:"編集",editCancel:"編集をキャンセル",delete:"削除",deleteConfirm:"確認",pin:"ピン",unpin:"ピン外し",seconds:"秒前",minutes:"分前",hours:"時間前",days:"数日前",now:"たった今",adminCheck:"管理者パスワードを入力してください:",captchaCheck:"続けるにはCAPTCHAを入力してください:",confirm:"確認",cancel:"取消",msgCenter:"メッセージ",ctrlCenter:"コンソール",frontend:"フロントエンド",backend:"バックエンド",loading:"読み込み中",loadFail:"読み込みに失敗しました",editing:"編集中",editFail:"編集に失敗しました",deleting:"削除中",deleteFail:"削除に失敗しました",reqGot:"リクエストを取得しました",reqAborted:"リクエストがタイムアウトしたか、予期せず終了した"},o=["jp-JP"],window.ArtalkI18n||(window.ArtalkI18n={}),window.ArtalkI18n[e]=n,o&&o.forEach((e=>{window.ArtalkI18n[e]=n})),n}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e="undefined"!=typeof globalThis?globalThis:e||self)["zh-TW"]=n()}(this,(function(){"use strict";var e,n,o;return e="zh-TW",n={placeholder:"鍵入內容...",noComment:"「此時無聲勝有聲」",send:"發送評論",save:"保存評論",nick:"昵稱",email:"郵箱",link:"網址",emoticon:"表情",preview:"預覽",image:"圖片",uploadFail:"上傳失敗",commentFail:"評論失敗",restoredMsg:"內容已自動恢復",onlyAdminCanReply:"僅管理員可評論",uploadLoginMsg:"填入你的名字郵箱才能上傳哦",counter:"{count} 條評論",sortLatest:"最新",sortOldest:"最早",sortBest:"最熱",sortAuthor:"作者",openComment:"打開評論",closeComment:"關閉評論",listLoadFailMsg:"無法獲取評論列表數據",listRetry:"點擊重新獲取",loadMore:"加載更多",admin:"管理員",reply:"回覆",voteUp:"贊同",voteDown:"反對",voteFail:"投票失敗",readMore:"閱讀更多",actionConfirm:"確認操作",collapse:"摺疊",collapsed:"已摺疊",collapsedMsg:"該評論已被系統或管理員摺疊",expand:"展開",approved:"已審",pending:"待審",pendingMsg:"審核中,僅本人可見。",edit:"編輯",editCancel:"取消編輯",delete:"刪除",deleteConfirm:"確認刪除",pin:"置頂",unpin:"取消置頂",seconds:"秒前",minutes:"分鐘前",hours:"小時前",days:"天前",now:"剛剛",adminCheck:"鍵入密碼來驗證管理員身份:",captchaCheck:"鍵入驗證碼繼續:",confirm:"確認",cancel:"取消",msgCenter:"通知中心",ctrlCenter:"控制中心",frontend:"前端",backend:"後端",loading:"加載中",loadFail:"加載失敗",editing:"修改中",editFail:"修改失敗",deleting:"刪除中",deleteFail:"刪除失敗",reqGot:"請求響應",reqAborted:"請求超時或意外終止"},window.ArtalkI18n||(window.ArtalkI18n={}),window.ArtalkI18n[e]=n,o&&o.forEach((e=>{window.ArtalkI18n[e]=n})),n}));
|
package/dist/src/api/index.d.ts
CHANGED
package/dist/src/api/system.d.ts
CHANGED
|
@@ -10,4 +10,11 @@ export default class SystemApi extends ApiBase {
|
|
|
10
10
|
getSettings(): Promise<string>;
|
|
11
11
|
/** 保存配置数据 */
|
|
12
12
|
saveSettings(yamlStr: string): Promise<boolean>;
|
|
13
|
+
/** 获取 API 版本信息 */
|
|
14
|
+
version(): Promise<{
|
|
15
|
+
app: string;
|
|
16
|
+
version: string;
|
|
17
|
+
commit_hash: string;
|
|
18
|
+
fe_min_version: string;
|
|
19
|
+
}>;
|
|
13
20
|
}
|
package/dist/src/api/user.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { UserData, NotifyData } from '../../types/artalk-data';
|
|
1
|
+
import { UserData, NotifyData, UserDataForAdmin } from '../../types/artalk-data';
|
|
2
2
|
import ApiBase from './api-base';
|
|
3
3
|
/**
|
|
4
4
|
* 用户 API
|
|
5
5
|
*/
|
|
6
6
|
export default class UserApi extends ApiBase {
|
|
7
7
|
/** 用户 · 登录 */
|
|
8
|
-
login(name: string, email: string, password: string): Promise<
|
|
8
|
+
login(name: string, email: string, password: string): Promise<{
|
|
9
|
+
token: string;
|
|
10
|
+
user: UserData;
|
|
11
|
+
}>;
|
|
9
12
|
/** 用户 · 获取 */
|
|
10
13
|
userGet(name: string, email: string): {
|
|
11
14
|
req: Promise<{
|
|
@@ -25,4 +28,15 @@ export default class UserApi extends ApiBase {
|
|
|
25
28
|
logout(): Promise<unknown>;
|
|
26
29
|
/** 已读标记 */
|
|
27
30
|
markRead(notifyKey: string, readAll?: boolean): Promise<unknown>;
|
|
31
|
+
/** 用户 · 列表 */
|
|
32
|
+
userList(offset?: number, limit?: number, type?: 'all' | 'admin' | 'in_conf'): Promise<{
|
|
33
|
+
users: UserDataForAdmin[];
|
|
34
|
+
total: number;
|
|
35
|
+
}>;
|
|
36
|
+
/** 用户 · 新增 */
|
|
37
|
+
userAdd(user: Partial<UserDataForAdmin>, password?: string): Promise<UserDataForAdmin>;
|
|
38
|
+
/** 用户 · 修改 */
|
|
39
|
+
userEdit(user: Partial<UserDataForAdmin>, password?: string): Promise<UserDataForAdmin>;
|
|
40
|
+
/** 用户 · 删除 */
|
|
41
|
+
userDel(userID: number): Promise<unknown>;
|
|
28
42
|
}
|
package/dist/src/artalk.d.ts
CHANGED
|
@@ -25,18 +25,12 @@ export default class Artalk {
|
|
|
25
25
|
private initComponents;
|
|
26
26
|
/** 基本配置初始化 */
|
|
27
27
|
static HandelBaseConf(customConf: Partial<ArtalkConfig>): ArtalkConfig;
|
|
28
|
-
/** 初始化组件根据远程加载的配置 */
|
|
29
|
-
private loadConfRemoteAndInitComponents;
|
|
30
|
-
/** 远程加载配置 */
|
|
31
|
-
private loadConfRemote;
|
|
32
28
|
/** 事件绑定初始化 */
|
|
33
29
|
private initEventBind;
|
|
34
30
|
/** 语言初始化 */
|
|
35
31
|
private initLocale;
|
|
36
32
|
/** Layer 初始化 */
|
|
37
33
|
private initLayer;
|
|
38
|
-
/** 暗黑模式初始化 */
|
|
39
|
-
private initDarkMode;
|
|
40
34
|
/** 监听事件 */
|
|
41
35
|
on<K extends keyof EventPayloadMap>(name: K, handler: Handler<EventPayloadMap[K]>): void;
|
|
42
36
|
/** 解除监听事件 */
|
package/dist/src/context.d.ts
CHANGED
|
@@ -69,4 +69,8 @@ export default class Context implements ContextApi {
|
|
|
69
69
|
[key: string]: string;
|
|
70
70
|
}): string;
|
|
71
71
|
setDarkMode(darkMode: boolean): void;
|
|
72
|
+
private darkModeMedia;
|
|
73
|
+
private darkModeAutoFunc?;
|
|
74
|
+
private refreshDarkModeConf;
|
|
75
|
+
updateConf(conf: Partial<ArtalkConfig>): void;
|
|
72
76
|
}
|
package/dist/src/i18n/index.d.ts
CHANGED
|
@@ -206,79 +206,73 @@ export declare const internal: {
|
|
|
206
206
|
reqAborted: string;
|
|
207
207
|
};
|
|
208
208
|
};
|
|
209
|
-
export declare
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
reqGot: string;
|
|
277
|
-
reqAborted: string;
|
|
278
|
-
};
|
|
279
|
-
'fr-FR': {};
|
|
280
|
-
'de-DE': {};
|
|
281
|
-
'bn-IN': {};
|
|
209
|
+
export declare function defineLocaleExternal(lang: string, locale: I18n, aliases?: string[]): {
|
|
210
|
+
placeholder: string;
|
|
211
|
+
noComment: string;
|
|
212
|
+
send: string;
|
|
213
|
+
save: string;
|
|
214
|
+
nick: string;
|
|
215
|
+
email: string;
|
|
216
|
+
link: string;
|
|
217
|
+
emoticon: string;
|
|
218
|
+
preview: string;
|
|
219
|
+
image: string;
|
|
220
|
+
uploadFail: string;
|
|
221
|
+
commentFail: string;
|
|
222
|
+
restoredMsg: string;
|
|
223
|
+
onlyAdminCanReply: string;
|
|
224
|
+
uploadLoginMsg: string;
|
|
225
|
+
counter: string;
|
|
226
|
+
sortLatest: string;
|
|
227
|
+
sortOldest: string;
|
|
228
|
+
sortBest: string;
|
|
229
|
+
sortAuthor: string;
|
|
230
|
+
openComment: string;
|
|
231
|
+
closeComment: string;
|
|
232
|
+
listLoadFailMsg: string;
|
|
233
|
+
listRetry: string;
|
|
234
|
+
loadMore: string;
|
|
235
|
+
admin: string;
|
|
236
|
+
reply: string;
|
|
237
|
+
voteUp: string;
|
|
238
|
+
voteDown: string;
|
|
239
|
+
voteFail: string;
|
|
240
|
+
readMore: string;
|
|
241
|
+
actionConfirm: string;
|
|
242
|
+
collapse: string;
|
|
243
|
+
collapsed: string;
|
|
244
|
+
collapsedMsg: string;
|
|
245
|
+
expand: string;
|
|
246
|
+
approved: string;
|
|
247
|
+
pending: string;
|
|
248
|
+
pendingMsg: string;
|
|
249
|
+
edit: string;
|
|
250
|
+
editCancel: string;
|
|
251
|
+
delete: string;
|
|
252
|
+
deleteConfirm: string;
|
|
253
|
+
pin: string;
|
|
254
|
+
unpin: string;
|
|
255
|
+
seconds: string;
|
|
256
|
+
minutes: string;
|
|
257
|
+
hours: string;
|
|
258
|
+
days: string;
|
|
259
|
+
now: string;
|
|
260
|
+
adminCheck: string;
|
|
261
|
+
captchaCheck: string;
|
|
262
|
+
confirm: string;
|
|
263
|
+
cancel: string;
|
|
264
|
+
msgCenter: string;
|
|
265
|
+
ctrlCenter: string;
|
|
266
|
+
frontend: string;
|
|
267
|
+
backend: string;
|
|
268
|
+
loading: string;
|
|
269
|
+
loadFail: string;
|
|
270
|
+
editing: string;
|
|
271
|
+
editFail: string;
|
|
272
|
+
deleting: string;
|
|
273
|
+
deleteFail: string;
|
|
274
|
+
reqGot: string;
|
|
275
|
+
reqAborted: string;
|
|
282
276
|
};
|
|
283
277
|
/**
|
|
284
278
|
* get a locale object by language name
|
|
@@ -290,5 +284,5 @@ declare function getLocaleSet(lang: string): I18n;
|
|
|
290
284
|
declare function getI18n(locale: I18n | string, key: keyof I18n, args?: {
|
|
291
285
|
[key: string]: string;
|
|
292
286
|
}): string;
|
|
293
|
-
export { getLocaleSet
|
|
287
|
+
export { getLocaleSet };
|
|
294
288
|
export default getI18n;
|
|
@@ -9,14 +9,23 @@ export default class ListLite extends Component {
|
|
|
9
9
|
protected $commentsWrap: HTMLElement;
|
|
10
10
|
protected data?: ListData;
|
|
11
11
|
protected isLoading: boolean;
|
|
12
|
-
noCommentText
|
|
12
|
+
noCommentText?: string;
|
|
13
|
+
/** 嵌套模式下的排序方式 */
|
|
14
|
+
private _nestSortBy?;
|
|
15
|
+
get nestSortBy(): ListNest.SortByType;
|
|
16
|
+
set nestSortBy(val: ListNest.SortByType);
|
|
13
17
|
/** 平铺模式 */
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
private _flatMode?;
|
|
19
|
+
get flatMode(): boolean;
|
|
20
|
+
set flatMode(val: boolean);
|
|
21
|
+
/** 分页方式 */
|
|
22
|
+
_pageMode?: 'pagination' | 'read-more';
|
|
23
|
+
get pageMode(): 'pagination' | 'read-more';
|
|
24
|
+
set pageMode(val: 'pagination' | 'read-more');
|
|
25
|
+
/** 每页数量 (每次请求获取量) */
|
|
26
|
+
private _pageSize?;
|
|
27
|
+
get pageSize(): number;
|
|
28
|
+
set pageSize(val: number);
|
|
20
29
|
scrollListenerAt?: HTMLElement;
|
|
21
30
|
repositionAt?: HTMLElement;
|
|
22
31
|
protected pagination?: Pagination;
|
|
@@ -35,6 +44,7 @@ export default class ListLite extends Component {
|
|
|
35
44
|
setLoading(val: boolean, isFirstLoad?: boolean): void;
|
|
36
45
|
/** 评论获取 */
|
|
37
46
|
fetchComments(offset: number): Promise<void>;
|
|
47
|
+
private confLoaded;
|
|
38
48
|
protected onLoad(data: ListData, offset: number): void;
|
|
39
49
|
/** 分页模式 */
|
|
40
50
|
private refreshPagination;
|
package/dist/src/list/list.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export default class List extends ListLite {
|
|
|
20
20
|
/** 管理员设置页面信息 */
|
|
21
21
|
adminPageEditSave(): void;
|
|
22
22
|
showUnreadBadge(count: number): void;
|
|
23
|
+
private dropdownLoaded;
|
|
23
24
|
/** 初始化选择下拉层 */
|
|
24
25
|
protected initDropdown(): void;
|
|
25
26
|
updateUnread(notifies: NotifyData[]): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { I18n } from '../src/i18n'
|
|
2
|
+
import { CommentData } from './artalk-data'
|
|
2
3
|
|
|
3
4
|
export default interface ArtalkConfig {
|
|
4
5
|
/** 装载元素 */
|
|
@@ -39,6 +40,9 @@ export default interface ArtalkConfig {
|
|
|
39
40
|
default: string
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
/** 头像链接生成器 */
|
|
44
|
+
avatarURLBuilder?: (comment: CommentData) => string,
|
|
45
|
+
|
|
42
46
|
/** 分页配置 */
|
|
43
47
|
pagination: {
|
|
44
48
|
/** 每次请求获取数量 */
|
|
@@ -103,7 +107,7 @@ export default interface ArtalkConfig {
|
|
|
103
107
|
/** 版本检测 */
|
|
104
108
|
versionCheck: boolean
|
|
105
109
|
|
|
106
|
-
/**
|
|
110
|
+
/** 引用后端配置 */
|
|
107
111
|
useBackendConf: boolean
|
|
108
112
|
|
|
109
113
|
/** 语言本地化 */
|
|
@@ -159,6 +159,29 @@ export interface UserData {
|
|
|
159
159
|
|
|
160
160
|
/** 是否属于管理员 */
|
|
161
161
|
is_admin: boolean
|
|
162
|
+
|
|
163
|
+
/** 所属站点名 */
|
|
164
|
+
site_names: string[]
|
|
165
|
+
|
|
166
|
+
/** 所属站点名 (字符串) */
|
|
167
|
+
site_names_raw: string,
|
|
168
|
+
|
|
169
|
+
/** 是否允许接收邮件通知 */
|
|
170
|
+
receive_email: boolean
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface UserDataForAdmin extends UserData {
|
|
174
|
+
/** 最后一次操作 IP */
|
|
175
|
+
last_ip: string
|
|
176
|
+
|
|
177
|
+
/** 最后一次操作 UA */
|
|
178
|
+
last_ua: string
|
|
179
|
+
|
|
180
|
+
/** 是否存在于配置文件中 */
|
|
181
|
+
is_in_conf: boolean
|
|
182
|
+
|
|
183
|
+
/** 评论数 */
|
|
184
|
+
comment_count: number
|
|
162
185
|
}
|
|
163
186
|
|
|
164
187
|
export interface NotifyData {
|
package/dist/types/context.d.ts
CHANGED
package/dist/types/event.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface EventPayloadMap {
|
|
|
9
9
|
'editor-submit': undefined // 编辑器提交时
|
|
10
10
|
'editor-submitted': undefined // 编辑器提交后
|
|
11
11
|
'user-changed': LocalUser // 本地用户数据变更时
|
|
12
|
-
'conf-
|
|
12
|
+
'conf-loaded': undefined // Artalk 配置变更时
|
|
13
13
|
'sidebar-show': undefined // 侧边栏显示
|
|
14
14
|
'sidebar-hide': undefined // 侧边栏隐藏
|
|
15
15
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "artalk",
|
|
3
|
-
"version": "2.4.
|
|
4
|
-
"backendMinVersion": "2.
|
|
3
|
+
"version": "2.4.3",
|
|
4
|
+
"backendMinVersion": "2.3.0",
|
|
5
5
|
"license": "LGPL-3.0",
|
|
6
6
|
"description": "A Selfhosted Comment System",
|
|
7
7
|
"keywords": [
|
|
@@ -34,8 +34,9 @@
|
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"dev": "vite",
|
|
37
|
-
"build": "pnpm lint && vite build && pnpm build:lite",
|
|
37
|
+
"build": "pnpm lint && vite build && pnpm build:lite && pnpm build:i18n",
|
|
38
38
|
"build:lite": "vite build --config vite-lite.config.ts",
|
|
39
|
+
"build:i18n": "node scripts/build-i18n.mjs",
|
|
39
40
|
"build:demo": "vite build --config vite-demo.config.ts",
|
|
40
41
|
"serve": "vite preview",
|
|
41
42
|
"deploy": "gh-pages -d deploy",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { build } from 'vite'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
const i18nPath = path.join(__dirname, '../src/i18n/')
|
|
8
|
+
const outDir = path.resolve(__dirname, '../dist/i18n')
|
|
9
|
+
|
|
10
|
+
// empty outDir before build
|
|
11
|
+
fs.rmSync(outDir, { recursive: true, force: true })
|
|
12
|
+
|
|
13
|
+
const libraries = []
|
|
14
|
+
|
|
15
|
+
fs.readdirSync(i18nPath).forEach(f => {
|
|
16
|
+
if (['index.ts'].includes(f)) return
|
|
17
|
+
|
|
18
|
+
const filename = path.join(i18nPath, f)
|
|
19
|
+
const lang = path.parse(filename).name
|
|
20
|
+
const content = fs.readFileSync(filename);
|
|
21
|
+
|
|
22
|
+
// only compile the external locale
|
|
23
|
+
if (!content.toString().includes('defineLocaleExternal')) return
|
|
24
|
+
|
|
25
|
+
libraries.push({
|
|
26
|
+
entry: filename,
|
|
27
|
+
name: lang,
|
|
28
|
+
fileName: (format) => ((format == "umd") ? `${lang}.js` : `${lang}.${format}.js`),
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
libraries.forEach(async (lib) => {
|
|
33
|
+
await build({
|
|
34
|
+
build: {
|
|
35
|
+
target: 'es2015',
|
|
36
|
+
outDir: outDir,
|
|
37
|
+
minify: 'terser',
|
|
38
|
+
emptyOutDir: false,
|
|
39
|
+
lib: {
|
|
40
|
+
...lib,
|
|
41
|
+
formats: ['umd'],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
configFile: false, // prevent load any vite config file
|
|
45
|
+
})
|
|
46
|
+
})
|
package/src/api/index.ts
CHANGED
|
@@ -18,11 +18,12 @@ const ApiComponents = {
|
|
|
18
18
|
|
|
19
19
|
class Api {
|
|
20
20
|
protected ctx: Context
|
|
21
|
-
public baseURL
|
|
21
|
+
public get baseURL() {
|
|
22
|
+
return `${this.ctx.conf.server}/api`
|
|
23
|
+
}
|
|
22
24
|
|
|
23
25
|
constructor (ctx: Context) {
|
|
24
26
|
this.ctx = ctx
|
|
25
|
-
this.baseURL = `${ctx.conf.server}/api`
|
|
26
27
|
|
|
27
28
|
Object.entries(ApiComponents).forEach(([key, ApiComponent]) => {
|
|
28
29
|
this[key] = new ApiComponent(this, this.ctx)
|
package/src/api/system.ts
CHANGED
|
@@ -36,4 +36,11 @@ export default class SystemApi extends ApiBase {
|
|
|
36
36
|
})
|
|
37
37
|
return data
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
/** 获取 API 版本信息 */
|
|
41
|
+
public async version() {
|
|
42
|
+
const resp = await fetch(`${this.api.baseURL}/version`, { method: 'POST' })
|
|
43
|
+
const data = await resp.json()
|
|
44
|
+
return data as { app: string, version: string, commit_hash: string, fe_min_version: string }
|
|
45
|
+
}
|
|
39
46
|
}
|
package/src/api/user.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UserData, NotifyData } from '~/types/artalk-data'
|
|
1
|
+
import { UserData, NotifyData, UserDataForAdmin } from '~/types/artalk-data'
|
|
2
2
|
import ApiBase from './api-base'
|
|
3
3
|
import { ToFormData } from './request'
|
|
4
4
|
|
|
@@ -15,7 +15,7 @@ export default class UserApi extends ApiBase {
|
|
|
15
15
|
if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
|
|
16
16
|
|
|
17
17
|
const data = await this.POST<any>('/login', params)
|
|
18
|
-
return
|
|
18
|
+
return data as { token: string, user: UserData }
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/** 用户 · 获取 */
|
|
@@ -72,4 +72,61 @@ export default class UserApi extends ApiBase {
|
|
|
72
72
|
|
|
73
73
|
return this.POST(`/mark-read`, params)
|
|
74
74
|
}
|
|
75
|
+
|
|
76
|
+
/** 用户 · 列表 */
|
|
77
|
+
public async userList(offset?: number, limit?: number, type?: 'all'|'admin'|'in_conf') {
|
|
78
|
+
const params: any = {
|
|
79
|
+
offset: offset || 0,
|
|
80
|
+
limit: limit || 15,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (type) params.type = type
|
|
84
|
+
|
|
85
|
+
const d = await this.POST<any>('/admin/user-get', params)
|
|
86
|
+
return (d as { users: UserDataForAdmin[], total: number })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** 用户 · 新增 */
|
|
90
|
+
public async userAdd(user: Partial<UserDataForAdmin>, password?: string) {
|
|
91
|
+
const params: any = {
|
|
92
|
+
name: user.name || '',
|
|
93
|
+
email: user.email || '',
|
|
94
|
+
password: password || '',
|
|
95
|
+
link: user.link || '',
|
|
96
|
+
is_admin: user.is_admin || false,
|
|
97
|
+
site_names: user.site_names_raw || '',
|
|
98
|
+
receive_email: user.receive_email || true,
|
|
99
|
+
badge_name: user.badge_name || '',
|
|
100
|
+
badge_color: user.badge_color || '',
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const d = await this.POST<any>('/admin/user-add', params)
|
|
104
|
+
return (d.user as UserDataForAdmin)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** 用户 · 修改 */
|
|
108
|
+
public async userEdit(user: Partial<UserDataForAdmin>, password?: string) {
|
|
109
|
+
const params: any = {
|
|
110
|
+
id: user.id,
|
|
111
|
+
name: user.name || '',
|
|
112
|
+
email: user.email || '',
|
|
113
|
+
password: password || '',
|
|
114
|
+
link: user.link || '',
|
|
115
|
+
is_admin: user.is_admin || false,
|
|
116
|
+
site_names: user.site_names_raw || '',
|
|
117
|
+
receive_email: user.receive_email || true,
|
|
118
|
+
badge_name: user.badge_name || '',
|
|
119
|
+
badge_color: user.badge_color || '',
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const d = await this.POST<any>('/admin/user-edit', params)
|
|
123
|
+
return (d.user as UserDataForAdmin)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** 用户 · 删除 */
|
|
127
|
+
public userDel(userID: number) {
|
|
128
|
+
return this.POST('/admin/user-del', {
|
|
129
|
+
id: String(userID)
|
|
130
|
+
})
|
|
131
|
+
}
|
|
75
132
|
}
|
package/src/artalk.ts
CHANGED
|
@@ -48,9 +48,6 @@ export default class Artalk {
|
|
|
48
48
|
/* 初始化 Context */
|
|
49
49
|
this.ctx = new ConcreteContext(this.conf, this.$root)
|
|
50
50
|
|
|
51
|
-
// @ts-ignore 远程加载引用后端的配置
|
|
52
|
-
if (this.conf.useBackendConf) return this.loadConfRemoteAndInitComponents()
|
|
53
|
-
|
|
54
51
|
/* 初始化组件 */
|
|
55
52
|
this.initComponents()
|
|
56
53
|
}
|
|
@@ -59,7 +56,6 @@ export default class Artalk {
|
|
|
59
56
|
private initComponents() {
|
|
60
57
|
this.initLocale()
|
|
61
58
|
this.initLayer()
|
|
62
|
-
this.initDarkMode()
|
|
63
59
|
Utils.initMarked(this.ctx)
|
|
64
60
|
|
|
65
61
|
const Components: { [name: string]: () => void } = {
|
|
@@ -91,7 +87,6 @@ export default class Artalk {
|
|
|
91
87
|
sidebarLayer: () => {
|
|
92
88
|
const sidebarLayer = new SidebarLayer(this.ctx)
|
|
93
89
|
this.ctx.setSidebarLayer(sidebarLayer)
|
|
94
|
-
this.$root.appendChild(sidebarLayer.$el)
|
|
95
90
|
},
|
|
96
91
|
|
|
97
92
|
// 默认事件绑定
|
|
@@ -148,24 +143,6 @@ export default class Artalk {
|
|
|
148
143
|
return conf
|
|
149
144
|
}
|
|
150
145
|
|
|
151
|
-
/** 初始化组件根据远程加载的配置 */
|
|
152
|
-
private async loadConfRemoteAndInitComponents() {
|
|
153
|
-
await this.loadConfRemote()
|
|
154
|
-
this.initComponents()
|
|
155
|
-
return this
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/** 远程加载配置 */
|
|
159
|
-
private async loadConfRemote() {
|
|
160
|
-
Ui.showLoading(this.$root)
|
|
161
|
-
let backendConf = {}
|
|
162
|
-
try {
|
|
163
|
-
backendConf = await this.ctx.getApi().system.conf()
|
|
164
|
-
} catch (err) { console.error("Load config from remote err", err) }
|
|
165
|
-
this.ctx.conf = Utils.mergeDeep(this.ctx.conf, backendConf)
|
|
166
|
-
Ui.hideLoading(this.$root)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
146
|
/** 事件绑定初始化 */
|
|
170
147
|
private initEventBind() {
|
|
171
148
|
// 锚点快速跳转评论
|
|
@@ -194,18 +171,6 @@ export default class Artalk {
|
|
|
194
171
|
Layer.BodyOrgPaddingRight = document.body.style.paddingRight
|
|
195
172
|
}
|
|
196
173
|
|
|
197
|
-
/** 暗黑模式初始化 */
|
|
198
|
-
private initDarkMode() {
|
|
199
|
-
if (this.conf.darkMode === 'auto') {
|
|
200
|
-
// 自动切换暗黑模式,事件监听
|
|
201
|
-
const darkModeMedia = window.matchMedia('(prefers-color-scheme: dark)')
|
|
202
|
-
darkModeMedia.addEventListener('change', (e) => { this.setDarkMode(e.matches) })
|
|
203
|
-
this.setDarkMode(darkModeMedia.matches)
|
|
204
|
-
} else {
|
|
205
|
-
this.setDarkMode(this.conf.darkMode || false)
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
174
|
/** 监听事件 */
|
|
210
175
|
public on<K extends keyof EventPayloadMap>(name: K, handler: Handler<EventPayloadMap[K]>) {
|
|
211
176
|
this.ctx.on(name, handler, 'external')
|
package/src/comment/render.ts
CHANGED
|
@@ -58,7 +58,8 @@ export default class CommentRender {
|
|
|
58
58
|
private renderAvatar() {
|
|
59
59
|
const $avatar = this.$el.querySelector<HTMLElement>('.atk-avatar')!
|
|
60
60
|
const $avatarImg = Utils.createElement<HTMLImageElement>('<img />')
|
|
61
|
-
|
|
61
|
+
const avatarURLBuilder = this.ctx.conf.avatarURLBuilder
|
|
62
|
+
$avatarImg.src = avatarURLBuilder ? avatarURLBuilder(this.data) : this.comment.getGravatarURL()
|
|
62
63
|
if (this.data.link) {
|
|
63
64
|
const $avatarA = Utils.createElement<HTMLLinkElement>('<a target="_blank" rel="noreferrer noopener nofollow"></a>')
|
|
64
65
|
$avatarA.href = Utils.isValidURL(this.data.link) ? this.data.link : `https://${this.data.link}`
|