adminforth 1.0.65 → 1.0.67

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 (37) hide show
  1. package/dist/modules/codeInjector.js +5 -0
  2. package/dist/spa/index.html +2 -2
  3. package/dist/spa/package-lock.json +189 -1
  4. package/dist/spa/package.json +6 -1
  5. package/dist/spa/spa/index.html +1 -1
  6. package/dist/spa/spa/src/App.vue +23 -13
  7. package/dist/spa/spa/src/stores/core.ts +3 -0
  8. package/dist/spa/spa/src/views/ShowView.vue +3 -2
  9. package/dist/spa/src/App.vue +76 -57
  10. package/dist/spa/src/components/AcceptModal.vue +1 -1
  11. package/dist/spa/src/components/CustomDatePicker.vue +3 -3
  12. package/dist/spa/src/components/CustomDateRangePicker.vue +3 -3
  13. package/dist/spa/src/components/CustomRangePicker.vue +148 -0
  14. package/dist/spa/src/components/Dropdown.vue +13 -4
  15. package/dist/spa/src/components/Filters.vue +55 -22
  16. package/dist/spa/src/components/MenuLink.vue +2 -2
  17. package/dist/spa/src/components/ResourceForm.vue +157 -99
  18. package/dist/spa/src/components/ValueRenderer.vue +11 -1
  19. package/dist/spa/src/router/index.ts +3 -2
  20. package/dist/spa/src/stores/core.ts +22 -33
  21. package/dist/spa/src/utils.ts +5 -3
  22. package/dist/spa/src/views/CreateView.vue +15 -7
  23. package/dist/spa/src/views/EditView.vue +36 -8
  24. package/dist/spa/src/views/ListView.vue +35 -23
  25. package/dist/spa/src/views/LoginView.vue +1 -1
  26. package/dist/spa/src/views/ResourceParent.vue +1 -1
  27. package/dist/spa/src/views/ShowView.vue +20 -9
  28. package/dist/spa/tailwind.config.js +5 -2
  29. package/modules/codeInjector.ts +6 -0
  30. package/package.json +1 -1
  31. package/spa/index.html +1 -1
  32. package/spa/src/App.vue +23 -13
  33. package/spa/src/stores/core.ts +3 -0
  34. package/spa/src/views/ShowView.vue +3 -2
  35. package/dist/spa/spa/src/components/ShowTableItem.vue +0 -14
  36. package/dist/spa/spa/src/views/HomeView.vue +0 -8
  37. package/dist/spa/src/views/HomeView.vue +0 -8
@@ -65,7 +65,7 @@
65
65
  </BreadcrumbsWithButtons>
66
66
 
67
67
  <!-- table -->
68
- <div class="relative overflow-x-auto shadow-md sm:rounded-lg dark:shadow-2xl">
68
+ <div class="relative overflow-x-auto shadow-list-table-shadow dark:shadow-black dark:s">
69
69
 
70
70
 
71
71
  <!-- skelet loader -->
@@ -90,7 +90,7 @@
90
90
  </div>
91
91
 
92
92
  <table v-else class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
93
- <thead class="text-xs text-gray-700 bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
93
+ <thead class="text-xs text-view-table-heading-text bg-list-view-table-heading dark:bg-gray-700 dark:text-gray-400">
94
94
  <tr>
95
95
  <th scope="col" class="p-4">
96
96
  <div v-if="rows && rows.length" class="flex items-center">
@@ -127,7 +127,7 @@
127
127
  </tr>
128
128
  </thead>
129
129
  <tbody>
130
- <tr v-if="!rows" class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
130
+ <tr v-if="!rows" class="bg-list-view-table-bg border-b dark:bg-gray-800 dark:border-gray-700">
131
131
  <td :colspan="coreStore.resourceColumns.length + 2">
132
132
 
133
133
  <div role="status"
@@ -171,7 +171,7 @@
171
171
  </div>
172
172
  </td>
173
173
  </tr>
174
- <tr v-else-if="rows.length === 0" class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
174
+ <tr v-else-if="rows.length === 0" class="bg-list-view-table-bg border-b dark:bg-gray-800 dark:border-gray-700">
175
175
  <td :colspan="coreStore.resourceColumns.length + 2">
176
176
 
177
177
  <div id="toast-simple"
@@ -185,7 +185,7 @@
185
185
  </tr>
186
186
 
187
187
  <tr v-else v-for="(row, rowI) in rows" :key="row.id"
188
- class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
188
+ class="bg-list-view-table-bg border-b dark:bg-gray-800 border-list-view-border-color dark:border-gray-700 hover:bg-list-view-table-row-hover dark:hover:bg-gray-600">
189
189
  <td class="w-4 p-4">
190
190
  <div class="flex items center">
191
191
  <input
@@ -198,7 +198,12 @@
198
198
  </div>
199
199
  </td>
200
200
  <td v-for="c in columnsListed" class="px-6 py-4">
201
- <ValueRenderer :column="c" :row="row"/>
201
+ <!-- if c.name in listComponentsPerColumn, render it. If not, render ValueRenderer -->
202
+ <component
203
+ :is="listComponentsPerColumn[c.name] || ValueRenderer"
204
+ :column="c"
205
+ :row="row"
206
+ />
202
207
  </td>
203
208
  <td class="flex items-center px-6 py-4">
204
209
 
@@ -310,29 +315,28 @@
310
315
  </template>
311
316
 
312
317
  <script setup>
313
- import {ref, onMounted, watch, computed} from 'vue';
314
- import {callAdminForthApi, getIcon} from '@/utils';
315
- import {useRoute} from 'vue-router';
316
- import {useCoreStore} from '@/stores/core';
317
- import {useModalStore} from '@/stores/modal';
318
318
  import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
319
- import {initFlowbite} from 'flowbite'
319
+ import { useCoreStore } from '@/stores/core';
320
+ import { useModalStore } from '@/stores/modal';
321
+ import { callAdminForthApi, getIcon } from '@/utils';
322
+ import { initFlowbite } from 'flowbite';
323
+ import { computed, onMounted, ref, watch } from 'vue';
324
+ import { useRoute } from 'vue-router';
320
325
 
321
326
  import ValueRenderer from '@/components/ValueRenderer.vue';
327
+ import { getCustomComponent } from '@/utils';
322
328
 
323
329
  import {
324
- IconChevronDoubleLeftOutline,
325
- IconChevronDoubleRightOutline,
326
- IconInboxFullSolid,
327
- IconInboxOutline,
328
- IconPlusOutline
330
+ IconInboxOutline,
331
+ IconPlusOutline
329
332
  } from '@iconify-prerendered/vue-flowbite';
330
333
 
331
334
  import {
332
- IconEyeSolid,
333
- IconTrashBinSolid,
334
- IconPenSolid,
335
- IconFilterOutline, IconBanOutline
335
+ IconBanOutline,
336
+ IconEyeSolid,
337
+ IconFilterOutline,
338
+ IconPenSolid,
339
+ IconTrashBinSolid
336
340
  } from '@iconify-prerendered/vue-flowbite';
337
341
 
338
342
  import Filters from '@/components/Filters.vue';
@@ -357,7 +361,6 @@ const totalRows = ref(0);
357
361
  const allCheckedActions = ref([]);
358
362
  const allowedActions = ref({});
359
363
 
360
-
361
364
  const DEFAULT_PAGE_SIZE = 10;
362
365
 
363
366
  const columnsListed = computed(() => coreStore.resourceColumns?.filter(c => c.showIn.includes('list')));
@@ -376,6 +379,7 @@ async function selectAll(value) {
376
379
 
377
380
  const pageSize = computed(() => coreStore.resourceById[route.params.resourceId]?.pageSize || DEFAULT_PAGE_SIZE);
378
381
  const totalPages = computed(() => Math.ceil(totalRows.value / pageSize.value));
382
+ let listComponentsPerColumn = {};
379
383
  const allFromThisPageChecked = computed(() => {
380
384
  if (!rows.value) return false;
381
385
  return rows.value.every((r) => checkboxes.value.includes(r.id));
@@ -419,6 +423,7 @@ async function getList() {
419
423
  path: '/get_resource_data',
420
424
  method: 'POST',
421
425
  body: {
426
+ source: 'list',
422
427
  resourceId: route.params.resourceId,
423
428
  limit: pageSize.value,
424
429
  offset: (page.value - 1) * pageSize.value,
@@ -426,7 +431,14 @@ async function getList() {
426
431
  sort: sort.value,
427
432
  }
428
433
  });
429
- fetchStatus.value.pending = false;
434
+ listComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
435
+ if (column.component?.list) {
436
+ acc[column.name] = getCustomComponent(column.component.list);
437
+ }
438
+ return acc;
439
+ }, {});
440
+
441
+ fetchStatus.value.pending = false;
430
442
  rows.value = data.data?.map(row => {
431
443
  row._primaryKeyValue = row[coreStore.resourceColumns.find(c => c.primaryKey).name];
432
444
  return row;
@@ -11,7 +11,7 @@
11
11
  <div id="authentication-modal" tabindex="-1" class=" overflow-y-auto overflow-x-hidden z-50 min-w-[400px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
12
12
  <div class="relative p-4 w-full max-w-md max-h-full">
13
13
  <!-- Modal content -->
14
- <div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
14
+ <div class="relative bg-white rounded-lg shadow dark:bg-gray-700 dark:shadow-black" >
15
15
  <!-- Modal header -->
16
16
  <div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
17
17
  <h3 class="text-xl font-semibold text-gray-900 dark:text-white">
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div>
2
+ <div :key="`${$route?.params.resourceId}---${$route?.params.primaryKey}`">
3
3
  <RouterView/>
4
4
  </div>
5
5
  </template>
@@ -27,10 +27,10 @@
27
27
  </div>
28
28
  <div
29
29
  v-else
30
- class="relative overflow-x-auto shadow-md sm:rounded-lg"
30
+ class="relative overflow-x-auto shadow-resourse-form-shadow "
31
31
  >
32
32
  <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
33
- <thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
33
+ <thead class="text-xs text-gray-700 uppercase bg-form-view-heading dark:bg-gray-700 dark:text-gray-400">
34
34
  <tr>
35
35
  <th scope="col" class="px-6 py-3">
36
36
  Field
@@ -42,13 +42,17 @@
42
42
  </thead>
43
43
  <tbody>
44
44
  <tr v-for="column in coreStore.resourceColumns?.filter(c => c.showIn.includes('show'))" :key="column.name"
45
- class="odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800 border-b dark:border-gray-700"
45
+ class="bg-form-view-bg odd:dark:bg-gray-900 even:dark:bg-gray-800 border-b dark:border-gray-700"
46
46
  >
47
47
  <td class="px-6 py-4 whitespace-nowrap">
48
48
  {{ column.label }}
49
49
  </td>
50
50
  <td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap">
51
- <ValueRenderer :column="column" :row="coreStore.record" />
51
+ <component
52
+ :is="showComponentsPerColumn[column.name] || ValueRenderer"
53
+ :column="column"
54
+ :row="coreStore.record"
55
+ />
52
56
  </td>
53
57
  </tr>
54
58
 
@@ -63,18 +67,19 @@
63
67
 
64
68
  <script setup>
65
69
 
66
- import { ref, computed, onMounted } from 'vue';
67
- import { useRoute } from 'vue-router';
68
- import { callAdminForthApi } from '@/utils';
69
70
  import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
70
- import { IconPenSolid, IconTrashBinSolid } from '@iconify-prerendered/vue-flowbite';
71
- import { useCoreStore } from '@/stores/core';
72
71
  import ValueRenderer from '@/components/ValueRenderer.vue';
72
+ import { useCoreStore } from '@/stores/core';
73
+ import { getCustomComponent } from '@/utils';
74
+ import { IconPenSolid, IconTrashBinSolid } from '@iconify-prerendered/vue-flowbite';
75
+ import { onMounted, ref } from 'vue';
76
+ import { useRoute } from 'vue-router';
73
77
 
74
78
 
75
79
  const item = ref(null);
76
80
  const route = useRoute();
77
81
  const loading = ref(false);
82
+ let showComponentsPerColumn = {};
78
83
 
79
84
  const coreStore = useCoreStore();
80
85
 
@@ -87,6 +92,12 @@ onMounted(async () => {
87
92
  resourceId: route.params.resourceId,
88
93
  primaryKey: route.params.primaryKey,
89
94
  });
95
+ showComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
96
+ if (column.component?.show) {
97
+ acc[column.name] = getCustomComponent(column.component.show);
98
+ }
99
+ return acc;
100
+ }, {});
90
101
  loading.value = false;
91
102
  });
92
103
 
@@ -1,9 +1,12 @@
1
1
  /** @type {import('tailwindcss').Config} */
2
2
  export default {
3
- content: ["./src/**/*.{vue, js}", "./node_modules/flowbite/**/*.js"],
3
+ content: ["./src/**/*.{vue, js}","./src/*.{vue, js}", "./index.html", "./node_modules/flowbite/**/*.js"],
4
4
  theme: {
5
- extend: {},
5
+ extend: {
6
+ /* IMPORTANT:ADMINFORTH TAILWIND STYLES */
7
+ }
6
8
  },
9
+
7
10
  darkMode: 'class',
8
11
  plugins: [
9
12
  require('flowbite/plugin'),
@@ -296,6 +296,12 @@ class CodeInjector {
296
296
  let routerVueContent = await fs.promises.readFile(routerVuePath, 'utf-8');
297
297
  routerVueContent = routerVueContent.replace('/* IMPORTANT:ADMINFORTH ROUTES IMPORTS */', routerComponents);
298
298
 
299
+ // inject title to index.html
300
+ const indexHtmlPath = path.join(CodeInjector.SPA_TMP_PATH, 'index.html');
301
+ let indexHtmlContent = await fs.promises.readFile(indexHtmlPath, 'utf-8');
302
+ indexHtmlContent = indexHtmlContent.replace('/* IMPORTANT:ADMINFORTH TITLE */', `${this.adminforth.config.title || 'AdminForth'}`);
303
+ await fs.promises.writeFile(indexHtmlPath, indexHtmlContent);
304
+
299
305
 
300
306
 
301
307
  /* generate custom routes */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.0.65",
3
+ "version": "1.0.67",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/spa/index.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8">
5
5
  <link rel="icon" href="/favicon.ico">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Vite App</title>
7
+ <title>/* IMPORTANT:ADMINFORTH TITLE */</title>
8
8
  <!--
9
9
  <script>
10
10
  // On page load or when changing themes, best to add inline in `head` to avoid FOUC
package/spa/src/App.vue CHANGED
@@ -5,7 +5,8 @@
5
5
  <div class="px-3 py-3 lg:px-5 lg:pl-3">
6
6
  <div class="flex items-center justify-between">
7
7
  <div class="flex items-center justify-start rtl:justify-end">
8
- <button data-drawer-target="logo-sidebar" data-drawer-toggle="logo-sidebar" aria-controls="logo-sidebar" type="button" class="inline-flex items-center p-2 text-sm rounded-lg sm:hidden hover:bg-nav-menu-bg-hover focus:outline-none focus:ring-2 focus:ring-nav-menu-devider dark:text-nav-menu-icons dark:hover:bg-nav-menu-bg-hover dark:focus:ring-nav-menu-devider">
8
+ <button @click="sideBarOpen = !sideBarOpen"
9
+ type="button" class="inline-flex items-center p-2 text-sm rounded-lg sm:hidden hover:bg-nav-menu-bg-hover focus:outline-none focus:ring-2 focus:ring-nav-menu-devider dark:text-nav-menu-icons dark:hover:bg-nav-menu-bg-hover dark:focus:ring-nav-menu-devider">
9
10
  <span class="sr-only">Open sidebar</span>
10
11
  <svg class="w-6 h-6" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
11
12
  <path clip-rule="evenodd" fill-rule="evenodd" d="M2 4.75A.75.75 0 012.75 4h14.5a.75.75 0 010 1.5H2.75A.75.75 0 012 4.75zm0 10.5a.75.75 0 01.75-.75h7.5a.75.75 0 010 1.5h-7.5a.75.75 0 01-.75-.75zM2 10a.75.75 0 01.75-.75h14.5a.75.75 0 010 1.5H2.75A.75.75 0 012 10z"></path>
@@ -58,7 +59,10 @@
58
59
 
59
60
  v-if="loggedIn && routerIsReady && loginRedirectCheckIsReady"
60
61
 
61
- id="logo-sidebar" class="fixed border-none top-0 left-0 z-40 w-64 h-screen transition-transform -translate-x-full bg-nav-menu-bg border-r border- sm:translate-x-0 dark:bg-gray-800 dark:border-gray-700" aria-label="Sidebar">
62
+ id="logo-sidebar" class="fixed border-none top-0 left-0 z-50 w-64 h-screen transition-transform bg-nav-menu-bg border-r border- sm:translate-x-0 dark:bg-gray-800 dark:border-gray-700"
63
+ :class="{ '-translate-x-full': !sideBarOpen, 'transform-none': sideBarOpen }"
64
+ aria-label="Sidebar"
65
+ >
62
66
  <div class="h-full px-3 pb-4 overflow-y-auto bg-nav-menu-bg dark:bg-gray-800">
63
67
 
64
68
  <div class="flex ms-2 md:me-24 m-4 ">
@@ -101,7 +105,8 @@
101
105
  </ul>
102
106
  </div>
103
107
  </aside>
104
-
108
+
109
+
105
110
  <div class="p-4 sm:ml-64 dark:bg-gray-800" v-if="loggedIn && routerIsReady && loginRedirectCheckIsReady">
106
111
  <div class="p-0 dark:border-gray-700 mt-14">
107
112
  <RouterView/>
@@ -119,6 +124,11 @@
119
124
  </div>
120
125
  <AcceptModal />
121
126
 
127
+ <div v-if="sideBarOpen"
128
+ @click="sideBarOpen = false"
129
+
130
+ drawer-backdrop="" class="bg-gray-900/50 dark:bg-gray-900/80 fixed inset-0 z-30"></div>
131
+
122
132
  </template>
123
133
 
124
134
  <style scoped>
@@ -144,7 +154,7 @@ const coreStore = useCoreStore();
144
154
  createHead()
145
155
 
146
156
 
147
-
157
+ const sideBarOpen = ref(false);
148
158
 
149
159
 
150
160
 
@@ -198,16 +208,16 @@ onMounted(async () => {
198
208
  await coreStore.fetchMenuAndResource();
199
209
  loginRedirectCheckIsReady.value = true;
200
210
  title.value = coreStore.config?.title || 'AdminForth';
201
- useHead({
202
- title: title.value,
203
- link: [
204
- {
205
- rel: 'icon',
206
- href: coreStore.config.favicon || '/favicon.ico',
207
- },
208
- ],
211
+ // useHead({
212
+ // title: title.value,
213
+ // link: [
214
+ // {
215
+ // rel: 'icon',
216
+ // href: coreStore.config.favicon || '/favicon.ico',
217
+ // },
218
+ // ],
209
219
 
210
- })
220
+ // })
211
221
  coreStore.menu.forEach((item, i) => {
212
222
  if (item.open) {
213
223
  opened.value.push(i);
@@ -8,6 +8,7 @@ export const useCoreStore = defineStore('core', () => {
8
8
  const config = ref({});
9
9
  const record = ref({});
10
10
  const resourceColumns = ref(null);
11
+ const resourceOptions = ref(null);
11
12
  const resourceColumnsError = ref('');
12
13
  const resourceColumnsId = ref(null);
13
14
  const user = ref(null);
@@ -62,6 +63,7 @@ export const useCoreStore = defineStore('core', () => {
62
63
 
63
64
  console.log('📦 record', respData);
64
65
  record.value = respData.data[0];
66
+ resourceOptions.value = respData.options;
65
67
  }
66
68
 
67
69
  async function fetchColumns({ resourceId }) {
@@ -127,6 +129,7 @@ export const useCoreStore = defineStore('core', () => {
127
129
  fetchColumns,
128
130
  resourceColumnsError,
129
131
  flowBitIsInitialised,
132
+ resourceOptions,
130
133
  logout
131
134
  }
132
135
  })
@@ -1,14 +1,14 @@
1
1
  <template>
2
2
  <div class="relative">
3
3
  <BreadcrumbsWithButtons>
4
- <RouterLink :to="{ name: 'resource-edit', params: { resourceId: $route.params.resourceId, primaryKey: $route.params.primaryKey } }"
4
+ <RouterLink v-if="coreStore?.resourceOptions?.allowedActions?.edit" :to="{ name: 'resource-edit', params: { resourceId: $route.params.resourceId, primaryKey: $route.params.primaryKey } }"
5
5
  class="flex items-center py-1 px-3 me-2 mb-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
6
6
  >
7
7
  <IconPenSolid class="w-4 h-4" />
8
8
  Edit
9
9
  </RouterLink>
10
10
 
11
- <button @click="deleteRecord"
11
+ <button v-if="coreStore?.resourceOptions?.allowedActions?.delete" @click="deleteRecord"
12
12
  class="flex items-center py-1 px-3 mb-2 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
13
13
  >
14
14
  <IconTrashBinSolid class="w-4 h-4" />
@@ -95,6 +95,7 @@ const loading = ref(false);
95
95
  let showComponentsPerColumn = {};
96
96
  let showRowComponentsPerColumn = {};
97
97
 
98
+
98
99
  const coreStore = useCoreStore();
99
100
 
100
101
  onMounted(async () => {
@@ -1,14 +0,0 @@
1
- <template>
2
- <td class="px-6 py-4 whitespace-nowrap">
3
- {{ column.label }}
4
- </td>
5
- <td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap">
6
- <ValueRenderer :column="column" :row="row" />
7
- </td>
8
- </template>
9
-
10
- <script setup>
11
- import ValueRenderer from '@/components/ValueRenderer.vue';
12
-
13
- defineProps(['column', 'row']);
14
- </script>
@@ -1,8 +0,0 @@
1
- <script setup lang="ts">
2
- </script>
3
-
4
- <template>
5
- <main>
6
- 123
7
- </main>
8
- </template>
@@ -1,8 +0,0 @@
1
- <script setup lang="ts">
2
- </script>
3
-
4
- <template>
5
- <main>
6
- 123
7
- </main>
8
- </template>