完成资产附加属性功能
This commit is contained in:
parent
25c3c6b929
commit
7599c09191
2
main.go
2
main.go
@ -22,7 +22,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Version = "v0.1.2"
|
const Version = "v0.2.0"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Fatal(Run())
|
log.Fatal(Run())
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/pkg/sftp"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -362,8 +363,16 @@ func SessionLsEndpoint(c echo.Context) error {
|
|||||||
return errors.New("获取sftp客户端失败")
|
return errors.New("获取sftp客户端失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tun.Subject.NextTerminal == nil {
|
||||||
|
nextTerminal, err := CreateNextTerminalBySession(session)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tun.Subject.NextTerminal = nextTerminal
|
||||||
|
}
|
||||||
|
|
||||||
if tun.Subject.NextTerminal.SftpClient == nil {
|
if tun.Subject.NextTerminal.SftpClient == nil {
|
||||||
sftpClient, err := CreateSftpClient(session)
|
sftpClient, err := sftp.NewClient(tun.Subject.NextTerminal.SshClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("创建sftp客户端失败:%v", err.Error())
|
logrus.Errorf("创建sftp客户端失败:%v", err.Error())
|
||||||
return err
|
return err
|
||||||
|
@ -4,9 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/pkg/sftp"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"next-terminal/pkg/global"
|
"next-terminal/pkg/global"
|
||||||
"next-terminal/pkg/guacd"
|
"next-terminal/pkg/guacd"
|
||||||
@ -97,6 +95,7 @@ func SSHEndpoint(c echo.Context) (err error) {
|
|||||||
|
|
||||||
tun := global.Tun{
|
tun := global.Tun{
|
||||||
Protocol: session.Protocol,
|
Protocol: session.Protocol,
|
||||||
|
Mode: session.Mode,
|
||||||
WebSocket: ws,
|
WebSocket: ws,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,18 +241,6 @@ func WriteMessage(ws *websocket.Conn, msg Message) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSshClientBySession(session model.Session) (sshClient *ssh.Client, err error) {
|
|
||||||
|
|
||||||
var (
|
|
||||||
username = session.Username
|
|
||||||
password = session.Password
|
|
||||||
privateKey = session.PrivateKey
|
|
||||||
passphrase = session.Passphrase
|
|
||||||
)
|
|
||||||
|
|
||||||
return term.NewSshClient(session.IP, session.Port, username, password, privateKey, passphrase)
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteByteMessage(ws *websocket.Conn, p []byte) {
|
func WriteByteMessage(ws *websocket.Conn, p []byte) {
|
||||||
err := ws.WriteMessage(websocket.TextMessage, p)
|
err := ws.WriteMessage(websocket.TextMessage, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -261,11 +248,14 @@ func WriteByteMessage(ws *websocket.Conn, p []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSftpClient(session model.Session) (sftpClient *sftp.Client, err error) {
|
func CreateNextTerminalBySession(session model.Session) (*term.NextTerminal, error) {
|
||||||
sshClient, err := CreateSshClientBySession(session)
|
var (
|
||||||
if err != nil {
|
username = session.Username
|
||||||
return nil, err
|
password = session.Password
|
||||||
}
|
privateKey = session.PrivateKey
|
||||||
|
passphrase = session.Passphrase
|
||||||
return sftp.NewClient(sshClient)
|
ip = session.IP
|
||||||
|
port = session.Port
|
||||||
|
)
|
||||||
|
return term.NewNextTerminal(ip, port, username, password, privateKey, passphrase, 10, 10, "")
|
||||||
}
|
}
|
||||||
|
@ -154,8 +154,9 @@ func TunEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
tun := global.Tun{
|
tun := global.Tun{
|
||||||
Protocol: session.Protocol,
|
Protocol: session.Protocol,
|
||||||
Tunnel: tunnel,
|
Mode: session.Mode,
|
||||||
WebSocket: ws,
|
WebSocket: ws,
|
||||||
|
Tunnel: tunnel,
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(session.ConnectionId) == 0 {
|
if len(session.ConnectionId) == 0 {
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
type Tun struct {
|
type Tun struct {
|
||||||
Protocol string
|
Protocol string
|
||||||
|
Mode string
|
||||||
WebSocket *websocket.Conn
|
WebSocket *websocket.Conn
|
||||||
Tunnel *guacd.Tunnel
|
Tunnel *guacd.Tunnel
|
||||||
NextTerminal *term.NextTerminal
|
NextTerminal *term.NextTerminal
|
||||||
@ -25,7 +26,7 @@ func (r *Tun) Close(code int, reason string) {
|
|||||||
|
|
||||||
ws := r.WebSocket
|
ws := r.WebSocket
|
||||||
if ws != nil {
|
if ws != nil {
|
||||||
if r.Protocol == "rdp" || r.Protocol == "vnc" {
|
if r.Mode == "guacd" {
|
||||||
err := guacd.NewInstruction("error", reason, strconv.Itoa(code))
|
err := guacd.NewInstruction("error", reason, strconv.Itoa(code))
|
||||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(err.String()))
|
_ = ws.WriteMessage(websocket.TextMessage, []byte(err.String()))
|
||||||
disconnect := guacd.NewInstruction("disconnect")
|
disconnect := guacd.NewInstruction("disconnect")
|
||||||
|
@ -92,6 +92,8 @@ func FindAssetAttrMapByAssetId(assetId string) (map[string]interface{}, error) {
|
|||||||
parameterNames = RDPParameterNames
|
parameterNames = RDPParameterNames
|
||||||
case "vnc":
|
case "vnc":
|
||||||
parameterNames = VNCParameterNames
|
parameterNames = VNCParameterNames
|
||||||
|
case "telnet":
|
||||||
|
parameterNames = TelnetParameterNames
|
||||||
}
|
}
|
||||||
propertiesMap := FindAllPropertiesMap()
|
propertiesMap := FindAllPropertiesMap()
|
||||||
var attributeMap = make(map[string]interface{})
|
var attributeMap = make(map[string]interface{})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "next-terminal",
|
"name": "next-terminal",
|
||||||
"version": "0.1.2",
|
"version": "0.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^4.3.0",
|
"@ant-design/icons": "^4.3.0",
|
||||||
|
@ -575,23 +575,45 @@ class Access extends Component {
|
|||||||
</Affix>
|
</Affix>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
|
|
||||||
<Draggable>
|
{
|
||||||
<Affix style={{position: 'absolute', top: 100, right: 100}}>
|
this.state.protocol === 'rdp' ?
|
||||||
<Button icon={<AppstoreTwoTone/>} onClick={() => {
|
<>
|
||||||
this.setState({
|
<Draggable>
|
||||||
fileSystemVisible: true,
|
<Affix style={{position: 'absolute', top: 100, right: 100}}>
|
||||||
});
|
<Button icon={<AppstoreTwoTone/>} onClick={() => {
|
||||||
}}/>
|
this.setState({
|
||||||
</Affix>
|
fileSystemVisible: true,
|
||||||
</Draggable>
|
});
|
||||||
|
}}/>
|
||||||
|
</Affix>
|
||||||
|
</Draggable>
|
||||||
|
|
||||||
|
<Draggable>
|
||||||
|
<Affix style={{position: 'absolute', top: 100, right: 150}}>
|
||||||
|
<Dropdown overlay={menu} trigger={['click']} placement="bottomLeft">
|
||||||
|
<Button icon={<DesktopOutlined/>}/>
|
||||||
|
</Dropdown>
|
||||||
|
</Affix>
|
||||||
|
</Draggable>
|
||||||
|
</> : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
this.state.protocol === 'ssh' ?
|
||||||
|
<>
|
||||||
|
<Draggable>
|
||||||
|
<Affix style={{position: 'absolute', top: 100, right: 100}}>
|
||||||
|
<Button icon={<AppstoreTwoTone/>} onClick={() => {
|
||||||
|
this.setState({
|
||||||
|
fileSystemVisible: true,
|
||||||
|
});
|
||||||
|
}}/>
|
||||||
|
</Affix>
|
||||||
|
</Draggable>
|
||||||
|
|
||||||
|
</> : undefined
|
||||||
|
}
|
||||||
|
|
||||||
<Draggable>
|
|
||||||
<Affix style={{position: 'absolute', top: 100, right: 150}}>
|
|
||||||
<Dropdown overlay={menu} trigger={['click']} placement="bottomLeft">
|
|
||||||
<Button icon={<DesktopOutlined/>}/>
|
|
||||||
</Dropdown>
|
|
||||||
</Affix>
|
|
||||||
</Draggable>
|
|
||||||
|
|
||||||
<Drawer
|
<Drawer
|
||||||
title={'会话详情'}
|
title={'会话详情'}
|
||||||
|
@ -285,128 +285,6 @@ Windows需要对远程应用程序的名称使用特殊的符号。
|
|||||||
<Input type='text' placeholder="remote app的命令行参数"/>
|
<Input type='text' placeholder="remote app的命令行参数"/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Panel>
|
</Panel>
|
||||||
<Panel header={<Text strong>性能</Text>} key="性能">
|
|
||||||
<Form.Item
|
|
||||||
{...RDPFormItemLayout}
|
|
||||||
name="enable-wallpaper"
|
|
||||||
label="启用桌面墙纸"
|
|
||||||
valuePropName="checked"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Switch checkedChildren="开启" unCheckedChildren="关闭"/>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
{...RDPFormItemLayout}
|
|
||||||
name="enable-theming"
|
|
||||||
label="启用桌面主题"
|
|
||||||
valuePropName="checked"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Switch checkedChildren="开启" unCheckedChildren="关闭"/>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
{...RDPFormItemLayout}
|
|
||||||
name="enable-font-smoothing"
|
|
||||||
label="启用字体平滑(ClearType)"
|
|
||||||
valuePropName="checked"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Switch checkedChildren="开启" unCheckedChildren="关闭"/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
{...RDPFormItemLayout}
|
|
||||||
name="enable-full-window-drag"
|
|
||||||
label="启用全窗口拖拽"
|
|
||||||
valuePropName="checked"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Switch checkedChildren="开启" unCheckedChildren="关闭"/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
{...RDPFormItemLayout}
|
|
||||||
name="enable-desktop-composition"
|
|
||||||
label="启用桌面合成效果(Aero)"
|
|
||||||
valuePropName="checked"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Switch checkedChildren="开启" unCheckedChildren="关闭"/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
{...RDPFormItemLayout}
|
|
||||||
name="enable-menu-animations"
|
|
||||||
label="启用菜单动画"
|
|
||||||
valuePropName="checked"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Switch checkedChildren="开启" unCheckedChildren="关闭"/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
{...RDPFormItemLayout}
|
|
||||||
name="disable-bitmap-caching"
|
|
||||||
label="禁用位图缓存"
|
|
||||||
valuePropName="checked"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Switch checkedChildren="开启" unCheckedChildren="关闭"/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
{...RDPFormItemLayout}
|
|
||||||
name="disable-offscreen-caching"
|
|
||||||
label="禁用离屏缓存"
|
|
||||||
valuePropName="checked"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Switch checkedChildren="开启" unCheckedChildren="关闭"/>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
{...RDPFormItemLayout}
|
|
||||||
name="disable-glyph-caching"
|
|
||||||
label="禁用字形缓存"
|
|
||||||
valuePropName="checked"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Switch checkedChildren="开启" unCheckedChildren="关闭"/>
|
|
||||||
</Form.Item>
|
|
||||||
</Panel>
|
|
||||||
</> : undefined
|
</> : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,14 +381,6 @@ Windows需要对远程应用程序的名称使用特殊的符号。
|
|||||||
<Option value="remote">远程</Option>
|
<Option value="remote">远程</Option>
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
name="swap-red-blue"
|
|
||||||
label="交换红蓝成分"
|
|
||||||
valuePropName="checked"
|
|
||||||
>
|
|
||||||
<Switch checkedChildren="开启" unCheckedChildren="关闭"/>
|
|
||||||
</Form.Item>
|
|
||||||
</Panel>
|
</Panel>
|
||||||
<Panel header={<Text strong>VNC中继</Text>} key="VNC中继">
|
<Panel header={<Text strong>VNC中继</Text>} key="VNC中继">
|
||||||
<Form.Item label={<Tooltip
|
<Form.Item label={<Tooltip
|
||||||
|
@ -41,7 +41,6 @@ class Setting extends Component {
|
|||||||
rdpSettingFormRef = React.createRef();
|
rdpSettingFormRef = React.createRef();
|
||||||
sshSettingFormRef = React.createRef();
|
sshSettingFormRef = React.createRef();
|
||||||
vncSettingFormRef = React.createRef();
|
vncSettingFormRef = React.createRef();
|
||||||
telnetSettingFormRef = React.createRef();
|
|
||||||
otherSettingFormRef = React.createRef();
|
otherSettingFormRef = React.createRef();
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -72,6 +71,9 @@ class Setting extends Component {
|
|||||||
if (!properties.hasOwnProperty(key)) {
|
if (!properties.hasOwnProperty(key)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (properties[key] === '-') {
|
||||||
|
properties[key] = '';
|
||||||
|
}
|
||||||
if (key.startsWith('enable') || key.startsWith("disable" || key === 'swap-red-blue')) {
|
if (key.startsWith('enable') || key.startsWith("disable" || key === 'swap-red-blue')) {
|
||||||
properties[key] = properties[key].bool();
|
properties[key] = properties[key].bool();
|
||||||
}
|
}
|
||||||
@ -93,10 +95,6 @@ class Setting extends Component {
|
|||||||
this.vncSettingFormRef.current.setFieldsValue(properties)
|
this.vncSettingFormRef.current.setFieldsValue(properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.telnetSettingFormRef.current) {
|
|
||||||
this.telnetSettingFormRef.current.setFieldsValue(properties)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.otherSettingFormRef.current) {
|
if (this.otherSettingFormRef.current) {
|
||||||
this.otherSettingFormRef.current.setFieldsValue(properties)
|
this.otherSettingFormRef.current.setFieldsValue(properties)
|
||||||
}
|
}
|
||||||
@ -317,11 +315,11 @@ class Setting extends Component {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane tab="SSH配置" key="ssh">
|
<TabPane tab="SSH/TELNET配置" key="ssh">
|
||||||
<Form ref={this.sshSettingFormRef} name="ssh" onFinish={this.changeProperties}
|
<Form ref={this.sshSettingFormRef} name="ssh" onFinish={this.changeProperties}
|
||||||
layout="vertical">
|
layout="vertical">
|
||||||
|
|
||||||
<Title level={3}>SSH配置</Title>
|
<Title level={3}>SSH/TELNET配置</Title>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
{...formItemLayout}
|
{...formItemLayout}
|
||||||
@ -474,97 +472,6 @@ class Setting extends Component {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane tab="TELNET配置" key="telnet">
|
|
||||||
<Form ref={this.telnetSettingFormRef} name="telnet" onFinish={this.changeProperties}
|
|
||||||
layout="vertical">
|
|
||||||
|
|
||||||
<Title level={3}>TELNET配置</Title>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
{...formItemLayout}
|
|
||||||
name="color-scheme"
|
|
||||||
label="配色方案"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '配色方案',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
initialValue="gray-black"
|
|
||||||
>
|
|
||||||
<Select style={{width: 120}} onChange={null}>
|
|
||||||
<Option value="gray-black">黑底灰字</Option>
|
|
||||||
<Option value="green-black">黑底绿字</Option>
|
|
||||||
<Option value="white-black">黑底白字</Option>
|
|
||||||
<Option value="black-white">白底黑字</Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
{...formItemLayout}
|
|
||||||
name="font-name"
|
|
||||||
label="字体名称"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '字体名称',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input type='text' placeholder="请输入字体名称"/>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
{...formItemLayout}
|
|
||||||
name="font-size"
|
|
||||||
label="字体大小"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '字体大小',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input type='number' placeholder="请输入字体大小"/>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
name="backspace"
|
|
||||||
label="退格键映射"
|
|
||||||
{...formItemLayout}
|
|
||||||
initialValue=""
|
|
||||||
>
|
|
||||||
<Select onChange={null}>
|
|
||||||
<Option value="">默认</Option>
|
|
||||||
<Option value="127">删除键(Ctrl-?)</Option>
|
|
||||||
<Option value="8">退格键(Ctrl-H)</Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
name="terminal-type"
|
|
||||||
label="终端类型"
|
|
||||||
{...formItemLayout}
|
|
||||||
initialValue=""
|
|
||||||
>
|
|
||||||
<Select onChange={null}>
|
|
||||||
<Option value="">默认</Option>
|
|
||||||
<Option value="ansi">ansi</Option>
|
|
||||||
<Option value="linux">linux</Option>
|
|
||||||
<Option value="vt100">vt100</Option>
|
|
||||||
<Option value="vt220">vt220</Option>
|
|
||||||
<Option value="xterm">xterm</Option>
|
|
||||||
<Option value="xterm-256color">xterm-256color</Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item {...formTailLayout}>
|
|
||||||
<Button type="primary" htmlType="submit">
|
|
||||||
更新
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</TabPane>
|
|
||||||
<TabPane tab="其他配置" key="other">
|
<TabPane tab="其他配置" key="other">
|
||||||
<Title level={3}>Guacd 服务配置</Title>
|
<Title level={3}>Guacd 服务配置</Title>
|
||||||
<Form ref={this.otherSettingFormRef} name="password" onFinish={this.changeProperties}
|
<Form ref={this.otherSettingFormRef} name="password" onFinish={this.changeProperties}
|
||||||
|
Loading…
Reference in New Issue
Block a user