From 6bd71a906f1331bfb135f9e754134d45692f0618 Mon Sep 17 00:00:00 2001 From: WASRUSGEN Date: Sat, 30 May 2026 15:12:02 +0300 Subject: [PATCH] feat: signature/stamp library with roles - whose sig, whose stamp, multi-party docs --- mockup.html | 351 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 330 insertions(+), 21 deletions(-) diff --git a/mockup.html b/mockup.html index 8ae3e29..8a669e4 100644 --- a/mockup.html +++ b/mockup.html @@ -2414,10 +2414,72 @@ body{font-family:var(--font-ui);background:var(--surf);color:var(--ink);line-hei
-
Кабинет

Реквизиты

-
-
Подпись и печать автоматически подставляются в документы. Загрузите один раз — и все документы будут подписаны 💛
+
Кабинет
+
+

Реквизиты

+
+
+
База подписей и печатей. Для каждой стороны документа выбирается нужная. Загрузите один раз — подставляются автоматически.
+
+ + +
+
Подписи
+
+ +
+
+ + +
+
Печати и штампы
+
+ +
+
+ +
+ + +
+

Реквизиты для документов

+
+ + + + + +
+
+ + +
+
+
+
+ +
+ + + + +
@@ -4833,17 +4895,18 @@ var STAMP_SIZES = { triangle: { w: 189, h: 135, label: 'Треугольная 35×50 мм' }, }; -function _getRequisiteImages() { - var sig = localStorage.getItem('zashita_sig') || null; - var stamp = localStorage.getItem('zashita_stamp') || null; - var stampType = (document.getElementById('stamp-type-sel') || {}).value - || localStorage.getItem('zashita_stamp_type') || 'round'; - return { sig: sig, stamp: stamp, stampType: stampType }; +function _getRequisiteImages(role) { + // Используем библиотеку с привязкой по роли + var sigImage = _getSigForRole(role); + var stampData = _getStampForRole(role); + return { sig: sigImage, stamp: stampData.image, stampType: stampData.type }; } -function _buildSignatureBlock(req, forPrint) { +function _buildSignatureBlock(req, forPrint, fromName, toName) { // req = {sig, stamp, stampType} if (!req.sig && !req.stamp) return ''; + fromName = fromName || ((_tplCurrent && _tplCurrent.parties && _tplCurrent.parties.from_name) || ''); + toName = toName || ((_tplCurrent && _tplCurrent.parties && _tplCurrent.parties.to_name) || ''); var stampSize = STAMP_SIZES[req.stampType] || STAMP_SIZES.round; // При печати: px → mm (96dpi: 1mm ≈ 3.78px) @@ -4856,20 +4919,19 @@ function _buildSignatureBlock(req, forPrint) { 'padding-top:' + (forPrint ? '6mm' : '16px') + ';' + 'border-top:1px solid ' + (forPrint ? '#ccc' : 'var(--line)') + '">'; - // Подпись слева + // Подпись + печать — наша сторона (слева) + html += '
'; + if (fromName) html += '
' + fromName + '
'; if (req.sig) { - html += '
' + - '
Подпись
' + - '' + - '
'; - } else { - html += '
'; + html += '
Подпись
' + + ''; } + html += '
'; - // Печать справа + // Печать — отдельный блок по центру-справа if (req.stamp) { - html += '
' + - '
М.П.
' + + html += '
' + + '
М.П.
' + '' + '
'; } @@ -7965,7 +8027,11 @@ function tab(name){ document.querySelectorAll('.tabpane').forEach(p=>p.classList.toggle('on',p.id==='p-'+name)); if(name==='sroki' && typeof renderDeadlines==='function') renderDeadlines(); if(name==='shab' && typeof renderContextTemplates==='function') renderContextTemplates(); - if(name==='requisites' && typeof _loadRequisites==='function') _loadRequisites(); + if(name==='requisites') { + if(typeof _loadRequisites==='function') _loadRequisites(); + if(typeof _renderSigLibrary==='function') _renderSigLibrary(); + if(typeof _renderStampLibrary==='function') _renderStampLibrary(); + } if(name==='casemap' && typeof renderCaseMap==='function') renderCaseMap(); if(name==='team' && typeof renderTeamDashboard==='function') renderTeamDashboard(); if(name==='docs') { @@ -9567,6 +9633,249 @@ function _toggleDocCheck(key, docId, checked) { } catch(e){} } +// ── БИБЛИОТЕКА ПОДПИСЕЙ И ПЕЧАТЕЙ ──────────────────────────────────────────── + +var _SIG_LIB_KEY = 'zashita_sig_library'; +var _STAMP_LIB_KEY = 'zashita_stamp_library'; + +function _getSigLib() { try { return JSON.parse(localStorage.getItem(_SIG_LIB_KEY) || '[]'); } catch(e){ return []; } } +function _getStampLib() { try { return JSON.parse(localStorage.getItem(_STAMP_LIB_KEY) || '[]'); } catch(e){ return []; } } + +function _saveSigLib(lib) { try { localStorage.setItem(_SIG_LIB_KEY, JSON.stringify(lib)); } catch(e){} } +function _saveStampLib(lib) { try { localStorage.setItem(_STAMP_LIB_KEY, JSON.stringify(lib)); } catch(e){} } + +// Рендер библиотеки подписей +function _renderSigLibrary() { + var el = document.getElementById('sig-library'); if (!el) return; + var lib = _getSigLib(); + if (!lib.length) { + el.innerHTML = '
Нет сохранённых подписей. Нажмите «+ Добавить».
'; + return; + } + el.innerHTML = lib.map(function(s, i) { + return '
' + + '' + + '
' + + '
' + s.label + '
' + + '
' + (s.role || 'Без привязки к роли') + '
' + + '
' + + '
' + + (!s.isDefault ? '' : '✓ По умолч.') + + '' + + '
' + + '
'; + }).join(''); +} + +// Рендер библиотеки печатей +function _renderStampLibrary() { + var el = document.getElementById('stamp-library'); if (!el) return; + var lib = _getStampLib(); + if (!lib.length) { + el.innerHTML = '
Нет сохранённых печатей. Нажмите «+ Добавить».
'; + return; + } + el.innerHTML = lib.map(function(s, i) { + var sz = STAMP_SIZES[s.stampType] || STAMP_SIZES.round; + return '
' + + '' + + '
' + + '
' + s.label + '
' + + '
' + sz.label + ' · ' + (s.role || 'Без привязки') + '
' + + '
' + + '
' + + (!s.isDefault ? '' : '✓ По умолч.') + + '' + + '
' + + '
'; + }).join(''); +} + +// Модал добавления подписи/печати +function _addSignatureModal() { + var old = document.getElementById('add-sig-modal'); if (old) old.remove(); + var modal = document.createElement('div'); + modal.id = 'add-sig-modal'; + modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1000;display:flex;align-items:flex-end;justify-content:center'; + modal.innerHTML = + '
' + + '
Добавить подпись или печать
' + + '
' + + '' + + '' + + '' + + '
' + + '
' + + '' + + '' + + '' + + '
' + + '' + + '' + + '
'; + document.body.appendChild(modal); + setTimeout(function(){ var i=document.getElementById('add-sig-label'); if(i) i.focus(); }, 200); +} + +var _pendingItemImage = null; + +function _addFromFile(input) { + var file = input.files[0]; if (!file) return; + var reader = new FileReader(); + reader.onload = function(e) { + var img = new Image(); + img.onload = function() { + var canvas = document.createElement('canvas'); + canvas.width = img.width; canvas.height = img.height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + var d = ctx.getImageData(0, 0, canvas.width, canvas.height); + for (var i = 0; i < d.data.length; i += 4) { + if (d.data[i] > 220 && d.data[i+1] > 220 && d.data[i+2] > 220) d.data[i+3] = 0; + } + ctx.putImageData(d, 0, 0); + _pendingItemImage = canvas.toDataURL('image/png'); + var prev = document.getElementById('add-sig-preview'); + var img2 = document.getElementById('add-sig-img'); + var btn = document.getElementById('add-sig-save-btn'); + if (prev) prev.style.display = ''; + if (img2) img2.src = _pendingItemImage; + if (btn) btn.style.display = ''; + }; + img.src = e.target.result; + }; + reader.readAsDataURL(file); +} + +function _addFromDraw() { + document.getElementById('add-sig-modal').remove(); + var drawModal = document.getElementById('sig-draw-modal'); + if (drawModal) { drawModal.style.display = 'flex'; _initSigCanvas(); } +} + +function _saveSigCanvasToLib() { + var c = document.getElementById('sig-canvas'); + if (!c) return; + _pendingItemImage = c.toDataURL('image/png'); + var drawModal = document.getElementById('sig-draw-modal'); + if (drawModal) drawModal.style.display = 'none'; + _addSignatureModal(); + setTimeout(function(){ + var prev = document.getElementById('add-sig-preview'); + var img2 = document.getElementById('add-sig-img'); + var btn = document.getElementById('add-sig-save-btn'); + if (prev) prev.style.display = ''; + if (img2) img2.src = _pendingItemImage; + if (btn) btn.style.display = ''; + }, 100); +} + +function _saveNewItem() { + if (!_pendingItemImage) { toast('Загрузите или нарисуйте изображение'); return; } + var label = (document.getElementById('add-sig-label') || {}).value || 'Без названия'; + var role = (document.getElementById('add-sig-role') || {}).value || ''; + var type = (document.getElementById('add-sig-type') || {}).value || 'signature'; + var isStamp = type.startsWith('stamp_'); + var stampType = isStamp ? type.replace('stamp_', '') : null; + + if (isStamp) { + var lib = _getStampLib(); + lib.push({ label: label, role: role, stampType: stampType, image: _pendingItemImage, isDefault: lib.length === 0 }); + _saveStampLib(lib); + // Обратная совместимость — первый штамп → legacy key + if (lib.length === 1) { localStorage.setItem('zashita_stamp', _pendingItemImage); localStorage.setItem('zashita_stamp_type', stampType); } + } else { + var lib2 = _getSigLib(); + lib2.push({ label: label, role: role, image: _pendingItemImage, isDefault: lib2.length === 0 }); + _saveSigLib(lib2); + // Обратная совместимость + if (lib2.length === 1) localStorage.setItem('zashita_sig', _pendingItemImage); + } + + _pendingItemImage = null; + document.getElementById('add-sig-modal').remove(); + _renderSigLibrary(); + _renderStampLibrary(); + toast('✅ Сохранено'); +} + +function _setSigDefault(idx) { + var lib = _getSigLib(); + lib.forEach(function(s,i){ s.isDefault = (i===idx); }); + _saveSigLib(lib); + // Обновляем legacy key + if (lib[idx]) localStorage.setItem('zashita_sig', lib[idx].image); + _renderSigLibrary(); +} + +function _setStampDefault(idx) { + var lib = _getStampLib(); + lib.forEach(function(s,i){ s.isDefault = (i===idx); }); + _saveStampLib(lib); + if (lib[idx]) { + localStorage.setItem('zashita_stamp', lib[idx].image); + localStorage.setItem('zashita_stamp_type', lib[idx].stampType || 'round'); + } + _renderStampLibrary(); +} + +function _deleteSig(idx) { + var lib = _getSigLib(); lib.splice(idx, 1); + if (lib.length && !lib.some(function(s){ return s.isDefault; })) lib[0].isDefault = true; + _saveSigLib(lib); + if (lib[0]) localStorage.setItem('zashita_sig', lib[0].image); else localStorage.removeItem('zashita_sig'); + _renderSigLibrary(); +} + +function _deleteStamp(idx) { + var lib = _getStampLib(); lib.splice(idx, 1); + if (lib.length && !lib.some(function(s){ return s.isDefault; })) lib[0].isDefault = true; + _saveStampLib(lib); + if (lib[0]) localStorage.setItem('zashita_stamp', lib[0].image); else localStorage.removeItem('zashita_stamp'); + _renderStampLibrary(); +} + +// Получить подпись/печать для конкретной стороны документа +function _getSigForRole(role) { + var lib = _getSigLib(); + if (!lib.length) return localStorage.getItem('zashita_sig') || null; + // Ищем по роли + if (role) { + var byRole = lib.find(function(s){ return s.role && s.role.toLowerCase().includes(role.toLowerCase()); }); + if (byRole) return byRole.image; + } + // По умолчанию + var def = lib.find(function(s){ return s.isDefault; }); + return def ? def.image : lib[0].image; +} + +function _getStampForRole(role) { + var lib = _getStampLib(); + if (!lib.length) return { image: localStorage.getItem('zashita_stamp'), type: localStorage.getItem('zashita_stamp_type') || 'round' }; + if (role) { + var byRole = lib.find(function(s){ return s.role && s.role.toLowerCase().includes(role.toLowerCase()); }); + if (byRole) return { image: byRole.image, type: byRole.stampType || 'round' }; + } + var def = lib.find(function(s){ return s.isDefault; }); + var item = def || lib[0]; + return { image: item.image, type: item.stampType || 'round' }; +} + // ── РЕКВИЗИТЫ — ПОДПИСЬ И ПЕЧАТЬ ──────────────────────────────────────────── function _uploadRequisite(type, input) {