artalk 2.0.4 → 2.1.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 (91) hide show
  1. package/README.md +7 -60
  2. package/dist/Artalk.css +1 -1
  3. package/dist/Artalk.es.js +5996 -1
  4. package/dist/Artalk.iife.js +266 -1
  5. package/dist/Artalk.js +266 -1
  6. package/package.json +3 -4
  7. package/src/api/index.ts +269 -0
  8. package/src/api/request.ts +125 -0
  9. package/src/artalk.ts +153 -0
  10. package/src/components/_component.ts.txt +10 -0
  11. package/src/components/action-btn.ts +169 -0
  12. package/src/components/admin/page-list.ts +194 -0
  13. package/src/components/admin/site-list-floater.ts +98 -0
  14. package/src/components/admin/site-list.ts +289 -0
  15. package/src/components/comment.ts +478 -0
  16. package/src/components/dialog.ts +63 -0
  17. package/src/components/editor-plugs/{_ExamplePlug.ts.txt → _example-plug.ts.txt} +0 -0
  18. package/src/components/editor-plugs/{EditorPlug.ts → editor-plug.ts} +3 -3
  19. package/src/components/editor-plugs/{EmoticonsPlug.less → emoticons-plug.less} +0 -0
  20. package/src/components/editor-plugs/{EmoticonsPlug.ts → emoticons-plug.ts} +9 -9
  21. package/src/components/editor-plugs/{PreviewPlug.less → preview-plug.less} +0 -0
  22. package/src/components/editor-plugs/{PreviewPlug.ts → preview-plug.ts} +10 -10
  23. package/src/components/{Editor.ts → editor.ts} +104 -102
  24. package/src/components/html/comment.html +2 -6
  25. package/src/components/html/list.html +0 -1
  26. package/src/components/html/sidebar.html +18 -4
  27. package/src/components/item-text-editor.ts +136 -0
  28. package/src/components/layer.ts +135 -0
  29. package/src/components/list-lite.ts +507 -0
  30. package/src/components/list.ts +172 -0
  31. package/src/components/pagination.ts +150 -0
  32. package/src/components/read-more-btn.ts +71 -0
  33. package/src/components/sidebar-view.ts +26 -0
  34. package/src/components/sidebar-views/comments-view.ts +66 -0
  35. package/src/components/sidebar-views/pages-view.ts +61 -0
  36. package/src/components/sidebar-views/setting-view.ts +21 -0
  37. package/src/components/sidebar-views/sites-view.ts +42 -0
  38. package/src/components/sidebar-views/transfer-view.ts +218 -0
  39. package/src/components/sidebar.ts +288 -0
  40. package/src/context.ts +42 -0
  41. package/src/default-conf.ts +42 -0
  42. package/src/lib/checker/admin-checker.ts +33 -0
  43. package/src/lib/checker/captcha-checker.ts +38 -0
  44. package/src/lib/checker/index.ts +134 -0
  45. package/src/lib/component.ts +3 -3
  46. package/src/lib/md5.js +6 -0
  47. package/src/lib/ui.ts +64 -74
  48. package/src/lib/user.ts +2 -8
  49. package/src/lib/utils.ts +30 -11
  50. package/src/main.ts +1 -1
  51. package/src/style/_color.less +8 -2
  52. package/src/style/_extend.less +81 -0
  53. package/src/style/_variables.less +3 -0
  54. package/src/style/comment.less +60 -24
  55. package/src/style/editor.less +3 -0
  56. package/src/style/item-text-editor.less +35 -0
  57. package/src/style/list.less +51 -32
  58. package/src/style/main.less +75 -1
  59. package/src/style/page-list.less +50 -0
  60. package/src/style/pagination.less +58 -0
  61. package/src/style/sidebar.less +274 -199
  62. package/src/style/site-list.less +208 -0
  63. package/tsconfig.json +27 -0
  64. package/types/artalk-config.d.ts +68 -51
  65. package/types/artalk-data.d.ts +38 -1
  66. package/types/event.d.ts +73 -0
  67. package/types/index.d.ts +1 -6
  68. package/.github/workflows/npm.yml +0 -31
  69. package/.github/workflows/pages.yml +0 -30
  70. package/dist/src/Event.d.ts +0 -59
  71. package/dist/types/artalk-config.d.ts +0 -79
  72. package/dist/types/artalk-data.d.ts +0 -153
  73. package/dist/types/global.d.ts +0 -1
  74. package/dist/types/index.d.ts +0 -8
  75. package/dist/types/vite-env.d.ts +0 -1
  76. package/example/pjax/res1.html +0 -41
  77. package/example/pjax/res2.html +0 -41
  78. package/src/Artalk.ts +0 -160
  79. package/src/Constant.ts +0 -6
  80. package/src/Context.ts +0 -34
  81. package/src/Event.d.ts +0 -59
  82. package/src/components/Comment.ts +0 -402
  83. package/src/components/Layer.ts +0 -138
  84. package/src/components/List.ts +0 -162
  85. package/src/components/ListLite.ts +0 -407
  86. package/src/components/Sidebar.ts +0 -141
  87. package/src/components/sidebar-views/AdminView.ts +0 -523
  88. package/src/components/sidebar-views/MessageView.ts +0 -48
  89. package/src/components/sidebar-views/SidebarView.ts +0 -24
  90. package/src/lib/api.ts +0 -380
  91. package/src/lib/checker.ts +0 -164
@@ -1,523 +0,0 @@
1
- import Context from '../../Context'
2
- import SidebarView from './SidebarView'
3
- import * as Utils from '../../lib/utils'
4
- import Comment from '../Comment'
5
- import ListLite from '../ListLite'
6
- import { CreateCommentList } from './MessageView'
7
- import Api from '~/src/lib/api'
8
-
9
- export default class AdminView extends SidebarView {
10
- name = 'admin'
11
- title = '控制台'
12
- actions = {
13
- comment: '评论',
14
- page: '页面',
15
- site: '站点',
16
- setting: '配置',
17
- }
18
- activeAction = ''
19
- adminOnly = true
20
-
21
- cListInstance?: ListLite
22
- get cList () {
23
- if (!this.cListInstance) this.cListInstance = CreateCommentList(this.ctx)
24
- return this.cListInstance
25
- }
26
-
27
- constructor (ctx: Context) {
28
- super(ctx)
29
-
30
- this.el = Utils.createElement(`<div class="atk-msg-center"></div>`)
31
- }
32
-
33
- init () {
34
- this.activeAction = 'comment'
35
- this.switch('comment')
36
- }
37
-
38
- switch (action: string) {
39
- this.el.innerHTML = ''
40
-
41
- if (action === 'comment') {
42
- this.initCommentList()
43
- } else if (action === 'page') {
44
- this.initPageList()
45
- } else if (action === 'site') {
46
- this.initSiteList()
47
- } else if (action === 'setting') {
48
- this.initSetting()
49
- }
50
- }
51
-
52
- /** site 筛选 · 初始化 */
53
- async initSiteFilterBar (clickEvt: (item: FilterBarItem) => void) {
54
- const sitesData = await new Api(this.ctx).siteGet()
55
- const siteItems: FilterBarItem[] = [
56
- {name: '__ATK_SITE_ALL', label: '全部站点'}
57
- ]
58
- if (sitesData) sitesData.forEach((site) => {
59
- siteItems.push({ name: site.name, label: site.name })
60
- })
61
-
62
- const filterBarEl = BuildFilterBar(siteItems, (item) => clickEvt(item))
63
- return filterBarEl
64
- }
65
-
66
- /** 评论列表 · 初始化 */
67
- async initCommentList() {
68
- const el = Utils.createElement('<div class="atk-admin-comment-list"></div>')
69
- el.append(this.cList.el) // TODO: 统一 loading 动画
70
-
71
- const reqComments = (type: string, siteName: string) => {
72
- if (!this.cList) return
73
- this.cList.type = `admin_${type}` as any
74
- this.cList.isFirstLoad = true
75
- this.cList.paramsEditor = (params) => {
76
- params.site_name = siteName
77
- }
78
- this.cList.reqComments()
79
- }
80
-
81
- let loaded = false
82
- const curt = { typeName: 'all', siteName: '' }
83
-
84
- // 初始化
85
- const typeFilterBarEl = BuildFilterBar([
86
- { name: 'all', label: '全部' },
87
- { name: 'pending', label: '待审' }
88
- ], (item) => {
89
- if (!loaded) return
90
- curt.typeName = item.name
91
- reqComments(curt.typeName, curt.siteName)
92
- })
93
- el.prepend(typeFilterBarEl)
94
-
95
- // 初始化 site 筛选
96
- const siteFilterBarEl = await this.initSiteFilterBar((item) => {
97
- curt.siteName = item.name
98
- reqComments(curt.typeName, curt.siteName)
99
- })
100
- el.prepend(siteFilterBarEl)
101
- loaded = true
102
-
103
- this.el.innerHTML = ''
104
- this.el.append(el)
105
- }
106
-
107
- /** 页面列表 · 初始化 */
108
- async initPageList () {
109
- const el = Utils.createElement('<div class="atk-admin-page-list"></div>')
110
-
111
- const pListEl = Utils.createElement(`<div class="atk-sidebar-list"></div>`)
112
- el.append(pListEl)
113
-
114
- // 初始化 site 筛选
115
- const siteFilterBarEl = await this.initSiteFilterBar((item) => {
116
- this.reqPages(pListEl, item.name)
117
- })
118
- el.prepend(siteFilterBarEl)
119
-
120
- this.el.innerHTML = ''
121
- this.el.append(el)
122
- }
123
-
124
- async reqPages(pListEl: HTMLElement, siteName?: string) {
125
- pListEl.innerHTML = ''
126
-
127
- const pages = await new Api(this.ctx).pageGet(siteName)
128
- if (!pages) {
129
- pListEl.innerHTML = '<div class="atk-sidebar-no-content">无内容</div>'
130
- return
131
- }
132
-
133
- pages.forEach(page => {
134
- const pageItemEl = Utils.createElement(`
135
- <div class="atk-item">
136
- <div class="atk-title"></div>
137
- <div class="atk-sub"></div>
138
- <div class="atk-actions">
139
- <div class="atk-item" data-action="page-edit-title">修改标题</div>
140
- <div class="atk-item" data-action="page-fetch">获取标题</div>
141
- <div class="atk-item" data-action="page-edit-key">修改 KEY</div>
142
- <div class="atk-item" data-action="page-admin-only"></div>
143
- <div class="atk-item" data-action="page-del">删除</div>
144
- </div>
145
- </div>`)
146
- pListEl.append(pageItemEl)
147
-
148
- pageItemEl.setAttribute('data-page-id', String(page.id))
149
-
150
- const titleEl = pageItemEl.querySelector<HTMLElement>('.atk-title')!
151
- const keyEl = pageItemEl.querySelector<HTMLElement>('.atk-sub')!
152
-
153
- titleEl.innerText = page.title || page.key
154
- keyEl.innerText = page.key
155
- titleEl.onclick = () => {
156
- window.open(`${page.url}`)
157
- }
158
- keyEl.onclick = () => {
159
- window.open(`${page.url}`)
160
- }
161
-
162
- const adminOnlyBtn = pageItemEl.querySelector<HTMLElement>('[data-action="page-admin-only"]')!
163
- const renderAdminOnlyBtn = () => {
164
- adminOnlyBtn.innerText = page.admin_only ? '仅管理员可评' : '所有人可评'
165
- }
166
- renderAdminOnlyBtn()
167
- adminOnlyBtn.onclick = (e) => {
168
- e.stopPropagation() // 防止穿透
169
-
170
- // TODO: loading ui
171
- page.admin_only = !page.admin_only
172
- new Api(this.ctx).pageEdit(page)
173
- .then((p) => {
174
- page = p
175
- renderAdminOnlyBtn()
176
- })
177
- }
178
-
179
- const editTitleBtn = pageItemEl.querySelector<HTMLElement>('[data-action="page-edit-title"]')!
180
- editTitleBtn.onclick = () => {
181
- const val = window.prompt('修改标题:', page.title)
182
- if (val !== null) {
183
- page.title = val
184
- new Api(this.ctx).pageEdit(page)
185
- .then(() => {
186
- this.reqPages(pListEl, siteName)
187
- })
188
- .catch((err) => {
189
- window.alert(`修改失败:${err.msg || '未知错误'}`)
190
- })
191
- }
192
- }
193
-
194
- const fetchBtn = pageItemEl.querySelector<HTMLElement>('[data-action="page-fetch"]')!
195
- fetchBtn.onclick = () => {
196
- const btnOrgTxt = fetchBtn.innerText
197
- fetchBtn.innerText = '获取中...'
198
- new Api(this.ctx).pageFetch(page.id)
199
- .then((p) => {
200
- page = p
201
- pageItemEl.querySelector<HTMLElement>('.atk-title')!.innerText = p.title
202
- })
203
- .catch((err) => {
204
- window.alert(`获取失败:${err.msg || '未知错误'}`)
205
- })
206
- .finally(() => {
207
- fetchBtn.innerText = btnOrgTxt
208
- })
209
- }
210
-
211
- const editKeyBtn = pageItemEl.querySelector<HTMLElement>('[data-action="page-edit-key"]')!
212
- editKeyBtn.onclick = () => {
213
- const val = window.prompt('修改 Key:', page.key)
214
- if (val !== null) {
215
- page.key = val
216
- new Api(this.ctx).pageEdit(page)
217
- .then(() => {
218
- this.reqPages(pListEl, siteName)
219
- })
220
- .catch((err) => {
221
- window.alert(`修改失败:${err.msg || '未知错误'}`)
222
- })
223
- }
224
- }
225
-
226
- const delBtn = pageItemEl.querySelector<HTMLElement>('[data-action="page-del"]')!
227
- delBtn.onclick = () => {
228
- const del = () => {
229
- new Api(this.ctx).pageDel(page.key, page.site_name)
230
- .then(success => {
231
- if (success) pageItemEl.remove()
232
- })
233
- }
234
- if (window.confirm(`确认删除页面 "${page.title || page.key}"?将会删除所有相关数据`)) del()
235
- }
236
- })
237
- }
238
-
239
- /** 站点列表 · 初始化 */
240
- async initSiteList () {
241
- const el = Utils.createElement('<div class="atk-site-list"></div>')
242
- // TODO: 可复用,特别是 actions
243
- const sListEl = Utils.createElement(`
244
- <div class="atk-sidebar-list">
245
- <div class="atk-site-add atk-form-inline-wrap">
246
- <input type="text" name="siteAdd_name" placeholder="Name..." />
247
- <input type="text" name="siteAdd_urls" placeholder="URL..." />
248
- <button name="siteAdd_submit">Add</button>
249
- </div>
250
- </div>
251
- `)
252
- el.append(sListEl)
253
- const siteAddNameEl = sListEl.querySelector<HTMLInputElement>('[name="siteAdd_name"]')!
254
- const siteAddUrlsEl = sListEl.querySelector<HTMLInputElement>('[name="siteAdd_urls"]')!
255
- sListEl.querySelector<HTMLElement>('[name="siteAdd_submit"]')!.onclick = () => {
256
- const name = siteAddNameEl.value.trim()
257
- const urls = siteAddUrlsEl.value.trim()
258
- if (name === '') {
259
- siteAddNameEl.focus()
260
- return
261
- }
262
- new Api(this.ctx).siteAdd(name, urls)
263
- .then(() => {
264
- this.initSiteList()
265
- })
266
- .catch((err) => {
267
- window.alert(`创建失败:${err.msg || ''}`)
268
- })
269
- }
270
- const sites = await new Api(this.ctx).siteGet()
271
- if (!sites) {
272
- el.append(Utils.createElement('<div class="atk-sidebar-no-content">无内容</div>'))
273
- return
274
- }
275
- sites.forEach(site => {
276
- const siteItemEl = Utils.createElement(`
277
- <div class="atk-item">
278
- <div class="atk-title"></div>
279
- <div class="atk-sub"></div>
280
- <div class="atk-actions">
281
- <div class="atk-item" data-action="site-rename">重命名</div>
282
- <div class="atk-item" data-action="site-edit-urls">修改 URL</div>
283
- <div class="atk-item" data-action="site-del">删除</div>
284
- </div>
285
- </div>`)
286
- sListEl.append(siteItemEl)
287
-
288
- siteItemEl.setAttribute('data-site-id', String(site.id))
289
-
290
- const nameEl = siteItemEl.querySelector<HTMLElement>('.atk-title')!
291
- const urlsEl = siteItemEl.querySelector<HTMLElement>('.atk-sub')!
292
-
293
- nameEl.innerText = site.name || site.first_url
294
- nameEl.onclick = () => {
295
- window.open(`${site.first_url}`)
296
- }
297
-
298
- if (site.urls) {
299
- site.urls.forEach((u) => {
300
- const urlItemEl = Utils.createElement('<span style="margin-right: 10px;" />')
301
- urlItemEl.innerText = u
302
- urlItemEl.onclick = () => {
303
- window.open(`${u}`)
304
- }
305
- urlsEl.append(urlItemEl)
306
- })
307
- }
308
-
309
- const delBtn = siteItemEl.querySelector<HTMLElement>('[data-action="site-del"]')!
310
- delBtn.onclick = () => {
311
- const del = () => {
312
- new Api(this.ctx).siteDel(site.id, true)
313
- .then(success => {
314
- if (success) siteItemEl.remove()
315
- })
316
- }
317
- if (window.confirm(`确认删除站点 "${site.name || site.first_url}"?将会删除所有相关数据`))
318
- del()
319
- }
320
-
321
- const editUrlsBtn = siteItemEl.querySelector<HTMLElement>('[data-action="site-edit-urls"]')!
322
- editUrlsBtn.onclick = () => {
323
- const val = window.prompt('修改 URL (多个 URL 用逗号隔开):', site.urls_raw)
324
- if (val !== null) {
325
- new Api(this.ctx).siteEdit(site.id, { name: site.name, urls: val })
326
- .then(() => {
327
- this.initSiteList()
328
- })
329
- .catch((err) => {
330
- window.alert(`修改失败:${err.msg || '未知错误'}`)
331
- })
332
- }
333
- }
334
-
335
- const renameBtn = siteItemEl.querySelector<HTMLElement>('[data-action="site-rename"]')!
336
- renameBtn.onclick = () => {
337
- const val = window.prompt('编辑站点名称:', site.name)
338
- if (val !== null) {
339
- new Api(this.ctx).siteEdit(site.id, { name: val, urls: site.urls_raw })
340
- .then(() => {
341
- this.initSiteList()
342
- })
343
- .catch((err) => {
344
- window.alert(`修改失败:${err.msg || '未知错误'}`)
345
- })
346
- }
347
- }
348
- })
349
-
350
- this.el.innerHTML = ''
351
- this.el.append(el)
352
- }
353
-
354
- /** 配置页面 · 初始化 */
355
- initSetting () {
356
- const el = Utils.createElement('<div class="atk-admin-setting"></div>')
357
-
358
- const settingEl = Utils.createElement(`
359
- <div class="atk-setting">
360
- <div class="atk-log-wrap" style="display: none;">
361
- <div class="atk-log-back-btn">返回</div>
362
- <div class="atk-log"></div>
363
- </div>
364
- <div class="atk-group atk-importer atk-form-wrap">
365
- <div class="atk-title">导入评论数据</div>
366
- <div class="atk-label">数据类型</div>
367
- <select name="importer_dataType">
368
- <option value="artrans">Artrans (数据行囊)</option>
369
- <option value="artalk_v1">Artalk v1 (PHP 旧版)</option>
370
- <option value="typecho">Typecho</option>
371
- <option value="wordpress">WordPress</option>
372
- <option value="disqus">Disqus</option>
373
- <option value="commento">Commento</option>
374
- <option value="valine">Valine</option>
375
- <option value="twikoo">Twikoo</option>
376
- </select>
377
- <input type="file" name="importer_dataFile" accept="text/plain,.json" />
378
- <div class="atk-label">目标站点名</div>
379
- <input type="text" name="importer_siteName" />
380
- <div class="atk-label">目标站点 URL</div>
381
- <input type="text" name="importer_siteUrl" />
382
- <div class="atk-label">启动参数</div>
383
- <textarea name="importer_payload"></textarea>
384
- <span class="atk-desc">启动参数请查阅:“<a href="https://artalk.js.org/guide/transfer.html" target="_blank">文档 · 数据搬家</a>”</span>
385
- <button class="atk-btn" name="importer_submit">导入</button>
386
- </div>
387
- </div>`)
388
- el.append(settingEl)
389
-
390
- const formWrapEl = settingEl.querySelector<HTMLElement>('.atk-form-wrap')!
391
- const logWrapEl = settingEl.querySelector<HTMLElement>('.atk-log-wrap')!
392
- const logEl = logWrapEl.querySelector<HTMLElement>('.atk-log')!
393
- const logBackBtnEl = logWrapEl?.querySelector<HTMLElement>('.atk-log-back-btn')!
394
- logBackBtnEl.onclick = () => {
395
- logWrapEl.style.display = 'none'
396
- formWrapEl.style.display = ''
397
- }
398
-
399
- const impDataTypeEl = settingEl.querySelector<HTMLSelectElement>('[name="importer_dataType"]')!
400
- const impDataFileEl = settingEl.querySelector<HTMLInputElement>('[name="importer_dataFile"]')!
401
- const impSiteNameEl = settingEl.querySelector<HTMLInputElement>('[name="importer_siteName"]')!
402
- const impSiteUrlEl = settingEl.querySelector<HTMLInputElement>('[name="importer_siteUrl"]')!
403
- const impPayloadEl = settingEl.querySelector<HTMLInputElement>('[name="importer_payload"]')!
404
-
405
- impDataTypeEl.onchange = () => {
406
- if (['typecho'].includes(impDataTypeEl.value)) {
407
- impDataFileEl.style.display = 'none'
408
- } else {
409
- impDataFileEl.style.display = ''
410
- }
411
- }
412
-
413
- const impSubmitEl = settingEl.querySelector<HTMLButtonElement>('[name="importer_submit"]')!
414
- impSubmitEl.onclick = () => {
415
- const dataType = impDataTypeEl.value.trim()
416
- const siteName = impSiteNameEl.value.trim()
417
- const siteUrl = impSiteUrlEl.value.trim()
418
- const payload = impPayloadEl.value.trim()
419
-
420
- if (dataType === '') {
421
- window.alert('请选择数据类型')
422
- return
423
- }
424
-
425
- const createImportSession = (data?: string) => {
426
- logWrapEl.style.display = ''
427
- formWrapEl.style.display = 'none'
428
-
429
- // 创建 iframe
430
- const frameName = `f_${+new Date()}`
431
- const frame = document.createElement('iframe')
432
- frame.className = 'atk-iframe'
433
- frame.name = frameName
434
- logEl.innerHTML = ''
435
- logEl.append(frame)
436
-
437
- const formEl = document.createElement('form')
438
- formEl.style.display = 'none'
439
- formEl.setAttribute('method', 'post')
440
- formEl.setAttribute('action', `${this.ctx.conf.server}/admin/artransfer`)
441
- formEl.setAttribute("target", frameName)
442
-
443
- let pJSON: any = {}
444
- if (payload) {
445
- // JSON 格式检验
446
- try {
447
- pJSON = JSON.parse(payload)
448
- } catch (err) {
449
- window.alert(`Payload JSON 格式有误:${String(err)}`)
450
- }
451
-
452
- if (pJSON !instanceof Object) {
453
- window.alert(`Payload 需为 JSON 对象`)
454
- }
455
- }
456
-
457
- if (siteName) pJSON.t_name = siteName
458
- if (siteUrl) pJSON.t_url = siteUrl
459
- if (data) pJSON.json_data = data
460
-
461
- const formParams: {[k: string]: string} = {
462
- type: dataType,
463
- payload: JSON.stringify(pJSON),
464
- token: this.ctx.user.data.token || '',
465
- }
466
-
467
- Object.entries(formParams).forEach(([key, val]) => {
468
- const hiddenField = document.createElement('input')
469
- hiddenField.setAttribute('type', 'hidden')
470
- hiddenField.setAttribute('name', key)
471
- hiddenField.value = val
472
- formEl.appendChild(hiddenField)
473
- })
474
-
475
- logWrapEl.append(formEl)
476
- formEl.submit()
477
- formEl.remove()
478
- }
479
-
480
- const reader = new FileReader()
481
- reader.onload = () => {
482
- const data = String(reader.result)
483
- createImportSession(data)
484
- }
485
- if (impDataFileEl.files?.length) {
486
- reader.readAsText(impDataFileEl.files[0])
487
- return
488
- }
489
-
490
- createImportSession()
491
- }
492
-
493
- this.el.innerHTML = ''
494
- this.el.append(el)
495
- }
496
- }
497
-
498
- /** 通用筛选条 */
499
- function BuildFilterBar (items: FilterBarItem[], clickEvt: (item: FilterBarItem) => void): HTMLElement {
500
- const filterBarEl = Utils.createElement(`<div class="atk-filter-bar"></div>`)
501
-
502
- items.forEach(item => {
503
- const itemEl = Utils.createElement(`<span></span>`)
504
- filterBarEl.append(itemEl)
505
- itemEl.innerText = item.label
506
- itemEl.addEventListener('click', () => {
507
- clickEvt(item)
508
- filterBarEl.querySelectorAll('.atk-active')
509
- .forEach(el => el.classList.remove('atk-active')) // 删除其他 active
510
- itemEl.classList.add('atk-active')
511
- })
512
- })
513
-
514
- ;(filterBarEl.firstChild as HTMLElement).click() // 默认打开第一个项目
515
-
516
- return filterBarEl
517
- }
518
-
519
- /** 筛选条中的项目 */
520
- interface FilterBarItem {
521
- name: string
522
- label: string
523
- }
@@ -1,48 +0,0 @@
1
- import Context from '../../Context'
2
- import SidebarView from './SidebarView'
3
- import * as Utils from '../../lib/utils'
4
- import ListLite from '../ListLite'
5
-
6
- export default class MessageView extends SidebarView {
7
- name = 'message'
8
- title = '通知中心'
9
- actions = {
10
- mentions: '提及',
11
- all: '全部',
12
- mine: '我的',
13
- pending: '待审',
14
- }
15
- activeAction = ''
16
-
17
- list?: ListLite
18
- type: string = 'mentions'
19
-
20
- init () {
21
- this.list = CreateCommentList(this.ctx)
22
- this.el.innerHTML = ''
23
- this.el.append(this.list.el)
24
-
25
- this.activeAction = this.type
26
- this.switch(this.type)
27
- }
28
-
29
- switch (action: string) {
30
- if (!this.list) return
31
- this.type = action
32
- this.list.type = action as any;
33
- this.list.isFirstLoad = true
34
- this.list.reqComments()
35
- }
36
- }
37
-
38
- export function CreateCommentList (ctx: Context) {
39
- const list = new ListLite(ctx)
40
- list.flatMode = true
41
- list.unreadHighlight = true
42
- list.noCommentText = '<div class="atk-sidebar-no-content">无内容</div>'
43
- list.renderComment = (comment) => {
44
- comment.setOpenURL(`${comment.data.page_key}#atk-comment-${comment.data.id}`)
45
- }
46
-
47
- return list
48
- }
@@ -1,24 +0,0 @@
1
- import Context from '../../Context'
2
- import Component from '../../lib/component'
3
- import * as Utils from '../../lib/utils'
4
- import Comment from '../Comment'
5
-
6
- export default class SidebarView extends Component {
7
- constructor (ctx: Context) {
8
- super(ctx)
9
-
10
- this.el = Utils.createElement(`<div class="atk-sidebar-view"></div>`)
11
- }
12
-
13
- name = ''
14
- title = this.name
15
- actions: { [name: string]: string } = {}
16
- activeAction?: string
17
-
18
- adminOnly = false
19
-
20
- init (): void {}
21
-
22
- switch (action: string): void {}
23
- }
24
-