flameresttable 1.0.118 → 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.
@@ -0,0 +1,324 @@
1
+ <template>
2
+ <v-dialog v-model="isDialogVisible" max-width="777px">
3
+ <v-card>
4
+ <slot name="header" />
5
+
6
+ <v-card-text>
7
+
8
+ <div v-for="column in ColumnNames" v-show="Table.columns[column].Popup.isShow" :key="'c' + column"
9
+ style="margin-bottom: 10px;">
10
+
11
+ <v-text-field v-if="Table.columns[column].Popup.popupType === 'string'"
12
+ v-model="Table.columns[column].Popup.model"
13
+ :disabled="!Table.columns[column].Popup.isEnabled"
14
+ :label="Table.columns[column].Popup.title || Table.columns[column].title || Table.columns[column].name"
15
+ :placeholder="Table.columns[column].Popup.placeholder" :hint="Table.columns[column].Popup.desc"
16
+ persistent-hint density="compact" variant="outlined" hide-details="auto" />
17
+
18
+ <div v-if="Table.columns[column].Popup.popupType === 'date'">
19
+ <div class="text-sm text-gray-500 mb-1">{{ Table.columns[column].Popup.title || Table.columns[column].title
20
+ || Table.columns[column].name }}</div>
21
+ <Datepicker v-model="Table.columns[column].Popup.model" :auto-apply="true" />
22
+ <div class="text-xs text-slate-400 mt-1">{{ Table.columns[column].Popup.desc }}</div>
23
+ </div>
24
+
25
+ <v-select v-if="Table.columns[column].Popup.popupType === 'selector'"
26
+ v-model="Table.columns[column].Popup.model"
27
+ :items="Table.columns[column].Popup.Selector.values" item-title="title" item-value="id"
28
+ :label="Table.columns[column].Popup.title || Table.columns[column].title || Table.columns[column].name"
29
+ :hint="Table.columns[column].Popup.desc" persistent-hint density="compact" hide-details="auto">
30
+ <template #item="{ props, item }">
31
+ <v-list-item v-bind="props">
32
+ <template #prepend v-if="item.raw.prepend">
33
+ <span :class="item.raw.prependClasses">{{ item.raw.prepend }}</span>
34
+ </template>
35
+ <template #append v-if="item.raw.append">
36
+ <span :class="item.raw.appendClasses">{{ item.raw.append }}</span>
37
+ </template>
38
+ </v-list-item>
39
+ </template>
40
+ <template #selection="{ item }">
41
+ <div class="d-flex align-center w-100">
42
+ <span v-if="item.raw.prepend" :class="item.raw.prependClasses" class="mr-1">{{ item.raw.prepend
43
+ }}</span>
44
+ <span class="font-normal">{{ item.raw.title }}</span>
45
+ <v-spacer></v-spacer>
46
+ <span v-if="item.raw.append" :class="item.raw.appendClasses">{{ item.raw.append }}</span>
47
+ </div>
48
+ </template>
49
+ </v-select>
50
+
51
+
52
+ <div v-if="Table.columns[column].Popup.popupType === 'image'">
53
+ <div class="text-sm text-gray-500 mb-1">{{ Table.columns[column].Popup.title || Table.columns[column].title
54
+ || Table.columns[column].name }}</div>
55
+ <div class="border border-slate-100 flex self-stretch">
56
+ <div class="flex-1">
57
+ <img class="max-w-[200px]" :src="fileGetData(Table.columns[column].Popup.model, true)">
58
+ </div>
59
+
60
+
61
+ <label :for="'cfile_' + column">
62
+
63
+ <svg class="my-auto cursor-pointer" width="24" height="24" viewbox="0 0 24 24" fill="none"
64
+ xmlns="http://www.w3.org/2000/svg">
65
+ <path fill-rule="evenodd" clip-rule="evenodd"
66
+ 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"
67
+ fill="#0F172A" />
68
+ </svg>
69
+
70
+ <input class="hidden" :id="'cfile_' + column" :name="'cfile_' + column" type="file" @change="fileUpdated(column, $event)">
71
+
72
+ </label>
73
+ </div>
74
+ <div class="text-xs text-slate-400 mt-1">{{ Table.columns[column].Popup.desc }}</div>
75
+ </div>
76
+
77
+
78
+
79
+ <div v-if="Table.columns[column].Popup.popupType === 'file'">
80
+ <div class="text-sm text-gray-500 mb-1">{{ Table.columns[column].Popup.title || Table.columns[column].title
81
+ || Table.columns[column].name }}</div>
82
+ <div class="border border-slate-100 flex self-stretch">
83
+
84
+ <div class="flex-1">
85
+ {{ getFileName(Table.columns[column].Popup.model) }}
86
+ </div>
87
+
88
+
89
+ <label :for="'cfile_' + column">
90
+
91
+ <svg class="my-auto cursor-pointer" width="24" height="24" viewbox="0 0 24 24" fill="none"
92
+ xmlns="http://www.w3.org/2000/svg">
93
+ <path fill-rule="evenodd" clip-rule="evenodd"
94
+ 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"
95
+ fill="#0F172A" />
96
+ </svg>
97
+
98
+ <input class="hidden" :id="'cfile_' + column" :name="'cfile_' + column" type="file" @change="fileUpdated(column, $event)">
99
+
100
+ </label>
101
+ </div>
102
+ <div class="text-xs text-slate-400 mt-1">{{ Table.columns[column].Popup.desc }}</div>
103
+ </div>
104
+
105
+ <div v-if="Table.columns[column].Popup.popupType === 'text'">
106
+ <div class="text-sm text-gray-500 mb-1">{{ Table.columns[column].Popup.title || Table.columns[column].title
107
+ || Table.columns[column].name }}</div>
108
+ <EditorPopup :value="Table.columns[column].Popup.model" @save="PopupTextSaved" :column="column"
109
+ :title="Table.columns[column].Popup.title ?? column">
110
+ </EditorPopup>
111
+ <div class="text-xs text-slate-400 mt-1">{{ Table.columns[column].Popup.desc }}</div>
112
+ </div>
113
+
114
+
115
+
116
+ <div class="mx-2 my-1 text-red-600" v-if="(typeof popupErrors[column] !== 'undefined')">{{ popupErrors[column]
117
+ }}</div>
118
+
119
+ </div>
120
+ </v-card-text>
121
+
122
+ <div class="text-red-600 mx-2 my-1" v-if="(typeof popupErrors['globalErrorStack'] !== 'undefined')">{{
123
+ popupErrors['globalErrorStack'] }}</div>
124
+
125
+
126
+ <v-card-actions>
127
+ <v-btn color="grey" variant="text" @click="close">
128
+ Отмена
129
+ </v-btn>
130
+ <v-spacer></v-spacer>
131
+ <v-btn color="blue-darken-1" variant="text" @click="SaveTable">
132
+ {{ Table.mode === 'add' ? 'Добавить' : 'Сохранить' }}
133
+ </v-btn>
134
+ </v-card-actions>
135
+ </v-card>
136
+ </v-dialog>
137
+ </template>
138
+ <script lang="ts">
139
+ import { onMounted, reactive, ref, watch, nextTick } from 'vue'; import type { Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST, { Rows } from "flamerest"
140
+
141
+ // Иконки
142
+ import { Column, IColumn } from './Columns';
143
+ import { defineComponent } from 'vue';
144
+
145
+ import TableOpts from './TableOpts';
146
+ import FlameTable from './FlameTable';
147
+
148
+ type Class<T> = new (...args: any[]) => T;
149
+
150
+ import Datepicker from '@vuepic/vue-datepicker';
151
+ import '@vuepic/vue-datepicker/dist/main.css';
152
+ import { isNumber } from 'lodash';
153
+ import EditorPopup from '../components/editor/EditorPopup.vue';
154
+
155
+ export interface TablePopupInstance {
156
+ showAdd: () => void;
157
+ showEdit: (row: any) => void;
158
+ }
159
+
160
+ export default defineComponent({
161
+ components: { Datepicker, EditorPopup },
162
+ props: {
163
+ table: {
164
+ required: true,
165
+ type: FlameTable
166
+ },
167
+ },
168
+
169
+ setup(props, { expose }) {
170
+
171
+ const Table = props.table as FlameTable<any>;
172
+ const isDialogVisible = ref(false);
173
+ const ColumnNames = Object.keys(Table.columns);
174
+
175
+ // Ошибки сохранения
176
+ let popupErrors: { [key: string]: string } = reactive({});
177
+
178
+ /**
179
+ * Добавление новой записи
180
+ */
181
+ const showAdd = () => {
182
+ Table.mode = 'add';
183
+
184
+ // Установим все поля в пустые значения
185
+ ColumnNames.forEach(key => {
186
+ const col = Table.columns[key];
187
+ if (col.Popup.popupType === 'selector' && col.Popup.Selector.allowNull) {
188
+ col.Popup.model = null;
189
+ } else {
190
+ col.Popup.model = "";
191
+ }
192
+ col.Popup.fileModel = null;
193
+ })
194
+
195
+ isDialogVisible.value = true;
196
+ }
197
+
198
+ /**
199
+ * Редактирование записи
200
+ */
201
+ const showEdit = async (row: any) => {
202
+ Table.mode = 'edit';
203
+
204
+ await Table.opts.Popup.load(row, Table as any);
205
+
206
+
207
+ // Установим все поля в фактические значения из таблицы
208
+ ColumnNames.forEach(key => {
209
+ if (row.hasOwnProperty(key)) {
210
+ if (Table.columns[key].Popup.popupType === 'file' || Table.columns[key].Popup.popupType === 'image')
211
+ Table.columns[key].Popup.fileModel = row[key];
212
+
213
+ Table.columns[key].Popup.model = row[key];
214
+ } else {
215
+ console.warn(`[FlameTable Debug] Column "${key}" configured in frontend but not found in the received row data. Skipping assignment for this field.`);
216
+ }
217
+ })
218
+
219
+ Table.OpenedRow = row;
220
+
221
+ isDialogVisible.value = true;
222
+ }
223
+
224
+ const close = () => {
225
+ Table.OpenedRow = null;
226
+ isDialogVisible.value = false;
227
+ }
228
+
229
+ const SaveTable = async () => {
230
+
231
+ try {
232
+ if (Table.mode === 'add') {
233
+ await Table.add()
234
+ }
235
+ else {
236
+ await Table.save()
237
+ }
238
+ popupErrors = {};
239
+ }
240
+ catch (ex: any) {
241
+
242
+ for (const keyx in popupErrors)
243
+ delete popupErrors[keyx];
244
+
245
+ if (typeof ex.stack !== 'undefined' && typeof ex.message !== 'undefined') {
246
+ popupErrors.globalErrorStack = ex.message;
247
+ }
248
+ else
249
+ Object.assign(popupErrors, ex);
250
+
251
+ return;
252
+ }
253
+
254
+ close();
255
+
256
+ }
257
+
258
+ const fileUpdated = async (column: any, ev: any) => {
259
+ // если файлы не выбраны
260
+ if (ev.target.files.length === 0) return;
261
+
262
+ Table.columns[column].Popup.model = ev;
263
+ const prepared = (await REST.prepare({ myParam: ev })).myParam;
264
+ Table.columns[column].Popup.model = prepared
265
+ Table.columns[column].Popup.fileModel = ev
266
+ }
267
+
268
+ const PopupTextSaved = (txt: string, column: string) => {
269
+ Table.columns[column].Popup.model = txt;
270
+ }
271
+
272
+ expose({
273
+ showAdd,
274
+ showEdit
275
+ })
276
+
277
+
278
+ return {
279
+ Table,
280
+ isDialogVisible,
281
+ ColumnNames,
282
+ SaveTable,
283
+ fileUpdated,
284
+ popupErrors,
285
+ PopupTextSaved,
286
+ close
287
+ }
288
+
289
+ },
290
+
291
+ methods: {
292
+ fileGetData(thisArr: any, getSource: boolean = true) {
293
+ if (thisArr instanceof Array && thisArr.length > 0) {
294
+ // Получить строку с исходником
295
+ if (getSource) {
296
+ if (typeof thisArr[0].data === 'string') return thisArr[0].data;
297
+ if (typeof thisArr[0].file === 'string') return REST.SERVER + "/" + thisArr[0].file;
298
+ return "";
299
+ }
300
+ // Получить данные целиком
301
+ return thisArr[0]
302
+ }
303
+ return null
304
+ },
305
+
306
+ getFileName(fileModel: any) {
307
+ const res = this.fileGetData(fileModel, false)
308
+ if (res?.name) {
309
+ if (typeof res.name === 'string' && res.name !== '') {
310
+ return res.name;
311
+ }
312
+ else {
313
+ if (res.file) {
314
+ const splitted = res.file.split('.');
315
+ return 'File.' + splitted[splitted.length - 1];
316
+ }
317
+ }
318
+ }
319
+ },
320
+ },
321
+
322
+ })
323
+
324
+ </script>
@@ -1,9 +1,29 @@
1
1
  export default interface ITableSelectorItem {
2
2
 
3
- id: string | number,
3
+ id: string | number | null,
4
4
  title?: string,
5
5
  label?: string,
6
6
  color?: string,
7
- position?: number
7
+ position?: number,
8
+
9
+ /**
10
+ * Строка, которая будет добавлена перед элементом
11
+ */
12
+ prepend?: string,
13
+
14
+ /**
15
+ * Строка, которая будет добавлена после элемента
16
+ */
17
+ append?: string,
18
+
19
+ /**
20
+ * CSS-классы для блока с препендом
21
+ */
22
+ prependClasses?: string,
23
+
24
+ /**
25
+ * CSS-классы для блока с аппендом
26
+ */
27
+ appendClasses?: string,
8
28
 
9
29
  }
@@ -92,7 +92,7 @@
92
92
  </template>
93
93
 
94
94
  <script setup lang="ts">
95
- import { onMounted, reactive, ref, defineProps, defineEmits, nextTick } from '@vue/runtime-core'; import type { Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
95
+ import { onMounted, reactive, ref, nextTick } from 'vue'; import type { Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
96
96
 
97
97
  // Иконки
98
98
  import { } from '@icons/24/solid'
@@ -1,37 +1,41 @@
1
- <template lang="pug">
2
-
3
- // УНИВЕРСАЛЬНОЕ МОДАЛЬНОЕ ОКНО
4
- Позволяет вызывать себя через show() и close()
5
-
6
- CustomModal.overall(v-model="isShow" :close="close" style="z-index: 999;")
7
-
8
- // Этот слот для замены самого окна
9
- slot
10
-
11
- .bg-white.rounded-xl
12
-
13
- // Этот слот позволяет заменить всё сразу внутри окна
14
- slot(name="inner")
15
-
16
- // Заголовок
17
- .flex.items-center.px-5.border-b.border-b-gray-300(v-if="title !== ''")
18
- h1.text-3xl.py-5 {{ title }}
19
- XCircleIcon.ml-24.w-6.opacity-25.cursor-pointer(@click="close")
20
-
21
- // Тело
22
- .my-3.px-5
23
- slot(name="body")
24
- .text-left {{ body === '' ? 'Укажите сообщение или используйте слот body' : '' }}
25
-
26
- // Кнопки
27
- slot(name="buttons")
28
- .border-t.border-t-gray-300.mb-4.pt-3.pr-5.flex.items-end.flex-col
29
- button.cursor-pointer.ml-5.rounded-full.bg-blue-700.px-4.py-1.text-white(@click="close") {{ closeButton === '' ? 'Закрыть' : closeButton }}
30
-
1
+ <template>
2
+ <!-- УНИВЕРСАЛЬНОЕ МОДАЛЬНОЕ ОКНО -->
3
+ <!-- Позволяет вызывать себя через show() и close() -->
4
+ <CustomModal class="overall" v-model="isShow" :close="close" style="z-index: 999;">
5
+ <!-- Этот слот для замены самого окна -->
6
+ <slot>
7
+ <div class="bg-white rounded-xl">
8
+ <!-- Этот слот позволяет заменить всё сразу внутри окна -->
9
+ <slot name="inner">
10
+ <!-- Заголовок -->
11
+ <div class="flex items-center px-5 border-b border-b-gray-300" v-if="title !== ''">
12
+ <h1 class="text-3xl py-5">{{ title }}</h1>
13
+ <XCircleIcon class="ml-24 w-6 opacity-25 cursor-pointer" @click="close"></XCircleIcon>
14
+ </div>
15
+
16
+ <!-- Тело -->
17
+ <div class="my-3 px-5">
18
+ <slot name="body">
19
+ <div class="text-left">{{ body === '' ? 'Укажите сообщение или используйте слот body' : '' }}</div>
20
+ </slot>
21
+ </div>
22
+
23
+ <!-- Кнопки -->
24
+ <slot name="buttons">
25
+ <div class="border-t border-t-gray-300 mb-4 pt-3 pr-5 flex items-end flex-col">
26
+ <button class="cursor-pointer ml-5 rounded-full bg-blue-700 px-4 py-1 text-white" @click="close">
27
+ {{ closeButton === '' ? 'Закрыть' : closeButton }}
28
+ </button>
29
+ </div>
30
+ </slot>
31
+ </slot>
32
+ </div>
33
+ </slot>
34
+ </CustomModal>
31
35
  </template>
32
36
 
33
37
  <script lang="ts">
34
- import { ref, defineComponent } from '@vue/runtime-core'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest";
38
+ import { ref, defineComponent } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest";
35
39
 
36
40
  // Иконки
37
41
  import { XCircleIcon } from '@icons/24/solid'
@@ -83,5 +87,6 @@ export default defineComponent({
83
87
  </script>
84
88
 
85
89
  <style scoped lang="scss">
86
- .overall {}
90
+ .overall {
91
+ }
87
92
  </style>
@@ -31,7 +31,7 @@
31
31
  </template>
32
32
 
33
33
  <script lang="ts">
34
- import { onMounted, reactive, ref, defineProps, defineEmits, nextTick, defineComponent } from '@vue/runtime-core'; import type { Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest";
34
+ import { onMounted, reactive, ref, nextTick, defineComponent } from 'vue'; import type { Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest";
35
35
  import { vuetifyProTipTap } from './TextEditor'
36
36
  // Иконки
37
37
  import { } from '@icons/24/solid'
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import type { Editor } from '@tiptap/vue-3'
3
- import { computed, ref, watch, withDefaults, defineProps } from 'vue'
3
+ import { computed, ref, watch } from 'vue'
4
4
  import { locale } from 'vuetify-pro-tiptap'
5
5
 
6
6
  interface LinkAttrs {
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, unref, watch, withDefaults, defineProps, defineEmits } from 'vue'
2
+ import { computed, unref, watch } from 'vue'
3
3
 
4
4
  interface SelectImageProps {
5
5
  modelValue?: Record<string, string>
package/src/main.ts CHANGED
@@ -4,19 +4,18 @@ import './index.css'
4
4
  import { createPinia } from 'pinia'
5
5
  import router from "./router";
6
6
 
7
- import 'vue-universal-modal/dist/index.css'
8
- import VueUniversalModal from 'vue-universal-modal'
9
7
  import REST from 'flamerest';
10
8
  import vSelect from 'vue-select'
11
- import * as components from 'vuetify/components'
12
- import * as directives from 'vuetify/directives'
13
9
 
14
- import { vuetifyProTipTap } from './components/editor/TextEditor'
15
10
 
16
11
  import { createVuetify } from 'vuetify';
12
+ import * as components from 'vuetify/components'
13
+ import * as directives from 'vuetify/directives'
17
14
  import 'vuetify/styles';
18
15
  import { aliases, mdi } from 'vuetify/iconsets/mdi';
19
16
 
17
+ import { vuetifyProTipTap } from './components/editor/TextEditor'
18
+
20
19
 
21
20
  REST.SERVER = 'http://testrest';
22
21
 
@@ -41,5 +40,4 @@ createApp(App)
41
40
  .use(router)
42
41
  .use(vuetify)
43
42
  .use(vuetifyProTipTap)
44
- .use(VueUniversalModal, { teleportTarget: '#my-modals', modalComponent: 'CustomModal' })
45
43
  .mount('#app')
@@ -1,14 +1,13 @@
1
- <template lang="pug">
2
- .flex.justify-center.h-screen.items-center.flex-col
3
- .px-8.py-6.mt-4.text-left.bg-white.shadow-lg(class="desktop:w-feed-lg")
4
- | Welcome!
5
-
1
+ <template>
2
+ <div class="flex justify-center h-screen items-center flex-col">
3
+ <div class="px-8 py-6 mt-4 text-left bg-white shadow-lg desktop:w-feed-lg">Welcome!</div>
4
+ </div>
6
5
  </template>
7
6
 
8
7
  <script setup lang="ts">
9
8
 
10
9
  // Основные импорты
11
- import { onMounted, reactive, defineProps } from '@vue/runtime-core'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
10
+ import { onMounted, reactive } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
12
11
 
13
12
  // Иконки
14
13
  import { XCircleIcon } from '@icons/24/solid'
@@ -1,30 +1,34 @@
1
- <template lang="pug">
2
- .fb.my-5.text-left(class="mobile:flex-wrap mobile:justify-between mobile:w-full")
3
- .bg-green(class="w-[300px] h-[300px]")
4
- .ml-5(class="mobile:w-full")
5
- .fb.my-1(class="mobile:flex-wrap mobile:w-full mobile:justify-between")
6
- div.inline-block.text-3xl NAME
7
- span.text-sm.inline-block.bg-blue-200.mx-5.py-1.px-2 TYPE
8
-
9
- div.text-sm.bg-slate-100.text-slate-500.px-2.py-1.inline-block
10
- span Flex right block
11
- span.ml-1 block 2
12
-
13
- div.my-1
14
- span.text-xl Text: xxxxx
15
-
16
- div.my-1
17
- .text-sm.inline-block.bg-yellow-100.mr-2.py-1.px-2 TEST
18
- div.my-1.text-sm Text: xxxxx
19
- div.my-1.text-sm Text: xxxxx
20
- div.my-1.text-sm Text: xxxxx
21
- div.my-1.text-sm Text: xxxxx
22
-
23
-
1
+ <template>
2
+ <div class="fb my-5 text-left mobile:flex-wrap mobile:justify-between mobile:w-full">
3
+ <div class="bg-green w-[300px] h-[300px]"></div>
4
+ <div class="ml-5 mobile:w-full">
5
+ <div class="fb my-1 mobile:flex-wrap mobile:w-full mobile:justify-between">
6
+ <div class="inline-block text-3xl">NAME</div>
7
+ <span class="text-sm inline-block bg-blue-200 mx-5 py-1 px-2">TYPE</span>
8
+
9
+ <div class="text-sm bg-slate-100 text-slate-500 px-2 py-1 inline-block">
10
+ <span>Flex right block</span>
11
+ <span class="ml-1">block 2</span>
12
+ </div>
13
+ </div>
14
+
15
+ <div class="my-1">
16
+ <span class="text-xl">Text: xxxxx</span>
17
+ </div>
18
+
19
+ <div class="my-1">
20
+ <div class="text-sm inline-block bg-yellow-100 mr-2 py-1 px-2">TEST</div>
21
+ </div>
22
+ <div class="my-1 text-sm">Text: xxxxx</div>
23
+ <div class="my-1 text-sm">Text: xxxxx</div>
24
+ <div class="my-1 text-sm">Text: xxxxx</div>
25
+ <div class="my-1 text-sm">Text: xxxxx</div>
26
+ </div>
27
+ </div>
24
28
  </template>
25
29
 
26
30
  <script setup lang="ts">
27
- import { onMounted, reactive, ref, defineProps, defineEmits, nextTick } from '@vue/runtime-core'; import type { Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
31
+ import { onMounted, reactive, ref, nextTick } from 'vue'; import type { Ref } from 'vue'; import { storeFile } from "@/store"; import { useRoute, useRouter } from 'vue-router'; import REST from "flamerest"
28
32
 
29
33
  // Иконки
30
34
  import { } from '@icons/24/solid'