Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
Clark Lin
student-app-frontend
Commits
1f7f2977
Commit
1f7f2977
authored
Aug 28, 2025
by
Administrator
Browse files
added StudentView page to handle CURD operations.
parent
38326bc5
Changes
6
Expand all
Show whitespace changes
Inline
Side-by-side
src/api/authService.js
View file @
1f7f2977
src/api/index.js
View file @
1f7f2977
...
@@ -22,6 +22,9 @@ export const apiEndpoints = {
...
@@ -22,6 +22,9 @@ export const apiEndpoints = {
// 学生相关
// 学生相关
STUDENTS
:
{
STUDENTS
:
{
LIST
:
'
/api/student/
'
,
LIST
:
'
/api/student/
'
,
CREATE
:
'
/api/student/
'
,
UPDATE
:
(
studentId
)
=>
`/api/student/
${
studentId
}
/`
,
DELETE
:
(
studentId
)
=>
`/api/student/
${
studentId
}
/`
,
},
},
}
}
...
...
src/api/studentService.js
View file @
1f7f2977
import
apiClient
,
{
apiEndpoints
}
from
'
./index.js
'
import
apiClient
,
{
apiEndpoints
}
from
'
./index.js
'
//
获取学生列表
//
Get student list
export
const
getStudents
=
async
()
=>
{
export
const
getStudents
=
async
()
=>
{
try
{
try
{
const
response
=
await
apiClient
.
get
(
apiEndpoints
.
STUDENTS
.
LIST
)
const
response
=
await
apiClient
.
get
(
apiEndpoints
.
STUDENTS
.
LIST
)
...
@@ -9,3 +9,118 @@ export const getStudents = async () => {
...
@@ -9,3 +9,118 @@ export const getStudents = async () => {
throw
new
Error
(
error
?.
response
?.
data
?.
detail
||
error
?.
message
||
'
Failed to fetch students
'
)
throw
new
Error
(
error
?.
response
?.
data
?.
detail
||
error
?.
message
||
'
Failed to fetch students
'
)
}
}
}
}
// Update student information
export
const
updateStudent
=
async
(
studentId
,
studentData
)
=>
{
try
{
// Convert data format to match API requirements
const
apiData
=
{
student_name
:
studentData
.
student_name
||
''
,
enabled
:
studentData
.
enabled
?
'
Y
'
:
'
N
'
,
grade
:
studentData
.
grade
||
''
,
age
:
parseInt
(
studentData
.
age
)
||
0
,
}
// If avatar data is included, add avatar-related fields
if
(
studentData
.
avatar
&&
studentData
.
avatar_mime_type
)
{
apiData
.
avatar_base64
=
studentData
.
avatar
apiData
.
avatar_mime_type
=
studentData
.
avatar_mime_type
apiData
.
avatar_file_name
=
studentData
.
avatar_file_name
||
''
}
console
.
log
(
'
Sending update request:
'
,
{
studentId
,
url
:
apiEndpoints
.
STUDENTS
.
UPDATE
(
studentId
),
data
:
{
...
apiData
,
avatar_base64
:
apiData
.
avatar_base64
?
'
[base64 data]
'
:
undefined
}
// Don't print full base64
})
const
response
=
await
apiClient
.
patch
(
apiEndpoints
.
STUDENTS
.
UPDATE
(
studentId
),
apiData
)
return
response
.
data
}
catch
(
error
)
{
console
.
error
(
'
API error details:
'
,
{
status
:
error
?.
response
?.
status
,
statusText
:
error
?.
response
?.
statusText
,
data
:
error
?.
response
?.
data
,
headers
:
error
?.
response
?.
headers
})
const
errorMessage
=
error
?.
response
?.
data
?.
detail
||
error
?.
response
?.
data
?.
message
||
error
?.
response
?.
data
?.
error
||
`HTTP
${
error
?.
response
?.
status
}
:
${
error
?.
response
?.
statusText
}
`
||
error
.
message
||
'
Failed to update student
'
throw
new
Error
(
errorMessage
)
}
}
// Create student
export
const
createStudent
=
async
(
studentData
)
=>
{
try
{
// Convert data format to match API requirements
const
apiData
=
{
student_name
:
studentData
.
student_name
||
''
,
enabled
:
studentData
.
enabled
?
'
Y
'
:
'
N
'
,
grade
:
studentData
.
grade
||
''
,
age
:
parseInt
(
studentData
.
age
)
||
0
,
}
// If avatar data is included, add avatar-related fields
if
(
studentData
.
avatar
&&
studentData
.
avatar_mime_type
)
{
apiData
.
avatar_base64
=
studentData
.
avatar
apiData
.
avatar_mime_type
=
studentData
.
avatar_mime_type
apiData
.
avatar_file_name
=
studentData
.
avatar_file_name
||
''
}
console
.
log
(
'
Sending create request:
'
,
{
url
:
apiEndpoints
.
STUDENTS
.
CREATE
,
data
:
{
...
apiData
,
avatar_base64
:
apiData
.
avatar_base64
?
'
[base64 data]
'
:
undefined
}
})
const
response
=
await
apiClient
.
post
(
apiEndpoints
.
STUDENTS
.
CREATE
,
apiData
)
return
response
.
data
}
catch
(
error
)
{
console
.
error
(
'
API error details:
'
,
{
status
:
error
?.
response
?.
status
,
statusText
:
error
?.
response
?.
statusText
,
data
:
error
?.
response
?.
data
,
headers
:
error
?.
response
?.
headers
})
const
errorMessage
=
error
?.
response
?.
data
?.
detail
||
error
?.
response
?.
data
?.
message
||
error
?.
response
?.
data
?.
error
||
`HTTP
${
error
?.
response
?.
status
}
:
${
error
?.
response
?.
statusText
}
`
||
error
.
message
||
'
Failed to create student
'
throw
new
Error
(
errorMessage
)
}
}
// Delete student
export
const
deleteStudent
=
async
(
studentId
)
=>
{
try
{
console
.
log
(
'
Sending delete request:
'
,
{
studentId
,
url
:
apiEndpoints
.
STUDENTS
.
DELETE
(
studentId
)
})
const
response
=
await
apiClient
.
delete
(
apiEndpoints
.
STUDENTS
.
DELETE
(
studentId
))
return
response
.
data
}
catch
(
error
)
{
console
.
error
(
'
API error details:
'
,
{
status
:
error
?.
response
?.
status
,
statusText
:
error
?.
response
?.
statusText
,
data
:
error
?.
response
?.
data
,
headers
:
error
?.
response
?.
headers
})
const
errorMessage
=
error
?.
response
?.
data
?.
detail
||
error
?.
response
?.
data
?.
message
||
error
?.
response
?.
data
?.
error
||
`HTTP
${
error
?.
response
?.
status
}
:
${
error
?.
response
?.
statusText
}
`
||
error
.
message
||
'
Failed to delete student
'
throw
new
Error
(
errorMessage
)
}
}
src/stores/auth.js
View file @
1f7f2977
import
{
ref
,
computed
}
from
'
vue
'
import
{
ref
,
computed
}
from
'
vue
'
import
{
defineStore
}
from
'
pinia
'
import
{
defineStore
}
from
'
pinia
'
import
{
useRouter
}
from
'
vue-router
'
import
{
useRouter
}
from
'
vue-router
'
import
a
xios
from
'
axio
s
'
import
a
piClient
from
'
@/api/index.j
s
'
import
{
login
as
authLogin
,
refreshAccessToken
as
authRefreshToken
}
from
'
@/api/authService.js
'
import
{
login
as
authLogin
,
refreshAccessToken
as
authRefreshToken
}
from
'
@/api/authService.js
'
export
const
useAuthStore
=
defineStore
(
'
auth
'
,
()
=>
{
export
const
useAuthStore
=
defineStore
(
'
auth
'
,
()
=>
{
...
@@ -58,9 +58,10 @@ export const useAuthStore = defineStore('auth', () => {
...
@@ -58,9 +58,10 @@ export const useAuthStore = defineStore('auth', () => {
const
setAxiosAuthHeader
=
(
token
)
=>
{
const
setAxiosAuthHeader
=
(
token
)
=>
{
if
(
token
)
{
if
(
token
)
{
axios
.
defaults
.
headers
.
common
.
Authorization
=
`Bearer
${
token
}
`
// 设置apiClient默认headers
apiClient
.
defaults
.
headers
.
common
.
Authorization
=
`Bearer
${
token
}
`
}
else
{
}
else
{
delete
a
xios
.
defaults
.
headers
.
common
.
Authorization
delete
a
piClient
.
defaults
.
headers
.
common
.
Authorization
}
}
}
}
...
@@ -92,8 +93,8 @@ export const useAuthStore = defineStore('auth', () => {
...
@@ -92,8 +93,8 @@ export const useAuthStore = defineStore('auth', () => {
const
setupAxiosInterceptors
=
()
=>
{
const
setupAxiosInterceptors
=
()
=>
{
if
(
interceptorsInitialized
.
value
)
return
if
(
interceptorsInitialized
.
value
)
return
// 请求拦截:总是携带最新的访问令牌
// 请求拦截
器
:总是携带最新的访问令牌
a
xios
.
interceptors
.
request
.
use
((
config
)
=>
{
a
piClient
.
interceptors
.
request
.
use
((
config
)
=>
{
if
(
accessToken
.
value
)
{
if
(
accessToken
.
value
)
{
config
.
headers
=
config
.
headers
||
{}
config
.
headers
=
config
.
headers
||
{}
config
.
headers
.
Authorization
=
`Bearer
${
accessToken
.
value
}
`
config
.
headers
.
Authorization
=
`Bearer
${
accessToken
.
value
}
`
...
@@ -101,8 +102,8 @@ export const useAuthStore = defineStore('auth', () => {
...
@@ -101,8 +102,8 @@ export const useAuthStore = defineStore('auth', () => {
return
config
return
config
})
})
// 响应拦截:遇到 401 尝试刷新一次并重试原请求
// 响应拦截
器
:遇到 401 尝试刷新一次并重试原请求
a
xios
.
interceptors
.
response
.
use
(
a
piClient
.
interceptors
.
response
.
use
(
(
response
)
=>
response
,
(
response
)
=>
response
,
async
(
error
)
=>
{
async
(
error
)
=>
{
const
originalRequest
=
error
.
config
||
{}
const
originalRequest
=
error
.
config
||
{}
...
@@ -121,7 +122,7 @@ export const useAuthStore = defineStore('auth', () => {
...
@@ -121,7 +122,7 @@ export const useAuthStore = defineStore('auth', () => {
await
refreshAccessToken
()
await
refreshAccessToken
()
originalRequest
.
headers
=
originalRequest
.
headers
||
{}
originalRequest
.
headers
=
originalRequest
.
headers
||
{}
originalRequest
.
headers
.
Authorization
=
`Bearer
${
accessToken
.
value
}
`
originalRequest
.
headers
.
Authorization
=
`Bearer
${
accessToken
.
value
}
`
return
a
xios
(
originalRequest
)
return
a
piClient
(
originalRequest
)
}
catch
(
e
)
{
}
catch
(
e
)
{
logout
()
logout
()
return
Promise
.
reject
(
e
)
return
Promise
.
reject
(
e
)
...
...
src/views/LoginView.vue
View file @
1f7f2977
<
script
setup
>
<
script
setup
>
import
{
ref
}
from
'
vue
'
import
{
ref
}
from
'
vue
'
import
{
useAuthStore
}
from
'
../stores/auth
'
import
{
useAuthStore
}
from
'
../stores/auth
'
import
loginImage
from
'
@/assets/images/login.jpg
'
const
authStore
=
useAuthStore
()
const
authStore
=
useAuthStore
()
const
username
=
ref
(
''
)
const
username
=
ref
(
''
)
...
@@ -84,6 +83,14 @@ const handleLogin = async () => {
...
@@ -84,6 +83,14 @@ const handleLogin = async () => {
variant=
"outlined"
variant=
"outlined"
density=
"comfortable"
density=
"comfortable"
/>
/>
<v-alert
v-if=
"error"
type=
"error"
variant=
"tonal"
class=
"mb-4"
>
{{
error
}}
</v-alert>
<v-btn
<v-btn
color=
"primary"
color=
"primary"
block
block
...
...
src/views/StudentView.vue
View file @
1f7f2977
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment