Motyw
Struktura katalogów
Układ projektu wynika z konwencji Quasara, rozszerzonej o strukturę modułową w katalogu src/modules/. Cała logika biznesowa znajduje się w modułach, a najwyższy poziom src/ zawiera wyłącznie elementy globalne.
Drzewo projektu
├── _templates/plop szablony generatora (model/, module/)
├── build/env-parser.js ładowanie plików .env
├── public/
├── src/
│ ├── App.vue root: <router-view> + reset cache przy zmianie języka
│ ├── menu-items.js cała nawigacja panelu (modules[] + settings[])
│ ├── assets/img/
│ ├── boot/
│ │ ├── app/ ui.js — domyślne Notify/Loading/LoadingBar
│ │ └── packages/ axios, i18n, yup, feather-icons, vue-query
│ ├── composables/ useNestedRoutes.js (jedyny globalny composable)
│ ├── css/ app.scss, partiale _mixins/_form/_table/...,
│ │ quasar.variables.scss, moon.variables.scss, fonts/
│ ├── i18n/ instance.js, pluralization-rules.js, en/, pl/
│ ├── icons/app/ własne SVG (kolekcja "app" dla unplugin-icons)
│ ├── layouts/ MasterLayout, AuthLayout, BlankLayout
│ ├── modules/ ← logika biznesowa, opisana niżej
│ ├── router/ index.js (guard), routes.js (składanie tras)
│ └── stores/ index.js — createPinia()
├── auto-imports.d.ts generowane, commitowane
├── components.d.ts generowane, commitowane
├── install-module.sh
├── plopfile.cjs
└── quasar.config.jsModuły
Projekt zawiera szesnaście modułów:
auth blog contentBuilder cookie core faq media menu
page redirect review seo setting slider staticText userPełnią one trzy różne role:
| Rola | Moduły | Charakterystyka |
|---|---|---|
| Jądro | core | Klasa Http, komponenty Base* i V*, composables useCoreTable i useFormSubmit, breadcrumbs, guardy, funkcje pomocnicze. Nie jest modułem domenowym |
| Infrastruktura | auth, media, contentBuilder, seo | Używane przez pozostałe moduły |
| Domena | reszta | Realizują ten sam wzorzec CRUD |
Katalog w camelCase, ścieżki i klucze w kebab-case
Nazwa katalogu modułu zapisywana jest w konwencji camelCase (contentBuilder, staticText), natomiast ścieżki API, nazwy tras i klucze i18n używają kebab-case (/content-builder/..., static-text.static-texts.index).
Anatomia modułu
Poniższy układ, pokazany na przykładzie modułu blog, powtarza się we wszystkich modułach domenowych:
src/modules/blog/
├── api/
│ ├── keys.js klucze cache dla Vue Query (per model)
│ ├── endpoints/article.js cienkie wrappery axiosa
│ ├── queries/article.queries.js useArticleDetailQuery()
│ └── mutations/article.mutations.js useArticleSaveMutation()
├── components/
│ ├── BlogArticleForm.vue <Moduł><Model>Form
│ ├── BlogArticlesTable.vue <Moduł><Modele>Table
│ ├── BlogCategoryForm.vue
│ ├── BlogCategoriesTable.vue
│ ├── BlogCategoryReorderTree.vue
│ └── BlogCategoryReorderTreeItem.vue
├── forms/
│ ├── article.form.js initialValues() + validationSchema()
│ └── category.form.js
├── pages/
│ ├── articles/ ArticleIndexPage / CreatePage / EditPage
│ └── categories/ + CategoryReorderPage
├── routes/
│ ├── index.js agreguje trasy modułu
│ ├── articles.js
│ └── categories.js
├── stores/
│ └── category.js Pinia — tylko tam, gdzie potrzebny
└── trans/
├── en/ index.js, articles.js, categories.js, messages.js
└── pl/ index.js, articles.js, categories.js, messages.jsPrzeznaczenie katalogów
| Katalog | Zawartość |
|---|---|
api/endpoints | Funkcje wywołujące API — bez logiki i bez obsługi błędów |
api/keys.js | createEntityKeys("/blog/articles") — klucze cache Vue Query |
api/queries | Composables oparte na useQuery — pobieranie danych |
api/mutations | Composables oparte na useMutation — zapis i unieważnianie cache |
forms | Wartości początkowe i schemat walidacji yup, bez części wizualnej |
components | Komponenty modułu, rejestrowane globalnie |
composables | Composables modułu; auto-importowane tylko wtedy, gdy katalog figuruje w quasar.config.js |
pages | Strony przypisane do tras, z podziałem na modele |
routes | Trasy z podziałem na modele oraz plik index.js |
stores | Store'y Pinia — tylko tam, gdzie potrzebny jest stan poza cache serwera |
services | Czysta logika domenowa, bez wywołań HTTP |
helpers, utils, constants, configs | Elementy pomocnicze; utils bywa auto-importowany |
trans | Tłumaczenia modułu w wersjach en i pl |
Szkieletu nie tworzy się ręcznie
Cały powyższy układ generuje npm run make, opisany w Modułach i generatorach.
Aliasy importów
Aliasy zdefiniowane są w quasar.config.js w sekcji build.alias:
js
// quasar.config.js
alias: {
modules: fileURLToPath(new URL("./src/modules", import.meta.url)),
composables: fileURLToPath(new URL("./src/composables", import.meta.url)),
}Uzupełniają je aliasy domyślne Quasara. Pełna lista odwzorowana jest w jsconfig.json — dzięki temu działają podpowiedzi w edytorze:
| Alias | Ścieżka |
|---|---|
modules/* | src/modules/* |
composables/* | src/composables/* |
src/* | src/* |
app/* | * (katalog główny projektu) |
layouts/* | src/layouts/* |
boot/* | src/boot/* |
stores/* | src/stores/* |
assets/* | src/assets/* |
components/* | src/components/* |
pages/* | src/pages/* |
W praktyce zastosowanie znajdują przede wszystkim dwa pierwsze:
js
import { api } from "modules/core/services/http";
import { articleKeys } from "modules/blog/api/keys";oraz alias layouts/ w definicjach tras:
js
component: () => import("layouts/MasterLayout.vue")Aliasy components/ i pages/ prowadzą do nieistniejących katalogów
Pochodzą z domyślnej konfiguracji Quasara, ale w tym projekcie src/components i src/pages nie istnieją — odpowiadające im pliki leżą wewnątrz modułów.