easycomponentstools 1.0.0-dev.1.1 → 1.0.0-dev.11

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.
@@ -0,0 +1,147 @@
1
+ <template>
2
+ <v-dialog v-model="dialog" max-width="450">
3
+ <template #activator="{ props }">
4
+ <v-btn
5
+ elevation="0"
6
+ v-bind="props"
7
+ style="margin-left: -14px"
8
+ class="text-md-body-large"
9
+ >
10
+ <i class="icon-columns-3 mr-1" /> {{ $i18n.t('table.columns') }}
11
+ </v-btn>
12
+ </template>
13
+ <v-card class="list_draggable" @mousemove="onDragMove">
14
+ <div
15
+ v-if="draggedItem"
16
+ class="floating-drag-item"
17
+ :style="`top:${mouseY}px;left:${mouseX}px`"
18
+ >
19
+ {{ draggedItem.title }}
20
+ </div>
21
+ <v-card-title>
22
+ <v-row>
23
+ <v-col cols="11">
24
+ {{ $i18n.t('table.columns') }}
25
+ </v-col>
26
+ <v-col cols="1" class="d-flex justify-end">
27
+ <i class="icon icon-x" style="cursor: pointer" @click="dialog = false"></i>
28
+ </v-col>
29
+ </v-row>
30
+ </v-card-title>
31
+ <v-card-text>
32
+ <v-list style="background: transparent">
33
+ <v-list-item
34
+ v-for="(header, index) in headerItems"
35
+ :key="index"
36
+ class="draggable-item"
37
+ @mousedown.left="onDragStart($event, header)"
38
+ @mouseup.left="onDrop($event, header)"
39
+ >
40
+ <v-row>
41
+ <v-col
42
+ cols="2"
43
+ class="d-flex justify-content-start align-items-center"
44
+ style="margin-top: -15px"
45
+ >
46
+ <v-switch
47
+ v-model="header.show"
48
+ hide-details
49
+ color="primary"
50
+ ></v-switch>
51
+ </v-col>
52
+ <v-col cols="7" class="d-flex align-items-center">
53
+ {{ header.title }}
54
+ </v-col>
55
+ <v-col cols="3" class="d-flex justify-content-end align-items-center">
56
+ <i class="icon icon-grip-horizontal"></i>
57
+ </v-col>
58
+ </v-row>
59
+ </v-list-item>
60
+ </v-list>
61
+ </v-card-text>
62
+ <v-card-actions>
63
+ <v-row>
64
+ <v-col class="d-flex justify-start">
65
+ <v-btn color="error" @click="dialog = false">
66
+ {{ $i18n.t('table.cancel') }}
67
+ </v-btn>
68
+ </v-col>
69
+ <v-col class="d-flex justify-end">
70
+ <v-btn color="success" @click="submit">
71
+ {{ $i18n.t('table.submit') }}
72
+ </v-btn>
73
+ </v-col>
74
+ </v-row>
75
+ </v-card-actions>
76
+ </v-card>
77
+ </v-dialog>
78
+ </template>
79
+
80
+ <script setup lang="ts">
81
+ import { onBeforeMount, ref, Ref } from 'vue'
82
+ import { HeaderDTO } from '../../../types/table'
83
+ import { useI18n } from 'vue-i18n'
84
+
85
+ type propsType = {
86
+ headers: HeaderDTO[]
87
+ }
88
+ const props = defineProps<propsType>()
89
+ const $i18n = useI18n()
90
+ const emit = defineEmits(['submit'])
91
+ const headerItems: Ref<HeaderDTO[]> = ref([])
92
+ const dialog: Ref<boolean> = ref(false)
93
+ const mouseX: Ref<number> = ref(0)
94
+ const mouseY: Ref<number> = ref(0)
95
+ const startX: Ref<number> = ref(0)
96
+ const startY: Ref<number> = ref(0)
97
+ const offsetX: Ref<number> = ref(0)
98
+ const offsetY: Ref<number> = ref(0)
99
+ const draggedItem: Ref<HeaderDTO | null> = ref(null)
100
+
101
+ const onDragStart = (event: DragEvent, header: HeaderDTO) => {
102
+ startX.value = event.clientX
103
+ startY.value = event.clientY
104
+ const rect = document.querySelector('.list_draggable')?.getBoundingClientRect()
105
+ offsetX.value = event.clientX - (rect?.left ?? 0) - 20
106
+ offsetY.value = event.clientY - (rect?.top ?? 0) - 20
107
+ draggedItem.value = header
108
+ }
109
+ const onDrop = (event: DragEvent, targetHeader: HeaderDTO) => {
110
+ event.preventDefault()
111
+ if (draggedItem.value && draggedItem.value !== targetHeader) {
112
+ const targetIndex = headerItems.value.findIndex((v) => v.value === targetHeader.value)
113
+ const draggableIndex = headerItems.value.findIndex(
114
+ (v) => v.value === draggedItem.value?.value
115
+ )
116
+ if (targetIndex !== -1 && draggableIndex !== -1) {
117
+ headerItems.value.splice(draggableIndex, 1)
118
+ headerItems.value.splice(targetIndex, 0, draggedItem.value)
119
+ }
120
+ }
121
+ draggedItem.value = null
122
+ }
123
+ const onDragMove = (event: DragEvent) => {
124
+ if (draggedItem.value) {
125
+ mouseX.value = event.clientX - startX.value + offsetX.value
126
+ mouseY.value = event.clientY - startY.value + offsetY.value
127
+ }
128
+ }
129
+ const submit = () => {
130
+ const headers = headerItems.value.map((header) => {
131
+ header['hidden'] = !header?.show
132
+ return header
133
+ })
134
+ emit('submit', headers)
135
+ dialog.value = false
136
+ }
137
+
138
+ onBeforeMount(() => {
139
+ headerItems.value = [...props.headers]
140
+ headerItems.value = headerItems.value.map((header) => {
141
+ Object.assign(header, { show: !header?.hidden })
142
+ return header
143
+ })
144
+ })
145
+ </script>
146
+
147
+ <style scoped lang="scss"></style>
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <div>
3
+ <v-list-item :disabled="disabledComputed" @click="$useDataTableExport.exportData('pdf')">
4
+ <i class="icon-file-sliders" /> PDF
5
+ </v-list-item>
6
+ <v-list-item :disabled="disabledComputed" @click="$useDataTableExport.exportData('csv')">
7
+ <i class="icon-file-sliders" /> CSV
8
+ </v-list-item>
9
+ <v-list-item :disabled="disabledComputed" @click="$useDataTableExport.exportData('xlsx')">
10
+ <i class="icon-file-sliders" /> EXCEL
11
+ </v-list-item>
12
+ <v-list-item :disabled="disabledComputed" @click="$useDataTableExport.exportData('word')">
13
+ <i class="icon-file-sliders" /> WORD
14
+ </v-list-item>
15
+ <v-list-item :disabled="disabledComputed" @click="$useDataTableExport.exportData('print')">
16
+ <i class="icon-file-sliders" /> {{ $i18n.t('table.print') }}
17
+ </v-list-item>
18
+ </div>
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import useDataTableExport from '../../../composables/useDataTableExport'
23
+ import { useI18n } from 'vue-i18n'
24
+ import { computed, watch } from 'vue'
25
+ import { AdvancedFiltersDTO, HardFiltersDTO, HeaderDTO, SortDTO } from '../../../types/table'
26
+
27
+ const emit = defineEmits(['loading'])
28
+ const $i18n = useI18n()
29
+ const $useDataTableExport = useDataTableExport()
30
+ const disabledComputed = computed(
31
+ () => !$useDataTableExport.api.value || $useDataTableExport.total.value === 0
32
+ )
33
+
34
+ const setExportDataInfo = (
35
+ api: string,
36
+ headers: HeaderDTO[],
37
+ total: number,
38
+ sort: SortDTO | null,
39
+ filters: AdvancedFiltersDTO[],
40
+ hard_filters: HardFiltersDTO[]
41
+ ) => {
42
+ $useDataTableExport.setApi(api)
43
+ if (sort) $useDataTableExport.setSort({ ...sort })
44
+ $useDataTableExport.setFilters([...filters])
45
+ $useDataTableExport.setHardFilters([...hard_filters])
46
+ $useDataTableExport.setHeaders([...headers])
47
+ $useDataTableExport.setTotal(total)
48
+ }
49
+ const setHeaders = (headers: HeaderDTO[]) => {
50
+ $useDataTableExport.setHeaders([...headers])
51
+ }
52
+ const setFilters = (filters: AdvancedFiltersDTO[]) => {
53
+ $useDataTableExport.setFilters([...filters])
54
+ }
55
+ const setSort = (sort: SortDTO) => {
56
+ $useDataTableExport.setSort({ ...sort })
57
+ }
58
+ const setHardFilters = (HardFilters: HardFiltersDTO[]) => {
59
+ $useDataTableExport.setHardFilters([...HardFilters])
60
+ }
61
+ const setTotal = (total: number) => {
62
+ $useDataTableExport.setTotal(total)
63
+ }
64
+
65
+ watch($useDataTableExport.loading, (v) => emit('loading', v), { immediate: true })
66
+
67
+ defineExpose({ setExportDataInfo, setHeaders, setFilters, setSort, setHardFilters, setTotal })
68
+ </script>
69
+
70
+ <style scoped></style>
@@ -0,0 +1,219 @@
1
+ <template>
2
+ <div style="width: 100%">
3
+ <v-btn
4
+ v-if="active_filters.length !== 0"
5
+ color="primary"
6
+ width="45%"
7
+ class="mr-2"
8
+ @click="clear"
9
+ >
10
+ <i class="icon-funnel-x"></i>
11
+ </v-btn>
12
+ <v-btn
13
+ v-bind="props"
14
+ color="primary"
15
+ :width="active_filters.length === 0 ? '100%' : '50%'"
16
+ @click="dialog = true"
17
+ >
18
+ <i class="icon-funnel mr-1"></i> {{ $i18n.t('table.filters') }}
19
+ </v-btn>
20
+ <v-dialog v-model="dialog" width="600">
21
+ <v-card>
22
+ <v-card-title>
23
+ <v-row>
24
+ <v-col cols="11">
25
+ {{ $i18n.t('table.filters') }}
26
+ </v-col>
27
+ <v-col cols="1" class="d-flex justify-end">
28
+ <i
29
+ class="icon icon-x"
30
+ style="cursor: pointer"
31
+ @click="dialog = false"
32
+ ></i>
33
+ </v-col>
34
+ </v-row>
35
+ </v-card-title>
36
+ <v-card-text>
37
+ <v-row v-for="(filter, index) in allFilters" :key="index">
38
+ <v-col v-if="isFilterTypeInput(filter.type)" cols="12">
39
+ <v-text-field
40
+ v-model="allFilters[index].value"
41
+ :label="filter.label"
42
+ hide-details
43
+ variant="underlined"
44
+ ></v-text-field>
45
+ </v-col>
46
+ <v-col v-if="filter.type === 'selects'" cols="12">
47
+ <v-select
48
+ v-model="allFilters[index].value"
49
+ :label="filter.label"
50
+ :items="filter.items"
51
+ hide-details
52
+ variant="underlined"
53
+ ></v-select>
54
+ </v-col>
55
+ <v-col
56
+ v-if="filter.type === 'date' && Array.isArray(allFilters[index].value)"
57
+ cols="12"
58
+ >
59
+ <RangeDatePicker
60
+ v-model="allFilters[index].value"
61
+ :input="{ ...inputRangDatePicker, label: filter?.label ?? '' }"
62
+ />
63
+ </v-col>
64
+ <v-col
65
+ v-if="
66
+ filter.type === 'dateTime' && Array.isArray(allFilters[index].value)
67
+ "
68
+ cols="12"
69
+ >
70
+ <RangeDatePicker
71
+ v-model="allFilters[index].value"
72
+ :input="{ ...inputRangDateTimePicker, label: filter?.label ?? '' }"
73
+ />
74
+ </v-col>
75
+ <v-col
76
+ v-if="
77
+ isAdvancedFiltersComparisonDTO(filter.type, allFilters[index].value)
78
+ "
79
+ cols="12"
80
+ >
81
+ <v-row>
82
+ <v-col cols="2">
83
+ <v-select
84
+ v-model="allFilters[index].value.symbol"
85
+ hide-details
86
+ :items="['<', '<=', '=', '>', '>=']"
87
+ variant="underlined"
88
+ ></v-select>
89
+ </v-col>
90
+ <v-col cols="10">
91
+ <v-text-field
92
+ v-model.number="allFilters[index].value.value"
93
+ type="number"
94
+ hide-details
95
+ :label="filter.label"
96
+ variant="underlined"
97
+ ></v-text-field>
98
+ </v-col>
99
+ </v-row>
100
+ </v-col>
101
+ </v-row>
102
+ </v-card-text>
103
+ <v-card-actions class="ma-2">
104
+ <v-row>
105
+ <v-col class="d-flex justify-start">
106
+ <v-btn color="error" variant="flat" @click="dialog = false">
107
+ {{ $i18n.t('message.cancel') }}
108
+ </v-btn>
109
+ </v-col>
110
+ <v-col class="d-flex justify-end">
111
+ <v-btn color="success" variant="flat" @click="submit">
112
+ {{ $i18n.t('message.submit') }}
113
+ </v-btn>
114
+ </v-col>
115
+ </v-row>
116
+ </v-card-actions>
117
+ </v-card>
118
+ </v-dialog>
119
+ </div>
120
+ </template>
121
+
122
+ <script setup lang="ts">
123
+ import RangeDatePicker from '../../Form/types/RangeDatePicker.vue'
124
+ import { onMounted, Ref, ref } from 'vue'
125
+ import { useI18n } from 'vue-i18n'
126
+ import { AdvancedFiltersComparisonDTO, AdvancedFiltersDTO } from '../../../types/table'
127
+ import { InputDTO } from '../../../types/form'
128
+
129
+ type propsType = {
130
+ filters: AdvancedFiltersDTO[]
131
+ }
132
+
133
+ const emit = defineEmits(['search'])
134
+ const dialog: Ref<boolean> = ref(false)
135
+ const $i18n = useI18n()
136
+ const active_filters: Ref<AdvancedFiltersDTO[]> = ref([])
137
+ const allFilters: Ref<AdvancedFiltersDTO[]> = ref([])
138
+ const props = defineProps<propsType>()
139
+ const inputRangDateTimePicker: InputDTO = {
140
+ type: 'rangeDate',
141
+ key: 'inputRangDateTimePicker',
142
+ label: '',
143
+ options: {
144
+ enableTime: true,
145
+ },
146
+ }
147
+ const inputRangDatePicker: InputDTO = {
148
+ type: 'rangeDate',
149
+ key: 'inputRangDatePicker',
150
+ label: '',
151
+ options: {
152
+ enableTime: false,
153
+ },
154
+ }
155
+
156
+ function isAdvancedFiltersComparisonDTO(
157
+ type: string,
158
+ obj: any
159
+ ): obj is AdvancedFiltersComparisonDTO {
160
+ return (
161
+ type === 'comparison' &&
162
+ typeof obj === 'object' &&
163
+ obj !== null &&
164
+ 'symbol' in obj &&
165
+ 'value' in obj
166
+ )
167
+ }
168
+ function isFilterTypeInput(type: string) {
169
+ return type === 'input' || type === 'inArray' || type === 'likeInArray'
170
+ }
171
+ const submit = () => {
172
+ active_filters.value = []
173
+ props.filters.forEach((filter: AdvancedFiltersDTO) => {
174
+ let val = null
175
+ if (
176
+ isAdvancedFiltersComparisonDTO(filter.type, filter.value) ||
177
+ ((filter.type === 'date' || filter.type === 'dateTime') &&
178
+ Array.isArray(filter.value) &&
179
+ filter.value.length !== 0) ||
180
+ ((filter.type === 'input' ||
181
+ filter.type === 'inArray' ||
182
+ filter.type === 'likeInArray' ||
183
+ filter.type === 'selects') &&
184
+ filter.value)
185
+ ) {
186
+ val = filter.value
187
+ }
188
+ if (val) {
189
+ active_filters.value.push({
190
+ type: filter.type,
191
+ column: filter.column,
192
+ value: val,
193
+ })
194
+ }
195
+ })
196
+ emit('search', active_filters.value)
197
+ dialog.value = false
198
+ }
199
+ const clear = () => {
200
+ dialog.value = false
201
+ active_filters.value = []
202
+ emit('search', active_filters.value)
203
+ props.filters.forEach((filter) => {
204
+ if (filter.type === 'comparison') {
205
+ filter.value = { value: 0, symbol: '<' }
206
+ } else if (filter.type === 'date' || filter.type === 'dateTime') {
207
+ filter.value = []
208
+ } else if (filter.value) {
209
+ filter.value = ''
210
+ }
211
+ })
212
+ }
213
+
214
+ onMounted(() => {
215
+ allFilters.value = props.filters
216
+ })
217
+ </script>
218
+
219
+ <style scoped></style>
@@ -0,0 +1,154 @@
1
+ <template>
2
+ <v-row class="mb-2">
3
+ <v-col cols="12" md="12" lg="6" sm="12">
4
+ <h4 v-if="title">{{ title }}</h4>
5
+ <v-chip-group
6
+ v-model="active_hard_filters"
7
+ :multiple="multipleHardFilters"
8
+ filter
9
+ @update:model-value="updateHardFilters"
10
+ >
11
+ <v-chip
12
+ v-for="(hardFilter, index) in hardFilters"
13
+ :key="index"
14
+ :text="hardFilter?.label ?? ''"
15
+ :value="hardFilter.key"
16
+ ></v-chip>
17
+ </v-chip-group>
18
+ </v-col>
19
+ <v-col cols="12" md="12" lg="2" sm="12" class="d-flex justify-end">
20
+ <v-btn v-if="create" color="primary" width="100%" @click="create">
21
+ <i class="icon-plus" /> {{ $i18n.t('table.create') }}
22
+ </v-btn>
23
+ </v-col>
24
+ <v-col cols="12" md="12" lg="2" sm="12" class="d-flex justify-end">
25
+ <Filters :filters="advancedFilters" @search="search"></Filters>
26
+ </v-col>
27
+ <v-col cols="12" md="12" lg="2" sm="12" class="d-flex justify-end">
28
+ <v-menu eager>
29
+ <template #activator="{ props: propsMenu }">
30
+ <v-btn color="primary" v-bind="propsMenu" width="100%">
31
+ <v-icon>icon-menu</v-icon>
32
+ {{ $i18n.t('table.more') }}
33
+ </v-btn>
34
+ </template>
35
+ <v-list>
36
+ <v-list-item>
37
+ <DialogColumns
38
+ :headers="props.headers"
39
+ @submit="submitHeaders"
40
+ ></DialogColumns>
41
+ </v-list-item>
42
+ <slot name="moreActions"></slot>
43
+ <ExportData
44
+ v-if="showExportData"
45
+ ref="exportDataRef"
46
+ @loading="(v: boolean) => emit('loading', v)"
47
+ ></ExportData>
48
+ </v-list>
49
+ </v-menu>
50
+ </v-col>
51
+ </v-row>
52
+ <v-row v-if="multipleDeletion && selected.length !== 0" class="mb-2 d-flex justify-end">
53
+ <v-col cols="12" md="12" lg="2" sm="12">
54
+ <v-btn color="error" width="100%" @click="multipleDelete">
55
+ <i class="icon-trash" /> {{ $i18n.t('table.delete') }}
56
+ </v-btn>
57
+ </v-col>
58
+ </v-row>
59
+ </template>
60
+
61
+ <script setup lang="ts">
62
+ import { AdvancedFiltersDTO, HardFiltersDTO, HeaderDTO, SortDTO } from '../../../types/table'
63
+ import { useI18n } from 'vue-i18n'
64
+ import { nextTick, onMounted, ref, Ref } from 'vue'
65
+ import useHeadersStorage from '../../../composables/useHeadersStorage'
66
+ import Filters from './Filters.vue'
67
+ import DialogColumns from './DialogColumns.vue'
68
+ import ExportData from './ExportData.vue'
69
+
70
+ type propsType = {
71
+ id: string
72
+ headers: HeaderDTO[]
73
+ create?: any
74
+ showExportData?: boolean
75
+ title?: string
76
+ multipleHardFilters?: boolean
77
+ multipleDeletion?: boolean
78
+ hardFilters: HardFiltersDTO[]
79
+ advancedFilters: AdvancedFiltersDTO[]
80
+ selected: any[]
81
+ }
82
+ const props = defineProps<propsType>()
83
+ const $i18n = useI18n()
84
+ const $useHeadersStorage = useHeadersStorage()
85
+ const emit = defineEmits(['loading', 'headers', 'search', 'hardFilters', 'multipleDelete'])
86
+ const active_hard_filters: Ref<any[]> = ref([])
87
+ const exportDataRef: any = ref(null)
88
+
89
+ const updateHardFilters = (hard_filters_keys: any) => {
90
+ const hard_filters: HardFiltersDTO[] = []
91
+ if (!Array.isArray(hard_filters_keys)) hard_filters_keys = [hard_filters_keys]
92
+ hard_filters_keys.forEach((key: string) => {
93
+ const hardFilter = props.hardFilters.filter((v) => v.key === key)
94
+ if (hardFilter.length !== 0) {
95
+ hard_filters.push({
96
+ type: hardFilter[0].type,
97
+ column: hardFilter[0].column,
98
+ })
99
+ }
100
+ })
101
+ exportDataRef.value?.setHardFilters(hard_filters)
102
+ emit('hardFilters', hard_filters)
103
+ }
104
+ const multipleDelete = () => {
105
+ emit('multipleDelete', true)
106
+ }
107
+ const search = (filters: AdvancedFiltersDTO[]) => {
108
+ exportDataRef.value?.setFilters(filters)
109
+ emit('search', filters)
110
+ }
111
+ const submitHeaders = (headers: HeaderDTO[]) => {
112
+ $useHeadersStorage.submitHeaders(headers, props.id)
113
+ const displayHeaders = headers.filter((v) => !v.hidden)
114
+ emit('headers', displayHeaders)
115
+ exportDataRef.value?.setHeaders(displayHeaders)
116
+ }
117
+ const updateExportSortData = (sort: SortDTO) => {
118
+ exportDataRef.value?.setSort(sort)
119
+ }
120
+ const updateExportDataTotal = (total: number) => {
121
+ exportDataRef.value?.setTotal(total)
122
+ }
123
+ const updateExportData = (
124
+ api: string,
125
+ headers: HeaderDTO[],
126
+ total: number,
127
+ sort: SortDTO | null,
128
+ filters: AdvancedFiltersDTO[],
129
+ hard_filters: HardFiltersDTO[]
130
+ ) => {
131
+ const displayHeaders = headers.filter((header: HeaderDTO) => !header.hidden)
132
+ nextTick(() => {
133
+ exportDataRef.value?.setExportDataInfo(
134
+ api,
135
+ displayHeaders,
136
+ total,
137
+ sort,
138
+ filters,
139
+ hard_filters
140
+ )
141
+ })
142
+ }
143
+
144
+ onMounted(() => {
145
+ active_hard_filters.value = props.hardFilters
146
+ .filter((item) => !!item.active)
147
+ .map((item) => item.column)
148
+ updateHardFilters(active_hard_filters.value)
149
+ })
150
+
151
+ defineExpose({ updateExportData, updateExportSortData, updateExportDataTotal })
152
+ </script>
153
+
154
+ <style scoped></style>
@@ -0,0 +1,19 @@
1
+ export const removeMdiSet = () => {
2
+ const run = () => {
3
+ setInterval(() => {
4
+ document.querySelectorAll('.v-icon')?.forEach((icon) => {
5
+ if (
6
+ [...icon.classList].some(
7
+ (cls) => cls.startsWith('icon-') || cls.startsWith('iconapp-')
8
+ )
9
+ ) {
10
+ icon.classList.remove('mdi')
11
+ }
12
+ })
13
+ }, 700)
14
+ }
15
+
16
+ return {
17
+ run,
18
+ }
19
+ }