flameresttable 1.0.119 → 1.0.123
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 +17 -17
- package/src/App.vue +16 -17
- package/src/Table/Columns.ts +66 -44
- package/src/Table/FlameTable.ts +90 -17
- package/src/Table/Paginator.vue +37 -19
- package/src/Table/Table.vue +23 -247
- package/src/Table/TableFilters.vue +89 -19
- package/src/Table/TableOpts.ts +5 -0
- package/src/Table/TablePopup.vue +324 -0
- package/src/Table/TableSelectorItem.ts +22 -2
- package/src/Table/TableTasksPanel.vue +1 -1
- package/src/components/default/Modal.vue +37 -32
- package/src/components/editor/EditorPopup.vue +1 -1
- package/src/components/editor/LinkDialog.vue +1 -1
- package/src/components/editor/SelectImage.vue +1 -1
- package/src/main.ts +4 -6
- package/src/pages/Home.vue +5 -6
- package/src/pages/SubRow.vue +28 -24
package/package.json
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flameresttable",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.123",
|
|
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
|
-
"
|
|
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.
|
|
16
|
-
"flamerest": "
|
|
17
|
-
"lodash-es": "^4.17.
|
|
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.
|
|
21
|
+
"sass": "^1.97.3",
|
|
22
22
|
"vue": "^3.5.13",
|
|
23
|
-
"vue-router": "^4.
|
|
23
|
+
"vue-router": "^4.6.4",
|
|
24
24
|
"vue-select": "^4.0.0-beta.6",
|
|
25
|
-
"vuetify": "^3.8
|
|
26
|
-
"vuetify-pro-tiptap": "^2.
|
|
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.
|
|
32
|
+
"@types/node": "^22.19.7",
|
|
33
33
|
"@types/vue-select": "^3.16.8",
|
|
34
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
35
|
-
"@typescript-eslint/parser": "^8.
|
|
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.
|
|
40
|
-
"eslint": "^9.
|
|
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.
|
|
45
|
-
"typescript": "^5.
|
|
46
|
-
"vite": "^6.
|
|
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
|
|
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>
|
package/src/Table/Columns.ts
CHANGED
|
@@ -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
|
-
|
|
129
|
+
allowNull: true,
|
|
128
130
|
|
|
129
131
|
/**
|
|
130
|
-
*
|
|
132
|
+
* Заголовок для пустого значения
|
|
131
133
|
*/
|
|
132
|
-
|
|
134
|
+
nullTitle: "—",
|
|
133
135
|
|
|
134
136
|
/**
|
|
135
|
-
*
|
|
137
|
+
* Кастомизация вывода отдельного элемента
|
|
138
|
+
* @param row
|
|
139
|
+
* @param col
|
|
140
|
+
* @returns
|
|
136
141
|
*/
|
|
137
|
-
|
|
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
|
-
|
|
173
|
-
|
|
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
|
-
|
|
247
|
-
|
|
254
|
+
allowNull: false,
|
|
255
|
+
|
|
248
256
|
/**
|
|
249
|
-
*
|
|
257
|
+
* Заголовок для пустого значения
|
|
250
258
|
*/
|
|
251
|
-
|
|
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
|
-
|
|
375
|
-
|
|
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?:
|
|
525
|
+
values?: ITableSelectorItem[],
|
|
504
526
|
|
|
505
|
-
// Загрузчик значений - используется всегда и перекрывает values
|
|
506
|
-
loader?: (column: Column) => Array<any> | null,
|
|
507
|
-
|
|
508
527
|
/**
|
|
509
|
-
*
|
|
528
|
+
* Разрешить выбирать пустое значение (null)
|
|
510
529
|
*/
|
|
511
|
-
|
|
512
|
-
|
|
530
|
+
allowNull?: boolean,
|
|
531
|
+
|
|
513
532
|
/**
|
|
514
|
-
*
|
|
533
|
+
* Заголовок для пустого значения
|
|
515
534
|
*/
|
|
516
|
-
|
|
535
|
+
nullTitle?: string,
|
|
536
|
+
|
|
537
|
+
// Загрузчик значений - используется всегда и перекрывает values
|
|
538
|
+
loader?: (column: Column) => ITableSelectorItem[] | null,
|
|
517
539
|
|
|
518
540
|
},
|
|
519
541
|
|
package/src/Table/FlameTable.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
593
|
+
public getColumnsForUpdate(mode: "add" | "edit") {
|
|
521
594
|
const res: { [key: string]: string } = {};
|
|
522
595
|
for (const key in this.columns) {
|
|
523
596
|
|
package/src/Table/Paginator.vue
CHANGED
|
@@ -1,25 +1,43 @@
|
|
|
1
|
-
<template
|
|
2
|
-
nav
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
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'
|