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`
|
||||
2. height: 编辑器高度,如:`100px`
|
||||
- `attachment`: 附件选择
|
||||
- `repeater`: 定义一个对象集合,可以让使用者可视化的操作集合。
|
||||
- `menuCheckbox`:选择一组菜单
|
||||
- `menuRadio`:选择一个菜单
|
||||
- `menuItemSelect`:选择菜单项
|
||||
|
@ -48,3 +49,68 @@ const postName = ref("")
|
|||
name: menus
|
||||
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/themes": "^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/components": "workspace:*",
|
||||
"@halo-dev/console-shared": "workspace:*",
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
:class="classes"
|
||||
:disabled="disabled"
|
||||
class="btn"
|
||||
type="button"
|
||||
@click="handleClick"
|
||||
>
|
||||
<span v-if="$slots.icon || loading" class="btn-icon">
|
||||
|
@ -128,11 +129,11 @@ function handleClick() {
|
|||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply text-white bg-primary;
|
||||
@apply text-white bg-primary #{!important};
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply text-white bg-secondary;
|
||||
@apply text-white bg-secondary #{!important};
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
|
|
|
@ -1,61 +1,61 @@
|
|||
// Vitest Snapshot v1
|
||||
|
||||
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>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
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>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
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>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
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>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
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>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
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>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
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>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
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>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
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>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
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>
|
||||
</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-title\\">Not 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>
|
||||
</button></div>
|
||||
</div>"
|
||||
|
|
|
@ -54,6 +54,8 @@ import IconRefreshLine from "~icons/ri/refresh-line";
|
|||
import IconWindowLine from "~icons/ri/window-line";
|
||||
import IconSendPlaneFill from "~icons/ri/send-plane-fill";
|
||||
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 {
|
||||
IconDashboard,
|
||||
|
@ -112,4 +114,6 @@ export {
|
|||
IconWindowLine,
|
||||
IconSendPlaneFill,
|
||||
IconRocketLine,
|
||||
IconArrowUpCircleLine,
|
||||
IconArrowDownCircleLine,
|
||||
};
|
||||
|
|
100
pnpm-lock.yaml
100
pnpm-lock.yaml
|
@ -10,6 +10,7 @@ importers:
|
|||
'@formkit/i18n': ^1.0.0-beta.11
|
||||
'@formkit/inputs': ^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
|
||||
'@halo-dev/api-client': ^0.0.46
|
||||
'@halo-dev/components': workspace:*
|
||||
|
@ -99,11 +100,12 @@ importers:
|
|||
yaml: ^2.1.3
|
||||
dependencies:
|
||||
'@emoji-mart/data': 1.0.6
|
||||
'@formkit/core': 1.0.0-beta.11
|
||||
'@formkit/i18n': 1.0.0-beta.11
|
||||
'@formkit/inputs': 1.0.0-beta.11
|
||||
'@formkit/themes': 1.0.0-beta.11_tailwindcss@3.2.1
|
||||
'@formkit/vue': 1.0.0-beta.11_vjnbgdptsk6bkj7ab5a6mk2cwm
|
||||
'@formkit/core': 1.0.0-beta.12-e579559
|
||||
'@formkit/i18n': 1.0.0-beta.12-e579559
|
||||
'@formkit/inputs': 1.0.0-beta.12-e579559
|
||||
'@formkit/themes': 1.0.0-beta.12-e579559_tailwindcss@3.2.1
|
||||
'@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/components': link:packages/components
|
||||
'@halo-dev/console-shared': link:packages/shared
|
||||
|
@ -1845,50 +1847,50 @@ packages:
|
|||
'@floating-ui/core': 0.3.1
|
||||
dev: false
|
||||
|
||||
/@formkit/core/1.0.0-beta.11:
|
||||
resolution: {integrity: sha512-1yIQUicY1ZVGkeT0AEVp3ahx5FdJg+snyc8yaitsNmQeCSq1Ju5o1nCRcpcZ7j2ezMbGUiL7nTscGGejC15N2Q==}
|
||||
/@formkit/core/1.0.0-beta.12-e579559:
|
||||
resolution: {integrity: sha512-y90ubMcFr6WtAjZqUOLgA3p4jm024f6R7iDRVKHsmdwSKm1/GZl4D+Yo2k8DL40xM7NbZhIvK40MiEC0pNZ3ig==}
|
||||
dependencies:
|
||||
'@formkit/utils': 1.0.0-beta.11
|
||||
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||
dev: false
|
||||
|
||||
/@formkit/dev/1.0.0-beta.11:
|
||||
resolution: {integrity: sha512-YuPfE4ZoIAOQfawKlnCxXdnNOktckGNUhtCirpgsyBuHVu15rX1E5mYRAyEBhDGeEPV8IOFNpooSKA/y9W7dtw==}
|
||||
/@formkit/dev/1.0.0-beta.12-e579559:
|
||||
resolution: {integrity: sha512-jH5o9VwQWVuyO8cIr3BiZFXai0BtQe8mL+EUECEoRYtp6vkzgk6ErezejX7jqnu4dW9NS6chMeLIF+OAjh6zIg==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.0.0-beta.11
|
||||
'@formkit/utils': 1.0.0-beta.11
|
||||
'@formkit/core': 1.0.0-beta.12-e579559
|
||||
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||
dev: false
|
||||
|
||||
/@formkit/i18n/1.0.0-beta.11:
|
||||
resolution: {integrity: sha512-ID4y9r5NoKy+rIiv41UGCtrg8iR3Lor30oBEUJlEDPAh34vlwh4g2jorXgysdSVMRCuLnqGyGspLnrPdxgSszw==}
|
||||
/@formkit/i18n/1.0.0-beta.12-e579559:
|
||||
resolution: {integrity: sha512-IKfhVR+LWSoIAd60OFHjLbt4y/owd7uQfI1uO8sjXs2iz+7VqmE33R7DcI3cu5+WuC9JE/letRteL9nSSJ6kBA==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.0.0-beta.11
|
||||
'@formkit/utils': 1.0.0-beta.11
|
||||
'@formkit/validation': 1.0.0-beta.11
|
||||
'@formkit/core': 1.0.0-beta.12-e579559
|
||||
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||
'@formkit/validation': 1.0.0-beta.12-e579559
|
||||
dev: false
|
||||
|
||||
/@formkit/inputs/1.0.0-beta.11:
|
||||
resolution: {integrity: sha512-DF+olkewjAE/q+alsG1t4GFX7JHzVvTCukOOEuqp8AzT+MGyZFzxikSXd8qA5zX+1BvRCgQwzWZMBaQLUo3IMA==}
|
||||
/@formkit/inputs/1.0.0-beta.12-e579559:
|
||||
resolution: {integrity: sha512-igRXzW9edZ4wMHkX7XFricPsVNU99BoMWKwDOzg+erjLKwPw+h4tVKWmZFe2xTUUDSCBoxkLS5TgdsUZ84vMng==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.0.0-beta.11
|
||||
'@formkit/core': 1.0.0-beta.12-e579559
|
||||
dev: false
|
||||
|
||||
/@formkit/observer/1.0.0-beta.11:
|
||||
resolution: {integrity: sha512-jpOqOJdpydl/2eOL0yXVZjXYdk9tY0EsAf9e5PTxXcWOZpBskSwPdjNMnx8y9ycrpj6lKPcbMfs2bjnQQue8IQ==}
|
||||
/@formkit/observer/1.0.0-beta.12-e579559:
|
||||
resolution: {integrity: sha512-6Nki4VmUN3OyU70N6AIttvn/ScZXq5mwv11BIYLgfsfiMI5zoiflmNHrD7yf4KTUVvz16ThJB5nRGIXlnUnH3Q==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.0.0-beta.11
|
||||
'@formkit/utils': 1.0.0-beta.11
|
||||
'@formkit/core': 1.0.0-beta.12-e579559
|
||||
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||
dev: false
|
||||
|
||||
/@formkit/rules/1.0.0-beta.11:
|
||||
resolution: {integrity: sha512-6SQ+fGXTx9MLMUk832x9oR/423U4/7TpDxyx/cjz1xaQjAKzorwNGbjiiRszp4f3cSYcpyi/SZ36n0UktLaCVA==}
|
||||
/@formkit/rules/1.0.0-beta.12-e579559:
|
||||
resolution: {integrity: sha512-xu3VRPTob2GWA54MQafekVX+fUWYJbcMrkcDEo8yB4ZgKdlW5Er1C3gYa/bByuMNpY0/lklwdpKP/8aoIKfmxw==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.0.0-beta.11
|
||||
'@formkit/utils': 1.0.0-beta.11
|
||||
'@formkit/validation': 1.0.0-beta.11
|
||||
'@formkit/core': 1.0.0-beta.12-e579559
|
||||
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||
'@formkit/validation': 1.0.0-beta.12-e579559
|
||||
dev: false
|
||||
|
||||
/@formkit/themes/1.0.0-beta.11_tailwindcss@3.2.1:
|
||||
resolution: {integrity: sha512-OmefvFoLHZLnmbuT+Mqbqr681rumc62Q08Y1g82ZxG6aXWculwQYVQfZJeJFrqrJPpccsfoSflWeAuAMWiP/Yw==}
|
||||
/@formkit/themes/1.0.0-beta.12-e579559_tailwindcss@3.2.1:
|
||||
resolution: {integrity: sha512-ceFbta+xDO87Y96zAGqe3qCvSVyqEgmpsDCjk3BZTghydhIJupmgKSyr6TmmyJ5WT8zpcs8wnc2kF1YbUVt8Yw==}
|
||||
peerDependencies:
|
||||
tailwindcss: ^3.0.0
|
||||
unocss: ^0.31.0
|
||||
|
@ -1901,35 +1903,35 @@ packages:
|
|||
windicss:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@formkit/core': 1.0.0-beta.11
|
||||
'@formkit/core': 1.0.0-beta.12-e579559
|
||||
tailwindcss: 3.2.1_postcss@8.4.18
|
||||
dev: false
|
||||
|
||||
/@formkit/utils/1.0.0-beta.11:
|
||||
resolution: {integrity: sha512-ajeB6A0WJXaErwIgdHGnVZotghvWOZ+JvFKK6fM/rYQza2O4YFiPAVKqJRQjMbW5Jxia4sT7PQhPUFvZQFbuKQ==}
|
||||
/@formkit/utils/1.0.0-beta.12-e579559:
|
||||
resolution: {integrity: sha512-4yTz4IRzPX3G08aAO+ZfEuBYfy6OSgk3csx3TBhOcL6NzyIbJkTc2ZsxaUYXgY6FOVclazLGMKM5+jBitMOL5Q==}
|
||||
dev: false
|
||||
|
||||
/@formkit/validation/1.0.0-beta.11:
|
||||
resolution: {integrity: sha512-7HePIek0Y2MsH45lHDyCekJfENNdIr8ZIkJQx4VwAlfbIyZElZx5VsxInCSLf/03aOu4PhabOxpNUt38AP2hAg==}
|
||||
/@formkit/validation/1.0.0-beta.12-e579559:
|
||||
resolution: {integrity: sha512-f/S0LefikeBZRxKkjx1fD7RODXMHglg6+B1NUYEKsEKc3ySjH/V9/XroaPszXfkupAnVBSTjZXG7T9hmn39VVA==}
|
||||
dependencies:
|
||||
'@formkit/core': 1.0.0-beta.11
|
||||
'@formkit/observer': 1.0.0-beta.11
|
||||
'@formkit/core': 1.0.0-beta.12-e579559
|
||||
'@formkit/observer': 1.0.0-beta.12-e579559
|
||||
dev: false
|
||||
|
||||
/@formkit/vue/1.0.0-beta.11_vjnbgdptsk6bkj7ab5a6mk2cwm:
|
||||
resolution: {integrity: sha512-0tVy8JZ/j4QbmYlVPJyFGZOukWgRBTV+DhyGo82FZvvb2+YrRAbDhqJGLIjLyOO0pNTwk7arSgDiiKd/OK8LtA==}
|
||||
/@formkit/vue/1.0.0-beta.12-e579559_vjnbgdptsk6bkj7ab5a6mk2cwm:
|
||||
resolution: {integrity: sha512-z281m0+6alG7RyztFBDFvhVB2iYqaVk5o3Flfrro9FkzGDNCFjMyk0LwSTVQd8DhOVYeXPGImtKqp+mFoxrH4Q==}
|
||||
peerDependencies:
|
||||
vue: ^3.2.1
|
||||
dependencies:
|
||||
'@formkit/core': 1.0.0-beta.11
|
||||
'@formkit/dev': 1.0.0-beta.11
|
||||
'@formkit/i18n': 1.0.0-beta.11
|
||||
'@formkit/inputs': 1.0.0-beta.11
|
||||
'@formkit/observer': 1.0.0-beta.11
|
||||
'@formkit/rules': 1.0.0-beta.11
|
||||
'@formkit/themes': 1.0.0-beta.11_tailwindcss@3.2.1
|
||||
'@formkit/utils': 1.0.0-beta.11
|
||||
'@formkit/validation': 1.0.0-beta.11
|
||||
'@formkit/core': 1.0.0-beta.12-e579559
|
||||
'@formkit/dev': 1.0.0-beta.12-e579559
|
||||
'@formkit/i18n': 1.0.0-beta.12-e579559
|
||||
'@formkit/inputs': 1.0.0-beta.12-e579559
|
||||
'@formkit/observer': 1.0.0-beta.12-e579559
|
||||
'@formkit/rules': 1.0.0-beta.12-e579559
|
||||
'@formkit/themes': 1.0.0-beta.12-e579559_tailwindcss@3.2.1
|
||||
'@formkit/utils': 1.0.0-beta.12-e579559
|
||||
'@formkit/validation': 1.0.0-beta.12-e579559
|
||||
vue: 3.2.41
|
||||
transitivePeerDependencies:
|
||||
- tailwindcss
|
||||
|
|
|
@ -5,6 +5,7 @@ import type { DefaultConfigOptions } from "@formkit/vue";
|
|||
import { form } from "./inputs/form";
|
||||
import { attachment } from "./inputs/attachment";
|
||||
import { code } from "./inputs/code";
|
||||
import { repeater } from "./inputs/repeater";
|
||||
import { menuCheckbox } from "./inputs/menu-checkbox";
|
||||
import { menuRadio } from "./inputs/menu-radio";
|
||||
import { menuItemSelect } from "./inputs/menu-item-select";
|
||||
|
@ -15,14 +16,18 @@ import { tagSelect } from "./inputs/tag-select";
|
|||
import { categoryCheckbox } from "./inputs/category-checkbox";
|
||||
import { tagCheckbox } from "./inputs/tag-checkbox";
|
||||
|
||||
import radioAlt from "./plugins/radio-alt";
|
||||
|
||||
const config: DefaultConfigOptions = {
|
||||
config: {
|
||||
classes: generateClasses(theme),
|
||||
},
|
||||
plugins: [radioAlt],
|
||||
inputs: {
|
||||
form,
|
||||
attachment,
|
||||
code,
|
||||
repeater,
|
||||
menuCheckbox,
|
||||
menuRadio,
|
||||
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",
|
||||
wrapper:
|
||||
"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:
|
||||
"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",
|
||||
|
@ -79,6 +79,18 @@ const theme: Record<string, Record<string, string>> = {
|
|||
input:
|
||||
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;
|
||||
|
|
|
@ -208,7 +208,10 @@ const onVisibleChange = (visible: boolean) => {
|
|||
name="displayName"
|
||||
validation="required"
|
||||
></FormKit>
|
||||
<FormKitSchema :schema="formSchema" />
|
||||
<FormKitSchema
|
||||
:schema="formSchema"
|
||||
:data="configMapFormData['default']"
|
||||
/>
|
||||
</FormKit>
|
||||
|
||||
<template #footer>
|
||||
|
|
|
@ -65,7 +65,7 @@ watch(
|
|||
type="form"
|
||||
@submit="handleSaveConfigMap"
|
||||
>
|
||||
<FormKitSchema :schema="formSchema" />
|
||||
<FormKitSchema :schema="formSchema" :data="configMapFormData[group]" />
|
||||
</FormKit>
|
||||
</div>
|
||||
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
||||
|
|
|
@ -297,7 +297,10 @@ const iframeClasses = computed(() => {
|
|||
type="form"
|
||||
@submit="handleSaveThemeConfigMap"
|
||||
>
|
||||
<FormKitSchema :schema="formSchema" />
|
||||
<FormKitSchema
|
||||
:schema="formSchema"
|
||||
:data="configMapFormData[tab.id]"
|
||||
/>
|
||||
</FormKit>
|
||||
</div>
|
||||
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
||||
|
|
|
@ -74,7 +74,7 @@ await handleFetchPlugin();
|
|||
type="form"
|
||||
@submit="handleSaveConfigMap"
|
||||
>
|
||||
<FormKitSchema :schema="formSchema" />
|
||||
<FormKitSchema :schema="formSchema" :data="configMapFormData[group]" />
|
||||
</FormKit>
|
||||
</div>
|
||||
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
||||
|
|
|
@ -45,7 +45,7 @@ await handleFetchConfigMap();
|
|||
type="form"
|
||||
@submit="handleSaveConfigMap"
|
||||
>
|
||||
<FormKitSchema :schema="formSchema" />
|
||||
<FormKitSchema :schema="formSchema" :data="configMapFormData[group]" />
|
||||
</FormKit>
|
||||
</div>
|
||||
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
||||
|
|
Loading…
Reference in New Issue