diff --git a/.vscode/settings.json b/.vscode/settings.json index 37880ba..933f37e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "cSpell.words": [ "antfu", "beian", + "cognitio", "demi", "iconify", "intlify", diff --git a/docs/成绩修改api.md b/docs/成绩修改api.md new file mode 100644 index 0000000..1e0acc4 --- /dev/null +++ b/docs/成绩修改api.md @@ -0,0 +1,198 @@ +--- +title: 艺考招生管理系统 API +language_tabs: + - shell: Shell + - http: HTTP + - javascript: JavaScript + - ruby: Ruby + - python: Python + - php: PHP + - java: Java + - go: Go +toc_footers: [] +includes: [] +search: true +code_clipboard: true +highlight_theme: darkula +headingLevel: 2 +generator: "@tarslib/widdershins v4.0.30" + +--- + +# 艺考招生管理系统 API + +提供用户认证、院校专业、历年招生、计算专业的管理接口 + +Base URLs: + +# Authentication + +# 用户分数操作 + +## POST 保存用户成绩 + +POST /user/score/save-score + +> Body 请求参数 + +```json +{ + "cognitioPolyclinic": "文科", + "subjectList": [ + "化学", + "生物", + "地理" + ], + "professionalCategory": "表演类", + "professionalCategoryChildren": [ + "服装表演", + "戏剧影视表演" + ], + "professionalCategoryChildrenScore": { + "服装表演": 50, + "戏剧影视表演": 10 + }, + "professionalScore": 250, + "culturalScore": 500, + "englishScore": 120, + "chineseScore": 121, + "province": "河南" +} +``` + +### 请求参数 + +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|body|body|[dto.SaveScoreRequest](#schemadto.savescorerequest)| 是 |none| + +> 返回示例 + +> 200 Response + +``` +{"code":200,"message":"success","data":{"id":"bdd80291-797e-451c-9bf0-c81705362dc9","type":"1","educationalLevel":"1","professionalCategory":"表演类","subjects":"化学,生物,地理","professionalScore":250,"culturalScore":500,"ranking":0,"createBy":"1779515858733772802","createTime":"0001-01-01T00:00:00Z","updateBy":"","updateTime":"0001-01-01T00:00:00Z","state":"1","province":"河南","cognitioPolyclinic":"文科","batch":"","englishScore":120,"chineseScore":121,"yybysy":0,"yybyqy":0,"yyjy":0,"xjysdy":0,"xjysby":10,"fzby":50,"professionalCategoryChildren":"服装表演,戏剧影视表演","kbdNum":0,"nlqNum":0,"kcjNum":0,"jwtNum":0,"calculationTableName":""}} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|OK|[common.Response](#schemacommon.response)| + +## GET 获取当前用户的当前分数 + +GET /user/score + +> 返回示例 + +> 200 Response + +``` +{"code":200,"message":"success","data":{"id":"2000794240905117697","type":"2","educationalLevel":"1","cognitioPolyclinic":"文科","subjectList":["政治","化学"],"professionalCategory":"美术与设计类","professionalCategoryChildren":[],"professionalCategoryChildrenScore":{},"professionalScore":234,"culturalScore":500,"englishScore":1,"chineseScore":1,"province":"河南","state":"1"}} +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|OK|Inline| + +### 返回数据结构 + +状态码 **200** + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|» code|integer|true|none||none| +|» message|string|true|none||none| +|» data|object|true|none||none| +|»» id|string|true|none||none| +|»» type|string|true|none||none| +|»» educationalLevel|string|true|none|1-本科,2-专科|none| +|»» cognitioPolyclinic|string|true|none|文理分班(文科/理科)|none| +|»» subjectList|[string]|true|none|专业选课|none| +|»» professionalCategory|string|true|none|专业类别(美术与设计类、音乐类...)|none| +|»» professionalCategoryChildren|[string]|true|none|专业子级列表|none| +|»» professionalCategoryChildrenScore|object|true|none|专业子级成绩|none| +|»» professionalScore|integer|true|none|统考成绩|none| +|»» culturalScore|integer|true|none|文化成绩|none| +|»» englishScore|integer|true|none|英语成绩|none| +|»» chineseScore|integer|true|none|语文成绩|none| +|»» province|string|true|none|地区|none| +|»» state|string|true|none|状态(1-启用,2-关闭)|none| + +# 数据模型 + +

common.Response

+ + + + + + +```json +{ + "code": 0, + "data": null, + "message": "string" +} + +``` + +### 属性 + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|code|integer|false|none||none| +|data|any|false|none||none| +|message|string|false|none||none| + +

dto.SaveScoreRequest

+ + + + + + +```json +{ + "ChineseScore": 0, + "CognitioPolyclinic": "string", + "CulturalScore": 0, + "EnglishScore": 0, + "ProfessionalCategory": "string", + "ProfessionalCategoryChildren": [ + "string" + ], + "ProfessionalCategoryChildrenScore": { + "property1": 0, + "property2": 0 + }, + "ProfessionalScore": 0, + "Province": "string", + "SubjectList": [ + "string" + ], + "createBy": "string" +} + +``` + +### 属性 + +|名称|类型|必选|约束|中文名|说明| +|---|---|---|---|---|---| +|ChineseScore|number|true|none||none| +|CognitioPolyclinic|string|true|none||none| +|CulturalScore|number|true|none||none| +|EnglishScore|number|true|none||none| +|ProfessionalCategory|string|true|none||none| +|ProfessionalCategoryChildren|[string]|true|none||none| +|ProfessionalCategoryChildrenScore|object|true|none||none| +|» **additionalProperties**|number(float64)|false|none||none| +|ProfessionalScore|number|true|none||none| +|Province|string|true|none||none| +|SubjectList|[string]|true|none||none| +|createBy|string|false|none||none| + diff --git a/project_codebase.md b/project_codebase.md index 8508b23..8bfe9a2 100644 --- a/project_codebase.md +++ b/project_codebase.md @@ -11,21 +11,34 @@ - **Purpose**: Encapsulates Axios for API requests. - **Features**: - Request Interceptor: - - Adds `token` to headers. - - Adds `X-App-Sign` (MD5(timestamp + secret)) and `X-App-Timestamp` headers for security. + - Adds `Authorization: Bearer `. + - Adds `X-App-Sign` and `X-App-Timestamp` headers. - Starts `NProgress`. - - Response Interceptor: Unwraps `data`; handles global errors (401, 403, 500); stops `NProgress`. + - Response Interceptor: Unwraps `data`; handles global errors (401, 403, 500); prioritizes backend error messages; stops `NProgress`. - Config: `showLoading`, `showError`. ### `src/service/api/auth.ts` - **Purpose**: API definitions for authentication. - **Methods**: `login`, `logout`, `getUserInfo`. +### `src/service/api/score.ts` +- **Purpose**: API definitions for score management. +- **Methods**: `getScore`, `saveScore`. +- **Types**: `SaveScoreRequest`, `ScoreInfo`. + ### `src/stores/user.ts` - **Purpose**: Manages user session state. - **State**: `token`, `userInfo`. - **Actions**: `login` (calls API), `logout`, `setToken`, `setUserInfo`. - **Persistence**: Loads/Saves state to `localStorage`. +### `src/stores/score.ts` +- **Purpose**: Manages user score state. +- **State**: `scoreInfo`. +- **Actions**: `fetchScore`, `saveScore`, `clearScore`. + ### `src/components/TheNavigation.vue` -- **Updated**: Added integration with `userStore` for login/logout and `message` for notifications. +- **Updated**: Added integration with `userStore` and `scoreStore`. Displays user info and score info. Handles login/logout logic. + +### `src/components/ScoreForm.vue` +- **Updated**: Integrated with `scoreStore` to load and save score data. Maps form state to backend API structure. diff --git a/project_doing.md b/project_doing.md index 6093cb5..5ce9598 100644 --- a/project_doing.md +++ b/project_doing.md @@ -12,3 +12,25 @@ - `src/service/api/auth.ts` (Create) - `src/stores/user.ts` (Update) - `src/components/TheNavigation.vue` (Update) + +### [Task 2] Score API Encapsulation and Business Integration +- **Time**: 2025-12-18 +- **Goal**: Encapsulate Score API and integrate into components. +- **Scope**: + - `src/service/api/score.ts` (Create) + - `src/stores/score.ts` (Create) + - `src/components/TheNavigation.vue` (Update) + - `src/components/ScoreForm.vue` (Update) + - `project_task.md` (Update) + +### [Task 2] Verification and Documentation +- **Time**: 2025-12-18 +- **Goal**: Verify score integration and update documentation. +- **Scope**: + - `project_task.md` (Update status) + +### [Task 2] Fix Score Refresh Issue +- **Time**: 2025-12-18 +- **Goal**: Ensure score data is fetched when ScoreForm mounts if store is empty. +- **Scope**: + - `src/components/ScoreForm.vue` (Update onMounted) diff --git a/project_index.md b/project_index.md index 894da7a..62022df 100644 --- a/project_index.md +++ b/project_index.md @@ -3,6 +3,9 @@ - `src/utils/message.ts`: Global message utility. - `src/service/request/index.ts`: Axios wrapper. - `src/service/api/auth.ts`: Authentication API. +- `src/service/api/score.ts`: Score API. - `src/stores/user.ts`: User Pinia store. +- `src/stores/score.ts`: Score Pinia store. - `src/components/ui/WMessage.vue`: Message component UI. - `src/components/TheNavigation.vue`: Main navigation component. +- `src/components/ScoreForm.vue`: Score editing form. diff --git a/project_task.md b/project_task.md index 0d14a0f..d51dce3 100644 --- a/project_task.md +++ b/project_task.md @@ -1,9 +1,24 @@ # Project Tasks -- [ ] [Task 1] Global Message Component, API Encapsulation, and Login/Logout Integration - - [ ] Check/Implement Global Message Component - - [ ] Install axios - - [ ] Create `src/service/request/index.ts` - - [ ] Create `src/service/api/auth.ts` - - [ ] Update `src/stores/user.ts` (Pinia + Persistence) - - [ ] Integrate Login/Logout in `TheNavigation.vue` +- [x] [Task 1] Global Message Component, API Encapsulation, and Login/Logout Integration + - [x] Check/Implement Global Message Component + - [x] Install axios + - [x] Create `src/service/request/index.ts` + - [x] Create `src/service/api/auth.ts` + - [x] Update `src/stores/user.ts` (Pinia + Persistence) + - [x] Integrate Login/Logout in `TheNavigation.vue` + - [x] Install crypto-js and types + - [x] Configure API secret in environment variables + - [x] Update Axios request interceptor with signature logic + - [x] Fix CORS by updating VITE_API_BASE_URL + - [x] Refactor vite.config.ts to use loadEnv for dynamic proxy configuration + - [x] Update .env.development with VITE_API_PROXY_TARGET + - [x] Prioritize backend error message in Axios response interceptor + - [x] Check TheNavigation.vue for reactive user state usage + - [x] Update TheNavigation.vue template to toggle Login/User info based on store state + +- [x] [Task 2] Score API Encapsulation and Business Integration + - [x] Create `src/service/api/score.ts` with types + - [x] Create `src/stores/score.ts` + - [x] Integrate Get Score in `TheNavigation.vue` + - [x] Integrate Save Score in `ScoreForm.vue` diff --git a/src/auto-imports.d.ts b/src/auto-imports.d.ts index d6ad0a4..4a86acd 100644 --- a/src/auto-imports.d.ts +++ b/src/auto-imports.d.ts @@ -245,6 +245,7 @@ declare global { const useRoute: typeof import('vue-router')['useRoute'] const useRouter: typeof import('vue-router')['useRouter'] const useSSRWidth: typeof import('@vueuse/core')['useSSRWidth'] + const useScoreStore: typeof import('./stores/score')['useScoreStore'] const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] @@ -570,6 +571,7 @@ declare module 'vue' { readonly useRoute: UnwrapRef readonly useRouter: UnwrapRef readonly useSSRWidth: UnwrapRef + readonly useScoreStore: UnwrapRef readonly useScreenOrientation: UnwrapRef readonly useScreenSafeArea: UnwrapRef readonly useScriptTag: UnwrapRef diff --git a/src/components.d.ts b/src/components.d.ts index 653b699..e2ea1d0 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -9,7 +9,9 @@ export {} declare module 'vue' { export interface GlobalComponents { BackToTop: typeof import('./components/BackToTop.vue')['default'] + copy: typeof import('./components/ScoreForm copy.vue')['default'] FilterBar: typeof import('./components/FilterBar.vue')['default'] + LoginForm: typeof import('./components/LoginForm.vue')['default'] README: typeof import('./components/README.md')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue new file mode 100644 index 0000000..682b27e --- /dev/null +++ b/src/components/LoginForm.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/src/components/ScoreForm.vue b/src/components/ScoreForm.vue index bf414b3..9d0f807 100644 --- a/src/components/ScoreForm.vue +++ b/src/components/ScoreForm.vue @@ -26,7 +26,7 @@ const emit = defineEmits<{ }>() // --- Form State --- -const examType = ref('历史组') +const examType = ref('') const selectedElectives = ref([]) const majorCategory = ref('') const selectedSubMajors = ref([]) @@ -260,7 +260,14 @@ function initForm() { } onMounted(() => { - initForm() + if (!scoreStore.scoreInfo) { + // scoreStore.fetchScore().catch(() => { + // // 忽略错误,可能用户未设置成绩 + // }) + } + else { + initForm() + } }) watch(() => scoreStore.scoreInfo, () => { diff --git a/src/components/TheNavigation.vue b/src/components/TheNavigation.vue index a8b5c12..84edb8d 100644 --- a/src/components/TheNavigation.vue +++ b/src/components/TheNavigation.vue @@ -1,12 +1,12 @@