cmdb增加一个功能界面(机房和机型管理)

时间:July 19, 2019 分类:

目录:

介绍

以增删查改主机资产进行展示

后端代码

django增加app

python manage.py startapp inventory

修改setting

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'account',
    'inventory',
]

将inventory的url指向inventory

path('inventory/', include('inventory.urls')),

后端代码

涉及到机房,机型和主机

models

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models

# Create your models here.


class Tags(models.Model):
    # 标签
    tag = models.CharField(max_length=100, unique=True, null=False)


class Idc(models.Model):
    # 机房
    name = models.CharField(max_length=50)
    # 代码
    code = models.CharField(max_length=20, unique=True)
    # 地址
    location = models.CharField(max_length=150)

    class Meta:
        permissions = (
            ("view_idc", ("查看机房")),
            ("add_idc", ("添加机房")),
            ("change_idc", ("编辑机房")),
            ("delete_idc", ("删除机房")),
        )
        default_permissions = ()

    def __str__(self):
        return '%s(%s)' % (self.name, self.code)


class Specification(models.Model):
    # 机房
    idc = models.ForeignKey(Idc, on_delete=models.PROTECT)
    # 机型
    name = models.CharField(max_length=100)
    # cpu单位核数
    cpu = models.IntegerField()
    # 内存单位G
    mem = models.IntegerField()

    class Meta:
        unique_together = (("idc", "name"),)
        permissions = (
            ("view_specification", ("查看类型")),
            ("add_specification", ("添加类型")),
            ("change_specification", ("编辑类型")),
            ("delete_specification", ("删除类型")),
        )
        default_permissions = ()

    def __str__(self):
        return '%s %s' % (str(self.idc), self.name)


class Storage(models.Model):
    media_choices = (
        (0, 'SSD'),
        (1, 'HDD'),
    )
    # n对1 主机
    machine = models.ForeignKey('Machine', on_delete=models.CASCADE)
    # 介质
    media = models.IntegerField(choices=media_choices)
    # 大小
    volume = models.IntegerField()

    class Meta:
        permissions = ()
        default_permissions = ()

    def __str__(self):
        return '%s : %d GB' % (self.get_media_display(), self.volume)


class Machine(models.Model):
    # 主机名
    hostname = models.CharField(max_length=50)
    # 主机型号
    specification = models.ForeignKey('Specification', null=True, on_delete=models.PROTECT)
    # ip
    ip = models.GenericIPAddressField(null=False)
    # 主机标签
    tags = models.ManyToManyField(Tags)
    # 创建时间
    created = models.DateTimeField(auto_now_add=True)
    # 修改时间
    last_modified = models.DateTimeField(auto_now=True)

    class Meta:
        permissions = (
            ("view_machine", ("查看服务器")),
            ("add_machine", ("添加服务器")),
            ("change_machine", ("编辑服务器")),
            ("delete_machine", ("删除服务器")),
            ("connect_machine", ("连接服务器")),
        )
        default_permissions = ()

url

from django.urls import path
from inventory import views

urlpatterns = [
    # 根据条件查找主机
    path('machineFilterOptions/', views.machine_filter_options),
    # 添加主机
    path('machineAdd/', views.machine_add),
    # 修改主机 machineAdd和machineChange都是调用了machine_edit用于编辑和创建主机
    path('machineChange/<int:id>/', views.machine_change),
    path('getMachines/', views.get_machines),
    # 添加主机 get方法用于获取需要添加主机的其他信息
    path('addMachine/', views.add_machine),
    # 修改主机 get方法用于获取需要修改主机的信息
    path('changeMachine/', views.change_machine),
    path('deleteMachine/', views.delete_machine),
    path('getIdcs/', views.get_idcs),
    path('addIdc/', views.add_idc),
    path('changeIdc/', views.change_idc),
    path('deleteIdc/', views.delete_idc),
    path('getSpecifications/', views.get_specifications),
    path('addSpecification/', views.add_specification),
    path('changeSpecification/', views.change_specification),
    path('deleteSpecification/', views.delete_specification),
]

view

# -*- coding: utf-8 -*-

from django.contrib.auth.decorators import login_required, permission_required

from inventory import machine, idc, specification

@login_required
@permission_required('inventory.view_machine', raise_exception=True)
def machine_filter_options(request):
    return machine.machine_filter_options(request)

@login_required
@permission_required('inventory.add_machine', raise_exception=True)
def machine_add(request):
    return machine.machine_edit(request, None)

@login_required
@permission_required('inventory.change_machine', raise_exception=True)
def machine_change(request, id):
    return machine.machine_edit(request, id)

@login_required
@permission_required('inventory.view_machine', raise_exception=True)
def get_machines(request):
    return machine.get_machines(request)

@login_required
@permission_required('inventory.add_machine', raise_exception=True)
def add_machine(request):
    return machine.add_machine(request)

@login_required
@permission_required('inventory.change_machine', raise_exception=True)
def change_machine(request):
    return machine.change_machine(request)

@login_required
@permission_required('inventory.delete_machine', raise_exception=True)
def delete_machine(request):
    return machine.delete_machine(request)

@login_required
@permission_required('inventory.view_idc', raise_exception=True)
def get_idcs(request):
    return idc.get_idcs(request)

@login_required
@permission_required('inventory.add_idc', raise_exception=True)
def add_idc(request):
    return idc.add_idc(request)

@login_required
@permission_required('inventory.change_idc', raise_exception=True)
def change_idc(request):
    return idc.change_idc(request)

@login_required
@permission_required('inventory.delete_idc', raise_exception=True)
def delete_idc(request):
    return idc.delete_idc(request)

@login_required
@permission_required('inventory.view_specification', raise_exception=True)
def get_specifications(request):
    return specification.get_specifications(request)

@login_required
@permission_required('inventory.add_specification', raise_exception=True)
def add_specification(request):
    return specification.add_specification(request)

@login_required
@permission_required('inventory.change_specification', raise_exception=True)
def change_specification(request):
    return specification.change_specification(request)

@login_required
@permission_required('inventory.delete_specification', raise_exception=True)
def delete_specification(request):
    return specification.delete_specification(request)

json序列化时间

python的json.dumps()序列化默认只支持以下类型

python Json
dict object
list, tuple array
str string
int, float number
True true
False false
None null

如果需要支持其他的类型

from datetime import datetime, date
import json

# 用于json格式化data和datatime类型
class DateEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        if isinstance(obj, date):
            return obj.strftime('%Y-%m-%d')
        return json.JSONEncoder.default(self, obj)

在json.dumps()的时候加上cls=DateEncoder的参数即可

idc

from django.http import HttpResponse, HttpResponseBadRequest
from django.db import transaction
from django.db.models import Q

from inventory.models import Idc

import json

def get_idcs(request):
    search = request.GET.get('search')
    order_by = request.GET.get('orderBy')
    order = request.GET.get('order')
    size = int(request.GET.get('size'))
    page = int(request.GET.get('page'))
    idcs = Idc.objects
    if search:
        idcs = idcs.filter(Q(name__icontains=search) | Q(code__icontains=search) | Q(location__icontains=search))
    if order_by and order:
        if order == 'ascending':
            idcs = idcs.order_by(order_by)
        if order == 'descending':
            idcs = idcs.order_by('-' + order_by)
    else:
        idcs = idcs.order_by('id')
    start = (page - 1) * size
    end = size * page
    total = idcs.count()
    idcs = idcs[start : end].values('id', 'name', 'code', 'location')
    return HttpResponse(json.dumps({'tableData': list(idcs), 'total': total}))

def add_idc(request):
    idc_data = json.loads(request.body.decode())
    name = idc_data.get('name')
    code = idc_data.get('code')
    location = idc_data.get('location')
    idc, status = Idc.objects.get_or_create(code=code, defaults={
        'name': name, 'location': location})
    if status:
        return HttpResponse(status=201)
    else:
        return HttpResponseBadRequest()

def change_idc(request):
    idc_data = json.loads(request.body.decode())
    id = idc_data.get('id')
    name = idc_data.get('name')
    code = idc_data.get('code')
    location = idc_data.get('location')
    count = Idc.objects.filter(id=id).update(name=name, code=code, location=location)
    if count > 0:
        return HttpResponse(status=202)
    else:
        return HttpResponseBadRequest()

@transaction.atomic
def delete_idc(request):
    id_list = json.loads(request.body.decode()).get('idList')
    print(id_list)
    result = Idc.objects.filter(id__in=id_list).delete()
    if result[0] > 0:
        return HttpResponse(status=204)
    else:
        return HttpResponseBadRequest()

specification

from django.http import HttpResponse, HttpResponseBadRequest
from django.db import transaction
from django.db.models import F
from django.db.models import Q
from inventory.models import Idc, Specification

import json


def get_specifications(request):
    search = request.GET.get('search')
    order_by = request.GET.get('orderBy')
    order = request.GET.get('order')
    size = int(request.GET.get('size'))
    page = int(request.GET.get('page'))
    specifications = Specification.objects.annotate(idcId=F('idc__id'), idcName=F('idc__name'), idcCode=F('idc__code'))
    if search:
        specifications = specifications.filter( Q(idc__name__icontains=search) | Q(idc__code__icontains=search))
    if order_by and order:
        if order == 'ascending':
            specifications = specifications.order_by(order_by)
        if order == 'descending':
            specifications = specifications.order_by('-' + order_by)
    else:
        specifications = specifications.order_by('id')
    start = (page - 1) * size
    end = size * page
    total = specifications.count()
    specifications = specifications[start : end].values('id', 'idcId', 'idcName', 'idcCode', 'name',  'cpu',  'mem')
    idc_options = list(Idc.objects.all().values('id', 'name', 'code'))
    return HttpResponse(json.dumps({'idcOptions': idc_options,'tableData': list(specifications),
        'total': total}))

def add_specification(request):
    specification_data = json.loads(request.body.decode())
    name = specification_data.get('name')
    cpu = specification_data.get('cpu')
    mem = specification_data.get('mem')
    idc_id = specification_data.get('idc')
    specification, status = Specification.objects.get_or_create(idc=Idc.objects.get(id=idc_id), name=name, cpu=cpu, mem=mem)
    if status:
        return HttpResponse(status=201)
    else:
        return HttpResponseBadRequest()

def change_specification(request):
    specification_data = json.loads(request.body.decode())
    id = specification_data.get('id')
    idc = specification_data.get('idc')
    name = specification_data.get('name')
    cpu = specification_data.get('cpu')
    mem = specification_data.get('mem')
    count = Specification.objects.filter(id=id).update(idc=idc, name=name, cpu=cpu, mem=mem)
    if count > 0:
        return HttpResponse(status=202)
    else:
        return HttpResponseBadRequest()

@transaction.atomic
def delete_specification(request):
    id_list = json.loads(request.body.decode()).get('idList')
    result = Specification.objects.filter(id__in=id_list).delete()
    if result[0] > 0:
        return HttpResponse(status=204)
    else:
        return HttpResponseBadRequest()

同步数据库

python manage.py makemigrations
python manage.py migrate

前端

添加路由

src/store.js的path增加页面和权限对应关系

在main中会判断这些组件是否存在对应的权限,在请求url或者在显示的时候也会进行判断

const state = {
...
  paths: {
    ...,
    'MachineAdd': 'inventory.add_machine',
    'MachineChange': 'inventory.change_machine',
    'MachineManagement': 'inventory.view_machine',
    'IdcManagement': 'inventory.view_idc',
    'SpecificationManagement': 'inventory.view_specification'
  },

src/components/Navigation.vue增加对应的跳转按钮

<el-menu>
...
    <el-submenu index="inventory">
      <template slot="title">
        <i class="fas fa-sitemap fa-lg"></i>
        <span slot="title">资产管理</span>
      </template>
      <el-submenu index="machine">
        <span slot="title"><i class="fas fa-server fa-lg"></i> 机器</span>
        <el-menu-item index="/machineAdd">
          <i class="fas fa-plus-circle fa-lg"></i>
          <span slot="title">添加服务器</span>
        </el-menu-item>
        <el-menu-item index="/machineManagement">
          <i class="fas fa-list-alt fa-lg"></i>
          <span slot="title">服务器管理</span>
        </el-menu-item>
      </el-submenu>
      <el-submenu index="idc">
        <span slot="title"><i class="fas fa-building fa-lg"></i> 机房</span>
        <el-menu-item index="/idcManagement">
          <i class="fas fa-cubes fa-lg"></i>
          <span slot="title">机房管理</span>
        </el-menu-item>
        <el-menu-item index="/specificationManagement">
          <i class="fas fa-cube fa-lg"></i>
          <span slot="title">机架位管理</span>
        </el-menu-item>
      </el-submenu>
    </el-submenu>
</el-menu>

src/router/index.js添加router关系

...
import MachineManagement from '@/components/MachineManagement'
import MachineEdit from '@/components/MachineEdit'
import IdcManagement from '@/components/IdcManagement'
import SpecificationManagement from '@/components/SpecificationManagement'
...
export default new Router({
  mode: 'history',
  routes: [
    ...,
    {
      path: '/machineManagement',
      name: 'MachineManagement',
      component: MachineManagement
    },
    {
      path: '/machineAdd',
      name: 'MachineAdd',
      component: MachineEdit
    },
    {
      path: '/machineChange/:id',
      name: 'MachineChange',
      component: MachineEdit
    },
    {
      path: '/idcManagement',
      name: 'IdcManagement',
      component: IdcManagement
    },
    {
      path: 'specificationManagement',
      name: 'SpecificationManagement',
      component: SpecificationManagement
    }
  ]
})

package.json更新依赖

{
  ...
  "dependencies": {
    ...,
    "file-saver": "^1.3.8",
  },
...
}

更新了依赖需要npm install

src/components/添加新的组件

IdcManagement管理idc组件

src/components/IdcManagement.vue

通过changemode参数控制一个编辑页面,可以用来编译和添加

<template>
  <div>
    <div class="div-header">
      <el-row>
        <el-col :span="3">
          <el-button type="primary" size="mini" icon="el-icon-plus"
            :disabled="$store.state.permissions.indexOf('inventory.add_idc') < 0"
            @click="handleAdd">
            添加机房
          </el-button>
        </el-col>
        <el-col :span="3">
          <el-badge :value="multipleSelection.length"
            :hidden="$store.state.permissions.indexOf('inventory.delete_idc') < 0 ||
            !multipleSelection.length > 0"
            :max="100" class="item">
            <el-button type="danger" size="mini" icon="el-icon-delete"
              :disabled="$store.state.permissions.indexOf('inventory.delete_idc') < 0 ||
              !multipleSelection.length > 0" @click="deleteMutiple">
              批量删除
            </el-button>
          </el-badge>
        </el-col>
        <el-col :offset="13" :span="4">
          <el-input placeholder="请输入搜索内容" clearable size="mini" suffix-icon="el-icon-search"
            v-model="search" @input="handleSearch">
          </el-input>
        </el-col>
      </el-row>
    </div>
    <el-table stripe border :data="tableData" style="width: 100%" v-loading="loading" element-loading-text="拼命加载中" @selection-change="handleSelectionChange"
      @sort-change="sortChange">
      <el-table-column type="selection"></el-table-column>
      <el-table-column prop="id" sortable="custom" label="ID"></el-table-column>
      <el-table-column prop="name" sortable="custom" label="IDC名称"></el-table-column>
      <el-table-column prop="code" sortable="custom" label="IDC代码"></el-table-column>
      <el-table-column prop="location" sortable="custom" label="地址"></el-table-column>
      <el-table-column label="操作" width="210">
        <template slot-scope="scope">
          <el-button size="mini" icon="el-icon-edit"
            :disabled="$store.state.permissions.indexOf('inventory.change_idc') < 0"
            @click="handleEdit(scope.$index, scope.row)">
            编辑
          </el-button>
          <el-button size="mini" icon="el-icon-delete" type="danger"
            :disabled="$store.state.permissions.indexOf('inventory.delete_idc') < 0"
            @click="handleDelete(scope.$index, scope.row)">
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <idcEdit :title="title" :idcFormVisible.sync="idcFormVisible"
      :changeMode="changeMode" :idcForm="idcForm" @reloadTable="reloadTable">
    </idcEdit>
    <pagination :total="total" :pageSizes="[20, 50, 100]" :pageSize.sync="size"
      :currentPage.sync="page" @reloadTable="reloadTable">
    </pagination>
  </div>
</template>

<script>
  import idcEdit from '@/components/IdcEdit.vue';
  import pagination from '@/components/Pagination.vue';
  export default {
    components: {
      'idcEdit': idcEdit,
      'pagination': pagination
    },
    data: function () {
      return {
        loading: false,
        multipleSelection: [],
        tableData: [],
        total: 0,
        title: '',
        idcFormVisible: false,
        changeMode: false,
        idcForm: {
          id: '',
          name: '',
          code: '',
          location: ''
        },
        size: 20,
        page: 1,
        search: '',
        orderBy: '',
        order: '',
      };
    },
    methods: {
      handleSelectionChange: function (val) {
        this.multipleSelection = val;
      },
      reloadTable: function () {
        var _this = this;
        var url = '/api/inventory/getIdcs/' + '?search=' + this.search + '&orderBy=' + this.orderBy + '&order=' + this.order + '&size=' + this.size + '&page=' + this.page;
        this.loading = true;
        this.$http.get(url).then(function (response) {
          _this.tableData = response.data.tableData;
          _this.total = response.data.total;
          _this.loading = false;
        }).catch(function (error) {
          _this.$message.error('获取NIC列表失败');
          _this.loading = false;
        });
      },
      handleSearch: function () {
        setTimeout(this.reloadTable, 500);
      },
      handleEdit: function (index, row) {
        this.idcForm.id = row.id;
        this.idcForm.name = row.name;
        this.idcForm.code = row.code;
        this.idcForm.location = row.location;
        this.title = '编辑IDC信息';
        this.changeMode = true;
        this.idcFormVisible = true;
      },
      handleDelete: function (index, row) {
        var idList = [row.id];
        this.deleteSubmit(idList);
      },
      deleteMutiple: function () {
        var idList = []
        this.multipleSelection.forEach(v => {idList.push(v.id)});
        this.deleteSubmit(idList);
      },
      handleAdd: function () {
        this.title = '添加IDC信息';
        this.idcForm.name = '';
        this.idcForm.code = '';
        this.idcForm.location = '';
        this.changeMode = false;
        this.idcFormVisible = true;
      },
      deleteSubmit: function (idList) {
        var _this = this;
        this.$confirm('删除IDC信息: [' + idList.toString() + '], 是否继续?', '确认', {
          confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'}).then(() => {
            this.$http.post('/api/inventory/deleteIdc/', {'idList': idList}).then(
              function (response) {
                _this.$notify({ title: '成功', message: '删除IDC信息成功', type: 'success'});
                _this.reloadTable();
              }
            ).catch(function (error) {
              if (error.response.status === 403) {
                _this.$message.warning('没有权限操作');
              } else {
                _this.$message.error('删除IDC信息失败');
              }
            }
          );
        }).catch(() => {
          this.$message({type: 'info', message: '已取消删除'});
        });
      },
      sortChange: function (sort) {
        this.orderBy = sort.prop;
        this.order = sort.order;
        this.reloadTable();
      }
    },
    mounted: function () {
      this.reloadTable();
    }
  }
</script>

IdcEdit编辑idc

src/components/IdcEdit.vue

使用el-dialog用于弹出

<template>
  <el-dialog :title="title" :visible="idcFormVisible" @close="handleClose">
    <el-form label-position="left" status-icon ref="idcForm" :model="idcForm" :rules="rules"
      label-width="100px">
      <el-form-item v-if="changeMode" label="ID" prop="id">
        <el-input :disabled="changeMode" v-model="idcForm.id"></el-input>
      </el-form-item>
      <el-form-item label="IDC名称" prop="name">
        <el-input v-model.trim="idcForm.name"></el-input>
      </el-form-item>
      <el-form-item label="IDC代码" prop="code">
        <el-input v-model.trim="idcForm.code">
        </el-input>
      </el-form-item>
      <el-form-item label="地址" prop="location">
        <el-input v-model.trim="idcForm.location">
        </el-input>
      </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
      <el-button @click="handleClose">取消</el-button>
      <el-button type="primary" @click="saveSubmit('idcForm')">保存</el-button>
    </div>
  </el-dialog>
</template>
<script>
  export default {
    props: ['title', 'idcFormVisible', 'changeMode', 'idcForm'],
    data: function () {
      return {
        rules: {
          name: [
            { required: true, message: '请输入IDC名称', trigger: 'blur' },
            { max: 50, message: '长度不得超过50个字符', trigger: 'blur' }
          ],
          code: [
            { required: true, message: '请输入IDC代码', trigger: 'blur' },
            { max: 20, message: '长度不得超过20个字符', trigger: 'blur' }
          ],
          location: [
            { max: 150, message: '长度不得超过150个字符', trigger: 'blur' }
          ]
        }
      }
    },
    methods: {
      saveSubmit: function (formName) {
        var _this = this;
        this.$refs[formName].validate((valid) => {
          if (valid) {
            var url = '';
            this.changeMode ? url = '/api/inventory/changeIdc/' : url = '/api/inventory/addIdc/';
            this.$http.post(url, this.idcForm).then(function (response) {
              _this.$notify({ title: '成功', message: '保存IDC信息成功', type: 'success'});
              _this.handleClose();
              _this.$emit('reloadTable');
            }).catch(function (error) {
              if (error.response.status === 403) {
                _this.$message.warning('没有权限操作');
              } else {
                _this.$message.error('保存IDC信息失败');
              }
            });
          } else {
            _this.$message.error('未通过表单验证');
            return false;
          }
        });
      },
      handleClose: function () {
        this.$emit('update:idcFormVisible', false);
      }
    }
  }
</script>

SpecificationManagement管理界面

src/components/SpecificationManagement.vue

<template>
  <div>
    <div class="div-header">
      <el-row>
        <el-col :span="3">
          <el-button type="primary" size="mini" icon="el-icon-plus"
            :disabled="$store.state.permissions.indexOf('inventory.add_specification') < 0"
            @click="handleAdd">
            添加机型
          </el-button>
        </el-col>
        <el-col :span="3">
          <el-badge :value="multipleSelection.length"
            :hidden="$store.state.permissions.indexOf('inventory.delete_specification') < 0 ||
            !multipleSelection.length > 0"
            :max="100" class="item">
            <el-button type="danger" size="mini" icon="el-icon-delete"
              :disabled="$store.state.permissions.indexOf('inventory.delete_specification') < 0 ||
              !multipleSelection.length > 0" @click="deleteMutiple">
              批量删除
            </el-button>
          </el-badge>
        </el-col>
        <el-col :offset="13" :span="4">
          <el-input placeholder="请输入搜索内容" clearable size="mini" suffix-icon="el-icon-search" v-model="search" @input="handleSearch">
          </el-input>
        </el-col>
      </el-row>
    </div>
    <el-table stripe border :data="tableData" style="width: 100%" v-loading="loading" element-loading-text="拼命加载中" @selection-change="handleSelectionChange" @sort-change="sortChange">
      <el-table-column type="selection"></el-table-column>
      <el-table-column prop="id" sortable="custom" label="ID"></el-table-column>
      <el-table-column prop="idcName" sortable="custom" label="IDC" :formatter="formatter"></el-table-column>
      <el-table-column prop="name" sortable="custom" label="机型"></el-table-column>
      <el-table-column prop="cpu" sortable="custom" label="CPU核数"></el-table-column>
      <el-table-column prop="mem" sortable="custom" label="内存大小"></el-table-column>
      <el-table-column label="操作" width="210">
        <template slot-scope="scope">
          <el-button size="mini" icon="el-icon-edit"
            :disabled="$store.state.permissions.indexOf('inventory.change_specification') < 0"
            @click="handleEdit(scope.$index, scope.row)">
            编辑
          </el-button>
          <el-button size="mini" icon="el-icon-delete" type="danger"
            :disabled="$store.state.permissions.indexOf('inventory.delete_specification') < 0"
            @click="handleDelete(scope.$index, scope.row)">
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <specificationEdit :title="title" :specificationFormVisible.sync="specificationFormVisible"
      :changeMode="changeMode" :idcOptions="idcOptions" :specificationForm="specificationForm"
      @reloadTable="reloadTable">
    </specificationEdit>
    <pagination :total="total" :pageSizes="[20, 50, 100]" :pageSize.sync="size"
      :currentPage.sync="page" @reloadTable="reloadTable">
    </pagination>
  </div>
</template>

<script>
  import specificationEdit from '@/components/SpecificationEdit.vue';
  import pagination from '@/components/Pagination.vue';
  export default {
    components: {
      'specificationEdit': specificationEdit,
      'pagination': pagination
    },
    data: function () {
      return {
        loading: false,
        multipleSelection: [],
        tableData: [],
        total: 0,
        specificationFormVisible: false,
        changeMode: false,
        title: '',
        idcOptions: [],
        specificationForm: {
          id: '',
          idc: '',
          name: '',
          cpu: '',
          mem: ''
        },
        size: 20,
        page: 1,
        search: '',
        orderBy: '',
        order: ''
      };
    },
    methods: {
      handleSelectionChange: function (val) {
        this.multipleSelection = val;
      },
      formatter: function (row, column) {
        return row.idcName + '(' + row.idcCode + ')';
      },
      reloadTable: function () {
        var _this = this;
        var url = '/api/inventory/getSpecifications/' + '?search=' + this.search + '&orderBy=' + this.orderBy + '&order=' + this.order + '&size=' + this.size + '&page=' + this.page;
        this.loading = true;
        this.$http.get(url).then(function (response) {
          _this.idcOptions = response.data.idcOptions;
          _this.tableData = response.data.tableData;
          _this.total = response.data.total;
          _this.loading = false;
        }).catch(function (error) {
          _this.$message.error('获取机型列表失败');
          _this.loading = false;
        });
      },
      handleSearch: function () {
        setTimeout(this.reloadTable, 500);
      },
      handleEdit: function (index, row) {
        this.specificationForm.id = row.id;
        this.specificationForm.idc = row.idcId;
        this.specificationForm.name = row.name;
        this.specificationForm.cpu = row.cpu;
        this.specificationForm.mem = row.mem;
        this.title = '编辑机型信息';
        this.changeMode = true;
        this.specificationFormVisible = true;
      },
      handleDelete: function (index, row) {
        var idList = [row.id];
        this.deleteSubmit(idList);
      },
      deleteMutiple: function () {
        var idList = []
        this.multipleSelection.forEach(v => {idList.push(v.id)});
        this.deleteSubmit(idList);
      },
      handleAdd: function () {
        this.title = '添加机型信息';
        this.changeMode = false;
        this.specificationForm.idc = '';
        this.specificationForm.name = '';
        this.specificationForm.cpu = '';
        this.specificationForm.mem = '';
        this.specificationFormVisible = true;
      },
      deleteSubmit: function (idList) {
        var _this = this;
        this.$confirm('删除机型信息: [' + idList.toString() + '], 是否继续?', '确认', {
          confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'}).then(() => {
            this.$http.post('/api/inventory/deleteSpecification/', {'idList': idList}).then(
              function (response) {
                _this.$notify({ title: '成功', message: '删除机型信息成功', type: 'success'});
                _this.reloadTable();
              }
            ).catch(function (error) {
              if (error.response.status === 403) {
                _this.$message.warning('没有权限操作');
              } else {
                _this.$message.error('删除机型信息失败');
              }
            }
          );
        }).catch(() => {
          this.$message({type: 'info', message: '已取消删除'});
        });
      },
      sortChange: function (sort) {
        this.orderBy = sort.prop;
        this.order = sort.order;
        this.reloadTable();
      }
    },
    mounted: function () {
     this.reloadTable();
    }
  }
</script>

SpecificationEdit机型编辑

<template>
  <el-dialog :title="title" :visible="specificationFormVisible" @close="handleClose">
    <el-form label-position="left" status-icon ref="specificationForm" :model="specificationForm" :rules="rules"
      label-width="100px">
      <el-form-item v-if="changeMode" label="ID" prop="id">
        <el-input :disabled="changeMode" v-model="specificationForm.id"></el-input>
      </el-form-item>
      <el-form-item label="IDC" prop="idc">
        <el-select v-model="specificationForm.idc" placeholder="请选择IDC">
          <el-option v-for="(item, index) in idcOptions" :key="index" 
            :label="item.name+'('+item.code+')'" :value="item.id">
          </el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="机型" prop="name">
        <el-input v-model.trim="specificationForm.name"></el-input>
      </el-form-item>
      <el-form-item label="CPU数量" prop="cpu">
        <el-input v-model.trim="specificationForm.cpu"></el-input>
      </el-form-item>
      <el-form-item label="内存大小" prop="mem">
        <el-input v-model.trim="specificationForm.mem"></el-input>
      </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
      <el-button @click="handleClose">取消</el-button>
      <el-button type="primary" @click="saveSubmit('specificationForm')">保存</el-button>
    </div>
  </el-dialog>
</template>
<script>
  export default {
    props: ['title', 'specificationFormVisible', 'changeMode', 'specificationForm', 'idcOptions'],
    data: function () {
      return {
        rules: {
          idc: [
            { required: true, message: '请选择IDC', trigger: 'change' }
          ],
          name: [
            { required: true, message: '请输入机型名称', trigger: 'blur' },
            {max: 100, message: '长度不得超过100个字符', trigger: 'blur'}
          ],
          cpu: [
            { required: true, message: '请输入CPU核数', trigger: 'blur' }
          ],
          mem: [
            { required: true, message: '请输入内存大小', trigger: 'blur' }
          ]

        }
      }
    },
    methods: {
      saveSubmit: function (formName) {
        var _this = this;
        this.$refs[formName].validate((valid) => {
          if (valid) {
            var url = '';
            this.changeMode ? url = '/api/inventory/changeSpecification/' : url = '/api/inventory/addSpecification/';
            this.$http.post(url, this.specificationForm).then(function (response) {
              _this.$notify({ title: '成功', message: '保存机型信息成功', type: 'success'});
              _this.handleClose();
              _this.$emit('reloadTable');
            }).catch(function (error) {
              if (error.response.status === 403) {
                _this.$message.warning('没有权限操作');
              } else {
                _this.$message.error('保存机型信息失败');
              }
            });
          } else {
            _this.$message.error('未通过表单验证');
            return false;
          }
        });
      },
      handleClose: function () {
        this.$emit('update:specificationFormVisible', false);
      }
    }
  }
</script>

<style scoped>
  .el-select {
    width: 100%;
  }
</style>

测试

机房管理界面

机型管理界面