Python Web开发学习

【CRM客户关系管理】18.多选字段filter_horizon

2018-12-23  本文已影响3人  吾星喵

js实现多选框数据移动

js实现左右多选框移动数据

为左边的select增加一个id="id_{{ field.name }}_from",为右边的select增加一个id="id_{{ field.name }}_to"

为左边的option增加一个双击事件ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_to')",为右边的option增加一个双击事件ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_from')"

查看编辑表单的客户姓名这一栏

<input type="text" name="name" value="测试" maxlength="50" class="form-control" required="" id="id_name">

以上{{ field }}自动生成了id和name等

所以需要在右边的多选框增加name字段:name="{{ field.name }}">

<div class="col-sm-10">
    {% if field.name in admin_class.filter_horizontal %}
        <!--字段名在多选的字段中,使用多选框-->
        <div class="col-md-5">
            <select multiple class="form-control" id="id_{{ field.name }}_from">
                {% get_available_m2m_data field.name form_obj admin_class as vailable_m2m_data %}
                {% for obj in vailable_m2m_data %}
                    <option value="{{ obj.id }}" ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_to')">{{ obj }}</option>
                {% endfor %}
            </select>
        </div>
        <div class="col-md-5">
            <select multiple class="form-control" id="id_{{ field.name }}_to" name="{{ field.name }}">
                {% get_selected_m2m_data field.name form_obj admin_class as selected_m2m_data %}
                {% for obj in selected_m2m_data %}
                    <option value="{{ obj.id }}" ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_from')">{{ obj }}</option>
                {% endfor %}
            </select>
        </div>
    {% else %}
        {{ field }}
    {% endif %}
    <span style="color: red">{{ field.errors.0 }}</span>
</div>

然后再{% endblock %}块前增加js用于多选框移除移入

<script>
    function MoveSelectedOption(ele, target_id) {
        let new_target_id = $(ele).parent().attr('id');
        let option = "<option value='" + $(ele).val() +"'ondblclick=MoveSelectedOption(this,'"+ new_target_id +"') >" + $(ele).text() +"</option>";
        //将双击的元素添加到另一方
        $("#"+ target_id).append(option);
        //移除本方的元素
        $(ele).remove();
    }
</script>

当双击咨询课程左边多选框内的数据,就会移动到右边多选框,当双击右边多选框内的数据,就会移动到左边多选框。

访问 http://127.0.0.1:8000/djadmin/crm/customerinfo/1/change/ ,修改咨询课程,然后提交保存,但会出现以下提示

image.png

说明name="{{ field.name }}">所在的select中的option没有一个为选中状态

js实现提交表单selected已选择数据

修改form表单提交的验证函数调用<form class="form-horizontal" method="post" onsubmit="VerificationBeforeFormSubmit();">。。。</form

定义一个提交选中右方所有选项的js函数

<script>
    function VerificationBeforeFormSubmit() {
        $("select[myflag] option").prop('selected', true);
    }
</script>

由于这个全选没有一个id或者是class找到右方的多选框,所以自定义一个myflag="selected_m2m"

<div class="col-md-5">
    <select myflag="selected_m2m" multiple class="form-control" id="id_{{ field.name }}_to" name="{{ field.name }}">
        {% get_selected_m2m_data field.name form_obj admin_class as selected_m2m_data %}
        {% for obj in selected_m2m_data %}
            <option value="{{ obj.id }}" ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_from')">{{ obj }}</option>
        {% endfor %}
    </select>
</div>

现在提交时就会把右方的多选框全选,进行提交。

如果不自定义,如下

<script>
    function VerificationBeforeFormSubmit() {
        $("select option").prop('selected', true);
    }
</script>

当表单提交时,所有的选框select--option都会被选中!这就回造成如果有单选框是不能保存。

最终table_edit.html内容是

{% extends 'djadmin/base.html' %}

{% load djadmin_tags %}

{% block title %}
    数据表编辑 - 后台管理
{% endblock %}

{% block content %}
    <h1 class="page-header">{{ app_name }} - {{ model_name }} - 编辑 &nbsp;&nbsp; {{ obj }} </h1>
    <!--<div>{{ form_obj }}</div>-->
    <form class="form-horizontal" method="post" onsubmit="VerificationBeforeFormSubmit();">
        {{ form_obj.errors }}

        {% for field in form_obj %}
            <div class="form-group">
                <label class="col-sm-2 control-label">{{ field.label }}</label>
                <div class="col-sm-10">
                    {% if field.name in admin_class.filter_horizontal %}
                        <!--字段名在多选的字段中,使用多选框-->
                        <div class="col-md-5">
                            <select multiple class="form-control" id="id_{{ field.name }}_from">
                                {% get_available_m2m_data field.name form_obj admin_class as vailable_m2m_data %}
                                {% for obj in vailable_m2m_data %}
                                    <option value="{{ obj.id }}" ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_to')">{{ obj }}</option>
                                {% endfor %}
                            </select>
                        </div>
                        <div class="col-md-5">
                            <select myflag="selected_m2m" multiple class="form-control" id="id_{{ field.name }}_to" name="{{ field.name }}">
                                {% get_selected_m2m_data field.name form_obj admin_class as selected_m2m_data %}
                                {% for obj in selected_m2m_data %}
                                    <option value="{{ obj.id }}" ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_from')">{{ obj }}</option>
                                {% endfor %}
                            </select>
                        </div>
                    {% else %}
                        {{ field }}
                    {% endif %}
                    <span style="color: red">{{ field.errors.0 }}</span>
                </div>
            </div>
        {% endfor %}
        {% csrf_token %}

        {% if not admin_class.form_add %}
            {# 当修改数据时,才显示下方只读字段,当增加数据时,下方则不会显示 #}
            {% for field in admin_class.readonly_fields %}
                <label class="col-sm-2 control-label">{{ field }}</label>
                <div class="col-sm-10">
                    <p>{% get_obj_field_val form_obj field %}</p>
                </div>
            {% endfor %}
        {% endif %}

        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <button type="submit" class="btn btn-primary">提交</button>
            </div>
        </div>
    </form>

    <script>
        function MoveSelectedOption(ele, target_id) {
            let new_target_id = $(ele).parent().attr('id');
            let option = "<option value='" + $(ele).val() +"'ondblclick=MoveSelectedOption(this,'"+ new_target_id +"') >" + $(ele).text() +"</option>";
            //将双击的元素添加到另一方
            $("#"+ target_id).append(option);
            //移除本方的元素
            $(ele).remove();
        }

        function VerificationBeforeFormSubmit() {
            //提交表单时选中所有的select
            $("select[myflag] option").prop('selected', true);
        }
    </script>
{% endblock %}
image.png

解决添加数据报错问题

现在更新数据就可以用了。但添加数据会报错。

错误提示是ValueError: "<CustomerInfo: >" needs to have a value for field "id" before this many-to-many relationship can be used.查看报错位置是模板标签中的selected_data = getattr(form_obj.instance, field_name).all(),当修改数据,获取所有的已选择数据,当添加数据时,已选择为空,这儿就会报错,暂时使用try来捕获该异常,修改下面的模板标签

@register.simple_tag
def get_available_m2m_data(field_name, form_obj, admin_class):
    """获取多对多字段关联表的所有数据"""
    field_obj = admin_class.model._meta.get_field(field_name)  # 获取字段对象

    # consult_courses = models.ManyToManyField(Course, verbose_name='咨询课程')  # 多对多关联课程
    # 这是一个多对多字段,通过consult_courses对象获取到Course,也就是获取到所有咨询的课程
    obj_list = field_obj.related_model.objects.all()

    obj_list = set(obj_list)  # 所有咨询课程的集合
    try:
        selected_data = set(getattr(form_obj.instance, field_name).all())  # 所以已选中课程的即可
    except:
        selected_data = set([])
    # 通过集合求差集,得到未选中的自选课程,填充到左边多选框中
    return obj_list - selected_data


@register.simple_tag
def get_selected_m2m_data(field_name, form_obj, admin_class):
    """获取已选中的多对多数据"""
    try:
        selected_data = getattr(form_obj.instance, field_name).all()
    except:
        selected_data = set([])
    # print(selected_data)

    return selected_data

现在访问 http://127.0.0.1:8000/djadmin/crm/customerinfo/add/ 不会报错,并且可以正常添加数据

image.png

添加全选和删除全部功能

image.png

修改table_edit.html增加a标签和js函数,用于选择全部,以及移除选择功能

{% if field.name in admin_class.filter_horizontal %}
    <!--字段名在多选的字段中,使用多选框-->
    <div class="col-md-5">
        <select multiple class="form-control" id="id_{{ field.name }}_from">
            {% get_available_m2m_data field.name form_obj admin_class as vailable_m2m_data %}
            {% for obj in vailable_m2m_data %}
                <option value="{{ obj.id }}" ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_to')">{{ obj }}</option>
            {% endfor %}
        </select>
        <p><a onclick="MoveAllElements('id_{{ field.name }}_from', 'id_{{ field.name }}_to')">全选></a></p>
    </div>
    <div class="col-md-5">
        <select myflag="selected_m2m" multiple class="form-control" id="id_{{ field.name }}_to" name="{{ field.name }}">
            {% get_selected_m2m_data field.name form_obj admin_class as selected_m2m_data %}
            {% for obj in selected_m2m_data %}
                <option value="{{ obj.id }}" ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_from')">{{ obj }}</option>
            {% endfor %}
        </select>
        <p><a onclick="MoveAllElements('id_{{ field.name }}_to', 'id_{{ field.name }}_from')"><删除全部</a></p>
    </div>
{% else %}
    {{ field }}
{% endif %}

添加js函数实现该功能

<script>
    function MoveAllElements(from_id, to_id) {
        console.log($("#" + from_id).children())
        $("#" + from_id).children().each(function () {
            MoveSelectedOption(this, to_id);
        })
    }
</script>

访问 http://127.0.0.1:8000/djadmin/crm/customerinfo/1/change/

点击删除全部,就会把所有已到左方

image.png

点击全选就会把所有移动到右方

image.png

添加数据页面功能也正常

多框框过滤功能

类似于Django Admin的选择框所有

创建搜索框

在左方多选框上增加一个type="search"的input标签

<div class="col-md-5">
    <input type="search" class="form-control" oninput="FuzzySearch(this)" placeholder="模糊搜索">
    <select multiple class="form-control" id="id_{{ field.name }}_from">
        {% get_available_m2m_data field.name form_obj admin_class as vailable_m2m_data %}
        {% for obj in vailable_m2m_data %}
            <option value="{{ obj.id }}" ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_to')">{{ obj }}</option>
        {% endfor %}
    </select>
    <p><a onclick="MoveAllElements('id_{{ field.name }}_from', 'id_{{ field.name }}_to')">全选></a></p>
</div>

js实现搜索过滤

<script>
    function FuzzySearch(ele) {
        //过滤多选框中的数据
        console.log($(ele).val());
        let search_text = $(ele).val().toUpperCase();
        $(ele).next().children().each(function () {
            if ($(this).text().toUpperCase().search(search_text) !== -1) {
                $(this).show();
            } else {
                $(this).hide();
            }
        })
    }
</script>

现在在输入框中输入值就会过滤下方的多选框内容。

image.png image.png

最终table_edit.html代码如下

{% extends 'djadmin/base.html' %}

{% load djadmin_tags %}

{% block title %}
    数据表编辑 - 后台管理
{% endblock %}

{% block content %}
    <h1 class="page-header">{{ app_name }} - {{ model_name }} - 编辑 &nbsp;&nbsp; {{ obj }} </h1>
    <!--<div>{{ form_obj }}</div>-->
    <form class="form-horizontal" method="post" onsubmit="VerificationBeforeFormSubmit();">
        {{ form_obj.errors }}

        {% for field in form_obj %}
            <div class="form-group">
                <label class="col-sm-2 control-label">{{ field.label }}</label>
                <div class="col-sm-10">
                    {% if field.name in admin_class.filter_horizontal %}
                        <!--字段名在多选的字段中,使用多选框-->
                        <div class="col-md-5">
                            <input type="search" class="form-control" oninput="FuzzySearch(this)" placeholder="模糊搜索">
                            <select multiple class="form-control" id="id_{{ field.name }}_from">
                                {% get_available_m2m_data field.name form_obj admin_class as vailable_m2m_data %}
                                {% for obj in vailable_m2m_data %}
                                    <option value="{{ obj.id }}" ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_to')">{{ obj }}</option>
                                {% endfor %}
                            </select>
                            <p><a onclick="MoveAllElements('id_{{ field.name }}_from', 'id_{{ field.name }}_to')">全选></a></p>
                        </div>
                        <div class="col-md-5">
                            <select myflag="selected_m2m" multiple class="form-control" id="id_{{ field.name }}_to" name="{{ field.name }}">
                                {% get_selected_m2m_data field.name form_obj admin_class as selected_m2m_data %}
                                {% for obj in selected_m2m_data %}
                                    <option value="{{ obj.id }}" ondblclick="MoveSelectedOption(this,'id_{{ field.name }}_from')">{{ obj }}</option>
                                {% endfor %}
                            </select>
                            <p><a onclick="MoveAllElements('id_{{ field.name }}_to', 'id_{{ field.name }}_from')">
                                <删除全部
                            </a></p>
                        </div>
                    {% else %}
                        {{ field }}
                    {% endif %}
                    <span style="color: red">{{ field.errors.0 }}</span>
                </div>
            </div>
        {% endfor %}
        {% csrf_token %}

        {% if not admin_class.form_add %}
            {# 当修改数据时,才显示下方只读字段,当增加数据时,下方则不会显示 #}
            {% for field in admin_class.readonly_fields %}
                <label class="col-sm-2 control-label">{{ field }}</label>
                <div class="col-sm-10">
                    <p>{% get_obj_field_val form_obj field %}</p>
                </div>
            {% endfor %}
        {% endif %}

        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <button type="submit" class="btn btn-primary">提交</button>
            </div>
        </div>
    </form>

    <script>
        function MoveSelectedOption(ele, target_id) {
            let new_target_id = $(ele).parent().attr('id');
            let option = "<option value='" + $(ele).val() + "'ondblclick=MoveSelectedOption(this,'" + new_target_id + "') >" + $(ele).text() + "</option>";
            //将双击的元素添加到另一方
            $("#" + target_id).append(option);
            //移除本方的元素
            $(ele).remove();
        }

        function VerificationBeforeFormSubmit() {
            //提交表单时选中所有的select
            $("select option").prop('selected', true);
        }

        function MoveAllElements(from_id, to_id) {
            console.log($("#" + from_id).children())
            $("#" + from_id).children().each(function () {
                MoveSelectedOption(this, to_id);
            })
        }

        function FuzzySearch(ele) {
            //过滤多选框中的数据
            console.log($(ele).val());
            let search_text = $(ele).val().toUpperCase();
            $(ele).next().children().each(function () {
                if ($(this).text().toUpperCase().search(search_text) !== -1) {
                    $(this).show();
                } else {
                    $(this).hide();
                }
            })
        }
    </script>
{% endblock %}
上一篇 下一篇

猜你喜欢

热点阅读