shared-ritm 1.3.78 → 1.3.79
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/dist/index.css +1 -1
- package/dist/shared-ritm.es.js +15 -14
- package/dist/shared-ritm.umd.js +2 -2
- package/package.json +70 -70
- package/src/App.vue +2461 -2461
- package/src/api/services/AuthService.ts +67 -67
- package/src/api/services/ControlsService.ts +96 -96
- package/src/api/services/EquipmentService.ts +29 -29
- package/src/api/services/MetricsService.ts +143 -143
- package/src/api/services/RepairsService.ts +111 -111
- package/src/api/services/UserIssueService.ts +32 -32
- package/src/api/services/UserService.ts +129 -129
- package/src/api/services/VideoService.ts +118 -118
- package/src/api/settings/ApiService.ts +184 -184
- package/src/api/types/Api_Auth.ts +121 -121
- package/src/api/types/Api_Metrics.ts +51 -51
- package/src/api/types/Api_Repairs.ts +187 -187
- package/src/api/types/Api_Tasks.ts +376 -376
- package/src/api/types/Api_User.ts +156 -156
- package/src/api/types/Api_User_Issue.ts +36 -36
- package/src/api/types/Api_Video.ts +244 -244
- package/src/common/app-button/Button.stories.ts +369 -369
- package/src/common/app-checkbox/AppCheckbox.vue +31 -31
- package/src/common/app-checkbox/Checkbox.stories.ts +252 -252
- package/src/common/app-date-picker/DatePicker.stories.ts +66 -66
- package/src/common/app-datepicker/Datepicker.stories.ts +145 -145
- package/src/common/app-dialogs/AppConfirmDialog.vue +109 -109
- package/src/common/app-dialogs/Confirm.stories.ts +93 -93
- package/src/common/app-dropdown/Dropdown.stories.ts +94 -94
- package/src/common/app-file/File.stories.ts +104 -104
- package/src/common/app-icon/AppIcon.vue +110 -110
- package/src/common/app-icon/Icon.stories.ts +91 -91
- package/src/common/app-input/AppInput.vue +2 -0
- package/src/common/app-input/Input.stories.ts +160 -160
- package/src/common/app-input-new/AppInputNew.vue +181 -181
- package/src/common/app-input-new/InputNew.stories.ts +240 -240
- package/src/common/app-input-search/InputSearch.stories.ts +149 -149
- package/src/common/app-layout/components/AppLayoutHeader.vue +289 -289
- package/src/common/app-loader/Loader.stories.ts +114 -114
- package/src/common/app-select/AppSelect.vue +159 -159
- package/src/common/app-select/Select.stories.ts +155 -155
- package/src/common/app-sidebar/AppSidebar.vue +174 -174
- package/src/common/app-table/AppTable.vue +313 -313
- package/src/common/app-table/components/ModalSelect.stories.ts +323 -323
- package/src/common/app-table/components/ModalSelect.vue +302 -302
- package/src/common/app-table/components/TableModal.vue +367 -367
- package/src/common/app-table/controllers/useColumnSelector.ts +45 -45
- package/src/common/app-table/controllers/useTableModel.ts +97 -97
- package/src/common/app-toggle/AppToggle.vue +12 -12
- package/src/common/app-toggle/Toggle.stories.ts +245 -245
- package/src/common/app-wrapper/AppWrapper.vue +31 -31
- package/src/configs/storybook.ts +14 -14
- package/src/icons/sidebar/user-requests-icon.vue +23 -23
- package/src/index.ts +134 -134
- package/src/shared/styles/general.css +140 -140
- package/src/styles/variables.sass +12 -12
- package/src/utils/files.ts +38 -38
- package/src/utils/helpers.ts +59 -59
- package/dist/types/api/services/PhotoService.d.ts +0 -40
- package/dist/types/stories/Button.stories.d.ts +0 -13
- package/dist/types/stories/Checkbox.stories.d.ts +0 -7
- package/dist/types/stories/Confirm.stories.d.ts +0 -8
- package/dist/types/stories/DatePicker.stories.d.ts +0 -8
- package/dist/types/stories/Dropdown.stories.d.ts +0 -8
- package/dist/types/stories/File.stories.d.ts +0 -8
- package/dist/types/stories/Icon.stories.d.ts +0 -7
- package/dist/types/stories/Input.stories.d.ts +0 -11
- package/dist/types/stories/InputNew.stories.d.ts +0 -12
- package/dist/types/stories/InputSearch.stories.d.ts +0 -10
- package/dist/types/stories/Loader.stories.d.ts +0 -8
- package/dist/types/stories/Select.stories.d.ts +0 -7
- package/dist/types/stories/Toggle.stories.d.ts +0 -8
|
@@ -1,155 +1,155 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
-
import AppSelect from '@/common/app-select/AppSelect.vue'
|
|
3
|
-
|
|
4
|
-
const SAMPLE_OPTIONS = [
|
|
5
|
-
{ id: 1, name: 'Проект Альфа', category: 'Проекты' },
|
|
6
|
-
{ id: 2, name: 'Ремонт №42', category: 'Ремонты' },
|
|
7
|
-
{ id: 3, name: 'Инструмент XYZ', category: 'Инструменты' },
|
|
8
|
-
{ id: 4, name: 'Объект "Центр"', category: 'Объекты' },
|
|
9
|
-
{ id: 5, name: 'Статус: Готов', category: 'Статусы' },
|
|
10
|
-
]
|
|
11
|
-
|
|
12
|
-
const meta: Meta<typeof AppSelect> = {
|
|
13
|
-
title: 'Components/AppSelect',
|
|
14
|
-
component: AppSelect,
|
|
15
|
-
tags: ['autodocs'],
|
|
16
|
-
argTypes: {
|
|
17
|
-
modelValue: {
|
|
18
|
-
control: 'object',
|
|
19
|
-
description: 'Выбранное значение (или массив при multiple).',
|
|
20
|
-
},
|
|
21
|
-
options: {
|
|
22
|
-
control: 'object',
|
|
23
|
-
description: 'Массив опций. Каждая опция — объект с полями, указанными в optionLabel/optionValue.',
|
|
24
|
-
},
|
|
25
|
-
label: {
|
|
26
|
-
control: 'text',
|
|
27
|
-
description: 'Подпись над селектом.',
|
|
28
|
-
},
|
|
29
|
-
placeholder: {
|
|
30
|
-
control: 'text',
|
|
31
|
-
},
|
|
32
|
-
emptyText: {
|
|
33
|
-
control: 'text',
|
|
34
|
-
description: 'Текст, отображаемый, если опции не найдены при поиске.',
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
multiple: {
|
|
38
|
-
control: 'boolean',
|
|
39
|
-
description: 'Множественный выбор.',
|
|
40
|
-
table: { category: '⚙️ Поведение' },
|
|
41
|
-
},
|
|
42
|
-
search: {
|
|
43
|
-
control: 'boolean',
|
|
44
|
-
description: 'Включить поиск по опциям.',
|
|
45
|
-
table: { category: '⚙️ Поведение' },
|
|
46
|
-
},
|
|
47
|
-
clearable: {
|
|
48
|
-
control: 'boolean',
|
|
49
|
-
description: 'Показывать крестик для сброса значения.',
|
|
50
|
-
table: { category: '⚙️ Поведение' },
|
|
51
|
-
},
|
|
52
|
-
isDisabled: {
|
|
53
|
-
control: 'boolean',
|
|
54
|
-
description: 'Отключить селект.',
|
|
55
|
-
table: { category: '⚙️ Поведение' },
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
optionLabel: {
|
|
59
|
-
control: 'text',
|
|
60
|
-
description: 'Ключ объекта опции, используемый как отображаемый текст (по умолчанию "label").',
|
|
61
|
-
table: { category: 'Опции' },
|
|
62
|
-
},
|
|
63
|
-
optionValue: {
|
|
64
|
-
control: 'text',
|
|
65
|
-
description: 'Ключ объекта опции, используемый как значение (по умолчанию сам объект).',
|
|
66
|
-
table: { category: 'Опции' },
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
borderColor: {
|
|
70
|
-
control: 'color',
|
|
71
|
-
description: 'Цвет обводки (в HEX/RGB).',
|
|
72
|
-
},
|
|
73
|
-
dataTest: {
|
|
74
|
-
control: 'text',
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
args: {
|
|
78
|
-
modelValue: null,
|
|
79
|
-
options: SAMPLE_OPTIONS,
|
|
80
|
-
label: 'Выберите элемент',
|
|
81
|
-
placeholder: 'Начните ввод...',
|
|
82
|
-
emptyText: 'Ничего не найдено',
|
|
83
|
-
optionLabel: 'name',
|
|
84
|
-
optionValue: 'id',
|
|
85
|
-
multiple: false,
|
|
86
|
-
search: true,
|
|
87
|
-
clearable: true,
|
|
88
|
-
isDisabled: false,
|
|
89
|
-
borderColor: '#007BFF',
|
|
90
|
-
dataTest: 'app-select',
|
|
91
|
-
},
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export default meta
|
|
95
|
-
|
|
96
|
-
type Story = StoryObj<typeof AppSelect>
|
|
97
|
-
|
|
98
|
-
export const Default: Story = {}
|
|
99
|
-
|
|
100
|
-
export const States: Story = {
|
|
101
|
-
render: () => ({
|
|
102
|
-
components: { AppSelect },
|
|
103
|
-
template: `
|
|
104
|
-
<div style="display: flex; flex-direction: column; gap: 24px;">
|
|
105
|
-
<AppSelect
|
|
106
|
-
v-model="value1"
|
|
107
|
-
:options="options"
|
|
108
|
-
label="Обычный"
|
|
109
|
-
placeholder="Выберите..."
|
|
110
|
-
:option-label="'name'"
|
|
111
|
-
:option-value="'id'"
|
|
112
|
-
empty-text="Нет данных"
|
|
113
|
-
/>
|
|
114
|
-
<AppSelect
|
|
115
|
-
v-model="value2"
|
|
116
|
-
:options="options"
|
|
117
|
-
label="С поиском"
|
|
118
|
-
placeholder="Начните ввод..."
|
|
119
|
-
:search="true"
|
|
120
|
-
:option-label="'name'"
|
|
121
|
-
:option-value="'id'"
|
|
122
|
-
empty-text="Нет данных"
|
|
123
|
-
/>
|
|
124
|
-
<AppSelect
|
|
125
|
-
v-model="value3"
|
|
126
|
-
:options="options"
|
|
127
|
-
label="Множественный"
|
|
128
|
-
placeholder="Выберите несколько..."
|
|
129
|
-
:multiple="true"
|
|
130
|
-
:option-label="'name'"
|
|
131
|
-
:option-value="'id'"
|
|
132
|
-
empty-text="Нет данных"
|
|
133
|
-
/>
|
|
134
|
-
<AppSelect
|
|
135
|
-
v-model="value4"
|
|
136
|
-
:options="options"
|
|
137
|
-
label="Отключён"
|
|
138
|
-
:is-disabled="true"
|
|
139
|
-
:option-label="'name'"
|
|
140
|
-
:option-value="'id'"
|
|
141
|
-
empty-text="Нет данных"
|
|
142
|
-
/>
|
|
143
|
-
</div>
|
|
144
|
-
`,
|
|
145
|
-
data() {
|
|
146
|
-
return {
|
|
147
|
-
options: SAMPLE_OPTIONS,
|
|
148
|
-
value1: 1,
|
|
149
|
-
value2: null,
|
|
150
|
-
value3: [2, 4],
|
|
151
|
-
value4: 3,
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
}),
|
|
155
|
-
}
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
+
import AppSelect from '@/common/app-select/AppSelect.vue'
|
|
3
|
+
|
|
4
|
+
const SAMPLE_OPTIONS = [
|
|
5
|
+
{ id: 1, name: 'Проект Альфа', category: 'Проекты' },
|
|
6
|
+
{ id: 2, name: 'Ремонт №42', category: 'Ремонты' },
|
|
7
|
+
{ id: 3, name: 'Инструмент XYZ', category: 'Инструменты' },
|
|
8
|
+
{ id: 4, name: 'Объект "Центр"', category: 'Объекты' },
|
|
9
|
+
{ id: 5, name: 'Статус: Готов', category: 'Статусы' },
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
const meta: Meta<typeof AppSelect> = {
|
|
13
|
+
title: 'Components/AppSelect',
|
|
14
|
+
component: AppSelect,
|
|
15
|
+
tags: ['autodocs'],
|
|
16
|
+
argTypes: {
|
|
17
|
+
modelValue: {
|
|
18
|
+
control: 'object',
|
|
19
|
+
description: 'Выбранное значение (или массив при multiple).',
|
|
20
|
+
},
|
|
21
|
+
options: {
|
|
22
|
+
control: 'object',
|
|
23
|
+
description: 'Массив опций. Каждая опция — объект с полями, указанными в optionLabel/optionValue.',
|
|
24
|
+
},
|
|
25
|
+
label: {
|
|
26
|
+
control: 'text',
|
|
27
|
+
description: 'Подпись над селектом.',
|
|
28
|
+
},
|
|
29
|
+
placeholder: {
|
|
30
|
+
control: 'text',
|
|
31
|
+
},
|
|
32
|
+
emptyText: {
|
|
33
|
+
control: 'text',
|
|
34
|
+
description: 'Текст, отображаемый, если опции не найдены при поиске.',
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
multiple: {
|
|
38
|
+
control: 'boolean',
|
|
39
|
+
description: 'Множественный выбор.',
|
|
40
|
+
table: { category: '⚙️ Поведение' },
|
|
41
|
+
},
|
|
42
|
+
search: {
|
|
43
|
+
control: 'boolean',
|
|
44
|
+
description: 'Включить поиск по опциям.',
|
|
45
|
+
table: { category: '⚙️ Поведение' },
|
|
46
|
+
},
|
|
47
|
+
clearable: {
|
|
48
|
+
control: 'boolean',
|
|
49
|
+
description: 'Показывать крестик для сброса значения.',
|
|
50
|
+
table: { category: '⚙️ Поведение' },
|
|
51
|
+
},
|
|
52
|
+
isDisabled: {
|
|
53
|
+
control: 'boolean',
|
|
54
|
+
description: 'Отключить селект.',
|
|
55
|
+
table: { category: '⚙️ Поведение' },
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
optionLabel: {
|
|
59
|
+
control: 'text',
|
|
60
|
+
description: 'Ключ объекта опции, используемый как отображаемый текст (по умолчанию "label").',
|
|
61
|
+
table: { category: 'Опции' },
|
|
62
|
+
},
|
|
63
|
+
optionValue: {
|
|
64
|
+
control: 'text',
|
|
65
|
+
description: 'Ключ объекта опции, используемый как значение (по умолчанию сам объект).',
|
|
66
|
+
table: { category: 'Опции' },
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
borderColor: {
|
|
70
|
+
control: 'color',
|
|
71
|
+
description: 'Цвет обводки (в HEX/RGB).',
|
|
72
|
+
},
|
|
73
|
+
dataTest: {
|
|
74
|
+
control: 'text',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
args: {
|
|
78
|
+
modelValue: null,
|
|
79
|
+
options: SAMPLE_OPTIONS,
|
|
80
|
+
label: 'Выберите элемент',
|
|
81
|
+
placeholder: 'Начните ввод...',
|
|
82
|
+
emptyText: 'Ничего не найдено',
|
|
83
|
+
optionLabel: 'name',
|
|
84
|
+
optionValue: 'id',
|
|
85
|
+
multiple: false,
|
|
86
|
+
search: true,
|
|
87
|
+
clearable: true,
|
|
88
|
+
isDisabled: false,
|
|
89
|
+
borderColor: '#007BFF',
|
|
90
|
+
dataTest: 'app-select',
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default meta
|
|
95
|
+
|
|
96
|
+
type Story = StoryObj<typeof AppSelect>
|
|
97
|
+
|
|
98
|
+
export const Default: Story = {}
|
|
99
|
+
|
|
100
|
+
export const States: Story = {
|
|
101
|
+
render: () => ({
|
|
102
|
+
components: { AppSelect },
|
|
103
|
+
template: `
|
|
104
|
+
<div style="display: flex; flex-direction: column; gap: 24px;">
|
|
105
|
+
<AppSelect
|
|
106
|
+
v-model="value1"
|
|
107
|
+
:options="options"
|
|
108
|
+
label="Обычный"
|
|
109
|
+
placeholder="Выберите..."
|
|
110
|
+
:option-label="'name'"
|
|
111
|
+
:option-value="'id'"
|
|
112
|
+
empty-text="Нет данных"
|
|
113
|
+
/>
|
|
114
|
+
<AppSelect
|
|
115
|
+
v-model="value2"
|
|
116
|
+
:options="options"
|
|
117
|
+
label="С поиском"
|
|
118
|
+
placeholder="Начните ввод..."
|
|
119
|
+
:search="true"
|
|
120
|
+
:option-label="'name'"
|
|
121
|
+
:option-value="'id'"
|
|
122
|
+
empty-text="Нет данных"
|
|
123
|
+
/>
|
|
124
|
+
<AppSelect
|
|
125
|
+
v-model="value3"
|
|
126
|
+
:options="options"
|
|
127
|
+
label="Множественный"
|
|
128
|
+
placeholder="Выберите несколько..."
|
|
129
|
+
:multiple="true"
|
|
130
|
+
:option-label="'name'"
|
|
131
|
+
:option-value="'id'"
|
|
132
|
+
empty-text="Нет данных"
|
|
133
|
+
/>
|
|
134
|
+
<AppSelect
|
|
135
|
+
v-model="value4"
|
|
136
|
+
:options="options"
|
|
137
|
+
label="Отключён"
|
|
138
|
+
:is-disabled="true"
|
|
139
|
+
:option-label="'name'"
|
|
140
|
+
:option-value="'id'"
|
|
141
|
+
empty-text="Нет данных"
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
`,
|
|
145
|
+
data() {
|
|
146
|
+
return {
|
|
147
|
+
options: SAMPLE_OPTIONS,
|
|
148
|
+
value1: 1,
|
|
149
|
+
value2: null,
|
|
150
|
+
value3: [2, 4],
|
|
151
|
+
value4: 3,
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
}),
|
|
155
|
+
}
|
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<q-drawer
|
|
3
|
-
:key="`${drawer}`"
|
|
4
|
-
v-model="drawer"
|
|
5
|
-
:class="$style.drawer"
|
|
6
|
-
:mini="isSidebarMini"
|
|
7
|
-
:overlay="!isFixSidebar"
|
|
8
|
-
:width="drawerWidth"
|
|
9
|
-
:mini-width="drawerMiniWidth"
|
|
10
|
-
:breakpoint="960"
|
|
11
|
-
show-if-above
|
|
12
|
-
persistent
|
|
13
|
-
@mouseover="toggleSidebarMini(false)"
|
|
14
|
-
@mouseout="toggleSidebarMini(true)"
|
|
15
|
-
>
|
|
16
|
-
<template v-if="isTablet">
|
|
17
|
-
<q-item :class="$style['tablet-logo']">
|
|
18
|
-
<q-item-section avatar>
|
|
19
|
-
<q-btn
|
|
20
|
-
round
|
|
21
|
-
unelevated
|
|
22
|
-
:class="$style['tablet-logo__btn']"
|
|
23
|
-
:icon="tabletLogoBtn"
|
|
24
|
-
@click="openDrawerIsTablet()"
|
|
25
|
-
/>
|
|
26
|
-
</q-item-section>
|
|
27
|
-
<q-item-section :class="$style['logo__text']">РИТМ</q-item-section>
|
|
28
|
-
</q-item>
|
|
29
|
-
</template>
|
|
30
|
-
<template v-else>
|
|
31
|
-
<q-item :class="$style.logo">
|
|
32
|
-
<q-item-section avatar>
|
|
33
|
-
<app-icon name="logo-icon" data-test="lotus" />
|
|
34
|
-
</q-item-section>
|
|
35
|
-
<q-item-section :class="$style['logo__text']">{{ isSidebarMini ? '' : 'РИТМ' }}</q-item-section>
|
|
36
|
-
</q-item>
|
|
37
|
-
<q-btn
|
|
38
|
-
dense
|
|
39
|
-
round
|
|
40
|
-
unelevated
|
|
41
|
-
class="q-mini-drawer-hide"
|
|
42
|
-
:class="$style['minify-btn']"
|
|
43
|
-
icon="chevron_left"
|
|
44
|
-
@click="isFixSidebar = !isFixSidebar"
|
|
45
|
-
>
|
|
46
|
-
<q-tooltip>Закрепить сайдбар</q-tooltip>
|
|
47
|
-
</q-btn>
|
|
48
|
-
</template>
|
|
49
|
-
<sidebar-menu :main="main" :menu-items="menuItems" :minify="isSidebarMini" :is-route-active="isRouteActive" />
|
|
50
|
-
<q-item v-ripple :class="$style['menu-exit']" clickable @click="logout()">
|
|
51
|
-
<q-item-section avatar>
|
|
52
|
-
<app-icon :name="'logout-icon'" color="white" size="24px" />
|
|
53
|
-
</q-item-section>
|
|
54
|
-
<q-item-section :class="$style['menu-exit__label']">Выход</q-item-section>
|
|
55
|
-
</q-item>
|
|
56
|
-
</q-drawer>
|
|
57
|
-
</template>
|
|
58
|
-
|
|
59
|
-
<script setup lang="ts">
|
|
60
|
-
import { computed, defineProps, defineEmits, ref, watch, withDefaults } from 'vue'
|
|
61
|
-
import SidebarMenu from './components/SidebarMenu.vue'
|
|
62
|
-
|
|
63
|
-
import { useQuasar } from 'quasar'
|
|
64
|
-
import AppIcon from '@/common/app-icon/AppIcon.vue'
|
|
65
|
-
|
|
66
|
-
interface Props {
|
|
67
|
-
isDrawer: boolean
|
|
68
|
-
minify?: boolean
|
|
69
|
-
menuItems?: any[]
|
|
70
|
-
main?: boolean
|
|
71
|
-
isRouteActive: (item: any) => boolean
|
|
72
|
-
}
|
|
73
|
-
const emits = defineEmits(['logout'])
|
|
74
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
75
|
-
main: false,
|
|
76
|
-
menuItems: () => [],
|
|
77
|
-
})
|
|
78
|
-
const $q = useQuasar()
|
|
79
|
-
|
|
80
|
-
const drawer = ref(true)
|
|
81
|
-
const isSidebarMini = ref(true)
|
|
82
|
-
const isFixSidebar = ref(false)
|
|
83
|
-
|
|
84
|
-
const isTablet = computed(() => $q.screen.lt.lg)
|
|
85
|
-
const tabletLogoBtn = computed(() => (isSidebarMini.value ? 'menu' : 'close'))
|
|
86
|
-
const drawerWidth = computed(() => (isTablet.value ? 250 : 275))
|
|
87
|
-
const drawerMiniWidth = computed(() => (isTablet.value ? 47 : 72))
|
|
88
|
-
|
|
89
|
-
function toggleSidebarMini(value: boolean): void {
|
|
90
|
-
if (!isFixSidebar.value && !isTablet.value) isSidebarMini.value = value
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function openDrawerIsTablet() {
|
|
94
|
-
isFixSidebar.value = !isFixSidebar.value
|
|
95
|
-
isSidebarMini.value = !isSidebarMini.value
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function logout(): void {
|
|
99
|
-
emits('logout')
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
watch(
|
|
103
|
-
() => isTablet.value,
|
|
104
|
-
(value: boolean) => {
|
|
105
|
-
isFixSidebar.value = value
|
|
106
|
-
},
|
|
107
|
-
)
|
|
108
|
-
</script>
|
|
109
|
-
|
|
110
|
-
<style lang="scss">
|
|
111
|
-
.q-drawer {
|
|
112
|
-
z-index: 3000;
|
|
113
|
-
}
|
|
114
|
-
</style>
|
|
115
|
-
|
|
116
|
-
<style lang="scss" module>
|
|
117
|
-
.drawer {
|
|
118
|
-
display: flex;
|
|
119
|
-
flex-direction: column;
|
|
120
|
-
height: 100vh;
|
|
121
|
-
background: linear-gradient(184deg, #0b3f8e 9.62%, #5386d3 59.23%, #5386d3 91.63%);
|
|
122
|
-
border-right: 1px solid #e4e6e8;
|
|
123
|
-
&::-webkit-scrollbar {
|
|
124
|
-
height: 4px;
|
|
125
|
-
width: 4px;
|
|
126
|
-
}
|
|
127
|
-
&::-webkit-scrollbar-thumb {
|
|
128
|
-
width: 4px;
|
|
129
|
-
height: 4px;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
.logo {
|
|
133
|
-
display: flex;
|
|
134
|
-
margin: 24px 0;
|
|
135
|
-
&__text {
|
|
136
|
-
color: white;
|
|
137
|
-
font-size: 20px;
|
|
138
|
-
font-weight: 700;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
.tablet-logo {
|
|
142
|
-
border-bottom: 1px solid white;
|
|
143
|
-
&__btn {
|
|
144
|
-
color: white;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
.minify-btn {
|
|
149
|
-
background: #0b3f8e;
|
|
150
|
-
color: white;
|
|
151
|
-
position: absolute;
|
|
152
|
-
top: 25px;
|
|
153
|
-
right: -17px;
|
|
154
|
-
|
|
155
|
-
&:global(.--mini) {
|
|
156
|
-
:global(.q-icon) {
|
|
157
|
-
transform: rotate(180deg);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
.menu-exit {
|
|
163
|
-
border-top: 1px solid white;
|
|
164
|
-
:global(.q-item__section--avatar) {
|
|
165
|
-
min-width: 24px;
|
|
166
|
-
}
|
|
167
|
-
&__label {
|
|
168
|
-
color: #fff;
|
|
169
|
-
font-family: 'Nunito Sans', sans-serif;
|
|
170
|
-
font-size: 16px;
|
|
171
|
-
font-weight: 300;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<q-drawer
|
|
3
|
+
:key="`${drawer}`"
|
|
4
|
+
v-model="drawer"
|
|
5
|
+
:class="$style.drawer"
|
|
6
|
+
:mini="isSidebarMini"
|
|
7
|
+
:overlay="!isFixSidebar"
|
|
8
|
+
:width="drawerWidth"
|
|
9
|
+
:mini-width="drawerMiniWidth"
|
|
10
|
+
:breakpoint="960"
|
|
11
|
+
show-if-above
|
|
12
|
+
persistent
|
|
13
|
+
@mouseover="toggleSidebarMini(false)"
|
|
14
|
+
@mouseout="toggleSidebarMini(true)"
|
|
15
|
+
>
|
|
16
|
+
<template v-if="isTablet">
|
|
17
|
+
<q-item :class="$style['tablet-logo']">
|
|
18
|
+
<q-item-section avatar>
|
|
19
|
+
<q-btn
|
|
20
|
+
round
|
|
21
|
+
unelevated
|
|
22
|
+
:class="$style['tablet-logo__btn']"
|
|
23
|
+
:icon="tabletLogoBtn"
|
|
24
|
+
@click="openDrawerIsTablet()"
|
|
25
|
+
/>
|
|
26
|
+
</q-item-section>
|
|
27
|
+
<q-item-section :class="$style['logo__text']">РИТМ</q-item-section>
|
|
28
|
+
</q-item>
|
|
29
|
+
</template>
|
|
30
|
+
<template v-else>
|
|
31
|
+
<q-item :class="$style.logo">
|
|
32
|
+
<q-item-section avatar>
|
|
33
|
+
<app-icon name="logo-icon" data-test="lotus" />
|
|
34
|
+
</q-item-section>
|
|
35
|
+
<q-item-section :class="$style['logo__text']">{{ isSidebarMini ? '' : 'РИТМ' }}</q-item-section>
|
|
36
|
+
</q-item>
|
|
37
|
+
<q-btn
|
|
38
|
+
dense
|
|
39
|
+
round
|
|
40
|
+
unelevated
|
|
41
|
+
class="q-mini-drawer-hide"
|
|
42
|
+
:class="$style['minify-btn']"
|
|
43
|
+
icon="chevron_left"
|
|
44
|
+
@click="isFixSidebar = !isFixSidebar"
|
|
45
|
+
>
|
|
46
|
+
<q-tooltip>Закрепить сайдбар</q-tooltip>
|
|
47
|
+
</q-btn>
|
|
48
|
+
</template>
|
|
49
|
+
<sidebar-menu :main="main" :menu-items="menuItems" :minify="isSidebarMini" :is-route-active="isRouteActive" />
|
|
50
|
+
<q-item v-ripple :class="$style['menu-exit']" clickable @click="logout()">
|
|
51
|
+
<q-item-section avatar>
|
|
52
|
+
<app-icon :name="'logout-icon'" color="white" size="24px" />
|
|
53
|
+
</q-item-section>
|
|
54
|
+
<q-item-section :class="$style['menu-exit__label']">Выход</q-item-section>
|
|
55
|
+
</q-item>
|
|
56
|
+
</q-drawer>
|
|
57
|
+
</template>
|
|
58
|
+
|
|
59
|
+
<script setup lang="ts">
|
|
60
|
+
import { computed, defineProps, defineEmits, ref, watch, withDefaults } from 'vue'
|
|
61
|
+
import SidebarMenu from './components/SidebarMenu.vue'
|
|
62
|
+
|
|
63
|
+
import { useQuasar } from 'quasar'
|
|
64
|
+
import AppIcon from '@/common/app-icon/AppIcon.vue'
|
|
65
|
+
|
|
66
|
+
interface Props {
|
|
67
|
+
isDrawer: boolean
|
|
68
|
+
minify?: boolean
|
|
69
|
+
menuItems?: any[]
|
|
70
|
+
main?: boolean
|
|
71
|
+
isRouteActive: (item: any) => boolean
|
|
72
|
+
}
|
|
73
|
+
const emits = defineEmits(['logout'])
|
|
74
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
75
|
+
main: false,
|
|
76
|
+
menuItems: () => [],
|
|
77
|
+
})
|
|
78
|
+
const $q = useQuasar()
|
|
79
|
+
|
|
80
|
+
const drawer = ref(true)
|
|
81
|
+
const isSidebarMini = ref(true)
|
|
82
|
+
const isFixSidebar = ref(false)
|
|
83
|
+
|
|
84
|
+
const isTablet = computed(() => $q.screen.lt.lg)
|
|
85
|
+
const tabletLogoBtn = computed(() => (isSidebarMini.value ? 'menu' : 'close'))
|
|
86
|
+
const drawerWidth = computed(() => (isTablet.value ? 250 : 275))
|
|
87
|
+
const drawerMiniWidth = computed(() => (isTablet.value ? 47 : 72))
|
|
88
|
+
|
|
89
|
+
function toggleSidebarMini(value: boolean): void {
|
|
90
|
+
if (!isFixSidebar.value && !isTablet.value) isSidebarMini.value = value
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function openDrawerIsTablet() {
|
|
94
|
+
isFixSidebar.value = !isFixSidebar.value
|
|
95
|
+
isSidebarMini.value = !isSidebarMini.value
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function logout(): void {
|
|
99
|
+
emits('logout')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
watch(
|
|
103
|
+
() => isTablet.value,
|
|
104
|
+
(value: boolean) => {
|
|
105
|
+
isFixSidebar.value = value
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<style lang="scss">
|
|
111
|
+
.q-drawer {
|
|
112
|
+
z-index: 3000;
|
|
113
|
+
}
|
|
114
|
+
</style>
|
|
115
|
+
|
|
116
|
+
<style lang="scss" module>
|
|
117
|
+
.drawer {
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-direction: column;
|
|
120
|
+
height: 100vh;
|
|
121
|
+
background: linear-gradient(184deg, #0b3f8e 9.62%, #5386d3 59.23%, #5386d3 91.63%);
|
|
122
|
+
border-right: 1px solid #e4e6e8;
|
|
123
|
+
&::-webkit-scrollbar {
|
|
124
|
+
height: 4px;
|
|
125
|
+
width: 4px;
|
|
126
|
+
}
|
|
127
|
+
&::-webkit-scrollbar-thumb {
|
|
128
|
+
width: 4px;
|
|
129
|
+
height: 4px;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
.logo {
|
|
133
|
+
display: flex;
|
|
134
|
+
margin: 24px 0;
|
|
135
|
+
&__text {
|
|
136
|
+
color: white;
|
|
137
|
+
font-size: 20px;
|
|
138
|
+
font-weight: 700;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
.tablet-logo {
|
|
142
|
+
border-bottom: 1px solid white;
|
|
143
|
+
&__btn {
|
|
144
|
+
color: white;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.minify-btn {
|
|
149
|
+
background: #0b3f8e;
|
|
150
|
+
color: white;
|
|
151
|
+
position: absolute;
|
|
152
|
+
top: 25px;
|
|
153
|
+
right: -17px;
|
|
154
|
+
|
|
155
|
+
&:global(.--mini) {
|
|
156
|
+
:global(.q-icon) {
|
|
157
|
+
transform: rotate(180deg);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.menu-exit {
|
|
163
|
+
border-top: 1px solid white;
|
|
164
|
+
:global(.q-item__section--avatar) {
|
|
165
|
+
min-width: 24px;
|
|
166
|
+
}
|
|
167
|
+
&__label {
|
|
168
|
+
color: #fff;
|
|
169
|
+
font-family: 'Nunito Sans', sans-serif;
|
|
170
|
+
font-size: 16px;
|
|
171
|
+
font-weight: 300;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
</style>
|