@5minds/node-red-dashboard-2-processcube-dynamic-form 2.6.1 → 2.7.1-develop-75db51-mh564jh8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -80,20 +80,28 @@
|
|
|
80
80
|
</p>
|
|
81
81
|
</div>
|
|
82
82
|
<div v-else-if="createComponent(field).isFileField" class="ui-dynamic-form-file-wrapper">
|
|
83
|
-
<div v-if="getFilePreviewsForField(field.id).length > 0" class="ui-dynamic-form-
|
|
83
|
+
<div v-if="getFilePreviewsForField(field.id).length > 0" class="ui-dynamic-form-file-previews">
|
|
84
84
|
<h4>Current files:</h4>
|
|
85
85
|
<div class="ui-dynamic-form-preview-grid">
|
|
86
86
|
<div
|
|
87
87
|
v-for="preview in getFilePreviewsForField(field.id)"
|
|
88
88
|
:key="preview.name"
|
|
89
89
|
class="ui-dynamic-form-preview-item"
|
|
90
|
+
@click="openFileModal(preview)"
|
|
90
91
|
>
|
|
92
|
+
<!-- Image preview -->
|
|
91
93
|
<img
|
|
94
|
+
v-if="preview.isImage"
|
|
92
95
|
:src="preview.url"
|
|
93
96
|
:alt="preview.name"
|
|
94
97
|
class="ui-dynamic-form-preview-image"
|
|
95
|
-
@click="openImageModal(preview)"
|
|
96
98
|
/>
|
|
99
|
+
<!-- File icon for non-images -->
|
|
100
|
+
<div v-else class="ui-dynamic-form-file-icon">
|
|
101
|
+
<v-icon size="48" :color="getFileIconColor(preview.type)">
|
|
102
|
+
{{ getFileIcon(preview.type) }}
|
|
103
|
+
</v-icon>
|
|
104
|
+
</div>
|
|
97
105
|
<div class="ui-dynamic-form-preview-info">
|
|
98
106
|
<span class="ui-dynamic-form-preview-name">{{ preview.name }}</span>
|
|
99
107
|
<span class="ui-dynamic-form-preview-size">{{ formatFileSize(preview.size) }}</span>
|
|
@@ -169,26 +177,37 @@
|
|
|
169
177
|
<UIDynamicFormFooterAction :actions="actions" :actionCallback="actionFn" />
|
|
170
178
|
</div>
|
|
171
179
|
|
|
172
|
-
<v-dialog v-model="
|
|
173
|
-
<v-card v-if="
|
|
174
|
-
<v-card-title class="headline">{{
|
|
180
|
+
<v-dialog v-model="fileModalOpen" max-width="800px">
|
|
181
|
+
<v-card v-if="modalFile">
|
|
182
|
+
<v-card-title class="headline">{{ modalFile.name }}</v-card-title>
|
|
175
183
|
<v-card-text>
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
184
|
+
<!-- Image preview -->
|
|
185
|
+
<div v-if="modalFile.isImage">
|
|
186
|
+
<img
|
|
187
|
+
:src="modalFile.url"
|
|
188
|
+
:alt="modalFile.name"
|
|
189
|
+
style="width: 100%; max-height: 70vh; object-fit: contain"
|
|
190
|
+
/>
|
|
191
|
+
</div>
|
|
192
|
+
<!-- File icon for non-images -->
|
|
193
|
+
<div v-else class="ui-dynamic-form-modal-file-icon">
|
|
194
|
+
<v-icon size="120" :color="getFileIconColor(modalFile.type)">
|
|
195
|
+
{{ getFileIcon(modalFile.type) }}
|
|
196
|
+
</v-icon>
|
|
197
|
+
<p class="text-h6 mt-4">{{ getFileTypeDescription(modalFile.type) }}</p>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
181
200
|
<div style="margin-top: 16px">
|
|
182
|
-
<strong>Size:</strong> {{ formatFileSize(
|
|
183
|
-
<strong>Type:</strong> {{
|
|
201
|
+
<strong>Size:</strong> {{ formatFileSize(modalFile.size) }}<br />
|
|
202
|
+
<strong>Type:</strong> {{ modalFile.type }}
|
|
184
203
|
</div>
|
|
185
204
|
</v-card-text>
|
|
186
205
|
<v-card-actions>
|
|
187
|
-
<
|
|
188
|
-
|
|
189
|
-
</
|
|
206
|
+
<v-btn variant="tonal" rounded="lg" @click="downloadFile(modalFile)">
|
|
207
|
+
Herunterladen
|
|
208
|
+
</v-btn>
|
|
190
209
|
<v-spacer></v-spacer>
|
|
191
|
-
<v-btn variant="flat" rounded="lg" @click="
|
|
210
|
+
<v-btn variant="flat" rounded="lg" @click="closeFileModal">Schließen</v-btn>
|
|
192
211
|
</v-card-actions>
|
|
193
212
|
</v-card>
|
|
194
213
|
</v-dialog>
|
|
@@ -350,8 +369,8 @@ export default {
|
|
|
350
369
|
msg: null,
|
|
351
370
|
collapsed: false,
|
|
352
371
|
firstFormFieldRef: null,
|
|
353
|
-
|
|
354
|
-
|
|
372
|
+
fileModalOpen: false,
|
|
373
|
+
modalFile: null,
|
|
355
374
|
objectUrlsByField: {},
|
|
356
375
|
previewCache: {},
|
|
357
376
|
};
|
|
@@ -1450,6 +1469,11 @@ export default {
|
|
|
1450
1469
|
|
|
1451
1470
|
return `data:${fileData.type};base64,${fileData.data}`;
|
|
1452
1471
|
},
|
|
1472
|
+
generateFileDownloadUrl(fileData) {
|
|
1473
|
+
if (!fileData || !fileData.data || !fileData.type) return null;
|
|
1474
|
+
|
|
1475
|
+
return `data:${fileData.type};base64,${fileData.data}`;
|
|
1476
|
+
},
|
|
1453
1477
|
getFilePreviewsForField(fieldId) {
|
|
1454
1478
|
const currentData = this.formData[fieldId] || null;
|
|
1455
1479
|
const cachedOriginalData = this.originalFileData[fieldId] || null;
|
|
@@ -1480,12 +1504,13 @@ export default {
|
|
|
1480
1504
|
} else if (originalFileData) {
|
|
1481
1505
|
const fileArray = Array.isArray(originalFileData) ? originalFileData : [originalFileData];
|
|
1482
1506
|
previews = fileArray
|
|
1483
|
-
.filter((file) =>
|
|
1507
|
+
.filter((file) => file && file.name) // Accept all files, not just images
|
|
1484
1508
|
.map((file) => ({
|
|
1485
1509
|
name: file.name,
|
|
1486
|
-
url: this.generateImagePreviewUrl(file),
|
|
1510
|
+
url: this.isImageFile(file) ? this.generateImagePreviewUrl(file) : this.generateFileDownloadUrl(file),
|
|
1487
1511
|
size: file.size,
|
|
1488
1512
|
type: file.type,
|
|
1513
|
+
isImage: this.isImageFile(file),
|
|
1489
1514
|
}));
|
|
1490
1515
|
}
|
|
1491
1516
|
|
|
@@ -1513,7 +1538,7 @@ export default {
|
|
|
1513
1538
|
actualFile = fileItem[0];
|
|
1514
1539
|
}
|
|
1515
1540
|
|
|
1516
|
-
return actualFile
|
|
1541
|
+
return actualFile; // Accept all files, not just images
|
|
1517
1542
|
})
|
|
1518
1543
|
.map((fileItem) => {
|
|
1519
1544
|
let actualFile = null;
|
|
@@ -1540,6 +1565,7 @@ export default {
|
|
|
1540
1565
|
url: url,
|
|
1541
1566
|
size: actualFile.size,
|
|
1542
1567
|
type: actualFile.type,
|
|
1568
|
+
isImage: actualFile.type && actualFile.type.startsWith('image/'),
|
|
1543
1569
|
isObjectUrl: true,
|
|
1544
1570
|
};
|
|
1545
1571
|
})
|
|
@@ -1589,6 +1615,172 @@ export default {
|
|
|
1589
1615
|
this.previewCache = {};
|
|
1590
1616
|
}
|
|
1591
1617
|
},
|
|
1618
|
+
getFileIcon(mimeType) {
|
|
1619
|
+
if (!mimeType) return 'mdi-file';
|
|
1620
|
+
|
|
1621
|
+
if (mimeType.startsWith('image/')) return 'mdi-image';
|
|
1622
|
+
if (mimeType === 'application/pdf') return 'mdi-file-pdf-box';
|
|
1623
|
+
if (mimeType.includes('word') || mimeType.includes('msword') ||
|
|
1624
|
+
mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
|
|
1625
|
+
return 'mdi-file-word';
|
|
1626
|
+
}
|
|
1627
|
+
if (mimeType.includes('excel') || mimeType.includes('spreadsheet') ||
|
|
1628
|
+
mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
|
|
1629
|
+
return 'mdi-file-excel';
|
|
1630
|
+
}
|
|
1631
|
+
if (mimeType.includes('powerpoint') || mimeType.includes('presentation') ||
|
|
1632
|
+
mimeType === 'application/vnd.openxmlformats-officedocument.presentationml.presentation') {
|
|
1633
|
+
return 'mdi-file-powerpoint';
|
|
1634
|
+
}
|
|
1635
|
+
if (mimeType === 'text/plain') return 'mdi-file-document';
|
|
1636
|
+
if (mimeType.includes('zip') || mimeType.includes('rar') || mimeType.includes('7z')) {
|
|
1637
|
+
return 'mdi-folder-zip';
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
return 'mdi-file';
|
|
1641
|
+
},
|
|
1642
|
+
getFileIconColor(mimeType) {
|
|
1643
|
+
if (!mimeType) return 'grey';
|
|
1644
|
+
|
|
1645
|
+
if (mimeType.startsWith('image/')) return 'green';
|
|
1646
|
+
if (mimeType === 'application/pdf') return 'red';
|
|
1647
|
+
if (mimeType.includes('word') || mimeType.includes('msword') ||
|
|
1648
|
+
mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
|
|
1649
|
+
return 'blue';
|
|
1650
|
+
}
|
|
1651
|
+
if (mimeType.includes('excel') || mimeType.includes('spreadsheet') ||
|
|
1652
|
+
mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
|
|
1653
|
+
return 'green';
|
|
1654
|
+
}
|
|
1655
|
+
if (mimeType.includes('powerpoint') || mimeType.includes('presentation') ||
|
|
1656
|
+
mimeType === 'application/vnd.openxmlformats-officedocument.presentationml.presentation') {
|
|
1657
|
+
return 'orange';
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
return 'grey';
|
|
1661
|
+
},
|
|
1662
|
+
getFileTypeDescription(mimeType) {
|
|
1663
|
+
if (!mimeType) return 'Unbekannter Dateityp';
|
|
1664
|
+
|
|
1665
|
+
if (mimeType === 'application/pdf') return 'PDF-Dokument';
|
|
1666
|
+
if (mimeType.includes('word') || mimeType.includes('msword') ||
|
|
1667
|
+
mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
|
|
1668
|
+
return 'Word-Dokument';
|
|
1669
|
+
}
|
|
1670
|
+
if (mimeType.includes('excel') || mimeType.includes('spreadsheet') ||
|
|
1671
|
+
mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
|
|
1672
|
+
return 'Excel-Arbeitsmappe';
|
|
1673
|
+
}
|
|
1674
|
+
if (mimeType.includes('powerpoint') || mimeType.includes('presentation') ||
|
|
1675
|
+
mimeType === 'application/vnd.openxmlformats-officedocument.presentationml.presentation') {
|
|
1676
|
+
return 'PowerPoint-Präsentation';
|
|
1677
|
+
}
|
|
1678
|
+
if (mimeType === 'text/plain') return 'Textdatei';
|
|
1679
|
+
if (mimeType.startsWith('image/')) return 'Bilddatei';
|
|
1680
|
+
|
|
1681
|
+
return 'Datei';
|
|
1682
|
+
},
|
|
1683
|
+
openFileModal(file) {
|
|
1684
|
+
this.modalFile = file;
|
|
1685
|
+
this.fileModalOpen = true;
|
|
1686
|
+
},
|
|
1687
|
+
closeFileModal() {
|
|
1688
|
+
this.fileModalOpen = false;
|
|
1689
|
+
this.modalFile = null;
|
|
1690
|
+
},
|
|
1691
|
+
downloadFile(file) {
|
|
1692
|
+
if (!file || !file.url || !file.name) return;
|
|
1693
|
+
|
|
1694
|
+
// For Object URLs (newly uploaded files), we need to use a different approach
|
|
1695
|
+
if (file.isObjectUrl) {
|
|
1696
|
+
// Find the original file from formData
|
|
1697
|
+
const fieldId = this.findFieldIdForFile(file);
|
|
1698
|
+
if (fieldId) {
|
|
1699
|
+
const fieldData = this.formData[fieldId];
|
|
1700
|
+
const originalFile = this.findOriginalFile(fieldData, file.name);
|
|
1701
|
+
|
|
1702
|
+
if (originalFile) {
|
|
1703
|
+
const link = document.createElement('a');
|
|
1704
|
+
link.href = URL.createObjectURL(originalFile);
|
|
1705
|
+
link.download = file.name;
|
|
1706
|
+
document.body.appendChild(link);
|
|
1707
|
+
link.click();
|
|
1708
|
+
document.body.removeChild(link);
|
|
1709
|
+
URL.revokeObjectURL(link.href);
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// For data URLs (base64 encoded files from server)
|
|
1716
|
+
if (file.url.startsWith('data:')) {
|
|
1717
|
+
const link = document.createElement('a');
|
|
1718
|
+
link.href = file.url;
|
|
1719
|
+
link.download = file.name;
|
|
1720
|
+
document.body.appendChild(link);
|
|
1721
|
+
link.click();
|
|
1722
|
+
document.body.removeChild(link);
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
// Fallback for regular URLs
|
|
1727
|
+
const link = document.createElement('a');
|
|
1728
|
+
link.href = file.url;
|
|
1729
|
+
link.download = file.name;
|
|
1730
|
+
link.target = '_blank';
|
|
1731
|
+
document.body.appendChild(link);
|
|
1732
|
+
link.click();
|
|
1733
|
+
document.body.removeChild(link);
|
|
1734
|
+
},
|
|
1735
|
+
findFieldIdForFile(file) {
|
|
1736
|
+
// Search through all file fields to find which one contains this file
|
|
1737
|
+
if (!this.userTask || !this.userTask.userTaskConfig || !this.userTask.userTaskConfig.formFields) {
|
|
1738
|
+
return null;
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
const fileFields = this.userTask.userTaskConfig.formFields.filter(field => field.type === 'file');
|
|
1742
|
+
|
|
1743
|
+
for (const field of fileFields) {
|
|
1744
|
+
const previews = this.getFilePreviewsForField(field.id);
|
|
1745
|
+
if (previews.some(preview => preview.name === file.name && preview.url === file.url)) {
|
|
1746
|
+
return field.id;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
return null;
|
|
1750
|
+
},
|
|
1751
|
+
findOriginalFile(fieldData, fileName) {
|
|
1752
|
+
if (!fieldData) return null;
|
|
1753
|
+
|
|
1754
|
+
if (Array.isArray(fieldData)) {
|
|
1755
|
+
for (const item of fieldData) {
|
|
1756
|
+
const file = this.extractFileFromItem(item);
|
|
1757
|
+
if (file && file.name === fileName) {
|
|
1758
|
+
return file;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
} else {
|
|
1762
|
+
const file = this.extractFileFromItem(fieldData);
|
|
1763
|
+
if (file && file.name === fileName) {
|
|
1764
|
+
return file;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
return null;
|
|
1768
|
+
},
|
|
1769
|
+
extractFileFromItem(item) {
|
|
1770
|
+
if (item instanceof File) {
|
|
1771
|
+
return item;
|
|
1772
|
+
}
|
|
1773
|
+
if (item && item.file instanceof File) {
|
|
1774
|
+
return item.file;
|
|
1775
|
+
}
|
|
1776
|
+
if (item instanceof FileList && item.length > 0) {
|
|
1777
|
+
return item[0];
|
|
1778
|
+
}
|
|
1779
|
+
if (item && item.file instanceof FileList && item.file.length > 0) {
|
|
1780
|
+
return item.file[0];
|
|
1781
|
+
}
|
|
1782
|
+
return null;
|
|
1783
|
+
},
|
|
1592
1784
|
},
|
|
1593
1785
|
};
|
|
1594
1786
|
|
|
@@ -701,3 +701,56 @@ code {
|
|
|
701
701
|
.ui-dynamic-form-dark .ui-dynamic-form-preview-size {
|
|
702
702
|
color: #ccc;
|
|
703
703
|
}
|
|
704
|
+
|
|
705
|
+
/* File icon styles for non-image files */
|
|
706
|
+
.ui-dynamic-form-file-icon {
|
|
707
|
+
display: flex;
|
|
708
|
+
align-items: center;
|
|
709
|
+
justify-content: center;
|
|
710
|
+
width: 100px;
|
|
711
|
+
height: 100px;
|
|
712
|
+
background-color: #f8f9fa;
|
|
713
|
+
border: 2px dashed #dee2e6;
|
|
714
|
+
border-radius: 8px;
|
|
715
|
+
margin-bottom: 8px;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
.ui-dynamic-form-dark .ui-dynamic-form-file-icon {
|
|
719
|
+
background-color: #2d2d2d;
|
|
720
|
+
border-color: #555;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/* Modal file icon styles */
|
|
724
|
+
.ui-dynamic-form-modal-file-icon {
|
|
725
|
+
display: flex;
|
|
726
|
+
flex-direction: column;
|
|
727
|
+
align-items: center;
|
|
728
|
+
justify-content: center;
|
|
729
|
+
padding: 40px;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/* Update class name for general file previews */
|
|
733
|
+
.ui-dynamic-form-file-previews {
|
|
734
|
+
margin-bottom: 16px;
|
|
735
|
+
padding: 16px;
|
|
736
|
+
background-color: #f8f9fa;
|
|
737
|
+
border-radius: 8px;
|
|
738
|
+
border: 1px solid #dee2e6;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.ui-dynamic-form-file-previews h4 {
|
|
742
|
+
margin: 0 0 12px 0;
|
|
743
|
+
color: #495057;
|
|
744
|
+
font-size: 14px;
|
|
745
|
+
font-weight: 600;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/* Dark theme adjustments for file previews */
|
|
749
|
+
.ui-dynamic-form-dark .ui-dynamic-form-file-previews {
|
|
750
|
+
background-color: #2d2d2d;
|
|
751
|
+
border-color: #555;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.ui-dynamic-form-dark .ui-dynamic-form-file-previews h4 {
|
|
755
|
+
color: #fff;
|
|
756
|
+
}
|