You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP MySQL三级联动下拉框异常:选服务类别显示物料物品

三级联动下拉框故障排查与修复

问题场景

基于PHP+MySQL开发三级联动下拉框,包含采购类型(typeAchat)、类别(categorie)、物品/服务(article):

  • 采购类型分「物料(materiel)」和「服务(service)」两个选项
  • 选「物料」应显示物料类别,选「服务」应显示服务类别
  • 物料类别能正确加载对应物品,但服务类别会错误加载物料物品

相关代码

主页面HTML

<!DOCTYPE html>
<html lang="fr">

<head>
    <meta charset="UTF-8">
    <title>新采购申请</title>
</head>

<body>
    <h2>新采购申请</h2>

    <form id="achatForm">
        <div class="form-group">
            <label for="typeAchat">采购类型:</label>
            <select class="form-control" id="typeAchat">
                <option value="">请选择采购类型</option>
                <?php
                // 连接到数据库'dac'
                $conn = mysqli_connect("localhost", "casablanca", "casablanca", "dac");

                // 检查连接
                if (!$conn) {
                    die("连接失败: " . mysqli_connect_error());
                }

                // 获取采购类型的查询语句
                $queryTypeAchat = "SELECT * FROM type_achat";
                $resultTypeAchat = mysqli_query($conn, $queryTypeAchat);

                while ($row = mysqli_fetch_assoc($resultTypeAchat)) {
                    echo '<option value="' . $row['id_type_achat'] . '">' . $row['nom_type_achat'] . '</option>';
                }

                // 关闭连接
                mysqli_close($conn);
                ?>
            </select>
        </div>

        <div class="form-group" id="categoriesGroup" style="display: none;">
            <label for="categorieSelect">类别:</label>
            <select class="form-control" id="categorieSelect"></select>
        </div>

        <div class="form-group" id="articlesGroup" style="display: none;">
            <label for="articleSelect">物品或服务:</label>
            <select class="form-control" id="articleSelect"></select>
        </div>

        <input type="submit" value="提交">
    </form>

    <script>
        document.getElementById('typeAchat').addEventListener('change', function() {
            var typeAchat = this.value;
            if (typeAchat !== '') {
                document.getElementById('categoriesGroup').style.display = 'block';

                // AJAX请求加载对应类别
                var xhr = new XMLHttpRequest();
                xhr.open('POST', 'load_categories.php', true);
                xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
                xhr.onload = function() {
                    if (xhr.status === 200) {
                        document.getElementById('categorieSelect').innerHTML = xhr.responseText;
                        document.getElementById('articlesGroup').style.display = 'none';
                    } else {
                        console.error('请求失败,状态码: ' + xhr.status);
                    }
                };
                xhr.send('typeAchat=' + typeAchat);
            } else {
                document.getElementById('categoriesGroup').style.display = 'none';
                document.getElementById('articlesGroup').style.display = 'none';
            }
        });

        document.getElementById('categorieSelect').addEventListener('change', function() {
            var categorieId = this.value;
            if (categorieId !== '') {
                // AJAX请求加载对应物品
                var xhr = new XMLHttpRequest();
                xhr.open('POST', 'load_articles.php', true);
                xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
                xhr.onload = function() {
                    if (xhr.status === 200) {
                        document.getElementById('articleSelect').innerHTML = xhr.responseText;
                        document.getElementById('articlesGroup').style.display = 'block';
                    } else {
                        console.error('请求失败,状态码: ' + xhr.status);
                    }
                };
                xhr.send('categorieId=' + categorieId);
            } else {
                document.getElementById('articlesGroup').style.display = 'none';
            }
        });
    </script>
</body>

</html>

load_categories.php

<?php
// 连接到数据库'dac'
$conn = mysqli_connect("localhost", "casablanca", "casablanca", "dac");

if (!$conn) {
    die("连接失败: " . mysqli_connect_error());
}

if (isset($_POST['typeAchat'])) {
    $typeAchat = $_POST['typeAchat'];
    
    // 根据采购类型(物料/服务)查询对应类别
    $query = "SELECT * FROM categorie_materiels WHERE id_type_achat = $typeAchat";
    $result = mysqli_query($conn, $query);

    $options = '<option value="">请选择类别</option>';
    while ($row = mysqli_fetch_assoc($result)) {
        $options .= '<option value="' . $row['id_categorie_materiels'] . '">' . $row['nom_categorie_materiels'] . '</option>';
    }

    // 根据采购类型添加服务类别
    $queryService = "SELECT * FROM categorie_services WHERE id_type_achat = $typeAchat";
    $resultService = mysqli_query($conn, $queryService);

    while ($row = mysqli_fetch_assoc($resultService)) {
        $options .= '<option value="' . $row['id_categorie_services'] . '">' . $row['nom_categorie_services'] . '</option>';
    }

    echo $options;
}

mysqli_close($conn);
?>

load_articles.php

<?php
// 连接到数据库'dac'
$conn = mysqli_connect("localhost", "casablanca", "casablanca", "dac");

if (!$conn) {
    die("连接失败: " . mysqli_connect_error());
}

if (isset($_POST['categorieId'])) {
    $categorieId = $_POST['categorieId'];

    // 检查该类别是否属于物料类别
    $queryMaterial = "SELECT * FROM categorie_materiels WHERE id_categorie_materiels = $categorieId";
    $resultMaterial = mysqli_query($conn, $queryMaterial);

    if (mysqli_num_rows($resultMaterial) > 0) {
        // 查询该物料类别下的物品
        $query = "SELECT * FROM materiel WHERE id_categorie_materiel = $categorieId";
        $result = mysqli_query($conn, $query);

        $options = '<option value="">请选择物品</option>';
        while ($row = mysqli_fetch_assoc($result)) {
            $options .= '<option value="' . $row['id_materiel'] . '">' . $row['nom_materiel'] . '</option>';
        }
    } else {
        // 查询该服务类别下的服务
        $queryService = "SELECT * FROM service WHERE id_categorie_services = $categorieId";
        $resultService = mysqli_query($conn, $queryService);

        $options = '<option value="">请选择服务</option>';
        while ($row = mysqli_fetch_assoc($resultService)) {
            $options .= '<option value="' . $row['id_service'] . '">' . $row['nom_service'] . '</option>';
        }
    }

    echo $options;
}

mysqli_close($conn);
?>

问题原因

  1. 类别ID冲突:categorie_materiels和categorie_services两个表的ID是独立自增的,极易出现相同ID值(比如物料类别ID=1,服务类别也有ID=1)。
  2. 判断逻辑缺陷:load_articles.php中优先检查物料类别表,若存在匹配ID就加载物料物品。当服务类别ID与物料类别ID重复时,会误判为物料类别,导致加载错误数据。
  3. 类别加载逻辑冗余:load_categories.php中不管选择哪种采购类型,都会同时加载两个表的类别数据,既不符合需求,也加剧了ID冲突的概率。

修复方案

方案一:给类别ID添加类型标识(推荐)

修改load_categories.php,在类别选项的value中加入类型前缀,明确区分物料和服务类别:

<?php
// 连接到数据库'dac'
$conn = mysqli_connect("localhost", "casablanca", "casablanca", "dac");

if (!$conn) {
    die("连接失败: " . mysqli_connect_error());
}

if (isset($_POST['typeAchat'])) {
    $typeAchat = $_POST['typeAchat'];
    $options = '<option value="">请选择类别</option>';
    
    // 根据采购类型加载对应类别
    if ($typeAchat == 1) { // 替换为实际物料类型的ID
        $query = "SELECT * FROM categorie_materiels WHERE id_type_achat = $typeAchat";
        $result = mysqli_query($conn, $query);
        while ($row = mysqli_fetch_assoc($result)) {
            // 添加'm-'前缀标识物料类别
            $options .= '<option value="m-' . $row['id_categorie_materiels'] . '">' . $row['nom_categorie_materiels'] . '</option>';
        }
    } elseif ($typeAchat == 2) { // 替换为实际服务类型的ID
        $queryService = "SELECT * FROM categorie_services WHERE id_type_achat = $typeAchat";
        $resultService = mysqli_query($conn, $queryService);
        while ($row = mysqli_fetch_assoc($resultService)) {
            // 添加's-'前缀标识服务类别
            $options .= '<option value="s-' . $row['id_categorie_services'] . '">' . $row['nom_categorie_services'] . '</option>';
        }
    }

    echo $options;
}

mysqli_close($conn);
?>

修改load_articles.php,根据前缀判断类别类型:

<?php
// 连接到数据库'dac'
$conn = mysqli_connect("localhost", "casablanca", "casablanca", "dac");

if (!$conn) {
    die("连接失败: " . mysqli_connect_error());
}

if (isset($_POST['categorieId'])) {
    $categorieId = $_POST['categorieId'];
    // 拆分类型标识和实际ID
    list($type, $id) = explode('-', $categorieId);
    
    if ($type == 'm') {
        // 查询物料类别下的物品
        $query = "SELECT * FROM materiel WHERE id_categorie_materiel = $id";
        $result = mysqli_query($conn, $query);
        $options = '<option value="">请选择物品</option>';
        while ($row = mysqli_fetch_assoc($result)) {
            $options .= '<option value="' . $row['id_materiel'] . '">' . $row['nom_materiel'] . '</option>';
        }
    } elseif ($type == 's') {
        // 查询服务类别下的服务
        $queryService = "SELECT * FROM service WHERE id_categorie_services = $id";
        $resultService = mysqli_query($conn, $queryService);
        $options = '<option value="">请选择服务</option>';
        while ($row = mysqli_fetch_assoc($resultService)) {
            $options .= '<option value="' . $row['id_service'] . '">' . $row['nom_service'] . '</option>';
        }
    }

    echo $options;
}

mysqli_close($conn);
?>

方案二:优化类别判断逻辑

若不想修改ID格式,可调整load_articles.php的查询顺序,优先检查服务类别表:

<?php
// 连接到数据库'dac'
$conn = mysqli_connect("localhost", "casablanca", "casablanca", "dac");

if (!$conn) {
    die("连接失败: " . mysqli_connect_error());
}

if (isset($_POST['categorieId'])) {
    $categorieId = $_POST['categorieId'];

    // 优先检查服务类别
    $queryServiceCat = "SELECT * FROM categorie_services WHERE id_categorie_services = $categorieId";
    $resultServiceCat = mysqli_query($conn, $queryServiceCat);

    if (mysqli_num_rows($resultServiceCat) > 0) {
        // 查询服务类别下的服务
        $queryService = "SELECT * FROM service WHERE id_categorie_services = $categorieId";
        $resultService = mysqli_query($conn, $queryService);
        $options = '<option value="">请选择服务</option>';
        while ($row = mysqli_fetch_assoc($resultService)) {
            $options .= '<option value="' . $row['id_service'] . '">' . $row['nom_service'] . '</option>';
        }
    } else {
        // 查询物料类别下的物品
        $query = "SELECT * FROM materiel WHERE id_categorie_materiel = $categorieId";
        $result = mysqli_query($conn, $query);
        $options = '<option value="">请选择物品</option>';
        while ($row = mysqli_fetch_assoc($result)) {
            $options .= '<option value="' . $row['id_materiel'] . '">' . $row['nom_materiel'] . '</option>';
        }
    }

    echo $options;
}

mysqli_close($conn);
?>

同时修改load_categories.php,仅加载对应类型的类别:

<?php
// 连接到数据库'dac'
$conn = mysqli_connect("localhost", "casablanca", "casablanca", "dac");

if (!$conn) {
    die("连接失败: " . mysqli_connect_error());
}

if (isset($_POST['typeAchat'])) {
    $typeAchat = $_POST['typeAchat'];
    $options = '<option value="">请选择类别</option>';
    
    // 仅加载对应类型的类别
    $query = "SELECT * FROM categorie_materiels WHERE id_type_achat = $typeAchat";
    $result = mysqli_query($conn, $query);
    if (mysqli_num_rows($result) > 0) {
        while ($row = mysqli_fetch_assoc($result)) {
            $options .= '<option value="' . $row['id_categorie_materiels'] . '">' . $row['nom_categorie_materiels'] . '</option>';
        }
    } else {
        $queryService = "SELECT * FROM categorie_services WHERE id_type_achat = $typeAchat";
        $resultService = mysqli_query($conn, $queryService);
        while ($row = mysqli_fetch_assoc($resultService)) {
            $options .= '<option value="' . $row['id_categorie_services'] . '">' . $row['nom_categorie_services'] . '</option>';
        }
    }

    echo $options;
}

mysqli_close($conn);
?>

内容的提问来源于stack exchange,提问作者barmajah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 08:12:01