Compare commits
5 Commits
main
..
ab31a3e39c
| Author | SHA1 | Date | |
|---|---|---|---|
| ab31a3e39c | |||
| 7ce05fd240 | |||
| eb5babb4f3 | |||
| 655ee9cba1 | |||
| d8d8a3fbce |
@@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
trigger: always_on
|
|
||||||
---
|
|
||||||
|
|
||||||
# GEMINI.md - Antigravity Kit (Auto-Generated)
|
|
||||||
|
|
||||||
|
|
||||||
## TIER 0: UNIVERSAL RULES (Always Active)
|
|
||||||
|
|
||||||
### 🌍 Global User Rules (MANDATORY)
|
|
||||||
|
|
||||||
1. **Idioma**: Conversar e desenvolver sempre em **Português do Brasil**.
|
|
||||||
2. **Automação Terminais**: Executar comandos de terminal, leitura de arquivos e edições de forma automática. Comandos de desenvolvimento (build, install, lint, git, etc.) são sempre 'SafeToAutoRun'.
|
|
||||||
3. **Memória & Monitoramento**: Após cada evolução ou atualização no app, é OBRIGATÓRIO:
|
|
||||||
- Atualizar a memória do app em '/root/Antigravity_Memory/knowledge/{AppName}.md'.
|
|
||||||
- Atualizar o status e credenciais no 'app.py' do Cronos Dashboard.
|
|
||||||
- Realizar 'git commit', 'git push' e executar o 'update.sh' do Cronos para deploy automático.
|
|
||||||
|
|
||||||
### 🌐 Language Handling
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
VITE_LOGTO_ENDPOINT=https://logto-bzlued1boxl3t8ewsyn99an9.187.77.227.172.sslip.io
|
|
||||||
VITE_LOGTO_APP_ID=3whpc0jru4ka5nerinnuc
|
|
||||||
VITE_LOGTO_ORG_ID=4j7uhxlmia9e
|
|
||||||
VITE_SUPABASE_SCHEMA=TSCUT
|
|
||||||
|
|
||||||
# --- LOGTO_SUPA UPGRADE ---
|
|
||||||
VITE_LOGTO_ENDPOINT=https://logto-bzlued1boxl3t8ewsyn99an9.187.77.227.172.sslip.io
|
|
||||||
VITE_LOGTO_APP_ID=3whpc0jru4ka5nerinnuc
|
|
||||||
VITE_LOGTO_ORG_ID=4j7uhxlmia9e
|
|
||||||
VITE_SUPABASE_SCHEMA=TSCUT
|
|
||||||
+6
-7
@@ -1,7 +1,6 @@
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
.vercel
|
.vercel
|
||||||
.vscode
|
.vscode
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.local
|
*.local
|
||||||
package-lock.json
|
|
||||||
|
|||||||
+4
-5
@@ -5,14 +5,14 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="icon" href="/logo.png" type="image/png">
|
<link rel="icon" href="/logo.png" type="image/png">
|
||||||
<title>Otimizador de Corte - TSCUT</title>
|
<title>Otimizador de Corte 2D- TSCUT</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<header>
|
<header>
|
||||||
<h1><img src="/logo.png" alt="Logo" class="app-logo">Otimizador de Plano de Corte</h1>
|
<h1><img src="/logo.png" alt="Logo" class="app-logo">Otimizador de Plano de Corte 2D</h1>
|
||||||
<p>Barras Lineares | Algoritmo FFD Avançado | Otimização com Múltiplas Barras</p>
|
<p>Barras Lineares | Otimização com Múltiplas Barras</p>
|
||||||
<button id="themeToggle" class="theme-toggle-btn" onclick="toggleTheme()" title="Alternar Tema">🌙</button>
|
<button id="themeToggle" class="theme-toggle-btn" onclick="toggleTheme()" title="Alternar Tema">🌙</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
</label>
|
</label>
|
||||||
<label class="process-option">
|
<label class="process-option">
|
||||||
<input type="radio" name="cuttingProcess" value="oxy" onchange="updateProcess()">
|
<input type="radio" name="cuttingProcess" value="oxy" onchange="updateProcess()">
|
||||||
<span class="process-name">Oxicorte (5mm)</span>
|
<span class="process-name">Oxicorte (6mm)</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -157,7 +157,6 @@
|
|||||||
<div class="export-buttons">
|
<div class="export-buttons">
|
||||||
<button class="btn-primary" onclick="exportHTML()">📄 Exportar</button>
|
<button class="btn-primary" onclick="exportHTML()">📄 Exportar</button>
|
||||||
<button class="btn-primary" onclick="printReport()">🖨️ Imprimir</button>
|
<button class="btn-primary" onclick="printReport()">🖨️ Imprimir</button>
|
||||||
<button class="btn-primary" onclick="printCompactReport()" style="background-color: #27ae60;">🖨️ Rel.Compacto</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
[phases.setup]
|
|
||||||
nixPkgs = ["nodejs_23"]
|
|
||||||
Generated
+1153
File diff suppressed because it is too large
Load Diff
+1
-3
@@ -18,10 +18,8 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@logto/browser": "^3.0.13",
|
|
||||||
"@logto/react": "^4.0.14",
|
|
||||||
"papaparse": "^5.5.3",
|
"papaparse": "^5.5.3",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"xlsx": "^0.18.5"
|
"xlsx": "^0.18.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+46
-265
@@ -1,76 +1,3 @@
|
|||||||
|
|
||||||
// --- LOGTO AUTH GUARD ---
|
|
||||||
import LogtoClient from '@logto/browser';
|
|
||||||
|
|
||||||
// Escondemos o app imediatamente
|
|
||||||
const style = document.createElement('style');
|
|
||||||
style.innerHTML = 'body { display: none !important; }';
|
|
||||||
document.head.appendChild(style);
|
|
||||||
|
|
||||||
// Implementação de Storage Persistente para evitar perda de sessão em cross-domain
|
|
||||||
const persistentStorage = {
|
|
||||||
getItem: (key) => localStorage.getItem(key),
|
|
||||||
setItem: (key, value) => {
|
|
||||||
try { localStorage.setItem(key, value); } catch (e) { console.error('Storage error', e); }
|
|
||||||
},
|
|
||||||
removeItem: (key) => localStorage.removeItem(key),
|
|
||||||
};
|
|
||||||
|
|
||||||
const logtoClient = new LogtoClient({
|
|
||||||
endpoint: import.meta.env.VITE_LOGTO_ENDPOINT,
|
|
||||||
appId: import.meta.env.VITE_LOGTO_APP_ID,
|
|
||||||
scopes: ['openid', 'offline_access', 'profile', 'email', 'organizations'],
|
|
||||||
storage: persistentStorage // CRÍTICO: localStorage forçado
|
|
||||||
});
|
|
||||||
|
|
||||||
async function protectPage() {
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
const isCallback = urlParams.has('code') && urlParams.has('state');
|
|
||||||
|
|
||||||
// A URI exata para a qual o Logto vai devolver o usuário
|
|
||||||
const redirectUri = window.location.origin + '/';
|
|
||||||
|
|
||||||
if (isCallback) {
|
|
||||||
try {
|
|
||||||
// Logto DEVE receber a URL completa com parâmetros para extrair o state
|
|
||||||
await logtoClient.handleSignInCallback(window.location.href);
|
|
||||||
console.log('TSCUT Auth: Login bem sucedido. Recarregando app...');
|
|
||||||
window.location.assign('/');
|
|
||||||
} catch (error) {
|
|
||||||
style.remove();
|
|
||||||
document.body.innerHTML = `
|
|
||||||
<div style="padding: 20px; color: #ff5555; background: #1a1a1a; min-height: 100vh; font-family: sans-serif;">
|
|
||||||
<h1>Falha na Autenticação Industrial</h1>
|
|
||||||
<p style="font-size: 1.2em;">Erro: ${error.message}</p>
|
|
||||||
<button onclick="window.location.assign('/')" style="margin-top: 20px; padding: 12px 25px; cursor: pointer;">
|
|
||||||
Tentar Novamente
|
|
||||||
</button>
|
|
||||||
</div>`;
|
|
||||||
console.error('Falha no callback do Logto:', error);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const isAuthenticated = await logtoClient.isAuthenticated();
|
|
||||||
if (!isAuthenticated) {
|
|
||||||
console.log('TSCUT Auth: Redirecionando para login Logto...');
|
|
||||||
await logtoClient.signIn(redirectUri);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acesso permitido
|
|
||||||
style.remove();
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Erro de autenticação:', err);
|
|
||||||
await logtoClient.signIn(redirectUri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execução imediata
|
|
||||||
protectPage();
|
|
||||||
// -------------------------
|
|
||||||
|
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import Papa from 'papaparse';
|
import Papa from 'papaparse';
|
||||||
import './style.css';
|
import './style.css';
|
||||||
@@ -495,13 +422,14 @@ function showFeedback(message, type) {
|
|||||||
showToast(message, type);
|
showToast(message, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== ALGORITMO OTIMIZADO AVANÇADO (Programação Dinâmica / Knapsack) =====
|
// ===== ALGORITMO FFD AVANÇADO =====
|
||||||
function optimizeCutting() {
|
function optimizeCutting() {
|
||||||
if (availableBars.length === 0 || demandPieces.length === 0) {
|
if (availableBars.length === 0 || demandPieces.length === 0) {
|
||||||
showToast("Adicione barras e peças", "warning");
|
showToast('Adicione barras e peças', 'warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expandir peças com ID único global
|
||||||
const expandedPieces = [];
|
const expandedPieces = [];
|
||||||
let uniqueIdCounter = 0;
|
let uniqueIdCounter = 0;
|
||||||
demandPieces.forEach(p => {
|
demandPieces.forEach(p => {
|
||||||
@@ -510,12 +438,14 @@ function optimizeCutting() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FFD em cada barra disponível
|
||||||
const usedBars = [];
|
const usedBars = [];
|
||||||
let unusedPieces = [...expandedPieces];
|
const unusedPieces = [...expandedPieces];
|
||||||
|
|
||||||
const sortedBarTypes = [...availableBars].sort((a, b) => b.length - a.length);
|
// Ordenar barras por comprimento (maior para menor) para tentar usar as maiores primeiro?
|
||||||
|
// O código original não ordenava, seguia a ordem de inserção. Manteremos assim por enquanto.
|
||||||
|
|
||||||
for (let barType of sortedBarTypes) {
|
for (let barType of availableBars) {
|
||||||
for (let barCopy = 0; barCopy < barType.qty; barCopy++) {
|
for (let barCopy = 0; barCopy < barType.qty; barCopy++) {
|
||||||
if (unusedPieces.length === 0) break;
|
if (unusedPieces.length === 0) break;
|
||||||
|
|
||||||
@@ -528,47 +458,48 @@ function optimizeCutting() {
|
|||||||
isSimulated: barType.isSimulated || false
|
isSimulated: barType.isSimulated || false
|
||||||
};
|
};
|
||||||
|
|
||||||
unusedPieces.sort((a, b) => b.length - a.length);
|
// Tentar encaixar peças (FFD)
|
||||||
|
// Ordenar peças restantes por tamanho decrescente
|
||||||
const N = unusedPieces.length;
|
const sorted = [...unusedPieces].sort((a, b) => b.length - a.length);
|
||||||
const capacity = bar.remaining;
|
|
||||||
|
|
||||||
const dp = new Int32Array(capacity + 1);
|
|
||||||
const keep = new Array(N);
|
|
||||||
|
|
||||||
for (let i = 0; i < N; i++) {
|
|
||||||
keep[i] = new Uint8Array(capacity + 1);
|
|
||||||
const w = unusedPieces[i].length + kerfSize;
|
|
||||||
|
|
||||||
for (let j = capacity; j >= 0; j--) {
|
|
||||||
if (j >= w && dp[j - w] + w > dp[j]) {
|
|
||||||
dp[j] = dp[j - w] + w;
|
|
||||||
keep[i][j] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let bestW = 0;
|
|
||||||
for(let j = 0; j <= capacity; j++) {
|
|
||||||
if(dp[j] > dp[bestW]) bestW = j;
|
|
||||||
}
|
|
||||||
let currW = bestW;
|
|
||||||
|
|
||||||
const toRemoveIds = [];
|
const toRemoveIds = [];
|
||||||
for (let i = N - 1; i >= 0; i--) {
|
|
||||||
if (keep[i][currW] === 1) {
|
for (let piece of sorted) {
|
||||||
const selectedPiece = unusedPieces[i];
|
// Check if piece fits considering kerf loss for this cut
|
||||||
bar.pieces.push(selectedPiece);
|
// We assume each piece consumes its length + kerf
|
||||||
const w = selectedPiece.length + kerfSize;
|
// Exception: The very last piece in a bar might not strictly need a kerf if it's the end,
|
||||||
bar.remaining -= w;
|
// but usually in cutting processes, you cut the piece out, so kerf is consumed.
|
||||||
currW -= w;
|
// User requirement: "consumo adicional de cada corte... sera de mais 2mm para cada corte"
|
||||||
toRemoveIds.push(selectedPiece.uniqueId);
|
|
||||||
|
const requiredSpace = piece.length + kerfSize;
|
||||||
|
|
||||||
|
// However, we need to be careful. If remaining is EXACTLY piece.length, can we cut it?
|
||||||
|
// If we cut it, we lose the kerf. So we need remaining >= piece.length + kerf?
|
||||||
|
// Or does the kerf come from the "waste"?
|
||||||
|
// Usually: Bar 6000. Piece 1000. Kerf 3.
|
||||||
|
// Cut 1: Consumes 1003. Remaining 4997.
|
||||||
|
// So yes, we treat piece length as (length + kerf).
|
||||||
|
|
||||||
|
if (bar.remaining >= requiredSpace) {
|
||||||
|
bar.pieces.push(piece);
|
||||||
|
bar.remaining -= requiredSpace;
|
||||||
|
toRemoveIds.push(piece.uniqueId);
|
||||||
|
} else if (bar.remaining >= piece.length && bar.remaining < requiredSpace) {
|
||||||
|
// Edge case: Fits exactly or with less than kerf remaining?
|
||||||
|
// If I have 1000mm remaining and need 1000mm piece.
|
||||||
|
// If I cut, I destroy 3mm. So I need 1003mm to get a 1000mm piece?
|
||||||
|
// Yes, usually. Unless it's the raw end of the bar, but we can't assume that.
|
||||||
|
// Let's stick to the rule: consumption = length + kerf.
|
||||||
|
// If bar.remaining < length + kerf, we can't cut it.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toRemoveIds.length > 0) {
|
// Remover peças colocadas usando ID único
|
||||||
const removeSet = new Set(toRemoveIds);
|
for (let uid of toRemoveIds) {
|
||||||
unusedPieces = unusedPieces.filter(p => !removeSet.has(p.uniqueId));
|
const idx = unusedPieces.findIndex(p => p.uniqueId === uid);
|
||||||
|
if (idx !== -1) unusedPieces.splice(idx, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bar.pieces.length > 0) {
|
||||||
usedBars.push(bar);
|
usedBars.push(bar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1207,158 +1138,8 @@ window.calculateOptimization = calculateOptimization;
|
|||||||
window.clearAll = clearAll;
|
window.clearAll = clearAll;
|
||||||
window.exportHTML = exportHTML;
|
window.exportHTML = exportHTML;
|
||||||
window.printReport = printReport;
|
window.printReport = printReport;
|
||||||
window.printCompactReport = printCompactReport;
|
|
||||||
window.saveJob = saveJob;
|
window.saveJob = saveJob;
|
||||||
|
|
||||||
function printCompactReport() {
|
|
||||||
const html = generateCompactReportHTML();
|
|
||||||
if (!html) {
|
|
||||||
showToast('Calcule a otimização primeiro', 'warning');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const win = window.open('', '_blank');
|
|
||||||
win.document.write(html);
|
|
||||||
win.document.close();
|
|
||||||
setTimeout(() => {
|
|
||||||
win.print();
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateCompactReportHTML() {
|
|
||||||
if (!lastResults) return null;
|
|
||||||
|
|
||||||
const { usedBars, totalWeight } = lastResults;
|
|
||||||
const dateStr = new Date().toLocaleDateString('pt-BR');
|
|
||||||
const timeStr = new Date().toLocaleTimeString('pt-BR');
|
|
||||||
const totalPieces = demandPieces.reduce((s, p) => s + p.qty, 0);
|
|
||||||
const jobTitle = document.getElementById('jobTitle').value.trim() || 'Relatório de Corte - Compacto';
|
|
||||||
|
|
||||||
const renderPieceGroups = (pieces) => {
|
|
||||||
const groups = {};
|
|
||||||
pieces.forEach(p => {
|
|
||||||
if (!groups[p.tag]) groups[p.tag] = 0;
|
|
||||||
groups[p.tag]++;
|
|
||||||
});
|
|
||||||
return Object.entries(groups).map(([tag, qt]) => `${tag} (${qt}x)`).join(', ');
|
|
||||||
};
|
|
||||||
|
|
||||||
let tableRows = usedBars.map((bar, idx) => {
|
|
||||||
const composition = renderPieceGroups(bar.pieces);
|
|
||||||
return `
|
|
||||||
<tr>
|
|
||||||
<td>${idx + 1}</td>
|
|
||||||
<td>${bar.barType}</td>
|
|
||||||
<td style="text-align: center;">${bar.count}</td>
|
|
||||||
<td style="text-align: right;">${bar.length} mm</td>
|
|
||||||
<td style="text-align: right; color: var(--danger);">${bar.remaining} mm</td>
|
|
||||||
<td>${composition}</td>
|
|
||||||
</tr>
|
|
||||||
`;
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
return `
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="pt-BR">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Relatório Compacto - ${dateStr}</title>
|
|
||||||
<style>
|
|
||||||
:root { --primary: #1a6b8f; --danger: #e74c3c; --gray: #bdc3c7; }
|
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
||||||
body { background: #555; font-family: 'Segoe UI', Arial, sans-serif; font-size: 10px; }
|
|
||||||
|
|
||||||
.page {
|
|
||||||
background: white;
|
|
||||||
width: 210mm;
|
|
||||||
min-height: 297mm;
|
|
||||||
margin: 20px auto;
|
|
||||||
padding: 10mm;
|
|
||||||
position: relative;
|
|
||||||
box-shadow: 0 0 10px rgba(0,0,0,0.3);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
@page { size: A4 portrait; margin: 0; }
|
|
||||||
body { background: white; margin: 0; padding: 0; }
|
|
||||||
.page { margin: 0; box-shadow: none; border: none; min-height: 100vh; }
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
border-bottom: 2px solid var(--primary);
|
|
||||||
padding-bottom: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
header h1 { font-size: 14pt; margin-bottom: 5px; }
|
|
||||||
header p { font-size: 8pt; color: #666; }
|
|
||||||
|
|
||||||
.content { flex: 1; }
|
|
||||||
|
|
||||||
.summary-box {
|
|
||||||
display: flex; justify-content: space-between;
|
|
||||||
background: #f4f4f4; padding: 8px; margin-bottom: 15px;
|
|
||||||
border: 1px solid #ddd; border-radius: 4px;
|
|
||||||
}
|
|
||||||
.summary-item { font-size: 9pt; }
|
|
||||||
.summary-item strong { display: block; font-size: 11pt; color: var(--primary); margin-top: 2px; }
|
|
||||||
|
|
||||||
table { width: 100%; border-collapse: collapse; font-size: 8pt; margin-bottom: 10px; }
|
|
||||||
th { background: var(--primary); color: white; text-align: left; padding: 6px; border: 1px solid #ddd; }
|
|
||||||
td { border: 1px solid #ddd; padding: 5px; }
|
|
||||||
tr:nth-child(even) { background: #f9f9f9; }
|
|
||||||
|
|
||||||
footer {
|
|
||||||
margin-top: 15px; border-top: 1px solid var(--gray);
|
|
||||||
padding-top: 10px; text-align: center; font-size: 8pt; color: #888;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="page">
|
|
||||||
<header>
|
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
||||||
<div>
|
|
||||||
<h1>📊 ${jobTitle}</h1>
|
|
||||||
<p>Gerado em: ${dateStr} às ${timeStr}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<div class="summary-box">
|
|
||||||
<div class="summary-item">Total de Peças<strong>${totalPieces}</strong></div>
|
|
||||||
<div class="summary-item">Barras (Padrões)<strong>${usedBars.length}</strong></div>
|
|
||||||
<div class="summary-item">Peso Total (kg)<strong>${totalWeight.toFixed(2)}</strong></div>
|
|
||||||
<div class="summary-item">Aproveitamento Global<strong>${((1 - (lastResults.usedBars.reduce((s, b) => s + b.remaining, 0) / lastResults.usedBars.reduce((s, b) => s + b.length, 0))) * 100).toFixed(2)}%</strong></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="width: 5%;">#</th>
|
|
||||||
<th style="width: 20%;">Perfil/Barra</th>
|
|
||||||
<th style="width: 8%; text-align: center;">Qtd</th>
|
|
||||||
<th style="width: 12%; text-align: right;">Comp. (mm)</th>
|
|
||||||
<th style="width: 12%; text-align: right;">Sobra (mm)</th>
|
|
||||||
<th style="width: 43%;">Composição (Tag x Qtd)</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
${tableRows}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
Otimizador de Corte (Compacto) | ${totalPieces} Peças Processadas | Página 1 de 1
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.loadJob = loadJob;
|
window.loadJob = loadJob;
|
||||||
window.closeModal = closeModal;
|
window.closeModal = closeModal;
|
||||||
|
|
||||||
|
|||||||
+1322
-1322
File diff suppressed because it is too large
Load Diff
+9
-9
@@ -1,9 +1,9 @@
|
|||||||
export default {
|
export default {
|
||||||
root: './',
|
root: './',
|
||||||
build: {
|
build: {
|
||||||
outDir: 'dist',
|
outDir: 'dist',
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
allowedHosts: true
|
allowedHosts: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user