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
@@ -0,0 +1,172 @@
1
+ import '../style/list.less'
2
+
3
+ import Context from '../context'
4
+ import * as Utils from '../lib/utils'
5
+ import * as Ui from '../lib/ui'
6
+ import Api from '../api'
7
+ import ListHTML from './html/list.html?raw'
8
+ import ListLite from './list-lite'
9
+ import { ListData } from '~/types/artalk-data'
10
+
11
+ export default class List extends ListLite {
12
+ private $closeCommentBtn!: HTMLElement
13
+ private $openSidebarBtn!: HTMLElement
14
+ private $unreadBadge!: HTMLElement
15
+
16
+ constructor (ctx: Context) {
17
+ const el = Utils.createElement(ListHTML)
18
+
19
+ super(ctx, el)
20
+
21
+ // 把 listLite $el 变为子元素
22
+ el.querySelector('.atk-list-body')!.append(this.$el)
23
+ this.$el = el
24
+
25
+ // 平铺模式
26
+ let flatMode = false
27
+ if (this.ctx.conf.flatMode === 'auto') {
28
+ if (window.matchMedia("(max-width: 768px)").matches)
29
+ flatMode = true
30
+ } else if (this.ctx.conf.flatMode === true) {
31
+ flatMode = true
32
+ }
33
+ this.flatMode = flatMode
34
+
35
+ // 分页模式
36
+ this.pageMode = this.conf.pagination?.readMore ? 'read-more' : 'pagination'
37
+
38
+ // 操作按钮
39
+ this.initListActionBtn()
40
+
41
+ // copyright
42
+ this.$el.querySelector<HTMLElement>('.atk-copyright')!.innerHTML = `Powered By <a href="https://artalk.js.org" target="_blank" title="Artalk v${ARTALK_VERSION}">Artalk</a>`
43
+
44
+ // event listen
45
+ this.ctx.on('list-reload', () => (this.reqComments(0))) // 刷新评论
46
+ this.ctx.on('list-refresh-ui', () => (this.refreshUI()))
47
+ this.ctx.on('list-import', (data) => (this.importComments(data)))
48
+ this.ctx.on('list-insert', (data) => (this.insertComment(data)))
49
+ this.ctx.on('list-delete', (comment) => (this.deleteComment(comment.id)))
50
+ this.ctx.on('list-update', (updateData) => { updateData(this.data);this.refreshUI() } )
51
+ this.ctx.on('unread-update', (data) => (this.showUnreadBadge(data.notifies?.length || 0)))
52
+ }
53
+
54
+ private initListActionBtn () {
55
+ // 侧边栏呼出按钮
56
+ this.$openSidebarBtn = this.$el.querySelector('[data-action="open-sidebar"]')!
57
+ this.$closeCommentBtn = this.$el.querySelector('[data-action="admin-close-comment"]')!
58
+ this.$unreadBadge = this.$el.querySelector('.atk-unread-badge')!
59
+
60
+ this.$openSidebarBtn.addEventListener('click', () => {
61
+ this.ctx.trigger('sidebar-show')
62
+ })
63
+
64
+ // 关闭评论按钮
65
+ this.$closeCommentBtn.addEventListener('click', () => {
66
+ if (!this.data) return
67
+
68
+ this.data.page.admin_only = !this.data.page.admin_only
69
+ this.adminPageEditSave()
70
+ })
71
+ }
72
+
73
+ /** 刷新界面 */
74
+ public refreshUI () {
75
+ super.refreshUI()
76
+
77
+ this.$el.querySelector<HTMLElement>('.atk-comment-count-num')!.innerText = String(this.commentsCount)
78
+
79
+ // 已输入个人信息
80
+ if (!!this.ctx.user.data.nick && !!this.ctx.user.data.email) {
81
+ this.$openSidebarBtn.classList.remove('atk-hide')
82
+ } else {
83
+ this.$openSidebarBtn.classList.add('atk-hide')
84
+ }
85
+
86
+ // 仅管理员显示控制
87
+ this.ctx.trigger('check-admin-show-el')
88
+ this.$openSidebarBtn.innerText = (!this.ctx.user.data.isAdmin) ? '通知中心' : '控制中心'
89
+
90
+ // 关闭评论
91
+ if (!!this.data && !!this.data.page && this.data.page.admin_only === true) {
92
+ this.ctx.trigger('editor-close')
93
+ this.$closeCommentBtn.innerHTML = '打开评论'
94
+ } else {
95
+ this.ctx.trigger('editor-open')
96
+ this.$closeCommentBtn.innerHTML = '关闭评论'
97
+ }
98
+ }
99
+
100
+ public onLoad(data: ListData, offset: number) {
101
+ super.onLoad(data, offset)
102
+
103
+ // 检测锚点跳转
104
+ this.checkGoToCommentByUrlHash()
105
+ }
106
+
107
+ /** 跳到评论项位置 - 根据 `location.hash` */
108
+ public async checkGoToCommentByUrlHash () {
109
+ let commentId = Number(Utils.getQueryParam('atk_comment')) // same as backend GetReplyLink()
110
+ if (!commentId) {
111
+ const match = window.location.hash.match(/#atk-comment-([0-9]+)/)
112
+ if (!match || !match[1] || Number.isNaN(Number(match[1]))) return
113
+ commentId = Number(match[1])
114
+ }
115
+ if (!commentId) return
116
+
117
+ // 已阅 API
118
+ const notifyKey = Utils.getQueryParam('atk_notify_key')
119
+ if (notifyKey) {
120
+ new Api(this.ctx).markRead(notifyKey)
121
+ .then(() => {
122
+ this.unread = this.unread.filter(o => o.comment_id !== commentId)
123
+ this.ctx.trigger('unread-update', {
124
+ notifies: this.unread
125
+ })
126
+ })
127
+ }
128
+
129
+ const comment = this.findComment(commentId)
130
+ // TODO 若找不到评论的情况
131
+ // if (!comment) { // 若找不到评论
132
+ // if (this.hasMoreComments) { // 阅读更多,并重试
133
+ // await this.reqComments()
134
+ // }
135
+ // }
136
+
137
+ if (!comment) return
138
+
139
+ Ui.scrollIntoView(comment.getEl(), false)
140
+ window.setTimeout(() => {
141
+ comment.getEl().classList.add('atk-flash-once')
142
+ }, 800)
143
+ }
144
+
145
+ /** 管理员设置页面信息 */
146
+ public adminPageEditSave () {
147
+ if (!this.data || !this.data.page) return
148
+
149
+ this.ctx.trigger('editor-show-loading')
150
+ new Api(this.ctx).pageEdit(this.data.page)
151
+ .then((page) => {
152
+ if (this.data)
153
+ this.data.page = { ...page }
154
+ this.refreshUI()
155
+ })
156
+ .catch(err => {
157
+ this.ctx.trigger('editor-notify', { msg: `修改页面数据失败:${err.msg || String(err)}`, type: 'e'})
158
+ })
159
+ .finally(() => {
160
+ this.ctx.trigger('editor-hide-loading')
161
+ })
162
+ }
163
+
164
+ public showUnreadBadge (count: number) {
165
+ if (count > 0) {
166
+ this.$unreadBadge.innerText = `${Number(count || 0)}`
167
+ this.$unreadBadge.style.display = 'block'
168
+ } else {
169
+ this.$unreadBadge.style.display = 'none'
170
+ }
171
+ }
172
+ }
@@ -0,0 +1,150 @@
1
+ import '../style/pagination.less'
2
+ import * as Utils from '../lib/utils'
3
+ import * as Ui from '../lib/ui'
4
+
5
+ export interface PaginationConf {
6
+ /** 每页条数 */
7
+ pageSize?: number
8
+
9
+ /** 回调函数 */
10
+ onChange: (offset: number) => void
11
+ }
12
+
13
+ export default class Pagination {
14
+ private conf: PaginationConf
15
+ public total: number
16
+ public $el: HTMLElement
17
+ public $input: HTMLInputElement
18
+ public inputTimer?: number
19
+ public $prevBtn: HTMLElement
20
+ public $nextBtn: HTMLElement
21
+
22
+ public page: number = 1
23
+ get pageSize(): number {
24
+ return this.conf.pageSize || 15
25
+ }
26
+ get offset(): number {
27
+ return this.pageSize * (this.page - 1)
28
+ }
29
+ get maxPage(): number {
30
+ return Math.ceil(this.total / this.pageSize)
31
+ }
32
+
33
+ public constructor(total: number, conf: PaginationConf) {
34
+ this.total = total
35
+ this.conf = conf
36
+
37
+ this.$el = Utils.createElement(
38
+ `<div class="atk-pagination-wrap">
39
+ <div class="atk-pagination">
40
+ <div class="atk-btn atk-btn-prev">Prev</div>
41
+ <input type="text" class="atk-input" />
42
+ <div class="atk-btn atk-btn-next">Next</div>
43
+ </div>
44
+ </div>`)
45
+ this.$input = this.$el.querySelector('.atk-input')!
46
+ this.$input.value = `${this.page}`
47
+
48
+ this.$input.oninput = () => this.input()
49
+ this.$input.onkeydown = (e) => this.keydown(e)
50
+
51
+ this.$prevBtn = this.$el.querySelector('.atk-btn-prev')!
52
+ this.$nextBtn = this.$el.querySelector('.atk-btn-next')!
53
+
54
+ this.$prevBtn.onclick = () => this.prev()
55
+ this.$nextBtn.onclick = () => this.next()
56
+
57
+ this.checkDisabled()
58
+ }
59
+
60
+ public update(offset: number, total: number) {
61
+ this.page = Math.ceil(offset / this.pageSize) + 1
62
+ this.total = total
63
+
64
+ this.setInput(this.page)
65
+ this.checkDisabled()
66
+ }
67
+
68
+ public setInput(page: number) {
69
+ this.$input.value = `${page}`
70
+ }
71
+
72
+ public input(now: boolean = false) {
73
+ window.clearTimeout(this.inputTimer)
74
+
75
+ const value = this.$input.value.trim()
76
+
77
+ const modify = () => {
78
+ if (value === '') { this.setInput(this.page);return }
79
+ let page = Number(value)
80
+ if (Number.isNaN(page)) { this.setInput(this.page);return }
81
+ if (page < 1) { this.setInput(this.page);return }
82
+ if (page > this.maxPage) { this.setInput(this.maxPage);page = this.maxPage }
83
+ this.change(page)
84
+ }
85
+
86
+ // 延迟 800ms 执行
87
+ if (!now) this.inputTimer = window.setTimeout(() => modify(), 800)
88
+ else modify()
89
+ }
90
+
91
+ public prev() {
92
+ const page = this.page - 1
93
+ if (page < 1) { return }
94
+ this.change(page)
95
+ }
96
+
97
+ public next() {
98
+ const page = this.page + 1
99
+ if (page > this.maxPage) { return }
100
+ this.change(page)
101
+ }
102
+
103
+ public change(page: number) {
104
+ this.page = page
105
+ this.conf.onChange(this.offset)
106
+ this.setInput(page)
107
+ this.checkDisabled()
108
+ }
109
+
110
+ public checkDisabled() {
111
+ if (this.page + 1 > this.maxPage) {
112
+ this.$nextBtn.classList.add('atk-disabled')
113
+ } else {
114
+ this.$nextBtn.classList.remove('atk-disabled')
115
+ }
116
+
117
+ if (this.page - 1 < 1) {
118
+ this.$prevBtn.classList.add('atk-disabled')
119
+ } else {
120
+ this.$prevBtn.classList.remove('atk-disabled')
121
+ }
122
+ }
123
+
124
+ public keydown(e: KeyboardEvent) {
125
+ const keyCode = e.keyCode || e.which
126
+
127
+ if (keyCode === 38) {
128
+ // 上键
129
+ const page = Number(this.$input.value) + 1
130
+ if (page > this.maxPage) { return }
131
+ this.setInput(page);
132
+ this.input()
133
+ } else if (keyCode === 40) {
134
+ // 下键
135
+ const page = Number(this.$input.value) - 1
136
+ if (page < 1) { return }
137
+ this.setInput(page)
138
+ this.input()
139
+ } else if (keyCode === 13) {
140
+ // 回车
141
+ this.input(true)
142
+ }
143
+ }
144
+
145
+ /** 加载 */
146
+ setLoading (isLoading: boolean) {
147
+ if (isLoading) Ui.showLoading(this.$el)
148
+ else Ui.hideLoading(this.$el)
149
+ }
150
+ }
@@ -0,0 +1,71 @@
1
+ import * as Utils from '../lib/utils'
2
+
3
+ export interface ReadMoreBtnConf {
4
+ /** 每页条数 */
5
+ pageSize?: number
6
+
7
+ /** 数据总数 */
8
+ total: number
9
+
10
+ /** 回调函数 */
11
+ onClick: () => void
12
+ }
13
+
14
+ /**
15
+ * 阅读更多按钮
16
+ */
17
+ export default class ReadMoreBtn {
18
+ public conf: ReadMoreBtnConf
19
+ public $el: HTMLElement
20
+ private $loading: HTMLElement
21
+ private $text: HTMLElement
22
+
23
+ public constructor(conf: ReadMoreBtnConf) {
24
+ this.conf = conf
25
+
26
+ this.$el = Utils.createElement(
27
+ `<div class="atk-list-read-more" style="display: none;">
28
+ <div class="atk-list-read-more-inner">
29
+ <div class="atk-loading-icon" style="display: none;"></div>
30
+ <span class="atk-text">查看更多</span>
31
+ </div>
32
+ </div>`)
33
+
34
+ this.$loading = this.$el.querySelector<HTMLElement>('.atk-loading-icon')!
35
+ this.$text = this.$el.querySelector<HTMLElement>('.atk-text')!
36
+
37
+ this.$el.onclick = () => this.click()
38
+ }
39
+
40
+ click() {
41
+ this.conf.onClick()
42
+ }
43
+
44
+ /** 显示 */
45
+ show() {
46
+ this.$el.style.display = ''
47
+ }
48
+
49
+ /** 隐藏 */
50
+ hide() {
51
+ this.$el.style.display = 'none'
52
+ }
53
+
54
+ /** 加载 */
55
+ setLoading (isLoading: boolean) {
56
+ this.$loading.style.display = isLoading ? '' : 'none'
57
+ this.$text.style.display = isLoading ? 'none' : ''
58
+ }
59
+
60
+ /** 错误提示 */
61
+ showErr(errMsg: string) {
62
+ this.setLoading(false)
63
+
64
+ this.$text.innerText = errMsg
65
+ this.$el.classList.add('atk-err')
66
+ window.setTimeout(() => {
67
+ this.$text.innerText = '查看更多'
68
+ this.$el.classList.remove('atk-err')
69
+ }, 2000) // 2s后错误提示复原
70
+ }
71
+ }
@@ -0,0 +1,26 @@
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
+ static viewName = ''
8
+ static viewTitle = ''
9
+ static viewAdminOnly = false
10
+
11
+ viewTabs = {}
12
+ viewActiveTab = ''
13
+
14
+ protected $parent: HTMLElement
15
+
16
+ constructor(ctx: Context, $parent: HTMLElement) {
17
+ super(ctx)
18
+
19
+ this.$parent = $parent
20
+ this.$el = Utils.createElement(`<div class="atk-sidebar-view"></div>`)
21
+ }
22
+
23
+ mount(siteName: string) {}
24
+
25
+ switchTab(tab: string, siteName: string): boolean|void {}
26
+ }
@@ -0,0 +1,66 @@
1
+ import Context from '../../context'
2
+ import SidebarView from '../sidebar-view'
3
+ import * as Utils from '../../lib/utils'
4
+ import ListLite from '../list-lite'
5
+
6
+ export default class MessageView extends SidebarView {
7
+ static viewName = 'comments'
8
+ static viewTitle = '评论'
9
+
10
+ viewTabs: any = {}
11
+ viewActiveTab = ''
12
+
13
+ list!: ListLite
14
+
15
+ mount(siteName: string) {
16
+ // tabs
17
+ if (this.ctx.user.data.isAdmin) {
18
+ this.viewTabs = {
19
+ admin_all: '全部',
20
+ admin_pending: '待审',
21
+ all: '个人',
22
+ }
23
+ this.viewActiveTab = 'admin_all'
24
+ } else {
25
+ this.viewTabs = {
26
+ mentions: '提及',
27
+ all: '全部',
28
+ mine: '我的',
29
+ pending: '待审',
30
+ }
31
+ this.viewActiveTab = 'mentions'
32
+ }
33
+
34
+ this.list = new ListLite(this.ctx, this.$el)
35
+ this.list.flatMode = true
36
+ this.list.unreadHighlight = true
37
+ this.list.autoLoadListenerAt = this.$parent
38
+ this.list.pageMode = 'pagination'
39
+ this.list.noCommentText = '<div class="atk-sidebar-no-content">无内容</div>'
40
+ this.list.renderComment = (comment) => {
41
+ comment.setOpenURL(`${comment.data.page_key}#atk-comment-${comment.data.id}`)
42
+ }
43
+ this.list.paramsEditor = (params) => {
44
+ params.site_name = siteName
45
+ }
46
+
47
+ this.$el.innerHTML = ''
48
+ this.$el.append(this.list.$el)
49
+
50
+ this.switchTab(this.viewActiveTab, siteName)
51
+ }
52
+
53
+ switchTab(tab: string, siteName: string): boolean|void {
54
+ //console.log(tab, siteName)
55
+ this.viewActiveTab = tab
56
+ this.list.type = tab as any;
57
+ this.list.isFirstLoad = true
58
+ this.list.paramsEditor = (params) => {
59
+ params.site_name = siteName
60
+ }
61
+ this.list.reqComments()
62
+
63
+ return true
64
+ }
65
+ }
66
+
@@ -0,0 +1,61 @@
1
+ import Api from '~/src/api'
2
+ import Context from '../../context'
3
+ import Component from '../../lib/component'
4
+ import * as Utils from '../../lib/utils'
5
+ import * as Ui from '../../lib/ui'
6
+ import PageList from '../admin/page-list'
7
+ import Comment from '../comment'
8
+ import Pagination, { PaginationConf } from '../pagination'
9
+ import SidebarView from '../sidebar-view'
10
+
11
+ const PAGE_SIZE = 20
12
+
13
+ export default class PagesView extends SidebarView {
14
+ static viewName = 'pages'
15
+ static viewTitle = '页面'
16
+ static viewAdminOnly = true
17
+
18
+ viewTabs = {}
19
+ viewActiveTab = ''
20
+
21
+ pageList!: PageList
22
+ pagination!: Pagination
23
+
24
+ mount(siteName: string) {
25
+ if (!this.pageList) {
26
+ this.pageList = new PageList(this.ctx)
27
+ this.$el.append(this.pageList.$el)
28
+ }
29
+
30
+ this.switchTab(this.viewActiveTab, siteName)
31
+ }
32
+
33
+ switchTab(tab: string, siteName: string): boolean|void {
34
+ this.reqPages(siteName, 0)
35
+ }
36
+
37
+ async reqPages(siteName: string, offset: number) {
38
+ this.pageList.clearAll()
39
+ ;(this.$el.parentNode as any)?.scrollTo(0, 0)
40
+
41
+ Ui.showLoading(this.$el)
42
+
43
+ const data = await new Api(this.ctx).pageGet(siteName, offset, PAGE_SIZE)
44
+ this.pageList.importPages(data.pages || [])
45
+
46
+ Ui.hideLoading(this.$el)
47
+
48
+ if (!this.pagination) {
49
+ this.pagination = new Pagination(data.total, {
50
+ pageSize: PAGE_SIZE,
51
+ onChange: (o: number) => {
52
+ this.reqPages(siteName, o)
53
+ }
54
+ })
55
+
56
+ this.$el.append(this.pagination.$el)
57
+ }
58
+ if (this.pagination && offset === 0)
59
+ this.pagination.update(offset, data.total)
60
+ }
61
+ }
@@ -0,0 +1,21 @@
1
+ import Api from '~/src/api'
2
+ import Context from '../../context'
3
+ import Component from '../../lib/component'
4
+ import * as Utils from '../../lib/utils'
5
+ import SiteList from '../admin/site-list'
6
+ import Comment from '../comment'
7
+ import SidebarView from '../sidebar-view'
8
+
9
+ export default class SettingView extends SidebarView {
10
+ static viewName = 'setting'
11
+ static viewTitle = '配置'
12
+ static viewAdminOnly = true
13
+
14
+ viewTabs = {}
15
+ viewActiveTab = ''
16
+
17
+ mount(siteName: string) {
18
+ }
19
+
20
+ switchTab(tab: string, siteName: string) {}
21
+ }
@@ -0,0 +1,42 @@
1
+ import Api from '~/src/api'
2
+ import Context from '../../context'
3
+ import Component from '../../lib/component'
4
+ import * as Utils from '../../lib/utils'
5
+ import SiteList from '../admin/site-list'
6
+ import Comment from '../comment'
7
+ import SidebarView from '../sidebar-view'
8
+
9
+ export default class SitesView extends SidebarView {
10
+ static viewName = 'sites'
11
+ static viewTitle = '站点'
12
+ static viewAdminOnly = true
13
+
14
+ viewTabs = {}
15
+ viewActiveTab = ''
16
+
17
+ siteList!: SiteList
18
+
19
+ constructor(ctx: Context) {
20
+ super(ctx)
21
+
22
+ this.$el = Utils.createElement(`<div class="atk-sidebar-view"></div>`)
23
+ }
24
+
25
+ mount(siteName: string) {
26
+ if (!this.siteList) {
27
+ this.siteList = new SiteList(this.ctx)
28
+ this.$el.append(this.siteList.$el)
29
+ }
30
+
31
+ this.reqSites()
32
+ }
33
+
34
+ switchTab(tab: string, siteName: string) {
35
+ this.reqSites()
36
+ }
37
+
38
+ async reqSites() {
39
+ const sites = await new Api(this.ctx).siteGet()
40
+ this.siteList.loadSites(sites)
41
+ }
42
+ }