init
This commit is contained in:
parent
83c0f74bf0
commit
2cb448eccf
3
.gitignore
vendored
3
.gitignore
vendored
@ -9,3 +9,6 @@ docs/_book
|
|||||||
# TODO: where does this rule come from?
|
# TODO: where does this rule come from?
|
||||||
test/
|
test/
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
dist/
|
||||||
13
.vscode/tasks.json
vendored
Normal file
13
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "npm",
|
||||||
|
"script": "build",
|
||||||
|
"group": "build",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"label": "npm: build",
|
||||||
|
"detail": "vite build"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -1,2 +1,5 @@
|
|||||||
# unilab-prints
|
# Vue 3 + Vite
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||||
|
|
||||||
|
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
|
||||||
|
|||||||
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + Vue</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1566
package-lock.json
generated
Normal file
1566
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
package.json
Normal file
20
package.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "vue-spa-export-tmp",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"html2pdf.js": "^0.10.3",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"xlsx": "^0.18.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.2.3",
|
||||||
|
"vite": "^6.3.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
public/index.html
Normal file
14
public/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Certificato Prova</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<script type="module" src="/vue-report/assets/main.js"></script>
|
||||||
|
<link rel="stylesheet" href="/vue-report/assets/index.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
public/report_header.png
Normal file
BIN
public/report_header.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
374
src/App.vue
Normal file
374
src/App.vue
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<button class="pdf-button" @click="exportPdf">Esporta PDF</button>
|
||||||
|
<div class="report" ref="printable" contenteditable="true">
|
||||||
|
<header>
|
||||||
|
<img src="/report_header.png" alt="Intestazione Unilab" class="report-header-image" />
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<h3 class="section-title">{{ intestazione.intest1 }}</h3>
|
||||||
|
|
||||||
|
<div>{{intestazione.modreport}}</div>
|
||||||
|
|
||||||
|
<h3 class="section-title">{{ intestazione.intest2 }}</h3>
|
||||||
|
|
||||||
|
<section class="info-block">
|
||||||
|
<table class="info-table">
|
||||||
|
<tr>
|
||||||
|
<td>Direttore dei Lavori:</td>
|
||||||
|
<td>{{ datiacc_info.directorName }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Indirizzo:</td>
|
||||||
|
<td>{{ datiacc_info.directorAddress }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Proprietà:</td>
|
||||||
|
<td>{{ datiacc_info.propertyName }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Indirizzo:</td>
|
||||||
|
<td>{{ datiacc_info.propertyAddress }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Cantiere:</td>
|
||||||
|
<td>{{ datiacc_info.siteName }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Indirizzo:</td>
|
||||||
|
<td>{{ datiacc_info.siteAddress }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Impresa esecutrice:</td>
|
||||||
|
<td>{{ datiacc_info.contractorName }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Natura dei campioni:</td>
|
||||||
|
<td>{{ datiacc_info.sampleNature }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- SEZIONE DINAMICA PER MATERIALE E TIPO CAMPIONE-->
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<component :is="resultComponent" :risult_data="risult_data" :intestazione="intestazione" :datiacc_data="datiacc_data" v-if="resultComponent" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- NOTE -->
|
||||||
|
|
||||||
|
<section v-if="note && note.length" class="note-section">
|
||||||
|
<table class="note-table">
|
||||||
|
<tr v-for="(n, index) in note" :key="index">
|
||||||
|
<td>{{ n.text }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="signatures">
|
||||||
|
<p>Lo sperimentatore<br>Dott. Ing. Giacomo Calussi</p>
|
||||||
|
<p>Il Direttore del Laboratorio<br>Dott. Ing. Paolo Neri</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, watch } from 'vue'
|
||||||
|
import html2pdf from 'html2pdf.js'
|
||||||
|
import A_CUB from './components/risultati/A_CUB.vue'
|
||||||
|
import A_CIL from './components/risultati/A_CIL.vue'
|
||||||
|
|
||||||
|
const componentMap = {
|
||||||
|
'A_CUB': A_CUB,
|
||||||
|
'A_CIL': A_CIL,
|
||||||
|
'B_BAR':B_BAR
|
||||||
|
}
|
||||||
|
|
||||||
|
const printable = ref(null)
|
||||||
|
|
||||||
|
const intestazione = ref({})
|
||||||
|
const datiacc_info = ref({})
|
||||||
|
const datiacc_data = ref([])
|
||||||
|
const risult_data = ref([])
|
||||||
|
const note = ref([])
|
||||||
|
|
||||||
|
|
||||||
|
const resultComponent = ref(null)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [intestazione.value.tipMat, intestazione.value.tipCam],
|
||||||
|
([tipMat, tipCam]) => {
|
||||||
|
const key = `${(tipMat || '').toUpperCase()}_${(tipCam || '').toUpperCase()}`
|
||||||
|
console.log('Updated key:', key)
|
||||||
|
resultComponent.value = componentMap[key] || null
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const params = new URLSearchParams(window.location.search)
|
||||||
|
const pSERCER = params.get('pSERCER') ?? 'DEFAULT'
|
||||||
|
|
||||||
|
const token = await getLoginToken('servizio_api', 'p0l01nf.'); // credenziali da gestire lato sicurezza
|
||||||
|
const data = await fetchReportDataWithToken(token, pSERCER);
|
||||||
|
|
||||||
|
intestazione.value = data.intestazione
|
||||||
|
datiacc_info.value = capitalizeAllFields(data.datiacc_info)
|
||||||
|
datiacc_data.value = data.datiacc_data
|
||||||
|
risult_data.value = calcolaMediaRcPerGruppo(data.risult_data)
|
||||||
|
note.value = data.note ?? []
|
||||||
|
|
||||||
|
console.log('intestazione', intestazione.value)
|
||||||
|
console.log('datiacc_info', datiacc_info.value)
|
||||||
|
console.log('datiacc_data', datiacc_data.value)
|
||||||
|
console.log('risult_data', risult_data.value)
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Errore caricamento dati:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function getLoginToken(username, password) {
|
||||||
|
const credentials = btoa(`${username}:${password}`); // base64 encoding
|
||||||
|
|
||||||
|
const response = await fetch('/unilab/servlet/oauth/token?scope=logintoken', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Basic ${credentials}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
credentials: 'omit',
|
||||||
|
body: JSON.stringify({ "sp_company": "001" })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error('Autenticazione fallita');
|
||||||
|
|
||||||
|
const json = await response.json();
|
||||||
|
return json.access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchReportDataWithToken(token, pSERCER) {
|
||||||
|
const response = await fetch(`/unilab/servlet/api/pi_flabreportapi?pSERCER=${encodeURIComponent(pSERCER)}`, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
},
|
||||||
|
credentials: 'omit'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error('Errore caricamento dati');
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
function capitalizeAllFields(obj) {
|
||||||
|
const result = {}
|
||||||
|
for (const key in obj) {
|
||||||
|
const val = obj[key]
|
||||||
|
result[key] = typeof val === 'string' ? capitalizeEachWord(val) : val
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function capitalizeEachWord(str) {
|
||||||
|
if (!str) return ''
|
||||||
|
return str
|
||||||
|
.toLowerCase()
|
||||||
|
.split(' ')
|
||||||
|
.map(word =>
|
||||||
|
word.length > 0
|
||||||
|
? word[0].toUpperCase() + word.slice(1)
|
||||||
|
: ''
|
||||||
|
)
|
||||||
|
.join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function exportPdf() {
|
||||||
|
html2pdf()
|
||||||
|
.from(printable.value)
|
||||||
|
.set({
|
||||||
|
filename: `certificato-prova.pdf`,
|
||||||
|
html2canvas: { scale: 2 },
|
||||||
|
jsPDF: {
|
||||||
|
unit: 'mm',
|
||||||
|
format: [210, 297], // A4
|
||||||
|
orientation: 'portrait'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDate(dateStr) {
|
||||||
|
const date = new Date(dateStr)
|
||||||
|
return date.toLocaleDateString('it-IT')
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(value, decimals = 2) {
|
||||||
|
return value != null
|
||||||
|
? Number(value).toLocaleString('it-IT', {
|
||||||
|
minimumFractionDigits: decimals,
|
||||||
|
maximumFractionDigits: decimals
|
||||||
|
})
|
||||||
|
: '—'
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcolaMediaRcPerGruppo(data) {
|
||||||
|
const gruppi = {}
|
||||||
|
|
||||||
|
// Raggruppa per serPre
|
||||||
|
data.forEach(row => {
|
||||||
|
if (!gruppi[row.serPre]) gruppi[row.serPre] = []
|
||||||
|
gruppi[row.serPre].push(row.rc)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Calcola la media per gruppo
|
||||||
|
const medie = {}
|
||||||
|
for (const serPre in gruppi) {
|
||||||
|
const somma = gruppi[serPre].reduce((acc, val) => acc + val, 0)
|
||||||
|
const media = somma / gruppi[serPre].length
|
||||||
|
medie[serPre] = media
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assegna la media a rprelievo per tutte le righe
|
||||||
|
return data.map(row => ({
|
||||||
|
...row,
|
||||||
|
rprelievo: formatNumber(medie[row.serPre],2)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-header-image {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 210mm;
|
||||||
|
padding: 4mm;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: white;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
header {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
padding: 6px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-header {
|
||||||
|
padding: 6px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 2px;
|
||||||
|
font-size: 0.85em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-table {
|
||||||
|
width: 80%;
|
||||||
|
border: 2px solid black;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-table th,
|
||||||
|
.info-table td {
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: 2px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-table th:first-child,
|
||||||
|
.info-table td:first-child {
|
||||||
|
font-weight: bold;
|
||||||
|
width: 30%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-section {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-table td {
|
||||||
|
padding: 1px 1px;
|
||||||
|
font-size: 0.7em;
|
||||||
|
border: none;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signatures {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 40px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-button {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background-color: #007BFF;
|
||||||
|
color: white;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
27
src/components/ExportButtons.vue
Normal file
27
src/components/ExportButtons.vue
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mt-4 space-x-2">
|
||||||
|
<button @click="downloadPDF" class="bg-blue-500 text-white px-4 py-2 rounded">Export PDF</button>
|
||||||
|
<button @click="downloadExcel" class="bg-green-500 text-white px-4 py-2 rounded">Export Excel</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import html2pdf from 'html2pdf.js';
|
||||||
|
import * as XLSX from 'xlsx';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
report: Object,
|
||||||
|
printRef: Object
|
||||||
|
});
|
||||||
|
|
||||||
|
function downloadPDF() {
|
||||||
|
html2pdf().from(props.printRef.value.$el).save('report.pdf');
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadExcel() {
|
||||||
|
const ws = XLSX.utils.json_to_sheet(props.report.samples);
|
||||||
|
const wb = XLSX.utils.book_new();
|
||||||
|
XLSX.utils.book_append_sheet(wb, ws, "Report");
|
||||||
|
XLSX.writeFile(wb, "report.xlsx");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
40
src/components/PagePreview.vue
Normal file
40
src/components/PagePreview.vue
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="printArea" class="border p-4 bg-white">
|
||||||
|
<div>
|
||||||
|
<h2 class="font-semibold">Report ID: {{ report.reportId }}</h2>
|
||||||
|
<p><strong>Date:</strong> {{ report.date }}</p>
|
||||||
|
<p><strong>Site:</strong> {{ report.site }}</p>
|
||||||
|
</div>
|
||||||
|
<table class="table-auto w-full mt-4 border">
|
||||||
|
<thead>
|
||||||
|
<tr class="bg-gray-100">
|
||||||
|
<th class="border px-2 py-1">ID</th>
|
||||||
|
<th class="border px-2 py-1">Date</th>
|
||||||
|
<th class="border px-2 py-1">a (mm)</th>
|
||||||
|
<th class="border px-2 py-1">b (mm)</th>
|
||||||
|
<th class="border px-2 py-1">h (mm)</th>
|
||||||
|
<th class="border px-2 py-1">Mass (kg/m³)</th>
|
||||||
|
<th class="border px-2 py-1">Rc (N/mm²)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="sample in report.samples" :key="sample.id">
|
||||||
|
<td class="border px-2 py-1">{{ sample.id }}</td>
|
||||||
|
<td class="border px-2 py-1">{{ sample.date }}</td>
|
||||||
|
<td class="border px-2 py-1">{{ sample.a }}</td>
|
||||||
|
<td class="border px-2 py-1">{{ sample.b }}</td>
|
||||||
|
<td class="border px-2 py-1">{{ sample.h || '-' }}</td>
|
||||||
|
<td class="border px-2 py-1">{{ sample.mass }}</td>
|
||||||
|
<td class="border px-2 py-1">{{ sample.rc }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="mt-4 italic text-sm">{{ report.note }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
report: Object
|
||||||
|
});
|
||||||
|
</script>
|
||||||
98
src/components/risultati/A_CIL.vue
Normal file
98
src/components/risultati/A_CIL.vue
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<template>
|
||||||
|
<h3 class="section-title">DATI DICHIARATI ALL’ACCETTAZIONE</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID<br>Provino</th>
|
||||||
|
<th>Contrassegno</th>
|
||||||
|
<th>Verbale<br>di prelievo n.</th>
|
||||||
|
<th>Data<br>prelievo</th>
|
||||||
|
<th>R<sub>CK</sub></th>
|
||||||
|
<th>Area o elemento strutturale</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(row, index) in datiacc_data" :key="row.idProvino">
|
||||||
|
<td>{{ row.idProvino }}</td>
|
||||||
|
<td>{{ row.contrassegno }}</td>
|
||||||
|
<td>{{ row.verbale }}</td>
|
||||||
|
<td>{{ formatDate(row.dataPrelievo) }}</td>
|
||||||
|
<td>{{ formatNumber(row.rck, 0) }}</td>
|
||||||
|
<td v-if="row.rowspanArea > 0" :rowspan="row.rowspanArea">{{ row.area }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- RISULTATI -->
|
||||||
|
|
||||||
|
<div class="section-title">{{ intestazione.prova1 }}</div>
|
||||||
|
<div class="sub-header">{{ intestazione.prova2 }}</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="5">DATI PRELIMINARI ALLA PROVA</th>
|
||||||
|
<th colspan="5">RESISTENZA ALLA COMPRESSIONE</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th rowspan="3">ID<br>Provino</th>
|
||||||
|
<th rowspan="3">Data<br>prova</th>
|
||||||
|
<th colspan="2">Dimensioni</th>
|
||||||
|
<th rowspan="2">Massa<br>volumica</th>
|
||||||
|
<th rowspan="2">F</th>
|
||||||
|
<th rowspan="2">R<sub>c</sub></th>
|
||||||
|
<th rowspan="2">R<sub>PRELIEVO</sub></th>
|
||||||
|
<th rowspan="3">R**</th>
|
||||||
|
<th rowspan="3">P***</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>D</th>
|
||||||
|
<th>h</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>[mm]</th>
|
||||||
|
<th>[mm]</th>
|
||||||
|
<th>[kg/m³]</th>
|
||||||
|
<th>[kN]</th>
|
||||||
|
<th>[N/mm²]</th>
|
||||||
|
<th>[N/mm²]</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="row in risult_data" :key="row.idProvino">
|
||||||
|
<td>{{ row.idProvino }}</td>
|
||||||
|
<td>{{ formatDate(row.dataProva) }}</td>
|
||||||
|
<td>{{ formatNumber(row.dimA, 2) }}</td>
|
||||||
|
<td>{{ formatNumber(row.dimH, 2) }}</td>
|
||||||
|
<td>{{ formatNumber(row.massaVolumica, 0) }}</td>
|
||||||
|
<td>{{ formatNumber(row.f, 1) }}</td>
|
||||||
|
<td>{{ formatNumber(row.rc, 2) }}</td>
|
||||||
|
<td v-if="row.rowspanRprelievo > 0" :rowspan="row.rowspanRprelievo">{{ row.rprelievo }}</td>
|
||||||
|
<td>{{ row.rType }}</td>
|
||||||
|
<td>{{ row.pType }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
risult_data: Array,
|
||||||
|
datiacc_data: Array,
|
||||||
|
intestazione: Object
|
||||||
|
})
|
||||||
|
|
||||||
|
function formatDate(dateStr) {
|
||||||
|
return new Date(dateStr).toLocaleDateString('it-IT')
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(val, decimals) {
|
||||||
|
return val != null
|
||||||
|
? Number(val).toLocaleString('it-IT', {
|
||||||
|
minimumFractionDigits: decimals,
|
||||||
|
maximumFractionDigits: decimals
|
||||||
|
})
|
||||||
|
: '—'
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
101
src/components/risultati/A_CUB.vue
Normal file
101
src/components/risultati/A_CUB.vue
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<template>
|
||||||
|
<h3 class="section-title">DATI DICHIARATI ALL’ACCETTAZIONE</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID<br>Provino</th>
|
||||||
|
<th>Contrassegno</th>
|
||||||
|
<th>Verbale<br>di prelievo n.</th>
|
||||||
|
<th>Data<br>prelievo</th>
|
||||||
|
<th>R<sub>CK</sub></th>
|
||||||
|
<th>Area o elemento strutturale</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(row, index) in datiacc_data" :key="row.idProvino">
|
||||||
|
<td>{{ row.idProvino }}</td>
|
||||||
|
<td>{{ row.contrassegno }}</td>
|
||||||
|
<td>{{ row.verbale }}</td>
|
||||||
|
<td>{{ formatDate(row.dataPrelievo) }}</td>
|
||||||
|
<td>{{ formatNumber(row.rck, 0) }}</td>
|
||||||
|
<td v-if="row.rowspanArea > 0" :rowspan="row.rowspanArea">{{ row.area }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- RISULTATI -->
|
||||||
|
|
||||||
|
<div class="section-title">{{ intestazione.prova1 }}</div>
|
||||||
|
<div class="sub-header">{{ intestazione.prova2 }}</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="6">DATI PRELIMINARI ALLA PROVA</th>
|
||||||
|
<th colspan="5">RESISTENZA ALLA COMPRESSIONE</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th rowspan="3">ID<br>Provino</th>
|
||||||
|
<th rowspan="3">Data<br>prova</th>
|
||||||
|
<th colspan="3">Dimensioni</th>
|
||||||
|
<th rowspan="2">Massa<br>volumica</th>
|
||||||
|
<th rowspan="2">F</th>
|
||||||
|
<th rowspan="2">R<sub>c</sub></th>
|
||||||
|
<th rowspan="2">R<sub>PRELIEVO</sub></th>
|
||||||
|
<th rowspan="3">R**</th>
|
||||||
|
<th rowspan="3">P***</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>a</th>
|
||||||
|
<th>b</th>
|
||||||
|
<th>h</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>[mm]</th>
|
||||||
|
<th>[mm]</th>
|
||||||
|
<th>[mm]</th>
|
||||||
|
<th>[kg/m³]</th>
|
||||||
|
<th>[kN]</th>
|
||||||
|
<th>[N/mm²]</th>
|
||||||
|
<th>[N/mm²]</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="row in risult_data" :key="row.idProvino">
|
||||||
|
<td>{{ row.idProvino }}</td>
|
||||||
|
<td>{{ formatDate(row.dataProva) }}</td>
|
||||||
|
<td>{{ formatNumber(row.dimA, 2) }}</td>
|
||||||
|
<td>{{ formatNumber(row.dimB, 2) }}</td>
|
||||||
|
<td>{{ formatNumber(row.dimH, 2) }}</td>
|
||||||
|
<td>{{ formatNumber(row.massaVolumica, 0) }}</td>
|
||||||
|
<td>{{ formatNumber(row.f, 1) }}</td>
|
||||||
|
<td>{{ formatNumber(row.rc, 2) }}</td>
|
||||||
|
<td v-if="row.rowspanRprelievo > 0" :rowspan="row.rowspanRprelievo">{{ row.rprelievo }}</td>
|
||||||
|
<td>{{ row.rType }}</td>
|
||||||
|
<td>{{ row.pType }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
risult_data: Array,
|
||||||
|
datiacc_data: Array,
|
||||||
|
intestazione: Object
|
||||||
|
})
|
||||||
|
|
||||||
|
function formatDate(dateStr) {
|
||||||
|
return new Date(dateStr).toLocaleDateString('it-IT')
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(val, decimals) {
|
||||||
|
return val != null
|
||||||
|
? Number(val).toLocaleString('it-IT', {
|
||||||
|
minimumFractionDigits: decimals,
|
||||||
|
maximumFractionDigits: decimals
|
||||||
|
})
|
||||||
|
: '—'
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
98
src/components/risultati/B_BAR.vue
Normal file
98
src/components/risultati/B_BAR.vue
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<template>
|
||||||
|
<h3 class="section-title">DATI PRELIMINARI DI PROVA</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID<br>Provino</th>
|
||||||
|
<th>Contrassegno</th>
|
||||||
|
<th>Verbale<br>di prelievo n.</th>
|
||||||
|
<th>Data<br>prelievo</th>
|
||||||
|
<th>R<sub>CK</sub></th>
|
||||||
|
<th>Area o elemento strutturale</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(row, index) in datiacc_data" :key="row.idProvino">
|
||||||
|
<td>{{ row.idProvino }}</td>
|
||||||
|
<td>{{ row.contrassegno }}</td>
|
||||||
|
<td>{{ row.verbale }}</td>
|
||||||
|
<td>{{ formatDate(row.dataPrelievo) }}</td>
|
||||||
|
<td>{{ formatNumber(row.rck, 0) }}</td>
|
||||||
|
<td v-if="row.rowspanArea > 0" :rowspan="row.rowspanArea">{{ row.area }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- RISULTATI -->
|
||||||
|
|
||||||
|
<div class="section-title">{{ intestazione.prova1 }}</div>
|
||||||
|
<div class="sub-header">{{ intestazione.prova2 }}</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="5">DATI PRELIMINARI ALLA PROVA</th>
|
||||||
|
<th colspan="5">RESISTENZA ALLA COMPRESSIONE</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th rowspan="3">ID<br>Provino</th>
|
||||||
|
<th rowspan="3">Data<br>prova</th>
|
||||||
|
<th colspan="2">Dimensioni</th>
|
||||||
|
<th rowspan="2">Massa<br>volumica</th>
|
||||||
|
<th rowspan="2">F</th>
|
||||||
|
<th rowspan="2">R<sub>c</sub></th>
|
||||||
|
<th rowspan="2">R<sub>PRELIEVO</sub></th>
|
||||||
|
<th rowspan="3">R**</th>
|
||||||
|
<th rowspan="3">P***</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>D</th>
|
||||||
|
<th>h</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>[mm]</th>
|
||||||
|
<th>[mm]</th>
|
||||||
|
<th>[kg/m³]</th>
|
||||||
|
<th>[kN]</th>
|
||||||
|
<th>[N/mm²]</th>
|
||||||
|
<th>[N/mm²]</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="row in risult_data" :key="row.idProvino">
|
||||||
|
<td>{{ row.idProvino }}</td>
|
||||||
|
<td>{{ formatDate(row.dataProva) }}</td>
|
||||||
|
<td>{{ formatNumber(row.dimA, 2) }}</td>
|
||||||
|
<td>{{ formatNumber(row.dimH, 2) }}</td>
|
||||||
|
<td>{{ formatNumber(row.massaVolumica, 0) }}</td>
|
||||||
|
<td>{{ formatNumber(row.f, 1) }}</td>
|
||||||
|
<td>{{ formatNumber(row.rc, 2) }}</td>
|
||||||
|
<td v-if="row.rowspanRprelievo > 0" :rowspan="row.rowspanRprelievo">{{ row.rprelievo }}</td>
|
||||||
|
<td>{{ row.rType }}</td>
|
||||||
|
<td>{{ row.pType }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
risult_data: Array,
|
||||||
|
datiacc_data: Array,
|
||||||
|
intestazione: Object
|
||||||
|
})
|
||||||
|
|
||||||
|
function formatDate(dateStr) {
|
||||||
|
return new Date(dateStr).toLocaleDateString('it-IT')
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(val, decimals) {
|
||||||
|
return val != null
|
||||||
|
? Number(val).toLocaleString('it-IT', {
|
||||||
|
minimumFractionDigits: decimals,
|
||||||
|
maximumFractionDigits: decimals
|
||||||
|
})
|
||||||
|
: '—'
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
5
src/main.js
Normal file
5
src/main.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import './style.css'
|
||||||
|
|
||||||
|
createApp(App).mount('#app')
|
||||||
8
src/style.css
Normal file
8
src/style.css
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
background: #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
17
vite.config.js
Normal file
17
vite.config.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
base: '/vue-report/',
|
||||||
|
build: {
|
||||||
|
outDir: 'dist',
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
entryFileNames: 'assets/main.js',
|
||||||
|
chunkFileNames: 'assets/[name].js',
|
||||||
|
assetFileNames: 'assets/[name][extname]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue
Block a user