mirror of https://github.com/openspug/spug
U 优化模版管理
parent
981fa28505
commit
375ac435e2
|
@ -12,6 +12,7 @@ class ExecTemplate(models.Model, ModelMixin):
|
|||
type = models.CharField(max_length=50)
|
||||
body = models.TextField()
|
||||
interpreter = models.CharField(max_length=20)
|
||||
host_ids = models.TextField(default='[]')
|
||||
desc = models.CharField(max_length=255, null=True)
|
||||
|
||||
created_at = models.CharField(max_length=20, default=human_datetime)
|
||||
|
@ -22,6 +23,11 @@ class ExecTemplate(models.Model, ModelMixin):
|
|||
def __repr__(self):
|
||||
return '<ExecTemplate %r>' % self.name
|
||||
|
||||
def to_view(self):
|
||||
tmp = self.to_dict()
|
||||
tmp['host_ids'] = json.loads(self.host_ids)
|
||||
return tmp
|
||||
|
||||
class Meta:
|
||||
db_table = 'exec_templates'
|
||||
ordering = ('-id',)
|
||||
|
|
|
@ -18,7 +18,7 @@ class TemplateView(View):
|
|||
def get(self, request):
|
||||
templates = ExecTemplate.objects.all()
|
||||
types = [x['type'] for x in templates.order_by('type').values('type').distinct()]
|
||||
return json_response({'types': types, 'templates': [x.to_dict() for x in templates]})
|
||||
return json_response({'types': types, 'templates': [x.to_view() for x in templates]})
|
||||
|
||||
@auth('exec.template.add|exec.template.edit')
|
||||
def post(self, request):
|
||||
|
@ -28,6 +28,7 @@ class TemplateView(View):
|
|||
Argument('type', help='请选择模版类型'),
|
||||
Argument('body', help='请输入模版内容'),
|
||||
Argument('interpreter', default='sh'),
|
||||
Argument('host_ids', type=list, handler=json.dumps, default=[]),
|
||||
Argument('desc', required=False)
|
||||
).parse(request.body)
|
||||
if error is None:
|
||||
|
|
|
@ -32,28 +32,33 @@ class TemplateSelector extends React.Component {
|
|||
|
||||
handleSubmit = () => {
|
||||
if (this.state.selectedRows.length > 0) {
|
||||
const {body, interpreter} = this.state.selectedRows[0]
|
||||
this.props.onOk(body, interpreter)
|
||||
const {host_ids, body, interpreter} = this.state.selectedRows[0]
|
||||
this.props.onOk(host_ids, body, interpreter)
|
||||
}
|
||||
this.props.onCancel()
|
||||
};
|
||||
|
||||
columns = [{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
}, {
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true
|
||||
}, {
|
||||
title: '内容',
|
||||
dataIndex: 'body',
|
||||
ellipsis: true
|
||||
}, {
|
||||
title: '备注',
|
||||
dataIndex: 'desc',
|
||||
ellipsis: true
|
||||
}];
|
||||
columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true
|
||||
}, {
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
}, {
|
||||
title: '目标主机',
|
||||
dataIndex: 'host_ids',
|
||||
render: v => `${v.length}台`
|
||||
}, {
|
||||
title: '内容',
|
||||
dataIndex: 'body',
|
||||
ellipsis: true
|
||||
}, {
|
||||
title: '备注',
|
||||
dataIndex: 'desc',
|
||||
ellipsis: true
|
||||
}];
|
||||
|
||||
render() {
|
||||
const {selectedRows} = this.state;
|
||||
|
@ -84,7 +89,7 @@ class TemplateSelector extends React.Component {
|
|||
<Input allowClear value={store.f_name} onChange={e => store.f_name = e.target.value} placeholder="请输入"/>
|
||||
</SearchForm.Item>
|
||||
<SearchForm.Item span={8}>
|
||||
<Button type="primary" icon={<SyncOutlined />} onClick={store.fetchRecords}>刷新</Button>
|
||||
<Button type="primary" icon={<SyncOutlined/>} onClick={store.fetchRecords}>刷新</Button>
|
||||
</SearchForm.Item>
|
||||
</SearchForm>
|
||||
<Table
|
||||
|
|
|
@ -29,6 +29,15 @@ function TaskIndex() {
|
|||
}
|
||||
}, [loading])
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
store.host_ids = []
|
||||
if (store.showConsole) {
|
||||
store.switchConsole()
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
function handleSubmit() {
|
||||
setLoading(true)
|
||||
const host_ids = store.host_ids;
|
||||
|
@ -37,7 +46,8 @@ function TaskIndex() {
|
|||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
function handleTemplate(command, interpreter) {
|
||||
function handleTemplate(host_ids, command, interpreter) {
|
||||
if (host_ids.length > 0) store.host_ids = host_ids
|
||||
setInterpreter(interpreter)
|
||||
setCommand(command)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import { observer } from 'mobx-react';
|
|||
import { ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import { Modal, Form, Input, Select, Button, Radio, message } from 'antd';
|
||||
import { ACEditor } from 'components';
|
||||
import Selector from 'pages/host/Selector';
|
||||
import { http, cleanCommand } from 'libs';
|
||||
import store from './store';
|
||||
|
||||
|
@ -15,12 +16,14 @@ export default observer(function () {
|
|||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [body, setBody] = useState(store.record.body);
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
function handleSubmit() {
|
||||
setLoading(true);
|
||||
const formData = form.getFieldsValue();
|
||||
formData['id'] = store.record.id;
|
||||
formData['body'] = cleanCommand(body);
|
||||
formData['host_ids'] = store.record.host_ids;
|
||||
http.post('/api/exec/template/', formData)
|
||||
.then(res => {
|
||||
message.success('操作成功');
|
||||
|
@ -91,10 +94,19 @@ export default observer(function () {
|
|||
height="300px"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="目标主机">
|
||||
{info.host_ids.length > 0 && <span style={{marginRight: 16}}>已选择 {info.host_ids.length} 台</span>}
|
||||
<Button type="link" style={{padding: 0}} onClick={() => setVisible(true)}>选择主机</Button>
|
||||
</Form.Item>
|
||||
<Form.Item name="desc" label="备注信息">
|
||||
<Input.TextArea placeholder="请输入模板备注信息"/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<Selector
|
||||
visible={visible}
|
||||
selectedRowKeys={[...info.host_ids]}
|
||||
onCancel={() => setVisible(false)}
|
||||
onOk={(_, ids) => info.host_ids = ids}/>
|
||||
</Modal>
|
||||
)
|
||||
})
|
|
@ -69,7 +69,7 @@ class ComTable extends React.Component {
|
|||
<Table.Column title="操作" render={info => (
|
||||
<Action>
|
||||
<Action.Button auth="exec.template.edit" onClick={() => store.showForm(info)}>编辑</Action.Button>
|
||||
<Action.Button auth="exec.template.del" onClick={() => this.handleDelete(info)}>删除</Action.Button>
|
||||
<Action.Button danger auth="exec.template.del" onClick={() => this.handleDelete(info)}>删除</Action.Button>
|
||||
</Action>
|
||||
)}/>
|
||||
)}
|
||||
|
|
|
@ -26,7 +26,7 @@ class Store {
|
|||
.finally(() => this.isFetching = false)
|
||||
};
|
||||
|
||||
showForm = (info = {interpreter: 'sh'}) => {
|
||||
showForm = (info = {interpreter: 'sh', host_ids: []}) => {
|
||||
this.formVisible = true;
|
||||
this.record = info
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue