From a9b3ae7aa75ea2598e1ca3f6dc07f3d12f8e4eae Mon Sep 17 00:00:00 2001 From: 3octaves <873551943@qq.com> Date: Fri, 13 Sep 2024 18:23:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E6=9D=83=E9=99=90=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E9=A1=B5=20#6905?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dbm-ui/frontend/.vscode/settings.json | 69 +- dbm-ui/frontend/package.json | 2 +- .../frontend/src/common/const/dbOperations.ts | 33 + dbm-ui/frontend/src/common/const/index.ts | 1 + .../src/components/cluster-selector/Index.vue | 59 +- .../components/tendbha/Index.vue | 2 +- .../components/instance-selector/Index.vue | 190 +++++- .../components/tendb-ha-host/Index.vue | 274 ++++++++ .../components/tendb-ha-host/table/Index.vue | 340 ++++++++++ .../tendb-ha-host/table/useTableData.ts | 107 +++ .../components/tendb-ha-host/useTopoData.ts | 107 +++ .../components/tendb-single-host/Index.vue | 274 ++++++++ .../tendb-single-host/table/Index.vue | 340 ++++++++++ .../tendb-single-host/table/useTableData.ts | 107 +++ .../tendb-single-host/useTopoData.ts | 107 +++ .../src/components/mult-line-text/Index.vue | 34 +- .../components/module-group/Mysql.vue | 7 + .../components/module-group/TendbCluster.vue | 7 + dbm-ui/frontend/src/locales/zh-cn.json | 21 + .../services/model/mysql/tendbHaMachine.ts | 151 +++++ .../model/mysql/tendbSingleMachine.ts | 151 +++++ .../services/model/spider/spiderMachine.ts | 12 +- .../src/services/source/mysqlPermission.ts | 106 +++ .../frontend/src/services/source/tendbha.ts | 23 + .../src/services/source/tendbsingle.ts | 23 + dbm-ui/frontend/src/types/router.d.ts | 1 + .../mysql/permission-rule/common/const.ts | 31 - .../permission-rule/components/CreateRule.vue | 22 +- .../db-manage/mysql/permission-rule/index.vue | 10 +- .../src/views/db-manage/mysql/routes.ts | 11 + .../tendb-cluster/permission-list/Index.vue | 10 +- .../tendb-cluster/permission/Index.vue | 10 +- .../tendb-cluster/permission/common/consts.ts | 9 - .../permission/components/CreateRule.vue | 20 +- .../views/db-manage/tendb-cluster/routes.ts | 12 + .../src/views/permission-retrieve/Index.vue | 152 +++++ .../components/options/Index.vue | 470 +++++++++++++ .../options/components/BatchInput.vue | 122 ++++ .../components/result/Index.vue | 627 ++++++++++++++++++ 39 files changed, 3893 insertions(+), 161 deletions(-) create mode 100644 dbm-ui/frontend/src/common/const/dbOperations.ts create mode 100644 dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/Index.vue create mode 100644 dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/table/Index.vue create mode 100644 dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/table/useTableData.ts create mode 100644 dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/useTopoData.ts create mode 100644 dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/Index.vue create mode 100644 dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/table/Index.vue create mode 100644 dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/table/useTableData.ts create mode 100644 dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/useTopoData.ts create mode 100644 dbm-ui/frontend/src/services/model/mysql/tendbHaMachine.ts create mode 100644 dbm-ui/frontend/src/services/model/mysql/tendbSingleMachine.ts create mode 100644 dbm-ui/frontend/src/services/source/mysqlPermission.ts create mode 100644 dbm-ui/frontend/src/views/permission-retrieve/Index.vue create mode 100644 dbm-ui/frontend/src/views/permission-retrieve/components/options/Index.vue create mode 100644 dbm-ui/frontend/src/views/permission-retrieve/components/options/components/BatchInput.vue create mode 100644 dbm-ui/frontend/src/views/permission-retrieve/components/result/Index.vue diff --git a/dbm-ui/frontend/.vscode/settings.json b/dbm-ui/frontend/.vscode/settings.json index 1f2666a091..69bce4736a 100644 --- a/dbm-ui/frontend/.vscode/settings.json +++ b/dbm-ui/frontend/.vscode/settings.json @@ -1,60 +1,19 @@ { - "eslint.validate": ["javascript", "javascriptreact", "html", "vue"], - "eslint.alwaysShowStatus": true, + "files.eol": "\n", + "editor.unfoldOnClickAfterEndOfLine": true, + "editor.tabSize": 2, + "diffEditor.ignoreTrimWhitespace": false, + "typescript.updateImportsOnFileMove.enabled": "always", + "explorer.confirmDelete": false, + "explorer.confirmDragAndDrop": false, + "editor.fontLigatures": false, + "editor.fontVariations": false, + "i18n-lazyer.defaultFolder": "\\src\\locales", + "search.smartCase": true, "editor.codeActionsOnSave": { "source.fixAll": "explicit" }, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[javascriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[typescriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[json]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[html]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[css]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, + // "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, - "editor.tabSize": 2, - "css.validate": false, - "stylelint.enable": true, - "scss.validate": false, - "stylelint.validate": ["css", "scss", "vue"], - "preCI.localCodeCheck.filter.whitePath": "", - "preCI.localCodeCheck.filter.skipPath": "", - "preCI.localCodeCheck.checkerSet.JS": ["standard_js"], - "peacock.remoteColor": "#45e35f", - "bk-code-ai.enable": false, - "workbench.colorCustomizations": { - "activityBar.activeBackground": "#71ea85", - "activityBar.background": "#71ea85", - "activityBar.foreground": "#15202b", - "activityBar.inactiveForeground": "#15202b99", - "activityBarBadge.background": "#8874ea", - "activityBarBadge.foreground": "#15202b", - "commandCenter.border": "#15202b99", - "sash.hoverBorder": "#71ea85", - "statusBar.background": "#45e35f", - "statusBar.foreground": "#15202b", - "statusBarItem.hoverBackground": "#20d53e", - "statusBarItem.remoteBackground": "#45e35f", - "statusBarItem.remoteForeground": "#15202b", - "titleBar.activeBackground": "#45e35f", - "titleBar.activeForeground": "#15202b", - "titleBar.inactiveBackground": "#45e35f99", - "titleBar.inactiveForeground": "#15202b99" - }, - "typescript.tsdk": "node_modules/typescript/lib" -} \ No newline at end of file + "editor.defaultFormatter": "esbenp.prettier-vscode" +} diff --git a/dbm-ui/frontend/package.json b/dbm-ui/frontend/package.json index 1ba0f12229..6697f96358 100644 --- a/dbm-ui/frontend/package.json +++ b/dbm-ui/frontend/package.json @@ -27,7 +27,7 @@ "@icon-cool/bk-icon-bk-biz-components": "0.0.4", "@vueuse/core": "^11.0.3", "axios": "^1.7.7", - "bkui-vue": "2.0.1-beta.69", + "bkui-vue": "^2.0.1-beta.69", "date-fns": "3.6.0", "dayjs": "^1.11.13", "html-to-image": "1.11.11", diff --git a/dbm-ui/frontend/src/common/const/dbOperations.ts b/dbm-ui/frontend/src/common/const/dbOperations.ts new file mode 100644 index 0000000000..a8f059c42f --- /dev/null +++ b/dbm-ui/frontend/src/common/const/dbOperations.ts @@ -0,0 +1,33 @@ +export const MysqlDbOperations = { + dml: ['select', 'insert', 'update', 'delete', 'show view'], + ddl: [ + 'create', + 'alter', + 'drop', + 'index', + 'create view', + 'execute', + 'trigger', + 'event', + 'create routine', + 'alter routine', + 'references', + 'create temporary tables', + ], + glob: ['file', 'reload', 'show databases', 'process', 'replication slave', 'replication client'], +}; + +export const MysqlDdlSensitiveWords = [ + 'trigger', + 'event', + 'create routine', + 'alter routine', + 'references', + 'create temporary tables', +]; + +export const TendbClusterDbOperations = { + dml: ['select', 'insert', 'update', 'delete'], + ddl: ['execute'], + glob: ['file', 'reload', 'process', 'show databases'], +}; diff --git a/dbm-ui/frontend/src/common/const/index.ts b/dbm-ui/frontend/src/common/const/index.ts index 658513ffc2..453a695286 100644 --- a/dbm-ui/frontend/src/common/const/index.ts +++ b/dbm-ui/frontend/src/common/const/index.ts @@ -3,6 +3,7 @@ export * from './clusterInsStatus'; export * from './clusterTypeInfos'; export * from './clusterTypes'; export * from './confLevels'; +export * from './dbOperations'; export * from './dbSysExclude'; export * from './dbTypeInfos'; export * from './dbTypes'; diff --git a/dbm-ui/frontend/src/components/cluster-selector/Index.vue b/dbm-ui/frontend/src/components/cluster-selector/Index.vue index b4b1491a24..5294cce8fc 100644 --- a/dbm-ui/frontend/src/components/cluster-selector/Index.vue +++ b/dbm-ui/frontend/src/components/cluster-selector/Index.vue @@ -101,13 +101,21 @@ - - {{ t('确定') }} - + + + + + + {{ t('确定') }} + + @@ -200,6 +208,7 @@ clusterTypes: string[]; tabListConfig?: Record; onlyOneType?: boolean; + disableDialogSubmitMethod?: (hostList: Array) => string | boolean; } interface Emits { @@ -214,6 +223,7 @@ default: false, }); + const slots = useSlots(); const copy = useCopy(); const { dialogWidth } = useSelectorDialogWidth(); const { t } = useI18n(); @@ -392,6 +402,41 @@ // 选中结果是否为空 const isEmpty = computed(() => _.every(Object.values(selectedMap.value), (item) => Object.keys(item).length < 1)); + const selectedClusterList = computed(() => + Object.values(selectedMap.value).reduce((prevList, selectedItem) => { + const clusterList = Object.values(selectedItem).map((clusterItem) => clusterItem.master_domain); + prevList.push(...clusterList); + return prevList; + }, []), + ); + + const submitButtonDisabledInfo = computed(() => { + const info = { + disabled: false, + tooltips: { + disabled: true, + content: '', + }, + }; + + if (isEmpty.value) { + info.disabled = true; + info.tooltips.disabled = false; + info.tooltips.content = t('请选择集群'); + return info; + } + + const checkValue = props?.disableDialogSubmitMethod + ? props.disableDialogSubmitMethod(selectedClusterList.value) + : false; + if (checkValue) { + info.disabled = true; + info.tooltips.disabled = false; + info.tooltips.content = _.isString(checkValue) ? checkValue : t('无法保存'); + } + return info; + }); + watch( () => props.clusterTypes, (types) => { diff --git a/dbm-ui/frontend/src/components/cluster-selector/components/tendbha/Index.vue b/dbm-ui/frontend/src/components/cluster-selector/components/tendbha/Index.vue index 52b74636f6..c27477740e 100644 --- a/dbm-ui/frontend/src/components/cluster-selector/components/tendbha/Index.vue +++ b/dbm-ui/frontend/src/components/cluster-selector/components/tendbha/Index.vue @@ -28,7 +28,7 @@ :is-anomalies="isAnomalies" :is-searching="searchValue.length > 0" :max-height="528" - :pagination="pagination.count < 10 ? false : pagination" + :pagination="pagination" remote-pagination :row-class="getRowClass" row-style="cursor: pointer" diff --git a/dbm-ui/frontend/src/components/instance-selector/Index.vue b/dbm-ui/frontend/src/components/instance-selector/Index.vue index 4add7352cc..8dbe5dd10b 100644 --- a/dbm-ui/frontend/src/components/instance-selector/Index.vue +++ b/dbm-ui/frontend/src/components/instance-selector/Index.vue @@ -69,15 +69,16 @@ - + + + + {{ t('确定') }} @@ -201,8 +202,8 @@ getSingleClusterList, getSqlServerInstanceList as getSqlServerSingleInstanceList, } from '@services/source/sqlserverSingleCluster'; - import { getTendbhaInstanceList } from '@services/source/tendbha'; - import { getTendbsingleInstanceList } from '@services/source/tendbsingle'; + import { getTendbhaInstanceList, getTendbHaMachineList } from '@services/source/tendbha'; + import { getTendbsingleInstanceList, getTendbSingleMachineList } from '@services/source/tendbsingle'; import { ClusterTypes } from '@common/const'; @@ -217,6 +218,8 @@ import SqlServerContent from './components/sqlserver/Index.vue'; import TendbClusterContent from './components/tendb-cluster/Index.vue'; import TendbClusterHostContent from './components/tendb-cluster-host/Index.vue'; + import TendbHaHostContent from './components/tendb-ha-host/Index.vue'; + import TendbSingleHostContent from './components/tendb-single-host/Index.vue'; export type TableSetting = ReturnType; @@ -269,12 +272,21 @@ type RedisHostModel = ServiceReturnType['results'][number]; interface Props { - clusterTypes: (ClusterTypes | 'TendbClusterHost' | 'RedisHost' | 'mongoCluster')[]; + clusterTypes: ( + | ClusterTypes + | 'TendbClusterHost' + | 'RedisHost' + | 'mongoCluster' + | 'TendbSingleHost' + | 'TendbHaHost' + )[]; tabListConfig?: Record; selected?: InstanceSelectorValues; unqiuePanelValue?: boolean; unqiuePanelTips?: string; hideManualInput?: boolean; + onlyOneType?: boolean; + disableDialogSubmitMethod?: (hostList: Array) => string | boolean; } interface Emits { @@ -288,6 +300,8 @@ unqiuePanelValue: false, unqiuePanelTips: t('仅可选择一种实例类型'), hideManualInput: false, + onlyOneType: false, + disableDialogSubmitMethod: () => false, }); const emits = defineEmits(); @@ -300,6 +314,8 @@ default: false, }); + const slots = useSlots(); + const tabListMap: Record = { [ClusterTypes.REDIS]: [ { @@ -679,6 +695,96 @@ content: ManualInputContent, }, ], + TendbSingleHost: [ + { + id: 'TendbSingleHost', + name: t('MySQL 单节点'), + topoConfig: { + getTopoList: queryMysqlCluster, + }, + tableConfig: { + getTableList: getTendbSingleMachineList, + firsrColumn: { + label: 'IP', + field: 'ip', + role: '', + }, + columnsChecked: ['ip', 'related_instances', 'cloud_area', 'alive', 'host_name', 'os_name'], + }, + previewConfig: { + displayKey: 'ip', + }, + content: TendbSingleHostContent, + }, + { + id: 'manualInput', + name: t('手动输入'), + tableConfig: { + getTableList: getTendbSingleMachineList, + firsrColumn: { + label: 'IP', + field: 'ip', + role: '', + }, + columnsChecked: ['ip', 'related_instances', 'cloud_area', 'alive', 'host_name', 'os_name'], + }, + manualConfig: { + checkInstances: getTendbSingleMachineList, + checkType: 'ip', + checkKey: 'ip', + activePanelId: 'TendbClusterHost', + }, + previewConfig: { + displayKey: 'ip', + }, + content: ManualInputHostContent, + }, + ], + TendbHaHost: [ + { + id: 'TendbHaHost', + name: t('MySQL 主从'), + topoConfig: { + getTopoList: queryMysqlCluster, + }, + tableConfig: { + getTableList: getTendbHaMachineList, + firsrColumn: { + label: 'IP', + field: 'ip', + role: '', + }, + columnsChecked: ['ip', 'cloud_area', 'alive', 'host_name', 'os_name'], + }, + previewConfig: { + displayKey: 'ip', + }, + content: TendbHaHostContent, + }, + { + id: 'manualInput', + name: t('手动输入'), + tableConfig: { + getTableList: getTendbHaMachineList, + firsrColumn: { + label: 'IP', + field: 'ip', + role: '', + }, + columnsChecked: ['ip', 'cloud_area', 'alive', 'host_name', 'os_name'], + }, + manualConfig: { + checkInstances: getTendbHaMachineList, + checkType: 'ip', + checkKey: 'ip', + activePanelId: 'TendbClusterHost', + }, + previewConfig: { + displayKey: 'ip', + }, + content: ManualInputHostContent, + }, + ], }; const panelTabActive = ref(''); @@ -753,6 +859,45 @@ const isEmpty = computed(() => Object.values(lastValues).every((values) => values.length < 1)); const renderCom = computed(() => (activePanelObj.value ? activePanelObj.value.content : 'div')); + const lastHostList = computed(() => + Object.values(lastValues).reduce((prevList, hostListItem) => { + const ipList = hostListItem.map((listItem) => listItem.ip); + prevList.push(...ipList); + return prevList; + }, []), + ); + + const submitButtonDisabledInfo = computed(() => { + const info = { + disabled: false, + tooltips: { + disabled: true, + content: '', + }, + }; + + if (isEmpty.value) { + info.disabled = true; + info.tooltips.disabled = false; + info.tooltips.content = panelTabActive.value.includes('Host') ? t('请选择主机') : t('请选择实例'); + return info; + } + + const hostList = Object.values(lastValues).reduce((prevList, hostListItem) => { + const ipList = hostListItem.map((listItem) => listItem.ip); + prevList.push(...ipList); + return prevList; + }, []); + + const checkValue = props.disableDialogSubmitMethod(hostList); + if (checkValue) { + info.disabled = true; + info.tooltips.disabled = false; + info.tooltips.content = _.isString(checkValue) ? checkValue : t('无法保存'); + } + return info; + }); + let isInnerChange = false; watch( @@ -783,9 +928,32 @@ const handleChangePanel = (obj: PanelListItem) => { activePanelObj.value = obj; + if (props.onlyOneType) { + const initValues = Object.keys(lastValues).reduce>( + (results, id) => + Object.assign({}, results, { + [id]: [], + }), + {}, + ); + Object.assign(lastValues, initValues); + } }; - const handleChange = (values: Props['selected']) => { + const handleChange = (values: Props['selected'] = {}) => { + // 如果只允许选一种类型, 则清空非当前类型的选中列表 + // 如果是勾选的取消全选,则忽略 + const currentKey = panelTabActive.value; + if (props.onlyOneType && values[currentKey].length > 0) { + Object.keys(lastValues).forEach((key) => { + if (key !== currentKey) { + lastValues[key] = []; + } else { + lastValues[key] = values[key]; + } + }); + return; + } Object.assign(lastValues, values); }; diff --git a/dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/Index.vue b/dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/Index.vue new file mode 100644 index 0000000000..264eac830a --- /dev/null +++ b/dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/Index.vue @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + {{ item.obj === 'biz' ? t('业') : t('集') }} + + + {{ item.name }} + + + {{ item.count }} + + + + + + + + + + + + + + + + + + + + diff --git a/dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/table/Index.vue b/dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/table/Index.vue new file mode 100644 index 0000000000..e713816000 --- /dev/null +++ b/dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/table/Index.vue @@ -0,0 +1,340 @@ + + + + + + + + + + + + + diff --git a/dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/table/useTableData.ts b/dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/table/useTableData.ts new file mode 100644 index 0000000000..42179d9869 --- /dev/null +++ b/dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/table/useTableData.ts @@ -0,0 +1,107 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * + * Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at https://opensource.org/licenses/MIT + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for + * the specific language governing permissions and limitations under the License. + */ + +import type { ComponentInternalInstance, Ref } from 'vue'; +import { useRequest } from 'vue-request'; + +import { useGlobalBizs } from '@stores'; + +/** + * 处理集群列表数据 + */ +export function useTableData(role?: Ref, clusterId?: Ref) { + const { currentBizId } = useGlobalBizs(); + const currentInstance = getCurrentInstance() as ComponentInternalInstance & { + proxy: { + getTableList: (params: any) => Promise; + }; + }; + + const tableData = shallowRef([]); + const isAnomalies = ref(false); + const pagination = reactive({ + count: 0, + current: 1, + limit: 10, + limitList: [10, 20, 50, 100], + align: 'right', + layout: ['total', 'limit', 'list'], + }); + const searchValue = ref(''); + + const { run: getTableListRun, loading: isLoading } = useRequest(currentInstance.proxy.getTableList, { + manual: true, + onSuccess(data) { + tableData.value = data.results; + pagination.count = data.count; + isAnomalies.value = false; + }, + onError() { + tableData.value = []; + pagination.count = 0; + isAnomalies.value = true; + }, + }); + + watch(searchValue, () => { + setTimeout(() => { + handleChangePage(1); + }); + }); + + const generateParams = () => { + const params = { + bk_biz_id: currentBizId, + ip: searchValue.value, + limit: pagination.limit, + offset: (pagination.current - 1) * pagination.limit, + }; + if (role?.value) { + Object.assign(params, { + instance_role: role.value, + }); + } + if (clusterId?.value && clusterId.value !== currentBizId) { + Object.assign(params, { + cluster_ids: clusterId.value, + }); + } + return params; + }; + + const fetchResources = async () => { + const params = generateParams(); + return getTableListRun(params); + }; + + const handleChangePage = (value: number) => { + pagination.current = value; + return fetchResources(); + }; + + const handeChangeLimit = (value: number) => { + pagination.limit = value; + return handleChangePage(1); + }; + + return { + isLoading, + data: tableData, + pagination, + searchValue, + generateParams, + fetchResources, + handleChangePage, + handeChangeLimit, + }; +} diff --git a/dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/useTopoData.ts b/dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/useTopoData.ts new file mode 100644 index 0000000000..a2170758c3 --- /dev/null +++ b/dbm-ui/frontend/src/components/instance-selector/components/tendb-ha-host/useTopoData.ts @@ -0,0 +1,107 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * + * Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at https://opensource.org/licenses/MIT + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for + * the specific language governing permissions and limitations under the License. + */ +import { type ComponentInternalInstance } from 'vue'; + +import { useGlobalBizs } from '@stores'; + +import { ClusterTypes } from '@common/const'; + +interface TopoTreeData { + id: number; + name: string; + obj: 'biz' | 'cluster'; + count: number; + children: Array; +} + +/** + * 处理集群列表数据 + */ +export function useTopoData>(filterClusterId: ComputedRef) { + const { currentBizId, currentBizInfo } = useGlobalBizs(); + const currentInstance = getCurrentInstance() as ComponentInternalInstance & { + proxy: { + getTopoList: (params: any) => Promise; + countFunc?: (data: T) => number; + }; + }; + + const isLoading = ref(false); + const selectClusterId = ref(); + const treeRef = ref(); + + const treeData = shallowRef([]); + + /** + * 获取列表 + */ + const fetchResources = async () => { + isLoading.value = true; + const params = { + bk_biz_id: currentBizId, + cluster_filters: [ + { + bk_biz_id: currentBizId, + cluster_type: ClusterTypes.TENDBHA, + }, + ], + } as Record; + if (filterClusterId.value) { + params.cluster_filters[0].id = filterClusterId.value; + } + return currentInstance.proxy + .getTopoList(params) + .then((data) => { + const countFn = currentInstance.proxy?.countFunc; + const formatData = data.map((item: T) => ({ ...item, count: countFn ? countFn(item) : item.masters.length })); + const children = formatData.map((item: T) => ({ + id: item.id, + name: item.master_domain, + obj: 'cluster', + count: item.count, + children: [], + })); + treeData.value = filterClusterId.value + ? children + : [ + { + name: currentBizInfo?.display_name || '--', + id: currentBizId, + obj: 'biz', + count: formatData.reduce((count: number, item: any) => count + item.count, 0), + children, + }, + ]; + setTimeout(() => { + if (data.length > 0) { + const [firstNode] = treeData.value; + selectClusterId.value = firstNode.id; + const [firstRawNode] = treeRef.value.getData().data; + treeRef.value.setOpen(firstRawNode); + treeRef.value.setSelect(firstRawNode); + } + }); + }) + .finally(() => { + isLoading.value = false; + }); + }; + + return { + treeRef, + isLoading, + treeData, + selectClusterId, + fetchResources, + }; +} diff --git a/dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/Index.vue b/dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/Index.vue new file mode 100644 index 0000000000..264eac830a --- /dev/null +++ b/dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/Index.vue @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + {{ item.obj === 'biz' ? t('业') : t('集') }} + + + {{ item.name }} + + + {{ item.count }} + + + + + + + + + + + + + + + + + + + + diff --git a/dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/table/Index.vue b/dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/table/Index.vue new file mode 100644 index 0000000000..e713816000 --- /dev/null +++ b/dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/table/Index.vue @@ -0,0 +1,340 @@ + + + + + + + + + + + + + diff --git a/dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/table/useTableData.ts b/dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/table/useTableData.ts new file mode 100644 index 0000000000..42179d9869 --- /dev/null +++ b/dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/table/useTableData.ts @@ -0,0 +1,107 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * + * Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at https://opensource.org/licenses/MIT + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for + * the specific language governing permissions and limitations under the License. + */ + +import type { ComponentInternalInstance, Ref } from 'vue'; +import { useRequest } from 'vue-request'; + +import { useGlobalBizs } from '@stores'; + +/** + * 处理集群列表数据 + */ +export function useTableData(role?: Ref, clusterId?: Ref) { + const { currentBizId } = useGlobalBizs(); + const currentInstance = getCurrentInstance() as ComponentInternalInstance & { + proxy: { + getTableList: (params: any) => Promise; + }; + }; + + const tableData = shallowRef([]); + const isAnomalies = ref(false); + const pagination = reactive({ + count: 0, + current: 1, + limit: 10, + limitList: [10, 20, 50, 100], + align: 'right', + layout: ['total', 'limit', 'list'], + }); + const searchValue = ref(''); + + const { run: getTableListRun, loading: isLoading } = useRequest(currentInstance.proxy.getTableList, { + manual: true, + onSuccess(data) { + tableData.value = data.results; + pagination.count = data.count; + isAnomalies.value = false; + }, + onError() { + tableData.value = []; + pagination.count = 0; + isAnomalies.value = true; + }, + }); + + watch(searchValue, () => { + setTimeout(() => { + handleChangePage(1); + }); + }); + + const generateParams = () => { + const params = { + bk_biz_id: currentBizId, + ip: searchValue.value, + limit: pagination.limit, + offset: (pagination.current - 1) * pagination.limit, + }; + if (role?.value) { + Object.assign(params, { + instance_role: role.value, + }); + } + if (clusterId?.value && clusterId.value !== currentBizId) { + Object.assign(params, { + cluster_ids: clusterId.value, + }); + } + return params; + }; + + const fetchResources = async () => { + const params = generateParams(); + return getTableListRun(params); + }; + + const handleChangePage = (value: number) => { + pagination.current = value; + return fetchResources(); + }; + + const handeChangeLimit = (value: number) => { + pagination.limit = value; + return handleChangePage(1); + }; + + return { + isLoading, + data: tableData, + pagination, + searchValue, + generateParams, + fetchResources, + handleChangePage, + handeChangeLimit, + }; +} diff --git a/dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/useTopoData.ts b/dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/useTopoData.ts new file mode 100644 index 0000000000..9cac258f94 --- /dev/null +++ b/dbm-ui/frontend/src/components/instance-selector/components/tendb-single-host/useTopoData.ts @@ -0,0 +1,107 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + * + * Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at https://opensource.org/licenses/MIT + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for + * the specific language governing permissions and limitations under the License. + */ +import { type ComponentInternalInstance } from 'vue'; + +import { useGlobalBizs } from '@stores'; + +import { ClusterTypes } from '@common/const'; + +interface TopoTreeData { + id: number; + name: string; + obj: 'biz' | 'cluster'; + count: number; + children: Array; +} + +/** + * 处理集群列表数据 + */ +export function useTopoData>(filterClusterId: ComputedRef) { + const { currentBizId, currentBizInfo } = useGlobalBizs(); + const currentInstance = getCurrentInstance() as ComponentInternalInstance & { + proxy: { + getTopoList: (params: any) => Promise; + countFunc?: (data: T) => number; + }; + }; + + const isLoading = ref(false); + const selectClusterId = ref(); + const treeRef = ref(); + + const treeData = shallowRef([]); + + /** + * 获取列表 + */ + const fetchResources = async () => { + isLoading.value = true; + const params = { + bk_biz_id: currentBizId, + cluster_filters: [ + { + bk_biz_id: currentBizId, + cluster_type: ClusterTypes.TENDBSINGLE, + }, + ], + } as Record; + if (filterClusterId.value) { + params.cluster_filters[0].id = filterClusterId.value; + } + return currentInstance.proxy + .getTopoList(params) + .then((data) => { + const countFn = currentInstance.proxy?.countFunc; + const formatData = data.map((item: T) => ({ ...item, count: countFn ? countFn(item) : item.masters.length })); + const children = formatData.map((item: T) => ({ + id: item.id, + name: item.master_domain, + obj: 'cluster', + count: item.count, + children: [], + })); + treeData.value = filterClusterId.value + ? children + : [ + { + name: currentBizInfo?.display_name || '--', + id: currentBizId, + obj: 'biz', + count: formatData.reduce((count: number, item: any) => count + item.count, 0), + children, + }, + ]; + setTimeout(() => { + if (data.length > 0) { + const [firstNode] = treeData.value; + selectClusterId.value = firstNode.id; + const [firstRawNode] = treeRef.value.getData().data; + treeRef.value.setOpen(firstRawNode); + treeRef.value.setSelect(firstRawNode); + } + }); + }) + .finally(() => { + isLoading.value = false; + }); + }; + + return { + treeRef, + isLoading, + treeData, + selectClusterId, + fetchResources, + }; +} diff --git a/dbm-ui/frontend/src/components/mult-line-text/Index.vue b/dbm-ui/frontend/src/components/mult-line-text/Index.vue index 9b9b5fc2d0..4f85d5c233 100644 --- a/dbm-ui/frontend/src/components/mult-line-text/Index.vue +++ b/dbm-ui/frontend/src/components/mult-line-text/Index.vue @@ -27,20 +27,27 @@ diff --git a/dbm-ui/frontend/src/views/permission-retrieve/components/options/Index.vue b/dbm-ui/frontend/src/views/permission-retrieve/components/options/Index.vue new file mode 100644 index 0000000000..f3964761a3 --- /dev/null +++ b/dbm-ui/frontend/src/views/permission-retrieve/components/options/Index.vue @@ -0,0 +1,470 @@ + + + + + + + (hostSelectorShow = true)" /> + + + (clusterSelectorShow = true)" /> + + + + + + handleUserClose(item.value)"> + {{ item.value }} + + + + + + + + + + + {{ t('查询') }} + + + {{ t('重置') }} + + + + + + + 50 + + + {{ resultHostList.length }} + + + + + + + + + 20 + + + {{ resultClusterList.length }} + + + + + + + + + + diff --git a/dbm-ui/frontend/src/views/permission-retrieve/components/options/components/BatchInput.vue b/dbm-ui/frontend/src/views/permission-retrieve/components/options/components/BatchInput.vue new file mode 100644 index 0000000000..097f663e75 --- /dev/null +++ b/dbm-ui/frontend/src/views/permission-retrieve/components/options/components/BatchInput.vue @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + diff --git a/dbm-ui/frontend/src/views/permission-retrieve/components/result/Index.vue b/dbm-ui/frontend/src/views/permission-retrieve/components/result/Index.vue new file mode 100644 index 0000000000..e4accb9c0d --- /dev/null +++ b/dbm-ui/frontend/src/views/permission-retrieve/components/result/Index.vue @@ -0,0 +1,627 @@ + + + + + + {{ t('无权限的IP共n个', { n: privIpMap.noPriv.length }) }} + + : + + + {{ privIpMap.noPriv.join(',') }} + + + + + + + + + {{ t('有权限的IP共n个', { n: privIpMap.hasPriv.length }) }} + + + + + + + + + + {{ t('IP 视角') }} + {{ t('域名视角') }} + + + + + + + + + + +