startup-ui 0.12.0 → 1.0.0
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/AGENTS.md +18 -0
- package/CHANGELOG.md +39 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +21 -0
- package/README.md +54 -0
- package/dist/defaults.css +83 -0
- package/dist/index.css +1 -1
- package/dist/startup-ui.cjs.js +33 -586
- package/dist/startup-ui.cjs.js.map +1 -1
- package/dist/startup-ui.es.js +7109 -8839
- package/dist/startup-ui.es.js.map +1 -1
- package/dist/types/components/SActionIcon.d.ts +2 -1
- package/dist/types/components/SActionIcon.d.ts.map +1 -1
- package/dist/types/components/SCanvas.d.ts +0 -1
- package/dist/types/components/SCanvas.d.ts.map +1 -1
- package/dist/types/components/SCheckbox.d.ts.map +1 -1
- package/dist/types/components/SCheckboxGroup.d.ts +4 -0
- package/dist/types/components/SCheckboxGroup.d.ts.map +1 -1
- package/dist/types/components/SColumnSettings.d.ts +4 -4
- package/dist/types/components/SColumnSettings.d.ts.map +1 -1
- package/dist/types/components/SConfirm/SConfirm.d.ts +2 -0
- package/dist/types/components/SConfirm/SConfirm.d.ts.map +1 -1
- package/dist/types/components/SCopyText.d.ts.map +1 -1
- package/dist/types/components/SDatePicker.d.ts +3 -4
- package/dist/types/components/SDatePicker.d.ts.map +1 -1
- package/dist/types/components/SDialog.d.ts.map +1 -1
- package/dist/types/components/SFilterGroup.d.ts +3 -3
- package/dist/types/components/SFilterGroup.d.ts.map +1 -1
- package/dist/types/components/SFooter.d.ts.map +1 -1
- package/dist/types/components/SForm.d.ts.map +1 -1
- package/dist/types/components/SFormRow.d.ts.map +1 -1
- package/dist/types/components/SHtmlEditor.d.ts +20 -0
- package/dist/types/components/SHtmlEditor.d.ts.map +1 -1
- package/dist/types/components/SImagePreview.d.ts.map +1 -1
- package/dist/types/components/SInput.d.ts +9 -2
- package/dist/types/components/SInput.d.ts.map +1 -1
- package/dist/types/components/SMenu.d.ts +39 -0
- package/dist/types/components/SMenu.d.ts.map +1 -0
- package/dist/types/components/SNote.d.ts.map +1 -1
- package/dist/types/components/SPagination.d.ts.map +1 -1
- package/dist/types/components/SProgressbar.d.ts.map +1 -1
- package/dist/types/components/SRadio.d.ts.map +1 -1
- package/dist/types/components/SRadioGroup.d.ts +4 -0
- package/dist/types/components/SRadioGroup.d.ts.map +1 -1
- package/dist/types/components/SSelect.d.ts +6 -0
- package/dist/types/components/SSelect.d.ts.map +1 -1
- package/dist/types/components/SStatus.d.ts.map +1 -1
- package/dist/types/components/STable.d.ts +4 -4
- package/dist/types/components/STable.d.ts.map +1 -1
- package/dist/types/components/SToggle.d.ts.map +1 -1
- package/dist/types/components/STooltip.d.ts +0 -1
- package/dist/types/components/STooltip.d.ts.map +1 -1
- package/dist/types/components/STree.d.ts +11 -5
- package/dist/types/components/STree.d.ts.map +1 -1
- package/dist/types/components/SUpload.d.ts +5 -4
- package/dist/types/components/SUpload.d.ts.map +1 -1
- package/dist/types/components/SVerticalMenu.d.ts.map +1 -1
- package/dist/types/components/htmlEditor/contentStyle.d.ts +9 -0
- package/dist/types/components/htmlEditor/contentStyle.d.ts.map +1 -0
- package/dist/types/components/icons.d.ts +24 -0
- package/dist/types/components/icons.d.ts.map +1 -0
- package/dist/types/config.d.ts +49 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/global-components.d.ts +3 -6
- package/dist/types/global-components.d.ts.map +1 -1
- package/dist/types/index.d.ts +12 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/locale/index.d.ts +49 -0
- package/dist/types/locale/index.d.ts.map +1 -0
- package/dist/types/locale/messages/en-US.d.ts +4 -0
- package/dist/types/locale/messages/en-US.d.ts.map +1 -0
- package/dist/types/locale/messages/en.d.ts +4 -0
- package/dist/types/locale/messages/en.d.ts.map +1 -0
- package/dist/types/locale/messages/ru.d.ts +4 -0
- package/dist/types/locale/messages/ru.d.ts.map +1 -0
- package/dist/types/locale/types.d.ts +74 -0
- package/dist/types/locale/types.d.ts.map +1 -0
- package/dist/types/plugin.d.ts +2 -1
- package/dist/types/plugin.d.ts.map +1 -1
- package/dist/types/utils/deepMerge.d.ts +9 -0
- package/dist/types/utils/deepMerge.d.ts.map +1 -0
- package/dist/types/utils/options.d.ts +25 -0
- package/dist/types/utils/options.d.ts.map +1 -0
- package/llms/components/data/sfilter.md +194 -0
- package/llms/components/data/spagination.md +114 -0
- package/llms/components/data/stable.md +638 -0
- package/llms/components/data/stree.md +213 -0
- package/llms/components/forms/scheckbox.md +139 -0
- package/llms/components/forms/sdatepicker.md +161 -0
- package/llms/components/forms/sform.md +240 -0
- package/llms/components/forms/shtmleditor.md +143 -0
- package/llms/components/forms/sinput.md +165 -0
- package/llms/components/forms/sradio.md +164 -0
- package/llms/components/forms/sselect.md +149 -0
- package/llms/components/forms/sswitch.md +69 -0
- package/llms/components/forms/supload.md +189 -0
- package/llms/components/interfaces/sactionbar.md +40 -0
- package/llms/components/interfaces/sactionicon.md +126 -0
- package/llms/components/interfaces/salert.md +87 -0
- package/llms/components/interfaces/sbutton.md +167 -0
- package/llms/components/interfaces/scolumnsettings.md +204 -0
- package/llms/components/interfaces/sconfirm.md +57 -0
- package/llms/components/interfaces/scopytext.md +67 -0
- package/llms/components/interfaces/sdashboard.md +130 -0
- package/llms/components/interfaces/sdialog.md +158 -0
- package/llms/components/interfaces/simagepreview.md +98 -0
- package/llms/components/interfaces/snote.md +64 -0
- package/llms/components/interfaces/sprogressbar.md +48 -0
- package/llms/components/interfaces/sstat.md +79 -0
- package/llms/components/interfaces/sstatus.md +76 -0
- package/llms/components/interfaces/stag.md +70 -0
- package/llms/components/interfaces/stimeline.md +47 -0
- package/llms/components/interfaces/stoggle.md +120 -0
- package/llms/components/interfaces/stooltip.md +88 -0
- package/llms/components/template/scanvas.md +61 -0
- package/llms/components/template/smenu.md +88 -0
- package/llms/components/template/sverticalmenu.md +113 -0
- package/llms/llms.txt +49 -0
- package/package.json +37 -4
- package/dist/types/components/SDropdownMenu.d.ts +0 -39
- package/dist/types/components/SDropdownMenu.d.ts.map +0 -1
- package/dist/types/components/SHorizontalMenu.d.ts +0 -33
- package/dist/types/components/SHorizontalMenu.d.ts.map +0 -1
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
# STable
|
|
2
|
+
|
|
3
|
+
Таблица.
|
|
4
|
+
|
|
5
|
+
<SToggleGroup>
|
|
6
|
+
<SToggle title="В чем отличие от аналогов?">
|
|
7
|
+
<p>В отличие от популярных библиотек компонентов для Vue3:</p>
|
|
8
|
+
<ol>
|
|
9
|
+
<li>Семантика поддерживает и итератор строк-значений, и просто вставку строк как есть.</li>
|
|
10
|
+
<li>Позволяет задать состояние, когда нет данных, одним атрибутом.</li>
|
|
11
|
+
<li>Позволяет сделать горизонтальный скролл сверху таблицы, что полезно для длинных таблиц с большим кол-вом колонок.</li>
|
|
12
|
+
</ol>
|
|
13
|
+
</SToggle>
|
|
14
|
+
</SToggleGroup>
|
|
15
|
+
|
|
16
|
+
## Базовый пример
|
|
17
|
+
|
|
18
|
+
```vue
|
|
19
|
+
<template>
|
|
20
|
+
<STable :data="users">
|
|
21
|
+
<template #header>
|
|
22
|
+
<td>Пользователь</td>
|
|
23
|
+
<td>Тариф</td>
|
|
24
|
+
<td>Баланс</td>
|
|
25
|
+
<td>Роль</td>
|
|
26
|
+
<td>Дата регистрации</td>
|
|
27
|
+
<td></td>
|
|
28
|
+
</template>
|
|
29
|
+
<template #row="{ row }">
|
|
30
|
+
<td>{{ row.username }}</td>
|
|
31
|
+
<td>{{ row.plan }}</td>
|
|
32
|
+
<td>{{ row.balance }}</td>
|
|
33
|
+
<td>{{ row.role }}</td>
|
|
34
|
+
<td>{{ row.created_at }}</td>
|
|
35
|
+
<td>
|
|
36
|
+
<SActionIcon title="Удалить" :confirm="`Вы действительно хотите удалить пользователя «${row.username}»?`"
|
|
37
|
+
@click="deleteUser(row.username)" icon="trash" danger />
|
|
38
|
+
</td>
|
|
39
|
+
</template>
|
|
40
|
+
<template #footer>
|
|
41
|
+
<td>ИТОГО</td>
|
|
42
|
+
<td></td>
|
|
43
|
+
<td>{{ totalBalance }}</td>
|
|
44
|
+
<td></td>
|
|
45
|
+
<td></td>
|
|
46
|
+
<td></td>
|
|
47
|
+
</template>
|
|
48
|
+
</STable>
|
|
49
|
+
</template>
|
|
50
|
+
<script setup>
|
|
51
|
+
import { ref, computed } from 'vue'
|
|
52
|
+
|
|
53
|
+
const users = ref([
|
|
54
|
+
{ username: 'Ivanov', role: 'customer', plan: 'Базовый', balance: 3000, created_at: '2025-11-04' },
|
|
55
|
+
{ username: 'Stepanov', role: 'customer', plan: 'Базовый', balance: 4500, created_at: '2025-11-05' },
|
|
56
|
+
{ username: 'Petrov', role: 'customer', plan: 'Базовый', balance: 1716, created_at: '2025-11-05' },
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
function deleteUser(username) {
|
|
60
|
+
users.value = users.value.filter(user => user.username !== username)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const totalBalance = computed(() => users.value.reduce((acc, user) => acc + user.balance, 0))
|
|
64
|
+
</script>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Утилиты выравнивания
|
|
68
|
+
|
|
69
|
+
По умолчанию ячейки выравниваются по позиции колонки: первая — по левому краю, последняя — по правому, остальные — по центру. Выравнивание конкретной ячейки можно переопределить классами на `<td>` — `.left`, `.center`, `.right`, а `.nowrap` запрещает перенос строки:
|
|
70
|
+
|
|
71
|
+
```vue
|
|
72
|
+
<template>
|
|
73
|
+
<STable :data="rows">
|
|
74
|
+
<template #header>
|
|
75
|
+
<td>Товар</td>
|
|
76
|
+
<td class="right">Цена</td>
|
|
77
|
+
<td class="left">Остаток</td>
|
|
78
|
+
<td class="nowrap">Артикул</td>
|
|
79
|
+
</template>
|
|
80
|
+
<template #row="{ row }">
|
|
81
|
+
<td>{{ row.name }}</td>
|
|
82
|
+
<td class="right">{{ row.price }}</td>
|
|
83
|
+
<td class="left">{{ row.stock }}</td>
|
|
84
|
+
<td class="nowrap">{{ row.sku }}</td>
|
|
85
|
+
</template>
|
|
86
|
+
</STable>
|
|
87
|
+
</template>
|
|
88
|
+
<script setup>
|
|
89
|
+
import { ref } from 'vue'
|
|
90
|
+
|
|
91
|
+
const rows = ref([
|
|
92
|
+
{ name: 'Кофемолка', price: '3 200 ₽', stock: 12, sku: 'KF-001-XL' },
|
|
93
|
+
{ name: 'Чайник', price: '1 850 ₽', stock: 4, sku: 'CH-204' },
|
|
94
|
+
])
|
|
95
|
+
</script>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Подсветка строк при наведении
|
|
99
|
+
|
|
100
|
+
Для подсветки при наведении добавляем атрибут <strong>hoverable</strong>
|
|
101
|
+
|
|
102
|
+
```vue
|
|
103
|
+
<template>
|
|
104
|
+
<STable :data="users" hoverable>
|
|
105
|
+
<template #header>
|
|
106
|
+
<td>Пользователь</td>
|
|
107
|
+
<td>Тариф</td>
|
|
108
|
+
<td>Баланс</td>
|
|
109
|
+
<td>Роль</td>
|
|
110
|
+
<td>Дата регистрации</td>
|
|
111
|
+
<td></td>
|
|
112
|
+
</template>
|
|
113
|
+
<template #row="{ row }">
|
|
114
|
+
<td>{{ row.username }}</td>
|
|
115
|
+
<td>{{ row.plan }}</td>
|
|
116
|
+
<td>{{ row.balance }}</td>
|
|
117
|
+
<td>{{ row.role }}</td>
|
|
118
|
+
<td>{{ row.created_at }}</td>
|
|
119
|
+
<td>
|
|
120
|
+
<SActionIcon title="Удалить" :confirm="`Вы действительно хотите удалить пользователя «${row.username}»?`"
|
|
121
|
+
@click="deleteUser(row.username)" icon="trash" danger />
|
|
122
|
+
</td>
|
|
123
|
+
</template>
|
|
124
|
+
<template #footer>
|
|
125
|
+
<td>ИТОГО</td>
|
|
126
|
+
<td></td>
|
|
127
|
+
<td>{{ totalBalance }}</td>
|
|
128
|
+
<td></td>
|
|
129
|
+
<td></td>
|
|
130
|
+
<td></td>
|
|
131
|
+
</template>
|
|
132
|
+
</STable>
|
|
133
|
+
</template>
|
|
134
|
+
<script setup>
|
|
135
|
+
import { ref, computed } from 'vue'
|
|
136
|
+
|
|
137
|
+
const users = ref([
|
|
138
|
+
{ username: 'Ivanov', role: 'customer', plan: 'Базовый', balance: 3000, created_at: '2025-11-04' },
|
|
139
|
+
{ username: 'Stepanov', role: 'customer', plan: 'Базовый', balance: 4500, created_at: '2025-11-05' },
|
|
140
|
+
{ username: 'Petrov', role: 'customer', plan: 'Базовый', balance: 1716, created_at: '2025-11-05' },
|
|
141
|
+
])
|
|
142
|
+
|
|
143
|
+
function deleteUser(username) {
|
|
144
|
+
users.value = users.value.filter(user => user.username !== username)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const totalBalance = computed(() => users.value.reduce((acc, user) => acc + user.balance, 0))
|
|
148
|
+
</script>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Явные границы по краям ячеек
|
|
152
|
+
|
|
153
|
+
Для отрисовки границ добавляем атрибут <strong>bordered</strong>
|
|
154
|
+
|
|
155
|
+
```vue
|
|
156
|
+
<template>
|
|
157
|
+
<STable :data="users" bordered>
|
|
158
|
+
<template #header>
|
|
159
|
+
<td>Пользователь</td>
|
|
160
|
+
<td>Тариф</td>
|
|
161
|
+
<td>Баланс</td>
|
|
162
|
+
<td>Роль</td>
|
|
163
|
+
<td>Дата регистрации</td>
|
|
164
|
+
<td></td>
|
|
165
|
+
</template>
|
|
166
|
+
<template #row="{ row }">
|
|
167
|
+
<td>{{ row.username }}</td>
|
|
168
|
+
<td>{{ row.plan }}</td>
|
|
169
|
+
<td>{{ row.balance }}</td>
|
|
170
|
+
<td>{{ row.role }}</td>
|
|
171
|
+
<td>{{ row.created_at }}</td>
|
|
172
|
+
<td>
|
|
173
|
+
<SActionIcon title="Удалить" :confirm="`Вы действительно хотите удалить пользователя «${row.username}»?`"
|
|
174
|
+
@click="deleteUser(row.username)" icon="trash" danger />
|
|
175
|
+
</td>
|
|
176
|
+
</template>
|
|
177
|
+
<template #footer>
|
|
178
|
+
<td>ИТОГО</td>
|
|
179
|
+
<td></td>
|
|
180
|
+
<td>{{ totalBalance }}</td>
|
|
181
|
+
<td></td>
|
|
182
|
+
<td></td>
|
|
183
|
+
<td></td>
|
|
184
|
+
</template>
|
|
185
|
+
</STable>
|
|
186
|
+
</template>
|
|
187
|
+
<script setup>
|
|
188
|
+
import { ref, computed } from 'vue'
|
|
189
|
+
|
|
190
|
+
const users = ref([
|
|
191
|
+
{ username: 'Ivanov', role: 'customer', plan: 'Базовый', balance: 3000, created_at: '2025-11-04' },
|
|
192
|
+
{ username: 'Stepanov', role: 'customer', plan: 'Базовый', balance: 4500, created_at: '2025-11-05' },
|
|
193
|
+
{ username: 'Petrov', role: 'customer', plan: 'Базовый', balance: 1716, created_at: '2025-11-05' },
|
|
194
|
+
])
|
|
195
|
+
|
|
196
|
+
function deleteUser(username) {
|
|
197
|
+
users.value = users.value.filter(user => user.username !== username)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const totalBalance = computed(() => users.value.reduce((acc, user) => acc + user.balance, 0))
|
|
201
|
+
</script>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Выделение четных строк
|
|
205
|
+
|
|
206
|
+
Для выделения четных строк добавляем атрибут <strong>striped</strong>
|
|
207
|
+
|
|
208
|
+
```vue
|
|
209
|
+
<template>
|
|
210
|
+
<STable :data="users" striped>
|
|
211
|
+
<template #header>
|
|
212
|
+
<td>Пользователь</td>
|
|
213
|
+
<td>Тариф</td>
|
|
214
|
+
<td>Баланс</td>
|
|
215
|
+
<td>Роль</td>
|
|
216
|
+
<td>Дата регистрации</td>
|
|
217
|
+
<td></td>
|
|
218
|
+
</template>
|
|
219
|
+
<template #row="{ row }">
|
|
220
|
+
<td>{{ row.username }}</td>
|
|
221
|
+
<td>{{ row.plan }}</td>
|
|
222
|
+
<td>{{ row.balance }}</td>
|
|
223
|
+
<td>{{ row.role }}</td>
|
|
224
|
+
<td>{{ row.created_at }}</td>
|
|
225
|
+
<td>
|
|
226
|
+
<SActionIcon title="Удалить" :confirm="`Вы действительно хотите удалить пользователя «${row.username}»?`"
|
|
227
|
+
@click="deleteUser(row.username)" icon="trash" danger />
|
|
228
|
+
</td>
|
|
229
|
+
</template>
|
|
230
|
+
<template #footer>
|
|
231
|
+
<td>ИТОГО</td>
|
|
232
|
+
<td></td>
|
|
233
|
+
<td>{{ totalBalance }}</td>
|
|
234
|
+
<td></td>
|
|
235
|
+
<td></td>
|
|
236
|
+
<td></td>
|
|
237
|
+
</template>
|
|
238
|
+
</STable>
|
|
239
|
+
</template>
|
|
240
|
+
<script setup>
|
|
241
|
+
import { ref, computed } from 'vue'
|
|
242
|
+
|
|
243
|
+
const users = ref([
|
|
244
|
+
{ username: 'Ivanov', role: 'customer', plan: 'Базовый', balance: 3000, created_at: '2025-11-04' },
|
|
245
|
+
{ username: 'Stepanov', role: 'customer', plan: 'Базовый', balance: 4500, created_at: '2025-11-05' },
|
|
246
|
+
{ username: 'Petrov', role: 'customer', plan: 'Базовый', balance: 1716, created_at: '2025-11-05' },
|
|
247
|
+
{ username: 'Sidorov', role: 'customer', plan: 'Базовый', balance: 6000, created_at: '2025-11-06' },
|
|
248
|
+
])
|
|
249
|
+
|
|
250
|
+
function deleteUser(username) {
|
|
251
|
+
users.value = users.value.filter(user => user.username !== username)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const totalBalance = computed(() => users.value.reduce((acc, user) => acc + user.balance, 0))
|
|
255
|
+
</script>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Фиксированная шапка
|
|
259
|
+
|
|
260
|
+
Чтобы зафиксировать шапку, устанавливаем высоту таблицы в атрибуте `height`.
|
|
261
|
+
|
|
262
|
+
```vue
|
|
263
|
+
<template>
|
|
264
|
+
<div class="table-container">
|
|
265
|
+
<STable :data="users" height="300px">
|
|
266
|
+
<template #header>
|
|
267
|
+
<td>Пользователь</td>
|
|
268
|
+
<td>Имя</td>
|
|
269
|
+
<td>Фамилия</td>
|
|
270
|
+
<td>Дата рождения</td>
|
|
271
|
+
<td>Рост</td>
|
|
272
|
+
<td>Вес</td>
|
|
273
|
+
<td>Тариф</td>
|
|
274
|
+
<td>Баланс</td>
|
|
275
|
+
<td>Роль</td>
|
|
276
|
+
<td>Дата регистрации</td>
|
|
277
|
+
<td></td>
|
|
278
|
+
</template>
|
|
279
|
+
<template #row="{ row }">
|
|
280
|
+
<td>{{ row.username }}</td>
|
|
281
|
+
<td>{{ row.name }}</td>
|
|
282
|
+
<td>{{ row.secondname }}</td>
|
|
283
|
+
<td>{{ row.birthdate }}</td>
|
|
284
|
+
<td>{{ row.height }}</td>
|
|
285
|
+
<td>{{ row.weight }}</td>
|
|
286
|
+
<td>{{ row.plan }}</td>
|
|
287
|
+
<td>{{ row.balance }}</td>
|
|
288
|
+
<td>{{ row.role }}</td>
|
|
289
|
+
<td>{{ row.created_at }}</td>
|
|
290
|
+
<td>
|
|
291
|
+
<SActionIcon title="Удалить" :confirm="`Вы действительно хотите удалить пользователя «${row.username}»?`"
|
|
292
|
+
@click="deleteUser(row.username)" icon="trash" danger />
|
|
293
|
+
</td>
|
|
294
|
+
</template>
|
|
295
|
+
<template #footer>
|
|
296
|
+
<td>ИТОГО</td>
|
|
297
|
+
<td></td>
|
|
298
|
+
<td></td>
|
|
299
|
+
<td></td>
|
|
300
|
+
<td></td>
|
|
301
|
+
<td></td>
|
|
302
|
+
<td></td>
|
|
303
|
+
<td>{{ totalBalance }}</td>
|
|
304
|
+
<td></td>
|
|
305
|
+
<td></td>
|
|
306
|
+
<td></td>
|
|
307
|
+
</template>
|
|
308
|
+
</STable>
|
|
309
|
+
</div>
|
|
310
|
+
</template>
|
|
311
|
+
<script setup>
|
|
312
|
+
import { ref, computed } from 'vue'
|
|
313
|
+
|
|
314
|
+
const base = [
|
|
315
|
+
{ username: 'Ivanov', name: 'Иван', secondname: 'Иванов', birthdate: '1995.06.06', height: '175', weight: '80', role: 'Пользователь', plan: 'Базовый', balance: 3000, created_at: '2025-11-04' },
|
|
316
|
+
{ username: 'Stepanov', name: 'Степан', secondname: 'Степанов', birthdate: '1990.02.14', height: '182', weight: '88', role: 'Админ', plan: '-', balance: 4500, created_at: '2025-11-05' },
|
|
317
|
+
{ username: 'Petrov', name: 'Петр', secondname: 'Петров', birthdate: '1988.09.21', height: '178', weight: '76', role: 'Пользователь', plan: 'Премиум', balance: 1716, created_at: '2025-11-05' },
|
|
318
|
+
{ username: 'Sidorov', name: 'Сергей', secondname: 'Сидоров', birthdate: '1993.12.01', height: '170', weight: '72', role: 'Пользователь', plan: 'Базовый', balance: 6000, created_at: '2025-11-06' },
|
|
319
|
+
{ username: 'Alexeev', name: 'Алексей', secondname: 'Алексеев', birthdate: '1996.03.30', height: '185', weight: '90', role: 'Редактор', plan: '-', balance: 2000, created_at: '2025-11-09' },
|
|
320
|
+
]
|
|
321
|
+
const users = ref([...base, ...base.map(u => ({ ...u, username: u.username + '2' }))])
|
|
322
|
+
|
|
323
|
+
function deleteUser(username) {
|
|
324
|
+
users.value = users.value.filter(user => user.username !== username)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const totalBalance = computed(() => users.value.reduce((acc, user) => acc + user.balance, 0))
|
|
328
|
+
</script>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
<SNote icon="lightbulb" attention>
|
|
332
|
+
Для высоты таблицы с фиксированной шапкой удобно использовать <code>80vh</code>, чтобы таблица занимала 80% от высоты экрана.
|
|
333
|
+
</SNote>
|
|
334
|
+
|
|
335
|
+
## Горизонтальный скролл сверху
|
|
336
|
+
|
|
337
|
+
```vue
|
|
338
|
+
<template>
|
|
339
|
+
<div class="table-container">
|
|
340
|
+
<STable :data="users" top-scroll>
|
|
341
|
+
<template #header>
|
|
342
|
+
<td>Пользователь</td>
|
|
343
|
+
<td>Имя</td>
|
|
344
|
+
<td>Фамилия</td>
|
|
345
|
+
<td>Дата рождения</td>
|
|
346
|
+
<td>Рост</td>
|
|
347
|
+
<td>Вес</td>
|
|
348
|
+
<td>Тариф</td>
|
|
349
|
+
<td>Баланс</td>
|
|
350
|
+
<td>Роль</td>
|
|
351
|
+
<td>Дата регистрации</td>
|
|
352
|
+
<td></td>
|
|
353
|
+
</template>
|
|
354
|
+
<template #row="{ row }">
|
|
355
|
+
<td>{{ row.username }}</td>
|
|
356
|
+
<td>{{ row.name }}</td>
|
|
357
|
+
<td>{{ row.secondname }}</td>
|
|
358
|
+
<td>{{ row.birthdate }}</td>
|
|
359
|
+
<td>{{ row.height }}</td>
|
|
360
|
+
<td>{{ row.weight }}</td>
|
|
361
|
+
<td>{{ row.plan }}</td>
|
|
362
|
+
<td>{{ row.balance }}</td>
|
|
363
|
+
<td>{{ row.role }}</td>
|
|
364
|
+
<td>{{ row.created_at }}</td>
|
|
365
|
+
<td>
|
|
366
|
+
<SActionIcon title="Удалить" :confirm="`Вы действительно хотите удалить пользователя «${row.username}»?`"
|
|
367
|
+
@click="deleteUser(row.username)" icon="trash" danger />
|
|
368
|
+
</td>
|
|
369
|
+
</template>
|
|
370
|
+
<template #footer>
|
|
371
|
+
<td>ИТОГО</td>
|
|
372
|
+
<td></td>
|
|
373
|
+
<td></td>
|
|
374
|
+
<td></td>
|
|
375
|
+
<td></td>
|
|
376
|
+
<td></td>
|
|
377
|
+
<td></td>
|
|
378
|
+
<td>{{ totalBalance }}</td>
|
|
379
|
+
<td></td>
|
|
380
|
+
<td></td>
|
|
381
|
+
<td></td>
|
|
382
|
+
</template>
|
|
383
|
+
</STable>
|
|
384
|
+
</div>
|
|
385
|
+
</template>
|
|
386
|
+
<script setup>
|
|
387
|
+
import { ref, computed } from 'vue'
|
|
388
|
+
|
|
389
|
+
const users = ref([
|
|
390
|
+
{ username: 'Ivanov', name: 'Иван', secondname: 'Иванов', birthdate: '1995.06.06', height: '175', weight: '80', role: 'Пользователь', plan: 'Базовый', balance: 3000, created_at: '2025-11-04' },
|
|
391
|
+
{ username: 'Stepanov', name: 'Степан', secondname: 'Степанов', birthdate: '1990.02.14', height: '182', weight: '88', role: 'Админ', plan: '-', balance: 4500, created_at: '2025-11-05' },
|
|
392
|
+
{ username: 'Petrov', name: 'Петр', secondname: 'Петров', birthdate: '1988.09.21', height: '178', weight: '76', role: 'Пользователь', plan: 'Премиум', balance: 1716, created_at: '2025-11-05' },
|
|
393
|
+
{ username: 'Sidorov', name: 'Сергей', secondname: 'Сидоров', birthdate: '1993.12.01', height: '170', weight: '72', role: 'Пользователь', plan: 'Базовый', balance: 6000, created_at: '2025-11-06' },
|
|
394
|
+
{ username: 'Alexeev', name: 'Алексей', secondname: 'Алексеев', birthdate: '1996.03.30', height: '185', weight: '90', role: 'Редактор', plan: '-', balance: 2000, created_at: '2025-11-09' },
|
|
395
|
+
])
|
|
396
|
+
|
|
397
|
+
function deleteUser(username) {
|
|
398
|
+
users.value = users.value.filter(user => user.username !== username)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const totalBalance = computed(() => users.value.reduce((acc, user) => acc + user.balance, 0))
|
|
402
|
+
</script>
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Кастомный контент внутри tbody
|
|
406
|
+
|
|
407
|
+
Если нужно задать явные строки в tbody вместо перебора #row, то используем дефолтный слот (в нем для отдельных строк нужно явно указать `<tr>`).
|
|
408
|
+
|
|
409
|
+
```vue
|
|
410
|
+
<template>
|
|
411
|
+
<STable>
|
|
412
|
+
<template #header>
|
|
413
|
+
<td>Пользователь</td>
|
|
414
|
+
<td>Тариф</td>
|
|
415
|
+
<td>Баланс</td>
|
|
416
|
+
<td>Роль</td>
|
|
417
|
+
<td>Дата регистрации</td>
|
|
418
|
+
<td></td>
|
|
419
|
+
</template>
|
|
420
|
+
<tr v-for="user in users" :key="user.username">
|
|
421
|
+
<td>{{ user.username }}</td>
|
|
422
|
+
<td>{{ user.plan }}</td>
|
|
423
|
+
<td>{{ user.balance }}</td>
|
|
424
|
+
<td>{{ user.role }}</td>
|
|
425
|
+
<td>{{ user.created_at }}</td>
|
|
426
|
+
<td>
|
|
427
|
+
<SActionIcon title="Удалить" :confirm="`Вы действительно хотите удалить пользователя «${user.username}»?`"
|
|
428
|
+
@click="deleteUser(user.username)" icon="trash" danger />
|
|
429
|
+
</td>
|
|
430
|
+
</tr>
|
|
431
|
+
<template #footer>
|
|
432
|
+
<td>ИТОГО</td>
|
|
433
|
+
<td></td>
|
|
434
|
+
<td>{{ totalBalance }}</td>
|
|
435
|
+
<td></td>
|
|
436
|
+
<td></td>
|
|
437
|
+
<td></td>
|
|
438
|
+
</template>
|
|
439
|
+
</STable>
|
|
440
|
+
</template>
|
|
441
|
+
<script setup>
|
|
442
|
+
import { ref, computed } from 'vue'
|
|
443
|
+
|
|
444
|
+
const users = ref([
|
|
445
|
+
{ username: 'Ivanov', role: 'customer', plan: 'Базовый', balance: 3000, created_at: '2025-11-04' },
|
|
446
|
+
{ username: 'Stepanov', role: 'customer', plan: 'Базовый', balance: 4500, created_at: '2025-11-05' },
|
|
447
|
+
{ username: 'Petrov', role: 'customer', plan: 'Базовый', balance: 1716, created_at: '2025-11-05' },
|
|
448
|
+
])
|
|
449
|
+
|
|
450
|
+
function deleteUser(username) {
|
|
451
|
+
users.value = users.value.filter(user => user.username !== username)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const totalBalance = computed(() => users.value.reduce((acc, user) => acc + user.balance, 0))
|
|
455
|
+
</script>
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Несколько строк в header / footer
|
|
459
|
+
|
|
460
|
+
Если это нужно, просто используем отдельные слоты headers / footers (добавляется в конце "s"), в которые уже добавляем `<tr>`.
|
|
461
|
+
|
|
462
|
+
```vue
|
|
463
|
+
<template>
|
|
464
|
+
<STable>
|
|
465
|
+
<template #headers>
|
|
466
|
+
<tr><td>...</td></tr>
|
|
467
|
+
<tr><td>...</td></tr>
|
|
468
|
+
</template>
|
|
469
|
+
...
|
|
470
|
+
<template #footers>
|
|
471
|
+
<tr><td>...</td></tr>
|
|
472
|
+
<tr><td>...</td></tr>
|
|
473
|
+
</template>
|
|
474
|
+
</STable>
|
|
475
|
+
</template>
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## Сообщение о том, что нет данных
|
|
479
|
+
|
|
480
|
+
Когда задаем data и перебираем его через `<template #row>`, то часто (например, когда есть фильтры страницы) бывает нужно показать состояние «Ничего не найдено», когда в data ноль строк. По умолчанию в таком случае выводится сообщение «Ничего не найдено», которое можно заменить на кастомное через пропс/слот nodata:
|
|
481
|
+
|
|
482
|
+
```vue
|
|
483
|
+
<template>
|
|
484
|
+
<STable :data="users" nodata="Пользователи не найдены">
|
|
485
|
+
<template #header>
|
|
486
|
+
<td>Пользователь</td>
|
|
487
|
+
<td>Тариф</td>
|
|
488
|
+
<td>Баланс</td>
|
|
489
|
+
<td>Роль</td>
|
|
490
|
+
<td>Дата регистрации</td>
|
|
491
|
+
<td></td>
|
|
492
|
+
</template>
|
|
493
|
+
<template #row="{ row }">
|
|
494
|
+
<td>{{ row.username }}</td>
|
|
495
|
+
<td>{{ row.plan }}</td>
|
|
496
|
+
<td>{{ row.balance }}</td>
|
|
497
|
+
<td>{{ row.role }}</td>
|
|
498
|
+
<td>{{ row.created_at }}</td>
|
|
499
|
+
<td>
|
|
500
|
+
<SActionIcon title="Удалить" :confirm="`Вы действительно хотите удалить пользователя «${row.username}»?`"
|
|
501
|
+
@click="deleteUser(row.username)" icon="trash" danger />
|
|
502
|
+
</td>
|
|
503
|
+
</template>
|
|
504
|
+
<template #footer>
|
|
505
|
+
<td>ИТОГО</td>
|
|
506
|
+
<td></td>
|
|
507
|
+
<td></td>
|
|
508
|
+
<td></td>
|
|
509
|
+
<td></td>
|
|
510
|
+
<td></td>
|
|
511
|
+
</template>
|
|
512
|
+
</STable>
|
|
513
|
+
</template>
|
|
514
|
+
<script setup>
|
|
515
|
+
import { ref } from 'vue'
|
|
516
|
+
|
|
517
|
+
const users = ref([])
|
|
518
|
+
|
|
519
|
+
function deleteUser(username) {
|
|
520
|
+
users.value = users.value.filter(user => user.username !== username)
|
|
521
|
+
}
|
|
522
|
+
</script>
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
или
|
|
526
|
+
|
|
527
|
+
```vue
|
|
528
|
+
<template>
|
|
529
|
+
<STable :data="users">
|
|
530
|
+
<template #nodata>
|
|
531
|
+
<SNote>По заданным критериям поиска ничего не нашлось</SNote>
|
|
532
|
+
</template>
|
|
533
|
+
<template #header>
|
|
534
|
+
<td>Пользователь</td>
|
|
535
|
+
<td>Тариф</td>
|
|
536
|
+
<td>Баланс</td>
|
|
537
|
+
<td>Роль</td>
|
|
538
|
+
<td>Дата регистрации</td>
|
|
539
|
+
<td></td>
|
|
540
|
+
</template>
|
|
541
|
+
<template #row="{ row }">
|
|
542
|
+
<td>{{ row.username }}</td>
|
|
543
|
+
<td>{{ row.plan }}</td>
|
|
544
|
+
<td>{{ row.balance }}</td>
|
|
545
|
+
<td>{{ row.role }}</td>
|
|
546
|
+
<td>{{ row.created_at }}</td>
|
|
547
|
+
<td>
|
|
548
|
+
<SActionIcon title="Удалить" :confirm="`Вы действительно хотите удалить пользователя «${row.username}»?`"
|
|
549
|
+
@click="deleteUser(row.username)" icon="trash" danger />
|
|
550
|
+
</td>
|
|
551
|
+
</template>
|
|
552
|
+
<template #footer>
|
|
553
|
+
<td>ИТОГО</td>
|
|
554
|
+
<td></td>
|
|
555
|
+
<td></td>
|
|
556
|
+
<td></td>
|
|
557
|
+
<td></td>
|
|
558
|
+
<td></td>
|
|
559
|
+
</template>
|
|
560
|
+
</STable>
|
|
561
|
+
</template>
|
|
562
|
+
<script setup>
|
|
563
|
+
import { ref } from 'vue'
|
|
564
|
+
|
|
565
|
+
const users = ref([])
|
|
566
|
+
|
|
567
|
+
function deleteUser(username) {
|
|
568
|
+
users.value = users.value.filter(user => user.username !== username)
|
|
569
|
+
}
|
|
570
|
+
</script>
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## Интерфейс компонента
|
|
574
|
+
|
|
575
|
+
### Свойства (Props)
|
|
576
|
+
|
|
577
|
+
| Название | Тип | По умолчанию | Описание |
|
|
578
|
+
|----------|-----|--------------|----------|
|
|
579
|
+
| data | `T[] \| Record<string \| number, T>` | `undefined` | Массив или объект данных для итерации (используется вместе со слотом `#row`). |
|
|
580
|
+
| hoverable | boolean | `false` | Подсветка строки при наведении курсора. |
|
|
581
|
+
| striped | boolean | `false` | Чередование цвета фона строк (выделение четных строк). |
|
|
582
|
+
| bordered | boolean | `false` | Отрисовка границ (бордеров) у всех ячеек. |
|
|
583
|
+
| nodata | string | `'Ничего не найдено'` | Текст, выводимый, когда `data` пустое. |
|
|
584
|
+
| fixedHeader | boolean | `false` | Фиксированная шапка (принудительно), если не задана `height`. |
|
|
585
|
+
| height | string | `undefined` | Фиксированная высота таблицы (например, `300px` или `80vh`). Автоматически включает `fixedHeader`. |
|
|
586
|
+
| topScroll | boolean | `false` | Переносит горизонтальный скролл наверх таблицы. |
|
|
587
|
+
|
|
588
|
+
### Слоты (Slots)
|
|
589
|
+
|
|
590
|
+
| Название | Описание |
|
|
591
|
+
|----------|-------------|
|
|
592
|
+
| header | Содержимое `<tr/>` внутри `<thead>`. Подходит для большинства таблиц (без rowspan/colspan в шапке). |
|
|
593
|
+
| headers | Полный контроль над `<thead>` (вместо `header`). Нужно явно писать теги `<tr>`. |
|
|
594
|
+
| row | Слот с областью видимости `{ row, index }`. Отрисовывается для каждого элемента из `data`. Внутри нужно писать только `<td>`. |
|
|
595
|
+
| default | Переопределяет содержимое `<tbody>`. Используется, если не передан `data`. Нужно явно писать теги `<tr>`. |
|
|
596
|
+
| footer | Содержимое `<tr/>` внутри `<tfoot>`. |
|
|
597
|
+
| footers | Полный контроль над `<tfoot>` (содержит теги `<tr>`). |
|
|
598
|
+
| nodata | Кастомный HTML для пустого состояния, заменяет пропс `nodata`. |
|
|
599
|
+
|
|
600
|
+
<style lang="scss">
|
|
601
|
+
.vp-doc table {
|
|
602
|
+
overflow-x: visible;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.vp-doc td {
|
|
606
|
+
border: none;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.vp-doc table h3 {
|
|
610
|
+
margin: 0;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.s-demo-preview .s-table,
|
|
614
|
+
.s-demo-preview .s-table table {
|
|
615
|
+
margin: 0 !important;
|
|
616
|
+
}
|
|
617
|
+
.s-demo-preview .s-table tr {
|
|
618
|
+
border-top: 0 !important;
|
|
619
|
+
background-color: transparent;
|
|
620
|
+
}
|
|
621
|
+
.s-demo-preview .s-table td {
|
|
622
|
+
border-bottom: 1px solid var(--s-border) !important;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.s-table tfoot {
|
|
626
|
+
color: var(--s-text) !important;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
.table-container .s-table > table {
|
|
630
|
+
width: 120%;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.dark .s-table.striped {
|
|
634
|
+
tbody tr:nth-of-type(even) {
|
|
635
|
+
background-color: #282828 !important;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
</style>
|