186 lines
7.9 KiB
JavaScript
186 lines
7.9 KiB
JavaScript
/* ===== RadiusOntParser.js (Vue 3 + TT-Core) ===== */
|
|
|
|
const RadiusOntParser = {
|
|
name: 'RadiusOntParser',
|
|
template: `
|
|
<div class="tt-scope ont-card">
|
|
<div v-if="step===1">
|
|
<div class="block-head">
|
|
<div class="h4"><i class="fa-duotone fa-file-spreadsheet"></i> Schritt 1 · Excel (XLSX) Upload</div>
|
|
<div class="muted small">Laden Sie eine XLSX-Datei mit Ihren Kundendaten.</div>
|
|
</div>
|
|
<tt-file-dropzone accept=".xlsx,.xls" @file-selected="readXlsx" />
|
|
</div>
|
|
<div v-if="step===2">
|
|
<div class="block-head">
|
|
<div class="h4"><i class="fa-duotone fa-sliders"></i> Schritt 2 · Spaltenzuordnung</div>
|
|
</div>
|
|
<div class="grid g-4 cols-2 cols-1@sm">
|
|
<div class="field" v-for="field in requiredFields" :key="field.key">
|
|
<label>{{ field.label }}</label>
|
|
<div class="select">
|
|
<select v-model="selectedColumns[field.key]">
|
|
<option v-for="h in headers" :key="h" :value="h">{{ h }}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="cluster mt-3">
|
|
<button class="primary-btn" @click="startProcessing">
|
|
<i class="fa-duotone fa-play"></i> Verarbeitung starten
|
|
</button>
|
|
<button class="ghost-btn" @click="step = 1">
|
|
<i class="fa-duotone fa-arrow-left"></i> Zurück
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div v-if="step===3">
|
|
<div class="block-head">
|
|
<div class="h4"><i class="fa-duotone fa-list-check"></i> Schritt 3 · Ergebnisse</div>
|
|
<div class="cluster">
|
|
<button class="primary-btn" @click="downloadResults" :disabled="loading">
|
|
<i class="fa-duotone fa-download"></i> Neue Excel herunterladen
|
|
</button>
|
|
<button class="ghost-btn" @click="step = 2" :disabled="loading">
|
|
<i class="fa-duotone fa-arrow-left"></i> Zurück
|
|
</button>
|
|
<button class="ghost-btn" @click="resetLocal" :disabled="loading">
|
|
<i class="fa-duotone fa-rotate-right"></i> Neue Verarbeitung
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="results-container mt-between">
|
|
<tt-loading-indicator
|
|
v-if="loading"
|
|
:text="'Aktueller Kunde: ' + (currentCustomerNumber || '—')"
|
|
:progress="progress"
|
|
style="min-height: 200px;"
|
|
/>
|
|
<tt-data-table
|
|
v-else
|
|
:items="processedData"
|
|
:has-searched="true"
|
|
no-results-placeholder-text="Keine Daten verarbeitet."
|
|
>
|
|
<template #head>
|
|
<thead>
|
|
<tr>
|
|
<th v-for="h in requiredFields" :key="h.key">{{ h.label }}</th>
|
|
<th>ONT SN</th>
|
|
</tr>
|
|
</thead>
|
|
</template>
|
|
<template #row="{ item }">
|
|
<td>{{ item[selectedColumns.kundennummer] }}</td>
|
|
<td>{{ item[selectedColumns.anschlussstrasse] }}</td>
|
|
<td>{{ item[selectedColumns.anschlussplz] }}</td>
|
|
<td>{{ item[selectedColumns.anschlusscity] }}</td>
|
|
<td class="mono">{{ item.ont_sn }}</td>
|
|
</template>
|
|
</tt-data-table>
|
|
<div v-if="!loading && processedData.length" class="results-summary">
|
|
{{ processedData.length }} Zeilen verarbeitet
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`,
|
|
data: () => ({
|
|
step: 1,
|
|
headers: [],
|
|
parsedData: [],
|
|
processedData: [],
|
|
selectedColumns: {
|
|
kundennummer: 'crmPartner',
|
|
anschlussstrasse: 'AnlStrasse',
|
|
anschlussplz: 'AnlPlz',
|
|
anschlusscity: 'AnlOrt'
|
|
},
|
|
requiredFields: [
|
|
{ key: 'kundennummer', label: 'Kundennummer' },
|
|
{ key: 'anschlussstrasse', label: 'Anschlussstraße' },
|
|
{ key: 'anschlussplz', label: 'Anschluss PLZ' },
|
|
{ key: 'anschlusscity', label: 'Anschluss City' }
|
|
],
|
|
loading: false,
|
|
progress: 0,
|
|
currentRow: 0,
|
|
totalRows: 0,
|
|
currentCustomerNumber: ''
|
|
}),
|
|
methods: {
|
|
async readXlsx(file) {
|
|
await window.TT_CORE.loadScript('https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.0/xlsx.full.min.js');
|
|
const fr = new FileReader();
|
|
fr.onload = (e) => {
|
|
const wb = XLSX.read(new Uint8Array(e.target.result), { type: 'array' });
|
|
this.parsedData = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
|
this.headers = Object.keys(this.parsedData[0] || {});
|
|
this.step = 2;
|
|
};
|
|
fr.readAsArrayBuffer(file);
|
|
},
|
|
async startProcessing() {
|
|
this.step = 3;
|
|
this.loading = true;
|
|
this.totalRows = this.parsedData.length;
|
|
this.processedData = [];
|
|
this.currentRow = 0;
|
|
const p = [];
|
|
const b = window.TT_CONFIG.BASE_PATH;
|
|
loop: for (let i = 0; i < this.parsedData.length; i++) {
|
|
this.currentRow = i;
|
|
this.progress = ((i + 1) / this.totalRows) * 100;
|
|
const row = { ...this.parsedData[i] };
|
|
this.currentCustomerNumber = row[this.selectedColumns.kundennummer] || '';
|
|
try {
|
|
const { data: users } = await axios.get(`${b}/Radius/proxyUnsecureHTTPRequestToRadius`, {
|
|
params: { custnume: row[this.selectedColumns.kundennummer] }
|
|
});
|
|
if (users.length === 0) {
|
|
row.ont_sn = 'N/A - Kein Benutzer';
|
|
} else if (users.length === 1) {
|
|
const { data: d } = await axios.get(`${b}/Radius/proxyUnsecureHTTPRequestToRadius`, {
|
|
params: { skipAdditional: 'true', action2: 'fetchRadacct', username: users[0].username }
|
|
});
|
|
row.ont_sn = d.ont_sn || 'N/A - Keine ONT SN';
|
|
} else {
|
|
const [s, pl, c] = [row[this.selectedColumns.anschlussstrasse], row[this.selectedColumns.anschlussplz], row[this.selectedColumns.anschlusscity]];
|
|
for (let u of users) {
|
|
if (window.TT_CORE.validateData(s, pl, c, u.info || users[0].info || '')) {
|
|
const { data: d } = await axios.get(`${b}/Radius/proxyUnsecureHTTPRequestToRadius`, {
|
|
params: { skipAdditional: 'true', action2: 'fetchRadacct', username: u.username }
|
|
});
|
|
row.ont_sn = d.ont_sn || 'N/A - Keine ONT SN';
|
|
p.push(row);
|
|
continue loop;
|
|
}
|
|
}
|
|
row.ont_sn = 'N/A - Anschluss nicht zugeordnet';
|
|
}
|
|
} catch {
|
|
row.ont_sn = 'N/A - Fehler';
|
|
}
|
|
p.push(row);
|
|
if ((i + 1) % 20 === 0) await new Promise(r => setTimeout(r, 20));
|
|
}
|
|
this.loading = false;
|
|
this.processedData = p;
|
|
},
|
|
downloadResults() {
|
|
const ws = XLSX.utils.json_to_sheet(this.processedData);
|
|
const wb = XLSX.utils.book_new();
|
|
XLSX.utils.book_append_sheet(wb, ws, 'Results');
|
|
XLSX.writeFile(wb, 'results.xlsx');
|
|
},
|
|
resetLocal() {
|
|
Object.assign(this.$data, this.$options.data.call(this));
|
|
}
|
|
}
|
|
};
|
|
|
|
// Register component with Vue 3 app
|
|
if (window.VueApp) {
|
|
window.VueApp.component('radius-ont-parser', RadiusOntParser);
|
|
}
|