From 47121789ea39d25570ce0d06724bbd8e8d1d34da Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Fri, 13 Dec 2019 21:15:25 +0800 Subject: [PATCH 1/2] Update DM Portal (#417) * portal: rename dm-fe folder to frontend * portal: udpate frontend README * portal: add option to control whether auto sync with upstream newly added schemas or tables * portal: validate the task name * portal: differ the undo and reset action more clearly --- dm/portal/{dm-fe => frontend}/.gitignore | 0 dm/portal/{dm-fe => frontend}/README.md | 2 +- .../{dm-fe => frontend}/config-overrides.js | 0 dm/portal/{dm-fe => frontend}/package.json | 0 .../{dm-fe => frontend}/public/favicon.ico | Bin .../{dm-fe => frontend}/public/index.html | 0 .../{dm-fe => frontend}/public/manifest.json | 0 .../{dm-fe => frontend}/src/App.test.tsx | 0 dm/portal/{dm-fe => frontend}/src/App.tsx | 0 .../src/components/BinlogFilterModal.tsx | 0 .../src/components/InstancesStep.tsx | 0 .../src/components/Layout.tsx | 0 .../src/components/MigrateStep.tsx | 90 +++++++++++------- .../src/components/NamingStep.tsx | 58 ++++++++--- .../src/components/StartStep.tsx | 3 +- .../src/components/UploadStep.tsx | 0 dm/portal/{dm-fe => frontend}/src/index.css | 0 dm/portal/{dm-fe => frontend}/src/index.tsx | 0 dm/portal/{dm-fe => frontend}/src/logo.svg | 0 .../src/react-app-env.d.ts | 0 .../{dm-fe => frontend}/src/serviceWorker.ts | 0 .../{dm-fe => frontend}/src/services/api.ts | 0 dm/portal/{dm-fe => frontend}/src/types.ts | 0 .../src/utils/config-util.ts | 17 ++-- .../{dm-fe => frontend}/src/utils/request.ts | 0 dm/portal/{dm-fe => frontend}/tsconfig.json | 0 dm/portal/{dm-fe => frontend}/yarn.lock | 0 27 files changed, 114 insertions(+), 56 deletions(-) rename dm/portal/{dm-fe => frontend}/.gitignore (100%) rename dm/portal/{dm-fe => frontend}/README.md (87%) rename dm/portal/{dm-fe => frontend}/config-overrides.js (100%) rename dm/portal/{dm-fe => frontend}/package.json (100%) rename dm/portal/{dm-fe => frontend}/public/favicon.ico (100%) rename dm/portal/{dm-fe => frontend}/public/index.html (100%) rename dm/portal/{dm-fe => frontend}/public/manifest.json (100%) rename dm/portal/{dm-fe => frontend}/src/App.test.tsx (100%) rename dm/portal/{dm-fe => frontend}/src/App.tsx (100%) rename dm/portal/{dm-fe => frontend}/src/components/BinlogFilterModal.tsx (100%) rename dm/portal/{dm-fe => frontend}/src/components/InstancesStep.tsx (100%) rename dm/portal/{dm-fe => frontend}/src/components/Layout.tsx (100%) rename dm/portal/{dm-fe => frontend}/src/components/MigrateStep.tsx (93%) rename dm/portal/{dm-fe => frontend}/src/components/NamingStep.tsx (53%) rename dm/portal/{dm-fe => frontend}/src/components/StartStep.tsx (77%) rename dm/portal/{dm-fe => frontend}/src/components/UploadStep.tsx (100%) rename dm/portal/{dm-fe => frontend}/src/index.css (100%) rename dm/portal/{dm-fe => frontend}/src/index.tsx (100%) rename dm/portal/{dm-fe => frontend}/src/logo.svg (100%) rename dm/portal/{dm-fe => frontend}/src/react-app-env.d.ts (100%) rename dm/portal/{dm-fe => frontend}/src/serviceWorker.ts (100%) rename dm/portal/{dm-fe => frontend}/src/services/api.ts (100%) rename dm/portal/{dm-fe => frontend}/src/types.ts (100%) rename dm/portal/{dm-fe => frontend}/src/utils/config-util.ts (98%) rename dm/portal/{dm-fe => frontend}/src/utils/request.ts (100%) rename dm/portal/{dm-fe => frontend}/tsconfig.json (100%) rename dm/portal/{dm-fe => frontend}/yarn.lock (100%) diff --git a/dm/portal/dm-fe/.gitignore b/dm/portal/frontend/.gitignore similarity index 100% rename from dm/portal/dm-fe/.gitignore rename to dm/portal/frontend/.gitignore diff --git a/dm/portal/dm-fe/README.md b/dm/portal/frontend/README.md similarity index 87% rename from dm/portal/dm-fe/README.md rename to dm/portal/frontend/README.md index 021f3cbc91..af1ab3e86a 100644 --- a/dm/portal/dm-fe/README.md +++ b/dm/portal/frontend/README.md @@ -11,7 +11,7 @@ in the repo root folder: ### Run dm-fe -in the repo `dm-portal/dm-fe` folder: +in the repo `dm/dm/portal/frontend` folder: 1. `yarn install` 1. `yarn start` diff --git a/dm/portal/dm-fe/config-overrides.js b/dm/portal/frontend/config-overrides.js similarity index 100% rename from dm/portal/dm-fe/config-overrides.js rename to dm/portal/frontend/config-overrides.js diff --git a/dm/portal/dm-fe/package.json b/dm/portal/frontend/package.json similarity index 100% rename from dm/portal/dm-fe/package.json rename to dm/portal/frontend/package.json diff --git a/dm/portal/dm-fe/public/favicon.ico b/dm/portal/frontend/public/favicon.ico similarity index 100% rename from dm/portal/dm-fe/public/favicon.ico rename to dm/portal/frontend/public/favicon.ico diff --git a/dm/portal/dm-fe/public/index.html b/dm/portal/frontend/public/index.html similarity index 100% rename from dm/portal/dm-fe/public/index.html rename to dm/portal/frontend/public/index.html diff --git a/dm/portal/dm-fe/public/manifest.json b/dm/portal/frontend/public/manifest.json similarity index 100% rename from dm/portal/dm-fe/public/manifest.json rename to dm/portal/frontend/public/manifest.json diff --git a/dm/portal/dm-fe/src/App.test.tsx b/dm/portal/frontend/src/App.test.tsx similarity index 100% rename from dm/portal/dm-fe/src/App.test.tsx rename to dm/portal/frontend/src/App.test.tsx diff --git a/dm/portal/dm-fe/src/App.tsx b/dm/portal/frontend/src/App.tsx similarity index 100% rename from dm/portal/dm-fe/src/App.tsx rename to dm/portal/frontend/src/App.tsx diff --git a/dm/portal/dm-fe/src/components/BinlogFilterModal.tsx b/dm/portal/frontend/src/components/BinlogFilterModal.tsx similarity index 100% rename from dm/portal/dm-fe/src/components/BinlogFilterModal.tsx rename to dm/portal/frontend/src/components/BinlogFilterModal.tsx diff --git a/dm/portal/dm-fe/src/components/InstancesStep.tsx b/dm/portal/frontend/src/components/InstancesStep.tsx similarity index 100% rename from dm/portal/dm-fe/src/components/InstancesStep.tsx rename to dm/portal/frontend/src/components/InstancesStep.tsx diff --git a/dm/portal/dm-fe/src/components/Layout.tsx b/dm/portal/frontend/src/components/Layout.tsx similarity index 100% rename from dm/portal/dm-fe/src/components/Layout.tsx rename to dm/portal/frontend/src/components/Layout.tsx diff --git a/dm/portal/dm-fe/src/components/MigrateStep.tsx b/dm/portal/frontend/src/components/MigrateStep.tsx similarity index 93% rename from dm/portal/dm-fe/src/components/MigrateStep.tsx rename to dm/portal/frontend/src/components/MigrateStep.tsx index 97bd18503d..19e920cd98 100644 --- a/dm/portal/dm-fe/src/components/MigrateStep.tsx +++ b/dm/portal/frontend/src/components/MigrateStep.tsx @@ -1,5 +1,5 @@ import React, { useState, useMemo, useRef } from 'react' -import { Button, Icon, Tree, Tooltip, message } from 'antd' +import { Button, Icon, Tree, Tooltip, message, Checkbox } from 'antd' import styled from 'styled-components' import { IPageAction, @@ -85,6 +85,10 @@ const Container = styled.div` top: 10px; right: 10px; } + + .auto-sync-option { + margin-top: 10px; + } ` type LastStateRef = { @@ -172,11 +176,14 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { // 左移,右移,拖拽,重命名需要记录 lastStateRef const lastStateRef = useRef(null) + // 是否自动同步上游新增库和新增表的选项 + const [autoSyncUpstream, setAutoSyncUpstream] = useState(false) + ///////////////////////////////// function cleanTargetInstance() { // confirm - if (!window.confirm('你确定要清空下游实例吗?')) { + if (!window.confirm('你确定要重置所有操作吗?此操作会清空下游实例。')) { return } @@ -196,7 +203,7 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { function undo() { // confirm - if (!window.confirm('你确定要撤消此次操作吗?')) { + if (!window.confirm('你确定要撤消此次操作,回到上一步吗?')) { return } @@ -808,7 +815,8 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { instancesConfig, sourceSchemas, targetSchemas, - allTables + allTables, + autoSyncUpstream ) let res = await generateConfig(finalConfig) setLoading(false) @@ -858,8 +866,8 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { <> {sourceSchemas[schemaKey].schema}{' '} @@ -877,8 +885,8 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { <> {allTables[tableKey].table}{' '} @@ -922,7 +930,7 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { title={ {schema.newName} @@ -936,10 +944,8 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { {table.newName} @@ -954,10 +960,8 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { {tb.newName} @@ -977,40 +981,56 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { return ( -
+

上游实例

-
{renderSourceTables()}
+
{renderSourceTables()}
+
+ setAutoSyncUpstream(e.target.checked)} + > + 自动同步上游新增库和新增表 + + + + +
-
+

下游实例

-
+
{renderTargetTables()} -
- - +
+ + + +   + + +
-
+
- diff --git a/dm/portal/dm-fe/src/components/NamingStep.tsx b/dm/portal/frontend/src/components/NamingStep.tsx similarity index 53% rename from dm/portal/dm-fe/src/components/NamingStep.tsx rename to dm/portal/frontend/src/components/NamingStep.tsx index 2d578e7f17..2ee62fe256 100644 --- a/dm/portal/dm-fe/src/components/NamingStep.tsx +++ b/dm/portal/frontend/src/components/NamingStep.tsx @@ -36,42 +36,74 @@ type Props = IPageAction & { taskInfo: ITaskInfo } +type TaskName = { + status: 'success' | 'error' + errMsg: string + value: string +} + function NamingStep({ onNext, onPrev, onData, taskInfo }: Props) { const edit = useContext(EditContext) - const [taskName, setTaskName] = useState(taskInfo.taskName) const [taskMode, setTaskMode] = useState(taskInfo.taskMode) + const [taskName, setTaskName] = useState(() => + handleTaskNameChange(taskInfo.taskName) + ) + + function handleTaskNameChange(name: string): TaskName { + if (name.length === 0 || /^[a-zA-Z0-9$_]+$/.test(name)) { + return { + status: 'success', + errMsg: '', + value: name + } + } else { + return { + status: 'error', + errMsg: '任务名称不合法', + value: name + } + } + } return (
- + setTaskName(e.target.value)} + placeholder='test-task' + value={taskName.value} + onChange={(e: any) => + setTaskName(handleTaskNameChange(e.target.value)) + } /> - + setTaskMode(e.target.value)} value={taskMode} > - 全量 - 增量 - All + 全量 + 增量 + All diff --git a/dm/portal/dm-fe/src/components/StartStep.tsx b/dm/portal/frontend/src/components/StartStep.tsx similarity index 77% rename from dm/portal/dm-fe/src/components/StartStep.tsx rename to dm/portal/frontend/src/components/StartStep.tsx index 0c03ed0409..9936f755e5 100644 --- a/dm/portal/dm-fe/src/components/StartStep.tsx +++ b/dm/portal/frontend/src/components/StartStep.tsx @@ -27,7 +27,8 @@ function StartStep(props: Props) {

DM 任务配置生成

- + {/* 编辑功能目前看没有使用场景,先隐藏,后续再移除 */} + {/* */}
) } diff --git a/dm/portal/dm-fe/src/components/UploadStep.tsx b/dm/portal/frontend/src/components/UploadStep.tsx similarity index 100% rename from dm/portal/dm-fe/src/components/UploadStep.tsx rename to dm/portal/frontend/src/components/UploadStep.tsx diff --git a/dm/portal/dm-fe/src/index.css b/dm/portal/frontend/src/index.css similarity index 100% rename from dm/portal/dm-fe/src/index.css rename to dm/portal/frontend/src/index.css diff --git a/dm/portal/dm-fe/src/index.tsx b/dm/portal/frontend/src/index.tsx similarity index 100% rename from dm/portal/dm-fe/src/index.tsx rename to dm/portal/frontend/src/index.tsx diff --git a/dm/portal/dm-fe/src/logo.svg b/dm/portal/frontend/src/logo.svg similarity index 100% rename from dm/portal/dm-fe/src/logo.svg rename to dm/portal/frontend/src/logo.svg diff --git a/dm/portal/dm-fe/src/react-app-env.d.ts b/dm/portal/frontend/src/react-app-env.d.ts similarity index 100% rename from dm/portal/dm-fe/src/react-app-env.d.ts rename to dm/portal/frontend/src/react-app-env.d.ts diff --git a/dm/portal/dm-fe/src/serviceWorker.ts b/dm/portal/frontend/src/serviceWorker.ts similarity index 100% rename from dm/portal/dm-fe/src/serviceWorker.ts rename to dm/portal/frontend/src/serviceWorker.ts diff --git a/dm/portal/dm-fe/src/services/api.ts b/dm/portal/frontend/src/services/api.ts similarity index 100% rename from dm/portal/dm-fe/src/services/api.ts rename to dm/portal/frontend/src/services/api.ts diff --git a/dm/portal/dm-fe/src/types.ts b/dm/portal/frontend/src/types.ts similarity index 100% rename from dm/portal/dm-fe/src/types.ts rename to dm/portal/frontend/src/types.ts diff --git a/dm/portal/dm-fe/src/utils/config-util.ts b/dm/portal/frontend/src/utils/config-util.ts similarity index 98% rename from dm/portal/dm-fe/src/utils/config-util.ts rename to dm/portal/frontend/src/utils/config-util.ts index 2c8702e83e..126b6c2fe6 100644 --- a/dm/portal/dm-fe/src/utils/config-util.ts +++ b/dm/portal/frontend/src/utils/config-util.ts @@ -302,7 +302,10 @@ export function genFiltersConfig( return filters } -export function genBlackWhiteList(allTables: IFullTables): IBWList { +export function genBlackWhiteList( + allTables: IFullTables, + autoSycnUpstream: boolean +): IBWList { const bwList: IBWList = {} const tables = Object.keys(allTables) .map(tableKey => allTables[tableKey]) @@ -314,6 +317,9 @@ export function genBlackWhiteList(allTables: IFullTables): IBWList { } const bwType: 'do-tables' | 'ignore-tables' = table.parentKey !== '' ? 'do-tables' : 'ignore-tables' + if (autoSycnUpstream && bwType === 'do-tables') { + return + } bwList[bwListKey][bwType].push({ 'db-name': table.schema, 'tbl-name': table.table @@ -327,11 +333,12 @@ export function genFinalConfig( instancesConfig: IInstances, sourceSchemas: IFullSchemas, targetSchemas: IFullSchemas, - allTables: IFullTables + allTables: IFullTables, + autoSycnUpstream: boolean ) { const routes: IRoutes = genRoutesConfig(targetSchemas, allTables) const filters: IFilters = genFiltersConfig(sourceSchemas, allTables) - const bwList: IBWList = genBlackWhiteList(allTables) + const bwList: IBWList = genBlackWhiteList(allTables, autoSycnUpstream) const finalConfig = { name: taskInfo.taskName, @@ -603,9 +610,7 @@ export function parseFinalConfig(finalConfig: IFinalConfig) { schema.filters = filterRule.events } else { // 说明此条 filter 属于 table - const tableKey = `${sourceId}:${filterRule['schema-pattern']}:${ - filterRule['table-pattern'] - }` + const tableKey = `${sourceId}:${filterRule['schema-pattern']}:${filterRule['table-pattern']}` const table = allTables[tableKey] table.filters = filterRule.events } diff --git a/dm/portal/dm-fe/src/utils/request.ts b/dm/portal/frontend/src/utils/request.ts similarity index 100% rename from dm/portal/dm-fe/src/utils/request.ts rename to dm/portal/frontend/src/utils/request.ts diff --git a/dm/portal/dm-fe/tsconfig.json b/dm/portal/frontend/tsconfig.json similarity index 100% rename from dm/portal/dm-fe/tsconfig.json rename to dm/portal/frontend/tsconfig.json diff --git a/dm/portal/dm-fe/yarn.lock b/dm/portal/frontend/yarn.lock similarity index 100% rename from dm/portal/dm-fe/yarn.lock rename to dm/portal/frontend/yarn.lock From bd60d0532cd0c5cd5c7dddf3c8f6e12e6d5ea5ca Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Fri, 14 Feb 2020 10:31:42 +0800 Subject: [PATCH 2/2] dm-portal: add i18n support (#476) --- dm/portal/frontend/package.json | 1 + dm/portal/frontend/src/App.tsx | 3 +- .../src/components/BinlogFilterModal.tsx | 11 +- .../frontend/src/components/InstancesStep.tsx | 38 ++++-- dm/portal/frontend/src/components/Layout.tsx | 35 +++++- .../frontend/src/components/MigrateStep.tsx | 104 ++++++++++------- .../frontend/src/components/NamingStep.tsx | 30 +++-- .../frontend/src/components/StartStep.tsx | 9 +- .../frontend/src/components/UploadStep.tsx | 6 +- dm/portal/frontend/src/locales/en-US.ts | 53 +++++++++ dm/portal/frontend/src/locales/index.ts | 7 ++ dm/portal/frontend/src/locales/zh-CN.ts | 49 ++++++++ dm/portal/frontend/yarn.lock | 108 ++++++++++++++++++ 13 files changed, 381 insertions(+), 73 deletions(-) create mode 100644 dm/portal/frontend/src/locales/en-US.ts create mode 100644 dm/portal/frontend/src/locales/index.ts create mode 100644 dm/portal/frontend/src/locales/zh-CN.ts diff --git a/dm/portal/frontend/package.json b/dm/portal/frontend/package.json index 29061246a3..e7f9e245ac 100644 --- a/dm/portal/frontend/package.json +++ b/dm/portal/frontend/package.json @@ -18,6 +18,7 @@ "react-app-rewired": "^2.1.3", "react-dom": "^16.8.6", "react-hot-loader": "^4.11.1", + "react-intl": "^3.12.0", "react-scripts": "3.0.1", "styled-components": "^4.3.2", "typescript": "3.5.2" diff --git a/dm/portal/frontend/src/App.tsx b/dm/portal/frontend/src/App.tsx index 22f061f64f..83910625f6 100644 --- a/dm/portal/frontend/src/App.tsx +++ b/dm/portal/frontend/src/App.tsx @@ -1,4 +1,5 @@ import React, { useState, createContext } from 'react' + import StartStep from './components/StartStep' import NamingStep from './components/NamingStep' import InstancesStep from './components/InstancesStep' @@ -114,4 +115,4 @@ function App() { ) } -export default (process.env.NODE_ENV === 'development' ? hot(App) : App) +export default process.env.NODE_ENV === 'development' ? hot(App) : App diff --git a/dm/portal/frontend/src/components/BinlogFilterModal.tsx b/dm/portal/frontend/src/components/BinlogFilterModal.tsx index 664d521de3..0b53abd06a 100644 --- a/dm/portal/frontend/src/components/BinlogFilterModal.tsx +++ b/dm/portal/frontend/src/components/BinlogFilterModal.tsx @@ -1,7 +1,8 @@ import React, { useState } from 'react' import { Modal, Tree } from 'antd' -import { IFullSchema, IFullTable } from '../types' import styled from 'styled-components' +import { FormattedMessage, useIntl } from 'react-intl' +import { IFullSchema, IFullTable } from '../types' import { ALL_DML, DDL_FOR_SCHEMA, DDL_FOR_TABLE } from '../utils/config-util' const { TreeNode } = Tree @@ -23,6 +24,7 @@ function BinlogFilterModal({ targetItem, onUpdateItem }: Props) { + const intl = useIntl() const forTable = (targetItem as IFullTable).type === 'table' const [checkedKeys, setCheckedKeys] = useState(targetItem.filters) const [filtersChanged, setFiltersChanged] = useState(false) @@ -46,7 +48,10 @@ function BinlogFilterModal({ return ( {!forTable && ( - 注意:修改库的过滤规则会重置所有此库的上游表的过滤规则 + )} (dbConfig) @@ -100,7 +102,7 @@ function DatabaseConfig({ dbConfig, onData }: DatabaseConfigProps) { onChange={changeHandler} />
- + - + - + ( @@ -336,7 +339,12 @@ function InstancesStep({ setLoading(true) const res = await checkTargetInstance(targetInstance) if (res.err) { - message.error(`验证下游实例失败!${res.err.message}`) + message.error( + intl.formatMessage( + { id: 'downstream_fail' }, + { errMsg: res.err.message } + ) + ) setLoading(false) return } @@ -348,7 +356,10 @@ function InstancesStep({ const res = await checkSourceInstance(instance) if (res.err) { message.error( - `验证上游实例 ${instance.sourceId} 失败!${res.err.message}` + intl.formatMessage( + { id: 'upstream_fail' }, + { sourceId: instance.sourceId, errMsg: res.err.message } + ) ) setLoading(false) return @@ -368,7 +379,9 @@ function InstancesStep({ return ( -

上游实例

+

+ +

{sourceInstances.map(item => (
)} -

下游实例

+

+ +

- +
diff --git a/dm/portal/frontend/src/components/Layout.tsx b/dm/portal/frontend/src/components/Layout.tsx index 1e6f37002a..9915b0e6ec 100644 --- a/dm/portal/frontend/src/components/Layout.tsx +++ b/dm/portal/frontend/src/components/Layout.tsx @@ -1,12 +1,43 @@ -import React from 'react' +import React, { useState } from 'react' +import { Radio } from 'antd' import styled from 'styled-components' +import { IntlProvider } from 'react-intl' +import getMessages from '../locales' + const Container = styled.div` padding-top: 60px; + + .change-locale { + position: fixed; + right: 12px; + top: 12px; + } ` const DmLayout: React.FC = ({ children }) => { - return {children} + const [locale, setLocale] = useState('en') + + return ( + +
+ setLocale(e.target.value)} + > + + English + + + 中文 + + +
+ + {children} + +
+ ) } export default DmLayout diff --git a/dm/portal/frontend/src/components/MigrateStep.tsx b/dm/portal/frontend/src/components/MigrateStep.tsx index 19e920cd98..30366b43cc 100644 --- a/dm/portal/frontend/src/components/MigrateStep.tsx +++ b/dm/portal/frontend/src/components/MigrateStep.tsx @@ -1,6 +1,9 @@ import React, { useState, useMemo, useRef } from 'react' import { Button, Icon, Tree, Tooltip, message, Checkbox } from 'antd' +import { AntTreeNodeSelectedEvent } from 'antd/lib/tree' +import { AntTreeNodeDropEvent, AntTreeNodeMouseEvent } from 'antd/lib/tree/Tree' import styled from 'styled-components' +import { FormattedMessage, useIntl } from 'react-intl' import { IPageAction, IFullInstance, @@ -14,8 +17,6 @@ import { ITaskInfo, IInstances } from '../types' -import { AntTreeNodeSelectedEvent } from 'antd/lib/tree' -import { AntTreeNodeDropEvent, AntTreeNodeMouseEvent } from 'antd/lib/tree/Tree' import BinlogFilterModal from './BinlogFilterModal' import { genFinalConfig } from '../utils/config-util' import { generateConfig, downloadConfig } from '../services/api' @@ -88,6 +89,7 @@ const Container = styled.div` .auto-sync-option { margin-top: 10px; + width: 300px; } ` @@ -122,6 +124,8 @@ type Props = IPageAction & { } function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { + const intl = useIntl() + const sourceInstances: IFullInstances = sourceConfig.sourceInstances const [sourceSchemas, setSourceSchemas] = useState( sourceConfig.sourceSchemas @@ -183,7 +187,7 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { function cleanTargetInstance() { // confirm - if (!window.confirm('你确定要重置所有操作吗?此操作会清空下游实例。')) { + if (!window.confirm(intl.formatMessage({ id: 'reset_confirm' }))) { return } @@ -203,7 +207,7 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { function undo() { // confirm - if (!window.confirm('你确定要撤消此次操作,回到上一步吗?')) { + if (!window.confirm(intl.formatMessage({ id: 'undo_confirm' }))) { return } @@ -339,6 +343,9 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { // console.log(JSON.stringify(selectedTargetItem)) // console.log(selectedTargetItem.toString()) // console.log('target', targetSchemas) + // 现在知道原因了,是因为打印了这个对象后马上对它进行了修改 + // 在 chrome 的 inspect 窗口观察 console.log(obj) 的输出时, + // 看到的并不是对象在打印时刻的值,而是当前最新的值,因为它是一个引用 recordLastState() // console.log('current:', lastStateRef.current) @@ -707,13 +714,16 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { if ((targetItem as IFullInstance).type === 'instance') { return } - const newName = prompt('请输入新名字:', targetItem.newName) + const newName = prompt( + intl.formatMessage({ id: 'new_name' }), + targetItem.newName + ) if (newName === null || newName === targetItem.newName) { // click cancel or change nothing return } if (newName === '') { - alert('名字不能为空') + alert(intl.formatMessage({ id: 'name_can_not_empty' })) return } let existNames: string[] = [] @@ -744,7 +754,7 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { } const nameExisted = existNames.includes(newName) if (nameExisted) { - alert('名字已被占用') + alert(intl.formatMessage({ id: 'name_taken' })) return } @@ -821,17 +831,22 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { let res = await generateConfig(finalConfig) setLoading(false) if (res.err) { - message.error('同步规则创建失败!') + message.error(intl.formatMessage({ id: 'config_create_fail' })) return } - message.info(`同步规则创建成功,文件在服务器上位于 ${res.data.filepath}`) + message.info( + intl.formatMessage( + { id: 'config_create_ok' }, + { filepath: res.data.filepath } + ) + ) downloadConfig(res.data.filepath) } ///////////////////////////////// function goHome() { - if (window.confirm('你确定要返回首页吗?返回将丢失所有操作!')) { + if (window.confirm(intl.formatMessage({ id: 'back_home_confirm' }))) { onNext() } } @@ -866,8 +881,8 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { <> {sourceSchemas[schemaKey].schema}{' '} @@ -885,8 +900,8 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { <> {allTables[tableKey].table}{' '} @@ -930,7 +945,7 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { title={ {schema.newName} @@ -944,7 +959,7 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { {table.newName} @@ -960,7 +975,7 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { {tb.newName} @@ -981,59 +996,68 @@ function MigrateStep({ onNext, onPrev, sourceConfig, ...remainProps }: Props) { return ( -
+
-

上游实例

-
{renderSourceTables()}
-
+

+ +

+
{renderSourceTables()}
+
setAutoSyncUpstream(e.target.checked)} > - 自动同步上游新增库和新增表 + +   + + + - - -
-
+
-

下游实例

-
+

+ +

+
{renderTargetTables()} -
- +
+   - +
-
- - + + -
(() => @@ -59,7 +61,7 @@ function NamingStep({ onNext, onPrev, onData, taskInfo }: Props) { } else { return { status: 'error', - errMsg: '任务名称不合法', + errMsg: intl.formatMessage({ id: 'invalid_task_name' }), value: name } } @@ -69,34 +71,40 @@ function NamingStep({ onNext, onPrev, onData, taskInfo }: Props) {
setTaskName(handleTaskNameChange(e.target.value)) } /> - + setTaskMode(e.target.value)} value={taskMode} > - 全量 - 增量 - All + + + + + + + All - + diff --git a/dm/portal/frontend/src/components/StartStep.tsx b/dm/portal/frontend/src/components/StartStep.tsx index 9936f755e5..73337f8dea 100644 --- a/dm/portal/frontend/src/components/StartStep.tsx +++ b/dm/portal/frontend/src/components/StartStep.tsx @@ -1,6 +1,7 @@ import React from 'react' import { Button } from 'antd' import styled from 'styled-components' +import { FormattedMessage } from 'react-intl' const Container = styled.div` display: flex; @@ -25,8 +26,12 @@ type Props = { function StartStep(props: Props) { return ( -

DM 任务配置生成

- +

+ +

+ {/* 编辑功能目前看没有使用场景,先隐藏,后续再移除 */} {/* */}
diff --git a/dm/portal/frontend/src/components/UploadStep.tsx b/dm/portal/frontend/src/components/UploadStep.tsx index 2a5d3dfe88..1d157299a6 100644 --- a/dm/portal/frontend/src/components/UploadStep.tsx +++ b/dm/portal/frontend/src/components/UploadStep.tsx @@ -1,8 +1,8 @@ import React from 'react' import { Upload, Button, Icon, message } from 'antd' +import { UploadProps, UploadChangeParam } from 'antd/lib/upload' import styled from 'styled-components' import { IPageAction, IFinalConfig } from '../types' -import { UploadProps, UploadChangeParam } from 'antd/lib/upload' import { parseFinalConfig } from '../utils/config-util' const { Dragger } = Upload @@ -47,9 +47,7 @@ function UploadStep({ onPrev, onNext, onData }: Props) { ) } else if (status === 'error') { message.error( - `${info.file.name} file upload failed. error: ${ - info.file.response.error - }` + `${info.file.name} file upload failed. error: ${info.file.response.error}` ) } } diff --git a/dm/portal/frontend/src/locales/en-US.ts b/dm/portal/frontend/src/locales/en-US.ts new file mode 100644 index 0000000000..5119b3d6da --- /dev/null +++ b/dm/portal/frontend/src/locales/en-US.ts @@ -0,0 +1,53 @@ +export default { + // start step + dm_task_config_generator: 'DM Task Configuration Generator', + new_task_config: 'New Task Configuration', + + // name step + task_name: 'Task Name', + sync_mode: 'Sync Mode', + full_mode: 'Full', + inc_mode: 'Incremental', + invalid_task_name: 'Invalid task name', + + // instance step + port: 'Port:', + user: 'User:', + pwd: 'Password:', + upstream: 'Upstream Instance', + downstream: 'Downstream Instance', + upstream_fail: 'Verify upstream instance {sourceId} failed! {errMsg}', + downstream_fail: 'Verify downstream instance failed! {errMsg}', + + // migrate step + reset_confirm: + 'Are you sure to reset all operations? It will clear the downstream instance', + undo_confirm: + 'Are you sure to undo this operation and go back to previous status?', + new_name: 'Please input the new name:', + name_can_not_empty: "The name can't be empty", + name_taken: 'This name has been taken', + config_create_fail: 'The sync rules are created failed!', + config_create_ok: + 'The sync rules are created successfully, the file locates in {filepath}', + back_home_confirm: + 'Are you sure to go back to home page, it will lose all operations!', + auto_sync: 'Auto sync new added databases and tables from upstream', + auto_sync_explain: + "When this option is selected, if the upstream has the new added databases or tables later, they will be sync to the downstream, otherwise they won't.", + go_back_tooltip: 'Go back', + reset_tooltip: 'Reset', + finish_and_download: 'Finish & Download', + go_home: 'Go Home', + + // modal + binlog_filter: 'Binlog filter ({target})', + binlog_modify_warning: + 'Note: It will reset all the tables of this database stayed in upstream filter rules when modify the database filter rules', + + // common + cancel: 'Cancel', + pre: 'Pre', + next: 'Next', + add: 'Add' +} diff --git a/dm/portal/frontend/src/locales/index.ts b/dm/portal/frontend/src/locales/index.ts new file mode 100644 index 0000000000..110afdfbe5 --- /dev/null +++ b/dm/portal/frontend/src/locales/index.ts @@ -0,0 +1,7 @@ +import zhCN from './zh-CN' +import enUS from './en-US' + +export default (locale: string) => { + if (locale.startsWith('zh')) return zhCN + return enUS +} diff --git a/dm/portal/frontend/src/locales/zh-CN.ts b/dm/portal/frontend/src/locales/zh-CN.ts new file mode 100644 index 0000000000..185170efff --- /dev/null +++ b/dm/portal/frontend/src/locales/zh-CN.ts @@ -0,0 +1,49 @@ +export default { + // start step + dm_task_config_generator: 'DM 任务配置生成', + new_task_config: '新建任务配置', + + // name step + task_name: '任务名称', + sync_mode: '同步模式', + full_mode: '全量', + inc_mode: '增量', + invalid_task_name: '任务名称不合法', + + // instance step + port: '端口:', + user: '用户名:', + pwd: '密码:', + upstream: '上游实例', + downstream: '下游实例', + upstream_fail: '验证上游实例 {sourceId} 失败!{errMsg}', + downstream_fail: '验证下游实例失败!{errMsg}', + + // migrate step + reset_confirm: '你确定要重置所有操作吗?此操作会清空下游实例。', + undo_confirm: '你确定要撤消此次操作,回到上一步吗?', + new_name: '请输入新名字:', + name_can_not_empty: '名字不能为空', + name_taken: '名字已被占用', + config_create_fail: '同步规则创建失败!', + config_create_ok: '同步规则创建成功,文件在服务器上位于 {filepath}', + back_home_confirm: '你确定要返回首页吗?返回将丢失所有操作!', + auto_sync: '自动同步上游新增库和新增表', + auto_sync_explain: + '当选中此选项时,后续如果上游有新增的库或表,也会同步到下游。否则,后续上游新增的库或表不会同步到下游。', + go_back_tooltip: '回到上一步', + reset_tooltip: '重置所有操作', + finish_and_download: '完成并下载', + go_home: '返回首页', + + // modal + binlog_filter: 'Binlog 过滤 ({target})', + binlog_modify_warning: + '注意:修改库的过滤规则会重置所有此库的上游表的过滤规则', + + // common + cancel: '取消', + pre: '上一步', + next: '下一步', + add: '添加' +} diff --git a/dm/portal/frontend/yarn.lock b/dm/portal/frontend/yarn.lock index 4603d8f40e..ac9d66621e 100644 --- a/dm/portal/frontend/yarn.lock +++ b/dm/portal/frontend/yarn.lock @@ -955,6 +955,44 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.3.tgz#6310a047f12d21a1036fb031317219892440416f" integrity sha512-4zAPlpDEh2VwXswwr/t8xGNDGg8RQiPxtxZ3qQEXyQsBV39ptTdESCjuBvGze1nLMVrxmTIKmnO/nAV8Tqjjzg== +"@formatjs/intl-displaynames@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-1.2.0.tgz#b89935e232a454d113c7a6684c01ae391682a46d" + integrity sha512-mUGI2sc6OABkrMj42HlOpK1h96EVrN+gOhzbyCTMH9SVH/gPPLr/zFRH3KFWtBwxqhYsDghvUwm8xkdFOK0kTg== + dependencies: + "@formatjs/intl-utils" "^2.2.0" + +"@formatjs/intl-listformat@^1.3.7": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-1.4.1.tgz#a467cc6857808f2eec78e5bdd0ae03b224e89d0c" + integrity sha512-AX0o1y5xXyMY4ebZOO+UujMcDhniYDs50KpwGzjUPV+bBILwRYqH/6IprZZG/V8YSOtetZlalZiwzJ50dH6PuQ== + dependencies: + "@formatjs/intl-utils" "^2.2.0" + +"@formatjs/intl-relativetimeformat@^4.5.7": + version "4.5.9" + resolved "https://registry.yarnpkg.com/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-4.5.9.tgz#d9b74724a7cbcb4edc9d751b2979195fab4d39cc" + integrity sha512-6rgPXQl5MrPPbCuNiHxolzO6xNCHphCVEWW6RWGy7t/Mek70gD7nq1erW8fbQJ0XL/UeAC0Cz/+ggh7vaSsKNA== + dependencies: + "@formatjs/intl-utils" "^2.2.0" + +"@formatjs/intl-unified-numberformat@^3.0.4", "@formatjs/intl-unified-numberformat@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@formatjs/intl-unified-numberformat/-/intl-unified-numberformat-3.2.0.tgz#5197987e61ba0972889105e525f1cbe6d91cf46f" + integrity sha512-SZMTV/tR0h7nYhS2x69S7zhHXaBmE0ZTR2OIiakt8W7uYWVgcRhu/LgUeVtGzpwPI2ChcOjNMtX/k6y1M9aDNA== + dependencies: + "@formatjs/intl-utils" "^2.2.0" + +"@formatjs/intl-utils@^2.0.4", "@formatjs/intl-utils@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@formatjs/intl-utils/-/intl-utils-2.2.0.tgz#ba6e12fe64ff7fd160be392007c47d24b7ae5c75" + integrity sha512-+Az7tR1av1DHZu9668D8uh9atT6vp+FFmEF8BrEssv0OqzpVjpVBGVmcgPzQP8k2PQjVlm/h2w8cTt0knn132w== + +"@formatjs/macro@^0.2.6": + version "0.2.6" + resolved "https://registry.yarnpkg.com/@formatjs/macro/-/macro-0.2.6.tgz#eb173658d803416a43210778b2f5c04c5a240bb6" + integrity sha512-DfdnLJf8+PwLHzJECZ1Xfa8+sI9akQnUuLN2UdkaExTQmlY0Vs36rMzEP0JoVDBMk+KdQbJNt72rPeZkBNcKWg== + "@hapi/address@2.x.x": version "2.0.0" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a" @@ -1278,6 +1316,19 @@ dependencies: "@babel/types" "^7.3.0" +"@types/hoist-non-react-statics@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + +"@types/invariant@^2.2.31": + version "2.2.31" + resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.31.tgz#4444c03004f215289dbca3856538434317dd28b2" + integrity sha512-jMlgg9pIURvy9jgBHCjQp/CyBjYHUwj91etVcDdXkFl2CwTFiQlB+8tcsMeXpXf2PFE5X2pjk4Gm43hQSMHAdA== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" @@ -4823,6 +4874,13 @@ hoist-non-react-statics@^3.3.0: dependencies: react-is "^16.7.0" +hoist-non-react-statics@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + hosted-git-info@^2.1.4: version "2.7.1" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" @@ -5157,6 +5215,31 @@ internal-ip@^4.2.0: default-gateway "^4.2.0" ipaddr.js "^1.9.0" +intl-format-cache@^4.2.19, intl-format-cache@^4.2.21: + version "4.2.21" + resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-4.2.21.tgz#d8e0bdfc357448f48dc1ab44670dc64a19b24f51" + integrity sha512-6pZlBdqTRUuuwRWywPItHY1JQwzQxWcpBHv6w4M8T6bGzAsiL/QmI+XsdOhsqJLaL4ZmTATn1kIkNlMk4VzSLQ== + +intl-locales-supported@^1.8.4: + version "1.8.4" + resolved "https://registry.yarnpkg.com/intl-locales-supported/-/intl-locales-supported-1.8.4.tgz#e1d19812afa50dc2e2a2b4741ceb4030522d45b1" + integrity sha512-wO0JhDqhshhkq8Pa9CLcstqd1aCXjfMgfMzjD6mDreS3mTSDbjGiMU+07O8BdJGxed7Q0Wf3TFVjGq0W3Y0n1w== + +intl-messageformat-parser@^3.6.2, intl-messageformat-parser@^3.6.4: + version "3.6.4" + resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-3.6.4.tgz#5199d106d816c3dda26ee0694362a9cf823978fb" + integrity sha512-RgPGwue0mJtoX2Ax8EmMzJzttxjnva7gx0Q7mKJ4oALrTZvtmCeAw5Msz2PcjW4dtCh/h7vN/8GJCxZO1uv+OA== + dependencies: + "@formatjs/intl-unified-numberformat" "^3.2.0" + +intl-messageformat@^7.8.2: + version "7.8.4" + resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-7.8.4.tgz#c29146a06b9cd26662978a4d95fff2b133e3642f" + integrity sha512-yS0cLESCKCYjseCOGXuV4pxJm/buTfyCJ1nzQjryHmSehlptbZbn9fnlk1I9peLopZGGbjj46yHHiTAEZ1qOTA== + dependencies: + intl-format-cache "^4.2.21" + intl-messageformat-parser "^3.6.4" + invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -8887,6 +8970,26 @@ react-hot-loader@^4.11.1, react-hot-loader@^4.6.3: shallowequal "^1.0.2" source-map "^0.7.3" +react-intl@^3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-3.12.0.tgz#98ef1c94434cc25a8c67448e1e283e6bfe11b2fc" + integrity sha512-VQWkFYSKKoi85p3gOXgG80KkBImdBJXwJxssO9gqdelW/fuVnxQLXgYOKuOqWrUz5beXK+qBve6bTpblh1ep2g== + dependencies: + "@formatjs/intl-displaynames" "^1.2.0" + "@formatjs/intl-listformat" "^1.3.7" + "@formatjs/intl-relativetimeformat" "^4.5.7" + "@formatjs/intl-unified-numberformat" "^3.0.4" + "@formatjs/intl-utils" "^2.0.4" + "@formatjs/macro" "^0.2.6" + "@types/hoist-non-react-statics" "^3.3.1" + "@types/invariant" "^2.2.31" + hoist-non-react-statics "^3.3.1" + intl-format-cache "^4.2.19" + intl-locales-supported "^1.8.4" + intl-messageformat "^7.8.2" + intl-messageformat-parser "^3.6.2" + shallow-equal "^1.2.1" + react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4: version "16.8.6" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" @@ -9587,6 +9690,11 @@ shallow-equal@^1.0.0: resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.2.0.tgz#fd828d2029ff4e19569db7e19e535e94e2d1f5cc" integrity sha512-Z21pVxR4cXsfwpMKMhCEIO1PCi5sp7KEp+CmOpBQ+E8GpHwKOw2sEzk7sgblM3d/j4z4gakoWEoPcjK0VJQogA== +shallow-equal@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da" + integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA== + shallowequal@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-0.2.2.tgz#1e32fd5bcab6ad688a4812cb0cc04efc75c7014e"