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.
- package/README.md +7 -60
- package/dist/Artalk.css +1 -1
- package/dist/Artalk.es.js +5996 -1
- package/dist/Artalk.iife.js +266 -1
- package/dist/Artalk.js +266 -1
- package/package.json +3 -4
- package/src/api/index.ts +269 -0
- package/src/api/request.ts +125 -0
- package/src/artalk.ts +153 -0
- package/src/components/_component.ts.txt +10 -0
- package/src/components/action-btn.ts +169 -0
- package/src/components/admin/page-list.ts +194 -0
- package/src/components/admin/site-list-floater.ts +98 -0
- package/src/components/admin/site-list.ts +289 -0
- package/src/components/comment.ts +478 -0
- package/src/components/dialog.ts +63 -0
- package/src/components/editor-plugs/{_ExamplePlug.ts.txt → _example-plug.ts.txt} +0 -0
- package/src/components/editor-plugs/{EditorPlug.ts → editor-plug.ts} +3 -3
- package/src/components/editor-plugs/{EmoticonsPlug.less → emoticons-plug.less} +0 -0
- package/src/components/editor-plugs/{EmoticonsPlug.ts → emoticons-plug.ts} +9 -9
- package/src/components/editor-plugs/{PreviewPlug.less → preview-plug.less} +0 -0
- package/src/components/editor-plugs/{PreviewPlug.ts → preview-plug.ts} +10 -10
- package/src/components/{Editor.ts → editor.ts} +104 -102
- package/src/components/html/comment.html +2 -6
- package/src/components/html/list.html +0 -1
- package/src/components/html/sidebar.html +18 -4
- package/src/components/item-text-editor.ts +136 -0
- package/src/components/layer.ts +135 -0
- package/src/components/list-lite.ts +507 -0
- package/src/components/list.ts +172 -0
- package/src/components/pagination.ts +150 -0
- package/src/components/read-more-btn.ts +71 -0
- package/src/components/sidebar-view.ts +26 -0
- package/src/components/sidebar-views/comments-view.ts +66 -0
- package/src/components/sidebar-views/pages-view.ts +61 -0
- package/src/components/sidebar-views/setting-view.ts +21 -0
- package/src/components/sidebar-views/sites-view.ts +42 -0
- package/src/components/sidebar-views/transfer-view.ts +218 -0
- package/src/components/sidebar.ts +288 -0
- package/src/context.ts +42 -0
- package/src/default-conf.ts +42 -0
- package/src/lib/checker/admin-checker.ts +33 -0
- package/src/lib/checker/captcha-checker.ts +38 -0
- package/src/lib/checker/index.ts +134 -0
- package/src/lib/component.ts +3 -3
- package/src/lib/md5.js +6 -0
- package/src/lib/ui.ts +64 -74
- package/src/lib/user.ts +2 -8
- package/src/lib/utils.ts +30 -11
- package/src/main.ts +1 -1
- package/src/style/_color.less +8 -2
- package/src/style/_extend.less +81 -0
- package/src/style/_variables.less +3 -0
- package/src/style/comment.less +60 -24
- package/src/style/editor.less +3 -0
- package/src/style/item-text-editor.less +35 -0
- package/src/style/list.less +51 -32
- package/src/style/main.less +75 -1
- package/src/style/page-list.less +50 -0
- package/src/style/pagination.less +58 -0
- package/src/style/sidebar.less +274 -199
- package/src/style/site-list.less +208 -0
- package/tsconfig.json +27 -0
- package/types/artalk-config.d.ts +68 -51
- package/types/artalk-data.d.ts +38 -1
- package/types/event.d.ts +73 -0
- package/types/index.d.ts +1 -6
- package/.github/workflows/npm.yml +0 -31
- package/.github/workflows/pages.yml +0 -30
- package/dist/src/Event.d.ts +0 -59
- package/dist/types/artalk-config.d.ts +0 -79
- package/dist/types/artalk-data.d.ts +0 -153
- package/dist/types/global.d.ts +0 -1
- package/dist/types/index.d.ts +0 -8
- package/dist/types/vite-env.d.ts +0 -1
- package/example/pjax/res1.html +0 -41
- package/example/pjax/res2.html +0 -41
- package/src/Artalk.ts +0 -160
- package/src/Constant.ts +0 -6
- package/src/Context.ts +0 -34
- package/src/Event.d.ts +0 -59
- package/src/components/Comment.ts +0 -402
- package/src/components/Layer.ts +0 -138
- package/src/components/List.ts +0 -162
- package/src/components/ListLite.ts +0 -407
- package/src/components/Sidebar.ts +0 -141
- package/src/components/sidebar-views/AdminView.ts +0 -523
- package/src/components/sidebar-views/MessageView.ts +0 -48
- package/src/components/sidebar-views/SidebarView.ts +0 -24
- package/src/lib/api.ts +0 -380
- 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
|
-
|