@studio-west/component-sw 0.11.23 → 0.11.24
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/README.md +152 -4
- package/package.json +1 -1
- package/src/components/SwTable.vue +25 -41
package/README.md
CHANGED
|
@@ -698,7 +698,7 @@ model - состояние чекбокса (checked ) булевое знач
|
|
|
698
698
|
|
|
699
699
|
## SwTable
|
|
700
700
|
|
|
701
|
-
|
|
701
|
+
Компонент `SwTable` представляет собой динамическую таблицу с поддержкой сложных заголовков, объединения ячеек и кастомизации содержимого через слоты.
|
|
702
702
|
|
|
703
703
|
### Свойства / Properties
|
|
704
704
|
|
|
@@ -708,15 +708,24 @@ model - состояние чекбокса (checked ) булевое знач
|
|
|
708
708
|
|
|
709
709
|
### Слоты / Slots
|
|
710
710
|
|
|
711
|
-
#`<
|
|
711
|
+
#`<prop>` - именованный слот для конкретной колонки (приоритет выше default). Доступные параметры: `{ row, $index, value }`
|
|
712
|
+
|
|
713
|
+
#default - универсальный слот для всех колонок. Доступные параметры: `{ row, $index, column, value }`
|
|
714
|
+
|
|
715
|
+
- `row` - весь объект строки данных
|
|
716
|
+
- `$index` - индекс строки (начиная с 0)
|
|
717
|
+
- `column` - имя колонки (prop)
|
|
718
|
+
- `value` - значение ячейки
|
|
712
719
|
|
|
713
720
|
### Пример использования / Example Usage
|
|
714
721
|
|
|
722
|
+
#### Базовый пример с вложенными колонками:
|
|
723
|
+
|
|
715
724
|
```html
|
|
716
725
|
<script>
|
|
717
726
|
let table = [
|
|
718
|
-
{city:{label:'Moscow', rowspan:2},district:'Center'},
|
|
719
|
-
{city:'Moscow',district:'MRCR'},
|
|
727
|
+
{city:{label:'Moscow', rowspan:2},district:'Center',speed:{max:80,min:50,midl:70}},
|
|
728
|
+
{city:'Moscow',district:'MRCR',speed:{max:130,min:90,midl:120}},
|
|
720
729
|
]
|
|
721
730
|
</script>
|
|
722
731
|
<sw-table :data="table">
|
|
@@ -724,6 +733,145 @@ model - состояние чекбокса (checked ) булевое знач
|
|
|
724
733
|
<sw-table-column label="City" prop="city"/>
|
|
725
734
|
<sw-table-column label="District" prop="district"/>
|
|
726
735
|
</sw-table-column>
|
|
736
|
+
<sw-table-column label="Speed" prop="speed.midl"/>
|
|
737
|
+
</sw-table>
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
#### Пример с v-for для динамических колонок:
|
|
741
|
+
|
|
742
|
+
```html
|
|
743
|
+
<script setup>
|
|
744
|
+
import { ref } from 'vue'
|
|
745
|
+
|
|
746
|
+
const columns = ref([
|
|
747
|
+
{ prop: 'name', label: 'Имя', width: '150px' },
|
|
748
|
+
{ prop: 'age', label: 'Возраст', width: '100px' },
|
|
749
|
+
{ prop: 'email', label: 'Email', width: '200px' }
|
|
750
|
+
])
|
|
751
|
+
|
|
752
|
+
const tableData = ref([
|
|
753
|
+
{ name: 'Иван', age: 25, email: 'ivan@example.com' },
|
|
754
|
+
{ name: 'Мария', age: 30, email: 'maria@example.com' },
|
|
755
|
+
{ name: 'Петр', age: 35, email: 'petr@example.com' }
|
|
756
|
+
])
|
|
757
|
+
</script>
|
|
758
|
+
|
|
759
|
+
<sw-table :data="tableData">
|
|
760
|
+
<sw-table-column
|
|
761
|
+
v-for="column in columns"
|
|
762
|
+
:key="column.prop"
|
|
763
|
+
:prop="column.prop"
|
|
764
|
+
:label="column.label"
|
|
765
|
+
:width="column.width"
|
|
766
|
+
/>
|
|
767
|
+
</sw-table>
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
#### Пример с кастомизацией через слоты:
|
|
771
|
+
|
|
772
|
+
```html
|
|
773
|
+
<script setup>
|
|
774
|
+
import { ref } from 'vue'
|
|
775
|
+
|
|
776
|
+
const users = ref([
|
|
777
|
+
{ id: 1, name: 'Иван Петров', age: 25, status: 'active', email: 'ivan@example.com' },
|
|
778
|
+
{ id: 2, name: 'Мария Сидорова', age: 30, status: 'inactive', email: 'maria@example.com' },
|
|
779
|
+
{ id: 3, name: 'Петр Иванов', age: 35, status: 'active', email: 'petr@example.com' }
|
|
780
|
+
])
|
|
781
|
+
|
|
782
|
+
const deleteUser = (id) => {
|
|
783
|
+
console.log('Delete user:', id)
|
|
784
|
+
}
|
|
785
|
+
</script>
|
|
786
|
+
|
|
787
|
+
<sw-table :data="users">
|
|
788
|
+
<sw-table-column prop="name" label="Имя" />
|
|
789
|
+
<sw-table-column prop="age" label="Возраст" />
|
|
790
|
+
<sw-table-column prop="status" label="Статус" />
|
|
791
|
+
<sw-table-column prop="actions" label="Действия" width="150px" />
|
|
792
|
+
|
|
793
|
+
<!-- Именованный слот для конкретной колонки -->
|
|
794
|
+
<template #name="{ row, value }">
|
|
795
|
+
<strong>{{ value }}</strong>
|
|
796
|
+
</template>
|
|
797
|
+
|
|
798
|
+
<template #age="{ value }">
|
|
799
|
+
<span style="color: blue">{{ value }} лет</span>
|
|
800
|
+
</template>
|
|
801
|
+
|
|
802
|
+
<template #status="{ value }">
|
|
803
|
+
<span :style="{ color: value === 'active' ? 'green' : 'red' }">
|
|
804
|
+
{{ value === 'active' ? '✓ Активен' : '✗ Неактивен' }}
|
|
805
|
+
</span>
|
|
806
|
+
</template>
|
|
807
|
+
|
|
808
|
+
<!-- Default слот для колонки actions -->
|
|
809
|
+
<template #actions="{ row }">
|
|
810
|
+
<button @click="deleteUser(row.id)">Удалить</button>
|
|
811
|
+
<a :href="`mailto:${row.email}`">Написать</a>
|
|
812
|
+
</template>
|
|
813
|
+
</sw-table>
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
#### Пример с универсальным default слотом:
|
|
817
|
+
|
|
818
|
+
```html
|
|
819
|
+
<script setup>
|
|
820
|
+
import { ref } from 'vue'
|
|
821
|
+
|
|
822
|
+
const products = ref([
|
|
823
|
+
{ name: 'Товар 1', price: 1000, quantity: 5 },
|
|
824
|
+
{ name: 'Товар 2', price: 2500, quantity: 3 },
|
|
825
|
+
{ name: 'Товар 3', price: 750, quantity: 10 }
|
|
826
|
+
])
|
|
827
|
+
</script>
|
|
828
|
+
|
|
829
|
+
<sw-table :data="products">
|
|
830
|
+
<sw-table-column prop="name" label="Название" />
|
|
831
|
+
<sw-table-column prop="price" label="Цена" />
|
|
832
|
+
<sw-table-column prop="quantity" label="Количество" />
|
|
833
|
+
|
|
834
|
+
<!-- Универсальный слот для всех колонок -->
|
|
835
|
+
<template #default="{ row, column, value }">
|
|
836
|
+
<span v-if="column === 'price'" style="font-weight: bold">
|
|
837
|
+
{{ value }} ₽
|
|
838
|
+
</span>
|
|
839
|
+
<span v-else-if="column === 'quantity' && value < 5" style="color: red">
|
|
840
|
+
{{ value }} ⚠️ Мало
|
|
841
|
+
</span>
|
|
842
|
+
<span v-else>{{ value }}</span>
|
|
843
|
+
</template>
|
|
844
|
+
</sw-table>
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
#### Пример с объединением ячеек (rowspan/colspan):
|
|
848
|
+
|
|
849
|
+
```html
|
|
850
|
+
<script setup>
|
|
851
|
+
import { ref } from 'vue'
|
|
852
|
+
|
|
853
|
+
const complexData = ref([
|
|
854
|
+
{
|
|
855
|
+
city: { label: 'Москва', rowspan: 2 },
|
|
856
|
+
district: 'Центр',
|
|
857
|
+
population: 500000
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
city: 'Москва', // будет скрыта из-за rowspan
|
|
861
|
+
district: 'Север',
|
|
862
|
+
population: 300000
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
region: { label: 'Общая статистика', colspan: 2 },
|
|
866
|
+
total: 800000
|
|
867
|
+
}
|
|
868
|
+
])
|
|
869
|
+
</script>
|
|
870
|
+
|
|
871
|
+
<sw-table :data="complexData">
|
|
872
|
+
<sw-table-column label="Город" prop="city" />
|
|
873
|
+
<sw-table-column label="Район" prop="district" />
|
|
874
|
+
<sw-table-column label="Население" prop="population" />
|
|
727
875
|
</sw-table>
|
|
728
876
|
```
|
|
729
877
|
---
|
package/package.json
CHANGED
|
@@ -22,13 +22,29 @@
|
|
|
22
22
|
:rowspan="cell.rowspan"
|
|
23
23
|
:style="cell.style"
|
|
24
24
|
>
|
|
25
|
+
<!-- Сначала пробуем именованный слот по prop -->
|
|
25
26
|
<slot
|
|
27
|
+
v-if="slots[cell.prop]"
|
|
26
28
|
:name="cell.prop"
|
|
27
29
|
:row="cell.originalRow"
|
|
28
30
|
:$index="cell.rowIndex"
|
|
31
|
+
:value="cell.label"
|
|
32
|
+
/>
|
|
33
|
+
<!-- Затем пробуем слот default с данными конкретной ячейки -->
|
|
34
|
+
<slot
|
|
35
|
+
v-else-if="slots.default"
|
|
36
|
+
name="default"
|
|
37
|
+
:row="cell.originalRow"
|
|
38
|
+
:$index="cell.rowIndex"
|
|
39
|
+
:column="cell.prop"
|
|
40
|
+
:value="cell.label"
|
|
29
41
|
>
|
|
30
42
|
{{ cell.label }}
|
|
31
43
|
</slot>
|
|
44
|
+
<!-- Fallback на простое значение -->
|
|
45
|
+
<template v-else>
|
|
46
|
+
{{ cell.label }}
|
|
47
|
+
</template>
|
|
32
48
|
</td>
|
|
33
49
|
</tr>
|
|
34
50
|
</tbody>
|
|
@@ -79,20 +95,12 @@ function buildColumnTree(vnodes) {
|
|
|
79
95
|
// Handle both function and array children
|
|
80
96
|
let children = []
|
|
81
97
|
if (typeof vnode.children?.default === 'function') {
|
|
82
|
-
try {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
console.warn('Error calling default slot:', e)
|
|
86
|
-
}
|
|
87
|
-
} else if (Array.isArray(vnode.children)) {
|
|
88
|
-
children = vnode.children
|
|
89
|
-
}
|
|
90
|
-
|
|
98
|
+
try {children = vnode.children.default()}
|
|
99
|
+
catch (e) {console.warn('Error calling default slot:', e)}
|
|
100
|
+
} else if (Array.isArray(vnode.children)) children = vnode.children
|
|
91
101
|
if (children && children.length > 0) {
|
|
92
102
|
const childColumns = buildColumnTree(children)
|
|
93
|
-
if (childColumns.length > 0)
|
|
94
|
-
column.children = childColumns
|
|
95
|
-
}
|
|
103
|
+
if (childColumns.length > 0) column.children = childColumns
|
|
96
104
|
}
|
|
97
105
|
result.push(column)
|
|
98
106
|
}
|
|
@@ -183,46 +191,29 @@ function extractLeafColumns(columns) {
|
|
|
183
191
|
// Обработка данных для tbody с поддержкой colspan
|
|
184
192
|
function processBodyData(data, columns) {
|
|
185
193
|
// Защита от undefined/null значений
|
|
186
|
-
if (!data || !Array.isArray(data))
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
if (!columns || !Array.isArray(columns)) {
|
|
190
|
-
return []
|
|
191
|
-
}
|
|
192
|
-
|
|
194
|
+
if (!data || !Array.isArray(data)) return []
|
|
195
|
+
if (!columns || !Array.isArray(columns)) return []
|
|
193
196
|
// Функция для получения значения по пути (например, "gant.0")
|
|
194
197
|
function getValueByPath(obj, path) {
|
|
195
198
|
if (!obj || !path) return undefined
|
|
196
|
-
|
|
197
199
|
// Разбиваем путь по точкам
|
|
198
200
|
const keys = path.split('.')
|
|
199
201
|
let value = obj
|
|
200
|
-
|
|
201
202
|
for (const key of keys) {
|
|
202
|
-
if (value === null || value === undefined)
|
|
203
|
-
return undefined
|
|
204
|
-
}
|
|
205
|
-
|
|
203
|
+
if (value === null || value === undefined) return undefined
|
|
206
204
|
// Пробуем обратиться как к свойству объекта или элементу массива
|
|
207
205
|
value = value[key]
|
|
208
206
|
}
|
|
209
|
-
|
|
210
207
|
return value
|
|
211
208
|
}
|
|
212
|
-
|
|
213
209
|
const processedRows = []
|
|
214
210
|
const cellMap = new Map() // Для отслеживания объединенных ячеек
|
|
215
211
|
data.forEach((row, rowIndex) => {
|
|
216
212
|
const processedRow = []
|
|
217
|
-
|
|
218
213
|
columns.forEach((column, colIndex) => {
|
|
219
214
|
const key = `${rowIndex}-${column.prop}`
|
|
220
|
-
|
|
221
215
|
// Проверяем, не занята ли эта ячейка из-за rowspan/colspan
|
|
222
|
-
if (cellMap.has(key))
|
|
223
|
-
return
|
|
224
|
-
}
|
|
225
|
-
|
|
216
|
+
if (cellMap.has(key)) return
|
|
226
217
|
// Получаем значение по пути (поддержка "gant.0", "gant.1" и т.д.)
|
|
227
218
|
const cellData = getValueByPath(row, column.prop)
|
|
228
219
|
let cellValue = cellData
|
|
@@ -237,7 +228,6 @@ function processBodyData(data, columns) {
|
|
|
237
228
|
rowspan = cellData.rowspan || 1
|
|
238
229
|
style = cellData.style || {}
|
|
239
230
|
}
|
|
240
|
-
|
|
241
231
|
const cell = {
|
|
242
232
|
key,
|
|
243
233
|
label: cellValue,
|
|
@@ -248,23 +238,17 @@ function processBodyData(data, columns) {
|
|
|
248
238
|
originalRow: row,
|
|
249
239
|
rowIndex
|
|
250
240
|
}
|
|
251
|
-
|
|
252
241
|
processedRow.push(cell)
|
|
253
|
-
|
|
254
|
-
|
|
255
242
|
// Помечаем объединенные ячейки
|
|
256
243
|
for (let r = 0; r < rowspan; r++) {
|
|
257
244
|
for (let c = 0; c < colspan; c++) {
|
|
258
245
|
if (r === 0 && c === 0) continue // Основная ячейка
|
|
259
246
|
const cellKey = `${rowIndex + r}-${columns[colIndex + c]?.prop}`
|
|
260
|
-
if (cellKey)
|
|
261
|
-
cellMap.set(cellKey, true)
|
|
262
|
-
}
|
|
247
|
+
if (cellKey) cellMap.set(cellKey, true)
|
|
263
248
|
}
|
|
264
249
|
}
|
|
265
250
|
})
|
|
266
251
|
|
|
267
|
-
// Добавляем строку только если в ней есть ячейки
|
|
268
252
|
if (processedRow.length > 0) {
|
|
269
253
|
processedRows.push(processedRow)
|
|
270
254
|
}
|