首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

jQuery + PHP + MySQL 實現類 360 安全衛士功能開關效果

     編輯:PHP 相關知識

項目背景

在後台管理系統中,常需實現「功能開關」:管理員點擊開關按鈕,無刷新即時啟用/禁用某項功能(如維護模式、註冊開關、評論審核等)。本文以 360 安全衛士「木馬防火牆」開關 UI 為靈感,構建一個完整的 AJAX 實時切換方案。

效果演示

功能開關動態演示

數據庫設計

CREATE TABLE `feature_switch` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `feature_key` VARCHAR(50) NOT NULL COMMENT '功能標識,如: maintenance_mode',
  `feature_name` VARCHAR(100) NOT NULL COMMENT '功能顯示名稱',
  `is_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '0關閉 1開啟',
  `description` VARCHAR(255) DEFAULT NULL,
  `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_feature_key` (`feature_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 初始化數據
INSERT INTO `feature_switch` (`feature_key`, `feature_name`, `is_enabled`, `description`) VALUES
('maintenance_mode', '網站維護模式', 0, '開啟後前台顯示維護頁面'),
('user_register', '用戶註冊功能', 1, '關閉後禁止新用戶註冊'),
('comment_audit', '評論人工審核', 1, '開啟後評論需後台審核才顯示');

後端 API (switch_api.php)

<?php
require_once 'db.php'; // PDO 連接
header('Content-Type: application/json; charset=utf-8');

$action = $_POST['action'] ?? '';
$key = $_POST['key'] ?? '';

if ($action !== 'toggle' || !$key) {
    echo json_encode(['code' => 400, 'msg' => '參數錯誤']);
    exit;
}

try {
    $pdo->beginTransaction();
    // 行鎖防併發
    $stmt = $pdo->prepare("SELECT is_enabled FROM feature_switch WHERE feature_key = ? FOR UPDATE");
    $stmt->execute([$key]);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$row) {
        throw new Exception('功能不存在');
    }
    $newStatus = $row['is_enabled'] ? 0 : 1;
    $stmt = $pdo->prepare("UPDATE feature_switch SET is_enabled = ? WHERE feature_key = ?");
    $stmt->execute([$newStatus, $key]);
    $pdo->commit();

    // 這裡可加入緩存更新、日誌記錄、事件分發等
    // $redis->set("switch:$key", $newStatus);
    // Log::info("Switch toggled", ['key'=>$key, 'new'=>$newStatus, 'admin_id'=>getCurrentAdminId()]);

    echo json_encode(['code' => 200, 'msg' => '操作成功', 'data' => ['enabled' => (bool)$newStatus]]);
} catch (Exception $e) {
    $pdo->rollBack();
    echo json_encode(['code' => 500, 'msg' => $e->getMessage()]);
}

前端頁面 (index.php)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>功能開關管理</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
    <style>
        .switch { position: relative; display: inline-block; width: 60px; height: 30px; }
        .switch input { opacity: 0; width: 0; height: 0; }
        .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 30px; }
        .slider:before { position: absolute; content: ""; height: 22px; width: 22px; left: 4px; bottom: 4px; background-color: white; transition: .4s; border-radius: 50%; box-shadow: 0 2px 4px rgba(0,0,0,.2); }
        input:checked + .slider { background-color: #28a745; }
        input:checked + .slider:before { transform: translateX(30px); }
        .switch-label { margin-left: 12px; font-weight: 500; vertical-align: middle; }
        .loading { display: none; width: 16px; height: 16px; border: 2px solid #28a745; border-radius: 50%; border-top-color: transparent; animation: spin 1s linear infinite; }
        @keyframes spin { to { transform: rotate(360deg); } }
    </style>
</head>
<body class="bg-light">
<div class="container py-5">
    <h3 class="mb-4">系統功能開關</h3>
    <div class="card">
        <div class="card-body">
            <table class="table table-hover align-middle mb-0">
                <thead class="table-light">
                    <tr><th>功能名稱</th><th>描述</th><th class="text-center" style="width:120px">狀態</th><th class="text-center" style="width:100px">操作</th></tr>
                </thead>
                <tbody id="switchList"></tbody>
            </table>
        </div>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<script>
$(function() {
    loadSwitches();

    function loadSwitches() {
        $.getJSON('switch_api.php?action=list', function(res) {
            if (res.code !== 200) return;
            let html = '';
            res.data.forEach(item => {
                html += `
                    <tr data-key="${item.feature_key}">
                        <td>${item.feature_name}</td>
                        <td class="text-muted">${item.description || '—'}</td>
                        <td class="text-center">
                            <label class="switch">
                                <input type="checkbox" class="toggle-switch" ${item.is_enabled ? 'checked' : ''} data-key="${item.feature_key}">
                                <span class="slider"></span>
                            </label>
                            <span class="switch-label">${item.is_enabled ? '已開啟' : '已關閉'}</span>
                        </td>
                        <td class="text-center">
                            <div class="loading" title="更新中..."></div>
                        </td>
                    </tr>`;
            });
            $('#switchList').html(html);
        });
    }

    $('#switchList').on('change', '.toggle-switch', function() {
        const $checkbox = $(this);
        const key = $checkbox.data('key');
        const $row = $checkbox.closest('tr');
        const $label = $row.find('.switch-label');
        const $loading = $row.find('.loading');

        $checkbox.prop('disabled', true);
        $loading.show();

        $.post('switch_api.php', {action: 'toggle', key: key}, function(res) {
            $loading.hide();
            $checkbox.prop('disabled', false);
            if (res.code === 200) {
                $label.text(res.data.enabled ? '已開啟' : '已關閉');
                // 可選:顯示 Toast 提示
            } else {
                // 失敗則回滾 UI
                $checkbox.prop('checked', !$checkbox.prop('checked'));
                alert('操作失敗:' + res.msg);
            }
        }, 'json').fail(function() {
            $loading.hide();
            $checkbox.prop('disabled', false);
            $checkbox.prop('checked', !$checkbox.prop('checked'));
            alert('網絡請求失敗,請重試');
        });
    });
});
</script>
</body>
</html>

擴展 API:獲取列表

在 switch_api.php 頂部添加:

if ($_GET['action'] ?? '' === 'list') {
    $stmt = $pdo->query("SELECT feature_key, feature_name, is_enabled, description FROM feature_switch ORDER BY id");
    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
    echo json_encode(['code' => 200, 'data' => $data]);
    exit;
}

安全與擴展建議

  • 權限控制:API 需驗證管理員身份及具體權限(RBAC),防止越權操作。
  • 操作审計:記錄操作人、時間、功能鍵、變更前後值,寫入審計日志表。
  • 緩存一致性:更新數據庫後同步更新 Redis,業務代碼讀取開關時優先讀緩存,降低 DB 壓力。
  • 防抖處理:前端可添加 300ms 防抖,避免用戶快速連點產生併發請求。
  • 批量操作:支持「全開/全關」按鈕,後端事務批量更新。

總結

通過 jQuery AJAX + PHP PDO + MySQL 行鎖 + Bootstrap Switch UI,我們實現了一個生產可用的功能開關系統。核心要點在於:併發安全(FOR UPDATE)、狀態原子翻轉、前端即時反饋、失敗回滾。該模式可直接應用於配置管理、功能開關、維護模式等場景,極大提升運營效率與用戶體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved