@electerm/electerm-react 1.38.65 → 1.38.70
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/constants.js +3 -2
- package/client/common/create-title.jsx +9 -1
- package/client/common/sftp.js +3 -0
- package/client/components/batch-op/batch-op.jsx +1 -6
- package/client/components/bookmark-form/bookmark-form.styl +3 -1
- package/client/components/bookmark-form/render-ssh-tunnel.jsx +210 -88
- package/client/components/bookmark-form/ssh-form-ui.jsx +1 -1
- package/client/components/main/main.jsx +14 -0
- package/client/components/sftp/{confirm-modal.jsx → confirm-modal-store.jsx} +81 -50
- package/client/components/sftp/file-item.jsx +2 -0
- package/client/components/sftp/sftp-entry.jsx +27 -37
- package/client/components/sftp/transfer-conflict-store.jsx +291 -0
- package/client/components/sftp/transport-action-store.jsx +430 -0
- package/client/components/sftp/transports-action-store.jsx +102 -0
- package/client/components/sftp/transports-ui-store.jsx +30 -0
- package/client/components/sidebar/transfer-list-control.jsx +5 -14
- package/client/components/sidebar/transport-ui.jsx +2 -12
- package/client/components/tabs/tab.jsx +43 -2
- package/client/components/tabs/tabs.styl +1 -1
- package/client/components/terminal/index.jsx +1 -0
- package/client/components/terminal/terminal-interactive.jsx +15 -0
- package/client/components/terminal-info/disk.jsx +9 -0
- package/client/store/index.js +4 -0
- package/client/store/init-state.js +2 -3
- package/client/store/sync.js +5 -2
- package/client/store/tab.js +1 -1
- package/client/store/transfer-list.js +55 -2
- package/client/store/watch.js +0 -8
- package/package.json +1 -1
- package/client/components/sftp/transfer-conflict.jsx +0 -323
- package/client/components/sftp/transport-action.jsx +0 -412
- package/client/components/sftp/transport-entry.jsx +0 -108
- package/client/components/sftp/transport-types.js +0 -8
- package/client/components/sftp/transports-action.jsx +0 -111
- package/client/components/sftp/transports-ui.jsx +0 -93
|
@@ -38,7 +38,7 @@ export const contextMenuPaddingTop = 10
|
|
|
38
38
|
export const sftpControlHeight = 28 + 42 + 33 + 36
|
|
39
39
|
export const sidebarWidth = 43
|
|
40
40
|
export const maxHistory = 50
|
|
41
|
-
export const maxTransport =
|
|
41
|
+
export const maxTransport = 1
|
|
42
42
|
export const maxSftpHistory = 20
|
|
43
43
|
export const maxZoom = 8
|
|
44
44
|
export const minZoom = 0.5
|
|
@@ -278,7 +278,8 @@ export const commonActions = {
|
|
|
278
278
|
closeContextMenu: 'close-context-menu',
|
|
279
279
|
clickContextMenu: 'click-context-menu',
|
|
280
280
|
openContextMenu: 'open-context-menu',
|
|
281
|
-
addTransfer: 'add-transfer'
|
|
281
|
+
addTransfer: 'add-transfer',
|
|
282
|
+
sftpList: 'sftp-list'
|
|
282
283
|
}
|
|
283
284
|
|
|
284
285
|
export const srcsSkipUpgradeCheck = [
|
|
@@ -15,7 +15,7 @@ export default function createTitle (res) {
|
|
|
15
15
|
}
|
|
16
16
|
const {
|
|
17
17
|
host, port, username, title, type,
|
|
18
|
-
path, connectionHoppings
|
|
18
|
+
path, connectionHoppings, sshTunnels
|
|
19
19
|
} = res
|
|
20
20
|
const fixTitle = `${username || ''}@${host}:${port}`
|
|
21
21
|
const extra = host || path ? (path || fixTitle) : ''
|
|
@@ -25,6 +25,14 @@ export default function createTitle (res) {
|
|
|
25
25
|
if (connectionHoppings && connectionHoppings.length) {
|
|
26
26
|
f = `[⋙]${f}`
|
|
27
27
|
}
|
|
28
|
+
if (
|
|
29
|
+
sshTunnels &&
|
|
30
|
+
sshTunnels.length &&
|
|
31
|
+
sshTunnels[0].sshTunnel &&
|
|
32
|
+
sshTunnels[0].sshTunnelRemoteHost
|
|
33
|
+
) {
|
|
34
|
+
f = `[T]${f}`
|
|
35
|
+
}
|
|
28
36
|
if (type) {
|
|
29
37
|
f = `[${type}]${f}`
|
|
30
38
|
}
|
package/client/common/sftp.js
CHANGED
|
@@ -7,6 +7,7 @@ import Transfer from './transfer'
|
|
|
7
7
|
import { transferTypeMap, instSftpKeys as keys } from './constants'
|
|
8
8
|
import initWs from './ws'
|
|
9
9
|
|
|
10
|
+
window.sftps = {}
|
|
10
11
|
const transferKeys = Object.keys(transferTypeMap)
|
|
11
12
|
|
|
12
13
|
class Sftp {
|
|
@@ -56,6 +57,7 @@ class Sftp {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
async destroy () {
|
|
60
|
+
delete window.sftps[this.sessionId]
|
|
59
61
|
const { ws } = this
|
|
60
62
|
ws.s({
|
|
61
63
|
action: 'sftp-destroy',
|
|
@@ -69,5 +71,6 @@ class Sftp {
|
|
|
69
71
|
export default async (sessionId) => {
|
|
70
72
|
const sftp = new Sftp()
|
|
71
73
|
await sftp.init(sessionId)
|
|
74
|
+
window.sftps[sessionId] = sftp
|
|
72
75
|
return sftp
|
|
73
76
|
}
|
|
@@ -26,7 +26,6 @@ import { autoRun } from 'manate'
|
|
|
26
26
|
import { pick } from 'lodash-es'
|
|
27
27
|
import { runCmd } from '../terminal/terminal-apis'
|
|
28
28
|
import deepCopy from 'json-deep-copy'
|
|
29
|
-
import postMsg from '../../common/post-msg'
|
|
30
29
|
import uid from '../../common/uid'
|
|
31
30
|
import wait from '../../common/wait'
|
|
32
31
|
import { getFolderFromFilePath } from '../sftp/file-read'
|
|
@@ -186,11 +185,7 @@ export default class BatchOp extends Component {
|
|
|
186
185
|
zip: true,
|
|
187
186
|
skipConfirm: true
|
|
188
187
|
}
|
|
189
|
-
|
|
190
|
-
list: [obj],
|
|
191
|
-
action: commonActions.addTransfer,
|
|
192
|
-
sessionId: tab.sessionId
|
|
193
|
-
})
|
|
188
|
+
window.store.addTransferList([obj])
|
|
194
189
|
const { store } = window
|
|
195
190
|
this.tm = setTimeout(() => {
|
|
196
191
|
reject(new Error('timeout'))
|
|
@@ -5,30 +5,154 @@ import {
|
|
|
5
5
|
Radio,
|
|
6
6
|
Space,
|
|
7
7
|
Button,
|
|
8
|
-
Tooltip
|
|
8
|
+
Tooltip,
|
|
9
|
+
Table
|
|
9
10
|
} from 'antd'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
11
|
+
import { useState } from 'react'
|
|
12
|
+
import { PlusOutlined, QuestionCircleOutlined, MinusCircleFilled } from '@ant-design/icons'
|
|
13
|
+
import { formItemLayout, tailFormItemLayout } from '../../common/form-layout'
|
|
14
|
+
import uid from '../../common/uid'
|
|
12
15
|
|
|
13
16
|
const FormItem = Form.Item
|
|
14
|
-
const FormList = Form.List
|
|
15
17
|
const {
|
|
16
18
|
Button: RadioButton,
|
|
17
19
|
Group: RadioGroup
|
|
18
20
|
} = Radio
|
|
19
21
|
const { prefix } = window
|
|
20
22
|
const e = prefix('ssh')
|
|
23
|
+
const s = prefix('sftp')
|
|
24
|
+
const m = prefix('menu')
|
|
21
25
|
|
|
22
|
-
export default function
|
|
23
|
-
|
|
26
|
+
export default function renderSshTunnels (props) {
|
|
27
|
+
const {
|
|
28
|
+
form,
|
|
29
|
+
formData
|
|
30
|
+
} = props
|
|
31
|
+
const [formChild] = Form.useForm()
|
|
32
|
+
const [initialValues] = useState({
|
|
33
|
+
sshTunnelLocalPort: 12200,
|
|
34
|
+
sshTunnelLocalHost: '127.0.0.1',
|
|
35
|
+
sshTunnelRemotePort: 12300,
|
|
36
|
+
sshTunnelRemoteHost: '127.0.0.1'
|
|
37
|
+
})
|
|
38
|
+
const [list, setList] = useState(formData.sshTunnels || [])
|
|
39
|
+
function onSubmit () {
|
|
40
|
+
formChild.submit()
|
|
41
|
+
}
|
|
42
|
+
function handleFinish (data) {
|
|
43
|
+
const nd = {
|
|
44
|
+
...data,
|
|
45
|
+
id: uid()
|
|
46
|
+
}
|
|
47
|
+
const v = [
|
|
48
|
+
...form.getFieldValue('sshTunnels'),
|
|
49
|
+
nd
|
|
50
|
+
]
|
|
51
|
+
form.setFieldsValue({
|
|
52
|
+
sshTunnels: v
|
|
53
|
+
})
|
|
54
|
+
setList(old => {
|
|
55
|
+
return [
|
|
56
|
+
...old,
|
|
57
|
+
data
|
|
58
|
+
]
|
|
59
|
+
})
|
|
60
|
+
formChild.resetFields()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function remove (id) {
|
|
64
|
+
setList(old => {
|
|
65
|
+
return old.filter(i => i.id !== id)
|
|
66
|
+
})
|
|
67
|
+
const v = form.getFieldValue('sshTunnels').filter(i => i.id !== id)
|
|
68
|
+
form.setFieldsValue({
|
|
69
|
+
sshTunnels: v
|
|
70
|
+
})
|
|
71
|
+
formChild.resetFields()
|
|
72
|
+
}
|
|
73
|
+
const cols = [
|
|
74
|
+
{
|
|
75
|
+
title: 'NO.',
|
|
76
|
+
dataIndex: 'index',
|
|
77
|
+
key: 'index',
|
|
78
|
+
render: (k) => k
|
|
79
|
+
}, {
|
|
80
|
+
title: e('sshTunnel'),
|
|
81
|
+
key: 'sshTunnel',
|
|
82
|
+
render: (k, item) => {
|
|
83
|
+
// sshTunnel is forwardRemoteToLocal or forwardLocalToRemote
|
|
84
|
+
const {
|
|
85
|
+
sshTunnel,
|
|
86
|
+
sshTunnelRemoteHost = '127.0.0.1',
|
|
87
|
+
sshTunnelRemotePort,
|
|
88
|
+
sshTunnelLocalHost = '127.0.0.1',
|
|
89
|
+
sshTunnelLocalPort,
|
|
90
|
+
name
|
|
91
|
+
} = item
|
|
92
|
+
const to = sshTunnel === 'forwardRemoteToLocal'
|
|
93
|
+
? `${s('local')}:${sshTunnelLocalHost}:${sshTunnelLocalPort}`
|
|
94
|
+
: `${s('remote')}:${sshTunnelRemoteHost}:${sshTunnelRemotePort}`
|
|
95
|
+
const from = sshTunnel === 'forwardRemoteToLocal'
|
|
96
|
+
? `${s('remote')}:${sshTunnelRemoteHost}:${sshTunnelRemotePort}`
|
|
97
|
+
: `${s('local')}:${sshTunnelLocalHost}:${sshTunnelLocalPort}`
|
|
98
|
+
return (
|
|
99
|
+
<span>
|
|
100
|
+
{name ? `[${name}] ` : ''}→ {from} → {to}
|
|
101
|
+
</span>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
}, {
|
|
105
|
+
title: m('del'),
|
|
106
|
+
key: 'op',
|
|
107
|
+
dataIndex: 'id',
|
|
108
|
+
render: (id) => {
|
|
109
|
+
return (
|
|
110
|
+
<MinusCircleFilled
|
|
111
|
+
className='pointer'
|
|
112
|
+
onClick={() => remove(id)}
|
|
113
|
+
/>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
function renderList () {
|
|
24
120
|
return (
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
121
|
+
<FormItem {...tailFormItemLayout}>
|
|
122
|
+
<Table
|
|
123
|
+
columns={cols}
|
|
124
|
+
className='mg3b'
|
|
125
|
+
pagination={false}
|
|
126
|
+
size='small'
|
|
127
|
+
dataSource={list.map((d, i) => {
|
|
128
|
+
return {
|
|
129
|
+
...d,
|
|
130
|
+
index: i + 1
|
|
131
|
+
}
|
|
132
|
+
})}
|
|
133
|
+
/>
|
|
134
|
+
</FormItem>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<div>
|
|
140
|
+
<FormItem
|
|
141
|
+
name='sshTunnels'
|
|
142
|
+
className='hide'
|
|
143
|
+
>
|
|
144
|
+
<Input />
|
|
145
|
+
</FormItem>
|
|
146
|
+
<Form
|
|
147
|
+
form={formChild}
|
|
148
|
+
onFinish={handleFinish}
|
|
149
|
+
initialValues={initialValues}
|
|
28
150
|
>
|
|
151
|
+
{renderList()}
|
|
29
152
|
<FormItem
|
|
30
153
|
label={e('sshTunnel')}
|
|
31
|
-
name=
|
|
154
|
+
name='sshTunnel'
|
|
155
|
+
{...formItemLayout}
|
|
32
156
|
required
|
|
33
157
|
>
|
|
34
158
|
<RadioGroup>
|
|
@@ -48,87 +172,85 @@ export default function renderSshTunnel () {
|
|
|
48
172
|
</RadioButton>
|
|
49
173
|
</RadioGroup>
|
|
50
174
|
</FormItem>
|
|
51
|
-
<Space.Compact className='mg2x'>
|
|
52
|
-
<FormItem
|
|
53
|
-
label={e('destination')}
|
|
54
|
-
name={[field.name, 'sshTunnelRemoteHost']}
|
|
55
|
-
initialValue='127.0.0.1'
|
|
56
|
-
required
|
|
57
|
-
>
|
|
58
|
-
<Input
|
|
59
|
-
className='compact-input'
|
|
60
|
-
placeholder={e('host')}
|
|
61
|
-
/>
|
|
62
|
-
</FormItem>
|
|
63
|
-
<FormItem
|
|
64
|
-
label=''
|
|
65
|
-
name={[field.name, 'sshTunnelRemotePort']}
|
|
66
|
-
initialValue={22}
|
|
67
|
-
required
|
|
68
|
-
>
|
|
69
|
-
<InputNumber
|
|
70
|
-
min={1}
|
|
71
|
-
max={65535}
|
|
72
|
-
// addonBefore={e('remotePort')}
|
|
73
|
-
className='compact-input'
|
|
74
|
-
placeholder={e('port')}
|
|
75
|
-
/>
|
|
76
|
-
</FormItem>
|
|
77
|
-
</Space.Compact>
|
|
78
175
|
<FormItem
|
|
79
|
-
label={
|
|
80
|
-
|
|
81
|
-
initialValue={22}
|
|
176
|
+
label={s('remote')}
|
|
177
|
+
{...formItemLayout}
|
|
82
178
|
required
|
|
83
|
-
className='
|
|
179
|
+
className='ssh-tunnels-host'
|
|
84
180
|
>
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
181
|
+
<Space.Compact>
|
|
182
|
+
<FormItem
|
|
183
|
+
name='sshTunnelRemoteHost'
|
|
184
|
+
label=''
|
|
185
|
+
required
|
|
186
|
+
>
|
|
187
|
+
<Input
|
|
188
|
+
placeholder={e('host')}
|
|
189
|
+
/>
|
|
190
|
+
</FormItem>
|
|
191
|
+
<FormItem
|
|
192
|
+
label=''
|
|
193
|
+
name='sshTunnelRemotePort'
|
|
194
|
+
required
|
|
195
|
+
>
|
|
196
|
+
<InputNumber
|
|
197
|
+
min={1}
|
|
198
|
+
max={65535}
|
|
199
|
+
placeholder={e('port')}
|
|
200
|
+
/>
|
|
201
|
+
</FormItem>
|
|
202
|
+
</Space.Compact>
|
|
203
|
+
</FormItem>
|
|
204
|
+
<FormItem
|
|
205
|
+
label={s('local')}
|
|
206
|
+
{...formItemLayout}
|
|
207
|
+
required
|
|
208
|
+
className='ssh-tunnels-host'
|
|
209
|
+
>
|
|
210
|
+
<Space.Compact>
|
|
211
|
+
<FormItem
|
|
212
|
+
name='sshTunnelLocalHost'
|
|
213
|
+
label=''
|
|
214
|
+
required
|
|
215
|
+
>
|
|
216
|
+
<Input
|
|
217
|
+
placeholder={e('host')}
|
|
218
|
+
/>
|
|
219
|
+
</FormItem>
|
|
220
|
+
<FormItem
|
|
221
|
+
label=''
|
|
222
|
+
name='sshTunnelLocalPort'
|
|
223
|
+
required
|
|
224
|
+
>
|
|
225
|
+
<InputNumber
|
|
226
|
+
min={1}
|
|
227
|
+
max={65535}
|
|
228
|
+
// addonBefore={e('localPort')}
|
|
229
|
+
placeholder={e('port')}
|
|
230
|
+
/>
|
|
231
|
+
</FormItem>
|
|
232
|
+
</Space.Compact>
|
|
233
|
+
</FormItem>
|
|
234
|
+
<FormItem
|
|
235
|
+
name='name'
|
|
236
|
+
label={s('name')}
|
|
237
|
+
{...formItemLayout}
|
|
238
|
+
>
|
|
239
|
+
<Input
|
|
240
|
+
placeholder={e('name')}
|
|
91
241
|
/>
|
|
92
242
|
</FormItem>
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
name='sshTunnels'
|
|
107
|
-
key='sshTunnels'
|
|
108
|
-
>
|
|
109
|
-
{
|
|
110
|
-
(fields, { add, remove }, { errors }) => {
|
|
111
|
-
return (
|
|
112
|
-
<div>
|
|
113
|
-
{
|
|
114
|
-
fields.map((field, i) => {
|
|
115
|
-
return renderItem(field, i, add, remove)
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
<FormItem>
|
|
119
|
-
<Button
|
|
120
|
-
type='dashed'
|
|
121
|
-
onClick={() => add()}
|
|
122
|
-
block
|
|
123
|
-
icon={<PlusOutlined />}
|
|
124
|
-
>
|
|
125
|
-
{e('sshTunnel')}
|
|
126
|
-
</Button>
|
|
127
|
-
</FormItem>
|
|
128
|
-
</div>
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
</FormList>
|
|
133
|
-
]
|
|
243
|
+
<FormItem {...tailFormItemLayout} className='mg60b'>
|
|
244
|
+
<Button
|
|
245
|
+
type='default'
|
|
246
|
+
htmlType='button'
|
|
247
|
+
icon={<PlusOutlined />}
|
|
248
|
+
onClick={onSubmit}
|
|
249
|
+
>
|
|
250
|
+
{e('sshTunnel')}
|
|
251
|
+
</Button>
|
|
252
|
+
</FormItem>
|
|
253
|
+
</Form>
|
|
254
|
+
</div>
|
|
255
|
+
)
|
|
134
256
|
}
|
|
@@ -12,6 +12,9 @@ import CssOverwrite from './css-overwrite'
|
|
|
12
12
|
import UiTheme from './ui-theme'
|
|
13
13
|
import CustomCss from './custom-css.jsx'
|
|
14
14
|
import TerminalInteractive from '../terminal/terminal-interactive'
|
|
15
|
+
import ConfirmModalStore from '../sftp/confirm-modal-store.jsx'
|
|
16
|
+
import TransferConflictStore from '../sftp/transfer-conflict-store.jsx'
|
|
17
|
+
import TransportsActionStore from '../sftp/transports-action-store.jsx'
|
|
15
18
|
import classnames from 'classnames'
|
|
16
19
|
import ShortcutControl from '../shortcuts/shortcut-control.jsx'
|
|
17
20
|
import { isMac, isWin } from '../../common/constants'
|
|
@@ -149,6 +152,17 @@ export default class Index extends Component {
|
|
|
149
152
|
/>
|
|
150
153
|
</div>
|
|
151
154
|
<ContextMenu store={store} />
|
|
155
|
+
<ConfirmModalStore
|
|
156
|
+
store={store}
|
|
157
|
+
/>
|
|
158
|
+
<TransferConflictStore
|
|
159
|
+
store={store}
|
|
160
|
+
_fileTransfers={store._fileTransfers}
|
|
161
|
+
/>
|
|
162
|
+
<TransportsActionStore
|
|
163
|
+
store={store}
|
|
164
|
+
_fileTransfers={store._fileTransfers}
|
|
165
|
+
/>
|
|
152
166
|
</div>
|
|
153
167
|
</ConfigProvider>
|
|
154
168
|
)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { Component } from '../common/react-subx'
|
|
6
7
|
import { Modal, Button } from 'antd'
|
|
7
8
|
import { isString } from 'lodash-es'
|
|
8
9
|
import AnimateText from '../common/animate-text'
|
|
@@ -23,42 +24,51 @@ function formatTimeAuto (strOrDigit) {
|
|
|
23
24
|
return formatTime(strOrDigit * 1000)
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
export default
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
modifyTime: modifyTimeTo,
|
|
43
|
-
size: sizeTo,
|
|
44
|
-
type: typeTo
|
|
45
|
-
},
|
|
46
|
-
id,
|
|
47
|
-
transferGroupId
|
|
48
|
-
} = props.transferToConfirm
|
|
49
|
-
function act (action) {
|
|
50
|
-
props.modifier({
|
|
51
|
-
transferToConfirm: null
|
|
52
|
-
})
|
|
27
|
+
export default class ConfirmModalStore extends Component {
|
|
28
|
+
act (action) {
|
|
29
|
+
const { store } = this.props
|
|
30
|
+
const {
|
|
31
|
+
transferToConfirm
|
|
32
|
+
} = store
|
|
33
|
+
store.setState(
|
|
34
|
+
'transferToConfirm', {}
|
|
35
|
+
)
|
|
36
|
+
const {
|
|
37
|
+
fromFile: {
|
|
38
|
+
id: fileId
|
|
39
|
+
},
|
|
40
|
+
id,
|
|
41
|
+
transferGroupId
|
|
42
|
+
} = transferToConfirm
|
|
53
43
|
postMessage({
|
|
54
44
|
transferGroupId,
|
|
55
45
|
fileId,
|
|
56
46
|
id,
|
|
57
|
-
transfer:
|
|
47
|
+
transfer: transferToConfirm,
|
|
58
48
|
action
|
|
59
49
|
})
|
|
60
50
|
}
|
|
61
|
-
|
|
51
|
+
|
|
52
|
+
renderContent () {
|
|
53
|
+
const {
|
|
54
|
+
transferToConfirm
|
|
55
|
+
} = this.props.store
|
|
56
|
+
const {
|
|
57
|
+
fromPath,
|
|
58
|
+
toPath,
|
|
59
|
+
fromFile: {
|
|
60
|
+
isDirectory,
|
|
61
|
+
name,
|
|
62
|
+
modifyTime: modifyTimeFrom,
|
|
63
|
+
size: sizeFrom,
|
|
64
|
+
type: typeFrom
|
|
65
|
+
},
|
|
66
|
+
toFile: {
|
|
67
|
+
modifyTime: modifyTimeTo,
|
|
68
|
+
size: sizeTo,
|
|
69
|
+
type: typeTo
|
|
70
|
+
}
|
|
71
|
+
} = transferToConfirm
|
|
62
72
|
const action = isDirectory ? e('merge') : e('replace')
|
|
63
73
|
const typeTxt = isDirectory ? e('folder') : e('file')
|
|
64
74
|
const Icon = isDirectory ? FolderOutlined : FileOutlined
|
|
@@ -95,27 +105,39 @@ export default (props) => {
|
|
|
95
105
|
</div>
|
|
96
106
|
)
|
|
97
107
|
}
|
|
98
|
-
|
|
108
|
+
|
|
109
|
+
renderFooter () {
|
|
110
|
+
const {
|
|
111
|
+
transferToConfirm
|
|
112
|
+
} = this.props.store
|
|
113
|
+
if (!transferToConfirm) {
|
|
114
|
+
return null
|
|
115
|
+
}
|
|
116
|
+
const {
|
|
117
|
+
fromFile: {
|
|
118
|
+
isDirectory
|
|
119
|
+
}
|
|
120
|
+
} = transferToConfirm
|
|
99
121
|
return (
|
|
100
122
|
<div className='mgq1t pd1y alignright'>
|
|
101
123
|
<Button
|
|
102
124
|
type='dashed'
|
|
103
125
|
className='mg1l'
|
|
104
|
-
onClick={() => act(fileActions.cancel)}
|
|
126
|
+
onClick={() => this.act(fileActions.cancel)}
|
|
105
127
|
>
|
|
106
128
|
{e('cancel')}
|
|
107
129
|
</Button>
|
|
108
130
|
<Button
|
|
109
131
|
type='dashed'
|
|
110
132
|
className='mg1l'
|
|
111
|
-
onClick={() => act(fileActions.skip)}
|
|
133
|
+
onClick={() => this.act(fileActions.skip)}
|
|
112
134
|
>
|
|
113
135
|
{e('skip')}
|
|
114
136
|
</Button>
|
|
115
137
|
<Button
|
|
116
138
|
type='dashed'
|
|
117
139
|
className='mg1l'
|
|
118
|
-
onClick={() => act(fileActions.skipAll)}
|
|
140
|
+
onClick={() => this.act(fileActions.skipAll)}
|
|
119
141
|
>
|
|
120
142
|
{e('skipAll')}
|
|
121
143
|
</Button>
|
|
@@ -123,7 +145,7 @@ export default (props) => {
|
|
|
123
145
|
danger
|
|
124
146
|
className='mg1l'
|
|
125
147
|
onClick={
|
|
126
|
-
() => act(fileActions.mergeOrOverwrite)
|
|
148
|
+
() => this.act(fileActions.mergeOrOverwrite)
|
|
127
149
|
}
|
|
128
150
|
>
|
|
129
151
|
{isDirectory ? e('merge') : e('overwrite')}
|
|
@@ -132,7 +154,7 @@ export default (props) => {
|
|
|
132
154
|
type='primary'
|
|
133
155
|
className='mg1l'
|
|
134
156
|
onClick={
|
|
135
|
-
() => act(fileActions.rename)
|
|
157
|
+
() => this.act(fileActions.rename)
|
|
136
158
|
}
|
|
137
159
|
>
|
|
138
160
|
{e('rename')}
|
|
@@ -148,7 +170,7 @@ export default (props) => {
|
|
|
148
170
|
: e('overwriteDesc')
|
|
149
171
|
}
|
|
150
172
|
onClick={
|
|
151
|
-
() => act(fileActions.mergeOrOverwriteAll)
|
|
173
|
+
() => this.act(fileActions.mergeOrOverwriteAll)
|
|
152
174
|
}
|
|
153
175
|
>
|
|
154
176
|
{isDirectory ? e('mergeAll') : e('overwriteAll')}
|
|
@@ -158,7 +180,7 @@ export default (props) => {
|
|
|
158
180
|
className='mg1l'
|
|
159
181
|
title={e('renameDesc')}
|
|
160
182
|
onClick={
|
|
161
|
-
() => act(fileActions.renameAll)
|
|
183
|
+
() => this.act(fileActions.renameAll)
|
|
162
184
|
}
|
|
163
185
|
>
|
|
164
186
|
{e('renameAll')}
|
|
@@ -167,18 +189,27 @@ export default (props) => {
|
|
|
167
189
|
</div>
|
|
168
190
|
)
|
|
169
191
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
192
|
+
|
|
193
|
+
render () {
|
|
194
|
+
const {
|
|
195
|
+
transferToConfirm
|
|
196
|
+
} = this.props.store
|
|
197
|
+
if (!transferToConfirm.id) {
|
|
198
|
+
return null
|
|
199
|
+
}
|
|
200
|
+
const modalProps = {
|
|
201
|
+
open: true,
|
|
202
|
+
width: 500,
|
|
203
|
+
title: e('fileConflict'),
|
|
204
|
+
footer: this.renderFooter(),
|
|
205
|
+
onCancel: () => this.act(fileActions.cancel)
|
|
206
|
+
}
|
|
207
|
+
return (
|
|
208
|
+
<Modal
|
|
209
|
+
{...modalProps}
|
|
210
|
+
>
|
|
211
|
+
{this.renderContent()}
|
|
212
|
+
</Modal>
|
|
213
|
+
)
|
|
176
214
|
}
|
|
177
|
-
return (
|
|
178
|
-
<Modal
|
|
179
|
-
{...modalProps}
|
|
180
|
-
>
|
|
181
|
-
{renderContent()}
|
|
182
|
-
</Modal>
|
|
183
|
-
)
|
|
184
215
|
}
|
|
@@ -168,6 +168,7 @@ export default class FileSection extends React.Component {
|
|
|
168
168
|
fromPath,
|
|
169
169
|
toPath,
|
|
170
170
|
id: generate(),
|
|
171
|
+
host: this.props.tab?.host,
|
|
171
172
|
...createTransferProps(this.props),
|
|
172
173
|
operation
|
|
173
174
|
})
|
|
@@ -804,6 +805,7 @@ export default class FileSection extends React.Component {
|
|
|
804
805
|
}
|
|
805
806
|
toPath = resolve(toPath, name)
|
|
806
807
|
const obj = {
|
|
808
|
+
host: this.props.tab?.host,
|
|
807
809
|
typeFrom: type,
|
|
808
810
|
typeTo,
|
|
809
811
|
fromPath: resolve(path, name),
|