shared-ritm 1.2.97 → 1.2.99

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.
Files changed (119) hide show
  1. package/README.md +103 -103
  2. package/dist/index.css +1 -1
  3. package/dist/shared-ritm.es.js +2051 -2054
  4. package/dist/shared-ritm.umd.js +25 -25
  5. package/dist/types/api/services/UserService.d.ts +7 -0
  6. package/dist/types/api/services/VideoService.d.ts +0 -14
  7. package/dist/types/api/types/Api_Tasks.d.ts +1 -1
  8. package/dist/types/index.d.ts +0 -1
  9. package/package.json +1 -1
  10. package/src/App.vue +2461 -2461
  11. package/src/api/services/AuthService.ts +64 -64
  12. package/src/api/services/CommentsService.ts +24 -24
  13. package/src/api/services/ControlsService.ts +65 -65
  14. package/src/api/services/FileService.ts +17 -17
  15. package/src/api/services/GanttService.ts +17 -17
  16. package/src/api/services/MetricsService.ts +110 -110
  17. package/src/api/services/ProjectsService.ts +68 -68
  18. package/src/api/services/RepairsService.ts +119 -119
  19. package/src/api/services/SearchService.ts +16 -16
  20. package/src/api/services/TasksService.ts +145 -145
  21. package/src/api/services/UserService.ts +101 -87
  22. package/src/api/services/VideoService.ts +0 -19
  23. package/src/api/types/Api_Comment.ts +40 -40
  24. package/src/api/types/Api_Controls.ts +72 -72
  25. package/src/api/types/Api_Files.ts +7 -7
  26. package/src/api/types/Api_Projects.ts +55 -55
  27. package/src/api/types/Api_Repairs.ts +115 -115
  28. package/src/api/types/Api_Search.ts +48 -48
  29. package/src/api/types/Api_Service.ts +9 -9
  30. package/src/api/types/Api_Tasks.ts +315 -315
  31. package/src/common/app-button/AppButton.vue +173 -173
  32. package/src/common/app-checkbox/AppCheckbox.vue +26 -26
  33. package/src/common/app-date-picker/AppDatePicker.vue +81 -81
  34. package/src/common/app-datepicker/AppDatepicker.vue +165 -165
  35. package/src/common/app-dialogs/AppConfirmDialog.vue +99 -99
  36. package/src/common/app-dropdown/AppDropdown.vue +31 -31
  37. package/src/common/app-icon/AppIcon.vue +108 -108
  38. package/src/common/app-input/AppInput.vue +148 -148
  39. package/src/common/app-input-new/AppInputNew.vue +152 -152
  40. package/src/common/app-input-search/AppInputSearch.vue +174 -174
  41. package/src/common/app-layout/AppLayout.vue +84 -84
  42. package/src/common/app-layout/components/AppLayoutHeader.vue +1 -0
  43. package/src/common/app-layout/components/AppLayoutPage.vue +16 -16
  44. package/src/common/app-loader/index.vue +43 -43
  45. package/src/common/app-page-layout/AppPageLayout.vue +122 -122
  46. package/src/common/app-select/AppSelect.vue +157 -157
  47. package/src/common/app-sheet/AppSheet.vue +120 -120
  48. package/src/common/app-sidebar/AppSidebar.vue +168 -168
  49. package/src/common/app-sidebar/components/SidebarMenu.vue +37 -37
  50. package/src/common/app-sidebar/components/SidebarMenuItem.vue +148 -148
  51. package/src/common/app-table/AppTable.vue +1 -8
  52. package/src/common/app-table/AppTableLayout.vue +4 -5
  53. package/src/common/app-table/components/TablePagination.vue +152 -152
  54. package/src/common/app-table/controllers/useColumnSelector.ts +38 -38
  55. package/src/common/app-table/controllers/useTableModel.ts +93 -93
  56. package/src/common/app-toggle/AppToggle.vue +24 -24
  57. package/src/common/app-wrapper/AppWrapper.vue +28 -28
  58. package/src/global.d.ts +1 -1
  59. package/src/icons/components/arrow-down-icon.vue +25 -25
  60. package/src/icons/components/arrow-frame-icon.vue +19 -19
  61. package/src/icons/components/arrow-square.vue +22 -22
  62. package/src/icons/components/table-filter-icon.vue +30 -30
  63. package/src/icons/dialogs/RemoveIcon.vue +12 -12
  64. package/src/icons/dialogs/SafetyIcon.vue +12 -12
  65. package/src/icons/header/NotificationIcon.vue +18 -18
  66. package/src/icons/header/PersonIcon.vue +11 -11
  67. package/src/icons/header/SettingIcon.vue +14 -14
  68. package/src/icons/header/flashIcon.vue +24 -24
  69. package/src/icons/header/searchStatusIcon.vue +24 -24
  70. package/src/icons/header/smallCapsIcon.vue +34 -34
  71. package/src/icons/sidebar/assign-module-icon.vue +36 -36
  72. package/src/icons/sidebar/instrument-history-icon.vue +32 -32
  73. package/src/icons/sidebar/instrument-order-icon.vue +38 -38
  74. package/src/icons/sidebar/instrument-work-zone-icon.vue +18 -18
  75. package/src/icons/sidebar/instruments-icon.vue +45 -45
  76. package/src/icons/sidebar/logo-icon.vue +15 -15
  77. package/src/icons/sidebar/logout-icon.vue +13 -13
  78. package/src/icons/sidebar/modules-icon.vue +16 -16
  79. package/src/icons/sidebar/notifications-icon.vue +24 -24
  80. package/src/icons/sidebar/order-icon.vue +44 -44
  81. package/src/icons/sidebar/pass-icon.vue +38 -38
  82. package/src/icons/sidebar/positions-icon.vue +42 -42
  83. package/src/icons/sidebar/preorder-icon.vue +19 -19
  84. package/src/icons/sidebar/projects-icon.vue +31 -31
  85. package/src/icons/sidebar/repair-object-icon.vue +18 -18
  86. package/src/icons/sidebar/repairs-icon.vue +20 -20
  87. package/src/icons/sidebar/roles-icon.vue +26 -26
  88. package/src/icons/sidebar/status-history-icon.vue +24 -24
  89. package/src/icons/sidebar/tasks-icon.vue +28 -28
  90. package/src/icons/sidebar/tasks_tasks-icon.vue +39 -39
  91. package/src/icons/sidebar/tasks_today-icon.vue +27 -27
  92. package/src/icons/sidebar/teams-icon.vue +32 -32
  93. package/src/icons/sidebar/user-icon.vue +18 -18
  94. package/src/icons/sidebar/users-icon.vue +46 -46
  95. package/src/icons/sidebar/videosources-icon.vue +19 -19
  96. package/src/icons/sidebar/videowall-icon.vue +13 -13
  97. package/src/icons/sidebar/videozones-icon.vue +21 -21
  98. package/src/icons/sidebar/warehouses-icon.vue +43 -43
  99. package/src/icons/sidebar/workshop-icon.vue +100 -100
  100. package/src/icons/sidebar/workzones-icon.vue +22 -22
  101. package/src/icons/task/attention-icon.vue +13 -13
  102. package/src/icons/task/clock-icon.vue +10 -10
  103. package/src/icons/task/delete-icon.vue +10 -10
  104. package/src/icons/task/fire-icon.vue +16 -16
  105. package/src/index.ts +0 -1
  106. package/src/main.ts +28 -28
  107. package/src/quasar-user-options.ts +17 -17
  108. package/src/router/index.ts +10 -10
  109. package/src/shared/styles/general.css +124 -124
  110. package/src/shims-vue.d.ts +5 -5
  111. package/src/utils/confirm.ts +12 -12
  112. package/src/utils/faceApiHelper.ts +137 -137
  113. package/src/utils/helpers.ts +59 -59
  114. package/src/utils/notification.ts +9 -9
  115. package/dist/types/api/services/ComentsServise.d.ts +0 -10
  116. package/dist/types/api/services/PhotoService.d.ts +0 -53
  117. package/dist/types/api/types/Api_Users.d.ts +0 -43
  118. package/dist/types/api/types/Api_Video.d.ts +0 -87
  119. package/src/api/types/Api_Video.ts +0 -90
@@ -1,148 +1,148 @@
1
- <template>
2
- <q-expansion-item
3
- v-if="item.items"
4
- :model-value="isRouteActive(item) && expand"
5
- header-class="text-purple"
6
- :class="[$style['menu-item'], { 'expansion-item-active': isRouteActive(item) && minify }]"
7
- :data-test="`sidebar-expansion-item-${(item.name || 'unnamed').toLowerCase().replace(/\s+/g, '-')}`"
8
- @update:model-value="expand = $event"
9
- >
10
- <template #header>
11
- <q-tooltip
12
- v-if="minify"
13
- :delay="100"
14
- transition-show="jump-right"
15
- transition-hide="jump-left"
16
- anchor="center right"
17
- self="center left"
18
- >
19
- {{ item.label }}
20
- </q-tooltip>
21
-
22
- <q-item-section v-if="item.icon" avatar>
23
- <app-icon :name="item.icon" color="white" size="24px" />
24
- </q-item-section>
25
-
26
- <q-item-section :class="$style['menu-item__label']">{{ item.label }}</q-item-section>
27
- </template>
28
-
29
- <sidebar-menu-item
30
- v-for="(subItem, index) in item.items"
31
- :key="index"
32
- :item="subItem"
33
- :main="main"
34
- :is-route-active="isRouteActive"
35
- />
36
- </q-expansion-item>
37
- <q-item
38
- v-else-if="item.name !== 'sign-out'"
39
- v-ripple
40
- :class="$style['menu-item']"
41
- :active="isRouteActive(item)"
42
- active-class="menu-active"
43
- clickable
44
- :to="main ? item.to : ''"
45
- :data-test="`sidebar-item-${item.name.toLowerCase().replace(/\s+/g, '-')}`"
46
- @click="goToRoute(item.to)"
47
- >
48
- <q-item-section v-if="item.icon" avatar>
49
- <app-icon :name="item.icon" color="white" size="24px" />
50
- </q-item-section>
51
-
52
- <q-item-section :class="$style['menu-item__label']">
53
- <p>{{ item.label }}</p>
54
- </q-item-section>
55
- <q-tooltip
56
- :delay="100"
57
- transition-show="jump-right"
58
- transition-hide="jump-left"
59
- anchor="center right"
60
- self="center left"
61
- >
62
- {{ item.label }}
63
- </q-tooltip>
64
- </q-item>
65
- </template>
66
-
67
- <script lang="ts" setup>
68
- import { defineProps, ref } from 'vue'
69
- import AppIcon from '@/common/app-icon/AppIcon.vue'
70
-
71
- interface Props {
72
- minify?: boolean
73
- main: boolean
74
- isRouteActive: (item: any) => boolean
75
- item: {
76
- name: string
77
- label: string
78
- icon: string
79
- href: string
80
- to: string
81
- items: any[]
82
- }
83
- }
84
-
85
- const emits = defineEmits(['goToRoute'])
86
- const props = defineProps<Props>()
87
-
88
- const expand = ref(true)
89
-
90
- const goToRoute = (to: string) => {
91
- if (!props.main) window.location.href = to
92
- }
93
- </script>
94
-
95
- <style lang="scss" scoped>
96
- .expansion-item-active {
97
- background: rgba(243, 249, 253, 0.1);
98
- border-radius: 10px;
99
- }
100
- </style>
101
-
102
- <style lang="scss" module>
103
- .menu-item {
104
- margin: 0 8px;
105
- &:global(.menu-active) {
106
- border-radius: 10px;
107
- background: rgba(243, 249, 253, 0.1);
108
- }
109
- :global(.q-item__section--avatar) {
110
- min-width: 24px;
111
- padding-right: 14px;
112
- }
113
-
114
- :global(.q-expansion-item__content .q-item) {
115
- padding-left: 20px;
116
- }
117
- :global(.q-item__section--main ~ .q-item__section--side) {
118
- color: white;
119
- }
120
- &__label {
121
- color: #fff;
122
- font-family: 'Nunito Sans', sans-serif;
123
- font-size: 16px;
124
- font-weight: 300;
125
- p {
126
- display: inline-block;
127
- max-width: 160px;
128
- white-space: nowrap;
129
- overflow: hidden;
130
- text-overflow: ellipsis;
131
- margin: 0;
132
- }
133
- }
134
- }
135
-
136
- @media (max-width: 1440px) {
137
- .menu-item {
138
- margin: 0 4px;
139
- :global(.q-item__section--avatar) {
140
- min-width: 24px;
141
- padding-right: 10px;
142
- }
143
- &__label {
144
- font-size: 14px;
145
- }
146
- }
147
- }
148
- </style>
1
+ <template>
2
+ <q-expansion-item
3
+ v-if="item.items"
4
+ :model-value="isRouteActive(item) && expand"
5
+ header-class="text-purple"
6
+ :class="[$style['menu-item'], { 'expansion-item-active': isRouteActive(item) && minify }]"
7
+ :data-test="`sidebar-expansion-item-${(item.name || 'unnamed').toLowerCase().replace(/\s+/g, '-')}`"
8
+ @update:model-value="expand = $event"
9
+ >
10
+ <template #header>
11
+ <q-tooltip
12
+ v-if="minify"
13
+ :delay="100"
14
+ transition-show="jump-right"
15
+ transition-hide="jump-left"
16
+ anchor="center right"
17
+ self="center left"
18
+ >
19
+ {{ item.label }}
20
+ </q-tooltip>
21
+
22
+ <q-item-section v-if="item.icon" avatar>
23
+ <app-icon :name="item.icon" color="white" size="24px" />
24
+ </q-item-section>
25
+
26
+ <q-item-section :class="$style['menu-item__label']">{{ item.label }}</q-item-section>
27
+ </template>
28
+
29
+ <sidebar-menu-item
30
+ v-for="(subItem, index) in item.items"
31
+ :key="index"
32
+ :item="subItem"
33
+ :main="main"
34
+ :is-route-active="isRouteActive"
35
+ />
36
+ </q-expansion-item>
37
+ <q-item
38
+ v-else-if="item.name !== 'sign-out'"
39
+ v-ripple
40
+ :class="$style['menu-item']"
41
+ :active="isRouteActive(item)"
42
+ active-class="menu-active"
43
+ clickable
44
+ :to="main ? item.to : ''"
45
+ :data-test="`sidebar-item-${item.name.toLowerCase().replace(/\s+/g, '-')}`"
46
+ @click="goToRoute(item.to)"
47
+ >
48
+ <q-item-section v-if="item.icon" avatar>
49
+ <app-icon :name="item.icon" color="white" size="24px" />
50
+ </q-item-section>
51
+
52
+ <q-item-section :class="$style['menu-item__label']">
53
+ <p>{{ item.label }}</p>
54
+ </q-item-section>
55
+ <q-tooltip
56
+ :delay="100"
57
+ transition-show="jump-right"
58
+ transition-hide="jump-left"
59
+ anchor="center right"
60
+ self="center left"
61
+ >
62
+ {{ item.label }}
63
+ </q-tooltip>
64
+ </q-item>
65
+ </template>
66
+
67
+ <script lang="ts" setup>
68
+ import { defineProps, ref } from 'vue'
69
+ import AppIcon from '@/common/app-icon/AppIcon.vue'
70
+
71
+ interface Props {
72
+ minify?: boolean
73
+ main: boolean
74
+ isRouteActive: (item: any) => boolean
75
+ item: {
76
+ name: string
77
+ label: string
78
+ icon: string
79
+ href: string
80
+ to: string
81
+ items: any[]
82
+ }
83
+ }
84
+
85
+ const emits = defineEmits(['goToRoute'])
86
+ const props = defineProps<Props>()
87
+
88
+ const expand = ref(true)
89
+
90
+ const goToRoute = (to: string) => {
91
+ if (!props.main) window.location.href = to
92
+ }
93
+ </script>
94
+
95
+ <style lang="scss" scoped>
96
+ .expansion-item-active {
97
+ background: rgba(243, 249, 253, 0.1);
98
+ border-radius: 10px;
99
+ }
100
+ </style>
101
+
102
+ <style lang="scss" module>
103
+ .menu-item {
104
+ margin: 0 8px;
105
+ &:global(.menu-active) {
106
+ border-radius: 10px;
107
+ background: rgba(243, 249, 253, 0.1);
108
+ }
109
+ :global(.q-item__section--avatar) {
110
+ min-width: 24px;
111
+ padding-right: 14px;
112
+ }
113
+
114
+ :global(.q-expansion-item__content .q-item) {
115
+ padding-left: 20px;
116
+ }
117
+ :global(.q-item__section--main ~ .q-item__section--side) {
118
+ color: white;
119
+ }
120
+ &__label {
121
+ color: #fff;
122
+ font-family: 'Nunito Sans', sans-serif;
123
+ font-size: 16px;
124
+ font-weight: 300;
125
+ p {
126
+ display: inline-block;
127
+ max-width: 160px;
128
+ white-space: nowrap;
129
+ overflow: hidden;
130
+ text-overflow: ellipsis;
131
+ margin: 0;
132
+ }
133
+ }
134
+ }
135
+
136
+ @media (max-width: 1440px) {
137
+ .menu-item {
138
+ margin: 0 4px;
139
+ :global(.q-item__section--avatar) {
140
+ min-width: 24px;
141
+ padding-right: 10px;
142
+ }
143
+ &__label {
144
+ font-size: 14px;
145
+ }
146
+ }
147
+ }
148
+ </style>
@@ -24,7 +24,7 @@
24
24
 
25
25
  <template #body-cell-index="cellProps">
26
26
  <q-td :props="cellProps" class="text-center" :class="{ 'q-td--no-hover': noHover }">
27
- {{ getIndex(cellProps.rowIndex) }}
27
+ {{ cellProps.rowIndex + 1 + (meta.value.currentPage - 1) * meta.value.perPage }}
28
28
  </q-td>
29
29
  </template>
30
30
 
@@ -157,7 +157,6 @@ const props = defineProps<{
157
157
  meta: Ref<{ currentPage: number; perPage: number }>
158
158
  enableMultiSelect?: boolean
159
159
  noHover?: boolean
160
- hidePagination?: boolean
161
160
  selectedRows: any[]
162
161
  actions?: string[]
163
162
  slots?: Slots
@@ -193,12 +192,6 @@ const rowClick = (e: Event, row: Record<string, any>) => {
193
192
  emit('row-click', row)
194
193
  }
195
194
 
196
- const getIndex = (rowIndex: number) => {
197
- if (props.hidePagination) return rowIndex + 1
198
-
199
- return rowIndex + 1 + (props.meta.value.currentPage - 1) * props.meta.value.perPage
200
- }
201
-
202
195
  watch(
203
196
  () => props.selectedRows,
204
197
  val => {
@@ -17,7 +17,6 @@
17
17
  :selected-rows="props.selectedRows"
18
18
  :no-hover="noHover"
19
19
  :slots="slots"
20
- :hide-pagination="hidePagination"
21
20
  v-on="props.tableEvents"
22
21
  @update:selectedRows="rows => emit('update:selectedRows', rows)"
23
22
  >
@@ -31,7 +30,7 @@
31
30
  </div>
32
31
 
33
32
  <app-table-pagination
34
- v-if="!hidePagination && props.currentPage && props.totalPages"
33
+ v-if="!hidePagination"
35
34
  :model-value="props.currentPage"
36
35
  :total-pages="props.totalPages"
37
36
  @page-change="props.onPageChange"
@@ -49,10 +48,10 @@ import { defineProps, defineEmits, useSlots, computed } from 'vue'
49
48
  const props = defineProps<{
50
49
  search?: string
51
50
  loading?: boolean
52
- currentPage?: number
53
- totalPages?: number
51
+ currentPage: number
52
+ totalPages: number
54
53
  tableProps: any
55
- tableEvents?: any
54
+ tableEvents: any
56
55
  onSearch?: (val: string) => void
57
56
  onPageChange?: (page: number) => void
58
57
  hideSearch?: boolean
@@ -1,152 +1,152 @@
1
- <template>
2
- <div class="table-pagination">
3
- <button class="arrow-button" :disabled="modelValue <= 1" @click="prevPage">
4
- <q-icon name="mdi-chevron-left" :color="modelValue <= 1 ? 'grey-4' : 'primary'" />
5
- </button>
6
-
7
- <div class="pages">
8
- <button
9
- v-for="page in pageArray"
10
- :key="page"
11
- class="page-button"
12
- :class="{ selected: page === modelValue, ellipsis: page === '...' }"
13
- :disabled="page === modelValue || page === '...'"
14
- @click="changePage(page)"
15
- >
16
- {{ page }}
17
- </button>
18
- </div>
19
-
20
- <button class="arrow-button" :disabled="modelValue >= totalPages" @click="nextPage">
21
- <q-icon name="mdi-chevron-right" :color="modelValue >= totalPages ? 'grey-4' : 'primary'" />
22
- </button>
23
- </div>
24
- </template>
25
- <script setup lang="ts">
26
- import { computed } from 'vue'
27
- import { defineProps, defineEmits } from 'vue'
28
-
29
- const props = defineProps<{
30
- modelValue: number
31
- totalPages: number
32
- }>()
33
-
34
- const emit = defineEmits<{
35
- (e: 'update:modelValue', value: number): void
36
- (e: 'page-change', value: number): void
37
- }>()
38
-
39
- const currentPage = computed({
40
- get: () => props.modelValue,
41
- set: (val: number) => {
42
- emit('update:modelValue', val)
43
- emit('page-change', val)
44
- },
45
- })
46
-
47
- const pageArray = computed(() => {
48
- const maxPagesToShow = 5
49
- let startPage = 1
50
- let endPage = props.totalPages
51
- const pages: (number | string)[] = []
52
-
53
- if (props.totalPages <= maxPagesToShow) {
54
- for (let i = 1; i <= props.totalPages; i++) pages.push(i)
55
- } else {
56
- if (props.modelValue <= 3) {
57
- startPage = 1
58
- endPage = maxPagesToShow - 1
59
- } else if (props.modelValue + 2 >= props.totalPages) {
60
- startPage = props.totalPages - (maxPagesToShow - 2)
61
- endPage = props.totalPages
62
- } else {
63
- startPage = props.modelValue - 2
64
- endPage = props.modelValue + 2
65
- }
66
-
67
- for (let i = startPage; i <= endPage; i++) pages.push(i)
68
-
69
- if (startPage > 1) {
70
- pages.unshift('...')
71
- pages.unshift(1)
72
- }
73
- if (endPage < props.totalPages) {
74
- pages.push('...')
75
- pages.push(props.totalPages)
76
- }
77
- }
78
-
79
- return pages
80
- })
81
-
82
- function changePage(page: number | string) {
83
- if (typeof page === 'number' && page !== props.modelValue) {
84
- currentPage.value = page
85
- }
86
- }
87
-
88
- function prevPage() {
89
- if (props.modelValue > 1) currentPage.value = props.modelValue - 1
90
- }
91
-
92
- function nextPage() {
93
- if (props.modelValue < props.totalPages) currentPage.value = props.modelValue + 1
94
- }
95
- </script>
96
-
97
- <style scoped lang="scss">
98
- .pages {
99
- display: flex;
100
- gap: 20px;
101
- }
102
- .table-pagination {
103
- display: flex;
104
- justify-content: start;
105
- align-items: center;
106
- gap: 20px;
107
- flex-wrap: wrap;
108
-
109
- .arrow-button,
110
- .page-button {
111
- display: flex;
112
- justify-content: center;
113
- align-items: center;
114
- width: 34px;
115
- height: 34px;
116
- border-radius: 4px;
117
- background: white;
118
- border: none;
119
- cursor: pointer;
120
- font-weight: 500;
121
- color: #3f8cff;
122
- font-size: 16px;
123
- transition: all 0.2s ease;
124
- }
125
-
126
- .page-button.selected {
127
- background: #3f8cff;
128
- color: white;
129
- cursor: not-allowed;
130
- }
131
-
132
- .page-button.ellipsis {
133
- cursor: default;
134
- color: #aab2c8;
135
- font-weight: 400;
136
- }
137
-
138
- .arrow-button:disabled {
139
- cursor: not-allowed;
140
- background: white;
141
- }
142
-
143
- @media (max-width: 768px) {
144
- .arrow-button,
145
- .page-button {
146
- width: 32px;
147
- height: 32px;
148
- font-size: 14px;
149
- }
150
- }
151
- }
152
- </style>
1
+ <template>
2
+ <div class="table-pagination">
3
+ <button class="arrow-button" :disabled="modelValue <= 1" @click="prevPage">
4
+ <q-icon name="mdi-chevron-left" :color="modelValue <= 1 ? 'grey-4' : 'primary'" />
5
+ </button>
6
+
7
+ <div class="pages">
8
+ <button
9
+ v-for="page in pageArray"
10
+ :key="page"
11
+ class="page-button"
12
+ :class="{ selected: page === modelValue, ellipsis: page === '...' }"
13
+ :disabled="page === modelValue || page === '...'"
14
+ @click="changePage(page)"
15
+ >
16
+ {{ page }}
17
+ </button>
18
+ </div>
19
+
20
+ <button class="arrow-button" :disabled="modelValue >= totalPages" @click="nextPage">
21
+ <q-icon name="mdi-chevron-right" :color="modelValue >= totalPages ? 'grey-4' : 'primary'" />
22
+ </button>
23
+ </div>
24
+ </template>
25
+ <script setup lang="ts">
26
+ import { computed } from 'vue'
27
+ import { defineProps, defineEmits } from 'vue'
28
+
29
+ const props = defineProps<{
30
+ modelValue: number
31
+ totalPages: number
32
+ }>()
33
+
34
+ const emit = defineEmits<{
35
+ (e: 'update:modelValue', value: number): void
36
+ (e: 'page-change', value: number): void
37
+ }>()
38
+
39
+ const currentPage = computed({
40
+ get: () => props.modelValue,
41
+ set: (val: number) => {
42
+ emit('update:modelValue', val)
43
+ emit('page-change', val)
44
+ },
45
+ })
46
+
47
+ const pageArray = computed(() => {
48
+ const maxPagesToShow = 5
49
+ let startPage = 1
50
+ let endPage = props.totalPages
51
+ const pages: (number | string)[] = []
52
+
53
+ if (props.totalPages <= maxPagesToShow) {
54
+ for (let i = 1; i <= props.totalPages; i++) pages.push(i)
55
+ } else {
56
+ if (props.modelValue <= 3) {
57
+ startPage = 1
58
+ endPage = maxPagesToShow - 1
59
+ } else if (props.modelValue + 2 >= props.totalPages) {
60
+ startPage = props.totalPages - (maxPagesToShow - 2)
61
+ endPage = props.totalPages
62
+ } else {
63
+ startPage = props.modelValue - 2
64
+ endPage = props.modelValue + 2
65
+ }
66
+
67
+ for (let i = startPage; i <= endPage; i++) pages.push(i)
68
+
69
+ if (startPage > 1) {
70
+ pages.unshift('...')
71
+ pages.unshift(1)
72
+ }
73
+ if (endPage < props.totalPages) {
74
+ pages.push('...')
75
+ pages.push(props.totalPages)
76
+ }
77
+ }
78
+
79
+ return pages
80
+ })
81
+
82
+ function changePage(page: number | string) {
83
+ if (typeof page === 'number' && page !== props.modelValue) {
84
+ currentPage.value = page
85
+ }
86
+ }
87
+
88
+ function prevPage() {
89
+ if (props.modelValue > 1) currentPage.value = props.modelValue - 1
90
+ }
91
+
92
+ function nextPage() {
93
+ if (props.modelValue < props.totalPages) currentPage.value = props.modelValue + 1
94
+ }
95
+ </script>
96
+
97
+ <style scoped lang="scss">
98
+ .pages {
99
+ display: flex;
100
+ gap: 20px;
101
+ }
102
+ .table-pagination {
103
+ display: flex;
104
+ justify-content: start;
105
+ align-items: center;
106
+ gap: 20px;
107
+ flex-wrap: wrap;
108
+
109
+ .arrow-button,
110
+ .page-button {
111
+ display: flex;
112
+ justify-content: center;
113
+ align-items: center;
114
+ width: 34px;
115
+ height: 34px;
116
+ border-radius: 4px;
117
+ background: white;
118
+ border: none;
119
+ cursor: pointer;
120
+ font-weight: 500;
121
+ color: #3f8cff;
122
+ font-size: 16px;
123
+ transition: all 0.2s ease;
124
+ }
125
+
126
+ .page-button.selected {
127
+ background: #3f8cff;
128
+ color: white;
129
+ cursor: not-allowed;
130
+ }
131
+
132
+ .page-button.ellipsis {
133
+ cursor: default;
134
+ color: #aab2c8;
135
+ font-weight: 400;
136
+ }
137
+
138
+ .arrow-button:disabled {
139
+ cursor: not-allowed;
140
+ background: white;
141
+ }
142
+
143
+ @media (max-width: 768px) {
144
+ .arrow-button,
145
+ .page-button {
146
+ width: 32px;
147
+ height: 32px;
148
+ font-size: 14px;
149
+ }
150
+ }
151
+ }
152
+ </style>