@mixd-id/web-scaffold 0.2.240701 → 0.2.240703
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/docs/schema/user-action.json +266 -0
- package/package.json +6 -2
- package/public/assets/dashboard/bar.png +0 -0
- package/public/assets/dashboard/doughnut.png +0 -0
- package/public/assets/dashboard/metric.png +0 -0
- package/public/assets/dashboard/pie.png +0 -0
- package/public/assets/dashboard/polar-area.png +0 -0
- package/public/assets/dashboard/virtual-table.png +0 -0
- package/public/static/dashboard/bar.png +0 -0
- package/public/static/dashboard/doughnut.png +0 -0
- package/public/static/dashboard/metric.png +0 -0
- package/public/static/dashboard/pie.png +0 -0
- package/public/static/dashboard/polar-area.png +0 -0
- package/public/static/dashboard/virtual-table.png +0 -0
- package/src/components/Button.vue +179 -160
- package/src/components/Checkbox.vue +0 -1
- package/src/components/Datepicker.vue +8 -6
- package/src/components/GHeatMaps.vue +317 -0
- package/src/components/GmapsDirection.vue +191 -0
- package/src/components/Grid.vue +2 -0
- package/src/components/HTMLEditor.vue +2 -2
- package/src/components/List.vue +384 -308
- package/src/components/Modal.vue +2 -3
- package/src/components/PresetSelectorFilterItem.vue +15 -2
- package/src/components/Switch.vue +3 -0
- package/src/components/Tabs.vue +1 -1
- package/src/components/TextWithTag.vue +67 -25
- package/src/components/Textbox.vue +5 -0
- package/src/components/VirtualGrid.vue +224 -228
- package/src/components/VirtualTable.vue +46 -28
- package/src/configs/dashboard/bar.js +10 -0
- package/src/configs/dashboard/collection-1.js +5 -0
- package/src/configs/dashboard/doughnut.js +7 -0
- package/src/configs/dashboard/gheatmaps.js +9 -0
- package/src/configs/dashboard/grid-2.js +34 -0
- package/src/configs/dashboard/grid-3.js +34 -0
- package/src/configs/dashboard/grid-4.js +34 -0
- package/src/configs/dashboard/grid.js +15 -0
- package/src/configs/dashboard/metric.js +10 -0
- package/src/configs/dashboard/pie.js +7 -0
- package/src/configs/dashboard/polar-area.js +7 -0
- package/src/configs/dashboard/virtual-table.js +9 -0
- package/src/defs/dashboard-preset.js +22 -0
- package/src/index.js +35 -23
- package/src/mixin/ready-state.js +37 -0
- package/src/stores/datasource.js +11 -0
- package/src/themes/default/index.js +1 -1
- package/src/utils/dashboard.js +1080 -0
- package/src/utils/event-bus.js +8 -0
- package/src/utils/helpers.js +56 -10
- package/src/utils/helpers.mjs +35 -1
- package/src/utils/preset-selector.js +5 -2
- package/src/utils/preset-selector.mjs +23 -13
- package/src/widgets/Dashboard/BarChart.vue +330 -0
- package/src/widgets/Dashboard/BarChartSetting.vue +317 -0
- package/src/widgets/Dashboard/DatasourceFilterSharing.vue +93 -0
- package/src/widgets/Dashboard/DatasourcePreview.vue +93 -0
- package/src/widgets/Dashboard/DatasourceSelector.vue +122 -0
- package/src/widgets/Dashboard/Doughnut.vue +157 -0
- package/src/widgets/Dashboard/DoughnutSetting.vue +196 -0
- package/src/widgets/Dashboard/GHeatMapsSetting.vue +108 -0
- package/src/widgets/Dashboard/InteractionEdit.vue +228 -0
- package/src/widgets/Dashboard/Metric.vue +76 -0
- package/src/widgets/Dashboard/MetricSetting.vue +174 -0
- package/src/widgets/Dashboard/Pie.vue +139 -0
- package/src/widgets/Dashboard/PieSetting.vue +247 -0
- package/src/widgets/Dashboard/PolarArea.vue +159 -0
- package/src/widgets/Dashboard/PolarAreaSetting.vue +195 -0
- package/src/widgets/Dashboard/SharingModal.vue +116 -0
- package/src/widgets/Dashboard/ViewSelector.vue +183 -0
- package/src/widgets/Dashboard/VirtualColumnEdit.vue +97 -0
- package/src/widgets/Dashboard/VirtualTableSetting.vue +234 -0
- package/src/widgets/Dashboard.vue +1773 -0
- package/src/widgets/PresetBar.vue +136 -175
- package/src/widgets/PresetBarPivot.vue +186 -0
- package/src/widgets/UserActionBuilder/UserActionCondition.vue +97 -0
- package/src/widgets/UserActionBuilder/UserActionConsole.vue +77 -0
- package/src/widgets/UserActionBuilder/UserActionItem.vue +163 -58
- package/src/widgets/UserActionBuilder/UserActionOutput.vue +35 -9
- package/src/widgets/UserActionBuilder/UserActionOutputDelay.vue +27 -0
- package/src/widgets/UserActionBuilder/UserActionOutputLog.vue +28 -0
- package/src/widgets/UserActionBuilder/UserActionOutputReply.vue +135 -0
- package/src/widgets/UserActionBuilder/UserActionProps.vue +211 -0
- package/src/widgets/UserActionBuilder.vue +68 -199
- package/src/widgets/WebPageBuilder4/GridSetting.vue +123 -73
- package/src/widgets/WebPageBuilder4/HeightSetting.vue +14 -11
- package/src/widgets/WebPageBuilder4/MarginSetting.vue +4 -1
- package/src/widgets/WebPageBuilder4/MultiValueSetting.vue +12 -4
- package/src/widgets/WebPageBuilder4/PaddingSetting.vue +4 -0
- package/src/widgets/WebPageBuilder4/TreeView.vue +6 -3
- package/src/widgets/WebPageBuilder4/TreeViewItem.vue +32 -58
- package/tailwind.config.js +2 -2
package/src/utils/helpers.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {Duplex} from "stream";
|
|
2
|
-
|
|
3
1
|
const md5 = require("md5");
|
|
4
2
|
const fs = require("fs");
|
|
5
3
|
const { Op } = require('sequelize')
|
|
6
4
|
const axios = require('axios')
|
|
7
5
|
const dayjs = require('dayjs')
|
|
6
|
+
const { camelCase } = require('lodash')
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
const ceil = (num, precision = 0) => {
|
|
@@ -142,13 +141,14 @@ const strSlug = (title, separator) => {
|
|
|
142
141
|
return title.replace(new RegExp('^[' + separator + '\\s]+|[' + separator + '\\s]+$', 'g'),'');
|
|
143
142
|
}
|
|
144
143
|
|
|
145
|
-
const strVars = (text, vars, opt = { emptyUndefined:true }) => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
144
|
+
const strVars = (text, vars = {}, opt = { emptyUndefined:true }) => {
|
|
145
|
+
if(text){
|
|
146
|
+
(text.match(/\{.*?(?=\})\}/gi) ?? []).forEach((match) => {
|
|
147
|
+
const key = match.substring(1, match.length - 1)
|
|
148
|
+
const value = vars[key] ?? (opt?.emptyUndefined ? '' : `{${key}}`)
|
|
149
|
+
text = text.replace(match, value)
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
152
|
|
|
153
153
|
return text
|
|
154
154
|
}
|
|
@@ -262,7 +262,7 @@ const unflatten = (flatObject) => {
|
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
const accessNestedObject = function(obj, path) {
|
|
265
|
-
if(!obj || typeof path !== 'string') return
|
|
265
|
+
if(!obj || typeof path !== 'string') return
|
|
266
266
|
|
|
267
267
|
const keys = path.split('.');
|
|
268
268
|
let nestedObj = obj;
|
|
@@ -279,6 +279,43 @@ const accessNestedObject = function(obj, path) {
|
|
|
279
279
|
return nestedObj;
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
+
const accessNestedSequelize = async(obj, path, opt = { defaultValue:'' }) => {
|
|
283
|
+
if(!obj || typeof path !== 'string') return
|
|
284
|
+
|
|
285
|
+
const keys = path.split('.');
|
|
286
|
+
let value = obj;
|
|
287
|
+
|
|
288
|
+
for (let i in keys) {
|
|
289
|
+
const key = keys[i]
|
|
290
|
+
const hasNext = i < keys.length - 1
|
|
291
|
+
|
|
292
|
+
if(hasNext){
|
|
293
|
+
|
|
294
|
+
if(!value[key]){
|
|
295
|
+
try{
|
|
296
|
+
value = await value[camelCase(`get-${key}`)]()
|
|
297
|
+
}
|
|
298
|
+
catch(e){}
|
|
299
|
+
}
|
|
300
|
+
else{
|
|
301
|
+
value = value[key]
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if(!value){
|
|
305
|
+
return
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
else{
|
|
310
|
+
value = key in value.dataValues ?
|
|
311
|
+
value[key] :
|
|
312
|
+
opt.defaultValue
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return value;
|
|
317
|
+
}
|
|
318
|
+
|
|
282
319
|
const createObjectFromPath = (obj, path, value) => {
|
|
283
320
|
const keys = path.split('.');
|
|
284
321
|
|
|
@@ -526,8 +563,16 @@ const dayTimeRange = (params, value) => {
|
|
|
526
563
|
})
|
|
527
564
|
}
|
|
528
565
|
|
|
566
|
+
function capitalize(str) {
|
|
567
|
+
return (str ?? '')
|
|
568
|
+
.toString()
|
|
569
|
+
.split(' ')
|
|
570
|
+
.map(_ => _.charAt(0).toUpperCase() + _.slice(1).toLowerCase())
|
|
571
|
+
.join(' ')
|
|
572
|
+
}
|
|
529
573
|
|
|
530
574
|
module.exports = {
|
|
575
|
+
capitalize,
|
|
531
576
|
ceil,
|
|
532
577
|
floor,
|
|
533
578
|
ftWildcard,
|
|
@@ -546,6 +591,7 @@ module.exports = {
|
|
|
546
591
|
getPresetSortWhereParams,
|
|
547
592
|
unflatten,
|
|
548
593
|
accessNestedObject,
|
|
594
|
+
accessNestedSequelize,
|
|
549
595
|
createObjectFromPath,
|
|
550
596
|
generateStylesheet,
|
|
551
597
|
hexToRgb,
|
package/src/utils/helpers.mjs
CHANGED
|
@@ -360,7 +360,40 @@ function applyDatasourceReplacer(value, datasource){
|
|
|
360
360
|
return value
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
function capitalize(str) {
|
|
364
|
+
return str.replace(/\b\w/g, function(char) {
|
|
365
|
+
return char.toUpperCase();
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const round = (num, precision = 0) => {
|
|
370
|
+
var p = Math.pow(10, precision)
|
|
371
|
+
var n = (num * p) * (1 + Number.EPSILON)
|
|
372
|
+
return Math.round(n) / p
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
let invokeAfterIdleStore = []
|
|
376
|
+
const invokeAfterIdle = (callback, delay = 1300) => {
|
|
377
|
+
let timeoutId
|
|
378
|
+
|
|
379
|
+
return function(){
|
|
380
|
+
if(invokeAfterIdleStore.includes(callback))
|
|
381
|
+
return
|
|
382
|
+
|
|
383
|
+
invokeAfterIdleStore.push(callback)
|
|
384
|
+
|
|
385
|
+
window.clearTimeout(timeoutId)
|
|
386
|
+
timeoutId = window.setTimeout(() => {
|
|
387
|
+
callback.apply(this, arguments)
|
|
388
|
+
invokeAfterIdleStore.splice(invokeAfterIdleStore.indexOf(callback), 1)
|
|
389
|
+
}, delay)
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
|
|
363
394
|
export {
|
|
395
|
+
capitalize,
|
|
396
|
+
round,
|
|
364
397
|
downsizeImage,
|
|
365
398
|
hexToRgb,
|
|
366
399
|
rgbToHex,
|
|
@@ -382,7 +415,8 @@ export {
|
|
|
382
415
|
createFormData,
|
|
383
416
|
unslugAndCapitalize,
|
|
384
417
|
applyDatasourceReplacer,
|
|
385
|
-
strVars
|
|
418
|
+
strVars,
|
|
419
|
+
invokeAfterIdle
|
|
386
420
|
}
|
|
387
421
|
|
|
388
422
|
function observeInit(){
|
|
@@ -925,9 +925,11 @@ const presetToSequelizeList = async(preset = {}, {
|
|
|
925
925
|
order:initialOrder
|
|
926
926
|
}) => {
|
|
927
927
|
|
|
928
|
+
const updatedAtField = model.rawAttributes['updatedAt'] ?? model.rawAttributes['updated_at']
|
|
929
|
+
|
|
928
930
|
if(!initialOrder){
|
|
929
931
|
initialOrder = [
|
|
930
|
-
[
|
|
932
|
+
...(updatedAtField ? [[ updatedAtField.fieldName, 'desc' ]] : []),
|
|
931
933
|
[ 'id', 'desc' ],
|
|
932
934
|
]
|
|
933
935
|
}
|
|
@@ -1036,7 +1038,8 @@ const presetToSequelizeList = async(preset = {}, {
|
|
|
1036
1038
|
...replacements,
|
|
1037
1039
|
...filterReplacements,
|
|
1038
1040
|
...searchReplacements
|
|
1039
|
-
]
|
|
1041
|
+
],
|
|
1042
|
+
itemsPerPage
|
|
1040
1043
|
}
|
|
1041
1044
|
}
|
|
1042
1045
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import md5 from "md5";
|
|
2
|
+
|
|
1
3
|
const sortsFn = function(a, b, sorts, index){
|
|
2
4
|
|
|
3
5
|
const sort = sorts[index]
|
|
@@ -31,24 +33,31 @@ const pickColumns = function(columns, keys){
|
|
|
31
33
|
return picked
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
const getPresetUid = function(extra = ''){
|
|
37
|
+
return md5('preset-' + new Date().getTime() + extra)
|
|
38
|
+
}
|
|
39
|
+
|
|
34
40
|
const setupConfig = (config) => {
|
|
35
41
|
|
|
36
42
|
if(Array.isArray(config.presets)){
|
|
37
43
|
for(let idx in config.presets){
|
|
38
|
-
if(!Array.isArray(config.presets[idx].columns)){
|
|
39
|
-
config.presets[idx].columns = []
|
|
44
|
+
if(!Array.isArray(config.presets[idx].params.columns)){
|
|
45
|
+
config.presets[idx].params.columns = []
|
|
40
46
|
}
|
|
41
47
|
|
|
42
|
-
if(config.presets[idx].columns[0] && typeof config.presets[idx].columns[0] === 'string'){
|
|
43
|
-
config.presets[idx].columns = pickColumns(config.columns ?? [], config.presets[idx].columns)
|
|
48
|
+
if(config.presets[idx].params.columns[0] && typeof config.presets[idx].params.columns[0] === 'string'){
|
|
49
|
+
config.presets[idx].params.columns = pickColumns(config.params.columns ?? [], config.presets[idx].params.columns)
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
if(!config.presets[idx].view)
|
|
47
|
-
config.presets[idx].view = 'table'
|
|
52
|
+
if(!config.presets[idx].params.view)
|
|
53
|
+
config.presets[idx].params.view = 'table'
|
|
54
|
+
|
|
55
|
+
if(!config.presets[idx].uid)
|
|
56
|
+
config.presets[idx].uid = getPresetUid(idx)
|
|
48
57
|
}
|
|
49
58
|
|
|
50
|
-
if(!config.presetIdx && config.presets.length > 0){
|
|
51
|
-
config.presetIdx = 0
|
|
59
|
+
if(!config.params.presetIdx && config.presets.length > 0){
|
|
60
|
+
config.params.presetIdx = config.presets[0].uid
|
|
52
61
|
}
|
|
53
62
|
}
|
|
54
63
|
|
|
@@ -147,11 +156,11 @@ const generatePivotColumns = (preset, items) => {
|
|
|
147
156
|
}
|
|
148
157
|
}
|
|
149
158
|
|
|
150
|
-
(preset.pivot.columns ?? []).filter(_ => !pivotColumns.find(i => i.key === _.key))
|
|
159
|
+
/*(preset.pivot.columns ?? []).filter(_ => !pivotColumns.find(i => i.key === _.key))
|
|
151
160
|
.forEach(_ => {
|
|
152
161
|
_.visible = false
|
|
153
162
|
pivotColumns.push(_)
|
|
154
|
-
})
|
|
163
|
+
})*/
|
|
155
164
|
|
|
156
165
|
preset.pivot.columns = pivotColumns
|
|
157
166
|
|
|
@@ -177,9 +186,10 @@ const generateTotalColumns = (preset, items) => {
|
|
|
177
186
|
}
|
|
178
187
|
|
|
179
188
|
export {
|
|
180
|
-
|
|
189
|
+
getPresetUid,
|
|
190
|
+
generatePivotColumns,
|
|
191
|
+
generateTotalColumns,
|
|
181
192
|
pickColumns,
|
|
182
193
|
setupConfig,
|
|
183
|
-
|
|
184
|
-
generateTotalColumns
|
|
194
|
+
sortsFn,
|
|
185
195
|
}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="$style.comp" :style="cStyle">
|
|
3
|
+
|
|
4
|
+
<div v-if="readyState === 2" class="flex-1 flex items-center justify-center">
|
|
5
|
+
<label class="text-text-300">Loading...</label>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div v-else-if="isError" class="flex-1 flex">
|
|
9
|
+
<div :style="barStyles" class="flex-1 flex items-center justify-center">
|
|
10
|
+
<p class="text-text-300">{{ value.message }}</p>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div v-else class="flex flex-col gap-3">
|
|
15
|
+
<div class="flex flex-row items-center gap-4">
|
|
16
|
+
<label class="text-text-400 text-ellipsis overflow-hidden whitespace-nowrap flex-1">{{ label }}</label>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div class="flex-1">
|
|
20
|
+
<Bar :style="barStyles"
|
|
21
|
+
:options="chartOptions"
|
|
22
|
+
:data="chartData"/>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div v-if="yAxeMultiple && yLegends?.length > 0" class="flex flex-row items-center flex-wrap justify-center gap-1">
|
|
26
|
+
<div v-for="yLegend in showedYLegends"
|
|
27
|
+
@click="toggleExcludes(yLegend)"
|
|
28
|
+
class="p-0.5 px-1 rounded-full text-black cursor-default text-xs min-w-[50px] max-w-[60px] text-center text-ellipsis overflow-hidden whitespace-nowrap capitalize"
|
|
29
|
+
:class="excluded.includes(yLegend.key) ? 'bg-text-200' : ''"
|
|
30
|
+
:style="{ backgroundColor:excluded.includes(yLegend.key) ? '' : yLegend.color }">
|
|
31
|
+
{{ yLegend.key }}
|
|
32
|
+
</div>
|
|
33
|
+
<button v-if="yLegends.length > showedYLegends.length" @click="$refs.legendModal.open()"
|
|
34
|
+
class="bg-text-200 hover:bg-primary-600 w-[21px] h-[21px] rounded-full flex items-center justify-center">
|
|
35
|
+
<svg width="14" height="14" class="fill-white opacity-70" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M64 192C28.61 192 0 220.6 0 256s28.61 64 64 64s64-28.62 64-64S99.39 192 64 192zM256 192C220.6 192 192 220.6 192 256s28.61 64 64 64s64-28.62 64-64S291.4 192 256 192zM448 192c-35.39 0-64 28.62-64 64s28.61 64 64 64s64-28.62 64-64S483.4 192 448 192z"/></svg>
|
|
36
|
+
</button>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<Modal ref="legendModal" width="320" height="360" :dismissable="true" @dismiss="$refs.legendModal.close()">
|
|
40
|
+
<template v-slot:head>
|
|
41
|
+
<div class="relative p-5">
|
|
42
|
+
<h3>Legend</h3>
|
|
43
|
+
<div class="absolute top-0 right-0 p-2">
|
|
44
|
+
<button type="button" class="p-2" @click="$refs.legendModal.close()">
|
|
45
|
+
<svg width="24" height="24" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
|
|
46
|
+
<path d="M6.53034 5.46965C6.23745 5.17676 5.76257 5.17676 5.46968 5.46965C5.17679 5.76255 5.17679 6.23742 5.46968 6.53031L10.9393 12L5.46967 17.4697C5.17678 17.7626 5.17678 18.2374 5.46967 18.5303C5.76256 18.8232 6.23744 18.8232 6.53033 18.5303L12 13.0606L17.4697 18.5303C17.7626 18.8232 18.2375 18.8232 18.5303 18.5303C18.8232 18.2374 18.8232 17.7626 18.5303 17.4697L13.0607 12L18.5303 6.53032C18.8232 6.23743 18.8232 5.76256 18.5303 5.46966C18.2374 5.17677 17.7626 5.17677 17.4697 5.46966L12 10.9393L6.53034 5.46965Z"/>
|
|
47
|
+
</svg>
|
|
48
|
+
</button>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
52
|
+
<div class="flex-1 p-5 pt-0">
|
|
53
|
+
|
|
54
|
+
<div class="flex flex-col divide-y divide-text-50">
|
|
55
|
+
<div v-for="yLegend in yLegends" class="flex flex-row items-center gap-2 py-2">
|
|
56
|
+
<div class="w-[19px] h-[19px] rounded-full" :style="{ backgroundColor:yLegend.color }"></div>
|
|
57
|
+
<div class="flex-1 text-ellipsis whitespace-nowrap overflow-hidden">{{ yLegend.key }}</div>
|
|
58
|
+
<Checkbox :value="!excluded.includes(yLegend.key)" @click.prevent="toggleExcludes(yLegend)" />
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<br />
|
|
63
|
+
|
|
64
|
+
</div>
|
|
65
|
+
</Modal>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<script>
|
|
72
|
+
|
|
73
|
+
import {Bar} from 'vue-chartjs'
|
|
74
|
+
import {color} from 'chart.js/helpers'
|
|
75
|
+
import {strVars} from "../../utils/helpers.mjs";
|
|
76
|
+
import {readyStateMixin} from "../../mixin/ready-state";
|
|
77
|
+
|
|
78
|
+
export default{
|
|
79
|
+
|
|
80
|
+
components: {
|
|
81
|
+
Bar
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
computed: {
|
|
85
|
+
|
|
86
|
+
barStyles(){
|
|
87
|
+
return {
|
|
88
|
+
position: "relative",
|
|
89
|
+
height: `${this.barHeight}px`
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
cStyle(){
|
|
94
|
+
return {
|
|
95
|
+
minHeight: this.height
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
chartData(){
|
|
100
|
+
if(!this.value?.datasets)
|
|
101
|
+
return { labels:[], datasets:[] }
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
labels: this.value?.labels ?? [],
|
|
105
|
+
datasets: this.value.datasets
|
|
106
|
+
.filter(_ => !this.excluded.includes(_.label))
|
|
107
|
+
.map(_ => {
|
|
108
|
+
return {
|
|
109
|
+
..._,
|
|
110
|
+
borderWidth: 0,
|
|
111
|
+
borderColor: function(context, options) {
|
|
112
|
+
const hex = options.color;
|
|
113
|
+
return color(hex).darken(0.5).rgbString()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
chartOptions(){
|
|
121
|
+
return {
|
|
122
|
+
responsive: true,
|
|
123
|
+
maintainAspectRatio: false,
|
|
124
|
+
borderRadius: 4,
|
|
125
|
+
plugins: {
|
|
126
|
+
legend: {
|
|
127
|
+
display: false // This hides the legend
|
|
128
|
+
},
|
|
129
|
+
tooltip: {
|
|
130
|
+
callbacks: {
|
|
131
|
+
title: function(tooltipItems) {
|
|
132
|
+
return `${tooltipItems[0].label}`;
|
|
133
|
+
},
|
|
134
|
+
label: (tooltipItem) => {
|
|
135
|
+
if(this.yAxeMultiple && this.usePercentage)
|
|
136
|
+
return `${tooltipItem.dataset.label}: ${tooltipItem.dataset.data0[tooltipItem.dataIndex]} (${tooltipItem.raw}%)`
|
|
137
|
+
else{
|
|
138
|
+
if(this.stacked){
|
|
139
|
+
const total = this.value.datasets.reduce((acc, cur) => acc + cur.data[tooltipItem.dataIndex], 0)
|
|
140
|
+
const percentage = ((tooltipItem.raw / total) * 100).toFixed(1)
|
|
141
|
+
|
|
142
|
+
return `${tooltipItem.dataset.label}: ${tooltipItem.raw} (${percentage}%)`;
|
|
143
|
+
}
|
|
144
|
+
return `${tooltipItem.dataset.label}: ${tooltipItem.raw}`;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
scales: {
|
|
151
|
+
x: {
|
|
152
|
+
grid: {
|
|
153
|
+
display: true,
|
|
154
|
+
color: function(context){
|
|
155
|
+
return `rgba(255,255,255, .1)`
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
ticks: {
|
|
159
|
+
callback: function(value) {
|
|
160
|
+
const label = this.getLabelForValue(value) ?? '';
|
|
161
|
+
return label.length > 10 ? label.slice(0, 30) + '...' : label;
|
|
162
|
+
},
|
|
163
|
+
minRotation: 0,
|
|
164
|
+
maxRotation: 0,
|
|
165
|
+
font: {
|
|
166
|
+
size: 11
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
stacked: !!this.stacked,
|
|
170
|
+
},
|
|
171
|
+
y: {
|
|
172
|
+
beginAtZero: true,
|
|
173
|
+
ticks: {
|
|
174
|
+
display: true,
|
|
175
|
+
font: {
|
|
176
|
+
size: 11,
|
|
177
|
+
},
|
|
178
|
+
callback: (value, index, ticks) => {
|
|
179
|
+
if(this.usePercentage)
|
|
180
|
+
return Math.round(index / (ticks.length - 1) * 100) + '%'
|
|
181
|
+
return value
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
grid: {
|
|
185
|
+
display: true,
|
|
186
|
+
color: function(context){
|
|
187
|
+
return `rgba(255,255,255, .1)`
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
stacked: !!this.stacked
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
onClick: this.onClick
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
isError(){
|
|
198
|
+
return this.value?.error === 1
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
yLegends(){
|
|
202
|
+
return this.value?.yLegends ?? []
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
showedYLegends(){
|
|
206
|
+
return this.yLegends.slice(0, 5)
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
data(){
|
|
212
|
+
return {
|
|
213
|
+
backgroundColors: [
|
|
214
|
+
'#5D9CEC',
|
|
215
|
+
'#A0D468',
|
|
216
|
+
'#FFCE54',
|
|
217
|
+
'#FC6E51',
|
|
218
|
+
'#48CFAD',
|
|
219
|
+
'#AC92EC',
|
|
220
|
+
'#4FC1E9',
|
|
221
|
+
'#FFCE54',
|
|
222
|
+
'#ED5565',
|
|
223
|
+
'#EC87C0'
|
|
224
|
+
],
|
|
225
|
+
|
|
226
|
+
height: '',
|
|
227
|
+
datasets: null
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
inject: [ 'selectPreset' ],
|
|
232
|
+
|
|
233
|
+
methods: {
|
|
234
|
+
|
|
235
|
+
onClick(e, segments){
|
|
236
|
+
|
|
237
|
+
const clickInteractions = (this.interactions ?? []).filter(_ => _.event === 'click')
|
|
238
|
+
const segment = segments[0]
|
|
239
|
+
if(!segment) return
|
|
240
|
+
|
|
241
|
+
const dataset = this.value.datasets[segment.datasetIndex]
|
|
242
|
+
const obj = {
|
|
243
|
+
segment: this.value.labels[segment.index],
|
|
244
|
+
key: dataset.label,
|
|
245
|
+
value: dataset.data[segment.index]
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
for(let click of clickInteractions){
|
|
249
|
+
const { action, presetUid, filters } = click
|
|
250
|
+
|
|
251
|
+
const params = filters.reduce((res, cur) => {
|
|
252
|
+
res[cur.datasourceUid + '.filters.' + cur.key + '.' + cur.operator] = strVars(cur.value, obj)
|
|
253
|
+
return res
|
|
254
|
+
}, {})
|
|
255
|
+
|
|
256
|
+
switch(action){
|
|
257
|
+
|
|
258
|
+
case 'openDashboardPreset':
|
|
259
|
+
if(typeof this.selectPreset === 'function')
|
|
260
|
+
this.selectPreset(presetUid, params)
|
|
261
|
+
break
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
toggleExcludes(yLegend){
|
|
268
|
+
if(this.excluded.includes(yLegend.key))
|
|
269
|
+
this.excluded.splice(this.excluded.indexOf(yLegend.key), 1)
|
|
270
|
+
else
|
|
271
|
+
this.excluded.push(yLegend.key)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
mounted() {
|
|
277
|
+
this.height = this.$el.clientHeight + 'px'
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
mixins: [ readyStateMixin ],
|
|
281
|
+
|
|
282
|
+
props: {
|
|
283
|
+
|
|
284
|
+
datasourceUid: String,
|
|
285
|
+
|
|
286
|
+
usePercentage: [ Boolean, Number ],
|
|
287
|
+
stacked: [ Boolean, Number ],
|
|
288
|
+
|
|
289
|
+
barHeight: {
|
|
290
|
+
type: Number,
|
|
291
|
+
default: 200
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
label: String,
|
|
295
|
+
|
|
296
|
+
xAxes: Array,
|
|
297
|
+
yAxes: Array,
|
|
298
|
+
xAxeMultiple: Boolean,
|
|
299
|
+
yAxeMultiple: Boolean,
|
|
300
|
+
|
|
301
|
+
excluded: {
|
|
302
|
+
type: Array,
|
|
303
|
+
default: []
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
segments: Array,
|
|
307
|
+
xAxesGroup: String,
|
|
308
|
+
|
|
309
|
+
yAxeOnClick: Array,
|
|
310
|
+
|
|
311
|
+
value: Object,
|
|
312
|
+
|
|
313
|
+
interactions: Array,
|
|
314
|
+
|
|
315
|
+
uid: String
|
|
316
|
+
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
</script>
|
|
322
|
+
|
|
323
|
+
<style module>
|
|
324
|
+
|
|
325
|
+
.comp{
|
|
326
|
+
@apply border-[1px] border-text-50 bg-base-500 rounded-lg;
|
|
327
|
+
@apply p-3 flex flex-col min-h-[200px];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
</style>
|