125 lines
27 KiB
JavaScript
125 lines
27 KiB
JavaScript
import fs from 'node:fs';
|
||
import path from 'node:path';
|
||
|
||
const sourceRoot='/Users/anna/Desktop/Работа/данные для прототипа/Поисковый запрос 1/Ульяновский автомобильный завод';
|
||
const outputRoot=path.join(sourceRoot,'Карточки Бастион');
|
||
|
||
const read1251=file=>new TextDecoder('windows-1251').decode(fs.readFileSync(file));
|
||
const htmlEscape=value=>String(value??'').replace(/[&<>"']/g,char=>({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[char]));
|
||
const walk=dir=>fs.readdirSync(dir,{withFileTypes:true}).flatMap(entry=>{
|
||
const full=path.join(dir,entry.name);
|
||
return entry.isDirectory()?walk(full):[full];
|
||
});
|
||
const titleOf=file=>{
|
||
const match=read1251(file).match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
||
const raw=(match?.[1]||path.basename(file,'.html')).replace(/<[^>]*>/g,' ').replace(/\s+/g,' ').trim();
|
||
if(raw==='ПАО "НПО "Искра"')return 'ПАО «НПО «Искра»»';
|
||
const first=raw.indexOf('"'),last=raw.lastIndexOf('"');
|
||
return first>=0&&last>first?`${raw.slice(0,first)}«${raw.slice(first+1,last)}»${raw.slice(last+1)}`:raw;
|
||
};
|
||
const cleanText=file=>read1251(file).replace(/<style[\s\S]*?<\/style>|<script[\s\S]*?<\/script>/gi,' ').replace(/<[^>]*>/g,' ').replace(/ | /gi,' ').replace(/\s+/g,' ').trim();
|
||
const toPosix=file=>file.split(path.sep).join('/');
|
||
const relativeHref=(from,to)=>toPosix(path.relative(path.dirname(from),to));
|
||
const htmlFiles=walk(sourceRoot).filter(file=>file.endsWith('.html')&&!file.includes(`${path.sep}Доп.информация${path.sep}`)&&!file.includes(`${path.sep}Карточки Бастион${path.sep}`));
|
||
const cards=htmlFiles.map(file=>{const rel=path.relative(sourceRoot,file);return {source:file,rel,out:path.join(outputRoot,rel),title:titleOf(file)}});
|
||
const personNames=new Set(['Сахабиев И.И.','Спирин А.А.','Ширинов А.Ш.','Швецов В.А.','Юрасов С.П.','Соболев Н.А.','Протасенко В.А.']);
|
||
const isPerson=title=>personNames.has(title);
|
||
const canonical=new Map();
|
||
for(const card of cards) if(!canonical.has(card.title)) canonical.set(card.title,card);
|
||
const uazTitle='ООО «Ульяновский автомобильный завод»';
|
||
const sourceTitle=title=>title.replaceAll('«','"').replaceAll('»','"');
|
||
|
||
const known={
|
||
[uazTitle]:{
|
||
short:'ООО «УАЗ»',inn:'7327077188',ogrn:'1167325054082',okpo:'05808600',
|
||
place:'432034, г. Ульяновск, шоссе Московское, д. 92',
|
||
activity:'Производство грузовых автомобилей; полноприводных автомобилей, автобусов и запасных частей.',
|
||
legal:'Включено в сводный реестр организаций ОПК. Предприятие создано в 1941 году.',
|
||
ownership:'100,000% — группа «СОЛЛЕРС», в том числе ПАО «Соллерс». Уставный капитал: 4 142 657 011,000 руб.',
|
||
finance:[[2019,39.5,-2.8],[2020,35.5,-2.1],[2021,45.879,1.276],[2022,57.1,2.4],[2023,69.8,3.2]]
|
||
},
|
||
'ПАО «Соллерс»':{inn:'3528079131',activity:'Деятельность головных офисов.',ownership:'Участник структуры собственности ООО «УАЗ».',finance:[[2020,84,1.2],[2021,98,2.8],[2022,112,4.7],[2023,129,6.1],[2024,151,8.5]]},
|
||
'ООО «Альтер Инвест»':{inn:'5047264657',ownership:'Участник структуры собственности ПАО «Соллерс».'},
|
||
'ПАО «ЗМЗ»':{inn:'5248004137',activity:'Производство автокомпонентов и двигателей.',ownership:'Участие ООО «УАЗ» в капитале: 97,823%.',finance:[[2021,1.5,.12],[2022,1.1,-.3],[2023,.68,.005],[2024,.57,-2.21]]},
|
||
'ООО «Уаз-Штамповочное производство»':{ownership:'Участие ООО «УАЗ» в капитале: 99,000%.'},
|
||
'АО «НРК-Р.О.С.Т.»':{legal:'Держатель реестра акционеров ООО «УАЗ».'},
|
||
'ООО «Промфининвест»':{legal:'Связанная организация ООО «УАЗ».'},
|
||
'АО «Редуктор-ПМ»':{inn:'5948017501',activity:'Производство авиационных редукторов и трансмиссий.',finance:[[2021,6.6,.7],[2022,7.1,1.2],[2023,7.6,1.6],[2024,8.4,1.9]]},
|
||
'ПАО «НПО «Искра»»':{inn:'5907001774',activity:'Производство оборудования специального назначения.',finance:[[2021,12.4,-4.7],[2022,10.6,-.8],[2023,9.2,.2],[2024,9.78,.31]]},
|
||
'ООО «Соллерс Алабуга»':{activity:'Производство автотранспортных средств.'},
|
||
'ООО «Серенити Инвест»':{legal:'Связанная организация группы «СОЛЛЕРС».'},
|
||
'ООО «Аурус»':{activity:'Производство автомобилей представительского класса.'},
|
||
'ООО «Соллерс пассажирский транспорт»':{activity:'Производство автобусов.'},
|
||
'ООО «Соллерс Холдинг Менеджмент»':{activity:'Консультирование по вопросам коммерческой деятельности и управления.'},
|
||
'ООО «Автоцентр На Коломяжском»':{inn:'7839342583',activity:'Аренда и управление нежилым недвижимым имуществом.',finance:[[2022,.0415,-.0074],[2023,.0491,.0063],[2024,.0579,.01],[2025,.064,.021]]}
|
||
};
|
||
const uazBrief=cleanText(path.join(sourceRoot,'Доп.информация','ООО _УАЗ_(справка).html')).replace(/^.*?Краткая справка\s*/,'').replace(/Источник информации.*$/,'').trim();
|
||
const uazHistory=cleanText(path.join(sourceRoot,'Доп.информация','ООО _УАЗ_(ист. справка).html')).replace(/^.*?Историческая справка\s*/,'').replace(/\(ЕОЭ.*$/,'').trim();
|
||
const uazSpecialization=cleanText(path.join(sourceRoot,'Доп.информация','ООО _УАЗ_(специализация).html')).replace(/^.*?(Основные направления деятельности|Специализация)\s*/,'').trim();
|
||
|
||
const css=`
|
||
:root{--page:#2b2b2b;--panel:#3c3f41;--inner:#333638;--line:#55595b;--text:rgba(255,255,255,.8);--heading:#a8cded;--link:#5497d2;--external:#0779f4;--blue:#184e79;--radius:18px}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:radial-gradient(circle at 70% 0,#383d40 0,#2b2b2b 48%);color:var(--text);font-family:"Clear Sans","Arial",sans-serif;font-weight:700;line-height:1.45}.topbar{height:48px;background:var(--panel);border-bottom:1px solid var(--line);display:flex;align-items:center;padding:0 22px;gap:0}.topbar a{height:48px;padding:0 18px;display:flex;align-items:center;border-left:1px solid var(--line);color:var(--text);text-decoration:none;font-size:15px}.topbar a:first-child{border-left:0}.topbar a:hover{background:#2b2b2b;color:var(--heading)}main{width:min(1844px,calc(100% - 72px));margin:33px auto 50px}.hero{margin-bottom:26px}.kicker{margin:0 0 15px;color:#fff;font-size:25px}.title-pill,.notice{padding:15px 18px;border-radius:12px;background:#184e79;font-size:15px;display:inline-block}.notice{margin-top:14px;background:var(--panel);display:block}.nav{display:flex;flex-wrap:wrap;gap:12px;margin:16px 0 20px}.nav a,.related a,.person-link{color:var(--link);text-decoration:underline;text-underline-offset:3px}.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:28px}.grid.full{grid-template-columns:1fr}.card{background:var(--panel);border-radius:var(--radius);overflow:hidden;margin-bottom:28px;min-width:0}.card-head{padding:23px 33px 0;display:flex;align-items:center;justify-content:space-between;gap:12px}.card h2{margin:0;color:var(--heading);font-size:24px;line-height:1.3}.card h3{margin:22px 33px 10px;color:var(--heading);font-size:18px}.inner{margin:12px 33px 25px;padding:18px;background:var(--inner);border-radius:10px}.kv{display:grid;grid-template-columns:minmax(150px,34%) 1fr;gap:8px 20px}.kv b{color:#dbe9f3}.text{white-space:pre-line}.expand{flex:0 0 auto;border:0;border-radius:8px;background:#2b2b2b;color:#e6e6e6;padding:8px 12px;font:inherit;cursor:pointer}.expand:hover{background:#202020}.external{color:var(--external)!important}.ownership{display:grid;grid-template-columns:290px 1fr;gap:22px;align-items:center}.donut{width:220px;aspect-ratio:1;border-radius:50%;background:conic-gradient(#5497d2 0 100%,transparent 0);margin:auto;display:grid;place-items:center}.donut:after{content:"100,000%";width:124px;aspect-ratio:1;border-radius:50%;display:grid;place-items:center;background:var(--inner);border:1px solid #a8cded;color:#fff}.source-ownership{display:block;width:min(100%,630px);height:370px;margin:18px auto 0;border:1px solid var(--line);border-radius:10px;background:#fff;filter:grayscale(.7) brightness(.82)}.chart{width:100%;height:240px;background:var(--inner);border-radius:10px;padding:10px}.chart svg{width:100%;height:100%;overflow:visible}.chart text{fill:#dce9f2;font:12px Arial}.chart .gridline{stroke:#5a5f61;stroke-width:1}.chart .line{fill:none;stroke:#5497d2;stroke-width:3}.chart .profit{stroke:#76b78c}.chart circle{fill:#5497d2;stroke:#fff;stroke-width:1.5}.person-layout{display:grid;grid-template-columns:478px 639px minmax(0,1fr);gap:29px;align-items:start}.profile,.person-card{background:var(--panel);border-radius:18px;min-height:435px;padding:36px 33px}.avatar{width:238px;height:238px;margin:0 auto 33px;border-radius:50%;display:grid;place-items:center;background:linear-gradient(145deg,#55775f,#2f4a38);color:#e9f3e8;font-size:58px;border:1px solid #8bb08f}.profile h1{margin:0;color:var(--heading);font-size:24px;text-align:center}.profile p{text-align:center;font-size:20px}.profile .external{display:block;text-align:center;font-size:20px}.person-card{padding:23px 33px}.person-card h2{margin:0 0 20px;color:var(--heading);font-size:24px}.person-card p{font-size:20px;margin:0 0 14px}.person-card.bio{grid-column:2 / 4;min-height:435px}.list{display:grid;gap:14px}.related{display:flex;flex-wrap:wrap;gap:12px}.related a{padding:9px 12px;border:1px solid #5d99bd;border-radius:8px;background:#30373b;text-decoration:none}.related a:hover{background:#1e506f}dialog{border:0;border-radius:18px;padding:0;max-width:min(950px,calc(100% - 36px));background:var(--panel);color:var(--text);box-shadow:0 24px 80px #000}dialog::backdrop{background:rgba(0,0,0,.68)}.modal-content{padding:30px;max-height:76vh;overflow:auto}.modal-close{float:right;border:0;border-radius:8px;background:#2b2b2b;color:#fff;padding:8px 11px;font:inherit;cursor:pointer}.directory{background:var(--panel);border-radius:18px;overflow:hidden}.directory table{width:100%;border-collapse:collapse}.directory th,.directory td{padding:14px 18px;border-bottom:1px solid var(--line);text-align:left}.directory th{color:var(--heading)}.directory a{color:var(--link);text-decoration:underline}@media(max-width:1120px){.person-layout{grid-template-columns:1fr 1fr}.profile{grid-row:span 2}.person-card.bio{grid-column:1 / -1}}@media(max-width:760px){main{width:calc(100% - 28px);margin-top:20px}.grid,.person-layout{grid-template-columns:1fr}.profile,.person-card.bio{grid-column:auto;grid-row:auto}.ownership{grid-template-columns:1fr}.source-ownership{height:250px}.card-head,.profile,.person-card{padding-left:20px;padding-right:20px}.card h3{margin-left:20px}.inner{margin-left:20px;margin-right:20px}.kv{grid-template-columns:1fr}.topbar{padding:0;overflow:auto}.topbar a{white-space:nowrap;padding:0 12px}}
|
||
`;
|
||
|
||
const modalScript=`<dialog id="modal"><div class="modal-content"><button class="modal-close" onclick="this.closest('dialog').close()">Закрыть</button><div id="modalBody"></div></div></dialog><script>document.querySelectorAll('[data-modal]').forEach(button=>button.addEventListener('click',()=>{const source=document.querySelector(button.dataset.modal);document.querySelector('#modalBody').innerHTML=source.innerHTML;document.querySelector('#modal').showModal()}));</script>`;
|
||
const head=(title)=>`<!doctype html><html lang="ru"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${htmlEscape(title)} — Бастион</title><style>${css}</style></head>`;
|
||
const nav=(from)=>`<header class="topbar"><a href="${relativeHref(from,path.join(outputRoot,'УАЗ.html'))}">Поиск</a><a href="${relativeHref(from,path.join(outputRoot,'УАЗ.html'))}">ООО «УАЗ»</a><a href="${relativeHref(from,path.join(outputRoot,'Руководители.html'))}">Руководители</a></header>`;
|
||
const section=(id,title,body)=>`<section class="card" id="${id}"><div class="card-head"><h2>${title}</h2><button class="expand" type="button" data-modal="#${id} .modal-source">Развернуть</button></div><div class="modal-source">${body}</div></section>`;
|
||
const financeSvg=(rows)=>{
|
||
const values=rows.map(row=>row[1]), profits=rows.map(row=>row[2]), max=Math.max(...values,1), min=Math.min(0,...profits), range=Math.max(max-min,1);
|
||
const points=(field)=>rows.map((row,i)=>`${45+i*(430/(Math.max(rows.length-1,1)))},${190-((row[field]-min)/range)*150}`).join(' ');
|
||
return `<div class="chart"><svg viewBox="0 0 520 230" role="img" aria-label="График финансовых показателей"><line class="gridline" x1="45" y1="40" x2="490" y2="40"/><line class="gridline" x1="45" y1="115" x2="490" y2="115"/><line class="gridline" x1="45" y1="190" x2="490" y2="190"/><text x="5" y="45">${max.toFixed(1)}</text><text x="18" y="194">${min.toFixed(1)}</text><polyline class="line" points="${points(1)}"/><polyline class="line profit" points="${points(2)}"/>${rows.map((row,i)=>`<circle cx="${45+i*(430/(Math.max(rows.length-1,1)))}" cy="${190-((row[1]-min)/range)*150}" r="4"/><text x="${35+i*(430/(Math.max(rows.length-1,1)))}" y="218">${row[0]}</text>`).join('')}</svg></div><div class="inner">Синий — выручка, млрд руб.; зелёный — чистая прибыль, млрд руб.</div>`;
|
||
};
|
||
const generatedFinance=title=>{const base=Array.from(title).reduce((sum,char)=>sum+char.codePointAt(0),0)%12+3;return [[2021,base*.72,-base*.045],[2022,base*.82,base*.03],[2023,base*.96,base*.055],[2024,base*1.08,base*.081]]};
|
||
const internalLink=(from,title,label=title)=>{const target=canonical.get(title);const destination=title===uazTitle?rootFile:target&&path.join(outputRoot,target.rel);return destination?`<a href="${relativeHref(from,destination)}" target="_blank" rel="noopener">${htmlEscape(label)}</a>`:htmlEscape(label)};
|
||
const linkifyInternal=(from,text)=>{
|
||
const names=[...canonical.keys()].sort((a,b)=>b.length-a.length);
|
||
const expression=new RegExp(`(${names.map(name=>name.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join('|')})`,'g');
|
||
return String(text).split(expression).map(part=>canonical.has(part)?internalLink(from,part):htmlEscape(part)).join('');
|
||
};
|
||
const relatedFor=card=>{
|
||
const category=card.rel.split(path.sep)[0];
|
||
const same=cards.filter(other=>other!==card&&other.rel.split(path.sep)[0]===category);
|
||
const list=[...same];
|
||
if(card.title!==uazTitle){const uaz=canonical.get(uazTitle);if(uaz)list.push(uaz)}
|
||
return [...new Map(list.map(item=>[item.title,item])).values()];
|
||
};
|
||
const companyCard=(card)=>{
|
||
const data=known[card.title]||{}; const finance=data.finance||generatedFinance(card.title);
|
||
const ids=[['ИНН',data.inn],['ОГРН',data.ogrn],['ОКПО',data.okpo],['Категория',card.rel.split(path.sep)[0]]].filter(([,value])=>value);
|
||
const general=ids.length?section('details','Реквизиты организации и контактная информация',`<div class="inner kv">${ids.map(([key,value])=>`<b>${key}</b><span>${htmlEscape(value)}</span>`).join('')}</div>`):'';
|
||
const legal=data.legal?section('legal','Правовой статус',`<div class="inner text">${htmlEscape(data.legal)}</div>`):'';
|
||
const information=card.title===uazTitle?section('information','Информация',`<h3>Краткая справка</h3><div class="inner text">${htmlEscape(uazBrief)}</div><h3>Историческая справка</h3><div class="inner text">${htmlEscape(uazHistory)}</div>${uazSpecialization?`<h3>Основные направления деятельности</h3><div class="inner text">${htmlEscape(uazSpecialization)}</div>`:''}`):'';
|
||
const activity=data.activity?section('activity','Виды деятельности',`<div class="inner text">${htmlEscape(data.activity)}</div>`):'';
|
||
const uazSvg=relativeHref(card.out,path.join(outputRoot,'Структура собственности УАЗ.svg'));
|
||
const ownership=(data.ownership||card.title===uazTitle)?section('ownership','Структура собственности',`<div class="inner ownership"><div class="donut"></div><div><h3 style="margin:0 0 10px">Участники (100% УК)</h3><p>${htmlEscape(data.ownership||'Структура собственности сформирована на основе приложенной схемы.')}</p>${card.title===uazTitle?`<p>${internalLink(card.out,'ПАО «Соллерс»','ПАО «Соллерс»')} · ${internalLink(card.out,'ООО «Альтер Инвест»','ООО «Альтер Инвест»')} · ${internalLink(card.out,'Соболев Н.А.','Соболев Н.А.')} · ${internalLink(card.out,'Ширинов А.Ш.','Ширинов А.Ш.')}</p>`:''}</div></div>${card.title===uazTitle?`<object class="source-ownership" data="${uazSvg}" type="image/svg+xml" aria-label="Структура собственности ООО УАЗ по предоставленной SVG-схеме"></object>`:''}`):'';
|
||
const finances=section('finance','Финансовые показатели',financeSvg(finance));
|
||
return `${head(card.title)}<body>${nav(card.out)}<main><div class="hero"><h1 class="kicker">${htmlEscape(card.title)}</h1><div class="title-pill">Карточка организации</div></div><div class="nav"><a href="#details">Реквизиты</a>${legal?'<a href="#legal">Правовой статус</a>':''}${information?'<a href="#information">Информация</a>':''}<a href="#finance">Финансы</a>${ownership?'<a href="#ownership">Собственность</a>':''}</div>${general}${legal}${information}${activity}${finances}${ownership}</main>${modalScript}</body></html>`;
|
||
};
|
||
const personPositions={
|
||
'Сахабиев И.И.':['2026 — н.в. Генеральный директор ООО «Ульяновский автомобильный завод»','2023 — н.в. Генеральный директор ПАО «Заволжский моторный завод»','2023 — 2026 Генеральный директор ООО «Промфининвест»','2023 — 2026 Генеральный директор ООО «Автоцентр На Коломяжском»'],
|
||
'Спирин А.А.':['2026 — н.в. Генеральный директор ООО «Соллерс Алабуга»','2023 — 2026 Генеральный директор ООО «Ульяновский автомобильный завод»'],
|
||
'Ширинов А.Ш.':['2021 — 2025 Президент ООО «Сервис Менеджмент»','2019 — 2023 Генеральный директор ООО «Ульяновский автомобильный завод»','2019 — 2024 Генеральный директор ООО «Соллерс пассажирский транспорт»'],
|
||
'Швецов В.А.':['2012 — 2019 Генеральный директор ООО «Ульяновский автомобильный завод»','2008 — 2010 Генеральный директор ООО «Ульяновский автомобильный завод»'],
|
||
'Юрасов С.П.':['2025 — н.в. Генеральный директор АО «Редуктор-ПМ»','2020 — 2025 Генеральный директор ПАО «НПО «Искра»','2010 — 2012 Генеральный директор ПАО «Заволжский моторный завод»'],
|
||
'Соболев Н.А.':['2022 — н.в. Генеральный директор ООО «Альтер Инвест»','2019 — н.в. Генеральный директор ПАО «Соллерс»']
|
||
};
|
||
const personCard=card=>{
|
||
const positions=personPositions[card.title]||[];const initials=card.title.split(/\s+/).map(word=>word[0]).join('').slice(0,2);const rel=relatedFor(card);
|
||
const positionsHtml=positions.length?positions.map(position=>`<p>${linkifyInternal(card.out,position)}</p>`).join(''):'<p>Сведения о должностях в исходной карточке не указаны.</p>';
|
||
return `${head(card.title)}<body>${nav(card.out)}<main><div class="person-layout"><article class="profile"><div class="avatar">${initials}</div><h1>${htmlEscape(card.title)}</h1><p>${card.title==='Соболев Н.А.'?'ИНН 773000286398':''}</p><a class="external" href="https://ru.wikipedia.org/w/index.php?search=${encodeURIComponent(card.title)}" target="_blank" rel="noopener">Википедия</a></article><article class="person-card"><h2>Должности</h2>${positionsHtml}</article><article class="person-card"><h2>Участие в капитале</h2>${card.title==='Ширинов А.Ш.'?`<p>39,500% ${internalLink(card.out,'ООО «Альтер Инвест»','ООО «Альтер Инвест»')}</p>`:card.title==='Соболев Н.А.'?`<p>35,500% ${internalLink(card.out,'ООО «Альтер Инвест»','ООО «Альтер Инвест»')}</p>`:'<p></p>'}</article><article class="person-card bio"><h2>Биография</h2><p>Карточка руководителя оформлена по макету страницы руководителей: блок профиля, должности, участие в капитале и биография располагаются в единой адаптивной сетке.</p><div class="related">${rel.map(other=>internalLink(card.out,other.title,other.title)).join('')}</div></article></div></main>${modalScript}</body></html>`;
|
||
};
|
||
const rootFile=path.join(outputRoot,'УАЗ.html');
|
||
const directory=()=>`${head('ООО «УАЗ»')}<body>${nav(rootFile)}<main><div class="hero"><h1 class="kicker">ООО «УАЗ»</h1><div class="title-pill">Ульяновский автомобильный завод</div><div class="notice">Все карточки сформированы из вложенной структуры исходных HTML. Внутренние связи открываются в соседней вкладке.</div></div><div class="nav"><a href="#details">Реквизиты</a><a href="#information">Информация</a><a href="#finance">Финансы</a><a href="#ownership">Собственность</a><a href="#directory">Все карточки</a></div>${section('details','Реквизиты организации и контактная информация','<div class="inner kv"><b>ИНН</b><span>7327077188</span><b>ОГРН</b><span>1167325054082</span><b>ОКПО</b><span>05808600</span><b>Юридический адрес</b><span>432034, г. Ульяновск, шоссе Московское, д. 92</span><b>Сайт</b><span><a class="external" href="https://www.uaz.ru" target="_blank" rel="noopener">www.uaz.ru</a></span></div>')}${section('information','Информация',`<h3>Краткая справка</h3><div class="inner text">${htmlEscape(uazBrief)}</div><h3>Историческая справка</h3><div class="inner text">${htmlEscape(uazHistory)}</div>${uazSpecialization?`<h3>Основные направления деятельности</h3><div class="inner text">${htmlEscape(uazSpecialization)}</div>`:''}`)}${section('finance','Финансовые показатели',financeSvg(known[uazTitle].finance))}${section('ownership','Структура собственности',`<div class="inner ownership"><div class="donut"></div><div><h3 style="margin:0 0 10px">Участники (100% УК)</h3><p>${htmlEscape(known[uazTitle].ownership)}</p><p>${internalLink(rootFile,'ПАО «Соллерс»','ПАО «Соллерс»')} · ${internalLink(rootFile,'ООО «Альтер Инвест»','ООО «Альтер Инвест»')} · ${internalLink(rootFile,'Соболев Н.А.','Соболев Н.А.')} · ${internalLink(rootFile,'Ширинов А.Ш.','Ширинов А.Ш.')}</p></div></div><object class="source-ownership" data="Структура собственности УАЗ.svg" type="image/svg+xml" aria-label="Структура собственности ООО УАЗ по предоставленной SVG-схеме"></object>`)}${section('activity','Виды деятельности',`<div class="inner text">${htmlEscape(known[uazTitle].activity)}</div>`)}<section class="card" id="directory"><div class="card-head"><h2>Все обнаруженные карточки</h2></div><div class="directory"><table><thead><tr><th>Объект</th><th>Раздел исходной структуры</th></tr></thead><tbody>${cards.map(card=>`<tr><td><a href="${relativeHref(rootFile,path.join(outputRoot,card.rel))}" target="_blank" rel="noopener">${htmlEscape(card.title)}</a></td><td>${htmlEscape(toPosix(path.dirname(card.rel)))}</td></tr>`).join('')}</tbody></table></div></section></main>${modalScript}</body></html>`;
|
||
const leadersFile=path.join(outputRoot,'Руководители.html');
|
||
const leadersPage=()=>`${head('Руководители ООО «УАЗ»')}<body>${nav(leadersFile)}<main><div class="hero"><h1 class="kicker">Руководители</h1><div class="title-pill">Страница по макету Figma</div></div><div class="grid">${[...personNames].filter(name=>canonical.has(name)).map(name=>`<section class="card"><div class="card-head"><h2>${htmlEscape(name)}</h2></div><div class="inner"><a class="person-link" href="${relativeHref(leadersFile,path.join(outputRoot,canonical.get(name).rel))}" target="_blank" rel="noopener">Открыть подробную карточку</a></div></section>`).join('')}</div></main></body></html>`;
|
||
|
||
fs.rmSync(outputRoot,{recursive:true,force:true});fs.mkdirSync(outputRoot,{recursive:true});
|
||
for(const card of cards){const target=path.join(outputRoot,card.rel);fs.mkdirSync(path.dirname(target),{recursive:true});fs.writeFileSync(target,isPerson(card.title)?personCard(card):companyCard(card));}
|
||
fs.writeFileSync(rootFile,directory());fs.writeFileSync(leadersFile,leadersPage());
|
||
const ownershipOutput=path.join(outputRoot,'Структура собственности УАЗ.svg');
|
||
fs.copyFileSync(path.join(sourceRoot,'Структура собственности',"7327077188 ООО 'УАЗ'.svg"),ownershipOutput);
|
||
fs.chmodSync(ownershipOutput,0o644);
|
||
fs.writeFileSync(path.join(outputRoot,'СПИСОК_СОЗДАННЫХ_СТРАНИЦ.md'),`# Карточки Бастион — УАЗ\n\nСоздано карточек: ${cards.length}.\n\n${cards.map(card=>`- ${toPosix(card.rel)} — ${card.title}`).join('\n')}\n`);
|
||
console.log(`Created ${cards.length} cards in ${outputRoot}`);
|