原生html+css+ajax实现二级下拉选择的增删改及树形结构列出

php 复制代码
<?php
$db_host = 'localhost';
$db_user = 'info_chalide';
$db_pass = 'j8c2rRr2RnA';
$db_name = 'info_chalide';
/*
数据库结构SQL
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
parent_id INT DEFAULT 0
);
*/
try {
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8mb4", $db_name, $db_pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
die("连接失败: " . $e->getMessage());
}
// AJAX处理
if (!empty($_GET['act'])) {
header('Content-Type: application/json');
switch ($_GET['act']) {
case 'list':
// 获取所有类别
try {
$sql = "SELECT * FROM cat ORDER BY parent_id, id";
$stmt = $pdo->query($sql);
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 组织树形结构
$tree = [];
foreach ($categories as $cat) {
if ($cat['parent_id'] == 0) {
$cat['children'] = [];
$tree[$cat['id']] = $cat;
}
}
foreach ($categories as $cat) {
if ($cat['parent_id'] > 0 && isset($tree[$cat['parent_id']])) {
$tree[$cat['parent_id']]['children'][] = $cat;
}
}
echo json_encode(['status' => 'success', 'data' => array_values($tree)]);
} catch (PDOException $e) {
echo json_encode(['status' => 'error', 'message' => '获取类别失败:' . $e->getMessage()]);
}
break;
case 'add':
if (empty($_POST['name'])) {
echo json_encode(['status' => 'error', 'message' => '类别名称不能为空']);
exit;
}
try {
$name = trim($_POST['name']);
$parent_id = isset($_POST['parent_id']) ? intval($_POST['parent_id']) : 0;
$sql = "INSERT INTO cat (name, parent_id) VALUES (?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name, $parent_id]);
echo json_encode(['status' => 'success', 'message' => '添加成功']);
} catch (PDOException $e) {
echo json_encode(['status' => 'error', 'message' => '添加失败:' . $e->getMessage()]);
}
break;
case 'edit':
if (empty($_POST['name']) || empty($_POST['id'])) {
echo json_encode(['status' => 'error', 'message' => '参数错误']);
exit;
}
try {
$name = trim($_POST['name']);
$id = intval($_POST['id']);
$sql = "UPDATE cat SET name = ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name, $id]);
echo json_encode(['status' => 'success', 'message' => '修改成功']);
} catch (PDOException $e) {
echo json_encode(['status' => 'error', 'message' => '修改失败:' . $e->getMessage()]);
}
break;
case 'delete':
if (empty($_POST['id'])) {
echo json_encode(['status' => 'error', 'message' => '参数错误']);
exit;
}
try {
$id = intval($_POST['id']);
// 删除该类别及其子类别
$sql = "DELETE FROM cat WHERE id = ? OR parent_id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$id, $id]);
echo json_encode(['status' => 'success', 'message' => '删除成功']);
} catch (PDOException $e) {
echo json_encode(['status' => 'error', 'message' => '删除失败:' . $e->getMessage()]);
}
break;
}
exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>类别管理</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: Arial, sans-serif; padding: 20px; }
/* 树形结构样式 */
.tree-item { margin: 5px 0; }
.tree-item.child { margin-left: 30px; }
.category-name { display: inline-block; border: 1px solid #ddd; min-width: 150px; }
/* 按钮样式 */
button {
padding: 3px 5px;
margin: 0 2px;
cursor: pointer;
border: 1px solid #ddd;
background: #f8f8f8;
border-radius: 3px;
}
button:hover { background: #e8e8e8; }
/* 遮罩层样式 */
.overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1000;
}
.modal {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
border-radius: 5px;
min-width: 300px;
max-width: 90%;
max-height: 90vh;
display: flex;
flex-direction: column;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-body {
overflow-y: auto;
margin-bottom: 20px;
}
.modal-footer {
border-top: 1px solid #eee;
padding-top: 15px;
text-align: right;
}
.close-btn {
cursor: pointer;
font-size: 20px;
}
/* 表单样式 */
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
}
.form-group input,
.form-group select {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 3px;
}
</style>
</head>
<body>
<h1>类别管理</h1>
<button onclick="showAddModal()">添加类别</button>
<div id="category-tree"></div>
<!-- 添加/修改类别的遮罩层 -->
<div id="categoryModal" class="overlay">
<div class="modal">
<div class="modal-header">
<h3 id="modalTitle">添加类别</h3>
<span class="close-btn" onclick="closeModal()">&times;</span>
</div>
<div class="modal-body">
<form id="categoryForm">
<input type="hidden" id="categoryId">
<div class="form-group">
<label for="categoryName">类别名称:</label>
<input type="text" id="categoryName" required>
</div>
<div class="form-group" id="parentSelectGroup">
<label for="parentCategory">上级类别:</label>
<select id="parentCategory">
<option value="0">无 (新增一级类别)</option>
</select>
</div>
</form>
</div>
<div class="modal-footer">
<button onclick="closeModal()">取消</button>
<button onclick="saveCategory()">保存</button>
</div>
</div>
</div>
<script>
// 加载类别列表
function loadCategories() {
fetch('?act=list')
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
renderCategoryTree(data.data);
updateParentSelect(data.data);
} else {
alert(data.message);
}
});
}
// 渲染类别树
function renderCategoryTree(categories) {
const tree = document.getElementById('category-tree');
tree.innerHTML = '';
categories.forEach(category => {
// 渲染一级类别
const item = document.createElement('div');
item.className = 'tree-item';
item.innerHTML = `
<span class="category-name">${category.name}</span>
<button onclick="showEditModal(${category.id}, '${category.name}')">修改</button>
<button onclick="deleteCategory(${category.id})">删除</button>
`;
tree.appendChild(item);
// 渲染二级类别
if (category.children && category.children.length > 0) {
category.children.forEach(child => {
const childItem = document.createElement('div');
childItem.className = 'tree-item child';
childItem.innerHTML = `
<span class="category-name">${child.name}</span>
<button onclick="showEditModal(${child.id}, '${child.name}')">修改</button>
<button onclick="deleteCategory(${child.id})">删除</button>
`;
tree.appendChild(childItem);
});
}
});
}
// 更新上级类别选择框
function updateParentSelect(categories) {
const select = document.getElementById('parentCategory');
select.innerHTML = '<option value="0">无 (新增一级类别)</option>';
categories.forEach(category => {
select.innerHTML += `<option value="${category.id}">${category.name}</option>`;
});
}
// 显示添加模态框
function showAddModal() {
document.getElementById('modalTitle').textContent = '添加类别';
document.getElementById('categoryId').value = '';
document.getElementById('categoryName').value = '';
document.getElementById('parentSelectGroup').style.display = 'block';
document.getElementById('categoryModal').style.display = 'block';
}
// 显示编辑模态框
function showEditModal(id, name) {
document.getElementById('modalTitle').textContent = '修改类别';
document.getElementById('categoryId').value = id;
document.getElementById('categoryName').value = name;
document.getElementById('parentSelectGroup').style.display = 'none';
document.getElementById('categoryModal').style.display = 'block';
}
// 关闭模态框
function closeModal() {
document.getElementById('categoryModal').style.display = 'none';
}
// 保存类别
function saveCategory() {
const id = document.getElementById('categoryId').value;
const name = document.getElementById('categoryName').value;
const parent_id = document.getElementById('parentCategory').value;
if (!name) {
alert('请输入类别名称');
return;
}
const formData = new FormData();
formData.append('name', name);
if (id) {
// 修改
formData.append('id', id);
fetch('?act=edit', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
closeModal();
loadCategories();
}
alert(data.message);
});
} else {
// 添加
formData.append('parent_id', parent_id);
fetch('?act=add', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
closeModal();
loadCategories();
}
alert(data.message);
});
}
}
// 删除类别
function deleteCategory(id) {
if (!confirm('确定要删除该类别吗?此操作无法恢复!')) {
return;
}
const formData = new FormData();
formData.append('id', id);
fetch('?act=delete', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
alert(data.message);
if (data.status === 'success') {
loadCategories();
}
});
}
// 页面加载完成后加载类别列表
window.onload = loadCategories;
</script>
</body>
</html>
相关推荐
代码s贝多芬的音符1 小时前
android mlkit 实现仰卧起坐和俯卧撑识别
android
jwn9992 小时前
Laravel9.x核心特性全解析
android
今天又在写代码2 小时前
数据智能分析平台部署服务器
android·服务器·adb
梦里花开知多少3 小时前
深入谈谈Launcher的启动流程
android·架构
jwn9993 小时前
Laravel11.x新特性全解析
android·开发语言·php·laravel
我就是马云飞4 小时前
停更5年后,我为什么重新开始写技术内容了
android·前端·程序员
stevenzqzq4 小时前
Kotlin 协程:withContext 与 async 核心区别与使用场景
android·开发语言·kotlin
唔664 小时前
原生 Android(Kotlin)仅串口「侵入式架构」完整案例三
android·架构·kotlin
唔664 小时前
原生 Android(Kotlin)仅串口「可插拔架构」完整案例一
android·架构·kotlin
Melrose4 小时前
移动端安全攻防
android·前端·安全