@tekkare/romulus 0.1.36 → 0.1.38
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.
- package/dist/module.json +1 -1
- package/dist/runtime/components/Button.vue +2 -2
- package/dist/runtime/components/Callout.vue +1 -1
- package/dist/runtime/components/ExportPanel.vue +744 -0
- package/dist/runtime/components/SavedViewCard.vue +2 -2
- package/dist/runtime/components/SavedViews.vue +9 -3
- package/dist/runtime/composables/useRmExport.d.ts +29 -0
- package/dist/runtime/composables/useRmExport.js +404 -0
- package/dist/runtime/utils/export.d.ts +199 -0
- package/dist/runtime/utils/export.js +98 -0
- package/package.json +9 -2
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vocabulaire de l'export OIP : ce qu'une page declare, ce que le wizard
|
|
3
|
+
* (`RmExportPanel`) en fait, ce que les generateurs (`useRmExport`) consomment.
|
|
4
|
+
*
|
|
5
|
+
* Un seul contrat pour toutes les apps, parce que l'export est la sortie de
|
|
6
|
+
* chaque ecran : sans vocabulaire commun, chaque page reecrit sa feuille de
|
|
7
|
+
* calcul, sa page de garde et son nom de fichier, et deux exports du meme
|
|
8
|
+
* produit ne se ressemblent plus.
|
|
9
|
+
*
|
|
10
|
+
* Le schema de colonne est celui de `write-excel-file` (`column`, `type`,
|
|
11
|
+
* `format`, `value(row)`) : c'est deja la forme utilisee par les apps, et la
|
|
12
|
+
* garder evite de traduire les schemas existants. Les autres formats (CSV,
|
|
13
|
+
* PDF) appellent le meme `value(row)`, donc une colonne se decrit une fois.
|
|
14
|
+
*/
|
|
15
|
+
/** Une colonne de tableau, telle que `write-excel-file` l'attend. */
|
|
16
|
+
export interface RmExportColumn {
|
|
17
|
+
column: string;
|
|
18
|
+
type?: unknown;
|
|
19
|
+
format?: string;
|
|
20
|
+
width?: number;
|
|
21
|
+
wrap?: boolean;
|
|
22
|
+
alignVertical?: string;
|
|
23
|
+
value: (row: any) => any;
|
|
24
|
+
}
|
|
25
|
+
/** Un onglet du classeur : son nom, ses colonnes, ses lignes. */
|
|
26
|
+
export interface RmExportSheet {
|
|
27
|
+
name: string;
|
|
28
|
+
schema: RmExportColumn[];
|
|
29
|
+
data: any[];
|
|
30
|
+
}
|
|
31
|
+
/** Chiffre cle repris sur la page de garde. */
|
|
32
|
+
export interface RmExportKpi {
|
|
33
|
+
label: string;
|
|
34
|
+
value: string | number;
|
|
35
|
+
}
|
|
36
|
+
/** Source citee, avec sa derniere mise a jour. */
|
|
37
|
+
export interface RmExportSource {
|
|
38
|
+
name: string;
|
|
39
|
+
date?: string | Date | null;
|
|
40
|
+
emoji?: string;
|
|
41
|
+
}
|
|
42
|
+
/** Filtre actif, tel qu'il s'ecrit dans le recapitulatif de l'export. */
|
|
43
|
+
export interface RmExportFilter {
|
|
44
|
+
label: string;
|
|
45
|
+
value: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Un format d'export.
|
|
49
|
+
*
|
|
50
|
+
* Les trois drapeaux disent au wizard quelles etapes ont un sens, plutot que
|
|
51
|
+
* de lui faire connaitre les formats par leur nom : un PNG n'a ni colonnes ni
|
|
52
|
+
* mise en page, un CSV n'a pas de graphique. Un format ajoute plus tard
|
|
53
|
+
* (Parquet, PowerPoint) herite du bon comportement en posant ses drapeaux,
|
|
54
|
+
* sans toucher au composant.
|
|
55
|
+
*/
|
|
56
|
+
export interface RmExportFormat {
|
|
57
|
+
key: string;
|
|
58
|
+
label: string;
|
|
59
|
+
/** Icone Phosphor, nom PascalCase sans le prefixe `Ph`. */
|
|
60
|
+
icon: string;
|
|
61
|
+
color?: string;
|
|
62
|
+
desc?: string;
|
|
63
|
+
/** Le format porte des lignes de tableau : plage et colonnes s'appliquent. */
|
|
64
|
+
tabular?: boolean;
|
|
65
|
+
/** Le format rend les graphiques. */
|
|
66
|
+
visual?: boolean;
|
|
67
|
+
/** Le format est mis en page : le choix A4 s'applique. */
|
|
68
|
+
paged?: boolean;
|
|
69
|
+
/** Format annonce mais pas encore livre : la carte reste visible, grisee. */
|
|
70
|
+
disabled?: boolean;
|
|
71
|
+
/** Raison affichee sur une carte grisee. */
|
|
72
|
+
disabledHint?: string;
|
|
73
|
+
}
|
|
74
|
+
/** Nature d'une section : elle decide de quels formats la section releve. */
|
|
75
|
+
export type RmExportSectionKind = 'table' | 'visual' | 'meta';
|
|
76
|
+
/** Section proposee a l'etape « Donnees ». */
|
|
77
|
+
export interface RmExportSection {
|
|
78
|
+
key: string;
|
|
79
|
+
label: string;
|
|
80
|
+
kind: RmExportSectionKind;
|
|
81
|
+
/** Cochee a l'ouverture. */
|
|
82
|
+
on?: boolean;
|
|
83
|
+
}
|
|
84
|
+
/** Plage de lignes : filtrees, toute la base, la selection. */
|
|
85
|
+
export interface RmExportRange {
|
|
86
|
+
key: string;
|
|
87
|
+
label: string;
|
|
88
|
+
rows: number;
|
|
89
|
+
}
|
|
90
|
+
/** Option de l'etape « Personnaliser » (logo, sources, ZIP...). */
|
|
91
|
+
export interface RmExportOption {
|
|
92
|
+
key: string;
|
|
93
|
+
label: string;
|
|
94
|
+
on?: boolean;
|
|
95
|
+
disabled?: boolean;
|
|
96
|
+
}
|
|
97
|
+
/** Mise en page, pour les formats pagines. */
|
|
98
|
+
export interface RmExportLayout {
|
|
99
|
+
key: string;
|
|
100
|
+
label: string;
|
|
101
|
+
}
|
|
102
|
+
/** Configuration complete, ce que le wizard emet. */
|
|
103
|
+
export interface RmExportConfig {
|
|
104
|
+
format: string;
|
|
105
|
+
range: string;
|
|
106
|
+
sections: string[];
|
|
107
|
+
columns: string[];
|
|
108
|
+
options: string[];
|
|
109
|
+
fileName: string;
|
|
110
|
+
layout: string;
|
|
111
|
+
schedule: 'once' | 'recurring';
|
|
112
|
+
frequency: string;
|
|
113
|
+
email: boolean;
|
|
114
|
+
}
|
|
115
|
+
/** Modele pre-rempli propose avant d'ouvrir le wizard. */
|
|
116
|
+
export interface RmExportTemplate {
|
|
117
|
+
key: string;
|
|
118
|
+
name: string;
|
|
119
|
+
meta: string;
|
|
120
|
+
icon: string;
|
|
121
|
+
color?: string;
|
|
122
|
+
config: Partial<RmExportConfig>;
|
|
123
|
+
}
|
|
124
|
+
/** Ligne de l'historique « Exports recents ». */
|
|
125
|
+
export interface RmExportRecent {
|
|
126
|
+
name: string;
|
|
127
|
+
format: string;
|
|
128
|
+
rows?: number;
|
|
129
|
+
size?: string;
|
|
130
|
+
/** Date de l'export, ISO ou Date. */
|
|
131
|
+
at: string | Date;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Ce que les generateurs consomment. La page fournit le contenu, le wizard
|
|
135
|
+
* fournit la configuration ; `useRmExport` croise les deux.
|
|
136
|
+
*/
|
|
137
|
+
export interface RmExportPayload {
|
|
138
|
+
/** Titre du document (page de garde PDF, en-tete du classeur). */
|
|
139
|
+
title: string;
|
|
140
|
+
subtitle?: string;
|
|
141
|
+
kpi?: RmExportKpi[];
|
|
142
|
+
sources?: RmExportSource[];
|
|
143
|
+
filters?: RmExportFilter[];
|
|
144
|
+
sheets: RmExportSheet[];
|
|
145
|
+
/**
|
|
146
|
+
* Graphiques a rasteriser ou vectoriser. Les charts Romulus sont rendus par
|
|
147
|
+
* ECharts en SVG : on recupere le noeud, pas une capture d'ecran.
|
|
148
|
+
*/
|
|
149
|
+
charts?: {
|
|
150
|
+
title?: string;
|
|
151
|
+
el: Element;
|
|
152
|
+
}[];
|
|
153
|
+
/** URL a copier pour le format « Permalink ». */
|
|
154
|
+
permalink?: string;
|
|
155
|
+
/**
|
|
156
|
+
* Logo a poser sur la page de garde PDF, en data URL PNG. Le paquet ne
|
|
157
|
+
* porte pas l'image : chaque app a la sienne dans ses assets, et un
|
|
158
|
+
* `import` depuis `node_modules` ne la trouverait pas.
|
|
159
|
+
*/
|
|
160
|
+
logoDataUrl?: string;
|
|
161
|
+
/** Plafond de lignes par onglet, pour ne pas geler l'onglet du navigateur. */
|
|
162
|
+
limit?: number;
|
|
163
|
+
}
|
|
164
|
+
/** Extension de fichier par format. */
|
|
165
|
+
export declare const RM_EXPORT_EXTENSIONS: Record<string, string>;
|
|
166
|
+
/**
|
|
167
|
+
* Catalogue par defaut. Une app peut en retirer, en ajouter, ou en desactiver
|
|
168
|
+
* un ; l'ordre est celui d'affichage.
|
|
169
|
+
*/
|
|
170
|
+
export declare const RM_EXPORT_FORMATS: RmExportFormat[];
|
|
171
|
+
/** Sections par defaut, dans l'ordre d'affichage. */
|
|
172
|
+
export declare const RM_EXPORT_SECTIONS: RmExportSection[];
|
|
173
|
+
/** Options par defaut de l'etape « Personnaliser ». */
|
|
174
|
+
export declare const RM_EXPORT_OPTIONS: RmExportOption[];
|
|
175
|
+
/** Mises en page par defaut. */
|
|
176
|
+
export declare const RM_EXPORT_LAYOUTS: RmExportLayout[];
|
|
177
|
+
/**
|
|
178
|
+
* Taille estimee, en ko. Une estimation, pas une mesure : elle sert a dire
|
|
179
|
+
* « quelques centaines de ko » contre « plusieurs Mo » avant de lancer.
|
|
180
|
+
*/
|
|
181
|
+
export declare function rmExportSizeKb(input: {
|
|
182
|
+
format: string;
|
|
183
|
+
rows: number;
|
|
184
|
+
columns: number;
|
|
185
|
+
totalColumns: number;
|
|
186
|
+
sections: number;
|
|
187
|
+
includesTable: boolean;
|
|
188
|
+
zip?: boolean;
|
|
189
|
+
}): number;
|
|
190
|
+
/** « ~ 340 ko » · « ~ 1,8 Mo ». */
|
|
191
|
+
export declare function rmExportSize(kb: number): string;
|
|
192
|
+
/** Duree estimee, du meme ordre de precision que la taille. */
|
|
193
|
+
export declare function rmExportDuration(kb: number, rows: number): string;
|
|
194
|
+
/**
|
|
195
|
+
* Nom de fichier sans extension : minuscules, sans accent, tirets, date du
|
|
196
|
+
* jour. Un nom stable d'une app a l'autre rend les exports triables dans un
|
|
197
|
+
* dossier de telechargements.
|
|
198
|
+
*/
|
|
199
|
+
export declare function rmExportFileName(...parts: (string | undefined | null)[]): string;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export const RM_EXPORT_EXTENSIONS = {
|
|
2
|
+
pdf: "pdf",
|
|
3
|
+
xlsx: "xlsx",
|
|
4
|
+
csv: "csv",
|
|
5
|
+
png: "png",
|
|
6
|
+
svg: "svg",
|
|
7
|
+
link: "url"
|
|
8
|
+
};
|
|
9
|
+
export const RM_EXPORT_FORMATS = [
|
|
10
|
+
{
|
|
11
|
+
key: "pdf",
|
|
12
|
+
label: "PDF",
|
|
13
|
+
icon: "FilePdf",
|
|
14
|
+
color: "var(--color-danger)",
|
|
15
|
+
desc: "Rapport mis en page, pr\xEAt \xE0 partager. KPI, graphiques, table, en-t\xEAte OIP Healthcare.",
|
|
16
|
+
tabular: true,
|
|
17
|
+
visual: true,
|
|
18
|
+
paged: true
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
key: "xlsx",
|
|
22
|
+
label: "Excel",
|
|
23
|
+
icon: "FileXls",
|
|
24
|
+
color: "var(--color-success)",
|
|
25
|
+
desc: "Donn\xE9es brutes avec page de garde. Pour analyse en local ou consolidation.",
|
|
26
|
+
tabular: true,
|
|
27
|
+
paged: true
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
key: "csv",
|
|
31
|
+
label: "CSV",
|
|
32
|
+
icon: "FileCsv",
|
|
33
|
+
color: "var(--text-secondary)",
|
|
34
|
+
desc: "Donn\xE9es tabulaires. Pour scripts Python ou R, BI, int\xE9gration API.",
|
|
35
|
+
tabular: true
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
key: "png",
|
|
39
|
+
label: "PNG",
|
|
40
|
+
icon: "Image",
|
|
41
|
+
color: "var(--color-info)",
|
|
42
|
+
desc: "Image des graphiques visibles. Pour slides, captures, presse.",
|
|
43
|
+
visual: true
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
key: "svg",
|
|
47
|
+
label: "SVG",
|
|
48
|
+
icon: "VectorThree",
|
|
49
|
+
color: "var(--color-warning)",
|
|
50
|
+
desc: "Vectoriel \xE9ditable. Pour Figma ou Illustrator, impression.",
|
|
51
|
+
visual: true
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
key: "link",
|
|
55
|
+
label: "Permalink",
|
|
56
|
+
icon: "Link",
|
|
57
|
+
color: "var(--color-secondary)",
|
|
58
|
+
desc: "Lien vers cette vue, filtres compris. Lecture seule."
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
export const RM_EXPORT_SECTIONS = [
|
|
62
|
+
{ key: "kpi", label: "Chiffres cl\xE9s", kind: "meta", on: true },
|
|
63
|
+
{ key: "charts", label: "Graphiques", kind: "visual", on: true },
|
|
64
|
+
{ key: "table", label: "Tableau", kind: "table", on: true },
|
|
65
|
+
{ key: "filters", label: "Filtres actifs", kind: "meta", on: true },
|
|
66
|
+
{ key: "sources", label: "Sources et mises \xE0 jour", kind: "meta", on: true }
|
|
67
|
+
];
|
|
68
|
+
export const RM_EXPORT_OPTIONS = [
|
|
69
|
+
{ key: "logo", label: "Logo Tekkare et en-t\xEAte OIP Healthcare", on: true },
|
|
70
|
+
{ key: "sources", label: "Liste des sources cit\xE9es", on: true }
|
|
71
|
+
];
|
|
72
|
+
export const RM_EXPORT_LAYOUTS = [
|
|
73
|
+
{ key: "a4-portrait", label: "A4 portrait" },
|
|
74
|
+
{ key: "a4-landscape", label: "A4 paysage" },
|
|
75
|
+
{ key: "raw", label: "Donn\xE9es brutes" }
|
|
76
|
+
];
|
|
77
|
+
const BASE_KB = { pdf: 280, xlsx: 60, csv: 4, png: 420, svg: 90, link: 0 };
|
|
78
|
+
const BYTES_PER_ROW = { pdf: 3200, xlsx: 1200, csv: 220, png: 0, svg: 0, link: 0 };
|
|
79
|
+
export function rmExportSizeKb(input) {
|
|
80
|
+
const base = BASE_KB[input.format] ?? 0;
|
|
81
|
+
const perRow = BYTES_PER_ROW[input.format] ?? 0;
|
|
82
|
+
const colFactor = input.includesTable ? Math.max(0.3, input.columns / Math.max(1, input.totalColumns)) : 0;
|
|
83
|
+
const tableKb = input.rows * perRow * colFactor / 1024;
|
|
84
|
+
const kb = base + tableKb + input.sections * 18;
|
|
85
|
+
return input.zip ? kb * 0.55 : kb;
|
|
86
|
+
}
|
|
87
|
+
export function rmExportSize(kb) {
|
|
88
|
+
if (kb < 1024) return `~ ${Math.round(kb)} ko`;
|
|
89
|
+
return `~ ${(kb / 1024).toFixed(1).replace(".", ",")} Mo`;
|
|
90
|
+
}
|
|
91
|
+
export function rmExportDuration(kb, rows) {
|
|
92
|
+
return `~ ${Math.max(2, Math.round(kb / 200 + rows / 500))} s`;
|
|
93
|
+
}
|
|
94
|
+
export function rmExportFileName(...parts) {
|
|
95
|
+
const slug = parts.filter(Boolean).join("-").normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
96
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
97
|
+
return `${slug || "export"}-${today}`;
|
|
98
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekkare/romulus",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"description": "Romulus Design System - Nuxt module by Tekkare",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
"types": "./dist/types.d.ts",
|
|
13
13
|
"import": "./dist/module.mjs",
|
|
14
14
|
"require": "./dist/module.cjs"
|
|
15
|
+
},
|
|
16
|
+
"./utils/*": {
|
|
17
|
+
"types": "./dist/runtime/utils/*.d.ts",
|
|
18
|
+
"import": "./dist/runtime/utils/*.js"
|
|
15
19
|
}
|
|
16
20
|
},
|
|
17
21
|
"main": "./dist/module.cjs",
|
|
@@ -28,7 +32,10 @@
|
|
|
28
32
|
"dependencies": {
|
|
29
33
|
"@nuxt/kit": "^3.16.0",
|
|
30
34
|
"@phosphor-icons/vue": "^2.2.1",
|
|
31
|
-
"echarts": "^5.6.0"
|
|
35
|
+
"echarts": "^5.6.0",
|
|
36
|
+
"jspdf": "^4.2.0",
|
|
37
|
+
"jspdf-autotable": "^5.0.7",
|
|
38
|
+
"write-excel-file": "^1.4.30"
|
|
32
39
|
},
|
|
33
40
|
"devDependencies": {
|
|
34
41
|
"@nuxt/module-builder": "^0.8.4",
|