Commit 2b664beb authored by Administrator's avatar Administrator
Browse files

docs: add obsidian knowledge graph for student-app-frontend (incl. django-backend)

parent 8f1cac35
{}
\ No newline at end of file
{}
\ No newline at end of file
{
"file-explorer": true,
"global-search": true,
"switcher": true,
"graph": true,
"backlink": true,
"canvas": true,
"outgoing-link": true,
"tag-pane": true,
"footnotes": false,
"properties": true,
"page-preview": true,
"daily-notes": true,
"templates": true,
"note-composer": true,
"command-palette": true,
"slash-command": false,
"editor-status": true,
"bookmarks": true,
"markdown-importer": false,
"zk-prefixer": false,
"random-note": false,
"outline": true,
"word-count": true,
"slides": false,
"audio-recorder": false,
"workspaces": false,
"file-recovery": true,
"publish": false,
"sync": true,
"bases": true,
"webviewer": false
}
\ No newline at end of file
{
"collapse-filter": true,
"search": "",
"showTags": false,
"showAttachments": false,
"hideUnresolved": false,
"showOrphans": true,
"collapse-color-groups": true,
"colorGroups": [],
"collapse-display": true,
"showArrow": false,
"textFadeMultiplier": 0,
"nodeSizeMultiplier": 1,
"lineSizeMultiplier": 1,
"collapse-forces": true,
"centerStrength": 0.518713248970312,
"repelStrength": 10,
"linkStrength": 1,
"linkDistance": 250,
"scale": 1,
"close": false
}
\ No newline at end of file
{
"main": {
"id": "15ce68762a86ad31",
"type": "split",
"children": [
{
"id": "4783fd2d62cc73c8",
"type": "tabs",
"children": [
{
"id": "bc4163210a84c426",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "10-19 Projects/01 student-app-frontend/01.01 src-api.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "01.01 src-api"
}
}
]
}
],
"direction": "vertical"
},
"left": {
"id": "08f4785d7b6fe88a",
"type": "split",
"children": [
{
"id": "615210a51d327ccf",
"type": "tabs",
"children": [
{
"id": "17e4634b25646b0f",
"type": "leaf",
"state": {
"type": "file-explorer",
"state": {
"sortOrder": "alphabetical",
"autoReveal": false
},
"icon": "lucide-folder-closed",
"title": "Files"
}
},
{
"id": "d1d24b920c0de5cf",
"type": "leaf",
"state": {
"type": "search",
"state": {
"query": "",
"matchingCase": false,
"explainSearch": false,
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical"
},
"icon": "lucide-search",
"title": "Search"
}
},
{
"id": "a2464f9fb32d7364",
"type": "leaf",
"state": {
"type": "bookmarks",
"state": {},
"icon": "lucide-bookmark",
"title": "Bookmarks"
}
}
]
}
],
"direction": "horizontal",
"width": 300
},
"right": {
"id": "4f692e2bd3af412c",
"type": "split",
"children": [
{
"id": "04e76b98948fd0eb",
"type": "tabs",
"children": [
{
"id": "ea2a57fe32c5a2da",
"type": "leaf",
"state": {
"type": "backlink",
"state": {
"file": "10-19 Projects/01 student-app-frontend/01.01 src-api.md",
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
"showSearch": false,
"searchQuery": "",
"backlinkCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-coming-in",
"title": "Backlinks for 01.01 src-api"
}
},
{
"id": "c3d9e4961f1ea9c7",
"type": "leaf",
"state": {
"type": "outgoing-link",
"state": {
"file": "10-19 Projects/01 student-app-frontend/01.01 src-api.md",
"linksCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-going-out",
"title": "Outgoing links from 01.01 src-api"
}
},
{
"id": "4ed1724c0afd2cd0",
"type": "leaf",
"state": {
"type": "tag",
"state": {
"sortOrder": "frequency",
"useHierarchy": true,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-tags",
"title": "Tags"
}
},
{
"id": "ee16e2e4102c5d3c",
"type": "leaf",
"state": {
"type": "all-properties",
"state": {
"sortOrder": "frequency",
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-archive",
"title": "All properties"
}
},
{
"id": "12492f75deb46be7",
"type": "leaf",
"state": {
"type": "outline",
"state": {
"file": "10-19 Projects/01 student-app-frontend/01.01 src-api.md",
"followCursor": false,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "Outline of 01.01 src-api"
}
}
]
}
],
"direction": "horizontal",
"width": 300,
"collapsed": true
},
"left-ribbon": {
"hiddenItems": {
"switcher:Open quick switcher": false,
"graph:Open graph view": false,
"canvas:Create new canvas": false,
"daily-notes:Open today's daily note": false,
"templates:Insert template": false,
"command-palette:Open command palette": false,
"bases:Create new base": false
}
},
"active": "bc4163210a84c426",
"lastOpenFiles": [
"10-19 Projects/01 student-app-frontend/01.00 student-app-frontend.md",
"10-19 Projects/01 student-app-frontend/01.11 django-backend.md"
]
}
\ No newline at end of file
---
jd-id: "01.00"
name: "student-app-frontend"
desc: "Vue 3 + Vite + Vuetify 3 SPA for managing students, subjects, terms, stories, and tasks. Talks to a Django REST Framework backend over JWT-authenticated JSON APIs."
author: "Clark Lin"
updateTime: "2026-07-06"
status: active
type: "A-高层系统"
tags:
- Vue3
- Vuetify
- JavaScript
- 前端
- Pinia
---
**Port:** localhost:5173
---
## Tech Stack
| Layer | Technology | Role |
|-------|------------|------|
| Framework | Vue 3.5 (Composition API, `<script setup>`) | SPA shell, reactive views |
| Build | Vite 7 (`vite.config.js`) | Dev server (`0.0.0.0:5173`), HMR, ESM bundling |
| UI | Vuetify 3.9 (Material Design) | Component library, light theme, all components/directives imported globally |
| Routing | vue-router 4 (`createWebHistory`) | Client-side navigation, JWT-aware guard |
| State | Pinia 3 (`stores/auth.js`) | Reactive auth store with axios interceptor setup |
| HTTP | axios (`api/index.js`) | REST client, baseURL hardcoded to `http://192.168.1.52:8001`, 401 → refresh → retry |
| i18n | vue-i18n 9 (en + zh-CN) | Composition mode (`legacy: false`), locale persisted to `localStorage('preferred-locale')` |
| Calendar | `@fullcalendar/*` 6.1 | Task view powered by `TaskView.vue` |
| Icons | `@mdi/font` 7 | Material Design Icons set |
| Quality | ESLint 9 + Prettier 3 | Lint with `--fix` and Prettier over `src/` |
> All Vuetify labs components must be imported at the call site — only the core set is global.
---
## Core Features
- **Master Data CRUD** — Students, Subjects, Terms, Stories (each with its own `*.vue` view under `src/views/`)
- **Task Calendar** — FullCalendar-driven `TaskView.vue` with image attachments fetched as blobs
- **JWT Authentication** — Access + refresh tokens in `localStorage`; axios interceptor refreshes and retries once per request, second 401 triggers logout
- **Bilingual i18n** — English (source) and `zh-CN`, switched via `LanguageSwitcher.vue`
- **Layout system**`App.vue` picks a layout from `route.meta.layout` (`'default'` for authenticated pages, `'none'` for `/login`)
---
## Configuration
- [[01.10 configs]]
---
## Hierarchy
- [[01.01 src-api]]
- [[01.02 src-components]]
- [[01.03 src-views]]
- [[01.04 src-router]]
- [[01.05 src-stores]]
- [[01.06 src-locales]]
- [[01.07 src-plugins]]
- [[01.08 src-utils]]
- [[01.09 src-assets-styles]]
- [[01.11 django-backend]]
---
**Location:** `C:\Projects\vue\student-app-frontend` | Tags: #Vue3 #Vuetify #JavaScript #前端 #Pinia
---
jd-id: "01.01"
name: "src-api"
desc: "REST client layer for the Django backend. Centralizes the axios instance with the JWT refresh interceptor and exposes one service module per resource domain."
author: "Clark Lin"
updateTime: "2026-07-06"
status: active
type: "A-高层系统"
tags:
- Service
- axios
- REST
- JWT
---
## Core Files
| File path | Role |
|-----------|------|
| `src/api/index.js` | Shared axios `apiClient` (`baseURL: http://192.168.1.52:8001`, `timeout: 30s`, JSON headers) and the `apiEndpoints` table (currently stale for `terms`, missing for `task`/`story`) |
| `src/api/authService.js` | `login()`, `refreshAccessToken()` — wraps `/api/token/` and `/api/token/refresh/` |
| `src/api/studentService.js` | CRUD for `/api/students/`; ships a `fetchAllPages()` helper that loops `page_size: 100` with a 100-page safety cap |
| `src/api/subjectService.js` | CRUD for `/api/subjects/` |
| `src/api/termService.js` | CRUD for `/api/terms/` |
| `src/api/storyService.js` | CRUD for `/api/stories/` (paths hardcoded; not present in `apiEndpoints`) |
| `src/api/taskService.js` | CRUD for tasks, including `getTaskImage(id)` which fetches images as `blob` with `Accept: */*` to avoid 406s; duplicates the `fetchAllPages()` helper verbatim from `studentService.js` |
| `src/api/README.md` | Working notes for the service layer |
---
## Conventions
- Plain JS modules exporting `async` functions that return `response.data`.
- Service functions log heavily to `console` (including sample data) when called.
- Base64 payloads are redacted in `studentService` — mirror that pattern when adding endpoints that return images/audio.
- DRF pagination responses (`{ count, next, previous, results }`) are normalized via the duplicated `fetchAllPages()` helper — if pagination logic changes, **update both** `studentService.js` and `taskService.js` (or factor it out).
---
Home: [[01.00 student-app-frontend]] | **Location:** `C:\Projects\vue\student-app-frontend\src\api`
---
jd-id: "01.02"
name: "src-components"
desc: "Shared UI building blocks for the authenticated layout (header, drawer, footer, layout shell) plus the language switcher and the global menu items definition."
author: "Clark Lin"
updateTime: "2026-07-06"
status: active
type: "A-高层系统"
tags:
- 前端
- Vue3
- Vuetify
- Components
---
## Core Files
| File path | Role |
|-----------|------|
| `src/components/AppLayout.vue` | Authenticated page chrome — composes `AppHeader` + `AppDrawer` + breadcrumbs + logout dialog; uses `100dvh` min-height so mobile full-bleed works on real devices |
| `src/components/AppHeader.vue` | Top app bar; renders the title, `LanguageSwitcher`, user info and triggers the logout dialog |
| `src/components/AppDrawer.vue` | Left navigation; resolves menu items from `components/constants/menuItems.js` |
| `src/components/AppFooter.vue` | Footer shown within the default layout |
| `src/components/LanguageSwitcher.vue` | Persists the user's locale choice via `setLocale(...)` into `localStorage('preferred-locale')` |
| `src/components/constants/menuItems.js` | `GLOBAL_MENU_ITEMS` with nested `children` for the **Master Data** group; matched against `route.meta.breadcrumbPath` to resolve icons in `AppLayout` |
| `src/components/constants/colors.js` | Shared color tokens for use across views |
---
## Conventions
- All authenticated views must keep `meta.layout: 'default'` in the router; the login route sets `meta.layout: 'none'` so `App.vue` renders a bare `<div>`.
- Breadcrumbs are **not** derived from the path. Each route declares `meta.breadcrumbPath` (an array of `{ key, to, disabled }` i18n keys); `AppLayout` resolves them and applies a fixed `home`/`master-data` icon, falling back to `meta.breadcrumb.icon` or matching `menuItems` for the leaf entry.
---
Home: [[01.00 student-app-frontend]] | **Location:** `C:\Projects\vue\student-app-frontend\src\components`
---
jd-id: "01.03"
name: "src-views"
desc: "Top-level route components one `.vue` file per URL in the SPA. Each is a `<script setup>` SFC using Vuetify components and the localized strings from `src/locales`."
author: "Clark Lin"
updateTime: "2026-07-06"
status: active
type: "A-高层系统"
tags:
- 前端
- Vue3
- Pages
---
## Core Files
| File path | Route | Role |
|-----------|-------|------|
| `src/views/LoginView.vue` | `/login` | Bare layout (`meta.layout: 'none'`); submits credentials via `useAuthStore().login(...)` |
| `src/views/HomeView.vue` | `/` | Landing page after authentication |
| `src/views/ProfileView.vue` | `/profile` | Current user profile |
| `src/views/StudentView.vue` | `/master-data/students` | Students CRUD |
| `src/views/SubjectView.vue` | `/master-data/subjects` | Subjects CRUD |
| `src/views/TermView.vue` | `/master-data/terms` | Terms CRUD |
| `src/views/StoryView.vue` | `/master-data/stories` | Stories CRUD |
| `src/views/TaskView.vue` | `/tasks` | FullCalendar task view; fetches image attachments via `getTaskImage(...)` (blob) |
| `src/views/SettingsView.vue` | `/settings` | App-level settings (locale via `LanguageSwitcher`) |
---
## Conventions
- All views use `<script setup>` Composition API.
- Localized strings must come from `useI18n()` / `$t(...)` — never hardcoded English.
- Each view is paired with parallel translation keys under `src/locales/{en,zh-CN}/modules/*.js`.
- Add new authenticated pages with `meta.layout: 'default'` and `meta.requiresAuth: true`, and declare `meta.breadcrumbPath` so breadcrumbs render in `AppLayout`.
---
Home: [[01.00 student-app-frontend]] | **Location:** `C:\Projects\vue\student-app-frontend\src\views`
---
jd-id: "01.04"
name: "src-router"
desc: "vue-router 4 configuration. Declares every authenticated route with auth + layout metadata, plus a navigation guard that proactively refreshes near-expiry access tokens and skips auth when the client is on the LAN."
author: "Clark Lin"
updateTime: "2026-07-06"
status: active
type: "A-高层系统"
tags:
- 前端
- Vue3
- Routing
- AuthGuard
---
## Core Files
| File path | Role |
|-----------|------|
| `src/router/index.js` | Creates the router with `createWebHistory(import.meta.env.BASE_URL)`, declares all routes, exports `beforeEach` guard. |
---
## Route Map
| Path | Name | `meta.requiresAuth` | `meta.layout` | Notes |
|------|------|---------------------|---------------|-------|
| `/` | `home` | true | `default` | Landing view |
| `/login` | `login` | false | `none` | Bare layout |
| `/profile` | `profile` | true | `default` | |
| `/master-data/students` | `students` | true | `default` | Master Data group |
| `/master-data/subjects` | `subjects` | true | `default` | Master Data group |
| `/master-data/terms` | `terms` | true | `default` | Master Data group |
| `/master-data/stories` | `stories` | true | `default` | Master Data group |
| `/tasks` | `tasks` | true | `default` | Calendar view |
| `/settings` | `settings` | true | `default` | |
| `/:pathMatch(.*)*` | — | — | — | Redirects to `/` |
> Each authenticated route carries a `meta.breadcrumbPath` array (`{ key, to, disabled }`) that `AppLayout` resolves against the i18n bundles.
---
## Navigation Guard
- **LAN bypass**`isLanAccess()` returns `true` → guard lets the navigation through without auth checks (used during local development when the device is on the same network as the backend).
- **Unauthenticated access** — when `to.meta.requiresAuth && !isAuthenticated`, redirect to `/login`.
- **Proactive refresh** — when the access token will expire within the threshold (default `60s`, see `isAccessTokenExpiringSoon`), the guard awaits `authStore.refreshAccessToken()` before navigating. Failure path cleans up tokens and redirects to `/login`.
- **Already authenticated** — visiting `/login` while logged in redirects to `/`.
- **Login-endpoint 401s** — handled separately by the axios interceptor; they are not retried via the refresh flow.
---
Home: [[01.00 student-app-frontend]] | **Location:** `C:\Projects\vue\student-app-frontend\src\router`
---
jd-id: "01.05"
name: "src-stores"
desc: "Pinia stores. The auth store owns the JWT lifecycle, axios interceptor setup, and route push; a counter stub remains for the default scaffolding."
author: "Clark Lin"
updateTime: "2026-07-06"
status: active
type: "A-高层系统"
tags:
- 前端
- Pinia
- StateManagement
- JWT
---
## Core Files
| File path | Role |
|-----------|------|
| `src/stores/auth.js` | The single production store. Defines `useAuthStore` (setup-style), persists tokens in `localStorage`, decodes JWT claims, exposes `isAuthenticated` / `isAccessTokenExpired` / `isAccessTokenExpiringSoon`, and wires the axios request/response interceptors exactly once (deduped via a module-level `refreshPromise`). |
| `src/stores/counter.js` | Boilerplate from the Vue scaffold; unused by the app. |
---
## Auth Store Surface
- **State**`accessToken`, `refreshToken`, `user`, `interceptorsInitialized`, `isRefreshing` (`refreshPromise` is a module-level `let`).
- **Computed**`isAuthenticated` (`!!accessToken`).
- **JWT helpers**`decodeJwt(jwt)`, `getAccessTokenExp()`, `isAccessTokenExpired()`, `isAccessTokenExpiringSoon(thresholdSeconds = 60)`.
- **Axios wiring**`setAxiosAuthHeader(token)`, `initializeAuth()` (called once from `main.js`), and `refreshAccessToken()` (uses the module-level `refreshPromise` to dedupe concurrent refreshes; second 401 in a request → `logout()`).
- **Lifecycle**`login(credentials)` navigates on success; `logout()` clears tokens and routes back to `/login`.
> Login-endpoint 401s are intentionally **not** passed through the retry flow — only requests that hit a regular endpoint (not `/api/token/`) get the refresh + retry treatment.
---
Home: [[01.00 student-app-frontend]] | **Location:** `C:\Projects\vue\student-app-frontend\src\stores`
---
jd-id: "01.06"
name: "src-locales"
desc: "vue-i18n message bundles. Two locales (`en`, `zh-CN`) mirrored as parallel trees; English is the source. Locale resolves from `localStorage('preferred-locale')` browser language `en`."
author: "Clark Lin"
updateTime: "2026-07-06"
status: active
type: "A-高层系统"
tags:
- 前端
- i18n
- vue-i18n
---
## Layout
```
src/locales/
├── index.js # vue-i18n setup (legacy: false), locale resolution, message merge
├── en/
│ ├── index.js # Aggregates en/* into a single message object
│ ├── common/
│ │ ├── buttons.js
│ │ ├── messages.js
│ │ ├── status.js
│ │ ├── table.js
│ │ └── validation.js
│ ├── components/
│ │ └── app-header.js
│ └── modules/
│ ├── auth.js
│ ├── navigation.js
│ ├── settings.js
│ ├── story.js
│ ├── student.js
│ ├── subject.js
│ ├── task.js
│ └── term.js
└── zh-CN/
├── index.js
├── common/...
├── components/app-header.js
└── modules/...
```
---
## Conventions
- **English is the source locale.** Mirror every new key into `zh-CN` in the same shape.
- Use `$t(...)` in templates and `const { t } = useI18n()` inside `<script setup>`.
- Locale is persisted across reloads: `LanguageSwitcher.vue` and `setLocale()` write the user's choice to `localStorage('preferred-locale')`.
- Domain namespaces are organized as `modules/<domain>.js` (one file per feature view), with cross-cutting strings under `common/` and per-component texts under `components/`.
---
Home: [[01.00 student-app-frontend]] | **Location:** `C:\Projects\vue\student-app-frontend\src\locales`
---
jd-id: "01.07"
name: "src-plugins"
desc: "Vuetify integration. Registers the component library and MD icons as a Vue plugin; defines the app-wide light theme."
author: "Clark Lin"
updateTime: "2026-07-06"
status: active
type: "A-高层系统"
tags:
- 前端
- Vuetify
- Plugin
---
## Core Files
| File path | Role |
|-----------|------|
| `src/plugins/vuetify.js` | Creates the Vuetify plugin instance; imports `@mdi/font/css/materialdesignicons.css` for icons and `vuetify/styles` for base CSS; uses namespace imports (`vuetify/components`, `vuetify/directives`) to register everything globally. Declares a single `light` theme with custom `primary`/`secondary`/`accent`/`error`/`info`/`success`/`warning` colors. |
---
## Conventions
- All **core** Vuetify components and directives are registered globally via `import * as components from 'vuetify/components'` and `import * as directives from 'vuetify/directives'`.
- **Labs** components are intentionally **not** registered here — import them at the call site (e.g. `import { VSomeLabs } from 'vuetify/labs/VSomeLabs'`).
- Only the light theme is enabled (`defaultTheme: 'light'`). Dark mode is not currently exposed.
> The Vuetify plugin is installed in `src/main.js` together with `pinia`, `router`, and `i18n`.
---
Home: [[01.00 student-app-frontend]] | **Location:** `C:\Projects\vue\student-app-frontend\src\plugins`
---
jd-id: "01.08"
name: "src-utils"
desc: "Cross-cutting helpers used by router, stores, and views."
author: "Clark Lin"
updateTime: "2026-07-06"
status: active
type: "A-高层系统"
tags:
- 前端
- Utilities
---
## Core Files
| File path | Role |
|-----------|------|
| `src/utils/lanAccess.js` | Exports `isLanAccess()` — detected by inspecting `window.location.hostname` against private IPv4 ranges (e.g. `10.*`, `192.168.*`, `172.16–31.*`) so the router guard can skip the JWT requirement during local development. |
---
## Conventions
- Utilities are imported with the `@/` alias (see `vite.config.js`) — e.g. `import { isLanAccess } from '@/utils/lanAccess'`.
- Keep helpers pure and dependency-free; place anything stateful inside a Pinia store instead.
---
Home: [[01.00 student-app-frontend]] | **Location:** `C:\Projects\vue\student-app-frontend\src\utils`
---
jd-id: "01.09"
name: "src-assets-styles"
desc: "Static assets shipped with the bundle plus the small set of CSS rules the app needs beyond Vuetify's stylesheet."
author: "Clark Lin"
updateTime: "2026-07-06"
status: active
type: "A-高层系统"
tags:
- 前端
- Assets
- Styles
---
## Core Files
| File path | Role |
|-----------|------|
| `src/assets/images/login.jpg` | Background image for the login screen |
| `src/assets/images/loginAvatar.jpg` | Avatar overlay for the login screen |
| `src/styles/safe-area.css` | Global CSS for iOS Home Indicator / safe-area insets; imported once from `src/main.js` |
| `src/App.vue` | Root component — selects a layout (`default` vs `none`) from `route.meta.layout` and renders `<router-view />` inside it |
---
## Conventions
- Static imagery and styles that the app references with `@/assets/...` or `@/styles/...` live here.
- All global stylesheets should be imported from `main.js`; per-component scoped styles live alongside each `.vue` file.
- The favicon used in `public/favicon.ico` is duplicated as `dist/favicon.ico` after build (handled by Vite).
---
Home: [[01.00 student-app-frontend]] | **Location:** `C:\Projects\vue\student-app-frontend\src\assets` and `C:\Projects\vue\student-app-frontend\src\styles` and `C:\Projects\vue\student-app-frontend\src\App.vue`
---
jd-id: "01.10"
name: "configs"
desc: "Project-root configuration files. Together they pin the Node engine, alias `@` to `src/`, set up the dev server on `0.0.0.0:5173`, and provide lint + format tooling."
author: "Clark Lin"
updateTime: "2026-07-06"
status: active
type: "A-高层系统"
tags:
- 配置
- Vite
- ESLint
- Prettier
---
## Core Files
| File path | Role |
|-----------|------|
| `package.json` | Dependencies and npm scripts: `dev` (`vite`), `build` (`vite build`), `preview` (`vite preview`), `lint` (`eslint . --fix`), `format` (`prettier --write src/`). Enforces `engines.node: "^20.19.0 || >=22.12.0"`. |
| `vite.config.js` | Vite config — installs `vue` + `vite-plugin-vue-devtools` plugins, registers the `@` alias pointing at `./src`, and binds the dev server to `host: '0.0.0.0'`, `port: 5173` (so other devices on the LAN can reach the app). |
| `eslint.config.js` | Flat-config ESLint setup — lints `**/*.{js,mjs,jsx,vue}`, uses `eslint-plugin-vue` (`flat/essential`), Prettier conflict-free via `skipFormatting`. |
| `jsconfig.json` | IDE/TS path mapping for the `@` alias (`@/* → ./src/*`); excluded `node_modules`, `dist`. |
| `.editorconfig` | Editor baseline (UTF-8, LF, 2-space indent, final newline). |
| `.prettierrc.json` | Prettier rules referenced by `npm run format`. |
| `.gitattributes` | Line-ending / diff handling for cross-platform development. |
| `.vscode/extensions.json` | Workspace-recommended VS Code extensions. |
| `docs/step 0 - 整体规划.md` | Project planning notes (中文). |
| `docs/step 1 - 项目初始化和环境搭建.md` | Setup walkthrough notes. |
---
## Conventions
- Use `npm run dev` for development (the server binds to all interfaces so LAN devices can reach it during paired testing).
- Path imports must use the `@/` alias — see `vite.config.js` (`'@': fileURLToPath(new URL('./src', import.meta.url))`) and `jsconfig.json`.
- No test runner is configured — add new lint/format scripts through `package.json` to keep tooling centralized.
---
Home: [[01.00 student-app-frontend]] | **Location:** `C:\Projects\vue\student-app-frontend` (project root)
---
jd-id: "01.11"
name: "django-backend"
desc: "External dependency the Django REST Framework backend that this frontend talks to over HTTP. The base URL is hardcoded in `src/api/index.js`."
author: "Clark Lin"
updateTime: "2026-07-06"
status: active
type: "A-高层系统"
tags:
- Backend
- Django
- DRF
- JWT
---
- [[01.00 student-app-frontend]] (← parent project)
- [[01.01 src-api]] (← consumer module)
The frontend issues JSON requests to a Django REST Framework service running at `http://192.168.1.52:8001`. Authentication uses JWT — the access + refresh pair is obtained from `/api/token/` and `/api/token/refresh/`.
## Endpoint Map (from `src/api/index.js`)
| Endpoint | Methods | Used by |
|----------|---------|---------|
| `/api/token/` | `POST` | `authService.login` |
| `/api/token/refresh/` | `POST` | `authService.refreshAccessToken`, `stores/auth.js` |
| `/api/students/` | `GET`, `POST` | `studentService` |
| `/api/students/{studentId}/` | `PUT`, `DELETE` | `studentService` |
| `/api/subjects/` | `GET`, `POST` | `subjectService` |
| `/api/subjects/{subjectId}/` | `PUT`, `DELETE` | `subjectService` |
| `/api/terms/` | `GET`, `POST` | `termService` |
| `/api/terms/{termId}/` | `PUT`, `DELETE` | `termService` |
| `/api/stories/` | `GET`, `POST` | `storyService` |
| `/api/stories/{storyId}/` | `PUT`, `DELETE` | `storyService` |
| `/api/tasks/` and task sub-routes | various | `taskService` (paths hardcoded — not in `apiEndpoints`) |
## Notable Behaviors
- DRF returns paginated lists as `{ count, next, previous, results }` — see `fetchAllPages()` in `studentService.js` and `taskService.js`.
- Task **images** are returned as binary. The frontend fetches them with `responseType: 'blob'` and `Accept: */*` (`getTaskImage`) — without that header the backend responds `406 Not Acceptable`.
## External Files in This Repo (Not Part of Vue Build)
The following live at the repo root but are not part of the Vue app:
- `swagger_schema.json` — the OpenAPI/Swagger schema for the backend; consult it before adding/changing API calls.
## Operational Notes
- The base URL is **hardcoded** — change `API_BASE_URL` in `src/api/index.js` when the backend moves (no env-based config exists).
- The `apiEndpoints` table in `src/api/index.js` is partially stale: `TERMS.UPDATE`/`DELETE` are still template strings (`/api/terms/{termId}/`) instead of functions, and `task`/`story` endpoints aren't listed. Prefer hardcoding paths in the service modules over trusting `apiEndpoints` until it's cleaned up.
---
Home: [[01.00 student-app-frontend]] | **Location:** `C:\Projects\vue\student-app-frontend\swagger_schema.json` (spec lives at the project root)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment