我们将使用 原生 JavaScript 和 AJAX 技术来实现,这样无需引入额外的 jQuery 库,页面加载更快,也更符合现代前端开发的趋势。

(图片来源网络,侵删)
核心思路
- 数据准备:在织梦后台创建两个自定义表单(或使用现有的数据表),分别用于存储“省份”和“城市”数据,为了方便联动,城市表中需要有一个字段来关联它所属的省份ID。
- 前端HTML:在需要的地方放置两个
<select>下拉框,一个用于省份,一个用于城市。 - JavaScript 逻辑:
- 页面加载时,通过 AJAX 请求获取所有省份数据,并填充到省份下拉框中。
- 当用户选择一个省份时,触发一个
onchange事件。 - 在事件处理函数中,获取用户选中的省份ID。
- 再次通过 AJAX,将这个省份ID作为参数,请求对应的城市数据。
- 接收到城市数据后,清空城市下拉框,然后将新获取的城市数据填充进去。
详细步骤
第一步:准备数据表(以织梦自定义表单为例)
这是最关键的一步,数据结构必须清晰。
-
创建省份表单:
- 登录织梦后台 -> 核心 -> 频道模型 -> 自定义表单。
- 创建一个新的自定义表单,命名为
provinces。 - 添加字段:
省份名称(文本字段,字段名设为province_name)省份ID(数字字段,字段名设为province_id,必须设为“主键”,并且是自增的)。
-
创建城市表单:
- 再次创建一个自定义表单,命名为
cities。 - 添加字段:
城市名称(文本字段,字段名设为city_name)所属省份ID(数字字段,字段名设为parent_id,用于关联省份的province_id)城市ID(数字字段,字段名设为city_id,设为“主键”,自增)。
- 再次创建一个自定义表单,命名为
-
录入数据:
(图片来源网络,侵删)- 进入
provinces表单的数据管理界面,手动录入一些省份数据,- province_id: 1, province_name: "广东省"
- province_id: 2, province_name: "江苏省"
- province_id: 3, province_name: "浙江省"
- 进入
cities表单的数据管理界面,录入城市数据,注意parent_id:- city_id: 1, city_name: "广州市", parent_id: 1
- city_id: 2, city_name: "深圳市", parent_id: 1
- city_id: 3, city_name: "东莞市", parent_id: 1
- city_id: 4, city_name: "南京市", parent_id: 2
- city_id: 5, city_name: "苏州市", parent_id: 2
- city_id: 6, city_name: "杭州市", parent_id: 3
- city_id: 7, city_name: "宁波市", parent_id: 3
- 进入
第二步:创建PHP接口文件
我们需要两个PHP文件来提供AJAX请求的数据,这些文件可以放在你的模板目录下,/templets/your_js/。
-
获取省份列表
get_provinces.php这个文件负责从dede_provinces表中读取所有省份。<?php // 引入数据库配置文件,根据你的实际路径调整 // 通常在 /include/common.inc.php require_once(dirname(__FILE__) . '/../../include/common.inc.php'); // 设置响应头为JSON格式 header('Content-Type: application/json; charset=utf-8'); // 查询省份数据 $sql = "SELECT province_id, province_name FROM dede_provinces ORDER BY province_id ASC"; $dsql->SetQuery($sql); $dsql->Execute(); $provinces = array(); while ($row = $dsql->GetArray()) { $provinces[] = $row; } // 将数据转换为JSON格式并输出 echo json_encode($provinces); ?> -
获取城市列表
get_cities.php这个文件根据传入的省份ID (parent_id) 来查询对应的城市。<?php require_once(dirname(__FILE__) . '/../../include/common.inc.php'); header('Content-Type: application/json; charset=utf-8'); // 获取前端传来的省份ID $parent_id = isset($_GET['parent_id']) ? intval($_GET['parent_id']) : 0; $cities = array(); if ($parent_id > 0) { $sql = "SELECT city_id, city_name FROM dede_cities WHERE parent_id = {$parent_id} ORDER BY city_id ASC"; $dsql->SetQuery($sql); $dsql->Execute(); while ($row = $dsql->GetArray()) { $cities[] = $row; } } echo json_encode($cities); ?>
第三步:编写HTML和JavaScript
在你的织梦模板文件(如 article_add.htm 或 index.htm)中,加入以下代码。

(图片来源网络,侵删)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">织梦二级联动示例</title>
<style>
/* 为了美观,可以加一点样式 */
select {
padding: 8px;
margin-right: 10px;
border-radius: 4px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<h2>请选择您的省份和城市:</h2>
<div>
<!-- 省份下拉框 -->
<select id="province-select">
<option value="">-- 请选择省份 --</option>
</select>
<!-- 城市下拉框 -->
<select id="city-select">
<option value="">-- 请先选择省份 --</option>
</select>
</div>
<!-- 引入我们的JavaScript文件 -->
<script src="{dede:global.cfg_cmspath/}/templets/your_js/linkage.js"></script>
</body>
</html>
第四步:编写JavaScript逻辑文件 linkage.js
将这个文件放在你指定的路径,/templets/your_js/linkage.js。
document.addEventListener('DOMContentLoaded', function() {
// 获取DOM元素
const provinceSelect = document.getElementById('province-select');
const citySelect = document.getElementById('city-select');
// --- 1. 加载省份数据 ---
fetch('/get_provinces.php') // 注意这里的路径要正确
.then(response => {
if (!response.ok) {
throw new Error('省份网络请求失败');
}
return response.json();
})
.then(provinces => {
// 遍历返回的省份数据,创建option并添加到select中
provinces.forEach(province => {
const option = document.createElement('option');
option.value = province.province_id;
option.textContent = province.province_name;
provinceSelect.appendChild(option);
});
})
.catch(error => {
console.error('加载省份数据时出错:', error);
provinceSelect.innerHTML = '<option value="">加载失败</option>';
});
// --- 2. 监听省份选择变化,联动加载城市 ---
provinceSelect.addEventListener('change', function() {
const selectedProvinceId = this.value;
// 清空城市下拉框,并添加一个默认提示
citySelect.innerHTML = '<option value="">-- 请选择城市 --</option>';
// 如果用户没有选择省份,则不进行后续操作
if (!selectedProvinceId) {
return;
}
// 发起AJAX请求获取对应的城市数据
fetch(`/get_cities.php?parent_id=${selectedProvinceId}`) // 注意路径
.then(response => {
if (!response.ok) {
throw new Error('城市网络请求失败');
}
return response.json();
})
.then(cities => {
// 遍历返回的城市数据,创建option并添加到城市select中
cities.forEach(city => {
const option = document.createElement('option');
option.value = city.city_id;
option.textContent = city.city_name;
citySelect.appendChild(option);
});
})
.catch(error => {
console.error('加载城市数据时出错:', error);
citySelect.innerHTML = '<option value="">加载失败</option>';
});
});
});
总结与注意事项
- 路径问题:
fetch请求的URL (/get_provinces.php和/get_cities.php) 必须是相对于网站根目录的,如果你的PHP文件在/templets/your_js/目录下,那么URL应该是/templets/your_js/get_provinces.php,请根据你的实际文件存放位置进行修改。 - 数据库表前缀:代码中
dede_provinces和dede_cities是默认的表前缀,如果你的织梦安装时使用了不同的表前缀(如abc_),请务必修改PHP文件中的SQL语句。 - 安全性:
intval($_GET['parent_id'])是一个基本的安全措施,确保传入的ID是整数,防止SQL注入,虽然dede自身有较强的安全机制,但养成好习惯总是对的。 - 用户体验:在数据加载过程中,可以给下拉框添加一个“加载中...”的提示,避免用户误操作。
- 扩展性:这个模式可以轻松扩展到三级(城市-区县)甚至更多级联动,只需在
city-select的onchange事件中再发起一次AJAX请求即可。
通过以上四个步骤,你就成功地在织梦CMS中实现了一个功能完善、体验良好的二级联动菜单。
