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
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import * as Utils from '../lib/utils'
|
|
2
|
+
|
|
3
|
+
interface ActionBtnConf {
|
|
4
|
+
/** 按钮文字 (动态/静态) */
|
|
5
|
+
text: (() => string) | string
|
|
6
|
+
|
|
7
|
+
/** 仅管理员可用 */
|
|
8
|
+
adminOnly?: boolean
|
|
9
|
+
|
|
10
|
+
/** 确认操作 */
|
|
11
|
+
confirm?: boolean
|
|
12
|
+
|
|
13
|
+
/** 确认时提示文字 */
|
|
14
|
+
confirmText?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 通用操作按钮
|
|
19
|
+
*/
|
|
20
|
+
export default class ActionBtn {
|
|
21
|
+
private conf: ActionBtnConf
|
|
22
|
+
public $el: HTMLElement
|
|
23
|
+
|
|
24
|
+
public isLoading = false // 正在加载
|
|
25
|
+
|
|
26
|
+
public msgRecTimer?: number // 消息显示复原定时器
|
|
27
|
+
public msgRecTimerFunc?: Function // 消息显示复原操作
|
|
28
|
+
public get isMessaging() { return !!this.msgRecTimer } // 消息正在显示
|
|
29
|
+
|
|
30
|
+
public isConfirming = false // 正在确认
|
|
31
|
+
public confirmRecTimer?: number // 确认消息复原定时器
|
|
32
|
+
|
|
33
|
+
/** 构造函数 */
|
|
34
|
+
constructor(conf: ActionBtnConf|string|(() => string)) {
|
|
35
|
+
this.$el = Utils.createElement(`<span class="atk-common-action-btn"></span>`)
|
|
36
|
+
|
|
37
|
+
this.conf = (typeof conf !== 'object') ? ({ text: conf }) : conf
|
|
38
|
+
this.$el.innerText = this.getText()
|
|
39
|
+
|
|
40
|
+
// 仅管理员可操作
|
|
41
|
+
if (this.conf.adminOnly) this.$el.setAttribute('atk-only-admin-show', '')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** 将按钮装载到指定元素 */
|
|
45
|
+
public appendTo(dom: HTMLElement) {
|
|
46
|
+
dom.append(this.$el)
|
|
47
|
+
return this
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 获取按钮文字(动态/静态) */
|
|
51
|
+
private getText() {
|
|
52
|
+
return (typeof this.conf.text === 'string') ? this.conf.text : this.conf.text()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** 设置点击事件 */
|
|
56
|
+
public setClick(func: Function) {
|
|
57
|
+
this.$el.onclick = (e) => {
|
|
58
|
+
e.stopPropagation() // 防止穿透
|
|
59
|
+
|
|
60
|
+
// 按钮处于加载状态,禁止点击
|
|
61
|
+
if (this.isLoading) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 操作确认
|
|
66
|
+
if (this.conf.confirm && !this.isMessaging) {
|
|
67
|
+
const confirmRestore = () => {
|
|
68
|
+
this.isConfirming = false
|
|
69
|
+
this.$el.classList.remove('atk-btn-confirm')
|
|
70
|
+
this.$el.innerText = this.getText()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!this.isConfirming) {
|
|
74
|
+
this.isConfirming = true
|
|
75
|
+
this.$el.classList.add('atk-btn-confirm')
|
|
76
|
+
this.$el.innerText = this.conf.confirmText || '确认操作'
|
|
77
|
+
this.confirmRecTimer = window.setTimeout(() => confirmRestore(), 5000)
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (this.confirmRecTimer) window.clearTimeout(this.confirmRecTimer)
|
|
82
|
+
confirmRestore()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 立刻释放掉原有的定时器(当显示消息后,用户立刻点击时)
|
|
86
|
+
if (this.msgRecTimer) {
|
|
87
|
+
this.fireMsgRecTimer()
|
|
88
|
+
this.clearMsgRecTimer()
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 触发设定的点击事件
|
|
93
|
+
func()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** 文字刷新(动态/静态) */
|
|
98
|
+
public updateText(text?: (() => string) | string) {
|
|
99
|
+
if (text) this.conf.text = text
|
|
100
|
+
this.setLoading(false)
|
|
101
|
+
this.$el.innerText = this.getText()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** 设置加载状态 */
|
|
105
|
+
public setLoading(value = true, loadingText?: string) {
|
|
106
|
+
if (this.isLoading === value) return
|
|
107
|
+
this.isLoading = value
|
|
108
|
+
if (value) {
|
|
109
|
+
this.$el.classList.add('atk-btn-loading')
|
|
110
|
+
this.$el.innerText = loadingText || '加载中...'
|
|
111
|
+
} else {
|
|
112
|
+
this.$el.classList.remove('atk-btn-loading')
|
|
113
|
+
this.$el.innerText = this.getText()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** 错误消息 */
|
|
118
|
+
public setError(text: string) {
|
|
119
|
+
this.setMsg(text, 'atk-btn-error')
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** 警告消息 */
|
|
123
|
+
public setWarn(text: string) {
|
|
124
|
+
this.setMsg(text, 'atk-btn-warn')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** 成功消息 */
|
|
128
|
+
public setSuccess(text: string) {
|
|
129
|
+
this.setMsg(text, 'atk-btn-success')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** 设置消息 */
|
|
133
|
+
public setMsg(text: string, className?: string, duringTime?: number, after?: Function) {
|
|
134
|
+
this.setLoading(false)
|
|
135
|
+
if (className) this.$el.classList.add(className)
|
|
136
|
+
this.$el.innerText = text
|
|
137
|
+
|
|
138
|
+
// 设定复原 timer
|
|
139
|
+
this.setMsgRecTimer(() => {
|
|
140
|
+
this.$el.innerText = this.getText()
|
|
141
|
+
if (className) this.$el.classList.remove(className)
|
|
142
|
+
if (after) after()
|
|
143
|
+
}, duringTime || 2500) // 消息默认显示持续 2500s
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** 设置消息复原操作定时器 */
|
|
147
|
+
private setMsgRecTimer(func: Function, duringTime: number) {
|
|
148
|
+
this.fireMsgRecTimer()
|
|
149
|
+
this.clearMsgRecTimer()
|
|
150
|
+
|
|
151
|
+
this.msgRecTimerFunc = func
|
|
152
|
+
this.msgRecTimer = window.setTimeout(() => {
|
|
153
|
+
func()
|
|
154
|
+
this.clearMsgRecTimer()
|
|
155
|
+
}, duringTime)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** 立刻触发器复原定时器 */
|
|
159
|
+
private fireMsgRecTimer() {
|
|
160
|
+
if (this.msgRecTimerFunc) this.msgRecTimerFunc()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** 仅清除 timer */
|
|
164
|
+
private clearMsgRecTimer() {
|
|
165
|
+
if (this.msgRecTimer) window.clearTimeout(this.msgRecTimer)
|
|
166
|
+
this.msgRecTimer = undefined
|
|
167
|
+
this.msgRecTimerFunc = undefined
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import '@/style/page-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 { PageData } from '~/types/artalk-data'
|
|
8
|
+
import ItemTextEditor from '../item-text-editor'
|
|
9
|
+
import Api from '~/src/api'
|
|
10
|
+
import ActionBtn from '../action-btn'
|
|
11
|
+
|
|
12
|
+
export default class PageList extends Component {
|
|
13
|
+
$editor?: HTMLElement
|
|
14
|
+
$inputer?: HTMLElement
|
|
15
|
+
|
|
16
|
+
pages: PageData[] = []
|
|
17
|
+
|
|
18
|
+
constructor(ctx: Context) {
|
|
19
|
+
super(ctx)
|
|
20
|
+
|
|
21
|
+
this.$el = Utils.createElement(`<div class="atk-page-list"></div>`)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** 清空所有 Pages */
|
|
25
|
+
clearAll() {
|
|
26
|
+
this.pages = []
|
|
27
|
+
this.$el.innerHTML = ''
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** 导入 Page 数据 */
|
|
31
|
+
importPages(pages: PageData[]) {
|
|
32
|
+
this.pages.push(...pages)
|
|
33
|
+
|
|
34
|
+
pages.forEach((page) => {
|
|
35
|
+
const $page = this.renderPage(page)
|
|
36
|
+
this.$el.append($page)
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Page Element */
|
|
41
|
+
renderPage(page: PageData) {
|
|
42
|
+
const $page = Utils.createElement(
|
|
43
|
+
`<div class="atk-page-item">
|
|
44
|
+
<div class="atk-page-main">
|
|
45
|
+
<div class="atk-title"></div>
|
|
46
|
+
<div class="atk-sub"></div>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="atk-page-actions">
|
|
49
|
+
<div class="atk-item atk-edit-btn">
|
|
50
|
+
<i class="atk-icon atk-icon-edit"></i>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>`)
|
|
54
|
+
|
|
55
|
+
const $main = $page.querySelector<HTMLElement>('.atk-page-main')!
|
|
56
|
+
const $title = $main.querySelector<HTMLElement>('.atk-title')!
|
|
57
|
+
const $sub = $main.querySelector<HTMLElement>('.atk-sub')!
|
|
58
|
+
const $editBtn = $page.querySelector<HTMLElement>('.atk-edit-btn')!
|
|
59
|
+
|
|
60
|
+
$title.innerText = page.title
|
|
61
|
+
$sub.innerText = page.url || page.key
|
|
62
|
+
$editBtn.onclick = () => this.showEditor(page, $page)
|
|
63
|
+
|
|
64
|
+
return $page
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** 显示编辑器 */
|
|
68
|
+
showEditor(page: PageData, $page: HTMLElement) {
|
|
69
|
+
this.closeEditor()
|
|
70
|
+
|
|
71
|
+
this.$editor = Utils.createElement(
|
|
72
|
+
`<div class="atk-page-edit-layer">
|
|
73
|
+
<div class="atk-page-main-actions">
|
|
74
|
+
<div class="atk-item atk-title-edit-btn">标题修改</div>
|
|
75
|
+
<div class="atk-item atk-key-edit-btn">KEY 变更</div>
|
|
76
|
+
<div class="atk-item atk-admin-only-btn"></div>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="atk-page-actions">
|
|
79
|
+
<div class="atk-item atk-sync-btn">
|
|
80
|
+
<i class="atk-icon atk-icon-sync"></i>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="atk-item atk-del-btn">
|
|
83
|
+
<i class="atk-icon atk-icon-del"></i>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="atk-item atk-close-btn">
|
|
86
|
+
<i class="atk-icon atk-icon-close"></i>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>`)
|
|
90
|
+
$page.prepend(this.$editor)
|
|
91
|
+
|
|
92
|
+
const $titleEditBtn = this.$editor.querySelector<HTMLElement>('.atk-title-edit-btn')!
|
|
93
|
+
const $keyEditBtn = this.$editor.querySelector<HTMLElement>('.atk-key-edit-btn')!
|
|
94
|
+
const $adminOnlyBtn = this.$editor.querySelector<HTMLElement>('.atk-admin-only-btn')!
|
|
95
|
+
const $syncBtn = this.$editor.querySelector<HTMLElement>('.atk-sync-btn')!
|
|
96
|
+
const $delBtn = this.$editor.querySelector<HTMLElement>('.atk-del-btn')!
|
|
97
|
+
const $closeBtn = this.$editor.querySelector<HTMLElement>('.atk-close-btn')!
|
|
98
|
+
const showLoading = () => { Ui.showLoading(this.$editor!) }
|
|
99
|
+
const hideLoading = () => { Ui.hideLoading(this.$editor!) }
|
|
100
|
+
const showError = (msg: string) => { window.alert(msg) }
|
|
101
|
+
|
|
102
|
+
// 关闭编辑器
|
|
103
|
+
$closeBtn.onclick = () => this.closeEditor()
|
|
104
|
+
|
|
105
|
+
// 文本编辑
|
|
106
|
+
const openTextEditor = (key: string) => {
|
|
107
|
+
const textEditor = new ItemTextEditor({
|
|
108
|
+
initValue: page[key] || '',
|
|
109
|
+
onYes: async (val: string) => {
|
|
110
|
+
Ui.showLoading(textEditor.$el)
|
|
111
|
+
let p: PageData
|
|
112
|
+
try {
|
|
113
|
+
p = await new Api(this.ctx).pageEdit({ ...page, [key]: val })
|
|
114
|
+
} catch (err: any) {
|
|
115
|
+
showError(`修改失败:${err.msg || '未知错误'}`)
|
|
116
|
+
console.error(err)
|
|
117
|
+
return false
|
|
118
|
+
} finally { Ui.hideLoading(textEditor.$el) }
|
|
119
|
+
$page.replaceWith(this.renderPage(p))
|
|
120
|
+
return true
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
textEditor.appendTo(this.$editor!)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 标题修改
|
|
127
|
+
$titleEditBtn.onclick = () => openTextEditor('title')
|
|
128
|
+
|
|
129
|
+
// Key 修改
|
|
130
|
+
$keyEditBtn.onclick = () => openTextEditor('key')
|
|
131
|
+
|
|
132
|
+
// 仅管理员可评
|
|
133
|
+
const adminOnlyActionBtn = new ActionBtn({
|
|
134
|
+
text: () => {
|
|
135
|
+
$adminOnlyBtn.classList.remove('atk-green', 'atk-yellow')
|
|
136
|
+
$adminOnlyBtn.classList.add(!page.admin_only ? 'atk-green' : 'atk-yellow')
|
|
137
|
+
return (!page.admin_only) ? '所有人可评' : '管理员可评'
|
|
138
|
+
}
|
|
139
|
+
}).appendTo($adminOnlyBtn)
|
|
140
|
+
|
|
141
|
+
$adminOnlyBtn.onclick = async () => {
|
|
142
|
+
showLoading()
|
|
143
|
+
let p: PageData
|
|
144
|
+
try {
|
|
145
|
+
p = await new Api(this.ctx).pageEdit({ ...page, admin_only: !page.admin_only })
|
|
146
|
+
} catch (err: any) {
|
|
147
|
+
showError(`修改失败:${err.msg || '未知错误'}`)
|
|
148
|
+
console.log(err)
|
|
149
|
+
return
|
|
150
|
+
} finally { hideLoading() }
|
|
151
|
+
page.admin_only = p.admin_only
|
|
152
|
+
adminOnlyActionBtn.updateText()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 同步操作
|
|
156
|
+
$syncBtn.onclick = async () => {
|
|
157
|
+
showLoading()
|
|
158
|
+
let p: PageData
|
|
159
|
+
try {
|
|
160
|
+
p = await new Api(this.ctx).pageFetch(page.id)
|
|
161
|
+
} catch (err: any) {
|
|
162
|
+
showError(`同步失败:${err.msg || '未知错误'}`)
|
|
163
|
+
console.log(err)
|
|
164
|
+
return
|
|
165
|
+
} finally { hideLoading() }
|
|
166
|
+
$page.replaceWith(this.renderPage(p))
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 删除
|
|
170
|
+
$delBtn.onclick = () => {
|
|
171
|
+
const del = async () => {
|
|
172
|
+
showLoading()
|
|
173
|
+
try {
|
|
174
|
+
await new Api(this.ctx).pageDel(page.key, page.site_name)
|
|
175
|
+
} catch (err: any) {
|
|
176
|
+
console.log(err)
|
|
177
|
+
showError(`删除失败 ${String(err)}`)
|
|
178
|
+
return
|
|
179
|
+
} finally { hideLoading() }
|
|
180
|
+
$page.remove()
|
|
181
|
+
}
|
|
182
|
+
if (window.confirm(
|
|
183
|
+
`确认删除页面 "${page.title || page.key}"?将会删除所有相关数据`
|
|
184
|
+
)) del()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** 关闭编辑器 */
|
|
189
|
+
public closeEditor() {
|
|
190
|
+
if (!this.$editor) return
|
|
191
|
+
|
|
192
|
+
this.$editor.remove()
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import '@/style/site-list.less'
|
|
2
|
+
|
|
3
|
+
import Context from '@/context'
|
|
4
|
+
import * as Utils from '@/lib/utils'
|
|
5
|
+
import * as Ui from '@/lib/ui'
|
|
6
|
+
import { SiteData } from '~/types/artalk-data'
|
|
7
|
+
import Api from '~/src/api'
|
|
8
|
+
|
|
9
|
+
interface SiteListFloaterConf {
|
|
10
|
+
onSwitchSite: (siteName: string) => boolean|void
|
|
11
|
+
onClickSitesViewBtn: () => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default class SiteListFloater {
|
|
15
|
+
ctx: Context
|
|
16
|
+
conf: SiteListFloaterConf
|
|
17
|
+
$el: HTMLElement
|
|
18
|
+
sites: SiteData[] = []
|
|
19
|
+
$sites: HTMLElement
|
|
20
|
+
|
|
21
|
+
constructor(ctx: Context, conf: SiteListFloaterConf) {
|
|
22
|
+
this.ctx = ctx
|
|
23
|
+
this.conf = conf
|
|
24
|
+
this.$el = Utils.createElement(
|
|
25
|
+
`<div class="atk-site-list-floater" style="display: none;">
|
|
26
|
+
<div class="atk-sites"></div>
|
|
27
|
+
</div>`)
|
|
28
|
+
this.$sites = this.$el.querySelector('.atk-sites')!
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** 装载 */
|
|
32
|
+
public async load(selectedSite?: string) {
|
|
33
|
+
this.$sites.innerHTML = ''
|
|
34
|
+
|
|
35
|
+
const renderSiteItem = (siteName: string, siteLogo: string, siteTarget?: string, onclick?: Function) => {
|
|
36
|
+
const $site = Utils.createElement(
|
|
37
|
+
`<div class="atk-site-item">
|
|
38
|
+
<div class="atk-site-logo"></div>
|
|
39
|
+
<div class="atk-site-name"></div>
|
|
40
|
+
</div>`)
|
|
41
|
+
$site.onclick = !onclick ? () => this.switch(siteTarget || siteName) : () => onclick()
|
|
42
|
+
$site.setAttribute('data-name', siteTarget || siteName)
|
|
43
|
+
const $siteLogo = $site.querySelector<HTMLElement>('.atk-site-logo')!
|
|
44
|
+
const $siteName = $site.querySelector<HTMLElement>('.atk-site-name')!
|
|
45
|
+
$siteLogo.innerText = siteLogo
|
|
46
|
+
$siteName.innerText = siteName
|
|
47
|
+
if (selectedSite === (siteTarget || siteName)) $site.classList.add('atk-active')
|
|
48
|
+
this.$sites.append($site)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
renderSiteItem('所有站点', '_', '__ATK_SITE_ALL')
|
|
52
|
+
|
|
53
|
+
const sites = await new Api(this.ctx).siteGet()
|
|
54
|
+
sites.forEach((site) => {
|
|
55
|
+
renderSiteItem(site.name, site.name.substr(0, 1))
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
renderSiteItem('站点管理', '+', '', () => { this.conf.onClickSitesViewBtn();this.hide() })
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 切换站点 */
|
|
62
|
+
private switch(siteName: string) {
|
|
63
|
+
if (this.conf.onSwitchSite(siteName) === false) { return }
|
|
64
|
+
|
|
65
|
+
// set active
|
|
66
|
+
this.$sites.querySelectorAll('.atk-site-item').forEach(e => {
|
|
67
|
+
if (e.getAttribute('data-name') !== siteName) {
|
|
68
|
+
e.classList.remove('atk-active')
|
|
69
|
+
} else {
|
|
70
|
+
e.classList.add('atk-active')
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
this.hide()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private outsideChecker?: (evt: MouseEvent) => void // outside checker
|
|
78
|
+
|
|
79
|
+
/** 显示 */
|
|
80
|
+
public show($trigger?: HTMLElement) {
|
|
81
|
+
this.$el.style.display = ''
|
|
82
|
+
|
|
83
|
+
// 点击外部隐藏
|
|
84
|
+
if ($trigger) {
|
|
85
|
+
this.outsideChecker = (evt: MouseEvent) => {
|
|
86
|
+
const isClickInside = $trigger.contains(evt.target as any)||this.$el.contains(evt.target as any)
|
|
87
|
+
if (!isClickInside) { this.hide() }
|
|
88
|
+
}
|
|
89
|
+
document.addEventListener('click', this.outsideChecker)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** 隐藏 */
|
|
94
|
+
public hide() {
|
|
95
|
+
this.$el.style.display = 'none'
|
|
96
|
+
if (this.outsideChecker) document.removeEventListener('click', this.outsideChecker)
|
|
97
|
+
}
|
|
98
|
+
}
|