diff --git a/apps/assets/forms.py b/apps/assets/forms.py
index 30e48a443448aae04e9456518be0d4c7dd931839..c2d7e9cd448f97a728e5fb5b385a800d7468288a 100644
--- a/apps/assets/forms.py
+++ b/apps/assets/forms.py
@@ -13,19 +13,24 @@ class AssetCreateForm(forms.ModelForm):
     class Meta:
         model = Asset
         fields = [
-            'hostname', 'ip', 'public_ip', 'port', 'type', 'comment', 'admin_user',
-            'idc', 'groups', 'status', 'env', 'is_active'
+            'hostname', 'ip', 'public_ip', 'port', 'type', 'comment',
+            'admin_user', 'idc', 'groups', 'status', 'env', 'is_active'
         ]
         widgets = {
-            'groups': forms.SelectMultiple(attrs={'class': 'select2',
-                                                  'data-placeholder': _('Select asset groups')}),
-            'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select asset admin user')}),
+            'groups': forms.SelectMultiple(
+                attrs={'class': 'select2',
+                       'data-placeholder': _('Select asset groups')}),
+            'admin_user': forms.Select(
+                attrs={'class': 'select2',
+                       'data-placeholder': _('Select asset admin user')}),
         }
         help_texts = {
             'hostname': '* required',
             'ip': '* required',
-            'system_users': _('System user will be granted for user to login assets (using ansible create automatic)'),
-            'admin_user': _('Admin user should be exist on asset already, And have sudo ALL permission'),
+            'system_users': _('System user will be granted for user to login '
+                              'assets (using ansible create automatic)'),
+            'admin_user': _('Admin user should be exist on asset already, '
+                            'And have sudo ALL permission'),
         }
 
     def clean_admin_user(self):
@@ -43,23 +48,43 @@ class AssetUpdateForm(forms.ModelForm):
             'cabinet_pos', 'number', 'comment'
         ]
         widgets = {
-            'groups': forms.SelectMultiple(attrs={'class': 'select2',
-                                                  'data-placeholder': _('Select asset groups')}),
-            'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select asset admin user')}),
+            'groups': forms.SelectMultiple(
+                attrs={'class': 'select2',
+                       'data-placeholder': _('Select asset groups')}),
+            'admin_user': forms.Select(
+                attrs={'class': 'select2',
+                       'data-placeholder': _('Select asset admin user')}),
         }
         help_texts = {
             'hostname': '* required',
             'ip': '* required',
-            'system_users': _('System user will be granted for user to login assets (using ansible create automatic)'),
-            'admin_user': _('Admin user should be exist on asset already, And have sudo ALL permission'),
+            'system_users': _('System user will be granted for user '
+                              'to login assets (using ansible create automatic)'),
+            'admin_user': _('Admin user should be exist on asset '
+                            'already, And have sudo ALL permission'),
         }
 
 
 class AssetBulkUpdateForm(forms.ModelForm):
+    assets = forms.MultipleChoiceField(
+        required=True,
+        help_text='* required',
+        label=_('Select assets'),
+        choices=[(asset.id, asset.hostname) for asset in Asset.objects.all()],
+        widget=forms.SelectMultiple(
+            attrs={
+                'class': 'select2',
+                'data-placeholder': _('Select assets')
+            }
+        )
+    )
+    port = forms.IntegerField(min_value=1, max_value=65535,
+                              required=False, label=_('Port'))
+
     class Meta:
         model = Asset
         fields = [
-            'port', 'groups', 'admin_user', 'idc',
+            'assets', 'port', 'groups', 'admin_user', 'idc',
             'type', 'env', 'status',
         ]
         widgets = {
@@ -71,6 +96,17 @@ class AssetBulkUpdateForm(forms.ModelForm):
                        'data-placeholder': _('Select asset admin user')}),
         }
 
+    def save(self, commit=True):
+        cleaned_data = {k: v for k, v in self.cleaned_data.items() if v is not None}
+        assets_id = cleaned_data.pop('assets')
+        groups = cleaned_data.pop('groups')
+        assets = Asset.objects.filter(id__in=assets_id)
+        assets.update(**cleaned_data)
+        if groups:
+            for asset in assets:
+                asset.groups.set(groups)
+        return assets
+
 
 class AssetGroupForm(forms.ModelForm):
     # See AdminUserForm comment same it
diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py
index 499ad2cf0f75d2cde06057901b7586e763101acd..bba94c749ca3d23f9c9f3518ab360752b653aafd 100644
--- a/apps/assets/models/asset.py
+++ b/apps/assets/models/asset.py
@@ -41,10 +41,13 @@ class Asset(models.Model):
     ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
     hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname'))
     port = models.IntegerField(default=22, verbose_name=_('Port'))
-    groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups'))
+    groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets',
+                                    verbose_name=_('Asset groups'))
     admin_user = models.ForeignKey(AdminUser, null=True, blank=True, related_name='assets',
                                    on_delete=models.SET_NULL, verbose_name=_("Admin user"))
-    system_users = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User"))
+    system_users = models.ManyToManyField(SystemUser, blank=True,
+                                          related_name='assets',
+                                          verbose_name=_("System User"))
     idc = models.ForeignKey(IDC, blank=True, null=True, related_name='assets',
                             on_delete=models.SET_NULL, verbose_name=_('IDC'),)
     is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
@@ -52,12 +55,14 @@ class Asset(models.Model):
                             default='Server', verbose_name=_('Asset type'),)
     env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True,
                            default='Prod', verbose_name=_('Asset environment'),)
-    status = models.CharField(choices=STATUS_CHOICES, max_length=8, null=True, blank=True,
+    status = models.CharField(choices=STATUS_CHOICES, max_length=12, null=True, blank=True,
                               default='In use', verbose_name=_('Asset status'))
 
     # Some information
-    public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP'))
-    remote_card_ip = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Remote control card IP'))
+    public_ip = models.GenericIPAddressField(max_length=32, blank=True,
+                                             null=True, verbose_name=_('Public IP'))
+    remote_card_ip = models.CharField(max_length=16, null=True, blank=True,
+                                      verbose_name=_('Remote control card IP'))
     cabinet_no = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Cabinet number'))
     cabinet_pos = models.IntegerField(null=True, blank=True, verbose_name=_('Cabinet position'))
     number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number'))
diff --git a/apps/assets/templates/assets/asset_bulk_update.html b/apps/assets/templates/assets/asset_bulk_update.html
index 44951778ed0195f9e70967ada0c3f98ef6dcfbb7..e72b443029f40ab6c220222d3f0e870ef4119848 100644
--- a/apps/assets/templates/assets/asset_bulk_update.html
+++ b/apps/assets/templates/assets/asset_bulk_update.html
@@ -9,32 +9,14 @@
     <div class="tagBtnList">
         <a class="label label-primary" id="change_all" value="1">全选</a>
         {% for field in form %}
+            {% if field.name != 'assets' %}
              <a data-id="{{ field.id_for_label }}" class="label label-default label-primary field-tag" value="1">{{ field.label }}</a>
+            {% endif %}
         {% endfor %}
     </div>
 </div>
-{% if errors %}
-    <div class="alert alert-danger">
-        {{ errors }}
-    </div>
-{% endif %}
 <form method="post" class="form-horizontal" id="add_form">
     {% csrf_token %}
-    <div class="form-group abc">
-        <label class="control-label col-sm-2 col-lg-2 " id="asset_on_count">{% trans 'Asset' %}</label>
-        <div class="col-sm-9">
-            <select class="form-control select2" multiple="multiple" name="assets">
-                {% for asset in assets %}
-                    {% if asset.id in assets_selected %}
-                        <option selected="selected" value="{{ asset.id }}">{{ asset.hostname }}</option>
-                    {% else %}
-                        <option value="{{ asset.id }}">{{ asset.hostname }}</option>
-                    {% endif %}
-                {% endfor %}
-            </select>
-            <span class="help-block">* required</span>
-        </div>
-    </div>
     {% bootstrap_form form layout="horizontal" %}
     <div class="form-group abc">
         <div class="col-sm-4 col-sm-offset-2">
@@ -83,33 +65,5 @@
             form_groups.filter(':has(#'  + field_id + ')').hide().find('select,input').prop('disabled', true)
         }
     }
-
-    function fsubmit(){
-        var assets_id = document.getElementsByName("assets");
-        var oForm = document.getElementById('add_form');
-        var parentElem = document.getElementById("add_form");
-        var aDiv = parentElem.getElementsByClassName('form-group');
-        if (assets_id.length === 0) {
-            swal({
-                title: "未选择需要修改的主机",
-                text: "请点击选择"
-            });
-        }else if (aDiv.length === 1) {
-            swal({
-                title: "未选需要修改的属性",
-                text: "请点击选择"
-            });
-        }else{
-            var m = document.getElementsByName('assets_ids');
-            alert(m.length);
-            for(var i=0;i<m.length;i++){
-                alert(m[0].value);
-                oForm.appendChild(m[0]);
-            }
-            action="/assets/asset/"+assets_id[0].value+"/update";
-            oForm.action=action;
-            oForm.submit();
-        }
-    }
 </script>
 {% endblock %}
diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html
index 3bfa068c1f7f6bba220bc3cab80ecbebea2cdf02..873acebbf8b04c258bdae780faafaab418318de7 100644
--- a/apps/assets/templates/assets/asset_list.html
+++ b/apps/assets/templates/assets/asset_list.html
@@ -88,6 +88,7 @@ function tagShow() {
 }  //onload;
 
 
+
 $(document).ready(function(){
     var options = {
         ele: $('#asset_list_table'),
@@ -125,6 +126,7 @@ $(document).ready(function(){
         op_html: $('#actions').html()
     };
     var table = jumpserver.initDataTable(options);
+
     $('.btn_export').click(function () {
         var assets = [];
         var rows = table.rows('.selected').data();
@@ -178,20 +180,18 @@ $(document).ready(function(){
         $data_table.ajax.reload();
     }, 3000);
 })
-
 .on('click', '#btn_bulk_update', function () {
     var action = $('#slct_bulk_update').val();
     var $data_table = $('#asset_list_table').DataTable();
     var id_list = [];
-    var plain_id_list = [];
     $data_table.rows({selected: true}).every(function(){
-        id_list.push({id: this.data().id});
-        plain_id_list.push(this.data().id);
+        id_list.push(this.data().id);
     });
-    if (plain_id_list.length == 0) {
+    if (id_list.length == 0) {
         return false;
     }
     var the_url = "{% url 'api-assets:asset-list' %}";
+
     function doDeactive() {
         var body = $.each(id_list, function(index, asset_object) {
             asset_object['is_active'] = false;
@@ -234,7 +234,9 @@ $(document).ready(function(){
         });
     }
     function doUpdate() {
-        $('#asset_bulk_update_modal').modal('show');
+        var id_list_string = id_list.join(',');
+        var url = "{% url 'assets:asset-bulk-update' %}?assets_id=" + id_list_string;
+        location.href = url
     }
     switch(action) {
         case 'deactive':
@@ -252,66 +254,66 @@ $(document).ready(function(){
         default:
             break;
     }
-})
-
-.on('click', '#btn_asset_bulk_update', function () {
-    var json_data = $("#fm_asset_bulk_update").serializeObject();
-    var body = {};
-    body.enable_otp = (json_data.enable_otp === 'on')? true: false;
-    if (json_data.type != '') {
-        body.type = json_data.type;
-    }
-    if (json_data.groups != undefined) {
-        body.groups = json_data.groups;
-    }
-    if (typeof body.groups === 'string') {
-        body.groups = [parseInt(body.groups)]
-    } else if(typeof body.groups === 'array') {
-        var new_groups = body.groups.map(Number);
-        body.groups = new_groups;
-    }
-
-    if (json_data.system_users != undefined) {
-        body.system_users = json_data.system_users;
-    }
-    if (typeof body.system_users === 'string') {
-        body.system_users = [parseInt(body.system_users)]
-    } else if(typeof body.system_users === 'array') {
-        var new_users = body.system_users.map(Number);
-        body.system_users = new_users;
-    }
-
-    if (json_data.tags != undefined) {
-        body.tags = json_data.tags;
-    }
-    if (typeof body.tags == 'string') {
-        body.tags = [parseInt(body.tags)];
-    } else if (typeof body.tags === 'array') {
-        var new_tags = body.tags.map(Number);
-        body.tags = new_tags;
-    }
-
-    var $data_table = $('#asset_list_table').DataTable();
-    var post_list = [];
-    $data_table.rows({selected: true}).every(function(){
-        var content = Object.assign({id: this.data().id}, body);
-        post_list.push(content);
-    });
-    if (post_list === []) {
-        return false
-    }
-    var the_url = "{% url 'api-assets:asset-list' %}";
-    var success = function() {
-        var msg = "{% trans 'The selected assets has been updated successfully.' %}";
-        swal("{% trans 'Asset Updated' %}", msg, "success");
-        $('#asset_list_table').DataTable().ajax.reload();
-        jumpserver.checked = false;
-    };
-    console.log(JSON.stringify(post_list));
-    console.log(the_url);
-{#    APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});#}
-    $('#asset_bulk_update_modal').modal('hide');
 });
+{##}
+{#.on('click', '#btn_asset_bulk_update', function () {#}
+{#    var json_data = $("#fm_asset_bulk_update").serializeObject();#}
+{#    var body = {};#}
+{#    body.enable_otp = (json_data.enable_otp === 'on')? true: false;#}
+{#    if (json_data.type != '') {#}
+{#        body.type = json_data.type;#}
+{#    }#}
+{#    if (json_data.groups != undefined) {#}
+{#        body.groups = json_data.groups;#}
+{#    }#}
+{#    if (typeof body.groups === 'string') {#}
+{#        body.groups = [parseInt(body.groups)]#}
+{#    } else if(typeof body.groups === 'array') {#}
+{#        var new_groups = body.groups.map(Number);#}
+{#        body.groups = new_groups;#}
+{#    }#}
+{##}
+{#    if (json_data.system_users != undefined) {#}
+{#        body.system_users = json_data.system_users;#}
+{#    }#}
+{#    if (typeof body.system_users === 'string') {#}
+{#        body.system_users = [parseInt(body.system_users)]#}
+{#    } else if(typeof body.system_users === 'array') {#}
+{#        var new_users = body.system_users.map(Number);#}
+{#        body.system_users = new_users;#}
+{#    }#}
+{##}
+{#    if (json_data.tags != undefined) {#}
+{#        body.tags = json_data.tags;#}
+{#    }#}
+{#    if (typeof body.tags == 'string') {#}
+{#        body.tags = [parseInt(body.tags)];#}
+{#    } else if (typeof body.tags === 'array') {#}
+{#        var new_tags = body.tags.map(Number);#}
+{#        body.tags = new_tags;#}
+{#    }#}
+{##}
+{#    var $data_table = $('#asset_list_table').DataTable();#}
+{#    var post_list = [];#}
+{#    $data_table.rows({selected: true}).every(function(){#}
+{#        var content = Object.assign({id: this.data().id}, body);#}
+{#        post_list.push(content);#}
+{#    });#}
+{#    if (post_list === []) {#}
+{#        return false#}
+{#    }#}
+{#    var the_url = "{% url 'api-assets:asset-list' %}";#}
+{#    var success = function() {#}
+{#        var msg = "{% trans 'The selected assets has been updated successfully.' %}";#}
+{#        swal("{% trans 'Asset Updated' %}", msg, "success");#}
+{#        $('#asset_list_table').DataTable().ajax.reload();#}
+{#        jumpserver.checked = false;#}
+{#    };#}
+{#    console.log(JSON.stringify(post_list));#}
+{#    console.log(the_url);#}
+{#    APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});#}
+{#    $('#asset_bulk_update_modal').modal('hide');#}
+{#})#}
 
 </script>
 {% endblock %}
diff --git a/apps/assets/views/asset.py b/apps/assets/views/asset.py
index 39ce7e0ddf8eb5344f4b6c076ed1277f3505333b..177e347984f125dd844110701ff8f1cc3dfcd468 100644
--- a/apps/assets/views/asset.py
+++ b/apps/assets/views/asset.py
@@ -20,7 +20,7 @@ from django.utils.decorators import method_decorator
 from django.core.cache import cache
 from django.utils import timezone
 from django.contrib.auth.mixins import LoginRequiredMixin
-from django.shortcuts import get_object_or_404, redirect
+from django.shortcuts import get_object_or_404, redirect, reverse
 
 from common.mixins import JSONResponseMixin
 from common.utils import get_object_or_none
@@ -43,7 +43,7 @@ class AssetListView(AdminUserRequiredMixin, TemplateView):
     def get_context_data(self, **kwargs):
         context = {
             'app': 'Assets',
-            'action': 'asset list',
+            'action': 'Asset list',
             'groups': AssetGroup.objects.all(),
             'system_users': SystemUser.objects.all(),
             # 'form': forms.AssetBulkUpdateForm(),
@@ -58,7 +58,7 @@ class UserAssetListView(LoginRequiredMixin, TemplateView):
     def get_context_data(self, **kwargs):
         context = {
             'app': 'Assets',
-            'action': 'asset list',
+            'action': 'Asset list',
             'system_users': SystemUser.objects.all(),
         }
         kwargs.update(context)
@@ -118,62 +118,24 @@ class AssetBulkUpdateView(AdminUserRequiredMixin, ListView):
     def get(self, request, *args, **kwargs):
         assets_id = self.request.GET.get('assets_id', '')
         self.assets_id_list = [int(i) for i in assets_id.split(',') if i.isdigit()]
-        self.form = self.form_class()
-        self.errors = kwargs.get('errors')
+
+        if kwargs.get('form'):
+            self.form = kwargs['form']
+        elif assets_id:
+            self.form = self.form_class(
+                initial={'assets': self.assets_id_list}
+            )
+        else:
+            self.form = self.form_class()
         return super(AssetBulkUpdateView, self).get(request, *args, **kwargs)
 
     def post(self, request, *args, **kwargs):
-        raw_data = request.POST
-        data = {}
-        errors = defaultdict(list)
-        for k in raw_data:
-            if not hasattr(Asset, k) or raw_data.get(k) == '':
-                if k not in ['assets']:
-                    continue
-            if k == 'assets':
-                v = Asset.objects.filter(id__in=raw_data.getlist(k))
-                if not v:
-                    errors['assets'].append(_('Required'))
-            elif k == 'port':
-                try:
-                    v = int(raw_data.get(k))
-                except ValueError:
-                    v = None
-                    errors['port'].append(_('Integer required'))
-            elif k == 'admin_user':
-                admin_user_id = raw_data.get(k)
-                try:
-                    v = int(admin_user_id)
-                except ValueError:
-                    v = None
-                    errors['admin_user'].append(_('Invalid admin user'))
-                v = get_object_or_none(AdminUser, id=v)
-            elif k == 'groups':
-                groups_id = raw_data.getlist(k)
-                v = [AssetGroup.objects.filter(id__in=groups_id)]
-            elif k == 'idc':
-                idc_id = raw_data.get(k)
-                try:
-                    v = int(idc_id)
-                except ValueError:
-                    v = None
-                    errors['idc'].append(_('Integer required'))
-                v = get_object_or_none(IDC, id=v)
-            else:
-                v = raw_data.get(k)
-            data[k] = v
-
-        if not errors:
-            for asset in data['assets']:
-                for k, v in data.items():
-                    if k == 'groups':
-                        asset.groups.set(data['groups'])
-                    else:
-                        setattr(asset, k, v)
-                asset.save()
-                return redirect(reverse_lazy('assets:asset-list'))
+        form = self.form_class(request.POST)
+        if form.is_valid():
+            form.save()
+            return redirect(self.success_url)
         else:
-            return self.get(request, errors=errors, *args, **kwargs)
+            return self.get(request, form=form, *args, **kwargs)
 
     def get_context_data(self, **kwargs):
         # assets_list = Asset.objects.filter(id__in=self.assets_id_list)
@@ -181,7 +143,6 @@ class AssetBulkUpdateView(AdminUserRequiredMixin, ListView):
             'app': 'Assets',
             'action': 'Bulk update asset',
             'form': self.form,
-            'errors': self.errors,
             'assets_selected': self.assets_id_list,
             'assets': Asset.objects.all(),
         }
@@ -236,9 +197,6 @@ class AssetDetailView(DetailView):
         return super(AssetDetailView, self).get_context_data(**kwargs)
 
 
-
-
-
 @method_decorator(csrf_exempt, name='dispatch')
 class AssetExportView(View):
     def get(self, request, *args, **kwargs):
diff --git a/apps/users/forms.py b/apps/users/forms.py
index b89b62660efdeaf1ace2b8c1820b7b4e782edf62..a334f7a7e443bdc102e982397e4829c9eb8ce881 100644
--- a/apps/users/forms.py
+++ b/apps/users/forms.py
@@ -114,15 +114,44 @@ class UserPublicKeyForm(forms.Form):
         return self.instance
 
 
-# class UserBulkImportForm(forms.ModelForm):
-#     class Meta:
-#         model = User
-#         fields = ['username', 'email', 'enable_otp', 'role']
-
 class UserBulkUpdateForm(forms.ModelForm):
+    role = forms.ChoiceField(
+        label=_('Role'),
+        choices=[('Admin', 'Administrator'), ('User', 'User')],
+    )
+    users = forms.MultipleChoiceField(
+        required=True,
+        help_text='* required',
+        label=_('Select users'),
+        choices=[(user.id, user.name ) for user in User.objects.all()],
+        widget=forms.SelectMultiple(
+            attrs={
+                'class': 'select2',
+                'data-placeholder': _('Select users')
+            }
+        )
+    )
+
     class Meta:
         model = User
-        fields = ['role', 'groups', 'date_expired', 'is_active', 'enable_otp']
+        fields = ['users', 'role', 'groups', 'date_expired', 'is_active', 'enable_otp']
+        widgets = {
+            'groups': forms.SelectMultiple(
+                attrs={'class': 'select2',
+                       'data-placeholder': _('Select user groups')}),
+        }
+
+    def save(self, commit=True):
+        cleaned_data = {k: v for k, v in self.cleaned_data.items() if
+                        v is not None}
+        users_id = cleaned_data.pop('users')
+        groups = cleaned_data.pop('groups')
+        users = User.objects.filter(id__in=users_id)
+        users.update(**cleaned_data)
+        if groups:
+            for user in users:
+                user.groups.set(groups)
+        return users
 
 
 class UserGroupForm(forms.ModelForm):
diff --git a/apps/users/templates/users/_user_bulk_update_modal.html b/apps/users/templates/users/_user_bulk_update_modal.html
index 8b25a7e0634b39f4286175fb2435d8bd58732b49..c1c5852e6782fea09c54e457a14309de91a8e441 100644
--- a/apps/users/templates/users/_user_bulk_update_modal.html
+++ b/apps/users/templates/users/_user_bulk_update_modal.html
@@ -5,33 +5,68 @@
 {% block modal_class %}modal-lg{% endblock %}
 {% block modal_title%}{% trans "Update selected user" %}{% endblock %}
 {% block modal_body %}
-<div class="ydxbd" id="ydxbd" style="display: block;">
-	<div>
-		<p id="tags_p">
-			<a href="/assets/asset-by-tag/5">
-				<span class="label label-default">三年质保(0)</span>
-			</a>
-            <a href="/assets/asset-by-tag/5">
-                <span class="label label-default">三年质保(0)</span>
-            </a>
-		</p>
-	</div>
+{% block form %}
+<div class="ydxbd" id="formlists" style="display: block;">
+    <p id="tags_p" class="mgl-5 c02">选择需要修改属性</p>
+    <div class="tagBtnList">
+        <a class="label label-primary" id="change_all" value="1">全选</a>
+        {% for field in form %}
+{#            {% if field.name != 'assets' %}#}
+             <a data-id="{{ field.id_for_label }}" class="label label-default label-primary field-tag" value="1">{{ field.label }}</a>
+{#            {% endif %}#}
+        {% endfor %}
+    </div>
 </div>
-<form method="post" class="form-horizontal" action="" id="fm_user_bulk_update">
-{#    {% for field in form %}#}
-{#        <input type="checkbox">#}
-{#        {% bootstrap_field field layout='horizontal' %}#}
-{#    {% endfor %}#}
-    {% bootstrap_form form layout='horizontal' %}
-
-{#    <div class="form-group">#}
-{#        <div class="col-sm-9 col-lg-9 col-sm-offset-2">#}
-{#            <div class="checkbox">#}
-{#                <input type="checkbox" name="enable_otp" checked id="id_enable_otp" disabled><label for="id_enable_otp">{% trans 'Enable-OTP' %}</label>#}
-{#            </div>#}
-{#        </div>#}
-{#    </div>#}
+<form method="post" class="form-horizontal" id="add_form">
+    {% csrf_token %}
+    {% bootstrap_form form layout="horizontal" %}
+    <div class="form-group abc">
+        <div class="col-sm-4 col-sm-offset-2">
+            <button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
+            <button class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
+        </div>
+    </div>
 </form>
 {% endblock %}
-{% block modal_confirm_id %}btn_user_bulk_update{% endblock %}
+{% endblock %}
+{#{% block custom_foot_js %}#}
+{#<script>#}
+{#    $(document).ready(function () {#}
+{#        $('.select2').select2();#}
+{#    }).on('click', '.field-tag', function() {#}
+{#        changeField(this);#}
+{#    }).on('click', '#change_all', function () {#}
+{#        var tag_fields = $('.field-tag');#}
+{#        var $this = $(this);#}
+{#        var active = '1';#}
+{#        if ($this.attr('value') == '0'){#}
+{#            active = '0';#}
+{#            $this.attr('value', '1').addClass('label-primary')#}
+{#        } else {#}
+{#            active = '1';#}
+{#            $this.attr('value', '0').removeClass('label-primary')#}
+{#        }#}
+{#        $.each(tag_fields, function (k, v) {#}
+{#            changeField(v, active)#}
+{#        })#}
+{#    });#}
+{##}
+{#    function changeField(obj, active) {#}
+{#        var $this = $(obj);#}
+{#        var field_id = $this.data('id');#}
+{#        if (!active) {#}
+{#            active = $this.attr('value');#}
+{#        }#}
+{#        if (active == '0') {#}
+{#            $this.attr('value', '1').addClass('label-primary');#}
+{#            var form_groups = $('#add_form .form-group:not(.abc)');#}
+{#            form_groups.filter(':has(#'  + field_id + ')').show().find('select,input').prop('disabled', false)#}
+{#        } else {#}
+{#            $this.attr('value', '0').removeClass('label-primary');#}
+{#            var form_groups = $('#add_form .form-group:not(.abc)');#}
+{#            form_groups.filter(':has(#'  + field_id + ')').hide().find('select,input').prop('disabled', true)#}
+{#        }#}
+{#    }#}
+{#</script>#}
+{#{% endblock %}#}
 
diff --git a/apps/users/templates/users/user_bulk_update.html b/apps/users/templates/users/user_bulk_update.html
new file mode 100644
index 0000000000000000000000000000000000000000..a1ddc6e39fb81fe739f5f458acdd58567e84094e
--- /dev/null
+++ b/apps/users/templates/users/user_bulk_update.html
@@ -0,0 +1,69 @@
+{% extends '_base_create_update.html' %}
+{% load static %}
+{% load bootstrap3 %}
+{% load i18n %}
+
+{% block form %}
+<div class="ydxbd" id="formlists" style="display: block;">
+    <p id="tags_p" class="mgl-5 c02">选择需要修改属性</p>
+    <div class="tagBtnList">
+        <a class="label label-primary" id="change_all" value="1">全选</a>
+        {% for field in form %}
+            {% if field.name != 'users' %}
+             <a data-id="{{ field.id_for_label }}" class="label label-default label-primary field-tag" value="1">{{ field.label }}</a>
+            {% endif %}
+        {% endfor %}
+    </div>
+</div>
+<form method="post" class="form-horizontal" id="add_form">
+    {% csrf_token %}
+    {% bootstrap_form form layout="horizontal" %}
+    <div class="form-group abc">
+        <div class="col-sm-4 col-sm-offset-2">
+            <button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
+            <button class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
+        </div>
+    </div>
+</form>
+{% endblock %}
+
+{% block custom_foot_js %}
+<script>
+    $(document).ready(function () {
+        $('.select2').select2();
+    }).on('click', '.field-tag', function() {
+        changeField(this);
+    }).on('click', '#change_all', function () {
+        var tag_fields = $('.field-tag');
+        var $this = $(this);
+        var active = '1';
+        if ($this.attr('value') == '0'){
+            active = '0';
+            $this.attr('value', '1').addClass('label-primary')
+        } else {
+            active = '1';
+            $this.attr('value', '0').removeClass('label-primary')
+        }
+        $.each(tag_fields, function (k, v) {
+            changeField(v, active)
+        })
+    });
+
+    function changeField(obj, active) {
+        var $this = $(obj);
+        var field_id = $this.data('id');
+        if (!active) {
+            active = $this.attr('value');
+        }
+        if (active == '0') {
+            $this.attr('value', '1').addClass('label-primary');
+            var form_groups = $('#add_form .form-group');
+            form_groups.filter(':has(#'  + field_id + ')').show().find('select,input').prop('disabled', false)
+        } else {
+            $this.attr('value', '0').removeClass('label-primary');
+            var form_groups = $('#add_form .form-group');
+            form_groups.filter(':has(#'  + field_id + ')').hide().find('select,input').prop('disabled', true)
+        }
+    }
+</script>
+{% endblock %}
diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html
index 01bb473f23b565d9636793fa98de73396f949eae..b7fcde2178ab5ae86be48fb5d372edf508ac6289 100644
--- a/apps/users/templates/users/user_list.html
+++ b/apps/users/templates/users/user_list.html
@@ -55,6 +55,22 @@
 {% block custom_foot_js %}
 <script src="{% static 'js/jquery.form.min.js' %}"></script>
 <script>
+function changeField(obj, active) {
+    var $this = $(obj);
+    var field_id = $this.data('id');
+    if (!active) {
+        active = $this.attr('value');
+    }
+    if (active == '0') {
+        $this.attr('value', '1').addClass('label-primary');
+        var form_groups = $('#add_form .form-group:not(.abc)');
+        form_groups.filter(':has(#'  + field_id + ')').show().find('select,input').prop('disabled', false)
+    } else {
+        $this.attr('value', '0').removeClass('label-primary');
+        var form_groups = $('#add_form .form-group:not(.abc)');
+        form_groups.filter(':has(#'  + field_id + ')').hide().find('select,input').prop('disabled', true)
+    }
+}
 
 function renderTable() {
      var options = {
@@ -193,7 +209,9 @@ $(document).ready(function(){
         });
     }
     function doUpdate() {
-        $('#user_bulk_update_modal').modal('show');
+        var users_id = plain_id_list.join(',');
+        var url = "{% url 'users:user-bulk-update' %}?users_id=" + users_id;
+        location.href = url
     }
     switch(action) {
         case 'deactive':
@@ -249,10 +267,9 @@ $(document).ready(function(){
         $('#user_list_table').DataTable().ajax.reload();
         jumpserver.checked = false;
     };
-    console.log(body);
 {#    APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});#}
     $('#user_bulk_update_modal').modal('hide');
-});
+})
 </script>
 {% endblock %}
 
diff --git a/apps/users/urls/views_urls.py b/apps/users/urls/views_urls.py
index a60f3a76477d69cf3a9421bf90a11b8971d74c03..23ab12012bc007ae6f0851cb65160475a6f809a2 100644
--- a/apps/users/urls/views_urls.py
+++ b/apps/users/urls/views_urls.py
@@ -22,14 +22,11 @@ urlpatterns = [
         name='reset-password-success'),
 
     # Profile
-    url(r'^profile/$',
-        views.UserProfileView.as_view(),
+    url(r'^profile/$', views.UserProfileView.as_view(),
         name='user-profile'),
-    url(r'^profile/update/$',
-        views.UserProfileUpdateView.as_view(),
+    url(r'^profile/update/$', views.UserProfileUpdateView.as_view(),
         name='user-profile-update'),
-    url(r'^profile/password/update/$',
-        views.UserPasswordUpdateView.as_view(),
+    url(r'^profile/password/update/$', views.UserPasswordUpdateView.as_view(),
         name='user-password-update'),
     url(r'^profile/pubkey/update/$',
         views.UserPublicKeyUpdateView.as_view(),
@@ -48,37 +45,29 @@ urlpatterns = [
     url(r'^user/(?P<pk>[0-9]+)/assets',
         views.UserGrantedAssetView.as_view(),
         name='user-granted-asset'),
-    url(r'^user/(?P<pk>[0-9]+)/login-history',
-        views.UserDetailView.as_view(),
+    url(r'^user/(?P<pk>[0-9]+)/login-history', views.UserDetailView.as_view(),
         name='user-login-history'),
-    url(r'^user/export/',
-        views.UserExportView.as_view(),
+    url(r'^user/export/', views.UserExportView.as_view(),
         name='user-export'),
-    url(r'^first-login/$',
-        views.UserFirstLoginView.as_view(),
+    url(r'^first-login/$', views.UserFirstLoginView.as_view(),
         name='user-first-login'),
-    url(r'^user/import/$',
-        views.UserBulkImportView.as_view(),
+    url(r'^user/import/$', views.UserBulkImportView.as_view(),
         name='user-import'),
-    url(r'^user/create$',
-        views.UserCreateView.as_view(),
+    url(r'^user/create$', views.UserCreateView.as_view(),
         name='user-create'),
-    url(r'^user/(?P<pk>[0-9]+)/update$',
-        views.UserUpdateView.as_view(),
+    url(r'^user/(?P<pk>[0-9]+)/update$', views.UserUpdateView.as_view(),
         name='user-update'),
+    url(r'^user/update$', views.UserBulkUpdateView.as_view(),
+        name='user-bulk-update'),
 
     # User group view
-    url(r'^user-group$',
-        views.UserGroupListView.as_view(),
+    url(r'^user-group$', views.UserGroupListView.as_view(),
         name='user-group-list'),
-    url(r'^user-group/(?P<pk>[0-9]+)$',
-        views.UserGroupDetailView.as_view(),
+    url(r'^user-group/(?P<pk>[0-9]+)$', views.UserGroupDetailView.as_view(),
         name='user-group-detail'),
-    url(r'^user-group/create$',
-        views.UserGroupCreateView.as_view(),
+    url(r'^user-group/create$', views.UserGroupCreateView.as_view(),
         name='user-group-create'),
-    url(r'^user-group/(?P<pk>[0-9]+)/update$',
-        views.UserGroupUpdateView.as_view(),
+    url(r'^user-group/(?P<pk>[0-9]+)/update$', views.UserGroupUpdateView.as_view(),
         name='user-group-update'),
     url(r'^user-group/(?P<pk>[0-9]+)/asset-permission$',
         views.UserGroupAssetPermissionView.as_view(),
diff --git a/apps/users/views/user.py b/apps/users/views/user.py
index 7a09353be74920d84267549ef3639001479cd321..9a7ab4d3c2b5cca747db20e84c6dcefe673fd20c 100644
--- a/apps/users/views/user.py
+++ b/apps/users/views/user.py
@@ -38,7 +38,7 @@ __all__ = ['UserListView', 'UserCreateView', 'UserDetailView',
            'UserAssetPermissionView', 'UserGrantedAssetView',
            'UserExportView',  'UserBulkImportView', 'UserProfileView',
            'UserProfileUpdateView', 'UserPasswordUpdateView',
-           'UserPublicKeyUpdateView',
+           'UserPublicKeyUpdateView', 'UserBulkUpdateView',
            ]
 
 logger = get_logger(__name__)
@@ -107,6 +107,46 @@ class UserUpdateView(AdminUserRequiredMixin, UpdateView):
         return context
 
 
+class UserBulkUpdateView(AdminUserRequiredMixin, ListView):
+    model = User
+    form_class = forms.UserBulkUpdateForm
+    template_name = 'users/user_bulk_update.html'
+    success_url = reverse_lazy('users:user-list')
+
+    def get(self, request, *args, **kwargs):
+        users_id = self.request.GET.get('users_id', '')
+        self.id_list = [int(i) for i in users_id.split(',') if i.isdigit()]
+
+        if kwargs.get('form'):
+            self.form = kwargs['form']
+        elif users_id:
+            self.form = self.form_class(
+                initial={'users': self.id_list}
+            )
+        else:
+            self.form = self.form_class()
+        return super(UserBulkUpdateView, self).get(request, *args, **kwargs)
+
+    def post(self, request, *args, **kwargs):
+        form = self.form_class(request.POST)
+        if form.is_valid():
+            form.save()
+            return redirect(self.success_url)
+        else:
+            return self.get(request, form=form, *args, **kwargs)
+
+    def get_context_data(self, **kwargs):
+        context = {
+            'app': 'Assets',
+            'action': 'Bulk update asset',
+            'form': self.form,
+            'users_selected': self.id_list,
+            'users': User.objects.all(),
+        }
+        kwargs.update(context)
+        return super(UserBulkUpdateView, self).get_context_data(**kwargs)
+
+
 class UserDetailView(AdminUserRequiredMixin, DetailView):
     model = User
     template_name = 'users/user_detail.html'