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.
@@ -35,7 +35,7 @@
35
35
  <div v-for="column in Table.columns" v-show="column.Table.isShow" :key="'cheader_' + column.name"
36
36
  :class="' defaultHeader ' + column.Table.classesHeader"
37
37
  class=" table-cell align-middle border-r border-r-slate-100 px-2"
38
- :style="(column.Table.width === null ? '' : 'width:' + column.Table.width + 'px')">
38
+ :style="(column.Table.width === null ? '' : `width: ${column.Table.width}px; min-width: ${column.Table.width}px`)">
39
39
  <span>{{ column.Table.titleCustom !== null ? (column as any).Table.titleCustom(column) : column.Table.title
40
40
  !==
41
41
  '' ?
@@ -76,7 +76,7 @@
76
76
  :class="' defaultCell ' + column.Table.classes"
77
77
  class="table-cell align-middle border-r border-r-slate-100 px-2 last:border-r-0 last:pr-0"
78
78
  @click="columnClick(row, column)"
79
- :style="(column.Table.width === null ? '' : 'width:' + column.Table.width + 'px')">
79
+ :style="(column.Table.width === null ? '' : `width: ${column.Table.width}px; min-width: ${column.Table.width}px`)">
80
80
 
81
81
  <span v-if="!column.Table.isRawValue">{{
82
82
  column.Table.value(row, column) }}</span>
@@ -119,137 +119,18 @@
119
119
  <Paginator class="mt-3" :table="Table" v-if="opts.Pagination.type === 'pages'" />
120
120
 
121
121
  <!-- МОДАЛКА ДОБАВЛЕНИЯ-->
122
- <ModalVue ref="FlameTableModal">
123
- <template #default>
124
- <div class="bg-white rounded-xl" :class="'w-full desktop:w-[777px]'">
125
-
126
- <!-- СЛОТ ЗАГОЛОВКА ПОПАПА -->
127
- <slot name="header" />
128
-
129
- <!-- КОЛОНКИ ТАБЛИЦЫ-->
130
- <label v-for="column in ColumnNames" v-show="Table.columns[column].Popup.isShow" :key="'c' + column"
131
- class="flex items-stretch justify-center w-full my-1 ml-2">
132
-
133
- <!-- Заголовок -->
134
- <div class="text-left px-2 py-1 bg-slate-100" style="width: 110px">
135
- <div>{{ Table.columns[column].Popup.title !== '' ? Table.columns[column].Popup.title === '' :
136
- Table.columns[column].title !== '' ? Table.columns[column].title : Table.columns[column].name }}</div>
137
- <div class="text-xs text-slate-400">{{ Table.columns[column].Popup.desc }}</div>
138
- </div>
139
-
140
- <div v-if="Table.columns[column].Popup.popupType === 'string'"
141
- class="flex-1 mr-5 w-full flex flex-col self-stretch">
142
- <input v-model="Table.columns[column].Popup.model"
143
- class="h-full self-stretch py-1 px-2 w-full outline-none border border-slate-100" type="text"
144
- :disabled="!Table.columns[column].Popup.isEnabled"
145
- :placeholder="Table.columns[column].Popup.placeholder !== '' ? Table.columns[column].Popup.placeholder : Table.columns[column].title !== '' ? Table.columns[column].title : Table.columns[column].name">
146
- </div>
147
-
148
- <div v-if="Table.columns[column].Popup.popupType === 'date'"
149
- class="flex-1 mr-5 w-full flex flex-col self-stretch">
150
- <Datepicker v-model="Table.columns[column].Popup.model" :auto-apply="true" />
151
- </div>
152
-
153
- <div v-if="Table.columns[column].Popup.popupType === 'selector'"
154
- class="flex-1 mr-5 w-full flex flex-col self-stretch"><select v-model="Table.columns[column].Popup.model"
155
- class="h-full self-stretch py-1 px-2 w-full outline-none border border-slate-100">
156
- <option v-for="(selectorVal, selectorKey) in Table.columns[column].Popup.Selector.values"
157
- :key="'sel_' + selectorKey"
158
- :value="getSelector(Table.columns[column].Popup.Selector.values, selectorKey, selectorVal)[1]">{{
159
- getSelector(Table.columns[column].Popup.Selector.values, selectorKey, selectorVal)[0] }}</option>
160
- </select>
161
- </div>
162
-
163
- <!-- КАРТИНКА -->
164
- <div v-if="Table.columns[column].Popup.popupType === 'image'"
165
- class="flex-1 border border-slate-100 mr-5 w-full flex self-stretch">
166
- <div class="flex-1">
167
- <img class="max-w-[200px]" :src="fileGetData(Table.columns[column].Popup.model, true)">
168
- </div>
169
-
170
- <!-- Загрузчик-->
171
- <label :for="'cfile_' + column">
172
-
173
- <svg class="my-auto cursor-pointer" width="24" height="24" viewbox="0 0 24 24" fill="none"
174
- xmlns="http://www.w3.org/2000/svg">
175
- <path fill-rule="evenodd" clip-rule="evenodd"
176
- d="M11.4697 2.46967C11.7626 2.17678 12.2374 2.17678 12.5303 2.46967L17.0303 6.96967C17.3232 7.26256 17.3232 7.73744 17.0303 8.03033C16.7374 8.32322 16.2626 8.32322 15.9697 8.03033L12.75 4.81066L12.75 16.5C12.75 16.9142 12.4142 17.25 12 17.25C11.5858 17.25 11.25 16.9142 11.25 16.5L11.25 4.81066L8.03033 8.03033C7.73744 8.32322 7.26256 8.32322 6.96967 8.03033C6.67678 7.73744 6.67678 7.26256 6.96967 6.96967L11.4697 2.46967ZM3 15.75C3.41421 15.75 3.75 16.0858 3.75 16.5V18.75C3.75 19.5784 4.42157 20.25 5.25 20.25H18.75C19.5784 20.25 20.25 19.5784 20.25 18.75V16.5C20.25 16.0858 20.5858 15.75 21 15.75C21.4142 15.75 21.75 16.0858 21.75 16.5V18.75C21.75 20.4069 20.4069 21.75 18.75 21.75H5.25C3.59315 21.75 2.25 20.4069 2.25 18.75V16.5C2.25 16.0858 2.58579 15.75 3 15.75Z"
177
- fill="#0F172A" />
178
- </svg>
179
-
180
- <input class="hidden" :name="'cfile_' + column" type="file" @change="fileUpdated(column, $event)">
181
-
182
- </label>
183
- </div>
184
-
185
- <!-- ФАЙЛЫ -->
186
- <div v-if="Table.columns[column].Popup.popupType === 'file'"
187
- class="flex-1 border border-slate-100 mr-5 w-full flex self-stretch">
188
-
189
- <div class="flex-1">
190
- {{ getFileName(Table.columns[column].Popup.model) }}
191
- </div>
192
-
193
- <!-- Загрузчик-->
194
- <label :for="'cfile_' + column">
195
-
196
- <svg class="my-auto cursor-pointer" width="24" height="24" viewbox="0 0 24 24" fill="none"
197
- xmlns="http://www.w3.org/2000/svg">
198
- <path fill-rule="evenodd" clip-rule="evenodd"
199
- d="M11.4697 2.46967C11.7626 2.17678 12.2374 2.17678 12.5303 2.46967L17.0303 6.96967C17.3232 7.26256 17.3232 7.73744 17.0303 8.03033C16.7374 8.32322 16.2626 8.32322 15.9697 8.03033L12.75 4.81066L12.75 16.5C12.75 16.9142 12.4142 17.25 12 17.25C11.5858 17.25 11.25 16.9142 11.25 16.5L11.25 4.81066L8.03033 8.03033C7.73744 8.32322 7.26256 8.32322 6.96967 8.03033C6.67678 7.73744 6.67678 7.26256 6.96967 6.96967L11.4697 2.46967ZM3 15.75C3.41421 15.75 3.75 16.0858 3.75 16.5V18.75C3.75 19.5784 4.42157 20.25 5.25 20.25H18.75C19.5784 20.25 20.25 19.5784 20.25 18.75V16.5C20.25 16.0858 20.5858 15.75 21 15.75C21.4142 15.75 21.75 16.0858 21.75 16.5V18.75C21.75 20.4069 20.4069 21.75 18.75 21.75H5.25C3.59315 21.75 2.25 20.4069 2.25 18.75V16.5C2.25 16.0858 2.58579 15.75 3 15.75Z"
200
- fill="#0F172A" />
201
- </svg>
202
-
203
- <input class="hidden" :name="'cfile_' + column" type="file" @change="fileUpdated(column, $event)">
204
-
205
- </label>
206
- </div>
207
-
208
- <div class="flex-1 mr-5 w-full flex flex-col self-stretch"
209
- v-if="Table.columns[column].Popup.popupType === 'text'">
210
- <EditorPopup :value="Table.columns[column].Popup.model" @save="PopupTextSaved" :column="column" :title="Table.columns[column].Popup.title ?? column">
211
-
212
- </EditorPopup>
213
- </div>
214
-
215
- <!-- ОШИБКИ СОХРАНЕНИЯ -->
216
- <div class="mx-2 my-1 text-red-600" v-if="(typeof popupErrors[column]!== 'undefined')">{{ popupErrors[column]
217
- }}</div>
218
-
219
- </label>
220
-
221
- <div class="text-red-600 mx-2 my-1" v-if="(typeof popupErrors['globalErrorStack']!== 'undefined')">{{
222
- popupErrors['globalErrorStack'] }}</div>
223
-
224
- <!-- КНОПКИ СОХРАНЕНИЯ-->
225
- <div class="flex w-full mt-3 mb-2 justify-between items-center">
226
- <div v-show="$props.opts?.Add.can"
227
- class="cursor-pointer ml-2 text-lg py-2 text-white block bg-gray-400 font-medium rounded px-4 text-center hover:bg-blue-700 focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-900"
228
- @click="Table.OpenedRow = null; FlameTableModal?.close()">
229
- Отмена
230
- </div>
231
- <div
232
- class="cursor-pointer ml-2 text-lg py-2 mr-3 text-white block bg-blue-600 font-medium rounded px-4 text-center hover:bg-blue-700 focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-900"
233
- @click=" SaveTable()">
234
- {{ Table.mode === 'add' ? 'Добавить' : 'Сохранить' }}
235
- </div>
236
- </div>
237
- </div>
238
- </template>
239
- </ModalVue>
122
+ <TablePopup :table="Table" ref="FlameTablePopup"></TablePopup>
240
123
  </template>
241
124
 
242
125
  <script lang="ts">
243
- 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, { Rows } from "flamerest"
126
+ import { onMounted, reactive, ref } from 'vue'; import type { Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST, { Rows } from "flamerest"
244
127
 
245
128
  // Иконки
246
- import { computed } from '@vue/reactivity';
247
129
  import { Column, IColumn } from './Columns';
248
130
  import { defineComponent } from 'vue';
249
131
 
250
132
  import TableOpts from './TableOpts';
251
133
  import FlameTable from './FlameTable';
252
- import ModalVue from './../components/default/Modal.vue';
253
134
  import Paginator from './Paginator.vue';
254
135
  import TableFilters from './TableFilters.vue';
255
136
  type Class<T> = new (...args: any[]) => T;
@@ -257,12 +138,11 @@ type Class<T> = new (...args: any[]) => T;
257
138
  import Datepicker from '@vuepic/vue-datepicker';
258
139
  import '@vuepic/vue-datepicker/dist/main.css';
259
140
  import { isNumber } from 'lodash';
260
- import 'vue-select/dist/vue-select.css';
261
141
  import TableTasksPanel from './TableTasksPanel.vue';
262
- import EditorPopup from '../components/editor/EditorPopup.vue';
142
+ import TablePopup, { type TablePopupInstance } from './TablePopup.vue';
263
143
 
264
144
  export default defineComponent({
265
- components: { ModalVue, Paginator, Datepicker, TableFilters, TableTasksPanel, EditorPopup },
145
+ components: { Paginator, Datepicker, TableFilters, TableTasksPanel, TablePopup },
266
146
  props: {
267
147
  rows: {
268
148
  default: [] as Array<any>,
@@ -282,81 +162,33 @@ export default defineComponent({
282
162
 
283
163
  const Table = (new FlameTable(props.model as any, props.opts)) as FlameTable<any>;
284
164
 
285
- const FlameTableModal = ref<InstanceType<typeof ModalVue>>();
165
+ const FlameTablePopup = ref<TablePopupInstance>();
286
166
 
287
- const ColumnNames = Object.keys(Table.model as any);
288
-
289
- // Ошибки сохранения
290
- let popupErrors: {[key:string]:string} = reactive({});
291
-
292
- // Добавляем событие скролла
167
+ const ColumnNames = Object.keys(Table.columns);
293
168
 
294
169
  /**
295
170
  * Добавление новой записи
296
171
  */
297
172
  const add = () => {
298
- Table.mode = 'add';
299
-
300
- // Установим все поля в пустые значения
301
- ColumnNames.forEach(key => {
302
- Table.columns[key].Popup.model = "";
303
- Table.columns[key].Popup.fileModel = null;
304
- })
305
-
306
- FlameTableModal.value?.show();
173
+ FlameTablePopup.value?.showAdd();
307
174
  }
308
175
 
309
176
  /**
310
177
  * Редактирование записи
311
178
  */
312
179
  const edit = async (row: any) => {
313
- Table.mode = 'edit';
314
-
315
- await Table.opts.Popup.load(row, Table);
316
-
317
- // Установим все поля в фактические значения из таблицы
318
- ColumnNames.forEach(key => {
319
-
320
- if (Table.columns[key].Popup.popupType === 'file' || Table.columns[key].Popup.popupType === 'image')
321
- Table.columns[key].Popup.fileModel = row[key];
322
-
323
- Table.columns[key].Popup.model = row[key];
324
- })
325
-
326
- Table.OpenedRow = row;
327
-
328
- FlameTableModal.value?.show();
329
- }
330
-
331
- const SaveTable = async () => {
332
-
333
- try {
334
- if (Table.mode === 'add') {
335
- await Table.add()
336
- }
337
- else {
338
- await Table.save()
339
- }
340
- popupErrors={};
341
- }
342
- catch (ex: any) {
343
-
344
- for (const keyx in popupErrors)
345
- delete popupErrors[keyx];
346
-
347
- if(typeof ex.stack !== 'undefined' && typeof ex.message !== 'undefined') {
348
- popupErrors.globalErrorStack = ex.message;
349
- }
350
- else
351
- Object.assign(popupErrors, ex);
352
-
353
- return;
180
+ // TODO OPTIMIZATION NOTE: Логирование для отладки открытия попапа редактирования
181
+ console.log('[FlameTable Debug] edit method called', {
182
+ popupInstance: FlameTablePopup.value,
183
+ rowData: row,
184
+ hasShowEdit: typeof FlameTablePopup.value?.showEdit === 'function'
185
+ });
186
+
187
+ if (!FlameTablePopup.value) {
188
+ console.error('[FlameTable Debug] FlameTablePopup ref is NULL. Убедитесь, что TablePopup примонтирован и ref="FlameTablePopup" указан верно.');
354
189
  }
355
190
 
356
- FlameTableModal.value?.close();
357
-
358
- Table.OpenedRow = null;
359
-
191
+ FlameTablePopup.value?.showEdit(row);
360
192
  }
361
193
 
362
194
  const deleteRow = async (row: any) => {
@@ -368,7 +200,7 @@ export default defineComponent({
368
200
  const fileUpdated = async (column: any, ev: any) => {
369
201
  // если файлы не выбраны
370
202
  if (ev.target.files.length === 0) return;
371
-
203
+
372
204
  Table.columns[column].Popup.model = ev;
373
205
  const prepared = (await REST.prepare({ myParam: ev })).myParam;
374
206
  Table.columns[column].Popup.model = prepared
@@ -386,17 +218,9 @@ export default defineComponent({
386
218
  })
387
219
  }*/
388
220
 
389
- const getSelector = (arr: any, selectorKey: any, selectorVal: any) => {
390
- if (Array.isArray(arr)) return [selectorVal, selectorVal];
391
- // TODO: тут загрузка объекта должна быть
392
- // сейчас объект перерабатывается где-то в map и теряет ключи и значения
393
- if (typeof arr === 'object' && arr !== null) return [selectorKey, selectorVal]
394
- return [selectorKey, selectorVal]
395
- }
396
-
397
221
  // Автозагрузка первичной версии
398
222
  if (props.opts.autoload === true) {
399
- /**
223
+ /**
400
224
  const tModel = (props.model as any)
401
225
  tModel.all().then((r: any) => {
402
226
  Table.load(r);
@@ -425,33 +249,19 @@ export default defineComponent({
425
249
  return Table.RowsParams[row[primaryKey]].collapsed
426
250
  }
427
251
 
428
- const PopupTextSaved = (txt: string, column: string)=> {
429
- Table.columns[column].Popup.model = txt;
430
- }
431
-
432
252
  const MainTableElement = ref();
433
253
 
434
-
435
-
436
254
  return {
437
255
  Table,
438
- FlameTableModal,
256
+ FlameTablePopup,
439
257
  ColumnNames,
440
258
  add,
441
259
  edit,
442
- SaveTable,
443
260
  deleteRow,
444
- fileUpdated,
445
- getSelector,
446
261
  primaryKey,
447
262
  columnClick,
448
263
  isRowCollapsed,
449
264
  MainTableElement,
450
-
451
- // TODO: проверить
452
- popupErrors,
453
-
454
- PopupTextSaved
455
265
  }
456
266
 
457
267
  },
@@ -487,40 +297,6 @@ export default defineComponent({
487
297
 
488
298
  },
489
299
 
490
- fileGetData(thisArr: any, getSource: boolean = true) {
491
-
492
-
493
-
494
- if (thisArr instanceof Array && thisArr.length > 0) {
495
- // Получить строку с исходником
496
- if (getSource) {
497
- if (typeof thisArr[0].data === 'string') return thisArr[0].data;
498
- if (typeof thisArr[0].file === 'string') return REST.SERVER + "/" + thisArr[0].file;
499
- return "";
500
- }
501
- // Получить данные целиком
502
- return thisArr[0]
503
- }
504
-
505
- return null
506
-
507
- },
508
-
509
- getFileName(fileModel: any) {
510
- const res = this.fileGetData(fileModel, false)
511
- if (res?.name) {
512
- if (typeof res.name === 'string' && res.name !== '') {
513
- return res.name;
514
- }
515
- else {
516
- if (res.file) {
517
- const splitted = res.file.split('.');
518
- return 'File.' + splitted[splitted.length - 1];
519
- }
520
- }
521
- }
522
- },
523
-
524
300
  CheckboxSelectionChanged(event: any, all: boolean = false) {
525
301
  if (all === true)
526
302
  for (const key in this.Table.RowsParams) {
@@ -529,7 +305,7 @@ export default defineComponent({
529
305
 
530
306
  this.Table.getSelectedRows();
531
307
 
532
- // ТУТ СОБЫТИЕ ОБ ИЗМЕНЕНИИ ПРИХОДИТ В ИСТАНС РАНЬШЕ САМОГО ИЗМЕНЕНИЯ МАССИВА
308
+ // ТУТ СОБЫТИЕ ОБ ИЗМЕНЕНИИ ПРИХОДИТ В ИСТАНС РАНШЬЕ САМОГО ИЗМЕНЕНИЯ МАССИВА
533
309
 
534
310
  }
535
311
  },
@@ -1,60 +1,98 @@
1
1
  <template>
2
2
  <div>
3
- <v-row dense>
4
- <v-col v-for="(col) in props.columns" :key="col.name" v-show="col.Filter.isShow"
3
+ <v-row no-gutters align="stretch">
4
+ <v-col v-for="(col) in sortedFilters" :key="col.name" v-show="col.Filter.isShow"
5
5
  :class="(col.Filter.classes + (col.Filter.type === 'selector' && col.Filter.selector.mode === 'horizontal' ? ' w-full ' : ''))"
6
- class="py-1" cols="12" md="auto">
6
+ class="pa-1" cols="12" md="auto" :style="getTextFilterStyle(col)">
7
7
 
8
8
  <div class="fc flex-col h-100">
9
9
  <div :class="col.Filter.titleClasses" class="text-xs text-slate-400 mb-1">{{ col.Filter.title ??
10
10
  col.title.toUpperCase() ?? col.name }}</div>
11
+ <v-spacer v-if="mdAndUp"></v-spacer>
11
12
 
12
13
  <!-- ТЕКСТ -->
13
- <div class="mobile:w-full"
14
+ <div class="w-full"
14
15
  v-if="col.Filter.type === 'text' || col.Filter.type === 'fixed' || col.Filter.type === 'fulltext'">
15
16
  <v-text-field density="compact" :label="col.title ?? col.name" type="text"
16
- @update:model-value="update()" v-model="col.Filter.valueString" hide-details />
17
+ @update:model-value="update()" v-model="col.Filter.valueString" hide-details variant="filled" />
17
18
  </div>
18
19
 
19
20
  <!-- ЦИФРЫ -->
20
21
  <v-row dense v-if="col.Filter.type === 'number'">
21
- <v-col cols="6">
22
- <v-text-field density="compact" :label="'От ' + (col.title ?? col.name)" type="number"
23
- @update:model-value="update()" v-model="col.Filter.valueRangeNumbers.from" hide-details />
22
+ <v-col :style="getNumberFilterStyle(col)">
23
+ <v-text-field class="w-full" density="compact" :label="'От ' + (col.title ?? col.name)" type="number"
24
+ @update:model-value="update()" v-model="col.Filter.valueRangeNumbers.from" hide-details variant="filled" />
24
25
  </v-col>
25
- <v-col cols="6">
26
- <v-text-field density="compact" :label="'До ' + (col.title ?? col.name)" type="number"
27
- @update:model-value="update()" v-model="col.Filter.valueRangeNumbers.to" hide-details />
26
+ <v-col :style="getNumberFilterStyle(col)">
27
+ <v-text-field class="w-full" density="compact" :label="'До ' + (col.title ?? col.name)" type="number"
28
+ @update:model-value="update()" v-model="col.Filter.valueRangeNumbers.to" hide-details variant="filled" />
28
29
  </v-col>
29
30
  </v-row>
30
31
 
31
32
 
32
33
  <!-- СЕЛЕКТОРЫ -->
33
- <div v-if="col.Filter.type === 'selector' && col.Filter.selector.mode !== 'horizontal'" class="mobile:w-full">
34
+ <div v-if="col.Filter.type === 'selector' && col.Filter.selector.mode !== 'horizontal'" class="w-full">
34
35
  <!-- Multi-select -->
35
36
  <v-select v-if="col.Filter.selector.multiselect" v-model="col.Filter.valueRange"
36
37
  :label="col.title ?? col.name" :items="col.Selector.values" item-title="title" item-value="id"
37
38
  class="mobile:w-full min-w-[200px]" density="compact" @update:model-value="update()" hide-details multiple
38
- chips closable-chips>
39
+ chips closable-chips variant="filled">
40
+ <template #item="{ props, item }">
41
+ <v-list-item v-bind="props">
42
+ <template #prepend v-if="item.raw.prepend">
43
+ <span :class="item.raw.prependClasses">{{ item.raw.prepend }}</span>
44
+ </template>
45
+ <template #append v-if="item.raw.append">
46
+ <span :class="item.raw.appendClasses">{{ item.raw.append }}</span>
47
+ </template>
48
+ </v-list-item>
49
+ </template>
50
+ <template #chip="{ props, item }">
51
+ <v-chip v-bind="props">
52
+ <span v-if="item.raw.prepend" :class="item.raw.prependClasses" class="mr-1">{{ item.raw.prepend
53
+ }}</span>
54
+ <span>{{ item.raw.title }}</span>
55
+ <span v-if="item.raw.append" :class="item.raw.appendClasses" class="ml-1">{{ item.raw.append }}</span>
56
+ </v-chip>
57
+ </template>
39
58
  </v-select>
40
59
  <!-- Single-select -->
41
60
  <v-select v-else v-model="col.Filter.valueString" :label="col.title ?? col.name"
42
61
  :items="col.Selector.values" item-title="title" item-value="id" class="mobile:w-full min-w-[200px]"
43
- density="compact" @update:model-value="update()" hide-details>
62
+ density="compact" @update:model-value="update()" hide-details variant="filled">
63
+ <template #item="{ props, item }">
64
+ <v-list-item v-bind="props">
65
+ <template #prepend v-if="item.raw.prepend">
66
+ <span :class="item.raw.prependClasses">{{ item.raw.prepend }}</span>
67
+ </template>
68
+ <template #append v-if="item.raw.append">
69
+ <span :class="item.raw.appendClasses">{{ item.raw.append }}</span>
70
+ </template>
71
+ </v-list-item>
72
+ </template>
73
+ <template #selection="{ item }">
74
+ <div class="d-flex align-center w-100">
75
+ <span v-if="item.raw.prepend" :class="item.raw.prependClasses" class="mr-1">{{ item.raw.prepend
76
+ }}</span>
77
+ <span class="font-normal">{{ item.raw.title }}</span>
78
+ <v-spacer></v-spacer>
79
+ <span v-if="item.raw.append" :class="item.raw.appendClasses">{{ item.raw.append }}</span>
80
+ </div>
81
+ </template>
44
82
  </v-select>
45
83
  </div>
46
84
 
47
85
  <!-- ГОРИЗОНТАЛЬНЫЕ -->
48
86
  <div v-if="col.Filter.type === 'selector' && col.Filter.selector.mode === 'horizontal'"
49
87
  class="w-full text-center">
50
- <div v-for="item in col.Selector.values" :key="item.id" class="px-2 py-1 mx-1 inline-block rounded-[30px]"
88
+ <div v-for="item in col.Selector.values" :key="item.id?.toString()" class="px-2 py-1 mx-1 inline-block rounded-[30px]"
51
89
  :style="'background-color:' + (item.color ?? 'rgb(148,163,184)')">
52
90
  {{ item.title }}
53
91
  </div>
54
92
  </div>
55
93
 
56
94
  <!-- ДАТА -->
57
- <div class="mobile:w-full" v-if="col.Filter.type === 'date' || col.Filter.type === 'daterange'">
95
+ <div class="w-full" v-if="col.Filter.type === 'date' || col.Filter.type === 'daterange'">
58
96
  <Datepicker class="mobile:w-full" v-if="col.Filter.type === 'date'" v-model="col.Filter.valueString"
59
97
  :auto-apply="true" :enable-time-picker="false" @update:model-value="update()" :teleport="true"></Datepicker>
60
98
  <Datepicker class="mobile:w-full" v-if="col.Filter.type === 'daterange'" v-model="col.Filter.valueRange"
@@ -70,7 +108,7 @@
70
108
  </template>
71
109
 
72
110
  <script setup lang="ts" generic="T">
73
- import { onMounted, reactive, ref, defineProps } from '@vue/runtime-core'; import { computed, type Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
111
+ import { onMounted, reactive, ref } from 'vue'; import { computed, type Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
74
112
 
75
113
  // Иконки
76
114
  import { XCircleIcon } from '@icons/24/solid'
@@ -81,9 +119,12 @@ import FlameTable from './FlameTable';
81
119
  import Datepicker from '@vuepic/vue-datepicker';
82
120
  // import vSelect from 'vue-select' // Больше не используется
83
121
  import ITableSelectorItem from './TableSelectorItem';
122
+ import { useDisplay } from 'vuetify';
123
+ import debounce from 'lodash-es/debounce';
84
124
 
85
125
  // Глобальное хранилище и роуты
86
126
  const store = storeFile(), router = useRouter(), route = useRoute();
127
+ const { mdAndUp } = useDisplay();
87
128
 
88
129
  // Локальное состояние компонента
89
130
  const state = reactive({
@@ -96,13 +137,42 @@ const props = defineProps<{
96
137
  table: FlameTable<any>,
97
138
  }>()
98
139
 
140
+ const sortedFilters = computed(() => {
141
+ return Object.values(props.columns).sort((a, b) => (a.Filter.position ?? 999) - (b.Filter.position ?? 999));
142
+ });
143
+
99
144
  // Примонтировано
100
145
  onMounted(async () => {
101
146
 
102
147
  })
103
148
 
149
+ const getTextFilterStyle = (col: Column) => {
150
+ if (!mdAndUp.value || col.Filter.type === 'number') return '';
151
+
152
+ let width = col.Filter.width;
153
+ if (width === null || typeof width === 'undefined') {
154
+ if (['text', 'fixed', 'fulltext'].includes(col.Filter.type)) {
155
+ width = 250;
156
+ }
157
+ }
104
158
 
105
- const update = () => {
159
+ if (width) {
160
+ return `min-width: ${width}px; max-width: ${width}px;`;
161
+ }
162
+ return '';
163
+ }
164
+
165
+ const getNumberFilterStyle = (col: Column) => {
166
+ if (!mdAndUp.value) return '';
167
+ let width = col.Filter.width;
168
+ if (width === null || typeof width === 'undefined') {
169
+ width = 85;
170
+ }
171
+ return `min-width: ${width}px; max-width: ${width}px;`;
172
+ }
173
+
174
+
175
+ const update = debounce(() => {
106
176
 
107
177
  // селектор преобразуем к v-model
108
178
  /*
@@ -115,7 +185,7 @@ const update = () => {
115
185
 
116
186
  props.table.update({}, null, 'filters');
117
187
 
118
- }
188
+ }, 300);
119
189
 
120
190
 
121
191
 
@@ -76,6 +76,11 @@ export default class TableOpts {
76
76
  // TODO: ЕСТЬ ОШИБКА???
77
77
  if (this.columnsOpts[ColumnName] === undefined) this.columnsOpts[ColumnName] = new Column as any
78
78
  merge(this.columnsOpts[ColumnName], mergingOpts)
79
+
80
+ // Если у нас есть ссылка на таблицу, переинициализируем селектор для этой колонки
81
+ if ((this as any)._tableInstance) {
82
+ (this as any)._tableInstance.reinitSelectorForColumn(ColumnName)
83
+ }
79
84
  }
80
85
 
81
86
  /**