feat: stop Implicit submission (halo-dev/console#766)

<!--  Thanks for sending a pull request!  Here are some tips for you:
1. 如果这是你的第一次,请阅读我们的贡献指南:<https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>。
1. If this is your first time, please read our contributor guidelines: <https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>.
2. 请根据你解决问题的类型为 Pull Request 添加合适的标签。
2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request.
3. 请确保你已经添加并运行了适当的测试。
3. Ensure you have added or ran the appropriate tests for your PR.
-->

#### What type of PR is this?
/kind feature
/area console
<!--
添加其中一个类别:
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind feature
/kind optimization
适当添加其中一个或多个类别(可选):
Optionally add one or more of the following kinds if applicable:
/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

#### What this PR does / why we need it:
阻止表单的[隐式提交](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission)

>For the purpose of the previous paragraph, an element is a field that blocks implicit submission of a element if it is an element whose [form owner](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-owner) is that element and whose attribute is in one of the following states: [Text](https://html.spec.whatwg.org/multipage/input.html#text-(type=text)-state-and-search-state-(type=search)), [Search](https://html.spec.whatwg.org/multipage/input.html#text-(type=text)-state-and-search-state-(type=search)), [URL](https://html.spec.whatwg.org/multipage/input.html#url-state-(type=url)), [Telephone](https://html.spec.whatwg.org/multipage/input.html#telephone-state-(type=tel)), [Email](https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email)), [Password](https://html.spec.whatwg.org/multipage/input.html#password-state-(type=password)), [Date](https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date)), [Month](https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month)), [Week](https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week)), [Time](https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time)), [Local Date and Time](https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local)), [Number](https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number))

当form的子元素只有一个且tyep为 ` "text","search","url","email","password","date","month","week","time","datetime-local","number"`其中任一。 阻止键入`enter` 触发`submit` ,做法是在from表单上监听键盘事件,然后阻止。

#### Which issue(s) this PR fixes:

<!--
PR 合并时自动关闭 issue。
Automatically closes linked issue when PR is merged.
用法:`Fixes #<issue 号>`,或者 `Fixes (粘贴 issue 完整链接)`
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes https://github.com/halo-dev/halo/issues/2893

#### Screenshots:

<!--
如果此 PR 有 UI 的改动,最好截图说明这个 PR 的改动。
If there are UI changes to this PR, it is best to take a screenshot to illustrate the changes to this PR.
eg.
Before:
![screenshot-before](https://user-images.githubusercontent.com/screenshot.png)
After:
![screenshot-after](https://user-images.githubusercontent.com/screenshot.png)
-->

#### Special notes for your reviewer:

+ 进入[测试页面](https://formkit.com/playground?fkv=1.0.0-beta.12&fileTab=Playground.vue&files=JTVCJTdCJTIybmFtZSUyMiUzQSUyMlBsYXlncm91bmQudnVlJTIyJTJDJTIyZWRpdG9yJTIyJTNBJTIyJTNDc2NyaXB0JTIwc2V0dXAlM0UlNUNuaW1wb3J0JTIwJTdCcmVmJTdEJTIwZnJvbSUyMCd2dWUnJTNCJTVDbmNvbnN0JTIwdHlwZUxpc3QlMjAlM0QlMjByZWYoJTVCJ2F1dG9jb21wbGV0ZSclMkMlMjAnYnV0dG9uJyUyQyUyMCdjaGVja2JveCclMkMlMjAnY29sb3InJTJDJTIwJ2RhdGUnJTJDJTIwJ2RhdGV0aW1lLWxvY2FsJyUyQyUyMCdkcm9wZG93biclMkMlMjAnZW1haWwnJTJDJTIwJ2ZpbGUnJTJDJTIwJTIwJ2xpc3QnJTJDJTIwJ21vbnRoJyUyQyUyMCdudW1iZXInJTJDJTIwJ3Bhc3N3b3JkJyUyQyUyMCdyYWRpbyclMkMlMjAncmFuZ2UnJTJDJTIwJ3JhdGluZyclMkMlMjAncmVwZWF0ZXInJTJDJTIwJ3NlYXJjaCclMkMlMjAnc2VsZWN0JyUyQyUyMCdzdWJtaXQnJTJDJTIwJ3RhZ2xpc3QnJTJDJTIwJ3RleHQnJTJDJTIwJ3RleHRhcmVhJyUyQyUyMCd0aW1lJyUyQyUyMCd0b2dnbGUnJTJDJTIwJ3VybCclMkMlMjAnd2VlayclNUQpJTNCJTVDbmNvbnN0JTIwaW1wbGljaVN1Ym1pc3Npb25UeXBlJTNEJTIwcmVmKCU1Qid0ZXh0JyUyQyUyMCdzZWFyY2gnJTJDJTIwJ3VybCclMkMlMjAlMjAnZW1haWwnJTJDJTIwJ3Bhc3N3b3JkJyUyQyUyMCdkYXRlJyUyQyUyMCdtb250aCclMkMlMjAnd2VlayclMkMlMjAndGltZSclMkMlMjAnZGF0ZXRpbWUtbG9jYWwnJTIwJTJDJTIwJ251bWJlciclNUQpJTVDbiUzQyUyRnNjcmlwdCUzRSU1Q24lNUNuJTNDdGVtcGxhdGUlM0UlNUNuJTIwJTNDaDIlM0VJbXBsaWNpdCUyMHN1Ym1pc3Npb24lMjB0ZXN0JTNDJTJGaDIlM0UlNUNuJTIwJTIwJTIwJTNDZGl2JTIwdi1mb3IlM0QlNUMlMjJ0eXBlJTIwaW4lMjBpbXBsaWNpU3VibWlzc2lvblR5cGUlNUMlMjIlMjAlM0FrZXklM0QlNUMlMjJ0eXBlJTVDJTIyJTIwc3R5bGUlM0QlNUMlMjJwYWRkaW5nJTNBJTIwMjBweCUyMDBweCU1QyUyMiUzRSU1Q24lMjAlMjAlMjAlMjAlM0NGb3JtS2l0JTVDbiUyMCUyMCUyMCUyMHR5cGUlM0QlNUMlMjJmb3JtJTVDJTIyJTVDbiUyMCUyMCUyMCUyMCUzQWxhYmVsJTNEJTVDJTIyJ0Zvcm1LaXQnJTIwJTJCJTIwdHlwZSU1QyUyMiU1Q24lMjAlMjAlMjAlMjBoZWxwJTNEJTVDJTIyZWRpdCUyMG1lJTIwdG8lMjBnZXQlMjBzdGFydGVkJTVDJTIyJTVDbiUyMCUyMCUyMCUyMCU0MGtleWRvd24lM0QlNUMlMjJmbiU1QyUyMiU1Q24lMjAlMjAlMjAlMjAlM0FpZCUzRCU1QyUyMnR5cGUlNUMlMjIlNUNuJTIwJTIwJTNFJTVDbiUyMCUyMCUzQ3AlMjBzdHlsZSUzRCU1QyUyMmJvcmRlci10b3AlM0ElMjA1cHglMjBzb2xpZCUzQiU1QyUyMiUzRXRlc3QlMjBmb3JtJTYwcyUyMGFjdGlvbiUyMHdoZW4lMjB0b3VjaCUyMCdlbnRlciclMjBrZXkuJTIwY2hpbGQlNjBzdHlwZSUyMCUzQSUyMCU3QiU3QnR5cGUlN0QlN0QlMjAlM0MlMkZwJTNFJTVDbiUyMCUyMCUzQ0Zvcm1LaXQlNUNuJTIwJTIwJTIwJTIwJTNBdHlwZSUzRCU1QyUyMnR5cGUlNUMlMjIlNUNuJTIwJTIwJTIwJTIwJTNBbGFiZWwlM0QlNUMlMjInbXklMjB0eXBlJTIwaXMlMjAnJTJCJTIwdHlwZSU1QyUyMiU1Q24lMjAlMjAlMjAlMjBoZWxwJTNEJTVDJTIydG91Y2glMjBlbnRlciUyMGtleSU1QyUyMiU1Q24lMjAlMjAlMjAlMjAlM0F2YWx1ZSUzRCU1QyUyMnR5cGUlNUMlMjIlNUNuJTIwJTIwJTJGJTNFJTVDbiUyMCUyMCUzQyUyRkZvcm1LaXQlM0UlNUNuJTIwJTIwJTNDJTJGZGl2JTNFJTVDbiUyMCUzQ2gyJTNFLi4uJTIwZnJvbSUyMHRlc3QlMjAlM0MlMkZoMiUzRSU1Q24lMjAlMjAlM0NkaXYlMjB2LWZvciUzRCU1QyUyMnR5cGUlMjBpbiUyMHR5cGVMaXN0JTVDJTIyJTIwJTNBa2V5JTNEJTVDJTIydHlwZSU1QyUyMiUyMHN0eWxlJTNEJTVDJTIycGFkZGluZyUzQSUyMDIwcHglMjAwcHglNUMlMjIlMjAlM0UlNUNuJTIwJTIwJTIwJTIwJTNDRm9ybUtpdCU1Q24lMjAlMjAlMjAlMjB2LWlmJTNEJTVDJTIyIWltcGxpY2lTdWJtaXNzaW9uVHlwZS5pbmNsdWRlcyh0eXBlKSU1QyUyMiU1Q24lMjAlMjAlMjAlMjB0eXBlJTNEJTVDJTIyZm9ybSU1QyUyMiU1Q24lMjAlMjAlMjAlMjAlM0FsYWJlbCUzRCU1QyUyMidGb3JtS2l0JyUyMCUyQiUyMHR5cGUlNUMlMjIlNUNuJTIwJTIwJTIwJTIwaGVscCUzRCU1QyUyMmVkaXQlMjBtZSUyMHRvJTIwZ2V0JTIwc3RhcnRlZCU1QyUyMiU1Q24lMjAlMjAlMjAlMjAlNDBrZXlkb3duJTNEJTVDJTIyZm4lNUMlMjIlNUNuJTIwJTIwJTIwJTIwJTNBaWQlM0QlNUMlMjJ0eXBlJTVDJTIyJTVDbiUyMCUyMCUzRSU1Q24lMjAlMjAlM0NwJTIwc3R5bGUlM0QlNUMlMjJib3JkZXItdG9wJTNBJTIwNXB4JTIwc29saWQlM0IlNUMlMjIlM0V0ZXN0JTIwZm9ybSU2MHMlMjBhY3Rpb24lMjB3aGVuJTIwdG91Y2glMjAnZW50ZXInJTIwa2V5LiUyMGNoaWxkJTYwc3R5cGUlMjAlM0ElMjAlN0IlN0J0eXBlJTdEJTdEJTIwJTNDJTJGcCUzRSU1Q24lMjAlMjAlM0NGb3JtS2l0JTVDbiUyMCUyMCUyMCUyMCUzQXR5cGUlM0QlNUMlMjJ0eXBlJTVDJTIyJTVDbiUyMCUyMCUyMCUyMCUzQWxhYmVsJTNEJTVDJTIyJ215JTIwdHlwZSUyMGlzJTIwJyUyQiUyMHR5cGUlNUMlMjIlNUNuJTIwJTIwJTIwJTIwaGVscCUzRCU1QyUyMnRvdWNoJTIwZW50ZXIlMjBrZXklNUMlMjIlNUNuJTIwJTIwJTIwJTIwJTNBdmFsdWUlM0QlNUMlMjJ0eXBlJTVDJTIyJTVDbiUyMCUyMCUyRiUzRSU1Q24lMjAlMjAlM0MlMkZGb3JtS2l0JTNFJTVDbiUyMCUyMCUzQyUyRmRpdiUzRSU1Q24lNUNuJTNDJTJGdGVtcGxhdGUlM0UlNUNuJTVDbiUzQ3N0eWxlJTIwc2NvcGVkJTNFJTVDbiUyRiolNUNudmFuaWxsYSUyMENTUyUyMGNhbiUyMGdvJTIwaGVyZS4lNUNuS2VlcCUyMHN0eWxlcyUyMHNjb3BlZCUyMHRvJTIwYXZvaWQlMjBtdWx0aXBsZSUyMGZpbGVzJTVDbm92ZXJ3cml0aW5nJTIwZWFjaCUyMG90aGVyJTIwaW4lMjB0aGUlMjByZW5kZXIlMjBvdXRwdXQuJTVDbiolMkYlNUNuJTNDJTJGc3R5bGUlM0UlNUNuJTIyJTJDJTIyYWRkZWQlMjIlM0F0cnVlJTdEJTJDJTdCJTIybmFtZSUyMiUzQSUyMmZvcm1raXQuY29uZmlnLmpzJTIyJTJDJTIyZWRpdG9yJTIyJTNBJTIyJTJGKiUyMEluJTIwdGhpcyUyMGZpbGUlMkMlMjBleHBvcnQlMjB5b3VyJTIwZmluYWwlMjBjb25maWcuJTVDbkl0JTIwd2lsbCUyMGF1dG9tYXRpY2FsbHklMjBiZSUyMGluamVjdGVkJTIwaW50byUyMHRoZSUyMHBsYXlncm91bmQlMjBmb3IlMjB5b3UuJTVDbkltcG9ydHMlMjBmcm9tJTIwb3RoZXIlMjBwbGF5Z3JvdW5kJTIwZmlsZXMlMjBhcmUlMjBzdXBwb3J0ZWQuJTIwKiUyRiU1Q25pbXBvcnQlMjAlN0IlMjBvbk1vdW50ZWQlMjAlN0QlMjBmcm9tJTIwJTVDJTIydnVlJTVDJTIyJTNCJTVDbmNvbnN0JTIwaW1wbGljaVN1Ym1pc3Npb25UeXBlJTIwJTNEJTIwJTVCJTVDbiUyMCUyMCU1QyUyMnRleHQlNUMlMjIlMkMlNUNuJTIwJTIwJTVDJTIyc2VhcmNoJTVDJTIyJTJDJTVDbiUyMCUyMCU1QyUyMnVybCU1QyUyMiUyQyU1Q24lMjAlMjAlNUMlMjJlbWFpbCU1QyUyMiUyQyU1Q24lMjAlMjAlNUMlMjJwYXNzd29yZCU1QyUyMiUyQyU1Q24lMjAlMjAlNUMlMjJudW1iZXIlNUMlMjIlMkMlNUNuJTVEJTNCJTVDbmZ1bmN0aW9uJTIwaGFuZGxlS2V5ZG93bihlKSUyMCU3QiU1Q24lMjAlMjBpZiUyMCglNUNuJTIwJTIwJTIwJTIwZS5rZXklMjAlM0QlM0QlMjAlNUMlMjJFbnRlciU1QyUyMiUyMCUyNiUyNiU1Q24lMjAlMjAlMjAlMjBlLmFsdEtleSUyMCUzRCUzRCUyMGZhbHNlJTIwJTI2JTI2JTVDbiUyMCUyMCUyMCUyMGUuY3RybEtleSUyMCUzRCUzRCUyMGZhbHNlJTIwJTI2JTI2JTVDbiUyMCUyMCUyMCUyMGUubWV0YUtleSUyMCUzRCUzRCUyMGZhbHNlJTVDbiUyMCUyMCklMjAlN0IlNUNuJTJGJTJGJTIwb3BlbiUyMHdpbGwlMjBzdG9wJTIwc3VibWl0JTVDbiUyRiUyRiUyMCUyMGUuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKCklM0IlNUNuJTJGJTJGJTIwJTIwZS5wcmV2ZW50RGVmYXVsdCgpJTNCJTVDbiUyRiUyRiUyMCUyMGUuc3RvcFByb3BhZ2F0aW9uKCklM0IlNUNuJTIwJTIwJTdEJTVDbiU3RCU1Q24lNUNuY29uc3QlMjBpbnB1dFByZXZlbnRGbiUyMCUzRCUyMChub2RlKSUyMCUzRCUzRSUyMCU3QiU1Q24lMjAlMjBpZiUyMChub2RlLnR5cGUlMjAlM0QlM0QlMjAlNUMlMjJncm91cCU1QyUyMiklMjAlN0IlNUNuJTIwJTIwJTIwJTIwb25Nb3VudGVkKCgpJTIwJTNEJTNFJTIwJTdCJTVDbiUyMCUyMCUyMCUyMCUyMCUyMGlmJTIwKG5vZGUuY2hpbGRyZW4ubGVuZ3RoJTIwJTNEJTNEJTIwMSUyMCUyNiUyNiUyMG5vZGUucHJvcHMudHlwZSUyMCUzRCUzRCUyMCU1QyUyMmZvcm0lNUMlMjIlMjAlMjYlMjYlMjBpbXBsaWNpU3VibWlzc2lvblR5cGUuaW5jbHVkZXMobm9kZS5jaGlsZHJlbiU1QjAlNUQucHJvcHMudHlwZSkpJTIwJTdCJTVDbiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMGNvbnN0JTIwaWQlMjAlM0QlMjBub2RlLnByb3BzLmlkJTIwJTNGJTNGJTIwJTVDJTIyJTVDJTIyJTNCJTVDbiUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMGNvbnN0JTIwcm9vdEZvcm0lMjAlM0QlMjBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChpZCklM0IlNUNuJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwcm9vdEZvcm0lM0YuYWRkRXZlbnRMaXN0ZW5lciglNUMlMjJrZXlkb3duJTVDJTIyJTJDJTIwaGFuZGxlS2V5ZG93biklM0IlNUNuJTIwJTIwJTIwJTIwJTIwJTIwJTdEJTVDbiUyMCUyMCUyMCUyMCU3RCklM0IlNUNuJTIwJTIwJTdEJTVDbiU3RCUzQiU1Q24lNUNuJTVDbmV4cG9ydCUyMGRlZmF1bHQlMjAlN0IlNUNuJTIwJTIwcGx1Z2lucyUzQSUyMCU1QmlucHV0UHJldmVudEZuJTIwJTVEJTVDbiU3RCU1Q24lMjIlMkMlMjJyZW1vdmFibGUlMjIlM0F0cnVlJTJDJTIyYWRkZWQlMjIlM0F0cnVlJTdEJTVE&imports=JTdCJTIybmFtZSUyMiUzQSUyMkltcG9ydE1hcCUyMiUyQyUyMmVkaXRvciUyMiUzQSUyMiU3QiU1Q24lMjAlMjAlNUMlMjJ2dWUlNUMlMjIlM0ElMjAlNUMlMjJodHRwcyUzQSUyRiUyRmNkbi5qc2RlbGl2ci5uZXQlMkZucG0lMkZ2dWUlNDAzJTJGZGlzdCUyRnZ1ZS5lc20tYnJvd3Nlci5taW4uanMlNUMlMjIlNUNuJTdEJTVDbiUyMiU3RA)
+ 点击表单元素,表单元素聚焦之后,之后键入`enter`观察是否触发`submit`
+ 打开`formkit.config.js`tab, 放开注释,阻止事件行为。重复第二点。观察`submit`事件。
#### Does this PR introduce a user-facing change?

<!--
如果当前 Pull Request 的修改不会造成用户侧的任何变更,在 `release-note` 代码块儿中填写 `NONE`。
否则请填写用户侧能够理解的 Release Note。如果当前 Pull Request 包含破坏性更新(Break Change),
Release Note 需要以 `action required` 开头。
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->

```release-note
 禁止使用 Enter 提交,需要使用组合键(Ctrl + Enter)
```
pull/3445/head
wzrove 2022-12-16 10:24:10 +08:00 committed by GitHub
parent 97d2066d42
commit be251c08cc
2 changed files with 82 additions and 1 deletions

View File

@ -17,12 +17,13 @@ import { categoryCheckbox } from "./inputs/category-checkbox";
import { tagCheckbox } from "./inputs/tag-checkbox";
import radioAlt from "./plugins/radio-alt";
import stopImplicitSubmission from "./plugins/stop-implicit-submission";
const config: DefaultConfigOptions = {
config: {
classes: generateClasses(theme),
},
plugins: [radioAlt],
plugins: [radioAlt, stopImplicitSubmission],
inputs: {
form,
attachment,

View File

@ -0,0 +1,80 @@
import router from "@/router";
import type { FormKitNode } from "@formkit/core";
import { onMounted } from "vue";
import { useRoute } from "vue-router";
function handleKeydown(e: KeyboardEvent) {
if (
e.key == "Enter" &&
e.altKey == false &&
e.ctrlKey == false &&
e.metaKey == false
) {
e.stopImmediatePropagation();
e.preventDefault();
e.stopPropagation();
}
}
// 以下表单键入enter引起表单提交
//https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission
const implicitSubmissionType = [
"text",
"search",
"url",
"email",
"password",
"date",
"month",
"week",
"time",
"datetime-local",
"number",
];
const FormKeydownEventControllerMap = new Map<string, AbortController>();
const clearFormKeydownEventByPath = (fullPath: string) => {
if (FormKeydownEventControllerMap.size) {
FormKeydownEventControllerMap.forEach((controller, path) => {
if (!fullPath.includes(path)) {
controller.abort();
FormKeydownEventControllerMap.delete(path);
}
});
}
};
//https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener#%E5%8F%82%E6%95%B0
//使用AbortSignal来取消事件监听
const inputPreventFn = (node: FormKitNode) => {
const id = node.props.id ?? "";
if (node.type == "group") {
onMounted(() => {
const { path } = useRoute();
let controller = FormKeydownEventControllerMap.get(path);
if (
node.children.length == 1 &&
node.props.type == "form" &&
implicitSubmissionType.includes(node.children[0].props.type)
) {
if (!controller) {
controller = new AbortController();
FormKeydownEventControllerMap.set(path, controller);
}
const rootForm = document.getElementById(id);
rootForm?.addEventListener("keydown", handleKeydown, {
signal: controller.signal,
});
}
});
}
};
// 进入下一个页面,清除上一个页面的事件。
// 可以做个测试后面。https://segmentfault.com/q/1010000042671015
// map里面的key是path这里传fullPath当有多级路由的时候避免清掉上一级的事件。
router.beforeEach(({ fullPath }) => {
clearFormKeydownEventByPath(fullPath);
});
export default inputPreventFn;