diff --git a/bcs-services/bcs-bscp/ui/src/api/client.ts b/bcs-services/bcs-bscp/ui/src/api/client.ts index f857a0d9c2..4ce106bf0d 100644 --- a/bcs-services/bcs-bscp/ui/src/api/client.ts +++ b/bcs-services/bcs-bscp/ui/src/api/client.ts @@ -160,8 +160,8 @@ export const getClientCommonlyUsedNameCheck = (bizId: string, appId: number, nam * @param ids 客户端ID * @returns */ -export const retryClients = (bizId: string, appId: number, ids: number[]) => - http.post(`/config/biz/${bizId}/apps/${appId}/clients/retry`, { client_ids: ids, all: false }); +export const retryClients = (bizId: string, appId: number, ids: number[], exclusion_operation: boolean) => + http.post(`/config/biz/${bizId}/apps/${appId}/clients/retry`, { client_ids: ids, all: false, exclusion_operation }); /** * 获取集群列表 diff --git a/bcs-services/bcs-bscp/ui/src/api/config.ts b/bcs-services/bcs-bscp/ui/src/api/config.ts index 7228993aec..31e9d4d577 100644 --- a/bcs-services/bcs-bscp/ui/src/api/config.ts +++ b/bcs-services/bcs-bscp/ui/src/api/config.ts @@ -415,10 +415,20 @@ export const updateBoundTemplateVersion = ( * @returns */ export const deleteBoundPkg = (bizId: string, appId: number, bindingId: number, template_set_ids: number[]) => - http.delete(`/config/biz/${bizId}/apps/${appId}/template_bindings/${bindingId}/template_sets`, { + http.delete(`/config/biz/${bizId}/apps/${appId}/template_bindings/${bindingId}/template_set`, { params: { template_set_ids: template_set_ids.join(',') }, }); +/** + * 删除服务下绑定的模板套餐 + * @param bizId 业务ID + * @param appId 应用ID + * @param id 配置ID + * @returns + */ +export const deleteCurrBoundPkg = (bizId: string, appId: number, id: number) => + http.delete(`/config/biz/${bizId}/apps/${appId}/template_set/${id}`); + /** * 导入非模板配置文件压缩包 * @param biz_id 业务ID diff --git a/bcs-services/bcs-bscp/ui/src/api/script.ts b/bcs-services/bcs-bscp/ui/src/api/script.ts index 26f66f4026..b30486fb60 100644 --- a/bcs-services/bcs-bscp/ui/src/api/script.ts +++ b/bcs-services/bcs-bscp/ui/src/api/script.ts @@ -61,10 +61,13 @@ export const deleteScript = (biz_id: string, id: number) => * 批量删除脚本 * @param biz_id 空间ID * @param ids 脚本ID列表 + * @param exclusion_operation 是否跨页 * @returns */ -export const batchDeleteScript = (biz_id: string, ids: number[]) => - http.post(`/config/biz/${biz_id}/hooks/batch_delete`, { force: true, ids }).then((res) => res.data); +export const batchDeleteScript = (biz_id: string, ids: number[], exclusion_operation: boolean) => + http + .post(`/config/biz/${biz_id}/hooks/batch_delete`, { force: false, ids, exclusion_operation }) + .then((res) => res.data); /** * 获取脚本标签列表 diff --git a/bcs-services/bcs-bscp/ui/src/api/template.ts b/bcs-services/bcs-bscp/ui/src/api/template.ts index e8b466fa10..2e9744ac18 100644 --- a/bcs-services/bcs-bscp/ui/src/api/template.ts +++ b/bcs-services/bcs-bscp/ui/src/api/template.ts @@ -300,11 +300,7 @@ export const downloadTemplateContent = (biz_id: string, templateSpaceId: number, * @param signature 文件内容的SHA256值 * @returns */ -export const getTemplateUploadFileIsExist = ( - bizId: string, - templateSpaceId: number, - signature: string, -) => +export const getTemplateUploadFileIsExist = (bizId: string, templateSpaceId: number, signature: string) => http .get(`/biz/${bizId}/content/metadata`, { headers: { @@ -319,11 +315,17 @@ export const getTemplateUploadFileIsExist = ( * @param biz_id 业务ID * @param template_space_id 空间ID * @param template_ids 模板ID列表 + * @param exclusion_operation 是否跨页全选 * @returns */ -export const deleteTemplate = (biz_id: string, template_space_id: number, template_ids: number[]) => +export const deleteTemplate = ( + biz_id: string, + template_space_id: number, + template_ids: number[], + exclusion_operation: boolean, +) => http.delete(`/config/biz/${biz_id}/template_spaces/${template_space_id}/templates`, { - params: { template_ids: template_ids.join(',') }, + params: { template_ids: template_ids.join(','), exclusion_operation }, }); /** @@ -341,6 +343,9 @@ export const getTemplatesDetailByIds = (biz_id: string, ids: number[]) => * @param template_space_id 空间ID * @param template_id 模板ID * @param template_set_ids 模板套餐列表 + * @param exclusion_operation 是否跨页全选 + * @param template_set_id 正在操作的套餐id,全部套餐和未指定套餐传0 + * @param no_set_specified 是否未指定套餐 * @returns */ export const addTemplateToPackage = ( @@ -348,11 +353,19 @@ export const addTemplateToPackage = ( template_space_id: number, template_ids: number[], template_set_ids: number[], + exclusion_operation: boolean, + template_set_id: number | string, + no_set_specified: boolean, ) => - http.post(`/config/biz/${biz_id}/template_spaces/${template_space_id}/templates/add_to_template_sets`, { - template_ids, - template_set_ids, - }); + http.post( + `/config/biz/${biz_id}/template_spaces/${template_space_id}/template_set/${template_set_id}/templates/add_to_template_sets`, + { + template_set_ids, + template_ids, + exclusion_operation, + no_set_specified, + }, + ); /** * 将模版移出套餐(多个模板移出多个套餐) @@ -360,6 +373,9 @@ export const addTemplateToPackage = ( * @param template_space_id 空间ID * @param template_id 模板ID * @param template_set_ids 模板套餐列表 + * @param exclusion_operation 是否跨页全选 + * @param template_set_id 正在操作的套餐id,全部套餐和未指定套餐传0 + * @param no_set_specified 是否未指定套餐 * @returns */ export const moveOutTemplateFromPackage = ( @@ -367,11 +383,17 @@ export const moveOutTemplateFromPackage = ( template_space_id: number, template_ids: number[], template_set_ids: number[], + exclusion_operation: boolean, + template_set_id: number | string, ) => - http.post(`/config/biz/${biz_id}/template_spaces/${template_space_id}/templates/delete_from_template_sets`, { - template_ids, - template_set_ids, - }); + http.post( + `/config/biz/${biz_id}/template_spaces/${template_space_id}/template_set/${template_set_id}/templates/delete_from_template_sets`, + { + template_ids, + template_set_ids, + exclusion_operation, + }, + ); /** * 查询单个模板被套餐引用详情 @@ -699,10 +721,16 @@ export const importTemplateBatchAdd = ( * @param biz_id 业务ID * @param template_space_id 空间id * @param configData 配置列表 + * @param exclusion_operation // 是否跨页 + * @param template_set_id // 正在操作的套餐id,全部套餐和未指定传0 + * @param no_set_specified // 是否未指定套餐 * @returns */ export const batchEditTemplatePermission = (biz_id: string, query: any) => - http.post(`/config/biz/${biz_id}/templates/batch_update_templates_permissions`, query); + http.post( + `/config/biz/${biz_id}/template_spaces/${query.template_space_id}/template_set/${query.template_set_id}/templates/batch_update_templates_permissions`, + query, + ); /** * 获取配置模板配置项元信息 diff --git a/bcs-services/bcs-bscp/ui/src/api/variable.ts b/bcs-services/bcs-bscp/ui/src/api/variable.ts index 7ef1dd6ebf..afd2f5dd20 100644 --- a/bcs-services/bcs-bscp/ui/src/api/variable.ts +++ b/bcs-services/bcs-bscp/ui/src/api/variable.ts @@ -46,9 +46,10 @@ export const deleteVariable = (biz_id: string, template_variable_id: number) => * 批量删除变量 * @param bizId 业务ID * @param ids 变量ID列表 + * @param exclusion_operation 是否跨页 */ -export const batchDeleteVariable = (biz_id: string, ids: number[]) => - http.post(`/config/biz/${biz_id}/template_variables/batch_delete`, { ids }); +export const batchDeleteVariable = (biz_id: string, ids: number[], exclusion_operation: boolean) => + http.post(`/config/biz/${biz_id}/template_variables/batch_delete`, { ids, exclusion_operation }); /** * 获取未命名版本服务变量列表 diff --git a/bcs-services/bcs-bscp/ui/src/components/across-check-table-tip.vue b/bcs-services/bcs-bscp/ui/src/components/across-check-table-tip.vue new file mode 100644 index 0000000000..652d794097 --- /dev/null +++ b/bcs-services/bcs-bscp/ui/src/components/across-check-table-tip.vue @@ -0,0 +1,89 @@ + + + diff --git a/bcs-services/bcs-bscp/ui/src/components/across-check.vue b/bcs-services/bcs-bscp/ui/src/components/across-check.vue new file mode 100644 index 0000000000..aae99053cd --- /dev/null +++ b/bcs-services/bcs-bscp/ui/src/components/across-check.vue @@ -0,0 +1,172 @@ + + + diff --git a/bcs-services/bcs-bscp/ui/src/components/across-checkbox.vue b/bcs-services/bcs-bscp/ui/src/components/across-checkbox.vue new file mode 100644 index 0000000000..b9c75f7884 --- /dev/null +++ b/bcs-services/bcs-bscp/ui/src/components/across-checkbox.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/bcs-services/bcs-bscp/ui/src/i18n/en-us.ts b/bcs-services/bcs-bscp/ui/src/i18n/en-us.ts index 5f1cd9aa56..829f537969 100644 --- a/bcs-services/bcs-bscp/ui/src/i18n/en-us.ts +++ b/bcs-services/bcs-bscp/ui/src/i18n/en-us.ts @@ -1011,4 +1011,10 @@ export default { '组件类型 / 版本分布': 'Component type / Version distribution', 下钻: 'Drill down', 总和: 'Total', + + // 跨页全选 + 跨页全选: 'AcrossChecked', + 已选择: '{count} item selected', + 选择所有: 'select all {count} items', + 取消选择所有数据: 'Unselect all items', }; diff --git a/bcs-services/bcs-bscp/ui/src/i18n/zh-cn.ts b/bcs-services/bcs-bscp/ui/src/i18n/zh-cn.ts index 339923d3b3..5a68f791fd 100644 --- a/bcs-services/bcs-bscp/ui/src/i18n/zh-cn.ts +++ b/bcs-services/bcs-bscp/ui/src/i18n/zh-cn.ts @@ -1016,4 +1016,10 @@ export default { '组件类型 / 版本分布': '组件类型 / 版本分布', 下钻: '下钻', 总和: '总和', + + // 跨页全选 + 跨页全选: '跨页全选', + 已选择: '已选择 {count} 条数据', + 选择所有: '选择所有 {count} 条', + 取消选择所有数据: '取消选择所有数据', }; diff --git a/bcs-services/bcs-bscp/ui/src/store/template.ts b/bcs-services/bcs-bscp/ui/src/store/template.ts index 27087bad42..36c330fa7a 100644 --- a/bcs-services/bcs-bscp/ui/src/store/template.ts +++ b/bcs-services/bcs-bscp/ui/src/store/template.ts @@ -24,6 +24,10 @@ export default defineStore('template', () => { const versionListPageShouldOpenView = ref(false); // 置顶id const topIds = ref([]); + // 表格是否跨页选择 + const isAcrossChecked = ref(false); + // 表格数据总数 + const dataCount = ref(0); return { templateSpaceList, @@ -36,5 +40,7 @@ export default defineStore('template', () => { versionListPageShouldOpenEdit, versionListPageShouldOpenView, topIds, + isAcrossChecked, + dataCount, }; }); diff --git a/bcs-services/bcs-bscp/ui/src/utils/hooks/use-table-acrosscheck-common.ts b/bcs-services/bcs-bscp/ui/src/utils/hooks/use-table-acrosscheck-common.ts new file mode 100644 index 0000000000..55e18ceb5b --- /dev/null +++ b/bcs-services/bcs-bscp/ui/src/utils/hooks/use-table-acrosscheck-common.ts @@ -0,0 +1,185 @@ +// useTableAcrossCheckCommon.ts +import { h, Ref, ref } from 'vue'; +import AcrossCheck from '../../components/across-check.vue'; +import TableTip from '../../components/across-check-table-tip.vue'; +import CheckType from '../../../types/across-checked'; + +export interface IAcrossCheckConfig { + dataSource: Ref; + curPageData: Ref; + rowKey?: string[]; // 每行数据唯一标识 + crossPageSelect: Ref; +} +export default function useTableAcrossCheckCommon({ + dataSource, + curPageData, + rowKey = ['name', 'id'], + crossPageSelect, +}: IAcrossCheckConfig) { + const selectType = ref(CheckType.Uncheck); + const selections = ref<{ [key: string]: any }[]>([]); + + const getDataLength = () => { + // 根据传入的是数字还是数组,返回不同的长度 + if (typeof dataSource.value === 'number') { + return dataSource.value; + } + return dataSource.value.length; + }; + + const renderSelection = () => { + // 渲染表头 + return h(AcrossCheck, { + value: selectType.value, + disabled: !getDataLength(), + crossPageSelect: crossPageSelect.value, + onChange: handleSelectTypeChange, + }); + }; + const renderTableTip = () => { + // 表格中间数据提示 + return h(TableTip, { + dataLength: getDataLength(), + selectionsLength: selections.value.length, + isFullDataMode: typeof dataSource.value !== 'number', + selectType: selectType.value, + crossPageSelect: crossPageSelect.value, + handleSelectTypeChange, + handleClearSelection, + }); + }; + + // 表头全选事件 + const handleSelectTypeChange = (value: number) => { + switch (value) { + case CheckType.Uncheck: + handleClearSelection(); + break; + case CheckType.Checked: + handleSelectCurrentPage(); + break; + case CheckType.AcrossChecked: + handleSelectionAll(); + break; + } + }; + // 当前页全选 + const handleSelectCurrentPage = () => { + selectType.value = CheckType.Checked; + selections.value = [...curPageData.value]; + }; + // 跨页全选 + const handleSelectionAll = () => { + selectType.value = CheckType.AcrossChecked; + // number非全量,反向传递 + selections.value = typeof dataSource.value === 'number' ? [] : [...dataSource.value]; + }; + // 清空全选 + const handleClearSelection = () => { + selectType.value = CheckType.Uncheck; + selections.value = []; + }; + + // 表格行勾选后重置状态 + const handleSetSelectType = () => { + // 全量数据 + if (typeof dataSource.value !== 'number') { + if (selections.value.length === 0) { + selectType.value = CheckType.Uncheck; + } else if ( + selections.value.length < curPageData.value.length && + [CheckType.Checked, CheckType.Uncheck].includes(selectType.value) + ) { + // 从当前页全选 -> 当前页半选 + selectType.value = CheckType.HalfChecked; + } else if ( + selections.value.length === curPageData.value.length && + ![CheckType.HalfAcrossChecked, CheckType.AcrossChecked].includes(selectType.value) + ) { + selectType.value = CheckType.Checked; + } else if (selections.value.length < dataSource.value.length && selectType.value === CheckType.AcrossChecked) { + // 从跨页全选 -> 跨页半选 + selectType.value = CheckType.HalfAcrossChecked; + } else if (selections.value.length === dataSource.value.length) { + selectType.value = CheckType.AcrossChecked; + } + } else { + // 非全量数据 + if ( + (selections.value.length === 0 && [CheckType.Checked, CheckType.HalfChecked].includes(selectType.value)) || + (selections.value.length === dataSource.value && + [CheckType.HalfAcrossChecked, CheckType.AcrossChecked].includes(selectType.value)) + ) { + // 取消全选状态 + selectType.value = CheckType.Uncheck; + selections.value = []; + } else if ( + selections.value.length < curPageData.value.length && + [CheckType.Checked, CheckType.Uncheck].includes(selectType.value) + ) { + // 从当前页全选/空数据 -> 当前页半选 + selectType.value = CheckType.HalfChecked; + } else if ( + selections.value.length === curPageData.value.length && + ![CheckType.HalfAcrossChecked, CheckType.AcrossChecked].includes(selectType.value) + ) { + // 当前页全选 + selectType.value = CheckType.Checked; + } else if (selections.value.length < dataSource.value && selectType.value === CheckType.AcrossChecked) { + // 跨页半选 + selectType.value = CheckType.HalfAcrossChecked; + } else if (selections.value.length === 0 && [CheckType.HalfAcrossChecked].includes(selectType.value)) { + // 跨页全选 + selectType.value = CheckType.AcrossChecked; + } + } + }; + + // 当前行选中事件 + const handleRowCheckChange = (value: boolean, row: any) => { + const index = selections.value.findIndex((item: { [key: string]: any }) => + rowKey.every((key) => item[key] === row[key]), + ); + // 全量数据 + if (typeof dataSource.value !== 'number') { + if (value && index === -1) { + selections.value.push(row); + } else if (!value && index > -1) { + selections.value.splice(index, 1); + } + } else { + if (value && index === -1 && [CheckType.Uncheck, CheckType.HalfChecked].includes(selectType.value)) { + // 非跨页选择时,勾选数据正常push + selections.value.push(row); + } else if (!value && index > -1 && [CheckType.Checked, CheckType.HalfChecked].includes(selectType.value)) { + // 非跨页选择时,取消勾选数据正常splice + selections.value.splice(index, 1); + } else if ( + !value && + index === -1 && + [CheckType.AcrossChecked, CheckType.HalfAcrossChecked].includes(selectType.value) + ) { + // 跨页全选/半选时,取消勾选数据push + selections.value.push(row); + } else if ( + value && + index > -1 && + [CheckType.AcrossChecked, CheckType.HalfAcrossChecked].includes(selectType.value) + ) { + // 跨页半选时,勾选数据splice + selections.value.splice(index, 1); + } + } + handleSetSelectType(); + }; + + return { + selectType, + selections, + renderSelection, + renderTableTip, + handleSelectionAll, + handleClearSelection, + handleRowCheckChange, + }; +} diff --git a/bcs-services/bcs-bscp/ui/src/utils/hooks/use-table-acrosscheck-fulldata.ts b/bcs-services/bcs-bscp/ui/src/utils/hooks/use-table-acrosscheck-fulldata.ts new file mode 100644 index 0000000000..fc0d8af3d6 --- /dev/null +++ b/bcs-services/bcs-bscp/ui/src/utils/hooks/use-table-acrosscheck-fulldata.ts @@ -0,0 +1,27 @@ +import { Ref, ref } from 'vue'; +import useTableAcrossCheckCommon from './use-table-acrosscheck-common'; + +export interface IAcrossCheckConfig { + tableData: Ref; // 全量数据 + curPageData: Ref; // 当前页数据 + rowKey?: string[]; // 每行数据唯一标识 + crossPageSelect: Ref; // 是否提供全选/跨页全选功能 +} +// 表格跨页全选功能 +export default function useTableAcrossCheck({ + tableData, + curPageData, + rowKey = ['name', 'id'], + crossPageSelect = ref(true), +}: IAcrossCheckConfig) { + const result = useTableAcrossCheckCommon({ + dataSource: tableData, + curPageData, + rowKey, + crossPageSelect, + }); + + return { + ...result, + }; +} diff --git a/bcs-services/bcs-bscp/ui/src/utils/hooks/use-table-acrosscheck.ts b/bcs-services/bcs-bscp/ui/src/utils/hooks/use-table-acrosscheck.ts new file mode 100644 index 0000000000..c15f0fc3c1 --- /dev/null +++ b/bcs-services/bcs-bscp/ui/src/utils/hooks/use-table-acrosscheck.ts @@ -0,0 +1,26 @@ +import { Ref, ref } from 'vue'; +import useTableAcrossCheckCommon from './use-table-acrosscheck-common'; + +export interface IAcrossCheckConfig { + dataCount: Ref; // 可选的数据总数,不含禁用状态 + curPageData: Ref; // 当前页数据 + rowKey?: string[]; // 每行数据唯一标识;需要在每行数据的第一子层级,暂不支持递归查找 + crossPageSelect: Ref; // 是否提供全选/跨页全选功能 +} +// 表格跨页全选功能 +export default function useTableAcrossCheck({ + dataCount, + curPageData, + rowKey = ['name', 'id'], + crossPageSelect = ref(true), +}: IAcrossCheckConfig) { + const result = useTableAcrossCheckCommon({ + dataSource: dataCount, + curPageData, + rowKey, + crossPageSelect, + }); + return { + ...result, + }; +} diff --git a/bcs-services/bcs-bscp/ui/src/views/space/client/search/components/batch-retry-btn.vue b/bcs-services/bcs-bscp/ui/src/views/space/client/search/components/batch-retry-btn.vue index 42718c6afd..1fdcff07ac 100644 --- a/bcs-services/bcs-bscp/ui/src/views/space/client/search/components/batch-retry-btn.vue +++ b/bcs-services/bcs-bscp/ui/src/views/space/client/search/components/batch-retry-btn.vue @@ -1,11 +1,14 @@ @@ -20,6 +23,7 @@ bkBizId: string; appId: number; selections: { id: number; uid: string; current_release_name: string; target_release_name: string }[]; + isAcrossChecked: boolean; }>(); const emits = defineEmits(['retried']); diff --git a/bcs-services/bcs-bscp/ui/src/views/space/client/search/components/retry-btn.vue b/bcs-services/bcs-bscp/ui/src/views/space/client/search/components/retry-btn.vue index 26d3034f51..290dbff9d9 100644 --- a/bcs-services/bcs-bscp/ui/src/views/space/client/search/components/retry-btn.vue +++ b/bcs-services/bcs-bscp/ui/src/views/space/client/search/components/retry-btn.vue @@ -8,6 +8,7 @@ :is-show="isRetryOpen" :is-batch="false" :selections="selections" + :is-across-checked="false" @close="isRetryOpen = false" @retried="emits('retried', $event)" /> diff --git a/bcs-services/bcs-bscp/ui/src/views/space/client/search/components/retry-dialog.vue b/bcs-services/bcs-bscp/ui/src/views/space/client/search/components/retry-dialog.vue index bd4b39e7a9..79e5c10760 100644 --- a/bcs-services/bcs-bscp/ui/src/views/space/client/search/components/retry-dialog.vue +++ b/bcs-services/bcs-bscp/ui/src/views/space/client/search/components/retry-dialog.vue @@ -14,7 +14,7 @@
{{ title }}
-
+
(); const emits = defineEmits(['close', 'retried']); @@ -74,7 +75,7 @@ pending.value = true; try { const ids = props.selections.map((item) => item.id); - await retryClients(props.bkBizId, props.appId, ids); + await retryClients(props.bkBizId, props.appId, ids, props.isAcrossChecked); close(); BkMessage({ theme: 'success', diff --git a/bcs-services/bcs-bscp/ui/src/views/space/client/search/index.vue b/bcs-services/bcs-bscp/ui/src/views/space/client/search/index.vue index fcce742726..601e3ac9b1 100644 --- a/bcs-services/bcs-bscp/ui/src/views/space/client/search/index.vue +++ b/bcs-services/bcs-bscp/ui/src/views/space/client/search/index.vue @@ -4,7 +4,12 @@
- + -