shared-ritm 1.2.79 → 1.2.81

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.
Files changed (56) hide show
  1. package/README.md +103 -103
  2. package/dist/index.css +1 -1
  3. package/dist/shared-ritm.es.js +9409 -8849
  4. package/dist/shared-ritm.umd.js +520 -520
  5. package/dist/types/api/services/UserService.d.ts +10 -1
  6. package/dist/types/api/types/Api_Service.d.ts +1 -1
  7. package/dist/types/api/types/Api_User.d.ts +11 -0
  8. package/dist/types/common/app-table/controllers/useTableModel.d.ts +1 -1
  9. package/dist/types/index.d.ts +3 -2
  10. package/package.json +64 -64
  11. package/src/api/services/PhotoService.ts +137 -137
  12. package/src/api/services/RepairsService.ts +119 -119
  13. package/src/api/services/UserService.ts +32 -19
  14. package/src/api/types/Api_Controls.ts +72 -72
  15. package/src/api/types/Api_Files.ts +1 -1
  16. package/src/api/types/Api_Instruments.ts +98 -98
  17. package/src/api/types/Api_Projects.ts +55 -55
  18. package/src/api/types/Api_Repairs.ts +115 -115
  19. package/src/api/types/Api_Service.ts +1 -1
  20. package/src/api/types/Api_User.ts +57 -44
  21. package/src/common/app-checkbox/AppCheckbox.vue +26 -26
  22. package/src/common/app-datepicker/AppDatepicker.vue +165 -165
  23. package/src/common/app-dialogs/AppConfirmDialog.vue +99 -99
  24. package/src/common/app-dropdown/AppDropdown.vue +31 -31
  25. package/src/common/app-input/AppInput.vue +148 -148
  26. package/src/common/app-input-new/AppInputNew.vue +152 -152
  27. package/src/common/app-select/AppSelect.vue +157 -157
  28. package/src/common/app-sheet/AppSheet.vue +120 -120
  29. package/src/common/app-sheet-new/AppSheetNew.vue +255 -0
  30. package/src/common/app-sidebar/components/SidebarMenuItem.vue +148 -148
  31. package/src/common/app-table/AppTable.vue +301 -297
  32. package/src/common/app-table/AppTableLayout.vue +126 -111
  33. package/src/common/app-table/components/ModalSelect.vue +270 -270
  34. package/src/common/app-table/components/TableModal.vue +329 -329
  35. package/src/common/app-table/components/TablePagination.vue +152 -152
  36. package/src/common/app-table/components/TableSearch.vue +76 -76
  37. package/src/common/app-table/controllers/useBaseTable.ts +42 -42
  38. package/src/common/app-table/controllers/useColumnSelector.ts +38 -38
  39. package/src/common/app-table/controllers/useTableModel.ts +93 -93
  40. package/src/common/app-toggle/AppToggle.vue +24 -24
  41. package/src/common/app-wrapper/AppWrapper.vue +28 -28
  42. package/src/icons/components/table-filter-icon.vue +30 -30
  43. package/src/icons/dialogs/RemoveIcon.vue +12 -12
  44. package/src/icons/dialogs/SafetyIcon.vue +12 -12
  45. package/src/icons/task/attention-icon.vue +13 -13
  46. package/src/icons/task/clock-icon.vue +10 -10
  47. package/src/icons/task/delete-icon.vue +10 -10
  48. package/src/icons/task/fire-icon.vue +16 -16
  49. package/src/index.ts +3 -1
  50. package/src/main.ts +28 -28
  51. package/src/shared/styles/general.css +125 -125
  52. package/src/styles/variables.sass +12 -12
  53. package/src/utils/confirm.ts +12 -12
  54. package/src/utils/helpers.ts +58 -58
  55. package/src/utils/notification.ts +9 -9
  56. package/dist/types/api/types/Api_Users.d.ts +0 -43
@@ -1,297 +1,301 @@
1
- <template>
2
- <q-page class="flex flex-col" style="height: 100%; min-height: 100%">
3
- <q-table
4
- v-model:selected="selected"
5
- :rows="rows.value"
6
- :columns="resCols"
7
- row-key="id"
8
- flat
9
- bordered
10
- hide-bottom
11
- class="full-width"
12
- :rows-per-page-options="[0]"
13
- :selection="props.enableMultiSelect ? 'multiple' : 'none'"
14
- style="height: 100%"
15
- @row-click="rowClick"
16
- >
17
- <template #header-selection="scope">
18
- <q-toggle v-model="scope.selected" />
19
- </template>
20
-
21
- <template #body-selection="scope">
22
- <q-toggle v-model="scope.selected" />
23
- </template>
24
-
25
- <template #body-cell-index="cellProps">
26
- <q-td :props="cellProps" class="text-center" :class="{ 'q-td--no-hover': noHover }">
27
- {{ cellProps.rowIndex + 1 + (meta.value.currentPage - 1) * meta.value.perPage }}
28
- </q-td>
29
- </template>
30
-
31
- <template v-for="col in resCols" :key="col.name" #[`header-cell-${col.name}`]="propsSlot">
32
- <q-th :props="propsSlot" :class="{ 'cursor-pointer': col.filterType, actions: col.name === 'actions' }">
33
- <div
34
- v-if="col.filterType"
35
- class="row items-center no-wrap"
36
- @click.stop="emit('open-filter-menu', col.name, !filterMenus.value[col.name])"
37
- >
38
- <filter-icon class="q-mr-xs" />
39
- <span>{{ col.label }}</span>
40
- <div
41
- v-if="['multi', 'single'].includes(col.filterType) && columnFilters.value[col.name]?.length"
42
- class="label-length"
43
- >
44
- - {{ col.filterType === 'multi' ? columnFilters.value[col.name]?.length : 1 }}
45
- </div>
46
- </div>
47
- <div v-else>{{ col.label }}</div>
48
-
49
- <q-menu
50
- v-if="col.filterType"
51
- :model-value="filterMenus.value[col.name]"
52
- fit
53
- max-height="300px"
54
- class="filter-menu"
55
- @update:model-value="isOpen => emit('open-filter-menu', col.name, isOpen)"
56
- >
57
- <div class="filter-content">
58
- <q-input v-model="localSearches[col.name]" dense outlined placeholder="Поиск" class="q-mb-sm" clearable />
59
- <q-scroll-area style="height: 200px">
60
- <q-list style="min-width: 200px">
61
- <q-item v-for="option in filteredOptions[col.name]" :key="`${col.name}-${option.id}`" tag="label">
62
- <q-item-section avatar>
63
- <q-checkbox
64
- v-if="col.filterType === 'multi'"
65
- dense
66
- :model-value="
67
- Array.isArray(columnFilters.value[col.name]) &&
68
- columnFilters.value[col.name]?.includes(option.name)
69
- "
70
- @update:model-value="() => emit('toggle-filter-value', col.name, option.name)"
71
- />
72
- <q-radio
73
- v-else
74
- dense
75
- :model-value="columnFilters.value[col.name]"
76
- :val="option.name"
77
- @update:model-value="emit('toggle-filter-value', col.name, $event)"
78
- />
79
- </q-item-section>
80
- <q-item-section>{{ option.name }}</q-item-section>
81
- </q-item>
82
- </q-list>
83
- </q-scroll-area>
84
-
85
- <div class="filter-footer">
86
- <q-btn color="negative" flat dense label="Сбросить фильтр" @click="emit('clear-filter', col.name)" />
87
- </div>
88
- </div>
89
- </q-menu>
90
- </q-th>
91
- </template>
92
-
93
- <template #body-cell="cellProps">
94
- <q-td
95
- :props="cellProps"
96
- :class="{
97
- 'q-td--no-hover': noHover || cellProps.col.name === 'actions',
98
- actions: cellProps.col.name === 'actions',
99
- }"
100
- >
101
- <div v-if="cellProps.col.name === 'actions'" class="actions__btns">
102
- <q-btn square icon="edit" size="sm" unelevated title="Редактировать" @click="emit('edit', cellProps.row)" />
103
- </div>
104
- <q-badge
105
- v-else-if="cellProps.col.badge && typeof cellProps.value === 'boolean'"
106
- :color="
107
- cellProps.col.badge.colorTrue && cellProps.value
108
- ? cellProps.col.badge.colorTrue
109
- : cellProps.col.badge.colorFalse && !cellProps.value
110
- ? cellProps.col.badge.colorFalse
111
- : cellProps.value
112
- ? 'green'
113
- : 'red'
114
- "
115
- outline
116
- class="text-bold"
117
- >
118
- {{ cellProps.value ? cellProps.col.badge.true ?? 'Да' : cellProps.col.badge.false ?? 'Нет' }}
119
- </q-badge>
120
- <span v-else-if="cellProps.col.html" v-html="cellProps.value"></span>
121
- <span v-else>{{ cellProps.value }}</span>
122
- </q-td>
123
- </template>
124
- </q-table>
125
- </q-page>
126
- </template>
127
- <script setup lang="ts">
128
- import { defineProps, defineEmits, ref, computed, watch } from 'vue'
129
- import type { Ref } from 'vue'
130
- import FilterIcon from '@/icons/components/table-filter-icon.vue'
131
-
132
- interface FilterOption {
133
- id: string
134
- name: string
135
- }
136
-
137
- interface TableEmits {
138
- 'toggle-filter-value': [colName: string, value: string]
139
- 'clear-filter': [colName: string]
140
- 'open-filter-menu': [colName: string, isOpen: boolean]
141
- 'row-click': [row: Record<string, any>]
142
- 'update:selectedRows': [rows: any[]] // синтаксис defineEmits для эмита с именованными параметрами
143
- edit: [row: Record<string, any>]
144
- }
145
-
146
- const props = defineProps<{
147
- rows: Ref<any[]>
148
- columns: any[]
149
- columnFilters: Ref<Record<string, string | string[] | undefined>>
150
- filterMenus: Ref<Record<string, boolean>>
151
- filtersOptions: Record<string, FilterOption[]>
152
- meta: Ref<{ currentPage: number; perPage: number }>
153
- enableMultiSelect?: boolean
154
- noHover?: boolean
155
- selectedRows: any[]
156
- actions?: string[]
157
- }>()
158
- const emit = defineEmits<TableEmits>()
159
-
160
- const localSearches = ref<Record<string, string>>({})
161
- const selected = ref<any[]>([])
162
-
163
- const resCols = computed(() => {
164
- if (!props.actions?.length) return props.columns
165
-
166
- return [...props.columns, { name: 'actions', label: 'Действия', align: 'center' }]
167
- })
168
-
169
- const filteredOptions = computed(() => {
170
- const result: Record<string, FilterOption[]> = {}
171
- for (const col of props.columns) {
172
- const search = localSearches.value[col.name]?.toLowerCase() || ''
173
- const options = props.filtersOptions[col.name] || []
174
- result[col.name] = options.filter(opt => opt.name.toLowerCase().includes(search))
175
- }
176
- return result
177
- })
178
-
179
- const rowClick = (e: Event, row: Record<string, any>) => {
180
- const target = e?.target
181
-
182
- if (!(target instanceof HTMLElement) || target.closest('.actions') || document.getSelection()?.toString()) return
183
-
184
- emit('row-click', row)
185
- }
186
-
187
- watch(
188
- () => props.selectedRows,
189
- val => {
190
- selected.value = val
191
- },
192
- { immediate: true },
193
- )
194
-
195
- watch(
196
- selected,
197
- val => {
198
- emit('update:selectedRows', val)
199
- },
200
- { deep: true },
201
- )
202
- </script>
203
- <style scoped lang="scss">
204
- .actions {
205
- width: 100px;
206
- background-color: #f2f7fb;
207
- position: sticky;
208
- right: 0;
209
- z-index: 1;
210
- border-left: 1px solid #d7e0ef;
211
-
212
- &__btns {
213
- display: flex;
214
- justify-content: center;
215
- gap: 0.5rem;
216
-
217
- button {
218
- background-color: #a4b4cf50;
219
- width: 32px;
220
- height: 32px;
221
- border-radius: 8px;
222
- }
223
- }
224
- }
225
-
226
- .cursor-pointer {
227
- cursor: pointer;
228
- }
229
-
230
- .filter-menu {
231
- padding: 10px;
232
- }
233
- .label-length {
234
- margin-left: 5px;
235
- }
236
- .filter-content {
237
- display: flex;
238
- flex-direction: column;
239
- color: #1d425d;
240
- .q-item {
241
- padding: 0 5px;
242
- min-height: 40px;
243
- }
244
- .q-item__section {
245
- min-width: 30px;
246
- padding: 0;
247
- }
248
- ::v-deep(.q-radio__inner),
249
- ::v-deep(.q-checkbox__inner) {
250
- color: #a4b4cf;
251
- }
252
- }
253
-
254
- .filter-footer {
255
- border-top: 1px solid #eee;
256
- display: flex;
257
- justify-content: center;
258
- }
259
-
260
- ::v-deep(.q-table thead) {
261
- position: sticky;
262
- top: 0;
263
- background: #f2f7fb;
264
- z-index: 2;
265
- }
266
-
267
- ::v-deep(.q-table th) {
268
- font-family: NunitoSansFont, sans-serif;
269
- color: #a4b4cf;
270
- font-size: 15px;
271
- font-style: normal;
272
- font-weight: 700;
273
- line-height: 20px;
274
- height: 61px;
275
- }
276
-
277
- ::v-deep(.q-table tbody td) {
278
- height: 61px;
279
- font-size: 14px;
280
- }
281
-
282
- .q-td--no-hover {
283
- cursor: default;
284
- }
285
-
286
- ::v-deep(.q-table tbody) {
287
- font-family: NunitoSansFont, sans-serif;
288
- border-color: #d7e0ef;
289
- color: #1d425d;
290
- line-height: 20px;
291
- font-size: 14px;
292
- }
293
-
294
- ::v-deep(.q-table tbody tr:last-child td) {
295
- border-bottom: 1px solid #d7e0ef;
296
- }
297
- </style>
1
+ <template>
2
+ <div class="flex flex-col" style="height: 100%; min-height: 100%">
3
+ <q-table
4
+ v-model:selected="selected"
5
+ :rows="rows.value"
6
+ :columns="resCols"
7
+ row-key="id"
8
+ flat
9
+ bordered
10
+ hide-bottom
11
+ class="full-width"
12
+ :rows-per-page-options="[0]"
13
+ :selection="props.enableMultiSelect ? 'multiple' : 'none'"
14
+ style="height: 100%"
15
+ @row-click="rowClick"
16
+ >
17
+ <template #header-selection="scope">
18
+ <q-toggle v-model="scope.selected" />
19
+ </template>
20
+
21
+ <template #body-selection="scope">
22
+ <q-toggle v-model="scope.selected" />
23
+ </template>
24
+
25
+ <template #body-cell-index="cellProps">
26
+ <q-td :props="cellProps" class="text-center" :class="{ 'q-td--no-hover': noHover }">
27
+ {{ cellProps.rowIndex + 1 + (meta.value.currentPage - 1) * meta.value.perPage }}
28
+ </q-td>
29
+ </template>
30
+
31
+ <template v-for="col in resCols" :key="col.name" #[`header-cell-${col.name}`]="propsSlot">
32
+ <q-th :props="propsSlot" :class="{ 'cursor-pointer': col.filterType, actions: col.name === 'actions' }">
33
+ <div
34
+ v-if="col.filterType"
35
+ class="row items-center no-wrap"
36
+ @click.stop="emit('open-filter-menu', col.name, !filterMenus.value[col.name])"
37
+ >
38
+ <filter-icon class="q-mr-xs" />
39
+ <span>{{ col.label }}</span>
40
+ <div
41
+ v-if="['multi', 'single'].includes(col.filterType) && columnFilters.value[col.name]?.length"
42
+ class="label-length"
43
+ >
44
+ - {{ col.filterType === 'multi' ? columnFilters.value[col.name]?.length : 1 }}
45
+ </div>
46
+ </div>
47
+ <div v-else>{{ col.label }}</div>
48
+
49
+ <q-menu
50
+ v-if="col.filterType"
51
+ :model-value="filterMenus.value[col.name]"
52
+ fit
53
+ max-height="300px"
54
+ class="filter-menu"
55
+ @update:model-value="isOpen => emit('open-filter-menu', col.name, isOpen)"
56
+ >
57
+ <div class="filter-content">
58
+ <q-input v-model="localSearches[col.name]" dense outlined placeholder="Поиск" class="q-mb-sm" clearable />
59
+ <q-scroll-area style="height: 200px">
60
+ <q-list style="min-width: 200px">
61
+ <q-item v-for="option in filteredOptions[col.name]" :key="`${col.name}-${option.id}`" tag="label">
62
+ <q-item-section avatar>
63
+ <q-checkbox
64
+ v-if="col.filterType === 'multi'"
65
+ dense
66
+ :model-value="
67
+ Array.isArray(columnFilters.value[col.name]) &&
68
+ columnFilters.value[col.name]?.includes(option.name)
69
+ "
70
+ @update:model-value="() => emit('toggle-filter-value', col.name, option.name)"
71
+ />
72
+ <q-radio
73
+ v-else
74
+ dense
75
+ :model-value="columnFilters.value[col.name]"
76
+ :val="option.name"
77
+ @update:model-value="emit('toggle-filter-value', col.name, $event)"
78
+ />
79
+ </q-item-section>
80
+ <q-item-section>{{ option.name }}</q-item-section>
81
+ </q-item>
82
+ </q-list>
83
+ </q-scroll-area>
84
+
85
+ <div class="filter-footer">
86
+ <q-btn color="negative" flat dense label="Сбросить фильтр" @click="emit('clear-filter', col.name)" />
87
+ </div>
88
+ </div>
89
+ </q-menu>
90
+ </q-th>
91
+ </template>
92
+
93
+ <template #body-cell="cellProps">
94
+ <q-td
95
+ :props="cellProps"
96
+ :class="{
97
+ 'q-td--no-hover': noHover || cellProps.col.name === 'actions',
98
+ actions: cellProps.col.name === 'actions',
99
+ }"
100
+ >
101
+ <div v-if="cellProps.col.name === 'actions'" class="actions__btns">
102
+ <q-btn square icon="edit" size="sm" unelevated title="Редактировать" @click="emit('edit', cellProps.row)" />
103
+ </div>
104
+ <q-badge
105
+ v-else-if="cellProps.col.badge && typeof cellProps.value === 'boolean'"
106
+ :color="
107
+ cellProps.col.badge.colorTrue && cellProps.value
108
+ ? cellProps.col.badge.colorTrue
109
+ : cellProps.col.badge.colorFalse && !cellProps.value
110
+ ? cellProps.col.badge.colorFalse
111
+ : cellProps.value
112
+ ? 'green'
113
+ : 'red'
114
+ "
115
+ outline
116
+ class="text-bold"
117
+ >
118
+ {{ cellProps.value ? cellProps.col.badge.true ?? 'Да' : cellProps.col.badge.false ?? 'Нет' }}
119
+ </q-badge>
120
+ <span v-else-if="cellProps.col.html" v-html="cellProps.value"></span>
121
+ <slot v-else-if="hasSlot(cellProps.col.name)" :name="cellProps.col.name" :cellProps="cellProps" />
122
+ <span v-else>{{ cellProps.value }}</span>
123
+ </q-td>
124
+ </template>
125
+ </q-table>
126
+ </div>
127
+ </template>
128
+ <script setup lang="ts">
129
+ import { defineProps, defineEmits, ref, computed, watch, Slots } from 'vue'
130
+ import type { Ref } from 'vue'
131
+ import FilterIcon from '@/icons/components/table-filter-icon.vue'
132
+
133
+ interface FilterOption {
134
+ id: string
135
+ name: string
136
+ }
137
+
138
+ interface TableEmits {
139
+ 'toggle-filter-value': [colName: string, value: string]
140
+ 'clear-filter': [colName: string]
141
+ 'open-filter-menu': [colName: string, isOpen: boolean]
142
+ 'row-click': [row: Record<string, any>]
143
+ 'update:selectedRows': [rows: any[]] // синтаксис defineEmits для эмита с именованными параметрами
144
+ edit: [row: Record<string, any>]
145
+ }
146
+
147
+ const props = defineProps<{
148
+ rows: Ref<any[]>
149
+ columns: any[]
150
+ columnFilters: Ref<Record<string, string | string[] | undefined>>
151
+ filterMenus: Ref<Record<string, boolean>>
152
+ filtersOptions: Record<string, FilterOption[]>
153
+ meta: Ref<{ currentPage: number; perPage: number }>
154
+ enableMultiSelect?: boolean
155
+ noHover?: boolean
156
+ selectedRows: any[]
157
+ actions?: string[]
158
+ slots?: Slots
159
+ }>()
160
+ const emit = defineEmits<TableEmits>()
161
+
162
+ const localSearches = ref<Record<string, string>>({})
163
+ const selected = ref<any[]>([])
164
+
165
+ const resCols = computed(() => {
166
+ if (!props.actions?.length) return props.columns
167
+
168
+ return [...props.columns, { name: 'actions', label: 'Действия', align: 'center' }]
169
+ })
170
+
171
+ const filteredOptions = computed(() => {
172
+ const result: Record<string, FilterOption[]> = {}
173
+ for (const col of props.columns) {
174
+ const search = localSearches.value[col.name]?.toLowerCase() || ''
175
+ const options = props.filtersOptions[col.name] || []
176
+ result[col.name] = options.filter(opt => opt.name.toLowerCase().includes(search))
177
+ }
178
+ return result
179
+ })
180
+
181
+ const hasSlot = (name: string) => props.slots && name in props.slots
182
+
183
+ const rowClick = (e: Event, row: Record<string, any>) => {
184
+ const target = e?.target
185
+
186
+ if (!(target instanceof HTMLElement) || target.closest('.actions') || document.getSelection()?.toString()) return
187
+
188
+ emit('row-click', row)
189
+ }
190
+
191
+ watch(
192
+ () => props.selectedRows,
193
+ val => {
194
+ selected.value = val
195
+ },
196
+ { immediate: true },
197
+ )
198
+
199
+ watch(
200
+ selected,
201
+ val => {
202
+ emit('update:selectedRows', val)
203
+ },
204
+ { deep: true },
205
+ )
206
+ </script>
207
+ <style scoped lang="scss">
208
+ .actions {
209
+ width: 100px;
210
+ background-color: #f2f7fb;
211
+ position: sticky;
212
+ right: 0;
213
+ z-index: 1;
214
+ border-left: 1px solid #d7e0ef;
215
+
216
+ &__btns {
217
+ display: flex;
218
+ justify-content: center;
219
+ gap: 0.5rem;
220
+
221
+ button {
222
+ background-color: #a4b4cf50;
223
+ width: 32px;
224
+ height: 32px;
225
+ border-radius: 8px;
226
+ }
227
+ }
228
+ }
229
+
230
+ .cursor-pointer {
231
+ cursor: pointer;
232
+ }
233
+
234
+ .filter-menu {
235
+ padding: 10px;
236
+ }
237
+ .label-length {
238
+ margin-left: 5px;
239
+ }
240
+ .filter-content {
241
+ display: flex;
242
+ flex-direction: column;
243
+ color: #1d425d;
244
+ .q-item {
245
+ padding: 0 5px;
246
+ min-height: 40px;
247
+ }
248
+ .q-item__section {
249
+ min-width: 30px;
250
+ padding: 0;
251
+ }
252
+ ::v-deep(.q-radio__inner),
253
+ ::v-deep(.q-checkbox__inner) {
254
+ color: #a4b4cf;
255
+ }
256
+ }
257
+
258
+ .filter-footer {
259
+ border-top: 1px solid #eee;
260
+ display: flex;
261
+ justify-content: center;
262
+ }
263
+
264
+ ::v-deep(.q-table thead) {
265
+ position: sticky;
266
+ top: 0;
267
+ background: #f2f7fb;
268
+ z-index: 2;
269
+ }
270
+
271
+ ::v-deep(.q-table th) {
272
+ font-family: NunitoSansFont, sans-serif;
273
+ color: #a4b4cf;
274
+ font-size: 15px;
275
+ font-style: normal;
276
+ font-weight: 700;
277
+ line-height: 20px;
278
+ height: 61px;
279
+ }
280
+
281
+ ::v-deep(.q-table tbody td) {
282
+ height: 61px;
283
+ font-size: 14px;
284
+ }
285
+
286
+ .q-td--no-hover {
287
+ cursor: default;
288
+ }
289
+
290
+ ::v-deep(.q-table tbody) {
291
+ font-family: NunitoSansFont, sans-serif;
292
+ border-color: #d7e0ef;
293
+ color: #1d425d;
294
+ line-height: 20px;
295
+ font-size: 14px;
296
+ }
297
+
298
+ ::v-deep(.q-table tbody tr:last-child td) {
299
+ border-bottom: 1px solid #d7e0ef;
300
+ }
301
+ </style>