halo/ui/docs/custom-formkit-input
Takagi 3be91fcb6f
refactor: enhance formkit select component to support additional features (#6473)
#### What type of PR is this?

/kind feature
/area ui
/milestone 2.19.x

#### What this PR does / why we need it:

此 PR 使用自定义的 Select 组件替换了原有的 Formkit Select 组件,原 Select 组件类型变为 `nativeSelect`。

新的 Select 组件具有如下功能:
- 增加多选、单选两种模式。
- 支持对内容进行搜索、过滤。
- 可使用接口远程加载,并可自定义数据获取。
- 支持扩展远程加载方式。
- 可创建新选项。
- 支持清空所有已选择项。
- 多选状态下可控制最大选择数量。
- 多选状态下可进行排序。

重构后的 Select 组件将自动兼容旧版组件。

使用方式如下:

```vue
<FormKit
  type="select"
  label="What country makes the best food?"
  name="countries"
  placeholder="Select a country"
  allow-create
  clearable
  sortable
  multiple
  searchable
  :max-count="3"
  :options="[
    { label: 'France', value: 'fr'},
    { label: 'Germany', value: 'de'},
    { label: 'Spain', value: 'es'},
    { label: 'Italy', value: 'ie' },
    { label: 'Greece', value: 'gr'},
  ]"
  help="Don’t worry, you can’t get this one wrong."
/>
```

#### How to test it?

1. 需要测试已使用的 Select 组件功能是否发生了变化。

测试在多选、单选状态下,Select 组件的功能是否可以正常使用。

测试在远程加载时,数据获取是否正常,是否可正常分页,加载状态是否显示。

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

see https://github.com/halo-dev/halo/issues/4931#issuecomment-2060637101
see #6369 

#### Does this PR introduce a user-facing change?
```release-note
重构 FormKit 选择器组件以支持更多功能
```
2024-08-26 09:03:14 +00:00
..
README.md refactor: enhance formkit select component to support additional features (#6473) 2024-08-26 09:03:14 +00:00

README.md

自定义 FormKit 输入组件

原由

目前在 Console 端的所有表单都使用了 FormKit但 FormKit 内置的 Input 组件并不满足所有的需求,因此需要自定义一些 Input 组件。此外,为了插件和主题能够更加方便的使用系统内的一些数据,所以同样需要自定义一些带数据的选择组件。

使用方式

目前已提供以下类型:

  • code: 代码编辑器
    • 参数
      1. language: 目前支持 yaml, html, css, javascript, json
      2. height: 编辑器高度,如:100px
  • attachment: 附件选择
    • 参数
      1. accepts允许上传的文件类型image/*
  • repeater: 定义一个对象集合,可以让使用者可视化的操作集合。
    • 参数
      1. min: 最小数量,默认为 0
      2. max: 最大数量,默认为 Infinity,即无限制。
      3. addLabel: 添加按钮的文本,默认为 添加
      4. addButton: 是否显示添加按钮,默认为 true
      5. upControl: 是否显示上移按钮,默认为 true
      6. downControl: 是否显示下移按钮,默认为 true
      7. insertControl: 是否显示插入按钮,默认为 true
      8. removeControl: 是否显示删除按钮,默认为 true
  • list: 动态列表,定义一个数组列表。
    • 参数
      1. itemType: 列表项的数据类型,用于初始化数据类型,可选参数 string, number, boolean, object,默认为 string
      2. min: 最小数量,默认为 0
      3. max: 最大数量,默认为 Infinity,即无限制。
      4. addLabel: 添加按钮的文本,默认为 添加
      5. addButton: 是否显示添加按钮,默认为 true
      6. upControl: 是否显示上移按钮,默认为 true
      7. downControl: 是否显示下移按钮,默认为 true
      8. insertControl: 是否显示插入按钮,默认为 true
      9. removeControl: 是否显示删除按钮,默认为 true
  • menuCheckbox:选择一组菜单
  • menuRadio:选择一个菜单
  • menuItemSelect:选择菜单项
  • postSelect:选择文章
  • singlePageSelect:选择自定义页面
  • categorySelect:选择分类
    • 参数
      1. multiple: 是否多选,默认为 false
  • categoryCheckbox:选择多个分类
  • tagSelect:选择标签
    • 参数
      1. multiple: 是否多选,默认为 false
  • tagCheckbox:选择多个标签
  • verificationForm: 远程验证一组数据是否符合某个规则
    • 参数
      1. action: 对目标数据进行验证的接口地址
      2. label: 验证按钮文本
      3. buttonAttrs: 验证按钮的额外属性
  • secret: 用于选择或者管理密钥Secret
    • 参数
      1. requiredKey用于确认所需密钥的字段名称
  • select: 自定义的选择器组件,用于在备选项中选择一个或多个选项
    • 参数
      1. options:静态数据源。当 actionremote 存在时,此参数无效。
      2. action:远程动态数据源的接口地址。
      3. requestOption 动态数据源的请求参数,可以通过此参数来指定如何获取数据,适配不同的接口。当 action 存在时,此参数有效。
      4. remote:标识当前是否由用户自定义的远程数据源。
      5. remoteOption:当 remotetrue 时,此配置项必须存在,用于为 Select 组件提供处理搜索及查询键值对的方法。
      6. remoteOptimize:是否开启远程数据源优化,默认为 true。开启后,将会对远程数据源进行优化,减少请求次数。仅在动态数据源下有效。
      7. allowCreate:是否允许创建新选项,默认为 false。仅在静态数据源下有效。
      8. clearable:是否允许清空选项,默认为 false
      9. multiple:是否多选,默认为 false
      10. maxCount:多选时最大可选数量,默认为 Infinity。仅在多选时有效。
      11. sortable:是否支持拖动排序,默认为 false。仅在多选时有效。
      12. searchable:是否支持搜索内容,默认为 false

在 Vue 单组件中使用:

<script lang="ts" setup>
const postName = ref("");
</script>

<template>
  <FormKit
    v-model="postName"
    placeholder="请选择文章"
    label="文章"
    type="postSelect"
    validation="required"
  />
</template>

在 FormKit Schema 中使用(插件 / 主题设置表单定义):

- $formkit: menuRadio
  name: menus
  label: 底部菜单组

select

select 是一个选择器类型的输入组件,使用者可以从一批待选数据中选择一个或多个选项。它支持单选、多选操作,并且支持静态数据及远程动态数据加载等多种方式。

在 Vue SFC 中以组件形式使用

静态数据源:

<script lang="ts" setup></script>
<template>
  <FormKit
    type="select"
    label="What country makes the best food?"
    name="countries"
    placeholder="Select a country"
    allow-create
    clearable
    sortable
    multiple
    searchable
    :options="[
      { label: "China", value: "China" },
      { label: "USA", value: "USA" },
      { label: "Japan", value: "Japan" },
      { label: "Korea", value: "Korea" },
      { label: "France", value: "France" },
      { label: "Italy", value: "Italy" },
      { label: "Germany", value: "Germany" },
      { label: "UK", value: "UK" },
      { label: "Canada", value: "Canada" },
      { label: "Australia", value: "Australia" },
    ]"
    help="Dont worry, you cant get this one wrong."
  />
</template>

动态数据源:

<script lang="ts" setup>
const ANONYMOUSUSER_NAME = "anonymousUser";
const DELETEDUSER_NAME = "ghost";

const handleSelectPostAuthorRemote = {
  search: async ({ keyword, page, size }) => {
    const { data } = await consoleApiClient.user.listUsers({
      page,
      size,
      keyword,
      fieldSelector: [
        `name!=${ANONYMOUSUSER_NAME}`,
        `name!=${DELETEDUSER_NAME}`,
      ],
    });
    return {
      options: data.items.map((item) => ({
        label: item.user.spec.displayName,
        value: item.user.metadata.name,
      })),
      total: data.total,
      page: data.page,
      size: data.size,
    };
  },

  findOptionsByValues: () => {
    return [];
  },
};
</script>
<template>
  <FormKit
    type="select"
    label="The author of the post is?"
    name="post_author"
    placeholder="Select a user"
    searchable
    remote
    :remote-option="handleSelectPostAuthorRemote"
  />
</template>

在 FormKit Schema 中使用

静态数据源:

- $formkit: select
  name: countries
  label: What country makes the best food?
  sortable: true
  multiple: true
  clearable: true
  placeholder: Select a country
  options:
    - label: France
      value: fr
    - label: Germany
      value: de
    - label: Spain
      value: es
    - label: Italy
      value: ie
    - label: Greece
      value: gr

远程动态数据源:

支持远程动态数据源,通过 actionrequestOption 参数来指定如何获取数据。

请求的接口将会自动拼接 pagesizekeyword 参数,其中 keyword 为搜索关键词。

- $formkit: select
  name: postName
  label: Choose an post
  clearable: true
  action: /apis/api.console.halo.run/v1alpha1/posts
  requestOption:
    method: GET
    pageField: page
    sizeField: size
    totalField: total
    itemsField: items
    labelField: post.spec.title
    valueField: post.metadata.name

[!NOTE] 当远程数据具有分页时,可能会出现默认选项不在第一页的情况,此时 Select 组件将会发送另一个查询请求,以获取默认选项的数据。此接口会携带如下参数 labelSelector: ${requestOption.valueField}=in(value1,value2,value3)

其中value1, value2, value3 为默认选项的值。返回值与查询一致,通过 requestOption 解析。

list

list 是一个数组类型的输入组件,可以让使用者可视化的操作数组。它支持动态添加、删除、上移、下移、插入数组项等操作。

在 Vue SFC 中以组件形式使用:

<script lang="ts" setup>
const users = ref([]);
</script>

<template>
  <FormKit
    :min="1"
    :max="3"
    type="list"
    label="Users"
    add-label="Add User"
    item-type="string"
  >
    <template #default="{ index }">
      <FormKit
        type="text"
        :index="index"
        validation="required"
      />
    </template>
  </FormKit>
</template>

在 FormKit Schema 中使用:

- $formkit: list
  name: users
  label: Users
  addLabel: Add User
  min: 1
  max: 3
  itemType: string
  children:
    - $formkit: text
      index: "$index"
      validation: required

[!NOTE] list 组件有且只有一个子节点,并且必须为子节点传递 index 属性。若想提供多个字段,则建议使用 group 组件包裹。

最终得到的数据类似于:

{
  "users": [
    "Jack",
    "John"
  ]
}

Repeater

Repeater 是一个集合类型的输入组件,可以让使用者可视化的操作集合。

在 Vue SFC 中以组件形式使用:

<script lang="ts" setup>
const users = ref([]);
</script>

<template>
  <FormKit
    v-model="users"
    :min="1"
    :max="3"
    addLabel="Add User"
    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 中使用:

- $formkit: repeater
  name: users
  label: Users
  addLabel: Add User
  min: 1
  max: 3
  items:
    - $formkit: text
      name: full_name
      label: Full Name
      validation: required
    - $formkit: email
      name: email
      label: Email
      validation: required|email

最终得到的数据类似于:

[
  {
    "full_name": "Jack",
    "email": "jack@example.com"
  },
  {
    "full_name": "John",
    "email": "john@example.com"
  }
]