update host edit

pull/289/head
vapao 2020-12-18 19:54:35 +08:00
parent 591a4d1659
commit 8917b1fe76
8 changed files with 53 additions and 18 deletions

View File

@ -20,6 +20,7 @@ def fetch_children(data):
def merge_children(data, prefix, childes):
for item in childes:
name = f'{prefix}/{item["title"]}'
item['name'] = name
if item['children']:
merge_children(data, name, item['children'])
else:
@ -36,8 +37,9 @@ class GroupView(View):
grp = Group.objects.create(name='Default', sort_id=1)
data[grp.id] = grp.to_view()
merge_children(data2, '', data.values())
return json_response({'treeData': list(data.values()), 'groups': data2})
data = list(data.values())
merge_children(data2, '', data)
return json_response({'treeData': data, 'groups': data2})
def post(self, request):
form, error = JsonParser(

View File

@ -10,7 +10,6 @@ from libs.ssh import SSH
class Host(models.Model, ModelMixin):
name = models.CharField(max_length=50)
zone = models.CharField(max_length=50)
hostname = models.CharField(max_length=50)
port = models.IntegerField()
username = models.CharField(max_length=50)
@ -52,6 +51,7 @@ class Group(models.Model, ModelMixin):
def to_view(self):
return {
'key': self.id,
'value': self.id,
'title': self.name,
'children': []
}

View File

@ -33,7 +33,7 @@ class HostView(View):
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
Argument('zone', help='请输入主机类型'),
Argument('group_ids', type=list, filter=lambda x: len(x), help='请选择主机分组'),
Argument('name', help='请输主机名称'),
Argument('username', handler=str.strip, help='请输入登录用户名'),
Argument('hostname', handler=str.strip, help='请输入主机名或IP'),
@ -47,14 +47,16 @@ class HostView(View):
pkey=form.pkey) is False:
return json_response('auth fail')
if form.id:
Host.objects.filter(pk=form.pop('id')).update(**form)
elif Host.objects.filter(name=form.name, deleted_by_id__isnull=True).exists():
group_ids = form.pop('group_ids')
other = Host.objects.filter(name=form.name, deleted_by_id__isnull=True).first()
if other and (not form.id or other.id != form.id):
return json_response(error=f'已存在的主机名称【{form.name}')
if form.id:
Host.objects.filter(pk=form.id).update(**form)
host = Host.objects.get(pk=form.id)
else:
host = Host.objects.create(created_by=request.user, **form)
if request.user.role:
request.user.role.add_host_perm(host.id)
host.groups.set(group_ids)
return json_response(error=error)
def patch(self, request):

View File

@ -0,0 +1,17 @@
import React from 'react';
import { observer } from 'mobx-react';
import { Drawer} from 'antd';
import store from './store';
export default observer(function () {
return (
<Drawer
width={500}
title={store.record.name}
placement="right"
onClose={() => store.detailVisible = false}
visible={store.detailVisible}>
</Drawer>
)
})

View File

@ -6,7 +6,7 @@
import React, { useState, useEffect } from 'react';
import { observer } from 'mobx-react';
import { ExclamationCircleOutlined, UploadOutlined } from '@ant-design/icons';
import { Modal, Form, Input, Select, Button, Upload, message } from 'antd';
import { Modal, Form, Input, TreeSelect, Button, Upload, Alert, message } from 'antd';
import { http, X_TOKEN } from 'libs';
import store from './store';
@ -102,12 +102,13 @@ export default observer(function () {
confirmLoading={loading}
onOk={handleSubmit}>
<Form form={form} labelCol={{span: 6}} wrapperCol={{span: 14}} initialValues={info}>
<Form.Item required name="zone" label="主机类别">
<Select placeholder="请选择主机类别/区域/分组">
{store.zones.map(item => (
<Select.Option value={item} key={item}>{item}</Select.Option>
))}
</Select>
<Form.Item required name="group_ids" label="主机分组">
<TreeSelect
multiple
treeNodeLabelProp="name"
treeData={store.treeData}
showCheckedStrategy={TreeSelect.SHOW_CHILD}
placeholder="请选择分组"/>
</Form.Item>
<Form.Item required name="name" label="主机名称">
<Input placeholder="请输入主机名称"/>
@ -133,7 +134,7 @@ export default observer(function () {
<Input.TextArea placeholder="请输入主机备注信息"/>
</Form.Item>
<Form.Item wrapperCol={{span: 14, offset: 6}}>
<span role="img" aria-label="notice"> 首次验证时需要输入登录用户名对应的密码但不会存储该密码</span>
<Alert showIcon type="info" message="首次验证时需要输入登录用户名对应的密码,该密码会用于配置密钥连接,不会存储该密码。" />
</Form.Item>
</Form>
</Modal>

View File

@ -62,7 +62,11 @@ class ComTable extends React.Component {
showTotal: total => `${total}`,
pageSizeOptions: ['10', '20', '50', '100']
}}>
<Table.Column showSorterTooltip={false} title="主机名称" dataIndex="name" sorter={(a, b) => a.name.localeCompare(b.name)}/>
<Table.Column
showSorterTooltip={false}
title="主机名称"
render={info => <Action.Button onClick={() => store.showDetail(info)}>{info.name}</Action.Button>}
sorter={(a, b) => a.name.localeCompare(b.name)}/>
<Table.Column title="连接地址" dataIndex="hostname" sorter={(a, b) => a.name.localeCompare(b.name)}/>
<Table.Column hide width={100} title="端口" dataIndex="port"/>
<Table.Column hide ellipsis title="备注信息" dataIndex="desc"/>

View File

@ -11,6 +11,7 @@ import Group from './Group';
import ComTable from './Table';
import ComForm from './Form';
import ComImport from './Import';
import Detail from './Detail';
import store from './store';
export default observer(function () {
@ -30,6 +31,7 @@ export default observer(function () {
</Col>
</Row>
<Detail/>
{store.formVisible && <ComForm/>}
{store.importVisible && <ComImport/>}
</AuthDiv>

View File

@ -17,6 +17,7 @@ class Store {
@observable isFetching = false;
@observable formVisible = false;
@observable importVisible = false;
@observable detailVisible = false;
@observable f_name;
@observable f_host;
@ -59,6 +60,11 @@ class Store {
this.record = info
}
showDetail = (info) => {
this.record = info;
this.detailVisible = true;
}
_updateGroupCount = () => {
if (this.treeData.length && this.records.length) {
const counter = {};
@ -83,6 +89,7 @@ class Store {
host_ids = host_ids.concat(this._updateCount(counter, child))
}
item.host_ids = Array.from(new Set(host_ids));
if (item.key === this.group.key) this.group = item;
return item.host_ids
}
}