小巧路径转换器优化

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>路径转换器 - 带预设功能</title>
    <style>
        * { 
            box-sizing: border-box; 
            margin: 0; 
            padding: 0; 
            font-family: 'Segoe UI', sans-serif; 
        }
        
        body { 
            background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); 
            min-height: 100vh; 
            display: flex; 
            justify-content: center; 
            align-items: center; 
            padding: 15px; 
        }
        
        .container { 
            width: 100%; 
            max-width: 550px; 
            background: white; 
            border-radius: 10px; 
            box-shadow: 0 5px 15px rgba(0,0,0,0.1); 
            padding: 20px; 
        }
        
        h1 { 
            text-align: center; 
            margin-bottom: 10px; 
            color: #2c3e50; 
            font-size: 1.5rem; 
        }
        
        .subtitle { 
            text-align: center; 
            color: #7f8c8d; 
            margin-bottom: 20px; 
            font-size: 0.9rem; 
        }
        
        .input-group, .output-group { 
            margin-bottom: 15px; 
        }
        
        label { 
            display: block; 
            margin-bottom: 5px; 
            font-weight: 600; 
            color: #2c3e50; 
        }
        
        textarea { 
            width: 100%; 
            height: 70px; 
            padding: 10px; 
            border: 1px solid #ddd; 
            border-radius: 5px; 
            resize: vertical; 
            font-size: 0.9rem; 
        }
        
        textarea:focus { 
            outline: none; 
            border-color: #3498db; 
        }
        
        .output-box { 
            background: #f8f9fa; 
            border: 1px solid #3498db; 
            border-radius: 5px; 
            padding: 10px; 
            min-height: 60px; 
            word-break: break-all; 
            font-size: 0.9rem; 
            white-space: pre-wrap;
        }
        
        .buttons { 
            display: flex; 
            gap: 10px; 
            margin-bottom: 15px; 
        }
        
        button { 
            flex: 1; 
            padding: 8px; 
            border: none; 
            border-radius: 5px; 
            cursor: pointer; 
            font-weight: 600; 
            transition: all 0.2s;
        }
        
        button:hover {
            transform: translateY(-2px);
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
        }
        
        .clear-btn { 
            background: #e74c3c; 
            color: white; 
        }
        
        .copy-btn { 
            background: #2ecc71; 
            color: white; 
        }
        
        .add-preset-btn {
            background: #3498db;
            color: white;
        }
        
        .presets-section { 
            margin-top: 20px; 
        }
        
        .presets-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }
        
        .presets-header h3 { 
            margin: 0;
            font-size: 1rem; 
        }
        
        .preset-item { 
            padding: 8px; 
            background: #f8f9fa; 
            border-radius: 5px; 
            margin-bottom: 5px; 
            cursor: pointer; 
            font-size: 0.85rem; 
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        .preset-item:hover {
            background: #e9ecef;
        }
        
        .preset-actions {
            display: flex;
            gap: 5px;
        }
        
        .preset-edit, .preset-delete {
            background: none;
            border: none;
            cursor: pointer;
            font-size: 0.8rem;
            padding: 2px 5px;
            border-radius: 3px;
        }
        
        .preset-edit {
            color: #3498db;
        }
        
        .preset-delete {
            color: #e74c3c;
        }
        
        .preset-input {
            width: 100%;
            padding: 5px;
            border: 1px solid #ddd;
            border-radius: 3px;
            font-size: 0.85rem;
        }
        
        .notification { 
            position: fixed; 
            top: 15px; 
            right: 15px; 
            padding: 10px 15px; 
            background: #2ecc71; 
            color: white; 
            border-radius: 5px; 
            opacity: 0; 
            transition: opacity 0.3s; 
            z-index: 100;
        }
        
        .notification.show { 
            opacity: 1; 
        }
        
        .notification.error { 
            background: #e74c3c; 
        }
        
        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            z-index: 1000;
            justify-content: center;
            align-items: center;
        }
        
        .modal-content {
            background: white;
            padding: 20px;
            border-radius: 8px;
            width: 90%;
            max-width: 400px;
        }
        
        .modal h3 {
            margin-bottom: 15px;
        }
        
        .modal-buttons {
            display: flex;
            gap: 10px;
            margin-top: 15px;
        }
        
        .modal-buttons button {
            flex: 1;
        }
        
        .save-btn {
            background: #2ecc71;
            color: white;
        }
        
        .cancel-btn {
            background: #95a5a6;
            color: white;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>路径转换器</h1>
        <p class="subtitle">Windows路径 ↔ file:///URL 实时转换</p>
        
        <div class="input-group">
            <label>Windows路径:</label>
            <textarea id="inputPath" placeholder="例如:D:\html\Homepage\js\main.js"></textarea>
        </div>
        
        <div class="output-group">
            <label>转换结果:</label>
            <div id="outputPath" class="output-box">file:///d:/html/Homepage/js/main.js</div>
        </div>
        
        <div class="buttons">
            <button class="clear-btn" id="clearBtn">清空输入</button>
            <button class="copy-btn" id="copyBtn">复制结果</button>
            <button class="add-preset-btn" id="addPresetBtn">添加预设</button>
        </div>
        
        <div class="presets-section">
            <div class="presets-header">
                <h3>预设路径:</h3>
            </div>
            <div id="presetsList"></div>
        </div>
    </div>
    
    <div id="notification" class="notification"></div>
    
    <div id="editModal" class="modal">
        <div class="modal-content">
            <h3>编辑预设</h3>
            <input type="text" id="editPresetInput" class="preset-input">
            <div class="modal-buttons">
                <button class="save-btn" id="saveEditBtn">保存</button>
                <button class="cancel-btn" id="cancelEditBtn">取消</button>
            </div>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // DOM 元素
            const inputPath = document.getElementById('inputPath');
            const outputPath = document.getElementById('outputPath');
            const clearBtn = document.getElementById('clearBtn');
            const copyBtn = document.getElementById('copyBtn');
            const addPresetBtn = document.getElementById('addPresetBtn');
            const presetsList = document.getElementById('presetsList');
            const notification = document.getElementById('notification');
            const editModal = document.getElementById('editModal');
            const editPresetInput = document.getElementById('editPresetInput');
            const saveEditBtn = document.getElementById('saveEditBtn');
            const cancelEditBtn = document.getElementById('cancelEditBtn');

            // 默认预设(仅首次加载时使用)
            const DEFAULT_PRESETS = [
                'D:\\html\\Homepage\\js\\main.js',
                'C:\\Users\\YourName\\Documents\\index.html',
                'E:\\projects\\my site\\images\\photo.jpg',
                'D:\\工作文件\\项目\\数据.xlsx',
                'C:\\Program Files\\App\\config.ini'
            ];

            // 加载预设(优先用 localStorage,否则用默认值)
            let presets = JSON.parse(localStorage.getItem('pathConverterPresets')) || [...DEFAULT_PRESETS];
            let editingIndex = -1;

            // ======================
            // 工具函数
            // ======================

            // 清理路径:去引号、标准化
            function sanitizePath(path) {
                path = path.trim();
                if ((path.startsWith('"') && path.endsWith('"')) || 
                    (path.startsWith("'") && path.endsWith("'"))) {
                    path = path.slice(1, -1);
                }
                return path;
            }

            // 判断是否为有效 Windows 路径(简单校验)
            function isValidWindowsPath(path) {
                return /^[A-Za-z]:[\\\/]/.test(path);
            }

            // 转换 Windows 路径为 file:// URI
            function windowsPathToFileURI(winPath) {
                if (!winPath) return 'file:///d:/html/Homepage/js/main.js';

                const cleanPath = sanitizePath(winPath);
                if (!isValidWindowsPath(cleanPath)) {
                    // 非标准路径也尝试转换(容错)
                    const unixStyle = cleanPath.replace(/\\/g, '/');
                    return 'file:///' + encodeURIComponent(unixStyle).replace(/%2F/g, '/');
                }

                // 提取盘符(转小写)
                const drive = cleanPath.substring(0, 1).toLowerCase() + ':/';
                const restPath = cleanPath.substring(3).replace(/\\/g, '/');

                // 对路径部分进行 URI 编码(保留 /)
                const encodedRest = restPath
                    .split('/')
                    .map(part => encodeURIComponent(part))
                    .join('/');

                return 'file:///' + drive + encodedRest;
            }

            // 显示通知
            function showNotification(message, isError = false) {
                notification.textContent = message;
                notification.className = 'notification' + (isError ? ' error' : '');
                notification.classList.add('show');
                setTimeout(() => notification.classList.remove('show'), 2000);
            }

            // ======================
            // 核心功能
            // ======================

            function convertPath() {
                const result = windowsPathToFileURI(inputPath.value);
                outputPath.textContent = result;
            }

            function copyToClipboard() {
                const text = outputPath.textContent;
                if (!text || text.includes('file:///d:/html/Homepage/js/main.js') && !inputPath.value.trim()) {
                    showNotification('请输入路径后再复制', true);
                    return;
                }
                navigator.clipboard.writeText(text)
                    .then(() => showNotification('已复制!'))
                    .catch(() => showNotification('复制失败,请手动复制', true));
            }

            function clearInput() {
                inputPath.value = '';
                convertPath();
                inputPath.focus();
            }

            // 规范化路径用于比较(忽略斜杠和大小写)
            function normalizeForCompare(path) {
                return path.toLowerCase().replace(/\\/g, '/');
            }

            function addPreset() {
                const raw = inputPath.value.trim();
                if (!raw) {
                    showNotification('请输入路径后再添加预设', true);
                    return;
                }
                const clean = sanitizePath(raw);
                const normalized = normalizeForCompare(clean);

                if (presets.some(p => normalizeForCompare(p) === normalized)) {
                    showNotification('该预设已存在(忽略大小写和斜杠)', true);
                    return;
                }

                presets.push(clean);
                savePresets();
                renderPresets();
                showNotification('预设已添加');
            }

            function editPreset(index) {
                editingIndex = index;
                editPresetInput.value = presets[index];
                editModal.style.display = 'flex';
                editPresetInput.focus();
            }

            function saveEdit() {
                const newValue = sanitizePath(editPresetInput.value);
                if (!newValue) {
                    showNotification('预设不能为空', true);
                    return;
                }
                const normalizedNew = normalizeForCompare(newValue);
                if (presets.some((p, i) => i !== editingIndex && normalizeForCompare(p) === normalizedNew)) {
                    showNotification('该预设已存在', true);
                    return;
                }

                presets[editingIndex] = newValue;
                savePresets();
                renderPresets();
                closeEditModal();
                showNotification('预设已更新');
            }

            function deletePreset(index) {
                if (confirm('确定要删除这个预设吗?')) {
                    presets.splice(index, 1);
                    savePresets();
                    renderPresets();
                    showNotification('预设已删除');
                }
            }

            function usePreset(index) {
                inputPath.value = presets[index];
                convertPath();
                inputPath.focus();
            }

            function savePresets() {
                localStorage.setItem('pathConverterPresets', JSON.stringify(presets));
            }

            function closeEditModal() {
                editModal.style.display = 'none';
                editingIndex = -1;
            }

            // ======================
            // 渲染与事件
            // ======================

            function renderPresets() {
                presetsList.innerHTML = '';
                if (presets.length === 0) {
                    presetsList.innerHTML = '<div style="text-align: center; color: #7f8c8d; padding: 10px;">暂无预设,点击"添加预设"按钮创建</div>';
                    return;
                }
                presets.forEach((preset, index) => {
                    const item = document.createElement('div');
                    item.className = 'preset-item';
                    item.innerHTML = `
                        <span> ${preset}</span>
                        <div class="preset-actions">
                            <button class="preset-edit" data-index=" ${index}">编辑</button>
                            <button class="preset-delete" data-index=" ${index}">删除</button>
                        </div>
                    `;
                    item.addEventListener('click', e => {
                        if (!e.target.closest('.preset-edit') && !e.target.closest('.preset-delete')) {
                            usePreset(index);
                        }
                    });
                    item.querySelector('.preset-edit').addEventListener('click', e => {
                        e.stopPropagation();
                        editPreset(index);
                    });
                    item.querySelector('.preset-delete').addEventListener('click', e => {
                        e.stopPropagation();
                        deletePreset(index);
                    });
                    presetsList.appendChild(item);
                });
            }

            // ======================
            // 事件绑定
            // ======================

            clearBtn.addEventListener('click', clearInput);
            copyBtn.addEventListener('click', copyToClipboard);
            addPresetBtn.addEventListener('click', addPreset);
            inputPath.addEventListener('input', convertPath);

            saveEditBtn.addEventListener('click', saveEdit);
            cancelEditBtn.addEventListener('click', closeEditModal);

            // 键盘快捷键
            document.addEventListener('keydown', e => {
                if (e.key === 'Escape') {
                    if (editModal.style.display === 'flex') {
                        closeEditModal();
                    } else {
                        clearInput();
                    }
                }
            });

            // 初始化
            renderPresets();
            convertPath(); // 设置默认示例
            inputPath.focus();
        });
    </script>
</body>
</html>

备用

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>路径转换器</title>
    <style>
        :root {
            --primary: #3498db;
            --success: #2ecc71;
            --danger: #e74c3c;
            --dark: #2c3e50;
            --light: #f8f9fa;
            --gray: #7f8c8d;
            --border: #ddd;
        }
        
        * { 
            box-sizing: border-box; 
            margin: 0; 
            padding: 0; 
            font-family: 'Segoe UI', sans-serif; 
        }
        
        body { 
            background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); 
            min-height: 100vh; 
            display: flex; 
            justify-content: center; 
            align-items: center; 
            padding: 15px; 
        }
        
        .container { 
            width: 100%; 
            max-width: 550px; 
            background: white; 
            border-radius: 10px; 
            box-shadow: 0 5px 15px rgba(0,0,0,0.1); 
            padding: 20px; 
        }
        
        h1 { 
            text-align: center; 
            margin-bottom: 10px; 
            color: var(--dark); 
            font-size: 1.5rem; 
        }
        
        .subtitle { 
            text-align: center; 
            color: var(--gray); 
            margin-bottom: 20px; 
            font-size: 0.9rem; 
        }
        
        .input-group, .output-group { 
            margin-bottom: 15px; 
        }
        
        label { 
            display: block; 
            margin-bottom: 5px; 
            font-weight: 600; 
            color: var(--dark); 
        }
        
        textarea { 
            width: 100%; 
            height: 70px; 
            padding: 10px; 
            border: 1px solid var(--border); 
            border-radius: 5px; 
            resize: vertical; 
            font-size: 0.9rem; 
        }
        
        textarea:focus { 
            outline: none; 
            border-color: var(--primary); 
        }
        
        .output-box { 
            background: var(--light); 
            border: 1px solid var(--primary); 
            border-radius: 5px; 
            padding: 10px; 
            min-height: 60px; 
            word-break: break-all; 
            font-size: 0.9rem; 
        }
        
        .buttons { 
            display: flex; 
            gap: 10px; 
            margin-bottom: 15px; 
        }
        
        button { 
            flex: 1; 
            padding: 8px; 
            border: none; 
            border-radius: 5px; 
            cursor: pointer; 
            font-weight: 600; 
            transition: all 0.2s;
        }
        
        button:hover {
            transform: translateY(-2px);
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
        }
        
        .clear-btn { 
            background: var(--danger); 
            color: white; 
        }
        
        .copy-btn { 
            background: var(--success); 
            color: white; 
        }
        
        .add-preset-btn {
            background: var(--primary);
            color: white;
        }
        
        .presets-section { 
            margin-top: 20px; 
        }
        
        .presets-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }
        
        .presets-header h3 { 
            margin: 0;
            font-size: 1rem; 
        }
        
        .preset-item { 
            padding: 8px; 
            background: var(--light); 
            border-radius: 5px; 
            margin-bottom: 5px; 
            cursor: pointer; 
            font-size: 0.85rem; 
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        .preset-item:hover {
            background: #e9ecef;
        }
        
        .preset-actions {
            display: flex;
            gap: 5px;
        }
        
        .preset-edit, .preset-delete {
            background: none;
            border: none;
            cursor: pointer;
            font-size: 0.8rem;
            padding: 2px 5px;
            border-radius: 3px;
        }
        
        .preset-edit {
            color: var(--primary);
        }
        
        .preset-delete {
            color: var(--danger);
        }
        
        .notification { 
            position: fixed; 
            top: 15px; 
            right: 15px; 
            padding: 10px 15px; 
            background: var(--success); 
            color: white; 
            border-radius: 5px; 
            opacity: 0; 
            transition: opacity 0.3s; 
            z-index: 100;
        }
        
        .notification.show { 
            opacity: 1; 
        }
        
        .notification.error { 
            background: var(--danger); 
        }
        
        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            z-index: 1000;
            justify-content: center;
            align-items: center;
        }
        
        .modal-content {
            background: white;
            padding: 20px;
            border-radius: 8px;
            width: 90%;
            max-width: 400px;
        }
        
        .modal h3 {
            margin-bottom: 15px;
        }
        
        .modal-buttons {
            display: flex;
            gap: 10px;
            margin-top: 15px;
        }
        
        .modal-buttons button {
            flex: 1;
        }
        
        .save-btn {
            background: var(--success);
            color: white;
        }
        
        .cancel-btn {
            background: var(--gray);
            color: white;
        }
        
        .empty-state {
            text-align: center; 
            color: var(--gray); 
            padding: 10px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>路径转换器</h1>
        <p class="subtitle">Windows路径 ↔ file:///URL 实时转换</p>
        
        <div class="input-group">
            <label>Windows路径:</label>
            <textarea id="inputPath" placeholder="例如:D:\html\Homepage\js\main.js"></textarea>
        </div>
        
        <div class="output-group">
            <label>转换结果:</label>
            <div id="outputPath" class="output-box">file:///d:/html/Homepage/js/main.js</div>
        </div>
        
        <div class="buttons">
            <button class="clear-btn" id="clearBtn">清空输入</button>
            <button class="copy-btn" id="copyBtn">复制结果</button>
            <button class="add-preset-btn" id="addPresetBtn">添加预设</button>
        </div>
        
        <div class="presets-section">
            <div class="presets-header">
                <h3>预设路径:</h3>
            </div>
            <div id="presetsList"></div>
        </div>
    </div>
    
    <div id="notification" class="notification"></div>
    
    <div id="editModal" class="modal">
        <div class="modal-content">
            <h3>编辑预设</h3>
            <input type="text" id="editPresetInput" class="preset-input" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
            <div class="modal-buttons">
                <button class="save-btn" id="saveEditBtn">保存</button>
                <button class="cancel-btn" id="cancelEditBtn">取消</button>
            </div>
        </div>
    </div>

    <script>
        // 路径转换器应用
        class PathConverter {
            constructor() {
                this.elements = {
                    inputPath: document.getElementById('inputPath'),
                    outputPath: document.getElementById('outputPath'),
                    clearBtn: document.getElementById('clearBtn'),
                    copyBtn: document.getElementById('copyBtn'),
                    addPresetBtn: document.getElementById('addPresetBtn'),
                    presetsList: document.getElementById('presetsList'),
                    notification: document.getElementById('notification'),
                    editModal: document.getElementById('editModal'),
                    editPresetInput: document.getElementById('editPresetInput'),
                    saveEditBtn: document.getElementById('saveEditBtn'),
                    cancelEditBtn: document.getElementById('cancelEditBtn')
                };
                
                this.presets = JSON.parse(localStorage.getItem('pathConverterPresets')) || [
                    'D:\\html\\Homepage\\js\\main.js',
                    'C:\\Users\\YourName\\Documents\\index.html',
                    'E:\\projects\\my site\\images\\photo.jpg',
                    'D:\\工作文件\\项目\\数据.xlsx',
                    'C:\\Program Files\\App\\config.ini'
                ];
                
                this.editingIndex = -1;
                
                this.init();
            }
            
            init() {
                this.renderPresets();
                this.bindEvents();
                this.elements.inputPath.focus();
            }
            
            bindEvents() {
                // 按钮事件
                this.elements.clearBtn.addEventListener('click', () => this.clearInput());
                this.elements.copyBtn.addEventListener('click', () => this.copyToClipboard());
                this.elements.addPresetBtn.addEventListener('click', () => this.addPreset());
                
                // 输入事件
                this.elements.inputPath.addEventListener('input', () => this.convertPath());
                
                // 模态框事件
                this.elements.saveEditBtn.addEventListener('click', () => this.saveEdit());
                this.elements.cancelEditBtn.addEventListener('click', () => this.closeModal());
                
                // 键盘快捷键
                document.addEventListener('keydown', (e) => this.handleKeyboard(e));
            }
            
            // 路径转换逻辑
            convertPath() {
                let input = this.elements.inputPath.value.trim();
                
                if (!input) {
                    this.elements.outputPath.textContent = 'file:///d:/html/Homepage/js/main.js';
                    return;
                }
                
                // 清理输入(去除引号)
                input = this.cleanInput(input);
                
                // 转换路径
                const converted = this.convertWindowsPath(input);
                
                // 显示结果
                this.elements.outputPath.textContent = converted;
            }
            
            cleanInput(input) {
                if ((input.startsWith('"') && input.endsWith('"')) || 
                    (input.startsWith("'") && input.endsWith("'"))) {
                    return input.substring(1, input.length - 1);
                }
                return input;
            }
            
            convertWindowsPath(input) {
                // 替换反斜杠为正斜杠
                let converted = input.replace(/\\/g, '/');
                
                // 确保盘符后的冒号是小写
                if (converted.match(/^[A-Za-z]:/)) {
                    converted = converted.charAt(0).toLowerCase() + converted.substring(1);
                }
                
                // 处理盘符和路径部分
                let driveLetter = '';
                let pathPart = converted;
                
                if (converted.match(/^[a-z]:\//)) {
                    driveLetter = converted.substring(0, 3);
                    pathPart = converted.substring(3);
                }
                
                // 编码路径(保留空格)
                const encodedPath = encodeURI(pathPart).replace(/%20/g, ' ');
                
                return 'file:///' + driveLetter + encodedPath;
            }
            
            copyToClipboard() {
                const text = this.elements.outputPath.textContent;
                
                if (!text || text === 'file:///d:/html/Homepage/js/main.js') {
                    this.showNotification('没有可复制的内容', true);
                    return;
                }
                
                navigator.clipboard.writeText(text)
                    .then(() => this.showNotification('已复制!'))
                    .catch(() => this.showNotification('复制失败', true));
            }
            
            clearInput() {
                this.elements.inputPath.value = '';
                this.elements.outputPath.textContent = 'file:///d:/html/Homepage/js/main.js';
                this.elements.inputPath.focus();
            }
            
            // 预设管理
            addPreset() {
                const input = this.elements.inputPath.value.trim();
                
                if (!input) {
                    this.showNotification('请输入路径后再添加预设', true);
                    return;
                }
                
                if (this.presets.includes(input)) {
                    this.showNotification('该预设已存在', true);
                    return;
                }
                
                this.presets.push(input);
                this.savePresets();
                this.renderPresets();
                this.showNotification('预设已添加');
            }
            
            editPreset(index) {
                this.editingIndex = index;
                this.elements.editPresetInput.value = this.presets[index];
                this.elements.editModal.style.display = 'flex';
                this.elements.editPresetInput.focus();
            }
            
            saveEdit() {
                if (this.editingIndex === -1) return;
                
                const newValue = this.elements.editPresetInput.value.trim();
                if (!newValue) {
                    this.showNotification('预设不能为空', true);
                    return;
                }
                
                this.presets[this.editingIndex] = newValue;
                this.savePresets();
                this.renderPresets();
                this.closeModal();
                this.showNotification('预设已更新');
            }
            
            deletePreset(index) {
                if (confirm('确定要删除这个预设吗?')) {
                    this.presets.splice(index, 1);
                    this.savePresets();
                    this.renderPresets();
                    this.showNotification('预设已删除');
                }
            }
            
            usePreset(index) {
                this.elements.inputPath.value = this.presets[index];
                this.convertPath();
                this.elements.inputPath.focus();
            }
            
            savePresets() {
                localStorage.setItem('pathConverterPresets', JSON.stringify(this.presets));
            }
            
            renderPresets() {
                this.elements.presetsList.innerHTML = '';
                
                if (this.presets.length === 0) {
                    this.elements.presetsList.innerHTML = '<div class="empty-state">暂无预设,点击"添加预设"按钮创建</div>';
                    return;
                }
                
                this.presets.forEach((preset, index) => {
                    const presetItem = document.createElement('div');
                    presetItem.className = 'preset-item';
                    
                    presetItem.innerHTML = `
                        <span>${preset}</span>
                        <div class="preset-actions">
                            <button class="preset-edit" data-index="${index}">编辑</button>
                            <button class="preset-delete" data-index="${index}">删除</button>
                        </div>
                    `;
                    
                    // 点击预设项使用该预设
                    presetItem.addEventListener('click', (e) => {
                        if (!e.target.classList.contains('preset-edit') && 
                            !e.target.classList.contains('preset-delete')) {
                            this.usePreset(index);
                        }
                    });
                    
                    // 编辑按钮
                    presetItem.querySelector('.preset-edit').addEventListener('click', (e) => {
                        e.stopPropagation();
                        this.editPreset(index);
                    });
                    
                    // 删除按钮
                    presetItem.querySelector('.preset-delete').addEventListener('click', (e) => {
                        e.stopPropagation();
                        this.deletePreset(index);
                    });
                    
                    this.elements.presetsList.appendChild(presetItem);
                });
            }
            
            // 辅助功能
            showNotification(message, isError = false) {
                this.elements.notification.textContent = message;
                this.elements.notification.className = 'notification' + (isError ? ' error' : '');
                this.elements.notification.classList.add('show');
                setTimeout(() => this.elements.notification.classList.remove('show'), 2000);
            }
            
            closeModal() {
                this.elements.editModal.style.display = 'none';
                this.editingIndex = -1;
            }
            
            handleKeyboard(e) {
                if (e.ctrlKey && e.key === 'c' && document.activeElement !== this.elements.inputPath) {
                    e.preventDefault();
                    this.copyToClipboard();
                }
                if (e.key === 'Escape') {
                    if (this.elements.editModal.style.display === 'flex') {
                        this.closeModal();
                    } else {
                        this.clearInput();
                    }
                }
            }
        }
        
        // 初始化应用
        document.addEventListener('DOMContentLoaded', () => {
            new PathConverter();
        });
    </script>
</body>
</html>
相关推荐
阿宇爱吃鱼2 小时前
uniapp input输入框,限制金额输入格式
前端·javascript·uni-app
Dreamy smile2 小时前
JavaScript 实现 HTTPS SSE 连接
开发语言·javascript·https
coding随想2 小时前
Web SQL Database API:一段被时代淘汰的浏览器存储技术
前端·数据库·sql
Marshmallowc2 小时前
React 刷新页面 Token 消失?深度解析 Redux + LocalStorage 数据持久化方案与 Hook 避坑指南
javascript·react·数据持久化·redux·前端工程化
Dreamy smile2 小时前
vue3 vite pinia实现动态路由,菜单权限,按钮权限
前端·javascript·vue.js
翱翔的苍鹰2 小时前
智谱(Zhipu)大模型的流式使用 response.iter_lines() 逐行解析 SSE 流
服务器·前端·数据库
未来之窗软件服务2 小时前
仙盟创梦IDE-集成开发测试:自动解析驱动的多线路自动化测试
前端·测试自动化·仙盟创梦ide·东方仙盟
天天睡大觉3 小时前
python命名规则(PEP8编码规则)
开发语言·前端·python
2501_944521593 小时前
Flutter for OpenHarmony 微动漫App实战:底部导航实现
android·开发语言·前端·javascript·redis·flutter·ecmascript