artalk 2.1.9 → 2.2.2
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/LICENSE +159 -668
- package/dist/Artalk.css +1 -1
- package/dist/Artalk.es.js +162 -56
- package/dist/Artalk.iife.js +1 -1
- package/dist/Artalk.js +1 -1
- package/package.json +3 -2
- package/src/api/index.ts +14 -2
- package/src/api/request.ts +1 -0
- package/src/components/comment.ts +1 -1
- package/src/components/editor.ts +2 -0
- package/src/components/layer.ts +19 -8
- package/src/components/list-lite.ts +12 -10
- package/src/components/sidebar-layer.ts +22 -0
- package/src/lib/checker/admin-checker.ts +4 -4
- package/src/lib/checker/captcha-checker.ts +52 -6
- package/src/lib/checker/index.ts +65 -23
- package/src/lib/ui.ts +2 -1
- package/src/style/main.less +35 -0
- package/tsconfig.json +2 -18
- package/types/event.d.ts +1 -0
package/package.json
CHANGED
package/src/api/index.ts
CHANGED
|
@@ -117,6 +117,12 @@ export default class Api {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
/** 用户 · 登录状态 */
|
|
121
|
+
public async loginStatus() {
|
|
122
|
+
const data = await POST<any>(this.ctx, `${this.baseURL}/login-status`)
|
|
123
|
+
return (data || { is_login: false }) as { is_login: boolean }
|
|
124
|
+
}
|
|
125
|
+
|
|
120
126
|
// ============================
|
|
121
127
|
// 页面 Page
|
|
122
128
|
// ============================
|
|
@@ -293,13 +299,19 @@ export default class Api {
|
|
|
293
299
|
|
|
294
300
|
/** 验证码 · 获取 */
|
|
295
301
|
public async captchaGet() {
|
|
296
|
-
const data = await
|
|
302
|
+
const data = await POST<any>(this.ctx, `${this.baseURL}/captcha/refresh`)
|
|
297
303
|
return (data.img_data || '') as string
|
|
298
304
|
}
|
|
299
305
|
|
|
300
306
|
/** 验证码 · 检验 */
|
|
301
307
|
public async captchaCheck(value: string) {
|
|
302
|
-
const data = await
|
|
308
|
+
const data = await POST<any>(this.ctx, `${this.baseURL}/captcha/check`, { value })
|
|
303
309
|
return (data.img_data || '') as string
|
|
304
310
|
}
|
|
311
|
+
|
|
312
|
+
/** 验证码 · 状态 */
|
|
313
|
+
public async captchaStatus() {
|
|
314
|
+
const data = await POST<any>(this.ctx, `${this.baseURL}/captcha/status`)
|
|
315
|
+
return (data || { is_pass: false }) as { is_pass: boolean }
|
|
316
|
+
}
|
|
305
317
|
}
|
package/src/api/request.ts
CHANGED
|
@@ -43,6 +43,7 @@ export async function Fetch(ctx: Context, input: RequestInfo, init: RequestInit,
|
|
|
43
43
|
json = await (new Promise<any>((resolve, reject) => {
|
|
44
44
|
ctx.trigger('checker-captcha', {
|
|
45
45
|
imgData: json.data.img_data,
|
|
46
|
+
iframe: json.data.iframe,
|
|
46
47
|
onSuccess: () => {
|
|
47
48
|
recall(resolve, reject)
|
|
48
49
|
},
|
package/src/components/editor.ts
CHANGED
|
@@ -551,6 +551,8 @@ export default class Editor extends Component {
|
|
|
551
551
|
const $travelPlace = Utils.createElement('<div></div>')
|
|
552
552
|
$afterEl.after($travelPlace)
|
|
553
553
|
$travelPlace.replaceWith(this.$el)
|
|
554
|
+
|
|
555
|
+
this.$el.classList.add('atk-fade-in') // 添加渐入动画
|
|
554
556
|
}
|
|
555
557
|
|
|
556
558
|
travelBack () {
|
package/src/components/layer.ts
CHANGED
|
@@ -65,12 +65,7 @@ export default class Layer extends Component {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// body style 禁止滚动 + 防抖
|
|
68
|
-
this.
|
|
69
|
-
this.bodyStyleOrgPaddingRight = document.body.style.paddingRight
|
|
70
|
-
document.body.style.overflow = 'hidden'
|
|
71
|
-
|
|
72
|
-
const bpr = parseInt(window.getComputedStyle(document.body, null).getPropertyValue('padding-right'), 10)
|
|
73
|
-
document.body.style.paddingRight = `${Ui.getScrollBarWidth() + bpr || 0}px`
|
|
68
|
+
this.pageBodyScrollBarHide()
|
|
74
69
|
}
|
|
75
70
|
|
|
76
71
|
hide () {
|
|
@@ -81,8 +76,7 @@ export default class Layer extends Component {
|
|
|
81
76
|
this.newActionTimer(() => {
|
|
82
77
|
this.$wrap.style.display = 'none'
|
|
83
78
|
// body style 禁止滚动解除
|
|
84
|
-
|
|
85
|
-
document.body.style.paddingRight = this.bodyStyleOrgPaddingRight
|
|
79
|
+
this.pageBodyScrollBarShow()
|
|
86
80
|
this.checkCleanLayer()
|
|
87
81
|
}, 450)
|
|
88
82
|
this.newActionTimer(() => {
|
|
@@ -95,6 +89,22 @@ export default class Layer extends Component {
|
|
|
95
89
|
this.maskClickHideEnable = enable
|
|
96
90
|
}
|
|
97
91
|
|
|
92
|
+
// 页面滚动条隐藏
|
|
93
|
+
pageBodyScrollBarHide() {
|
|
94
|
+
this.bodyStyleOrgOverflow = document.body.style.overflow
|
|
95
|
+
this.bodyStyleOrgPaddingRight = document.body.style.paddingRight
|
|
96
|
+
document.body.style.overflow = 'hidden'
|
|
97
|
+
|
|
98
|
+
const bpr = parseInt(window.getComputedStyle(document.body, null).getPropertyValue('padding-right'), 10)
|
|
99
|
+
document.body.style.paddingRight = `${Ui.getScrollBarWidth() + bpr || 0}px`
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 页面滚动条显示
|
|
103
|
+
pageBodyScrollBarShow() {
|
|
104
|
+
document.body.style.overflow = this.bodyStyleOrgOverflow
|
|
105
|
+
document.body.style.paddingRight = this.bodyStyleOrgPaddingRight
|
|
106
|
+
}
|
|
107
|
+
|
|
98
108
|
// Timers
|
|
99
109
|
private static actionTimers: {act: Function, tid: number}[] = []
|
|
100
110
|
|
|
@@ -120,6 +130,7 @@ export default class Layer extends Component {
|
|
|
120
130
|
disposeNow () {
|
|
121
131
|
document.body.style.overflow = ''
|
|
122
132
|
this.$el.remove()
|
|
133
|
+
this.pageBodyScrollBarShow()
|
|
123
134
|
// this.$el dispose
|
|
124
135
|
this.checkCleanLayer()
|
|
125
136
|
}
|
|
@@ -7,6 +7,7 @@ import Comment from './comment'
|
|
|
7
7
|
import { ListData, CommentData, NotifyData, ApiVersionData } from '~/types/artalk-data'
|
|
8
8
|
import Pagination from './pagination'
|
|
9
9
|
import ReadMoreBtn from './read-more-btn'
|
|
10
|
+
import { backendMinVersion } from '../../package.json'
|
|
10
11
|
|
|
11
12
|
export default class ListLite extends Component {
|
|
12
13
|
protected $commentsWrap: HTMLElement
|
|
@@ -117,7 +118,9 @@ export default class ListLite extends Component {
|
|
|
117
118
|
this.data = data
|
|
118
119
|
|
|
119
120
|
// 版本检测
|
|
120
|
-
|
|
121
|
+
const feMinVersion = data.api_version?.fe_min_version || '0.0.0'
|
|
122
|
+
if (this.ctx.conf.versionCheck && this.versionCheck('前端', feMinVersion, ARTALK_VERSION)) return
|
|
123
|
+
if (this.ctx.conf.versionCheck && this.versionCheck('后端', backendMinVersion, data.api_version?.version)) return
|
|
121
124
|
|
|
122
125
|
// 图片上传功能
|
|
123
126
|
if (data.conf && typeof data.conf.img_upload === "boolean") {
|
|
@@ -472,26 +475,25 @@ export default class ListLite extends Component {
|
|
|
472
475
|
}
|
|
473
476
|
}
|
|
474
477
|
|
|
475
|
-
/**
|
|
476
|
-
public versionCheck(
|
|
477
|
-
const
|
|
478
|
-
const needUpdate = Utils.versionCompare(needVersion, ARTALK_VERSION) === 1
|
|
478
|
+
/** 版本检测 */
|
|
479
|
+
public versionCheck(name: '前端'|'后端', needVersion: string, curtVersion: string): boolean {
|
|
480
|
+
const needUpdate = Utils.versionCompare(needVersion, curtVersion) === 1
|
|
479
481
|
if (needUpdate) {
|
|
480
482
|
// 需要更新
|
|
481
|
-
const errEl = Utils.createElement(`<div
|
|
482
|
-
+
|
|
483
|
+
const errEl = Utils.createElement(`<div>Artalk ${name}版本已过时,请更新以获得完整体验<br/>`
|
|
484
|
+
+ `如果你是管理员,请前往 “<a href="https://artalk.js.org/" target="_blank">官方文档</a>” 获得帮助`
|
|
483
485
|
+ `<br/><br/>`
|
|
484
|
-
+ `<span style="color: var(--at-color-meta);"
|
|
486
|
+
+ `<span style="color: var(--at-color-meta);">当前${name}版本 ${curtVersion},需求版本 >= ${needVersion}</span><br/><br/>`
|
|
485
487
|
+ `</div>`)
|
|
486
488
|
const ignoreBtn = Utils.createElement('<span style="cursor:pointer;">忽略</span>')
|
|
487
489
|
ignoreBtn.onclick = () => {
|
|
488
|
-
Ui.setError(this.
|
|
490
|
+
Ui.setError(this.$el.parentElement!, null)
|
|
489
491
|
this.ctx.conf.versionCheck = false
|
|
490
492
|
this.ctx.trigger('conf-updated')
|
|
491
493
|
this.fetchComments(0)
|
|
492
494
|
}
|
|
493
495
|
errEl.append(ignoreBtn)
|
|
494
|
-
Ui.setError(this.
|
|
496
|
+
Ui.setError(this.$el.parentElement!, errEl, '<span class="atk-warn-title">Artalk Warn</span>')
|
|
495
497
|
}
|
|
496
498
|
|
|
497
499
|
return needUpdate
|
|
@@ -6,6 +6,7 @@ import * as Utils from '@/lib/utils'
|
|
|
6
6
|
import SidebarHTML from './html/sidebar-layer.html?raw'
|
|
7
7
|
import * as Ui from '@/lib/ui'
|
|
8
8
|
import Layer from './layer'
|
|
9
|
+
import Api from '../api'
|
|
9
10
|
|
|
10
11
|
export default class SidebarLayer extends Component {
|
|
11
12
|
public layer?: Layer
|
|
@@ -60,6 +61,26 @@ export default class SidebarLayer extends Component {
|
|
|
60
61
|
|
|
61
62
|
// 第一次加载
|
|
62
63
|
if (this.firstShow) {
|
|
64
|
+
// 管理员身份验证
|
|
65
|
+
if (this.ctx.user.data.isAdmin) {
|
|
66
|
+
const resp = await new Api(this.ctx).loginStatus()
|
|
67
|
+
if (!resp.is_login) {
|
|
68
|
+
await (new Promise<any>((resolve, reject) => {
|
|
69
|
+
this.ctx.trigger('checker-admin', {
|
|
70
|
+
onSuccess: () => {
|
|
71
|
+
resolve(null)
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
this.show()
|
|
74
|
+
}, 500)
|
|
75
|
+
},
|
|
76
|
+
onCancel: () => {
|
|
77
|
+
this.layer?.hide()
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
}))
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
63
84
|
this.$iframeWrap.innerHTML = ''
|
|
64
85
|
this.$iframe = Utils.createElement<HTMLIFrameElement>('<iframe></iframe>')
|
|
65
86
|
|
|
@@ -72,6 +93,7 @@ export default class SidebarLayer extends Component {
|
|
|
72
93
|
+ `?pageKey=${encodeURIComponent(this.conf.pageKey)}`
|
|
73
94
|
+ `&site=${encodeURIComponent(this.conf.site || '')}`
|
|
74
95
|
+ `&user=${userData}`
|
|
96
|
+
+ `&time=${+new Date()}`
|
|
75
97
|
+ `&referer=${encodeURIComponent(`${location.protocol}//${location.host}${location.pathname}`)}`
|
|
76
98
|
+ `${((this.conf.darkMode) ? `&darkMode=1` : ``)}`)
|
|
77
99
|
|
|
@@ -5,7 +5,7 @@ import { Checker } from '.'
|
|
|
5
5
|
const AdminChecker: Checker = {
|
|
6
6
|
inputType: 'password',
|
|
7
7
|
|
|
8
|
-
request(that, inputVal) {
|
|
8
|
+
request(that, ctx, inputVal) {
|
|
9
9
|
const data = {
|
|
10
10
|
name: that.ctx.user.data.nick,
|
|
11
11
|
email: that.ctx.user.data.email,
|
|
@@ -15,11 +15,11 @@ const AdminChecker: Checker = {
|
|
|
15
15
|
return new Api(that.ctx).login(data.name, data.email, data.password)
|
|
16
16
|
},
|
|
17
17
|
|
|
18
|
-
body() {
|
|
18
|
+
body(that, ctx) {
|
|
19
19
|
return Utils.createElement('<span>敲入密码来验证管理员身份:</span>')
|
|
20
20
|
},
|
|
21
21
|
|
|
22
|
-
onSuccess(that, userToken, inputVal, formEl) {
|
|
22
|
+
onSuccess(that, ctx, userToken, inputVal, formEl) {
|
|
23
23
|
that.ctx.user.data.isAdmin = true
|
|
24
24
|
that.ctx.user.data.token = userToken
|
|
25
25
|
that.ctx.user.save()
|
|
@@ -27,7 +27,7 @@ const AdminChecker: Checker = {
|
|
|
27
27
|
that.ctx.trigger('list-reload')
|
|
28
28
|
},
|
|
29
29
|
|
|
30
|
-
onError(that, err, inputVal, formEl) {}
|
|
30
|
+
onError(that, ctx, err, inputVal, formEl) {}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export default AdminChecker
|
|
@@ -1,15 +1,61 @@
|
|
|
1
1
|
import Api from '@/api'
|
|
2
2
|
import * as Utils from '../utils'
|
|
3
|
+
import * as Ui from '../ui'
|
|
3
4
|
import { Checker } from '.'
|
|
4
5
|
|
|
5
6
|
const CaptchaChecker: Checker = {
|
|
6
|
-
request(that, inputVal) {
|
|
7
|
+
request(that, ctx, inputVal) {
|
|
7
8
|
return new Api(that.ctx).captchaCheck(inputVal)
|
|
8
9
|
},
|
|
9
10
|
|
|
10
|
-
body(that) {
|
|
11
|
+
body(that, ctx) {
|
|
12
|
+
// Iframe 验证方式
|
|
13
|
+
if (that.captchaConf.iframe) {
|
|
14
|
+
const $iframeWrap = Utils.createElement(`<div class="atk-checker-iframe-wrap"></div>`)
|
|
15
|
+
const $iframe = Utils.createElement<HTMLIFrameElement>(`<iframe class="atk-fade-in"></iframe>`)
|
|
16
|
+
$iframe.style.display = 'none'
|
|
17
|
+
Ui.showLoading($iframeWrap, { transparentBg: true })
|
|
18
|
+
$iframe.src = `${that.ctx.conf.server}/captcha/get`
|
|
19
|
+
$iframe.onload = () => {
|
|
20
|
+
$iframe.style.display = ''
|
|
21
|
+
Ui.hideLoading($iframeWrap)
|
|
22
|
+
}
|
|
23
|
+
$iframeWrap.append($iframe)
|
|
24
|
+
|
|
25
|
+
const $closeBtn = Utils.createElement(`<div class="atk-close-btn"><i class="atk-icon atk-icon-close"></i></div>`)
|
|
26
|
+
$iframeWrap.append($closeBtn)
|
|
27
|
+
|
|
28
|
+
ctx.hideInteractInput()
|
|
29
|
+
|
|
30
|
+
// 轮询状态
|
|
31
|
+
let stop = false // 打断
|
|
32
|
+
const sleep = (ms: number) => new Promise((resolve) => { window.setTimeout(() => { resolve(null) }, ms) })
|
|
33
|
+
;(async function queryStatus() {
|
|
34
|
+
await sleep(1000)
|
|
35
|
+
if (stop) return
|
|
36
|
+
let isPass = false
|
|
37
|
+
try {
|
|
38
|
+
const resp = await new Api(that.ctx).captchaStatus()
|
|
39
|
+
isPass = resp.is_pass
|
|
40
|
+
} catch { isPass = false }
|
|
41
|
+
if (isPass) {
|
|
42
|
+
ctx.triggerSuccess()
|
|
43
|
+
} else {
|
|
44
|
+
queryStatus()
|
|
45
|
+
}
|
|
46
|
+
})()
|
|
47
|
+
|
|
48
|
+
$closeBtn.onclick = () => {
|
|
49
|
+
stop = true
|
|
50
|
+
ctx.cancel()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return $iframeWrap
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 图片验证方式
|
|
11
57
|
const elem = Utils.createElement(
|
|
12
|
-
`<span><img class="atk-captcha-img" src="${that.
|
|
58
|
+
`<span><img class="atk-captcha-img" src="${that.captchaConf.imgData || ''}" alt="验证码">敲入验证码继续:</span>`
|
|
13
59
|
);
|
|
14
60
|
|
|
15
61
|
// 刷新验证码
|
|
@@ -26,11 +72,11 @@ const CaptchaChecker: Checker = {
|
|
|
26
72
|
return elem
|
|
27
73
|
},
|
|
28
74
|
|
|
29
|
-
onSuccess(that, data, inputVal, formEl) {
|
|
30
|
-
that.
|
|
75
|
+
onSuccess(that, ctx, data, inputVal, formEl) {
|
|
76
|
+
that.captchaConf.val = inputVal
|
|
31
77
|
},
|
|
32
78
|
|
|
33
|
-
onError(that, err, inputVal, formEl) {
|
|
79
|
+
onError(that, ctx, err, inputVal, formEl) {
|
|
34
80
|
formEl.querySelector<HTMLElement>('.atk-captcha-img')!.click() // 刷新验证码
|
|
35
81
|
}
|
|
36
82
|
}
|
package/src/lib/checker/index.ts
CHANGED
|
@@ -14,8 +14,7 @@ export default class CheckerLauncher {
|
|
|
14
14
|
public ctx: Context
|
|
15
15
|
|
|
16
16
|
public launched: Checker[] = []
|
|
17
|
-
public
|
|
18
|
-
public submitCaptchaImgData?: string
|
|
17
|
+
public captchaConf: { val?: string, imgData?: string, iframe?: string } = {}
|
|
19
18
|
|
|
20
19
|
constructor(ctx: Context) {
|
|
21
20
|
this.ctx = ctx
|
|
@@ -26,9 +25,9 @@ export default class CheckerLauncher {
|
|
|
26
25
|
/** 初始化事件绑定 */
|
|
27
26
|
public initEventBind() {
|
|
28
27
|
this.ctx.on('checker-captcha', (conf) => {
|
|
29
|
-
if (conf.imgData)
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
if (conf.imgData) this.captchaConf.imgData = conf.imgData
|
|
29
|
+
if (conf.iframe) this.captchaConf.iframe = conf.iframe
|
|
30
|
+
|
|
32
31
|
this.fire(CaptchaChecker, conf)
|
|
33
32
|
})
|
|
34
33
|
|
|
@@ -41,20 +40,42 @@ export default class CheckerLauncher {
|
|
|
41
40
|
if (this.launched.includes(checker)) return // 阻止同时 fire 相同的 checker
|
|
42
41
|
this.launched.push(checker)
|
|
43
42
|
|
|
43
|
+
// 创建层
|
|
44
44
|
const layer = new Layer(this.ctx, `checker-${new Date().getTime()}`)
|
|
45
45
|
layer.setMaskClickHide(false)
|
|
46
46
|
layer.show()
|
|
47
47
|
|
|
48
|
+
// Checker 的上下文
|
|
49
|
+
let hideInteractInput = false
|
|
50
|
+
const checkerCtx: CheckerCtx = {
|
|
51
|
+
getLayer: () => layer,
|
|
52
|
+
hideInteractInput: () => {
|
|
53
|
+
hideInteractInput = true
|
|
54
|
+
},
|
|
55
|
+
triggerSuccess: () => {
|
|
56
|
+
this.close(checker, layer)
|
|
57
|
+
if (checker.onSuccess) checker.onSuccess(this, checkerCtx, "", "", formEl)
|
|
58
|
+
if (payload.onSuccess) payload.onSuccess("", dialog.$el)
|
|
59
|
+
},
|
|
60
|
+
cancel: () => {
|
|
61
|
+
this.close(checker, layer)
|
|
62
|
+
if (payload.onCancel) payload.onCancel()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 创建表单
|
|
48
67
|
const formEl = Utils.createElement()
|
|
49
|
-
formEl.appendChild(checker.body(this))
|
|
68
|
+
formEl.appendChild(checker.body(this, checkerCtx))
|
|
50
69
|
|
|
51
|
-
|
|
70
|
+
// 输入框
|
|
71
|
+
const $input = Utils.createElement<HTMLInputElement>(
|
|
52
72
|
`<input id="check" type="${checker.inputType || 'text'}" autocomplete="off" required placeholder="">`
|
|
53
73
|
)
|
|
54
|
-
formEl.appendChild(input)
|
|
55
|
-
setTimeout(() => input.focus(), 80) // 延迟 Focus
|
|
74
|
+
formEl.appendChild($input)
|
|
75
|
+
setTimeout(() => $input.focus(), 80) // 延迟 Focus
|
|
56
76
|
|
|
57
|
-
|
|
77
|
+
// 绑定键盘事件
|
|
78
|
+
$input.onkeyup = (evt) => {
|
|
58
79
|
if (evt.key === 'Enter' || evt.keyCode === 13) {
|
|
59
80
|
// 按下回车键
|
|
60
81
|
evt.preventDefault()
|
|
@@ -70,7 +91,9 @@ export default class CheckerLauncher {
|
|
|
70
91
|
|
|
71
92
|
// 确认按钮
|
|
72
93
|
dialog.setYes((btnEl) => {
|
|
73
|
-
const inputVal = input.value.trim()
|
|
94
|
+
const inputVal = $input.value.trim()
|
|
95
|
+
|
|
96
|
+
// 按钮操作
|
|
74
97
|
if (!btnTextOrg) btnTextOrg = btnEl.innerText
|
|
75
98
|
const btnTextSet = (btnText: string) => {
|
|
76
99
|
btnEl.innerText = btnText
|
|
@@ -83,21 +106,25 @@ export default class CheckerLauncher {
|
|
|
83
106
|
|
|
84
107
|
btnEl.innerText = '加载中...'
|
|
85
108
|
|
|
109
|
+
// 发送请求
|
|
86
110
|
checker
|
|
87
|
-
.request(this, inputVal)
|
|
111
|
+
.request(this, checkerCtx, inputVal)
|
|
88
112
|
.then((data) => {
|
|
89
113
|
// 请求成功
|
|
90
|
-
this.
|
|
91
|
-
|
|
114
|
+
this.close(checker, layer)
|
|
115
|
+
|
|
116
|
+
if (checker.onSuccess) checker.onSuccess(this, checkerCtx, data, inputVal, formEl)
|
|
92
117
|
if (payload.onSuccess) payload.onSuccess(inputVal, dialog.$el)
|
|
93
118
|
})
|
|
94
119
|
.catch((err) => {
|
|
95
120
|
// 请求失败
|
|
96
121
|
btnTextSet(String(err.msg || String(err)))
|
|
97
|
-
if (checker.onError) checker.onError(this, err, inputVal, formEl)
|
|
98
122
|
|
|
123
|
+
if (checker.onError) checker.onError(this, checkerCtx, err, inputVal, formEl)
|
|
124
|
+
|
|
125
|
+
// 错误显示 3s 后恢复按钮
|
|
99
126
|
const tf = setTimeout(() => btnTextRestore(), 3000)
|
|
100
|
-
input.onfocus = () => {
|
|
127
|
+
$input.onfocus = () => {
|
|
101
128
|
btnTextRestore()
|
|
102
129
|
clearTimeout(tf)
|
|
103
130
|
}
|
|
@@ -108,17 +135,25 @@ export default class CheckerLauncher {
|
|
|
108
135
|
|
|
109
136
|
// 取消按钮
|
|
110
137
|
dialog.setNo(() => {
|
|
111
|
-
this.
|
|
138
|
+
this.close(checker, layer)
|
|
112
139
|
if (payload.onCancel) payload.onCancel()
|
|
113
140
|
return false
|
|
114
141
|
})
|
|
115
142
|
|
|
143
|
+
if (hideInteractInput) {
|
|
144
|
+
$input.style.display = 'none'
|
|
145
|
+
dialog.$el.querySelector<HTMLElement>('.atk-layer-dialog-actions')!.style.display = 'none'
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 层装载 dialog 元素
|
|
116
149
|
layer.getEl().append(dialog.$el)
|
|
117
150
|
|
|
118
|
-
|
|
151
|
+
// onMount 回调
|
|
152
|
+
if (payload.onMount) payload.onMount(dialog.$el)
|
|
119
153
|
}
|
|
120
154
|
|
|
121
|
-
|
|
155
|
+
// 关闭 checker 对话框
|
|
156
|
+
private close(checker: Checker, layer: Layer) {
|
|
122
157
|
layer.disposeNow()
|
|
123
158
|
this.launched = this.launched.filter(c => c !== checker)
|
|
124
159
|
}
|
|
@@ -127,8 +162,15 @@ export default class CheckerLauncher {
|
|
|
127
162
|
export interface Checker {
|
|
128
163
|
el?: HTMLElement
|
|
129
164
|
inputType?: 'password' | 'text'
|
|
130
|
-
body: (launcher: CheckerLauncher) => HTMLElement
|
|
131
|
-
request: (launcher: CheckerLauncher, inputVal: string) => Promise<string>
|
|
132
|
-
onSuccess?: (launcher: CheckerLauncher, data: string, inputVal: string, formEl: HTMLElement) => void
|
|
133
|
-
onError?: (launcher: CheckerLauncher, err: any, inputVal: string, formEl: HTMLElement) => void
|
|
165
|
+
body: (launcher: CheckerLauncher, ctx: CheckerCtx) => HTMLElement
|
|
166
|
+
request: (launcher: CheckerLauncher, ctx: CheckerCtx, inputVal: string) => Promise<string>
|
|
167
|
+
onSuccess?: (launcher: CheckerLauncher, ctx: CheckerCtx, data: string, inputVal: string, formEl: HTMLElement) => void
|
|
168
|
+
onError?: (launcher: CheckerLauncher, ctx: CheckerCtx, err: any, inputVal: string, formEl: HTMLElement) => void
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface CheckerCtx {
|
|
172
|
+
getLayer(): Layer
|
|
173
|
+
hideInteractInput(): void
|
|
174
|
+
triggerSuccess(): void
|
|
175
|
+
cancel(): void
|
|
134
176
|
}
|
package/src/lib/ui.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as Utils from './utils'
|
|
|
2
2
|
import Context from '../context'
|
|
3
3
|
|
|
4
4
|
/** 显示加载 */
|
|
5
|
-
export function showLoading(parentElem: HTMLElement|Context) {
|
|
5
|
+
export function showLoading(parentElem: HTMLElement|Context, conf?: { transparentBg?: boolean }) {
|
|
6
6
|
if (parentElem instanceof Context) parentElem = parentElem.$root
|
|
7
7
|
|
|
8
8
|
let $loading = parentElem.querySelector<HTMLElement>('.atk-loading')
|
|
@@ -13,6 +13,7 @@ export function showLoading(parentElem: HTMLElement|Context) {
|
|
|
13
13
|
<svg viewBox="25 25 50 50"><circle cx="50" cy="50" r="20" fill="none" stroke-width="2" stroke-miterlimit="10"></circle></svg>
|
|
14
14
|
</div>
|
|
15
15
|
</div>`)
|
|
16
|
+
if (conf?.transparentBg) $loading.style.background = 'transparent'
|
|
16
17
|
parentElem.appendChild($loading)
|
|
17
18
|
}
|
|
18
19
|
$loading.style.display = ''
|
package/src/style/main.less
CHANGED
|
@@ -303,6 +303,41 @@ i.atk-icon {
|
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
|
+
|
|
307
|
+
.atk-checker-iframe-wrap {
|
|
308
|
+
position: fixed;
|
|
309
|
+
z-index: 999998;
|
|
310
|
+
left: 0;
|
|
311
|
+
top: 0;
|
|
312
|
+
height: 100vh;
|
|
313
|
+
width: 100vw;
|
|
314
|
+
|
|
315
|
+
& > iframe {
|
|
316
|
+
width: 100%;
|
|
317
|
+
height: 100%;
|
|
318
|
+
border: 0;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.atk-close-btn {
|
|
322
|
+
z-index: 999999;
|
|
323
|
+
position: fixed;
|
|
324
|
+
top: 20px;
|
|
325
|
+
right: 20px;
|
|
326
|
+
display: flex;
|
|
327
|
+
flex-direction: column;
|
|
328
|
+
width: 50px;
|
|
329
|
+
height: 50px;
|
|
330
|
+
align-items: center;
|
|
331
|
+
place-content: center;
|
|
332
|
+
cursor: pointer;
|
|
333
|
+
user-select: none;
|
|
334
|
+
margin-left: 10px;
|
|
335
|
+
|
|
336
|
+
&:hover .atk-icon-close {
|
|
337
|
+
background-color: rgba(232, 17, 35, 0.9);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
306
341
|
}
|
|
307
342
|
}
|
|
308
343
|
|
package/tsconfig.json
CHANGED
|
@@ -1,28 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
2
3
|
"compilerOptions": {
|
|
3
4
|
"baseUrl": ".",
|
|
4
5
|
"outDir": "dist",
|
|
5
|
-
"target": "esnext",
|
|
6
|
-
"useDefineForClassFields": true,
|
|
7
|
-
"module": "esnext",
|
|
8
|
-
"lib": ["esnext", "dom"],
|
|
9
|
-
"moduleResolution": "node",
|
|
10
|
-
"strict": true,
|
|
11
|
-
"sourceMap": true,
|
|
12
|
-
"experimentalDecorators": true,
|
|
13
|
-
"resolveJsonModule": true,
|
|
14
|
-
"esModuleInterop": true,
|
|
15
|
-
"noEmit": true,
|
|
16
|
-
"noImplicitAny": false,
|
|
17
|
-
"allowSyntheticDefaultImports": true,
|
|
18
|
-
"noUnusedLocals": true,
|
|
19
|
-
"noUnusedParameters": true,
|
|
20
|
-
"noImplicitReturns": true,
|
|
21
6
|
"paths": {
|
|
22
7
|
"~/*": ["./*"],
|
|
23
8
|
"@/*": ["./src/*"]
|
|
24
|
-
}
|
|
25
|
-
"types": ["vite/client"]
|
|
9
|
+
}
|
|
26
10
|
},
|
|
27
11
|
"include": ["./src", "./types"]
|
|
28
12
|
}
|