@electerm/electerm-react 3.10.0 → 3.11.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/common/bookmark-schemas.js +165 -0
- package/client/common/constants.js +7 -0
- package/client/common/parse-quick-connect.js +13 -10
- package/client/common/sanitize-filename.js +66 -0
- package/client/common/ws.js +25 -6
- package/client/common/zod.js +180 -0
- package/client/components/ai/agent-tool-call-card.jsx +90 -0
- package/client/components/ai/agent-tools.js +193 -0
- package/client/components/ai/agent.js +159 -0
- package/client/components/ai/ai-chat-entry.jsx +11 -0
- package/client/components/ai/ai-chat-history-item.jsx +48 -2
- package/client/components/ai/ai-chat.jsx +25 -6
- package/client/components/ai/ai-config.jsx +45 -4
- package/client/components/ai/ai.styl +73 -0
- package/client/components/bookmark-form/bookmark-schema.js +1 -0
- package/client/components/bookmark-form/config/serial.js +2 -1
- package/client/components/common/font-select.jsx +45 -0
- package/client/components/main/main.jsx +3 -3
- package/client/components/rdp/file-transfer.js +3 -0
- package/client/components/session/session.jsx +2 -2
- package/client/components/setting-panel/setting-terminal.jsx +6 -28
- package/client/components/setting-panel/text-bg-modal.jsx +8 -27
- package/client/components/setting-sync/setting-sync-form.jsx +1 -1
- package/client/components/sftp/file-item.jsx +5 -4
- package/client/components/shortcuts/shortcut-handler.js +9 -9
- package/client/components/terminal/terminal-error-handle.jsx +1 -1
- package/client/components/terminal/terminal-interactive-ui.jsx +157 -0
- package/client/components/terminal/terminal-interactive.jsx +64 -163
- package/client/components/terminal/terminal.jsx +11 -0
- package/client/components/terminal-info/terminal-info-entry.jsx +11 -0
- package/client/components/text-editor/text-editor-entry.jsx +11 -0
- package/client/components/widgets/widget-form.jsx +27 -2
- package/client/entry/worker.js +9 -5
- package/client/store/mcp-handler.js +22 -2
- package/client/store/watch.js +38 -36
- package/package.json +1 -1
- package/client/common/safe-name.js +0 -19
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formItemLayout } from '../../../common/form-layout.js'
|
|
2
|
-
import { terminalSerialType, commonBaudRates, commonDataBits, commonStopBits, commonParities } from '../../../common/constants.js'
|
|
2
|
+
import { terminalSerialType, commonBaudRates, commonDataBits, commonStopBits, commonParities, commonLineEndings } from '../../../common/constants.js'
|
|
3
3
|
import defaultSettings from '../../../common/default-setting.js'
|
|
4
4
|
import { createBaseInitValues, getTerminalBackgroundDefaults } from '../common/init-values.js'
|
|
5
5
|
import { commonFields } from './common-fields.js'
|
|
@@ -57,6 +57,7 @@ const serialConfig = {
|
|
|
57
57
|
{ type: 'switch', name: 'xon', label: 'xon', valuePropName: 'checked' },
|
|
58
58
|
{ type: 'switch', name: 'xoff', label: 'xoff', valuePropName: 'checked' },
|
|
59
59
|
{ type: 'switch', name: 'xany', label: 'xany', valuePropName: 'checked' },
|
|
60
|
+
{ type: 'select', name: 'lineEnding', label: 'lineEnding', options: commonLineEndings.map(d => ({ value: d.value, label: d.label })) },
|
|
60
61
|
commonFields.runScripts,
|
|
61
62
|
commonFields.description,
|
|
62
63
|
{ type: 'input', name: 'type', label: 'type', hidden: true }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { useMemo, useCallback } from 'react'
|
|
2
|
+
import { Select } from 'antd'
|
|
3
|
+
|
|
4
|
+
const e = window.translate
|
|
5
|
+
|
|
6
|
+
export default function FontSelect ({
|
|
7
|
+
value,
|
|
8
|
+
onChange,
|
|
9
|
+
placeholder,
|
|
10
|
+
style
|
|
11
|
+
}) {
|
|
12
|
+
const { fonts = [] } = window.et || {}
|
|
13
|
+
|
|
14
|
+
const options = useMemo(() => {
|
|
15
|
+
return fonts.map(f => ({
|
|
16
|
+
value: f,
|
|
17
|
+
label: (
|
|
18
|
+
<span
|
|
19
|
+
className='iblock'
|
|
20
|
+
style={{ fontFamily: f }}
|
|
21
|
+
>
|
|
22
|
+
{f}
|
|
23
|
+
</span>
|
|
24
|
+
)
|
|
25
|
+
}))
|
|
26
|
+
}, [fonts])
|
|
27
|
+
|
|
28
|
+
const handleChange = useCallback((vals) => {
|
|
29
|
+
onChange(vals)
|
|
30
|
+
}, [onChange])
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Select
|
|
34
|
+
mode='tags'
|
|
35
|
+
value={value}
|
|
36
|
+
onChange={handleChange}
|
|
37
|
+
className='width-100'
|
|
38
|
+
placeholder={placeholder || e('selectFontFamily')}
|
|
39
|
+
showSearch
|
|
40
|
+
options={options}
|
|
41
|
+
filterOption={(input, option) =>
|
|
42
|
+
(option?.value ?? '').toLowerCase().includes(input.toLowerCase())}
|
|
43
|
+
/>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
@@ -4,7 +4,7 @@ import Layout from '../layout/layout'
|
|
|
4
4
|
import FileInfoModal from '../sftp/file-info-modal'
|
|
5
5
|
import UpdateCheck from './upgrade'
|
|
6
6
|
import SettingModal from '../setting-panel/setting-modal'
|
|
7
|
-
import TextEditor from '../text-editor/text-editor'
|
|
7
|
+
import TextEditor from '../text-editor/text-editor-entry'
|
|
8
8
|
import Sidebar from '../sidebar'
|
|
9
9
|
import CssOverwrite from '../bg/css-overwrite'
|
|
10
10
|
import UiTheme from './ui-theme'
|
|
@@ -19,7 +19,6 @@ import TransportsActionStore from '../file-transfer/transports-action-store.jsx'
|
|
|
19
19
|
import classnames from 'classnames'
|
|
20
20
|
import ShortcutControl from '../shortcuts/shortcut-control.jsx'
|
|
21
21
|
import { isMac, isWin, textTerminalBgValue } from '../../common/constants'
|
|
22
|
-
import TerminalInfo from '../terminal-info/terminal-info'
|
|
23
22
|
import { ConfigProvider } from 'antd'
|
|
24
23
|
import { NotificationContainer } from '../common/notification'
|
|
25
24
|
import InfoModal from '../sidebar/info-modal.jsx'
|
|
@@ -27,7 +26,7 @@ import RightSidePanel from '../side-panel-r/side-panel-r'
|
|
|
27
26
|
import ConnectionHoppingWarning from './connection-hopping-warnning'
|
|
28
27
|
import SshConfigLoadNotify from '../ssh-config/ssh-config-load-notify'
|
|
29
28
|
import LoadSshConfigs from '../ssh-config/load-ssh-configs'
|
|
30
|
-
import AIChat from '../ai/ai-chat'
|
|
29
|
+
import AIChat from '../ai/ai-chat-entry'
|
|
31
30
|
import AIConfigModal from '../ai/ai-config-modal'
|
|
32
31
|
import Opacity from '../common/opacity'
|
|
33
32
|
import MoveItemModal from '../tree-list/move-item-modal'
|
|
@@ -40,6 +39,7 @@ import UnixTimestampTooltip from '../terminal/unix-timestamp-tooltip'
|
|
|
40
39
|
import { pick } from 'lodash-es'
|
|
41
40
|
import deepCopy from 'json-deep-copy'
|
|
42
41
|
import './wrapper.styl'
|
|
42
|
+
import TerminalInfo from '../terminal-info/terminal-info-entry'
|
|
43
43
|
import './term-fullscreen.styl'
|
|
44
44
|
|
|
45
45
|
export default auto(function Index (props) {
|
|
@@ -273,6 +273,9 @@ export class FileTransferManager {
|
|
|
273
273
|
})
|
|
274
274
|
|
|
275
275
|
this.log(`Downloaded ${fileInfo.name} (${filesize(fileInfo._totalSize)}) to ${fullPath}`, 'success')
|
|
276
|
+
this.hasRemoteFiles = false
|
|
277
|
+
this.pendingDownloads.clear()
|
|
278
|
+
this.notifyStateChange()
|
|
276
279
|
if (this.onDownloadComplete) {
|
|
277
280
|
this.onDownloadComplete(fullPath, fileInfo.name, fileInfo._totalSize)
|
|
278
281
|
}
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
} from '../../common/constants'
|
|
36
36
|
import { SplitViewIcon } from '../icons/split-view'
|
|
37
37
|
import { refs } from '../common/ref'
|
|
38
|
-
import
|
|
38
|
+
import sanitizeFilename from '../../common/sanitize-filename.js'
|
|
39
39
|
import { HeartbeatIcon } from '../icons/heartbeat'
|
|
40
40
|
import './session.styl'
|
|
41
41
|
|
|
@@ -343,7 +343,7 @@ export default class SessionWrapper extends Component {
|
|
|
343
343
|
height
|
|
344
344
|
} = this.calcTermWidthHeight()
|
|
345
345
|
const themeConfig = copy(window.store.getThemeConfig())
|
|
346
|
-
const logName =
|
|
346
|
+
const logName = sanitizeFilename(`${tab.title ? tab.title + '_' : ''}${tab.host ? tab.host + '_' : ''}${tab.id}`)
|
|
347
347
|
const pops = {
|
|
348
348
|
...this.props,
|
|
349
349
|
sftpPathFollowSsh,
|
|
@@ -28,6 +28,7 @@ import { chooseSaveDirectory } from '../../common/choose-save-folder'
|
|
|
28
28
|
import mapper from '../../common/auto-complete-data-mapper'
|
|
29
29
|
import KeywordForm from './keywords-form'
|
|
30
30
|
import Link from '../common/external-link'
|
|
31
|
+
import FontSelect from '../common/font-select'
|
|
31
32
|
import HelpIcon from '../common/help-icon'
|
|
32
33
|
import KeywordsTransport from './keywords-transport'
|
|
33
34
|
import fs from '../../common/fs'
|
|
@@ -427,36 +428,13 @@ export default class SettingTerminal extends Component {
|
|
|
427
428
|
}
|
|
428
429
|
|
|
429
430
|
renderFontFamily = () => {
|
|
430
|
-
const { fonts = [] } = window.et
|
|
431
431
|
const { fontFamily } = this.props.config
|
|
432
|
-
const props = {
|
|
433
|
-
mode: 'multiple',
|
|
434
|
-
onChange: this.handleChangeFont,
|
|
435
|
-
value: fontFamily.split(/, */g).filter(d => d.trim()),
|
|
436
|
-
style: { width: '100%' }
|
|
437
|
-
}
|
|
438
432
|
return (
|
|
439
|
-
<
|
|
440
|
-
{
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
fonts.map(f => {
|
|
445
|
-
return (
|
|
446
|
-
<Option value={f} key={f}>
|
|
447
|
-
<span
|
|
448
|
-
className='font-option'
|
|
449
|
-
style={{
|
|
450
|
-
fontFamily: f
|
|
451
|
-
}}
|
|
452
|
-
>
|
|
453
|
-
{f}
|
|
454
|
-
</span>
|
|
455
|
-
</Option>
|
|
456
|
-
)
|
|
457
|
-
})
|
|
458
|
-
}
|
|
459
|
-
</Select>
|
|
433
|
+
<FontSelect
|
|
434
|
+
onChange={this.handleChangeFont}
|
|
435
|
+
value={fontFamily.split(/, */g).filter(d => d.trim())}
|
|
436
|
+
style={{ width: '100%' }}
|
|
437
|
+
/>
|
|
460
438
|
)
|
|
461
439
|
}
|
|
462
440
|
|
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
Input,
|
|
4
4
|
InputNumber,
|
|
5
5
|
Space,
|
|
6
|
-
Select,
|
|
7
6
|
Button,
|
|
8
7
|
Modal
|
|
9
8
|
} from 'antd'
|
|
10
9
|
import { ColorPicker } from '../bookmark-form/common/color-picker.jsx'
|
|
10
|
+
import FontSelect from '../common/font-select'
|
|
11
11
|
|
|
12
12
|
const { TextArea } = Input
|
|
13
13
|
const e = window.translate
|
|
@@ -24,16 +24,16 @@ export default function TextBgModal ({
|
|
|
24
24
|
const [text, setText] = useState(initialText)
|
|
25
25
|
const [fontSize, setFontSize] = useState(initialSize)
|
|
26
26
|
const [color, setColor] = useState(initialColor)
|
|
27
|
-
const [fontFamily, setFontFamily] = useState(
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const [fontFamily, setFontFamily] = useState(
|
|
28
|
+
initialFontFamily.split(/, */g).filter(d => d.trim())
|
|
29
|
+
)
|
|
30
30
|
|
|
31
31
|
const handleOk = () => {
|
|
32
32
|
onOk({
|
|
33
33
|
text,
|
|
34
34
|
fontSize,
|
|
35
35
|
color,
|
|
36
|
-
fontFamily
|
|
36
|
+
fontFamily: fontFamily.join(', ')
|
|
37
37
|
})
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -43,7 +43,7 @@ export default function TextBgModal ({
|
|
|
43
43
|
setText(initialText)
|
|
44
44
|
setFontSize(initialSize)
|
|
45
45
|
setColor(initialColor)
|
|
46
|
-
setFontFamily(initialFontFamily)
|
|
46
|
+
setFontFamily(initialFontFamily.split(/, */g).filter(d => d.trim()))
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
const footer = (
|
|
@@ -108,30 +108,11 @@ export default function TextBgModal ({
|
|
|
108
108
|
|
|
109
109
|
<div>
|
|
110
110
|
<b>{e('fontFamily')}</b>
|
|
111
|
-
<
|
|
111
|
+
<FontSelect
|
|
112
112
|
value={fontFamily}
|
|
113
113
|
onChange={setFontFamily}
|
|
114
|
-
style={{ width: '100%' }}
|
|
115
114
|
placeholder={e('selectFontFamily')}
|
|
116
|
-
|
|
117
|
-
>
|
|
118
|
-
{
|
|
119
|
-
fonts.map(f => {
|
|
120
|
-
return (
|
|
121
|
-
<Select.Option value={f} key={f}>
|
|
122
|
-
<span
|
|
123
|
-
className='font-option'
|
|
124
|
-
style={{
|
|
125
|
-
fontFamily: f
|
|
126
|
-
}}
|
|
127
|
-
>
|
|
128
|
-
{f}
|
|
129
|
-
</span>
|
|
130
|
-
</Select.Option>
|
|
131
|
-
)
|
|
132
|
-
})
|
|
133
|
-
}
|
|
134
|
-
</Select>
|
|
115
|
+
/>
|
|
135
116
|
</div>
|
|
136
117
|
</Space>
|
|
137
118
|
</div>
|
|
@@ -36,6 +36,7 @@ import time from '../../common/time'
|
|
|
36
36
|
import { filesize } from 'filesize'
|
|
37
37
|
import { createTransferProps } from './transfer-common'
|
|
38
38
|
import generate from '../../common/uid'
|
|
39
|
+
import sanitizeFilename from '../../common/sanitize-filename'
|
|
39
40
|
import { refsStatic, refs, filesRef } from '../common/ref'
|
|
40
41
|
import iconsMap from '../sys-menu/icons-map'
|
|
41
42
|
|
|
@@ -174,7 +175,7 @@ export default class FileSection extends React.Component {
|
|
|
174
175
|
? item.replace(/^remote:/, '')
|
|
175
176
|
: item
|
|
176
177
|
const { name } = getFolderFromFilePath(fromPath, isRemote)
|
|
177
|
-
const toPath = resolve(path, name)
|
|
178
|
+
const toPath = resolve(path, sanitizeFilename(name))
|
|
178
179
|
res.push({
|
|
179
180
|
typeFrom: isRemote ? typeMap.remote : typeMap.local,
|
|
180
181
|
typeTo: type,
|
|
@@ -326,7 +327,7 @@ export default class FileSection extends React.Component {
|
|
|
326
327
|
toFile = {
|
|
327
328
|
...toFile,
|
|
328
329
|
...getFolderFromFilePath(
|
|
329
|
-
resolve(toFile.path, toFile.name)
|
|
330
|
+
resolve(toFile.path, sanitizeFilename(toFile.name))
|
|
330
331
|
),
|
|
331
332
|
id: undefined
|
|
332
333
|
}
|
|
@@ -367,7 +368,7 @@ export default class FileSection extends React.Component {
|
|
|
367
368
|
return this.doTransferSelected(
|
|
368
369
|
null,
|
|
369
370
|
files,
|
|
370
|
-
resolve(toFile.path, toFile.name),
|
|
371
|
+
resolve(toFile.path, sanitizeFilename(toFile.name)),
|
|
371
372
|
toFile.type,
|
|
372
373
|
operation
|
|
373
374
|
)
|
|
@@ -784,7 +785,7 @@ export default class FileSection extends React.Component {
|
|
|
784
785
|
if (toPathBase) {
|
|
785
786
|
toPath = toPathBase
|
|
786
787
|
}
|
|
787
|
-
toPath = resolve(toPath, name)
|
|
788
|
+
toPath = resolve(toPath, sanitizeFilename(name))
|
|
788
789
|
const obj = {
|
|
789
790
|
host: this.props.tab?.host,
|
|
790
791
|
tabType: this.props.tab?.type,
|
|
@@ -54,6 +54,9 @@ export function handleTerminalSelectionReplace (event, ctx) {
|
|
|
54
54
|
const isPrintable = key && key.length === 1
|
|
55
55
|
if (!isBackspace && !isDelete && !isPrintable) return false
|
|
56
56
|
|
|
57
|
+
const info = getSelectionReplaceInfo(ctx.term)
|
|
58
|
+
if (!info) return false
|
|
59
|
+
|
|
57
60
|
if (event && event.preventDefault) {
|
|
58
61
|
event.preventDefault()
|
|
59
62
|
}
|
|
@@ -61,9 +64,6 @@ export function handleTerminalSelectionReplace (event, ctx) {
|
|
|
61
64
|
event.stopPropagation()
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
const info = getSelectionReplaceInfo(ctx.term)
|
|
65
|
-
if (!info) return false
|
|
66
|
-
|
|
67
67
|
const { startX, endX, cursorX } = info
|
|
68
68
|
const move = startX - cursorX
|
|
69
69
|
if (move > 0) {
|
|
@@ -146,6 +146,12 @@ export function shortcutExtend (Cls) {
|
|
|
146
146
|
if (isInAntdInput()) {
|
|
147
147
|
return
|
|
148
148
|
}
|
|
149
|
+
// During IME composition, let xterm handle the event through its
|
|
150
|
+
// CompositionHelper. Intercepting here breaks composition (e.g. first
|
|
151
|
+
// char lost, closing bracket duplicated when typing Chinese inside brackets).
|
|
152
|
+
if (event.isComposing) {
|
|
153
|
+
return
|
|
154
|
+
}
|
|
149
155
|
if (handleTerminalSelectionReplace(event, this)) {
|
|
150
156
|
return false
|
|
151
157
|
}
|
|
@@ -156,12 +162,6 @@ export function shortcutExtend (Cls) {
|
|
|
156
162
|
!altKey &&
|
|
157
163
|
!ctrlKey
|
|
158
164
|
) {
|
|
159
|
-
// If IME is composing, let the browser delete the composition char only
|
|
160
|
-
// Returning false tells xterm not to process the event (and not to call
|
|
161
|
-
// preventDefault), so the native textarea backspace still works for IME.
|
|
162
|
-
if (event.isComposing) {
|
|
163
|
-
return false
|
|
164
|
-
}
|
|
165
165
|
this.props.onDelKeyPressed()
|
|
166
166
|
const delKey = this.props.config.backspaceMode === '^?' ? 8 : 127
|
|
167
167
|
const altDelDelKey = delKey === 8 ? 127 : 8
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* terminal interactive UI - renders a single interactive event modal
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Form, Button } from 'antd'
|
|
6
|
+
import Modal from '../common/modal'
|
|
7
|
+
import InputAutoFocus from '../common/input-auto-focus'
|
|
8
|
+
|
|
9
|
+
const e = window.translate
|
|
10
|
+
const FormItem = Form.Item
|
|
11
|
+
|
|
12
|
+
export default function TermInteractiveUI ({
|
|
13
|
+
opts,
|
|
14
|
+
onSend,
|
|
15
|
+
onClose
|
|
16
|
+
}) {
|
|
17
|
+
const [form] = Form.useForm()
|
|
18
|
+
|
|
19
|
+
function onCancel () {
|
|
20
|
+
onSend({
|
|
21
|
+
id: opts.id,
|
|
22
|
+
results: []
|
|
23
|
+
})
|
|
24
|
+
onClose()
|
|
25
|
+
}
|
|
26
|
+
function onOk () {
|
|
27
|
+
form.submit()
|
|
28
|
+
}
|
|
29
|
+
function onConfirm () {
|
|
30
|
+
onSend({
|
|
31
|
+
id: opts.id,
|
|
32
|
+
results: [opts.options.confirmResult || 'yes']
|
|
33
|
+
})
|
|
34
|
+
onClose()
|
|
35
|
+
}
|
|
36
|
+
function onIgnore () {
|
|
37
|
+
onSend({
|
|
38
|
+
id: opts.id,
|
|
39
|
+
results: Object.keys(opts.options.prompts).map(() => '')
|
|
40
|
+
})
|
|
41
|
+
onClose()
|
|
42
|
+
}
|
|
43
|
+
function onFinish (res) {
|
|
44
|
+
onSend({
|
|
45
|
+
id: opts.id,
|
|
46
|
+
results: Object.values(res)
|
|
47
|
+
})
|
|
48
|
+
onClose()
|
|
49
|
+
}
|
|
50
|
+
function renderFormItem (pro, i) {
|
|
51
|
+
const {
|
|
52
|
+
prompt,
|
|
53
|
+
echo
|
|
54
|
+
} = pro
|
|
55
|
+
const note = (opts.options.instructions || [])[i]
|
|
56
|
+
const type = echo
|
|
57
|
+
? 'input'
|
|
58
|
+
: 'password'
|
|
59
|
+
return (
|
|
60
|
+
<FormItem
|
|
61
|
+
key={prompt + i}
|
|
62
|
+
label={prompt}
|
|
63
|
+
rules={[{
|
|
64
|
+
required: true, message: 'required'
|
|
65
|
+
}]}
|
|
66
|
+
>
|
|
67
|
+
<div>
|
|
68
|
+
<pre>{note}</pre>
|
|
69
|
+
</div>
|
|
70
|
+
<FormItem noStyle name={'item' + i}>
|
|
71
|
+
<InputAutoFocus
|
|
72
|
+
type={type}
|
|
73
|
+
placeholder={note}
|
|
74
|
+
/>
|
|
75
|
+
</FormItem>
|
|
76
|
+
</FormItem>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
function renderConfirmBody () {
|
|
80
|
+
const instructions = opts.options.instructions || []
|
|
81
|
+
return (
|
|
82
|
+
<div>
|
|
83
|
+
{
|
|
84
|
+
instructions.map((note, index) => {
|
|
85
|
+
return <pre key={note + index}>{note}</pre>
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
<FormItem>
|
|
89
|
+
<Button
|
|
90
|
+
type='primary'
|
|
91
|
+
onClick={onConfirm}
|
|
92
|
+
>
|
|
93
|
+
{opts.options.submitText || e('submit')}
|
|
94
|
+
</Button>
|
|
95
|
+
<Button
|
|
96
|
+
className='mg1l'
|
|
97
|
+
onClick={onCancel}
|
|
98
|
+
>
|
|
99
|
+
{opts.options.cancelText || e('cancel')}
|
|
100
|
+
</Button>
|
|
101
|
+
</FormItem>
|
|
102
|
+
</div>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
const props = {
|
|
106
|
+
maskClosable: false,
|
|
107
|
+
okText: e('submit'),
|
|
108
|
+
onCancel,
|
|
109
|
+
onOk,
|
|
110
|
+
closable: false,
|
|
111
|
+
open: true,
|
|
112
|
+
title: opts.options?.name || '?',
|
|
113
|
+
footer: null
|
|
114
|
+
}
|
|
115
|
+
return (
|
|
116
|
+
<Modal
|
|
117
|
+
{...props}
|
|
118
|
+
>
|
|
119
|
+
{
|
|
120
|
+
opts.options?.mode === 'confirm'
|
|
121
|
+
? renderConfirmBody()
|
|
122
|
+
: (
|
|
123
|
+
<Form
|
|
124
|
+
form={form}
|
|
125
|
+
layout='vertical'
|
|
126
|
+
onFinish={onFinish}
|
|
127
|
+
>
|
|
128
|
+
{
|
|
129
|
+
opts.options.prompts.map(renderFormItem)
|
|
130
|
+
}
|
|
131
|
+
<FormItem>
|
|
132
|
+
<Button
|
|
133
|
+
type='primary'
|
|
134
|
+
htmlType='submit'
|
|
135
|
+
>
|
|
136
|
+
{e('submit')}
|
|
137
|
+
</Button>
|
|
138
|
+
<Button
|
|
139
|
+
type='dashed'
|
|
140
|
+
className='mg1l'
|
|
141
|
+
onClick={onIgnore}
|
|
142
|
+
>
|
|
143
|
+
{e('ignore')}
|
|
144
|
+
</Button>
|
|
145
|
+
<Button
|
|
146
|
+
className='mg1l'
|
|
147
|
+
onClick={onCancel}
|
|
148
|
+
>
|
|
149
|
+
{e('cancel')}
|
|
150
|
+
</Button>
|
|
151
|
+
</FormItem>
|
|
152
|
+
</Form>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
</Modal>
|
|
156
|
+
)
|
|
157
|
+
}
|