Add hidden flag for attachment policies in upload UI (#7792)

#### What type of PR is this?

/area ui
/milestone 2.22.x
/kind feature

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

This PR adds an option in the storage policy settings to hide a policy from the upload interface.

This is useful because some storage policies are not intended for direct user uploads. Hiding them can help declutter the UI and prevent users from selecting them by mistake.

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

Fixes #7787 

#### Does this PR introduce a user-facing change?

```release-note
支持在附件上传界面隐藏部分存储策略
```
pull/7794/head^2
Ryan Wang 2025-10-04 01:04:28 +08:00 committed by GitHub
parent 84eefe37fa
commit b21e77b911
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 86 additions and 16 deletions

View File

@ -1,8 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import SubmitButton from "@/components/button/SubmitButton.vue"; import SubmitButton from "@/components/button/SubmitButton.vue";
import { attachmentPolicyLabels } from "@/constants/labels";
import { setFocus } from "@/formkit/utils/focus"; import { setFocus } from "@/formkit/utils/focus";
import type { FormKitSchemaCondition, FormKitSchemaNode } from "@formkit/core"; import type { FormKitSchemaCondition, FormKitSchemaNode } from "@formkit/core";
import type { Policy } from "@halo-dev/api-client"; import type { JsonPatchInner, Policy } from "@halo-dev/api-client";
import { consoleApiClient, coreApiClient } from "@halo-dev/api-client"; import { consoleApiClient, coreApiClient } from "@halo-dev/api-client";
import { Toast, VButton, VLoading, VModal, VSpace } from "@halo-dev/components"; import { Toast, VButton, VLoading, VModal, VSpace } from "@halo-dev/components";
import { useQuery } from "@tanstack/vue-query"; import { useQuery } from "@tanstack/vue-query";
@ -112,6 +113,7 @@ const isSubmitting = ref(false);
const handleSave = async (data: { const handleSave = async (data: {
displayName: string; displayName: string;
hidden: string;
config: Record<string, unknown>; config: Record<string, unknown>;
}) => { }) => {
try { try {
@ -127,15 +129,33 @@ const handleSave = async (data: {
body: data.config, body: data.config,
}); });
const jsonPatchInner: JsonPatchInner[] = [
{
op: "add",
path: "/spec/displayName",
value: data.displayName,
},
];
if (policy.value.metadata.labels) {
jsonPatchInner.push({
op: "add",
path: `/metadata/labels/${attachmentPolicyLabels.HIDDEN_WITH_JSON_PATCH}`,
value: data.hidden,
});
} else {
jsonPatchInner.push({
op: "add",
path: `/metadata/labels`,
value: {
[attachmentPolicyLabels.HIDDEN]: data.hidden,
},
});
}
await coreApiClient.storage.policy.patchPolicy({ await coreApiClient.storage.policy.patchPolicy({
name: policy.value.metadata.name, name: policy.value.metadata.name,
jsonPatchInner: [ jsonPatchInner: jsonPatchInner,
{
op: "add",
path: "/spec/displayName",
value: data.displayName,
},
],
}); });
} else { } else {
const { data: policies } = const { data: policies } =
@ -179,6 +199,9 @@ const handleSave = async (data: {
metadata: { metadata: {
name: "", name: "",
generateName: "attachment-policy-", generateName: "attachment-policy-",
labels: {
[attachmentPolicyLabels.HIDDEN]: data.hidden,
},
}, },
}, },
}); });
@ -232,6 +255,22 @@ const modalTitle = props.policy
name="displayName" name="displayName"
validation="required|length:0,50" validation="required|length:0,50"
></FormKit> ></FormKit>
<FormKit
name="hidden"
:value="
policy?.metadata.labels?.[attachmentPolicyLabels.HIDDEN] ||
'false'
"
type="checkbox"
:label="
$t('core.attachment.policy_editing_modal.fields.hidden.label')
"
:help="
$t('core.attachment.policy_editing_modal.fields.hidden.help')
"
on-value="true"
off-value="false"
></FormKit>
<FormKit <FormKit
v-if="formSchema && configMapGroupData" v-if="formSchema && configMapGroupData"
:value="toRaw(configMapGroupData) || {}" :value="toRaw(configMapGroupData) || {}"

View File

@ -1,4 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { attachmentPolicyLabels } from "@/constants/labels";
import type { PolicyTemplate } from "@halo-dev/api-client"; import type { PolicyTemplate } from "@halo-dev/api-client";
import { import {
IconAddCircle, IconAddCircle,
@ -9,7 +10,7 @@ import {
VTabItem, VTabItem,
VTabs, VTabs,
} from "@halo-dev/components"; } from "@halo-dev/components";
import { onMounted, ref } from "vue"; import { computed, onMounted, ref } from "vue";
import { useFetchAttachmentGroup } from "../composables/use-attachment-group"; import { useFetchAttachmentGroup } from "../composables/use-attachment-group";
import { import {
useFetchAttachmentPolicy, useFetchAttachmentPolicy,
@ -30,10 +31,6 @@ const emit = defineEmits<{
(event: "close"): void; (event: "close"): void;
}>(); }>();
const { groups, handleFetchGroups } = useFetchAttachmentGroup();
const { policies, handleFetchPolicies } = useFetchAttachmentPolicy();
const { policyTemplates } = useFetchAttachmentPolicyTemplate();
const modal = ref<InstanceType<typeof VModal> | null>(null); const modal = ref<InstanceType<typeof VModal> | null>(null);
const selectedGroupName = ref(initialGroupName || ""); const selectedGroupName = ref(initialGroupName || "");
const selectedPolicyName = ref(initialPolicyName); const selectedPolicyName = ref(initialPolicyName);
@ -41,10 +38,23 @@ const policyEditingModal = ref(false);
const groupEditingModal = ref(false); const groupEditingModal = ref(false);
const policyTemplateNameToCreate = ref(); const policyTemplateNameToCreate = ref();
const { groups, handleFetchGroups } = useFetchAttachmentGroup();
const { policyTemplates } = useFetchAttachmentPolicyTemplate();
const { policies: allPolicies, handleFetchPolicies } =
useFetchAttachmentPolicy();
const policies = computed(() => {
return allPolicies.value?.filter((policy) => {
return policy.metadata.labels?.[attachmentPolicyLabels.HIDDEN] !== "true";
});
});
onMounted(() => { onMounted(() => {
if (!selectedPolicyName.value) { const initialPolicy = policies.value?.find(
selectedPolicyName.value = policies.value?.[0].metadata.name; (p) => p.metadata.name === initialPolicyName
} );
selectedPolicyName.value =
initialPolicy?.metadata.name || policies.value?.[0]?.metadata.name;
}); });
const handleOpenCreateNewPolicyModal = async ( const handleOpenCreateNewPolicyModal = async (

View File

@ -28,3 +28,10 @@ export enum singlePageLabels {
VISIBLE = "content.halo.run/visible", VISIBLE = "content.halo.run/visible",
PHASE = "content.halo.run/phase", PHASE = "content.halo.run/phase",
} }
// attachment
export enum attachmentPolicyLabels {
// Used for ui display only
HIDDEN = "storage.halo.run/policy-hidden-in-upload-ui",
HIDDEN_WITH_JSON_PATCH = "storage.halo.run~1policy-hidden-in-upload-ui",
}

View File

@ -752,6 +752,11 @@ core:
label: Display name label: Display name
config: config:
label: Policy configuration label: Policy configuration
hidden:
label: Hide on upload screen
help: >-
If enabled, this storage policy will not be visible on the upload
screen
toast: toast:
policy_name_exists: Storage policy name already exists policy_name_exists: Storage policy name already exists
upload_modal: upload_modal:

View File

@ -504,6 +504,9 @@ core:
fields: fields:
display_name: display_name:
label: Nombre para Mostrar label: Nombre para Mostrar
hidden:
label: Hide in upload interface
help: When enabled, this storage policy will be hidden in the upload interface
upload_modal: upload_modal:
title: Cargar adjunto title: Cargar adjunto
filters: filters:

View File

@ -706,6 +706,9 @@ core:
label: 名称 label: 名称
config: config:
label: 策略配置 label: 策略配置
hidden:
label: 在上传界面隐藏
help: 开启后,会在上传界面隐藏该存储策略
toast: toast:
policy_name_exists: 存储策略名称已存在 policy_name_exists: 存储策略名称已存在
upload_modal: upload_modal:

View File

@ -691,6 +691,9 @@ core:
label: 名稱 label: 名稱
config: config:
label: 策略配置 label: 策略配置
hidden:
label: 在上傳介面隱藏
help: 開啟後,會在上傳介面隱藏該儲存策略
toast: toast:
policy_name_exists: 儲存策略名稱已存在 policy_name_exists: 儲存策略名稱已存在
upload_modal: upload_modal: