Spaces:
Running
Running
| <html lang="ru"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>🌊 Karnet Manager Pro v3.0 - Морской интерфейс</title> | |
| <style> | |
| :root { | |
| --sea-bg: #f0f8ff; | |
| --sea-fg: #2f4f4f; | |
| --sea-accent: #4682b4; | |
| --sea-success: #20b2aa; | |
| --sea-warning: #daa520; | |
| --sea-error: #cd5c5c; | |
| --sea-button: #e0f6ff; | |
| --sea-button-hover: #87ceeb; | |
| --sea-frame: #f5f5f5; | |
| --sea-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| --sea-transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| color: var(--sea-fg); | |
| position: relative; | |
| } | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"><path fill="%234682b4" fill-opacity="0.1" d="M0,96L48,112C96,128,192,160,288,160C384,160,480,128,576,122.7C672,117,768,139,864,138.7C960,139,1056,117,1152,106.7C1248,96,1344,96,1392,96L1440,96L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"></path></svg>') no-repeat bottom; | |
| background-size: cover; | |
| pointer-events: none; | |
| opacity: 0.3; | |
| } | |
| .container { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| header { | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(10px); | |
| border-radius: 20px; | |
| padding: 25px; | |
| margin-bottom: 30px; | |
| box-shadow: var(--sea-shadow); | |
| animation: slideDown 0.5s ease-out; | |
| } | |
| @keyframes slideDown { | |
| from { | |
| transform: translateY(-20px); | |
| opacity: 0; | |
| } | |
| to { | |
| transform: translateY(0); | |
| opacity: 1; | |
| } | |
| } | |
| h1 { | |
| color: var(--sea-accent); | |
| font-size: 2.5em; | |
| margin-bottom: 10px; | |
| display: flex; | |
| align-items: center; | |
| gap: 15px; | |
| } | |
| .subtitle { | |
| color: var(--sea-fg); | |
| font-size: 1.1em; | |
| opacity: 0.8; | |
| } | |
| .tabs { | |
| display: flex; | |
| gap: 10px; | |
| margin-bottom: 30px; | |
| flex-wrap: wrap; | |
| background: rgba(255, 255, 255, 0.9); | |
| padding: 10px; | |
| border-radius: 15px; | |
| box-shadow: var(--sea-shadow); | |
| } | |
| .tab-button { | |
| padding: 12px 24px; | |
| background: var(--sea-button); | |
| border: none; | |
| border-radius: 10px; | |
| cursor: pointer; | |
| font-size: 1em; | |
| font-weight: 600; | |
| color: var(--sea-fg); | |
| transition: var(--sea-transition); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .tab-button::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: -100%; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent); | |
| transition: left 0.5s; | |
| } | |
| .tab-button:hover::before { | |
| left: 100%; | |
| } | |
| .tab-button:hover { | |
| background: var(--sea-button-hover); | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 12px rgba(70, 130, 180, 0.3); | |
| } | |
| .tab-button.active { | |
| background: var(--sea-accent); | |
| color: white; | |
| } | |
| .tab-content { | |
| display: none; | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(10px); | |
| border-radius: 20px; | |
| padding: 30px; | |
| box-shadow: var(--sea-shadow); | |
| animation: fadeIn 0.5s ease-out; | |
| } | |
| .tab-content.active { | |
| display: block; | |
| } | |
| @keyframes fadeIn { | |
| from { | |
| opacity: 0; | |
| transform: translateY(10px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .form-group { | |
| margin-bottom: 25px; | |
| } | |
| label { | |
| display: block; | |
| margin-bottom: 8px; | |
| font-weight: 600; | |
| color: var(--sea-fg); | |
| } | |
| input[type="text"], | |
| input[type="email"], | |
| select, | |
| textarea { | |
| width: 100%; | |
| padding: 12px; | |
| border: 2px solid #e0e0e0; | |
| border-radius: 10px; | |
| font-size: 1em; | |
| transition: var(--sea-transition); | |
| background: white; | |
| } | |
| input:focus, | |
| select:focus, | |
| textarea:focus { | |
| outline: none; | |
| border-color: var(--sea-accent); | |
| box-shadow: 0 0 0 3px rgba(70, 130, 180, 0.1); | |
| } | |
| .button-group { | |
| display: flex; | |
| gap: 15px; | |
| flex-wrap: wrap; | |
| margin-top: 20px; | |
| } | |
| button { | |
| padding: 12px 24px; | |
| background: var(--sea-accent); | |
| color: white; | |
| border: none; | |
| border-radius: 10px; | |
| cursor: pointer; | |
| font-size: 1em; | |
| font-weight: 600; | |
| transition: var(--sea-transition); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| button::after { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 0; | |
| height: 0; | |
| border-radius: 50%; | |
| background: rgba(255, 255, 255, 0.5); | |
| transform: translate(-50%, -50%); | |
| transition: width 0.6s, height 0.6s; | |
| } | |
| button:hover::after { | |
| width: 300px; | |
| height: 300px; | |
| } | |
| button:hover { | |
| background: var(--sea-button-hover); | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 12px rgba(70, 130, 180, 0.3); | |
| } | |
| button:active { | |
| transform: translateY(0); | |
| } | |
| button.secondary { | |
| background: var(--sea-button); | |
| color: var(--sea-fg); | |
| } | |
| button.success { | |
| background: var(--sea-success); | |
| } | |
| button.warning { | |
| background: var(--sea-warning); | |
| } | |
| button.error { | |
| background: var(--sea-error); | |
| } | |
| .log-container { | |
| background: #1e1e1e; | |
| color: #00ff00; | |
| padding: 20px; | |
| border-radius: 10px; | |
| font-family: 'Consolas', 'Monaco', monospace; | |
| font-size: 0.9em; | |
| height: 300px; | |
| overflow-y: auto; | |
| margin-top: 20px; | |
| box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3); | |
| } | |
| .log-entry { | |
| margin-bottom: 5px; | |
| padding: 5px; | |
| border-radius: 3px; | |
| transition: background 0.2s; | |
| } | |
| .log-entry:hover { | |
| background: rgba(255, 255, 255, 0.05); | |
| } | |
| .log-entry.info { | |
| color: #87ceeb; | |
| } | |
| .log-entry.success { | |
| color: #90ee90; | |
| } | |
| .log-entry.warning { | |
| color: #ffd700; | |
| } | |
| .log-entry.error { | |
| color: #ff6b6b; | |
| } | |
| .status-bar { | |
| position: fixed; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| background: var(--sea-accent); | |
| color: white; | |
| padding: 15px 30px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| box-shadow: 0 -4px 6px rgba(0, 0, 0, 0.1); | |
| z-index: 1000; | |
| } | |
| .status-message { | |
| font-weight: 600; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .progress-bar { | |
| width: 200px; | |
| height: 6px; | |
| background: rgba(255, 255, 255, 0.3); | |
| border-radius: 3px; | |
| overflow: hidden; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| background: white; | |
| border-radius: 3px; | |
| animation: progress 2s ease-in-out infinite; | |
| } | |
| @keyframes progress { | |
| 0% { width: 0%; } | |
| 50% { width: 70%; } | |
| 100% { width: 0%; } | |
| } | |
| .file-list { | |
| background: var(--sea-frame); | |
| border-radius: 10px; | |
| padding: 15px; | |
| margin-top: 15px; | |
| max-height: 200px; | |
| overflow-y: auto; | |
| } | |
| .file-item { | |
| padding: 10px; | |
| background: white; | |
| border-radius: 8px; | |
| margin-bottom: 10px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| transition: var(--sea-transition); | |
| } | |
| .file-item:hover { | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | |
| transform: translateX(5px); | |
| } | |
| .checkbox-group { | |
| display: flex; | |
| gap: 20px; | |
| margin: 15px 0; | |
| flex-wrap: wrap; | |
| } | |
| .checkbox-wrapper { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| input[type="checkbox"] { | |
| width: 20px; | |
| height: 20px; | |
| cursor: pointer; | |
| } | |
| .stats-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 20px; | |
| margin-top: 20px; | |
| } | |
| .stat-card { | |
| background: linear-gradient(135deg, var(--sea-accent), var(--sea-button-hover)); | |
| color: white; | |
| padding: 20px; | |
| border-radius: 15px; | |
| text-align: center; | |
| transition: var(--sea-transition); | |
| } | |
| .stat-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 10px 20px rgba(70, 130, 180, 0.3); | |
| } | |
| .stat-number { | |
| font-size: 2.5em; | |
| font-weight: bold; | |
| margin-bottom: 5px; | |
| } | |
| .stat-label { | |
| font-size: 0.9em; | |
| opacity: 0.9; | |
| } | |
| .modal { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(0, 0, 0, 0.5); | |
| z-index: 2000; | |
| animation: fadeIn 0.3s; | |
| } | |
| .modal.active { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| .modal-content { | |
| background: white; | |
| border-radius: 20px; | |
| padding: 30px; | |
| max-width: 500px; | |
| width: 90%; | |
| animation: slideUp 0.3s; | |
| } | |
| @keyframes slideUp { | |
| from { | |
| transform: translateY(20px); | |
| opacity: 0; | |
| } | |
| to { | |
| transform: translateY(0); | |
| opacity: 1; | |
| } | |
| } | |
| .toast { | |
| position: fixed; | |
| top: 20px; | |
| right: 20px; | |
| padding: 15px 20px; | |
| border-radius: 10px; | |
| color: white; | |
| font-weight: 600; | |
| z-index: 3000; | |
| animation: slideInRight 0.3s; | |
| display: none; | |
| } | |
| .toast.show { | |
| display: block; | |
| } | |
| .toast.success { | |
| background: var(--sea-success); | |
| } | |
| .toast.error { | |
| background: var(--sea-error); | |
| } | |
| .toast.warning { | |
| background: var(--sea-warning); | |
| } | |
| .toast.info { | |
| background: var(--sea-accent); | |
| } | |
| @keyframes slideInRight { | |
| from { | |
| transform: translateX(100%); | |
| opacity: 0; | |
| } | |
| to { | |
| transform: translateX(0); | |
| opacity: 1; | |
| } | |
| } | |
| .file-input-wrapper { | |
| position: relative; | |
| overflow: hidden; | |
| display: inline-block; | |
| width: 100%; | |
| } | |
| .file-input-wrapper input[type=file] { | |
| position: absolute; | |
| left: -9999px; | |
| } | |
| .file-input-label { | |
| display: block; | |
| padding: 12px; | |
| background: var(--sea-button); | |
| border: 2px dashed var(--sea-accent); | |
| border-radius: 10px; | |
| text-align: center; | |
| cursor: pointer; | |
| transition: var(--sea-transition); | |
| } | |
| .file-input-label:hover { | |
| background: var(--sea-button-hover); | |
| border-style: solid; | |
| } | |
| @media (max-width: 768px) { | |
| .container { | |
| padding: 10px; | |
| } | |
| h1 { | |
| font-size: 1.8em; | |
| } | |
| .tabs { | |
| justify-content: center; | |
| } | |
| .tab-button { | |
| padding: 10px 16px; | |
| font-size: 0.9em; | |
| } | |
| .button-group { | |
| flex-direction: column; | |
| } | |
| button { | |
| width: 100%; | |
| } | |
| .stats-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| .loader { | |
| border: 3px solid #f3f3f3; | |
| border-top: 3px solid var(--sea-accent); | |
| border-radius: 50%; | |
| width: 40px; | |
| height: 40px; | |
| animation: spin 1s linear infinite; | |
| margin: 20px auto; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .floating { | |
| animation: floating 3s ease-in-out infinite; | |
| } | |
| @keyframes floating { | |
| 0% { transform: translateY(0px); } | |
| 50% { transform: translateY(-10px); } | |
| 100% { transform: translateY(0px); } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <header> | |
| <h1> | |
| <span class="floating">🌊</span> | |
| Karnet Manager Pro v3.0 | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" style="font-size: 0.4em; color: var(--sea-accent); text-decoration: none;">Built with anycoder</a> | |
| </h1> | |
| <p class="subtitle">Морской интерфейс для управления карнетами</p> | |
| </header> | |
| <nav class="tabs"> | |
| <button class="tab-button active" onclick="switchTab('email')"> | |
| 📧 Email Manager | |
| </button> | |
| <button class="tab-button" onclick="switchTab('karnet')"> | |
| 📊 Karnet Manager Pro | |
| </button> | |
| <button class="tab-button" onclick="switchTab('daily')"> | |
| 📅 Ежедневный Учет | |
| </button> | |
| <button class="tab-button" onclick="switchTab('settings')"> | |
| ⚙️ Настройки | |
| </button> | |
| </nav> | |
| <main> | |
| <!-- Email Manager Tab --> | |
| <div id="email" class="tab-content active"> | |
| <h2>📧 Outlook Attachment Saver</h2> | |
| <div class="form-group"> | |
| <label for="email">📧 Email:</label> | |
| <input type="email" id="email" placeholder="Введите email адрес"> | |
| </div> | |
| <div class="button-group"> | |
| <button onclick="runOutlookSaver()"> | |
| 🚀 Запустить OutlookAttachmentSaver | |
| </button> | |
| <button class="secondary" onclick="saveEmail()"> | |
| 💾 Сохранить Email | |
| </button> | |
| </div> | |
| <h3 style="margin-top: 30px;">📋 Лог операций</h3> | |
| <div id="emailLog" class="log-container"></div> | |
| </div> | |
| <!-- Karnet Manager Pro Tab --> | |
| <div id="karnet" class="tab-content"> | |
| <h2>📊 Karnet Manager Pro</h2> | |
| <div class="form-group"> | |
| <label for="karnetFolder">📁 Папка с Excel файлами:</label> | |
| <div style="display: flex; gap: 10px;"> | |
| <input type="text" id="karnetFolder" placeholder="Выберите папку с файлами"> | |
| <button onclick="browseFolder()">📁 Обзор</button> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label>🔍 Управление шаблоном</label> | |
| <div style="background: var(--sea-frame); padding: 15px; border-radius: 10px;"> | |
| <p id="templateInfo">📋 Мастер-шаблон: Шаблоны/Книга5_fixed.xlsx</p> | |
| <div class="button-group" style="margin-top: 15px;"> | |
| <button onclick="checkTemplate()">🔍 Проверить шаблон</button> | |
| <button class="success" onclick="createTemplate()">🛠️ Создать шаблон</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label>⚡ Быстрая обработка файлов</label> | |
| <div class="file-input-wrapper"> | |
| <input type="file" id="quickFiles" multiple accept=".xlsx,.xlsb"> | |
| <label for="quickFiles" class="file-input-label"> | |
| 📁 Выберите файлы для быстрой обработки | |
| </label> | |
| </div> | |
| <div id="quickFilesList" class="file-list"></div> | |
| <div class="checkbox-group"> | |
| <div class="checkbox-wrapper"> | |
| <input type="checkbox" id="quickToTarget" checked> | |
| <label for="quickToTarget">в Целевой файл</label> | |
| </div> | |
| <div class="checkbox-wrapper"> | |
| <input type="checkbox" id="quickToNew"> | |
| <label for="quickToNew">в Новый файл</label> | |
| </div> | |
| </div> | |
| <div class="button-group"> | |
| <button onclick="quickProcessFiles()">⚡ Обработать</button> | |
| <button class="secondary" onclick="clearQuickFiles()">🗑️ Очистить</button> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label for="targetFile">🎯 Целевой файл:</label> | |
| <div style="display: flex; gap: 10px;"> | |
| <input type="text" id="targetFile" placeholder="Выберите целевой файл"> | |
| <button onclick="browseTargetFile()">📄 Выбрать</button> | |
| </div> | |
| <div class="checkbox-group"> | |
| <div class="checkbox-wrapper"> | |
| <input type="checkbox" id="headerPresent" checked> | |
| <label for="headerPresent">Заголовки есть</label> | |
| </div> | |
| <div class="checkbox-wrapper"> | |
| <input type="checkbox" id="headerAbsent"> | |
| <label for="headerAbsent">Заголовков нет</label> | |
| </div> | |
| </div> | |
| <div class="button-group"> | |
| <button onclick="mergeKarnets()">⚡ Обработать</button> | |
| <button class="secondary" onclick="clearTargetFile()">🗑️ Очистить</button> | |
| <button class="success" onclick="mergeKarnetsToNewFile()"> | |
| 📥 Объединить в новый файл | |
| </button> | |
| </div> | |
| </div> | |
| <div class="stats-grid"> | |
| <div class="stat-card"> | |
| <div class="stat-number" id="filesProcessed">0</div> | |
| <div class="stat-label">Файлов обработано</div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-number" id="recordsProcessed">0</div> | |
| <div class="stat-label">Записей обработано</div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-number" id="errorsCount">0</div> | |
| <div class="stat-label">Ошибок</div> | |
| </div> | |
| </div> | |
| <h3 style="margin-top: 30px;">📋 Лог операций</h3> | |
| <div id="karnetLog" class="log-container"></div> | |
| </div> | |
| <!-- Daily Accounting Tab --> | |
| <div id="daily" class="tab-content"> | |
| <h2>📅 Ежедневный Учет</h2> | |
| <div class="form-group"> | |
| <label for="dailyFolder">📁 Папка с файлами:</label> | |
| <div style="display: flex; gap: 10px;"> | |
| <input type="text" id="dailyFolder" value="C:\\Work\\APP2\\Ежедневный"> | |
| <button onclick="browseDailyFolder()">📁 Обзор</button> | |
| </div> | |
| </div> | |
| <div style="display: grid; grid-template-columns: 1fr auto 1fr; gap: 20px; margin-top: 20px;"> | |
| <div> | |
| <h3>📄 Файлы и листы</h3> | |
| <div id="filesTree" style="background: var(--sea-frame); padding: 15px; border-radius: 10px; height: 300px; overflow-y: auto;"> | |
| <!-- Files tree will be populated here --> | |
| </div> | |
| </div> | |
| <div style="display: flex; flex-direction: column; justify-content: center; gap: 10px;"> | |
| <button onclick="moveSelectedSheet()">></button> | |
| <button onclick="moveAllSheets()">>></button> | |
| <button onclick="removeSelectedSheet()"><</button> | |
| <button onclick="clearSelectedSheets()">X</button> | |
| </div> | |
| <div> | |
| <h3>📋 Выбранные листы</h3> | |
| <div id="selectedTree" style="background: var(--sea-frame); padding: 15px; border-radius: 10px; height: 300px; overflow-y: auto;"> | |
| <!-- Selected sheets will be shown here --> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="button-group" style="margin-top: 20px;"> | |
| <button onclick="transferSheets()">📦 Перенести</button> | |
| <button class="success" onclick="mergeSheets()">🔗 Объединить</button> | |
| <button class="secondary" onclick="refreshFiles()">🔄 Обновить</button> | |
| </div> | |
| </div> | |
| <!-- Settings Tab --> | |
| <div id="settings" class="tab-content"> | |
| <h2>⚙️ Настройки приложения</h2> | |
| <div style="background: var(--sea-frame); padding: 20px; border-radius: 10px; margin-top: 20px;"> | |
| <h3>🌊 Karnet Manager Pro v3.0 - Морская версия</h3> | |
| <div style="margin-top: 20px;"> | |
| <h4>✨ Новые возможности:</h4> | |
| <ul style="margin-left: 20px; margin-top: 10px;"> | |
| <li>Морской интерфейс в спокойных тонах</li> | |
| <li>Создание и управление шаблоном Книга5_fixed.xlsx</li> | |
| <li>Быстрая обработка отдельных файлов карнетов</li> | |
| <li>Улучшенная проверка соответствия заголовков</li> | |
| <li>Детальная система логирования с эмодзи</li> | |
| <li>Оптимизированный процесс объединения карнетов</li> | |
| <li>Проверка отсутствующих заголовков в исходных файлах</li> | |
| </ul> | |
| </div> | |
| <p style="margin-top: 20px; opacity: 0.8;"> | |
| 💾 Все настройки автоматически сохраняются в config.json | |
| </p> | |
| </div> | |
| <div class="button-group" style="margin-top: 30px;"> | |
| <button onclick="saveSettings()">💾 Сохранить настройки</button> | |
| <button class="warning" onclick="resetSettings()">🔄 Сбросить настройки</button> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <div class="status-bar"> | |
| <div class="status-message"> | |
| <span id="statusIcon">🌊</span> | |
| <span id="statusText">Готов к работе</span> | |
| </div> | |
| <div class="progress-bar" id="progressBar" style="display: none;"> | |
| <div class="progress-fill"></div> | |
| </div> | |
| </div> | |
| <div id="toast" class="toast"></div> | |
| <div id="modal" class="modal"> | |
| <div class="modal-content"> | |
| <h3 id="modalTitle">Заголовок</h3> | |
| <p id="modalMessage">Сообщение</p> | |
| <button onclick="closeModal()" style="margin-top: 20px;">Закрыть</button> | |
| </div> | |
| </div> | |
| <script> | |
| // Global variables | |
| let config = { | |
| email: '', | |
| emailHistory: [], | |
| lastFolder: '', | |
| targetFile: '', | |
| templateFile: '', | |
| writeIntoTarget: true | |
| }; | |
| let stats = { | |
| filesProcessed: 0, | |
| recordsProcessed: 0, | |
| errorsCount: 0 | |
| }; | |
| let quickFiles = []; | |
| let selectedSheets = []; | |
| // Initialize app | |
| document.addEventListener('DOMContentLoaded', function() { | |
| loadConfig(); | |
| initializeEventListeners(); | |
| updateStats(); | |
| logMessage('emailLog', '🌊 Приложение запущено и готово к работе', 'info'); | |
| logMessage('karnetLog', '🌊 Karnet Manager Pro v3.0 инициализирован', 'info'); | |
| }); | |
| function initializeEventListeners() { | |
| // File input listeners | |
| document.getElementById('quickFiles').addEventListener('change', handleQuickFilesSelect); | |
| // Checkbox listeners | |
| document.getElementById('headerPresent').addEventListener('change', function() { | |
| if (this.checked) { | |
| document.getElementById('headerAbsent').checked = false; | |
| } | |
| }); | |
| document.getElementById('headerAbsent').addEventListener('change', function() { | |
| if (this.checked) { | |
| document.getElementById('headerPresent').checked = false; | |
| } | |
| }); | |
| document.getElementById('quickToTarget').addEventListener('change', function() { | |
| if (this.checked) { | |
| document.getElementById('quickToNew').checked = false; | |
| } | |
| }); | |
| document.getElementById('quickToNew').addEventListener('change', function() { | |
| if (this.checked) { | |
| document.getElementById('quickToTarget').checked = false; | |
| } | |
| }); | |
| } | |
| function switchTab(tabName) { | |
| // Hide all tabs | |
| document.querySelectorAll('.tab-content').forEach(tab => { | |
| tab.classList.remove('active'); | |
| }); | |
| // Remove active class from all buttons | |
| document.querySelectorAll('.tab-button').forEach(btn => { | |
| btn.classList.remove('active'); | |
| }); | |
| // Show selected tab | |
| document.getElementById(tabName).classList.add('active'); | |
| // Add active class to clicked button | |
| event.target.classList.add('active'); | |
| } | |
| function loadConfig() { | |
| const savedConfig = localStorage.getItem('karnetConfig'); | |
| if (savedConfig) { | |
| config = JSON.parse(savedConfig); | |
| document.getElementById('email').value = config.email || ''; | |
| document.getElementById('karnetFolder').value = config.lastFolder || ''; | |
| document.getElementById('targetFile').value = config.targetFile || ''; | |
| } | |
| } | |
| function saveConfig() { | |
| config.email = document.getElementById('email').value; | |
| config.lastFolder = document.getElementById('karnetFolder').value; | |
| config.targetFile = document.getElementById('targetFile').value; | |
| localStorage.setItem('karnetConfig', JSON.stringify(config)); | |
| showToast('Настройки сохранены', 'success'); | |
| } | |
| function logMessage(logId, message, level = 'info') { | |
| const log = document.getElementById(logId); | |
| const timestamp = new Date().toLocaleTimeString(); | |
| const entry = document.createElement('div'); | |
| entry.className = `log-entry ${level}`; | |
| const icons = { | |
| info: 'ℹ️', | |
| success: '✅', | |
| warning: '⚠️', | |
| error: '❌' | |
| }; | |
| entry.textContent = `[${timestamp}] ${icons[level] || 'ℹ️'} ${message}`; | |
| log.appendChild(entry); | |
| log.scrollTop = log.scrollHeight; | |
| } | |
| function updateStatus(message, level = 'info') { | |
| const statusText = document.getElementById('statusText'); | |
| const statusIcon = document.getElementById('statusIcon'); | |
| const icons = { | |
| info: '🌊', | |
| success: '🐚', | |
| warning: '⚓', | |
| error: '🦈', | |
| working: '🌀' | |
| }; | |
| statusIcon.textContent = icons[level] || '🌊'; | |
| statusText.textContent = message; | |
| } | |
| function showProgress(show = true) { | |
| document.getElementById('progressBar').style.display = show ? 'block' : 'none'; | |
| } | |
| function showToast(message, type = 'info') { | |
| const toast = document.getElementById('toast'); | |
| toast.textContent = message; | |
| toast.className = `toast ${type} show`; | |
| setTimeout(() => { | |
| toast.classList.remove('show'); | |
| }, 3000); | |
| } | |
| function showModal(title, message) { | |
| document.getElementById('modalTitle').textContent = title; | |
| document.getElementById('modalMessage').textContent = message; | |
| document.getElementById('modal').classList.add('active'); | |
| } | |
| function closeModal() { | |
| document.getElementById('modal').classList.remove('active'); | |
| } | |
| function updateStats() { | |
| document.getElementById('filesProcessed').textContent = stats.filesProcessed; | |
| document.getElementById('recordsProcessed').textContent = stats.recordsProcessed; | |
| document.getElementById('errorsCount').textContent = stats.errorsCount; | |
| } | |
| // Email Manager functions | |
| function runOutlookSaver() { | |
| updateStatus('Запуск OutlookAttachmentSaver...', 'working'); | |
| showProgress(true); | |
| logMessage('emailLog', 'Запуск OutlookAttachmentSaver...', 'info'); | |
| // Simulate process | |
| setTimeout(() => { | |
| logMessage('emailLog', 'OutlookAttachmentSaver успешно выполнен', 'success'); | |
| updateStatus('OutlookAttachmentSaver выполнен', 'success'); | |
| showProgress(false); | |
| showToast('OutlookAttachmentSaver выполнен успешно', 'success'); | |
| }, 2000); | |
| } | |
| function saveEmail() { | |
| const email = document.getElementById('email').value.trim(); | |
| if (!email) { | |
| showToast('Введите email адрес', 'warning'); | |
| return; | |
| } | |
| if (!config.emailHistory.includes(email)) { | |
| config.emailHistory.push(email); | |
| } | |
| config.email = email; | |
| saveConfig(); | |
| logMessage('emailLog', `Email ${email} сохранен`, 'success'); | |
| showToast('Email сохранен', 'success'); | |
| } | |
| // Karnet Manager functions | |
| function browseFolder() { | |
| // Simulate folder selection | |
| const folder = 'C:\\Work\\Karnets\\2024'; | |
| document.getElementById('karnetFolder').value = folder; | |
| config.lastFolder = folder; | |
| saveConfig(); | |
| logMessage('karnetLog', `Выбрана папка: ${folder}`, 'info'); | |
| } | |
| function browseTargetFile() { | |
| // Simulate file selection | |
| const file = 'C:\\Work\\Karnets\\Целевой файл.xlsm'; | |
| document.getElementById('targetFile').value = file; | |
| config.targetFile = file; | |
| saveConfig(); | |
| logMessage('karnetLog', `Выбран целевой файл: ${file}`, 'info'); | |
| } | |
| function checkTemplate() { | |
| updateStatus('Проверка шаблона...', 'working'); | |
| showProgress(true); | |
| logMessage('karnetLog', '🔍 Проверка соответствия с мастер-шаблоном', 'info'); | |
| setTimeout(() => { | |
| logMessage('karnetLog', '📋 Ожидаемых заголовков в шаблоне: 25', 'info'); | |
| logMessage('karnetLog', '📊 Обработано файлов: 12', 'info'); | |
| logMessage('karnetLog', '✅ Все заголовки соответствуют шаблону!', 'success'); | |
| updateStatus('Шаблон полностью соответствует', 'success'); | |
| showProgress(false); | |
| showToast('Проверка шаблона завершена успешно', 'success'); | |
| }, 3000); | |
| } | |
| function createTemplate() { | |
| updateStatus('Создание шаблона...', 'working'); | |
| showProgress(true); | |
| logMessage('karnetLog', '🛠️ Создание шаблона объединения карнетов...', 'info'); | |
| setTimeout(() => { | |
| logMessage('karnetLog', '🎉 Шаблон создан: Шаблоны/Шаблон_ |