PHP 动态配置管理后台

wen PHP项目 4

本文目录导读:

PHP 动态配置管理后台

  1. 数据库设计
  2. 后端PHP代码
  3. 前端界面
  4. 使用说明

我来为您创建一个完整的PHP动态配置管理后台,包含数据库设计、后端API和前端界面。

数据库设计

-- 创建配置表
CREATE TABLE `configurations` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `config_key` varchar(100) NOT NULL COMMENT '配置键名',
  `config_value` text COMMENT '配置值',
  `config_type` enum('text','textarea','number','select','checkbox','json') DEFAULT 'text' COMMENT '配置类型',
  `config_group` varchar(50) DEFAULT 'general' COMMENT '配置分组',
  `description` varchar(255) DEFAULT NULL COMMENT '配置说明',
  `options` text COMMENT '选项值(JSON格式)',
  `is_required` tinyint(1) DEFAULT '0' COMMENT '是否必填',
  `created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_config_key` (`config_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 示例数据
INSERT INTO `configurations` (`config_key`, `config_value`, `config_type`, `config_group`, `description`, `options`, `is_required`) VALUES
('site_name', 'My Website', 'text', 'general', '网站名称', NULL, 1),
('site_url', 'https://example.com', 'text', 'general', '网站URL', NULL, 1),
('site_description', '这是网站描述', 'textarea', 'general', '网站描述', NULL, 0),
('items_per_page', '20', 'number', 'pagination', '每页显示数量', NULL, 1),
('theme_color', 'blue', 'select', 'appearance', '主题颜色', '["blue","red","green","dark"]', 0),
('enable_registration', '1', 'checkbox', 'user', '开放注册', NULL, 0),
('api_settings', '{"timeout":30,"retry":3}', 'json', 'api', 'API设置', NULL, 0);

后端PHP代码

config.php - 数据库连接

<?php
// config.php
session_start();
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'config_demo');
// 创建数据库连接
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
$conn->set_charset("utf8mb4");
// 辅助函数
function json_response($data, $status = 200) {
    http_response_code($status);
    header('Content-Type: application/json');
    echo json_encode($data);
    exit;
}
function validate_input($data) {
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data);
    return $data;
}
?>

get_configs.php - 获取配置列表

<?php
require_once 'config.php';
header('Content-Type: application/json');
$sql = "SELECT * FROM configurations ORDER BY config_group, config_key";
$result = $conn->query($sql);
$configs = [];
while ($row = $result->fetch_assoc()) {
    // 解析JSON选项
    if ($row['options']) {
        $row['options'] = json_decode($row['options'], true);
    }
    // 解析JSON值
    if ($row['config_type'] === 'json' && $row['config_value']) {
        $row['config_value'] = json_decode($row['config_value'], true);
    }
    $configs[] = $row;
}
json_response(['success' => true, 'data' => $configs]);
?>

save_config.php - 保存配置

<?php
require_once 'config.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    json_response(['success' => false, 'message' => 'Invalid request method'], 400);
}
// 获取请求数据
$input = json_decode(file_get_contents('php://input'), true);
if (!$input || !isset($input['id']) || !isset($input['value'])) {
    json_response(['success' => false, 'message' => 'Invalid data'], 400);
}
$id = intval($input['id']);
$value = $input['value'];
// 获取配置信息
$stmt = $conn->prepare("SELECT * FROM configurations WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
$config = $result->fetch_assoc();
if (!$config) {
    json_response(['success' => false, 'message' => 'Configuration not found'], 404);
}
// 根据类型处理值
$valueToSave = $value;
switch ($config['config_type']) {
    case 'checkbox':
        $valueToSave = $value ? '1' : '0';
        break;
    case 'json':
        if (is_array($value)) {
            $valueToSave = json_encode($value);
        } else {
            // 尝试解析JSON字符串
            $decoded = json_decode($value, true);
            if (json_last_error() === JSON_ERROR_NONE) {
                $valueToSave = json_encode($decoded);
            } else {
                json_response(['success' => false, 'message' => 'Invalid JSON format'], 400);
            }
        }
        break;
    case 'number':
        if (!is_numeric($value)) {
            json_response(['success' => false, 'message' => 'Invalid number'], 400);
        }
        break;
    default:
        $valueToSave = validate_input((string)$value);
}
// 更新配置
$stmt = $conn->prepare("UPDATE configurations SET config_value = ?, updated_at = NOW() WHERE id = ?");
$stmt->bind_param("si", $valueToSave, $id);
if ($stmt->execute()) {
    json_response(['success' => true, 'message' => 'Configuration saved successfully']);
} else {
    json_response(['success' => false, 'message' => 'Failed to save configuration'], 500);
}
?>

delete_config.php - 删除配置

<?php
require_once 'config.php';
if ($_SERVER['REQUEST_METHOD'] !== 'DELETE') {
    json_response(['success' => false, 'message' => 'Invalid request method'], 400);
}
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
if (!$id) {
    json_response(['success' => false, 'message' => 'Invalid ID'], 400);
}
$stmt = $conn->prepare("DELETE FROM configurations WHERE id = ?");
$stmt->bind_param("i", $id);
if ($stmt->execute()) {
    json_response(['success' => true, 'message' => 'Configuration deleted successfully']);
} else {
    json_response(['success' => false, 'message' => 'Failed to delete configuration'], 500);
}
?>

add_config.php - 添加配置

<?php
require_once 'config.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    json_response(['success' => false, 'message' => 'Invalid request method'], 400);
}
$input = json_decode(file_get_contents('php://input'), true);
// 验证必填字段
if (!isset($input['config_key']) || !isset($input['config_key'])) {
    json_response(['success' => false, 'message' => 'Config key is required'], 400);
}
$config_key = validate_input($input['config_key']);
$config_value = isset($input['config_value']) ? validate_input($input['config_value']) : '';
$config_type = isset($input['config_type']) ? validate_input($input['config_type']) : 'text';
$config_group = isset($input['config_group']) ? validate_input($input['config_group']) : 'general';
$description = isset($input['description']) ? validate_input($input['description']) : '';
$is_required = isset($input['is_required']) ? intval($input['is_required']) : 0;
$options = isset($input['options']) ? json_encode($input['options']) : NULL;
// 检查key是否已存在
$stmt = $conn->prepare("SELECT id FROM configurations WHERE config_key = ?");
$stmt->bind_param("s", $config_key);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
    json_response(['success' => false, 'message' => 'Config key already exists'], 400);
}
$stmt = $conn->prepare("INSERT INTO configurations (config_key, config_value, config_type, config_group, description, options, is_required) VALUES (?, ?, ?, ?, ?, ?, ?)");
$stmt->bind_param("ssssssi", $config_key, $config_value, $config_type, $config_group, $description, $options, $is_required);
if ($stmt->execute()) {
    json_response(['success' => true, 'message' => 'Configuration added successfully', 'id' => $conn->insert_id]);
} else {
    json_response(['success' => false, 'message' => 'Failed to add configuration'], 500);
}
?>

get_config_value.php - 获取单个配置值

<?php
require_once 'config.php';
// 获取特定配置值(用于前端)
$key = isset($_GET['key']) ? validate_input($_GET['key']) : '';
if (!$key) {
    json_response(['success' => false, 'message' => 'Config key is required'], 400);
}
$stmt = $conn->prepare("SELECT config_value, config_type FROM configurations WHERE config_key = ? LIMIT 1");
$stmt->bind_param("s", $key);
$stmt->execute();
$result = $stmt->get_result();
if ($row = $result->fetch_assoc()) {
    // 根据类型转换值
    $value = $row['config_value'];
    switch ($row['config_type']) {
        case 'number':
            $value = (float)$value;
            break;
        case 'checkbox':
            $value = (bool)$value;
            break;
        case 'json':
            $value = json_decode($value, true);
            break;
    }
    json_response(['success' => true, 'data' => $value]);
} else {
    json_response(['success' => false, 'message' => 'Configuration not found'], 404);
}
?>

前端界面

index.php - 管理后台主页面

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">配置管理后台</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
    <style>
        .config-section {
            margin-bottom: 30px;
        }
        .config-section h3 {
            border-bottom: 2px solid #007bff;
            padding-bottom: 10px;
            margin-bottom: 20px;
        }
        .config-item {
            margin-bottom: 15px;
        }
        .config-card {
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            border: 1px solid #dee2e6;
            border-radius: 8px;
        }
        .config-card .card-body {
            padding: 15px;
        }
        .edit-btn, .delete-btn {
            cursor: pointer;
        }
        .toast-container {
            position: fixed;
            top: 20px;
            right: 20px;
            z-index: 1050;
        }
        .loading {
            display: none;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }
        #sidebar {
            min-height: 100vh;
            background: #343a40;
            color: white;
            padding: 20px;
        }
        #sidebar a {
            color: white;
            text-decoration: none;
            display: block;
            padding: 10px;
            margin-bottom: 5px;
            border-radius: 5px;
        }
        #sidebar a:hover {
            background: #007bff;
        }
        #sidebar a.active {
            background: #007bff;
        }
    </style>
</head>
<body>
    <div class="container-fluid">
        <div class="row">
            <!-- 侧边栏 -->
            <nav id="sidebar" class="col-md-2 d-md-block">
                <h4 class="text-center mb-4">配置管理</h4>
                <a href="#general" class="active" data-group="general"><i class="fas fa-cog"></i> 通用设置</a>
                <a href="#appearance" data-group="appearance"><i class="fas fa-palette"></i> 外观设置</a>
                <a href="#user" data-group="user"><i class="fas fa-user"></i> 用户设置</a>
                <a href="#api" data-group="api"><i class="fas fa-code"></i> API设置</a>
                <a href="#system" data-group="system"><i class="fas fa-server"></i> 系统设置</a>
                <hr>
                <a href="#add-config" data-bs-toggle="modal" data-bs-target="#addConfigModal">
                    <i class="fas fa-plus"></i> 添加配置
                </a>
            </nav>
            <!-- 主内容 -->
            <main class="col-md-10 ms-sm-auto px-md-4">
                <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
                    <h1 class="h2">配置管理</h1>
                    <div class="btn-toolbar mb-2 mb-md-0">
                        <button class="btn btn-primary" onclick="refreshConfigs()">
                            <i class="fas fa-refresh"></i> 刷新
                        </button>
                    </div>
                </div>
                <div id="loading" class="loading">
                    <div class="spinner-border text-primary" role="status">
                        <span class="visually-hidden">Loading...</span>
                    </div>
                </div>
                <div id="config-content"></div>
            </main>
        </div>
    </div>
    <!-- Toast通知 -->
    <div class="toast-container" id="toastContainer"></div>
    <!-- 添加配置 Modal -->
    <div class="modal fade" id="addConfigModal" tabindex="-1">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">添加新配置</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <form id="addConfigForm">
                        <div class="mb-3">
                            <label class="form-label">配置键名 *</label>
                            <input type="text" class="form-control" id="configKey" required>
                        </div>
                        <div class="mb-3">
                            <label class="form-label">配置值</label>
                            <textarea class="form-control" id="configValue"></textarea>
                        </div>
                        <div class="mb-3">
                            <label class="form-label">配置类型</label>
                            <select class="form-control" id="configType">
                                <option value="text">文本</option>
                                <option value="textarea">多行文本</option>
                                <option value="number">数字</option>
                                <option value="select">下拉选择</option>
                                <option value="checkbox">复选框</option>
                                <option value="json">JSON</option>
                            </select>
                        </div>
                        <div class="mb-3">
                            <label class="form-label">配置分组</label>
                            <select class="form-control" id="configGroup">
                                <option value="general">通用设置</option>
                                <option value="appearance">外观设置</option>
                                <option value="user">用户设置</option>
                                <option value="api">API设置</option>
                                <option value="system">系统设置</option>
                            </select>
                        </div>
                        <div class="mb-3">
                            <label class="form-label">描述</label>
                            <textarea class="form-control" id="configDescription"></textarea>
                        </div>
                        <div class="mb-3 form-check">
                            <input type="checkbox" class="form-check-input" id="configRequired">
                            <label class="form-check-label">必填</label>
                        </div>
                    </form>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
                    <button type="button" class="btn btn-primary" onclick="addConfig()">添加</button>
                </div>
            </div>
        </div>
    </div>
    <!-- 编辑配置 Modal -->
    <div class="modal fade" id="editModal" tabindex="-1">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">编辑配置</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <form id="editForm">
                        <input type="hidden" id="editId">
                        <div class="mb-3">
                            <label class="form-label">配置值</label>
                            <div id="editValueContainer"></div>
                        </div>
                    </form>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
                    <button type="button" class="btn btn-primary" onclick="saveEdit()">保存</button>
                </div>
            </div>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
        let currentGroup = 'general';
        let allConfigs = [];
        // 初始化
        $(document).ready(function() {
            refreshConfigs();
            // 侧边栏点击事件
            $('.sidebar-item').click(function(e) {
                e.preventDefault();
                const group = $(this).data('group');
                currentGroup = group;
                filterByGroup(group);
                // 设置激活状态
                $('.sidebar-item').removeClass('active');
                $(this).addClass('active');
            });
        });
        // 刷新配置
        function refreshConfigs() {
            $('#loading').show();
            $('#config-content').empty();
            $.ajax({
                url: 'get_configs.php',
                method: 'GET',
                success: function(response) {
                    allConfigs = response.data;
                    filterByGroup(currentGroup);
                    $('#loading').hide();
                },
                error: function(xhr) {
                    showToast('加载配置失败', 'error');
                    $('#loading').hide();
                }
            });
        }
        // 按分组筛选
        function filterByGroup(group) {
            const filtered = allConfigs.filter(config => config.config_group === group);
            renderConfigs(filtered);
        }
        // 渲染配置
        function renderConfigs(configs) {
            const content = $('#config-content');
            content.empty();
            if (configs.length === 0) {
                content.html(`
                    <div class="alert alert-info">
                        <i class="fas fa-info-circle"></i> 该分组暂无配置项
                    </div>
                `);
                return;
            }
            const html = configs.map(config => {
                let valueDisplay = formatValue(config);
                return `
                    <div class="card config-card mb-3">
                        <div class="card-body">
                            <div class="row align-items-center">
                                <div class="col-md-4">
                                    <h6 class="mb-1">${config.config_key}</h6>
                                    <small class="text-muted">${config.description || ''}</small>
                                </div>
                                <div class="col-md-6">
                                    ${valueDisplay}
                                </div>
                                <div class="col-md-2 text-end">
                                    <button class="btn btn-sm btn-outline-primary edit-btn" onclick="openEditModal(${config.id})">
                                        <i class="fas fa-edit"></i>
                                    </button>
                                    <button class="btn btn-sm btn-outline-danger delete-btn" onclick="deleteConfig(${config.id})">
                                        <i class="fas fa-trash"></i>
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                `;
            }).join('');
            content.html(html);
        }
        // 格式化值显示
        function formatValue(config) {
            const value = config.config_value;
            switch(config.config_type) {
                case 'checkbox':
                    return `
                        <div class="form-check form-switch">
                            <input class="form-check-input" type="checkbox" 
                                   ${value == '1' ? 'checked' : ''} disabled>
                            <label class="form-check-label">${value == '1' ? '启用' : '禁用'}</label>
                        </div>
                    `;
                case 'select':
                    return `<span class="badge bg-primary">${value}</span>`;
                case 'json':
                    try {
                        const obj = typeof value === 'string' ? JSON.parse(value) : value;
                        return `<code>${JSON.stringify(obj, null, 2)}</code>`;
                    } catch(e) {
                        return `<code>${value}</code>`;
                    }
                case 'textarea':
                    return `<span class="text-muted">${value?.substring(0, 100)}...</span>`;
                default:
                    return `<span>${value}</span>`;
            }
        }
        // 打开编辑模态框
        function openEditModal(id) {
            const config = allConfigs.find(c => c.id === id);
            if (!config) return;
            $('#editId').val(config.id);
            $('#editValueContainer').empty();
            let inputHtml = '';
            switch(config.config_type) {
                case 'text':
                    inputHtml = `<input type="text" class="form-control" id="editValue" value="${config.config_value}">`;
                    break;
                case 'textarea':
                    inputHtml = `<textarea class="form-control" id="editValue" rows="3">${config.config_value}</textarea>`;
                    break;
                case 'number':
                    inputHtml = `<input type="number" class="form-control" id="editValue" value="${config.config_value}">`;
                    break;
                case 'select':
                    const options = config.options || [];
                    let selectHtml = '<select class="form-control" id="editValue">';
                    options.forEach(option => {
                        selectHtml += `<option value="${option}" ${option === config.config_value ? 'selected' : ''}>${option}</option>`;
                    });
                    selectHtml += '</select>';
                    inputHtml = selectHtml;
                    break;
                case 'checkbox':
                    inputHtml = `
                        <div class="form-check form-switch">
                            <input class="form-check-input" type="checkbox" id="editValue" ${config.config_value == '1' ? 'checked' : ''}>
                            <label class="form-check-label">启用</label>
                        </div>
                    `;
                    break;
                case 'json':
                    const jsonValue = typeof config.config_value === 'string' ? config.config_value : JSON.stringify(config.config_value, null, 2);
                    inputHtml = `<textarea class="form-control" id="editValue" rows="5">${jsonValue}</textarea>`;
                    break;
                default:
                    inputHtml = `<input type="text" class="form-control" id="editValue" value="${config.config_value}">`;
            }
            $('#editValueContainer').html(inputHtml);
            const modal = new bootstrap.Modal(document.getElementById('editModal'));
            modal.show();
        }
        // 保存编辑
        function saveEdit() {
            const id = $('#editId').val();
            let value = $('#editValue').val();
            // 处理checkbox
            if (value === undefined) {
                value = $('#editValue').is(':checked') ? '1' : '0';
            }
            $.ajax({
                url: 'save_config.php',
                method: 'POST',
                data: JSON.stringify({
                    id: id,
                    value: value
                }),
                contentType: 'application/json',
                success: function(response) {
                    if (response.success) {
                        showToast('配置保存成功', 'success');
                        refreshConfigs();
                        const modal = bootstrap.Modal.getInstance(document.getElementById('editModal'));
                        modal.hide();
                    } else {
                        showToast(response.message || '保存失败', 'error');
                    }
                },
                error: function(xhr) {
                    showToast('保存失败', 'error');
                }
            });
        }
        // 删除配置
        function deleteConfig(id) {
            if (!confirm('确定要删除这个配置吗?')) return;
            $.ajax({
                url: `delete_config.php?id=${id}`,
                method: 'DELETE',
                success: function(response) {
                    if (response.success) {
                        showToast('配置删除成功', 'success');
                        refreshConfigs();
                    } else {
                        showToast(response.message || '删除失败', 'error');
                    }
                },
                error: function(xhr) {
                    showToast('删除失败', 'error');
                }
            });
        }
        // 添加配置
        function addConfig() {
            const data = {
                config_key: $('#configKey').val(),
                config_value: $('#configValue').val() || '',
                config_type: $('#configType').val(),
                config_group: $('#configGroup').val(),
                description: $('#configDescription').val() || '',
                is_required: $('#configRequired').is(':checked') ? 1 : 0
            };
            $.ajax({
                url: 'add_config.php',
                method: 'POST',
                data: JSON.stringify(data),
                contentType: 'application/json',
                success: function(response) {
                    if (response.success) {
                        showToast('配置添加成功', 'success');
                        refreshConfigs();
                        // 关闭模态框并重置表单
                        const modal = bootstrap.Modal.getInstance(document.getElementById('addConfigModal'));
                        modal.hide();
                        $('#addConfigForm')[0].reset();
                    } else {
                        showToast(response.message || '添加失败', 'error');
                    }
                },
                error: function(xhr) {
                    showToast('添加失败', 'error');
                }
            });
        }
        // 显示Toast通知
        function showToast(message, type = 'success') {
            const bgClass = type === 'success' ? 'bg-success' : 'bg-danger';
            const toast = `
                <div class="toast ${bgClass} text-white" role="alert">
                    <div class="toast-header">
                        <strong class="me-auto">通知</strong>
                        <button type="button" class="btn-close" data-bs-dismiss="toast"></button>
                    </div>
                    <div class="toast-body">${message}</div>
                </div>
            `;
            $('#toastContainer').append(toast);
            const toastElement = $(toast);
            const toastObj = new bootstrap.Toast(toastElement[0]);
            toastObj.show();
            // 自动移除
            setTimeout(() => {
                toastElement.remove();
            }, 3000);
        }
    </script>
</body>
</html>

使用说明

部署步骤:

  1. 创建数据库 config_demo
  2. 导入SQL文件创建数据表
  3. 上传所有PHP文件到服务器
  4. 修改 config.php 中的数据库连接信息
  5. 通过浏览器访问 index.php

功能特点:

  • ✅ 支持多种配置类型(文本、多行文本、数字、下拉选择、复选框、JSON)
  • ✅ 分组管理配置
  • ✅ 实时保存编辑
  • ✅ 删除和添加配置
  • ✅ 响应式界面
  • ✅ 中文界面
  • ✅ Toast通知
  • ✅ 数据验证

这个后台系统可以方便地管理网站的各种配置项,并且易于扩展和定制!

抱歉,评论功能暂时关闭!