diff --git a/ui/docs/custom-formkit-input/README.md b/ui/docs/custom-formkit-input/README.md index 671638660..50e48dbb6 100644 --- a/ui/docs/custom-formkit-input/README.md +++ b/ui/docs/custom-formkit-input/README.md @@ -28,14 +28,14 @@ - `list`: 动态列表,定义一个数组列表。 - 参数 1. itemType: 列表项的数据类型,用于初始化数据类型,可选参数 `string`, `number`, `boolean`, `object`,默认为 `string` - 1. min: 最小数量,默认为 `0` - 2. max: 最大数量,默认为 `Infinity`,即无限制。 - 3. addLabel: 添加按钮的文本,默认为 `添加` - 4. addButton: 是否显示添加按钮,默认为 `true` - 5. upControl: 是否显示上移按钮,默认为 `true` - 6. downControl: 是否显示下移按钮,默认为 `true` - 7. insertControl: 是否显示插入按钮,默认为 `true` - 8. removeControl: 是否显示删除按钮,默认为 `true` + 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`:选择菜单项 @@ -54,6 +54,9 @@ 1. action: 对目标数据进行验证的接口地址 2. label: 验证按钮文本 3. buttonAttrs: 验证按钮的额外属性 +- `secret`: 用于选择或者管理密钥(Secret) + - 参数 + 1. requiredKey:用于确认所需密钥的字段名称 在 Vue 单组件中使用: @@ -131,7 +134,6 @@ const users = ref([]); > [!NOTE] > `list` 组件有且只有一个子节点,并且必须为子节点传递 `index` 属性。若想提供多个字段,则建议使用 `group` 组件包裹。 - 最终得到的数据类似于: ```json @@ -143,7 +145,6 @@ const users = ref([]); } ``` - ### Repeater Repeater 是一个集合类型的输入组件,可以让使用者可视化的操作集合。 diff --git a/ui/src/constants/annotations.ts b/ui/src/constants/annotations.ts index abdfa85cb..4a165ebec 100644 --- a/ui/src/constants/annotations.ts +++ b/ui/src/constants/annotations.ts @@ -29,3 +29,8 @@ export enum contentAnnotations { export enum patAnnotations { ACCESS_TOKEN = "security.halo.run/access-token", } + +// Secret +export enum secretAnnotations { + DESCRIPTION = "secret.halo.run/description", +} diff --git a/ui/src/formkit/formkit.config.ts b/ui/src/formkit/formkit.config.ts index b16d518e8..63ed00790 100644 --- a/ui/src/formkit/formkit.config.ts +++ b/ui/src/formkit/formkit.config.ts @@ -18,6 +18,7 @@ import { password } from "./inputs/password"; import { postSelect } from "./inputs/post-select"; import { repeater } from "./inputs/repeater"; import { roleSelect } from "./inputs/role-select"; +import { secret } from "./inputs/secret"; import { singlePageSelect } from "./inputs/singlePage-select"; import { tagCheckbox } from "./inputs/tag-checkbox"; import { tagSelect } from "./inputs/tag-select"; @@ -42,26 +43,27 @@ const config: DefaultConfigOptions = { autoScrollToErrors, ], inputs: { - list, - form, - password, - group, - nativeGroup, attachment, - code, - repeater, - menuCheckbox, - menuRadio, - menuItemSelect, - postSelect, - categorySelect, - tagSelect, - singlePageSelect, - categoryCheckbox, - tagCheckbox, - roleSelect, - attachmentPolicySelect, attachmentGroupSelect, + attachmentPolicySelect, + categoryCheckbox, + categorySelect, + code, + form, + group, + list, + menuCheckbox, + menuItemSelect, + menuRadio, + nativeGroup, + password, + postSelect, + repeater, + roleSelect, + secret, + singlePageSelect, + tagCheckbox, + tagSelect, verificationForm, }, locales: { zh, en }, diff --git a/ui/src/formkit/inputs/secret/SecretSelect.vue b/ui/src/formkit/inputs/secret/SecretSelect.vue new file mode 100644 index 000000000..5afcc6370 --- /dev/null +++ b/ui/src/formkit/inputs/secret/SecretSelect.vue @@ -0,0 +1,316 @@ + + + diff --git a/ui/src/formkit/inputs/secret/components/SecretCreationModal.vue b/ui/src/formkit/inputs/secret/components/SecretCreationModal.vue new file mode 100644 index 000000000..6e59f1d90 --- /dev/null +++ b/ui/src/formkit/inputs/secret/components/SecretCreationModal.vue @@ -0,0 +1,81 @@ + + + diff --git a/ui/src/formkit/inputs/secret/components/SecretEditModal.vue b/ui/src/formkit/inputs/secret/components/SecretEditModal.vue new file mode 100644 index 000000000..b479cfa70 --- /dev/null +++ b/ui/src/formkit/inputs/secret/components/SecretEditModal.vue @@ -0,0 +1,102 @@ + + + diff --git a/ui/src/formkit/inputs/secret/components/SecretForm.vue b/ui/src/formkit/inputs/secret/components/SecretForm.vue new file mode 100644 index 000000000..2025c23c2 --- /dev/null +++ b/ui/src/formkit/inputs/secret/components/SecretForm.vue @@ -0,0 +1,58 @@ + + + diff --git a/ui/src/formkit/inputs/secret/components/SecretListItem.vue b/ui/src/formkit/inputs/secret/components/SecretListItem.vue new file mode 100644 index 000000000..822a3e558 --- /dev/null +++ b/ui/src/formkit/inputs/secret/components/SecretListItem.vue @@ -0,0 +1,84 @@ + + + diff --git a/ui/src/formkit/inputs/secret/components/SecretListModal.vue b/ui/src/formkit/inputs/secret/components/SecretListModal.vue new file mode 100644 index 000000000..dc818ebb7 --- /dev/null +++ b/ui/src/formkit/inputs/secret/components/SecretListModal.vue @@ -0,0 +1,51 @@ + + + diff --git a/ui/src/formkit/inputs/secret/composables/use-secrets-fetch.ts b/ui/src/formkit/inputs/secret/composables/use-secrets-fetch.ts new file mode 100644 index 000000000..5e1ce9433 --- /dev/null +++ b/ui/src/formkit/inputs/secret/composables/use-secrets-fetch.ts @@ -0,0 +1,20 @@ +import { coreApiClient } from "@halo-dev/api-client"; +import { useQuery } from "@tanstack/vue-query"; + +export const Q_KEY = () => ["secrets"]; + +export function useSecretsFetch() { + return useQuery({ + queryKey: Q_KEY(), + queryFn: async () => { + const { data } = await coreApiClient.secret.listSecret(); + return data; + }, + refetchInterval(data) { + const hasDeletingData = data?.items.some( + (item) => !!item.metadata.deletionTimestamp + ); + return hasDeletingData ? 1000 : false; + }, + }); +} diff --git a/ui/src/formkit/inputs/secret/index.ts b/ui/src/formkit/inputs/secret/index.ts new file mode 100644 index 000000000..cbcc19d03 --- /dev/null +++ b/ui/src/formkit/inputs/secret/index.ts @@ -0,0 +1,32 @@ +import type { FormKitTypeDefinition } from "@formkit/core"; +import { + help, + icon, + inner, + label, + message, + messages, + outer, + prefix, + suffix, + wrapper, +} from "@formkit/inputs"; +import SecretSelect from "./SecretSelect.vue"; +import { SecretSection } from "./sections"; + +export const secret: FormKitTypeDefinition = { + schema: outer( + wrapper( + label("$label"), + inner(icon("prefix"), prefix(), SecretSection(), suffix(), icon("suffix")) + ), + help("$help"), + messages(message("$message.value")) + ), + type: "input", + props: ["requiredKey"], + library: { + SecretSelect: SecretSelect, + }, + schemaMemoKey: "custom-secret-select", +}; diff --git a/ui/src/formkit/inputs/secret/sections/index.ts b/ui/src/formkit/inputs/secret/sections/index.ts new file mode 100644 index 000000000..cef32abbc --- /dev/null +++ b/ui/src/formkit/inputs/secret/sections/index.ts @@ -0,0 +1,8 @@ +import { createSection } from "@formkit/inputs"; + +export const SecretSection = createSection("SecretSection", () => ({ + $cmp: "SecretSelect", + props: { + context: "$node.context", + }, +})); diff --git a/ui/src/formkit/inputs/secret/types/index.ts b/ui/src/formkit/inputs/secret/types/index.ts new file mode 100644 index 000000000..bba9d7b1a --- /dev/null +++ b/ui/src/formkit/inputs/secret/types/index.ts @@ -0,0 +1,4 @@ +export interface SecretFormState { + description?: string; + stringDataArray: { key: string; value: string }[]; +} diff --git a/ui/src/formkit/theme.ts b/ui/src/formkit/theme.ts index fc1c20f71..52dfba5de 100644 --- a/ui/src/formkit/theme.ts +++ b/ui/src/formkit/theme.ts @@ -148,6 +148,11 @@ const theme: Record> = { "dropdown-wrapper": "absolute ring-1 ring-gray-100 top-full bottom-auto right-0 z-10 mt-1 max-h-96 w-full overflow-auto rounded bg-white shadow-lg", }, + secret: { + ...textClassification, + inner: `${textClassification.inner} !overflow-visible min-h-[2.25rem] !border-none`, + input: `w-0 flex-grow bg-transparent py-1 px-3 block transition-all text-sm`, + }, }; export default theme; diff --git a/ui/src/locales/en.yaml b/ui/src/locales/en.yaml index 1d5bb924d..80ce5f9fd 100644 --- a/ui/src/locales/en.yaml +++ b/ui/src/locales/en.yaml @@ -428,7 +428,10 @@ core: prevent_parent_post_cascade_query: >- Prevent parent category from including this category and its subcategories in cascade post queries - hide_from_list: This category is hidden, This category and its subcategories, as well as its posts, will not be displayed in the front-end list. You need to actively visit the category archive page + hide_from_list: >- + This category is hidden, This category and its subcategories, as well + as its posts, will not be displayed in the front-end list. You need to + actively visit the category archive page page: title: Pages actions: @@ -1644,6 +1647,27 @@ core: no_action_defined: "{label} interface not defined" verify_success: "{label} successful" verify_failed: "{label} failed" + secret: + creation_label: Create a new secret based on the text entered + placeholder: Search for an existing secret or enter new content to create one + required_key_missing_label: The needed fields are missing, Please select and complete them + creation_modal: + title: Create secret + edit_modal: + title: Edit secret + list_modal: + title: Secrets + operations: + delete: + title: Delete secret + description: >- + Are you sure you want to delete this secret? Please make sure that + this secret is not being used anywhere, otherwise you need to reset + it in a specific place + form: + fields: + description: Description + string_data: String Data common: buttons: save: Save diff --git a/ui/src/locales/zh-CN.yaml b/ui/src/locales/zh-CN.yaml index 880797a37..36e788081 100644 --- a/ui/src/locales/zh-CN.yaml +++ b/ui/src/locales/zh-CN.yaml @@ -1567,6 +1567,24 @@ core: no_action_defined: 未定义{label}接口 verify_success: "{label}成功" verify_failed: "{label}失败" + secret: + creation_label: 根据输入的文本创建新密钥 + placeholder: 搜索已存在的密钥或者输入内容以创建新的密钥 + required_key_missing_label: 缺少当前选项所需的字段,请选择之后补充完整 + creation_modal: + title: 创建密钥 + edit_modal: + title: 编辑密钥 + list_modal: + title: 管理密钥 + operations: + delete: + title: 删除密钥 + description: 确定删除此密钥吗?请确保没有地方正在使用此密钥,否则需要在具体的地方重新设置 + form: + fields: + description: 备注 + string_data: 字符串数据 common: buttons: save: 保存 diff --git a/ui/src/locales/zh-TW.yaml b/ui/src/locales/zh-TW.yaml index 162df9c24..fcb007b95 100644 --- a/ui/src/locales/zh-TW.yaml +++ b/ui/src/locales/zh-TW.yaml @@ -1524,6 +1524,24 @@ core: no_action_defined: 未定義{label}介面 verify_success: "{label}成功" verify_failed: "{label}失敗" + secret: + creation_label: 根據輸入的文本創建新密鈅 + placeholder: 搜索已存在的密鈅或者輸入內容以創建新的密鈅 + required_key_missing_label: 缺少當前選項所需的字段,請選擇之後補充完整 + creation_modal: + title: 創建密鈅 + edit_modal: + title: 編輯密鈅 + list_modal: + title: 管理密鈅 + operations: + delete: + title: 刪除密鈅 + description: 確定刪除此密鈅嗎?請確保沒有地方正在使用此密鈅,否則需要在具體的地方重新設置 + form: + fields: + description: 備註 + string_data: 字符串數據 common: buttons: save: 保存