diff --git a/ui/package.json b/ui/package.json
index 8066f0353..e954db854 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -19,7 +19,7 @@
"ahooks": "^3.7.8",
"antd": "^5.4.0",
"copy-to-clipboard": "^3.3.3",
- "i18next": "^23.10.0",
+ "i18next": "^23.10.1",
"jsencrypt": "^3.3.2",
"lodash": "^4.17.21",
"moment": "^2.29.4"
diff --git a/ui/src/components/TopoComponent/index.tsx b/ui/src/components/TopoComponent/index.tsx
index 7c50e1104..78239a7dc 100644
--- a/ui/src/components/TopoComponent/index.tsx
+++ b/ui/src/components/TopoComponent/index.tsx
@@ -14,6 +14,7 @@ import { deleteObcluster, deleteObzone, getClusterDetailReq } from '@/services';
import { getNSName } from '../../pages/Cluster/Detail/Overview/helper';
import { ReactNode, config } from './G6register';
import type { OperateTypeLabel } from './constants';
+import { RESULT_STATUS } from '@/constants';
import {
clusterOperate,
clusterOperateOfTenant,
@@ -223,7 +224,7 @@ export default function TopoComponent({
useUpdateEffect(() => {
let checkStatusTimer: NodeJS.Timer;
//polling
- if (originTopoData.topoData.status !== 'running') {
+ if (!RESULT_STATUS.includes(originTopoData.topoData.status)) {
if (!operateDisable) setOperateDisable(true);
checkStatusTimer = setInterval(() => {
getTopoData({ ns, name, useFor: 'topo', tenantReplicas });
diff --git a/ui/src/components/customModal/AddZoneModal.tsx b/ui/src/components/customModal/AddZoneModal.tsx
index c52fc6321..017d670de 100644
--- a/ui/src/components/customModal/AddZoneModal.tsx
+++ b/ui/src/components/customModal/AddZoneModal.tsx
@@ -1,6 +1,7 @@
import { intl } from '@/utils/intl';
import { Form, Input, InputNumber, message } from 'antd';
+import { TZ_NAME_REG } from '@/constants';
import { getNSName } from '@/pages/Cluster/Detail/Overview/helper';
import { addObzone } from '@/services';
import type { CommonModalType } from '.';
@@ -71,6 +72,13 @@ export default function AddZoneModal({
defaultMessage: '请输入zone名称!',
}),
},
+ {
+ pattern: TZ_NAME_REG,
+ message: intl.formatMessage({
+ id: 'Dashboard.components.customModal.AddZoneModal.TheFirstCharacterMustBe',
+ defaultMessage: '首字符必须是字母或者下划线,不能包含 -',
+ }),
+ },
]}
>
{/* 如果不能为空的话应该需要添加默认值 */}
{/*
node-selector:
-
- {(fields, { add, remove }) => (
- <>
- {fields.map(({ key, name, ...restField }) => (
-
-
-
-
- :
-
-
-
- remove(name)} />
-
- ))}
-
-
-
- >
- )}
- */}
+
+ {(fields, { add, remove }) => (
+ <>
+ {fields.map(({ key, name, ...restField }) => (
+
+
+
+
+ :
+
+
+
+ remove(name)} />
+
+ ))}
+
+
+
+ >
+ )}
+ */}
{
try {
@@ -30,7 +32,7 @@ export default function ModifyPasswordModal({
ns: namespace,
name,
User: 'root',
- Password: values.password,
+ Password: encryptText(values.password,publicKey) as string,
});
if (res.successful) {
message.success(res.message);
diff --git a/ui/src/components/customModal/ModifyUnitDetailModal.tsx b/ui/src/components/customModal/ModifyUnitDetailModal.tsx
index f8503dd3a..1c702a3ba 100644
--- a/ui/src/components/customModal/ModifyUnitDetailModal.tsx
+++ b/ui/src/components/customModal/ModifyUnitDetailModal.tsx
@@ -1,17 +1,52 @@
import { SUFFIX_UNIT } from '@/constants';
import { getNSName } from '@/pages/Cluster/Detail/Overview/helper';
+import type { MaxResourceType } from '@/pages/Tenant/New/ResourcePools';
+import ZoneItem from '@/pages/Tenant/ZoneItem';
+import { findMinParameter,getNewClusterList } from '@/pages/Tenant/helper';
import { patchTenantConfiguration } from '@/services/tenant';
+import { formatUnitDetailData } from '@/utils/helper';
import { intl } from '@/utils/intl';
+import { useEffect,useState } from 'react';
+import InputNumber from '@/components/InputNumber';
-import { Col, Form, InputNumber, Row, message } from 'antd';
+import { Col, Form, Row, message } from 'antd';
import type { CommonModalType } from '.';
import CustomModal from '.';
+
+export type UnitDetailType = {
+ unitConfig: {
+ unitConfig: {
+ cpuCount: number|string;
+ iopsWeight: number;
+ logDiskSize: number | string;
+ maxIops: number;
+ memorySize: number | string;
+ minIops: number;
+ };
+ pools:any;
+ };
+};
+
+type UnitConfigType = {
+ clusterList?: API.SimpleClusterList;
+ clusterName?: string;
+ essentialParameter?: API.EssentialParametersType;
+ setClusterList: React.Dispatch>;
+};
+
export default function ModifyUnitDetailModal({
visible,
setVisible,
successCallback,
-}: CommonModalType) {
- const [form] = Form.useForm();
+ clusterList = [],
+ setClusterList,
+ essentialParameter = {},
+ clusterName = '',
+}: CommonModalType & UnitConfigType) {
+ const [form] = Form.useForm();
+ const [minMemory, setMinMemory] = useState(2);
+ const [maxResource, setMaxResource] = useState({});
+ const [selectZones, setSelectZones] = useState([]);
const handleSubmit = async () => {
try {
await form.validateFields();
@@ -20,8 +55,12 @@ export default function ModifyUnitDetailModal({
};
const handleCancel = () => setVisible(false);
const onFinish = async (values: any) => {
- const [namespace, name] = getNSName();
- const res = await patchTenantConfiguration({ namespace, name, ...values });
+ const [ns, name] = getNSName();
+ const res = await patchTenantConfiguration({
+ ns,
+ name,
+ ...formatUnitDetailData(values),
+ });
if (res.successful) {
message.success(res.message);
successCallback();
@@ -29,8 +68,41 @@ export default function ModifyUnitDetailModal({
setVisible(false);
}
};
+ const checkBoxOnChange = (checked: boolean, name: string) => {
+ if (!checked) {
+ form.setFieldValue(['pools', name, 'priority'], undefined);
+ setSelectZones(selectZones.filter((zone) => zone !== name));
+ } else {
+ setSelectZones([...selectZones, name]);
+ }
+ setClusterList(
+ getNewClusterList(clusterList, name, checked, { name: clusterName }),
+ );
+ };
+
+ const targetZoneList = clusterList
+ .filter((cluster) => cluster.clusterName === clusterName)[0]
+ ?.topology.map((zone) => ({ zone: zone.zone, checked: zone.checked }));
+
+ useEffect(() => {
+ if (essentialParameter) {
+ setMinMemory(essentialParameter.minPoolMemory / (1 << 30));
+ }
+ }, [essentialParameter]);
+
+ useEffect(() => {
+ if (essentialParameter) {
+ if (selectZones.length === 0) {
+ setMaxResource({});
+ return;
+ }
+ setMaxResource(findMinParameter(selectZones, essentialParameter));
+ }
+ }, [selectZones]);
+
return (
-
-
-
-
-
-
-
-
-
-
-
-
+ {targetZoneList && essentialParameter && (
+
+
+ {intl.formatMessage({
+ id: 'Dashboard.Tenant.New.ResourcePools.ZonePriority',
+ defaultMessage: 'Zone优先级',
+ })}
+
+ {targetZoneList.map((item, index) => (
+
+ ))}
+
+ )}
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
);
diff --git a/ui/src/components/customModal/OperateModal.tsx b/ui/src/components/customModal/OperateModal.tsx
index fe7b1f4b1..9538d0c98 100644
--- a/ui/src/components/customModal/OperateModal.tsx
+++ b/ui/src/components/customModal/OperateModal.tsx
@@ -13,12 +13,14 @@ import ModifyUnitDetailModal from './ModifyUnitDetailModal';
interface OperateModalProps {
type: API.ModalType;
zoneName?: any;
- defaultValue?: number;
+ defaultValue?: any;
disabled?: boolean;
+ defaultValueForUnitDetail?: any;
}
export default function OperateModal({
type,
+ defaultValueForUnitDetail,
...props
}: OperateModalProps & CommonModalType) {
if (type === 'addZone') {
@@ -52,7 +54,7 @@ export default function OperateModal({
}
if(type === 'modifyUnitSpecification'){
- return
+ return
}
if(type === 'changeUnitCount'){
diff --git a/ui/src/constants/index.ts b/ui/src/constants/index.ts
index 79d483642..3677e365c 100644
--- a/ui/src/constants/index.ts
+++ b/ui/src/constants/index.ts
@@ -58,6 +58,12 @@ const MINIMAL_CONFIG = {
redoLog: 30,
};
+const RESULT_STATUS = ['running','failed'];
+
+const RESOURCE_NAME_REG = /^[a-z\-]+$/;
+// use for tenant name or zone name
+const TZ_NAME_REG = /^[_a-zA-Z][^-\n]*$/;
+
export {
BADGE_IMG_MAP,
CLUSTER_IMG_MAP,
@@ -71,4 +77,7 @@ export {
STATUS,
SUFFIX_UNIT,
ZONE_IMG_MAP,
+ RESULT_STATUS,
+ RESOURCE_NAME_REG,
+ TZ_NAME_REG
};
diff --git a/ui/src/i18n/strings/en-US.json b/ui/src/i18n/strings/en-US.json
index c91e7de45..311b5050a 100644
--- a/ui/src/i18n/strings/en-US.json
+++ b/ui/src/i18n/strings/en-US.json
@@ -579,5 +579,14 @@
"Dashboard.Detail.Overview.AdjustTheNumberOfUnits": "Adjust the number of units",
"Dashboard.Tenant.New.ResourcePools.AvailableResources": "Available resources:",
"Dashboard.Tenant.New.ResourcePools.LogDiskSize": "Log disk size",
- "Dashboard.Tenant.New.ResourcePools.ThisRefersToTheTenant": "This refers to the tenant's clog disk space."
+ "Dashboard.Tenant.New.ResourcePools.ThisRefersToTheTenant": "This refers to the tenant's clog disk space.",
+ "Dashboard.Cluster.New.BasicInfo.RegularMode": "Regular mode",
+ "Dashboard.Cluster.New.BasicInfo.MonomerMode": "Monomer mode",
+ "Dashboard.Cluster.New.BasicInfo.ServiceMode": "Service mode",
+ "Dashboard.Cluster.New.BasicInfo.ClusterMode": "Cluster mode",
+ "Dashboard.Cluster.New.BasicInfo.SelectClusterMode": "Select cluster mode",
+ "Dashboard.Cluster.New.BasicInfo.PleaseSelect": "Please select",
+ "Dashboard.Tenant.New.BasicInfo.ResourceNamesCanOnlyConsist": "Resource names can only consist of lowercase letters and-",
+ "Dashboard.Tenant.New.BasicInfo.TheFirstCharacterMustBe": "The first character must be a letter or an underscore and cannot contain-",
+ "Dashboard.components.customModal.AddZoneModal.TheFirstCharacterMustBe": "The first character must be a letter or an underscore and cannot contain-"
}
diff --git a/ui/src/i18n/strings/zh-CN.json b/ui/src/i18n/strings/zh-CN.json
index 25f8ebcab..a5e1c6cd6 100644
--- a/ui/src/i18n/strings/zh-CN.json
+++ b/ui/src/i18n/strings/zh-CN.json
@@ -579,5 +579,14 @@
"Dashboard.Detail.Overview.AdjustTheNumberOfUnits": "调整 Unit 数量",
"Dashboard.Tenant.New.ResourcePools.AvailableResources": "可用资源:",
"Dashboard.Tenant.New.ResourcePools.LogDiskSize": "日志磁盘大小",
- "Dashboard.Tenant.New.ResourcePools.ThisRefersToTheTenant": "此处指的是租户的 clog 磁盘空间"
+ "Dashboard.Tenant.New.ResourcePools.ThisRefersToTheTenant": "此处指的是租户的 clog 磁盘空间",
+ "Dashboard.Cluster.New.BasicInfo.RegularMode": "常规模式",
+ "Dashboard.Cluster.New.BasicInfo.MonomerMode": "单体模式",
+ "Dashboard.Cluster.New.BasicInfo.ServiceMode": "Service模式",
+ "Dashboard.Cluster.New.BasicInfo.ClusterMode": "集群模式",
+ "Dashboard.Cluster.New.BasicInfo.SelectClusterMode": "请选择集群模式",
+ "Dashboard.Cluster.New.BasicInfo.PleaseSelect": "请选择",
+ "Dashboard.Tenant.New.BasicInfo.ResourceNamesCanOnlyConsist": "资源名只能由小写字母和 - 组成",
+ "Dashboard.Tenant.New.BasicInfo.TheFirstCharacterMustBe": "首字符必须是字母或者下划线,不能包含 -",
+ "Dashboard.components.customModal.AddZoneModal.TheFirstCharacterMustBe": "首字符必须是字母或者下划线,不能包含 -"
}
diff --git a/ui/src/pages/Cluster/New/BasicInfo.tsx b/ui/src/pages/Cluster/New/BasicInfo.tsx
index 8c21910a5..2e20f6ad3 100644
--- a/ui/src/pages/Cluster/New/BasicInfo.tsx
+++ b/ui/src/pages/Cluster/New/BasicInfo.tsx
@@ -1,30 +1,14 @@
import { intl } from '@/utils/intl';
import { PlusOutlined } from '@ant-design/icons';
-import { useRequest, useUpdateEffect } from 'ahooks';
-import {
- Button,
- Card,
- Col,
- Divider,
- Form,
- Input,
- Row,
- Select,
- Tooltip,
- message,
-} from 'antd';
+import { useRequest } from 'ahooks';
+import { Card, Col, Divider, Form, Input, Row, Select, Tooltip } from 'antd';
import type { FormInstance } from 'antd/lib/form';
+import PasswordInput from '@/components/PasswordInput';
import AddNSModal from '@/components/customModal/AddNSModal';
import { getNameSpaces } from '@/services';
-import copy from 'copy-to-clipboard';
import { useState } from 'react';
-import PasswordInput from '@/components/PasswordInput';
-import {
- generateRandomPassword,
- passwordRules,
- resourceNameRule,
-} from './helper';
+import { resourceNameRule } from './helper';
import styles from './index.less';
interface BasicInfoProps {
@@ -41,13 +25,35 @@ export default function BasicInfo({
// control the modal for adding a new namespace
const [visible, setVisible] = useState(false);
const { data, run: getNS } = useRequest(getNameSpaces);
+ const CLUSTER_MODE = [
+ {
+ value: 'NORMAL',
+ label: intl.formatMessage({
+ id: 'Dashboard.Cluster.New.BasicInfo.RegularMode',
+ defaultMessage: '常规模式',
+ }),
+ },
+ {
+ value: 'STANDLONE',
+ label: intl.formatMessage({
+ id: 'Dashboard.Cluster.New.BasicInfo.MonomerMode',
+ defaultMessage: '单体模式',
+ }),
+ },
+ {
+ value: 'SERVICE',
+ label: intl.formatMessage({
+ id: 'Dashboard.Cluster.New.BasicInfo.ServiceMode',
+ defaultMessage: 'Service模式',
+ }),
+ },
+ ];
const filterOption = (
input: string,
option: { label: string; value: string },
) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase());
-
const DropDownComponent = (menu: any) => {
return (
@@ -69,11 +75,11 @@ export default function BasicInfo({
);
};
- const addNSCallback = (newNS:string)=>{
- form.setFieldValue('namespace',newNS)
- form.validateFields(['namespace'])
- getNS()
- }
+ const addNSCallback = (newNS: string) => {
+ form.setFieldValue('namespace', newNS);
+ form.validateFields(['namespace']);
+ getNS();
+ };
return (
@@ -119,12 +125,12 @@ export default function BasicInfo({
-
+
+
+
+
+
+
(false);
const curConfig = useRef({});
const [ns, name] = getNSName();
+ const publicKey = usePublicKey()
const INFO_CONFIG = {
archivePath: {
@@ -186,8 +188,8 @@ export default function BackupConfiguration({
if (
checkIsSame(
- formatBackupForm(initialValues),
- formatBackupForm(form.getFieldsValue()),
+ formatBackupForm(initialValues,publicKey),
+ formatBackupForm(form.getFieldsValue(),publicKey),
)
) {
message.info(
@@ -207,10 +209,10 @@ export default function BackupConfiguration({
const { successful, data } = await updateBackupPolicyOfTenant({
ns,
name,
- ...formatBackupForm(values),
+ ...formatBackupForm(values,publicKey),
});
if (successful) {
- curConfig.current = formatBackupForm(form.getFieldsValue());
+ curConfig.current = formatBackupForm(form.getFieldsValue(),publicKey);
if (checkIsSame(data, curConfig.current)) {
setIsLoading(true);
getBackupPolicyReq({ ns, name });
diff --git a/ui/src/pages/Tenant/Detail/Backup/index.tsx b/ui/src/pages/Tenant/Detail/Backup/index.tsx
index ce31cdcd3..8aba41e08 100644
--- a/ui/src/pages/Tenant/Detail/Backup/index.tsx
+++ b/ui/src/pages/Tenant/Detail/Backup/index.tsx
@@ -1,15 +1,15 @@
import EmptyImg from '@/assets/empty.svg';
import { getNSName } from '@/pages/Cluster/Detail/Overview/helper';
-import { getBackupPolicy, getTenant } from '@/services/tenant';
+import { getBackupPolicy,getTenant } from '@/services/tenant';
import { intl } from '@/utils/intl';
import { PageContainer } from '@ant-design/pro-components';
import { history } from '@umijs/max';
import { useRequest } from 'ahooks';
-import { Button, Card, Row } from 'antd';
+import { Button,Card,Col,Row } from 'antd';
+import { useState } from 'react';
import BasicInfo from '../Overview/BasicInfo';
import BackupConfiguration from './BackupConfiguration';
import BackupJobs from './BackupJobs';
-import { useState } from 'react';
export default function Backup() {
const [ns, name] = getNSName();
@@ -71,7 +71,12 @@ export default function Backup() {
{tenantDetail && (
)}
-
+
+
+
)}
diff --git a/ui/src/pages/Tenant/Detail/NewBackup/index.tsx b/ui/src/pages/Tenant/Detail/NewBackup/index.tsx
index 1700aa53d..9a809cc62 100644
--- a/ui/src/pages/Tenant/Detail/NewBackup/index.tsx
+++ b/ui/src/pages/Tenant/Detail/NewBackup/index.tsx
@@ -85,7 +85,7 @@ export default function NewBackup() {
defaultMessage: '取消',
})}
,
-