index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>简单导航 &nbsp;</title>
    <meta name="keywords" content="简单导航">
    <meta name="description" content=""/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="shortcut icon" href="/dd3.png">
    <script>
        var _hmt = _hmt || [];
        (function() {
          var hm = document.createElement("script");
          hm.src = "https://hm.baidu.com/hm.js?786a6208323fbecfb41d71bfda8daca6";
          var s = document.getElementsByTagName("script")[0];
          s.parentNode.insertBefore(hm, s);
        })();
    </script>
    <style>
        /* ================ reset 样式 start  ================ */
        * { margin: 0; padding: 0; font-family: "微软雅黑", "Microsoft YaHei", sans-serif; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
        body, ul, li, h1, h2, h3, h4, h5, h6, p, form, dl, dt, dd { margin: 0px; padding: 0px; font-size: 14px; font-weight: normal; }
        img { border-style: none; }
        li { list-style: none; }
        a { text-decoration: none; }
        html, body { background: #fff; height: 100%; }
        input[type="button"], input[type="submit"], input[type="reset"] { -webkit-appearance: none; outline: 0; }
        textarea { -webkit-appearance: none; }
        /* ================ reset 样式 end  ================ */

        .inner-center { width: 1000px; margin: 0 auto; }
        .main { padding-top: 5%; min-height: 100%; }
        .content-inside{ padding-bottom: 100px; }
        .footer { height: 60px; text-align: center; margin-top: -60px; }

        /* 搜索框 start */
        .search-section { margin-top: 14px; margin-bottom: 15px; position: relative; display: flex; height: 44px; width: 100%; }
        .search-left { display: flex; width: calc(100% - 109.09px); height: 100%; border: 1px solid rgba(217, 217, 217, 0.96); border-right: none; position: relative; border-radius: 2px 0 0 2px; }
        .search-engine-selector { width: 105px; height: 100%; display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0 10px; border-right: 1px solid rgba(217, 217, 217, 0.96); font-size: 14px; color: #000; position: relative; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-radius: 2px 0 0 2px; }
        .search-engine-selector:hover { background-color: #f0f0f0; }
        .search-methods { display: none; position: absolute; left: -1px; top: 43px; width: 106px; border: 1px solid rgba(217, 217, 217, 0.96); background-color: #fff; z-index: 100; border-radius: 2px; }
        .search-methods li { padding-left: 38px; overflow: hidden; height: 34px; line-height: 34px; color: #000; cursor: pointer; font-size: 14px; background-image: none !important; }
        .search-methods .search-item:hover { background-color: #f0f0f0; }
        .input-wrap { position: relative; flex: 1; display: flex; height: 100%; cursor: default; }
        .input-wrap .search-input { height: 100%; width: 100%; outline: 0; border: 0; font-size: 16px; padding-left: 15px; padding-right: 30px; }
        .input-wrap .search-input:focus { outline: none; box-shadow: none; }
        .input-wrap .clear-keyword { display: none; position: absolute; top: 50%; right: 10px; transform: translateY(-50%); cursor: pointer; color: #d2d2d2; font-size: 20px; line-height: 1; }
        .search-submit { width: 111px; height: 44px; line-height: 44px; background-color: #f0f0f0; color: #000; font-size: 14px; font-weight: 500; text-align: center; font-family: Roboto, "Microsoft YaHei", sans-serif; border: 1px solid rgba(217, 217, 217, 0.96); cursor: pointer; transition: background-color 0.3s; border-radius: 0 2px 2px 0; }
        .search-submit:hover { background-color: #d0e9f8; }
        /* 搜索框 end */

        /* 导航内容 start  */
        .nav-content { overflow: hidden; padding-top: 20px; }
        .jj-list { width: 33.33%; float: left; margin-bottom: 30px; padding-right: 16px; }
        .jj-list:nth-of-type(3n) { padding-right: 0; }
        .jj-list-tit { font-size: 14px; line-height: 25px; color: rgba(49, 70, 89, 0.75); font-weight: bold; margin-bottom: 10px; text-align: center; border-radius: 2px; }
        .jj-list-con { overflow: hidden; margin: 0 auto; }
        .jj-list-con li { padding: 1px; width: 33.33%; float: left; }
        .jj-list-link { display: block; background: rgba(230, 247, 255, 0.96); color:rgba(49, 70, 89, 1); font-size: 13px; text-align: center; line-height: 44px; transition: all 0.2s; border-radius: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; position: relative; }
        .jj-list-link .link-text { display: inline-block; }
        .jj-list-link:hover { background: #1890FF;  color: #fff; }

        /* Mobile Responsiveness */
        @media (max-width: 1024px) { .search-section, .search-left { width: 100%; } .search-left { width: calc(100% - 109.09px); } }
        @media (max-width: 768px) {
            .inner-center { width: 95%; }
            .main { padding-top: 3%; }
            .search-section { flex-direction: column; height: auto; }
            .search-left { width: 100%; margin-bottom: 10px; height: 44px; border-radius: 2px; }
            .search-engine-selector { width: 90px; font-size: 13px; border-radius: 2px 0 0 2px; }
            .search-methods { width: 90px; }
            .input-wrap .search-input { font-size: 14px; }
            .search-submit { width: 100%; font-size: 16px; height: 44px; line-height: 44px; border-radius: 2px; }
            .jj-list { width: 100%; padding-right: 0; margin-bottom: 20px; }
            .jj-list:nth-of-type(3n) { padding-right: 0; }
            .jj-list-con li { width: 50%; }
            .jj-list-link { line-height: 40px; font-size: 13px; }
        }
        @media (max-width: 480px) {
            .jj-list-con li { width: 100%; }
            .search-engine-selector { padding: 0 5px; width: 80px; }
            .search-methods { width: 80px; }
            .input-wrap .search-input { padding-left: 10px; }
        }

        /* Styles for Edit Mode & Modals */
        .edit-mode-controls { margin-bottom: 20px; padding: 10px; background-color: #f0f0f0; border: 1px solid #ccc; border-radius: 2px; text-align: center; }
        .edit-mode-controls button { padding: 8px 15px; margin: 0 5px; background-color: #007bff; color: white; border: none; border-radius: 2px; cursor: pointer; }
        .edit-mode-controls button:hover { background-color: #0056b3; }
        
        .jj-list-link.editable, .jj-list-tit.editable { padding: 5px; line-height: normal; height: auto; }
        .jj-list-link.editable input[type="text"], .jj-list-tit.editable input[type="text"] { box-sizing: border-box; padding: 5px; margin: 3px 0; font-size: 13px; border: 1px solid #ccc; border-radius: 2px; display: block; }
        .jj-list-link.editable input[type="text"] { width: calc(100% - 10px); }
        .jj-list-link.editable label { font-size: 12px; display: block; text-align: left; margin-top: 3px; color: #333; }
        .jj-list-tit.editable { display: flex; justify-content: space-between; align-items: center; padding: 5px; }
        .jj-list-tit.editable input[type="text"] { flex-grow: 1; text-align: center; font-weight: bold; font-size: 16px; margin-right: 10px;}

        .action-button { font-size: 12px; padding: 4px 8px; margin-left: 8px; background-color: #dc3545; color: white; border: none; border-radius: 2px; cursor: pointer; line-height: 1.2; }
        .action-button:hover { background-color: #c82333; }
        .action-button.add-link-btn { background-color: #28a745; display: block; margin: 10px auto 5px auto; }
        .action-button.add-link-btn:hover { background-color: #218838; }

        #statusMessage { text-align: center; padding: 8px; margin-bottom:15px; font-weight: bold; border-radius: 2px; }
        #statusMessage.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
        #statusMessage.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }

        /* Custom Modal Styles */
        #customModalOverlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.6); z-index: 1000; display: none; justify-content: center; align-items: center; }
        #customModal { background-color: #fff; padding: 25px; border-radius: 4px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); width: 90%; max-width: 450px; z-index: 1001; }
        #customModalTitle { margin-top: 0; margin-bottom: 20px; font-size: 20px; color: #333; text-align: center; }
        #customModalContent input[type="text"], #customModalContent textarea { width: calc(100% - 16px); padding: 10px; margin-bottom: 12px; border: 1px solid #ddd; border-radius: 2px; font-size: 14px; }
        #customModalContent label { display: block; margin-bottom: 5px; font-weight: bold; font-size: 14px; color: #555; }
        #customModalActions { text-align: right; margin-top: 25px; }
        #customModalActions button { padding: 10px 18px; margin-left: 10px; border: none; border-radius: 2px; cursor: pointer; font-size: 14px; font-weight: 500; }
        #customModalSave { background-color: #007bff; color: white; }
        #customModalSave:hover { background-color: #0056b3; }
        #customModalCancel { background-color: #6c757d; color: white; }
        #customModalCancel:hover { background-color: #545b62; }
    </style>
</head>
<body>
<div class="inner-center main">
  <div class="content-inside">

    <div id="search_form" class="search-section" data-search-engine="google">
        <div class="search-left">
            <div id="search_engine_display" class="search-engine-selector">
                <span id="current_search_engine_name">谷歌</span>
            </div>
            <div class="input-wrap" id="searchInputWrap">
                <input id="search_keyword" class="search-input" name="keyword" maxlength="100" autocomplete="off" type="text" placeholder="">
                <div class="clear-keyword" id="clear_keyword" title="清空">x</div>
            </div>
            <ul id="search_methods" class="search-methods">
              <li data-type="google" class="search-item google">谷歌</li>
              <li data-type="baidu" class="search-item baidu">百度</li>
            </ul>
        </div>
        <input class="search-submit" value="搜索" id="search_submit" type="button">  
    </div>

    <div id="statusMessage"></div>

    <div class="edit-mode-controls" id="editModeControlsContainer" style="display:none;">
        <button id="exitEditModeBtn">退出编辑模式</button>
        <button id="saveAllChangesBtn">保存所有更改</button>
        <button id="addCategoryBtn">添加新分类</button>
    </div>

    <div class="nav-content" id="navContent">
        </div>  
  </div>
</div>
<div class="footer"></div>

<div id="customModalOverlay">
    <div id="customModal">
        <h3 id="customModalTitle">Modal Title</h3>
        <div id="customModalContent">
            </div>
        <div id="customModalActions">
            <button id="customModalSave">保存</button>
            <button id="customModalCancel">取消</button>
        </div>
    </div>
</div>

<script>
document.addEventListener('DOMContentLoaded', function() {
    // --- Search Functionality ---
    const searchEngineDisplay = document.getElementById('search_engine_display');
    const currentSearchEngineName = document.getElementById('current_search_engine_name');
    const searchMethodsList = document.getElementById('search_methods');
    const searchForm = document.getElementById('search_form');
    const searchKeywordInput = document.getElementById('search_keyword');
    const searchSubmitButton = document.getElementById('search_submit');
    const clearKeywordButton = document.getElementById('clear_keyword');

    if (searchEngineDisplay) {
        searchEngineDisplay.addEventListener('click', function(event) {
            event.stopPropagation();
            if (searchMethodsList) {
                searchMethodsList.style.display = searchMethodsList.style.display === 'block' ? 'none' : 'block';
            }
        });
    }
    document.addEventListener('click', function(event) {
        if (searchMethodsList && searchMethodsList.style.display === 'block' &&
            searchEngineDisplay && !searchEngineDisplay.contains(event.target) &&
            !searchMethodsList.contains(event.target)) {
            searchMethodsList.style.display = 'none';
        }
    });
    if (searchMethodsList) {
        searchMethodsList.addEventListener('click', function(event) {
            event.stopPropagation();
            if (event.target.tagName === 'LI') {
                const selectedType = event.target.getAttribute('data-type');
                const selectedName = event.target.textContent;
                if (currentSearchEngineName) currentSearchEngineName.textContent = selectedName;
                if (searchForm) searchForm.setAttribute('data-search-engine', selectedType);
                searchMethodsList.style.display = 'none';
                // Placeholder does not change
            }
        });
    }
    if (searchSubmitButton) {
        searchSubmitButton.addEventListener('click', function() {
            if (!searchKeywordInput || !searchForm) return;
            const keyword = searchKeywordInput.value.trim();
            if (!keyword) {
                searchKeywordInput.focus();
                return;
            }
            const engine = searchForm.getAttribute('data-search-engine');
            let url = '';
            if (engine === 'baidu') {
                url = `https://www.baidu.com/s?wd=${encodeURIComponent(keyword)}`;
            } else if (engine === 'google') {
                url = `https://www.google.com/search?q=${encodeURIComponent(keyword)}`;
            }
            if (url) window.open(url, '_blank');
        });
    }
    if (searchKeywordInput) {
        searchKeywordInput.addEventListener('keypress', function(event) {
            if (event.key === 'Enter' && searchSubmitButton) searchSubmitButton.click();
        });
        searchKeywordInput.addEventListener('input', function() {
            if (clearKeywordButton) {
                clearKeywordButton.style.display = searchKeywordInput.value.length > 0 ? 'block' : 'none';
            }
        });
    }
    if (clearKeywordButton) {
        clearKeywordButton.addEventListener('click', function() {
            if (searchKeywordInput) {
                searchKeywordInput.value = '';
                searchKeywordInput.focus();
            }
            clearKeywordButton.style.display = 'none';
        });
    }
    // --- End of Search Functionality ---

    // --- Navigation Editing Functionality ---
    const navContentContainer = document.getElementById('navContent');
    const editModeControlsContainer = document.getElementById('editModeControlsContainer');
    const exitEditModeBtn = document.getElementById('exitEditModeBtn');
    const saveAllChangesBtn = document.getElementById('saveAllChangesBtn');
    const addCategoryBtn = document.getElementById('addCategoryBtn');
    const statusMessageEl = document.getElementById('statusMessage');
    const searchInputWrap = document.getElementById('searchInputWrap'); 

    // Modal elements
    const customModalOverlay = document.getElementById('customModalOverlay');
    const customModalTitleEl = document.getElementById('customModalTitle');
    const customModalContentEl = document.getElementById('customModalContent');
    const customModalSaveBtn = document.getElementById('customModalSave');
    const customModalCancelBtn = document.getElementById('customModalCancel');
    let currentModalSaveHandler = null;

    let navigationData = []; 
    let isEditMode = false;

    // 6-click edit mode trigger
    let clickCount = 0;
    let clickTimer = null;
    const CLICK_DELAY_MS = 400; 
    const REQUIRED_CLICKS = 6;

    if (searchInputWrap) {
        searchInputWrap.addEventListener('click', () => {
            clickCount++;
            if (clickTimer) clearTimeout(clickTimer);
            clickTimer = setTimeout(() => {
                clickCount = 0; 
            }, CLICK_DELAY_MS * (REQUIRED_CLICKS -1) );
            if (clickCount === REQUIRED_CLICKS) {
                clearTimeout(clickTimer);
                clickCount = 0;
                if (!isEditMode) {
                    isEditMode = true;
                    if(editModeControlsContainer) editModeControlsContainer.style.display = 'block';
                    renderNavigation(); 
                    showStatusMessage('已进入编辑模式。', 'success', 2000); 
                }
            }
        });
    }

    function showStatusMessage(message, type = 'success', duration = 3000) {
        if (!statusMessageEl) return;
        statusMessageEl.textContent = message;
        statusMessageEl.className = type; 
        setTimeout(() => {  
            statusMessageEl.textContent = ''; 
            statusMessageEl.className = '';
        }, duration);
    }

    function renderNavigation() {
        if (!navContentContainer) return;
        navContentContainer.innerHTML = ''; 

        if (navigationData.length === 0 && !isEditMode) {
            navContentContainer.innerHTML = '<p style="text-align:center; color: #777; margin-top: 20px;">导航数据为空。尝试进入编辑模式添加内容,或检查 navigation_data.json 文件。</p>';
            return;
        }
         if (navigationData.length === 0 && isEditMode) {
            navContentContainer.innerHTML = '<p style="text-align:center; color: #777; margin-top: 20px;">导航数据为空。点击 "添加新分类" 开始。</p>';
        }

        navigationData.forEach((category, catIndex) => {
            const catDiv = document.createElement('div');
            catDiv.className = 'jj-list';
            const titleDiv = document.createElement('div');
            titleDiv.className = 'jj-list-tit';
            if (isEditMode) {
                titleDiv.classList.add('editable');
                const titleInput = document.createElement('input');
                titleInput.type = 'text';
                titleInput.value = category.categoryTitle;
                titleInput.onchange = (e) => { 
                    navigationData[catIndex].categoryTitle = e.target.value.trim();
                };
                titleDiv.appendChild(titleInput);
                const removeCategoryBtn = document.createElement('button');
                removeCategoryBtn.textContent = '删除分类';
                removeCategoryBtn.className = 'action-button';
                removeCategoryBtn.onclick = () => removeCategory(catIndex);
                titleDiv.appendChild(removeCategoryBtn);
            } else {
                titleDiv.textContent = category.categoryTitle;
            }
            catDiv.appendChild(titleDiv);
            const ul = document.createElement('ul');
            ul.className = 'jj-list-con';
            category.links.forEach((link, linkIndex) => {
                const li = document.createElement('li');
                const a = document.createElement('a');
                a.className = 'jj-list-link';
                if (isEditMode) {
                    a.classList.add('editable');
                    const nameLabel = document.createElement('label');
                    nameLabel.textContent = '名称:';
                    const textInput = document.createElement('input');
                    textInput.type = 'text';
                    textInput.value = link.text;
                    textInput.placeholder = 'Link Name';
                    textInput.onchange = (e) => { 
                        navigationData[catIndex].links[linkIndex].text = e.target.value.trim();
                    };
                    const urlLabel = document.createElement('label');
                    urlLabel.textContent = 'URL:';
                    const urlInput = document.createElement('input');
                    urlInput.type = 'text';
                    urlInput.value = link.url;
                    urlInput.placeholder = 'Link URL';
                    urlInput.onchange = (e) => { 
                        navigationData[catIndex].links[linkIndex].url = e.target.value.trim();
                    };
                    const removeBtn = document.createElement('button');
                    removeBtn.textContent = 'X';
                    removeBtn.title = '删除此链接';
                    removeBtn.className = 'action-button';
                    removeBtn.style.cssText = 'float: right; margin-top: 0; padding: 2px 5px;'; 
                    removeBtn.onclick = (event) => {
                        event.preventDefault(); 
                        event.stopPropagation(); 
                        removeLink(catIndex, linkIndex);
                    };
                    a.appendChild(removeBtn); 
                    a.appendChild(nameLabel);
                    a.appendChild(textInput);
                    a.appendChild(urlLabel);
                    a.appendChild(urlInput);
                } else {
                    a.href = link.url || '#'; 
                    a.target = '_blank';
                    const textSpan = document.createElement('span');
                    textSpan.className = 'link-text';
                    textSpan.textContent = link.text;
                    a.appendChild(textSpan);
                }
                li.appendChild(a);
                ul.appendChild(li);
            });
            catDiv.appendChild(ul);
            if(isEditMode){
                const addLinkBtn = document.createElement('button');
                addLinkBtn.textContent = '为此分类添加链接';
                addLinkBtn.className = 'action-button add-link-btn';
                addLinkBtn.onclick = () => showAddLinkModal(catIndex);
                catDiv.appendChild(addLinkBtn);
            }
            navContentContainer.appendChild(catDiv);
        });
    }
    
    // --- Modal Functions ---
    function showModal(title, contentHTML, saveHandler) {
        if (!customModalOverlay || !customModalTitleEl || !customModalContentEl) return;
        customModalTitleEl.textContent = title;
        customModalContentEl.innerHTML = contentHTML;
        customModalOverlay.style.display = 'flex';
        currentModalSaveHandler = saveHandler;
        const firstInput = customModalContentEl.querySelector('input[type="text"], textarea');
        if (firstInput) {
            firstInput.focus();
        }
    }

    function hideModal() {
        if (!customModalOverlay || !customModalContentEl) return;
        customModalOverlay.style.display = 'none';
        customModalContentEl.innerHTML = '';
        currentModalSaveHandler = null;
    }

    if (customModalSaveBtn) {
        customModalSaveBtn.onclick = () => {
            if (currentModalSaveHandler) {
                currentModalSaveHandler(); 
            }
        };
    }
    if (customModalCancelBtn) customModalCancelBtn.onclick = hideModal;
    // --- End Modal Functions ---

    function showAddCategoryModal() {
        const contentHTML = `
            <label for="newCategoryTitleInput">分类名称:</label>
            <input type="text" id="newCategoryTitleInput" placeholder="输入新分类的名称">
        `;
        showModal("添加新分类", contentHTML, () => {
            const titleInput = document.getElementById('newCategoryTitleInput');
            if (!titleInput) return;
            const newCategoryTitle = titleInput.value.trim();
            if (newCategoryTitle) {
                navigationData.push({ categoryTitle: newCategoryTitle, links: [] });
                renderNavigation();
                hideModal();
                 showStatusMessage(`分类 "${newCategoryTitle}" 已添加 (未保存)`, 'success');
            } else {
                titleInput.style.border = "1px solid red"; 
                setTimeout(()=> { if(titleInput) titleInput.style.border = ""; }, 2000);
                titleInput.focus();
            }
        });
    }
    
    function showAddLinkModal(catIndex) {
        if (!navigationData[catIndex]) return;
        const contentHTML = `
            <label for="newLinkTextInput">链接名称:</label>
            <input type="text" id="newLinkTextInput" placeholder="例如:谷歌">
            <label for="newLinkUrlInput">链接 URL:</label>
            <input type="text" id="newLinkUrlInput" placeholder="例如:https://www.google.com">
        `;
        showModal(`为 "${navigationData[catIndex].categoryTitle}" 添加链接`, contentHTML, () => {
            const textInput = document.getElementById('newLinkTextInput');
            const urlInput = document.getElementById('newLinkUrlInput');
            if (!textInput || !urlInput) return;
            const newLinkText = textInput.value.trim();
            const newLinkUrl = urlInput.value.trim();
            let isValid = true;

            if (!newLinkText) {
                textInput.style.border = "1px solid red"; isValid = false;
            } else {
                textInput.style.border = "";
            }
            if (!newLinkUrl) {
                urlInput.style.border = "1px solid red"; isValid = false;
            } else {
                if (!newLinkUrl.toLowerCase().startsWith('http://') && !newLinkUrl.toLowerCase().startsWith('https://') && !newLinkUrl.toLowerCase().startsWith('ftp://')) {
                    urlInput.style.border = "1px solid red"; isValid = false;
                } else {
                    urlInput.style.border = "";
                }
            }
            
            if (isValid) {
                navigationData[catIndex].links.push({ text: newLinkText, url: newLinkUrl });
                renderNavigation();
                hideModal();
                showStatusMessage(`链接 "${newLinkText}" 已添加到 "${navigationData[catIndex].categoryTitle}" (未保存)`, 'success');
            } else {
                if (!newLinkText && textInput) textInput.focus();
                else if (urlInput) urlInput.focus();
                setTimeout(()=> {
                    if(textInput) textInput.style.border = "";
                    if(urlInput) urlInput.style.border = "";
                }, 2000);
            }
        });
    }

    function removeLink(catIndex, linkIndex) {
        // No confirm dialog
        const removedLinkName = navigationData[catIndex].links[linkIndex].text;
        navigationData[catIndex].links.splice(linkIndex, 1);
        renderNavigation();
        showStatusMessage(`链接 "${removedLinkName}" 已删除 (未保存)`, 'success');
    }

    function removeCategory(catIndex) {
        // No confirm dialog
        const removedCategoryName = navigationData[catIndex].categoryTitle;
        navigationData.splice(catIndex, 1);
        renderNavigation();
        showStatusMessage(`分类 "${removedCategoryName}" 已删除 (未保存)`, 'success');
    }

    // --- Data Loading and Saving ---
    const DATA_SOURCE_URL = 'navigation_data.json'; 
    const SAVE_ENDPOINT_URL = 'save_navigation.php';

    async function loadNavigationData() {
        try {
            const response = await fetch(DATA_SOURCE_URL + '?t=' + new Date().getTime()); 
            if (!response.ok) {
                if (response.status === 404) {
                    console.warn(`'${DATA_SOURCE_URL}' not found. Using fallback data. Create this file to use your own data.`);
                    navigationData = getExampleFallbackData(); 
                    showStatusMessage(`导航文件 '${DATA_SOURCE_URL}' 未找到, 已加载示例数据。`, 'error', 5000);
                } else {
                    throw new Error(`HTTP error! status: ${response.status} while fetching ${DATA_SOURCE_URL}`);
                }
            } else {
                const data = await response.json();
                if (data && Array.isArray(data)) {
                    navigationData = data;
                    // No success message for loading data as per user request
                    if (data.length === 0) {
                         console.log(`从 '${DATA_SOURCE_URL}' 加载到空导航数据。`);
                    }
                } else {
                    console.warn(`Data from '${DATA_SOURCE_URL}' is not a valid array. Using fallback data.`);
                    navigationData = getExampleFallbackData();
                    showStatusMessage(`'${DATA_SOURCE_URL}' 中的数据格式无效, 已加载示例数据。`, 'error', 5000);
                }
            }
        } catch (error) { 
            console.error("无法从服务器或文件加载导航:", error);
            showStatusMessage(`加载导航失败: ${error.message}. 已加载示例数据。`, 'error', 5000);
            navigationData = getExampleFallbackData(); 
        } finally {
            renderNavigation(); 
        }
    }
    
    function getExampleFallbackData() {
        return [
            {
                "categoryTitle": "常用项目 (示例)",
                "links": [
                    { "text": "博客", "url": "https://wowood.cn/" },
                    { "text": "笔记", "url": "https://baicai.online/" }
                ]
            },
            {
                "categoryTitle": "实用工具 (示例)",
                "links": [
                    { "text": "谷歌翻译", "url": "https://translate.google.com/" }
                ]
            }
        ];
    }

    async function saveNavigationToServer() {
        if (!isEditMode) {
            showStatusMessage('不在编辑模式,无需保存。', 'error');
            return;
        }
        for (const category of navigationData) {
            if (!category.categoryTitle.trim()) {
                showStatusMessage('错误:分类名称不能为空!请修正后再保存。', 'error', 5000);
                return;
            }
            for (const link of category.links) {
                if (!link.text.trim() || !link.url.trim()) {
                    showStatusMessage(`错误:分类 "${category.categoryTitle}" 中的链接名称和URL均不能为空!`, 'error', 7000);
                    return;
                }
                 if (!link.url.toLowerCase().startsWith('http://') && !link.url.toLowerCase().startsWith('https://') && !link.url.toLowerCase().startsWith('ftp://')) {
                    showStatusMessage(`错误:链接 "${link.text}" 的URL格式无效 (需以 http://, https:// 或 ftp:// 开头)!`, 'error', 7000);
                    return;
                }
            }
        }
        showStatusMessage('正在保存到服务器...', 'success', 10000); 
        try {
            const response = await fetch(SAVE_ENDPOINT_URL, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(navigationData), 
            });
            const contentType = response.headers.get("content-type");
            let result;
            if (contentType && contentType.indexOf("application/json") !== -1) {
                result = await response.json();
            } else {
                const textResponse = await response.text();
                if (!response.ok) {
                     throw new Error(`服务器错误: ${response.status}. 响应: ${textResponse}`);
                }
                result = { status: 'success', message: textResponse || '保存成功 (非JSON响应)' };
            }
            if (response.ok && result.status === 'success') { 
                showStatusMessage(result.message || '导航已成功保存到服务器!', 'success');
            } else {
                throw new Error(result.message || `保存操作未成功 (服务器状态: ${response.status})`);
            }
        } catch (error) {
            console.error("保存导航到服务器失败:", error);
            showStatusMessage(`保存失败: ${error.message}`, 'error', 7000);
        }
    }

    if(exitEditModeBtn) {
        exitEditModeBtn.addEventListener('click', () => {
            if (isEditMode) { 
                // No confirm dialog
                isEditMode = false;
                if(editModeControlsContainer) editModeControlsContainer.style.display = 'none'; 
                loadNavigationData(); // Reload original data to discard local changes
                showStatusMessage('已退出编辑模式。未保存的更改已丢弃。', 'success', 2000); 
            }
        });
    }

    if(saveAllChangesBtn) saveAllChangesBtn.addEventListener('click', saveNavigationToServer);
    if(addCategoryBtn) addCategoryBtn.addEventListener('click', showAddCategoryModal);

    // --- Initial Load ---
    loadNavigationData();
    console.log("页面脚本已加载。导航数据将从 'navigation_data.json' 或回退到示例数据。");
});
</script>
</body>
</html>

save_navigation.php

<?php
header('Content-Type: application/json'); // Send JSON response back

$dataFile = 'navigation_data.json';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Get the raw POST data (which should be a JSON string)
    $jsonData = file_get_contents('php://input');

    if ($jsonData) {
        $data = json_decode($jsonData, true); // Decode JSON into a PHP array

        // Basic validation: check if json_decode was successful
        if (json_last_error() === JSON_ERROR_NONE) {
            // Overwrite the file with the new data
            // JSON_PRETTY_PRINT makes the file human-readable
            // JSON_UNESCAPED_UNICODE ensures Chinese characters are saved correctly
            if (file_put_contents($dataFile, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
                echo json_encode(['status' => 'success', 'message' => '导航已保存!']);
            } else {
                // http_response_code(500); // Internal Server Error
                echo json_encode(['status' => 'error', 'message' => '无法写入文件. 请检查权限.']);
            }
        } else {
            // http_response_code(400); // Bad Request
            echo json_encode(['status' => 'error', 'message' => '无效的JSON数据.']);
        }
    } else {
        // http_response_code(400); // Bad Request
        echo json_encode(['status' => 'error', 'message' => '没有收到数据.']);
    }
} else {
    // http_response_code(405); // Method Not Allowed
    echo json_encode(['status' => 'error', 'message' => '仅允许POST请求.']);
}
?>

load_navigation.php

<?php
header('Content-Type: application/json'); // Important: tell the browser it's JSON

$dataFile = 'navigation_data.json';

if (file_exists($dataFile)) {
    $contents = file_get_contents($dataFile);
    // Optionally validate if $contents is valid JSON, though if you control the save script, it should be.
    echo $contents;
} else {
    // If the file doesn't exist, you could return an empty array
    // or a default structure if you have one.
    // For simplicity, we'll return an empty JSON array,
    // the frontend will then use the static HTML as a base.
    echo json_encode([]);
}
?>

navigation_data.json

[
    {
        "categoryTitle": "常用项目",
        "links": [
            {
                "text": "博客",
                "url": "https:\/\/wowood.cn\/"
            },
            {
                "text": "笔记",
                "url": "https:\/\/baicai.online\/"
            },
            {
                "text": "网盘",
                "url": "https:\/\/download.wowood.cn\/"
            },
            {
                "text": "看板",
                "url": "https:\/\/plan.baicai.online\/"
            },
            {
                "text": "宝塔",
                "url": "http:\/\/111.229.144.188:8888\/"
            },
            {
                "text": "BI",
                "url": "https:\/\/work.jiushuyun.com\/decision\/shared\/e1f34feb"
            },
            {
                "text": "订阅",
                "url": "https:\/\/amz.baicai.online\/"
            },
            {
                "text": "领星",
                "url": "https:\/\/ruotai.lingxing.com\/"
            },
            {
                "text": "翻译",
                "url": "https:\/\/translate.google.com\/"
            }
        ]
    },
    {
        "categoryTitle": "跨境相关",
        "links": [
            {
                "text": "北美",
                "url": "https:\/\/www.amazon.com\/"
            },
            {
                "text": "加拿大",
                "url": "https:\/\/www.amazon.ca\/"
            },
            {
                "text": "V2EX",
                "url": "https:\/\/www.v2ex.com\/"
            },
            {
                "text": "微博",
                "url": "https:\/\/weibo.com\/"
            },
            {
                "text": "简书",
                "url": "https:\/\/www.jianshu.com\/"
            },
            {
                "text": "豆瓣",
                "url": "https:\/\/www.douban.com\/"
            },
            {
                "text": "产品经理",
                "url": "http:\/\/www.woshipm.com\/"
            },
            {
                "text": "产品壹佰",
                "url": "http:\/\/www.chanpin100.com\/"
            },
            {
                "text": "只言片语",
                "url": "http:\/\/pm.budong.me\/"
            }
        ]
    },
    {
        "categoryTitle": "实用工具",
        "links": [
            {
                "text": "腾讯课堂",
                "url": "https:\/\/ke.qq.com\/"
            },
            {
                "text": "网易云课堂",
                "url": "https:\/\/study.163.com\/"
            },
            {
                "text": "慕课网",
                "url": "https:\/\/www.imooc.com\/"
            },
            {
                "text": "哔哩哔哩",
                "url": "https:\/\/www.bilibili.com\/"
            },
            {
                "text": "爱奇艺",
                "url": "https:\/\/www.iqiyi.com\/"
            },
            {
                "text": "腾讯视频",
                "url": "https:\/\/v.qq.com\/"
            },
            {
                "text": "优酷",
                "url": "https:\/\/www.youku.com\/"
            },
            {
                "text": "1024",
                "url": "http:\/\/cpc.people.com.cn\/"
            },
            {
                "text": "人人影视",
                "url": "http:\/\/www.zimuzu.tv\/"
            }
        ]
    },
    {
        "categoryTitle": "优质博客",
        "links": [
            {
                "text": "36氪",
                "url": "https:\/\/www.36kr.com\/"
            },
            {
                "text": "虎嗅",
                "url": "https:\/\/www.huxiu.com\/"
            },
            {
                "text": "今日头条",
                "url": "https:\/\/www.toutiao.com\/"
            },
            {
                "text": "网易",
                "url": "https:\/\/www.163.com\/"
            },
            {
                "text": "少数派",
                "url": "https:\/\/sspai.com\/"
            },
            {
                "text": "谷歌镜像",
                "url": "http:\/\/ac.scmor.com\/"
            },
            {
                "text": "小仙女博客",
                "url": "http:\/\/crud.budong.me\/"
            },
            {
                "text": "Coding",
                "url": "https:\/\/coding.net\/"
            },
            {
                "text": "酷软清单",
                "url": "https:\/\/www.coolist.net\/"
            }
        ]
    },
    {
        "categoryTitle": "其他收藏",
        "links": [
            {
                "text": "百度地图",
                "url": "https:\/\/map.baidu.com\/"
            },
            {
                "text": "谷歌地图",
                "url": "http:\/\/www.google.cn\/maps"
            },
            {
                "text": "高德地图",
                "url": "https:\/\/www.amap.com\/"
            },
            {
                "text": "SM图床",
                "url": "https:\/\/sm.ms\/"
            },
            {
                "text": "程序员工具",
                "url": "https:\/\/tool.lu\/"
            },
            {
                "text": "前端工具",
                "url": "http:\/\/www.alloyteam.com\/nav\/"
            },
            {
                "text": "网盘搜索",
                "url": "http:\/\/www.99baiduyun.com\/"
            },
            {
                "text": "二维码",
                "url": "https:\/\/cli.im\/"
            },
            {
                "text": "条形码",
                "url": "http:\/\/www.qinms.com\/webapp\/barcode\/index.aspx"
            }
        ]
    },
    {
        "categoryTitle": "个人兴趣",
        "links": [
            {
                "text": "电影天堂",
                "url": "http:\/\/www.dytt8.net\/"
            },
            {
                "text": "图片素材",
                "url": "https:\/\/zhuanlan.zhihu.com\/p\/36456284"
            },
            {
                "text": "效率工具",
                "url": "https:\/\/www.portablesoft.org\/"
            },
            {
                "text": "淘宝",
                "url": "https:\/\/www.taobao.com\/"
            },
            {
                "text": "京东",
                "url": "https:\/\/www.jd.com\/"
            },
            {
                "text": "苏宁",
                "url": "https:\/\/www.suning.com\/"
            },
            {
                "text": "什么值得买",
                "url": "https:\/\/www.smzdm.com\/"
            },
            {
                "text": "Zealer",
                "url": "http:\/\/www.zealer.com\/"
            },
            {
                "text": "慢慢买",
                "url": "http:\/\/www.manmanbuy.com\/"
            }
        ]
    }
]