flameresttable 1.0.119 → 1.0.122

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/package.json CHANGED
@@ -1,49 +1,49 @@
1
1
  {
2
2
  "name": "flameresttable",
3
- "version": "1.0.119",
3
+ "version": "1.0.122",
4
4
  "main": "./src/plugin.ts",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/FlameArt/resttable.git"
8
8
  },
9
9
  "scripts": {
10
- "push and publish": "npm --no-git-tag-version version patch && npm publish",
10
+ "publish": "npm --no-git-tag-version version patch && npm publish",
11
11
  "install/update": "npm i && npm update --save",
12
12
  "dev": "vite"
13
13
  },
14
14
  "dependencies": {
15
- "@vuepic/vue-datepicker": "^11.0.2",
16
- "flamerest": "^1.0.125",
17
- "lodash-es": "^4.17.21",
15
+ "@vuepic/vue-datepicker": "^11.0.3",
16
+ "flamerest": "file:../REST JS",
17
+ "lodash-es": "^4.17.23",
18
18
  "pinia": "^2.3.1",
19
19
  "pug": "github:FlameArt/pug-tailwind-only",
20
20
  "pug-lexer": "github:FlameArt/pug-lexer-only",
21
- "sass": "^1.89.2",
21
+ "sass": "^1.97.3",
22
22
  "vue": "^3.5.13",
23
- "vue-router": "^4.5.1",
23
+ "vue-router": "^4.6.4",
24
24
  "vue-select": "^4.0.0-beta.6",
25
- "vuetify": "^3.8.12",
26
- "vuetify-pro-tiptap": "^2.6.0"
25
+ "vuetify": "^3.11.8",
26
+ "vuetify-pro-tiptap": "^2.8.2"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@heroicons/vue": "^2.2.0",
30
30
  "@types/lodash-es": "^4.17.12",
31
31
  "@types/lodash.merge": "^4.6.9",
32
- "@types/node": "^22.16.0",
32
+ "@types/node": "^22.19.7",
33
33
  "@types/vue-select": "^3.16.8",
34
- "@typescript-eslint/eslint-plugin": "^8.35.1",
35
- "@typescript-eslint/parser": "^8.35.1",
34
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
35
+ "@typescript-eslint/parser": "^8.54.0",
36
36
  "@vitejs/plugin-vue": "^5.2.4",
37
37
  "@volar/vue-language-plugin-pug": "^1.6.5",
38
38
  "@vue/compiler-sfc": "^3.5.13",
39
- "autoprefixer": "^10.4.21",
40
- "eslint": "^9.30.1",
39
+ "autoprefixer": "^10.4.24",
40
+ "eslint": "^9.39.2",
41
41
  "eslint-plugin-vue": "^9.33.0",
42
42
  "postcss": "^8.5.6",
43
43
  "postcss-import": "^16.1.1",
44
- "tailwindcss": "^3.4.17",
45
- "typescript": "^5.8.3",
46
- "vite": "^6.3.5",
44
+ "tailwindcss": "^3.4.19",
45
+ "typescript": "^5.9.3",
46
+ "vite": "^6.4.1",
47
47
  "vue-tsc": "^2.2.12",
48
48
  "vue-universal-modal": "^1.1.4"
49
49
  },
package/src/App.vue CHANGED
@@ -1,5 +1,20 @@
1
+ <template>
2
+ <v-app>
3
+ <v-main>
4
+ <Table :model="Model" :opts="opts">
5
+ <template v-slot:RowSubSlot="params">
6
+ <!-- {{ params.row.name + ' LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG line' }} -->
7
+ <!-- <SubRow /> -->
8
+ <div class="flex items-center justify-center w-full mx-auto">AAA</div>
9
+ </template>
10
+ <template #otherButtons></template>
11
+ </Table>
12
+ </v-main>
13
+ </v-app>
14
+ </template>
15
+
1
16
  <script setup lang="ts">
2
- import { onMounted, reactive, defineProps } from '@vue/runtime-core'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
17
+ import { onMounted, reactive } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
3
18
  import Table from './Table/Table.vue';
4
19
  import { ref } from 'vue';
5
20
  import Model from '@models/Testtable';
@@ -55,26 +70,10 @@ opts.LoadParams.sort = ['-dt1'];
55
70
 
56
71
  </script>
57
72
 
58
- <template lang="pug">
59
- v-app
60
- v-main
61
- Table(:model="Model", :opts="opts")
62
- template(v-slot:RowSubSlot="params")
63
- //| {{ params.row.name + ' LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG LOOOONG line' }}
64
- //SubRow
65
- div(class="flex items-center justify-center w-full mx-auto") AAA
66
- template(#otherButtons)
67
- </template>
68
-
69
73
  <style>
70
74
  #app {
71
75
  font-family: Avenir, Helvetica, Arial, sans-serif;
72
76
  -webkit-font-smoothing: antialiased;
73
77
  -moz-osx-font-smoothing: grayscale;
74
78
  }
75
-
76
- /* Цвет стандартного фона модального окна */
77
- .vue-universal-modal {
78
- background-color: rgba(0, 0, 0, 0.15) !important;
79
- }
80
79
  </style>
@@ -30,6 +30,11 @@ export class Column implements IColumn {
30
30
  */
31
31
  isShow: true,
32
32
 
33
+ /**
34
+ * Позиция колонки для сортировки
35
+ */
36
+ position: 999,
37
+
33
38
  /**
34
39
  * Заголовок столбца в таблице
35
40
  */
@@ -119,22 +124,22 @@ export class Column implements IColumn {
119
124
  values: [] as ITableSelectorItem[],
120
125
 
121
126
  /**
122
- * Кастомизация вывода отдельного элемента
123
- * @param row
124
- * @param col
125
- * @returns
127
+ * Разрешить выбирать пустое значение (null)
126
128
  */
127
- value: (row: any, col: string) => row[col],
129
+ allowNull: true,
128
130
 
129
131
  /**
130
- * Добавить в начало списка
132
+ * Заголовок для пустого значения
131
133
  */
132
- prepend: [] as ITableSelectorItem[],
134
+ nullTitle: "—",
133
135
 
134
136
  /**
135
- * Добавить в конец списка
137
+ * Кастомизация вывода отдельного элемента
138
+ * @param row
139
+ * @param col
140
+ * @returns
136
141
  */
137
- append: [] as ITableSelectorItem[]
142
+ value: (row: any, col: string) => row[col],
138
143
 
139
144
  };
140
145
 
@@ -168,9 +173,15 @@ export class Column implements IColumn {
168
173
  // Минимальное число символов в строке фильтра, после которого будет произведён запрос
169
174
  filterMinSymbolsRequest: 0,
170
175
 
171
- // Позиция фильтра по порядку
172
- filterRow: 0,
173
- filterColumn: 0,
176
+ /**
177
+ * Позиция фильтра в общей очереди
178
+ */
179
+ position: 999,
180
+
181
+ /**
182
+ * Ширина фильтра в пикселях
183
+ */
184
+ width: null as number | null,
174
185
 
175
186
  /**
176
187
  * CSS-классы,
@@ -192,7 +203,7 @@ export class Column implements IColumn {
192
203
  public Popup = {
193
204
 
194
205
  // Тут привязка к реальным данным
195
- model: "",
206
+ model: "" as any,
196
207
 
197
208
  // Тут модель для файлов
198
209
  fileModel: null as any,
@@ -235,20 +246,20 @@ export class Column implements IColumn {
235
246
  Selector: {
236
247
 
237
248
  // Значения селектора - готовые
238
- values: [],
239
-
240
- // Загрузчик значений - используется всегда и перекрывает values
241
- loader: (column: Column) => null,
249
+ values: [] as ITableSelectorItem[],
242
250
 
243
251
  /**
244
- * Добавить в начало списка
252
+ * Разрешить выбирать пустое значение (null)
245
253
  */
246
- prepend: [] as ITableSelectorItem[],
247
-
254
+ allowNull: false,
255
+
248
256
  /**
249
- * Добавить в конец списка
257
+ * Заголовок для пустого значения
250
258
  */
251
- append: [] as ITableSelectorItem[]
259
+ nullTitle: "—",
260
+
261
+ // Загрузчик значений - используется всегда и перекрывает values
262
+ loader: (column: Column) => null as ITableSelectorItem[] | null,
252
263
 
253
264
  },
254
265
 
@@ -338,6 +349,11 @@ export interface IColumn {
338
349
  width?: number | null,
339
350
  isSelected?: boolean,
340
351
 
352
+ /**
353
+ * Позиция колонки для сортировки
354
+ */
355
+ position?: number,
356
+
341
357
  /**
342
358
  * Кастомный заголовок
343
359
  * @param row
@@ -370,9 +386,15 @@ export interface IColumn {
370
386
  // Минимальное число символов в строке фильтра, после которого будет произведён запрос
371
387
  filterMinSymbolsRequest?: number,
372
388
 
373
- // Позиция фильтра по порядку
374
- filterRow?: number,
375
- filterColumn?: number,
389
+ /**
390
+ * Позиция фильтра в общей очереди
391
+ */
392
+ position?: number,
393
+
394
+ /**
395
+ * Ширина фильтра в пикселях
396
+ */
397
+ width?: number | null,
376
398
 
377
399
  /**
378
400
  * Список значений под выбор
@@ -425,6 +447,16 @@ export interface IColumn {
425
447
  */
426
448
  values?: ITableSelectorItem[],
427
449
 
450
+ /**
451
+ * Разрешить выбирать пустое значение (null)
452
+ */
453
+ allowNull?: boolean,
454
+
455
+ /**
456
+ * Заголовок для пустого значения
457
+ */
458
+ nullTitle?: string,
459
+
428
460
 
429
461
  /**
430
462
  * Модель, с которой выгружать значения
@@ -440,16 +472,6 @@ export interface IColumn {
440
472
  */
441
473
  value?: (row: any, col: string) => any,
442
474
 
443
- /**
444
- * Добавить в начало списка
445
- */
446
- prepend?: ITableSelectorItem[],
447
-
448
- /**
449
- * Добавить в конец списка
450
- */
451
- append?: ITableSelectorItem[]
452
-
453
475
  },
454
476
 
455
477
 
@@ -500,20 +522,20 @@ export interface IColumn {
500
522
  Selector?: {
501
523
 
502
524
  // Значения селектора - готовые
503
- values?: Array<any>,
525
+ values?: ITableSelectorItem[],
504
526
 
505
- // Загрузчик значений - используется всегда и перекрывает values
506
- loader?: (column: Column) => Array<any> | null,
507
-
508
527
  /**
509
- * Добавить в начало списка
528
+ * Разрешить выбирать пустое значение (null)
510
529
  */
511
- prepend?: ITableSelectorItem[],
512
-
530
+ allowNull?: boolean,
531
+
513
532
  /**
514
- * Добавить в конец списка
533
+ * Заголовок для пустого значения
515
534
  */
516
- append?: ITableSelectorItem[]
535
+ nullTitle?: string,
536
+
537
+ // Загрузчик значений - используется всегда и перекрывает values
538
+ loader?: (column: Column) => ITableSelectorItem[] | null,
517
539
 
518
540
  },
519
541
 
@@ -94,11 +94,12 @@ export default class FlameTable<T> {
94
94
  // удалим так же дубликаты
95
95
  // TODO: as any
96
96
  const allColumns = [...Object.keys(this.model as any), ...Object.keys(opts.columnsOpts)].filter((value, index, self) => self.indexOf(value) === index)
97
+ const tempColumns: Column[] = [];
97
98
 
98
99
  // генерим полноценные колонки из описания модели
99
100
  for (const key of allColumns) {
100
101
  const newCol = new Column;
101
- this.columns[key] = newCol;
102
+ //this.columns[key] = newCol;
102
103
 
103
104
  // Выключаем из редактирования все праймари ключи
104
105
  if ((this.model as any).constructor['primaryKeys'].includes(key))
@@ -113,15 +114,26 @@ export default class FlameTable<T> {
113
114
  // Загружаем селекторы один раз за страницу
114
115
  if (newCol.Popup.popupType === 'selector' && newCol.Popup.Selector.loader !== null) {
115
116
  newCol.Popup.Selector.values = reactive(newCol.Popup.Selector.loader(newCol) ?? newCol.Popup.Selector.values)
116
- if (newCol.Popup.Selector.prepend.length > 0) (newCol.Popup.Selector.values as any[]).unshift(...newCol.Popup.Selector.prepend);
117
- if (newCol.Popup.Selector.append.length > 0) (newCol.Popup.Selector.values as any[]).push(...newCol.Popup.Selector.append);
118
117
  }
119
118
 
119
+ tempColumns.push(newCol);
120
+
121
+ }
122
+
123
+ // Сортируем колонки по их позиции
124
+ tempColumns.sort((a, b) => (a.Table.position ?? 999) - (b.Table.position ?? 999));
125
+
126
+ // Заполняем итоговый объект в правильном порядке
127
+ for (const col of tempColumns) {
128
+ this.columns[col.name] = col;
120
129
  }
121
130
 
122
131
  // Мержим параметры с базовой версией
123
132
  merge(this.opts, opts);
124
133
 
134
+ // Устанавливаем ссылку на таблицу для обратного вызова
135
+ (this.opts as any)._tableInstance = this;
136
+
125
137
  // TODO: Сортируем колонки, если указан порядок
126
138
  //this.columns
127
139
 
@@ -130,7 +142,8 @@ export default class FlameTable<T> {
130
142
  // Предзагружаем колонки
131
143
  this.preloadRelated()
132
144
 
133
-
145
+ // Инициализируем null-элементы для селекторов
146
+ this.initSelectorNulls()
134
147
 
135
148
  }
136
149
 
@@ -199,6 +212,7 @@ export default class FlameTable<T> {
199
212
  col.Selector.values.push(...col.Selector.loader(r.data))
200
213
  }
201
214
 
215
+
202
216
  // Преобразования не найдено, попытка автоматически определить параметры
203
217
  if (col.Selector.loader === null) {
204
218
  const rows = r.data;
@@ -228,13 +242,59 @@ export default class FlameTable<T> {
228
242
  // Начинаем процесс предзагрузки
229
243
  await Promise.allSettled(allPreloadPromises);
230
244
 
231
- // Добавляем prepend/append
245
+ }
246
+
247
+ /**
248
+ * Переинициализирует селекторы для указанной колонки
249
+ */
250
+ public reinitSelectorForColumn(columnName: string) {
251
+ if (!this.columns[columnName]) return
252
+
253
+ const col = this.columns[columnName]
254
+
255
+ // Для popup selector
256
+ if (col.Popup.popupType === 'selector') {
257
+ // Удаляем существующий null-элемент если есть
258
+ col.Popup.Selector.values = col.Popup.Selector.values.filter(v => v.id !== null)
259
+
260
+ // Добавляем null только если allowNull = true
261
+ if (col.Popup.Selector.allowNull) {
262
+ col.Popup.Selector.values.unshift({
263
+ id: null,
264
+ title: col.Popup.Selector.nullTitle,
265
+ label: col.Popup.Selector.nullTitle,
266
+ })
267
+ }
268
+ }
269
+
270
+ // Для обычного селектора (фильтры)
271
+ if (col.Selector.allowNull) {
272
+ // Удаляем существующий null-элемент если есть
273
+ col.Selector.values = col.Selector.values.filter(v => v.id !== null)
274
+
275
+ col.Selector.values.unshift({
276
+ id: null,
277
+ title: col.Selector.nullTitle,
278
+ label: col.Selector.nullTitle,
279
+ position: -1
280
+ })
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Инициализирует null-элементы для всех селекторов, где allowNull = true
286
+ */
287
+ private initSelectorNulls() {
232
288
  for (const key in this.columns) {
233
- const col = this.columns[key];
234
- if (col.Selector.prepend.length > 0) col.Selector.values.unshift(...col.Selector.prepend);
235
- if (col.Selector.append.length > 0) col.Selector.values.push(...col.Selector.append);
289
+ this.reinitSelectorForColumn(key)
236
290
  }
291
+ }
237
292
 
293
+ /**
294
+ * Переинициализирует все селекторы (можно вызывать после изменения данных в лоадерах)
295
+ */
296
+ public reinitSelectors() {
297
+ this.initSelectorNulls()
238
298
  }
239
299
 
240
300
  /**
@@ -244,6 +304,17 @@ export default class FlameTable<T> {
244
304
  */
245
305
  public async update(CustomLoadParams: ITableLoadParams = {}, exportFilename: string | null = null, from: 'pager' | 'filters' = 'pager') {
246
306
 
307
+ if (from !== 'pager') {
308
+ this.Pager.page = 1;
309
+
310
+ // Сбрасываем выделенные строки
311
+ for (const key in this.RowsParams) {
312
+ this.RowsParams[key].selected = false;
313
+ }
314
+ this.RowsSelected.splice(0);
315
+
316
+ }
317
+
247
318
  // Дополняем загрузочные параметры фильтрами
248
319
  const filters = merge(this.applyFiltersParams(), CustomLoadParams);
249
320
 
@@ -345,7 +416,7 @@ export default class FlameTable<T> {
345
416
  // Частичное совпадение
346
417
  case 'text':
347
418
 
348
- if (el.valueString.trim() === '') continue;
419
+ if (el.valueString === null || el.valueString.trim() === '') continue;
349
420
  customFilters.where[key] = ['LIKE', key, el.valueString];
350
421
 
351
422
  break;
@@ -353,7 +424,7 @@ export default class FlameTable<T> {
353
424
  // Фуллтекстовый поиск
354
425
  case 'fulltext':
355
426
 
356
- if (el.valueString.trim() === '') continue;
427
+ if (el.valueString === null || el.valueString.trim() === '') continue;
357
428
  customFilters.where[key] = ['FULLTEXT', key, el.valueString];
358
429
  break;
359
430
 
@@ -361,7 +432,7 @@ export default class FlameTable<T> {
361
432
  case 'fixed':
362
433
  case 'date':
363
434
 
364
- if (el.valueString.trim() === '') continue;
435
+ if (el.valueString === null || el.valueString.trim() === '') continue;
365
436
  customFilters.where[key] = el.valueString;
366
437
 
367
438
  break;
@@ -394,8 +465,10 @@ export default class FlameTable<T> {
394
465
  if (el.valueRange.length > 0)
395
466
  customFilters.where[key] = ['IN', key, el.valueRange];
396
467
  }
397
- else
398
- customFilters.where[key] = el.valueString;
468
+ else {
469
+ if (el.valueString !== "")
470
+ customFilters.where[key] = el.valueString;
471
+ }
399
472
 
400
473
  break;
401
474
 
@@ -411,7 +484,7 @@ export default class FlameTable<T> {
411
484
 
412
485
  // Сперва предобработка
413
486
  // TODO: убрать пустой массив т.к. при добавлении он не нужен или прикрепить реактивную модель?
414
- if (await this.opts.Popup.beforeAdd({}, this) === false) { return null; }
487
+ if (await this.opts.Popup.beforeAdd({}, this as any) === false) { return null; }
415
488
 
416
489
  const cols = await (window as any).REST.prepare(this.getColumnsForUpdate('add'), true);
417
490
  const res: SavedObject<T> = await Object.getPrototypeOf(this.model).constructor.create(cols);
@@ -433,7 +506,7 @@ export default class FlameTable<T> {
433
506
  const indexKey = tClass.primaryKeys[0];
434
507
 
435
508
  // Сперва предобработка
436
- if (await this.opts.Popup.beforeEdit(columns, this) === false) { return null; }
509
+ if (await this.opts.Popup.beforeEdit(columns, this as any) === false) { return null; }
437
510
 
438
511
  // Убираем поля, которые не требуют изменения
439
512
  const cleanedCols = this.RemoveUnchagedFields(columns);
@@ -466,7 +539,7 @@ export default class FlameTable<T> {
466
539
 
467
540
  }
468
541
 
469
- private RemoveUnchagedFields(row: { [key: string]: any }) {
542
+ public RemoveUnchagedFields(row: { [key: string]: any }) {
470
543
 
471
544
  // primarykey
472
545
  const pk = (this.model as any).constructor.primaryKeys[0];
@@ -517,7 +590,7 @@ export default class FlameTable<T> {
517
590
 
518
591
  }
519
592
 
520
- private getColumnsForUpdate(mode: "add" | "edit") {
593
+ public getColumnsForUpdate(mode: "add" | "edit") {
521
594
  const res: { [key: string]: string } = {};
522
595
  for (const key in this.columns) {
523
596
 
@@ -1,25 +1,43 @@
1
- <template lang="pug">
2
- nav.relative.z-0.w-full.fc.flex-wrap.rounded-md.-space-x-px(aria-label='Pagination' @click="")
3
- a.relative.inline-flex.items-center.px-2.py-2.rounded-l-md.border.border-gray-300.bg-white.text-sm.font-medium.text-gray-500(@click="goPage(props.table.Pager.page-1)" :class="props.table.Pager.page === 1 ? 'opacity-30' : 'cursor-pointer'" class='hover:bg-gray-50')
4
- span.sr-only Previous
5
- // Heroicon name: solid/chevron-left
6
- svg.h-5.w-5(xmlns='http://www.w3.org/2000/svg' viewbox='0 0 20 20' fill='currentColor' aria-hidden='true')
7
- path(fill-rule='evenodd' d='M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z' clip-rule='evenodd')
8
- // Current: "z-10 bg-indigo-50 border-indigo-500 text-indigo-600", Default: "bg-white border-gray-300 text-gray-500 hover:bg-gray-50"
9
- a.cursor-pointer.bg-white.border-gray-300.text-gray-500.relative.inline-flex.items-center.px-4.py-2.border.text-sm.font-medium(v-for="page in props.table.Pager.count" class="hover:bg-gray-50" :class="props.table.Pager.page === page ? 'selectedPage' : ''" @click="goPage(page)")
10
- | {{ page }}
11
- //span.relative.inline-flex.items-center.px-4.py-2.border.border-gray-300.bg-white.text-sm.font-medium.text-gray-700 ...
12
- a.relative.inline-flex.items-center.px-2.py-2.rounded-r-md.border.border-gray-300.bg-white.text-sm.font-medium.text-gray-500(@click="goPage(props.table.Pager.page+1)" :class="props.table.Pager.page >= props.table.Pager.count ? 'opacity-30' : 'cursor-pointer'" class='hover:bg-gray-50')
13
- span.sr-only Next
14
- // Heroicon name: solid/chevron-right
15
- svg.h-5.w-5(xmlns='http://www.w3.org/2000/svg' viewbox='0 0 20 20' fill='currentColor' aria-hidden='true')
16
- path(fill-rule='evenodd' d='M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z' clip-rule='evenodd')
17
-
18
-
1
+ <template>
2
+ <nav class="relative z-0 w-full fc flex-wrap rounded-md -space-x-px" aria-label="Pagination" @click="">
3
+ <a
4
+ class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
5
+ @click="goPage(props.table.Pager.page - 1)"
6
+ :class="props.table.Pager.page === 1 ? 'opacity-30' : 'cursor-pointer'"
7
+ >
8
+ <span class="sr-only">Previous</span>
9
+ <!-- Heroicon name: solid/chevron-left -->
10
+ <svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
11
+ <path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd"></path>
12
+ </svg>
13
+ </a>
14
+ <!-- Current: "z-10 bg-indigo-50 border-indigo-500 text-indigo-600", Default: "bg-white border-gray-300 text-gray-500 hover:bg-gray-50" -->
15
+ <a
16
+ class="cursor-pointer bg-white border-gray-300 text-gray-500 relative inline-flex items-center px-4 py-2 border text-sm font-medium hover:bg-gray-50"
17
+ v-for="page in props.table.Pager.count"
18
+ :key="page"
19
+ :class="props.table.Pager.page === page ? 'selectedPage' : ''"
20
+ @click="goPage(page)"
21
+ >
22
+ {{ page }}
23
+ </a>
24
+ <!-- <span class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700">...</span> -->
25
+ <a
26
+ class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"
27
+ @click="goPage(props.table.Pager.page + 1)"
28
+ :class="props.table.Pager.page >= props.table.Pager.count ? 'opacity-30' : 'cursor-pointer'"
29
+ >
30
+ <span class="sr-only">Next</span>
31
+ <!-- Heroicon name: solid/chevron-right -->
32
+ <svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
33
+ <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
34
+ </svg>
35
+ </a>
36
+ </nav>
19
37
  </template>
20
38
 
21
39
  <script setup lang="ts">
22
- import { onMounted, reactive, ref, defineProps } from '@vue/runtime-core'; import type { Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
40
+ import { onMounted, reactive, ref } from 'vue'; import type { Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
23
41
 
24
42
  // Иконки
25
43
  import { XCircleIcon } from '@icons/24/solid'