- Import di un file XLS contenente una lista di libri, all'interno di un catalogo.

This commit is contained in:
Surya Paolo
2025-07-11 12:55:24 +02:00
parent 2ce8a72286
commit d37797fdad
20 changed files with 568 additions and 80 deletions

4
.env
View File

@@ -1,6 +1,6 @@
VITE_APP_VERSION="1.2.63"
VITE_APP_VERSION="1.2.65"
VITE_LANG_DEFAULT="it"
VITE_PAO_APP_ID="KKPPAA5KJK435J3KSS9F9D8S9F8SD98F9SDF"
VITE_SERVICE_WORKER_FILE="sw-1.2.63.js"
VITE_SERVICE_WORKER_FILE="sw-1.2.65.js"
VITE_PROJECT_ID_MAIN="5cc0a13fe5c9d156728f400a"
VITE_VUE_ROUTER_MODE="history"

View File

@@ -10,7 +10,7 @@
<meta name="description" content="<%= productDescription %>">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="version" content="1.2.63">
<meta name="version" content="1.2.65">
<meta name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>">

View File

@@ -1,6 +1,6 @@
{
"name": "gruppomacro",
"version": "1.2.63",
"version": "1.2.65",
"description": "Gruppo Macro",
"productName": "Gruppo Macro",
"author": "Surya",
@@ -9,20 +9,20 @@
"license": "MIT",
"type": "module",
"scripts": {
"dev": "PORT=8089 APP_VERSION='1.2.63' quasar dev",
"dev": "PORT=8089 APP_VERSION='1.2.65' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "APP_VERSION='1.2.63' quasar build -m spa",
"buildspa": "APP_VERSION='1.2.65' quasar build -m spa",
"lint": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\"",
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8099 APP_VERSION='1.2.63' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8089 APP_VERSION='1.2.63' quasar dev",
"pwa": "NODE_ENV=development PORT=8099 APP_VERSION='1.2.65' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8089 APP_VERSION='1.2.65' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{
"name": "cnm",
"version": "1.2.63",
"version": "1.2.65",
"description": "Comunita Nuovo Mondo",
"productName": "ComunitaNuovoMondo",
"author": "Surya",
@@ -9,7 +9,7 @@
"license": "MIT",
"type": "module",
"scripts": {
"dev": "PORT=8083 APP_VERSION='1.2.63' quasar dev",
"dev": "PORT=8083 APP_VERSION='1.2.65' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build",
"buildpwa": "NODE_ENV=production quasar build -m pwa",
@@ -21,8 +21,8 @@
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.63' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.63' quasar dev",
"pwa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.65' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.65' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{
"name": "freeplanet",
"version": "1.2.63",
"version": "1.2.65",
"description": "freeplanet",
"productName": "freeplanet",
"author": "Surya",
@@ -9,11 +9,11 @@
"license": "MIT",
"type": "module",
"scripts": {
"dev": "PORT=8087 APP_VERSION='1.2.63' quasar dev",
"dev": "PORT=8087 APP_VERSION='1.2.65' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa",
@@ -21,8 +21,8 @@
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8097 APP_VERSION='1.2.63' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8087 APP_VERSION='1.2.63' quasar dev",
"pwa": "NODE_ENV=development PORT=8097 APP_VERSION='1.2.65' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8087 APP_VERSION='1.2.65' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{
"name": "gruppomacro",
"version": "1.2.63",
"version": "1.2.65",
"description": "Gruppo Macro",
"productName": "Gruppo Macro",
"author": "Surya",
@@ -9,20 +9,20 @@
"license": "MIT",
"type": "module",
"scripts": {
"dev": "PORT=8089 APP_VERSION='1.2.63' quasar dev",
"dev": "PORT=8089 APP_VERSION='1.2.65' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "APP_VERSION='1.2.63' quasar build -m spa",
"buildspa": "APP_VERSION='1.2.65' quasar build -m spa",
"lint": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\"",
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8099 APP_VERSION='1.2.63' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8089 APP_VERSION='1.2.63' quasar dev",
"pwa": "NODE_ENV=development PORT=8099 APP_VERSION='1.2.65' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8089 APP_VERSION='1.2.65' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{
"name": "kolibrilab",
"version": "1.2.63",
"version": "1.2.65",
"description": "kolibrilab",
"productName": "kolibrilab",
"author": "Surya Paolo",
@@ -8,11 +8,11 @@
"keywords": [],
"license": "MIT",
"scripts": {
"dev": "PORT=8083 APP_VERSION='1.2.63' quasar dev",
"dev": "PORT=8083 APP_VERSION='1.2.65' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa",
@@ -20,8 +20,8 @@
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint --ext .ts,.vue --ignore-path .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.63' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.63' quasar dev",
"pwa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.65' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8083 APP_VERSION='1.2.65' quasar dev",
"spanorefresh": "NODE_ENV=development NODE_OPTIONS=--max_old_space_size=4096 DEBUG=v8:* quasar dev -m spa",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js"

View File

@@ -1,6 +1,6 @@
{
"name": "nutriben",
"version": "1.2.63",
"version": "1.2.65",
"description": "Nutriben",
"productName": "Nutriben",
"author": "Surya",
@@ -9,20 +9,20 @@
"license": "MIT",
"type": "module",
"scripts": {
"dev": "PORT=8093 APP_VERSION='1.2.63' quasar dev",
"dev": "PORT=8093 APP_VERSION='1.2.65' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "APP_VERSION='1.2.63' quasar build -m spa",
"buildspa": "APP_VERSION='1.2.65' quasar build -m spa",
"lint": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\"",
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8099 APP_VERSION='1.2.63' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.63' quasar dev",
"pwa": "NODE_ENV=development PORT=8099 APP_VERSION='1.2.65' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8093 APP_VERSION='1.2.65' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{
"name": "piuchebuono",
"version": "1.2.63",
"version": "1.2.65",
"description": "PiuCheBuono",
"productName": "PiuCheBuono",
"author": "Surya",
@@ -9,11 +9,11 @@
"license": "MIT",
"type": "module",
"scripts": {
"dev": "PORT=8085 APP_VERSION='1.2.63' quasar dev",
"dev": "PORT=8085 APP_VERSION='1.2.65' quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa",
@@ -21,8 +21,8 @@
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8085 APP_VERSION='1.2.63' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8085 APP_VERSION='1.2.63' quasar dev",
"pwa": "NODE_ENV=development PORT=8085 APP_VERSION='1.2.65' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8085 APP_VERSION='1.2.65' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -1,6 +1,6 @@
{
"name": "riso",
"version": "1.2.63",
"version": "1.2.65",
"description": "Siamo la Rete Italiana di Scambio Orizzontale, abbiamo creato questa piattaforma per metterla al servizio di chi vuole riscoprire il valore della condivisione e della cooperazione. Valori semplici e profondi che ci aiutano a ritrovare il Senso della Vita, perduto in questa società consumista, e riporti quei Sani Pricìpi Naturali ed Umani di Fratellanza che intere popolazioni antiche conoscevano bene.",
"productName": "Riso",
"author": "Surya",
@@ -9,11 +9,11 @@
"license": "MIT",
"type": "module",
"scripts": {
"dev": "APP_VERSION='1.2.63' PORT=8084 quasar dev",
"dev": "APP_VERSION='1.2.65' PORT=8084 quasar dev",
"dev_noCheck": "SKIP_TSC=true quasar dev",
"build": "quasar build",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.63' quasar build -m pwa",
"buildpwa": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"buildpwatest": "NODE_ENV=production APP_VERSION='1.2.65' quasar build -m pwa",
"type-check": "vue-tsc --noEmit",
"type-check:watch": "vue-tsc --noEmit --watch",
"buildspa": "quasar build -m spa",
@@ -21,8 +21,8 @@
"lintfile": "eslint --ext .js,.ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"lintfileNoJS": "eslint --ext .ts,.vue --ignore-path .gitignore ./ > file.out.txt",
"fix": "eslint -c ./eslint.config.js \"./src*/**/*.{ts,js,cjs,mjs,vue}\" --ignore-pattern .gitignore ./ --fix > file.out.txt",
"pwa": "NODE_ENV=development PORT=8094 APP_VERSION='1.2.63' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8084 APP_VERSION='1.2.63' quasar dev",
"pwa": "NODE_ENV=development PORT=8094 APP_VERSION='1.2.65' quasar dev -m pwa",
"spa": "NODE_ENV=development PORT=8084 APP_VERSION='1.2.65' quasar dev",
"debug": "quasar dev --mode debug",
"test": "echo \"No test specified\" && exit 0",
"generate-sw": "workbox generateSW workbox-config.js",

View File

@@ -3,7 +3,7 @@
/* global workbox */
/* global cfgenv */
const VITE_APP_VERSION = '1.2.63';
const VITE_APP_VERSION = '1.2.65';
// Costanti di configurazione
const DYNAMIC_CACHE = 'dynamic-cache-v2';

View File

@@ -163,7 +163,7 @@
>
</q-btn>
</div>
<div v-if="!optcatalogo.generazionePDFInCorso">
<div v-if="!optcatalogo.generazionePDFInCorso && tools.isLogged()">
<q-btn
icon-right="fas fa-cart-plus"
color="positive"

View File

@@ -0,0 +1,23 @@
button {
margin-top: 10px;
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
input[type="file"] {
margin-top: 20px;
}
.error {
color: red;
margin-top: 10px;
}

View File

@@ -0,0 +1,313 @@
import type { PropType } from 'vue';
import {
defineComponent,
ref,
toRef,
computed,
watch,
onMounted,
reactive,
onBeforeUnmount,
} from 'vue';
import { useI18n } from 'vue-i18n';
import { useUserStore } from '@store/UserStore';
import { useGlobalStore } from '@store/globalStore';
import { useQuasar } from 'quasar';
import { tools } from '@tools';
import { useProducts } from '@store/Products';
import { shared_consts } from '@src/common/shared_vuejs';
import { useRouter } from 'vue-router';
import { costanti } from '@costanti';
import * as XLSX from 'xlsx';
import { Api } from 'app/src/store/Api';
export default defineComponent({
name: 'CImportListaTitoli',
emits: ['addArrayTitlesToList'],
props: {},
components: {},
setup(props, { emit }) {
const $q = useQuasar();
const { t } = useI18n();
const userStore = useUserStore();
const globalStore = useGlobalStore();
const Products = useProducts();
// Stati reattivi
const fileData = ref<any[]>([]);
const searchResults = ref<any[]>([]);
const loading = ref(false);
const error = ref<string | null>(null);
const columns = ref([
{
name: 'title',
label: 'Titolo',
field: 'title',
align: 'left',
sortable: true,
},
{
name: 'isbn',
label: 'ISBN',
field: 'isbn',
align: 'left',
sortable: true,
},
{
name: 'select',
label: 'Seleziona',
field: 'select',
align: 'left',
},
]);
const onRequest = (props: any) => {
const { page, rowsPerPage, sortBy, descending } = props.pagination;
const filterValue = filter.value;
const filteredRows = searchResults.value
.filter((row) => {
const title = row.title?.toLowerCase() || '';
const author = row.author?.toLowerCase() || '';
const isbn = row.isbn?.toLowerCase() || '';
return (
title.includes(filterValue.toLowerCase()) ||
author.includes(filterValue.toLowerCase()) ||
isbn.includes(filterValue.toLowerCase())
);
})
.sort((a, b) => {
const sortA = a[sortBy];
const sortB = b[sortBy];
if (descending) {
return sortA < sortB ? 1 : -1;
} else {
return sortA > sortB ? 1 : -1;
}
});
pagination.rowsNumber = filteredRows.length;
pagination.page = page;
pagination.rowsPerPage = rowsPerPage;
pagination.sortBy = sortBy;
pagination.descending = descending;
return {
pagination,
rows: filteredRows.slice((page - 1) * rowsPerPage, page * rowsPerPage),
};
};
const filter = ref('');
const importSelectedBooks = () => {
if (searchResults.value.length === 0) {
$q.notify({
message: t('Nessun libro selezionato'),
color: 'warning',
});
return;
}
// Aggiungi i libri selezionati alla lista dell'utente
emit(
'addArrayTitlesToList',
searchResults.value.filter((row) => row.select),
);
};
const pagination = reactive({
sortBy: 'title',
descending: false,
page: 1,
rowsPerPage: 50,
rowsNumber: searchResults.value.length,
});
const filteredResults = computed(() => {
const { sortBy, descending, page, rowsPerPage, rowsNumber } = pagination;
const firstIndex = (page - 1) * rowsPerPage;
const lastIndex = firstIndex + rowsPerPage;
return searchResults.value.slice(firstIndex, lastIndex).sort((a, b) => {
const aValue = a[sortBy];
const bValue = b[sortBy];
return descending ? bValue.localeCompare(aValue) : aValue.localeCompare(bValue);
});
});
// Gestore del caricamento del file
const handleFileUpload = (event: Event) => {
const file = (event.target as HTMLInputElement).files?.[0];
if (file) {
loading.value = true;
error.value = null;
const reader = new FileReader();
reader.onload = async () => {
try {
// Verifica il tipo di file (CSV o Excel) e parse
if (file.name.endsWith('.csv')) {
fileData.value = await parseCSV(reader.result as string);
} else if (file.name.endsWith('.xls') || file.name.endsWith('.xlsx')) {
fileData.value = await parseExcel(reader.result as ArrayBuffer);
}
} catch (err) {
error.value = 'Errore nel parsing del file: ' + err;
} finally {
loading.value = false;
}
};
// Leggi il file con il metodo appropriato basato sul tipo
if (file.name.endsWith('.csv')) {
reader.readAsText(file);
} else if (file.name.endsWith('.xls') || file.name.endsWith('.xlsx')) {
reader.readAsArrayBuffer(file); // ← Questa è la correzione principale
}
}
};
// Funzione di ricerca dei libri
const searchBooksHandler = async () => {
if (!fileData.value || fileData.value.length === 0) {
return;
}
loading.value = true;
error.value = null;
try {
// Esegui la ricerca con la funzione di backend
searchResults.value = await searchBooks(fileData.value);
if (searchResults.value && searchResults.value.length > 0) {
$q.notify({
message: `Trovati ${searchResults.value.length} libri`,
color: 'positive',
});
}
if (searchResults.value && searchResults.value.length === 0) {
$q.notify({
message: 'Nessun libro importabile',
color: 'negative',
});
}
} catch (err) {
error.value = 'Errore nella ricerca dei libri';
} finally {
loading.value = false;
}
};
async function parseCSV(csvText: string): Promise<any[]> {
// Funzione per analizzare un file CSV
const lines = csvText.split('\n');
const headers = lines[0].split(',');
const data = lines.slice(1).map((line) => {
const values = line.split(',');
return headers.reduce((acc, header, index) => {
acc[header.trim()] = values[index].trim();
return acc;
}, {} as any);
});
return data;
}
async function parseExcel(excelData: ArrayBuffer): Promise<any[]> {
try {
// Debug: verifica il tipo e la dimensione di excelData
console.log('Tipo di excelData:', typeof excelData);
console.log('È ArrayBuffer?', excelData instanceof ArrayBuffer);
console.log('Dimensione excelData:', excelData.byteLength);
// Usa direttamente l'ArrayBuffer con type: 'buffer'
const workbook = XLSX.read(excelData, { type: 'buffer' });
// Controllo se ci sono fogli
if (!workbook.SheetNames || workbook.SheetNames.length === 0) {
throw new Error('Nessun foglio trovato nel file Excel');
}
console.log('Fogli disponibili:', workbook.SheetNames);
// Prendi il nome del primo foglio
const sheetName = workbook.SheetNames[0];
const sheet = workbook.Sheets[sheetName];
// Controllo se il foglio esiste
if (!sheet) {
throw new Error(`Il foglio "${sheetName}" non è stato trovato`);
}
// Debug: mostra la struttura del foglio
console.log('Foglio:', sheetName);
console.log('Range del foglio:', sheet['!ref']);
console.log(
'Tutte le celle:',
Object.keys(sheet).filter((key) => !key.startsWith('!'))
);
// Usa sheet_to_json per convertire il foglio in array di righe
const rows = XLSX.utils.sheet_to_json(sheet, {
header: 1,
defval: '',
raw: false,
});
console.log('Righe estratte:', rows.length);
console.log('Prime 3 righe:', rows.slice(0, 3));
// Filtra le righe completamente vuote
const filteredRows = rows.filter(
(row: any[]) =>
Array.isArray(row) &&
row.some((cell) => cell !== null && cell !== undefined && cell !== '')
);
return filteredRows;
} catch (error) {
console.error("Errore durante l'analisi del file Excel:", error);
throw new Error(`Errore durante l'analisi del file Excel: ${error.message}`);
}
}
async function searchBooks(books: any[]): Promise<any[]> {
const response = await Api.SendReq('/api/search-books', 'POST', { books });
if (response.status !== 200) {
throw new Error('Errore nella risposta del server');
}
return response.data; // Supponiamo che il backend ritorni un array di oggetti con id e title
}
function deselectAll() {
searchResults.value.forEach((row) => (row.select = false));
}
return {
handleFileUpload,
searchBooks: searchBooksHandler,
searchResults,
loading,
error,
fileData,
pagination,
filteredResults,
importSelectedBooks,
filter,
onRequest,
columns,
deselectAll,
};
},
});

View File

@@ -0,0 +1,87 @@
<template>
<div class="row justify-center">
<h3>Carica un file Excel contenente l'ISBN oppure il nome del titolo del libro</h3>
<!-- Input per il file -->
<input
type="file"
@change="handleFileUpload"
accept=".csv, .xls, .xlsx"
/>
<br />
<q-btn
class="row"
@click="searchBooks"
:disabled="!fileData || loading"
>
Cerca Libri
</q-btn>
<br />
<!-- Risultati della ricerca -->
<div v-if="searchResults.length">
<h3>{{ searchResults.length }} Libri trovati:</h3>
<br />
<q-btn
@click="deselectAll"
:disabled="!searchResults.length"
color="negative"
dense
>
Deseleziona tutti
</q-btn>
<q-table
:columns="columns"
:rows="searchResults"
row-key="_id"
:filter="filter"
:pagination.sync="pagination"
:loading="loading"
:rows-per-page-options="[0]"
@request="onRequest"
binary-state-sort
>
<template v-slot:top-right>
<!--<q-input
borderless
dense
debounce="300"
v-model="filter"
placeholder="Cerca"
>
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>-->
</template>
<template v-slot:body-cell-select="props">
<q-td :props="props">
<q-checkbox v-model="props.row.select" />
</q-td>
</template>
</q-table>
<q-btn
color="primary"
@click="importSelectedBooks"
:disabled="!searchResults.some((r) => r.select)"
label="Importa selezionati"
/>
</div>
<!-- Messaggio di errore -->
<div
v-if="error"
class="error"
>
{{ error }}
</div>
</div>
</template>
<script lang="ts" src="./CImportListaTitoli.ts"></script>
<style lang="scss" scoped>
@import './CImportListaTitoli.scss';
</style>

View File

@@ -0,0 +1 @@
export {default as CImportListaTitoli} from './CImportListaTitoli.vue'

View File

@@ -23,6 +23,7 @@ import { CSchedaProdotto } from '@src/components/CSchedaProdotto';
import { CSearchProduct } from '@src/components/CSearchProduct';
import { CMyDialog } from '@src/components/CMyDialog';
import { CModifTrafiletto } from '@src/components/CModifTrafiletto';
import { CImportListaTitoli } from '@src/components/CImportListaTitoli';
import { costanti } from '@costanti';
import { IAuthor, ICatProd } from 'app/src/model';
@@ -47,12 +48,18 @@ export default defineComponent({
CLabel,
CSchedaProdotto,
CModifTrafiletto,
CImportListaTitoli,
},
props: {
lista_prodotti: {
type: Array,
required: true,
},
canadd: {
trype: Boolean,
required: false,
default: false,
},
lista_prod_confronto: {
type: Array,
required: false,
@@ -128,6 +135,7 @@ export default defineComponent({
const addstr = ref('');
const showDialogExport = ref(false);
const showDialogImport = ref(false);
const selectedExportColumns = ref([]);
const optionscatalogo = ref(<any>{ maxlength: 0 });
@@ -138,7 +146,7 @@ export default defineComponent({
}
function riaggiornaListaProdAlGenitore() {
emit('update:lista_prodotti', internalProducts.value);
aggiornaLista();
}
const editOn = computed({
@@ -551,7 +559,8 @@ export default defineComponent({
};
const savedColumns = tools.getCookie(addstr.value + 'selColCat_2');
selectedExportColumns.value = tools.getCookie(addstr.value + 'Exp_Columns');
const col = tools.getCookie(addstr.value + 'Exp_Columns', null);
selectedExportColumns.value = col ? col : [];
if (savedColumns) {
selectedColumns.value = savedColumns;
}
@@ -987,13 +996,27 @@ export default defineComponent({
persistent: false,
})
.onOk(() => {
internalProducts.value = internalProducts.value.filter(
(p: any) => p._id !== product._id
);
emit('update:lista_prodotti', internalProducts.value); // Notifica il parent del cambiamento
aggiornaLista(product);
});
};
function aggiornaLista(deleteelem: any = null) {
const precsearch = searchText.value;
searchText.value = '';
internalProducts.value = [...props.lista_prodotti];
if (deleteelem) {
internalProducts.value = internalProducts.value.filter(
(p: any) => p._id !== deleteelem._id
);
}
emit('update:lista_prodotti', internalProducts.value); // Notifica il parent del cambiamento
searchText.value = precsearch;
}
// 8. Salvataggio delle colonne selezionate in un cookie
const saveSelectedColumns = () => {
tools.setCookie(
@@ -1031,7 +1054,7 @@ export default defineComponent({
// Funzione chiamata alla fine del drag-and-drop
const onDragEnd = () => {
// console.log("Nuovo ordine:", internalProducts.value);
emit('update:lista_prodotti', internalProducts.value); // Notifica il parent del cambiamento
aggiornaLista();
};
function formatAuthors(authors: IAuthor[] | undefined | null): string {
@@ -1096,7 +1119,7 @@ export default defineComponent({
return prod;
});
emit('update:lista_prodotti', internalProducts.value); // Notifica il parent del cambiamento
aggiornaLista();
}
async function updateproductmodif(element: any) {
@@ -1184,7 +1207,7 @@ export default defineComponent({
emit('rigenera');
}
function addtolist(element) {
function addtolist(element: any) {
emit('addtolist', element);
}
@@ -1221,13 +1244,9 @@ export default defineComponent({
) {
saveSelectedColumnsExport(columns);
const csvContent = [
columns
.filter((col) => !columns.find((c) => c.name === col)?.noexp)
.map((col) => getColumnLabelByName(col))
.join(separatore),
columns.map((col) => getColumnLabelByName(col)).join(separatore),
...internalProducts.value.map((product: any) => {
return columns
.filter((col) => !columns.find((c) => c.name === col)?.noexp)
.map((col: string) => {
const field = { field: col };
return field.field === 'pos'
@@ -1257,15 +1276,15 @@ export default defineComponent({
columns: any[],
separatore: string = '\t'
) {
if (!Array.isArray(columns)) {
console.error('Errore: columns non è un array:', columns);
return;
}
saveSelectedColumnsExport(columns);
const csvContent = [
columns
.filter((col) => !columns.find((c) => c.name === col)?.noexp)
.map((col) => getColumnLabelByName(col))
.join(separatore),
columns.map((col) => getColumnLabelByName(col)).join(separatore),
...internalProducts.value.map((product: any) => {
return columns
.filter((col) => !columns.find((c) => c.name === col)?.noexp)
.map((col: string) => {
const field = { field: col };
return field.field === 'pos'
@@ -1276,6 +1295,12 @@ export default defineComponent({
}),
].join('\r\n');
// Verifica che csvContent non sia vuoto e che abbia un formato valido
if (!csvContent || csvContent.trim() === '') {
console.error('Errore: csvContent è vuoto o malformato');
return;
}
// Creazione del file XLS dopo il CSV
exportToXLS(csvContent, title);
}
@@ -1403,6 +1428,16 @@ export default defineComponent({
});
}
function addArrayTitlesToList(myarr: IProduct[]) {
console.log('addArrayTitlesToList');
for (const elem of myarr) {
addtolist(elem);
}
showDialogImport.value = false; // chiudi dialog
}
onMounted(mounted);
return {
@@ -1459,8 +1494,10 @@ export default defineComponent({
isElementVisible,
fabexp,
showDialogExport,
showDialogImport,
selectedExportColumns,
allColumnsToExported,
addArrayTitlesToList,
};
},
});

View File

@@ -44,9 +44,7 @@
<q-icon name="settings" />
</template>
</q-select>
<q-dialog
v-model="showDialogExport"
>
<q-dialog v-model="showDialogExport">
<q-card style="min-width: 400px">
<q-card-section>
<div class="text-h6">Esporta {{ title }}</div>
@@ -112,14 +110,40 @@
</q-card-actions>
</q-card>
</q-dialog>
<q-dialog v-model="showDialogImport">
<q-card style="min-width: 800px">
<q-card-section>
<div class="text-h6">Importa su {{ title }}</div>
</q-card-section>
<q-card-section class="q-pt-none">
<q-markup-table
separator="cell"
flat
bordered
>
<CImportListaTitoli @addArrayTitlesToList="addArrayTitlesToList"></CImportListaTitoli>
</q-markup-table>
</q-card-section>
</q-card>
</q-dialog>
<q-btn
color="positive"
icon="archive"
label="Esporta"
label="Esporta Lista"
flat
dense
@click="showDialogExport = true"
/>
<q-btn
v-if="tools.isLogged() && canadd && tools.isCollaboratore()"
color="accent"
icon="fas fa-file-import"
label="Importa da XLS"
flat
dense
@click="showDialogImport = true"
/>
</div>
</div>

View File

@@ -2394,7 +2394,7 @@ export default defineComponent({
}
function addProductToList(element: IProduct, where: string) {
// console.log('addProductToList', element)
console.log('addProductToList', element)
if (element) {
// add this record to lista_prodotti

View File

@@ -203,7 +203,7 @@
></q-btn>-->
<q-btn
v-if="!showListaFiltrata"
v-if="!showListaFiltrata && tools.isCollaboratore()"
rounded
label="Aggiungi"
icon="fas fa-plus"
@@ -253,6 +253,8 @@
table="products"
@rigenera="generaListaLibri()"
:title="getTitoloCatalogo()"
:canadd="!showListaFiltrata"
@addtolist="(elem: any) => addProductToList(elem, shared_consts.WHERE_INSERT.ONBOTTOM)"
/>
</q-tab-panel>
<q-tab-panel
@@ -1862,7 +1864,8 @@
table="products"
@rigenera="generaListaLibri()"
:title="getTitoloCatalogo()"
@addtolist="(elem) => addProductToList(elem, shared_consts.WHERE_INSERT.ONTOP)"
:canadd="!showListaFiltrata"
@addtolist="(elem: any) => addProductToList(elem, shared_consts.WHERE_INSERT.ONTOP)"
/>
</q-tab-panel>
</q-tab-panels>