updates
This commit is contained in:
parent
57f7937c2d
commit
256c34d5af
|
|
@ -1,5 +1,10 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, onMounted, watch } from 'vue'
|
||||||
|
import { useScoreStore } from '~/stores/score'
|
||||||
|
import { type SaveScoreRequest } from '~/service/api/score'
|
||||||
|
import message from '~/utils/message'
|
||||||
|
|
||||||
|
const scoreStore = useScoreStore()
|
||||||
|
|
||||||
// 定义回传的数据类型
|
// 定义回传的数据类型
|
||||||
export interface ScoreFormData {
|
export interface ScoreFormData {
|
||||||
|
|
@ -25,6 +30,7 @@ const examType = ref('历史组')
|
||||||
const selectedElectives = ref<string[]>([])
|
const selectedElectives = ref<string[]>([])
|
||||||
const majorCategory = ref('')
|
const majorCategory = ref('')
|
||||||
const selectedSubMajors = ref<string[]>([])
|
const selectedSubMajors = ref<string[]>([])
|
||||||
|
const isSubmitting = ref(false)
|
||||||
|
|
||||||
// Score inputs
|
// Score inputs
|
||||||
const scores = ref({
|
const scores = ref({
|
||||||
|
|
@ -54,6 +60,8 @@ const electiveOptions = [
|
||||||
{ label: '政治', value: '政治' },
|
{ label: '政治', value: '政治' },
|
||||||
{ label: '化学', value: '化学' },
|
{ label: '化学', value: '化学' },
|
||||||
{ label: '生物', value: '生物' },
|
{ label: '生物', value: '生物' },
|
||||||
|
{ label: '历史', value: '历史' },
|
||||||
|
{ label: '物理', value: '物理' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const majorCategoryOptions = [
|
const majorCategoryOptions = [
|
||||||
|
|
@ -194,7 +202,7 @@ function validateForm() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Submit ---
|
// --- Submit ---
|
||||||
function handleSubmit() {
|
async function handleSubmit() {
|
||||||
if (validateForm()) {
|
if (validateForm()) {
|
||||||
// 组装数据
|
// 组装数据
|
||||||
const formData: ScoreFormData = {
|
const formData: ScoreFormData = {
|
||||||
|
|
@ -205,11 +213,60 @@ function handleSubmit() {
|
||||||
scores: { ...scores.value },
|
scores: { ...scores.value },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transform to API request
|
||||||
|
const requestData: SaveScoreRequest = {
|
||||||
|
cognitioPolyclinic: examType.value === '历史组' ? '文科' : '理科',
|
||||||
|
subjectList: selectedElectives.value,
|
||||||
|
professionalCategory: majorCategory.value,
|
||||||
|
professionalCategoryChildren: selectedSubMajors.value,
|
||||||
|
professionalCategoryChildrenScore: {},
|
||||||
|
professionalScore: Number(scores.value.unified) || 0,
|
||||||
|
culturalScore: Number(scores.value.culture) || 0,
|
||||||
|
englishScore: Number(scores.value.english) || 0,
|
||||||
|
chineseScore: Number(scores.value.chinese) || 0,
|
||||||
|
province: '河南', // Default
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
isSubmitting.value = true
|
||||||
|
await scoreStore.saveScore(requestData)
|
||||||
|
// 刷新本地数据
|
||||||
|
scoreStore.fetchScore()
|
||||||
|
message.success('保存成功')
|
||||||
// 抛出事件
|
// 抛出事件
|
||||||
emit('confirm', formData)
|
emit('confirm', formData)
|
||||||
|
} catch (e: any) {
|
||||||
|
message.error(e.message || '保存失败')
|
||||||
|
} finally {
|
||||||
|
isSubmitting.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Init ---
|
||||||
|
function initForm() {
|
||||||
|
console.warn('initForm', scoreStore.scoreInfo)
|
||||||
|
if (scoreStore.scoreInfo) {
|
||||||
|
const info = scoreStore.scoreInfo
|
||||||
|
examType.value = info.cognitioPolyclinic === '文科' ? '历史组' : '物理组'
|
||||||
|
selectedElectives.value = info.subjectList || []
|
||||||
|
majorCategory.value = info.professionalCategory || ''
|
||||||
|
selectedSubMajors.value = info.professionalCategoryChildren || []
|
||||||
|
scores.value.unified = info.professionalScore?.toString() || ''
|
||||||
|
scores.value.culture = info.culturalScore?.toString() || ''
|
||||||
|
scores.value.chinese = info.chineseScore?.toString() || ''
|
||||||
|
scores.value.english = info.englishScore?.toString() || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initForm()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => scoreStore.scoreInfo, () => {
|
||||||
|
initForm()
|
||||||
|
})
|
||||||
|
|
||||||
// 暴露重置方法给父组件(如果需要)
|
// 暴露重置方法给父组件(如果需要)
|
||||||
defineExpose({
|
defineExpose({
|
||||||
resetForm: () => {
|
resetForm: () => {
|
||||||
|
|
@ -333,8 +390,8 @@ defineExpose({
|
||||||
v-model="scores.culture"
|
v-model="scores.culture"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="300"
|
max="750"
|
||||||
placeholder="0-300"
|
placeholder="0-750"
|
||||||
class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
class="w-full border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
>
|
>
|
||||||
<div v-if="errors.scores.culture" class="mt-2 text-sm text-red-600">
|
<div v-if="errors.scores.culture" class="mt-2 text-sm text-red-600">
|
||||||
|
|
@ -377,7 +434,7 @@ defineExpose({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Submit Button -->
|
<!-- Submit Button -->
|
||||||
<button
|
<button :disabled="isSubmitting"
|
||||||
class="w-full rounded-full bg-blue-600 px-6 py-3 text-lg text-white font-semibold shadow-lg transition-colors hover:bg-blue-700 hover:shadow-xl"
|
class="w-full rounded-full bg-blue-600 px-6 py-3 text-lg text-white font-semibold shadow-lg transition-colors hover:bg-blue-700 hover:shadow-xl"
|
||||||
@click="handleSubmit"
|
@click="handleSubmit"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,14 @@
|
||||||
import type { ScoreFormData } from './ScoreForm.vue'
|
import type { ScoreFormData } from './ScoreForm.vue'
|
||||||
import { useWindowScroll } from '@vueuse/core' // 假设你使用了 VueUse
|
import { useWindowScroll } from '@vueuse/core' // 假设你使用了 VueUse
|
||||||
import { ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useUserStore } from '../stores/user'
|
import { useUserStore } from '../stores/user'
|
||||||
|
import { useScoreStore } from '../stores/score'
|
||||||
import message from '~/utils/message'
|
import message from '~/utils/message'
|
||||||
|
import { logout as apiLogout } from '~/service/api/auth'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
const scoreStore = useScoreStore()
|
||||||
const isLoginModalOpen = ref(false)
|
const isLoginModalOpen = ref(false)
|
||||||
// 新增:控制移动端菜单开关的状态
|
// 新增:控制移动端菜单开关的状态
|
||||||
const isMobileMenuOpen = ref(false)
|
const isMobileMenuOpen = ref(false)
|
||||||
|
|
@ -259,10 +262,16 @@ function handleMobileLinkClick() {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="mb-3 flex items-center px-3">
|
<div v-if="scoreStore.scoreInfo" class="mb-3 flex items-center px-3">
|
||||||
<span class="border-r-2 border-gray-3 pr-2 text-sm text-gray-700 dark:text-gray-200">音乐类</span>
|
<span class="border-r-2 border-gray-3 pr-2 text-sm text-gray-700 dark:text-gray-200">{{ scoreStore.scoreInfo.professionalCategory || '未设置' }}</span>
|
||||||
<span class="border-r-2 border-gray-3 pl-2 pr-2 text-sm text-gray-700 dark:text-gray-200">文化成绩</span>
|
<span class="border-r-2 border-gray-3 pl-2 pr-2 text-sm text-gray-700 dark:text-gray-200">文化成绩</span>
|
||||||
<span class="pl-2 pr-2 text-sm text-gray-700 dark:text-gray-200">334</span>
|
<span class="pl-2 pr-2 text-sm text-gray-700 dark:text-gray-200">{{ scoreStore.scoreInfo.culturalScore }}</span>
|
||||||
|
<a target="_blank" class="cursor-pointer text-gray-400 transition-colors hover:text-gray-500 dark:hover:text-gray-300" @click="openScoreFormModal">
|
||||||
|
<div i-carbon:edit class="text-xs" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div v-else class="mb-3 flex items-center px-3">
|
||||||
|
<span class="text-sm text-gray-700 dark:text-gray-200">未设置分数</span>
|
||||||
<a target="_blank" class="cursor-pointer text-gray-400 transition-colors hover:text-gray-500 dark:hover:text-gray-300" @click="openScoreFormModal">
|
<a target="_blank" class="cursor-pointer text-gray-400 transition-colors hover:text-gray-500 dark:hover:text-gray-300" @click="openScoreFormModal">
|
||||||
<div i-carbon:edit class="text-xs" />
|
<div i-carbon:edit class="text-xs" />
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,13 @@ export interface LoginResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function login(params: LoginParams) {
|
export function login(params: LoginParams) {
|
||||||
return request.post<LoginResult>('/auth/login', params)
|
return request.post<LoginResult>('/user/auth/login', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logout() {
|
export function logout() {
|
||||||
return request.post<null>('/auth/logout')
|
return request.post<null>('/user/auth/logout')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUserInfo() {
|
export function getUserInfo() {
|
||||||
return request.get<UserInfo>('/auth/info')
|
return request.get<UserInfo>('/user/auth/info')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import request from '../request'
|
||||||
|
|
||||||
|
export interface SaveScoreRequest {
|
||||||
|
cognitioPolyclinic: string
|
||||||
|
subjectList: string[]
|
||||||
|
professionalCategory: string
|
||||||
|
professionalCategoryChildren: string[]
|
||||||
|
professionalCategoryChildrenScore: Record<string, number>
|
||||||
|
professionalScore: number
|
||||||
|
culturalScore: number
|
||||||
|
englishScore: number
|
||||||
|
chineseScore: number
|
||||||
|
province: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScoreInfo {
|
||||||
|
id: string
|
||||||
|
type: string
|
||||||
|
educationalLevel: string
|
||||||
|
cognitioPolyclinic: string
|
||||||
|
subjectList: string[]
|
||||||
|
professionalCategory: string
|
||||||
|
professionalCategoryChildren: string[]
|
||||||
|
professionalCategoryChildrenScore: Record<string, number>
|
||||||
|
professionalScore: number
|
||||||
|
culturalScore: number
|
||||||
|
englishScore: number
|
||||||
|
chineseScore: number
|
||||||
|
province: string
|
||||||
|
state: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveScore(data: SaveScoreRequest) {
|
||||||
|
return request.post<ScoreInfo>('/user/score/save-score', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getScore() {
|
||||||
|
return request.get<ScoreInfo>('/user/score')
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import CryptoJS from 'crypto-js'
|
||||||
import { useUserStore } from '~/stores/user'
|
import { useUserStore } from '~/stores/user'
|
||||||
import message from '~/utils/message'
|
import message from '~/utils/message'
|
||||||
import { InternalAxiosRequestConfig } from 'axios'
|
import { InternalAxiosRequestConfig } from 'axios'
|
||||||
|
|
||||||
interface CustomRequestConfig extends InternalAxiosRequestConfig {
|
interface CustomRequestConfig extends InternalAxiosRequestConfig {
|
||||||
showLoading?: boolean,
|
showLoading?: boolean,
|
||||||
showError?: boolean
|
showError?: boolean
|
||||||
|
|
@ -81,7 +82,10 @@ class Request {
|
||||||
case 401:
|
case 401:
|
||||||
msg = backendMsg || 'Unauthorized'
|
msg = backendMsg || 'Unauthorized'
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
userStore.logout()
|
userStore.clearToken()
|
||||||
|
userStore.clearUserInfo()
|
||||||
|
window.location.href= '/'
|
||||||
|
// userStore.logout()
|
||||||
// router push login?
|
// router push login?
|
||||||
break
|
break
|
||||||
case 403:
|
case 403:
|
||||||
|
|
@ -98,9 +102,9 @@ class Request {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config?.showError !== false) {
|
// if (config?.showError !== false) {
|
||||||
message.error(msg)
|
// message.error(msg)
|
||||||
}
|
// }
|
||||||
// 返回包含具体错误信息的 Error 对象
|
// 返回包含具体错误信息的 Error 对象
|
||||||
return Promise.reject(new Error(msg))
|
return Promise.reject(new Error(msg))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { getScore, saveScore as apiSaveScore, type SaveScoreRequest, type ScoreInfo } from '~/service/api/score'
|
||||||
|
|
||||||
|
export const useScoreStore = defineStore('score', () => {
|
||||||
|
const scoreInfo = ref<ScoreInfo | null>(null)
|
||||||
|
|
||||||
|
async function fetchScore() {
|
||||||
|
try {
|
||||||
|
const data = await getScore()
|
||||||
|
scoreInfo.value = data
|
||||||
|
return data
|
||||||
|
} catch (error) {
|
||||||
|
// If error occurs (e.g. 404 not found if user hasn't set score), we might want to handle it.
|
||||||
|
// For now, just throw or log.
|
||||||
|
console.error('Failed to fetch score', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveScore(data: SaveScoreRequest) {
|
||||||
|
try {
|
||||||
|
const response = await apiSaveScore(data)
|
||||||
|
scoreInfo.value = response
|
||||||
|
return response
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save score', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearScore() {
|
||||||
|
scoreInfo.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
scoreInfo,
|
||||||
|
fetchScore,
|
||||||
|
saveScore,
|
||||||
|
clearScore,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (import.meta.hot)
|
||||||
|
import.meta.hot.accept(acceptHMRUpdate(useScoreStore as any, import.meta.hot))
|
||||||
|
|
@ -81,6 +81,8 @@ export const useUserStore = defineStore('user', () => {
|
||||||
userInfo,
|
userInfo,
|
||||||
setToken,
|
setToken,
|
||||||
setUserInfo,
|
setUserInfo,
|
||||||
|
clearToken,
|
||||||
|
clearUserInfo,
|
||||||
login,
|
login,
|
||||||
logout,
|
logout,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue