Django-客户管理模块(5)前端优化
Django-客户管理模块(5)前端优化

Django-客户管理模块(5)前端优化

看了下Javascript相关的代码,感觉像是把一部分的逻辑处理放到了前端里面。问了下Gemini,发现这个确实可以写后端,Node.js就是。不过说回我们这个代码,为什么增删改查一类的不能直接放到python代码里,而是需要Javascript在前端处理?这就是上一章最后说到的问题,那就是如果把这些功能完全放到后端,那么每次运行一个简单的功能,整个网页都会重新载入,很费劲,而Javascript可以在不刷新页面的情况下,局部处理一些简单的功能。

因为之前我们在后端已经写好了客户的增删改查,所以这一章主要是前端Javascript相关的编写。因为不是学习重点,简单看了下了解下就算了。

{% extends "customer/base.html" %}

{% block title %}我的客户 - 喵喵CRM{% endblock %}

{% block content %}
<style>
    /* --- Modal 样式 --- */
    /* 弹窗背景遮罩 */
    .modal-overlay {
        display: none; /* 默认隐藏 */
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.5); /* 半透明黑色背景 */
        justify-content: center;
        align-items: center;
        z-index: 1000;
    }

    /* 弹窗内容区域 */
    .modal-content {
        background-color: white;
        padding: 25px;
        border-radius: 8px;
        box-shadow: 0 4px 10px rgba(0,0,0,0.2);
        width: 400px;
        max-width: 90%;
    }

    .modal-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 20px;
        border-bottom: 1px solid #eee;
        padding-bottom: 15px;
    }

    .modal-header h3 {
        margin: 0;
    }

    /* 关闭按钮 */
    .close-btn {
        background: none;
        border: none;
        font-size: 24px;
        cursor: pointer;
        color: #888;
    }

    /* 表单样式 */
    .modal-form-group {
        margin-bottom: 15px;
    }

    .modal-form-group label {
        display: block;
        margin-bottom: 5px;
        font-weight: bold;
    }

    .modal-form-group input {
        width: 100%;
        padding: 8px;
        border: 1px solid #ccc;
        border-radius: 4px;
        box-sizing: border-box; /* 确保 padding 不会影响宽度 */
    }

    .modal-actions {
        text-align: right;
        margin-top: 20px;
    }

    .action-btn {
        padding: 10px 15px;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        margin-left: 10px;
    }

    .save-btn {
        background-color: #28a745;
        color: white;
    }
    .cancel-btn {
        background-color: #6c757d;
        color: white;
    }

    /* --- 表格按钮样式 --- */
    .table-action-btn {
        padding: 5px 10px;
        border: 1px solid #ddd;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        margin-right: 5px;
    }
    .edit-btn {
        background-color: #ffc107;
        color: #333;
    }
    .delete-btn {
        background-color: #dc3545;
        color: white;
    }
</style>

<div style="max-width: 900px; margin: 0 auto; padding: 20px;">
    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
        <h2 style="margin: 0;">我的客户列表</h2>
        <button id="add-customer-btn" style="padding: 10px 15px; background-color: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px;">
            &#43; 新增客户
        </button>
    </div>

    <table style="width: 100%; border-collapse: collapse;">
        <thead style="background-color: #333; color: white;">
            <tr>
                <th style="padding: 12px; text-align: left;">姓名</th>
                <th style="padding: 12px; text-align: left;">邮箱</th>
                <th style="padding: 12px; text-align: left;">电话</th>
                <th style="padding: 12px; text-align: left;">地址</th>
                <th style="padding: 12px; text-align: left;">负责人</th>
                <th style="padding: 12px; text-align: left; width: 120px;">操作</th>
            </tr>
        </thead>
        <tbody id="customer-table-body">
            {% for customer in customers %}
                <tr style="border-bottom: 1px solid #ddd;" data-id="{{ customer.id }}">
                    <td style="padding: 12px;" data-field="name">{{ customer.name }}</td>
                    <td style="padding: 12px;" data-field="email">{{ customer.email|default:'--' }}</td>
                    <td style="padding: 12px;" data-field="phone">{{ customer.phone|default:'--' }}</td>
                    <td style="padding: 12px;" data-field="address">{{ customer.address|default:'--' }}</td>
                    <td style="padding: 12px;" data-field="owner">{{ customer.owner }}</td>
                    <td style="padding: 12px;">
                        <button class="table-action-btn edit-btn">编辑</button>
                        <button class="table-action-btn delete-btn">删除</button>
                    </td>
                </tr>
            {% empty %}
                <tr id="empty-row">
                    <td colspan="5" style="text-align: center; padding: 20px; color: #777;">
                        您还没有客户,快去添加第一个客户吧!
                    </td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
</div>

<div id="customer-modal" class="modal-overlay">
    <div class="modal-content">
        <div class="modal-header">
            <h3 id="modal-title">新增客户</h3>
            <button id="close-modal-btn" class="close-btn">&times;</button>
        </div>
        <form id="modal-form">
            <input type="hidden" id="customer-id-input">
            <div class="modal-form-group">
                <label for="name-input">姓名</label>
                <input type="text" id="name-input" required>
            </div>
            <div class="modal-form-group">
                <label for="phone-input">电话 (可选)</label>
                <input type="tel" id="phone-input">
            </div>
            <div class="modal-form-group">
                <label for="email-input">邮箱 (可选)</label>
                <input type="email" id="email-input">
            </div>
            <div class="modal-form-group">
                <label for="address-input">地址 (可选)</label>
                <input type="text" id="address-input">
            </div>
            <div class="modal-actions">
                <button type="button" class="action-btn cancel-btn">取消</button>
                <button type="submit" class="action-btn save-btn">保存</button>
            </div>
        </form>
    </div>
</div>

<script>
document.addEventListener('DOMContentLoaded', function() {
    // --- CSRF Token 获取函数 ---
    function getCookie(name) {
        let cookieValue = null;
        if (document.cookie && document.cookie !== '') {
            const cookies = document.cookie.split(';');
            for (let i = 0; i < cookies.length; i++) {
                const cookie = cookies[i].trim();
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    const csrftoken = getCookie('csrftoken');

    // --- 获取所有需要的DOM元素 ---
    const addBtn = document.getElementById('add-customer-btn');
    const customerTableBody = document.getElementById('customer-table-body');
    const modal = document.getElementById('customer-modal');
    const modalForm = document.getElementById('modal-form');
    const modalTitle = document.getElementById('modal-title');
    const customerIdInput = document.getElementById('customer-id-input');
    const nameInput = document.getElementById('name-input');
    const phoneInput = document.getElementById('phone-input');
    const emailInput = document.getElementById('email-input');
    const addressInput = document.getElementById('address-input');

    // --- 弹窗控制函数 ---
    function openModal() {
        modal.style.display = 'flex';
    }

    function closeModal() {
        modal.style.display = 'none';
        modalForm.reset(); // 清空表单
        customerIdInput.value = ''; // 清空隐藏的ID
    }

    // 弹窗关闭按钮 和 取消按钮
    modal.querySelector('#close-modal-btn').addEventListener('click', closeModal);
    modal.querySelector('.cancel-btn').addEventListener('click', closeModal);

    // 点击遮罩区域关闭弹窗
    modal.addEventListener('click', function(event) {
        if (event.target === modal) {
            closeModal();
        }
    });

    // --- 事件监听:点击“新增客户”按钮 ---
    addBtn.addEventListener('click', function() {
        modalTitle.textContent = '新增客户';
        openModal();
    });

    // --- 表单提交处理(新增或编辑)---
    modalForm.addEventListener('submit', function(event) {
        event.preventDefault(); // 阻止表单默认的刷新页面的提交行为

        const customerId = customerIdInput.value;
        const isEditing = !!customerId; // 如果customerId有值,则为编辑模式

        const url = isEditing ? `/api/customers/${customerId}/` : '/api/customers/';
        const method = isEditing ? 'PATCH' : 'POST';

        const formData = {
            name: nameInput.value,
            phone: phoneInput.value,
            email: emailInput.value,
            address: addressInput.value,
        };

        fetch(url, {
            method: method,
            headers: {
                'Content-Type': 'application/json',
                'X-CSRFToken': csrftoken,
            },
            body: JSON.stringify(formData),
        })
        .then(response => {
            if (!response.ok) throw new Error('请求失败');
            return response.json();
        })
        .then(data => {
            if (isEditing) {
                // 更新表格中的行
                const row = customerTableBody.querySelector(`tr[data-id='${customerId}']`);
                row.querySelector('[data-field="name"]').textContent = data.name;
                row.querySelector('[data-field="email"]').textContent = data.email || '--';
                row.querySelector('[data-field="address"]').textContent = data.address || '--';
                row.querySelector('[data-field="phone"]').textContent = data.phone || '--';
                alert('客户更新成功!');
            } else {
                // 在表格中添加新行
                const emptyRow = document.getElementById('empty-row');
                if(emptyRow) emptyRow.remove(); // 如果存在“空数据”提示,先移除

                const newRowHtml = `
                    <tr style="border-bottom: 1px solid #ddd;" data-id="${data.id}">
                        <td style="padding: 12px;" data-field="name">${data.name}</td>
                        <td style="padding: 12px;" data-field="email">${data.email || '--'}</td>
                        <td style="padding: 12px;" data-field="phone">${data.phone || '--'}</td>
                        <td style="padding: 12px;" data-field="address">${data.address || '--'}</td>
                        <td style="padding: 12px;" data-field="owner">${data.owner}</td>
                        <td style="padding: 12px;">
                            <button class="table-action-btn edit-btn">编辑</button>
                            <button class="table-action-btn delete-btn">删除</button>
                        </td>
                    </tr>
                `;
                customerTableBody.insertAdjacentHTML('beforeend', newRowHtml);
                alert('客户添加成功!');
            }
            closeModal();
        })
        .catch(error => {
            console.error('操作失败:', error);
            alert('操作失败,请检查控制台输出。');
        });
    });

    // --- 事件委托:处理表格中的点击事件 ---
    customerTableBody.addEventListener('click', function(event) {
        const target = event.target;
        const customerRow = target.closest('tr');
        if (!customerRow || !customerRow.dataset.id) return;

        const customerId = customerRow.dataset.id;

        // --- 处理“编辑”逻辑 ---
        if (target.classList.contains('edit-btn')) {
            modalTitle.textContent = '编辑客户';

            // 从表格行中获取当前数据并填充到表单
            customerIdInput.value = customerId;
            nameInput.value = customerRow.querySelector('[data-field="name"]').textContent;
            phoneInput.value = customerRow.querySelector('[data-field="phone"]').textContent;
            emailInput.value = customerRow.querySelector('[data-field="email"]').textContent.replace('--', '');
            addressInput.value = customerRow.querySelector('[data-field="address"]').textContent;
            openModal()
        }

        // --- 处理“删除”逻辑 ---
        if (target.classList.contains('delete-btn')) {
            if (confirm('确定要删除这位客户吗?')) {
                fetch(`/api/customers/${customerId}/`, {
                    method: 'DELETE',
                    headers: { 'X-CSRFToken': csrftoken },
                })
                .then(response => {
                    if (response.ok) {
                        customerRow.remove();
                        alert('删除成功!');
                        if (customerTableBody.getElementsByTagName('tr').length === 0) {
                            const emptyRowHtml = `<tr id="empty-row"><td colspan="5" style="text-align: center; padding: 20px; color: #777;">您还没有客户,快去添加第一个客户吧!</td></tr>`;
                            customerTableBody.innerHTML = emptyRowHtml;
                        }
                    } else {
                        alert('删除失败!');
                    }
                })
                .catch(error => console.error('删除请求失败:', error));
            }
        }
    });
});
</script>
{% endblock %}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注