mirror of https://github.com/halo-dev/halo
feat: add formkit custom input of repeater (halo-dev/console#692)
#### What type of PR is this? /kind feature /milestone 2.0 #### What this PR does / why we need it: 为 FormKit 添加 Repeater 输入类型,用于让用户动态操作一个对象数组。 使用方式可以查阅:https://github.com/ruibaby/halo-console/tree/feat/formkit-repeater/docs/custom-formkit-input#repeater #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/2529 #### Screenshots: <img width="635" alt="image" src="https://user-images.githubusercontent.com/21301288/201640327-5eb0489a-2193-445d-9dfe-7405ae75a297.png"> #### Special notes for your reviewer: /cc @halo-dev/sig-halo-console 测试方式: 1. 按照 https://github.com/ruibaby/halo-console/tree/feat/formkit-repeater/docs/custom-formkit-input#repeater 文档,在主题或者插件中使用 FormKit Schema 的形式定义设置表单,然后对表单进行保存等设置,检查是否符合预期。 2. 或者使用 https://github.com/halo-sigs/theme-earth/tree/refactor/setting-spec 主题进行测试,这个分支已经对社交媒体和侧边栏进行了适配。可以在主题设置中测试社交媒体和侧边栏配置,检查在主题端的效果。 #### Does this PR introduce a user-facing change? ```release-note 为 FormKit 添加 Repeater 输入类型。 ```pull/3445/head
parent
949697804d
commit
6d0e13365a
|
@ -13,6 +13,7 @@
|
||||||
1. language: 目前支持 `yaml`, `html`, `css`, `javascript`, `json`
|
1. language: 目前支持 `yaml`, `html`, `css`, `javascript`, `json`
|
||||||
2. height: 编辑器高度,如:`100px`
|
2. height: 编辑器高度,如:`100px`
|
||||||
- `attachment`: 附件选择
|
- `attachment`: 附件选择
|
||||||
|
- `repeater`: 定义一个对象集合,可以让使用者可视化的操作集合。
|
||||||
- `menuCheckbox`:选择一组菜单
|
- `menuCheckbox`:选择一组菜单
|
||||||
- `menuRadio`:选择一个菜单
|
- `menuRadio`:选择一个菜单
|
||||||
- `menuItemSelect`:选择菜单项
|
- `menuItemSelect`:选择菜单项
|
||||||
|
@ -48,3 +49,68 @@ const postName = ref("")
|
||||||
name: menus
|
name: menus
|
||||||
label: 底部菜单组
|
label: 底部菜单组
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Repeater
|
||||||
|
|
||||||
|
Repeater 是一个集合类型的输入组件,可以让使用者可视化的操作集合。
|
||||||
|
|
||||||
|
在 Vue SFC 中以组件形式使用:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const users = ref([])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FormKit
|
||||||
|
v-model="users"
|
||||||
|
type="repeater"
|
||||||
|
label="Users"
|
||||||
|
>
|
||||||
|
<FormKit
|
||||||
|
type="text"
|
||||||
|
label="Full Name"
|
||||||
|
name="full_name"
|
||||||
|
validation="required"
|
||||||
|
/>
|
||||||
|
<FormKit
|
||||||
|
type="email"
|
||||||
|
label="Email"
|
||||||
|
name="email"
|
||||||
|
validation="required|email"
|
||||||
|
/>
|
||||||
|
</FormKit>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
在 FormKit Schema 中使用:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- $formkit: repeater
|
||||||
|
name: users
|
||||||
|
label: Users
|
||||||
|
items:
|
||||||
|
- $formkit: text
|
||||||
|
name: full_name
|
||||||
|
label: Full Name
|
||||||
|
validation: required
|
||||||
|
- $formkit: email
|
||||||
|
name: email
|
||||||
|
label: Email
|
||||||
|
validation: required|email
|
||||||
|
```
|
||||||
|
|
||||||
|
最终得到的数据类似于:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"full_name": "Jack",
|
||||||
|
"email": "jack@example.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"full_name": "John",
|
||||||
|
"email": "john@example.com"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
"@formkit/inputs": "^1.0.0-beta.11",
|
"@formkit/inputs": "^1.0.0-beta.11",
|
||||||
"@formkit/themes": "^1.0.0-beta.11",
|
"@formkit/themes": "^1.0.0-beta.11",
|
||||||
"@formkit/vue": "^1.0.0-beta.11",
|
"@formkit/vue": "^1.0.0-beta.11",
|
||||||
|
"@formkit/utils": "^1.0.0-beta.11",
|
||||||
"@halo-dev/api-client": "^0.0.46",
|
"@halo-dev/api-client": "^0.0.46",
|
||||||
"@halo-dev/components": "workspace:*",
|
"@halo-dev/components": "workspace:*",
|
||||||
"@halo-dev/console-shared": "workspace:*",
|
"@halo-dev/console-shared": "workspace:*",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
:class="classes"
|
:class="classes"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
class="btn"
|
class="btn"
|
||||||
|
type="button"
|
||||||
@click="handleClick"
|
@click="handleClick"
|
||||||
>
|
>
|
||||||
<span v-if="$slots.icon || loading" class="btn-icon">
|
<span v-if="$slots.icon || loading" class="btn-icon">
|
||||||
|
@ -128,11 +129,11 @@ function handleClick() {
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
@apply text-white bg-primary;
|
@apply text-white bg-primary #{!important};
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
@apply text-white bg-secondary;
|
@apply text-white bg-secondary #{!important};
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-danger {
|
.btn-danger {
|
||||||
|
|
|
@ -1,61 +1,61 @@
|
||||||
// Vitest Snapshot v1
|
// Vitest Snapshot v1
|
||||||
|
|
||||||
exports[`Button > should render 1`] = `
|
exports[`Button > should render 1`] = `
|
||||||
"<button class=\\"btn-md btn-default btn\\">
|
"<button class=\\"btn-md btn-default btn\\" type=\\"button\\">
|
||||||
<!--v-if--><span class=\\"btn-content\\"></span>
|
<!--v-if--><span class=\\"btn-content\\"></span>
|
||||||
</button>"
|
</button>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Button > should work with block prop 1`] = `
|
exports[`Button > should work with block prop 1`] = `
|
||||||
"<button class=\\"btn-md btn-default btn-block btn\\">
|
"<button class=\\"btn-md btn-default btn-block btn\\" type=\\"button\\">
|
||||||
<!--v-if--><span class=\\"btn-content\\"></span>
|
<!--v-if--><span class=\\"btn-content\\"></span>
|
||||||
</button>"
|
</button>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Button > should work with circle prop 1`] = `
|
exports[`Button > should work with circle prop 1`] = `
|
||||||
"<button class=\\"btn-md btn-default btn-circle btn\\">
|
"<button class=\\"btn-md btn-default btn-circle btn\\" type=\\"button\\">
|
||||||
<!--v-if--><span class=\\"btn-content\\"></span>
|
<!--v-if--><span class=\\"btn-content\\"></span>
|
||||||
</button>"
|
</button>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Button > should work with disabled prop 1`] = `
|
exports[`Button > should work with disabled prop 1`] = `
|
||||||
"<button class=\\"btn-md btn-default btn\\" disabled=\\"\\">
|
"<button class=\\"btn-md btn-default btn\\" type=\\"button\\" disabled=\\"\\">
|
||||||
<!--v-if--><span class=\\"btn-content\\"></span>
|
<!--v-if--><span class=\\"btn-content\\"></span>
|
||||||
</button>"
|
</button>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Button > should work with size prop 1`] = `
|
exports[`Button > should work with size prop 1`] = `
|
||||||
"<button class=\\"btn-lg btn-default btn\\">
|
"<button class=\\"btn-lg btn-default btn\\" type=\\"button\\">
|
||||||
<!--v-if--><span class=\\"btn-content\\"></span>
|
<!--v-if--><span class=\\"btn-content\\"></span>
|
||||||
</button>"
|
</button>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Button > should work with size prop 2`] = `
|
exports[`Button > should work with size prop 2`] = `
|
||||||
"<button class=\\"btn-sm btn-default btn\\">
|
"<button class=\\"btn-sm btn-default btn\\" type=\\"button\\">
|
||||||
<!--v-if--><span class=\\"btn-content\\"></span>
|
<!--v-if--><span class=\\"btn-content\\"></span>
|
||||||
</button>"
|
</button>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Button > should work with size prop 3`] = `
|
exports[`Button > should work with size prop 3`] = `
|
||||||
"<button class=\\"btn-xs btn-default btn\\">
|
"<button class=\\"btn-xs btn-default btn\\" type=\\"button\\">
|
||||||
<!--v-if--><span class=\\"btn-content\\"></span>
|
<!--v-if--><span class=\\"btn-content\\"></span>
|
||||||
</button>"
|
</button>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Button > should work with type prop 1`] = `
|
exports[`Button > should work with type prop 1`] = `
|
||||||
"<button class=\\"btn-md btn-primary btn\\">
|
"<button class=\\"btn-md btn-primary btn\\" type=\\"button\\">
|
||||||
<!--v-if--><span class=\\"btn-content\\"></span>
|
<!--v-if--><span class=\\"btn-content\\"></span>
|
||||||
</button>"
|
</button>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Button > should work with type prop 2`] = `
|
exports[`Button > should work with type prop 2`] = `
|
||||||
"<button class=\\"btn-md btn-secondary btn\\">
|
"<button class=\\"btn-md btn-secondary btn\\" type=\\"button\\">
|
||||||
<!--v-if--><span class=\\"btn-content\\"></span>
|
<!--v-if--><span class=\\"btn-content\\"></span>
|
||||||
</button>"
|
</button>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`Button > should work with type prop 3`] = `
|
exports[`Button > should work with type prop 3`] = `
|
||||||
"<button class=\\"btn-md btn-danger btn\\">
|
"<button class=\\"btn-md btn-danger btn\\" type=\\"button\\">
|
||||||
<!--v-if--><span class=\\"btn-content\\"></span>
|
<!--v-if--><span class=\\"btn-content\\"></span>
|
||||||
</button>"
|
</button>"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -5,7 +5,7 @@ exports[`Empty > should match snapshot 1`] = `
|
||||||
<div class=\\"empty-image h-32 w-32\\"><img src=\\"/src/components/empty/Empty.svg\\" alt=\\"Empty\\"></div>
|
<div class=\\"empty-image h-32 w-32\\"><img src=\\"/src/components/empty/Empty.svg\\" alt=\\"Empty\\"></div>
|
||||||
<div class=\\"empty-title\\">Not found</div>
|
<div class=\\"empty-title\\">Not found</div>
|
||||||
<div class=\\"empty-message\\">No posts found</div>
|
<div class=\\"empty-message\\">No posts found</div>
|
||||||
<div class=\\"empty-actions\\"><button class=\\"btn-md btn-primary btn\\">
|
<div class=\\"empty-actions\\"><button class=\\"btn-md btn-primary btn\\" type=\\"button\\">
|
||||||
<!--v-if--><span class=\\"btn-content\\">New Post</span>
|
<!--v-if--><span class=\\"btn-content\\">New Post</span>
|
||||||
</button></div>
|
</button></div>
|
||||||
</div>"
|
</div>"
|
||||||
|
|
|
@ -54,6 +54,8 @@ import IconRefreshLine from "~icons/ri/refresh-line";
|
||||||
import IconWindowLine from "~icons/ri/window-line";
|
import IconWindowLine from "~icons/ri/window-line";
|
||||||
import IconSendPlaneFill from "~icons/ri/send-plane-fill";
|
import IconSendPlaneFill from "~icons/ri/send-plane-fill";
|
||||||
import IconRocketLine from "~icons/ri/rocket-line";
|
import IconRocketLine from "~icons/ri/rocket-line";
|
||||||
|
import IconArrowUpCircleLine from "~icons/ri/arrow-up-circle-line";
|
||||||
|
import IconArrowDownCircleLine from "~icons/ri/arrow-down-circle-line";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
IconDashboard,
|
IconDashboard,
|
||||||
|
@ -112,4 +114,6 @@ export {
|
||||||
IconWindowLine,
|
IconWindowLine,
|
||||||
IconSendPlaneFill,
|
IconSendPlaneFill,
|
||||||
IconRocketLine,
|
IconRocketLine,
|
||||||
|
IconArrowUpCircleLine,
|
||||||
|
IconArrowDownCircleLine,
|
||||||
};
|
};
|
||||||
|
|
100
pnpm-lock.yaml
100
pnpm-lock.yaml
|
@ -10,6 +10,7 @@ importers:
|
||||||
'@formkit/i18n': ^1.0.0-beta.11
|
'@formkit/i18n': ^1.0.0-beta.11
|
||||||
'@formkit/inputs': ^1.0.0-beta.11
|
'@formkit/inputs': ^1.0.0-beta.11
|
||||||
'@formkit/themes': ^1.0.0-beta.11
|
'@formkit/themes': ^1.0.0-beta.11
|
||||||
|
'@formkit/utils': ^1.0.0-beta.11
|
||||||
'@formkit/vue': ^1.0.0-beta.11
|
'@formkit/vue': ^1.0.0-beta.11
|
||||||
'@halo-dev/api-client': ^0.0.46
|
'@halo-dev/api-client': ^0.0.46
|
||||||
'@halo-dev/components': workspace:*
|
'@halo-dev/components': workspace:*
|
||||||
|
@ -99,11 +100,12 @@ importers:
|
||||||
yaml: ^2.1.3
|
yaml: ^2.1.3
|
||||||
dependencies:
|
dependencies:
|
||||||
'@emoji-mart/data': 1.0.6
|
'@emoji-mart/data': 1.0.6
|
||||||
'@formkit/core': 1.0.0-beta.11
|
'@formkit/core': 1.0.0-beta.12-e579559
|
||||||
'@formkit/i18n': 1.0.0-beta.11
|
'@formkit/i18n': 1.0.0-beta.12-e579559
|
||||||
'@formkit/inputs': 1.0.0-beta.11
|
'@formkit/inputs': 1.0.0-beta.12-e579559
|
||||||
'@formkit/themes': 1.0.0-beta.11_tailwindcss@3.2.1
|
'@formkit/themes': 1.0.0-beta.12-e579559_tailwindcss@3.2.1
|
||||||
'@formkit/vue': 1.0.0-beta.11_vjnbgdptsk6bkj7ab5a6mk2cwm
|
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||||
|
'@formkit/vue': 1.0.0-beta.12-e579559_vjnbgdptsk6bkj7ab5a6mk2cwm
|
||||||
'@halo-dev/api-client': 0.0.46
|
'@halo-dev/api-client': 0.0.46
|
||||||
'@halo-dev/components': link:packages/components
|
'@halo-dev/components': link:packages/components
|
||||||
'@halo-dev/console-shared': link:packages/shared
|
'@halo-dev/console-shared': link:packages/shared
|
||||||
|
@ -1845,50 +1847,50 @@ packages:
|
||||||
'@floating-ui/core': 0.3.1
|
'@floating-ui/core': 0.3.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@formkit/core/1.0.0-beta.11:
|
/@formkit/core/1.0.0-beta.12-e579559:
|
||||||
resolution: {integrity: sha512-1yIQUicY1ZVGkeT0AEVp3ahx5FdJg+snyc8yaitsNmQeCSq1Ju5o1nCRcpcZ7j2ezMbGUiL7nTscGGejC15N2Q==}
|
resolution: {integrity: sha512-y90ubMcFr6WtAjZqUOLgA3p4jm024f6R7iDRVKHsmdwSKm1/GZl4D+Yo2k8DL40xM7NbZhIvK40MiEC0pNZ3ig==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@formkit/utils': 1.0.0-beta.11
|
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@formkit/dev/1.0.0-beta.11:
|
/@formkit/dev/1.0.0-beta.12-e579559:
|
||||||
resolution: {integrity: sha512-YuPfE4ZoIAOQfawKlnCxXdnNOktckGNUhtCirpgsyBuHVu15rX1E5mYRAyEBhDGeEPV8IOFNpooSKA/y9W7dtw==}
|
resolution: {integrity: sha512-jH5o9VwQWVuyO8cIr3BiZFXai0BtQe8mL+EUECEoRYtp6vkzgk6ErezejX7jqnu4dW9NS6chMeLIF+OAjh6zIg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@formkit/core': 1.0.0-beta.11
|
'@formkit/core': 1.0.0-beta.12-e579559
|
||||||
'@formkit/utils': 1.0.0-beta.11
|
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@formkit/i18n/1.0.0-beta.11:
|
/@formkit/i18n/1.0.0-beta.12-e579559:
|
||||||
resolution: {integrity: sha512-ID4y9r5NoKy+rIiv41UGCtrg8iR3Lor30oBEUJlEDPAh34vlwh4g2jorXgysdSVMRCuLnqGyGspLnrPdxgSszw==}
|
resolution: {integrity: sha512-IKfhVR+LWSoIAd60OFHjLbt4y/owd7uQfI1uO8sjXs2iz+7VqmE33R7DcI3cu5+WuC9JE/letRteL9nSSJ6kBA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@formkit/core': 1.0.0-beta.11
|
'@formkit/core': 1.0.0-beta.12-e579559
|
||||||
'@formkit/utils': 1.0.0-beta.11
|
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||||
'@formkit/validation': 1.0.0-beta.11
|
'@formkit/validation': 1.0.0-beta.12-e579559
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@formkit/inputs/1.0.0-beta.11:
|
/@formkit/inputs/1.0.0-beta.12-e579559:
|
||||||
resolution: {integrity: sha512-DF+olkewjAE/q+alsG1t4GFX7JHzVvTCukOOEuqp8AzT+MGyZFzxikSXd8qA5zX+1BvRCgQwzWZMBaQLUo3IMA==}
|
resolution: {integrity: sha512-igRXzW9edZ4wMHkX7XFricPsVNU99BoMWKwDOzg+erjLKwPw+h4tVKWmZFe2xTUUDSCBoxkLS5TgdsUZ84vMng==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@formkit/core': 1.0.0-beta.11
|
'@formkit/core': 1.0.0-beta.12-e579559
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@formkit/observer/1.0.0-beta.11:
|
/@formkit/observer/1.0.0-beta.12-e579559:
|
||||||
resolution: {integrity: sha512-jpOqOJdpydl/2eOL0yXVZjXYdk9tY0EsAf9e5PTxXcWOZpBskSwPdjNMnx8y9ycrpj6lKPcbMfs2bjnQQue8IQ==}
|
resolution: {integrity: sha512-6Nki4VmUN3OyU70N6AIttvn/ScZXq5mwv11BIYLgfsfiMI5zoiflmNHrD7yf4KTUVvz16ThJB5nRGIXlnUnH3Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@formkit/core': 1.0.0-beta.11
|
'@formkit/core': 1.0.0-beta.12-e579559
|
||||||
'@formkit/utils': 1.0.0-beta.11
|
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@formkit/rules/1.0.0-beta.11:
|
/@formkit/rules/1.0.0-beta.12-e579559:
|
||||||
resolution: {integrity: sha512-6SQ+fGXTx9MLMUk832x9oR/423U4/7TpDxyx/cjz1xaQjAKzorwNGbjiiRszp4f3cSYcpyi/SZ36n0UktLaCVA==}
|
resolution: {integrity: sha512-xu3VRPTob2GWA54MQafekVX+fUWYJbcMrkcDEo8yB4ZgKdlW5Er1C3gYa/bByuMNpY0/lklwdpKP/8aoIKfmxw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@formkit/core': 1.0.0-beta.11
|
'@formkit/core': 1.0.0-beta.12-e579559
|
||||||
'@formkit/utils': 1.0.0-beta.11
|
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||||
'@formkit/validation': 1.0.0-beta.11
|
'@formkit/validation': 1.0.0-beta.12-e579559
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@formkit/themes/1.0.0-beta.11_tailwindcss@3.2.1:
|
/@formkit/themes/1.0.0-beta.12-e579559_tailwindcss@3.2.1:
|
||||||
resolution: {integrity: sha512-OmefvFoLHZLnmbuT+Mqbqr681rumc62Q08Y1g82ZxG6aXWculwQYVQfZJeJFrqrJPpccsfoSflWeAuAMWiP/Yw==}
|
resolution: {integrity: sha512-ceFbta+xDO87Y96zAGqe3qCvSVyqEgmpsDCjk3BZTghydhIJupmgKSyr6TmmyJ5WT8zpcs8wnc2kF1YbUVt8Yw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
tailwindcss: ^3.0.0
|
tailwindcss: ^3.0.0
|
||||||
unocss: ^0.31.0
|
unocss: ^0.31.0
|
||||||
|
@ -1901,35 +1903,35 @@ packages:
|
||||||
windicss:
|
windicss:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@formkit/core': 1.0.0-beta.11
|
'@formkit/core': 1.0.0-beta.12-e579559
|
||||||
tailwindcss: 3.2.1_postcss@8.4.18
|
tailwindcss: 3.2.1_postcss@8.4.18
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@formkit/utils/1.0.0-beta.11:
|
/@formkit/utils/1.0.0-beta.12-e579559:
|
||||||
resolution: {integrity: sha512-ajeB6A0WJXaErwIgdHGnVZotghvWOZ+JvFKK6fM/rYQza2O4YFiPAVKqJRQjMbW5Jxia4sT7PQhPUFvZQFbuKQ==}
|
resolution: {integrity: sha512-4yTz4IRzPX3G08aAO+ZfEuBYfy6OSgk3csx3TBhOcL6NzyIbJkTc2ZsxaUYXgY6FOVclazLGMKM5+jBitMOL5Q==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@formkit/validation/1.0.0-beta.11:
|
/@formkit/validation/1.0.0-beta.12-e579559:
|
||||||
resolution: {integrity: sha512-7HePIek0Y2MsH45lHDyCekJfENNdIr8ZIkJQx4VwAlfbIyZElZx5VsxInCSLf/03aOu4PhabOxpNUt38AP2hAg==}
|
resolution: {integrity: sha512-f/S0LefikeBZRxKkjx1fD7RODXMHglg6+B1NUYEKsEKc3ySjH/V9/XroaPszXfkupAnVBSTjZXG7T9hmn39VVA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@formkit/core': 1.0.0-beta.11
|
'@formkit/core': 1.0.0-beta.12-e579559
|
||||||
'@formkit/observer': 1.0.0-beta.11
|
'@formkit/observer': 1.0.0-beta.12-e579559
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@formkit/vue/1.0.0-beta.11_vjnbgdptsk6bkj7ab5a6mk2cwm:
|
/@formkit/vue/1.0.0-beta.12-e579559_vjnbgdptsk6bkj7ab5a6mk2cwm:
|
||||||
resolution: {integrity: sha512-0tVy8JZ/j4QbmYlVPJyFGZOukWgRBTV+DhyGo82FZvvb2+YrRAbDhqJGLIjLyOO0pNTwk7arSgDiiKd/OK8LtA==}
|
resolution: {integrity: sha512-z281m0+6alG7RyztFBDFvhVB2iYqaVk5o3Flfrro9FkzGDNCFjMyk0LwSTVQd8DhOVYeXPGImtKqp+mFoxrH4Q==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: ^3.2.1
|
vue: ^3.2.1
|
||||||
dependencies:
|
dependencies:
|
||||||
'@formkit/core': 1.0.0-beta.11
|
'@formkit/core': 1.0.0-beta.12-e579559
|
||||||
'@formkit/dev': 1.0.0-beta.11
|
'@formkit/dev': 1.0.0-beta.12-e579559
|
||||||
'@formkit/i18n': 1.0.0-beta.11
|
'@formkit/i18n': 1.0.0-beta.12-e579559
|
||||||
'@formkit/inputs': 1.0.0-beta.11
|
'@formkit/inputs': 1.0.0-beta.12-e579559
|
||||||
'@formkit/observer': 1.0.0-beta.11
|
'@formkit/observer': 1.0.0-beta.12-e579559
|
||||||
'@formkit/rules': 1.0.0-beta.11
|
'@formkit/rules': 1.0.0-beta.12-e579559
|
||||||
'@formkit/themes': 1.0.0-beta.11_tailwindcss@3.2.1
|
'@formkit/themes': 1.0.0-beta.12-e579559_tailwindcss@3.2.1
|
||||||
'@formkit/utils': 1.0.0-beta.11
|
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||||
'@formkit/validation': 1.0.0-beta.11
|
'@formkit/validation': 1.0.0-beta.12-e579559
|
||||||
vue: 3.2.41
|
vue: 3.2.41
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- tailwindcss
|
- tailwindcss
|
||||||
|
|
|
@ -5,6 +5,7 @@ import type { DefaultConfigOptions } from "@formkit/vue";
|
||||||
import { form } from "./inputs/form";
|
import { form } from "./inputs/form";
|
||||||
import { attachment } from "./inputs/attachment";
|
import { attachment } from "./inputs/attachment";
|
||||||
import { code } from "./inputs/code";
|
import { code } from "./inputs/code";
|
||||||
|
import { repeater } from "./inputs/repeater";
|
||||||
import { menuCheckbox } from "./inputs/menu-checkbox";
|
import { menuCheckbox } from "./inputs/menu-checkbox";
|
||||||
import { menuRadio } from "./inputs/menu-radio";
|
import { menuRadio } from "./inputs/menu-radio";
|
||||||
import { menuItemSelect } from "./inputs/menu-item-select";
|
import { menuItemSelect } from "./inputs/menu-item-select";
|
||||||
|
@ -15,14 +16,18 @@ import { tagSelect } from "./inputs/tag-select";
|
||||||
import { categoryCheckbox } from "./inputs/category-checkbox";
|
import { categoryCheckbox } from "./inputs/category-checkbox";
|
||||||
import { tagCheckbox } from "./inputs/tag-checkbox";
|
import { tagCheckbox } from "./inputs/tag-checkbox";
|
||||||
|
|
||||||
|
import radioAlt from "./plugins/radio-alt";
|
||||||
|
|
||||||
const config: DefaultConfigOptions = {
|
const config: DefaultConfigOptions = {
|
||||||
config: {
|
config: {
|
||||||
classes: generateClasses(theme),
|
classes: generateClasses(theme),
|
||||||
},
|
},
|
||||||
|
plugins: [radioAlt],
|
||||||
inputs: {
|
inputs: {
|
||||||
form,
|
form,
|
||||||
attachment,
|
attachment,
|
||||||
code,
|
code,
|
||||||
|
repeater,
|
||||||
menuCheckbox,
|
menuCheckbox,
|
||||||
menuRadio,
|
menuRadio,
|
||||||
menuItemSelect,
|
menuItemSelect,
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {
|
||||||
|
VButton,
|
||||||
|
IconAddCircle,
|
||||||
|
IconCloseCircle,
|
||||||
|
IconArrowUpCircleLine,
|
||||||
|
IconArrowDownCircleLine,
|
||||||
|
} from "@halo-dev/components";
|
||||||
|
import type { FormKitFrameworkContext } from "@formkit/core";
|
||||||
|
import type { PropType } from "vue";
|
||||||
|
import cloneDeep from "lodash.clonedeep";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
context: {
|
||||||
|
type: Object as PropType<FormKitFrameworkContext>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleAppend = (index: number) => {
|
||||||
|
const value = cloneDeep(props.context._value);
|
||||||
|
value.splice(index + 1, 0, {});
|
||||||
|
props.context.node.input(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemove = (index: number) => {
|
||||||
|
const value = cloneDeep(props.context._value);
|
||||||
|
value.splice(index, 1);
|
||||||
|
props.context.node.input(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMoveUp = (index: number) => {
|
||||||
|
if (index === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = cloneDeep(props.context._value);
|
||||||
|
value.splice(index - 1, 0, value.splice(index, 1)[0]);
|
||||||
|
props.context.node.input(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMoveDown = (index: number) => {
|
||||||
|
if (index === props.context._value.length - 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = cloneDeep(props.context._value);
|
||||||
|
value.splice(index + 1, 0, value.splice(index, 1)[0]);
|
||||||
|
props.context.node.input(value);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ul :class="context.classes.items">
|
||||||
|
<li
|
||||||
|
v-for="(item, index) in context._value"
|
||||||
|
:key="index"
|
||||||
|
:class="context.classes.item"
|
||||||
|
>
|
||||||
|
<div :class="context.classes.content">
|
||||||
|
<FormKit
|
||||||
|
:id="`${context.node.name}-group-${index}`"
|
||||||
|
:key="`${context.node.name}-group-${index}`"
|
||||||
|
:model-value="item"
|
||||||
|
type="group"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</FormKit>
|
||||||
|
</div>
|
||||||
|
<div :class="context.classes.controls">
|
||||||
|
<ul class="flex flex-col items-center justify-center gap-1.5 py-2">
|
||||||
|
<li
|
||||||
|
class="cursor-pointer text-gray-500 transition-all hover:text-primary"
|
||||||
|
:class="{
|
||||||
|
'cursor-not-allowed opacity-50 hover:!text-gray-500': index === 0,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<IconArrowUpCircleLine
|
||||||
|
class="h-5 w-5"
|
||||||
|
@click="handleMoveUp(index)"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="cursor-pointer text-gray-500 transition-all hover:text-primary"
|
||||||
|
>
|
||||||
|
<IconCloseCircle class="h-5 w-5" @click="handleRemove(index)" />
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="cursor-pointer text-gray-500 transition-all hover:text-primary"
|
||||||
|
>
|
||||||
|
<IconAddCircle class="h-5 w-5" @click="handleAppend(index)" />
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="cursor-pointer text-gray-500 transition-all hover:text-primary"
|
||||||
|
:class="{
|
||||||
|
'cursor-not-allowed opacity-50 hover:!text-gray-500':
|
||||||
|
index === context._value.length - 1,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<IconArrowDownCircleLine
|
||||||
|
class="h-5 w-5"
|
||||||
|
@click="handleMoveDown(index)"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div :class="context.classes.add">
|
||||||
|
<VButton type="secondary" @click="handleAppend(context._value.length)">
|
||||||
|
<template #icon>
|
||||||
|
<IconAddCircle class="h-full w-full" />
|
||||||
|
</template>
|
||||||
|
添加
|
||||||
|
</VButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -0,0 +1,29 @@
|
||||||
|
import type { FormKitTypeDefinition } from "@formkit/core";
|
||||||
|
import {
|
||||||
|
fieldset,
|
||||||
|
help,
|
||||||
|
inner,
|
||||||
|
legend,
|
||||||
|
message,
|
||||||
|
messages,
|
||||||
|
outer,
|
||||||
|
prefix,
|
||||||
|
suffix,
|
||||||
|
} from "@formkit/inputs";
|
||||||
|
import { repeaterItems } from "./sections";
|
||||||
|
import Repeater from "./Repeater.vue";
|
||||||
|
|
||||||
|
export const repeater: FormKitTypeDefinition = {
|
||||||
|
schema: outer(
|
||||||
|
fieldset(
|
||||||
|
legend("$label"),
|
||||||
|
help("$help"),
|
||||||
|
inner(prefix(), repeaterItems("$slots.default"), suffix())
|
||||||
|
),
|
||||||
|
messages(message("$message.value"))
|
||||||
|
),
|
||||||
|
type: "list",
|
||||||
|
library: {
|
||||||
|
Repeater: Repeater,
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { createSection } from "@formkit/inputs";
|
||||||
|
|
||||||
|
export const repeaterItems = createSection("repeaterItems", () => ({
|
||||||
|
$cmp: "Repeater",
|
||||||
|
props: {
|
||||||
|
context: "$node.context",
|
||||||
|
},
|
||||||
|
}));
|
|
@ -0,0 +1,9 @@
|
||||||
|
import type { FormKitNode } from "@formkit/core";
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
export default function radioAlt(node: FormKitNode) {
|
||||||
|
if (node.props.type === "radio") {
|
||||||
|
node.props.altName = `radio_${node.name}_${++i}`;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ const boxClassification = {
|
||||||
"group border border-gray-300 rounded-base px-2 py-2 focus-within:border-primary max-w-lg",
|
"group border border-gray-300 rounded-base px-2 py-2 focus-within:border-primary max-w-lg",
|
||||||
wrapper:
|
wrapper:
|
||||||
"flex items-center mb-1 cursor-pointer group-[.formkit-fieldset]:px-2",
|
"flex items-center mb-1 cursor-pointer group-[.formkit-fieldset]:px-2",
|
||||||
help: "mb-2 mt-0",
|
help: "mb-2 mt-0 px-2",
|
||||||
input:
|
input:
|
||||||
"form-check-input h-4 w-4 mr-2 border border-gray-500 rounded-sm bg-white checked:bg-primary focus:outline-none focus:ring-0 transition duration-200",
|
"form-check-input h-4 w-4 mr-2 border border-gray-500 rounded-sm bg-white checked:bg-primary focus:outline-none focus:ring-0 transition duration-200",
|
||||||
inner: "flex items-center",
|
inner: "flex items-center",
|
||||||
|
@ -79,6 +79,18 @@ const theme: Record<string, Record<string, string>> = {
|
||||||
input:
|
input:
|
||||||
textClassification.input.replace("resize-none", "resize-y") + " py-2",
|
textClassification.input.replace("resize-none", "resize-y") + " py-2",
|
||||||
},
|
},
|
||||||
|
repeater: {
|
||||||
|
label: textClassification.label,
|
||||||
|
legend: `${textClassification.label} px-2`,
|
||||||
|
fieldset: boxClassification.fieldset,
|
||||||
|
wrapper: boxClassification.wrapper,
|
||||||
|
help: boxClassification.wrapper,
|
||||||
|
inner: "flex flex-col gap-4",
|
||||||
|
items: "flex flex-col w-full gap-2",
|
||||||
|
item: "border rounded-base grid grid-cols-12 focus-within:border-primary transition-all overflow-hidden focus-within:shadow-sm",
|
||||||
|
content: "flex-1 p-2 col-span-11",
|
||||||
|
controls: "bg-gray-200 col-span-1 flex items-center justify-center",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default theme;
|
export default theme;
|
||||||
|
|
|
@ -208,7 +208,10 @@ const onVisibleChange = (visible: boolean) => {
|
||||||
name="displayName"
|
name="displayName"
|
||||||
validation="required"
|
validation="required"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
<FormKitSchema :schema="formSchema" />
|
<FormKitSchema
|
||||||
|
:schema="formSchema"
|
||||||
|
:data="configMapFormData['default']"
|
||||||
|
/>
|
||||||
</FormKit>
|
</FormKit>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|
|
@ -65,7 +65,7 @@ watch(
|
||||||
type="form"
|
type="form"
|
||||||
@submit="handleSaveConfigMap"
|
@submit="handleSaveConfigMap"
|
||||||
>
|
>
|
||||||
<FormKitSchema :schema="formSchema" />
|
<FormKitSchema :schema="formSchema" :data="configMapFormData[group]" />
|
||||||
</FormKit>
|
</FormKit>
|
||||||
</div>
|
</div>
|
||||||
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
||||||
|
|
|
@ -297,7 +297,10 @@ const iframeClasses = computed(() => {
|
||||||
type="form"
|
type="form"
|
||||||
@submit="handleSaveThemeConfigMap"
|
@submit="handleSaveThemeConfigMap"
|
||||||
>
|
>
|
||||||
<FormKitSchema :schema="formSchema" />
|
<FormKitSchema
|
||||||
|
:schema="formSchema"
|
||||||
|
:data="configMapFormData[tab.id]"
|
||||||
|
/>
|
||||||
</FormKit>
|
</FormKit>
|
||||||
</div>
|
</div>
|
||||||
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
||||||
|
|
|
@ -74,7 +74,7 @@ await handleFetchPlugin();
|
||||||
type="form"
|
type="form"
|
||||||
@submit="handleSaveConfigMap"
|
@submit="handleSaveConfigMap"
|
||||||
>
|
>
|
||||||
<FormKitSchema :schema="formSchema" />
|
<FormKitSchema :schema="formSchema" :data="configMapFormData[group]" />
|
||||||
</FormKit>
|
</FormKit>
|
||||||
</div>
|
</div>
|
||||||
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
||||||
|
|
|
@ -45,7 +45,7 @@ await handleFetchConfigMap();
|
||||||
type="form"
|
type="form"
|
||||||
@submit="handleSaveConfigMap"
|
@submit="handleSaveConfigMap"
|
||||||
>
|
>
|
||||||
<FormKitSchema :schema="formSchema" />
|
<FormKitSchema :schema="formSchema" :data="configMapFormData[group]" />
|
||||||
</FormKit>
|
</FormKit>
|
||||||
</div>
|
</div>
|
||||||
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
||||||
|
|
Loading…
Reference in New Issue