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,289 @@
1
+ import '@/style/site-list.less'
2
+
3
+ import Context from '@/context'
4
+ import Component from '@/lib/component'
5
+ import * as Utils from '@/lib/utils'
6
+ import * as Ui from '@/lib/ui'
7
+ import { SiteData } from '~/types/artalk-data'
8
+ import ItemTextEditor from '../item-text-editor'
9
+ import Api from '~/src/api'
10
+
11
+ export default class SiteList extends Component {
12
+ sites: SiteData[] = []
13
+
14
+ $header: HTMLElement
15
+ $headerTitle: HTMLElement
16
+ $headerActions: HTMLElement
17
+ $rowsWrap: HTMLElement
18
+
19
+ $editor?: HTMLElement
20
+ activeSite: string = ''
21
+
22
+ $add?: HTMLElement
23
+
24
+ constructor(ctx: Context) {
25
+ super(ctx)
26
+
27
+ this.$el = Utils.createElement(
28
+ `<div class="atk-site-list">
29
+ <div class="atk-header">
30
+ <div class="atk-title"></div>
31
+ <div class="atk-actions">
32
+ <div class="atk-item atk-site-add-btn"><i class="atk-icon atk-icon-plus"></i></div>
33
+ </div>
34
+ </div>
35
+ <div class="atk-site-rows-wrap"></div>
36
+ </div>`)
37
+
38
+ this.$header = this.$el.querySelector('.atk-header')!
39
+ this.$headerTitle = this.$header.querySelector('.atk-title')!
40
+ this.$headerActions = this.$header.querySelector('.atk-actions')!
41
+ this.$rowsWrap = this.$el.querySelector('.atk-site-rows-wrap')!
42
+
43
+ // 标题
44
+ this.$headerTitle.innerText = `共 0 个站点`
45
+
46
+ // 新建站点按钮
47
+ const $addBtn = this.$headerActions.querySelector<HTMLElement>('.atk-site-add-btn')!
48
+ $addBtn.onclick = () => {
49
+ this.closeEditor()
50
+ this.showAdd()
51
+ }
52
+ }
53
+
54
+ public loadSites(sites: SiteData[]) {
55
+ this.sites = sites
56
+ this.activeSite = ''
57
+ this.$rowsWrap.innerHTML = ''
58
+ this.$headerTitle.innerText = `共 0 个站点`
59
+
60
+ let $row: HTMLElement
61
+
62
+ for (let i = 0; i < sites.length; i++) {
63
+ const site = sites[i]
64
+
65
+ if (i % 4 === 0) { // 每 4 个 item 为一行
66
+ $row = Utils.createElement('<div class="atk-site-row">')
67
+ this.$rowsWrap.append($row)
68
+ }
69
+
70
+ // 创建 site item
71
+ const $site = this.renderSite(site, $row!)
72
+ $row!.append($site)
73
+ }
74
+
75
+ this.$headerTitle.innerText = `共 ${sites.length} 个站点`
76
+ }
77
+
78
+ public renderSite(site: SiteData, $row: HTMLElement) {
79
+ const $site = Utils.createElement(
80
+ `<div class="atk-site-item">
81
+ <div class="atk-site-logo"></div>
82
+ <div class="atk-site-name"></div>
83
+ </div>`)
84
+
85
+ const $siteLogo = $site.querySelector<HTMLElement>('.atk-site-logo')!
86
+ const $siteName = $site.querySelector<HTMLElement>('.atk-site-name')!
87
+ const setActive = () => { $site.classList.add('atk-active') }
88
+
89
+ $siteLogo.innerText = site.name.substr(0, 1)
90
+ $siteName.innerText = site.name
91
+
92
+ // click
93
+ $site.onclick = () => {
94
+ this.closeEditor()
95
+ this.closeAdd()
96
+ setActive()
97
+ this.showEditor(site, $site, $row)
98
+ }
99
+
100
+ if (this.activeSite === site.name) { setActive() }
101
+
102
+ return $site
103
+ }
104
+
105
+ public showEditor(site: SiteData, $site: HTMLElement, $row: HTMLElement) {
106
+ this.activeSite = site.name
107
+ this.$editor = Utils.createElement(`
108
+ <div class="atk-site-edit">
109
+ <div class="atk-header">
110
+ <div class="atk-site-info">
111
+ <span class="atk-site-name"></span>
112
+ <span class="atk-site-urls"></span>
113
+ </div>
114
+ <div class="atk-close-btn">
115
+ <i class="atk-icon atk-icon-close"></i>
116
+ </div>
117
+ </div>
118
+ <div class="atk-main">
119
+ <div class="atk-site-text-actions">
120
+ <div class="atk-item atk-rename-btn">重命名</div>
121
+ <div class="atk-item atk-edit-url-btn">修改 URL</div>
122
+ <!--<div class="atk-item atk-export-btn">导出</div>
123
+ <div class="atk-item atk-import-btn">导入</div>-->
124
+ </div>
125
+ <div class="atk-site-btn-actions">
126
+ <div class="atk-item atk-del-btn">
127
+ <i class="atk-icon atk-icon-del"></i>
128
+ </div>
129
+ </div>
130
+ </div>
131
+ </div>`)
132
+
133
+ // 插入
134
+ $row.before(this.$editor)
135
+
136
+ // header
137
+ const $siteName = this.$editor.querySelector<HTMLElement>('.atk-site-name')!
138
+ const $siteUrls = this.$editor.querySelector<HTMLElement>('.atk-site-urls')!
139
+ const $closeBtn = this.$editor.querySelector<HTMLElement>('.atk-close-btn')!
140
+ $closeBtn.onclick = () => this.closeEditor()
141
+
142
+ const update = (s: SiteData) => {
143
+ site = s
144
+ $siteName.innerText = site.name
145
+ $siteName.onclick = () => { if (site.first_url) window.open(site.first_url) }
146
+ $siteUrls.innerHTML = ''
147
+ site.urls?.forEach(u => {
148
+ const $item = Utils.createElement('<span class="atk-url-item"></span>')
149
+ $siteUrls.append($item)
150
+ $item.innerText = (u || '').replace(/\/$/, '')
151
+ $item.onclick = () => { window.open(u) }
152
+ })
153
+ }
154
+ update(site)
155
+
156
+ // actions
157
+ const $main = this.$editor.querySelector<HTMLElement>('.atk-main')!
158
+ const $actions = this.$editor.querySelector<HTMLElement>('.atk-site-text-actions')!
159
+ const $renameBtn = $actions.querySelector<HTMLElement>('.atk-rename-btn')!
160
+ const $editUrlBtn = $actions.querySelector<HTMLElement>('.atk-edit-url-btn')!
161
+ // const $exportBtn = $actions.querySelector<HTMLElement>('.atk-export-btn')!
162
+ // const $importBtn = $actions.querySelector<HTMLElement>(`.atk-import-btn`)!
163
+ const $delBtn = this.$editor.querySelector<HTMLElement>('.atk-del-btn')!
164
+ const showLoading = () => { Ui.showLoading(this.$editor!) }
165
+ const hideLoading = () => { Ui.hideLoading(this.$editor!) }
166
+ const showError = (msg: string) => { window.alert(msg) }
167
+
168
+ // 文本编辑
169
+ const openTextEditor = (key: string) => {
170
+ let initValue = site[key] || ''
171
+ if (key === 'urls') initValue = site.urls_raw || ''
172
+ const textEditor = new ItemTextEditor({
173
+ initValue,
174
+ onYes: async (val: string) => {
175
+ Ui.showLoading(textEditor.$el)
176
+ let s: SiteData
177
+ try {
178
+ s = await new Api(this.ctx).siteEdit({ ...site, [key]: val })
179
+ } catch (err: any) {
180
+ showError(`修改失败:${err.msg || '未知错误'}`)
181
+ console.error(err)
182
+ return false
183
+ } finally { Ui.hideLoading(textEditor.$el) }
184
+ $site.replaceWith(this.renderSite(s, $row))
185
+ update(s)
186
+ return true
187
+ }
188
+ })
189
+ textEditor.appendTo($main)
190
+ }
191
+
192
+ // 重命名
193
+ $renameBtn.onclick = () => openTextEditor('name')
194
+
195
+ // 修改 URL
196
+ $editUrlBtn.onclick = () => openTextEditor('urls')
197
+
198
+ // 导出
199
+ // $exportBtn.onclick = () => {}
200
+
201
+ // // 导入
202
+ // $importBtn.onclick = () => {}
203
+
204
+ // 删除
205
+ $delBtn.onclick = () => {
206
+ const del = async () => {
207
+ showLoading()
208
+ try {
209
+ await new Api(this.ctx).siteDel(site.id, true)
210
+ } catch (err: any) {
211
+ console.log(err)
212
+ showError(`删除失败 ${String(err)}`)
213
+ return
214
+ } finally { hideLoading() }
215
+ this.closeEditor()
216
+ $site.remove()
217
+ this.sites = this.sites.filter(s => s.name !== site.name)
218
+ }
219
+ if (window.confirm(
220
+ `确认删除站点 "${site.name}"?将会删除所有相关数据`
221
+ )) del()
222
+ }
223
+ }
224
+
225
+ public closeEditor() {
226
+ if (!this.$editor) return
227
+
228
+ this.$editor.remove()
229
+ this.$rowsWrap.querySelectorAll('.atk-site-item').forEach((e) => e.classList.remove('atk-active'))
230
+ this.activeSite = ''
231
+ }
232
+
233
+ public showAdd() {
234
+ this.closeAdd()
235
+
236
+ this.$add = Utils.createElement(`
237
+ <div class="atk-site-add">
238
+ <div class="atk-header">
239
+ <div class="atk-title">新增站点</div>
240
+ <div class="atk-close-btn">
241
+ <i class="atk-icon atk-icon-close"></i>
242
+ </div>
243
+ </div>
244
+ <div class="atk-form">
245
+ <input type="text" name="AtkSiteName" placeholder="站点名称" autocomplete="off">
246
+ <input type="text" name="AtkSiteUrls" placeholder="站点 URL(多个用逗号隔开)" autocomplete="off">
247
+ <button class="atk-btn" name="AtkSubmit">创建</button>
248
+ </div>
249
+ </div>`)
250
+ this.$header.after(this.$add)
251
+
252
+ const $closeBtn = this.$add.querySelector<HTMLElement>('.atk-close-btn')!
253
+ $closeBtn.onclick = () => this.closeAdd()
254
+
255
+ const $siteName = this.$add.querySelector<HTMLInputElement>('[name="AtkSiteName"]')!
256
+ const $siteUrls = this.$add.querySelector<HTMLInputElement>('[name="AtkSiteUrls"]')!
257
+ const $submitBtn = this.$add.querySelector<HTMLButtonElement>('[name="AtkSubmit"]')!
258
+
259
+ $submitBtn.onclick = async () => {
260
+ const siteName = $siteName.value.trim()
261
+ const siteUrls = $siteUrls.value.trim()
262
+
263
+ if (siteName === '') { $siteName.focus(); return }
264
+
265
+ Ui.showLoading(this.$add!)
266
+ let s: SiteData
267
+ try {
268
+ s = await new Api(this.ctx).siteAdd(siteName, siteUrls)
269
+ } catch (err: any) {
270
+ window.alert(`创建失败:${err.msg || ''}`)
271
+ console.error(err)
272
+ return
273
+ } finally { Ui.hideLoading(this.$add!) }
274
+
275
+ this.sites.push(s)
276
+ this.loadSites(this.sites)
277
+ this.closeAdd()
278
+ }
279
+
280
+ // 回车键提交
281
+ const keyDown = (evt: KeyboardEvent) => { if (evt.key === 'Enter') { $submitBtn.click() } }
282
+ $siteName.onkeyup = (evt) => keyDown(evt)
283
+ $siteUrls.onkeyup = (evt) => keyDown(evt)
284
+ }
285
+
286
+ public closeAdd() {
287
+ this.$add?.remove()
288
+ }
289
+ }