artalk 2.1.7 → 2.1.8
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/dist/Artalk.css +1 -1
- package/dist/Artalk.es.js +103 -5
- package/dist/Artalk.iife.js +1 -1
- package/dist/Artalk.js +1 -1
- package/package.json +1 -1
- package/src/api/index.ts +23 -0
- package/src/artalk.ts +1 -0
- package/src/components/editor.ts +90 -1
- package/src/components/list-lite.ts +7 -0
- package/src/components/list.ts +1 -1
- package/src/defaults.ts +1 -0
- package/src/style/list.less +3 -3
- package/types/artalk-config.d.ts +3 -0
- package/types/artalk-data.d.ts +3 -0
- package/types/event.d.ts +1 -0
package/package.json
CHANGED
package/src/api/index.ts
CHANGED
|
@@ -264,6 +264,29 @@ export default class Api {
|
|
|
264
264
|
return p.pv as number
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
+
/** 图片上传 */
|
|
268
|
+
public async imgUpload(file: File) {
|
|
269
|
+
const params: any = {
|
|
270
|
+
name: this.ctx.user.data.nick,
|
|
271
|
+
email: this.ctx.user.data.email,
|
|
272
|
+
page_key: this.ctx.conf.pageKey,
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
|
|
276
|
+
|
|
277
|
+
const form = ToFormData(params)
|
|
278
|
+
form.set('file', file)
|
|
279
|
+
|
|
280
|
+
const init: RequestInit = {
|
|
281
|
+
method: 'POST',
|
|
282
|
+
body: form
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const json = await Fetch(this.ctx, `${this.baseURL}/img-upload`, init)
|
|
286
|
+
return ((json.data || {}) as any) as { img_file: string, img_url: string }
|
|
287
|
+
|
|
288
|
+
}
|
|
289
|
+
|
|
267
290
|
// ============================
|
|
268
291
|
// 验证码
|
|
269
292
|
// ============================
|
package/src/artalk.ts
CHANGED
package/src/components/editor.ts
CHANGED
|
@@ -21,6 +21,7 @@ export default class Editor extends Component {
|
|
|
21
21
|
public $plugWrap: HTMLElement
|
|
22
22
|
public $bottom: HTMLElement
|
|
23
23
|
public $plugBtnWrap: HTMLElement
|
|
24
|
+
public $imgUploadBtn?: HTMLElement
|
|
24
25
|
public $submitBtn: HTMLButtonElement
|
|
25
26
|
public $notifyWrap: HTMLElement
|
|
26
27
|
|
|
@@ -63,6 +64,7 @@ export default class Editor extends Component {
|
|
|
63
64
|
this.ctx.on('editor-notify', (f) => (this.showNotify(f.msg, f.type)))
|
|
64
65
|
this.ctx.on('editor-travel', ($el) => (this.travel($el)))
|
|
65
66
|
this.ctx.on('editor-travel-back', () => (this.travelBack()))
|
|
67
|
+
this.ctx.on('conf-updated', () => (this.refreshUploadBtn()))
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
initLocalStorage () {
|
|
@@ -200,7 +202,7 @@ export default class Editor extends Component {
|
|
|
200
202
|
// 依次实例化 plug
|
|
201
203
|
this.LOADABLE_PLUG_LIST.forEach((PlugObj) => {
|
|
202
204
|
// 切换按钮
|
|
203
|
-
const btnElem = Utils.createElement(`<span class="atk-plug-btn">${PlugObj.BtnHTML}</span>`)
|
|
205
|
+
const btnElem = Utils.createElement(`<span class="atk-plug-btn" data-plug-name="${PlugObj.Name}">${PlugObj.BtnHTML}</span>`)
|
|
204
206
|
this.$plugBtnWrap.appendChild(btnElem)
|
|
205
207
|
btnElem.addEventListener('click', () => {
|
|
206
208
|
let plug = this.plugList[PlugObj.Name]
|
|
@@ -244,6 +246,8 @@ export default class Editor extends Component {
|
|
|
244
246
|
btnElem.classList.add('active')
|
|
245
247
|
})
|
|
246
248
|
})
|
|
249
|
+
|
|
250
|
+
this.initImgUploadBtn()
|
|
247
251
|
}
|
|
248
252
|
|
|
249
253
|
/** 关闭编辑器插件 */
|
|
@@ -253,6 +257,91 @@ export default class Editor extends Component {
|
|
|
253
257
|
this.openedPlugName = null
|
|
254
258
|
}
|
|
255
259
|
|
|
260
|
+
/** 允许的图片格式 */
|
|
261
|
+
allowImgExts = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'svg', 'webp']
|
|
262
|
+
|
|
263
|
+
/** 初始化图片上传按钮 */
|
|
264
|
+
initImgUploadBtn() {
|
|
265
|
+
this.$imgUploadBtn = Utils.createElement(`<span class="atk-plug-btn">图片</span>`)
|
|
266
|
+
this.$plugBtnWrap.querySelector('[data-plug-name="preview"]')!.before(this.$imgUploadBtn) // 显示在预览图标之前
|
|
267
|
+
|
|
268
|
+
this.$imgUploadBtn.onclick = () => {
|
|
269
|
+
// 选择图片
|
|
270
|
+
const $input = document.createElement('input')
|
|
271
|
+
$input.type = 'file'
|
|
272
|
+
$input.accept = this.allowImgExts.map(o => `.${o}`).join(',')
|
|
273
|
+
$input.onchange = () => {
|
|
274
|
+
(async () => { // 解决阻塞 UI 问题
|
|
275
|
+
if (!$input.files || $input.files.length === 0) return
|
|
276
|
+
const file = $input.files[0]
|
|
277
|
+
this.uploadImg(file)
|
|
278
|
+
})()
|
|
279
|
+
}
|
|
280
|
+
$input.click() // 显示选择图片对话框
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// @link https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop
|
|
284
|
+
// 阻止浏览器的默认释放行为
|
|
285
|
+
this.$textarea.addEventListener('dragover', (evt) => {
|
|
286
|
+
evt.stopPropagation()
|
|
287
|
+
evt.preventDefault()
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
this.$textarea.addEventListener('drop', (evt) => {
|
|
291
|
+
if (evt.dataTransfer?.files) {
|
|
292
|
+
evt.preventDefault()
|
|
293
|
+
for (let i = 0; i < evt.dataTransfer.files.length; i++) {
|
|
294
|
+
const file = evt.dataTransfer.files[i]
|
|
295
|
+
this.uploadImg(file)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** 刷新图片上传按钮 */
|
|
302
|
+
refreshUploadBtn() {
|
|
303
|
+
if (!this.$imgUploadBtn) return
|
|
304
|
+
|
|
305
|
+
if (!this.ctx.conf.imgUpload) {
|
|
306
|
+
this.$imgUploadBtn.setAttribute('atk-only-admin-show', '')
|
|
307
|
+
this.ctx.trigger('check-admin-show-el')
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async uploadImg(file: File) {
|
|
312
|
+
const fileExt = /[^.]+$/.exec(file.name)
|
|
313
|
+
if (!fileExt || !this.allowImgExts.includes(fileExt[0])) return
|
|
314
|
+
|
|
315
|
+
// 插入占位加载文字
|
|
316
|
+
const uploadPlaceholderTxt = `\n`
|
|
317
|
+
this.insertContent(uploadPlaceholderTxt)
|
|
318
|
+
|
|
319
|
+
// 上传图片
|
|
320
|
+
let resp: any
|
|
321
|
+
try {
|
|
322
|
+
resp = await new Api(this.ctx).imgUpload(file)
|
|
323
|
+
} catch (err: any) {
|
|
324
|
+
console.error(err)
|
|
325
|
+
this.showNotify(`图片上传失败,${err.msg}`, 'e')
|
|
326
|
+
}
|
|
327
|
+
if (!!resp && resp.img_url) {
|
|
328
|
+
let imgURL = resp.img_url as string
|
|
329
|
+
if (imgURL.startsWith('.') || (imgURL.startsWith('/') && !imgURL.startsWith('//'))) {
|
|
330
|
+
// 若为相对路径,加上 artalk server
|
|
331
|
+
imgURL = `${this.ctx.conf.server.replace(/\/api\/?$/, '')}/${imgURL.replace(/^\//, '')}`
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// 上传成功插入图片
|
|
335
|
+
this.$textarea.value = this.$textarea.value.replace(uploadPlaceholderTxt, `\n`)
|
|
336
|
+
} else {
|
|
337
|
+
// 上传失败删除加载文字
|
|
338
|
+
this.$textarea.value = this.$textarea.value.replace(uploadPlaceholderTxt, '')
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 更新内容保存到 localStorage
|
|
342
|
+
this.saveContent()
|
|
343
|
+
}
|
|
344
|
+
|
|
256
345
|
insertContent (val: string) {
|
|
257
346
|
if ((document as any).selection) {
|
|
258
347
|
this.$textarea.focus();
|
|
@@ -119,6 +119,11 @@ export default class ListLite extends Component {
|
|
|
119
119
|
// 版本检测
|
|
120
120
|
if (this.ctx.conf.versionCheck && this.versionCheck(data.api_version)) return
|
|
121
121
|
|
|
122
|
+
// 图片上传功能
|
|
123
|
+
if (data.conf && typeof data.conf.img_upload === "boolean") {
|
|
124
|
+
this.ctx.conf.imgUpload = data.conf.img_upload
|
|
125
|
+
}
|
|
126
|
+
|
|
122
127
|
// 导入数据
|
|
123
128
|
this.importComments(data.comments)
|
|
124
129
|
|
|
@@ -130,6 +135,7 @@ export default class ListLite extends Component {
|
|
|
130
135
|
|
|
131
136
|
this.ctx.trigger('unread-update', { notifies: data.unread || [] })
|
|
132
137
|
this.ctx.trigger('comments-loaded')
|
|
138
|
+
this.ctx.trigger('conf-updated')
|
|
133
139
|
|
|
134
140
|
if (this.onAfterLoad) this.onAfterLoad(data)
|
|
135
141
|
}
|
|
@@ -481,6 +487,7 @@ export default class ListLite extends Component {
|
|
|
481
487
|
ignoreBtn.onclick = () => {
|
|
482
488
|
Ui.setError(this.ctx, null)
|
|
483
489
|
this.ctx.conf.versionCheck = false
|
|
490
|
+
this.ctx.trigger('conf-updated')
|
|
484
491
|
this.fetchComments(0)
|
|
485
492
|
}
|
|
486
493
|
errEl.append(ignoreBtn)
|
package/src/components/list.ts
CHANGED
|
@@ -184,7 +184,7 @@ export default class List extends ListLite {
|
|
|
184
184
|
this.$commentCount.classList.add('atk-dropdown-wrap')
|
|
185
185
|
|
|
186
186
|
// 插入图标
|
|
187
|
-
this.$
|
|
187
|
+
this.$dropdownWrap.append(Utils.createElement(`<span class="atk-arrow-down-icon"></span>`))
|
|
188
188
|
|
|
189
189
|
const reloadUseParamsEditor = (func: (p: any) => void) => {
|
|
190
190
|
this.paramsEditor = (p) => { func(p) }
|
package/src/defaults.ts
CHANGED
package/src/style/list.less
CHANGED
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
.atk-arrow-down-icon {
|
|
20
20
|
cursor: pointer;
|
|
21
21
|
vertical-align: middle;
|
|
22
|
-
border-top:
|
|
23
|
-
border-left:
|
|
24
|
-
border-right:
|
|
22
|
+
border-top: 5px solid var(--at-color-grey);
|
|
23
|
+
border-left: 3px solid transparent;
|
|
24
|
+
border-right: 3px solid transparent;
|
|
25
25
|
margin-top: -1px;
|
|
26
26
|
margin-left: 0.4rem;
|
|
27
27
|
display: inline-block;
|
package/types/artalk-config.d.ts
CHANGED
package/types/artalk-data.d.ts
CHANGED
package/types/event.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface EventPayloadMap {
|
|
|
8
8
|
'editor-submit': undefined // 编辑器提交时
|
|
9
9
|
'editor-submitted': undefined // 编辑器提交后
|
|
10
10
|
'user-changed': LocalUser // 本地用户数据变更时
|
|
11
|
+
'conf-updated': undefined // Artalk 配置变更时
|
|
11
12
|
|
|
12
13
|
// List 操作(外部:不建议 listen,仅 trigger)
|
|
13
14
|
'list-reload': undefined // 重新加载 List
|